diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 4895987da4..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:native - MSYSTEM: MINGW64 - MSYS2_URL: https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-base-x86_64-20220603.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/.gitattributes b/.gitattributes index a217cb7bfe..9ce7a19581 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,8 @@ *.h.inc diff=c *.m diff=objc *.py diff=python +*.rs diff=rust +*.rs.inc diff=rust +Cargo.lock diff=toml merge=binary + +*.patch -text -whitespace diff --git a/.gitlab-ci.d/base.yml b/.gitlab-ci.d/base.yml index 69b36c148a..25b88aaa06 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 == 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: '$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..39da7698b0 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -1,68 +1,111 @@ .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: + - source scripts/ci/gitlab-ci-section + - section_start setup "Pre-script setup" - JOBS=$(expr $(nproc) + 1) + - cat /packages.txt + - section_end setup 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 + - section_start configure "Running configure" + - ../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" + - section_end configure + - section_start build "Building QEMU" + - $MAKE -j"$JOBS" + - section_end build + - section_start test "Running tests" - if test -n "$MAKE_CHECK_ARGS"; then - make -j"$JOBS" $MAKE_CHECK_ARGS ; + $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; fi + - section_end test + - 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/**/*.c.o + - build/**/*.c.o.d .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) + - source scripts/ci/gitlab-ci-section + - section_start buildenv "Setting up to run tests" + - scripts/git-submodule.sh update roms/SLOF + - build/pyvenv/bin/meson subprojects download $(cd build/subprojects && echo *) - cd build - find . -type f -exec touch {} + # Avoid recompiling by hiding ninja with NINJA=":" - - make NINJA=":" $MAKE_CHECK_ARGS + # We also have to pre-cache the functional tests manually in this case + - if [ "x${QEMU_TEST_CACHE_DIR}" != "x" ]; then + $MAKE precache-functional ; + fi + - section_end buildenv + - section_start test "Running tests" + - $MAKE NINJA=":" $MAKE_CHECK_ARGS + - section_end test .native_test_job_template: 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 reports: junit: build/meson-logs/testlog.junit.xml -.avocado_test_job_template: +.functional_test_job_template: extends: .common_test_job_template cache: key: "${CI_JOB_NAME}-cache" paths: - ${CI_PROJECT_DIR}/avocado-cache + - ${CI_PROJECT_DIR}/functional-cache 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 - build/tests/results/latest/test-results + - build/tests/functional/*/*/*.log reports: junit: build/tests/results/latest/results.xml before_script: @@ -73,11 +116,13 @@ - echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]' >> ~/.config/avocado/avocado.conf - if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then - du -chs ${CI_PROJECT_DIR}/avocado-cache ; + du -chs ${CI_PROJECT_DIR}/*-cache ; fi - export AVOCADO_ALLOW_UNTRUSTED_CODE=1 + - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 + - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache after_script: - cd build - - du -chs ${CI_PROJECT_DIR}/avocado-cache + - du -chs ${CI_PROJECT_DIR}/*-cache variables: QEMU_JOB_AVOCADO: 1 diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 10886bb414..336223484d 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 @@ -26,29 +22,27 @@ check-system-alpine: IMAGE: alpine MAKE_CHECK_ARGS: check-unit check-qtest -avocado-system-alpine: - extends: .avocado_test_job_template +functional-system-alpine: + extends: .functional_test_job_template needs: - job: build-system-alpine artifacts: true variables: IMAGE: alpine - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional + 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-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 @@ -56,31 +50,31 @@ check-system-ubuntu: - job: build-system-ubuntu artifacts: true variables: - IMAGE: ubuntu2004 + IMAGE: ubuntu2204 MAKE_CHECK_ARGS: check -avocado-system-ubuntu: - extends: .avocado_test_job_template +functional-system-ubuntu: + extends: .functional_test_job_template needs: - job: build-system-ubuntu artifacts: true variables: - IMAGE: ubuntu2004 - MAKE_CHECK_ARGS: check-avocado + IMAGE: ubuntu2204 + MAKE_CHECK_ARGS: check-avocado check-functional + 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 @@ -88,17 +82,18 @@ check-system-debian: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 + IMAGE: debian MAKE_CHECK_ARGS: check -avocado-system-debian: - extends: .avocado_test_job_template +functional-system-debian: + extends: .functional_test_job_template needs: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 - MAKE_CHECK_ARGS: check-avocado + IMAGE: debian + MAKE_CHECK_ARGS: check-avocado check-functional + AVOCADO_TAGS: arch:arm arch:i386 arch:riscv64 arch:sh4 arch:sparc arch:xtensa crash-test-debian: extends: .native_test_job_template @@ -106,27 +101,37 @@ crash-test-debian: - job: build-system-debian artifacts: true variables: - IMAGE: debian-amd64 + IMAGE: debian script: - cd build - make NINJA=":" check-venv - - tests/venv/bin/python3 scripts/device-crash-test -q --tcg-only ./qemu-system-i386 + - 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 --enable-capstone - TARGETS: tricore-softmmu microblaze-softmmu mips-softmmu + CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust + 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 + +build-system-fedora-rust-nightly: + extends: + - .native_build_job_template + - .native_build_artifact_template + needs: + job: amd64-fedora-rust-nightly-container + variables: + IMAGE: fedora-rust-nightly + CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints + TARGETS: aarch64-softmmu + MAKE_CHECK_ARGS: check-build + allow_failure: true check-system-fedora: extends: .native_test_job_template @@ -137,14 +142,16 @@ check-system-fedora: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-system-fedora: - extends: .avocado_test_job_template +functional-system-fedora: + extends: .functional_test_job_template needs: - job: build-system-fedora artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional + 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 +163,98 @@ crash-test-fedora: script: - cd build - make NINJA=":" 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 + - 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 + job: amd64-centos9-container variables: - IMAGE: centos8 - CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-fdt=system + IMAGE: centos9 + CONFIGURE_ARGS: --disable-nettle --enable-gcrypt --enable-vfio-user-server --enable-modules --enable-trace-backends=dtrace --enable-docs - --enable-vfio-user-server TARGETS: ppc64-softmmu or1k-softmmu s390x-softmmu - x86_64-softmmu rx-softmmu sh4-softmmu nios2-softmmu + x86_64-softmmu rx-softmmu sh4-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/**/*.c.o + - build-previous/**/*.c.o.d + 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: + - source scripts/ci/gitlab-ci-section + - 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 + # Don't allow python-based tests to run. The + # vmstate-checker-script test has a race that causes it to fail + # sometimes. It cannot be fixed it because this job runs the test + # from the old QEMU version. The test will be removed on master, + # but this job will only see the change in the next release. + # + # TODO: remove this line after 9.2 release + - unset PYTHON + # 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 @@ -182,31 +262,30 @@ check-system-centos: - job: build-system-centos artifacts: true variables: - IMAGE: centos8 + IMAGE: centos9 MAKE_CHECK_ARGS: check -avocado-system-centos: - extends: .avocado_test_job_template +functional-system-centos: + extends: .functional_test_job_template needs: - job: build-system-centos artifacts: true variables: - IMAGE: centos8 - MAKE_CHECK_ARGS: check-avocado + IMAGE: centos9 + MAKE_CHECK_ARGS: check-avocado check-functional + AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx + arch:sh4 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 @@ -217,15 +296,46 @@ check-system-opensuse: IMAGE: opensuse-leap MAKE_CHECK_ARGS: check -avocado-system-opensuse: - extends: .avocado_test_job_template +functional-system-opensuse: + extends: .functional_test_job_template needs: - job: build-system-opensuse artifacts: true variables: IMAGE: opensuse-leap - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional + 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 + +functional-system-flaky: + extends: .functional_test_job_template + needs: + - job: build-system-flaky + artifacts: true + allow_failure: true + variables: + IMAGE: debian + MAKE_CHECK_ARGS: check-avocado check-functional + 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 @@ -237,13 +347,14 @@ avocado-system-opensuse: build-tcg-disabled: extends: .native_build_job_template needs: - job: amd64-centos8-container + job: amd64-centos9-container variables: - IMAGE: centos8 + IMAGE: centos9 script: - 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 @@ -251,11 +362,13 @@ build-tcg-disabled: - cd tests/qemu-iotests/ - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163 - 170 171 183 184 192 194 208 221 226 227 236 253 277 image-fleecing + 170 171 184 192 194 208 221 226 227 236 253 277 image-fleecing - ./check -qcow2 028 051 056 057 058 065 068 082 085 091 095 096 102 122 124 132 139 142 144 145 151 152 155 157 165 194 196 200 202 208 209 216 218 227 234 246 247 248 250 254 255 257 258 260 261 262 263 264 270 272 273 277 279 image-fleecing + - cd ../.. + - make distclean build-user: extends: .native_build_job_template @@ -264,6 +377,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 +387,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 +421,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 @@ -317,10 +452,9 @@ clang-system: job: amd64-fedora-container variables: 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 + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --enable-ubsan + --extra-cflags=-fno-sanitize-recover=undefined + TARGETS: alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu s390x-softmmu MAKE_CHECK_ARGS: check-qtest check-tcg clang-user: @@ -330,9 +464,9 @@ clang-user: 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 - --extra-cflags=-fsanitize=undefined --extra-cflags=-fno-sanitize-recover=undefined + CONFIGURE_ARGS: --cc=clang --cxx=clang++ --disable-system --enable-ubsan + --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=-fno-sanitize-recover=undefined MAKE_CHECK_ARGS: check-unit check-tcg # Set LD_JOBS=1 because this requires LTO and ld consumes a large amount of memory. @@ -345,7 +479,9 @@ clang-user: # 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: @@ -361,10 +497,6 @@ build-cfi-aarch64: # skipped until the situation has been solved. QEMU_JOB_SKIPPED: 1 timeout: 90m - artifacts: - expire_in: 2 days - paths: - - build check-cfi-aarch64: extends: .native_test_job_template @@ -375,17 +507,19 @@ check-cfi-aarch64: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-cfi-aarch64: - extends: .avocado_test_job_template +functional-cfi-aarch64: + extends: .functional_test_job_template needs: - job: build-cfi-aarch64 artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional build-cfi-ppc64-s390x: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-fedora-container variables: @@ -401,10 +535,6 @@ build-cfi-ppc64-s390x: # skipped until the situation has been solved. QEMU_JOB_SKIPPED: 1 timeout: 80m - artifacts: - expire_in: 2 days - paths: - - build check-cfi-ppc64-s390x: extends: .native_test_job_template @@ -415,17 +545,19 @@ check-cfi-ppc64-s390x: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-cfi-ppc64-s390x: - extends: .avocado_test_job_template +functional-cfi-ppc64-s390x: + extends: .functional_test_job_template needs: - job: build-cfi-ppc64-s390x artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional build-cfi-x86_64: - extends: .native_build_job_template + extends: + - .native_build_job_template + - .native_build_artifact_template needs: - job: amd64-fedora-container variables: @@ -437,10 +569,6 @@ build-cfi-x86_64: 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 @@ -451,48 +579,39 @@ check-cfi-x86_64: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-cfi-x86_64: - extends: .avocado_test_job_template +functional-cfi-x86_64: + extends: .functional_test_job_template needs: - job: build-cfi-x86_64 artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional 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 --disable-slirp + 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 + # Remove when we switch to a distro with clang >= 18 + # https://github.com/google/sanitizers/issues/1716 + MAKE: setarch -R make -# 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: - cd build - gcovr --xml-pretty --exclude-unreachable-branches --print-summary @@ -500,8 +619,12 @@ check-gprof-gcov: 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 @@ -514,15 +637,19 @@ 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" + - failures=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 ; + "${fuzzer}" -runs=1 -seed=1 || { echo "FAILED:"" ${fuzzer} exit code is $?"; failures=$(($failures+1)); }; done + - echo "Number of failures:"" $failures" + - test $failures = 0 build-tci: extends: .native_build_job_template @@ -531,11 +658,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-kvm --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 @@ -547,47 +675,27 @@ 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 + job: amd64-centos9-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 + IMAGE: centos9 CONFIGURE_ARGS: + --without-default-devices --without-default-features - --disable-capstone + --disable-fdt --disable-pie --disable-qom-cast-debug --disable-strip - TARGETS: avr-softmmu i386-softmmu mips64-softmmu s390x-softmmu sh4-softmmu - sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user - MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow + --target-list-exclude=aarch64-softmmu,microblaze-softmmu,mips64-softmmu,mipsel-softmmu,ppc64-softmmu,sh4el-softmmu,xtensa-softmmu,x86_64-softmmu + 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: @@ -599,20 +707,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 + 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 @@ -629,7 +735,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 @@ -637,14 +743,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/check-dco.py b/.gitlab-ci.d/check-dco.py index 632c8bcce8..70dec7d6ee 100755 --- a/.gitlab-ci.d/check-dco.py +++ b/.gitlab-ci.d/check-dco.py @@ -19,10 +19,9 @@ cwd = os.getcwd() reponame = os.path.basename(cwd) repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame) +print(f"adding upstream git repo @ {repourl}") subprocess.check_call(["git", "remote", "add", "check-dco", repourl]) -subprocess.check_call(["git", "fetch", "check-dco", "master"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) +subprocess.check_call(["git", "fetch", "check-dco", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-dco/master", "HEAD"], @@ -79,7 +78,10 @@ of Origin 1.1 (DCO): To indicate acceptance of the DCO every commit must have a tag - Signed-off-by: REAL NAME + Signed-off-by: YOUR NAME + +where "YOUR NAME" is your commonly known identity in the context +of the community. This can be achieved by passing the "-s" flag to the "git commit" command. diff --git a/.gitlab-ci.d/check-patch.py b/.gitlab-ci.d/check-patch.py index 39e2b403c9..68c549a146 100755 --- a/.gitlab-ci.d/check-patch.py +++ b/.gitlab-ci.d/check-patch.py @@ -19,13 +19,12 @@ cwd = os.getcwd() reponame = os.path.basename(cwd) repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame) +print(f"adding upstream git repo @ {repourl}") # GitLab CI environment does not give us any direct info about the # base for the user's branch. We thus need to figure out a common # ancestor between the user's branch and current git master. subprocess.check_call(["git", "remote", "add", "check-patch", repourl]) -subprocess.check_call(["git", "fetch", "check-patch", "master"], - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) +subprocess.check_call(["git", "fetch", "check-patch", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-patch/master", "HEAD"], diff --git a/.gitlab-ci.d/cirrus.yml b/.gitlab-ci.d/cirrus.yml index 634a73a742..a9e43e21d0 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,80 +46,30 @@ variables: QEMU_JOB_CIRRUS: 1 -x64-freebsd-12-build: +x64-freebsd-14-build: extends: .cirrus_build_job variables: - NAME: freebsd-12 + NAME: freebsd-14 CIRRUS_VM_INSTANCE_TYPE: freebsd_instance CIRRUS_VM_IMAGE_SELECTOR: image_family - CIRRUS_VM_IMAGE_NAME: freebsd-12-3 + CIRRUS_VM_IMAGE_NAME: freebsd-14-1 CIRRUS_VM_CPUS: 8 CIRRUS_VM_RAM: 8G - UPDATE_COMMAND: pkg update + UPDATE_COMMAND: pkg update; pkg upgrade -y INSTALL_COMMAND: pkg install -y + CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblaze-softmmu,mips64el-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4eb-softmmu,xtensa-softmmu TEST_TARGETS: check -x64-freebsd-13-build: +aarch64-macos-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-1 - CIRRUS_VM_CPUS: 8 - CIRRUS_VM_RAM: 8G - UPDATE_COMMAND: pkg update - INSTALL_COMMAND: pkg install -y - TEST_TARGETS: check - -aarch64-macos-12-base-build: - extends: .cirrus_build_job - variables: - NAME: macos-12 + NAME: macos-14 CIRRUS_VM_INSTANCE_TYPE: macos_instance CIRRUS_VM_IMAGE_SELECTOR: image - CIRRUS_VM_IMAGE_NAME: ghcr.io/cirruslabs/macos-monterey-base:latest - CIRRUS_VM_CPUS: 12 - CIRRUS_VM_RAM: 24G + CIRRUS_VM_IMAGE_NAME: ghcr.io/cirruslabs/macos-runner:sonoma 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 + CONFIGURE_ARGS: --target-list-exclude=arm-softmmu,i386-softmmu,microblazeel-softmmu,mips64-softmmu,mipsel-softmmu,mips-softmmu,ppc-softmmu,sh4-softmmu,xtensaeb-softmmu TEST_TARGETS: check-unit check-block check-qapi-schema check-softfloat check-qtest-x86_64 - - -# The following jobs run VM-based tests via KVM on a Linux-based Cirrus-CI job -.cirrus_kvm_job: - extends: .base_job_template - stage: build - image: registry.gitlab.com/libvirt/libvirt-ci/cirrus-run:master - needs: [] - timeout: 80m - script: - - sed -e "s|[@]CI_REPOSITORY_URL@|$CI_REPOSITORY_URL|g" - -e "s|[@]CI_COMMIT_REF_NAME@|$CI_COMMIT_REF_NAME|g" - -e "s|[@]CI_COMMIT_SHA@|$CI_COMMIT_SHA|g" - -e "s|[@]NAME@|$NAME|g" - -e "s|[@]CONFIGURE_ARGS@|$CONFIGURE_ARGS|g" - -e "s|[@]TEST_TARGETS@|$TEST_TARGETS|g" - <.gitlab-ci.d/cirrus/kvm-build.yml >.gitlab-ci.d/cirrus/$NAME.yml - - cat .gitlab-ci.d/cirrus/$NAME.yml - - cirrus-run -v --show-build-log always .gitlab-ci.d/cirrus/$NAME.yml - variables: - QEMU_JOB_CIRRUS: 1 - QEMU_JOB_OPTIONAL: 1 - - -x86-netbsd: - extends: .cirrus_kvm_job - variables: - NAME: netbsd - CONFIGURE_ARGS: --target-list=x86_64-softmmu,ppc64-softmmu,aarch64-softmmu - TEST_TARGETS: check - -x86-openbsd: - extends: .cirrus_kvm_job - variables: - NAME: openbsd - CONFIGURE_ARGS: --target-list=i386-softmmu,riscv64-softmmu,mips64-softmmu - TEST_TARGETS: check diff --git a/.gitlab-ci.d/cirrus/build.yml b/.gitlab-ci.d/cirrus/build.yml index 7ef6af8d33..102cdbd8b1 100644 --- a/.gitlab-ci.d/cirrus/build.yml +++ b/.gitlab-ci.d/cirrus/build.yml @@ -16,15 +16,17 @@ 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" - git reset --hard "$CI_COMMIT_SHA" - build_script: + step_script: - mkdir build - cd build - ../configure --enable-werror $CONFIGURE_ARGS @@ -32,6 +34,9 @@ build_task: - $MAKE -j$(sysctl -n hw.ncpu) - for TARGET in $TEST_TARGETS ; do - $MAKE -j$(sysctl -n hw.ncpu) $TARGET V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + $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 e3fc3235b9..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 bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' -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 deleted file mode 100644 index 9f56babd9c..0000000000 --- a/.gitlab-ci.d/cirrus/freebsd-13.vars +++ /dev/null @@ -1,16 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool variables freebsd-13 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 bison bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage cmocka ctags curl cyrus-sasl dbus diffutils dtc flex fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 json-c libepoxy libffi libgcrypt libjpeg-turbo libnfs libslirp libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv perl5 pixman pkgconf png py39-numpy py39-pillow py39-pip py39-sphinx py39-sphinx_rtd_theme py39-yaml python3 rpm2cpio sdl2 sdl2_image snappy sndio spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' -PYPI_PKGS='' -PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/freebsd-14.vars b/.gitlab-ci.d/cirrus/freebsd-14.vars new file mode 100644 index 0000000000..0a7ac5e0e1 --- /dev/null +++ b/.gitlab-ci.d/cirrus/freebsd-14.vars @@ -0,0 +1,16 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables freebsd-14 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' +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 gtk-vnc 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 py311-numpy py311-pillow py311-pip py311-pyyaml py311-sphinx py311-sphinx_rtd_theme py311-tomli python3 rpm2cpio rust rust-bindgen-cli 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 deleted file mode 100644 index 4334fabf39..0000000000 --- a/.gitlab-ci.d/cirrus/kvm-build.yml +++ /dev/null @@ -1,31 +0,0 @@ -container: - image: fedora:35 - cpu: 4 - memory: 8Gb - kvm: true - -env: - CIRRUS_CLONE_DEPTH: 1 - CI_REPOSITORY_URL: "@CI_REPOSITORY_URL@" - CI_COMMIT_REF_NAME: "@CI_COMMIT_REF_NAME@" - CI_COMMIT_SHA: "@CI_COMMIT_SHA@" - -@NAME@_task: - @NAME@_vm_cache: - folder: $HOME/.cache/qemu-vm - install_script: - - dnf update -y - - dnf install -y git make openssh-clients qemu-img qemu-system-x86 wget - clone_script: - - git clone --depth 100 "$CI_REPOSITORY_URL" . - - git fetch origin "$CI_COMMIT_REF_NAME" - - git reset --hard "$CI_COMMIT_SHA" - build_script: - - if [ -f $HOME/.cache/qemu-vm/images/@NAME@.img ]; then - make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) - EXTRA_CONFIGURE_OPTS="@CONFIGURE_ARGS@" - BUILD_TARGET="@TEST_TARGETS@" ; - else - make vm-build-@NAME@ J=$(getconf _NPROCESSORS_ONLN) BUILD_TARGET=help - EXTRA_CONFIGURE_OPTS="--disable-system --disable-user --disable-tools" ; - fi diff --git a/.gitlab-ci.d/cirrus/macos-12.vars b/.gitlab-ci.d/cirrus/macos-12.vars deleted file mode 100644 index ef9e14b373..0000000000 --- a/.gitlab-ci.d/cirrus/macos-12.vars +++ /dev/null @@ -1,16 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool variables macos-12 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 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' -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..25dff322e6 --- /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 bindgen bison bzip2 capstone ccache cmocka ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python3 rpm2cpio rust 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 08f8450fa1..5459447676 100644 --- a/.gitlab-ci.d/container-core.yml +++ b/.gitlab-ci.d/container-core.yml @@ -1,10 +1,10 @@ include: - local: '/.gitlab-ci.d/container-template.yml' -amd64-centos8-container: +amd64-centos9-container: extends: .container_job_template variables: - NAME: centos8 + NAME: centos9 amd64-fedora-container: extends: .container_job_template diff --git a/.gitlab-ci.d/container-cross.yml b/.gitlab-ci.d/container-cross.yml index 2d560e9764..34c0e729ad 100644 --- a/.gitlab-ci.d/container-cross.yml +++ b/.gitlab-ci.d/container-cross.yml @@ -1,9 +1,3 @@ -alpha-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-alpha-cross - amd64-debian-cross-container: extends: .container_job_template stage: containers @@ -16,67 +10,41 @@ amd64-debian-user-cross-container: 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 variables: NAME: debian-arm64-cross -armel-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-armel-cross - armhf-debian-cross-container: extends: .container_job_template stage: containers 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 variables: - NAME: debian-hppa-cross + NAME: debian-loongarch-cross -m68k-debian-cross-container: +i686-debian-cross-container: extends: .container_job_template stage: containers variables: - NAME: debian-m68k-cross - -mips64-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-mips64-cross + NAME: debian-i686-cross mips64el-debian-cross-container: extends: .container_job_template @@ -84,24 +52,12 @@ mips64el-debian-cross-container: variables: NAME: debian-mips64el-cross -mips-debian-cross-container: - extends: .container_job_template - stage: containers - 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 - variables: - NAME: debian-powerpc-test-cross - ppc64el-debian-cross-container: extends: .container_job_template stage: containers @@ -115,13 +71,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 - variables: - NAME: debian-riscv64-test-cross + QEMU_JOB_OPTIONAL: 1 s390x-debian-cross-container: extends: .container_job_template @@ -129,18 +79,6 @@ s390x-debian-cross-container: variables: NAME: debian-s390x-cross -sh4-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-sh4-cross - -sparc64-debian-cross-container: - extends: .container_job_template - stage: containers - variables: - NAME: debian-sparc64-cross - tricore-debian-cross-container: extends: .container_job_template stage: containers @@ -152,21 +90,6 @@ xtensa-debian-cross-container: variables: NAME: debian-xtensa-cross -cris-fedora-cross-container: - extends: .container_job_template - 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 96d2a3b58b..db9b4d5e57 100644 --- a/.gitlab-ci.d/containers.yml +++ b/.gitlab-ci.d/containers.yml @@ -11,12 +11,12 @@ 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 @@ -27,3 +27,9 @@ python-container: extends: .container_job_template variables: NAME: python + +amd64-fedora-rust-nightly-container: + extends: .container_job_template + variables: + NAME: fedora-rust-nightly + allow_failure: true diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 5e8892fd49..303943f818 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -1,23 +1,52 @@ .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 + before_script: + - source scripts/ci/gitlab-ci-section + - section_start setup "Pre-script setup" + - JOBS=$(expr $(nproc) + 1) + - cat /packages.txt + - section_end setup 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 + - section_start configure "Running configure" + - ../configure --enable-werror --disable-docs --enable-fdt=system + --disable-user $QEMU_CONFIGURE_OPTS $EXTRA_CONFIGURE_OPTS + --target-list-exclude="arm-softmmu i386-softmmu microblaze-softmmu mips-softmmu mipsel-softmmu mips64-softmmu ppc-softmmu riscv32-softmmu sh4-softmmu sparc-softmmu xtensa-softmmu $CROSS_SKIP_TARGETS" - - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS + - section_end configure + - section_start build "Building QEMU" + - make -j"$JOBS" all check-build + - section_end build + - section_start test "Running tests" + - if test -n "$MAKE_CHECK_ARGS"; + then + $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; + fi + - section_end test + - section_start installer "Building the installer" - if grep -q "EXESUF=.exe" config-host.mak; then make installer; 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 + - section_end installer + - ccache --show-stats # Job to cross-build specific accelerators. # @@ -27,27 +56,78 @@ .cross_accel_build_job: extends: .base_job_template stage: build - image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:latest - timeout: 30m + image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG + timeout: 60m + cache: + paths: + - ccache/ + key: "$CI_JOB_NAME" + before_script: + - source scripts/ci/gitlab-ci-section + - JOBS=$(expr $(nproc) + 1) 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 + - section_start configure "Running configure" + - ../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 + - section_end configure + - section_start build "Building QEMU" + - make -j"$JOBS" all check-build + - section_end build + - section_start test "Running tests" + - if test -n "$MAKE_CHECK_ARGS"; + then + $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; + fi + - section_end test .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" + before_script: + - source scripts/ci/gitlab-ci-section + - JOBS=$(expr $(nproc) + 1) 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 + - section_start configure "Running configure" + - ../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 + alpha-linux-user m68k-linux-user microblazeel-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 + - section_end configure + - section_start build "Building QEMU" + - make -j"$JOBS" all check-build + - section_end build + - section_start test "Running tests" + - if test -n "$MAKE_CHECK_ARGS"; + then + $MAKE -j"$JOBS" $MAKE_CHECK_ARGS ; + fi + - section_end test + +# 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 c4cd96433d..95dfc39224 100644 --- a/.gitlab-ci.d/crossbuilds.yml +++ b/.gitlab-ci.d/crossbuilds.yml @@ -1,27 +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: - job: armel-debian-cross-container - 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,32 +22,51 @@ 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 + IMAGE: debian-arm64-cross + EXTRA_CONFIGURE_OPTS: --disable-tcg --without-default-features + +cross-i686-system: + extends: + - .cross_system_build_job + - .cross_test_artifacts + needs: + job: i686-debian-cross-container + variables: + IMAGE: debian-i686-cross + EXTRA_CONFIGURE_OPTS: --disable-kvm MAKE_CHECK_ARGS: check-qtest -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 - MAKE_CHECK_ARGS: check check-tcg + EXTRA_CONFIGURE_OPTS: --target-list=i386-softmmu,i386-linux-user,aarch64-softmmu,aarch64-linux-user,ppc-softmmu,ppc-linux-user --disable-plugins --disable-kvm + # Force tests to run with reduced parallelism, to see whether this + # reduces the flakiness of this CI job. The CI + # environment by default shows us 8 CPUs and so we + # would otherwise be using a parallelism of 9. + MAKE_CHECK_ARGS: check check-tcg -j2 cross-mipsel-system: extends: .cross_system_build_job @@ -112,6 +110,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. @@ -151,7 +157,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 @@ -161,27 +167,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 + 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 97f99e29c2..1aa3c60efe 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -10,11 +10,25 @@ # 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-22.04-s390x.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 deleted file mode 100644 index 068b0c4335..0000000000 --- a/.gitlab-ci.d/custom-runners/centos-stream-8-x86_64.yml +++ /dev/null @@ -1,30 +0,0 @@ -centos-stream-8-x86_64: - allow_failure: true - needs: [] - stage: build - tags: - - 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 - || { cat meson-logs/testlog.txt; exit 1; } ; - - ../scripts/ci/org.centos/stream/8/x86_64/test-avocado diff --git a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml deleted file mode 100644 index 0c835939db..0000000000 --- a/.gitlab-ci.d/custom-runners/ubuntu-20.04-s390x.yml +++ /dev/null @@ -1,131 +0,0 @@ -# All ubuntu-20.04 jobs should run successfully in an environment -# setup by the scripts/ci/setup/build-environment.yml task -# "Install basic packages to build QEMU on Ubuntu 20.04/20.04" - -ubuntu-20.04-s390x-all-linux-static: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$S390X_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 - || { cat config.log meson-logs/meson-log.txt; exit 1; } - - make --output-sync -j`nproc` - - make --output-sync -j`nproc` check V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; - - make --output-sync -j`nproc` check-tcg V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; - -ubuntu-20.04-s390x-all: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - timeout: 75m - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$S390X_RUNNER_AVAILABLE" - script: - - 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 - || { cat meson-logs/testlog.txt; exit 1; } ; - -ubuntu-20.04-s390x-alldbg: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$S390X_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - 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 - || { cat meson-logs/testlog.txt; exit 1; } ; - -ubuntu-20.04-s390x-clang: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$S390X_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` - - make --output-sync -j`nproc` check V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; - -ubuntu-20.04-s390x-tci: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$S390X_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - 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: - needs: [] - stage: build - tags: - - ubuntu_20.04 - - s390x - rules: - - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - when: manual - allow_failure: true - - if: "$S390X_RUNNER_AVAILABLE" - when: manual - allow_failure: true - script: - - 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 - || { cat meson-logs/testlog.txt; exit 1; } ; diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml index 1a2f9b8dbe..8727687e2b 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch32.yml @@ -1,8 +1,9 @@ # 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" +# setup by the scripts/ci/setup/ubuntu/build-environment.yml task +# "Install basic packages to build QEMU on Ubuntu 22.04" ubuntu-22.04-aarch32-all: + extends: .custom_runner_template needs: [] stage: build tags: @@ -21,5 +22,4 @@ ubuntu-22.04-aarch32-all: - ../configure --cross-prefix=arm-linux-gnueabihf- || { 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 V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + - 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 index ce0b18af6f..ca2f140471 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-aarch64.yml @@ -1,8 +1,9 @@ -# 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" +# All ubuntu-22.04 jobs should run successfully in an environment +# setup by the scripts/ci/setup/ubuntu/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: @@ -19,12 +20,11 @@ ubuntu-22.04-aarch64-all-linux-static: - ../configure --enable-debug --static --disable-system --disable-pie || { cat config.log meson-logs/meson-log.txt; exit 1; } - make --output-sync -j`nproc --ignore=40` - - make --output-sync -j`nproc --ignore=40` check V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; - - make --output-sync -j`nproc --ignore=40` check-tcg V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + - make check-tcg + - make --output-sync -j`nproc --ignore=40` check ubuntu-22.04-aarch64-all: + extends: .custom_runner_template needs: [] stage: build tags: @@ -43,10 +43,32 @@ ubuntu-22.04-aarch64-all: - ../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 V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + - 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: @@ -62,10 +84,10 @@ ubuntu-22.04-aarch64-alldbg: || { 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 V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + - make --output-sync -j`nproc --ignore=40` check ubuntu-22.04-aarch64-clang: + extends: .custom_runner_template needs: [] stage: build tags: @@ -81,11 +103,10 @@ ubuntu-22.04-aarch64-clang: script: - mkdir build - cd build - - ../configure --disable-libssh --cc=clang-10 --cxx=clang++-10 --enable-sanitizers + - ../configure --disable-libssh --cc=clang --cxx=clang++ --enable-ubsan || { 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 V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + - make --output-sync -j`nproc --ignore=40` check ubuntu-22.04-aarch64-tci: needs: [] @@ -108,6 +129,7 @@ ubuntu-22.04-aarch64-tci: - make --output-sync -j`nproc --ignore=40` ubuntu-22.04-aarch64-notcg: + extends: .custom_runner_template needs: [] stage: build tags: @@ -123,8 +145,7 @@ ubuntu-22.04-aarch64-notcg: script: - mkdir build - cd build - - ../configure --disable-tcg + - ../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 V=1 - || { cat meson-logs/testlog.txt; exit 1; } ; + - make --output-sync -j`nproc --ignore=40` check diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml new file mode 100644 index 0000000000..ca374acb8c --- /dev/null +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -0,0 +1,128 @@ +# All ubuntu-22.04 jobs should run successfully in an environment +# setup by the scripts/ci/setup/ubuntu/build-environment.yml task +# "Install basic packages to build QEMU on Ubuntu 22.04" + +ubuntu-22.04-s390x-all-linux: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - s390x + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + - if: "$S390X_RUNNER_AVAILABLE" + script: + - mkdir build + - cd build + - ../configure --enable-debug --disable-system --disable-tools --disable-docs + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc` + - make --output-sync check-tcg + - make --output-sync -j`nproc` check + +ubuntu-22.04-s390x-all-system: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - s390x + timeout: 75m + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + - if: "$S390X_RUNNER_AVAILABLE" + script: + - mkdir build + - cd build + - ../configure --disable-user + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc` + - make --output-sync -j`nproc` check + +ubuntu-22.04-s390x-alldbg: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - s390x + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$S390X_RUNNER_AVAILABLE" + when: manual + allow_failure: true + 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` + - make --output-sync -j`nproc` check + +ubuntu-22.04-s390x-clang: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - s390x + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$S390X_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --cc=clang --cxx=clang++ --enable-ubsan + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc` + - make --output-sync -j`nproc` check + +ubuntu-22.04-s390x-tci: + needs: [] + stage: build + tags: + - ubuntu_22.04 + - s390x + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$S390X_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` + +ubuntu-22.04-s390x-notcg: + extends: .custom_runner_template + needs: [] + stage: build + tags: + - ubuntu_22.04 + - s390x + rules: + - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + when: manual + allow_failure: true + - if: "$S390X_RUNNER_AVAILABLE" + when: manual + allow_failure: true + script: + - mkdir build + - cd build + - ../configure --disable-tcg + || { cat config.log meson-logs/meson-log.txt; exit 1; } + - make --output-sync -j`nproc` + - make --output-sync -j`nproc` check diff --git a/.gitlab-ci.d/edk2.yml b/.gitlab-ci.d/edk2.yml deleted file mode 100644 index 314e101745..0000000000 --- a/.gitlab-ci.d/edk2.yml +++ /dev/null @@ -1,85 +0,0 @@ -# All jobs needing docker-edk2 must use the same rules it uses. -.edk2_job_rules: - 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 of the files affecting the build are touched - - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project"' - changes: - - .gitlab-ci.d/edk2.yml - - .gitlab-ci.d/edk2/Dockerfile - - roms/edk2/* - when: manual - - # In forks, if QEMU_CI=1 is set, then create manual job - # if the branch/tag starts with 'edk2' - - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_REF_NAME =~ /^edk2/' - when: manual - - # In forks, if QEMU_CI=1 is set, then create manual job - # if last commit msg contains 'EDK2' (case insensitive) - - if: '$QEMU_CI == "1" && $CI_PROJECT_NAMESPACE != "qemu-project" && $CI_COMMIT_MESSAGE =~ /edk2/i' - when: manual - - # Run if any files affecting the build output are touched - - changes: - - .gitlab-ci.d/edk2.yml - - .gitlab-ci.d/edk2/Dockerfile - - roms/edk2/* - when: on_success - - # Run if the branch/tag starts with 'edk2' - - if: '$CI_COMMIT_REF_NAME =~ /^edk2/' - when: on_success - - # Run if last commit msg contains 'EDK2' (case insensitive) - - if: '$CI_COMMIT_MESSAGE =~ /edk2/i' - 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 04ed5a3ea1..42f137d624 100644 --- a/.gitlab-ci.d/opensbi.yml +++ b/.gitlab-ci.d/opensbi.yml @@ -24,6 +24,10 @@ - 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 @@ -42,17 +46,15 @@ docker-opensbi: extends: .opensbi_job_rules stage: containers - image: docker:19.03.1 + image: docker:latest services: - - docker:19.03.1-dind + - docker: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 + - 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 @@ -65,6 +67,7 @@ build-opensbi: 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 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/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 a3e7a37022..45ed0c96fe 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -1,25 +1,67 @@ -.shared_msys2_builder: +msys2-64bit: extends: .base_job_template tags: - - shared-windows - - windows - - windows-1809 + - saas-windows-medium-amd64 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 + CONFIGURE_ARGS: --disable-system --enable-tools -Ddebug=false -Doptimization=0 + # 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-06-03/msys2-base-x86_64-20220603.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,72 +70,37 @@ - .\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 bison diffutils flex git grep make sed - mingw-w64-x86_64-capstone + mingw-w64-x86_64-binutils + mingw-w64-x86_64-ccache mingw-w64-x86_64-curl - mingw-w64-x86_64-cyrus-sasl mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 - mingw-w64-x86_64-gnutls 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-nettle mingw-w64-x86_64-ninja mingw-w64-x86_64-pixman mingw-w64-x86_64-pkgconf mingw-w64-x86_64-python - mingw-w64-x86_64-SDL2 - mingw-w64-x86_64-SDL2_image - mingw-w64-x86_64-snappy - 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:JOBS = $(.\msys64\usr\bin\bash -lc nproc) - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory - - $env:MSYSTEM = 'MINGW64' # Start a 64 bit Mingw environment - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink - - .\msys64\usr\bin\bash -lc './configure --target-list=x86_64-softmmu - --enable-capstone --without-default-devices' - - .\msys64\usr\bin\bash -lc 'make' - - .\msys64\usr\bin\bash -lc 'make check || { cat build/meson-logs/testlog.txt; exit 1; } ;' - -msys2-32bit: - extends: .shared_msys2_builder - script: - - .\msys64\usr\bin\bash -lc "pacman -Sy --noconfirm --needed - bison diffutils flex - 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 - - $env:MSYS = 'winsymlinks:native' # Enable native Windows symlink - - mkdir output - - cd output - - ..\msys64\usr\bin\bash -lc "../configure --target-list=ppc64-softmmu" - - ..\msys64\usr\bin\bash -lc 'make' - - ..\msys64\usr\bin\bash -lc 'make check || { cat meson-logs/testlog.txt; exit 1; } ;' + - $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 $CONFIGURE_ARGS" + - ..\msys64\usr\bin\bash -lc "make -j$env:JOBS" + - ..\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 9c16165d28..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,21 +22,12 @@ [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 @@ -52,26 +37,9 @@ [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 [submodule "tests/lcitool/libvirt-ci"] path = tests/lcitool/libvirt-ci url = https://gitlab.com/libvirt/libvirt-ci.git -[submodule "subprojects/libvfio-user"] - path = subprojects/libvfio-user - url = https://gitlab.com/qemu-project/libvfio-user.git -[submodule "ui/thirdparty/imgui"] - path = ui/thirdparty/imgui - url = https://github.com/xemu-project/imgui.git - ignore = untracked -[submodule "ui/thirdparty/implot"] - path = ui/thirdparty/implot - url = https://github.com/epezent/implot.git - ignore = untracked -[submodule "genconfig"] - path = genconfig - url = https://github.com/mborgerson/genconfig.git diff --git a/.mailmap b/.mailmap index 35dddbe27b..727ce204b2 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,12 @@ Aleksandar Markovic Aleksandar Rikalo Aleksandar Rikalo Alexander Graf +Ani Sinha Anthony Liguori Anthony Liguori +Brian Cain +Brian Cain Christian Borntraeger +Damien Hedde Filip Bozuta Frederic Konrad Frederic Konrad @@ -62,8 +86,12 @@ 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 @@ -73,7 +101,11 @@ Paul Burton Philippe Mathieu-Daudé Philippe Mathieu-Daudé Philippe Mathieu-Daudé +Roman Bolshakov +Sriram Yagnaraman 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 fb3baabca9..8fc1ae0cf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ os: linux -dist: focal +dist: jammy language: c compiler: - gcc @@ -7,50 +7,11 @@ cache: # There is one cache per branch and compiler version. # characteristics of each job are used to identify the cache: # - OS name (currently only linux) - # - OS distribution (for Linux, bionic or focal) + # - OS distribution (e.g. "jammy" for Linux) # - Names and values of visible environment variables set in .travis.yml or Settings panel timeout: 1200 ccache: true pip: true - directories: - - $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 @@ -71,8 +32,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 @@ -120,7 +81,6 @@ jobs: - name: "[aarch64] GCC check-tcg" arch: arm64 - dist: focal addons: apt_packages: - libaio-dev @@ -128,6 +88,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -145,16 +106,17 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS} --cxx=/bin/false" - - UNRELIABLE=true + - CONFIG="--disable-containers --enable-fdt=system + --target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false" - - name: "[ppc64] GCC check-tcg" + - name: "[ppc64] Clang check-tcg" arch: ppc64le - dist: focal + compiler: clang addons: apt_packages: - libaio-dev @@ -162,6 +124,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -179,15 +142,16 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - 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 - dist: focal addons: apt_packages: - libaio-dev @@ -195,6 +159,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -212,12 +177,13 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user" - - UNRELIABLE=true + - CONFIG="--disable-containers + --target-list=hppa-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu" script: - BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$? - | @@ -228,15 +194,16 @@ jobs: $(exit $BUILD_RC); fi - - name: "[s390x] GCC (other-softmmu)" + - name: "[s390x] Clang (other-system)" arch: s390x - dist: focal + compiler: clang addons: apt_packages: - libaio-dev - libattr1-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgnutls28-dev - libiscsi-dev - liblttng-ust-dev @@ -250,29 +217,31 @@ jobs: - libsnappy-dev - libzstd-dev - nettle-dev - - xfslibs-dev - ninja-build + - python3-tomli # Tests dependencies - genisoimage env: - CONFIG="--disable-containers --audio-drv-list=sdl --disable-user - --target-list-exclude=${MAIN_SOFTMMU_TARGETS}" + --target-list=arm-softmmu,avr-softmmu,microblaze-softmmu,sh4eb-softmmu,sparc64-softmmu,xtensaeb-softmmu" - name: "[s390x] GCC (user)" arch: s390x - dist: focal addons: apt_packages: - libgcrypt20-dev - libglib2.0-dev - libgnutls28-dev - ninja-build + - flex + - bison + - python3-tomli env: + - TEST_CMD="make check check-tcg V=1" - CONFIG="--disable-containers --disable-system" - name: "[s390x] Clang (disable-tcg)" arch: s390x - dist: focal compiler: clang addons: apt_packages: @@ -281,6 +250,7 @@ jobs: - libbrlapi-dev - libcacard-dev - libcap-ng-dev + - libfdt-dev - libgcrypt20-dev - libgnutls28-dev - libgtk-3-dev @@ -298,8 +268,8 @@ jobs: - libvdeplug-dev - libvte-2.91-dev - ninja-build + - python3-tomli env: - TEST_CMD="make check-unit" - - CONFIG="--disable-containers --disable-tcg --enable-kvm - --disable-tools --host-cc=clang --cxx=clang++" - - UNRELIABLE=true + - CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools + --enable-fdt=system --host-cc=clang --cxx=clang++" diff --git a/Kconfig b/Kconfig index fb6a24a2de..63ca7f46df 100644 --- a/Kconfig +++ b/Kconfig @@ -4,3 +4,4 @@ source accel/Kconfig source target/Kconfig source hw/Kconfig source semihosting/Kconfig +source rust/Kconfig diff --git a/Kconfig.host b/Kconfig.host index d763d89269..842cbe0d6c 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -5,12 +5,21 @@ config LINUX bool +config LIBCBOR + bool + +config GNUTLS + bool + config OPENGL bool config X11 bool +config PIXMAN + bool + config SPICE bool @@ -20,6 +29,9 @@ config IVSHMEM config TPM bool +config FDT + bool + config VHOST_USER bool @@ -32,9 +44,6 @@ config VHOST_KERNEL config VIRTFS bool -config PVRDMA - bool - config MULTIPROCESS_ALLOWED bool imply MULTIPROCESS @@ -46,3 +55,9 @@ config FUZZ config VFIO_USER_SERVER_ALLOWED bool imply VFIO_USER_SERVER + +config HV_BALLOON_POSSIBLE + bool + +config HAVE_RUST + bool diff --git a/MAINTAINERS b/MAINTAINERS index 6966490c94..aaf0505a21 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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,6 +92,7 @@ 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 @@ -112,6 +127,21 @@ 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 +R: Zhao Liu +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 +F: tools/i386/ Guest CPU cores (TCG) --------------------- @@ -119,9 +149,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 @@ -129,12 +161,22 @@ 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/exec/page-protection.h 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 @@ -157,6 +199,7 @@ 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 @@ -180,46 +223,49 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/avocado/machine_avr6.py - -CRIS TCG CPUs -M: Edgar E. Iglesias -S: Maintained -F: target/cris/ -F: hw/cris/ -F: include/hw/cris/ -F: tests/tcg/cris/ -F: disas/cris.c +F: tests/functional/test_avr_mega2560.py 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 +T: git https://github.com/quic/qemu.git hex-next + +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/functional/test_loongarch64_virt.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 @@ -228,66 +274,81 @@ 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é R: Aurelien Jarno R: Jiaxun Yang -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Odd Fixes F: target/mips/ F: disas/*mips.c F: docs/system/cpu-models-mips.rst.inc F: tests/tcg/mips/ -NiosII TCG CPUs -M: Chris Wulff -M: Marek Vasut -S: Maintained -F: target/nios2/ -F: hw/nios2/ -F: disas/nios2.c -F: configs/devices/nios2-softmmu/default.mak -F: tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh - 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: Nicholas Piggin M: Daniel Henrique Barboza -R: Cédric Le Goater -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*/* +F: tests/functional/test_ppc_74xx.py RISC-V TCG CPUs M: Palmer Dabbelt M: Alistair Francis -M: Bin Meng +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/functional/test_riscv* +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: target/riscv/th_* +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 @@ -312,6 +373,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 @@ -322,6 +384,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 @@ -332,6 +395,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 @@ -342,6 +406,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 @@ -385,11 +450,9 @@ F: target/mips/kvm* F: target/mips/sysemu/ PPC KVM CPUs -M: Daniel Henrique Barboza -R: Cédric Le Goater -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 @@ -399,8 +462,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 @@ -416,6 +477,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 @@ -423,7 +493,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 @@ -436,14 +506,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/ @@ -460,8 +530,9 @@ Guest CPU Cores (Xen) --------------------- X86 Xen CPUs M: Stefano Stabellini -M: Anthony Perard +M: Anthony PERARD M: Paul Durrant +M: Edgar E. Iglesias L: xen-devel@lists.xenproject.org S: Supported F: */xen* @@ -482,22 +553,12 @@ F: include/hw/xen/ 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/ +F: docs/system/arm/xenpvh.rst +F: docs/system/i386/xenpvh.rst Guest CPU Cores (NVMM) ---------------------- NetBSD Virtual Machine Monitor (NVMM) CPU support -M: Kamil Rytarowski M: Reinoud Zandijk S: Maintained F: include/sysemu/nvmm.h @@ -511,6 +572,7 @@ M: Cornelia Huck M: Paolo Bonzini S: Maintained F: linux-headers/ +F: include/standard-headers/ F: scripts/update-linux-headers.sh POSIX @@ -522,7 +584,6 @@ F: util/*posix*.c F: include/qemu/*posix*.h NETBSD -M: Kamil Rytarowski M: Reinoud Zandijk M: Ryo ONODERA S: Maintained @@ -563,12 +624,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 @@ -578,6 +642,7 @@ F: hw/*/allwinner-h3* F: include/hw/*/allwinner-h3* F: hw/arm/orangepi.c F: docs/system/arm/orangepi.rst +F: tests/functional/test_arm_orangepi.py ARM PrimeCell and CMSDK devices M: Peter Maydell @@ -591,6 +656,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 @@ -621,7 +687,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 @@ -638,6 +704,14 @@ F: include/hw/timer/armv7m_systick.h F: include/hw/misc/armv7m_ras.h F: tests/qtest/test-arm-mptimer.c +B-L475E-IOT01A IoT Node +M: Samuel Tardieu +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/b-l475e-iot01a.c +F: hw/display/dm163.c +F: tests/qtest/dm163-test.c + Exynos M: Igor Mitsyanko M: Peter Maydell @@ -645,6 +719,7 @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/exynos* F: include/hw/*/exynos* +F: docs/system/arm/exynos.rst Calxeda Highbank M: Rob Herring @@ -663,7 +738,7 @@ S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* -F: tests/avocado/machine_arm_canona1100.py +F: tests/functional/test_arm_canona1100.py F: docs/system/arm/digic.rst Goldfish RTC @@ -674,14 +749,6 @@ S: Maintained F: hw/rtc/goldfish_rtc.c F: include/hw/rtc/goldfish_rtc.h -Gumstix -M: Peter Maydell -R: Philippe Mathieu-Daudé -L: qemu-arm@nongnu.org -S: Odd Fixes -F: hw/arm/gumstix.c -F: docs/system/arm/gumstix.rst - i.MX25 PDK M: Peter Maydell R: Jean-Christophe Dubois @@ -714,7 +781,7 @@ S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h -F: tests/avocado/machine_arm_integratorcp.py +F: tests/functional/test_arm_integratorcp.py F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul @@ -727,6 +794,7 @@ F: hw/arm/fsl-imx6ul.c F: hw/misc/imx6ul_ccm.c F: include/hw/arm/fsl-imx6ul.h F: include/hw/misc/imx6ul_ccm.h +F: docs/system/arm/mcimx6ul-evk.rst MCIMX7D SABRE / i.MX7 M: Peter Maydell @@ -740,13 +808,15 @@ F: include/hw/arm/fsl-imx7.h F: include/hw/misc/imx7_*.h F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h +F: docs/system/arm/mcimx7d-sabre.rst -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 @@ -788,43 +858,19 @@ 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 -nSeries -M: Peter Maydell -L: qemu-arm@nongnu.org -S: Odd Fixes -F: hw/arm/nseries.c -F: hw/display/blizzard.c -F: hw/input/lm832x.c -F: hw/input/tsc2005.c -F: hw/misc/cbus.c -F: hw/rtc/twl92230.c -F: include/hw/display/blizzard.h -F: include/hw/input/lm832x.h -F: include/hw/input/tsc2xxx.h -F: include/hw/misc/cbus.h -F: tests/avocado/machine_arm_n8x0.py -F: docs/system/arm/nseries.rst - -Palm -M: Peter Maydell -L: qemu-arm@nongnu.org -S: Odd Fixes -F: hw/arm/palm.c -F: hw/input/tsc210x.c -F: include/hw/input/tsc2xxx.h -F: docs/system/arm/palm.rst - Raspberry Pi M: Peter Maydell R: Philippe Mathieu-Daudé @@ -833,9 +879,12 @@ 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 +F: tests/functional/test_arm_raspi2.py +F: tests/functional/test_aarch64_raspi3.py +F: tests/functional/test_aarch64_raspi4.py Real View M: Peter Maydell @@ -847,32 +896,12 @@ F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h F: docs/system/arm/realview.rst -PXA2XX -M: Peter Maydell -L: qemu-arm@nongnu.org -S: Odd Fixes -F: hw/arm/mainstone.c -F: hw/arm/spitz.c -F: hw/arm/tosa.c -F: hw/arm/z2.c -F: hw/*/pxa2xx* -F: hw/display/tc6393xb.c -F: hw/gpio/max7310.c -F: hw/gpio/zaurus.c -F: hw/misc/mst_fpga.c -F: hw/adc/max111x.c -F: include/hw/adc/max111x.h -F: include/hw/arm/pxa.h -F: include/hw/arm/sharpsl.h -F: include/hw/display/tc6393xb.h -F: docs/system/arm/xscale.rst -F: docs/system/arm/mainstone.rst - SABRELITE / i.MX6 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 @@ -887,10 +916,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/functional/test_aarch64_sbsaref*.py Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -898,16 +932,34 @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/collie.c F: hw/arm/strongarm* +F: hw/gpio/zaurus.c +F: include/hw/arm/sharpsl.h F: docs/system/arm/collie.rst +F: tests/functional/test_arm_collie.py Stellaris 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 +STM32L4x5 SoC Family +M: Samuel Tardieu +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/stm32l4x5_soc.c +F: hw/char/stm32l4x5_usart.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 +F: tests/qtest/stm32l4x5* + STM32VLDISCOVERY M: Alexandre Iooss L: qemu-arm@nongnu.org @@ -920,13 +972,16 @@ 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 +F: tests/functional/test_arm_vexpress.py Versatile PB 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 @@ -938,7 +993,9 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/avocado/machine_aarch64_virt.py +F: tests/functional/test_aarch64_virt.py +F: tests/functional/test_aarch64_tuxrun.py +F: tests/functional/test_arm_tuxrun.py Xilinx Zynq M: Edgar E. Iglesias @@ -953,6 +1010,7 @@ F: hw/adc/zynq-xadc.c F: include/hw/misc/zynq_slcr.h F: include/hw/adc/zynq-xadc.h X: hw/ssi/xilinx_* +F: docs/system/arm/xlnx-zynq.rst Xilinx ZynqMP and Versal M: Alistair Francis @@ -966,18 +1024,23 @@ F: include/hw/ssi/xilinx_spips.h F: hw/display/dpcd.c F: include/hw/display/dpcd.h F: docs/system/arm/xlnx-versal-virt.rst +F: docs/system/arm/xlnx-zcu102.rst Xilinx Versal OSPI -M: Francisco Iglesias +M: Francisco Iglesias 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 @@ -1006,6 +1069,8 @@ S: Maintained F: hw/arm/stm32f405_soc.c F: hw/misc/stm32f4xx_syscfg.c F: hw/misc/stm32f4xx_exti.c +F: hw/misc/stm32_rcc.c +F: include/hw/misc/stm32_rcc.h Netduino 2 M: Alistair Francis @@ -1021,6 +1086,12 @@ 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 + SmartFusion2 M: Subbaraya Sundeep M: Peter Maydell @@ -1048,18 +1119,21 @@ F: docs/system/arm/emcraft-sf2.rst ASPEED BMCs M: Cédric Le Goater M: Peter Maydell -R: Andrew Jeffery +R: Steven Lee +R: Troy Lee +R: Jamin Lin +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: docs/system/arm/fby35.rst +F: tests/*/*aspeed* +F: tests/*/*ast2700* F: hw/arm/fby35.c NRF51 @@ -1074,6 +1148,11 @@ F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c F: docs/system/arm/nrf.rst +ARM PL011 Rust device +M: Manos Pitsidianakis +S: Maintained +F: rust/hw/char/pl011/ + AVR Machines ------------- @@ -1094,35 +1173,34 @@ M: Philippe Mathieu-Daudé S: Maintained F: hw/avr/arduino.c -CRIS Machines -------------- -Axis Dev88 -M: Edgar E. Iglesias -S: Maintained -F: hw/cris/axis_dev88.c -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 +M: Bibo Mao +R: Jiaxun Yang S: Maintained F: docs/system/loongarch/virt.rst F: configs/targets/loongarch64-softmmu.mak @@ -1130,7 +1208,9 @@ F: configs/devices/loongarch64-softmmu/default.mak F: hw/loongarch/ F: include/hw/loongarch/virt.h F: include/hw/intc/loongarch_*.h +F: include/hw/intc/loongson_ipi_common.h F: hw/intc/loongarch_*.c +F: hw/intc/loongson_ipi_common.c F: include/hw/pci-host/ls7a.h F: hw/rtc/ls7a_rtc.c F: gdb-xml/loongarch*.xml @@ -1158,15 +1238,20 @@ S: Odd Fixes F: hw/m68k/next-*.c F: hw/display/next-fb.c F: include/hw/m68k/next-cube.h +F: tests/functional/test_m68k_nextcube.py 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 @@ -1174,6 +1259,12 @@ 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 +F: tests/functional/test_m68k_q800.py virt M: Laurent Vivier @@ -1187,6 +1278,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 ------------------- @@ -1195,7 +1287,7 @@ M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h -F: tests/avocado/machine_microblaze.py +F: tests/functional/test_microblaze*.py petalogix_ml605 M: Edgar E. Iglesias @@ -1213,26 +1305,30 @@ F: include/hw/mips/ Jazz M: Hervé Poussineau -R: Aleksandar Rikalo +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é 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 +F: tests/functional/test_mips*_malta.py +F: tests/functional/test_mips*_tuxrun.py Mipssim -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Orphan F: hw/mips/mipssim.c F: hw/net/mipsnet.c @@ -1243,25 +1339,28 @@ 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: tests/avocado/machine_mips_fuloong2e.py +F: include/hw/pci-host/bonito.h +F: tests/functional/test_mips64el_fuloong2e.py Loongson-3 virtual platforms M: Huacai Chen R: Jiaxun Yang S: Maintained +F: hw/intc/loongson_ipi_common.c +F: hw/intc/loongson_ipi.c 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: tests/avocado/machine_mips_loongson3v.py +F: include/hw/intc/loongson_ipi_common.h +F: include/hw/intc/loongson_ipi.h +F: include/hw/intc/loongson_liointc.h +F: tests/functional/test_mips64el_loongson3v.py Boston M: Paul Burton -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Odd Fixes F: hw/core/loader-fit.c F: hw/mips/boston.c @@ -1273,6 +1372,8 @@ 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 @@ -1280,18 +1381,22 @@ PowerPC Machines 405 (ref405ep) L: qemu-ppc@nongnu.org S: Orphan -F: hw/ppc/ppc405_boards.c +F: hw/ppc/ppc405* +F: tests/functional/test_ppc_405.py Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c -F: tests/avocado/ppc_bamboo.py +F: hw/pci-host/ppc4xx_pci.c +F: tests/functional/test_ppc_bamboo.py e500 +M: Bernhard Beschow L: qemu-ppc@nongnu.org -S: Orphan +S: Odd Fixes 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/ @@ -1299,15 +1404,19 @@ 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 +F: tests/functional/test_ppc64_e500.py +F: tests/functional/test_ppc_tuxrun.py mpc8544ds +M: Bernhard Beschow L: qemu-ppc@nongnu.org -S: Orphan +S: Odd Fixes F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -F: tests/avocado/ppc_mpc8544ds.py +F: tests/functional/test_ppc_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland @@ -1320,6 +1429,7 @@ 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 @@ -1328,6 +1438,7 @@ F: include/hw/ppc/mac_dbdma.h F: include/hw/pci-host/uninorth.h F: include/hw/input/adb* F: pc-bios/qemu_vga.ndrv +F: tests/functional/test_ppc_mac.py Old World (g3beige) M: Mark Cave-Ayland @@ -1343,6 +1454,7 @@ 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 +F: tests/functional/test_ppc_mac.py PReP M: Hervé Poussineau @@ -1359,19 +1471,22 @@ F: hw/dma/i82374.c F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h F: include/hw/rtc/m48t59.h -F: tests/avocado/ppc_prep_40p.py +F: tests/functional/test_ppc_40p.py sPAPR (pseries) -M: Daniel Henrique Barboza -R: Cédric Le Goater -R: David Gibson -R: Greg Kurz +M: Nicholas Piggin +R: Daniel Henrique Barboza +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-* @@ -1379,10 +1494,13 @@ F: tests/qtest/spapr* F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* -F: tests/avocado/ppc_pseries.py +F: tests/functional/test_ppc64_pseries.py +F: tests/functional/test_ppc64_hv.py +F: tests/functional/test_ppc64_tuxrun.py PowerNV (Non-Virtualized) -M: Cédric Le Goater +M: Nicholas Piggin +R: Frédéric Barrat L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst @@ -1390,30 +1508,43 @@ F: hw/ppc/pnv* F: hw/intc/pnv* F: hw/intc/xics_pnv.c F: hw/pci-host/pnv* +F: hw/ssi/pnv_spi.c F: include/hw/ppc/pnv* F: include/hw/pci-host/pnv* +F: include/hw/ssi/pnv_spi* F: pc-bios/skiboot.lid F: tests/qtest/pnv* +F: tests/functional/test_ppc64_powernv.py + +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 S: Odd Fixes F: hw/ppc/virtex_ml507.c -F: tests/avocado/ppc_virtex_ml507.py +F: tests/functional/test_ppc_virtex_ml507.py sam460ex 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 @@ -1424,14 +1555,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 +F: tests/functional/test_ppc_amiga.py + +Virtual Open Firmware (VOF) +M: Alexey Kardashevskiy +L: qemu-ppc@nongnu.org +S: Odd Fixes F: hw/ppc/spapr_vof* F: hw/ppc/vof* F: include/hw/ppc/vof* @@ -1450,9 +1586,10 @@ F: include/hw/riscv/opentitan.h F: include/hw/*/ibex_*.h Microchip PolarFire SoC Icicle Kit -M: Bin Meng +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 @@ -1468,6 +1605,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 @@ -1475,10 +1613,11 @@ F: include/hw/char/shakti_uart.h SiFive Machines M: Alistair Francis -M: Bin Meng +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 @@ -1489,7 +1628,7 @@ R: Yoshinori Sato S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c -F: tests/avocado/machine_rx_gdbsim.py +F: tests/functional/test_rx_gdbsim.py SH4 Machines ------------ @@ -1503,17 +1642,9 @@ 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 - -Shix -R: Yoshinori Sato -R: Magnus Damm -S: Odd Fixes -F: hw/block/tc58128.c -F: hw/char/sh_serial.c -F: hw/sh4/shix.c -F: hw/intc/sh_intc.c -F: hw/timer/sh_timer.c -F: include/hw/sh4/sh_intc.h +F: include/hw/timer/tmu012.h +F: tests/functional/test_sh4_r2d.py +F: tests/functional/test_sh4_tuxrun.py SPARC Machines -------------- @@ -1531,6 +1662,7 @@ F: include/hw/nvram/sun_nvram.h F: include/hw/sparc/sparc32_dma.h F: include/hw/sparc/sun4m_iommu.h F: pc-bios/openbios-sparc32 +F: tests/functional/test_sparc_sun4m.py Sun4u M: Mark Cave-Ayland @@ -1543,7 +1675,8 @@ F: include/hw/pci-host/sabre.h F: hw/pci-bridge/simba.c F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 -F: tests/avocado/machine_sparc64_sun4u.py +F: tests/functional/test_sparc64_sun4u.py +F: tests/functional/test_sparc64_tuxrun.py Sun4v M: Artyom Tarasenko @@ -1553,13 +1686,12 @@ 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 F: hw/*/grlib* F: include/hw/*/grlib* -F: tests/avocado/machine_sparc_leon3.py S390 Machines ------------- @@ -1571,7 +1703,7 @@ S: Supported F: hw/s390x/ F: include/hw/s390x/ F: configs/devices/s390x-softmmu/default.mak -F: tests/avocado/machine_s390_ccw_virtio.py +F: tests/functional/test_s390x_* T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1626,6 +1758,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/functional/test_s390x_topology.py + X86 Machines ------------ PC @@ -1640,18 +1782,23 @@ 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 +F: tests/functional/test_i386_tuxrun.py +F: tests/functional/test_mem_addr_space.py +F: tests/functional/test_pc_cpu_hotplug_props.py +F: tests/functional/test_x86_64_tuxrun.py +F: tests/functional/test_x86_cpu_model_versions.py PC Chipset M: Michael S. Tsirkin @@ -1675,10 +1822,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 @@ -1694,14 +1843,25 @@ F: hw/i386/microvm.c F: include/hw/i386/microvm.h F: pc-bios/bios-microvm.bin +nitro-enclave +M: Alexander Graf +M: Dorjoy Chowdhury +S: Maintained +F: hw/core/eif.c +F: hw/core/eif.h +F: hw/i386/nitro_enclave.c +F: include/hw/i386/nitro_enclave.h +F: docs/system/i386/nitro-enclave.rst + Machine core M: Eduardo Habkost M: Marcel Apfelbaum R: Philippe Mathieu-Daudé R: Yanan Wang +R: Zhao Liu 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 @@ -1709,11 +1869,14 @@ 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 F: include/hw/cpu/cluster.h F: include/sysemu/numa.h +F: tests/functional/test_cpu_queries.py +F: tests/functional/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -1734,6 +1897,8 @@ M: Max Filippov S: Maintained F: hw/xtensa/xtfpga.c F: hw/net/opencores_eth.c +F: include/hw/xtensa/mx_pic.h +F: tests/functional/test_xtensa_lx60.py Devices ------- @@ -1748,23 +1913,22 @@ F: tests/qtest/intel-hda-test.c F: tests/qtest/fuzz-sb16-test.c Xilinx CAN -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 @@ -1788,7 +1952,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 @@ -1839,7 +2003,7 @@ 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 @@ -1857,6 +2021,18 @@ 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 @@ -1864,11 +2040,11 @@ F: hw/acpi/viot.c F: hw/acpi/viot.h ACPI/AVOCADO/BIOSBITS -M: Ani Sinha +M: Ani Sinha M: Michael S. Tsirkin S: Supported -F: tests/avocado/acpi-bits/* -F: tests/avocado/acpi-bits.py +F: tests/functional/acpi-bits/* +F: tests/functional/test_acpi_bits.py F: docs/devel/acpi-bits.rst ACPI/HEST/GHES @@ -1882,8 +2058,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 @@ -1894,6 +2073,7 @@ M: Marc-André Lureau R: Paolo Bonzini S: Odd Fixes F: hw/char/ +F: include/hw/char/ Network devices M: Jason Wang @@ -1901,6 +2081,7 @@ S: Odd Fixes F: hw/net/ F: include/hw/net/ F: tests/qtest/virtio-net-test.c +F: tests/functional/test_info_usernet.py F: docs/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -1939,7 +2120,7 @@ F: hw/ssi/xilinx_* SD (Secure Card) M: Philippe Mathieu-Daudé -M: Bin Meng +M: Bin Meng L: qemu-block@nongnu.org S: Odd Fixes F: include/hw/sd/sd* @@ -1950,8 +2131,7 @@ F: tests/qtest/fuzz-sdcard-test.c F: tests/qtest/sdhci-test.c USB -M: Gerd Hoffmann -S: Odd Fixes +S: Orphan F: hw/usb/* F: stubs/usb-dev-stub.c F: tests/qtest/usb-*-test.c @@ -1960,18 +2140,19 @@ F: include/hw/usb.h F: include/hw/usb/ USB (serial adapter) -R: Gerd Hoffmann M: Samuel Thibault S: Maintained 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 +F: qapi/vfio.json vfio-ccw M: Eric Farman @@ -1996,8 +2177,22 @@ 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: backends/host_iommu_device.c +F: include/sysemu/host_iommu_device.h +F: include/qemu/chardev_open.h +F: util/chardev_open.c +F: docs/devel/vfio-iommufd.rst + vhost M: Michael S. Tsirkin +R: Stefano Garzarella S: Supported F: hw/*/*vhost* F: docs/interop/vhost-user.json @@ -2007,6 +2202,10 @@ 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 S: Supported @@ -2016,6 +2215,9 @@ 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 +F: tests/functional/test_virtio_version.py virtio-balloon M: Michael S. Tsirkin @@ -2024,19 +2226,20 @@ 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 +F: tests/qtest/virtio-balloon-test.c 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* F: fsdev/ -F: docs/tools/virtfs-proxy-helper.rst 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 @@ -2062,21 +2265,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/* @@ -2105,10 +2313,26 @@ F: include/sysemu/rng*.h F: backends/rng*.c F: tests/qtest/virtio-rng-test.c +virtio-nsm +M: Alexander Graf +M: Dorjoy Chowdhury +S: Maintained +F: hw/virtio/cbor-helpers.c +F: hw/virtio/virtio-nsm.c +F: hw/virtio/virtio-nsm-pci.c +F: include/hw/virtio/cbor-helpers.h +F: include/hw/virtio/virtio-nsm.h + +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 @@ -2122,6 +2346,20 @@ 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 @@ -2129,6 +2367,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 @@ -2138,17 +2383,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 @@ -2160,6 +2422,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 @@ -2169,9 +2432,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 @@ -2179,18 +2448,32 @@ S: Maintained F: hw/net/rocker/ F: qapi/rocker.json F: tests/rocker/ -F: docs/specs/rocker.txt +F: docs/specs/rocker.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/functional/test_netdev_ethtool.py +F: tests/qtest/igb-test.c +F: tests/qtest/libqos/igb.c eepro100 M: Stefan Weil @@ -2204,11 +2487,17 @@ F: hw/net/tulip.c F: hw/net/tulip.h pca954x -M: Patrick Venture +M: Patrick Leis 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 @@ -2242,9 +2531,8 @@ 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é @@ -2275,6 +2563,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 @@ -2283,14 +2572,14 @@ F: hw/display/ramfb*.c F: include/hw/display/ramfb.h virtio-gpu -M: Gerd Hoffmann -S: Odd Fixes +S: Orphan 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/ @@ -2305,7 +2594,6 @@ F: include/hw/virtio/virtio-blk-common.h vhost-user-gpu M: Marc-André Lureau -R: Gerd Hoffmann S: Maintained F: docs/interop/vhost-user-gpu.rst F: contrib/vhost-user-gpu @@ -2328,9 +2616,18 @@ PIIX4 South Bridge (i82371AB) M: Hervé Poussineau 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é R: Gerd Hoffmann @@ -2345,11 +2642,12 @@ F: tests/qtest/fw_cfg-test.c 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: Odd Fixes F: hw/*/*xive* F: include/hw/*/*xive* +F: tests/qtest/*xive* F: docs/*/*xive* Renesas peripherals @@ -2374,12 +2672,13 @@ F: include/hw/rx/ CAN bus subsystem and hardware M: Pavel Pisa -M: Vikram Garhwal +M: Francisco Iglesias S: Maintained 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 @@ -2423,7 +2722,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 @@ -2431,7 +2730,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 @@ -2451,10 +2750,19 @@ 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 @@ -2478,7 +2786,7 @@ Core Audio framework backend M: Gerd Hoffmann M: Philippe Mathieu-Daudé R: Christian Schoenebeck -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: audio/coreaudio.c @@ -2553,11 +2861,12 @@ 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 @@ -2600,12 +2909,13 @@ T: git https://gitlab.com/jsnow/qemu.git jobs 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 @@ -2644,6 +2954,7 @@ S: Supported F: include/qemu/option.h F: tests/unit/test-keyval.c F: tests/unit/test-qemu-opts.c +F: tests/functional/test_version.py F: util/keyval.c F: util/qemu-option.c @@ -2661,7 +2972,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 @@ -2676,6 +2987,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 @@ -2684,7 +2996,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 @@ -2696,11 +3008,15 @@ GDB stub M: Alex Bennée R: Philippe Mathieu-Daudé S: Maintained +F: docs/system/gdb.rst F: gdbstub/* F: include/exec/gdbstub.h +F: include/gdbstub/* F: gdb-xml/ -F: tests/tcg/multiarch/gdbstub/ -F: scripts/feature_to_c.sh +F: tests/tcg/multiarch/gdbstub/* +F: scripts/feature_to_c.py +F: scripts/probe-gdb-support.py +T: git https://gitlab.com/stsquad/qemu gdbstub/next Memory API M: Paolo Bonzini @@ -2714,11 +3030,11 @@ 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 @@ -2733,11 +3049,11 @@ 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 +S: Orphan F: include/ui/qemu-spice.h F: include/ui/spice-display.h F: ui/spice-*.c @@ -2747,49 +3063,51 @@ F: qapi/ui.json 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 +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: softmmu/runstate.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.rst +F: docs/devel/rcu.rst F: include/qemu/rcu*.h +F: include/qemu/lockcnt.h F: tests/unit/rcutorture.c F: tests/unit/test-rcu-*.c +F: util/lockcnt.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 @@ -2819,24 +3137,30 @@ 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 M: Cleber Rosa -R: Beraldo Leal S: Maintained F: python/ T: git https://gitlab.com/jsnow/qemu.git python @@ -2886,6 +3210,7 @@ M: Eric Blake M: Markus Armbruster S: Supported F: qapi/*.json +F: qga/qapi-schema.json T: git https://repo.or.cz/qemu/armbru.git qapi-next QObject @@ -2911,10 +3236,11 @@ 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 @@ -2930,6 +3256,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 @@ -2940,7 +3267,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 @@ -2970,27 +3298,31 @@ F: tests/qtest/qmp-cmd-test.c T: git https://repo.or.cz/qemu/armbru.git qapi-next qtest -M: Thomas Huth +M: Fabiano Rosas 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* +X: tests/qtest/migration-* Device Fuzzing M: Alexander Bulekov R: Paolo Bonzini R: Bandan Das R: Stefan Hajnoczi -R: Thomas Huth +R: Fabiano Rosas R: Darren Kenny 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 @@ -3002,6 +3334,17 @@ F: hw/core/register.c F: include/hw/register.h F: include/hw/registerfields.h +Rust +M: Manos Pitsidianakis +S: Maintained +F: rust/qemu-api +F: rust/qemu-api-macros +F: rust/rustfmt.toml + +Rust-related patches CC here +L: qemu-rust@nongnu.org +F: rust/ + SLIRP M: Samuel Thibault S: Maintained @@ -3009,6 +3352,12 @@ 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 @@ -3022,6 +3371,7 @@ F: stubs/ Tracing M: Stefan Hajnoczi +R: Mads Ynddal S: Maintained F: trace/ F: trace-events @@ -3034,36 +3384,73 @@ 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 +SPDM +M: Alistair Francis +S: Maintained +F: backends/spdm-socket.c +F: include/sysemu/spdm-socket.h + Checkpatch 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: tests/qtest/migration-* +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 +F: docs/devel/luks-detached-header.rst D-Bus M: Marc-André Lureau @@ -3078,11 +3465,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 @@ -3091,11 +3479,12 @@ 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-* F: tests/unit/crypto-tls-* -F: tests/unit/pkix_asn1_tab.c +F: tests/unit/pkix_asn1_tab.c.inc F: qemu.sasl Coroutines @@ -3177,7 +3566,7 @@ F: include/migration/failover.h F: docs/COLO-FT.txt COLO Proxy -M: Zhang Chen +M: Zhang Chen M: Li Zhijian S: Supported F: docs/colo-proxy.txt @@ -3194,8 +3583,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 @@ -3210,9 +3601,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 @@ -3238,6 +3636,15 @@ 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é R: Daniel P. Berrange @@ -3257,18 +3664,21 @@ 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 +R: Yi Liu +R: Clément Mathieu--Drif 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 @@ -3278,7 +3688,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 @@ -3287,6 +3697,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 @@ -3313,7 +3733,6 @@ F: configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh -F: scripts/gensyscalls.sh Tiny Code Generator (TCG) ------------------------- @@ -3325,14 +3744,17 @@ F: include/tcg/ TCG Plugins M: Alex Bennée +T: git https://gitlab.com/stsquad/qemu plugins/next R: Alexandre Iooss R: Mahmoud Mandour +R: Pierrick Bouvier S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ -F: tests/plugin/ -F: tests/avocado/tcg_plugins.py +F: tests/tcg/plugins/ +F: tests/functional/test_aarch64_tcg_plugins.py F: contrib/plugins/ +F: scripts/qemu-plugin-symbols.py AArch64 TCG target M: Richard Henderson @@ -3361,7 +3783,7 @@ M: Philippe Mathieu-Daudé R: Aurelien Jarno R: Huacai Chen R: Jiaxun Yang -R: Aleksandar Rikalo +R: Aleksandar Rikalo S: Odd Fixes F: tcg/mips/ @@ -3376,7 +3798,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 @@ -3406,7 +3828,7 @@ F: block/vmdk.c RBD M: Ilya Dryomov -R: Peter Lieven +R: Peter Lieven L: qemu-block@nongnu.org S: Supported F: block/rbd.c @@ -3432,7 +3854,7 @@ F: block/blkio.c iSCSI M: Ronnie Sahlberg M: Paolo Bonzini -M: Peter Lieven +M: Peter Lieven L: qemu-block@nongnu.org S: Odd Fixes F: block/iscsi.c @@ -3448,14 +3870,14 @@ F: nbd/ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c -F: docs/interop/nbd.txt +F: docs/interop/nbd.rst 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://gitlab.com/vsementsov/qemu.git block NFS -M: Peter Lieven +M: Peter Lieven L: qemu-block@nongnu.org S: Maintained F: block/nfs.c @@ -3496,7 +3918,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 @@ -3515,6 +3937,7 @@ M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported F: block/blkverify.c +F: docs/devel/blkverify.rst bochs M: Stefan Hajnoczi @@ -3537,13 +3960,13 @@ 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://gitlab.com/vsementsov/qemu.git block +F: docs/interop/parallels.rst +F: docs/interop/prl-xml.rst +T: git https://src.openvz.org/scm/~den/qemu.git parallels qed M: Stefan Hajnoczi @@ -3592,6 +4015,7 @@ M: Hanna Reitz L: qemu-block@nongnu.org S: Supported F: block/blkdebug.c +F: docs/devel/blkdebug.rst vpc M: Kevin Wolf @@ -3646,16 +4070,6 @@ F: block/replication.c F: tests/unit/test-replication.c F: docs/block-replication.txt -PVRDMA -M: Yuval Shaia -M: Marcel Apfelbaum -S: Maintained -F: hw/rdma/* -F: hw/rdma/vmw/* -F: docs/pvrdma.txt -F: contrib/rdmacm-mux/* -F: qapi/rdma.json - Semihosting M: Alex Bennée S: Maintained @@ -3667,7 +4081,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 @@ -3698,6 +4111,7 @@ M: Jason Wang R: Andrew Melnychenko R: Yuri Benditovich S: Maintained +F: docs/devel/ebpf_rss.rst F: ebpf/* F: tools/ebpf/* @@ -3705,20 +4119,23 @@ Build and test automation ------------------------- Build and test automation, general continuous integration M: Alex Bennée +T: git https://gitlab.com/stsquad/qemu testing/next M: Philippe Mathieu-Daudé M: Thomas Huth R: Wainer dos Santos Moschetta -R: Beraldo Leal S: Maintained 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/functional/test_*_tuxrun.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 @@ -3730,11 +4147,15 @@ F: .gitlab-ci.d/cirrus/freebsd* F: tests/vm/freebsd W: https://cirrus-ci.com/github/qemu/qemu +Functional testing framework +M: Thomas Huth +R: Philippe Mathieu-Daudé +F: tests/functional/qemu_test/ + 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 @@ -3747,7 +4168,6 @@ W: https://trello.com/b/6Qi1pxVn/avocado-qemu R: Cleber Rosa R: Philippe Mathieu-Daudé R: Wainer dos Santos Moschetta -R: Beraldo Leal S: Odd Fixes F: tests/avocado/ @@ -3772,7 +4192,7 @@ 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 @@ -3781,7 +4201,6 @@ Meson M: Paolo Bonzini R: Marc-André Lureau R: Daniel P. Berrange -R: Thomas Huth R: Philippe Mathieu-Daudé S: Maintained F: meson.build @@ -3800,6 +4219,16 @@ 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 @@ -3816,6 +4245,17 @@ F: docs/conf.py F: docs/*/conf.py F: docs/sphinx/ F: docs/_templates/ +F: docs/devel/docs.rst + +Rust build system integration +M: Manos Pitsidianakis +L: qemu-rust@nongnu.org +S: Maintained +F: scripts/rust/ +F: rust/.gitignore +F: rust/Kconfig +F: rust/meson.build +F: rust/wrapper.h Miscellaneous ------------- @@ -3823,3 +4263,13 @@ 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/ + +Machine development tool +M: Maksim Davydov +S: Supported +F: scripts/compare-machine-types.py diff --git a/Makefile b/Makefile index 1270f698b4..7faa861765 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 @@ -45,18 +45,6 @@ include config-host.mak include Makefile.prereqs Makefile.prereqs: 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 - # 0. ensure the build tree is okay # Check that we're not trying to do an out-of-tree build from @@ -90,21 +78,23 @@ 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)/QEMU_VERSION +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/scripts/meson-buildoptions.sh \ + $(SRC_PATH)/pythondeps.toml $(SRC_PATH)/QEMU_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 @@ -115,15 +105,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 @@ -144,13 +142,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)) -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". @@ -167,19 +170,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 @@ -189,15 +182,15 @@ include $(SRC_PATH)/tests/Makefile.include all: recurse-all -ROMS_RULES=$(foreach t, all clean distclean, $(addsuffix /$(t), $(ROMS))) -.PHONY: $(ROMS_RULES) -$(ROMS_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 $@),) .PHONY: recurse-all recurse-clean -recurse-all: $(addsuffix /all, $(ROMS)) -recurse-clean: $(addsuffix /clean, $(ROMS)) -recurse-distclean: $(addsuffix /distclean, $(ROMS)) +recurse-all: $(addsuffix /all, $(SUBDIRS)) +recurse-clean: $(addsuffix /clean, $(SUBDIRS)) +recurse-distclean: $(addsuffix /distclean, $(SUBDIRS)) ###################################################################### @@ -210,6 +203,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)/QEMU_VERSION) @@ -220,7 +214,7 @@ qemu-%.tar.bz2: distclean: clean recurse-distclean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : - rm -f config-host.mak Makefile.prereqs qemu-bundle + 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 @@ -230,7 +224,7 @@ distclean: clean recurse-distclean 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" \) @@ -291,6 +285,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:' @@ -301,16 +302,14 @@ help: $(call print-help,cscope,Generate cscope index) $(call print-help,sparse,Run sparse on the QEMU source) @echo '' -ifeq ($(CONFIG_PLUGIN),y) - @echo 'Plugin targets:' - $(call print-help,plugins,Build the example TCG plugins) - @echo '' -endif @echo 'Cleaning targets:' $(call print-help,clean,Remove most generated files but keep the config) $(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) @@ -321,7 +320,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/QEMU_VERSION b/QEMU_VERSION index 2bbaead448..deeb3d66ef 100644 --- a/QEMU_VERSION +++ b/QEMU_VERSION @@ -1 +1 @@ -7.2.4 +9.2.0 diff --git a/README.rst b/README.rst index 21df79ef43..b120a1f69e 100644 --- a/README.rst +++ b/README.rst @@ -82,7 +82,7 @@ guidelines set out in the `style section the Developers Guide. Additional information on submitting patches can be found online via -the QEMU website +the QEMU website: * ``_ * ``_ @@ -102,7 +102,7 @@ requires a working 'git send-email' setup, and by default doesn't automate everything, so you may want to go through the above steps manually for once. -For installation instructions, please go to +For installation instructions, please go to: * ``_ @@ -159,7 +159,7 @@ Contact ======= The QEMU community can be contacted in a number of ways, with the two -main methods being email and IRC +main methods being email and IRC: * ``_ * ``_ 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..75daaa2911 --- /dev/null +++ b/accel/accel-blocker.c @@ -0,0 +1,155 @@ +/* + * 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/lockcnt.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-common.c b/accel/accel-common.c deleted file mode 100644 index df72cc989a..0000000000 --- a/accel/accel-common.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * QEMU accel class, components common to system emulation and user mode - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2014 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/accel.h" - -#include "cpu.h" -#include "hw/core/accel-cpu.h" - -#ifndef CONFIG_USER_ONLY -#include "accel-softmmu.h" -#endif /* !CONFIG_USER_ONLY */ - -static const TypeInfo accel_type = { - .name = TYPE_ACCEL, - .parent = TYPE_OBJECT, - .class_size = sizeof(AccelClass), - .instance_size = sizeof(AccelState), -}; - -/* Lookup AccelClass from opt_name. Returns NULL if not found */ -AccelClass *accel_find(const char *opt_name) -{ - char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); - AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name)); - g_free(class_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); - AccelCPUClass *accel_cpu = opaque; - - /* - * The first callback allows accel-cpu to run initializations - * for the CPU, customizing CPU behavior according to the accelerator. - * - * The second one allows the CPU to customize the accel-cpu - * behavior according to the CPU. - * - * The second is currently only used by TCG, to specialize the - * TCGCPUOps depending on the CPU type. - */ - cc->accel_cpu = accel_cpu; - if (accel_cpu->cpu_class_init) { - accel_cpu->cpu_class_init(cc); - } - if (cc->init_accel_cpu) { - cc->init_accel_cpu(accel_cpu, cc); - } -} - -/* initialize the arch-specific accel CpuClass interfaces */ -static void accel_init_cpu_interfaces(AccelClass *ac) -{ - const char *ac_name; /* AccelClass name */ - char *acc_name; /* AccelCPUClass name */ - ObjectClass *acc; /* AccelCPUClass */ - - ac_name = object_class_get_name(OBJECT_CLASS(ac)); - g_assert(ac_name != NULL); - - acc_name = g_strdup_printf("%s-%s", ac_name, CPU_RESOLVING_TYPE); - acc = object_class_by_name(acc_name); - g_free(acc_name); - - if (acc) { - object_class_foreach(accel_init_cpu_int_aux, - CPU_RESOLVING_TYPE, false, acc); - } -} - -void accel_init_interfaces(AccelClass *ac) -{ -#ifndef CONFIG_USER_ONLY - accel_init_ops_interfaces(ac); -#endif /* !CONFIG_USER_ONLY */ - - accel_init_cpu_interfaces(ac); -} - -void accel_cpu_instance_init(CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->accel_cpu && cc->accel_cpu->cpu_instance_init) { - cc->accel_cpu->cpu_instance_init(cpu); - } -} - -bool accel_cpu_realizefn(CPUState *cpu, Error **errp) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->accel_cpu && cc->accel_cpu->cpu_realizefn) { - return cc->accel_cpu->cpu_realizefn(cpu, errp); - } - return true; -} - -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, - .abstract = true, - .class_size = sizeof(AccelCPUClass), -}; - -static void register_accel_types(void) -{ - type_register_static(&accel_type); - type_register_static(&accel_cpu_type); -} - -type_init(register_accel_types); diff --git a/accel/accel-softmmu.c b/accel/accel-softmmu.c deleted file mode 100644 index f9cdafb148..0000000000 --- a/accel/accel-softmmu.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * QEMU accel class, system emulation components - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2014 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/accel.h" -#include "hw/boards.h" -#include "sysemu/cpus.h" - -#include "accel-softmmu.h" - -int accel_init_machine(AccelState *accel, MachineState *ms) -{ - AccelClass *acc = ACCEL_GET_CLASS(accel); - int ret; - ms->accelerator = accel; - *(acc->allowed) = true; - ret = acc->init_machine(ms); - if (ret < 0) { - ms->accelerator = NULL; - *(acc->allowed) = false; - object_unref(OBJECT(accel)); - } else { - object_set_accelerator_compat_props(acc->compat_props); - } - return ret; -} - -AccelState *current_accel(void) -{ - return current_machine->accelerator; -} - -void accel_setup_post(MachineState *ms) -{ - AccelState *accel = ms->accelerator; - AccelClass *acc = ACCEL_GET_CLASS(accel); - if (acc->setup_post) { - acc->setup_post(ms, accel); - } -} - -/* initialize the arch-independent accel operation interfaces */ -void accel_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)); - g_assert(ac_name != NULL); - - 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. - */ - g_assert(ops != NULL); - if (ops->ops_init) { - ops->ops_init(ops); - } - cpus_register_accel(ops); -} - -static const TypeInfo accel_ops_type_info = { - .name = TYPE_ACCEL_OPS, - .parent = TYPE_OBJECT, - .abstract = true, - .class_size = sizeof(AccelOpsClass), -}; - -static void accel_softmmu_register_types(void) -{ - type_register_static(&accel_ops_type_info); -} -type_init(accel_softmmu_register_types); diff --git a/accel/accel-softmmu.h b/accel/accel-softmmu.h deleted file mode 100644 index 5e192f1882..0000000000 --- a/accel/accel-softmmu.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * QEMU System Emulation accel internal 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. - */ - -#ifndef ACCEL_SOFTMMU_H -#define ACCEL_SOFTMMU_H - -void accel_init_ops_interfaces(AccelClass *ac); - -#endif /* ACCEL_SOFTMMU_H */ diff --git a/accel/accel-system.c b/accel/accel-system.c new file mode 100644 index 0000000000..61d689935e --- /dev/null +++ b/accel/accel-system.c @@ -0,0 +1,104 @@ +/* + * QEMU accel class, system emulation components + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 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/accel.h" +#include "hw/boards.h" +#include "sysemu/cpus.h" +#include "qemu/error-report.h" +#include "accel-system.h" + +int accel_init_machine(AccelState *accel, MachineState *ms) +{ + AccelClass *acc = ACCEL_GET_CLASS(accel); + int ret; + ms->accelerator = accel; + *(acc->allowed) = true; + ret = acc->init_machine(ms); + if (ret < 0) { + ms->accelerator = NULL; + *(acc->allowed) = false; + object_unref(OBJECT(accel)); + } else { + object_set_accelerator_compat_props(acc->compat_props); + } + return ret; +} + +AccelState *current_accel(void) +{ + return current_machine->accelerator; +} + +void accel_setup_post(MachineState *ms) +{ + AccelState *accel = ms->accelerator; + AccelClass *acc = ACCEL_GET_CLASS(accel); + if (acc->setup_post) { + acc->setup_post(ms, accel); + } +} + +/* initialize the arch-independent accel operation interfaces */ +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)); + g_assert(ac_name != NULL); + + ops_name = g_strdup_printf("%s" ACCEL_OPS_SUFFIX, ac_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); + /* + * all accelerators need to define ops, providing at least a mandatory + * non-NULL create_vcpu_thread operation. + */ + ops = ACCEL_OPS_CLASS(oc); + if (ops->ops_init) { + ops->ops_init(ops); + } + cpus_register_accel(ops); +} + +static const TypeInfo accel_ops_type_info = { + .name = TYPE_ACCEL_OPS, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(AccelOpsClass), +}; + +static void accel_system_register_types(void) +{ + type_register_static(&accel_ops_type_info); +} +type_init(accel_system_register_types); diff --git a/accel/accel-system.h b/accel/accel-system.h new file mode 100644 index 0000000000..2d37c73c97 --- /dev/null +++ b/accel/accel-system.h @@ -0,0 +1,15 @@ +/* + * QEMU System Emulation accel internal 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. + */ + +#ifndef ACCEL_SYSTEM_H +#define ACCEL_SYSTEM_H + +void accel_system_init_ops_interfaces(AccelClass *ac); + +#endif /* ACCEL_SYSTEM_H */ diff --git a/accel/accel-target.c b/accel/accel-target.c new file mode 100644 index 0000000000..08626c00c2 --- /dev/null +++ b/accel/accel-target.c @@ -0,0 +1,176 @@ +/* + * QEMU accel class, components common to system emulation and user mode + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 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/accel.h" + +#include "cpu.h" +#include "hw/core/accel-cpu.h" + +#ifndef CONFIG_USER_ONLY +#include "accel-system.h" +#endif /* !CONFIG_USER_ONLY */ + +static const TypeInfo accel_type = { + .name = TYPE_ACCEL, + .parent = TYPE_OBJECT, + .class_size = sizeof(AccelClass), + .instance_size = sizeof(AccelState), +}; + +/* Lookup AccelClass from opt_name. Returns NULL if not found */ +AccelClass *accel_find(const char *opt_name) +{ + char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); + AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name)); + g_free(class_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); + AccelCPUClass *accel_cpu = opaque; + + /* + * The first callback allows accel-cpu to run initializations + * for the CPU, customizing CPU behavior according to the accelerator. + * + * The second one allows the CPU to customize the accel-cpu + * behavior according to the CPU. + * + * The second is currently only used by TCG, to specialize the + * TCGCPUOps depending on the CPU type. + */ + cc->accel_cpu = accel_cpu; + if (accel_cpu->cpu_class_init) { + accel_cpu->cpu_class_init(cc); + } + if (cc->init_accel_cpu) { + cc->init_accel_cpu(accel_cpu, cc); + } +} + +/* initialize the arch-specific accel CpuClass interfaces */ +static void accel_init_cpu_interfaces(AccelClass *ac) +{ + const char *ac_name; /* AccelClass name */ + char *acc_name; /* AccelCPUClass name */ + ObjectClass *acc; /* AccelCPUClass */ + + ac_name = object_class_get_name(OBJECT_CLASS(ac)); + g_assert(ac_name != NULL); + + acc_name = g_strdup_printf("%s-%s", ac_name, CPU_RESOLVING_TYPE); + acc = object_class_by_name(acc_name); + g_free(acc_name); + + if (acc) { + object_class_foreach(accel_init_cpu_int_aux, + CPU_RESOLVING_TYPE, false, acc); + } +} + +void accel_init_interfaces(AccelClass *ac) +{ +#ifndef CONFIG_USER_ONLY + accel_system_init_ops_interfaces(ac); +#endif /* !CONFIG_USER_ONLY */ + + accel_init_cpu_interfaces(ac); +} + +void accel_cpu_instance_init(CPUState *cpu) +{ + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->accel_cpu && cc->accel_cpu->cpu_instance_init) { + cc->accel_cpu->cpu_instance_init(cpu); + } +} + +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); + + /* 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, + .abstract = true, + .class_size = sizeof(AccelCPUClass), +}; + +static void register_accel_types(void) +{ + type_register_static(&accel_type); + type_register_static(&accel_cpu_type); +} + +type_init(register_accel_types); diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index d6a1b8d0a2..f32d8c8dc3 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -24,10 +24,9 @@ static void *dummy_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; #ifndef _WIN32 @@ -43,7 +42,7 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { - qemu_mutex_unlock_iothread(); + bql_unlock(); #ifndef _WIN32 do { int sig; @@ -56,11 +55,11 @@ static void *dummy_cpu_thread_fn(void *arg) #else qemu_sem_wait(&cpu->sem); #endif - qemu_mutex_lock_iothread(); + bql_lock(); qemu_wait_io_event(cpu); } while (!cpu->unplug); - qemu_mutex_unlock_iothread(); + bql_unlock(); rcu_unregister_thread(); return NULL; } @@ -69,9 +68,6 @@ void dummy_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/DUMMY", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, dummy_cpu_thread_fn, cpu, diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 24913ca9c4..d60874d3e6 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -52,6 +52,8 @@ #include "qemu/main-loop.h" #include "exec/address-spaces.h" #include "exec/exec-all.h" +#include "gdbstub/enums.h" +#include "hw/boards.h" #include "sysemu/cpus.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" @@ -60,10 +62,6 @@ HVFState *hvf_state; -#ifdef __aarch64__ -#define HV_VM_DEFAULT NULL -#endif - /* Memory slots */ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) @@ -203,15 +201,15 @@ static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { hvf_get_registers(cpu); - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } } static void hvf_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -220,7 +218,7 @@ static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu, run_on_cpu_data arg) { /* QEMU state is the reference, push it to HVF now and on next entry */ - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } static void hvf_cpu_synchronize_post_reset(CPUState *cpu) @@ -303,7 +301,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, @@ -322,8 +320,17 @@ static int hvf_accel_init(MachineState *ms) int x; hv_return_t ret; HVFState *s; + int pa_range = 36; + MachineClass *mc = MACHINE_GET_CLASS(ms); - ret = hv_vm_create(HV_VM_DEFAULT); + if (mc->hvf_get_physical_address_range) { + pa_range = mc->hvf_get_physical_address_range(ms); + if (pa_range < 0) { + return -EINVAL; + } + } + + ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); assert_hvf_ok(ret); s = g_new0(HVFState, 1); @@ -334,18 +341,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 +378,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 +399,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(&cpu->accel->fd, HV_VCPU_DEFAULT); #endif - cpu->vcpu_dirty = 1; + cpu->accel->dirty = true; assert_hvf_ok(r); + cpu->accel->guest_debug_enabled = false; + return hvf_arch_init_vcpu(cpu); } @@ -412,11 +430,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 +454,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; } @@ -452,16 +469,114 @@ static void hvf_start_vcpu_thread(CPUState *cpu) */ assert(hvf_enabled()); - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, hvf_cpu_thread_fn, 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 +588,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..6ca0850b20 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -13,34 +13,53 @@ #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" -void assert_hvf_ok(hv_return_t ret) +const char *hvf_return_string(hv_return_t ret) +{ + switch (ret) { + case HV_SUCCESS: return "HV_SUCCESS"; + case HV_ERROR: return "HV_ERROR"; + case HV_BUSY: return "HV_BUSY"; + case HV_BAD_ARGUMENT: return "HV_BAD_ARGUMENT"; + case HV_NO_RESOURCES: return "HV_NO_RESOURCES"; + case HV_NO_DEVICE: return "HV_NO_DEVICE"; + case HV_UNSUPPORTED: return "HV_UNSUPPORTED"; + case HV_DENIED: return "HV_DENIED"; + default: return "[unknown hv_return value]"; + } +} + +void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, + const char *exp) { if (ret == HV_SUCCESS) { return; } - switch (ret) { - case HV_ERROR: - error_report("Error: HV_ERROR"); - break; - case HV_BUSY: - error_report("Error: HV_BUSY"); - break; - case HV_BAD_ARGUMENT: - error_report("Error: HV_BAD_ARGUMENT"); - break; - case HV_NO_RESOURCES: - error_report("Error: HV_NO_RESOURCES"); - break; - case HV_NO_DEVICE: - error_report("Error: HV_NO_DEVICE"); - break; - case HV_UNSUPPORTED: - error_report("Error: HV_UNSUPPORTED"); - break; - default: - error_report("Unknown Error"); - } + error_report("Error: %s = %s (0x%x, at %s:%u)", + exp, hvf_return_string(ret), ret, file, line); 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 fbf4fe3497..c239dfc87a 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -33,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); @@ -58,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; } @@ -67,9 +66,6 @@ static void kvm_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; - cpu->thread = g_malloc0(sizeof(QemuThread)); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/KVM", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, kvm_vcpu_thread_fn, @@ -83,9 +79,16 @@ static bool kvm_vcpu_thread_is_idle(CPUState *cpu) static bool kvm_cpus_are_resettable(void) { - return !kvm_enabled() || kvm_cpu_check_are_resettable(); + return !kvm_enabled() || !kvm_state->guest_state_protected; } +#ifdef TARGET_KVM_HAVE_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); @@ -98,7 +101,8 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) ops->synchronize_state = kvm_cpu_synchronize_state; ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_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; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f99b0becd8..801cff16a5 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -27,10 +27,11 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/s390x/adapter.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #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" @@ -46,9 +47,10 @@ #include "sysemu/hw_accel.h" #include "kvm-cpus.h" #include "sysemu/dirtylimit.h" +#include "qemu/range.h" #include "hw/boards.h" -#include "monitor/stats.h" +#include "sysemu/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD @@ -67,15 +69,10 @@ #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 +/* Default num of memslots to be allocated when VM starts */ +#define KVM_MEMSLOTS_NR_ALLOC_DEFAULT 16 +/* Default max allowed memslots if kernel reported nothing */ +#define KVM_MEMSLOTS_NR_MAX_DEFAULT 32 struct KVMParkedVcpu { unsigned long vcpu_id; @@ -88,8 +85,6 @@ 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; @@ -97,18 +92,21 @@ 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; +static bool kvm_has_guest_debug; static int kvm_sstep_flags; static bool kvm_immediate_exit; +static uint64_t kvm_supported_memory_attributes; +static bool kvm_guest_memfd_supported; static hwaddr kvm_max_slot_size = ~0; 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 }; @@ -172,41 +170,108 @@ void kvm_resample_fd_notify(int gsi) } } -int kvm_get_max_memslots(void) +/** + * kvm_slots_grow(): Grow the slots[] array in the KVMMemoryListener + * + * @kml: The KVMMemoryListener* to grow the slots[] array + * @nr_slots_new: The new size of slots[] array + * + * Returns: True if the array grows larger, false otherwise. + */ +static bool kvm_slots_grow(KVMMemoryListener *kml, unsigned int nr_slots_new) +{ + unsigned int i, cur = kml->nr_slots_allocated; + KVMSlot *slots; + + if (nr_slots_new > kvm_state->nr_slots_max) { + nr_slots_new = kvm_state->nr_slots_max; + } + + if (cur >= nr_slots_new) { + /* Big enough, no need to grow, or we reached max */ + return false; + } + + if (cur == 0) { + slots = g_new0(KVMSlot, nr_slots_new); + } else { + assert(kml->slots); + slots = g_renew(KVMSlot, kml->slots, nr_slots_new); + /* + * g_renew() doesn't initialize extended buffers, however kvm + * memslots require fields to be zero-initialized. E.g. pointers, + * memory_size field, etc. + */ + memset(&slots[cur], 0x0, sizeof(slots[0]) * (nr_slots_new - cur)); + } + + for (i = cur; i < nr_slots_new; i++) { + slots[i].slot = i; + } + + kml->slots = slots; + kml->nr_slots_allocated = nr_slots_new; + trace_kvm_slots_grow(cur, nr_slots_new); + + return true; +} + +static bool kvm_slots_double(KVMMemoryListener *kml) +{ + return kvm_slots_grow(kml, kml->nr_slots_allocated * 2); +} + +unsigned int kvm_get_max_memslots(void) { KVMState *s = KVM_STATE(current_accel()); - return s->nr_slots; + return s->nr_slots_max; +} + +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_slots_used); + } + kvm_slots_unlock(); + + return s->nr_slots_max - used_slots; } /* Called with KVMMemoryListener.slots_lock held */ static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml) { - KVMState *s = kvm_state; + unsigned int n; int i; - for (i = 0; i < s->nr_slots; i++) { + for (i = 0; i < kml->nr_slots_allocated; i++) { if (kml->slots[i].memory_size == 0) { return &kml->slots[i]; } } + /* + * If no free slots, try to grow first by doubling. Cache the old size + * here to avoid another round of search: if the grow succeeded, it + * means slots[] now must have the existing "n" slots occupied, + * followed by one or more free slots starting from slots[n]. + */ + n = kml->nr_slots_allocated; + if (kvm_slots_double(kml)) { + return &kml->slots[n]; + } + 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) { @@ -224,10 +289,9 @@ static KVMSlot *kvm_lookup_matching_slot(KVMMemoryListener *kml, hwaddr start_addr, hwaddr size) { - KVMState *s = kvm_state; int i; - for (i = 0; i < s->nr_slots; i++) { + for (i = 0; i < kml->nr_slots_allocated; i++) { KVMSlot *mem = &kml->slots[i]; if (start_addr == mem->start_addr && size == mem->memory_size) { @@ -269,7 +333,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, int i, ret = 0; kvm_slots_lock(); - for (i = 0; i < s->nr_slots; i++) { + for (i = 0; i < kml->nr_slots_allocated; i++) { KVMSlot *mem = &kml->slots[i]; if (ram >= mem->ram && ram < mem->ram + mem->memory_size) { @@ -286,46 +350,140 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) { KVMState *s = kvm_state; - struct kvm_userspace_memory_region mem; + struct kvm_userspace_memory_region2 mem; int ret; mem.slot = slot->slot | (kml->as_id << 16); mem.guest_phys_addr = slot->start_addr; mem.userspace_addr = (unsigned long)slot->ram; mem.flags = slot->flags; + mem.guest_memfd = slot->guest_memfd; + mem.guest_memfd_offset = slot->guest_memfd_offset; if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { /* Set the slot size to 0 before setting the slot to the desired * value. This is needed based on KVM commit 75d61fbc. */ mem.memory_size = 0; - ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + + if (kvm_guest_memfd_supported) { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION2, &mem); + } else { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + } if (ret < 0) { goto err; } } mem.memory_size = slot->memory_size; - ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + if (kvm_guest_memfd_supported) { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION2, &mem); + } else { + ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + } slot->old_flags = mem.flags; err: - trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, - mem.memory_size, mem.userspace_addr, ret); + trace_kvm_set_user_memory(mem.slot >> 16, (uint16_t)mem.slot, mem.flags, + mem.guest_phys_addr, mem.memory_size, + mem.userspace_addr, mem.guest_memfd, + mem.guest_memfd_offset, ret); if (ret < 0) { - error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d," - " start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s", - __func__, mem.slot, slot->start_addr, - (uint64_t)mem.memory_size, strerror(errno)); + if (kvm_guest_memfd_supported) { + error_report("%s: KVM_SET_USER_MEMORY_REGION2 failed, slot=%d," + " start=0x%" PRIx64 ", size=0x%" PRIx64 "," + " flags=0x%" PRIx32 ", guest_memfd=%" PRId32 "," + " guest_memfd_offset=0x%" PRIx64 ": %s", + __func__, mem.slot, slot->start_addr, + (uint64_t)mem.memory_size, mem.flags, + mem.guest_memfd, (uint64_t)mem.guest_memfd_offset, + strerror(errno)); + } else { + error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d," + " start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s", + __func__, mem.slot, slot->start_addr, + (uint64_t)mem.memory_size, strerror(errno)); + } } return ret; } +void kvm_park_vcpu(CPUState *cpu) +{ + struct KVMParkedVcpu *vcpu; + + trace_kvm_park_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); + + vcpu = g_malloc0(sizeof(*vcpu)); + vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); + vcpu->kvm_fd = cpu->kvm_fd; + QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); +} + +int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) +{ + struct KVMParkedVcpu *cpu; + int kvm_fd = -ENOENT; + + QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { + if (cpu->vcpu_id == vcpu_id) { + QLIST_REMOVE(cpu, node); + kvm_fd = cpu->kvm_fd; + g_free(cpu); + break; + } + } + + trace_kvm_unpark_vcpu(vcpu_id, kvm_fd > 0 ? "unparked" : "!found parked"); + + return kvm_fd; +} + +int kvm_create_vcpu(CPUState *cpu) +{ + unsigned long vcpu_id = kvm_arch_vcpu_id(cpu); + KVMState *s = kvm_state; + int kvm_fd; + + /* check if the KVM vCPU already exist but is parked */ + kvm_fd = kvm_unpark_vcpu(s, vcpu_id); + if (kvm_fd < 0) { + /* vCPU not parked: create a new KVM vCPU */ + kvm_fd = kvm_vm_ioctl(s, KVM_CREATE_VCPU, vcpu_id); + if (kvm_fd < 0) { + error_report("KVM_CREATE_VCPU IOCTL failed for vCPU %lu", vcpu_id); + return kvm_fd; + } + } + + cpu->kvm_fd = kvm_fd; + cpu->kvm_state = s; + cpu->vcpu_dirty = true; + cpu->dirty_pages = 0; + cpu->throttle_us_per_full = 0; + + trace_kvm_create_vcpu(cpu->cpu_index, vcpu_id, kvm_fd); + + return 0; +} + +int kvm_create_and_park_vcpu(CPUState *cpu) +{ + int ret = 0; + + ret = kvm_create_vcpu(cpu); + if (!ret) { + kvm_park_vcpu(cpu); + } + + return ret; +} + static int do_kvm_destroy_vcpu(CPUState *cpu) { KVMState *s = kvm_state; - long mmap_size; - struct KVMParkedVcpu *vcpu = NULL; + int mmap_size; int ret = 0; - DPRINTF("kvm_destroy_vcpu\n"); + trace_kvm_destroy_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); ret = kvm_arch_destroy_vcpu(cpu); if (ret < 0) { @@ -335,7 +493,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; } @@ -351,10 +509,7 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) } } - vcpu = g_malloc0(sizeof(*vcpu)); - vcpu->vcpu_id = kvm_arch_vcpu_id(cpu); - vcpu->kvm_fd = cpu->kvm_fd; - QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); + kvm_park_vcpu(cpu); err: return ret; } @@ -367,45 +522,22 @@ void kvm_destroy_vcpu(CPUState *cpu) } } -static int kvm_get_vcpu(KVMState *s, unsigned long vcpu_id) -{ - struct KVMParkedVcpu *cpu; - - QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { - if (cpu->vcpu_id == vcpu_id) { - int kvm_fd; - - QLIST_REMOVE(cpu, node); - kvm_fd = cpu->kvm_fd; - g_free(cpu); - return kvm_fd; - } - } - - return kvm_vm_ioctl(s, KVM_CREATE_VCPU, (void *)vcpu_id); -} - int kvm_init_vcpu(CPUState *cpu, Error **errp) { KVMState *s = kvm_state; - long mmap_size; + int mmap_size; int ret; trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); - ret = kvm_get_vcpu(s, kvm_arch_vcpu_id(cpu)); + ret = kvm_create_vcpu(cpu); if (ret < 0) { - error_setg_errno(errp, -ret, "kvm_init_vcpu: kvm_get_vcpu failed (%lu)", + error_setg_errno(errp, -ret, + "kvm_init_vcpu: kvm_create_vcpu failed (%lu)", kvm_arch_vcpu_id(cpu)); goto err; } - cpu->kvm_fd = ret; - 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) { ret = mmap_size; @@ -437,7 +569,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; } } @@ -448,6 +579,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; } @@ -467,6 +600,10 @@ static int kvm_mem_flags(MemoryRegion *mr) if (readonly && kvm_readonly_mem_allowed) { flags |= KVM_MEM_READONLY; } + if (memory_region_has_guest_memfd(mr)) { + assert(kvm_guest_memfd_supported); + flags |= KVM_MEM_GUEST_MEMFD; + } return flags; } @@ -683,6 +820,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); @@ -800,7 +946,7 @@ 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. @@ -991,7 +1137,7 @@ static int kvm_physical_log_clear(KVMMemoryListener *kml, kvm_slots_lock(); - for (i = 0; i < s->nr_slots; i++) { + for (i = 0; i < kml->nr_slots_allocated; i++) { mem = &kml->slots[i]; /* Discard slots that are empty or do not overlap the section */ if (!mem->memory_size || @@ -1088,12 +1234,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; @@ -1119,6 +1259,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; @@ -1152,6 +1297,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 @@ -1235,43 +1385,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) { @@ -1292,6 +1405,37 @@ void kvm_set_max_memslot_size(hwaddr max_slot_size) kvm_max_slot_size = max_slot_size; } +static int kvm_set_memory_attributes(hwaddr start, uint64_t size, uint64_t attr) +{ + struct kvm_memory_attributes attrs; + int r; + + assert((attr & kvm_supported_memory_attributes) == attr); + attrs.attributes = attr; + attrs.address = start; + attrs.size = size; + attrs.flags = 0; + + r = kvm_vm_ioctl(kvm_state, KVM_SET_MEMORY_ATTRIBUTES, &attrs); + if (r) { + error_report("failed to set memory (0x%" HWADDR_PRIx "+0x%" PRIx64 ") " + "with attr 0x%" PRIx64 " error '%s'", + start, size, attr, strerror(errno)); + } + return r; +} + +int kvm_set_memory_attributes_private(hwaddr start, uint64_t size) +{ + return kvm_set_memory_attributes(start, size, KVM_MEMORY_ATTRIBUTE_PRIVATE); +} + +int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size) +{ + return kvm_set_memory_attributes(start, size, 0); +} + +/* Called with KVMMemoryListener.slots_lock held */ static void kvm_set_phys_mem(KVMMemoryListener *kml, MemoryRegionSection *section, bool add) { @@ -1326,14 +1470,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) { /* @@ -1351,6 +1493,10 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, */ if (kvm_state->kvm_dirty_ring_size) { 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); } @@ -1370,8 +1516,9 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, } start_addr += slot_size; size -= slot_size; + kml->nr_slots_used--; } while (size); - goto out; + return; } /* register the new slot */ @@ -1384,6 +1531,9 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, mem->ram_start_offset = ram_start_offset; mem->ram = ram; mem->flags = kvm_mem_flags(mr); + mem->guest_memfd = mr->ram_block->guest_memfd; + mem->guest_memfd_offset = (uint8_t*)ram - mr->ram_block->host; + kvm_slot_init_dirty_bitmap(mem); err = kvm_set_user_memory_region(kml, mem, true); if (err) { @@ -1391,14 +1541,22 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, strerror(-err)); abort(); } + + if (memory_region_has_guest_memfd(mr)) { + err = kvm_set_memory_attributes_private(start_addr, slot_size); + if (err) { + error_report("%s: failed to set memory attribute private: %s", + __func__, strerror(-err)); + exit(1); + } + } + start_addr += slot_size; ram_start_offset += slot_size; ram += slot_size; size -= slot_size; + kml->nr_slots_used++; } while (size); - -out: - kvm_slots_unlock(); } static void *kvm_dirty_ring_reaper_thread(void *data) @@ -1426,27 +1584,84 @@ static void *kvm_dirty_ring_reaper_thread(void *data) trace_kvm_dirty_ring_reaper("wakeup"); r->reaper_state = KVM_DIRTY_RING_REAPER_REAPING; - qemu_mutex_lock_iothread(); + bql_lock(); kvm_dirty_ring_reap(s, NULL); - qemu_mutex_unlock_iothread(); + bql_unlock(); r->reaper_iteration++; } - trace_kvm_dirty_ring_reaper("exit"); - - rcu_unregister_thread(); - - return NULL; + g_assert_not_reached(); } -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; } @@ -1455,18 +1670,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, @@ -1479,7 +1771,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; @@ -1489,15 +1781,17 @@ static void kvm_log_sync_global(MemoryListener *l) /* Flush all kernel dirty addresses into KVMSlot dirty bitmap */ kvm_dirty_ring_flush(); - /* - * TODO: make this faster when nr_slots is big while there are - * only a few used slots (small VMs). - */ kvm_slots_lock(); - for (i = 0; i < s->nr_slots; i++) { + for (i = 0; i < kml->nr_slots_allocated; i++) { 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. @@ -1603,18 +1897,19 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, { int i; - kml->slots = g_new0(KVMSlot, s->nr_slots); kml->as_id = as_id; - for (i = 0; i < s->nr_slots; i++) { - kml->slots[i].slot = i; - } + kvm_slots_grow(kml, KVM_MEMSLOTS_NR_ALLOC_DEFAULT); + + 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) { @@ -1637,9 +1932,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) @@ -1678,7 +1975,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) { @@ -1690,12 +1987,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); } @@ -1717,8 +2008,8 @@ void kvm_irqchip_commit_routes(KVMState *s) assert(ret == 0); } -static void kvm_add_routing_entry(KVMState *s, - struct kvm_irq_routing_entry *entry) +void kvm_add_routing_entry(KVMState *s, + struct kvm_irq_routing_entry *entry) { struct kvm_irq_routing_entry *new; int n, size; @@ -1815,41 +2106,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 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) { @@ -1859,63 +2119,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) @@ -1957,12 +2171,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; } @@ -2042,69 +2261,9 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, EventNotifier *event, } } - if (!kvm_irqfds_enabled()) { - return -ENOSYS; - } - return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); } -int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) -{ - struct kvm_irq_routing_entry kroute = {}; - int virq; - - if (!kvm_gsi_routing_enabled()) { - return -ENOSYS; - } - - virq = kvm_irqchip_get_virq(s); - if (virq < 0) { - return virq; - } - - kroute.gsi = virq; - kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; - kroute.flags = 0; - kroute.u.adapter.summary_addr = adapter->summary_addr; - kroute.u.adapter.ind_addr = adapter->ind_addr; - kroute.u.adapter.summary_offset = adapter->summary_offset; - kroute.u.adapter.ind_offset = adapter->ind_offset; - kroute.u.adapter.adapter_id = adapter->adapter_id; - - kvm_add_routing_entry(s, &kroute); - - return virq; -} - -int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint) -{ - struct kvm_irq_routing_entry kroute = {}; - int virq; - - if (!kvm_gsi_routing_enabled()) { - return -ENOSYS; - } - if (!kvm_check_extension(s, KVM_CAP_HYPERV_SYNIC)) { - return -ENOSYS; - } - virq = kvm_irqchip_get_virq(s); - if (virq < 0) { - return virq; - } - - kroute.gsi = virq; - kroute.type = KVM_IRQ_ROUTING_HV_SINT; - kroute.flags = 0; - kroute.u.hv_sint.vcpu = vcpu; - kroute.u.hv_sint.sint = sint; - - kvm_add_routing_entry(s, &kroute); - kvm_irqchip_commit_routes(s); - - return virq; -} - #else /* !KVM_CAP_IRQ_ROUTING */ void kvm_init_irq_routing(KVMState *s) @@ -2206,6 +2365,11 @@ 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); @@ -2264,7 +2428,7 @@ bool kvm_vcpu_id_is_valid(int vcpu_id) bool kvm_dirty_ring_enabled(void) { - return kvm_state->kvm_dirty_ring_size ? true : false; + return kvm_state && kvm_state->kvm_dirty_ring_size; } static void query_stats_cb(StatsResultList **result, StatsTarget target, @@ -2276,191 +2440,72 @@ uint32_t kvm_dirty_ring_size(void) return kvm_state->kvm_dirty_ring_size; } -static int kvm_init(MachineState *ms) +static int do_kvm_create_vm(MachineState *ms, int type) { - 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 char *name; - int num; - } num_cpus[] = { - { "SMP", ms->smp.cpus }, - { "hotpluggable", ms->smp.max_cpus }, - { NULL, } - }, *nc = num_cpus; - int soft_vcpus_limit, hard_vcpus_limit; KVMState *s; - const KVMCapabilityInfo *missing_cap; int ret; - int type = 0; - uint64_t dirty_log_manual_caps; - - qemu_mutex_init(&kml_slots_lock); s = KVM_STATE(ms->accelerator); - /* - * On systems where the kernel can support different base page - * sizes, host page size may be different from TARGET_PAGE_SIZE, - * even with KVM. TARGET_PAGE_SIZE is assumed to be the minimum - * page size for the system though. - */ - assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size()); - - s->sigmask_len = 8; - -#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); - if (s->fd == -1) { - fprintf(stderr, "Could not access KVM kernel module: %m\n"); - ret = -errno; - goto err; - } - - ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0); - if (ret < KVM_API_VERSION) { - if (ret >= 0) { - ret = -EINVAL; - } - fprintf(stderr, "kvm version too old\n"); - goto err; - } - - if (ret > KVM_API_VERSION) { - ret = -EINVAL; - fprintf(stderr, "kvm version not supported\n"); - goto err; - } - - kvm_immediate_exit = kvm_check_extension(s, KVM_CAP_IMMEDIATE_EXIT); - s->nr_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); - - /* If unspecified, use the default value */ - if (!s->nr_slots) { - s->nr_slots = 32; - } - - s->nr_as = kvm_check_extension(s, KVM_CAP_MULTI_ADDRESS_SPACE); - if (s->nr_as <= 1) { - s->nr_as = 1; - } - s->as = g_new0(struct KVMAs, s->nr_as); - - if (object_property_find(OBJECT(current_machine), "kvm-type")) { - g_autofree char *kvm_type = object_property_get_str(OBJECT(current_machine), - "kvm-type", - &error_abort); - type = mc->kvm_type(ms, kvm_type); - } else if (mc->kvm_type) { - type = mc->kvm_type(ms, NULL); - } - do { ret = kvm_ioctl(s, KVM_CREATE_VM, type); } while (ret == -EINTR); if (ret < 0) { - fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %s\n", -ret, - strerror(-ret)); + error_report("ioctl(KVM_CREATE_VM) failed: %s", strerror(-ret)); #ifdef TARGET_S390X if (ret == -EINVAL) { - fprintf(stderr, - "Host kernel setup problem detected. Please verify:\n"); - fprintf(stderr, "- for kernels supporting the switch_amode or" - " user_mode parameters, whether\n"); - fprintf(stderr, - " user space is running in primary address space\n"); - fprintf(stderr, - "- for kernels supporting the vm.allocate_pgste sysctl, " - "whether it is enabled\n"); + error_printf("Host kernel setup problem detected." + " Please verify:\n"); + error_printf("- for kernels supporting the" + " switch_amode or user_mode parameters, whether"); + error_printf(" user space is running in primary address space\n"); + error_printf("- for kernels supporting the vm.allocate_pgste" + " sysctl, whether it is enabled\n"); } #elif defined(TARGET_PPC) if (ret == -EINVAL) { - fprintf(stderr, - "PPC KVM module is not loaded. Try modprobe kvm_%s.\n", - (type == 2) ? "pr" : "hv"); + error_printf("PPC KVM module is not loaded. Try modprobe kvm_%s.\n", + (type == 2) ? "pr" : "hv"); } #endif - goto err; } - s->vmfd = ret; + return ret; +} - /* check the vcpu limits */ - soft_vcpus_limit = kvm_recommended_vcpus(s); - hard_vcpus_limit = kvm_max_vcpus(s); +static int find_kvm_machine_type(MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + int type; - while (nc->name) { - if (nc->num > soft_vcpus_limit) { - warn_report("Number of %s cpus requested (%d) exceeds " - "the recommended cpus supported by KVM (%d)", - nc->name, nc->num, soft_vcpus_limit); - - if (nc->num > hard_vcpus_limit) { - fprintf(stderr, "Number of %s cpus requested (%d) exceeds " - "the maximum cpus supported by KVM (%d)\n", - nc->name, nc->num, hard_vcpus_limit); - exit(1); - } - } - nc++; + if (object_property_find(OBJECT(current_machine), "kvm-type")) { + g_autofree char *kvm_type; + kvm_type = object_property_get_str(OBJECT(current_machine), + "kvm-type", + &error_abort); + 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); } + return type; +} - missing_cap = kvm_check_extension_list(s, kvm_required_capabilites); - if (!missing_cap) { - missing_cap = - kvm_check_extension_list(s, kvm_arch_required_capabilities); - } - if (missing_cap) { - ret = -EINVAL; - fprintf(stderr, "kvm does not support %s\n%s", - missing_cap->name, upgrade_note); - goto err; - } - - s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); - s->coalesced_pio = s->coalesced_mmio && - kvm_check_extension(s, KVM_CAP_COALESCED_PIO); +static int kvm_setup_dirty_ring(KVMState *s) +{ + uint64_t dirty_log_manual_caps; + int ret; /* * 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) { + return ret; } /* @@ -2495,38 +2540,150 @@ static int kvm_init(MachineState *ms) } } + return 0; +} + +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"; + const struct { + const char *name; + int num; + } num_cpus[] = { + { "SMP", ms->smp.cpus }, + { "hotpluggable", ms->smp.max_cpus }, + { /* end of list */ } + }, *nc = num_cpus; + int soft_vcpus_limit, hard_vcpus_limit; + KVMState *s; + const KVMCapabilityInfo *missing_cap; + int ret; + int type; + + qemu_mutex_init(&kml_slots_lock); + + s = KVM_STATE(ms->accelerator); + + /* + * On systems where the kernel can support different base page + * sizes, host page size may be different from TARGET_PAGE_SIZE, + * even with KVM. TARGET_PAGE_SIZE is assumed to be the minimum + * page size for the system though. + */ + assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size()); + + s->sigmask_len = 8; + accel_blocker_init(); + +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG + QTAILQ_INIT(&s->kvm_sw_breakpoints); +#endif + QLIST_INIT(&s->kvm_parked_vcpus); + s->fd = qemu_open_old(s->device ?: "/dev/kvm", O_RDWR); + if (s->fd == -1) { + error_report("Could not access KVM kernel module: %m"); + ret = -errno; + goto err; + } + + ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0); + if (ret < KVM_API_VERSION) { + if (ret >= 0) { + ret = -EINVAL; + } + error_report("kvm version too old"); + goto err; + } + + if (ret > KVM_API_VERSION) { + ret = -EINVAL; + error_report("kvm version not supported"); + goto err; + } + + kvm_immediate_exit = kvm_check_extension(s, KVM_CAP_IMMEDIATE_EXIT); + s->nr_slots_max = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS); + + /* If unspecified, use the default value */ + if (!s->nr_slots_max) { + s->nr_slots_max = KVM_MEMSLOTS_NR_MAX_DEFAULT; + } + + type = find_kvm_machine_type(ms); + if (type < 0) { + ret = -EINVAL; + goto err; + } + + ret = do_kvm_create_vm(ms, type); + if (ret < 0) { + goto err; + } + + s->vmfd = ret; + + s->nr_as = kvm_vm_check_extension(s, KVM_CAP_MULTI_ADDRESS_SPACE); + if (s->nr_as <= 1) { + s->nr_as = 1; + } + s->as = g_new0(struct KVMAs, s->nr_as); + + /* check the vcpu limits */ + soft_vcpus_limit = kvm_recommended_vcpus(s); + hard_vcpus_limit = kvm_max_vcpus(s); + + while (nc->name) { + if (nc->num > soft_vcpus_limit) { + warn_report("Number of %s cpus requested (%d) exceeds " + "the recommended cpus supported by KVM (%d)", + nc->name, nc->num, soft_vcpus_limit); + + if (nc->num > hard_vcpus_limit) { + error_report("Number of %s cpus requested (%d) exceeds " + "the maximum cpus supported by KVM (%d)", + nc->name, nc->num, hard_vcpus_limit); + exit(1); + } + } + nc++; + } + + missing_cap = kvm_check_extension_list(s, kvm_required_capabilites); + if (!missing_cap) { + missing_cap = + kvm_check_extension_list(s, kvm_arch_required_capabilities); + } + if (missing_cap) { + ret = -EINVAL; + error_report("kvm does not support %s", missing_cap->name); + error_printf("%s", upgrade_note); + goto err; + } + + s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); + s->coalesced_pio = s->coalesced_mmio && + kvm_check_extension(s, KVM_CAP_COALESCED_PIO); + + ret = kvm_setup_dirty_ring(s); + if (ret < 0) { + goto err; + } + #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; } 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_vm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); kvm_resamplefds_allowed = (kvm_check_extension(s, KVM_CAP_IRQFD_RESAMPLE) > 0); @@ -2534,10 +2691,7 @@ static int kvm_init(MachineState *ms) 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 +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG kvm_has_guest_debug = (kvm_check_extension(s, KVM_CAP_SET_GUEST_DEBUG) > 0); #endif @@ -2546,7 +2700,7 @@ static int kvm_init(MachineState *ms) if (kvm_has_guest_debug) { kvm_sstep_flags = SSTEP_ENABLE; -#if defined KVM_CAP_SET_GUEST_DEBUG2 +#if defined TARGET_KVM_HAVE_GUEST_DEBUG int guest_debug_flags = kvm_check_extension(s, KVM_CAP_SET_GUEST_DEBUG2); @@ -2563,6 +2717,12 @@ static int kvm_init(MachineState *ms) goto err; } + kvm_supported_memory_attributes = kvm_vm_check_extension(s, KVM_CAP_MEMORY_ATTRIBUTES); + kvm_guest_memfd_supported = + kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) && + kvm_check_extension(s, KVM_CAP_USER_MEMORY2) && + (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE); + if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) { s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } @@ -2573,24 +2733,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); @@ -2598,10 +2750,7 @@ 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)) { @@ -2619,6 +2768,7 @@ err: if (s->fd != -1) { close(s->fd); } + g_free(s->as); g_free(s->memory_listener.slots); return ret; @@ -2645,16 +2795,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"); @@ -2673,7 +2821,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; } @@ -2701,29 +2849,48 @@ void kvm_flush_coalesced_mmio_buffer(void) s->coalesced_flush_in_progress = false; } -bool kvm_cpu_check_are_resettable(void) -{ - return kvm_arch_cpu_check_are_resettable(); -} - static void do_kvm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->vcpu_dirty) { - kvm_arch_get_registers(cpu); + if (!cpu->vcpu_dirty && !kvm_state->guest_state_protected) { + Error *err = NULL; + int ret = kvm_arch_get_registers(cpu, &err); + if (ret) { + if (err) { + error_reportf_err(err, "Failed to synchronize CPU state: "); + } else { + 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; } } void kvm_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->vcpu_dirty && !kvm_state->guest_state_protected) { run_on_cpu(cpu, do_kvm_cpu_synchronize_state, RUN_ON_CPU_NULL); } } static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { - kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE); + Error *err = NULL; + int ret = kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE, &err); + if (ret) { + if (err) { + error_reportf_err(err, "Restoring resisters after reset: "); + } else { + 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; } @@ -2734,13 +2901,30 @@ 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); + Error *err = NULL; + int ret = kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE, &err); + if (ret) { + if (err) { + error_reportf_err(err, "Putting registers after init: "); + } else { + error_report("Failed to put registers after init: %s", + strerror(-ret)); + } + exit(1); + } + cpu->vcpu_dirty = false; } void kvm_cpu_synchronize_post_init(CPUState *cpu) { - run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL); + if (!kvm_state->guest_state_protected) { + /* + * This runs before the machine_init_done notifiers, and is the last + * opportunity to synchronize the state of confidential guests. + */ + run_on_cpu(cpu, do_kvm_cpu_synchronize_post_init, RUN_ON_CPU_NULL); + } } static void do_kvm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) @@ -2808,32 +2992,132 @@ static void kvm_eat_signals(CPUState *cpu) } while (sigismember(&chkset, SIG_IPI)); } +int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) +{ + MemoryRegionSection section; + ram_addr_t offset; + MemoryRegion *mr; + RAMBlock *rb; + void *addr; + int ret = -1; + + trace_kvm_convert_memory(start, size, to_private ? "shared_to_private" : "private_to_shared"); + + if (!QEMU_PTR_IS_ALIGNED(start, qemu_real_host_page_size()) || + !QEMU_PTR_IS_ALIGNED(size, qemu_real_host_page_size())) { + return -1; + } + + if (!size) { + return -1; + } + + section = memory_region_find(get_system_memory(), start, size); + mr = section.mr; + if (!mr) { + /* + * Ignore converting non-assigned region to shared. + * + * TDX requires vMMIO region to be shared to inject #VE to guest. + * OVMF issues conservatively MapGPA(shared) on 32bit PCI MMIO region, + * and vIO-APIC 0xFEC00000 4K page. + * OVMF assigns 32bit PCI MMIO region to + * [top of low memory: typically 2GB=0xC000000, 0xFC00000) + */ + if (!to_private) { + return 0; + } + return -1; + } + + if (!memory_region_has_guest_memfd(mr)) { + /* + * Because vMMIO region must be shared, guest TD may convert vMMIO + * region to shared explicitly. Don't complain such case. See + * memory_region_type() for checking if the region is MMIO region. + */ + if (!to_private && + !memory_region_is_ram(mr) && + !memory_region_is_ram_device(mr) && + !memory_region_is_rom(mr) && + !memory_region_is_romd(mr)) { + ret = 0; + } else { + error_report("Convert non guest_memfd backed memory region " + "(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s", + start, size, to_private ? "private" : "shared"); + } + goto out_unref; + } + + if (to_private) { + ret = kvm_set_memory_attributes_private(start, size); + } else { + ret = kvm_set_memory_attributes_shared(start, size); + } + if (ret) { + goto out_unref; + } + + addr = memory_region_get_ram_ptr(mr) + section.offset_within_region; + rb = qemu_ram_block_from_host(addr, false, &offset); + + if (to_private) { + if (rb->page_size != qemu_real_host_page_size()) { + /* + * shared memory is backed by hugetlb, which is supposed to be + * pre-allocated and doesn't need to be discarded + */ + goto out_unref; + } + ret = ram_block_discard_range(rb, offset, size); + } else { + ret = ram_block_discard_guest_memfd_range(rb, offset, size); + } + +out_unref: + memory_region_unref(mr); + return ret; +} + 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); + Error *err = NULL; + ret = kvm_arch_put_registers(cpu, KVM_PUT_RUNTIME_STATE, &err); + if (ret) { + if (err) { + error_reportf_err(err, "Putting registers after init: "); + } else { + 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 @@ -2853,39 +3137,40 @@ 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; } - fprintf(stderr, "error: kvm run failed %s\n", - strerror(-run_ret)); + if (!(run_ret == -EFAULT && run->exit_reason == KVM_EXIT_MEMORY_FAULT)) { + fprintf(stderr, "error: kvm run failed %s\n", + strerror(-run_ret)); #ifdef TARGET_PPC - if (run_ret == -EBUSY) { - fprintf(stderr, - "This is probably because your SMT is enabled.\n" - "VCPU can only run on primary threads with all " - "secondary threads offline.\n"); - } + if (run_ret == -EBUSY) { + fprintf(stderr, + "This is probably because your SMT is enabled.\n" + "VCPU can only run on primary threads with all " + "secondary threads offline.\n"); + } #endif - ret = -1; - break; + ret = -1; + break; + } } 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, @@ -2895,7 +3180,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, @@ -2905,11 +3189,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; @@ -2927,7 +3209,7 @@ 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(); + bql_lock(); /* * We throttle vCPU by making it sleep once it exit from kernel * due to dirty ring full. In the dirtylimit scenario, reaping @@ -2939,11 +3221,12 @@ int kvm_cpu_exec(CPUState *cpu) } else { kvm_dirty_ring_reap(kvm_state, NULL); } - qemu_mutex_unlock_iothread(); + 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); @@ -2955,26 +3238,37 @@ 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; + case KVM_EXIT_MEMORY_FAULT: + trace_kvm_memory_fault(run->memory_fault.gpa, + run->memory_fault.size, + run->memory_fault.flags); + if (run->memory_fault.flags & ~KVM_MEMORY_EXIT_FLAG_PRIVATE) { + error_report("KVM_EXIT_MEMORY_FAULT: Unknown flag 0x%" PRIx64, + (uint64_t)run->memory_fault.flags); + ret = -1; + break; + } + ret = kvm_convert_memory(run->memory_fault.gpa, run->memory_fault.size, + run->memory_fault.flags & KVM_MEMORY_EXIT_FLAG_PRIVATE); + 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); @@ -2985,7 +3279,7 @@ int kvm_cpu_exec(CPUState *cpu) return ret; } -int kvm_ioctl(KVMState *s, int type, ...) +int kvm_ioctl(KVMState *s, unsigned long type, ...) { int ret; void *arg; @@ -3003,7 +3297,7 @@ int kvm_ioctl(KVMState *s, int type, ...) return ret; } -int kvm_vm_ioctl(KVMState *s, int type, ...) +int kvm_vm_ioctl(KVMState *s, unsigned long type, ...) { int ret; void *arg; @@ -3014,14 +3308,16 @@ 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; } return ret; } -int kvm_vcpu_ioctl(CPUState *cpu, int type, ...) +int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...) { int ret; void *arg; @@ -3032,14 +3328,16 @@ 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; } return ret; } -int kvm_device_ioctl(int fd, int type, ...) +int kvm_device_ioctl(int fd, unsigned long type, ...) { int ret; void *arg; @@ -3050,7 +3348,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; } @@ -3118,29 +3418,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 @@ -3150,19 +3432,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) +#ifdef TARGET_KVM_HAVE_GUEST_DEBUG +struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, vaddr pc) { struct kvm_sw_breakpoint *bp; @@ -3219,7 +3495,7 @@ bool kvm_supports_guest_debug(void) return kvm_has_guest_debug; } -int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) +int kvm_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct kvm_sw_breakpoint *bp; int err; @@ -3257,7 +3533,7 @@ int kvm_insert_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) return 0; } -int kvm_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len) +int kvm_remove_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct kvm_sw_breakpoint *bp; int err; @@ -3321,7 +3597,7 @@ void kvm_remove_all_breakpoints(CPUState *cpu) } } -#endif /* !KVM_CAP_SET_GUEST_DEBUG */ +#endif /* !TARGET_KVM_HAVE_GUEST_DEBUG */ static int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset) { @@ -3586,7 +3862,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) { @@ -3594,9 +3869,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)) { @@ -3607,6 +3880,39 @@ 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_set_kvm_rapl(Object *obj, bool value, Error **errp) +{ + KVMState *s = KVM_STATE(obj); + s->msr_energy.enable = value; +} + +static void kvm_set_kvm_rapl_socket_path(Object *obj, + const char *str, + Error **errp) +{ + KVMState *s = KVM_STATE(obj); + g_free(s->msr_energy.socket_path); + s->msr_energy.socket_path = g_strdup(str); +} + static void kvm_accel_instance_init(Object *obj) { KVMState *s = KVM_STATE(obj); @@ -3618,8 +3924,15 @@ 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; + s->msr_energy.enable = false; } /** @@ -3660,6 +3973,21 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data) 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)"); + + object_class_property_add_bool(oc, "rapl", + NULL, + kvm_set_kvm_rapl); + object_class_property_set_description(oc, "rapl", + "Allow energy related MSRs for RAPL interface in Guest"); + + object_class_property_add_str(oc, "rapl-helper-socket", NULL, + kvm_set_kvm_rapl_socket_path); + object_class_property_set_description(oc, "rapl-helper-socket", + "Socket Path for comminucating with the Virtual MSR helper daemon"); + kvm_arch_accel_class_init(oc); } @@ -3730,7 +4058,7 @@ static StatsList *add_kvmstat_entry(struct kvm_stats_desc *pdesc, /* Alloc and populate data list */ stats = g_new0(Stats, 1); stats->name = g_strdup(pdesc->name); - stats->value = g_new0(StatsValue, 1);; + stats->value = g_new0(StatsValue, 1); if ((pdesc->flags & KVM_STATS_UNIT_MASK) == KVM_STATS_UNIT_BOOLEAN) { stats->value->u.boolean = *stats_data; @@ -3864,7 +4192,7 @@ static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd /* Read stats header */ kvm_stats_header = &descriptors->kvm_stats_header; - ret = read(stats_fd, kvm_stats_header, sizeof(*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", @@ -3895,7 +4223,8 @@ static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd } static void query_stats(StatsResultList **result, StatsTarget target, - strList *names, int stats_fd, Error **errp) + strList *names, int stats_fd, CPUState *cpu, + Error **errp) { struct kvm_stats_desc *kvm_stats_desc; struct kvm_stats_header *kvm_stats_header; @@ -3953,7 +4282,7 @@ static void query_stats(StatsResultList **result, StatsTarget target, break; case STATS_TARGET_VCPU: add_stats_entry(result, STATS_PROVIDER_KVM, - current_cpu->parent_obj.canonical_path, + cpu->parent_obj.canonical_path, stats_list); break; default: @@ -3990,10 +4319,9 @@ static void query_stats_schema(StatsSchemaList **result, StatsTarget target, add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list); } -static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data) +static void query_stats_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args) { - StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr; - int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + int stats_fd = cpu->kvm_vcpu_stats_fd; Error *local_err = NULL; if (stats_fd == -1) { @@ -4002,14 +4330,13 @@ static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data) return; } query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU, - kvm_stats_args->names, stats_fd, kvm_stats_args->errp); - close(stats_fd); + kvm_stats_args->names, stats_fd, cpu, + kvm_stats_args->errp); } -static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data) +static void query_stats_schema_vcpu(CPUState *cpu, StatsArgs *kvm_stats_args) { - StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr; - int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + int stats_fd = cpu->kvm_vcpu_stats_fd; Error *local_err = NULL; if (stats_fd == -1) { @@ -4019,7 +4346,6 @@ static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data) } query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd, kvm_stats_args->errp); - close(stats_fd); } static void query_stats_cb(StatsResultList **result, StatsTarget target, @@ -4037,7 +4363,7 @@ static void query_stats_cb(StatsResultList **result, StatsTarget target, error_setg_errno(errp, errno, "KVM stats: ioctl failed"); return; } - query_stats(result, target, names, stats_fd, errp); + query_stats(result, target, names, stats_fd, NULL, errp); close(stats_fd); break; } @@ -4051,7 +4377,7 @@ static void query_stats_cb(StatsResultList **result, StatsTarget target, if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) { continue; } - run_on_cpu(cpu, query_stats_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args)); + query_stats_vcpu(cpu, &stats_args); } break; } @@ -4077,6 +4403,33 @@ void query_stats_schemas_cb(StatsSchemaList **result, Error **errp) if (first_cpu) { stats_args.result.schema = result; stats_args.errp = errp; - run_on_cpu(first_cpu, query_stats_schema_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args)); + query_stats_schema_vcpu(first_cpu, &stats_args); } } + +void kvm_mark_guest_state_protected(void) +{ + kvm_state->guest_state_protected = true; +} + +int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp) +{ + int fd; + struct kvm_create_guest_memfd guest_memfd = { + .size = size, + .flags = flags, + }; + + if (!kvm_guest_memfd_supported) { + error_setg(errp, "KVM does not support guest_memfd"); + return -1; + } + + fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_GUEST_MEMFD, &guest_memfd); + if (fd < 0) { + error_setg_errno(errp, errno, "Error creating KVM guest_memfd"); + return -1; + } + + return fd; +} diff --git a/accel/kvm/kvm-cpus.h b/accel/kvm/kvm-cpus.h index fd63fe6a59..171b22fd29 100644 --- a/accel/kvm/kvm-cpus.h +++ b/accel/kvm/kvm-cpus.h @@ -19,8 +19,7 @@ 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, hwaddr addr, hwaddr len); -int kvm_remove_breakpoint(CPUState *cpu, int type, hwaddr addr, hwaddr len); +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..e43d18a869 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -1,21 +1,25 @@ # See docs/devel/tracing.rst for syntax documentation. # kvm-all.c -kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" -kvm_vm_ioctl(int type, void *arg) "type 0x%x, arg %p" -kvm_vcpu_ioctl(int cpu_index, int type, void *arg) "cpu_index %d, type 0x%x, arg %p" +kvm_ioctl(unsigned long type, void *arg) "type 0x%lx, arg %p" +kvm_vm_ioctl(unsigned long type, void *arg) "type 0x%lx, arg %p" +kvm_vcpu_ioctl(int cpu_index, unsigned long type, void *arg) "cpu_index %d, type 0x%lx, arg %p" kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d" -kvm_device_ioctl(int fd, int type, void *arg) "dev fd %d, type 0x%x, arg %p" +kvm_device_ioctl(int fd, unsigned long type, void *arg) "dev fd %d, type 0x%lx, arg %p" kvm_failed_reg_get(uint64_t id, const char *msg) "Warning: Unable to retrieve ONEREG %" PRIu64 " from KVM: %s" kvm_failed_reg_set(uint64_t id, const char *msg) "Warning: Unable to set ONEREG %" PRIu64 " to KVM: %s" kvm_init_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" +kvm_create_vcpu(int cpu_index, unsigned long arch_cpu_id, int kvm_fd) "index: %d, id: %lu, kvm fd: %d" +kvm_destroy_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" +kvm_park_vcpu(int cpu_index, unsigned long arch_cpu_id) "index: %d id: %lu" +kvm_unpark_vcpu(unsigned long arch_cpu_id, const char *msg) "id: %lu %s" kvm_irqchip_commit_routes(void) "" kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_release_virq(int virq) "virq %d" kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" -kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" +kvm_set_user_memory(uint16_t as, uint16_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, uint32_t fd, uint64_t fd_offset, int ret) "AddrSpace#%d Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " guest_memfd=%d" " guest_memfd_offset=0x%" PRIx64 " ret=%d" kvm_clear_dirty_log(uint32_t slot, uint64_t start, uint32_t size) "slot#%"PRId32" start 0x%"PRIx64" size 0x%"PRIx32 kvm_resample_fd_notify(int gsi) "gsi %d" kvm_dirty_ring_full(int id) "vcpu %d" @@ -25,4 +29,11 @@ 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_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 +kvm_convert_memory(uint64_t start, uint64_t size, const char *msg) "start 0x%" PRIx64 " size 0x%" PRIx64 " %s" +kvm_memory_fault(uint64_t start, uint64_t size, uint64_t flags) "start 0x%" PRIx64 " size 0x%" PRIx64 " flags 0x%" PRIx64 +kvm_slots_grow(unsigned int old, unsigned int new) "%u -> %u" diff --git a/accel/meson.build b/accel/meson.build index 259c35c4c8..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'], 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 176d990ae1..2018de8a05 100644 --- a/accel/qtest/meson.build +++ b/accel/qtest/meson.build @@ -1 +1 @@ -qtest_module_ss.add(when: ['CONFIG_SOFTMMU'], if_true: files('qtest.c')) +qtest_module_ss.add(when: ['CONFIG_SYSTEM_ONLY'], if_true: files('qtest.c')) diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index f6056ac836..bf14032d29 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -24,6 +24,18 @@ #include "qemu/main-loop.h" #include "hw/core/cpu.h" +static int64_t qtest_clock_counter; + +static int64_t qtest_get_virtual_clock(void) +{ + return qatomic_read_i64(&qtest_clock_counter); +} + +static void qtest_set_virtual_clock(int64_t count) +{ + qatomic_set_i64(&qtest_clock_counter, count); +} + static int qtest_init_accel(MachineState *ms) { return 0; @@ -52,6 +64,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) ops->create_vcpu_thread = dummy_start_vcpu_thread; ops->get_virtual_clock = qtest_get_virtual_clock; + ops->set_virtual_clock = qtest_set_virtual_clock; }; static const TypeInfo qtest_accel_ops_type = { 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 5d2dd8f351..8e0eb22e61 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,11 +38,6 @@ bool kvm_has_sync_mmu(void) return false; } -int kvm_has_many_ioeventfds(void) -{ - return 0; -} - int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) { return 1; @@ -91,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) { @@ -108,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) @@ -132,3 +124,13 @@ uint32_t kvm_dirty_ring_size(void) { return 0; } + +bool kvm_hwpoisoned_mem(void) +{ + return false; +} + +int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp) +{ + return -ENOSYS; +} 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 c1b05767c0..7f4208fddf 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -11,34 +11,13 @@ */ #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 tcg_flush_jmp_cache(CPUState *cpu) -{ -} - -int probe_access_flags(CPUArchState *env, target_ulong addr, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t retaddr) -{ - g_assert_not_reached(); -} - -void *probe_access(CPUArchState *env, target_ulong addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - /* Handled by hardware accelerator. */ - g_assert_not_reached(); -} - G_NORETURN void cpu_loop_exit(CPUState *cpu) { g_assert_not_reached(); diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6602d7689f..6056598c23 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -13,26 +13,23 @@ * 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, + uint64_t read_value_low, + uint64_t read_value_high, + uint64_t write_value_low, + uint64_t write_value_high, MemOpIdx oi) { - qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_RW); + if (cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, + read_value_low, read_value_high, + oi, QEMU_PLUGIN_MEM_R); + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, + write_value_low, write_value_high, + oi, QEMU_PLUGIN_MEM_W); + } } -#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 +37,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 +52,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..89593b2502 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -53,6 +53,14 @@ # error unsupported data size #endif +#if DATA_SIZE == 16 +# define VALUE_LOW(val) int128_getlo(val) +# define VALUE_HIGH(val) int128_gethi(val) +#else +# define VALUE_LOW(val) val +# define VALUE_HIGH(val) 0 +#endif + #if DATA_SIZE >= 4 # define ABI_TYPE DATA_TYPE #else @@ -69,12 +77,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 @@ -83,60 +91,48 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ret = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); #endif ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, oi); + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(newv), + VALUE_HIGH(newv), + oi); 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); ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, oi); + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); return ret; } #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); \ + atomic_trace_rmw_post(env, addr, \ + VALUE_LOW(ret), \ + VALUE_HIGH(ret), \ + VALUE_LOW(val), \ + VALUE_HIGH(val), \ + oi); \ return ret; \ } @@ -160,12 +156,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 { \ @@ -173,7 +168,12 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ cmp = qatomic_cmpxchg__nocheck(haddr, old, new); \ } while (cmp != old); \ ATOMIC_MMU_CLEANUP; \ - atomic_trace_rmw_post(env, addr, oi); \ + atomic_trace_rmw_post(env, addr, \ + VALUE_LOW(old), \ + VALUE_HIGH(old), \ + VALUE_LOW(xval), \ + VALUE_HIGH(xval), \ + oi); \ return RET; \ } @@ -188,7 +188,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 +202,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 @@ -216,61 +216,48 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ret = qatomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); #endif ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, oi); + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(newv), + VALUE_HIGH(newv), + oi); 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)); ATOMIC_MMU_CLEANUP; - atomic_trace_rmw_post(env, addr, oi); + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); return BSWAP(ret); } #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); \ + atomic_trace_rmw_post(env, addr, \ + VALUE_LOW(ret), \ + VALUE_HIGH(ret), \ + VALUE_LOW(val), \ + VALUE_HIGH(val), \ + oi); \ return BSWAP(ret); \ } @@ -291,12 +278,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 { \ @@ -304,7 +290,12 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ ldn = qatomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ } while (ldo != ldn); \ ATOMIC_MMU_CLEANUP; \ - atomic_trace_rmw_post(env, addr, oi); \ + atomic_trace_rmw_post(env, addr, \ + VALUE_LOW(old), \ + VALUE_HIGH(old), \ + VALUE_LOW(xval), \ + VALUE_HIGH(xval), \ + oi); \ return RET; \ } @@ -326,7 +317,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 */ @@ -338,3 +329,5 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #undef SUFFIX #undef DATA_SIZE #undef SHIFT +#undef VALUE_LOW +#undef VALUE_HIGH diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index c7bc8c6efa..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,40 +32,12 @@ 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); } @@ -78,6 +51,8 @@ void cpu_loop_exit_restore(CPUState *cpu, uintptr_t pc) 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 84a0bcaa90..9376de2a4a 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,24 +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. */ @@ -65,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) { @@ -76,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; @@ -127,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; } @@ -149,6 +144,16 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) } #endif /* CONFIG USER ONLY */ +bool tcg_cflags_has(CPUState *cpu, uint32_t flags) +{ + return cpu->tcg_cflags & flags; +} + +void tcg_cflags_set(CPUState *cpu, uint32_t flags) +{ + cpu->tcg_cflags |= flags; +} + uint32_t curr_cflags(CPUState *cpu) { uint32_t cflags = cpu->tcg_cflags; @@ -162,7 +167,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; @@ -172,13 +177,12 @@ uint32_t curr_cflags(CPUState *cpu) } struct tb_desc { - target_ulong pc; - target_ulong cs_base; + vaddr pc; + uint64_t cs_base; CPUArchState *env; tb_page_addr_t page_addr0; uint32_t flags; uint32_t cflags; - uint32_t trace_vcpu_dstate; }; static bool tb_lookup_cmp(const void *p, const void *d) @@ -186,11 +190,10 @@ static bool tb_lookup_cmp(const void *p, const void *d) const TranslationBlock *tb = p; const struct tb_desc *desc = d; - if ((TARGET_TB_PCREL || tb_pc(tb) == desc->pc) && + 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->trace_vcpu_dstate == desc->trace_vcpu_dstate && (tb_cflags(tb) & ~CF_INVALID) == desc->cflags) { /* check next page if needed */ tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb); @@ -198,7 +201,7 @@ static bool tb_lookup_cmp(const void *p, const void *d) return true; } else { tb_page_addr_t phys_page1; - target_ulong virt_page1; + vaddr virt_page1; /* * We know that the first page matched, and an otherwise valid TB @@ -220,33 +223,31 @@ static bool tb_lookup_cmp(const void *p, const void *d) } static TranslationBlock * -tb_htable_lookup_common(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, - uint32_t cflags, const struct qht *ht, +tb_htable_lookup_common(CPUState *cpu, vaddr pc, uint64_t cs_base, + uint32_t flags, uint32_t cflags, const struct qht *ht, qht_lookup_func_t func) { tb_page_addr_t phys_pc; struct tb_desc desc; uint32_t h; - desc.env = cpu->env_ptr; + desc.env = cpu_env(cpu); 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.page_addr0 = phys_pc; - h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : pc), - flags, cflags, *cpu->trace_dstate); + h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc), + flags, cs_base, cflags); return qht_lookup_custom(ht, &desc, h, func); } -TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, +TranslationBlock *tb_htable_lookup(CPUState *cpu, uint64_t pc, + uint64_t cs_base, uint32_t flags, uint32_t cflags) { return tb_htable_lookup_common(cpu, pc, cs_base, flags, cflags, @@ -262,8 +263,8 @@ static bool inv_tb_lookup_cmp(const void *p, const void *d) tb->ihash == tb_code_hash_func(desc->env, desc->pc, tb->size); } -TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, +TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, uint64_t pc, + uint64_t cs_base, uint32_t flags, uint32_t cflags) { return tb_htable_lookup_common(cpu, pc, cs_base, flags, cflags, @@ -271,9 +272,9 @@ TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, target_ulong pc, } /* 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; @@ -284,35 +285,43 @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, target_ulong pc, hash = tb_jmp_cache_hash_func(pc); jc = cpu->tb_jmp_cache; - tb = tb_jmp_cache_get_tb(jc, hash); + tb = qatomic_read(&jc->array[hash].tb); if (likely(tb && - tb_jmp_cache_get_pc(jc, hash, tb) == 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; } - tb_jmp_cache_set(jc, hash, tb, pc); + + 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 void log_cpu_exec(target_ulong pc, CPUState *cpu, +static void log_cpu_exec(vaddr pc, CPUState *cpu, const TranslationBlock *tb) { 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) { @@ -324,15 +333,17 @@ static 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_slow(CPUState *cpu, target_ulong pc, +static bool check_for_breakpoints_slow(CPUState *cpu, vaddr pc, uint32_t *cflags) { CPUBreakpoint *bp; @@ -365,9 +376,9 @@ static bool check_for_breakpoints_slow(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 } @@ -393,12 +404,12 @@ static bool check_for_breakpoints_slow(CPUState *cpu, target_ulong pc, * breakpoints are removed. */ if (match_page) { - *cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | 1; + *cflags = (*cflags & ~CF_COUNT_MASK) | CF_NO_GOTO_TB | CF_BP_PAGE | 1; } return false; } -static inline bool check_for_breakpoints(CPUState *cpu, target_ulong pc, +static inline bool check_for_breakpoints(CPUState *cpu, vaddr pc, uint32_t *cflags) { return unlikely(!QTAILQ_EMPTY(&cpu->breakpoints)) && @@ -417,9 +428,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); @@ -452,7 +472,6 @@ 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; @@ -462,8 +481,9 @@ cpu_tb_exec(CPUState *cpu, TranslationBlock *itb, int *tb_exit) } 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, @@ -482,20 +502,21 @@ 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); + CPUClass *cc = cpu->cc; + const TCGCPUOps *tcg_ops = cc->tcg_ops; - if (cc->tcg_ops->synchronize_from_tb) { - cc->tcg_ops->synchronize_from_tb(cpu, last_tb); + if (tcg_ops->synchronize_from_tb) { + tcg_ops->synchronize_from_tb(cpu, last_tb); } else { - assert(!TARGET_TB_PCREL); + tcg_debug_assert(!(tb_cflags(last_tb) & CF_PCREL)); assert(cc->set_pc); - cc->set_pc(cpu, tb_pc(last_tb)); + cc->set_pc(cpu, last_tb->pc); } if (qemu_loglevel_mask(CPU_LOG_EXEC)) { - target_ulong pc = log_pc(cpu, last_tb); + vaddr pc = log_pc(cpu, last_tb); if (qemu_log_in_addr_range(pc)) { - qemu_log("Stopped execution of TB chain before %p [" - TARGET_FMT_lx "] %s\n", + qemu_log("Stopped execution of TB chain before %p [%016" + VADDR_PRIx "] %s\n", last_tb->tc.ptr, pc, lookup_symbol(pc)); } } @@ -517,27 +538,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; @@ -574,17 +633,7 @@ void cpu_exec_step_atomic(CPUState *cpu) cpu_tb_exec(cpu, tb, &tb_exit); cpu_exec_exit(cpu); } else { -#ifndef CONFIG_SOFTMMU - clear_helper_retaddr(); - if (have_mmap_lock()) { - mmap_unlock(); - } -#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); } /* @@ -599,15 +648,18 @@ void cpu_exec_step_atomic(CPUState *cpu) 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, @@ -652,16 +704,10 @@ 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(); - } -#endif /* TARGET_I386 */ - if (!cpu_has_work(cpu)) { + const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + bool leave_halt = tcg_ops->cpu_exec_halt(cpu); + + if (!leave_halt) { return true; } @@ -674,7 +720,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) { @@ -683,8 +729,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); } } @@ -693,7 +739,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; @@ -701,6 +747,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; @@ -709,62 +756,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) @@ -783,11 +827,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. */ @@ -796,7 +840,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) @@ -807,7 +851,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) @@ -818,14 +862,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 */ @@ -834,11 +878,11 @@ 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(cpu, interrupt_request)) { + if (!tcg_ops->need_replay_interrupt || + tcg_ops->need_replay_interrupt(interrupt_request)) { replay_interrupt(); } /* @@ -848,7 +892,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; @@ -867,14 +911,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; @@ -886,11 +927,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, - target_ulong pc, - TranslationBlock **last_tb, int *tb_exit) + vaddr pc, TranslationBlock **last_tb, + int *tb_exit) { - int32_t insns_left; - trace_exec_tb(tb, pc); tb = cpu_tb_exec(cpu, tb, tb_exit); if (*tb_exit != TB_EXIT_REQUESTED) { @@ -899,8 +938,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); - if (insns_left < 0) { + if (cpu_loop_exit_requested(cpu)) { /* Something asked us to stop executing chained TBs; just * continue round the main loop. Whatever requested the exit * will also have set something else (eg exit_request or @@ -917,8 +955,8 @@ static inline void cpu_loop_exec_tb(CPUState *cpu, TranslationBlock *tb, /* Ensure global icount has gone forward */ icount_update(cpu); /* Refill decrementer and continue execution. */ - insns_left = MIN(0xffff, cpu->icount_budget); - cpu_neg(cpu)->icount_decr.u16.low = insns_left; + int32_t insns_left = MIN(0xffff, cpu->icount_budget); + cpu->neg.icount_decr.u16.low = insns_left; cpu->icount_extra = cpu->icount_budget - insns_left; /* @@ -936,64 +974,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(); - if (have_mmap_lock()) { - mmap_unlock(); - } -#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)) { @@ -1002,10 +986,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 @@ -1027,17 +1012,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 */ h = tb_jmp_cache_hash_func(pc); - tb_jmp_cache_set(cpu->tb_jmp_cache, h, tb, pc); + jc = cpu->tb_jmp_cache; + jc->array[h].pc = pc; + qatomic_set(&jc->array[h].tb, tb); } #ifndef CONFIG_USER_ONLY @@ -1060,23 +1049,62 @@ int cpu_exec(CPUState *cpu) /* 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(); + /* Check mandatory TCGCPUOps handlers */ +#ifndef CONFIG_USER_ONLY + assert(cpu->cc->tcg_ops->cpu_exec_halt); + assert(cpu->cc->tcg_ops->cpu_exec_interrupt); +#endif /* !CONFIG_USER_ONLY */ + cpu->cc->tcg_ops->initialize(); tcg_target_initialized = true; } @@ -1086,99 +1114,17 @@ void tcg_exec_realizefn(CPUState *cpu, Error **errp) 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 */ void tcg_exec_unrealizefn(CPUState *cpu) { - qemu_plugin_vcpu_exit_hook(cpu); #ifndef CONFIG_USER_ONLY tcg_iommu_free_notifier_list(cpu); #endif /* !CONFIG_USER_ONLY */ tlb_destroy(cpu); - g_free(cpu->tb_jmp_cache); + g_free_rcu(cpu->tb_jmp_cache, rcu); } - -#ifndef CONFIG_USER_ONLY - -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"); - } -} - -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; - } - - tcg_dump_op_count(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 9ea4904fac..265730fb83 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -21,25 +21,32 @@ #include "qemu/main-loop.h" #include "hw/core/tcg-cpu-ops.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #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 "exec/mmu-access-type.h" +#include "exec/tlb-common.h" +#include "exec/vaddr.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 +80,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. */ @@ -91,6 +99,54 @@ static inline size_t sizeof_tlb(CPUTLBDescFast *fast) return fast->mask + (1 << CPU_TLB_ENTRY_BITS); } +static inline uint64_t tlb_read_idx(const CPUTLBEntry *entry, + MMUAccessType access_type) +{ + /* 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 + 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 +} + +static inline uint64_t tlb_addr_write(const CPUTLBEntry *entry) +{ + 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(CPUState *cpu, uintptr_t mmu_idx, + vaddr addr) +{ + return &cpu->neg.tlb.f[mmu_idx].table[tlb_index(cpu, mmu_idx, addr)]; +} + static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, size_t max_entries) { @@ -98,11 +154,16 @@ 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) { - int i, i0 = tb_jmp_cache_hash_page(page_addr); CPUJumpCache *jc = cpu->tb_jmp_cache; + int i, i0; + if (unlikely(!jc)) { + return; + } + + 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); } @@ -233,11 +294,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); @@ -255,41 +316,39 @@ static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now) 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->fulltlb); @@ -315,26 +374,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(); @@ -343,32 +384,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); 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)); } } } @@ -377,12 +418,9 @@ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); - if (cpu->created && !qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work, - RUN_ON_CPU_HOST_INT(idxmap)); - } else { - tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); - } + assert_cpu_is_self(cpu); + + tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); } void tlb_flush(CPUState *cpu) @@ -390,21 +428,6 @@ void tlb_flush(CPUState *cpu) tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS); } -void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap) -{ - const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; - - tlb_debug("mmu_idx: 0x%"PRIx16"\n", idxmap); - - flush_all_helper(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); - fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap)); -} - -void tlb_flush_all_cpus(CPUState *src_cpu) -{ - tlb_flush_by_mmuidx_all_cpus(src_cpu, ALL_MMUIDX_BITS); -} - void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) { const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; @@ -421,7 +444,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; @@ -431,8 +454,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); } @@ -448,8 +470,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)); @@ -458,51 +480,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); } } @@ -516,23 +536,22 @@ 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); /* * Discard jump cache entries for any tb which might potentially @@ -555,15 +574,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; @@ -586,84 +605,28 @@ 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); + + assert_cpu_is_self(cpu); /* This should already be page aligned */ addr &= TARGET_PAGE_MASK; - if (qemu_cpu_is_self(cpu)) { - tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); - } else if (idxmap < TARGET_PAGE_SIZE) { - /* - * Most targets have only a few mmu_idx. In the case where - * we can stuff idxmap into the low TARGET_PAGE_BITS, avoid - * allocating memory for this operation. - */ - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1, - RUN_ON_CPU_TARGET_PTR(addr | idxmap)); - } else { - TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1); - - /* Otherwise allocate a structure, freed by the worker. */ - d->addr = addr; - d->idxmap = idxmap; - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2, - RUN_ON_CPU_HOST_PTR(d)); - } + tlb_flush_page_by_mmuidx_async_0(cpu, addr, 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, - uint16_t idxmap) -{ - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); - - /* This should already be page aligned */ - addr &= TARGET_PAGE_MASK; - - /* - * Allocate memory to hold addr+idxmap only when needed. - * See tlb_flush_page_by_mmuidx for details. - */ - if (idxmap < TARGET_PAGE_SIZE) { - flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, - RUN_ON_CPU_TARGET_PTR(addr | idxmap)); - } else { - CPUState *dst_cpu; - - /* Allocate a separate data block for each destination cpu. */ - CPU_FOREACH(dst_cpu) { - if (dst_cpu != src_cpu) { - TLBFlushPageByMMUIdxData *d - = g_new(TLBFlushPageByMMUIdxData, 1); - - d->addr = addr; - d->idxmap = idxmap; - async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, - RUN_ON_CPU_HOST_PTR(d)); - } - } - } - - tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap); -} - -void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) -{ - 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; @@ -700,18 +663,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 @@ -725,9 +688,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; } @@ -738,26 +701,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; @@ -765,21 +728,20 @@ 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 @@ -795,7 +757,7 @@ static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu, * overlap the flushed pages, which includes the previous. */ d.addr -= TARGET_PAGE_SIZE; - for (target_ulong i = 0, n = d.len / TARGET_PAGE_SIZE + 1; i < n; i++) { + 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; } @@ -809,12 +771,14 @@ 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; + assert_cpu_is_self(cpu); + /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. @@ -835,73 +799,18 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, target_ulong addr, d.idxmap = idxmap; d.bits = bits; - if (qemu_cpu_is_self(cpu)) { - tlb_flush_range_by_mmuidx_async_0(cpu, d); - } else { - /* Otherwise allocate a structure, freed by the worker. */ - TLBFlushRangeData *p = g_memdup(&d, sizeof(d)); - async_run_on_cpu(cpu, tlb_flush_range_by_mmuidx_async_1, - RUN_ON_CPU_HOST_PTR(p)); - } + tlb_flush_range_by_mmuidx_async_0(cpu, d); } -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, - uint16_t idxmap, unsigned bits) -{ - TLBFlushRangeData d; - CPUState *dst_cpu; - - /* - * If all bits are significant, and len is small, - * this devolves to tlb_flush_page. - */ - if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) { - tlb_flush_page_by_mmuidx_all_cpus(src_cpu, addr, idxmap); - return; - } - /* If no page bits are significant, this devolves to tlb_flush. */ - if (bits < TARGET_PAGE_BITS) { - tlb_flush_by_mmuidx_all_cpus(src_cpu, idxmap); - return; - } - - /* This should already be page aligned */ - d.addr = addr & TARGET_PAGE_MASK; - d.len = len; - d.idxmap = idxmap; - d.bits = bits; - - /* Allocate a separate data block for each destination cpu. */ - CPU_FOREACH(dst_cpu) { - if (dst_cpu != src_cpu) { - TLBFlushRangeData *p = g_memdup(&d, sizeof(d)); - async_run_on_cpu(dst_cpu, - tlb_flush_range_by_mmuidx_async_1, - RUN_ON_CPU_HOST_PTR(p)); - } - } - - tlb_flush_range_by_mmuidx_async_0(src_cpu, d); -} - -void tlb_flush_page_bits_by_mmuidx_all_cpus(CPUState *src_cpu, - target_ulong 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) { @@ -943,7 +852,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) { @@ -994,11 +903,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 } } @@ -1020,84 +933,98 @@ 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) +static 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; +} + +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; } /* @@ -1109,19 +1036,16 @@ static void tlb_add_large_page(CPUArchState *env, int mmu_idx, * critical section. */ void tlb_set_page_full(CPUState *cpu, int mmu_idx, - target_ulong vaddr, CPUTLBEntryFull *full) + 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; + vaddr addr_page; int asidx, wp_flags, prot; bool is_ram, is_romd; @@ -1131,9 +1055,9 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, sz = TARGET_PAGE_SIZE; } else { sz = (hwaddr)1 << full->lg_page_size; - tlb_add_large_page(env, mmu_idx, vaddr, sz); + tlb_add_large_page(cpu, mmu_idx, addr, sz); } - vaddr_page = vaddr & TARGET_PAGE_MASK; + addr_page = addr & TARGET_PAGE_MASK; paddr_page = full->phys_addr & TARGET_PAGE_MASK; prot = full->prot; @@ -1142,17 +1066,14 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, &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, full->phys_addr, prot, mmu_idx); + addr, full->phys_addr, prot, mmu_idx); - address = vaddr_page; + 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 (full->attrs.byte_swap) { - address |= TLB_BSWAP; + read_flags |= TLB_INVALID_MASK; } is_ram = memory_region_is_ram(section->mr); @@ -1166,18 +1087,19 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, 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 { @@ -1188,13 +1110,13 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, * 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); #ifdef XBOX wp_flags |= mem_access_callback_address_matches(cpu, @@ -1202,8 +1124,8 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, TARGET_PAGE_SIZE); #endif - 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 @@ -1218,76 +1140,71 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, 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->vfulltlb[vidx] = desc->fulltlb[index]; - tlb_n_used_entries_dec(env, mmu_idx); + 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->fulltlb[index] = *full; - desc->fulltlb[index].xlat_section = iotlb - vaddr_page; - desc->fulltlb[index].phys_addr = paddr_page; - desc->fulltlb[index].prot = prot; + 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); } -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 mmu_idx, uint64_t size) { CPUTLBEntryFull full = { .phys_addr = paddr, @@ -1297,34 +1214,47 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, }; assert(is_power_of_2(size)); - tlb_set_page_full(cpu, mmu_idx, vaddr, &full); + tlb_set_page_full(cpu, mmu_idx, addr, &full); } -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, uint64_t size) { - tlb_set_page_with_attrs(cpu, vaddr, paddr, MEMTXATTRS_UNSPECIFIED, + tlb_set_page_with_attrs(cpu, addr, paddr, MEMTXATTRS_UNSPECIFIED, prot, mmu_idx, size); } /* - * Note: tlb_fill() can trigger a resize of the TLB. This means that all of the - * caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must - * be discarded and looked up again (e.g. via tlb_entry()). + * Note: tlb_fill_align() can trigger a resize of the TLB. + * This means that all of the 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, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) +static bool tlb_fill_align(CPUState *cpu, vaddr addr, MMUAccessType type, + int mmu_idx, MemOp memop, int size, + bool probe, uintptr_t ra) { - bool ok; + const TCGCPUOps *ops = cpu->cc->tcg_ops; + CPUTLBEntryFull full; - /* - * This is not a probe, so only valid return is success; failure - * should result in exception + longjmp to the cpu loop. - */ - ok = cpu->cc->tcg_ops->tlb_fill(cpu, addr, size, - access_type, mmu_idx, false, retaddr); - assert(ok); + if (ops->tlb_fill_align) { + if (ops->tlb_fill_align(cpu, &full, addr, type, mmu_idx, + memop, size, probe, ra)) { + tlb_set_page_full(cpu, mmu_idx, addr, &full); + return true; + } + } else { + /* Legacy behaviour is alignment before paging. */ + if (addr & ((1u << memop_alignment_bits(memop)) - 1)) { + ops->do_unaligned_access(cpu, addr, type, mmu_idx, ra); + } + if (ops->tlb_fill(cpu, addr, size, type, mmu_idx, probe, ra)) { + return true; + } + } + assert(probe); + return false; } static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, @@ -1335,163 +1265,62 @@ static inline void cpu_unaligned_access(CPUState *cpu, vaddr addr, 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, CPUTLBEntryFull *full, - 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, full->xlat_section, full->attrs); - mr = section->mr; - mr_offset = (full->xlat_section & 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, full->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, full->attrs, r, retaddr); - } - if (locked) { - qemu_mutex_unlock_iothread(); - } - - return val; + *out_offset = mr_offset; + return section; } -/* - * Save a potentially trashed CPUTLBEntryFull for later lookup by plugin. - * This is read by tlb_plugin_lookup if the fulltlb entry doesn't match - * because of the side effect of io_writex changing memory layout. - */ -static void save_iotlb_data(CPUState *cs, 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->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, CPUTLBEntryFull *full, - 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, full->xlat_section, full->attrs); - mr = section->mr; - mr_offset = (full->xlat_section & 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, section, mr_offset); - - if (!qemu_mutex_iothread_locked()) { - qemu_mutex_lock_iothread(); - locked = true; - } - r = memory_region_dispatch_write(mr, mr_offset, val, op, full->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, full->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); - CPUTLBEntryFull *f1 = &env_tlb(env)->d[mmu_idx].fulltlb[index]; - CPUTLBEntryFull *f2 = &env_tlb(env)->d[mmu_idx].vfulltlb[vidx]; + 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; @@ -1500,11 +1329,6 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, 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) - static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, CPUTLBEntryFull *full, uintptr_t retaddr) { @@ -1513,10 +1337,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, 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); } /* @@ -1532,66 +1353,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, CPUTLBEntryFull **pfull, - uintptr_t retaddr) + 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); - - flags = TLB_FLAGS_MASK; - 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); - - if (!cs->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 (!tlb_fill_align(cpu, addr, access_type, mmu_idx, + 0, fault_size, 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. */ - index = tlb_index(env, mmu_idx, addr); - entry = tlb_entry(env, mmu_idx, addr); + /* TLB resize via tlb_fill_align may have moved the entry. */ + 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. + * to force the next access through tlb_fill_align. We've just + * called tlb_fill_align, 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; - *pfull = &env_tlb(env)->d[mmu_idx].fulltlb[index]; + *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; } @@ -1601,34 +1407,73 @@ static int probe_access_internal(CPUArchState *env, target_ulong addr, return flags; } -int probe_access_full(CPUArchState *env, target_ulong addr, +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 = probe_access_internal(env, addr, 0, access_type, mmu_idx, - nonfault, phost, pfull, 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)) { - notdirty_write(env_cpu(env), addr, 1, *pfull, retaddr); + int dirtysize = size == 0 ? 1 : size; + notdirty_write(env_cpu(env), addr, dirtysize, *pfull, retaddr); flags &= ~TLB_NOTDIRTY; } return flags; } -int probe_access_flags(CPUArchState *env, target_ulong addr, +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; - return probe_access_full(env, addr, access_type, mmu_idx, - nonfault, phost, &full, retaddr); + 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, target_ulong addr, int size, +void *probe_access(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { CPUTLBEntryFull *full; @@ -1637,8 +1482,9 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, g_assert(-(addr | TARGET_PAGE_MASK) >= size); - flags = probe_access_internal(env, addr, size, access_type, mmu_idx, - false, &host, &full, 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) { @@ -1660,7 +1506,7 @@ void *probe_access(CPUArchState *env, target_ulong addr, int size, /* Handle clean RAM pages. */ if (flags & TLB_NOTDIRTY) { - notdirty_write(env_cpu(env), addr, 1, full, retaddr); + notdirty_write(env_cpu(env), addr, size, full, retaddr); } } @@ -1674,8 +1520,8 @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, void *host; int flags; - flags = probe_access_internal(env, addr, 0, access_type, - mmu_idx, true, &host, &full, 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; @@ -1691,23 +1537,32 @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, * 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, +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, void **hostp) { CPUTLBEntryFull *full; void *p; - (void)probe_access_internal(env, addr, 1, MMU_INST_FETCH, - cpu_mmu_index(env, true), false, &p, &full, 0); + (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. @@ -1715,164 +1570,348 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong 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 CPUTLBEntryFull. 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) { - CPUTLBEntryFull *full; - full = &env_tlb(env)->d[mmu_idx].fulltlb[index]; - data->is_io = true; - data->v.io.section = - iotlb_to_section(cpu, full->xlat_section, full->attrs); - data->v.io.offset = (full->xlat_section & 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 + * @memop: memory operation for the access, or 0 + * @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_align will longjmp out. Return true if the softmmu tlb for + * @mmu_idx may have resized. + */ +static bool mmu_lookup1(CPUState *cpu, MMULookupPageData *data, MemOp memop, + 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_align(cpu, addr, access_type, mmu_idx, + memop, data->size, false, 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]; + + if (likely(!maybe_resized)) { + /* Alignment has not been checked by tlb_fill_align. */ + int a_bits = memop_alignment_bits(memop); + + /* + * 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)) { + int at_bits = memop_atomicity_bits(memop); + a_bits = MAX(a_bits, at_bits); + } + if (unlikely(addr & ((1 << a_bits) - 1))) { + cpu_unaligned_access(cpu, addr, access_type, mmu_idx, ra); + } + } + + 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; } +/** + * 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; +#ifdef XBOX + mem_check_access_callback_vaddr(cpu, addr, size, wp, full); #endif + 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) +{ + bool crosspage; + int flags; + + l->memop = get_memop(oi); + l->mmu_idx = get_mmuidx(oi); + + tcg_debug_assert(l->mmu_idx < NB_MMU_MODES); + + 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->memop, 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->memop, l->mmu_idx, type, ra); + if (mmu_lookup1(cpu, &l->page[1], 0, 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); + } + + 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; + bool did_tlb_fill = false; tcg_debug_assert(mmu_idx < NB_MMU_MODES); /* Adjust the given return address. */ retaddr -= GETPC_ADJ; - /* 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, - mmu_idx, retaddr); + index = tlb_index(cpu, mmu_idx, addr); + tlbe = tlb_entry(cpu, mmu_idx, addr); + + /* Check TLB entry and enforce page permissions. */ + 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_align(cpu, addr, MMU_DATA_STORE, mmu_idx, + mop, size, false, retaddr); + did_tlb_fill = true; + 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_align(cpu, addr, MMU_DATA_LOAD, mmu_idx, + 0, size, false, 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. + */ + g_assert_not_reached(); + } + + /* Enforce guest required alignment, if not handled by tlb_fill_align. */ + if (!did_tlb_fill && (addr & ((1 << memop_alignment_bits(mop)) - 1))) { + cpu_unaligned_access(cpu, addr, MMU_DATA_STORE, mmu_idx, retaddr); } /* Enforce qemu required alignment. */ if (unlikely(addr & (size - 1))) { - /* We get here if guest alignment was not requested, - or was not enforced by cpu_unaligned_access above. - We might widen the access and emulate, but for now - mark an exception and exit the cpu loop. */ + /* + * We get here if guest alignment was not requested, or was not + * enforced by cpu_unaligned_access or tlb_fill_align above. + * We might widen the access and emulate, but for now + * mark an exception and exit the cpu loop. + */ goto stop_the_world; } - index = tlb_index(env, mmu_idx, addr); - tlbe = tlb_entry(env, 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_read, 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; - } - } + /* 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].fulltlb[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) { +#ifdef XBOX + mem_check_access_callback_vaddr(cpu, addr, size, wp_flags, full); +#endif + 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); } /* @@ -1882,151 +1921,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)) { - CPUTLBEntryFull *full; - bool need_swap; - - /* For anything that is unaligned, recurse through full_load. */ - if ((addr & (size - 1)) != 0) { - goto do_unaligned_access; - } - - full = &env_tlb(env)->d[mmu_idx].fulltlb[index]; - - /* Handle watchpoints. */ - if (unlikely(tlb_addr & TLB_WATCHPOINT)) { -#ifdef XBOX - mem_check_access_callback_vaddr(env_cpu(env), addr, size, - BP_MEM_READ, full); -#endif - - /* On watchpoint hit, this will longjmp out. */ - cpu_check_watchpoint(env_cpu(env), addr, size, - full->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, full, 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 @@ -2036,551 +1931,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 page1, 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. An exception to this rule is PAGE_WRITE_INV - * handling: the first page could have evicted itself. - */ - page1 = addr & TARGET_PAGE_MASK; - page2 = (addr + size) & TARGET_PAGE_MASK; - if (page1 != page2) { - 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); } - } else { - size2 = 0; - } - - 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)) { -#ifdef XBOX - mem_check_access_callback_vaddr(env_cpu(env), addr, size - size2, - BP_MEM_WRITE, &env_tlb(env)->d[mmu_idx].fulltlb[index]); -#endif - cpu_check_watchpoint(env_cpu(env), addr, size - size2, - env_tlb(env)->d[mmu_idx].fulltlb[index].attrs, - BP_MEM_WRITE, retaddr); - } - if (size2 && unlikely(tlb_addr2 & TLB_WATCHPOINT)) { -#ifdef XBOX - mem_check_access_callback_vaddr(env_cpu(env), page2, size2, - BP_MEM_WRITE, &env_tlb(env)->d[mmu_idx].fulltlb[index2]); -#endif - cpu_check_watchpoint(env_cpu(env), page2, size2, - env_tlb(env)->d[mmu_idx].fulltlb[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)) { - CPUTLBEntryFull *full; - 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; } - full = &env_tlb(env)->d[mmu_idx].fulltlb[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)) { -#ifdef XBOX - mem_check_access_callback_vaddr(env_cpu(env), addr, size, - BP_MEM_WRITE, full); -#endif - - /* On watchpoint hit, this will longjmp out. */ - cpu_check_watchpoint(env_cpu(env), addr, size, - full->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, full, 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, full, 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" @@ -2611,65 +2901,68 @@ 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); -} - -void cpu_ld_code(CPUArchState *env, abi_ptr addr, size_t len, uint8_t *out) -{ - MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(env, true)); - for (size_t i = 0; i < len; i++) { - out[i] = full_ldub_code(env, addr+i, 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); +} + +void cpu_ld_code(CPUArchState *env, abi_ptr addr, size_t len, uint8_t *out) +{ + for (size_t i = 0; i < len; i++) { + out[i] = cpu_ldub_code(env, addr+i); + } } 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/accel/tcg/icount-common.c b/accel/tcg/icount-common.c new file mode 100644 index 0000000000..30bf8500dc --- /dev/null +++ b/accel/tcg/icount-common.c @@ -0,0 +1,501 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/cpus.h" +#include "sysemu/qtest.h" +#include "qemu/main-loop.h" +#include "qemu/option.h" +#include "qemu/seqlock.h" +#include "sysemu/replay.h" +#include "sysemu/runstate.h" +#include "hw/core/cpu.h" +#include "sysemu/cpu-timers.h" +#include "sysemu/cpu-timers-internal.h" + +/* + * ICOUNT: Instruction Counter + * + * this module is split off from cpu-timers because the icount part + * is TCG-specific, and does not need to be built for other accels. + */ +static bool icount_sleep = true; +/* Arbitrarily pick 1MIPS as the minimum allowable speed. */ +#define MAX_ICOUNT_SHIFT 10 + +/* Do not count executed instructions */ +ICountMode use_icount = ICOUNT_DISABLED; + +static void icount_enable_precise(void) +{ + /* Fixed conversion of insn to ns via "shift" option */ + use_icount = ICOUNT_PRECISE; +} + +static void icount_enable_adaptive(void) +{ + /* Runtime adaptive algorithm to compute shift */ + use_icount = ICOUNT_ADAPTATIVE; +} + +/* + * The current number of executed instructions is based on what we + * originally budgeted minus the current state of the decrementing + * icount counters in extra/u16.low. + */ +static int64_t icount_get_executed(CPUState *cpu) +{ + return (cpu->icount_budget - + (cpu->neg.icount_decr.u16.low + cpu->icount_extra)); +} + +/* + * Update the global shared timer_state.qemu_icount to take into + * account executed instructions. This is done by the TCG vCPU + * thread so the main-loop can see time has moved forward. + */ +static void icount_update_locked(CPUState *cpu) +{ + int64_t executed = icount_get_executed(cpu); + cpu->icount_budget -= executed; + + qatomic_set_i64(&timers_state.qemu_icount, + timers_state.qemu_icount + executed); +} + +/* + * Update the global shared timer_state.qemu_icount to take into + * account executed instructions. This is done by the TCG vCPU + * thread so the main-loop can see time has moved forward. + */ +void icount_update(CPUState *cpu) +{ + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + icount_update_locked(cpu); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); +} + +static int64_t icount_get_raw_locked(void) +{ + CPUState *cpu = current_cpu; + + if (cpu && cpu->running) { + if (!cpu->neg.can_do_io) { + error_report("Bad icount read"); + exit(1); + } + /* Take into account what has run */ + icount_update_locked(cpu); + } + /* The read is protected by the seqlock, but needs atomic64 to avoid UB */ + return qatomic_read_i64(&timers_state.qemu_icount); +} + +static int64_t icount_get_locked(void) +{ + int64_t icount = icount_get_raw_locked(); + return qatomic_read_i64(&timers_state.qemu_icount_bias) + + icount_to_ns(icount); +} + +int64_t icount_get_raw(void) +{ + int64_t icount; + unsigned start; + + do { + start = seqlock_read_begin(&timers_state.vm_clock_seqlock); + icount = icount_get_raw_locked(); + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); + + return icount; +} + +/* Return the virtual CPU time, based on the instruction counter. */ +int64_t icount_get(void) +{ + int64_t icount; + unsigned start; + + do { + start = seqlock_read_begin(&timers_state.vm_clock_seqlock); + icount = icount_get_locked(); + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); + + return icount; +} + +int64_t icount_to_ns(int64_t icount) +{ + return icount << qatomic_read(&timers_state.icount_time_shift); +} + +/* + * Correlation between real and virtual time is always going to be + * fairly approximate, so ignore small variation. + * When the guest is idle real and virtual time will be aligned in + * the IO wait loop. + */ +#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10) + +static void icount_adjust(void) +{ + int64_t cur_time; + int64_t cur_icount; + int64_t delta; + + /* If the VM is not running, then do nothing. */ + if (!runstate_is_running()) { + return; + } + + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + cur_time = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT, + cpu_get_clock_locked()); + cur_icount = icount_get_locked(); + + delta = cur_icount - cur_time; + /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ + if (delta > 0 + && timers_state.last_delta + ICOUNT_WOBBLE < delta * 2 + && timers_state.icount_time_shift > 0) { + /* The guest is getting too far ahead. Slow time down. */ + qatomic_set(&timers_state.icount_time_shift, + timers_state.icount_time_shift - 1); + } + if (delta < 0 + && timers_state.last_delta - ICOUNT_WOBBLE > delta * 2 + && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) { + /* The guest is getting too far behind. Speed time up. */ + qatomic_set(&timers_state.icount_time_shift, + timers_state.icount_time_shift + 1); + } + timers_state.last_delta = delta; + qatomic_set_i64(&timers_state.qemu_icount_bias, + cur_icount - (timers_state.qemu_icount + << timers_state.icount_time_shift)); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); +} + +static void icount_adjust_rt(void *opaque) +{ + timer_mod(timers_state.icount_rt_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); + icount_adjust(); +} + +static void icount_adjust_vm(void *opaque) +{ + timer_mod(timers_state.icount_vm_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + NANOSECONDS_PER_SECOND / 10); + icount_adjust(); +} + +int64_t icount_round(int64_t count) +{ + int shift = qatomic_read(&timers_state.icount_time_shift); + return (count + (1 << shift) - 1) >> shift; +} + +static void icount_warp_rt(void) +{ + unsigned seq; + int64_t warp_start; + + /* + * The icount_warp_timer is rescheduled soon after vm_clock_warp_start + * changes from -1 to another value, so the race here is okay. + */ + do { + seq = seqlock_read_begin(&timers_state.vm_clock_seqlock); + warp_start = timers_state.vm_clock_warp_start; + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq)); + + if (warp_start == -1) { + return; + } + + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + if (runstate_is_running()) { + int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT, + cpu_get_clock_locked()); + int64_t warp_delta; + + warp_delta = clock - timers_state.vm_clock_warp_start; + if (icount_enabled() == ICOUNT_ADAPTATIVE) { + /* + * 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, + timers_state.qemu_icount_bias + warp_delta); + } + timers_state.vm_clock_warp_start = -1; + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + + if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } +} + +static void icount_timer_cb(void *opaque) +{ + /* + * No need for a checkpoint because the timer already synchronizes + * with CHECKPOINT_CLOCK_VIRTUAL_RT. + */ + icount_warp_rt(); +} + +void icount_start_warp_timer(void) +{ + int64_t clock; + int64_t deadline; + + assert(icount_enabled()); + + /* + * Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers + * do not fire, so computing the deadline does not make sense. + */ + if (!runstate_is_running()) { + return; + } + + if (replay_mode != REPLAY_MODE_PLAY) { + if (!all_cpu_threads_idle()) { + return; + } + + if (qtest_enabled()) { + /* When testing, qtest commands advance icount. */ + return; + } + + replay_checkpoint(CHECKPOINT_CLOCK_WARP_START); + } else { + /* warp clock deterministically in record/replay mode */ + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) { + /* + * 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 something. + */ + if (replay_has_event()) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } + return; + } + } + + /* We want to use the earliest deadline from ALL vm_clocks */ + clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + ~QEMU_TIMER_ATTR_EXTERNAL); + if (deadline < 0) { + if (!icount_sleep) { + warn_report_once("icount sleep disabled and no active timers"); + } + return; + } + + if (deadline > 0) { + /* + * Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to + * sleep. Otherwise, the CPU might be waiting for a future timer + * interrupt to wake it up, but the interrupt never comes because + * the vCPU isn't running any insns and thus doesn't advance the + * QEMU_CLOCK_VIRTUAL. + */ + if (!icount_sleep) { + /* + * We never let VCPUs sleep in no sleep icount mode. + * If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance + * to the next QEMU_CLOCK_VIRTUAL event and notify it. + * It is useful when we want a deterministic execution time, + * isolated from host latencies. + */ + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + qatomic_set_i64(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + deadline); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } else { + /* + * We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some + * "real" time, (related to the time left until the next event) has + * passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this. + * This avoids that the warps are visible externally; for example, + * you will not be sending network packets continuously instead of + * every 100ms. + */ + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + if (timers_state.vm_clock_warp_start == -1 + || timers_state.vm_clock_warp_start > clock) { + timers_state.vm_clock_warp_start = clock; + } + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + timer_mod_anticipate(timers_state.icount_warp_timer, + clock + deadline); + } + } else if (deadline == 0) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } +} + +void icount_account_warp_timer(void) +{ + if (!icount_sleep) { + return; + } + + /* + * Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers + * do not fire, so computing the deadline does not make sense. + */ + if (!runstate_is_running()) { + return; + } + + replay_async_events(); + + /* warp clock deterministically in record/replay mode */ + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) { + return; + } + + timer_del(timers_state.icount_warp_timer); + icount_warp_rt(); +} + +bool icount_configure(QemuOpts *opts, Error **errp) +{ + const char *option = qemu_opt_get(opts, "shift"); + bool sleep = qemu_opt_get_bool(opts, "sleep", true); + bool align = qemu_opt_get_bool(opts, "align", false); + long time_shift = -1; + + if (!option) { + if (qemu_opt_get(opts, "align") != NULL) { + error_setg(errp, "Please specify shift option when using align"); + return false; + } + return true; + } + + if (align && !sleep) { + error_setg(errp, "align=on and sleep=off are incompatible"); + 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 false; + } + } else if (icount_align_option) { + error_setg(errp, "shift=auto and align=on are incompatible"); + return false; + } else if (!icount_sleep) { + error_setg(errp, "shift=auto and sleep=off are incompatible"); + return false; + } + + icount_sleep = sleep; + if (icount_sleep) { + timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + icount_timer_cb, NULL); + } + + icount_align_option = align; + + if (time_shift >= 0) { + timers_state.icount_time_shift = time_shift; + icount_enable_precise(); + return true; + } + + icount_enable_adaptive(); + + /* + * 125MIPS seems a reasonable initial guess at the guest speed. + * It will be corrected fairly quickly anyway. + */ + timers_state.icount_time_shift = 3; + + /* + * Have both realtime and virtual time triggers for speed adjustment. + * The realtime trigger catches emulated time passing too slowly, + * the virtual time trigger catches emulated time passing too fast. + * Realtime triggers occur even when idle, so use them less frequently + * than VM triggers. + */ + timers_state.vm_clock_warp_start = -1; + timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT, + icount_adjust_rt, NULL); + timer_mod(timers_state.icount_rt_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); + timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + icount_adjust_vm, NULL); + 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) +{ + 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..a8fc3db774 --- /dev/null +++ b/accel/tcg/internal-common.h @@ -0,0 +1,59 @@ +/* + * 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/cpu-common.h" +#include "exec/translation-block.h" + +extern int64_t max_delay; +extern int64_t max_advance; + +extern bool one_insn_per_tb; + +/* + * 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 !tcg_cflags_has(cs, CF_PARALLEL) || cpu_in_exclusive_context(cs); +} + +/** + * 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->neg.plugin_mem_cbs; +#else + return false; +#endif +} + +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); +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); + +#endif diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h new file mode 100644 index 0000000000..fe109724c6 --- /dev/null +++ b/accel/tcg/internal-target.h @@ -0,0 +1,118 @@ +/* + * 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 */ + +bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); + +/* 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; + } +} + +/** + * 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 cb13bade4f..0000000000 --- a/accel/tcg/internal.h +++ /dev/null @@ -1,122 +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" - -/* - * 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 - -typedef struct PageDesc { - /* list of TBs intersecting this ram page */ - uintptr_t first_tb; -#ifdef CONFIG_USER_ONLY - unsigned long flags; - void *target_data; -#endif -#ifdef CONFIG_SOFTMMU - QemuSpin lock; -#endif -} PageDesc; - -/* 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 - */ -extern int v_l1_size; -extern int v_l1_shift; -extern 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) - -extern void *l1_map[V_L1_MAX_SIZE]; - -PageDesc *page_find_alloc(tb_page_addr_t index, bool alloc); - -static inline PageDesc *page_find(tb_page_addr_t index) -{ - return page_find_alloc(index, false); -} - -/* 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 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) { } -#else -#ifdef CONFIG_DEBUG_TCG -void do_assert_page_locked(const PageDesc *pd, const char *file, int line); -#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__) -#else -#define assert_page_locked(pd) -#endif -void page_lock(PageDesc *pd); -void page_unlock(PageDesc *pd); -#endif -#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 - -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); -void tb_reset_jump(TranslationBlock *tb, int n); -TranslationBlock *tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2); -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); - -/* Return the current PC from CPU, which may be cached in TB. */ -static inline target_ulong log_pc(CPUState *cpu, const TranslationBlock *tb) -{ -#if TARGET_TB_PCREL - return cpu->cc->get_pc(cpu); -#else - return tb_pc(tb); -#endif -} - -#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..c735add261 --- /dev/null +++ b/accel/tcg/ldst_atomicity.c.inc @@ -0,0 +1,1120 @@ +/* + * 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.inc" +#include "host/store-insert-al16.h.inc" + +#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. */ + trace_load_atom8_or_exit_fallback(ra); + 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. */ + trace_load_atom16_or_exit_fallback(ra); + 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); + } + trace_load_atom8_fallback(memop, ra); + 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) { + trace_load_atom16_fallback(memop, ra); + cpu_loop_exit_atomic(cpu, ra); + } + a = load_atomic8(pv); + b = load_atomic8(pv + 8); + break; + case -MO_64: + if (!HAVE_al8) { + trace_load_atom16_fallback(memop, ra); + 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(); + } + + trace_store_atom2_fallback(memop, ra); + 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; + } + } + trace_store_atom4_fallback(memop, ra); + 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(); + } + trace_store_atom8_fallback(memop, ra); + 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(); + } + trace_store_atom16_fallback(memop, ra); + cpu_loop_exit_atomic(cpu, ra); +} diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index 6ac8d871a3..ebbf380d76 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -8,6 +8,245 @@ * 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, + uint64_t value_low, + uint64_t value_high, + MemOpIdx oi) +{ + if (cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, + value_low, value_high, + 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, ret, 0, 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, ret, 0, 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, ret, 0, 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, ret, 0, 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, int128_getlo(ret), int128_gethi(ret), oi); + return ret; +} + +/* + * Store helpers for cpu_ldst.h + */ + +static void plugin_store_cb(CPUArchState *env, abi_ptr addr, + uint64_t value_low, + uint64_t value_high, + MemOpIdx oi) +{ + if (cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, + value_low, value_high, + 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, val, 0, 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, val, 0, 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, val, 0, 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, val, 0, 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, int128_getlo(val), int128_gethi(val), oi); +} + +/* + * Wrappers of the above + */ uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) @@ -26,7 +265,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 +278,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 +305,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 +326,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 +379,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 +390,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 +413,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 03c0cd34b8..ff5d7a45eb 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -1,7 +1,9 @@ -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', @@ -9,19 +11,25 @@ tcg_ss.add(files( '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')]) -tcg_ss.add(when: 'CONFIG_VTUNE_JITPROFILING', if_true: [vtune_jitprofiling]) +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')) +tcg_specific_ss.add(when: 'CONFIG_VTUNE_JITPROFILING', if_true: [vtune_jitprofiling]) +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_all(when: 'CONFIG_TCG', if_true: tcg_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 80dff68934..1ef075552c 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -14,907 +14,438 @@ * Injecting the desired instrumentation could be done with a second * translation pass that combined the instrumentation requests, but that * would be ugly and inefficient since we would decode the guest code twice. - * Instead, during TB translation we add "empty" instrumentation calls for all - * possible instrumentation events, and then once we collect the instrumentation - * requests from plugins, we either "fill in" those empty events or remove them - * if they have no requests. - * - * When "filling in" an event we first copy the empty callback's TCG ops. This - * might seem unnecessary, but it is done to support an arbitrary number - * of callbacks per event. Take for example a regular instruction callback. - * We first generate a callback to an empty helper function. Then, if two - * plugins register one callback each for this instruction, we make two copies - * of the TCG ops generated for the empty callback, substituting the function - * pointer that points to the empty helper function with the plugins' desired - * callback functions. After that we remove the empty callback's ops. - * - * Note that the location in TCGOp.args[] of the pointer to a helper function - * varies across different guest and host architectures. Instead of duplicating - * the logic that figures this out, we rely on the fact that the empty - * callbacks point to empty functions that are unique pointers in the program. - * Thus, to find the right location we just have to look for a match in - * TCGOp.args[]. This is the main reason why we first copy an empty callback's - * TCG ops and then fill them in; regardless of whether we have one or many - * callbacks for that event, the logic to add all of them is the same. - * - * When generating more than one callback per event, we make a small - * optimization to avoid generating redundant operations. For instance, for the - * second and all subsequent callbacks of an event, we do not need to reload the - * CPU's index into a TCG temp, since the first callback did it already. + * Instead, during TB translation we add "plugin_cb" marker opcodes + * for all possible instrumentation events, and then once we collect the + * instrumentation requests from plugins, we generate code for those markers + * or remove them if they have no requests. */ #include "qemu/osdep.h" +#include "qemu/plugin.h" +#include "qemu/log.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" -#ifdef CONFIG_SOFTMMU -# define CONFIG_SOFTMMU_GATE 1 -#else -# define CONFIG_SOFTMMU_GATE 0 -#endif - -/* - * plugin_cb_start TCG op args[]: - * 0: enum plugin_gen_from - * 1: enum plugin_gen_cb - * 2: set to 1 for mem callback that is a write, 0 otherwise. - */ - enum plugin_gen_from { PLUGIN_GEN_FROM_TB, PLUGIN_GEN_FROM_INSN, - PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_AFTER_INSN, - PLUGIN_GEN_N_FROMS, + PLUGIN_GEN_AFTER_TB, }; -enum plugin_gen_cb { - PLUGIN_GEN_CB_UDATA, - PLUGIN_GEN_CB_INLINE, - PLUGIN_GEN_CB_MEM, - PLUGIN_GEN_ENABLE_MEM_HELPER, - PLUGIN_GEN_DISABLE_MEM_HELPER, - PLUGIN_GEN_N_CBS, -}; - -/* - * 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_mem_cb)(unsigned int vcpu_index, - qemu_plugin_meminfo_t info, uint64_t vaddr, - void *userdata) -{ } - -static void do_gen_mem_cb(TCGv vaddr, uint32_t info) +/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ +void plugin_gen_disable_mem_helpers(void) { - 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); + if (tcg_ctx->plugin_insn) { + tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB); + } +} - tcg_gen_ld_i32(cpu_index, cpu_env, +static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, + struct qemu_plugin_insn *insn) +{ + GArray *arr; + size_t len; + + /* + * Tracking memory accesses performed from helpers requires extra work. + * If an instruction is emulated with helpers, we do two things: + * (1) copy the CB descriptors, and keep track of it so that they can be + * freed later on, and (2) point CPUState.neg.plugin_mem_cbs to the + * descriptors, so that we can read them at run-time + * (i.e. when the helper executes). + * This run-time access is performed from qemu_plugin_vcpu_mem_cb. + * + * Note that plugin_gen_disable_mem_helpers undoes (2). Since it + * is possible that the code we generate after the instruction is + * dead, we also add checks before generating tb_exit etc. + */ + if (!insn->calls_helpers) { + return; + } + + if (!insn->mem_cbs || !insn->mem_cbs->len) { + insn->mem_helper = false; + return; + } + insn->mem_helper = true; + ptb->mem_helper = true; + + /* + * TODO: It seems like we should be able to use ref/unref + * to avoid needing to actually copy this array. + * Alternately, perhaps we could allocate new memory adjacent + * to the TranslationBlock itself, so that we do not have to + * actively manage the lifetime after this. + */ + len = insn->mem_cbs->len; + arr = g_array_sized_new(false, false, + sizeof(struct qemu_plugin_dyn_cb), len); + g_array_append_vals(arr, insn->mem_cbs->data, len); + qemu_plugin_add_dyn_cb_arr(arr); + + tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, + offsetof(CPUState, neg.plugin_mem_cbs) - + offsetof(ArchCPU, env)); +} + +static void gen_disable_mem_helper(void) +{ + tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, + offsetof(CPUState, neg.plugin_mem_cbs) - + offsetof(ArchCPU, env)); +} + +static TCGv_i32 gen_cpu_index(void) +{ + TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); + tcg_gen_ld_i32(cpu_index, tcg_env, -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - tcg_gen_extu_tl_i64(vaddr64, vaddr); + return cpu_index; +} - gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata); - - tcg_temp_free_ptr(udata); - tcg_temp_free_i64(vaddr64); - tcg_temp_free_i32(meminfo); +static void gen_udata_cb(struct qemu_plugin_regular_cb *cb) +{ + TCGv_i32 cpu_index = gen_cpu_index(); + tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, + tcgv_i32_temp(cpu_index), + tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); tcg_temp_free_i32(cpu_index); } -static void gen_empty_udata_cb(void) +static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry) { - TCGv_i32 cpu_index = tcg_temp_new_i32(); - TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */ + TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); - tcg_gen_ld_i32(cpu_index, cpu_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); - gen_helper_plugin_vcpu_udata_cb(cpu_index, udata); + GArray *arr = entry.score->data; + char *base_ptr = arr->data + entry.offset; + size_t entry_size = g_array_get_element_size(arr); - tcg_temp_free_ptr(udata); + TCGv_i32 cpu_index = gen_cpu_index(); + tcg_gen_muli_i32(cpu_index, cpu_index, entry_size); + tcg_gen_ext_i32_ptr(ptr, cpu_index); tcg_temp_free_i32(cpu_index); + tcg_gen_addi_ptr(ptr, ptr, (intptr_t) base_ptr); + + return ptr; } -/* - * For now we only support addi_i64. - * When we support more ops, we can generate one empty inline cb for each. - */ -static void gen_empty_inline_cb(void) +static TCGCond plugin_cond_to_tcgcond(enum qemu_plugin_cond cond) { - TCGv_i64 val = tcg_temp_new_i64(); - TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */ + switch (cond) { + case QEMU_PLUGIN_COND_EQ: + return TCG_COND_EQ; + case QEMU_PLUGIN_COND_NE: + return TCG_COND_NE; + case QEMU_PLUGIN_COND_LT: + return TCG_COND_LTU; + case QEMU_PLUGIN_COND_LE: + return TCG_COND_LEU; + case QEMU_PLUGIN_COND_GT: + return TCG_COND_GTU; + case QEMU_PLUGIN_COND_GE: + return TCG_COND_GEU; + default: + /* ALWAYS and NEVER conditions should never reach */ + g_assert_not_reached(); + } +} + +static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb) +{ + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + TCGLabel *after_cb = gen_new_label(); + + /* Condition should be negated, as calling the cb is the "else" path */ + TCGCond cond = tcg_invert_cond(plugin_cond_to_tcgcond(cb->cond)); 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); - tcg_gen_st_i64(val, ptr, 0); - tcg_temp_free_ptr(ptr); + tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb); + TCGv_i32 cpu_index = gen_cpu_index(); + tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, + tcgv_i32_temp(cpu_index), + tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_temp_free_i32(cpu_index); + gen_set_label(after_cb); + tcg_temp_free_i64(val); -} - -static void gen_empty_mem_cb(TCGv addr, uint32_t info) -{ - do_gen_mem_cb(addr, info); -} - -/* - * Share the same function for enable/disable. When enabling, the NULL - * pointer will be overwritten later. - */ -static void gen_empty_mem_helper(void) -{ - TCGv_ptr ptr; - - 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); } -static void gen_plugin_cb_start(enum plugin_gen_from from, - enum plugin_gen_cb type, unsigned wr) +static void gen_inline_add_u64_cb(struct qemu_plugin_inline_cb *cb) { - tcg_gen_plugin_cb_start(from, type, wr); + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_temp_ebb_new_i64(); + + tcg_gen_ld_i64(val, ptr, 0); + tcg_gen_addi_i64(val, val, cb->imm); + tcg_gen_st_i64(val, ptr, 0); + + tcg_temp_free_i64(val); + tcg_temp_free_ptr(ptr); } -static void gen_wrapped(enum plugin_gen_from from, - enum plugin_gen_cb type, void (*func)(void)) +static void gen_inline_store_u64_cb(struct qemu_plugin_inline_cb *cb) { - gen_plugin_cb_start(from, type, 0); - func(); - tcg_gen_plugin_cb_end(); + TCGv_ptr ptr = gen_plugin_u64_ptr(cb->entry); + TCGv_i64 val = tcg_constant_i64(cb->imm); + + tcg_gen_st_i64(val, ptr, 0); + + tcg_temp_free_ptr(ptr); } -static void plugin_gen_empty_callback(enum plugin_gen_from from) +static void gen_mem_cb(struct qemu_plugin_regular_cb *cb, + qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { - switch (from) { - case PLUGIN_GEN_AFTER_INSN: - gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER, - gen_empty_mem_helper); + TCGv_i32 cpu_index = gen_cpu_index(); + tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL, + tcgv_i32_temp(cpu_index), + tcgv_i32_temp(tcg_constant_i32(meminfo)), + tcgv_i64_temp(addr), + tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_temp_free_i32(cpu_index); +} + +static void inject_cb(struct qemu_plugin_dyn_cb *cb) + +{ + switch (cb->type) { + case PLUGIN_CB_REGULAR: + gen_udata_cb(&cb->regular); break; - case PLUGIN_GEN_FROM_INSN: - /* - * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being - * the first callback of an instruction - */ - gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER, - 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_INLINE, gen_empty_inline_cb); + case PLUGIN_CB_COND: + gen_udata_cond_cb(&cb->cond); + break; + case PLUGIN_CB_INLINE_ADD_U64: + gen_inline_add_u64_cb(&cb->inline_insn); + break; + case PLUGIN_CB_INLINE_STORE_U64: + gen_inline_store_u64_cb(&cb->inline_insn); break; default: g_assert_not_reached(); } } -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) +static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb, + enum qemu_plugin_mem_rw rw, + qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { - 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(); - } - 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); -} - -static TCGOp *find_op(TCGOp *op, TCGOpcode opc) -{ - while (op) { - if (op->opc == opc) { - return op; + switch (cb->type) { + case PLUGIN_CB_MEM_REGULAR: + if (rw & cb->regular.rw) { + gen_mem_cb(&cb->regular, meminfo, addr); } - op = QTAILQ_NEXT(op, link); - } - return NULL; -} - -static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end) -{ - TCGOp *ret = QTAILQ_NEXT(end, link); - - QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link); - return ret; -} - -/* remove all ops until (and including) plugin_cb_end */ -static TCGOp *rm_ops(TCGOp *op) -{ - TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end); - - tcg_debug_assert(end_op); - return rm_ops_range(op, end_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)); - return op; -} - -static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc) -{ - op = copy_op_nocheck(begin_op, op); - tcg_debug_assert((*begin_op)->opc == 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) { - /* mov_i32 */ - op = copy_op(begin_op, op, INDEX_op_mov_i32); - op->args[1] = tcgv_i32_arg(tcg_constant_i32((uintptr_t)ptr)); - } else { - /* mov_i64 */ - op = copy_op(begin_op, op, INDEX_op_mov_i64); - op->args[1] = tcgv_i64_arg(tcg_constant_i64((uintptr_t)ptr)); - } - return op; -} - -static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op) -{ - if (TARGET_LONG_BITS == 32) { - /* extu_i32_i64 */ - op = copy_extu_i32_i64(begin_op, op); - } else { - /* mov_i64 */ - op = copy_mov_i64(begin_op, op); - } - return op; -} - -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); - } else { - /* ld_i64 */ - op = copy_op(begin_op, op, INDEX_op_ld_i64); - } - return op; -} - -static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* 2x st_i32 */ - op = copy_op(begin_op, op, INDEX_op_st_i32); - op = copy_op(begin_op, op, INDEX_op_st_i32); - } else { - /* st_i64 */ - op = copy_op(begin_op, op, INDEX_op_st_i64); - } - return op; -} - -static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) -{ - if (TCG_TARGET_REG_BITS == 32) { - /* all 32-bit backends must implement add2_i32 */ - g_assert(TCG_TARGET_HAS_add2_i32); - op = copy_op(begin_op, op, INDEX_op_add2_i32); - op->args[4] = tcgv_i32_arg(tcg_constant_i32(v)); - op->args[5] = tcgv_i32_arg(tcg_constant_i32(v >> 32)); - } else { - op = copy_op(begin_op, op, INDEX_op_add_i64); - op->args[2] = tcgv_i64_arg(tcg_constant_i64(v)); - } - return op; -} - -static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) -{ - if (UINTPTR_MAX == UINT32_MAX) { - /* st_i32 */ - op = copy_op(begin_op, op, INDEX_op_st_i32); - } else { - /* st_i64 */ - op = copy_st_i64(begin_op, op); - } - return op; -} - -static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, - void *func, int *cb_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; - 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; - } + break; + case PLUGIN_CB_INLINE_ADD_U64: + case PLUGIN_CB_INLINE_STORE_U64: + if (rw & cb->inline_insn.rw) { + inject_cb(cb); } - tcg_debug_assert(i < MAX_OPC_PARAM_ARGS); + break; + default: + g_assert_not_reached(); } - op->args[*cb_idx] = (uintptr_t)func; - op->args[*cb_idx + 1] = (*begin_op)->args[*cb_idx + 1]; - - return op; } -/* - * When we append/replace ops here we are sensitive to changing patterns of - * TCGOps generated by the tcg_gen_FOO calls when we generated the - * empty callbacks. This will assert very quickly in a debug build as - * we assert the ops we are replacing are the correct ones. - */ -static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *cb_idx) +static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) { - /* const_ptr */ - 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)); - } - - /* call */ - op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb), - cb->f.vcpu_udata, cb_idx); - - return op; -} - -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); - - /* ld_i64 */ - 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; -} - -static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *cb_idx) -{ - enum plugin_gen_cb type = begin_op->args[1]; - - tcg_debug_assert(type == PLUGIN_GEN_CB_MEM); - - /* const_i32 == mov_i32 ("info", so it remains as is) */ - op = copy_op(&begin_op, op, INDEX_op_mov_i32); - - /* const_ptr */ - 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)); - } - - /* 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); - } - - return op; -} - -typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb, - TCGOp *begin_op, TCGOp *op, int *intp); -typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb); - -static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) -{ - return true; -} - -static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) -{ - int w; - - w = op->args[2]; - return !!(cb->rw & (w + 1)); -} - -static void inject_cb_type(const GArray *cbs, TCGOp *begin_op, - inject_fn inject, op_ok_fn ok) -{ - TCGOp *end_op; - TCGOp *op; - int cb_idx = -1; - int i; - - if (!cbs || cbs->len == 0) { - rm_ops(begin_op); - return; - } - - end_op = find_op(begin_op, INDEX_op_plugin_cb_end); - tcg_debug_assert(end_op); - - op = end_op; - for (i = 0; i < cbs->len; i++) { - struct qemu_plugin_dyn_cb *cb = - &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); - - if (!ok(begin_op, cb)) { - continue; - } - op = inject(cb, begin_op, op, &cb_idx); - } - rm_ops_range(begin_op, end_op); -} - -static void -inject_udata_cb(const GArray *cbs, TCGOp *begin_op) -{ - inject_cb_type(cbs, begin_op, append_udata_cb, op_ok); -} - -static void -inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok) -{ - inject_cb_type(cbs, begin_op, append_inline_cb, ok); -} - -static void -inject_mem_cb(const GArray *cbs, TCGOp *begin_op) -{ - inject_cb_type(cbs, begin_op, append_mem_cb, op_rw); -} - -/* we could change the ops in place, but we can reuse more code by copying */ -static void inject_mem_helper(TCGOp *begin_op, GArray *arr) -{ - TCGOp *orig_op = begin_op; - TCGOp *end_op; - TCGOp *op; - - end_op = find_op(begin_op, INDEX_op_plugin_cb_end); - tcg_debug_assert(end_op); - - /* const ptr */ - op = copy_const_ptr(&begin_op, end_op, arr); - - /* st_ptr */ - op = copy_st_ptr(&begin_op, op); - - rm_ops_range(orig_op, end_op); -} - -/* - * Tracking memory accesses performed from helpers requires extra work. - * If an instruction is emulated with helpers, we do two things: - * (1) copy the CB descriptors, and keep track of it so that they can be - * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so - * that we can read them at run-time (i.e. when the helper executes). - * This run-time access is performed from qemu_plugin_vcpu_mem_cb. - * - * Note that plugin_gen_disable_mem_helpers undoes (2). Since it - * 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, - TCGOp *begin_op) -{ - GArray *cbs[2]; - GArray *arr; - size_t n_cbs, i; - - cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; - cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; - - n_cbs = 0; - for (i = 0; i < ARRAY_SIZE(cbs); i++) { - n_cbs += cbs[i]->len; - } - - plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs; - if (likely(!plugin_insn->mem_helper)) { - rm_ops(begin_op); - return; - } - - arr = g_array_sized_new(false, false, - sizeof(struct qemu_plugin_dyn_cb), n_cbs); - - for (i = 0; i < ARRAY_SIZE(cbs); i++) { - g_array_append_vals(arr, cbs[i]->data, cbs[i]->len); - } - - qemu_plugin_add_dyn_cb_arr(arr); - inject_mem_helper(begin_op, arr); -} - -static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn, - TCGOp *begin_op) -{ - if (likely(!plugin_insn->mem_helper)) { - rm_ops(begin_op); - return; - } - inject_mem_helper(begin_op, NULL); -} - -/* 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)) { - return; - } - ptr = tcg_const_ptr(NULL); - tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - - offsetof(ArchCPU, env)); - tcg_temp_free_ptr(ptr); - tcg_ctx->plugin_insn->mem_helper = false; -} - -static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op) -{ - inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op); -} - -static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op) -{ - inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok); -} - -static void plugin_gen_insn_udata(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], begin_op); -} - -static void plugin_gen_insn_inline(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_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], - begin_op, op_ok); -} - -static void plugin_gen_mem_regular(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_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op); -} - -static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, - TCGOp *begin_op, int insn_idx) -{ - const GArray *cbs; - struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); - - cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; - inject_inline_cb(cbs, begin_op, op_rw); -} - -static void plugin_gen_enable_mem_helper(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_mem_enable_helper(insn, begin_op); -} - -static void plugin_gen_disable_mem_helper(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_mem_disable_helper(insn, begin_op); -} - -/* #define DEBUG_PLUGIN_GEN_OPS */ -static void pr_ops(void) -{ -#ifdef DEBUG_PLUGIN_GEN_OPS - TCGOp *op; - int i = 0; - - QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { - const char *name = ""; - const char *type = ""; - - if (op->opc == INDEX_op_plugin_cb_start) { - switch (op->args[0]) { - case PLUGIN_GEN_FROM_TB: - name = "tb"; - break; - case PLUGIN_GEN_FROM_INSN: - name = "insn"; - break; - case PLUGIN_GEN_FROM_MEM: - name = "mem"; - break; - case PLUGIN_GEN_AFTER_INSN: - name = "after insn"; - break; - default: - break; - } - switch (op->args[1]) { - case PLUGIN_GEN_CB_UDATA: - type = "udata"; - break; - case PLUGIN_GEN_CB_INLINE: - type = "inline"; - break; - case PLUGIN_GEN_CB_MEM: - type = "mem"; - break; - case PLUGIN_GEN_ENABLE_MEM_HELPER: - type = "enable mem helper"; - break; - case PLUGIN_GEN_DISABLE_MEM_HELPER: - type = "disable mem helper"; - break; - default: - break; - } - } - printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type); - i++; - } -#endif -} - -static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) -{ - TCGOp *op; + TCGOp *op, *next; int insn_idx = -1; - pr_ops(); + if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN) + && qemu_log_in_addr_range(tcg_ctx->plugin_db->pc_first))) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "OP before plugin injection:\n"); + tcg_dump_ops(tcg_ctx, logfile, false); + fprintf(logfile, "\n"); + qemu_log_unlock(logfile); + } + } - QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { + /* + * While injecting code, we cannot afford to reuse any ebb temps + * that might be live within the existing opcode stream. + * The simplest solution is to release them all and create new. + */ + tcg_temp_ebb_reset_freed(tcg_ctx); + + QTAILQ_FOREACH_SAFE(op, &tcg_ctx->ops, link, next) { switch (op->opc) { case INDEX_op_insn_start: insn_idx++; break; - case INDEX_op_plugin_cb_start: + + case INDEX_op_plugin_cb: { enum plugin_gen_from from = op->args[0]; - enum plugin_gen_cb type = op->args[1]; + struct qemu_plugin_insn *insn = NULL; + const GArray *cbs; + int i, n; + + if (insn_idx >= 0) { + insn = g_ptr_array_index(plugin_tb->insns, insn_idx); + } + + tcg_ctx->emit_before_op = op; switch (from) { - case PLUGIN_GEN_FROM_TB: - { - g_assert(insn_idx == -1); - - switch (type) { - case PLUGIN_GEN_CB_UDATA: - plugin_gen_tb_udata(plugin_tb, op); - break; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_tb_inline(plugin_tb, op); - break; - default: - g_assert_not_reached(); + case PLUGIN_GEN_AFTER_TB: + if (plugin_tb->mem_helper) { + gen_disable_mem_helper(); } break; - } - case PLUGIN_GEN_FROM_INSN: - { - g_assert(insn_idx >= 0); - switch (type) { - case PLUGIN_GEN_CB_UDATA: - plugin_gen_insn_udata(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_insn_inline(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_ENABLE_MEM_HELPER: - plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx); - break; - default: - g_assert_not_reached(); - } - break; - } - case PLUGIN_GEN_FROM_MEM: - { - g_assert(insn_idx >= 0); - - switch (type) { - case PLUGIN_GEN_CB_MEM: - plugin_gen_mem_regular(plugin_tb, op, insn_idx); - break; - case PLUGIN_GEN_CB_INLINE: - plugin_gen_mem_inline(plugin_tb, op, insn_idx); - break; - default: - g_assert_not_reached(); - } - - break; - } case PLUGIN_GEN_AFTER_INSN: - { - g_assert(insn_idx >= 0); - - switch (type) { - case PLUGIN_GEN_DISABLE_MEM_HELPER: - plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx); - break; - default: - g_assert_not_reached(); + assert(insn != NULL); + if (insn->mem_helper) { + gen_disable_mem_helper(); } break; - } + + case PLUGIN_GEN_FROM_TB: + assert(insn == NULL); + + cbs = plugin_tb->cbs; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + inject_cb( + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i)); + } + break; + + case PLUGIN_GEN_FROM_INSN: + assert(insn != NULL); + + gen_enable_mem_helper(plugin_tb, insn); + + cbs = insn->insn_cbs; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + inject_cb( + &g_array_index(cbs, struct qemu_plugin_dyn_cb, i)); + } + break; + default: g_assert_not_reached(); } + + tcg_ctx->emit_before_op = NULL; + tcg_op_remove(tcg_ctx, op); break; } + + case INDEX_op_plugin_mem_cb: + { + TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0])); + qemu_plugin_meminfo_t meminfo = op->args[1]; + enum qemu_plugin_mem_rw rw = + (qemu_plugin_mem_is_store(meminfo) + ? QEMU_PLUGIN_MEM_W : QEMU_PLUGIN_MEM_R); + struct qemu_plugin_insn *insn; + const GArray *cbs; + int i, n; + + assert(insn_idx >= 0); + insn = g_ptr_array_index(plugin_tb->insns, insn_idx); + + tcg_ctx->emit_before_op = op; + + cbs = insn->mem_cbs; + for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { + inject_mem_cb(&g_array_index(cbs, struct qemu_plugin_dyn_cb, i), + rw, meminfo, addr); + } + + tcg_ctx->emit_before_op = NULL; + tcg_op_remove(tcg_ctx, op); + break; + } + default: /* plugins don't care about any other ops */ break; } } - pr_ops(); } -bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, - bool mem_only) +bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db) { - bool ret = false; + struct qemu_plugin_tb *ptb; - if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) { - struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - int i; - - /* reset callbacks */ - for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { - if (ptb->cbs[i]) { - g_array_set_size(ptb->cbs[i], 0); - } - } - ptb->n = 0; - - ret = true; - - ptb->vaddr = db->pc_first; - ptb->vaddr2 = -1; - ptb->haddr1 = db->host_addr[0]; - ptb->haddr2 = NULL; - ptb->mem_only = mem_only; - - plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); + if (!test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, + cpu->plugin_state->event_mask)) { + return false; } + tcg_ctx->plugin_db = db; tcg_ctx->plugin_insn = NULL; + ptb = tcg_ctx->plugin_tb; - return ret; + if (ptb) { + /* Reset callbacks */ + if (ptb->cbs) { + g_array_set_size(ptb->cbs, 0); + } + ptb->n = 0; + ptb->mem_helper = false; + } else { + ptb = g_new0(struct qemu_plugin_tb, 1); + tcg_ctx->plugin_tb = ptb; + ptb->insns = g_ptr_array_new(); + } + + tcg_gen_plugin_cb(PLUGIN_GEN_FROM_TB); + return true; } void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - struct qemu_plugin_insn *pinsn; + struct qemu_plugin_insn *insn; + size_t n = db->num_insns; + vaddr pc; - pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next); - tcg_ctx->plugin_insn = pinsn; - plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); - - /* - * Detect page crossing to get the new host address. - * Note that we skip this when haddr1 == NULL, e.g. when we're - * fetching instructions from a region not backed by RAM. - */ - if (ptb->haddr1 == NULL) { - pinsn->haddr = NULL; - } else if (is_same_page(db, db->pc_next)) { - pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr; + assert(n >= 1); + ptb->n = n; + if (n <= ptb->insns->len) { + insn = g_ptr_array_index(ptb->insns, n - 1); } else { - if (ptb->vaddr2 == -1) { - ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first); - get_page_addr_code_hostp(cpu->env_ptr, ptb->vaddr2, &ptb->haddr2); - } - pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2; + assert(n - 1 == ptb->insns->len); + insn = g_new0(struct qemu_plugin_insn, 1); + g_ptr_array_add(ptb->insns, insn); } + + tcg_ctx->plugin_insn = insn; + insn->calls_helpers = false; + insn->mem_helper = false; + if (insn->insn_cbs) { + g_array_set_size(insn->insn_cbs, 0); + } + if (insn->mem_cbs) { + g_array_set_size(insn->mem_cbs, 0); + } + + pc = db->pc_next; + insn->vaddr = pc; + + tcg_gen_plugin_cb(PLUGIN_GEN_FROM_INSN); } void plugin_gen_insn_end(void) { - plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN); + const DisasContextBase *db = tcg_ctx->plugin_db; + struct qemu_plugin_insn *pinsn = tcg_ctx->plugin_insn; + + pinsn->len = db->fake_insn ? db->record_len : db->pc_next - pinsn->vaddr; + + tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_INSN); } /* @@ -923,13 +454,21 @@ 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); /* inject the instrumentation at the appropriate places */ plugin_gen_inject(ptb); + + /* reset plugin translation state (plugin_tb is reused between blocks) */ + tcg_ctx->plugin_db = NULL; + tcg_ctx->plugin_insn = NULL; } diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h deleted file mode 100644 index 9829abe4a9..0000000000 --- a/accel/tcg/plugin-helpers.h +++ /dev/null @@ -1,4 +0,0 @@ -#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) -#endif diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h index 9854116427..c7f34c066c 100644 --- a/accel/tcg/tb-hash.h +++ b/accel/tcg/tb-hash.h @@ -21,6 +21,7 @@ #define EXEC_TB_HASH_H #include "exec/cpu-defs.h" +#include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "qemu/xxhash.h" #include "qemu/fast-hash.h" @@ -36,16 +37,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)); @@ -54,7 +55,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); } @@ -62,10 +63,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); } static inline diff --git a/accel/tcg/tb-jmp-cache.h b/accel/tcg/tb-jmp-cache.h index ff5ffc8fc2..c3a505e394 100644 --- a/accel/tcg/tb-jmp-cache.h +++ b/accel/tcg/tb-jmp-cache.h @@ -9,57 +9,25 @@ #ifndef ACCEL_TCG_TB_JMP_CACHE_H #define ACCEL_TCG_TB_JMP_CACHE_H +#include "qemu/rcu.h" +#include "exec/cpu-common.h" + #define TB_JMP_CACHE_BITS 12 #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) /* - * Accessed in parallel; all accesses to 'tb' must be atomic. - * For TARGET_TB_PCREL, accesses to 'pc' must be protected by - * a load_acquire/store_release to 'tb'. + * 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 { +typedef struct CPUJumpCache { + struct rcu_head rcu; struct { TranslationBlock *tb; -#if TARGET_TB_PCREL - target_ulong pc; -#endif + vaddr pc; } array[TB_JMP_CACHE_SIZE]; -}; - -static inline TranslationBlock * -tb_jmp_cache_get_tb(CPUJumpCache *jc, uint32_t hash) -{ -#if TARGET_TB_PCREL - /* Use acquire to ensure current load of pc from jc. */ - return qatomic_load_acquire(&jc->array[hash].tb); -#else - /* Use rcu_read to ensure current load of pc from *tb. */ - return qatomic_rcu_read(&jc->array[hash].tb); -#endif -} - -static inline target_ulong -tb_jmp_cache_get_pc(CPUJumpCache *jc, uint32_t hash, TranslationBlock *tb) -{ -#if TARGET_TB_PCREL - return jc->array[hash].pc; -#else - return tb_pc(tb); -#endif -} - -static inline void -tb_jmp_cache_set(CPUJumpCache *jc, uint32_t hash, - TranslationBlock *tb, target_ulong pc) -{ -#if TARGET_TB_PCREL - jc->array[hash].pc = pc; - /* Use store_release on tb to ensure pc is written first. */ - qatomic_store_release(&jc->array[hash].tb, tb); -#else - /* Use the pc value already stored in tb->pc. */ - qatomic_set(&jc->array[hash].tb, tb); -#endif -} +} CPUJumpCache; #endif /* ACCEL_TCG_TB_JMP_CACHE_H */ diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 474416b571..d323d0e10f 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1,5 +1,5 @@ /* - * Translation Block Maintaince + * Translation Block Maintenance * * Copyright (c) 2003 Fabrice Bellard * @@ -18,27 +18,40 @@ */ #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/page-protection.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.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 ((TARGET_TB_PCREL || tb_pc(a) == tb_pc(b)) && + 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) && - a->trace_vcpu_dstate == b->trace_vcpu_dstate && tb_page_addr0(a) == tb_page_addr0(b) && tb_page_addr1(a) == tb_page_addr1(b)); } @@ -57,8 +70,595 @@ void tb_htable_init(void) qht_init(&tb_ctx.inv_htable, inv_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 page_flush_tb_1(int level, void **lp) +static void tb_remove_all_1(int level, void **lp) { int i; @@ -77,20 +677,91 @@ static void page_flush_tb_1(int level, void **lp) void **pp = *lp; for (i = 0; i < V_L2_SIZE; ++i) { - page_flush_tb_1(level - 1, pp + i); + tb_remove_all_1(level - 1, pp + i); } } } -static void page_flush_tb(void) +static void tb_remove_all(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); + 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 = paddr1 >> 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 = paddr1 >> 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) { @@ -109,11 +780,11 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE); qht_reset_size(&tb_ctx.inv_htable, CODE_GEN_HTABLE_SIZE); - page_flush_tb(); + tb_remove_all(); 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); + qatomic_inc(&tb_ctx.tb_flush_count); done: mmap_unlock(); @@ -125,9 +796,9 @@ done: void tb_flush(CPUState *cpu) { if (tcg_enabled()) { - unsigned tb_flush_count = qatomic_mb_read(&tb_ctx.tb_flush_count); + unsigned tb_flush_count = qatomic_read(&tb_ctx.tb_flush_count); - if (cpu_in_exclusive_context(cpu)) { + 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, @@ -136,28 +807,6 @@ void tb_flush(CPUState *cpu) } } -/* - * 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) { @@ -238,13 +887,13 @@ static void tb_jmp_cache_inval_tb(TranslationBlock *tb) { CPUState *cpu; - if (TARGET_TB_PCREL) { + 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(tb)); + uint32_t h = tb_jmp_cache_hash_func(tb->pc); CPU_FOREACH(cpu) { CPUJumpCache *jc = cpu->tb_jmp_cache; @@ -263,7 +912,6 @@ static void tb_jmp_cache_inval_tb(TranslationBlock *tb) */ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) { - PageDesc *p; uint32_t h; tb_page_addr_t phys_pc; uint32_t orig_cflags = tb_cflags(tb); @@ -278,8 +926,8 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) /* remove the TB from the hash list */ phys_pc = tb_page_addr0(tb); - h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)), - tb->flags, orig_cflags, tb->trace_vcpu_dstate); + 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; } @@ -289,13 +937,7 @@ static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) /* remove the TB from the page list */ if (rm_from_page_list) { - p = page_find(phys_pc >> TARGET_PAGE_BITS); - tb_page_remove(p, tb); - phys_pc = tb_page_addr1(tb); - if (phys_pc != -1) { - p = page_find(phys_pc >> TARGET_PAGE_BITS); - tb_page_remove(p, tb); - } + tb_remove(tb); } /* remove the TB from the hash list */ @@ -319,71 +961,6 @@ static void tb_phys_invalidate__locked(TranslationBlock *tb) qemu_thread_jit_execute(); } -static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, - PageDesc **ret_p2, tb_page_addr_t phys2, bool 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); - } -} - -#ifdef CONFIG_USER_ONLY -static inline void page_lock_tb(const TranslationBlock *tb) { } -static inline void page_unlock_tb(const TranslationBlock *tb) { } -#else -/* lock the page(s) of a TB in the correct acquisition order */ -static void page_lock_tb(const TranslationBlock *tb) -{ - page_lock_pair(NULL, tb_page_addr0(tb), NULL, tb_page_addr1(tb), false); -} - -static void page_unlock_tb(const TranslationBlock *tb) -{ - PageDesc *p1 = page_find(tb_page_addr0(tb) >> TARGET_PAGE_BITS); - - page_unlock(p1); - if (unlikely(tb_page_addr1(tb) != -1)) { - PageDesc *p2 = page_find(tb_page_addr1(tb) >> TARGET_PAGE_BITS); - - if (p2 != p1) { - page_unlock(p2); - } - } -} -#endif - /* * Invalidate one TB. * Called with mmap_lock held in user-mode. @@ -391,53 +968,16 @@ static void page_unlock_tb(const TranslationBlock *tb) void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) { if (page_addr == -1 && tb_page_addr0(tb) != -1) { - page_lock_tb(tb); + tb_lock_pages(tb); do_tb_phys_invalidate(tb, true); - page_unlock_tb(tb); + tb_unlock_pages(tb); } else { do_tb_phys_invalidate(tb, false); } } /* - * 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_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; - -#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. - * + * 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. @@ -445,105 +985,166 @@ static inline void tb_page_add(PageDesc *p, TranslationBlock *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, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2) +TranslationBlock *tb_link_page(TranslationBlock *tb) { - 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, true); - tb_page_add(p, tb, 0, phys_pc); - if (p2) { - tb_page_add(p2, tb, 1, phys_page2); - } + tb_record(tb); /* add in the hash table */ - h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)), - tb->flags, tb->cflags, tb->trace_vcpu_dstate); + 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_page_remove(p, tb); - if (p2) { - tb_page_remove(p2, tb); - } - tb = existing_tb; + tb_remove(tb); + tb_unlock_pages(tb); + return existing_tb; } - if (p2 && p2 != p) { - page_unlock(p2); - } - page_unlock(p); + 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. - * user-mode: call with mmap_lock held. - * !user-mode: call with all @pages locked. + * 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, + tb_page_addr_t last, uintptr_t retaddr) { TranslationBlock *tb; -#ifndef XBOX - tb_page_addr_t tb_start, tb_end; -#endif - int n; + PageForEachNext n; #ifdef TARGET_HAS_PRECISE_SMC - CPUState *cpu = current_cpu; - bool current_tb_not_found = retaddr != 0; bool current_tb_modified = false; - TranslationBlock *current_tb = NULL; + TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL; #endif /* TARGET_HAS_PRECISE_SMC */ - assert_page_locked(p); + /* Range may not cross a page. */ + tcg_debug_assert(((start ^ last) & TARGET_PAGE_MASK) == 0); /* - * We remove all the TBs in the range [start, end[. + * 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(p, tb, n) { - assert_page_locked(p); + PAGE_FOR_EACH_TB(start, last, p, tb, n) { #ifndef XBOX + 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) { - /* NOTE: tb_end may be after the end of the page, but - it is not a problem */ - tb_start = tb_page_addr0(tb); - tb_end = tb_start + tb->size; + tb_last = MIN(tb_last, tb_start | ~TARGET_PAGE_MASK); } else { tb_start = tb_page_addr1(tb); - tb_end = tb_start + ((tb_page_addr0(tb) + tb->size) - & ~TARGET_PAGE_MASK); + tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK); } - if (!(tb_end <= start || tb_start >= end)) { + + if (!(tb_last < start || tb_start > last)) { #else { #endif #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) { /* @@ -554,169 +1155,93 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages, * restore the CPU state. */ current_tb_modified = true; - cpu_restore_state_from_tb(cpu, current_tb, retaddr); + cpu_restore_state_from_tb(current_cpu, current_tb, retaddr); } #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) { 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); + current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); mmap_unlock(); - cpu_loop_exit_noexc(cpu); + cpu_loop_exit_noexc(current_cpu); } #endif } -/* - * Invalidate all TBs which intersect with the target physical - * address page @addr. - * - * Called with mmap_lock held for user-mode emulation - */ -void tb_invalidate_phys_page(tb_page_addr_t addr) -{ - struct page_collection *pages; - tb_page_addr_t start, end; - PageDesc *p; - - assert_memory_lock(); - - p = page_find(addr >> TARGET_PAGE_BITS); - if (p == NULL) { - return; - } - - start = addr & TARGET_PAGE_MASK; - end = start + TARGET_PAGE_SIZE; - 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. + * [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. - * - * Called with mmap_lock held for user-mode emulation. */ -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) +void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) { struct page_collection *pages; - tb_page_addr_t next; + tb_page_addr_t index, index_last; - assert_memory_lock(); + pages = page_collection_lock(start, last); - 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); + 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; } - tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0); + 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); } -#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) +static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages, + tb_page_addr_t start, + unsigned len, uintptr_t ra) { PageDesc *p; - assert_memory_lock(); - 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, - retaddr); + tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra); } -#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). + * 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. */ -bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) +void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, + unsigned size, + uintptr_t retaddr) { - TranslationBlock *tb; - PageDesc *p; - int n; -#ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = NULL; - CPUState *cpu = current_cpu; - bool current_tb_modified = false; -#endif + struct page_collection *pages; - 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); - } -#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 = true; - cpu_restore_state_from_tb(cpu, current_tb, pc); - } -#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; + 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 + +#endif /* CONFIG_USER_ONLY */ diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index 84cc7421be..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,24 +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 iothread lock, so must take it while + * We're called without the BQL, so must take it while * we're calling timer handlers. */ - qemu_mutex_lock_iothread(); + bql_lock(); icount_notify_aio_contexts(); - qemu_mutex_unlock_iothread(); + bql_unlock(); } } @@ -125,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; @@ -140,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..49814ec4af 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; @@ -142,18 +137,10 @@ void mttcg_start_vcpu_thread(CPUState *cpu) g_assert(tcg_enabled()); tcg_cpu_init_cflags(cpu, current_machine->smp.max_cpus > 1); - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_malloc0(sizeof(QemuCond)); - qemu_cond_init(cpu->halt_cond); - /* create a thread per vCPU with TCG (MTTCG) */ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG", cpu->cpu_index); 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 290833a37f..8ebadf8e9e 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" @@ -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) @@ -108,7 +111,7 @@ static void rr_wait_io_event(void) while (all_cpu_threads_idle()) { 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()) { @@ -264,9 +302,7 @@ static void *rr_cpu_thread_fn(void *arg) rr_deal_with_unplugged_cpus(); } - rcu_remove_force_rcu_notifier(&force_rcu); - rcu_unregister_thread(); - return NULL; + g_assert_not_reached(); } void rr_start_vcpu_thread(CPUState *cpu) @@ -279,27 +315,25 @@ void rr_start_vcpu_thread(CPUState *cpu) tcg_cpu_init_cflags(cpu, false); if (!single_tcg_cpu_thread) { - cpu->thread = g_new0(QemuThread, 1); - cpu->halt_cond = g_new0(QemuCond, 1); - qemu_cond_init(cpu->halt_cond); + single_tcg_halt_cond = cpu->halt_cond; + single_tcg_cpu_thread = cpu->thread; /* share a single thread for all cpus with TCG */ snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG"); qemu_thread_create(cpu->thread, thread_name, rr_cpu_thread_fn, cpu, QEMU_THREAD_JOINABLE); - - 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 */ + /* we share the thread, dump spare data */ + g_free(cpu->thread); + qemu_cond_destroy(cpu->halt_cond); + g_free(cpu->halt_cond); cpu->thread = single_tcg_cpu_thread; cpu->halt_cond = single_tcg_halt_cond; + + /* copy the stuff done at start of rr_cpu_thread_fn */ 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 19cbf1db3a..4dd3fad484 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -31,9 +31,13 @@ #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/gdbstub.h" +#include "exec/tb-flush.h" +#include "gdbstub/enums.h" + +#include "hw/core/cpu.h" #include "tcg-accel-ops.h" #include "tcg-accel-ops-mttcg.h" @@ -44,41 +48,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; + tcg_cflags_set(cpu, 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_all_cpus_synced(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; @@ -89,7 +101,7 @@ 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); } } @@ -116,7 +128,7 @@ static inline int xlat_gdb_type(CPUState *cpu, int gdbtype) return cputype; } -static int tcg_insert_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len) +static int tcg_insert_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; @@ -147,7 +159,7 @@ static int tcg_insert_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len } } -static int tcg_remove_breakpoint(CPUState *cs, int type, hwaddr addr, hwaddr len) +static int tcg_remove_breakpoint(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; @@ -203,6 +215,7 @@ static void tcg_accel_ops_init(AccelOpsClass *ops) } } + 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; 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 30b503fb22..2090907dba 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -25,23 +25,26 @@ #include "qemu/osdep.h" #include "sysemu/tcg.h" -#include "sysemu/replay.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-common.h" struct TCGState { AccelState parent_obj; bool mttcg_enabled; + bool one_insn_per_tb; int splitwx_enabled; unsigned long tb_size; }; @@ -61,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) @@ -109,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) { @@ -131,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; @@ -158,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) { @@ -208,6 +193,20 @@ 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) { /* @@ -228,6 +227,8 @@ 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; @@ -245,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..14f638810c 100644 --- a/accel/tcg/trace-events +++ b/accel/tcg/trace-events @@ -6,5 +6,21 @@ 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" + +# ldst_atomicity +load_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +load_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +load_atom8_or_exit_fallback(uintptr_t ra) "ra:0x%"PRIxPTR"" +load_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +load_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +load_atom16_or_exit_fallback(uintptr_t ra) "ra:0x%"PRIxPTR"" +store_atom2_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +store_atom4_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +store_atom8_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" +store_atom16_fallback(uint32_t memop, uintptr_t ra) "mop:0x%"PRIx32", ra:0x%"PRIxPTR"" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index d2a138dbfc..d0c0ce7321 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" @@ -47,11 +46,12 @@ #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" @@ -61,114 +61,22 @@ #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" -#include "internal.h" +#include "internal-common.h" +#include "internal-target.h" +#include "tcg/perf.h" +#include "tcg/insn-start-words.h" #if defined(CONFIG_VTUNE_JITPROFILING) #include #endif -/* make various TB consistency checks */ - -/** - * 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; -}; - -/* - * 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 - -/* 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 - */ -int v_l1_size; -int v_l1_shift; -int v_l2_levels; - -void *l1_map[V_L1_MAX_SIZE]; - 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; @@ -186,21 +94,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; @@ -222,22 +132,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 = (!TARGET_TB_PCREL && j == 0 ? tb_pc(tb) : 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 @@ -265,8 +179,8 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, } memset(data, 0, sizeof(uint64_t) * TARGET_INSN_START_WORDS); - if (!TARGET_TB_PCREL) { - data[0] = tb_pc(tb); + if (!(tb_cflags(tb) & CF_PCREL)) { + data[0] = tb->pc; } /* @@ -293,10 +207,6 @@ void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, uintptr_t host_pc) { uint64_t data[TARGET_INSN_START_WORDS]; -#ifdef CONFIG_PROFILER - TCGProfile *prof = &tcg_ctx->prof; - int64_t ti = profile_getclock(); -#endif int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data); if (insns_left < 0) { @@ -309,16 +219,10 @@ void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, * 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 += insns_left; + cpu->neg.icount_decr.u16.low += insns_left; } cpu->cc->tcg_ops->restore_state_to_opc(cpu, 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 } bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc) @@ -356,402 +260,15 @@ bool cpu_unwind_state_data(CPUState *cpu, uintptr_t host_pc, uint64_t *data) 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 } -PageDesc *page_find_alloc(tb_page_addr_t index, bool 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)); -} - -/* In user-mode page locks aren't used; mmap_lock is enough */ -#ifdef CONFIG_USER_ONLY -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); -} - -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(); - } -} - -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) { } - -#endif /* CONFIG_DEBUG_TCG */ - -void page_lock(PageDesc *pd) -{ - page_lock__debug(pd); - qemu_spin_lock(&pd->lock); -} - -void page_unlock(PageDesc *pd) -{ - qemu_spin_unlock(&pd->lock); - page_unlock__debug(pd); -} - -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. - */ -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 = g_tree_lookup(set->tree, &index); - if (pe) { - return false; - } - - pd = page_find(index); - if (pd == NULL) { - return false; - } - - pe = page_entry_new(pd, index); - g_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,@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_addr0(tb)) || - (tb_page_addr1(tb) != -1 && - page_trylock_add(set, tb_page_addr1(tb)))) { - /* 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 */ - /* * Isolate the portion of code gen which can setjmp/longjmp. * Return the size of the generated code, or negative on error. */ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, - target_ulong pc, void *host_pc, + vaddr pc, void *host_pc, int *max_insns, int64_t *ti) { int ret = sigsetjmp(tcg_ctx->jmp_trans, 0); @@ -762,34 +279,24 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, tcg_func_start(tcg_ctx); tcg_ctx->cpu = env_cpu(env); - gen_intermediate_code(env_cpu(env), tb, *max_insns, pc, host_pc); + gen_intermediate_code(env_cpu(env), tb, max_insns, pc, host_pc); assert(tb->size != 0); tcg_ctx->cpu = NULL; *max_insns = tb->icount; -#ifdef CONFIG_PROFILER - qatomic_set(&tcg_ctx->prof.tb_count, tcg_ctx->prof.tb_count + 1); - qatomic_set(&tcg_ctx->prof.interm_time, - tcg_ctx->prof.interm_time + profile_getclock() - *ti); - *ti = profile_getclock(); -#endif - 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; + 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; -#endif int64_t ti; void *host_pc; bool recycled = false; @@ -801,7 +308,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, 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; @@ -815,16 +322,22 @@ TranslationBlock *tb_gen_code(CPUState *cpu, qemu_spin_lock(&tb->jmp_lock); qatomic_set(&tb->cflags, tb->cflags & ~CF_INVALID); qemu_spin_unlock(&tb->jmp_lock); - uint32_t h = tb_hash_func(phys_pc, (TARGET_TB_PCREL ? 0 : tb_pc(tb)), - tb->flags, tb_cflags(tb), - tb->trace_vcpu_dstate); + uint32_t h = tb_hash_func(phys_pc, (tb->cflags & CF_PCREL ? 0 : tb->pc), + tb->flags, tb->cs_base, tb->cflags); bool removed = qht_remove(&tb_ctx.inv_htable, tb, h); g_assert(removed); + if (phys_pc != -1) { + tb_lock_page0(phys_pc); + if (tb->page_addr[1] != -1) { + tb_lock_page1(phys_pc, tb->page_addr[1]); + } + } recycled = true; goto recycle_tb; } buffer_overflow: + assert_no_pages_locked(); tb = tcg_tb_alloc(tcg_ctx); if (unlikely(!tb)) { /* flush must be done */ @@ -837,24 +350,33 @@ TranslationBlock *tb_gen_code(CPUState *cpu, gen_code_buf = tcg_ctx->code_gen_ptr; tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); -#if !TARGET_TB_PCREL - tb->pc = pc; -#endif + 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; tb_set_page_addr0(tb, phys_pc); tb_set_page_addr1(tb, -1); - tcg_ctx->tb_cflags = cflags; - tb_overflow: + if (phys_pc != -1) { + tb_lock_page0(phys_pc); + } -#ifdef CONFIG_PROFILER - /* includes aborted translations because of exceptions */ - qatomic_set(&prof->tb_count1, prof->tb_count1 + 1); - 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 + restart_translate: trace_translate_block(tb, pc, tb->tc.ptr); gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); @@ -873,6 +395,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: @@ -891,26 +415,49 @@ 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(pc)) { FILE *logfile = qemu_log_trylock(); @@ -933,8 +480,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); @@ -946,8 +493,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; @@ -983,7 +530,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, @@ -1000,10 +546,10 @@ recycle_tb: 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); } @@ -1013,6 +559,7 @@ recycle_tb: * before attempting to link to other TBs or add to the lookup table. */ if (tb_page_addr0(tb) == -1) { + assert_no_pages_locked(); return tb; } @@ -1027,7 +574,9 @@ recycle_tb: * No explicit memory barrier is required -- tb_link_page() makes the * TB visible in a consistent state. */ - existing_tb = tb_link_page(tb, tb_page_addr0(tb), tb_page_addr1(tb)); + 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)) { if (!recycled) { @@ -1072,15 +621,16 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) } 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); } } } @@ -1114,7 +664,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; } @@ -1124,488 +674,19 @@ 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; if (qemu_loglevel_mask(CPU_LOG_EXEC)) { - target_ulong pc = log_pc(cpu, tb); + vaddr pc = cpu->cc->get_pc(cpu); if (qemu_log_in_addr_range(pc)) { - qemu_log("cpu_io_recompile: rewound execution of TB to " - TARGET_FMT_lx "\n", pc); + qemu_log("cpu_io_recompile: rewound execution of TB to %016" + VADDR_PRIx "\n", 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_addr1(tb) != -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++; - } - } - 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); -} - -#else /* CONFIG_USER_ONLY */ - -void cpu_interrupt(CPUState *cpu, int mask) -{ - g_assert(qemu_mutex_iothread_locked()); - cpu->interrupt_request |= mask; - qatomic_set(&cpu_neg(cpu)->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; -} - -/* - * 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) - -/* 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, 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(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 = !(flags & PAGE_VALID) || (flags & PAGE_RESET); - if (reset) { - page_reset_target_data(start, end); - } - 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, true); - - /* - * If the page was executable, but is reset, or is no longer - * executable, or has become writable, then invalidate any code. - */ - if ((p->flags & PAGE_EXEC) - && (reset || - !(flags & PAGE_EXEC) || - (flags & ~p->flags & PAGE_WRITE))) { - inval_tb = true; - } - /* Using mprotect on a page does not change sticky bits. */ - p->flags = (reset ? 0 : p->flags & PAGE_STICKY) | flags; - } - - if (inval_tb) { - tb_invalidate_phys_range(start, end); - } -} - -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); - } -} - -/* 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_unwind(addr, pc); - } - 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 */ /* @@ -1625,11 +706,3 @@ void tcg_flush_jmp_cache(CPUState *cpu) qatomic_set(&jc->array[i].tb, NULL); } } - -/* This is a wrapper for common code that can not use CONFIG_SOFTMMU */ -void tcg_flush_softmmu_tlb(CPUState *cs) -{ -#ifdef CONFIG_SOFTMMU - tlb_flush(cs); -#endif -} diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 3a04b02f62..9e4410c19f 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -8,31 +8,102 @@ */ #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/cpu_ldst.h" #include "exec/plugin-gen.h" -#include "sysemu/replay.h" #include "accel/tcg/tb-hash.h" +#include "exec/cpu_ldst.h" +#include "tcg/tcg-op-common.h" +#include "internal-target.h" +#include "disas/disas.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) { @@ -43,11 +114,13 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; } -void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc, - const TranslatorOps *ops, DisasContextBase *db) +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 */ @@ -56,49 +129,57 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns, db->pc_next = pc; db->is_jmp = DISAS_NEXT; db->num_insns = 0; - db->max_insns = max_insns; - db->singlestep_enabled = cflags & CF_SINGLE_STEP; + db->max_insns = *max_insns; + db->insn_start = NULL; + db->fake_insn = false; db->host_addr[0] = host_pc; db->host_addr[1] = NULL; - -#ifdef CONFIG_USER_ONLY - page_protect(pc); -#endif + db->record_start = 0; + db->record_len = 0; 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, db, cflags & CF_MEMI_ONLY); + plugin_enabled = plugin_gen_tb_start(cpu, db); + 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. */ @@ -106,14 +187,6 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns, 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) { @@ -124,129 +197,280 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns, /* 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); - if (plugin_enabled) { - plugin_gen_tb_end(cpu); + /* + * 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; - /* The disas_log hook may use these values rather than recompute. */ + /* May be used by disas_log or plugin callbacks. */ tb->size = db->pc_next - db->pc_first; tb->icount = db->num_insns; - tb->ihash = tb_code_hash_func(cpu->env_ptr, db->pc_first, tb->size); + tb->ihash = tb_code_hash_func(cpu_env(cpu), db->pc_first, tb->size); + + if (plugin_enabled) { + plugin_gen_tb_end(cpu, 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(); if (logfile) { fprintf(logfile, "----------------\n"); - ops->disas_log(db, cpu, logfile); + + if (!ops->disas_log || + !ops->disas_log(db, cpu, logfile)) { + fprintf(logfile, "IN: %s\n", lookup_symbol(db->pc_first)); + target_disas(logfile, cpu, db); + } fprintf(logfile, "\n"); qemu_log_unlock(logfile); } } -#endif } -static void *translator_access(CPUArchState *env, DisasContextBase *db, - target_ulong pc, size_t len) +static bool translator_ld(CPUArchState *env, DisasContextBase *db, + void *dest, vaddr pc, size_t len) { + TranslationBlock *tb = db->tb; + vaddr last = pc + len - 1; void *host; - target_ulong base, end; - TranslationBlock *tb; - - tb = db->tb; + vaddr base; /* Use slow path if first page is MMIO. */ if (unlikely(tb_page_addr0(tb) == -1)) { - return NULL; + /* We capped translation with first page MMIO in tb_gen_code. */ + tcg_debug_assert(db->max_insns == 1); + return false; } - 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[0]; + base = db->pc_first; + + if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) { + /* Entire read is from the first page. */ + memcpy(dest, host + (pc - base), len); + return true; + } + + if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) { + /* Read begins on the first page and extends to the second. */ + size_t len0 = -(pc | TARGET_PAGE_MASK); + memcpy(dest, host + (pc - base), len0); + pc += len0; + dest += len0; + len -= len0; + } + + /* + * The read must conclude on the second page and not extend to a third. + * + * TODO: We could allow the two pages to be virtually discontiguous, + * since we already allow the two pages to be physically discontiguous. + * The only reasonable use case would be executing an insn at the end + * of the address space wrapping around to the beginning. For that, + * we would need to know the current width of the address space. + * In the meantime, assert. + */ + base = (base & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + assert(((base ^ pc) & TARGET_PAGE_MASK) == 0); + assert(((base ^ last) & TARGET_PAGE_MASK) == 0); + host = db->host_addr[1]; + + 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); + /* Require that this be the final insn. */ + db->max_insns = db->num_insns; + return false; + } + + /* + * 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]; - base = TARGET_PAGE_ALIGN(db->pc_first); - if (host == NULL) { - tb_page_addr_t phys_page = - get_page_addr_code_hostp(env, base, &db->host_addr[1]); - /* We cannot handle MMIO as second page. */ - assert(phys_page != -1); - tb_set_page_addr1(tb, phys_page); -#ifdef CONFIG_USER_ONLY - page_protect(end); -#endif - host = db->host_addr[1]; + } + + memcpy(dest, host + (pc - base), len); + return true; +} + +static void record_save(DisasContextBase *db, vaddr pc, + const void *from, int size) +{ + int offset; + + /* Do not record probes before the start of TB. */ + if (pc < db->pc_first) { + return; + } + + /* + * In translator_access, we verified that pc is within 2 pages + * of pc_first, thus this will never overflow. + */ + offset = pc - db->pc_first; + + /* + * Either the first or second page may be I/O. If it is the second, + * then the first byte we need to record will be at a non-zero offset. + * In either case, we should not need to record but a single insn. + */ + if (db->record_len == 0) { + db->record_start = offset; + db->record_len = size; + } else { + assert(offset == db->record_start + db->record_len); + assert(db->record_len + size <= sizeof(db->record)); + db->record_len += size; + } + + memcpy(db->record + (offset - db->record_start), from, size); +} + +size_t translator_st_len(const DisasContextBase *db) +{ + return db->fake_insn ? db->record_len : db->tb->size; +} + +bool translator_st(const DisasContextBase *db, void *dest, + vaddr addr, size_t len) +{ + size_t offset, offset_end; + + if (addr < db->pc_first) { + return false; + } + offset = addr - db->pc_first; + offset_end = offset + len; + if (offset_end > translator_st_len(db)) { + return false; + } + + if (!db->fake_insn) { + size_t offset_page1 = -(db->pc_first | TARGET_PAGE_MASK); + + /* Get all the bytes from the first page. */ + if (db->host_addr[0]) { + if (offset_end <= offset_page1) { + memcpy(dest, db->host_addr[0] + offset, len); + return true; + } + if (offset < offset_page1) { + size_t len0 = offset_page1 - offset; + memcpy(dest, db->host_addr[0] + offset, len0); + offset += len0; + dest += len0; + } } - /* Use slow path when crossing pages. */ - if (is_same_page(db, pc)) { - return NULL; + /* Get any bytes from the second page. */ + if (db->host_addr[1] && offset >= offset_page1) { + memcpy(dest, db->host_addr[1] + (offset - offset_page1), + offset_end - offset); + return true; } } - tcg_debug_assert(pc >= base); - return host + (pc - base); -} - -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); + /* Else get recorded bytes. */ + if (db->record_len != 0 && + offset >= db->record_start && + offset_end <= db->record_start + db->record_len) { + memcpy(dest, db->record + (offset - db->record_start), + offset_end - offset); + return true; } - ret = cpu_ldub_code(env, pc); - plugin_insn_append(pc, &ret, sizeof(ret)); - return ret; + return false; } -uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint16_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint8_t raw; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return lduw_p(p); + if (!translator_ld(env, db, &raw, pc, sizeof(raw))) { + raw = cpu_ldub_code(env, pc); + record_save(db, pc, &raw, sizeof(raw)); } - ret = cpu_lduw_code(env, pc); - plug = tswap16(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + return raw; } -uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint32_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint16_t raw, tgt; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldl_p(p); + if (translator_ld(env, db, &raw, pc, sizeof(raw))) { + tgt = tswap16(raw); + } else { + tgt = cpu_lduw_code(env, pc); + raw = tswap16(tgt); + record_save(db, pc, &raw, sizeof(raw)); } - ret = cpu_ldl_code(env, pc); - plug = tswap32(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + return tgt; } -uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) +uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint64_t ret, plug; - void *p = translator_access(env, db, pc, sizeof(ret)); + uint32_t raw, tgt; - if (p) { - plugin_insn_append(pc, p, sizeof(ret)); - return ldq_p(p); + if (translator_ld(env, db, &raw, pc, sizeof(raw))) { + tgt = tswap32(raw); + } else { + tgt = cpu_ldl_code(env, pc); + raw = tswap32(tgt); + record_save(db, pc, &raw, sizeof(raw)); } - ret = cpu_ldq_code(env, pc); - plug = tswap64(ret); - plugin_insn_append(pc, &plug, sizeof(ret)); - return ret; + return tgt; +} + +uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + uint64_t raw, tgt; + + if (translator_ld(env, db, &raw, pc, sizeof(raw))) { + tgt = tswap64(raw); + } else { + tgt = cpu_ldq_code(env, pc); + raw = tswap64(tgt); + record_save(db, pc, &raw, sizeof(raw)); + } + return tgt; +} + +void translator_fake_ld(DisasContextBase *db, const void *data, size_t len) +{ + db->fake_insn = true; + record_save(db, db->pc_first, data, len); } 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 fb7d6ee9e9..06016eb030 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -22,18 +22,29 @@ #include "exec/exec-all.h" #include "tcg/tcg.h" #include "qemu/bitops.h" +#include "qemu/rcu.h" #include "exec/cpu_ldst.h" +#include "qemu/main-loop.h" #include "exec/translate-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #include "qemu/atomic128.h" -#include "trace/trace-root.h" +#include "trace.h" #include "tcg/tcg-ldst.h" -#include "internal.h" +#include "internal-common.h" +#include "internal-target.h" __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL +void cpu_interrupt(CPUState *cpu, int mask) +{ + g_assert(bql_locked()); + cpu->interrupt_request |= mask; + qatomic_set(&cpu->neg.icount_decr.u16.high, -1); +} + /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ @@ -135,7 +146,637 @@ 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; +} + +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_RWX); + } + 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) { @@ -159,6 +800,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 (access_type != MMU_INST_FETCH + && cpu_plugin_mem_cbs_enabled(env_cpu(env))) { + return TLB_MMIO; + } return 0; /* success */ } maperr = !(page_flags & PAGE_VALID); @@ -173,30 +818,31 @@ 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; } -tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr, +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, void **hostp) { int flags; @@ -210,272 +856,278 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr, return addr; } -void page_reset_target_data(target_ulong start, target_ulong end) -{ #ifdef TARGET_PAGE_DATA_SIZE - target_ulong addr, len; +/* + * 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%. + */ +#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) +{ + IntervalTreeNode *n, *next; - /* - * 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); assert_memory_lock(); - start = start & TARGET_PAGE_MASK; - end = TARGET_PAGE_ALIGN(end); + start &= TARGET_PAGE_MASK; + last |= ~TARGET_PAGE_MASK; - 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); + 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); - g_free(p->target_data); - p->target_data = NULL; + 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); } -#endif } -#ifdef TARGET_PAGE_DATA_SIZE void *page_get_target_data(target_ulong address) { - PageDesc *p = page_find(address >> TARGET_PAGE_BITS); - void *ret = p->target_data; + IntervalTreeNode *n; + TargetPageDataNode *t; + target_ulong page, region, p_ofs; - if (!ret) { - ret = g_malloc0(TARGET_PAGE_DATA_SIZE); - p->target_data = ret; + page = address & TARGET_PAGE_MASK; + region = address & TBD_MASK; + + 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(); } - return ret; + + t = container_of(n, TargetPageDataNode, itree); + p_ofs = (page - region) >> TARGET_PAGE_BITS; + return t->data + p_ofs * TARGET_PAGE_DATA_SIZE; } -#endif +#else +void page_reset_target_data(target_ulong start, target_ulong last) { } +#endif /* TARGET_PAGE_DATA_SIZE */ -/* The softmmu versions of these helpers are in cputlb.c. */ +/* The system-mode versions of these helpers are in cputlb.c. */ -/* - * 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. - */ -static void validate_memop(MemOpIdx oi, MemOp expected) +static void *cpu_mmu_lookup(CPUState *cpu, vaddr addr, + MemOp mop, uintptr_t ra, MMUAccessType type) { -#ifdef CONFIG_DEBUG_TCG - MemOp have = get_memop(oi) & (MO_SIZE | MO_BSWAP); - assert(have == expected); -#endif -} - -void helper_unaligned_ld(CPUArchState *env, target_ulong addr) -{ - cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_LOAD, GETPC()); -} - -void helper_unaligned_st(CPUArchState *env, target_ulong addr) -{ - cpu_loop_exit_sigbus(env_cpu(env), addr, MMU_DATA_STORE, GETPC()); -} - -static void *cpu_mmu_lookup(CPUArchState *env, target_ulong addr, - MemOpIdx oi, uintptr_t ra, MMUAccessType type) -{ - MemOp mop = get_memop(oi); - int a_bits = get_alignment_bits(mop); + int a_bits = memop_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) @@ -518,33 +1170,86 @@ 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); + int a_bits = memop_alignment_bits(mop); void *ret; /* 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; } @@ -574,7 +1279,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/vcpu-state.h b/accel/tcg/vcpu-state.h new file mode 100644 index 0000000000..e407d914df --- /dev/null +++ b/accel/tcg/vcpu-state.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileContributor: Philippe Mathieu-Daudé + * SPDX-FileCopyrightText: 2023 Linaro Ltd. + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ACCEL_TCG_VCPU_STATE_H +#define ACCEL_TCG_VCPU_STATE_H + +#include "hw/core/cpu.h" + +#ifdef CONFIG_USER_ONLY +static inline TaskState *get_task_state(const CPUState *cs) +{ + return cs->opaque; +} +#endif + +#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 7a2a94cd42..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; @@ -908,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); @@ -917,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; @@ -969,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 065602ce1b..af0ae33fed 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -28,9 +28,14 @@ #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" @@ -58,19 +63,22 @@ 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; @@ -96,6 +104,7 @@ audio_driver *audio_driver_lookup(const char *name) 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, @@ -108,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) { @@ -146,26 +153,6 @@ static inline int audio_bits_to_index (int bits) } } -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); - return NULL; - } - - return g_malloc0 (len); -} - void AUD_vlog (const char *cap, const char *fmt, va_list ap) { if (cap) { @@ -398,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) { @@ -502,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 @@ -545,8 +518,8 @@ 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); + 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; @@ -555,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; @@ -573,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); + 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; } /* @@ -658,8 +640,8 @@ 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); + 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; @@ -676,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; } @@ -695,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); + 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 @@ -990,18 +1001,6 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) } } -/** - * audio_frontend_frames_in() - returns the number of frames the resampling - * code generates from frames_in frames - * - * @sw: audio recording frontend - * @frames_in: number of frames - */ -static size_t audio_frontend_frames_in(SWVoiceIn *sw, size_t frames_in) -{ - return (int64_t)frames_in * sw->ratio >> 32; -} - static size_t audio_get_avail (SWVoiceIn *sw) { size_t live; @@ -1011,33 +1010,21 @@ 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); + 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 frontend frames %zu\n", + "%s: get_avail live %zu frontend frames %u\n", SW_NAME (sw), - live, audio_frontend_frames_in(sw, live) + live, st_rate_frames_out(sw->rate, live) ); return live; } -/** - * audio_frontend_frames_out() - returns the number of frames needed to - * get frames_out frames after resampling - * - * @sw: audio playback frontend - * @frames_out: number of frames - */ -static size_t audio_frontend_frames_out(SWVoiceOut *sw, size_t frames_out) -{ - return ((int64_t)frames_out << 32) / sw->ratio; -} - static size_t audio_get_free(SWVoiceOut *sw) { size_t live, dead; @@ -1048,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); + 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 frontend frames %zu\n", - SW_NAME(sw), live, dead, audio_frontend_frames_out(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; @@ -1074,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) @@ -1125,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; @@ -1179,12 +1174,14 @@ static void audio_run_out (AudioState *s) size_t free; if (hw_free > sw->total_hw_samples_mixed) { - free = audio_frontend_frames_out(sw, + free = st_rate_frames_in(sw->rate, MIN(sw_free, hw_free - sw->total_hw_samples_mixed)); } else { free = 0; } - if (free > 0) { + 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); } @@ -1196,8 +1193,8 @@ 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); + if (audio_bug(__func__, live > hw->mix_buf.size)) { + dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size); continue; } @@ -1225,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); - hw->mix_buf->pos = 0; + 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 @@ -1312,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; @@ -1328,8 +1325,9 @@ static void audio_run_in (AudioState *s) size_t sw_avail = audio_get_avail(sw); size_t avail; - avail = audio_frontend_frames_in(sw, sw_avail); + avail = st_rate_frames_out(sw->rate, sw_avail); if (avail > 0) { + avail = MIN(avail, sw->resample_buf.size); sw->callback.fn(sw->callback.opaque, avail * sw->info.bytes_per_frame); } @@ -1348,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); @@ -1363,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) { @@ -1559,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) { @@ -1573,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; } @@ -1659,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); @@ -1680,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; } /* @@ -1706,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); @@ -1773,56 +1736,39 @@ static AudioState *audio_init(Audiodev *dev, const char *name) 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 < 1) { - 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) { - free_audio_state(s); - return NULL; + 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; @@ -1830,37 +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) @@ -1882,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) { @@ -1906,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)); @@ -1925,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]; @@ -1943,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) @@ -1977,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); } @@ -2035,29 +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: @@ -2169,17 +2147,24 @@ void audio_define(Audiodev *dev) QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next); } -bool audio_init_audiodevs(void) +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) { - if (!audio_init(e->dev, NULL)) { - return false; - } + audio_init(e->dev, &error_fatal); } - - return true; } audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo) @@ -2241,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) { @@ -2250,6 +2235,7 @@ AudioState *audio_state_by_name(const char *name) return s; } } + error_setg(errp, "audiodev '%s' not found", name); return NULL; } @@ -2313,3 +2299,13 @@ size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info, return bytes; } + +AudiodevList *qmp_query_audiodevs(Error **errp) +{ + AudiodevList *ret = NULL; + AudiodevListEntry *e; + QSIMPLEQ_FOREACH(e, &audiodevs, next) { + QAPI_LIST_PREPEND(ret, QAPI_CLONE(Audiodev, e->dev)); + } + return ret; +} diff --git a/audio/audio.h b/audio/audio.h index 01bdc567fb..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,12 +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); -bool audio_init_audiodevs(void); +void audio_create_default_audiodevs(void); +void audio_init_audiodevs(void); void audio_help(void); -void audio_legacy_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 e87ce014a0..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); @@ -294,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 720a32e57e..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,6 +57,12 @@ 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); @@ -63,16 +70,17 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, } if (audio_bug(__func__, voice_size && !max_voices)) { - dolog ("drv=`%s' voice_size=%d max_voices=0\n", - drv->name, voice_size); + 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) @@ -83,56 +91,67 @@ static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) dolog("Attempted to allocate empty buffer\n"); } - 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; } -#ifdef DAC - samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio; -#else - samples = (int64_t)sw->HWBUF->size * sw->ratio >> 32; -#endif + 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; } @@ -149,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) { @@ -264,13 +280,11 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, return NULL; } - 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; @@ -326,27 +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; @@ -398,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; } @@ -495,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; } @@ -507,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..cadd729d50 100644 --- a/audio/coreaudio.m +++ b/audio/coreaudio.m @@ -44,11 +44,6 @@ typedef struct coreaudioVoiceOut { bool enabled; } coreaudioVoiceOut; -#if !defined(MAC_OS_VERSION_12_0) \ - || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) -#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster -#endif - static const AudioObjectPropertyAddress voice_addr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, @@ -299,7 +294,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 +533,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 +542,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 +552,7 @@ static OSStatus handle_voice_change( update_device_playback_state(core); } - qemu_mutex_unlock_iothread(); + bql_unlock(); return 0; } @@ -644,7 +639,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 +668,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 722df0355e..095e739382 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; @@ -100,7 +105,7 @@ static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size); vo->buf_pos += size; - trace_dbus_audio_put_buffer_out(size); + trace_dbus_audio_put_buffer_out(vo->buf_pos, vo->buf_size); if (vo->buf_pos < vo->buf_size) { return size; @@ -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 34aed78342..59f0a431d5 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 : [ @@ -18,6 +18,7 @@ foreach m : [ ['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() @@ -29,7 +30,8 @@ endforeach if dbus_display module_ss = ss.source_set() - module_ss.add(when: gio, if_true: files('dbusaudio.c')) + module_ss.add(when: [gio, pixman], + if_true: [dbus_display1, 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 4fdee5adec..1b60d8518a 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -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..8e13b58286 --- /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); + error_setg_errno(errp, errno, "Failed to connect to PipeWire instance"); + goto fail; + } + + if (pw_core_add_listener(pw->core, &pw->core_listener, + &core_events, pw) < 0) { + pw_thread_loop_unlock(pw->thread_loop); + error_setg(errp, "Failed to add PipeWire listener"); + goto fail; + } + 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: + 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 b432719ebb..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,15 +57,17 @@ 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++; @@ -78,6 +78,11 @@ void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, } } + /* 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 */ 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 index 7c45276d36..8eb35e1e53 100644 --- a/audio/sndioaudio.c +++ b/audio/sndioaudio.c @@ -14,9 +14,9 @@ * to recording, which is what guest systems expect. */ +#include "qemu/osdep.h" #include #include -#include "qemu/osdep.h" #include "qemu/main-loop.h" #include "audio.h" #include "trace.h" @@ -333,7 +333,7 @@ static int sndio_init(SndioVoice *self, unsigned int nch; int i, nfds; - dev_name = opts->has_dev ? opts->dev : SIO_DEVANY; + dev_name = opts->dev ?: SIO_DEVANY; latency = opts->has_latency ? opts->latency : SNDIO_LATENCY_US; /* open the device in non-blocking mode */ @@ -518,7 +518,7 @@ static void sndio_fini_in(HWVoiceIn *hw) sndio_fini(self); } -static void *sndio_audio_init(Audiodev *dev) +static void *sndio_audio_init(Audiodev *dev, Error **errp) { assert(dev->driver == AUDIODEV_DRIVER_SNDIO); return dev; @@ -550,7 +550,6 @@ static struct audio_driver sndio_audio_driver = { .init = sndio_audio_init, .fini = sndio_audio_fini, .pcm_ops = &sndio_pcm_ops, - .can_be_default = 1, .max_voices_out = INT_MAX, .max_voices_in = INT_MAX, .voice_size_out = sizeof(SndioVoice), diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index d17ef1a25e..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; } diff --git a/audio/trace-events b/audio/trace-events index e1ab643add..7e3f1593c8 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -15,9 +15,17 @@ oss_version(int version) "OSS version = 0x%x" # dbusaudio.c 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_put_buffer_out(size_t pos, size_t size) "buf_pos = %zu, buf_size = %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 3e1d84db83..a8798a1c42 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -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..d3dbe19868 100644 --- a/backends/Kconfig +++ b/backends/Kconfig @@ -1 +1,9 @@ source tpm/Kconfig + +config IOMMUFD + bool + depends on VFIO + +config SPDM_SOCKET + bool + default y diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index cda6ca3b71..b1486be630 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -23,6 +23,7 @@ #include "qemu/osdep.h" #include "sysemu/cryptodev.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "standard-headers/linux/virtio_crypto.h" #include "crypto/cipher.h" @@ -59,6 +60,19 @@ struct CryptoDevBackendBuiltin { CryptoDevBackendBuiltinSession *sessions[MAX_NUM_SESSIONS]; }; +static void cryptodev_builtin_init_akcipher(CryptoDevBackend *backend) +{ + QCryptoAkCipherOptions opts; + + opts.alg = QCRYPTO_AK_CIPHER_ALGO_RSA; + opts.u.rsa.padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW; + if (qcrypto_akcipher_supports(&opts)) { + backend->conf.crypto_services |= + (1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER); + backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; + } +} + static void cryptodev_builtin_init( CryptoDevBackend *backend, Error **errp) { @@ -72,21 +86,18 @@ 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 << VIRTIO_CRYPTO_SERVICE_AKCIPHER; + 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_CIPHER | + 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_HASH | + 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC; backend->conf.cipher_algo_l = 1u << VIRTIO_CRYPTO_CIPHER_AES_CBC; backend->conf.hash_algo = 1u << VIRTIO_CRYPTO_HASH_SHA1; - backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; /* * Set the Maximum length of crypto request. * Why this value? Just avoid to overflow when @@ -95,6 +106,7 @@ static void cryptodev_builtin_init( 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); } @@ -126,18 +138,18 @@ cryptodev_builtin_get_aes_algo(uint32_t key_len, int mode, Error **errp) int algo; if (key_len == AES_KEYSIZE_128) { - algo = QCRYPTO_CIPHER_ALG_AES_128; + algo = QCRYPTO_CIPHER_ALGO_AES_128; } else if (key_len == AES_KEYSIZE_192) { - algo = QCRYPTO_CIPHER_ALG_AES_192; + algo = QCRYPTO_CIPHER_ALGO_AES_192; } else if (key_len == AES_KEYSIZE_256) { /* equals AES_KEYSIZE_128_XTS */ if (mode == QCRYPTO_CIPHER_MODE_XTS) { - algo = QCRYPTO_CIPHER_ALG_AES_128; + algo = QCRYPTO_CIPHER_ALGO_AES_128; } else { - algo = QCRYPTO_CIPHER_ALG_AES_256; + algo = QCRYPTO_CIPHER_ALGO_AES_256; } } else if (key_len == AES_KEYSIZE_256_XTS) { if (mode == QCRYPTO_CIPHER_MODE_XTS) { - algo = QCRYPTO_CIPHER_ALG_AES_256; + algo = QCRYPTO_CIPHER_ALGO_AES_256; } else { goto err; } @@ -157,16 +169,16 @@ static int cryptodev_builtin_get_rsa_hash_algo( { switch (virtio_rsa_hash) { case VIRTIO_CRYPTO_RSA_MD5: - return QCRYPTO_HASH_ALG_MD5; + return QCRYPTO_HASH_ALGO_MD5; case VIRTIO_CRYPTO_RSA_SHA1: - return QCRYPTO_HASH_ALG_SHA1; + return QCRYPTO_HASH_ALGO_SHA1; case VIRTIO_CRYPTO_RSA_SHA256: - return QCRYPTO_HASH_ALG_SHA256; + return QCRYPTO_HASH_ALGO_SHA256; case VIRTIO_CRYPTO_RSA_SHA512: - return QCRYPTO_HASH_ALG_SHA512; + return QCRYPTO_HASH_ALGO_SHA512; default: error_setg(errp, "Unsupported rsa hash algo: %d", virtio_rsa_hash); @@ -188,12 +200,12 @@ static int cryptodev_builtin_set_rsa_options( return -1; } opt->hash_alg = hash_alg; - opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1; + opt->padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1; return 0; } if (virtio_padding_algo == VIRTIO_CRYPTO_RSA_RAW_PADDING) { - opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + opt->padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW; return 0; } @@ -259,15 +271,15 @@ static int cryptodev_builtin_create_cipher_session( break; case VIRTIO_CRYPTO_CIPHER_3DES_ECB: mode = QCRYPTO_CIPHER_MODE_ECB; - algo = QCRYPTO_CIPHER_ALG_3DES; + algo = QCRYPTO_CIPHER_ALGO_3DES; break; case VIRTIO_CRYPTO_CIPHER_3DES_CBC: mode = QCRYPTO_CIPHER_MODE_CBC; - algo = QCRYPTO_CIPHER_ALG_3DES; + algo = QCRYPTO_CIPHER_ALGO_3DES; break; case VIRTIO_CRYPTO_CIPHER_3DES_CTR: mode = QCRYPTO_CIPHER_MODE_CTR; - algo = QCRYPTO_CIPHER_ALG_3DES; + algo = QCRYPTO_CIPHER_ALGO_3DES; break; default: error_setg(errp, "Unsupported cipher alg :%u", @@ -306,7 +318,7 @@ static int cryptodev_builtin_create_akcipher_session( switch (sess_info->algo) { case VIRTIO_CRYPTO_AKCIPHER_RSA: - opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; + opts.alg = QCRYPTO_AK_CIPHER_ALGO_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; @@ -322,11 +334,11 @@ static int cryptodev_builtin_create_akcipher_session( switch (sess_info->keytype) { case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: - type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC; + type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC; break; case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: - type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE; + type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE; break; default: @@ -385,8 +397,8 @@ static int cryptodev_builtin_create_session( case VIRTIO_CRYPTO_HASH_CREATE_SESSION: case VIRTIO_CRYPTO_MAC_CREATE_SESSION: default: - error_setg(&local_error, "Unsupported opcode :%" PRIu32 "", - sess_info->op_code); + error_report("Unsupported opcode :%" PRIu32 "", + sess_info->op_code); return -VIRTIO_CRYPTO_NOTSUPP; } @@ -416,7 +428,9 @@ static int cryptodev_builtin_close_session( CRYPTODEV_BACKEND_BUILTIN(backend); CryptoDevBackendBuiltinSession *session; - assert(session_id < MAX_NUM_SESSIONS && builtin->sessions[session_id]); + if (session_id >= MAX_NUM_SESSIONS || !builtin->sessions[session_id]) { + return -VIRTIO_CRYPTO_INVSESS; + } session = builtin->sessions[session_id]; if (session->cipher) { @@ -528,33 +542,30 @@ static int cryptodev_builtin_asym_operation( static int cryptodev_builtin_operation( CryptoDevBackend *backend, - CryptoDevBackendOpInfo *op_info, - uint32_t queue_index, - CryptoDevCompletionFunc cb, - void *opaque) + CryptoDevBackendOpInfo *op_info) { CryptoDevBackendBuiltin *builtin = CRYPTODEV_BACKEND_BUILTIN(backend); CryptoDevBackendBuiltinSession *sess; CryptoDevBackendSymOpInfo *sym_op_info; CryptoDevBackendAsymOpInfo *asym_op_info; - enum CryptoDevBackendAlgType algtype = op_info->algtype; + QCryptodevBackendAlgoType 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); + error_report("Cannot find a valid session id: %" PRIu64 "", + op_info->session_id); return -VIRTIO_CRYPTO_INVSESS; } sess = builtin->sessions[op_info->session_id]; - if (algtype == CRYPTODEV_BACKEND_ALG_SYM) { + if (algtype == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { sym_op_info = op_info->u.sym_op_info; status = cryptodev_builtin_sym_operation(sess, sym_op_info, &local_error); - } else if (algtype == CRYPTODEV_BACKEND_ALG_ASYM) { + } else if (algtype == QCRYPTODEV_BACKEND_ALGO_TYPE_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); @@ -563,8 +574,8 @@ static int cryptodev_builtin_operation( if (local_error) { error_report_err(local_error); } - if (cb) { - cb(opaque, status); + if (op_info->cb) { + op_info->cb(op_info->opaque, status); } return 0; } 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 index 133bd706a4..38deac0717 100644 --- a/backends/cryptodev-lkcf.c +++ b/backends/cryptodev-lkcf.c @@ -133,20 +133,20 @@ static int cryptodev_lkcf_set_op_desc(QCryptoAkCipherOptions *opts, Error **errp) { QCryptoAkCipherOptionsRSA *rsa_opt; - if (opts->alg != QCRYPTO_AKCIPHER_ALG_RSA) { + if (opts->alg != QCRYPTO_AK_CIPHER_ALGO_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) { + if (rsa_opt->padding_alg == QCRYPTO_RSA_PADDING_ALGO_PKCS1) { snprintf(key_desc, desc_len, "enc=%s hash=%s", - QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg), - QCryptoHashAlgorithm_str(rsa_opt->hash_alg)); + QCryptoRSAPaddingAlgo_str(rsa_opt->padding_alg), + QCryptoHashAlgo_str(rsa_opt->hash_alg)); } else { snprintf(key_desc, desc_len, "enc=%s", - QCryptoRSAPaddingAlgorithm_str(rsa_opt->padding_alg)); + QCryptoRSAPaddingAlgo_str(rsa_opt->padding_alg)); } return 0; } @@ -157,23 +157,23 @@ static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg, Error **errp) { if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_PKCS1_PADDING) { - opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1; + opt->padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1; switch (virtio_hash_alg) { case VIRTIO_CRYPTO_RSA_MD5: - opt->hash_alg = QCRYPTO_HASH_ALG_MD5; + opt->hash_alg = QCRYPTO_HASH_ALGO_MD5; break; case VIRTIO_CRYPTO_RSA_SHA1: - opt->hash_alg = QCRYPTO_HASH_ALG_SHA1; + opt->hash_alg = QCRYPTO_HASH_ALGO_SHA1; break; case VIRTIO_CRYPTO_RSA_SHA256: - opt->hash_alg = QCRYPTO_HASH_ALG_SHA256; + opt->hash_alg = QCRYPTO_HASH_ALGO_SHA256; break; case VIRTIO_CRYPTO_RSA_SHA512: - opt->hash_alg = QCRYPTO_HASH_ALG_SHA512; + opt->hash_alg = QCRYPTO_HASH_ALGO_SHA512; break; default: @@ -184,7 +184,7 @@ static int cryptodev_lkcf_set_rsa_opt(int virtio_padding_alg, } if (virtio_padding_alg == VIRTIO_CRYPTO_RSA_RAW_PADDING) { - opt->padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW; + opt->padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW; return 0; } @@ -223,14 +223,14 @@ static void cryptodev_lkcf_init(CryptoDevBackend *backend, Error **errp) return; } - cc = cryptodev_backend_new_client("cryptodev-lkcf", NULL); + cc = cryptodev_backend_new_client(); cc->info_str = g_strdup_printf("cryptodev-lkcf0"); cc->queue_index = 0; - cc->type = CRYPTODEV_BACKEND_TYPE_LKCF; + cc->type = QCRYPTODEV_BACKEND_TYPE_LKCF; backend->conf.peers.ccs[0] = cc; backend->conf.crypto_services = - 1u << VIRTIO_CRYPTO_SERVICE_AKCIPHER; + 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER; backend->conf.akcipher_algo = 1u << VIRTIO_CRYPTO_AKCIPHER_RSA; lkcf->running = true; @@ -322,7 +322,7 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) * 2. generally, public key related compution is fast, just compute it with * thread-pool. */ - if (session->keytype == QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE) { + if (session->keytype == QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE) { if (qcrypto_akcipher_export_p8info(&session->akcipher_opts, session->key, session->keylen, &p8info, &p8info_len, @@ -469,15 +469,12 @@ static void *cryptodev_lkcf_worker(void *arg) static int cryptodev_lkcf_operation( CryptoDevBackend *backend, - CryptoDevBackendOpInfo *op_info, - uint32_t queue_index, - CryptoDevCompletionFunc cb, - void *opaque) + CryptoDevBackendOpInfo *op_info) { CryptoDevBackendLKCF *lkcf = CRYPTODEV_BACKEND_LKCF(backend); CryptoDevBackendLKCFSession *sess; - enum CryptoDevBackendAlgType algtype = op_info->algtype; + QCryptodevBackendAlgoType algtype = op_info->algtype; CryptoDevLKCFTask *task; if (op_info->session_id >= MAX_SESSIONS || @@ -488,15 +485,15 @@ static int cryptodev_lkcf_operation( } sess = lkcf->sess[op_info->session_id]; - if (algtype != CRYPTODEV_BACKEND_ALG_ASYM) { + if (algtype != QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { error_report("algtype not supported: %u", algtype); return -VIRTIO_CRYPTO_NOTSUPP; } task = g_new0(CryptoDevLKCFTask, 1); task->op_info = op_info; - task->cb = cb; - task->opaque = opaque; + task->cb = op_info->cb; + task->opaque = op_info->opaque; task->sess = sess; task->lkcf = lkcf; task->status = -VIRTIO_CRYPTO_ERR; @@ -521,7 +518,7 @@ static int cryptodev_lkcf_create_asym_session( switch (sess_info->algo) { case VIRTIO_CRYPTO_AKCIPHER_RSA: - sess->akcipher_opts.alg = QCRYPTO_AKCIPHER_ALG_RSA; + sess->akcipher_opts.alg = QCRYPTO_AK_CIPHER_ALGO_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) { @@ -537,11 +534,11 @@ static int cryptodev_lkcf_create_asym_session( switch (sess_info->keytype) { case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: - sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC; + sess->keytype = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC; break; case VIRTIO_CRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: - sess->keytype = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE; + sess->keytype = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE; break; default: diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index ab3028e045..e33fb78521 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_TYPE_CIPHER | + 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_HASH | + 1u << QCRYPTODEV_BACKEND_SERVICE_TYPE_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 = @@ -267,18 +266,17 @@ static int cryptodev_vhost_user_create_session( void *opaque) { uint32_t op_code = sess_info->op_code; - CryptoDevBackendSymSessionInfo *sym_sess_info; 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: - sym_sess_info = &sess_info->u.sym_sess_info; - ret = cryptodev_vhost_user_sym_create_session(backend, sym_sess_info, + ret = cryptodev_vhost_user_crypto_create_session(backend, sess_info, queue_index, &local_error); break; diff --git a/backends/cryptodev-vhost.c b/backends/cryptodev-vhost.c index 572f87b3be..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" @@ -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 54ee8c81f5..d8bd2a1ae6 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -23,29 +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_TYPE__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; @@ -55,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); } @@ -71,6 +132,9 @@ void cryptodev_backend_cleanup( if (bc->cleanup) { bc->cleanup(backend, errp); } + + g_free(backend->sym_stat); + g_free(backend->asym_stat); } int cryptodev_backend_create_session( @@ -107,38 +171,122 @@ int cryptodev_backend_close_session( static int cryptodev_backend_operation( CryptoDevBackend *backend, - CryptoDevBackendOpInfo *op_info, - uint32_t queue_index, - CryptoDevCompletionFunc cb, - void *opaque) + CryptoDevBackendOpInfo *op_info) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(backend); if (bc->do_op) { - return bc->do_op(backend, op_info, queue_index, cb, opaque); + return bc->do_op(backend, op_info); } return -VIRTIO_CRYPTO_NOTSUPP; } -int cryptodev_backend_crypto_operation( - CryptoDevBackend *backend, - void *opaque1, - uint32_t queue_index, - CryptoDevCompletionFunc cb, void *opaque2) +static int cryptodev_backend_account(CryptoDevBackend *backend, + CryptoDevBackendOpInfo *op_info) { - VirtIOCryptoReq *req = opaque1; - CryptoDevBackendOpInfo *op_info = &req->op_info; - enum CryptoDevBackendAlgType algtype = req->flags; + enum QCryptodevBackendAlgoType algtype = op_info->algtype; + int len; - if ((algtype != CRYPTODEV_BACKEND_ALG_SYM) - && (algtype != CRYPTODEV_BACKEND_ALG_ASYM)) { + if (algtype == QCRYPTODEV_BACKEND_ALGO_TYPE_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_ALGO_TYPE_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 cryptodev_backend_operation(backend, op_info, queue_index, - cb, opaque2); + 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, + CryptoDevBackendOpInfo *op_info) +{ + int ret; + + if (!throttle_enabled(&backend->tc)) { + goto do_account; + } + + 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 @@ -169,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_TYPE_CIPHER)) { + backend->sym_stat = g_new0(CryptodevBackendSymStat, 1); + } + + if (services & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AKCIPHER)) { + backend->asym_stat = g_new0(CryptodevBackendAsymStat, 1); } } @@ -208,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) @@ -217,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 @@ -232,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 57369ec0f2..be6c4d8e0a 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -393,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() @@ -426,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/host_iommu_device.c b/backends/host_iommu_device.c new file mode 100644 index 0000000000..8f2dda1beb --- /dev/null +++ b/backends/host_iommu_device.c @@ -0,0 +1,33 @@ +/* + * Host IOMMU device abstract + * + * Copyright (C) 2024 Intel Corporation. + * + * Authors: Zhenzhong Duan + * + * 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 "sysemu/host_iommu_device.h" + +OBJECT_DEFINE_ABSTRACT_TYPE(HostIOMMUDevice, + host_iommu_device, + HOST_IOMMU_DEVICE, + OBJECT) + +static void host_iommu_device_class_init(ObjectClass *oc, void *data) +{ +} + +static void host_iommu_device_init(Object *obj) +{ +} + +static void host_iommu_device_finalize(Object *obj) +{ + HostIOMMUDevice *hiod = HOST_IOMMU_DEVICE(obj); + + g_free(hiod->name); +} diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index 037292d267..6c024d6217 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -9,39 +9,36 @@ * 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); + fd = qemu_open("/dev/sgx_vepc", O_RDWR, errp); if (fd < 0) { - error_setg_errno(errp, errno, - "failed to open /dev/sgx_vepc to alloc SGX EPC"); - return; + return false; } + backend->aligned = true; 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..7e5072e33e 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,69 @@ 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(); + } + + backend->aligned = true; 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 |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; 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 +157,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 +230,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 +285,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 +298,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..9f890a813e 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -18,8 +18,6 @@ #include "qapi/error.h" #include "qom/object.h" -#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" - OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendMemfd, MEMORY_BACKEND_MEMFD) @@ -31,17 +29,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 +47,16 @@ 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; } + backend->aligned = true; 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); + ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; + 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..f7d81af783 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -16,23 +16,24 @@ #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); + ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0; + return memory_region_init_ram_flags_nomigrate(&backend->mr, OBJECT(backend), + name, backend->size, + ram_flags, errp); } static void diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c new file mode 100644 index 0000000000..374edc3db8 --- /dev/null +++ b/backends/hostmem-shm.c @@ -0,0 +1,123 @@ +/* + * QEMU host POSIX shared memory object backend + * + * Copyright (C) 2024 Red Hat Inc + * + * Authors: + * Stefano Garzarella + * + * 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 "sysemu/hostmem.h" +#include "qapi/error.h" + +#define TYPE_MEMORY_BACKEND_SHM "memory-backend-shm" + +OBJECT_DECLARE_SIMPLE_TYPE(HostMemoryBackendShm, MEMORY_BACKEND_SHM) + +struct HostMemoryBackendShm { + HostMemoryBackend parent_obj; +}; + +static bool +shm_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) +{ + g_autoptr(GString) shm_name = g_string_new(NULL); + g_autofree char *backend_name = NULL; + uint32_t ram_flags; + int fd, oflag; + mode_t mode; + + if (!backend->size) { + error_setg(errp, "can't create shm backend with size 0"); + return false; + } + + if (!backend->share) { + error_setg(errp, "can't create shm backend with `share=off`"); + return false; + } + + /* + * Let's use `mode = 0` because we don't want other processes to open our + * memory unless we share the file descriptor with them. + */ + mode = 0; + oflag = O_RDWR | O_CREAT | O_EXCL; + backend_name = host_memory_backend_get_name(backend); + + /* + * Some operating systems allow creating anonymous POSIX shared memory + * objects (e.g. FreeBSD provides the SHM_ANON constant), but this is not + * defined by POSIX, so let's create a unique name. + * + * From Linux's shm_open(3) man-page: + * For portable use, a shared memory object should be identified + * by a name of the form /somename;" + */ + g_string_printf(shm_name, "/qemu-" FMT_pid "-shm-%s", getpid(), + backend_name); + + fd = shm_open(shm_name->str, oflag, mode); + if (fd < 0) { + error_setg_errno(errp, errno, + "failed to create POSIX shared memory"); + return false; + } + + /* + * We have the file descriptor, so we no longer need to expose the + * POSIX shared memory object. However it will remain allocated as long as + * there are file descriptors pointing to it. + */ + shm_unlink(shm_name->str); + + if (ftruncate(fd, backend->size) == -1) { + error_setg_errno(errp, errno, + "failed to resize POSIX shared memory to %" PRIu64, + backend->size); + close(fd); + return false; + } + + ram_flags = RAM_SHARED; + ram_flags |= backend->reserve ? 0 : RAM_NORESERVE; + + return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), + backend_name, backend->size, + ram_flags, fd, 0, errp); +} + +static void +shm_backend_instance_init(Object *obj) +{ + HostMemoryBackendShm *m = MEMORY_BACKEND_SHM(obj); + + MEMORY_BACKEND(m)->share = true; +} + +static void +shm_backend_class_init(ObjectClass *oc, void *data) +{ + HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); + + bc->alloc = shm_backend_memory_alloc; +} + +static const TypeInfo shm_backend_info = { + .name = TYPE_MEMORY_BACKEND_SHM, + .parent = TYPE_MEMORY_BACKEND, + .instance_init = shm_backend_instance_init, + .class_init = shm_backend_class_init, + .instance_size = sizeof(HostMemoryBackendShm), +}; + +static void register_types(void) +{ + type_register_static(&shm_backend_info); +} + +type_init(register_types); diff --git a/backends/hostmem.c b/backends/hostmem.c index 8640294c10..181446626a 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -20,10 +20,17 @@ #include "qom/object_interfaces.h" #include "qemu/mmap-alloc.h" #include "qemu/madvise.h" +#include "qemu/cutils.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); @@ -163,19 +170,24 @@ static void host_memory_backend_set_merge(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); - if (!host_memory_backend_mr_inited(backend)) { - backend->merge = value; + if (QEMU_MADV_MERGEABLE == QEMU_MADV_INVALID) { + if (value) { + error_setg(errp, "Memory merging is not supported on this host"); + } + assert(!backend->merge); return; } - if (value != backend->merge) { + if (host_memory_backend_mr_inited(backend) && + value != backend->merge) { void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); qemu_madvise(ptr, sz, value ? QEMU_MADV_MERGEABLE : QEMU_MADV_UNMERGEABLE); - backend->merge = value; } + + backend->merge = value; } static bool host_memory_backend_get_dump(Object *obj, Error **errp) @@ -189,19 +201,24 @@ static void host_memory_backend_set_dump(Object *obj, bool value, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); - if (!host_memory_backend_mr_inited(backend)) { - backend->dump = value; + if (QEMU_MADV_DONTDUMP == QEMU_MADV_INVALID) { + if (!value) { + error_setg(errp, "Dumping guest memory cannot be disabled on this host"); + } + assert(backend->dump); return; } - if (value != backend->dump) { + if (host_memory_backend_mr_inited(backend) && + value != backend->dump) { void *ptr = memory_region_get_ram_ptr(&backend->mr); uint64_t sz = memory_region_size(&backend->mr); qemu_madvise(ptr, sz, value ? QEMU_MADV_DODUMP : QEMU_MADV_DONTDUMP); - backend->dump = value; } + + backend->dump = value; } static bool host_memory_backend_get_prealloc(Object *obj, Error **errp) @@ -214,7 +231,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,10 +248,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); - qemu_prealloc_mem(fd, ptr, sz, backend->prealloc_threads, - backend->prealloc_context, &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; @@ -274,6 +288,7 @@ static void host_memory_backend_init(Object *obj) /* TODO: convert access to globals to compat properties */ backend->merge = machine_mem_merge(machine); backend->dump = machine_dump_guest_core(machine); + backend->guest_memfd = machine_require_guest_memfd(machine); backend->reserve = true; backend->prealloc_threads = machine->smp.cpus; } @@ -319,81 +334,101 @@ 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; + size_t pagesize; + 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); + pagesize = qemu_ram_pagesize(backend->mr.ram_block); - if (backend->merge) { - qemu_madvise(ptr, sz, QEMU_MADV_MERGEABLE); - } - if (!backend->dump) { - qemu_madvise(ptr, sz, QEMU_MADV_DONTDUMP); - } + if (backend->aligned && !QEMU_IS_ALIGNED(sz, pagesize)) { + g_autofree char *pagesize_str = size_to_str(pagesize); + error_setg(errp, "backend '%s' memory size must be multiple of %s", + object_get_typename(OBJECT(uc)), pagesize_str); + return; + } + + 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) { - qemu_prealloc_mem(memory_region_get_fd(&backend->mr), ptr, sz, - backend->prealloc_threads, - backend->prealloc_context, &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 diff --git a/backends/iommufd.c b/backends/iommufd.c new file mode 100644 index 0000000000..9bc466a89c --- /dev/null +++ b/backends/iommufd.c @@ -0,0 +1,360 @@ +/* + * 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 "qemu/module.h" +#include "qom/object_interfaces.h" +#include "qemu/error-report.h" +#include "monitor/monitor.h" +#include "trace.h" +#include "hw/vfio/vfio-common.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); +} + +bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) +{ + int fd; + + if (be->owned && !be->users) { + fd = qemu_open("/dev/iommu", O_RDWR, errp); + if (fd < 0) { + return false; + } + be->fd = fd; + } + be->users++; + + trace_iommufd_backend_connect(be->fd, be->owned, be->users); + return true; +} + +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); +} + +bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, + Error **errp) +{ + int fd = be->fd; + struct iommu_ioas_alloc alloc_data = { + .size = sizeof(alloc_data), + .flags = 0, + }; + + if (ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data)) { + error_setg_errno(errp, errno, "Failed to allocate ioas"); + return false; + } + + *ioas_id = alloc_data.out_ioas_id; + trace_iommufd_backend_alloc_ioas(fd, *ioas_id); + + return true; +} + +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; +} + +bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, + uint32_t pt_id, uint32_t flags, + uint32_t data_type, uint32_t data_len, + void *data_ptr, uint32_t *out_hwpt, + Error **errp) +{ + int ret, fd = be->fd; + struct iommu_hwpt_alloc alloc_hwpt = { + .size = sizeof(struct iommu_hwpt_alloc), + .flags = flags, + .dev_id = dev_id, + .pt_id = pt_id, + .data_type = data_type, + .data_len = data_len, + .data_uptr = (uintptr_t)data_ptr, + }; + + ret = ioctl(fd, IOMMU_HWPT_ALLOC, &alloc_hwpt); + trace_iommufd_backend_alloc_hwpt(fd, dev_id, pt_id, flags, data_type, + data_len, (uintptr_t)data_ptr, + alloc_hwpt.out_hwpt_id, ret); + if (ret) { + error_setg_errno(errp, errno, "Failed to allocate hwpt"); + return false; + } + + *out_hwpt = alloc_hwpt.out_hwpt_id; + return true; +} + +bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, + uint32_t hwpt_id, bool start, + Error **errp) +{ + int ret; + struct iommu_hwpt_set_dirty_tracking set_dirty = { + .size = sizeof(set_dirty), + .hwpt_id = hwpt_id, + .flags = start ? IOMMU_HWPT_DIRTY_TRACKING_ENABLE : 0, + }; + + ret = ioctl(be->fd, IOMMU_HWPT_SET_DIRTY_TRACKING, &set_dirty); + trace_iommufd_backend_set_dirty(be->fd, hwpt_id, start, ret ? errno : 0); + if (ret) { + error_setg_errno(errp, errno, + "IOMMU_HWPT_SET_DIRTY_TRACKING(hwpt_id %u) failed", + hwpt_id); + return false; + } + + return true; +} + +bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, + uint32_t hwpt_id, + uint64_t iova, ram_addr_t size, + uint64_t page_size, uint64_t *data, + Error **errp) +{ + int ret; + struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap = { + .size = sizeof(get_dirty_bitmap), + .hwpt_id = hwpt_id, + .iova = iova, + .length = size, + .page_size = page_size, + .data = (uintptr_t)data, + }; + + ret = ioctl(be->fd, IOMMU_HWPT_GET_DIRTY_BITMAP, &get_dirty_bitmap); + trace_iommufd_backend_get_dirty_bitmap(be->fd, hwpt_id, iova, size, + page_size, ret ? errno : 0); + if (ret) { + error_setg_errno(errp, errno, + "IOMMU_HWPT_GET_DIRTY_BITMAP (iova: 0x%"HWADDR_PRIx + " size: 0x"RAM_ADDR_FMT") failed", iova, size); + return false; + } + + return true; +} + +bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, + uint32_t *type, void *data, uint32_t len, + uint64_t *caps, Error **errp) +{ + struct iommu_hw_info info = { + .size = sizeof(info), + .dev_id = devid, + .data_len = len, + .data_uptr = (uintptr_t)data, + }; + + if (ioctl(be->fd, IOMMU_GET_HW_INFO, &info)) { + error_setg_errno(errp, errno, "Failed to get hardware info"); + return false; + } + + g_assert(type); + *type = info.out_data_type; + g_assert(caps); + *caps = info.out_capabilities; + + return true; +} + +static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) +{ + HostIOMMUDeviceCaps *caps = &hiod->caps; + + switch (cap) { + case HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE: + return caps->type; + case HOST_IOMMU_DEVICE_CAP_AW_BITS: + return vfio_device_get_aw_bits(hiod->agent); + default: + error_setg(errp, "%s: unsupported capability %x", hiod->name, cap); + return -EINVAL; + } +} + +static void hiod_iommufd_class_init(ObjectClass *oc, void *data) +{ + HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); + + hioc->get_cap = hiod_iommufd_get_cap; +}; + +static const TypeInfo types[] = { + { + .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 }, + { } + } + }, { + .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, + .parent = TYPE_HOST_IOMMU_DEVICE, + .class_init = hiod_iommufd_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(types) diff --git a/backends/meson.build b/backends/meson.build index 954e658b25..da714b93d1 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,20 +10,29 @@ 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')) + system_ss.add([files('hostmem-shm.c'), rt]) +endif +if host_os == 'linux' + system_ss.add(files('hostmem-memfd.c')) + system_ss.add(files('host_iommu_device.c')) +endif if keyutils.found() - softmmu_ss.add(keyutils, files('cryptodev-lkcf.c')) + 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')) + +system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) subdir('tpm') diff --git a/backends/rng-random.c b/backends/rng-random.c index 80eb5be138..489c0917f0 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -75,10 +75,7 @@ static void rng_random_opened(RngBackend *b, Error **errp) error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "filename", "a valid filename"); } else { - s->fd = qemu_open_old(s->filename, O_RDONLY | O_NONBLOCK); - if (s->fd == -1) { - error_setg_file_open(errp, errno, s->filename); - } + s->fd = qemu_open(s->filename, O_RDONLY | O_NONBLOCK, errp); } } 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/spdm-socket.c b/backends/spdm-socket.c new file mode 100644 index 0000000000..d0663d696c --- /dev/null +++ b/backends/spdm-socket.c @@ -0,0 +1,216 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * QEMU SPDM socket support + * + * This is based on: + * https://github.com/DMTF/spdm-emu/blob/07c0a838bcc1c6207c656ac75885c0603e344b6f/spdm_emu/spdm_emu_common/command.c + * but has been re-written to match QEMU style + * + * Copyright (c) 2021, DMTF. All rights reserved. + * Copyright (c) 2023. Western Digital Corporation or its affiliates. + */ + +#include "qemu/osdep.h" +#include "sysemu/spdm-socket.h" +#include "qapi/error.h" + +static bool read_bytes(const int socket, uint8_t *buffer, + size_t number_of_bytes) +{ + ssize_t number_received = 0; + ssize_t result; + + while (number_received < number_of_bytes) { + result = recv(socket, buffer + number_received, + number_of_bytes - number_received, 0); + if (result <= 0) { + return false; + } + number_received += result; + } + return true; +} + +static bool read_data32(const int socket, uint32_t *data) +{ + bool result; + + result = read_bytes(socket, (uint8_t *)data, sizeof(uint32_t)); + if (!result) { + return result; + } + *data = ntohl(*data); + return true; +} + +static bool read_multiple_bytes(const int socket, uint8_t *buffer, + uint32_t *bytes_received, + uint32_t max_buffer_length) +{ + uint32_t length; + bool result; + + result = read_data32(socket, &length); + if (!result) { + return result; + } + + if (length > max_buffer_length) { + return false; + } + + if (bytes_received) { + *bytes_received = length; + } + + if (length == 0) { + return true; + } + + return read_bytes(socket, buffer, length); +} + +static bool receive_platform_data(const int socket, + uint32_t transport_type, + uint32_t *command, + uint8_t *receive_buffer, + uint32_t *bytes_to_receive) +{ + bool result; + uint32_t response; + uint32_t bytes_received; + + result = read_data32(socket, &response); + if (!result) { + return result; + } + *command = response; + + result = read_data32(socket, &transport_type); + if (!result) { + return result; + } + + bytes_received = 0; + result = read_multiple_bytes(socket, receive_buffer, &bytes_received, + *bytes_to_receive); + if (!result) { + return result; + } + *bytes_to_receive = bytes_received; + + return result; +} + +static bool write_bytes(const int socket, const uint8_t *buffer, + uint32_t number_of_bytes) +{ + ssize_t number_sent = 0; + ssize_t result; + + while (number_sent < number_of_bytes) { + result = send(socket, buffer + number_sent, + number_of_bytes - number_sent, 0); + if (result == -1) { + return false; + } + number_sent += result; + } + return true; +} + +static bool write_data32(const int socket, uint32_t data) +{ + data = htonl(data); + return write_bytes(socket, (uint8_t *)&data, sizeof(uint32_t)); +} + +static bool write_multiple_bytes(const int socket, const uint8_t *buffer, + uint32_t bytes_to_send) +{ + bool result; + + result = write_data32(socket, bytes_to_send); + if (!result) { + return result; + } + + return write_bytes(socket, buffer, bytes_to_send); +} + +static bool send_platform_data(const int socket, + uint32_t transport_type, uint32_t command, + const uint8_t *send_buffer, size_t bytes_to_send) +{ + bool result; + + result = write_data32(socket, command); + if (!result) { + return result; + } + + result = write_data32(socket, transport_type); + if (!result) { + return result; + } + + return write_multiple_bytes(socket, send_buffer, bytes_to_send); +} + +int spdm_socket_connect(uint16_t port, Error **errp) +{ + int client_socket; + struct sockaddr_in server_addr; + + client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client_socket < 0) { + error_setg(errp, "cannot create socket: %s", strerror(errno)); + return -1; + } + + memset((char *)&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + server_addr.sin_port = htons(port); + + + if (connect(client_socket, (struct sockaddr *)&server_addr, + sizeof(server_addr)) < 0) { + error_setg(errp, "cannot connect: %s", strerror(errno)); + close(client_socket); + return -1; + } + + return client_socket; +} + +uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type, + void *req, uint32_t req_len, + void *rsp, uint32_t rsp_len) +{ + uint32_t command; + bool result; + + result = send_platform_data(socket, transport_type, + SPDM_SOCKET_COMMAND_NORMAL, + req, req_len); + if (!result) { + return 0; + } + + result = receive_platform_data(socket, transport_type, &command, + (uint8_t *)rsp, &rsp_len); + if (!result) { + return 0; + } + + assert(command != 0); + + return rsp_len; +} + +void spdm_socket_close(const int socket, uint32_t transport_type) +{ + send_platform_data(socket, transport_type, + SPDM_SOCKET_COMMAND_SHUTDOWN, NULL, 0); +} 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 49cc3d749d..aa05dab6ae 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -35,7 +35,6 @@ #include "sysemu/runstate.h" #include "sysemu/tpm_backend.h" #include "sysemu/tpm_util.h" -#include "sysemu/runstate.h" #include "tpm_int.h" #include "tpm_ioctl.h" #include "migration/blocker.h" @@ -73,7 +72,7 @@ struct TPMEmulator { CharBackend ctrl_chr; QIOChannel *data_ioc; TPMVersion tpm_version; - ptm_cap caps; /* capabilities of the TPM */ + uint32_t caps; /* capabilities of the TPM */ uint8_t cur_locty_number; /* last set locality */ Error *migration_blocker; @@ -124,12 +123,14 @@ static const char *tpm_emulator_strerror(uint32_t tpm_result) } static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, - size_t msg_len_in, size_t msg_len_out) + size_t msg_len_in, size_t msg_len_out_err, + size_t msg_len_out_total) { CharBackend *dev = &tpm->ctrl_chr; uint32_t cmd_no = cpu_to_be32(cmd); ssize_t n = sizeof(uint32_t) + msg_len_in; uint8_t *buf = NULL; + ptm_res res; WITH_QEMU_LOCK_GUARD(&tpm->mutex) { buf = g_alloca(n); @@ -141,8 +142,25 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, return -1; } - if (msg_len_out != 0) { - n = qemu_chr_fe_read_all(dev, msg, msg_len_out); + if (msg_len_out_total > 0) { + assert(msg_len_out_total >= msg_len_out_err); + + n = qemu_chr_fe_read_all(dev, (uint8_t *)msg, msg_len_out_err); + if (n <= 0) { + return -1; + } + if (msg_len_out_err == msg_len_out_total) { + return 0; + } + /* result error code is always in the first 4 bytes */ + assert(sizeof(res) <= msg_len_out_err); + memcpy(&res, msg, sizeof(res)); + if (res) { + return 0; + } + + n = qemu_chr_fe_read_all(dev, (uint8_t *)msg + msg_len_out_err, + msg_len_out_total - msg_len_out_err); if (n <= 0) { return -1; } @@ -205,7 +223,8 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, memset(&loc, 0, sizeof(loc)); loc.u.req.loc = locty_number; if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, - sizeof(loc), sizeof(loc)) < 0) { + sizeof(loc), sizeof(loc.u.resp.tpm_result), + sizeof(loc)) < 0) { error_setg(errp, "tpm-emulator: could not set locality : %s", strerror(errno)); return -1; @@ -240,13 +259,16 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) { - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, - &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { + ptm_cap_n cap_n; + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, &cap_n, 0, + sizeof(cap_n.u.resp.tpm_result), + sizeof(cap_n)) < 0) { error_report("tpm-emulator: probing failed : %s", strerror(errno)); return -1; } - tpm_emu->caps = be64_to_cpu(tpm_emu->caps); + tpm_emu->caps = be32_to_cpu(cap_n.u.resp.caps); trace_tpm_emulator_probe_caps(tpm_emu->caps); @@ -255,7 +277,7 @@ static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) { - ptm_cap caps = 0; + uint32_t caps = 0; const char *tpm = NULL; /* check for min. required capabilities */ @@ -291,7 +313,8 @@ static int tpm_emulator_stop_tpm(TPMBackend *tb) TPMEmulator *tpm_emu = TPM_EMULATOR(tb); ptm_res res; - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, + sizeof(ptm_res), sizeof(res)) < 0) { error_report("tpm-emulator: Could not stop TPM: %s", strerror(errno)); return -1; @@ -318,8 +341,9 @@ static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu) /* 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) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_LOCK_STORAGE, &pls, sizeof(pls.u.req), + sizeof(pls.u.resp.tpm_result), + sizeof(pls.u.resp)) < 0) { error_report("tpm-emulator: Could not lock storage within 3 seconds: " "%s", strerror(errno)); return -1; @@ -350,7 +374,8 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb, psbs.u.req.buffersize = cpu_to_be32(wanted_size); if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, - sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { + sizeof(psbs.u.req), sizeof(psbs.u.resp.tpm_result), + sizeof(psbs.u.resp)) < 0) { error_report("tpm-emulator: Could not set buffer size: %s", strerror(errno)); return -1; @@ -397,6 +422,7 @@ static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, } if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), + sizeof(init.u.resp.tpm_result), sizeof(init)) < 0) { error_report("tpm-emulator: could not send INIT: %s", strerror(errno)); @@ -438,8 +464,9 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) return tpm_emu->established_flag; } - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, - 0, sizeof(est)) < 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, 0, + sizeof(est) /* always returns resp.bit */, + sizeof(est)) < 0) { error_report("tpm-emulator: Could not get the TPM established flag: %s", strerror(errno)); return false; @@ -467,6 +494,7 @@ static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, reset_est.u.req.loc = tpm_emu->cur_locty_number; if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, &reset_est, sizeof(reset_est), + sizeof(reset_est.u.resp.tpm_result), sizeof(reset_est)) < 0) { error_report("tpm-emulator: Could not reset the establishment bit: %s", strerror(errno)); @@ -498,7 +526,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb) /* FIXME: make the function non-blocking, or it may block a VCPU */ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, - sizeof(res)) < 0) { + sizeof(ptm_res), sizeof(res)) < 0) { error_report("tpm-emulator: Could not cancel command: %s", strerror(errno)); } else if (res != 0) { @@ -528,18 +556,15 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) { Error *err = NULL; - ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | - PTM_CAP_STOP; + uint32_t caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | + PTM_CAP_STOP; if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { 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; } } @@ -553,7 +578,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; } @@ -561,7 +586,7 @@ static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, - sizeof(res)) < 0 || res != 0) { + sizeof(ptm_res), sizeof(res)) < 0 || res != 0) { error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", strerror(errno)); goto err_exit; @@ -574,13 +599,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; } @@ -708,6 +733,8 @@ static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, &pgs, sizeof(pgs.u.req), + /* always returns up to resp.data */ + offsetof(ptm_getstate, u.resp.data), offsetof(ptm_getstate, u.resp.data)) < 0) { error_report("tpm-emulator: could not get state blob type %d : %s", type, strerror(errno)); @@ -810,7 +837,7 @@ static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, /* write the header only */ if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, - offsetof(ptm_setstate, u.req.data), 0) < 0) { + offsetof(ptm_setstate, u.req.data), 0, 0) < 0) { error_report("tpm-emulator: could not set state blob type %d : %s", type, strerror(errno)); return -1; @@ -908,7 +935,7 @@ static void tpm_emulator_vm_state_change(void *opaque, bool running, trace_tpm_emulator_vm_state_change(running, state); - if (!running || state != RUN_STATE_RUNNING || !tpm_emu->relock_storage) { + if (!running || !tpm_emu->relock_storage) { return; } @@ -943,7 +970,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, @@ -979,8 +1006,7 @@ static void tpm_emulator_inst_init(Object *obj) 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); } /* @@ -995,7 +1021,8 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) return; } - if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, + sizeof(ptm_res), sizeof(res)) < 0) { error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", strerror(errno)); } else if (res != 0) { @@ -1017,10 +1044,7 @@ 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); diff --git a/backends/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h index e506ef5160..ee2dd15d35 100644 --- a/backends/tpm/tpm_ioctl.h +++ b/backends/tpm/tpm_ioctl.h @@ -12,8 +12,6 @@ # define __USE_LINUX_IOCTL_DEFS #endif -#include -#include #ifndef _WIN32 #include #include @@ -31,6 +29,16 @@ typedef uint32_t ptm_res; +/* PTM_GET_CAPABILITY: Get supported capabilities (ioctl's) */ +struct ptm_cap_n { + union { + struct { + ptm_res tpm_result; /* will always be TPM_SUCCESS (0) */ + uint32_t caps; + } resp; /* response */ + } u; +}; + /* PTM_GET_TPMESTABLISHED: get the establishment bit */ struct ptm_est { union { @@ -240,11 +248,12 @@ struct ptm_lockstorage { } req; /* request */ struct { ptm_res tpm_result; - } resp; /* reponse */ + } resp; /* response */ } u; }; -typedef uint64_t ptm_cap; +typedef uint64_t ptm_cap; /* CUSE-only; use ptm_cap_n otherwise */ +typedef struct ptm_cap_n ptm_cap_n; typedef struct ptm_est ptm_est; typedef struct ptm_reset_est ptm_reset_est; typedef struct ptm_loc ptm_loc; 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..cf138551df 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; } @@ -346,10 +339,11 @@ void tpm_util_show_buffer(const unsigned char *buffer, size_t len, i; char *line_buffer, *p; - if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) { + if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER_CONTENT)) { return; } len = MIN(tpm_cmd_get_size(buffer), buffer_size); + trace_tpm_util_show_buffer_header(string, len); /* * allocate enough room for 3 chars per buffer entry plus a @@ -363,7 +357,7 @@ void tpm_util_show_buffer(const unsigned char *buffer, } p += sprintf(p, "%.2X ", buffer[i]); } - trace_tpm_util_show_buffer(string, len, line_buffer); + trace_tpm_util_show_buffer_content(line_buffer); g_free(line_buffer); } diff --git a/backends/tpm/trace-events b/backends/tpm/trace-events index 1ecef42a07..05e30533ce 100644 --- a/backends/tpm/trace-events +++ b/backends/tpm/trace-events @@ -10,12 +10,13 @@ tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu" tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu" tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu" -tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s" +tpm_util_show_buffer_header(const char *direction, size_t len) "direction: %s len: %zu" +tpm_util_show_buffer_content(const char *buf) "%s" # tpm_emulator.c tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" tpm_emulator_handle_request(void) "processing TPM command" -tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 +tpm_emulator_probe_caps(uint32_t caps) "capabilities: 0x%x" tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u" 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" diff --git a/backends/trace-events b/backends/trace-events index 652eb76a57..40811a3162 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -5,3 +5,16 @@ 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) "fd=%d owned=%d users=%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) " iommufd=%d ioas=%d" +iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_t flags, uint32_t hwpt_type, uint32_t len, uint64_t data_ptr, uint32_t out_hwpt_id, int ret) " iommufd=%d dev_id=%u pt_id=%u flags=0x%x hwpt_type=%u len=%u data_ptr=0x%"PRIx64" out_hwpt=%u (%d)" +iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" +iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)" +iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" page_size=0x%"PRIx64" (%d)" diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 7bfcaef976..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; } diff --git a/block.c b/block.c index dbc28ccc30..8303f3ce46 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" @@ -85,16 +86,17 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, + bool parse_filename, Error **errp); static bool bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child); -static void bdrv_replace_child_noperm(BdrvChild *child, - BlockDriverState *new_bs); -static void bdrv_remove_child(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, @@ -278,8 +280,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 +373,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 +420,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(); @@ -528,65 +532,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; } @@ -597,8 +560,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; @@ -606,14 +570,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, @@ -670,6 +634,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; @@ -699,11 +664,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; } @@ -721,11 +688,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; @@ -766,7 +734,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); @@ -780,6 +748,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); @@ -853,12 +822,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); } @@ -953,7 +927,6 @@ BlockDriver *bdrv_find_protocol(const char *filename, int i; GLOBAL_STATE_CODE(); - /* TODO Drivers without bdrv_file_open must be specified explicitly */ /* * XXX(hch): we really should not let host device detection @@ -1076,22 +1049,24 @@ static int find_image_format(BlockBackend *file, const char *filename, * Set the current 'total_sectors' value * Return 0 on success, -errno on error. */ -int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs, + int64_t hint) { BlockDriver *drv = bs->drv; IO_CODE(); + 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; } @@ -1225,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) @@ -1284,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; @@ -1336,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) { @@ -1350,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"); } @@ -1445,11 +1436,11 @@ 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)) { /* @@ -1485,11 +1476,9 @@ static void bdrv_child_cb_attach(BdrvChild *child) 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; @@ -1497,9 +1486,7 @@ 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); @@ -1510,10 +1497,14 @@ static void bdrv_child_cb_detach(BdrvChild *child) } 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; } @@ -1647,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; @@ -1664,10 +1655,8 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, bs->drv = drv; bs->opaque = g_malloc0(drv->instance_size); - if (drv->bdrv_file_open) { - assert(!drv->bdrv_needs_filename || bs->filename[0]); - ret = drv->bdrv_file_open(bs, options, open_flags, &local_err); - } else if (drv->bdrv_open) { + assert(!drv->bdrv_needs_filename || bs->filename[0]); + if (drv->bdrv_open) { ret = drv->bdrv_open(bs, options, open_flags, &local_err); } else { ret = 0; @@ -1698,13 +1687,16 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; - ret = refresh_total_sectors(bs, bs->total_sectors); + ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { error_setg_errno(errp, -ret, "Could not refresh total sector count"); return ret; } + 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; @@ -1715,18 +1707,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); assert(!bs->file); } + bdrv_graph_wrunlock(); + g_free(bs->opaque); bs->opaque = NULL; return ret; @@ -1868,9 +1864,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)) { @@ -1895,7 +1894,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 { /* @@ -1922,7 +1924,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; } @@ -1977,7 +1981,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, open_flags = bdrv_open_flags(bs, bs->open_flags); node_name = qemu_opt_get(opts, "node-name"); - assert(!drv->bdrv_file_open || file == NULL); + assert(!drv->protocol_name || file == NULL); ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp); if (ret < 0) { goto fail_opts; @@ -1993,6 +1997,7 @@ fail_opts: static QDict *parse_json_filename(const char *filename, Error **errp) { + ERRP_GUARD(); QObject *options_obj; QDict *options; int ret; @@ -2051,7 +2056,8 @@ static void parse_json_protocol(QDict *options, const char **pfilename, * block driver has been specified explicitly. */ static int bdrv_fill_options(QDict **options, const char *filename, - int *flags, Error **errp) + int *flags, bool allow_parse_filename, + Error **errp) { const char *drvname; bool protocol = *flags & BDRV_O_PROTOCOL; @@ -2077,7 +2083,7 @@ static int bdrv_fill_options(QDict **options, const char *filename, } /* If the user has explicitly specified the driver, this choice should * override the BDRV_O_PROTOCOL flag */ - protocol = drv->bdrv_file_open; + protocol = drv->protocol_name; } if (protocol) { @@ -2093,7 +2099,7 @@ static int bdrv_fill_options(QDict **options, const char *filename, if (protocol && filename) { if (!qdict_haskey(*options, "filename")) { qdict_put_str(*options, "filename", filename); - parse_filename = true; + parse_filename = allow_parse_filename; } else { error_setg(errp, "Can't specify 'file' and 'filename' options at " "the same time"); @@ -2140,7 +2146,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; @@ -2226,7 +2231,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(); @@ -2251,11 +2257,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(); @@ -2279,8 +2286,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; @@ -2343,7 +2350,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; @@ -2356,7 +2363,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(); @@ -2371,9 +2378,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) { @@ -2399,22 +2410,39 @@ typedef struct BdrvReplaceChildState { BlockDriverState *old_bs; } 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(); - 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(); + 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); } @@ -2429,12 +2457,23 @@ 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. */ -static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, - Transaction *tran) +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 = child, .old_bs = child->bs, @@ -2444,6 +2483,7 @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, if (new_bs) { bdrv_ref(new_bs); } + bdrv_replace_child_noperm(child, new_bs); /* old_bs reference is transparently moved from @child to @s */ } @@ -2451,9 +2491,13 @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, /* * 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; @@ -2523,8 +2567,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; @@ -2546,6 +2598,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) { @@ -2593,15 +2667,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; } @@ -2617,7 +2705,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); @@ -2832,14 +2920,41 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) return permissions[qapi_perm]; } -static void bdrv_replace_child_noperm(BdrvChild *child, - BlockDriverState *new_bs) +/* + * Replaces the node that a BdrvChild points to without updating permissions. + * + * If @new_bs is non-NULL, the parent of @child must already be drained through + * @child. + */ +static void GRAPH_WRLOCK +bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) { 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(); @@ -2847,59 +2962,30 @@ static void bdrv_replace_child_noperm(BdrvChild *child, 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) { - 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++; } } @@ -2913,6 +2999,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); @@ -2925,12 +3013,14 @@ typedef struct BdrvAttachChildCommonState { 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; BlockDriverState *bs = s->child->bs; GLOBAL_STATE_CODE(); + assert_bdrv_graph_writable(); + bdrv_replace_child_noperm(s->child, NULL); if (bdrv_get_aio_context(bs) != s->old_child_ctx) { @@ -2955,7 +3045,7 @@ static void bdrv_attach_child_common_abort(void *opaque) tran_commit(tran); } - bdrv_unref(bs); + bdrv_schedule_unref(bs); bdrv_child_free(s->child); } @@ -2969,15 +3059,22 @@ static TransactionActionDrv bdrv_attach_child_common_drv = { * * 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 BdrvChild *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) +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; @@ -3009,18 +3106,19 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, &local_err); if (ret < 0 && child_class->change_aio_ctx) { - Transaction *tran = tran_new(); + 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, tran, NULL); + visited, aio_ctx_tran, + NULL); if (ret_child == true) { error_free(local_err); ret = 0; } - tran_finalize(tran, ret_child == true ? 0 : -1); + tran_finalize(aio_ctx_tran, ret_child == true ? 0 : -1); g_hash_table_destroy(visited); } @@ -3032,6 +3130,24 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, } bdrv_ref(child_bs); + /* + * 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); @@ -3047,14 +3163,21 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, /* * 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 BdrvChild *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) +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) { uint64_t perm, shared_perm; @@ -3076,39 +3199,12 @@ static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs, tran, errp); } -static void bdrv_detach_child(BdrvChild *child) -{ - BlockDriverState *old_bs = child->bs; - - GLOBAL_STATE_CODE(); - bdrv_replace_child_noperm(child, NULL); - bdrv_child_free(child); - - 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_change_aio_context(old_bs, qemu_get_aio_context(), NULL, NULL); - } -} - /* * This function steals the reference to child_bs from the caller. * That reference is later dropped by bdrv_root_unref_child(). * * 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, @@ -3131,12 +3227,12 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, goto out; } - ret = bdrv_refresh_perms(child_bs, errp); + ret = bdrv_refresh_perms(child_bs, tran, errp); out: tran_finalize(tran, ret); - bdrv_unref(child_bs); + bdrv_schedule_unref(child_bs); return ret < 0 ? NULL : child; } @@ -3148,9 +3244,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, @@ -3172,7 +3265,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, goto out; } - ret = bdrv_refresh_perms(parent_bs, errp); + ret = bdrv_refresh_perms(parent_bs, tran, errp); if (ret < 0) { goto out; } @@ -3180,7 +3273,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, out: tran_finalize(tran, ret); - bdrv_unref(child_bs); + bdrv_schedule_unref(child_bs); return ret < 0 ? NULL : child; } @@ -3188,13 +3281,29 @@ out: /* 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 { @@ -3238,8 +3347,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; @@ -3276,7 +3386,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(); @@ -3317,12 +3428,22 @@ 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) { bool update_inherits_from = bdrv_inherits_from_recursive(child_bs, parent_bs); @@ -3373,6 +3494,7 @@ 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_child(child, tran); } @@ -3404,33 +3526,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; } @@ -3448,6 +3592,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; @@ -3459,6 +3604,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; @@ -3522,7 +3668,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, } backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs, - &child_of_bds, bdrv_backing_role(bs), errp); + &child_of_bds, bdrv_backing_role(bs), true, + errp); if (!backing_hd) { bs->open_flags |= BDRV_O_NO_BACKING; error_prepend(errp, "Could not open backing file: "); @@ -3540,6 +3687,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; } @@ -3555,7 +3703,8 @@ free_exit: static BlockDriverState * bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, const BdrvChildClass *child_class, - BdrvChildRole child_role, bool allow_none, Error **errp) + BdrvChildRole child_role, bool allow_none, + bool parse_filename, Error **errp) { BlockDriverState *bs = NULL; QDict *image_options; @@ -3586,7 +3735,8 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, } bs = bdrv_open_inherit(filename, reference, image_options, 0, - parent, child_class, child_role, errp); + parent, child_class, child_role, parse_filename, + errp); if (!bs) { goto done; } @@ -3596,6 +3746,33 @@ done: return bs; } +static BdrvChild *bdrv_open_child_common(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, + const BdrvChildClass *child_class, + BdrvChildRole child_role, + bool allow_none, bool parse_filename, + Error **errp) +{ + BlockDriverState *bs; + BdrvChild *child; + + GLOBAL_STATE_CODE(); + + bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, + child_role, allow_none, parse_filename, errp); + if (bs == NULL) { + return NULL; + } + + bdrv_graph_wrlock(); + child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, + errp); + bdrv_graph_wrunlock(); + + return child; +} + /* * Opens a disk image whose options are given as BlockdevRef in another block * device's options. @@ -3609,6 +3786,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, @@ -3617,22 +3796,17 @@ BdrvChild *bdrv_open_child(const char *filename, BdrvChildRole child_role, bool allow_none, Error **errp) { - BlockDriverState *bs; - - GLOBAL_STATE_CODE(); - - bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, - child_role, allow_none, errp); - if (bs == NULL) { - return NULL; - } - - return bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, - errp); + return bdrv_open_child_common(filename, options, bdref_key, parent, + child_class, child_role, allow_none, false, + errp); } /* - * Wrapper on bdrv_open_child() for most popular case: open primary child of bs. + * This does mostly the same as bdrv_open_child(), but for opening the primary + * child of a node. A notable difference from bdrv_open_child() is that it + * enables filename parsing for protocol names (including json:). + * + * @parent can move to a different AioContext in this function. */ int bdrv_open_file_child(const char *filename, QDict *options, const char *bdref_key, @@ -3645,8 +3819,8 @@ int bdrv_open_file_child(const char *filename, 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)) + if (!bdrv_open_child_common(filename, options, bdref_key, parent, + &child_of_bds, role, false, true, errp)) { return -EINVAL; } @@ -3691,7 +3865,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) } - bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, errp); + bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, false, + errp); obj = NULL; qobject_unref(obj); visit_free(v); @@ -3703,6 +3878,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, QDict *snapshot_options, Error **errp) { + ERRP_GUARD(); g_autofree char *tmp_filename = NULL; int64_t total_size; QemuOpts *opts = NULL; @@ -3716,6 +3892,7 @@ 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; @@ -3775,13 +3952,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, + bool parse_filename, Error **errp) { int ret; BlockBackend *file = NULL; @@ -3797,6 +3972,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; @@ -3825,9 +4004,11 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, } /* json: syntax counts as explicit options, as if in the QDict */ - parse_json_protocol(options, &filename, &local_err); - if (local_err) { - goto fail; + if (parse_filename) { + parse_json_protocol(options, &filename, &local_err); + if (local_err) { + goto fail; + } } bs->explicit_options = qdict_clone_shallow(options); @@ -3852,7 +4033,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, parent->open_flags, parent->options); } - ret = bdrv_fill_options(&options, filename, &flags, &local_err); + ret = bdrv_fill_options(&options, filename, &flags, parse_filename, + &local_err); if (ret < 0) { goto fail; } @@ -3921,7 +4103,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, file_bs = bdrv_open_child_bs(filename, options, "file", bs, &child_of_bds, BDRV_CHILD_IMAGE, - true, &local_err); + true, true, &local_err); if (local_err) { goto fail; } @@ -3929,9 +4111,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; } @@ -3966,7 +4150,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, } /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */ - assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->bdrv_file_open); + assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->protocol_name); /* file must be NULL if a protocol BDS is about to be created * (the inverse results in an error message from bdrv_open_common()) */ assert(!(flags & BDRV_O_PROTOCOL) || !file); @@ -4068,7 +4252,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, GLOBAL_STATE_CODE(); return bdrv_open_inherit(filename, reference, options, flags, NULL, - NULL, 0, errp); + NULL, 0, true, errp); } /* Return true if the NULL-terminated @list contains @str */ @@ -4121,8 +4305,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; @@ -4159,17 +4343,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); @@ -4179,12 +4362,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); @@ -4318,11 +4504,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); @@ -4334,6 +4522,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); @@ -4365,9 +4554,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()); @@ -4375,10 +4562,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; @@ -4387,28 +4571,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); } } @@ -4418,7 +4596,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; } @@ -4433,22 +4614,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); } } @@ -4456,13 +4633,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); } } @@ -4475,26 +4652,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, @@ -4524,19 +4688,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(); @@ -4545,6 +4717,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 */ @@ -4554,11 +4728,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: @@ -4569,19 +4748,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; } } @@ -4592,7 +4775,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) { @@ -4601,8 +4785,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; } /* @@ -4621,10 +4826,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; @@ -4686,7 +4893,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; @@ -4709,7 +4919,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); } @@ -4718,9 +4930,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; } @@ -4732,13 +4946,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 @@ -4766,6 +4983,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); @@ -4839,7 +5058,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; @@ -4856,6 +5075,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); @@ -4878,13 +5099,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; @@ -4919,12 +5141,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); } assert(!bs->backing); assert(!bs->file); + bdrv_graph_wrunlock(); + g_free(bs->opaque); bs->opaque = NULL; qatomic_set(&bs->copy_on_read, 0); @@ -4976,7 +5201,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; @@ -5065,14 +5290,22 @@ static TransactionActionDrv bdrv_remove_child_drv = { .commit = bdrv_remove_child_commit, }; -/* Function doesn't update permissions, caller is responsible for this. */ -static void bdrv_remove_child(BdrvChild *child, Transaction *tran) +/* + * 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 GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) { if (!child) { return; } if (child->bs) { + assert(child->quiesced_parent); bdrv_replace_child_tran(child, NULL, tran); } @@ -5080,25 +5313,25 @@ static void bdrv_remove_child(BdrvChild *child, Transaction *tran) } /* - * 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_child(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; 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)) { @@ -5121,6 +5354,9 @@ static int bdrv_replace_node_noperm(BlockDriverState *from, } /* + * 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. * @@ -5130,19 +5366,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. */ -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(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)); assert(from != to); @@ -5154,14 +5392,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 @@ -5174,13 +5404,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) { @@ -5191,27 +5420,33 @@ static int bdrv_replace_node_common(BlockDriverState *from, out: tran_finalize(tran, ret); - - bdrv_drained_end(from); - bdrv_unref(from); - return ret; } 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; } /* @@ -5235,7 +5470,14 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, GLOBAL_STATE_CODE(); + bdrv_graph_rdlock_main_loop(); assert(!bs_new->backing); + bdrv_graph_rdunlock_main_loop(); + + 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), @@ -5250,11 +5492,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; } @@ -5265,7 +5511,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; @@ -5274,17 +5519,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); - 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); @@ -5306,6 +5552,8 @@ static void bdrv_delete(BlockDriverState *bs) bdrv_close(bs); + qemu_mutex_destroy(&bs->reqs_lock); + g_free(bs); } @@ -5317,12 +5565,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; @@ -5345,15 +5597,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: "); @@ -5379,6 +5643,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; } @@ -5398,13 +5663,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; @@ -5419,8 +5685,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; } @@ -5477,8 +5743,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; @@ -5588,7 +5855,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; @@ -5601,15 +5869,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 @@ -5641,6 +5910,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; @@ -5651,6 +5922,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) { /* @@ -5673,18 +5945,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; @@ -5693,7 +5970,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; } @@ -5708,19 +5985,20 @@ 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) { + if (drv->protocol_name) { /* * Protocol drivers default to -ENOTSUP (most of their data is * not stored in any of their children (if they even have any), @@ -5729,7 +6007,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); @@ -5775,16 +6053,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; } @@ -5792,15 +6071,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; } @@ -5810,15 +6113,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(); @@ -5873,12 +6167,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); } @@ -5946,6 +6240,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) { @@ -6062,7 +6357,7 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) if (!*name) { name = allocated_name = blk_get_attached_dev_id(blk); } - xdbg_graph_add_node(gr, blk, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, + xdbg_graph_add_node(gr, blk, XDBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, name); g_free(allocated_name); if (blk_root(blk)) { @@ -6075,7 +6370,7 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) job = block_job_next_locked(job)) { GSList *el; - xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, + xdbg_graph_add_node(gr, job, XDBG_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); @@ -6084,7 +6379,7 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) } QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { - xdbg_graph_add_node(gr, bs, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, + xdbg_graph_add_node(gr, bs, XDBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, bs->node_name); QLIST_FOREACH(child, &bs->children, next) { xdbg_graph_add_edge(gr, bs, child); @@ -6216,7 +6511,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(); @@ -6260,24 +6555,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; } @@ -6310,17 +6614,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) { @@ -6339,6 +6646,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); @@ -6350,6 +6659,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); @@ -6361,6 +6672,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); } @@ -6375,6 +6688,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); } @@ -6403,6 +6718,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; @@ -6522,6 +6838,7 @@ int bdrv_activate(BlockDriverState *bs, Error **errp) BdrvDirtyBitmap *bm; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs->drv) { return -ENOMEDIUM; @@ -6550,7 +6867,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; @@ -6566,7 +6883,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"); @@ -6594,6 +6911,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); @@ -6612,14 +6930,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; @@ -6627,7 +6943,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(); @@ -6644,7 +6961,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; @@ -6695,7 +7012,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) { @@ -6713,18 +7030,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 @@ -6736,17 +7044,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; } @@ -6756,20 +7057,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; } } @@ -6779,13 +7081,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); } } @@ -6793,14 +7096,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); } } @@ -6826,6 +7130,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; @@ -6835,6 +7162,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]); @@ -6900,6 +7228,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, @@ -7032,7 +7364,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; } @@ -7056,7 +7388,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; } @@ -7121,39 +7456,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(); @@ -7184,10 +7486,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); - } - assert_bdrv_graph_writable(bs); bs->aio_context = NULL; } @@ -7197,11 +7495,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); - } - - assert_bdrv_graph_writable(bs); bs->aio_context = new_context; if (bs->drv && bs->drv->bdrv_attach_aio_context) { @@ -7281,22 +7574,8 @@ static void bdrv_set_aio_context_commit(void *opaque) BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; BlockDriverState *bs = (BlockDriverState *) state->bs; AioContext *new_context = state->new_ctx; - AioContext *old_context = bdrv_get_aio_context(bs); - assert_bdrv_graph_writable(bs); - /* - * Take the old AioContex when detaching it from bs. - * At this point, new_context lock is already acquired, and we are now - * also taking old_context. This is safe as long as bdrv_detach_aio_context - * does not call AIO_POLL_WHILE(). - */ - if (old_context != qemu_get_aio_context()) { - aio_context_acquire(old_context); - } bdrv_detach_aio_context(bs); - if (old_context != qemu_get_aio_context()) { - aio_context_release(old_context); - } bdrv_attach_aio_context(bs, new_context); } @@ -7311,10 +7590,6 @@ static TransactionActionDrv set_aio_context = { * * 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). - * * @visited will accumulate all visited BdrvChild objects. The caller is * responsible for freeing the list afterwards. */ @@ -7331,17 +7606,21 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, return true; } + bdrv_graph_rdlock_main_loop(); QLIST_FOREACH(c, &bs->parents, next_parent) { 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_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) { @@ -7363,13 +7642,6 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, * * If ignore_child is not NULL, that child (and its subgraph) will not * be touched. - * - * This function still requires the caller to take the bs current - * AioContext lock, otherwise draining will fail since AIO_WAIT_WHILE - * assumes the lock is always held if bs is in another AioContext. - * For the same reason, it temporarily also holds the new AioContext, since - * bdrv_drained_end calls BDRV_POLL_WHILE that assumes the lock is taken too. - * Therefore the new AioContext lock must not be taken by the caller. */ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, BdrvChild *ignore_child, Error **errp) @@ -7377,13 +7649,12 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, Transaction *tran; GHashTable *visited; int ret; - AioContext *old_context = bdrv_get_aio_context(bs); GLOBAL_STATE_CODE(); /* * Recursion phase: go through all nodes of the graph. * Take care of checking that all nodes support changing AioContext - * and drain them, builing a linear list of callbacks to run if everything + * and drain them, building a linear list of callbacks to run if everything * is successful (the transaction itself). */ tran = tran_new(); @@ -7396,8 +7667,8 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, /* * Linear phase: go through all callbacks collected in the transaction. - * Run all callbacks collected in the recursion to switch all nodes - * AioContext lock (transaction commit), or undo all changes done in the + * 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). */ @@ -7407,34 +7678,7 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, return -EPERM; } - /* - * Release old AioContext, it won't be needed anymore, as all - * bdrv_drained_begin() have been called already. - */ - if (qemu_get_aio_context() != old_context) { - aio_context_release(old_context); - } - - /* - * Acquire new AioContext since bdrv_drained_end() is going to be called - * after we switched all nodes in the new AioContext, and the function - * assumes that the lock of the bs is always taken. - */ - if (qemu_get_aio_context() != ctx) { - aio_context_acquire(ctx); - } - tran_commit(tran); - - if (qemu_get_aio_context() != ctx) { - aio_context_release(ctx); - } - - /* Re-acquire the old AioContext, since the caller takes and releases it. */ - if (qemu_get_aio_context() != old_context) { - aio_context_acquire(old_context); - } - return 0; } @@ -7556,7 +7800,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(); @@ -7565,12 +7808,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 @@ -7583,12 +7822,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; } @@ -7687,7 +7923,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) { @@ -7821,7 +8057,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) * Both of these conditions are represented by generate_json_filename. */ if (primary_child_bs->exact_filename[0] && - primary_child_bs->drv->bdrv_file_open && + primary_child_bs->drv->protocol_name && !drv->is_filter && !generate_json_filename) { strcpy(bs->exact_filename, primary_child_bs->exact_filename); @@ -7886,6 +8122,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); @@ -8042,8 +8297,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/aio_task.c b/block/aio_task.c index 9bd17ea2c1..bb5c05f455 100644 --- a/block/aio_task.c +++ b/block/aio_task.c @@ -119,8 +119,3 @@ int aio_task_pool_status(AioTaskPool *pool) return pool->status; } - -bool aio_task_pool_empty(AioTaskPool *pool) -{ - return pool->busy_tasks == 0; -} 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 6a9ad97a53..a1292c01ec 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" @@ -269,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; } @@ -353,7 +356,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockDriverState *target, int64_t speed, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, BitmapSyncMode bitmap_mode, - bool compress, + bool compress, bool discard_source, const char *filter_node_name, BackupPerf *perf, BlockdevOnError on_source_error, @@ -381,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); @@ -433,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; @@ -440,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; @@ -450,7 +457,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } - cbw = bdrv_cbw_append(bs, target, filter_node_name, &bcs, errp); + cbw = bdrv_cbw_append(bs, target, filter_node_name, discard_source, + perf->min_cluster_size, &bcs, errp); if (!cbw) { goto error; } @@ -489,8 +497,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; @@ -503,4 +513,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 4265ca125e..c95c818c38 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; } @@ -509,6 +508,8 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, 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 | @@ -521,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); @@ -531,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); @@ -540,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); @@ -550,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); @@ -559,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); @@ -569,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); @@ -584,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; @@ -627,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) { @@ -648,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) { @@ -669,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 coroutine_fn 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); @@ -680,9 +683,9 @@ static int coroutine_fn 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); @@ -713,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; @@ -747,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; @@ -837,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; @@ -967,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; @@ -1071,12 +1073,12 @@ static BlockDriver bdrv_blkdebug = { .is_filter = true, .bdrv_parse_filename = blkdebug_parse_filename, - .bdrv_file_open = blkdebug_open, + .bdrv_open = blkdebug_open, .bdrv_close = blkdebug_close, .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, @@ -1087,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 index 5eae3adfaf..e0e765af63 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -13,21 +13,15 @@ #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() */ -/* - * Keep the QEMU BlockDriver names identical to the libblkio driver names. - * Using macros instead of typing out the string literals avoids typos. - */ -#define DRIVER_IO_URING "io_uring" -#define DRIVER_NVME_IO_URING "nvme-io_uring" -#define DRIVER_VIRTIO_BLK_VFIO_PCI "virtio-blk-vfio-pci" -#define DRIVER_VIRTIO_BLK_VHOST_USER "virtio-blk-vhost-user" -#define DRIVER_VIRTIO_BLK_VHOST_VDPA "virtio-blk-vhost-vdpa" +#include "block/block-io.h" /* * Allocated bounce buffers are kept in a list sorted by buffer address. @@ -74,7 +68,7 @@ typedef struct { CoQueue bounce_available; /* The value of the "mem-region-alignment" property */ - size_t mem_region_alignment; + uint64_t mem_region_alignment; /* Can we skip adding/deleting blkio_mem_regions? */ bool needs_mem_regions; @@ -95,6 +89,9 @@ static int blkio_resize_bounce_pool(BDRVBlkioState *s, int64_t bytes) /* 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; @@ -304,35 +301,44 @@ static void blkio_attach_aio_context(BlockDriverState *bs, { BDRVBlkioState *s = bs->opaque; - aio_set_fd_handler(new_context, - s->completion_fd, - false, - blkio_completion_fd_read, - NULL, + aio_set_fd_handler(new_context, s->completion_fd, + blkio_completion_fd_read, NULL, blkio_completion_fd_poll, - blkio_completion_fd_poll_ready, - bs); + 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, - false, NULL, NULL, NULL, NULL, NULL); + aio_set_fd_handler(bdrv_get_aio_context(bs), s->completion_fd, NULL, NULL, + NULL, NULL, NULL); } -/* Call with s->blkio_lock held to submit I/O after enqueuing a new request */ -static void blkio_submit_io(BlockDriverState *bs) +/* + * Called by defer_call_end() or immediately if not in a deferred section. + * Called without blkio_lock. + */ +static void blkio_deferred_fn(void *opaque) { - if (qatomic_read(&bs->io_plugged) == 0) { - BDRVBlkioState *s = bs->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) { @@ -343,9 +349,9 @@ blkio_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { blkioq_discard(s->blkioq, offset, bytes, &cod, 0); - blkio_submit_io(bs); } + blkio_submit_io(bs); qemu_coroutine_yield(); return cod.ret; } @@ -376,9 +382,9 @@ blkio_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { blkioq_readv(s->blkioq, offset, iov, iovcnt, &cod, 0); - blkio_submit_io(bs); } + blkio_submit_io(bs); qemu_coroutine_yield(); if (use_bounce_buffer) { @@ -421,9 +427,9 @@ static int coroutine_fn blkio_co_pwritev(BlockDriverState *bs, int64_t offset, WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { blkioq_writev(s->blkioq, offset, iov, iovcnt, &cod, blkio_flags); - blkio_submit_io(bs); } + blkio_submit_io(bs); qemu_coroutine_yield(); if (use_bounce_buffer) { @@ -442,9 +448,9 @@ static int coroutine_fn blkio_co_flush(BlockDriverState *bs) WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { blkioq_flush(s->blkioq, &cod, 0); - blkio_submit_io(bs); } + blkio_submit_io(bs); qemu_coroutine_yield(); return cod.ret; } @@ -470,22 +476,13 @@ static int coroutine_fn blkio_co_pwrite_zeroes(BlockDriverState *bs, WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { blkioq_write_zeroes(s->blkioq, offset, bytes, &cod, blkio_flags); - blkio_submit_io(bs); } + blkio_submit_io(bs); qemu_coroutine_yield(); return cod.ret; } -static void blkio_io_unplug(BlockDriverState *bs) -{ - BDRVBlkioState *s = bs->opaque; - - WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { - blkio_submit_io(bs); - } -} - typedef enum { BMRR_OK, BMRR_SKIP, @@ -610,8 +607,8 @@ static void blkio_unregister_buf(BlockDriverState *bs, void *host, size_t size) } } -static int blkio_io_uring_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +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; @@ -634,11 +631,18 @@ static int blkio_io_uring_open(BlockDriverState *bs, QDict *options, int flags, } } + 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(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +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; @@ -662,38 +666,133 @@ static int blkio_nvme_io_uring(BlockDriverState *bs, QDict *options, int flags, 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_common_open(BlockDriverState *bs, - QDict *options, int flags, Error **errp) +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; - int ret; + bool fd_supported = false; + int fd = -1, 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; } + + 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_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) +static int blkio_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { const char *blkio_driver = bs->drv->protocol_name; BDRVBlkioState *s = bs->opaque; @@ -706,24 +805,6 @@ static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags, return ret; } - if (strcmp(blkio_driver, DRIVER_IO_URING) == 0) { - ret = blkio_io_uring_open(bs, options, flags, errp); - } else if (strcmp(blkio_driver, DRIVER_NVME_IO_URING) == 0) { - ret = blkio_nvme_io_uring(bs, options, flags, errp); - } else if (strcmp(blkio_driver, DRIVER_VIRTIO_BLK_VFIO_PCI) == 0) { - ret = blkio_virtio_blk_common_open(bs, options, flags, errp); - } else if (strcmp(blkio_driver, DRIVER_VIRTIO_BLK_VHOST_USER) == 0) { - ret = blkio_virtio_blk_common_open(bs, options, flags, errp); - } else if (strcmp(blkio_driver, DRIVER_VIRTIO_BLK_VHOST_VDPA) == 0) { - ret = blkio_virtio_blk_common_open(bs, options, flags, errp); - } else { - g_assert_not_reached(); - } - if (ret < 0) { - blkio_destroy(&s->blkio); - return ret; - } - if (!(flags & BDRV_O_RDWR)) { ret = blkio_set_bool(s->blkio, "read-only", true); if (ret < 0) { @@ -734,10 +815,20 @@ static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags, } } - ret = blkio_connect(s->blkio); + 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) { - error_setg_errno(errp, -ret, "blkio_connect failed: %s", - blkio_get_error_msg()); blkio_destroy(&s->blkio); return ret; } @@ -808,8 +899,10 @@ static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags, } 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; + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; +#ifdef CONFIG_BLKIO_WRITE_ZEROS_FUA + bs->supported_zero_flags |= BDRV_REQ_FUA; +#endif qemu_mutex_init(&s->blkio_lock); qemu_co_mutex_init(&s->bounce_lock); @@ -817,6 +910,7 @@ static int blkio_file_open(BlockDriverState *bs, QDict *options, int flags, 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; @@ -837,7 +931,7 @@ static void blkio_close(BlockDriverState *bs) } } -static int64_t blkio_getlength(BlockDriverState *bs) +static int64_t coroutine_fn blkio_co_getlength(BlockDriverState *bs) { BDRVBlkioState *s = bs->opaque; uint64_t capacity; @@ -865,7 +959,7 @@ static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset, return -ENOTSUP; } - current_length = blkio_getlength(bs); + current_length = blkio_co_getlength(bs); if (offset > current_length) { error_setg(errp, "Cannot grow device"); @@ -878,7 +972,8 @@ static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset, return 0; } -static int blkio_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int coroutine_fn +blkio_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { return 0; } @@ -989,50 +1084,63 @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp) * - truncate */ -#define BLKIO_DRIVER(name, ...) \ - { \ - .format_name = name, \ - .protocol_name = name, \ - .instance_size = sizeof(BDRVBlkioState), \ - .bdrv_file_open = blkio_file_open, \ - .bdrv_close = blkio_close, \ - .bdrv_getlength = blkio_getlength, \ - .bdrv_co_truncate = blkio_truncate, \ - .bdrv_get_info = blkio_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_io_unplug = blkio_io_unplug, \ - .bdrv_refresh_limits = blkio_refresh_limits, \ - .bdrv_register_buf = blkio_register_buf, \ - .bdrv_unregister_buf = blkio_unregister_buf, \ - __VA_ARGS__ \ - } +/* + * 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_open = blkio_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, -static BlockDriver bdrv_io_uring = BLKIO_DRIVER( - DRIVER_IO_URING, +/* + * 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 = BLKIO_DRIVER( - DRIVER_NVME_IO_URING, -); +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 = BLKIO_DRIVER( - DRIVER_VIRTIO_BLK_VFIO_PCI -); +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 = BLKIO_DRIVER( - DRIVER_VIRTIO_BLK_VHOST_USER -); +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 = BLKIO_DRIVER( - DRIVER_VIRTIO_BLK_VHOST_VDPA -); +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) { diff --git a/block/blklogwrites.c b/block/blklogwrites.c index cef9efe55d..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 = { @@ -168,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) { @@ -230,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); @@ -250,8 +281,11 @@ 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: qemu_opts_del(opts); @@ -262,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, @@ -289,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) { @@ -306,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; @@ -318,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)); @@ -362,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, @@ -427,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) { @@ -460,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) { @@ -469,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, @@ -497,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 76a0b8d12a..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" @@ -39,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. @@ -68,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); @@ -79,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); @@ -90,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); @@ -101,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); @@ -112,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); @@ -125,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 = { @@ -135,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 c60a2dc624..5a9bf674d9 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 */ @@ -150,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) @@ -166,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); } @@ -176,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) @@ -218,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) { @@ -247,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) { @@ -255,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 coroutine_fn blkverify_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK blkverify_co_flush(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; @@ -263,8 +273,9 @@ static int coroutine_fn 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; @@ -277,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; @@ -310,10 +321,10 @@ static BlockDriver bdrv_blkverify = { .instance_size = sizeof(BDRVBlkverifyState), .bdrv_parse_filename = blkverify_parse_filename, - .bdrv_file_open = blkverify_open, + .bdrv_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 d98a96ff37..85bcdedcef 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 */ @@ -80,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; @@ -102,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), }; @@ -120,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) @@ -129,7 +131,7 @@ 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); @@ -185,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; @@ -206,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; @@ -225,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; @@ -258,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; @@ -368,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); @@ -404,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). @@ -449,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; @@ -485,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); @@ -557,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); } } @@ -593,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; @@ -614,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 @@ -658,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); @@ -760,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) { @@ -792,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; @@ -848,15 +853,6 @@ BlockBackendPublic *blk_get_public(BlockBackend *blk) return &blk->public; } -/* - * Returns a BlockBackend given the associated @public fields. - */ -BlockBackend *blk_by_public(BlockBackendPublic *public) -{ - GLOBAL_STATE_CODE(); - return container_of(public, BlockBackend, public); -} - /* * Disassociates the currently associated BlockDriverState from @blk. */ @@ -892,7 +888,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(); } /* @@ -901,12 +900,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; } @@ -932,8 +934,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(); @@ -951,6 +954,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(); @@ -1007,22 +1019,34 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) return blk->dev; } -/* Return the qdev ID, or if no ID is assigned the QOM path, of the block - * device attached to the BlockBackend. */ -char *blk_get_attached_dev_id(BlockBackend *blk) +static char *blk_get_attached_dev_id_or_path(BlockBackend *blk, bool want_id) { DeviceState *dev = blk->dev; IO_CODE(); if (!dev) { return g_strdup(""); - } else if (dev->id) { + } else if (want_id && dev->id) { return g_strdup(dev->id); } return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); } +/* + * Return the qdev ID, or if no ID is assigned the QOM path, of the block + * device attached to the BlockBackend. + */ +char *blk_get_attached_dev_id(BlockBackend *blk) +{ + return blk_get_attached_dev_id_or_path(blk, true); +} + +static char *blk_get_attached_dev_path(BlockBackend *blk) +{ + return blk_get_attached_dev_id_or_path(blk, false); +} + /* * Return the BlockBackend which has the device model @dev attached if it * exists, else null. @@ -1057,7 +1081,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 && ops->drained_begin) { + if (qatomic_read(&blk->quiesce_counter) && ops && ops->drained_begin) { ops->drained_begin(opaque); } } @@ -1193,12 +1217,6 @@ BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) return blk->iostatus; } -void blk_iostatus_disable(BlockBackend *blk) -{ - GLOBAL_STATE_CODE(); - blk->iostatus_enabled = false; -} - void blk_iostatus_reset(BlockBackend *blk) { GLOBAL_STATE_CODE(); @@ -1232,11 +1250,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; @@ -1244,7 +1262,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; } @@ -1253,7 +1271,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; } @@ -1266,15 +1284,30 @@ 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); } } @@ -1289,6 +1322,7 @@ blk_co_do_preadv_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); @@ -1304,7 +1338,7 @@ blk_co_do_preadv_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, false); + bytes, THROTTLE_READ); } ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, @@ -1363,6 +1397,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); @@ -1377,7 +1412,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) { @@ -1424,6 +1459,29 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, 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) +{ + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum, + map, 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) +{ + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset, + bytes, pnum); +} + typedef struct BlkRwCo { BlockBackend *blk; int64_t offset; @@ -1472,7 +1530,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; } @@ -1484,16 +1542,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) @@ -1534,11 +1584,11 @@ 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); } @@ -1578,34 +1628,65 @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, flags | BDRV_REQ_ZERO_WRITE, cb, opaque); } -int64_t blk_getlength(BlockBackend *blk) +int64_t coroutine_fn blk_co_getlength(BlockBackend *blk) { IO_CODE(); - if (!blk_is_available(blk)) { + 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, @@ -1647,8 +1728,9 @@ 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; } @@ -1693,6 +1775,7 @@ blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) IO_CODE(); blk_wait_while_drained(blk); + GRAPH_RDLOCK_GUARD(); ret = blk_check_byte_request(blk, offset, bytes); if (ret < 0) { @@ -1736,10 +1819,11 @@ int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, /* To be called between exactly one pair of blk_inc/dec_in_flight() */ 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; } @@ -1774,6 +1858,204 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) return ret; } +static void coroutine_fn blk_aio_zone_report_entry(void *opaque) +{ + 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); + 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; +} + void blk_drain(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); @@ -1786,7 +2068,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); @@ -1803,14 +2085,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(); @@ -1860,7 +2136,8 @@ 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), + blk_get_attached_dev_path(blk), bs ? bdrv_get_node_name(bs) : NULL, optype, action, blk_iostatus_is_enabled(blk), error == ENOSPC, strerror(error)); @@ -1949,51 +2226,41 @@ void blk_set_enable_write_cache(BlockBackend *blk, bool wce) blk->enable_write_cache = wce; } -void blk_activate(BlockBackend *blk, Error **errp) -{ - BlockDriverState *bs = blk_bs(blk); - GLOBAL_STATE_CODE(); - - if (!bs) { - error_setg(errp, "Device '%s' has no medium", blk->name); - return; - } - - bdrv_activate(bs, errp); -} - -bool blk_is_inserted(BlockBackend *blk) +bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); IO_CODE(); + assert_bdrv_graph_readable(); - return bs && bdrv_is_inserted(bs); + return bs && bdrv_co_is_inserted(bs); } -bool blk_is_available(BlockBackend *blk) +bool coroutine_fn blk_co_is_available(BlockBackend *blk) { IO_CODE(); - return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk); + return blk_co_is_inserted(blk) && !blk_dev_is_tray_open(blk); } -void blk_lock_medium(BlockBackend *blk, bool locked) +void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) { BlockDriverState *bs = blk_bs(blk); IO_CODE(); + GRAPH_RDLOCK_GUARD(); if (bs) { - bdrv_lock_medium(bs, locked); + bdrv_co_lock_medium(bs, locked); } } -void blk_eject(BlockBackend *blk, bool eject_flag) +void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) { BlockDriverState *bs = blk_bs(blk); char *id; IO_CODE(); + GRAPH_RDLOCK_GUARD(); if (bs) { - bdrv_eject(bs, eject_flag); + bdrv_co_eject(bs, eject_flag); } /* Whether or not we ejected on the backend, @@ -2080,6 +2347,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; @@ -2088,101 +2356,49 @@ bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) return bdrv_op_is_blocked(bs, op, errp); } -void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) -{ - BlockDriverState *bs = blk_bs(blk); - GLOBAL_STATE_CODE(); - - if (bs) { - bdrv_op_unblock(bs, op, reason); - } -} - -void blk_op_block_all(BlockBackend *blk, Error *reason) -{ - BlockDriverState *bs = blk_bs(blk); - GLOBAL_STATE_CODE(); - - if (bs) { - bdrv_op_block_all(bs, reason); - } -} - -void blk_op_unblock_all(BlockBackend *blk, Error *reason) -{ - BlockDriverState *bs = blk_bs(blk); - GLOBAL_STATE_CODE(); - - if (bs) { - bdrv_op_unblock_all(bs, reason); - } -} - +/** + * 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) { - /* - * update_root_node MUST be false for blk_root_set_aio_ctx_commit(), - * as we are already in the commit function of a transaction. - */ - ret = bdrv_try_change_aio_context(bs, new_context, blk->root, errp); - if (ret < 0) { - bdrv_unref(bs); - return ret; - } - } - /* - * Make blk->ctx consistent with the root node before we invoke any - * other operations like drain that might inquire blk->ctx - */ - blk->ctx = new_context; - 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); - } else { - 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; } typedef struct BdrvStateBlkRootContext { @@ -2194,8 +2410,14 @@ 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; - blk_do_set_aio_context(blk, s->new_ctx, false, &error_abort); + 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 = { @@ -2288,32 +2510,6 @@ void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) notifier_list_add(&blk->remove_bs_notifiers, notify); } -void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) -{ - GLOBAL_STATE_CODE(); - notifier_list_add(&blk->insert_bs_notifiers, notify); -} - -void blk_io_plug(BlockBackend *blk) -{ - BlockDriverState *bs = blk_bs(blk); - IO_CODE(); - - if (bs) { - bdrv_io_plug(bs); - } -} - -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(); @@ -2349,7 +2545,8 @@ int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, 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; } @@ -2392,6 +2589,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; } @@ -2452,22 +2651,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; } @@ -2530,7 +2726,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); } @@ -2548,7 +2744,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); @@ -2556,21 +2752,24 @@ 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); } } @@ -2604,6 +2803,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 +2813,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 +2828,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 bb947afdda..eddb0b81e0 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; @@ -133,6 +137,7 @@ typedef struct BlockCopyState { CoMutex lock; int64_t in_flight_bytes; BlockCopyMethod method; + bool discard_source; BlockReqList reqs; QLIST_HEAD(, BlockCopyCallState) calls; /* @@ -305,11 +310,20 @@ void block_copy_set_copy_opts(BlockCopyState *s, bool use_copy_range, } static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, + int64_t min_cluster_size, Error **errp) { int ret; BlockDriverInfo bdi; - bool target_does_cow = bdrv_backing_chain_next(target); + bool target_does_cow; + + GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + min_cluster_size = MAX(min_cluster_size, + (int64_t)BLOCK_COPY_CLUSTER_SIZE_DEFAULT); + + target_does_cow = bdrv_backing_chain_next(target); /* * If there is no backing file on the target, we cannot rely on COW if our @@ -319,13 +333,13 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, ret = bdrv_get_info(target, &bdi); if (ret == -ENOTSUP && !target_does_cow) { /* Cluster size is not defined */ - warn_report("The target block device doesn't provide " - "information about the block size and it doesn't have a " - "backing file. The default block size of %u bytes is " - "used. If the actual block size of the target exceeds " - "this default, the backup may be unusable", - BLOCK_COPY_CLUSTER_SIZE_DEFAULT); - return BLOCK_COPY_CLUSTER_SIZE_DEFAULT; + warn_report("The target block device doesn't provide information about " + "the block size and it doesn't have a backing file. The " + "(default) block size of %" PRIi64 " bytes is used. If the " + "actual block size of the target exceeds this value, the " + "backup may be unusable", + min_cluster_size); + return min_cluster_size; } else if (ret < 0 && !target_does_cow) { error_setg_errno(errp, -ret, "Couldn't determine the cluster size of the target image, " @@ -335,14 +349,17 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target, return ret; } else if (ret < 0 && target_does_cow) { /* Not fatal; just trudge on ahead. */ - return BLOCK_COPY_CLUSTER_SIZE_DEFAULT; + return min_cluster_size; } - return MAX(BLOCK_COPY_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); + return MAX(min_cluster_size, bdi.cluster_size); } BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + BlockDriverState *copy_bitmap_bs, const BdrvDirtyBitmap *bitmap, + bool discard_source, + uint64_t min_cluster_size, Error **errp) { ERRP_GUARD(); @@ -351,12 +368,25 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, BdrvDirtyBitmap *copy_bitmap; bool is_fleecing; - cluster_size = block_copy_calculate_cluster_size(target->bs, errp); + GLOBAL_STATE_CODE(); + + if (min_cluster_size > INT64_MAX) { + error_setg(errp, "min-cluster-size too large: %" PRIu64 " > %" PRIi64, + min_cluster_size, INT64_MAX); + return NULL; + } else if (min_cluster_size && !is_power_of_2(min_cluster_size)) { + error_setg(errp, "min-cluster-size needs to be a power of 2"); + return NULL; + } + + cluster_size = block_copy_calculate_cluster_size(target->bs, + (int64_t)min_cluster_size, + errp); if (cluster_size < 0) { return NULL; } - copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL, + copy_bitmap = bdrv_create_dirty_bitmap(copy_bitmap_bs, cluster_size, NULL, errp); if (!copy_bitmap) { return NULL; @@ -388,7 +418,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) { @@ -404,6 +436,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, cluster_size), }; + s->discard_source = discard_source; block_copy_set_copy_opts(s, false, false); ratelimit_init(&s->rate_limit); @@ -458,17 +491,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; @@ -552,10 +584,12 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) BlockCopyState *s = t->s; bool error_is_read = false; BlockCopyMethod method = t->method; - int ret; + int ret = -1; - 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) { @@ -574,11 +608,20 @@ static coroutine_fn int block_copy_task_entry(AioTask *task) co_put_to_shres(s->mem, t->req.bytes); block_copy_task_end(t, ret); + if (s->discard_source && ret == 0) { + int64_t nbytes = + MIN(t->req.offset + t->req.bytes, s->len) - t->req.offset; + WITH_GRAPH_RDLOCK_GUARD() { + bdrv_co_pdiscard(s->source, t->req.offset, nbytes); + } + } + 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 +633,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 +656,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 +668,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 +714,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 +743,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 +866,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; @@ -885,6 +932,7 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state) static void coroutine_fn block_copy_async_co_entry(void *opaque) { + GRAPH_RDLOCK_GUARD(); block_copy_common(opaque); } 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/bochs.c b/block/bochs.c index e30e3908d9..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,8 +105,12 @@ 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; } @@ -115,6 +120,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + ret = bdrv_pread(bs->file, 0, sizeof(bochs), &bochs, 0); if (ret < 0) { return ret; @@ -202,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; @@ -223,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), 1, - &bitmap_entry, 0); + ret = bdrv_co_pread(bs->file, bitmap_offset + (extent_offset / 8), 1, + &bitmap_entry, 0); if (ret < 0) { return ret; } @@ -236,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 3ff975a94d..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,7 +67,11 @@ 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; } @@ -76,6 +81,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* read header */ ret = bdrv_pread(bs->file, 128, 4, &s->block_size, 0); if (ret < 0) { @@ -211,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; @@ -219,8 +227,8 @@ 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], bytes, - s->compressed_block, 0); + ret = bdrv_co_pread(bs->file, s->offsets[block_num], bytes, + s->compressed_block, 0); if (ret < 0) { return -1; } @@ -243,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 0029b31944..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,19 +130,18 @@ 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; } @@ -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); @@ -245,6 +256,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) { CommitBlockJob *s; @@ -258,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) { @@ -327,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); @@ -357,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; } @@ -391,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); @@ -399,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); @@ -414,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); } } @@ -437,6 +464,7 @@ int bdrv_commit(BlockDriverState *bs) Error *local_err = NULL; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!drv) return -ENOMEDIUM; diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 4abaa7339e..81afeff1c7 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -32,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" @@ -42,7 +43,8 @@ typedef struct BDRVCopyBeforeWriteState { BlockCopyState *bcs; BdrvChild *target; OnCbwError on_cbw_error; - uint32_t cbw_timeout_ns; + uint64_t cbw_timeout_ns; + bool discard_source; /* * @lock: protects access to @access_bitmap, @done_bitmap and @@ -64,7 +66,8 @@ typedef struct BDRVCopyBeforeWriteState { /* * @frozen_read_reqs: current read requests for fleecing user in bs->file - * node. These areas must not be rewritten by guest. + * node. These areas must not be rewritten by guest. There can be multiple + * overlapping read requests. */ BlockReqList frozen_read_reqs; @@ -77,9 +80,9 @@ typedef struct BDRVCopyBeforeWriteState { 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); } @@ -148,8 +151,8 @@ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs, 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) { @@ -159,8 +162,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) { @@ -170,11 +174,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) { @@ -184,7 +186,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; @@ -203,7 +205,7 @@ 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 coroutine_fn BlockReq * +static BlockReq * coroutine_fn GRAPH_RDLOCK cbw_snapshot_read_lock(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, BdrvChild **file) { @@ -256,7 +258,7 @@ 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) { @@ -288,7 +290,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, @@ -305,7 +307,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. @@ -321,32 +323,44 @@ 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; + uint32_t cluster_size = block_copy_cluster_size(s->bcs); + int64_t aligned_offset = QEMU_ALIGN_UP(offset, cluster_size); + int64_t aligned_end = QEMU_ALIGN_DOWN(offset + bytes, cluster_size); + int64_t aligned_bytes; + + if (aligned_end <= aligned_offset) { + return 0; + } + aligned_bytes = aligned_end - aligned_offset; WITH_QEMU_LOCK_GUARD(&s->lock) { - bdrv_reset_dirty_bitmap(s->access_bitmap, offset, bytes); + bdrv_reset_dirty_bitmap(s->access_bitmap, aligned_offset, + aligned_bytes); } - block_copy_reset(s->bcs, offset, bytes); + block_copy_reset(s->bcs, aligned_offset, aligned_bytes); - return bdrv_co_pdiscard(s->target, offset, bytes); + return bdrv_co_pdiscard(s->target, aligned_offset, aligned_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) { + BDRVCopyBeforeWriteState *s = bs->opaque; + if (!(role & BDRV_CHILD_FILTERED)) { /* * Target child @@ -364,9 +378,17 @@ static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c, perm, shared, nperm, nshared); if (!QLIST_EMPTY(&bs->parents)) { - if (perm & BLK_PERM_WRITE) { - *nperm = *nperm | BLK_PERM_CONSISTENT_READ; + /* + * Note, that source child may be shared with backup job. Backup job + * does create own blk parent on copy-before-write node, so this + * works even if source node does not have any parents before backup + * start + */ + *nperm = *nperm | BLK_PERM_CONSISTENT_READ; + if (s->discard_source) { + *nperm = *nperm | BLK_PERM_WRITE; } + *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); } } @@ -396,6 +418,7 @@ static BlockdevOptions *cbw_parse_options(QDict *options, Error **errp) qdict_extract_subqdict(options, NULL, "bitmap"); qdict_del(options, "on-cbw-error"); qdict_del(options, "cbw-timeout"); + qdict_del(options, "min-cluster-size"); out: visit_free(v); @@ -407,6 +430,7 @@ out: 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; @@ -432,7 +456,9 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, return -EINVAL; } - if (opts->has_bitmap) { + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + if (opts->bitmap) { bitmap = block_dirty_bitmap_lookup(opts->bitmap->node, opts->bitmap->name, NULL, errp); if (!bitmap) { @@ -451,7 +477,11 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags, ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); - s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp); + s->discard_source = flags & BDRV_O_CBW_DISCARD_SOURCE; + + s->bcs = block_copy_state_new(bs->file, s->target, bs, bitmap, + flags & BDRV_O_CBW_DISCARD_SOURCE, + opts->min_cluster_size, errp); if (!s->bcs) { error_prepend(errp, "Cannot create block-copy-state: "); return -EINVAL; @@ -477,7 +507,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; } @@ -492,7 +521,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), @@ -519,13 +548,15 @@ BlockDriver bdrv_cbw_filter = { BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, + bool discard_source, + uint64_t min_cluster_size, BlockCopyState **bcs, Error **errp) { - ERRP_GUARD(); BDRVCopyBeforeWriteState *state; BlockDriverState *top; QDict *opts; + int flags = BDRV_O_RDWR | (discard_source ? BDRV_O_CBW_DISCARD_SOURCE : 0); assert(source->total_sectors == target->total_sectors); GLOBAL_STATE_CODE(); @@ -538,7 +569,15 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, qdict_put_str(opts, "file", bdrv_get_node_name(source)); qdict_put_str(opts, "target", bdrv_get_node_name(target)); - top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp); + if (min_cluster_size > INT64_MAX) { + error_setg(errp, "min-cluster-size too large: %" PRIu64 " > %" PRIi64, + min_cluster_size, INT64_MAX); + qobject_unref(opts); + return NULL; + } + qdict_put_int(opts, "min-cluster-size", (int64_t)min_cluster_size); + + top = bdrv_insert_node(source, opts, flags, errp); if (!top) { return NULL; } diff --git a/block/copy-before-write.h b/block/copy-before-write.h index 6e72bb25e9..2a5d4ba693 100644 --- a/block/copy-before-write.h +++ b/block/copy-before-write.h @@ -39,6 +39,8 @@ BlockDriverState *bdrv_cbw_append(BlockDriverState *source, BlockDriverState *target, const char *filter_node_name, + bool discard_source, + uint64_t min_cluster_size, BlockCopyState **bcs, Error **errp); void bdrv_cbw_drop(BlockDriverState *bs); diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 815ac1d835..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,8 +35,8 @@ 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; @@ -43,11 +44,15 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, const char *bottom_node = qdict_get_try_str(options, "bottom"); int ret; + 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 | @@ -120,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; @@ -146,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; } @@ -179,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); @@ -249,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, @@ -257,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 3a2bad564f..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,12 +55,13 @@ 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); @@ -71,7 +74,7 @@ nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking, * the "I/O or GS" API. */ -int generated_co_wrapper +int co_wrapper_mixed_bdrv_rdlock bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, @@ -82,7 +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); #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 2fb8add458..80b2dba17a 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 */ }; @@ -63,9 +64,14 @@ static int block_crypto_read_func(QCryptoBlock *block, Error **errp) { BlockDriverState *bs = opaque; + BlockCrypto *crypto = bs->opaque; ssize_t ret; - ret = bdrv_pread(bs->file, offset, buflen, buf, 0); + 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; @@ -81,9 +87,14 @@ static int block_crypto_write_func(QCryptoBlock *block, Error **errp) { BlockDriverState *bs = opaque; + BlockCrypto *crypto = bs->opaque; ssize_t ret; - ret = bdrv_pwrite(bs->file, offset, buflen, buf, 0); + 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; @@ -99,12 +110,10 @@ struct BlockCryptoCreateData { }; -static int 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; @@ -117,10 +126,9 @@ static int block_crypto_create_write_func(QCryptoBlock *block, return 0; } -static int 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; @@ -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,6 +310,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, int flags, Error **errp) { + ERRP_GUARD(); + BlockCrypto *crypto = bs->opaque; QemuOpts *opts = NULL; int ret; @@ -266,11 +319,22 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, unsigned int cflags = 0; QDict *cryptoopts = NULL; + 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; @@ -292,11 +356,13 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, 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, cflags, - 1, errp); if (!crypto->block) { @@ -314,19 +380,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; @@ -338,7 +405,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, }; @@ -346,6 +413,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) { @@ -356,11 +424,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) @@ -398,7 +466,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) { @@ -460,7 +528,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) { @@ -531,10 +599,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); @@ -613,7 +682,7 @@ err: static int block_crypto_probe_luks(const uint8_t *buf, int buf_size, const char *filename) { - return block_crypto_probe_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, + return block_crypto_probe_generic(QCRYPTO_BLOCK_FORMAT_LUKS, buf, buf_size, filename); } @@ -622,30 +691,40 @@ static int block_crypto_open_luks(BlockDriverState *bs, int flags, Error **errp) { - return block_crypto_open_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS, + return block_crypto_open_generic(QCRYPTO_BLOCK_FORMAT_LUKS, &block_crypto_runtime_opts_luks, 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) { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts), }; @@ -653,22 +732,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; @@ -676,6 +791,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; @@ -703,20 +821,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; } @@ -728,22 +851,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; } @@ -764,7 +889,7 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) if (!info) { return NULL; } - assert(info->format == Q_CRYPTO_BLOCK_FORMAT_LUKS); + assert(info->format == QCRYPTO_BLOCK_FORMAT_LUKS); spec_info = g_new(ImageInfoSpecific, 1); spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; @@ -779,7 +904,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; @@ -795,7 +920,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; @@ -830,7 +955,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, @@ -877,7 +1002,7 @@ coroutine_fn block_crypto_co_amend_luks(BlockDriverState *bs, QCryptoBlockAmendOptions amend_opts; amend_opts = (QCryptoBlockAmendOptions) { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = *qapi_BlockdevAmendOptionsLUKS_base(&opts->u.luks), }; return block_crypto_amend_options_generic_luks(bs, &amend_opts, @@ -953,9 +1078,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 0b125095e3..0fdb6d39ac 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" @@ -131,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; } @@ -179,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; } @@ -209,37 +210,29 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) { BDRVCURLState *s = opaque; size_t realsize = size * nmemb; - const char *header = (char *)ptr; - const char *end = header + realsize; - const char *accept_ranges = "accept-ranges:"; - const char *bytes = "bytes"; + const char *p = ptr; + const char *end = p + realsize; + const char *t = "accept-ranges : bytes "; /* A lowercase template */ - if (realsize >= strlen(accept_ranges) - && g_ascii_strncasecmp(header, accept_ranges, - strlen(accept_ranges)) == 0) { - - char *p = strchr(header, ':') + 1; - - /* Skip whitespace between the header name and value. */ - while (p < end && *p && g_ascii_isspace(*p)) { - p++; - } - - if (end - p >= strlen(bytes) - && strncmp(p, bytes, strlen(bytes)) == 0) { - - /* Check that there is nothing but whitespace after the value. */ - p += strlen(bytes); - while (p < end && *p && g_ascii_isspace(*p)) { - p++; - } - - if (p == end || !*p) { - s->accept_range = true; + /* check if header matches the "t" template */ + for (;;) { + if (*t == ' ') { /* space in t matches any amount of isspace in p */ + if (p < end && g_ascii_isspace(*p)) { + ++p; + } else { + ++t; } + } else if (*t && p < end && *t == g_ascii_tolower(*p)) { + ++p, ++t; + } else { + break; } } + if (!*t && p == end) { /* if we managed to reach ends of both strings */ + s->accept_range = true; + } + return realsize; } @@ -695,8 +688,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, 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; } @@ -879,8 +874,10 @@ 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; } @@ -987,7 +984,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; @@ -1029,9 +1026,9 @@ static BlockDriver bdrv_http = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1048,9 +1045,9 @@ static BlockDriver bdrv_https = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1067,9 +1064,9 @@ static BlockDriver bdrv_ftp = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_open = curl_open, .bdrv_close = curl_close, - .bdrv_getlength = curl_getlength, + .bdrv_co_getlength = curl_co_getlength, .bdrv_co_preadv = curl_co_preadv, @@ -1086,9 +1083,9 @@ static BlockDriver bdrv_ftps = { .instance_size = sizeof(BDRVCURLState), .bdrv_parse_filename = curl_parse_filename, - .bdrv_file_open = curl_open, + .bdrv_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 bf3dc0512a..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 { @@ -388,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); } @@ -399,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) { @@ -447,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, @@ -470,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); @@ -541,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); 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 675e840ca5..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,7 +70,8 @@ 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; @@ -86,7 +85,8 @@ 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; @@ -323,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; @@ -390,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; @@ -454,7 +456,11 @@ 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; } @@ -463,6 +469,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, if (ret < 0) { return ret; } + + GRAPH_RDLOCK_GUARD_MAINLOOP(); + /* * NB: if uncompress submodules are absent, * ie block_module_load return value == 0, the function pointers @@ -618,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; @@ -635,8 +645,8 @@ 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->lengths[chunk], - s->compressed_chunk, 0); + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); if (ret < 0) { return -1; } @@ -661,8 +671,8 @@ 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->lengths[chunk], - s->compressed_chunk, 0); + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); if (ret < 0) { return -1; } @@ -682,8 +692,8 @@ 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->lengths[chunk], - s->compressed_chunk, 0); + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->compressed_chunk, 0); if (ret < 0) { return -1; } @@ -698,8 +708,8 @@ 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->lengths[chunk], - s->uncompressed_chunk, 0); + ret = bdrv_co_pread(bs->file, s->offsets[chunk], s->lengths[chunk], + s->uncompressed_chunk, 0); if (ret < 0) { return -1; } @@ -715,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 7cc0c25c1c..6d51ae8ed7 100644 --- a/block/export/export.c +++ b/block/export/export.c @@ -83,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; @@ -112,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; @@ -131,8 +132,6 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp) set_context_errp = fixed_iothread ? errp : NULL; 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; @@ -145,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) { @@ -187,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); @@ -201,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); @@ -243,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); @@ -266,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); } /* @@ -306,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) @@ -339,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 1b26ddfcf3..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; } @@ -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/vduse-blk.c b/block/export/vduse-blk.c index f101c24c3f..bd852e538d 100644 --- a/block/export/vduse-blk.c +++ b/block/export/vduse-blk.c @@ -10,9 +10,9 @@ * later. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include -#include "qemu/osdep.h" #include "qapi/error.h" #include "block/export.h" #include "qemu/error-report.h" @@ -31,7 +31,8 @@ typedef struct VduseBlkExport { VduseDev *dev; uint16_t num_queues; char *recon_file; - unsigned int inflight; + unsigned int inflight; /* atomic */ + bool vqs_started; } VduseBlkExport; typedef struct VduseBlkReq { @@ -41,13 +42,20 @@ typedef struct VduseBlkReq { static void vduse_blk_inflight_inc(VduseBlkExport *vblk_exp) { - vblk_exp->inflight++; + 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 (--vblk_exp->inflight == 0) { + 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); } } @@ -124,18 +132,27 @@ 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), - true, on_vduse_vq_kick, NULL, NULL, NULL, vq); - /* Make sure we don't miss any kick afer reconnecting */ + 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); - aio_set_fd_handler(vblk_exp->export.ctx, vduse_queue_get_fd(vq), - true, NULL, NULL, NULL, NULL, NULL); + if (fd < 0) { + return; + } + + aio_set_fd_handler(vblk_exp->export.ctx, fd, + NULL, NULL, NULL, NULL, NULL); } static const VduseOps vduse_blk_ops = { @@ -152,42 +169,19 @@ static void on_vduse_dev_kick(void *opaque) static void vduse_blk_attach_ctx(VduseBlkExport *vblk_exp, AioContext *ctx) { - int i; - aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), - true, on_vduse_dev_kick, NULL, NULL, NULL, + on_vduse_dev_kick, NULL, NULL, NULL, vblk_exp->dev); - for (i = 0; i < vblk_exp->num_queues; i++) { - VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); - int fd = vduse_queue_get_fd(vq); - - if (fd < 0) { - continue; - } - aio_set_fd_handler(vblk_exp->export.ctx, fd, true, - on_vduse_vq_kick, NULL, NULL, NULL, vq); - } + /* Virtqueues are handled by vduse_blk_drained_end() */ } static void vduse_blk_detach_ctx(VduseBlkExport *vblk_exp) { - int i; - - for (i = 0; i < vblk_exp->num_queues; i++) { - VduseVirtq *vq = vduse_dev_get_queue(vblk_exp->dev, i); - int fd = vduse_queue_get_fd(vq); - - if (fd < 0) { - continue; - } - aio_set_fd_handler(vblk_exp->export.ctx, fd, - true, NULL, NULL, NULL, NULL, NULL); - } aio_set_fd_handler(vblk_exp->export.ctx, vduse_dev_get_fd(vblk_exp->dev), - true, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); - AIO_WAIT_WHILE(vblk_exp->export.ctx, vblk_exp->inflight > 0); + /* Virtqueues are handled by vduse_blk_drained_begin() */ } @@ -220,8 +214,55 @@ static void vduse_blk_resize(void *opaque) (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, + .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, @@ -232,7 +273,6 @@ static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, 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; @@ -256,19 +296,17 @@ static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, 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); + if (!check_block_size("logical-block-size", logical_block_size, + errp)) { return -EINVAL; } } vblk_exp->num_queues = num_queues; vblk_exp->handler.blk = exp->blk; - vblk_exp->handler.serial = g_strdup(vblk_opts->has_serial ? - vblk_opts->serial : ""); + 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); @@ -323,14 +361,20 @@ static int vduse_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, vduse_dev_setup_queue(vblk_exp->dev, i, queue_size); } - aio_set_fd_handler(exp->ctx, vduse_dev_get_fd(vblk_exp->dev), true, + 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); @@ -345,9 +389,11 @@ 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); - blk_set_dev_ops(exp->blk, NULL, NULL); ret = vduse_dev_destroy(vblk_exp->dev); if (ret != -EBUSY) { unlink(vblk_exp->recon_file); @@ -356,13 +402,12 @@ static void vduse_blk_exp_delete(BlockExport *exp) 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); - aio_context_acquire(vblk_exp->export.ctx); - vduse_blk_detach_ctx(vblk_exp); - aio_context_acquire(vblk_exp->export.ctx); + vduse_blk_stop_virtqueues(vblk_exp); } const BlockExportDriver blk_exp_vduse_blk = { diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index 3409d9e02e..d9d2014d9b 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" @@ -49,7 +50,10 @@ static void vu_blk_req_complete(VuBlkReq *req, size_t in_len) free(req); } -/* 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; @@ -67,12 +71,12 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) in_num, out_num); if (in_len < 0) { free(req); - vhost_user_server_unref(server); + vhost_user_server_dec_in_flight(server); return; } vu_blk_req_complete(req, in_len); - vhost_user_server_unref(server); + vhost_user_server_dec_in_flight(server); } static void vu_blk_process_vq(VuDev *vu_dev, int idx) @@ -94,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); } } @@ -163,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; } @@ -208,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; } @@ -251,12 +261,64 @@ 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) { VuBlkExport *vexp = container_of(exp, VuBlkExport, export); BlockExportOptionsVhostUserBlk *vu_opts = &opts->u.vhost_user_blk; - Error *local_err = NULL; uint64_t logical_block_size; uint16_t num_queues = VHOST_USER_BLK_NUM_QUEUES_DEFAULT; @@ -267,10 +329,7 @@ static int vu_blk_exp_create(BlockExport *exp, BlockExportOptions *opts, } else { logical_block_size = VIRTIO_BLK_SECTOR_SIZE; } - check_block_size(exp->id, "logical-block-size", logical_block_size, - &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!check_block_size("logical-block-size", logical_block_size, errp)) { return -EINVAL; } @@ -292,6 +351,8 @@ 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, 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 index 313666e8ab..bc1cec6757 100644 --- a/block/export/virtio-blk-handler.c +++ b/block/export/virtio-blk-handler.c @@ -22,8 +22,9 @@ struct virtio_blk_inhdr { unsigned char status; }; -static bool virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size, - uint64_t sector, size_t size) +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; @@ -41,7 +42,7 @@ static bool virtio_blk_sect_range_ok(BlockBackend *blk, uint32_t block_size, if ((sector << VIRTIO_BLK_SECTOR_BITS) % block_size) { return false; } - blk_get_geometry(blk, &total_sectors); + blk_co_get_geometry(blk, &total_sectors); if (sector > total_sectors || nb_sectors > total_sectors - sector) { return false; } diff --git a/block/file-posix.c b/block/file-posix.c index b9647c5ffc..90fa54352c 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 @@ -155,6 +159,7 @@ typedef struct BDRVRawState { bool has_discard:1; bool has_write_zeroes:1; bool use_linux_aio:1; + bool has_laio_fdsync:1; bool use_linux_io_uring:1; int page_cache_inconsistent; /* errno from fdatasync failure */ bool has_fallocate; @@ -215,6 +220,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; @@ -701,17 +713,14 @@ 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 && !(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 (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; - } + s->has_laio_fdsync = laio_has_fdsync(s->fd); } #else if (s->use_linux_aio) { @@ -722,14 +731,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."); @@ -765,6 +767,18 @@ 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 __linux__ @@ -1029,8 +1043,7 @@ static int fcntl_setfl(int fd, int flag) } static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, - int *open_flags, uint64_t perm, bool force_dup, - Error **errp) + int *open_flags, uint64_t perm, Error **errp) { BDRVRawState *s = bs->opaque; int fd = -1; @@ -1058,7 +1071,7 @@ static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, assert((s->open_flags & O_ASYNC) == 0); #endif - if (!force_dup && *open_flags == s->open_flags) { + if (*open_flags == s->open_flags) { /* We're lucky, the existing fd is fine */ return s->fd; } @@ -1135,9 +1148,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; @@ -1201,15 +1214,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) { @@ -1217,46 +1304,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 = BLK_Z_NONE; + 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; @@ -1298,6 +1518,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) @@ -1321,9 +1543,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) { @@ -1379,9 +1604,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; } @@ -1463,18 +1688,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; @@ -1494,7 +1718,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, @@ -1587,7 +1811,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; @@ -1740,7 +1964,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) { @@ -1792,6 +2016,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; @@ -1899,9 +2264,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; @@ -2044,12 +2407,9 @@ 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); } /* @@ -2074,14 +2434,68 @@ static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) return true; } -static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, +#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 @@ -2092,17 +2506,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 } @@ -2119,55 +2533,52 @@ 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) { - 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 + return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE); } static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) @@ -2188,38 +2599,16 @@ static int coroutine_fn 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); + 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(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 (s->has_laio_fdsync && raw_check_linux_aio(s)) { + return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); } #endif + return raw_thread_pool_submit(handle_aiocb_flush, &acb); } static void raw_close(BlockDriverState *bs) @@ -2227,6 +2616,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; } @@ -2255,7 +2647,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, @@ -2468,7 +2860,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; @@ -2611,10 +3008,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; @@ -2924,8 +3320,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; @@ -2985,6 +3381,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) @@ -3005,7 +3564,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; } @@ -3080,7 +3639,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( @@ -3090,11 +3649,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; @@ -3168,8 +3756,7 @@ static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, int ret; /* We may need a new fd if auto-read-only switches the mode */ - ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, - false, errp); + ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, errp); if (ret < 0) { return ret; } else if (ret != s->fd) { @@ -3247,7 +3834,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) @@ -3256,14 +3843,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; @@ -3291,7 +3876,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 = { @@ -3301,7 +3886,7 @@ BlockDriver bdrv_file = { .bdrv_needs_filename = true, .bdrv_probe = NULL, /* no probe for protocols */ .bdrv_parse_filename = raw_parse_filename, - .bdrv_file_open = raw_open, + .bdrv_open = raw_open, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, .bdrv_reopen_abort = raw_reopen_abort, @@ -3321,15 +3906,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, @@ -3347,11 +3929,6 @@ BlockDriver bdrv_file = { static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize, int flags); -#if !defined(MAC_OS_VERSION_12_0) \ - || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) -#define IOMainPort IOMasterPort -#endif - static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) { kern_return_t kernResult = KERN_FAILURE; @@ -3621,7 +4198,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); } } @@ -3637,7 +4214,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 */ @@ -3675,7 +4252,7 @@ static BlockDriver bdrv_host_device = { .bdrv_needs_filename = true, .bdrv_probe_device = hdev_probe_device, .bdrv_parse_filename = hdev_parse_filename, - .bdrv_file_open = hdev_open, + .bdrv_open = hdev_open, .bdrv_close = raw_close, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, @@ -3693,15 +4270,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, @@ -3713,6 +4287,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__) @@ -3721,6 +4303,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__ @@ -3761,7 +4349,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; @@ -3770,7 +4358,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; @@ -3783,7 +4371,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; @@ -3803,7 +4391,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_parse_filename = cdrom_parse_filename, - .bdrv_file_open = cdrom_open, + .bdrv_open = cdrom_open, .bdrv_close = raw_close, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, @@ -3816,21 +4404,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, @@ -3887,12 +4470,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; @@ -3912,7 +4495,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,7 +4517,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_needs_filename = true, .bdrv_probe_device = cdrom_probe_device, .bdrv_parse_filename = cdrom_parse_filename, - .bdrv_file_open = cdrom_open, + .bdrv_open = cdrom_open, .bdrv_close = raw_close, .bdrv_reopen_prepare = raw_reopen_prepare, .bdrv_reopen_commit = raw_reopen_commit, @@ -3946,21 +4529,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 5f3249693a..91bfec0b34 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" @@ -153,7 +154,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; @@ -168,8 +168,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) @@ -553,7 +552,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; @@ -586,7 +585,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 wchar_t *filename, DWORD * high); @@ -648,10 +647,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; @@ -784,7 +782,7 @@ BlockDriver bdrv_file = { .instance_size = sizeof(BDRVRawState), .bdrv_needs_filename = true, .bdrv_parse_filename = raw_parse_filename, - .bdrv_file_open = raw_open, + .bdrv_open = raw_open, .bdrv_refresh_limits = raw_probe_alignment, .bdrv_close = raw_close, .bdrv_co_create_opts = raw_co_create_opts, @@ -799,9 +797,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, }; @@ -874,6 +872,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, @@ -963,7 +962,7 @@ static BlockDriver bdrv_host_device = { .bdrv_needs_filename = true, .bdrv_parse_filename = hdev_parse_filename, .bdrv_probe_device = hdev_probe_device, - .bdrv_file_open = hdev_open, + .bdrv_open = hdev_open, .bdrv_close = raw_close, .bdrv_refresh_limits = hdev_refresh_limits, @@ -974,11 +973,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 305716c86c..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" @@ -35,6 +36,8 @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags, 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", @@ -54,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; @@ -116,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); } @@ -134,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, @@ -142,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 7c90f7ba4b..e9c038042b 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -11,12 +11,12 @@ #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" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" -#include "qemu/uri.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" @@ -288,9 +288,9 @@ static void glfs_clear_preopened(glfs_t *fs) } } -static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path) +static int parse_volume_options(BlockdevOptionsGluster *gconf, const char *path) { - char *p, *q; + const char *p, *q; if (!path) { return -EINVAL; @@ -348,13 +348,13 @@ static int parse_volume_options(BlockdevOptionsGluster *gconf, char *path) static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf, const char *filename) { + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + g_autoptr(GHashTable) qp = NULL; SocketAddress *gsconf; - URI *uri; - QueryParams *qp = NULL; bool is_unix = false; - int ret = 0; + const char *uri_scheme, *uri_query, *uri_server; + int uri_port, ret; - uri = uri_parse(filename); if (!uri) { return -EINVAL; } @@ -363,57 +363,54 @@ static int qemu_gluster_parse_uri(BlockdevOptionsGluster *gconf, QAPI_LIST_PREPEND(gconf->server, gsconf); /* transport */ - if (!uri->scheme || !strcmp(uri->scheme, "gluster")) { + uri_scheme = g_uri_get_scheme(uri); + if (!uri_scheme || !strcmp(uri_scheme, "gluster")) { gsconf->type = SOCKET_ADDRESS_TYPE_INET; - } else if (!strcmp(uri->scheme, "gluster+tcp")) { + } else if (!strcmp(uri_scheme, "gluster+tcp")) { gsconf->type = SOCKET_ADDRESS_TYPE_INET; - } else if (!strcmp(uri->scheme, "gluster+unix")) { + } else if (!strcmp(uri_scheme, "gluster+unix")) { gsconf->type = SOCKET_ADDRESS_TYPE_UNIX; is_unix = true; - } else if (!strcmp(uri->scheme, "gluster+rdma")) { - gsconf->type = SOCKET_ADDRESS_TYPE_INET; - warn_report("rdma feature is not supported, falling back to tcp"); } else { - ret = -EINVAL; - goto out; + return -EINVAL; } - ret = parse_volume_options(gconf, uri->path); + ret = parse_volume_options(gconf, g_uri_get_path(uri)); if (ret < 0) { - goto out; + return ret; } - qp = query_params_parse(uri->query); - if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { - ret = -EINVAL; - goto out; + uri_query = g_uri_get_query(uri); + if (uri_query) { + qp = g_uri_parse_params(uri_query, -1, "&", G_URI_PARAMS_NONE, NULL); + if (!qp) { + return -EINVAL; + } + ret = g_hash_table_size(qp); + if (ret > 1 || (is_unix && !ret) || (!is_unix && ret)) { + return -EINVAL; + } } + uri_server = g_uri_get_host(uri); + uri_port = g_uri_get_port(uri); + if (is_unix) { - if (uri->server || uri->port) { - ret = -EINVAL; - goto out; + char *uri_socket = g_hash_table_lookup(qp, "socket"); + if (uri_server || uri_port != -1 || !uri_socket) { + return -EINVAL; } - if (strcmp(qp->p[0].name, "socket")) { - ret = -EINVAL; - goto out; - } - gsconf->u.q_unix.path = g_strdup(qp->p[0].value); + gsconf->u.q_unix.path = g_strdup(uri_socket); } else { - gsconf->u.inet.host = g_strdup(uri->server ? uri->server : "localhost"); - if (uri->port) { - gsconf->u.inet.port = g_strdup_printf("%d", uri->port); + gsconf->u.inet.host = g_strdup(uri_server ? uri_server : "localhost"); + if (uri_port > 0) { + gsconf->u.inet.port = g_strdup_printf("%d", uri_port); } else { gsconf->u.inet.port = g_strdup_printf("%d", GLUSTER_DEFAULT_PORT); } } -out: - if (qp) { - query_params_free(qp); - } - uri_free(uri); - return ret; + return 0; } static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, @@ -423,7 +420,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 +441,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); @@ -517,7 +514,6 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, SocketAddressList **tail; QDict *backing_options = NULL; Error *local_err = NULL; - char *str = NULL; const char *ptr; int i, type, num_servers; @@ -550,7 +546,8 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, tail = &gconf->server; for (i = 0; i < num_servers; i++) { - str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", i); + g_autofree char *str = g_strdup_printf(GLUSTER_OPT_SERVER_PATTERN"%d.", + i); qdict_extract_subqdict(options, &backing_options, str); /* create opts info from runtime_type_opts list */ @@ -661,8 +658,6 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, qobject_unref(backing_options); backing_options = NULL; - g_free(str); - str = NULL; } return 0; @@ -671,7 +666,6 @@ out: error_propagate(errp, local_err); qapi_free_SocketAddress(gsconf); qemu_opts_del(opts); - g_free(str); qobject_unref(backing_options); errno = EINVAL; return -errno; @@ -812,6 +806,8 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, goto out; } + warn_report_once("'gluster' is deprecated"); + filename = qemu_opt_get(opts, GLUSTER_OPT_FILENAME); s->debug = qemu_opt_get_number(opts, GLUSTER_OPT_DEBUG, @@ -830,7 +826,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 +858,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); @@ -917,7 +914,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 +1158,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) { @@ -1320,7 +1315,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; @@ -1333,7 +1328,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; @@ -1512,7 +1508,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); @@ -1554,15 +1550,15 @@ static BlockDriver bdrv_gluster = { .format_name = "gluster", .protocol_name = "gluster", .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, + .bdrv_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .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, @@ -1583,15 +1579,15 @@ static BlockDriver bdrv_gluster_tcp = { .format_name = "gluster", .protocol_name = "gluster+tcp", .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, + .bdrv_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .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, @@ -1612,50 +1608,15 @@ static BlockDriver bdrv_gluster_unix = { .format_name = "gluster", .protocol_name = "gluster+unix", .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, + .bdrv_open = qemu_gluster_open, .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, .bdrv_reopen_commit = qemu_gluster_reopen_commit, .bdrv_reopen_abort = qemu_gluster_reopen_abort, .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_truncate = qemu_gluster_co_truncate, - .bdrv_co_readv = qemu_gluster_co_readv, - .bdrv_co_writev = qemu_gluster_co_writev, - .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, -#ifdef CONFIG_GLUSTERFS_DISCARD - .bdrv_co_pdiscard = qemu_gluster_co_pdiscard, -#endif -#ifdef CONFIG_GLUSTERFS_ZEROFILL - .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes, -#endif - .bdrv_co_block_status = qemu_gluster_co_block_status, - .bdrv_refresh_limits = qemu_gluster_refresh_limits, - .create_opts = &qemu_gluster_create_opts, - .strong_runtime_opts = gluster_strong_open_opts, -}; - -/* rdma is deprecated (actually never supported for volfile fetch). - * Let's maintain it for the protocol compatibility, to make sure things - * won't break immediately. For now, gluster+rdma will fall back to gluster+tcp - * protocol with a warning. - * TODO: remove gluster+rdma interface support - */ -static BlockDriver bdrv_gluster_rdma = { - .format_name = "gluster", - .protocol_name = "gluster+rdma", - .instance_size = sizeof(BDRVGlusterState), - .bdrv_file_open = qemu_gluster_open, - .bdrv_reopen_prepare = qemu_gluster_reopen_prepare, - .bdrv_reopen_commit = qemu_gluster_reopen_commit, - .bdrv_reopen_abort = qemu_gluster_reopen_abort, - .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, @@ -1674,7 +1635,6 @@ static BlockDriver bdrv_gluster_rdma = { static void bdrv_gluster_init(void) { - bdrv_register(&bdrv_gluster_rdma); bdrv_register(&bdrv_gluster_unix); bdrv_register(&bdrv_gluster_tcp); bdrv_register(&bdrv_gluster); 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 bbaa0d1b2d..301514c880 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,69 +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; - AioContext *ctx = bdrv_child_get_parent_aio_context(c); - IO_OR_GS_CODE(); - bdrv_parent_drained_end_single_no_poll(c, &drained_end_counter); - AIO_WAIT_WHILE(ctx, 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)) { @@ -115,17 +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) { - AioContext *ctx = bdrv_child_get_parent_aio_context(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) { - AIO_WAIT_WHILE(ctx, bdrv_parent_drained_poll_single(c)); - } } static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) @@ -199,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) { @@ -245,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; @@ -318,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) { @@ -350,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(); @@ -374,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(). */ @@ -394,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); } } @@ -477,117 +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); + bdrv_do_drained_end(bs, NULL); } void bdrv_drain(BlockDriverState *bs) @@ -600,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) { @@ -613,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; @@ -639,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 @@ -667,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); @@ -684,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(); /* @@ -712,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--; } @@ -744,10 +588,16 @@ 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); } /** @@ -774,9 +624,9 @@ static void coroutine_fn 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, @@ -881,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 { @@ -941,9 +790,9 @@ bdrv_wait_serialising_requests(BdrvTrackedRequest *self) return; } - qemu_co_mutex_lock(&bs->reqs_lock); + qemu_mutex_lock(&bs->reqs_lock); bdrv_wait_serialising_requests_locked(self); - qemu_co_mutex_unlock(&bs->reqs_lock); + qemu_mutex_unlock(&bs->reqs_lock); } void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, @@ -951,12 +800,12 @@ void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, { IO_CODE(); - qemu_co_mutex_lock(&req->bs->reqs_lock); + qemu_mutex_lock(&req->bs->reqs_lock); tracked_request_set_serialising(req, align); bdrv_wait_serialising_requests_locked(req); - qemu_co_mutex_unlock(&req->bs->reqs_lock); + qemu_mutex_unlock(&req->bs->reqs_lock); } int bdrv_check_qiov_request(int64_t offset, int64_t bytes, @@ -1093,6 +942,7 @@ int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, { int ret; IO_CODE(); + assert_bdrv_graph_readable(); ret = bdrv_co_pwrite(child, offset, bytes, buf, flags); if (ret < 0) { @@ -1120,16 +970,16 @@ 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 & ~bs->supported_read_flags)); @@ -1189,11 +1039,10 @@ 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; @@ -1201,6 +1050,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, unsigned int nb_sectors; QEMUIOVector local_qiov; int ret; + assert_bdrv_graph_readable(); bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); @@ -1271,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) @@ -1279,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); @@ -1306,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; @@ -1320,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, @@ -1355,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 */ @@ -1394,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); @@ -1406,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); } @@ -1453,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; } @@ -1470,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; @@ -1515,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; } @@ -1530,7 +1382,7 @@ 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; @@ -1592,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; @@ -1600,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; @@ -1636,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 coroutine_fn 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; @@ -1657,10 +1524,10 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); if (pad->head) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); } if (pad->merge_reads && pad->tail) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); } ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, align, &local_qiov, 0, 0); @@ -1668,10 +1535,10 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, return ret; } if (pad->head) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); } if (pad->merge_reads && pad->tail) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); } if (pad->merge_reads) { @@ -1682,7 +1549,7 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, if (pad->tail) { qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ret = bdrv_aligned_preadv( child, req, req->overlap_offset + req->overlap_bytes - align, @@ -1690,7 +1557,7 @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child, if (ret < 0) { return ret; } - bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); } zero_mem: @@ -1701,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); @@ -1710,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 * @@ -1717,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 @@ -1725,32 +1704,51 @@ 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, + 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; } @@ -1783,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; } @@ -1811,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, &flags); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, false, + &pad, NULL, &flags); if (ret < 0) { goto fail; } @@ -1822,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); @@ -1830,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; @@ -1847,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) { @@ -1862,6 +1862,11 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, return -EINVAL; } + /* If opened with discard=off we should never unmap. */ + if (!(bs->open_flags & BDRV_O_UNMAP)) { + flags &= ~BDRV_REQ_MAY_UNMAP; + } + /* Invalidate the cached block-status data range if this write overlaps */ bdrv_bsc_invalidate_range(bs, offset, bytes); @@ -1952,7 +1957,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) { @@ -2006,7 +2011,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) { @@ -2050,10 +2055,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; @@ -2095,16 +2101,16 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, if (ret < 0) { /* Do nothing, write notifier decided to fail this request */ } else if (flags & BDRV_REQ_ZERO_WRITE) { - bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, qiov, qiov_offset); } else if (bytes <= max_transfer) { - bdrv_debug_event(bs, BLKDBG_PWRITEV); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); } else { - bdrv_debug_event(bs, BLKDBG_PWRITEV); + bdrv_co_debug_event(bs, BLKDBG_PWRITEV); while (bytes_remaining) { int num = MIN(bytes_remaining, max_transfer); int local_flags = flags; @@ -2127,7 +2133,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; @@ -2137,11 +2143,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; @@ -2153,7 +2157,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, /* This flag doesn't make sense for padding or zero writes */ flags &= ~BDRV_REQ_REGISTERED_BUF; - padding = bdrv_init_padding(bs, offset, bytes, &pad); + padding = bdrv_init_padding(bs, offset, bytes, true, &pad); if (padding) { assert(!(flags & BDRV_REQ_NO_WAIT)); bdrv_make_request_serialising(req, align); @@ -2201,7 +2205,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, } out: - bdrv_padding_destroy(&pad); + bdrv_padding_finalize(&pad); return ret; } @@ -2231,7 +2235,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; } @@ -2269,8 +2273,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, &flags); + ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, true, + &pad, &padded, &flags); if (ret < 0) { return ret; } @@ -2300,7 +2304,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); @@ -2314,10 +2318,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); - - if (!(child->bs->open_flags & BDRV_O_UNMAP)) { - flags &= ~BDRV_REQ_MAY_UNMAP; - } + assert_bdrv_graph_readable(); return bdrv_co_pwritev(child, offset, bytes, NULL, BDRV_REQ_ZERO_WRITE | flags); @@ -2333,6 +2334,7 @@ int bdrv_flush_all(void) int result = 0; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); /* * bdrv queue is managed by record/replay, @@ -2344,15 +2346,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; @@ -2385,11 +2382,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 */ @@ -2401,8 +2397,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; @@ -2422,7 +2419,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) { @@ -2547,8 +2544,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; } @@ -2560,7 +2557,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; @@ -2575,8 +2572,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. @@ -2595,6 +2592,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: @@ -2631,6 +2638,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; @@ -2642,7 +2650,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; @@ -2658,8 +2667,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; @@ -2714,21 +2723,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); } /* @@ -2759,16 +2771,16 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); } -int 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; } @@ -2792,16 +2804,18 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, * 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; } @@ -2819,6 +2833,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) { @@ -2831,8 +2846,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 { @@ -2851,6 +2866,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) { @@ -2863,8 +2879,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 { @@ -2899,25 +2915,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); } @@ -2943,14 +2952,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 */ @@ -2960,7 +2970,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) { @@ -2969,7 +2979,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) { @@ -2987,7 +2997,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) */ @@ -3048,11 +3058,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); @@ -3068,8 +3078,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; } @@ -3187,6 +3198,7 @@ int coroutine_fn 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)) { @@ -3209,6 +3221,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(); @@ -3247,49 +3327,16 @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) return mem; } -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); - } -} - /* Helper that undoes bdrv_register_buf() when it fails partway through */ -static void bdrv_register_buf_rollback(BlockDriverState *bs, - void *host, - size_t size, - BdrvChild *final_child) +static void GRAPH_RDLOCK +bdrv_register_buf_rollback(BlockDriverState *bs, void *host, size_t size, + BdrvChild *final_child) { BdrvChild *child; + GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); + QLIST_FOREACH(child, &bs->children, next) { if (child == final_child) { break; @@ -3309,6 +3356,8 @@ bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, 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; @@ -3328,6 +3377,8 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size) BdrvChild *child; GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (bs->drv && bs->drv->bdrv_unregister_buf) { bs->drv->bdrv_unregister_buf(bs, host, size); } @@ -3336,7 +3387,7 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t 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, @@ -3344,6 +3395,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)); @@ -3351,7 +3403,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); @@ -3362,7 +3414,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); @@ -3425,6 +3477,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, @@ -3442,6 +3495,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, @@ -3454,14 +3508,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); @@ -3487,6 +3547,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) { @@ -3503,7 +3564,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; @@ -3554,7 +3615,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"); @@ -3584,15 +3645,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: @@ -3605,6 +3668,8 @@ out: void bdrv_cancel_in_flight(BlockDriverState *bs) { GLOBAL_STATE_CODE(); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + if (!bs || !bs->drv) { return; } @@ -3622,6 +3687,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; @@ -3647,6 +3713,7 @@ bdrv_co_snapshot_block_status(BlockDriverState *bs, BlockDriver *drv = bs->drv; int ret; IO_CODE(); + assert_bdrv_graph_readable(); if (!drv) { return -ENOMEDIUM; @@ -3670,6 +3737,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 973e15d876..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: @@ -99,7 +102,7 @@ static void luring_resubmit_short_read(LuringState *s, LuringAIOCB *luringcb, /* Update sqe */ luringcb->sqeq.off += nread; - luringcb->sqeq.addr = (__u64)(uintptr_t)luringcb->resubmit_qiov.iov; + luringcb->sqeq.addr = (uintptr_t)luringcb->resubmit_qiov.iov; luringcb->sqeq.len = luringcb->resubmit_qiov.niov; luring_resubmit(s, luringcb); @@ -122,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 @@ -209,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) @@ -262,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) @@ -300,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); } } @@ -345,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); @@ -361,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, @@ -399,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; @@ -409,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); } @@ -424,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 1bba42a71b..979bf90cb7 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" @@ -362,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, @@ -1058,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", @@ -1127,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; @@ -1353,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)) { @@ -1536,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) { @@ -1921,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; } @@ -2155,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; @@ -2171,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; @@ -2185,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); @@ -2326,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; @@ -2426,7 +2429,7 @@ static BlockDriver bdrv_iscsi = { .instance_size = sizeof(IscsiLun), .bdrv_parse_filename = iscsi_parse_filename, - .bdrv_file_open = iscsi_open, + .bdrv_open = iscsi_open, .bdrv_close = iscsi_close, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, @@ -2434,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, @@ -2465,7 +2468,7 @@ static BlockDriver bdrv_iser = { .instance_size = sizeof(IscsiLun), .bdrv_parse_filename = iscsi_parse_filename, - .bdrv_file_open = iscsi_open, + .bdrv_open = iscsi_open, .bdrv_close = iscsi_close, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, @@ -2473,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 d2cfb7f523..e3b5ec9aba 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,26 +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); - s->io_q.plugged--; - - /* - * Why max batch checking is performed here: - * Another BDS may have queued requests with a higher dev_max_batch and - * therefore in_queue could now exceed our dev_max_batch. Re-check the max - * batch so we can honor our device's dev_max_batch. - */ - if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch) || - (!s->io_q.plugged && - !s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending))) { + if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { ioq_submit(s); } } @@ -389,9 +378,15 @@ 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; + case QEMU_AIO_FLUSH: + io_prep_fdsync(iocbs, fd); + break; /* Currently Linux kernel does not support other operations */ default: fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", @@ -402,24 +397,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, + .nbytes = qiov ? qiov->size : 0, + .ctx = aio_get_linux_aio(ctx), .ret = -EINPROGRESS, .is_read = (type == QEMU_AIO_READ), .qiov = qiov, @@ -438,7 +435,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; } @@ -447,7 +444,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); @@ -492,3 +489,19 @@ void laio_cleanup(LinuxAioState *s) } g_free(s); } + +bool laio_has_fdsync(int fd) +{ + struct iocb cb; + struct iocb *cbs[] = {&cb, NULL}; + + io_context_t ctx = 0; + io_setup(1, &ctx); + + /* check if host kernel supports IO_CMD_FDSYNC */ + io_prep_fdsync(&cb, fd); + int ret = io_submit(ctx, 1, cbs); + + io_destroy(ctx); + return (ret == -EINVAL) ? false : true; +} diff --git a/block/meson.build b/block/meson.build index b7c68b83a3..f1262ec2ba 100644 --- a/block/meson.build +++ b/block/meson.build @@ -4,49 +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) +), zstd, zlib) -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c')) -softmmu_ss.add(files('block-ram-registrar.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')) @@ -54,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 @@ -79,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')) @@ -96,7 +110,7 @@ foreach m : [ [blkio, 'blkio', files('blkio.c')], [curl, 'curl', files('curl.c')], [glusterfs, 'gluster', files('gluster.c')], - [libiscsi, 'iscsi', [files('iscsi.c'), libm]], + [libiscsi, 'iscsi', files('iscsi.c')], [libnfs, 'nfs', files('nfs.c')], [libssh, 'ssh', files('ssh.c')], [rbd, 'rbd', files('rbd.c')], @@ -105,7 +119,7 @@ foreach m : [ module_ss = ss.source_set() module_ss.add(when: m[0], if_true: m[2]) if enable_modules - modsrc += module_ss.all_sources() + modsrc += m[2] endif block_modules += {m[1] : module_ss} endif @@ -137,7 +151,10 @@ 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' ), @@ -146,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 251adc5ae0..2afe700b4d 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; @@ -85,6 +93,7 @@ typedef struct MirrorBlockJob { int64_t active_write_bytes_in_flight; bool prepared; bool in_drain; + bool base_ro; } MirrorBlockJob; typedef struct MirrorBDSOpaque { @@ -122,7 +131,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); @@ -270,8 +279,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; @@ -283,8 +292,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) { @@ -340,7 +349,7 @@ static void coroutine_fn mirror_co_read(void *opaque) MirrorOp *op = opaque; MirrorBlockJob *s = op->s; int nb_chunks; - uint64_t ret; + int ret = -1; uint64_t max_bytes; max_bytes = s->granularity * s->max_iov; @@ -389,8 +398,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); } @@ -469,17 +480,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) { @@ -501,7 +515,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) 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) { @@ -551,15 +565,17 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); while (nb_chunks > 0 && offset < s->bdev_length) { - int ret; + int ret = -1; int64_t io_bytes; int64_t io_bytes_acct; 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) { @@ -572,8 +588,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 ? @@ -602,16 +620,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) @@ -652,7 +667,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; @@ -660,11 +674,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; @@ -682,6 +700,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 @@ -695,9 +715,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); @@ -719,11 +743,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; @@ -736,12 +756,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 { @@ -750,7 +771,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; @@ -761,11 +783,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 @@ -773,7 +791,16 @@ 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(); + + if (abort && s->base_ro && !bdrv_is_read_only(target_bs)) { + bdrv_reopen_set_read_only(target_bs, true, NULL); + } + + bdrv_drained_end(target_bs); + bdrv_unref(target_bs); bs_opaque->job = NULL; @@ -809,14 +836,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; + int ret = -1; 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); @@ -862,8 +893,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; } @@ -880,9 +913,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; @@ -894,10 +927,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; + BlockDeviceIoStatus iostatus = BLOCK_DEVICE_IO_STATUS__MAX; int64_t length; int64_t target_length; BlockDriverInfo bdi; @@ -905,17 +939,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; @@ -940,7 +981,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); } @@ -956,7 +997,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; @@ -967,6 +1009,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) { @@ -984,10 +1027,15 @@ 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; @@ -1027,7 +1075,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) mirror_wait_for_free_in_flight_slot(s); continue; } else if (cnt != 0) { - delay_ns = mirror_iteration(s); + mirror_iteration(s); } } @@ -1045,9 +1093,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 || @@ -1090,12 +1138,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); } @@ -1136,24 +1186,17 @@ 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; @@ -1215,6 +1258,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), @@ -1229,6 +1314,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 = { @@ -1347,7 +1434,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) { @@ -1400,13 +1487,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 && @@ -1425,26 +1513,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 = false; - - if (s->job) { - 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); @@ -1467,6 +1557,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; } @@ -1482,20 +1577,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 = false; - - if (s->job) { - 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 @@ -1512,8 +1601,8 @@ static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, 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); @@ -1523,7 +1612,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 */ @@ -1532,21 +1621,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 @@ -1630,6 +1722,7 @@ static BlockJob *mirror_start_job( bool is_none_mode, BlockDriverState *base, bool auto_complete, const char *filter_node_name, bool is_mirror, MirrorCopyMode copy_mode, + bool base_ro, Error **errp) { MirrorBlockJob *s; @@ -1639,6 +1732,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); } @@ -1654,12 +1749,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 @@ -1703,12 +1801,12 @@ 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); s->mirror_top_bs = mirror_top_bs; + s->base_ro = base_ro; /* No resize for the target either; while the mirror is still running, a * consistent read isn't necessarily possible. We could possibly allow @@ -1743,14 +1841,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, @@ -1771,13 +1874,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; @@ -1786,20 +1890,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; } @@ -1844,14 +1955,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); @@ -1876,9 +1990,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); @@ -1907,13 +2026,17 @@ 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, &mirror_job_driver, is_none_mode, base, false, - filter_node_name, true, copy_mode, errp); + filter_node_name, true, copy_mode, false, errp); } BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, @@ -1942,7 +2065,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, - errp); + base_read_only, errp); if (!job) { goto error_restore_flags; } diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index 282363606f..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,37 +244,38 @@ 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; 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; } 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); + 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) { goto fail; } diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index cf21b5e40a..bdf2eb50b6 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,11 +199,13 @@ 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) { @@ -215,18 +214,13 @@ void hmp_commit(Monitor *mon, const QDict *qdict) } bs = bdrv_skip_implicit_filters(blk_bs(blk)); - aio_context = bdrv_get_aio_context(bs); - aio_context_acquire(aio_context); if (!blk_is_available(blk)) { error_report("Device '%s' has no medium", device); - aio_context_release(aio_context); return; } ret = bdrv_commit(bs); - - aio_context_release(aio_context); } if (ret < 0) { error_report("'commit' error for '%s': %s", device, strerror(-ret)); @@ -243,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, @@ -272,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, @@ -362,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); @@ -387,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); } @@ -413,7 +402,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - nbd_server_start(addr, NULL, NULL, 0, &local_err); + nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS, + &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; @@ -429,7 +419,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; } @@ -462,7 +452,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, @@ -497,7 +486,7 @@ void coroutine_fn hmp_block_resize(Monitor *mon, const QDict *qdict) 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); } @@ -508,11 +497,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); } @@ -536,10 +524,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; } @@ -553,7 +539,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); } @@ -562,7 +548,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"); @@ -584,9 +569,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); @@ -624,11 +606,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); } @@ -637,18 +614,18 @@ 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_puts(mon, info->device); - if (inserted && inserted->has_node_name) { + if (inserted && inserted->node_name) { monitor_printf(mon, " (%s)", inserted->node_name); } } else { assert(info || inserted); monitor_puts(mon, - inserted && inserted->has_node_name ? inserted->node_name - : info && info->has_qdev ? info->qdev + inserted && inserted->node_name ? inserted->node_name + : info && info->qdev ? info->qdev : ""); } @@ -663,7 +640,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) { @@ -688,7 +665,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", @@ -736,8 +713,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; @@ -771,8 +748,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; } @@ -786,7 +762,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; } @@ -807,7 +783,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; } @@ -852,7 +828,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", @@ -864,7 +840,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, @@ -885,7 +861,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; @@ -905,16 +880,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); @@ -925,9 +899,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); @@ -945,7 +917,6 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) } g_free(sn); } - aio_context_release(ctx); } if (no_snapshot) { @@ -1017,3 +988,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 7d485c86d2..d464315766 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 * @@ -31,7 +31,6 @@ #include "qemu/osdep.h" #include "trace.h" -#include "qemu/uri.h" #include "qemu/option.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" @@ -50,8 +49,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; @@ -275,7 +274,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 +322,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 +339,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 +352,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,7 +370,7 @@ 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; @@ -396,7 +397,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)); @@ -416,25 +416,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; @@ -444,9 +445,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. */ @@ -455,24 +456,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; } @@ -480,9 +487,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; @@ -518,7 +525,8 @@ 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); @@ -607,13 +615,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 +640,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 +669,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 +683,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 +848,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 +862,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 +888,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 +939,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 +953,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,10 +996,10 @@ 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 @@ -985,7 +1007,7 @@ static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) */ static bool coroutine_fn nbd_reply_chunk_iter_receive(BDRVNBDState *s, NBDReplyChunkIter *iter, - uint64_t handle, + uint64_t cookie, QEMUIOVector *qiov, NBDReply *reply, void **payload) @@ -1004,7 +1026,7 @@ static bool coroutine_fn 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) { @@ -1037,7 +1059,7 @@ static bool coroutine_fn 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); @@ -1045,12 +1067,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 */ } @@ -1059,16 +1082,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; @@ -1111,10 +1136,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; @@ -1123,14 +1148,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"); @@ -1138,9 +1169,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); @@ -1171,8 +1202,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; @@ -1192,11 +1224,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)); @@ -1208,9 +1240,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; @@ -1251,10 +1283,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 +1298,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 +1323,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 +1360,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 +1375,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 +1396,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 +1418,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 +1446,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 +1490,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); @@ -1475,30 +1513,31 @@ static void nbd_client_close(BlockDriverState *bs) static int nbd_parse_uri(const char *filename, QDict *options) { - URI *uri; + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + g_autoptr(GHashTable) qp = NULL; const char *p; - QueryParams *qp = NULL; - int ret = 0; + int qp_n; bool is_unix; + const char *uri_scheme, *uri_query, *uri_server; + int uri_port; - uri = uri_parse(filename); if (!uri) { return -EINVAL; } /* transport */ - if (!g_strcmp0(uri->scheme, "nbd")) { + uri_scheme = g_uri_get_scheme(uri); + if (!g_strcmp0(uri_scheme, "nbd")) { is_unix = false; - } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) { + } else if (!g_strcmp0(uri_scheme, "nbd+tcp")) { is_unix = false; - } else if (!g_strcmp0(uri->scheme, "nbd+unix")) { + } else if (!g_strcmp0(uri_scheme, "nbd+unix")) { is_unix = true; } else { - ret = -EINVAL; - goto out; + return -EINVAL; } - p = uri->path ? uri->path : ""; + p = g_uri_get_path(uri) ?: ""; if (p[0] == '/') { p++; } @@ -1506,52 +1545,50 @@ static int nbd_parse_uri(const char *filename, QDict *options) qdict_put_str(options, "export", p); } - qp = query_params_parse(uri->query); - if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { - ret = -EINVAL; - goto out; + uri_query = g_uri_get_query(uri); + if (uri_query) { + qp = g_uri_parse_params(uri_query, -1, "&", G_URI_PARAMS_NONE, NULL); + if (!qp) { + return -EINVAL; + } + qp_n = g_hash_table_size(qp); + if (qp_n > 1 || (is_unix && !qp_n) || (!is_unix && qp_n)) { + return -EINVAL; + } + } + + uri_server = g_uri_get_host(uri); + if (uri_server && !uri_server[0]) { + uri_server = NULL; } + uri_port = g_uri_get_port(uri); if (is_unix) { /* nbd+unix:///export?socket=path */ - if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { - ret = -EINVAL; - goto out; + const char *uri_socket = g_hash_table_lookup(qp, "socket"); + if (uri_server || uri_port != -1 || !uri_socket) { + return -EINVAL; } qdict_put_str(options, "server.type", "unix"); - qdict_put_str(options, "server.path", qp->p[0].value); + qdict_put_str(options, "server.path", uri_socket); } else { - QString *host; char *port_str; /* nbd[+tcp]://host[:port]/export */ - if (!uri->server) { - ret = -EINVAL; - goto out; - } - - /* strip braces from literal IPv6 address */ - if (uri->server[0] == '[') { - host = qstring_from_substr(uri->server, 1, - strlen(uri->server) - 1); - } else { - host = qstring_from_str(uri->server); + if (!uri_server) { + return -EINVAL; } qdict_put_str(options, "server.type", "inet"); - qdict_put(options, "server.host", host); + qdict_put_str(options, "server.host", uri_server); - port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); + port_str = g_strdup_printf("%d", uri_port > 0 ? uri_port + : NBD_DEFAULT_PORT); qdict_put_str(options, "server.port", port_str); g_free(port_str); } -out: - if (qp) { - query_params_free(qp); - } - uri_free(uri); - return ret; + return 0; } static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) @@ -1920,11 +1957,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 +1985,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 +2032,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 +2129,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 +2137,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 = { @@ -2114,17 +2146,17 @@ static BlockDriver bdrv_nbd = { .bdrv_parse_filename = nbd_parse_filename, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, - .bdrv_file_open = nbd_open, + .bdrv_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .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, @@ -2142,17 +2174,17 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_parse_filename = nbd_parse_filename, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, - .bdrv_file_open = nbd_open, + .bdrv_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .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, @@ -2170,17 +2202,17 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_parse_filename = nbd_parse_filename, .bdrv_co_create_opts = bdrv_co_create_opts_simple, .create_opts = &bdrv_create_opts_simple, - .bdrv_file_open = nbd_open, + .bdrv_open = nbd_open, .bdrv_reopen_prepare = nbd_client_reopen_prepare, .bdrv_co_preadv = nbd_client_co_preadv, .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 ece22353ac..0500f60c08 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" @@ -37,7 +38,6 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" -#include "qemu/uri.h" #include "qemu/cutils.h" #include "sysemu/replay.h" #include "qapi/qapi-visit-block-core.h" @@ -78,77 +78,76 @@ typedef struct NFSRPC { static int nfs_parse_uri(const char *filename, QDict *options, Error **errp) { - URI *uri = NULL; - QueryParams *qp = NULL; - int ret = -EINVAL, i; + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + GUriParamsIter qp; + const char *uri_server, *uri_path, *uri_query; + char *qp_name, *qp_value; + GError *gerror = NULL; - uri = uri_parse(filename); if (!uri) { error_setg(errp, "Invalid URI specified"); - goto out; + return -EINVAL; } - if (g_strcmp0(uri->scheme, "nfs") != 0) { + if (!g_str_equal(g_uri_get_scheme(uri), "nfs")) { error_setg(errp, "URI scheme must be 'nfs'"); - goto out; + return -EINVAL; } - if (!uri->server) { + uri_server = g_uri_get_host(uri); + if (!uri_server || !uri_server[0]) { error_setg(errp, "missing hostname in URI"); - goto out; + return -EINVAL; } - if (!uri->path) { + uri_path = g_uri_get_path(uri); + if (!uri_path || !uri_path[0]) { error_setg(errp, "missing file path in URI"); - goto out; + return -EINVAL; } - qp = query_params_parse(uri->query); - if (!qp) { - error_setg(errp, "could not parse query parameters"); - goto out; - } - - qdict_put_str(options, "server.host", uri->server); + qdict_put_str(options, "server.host", uri_server); qdict_put_str(options, "server.type", "inet"); - qdict_put_str(options, "path", uri->path); + qdict_put_str(options, "path", uri_path); - for (i = 0; i < qp->n; i++) { - unsigned long long 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)) { - error_setg(errp, "Illegal value for NFS parameter: %s", - qp->p[i].name); - goto out; - } - if (!strcmp(qp->p[i].name, "uid")) { - qdict_put_str(options, "user", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "gid")) { - qdict_put_str(options, "group", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "tcp-syncnt")) { - qdict_put_str(options, "tcp-syn-count", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "readahead")) { - qdict_put_str(options, "readahead-size", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "pagecache")) { - qdict_put_str(options, "page-cache-size", qp->p[i].value); - } else if (!strcmp(qp->p[i].name, "debug")) { - qdict_put_str(options, "debug", qp->p[i].value); - } else { - error_setg(errp, "Unknown NFS parameter name: %s", - qp->p[i].name); - goto out; + uri_query = g_uri_get_query(uri); + if (uri_query) { + g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE); + while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) { + uint64_t val; + if (!qp_name || gerror) { + error_setg(errp, "Failed to parse NFS parameter"); + return -EINVAL; + } + if (!qp_value) { + error_setg(errp, "Value for NFS parameter expected: %s", + qp_name); + return -EINVAL; + } + if (parse_uint_full(qp_value, 0, &val)) { + error_setg(errp, "Invalid value for NFS parameter: %s", + qp_name); + return -EINVAL; + } + if (g_str_equal(qp_name, "uid")) { + qdict_put_str(options, "user", qp_value); + } else if (g_str_equal(qp_name, "gid")) { + qdict_put_str(options, "group", qp_value); + } else if (g_str_equal(qp_name, "tcp-syncnt")) { + qdict_put_str(options, "tcp-syn-count", qp_value); + } else if (g_str_equal(qp_name, "readahead")) { + qdict_put_str(options, "readahead-size", qp_value); + } else if (g_str_equal(qp_name, "pagecache")) { + qdict_put_str(options, "page-cache-size", qp_value); + } else if (g_str_equal(qp_name, "debug")) { + qdict_put_str(options, "debug", qp_value); + } else { + error_setg(errp, "Unknown NFS parameter name: %s", qp_name); + return -EINVAL; + } } } - ret = 0; -out: - if (qp) { - query_params_free(qp); - } - uri_free(uri); - return ret; + + return 0; } static bool nfs_has_filename_options_conflict(QDict *options, Error **errp) @@ -194,7 +193,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); @@ -372,7 +370,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 +388,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); @@ -725,13 +723,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}; @@ -742,15 +738,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); } @@ -841,7 +841,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; @@ -884,11 +884,11 @@ 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, - .bdrv_file_open = nfs_file_open, + .bdrv_open = nfs_file_open, .bdrv_close = nfs_file_close, .bdrv_co_create = nfs_file_co_create, .bdrv_co_create_opts = nfs_file_co_create_opts, diff --git a/block/null.c b/block/null.c index 75f7d0db40..4730acc1eb 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" @@ -76,8 +77,8 @@ static void null_aio_parse_filename(const char *filename, QDict *options, } } -static int null_file_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int null_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { QemuOpts *opts; BDRVNullState *s = bs->opaque; @@ -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; } @@ -281,10 +283,10 @@ static BlockDriver bdrv_null_co = { .protocol_name = "null-co", .instance_size = sizeof(BDRVNullState), - .bdrv_file_open = null_file_open, + .bdrv_open = null_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, @@ -302,10 +304,10 @@ static BlockDriver bdrv_null_aio = { .protocol_name = "null-aio", .instance_size = sizeof(BDRVNullState), - .bdrv_file_open = null_file_open, + .bdrv_open = null_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 656624c585..3b588b139f 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); @@ -386,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(). @@ -419,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); @@ -479,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) @@ -495,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) @@ -527,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 { @@ -861,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)) { @@ -878,7 +889,7 @@ out: qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)regs, 0, sizeof(NvmeBar)); } - /* Cleaning up is done in nvme_file_open() upon error. */ + /* Cleaning up is done in nvme_open() upon error. */ return ret; } @@ -947,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); @@ -956,8 +967,8 @@ static void nvme_close(BlockDriverState *bs) g_free(s->device); } -static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags, - Error **errp) +static int nvme_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { const char *device; QemuOpts *opts; @@ -1001,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; @@ -1485,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; @@ -1545,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, @@ -1555,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++) { @@ -1566,27 +1577,6 @@ 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 bool nvme_register_buf(BlockDriverState *bs, void *host, size_t size, Error **errp) { @@ -1640,9 +1630,9 @@ static BlockDriver bdrv_nvme = { .create_opts = &bdrv_create_opts_simple, .bdrv_parse_filename = nvme_parse_filename, - .bdrv_file_open = nvme_file_open, + .bdrv_open = nvme_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, @@ -1663,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 c9dbbf5089..778b8f684e 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; @@ -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; @@ -205,7 +206,7 @@ static int parallels_parse_format_extension(BlockDriverState *bs, goto fail; } - ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining, + ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALGO_MD5, (char *)pos, remaining, &hash, &hash_len, errp); if (ret < 0) { goto fail; diff --git a/block/parallels.c b/block/parallels.c index fa08c1104b..071b6dcaf8 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,13 +171,89 @@ static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, return start_off; } -static coroutine_fn 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 ((uint64_t)cluster_index + count > bitmap_size) { + return -E2BIG; + } + next_used = find_next_bit(bitmap, bitmap_size, cluster_index); + if (next_used < (uint64_t)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) { @@ -181,7 +263,8 @@ static coroutine_fn int64_t allocate_clusters(BlockDriverState *bs, 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. @@ -189,24 +272,25 @@ static coroutine_fn int64_t allocate_clusters(BlockDriverState *bs, * 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_co_truncate(bs->file, - (s->data_end + space) << BDRV_SECTOR_BITS, + ret = bdrv_co_truncate(bs->file, host_off + bytes, false, PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE, NULL); if (ret == -ENOTSUP) { @@ -214,22 +298,53 @@ static coroutine_fn int64_t allocate_clusters(BlockDriverState *bs, } } if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { - ret = bdrv_co_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; @@ -250,18 +365,26 @@ static coroutine_fn int64_t allocate_clusters(BlockDriverState *bs, } } + 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); @@ -292,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; @@ -320,9 +439,9 @@ 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; @@ -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_co_pwrite_sync(bs->file, 0, s->header_size, s->header, 0); - 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; @@ -508,20 +774,226 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs, 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; @@ -611,8 +1083,8 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts, 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), @@ -726,24 +1197,74 @@ static int parallels_update_header(BlockDriverState *bs) 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, Error **errp) { 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; + + 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, sizeof(ph), &ph, 0); if (ret < 0) { - goto fail; + return ret; } bs->total_sectors = le64_to_cpu(ph.nb_sectors); @@ -763,38 +1284,26 @@ 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_size, s->header, 0); @@ -803,45 +1312,23 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, } 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) { @@ -877,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; } @@ -899,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); @@ -908,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 d50ee7f49b..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 = { @@ -136,17 +143,22 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags, 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); 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; } @@ -161,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); } } @@ -197,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)) { @@ -204,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; @@ -225,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); } @@ -244,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; @@ -268,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; @@ -286,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; } @@ -308,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; } @@ -344,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)); @@ -356,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); @@ -369,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) @@ -380,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; @@ -436,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; @@ -450,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; @@ -459,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); } } @@ -518,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; /* @@ -532,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, @@ -551,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 daa38839ab..84d1cca296 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -92,8 +92,8 @@ typedef struct BDRVQcowState { static QemuOptsList qcow_create_opts; -static int coroutine_fn 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) { @@ -124,9 +124,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = bdrv_open_file_child(NULL, options, "file", bs, errp); if (ret < 0) { - goto fail; + goto fail_unlocked; } + bdrv_graph_rdlock_main_loop(); + ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); if (ret < 0) { goto fail; @@ -209,7 +211,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } s->crypto = qcrypto_block_open(crypto_opts, "encrypt.", - NULL, NULL, cflags, 1, errp); + NULL, NULL, cflags, errp); if (!s->crypto) { ret = -EINVAL; goto fail; @@ -304,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); @@ -350,11 +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 coroutine_fn 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 coroutine_fn 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,7 +384,7 @@ static int coroutine_fn 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); + 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); @@ -411,7 +415,7 @@ static int coroutine_fn 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_co_pwrite_sync(bs->file, l2_offset, @@ -435,7 +439,7 @@ static int coroutine_fn 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 coroutine_fn 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); + 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; } @@ -492,7 +496,7 @@ static int coroutine_fn get_cluster_offset(BlockDriverState *bs, NULL) < 0) { return -EIO; } - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_pwrite(bs->file, cluster_offset + i, BDRV_SECTOR_SIZE, s->cluster_data, 0); @@ -511,9 +515,9 @@ static int coroutine_fn 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_co_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), sizeof(tmp), &tmp, 0); @@ -525,11 +529,10 @@ static int coroutine_fn 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,8 +592,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, return 0; } -static int coroutine_fn 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; @@ -597,7 +603,7 @@ static int coroutine_fn decompress_cluster(BlockDriverState *bs, 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); + 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; @@ -619,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; @@ -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; @@ -767,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); @@ -798,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; @@ -825,21 +830,21 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, return -EINVAL; } - if (qcow_opts->has_encrypt && - qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW) + if (qcow_opts->encrypt && + qcow_opts->encrypt->format != QCRYPTO_BLOCK_FORMAT_QCOW) { error_setg(errp, "Unsupported encryption format"); return -EINVAL; } /* 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; @@ -853,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); @@ -861,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 */ @@ -876,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; @@ -895,7 +900,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, goto exit; } - if (qcow_opts->has_backing_file) { + if (qcow_opts->backing_file) { ret = blk_co_pwrite(qcow_blk, sizeof(header), backing_filename_len, qcow_opts->backing_file, 0); if (ret < 0) { @@ -917,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; @@ -973,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; @@ -1017,12 +1022,12 @@ 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); @@ -1046,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) { @@ -1116,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; @@ -1129,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; @@ -1198,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 3dff99ba06..256ec99878 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; @@ -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; @@ -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, s->cluster_size, buf, 0); + 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; @@ -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; @@ -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 coroutine_fn 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; @@ -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; @@ -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; @@ -1551,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)) { @@ -1600,7 +1609,7 @@ bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, name); goto fail; } - tb = g_memdup(&bm->table, sizeof(bm->table)); + tb = g_memdup2(&bm->table, sizeof(bm->table)); bm->table.offset = 0; bm->table.size = 0; QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); @@ -1621,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; @@ -1701,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 54b2d5f4de..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; @@ -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; diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 40ed847f97..ce8c0076b3 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include +#include "block/block-io.h" #include "qapi/error.h" #include "qcow2.h" #include "qemu/bswap.h" @@ -47,7 +48,7 @@ int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, 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); + 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); @@ -60,7 +61,7 @@ int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, 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; @@ -206,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) * @@ -268,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; @@ -389,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; @@ -440,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; @@ -490,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; @@ -501,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; @@ -534,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; @@ -552,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) { @@ -752,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; @@ -824,10 +824,9 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, * * Return 0 on success and -errno in error cases */ -int coroutine_fn 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; @@ -873,7 +872,7 @@ int coroutine_fn 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)) { @@ -885,7 +884,8 @@ int coroutine_fn qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, return 0; } -static int coroutine_fn 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; @@ -992,7 +992,7 @@ static int coroutine_fn 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 */ @@ -1126,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) { @@ -1156,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); @@ -1328,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: @@ -1359,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); @@ -1489,9 +1491,9 @@ static int coroutine_fn handle_dependencies(BlockDriverState *bs, * * -errno: in error cases */ -static int coroutine_fn 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; @@ -1599,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; @@ -1655,9 +1658,9 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, * * -errno: in error cases */ -static int coroutine_fn 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; @@ -1895,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; @@ -1921,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, @@ -1939,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; + } } } @@ -1956,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); @@ -2010,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; @@ -2034,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 { @@ -2054,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); + } } } @@ -2065,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; @@ -2202,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); diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 81264740f0..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" @@ -117,7 +118,7 @@ int coroutine_fn qcow2_refcount_init(BlockDriverState *bs) ret = -ENOMEM; goto fail; } - BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); + 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) { @@ -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; @@ -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)); @@ -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_entry_size(s), - &l2_table[idx], 0); + 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_size_bytes, l2_table, 0); + 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_size_bytes, l1_table, 0); + 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, s->l2_size * l2_entry_size(s), - l2_table, 0); + 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, s->cluster_size, l2_table, - 0); + 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, s->cluster_size, - on_disk_refblock, 0); + 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, reftable_length, - on_disk_reftable, 0); + 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), - sizeof(reftable_offset_and_clusters), - &reftable_offset_and_clusters, 0); + 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; @@ -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; @@ -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; @@ -3540,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); @@ -3559,7 +3567,7 @@ static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset) return covering_refblock_offset; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs) { BDRVQcow2State *s = bs->opaque; @@ -3684,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; @@ -3706,7 +3714,8 @@ int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) return -EIO; } -int coroutine_fn 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; @@ -3714,12 +3723,12 @@ int coroutine_fn 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 62e8a0335d..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, sizeof(h), &h, 0); + 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, - MIN(sizeof(extra), sn->extra_data_size), &extra, 0); + 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, unknown_extra_data_size, - sn->unknown_extra_data, 0); + 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, id_str_size, sn->id_str, 0); + 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, name_size, sn->name, 0); + 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); } 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 4d6666d3ff..803ca73a2f 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 int 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; @@ -117,8 +119,9 @@ static int qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, } -static int 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,9 +146,7 @@ static int 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; @@ -155,9 +156,11 @@ static int qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, } -static int 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; @@ -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, sizeof(ext), &ext, 0); + 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, ext.len, bs->backing_format, 0); + 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,7 +274,7 @@ 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, ext.len, feature_table, 0); + 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"); @@ -297,7 +300,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return -EINVAL; } - ret = bdrv_pread(bs->file, offset, ext.len, &s->crypto_header, 0); + 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"); @@ -318,7 +321,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, } s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", qcow2_crypto_hdr_read_func, - bs, cflags, QCOW2_MAX_THREADS, errp); + bs, cflags, errp); if (!s->crypto) { return -EINVAL; } @@ -353,7 +356,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, break; } - ret = bdrv_pread(bs->file, offset, ext.len, &bitmaps_ext, 0); + 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"); @@ -417,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, ext.len, s->image_data_file, 0); + 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"); @@ -441,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->len, uext->data, 0); + 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"); @@ -534,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; @@ -568,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; @@ -600,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 = {}; @@ -639,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; @@ -680,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, @@ -724,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, @@ -967,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; @@ -1138,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) { @@ -1218,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; @@ -1240,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; @@ -1293,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; @@ -1614,11 +1636,28 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, goto fail; } - if (open_data_file) { + if (open_data_file && (flags & BDRV_O_NO_IO)) { + /* + * Don't open the data file for 'qemu-img info' so that it can be used + * to verify that an untrusted qcow2 image doesn't refer to external + * files. + * + * Note: This still makes has_data_file() return true. + */ + if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { + s->data_file = NULL; + } else { + s->data_file = bs->file; + } + qdict_extract_subqdict(options, NULL, "data-file."); + qdict_del(options, "data-file"); + } else 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; @@ -1626,9 +1665,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; @@ -1674,8 +1716,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", - NULL, NULL, cflags, - QCOW2_MAX_THREADS, errp); + NULL, NULL, cflags, errp); if (!s->crypto) { ret = -EINVAL; goto fail; @@ -1853,7 +1894,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); @@ -1888,10 +1931,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, @@ -1915,14 +1962,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* 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; } @@ -1938,13 +1984,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; @@ -1994,6 +2044,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) { /* @@ -2007,6 +2059,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; @@ -2027,6 +2081,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 @@ -2084,11 +2140,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; @@ -2132,12 +2187,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; @@ -2168,7 +2225,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, @@ -2196,7 +2253,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; @@ -2269,12 +2326,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; @@ -2288,7 +2343,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); @@ -2302,7 +2357,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); @@ -2313,7 +2368,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); @@ -2324,11 +2383,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; @@ -2448,7 +2506,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 coroutine_fn 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 @@ -2466,8 +2525,8 @@ static int coroutine_fn is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) m->cow_end.nb_bytes); } -static int coroutine_fn 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; @@ -2508,7 +2567,7 @@ static int coroutine_fn handle_alloc_space(BlockDriverState *bs, 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) { @@ -2530,12 +2589,10 @@ static int coroutine_fn handle_alloc_space(BlockDriverState *bs, * 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; @@ -2575,7 +2632,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); @@ -2601,7 +2658,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); @@ -2612,9 +2673,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; @@ -2694,7 +2756,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; @@ -2729,7 +2791,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); @@ -2756,21 +2819,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; @@ -3103,8 +3174,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; @@ -3132,19 +3204,20 @@ 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; int fmt, ret; switch (cryptoopts->format) { - case Q_CRYPTO_BLOCK_FORMAT_LUKS: + case QCRYPTO_BLOCK_FORMAT_LUKS: fmt = QCOW_CRYPT_LUKS; break; - case Q_CRYPTO_BLOCK_FORMAT_QCOW: + case QCRYPTO_BLOCK_FORMAT_QCOW: fmt = QCOW_CRYPT_AES; break; default: @@ -3157,7 +3230,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; } @@ -3181,9 +3254,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; @@ -3226,7 +3299,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; @@ -3421,9 +3494,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; @@ -3453,7 +3527,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; } @@ -3508,7 +3582,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) { @@ -3517,7 +3591,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; @@ -3558,7 +3632,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; @@ -3584,7 +3658,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) { @@ -3595,7 +3669,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; @@ -3628,8 +3702,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; @@ -3697,7 +3771,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) goto out; } - blk_unref(blk); + blk_co_unref(blk); blk = NULL; /* @@ -3711,16 +3785,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; @@ -3738,6 +3814,8 @@ 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; @@ -3752,15 +3830,18 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } /* 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, @@ -3770,14 +3851,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. @@ -3792,9 +3876,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; @@ -3802,16 +3886,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; @@ -3871,13 +3954,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; @@ -3886,14 +3969,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; @@ -3929,21 +4012,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; @@ -3964,7 +4050,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); @@ -3972,8 +4058,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; @@ -4027,8 +4114,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; @@ -4056,7 +4143,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, @@ -4089,7 +4176,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 { @@ -4139,7 +4226,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, @@ -4207,10 +4294,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; @@ -4284,7 +4372,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"); @@ -4377,7 +4465,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"); @@ -4583,7 +4671,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) @@ -4634,7 +4722,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; @@ -4647,7 +4735,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); @@ -4661,7 +4755,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) @@ -4679,7 +4773,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; } @@ -4724,7 +4818,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, @@ -4747,7 +4841,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; @@ -4767,7 +4861,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; @@ -4918,7 +5012,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; @@ -4962,7 +5056,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; @@ -5142,17 +5236,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; @@ -5195,7 +5291,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), @@ -5204,17 +5299,17 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, } else { /* if this assertion fails, this probably means a new version was * added without having it covered here */ - assert(false); + g_assert_not_reached(); } if (encrypt_info) { ImageInfoSpecificQCow2Encryption *qencrypt = g_new(ImageInfoSpecificQCow2Encryption, 1); switch (encrypt_info->format) { - case Q_CRYPTO_BLOCK_FORMAT_QCOW: + case QCRYPTO_BLOCK_FORMAT_QCOW: qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES; break; - case Q_CRYPTO_BLOCK_FORMAT_LUKS: + case QCRYPTO_BLOCK_FORMAT_LUKS: qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS; qencrypt->u.luks = encrypt_info->u.luks; break; @@ -5226,14 +5321,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; @@ -5287,31 +5382,31 @@ static int64_t qcow2_check_vmstate_request(BlockDriverState *bs, return pos; } -static coroutine_fn int qcow2_save_vmstate(BlockDriverState *bs, - QEMUIOVector *qiov, int64_t pos) +static 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 coroutine_fn 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); @@ -5347,9 +5442,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; @@ -5457,9 +5553,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; @@ -5585,11 +5682,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; @@ -5846,13 +5942,13 @@ 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; } - if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { + if (qopts->encrypt->format != QCRYPTO_BLOCK_FORMAT_LUKS) { error_setg(errp, "Amend can't be used to change the qcow2 encryption format"); return -EOPNOTSUPP; @@ -5911,7 +6007,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); @@ -6044,64 +6140,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 2285f18a73..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,158 +849,211 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, Error **errp); /* qcow2-refcount.c functions */ -int coroutine_fn 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 qcow2_flush_caches(BlockDriverState *bs); -int 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 coroutine_fn qcow2_shrink_reftable(BlockDriverState *bs); -int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); -int coroutine_fn 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 coroutine_fn 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 coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, - uint64_t *host_offset, QCowL2Meta **m); -int coroutine_fn 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 coroutine_fn 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 coroutine_fn 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 coroutine_fn 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 coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, - const char *name, - uint32_t granularity, - Error **errp); -int coroutine_fn 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 aa203f2627..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; @@ -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 2f36ad342c..fa5bc11085 100644 --- a/block/qed.c +++ b/block/qed.c @@ -100,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 @@ -195,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, n, buf, 0); + ret = bdrv_co_pread(file, offset, n, buf, 0); if (ret < 0) { return ret; } @@ -262,7 +263,7 @@ static bool coroutine_fn 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; @@ -282,12 +283,12 @@ static void coroutine_fn 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; @@ -310,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); } @@ -355,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; @@ -363,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); } } @@ -379,8 +396,8 @@ 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; @@ -410,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; @@ -546,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, @@ -569,14 +588,11 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, } 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; } @@ -596,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; @@ -615,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; @@ -662,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; @@ -698,7 +722,7 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts, 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); @@ -731,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; @@ -764,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; @@ -803,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); @@ -865,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); @@ -884,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; @@ -904,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; @@ -979,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; @@ -1009,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; @@ -1047,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 + @@ -1055,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); } @@ -1065,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; @@ -1123,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) { @@ -1143,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; @@ -1206,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; @@ -1249,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; @@ -1282,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); @@ -1308,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); } @@ -1320,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; @@ -1365,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, @@ -1384,24 +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) { 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; @@ -1428,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; @@ -1466,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; @@ -1482,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; @@ -1546,7 +1568,7 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs, } /* Write new header */ - ret = bdrv_pwrite_sync(bs->file, 0, buffer_len, buffer, 0); + 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)); @@ -1554,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); @@ -1571,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; @@ -1620,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 f9e6539ceb..46be65a95f 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -202,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; @@ -219,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; @@ -270,7 +270,11 @@ static void quorum_report_bad_versions(BDRVQuorumState *s, } } -static void coroutine_fn 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; @@ -290,8 +294,8 @@ static void coroutine_fn 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; @@ -389,7 +393,7 @@ static int quorum_compute_hash(QuorumAIOCB *acb, int i, QuorumVoteValue *hash) /* XXX - would be nice if we could pass in the Error ** * and propagate that back, but this quorum code is * restricted to just errno values currently */ - if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, + if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA256, qiov->iov, qiov->niov, &data, &len, NULL) < 0) { @@ -491,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; @@ -571,7 +575,11 @@ free_exit: quorum_free_vote_list(&acb->votes); } -static void coroutine_fn 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; @@ -599,7 +607,7 @@ static void coroutine_fn read_quorum_children_entry(void *opaque) } } -static int coroutine_fn read_quorum_children(QuorumAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK read_quorum_children(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; int i; @@ -640,7 +648,7 @@ static int coroutine_fn read_quorum_children(QuorumAIOCB *acb) return acb->vote_ret; } -static int coroutine_fn read_fifo_child(QuorumAIOCB *acb) +static int coroutine_fn GRAPH_RDLOCK read_fifo_child(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; int n, ret; @@ -661,10 +669,9 @@ static int coroutine_fn read_fifo_child(QuorumAIOCB *acb) return ret; } -static int coroutine_fn 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); @@ -683,7 +690,11 @@ static int coroutine_fn quorum_co_preadv(BlockDriverState *bs, return ret; } -static void coroutine_fn 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; @@ -714,9 +725,9 @@ static void coroutine_fn write_quorum_entry(void *opaque) } } -static int coroutine_fn 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); @@ -745,28 +756,28 @@ static int coroutine_fn quorum_co_pwritev(BlockDriverState *bs, int64_t offset, return ret; } -static int coroutine_fn 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, @@ -1301,7 +1308,7 @@ static BlockDriver bdrv_quorum = { static void bdrv_quorum_init(void) { - if (!qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA256)) { + if (!qcrypto_hash_supports(QCRYPTO_HASH_ALGO_SHA256)) { /* SHA256 hash support is required for quorum device */ return; } diff --git a/block/raw-format.c b/block/raw-format.c index a68014ef0b..e08526e2ec 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; @@ -110,7 +111,7 @@ static int raw_apply_options(BlockDriverState *bs, BDRVRawState *s, if (offset > real_size) { error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " "size of the containing file (%" PRId64 ")", - s->offset, real_size); + offset, real_size); return -EINVAL; } @@ -118,7 +119,7 @@ static int raw_apply_options(BlockDriverState *bs, BDRVRawState *s, error_setg(errp, "The sum of offset (%" PRIu64 ") and size " "(%" PRIu64 ") has to be smaller or equal to the " " actual size of the containing file (%" PRId64 ")", - s->offset, s->size, real_size); + offset, size, real_size); return -EINVAL; } @@ -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; @@ -267,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: @@ -278,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; @@ -291,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; @@ -304,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; @@ -316,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; } @@ -367,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 @@ -382,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; @@ -403,18 +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 coroutine_fn 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) { @@ -423,17 +454,16 @@ static int coroutine_fn raw_co_ioctl(BlockDriverState *bs, 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, @@ -445,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; @@ -462,6 +494,8 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, bdrv_open_child(NULL, options, "file", bs, &child_of_bds, file_role, false, errp); + + GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!bs->file) { return -EINVAL; } @@ -508,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; @@ -525,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) { @@ -534,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; @@ -553,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; @@ -579,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); } @@ -606,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, @@ -617,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 f826410f40..04ed0e242e 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, @@ -356,11 +367,11 @@ static int qemu_rbd_convert_luks_create_options( if (luks_opts->has_cipher_alg) { switch (luks_opts->cipher_alg) { - case QCRYPTO_CIPHER_ALG_AES_128: { + case QCRYPTO_CIPHER_ALGO_AES_128: { *alg = RBD_ENCRYPTION_ALGORITHM_AES128; break; } - case QCRYPTO_CIPHER_ALG_AES_256: { + case QCRYPTO_CIPHER_ALGO_AES_256: { *alg = RBD_ENCRYPTION_ALGORITHM_AES256; break; } @@ -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; } @@ -833,7 +976,7 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, } #ifdef HAVE_RBD_NAMESPACE_EXISTS - if (opts->has_q_namespace && strlen(opts->q_namespace) > 0) { + if (opts->q_namespace && strlen(opts->q_namespace) > 0) { bool exists; r = rbd_namespace_exists(*io_ctx, opts->q_namespace, &exists); @@ -991,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; } @@ -1016,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; } @@ -1056,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", @@ -1138,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; } @@ -1244,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; @@ -1284,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; } @@ -1434,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; @@ -1648,17 +1815,18 @@ static const char *const qemu_rbd_strong_runtime_opts[] = { static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), + .bdrv_parse_filename = qemu_rbd_parse_filename, - .bdrv_file_open = qemu_rbd_open, + .bdrv_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, .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 f1eed25e43..0415a5e8b7 100644 --- a/block/replication.c +++ b/block/replication.c @@ -179,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) @@ -220,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; @@ -244,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; @@ -278,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; } @@ -309,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; @@ -327,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); @@ -362,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. @@ -374,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); @@ -392,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) @@ -435,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; @@ -462,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 || @@ -477,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; } @@ -498,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); @@ -527,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; } @@ -566,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; } @@ -578,30 +571,30 @@ 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, - &perf, + 0, MIRROR_SYNC_MODE_NONE, NULL, 0, false, false, + NULL, &perf, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); 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(); } @@ -612,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) { @@ -632,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) @@ -674,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; @@ -688,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) { @@ -702,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; } @@ -724,29 +697,26 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) * disk, secondary disk in backup_job_completed(). */ if (s->backup_job) { - aio_context_release(aio_context); job_cancel_sync(&s->backup_job->job, true); - aio_context_acquire(aio_context); } 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/reqlist.c b/block/reqlist.c index 08cb57cfa4..098e807378 100644 --- a/block/reqlist.c +++ b/block/reqlist.c @@ -20,8 +20,6 @@ void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset, int64_t bytes) { - assert(!reqlist_find_conflict(reqs, offset, bytes)); - *req = (BlockReq) { .offset = offset, .bytes = bytes, diff --git a/block/snapshot-access.c b/block/snapshot-access.c index 0a30ec6cd9..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); @@ -85,6 +85,9 @@ static int snapshot_access_open(BlockDriverState *bs, QDict *options, int flags, 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 e22ac3eac6..8fd1756777 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -28,7 +28,6 @@ #include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qstring.h" #include "qemu/option.h" #include "sysemu/block-backend.h" @@ -155,11 +154,15 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, * back if the given BDS does not support snapshots. * Return NULL if there is no BDS to (safely) fall back to. */ -static BdrvChild *bdrv_snapshot_fallback_child(BlockDriverState *bs) +static BdrvChild * GRAPH_RDLOCK +bdrv_snapshot_fallback_child(BlockDriverState *bs) { BdrvChild *fallback = bdrv_primary_child(bs); BdrvChild *child; + GLOBAL_STATE_CODE(); + assert_bdrv_graph_readable(); + /* We allow fallback only to primary child */ if (!fallback) { return NULL; @@ -182,16 +185,20 @@ static BdrvChild *bdrv_snapshot_fallback_child(BlockDriverState *bs) return fallback; } -static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs) +static BlockDriverState * GRAPH_RDLOCK +bdrv_snapshot_fallback(BlockDriverState *bs) { + 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; } @@ -254,7 +261,10 @@ int bdrv_snapshot_goto(BlockDriverState *bs, return ret; } + bdrv_graph_rdlock_main_loop(); fallback = bdrv_snapshot_fallback_child(bs); + bdrv_graph_rdunlock_main_loop(); + if (fallback) { QDict *options; QDict *file_options; @@ -281,7 +291,9 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* .bdrv_open() will re-attach it */ + 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); @@ -300,7 +312,10 @@ int bdrv_snapshot_goto(BlockDriverState *bs, * respective option (with the qdict_put_str() call above). * Assert that .bdrv_open() has attached the right BDS as primary child. */ + bdrv_graph_rdlock_main_loop(); assert(bdrv_primary_bs(bs) == fallback_bs); + bdrv_graph_rdunlock_main_loop(); + bdrv_unref(fallback_bs); return ret; } @@ -343,7 +358,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, GLOBAL_STATE_CODE(); if (!drv) { - error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs)); + error_setg(errp, "Device '%s' has no medium", + bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { @@ -372,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; } @@ -416,9 +434,11 @@ 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)); + error_setg(errp, "Device '%s' has no medium", + bdrv_get_device_name(bs)); return -ENOMEDIUM; } if (!snapshot_id && !name) { @@ -460,9 +480,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; @@ -494,8 +514,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; } @@ -505,9 +528,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) @@ -516,6 +537,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; @@ -524,14 +546,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)); @@ -548,10 +567,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; @@ -560,18 +581,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)); @@ -589,29 +607,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; } @@ -629,6 +655,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; @@ -637,15 +664,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; @@ -671,7 +695,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; @@ -680,10 +706,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); @@ -691,7 +715,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)); @@ -713,6 +736,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; @@ -721,13 +745,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 04726d4ecb..b9f33ec739 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" @@ -36,7 +37,6 @@ #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/sockets.h" -#include "qemu/uri.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/qapi-visit-block-core.h" #include "qapi/qmp/qdict.h" @@ -180,65 +180,71 @@ static void sftp_error_trace(BDRVSSHState *s, const char *op) static int parse_uri(const char *filename, QDict *options, Error **errp) { - URI *uri = NULL; - QueryParams *qp; + g_autoptr(GUri) uri = g_uri_parse(filename, G_URI_FLAGS_NONE, NULL); + const char *uri_host, *uri_path, *uri_user, *uri_query; char *port_str; - int i; + int port; + g_autoptr(GError) gerror = NULL; + char *qp_name, *qp_value; + GUriParamsIter qp; - uri = uri_parse(filename); if (!uri) { return -EINVAL; } - if (g_strcmp0(uri->scheme, "ssh") != 0) { + if (g_strcmp0(g_uri_get_scheme(uri), "ssh") != 0) { error_setg(errp, "URI scheme must be 'ssh'"); - goto err; + return -EINVAL; } - if (!uri->server || strcmp(uri->server, "") == 0) { + uri_host = g_uri_get_host(uri); + if (!uri_host || g_str_equal(uri_host, "")) { error_setg(errp, "missing hostname in URI"); - goto err; + return -EINVAL; } - if (!uri->path || strcmp(uri->path, "") == 0) { + uri_path = g_uri_get_path(uri); + if (!uri_path || g_str_equal(uri_path, "")) { error_setg(errp, "missing remote path in URI"); - goto err; + return -EINVAL; } - qp = query_params_parse(uri->query); - if (!qp) { - error_setg(errp, "could not parse query parameters"); - goto err; + uri_user = g_uri_get_user(uri); + if (uri_user && !g_str_equal(uri_user, "")) { + qdict_put_str(options, "user", uri_user); } - if(uri->user && strcmp(uri->user, "") != 0) { - qdict_put_str(options, "user", uri->user); - } + qdict_put_str(options, "server.host", uri_host); - qdict_put_str(options, "server.host", uri->server); - - port_str = g_strdup_printf("%d", uri->port ?: 22); + port = g_uri_get_port(uri); + port_str = g_strdup_printf("%d", port > 0 ? port : 22); qdict_put_str(options, "server.port", port_str); g_free(port_str); - qdict_put_str(options, "path", uri->path); + qdict_put_str(options, "path", uri_path); - /* Pick out any query parameters that we understand, and ignore - * the rest. - */ - for (i = 0; i < qp->n; ++i) { - if (strcmp(qp->p[i].name, "host_key_check") == 0) { - qdict_put_str(options, "host_key_check", qp->p[i].value); + uri_query = g_uri_get_query(uri); + if (uri_query) { + g_uri_params_iter_init(&qp, uri_query, -1, "&", G_URI_PARAMS_NONE); + while (g_uri_params_iter_next(&qp, &qp_name, &qp_value, &gerror)) { + if (!qp_name || !qp_value || gerror) { + warn_report("Failed to parse SSH URI parameters '%s'", + uri_query); + break; + } + /* + * Pick out the query parameters that we understand, and ignore + * (or rather warn about) the rest. + */ + if (g_str_equal(qp_name, "host_key_check")) { + qdict_put_str(options, "host_key_check", qp_value); + } else { + warn_report("Unsupported parameter '%s' in URI", qp_name); + } } } - query_params_free(qp); - uri_free(uri); return 0; - - err: - uri_free(uri); - return -EINVAL; } static bool ssh_has_filename_options_conflict(QDict *options, Error **errp) @@ -358,7 +364,7 @@ static unsigned hex2decimal(char ch) return 10 + (ch - 'A'); } - return -1; + return UINT_MAX; } /* Compare the binary fingerprint (hash of host key) with the @@ -370,13 +376,15 @@ static int compare_fingerprint(const unsigned char *fingerprint, size_t len, unsigned c; while (len > 0) { + unsigned c0, c1; while (*host_key_check == ':') host_key_check++; - if (!qemu_isxdigit(host_key_check[0]) || - !qemu_isxdigit(host_key_check[1])) + c0 = hex2decimal(host_key_check[0]); + c1 = hex2decimal(host_key_check[1]); + if (c0 > 0xf || c1 > 0xf) { return 1; - c = hex2decimal(host_key_check[0]) * 16 + - hex2decimal(host_key_check[1]); + } + c = c0 * 16 + c1; if (c - *fingerprint != 0) return c - *fingerprint; fingerprint++; @@ -468,7 +476,6 @@ static int check_host_key(BDRVSSHState *s, SshHostKeyCheck *hkc, Error **errp) errp); } g_assert_not_reached(); - break; case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS: return check_host_key_knownhosts(s, errp); default: @@ -643,7 +650,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()); @@ -831,8 +838,8 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, return ret; } -static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, - Error **errp) +static int ssh_open(BlockDriverState *bs, QDict *options, int bdrv_flags, + Error **errp) { BDRVSSHState *s = bs->opaque; BlockdevOptionsSsh *opts; @@ -859,9 +866,6 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags, goto err; } - /* Go non-blocking. */ - ssh_set_blocking(s->session, 0); - if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) { bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; } @@ -1018,7 +1022,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 +1052,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); } @@ -1252,7 +1256,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; @@ -1356,14 +1360,14 @@ static BlockDriver bdrv_ssh = { .protocol_name = "ssh", .instance_size = sizeof(BDRVSSHState), .bdrv_parse_filename = ssh_parse_filename, - .bdrv_file_open = ssh_file_open, + .bdrv_open = ssh_open, .bdrv_co_create = ssh_co_create, .bdrv_co_create_opts = ssh_co_create_opts, .bdrv_close = ssh_close, .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..9076203193 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,53 +155,59 @@ 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); - int64_t len; + BlockDriverState *unfiltered_bs = NULL; + int64_t len = -1; 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); for ( ; offset < len; offset += n) { bool copy; - int ret; + int ret = -1; /* 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 131eba3ab4..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" @@ -82,6 +84,9 @@ static int throttle_open(BlockDriverState *bs, QDict *options, 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 | @@ -104,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 coroutine_fn throttle_co_flush(BlockDriverState *bs) +static int coroutine_fn GRAPH_RDLOCK throttle_co_flush(BlockDriverState *bs) { return bdrv_co_flush(bs->file->bs); } @@ -214,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) { @@ -222,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); @@ -245,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, @@ -261,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 48dbf10c66..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,8 +166,9 @@ 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" @@ -209,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 c0c111c4b9..26f7638f1f 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -3,10 +3,12 @@ * * Copyright (c) 2009, 2012 Stefan Weil * + * SPDX-License-Identifier: GPL-2.0-or-later + * * 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) version 3 or any later version. + * (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 @@ -85,7 +87,7 @@ /* Command line option for static images. */ #define BLOCK_OPT_STATIC "static" -#define SECTOR_SIZE 512 +#define SECTOR_SIZE 512ULL #define DEFAULT_CLUSTER_SIZE 1048576 /* Note: can't use 1 * MiB, because it's passed to stringify() */ @@ -239,7 +241,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 +329,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"); @@ -382,6 +385,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + logout("\n"); ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0); @@ -437,7 +442,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } else if (header.sector_size != SECTOR_SIZE) { error_setg(errp, "unsupported VDI image (sector size %" PRIu32 - " is not %u)", header.sector_size, SECTOR_SIZE); + " is not %llu)", header.sector_size, SECTOR_SIZE); ret = -ENOTSUP; goto fail; } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { @@ -494,9 +499,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; } @@ -517,11 +522,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; @@ -543,7 +547,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) { @@ -599,7 +603,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) { @@ -633,7 +637,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)) { @@ -699,7 +702,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); @@ -722,20 +725,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_co_pwrite(bs->file, offset * SECTOR_SIZE, + ret = bdrv_co_pwrite(bs->file, bmap_offset * SECTOR_SIZE, n_sectors * SECTOR_SIZE, base, 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 +804,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; @@ -885,22 +890,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 +938,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 +979,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 +989,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 +1052,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 0866897a85..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; @@ -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; @@ -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, VHDX_LOG_SECTOR_SIZE, buffer_tmp, - 0); + 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; @@ -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, VHDX_LOG_SECTOR_SIZE, - merged_sector, 0); + ret = bdrv_co_pread(bs->file, file_offset, VHDX_LOG_SECTOR_SIZE, + merged_sector, 0); if (ret < 0) { goto exit; } @@ -980,9 +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 + trailing_length, - VHDX_LOG_SECTOR_SIZE - trailing_length, - merged_sector + trailing_length, 0); + 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; } @@ -1035,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, @@ -1046,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; } @@ -1058,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 bad9ca691b..5aa1a13506 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -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; @@ -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; @@ -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; @@ -885,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); @@ -985,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); @@ -1001,11 +1004,15 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, uint64_t signature; Error *local_err = NULL; + 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; @@ -1077,7 +1084,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, 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]); } @@ -1093,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; } @@ -1161,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; @@ -1171,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; @@ -1248,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; } @@ -1269,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); } /* @@ -1323,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; @@ -1504,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; @@ -1567,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; @@ -1663,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, VHDX_HEADER_BLOCK_SIZE, buffer, 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), - VHDX_METADATA_ENTRY_BUFFER_SIZE, entry_buffer, 0); + ret = blk_co_pwrite(blk, metadata_offset + (64 * KiB), + VHDX_METADATA_ENTRY_BUFFER_SIZE, entry_buffer, 0); if (ret < 0) { goto exit; } @@ -1689,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; @@ -1701,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); @@ -1713,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; } @@ -1730,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) { @@ -1754,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, length, s->bat, 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; @@ -1775,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; @@ -1858,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, VHDX_HEADER_BLOCK_SIZE, - buffer, 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, VHDX_HEADER_BLOCK_SIZE, - buffer, 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; @@ -1895,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; @@ -1990,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; @@ -2051,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; @@ -2084,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; @@ -2143,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; } @@ -2155,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; @@ -2170,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; @@ -2245,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 26376352b9..78f6433607 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -28,7 +28,6 @@ #include "block/block_int.h" #include "sysemu/block-backend.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/option.h" @@ -272,6 +271,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 +282,8 @@ static void vmdk_free_extents(BlockDriverState *bs) bdrv_unref_child(bs, e->file); } } + bdrv_graph_wrunlock(); + g_free(s->extents); } @@ -297,7 +299,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; @@ -339,36 +342,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_SIZE, desc, 0); + 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_SIZE, desc, 0); + ret = bdrv_co_pwrite_sync(bs->file, s->desc_offset, desc_buf_size, desc, 0); out: g_free(desc); @@ -376,7 +392,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 +427,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 +466,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 +483,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; @@ -574,8 +596,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; @@ -637,9 +659,9 @@ 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; @@ -793,9 +815,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; @@ -909,9 +931,9 @@ static char *vmdk_read_desc(BdrvChild *file, uint64_t desc_offset, Error **errp) 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; @@ -1091,8 +1113,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; @@ -1119,9 +1142,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]; @@ -1139,6 +1164,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: * @@ -1206,7 +1233,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; @@ -1219,7 +1246,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; @@ -1234,20 +1265,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; } @@ -1271,8 +1314,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]; @@ -1308,6 +1352,8 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, BDRVVmdkState *s = bs->opaque; uint32_t magic; + GRAPH_RDLOCK_GUARD_MAINLOOP(); + ret = bdrv_open_file_child(NULL, options, "file", bs, errp); if (ret < 0) { return ret; @@ -1359,9 +1405,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; } @@ -1403,13 +1448,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 coroutine_fn 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; @@ -1439,7 +1482,7 @@ static int coroutine_fn 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); + BLKDBG_CO_EVENT(extent->file, BLKDBG_COW_READ); ret = bdrv_co_pread(bs->backing, offset, skip_start_bytes, whole_grain, 0); if (ret < 0) { @@ -1447,7 +1490,7 @@ static int coroutine_fn get_whole_cluster(BlockDriverState *bs, goto exit; } } - BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE); + 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) { @@ -1459,7 +1502,7 @@ static int coroutine_fn 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); + 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); @@ -1468,7 +1511,7 @@ static int coroutine_fn get_whole_cluster(BlockDriverState *bs, goto exit; } } - BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE); + 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); @@ -1484,12 +1527,12 @@ exit: return ret; } -static int coroutine_fn 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); + 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)), @@ -1536,14 +1579,11 @@ static int coroutine_fn vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, * VMDK_UNALLOC if cluster is not mapped and @allocate is false. * VMDK_ERROR if failed. */ -static int coroutine_fn 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; @@ -1622,7 +1662,7 @@ static int coroutine_fn get_cluster_offset(BlockDriverState *bs, } } l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes); - BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD); + BLKDBG_CO_EVENT(extent->file, BLKDBG_L2_LOAD); if (bdrv_co_pread(extent->file, (int64_t)l2_offset * 512, l2_size_bytes, @@ -1736,11 +1776,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; @@ -1775,6 +1814,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; @@ -1785,7 +1826,7 @@ static int coroutine_fn vmdk_co_block_status(BlockDriverState *bs, return ret; } -static int coroutine_fn +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, @@ -1834,12 +1875,12 @@ 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; @@ -1867,10 +1908,9 @@ vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, return ret; } -static int coroutine_fn +static int coroutine_fn GRAPH_RDLOCK vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset, - int64_t offset_in_cluster, QEMUIOVector *qiov, - int bytes) + int64_t offset_in_cluster, QEMUIOVector *qiov, int bytes) { int ret; int cluster_bytes, buf_bytes; @@ -1882,7 +1922,7 @@ 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); @@ -1896,7 +1936,7 @@ 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); + BLKDBG_CO_EVENT(extent->file, BLKDBG_READ_COMPRESSED); ret = bdrv_co_pread(extent->file, cluster_offset, buf_bytes, cluster_buf, 0); if (ret < 0) { @@ -1934,7 +1974,7 @@ 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) { @@ -1974,7 +2014,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) { @@ -2016,9 +2056,9 @@ fail: * * Returns: error code with 0 for success. */ -static int coroutine_fn 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; @@ -2114,7 +2154,7 @@ static int coroutine_fn 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) { @@ -2126,7 +2166,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) { @@ -2138,7 +2178,7 @@ 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; } @@ -2154,10 +2194,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; @@ -2173,10 +2212,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; @@ -2185,7 +2223,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); @@ -2237,19 +2275,19 @@ static int vmdk_init_extent(BlockBackend *blk, header.check_bytes[3] = 0xa; /* write all the data */ - ret = blk_pwrite(blk, 0, sizeof(magic), &magic, 0); + ret = blk_co_pwrite(blk, 0, sizeof(magic), &magic, 0); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, -ret, "failed to write VMDK magic"); goto exit; } - ret = blk_pwrite(blk, sizeof(magic), sizeof(header), &header, 0); + ret = blk_co_pwrite(blk, sizeof(magic), sizeof(header), &header, 0); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, -ret, "failed to write VMDK header"); 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; } @@ -2261,10 +2299,10 @@ 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_size, gd_buf, 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); + error_setg_errno(errp, -ret, "failed to write VMDK grain directory"); goto exit; } @@ -2273,10 +2311,11 @@ 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_size, gd_buf, 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); + error_setg_errno(errp, -ret, + "failed to write VMDK backup grain directory"); } ret = 0; @@ -2285,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; @@ -2314,7 +2353,7 @@ exit: if (pbb) { *pbb = blk; } else { - blk_unref(blk); + blk_co_unref(blk); blk = NULL; } } @@ -2366,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, @@ -2386,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; @@ -2517,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; @@ -2527,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; @@ -2553,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; @@ -2601,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); @@ -2616,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; @@ -2652,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; @@ -2768,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; @@ -2779,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; @@ -2794,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; @@ -2829,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) @@ -2856,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; } @@ -2875,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; } @@ -2884,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; @@ -2901,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, @@ -2919,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; @@ -2956,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 %" @@ -2979,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, @@ -3015,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; @@ -3034,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", @@ -3128,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 95841f259a..d95a204612 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -238,6 +238,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, 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; @@ -449,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; } @@ -486,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; @@ -510,13 +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, s->bitmap_size, bitmap, - 0); + 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, sizeof(s->footer), &s->footer, 0); + 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, - s->bitmap_size, bitmap, 0); + 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, 4, &bat_value, 0); + 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, sizeof(*footer), 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, sizeof(*footer), 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, 512, bat_sector, 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, sizeof(dyndisk_header), &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), sizeof(*footer), - 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 723c91216e..8ffe8b3b9b 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -27,6 +27,7 @@ #include #include #include "qapi/error.h" +#include "block/block-io.h" #include "block/block_int.h" #include "block/qdict.h" #include "qemu/module.h" @@ -776,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,".."); @@ -856,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)); } @@ -1052,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", @@ -1144,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 @@ -1266,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; } } @@ -1368,8 +1369,9 @@ static int open_file(BDRVVVFATState* s,mapping_t* mapping) return -1; vvfat_close_current_file(s); s->current_fd = fd; - s->current_mapping = mapping; } + + s->current_mapping = mapping; return 0; } @@ -1407,7 +1409,9 @@ read_cluster_directory: assert(s->current_fd); - offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset; + offset = s->cluster_size * + ((cluster_num - s->current_mapping->begin) + + s->current_mapping->info.file.offset); if(lseek(s->current_fd, offset, SEEK_SET)!=offset) return -3; s->cluster=s->cluster_buffer; @@ -1468,8 +1472,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; @@ -1480,8 +1484,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; } @@ -1489,8 +1493,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, n, - buf + i * 0x200, 0) < 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; @@ -1531,7 +1535,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) { @@ -1795,8 +1799,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; @@ -1806,10 +1810,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); } /* @@ -1851,8 +1855,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: @@ -1877,7 +1881,6 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, uint32_t cluster_num = begin_of_direntry(direntry); uint32_t offset = 0; - int first_mapping_index = -1; mapping_t* mapping = NULL; const char* basename2 = NULL; @@ -1928,8 +1931,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, (mapping->mode & MODE_DIRECTORY) == 0) { /* was modified in qcow */ - if (offset != mapping->info.file.offset + s->cluster_size - * (cluster_num - mapping->begin)) { + if (offset != s->cluster_size + * ((cluster_num - mapping->begin) + + mapping->info.file.offset)) { /* offset of this cluster in file chain has changed */ abort(); copy_it = 1; @@ -1938,14 +1942,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s, if (strcmp(basename, basename2)) copy_it = 1; - first_mapping_index = array_index(&(s->mapping), mapping); - } - - if (mapping->first_mapping_index != first_mapping_index - && mapping->info.file.offset > 0) { - abort(); - copy_it = 1; } + assert(mapping->first_mapping_index == -1 + || mapping->info.file.offset > 0); /* need to write out? */ if (!was_modified && is_file(direntry)) { @@ -1961,26 +1960,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, - BDRV_SECTOR_SIZE, s->cluster_buffer, - 0); + res = bdrv_co_pwrite(s->qcow, offs * BDRV_SECTOR_SIZE, + BDRV_SECTOR_SIZE, s->cluster_buffer, + 0); if (res < 0) { return -2; } @@ -2010,8 +2009,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); @@ -2137,7 +2136,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; @@ -2402,7 +2402,7 @@ static int commit_mappings(BDRVVVFATState* s, (mapping->end - mapping->begin); } else next_mapping->info.file.offset = mapping->info.file.offset + - mapping->end - mapping->begin; + (mapping->end - mapping->begin); mapping = next_mapping; } @@ -2413,8 +2413,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); @@ -2465,8 +2465,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; @@ -2503,8 +2504,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); @@ -2522,8 +2523,9 @@ static int commit_one_file(BDRVVVFATState* s, return -1; } - for (i = s->cluster_size; i < offset; i += s->cluster_size) + for (i = 0; i < offset; i += s->cluster_size) { c = modified_fat_get(s, c); + } fd = qemu_open_old(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666); if (fd < 0) { @@ -2688,12 +2690,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; @@ -2714,8 +2716,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); } } @@ -2769,7 +2771,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; @@ -2783,13 +2785,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); @@ -2805,16 +2804,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; } @@ -2834,8 +2833,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; } @@ -2915,7 +2915,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; @@ -2965,7 +2965,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()); @@ -2974,8 +2974,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; @@ -3084,8 +3085,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, - nb_sectors * BDRV_SECTOR_SIZE, buf, 0); + 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; @@ -3105,7 +3106,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) { @@ -3236,8 +3237,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); } } @@ -3257,7 +3257,7 @@ static BlockDriver bdrv_vvfat = { .instance_size = sizeof(BDRVVVFATState), .bdrv_parse_filename = vvfat_parse_filename, - .bdrv_file_open = vvfat_open, + .bdrv_open = vvfat_open, .bdrv_refresh_limits = vvfat_refresh_limits, .bdrv_close = vvfat_close, .bdrv_child_perm = vvfat_child_perm, 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..b36f41b7c5 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -21,12 +21,18 @@ #include "io/channel-socket.h" #include "io/net-listener.h" +typedef struct NBDConn { + QIOChannelSocket *cioc; + QLIST_ENTRY(NBDConn) next; +} NBDConn; + typedef struct NBDServerData { QIONetListener *listener; QCryptoTLSCreds *tlscreds; char *tlsauthz; uint32_t max_connections; uint32_t connections; + QLIST_HEAD(, NBDConn) conns; } NBDServerData; static NBDServerData *nbd_server; @@ -51,6 +57,14 @@ int nbd_server_max_connections(void) static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) { + NBDConn *conn = nbd_client_owner(client); + + assert(qemu_in_main_thread() && nbd_server); + + object_unref(OBJECT(conn->cioc)); + QLIST_REMOVE(conn, next); + g_free(conn); + nbd_client_put(client); assert(nbd_server->connections > 0); nbd_server->connections--; @@ -60,31 +74,56 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored) static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, gpointer opaque) { + NBDConn *conn = g_new0(NBDConn, 1); + + assert(qemu_in_main_thread() && nbd_server); nbd_server->connections++; + object_ref(OBJECT(cioc)); + conn->cioc = cioc; + QLIST_INSERT_HEAD(&nbd_server->conns, conn, next); nbd_update_server_watch(nbd_server); qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); - nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz, - nbd_blockdev_client_closed); + /* TODO - expose handshake timeout as QMP option */ + nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + nbd_server->tlscreds, nbd_server->tlsauthz, + nbd_blockdev_client_closed, conn); } static void nbd_update_server_watch(NBDServerData *s) { - if (!s->max_connections || s->connections < s->max_connections) { - qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, NULL); - } else { - qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + if (s->listener) { + if (!s->max_connections || s->connections < s->max_connections) { + qio_net_listener_set_client_func(s->listener, nbd_accept, NULL, + NULL); + } else { + qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL); + } } } static void nbd_server_free(NBDServerData *server) { + NBDConn *conn, *tmp; + if (!server) { return; } + /* + * Forcefully close the listener socket, and any clients that have + * not yet disconnected on their own. + */ qio_net_listener_disconnect(server->listener); object_unref(OBJECT(server->listener)); + server->listener = NULL; + QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) { + qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); + } + + AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0); + if (server->tlscreds) { object_unref(OBJECT(server->tlscreds)); } @@ -168,18 +207,26 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, void nbd_server_start_options(NbdServerOptions *arg, Error **errp) { + if (!arg->has_max_connections) { + arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS; + } + nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz, arg->max_connections, 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) { SocketAddress *addr_flat = socket_address_flatten(addr); + if (!has_max_connections) { + max_connections = NBD_DEFAULT_MAX_CONNECTIONS; + } + nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp); qapi_free_SocketAddress(addr_flat); } @@ -200,8 +247,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 +261,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 0f0529d330..3aec417730 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" @@ -254,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)) { @@ -340,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 { @@ -684,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); } } @@ -979,6 +976,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"); @@ -1043,6 +1049,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; @@ -1055,7 +1063,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; @@ -1067,26 +1075,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, @@ -1128,64 +1130,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); @@ -1200,85 +1190,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; @@ -1287,57 +1216,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 */ @@ -1345,12 +1263,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 */ @@ -1371,32 +1289,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 " @@ -1404,38 +1317,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; @@ -1448,12 +1360,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) { @@ -1469,8 +1383,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; } @@ -1480,54 +1394,50 @@ 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, "Device '%s' 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; + ret = bdrv_flush(state->old_bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Write to node '%s' failed", + bdrv_get_device_or_node_name(state->old_bs)); + 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; @@ -1540,16 +1450,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; } } @@ -1562,9 +1474,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; } /* @@ -1575,42 +1488,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 @@ -1618,14 +1523,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; @@ -1633,7 +1535,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 */ @@ -1646,46 +1547,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_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; @@ -1696,15 +1586,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; @@ -1712,8 +1612,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; @@ -1730,20 +1631,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; @@ -1769,63 +1671,58 @@ 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; + return; } - /* Honor bdrv_try_change_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_change_aio_context(target_bs, aio_context, NULL, errp); if (ret < 0) { bdrv_unref(target_bs); - aio_context_release(old_context); return; } - aio_context_release(old_context); - aio_context_acquire(aio_context); - if (set_backing_hd) { if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { goto unref; @@ -1834,72 +1731,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) { job_cancel_sync(&state->job->job, true); } } -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) { @@ -1913,17 +1803,12 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) /* 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_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() */ @@ -1931,73 +1816,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) { job_cancel_sync(&state->job->job, true); } } -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, @@ -2006,39 +1876,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, @@ -2054,36 +1922,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, @@ -2100,28 +1967,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, @@ -2138,46 +2005,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); @@ -2187,10 +2056,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); @@ -2198,210 +2066,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); } @@ -2430,8 +2244,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; @@ -2439,9 +2252,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; @@ -2452,38 +2263,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_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) @@ -2495,24 +2306,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; } @@ -2523,49 +2340,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); } @@ -2573,21 +2391,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) { @@ -2597,29 +2416,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) @@ -2632,21 +2455,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 @@ -2667,29 +2493,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 @@ -2701,35 +2526,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; } } @@ -2739,14 +2564,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; } /* @@ -2761,7 +2586,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"); @@ -2769,9 +2594,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 @@ -2785,19 +2610,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 */ @@ -2821,9 +2644,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; } @@ -2844,12 +2664,15 @@ static BlockJob *do_backup_common(BackupCommon *backup, if (backup->x_perf->has_max_chunk) { perf.max_chunk = backup->x_perf->max_chunk; } + if (backup->x_perf->has_min_cluster_size) { + perf.min_cluster_size = backup->x_perf->min_cluster_size; + } } 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; @@ -2870,7 +2693,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); @@ -2903,7 +2726,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; } @@ -2917,7 +2740,7 @@ static BlockJob *do_backup_common(BackupCommon *backup, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, bmap, backup->bitmap_mode, - backup->compress, + backup->compress, backup->discard_source, backup->filter_node_name, &perf, backup->on_source_error, @@ -2946,6 +2769,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); } @@ -2963,7 +2788,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, @@ -2975,7 +2800,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, @@ -2985,6 +2809,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; } @@ -3003,9 +2830,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; } @@ -3038,18 +2862,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); @@ -3063,10 +2885,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, @@ -3084,7 +2903,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); @@ -3095,7 +2914,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; @@ -3111,18 +2929,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); } @@ -3135,18 +2954,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; } } @@ -3167,16 +2987,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, @@ -3189,11 +3014,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) { @@ -3205,31 +3030,24 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) */ target_bs = bdrv_open(arg->target, NULL, options, flags, errp); if (!target_bs) { - goto out; + return; } + 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(); - /* Honor bdrv_try_change_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_change_aio_context(target_bs, aio_context, NULL, errp); if (ret < 0) { bdrv_unref(target_bs); - aio_context_release(old_context); return; } - aio_context_release(old_context); - aio_context_acquire(aio_context); - - blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs, - arg->has_replaces, arg->replaces, arg->sync, + 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, @@ -3237,19 +3055,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, @@ -3258,7 +3074,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, @@ -3268,7 +3083,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; @@ -3285,35 +3099,25 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, zero_target = (sync == MIRROR_SYNC_MODE_FULL); - /* Honor bdrv_try_change_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_change_aio_context(target_bs, aio_context, NULL, errp); - - aio_context_release(old_context); - aio_context_acquire(aio_context); - 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); } /* @@ -3456,13 +3260,26 @@ void qmp_block_job_dismiss(const char *id, Error **errp) 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, const char *image_node_name, const char *backing_file, Error **errp) { BlockDriverState *bs = NULL; - AioContext *aio_context; BlockDriverState *image_bs = NULL; Error *local_err = NULL; bool ro; @@ -3473,45 +3290,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; } } @@ -3529,9 +3346,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) @@ -3566,20 +3384,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; } @@ -3601,14 +3416,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 */ @@ -3617,23 +3425,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) { @@ -3644,34 +3443,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; @@ -3684,45 +3478,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) @@ -3753,10 +3551,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); @@ -3783,12 +3582,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_change_aio_context(bs, new_context, NULL, errp); - - aio_context_release(old_context); } QemuOptsList qemu_common_drive_opts = { diff --git a/blockjob.c b/blockjob.c index f51d4e18f3..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,7 +33,6 @@ #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" @@ -120,7 +120,7 @@ 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); @@ -198,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; @@ -209,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) @@ -230,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; } @@ -319,16 +312,55 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) return block_job_set_speed_locked(job, speed, errp); } -int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) +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(); - return ratelimit_calculate_delay(&job->limit, n); + 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(); @@ -341,7 +373,7 @@ BlockJobInfo *block_job_query_locked(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 = job->job.busy; info->paused = job->job.pause_count > 0; @@ -354,11 +386,15 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) 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; } @@ -414,7 +450,6 @@ static void block_job_event_completed_locked(Notifier *n, void *opaque) progress_total, progress_current, job->speed, - !!msg, msg); } @@ -461,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); } @@ -468,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; } @@ -507,9 +545,11 @@ 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; } diff --git a/bsd-user/aarch64/signal.c b/bsd-user/aarch64/signal.c new file mode 100644 index 0000000000..6bc73a798f --- /dev/null +++ b/bsd-user/aarch64/signal.c @@ -0,0 +1,137 @@ +/* + * ARM AArch64 specific signal definitions for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#include "qemu/osdep.h" + +#include "qemu.h" + +/* + * Compare to sendsig() in sys/arm64/arm64/exec_machdep.c + * Assumes that target stack frame memory is locked. + */ +abi_long set_sigtramp_args(CPUARMState *regs, int sig, + struct target_sigframe *frame, + abi_ulong frame_addr, + struct target_sigaction *ka) +{ + /* + * Arguments to signal handler: + * x0 = signal number + * x1 = siginfo pointer + * x2 = ucontext pointer + * pc/elr = signal handler pointer + * sp = sigframe struct pointer + * lr = sigtramp at base of user stack + */ + + regs->xregs[0] = sig; + regs->xregs[1] = frame_addr + + offsetof(struct target_sigframe, sf_si); + regs->xregs[2] = frame_addr + + offsetof(struct target_sigframe, sf_uc); + + regs->pc = ka->_sa_handler; + regs->xregs[TARGET_REG_SP] = frame_addr; + regs->xregs[TARGET_REG_LR] = TARGET_PS_STRINGS - TARGET_SZSIGCODE; + + return 0; +} + +/* + * Compare to get_mcontext() in arm64/arm64/machdep.c + * Assumes that the memory is locked if mcp points to user memory. + */ +abi_long get_mcontext(CPUARMState *regs, target_mcontext_t *mcp, int flags) +{ + int err = 0, i; + uint64_t *gr = mcp->mc_gpregs.gp_x; + + mcp->mc_gpregs.gp_spsr = pstate_read(regs); + if (flags & TARGET_MC_GET_CLEAR_RET) { + gr[0] = 0UL; + mcp->mc_gpregs.gp_spsr &= ~CPSR_C; + } else { + gr[0] = tswap64(regs->xregs[0]); + } + + for (i = 1; i < 30; i++) { + gr[i] = tswap64(regs->xregs[i]); + } + + mcp->mc_gpregs.gp_sp = tswap64(regs->xregs[TARGET_REG_SP]); + mcp->mc_gpregs.gp_lr = tswap64(regs->xregs[TARGET_REG_LR]); + mcp->mc_gpregs.gp_elr = tswap64(regs->pc); + + /* XXX FP? */ + + return err; +} + +/* + * Compare to arm64/arm64/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUARMState *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags) +{ + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + get_mcontext(env, mcp, flags); + return 0; +} + +/* + * Compare to set_mcontext() in arm64/arm64/machdep.c + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long set_mcontext(CPUARMState *regs, target_mcontext_t *mcp, int srflag) +{ + int err = 0, i; + const uint64_t *gr = mcp->mc_gpregs.gp_x; + + for (i = 0; i < 30; i++) { + regs->xregs[i] = tswap64(gr[i]); + } + + regs->xregs[TARGET_REG_SP] = tswap64(mcp->mc_gpregs.gp_sp); + regs->xregs[TARGET_REG_LR] = tswap64(mcp->mc_gpregs.gp_lr); + regs->pc = mcp->mc_gpregs.gp_elr; + pstate_write(regs, mcp->mc_gpregs.gp_spsr); + + /* XXX FP? */ + + return err; +} + +/* Compare to sys_sigreturn() in arm64/arm64/machdep.c */ +abi_long get_ucontext_sigreturn(CPUARMState *regs, abi_ulong target_sf, + abi_ulong *target_uc) +{ + uint32_t pstate = pstate_read(regs); + + *target_uc = 0; + + if ((pstate & PSTATE_M) != PSTATE_MODE_EL0t || + (pstate & (PSTATE_F | PSTATE_I | PSTATE_A | PSTATE_D)) != 0) { + return -TARGET_EINVAL; + } + + *target_uc = target_sf; + + return 0; +} diff --git a/bsd-user/aarch64/target.h b/bsd-user/aarch64/target.h new file mode 100644 index 0000000000..702aeb7fc5 --- /dev/null +++ b/bsd-user/aarch64/target.h @@ -0,0 +1,20 @@ +/* + * Aarch64 general target stuff that's common to all aarch details + * + * Copyright (c) 2022 M. Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_H +#define TARGET_H + +/* + * aaarch64 ABI does not 'lump' the registers for 64-bit args. + */ +static inline bool regpairs_aligned(void *cpu_env) +{ + return false; +} + +#endif /* TARGET_H */ diff --git a/bsd-user/aarch64/target_arch.h b/bsd-user/aarch64/target_arch.h new file mode 100644 index 0000000000..4815a56ae3 --- /dev/null +++ b/bsd-user/aarch64/target_arch.h @@ -0,0 +1,29 @@ +/* + * ARM AArch64 specific prototypes for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 TARGET_ARCH_H +#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); + +#endif /* TARGET_ARCH_H */ diff --git a/bsd-user/aarch64/target_arch_cpu.c b/bsd-user/aarch64/target_arch_cpu.c new file mode 100644 index 0000000000..b2fa59efaf --- /dev/null +++ b/bsd-user/aarch64/target_arch_cpu.c @@ -0,0 +1,31 @@ +/* + * ARM AArch64 specific CPU for bsd-user + * + * Copyright (c) 2015 Stacey Son + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#include "qemu/osdep.h" +#include "target_arch.h" + +/* See cpu_set_user_tls() in arm64/arm64/vm_machdep.c */ +void target_cpu_set_tls(CPUARMState *env, target_ulong newtls) +{ + env->cp15.tpidr_el[0] = newtls; +} + +target_ulong target_cpu_get_tls(CPUARMState *env) +{ + return env->cp15.tpidr_el[0]; +} diff --git a/bsd-user/aarch64/target_arch_cpu.h b/bsd-user/aarch64/target_arch_cpu.h new file mode 100644 index 0000000000..b288e0d069 --- /dev/null +++ b/bsd-user/aarch64/target_arch_cpu.h @@ -0,0 +1,189 @@ +/* + * ARM AArch64 cpu init and loop + * + * Copyright (c) 2015 Stacey Son + * + * 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 TARGET_ARCH_CPU_H +#define TARGET_ARCH_CPU_H + +#include "target_arch.h" +#include "signal-common.h" +#include "target/arm/syndrome.h" + +#define TARGET_DEFAULT_CPU_MODEL "any" + +static inline void target_cpu_init(CPUARMState *env, + struct target_pt_regs *regs) +{ + int i; + + if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { + fprintf(stderr, "The selected ARM CPU does not support 64 bit mode\n"); + exit(1); + } + for (i = 0; i < 31; i++) { + env->xregs[i] = regs->regs[i]; + } + env->pc = regs->pc; + env->xregs[31] = regs->sp; +} + + +static inline void target_cpu_loop(CPUARMState *env) +{ + CPUState *cs = env_cpu(env); + int trapnr, ec, fsc, si_code, si_signo; + uint64_t code, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; + 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_SWI: + /* See arm64/arm64/trap.c cpu_fetch_syscall_args() */ + code = env->xregs[8]; + if (code == TARGET_FREEBSD_NR_syscall || + code == TARGET_FREEBSD_NR___syscall) { + code = env->xregs[0]; + arg1 = env->xregs[1]; + arg2 = env->xregs[2]; + arg3 = env->xregs[3]; + arg4 = env->xregs[4]; + arg5 = env->xregs[5]; + arg6 = env->xregs[6]; + arg7 = env->xregs[7]; + arg8 = 0; + } else { + arg1 = env->xregs[0]; + arg2 = env->xregs[1]; + arg3 = env->xregs[2]; + arg4 = env->xregs[3]; + arg5 = env->xregs[4]; + arg6 = env->xregs[5]; + arg7 = env->xregs[6]; + arg8 = env->xregs[7]; + } + ret = do_freebsd_syscall(env, code, arg1, arg2, arg3, + arg4, arg5, arg6, arg7, arg8); + /* + * The carry bit is cleared for no error; set for error. + * See arm64/arm64/vm_machdep.c cpu_set_syscall_retval() + */ + if (ret >= 0) { + env->CF = 0; + env->xregs[0] = ret; + } else if (ret == -TARGET_ERESTART) { + env->pc -= 4; + break; + } else if (ret != -TARGET_EJUSTRETURN) { + env->CF = 1; + env->xregs[0] = -ret; + } + break; + + case EXCP_INTERRUPT: + /* Just indicate that signals should be handle ASAP. */ + break; + + case EXCP_UDEF: + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->pc); + break; + + + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + /* We should only arrive here with EC in {DATAABORT, INSNABORT}. */ + ec = syn_get_ec(env->exception.syndrome); + assert(ec == EC_DATAABORT || ec == EC_INSNABORT); + + /* Both EC have the same format for FSC, or close enough. */ + fsc = extract32(env->exception.syndrome, 0, 6); + switch (fsc) { + case 0x04 ... 0x07: /* Translation fault, level {0-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ + case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x11: /* Synchronous Tag Check Fault */ + si_signo = TARGET_SIGSEGV; + si_code = /* TARGET_SEGV_MTESERR; */ TARGET_SEGV_ACCERR; + break; + case 0x21: /* Alignment fault */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + default: + g_assert_not_reached(); + } + force_sig_fault(si_signo, si_code, env->exception.vaddress); + break; + + case EXCP_DEBUG: + case EXCP_BKPT: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + break; + + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + + case EXCP_YIELD: + /* nothing to do here for user-mode, just resume guest code */ + break; + default: + fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", + trapnr); + cpu_dump_state(cs, stderr, 0); + abort(); + } /* switch() */ + process_pending_signals(env); + /* + * Exception return on AArch64 always clears the exclusive + * monitor, so any return to running guest code implies this. + * A strex (successful or otherwise) also clears the monitor, so + * we don't need to specialcase EXCP_STREX. + */ + env->exclusive_addr = -1; + } /* for (;;) */ +} + + +/* See arm64/arm64/vm_machdep.c cpu_fork() */ +static inline void target_cpu_clone_regs(CPUARMState *env, target_ulong newsp) +{ + if (newsp) { + env->xregs[31] = newsp; + } + env->regs[0] = 0; + env->regs[1] = 0; + pstate_write(env, 0); +} + +static inline void target_cpu_reset(CPUArchState *env) +{ +} + + +#endif /* TARGET_ARCH_CPU_H */ diff --git a/bsd-user/aarch64/target_arch_elf.h b/bsd-user/aarch64/target_arch_elf.h new file mode 100644 index 0000000000..cc87f475b3 --- /dev/null +++ b/bsd-user/aarch64/target_arch_elf.h @@ -0,0 +1,163 @@ +/* + * ARM AArch64 ELF definitions for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 TARGET_ARCH_ELF_H +#define TARGET_ARCH_ELF_H + +#define ELF_START_MMAP 0x80000000 +#define ELF_ET_DYN_LOAD_ADDR 0x100000 + +#define elf_check_arch(x) ((x) == EM_AARCH64) + +#define ELF_CLASS ELFCLASS64 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_AARCH64 + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +enum { + ARM_HWCAP_A64_FP = 1 << 0, + ARM_HWCAP_A64_ASIMD = 1 << 1, + ARM_HWCAP_A64_EVTSTRM = 1 << 2, + ARM_HWCAP_A64_AES = 1 << 3, + ARM_HWCAP_A64_PMULL = 1 << 4, + ARM_HWCAP_A64_SHA1 = 1 << 5, + ARM_HWCAP_A64_SHA2 = 1 << 6, + ARM_HWCAP_A64_CRC32 = 1 << 7, + ARM_HWCAP_A64_ATOMICS = 1 << 8, + ARM_HWCAP_A64_FPHP = 1 << 9, + ARM_HWCAP_A64_ASIMDHP = 1 << 10, + ARM_HWCAP_A64_CPUID = 1 << 11, + ARM_HWCAP_A64_ASIMDRDM = 1 << 12, + ARM_HWCAP_A64_JSCVT = 1 << 13, + ARM_HWCAP_A64_FCMA = 1 << 14, + ARM_HWCAP_A64_LRCPC = 1 << 15, + ARM_HWCAP_A64_DCPOP = 1 << 16, + ARM_HWCAP_A64_SHA3 = 1 << 17, + ARM_HWCAP_A64_SM3 = 1 << 18, + ARM_HWCAP_A64_SM4 = 1 << 19, + ARM_HWCAP_A64_ASIMDDP = 1 << 20, + ARM_HWCAP_A64_SHA512 = 1 << 21, + ARM_HWCAP_A64_SVE = 1 << 22, + ARM_HWCAP_A64_ASIMDFHM = 1 << 23, + ARM_HWCAP_A64_DIT = 1 << 24, + ARM_HWCAP_A64_USCAT = 1 << 25, + ARM_HWCAP_A64_ILRCPC = 1 << 26, + ARM_HWCAP_A64_FLAGM = 1 << 27, + ARM_HWCAP_A64_SSBS = 1 << 28, + ARM_HWCAP_A64_SB = 1 << 29, + ARM_HWCAP_A64_PACA = 1 << 30, + ARM_HWCAP_A64_PACG = 1UL << 31, + + ARM_HWCAP2_A64_DCPODP = 1 << 0, + ARM_HWCAP2_A64_SVE2 = 1 << 1, + ARM_HWCAP2_A64_SVEAES = 1 << 2, + ARM_HWCAP2_A64_SVEPMULL = 1 << 3, + ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, + ARM_HWCAP2_A64_SVESHA3 = 1 << 5, + ARM_HWCAP2_A64_SVESM4 = 1 << 6, + ARM_HWCAP2_A64_FLAGM2 = 1 << 7, + ARM_HWCAP2_A64_FRINT = 1 << 8, + ARM_HWCAP2_A64_SVEI8MM = 1 << 9, + ARM_HWCAP2_A64_SVEF32MM = 1 << 10, + ARM_HWCAP2_A64_SVEF64MM = 1 << 11, + ARM_HWCAP2_A64_SVEBF16 = 1 << 12, + ARM_HWCAP2_A64_I8MM = 1 << 13, + ARM_HWCAP2_A64_BF16 = 1 << 14, + ARM_HWCAP2_A64_DGH = 1 << 15, + ARM_HWCAP2_A64_RNG = 1 << 16, + ARM_HWCAP2_A64_BTI = 1 << 17, + ARM_HWCAP2_A64_MTE = 1 << 18, +}; + +#define ELF_HWCAP get_elf_hwcap() +#define ELF_HWCAP2 get_elf_hwcap2() + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + +static uint32_t get_elf_hwcap(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + uint32_t hwcaps = 0; + + hwcaps |= ARM_HWCAP_A64_FP; + hwcaps |= ARM_HWCAP_A64_ASIMD; + hwcaps |= ARM_HWCAP_A64_CPUID; + + /* probe for the extra features */ + + GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); + GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); + GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); + GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); + GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); + GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); + GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); + GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); + 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_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_jscvt, ARM_HWCAP_A64_JSCVT); + GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); + GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); + GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); + GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); + + return hwcaps; +} + +static uint32_t get_elf_hwcap2(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + uint32_t hwcaps = 0; + + GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); + GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); + GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); + GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); + GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); + GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); + GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); + GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); + GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); + GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); + GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); + GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); + GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); + GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); + GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); + 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); + + return hwcaps; +} + +#undef GET_FEATURE_ID + +#endif /* TARGET_ARCH_ELF_H */ diff --git a/bsd-user/aarch64/target_arch_reg.h b/bsd-user/aarch64/target_arch_reg.h new file mode 100644 index 0000000000..b53302e7f7 --- /dev/null +++ b/bsd-user/aarch64/target_arch_reg.h @@ -0,0 +1,56 @@ +/* + * FreeBSD arm64 register structures + * + * Copyright (c) 2015 Stacey Son + * 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 TARGET_ARCH_REG_H +#define TARGET_ARCH_REG_H + +/* See sys/arm64/include/reg.h */ +typedef struct target_reg { + uint64_t x[30]; + uint64_t lr; + uint64_t sp; + uint64_t elr; + uint64_t spsr; +} target_reg_t; + +typedef struct target_fpreg { + Int128 fp_q[32]; + uint32_t fp_sr; + uint32_t fp_cr; +} target_fpreg_t; + +#define tswapreg(ptr) tswapal(ptr) + +static inline void target_copy_regs(target_reg_t *regs, CPUARMState *env) +{ + int i; + + for (i = 0; i < 30; i++) { + regs->x[i] = tswapreg(env->xregs[i]); + } + regs->lr = tswapreg(env->xregs[30]); + regs->sp = tswapreg(env->xregs[31]); + regs->elr = tswapreg(env->pc); + regs->spsr = tswapreg(pstate_read(env)); +} + +#undef tswapreg + +#endif /* TARGET_ARCH_REG_H */ diff --git a/bsd-user/aarch64/target_arch_signal.h b/bsd-user/aarch64/target_arch_signal.h new file mode 100644 index 0000000000..b72ba7aa50 --- /dev/null +++ b/bsd-user/aarch64/target_arch_signal.h @@ -0,0 +1,82 @@ +/* + * ARM AArch64 specific signal definitions for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 TARGET_ARCH_SIGNAL_H +#define TARGET_ARCH_SIGNAL_H + +#include "cpu.h" + +#define TARGET_REG_X0 0 +#define TARGET_REG_X30 30 +#define TARGET_REG_X31 31 +#define TARGET_REG_LR TARGET_REG_X30 +#define TARGET_REG_SP TARGET_REG_X31 + +#define TARGET_INSN_SIZE 4 /* arm64 instruction size */ + +/* Size of the signal trampolin code. See _sigtramp(). */ +#define TARGET_SZSIGCODE ((abi_ulong)(9 * TARGET_INSN_SIZE)) + +/* compare to sys/arm64/include/_limits.h */ +#define TARGET_MINSIGSTKSZ (1024 * 4) /* min sig stack size */ +#define TARGET_SIGSTKSZ (TARGET_MINSIGSTKSZ + 32768) /* recommended size */ + +/* struct __mcontext in sys/arm64/include/ucontext.h */ + +struct target_gpregs { + uint64_t gp_x[30]; + uint64_t gp_lr; + uint64_t gp_sp; + uint64_t gp_elr; + uint32_t gp_spsr; + uint32_t gp_pad; +}; + +struct target_fpregs { + Int128 fp_q[32]; + uint32_t fp_sr; + uint32_t fp_cr; + uint32_t fp_flags; + uint32_t fp_pad; +}; + +struct target__mcontext { + struct target_gpregs mc_gpregs; + struct target_fpregs mc_fpregs; + uint32_t mc_flags; +#define TARGET_MC_FP_VALID 0x1 + uint32_t mc_pad; + uint64_t mc_spare[8]; +}; + +typedef struct target__mcontext target_mcontext_t; + +#define TARGET_MCONTEXT_SIZE 880 +#define TARGET_UCONTEXT_SIZE 960 + +#include "target_os_ucontext.h" + +struct target_sigframe { + target_siginfo_t sf_si; /* saved siginfo */ + target_ucontext_t sf_uc; /* saved ucontext */ +}; + +#define TARGET_SIGSTACK_ALIGN 16 + +#endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/aarch64/target_arch_sigtramp.h b/bsd-user/aarch64/target_arch_sigtramp.h new file mode 100644 index 0000000000..8cdd33b621 --- /dev/null +++ b/bsd-user/aarch64/target_arch_sigtramp.h @@ -0,0 +1,48 @@ +/* + * ARM AArch64 sigcode for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 TARGET_ARCH_SIGTRAMP_H +#define TARGET_ARCH_SIGTRAMP_H + +/* Compare to ENTRY(sigcode) in arm64/arm64/locore.S */ +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + int i; + uint32_t sys_exit = TARGET_FREEBSD_NR_exit; + + uint32_t sigtramp_code[] = { + /* 1 */ 0x910003e0, /* mov x0, sp */ + /* 2 */ 0x91000000 + (sigf_uc << 10), /* add x0, x0, #SIGF_UC */ + /* 3 */ 0xd2800000 + (sys_sigreturn << 5) + 0x8, /* mov x8, #SYS_sigreturn */ + /* 4 */ 0xd4000001, /* svc #0 */ + /* 5 */ 0xd2800028 + (sys_exit << 5) + 0x8, /* mov x8, #SYS_exit */ + /* 6 */ 0xd4000001, /* svc #0 */ + /* 7 */ 0x17fffffc, /* b -4 */ + /* 8 */ sys_sigreturn, + /* 9 */ sys_exit + }; + + for (i = 0; i < 9; i++) { + tswap32s(&sigtramp_code[i]); + } + + return memcpy_to_target(offset, sigtramp_code, TARGET_SZSIGCODE); +} +#endif /* TARGET_ARCH_SIGTRAMP_H */ diff --git a/bsd-user/aarch64/target_arch_sysarch.h b/bsd-user/aarch64/target_arch_sysarch.h new file mode 100644 index 0000000000..b003015daf --- /dev/null +++ b/bsd-user/aarch64/target_arch_sysarch.h @@ -0,0 +1,42 @@ +/* + * ARM AArch64 sysarch() system call emulation for bsd-user. + * + * Copyright (c) 2015 + * + * 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 TARGET_ARCH_SYSARCH_H +#define TARGET_ARCH_SYSARCH_H + +#include "target_syscall.h" +#include "target_arch.h" + +/* See sysarch() in sys/arm64/arm64/sys_machdep.c */ +static inline abi_long do_freebsd_arch_sysarch(CPUARMState *env, int op, + abi_ulong parms) +{ + int ret = -TARGET_EOPNOTSUPP; + + fprintf(stderr, "sysarch"); + return ret; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ +} + +#endif /* TARGET_ARCH_SYSARCH_H */ diff --git a/bsd-user/aarch64/target_arch_thread.h b/bsd-user/aarch64/target_arch_thread.h new file mode 100644 index 0000000000..4c911e605a --- /dev/null +++ b/bsd-user/aarch64/target_arch_thread.h @@ -0,0 +1,61 @@ +/* + * ARM AArch64 thread support for bsd-user. + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 TARGET_ARCH_THREAD_H +#define TARGET_ARCH_THREAD_H + +/* Compare to arm64/arm64/vm_machdep.c cpu_set_upcall_kse() */ +static inline void target_thread_set_upcall(CPUARMState *regs, abi_ulong entry, + abi_ulong arg, abi_ulong stack_base, abi_ulong stack_size) +{ + abi_ulong sp; + + /* + * Make sure the stack is properly aligned. + * arm64/include/param.h (STACKLIGN() macro) + */ + sp = ROUND_DOWN(stack_base + stack_size, 16); + + /* sp = stack base */ + regs->xregs[31] = sp; + /* pc = start function entry */ + regs->pc = entry; + /* r0 = arg */ + regs->xregs[0] = arg; + + +} + +static inline void target_thread_init(struct target_pt_regs *regs, + struct image_info *infop) +{ + abi_long stack = infop->start_stack; + + /* + * Make sure the stack is properly aligned. + * arm64/include/param.h (STACKLIGN() macro) + */ + + memset(regs, 0, sizeof(*regs)); + regs->regs[0] = infop->start_stack; + regs->pc = infop->entry; + regs->sp = ROUND_DOWN(stack, 16); +} + +#endif /* TARGET_ARCH_THREAD_H */ diff --git a/bsd-user/aarch64/target_arch_vmparam.h b/bsd-user/aarch64/target_arch_vmparam.h new file mode 100644 index 0000000000..0c35491970 --- /dev/null +++ b/bsd-user/aarch64/target_arch_vmparam.h @@ -0,0 +1,74 @@ +/* + * ARM AArch64 VM parameters definitions for bsd-user. + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 TARGET_ARCH_VMPARAM_H +#define TARGET_ARCH_VMPARAM_H + +#include "cpu.h" + +/** + * FreeBSD/arm64 Address space layout. + * + * ARMv8 implements up to a 48 bit virtual address space. The address space is + * split into 2 regions at each end of the 64 bit address space, with an + * out of range "hole" in the middle. + * + * We limit the size of the two spaces to 39 bits each. + * + * Upper region: 0xffffffffffffffff + * 0xffffff8000000000 + * + * Hole: 0xffffff7fffffffff + * 0x0000008000000000 + * + * Lower region: 0x0000007fffffffff + * 0x0000000000000000 + * + * The upper region for the kernel, and the lower region for userland. + */ + + +/* compare to sys/arm64/include/vmparam.h */ +#define TARGET_MAXTSIZ (1 * GiB) /* max text size */ +#define TARGET_DFLDSIZ (128 * MiB) /* initial data size limit */ +#define TARGET_MAXDSIZ (1 * GiB) /* max data size */ +#define TARGET_DFLSSIZ (128 * MiB) /* initial stack size limit */ +#define TARGET_MAXSSIZ (1 * GiB) /* max stack size */ +#define TARGET_SGROWSIZ (128 * KiB) /* amount to grow stack */ + + /* KERNBASE - 512 MB */ +#define TARGET_VM_MAXUSER_ADDRESS (0x00007fffff000000ULL - (512 * MiB)) +#define TARGET_USRSTACK TARGET_VM_MAXUSER_ADDRESS + +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->xregs[31]; /* sp */ +} + +static inline void set_second_rval(CPUARMState *state, abi_ulong retval2) +{ + state->xregs[1] = retval2; /* XXX not really used on 64-bit arch */ +} + +static inline abi_ulong get_second_rval(CPUARMState *state) +{ + return state->xregs[1]; +} + +#endif /* TARGET_ARCH_VMPARAM_H */ diff --git a/bsd-user/aarch64/target_syscall.h b/bsd-user/aarch64/target_syscall.h new file mode 100644 index 0000000000..08ae913c42 --- /dev/null +++ b/bsd-user/aarch64/target_syscall.h @@ -0,0 +1,51 @@ +/* + * ARM AArch64 specific CPU for bsd-user + * + * Copyright (c) 2015 Stacey D. Son + * + * 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 BSD_USER_AARCH64_TARGET_SYSCALL_H +#define BSD_USER_AARCH64_TARGET_SYSCALL_H + +/* + * The aarch64 registers are named: + * + * x0 through x30 - for 64-bit-wide access (same registers) + * Register '31' is one of two registers depending on the instruction context: + * For instructions dealing with the stack, it is the stack pointer, named rsp + * For all other instructions, it is a "zero" register, which returns 0 when + * read and discards data when written - named rzr (xzr, wzr) + * + * Usage during syscall/function call: + * r0-r7 are used for arguments and return values + * For syscalls, the syscall number is in r8 + * r9-r15 are for temporary values (may get trampled) + * r16-r18 are used for intra-procedure-call and platform values (avoid) + * The called routine is expected to preserve r19-r28 + * r29 and r30 are used as the frame register and link register (avoid) + * See the ARM Procedure Call Reference for details. + */ +struct target_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#define TARGET_HW_MACHINE "arm64" +#define TARGET_HW_MACHINE_ARCH "aarch64" + +#endif /* BSD_USER_AARCH64_TARGET_SYSCALL_H */ 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/arm/target_arch_signal.h b/bsd-user/arm/target_arch_signal.h index 02b2b33e07..10f96b8bfc 100644 --- a/bsd-user/arm/target_arch_signal.h +++ b/bsd-user/arm/target_arch_signal.h @@ -86,4 +86,6 @@ struct target_sigframe { target_mcontext_vfp_t sf_vfp; /* actual saved VFP context */ }; +#define TARGET_SIGSTACK_ALIGN 8 + #endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/bsd-file.h b/bsd-user/bsd-file.h index 588e0c50d4..6fa2c30b4d 100644 --- a/bsd-user/bsd-file.h +++ b/bsd-user/bsd-file.h @@ -51,10 +51,8 @@ do { \ unlock_user(p1, arg1, 0); \ } while (0) -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); +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); @@ -643,7 +641,7 @@ static abi_long do_bsd_readlink(CPUArchState *env, abi_long arg1, } if (strcmp(p1, "/proc/curproc/file") == 0) { CPUState *cpu = env_cpu(env); - TaskState *ts = (TaskState *)cpu->opaque; + TaskState *ts = get_task_state(cpu); strncpy(p2, ts->bprm->fullpath, arg3); ret = MIN((abi_long)strlen(ts->bprm->fullpath), arg3); } else { 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..eef6b222d9 --- /dev/null +++ b/bsd-user/bsd-mem.h @@ -0,0 +1,453 @@ +/* + * 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" +#include "exec/page-protection.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 index 68b66e571d..8b1c2deea3 100644 --- a/bsd-user/bsd-proc.h +++ b/bsd-user/bsd-proc.h @@ -20,18 +20,18 @@ #ifndef BSD_PROC_H_ #define BSD_PROC_H_ -#include -#include -#include #include -#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) { -#ifdef TARGET_GPROF - _mcleanup(); -#endif gdb_exit(arg1); qemu_plugin_user_exit(); _exit(arg1); @@ -39,4 +39,376 @@ static inline abi_long do_bsd_exit(void *cpu_env, abi_long 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..833fa3bd05 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; @@ -382,7 +383,7 @@ static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) return ""; } -/* FIXME: This should use elf_ops.h */ +/* FIXME: This should use elf_ops.h.inc */ static int symcmp(const void *s0, const void *s1) { struct elf_sym *sym0 = (struct elf_sym *)s0; @@ -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..bf993f1b66 --- /dev/null +++ b/bsd-user/freebsd/os-proc.c @@ -0,0 +1,368 @@ +/* + * 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" + +/* + * 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, **qarg0; + 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(); + *argp++ = (char *)getprogname(); + 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; + + 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) { + ret = get_errno(fexecve((int)path_or_fd, argp, envp)); + } else { + p = lock_user_string(path_or_fd); + if (p == NULL) { + ret = -TARGET_EFAULT; + goto execve_end; + } + 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 57996cad8a..ca2f6fdb66 100644 --- a/bsd-user/freebsd/os-syscall.c +++ b/bsd-user/freebsd/os-syscall.c @@ -17,17 +17,6 @@ * 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" @@ -38,13 +27,22 @@ #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" +/* 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, @@ -64,9 +62,11 @@ 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); -void target_set_brk(abi_ulong new_brk) -{ -} +/* 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. @@ -225,10 +225,207 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, /* * 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. */ @@ -246,6 +443,7 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, 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); @@ -491,6 +689,230 @@ static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1, 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; @@ -511,10 +933,8 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg5, abi_long arg6, abi_long arg7, abi_long arg8) { - CPUState *cpu = env_cpu(cpu_env); - int ret; + abi_long ret; - trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); if (do_strace) { print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); } @@ -524,7 +944,6 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, if (do_strace) { print_freebsd_syscall_ret(num, ret); } - trace_guest_user_syscall_ret(cpu, num, ret); return ret; } 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_elf.h b/bsd-user/freebsd/target_os_elf.h index 9df17d56d8..01124979f7 100644 --- a/bsd-user/freebsd/target_os_elf.h +++ b/bsd-user/freebsd/target_os_elf.h @@ -22,6 +22,7 @@ #include "target_arch_elf.h" #include "elf.h" +#include "user/tswap-target.h" #define bsd_get_ncpu() 1 /* until we pull in bsd-proc.[hc] */ 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..ac0ef22cd7 100644 --- a/bsd-user/freebsd/target_os_stack.h +++ b/bsd-user/freebsd/target_os_stack.h @@ -23,9 +23,10 @@ #include #include "target_arch_sigtramp.h" #include "qemu/guest-random.h" +#include "user/tswap-target.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 +60,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/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/i386/target_arch_signal.h b/bsd-user/i386/target_arch_signal.h index 279dadc22c..2c14153ab6 100644 --- a/bsd-user/i386/target_arch_signal.h +++ b/bsd-user/i386/target_arch_signal.h @@ -88,4 +88,6 @@ struct target_sigframe { uint32_t __spare__[2]; }; +#define TARGET_SIGSTACK_ALIGN 8 + #endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/main.c b/bsd-user/main.c index 6f09180d65..61ca73c478 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" @@ -37,8 +35,10 @@ #include "qemu/path.h" #include "qemu/help_option.h" #include "qemu/module.h" +#include "qemu/plugin.h" #include "exec/exec-all.h" -#include "tcg/tcg.h" +#include "user/guest-base.h" +#include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" #include "qemu/cutils.h" @@ -46,11 +46,21 @@ #include "trace/control.h" #include "crypto/init.h" #include "qemu/guest-random.h" +#include "gdbstub/user.h" +#include "exec/page-vary.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; +static unsigned long opt_tb_size; uintptr_t guest_base; bool have_guest_base; /* @@ -69,33 +79,19 @@ 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(CPU) 0xfffffffful # else -# define MAX_RESERVED_VA (1ul << TARGET_VIRT_ADDR_SPACE_BITS) +# define MAX_RESERVED_VA(CPU) ((1ul << TARGET_VIRT_ADDR_SPACE_BITS) - 1) # endif # else -# define MAX_RESERVED_VA 0 +# define MAX_RESERVED_VA(CPU) 0 # endif #endif -/* - * That said, reserving *too* much vm space via mmap can run into problems - * with rlimits, oom due to page table creation, etc. We will still try it, - * if directed by the command-line option, but not by default. - */ -#if HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32 -unsigned long reserved_va = MAX_RESERVED_VA; -#else 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 */ unsigned long target_maxtsiz = TARGET_MAXTSIZ; /* max text size */ unsigned long target_dfldsiz = TARGET_DFLDSIZ; /* initial data size limit */ @@ -109,35 +105,41 @@ unsigned long target_sgrowsiz = TARGET_SGROWSIZ; /* amount to grow stack */ void fork_start(void) { start_exclusive(); - cpu_list_lock(); 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; /* - * Child processes created by fork() only have a single thread. Discard - * information about the parent threads. + * Child processes created by fork() only have a single thread. + * 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); } } - mmap_fork_end(child); - /* - * qemu_init_cpu_list() takes care of reinitializing the exclusive - * 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(); } else { - mmap_fork_end(child); 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(); } void cpu_loop(CPUArchState *env) @@ -167,7 +169,8 @@ 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" + "-tb-size size TCG translation block cache size\n" "-strace log system calls\n" "-trace [[enable=]][,events=][,file=]\n" " specify tracing options\n" @@ -247,22 +250,6 @@ adjust_ssize(void) setrlimit(RLIMIT_STACK, &rl); } -static void save_proc_pathname(char *argv0) -{ - int mib[4]; - size_t len; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PATHNAME; - mib[3] = -1; - - len = sizeof(qemu_proc_pathname); - if (sysctl(mib, 4, qemu_proc_pathname, &len, NULL, 0)) { - perror("sysctl"); - } -} - int main(int argc, char **argv) { const char *filename; @@ -283,6 +270,8 @@ int main(int argc, char **argv) char **target_environ, **wrk; envlist_t *envlist = NULL; char *argv0 = NULL; + int host_page_size; + unsigned long max_reserved_va; adjust_ssize(); @@ -290,7 +279,6 @@ int main(int argc, char **argv) usage(); } - save_proc_pathname(argv[0]); error_init(argv[0]); module_call_init(MODULE_INIT_TRACE); @@ -299,11 +287,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 +360,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 +374,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 +387,13 @@ 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, "tb-size")) { + r = argv[optind++]; + if (qemu_strtoul(r, NULL, 0, &opt_tb_size)) { + usage(); + } } else if (!strcmp(r, "strace")) { do_strace = 1; } else if (!strcmp(r, "trace")) { @@ -403,6 +405,8 @@ int main(int argc, char **argv) } } + qemu_host_page_mask = -qemu_host_page_size; + /* init debug */ { int mask = 0; @@ -449,16 +453,54 @@ 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); + object_property_set_int(OBJECT(accel), "tb-size", + opt_tb_size, &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; + /* + * Reserving too much vm space via mmap can run into problems with rlimits, + * oom due to page table creation, etc. We will still try it, if directed + * by the command-line option, but not by default. Unless we're running a + * target address space of 32 or fewer bits on a host with 64 bits. + */ + 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) { + /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */ + reserved_va = max_reserved_va; + } + if (getenv("QEMU_STRACE")) { do_strace = 1; } @@ -466,10 +508,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 +525,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 +588,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", @@ -532,6 +610,7 @@ int main(int argc, char **argv) init_task_state(ts); ts->info = info; ts->bprm = &bprm; + ts->ts_tid = qemu_get_thread_id(); cpu->opaque = ts; target_set_brk(info->brk); @@ -543,13 +622,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 d6c5a344c9..775e905960 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "exec/page-protection.h" #include "qemu.h" @@ -32,6 +33,7 @@ void mmap_lock(void) void mmap_unlock(void) { + assert(mmap_lock_count > 0); if (--mmap_lock_count == 0) { pthread_mutex_unlock(&mmap_mutex); } @@ -95,7 +97,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) end = host_end; } ret = mprotect(g2h_untagged(host_start), - qemu_host_page_size, prot1 & PAGE_BITS); + qemu_host_page_size, prot1 & PAGE_RWX); if (ret != 0) goto error; host_start += qemu_host_page_size; @@ -106,7 +108,7 @@ int target_mprotect(abi_ulong start, abi_ulong len, int prot) prot1 |= page_get_flags(addr); } ret = mprotect(g2h_untagged(host_end - qemu_host_page_size), - qemu_host_page_size, prot1 & PAGE_BITS); + qemu_host_page_size, prot1 & PAGE_RWX); if (ret != 0) goto error; host_end -= qemu_host_page_size; @@ -118,7 +120,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: @@ -126,6 +128,40 @@ error: return ret; } +/* + * Perform a pread on behalf of target_mmap. We can reach EOF, we can be + * interrupted by signals, and in general there's no good error return path. + * If @zero, zero the rest of the block at EOF. + * Return true on success. + */ +static bool mmap_pread(int fd, void *p, size_t len, off_t offset, bool zero) +{ + while (1) { + ssize_t r = pread(fd, p, len, offset); + + if (likely(r == len)) { + /* Complete */ + return true; + } + if (r == 0) { + /* EOF */ + if (zero) { + memset(p, 0, len); + } + return true; + } + if (r > 0) { + /* Short read */ + p += r; + len -= r; + offset += r; + } else if (errno != EINTR) { + /* Error */ + return false; + } + } +} + /* * map an incomplete host page * @@ -173,7 +209,7 @@ static int mmap_frag(abi_ulong real_start, return -1; prot1 = prot; } - prot1 &= PAGE_BITS; + prot1 &= PAGE_RWX; prot_new = prot | prot1; if (fd != -1) { @@ -188,7 +224,7 @@ static int mmap_frag(abi_ulong real_start, 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) { + if (!mmap_pread(fd, g2h_untagged(start), end - start, offset, true)) { return -1; } @@ -213,8 +249,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 +256,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 +293,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; @@ -598,7 +599,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, -1, 0); if (retaddr == -1) goto fail; - if (pread(fd, g2h_untagged(start), len, offset) == -1) { + if (!mmap_pread(fd, g2h_untagged(start), len, offset, false)) { goto fail; } if (!(prot & PROT_WRITE)) { @@ -609,7 +610,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,7 +657,7 @@ 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); @@ -670,7 +671,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; @@ -767,7 +768,7 @@ int target_munmap(abi_ulong start, abi_ulong len) } if (ret == 0) { - page_set_flags(start, start + len, 0); + page_set_flags(start, start + len - 1, 0); } mmap_unlock(); return ret; diff --git a/bsd-user/netbsd/target_os_elf.h b/bsd-user/netbsd/target_os_elf.h index 2f3cb20871..9de0f290c0 100644 --- a/bsd-user/netbsd/target_os_elf.h +++ b/bsd-user/netbsd/target_os_elf.h @@ -22,6 +22,7 @@ #include "target_arch_elf.h" #include "elf.h" +#include "user/tswap-target.h" /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE diff --git a/bsd-user/openbsd/target_os_elf.h b/bsd-user/openbsd/target_os_elf.h index 6dca9c5a85..4cf5747dcd 100644 --- a/bsd-user/openbsd/target_os_elf.h +++ b/bsd-user/openbsd/target_os_elf.h @@ -22,6 +22,7 @@ #include "target_arch_elf.h" #include "elf.h" +#include "user/tswap-target.h" /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE 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..3736c41786 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -17,19 +17,19 @@ #ifndef QEMU_H #define QEMU_H -#include "qemu/osdep.h" +#include + +#include "qemu/int128.h" #include "cpu.h" #include "qemu/units.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" -#undef DEBUG_REMAP - -#include "exec/user/abitypes.h" +#include "user/abitypes.h" extern char **environ; -#include "exec/user/thunk.h" +#include "user/thunk.h" #include "target_arch.h" #include "syscall_defs.h" #include "target_syscall.h" @@ -37,6 +37,17 @@ extern char **environ; #include "target_os_signal.h" #include "target.h" #include "exec/gdbstub.h" +#include "exec/page-protection.h" +#include "qemu/clang-tsa.h" +#include "accel/tcg/vcpu-state.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 +61,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; @@ -72,7 +80,7 @@ struct emulated_sigtable { /* * NOTE: we force a big alignment so that the stack stored after is aligned too */ -typedef struct TaskState { +struct TaskState { pid_t ts_tid; /* tid (or pid) of this task */ struct TaskState *next; @@ -110,15 +118,16 @@ typedef struct TaskState { /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; -} __attribute__((aligned(16))) TaskState; +} __attribute__((aligned(16))); 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 +190,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 +241,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 +261,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 +282,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 +291,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 +361,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 +375,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; \ @@ -401,7 +435,7 @@ static inline void *lock_user(int type, abi_ulong guest_addr, long len, if (!access_ok(type, guest_addr, len)) { return NULL; } -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP { void *addr; addr = g_malloc(len); @@ -425,7 +459,7 @@ static inline void unlock_user(void *host_ptr, abi_ulong guest_addr, long len) { -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP if (!host_ptr) { return; } diff --git a/bsd-user/riscv/signal.c b/bsd-user/riscv/signal.c new file mode 100644 index 0000000000..10c940cd49 --- /dev/null +++ b/bsd-user/riscv/signal.c @@ -0,0 +1,170 @@ +/* + * RISC-V signal definitions + * + * Copyright (c) 2019 Mark Corbin + * + * 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" + +/* + * Compare with sendsig() in riscv/riscv/exec_machdep.c + * Assumes that target stack frame memory is locked. + */ +abi_long +set_sigtramp_args(CPURISCVState *regs, int sig, struct target_sigframe *frame, + abi_ulong frame_addr, struct target_sigaction *ka) +{ + /* + * Arguments to signal handler: + * a0 (10) = signal number + * a1 (11) = siginfo pointer + * a2 (12) = ucontext pointer + * pc = signal pointer handler + * sp (2) = sigframe pointer + * ra (1) = sigtramp at base of user stack + */ + + regs->gpr[xA0] = sig; + regs->gpr[xA1] = frame_addr + + offsetof(struct target_sigframe, sf_si); + regs->gpr[xA2] = frame_addr + + offsetof(struct target_sigframe, sf_uc); + regs->pc = ka->_sa_handler; + regs->gpr[xSP] = frame_addr; + regs->gpr[xRA] = TARGET_PS_STRINGS - TARGET_SZSIGCODE; + return 0; +} + +/* + * Compare to riscv/riscv/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPURISCVState *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags) +{ + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + get_mcontext(env, mcp, flags); + return 0; +} + +/* + * Compare with get_mcontext() in riscv/riscv/machdep.c + * Assumes that the memory is locked if mcp points to user memory. + */ +abi_long get_mcontext(CPURISCVState *regs, target_mcontext_t *mcp, + int flags) +{ + + mcp->mc_gpregs.gp_t[0] = tswap64(regs->gpr[5]); + mcp->mc_gpregs.gp_t[1] = tswap64(regs->gpr[6]); + mcp->mc_gpregs.gp_t[2] = tswap64(regs->gpr[7]); + mcp->mc_gpregs.gp_t[3] = tswap64(regs->gpr[28]); + mcp->mc_gpregs.gp_t[4] = tswap64(regs->gpr[29]); + mcp->mc_gpregs.gp_t[5] = tswap64(regs->gpr[30]); + mcp->mc_gpregs.gp_t[6] = tswap64(regs->gpr[31]); + + mcp->mc_gpregs.gp_s[0] = tswap64(regs->gpr[8]); + mcp->mc_gpregs.gp_s[1] = tswap64(regs->gpr[9]); + mcp->mc_gpregs.gp_s[2] = tswap64(regs->gpr[18]); + mcp->mc_gpregs.gp_s[3] = tswap64(regs->gpr[19]); + mcp->mc_gpregs.gp_s[4] = tswap64(regs->gpr[20]); + mcp->mc_gpregs.gp_s[5] = tswap64(regs->gpr[21]); + mcp->mc_gpregs.gp_s[6] = tswap64(regs->gpr[22]); + mcp->mc_gpregs.gp_s[7] = tswap64(regs->gpr[23]); + mcp->mc_gpregs.gp_s[8] = tswap64(regs->gpr[24]); + mcp->mc_gpregs.gp_s[9] = tswap64(regs->gpr[25]); + mcp->mc_gpregs.gp_s[10] = tswap64(regs->gpr[26]); + mcp->mc_gpregs.gp_s[11] = tswap64(regs->gpr[27]); + + mcp->mc_gpregs.gp_a[0] = tswap64(regs->gpr[10]); + mcp->mc_gpregs.gp_a[1] = tswap64(regs->gpr[11]); + mcp->mc_gpregs.gp_a[2] = tswap64(regs->gpr[12]); + mcp->mc_gpregs.gp_a[3] = tswap64(regs->gpr[13]); + mcp->mc_gpregs.gp_a[4] = tswap64(regs->gpr[14]); + mcp->mc_gpregs.gp_a[5] = tswap64(regs->gpr[15]); + mcp->mc_gpregs.gp_a[6] = tswap64(regs->gpr[16]); + mcp->mc_gpregs.gp_a[7] = tswap64(regs->gpr[17]); + + if (flags & TARGET_MC_GET_CLEAR_RET) { + mcp->mc_gpregs.gp_a[0] = 0; /* a0 */ + mcp->mc_gpregs.gp_a[1] = 0; /* a1 */ + mcp->mc_gpregs.gp_t[0] = 0; /* clear syscall error */ + } + + mcp->mc_gpregs.gp_ra = tswap64(regs->gpr[1]); + mcp->mc_gpregs.gp_sp = tswap64(regs->gpr[2]); + mcp->mc_gpregs.gp_gp = tswap64(regs->gpr[3]); + mcp->mc_gpregs.gp_tp = tswap64(regs->gpr[4]); + mcp->mc_gpregs.gp_sepc = tswap64(regs->pc); + + return 0; +} + +/* Compare with set_mcontext() in riscv/riscv/exec_machdep.c */ +abi_long set_mcontext(CPURISCVState *regs, target_mcontext_t *mcp, + int srflag) +{ + + regs->gpr[5] = tswap64(mcp->mc_gpregs.gp_t[0]); + regs->gpr[6] = tswap64(mcp->mc_gpregs.gp_t[1]); + regs->gpr[7] = tswap64(mcp->mc_gpregs.gp_t[2]); + regs->gpr[28] = tswap64(mcp->mc_gpregs.gp_t[3]); + regs->gpr[29] = tswap64(mcp->mc_gpregs.gp_t[4]); + regs->gpr[30] = tswap64(mcp->mc_gpregs.gp_t[5]); + regs->gpr[31] = tswap64(mcp->mc_gpregs.gp_t[6]); + + regs->gpr[8] = tswap64(mcp->mc_gpregs.gp_s[0]); + regs->gpr[9] = tswap64(mcp->mc_gpregs.gp_s[1]); + regs->gpr[18] = tswap64(mcp->mc_gpregs.gp_s[2]); + regs->gpr[19] = tswap64(mcp->mc_gpregs.gp_s[3]); + regs->gpr[20] = tswap64(mcp->mc_gpregs.gp_s[4]); + regs->gpr[21] = tswap64(mcp->mc_gpregs.gp_s[5]); + regs->gpr[22] = tswap64(mcp->mc_gpregs.gp_s[6]); + regs->gpr[23] = tswap64(mcp->mc_gpregs.gp_s[7]); + regs->gpr[24] = tswap64(mcp->mc_gpregs.gp_s[8]); + regs->gpr[25] = tswap64(mcp->mc_gpregs.gp_s[9]); + regs->gpr[26] = tswap64(mcp->mc_gpregs.gp_s[10]); + regs->gpr[27] = tswap64(mcp->mc_gpregs.gp_s[11]); + + regs->gpr[10] = tswap64(mcp->mc_gpregs.gp_a[0]); + regs->gpr[11] = tswap64(mcp->mc_gpregs.gp_a[1]); + regs->gpr[12] = tswap64(mcp->mc_gpregs.gp_a[2]); + regs->gpr[13] = tswap64(mcp->mc_gpregs.gp_a[3]); + regs->gpr[14] = tswap64(mcp->mc_gpregs.gp_a[4]); + regs->gpr[15] = tswap64(mcp->mc_gpregs.gp_a[5]); + regs->gpr[16] = tswap64(mcp->mc_gpregs.gp_a[6]); + regs->gpr[17] = tswap64(mcp->mc_gpregs.gp_a[7]); + + + regs->gpr[1] = tswap64(mcp->mc_gpregs.gp_ra); + regs->gpr[2] = tswap64(mcp->mc_gpregs.gp_sp); + regs->gpr[3] = tswap64(mcp->mc_gpregs.gp_gp); + regs->gpr[4] = tswap64(mcp->mc_gpregs.gp_tp); + regs->pc = tswap64(mcp->mc_gpregs.gp_sepc); + + return 0; +} + +/* Compare with sys_sigreturn() in riscv/riscv/machdep.c */ +abi_long get_ucontext_sigreturn(CPURISCVState *regs, + abi_ulong target_sf, abi_ulong *target_uc) +{ + + *target_uc = target_sf; + return 0; +} diff --git a/bsd-user/riscv/target.h b/bsd-user/riscv/target.h new file mode 100644 index 0000000000..036ddd185e --- /dev/null +++ b/bsd-user/riscv/target.h @@ -0,0 +1,20 @@ +/* + * Riscv64 general target stuff that's common to all aarch details + * + * Copyright (c) 2022 M. Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TARGET_H +#define TARGET_H + +/* + * riscv64 ABI does not 'lump' the registers for 64-bit args. + */ +static inline bool regpairs_aligned(void *cpu_env) +{ + return false; +} + +#endif /* TARGET_H */ diff --git a/bsd-user/riscv/target_arch.h b/bsd-user/riscv/target_arch.h new file mode 100644 index 0000000000..26ce07f343 --- /dev/null +++ b/bsd-user/riscv/target_arch.h @@ -0,0 +1,27 @@ +/* + * RISC-V specific prototypes + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_H +#define TARGET_ARCH_H + +#include "qemu.h" + +void target_cpu_set_tls(CPURISCVState *env, target_ulong newtls); + +#endif /* TARGET_ARCH_H */ diff --git a/bsd-user/riscv/target_arch_cpu.c b/bsd-user/riscv/target_arch_cpu.c new file mode 100644 index 0000000000..44e25d2ddf --- /dev/null +++ b/bsd-user/riscv/target_arch_cpu.c @@ -0,0 +1,29 @@ +/* + * RISC-V CPU related code + * + * Copyright (c) 2019 Mark Corbin + * + * 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 "target_arch.h" + +#define TP_OFFSET 16 + +/* Compare with cpu_set_user_tls() in riscv/riscv/vm_machdep.c */ +void target_cpu_set_tls(CPURISCVState *env, target_ulong newtls) +{ + env->gpr[xTP] = newtls + TP_OFFSET; +} diff --git a/bsd-user/riscv/target_arch_cpu.h b/bsd-user/riscv/target_arch_cpu.h new file mode 100644 index 0000000000..a93ea3915a --- /dev/null +++ b/bsd-user/riscv/target_arch_cpu.h @@ -0,0 +1,148 @@ +/* + * RISC-V CPU init and loop + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_CPU_H +#define TARGET_ARCH_CPU_H + +#include "target_arch.h" +#include "signal-common.h" + +#define TARGET_DEFAULT_CPU_MODEL "max" + +static inline void target_cpu_init(CPURISCVState *env, + struct target_pt_regs *regs) +{ + int i; + + for (i = 1; i < 32; i++) { + env->gpr[i] = regs->regs[i]; + } + + env->pc = regs->sepc; +} + +static inline void target_cpu_loop(CPURISCVState *env) +{ + CPUState *cs = env_cpu(env); + int trapnr; + abi_long ret; + unsigned int syscall_num; + int32_t signo, code; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + signo = 0; + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + case RISCV_EXCP_U_ECALL: + syscall_num = env->gpr[xT0]; + env->pc += TARGET_INSN_SIZE; + /* Compare to cpu_fetch_syscall_args() in riscv/riscv/trap.c */ + if (TARGET_FREEBSD_NR___syscall == syscall_num || + TARGET_FREEBSD_NR_syscall == syscall_num) { + ret = do_freebsd_syscall(env, + env->gpr[xA0], + env->gpr[xA1], + env->gpr[xA2], + env->gpr[xA3], + env->gpr[xA4], + env->gpr[xA5], + env->gpr[xA6], + env->gpr[xA7], + 0); + } else { + ret = do_freebsd_syscall(env, + syscall_num, + env->gpr[xA0], + env->gpr[xA1], + env->gpr[xA2], + env->gpr[xA3], + env->gpr[xA4], + env->gpr[xA5], + env->gpr[xA6], + env->gpr[xA7] + ); + } + + /* + * Compare to cpu_set_syscall_retval() in + * riscv/riscv/vm_machdep.c + */ + if (ret >= 0) { + env->gpr[xA0] = ret; + env->gpr[xT0] = 0; + } else if (ret == -TARGET_ERESTART) { + env->pc -= TARGET_INSN_SIZE; + } else if (ret != -TARGET_EJUSTRETURN) { + env->gpr[xA0] = -ret; + env->gpr[xT0] = 1; + } + break; + case RISCV_EXCP_ILLEGAL_INST: + signo = TARGET_SIGILL; + code = TARGET_ILL_ILLOPC; + break; + case RISCV_EXCP_BREAKPOINT: + signo = TARGET_SIGTRAP; + code = TARGET_TRAP_BRKPT; + break; + case EXCP_DEBUG: + signo = TARGET_SIGTRAP; + code = TARGET_TRAP_BRKPT; + break; + default: + fprintf(stderr, "qemu: unhandled CPU exception " + "0x%x - aborting\n", trapnr); + cpu_dump_state(cs, stderr, 0); + abort(); + } + + if (signo) { + force_sig_fault(signo, code, env->pc); + } + + process_pending_signals(env); + } +} + +static inline void target_cpu_clone_regs(CPURISCVState *env, target_ulong newsp) +{ + if (newsp) { + env->gpr[xSP] = newsp; + } + + env->gpr[xA0] = 0; + env->gpr[xT0] = 0; +} + +static inline void target_cpu_reset(CPUArchState *env) +{ +} + +#endif /* TARGET_ARCH_CPU_H */ diff --git a/bsd-user/riscv/target_arch_elf.h b/bsd-user/riscv/target_arch_elf.h new file mode 100644 index 0000000000..4eb915e61e --- /dev/null +++ b/bsd-user/riscv/target_arch_elf.h @@ -0,0 +1,42 @@ +/* + * RISC-V ELF definitions + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_ELF_H +#define TARGET_ARCH_ELF_H + +#define elf_check_arch(x) ((x) == EM_RISCV) +#define ELF_START_MMAP 0x80000000 +#define ELF_ET_DYN_LOAD_ADDR 0x100000 +#define ELF_CLASS ELFCLASS64 + +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_RISCV + +#define ELF_HWCAP get_elf_hwcap() +static uint32_t get_elf_hwcap(void) +{ + RISCVCPU *cpu = RISCV_CPU(thread_cpu); + + return cpu->env.misa_ext_mask; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif /* TARGET_ARCH_ELF_H */ diff --git a/bsd-user/riscv/target_arch_reg.h b/bsd-user/riscv/target_arch_reg.h new file mode 100644 index 0000000000..12b1c96b61 --- /dev/null +++ b/bsd-user/riscv/target_arch_reg.h @@ -0,0 +1,88 @@ +/* + * RISC-V register structures + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_REG_H +#define TARGET_ARCH_REG_H + +/* Compare with riscv/include/reg.h */ +typedef struct target_reg { + uint64_t ra; /* return address */ + uint64_t sp; /* stack pointer */ + uint64_t gp; /* global pointer */ + uint64_t tp; /* thread pointer */ + uint64_t t[7]; /* temporaries */ + uint64_t s[12]; /* saved registers */ + uint64_t a[8]; /* function arguments */ + uint64_t sepc; /* exception program counter */ + uint64_t sstatus; /* status register */ +} target_reg_t; + +typedef struct target_fpreg { + uint64_t fp_x[32][2]; /* Floating point registers */ + uint64_t fp_fcsr; /* Floating point control reg */ +} target_fpreg_t; + +#define tswapreg(ptr) tswapal(ptr) + +/* Compare with struct trapframe in riscv/include/frame.h */ +static inline void target_copy_regs(target_reg_t *regs, + const CPURISCVState *env) +{ + + regs->ra = tswapreg(env->gpr[1]); + regs->sp = tswapreg(env->gpr[2]); + regs->gp = tswapreg(env->gpr[3]); + regs->tp = tswapreg(env->gpr[4]); + + regs->t[0] = tswapreg(env->gpr[5]); + regs->t[1] = tswapreg(env->gpr[6]); + regs->t[2] = tswapreg(env->gpr[7]); + regs->t[3] = tswapreg(env->gpr[28]); + regs->t[4] = tswapreg(env->gpr[29]); + regs->t[5] = tswapreg(env->gpr[30]); + regs->t[6] = tswapreg(env->gpr[31]); + + regs->s[0] = tswapreg(env->gpr[8]); + regs->s[1] = tswapreg(env->gpr[9]); + regs->s[2] = tswapreg(env->gpr[18]); + regs->s[3] = tswapreg(env->gpr[19]); + regs->s[4] = tswapreg(env->gpr[20]); + regs->s[5] = tswapreg(env->gpr[21]); + regs->s[6] = tswapreg(env->gpr[22]); + regs->s[7] = tswapreg(env->gpr[23]); + regs->s[8] = tswapreg(env->gpr[24]); + regs->s[9] = tswapreg(env->gpr[25]); + regs->s[10] = tswapreg(env->gpr[26]); + regs->s[11] = tswapreg(env->gpr[27]); + + regs->a[0] = tswapreg(env->gpr[10]); + regs->a[1] = tswapreg(env->gpr[11]); + regs->a[2] = tswapreg(env->gpr[12]); + regs->a[3] = tswapreg(env->gpr[13]); + regs->a[4] = tswapreg(env->gpr[14]); + regs->a[5] = tswapreg(env->gpr[15]); + regs->a[6] = tswapreg(env->gpr[16]); + regs->a[7] = tswapreg(env->gpr[17]); + + regs->sepc = tswapreg(env->pc); +} + +#undef tswapreg + +#endif /* TARGET_ARCH_REG_H */ diff --git a/bsd-user/riscv/target_arch_signal.h b/bsd-user/riscv/target_arch_signal.h new file mode 100644 index 0000000000..1a634b865b --- /dev/null +++ b/bsd-user/riscv/target_arch_signal.h @@ -0,0 +1,75 @@ +/* + * RISC-V signal definitions + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_SIGNAL_H +#define TARGET_ARCH_SIGNAL_H + +#include "cpu.h" + + +#define TARGET_INSN_SIZE 4 /* riscv instruction size */ + +/* Size of the signal trampoline code placed on the stack. */ +#define TARGET_SZSIGCODE ((abi_ulong)(7 * TARGET_INSN_SIZE)) + +/* Compare with riscv/include/_limits.h */ +#define TARGET_MINSIGSTKSZ (1024 * 4) +#define TARGET_SIGSTKSZ (TARGET_MINSIGSTKSZ + 32768) + +struct target_gpregs { + uint64_t gp_ra; + uint64_t gp_sp; + uint64_t gp_gp; + uint64_t gp_tp; + uint64_t gp_t[7]; + uint64_t gp_s[12]; + uint64_t gp_a[8]; + uint64_t gp_sepc; + uint64_t gp_sstatus; +}; + +struct target_fpregs { + uint64_t fp_x[32][2]; + uint64_t fp_fcsr; + uint32_t fp_flags; + uint32_t pad; +}; + +typedef struct target_mcontext { + struct target_gpregs mc_gpregs; + struct target_fpregs mc_fpregs; + uint32_t mc_flags; +#define TARGET_MC_FP_VALID 0x01 + uint32_t mc_pad; + uint64_t mc_spare[8]; +} target_mcontext_t; + +#define TARGET_MCONTEXT_SIZE 864 +#define TARGET_UCONTEXT_SIZE 936 + +#include "target_os_ucontext.h" + +struct target_sigframe { + target_ucontext_t sf_uc; /* = *sf_uncontext */ + target_siginfo_t sf_si; /* = *sf_siginfo (SA_SIGINFO case)*/ +}; + +#define TARGET_SIGSTACK_ALIGN 16 + +#endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/riscv/target_arch_sigtramp.h b/bsd-user/riscv/target_arch_sigtramp.h new file mode 100644 index 0000000000..dfe5076739 --- /dev/null +++ b/bsd-user/riscv/target_arch_sigtramp.h @@ -0,0 +1,41 @@ +/* + * RISC-V sigcode + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_SIGTRAMP_H +#define TARGET_ARCH_SIGTRAMP_H + +/* Compare with sigcode() in riscv/riscv/locore.S */ +static inline abi_long setup_sigtramp(abi_ulong offset, unsigned sigf_uc, + unsigned sys_sigreturn) +{ + uint32_t sys_exit = TARGET_FREEBSD_NR_exit; + + uint32_t sigtramp_code[] = { + /*1*/ const_le32(0x00010513), /*mv a0, sp*/ + /*2*/ const_le32(0x00050513 + (sigf_uc << 20)), /*addi a0,a0,sigf_uc*/ + /*3*/ const_le32(0x00000293 + (sys_sigreturn << 20)),/*li t0,sys_sigreturn*/ + /*4*/ const_le32(0x00000073), /*ecall*/ + /*5*/ const_le32(0x00000293 + (sys_exit << 20)), /*li t0,sys_exit*/ + /*6*/ const_le32(0x00000073), /*ecall*/ + /*7*/ const_le32(0xFF1FF06F) /*b -16*/ + }; + + return memcpy_to_target(offset, sigtramp_code, TARGET_SZSIGCODE); +} +#endif /* TARGET_ARCH_SIGTRAMP_H */ diff --git a/bsd-user/riscv/target_arch_sysarch.h b/bsd-user/riscv/target_arch_sysarch.h new file mode 100644 index 0000000000..9af42331b4 --- /dev/null +++ b/bsd-user/riscv/target_arch_sysarch.h @@ -0,0 +1,41 @@ +/* + * RISC-V sysarch() system call emulation + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_SYSARCH_H +#define TARGET_ARCH_SYSARCH_H + +#include "target_syscall.h" +#include "target_arch.h" + +static inline abi_long do_freebsd_arch_sysarch(CPURISCVState *env, int op, + abi_ulong parms) +{ + + return -TARGET_EOPNOTSUPP; +} + +static inline void do_freebsd_arch_print_sysarch( + const struct syscallname *name, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6) +{ + + gemu_log("UNKNOWN OP: %d, " TARGET_ABI_FMT_lx ")", (int)arg1, arg2); +} + +#endif /* TARGET_ARCH_SYSARCH_H */ diff --git a/bsd-user/riscv/target_arch_thread.h b/bsd-user/riscv/target_arch_thread.h new file mode 100644 index 0000000000..95cd0b6ad7 --- /dev/null +++ b/bsd-user/riscv/target_arch_thread.h @@ -0,0 +1,47 @@ +/* + * RISC-V thread support + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_THREAD_H +#define TARGET_ARCH_THREAD_H + +/* Compare with cpu_set_upcall() in riscv/riscv/vm_machdep.c */ +static inline void target_thread_set_upcall(CPURISCVState *regs, + abi_ulong entry, abi_ulong arg, abi_ulong stack_base, + abi_ulong stack_size) +{ + abi_ulong sp; + + sp = ROUND_DOWN(stack_base + stack_size, 16); + + regs->gpr[xSP] = sp; + regs->pc = entry; + regs->gpr[xA0] = arg; +} + +/* Compare with exec_setregs() in riscv/riscv/machdep.c */ +static inline void target_thread_init(struct target_pt_regs *regs, + struct image_info *infop) +{ + regs->sepc = infop->entry; + regs->regs[xRA] = infop->entry; + regs->regs[xA0] = infop->start_stack; + regs->regs[xSP] = ROUND_DOWN(infop->start_stack, 16); +} + +#endif /* TARGET_ARCH_THREAD_H */ diff --git a/bsd-user/riscv/target_arch_vmparam.h b/bsd-user/riscv/target_arch_vmparam.h new file mode 100644 index 0000000000..0f2486def1 --- /dev/null +++ b/bsd-user/riscv/target_arch_vmparam.h @@ -0,0 +1,53 @@ +/* + * RISC-V VM parameters definitions + * + * Copyright (c) 2019 Mark Corbin + * + * 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 TARGET_ARCH_VMPARAM_H +#define TARGET_ARCH_VMPARAM_H + +#include "cpu.h" + +/* Compare with riscv/include/vmparam.h */ +#define TARGET_MAXTSIZ (1 * GiB) /* max text size */ +#define TARGET_DFLDSIZ (128 * MiB) /* initial data size limit */ +#define TARGET_MAXDSIZ (1 * GiB) /* max data size */ +#define TARGET_DFLSSIZ (128 * MiB) /* initial stack size limit */ +#define TARGET_MAXSSIZ (1 * GiB) /* max stack size */ +#define TARGET_SGROWSIZ (128 * KiB) /* amount to grow stack */ + +#define TARGET_VM_MINUSER_ADDRESS (0x0000000000000000UL) +#define TARGET_VM_MAXUSER_ADDRESS (0x0000004000000000UL) + +#define TARGET_USRSTACK (TARGET_VM_MAXUSER_ADDRESS - TARGET_PAGE_SIZE) + +static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) +{ + return state->gpr[xSP]; +} + +static inline void set_second_rval(CPURISCVState *state, abi_ulong retval2) +{ + state->gpr[xA1] = retval2; +} + +static inline abi_ulong get_second_rval(CPURISCVState *state) +{ + return state->gpr[xA1]; +} + +#endif /* TARGET_ARCH_VMPARAM_H */ diff --git a/bsd-user/riscv/target_syscall.h b/bsd-user/riscv/target_syscall.h new file mode 100644 index 0000000000..e7e5231309 --- /dev/null +++ b/bsd-user/riscv/target_syscall.h @@ -0,0 +1,38 @@ +/* + * RISC-V system call definitions + * + * Copyright (c) Mark Corbin + * + * 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_RISCV_TARGET_SYSCALL_H +#define BSD_USER_RISCV_TARGET_SYSCALL_H + +/* + * struct target_pt_regs defines the way the registers are stored on the stack + * during a system call. + */ + +struct target_pt_regs { + abi_ulong regs[32]; + abi_ulong sepc; +}; + +#define UNAME_MACHINE "riscv64" + +#define TARGET_HW_MACHINE "riscv" +#define TARGET_HW_MACHINE_ARCH UNAME_MACHINE + +#endif /* BSD_USER_RISCV_TARGET_SYSCALL_H */ 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..da49b9bffc 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,11 +21,17 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qemu.h" +#include "exec/page-protection.h" +#include "user/tswap-target.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 +49,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 +246,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 +316,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 +362,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 +426,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 +468,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 +589,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 +718,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 */ @@ -718,14 +728,7 @@ static inline abi_ulong get_sigframe(struct target_sigaction *ka, sp = ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size; } -/* TODO: make this a target_arch function / define */ -#if defined(TARGET_ARM) - return (sp - frame_size) & ~7; -#elif defined(TARGET_AARCH64) - return (sp - frame_size) & ~15; -#else - return sp - frame_size; -#endif + return ROUND_DOWN(sp - frame_size, TARGET_SIGSTACK_ALIGN); } /* compare to $M/$M/exec_machdep.c sendsig and sys/kern/kern_sig.c sigexit */ @@ -783,13 +786,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 +836,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 +850,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 +875,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 +887,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 +964,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 +1019,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 +1035,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..6dc01d3be7 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -20,9 +20,9 @@ #include #include #include -#include #include "qemu.h" +#include "user/tswap-target.h" #include "os-strace.h" /* OS dependent strace print functions */ diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index b6d113d24a..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,8 +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/bsd-user/x86_64/target_arch_signal.h b/bsd-user/x86_64/target_arch_signal.h index ca24bf1e7f..f833ee66ce 100644 --- a/bsd-user/x86_64/target_arch_signal.h +++ b/bsd-user/x86_64/target_arch_signal.h @@ -97,4 +97,6 @@ struct target_sigframe { uint32_t __spare__[2]; }; +#define TARGET_SIGSTACK_ALIGN 16 + #endif /* TARGET_ARCH_SIGNAL_H */ diff --git a/bsd-user/x86_64/target_arch_thread.h b/bsd-user/x86_64/target_arch_thread.h index 52c28906d6..7739bb2154 100644 --- a/bsd-user/x86_64/target_arch_thread.h +++ b/bsd-user/x86_64/target_arch_thread.h @@ -31,7 +31,7 @@ static inline void target_thread_init(struct target_pt_regs *regs, struct image_info *infop) { regs->rax = 0; - regs->rsp = infop->start_stack; + regs->rsp = ((infop->start_stack - 8) & ~0xfUL) + 8; regs->rip = infop->entry; regs->rdi = infop->start_stack; } diff --git a/build.sh b/build.sh index fc79746789..23e4210068 100755 --- a/build.sh +++ b/build.sh @@ -158,8 +158,6 @@ target="qemu-system-i386" if test ! -z "$debug"; then build_cflags='-DXEMU_DEBUG_BUILD=1' opts="--enable-debug --enable-trace-backends=log" -else - opts="--enable-lto" fi most_recent_macosx_sdk_ver () { diff --git a/chardev/baum.c b/chardev/baum.c index 0a0d12661a..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 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..8ac6bebb6f 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "sysemu/replay.h" #include "chardev/char-fe.h" @@ -192,33 +191,27 @@ bool qemu_chr_fe_backend_open(CharBackend *be) bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp) { - int tag = 0; + unsigned int tag = 0; if (s) { if (CHARDEV_IS_MUX(s)) { MuxChardev *d = MUX_CHARDEV(s); - if (d->mux_cnt >= MAX_MUX) { - goto unavailable; + if (!mux_chr_attach_frontend(d, b, &tag, errp)) { + 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) @@ -232,7 +225,7 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del) } if (CHARDEV_IS_MUX(b->chr)) { MuxChardev *d = MUX_CHARDEV(b->chr); - d->backends[b->tag] = NULL; + mux_chr_detach_frontend(d, b->tag); } if (del) { Object *obj = OBJECT(b->chr); @@ -257,7 +250,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 +258,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 +329,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 +337,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..3be17b51ca 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,55 @@ 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) +{ + IOWatchPoll *iwp = io_watch_poll_from_source(source); + if (iwp->src) { + g_source_destroy(iwp->src); + g_source_unref(iwp->src); + 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 +119,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 +130,18 @@ 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); + 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-mux.c b/chardev/char-mux.c index ee2d47b20d..e13042d381 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/option.h" +#include "qemu/bitops.h" #include "chardev/char.h" #include "sysemu/block-backend.h" #include "qapi/qapi-commands-control.h" @@ -73,11 +74,11 @@ static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len) * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&d->chr, (uint8_t *)buf1, strlen(buf1)); - d->linestart = 0; + d->linestart = false; } ret += qemu_chr_fe_write(&d->chr, buf + i, 1); if (buf[i] == '\n') { - d->linestart = 1; + d->linestart = true; } } } @@ -124,7 +125,8 @@ static void mux_print_help(Chardev *chr) } } -static void mux_chr_send_event(MuxChardev *d, int mux_nr, QEMUChrEvent event) +static void mux_chr_send_event(MuxChardev *d, unsigned int mux_nr, + QEMUChrEvent event) { CharBackend *be = d->backends[mux_nr]; @@ -145,7 +147,7 @@ static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event) static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch) { if (d->term_got_escape) { - d->term_got_escape = 0; + d->term_got_escape = false; if (ch == term_escape_char) { goto send_char; } @@ -167,19 +169,26 @@ static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch) case 'b': qemu_chr_be_event(chr, CHR_EVENT_BREAK); break; - case 'c': - assert(d->mux_cnt > 0); /* handler registered with first fe */ + case 'c': { + unsigned int bit; + + /* Handler registered with first fe */ + assert(d->mux_bitset != 0); /* Switch to the next registered device */ - mux_set_focus(chr, (d->focus + 1) % d->mux_cnt); + bit = find_next_bit(&d->mux_bitset, MAX_MUX, d->focus + 1); + if (bit >= MAX_MUX) { + bit = find_next_bit(&d->mux_bitset, MAX_MUX, 0); + } + mux_set_focus(chr, bit); break; - case 't': + } case 't': d->timestamps = !d->timestamps; d->timestamps_start = -1; - d->linestart = 0; + d->linestart = false; break; } } else if (ch == term_escape_char) { - d->term_got_escape = 1; + d->term_got_escape = true; } else { send_char: return 1; @@ -242,15 +251,16 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size) void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event) { MuxChardev *d = MUX_CHARDEV(chr); - int i; + int bit; if (!muxes_opened) { return; } /* Send the event to all registered listeners */ - for (i = 0; i < d->mux_cnt; i++) { - mux_chr_send_event(d, i, event); + bit = -1; + while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) { + mux_chr_send_event(d, bit, event); } } @@ -275,14 +285,15 @@ static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond) static void char_mux_finalize(Object *obj) { MuxChardev *d = MUX_CHARDEV(obj); - int i; + int bit; - for (i = 0; i < d->mux_cnt; i++) { - CharBackend *be = d->backends[i]; - if (be) { - be->chr = NULL; - } + bit = -1; + while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) { + CharBackend *be = d->backends[bit]; + be->chr = NULL; + d->backends[bit] = NULL; } + d->mux_bitset = 0; qemu_chr_fe_deinit(&d->chr, false); } @@ -300,12 +311,46 @@ static void mux_chr_update_read_handlers(Chardev *chr) chr->gcontext, true, false); } -void mux_set_focus(Chardev *chr, int focus) +bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b, + unsigned int *tag, Error **errp) +{ + unsigned int bit; + + QEMU_BUILD_BUG_ON(MAX_MUX > (sizeof(d->mux_bitset) * BITS_PER_BYTE)); + + bit = find_next_zero_bit(&d->mux_bitset, MAX_MUX, 0); + if (bit >= MAX_MUX) { + error_setg(errp, + "too many uses of multiplexed chardev '%s'" + " (maximum is " stringify(MAX_MUX) ")", + d->parent.label); + return false; + } + + d->mux_bitset |= (1ul << bit); + d->backends[bit] = b; + *tag = bit; + + return true; +} + +bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag) +{ + if (!(d->mux_bitset & (1ul << tag))) { + return false; + } + + d->mux_bitset &= ~(1ul << tag); + d->backends[tag] = NULL; + + return true; +} + +void mux_set_focus(Chardev *chr, unsigned int focus) { MuxChardev *d = MUX_CHARDEV(chr); - assert(focus >= 0); - assert(focus < d->mux_cnt); + assert(d->mux_bitset & (1ul << focus)); if (d->focus != -1) { mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); 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..cbb21b76ae 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -29,6 +29,7 @@ #include "qemu/sockets.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "qemu/option.h" #include "qemu/qemu-print.h" #include "chardev/char-io.h" @@ -41,6 +42,7 @@ struct PtyChardev { int connected; GSource *timer_src; + char *path; }; typedef struct PtyChardev PtyChardev; @@ -93,9 +95,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 +108,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) @@ -190,6 +206,12 @@ static void char_pty_finalize(Object *obj) Chardev *chr = CHARDEV(obj); PtyChardev *s = PTY_CHARDEV(obj); + /* unlink symlink */ + if (s->path) { + unlink(s->path); + g_free(s->path); + } + pty_chr_state(chr, 0); object_unref(OBJECT(s->ioc)); pty_chr_timer_cancel(s); @@ -316,6 +338,7 @@ static void char_pty_open(Chardev *chr, int master_fd, slave_fd; char pty_name[PATH_MAX]; char *name; + char *path = backend->u.pty.data->path; master_fd = qemu_openpty_raw(&slave_fd, pty_name); if (master_fd < 0) { @@ -336,16 +359,40 @@ 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; + + /* create symbolic link */ + if (path) { + int res = symlink(pty_name, path); + + if (res != 0) { + error_setg_errno(errp, errno, "Failed to create PTY symlink"); + } else { + s->path = g_strdup(path); + } + } +} + +static void char_pty_parse(QemuOpts *opts, ChardevBackend *backend, + Error **errp) +{ + const char *path = qemu_opt_get(opts, "path"); + ChardevPty *pty; + + backend->type = CHARDEV_BACKEND_KIND_PTY; + pty = backend->u.pty.data = g_new0(ChardevPty, 1); + qemu_chr_parse_common(opts, qapi_ChardevPty_base(pty)); + pty->path = g_strdup(path); } static void char_pty_class_init(ObjectClass *oc, void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); + cc->parse = char_pty_parse; cc->open = char_pty_open; cc->chr_write = char_pty_chr_write; cc->chr_update_read_handler = pty_chr_update_read_handler; diff --git a/chardev/char-socket.c b/chardev/char-socket.c index b00efb1482..91496ceda9 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -33,6 +33,7 @@ #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-sockets.h" #include "qemu/yank.h" +#include "trace.h" #include "chardev/char-io.h" #include "chardev/char-socket.h" @@ -73,7 +74,7 @@ static void qemu_chr_socket_restart_timer(Chardev *chr) assert(!s->reconnect_timer); name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label); s->reconnect_timer = qemu_chr_timeout_add_ms(chr, - s->reconnect_time * 1000, + s->reconnect_time_ms, socket_reconnect_timeout, chr); g_source_set_name(s->reconnect_timer, name); @@ -126,6 +127,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) if (ret < 0 && errno != EAGAIN) { if (tcp_chr_read_poll(chr) <= 0) { /* Perform disconnect and return error. */ + trace_chr_socket_poll_err(chr, chr->label); tcp_chr_disconnect_locked(chr); } /* else let the read handler finish it properly */ } @@ -279,15 +281,16 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) size_t i; int *msgfds = NULL; size_t msgfds_num = 0; + Error *err = NULL; 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, &err); } else { ret = qio_channel_readv_full(s->ioc, &iov, 1, NULL, NULL, - NULL); + 0, &err); } if (msgfds_num) { @@ -322,7 +325,11 @@ static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) errno = EAGAIN; ret = -1; } else if (ret == -1) { + trace_chr_socket_recv_err(chr, chr->label, error_get_pretty(err)); + error_free(err); errno = EIO; + } else if (ret == 0) { + trace_chr_socket_recv_eof(chr, chr->label); } return ret; @@ -378,6 +385,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)); @@ -459,6 +470,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr) SocketChardev *s = SOCKET_CHARDEV(chr); bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED; + trace_chr_socket_disconnect(chr, chr->label); tcp_chr_free_connection(chr); if (s->listener) { @@ -469,7 +481,7 @@ static void tcp_chr_disconnect_locked(Chardev *chr) if (emit_close) { qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } - if (s->reconnect_time && !s->reconnect_timer) { + if (s->reconnect_time_ms && !s->reconnect_timer) { qemu_chr_socket_restart_timer(chr); } } @@ -517,6 +529,7 @@ static gboolean tcp_chr_hup(QIOChannel *channel, void *opaque) { Chardev *chr = CHARDEV(opaque); + trace_chr_socket_hangup(chr, chr->label); tcp_chr_disconnect(chr); return G_SOURCE_REMOVE; } @@ -597,6 +610,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); @@ -652,15 +681,18 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, SocketChardev *s = user_data; Chardev *chr = CHARDEV(s); TCPChardevTelnetInit *init = s->telnet_init; + Error *err = NULL; ssize_t ret; assert(init); - ret = qio_channel_write(ioc, init->buf, init->buflen, NULL); + ret = qio_channel_write(ioc, init->buf, init->buflen, &err); if (ret < 0) { if (ret == QIO_CHANNEL_ERR_BLOCK) { ret = 0; } else { + trace_chr_socket_write_err(chr, chr->label, error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); goto end; } @@ -710,7 +742,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 */ @@ -718,7 +750,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 */ @@ -742,8 +774,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)) { + trace_chr_socket_ws_handshake_err(chr, chr->label, + error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); } else { if (s->do_telnetopt) { @@ -778,8 +814,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)) { + trace_chr_socket_tls_handshake_err(chr, chr->label, + error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); } else { if (s->is_websock) { @@ -798,19 +838,22 @@ static void tcp_chr_tls_init(Chardev *chr) SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelTLS *tioc; gchar *name; + Error *err = NULL; if (s->is_listen) { tioc = qio_channel_tls_new_server( s->ioc, s->tls_creds, s->tls_authz, - NULL); + &err); } else { tioc = qio_channel_tls_new_client( s->ioc, s->tls_creds, s->addr->u.inet.host, - NULL); + &err); } if (tioc == NULL) { + trace_chr_socket_tls_init_err(chr, chr->label, error_get_pretty(err)); + error_free(err); tcp_chr_disconnect(chr); return; } @@ -1037,9 +1080,9 @@ static int tcp_chr_wait_connected(Chardev *chr, Error **errp) } else { Error *err = NULL; if (tcp_chr_connect_client_sync(chr, &err) < 0) { - if (s->reconnect_time) { + if (s->reconnect_time_ms) { error_free(err); - g_usleep(s->reconnect_time * 1000ULL * 1000ULL); + g_usleep(s->reconnect_time_ms * 1000ULL); } else { error_propagate(errp, err); return -1; @@ -1224,13 +1267,13 @@ skip_listen: static int qmp_chardev_open_socket_client(Chardev *chr, - int64_t reconnect, + int64_t reconnect_ms, Error **errp) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (reconnect > 0) { - s->reconnect_time = reconnect; + if (reconnect_ms > 0) { + s->reconnect_time_ms = reconnect_ms; tcp_chr_connect_client_async(chr); return 0; } else { @@ -1252,7 +1295,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 " @@ -1262,7 +1305,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"); @@ -1274,7 +1317,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"); @@ -1285,12 +1328,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, @@ -1311,6 +1354,12 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock, } } + if (sock->has_reconnect_ms && sock->has_reconnect) { + error_setg(errp, + "'reconnect' and 'reconnect-ms' are mutually exclusive"); + return false; + } + return true; } @@ -1328,7 +1377,7 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; bool is_websock = sock->has_websocket ? sock->websocket : false; - int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; + int64_t reconnect_ms = 0; SocketAddress *addr; s->is_listen = is_listen; @@ -1400,7 +1449,13 @@ static void qmp_chardev_open_socket(Chardev *chr, return; } } else { - if (qmp_chardev_open_socket_client(chr, reconnect, errp) < 0) { + if (sock->has_reconnect) { + reconnect_ms = sock->reconnect * 1000ULL; + } else if (sock->has_reconnect_ms) { + reconnect_ms = sock->reconnect_ms; + } + + if (qmp_chardev_open_socket_client(chr, reconnect_ms, errp) < 0) { return; } } @@ -1466,9 +1521,10 @@ 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->has_reconnect_ms = qemu_opt_find(opts, "reconnect-ms"); + sock->reconnect_ms = qemu_opt_get_number(opts, "reconnect-ms", 0); + 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); @@ -1498,7 +1554,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-stdio.c b/chardev/char-stdio.c index 3c648678ab..b960ddd4e4 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -41,6 +41,7 @@ /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; +static int old_fd1_flags; static bool stdio_in_use; static bool stdio_allow_signal; static bool stdio_echo_state; @@ -50,6 +51,8 @@ static void term_exit(void) if (stdio_in_use) { tcsetattr(0, TCSANOW, &oldtty); fcntl(0, F_SETFL, old_fd0_flags); + fcntl(1, F_SETFL, old_fd1_flags); + stdio_in_use = false; } } @@ -102,6 +105,7 @@ static void qemu_chr_open_stdio(Chardev *chr, stdio_in_use = true; old_fd0_flags = fcntl(0, F_GETFL); + old_fd1_flags = fcntl(1, F_GETFL); tcgetattr(0, &oldtty); if (!g_unix_set_fd_nonblocking(0, true, NULL)) { error_setg_errno(errp, errno, "Failed to set FD nonblocking"); 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 eb830eabd9..13325ca967 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -33,6 +33,7 @@ struct WinStdioChardev { Chardev parent; HANDLE hStdIn; + DWORD dwOldMode; HANDLE hInputReadyEvent; HANDLE hInputDoneEvent; HANDLE hInputThread; @@ -159,6 +160,7 @@ static void qemu_chr_open_stdio(Chardev *chr, } is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; + stdio->dwOldMode = dwMode; if (is_console) { if (qemu_add_wait_object(stdio->hStdIn, @@ -190,7 +192,7 @@ 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 */ @@ -221,6 +223,9 @@ static void char_win_stdio_finalize(Object *obj) { WinStdioChardev *stdio = WIN_STDIO_CHARDEV(obj); + if (stdio->hStdIn != INVALID_HANDLE_VALUE) { + SetConsoleMode(stdio->hStdIn, stdio->dwOldMode); + } if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) { CloseHandle(stdio->hInputReadyEvent); } diff --git a/chardev/char.c b/chardev/char.c index 852d9d5f1b..0c634ef5ba 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" @@ -174,6 +175,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) { @@ -254,7 +267,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) { @@ -340,7 +353,7 @@ static bool qemu_chr_is_busy(Chardev *s) { if (CHARDEV_IS_MUX(s)) { MuxChardev *d = MUX_CHARDEV(s); - return d->mux_cnt >= 0; + return d->mux_bitset != 0; } else { return s->be != NULL; } @@ -435,6 +448,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename, qemu_opt_set(opts, "path", p, &error_abort); return opts; } + if (strstart(filename, "pty:", &p)) { + qemu_opt_set(opts, "backend", "pty", &error_abort); + qemu_opt_set(opts, "path", p, &error_abort); + return opts; + } if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p) || strstart(filename, "tn3270:", &p) || @@ -516,9 +534,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); } @@ -539,7 +555,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; } @@ -552,19 +568,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; @@ -600,28 +603,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", @@ -653,13 +640,26 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) return backend; } -Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, - Error **errp) +static void qemu_chardev_set_replay(Chardev *chr, Error **errp) +{ + if (replay_mode != REPLAY_MODE_NONE) { + if (CHARDEV_GET_CLASS(chr)->chr_ioctl) { + error_setg(errp, "Replay: ioctl is not supported " + "for serial devices yet"); + return; + } + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); + replay_register_char_driver(chr); + } +} + +static Chardev *do_qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, + bool replay, Error **errp) { const ChardevClass *cc; - Chardev *chr = NULL; + Chardev *base = NULL, *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; @@ -695,11 +695,11 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, chr = qemu_chardev_new(bid ? bid : id, object_class_get_name(OBJECT_CLASS(cc)), backend, context, errp); - if (chr == NULL) { goto out; } + base = chr; if (bid) { Chardev *mux; qapi_free_ChardevBackend(backend); @@ -719,11 +719,25 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, out: qapi_free_ChardevBackend(backend); g_free(bid); + + if (replay && base) { + /* RR should be set on the base device, not the mux */ + qemu_chardev_set_replay(base, errp); + } + return chr; } -Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, - bool permit_mux_mon, GMainContext *context) +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, + Error **errp) +{ + /* XXX: should this really not record/replay? */ + return do_qemu_chr_new_from_opts(opts, context, false, errp); +} + +static Chardev *qemu_chr_new_from_name(const char *label, const char *filename, + bool permit_mux_mon, + GMainContext *context, bool replay) { const char *p; Chardev *chr; @@ -731,14 +745,22 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, Error *err = NULL; if (strstart(filename, "chardev:", &p)) { - return qemu_chr_find(p); + chr = qemu_chr_find(p); + if (replay && chr) { + qemu_chardev_set_replay(chr, &err); + if (err) { + error_report_err(err); + return NULL; + } + } + return chr; } opts = qemu_chr_parse_compat(label, filename, permit_mux_mon); if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, context, &err); + chr = do_qemu_chr_new_from_opts(opts, context, replay, &err); if (!chr) { error_report_err(err); goto out; @@ -760,24 +782,20 @@ out: return chr; } +Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, + bool permit_mux_mon, GMainContext *context) +{ + return qemu_chr_new_from_name(label, filename, permit_mux_mon, context, + false); +} + static Chardev *qemu_chr_new_permit_mux_mon(const char *label, const char *filename, bool permit_mux_mon, GMainContext *context) { - Chardev *chr; - chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context); - if (chr) { - if (replay_mode != REPLAY_MODE_NONE) { - qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); - } - if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) { - error_report("Replay: ioctl is not supported " - "for serial devices yet"); - } - replay_register_char_driver(chr); - } - return chr; + return qemu_chr_new_from_name(label, filename, permit_mux_mon, context, + true); } Chardev *qemu_chr_new(const char *label, const char *filename, @@ -800,7 +818,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); @@ -855,6 +873,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "path", .type = QEMU_OPT_STRING, + },{ + .name = "input-path", + .type = QEMU_OPT_STRING, },{ .name = "host", .type = QEMU_OPT_STRING, @@ -894,6 +915,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "reconnect", .type = QEMU_OPT_NUMBER, + },{ + .name = "reconnect-ms", + .type = QEMU_OPT_NUMBER, },{ .name = "telnet", .type = QEMU_OPT_BOOL, @@ -1086,7 +1110,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; @@ -1172,7 +1195,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; /* @@ -1189,7 +1212,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; @@ -1228,6 +1250,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/chardev-internal.h b/chardev/chardev-internal.h index 4e03af3147..853807f3cb 100644 --- a/chardev/chardev-internal.h +++ b/chardev/chardev-internal.h @@ -37,20 +37,19 @@ struct MuxChardev { Chardev parent; CharBackend *backends[MAX_MUX]; CharBackend chr; + unsigned long mux_bitset; int focus; - int mux_cnt; - int term_got_escape; - int max_size; + bool term_got_escape; /* Intermediate input buffer catches escape sequences even if the currently active device is not accepting any input - but only until it is full as well. */ unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE]; - int prod[MAX_MUX]; - int cons[MAX_MUX]; + unsigned int prod[MAX_MUX]; + unsigned int cons[MAX_MUX]; int timestamps; /* Protected by the Chardev chr_write_lock. */ - int linestart; + bool linestart; int64_t timestamps_start; }; typedef struct MuxChardev MuxChardev; @@ -60,7 +59,10 @@ DECLARE_INSTANCE_CHECKER(MuxChardev, MUX_CHARDEV, #define CHARDEV_IS_MUX(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) -void mux_set_focus(Chardev *chr, int focus); +bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b, + unsigned int *tag, Error **errp); +bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag); +void mux_set_focus(Chardev *chr, unsigned int focus); void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event); Object *get_chardevs_root(void); 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 ab8fe981d6..2279694cfa 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -81,7 +81,7 @@ static void msmouse_chr_accept_input(Chardev *chr) const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&mouse->outbuf, MIN(len, avail), &size); + buf = fifo8_pop_bufptr(&mouse->outbuf, MIN(len, avail), &size); qemu_chr_be_write(chr, buf, size); len = qemu_chr_be_can_write(chr); avail -= size; @@ -171,7 +171,7 @@ static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len) return len; } -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, 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/trace-events b/chardev/trace-events index 027107b0c1..7e97b8a988 100644 --- a/chardev/trace-events +++ b/chardev/trace-events @@ -17,3 +17,13 @@ spice_vmc_register_interface(void *scd) "spice vmc registered interface %p" spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p" spice_vmc_event(int event) "spice vmc event %d" +# char-socket.c +chr_socket_poll_err(void *chrdev, const char *label) "chardev socket poll error %p (%s)" +chr_socket_recv_err(void *chrdev, const char *label, const char *err) "chardev socket recv error %p (%s): %s" +chr_socket_recv_eof(void *chrdev, const char *label) "chardev socket recv end-of-file %p (%s)" +chr_socket_write_err(void *chrdev, const char *label, const char *err) "chardev socket write error %p (%s): %s" +chr_socket_disconnect(void *chrdev, const char *label) "chardev socket disconnect %p (%s)" +chr_socket_hangup(void *chrdev, const char *label) "chardev socket hangup %p (%s)" +chr_socket_ws_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket websock handshake error %p (%s): %s" +chr_socket_tls_handshake_err(void *chrdev, const char *label, const char *err) "chardev socket TLS handshake error %p (%s): %s" +chr_socket_tls_init_err(void *chrdev, const char *label, const char *err) "chardev socket TLS init error %p (%s): %s" diff --git a/chardev/wctablet.c b/chardev/wctablet.c index 43bdf6b608..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, diff --git a/common-user/host/ppc/safe-syscall.inc.S b/common-user/host/ppc/safe-syscall.inc.S new file mode 100644 index 0000000000..0851f6c0b8 --- /dev/null +++ b/common-user/host/ppc/safe-syscall.inc.S @@ -0,0 +1,107 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by common-user/safe-syscall.S + * + * Copyright (C) 2022 Linaro, Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Standardize on the _CALL_FOO symbols used by GCC: + * Apple XCode does not define _CALL_DARWIN. + * Clang defines _CALL_ELF (64-bit) but not _CALL_SYSV (32-bit). + */ +#if !defined(_CALL_SYSV) && \ + !defined(_CALL_DARWIN) && \ + !defined(_CALL_AIX) && \ + !defined(_CALL_ELF) +# if defined(__APPLE__) +# define _CALL_DARWIN +# elif defined(__ELF__) && TCG_TARGET_REG_BITS == 32 +# define _CALL_SYSV +# else +# error "Unknown ABI" +# endif +#endif + +#ifndef _CALL_SYSV +# error "Unsupported ABI" +#endif + + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + .text + + /* + * This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + */ +safe_syscall_base: + .cfi_startproc + stwu 1, -8(1) + .cfi_def_cfa_offset 8 + stw 30, 4(1) + .cfi_offset 30, -4 + + /* + * We enter with r3 == &signal_pending + * r4 == syscall number + * r5 ... r10 == syscall arguments + * and return the result in r3 + * and the syscall instruction needs + * r0 == syscall number + * r3 ... r8 == syscall arguments + * and returns the result in r3 + * Shuffle everything around appropriately. + */ + mr 30, 3 /* signal_pending */ + mr 0, 4 /* syscall number */ + mr 3, 5 /* syscall arguments */ + mr 4, 6 + mr 5, 7 + mr 6, 8 + mr 7, 9 + mr 8, 10 + + /* + * This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + lwz 12, 0(30) + cmpwi 0, 12, 0 + bne- 2f + sc +safe_syscall_end: + /* code path when we did execute the syscall */ + lwz 30, 4(1) /* restore r30 */ + addi 1, 1, 8 /* restore stack */ + .cfi_restore 30 + .cfi_def_cfa_offset 0 + bnslr+ /* return on success */ + b safe_syscall_set_errno_tail + + /* code path when we didn't execute the syscall */ +2: lwz 30, 4(1) + addi 1, 1, 8 + addi 3, 0, QEMU_ERESTARTSYS + b safe_syscall_set_errno_tail + + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/configs/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/alpha-softmmu/default.mak b/configs/devices/alpha-softmmu/default.mak index d186fe8e9b..3de6a9f577 100644 --- a/configs/devices/alpha-softmmu/default.mak +++ b/configs/devices/alpha-softmmu/default.mak @@ -5,6 +5,5 @@ #CONFIG_PCI_DEVICES=n #CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_DP264=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_DP264=n diff --git a/configs/devices/arm-softmmu/default.mak b/configs/devices/arm-softmmu/default.mak index 6985a25377..57ef1b8a70 100644 --- a/configs/devices/arm-softmmu/default.mak +++ b/configs/devices/arm-softmmu/default.mak @@ -1,44 +1,44 @@ # Default configuration for arm-softmmu +# Uncomment the following lines to disable these optional devices: +# CONFIG_I2C_DEVICES=n # CONFIG_PCI_DEVICES=n # 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 +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_ARM_VIRT=n + +# 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_SX1=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_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/avr-softmmu/default.mak b/configs/devices/avr-softmmu/default.mak index 80218add98..4207e7b3ce 100644 --- a/configs/devices/avr-softmmu/default.mak +++ b/configs/devices/avr-softmmu/default.mak @@ -1,5 +1,4 @@ # Default configuration for avr-softmmu -# Boards: -# -CONFIG_ARDUINO=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_ARDUINO=n diff --git a/configs/devices/cris-softmmu/default.mak b/configs/devices/cris-softmmu/default.mak deleted file mode 100644 index 5932cf4d06..0000000000 --- a/configs/devices/cris-softmmu/default.mak +++ /dev/null @@ -1,5 +0,0 @@ -# Default configuration for cris-softmmu - -# Boards: -# -CONFIG_AXIS=y diff --git a/configs/devices/hppa-softmmu/default.mak b/configs/devices/hppa-softmmu/default.mak index b0364bb88f..059510cdbb 100644 --- a/configs/devices/hppa-softmmu/default.mak +++ b/configs/devices/hppa-softmmu/default.mak @@ -4,6 +4,5 @@ # #CONFIG_PCI_DEVICES=n -# Boards: -# -CONFIG_HPPA_B160L=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_HPPA_B160L=n diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index b4c920b531..52062dfbd9 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -2,32 +2,83 @@ # Uncomment the following lines to disable these optional devices: # -#CONFIG_AMD_IOMMU=n -#CONFIG_APPLESMC=n -#CONFIG_FDC=n -#CONFIG_HPET=n -#CONFIG_HYPERV=n -#CONFIG_ISA_DEBUG=n -#CONFIG_ISA_IPMI_BT=n -#CONFIG_ISA_IPMI_KCS=n -#CONFIG_PCI_IPMI_KCS=n -#CONFIG_PCI_IPMI_BT=n -#CONFIG_IPMI_SSIF=n -#CONFIG_PCI_DEVICES=n -#CONFIG_PVPANIC=n -#CONFIG_QXL=n -#CONFIG_SEV=n -#CONFIG_SGA=n -#CONFIG_TEST_DEVICES=n -#CONFIG_TPM_CRB=n -#CONFIG_TPM_TIS_ISA=n -#CONFIG_VTD=n -#CONFIG_SGX=n +CONFIG_AMD_IOMMU=n +CONFIG_APPLESMC=n +CONFIG_FDC_ISA=n +CONFIG_FDC=n +CONFIG_HPET=n +CONFIG_HYPERV=n +CONFIG_ISA_DEBUG=n +CONFIG_ISA_IPMI_BT=n +CONFIG_ISA_IPMI_KCS=n +CONFIG_PCI_IPMI_KCS=n +CONFIG_PCI_IPMI_BT=n +CONFIG_IPMI_SSIF=n +CONFIG_PCI_DEVICES=n +CONFIG_QXL=n +CONFIG_SEV=n +CONFIG_TEST_DEVICES=n +CONFIG_TPM_CRB=n +CONFIG_TPM_TIS_ISA=n +CONFIG_VTD=n +CONFIG_SGX=n -# Boards: -# -# CONFIG_ISAPC=y -# CONFIG_I440FX=y -# CONFIG_Q35=y -# CONFIG_MICROVM=y +# Boards are selected by default, uncomment to keep out of the build. +CONFIG_ISAPC=n +CONFIG_I440FX=n +CONFIG_Q35=n +CONFIG_MICROVM=n +CONFIG_NITRO_ENCLAVE=n CONFIG_XBOX=y + +CONFIG_ADLIB=n +CONFIG_APPLESMC=n +CONFIG_CAN_CTUCANFD=n +CONFIG_CAN_CTUCANFD_PCI=n +CONFIG_CAN_PCI=n +CONFIG_CAN_SJA1000=n +CONFIG_CS4231A=n +CONFIG_CXL=n +CONFIG_E1000_PCI=n +CONFIG_E1000E_PCI_EXPRESS=n +CONFIG_EEPRO100_PCI=n +CONFIG_ES1370=n +CONFIG_FDC_ISA=n +CONFIG_GUS=n +CONFIG_HDA=n +CONFIG_HYPERV=n +CONFIG_IGB_PCI_EXPRESS=n +CONFIG_IPMI_SSIF=n +CONFIG_ISA_DEBUG=n +CONFIG_ISA_IPMI_BT=n +CONFIG_ISA_IPMI_KCS=n +CONFIG_NE2000_ISA=n +CONFIG_NE2000_PCI=n +CONFIG_NVDIMM=n +CONFIG_PARALLEL=n +CONFIG_PCI_EXPRESS=n +CONFIG_PCI_IPMI_BT=n +CONFIG_PCI_IPMI_KCS=n +CONFIG_PCNET_PCI=n +CONFIG_PVPANIC_ISA=n +CONFIG_QXL=n +CONFIG_ROCKER=n +CONFIG_RTL8139_PCI=n +CONFIG_SB16=n +CONFIG_SEV=n +CONFIG_SGX=n +CONFIG_TEST_DEVICES=n +CONFIG_TPM_CRB=n +CONFIG_TPM_TIS_ISA=n +CONFIG_TULIP=n +CONFIG_USB_EHCI=n +CONFIG_VFIO_AMD_XGBE=n +CONFIG_VFIO_AP=n +CONFIG_VFIO_CCW=n +CONFIG_VFIO_PCI=n +CONFIG_VFIO_PLATFORM=n +CONFIG_VFIO_XGMAC=n +CONFIG_VIRTIO_SND=n +CONFIG_VIRTIO_VGA=n +CONFIG_VMXNET3_PCI=n +CONFIG_PCI_TESTDEV=y diff --git a/configs/devices/loongarch64-softmmu/default.mak b/configs/devices/loongarch64-softmmu/default.mak index 928bc117ef..ffe705836f 100644 --- a/configs/devices/loongarch64-softmmu/default.mak +++ b/configs/devices/loongarch64-softmmu/default.mak @@ -1,3 +1,7 @@ # Default configuration for loongarch64-softmmu -CONFIG_LOONGARCH_VIRT=y +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_LOONGARCH_VIRT=n diff --git a/configs/devices/m68k-softmmu/default.mak b/configs/devices/m68k-softmmu/default.mak index 7f8619e427..3ceda6b041 100644 --- a/configs/devices/m68k-softmmu/default.mak +++ b/configs/devices/m68k-softmmu/default.mak @@ -1,11 +1,8 @@ # Default configuration for m68k-softmmu -CONFIG_SEMIHOSTING=y - -# Boards: -# -CONFIG_AN5206=y -CONFIG_MCF5208=y -CONFIG_NEXTCUBE=y -CONFIG_Q800=y -CONFIG_M68K_VIRT=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_AN5206=n +# CONFIG_MCF5208=n +# CONFIG_NEXTCUBE=n +# CONFIG_Q800=n +# CONFIG_M68K_VIRT=n diff --git a/configs/devices/microblaze-softmmu/default.mak b/configs/devices/microblaze-softmmu/default.mak index db8c6e4bba..7894106465 100644 --- a/configs/devices/microblaze-softmmu/default.mak +++ b/configs/devices/microblaze-softmmu/default.mak @@ -1,7 +1,4 @@ # Default configuration for microblaze-softmmu -# Boards: -# -CONFIG_PETALOGIX_S3ADSP1800=y -CONFIG_PETALOGIX_ML605=y -CONFIG_XLNX_ZYNQMP_PMU=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_PETALOGIX_S3ADSP1800=n diff --git a/configs/devices/microblazeel-softmmu/default.mak b/configs/devices/microblazeel-softmmu/default.mak index 29f7f13816..4c1086435b 100644 --- a/configs/devices/microblazeel-softmmu/default.mak +++ b/configs/devices/microblazeel-softmmu/default.mak @@ -1,3 +1,6 @@ # Default configuration for microblazeel-softmmu -include ../microblaze-softmmu/default.mak +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_PETALOGIX_S3ADSP1800=n +# CONFIG_PETALOGIX_ML605=n +# CONFIG_XLNX_ZYNQMP_PMU=n diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak index 416161f833..b50107feaf 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -1,37 +1,9 @@ # 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_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 +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_MALTA=n +# CONFIG_MIPSSIM=n diff --git a/configs/devices/mips64-softmmu/default.mak b/configs/devices/mips64-softmmu/default.mak index 566672f3c2..1b8d7ced1c 100644 --- a/configs/devices/mips64-softmmu/default.mak +++ b/configs/devices/mips64-softmmu/default.mak @@ -1,4 +1,6 @@ # Default configuration for mips64-softmmu include ../mips-softmmu/common.mak -CONFIG_JAZZ=y + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_JAZZ=n diff --git a/configs/devices/mips64el-softmmu/default.mak b/configs/devices/mips64el-softmmu/default.mak index d5188f7ea5..9dce346c4f 100644 --- a/configs/devices/mips64el-softmmu/default.mak +++ b/configs/devices/mips64el-softmmu/default.mak @@ -1,10 +1,9 @@ # Default configuration for mips64el-softmmu include ../mips-softmmu/common.mak -CONFIG_FULOONG=y -CONFIG_LOONGSON3V=y -CONFIG_ATI_VGA=y -CONFIG_RTL8139_PCI=y -CONFIG_JAZZ=y -CONFIG_VT82C686=y -CONFIG_MIPS_BOSTON=y + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_FULOONG=n +# CONFIG_LOONGSON3V=n +# CONFIG_JAZZ=n +# CONFIG_MIPS_BOSTON=n diff --git a/configs/devices/nios2-softmmu/default.mak b/configs/devices/nios2-softmmu/default.mak deleted file mode 100644 index 1bc4082ea9..0000000000 --- a/configs/devices/nios2-softmmu/default.mak +++ /dev/null @@ -1,8 +0,0 @@ -# Default configuration for nios2-softmmu - -CONFIG_SEMIHOSTING=y - -# Boards: -# -CONFIG_NIOS2_10M50=y -CONFIG_NIOS2_GENERIC_NOMMU=y diff --git a/configs/devices/or1k-softmmu/default.mak b/configs/devices/or1k-softmmu/default.mak index 89c39e3123..efe3bc278b 100644 --- a/configs/devices/or1k-softmmu/default.mak +++ b/configs/devices/or1k-softmmu/default.mak @@ -1,6 +1,9 @@ # Default configuration for or1k-softmmu +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n + # Boards: -# -CONFIG_OR1K_SIM=y -CONFIG_OR1K_VIRT=y +# CONFIG_OR1K_SIM=n +# CONFIG_OR1K_VIRT=n diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak index a887f5438b..460d15e676 100644 --- a/configs/devices/ppc-softmmu/default.mak +++ b/configs/devices/ppc-softmmu/default.mak @@ -1,20 +1,27 @@ # Default configuration for ppc-softmmu -# For embedded PPCs: -CONFIG_E500PLAT=y -CONFIG_MPC8544DS=y -CONFIG_PPC405=y -CONFIG_PPC440=y -CONFIG_VIRTEX=y +# Uncomment the following lines to disable these optional devices: +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n + +# Boards are selected by default, uncomment to keep out of the build. + +# Embedded PPCs: +# CONFIG_E500PLAT=n +# CONFIG_MPC8544DS=n +# CONFIG_PPC405=n +# CONFIG_PPC440=n +# CONFIG_VIRTEX=n # For Sam460ex -CONFIG_SAM460EX=y +# CONFIG_SAM460EX=n # For Macs -CONFIG_MAC_OLDWORLD=y -CONFIG_MAC_NEWWORLD=y +# CONFIG_MAC_OLDWORLD=n +# CONFIG_MAC_NEWWORLD=n -CONFIG_PEGASOS2=y +# CONFIG_AMIGAONE=n +# CONFIG_PEGASOS2=n # For PReP -CONFIG_PREP=y +# CONFIG_PREP=n diff --git a/configs/devices/ppc64-softmmu/default.mak b/configs/devices/ppc64-softmmu/default.mak index b90e5bf455..e8ad260313 100644 --- a/configs/devices/ppc64-softmmu/default.mak +++ b/configs/devices/ppc64-softmmu/default.mak @@ -3,8 +3,6 @@ # Include all 32-bit boards include ../ppc-softmmu/default.mak -# For PowerNV -CONFIG_POWERNV=y - -# For pSeries -CONFIG_PSERIES=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_POWERNV=n +# CONFIG_PSERIES=n diff --git a/configs/devices/riscv32-softmmu/default.mak b/configs/devices/riscv32-softmmu/default.mak index d847bd5692..c2cd86ce05 100644 --- a/configs/devices/riscv32-softmmu/default.mak +++ b/configs/devices/riscv32-softmmu/default.mak @@ -1,15 +1,12 @@ # Default configuration for riscv32-softmmu # Uncomment the following lines to disable these optional devices: -# -#CONFIG_PCI_DEVICES=n -CONFIG_SEMIHOSTING=y -CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_SPIKE=y -CONFIG_SIFIVE_E=y -CONFIG_SIFIVE_U=y -CONFIG_RISCV_VIRT=y -CONFIG_OPENTITAN=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SPIKE=n +# CONFIG_SIFIVE_E=n +# CONFIG_SIFIVE_U=n +# CONFIG_RISCV_VIRT=n +# CONFIG_OPENTITAN=n diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index bc69301fa4..39ed3a0061 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -1,16 +1,13 @@ # Default configuration for riscv64-softmmu # Uncomment the following lines to disable these optional devices: -# -#CONFIG_PCI_DEVICES=n -CONFIG_SEMIHOSTING=y -CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +# CONFIG_PCI_DEVICES=n +# CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_SPIKE=y -CONFIG_SIFIVE_E=y -CONFIG_SIFIVE_U=y -CONFIG_RISCV_VIRT=y -CONFIG_MICROCHIP_PFSOC=y -CONFIG_SHAKTI_C=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SPIKE=n +# CONFIG_SIFIVE_E=n +# CONFIG_SIFIVE_U=n +# CONFIG_RISCV_VIRT=n +# CONFIG_MICROCHIP_PFSOC=n +# CONFIG_SHAKTI_C=n diff --git a/configs/devices/rx-softmmu/default.mak b/configs/devices/rx-softmmu/default.mak index df2b4e4f42..e7caebe197 100644 --- a/configs/devices/rx-softmmu/default.mak +++ b/configs/devices/rx-softmmu/default.mak @@ -1,3 +1,4 @@ # Default configuration for rx-softmmu -CONFIG_RX_GDBSIM=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_RX_GDBSIM=n diff --git a/configs/devices/s390x-softmmu/default.mak b/configs/devices/s390x-softmmu/default.mak index f2287a133f..340c109292 100644 --- a/configs/devices/s390x-softmmu/default.mak +++ b/configs/devices/s390x-softmmu/default.mak @@ -7,7 +7,7 @@ #CONFIG_VFIO_CCW=n #CONFIG_VIRTIO_PCI=n #CONFIG_WDT_DIAG288=n +#CONFIG_PCIE_DEVICES=n -# Boards: -# -CONFIG_S390_CCW_VIRTIO=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_S390_CCW_VIRTIO=n diff --git a/configs/devices/sh4-softmmu/default.mak b/configs/devices/sh4-softmmu/default.mak index 565e8b0b5d..efb401bfb1 100644 --- a/configs/devices/sh4-softmmu/default.mak +++ b/configs/devices/sh4-softmmu/default.mak @@ -1,11 +1,9 @@ -# Default configuration for sh4eb-softmmu +# Default configuration for sh4-softmmu # Uncomment the following lines to disable these optional devices: # #CONFIG_PCI_DEVICES=n #CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_R2D=y -CONFIG_SHIX=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_R2D=n diff --git a/configs/devices/sparc-softmmu/default.mak b/configs/devices/sparc-softmmu/default.mak index ee85218115..87668fda5e 100644 --- a/configs/devices/sparc-softmmu/default.mak +++ b/configs/devices/sparc-softmmu/default.mak @@ -5,7 +5,6 @@ #CONFIG_TCX=n #CONFIG_CG3=n -# Boards: -# -CONFIG_SUN4M=y -CONFIG_LEON3=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SUN4M=n +# CONFIG_LEON3=n diff --git a/configs/devices/sparc64-softmmu/default.mak b/configs/devices/sparc64-softmmu/default.mak index e50030a229..fa82f39a20 100644 --- a/configs/devices/sparc64-softmmu/default.mak +++ b/configs/devices/sparc64-softmmu/default.mak @@ -6,7 +6,6 @@ #CONFIG_SUNHME=n #CONFIG_TEST_DEVICES=n -# Boards: -# -CONFIG_SUN4U=y -CONFIG_NIAGARA=y +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_SUN4U=n +# CONFIG_NIAGARA=n diff --git a/configs/devices/tricore-softmmu/default.mak b/configs/devices/tricore-softmmu/default.mak index cb8fc286eb..c7ab542244 100644 --- a/configs/devices/tricore-softmmu/default.mak +++ b/configs/devices/tricore-softmmu/default.mak @@ -1,2 +1,5 @@ -CONFIG_TRICORE_TESTBOARD=y -CONFIG_TRIBOARD=y +# Default configuration for tricore-softmmu + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_TRICORE_TESTBOARD=n +# CONFIG_TRIBOARD=n diff --git a/configs/devices/xtensa-softmmu/default.mak b/configs/devices/xtensa-softmmu/default.mak index 4fe1bf00c9..fbc3079a94 100644 --- a/configs/devices/xtensa-softmmu/default.mak +++ b/configs/devices/xtensa-softmmu/default.mak @@ -1,9 +1,10 @@ # Default configuration for Xtensa -CONFIG_SEMIHOSTING=y - -# Boards: +# Uncomment the following lines to disable these optional devices: # -CONFIG_XTENSA_SIM=y -CONFIG_XTENSA_VIRT=y -CONFIG_XTENSA_XTFPGA=y +#CONFIG_PCI_DEVICES=n + +# Boards are selected by default, uncomment to keep out of the build. +# CONFIG_XTENSA_SIM=n +# CONFIG_XTENSA_VIRT=n +# CONFIG_XTENSA_XTFPGA=n 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-bsd-user.mak b/configs/targets/aarch64-bsd-user.mak new file mode 100644 index 0000000000..8aaa5d8c80 --- /dev/null +++ b/configs/targets/aarch64-bsd-user.mak @@ -0,0 +1,3 @@ +TARGET_ARCH=aarch64 +TARGET_BASE_ARCH=arm +TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml diff --git a/configs/targets/aarch64-linux-user.mak b/configs/targets/aarch64-linux-user.mak index db552f1839..4c6570f56a 100644 --- a/configs/targets/aarch64-linux-user.mak +++ b/configs/targets/aarch64-linux-user.mak @@ -1,6 +1,8 @@ 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 gdb-xml/aarch64-mte.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +TARGET_SYSTBL_ABI=common,64,renameat,rlimit,memfd_secret +TARGET_SYSTBL=syscall_64.tbl diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index d489e6da83..84cb32dc2f 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -1,5 +1,7 @@ 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_KVM_HAVE_GUEST_DEBUG=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 gdb-xml/aarch64-pauth.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/aarch64_be-linux-user.mak b/configs/targets/aarch64_be-linux-user.mak index dc78044fb1..dcef597a80 100644 --- a/configs/targets/aarch64_be-linux-user.mak +++ b/configs/targets/aarch64_be-linux-user.mak @@ -1,7 +1,9 @@ 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 gdb-xml/aarch64-mte.xml TARGET_HAS_BFLT=y CONFIG_SEMIHOSTING=y CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y +TARGET_SYSTBL_ABI=common,64,renameat,rlimit,memfd_secret +TARGET_SYSTBL=syscall_64.tbl 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-softmmu.mak b/configs/targets/arm-softmmu.mak index 92c8349b96..bf390b7a8d 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=arm TARGET_SUPPORTS_MTTCG=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 +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/cris-linux-user.mak b/configs/targets/cris-linux-user.mak deleted file mode 100644 index e483c42066..0000000000 --- a/configs/targets/cris-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -TARGET_ARCH=cris diff --git a/configs/targets/cris-softmmu.mak b/configs/targets/cris-softmmu.mak deleted file mode 100644 index e483c42066..0000000000 --- a/configs/targets/cris-softmmu.mak +++ /dev/null @@ -1 +0,0 @@ -TARGET_ARCH=cris diff --git a/configs/targets/hexagon-linux-user.mak b/configs/targets/hexagon-linux-user.mak index 003ed0a408..b912045bd3 100644 --- a/configs/targets/hexagon-linux-user.mak +++ b/configs/targets/hexagon-linux-user.mak @@ -1 +1,4 @@ TARGET_ARCH=hexagon +TARGET_XML_FILES=gdb-xml/hexagon-core.xml gdb-xml/hexagon-hvx.xml +TARGET_SYSTBL=syscall.tbl +TARGET_SYSTBL_ABI=common,32,hexagon,time32,stat64,rlimit,renameat 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/i386-linux-user.mak b/configs/targets/i386-linux-user.mak index 5b2546a430..b72a156473 100644 --- a/configs/targets/i386-linux-user.mak +++ b/configs/targets/i386-linux-user.mak @@ -1,4 +1,4 @@ TARGET_ARCH=i386 TARGET_SYSTBL_ABI=i386 TARGET_SYSTBL=syscall_32.tbl -TARGET_XML_FILES= gdb-xml/i386-32bit.xml +TARGET_XML_FILES= gdb-xml/i386-32bit.xml gdb-xml/i386-32bit-linux.xml diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 6b3c99fc86..2ac69d5ba3 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=i386 TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak index 7d1b964020..dfded79dfa 100644 --- a/configs/targets/loongarch64-linux-user.mak +++ b/configs/targets/loongarch64-linux-user.mak @@ -1,3 +1,6 @@ # 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 gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml +TARGET_SYSTBL=syscall.tbl +TARGET_SYSTBL_ABI=common,64 diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index 9abc99056f..ce19ab6a16 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -1,5 +1,7 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_SUPPORTS_MTTCG=y -TARGET_XML_FILES= gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml +TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml +# all boards require libfdt 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..eea266d4f3 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -1,4 +1,6 @@ TARGET_ARCH=microblaze TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +# needed by boot.c 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..77b968acad 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -1,3 +1,5 @@ TARGET_ARCH=microblaze TARGET_SUPPORTS_MTTCG=y +# needed by boot.c 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..3864daa736 100644 --- a/configs/targets/mips64el-softmmu.mak +++ b/configs/targets/mips64el-softmmu.mak @@ -1,4 +1,2 @@ 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-linux-user.mak b/configs/targets/nios2-linux-user.mak deleted file mode 100644 index 9a372f0717..0000000000 --- a/configs/targets/nios2-linux-user.mak +++ /dev/null @@ -1 +0,0 @@ -TARGET_ARCH=nios2 diff --git a/configs/targets/nios2-softmmu.mak b/configs/targets/nios2-softmmu.mak deleted file mode 100644 index 1e93b54cd1..0000000000 --- a/configs/targets/nios2-softmmu.mak +++ /dev/null @@ -1,2 +0,0 @@ -TARGET_ARCH=nios2 -TARGET_ALIGNED_ONLY=y diff --git a/configs/targets/or1k-linux-user.mak b/configs/targets/or1k-linux-user.mak index 39558f77ec..eecb1e2241 100644 --- a/configs/targets/or1k-linux-user.mak +++ b/configs/targets/or1k-linux-user.mak @@ -1,2 +1,4 @@ TARGET_ARCH=openrisc TARGET_BIG_ENDIAN=y +TARGET_SYSTBL_ABI=common,32,or1k,time32,stat64,rlimit,renameat +TARGET_SYSTBL=syscall.tbl diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index 432f855a30..0341cb2a6b 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=openrisc TARGET_SUPPORTS_MTTCG=y TARGET_BIG_ENDIAN=y +# needed by boot.c and all boards TARGET_NEED_FDT=y diff --git a/configs/targets/ppc-softmmu.mak b/configs/targets/ppc-softmmu.mak index 774440108f..53120dab41 100644 --- a/configs/targets/ppc-softmmu.mak +++ b/configs/targets/ppc-softmmu.mak @@ -1,4 +1,4 @@ TARGET_ARCH=ppc TARGET_BIG_ENDIAN=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml -TARGET_NEED_FDT=y diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index ddf0c39617..40881d9396 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -2,5 +2,7 @@ TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_BIG_ENDIAN=y TARGET_SUPPORTS_MTTCG=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml +# all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/riscv32-linux-user.mak b/configs/targets/riscv32-linux-user.mak index 9761618e67..0dbaf5210a 100644 --- a/configs/targets/riscv32-linux-user.mak +++ b/configs/targets/riscv32-linux-user.mak @@ -4,3 +4,6 @@ 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 +TARGET_SYSTBL_ABI=32 +TARGET_SYSTBL_ABI=common,32,riscv,memfd_secret +TARGET_SYSTBL=syscall.tbl diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak index d8b71cddcd..338182d5b8 100644 --- a/configs/targets/riscv32-softmmu.mak +++ b/configs/targets/riscv32-softmmu.mak @@ -2,4 +2,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y 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 +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/riscv64-bsd-user.mak b/configs/targets/riscv64-bsd-user.mak new file mode 100644 index 0000000000..191c2c483f --- /dev/null +++ b/configs/targets/riscv64-bsd-user.mak @@ -0,0 +1,4 @@ +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 diff --git a/configs/targets/riscv64-linux-user.mak b/configs/targets/riscv64-linux-user.mak index cfd1fd382f..477cd4523e 100644 --- a/configs/targets/riscv64-linux-user.mak +++ b/configs/targets/riscv64-linux-user.mak @@ -4,3 +4,6 @@ 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 +TARGET_SYSTBL_ABI=64 +TARGET_SYSTBL_ABI=common,64,riscv,rlimit,memfd_secret +TARGET_SYSTBL=syscall.tbl diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index 7c0e7eeb42..6c5de72e03 100644 --- a/configs/targets/riscv64-softmmu.mak +++ b/configs/targets/riscv64-softmmu.mak @@ -1,5 +1,7 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv TARGET_SUPPORTS_MTTCG=y -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 +TARGET_KVM_HAVE_GUEST_DEBUG=y +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 gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-virtual.xml +# needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/rx-softmmu.mak b/configs/targets/rx-softmmu.mak index 0c458b2d07..706bbe6062 100644 --- a/configs/targets/rx-softmmu.mak +++ b/configs/targets/rx-softmmu.mak @@ -1,3 +1,4 @@ TARGET_ARCH=rx TARGET_XML_FILES= gdb-xml/rx-core.xml +# all boards require libfdt TARGET_NEED_FDT=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..b22218aacc 100644 --- a/configs/targets/s390x-softmmu.mak +++ b/configs/targets/s390x-softmmu.mak @@ -1,4 +1,5 @@ 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_KVM_HAVE_GUEST_DEBUG=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-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/configs/targets/x86_64-linux-user.mak b/configs/targets/x86_64-linux-user.mak index 9ceefbb615..86042814d3 100644 --- a/configs/targets/x86_64-linux-user.mak +++ b/configs/targets/x86_64-linux-user.mak @@ -2,4 +2,4 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall_64.tbl -TARGET_XML_FILES= gdb-xml/i386-64bit.xml +TARGET_XML_FILES= gdb-xml/i386-64bit.xml gdb-xml/i386-64bit-linux.xml diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index 197817c943..e12ac3dc59 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -1,5 +1,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 TARGET_SUPPORTS_MTTCG=y -TARGET_NEED_FDT=y +TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/configure b/configure index e4b5993b6f..c660bcfbe4 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 @@ -14,7 +13,7 @@ export CCACHE_RECACHE=yes # make source path absolute source_path=$(cd "$(dirname -- "$0")"; pwd) -if test "$PWD" = "$source_path" +if test "$PWD" -ef "$source_path" then echo "Using './build' as the directory for build output" @@ -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,8 +49,9 @@ endif then \ rm -rf build GNUmakefile ; \ fi -force: ; -.PHONY: force +%: build + @ +.PHONY: build GNUmakefile: ; EOF @@ -75,7 +74,6 @@ fi TMPB="qemu-conf" TMPC="${TMPDIR1}/${TMPB}.c" TMPO="${TMPDIR1}/${TMPB}.o" -TMPM="${TMPDIR1}/${TMPB}.m" TMPE="${TMPDIR1}/${TMPB}.exe" rm -f config.log @@ -83,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 @@ -99,10 +98,6 @@ print_error() { shift done echo) >&2 -} - -error_exit() { - print_error "$@" exit 1 } @@ -120,62 +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_objc() { - do_compiler_werror "$objcc" $CPU_CFLAGS "$@" -} - -# Append $2 to the variable named $1, with space separation -add_to() { - eval $1=\${$1:+\"\$$1 \"}\$2 + 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". @@ -210,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 ui/thirdparty/imgui ui/thirdparty/implot genconfig" -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 @@ -255,54 +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="no" -# 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="" -ninja="" -bindir="bin" -skip_meson=no -vfio_user_server="disabled" - -# The following Meson options are handled manually (still they -# are included in the automatically generated help message) - -# 1. Track which submodules are needed -fdt="auto" - -# 2. Automatically enable/disable other options -tcg="auto" -cfi="false" - -# parse CC options second for opt do optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)') case "$opt" in @@ -313,6 +205,10 @@ for opt do ;; --cxx=*) CXX="$optarg" ;; + --objcc=*) objcc="$optarg" + ;; + --rustc=*) RUSTC="$optarg" + ;; --cpu=*) cpu="$optarg" ;; --extra-cflags=*) @@ -339,18 +235,52 @@ 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" +rust="disabled" +rust_target_triple="" +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 @@ -361,40 +291,38 @@ 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" +readelf="${READELF-${cross_prefix}readelf}" 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= - +rustc="${RUSTC-rustc}" check_define() { cat > $TMPC < $TMPC < -int main(void) { return 0; } -EOF - compile_object -} - write_c_skeleton() { cat > $TMPC < $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= @@ -627,28 +604,13 @@ 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 "$targetos" = windows; then + (if test "$host_os" = windows; then IFS=\; else IFS=: @@ -666,7 +628,10 @@ meson_option_build_array() { 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 "$@")" @@ -676,6 +641,17 @@ meson_option_parse() { exit 1 fi } +has_meson_option() { + test "${meson_options#*"$1"}" != "$meson_options" +} + +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[^=]*=\(.*\)') @@ -684,8 +660,6 @@ for opt do ;; --version|-V) exec cat $source_path/QEMU_VERSION ;; - --prefix=*) prefix="$optarg" - ;; --cross-prefix=*) ;; --cc=*) @@ -694,22 +668,20 @@ for opt do ;; --cxx=*) ;; - --objcc=*) objcc="$optarg" + --objcc=*) ;; - --make=*) make="$optarg" + --rustc=*) + ;; + --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=*) @@ -722,15 +694,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=*) ;; @@ -762,13 +728,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|\ @@ -780,37 +740,25 @@ 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" + default_cflags='-O0 -g' ;; --disable-fortify-source) fortify_source="no" ;; --enable-fortify-source) fortify_source="yes" ;; --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" ; @@ -829,55 +777,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" ;; @@ -885,96 +793,89 @@ for opt do ;; --disable-containers) use_containers="no" ;; + --container-engine=*) container_engine="$optarg" + ;; + --rust-target-triple=*) rust_target_triple="$optarg" + ;; --gdb=*) gdb_bin="$optarg" ;; - # backwards compatibility options - --enable-trace-backend=*) meson_option_parse "--enable-trace-backends=$optarg" "$optarg" + --enable-rust) rust=enabled ;; - --disable-blobs) meson_option_parse --disable-install-blobs "" - ;; - --enable-vfio-user-server) vfio_user_server="enabled" - ;; - --disable-vfio-user-server) vfio_user_server="disabled" - ;; - --enable-tcmalloc) meson_option_parse --enable-malloc=tcmalloc tcmalloc - ;; - --enable-jemalloc) meson_option_parse --enable-malloc=jemalloc jemalloc + --disable-rust) rust=disabled ;; # everything else has the same name in configure and meson --*) meson_option_parse "$opt" "$optarg" ;; + # Pass through -Dxxxx options to meson + -D*) meson_option_add "$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)" @@ -991,19 +892,20 @@ 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] + --rustc=RUSTC use Rust compiler RUSTC [$rustc] --extra-cflags=CFLAGS append extra C compiler flags CFLAGS --extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS --extra-objcflags=OBJCFLAGS append extra Objective C compiler flags OBJCFLAGS @@ -1011,34 +913,19 @@ 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 + --rust-target-triple=TRIPLE compilation target for Rust code [autodetect] + --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 @@ -1048,79 +935,106 @@ 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 exit 0 fi +# Now that we are sure that the user did not only want to print the --help +# information, we should double-check that the C compiler really works: +write_c_skeleton +if ! compile_object ; then + error_exit "C compiler \"$cc\" either does not exist or does not work." +fi + # Remove old dependency files to make sure that they get properly regenerated 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 + +# 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.61.5; 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 + +$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 @@ -1135,32 +1049,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 @@ -1168,157 +1057,32 @@ 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 -add_to nowarn_flags -Wno-gnu-variable-sized-type-not-at-end - -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[argc - 1]; - 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" && test $host_bits -eq 64; then + if has_meson_option "-Dtcg_interpreter=true"; then + plugins="no" + else + plugins=yes + fi fi -test "$plugins" = "" && plugins=yes cat > $TMPC << EOF @@ -1331,66 +1095,30 @@ static THREAD int tls_var; int main(void) { return tls_var; } EOF -# Meson currently only handles pie as a boolean for now so if we have -# explicitly disabled PIE we need to extend our cflags because it wont. - -if echo | $cc -dM -E - | grep __clang__ > /dev/null 2>&1; then - NO_PIE_LDFLAG="" -else - NO_PIE_LDFLAG="-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" +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 + echo "Disabling PIE due to missing toolchain support" pie="no" - QEMU_CFLAGS="-fno-pie $NO_PIE_LDFLAG $QEMU_CFLAGS" fi -elif test "$pie" = "no"; then - if compile_prog "-Werror -fno-pie" $NO_PIE_LDFLAG; then - CONFIGURE_CFLAGS="-fno-pie $CONFIGURE_CFLAGS" - CONFIGURE_LDFLAGS="$NO_PIE_LDFLAG $CONFIGURE_LDFLAGS" - QEMU_CFLAGS="-fno-pie $NO_PIE_LDFLAG $QEMU_CFLAGS" - fi -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 ########################################## -# __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 -z "${target_list+xxx}" ; then default_targets=yes @@ -1414,14 +1142,6 @@ 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" @@ -1430,9 +1150,18 @@ if test "$tcg" = "auto"; then 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" +######################################### +# gdb test + +if test -n "$gdb_bin"; then + gdb_version_string=$($gdb_bin --version | head -n 1) + # Extract last field in the version string + gdb_version=${gdb_version_string##* } + 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 ########################################## @@ -1462,103 +1191,9 @@ EOF fi 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) -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 - ########################################## # epoxy probe + if $pkg_config --libs --silence-errors epoxy > /dev/null 2>&1 ; then epoxy_libs=$($pkg_config --libs --silence-errors epoxy) epoxy_cflags=$($pkg_config --cflags --silence-errors epoxy) @@ -1568,232 +1203,128 @@ else fi ########################################## -# check and set a backend for coroutine +# detect rust triple -# 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 test "$rust" != disabled && has "$rustc" && $rustc -vV > "${TMPDIR1}/${TMPB}.out"; then + rust_host_triple=$(sed -n 's/^host: //p' "${TMPDIR1}/${TMPB}.out") else - case $coroutine in + if test "$rust" = enabled; then + error_exit "could not execute rustc binary \"$rustc\"" + fi + rust=disabled +fi +if test "$rust" != disabled && test -z "$rust_target_triple"; then + # arch and os generally matches between meson and rust + rust_arch=$host_arch + rust_os=$host_os + rust_machine=unknown + rust_osvariant= + + # tweak rust_os if needed; also, machine and variant depend on the OS + android=no + case "$host_os" in + darwin) + # e.g. aarch64-apple-darwin + rust_machine=apple + ;; + + linux) + # detect android/glibc/musl + if check_define __ANDROID__; then + rust_osvariant=android + android=yes + else + cat > $TMPC << EOF +#define _GNU_SOURCE +#include +#ifndef __USE_GNU +error using musl +#endif +EOF + if compile_object; then + rust_osvariant=gnu + else + rust_osvariant=musl + fi + fi + + case "$cpu" in + arm) + # e.g. arm-unknown-linux-gnueabi, arm-unknown-linux-gnueabihf + write_c_skeleton + compile_object + if $READELF -A $TMPO | grep Tag_API_VFP_args: > /dev/null; then + rust_osvariant=${rust_osvariant}eabihf + else + rust_osvariant=${rust_osvariant}eabi + fi + ;; + + mips64) + # e.g. mips64-unknown-linux-gnuabi64 + rust_osvariant=${rust_osvariant}abi64 + ;; + esac + ;; + + netbsd) + # e.g. arm-unknown-netbsd-eabihf + test "$host_arch" = arm && rust_osvariant=eabihf + ;; + + sunos) + rust_machine=pc + rust_os=solaris + ;; + windows) - if test "$mingw32" != "yes"; then - error_exit "'windows' coroutine backend only valid for Windows" + # e.g. aarch64-pc-windows-gnullvm, x86_64-pc-windows-gnu (MSVC not supported) + rust_machine=pc + if test "$host_arch" = aarch64; then + rust_osvariant=gnullvm + else + rust_osvariant=gnu 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 - error_exit "'ucontext' backend requested but makecontext not available" - 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 + # now tweak the architecture part, possibly based on pre-canonicalization --cpu + case "$host_arch" in + arm) + # preserve ISA version (armv7 etc.) from $raw_cpu if passed via --cpu + rust_arch=$raw_cpu + test "$rust_arch" = arm && test "$rust_os" != linux && rust_arch=armv7 + ;; + mips) + # preserve ISA version (mipsisa64r6 etc.) and include endianness + rust_arch=${raw_cpu%el} + test "$bigendian" = no && rust_arch=${rust_arch}el + ;; -if test "$safe_stack" = "yes"; then -cat > $TMPC << EOF -int main(void) -{ -#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(void) -{ -#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" + riscv32|riscv64) + # e.g. riscv64gc-unknown-linux-gnu, but riscv64-linux-android + test "$android" = no && rust_arch=${rust_arch}gc + ;; + + sparc64) + if test "$rust_os" = solaris; then + rust_arch=sparcv9 + rust_machine=sun 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" + ;; + + x86_64) + # e.g. x86_64-unknown-linux-gnux32 + test "$raw_cpu" = x32 && rust_osvariant=${rust_osvariant}x32 + ;; + esac + + if test "$android" = yes; then + # e.g. aarch64-linux-android + rust_target_triple=$rust_arch-$rust_os-$rust_osvariant 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(void) -{ - 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; -} -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 - 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 + rust_target_triple=$rust_arch-$rust_machine-$rust_os${rust_osvariant:+-$rust_osvariant} fi fi @@ -1801,14 +1332,16 @@ fi # functions to probe cross compilers container="no" +runc="" if test $use_containers = "yes" && (has "docker" || has "podman"); then - case $($python "$source_path"/tests/docker/docker.py probe) in + 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 @@ -1828,7 +1361,6 @@ fi : ${cross_prefix_mips64="mips64-linux-gnuabi64-"} : ${cross_prefix_mipsel="mipsel-linux-gnu-"} : ${cross_prefix_mips="mips-linux-gnu-"} -: ${cross_prefix_nios2="nios2-linux-gnu-"} : ${cross_prefix_ppc="powerpc-linux-gnu-"} : ${cross_prefix_ppc64="powerpc64-linux-gnu-"} : ${cross_prefix_ppc64le="$cross_prefix_ppc64"} @@ -1837,6 +1369,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"} @@ -1844,7 +1377,7 @@ 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 -mbig-endian"} : ${cross_cc_cflags_ppc64="-m64 -mbig-endian"} @@ -1853,7 +1386,7 @@ fi : ${cross_cc_cflags_sparc64="-m64 -mcpu=ultrasparc"} : ${cross_cc_sparc="$cross_cc_sparc64"} : ${cross_cc_cflags_sparc="-m32 -mcpu=supersparc"} -: ${cross_cc_cflags_x86_64="-m64"} +: ${cross_cc_cflags_x86_64="-m64 -mcx16"} compute_target_variable() { eval "$2=" @@ -1890,6 +1423,7 @@ probe_target_compiler() { got_cross_cc=no container_image= container_hosts= + container_cross_prefix= container_cross_cc= container_cross_ar= container_cross_as= @@ -1899,21 +1433,12 @@ probe_target_compiler() { container_cross_ranlib= container_cross_strip= - # We shall skip configuring the target compiler if the user didn't - # bother enabling an appropriate guest. This avoids building - # extraneous firmware images and tests. - if test "${target_list#*$1}" != "$1"; then - break; - else - return 1 - fi - target_arch=${1%%-*} case $target_arch in aarch64) container_hosts="x86_64 aarch64" ;; + aarch64_be) container_hosts="x86_64 aarch64" ;; alpha) container_hosts=x86_64 ;; arm) container_hosts="x86_64 aarch64" ;; - cris) container_hosts=x86_64 ;; hexagon) container_hosts=x86_64 ;; hppa) container_hosts=x86_64 ;; i386) container_hosts=x86_64 ;; @@ -1924,7 +1449,6 @@ probe_target_compiler() { mips64) container_hosts=x86_64 ;; mipsel) container_hosts=x86_64 ;; mips) container_hosts=x86_64 ;; - nios2) container_hosts=x86_64 ;; ppc) container_hosts=x86_64 ;; ppc64|ppc64le) container_hosts=x86_64 ;; riscv64) container_hosts=x86_64 ;; @@ -1932,7 +1456,7 @@ 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 @@ -1940,115 +1464,79 @@ probe_target_compiler() { test "$container" != no || continue test "$host" = "$cpu" || continue case $target_arch in + # debian-all-test-cross architectures + + aarch64_be) + container_image=debian-all-test-cross + container_cross_prefix=aarch64-linux-gnu- + ;; + 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 container_image=debian-armhf-cross container_cross_prefix=arm-linux-gnueabihf- ;; - 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- ;; loongarch64) container_image=debian-loongarch-cross container_cross_prefix=loongarch64-unknown-linux-gnu- ;; - m68k) - container_image=debian-m68k-cross - container_cross_prefix=m68k-linux-gnu- - ;; microblaze) - container_image=debian-microblaze-cross container_cross_prefix=microblaze-linux-musl- ;; mips64el) - container_image=debian-mips64el-cross + container_image=debian-all-test-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${target_arch#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*) - 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} @@ -2060,16 +1548,19 @@ probe_target_compiler() { done try=cross - 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 + # 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 @@ -2211,67 +1702,6 @@ write_target_makefile() { fi } -########################################## -# check for vfio_user_server - -case "$vfio_user_server" in - enabled ) - if test "$git_submodules_action" != "ignore"; then - git_submodules="${git_submodules} subprojects/libvfio-user" - fi - ;; -esac - -########################################## -# 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 - -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 - -########################################## -# 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/QEMU_VERSION) -fi - - ####################################### # cross-compiled firmware targets @@ -2284,30 +1714,33 @@ fi # tests might fail. Prefer to keep the relevant files in their own # directory and symlink the directory instead. LINKS="Makefile" +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 +# 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 + echo "# Automatically generated by configure - do not modify" > Makefile.prereqs # Mac OS X ships with a broken assembler -roms= if have_target i386-softmmu x86_64-softmmu && \ - test "$targetos" != "darwin" && test "$targetos" != "sunos" && \ - test "$targetos" != "haiku" && \ + test "$host_os" != "darwin" && test "$host_os" != "sunos" && \ + test "$host_os" != "haiku" && \ probe_target_compiler i386-softmmu; then - roms="pc-bios/optionrom" + 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 @@ -2316,7 +1749,7 @@ fi if have_target ppc-softmmu ppc64-softmmu && \ probe_target_compiler ppc-softmmu; then - roms="$roms pc-bios/vof" + 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 @@ -2325,7 +1758,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) -if have_target s390x-softmmu && probe_target_compiler s390x-softmmu; 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=$? @@ -2334,185 +1768,55 @@ if have_target s390x-softmmu && probe_target_compiler s390x-softmmu; 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 pc-bios/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 -if ! (GIT="$git" "$source_path/scripts/git-submodule.sh" "$git_submodules_action" "$git_submodules"); then - exit 1 -fi - config_host_mak="config-host.mak" echo "# Automatically generated by configure - do not modify" > $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 -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 - else - gdb_bin="" - fi -fi - +echo "GDB=$gdb_bin" >> $config_host_mak if test "$container" != no; then - echo "ENGINE=$container" >> $config_host_mak + echo "RUNC=$runc" >> $config_host_mak +fi +echo "SUBDIRS=$subdirs" >> $config_host_mak +if test "$rust" != disabled; then + echo "RUST_TARGET_TRIPLE=$rust_target_triple" >> $config_host_mak fi -echo "ROMS=$roms" >> $config_host_mak -echo "MAKE=$make" >> $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_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 +if test "$default_targets" = "yes"; then + echo "CONFIG_DEFAULT_TARGETS=y" >> $config_host_mak +fi # FIXME: Use meson echo "EPOXY_CFLAGS=$epoxy_cflags" >> $config_host_mak echo "EPOXY_LIBS=$epoxy_libs" >> $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 -fi - -if test "$safe_stack" = "yes"; then - echo "CONFIG_SAFESTACK=y" >> $config_host_mak -fi - # tests/tcg configuration -(config_host_mak=tests/tcg/config-host.mak mkdir -p tests/tcg -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 - -# versioned checked in the main config_host.mak above -if test -n "$gdb_bin"; then - echo "HAVE_GDB_BIN=$gdb_bin" >> $config_host_mak -fi +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" >> $config_host_mak + echo "CONFIG_PLUGIN=y" >> tests/tcg/$config_host_mak fi tcg_tests_targets= @@ -2521,7 +1825,7 @@ for target in $target_list; do case $target in xtensa*-linux-user) - # the toolchain is not complete with headers, only build softmmu tests + # the toolchain is not complete with headers, only build system tests continue ;; *-softmmu) @@ -2544,11 +1848,29 @@ for target in $target_list; do 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" + + # will GDB work with these binaries? + if test "${gdb_arches#*$arch}" != "$gdb_arches"; then + echo "GDB=$gdb_bin" >> $config_target_mak + fi + + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.1; then + echo "GDB_HAS_MTE=y" >> $config_target_mak + fi + + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 16.0; then + # GDB has to support MTE in baremetal to allow debugging MTE in QEMU system mode + echo "GDB_SUPPORTS_MTE_IN_BAREMETAL=y" >> $config_target_mak + fi + 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" >> config-host.mak) + +if test "$tcg" = "enabled"; then + echo "TCG_TESTS_TARGETS=$tcg_tests_targets" >> $config_host_mak +fi if test "$skip_meson" = no; then cross="config-meson.cross.new" @@ -2572,24 +1894,57 @@ if test "$skip_meson" = no; then 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 + if test "$rust" != disabled; then + if test "$rust_host_triple" != "$rust_target_triple"; then + echo "rust = [$(meson_quote $rustc --target "$rust_target_triple")]" >> $cross + else + echo "rust = [$(meson_quote $rustc)]" >> $cross + fi + fi 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 + echo "readelf = [$(meson_quote $readelf)]" >> $cross if has $sdl2_config; then echo "sdl2-config = [$(meson_quote $sdl2_config)]" >> $cross fi 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 @@ -2604,42 +1959,52 @@ 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 + if test "$rust" != disabled; then + echo "rust = [$(meson_quote $rustc)]" >> $cross + fi + 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 "$rust" != "disabled" && meson_option_add "-Drust=$rust" + 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 "$smbd" != '' && meson_option_add "-Dsmbd=$smbd" + test "$plugins" = yes && meson_option_add "-Dplugins=true" test "$tcg" != enabled && meson_option_add "-Dtcg=$tcg" - test "$vfio_user_server" != auto && meson_option_add "-Dvfio_user_server=$vfio_user_server" 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 @@ -2675,10 +2040,10 @@ preserve_env CC preserve_env CFLAGS preserve_env CXX preserve_env CXXFLAGS +preserve_env DLLTOOL preserve_env LD preserve_env LDFLAGS preserve_env LD_LIBRARY_PATH -preserve_env MAKE preserve_env NM preserve_env OBJCFLAGS preserve_env OBJCOPY @@ -2687,11 +2052,15 @@ 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 @@ -2701,3 +2070,11 @@ echo ' "$@"' >>config.status chmod +x config.status rm -r "$TMPDIR1" + +if test "$rust" != disabled; then + echo + echo 'INFO: Rust bindings generation with `bindgen` might fail in some cases where' + echo 'the detected `libclang` does not match the expected `clang` version/target. In' + echo 'this case you must pass the path to `clang` and `libclang` to your build' + echo 'command invocation using the environment variables CLANG_PATH and LIBCLANG_PATH' +fi 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 d77b8f98f7..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,14 +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"); - free(kdbg); + g_free(kdbg); return NULL; } @@ -187,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; } /* @@ -207,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); } } @@ -221,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); @@ -243,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) { @@ -281,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"); @@ -346,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; @@ -473,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)); @@ -520,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 3727918641..bf1dce03fd 100644 --- a/contrib/gitdm/domain-map +++ b/contrib/gitdm/domain-map @@ -4,17 +4,26 @@ # 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 @@ -31,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-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 53883cc526..d7116f5444 100644 --- a/contrib/gitdm/group-map-individuals +++ b/contrib/gitdm/group-map-individuals @@ -37,3 +37,8 @@ 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 deleted file mode 100644 index 23e0396687..0000000000 --- a/contrib/plugins/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -*- Mode: makefile -*- -# -# 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. -# - -BUILD_DIR := $(CURDIR)/../.. - -include $(BUILD_DIR)/config-host.mak - -VPATH += $(SRC_PATH)/contrib/plugins - -NAMES := -NAMES += execlog -NAMES += hotblocks -NAMES += hotpages -NAMES += howvec -NAMES += lockstep -NAMES += hwprofile -NAMES += cache -NAMES += drcov - -SONAMES := $(addsuffix .so,$(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 += $(if $(CONFIG_DEBUG_TCG), -ggdb -O0) -CFLAGS += -I$(SRC_PATH)/include/qemu - -all: $(SONAMES) - -%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -lib%.so: %.o - $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDLIBS) - -clean: - rm -f *.o *.so *.d - rm -Rf .libs - -.PHONY: all clean diff --git a/contrib/plugins/bbv.c b/contrib/plugins/bbv.c new file mode 100644 index 0000000000..a5256517dd --- /dev/null +++ b/contrib/plugins/bbv.c @@ -0,0 +1,158 @@ +/* + * Generate basic block vectors for use with the SimPoint analysis tool. + * SimPoint: https://cseweb.ucsd.edu/~calder/simpoint/ + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include + +typedef struct Bb { + uint64_t vaddr; + struct qemu_plugin_scoreboard *count; + unsigned int index; +} Bb; + +typedef struct Vcpu { + uint64_t count; + FILE *file; +} Vcpu; + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +static GHashTable *bbs; +static GRWLock bbs_lock; +static char *filename; +static struct qemu_plugin_scoreboard *vcpus; +static uint64_t interval = 100000000; + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + for (int i = 0; i < qemu_plugin_num_vcpus(); i++) { + fclose(((Vcpu *)qemu_plugin_scoreboard_find(vcpus, i))->file); + } + + g_hash_table_unref(bbs); + g_free(filename); + qemu_plugin_scoreboard_free(vcpus); +} + +static void free_bb(void *data) +{ + qemu_plugin_scoreboard_free(((Bb *)data)->count); + g_free(data); +} + +static qemu_plugin_u64 count_u64(void) +{ + return qemu_plugin_scoreboard_u64_in_struct(vcpus, Vcpu, count); +} + +static qemu_plugin_u64 bb_count_u64(Bb *bb) +{ + return qemu_plugin_scoreboard_u64(bb->count); +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index) +{ + g_autofree gchar *vcpu_filename = NULL; + Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index); + + vcpu_filename = g_strdup_printf("%s.%u.bb", filename, vcpu_index); + vcpu->file = fopen(vcpu_filename, "w"); +} + +static void vcpu_interval_exec(unsigned int vcpu_index, void *udata) +{ + Vcpu *vcpu = qemu_plugin_scoreboard_find(vcpus, vcpu_index); + GHashTableIter iter; + void *value; + + if (!vcpu->file) { + return; + } + + vcpu->count -= interval; + + fputc('T', vcpu->file); + + g_rw_lock_reader_lock(&bbs_lock); + g_hash_table_iter_init(&iter, bbs); + + while (g_hash_table_iter_next(&iter, NULL, &value)) { + Bb *bb = value; + uint64_t bb_count = qemu_plugin_u64_get(bb_count_u64(bb), vcpu_index); + + if (!bb_count) { + continue; + } + + fprintf(vcpu->file, ":%u:%" PRIu64 " ", bb->index, bb_count); + qemu_plugin_u64_set(bb_count_u64(bb), vcpu_index, 0); + } + + g_rw_lock_reader_unlock(&bbs_lock); + fputc('\n', vcpu->file); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + uint64_t n_insns = qemu_plugin_tb_n_insns(tb); + uint64_t vaddr = qemu_plugin_tb_vaddr(tb); + Bb *bb; + + g_rw_lock_writer_lock(&bbs_lock); + bb = g_hash_table_lookup(bbs, &vaddr); + if (!bb) { + bb = g_new(Bb, 1); + bb->vaddr = vaddr; + bb->count = qemu_plugin_scoreboard_new(sizeof(uint64_t)); + bb->index = g_hash_table_size(bbs); + g_hash_table_replace(bbs, &bb->vaddr, bb); + } + g_rw_lock_writer_unlock(&bbs_lock); + + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, count_u64(), n_insns); + + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, bb_count_u64(bb), n_insns); + + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, vcpu_interval_exec, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_GE, count_u64(), interval, NULL); +} + +QEMU_PLUGIN_EXPORT 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++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "interval") == 0) { + interval = g_ascii_strtoull(tokens[1], NULL, 10); + } else if (g_strcmp0(tokens[0], "outfile") == 0) { + filename = tokens[1]; + tokens[1] = NULL; + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + if (!filename) { + fputs("outfile unspecified\n", stderr); + return -1; + } + + bbs = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, free_bb); + vcpus = qemu_plugin_scoreboard_new(sizeof(Vcpu)); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + + return 0; +} diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index ac1510aaa1..512ef6776b 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -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,12 +552,13 @@ 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); + l2_miss_rate); } g_string_append(line, "\n"); @@ -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/cflow.c b/contrib/plugins/cflow.c new file mode 100644 index 0000000000..b39974d1cf --- /dev/null +++ b/contrib/plugins/cflow.c @@ -0,0 +1,388 @@ +/* + * Control Flow plugin + * + * This plugin will track changes to control flow and detect where + * instructions fault. + * + * Copyright (c) 2024 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef enum { + SORT_HOTTEST, /* hottest branch insn */ + SORT_EXCEPTION, /* most early exits */ + SORT_POPDEST, /* most destinations (usually ret's) */ +} ReportType; + +ReportType report = SORT_HOTTEST; +int topn = 10; + +typedef struct { + uint64_t daddr; + uint64_t dcount; +} DestData; + +/* A node is an address where we can go to multiple places */ +typedef struct { + GMutex lock; + /* address of the branch point */ + uint64_t addr; + /* array of DestData */ + GArray *dests; + /* early exit/fault count */ + uint64_t early_exit; + /* jump destination count */ + uint64_t dest_count; + /* instruction data */ + char *insn_disas; + /* symbol? */ + const char *symbol; + /* times translated as last in block? */ + int last_count; + /* times translated in the middle of block? */ + int mid_count; +} NodeData; + +typedef enum { + /* last insn in block, expected flow control */ + LAST_INSN = (1 << 0), + /* mid-block insn, can only be an exception */ + EXCP_INSN = (1 << 1), + /* multiple disassembly, may have changed */ + MULT_INSN = (1 << 2), +} InsnTypes; + +typedef struct { + /* address of the branch point */ + uint64_t addr; + /* disassembly */ + char *insn_disas; + /* symbol? */ + const char *symbol; + /* types */ + InsnTypes type_flag; +} InsnData; + +/* We use this to track the current execution state */ +typedef struct { + /* address of end of block */ + uint64_t end_block; + /* next pc after end of block */ + uint64_t pc_after_block; + /* address of last executed PC */ + uint64_t last_pc; +} VCPUScoreBoard; + +/* descriptors for accessing the above scoreboard */ +static qemu_plugin_u64 end_block; +static qemu_plugin_u64 pc_after_block; +static qemu_plugin_u64 last_pc; + + +static GMutex node_lock; +static GHashTable *nodes; +struct qemu_plugin_scoreboard *state; + +/* SORT_HOTTEST */ +static gint hottest(gconstpointer a, gconstpointer b) +{ + NodeData *na = (NodeData *) a; + NodeData *nb = (NodeData *) b; + + return na->dest_count > nb->dest_count ? -1 : + na->dest_count == nb->dest_count ? 0 : 1; +} + +static gint exception(gconstpointer a, gconstpointer b) +{ + NodeData *na = (NodeData *) a; + NodeData *nb = (NodeData *) b; + + return na->early_exit > nb->early_exit ? -1 : + na->early_exit == nb->early_exit ? 0 : 1; +} + +static gint popular(gconstpointer a, gconstpointer b) +{ + NodeData *na = (NodeData *) a; + NodeData *nb = (NodeData *) b; + + return na->dests->len > nb->dests->len ? -1 : + na->dests->len == nb->dests->len ? 0 : 1; +} + +/* Filter out non-branches - returns true to remove entry */ +static gboolean filter_non_branches(gpointer key, gpointer value, + gpointer user_data) +{ + NodeData *node = (NodeData *) value; + + return node->dest_count == 0; +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_autoptr(GString) result = g_string_new("collected "); + GList *data; + GCompareFunc sort = &hottest; + int i = 0; + + g_mutex_lock(&node_lock); + g_string_append_printf(result, "%d control flow nodes in the hash table\n", + g_hash_table_size(nodes)); + + /* remove all nodes that didn't branch */ + g_hash_table_foreach_remove(nodes, filter_non_branches, NULL); + + data = g_hash_table_get_values(nodes); + + switch (report) { + case SORT_HOTTEST: + sort = &hottest; + break; + case SORT_EXCEPTION: + sort = &exception; + break; + case SORT_POPDEST: + sort = &popular; + break; + } + + data = g_list_sort(data, sort); + + for (GList *l = data; + l != NULL && i < topn; + l = l->next, i++) { + NodeData *n = l->data; + const char *type = n->mid_count ? "sync fault" : "branch"; + g_string_append_printf(result, " addr: 0x%"PRIx64 " %s: %s (%s)\n", + n->addr, n->symbol, n->insn_disas, type); + if (n->early_exit) { + g_string_append_printf(result, " early exits %"PRId64"\n", + n->early_exit); + } + g_string_append_printf(result, " branches %"PRId64"\n", + n->dest_count); + for (int j = 0; j < n->dests->len; j++) { + DestData *dd = &g_array_index(n->dests, DestData, j); + g_string_append_printf(result, " to 0x%"PRIx64" (%"PRId64")\n", + dd->daddr, dd->dcount); + } + } + + qemu_plugin_outs(result->str); + + g_mutex_unlock(&node_lock); +} + +static void plugin_init(void) +{ + g_mutex_init(&node_lock); + nodes = g_hash_table_new(NULL, g_direct_equal); + state = qemu_plugin_scoreboard_new(sizeof(VCPUScoreBoard)); + + /* score board declarations */ + end_block = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, + end_block); + pc_after_block = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, + pc_after_block); + last_pc = qemu_plugin_scoreboard_u64_in_struct(state, VCPUScoreBoard, + last_pc); +} + +static NodeData *create_node(uint64_t addr) +{ + NodeData *node = g_new0(NodeData, 1); + g_mutex_init(&node->lock); + node->addr = addr; + node->dests = g_array_new(true, true, sizeof(DestData)); + return node; +} + +static NodeData *fetch_node(uint64_t addr, bool create_if_not_found) +{ + NodeData *node = NULL; + + g_mutex_lock(&node_lock); + node = (NodeData *) g_hash_table_lookup(nodes, (gconstpointer) addr); + if (!node && create_if_not_found) { + node = create_node(addr); + g_hash_table_insert(nodes, (gpointer) addr, (gpointer) node); + } + g_mutex_unlock(&node_lock); + return node; +} + +/* + * Called when we detect a non-linear execution (pc != + * pc_after_block). This could be due to a fault causing some sort of + * exit exception (if last_pc != block_end) or just a taken branch. + */ +static void vcpu_tb_branched_exec(unsigned int cpu_index, void *udata) +{ + uint64_t lpc = qemu_plugin_u64_get(last_pc, cpu_index); + uint64_t ebpc = qemu_plugin_u64_get(end_block, cpu_index); + uint64_t npc = qemu_plugin_u64_get(pc_after_block, cpu_index); + uint64_t pc = GPOINTER_TO_UINT(udata); + + /* return early for address 0 */ + if (!lpc) { + return; + } + + NodeData *node = fetch_node(lpc, true); + DestData *data = NULL; + bool early_exit = (lpc != ebpc); + GArray *dests; + + /* the condition should never hit */ + g_assert(pc != npc); + + g_mutex_lock(&node->lock); + + if (early_exit) { + fprintf(stderr, "%s: pc=%"PRIx64", epbc=%"PRIx64 + " npc=%"PRIx64", lpc=%"PRIx64"\n", + __func__, pc, ebpc, npc, lpc); + node->early_exit++; + if (!node->mid_count) { + /* count now as we've only just allocated */ + node->mid_count++; + } + } + + dests = node->dests; + for (int i = 0; i < dests->len; i++) { + if (g_array_index(dests, DestData, i).daddr == pc) { + data = &g_array_index(dests, DestData, i); + } + } + + /* we've never seen this before, allocate a new entry */ + if (!data) { + DestData new_entry = { .daddr = pc }; + g_array_append_val(dests, new_entry); + data = &g_array_index(dests, DestData, dests->len - 1); + g_assert(data->daddr == pc); + } + + data->dcount++; + node->dest_count++; + + g_mutex_unlock(&node->lock); +} + +/* + * At the start of each block we need to resolve two things: + * + * - is last_pc == block_end, if not we had an early exit + * - is start of block last_pc + insn width, if not we jumped + * + * Once those are dealt with we can instrument the rest of the + * instructions for their execution. + * + */ +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + uint64_t pc = qemu_plugin_tb_vaddr(tb); + size_t insns = qemu_plugin_tb_n_insns(tb); + struct qemu_plugin_insn *first_insn = qemu_plugin_tb_get_insn(tb, 0); + struct qemu_plugin_insn *last_insn = qemu_plugin_tb_get_insn(tb, insns - 1); + + /* + * check if we are executing linearly after the last block. We can + * handle both early block exits and normal branches in the + * callback if we hit it. + */ + gpointer udata = GUINT_TO_POINTER(pc); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, vcpu_tb_branched_exec, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_NE, pc_after_block, pc, udata); + + /* + * Now we can set start/end for this block so the next block can + * check where we are at. Do this on the first instruction and not + * the TB so we don't get mixed up with above. + */ + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(first_insn, + QEMU_PLUGIN_INLINE_STORE_U64, + end_block, qemu_plugin_insn_vaddr(last_insn)); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(first_insn, + QEMU_PLUGIN_INLINE_STORE_U64, + pc_after_block, + qemu_plugin_insn_vaddr(last_insn) + + qemu_plugin_insn_size(last_insn)); + + for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx); + uint64_t ipc = qemu_plugin_insn_vaddr(insn); + /* + * If this is a potential branch point check if we could grab + * the disassembly for it. If it is the last instruction + * always create an entry. + */ + NodeData *node = fetch_node(ipc, last_insn); + if (node) { + g_mutex_lock(&node->lock); + if (!node->insn_disas) { + node->insn_disas = qemu_plugin_insn_disas(insn); + } + if (!node->symbol) { + node->symbol = qemu_plugin_insn_symbol(insn); + } + if (last_insn == insn) { + node->last_count++; + } else { + node->mid_count++; + } + g_mutex_unlock(&node->lock); + } + + /* Store the PC of what we are about to execute */ + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn, + QEMU_PLUGIN_INLINE_STORE_U64, + last_pc, ipc); + } +} + +QEMU_PLUGIN_EXPORT +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++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "sort") == 0) { + if (g_strcmp0(tokens[1], "hottest") == 0) { + report = SORT_HOTTEST; + } else if (g_strcmp0(tokens[1], "early") == 0) { + report = SORT_EXCEPTION; + } else if (g_strcmp0(tokens[1], "exceptions") == 0) { + report = SORT_POPDEST; + } else { + fprintf(stderr, "failed to parse: %s\n", tokens[1]); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + plugin_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/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 e255bd21fd..d67d010761 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,31 +15,40 @@ #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 */ -static GPtrArray *last_exec; -static GMutex expand_array_lock; +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; -/* - * Expand last_exec array. - * - * As we could have multiple threads trying to do this we need to - * serialise the expansion under a lock. Threads accessing already - * created entries can continue without issue even if the ptr array - * gets reallocated during resize. - */ -static void expand_last_exec(int cpu_index) +static CPU *get_cpu(int vcpu_index) { - g_mutex_lock(&expand_array_lock); - while (cpu_index >= last_exec->len) { - GString *s = g_string_new(NULL); - g_ptr_array_add(last_exec, s); - } - g_mutex_unlock(&expand_array_lock); + 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; } /** @@ -48,11 +57,10 @@ static void expand_last_exec(int cpu_index) 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_ptr_array_index(last_exec, cpu_index); /* Indicate type of memory access */ if (qemu_plugin_mem_is_store(info)) { @@ -73,28 +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 */ - if (cpu_index >= last_exec->len) { - expand_last_exec(cpu_index); + 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 - 1; i >= 0; i--) { + g_string_append_printf(cpu->last_exec, "%02x", + reg->new->data[i]); + } + reg->last = reg->new; + reg->new = temp; + } } - s = g_ptr_array_index(last_exec, 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); } /** @@ -107,9 +178,11 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { struct qemu_plugin_insn *insn; 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++) { + size_t n_insns = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < n_insns; i++) { char *insn_disas; uint64_t insn_vaddr; @@ -127,7 +200,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) /* * 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. + * after the instruction we care about. Also enable register + * checking on the next instruction. */ if (skip && imatches) { int j; @@ -135,6 +209,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) char *m = g_ptr_array_index(imatches, j); if (g_str_has_prefix(insn_disas, m)) { skip = false; + check_regs_next = rmatches; } } } @@ -149,11 +224,43 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) } } + /* + * 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) { - g_free(insn_disas); + 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)); + uint32_t insn_opcode = 0; + qemu_plugin_insn_data(insn, &insn_opcode, sizeof(insn_opcode)); + char *output = g_strdup_printf("0x%"PRIx64", 0x%"PRIx32", \"%s\"", insn_vaddr, insn_opcode, insn_disas); @@ -163,30 +270,142 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) QEMU_PLUGIN_MEM_RW, NULL); /* Register callback on instruction */ - qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec, - QEMU_PLUGIN_CB_NO_REGS, output); + 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_ptr_array_index(last_exec, 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 */ @@ -195,7 +414,7 @@ static void parse_insn_match(char *match) if (!imatches) { imatches = g_ptr_array_new(); } - g_ptr_array_add(imatches, match); + g_ptr_array_add(imatches, g_strdup(match)); } static void parse_vaddr_match(char *match) @@ -208,6 +427,18 @@ static void parse_vaddr_match(char *match) 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)); +} + /** * Install the plugin */ @@ -219,26 +450,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. */ - if (info->system_emulation) { - last_exec = g_ptr_array_sized_new(info->system.max_vcpus); - } else { - last_exec = g_ptr_array_new(); - } + cpus = g_array_sized_new(true, true, sizeof(CPU), + info->system_emulation ? info->system.max_vcpus : 1); 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], "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 translation block and exit callbacks */ + /* 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..9be67f7453 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,15 +243,16 @@ 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; - uint32_t opcode; + uint32_t opcode = 0; InsnClassExecCount *class = NULL; /* @@ -247,7 +261,7 @@ static uint64_t *find_counter(struct qemu_plugin_insn *insn) * They would probably benefit from a more tailored plugin. * However we can fall back to individual instruction counting. */ - opcode = *((uint32_t *)qemu_plugin_insn_data(insn)); + qemu_plugin_insn_data(insn, &opcode, sizeof(opcode)); for (i = 0; !cnt && i < class_table_sz; i++) { class = &class_table[i]; @@ -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/ips.c b/contrib/plugins/ips.c new file mode 100644 index 0000000000..e5297dbb01 --- /dev/null +++ b/contrib/plugins/ips.c @@ -0,0 +1,170 @@ +/* + * Instructions Per Second (IPS) rate limiting plugin. + * + * This plugin can be used to restrict the execution of a system to a + * particular number of Instructions Per Second (IPS). This controls + * time as seen by the guest so while wall-clock time may be longer + * from the guests point of view time will pass at the normal rate. + * + * This uses the new plugin API which allows the plugin to control + * system time. + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +/* how many times do we update time per sec */ +#define NUM_TIME_UPDATE_PER_SEC 10 +#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000) + +static GMutex global_state_lock; + +static uint64_t max_insn_per_second = 1000 * 1000 * 1000; /* ips per core, per second */ +static uint64_t max_insn_per_quantum; /* trap every N instructions */ +static int64_t virtual_time_ns; /* last set virtual time */ + +static const void *time_handle; + +typedef struct { + uint64_t total_insn; + uint64_t quantum_insn; /* insn in last quantum */ + int64_t last_quantum_time; /* time when last quantum started */ +} vCPUTime; + +struct qemu_plugin_scoreboard *vcpus; + +/* return epoch time in ns */ +static int64_t now_ns(void) +{ + return g_get_real_time() * 1000; +} + +static uint64_t num_insn_during(int64_t elapsed_ns) +{ + double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC; + return num_secs * (double) max_insn_per_second; +} + +static int64_t time_for_insn(uint64_t num_insn) +{ + double num_secs = (double) num_insn / (double) max_insn_per_second; + return num_secs * (double) NSEC_IN_ONE_SEC; +} + +static void update_system_time(vCPUTime *vcpu) +{ + int64_t elapsed_ns = now_ns() - vcpu->last_quantum_time; + uint64_t max_insn = num_insn_during(elapsed_ns); + + if (vcpu->quantum_insn >= max_insn) { + /* this vcpu ran faster than expected, so it has to sleep */ + uint64_t insn_advance = vcpu->quantum_insn - max_insn; + uint64_t time_advance_ns = time_for_insn(insn_advance); + int64_t sleep_us = time_advance_ns / 1000; + g_usleep(sleep_us); + } + + vcpu->total_insn += vcpu->quantum_insn; + vcpu->quantum_insn = 0; + vcpu->last_quantum_time = now_ns(); + + /* based on total number of instructions, what should be the new time? */ + int64_t new_virtual_time = time_for_insn(vcpu->total_insn); + + g_mutex_lock(&global_state_lock); + + /* Time only moves forward. Another vcpu might have updated it already. */ + if (new_virtual_time > virtual_time_ns) { + qemu_plugin_update_ns(time_handle, new_virtual_time); + virtual_time_ns = new_virtual_time; + } + + g_mutex_unlock(&global_state_lock); +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + vcpu->total_insn = 0; + vcpu->quantum_insn = 0; + vcpu->last_quantum_time = now_ns(); +} + +static void vcpu_exit(qemu_plugin_id_t id, unsigned int cpu_index) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + update_system_time(vcpu); +} + +static void every_quantum_insn(unsigned int cpu_index, void *udata) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + g_assert(vcpu->quantum_insn >= max_insn_per_quantum); + update_system_time(vcpu); +} + +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); + qemu_plugin_u64 quantum_insn = + qemu_plugin_scoreboard_u64_in_struct(vcpus, vCPUTime, quantum_insn); + /* count (and eventually trap) once per tb */ + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, quantum_insn, n_insns); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, every_quantum_insn, + QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_COND_GE, + quantum_insn, max_insn_per_quantum, NULL); +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) +{ + qemu_plugin_scoreboard_free(vcpus); +} + +QEMU_PLUGIN_EXPORT 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++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "ips") == 0) { + max_insn_per_second = g_ascii_strtoull(tokens[1], NULL, 10); + if (!max_insn_per_second && errno) { + fprintf(stderr, "%s: couldn't parse %s (%s)\n", + __func__, tokens[1], g_strerror(errno)); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + vcpus = qemu_plugin_scoreboard_new(sizeof(vCPUTime)); + max_insn_per_quantum = max_insn_per_second / NUM_TIME_UPDATE_PER_SEC; + + if (max_insn_per_quantum == 0) { + fprintf(stderr, "minimum of %d instructions per second needed\n", + NUM_TIME_UPDATE_PER_SEC); + return -1; + } + + time_handle = qemu_plugin_request_time_control(); + g_assert(time_handle); + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_vcpu_exit_cb(id, vcpu_exit); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index a41ffe83fa..62981d4e09 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -14,7 +14,8 @@ * particular run may execute the exact same sequence of blocks. An * asynchronous event (for example X11 graphics update) may cause a * block to end early and a new partial block to start. This means - * serial only test cases are a better bet. -d nochain may also help. + * serial only test cases are a better bet. -d nochain may also help + * as well as -accel tcg,one-insn-per-tb=on * * This code is not thread safe! * @@ -57,7 +58,7 @@ typedef struct { /* The execution state we compare */ typedef struct { uint64_t pc; - unsigned long insn_count; + uint64_t insn_count; } ExecState; typedef struct { @@ -100,6 +101,31 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) plugin_cleanup(id); } +/* + * g_memdup has been deprecated in Glib since 2.68 and + * will complain about it if you try to use it. However until + * glib_req_ver for QEMU is bumped we make a copy of the glib-compat + * handler. + */ +static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size) +{ +#if GLIB_CHECK_VERSION(2, 68, 0) + return g_memdup2(mem, byte_size); +#else + gpointer new_mem; + + if (mem && byte_size != 0) { + new_mem = g_malloc(byte_size); + memcpy(new_mem, mem, byte_size); + } else { + new_mem = NULL; + } + + return new_mem; +#endif +} +#define g_memdup2(m, s) g_memdup2_qemu(m, s) + static void report_divergance(ExecState *us, ExecState *them) { DivergeState divrec = { log, 0 }; @@ -108,7 +134,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,12 +156,17 @@ 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", - us->pc, them->pc, g_slist_length(divergence_log), + g_string_printf(out, "@ " + "0x%016" PRIx64 " (%" PRId64 ") vs " + "0x%016" PRIx64 " (%" PRId64 ")" + " (%d/%d since last)\n", + us->pc, us->insn_count, + them->pc, them->insn_count, + g_slist_length(divergence_log), divrec.distance); qemu_plugin_outs(out->str); } @@ -144,20 +175,20 @@ 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", - us->pc, us->insn_count, them->pc, them->insn_count); + g_string_printf(out, "Δ too high, we have diverged, previous insns\n"); for (entry = log, i = 0; g_slist_next(entry) && i < 5; 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); } qemu_plugin_outs(out->str); - qemu_plugin_outs("too much divergence... giving up."); + qemu_plugin_outs("giving up\n"); qemu_plugin_uninstall(our_id, plugin_cleanup); } } @@ -240,6 +271,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 +281,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 +319,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 +328,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 +365,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)) { @@ -331,7 +373,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, return -1; } } else if (g_strcmp0(tokens[0], "sockpath") == 0) { - sock_path = tokens[1]; + sock_path = g_strdup(tokens[1]); } else { fprintf(stderr, "option parsing failed: %s\n", p); return -1; diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build new file mode 100644 index 0000000000..63a32c2b4f --- /dev/null +++ b/contrib/plugins/meson.build @@ -0,0 +1,28 @@ +contrib_plugins = ['bbv', 'cache', 'cflow', 'drcov', 'execlog', 'hotblocks', + 'hotpages', 'howvec', 'hwprofile', 'ips', 'stoptrigger'] +if host_os != 'windows' + # lockstep uses socket.h + contrib_plugins += 'lockstep' +endif + +t = [] +if get_option('plugins') + foreach i : contrib_plugins + if host_os == 'windows' + t += shared_module(i, files(i + '.c') + '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('contrib-plugins', t) +else + run_target('contrib-plugins', command: find_program('true')) +endif diff --git a/contrib/plugins/stoptrigger.c b/contrib/plugins/stoptrigger.c new file mode 100644 index 0000000000..03ee22f4c6 --- /dev/null +++ b/contrib/plugins/stoptrigger.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2024, Simon Hamelin + * + * Stop execution once a given address is reached or if the + * count of executed instructions reached a specified limit + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +/* Scoreboard to track executed instructions count */ +typedef struct { + uint64_t insn_count; +} InstructionsCount; +static struct qemu_plugin_scoreboard *insn_count_sb; +static qemu_plugin_u64 insn_count; + +static uint64_t icount; +static int icount_exit_code; + +static bool exit_on_icount; +static bool exit_on_address; + +/* Map trigger addresses to exit code */ +static GHashTable *addrs_ht; + +static void exit_emulation(int return_code, char *message) +{ + qemu_plugin_outs(message); + g_free(message); + exit(return_code); +} + +static void exit_icount_reached(unsigned int cpu_index, void *udata) +{ + uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + char *msg = g_strdup_printf("icount reached at 0x%" PRIx64 ", exiting\n", + insn_vaddr); + + exit_emulation(icount_exit_code, msg); +} + +static void exit_address_reached(unsigned int cpu_index, void *udata) +{ + uint64_t insn_vaddr = GPOINTER_TO_UINT(udata); + char *msg = g_strdup_printf("0x%" PRIx64 " reached, exiting\n", insn_vaddr); + int exit_code; + + exit_code = GPOINTER_TO_INT( + g_hash_table_lookup(addrs_ht, GUINT_TO_POINTER(insn_vaddr))); + + exit_emulation(exit_code, msg); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t tb_n = qemu_plugin_tb_n_insns(tb); + for (size_t i = 0; i < tb_n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + gpointer insn_vaddr = GUINT_TO_POINTER(qemu_plugin_insn_vaddr(insn)); + + if (exit_on_icount) { + /* Increment and check scoreboard for each instruction */ + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); + qemu_plugin_register_vcpu_insn_exec_cond_cb( + insn, exit_icount_reached, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, insn_count, icount + 1, insn_vaddr); + } + + if (exit_on_address) { + if (g_hash_table_contains(addrs_ht, insn_vaddr)) { + /* Exit triggered by address */ + qemu_plugin_register_vcpu_insn_exec_cb( + insn, exit_address_reached, QEMU_PLUGIN_CB_NO_REGS, + insn_vaddr); + } + } + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_hash_table_destroy(addrs_ht); + qemu_plugin_scoreboard_free(insn_count_sb); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + addrs_ht = g_hash_table_new(NULL, g_direct_equal); + + insn_count_sb = qemu_plugin_scoreboard_new(sizeof(InstructionsCount)); + insn_count = qemu_plugin_scoreboard_u64_in_struct( + insn_count_sb, InstructionsCount, insn_count); + + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "icount") == 0) { + g_auto(GStrv) icount_tokens = g_strsplit(tokens[1], ":", 2); + icount = g_ascii_strtoull(icount_tokens[0], NULL, 0); + if (icount < 1 || g_strrstr(icount_tokens[0], "-") != NULL) { + fprintf(stderr, + "icount parsing failed: '%s' must be a positive " + "integer\n", + icount_tokens[0]); + return -1; + } + if (icount_tokens[1]) { + icount_exit_code = g_ascii_strtoull(icount_tokens[1], NULL, 0); + } + exit_on_icount = true; + } else if (g_strcmp0(tokens[0], "addr") == 0) { + g_auto(GStrv) addr_tokens = g_strsplit(tokens[1], ":", 2); + uint64_t exit_addr = g_ascii_strtoull(addr_tokens[0], NULL, 0); + int exit_code = 0; + if (addr_tokens[1]) { + exit_code = g_ascii_strtoull(addr_tokens[1], NULL, 0); + } + g_hash_table_insert(addrs_ht, GUINT_TO_POINTER(exit_addr), + GINT_TO_POINTER(exit_code)); + exit_on_address = true; + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + if (!exit_on_icount && !exit_on_address) { + fprintf(stderr, "'icount' or 'addr' argument missing\n"); + return -1; + } + + /* Register translation block and exit callbacks */ + 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/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/rdmacm-mux/main.c b/contrib/rdmacm-mux/main.c deleted file mode 100644 index 771ca01e03..0000000000 --- a/contrib/rdmacm-mux/main.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * QEMU paravirtual RDMA - rdmacm-mux implementation - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 -#include -#include -#include - -#include "rdmacm-mux.h" - -#define SCALE_US 1000 -#define COMMID_TTL 2 /* How many SCALE_US a context of MAD session is saved */ -#define SLEEP_SECS 5 /* This is used both in poll() and thread */ -#define SERVER_LISTEN_BACKLOG 10 -#define MAX_CLIENTS 4096 -#define MAD_RMPP_VERSION 0 -#define MAD_METHOD_MASK0 0x8 - -#define IB_USER_MAD_LONGS_PER_METHOD_MASK (128 / (8 * sizeof(long))) - -#define CM_REQ_DGID_POS 80 -#define CM_SIDR_REQ_DGID_POS 44 - -/* The below can be override by command line parameter */ -#define UNIX_SOCKET_PATH "/var/run/rdmacm-mux" -/* Has format %s-%s-%d" -- */ -#define SOCKET_PATH_MAX (PATH_MAX - NAME_MAX - sizeof(int) - 2) -#define RDMA_PORT_NUM 1 - -typedef struct RdmaCmServerArgs { - char unix_socket_path[PATH_MAX]; - char rdma_dev_name[NAME_MAX]; - int rdma_port_num; -} RdmaCMServerArgs; - -typedef struct CommId2FdEntry { - int fd; - int ttl; /* Initialized to 2, decrement each timeout, entry delete when 0 */ - __be64 gid_ifid; -} CommId2FdEntry; - -typedef struct RdmaCmUMadAgent { - int port_id; - int agent_id; - GHashTable *gid2fd; /* Used to find fd of a given gid */ - GHashTable *commid2fd; /* Used to find fd on of a given comm_id */ -} RdmaCmUMadAgent; - -typedef struct RdmaCmServer { - bool run; - RdmaCMServerArgs args; - struct pollfd fds[MAX_CLIENTS]; - int nfds; - RdmaCmUMadAgent umad_agent; - pthread_t umad_recv_thread; - pthread_rwlock_t lock; -} RdmaCMServer; - -static RdmaCMServer server = {0}; - -static void usage(const char *progname) -{ - printf("Usage: %s [OPTION]...\n" - "Start a RDMA-CM multiplexer\n" - "\n" - "\t-h Show this help\n" - "\t-d rdma-device-name Name of RDMA device to register with\n" - "\t-s unix-socket-path Path to unix socket to listen on (default %s)\n" - "\t-p rdma-device-port Port number of RDMA device to register with (default %d)\n", - progname, UNIX_SOCKET_PATH, RDMA_PORT_NUM); -} - -static void help(const char *progname) -{ - fprintf(stderr, "Try '%s -h' for more information.\n", progname); -} - -static void parse_args(int argc, char *argv[]) -{ - int c; - char unix_socket_path[SOCKET_PATH_MAX]; - - strcpy(server.args.rdma_dev_name, ""); - strcpy(unix_socket_path, UNIX_SOCKET_PATH); - server.args.rdma_port_num = RDMA_PORT_NUM; - - while ((c = getopt(argc, argv, "hs:d:p:")) != -1) { - switch (c) { - case 'h': - usage(argv[0]); - exit(0); - - case 'd': - strncpy(server.args.rdma_dev_name, optarg, NAME_MAX - 1); - break; - - case 's': - /* This is temporary, final name will build below */ - strncpy(unix_socket_path, optarg, SOCKET_PATH_MAX - 1); - break; - - case 'p': - server.args.rdma_port_num = atoi(optarg); - break; - - default: - help(argv[0]); - exit(1); - } - } - - if (!strcmp(server.args.rdma_dev_name, "")) { - fprintf(stderr, "Missing RDMA device name\n"); - help(argv[0]); - exit(1); - } - - /* Build unique unix-socket file name */ - snprintf(server.args.unix_socket_path, PATH_MAX, "%s-%s-%d", - unix_socket_path, server.args.rdma_dev_name, - server.args.rdma_port_num); - - syslog(LOG_INFO, "unix_socket_path=%s", server.args.unix_socket_path); - syslog(LOG_INFO, "rdma-device-name=%s", server.args.rdma_dev_name); - syslog(LOG_INFO, "rdma-device-port=%d", server.args.rdma_port_num); -} - -static void hash_tbl_alloc(void) -{ - - server.umad_agent.gid2fd = g_hash_table_new_full(g_int64_hash, - g_int64_equal, - g_free, g_free); - server.umad_agent.commid2fd = g_hash_table_new_full(g_int_hash, - g_int_equal, - g_free, g_free); -} - -static void hash_tbl_free(void) -{ - if (server.umad_agent.commid2fd) { - g_hash_table_destroy(server.umad_agent.commid2fd); - } - if (server.umad_agent.gid2fd) { - g_hash_table_destroy(server.umad_agent.gid2fd); - } -} - - -static int _hash_tbl_search_fd_by_ifid(__be64 *gid_ifid) -{ - int *fd; - - fd = g_hash_table_lookup(server.umad_agent.gid2fd, gid_ifid); - if (!fd) { - /* Let's try IPv4 */ - *gid_ifid |= 0x00000000ffff0000; - fd = g_hash_table_lookup(server.umad_agent.gid2fd, gid_ifid); - } - - return fd ? *fd : 0; -} - -static int hash_tbl_search_fd_by_ifid(int *fd, __be64 *gid_ifid) -{ - pthread_rwlock_rdlock(&server.lock); - *fd = _hash_tbl_search_fd_by_ifid(gid_ifid); - pthread_rwlock_unlock(&server.lock); - - if (!*fd) { - syslog(LOG_WARNING, "Can't find matching for ifid 0x%llx\n", *gid_ifid); - return -ENOENT; - } - - return 0; -} - -static int hash_tbl_search_fd_by_comm_id(uint32_t comm_id, int *fd, - __be64 *gid_idid) -{ - CommId2FdEntry *fde; - - pthread_rwlock_rdlock(&server.lock); - fde = g_hash_table_lookup(server.umad_agent.commid2fd, &comm_id); - pthread_rwlock_unlock(&server.lock); - - if (!fde) { - syslog(LOG_WARNING, "Can't find matching for comm_id 0x%x\n", comm_id); - return -ENOENT; - } - - *fd = fde->fd; - *gid_idid = fde->gid_ifid; - - return 0; -} - -static RdmaCmMuxErrCode add_fd_ifid_pair(int fd, __be64 gid_ifid) -{ - int fd1; - - pthread_rwlock_wrlock(&server.lock); - - fd1 = _hash_tbl_search_fd_by_ifid(&gid_ifid); - if (fd1) { /* record already exist - an error */ - pthread_rwlock_unlock(&server.lock); - return fd == fd1 ? RDMACM_MUX_ERR_CODE_EEXIST : - RDMACM_MUX_ERR_CODE_EACCES; - } - - g_hash_table_insert(server.umad_agent.gid2fd, g_memdup(&gid_ifid, - sizeof(gid_ifid)), g_memdup(&fd, sizeof(fd))); - - pthread_rwlock_unlock(&server.lock); - - syslog(LOG_INFO, "0x%lx registered on socket %d", - be64toh((uint64_t)gid_ifid), fd); - - return RDMACM_MUX_ERR_CODE_OK; -} - -static RdmaCmMuxErrCode delete_fd_ifid_pair(int fd, __be64 gid_ifid) -{ - int fd1; - - pthread_rwlock_wrlock(&server.lock); - - fd1 = _hash_tbl_search_fd_by_ifid(&gid_ifid); - if (!fd1) { /* record not exist - an error */ - pthread_rwlock_unlock(&server.lock); - return RDMACM_MUX_ERR_CODE_ENOTFOUND; - } - - g_hash_table_remove(server.umad_agent.gid2fd, g_memdup(&gid_ifid, - sizeof(gid_ifid))); - pthread_rwlock_unlock(&server.lock); - - syslog(LOG_INFO, "0x%lx unregistered on socket %d", - be64toh((uint64_t)gid_ifid), fd); - - return RDMACM_MUX_ERR_CODE_OK; -} - -static void hash_tbl_save_fd_comm_id_pair(int fd, uint32_t comm_id, - uint64_t gid_ifid) -{ - CommId2FdEntry fde = {fd, COMMID_TTL, gid_ifid}; - - pthread_rwlock_wrlock(&server.lock); - g_hash_table_insert(server.umad_agent.commid2fd, - g_memdup(&comm_id, sizeof(comm_id)), - g_memdup(&fde, sizeof(fde))); - pthread_rwlock_unlock(&server.lock); -} - -static gboolean remove_old_comm_ids(gpointer key, gpointer value, - gpointer user_data) -{ - CommId2FdEntry *fde = (CommId2FdEntry *)value; - - return !fde->ttl--; -} - -static gboolean remove_entry_from_gid2fd(gpointer key, gpointer value, - gpointer user_data) -{ - if (*(int *)value == *(int *)user_data) { - syslog(LOG_INFO, "0x%lx unregistered on socket %d", - be64toh(*(uint64_t *)key), *(int *)value); - return true; - } - - return false; -} - -static void hash_tbl_remove_fd_ifid_pair(int fd) -{ - pthread_rwlock_wrlock(&server.lock); - g_hash_table_foreach_remove(server.umad_agent.gid2fd, - remove_entry_from_gid2fd, (gpointer)&fd); - pthread_rwlock_unlock(&server.lock); -} - -static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid) -{ - struct umad_hdr *hdr = (struct umad_hdr *)mad; - char *data = (char *)hdr + sizeof(*hdr); - int32_t comm_id = 0; - uint16_t attr_id = be16toh(hdr->attr_id); - int rc = 0; - - if (umad_len <= sizeof(*hdr)) { - rc = -EINVAL; - syslog(LOG_DEBUG, "Ignoring MAD packets with header only\n"); - goto out; - } - - switch (attr_id) { - case UMAD_CM_ATTR_REQ: - if (unlikely(umad_len < sizeof(*hdr) + CM_REQ_DGID_POS + - sizeof(*gid_ifid))) { - rc = -EINVAL; - syslog(LOG_WARNING, - "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, - attr_id); - goto out; - } - memcpy(gid_ifid, data + CM_REQ_DGID_POS, sizeof(*gid_ifid)); - rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid); - break; - - case UMAD_CM_ATTR_SIDR_REQ: - if (unlikely(umad_len < sizeof(*hdr) + CM_SIDR_REQ_DGID_POS + - sizeof(*gid_ifid))) { - rc = -EINVAL; - syslog(LOG_WARNING, - "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, - attr_id); - goto out; - } - memcpy(gid_ifid, data + CM_SIDR_REQ_DGID_POS, sizeof(*gid_ifid)); - rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid); - break; - - case UMAD_CM_ATTR_REP: - /* Fall through */ - case UMAD_CM_ATTR_REJ: - /* Fall through */ - case UMAD_CM_ATTR_DREQ: - /* Fall through */ - case UMAD_CM_ATTR_DREP: - /* Fall through */ - case UMAD_CM_ATTR_RTU: - data += sizeof(comm_id); - /* Fall through */ - case UMAD_CM_ATTR_SIDR_REP: - if (unlikely(umad_len < sizeof(*hdr) + sizeof(comm_id))) { - rc = -EINVAL; - syslog(LOG_WARNING, - "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, - attr_id); - goto out; - } - memcpy(&comm_id, data, sizeof(comm_id)); - if (comm_id) { - rc = hash_tbl_search_fd_by_comm_id(comm_id, fd, gid_ifid); - } - break; - - default: - rc = -EINVAL; - syslog(LOG_WARNING, "Unsupported attr_id 0x%x\n", attr_id); - } - - syslog(LOG_DEBUG, "mad_to_vm: %d 0x%x 0x%x\n", *fd, attr_id, comm_id); - -out: - return rc; -} - -static void *umad_recv_thread_func(void *args) -{ - int rc; - RdmaCmMuxMsg msg = {}; - int fd = -2; - - msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ; - msg.hdr.op_code = RDMACM_MUX_OP_CODE_MAD; - - while (server.run) { - do { - msg.umad_len = sizeof(msg.umad.mad); - rc = umad_recv(server.umad_agent.port_id, &msg.umad, &msg.umad_len, - SLEEP_SECS * SCALE_US); - if ((rc == -EIO) || (rc == -EINVAL)) { - syslog(LOG_CRIT, "Fatal error while trying to read MAD"); - } - - if (rc == -ETIMEDOUT) { - g_hash_table_foreach_remove(server.umad_agent.commid2fd, - remove_old_comm_ids, NULL); - } - } while (rc && server.run); - - if (server.run) { - rc = get_fd(msg.umad.mad, msg.umad_len, &fd, - &msg.hdr.sgid.global.interface_id); - if (rc) { - continue; - } - - send(fd, &msg, sizeof(msg), 0); - } - } - - return NULL; -} - -static int read_and_process(int fd) -{ - int rc; - RdmaCmMuxMsg msg = {}; - struct umad_hdr *hdr; - uint32_t *comm_id = 0; - uint16_t attr_id; - - rc = recv(fd, &msg, sizeof(msg), 0); - syslog(LOG_DEBUG, "Socket %d, recv %d\n", fd, rc); - - if (rc < 0 && errno != EWOULDBLOCK) { - syslog(LOG_ERR, "Fail to read from socket %d\n", fd); - return -EIO; - } - - if (!rc) { - syslog(LOG_ERR, "Fail to read from socket %d\n", fd); - return -EPIPE; - } - - if (msg.hdr.msg_type != RDMACM_MUX_MSG_TYPE_REQ) { - syslog(LOG_WARNING, "Got non-request message (%d) from socket %d\n", - msg.hdr.msg_type, fd); - return -EPERM; - } - - switch (msg.hdr.op_code) { - case RDMACM_MUX_OP_CODE_REG: - rc = add_fd_ifid_pair(fd, msg.hdr.sgid.global.interface_id); - break; - - case RDMACM_MUX_OP_CODE_UNREG: - rc = delete_fd_ifid_pair(fd, msg.hdr.sgid.global.interface_id); - break; - - case RDMACM_MUX_OP_CODE_MAD: - /* If this is REQ or REP then store the pair comm_id,fd to be later - * used for other messages where gid is unknown */ - hdr = (struct umad_hdr *)msg.umad.mad; - attr_id = be16toh(hdr->attr_id); - if ((attr_id == UMAD_CM_ATTR_REQ) || (attr_id == UMAD_CM_ATTR_DREQ) || - (attr_id == UMAD_CM_ATTR_SIDR_REQ) || - (attr_id == UMAD_CM_ATTR_REP) || (attr_id == UMAD_CM_ATTR_DREP)) { - comm_id = (uint32_t *)(msg.umad.mad + sizeof(*hdr)); - hash_tbl_save_fd_comm_id_pair(fd, *comm_id, - msg.hdr.sgid.global.interface_id); - } - - syslog(LOG_DEBUG, "vm_to_mad: %d 0x%x 0x%x\n", fd, attr_id, - comm_id ? *comm_id : 0); - rc = umad_send(server.umad_agent.port_id, server.umad_agent.agent_id, - &msg.umad, msg.umad_len, 1, 0); - if (rc) { - syslog(LOG_ERR, - "Fail to send MAD message (0x%x) from socket %d, err=%d", - attr_id, fd, rc); - } - break; - - default: - syslog(LOG_ERR, "Got invalid op_code (%d) from socket %d", - msg.hdr.msg_type, fd); - rc = RDMACM_MUX_ERR_CODE_EINVAL; - } - - msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_RESP; - msg.hdr.err_code = rc; - rc = send(fd, &msg, sizeof(msg), 0); - - return rc == sizeof(msg) ? 0 : -EPIPE; -} - -static int accept_all(void) -{ - int fd, rc = 0; - - pthread_rwlock_wrlock(&server.lock); - - do { - if ((server.nfds + 1) > MAX_CLIENTS) { - syslog(LOG_WARNING, "Too many clients (%d)", server.nfds); - rc = -EIO; - goto out; - } - - fd = accept(server.fds[0].fd, NULL, NULL); - if (fd < 0) { - if (errno != EWOULDBLOCK) { - syslog(LOG_WARNING, "accept() failed"); - rc = -EIO; - goto out; - } - break; - } - - syslog(LOG_INFO, "Client connected on socket %d\n", fd); - server.fds[server.nfds].fd = fd; - server.fds[server.nfds].events = POLLIN; - server.nfds++; - } while (fd != -1); - -out: - pthread_rwlock_unlock(&server.lock); - return rc; -} - -static void compress_fds(void) -{ - int i, j; - int closed = 0; - - pthread_rwlock_wrlock(&server.lock); - - for (i = 1; i < server.nfds; i++) { - if (!server.fds[i].fd) { - closed++; - for (j = i; j < server.nfds - 1; j++) { - server.fds[j] = server.fds[j + 1]; - } - } - } - - server.nfds -= closed; - - pthread_rwlock_unlock(&server.lock); -} - -static void close_fd(int idx) -{ - close(server.fds[idx].fd); - syslog(LOG_INFO, "Socket %d closed\n", server.fds[idx].fd); - hash_tbl_remove_fd_ifid_pair(server.fds[idx].fd); - server.fds[idx].fd = 0; -} - -static void run(void) -{ - int rc, nfds, i; - bool compress = false; - - syslog(LOG_INFO, "Service started"); - - while (server.run) { - rc = poll(server.fds, server.nfds, SLEEP_SECS * SCALE_US); - if (rc < 0) { - if (errno != EINTR) { - syslog(LOG_WARNING, "poll() failed"); - } - continue; - } - - if (rc == 0) { - continue; - } - - nfds = server.nfds; - for (i = 0; i < nfds; i++) { - syslog(LOG_DEBUG, "pollfd[%d]: revents 0x%x, events 0x%x\n", i, - server.fds[i].revents, server.fds[i].events); - if (server.fds[i].revents == 0) { - continue; - } - - if (server.fds[i].revents != POLLIN) { - if (i == 0) { - syslog(LOG_NOTICE, "Unexpected poll() event (0x%x)\n", - server.fds[i].revents); - } else { - close_fd(i); - compress = true; - } - continue; - } - - if (i == 0) { - rc = accept_all(); - if (rc) { - continue; - } - } else { - rc = read_and_process(server.fds[i].fd); - if (rc) { - close_fd(i); - compress = true; - } - } - } - - if (compress) { - compress = false; - compress_fds(); - } - } -} - -static void fini_listener(void) -{ - int i; - - if (server.fds[0].fd <= 0) { - return; - } - - for (i = server.nfds - 1; i >= 0; i--) { - if (server.fds[i].fd) { - close(server.fds[i].fd); - } - } - - unlink(server.args.unix_socket_path); -} - -static void fini_umad(void) -{ - if (server.umad_agent.agent_id) { - umad_unregister(server.umad_agent.port_id, server.umad_agent.agent_id); - } - - if (server.umad_agent.port_id) { - umad_close_port(server.umad_agent.port_id); - } - - hash_tbl_free(); -} - -static void fini(void) -{ - if (server.umad_recv_thread) { - pthread_join(server.umad_recv_thread, NULL); - server.umad_recv_thread = 0; - } - fini_umad(); - fini_listener(); - pthread_rwlock_destroy(&server.lock); - - syslog(LOG_INFO, "Service going down"); -} - -static int init_listener(void) -{ - struct sockaddr_un sun; - int rc, on = 1; - - server.fds[0].fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (server.fds[0].fd < 0) { - syslog(LOG_ALERT, "socket() failed"); - return -EIO; - } - - rc = setsockopt(server.fds[0].fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, - sizeof(on)); - if (rc < 0) { - syslog(LOG_ALERT, "setsockopt() failed"); - rc = -EIO; - goto err; - } - - rc = ioctl(server.fds[0].fd, FIONBIO, (char *)&on); - if (rc < 0) { - syslog(LOG_ALERT, "ioctl() failed"); - rc = -EIO; - goto err; - } - - if (strlen(server.args.unix_socket_path) >= sizeof(sun.sun_path)) { - syslog(LOG_ALERT, - "Invalid unix_socket_path, size must be less than %ld\n", - sizeof(sun.sun_path)); - rc = -EINVAL; - goto err; - } - - sun.sun_family = AF_UNIX; - rc = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", - server.args.unix_socket_path); - if (rc < 0 || rc >= sizeof(sun.sun_path)) { - syslog(LOG_ALERT, "Could not copy unix socket path\n"); - rc = -EINVAL; - goto err; - } - - rc = bind(server.fds[0].fd, (struct sockaddr *)&sun, sizeof(sun)); - if (rc < 0) { - syslog(LOG_ALERT, "bind() failed"); - rc = -EIO; - goto err; - } - - rc = listen(server.fds[0].fd, SERVER_LISTEN_BACKLOG); - if (rc < 0) { - syslog(LOG_ALERT, "listen() failed"); - rc = -EIO; - goto err; - } - - server.fds[0].events = POLLIN; - server.nfds = 1; - server.run = true; - - return 0; - -err: - close(server.fds[0].fd); - return rc; -} - -static int init_umad(void) -{ - long method_mask[IB_USER_MAD_LONGS_PER_METHOD_MASK]; - - server.umad_agent.port_id = umad_open_port(server.args.rdma_dev_name, - server.args.rdma_port_num); - - if (server.umad_agent.port_id < 0) { - syslog(LOG_WARNING, "umad_open_port() failed"); - return -EIO; - } - - memset(&method_mask, 0, sizeof(method_mask)); - method_mask[0] = MAD_METHOD_MASK0; - server.umad_agent.agent_id = umad_register(server.umad_agent.port_id, - UMAD_CLASS_CM, - UMAD_SA_CLASS_VERSION, - MAD_RMPP_VERSION, method_mask); - if (server.umad_agent.agent_id < 0) { - syslog(LOG_WARNING, "umad_register() failed"); - return -EIO; - } - - hash_tbl_alloc(); - - return 0; -} - -static void signal_handler(int sig, siginfo_t *siginfo, void *context) -{ - static bool warned; - - /* Prevent stop if clients are connected */ - if (server.nfds != 1) { - if (!warned) { - syslog(LOG_WARNING, - "Can't stop while active client exist, resend SIGINT to overid"); - warned = true; - return; - } - } - - if (sig == SIGINT) { - server.run = false; - fini(); - } - - exit(0); -} - -static int init(void) -{ - int rc; - struct sigaction sig = {}; - - rc = init_listener(); - if (rc) { - return rc; - } - - rc = init_umad(); - if (rc) { - return rc; - } - - pthread_rwlock_init(&server.lock, 0); - - rc = pthread_create(&server.umad_recv_thread, NULL, umad_recv_thread_func, - NULL); - if (rc) { - syslog(LOG_ERR, "Fail to create UMAD receiver thread (%d)\n", rc); - return rc; - } - - sig.sa_sigaction = &signal_handler; - sig.sa_flags = SA_SIGINFO; - rc = sigaction(SIGINT, &sig, NULL); - if (rc < 0) { - syslog(LOG_ERR, "Fail to install SIGINT handler (%d)\n", errno); - return rc; - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - int rc; - - memset(&server, 0, sizeof(server)); - - parse_args(argc, argv); - - rc = init(); - if (rc) { - syslog(LOG_ERR, "Fail to initialize server (%d)\n", rc); - rc = -EAGAIN; - goto out; - } - - run(); - -out: - fini(); - - return rc; -} diff --git a/contrib/rdmacm-mux/meson.build b/contrib/rdmacm-mux/meson.build deleted file mode 100644 index 36c9c89630..0000000000 --- a/contrib/rdmacm-mux/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -if have_pvrdma - # FIXME: broken on big endian architectures - executable('rdmacm-mux', files('main.c'), genh, - dependencies: [glib, libumad], - build_by_default: false, - install: false) -endif diff --git a/contrib/rdmacm-mux/rdmacm-mux.h b/contrib/rdmacm-mux/rdmacm-mux.h deleted file mode 100644 index 07a4722913..0000000000 --- a/contrib/rdmacm-mux/rdmacm-mux.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * QEMU paravirtual RDMA - rdmacm-mux declarations - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 RDMACM_MUX_H -#define RDMACM_MUX_H - -#include "linux/if.h" -#include -#include -#include - -typedef enum RdmaCmMuxMsgType { - RDMACM_MUX_MSG_TYPE_REQ = 0, - RDMACM_MUX_MSG_TYPE_RESP = 1, -} RdmaCmMuxMsgType; - -typedef enum RdmaCmMuxOpCode { - RDMACM_MUX_OP_CODE_REG = 0, - RDMACM_MUX_OP_CODE_UNREG = 1, - RDMACM_MUX_OP_CODE_MAD = 2, -} RdmaCmMuxOpCode; - -typedef enum RdmaCmMuxErrCode { - RDMACM_MUX_ERR_CODE_OK = 0, - RDMACM_MUX_ERR_CODE_EINVAL = 1, - RDMACM_MUX_ERR_CODE_EEXIST = 2, - RDMACM_MUX_ERR_CODE_EACCES = 3, - RDMACM_MUX_ERR_CODE_ENOTFOUND = 4, -} RdmaCmMuxErrCode; - -typedef struct RdmaCmMuxHdr { - RdmaCmMuxMsgType msg_type; - RdmaCmMuxOpCode op_code; - union ibv_gid sgid; - RdmaCmMuxErrCode err_code; -} RdmaCmUHdr; - -typedef struct RdmaCmUMad { - struct ib_user_mad hdr; - char mad[RDMA_MAX_PRIVATE_DATA]; -} RdmaCmUMad; - -typedef struct RdmaCmMuxMsg { - RdmaCmUHdr hdr; - int umad_len; - RdmaCmUMad umad; -} RdmaCmMuxMsg; - -#endif diff --git a/contrib/systemd/qemu-vmsr-helper.service b/contrib/systemd/qemu-vmsr-helper.service new file mode 100644 index 0000000000..8fd397bf79 --- /dev/null +++ b/contrib/systemd/qemu-vmsr-helper.service @@ -0,0 +1,15 @@ +[Unit] +Description=Virtual RAPL MSR Daemon for QEMU + +[Service] +WorkingDirectory=/tmp +Type=simple +ExecStart=/usr/bin/qemu-vmsr-helper +PrivateTmp=yes +ProtectSystem=strict +ReadWritePaths=/var/run +RestrictAddressFamilies=AF_UNIX +Restart=always +RestartSec=0 + +[Install] diff --git a/contrib/systemd/qemu-vmsr-helper.socket b/contrib/systemd/qemu-vmsr-helper.socket new file mode 100644 index 0000000000..183e8304d6 --- /dev/null +++ b/contrib/systemd/qemu-vmsr-helper.socket @@ -0,0 +1,9 @@ +[Unit] +Description=Virtual RAPL MSR helper for QEMU + +[Socket] +ListenStream=/run/qemu-vmsr-helper.sock +SocketMode=0600 + +[Install] +WantedBy=multi-user.target diff --git a/contrib/vhost-user-blk/meson.build b/contrib/vhost-user-blk/meson.build index dcb9e2ffcd..ac1eece37a 100644 --- a/contrib/vhost-user-blk/meson.build +++ b/contrib/vhost-user-blk/meson.build @@ -1,4 +1,4 @@ executable('vhost-user-blk', files('vhost-user-blk.c'), dependencies: [qemuutil, vhost_user], - build_by_default: targetos == 'linux', + 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 d6932a2645..6cc18a1c04 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "standard-headers/linux/virtio_blk.h" #include "libvhost-user-glib.h" @@ -193,9 +194,9 @@ 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; - uint64_t range[2] = { le64toh(desc->sector) << 9, - le32toh(desc->num_sectors) << 9 }; + desc = buf; + uint64_t range[2] = { le64_to_cpu(desc->sector) << 9, + (uint64_t)le32_to_cpu(desc->num_sectors) << 9 }; if (type == VIRTIO_BLK_T_DISCARD) { if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) { g_free(buf); @@ -267,13 +268,13 @@ static int vub_virtio_process_req(VubDev *vdev_blk, req->in = (struct virtio_blk_inhdr *)elem->in_sg[in_num - 1].iov_base; in_num--; - type = le32toh(req->out->type); + type = le32_to_cpu(req->out->type); switch (type & ~VIRTIO_BLK_T_BARRIER) { case VIRTIO_BLK_T_IN: case VIRTIO_BLK_T_OUT: { ssize_t ret = 0; bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = le64toh(req->out->sector); + req->sector_num = le64_to_cpu(req->out->sector); if (is_write) { ret = vub_writev(req, &elem->out_sg[1], out_num); } else { @@ -421,7 +422,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; } @@ -469,7 +470,6 @@ static int unix_sock_new(char *unix_fn) { int sock; struct sockaddr_un un; - size_t len; assert(unix_fn); @@ -481,10 +481,9 @@ static int unix_sock_new(char *unix_fn) un.sun_family = AF_UNIX; (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn); - len = sizeof(un.sun_family) + strlen(un.sun_path); (void)unlink(unix_fn); - if (bind(sock, (struct sockaddr *)&un, len) < 0) { + if (bind(sock, (struct sockaddr *)&un, sizeof(un)) < 0) { perror("bind"); goto fail; } @@ -532,9 +531,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/main.c b/contrib/vhost-user-input/main.c index 081230da54..f3362d41ac 100644 --- a/contrib/vhost-user-input/main.c +++ b/contrib/vhost-user-input/main.c @@ -51,8 +51,8 @@ static void vi_input_send(VuInput *vi, struct virtio_input_event *event) vi->queue[vi->qindex++].event = *event; /* ... until we see a report sync ... */ - if (event->type != htole16(EV_SYN) || - event->code != htole16(SYN_REPORT)) { + if (event->type != cpu_to_le16(EV_SYN) || + event->code != cpu_to_le16(SYN_REPORT)) { return; } @@ -103,9 +103,9 @@ vi_evdev_watch(VuDev *dev, int condition, void *data) g_debug("input %d %d %d", evdev.type, evdev.code, evdev.value); - virtio.type = htole16(evdev.type); - virtio.code = htole16(evdev.code); - virtio.value = htole32(evdev.value); + virtio.type = cpu_to_le16(evdev.type); + virtio.code = cpu_to_le16(evdev.code); + virtio.value = cpu_to_le32(evdev.value); vi_input_send(vi, &virtio); } } @@ -124,9 +124,9 @@ static void vi_handle_status(VuInput *vi, virtio_input_event *event) evdev.input_event_sec = tval.tv_sec; evdev.input_event_usec = tval.tv_usec; - evdev.type = le16toh(event->type); - evdev.code = le16toh(event->code); - evdev.value = le32toh(event->value); + evdev.type = le16_to_cpu(event->type); + evdev.code = le16_to_cpu(event->code); + evdev.value = le32_to_cpu(event->value); rc = write(vi->evdevfd, &evdev, sizeof(evdev)); if (rc == -1) { 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/cpu-common.c b/cpu-common.c new file mode 100644 index 0000000000..0d607bbe49 --- /dev/null +++ b/cpu-common.c @@ -0,0 +1,457 @@ +/* + * CPU thread main loop - common bits for user and system mode emulation + * + * 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 "qemu/main-loop.h" +#include "exec/cpu-common.h" +#include "hw/core/cpu.h" +#include "sysemu/cpus.h" +#include "qemu/lockable.h" +#include "trace/trace-root.h" + +QemuMutex qemu_cpu_list_lock; +static QemuCond exclusive_cond; +static QemuCond exclusive_resume; +static QemuCond qemu_work_cond; + +/* >= 1 if a thread is inside start_exclusive/end_exclusive. Written + * under qemu_cpu_list_lock, read with atomic operations. + */ +static int pending_cpus; + +void qemu_init_cpu_list(void) +{ + /* This is needed because qemu_init_cpu_list is also called by the + * child process in a fork. */ + pending_cpus = 0; + + qemu_mutex_init(&qemu_cpu_list_lock); + qemu_cond_init(&exclusive_cond); + qemu_cond_init(&exclusive_resume); + qemu_cond_init(&qemu_work_cond); +} + +void cpu_list_lock(void) +{ + qemu_mutex_lock(&qemu_cpu_list_lock); +} + +void cpu_list_unlock(void) +{ + qemu_mutex_unlock(&qemu_cpu_list_lock); +} + + +int cpu_get_free_index(void) +{ + CPUState *some_cpu; + int max_cpu_index = 0; + + CPU_FOREACH(some_cpu) { + if (some_cpu->cpu_index >= max_cpu_index) { + max_cpu_index = some_cpu->cpu_index + 1; + } + } + return max_cpu_index; +} + +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) +{ + static bool cpu_index_auto_assigned; + + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); + if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) { + cpu_index_auto_assigned = true; + cpu->cpu_index = cpu_get_free_index(); + assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); + } else { + assert(!cpu_index_auto_assigned); + } + QTAILQ_INSERT_TAIL_RCU(&cpus_queue, cpu, node); + cpu_list_generation_id++; +} + +void cpu_list_remove(CPUState *cpu) +{ + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); + if (!QTAILQ_IN_USE(cpu, node)) { + /* there is nothing to undo since cpu_exec_init() hasn't been called */ + return; + } + + QTAILQ_REMOVE_RCU(&cpus_queue, cpu, node); + cpu->cpu_index = UNASSIGNED_CPU_INDEX; + cpu_list_generation_id++; +} + +CPUState *qemu_get_cpu(int index) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (cpu->cpu_index == index) { + return cpu; + } + } + + return NULL; +} + +/* current CPU in the current thread. It is only valid inside cpu_exec() */ +__thread CPUState *current_cpu; + +struct qemu_work_item { + QSIMPLEQ_ENTRY(qemu_work_item) node; + run_on_cpu_func func; + run_on_cpu_data data; + bool free, exclusive, done; +}; + +static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) +{ + qemu_mutex_lock(&cpu->work_mutex); + QSIMPLEQ_INSERT_TAIL(&cpu->work_list, wi, node); + wi->done = false; + qemu_mutex_unlock(&cpu->work_mutex); + + qemu_cpu_kick(cpu); +} + +void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, + QemuMutex *mutex) +{ + struct qemu_work_item wi; + + if (qemu_cpu_is_self(cpu)) { + func(cpu, data); + return; + } + + wi.func = func; + wi.data = data; + wi.done = false; + wi.free = false; + wi.exclusive = false; + + queue_work_on_cpu(cpu, &wi); + while (!qatomic_load_acquire(&wi.done)) { + CPUState *self_cpu = current_cpu; + + qemu_cond_wait(&qemu_work_cond, mutex); + current_cpu = self_cpu; + } +} + +void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) +{ + struct qemu_work_item *wi; + + wi = g_new0(struct qemu_work_item, 1); + wi->func = func; + wi->data = data; + wi->free = true; + + queue_work_on_cpu(cpu, wi); +} + +/* Wait for pending exclusive operations to complete. The CPU list lock + must be held. */ +static inline void exclusive_idle(void) +{ + while (pending_cpus) { + qemu_cond_wait(&exclusive_resume, &qemu_cpu_list_lock); + } +} + +/* Start an exclusive operation. + Must only be called from outside cpu_exec. */ +void start_exclusive(void) +{ + CPUState *other_cpu; + int running_cpus; + + /* Ensure we are not running, or start_exclusive will be blocked. */ + g_assert(!current_cpu->running); + + if (current_cpu->exclusive_context_count) { + current_cpu->exclusive_context_count++; + return; + } + + qemu_mutex_lock(&qemu_cpu_list_lock); + exclusive_idle(); + + /* Make all other cpus stop executing. */ + qatomic_set(&pending_cpus, 1); + + /* Write pending_cpus before reading other_cpu->running. */ + smp_mb(); + running_cpus = 0; + CPU_FOREACH(other_cpu) { + if (qatomic_read(&other_cpu->running)) { + other_cpu->has_waiter = true; + running_cpus++; + qemu_cpu_kick(other_cpu); + } + } + + qatomic_set(&pending_cpus, running_cpus + 1); + while (pending_cpus > 1) { + qemu_cond_wait(&exclusive_cond, &qemu_cpu_list_lock); + } + + /* Can release mutex, no one will enter another exclusive + * section until end_exclusive resets pending_cpus to 0. + */ + qemu_mutex_unlock(&qemu_cpu_list_lock); + + current_cpu->exclusive_context_count = 1; +} + +/* Finish an exclusive operation. */ +void end_exclusive(void) +{ + current_cpu->exclusive_context_count--; + if (current_cpu->exclusive_context_count) { + return; + } + + qemu_mutex_lock(&qemu_cpu_list_lock); + qatomic_set(&pending_cpus, 0); + qemu_cond_broadcast(&exclusive_resume); + qemu_mutex_unlock(&qemu_cpu_list_lock); +} + +/* Wait for exclusive ops to finish, and begin cpu execution. */ +void cpu_exec_start(CPUState *cpu) +{ + qatomic_set(&cpu->running, true); + + /* Write cpu->running before reading pending_cpus. */ + smp_mb(); + + /* 1. start_exclusive saw cpu->running == true and pending_cpus >= 1. + * After taking the lock we'll see cpu->has_waiter == true and run---not + * for long because start_exclusive kicked us. cpu_exec_end will + * decrement pending_cpus and signal the waiter. + * + * 2. start_exclusive saw cpu->running == false but pending_cpus >= 1. + * This includes the case when an exclusive item is running now. + * Then we'll see cpu->has_waiter == false and wait for the item to + * complete. + * + * 3. pending_cpus == 0. Then start_exclusive is definitely going to + * see cpu->running == true, and it will kick the CPU. + */ + if (unlikely(qatomic_read(&pending_cpus))) { + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); + if (!cpu->has_waiter) { + /* Not counted in pending_cpus, let the exclusive item + * run. Since we have the lock, just set cpu->running to true + * while holding it; no need to check pending_cpus again. + */ + qatomic_set(&cpu->running, false); + exclusive_idle(); + /* Now pending_cpus is zero. */ + qatomic_set(&cpu->running, true); + } else { + /* Counted in pending_cpus, go ahead and release the + * waiter at cpu_exec_end. + */ + } + } +} + +/* Mark cpu as not executing, and release pending exclusive ops. */ +void cpu_exec_end(CPUState *cpu) +{ + qatomic_set(&cpu->running, false); + + /* Write cpu->running before reading pending_cpus. */ + smp_mb(); + + /* 1. start_exclusive saw cpu->running == true. Then it will increment + * pending_cpus and wait for exclusive_cond. After taking the lock + * we'll see cpu->has_waiter == true. + * + * 2. start_exclusive saw cpu->running == false but here pending_cpus >= 1. + * This includes the case when an exclusive item started after setting + * cpu->running to false and before we read pending_cpus. Then we'll see + * cpu->has_waiter == false and not touch pending_cpus. The next call to + * cpu_exec_start will run exclusive_idle if still necessary, thus waiting + * for the item to complete. + * + * 3. pending_cpus == 0. Then start_exclusive is definitely going to + * see cpu->running == false, and it can ignore this CPU until the + * next cpu_exec_start. + */ + if (unlikely(qatomic_read(&pending_cpus))) { + QEMU_LOCK_GUARD(&qemu_cpu_list_lock); + if (cpu->has_waiter) { + cpu->has_waiter = false; + qatomic_set(&pending_cpus, pending_cpus - 1); + if (pending_cpus == 1) { + qemu_cond_signal(&exclusive_cond); + } + } + } +} + +void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, + run_on_cpu_data data) +{ + struct qemu_work_item *wi; + + wi = g_new0(struct qemu_work_item, 1); + wi->func = func; + wi->data = data; + wi->free = true; + wi->exclusive = true; + + queue_work_on_cpu(cpu, wi); +} + +void free_queued_cpu_work(CPUState *cpu) +{ + while (!QSIMPLEQ_EMPTY(&cpu->work_list)) { + struct qemu_work_item *wi = QSIMPLEQ_FIRST(&cpu->work_list); + QSIMPLEQ_REMOVE_HEAD(&cpu->work_list, node); + if (wi->free) { + g_free(wi); + } + } +} + +void process_queued_cpu_work(CPUState *cpu) +{ + struct qemu_work_item *wi; + + qemu_mutex_lock(&cpu->work_mutex); + if (QSIMPLEQ_EMPTY(&cpu->work_list)) { + qemu_mutex_unlock(&cpu->work_mutex); + return; + } + while (!QSIMPLEQ_EMPTY(&cpu->work_list)) { + wi = QSIMPLEQ_FIRST(&cpu->work_list); + QSIMPLEQ_REMOVE_HEAD(&cpu->work_list, node); + qemu_mutex_unlock(&cpu->work_mutex); + if (wi->exclusive) { + /* Running work items outside the BQL avoids the following deadlock: + * 1) start_exclusive() is called with the BQL taken while another + * CPU is running; 2) cpu_exec in the other CPU tries to takes the + * BQL, so it goes to sleep; start_exclusive() is sleeping too, so + * neither CPU can proceed. + */ + bql_unlock(); + start_exclusive(); + wi->func(cpu, wi->data); + end_exclusive(); + bql_lock(); + } else { + wi->func(cpu, wi->data); + } + qemu_mutex_lock(&cpu->work_mutex); + if (wi->free) { + g_free(wi); + } else { + 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-target.c b/cpu-target.c new file mode 100644 index 0000000000..499facf774 --- /dev/null +++ b/cpu-target.c @@ -0,0 +1,474 @@ +/* + * Target-specific parts of the CPU object + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" + +#include "exec/target_page.h" +#include "exec/page-protection.h" +#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 "exec/tswap.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" + +#ifndef CONFIG_USER_ONLY +static int cpu_common_post_load(void *opaque, int version_id) +{ + CPUState *cpu = opaque; + + /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + version_id is increased. */ + cpu->interrupt_request &= ~0x01; + tlb_flush(cpu); + + /* loadvm has just updated the content of RAM, bypassing the + * usual mechanisms that ensure we flush TBs for writes to + * memory we've translated code from. So we must flush all TBs, + * which will now be stale. + */ + tb_flush(cpu); + + return 0; +} + +static int cpu_common_pre_load(void *opaque) +{ + CPUState *cpu = opaque; + + cpu->exception_index = -1; + + return 0; +} + +static bool cpu_common_exception_index_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return tcg_enabled() && cpu->exception_index != -1; +} + +static const VMStateDescription vmstate_cpu_common_exception_index = { + .name = "cpu_common/exception_index", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_exception_index_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(exception_index, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool cpu_common_crash_occurred_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return cpu->crash_occurred; +} + +static const VMStateDescription vmstate_cpu_common_crash_occurred = { + .name = "cpu_common/crash_occurred", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_crash_occurred_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(crash_occurred, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cpu_common = { + .name = "cpu_common", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = cpu_common_pre_load, + .post_load = cpu_common_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(halted, CPUState), + VMSTATE_UINT32(interrupt_request, CPUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_cpu_common_exception_index, + &vmstate_cpu_common_crash_occurred, + NULL + } +}; +#endif + +bool cpu_exec_realizefn(CPUState *cpu, Error **errp) +{ + /* 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); + +#ifdef CONFIG_USER_ONLY + assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || + qdev_get_vmsd(DEVICE(cpu))->unmigratable); +#else + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, 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) +{ +#ifndef CONFIG_USER_ONLY + CPUClass *cc = CPU_GET_CLASS(cpu); + + if (cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_unregister(NULL, cc->sysemu_ops->legacy_vmsd, cpu); + } + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_unregister(NULL, &vmstate_cpu_common, cpu); + } +#endif + + 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); +} + +/* + * This can't go in hw/core/cpu.c because that file is compiled only + * once for both user-mode and system builds. + */ +static Property cpu_common_props[] = { +#ifdef CONFIG_USER_ONLY + /* + * Create a property for the user-only object, so users can + * adjust prctl(PR_SET_UNALIGN) from the command-line. + * Has no effect if the target does not support the feature. + */ + DEFINE_PROP_BOOL("prctl-unalign-sigbus", CPUState, + prctl_unalign_sigbus, false), +#else + /* + * 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. + */ + DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), +#endif + DEFINE_PROP_END_OF_LIST(), +}; + +#ifndef CONFIG_USER_ONLY +static bool cpu_get_start_powered_off(Object *obj, Error **errp) +{ + CPUState *cpu = CPU(obj); + return cpu->start_powered_off; +} + +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); + + /* + * We can't use DEFINE_PROP_BOOL in the Property array for this + * property, because we want this to be settable after realize. + */ + 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) +{ + cpu->as = NULL; + cpu->num_ases = 0; + +#ifndef CONFIG_USER_ONLY + cpu->memory = get_system_memory(); + object_ref(OBJECT(cpu->memory)); +#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; + CPUClass *cc; + gchar **model_pieces; + const char *cpu_type; + + model_pieces = g_strsplit(cpu_option, ",", 2); + if (!model_pieces[0]) { + error_report("-cpu option cannot be empty"); + exit(1); + } + + oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]); + if (oc == NULL) { + error_report("unable to find CPU model '%s'", model_pieces[0]); + g_strfreev(model_pieces); + exit(EXIT_FAILURE); + } + + cpu_type = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(cpu_type, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); + return cpu_type; +} + +#ifndef cpu_list +static void cpu_list_entry(gpointer data, gpointer user_data) +{ + 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 (cc->deprecation_note) { + qemu_printf(" %s (deprecated)\n", model); + } else { + qemu_printf(" %s\n", model); + } +} + +static void cpu_list(void) +{ + GSList *list; + + 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 + +void list_cpus(void) +{ + cpu_list(); +} + +/* enable or disable single step mode. EXCP_DEBUG is returned by the + CPU loop after each instruction */ +void cpu_single_step(CPUState *cpu, int enabled) +{ + if (cpu->singlestep_enabled != enabled) { + cpu->singlestep_enabled = enabled; + +#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); + } +} + +void cpu_abort(CPUState *cpu, const char *fmt, ...) +{ + va_list ap; + va_list ap2; + + va_start(ap, fmt); + va_copy(ap2, ap); + fprintf(stderr, "qemu: fatal: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP); + if (qemu_log_separate()) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + fprintf(logfile, "qemu: fatal: "); + vfprintf(logfile, fmt, ap2); + fprintf(logfile, "\n"); + cpu_dump_state(cpu, logfile, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_unlock(logfile); + } + } + va_end(ap2); + va_end(ap); + replay_finish(); +#if defined(CONFIG_USER_ONLY) + { + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + sigaction(SIGABRT, &act, NULL); + } +#endif + abort(); +} + +/* physical memory access (slow version, mainly for debug) */ +#if defined(CONFIG_USER_ONLY) +int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, + void *ptr, size_t len, bool is_write) +{ + int flags; + 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; + l = (page + TARGET_PAGE_SIZE) - addr; + if (l > len) + l = len; + flags = page_get_flags(page); + if (!(flags & PAGE_VALID)) { + goto out_close; + } + if (is_write) { + 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 */ + 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; + } + ret = 0; +out_close: + if (fd != -1) { + close(fd); + } +out: + return ret; +} +#endif + +bool target_words_bigendian(void) +{ + return TARGET_BIG_ENDIAN; +} + +const char *target_name(void) +{ + return TARGET_NAME; +} diff --git a/cpu.c b/cpu.c deleted file mode 100644 index 4a7d865427..0000000000 --- a/cpu.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Target-specific parts of the CPU object - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" - -#include "exec/target_page.h" -#include "hw/qdev-core.h" -#include "hw/qdev-properties.h" -#include "qemu/error-report.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" -#endif -#include "sysemu/tcg.h" -#include "sysemu/kvm.h" -#include "sysemu/replay.h" -#include "exec/cpu-common.h" -#include "exec/exec-all.h" -#include "exec/translate-all.h" -#include "exec/log.h" -#include "hw/core/accel-cpu.h" -#include "trace/trace-root.h" -#include "qemu/accel.h" - -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) -{ - CPUState *cpu = opaque; - - /* 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the - version_id is increased. */ - cpu->interrupt_request &= ~0x01; - tlb_flush(cpu); - - /* loadvm has just updated the content of RAM, bypassing the - * usual mechanisms that ensure we flush TBs for writes to - * memory we've translated code from. So we must flush all TBs, - * which will now be stale. - */ - tb_flush(cpu); - - return 0; -} - -static int cpu_common_pre_load(void *opaque) -{ - CPUState *cpu = opaque; - - cpu->exception_index = -1; - - return 0; -} - -static bool cpu_common_exception_index_needed(void *opaque) -{ - CPUState *cpu = opaque; - - return tcg_enabled() && cpu->exception_index != -1; -} - -static const VMStateDescription vmstate_cpu_common_exception_index = { - .name = "cpu_common/exception_index", - .version_id = 1, - .minimum_version_id = 1, - .needed = cpu_common_exception_index_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(exception_index, CPUState), - VMSTATE_END_OF_LIST() - } -}; - -static bool cpu_common_crash_occurred_needed(void *opaque) -{ - CPUState *cpu = opaque; - - return cpu->crash_occurred; -} - -static const VMStateDescription vmstate_cpu_common_crash_occurred = { - .name = "cpu_common/crash_occurred", - .version_id = 1, - .minimum_version_id = 1, - .needed = cpu_common_crash_occurred_needed, - .fields = (VMStateField[]) { - VMSTATE_BOOL(crash_occurred, CPUState), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_cpu_common = { - .name = "cpu_common", - .version_id = 1, - .minimum_version_id = 1, - .pre_load = cpu_common_pre_load, - .post_load = cpu_common_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(halted, CPUState), - VMSTATE_UINT32(interrupt_request, CPUState), - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_cpu_common_exception_index, - &vmstate_cpu_common_crash_occurred, - NULL - } -}; -#endif - -void cpu_exec_realizefn(CPUState *cpu, Error **errp) -{ - /* cache the cpu class for the hotpath */ - cpu->cc = CPU_GET_CLASS(cpu); - - if (!accel_cpu_realizefn(cpu, errp)) { - return; - } - - /* NB: errp parameter is unused currently */ - if (tcg_enabled()) { - tcg_exec_realizefn(cpu, errp); - } - - /* Wait until cpu initialization complete before exposing cpu. */ - cpu_list_add(cpu); - - /* Plugin initialization must wait until cpu_index assigned. */ - if (tcg_enabled()) { - qemu_plugin_vcpu_init_hook(cpu); - } - -#ifdef CONFIG_USER_ONLY - assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || - qdev_get_vmsd(DEVICE(cpu))->unmigratable); -#else - if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { - vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, 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 */ -} - -void cpu_exec_unrealizefn(CPUState *cpu) -{ -#ifndef CONFIG_USER_ONLY - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->legacy_vmsd != NULL) { - vmstate_unregister(NULL, cc->sysemu_ops->legacy_vmsd, cpu); - } - if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { - vmstate_unregister(NULL, &vmstate_cpu_common, cpu); - } -#endif - if (tcg_enabled()) { - tcg_exec_unrealizefn(cpu); - } - - cpu_list_remove(cpu); -} - -/* - * This can't go in hw/core/cpu.c because that file is compiled only - * once for both user-mode and system builds. - */ -static Property cpu_common_props[] = { -#ifdef CONFIG_USER_ONLY - /* - * Create a property for the user-only object, so users can - * adjust prctl(PR_SET_UNALIGN) from the command-line. - * Has no effect if the target does not support the feature. - */ - DEFINE_PROP_BOOL("prctl-unalign-sigbus", CPUState, - prctl_unalign_sigbus, false), -#else - /* - * Create a memory property for softmmu CPU object, so users can - * wire up its memory. The default if no link is set up is to use - * the system address space. - */ - DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, - MemoryRegion *), -#endif - DEFINE_PROP_END_OF_LIST(), -}; - -static bool cpu_get_start_powered_off(Object *obj, Error **errp) -{ - CPUState *cpu = CPU(obj); - return cpu->start_powered_off; -} - -static void cpu_set_start_powered_off(Object *obj, bool value, Error **errp) -{ - CPUState *cpu = CPU(obj); - cpu->start_powered_off = value; -} - -void cpu_class_init_props(DeviceClass *dc) -{ - 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. - */ - object_class_property_add_bool(oc, "start-powered-off", - cpu_get_start_powered_off, - cpu_set_start_powered_off); -} - -void cpu_exec_initfn(CPUState *cpu) -{ - cpu->as = NULL; - cpu->num_ases = 0; - -#ifndef CONFIG_USER_ONLY - cpu->thread_id = qemu_get_thread_id(); - cpu->memory = get_system_memory(); - object_ref(OBJECT(cpu->memory)); -#endif -} - -const char *parse_cpu_option(const char *cpu_option) -{ - ObjectClass *oc; - CPUClass *cc; - gchar **model_pieces; - const char *cpu_type; - - model_pieces = g_strsplit(cpu_option, ",", 2); - if (!model_pieces[0]) { - error_report("-cpu option cannot be empty"); - exit(1); - } - - oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]); - if (oc == NULL) { - error_report("unable to find CPU model '%s'", model_pieces[0]); - g_strfreev(model_pieces); - exit(EXIT_FAILURE); - } - - cpu_type = object_class_get_name(oc); - cc = CPU_CLASS(oc); - cc->parse_features(cpu_type, model_pieces[1], &error_fatal); - g_strfreev(model_pieces); - return cpu_type; -} - -void list_cpus(const char *optarg) -{ - /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(); -#endif -} - -#if defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(target_ulong addr) -{ - mmap_lock(); - tb_invalidate_phys_page(addr); - 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(ram_addr); -} -#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); - } 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); - } - } -} - -/* enable or disable single step mode. EXCP_DEBUG is returned by the - CPU loop after each instruction */ -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); - } - trace_breakpoint_singlestep(cpu->cpu_index, enabled); - } -} - -void cpu_abort(CPUState *cpu, const char *fmt, ...) -{ - va_list ap; - va_list ap2; - - va_start(ap, fmt); - va_copy(ap2, ap); - fprintf(stderr, "qemu: fatal: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - cpu_dump_state(cpu, stderr, CPU_DUMP_FPU | CPU_DUMP_CCOP); - if (qemu_log_separate()) { - FILE *logfile = qemu_log_trylock(); - if (logfile) { - fprintf(logfile, "qemu: fatal: "); - vfprintf(logfile, fmt, ap2); - fprintf(logfile, "\n"); - cpu_dump_state(cpu, logfile, CPU_DUMP_FPU | CPU_DUMP_CCOP); - qemu_log_unlock(logfile); - } - } - va_end(ap2); - va_end(ap); - replay_finish(); -#if defined(CONFIG_USER_ONLY) - { - struct sigaction act; - sigfillset(&act.sa_mask); - act.sa_handler = SIG_DFL; - act.sa_flags = 0; - sigaction(SIGABRT, &act, NULL); - } -#endif - abort(); -} - -/* physical memory access (slow version, mainly for debug) */ -#if defined(CONFIG_USER_ONLY) -int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, - void *ptr, size_t len, bool is_write) -{ - int flags; - vaddr l, page; - void * p; - uint8_t *buf = ptr; - - while (len > 0) { - page = addr & TARGET_PAGE_MASK; - l = (page + TARGET_PAGE_SIZE) - addr; - if (l > len) - l = len; - flags = page_get_flags(page); - if (!(flags & PAGE_VALID)) - return -1; - if (is_write) { - if (!(flags & PAGE_WRITE)) - return -1; - /* 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; - memcpy(buf, p, l); - unlock_user(p, addr, 0); - } - len -= l; - buf += l; - addr += l; - } - return 0; -} -#endif - -bool target_words_bigendian(void) -{ -#if TARGET_BIG_ENDIAN - return true; -#else - return false; -#endif -} - -void page_size_init(void) -{ - /* NOTE: we can always suppose that qemu_host_page_size >= - TARGET_PAGE_SIZE */ - if (qemu_host_page_size == 0) { - qemu_host_page_size = qemu_real_host_page_size(); - } - 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; -} diff --git a/cpus-common.c b/cpus-common.c deleted file mode 100644 index 793364dc0e..0000000000 --- a/cpus-common.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * CPU thread main loop - common bits for user and system mode emulation - * - * 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 "qemu/main-loop.h" -#include "exec/cpu-common.h" -#include "hw/core/cpu.h" -#include "sysemu/cpus.h" -#include "qemu/lockable.h" - -static QemuMutex qemu_cpu_list_lock; -static QemuCond exclusive_cond; -static QemuCond exclusive_resume; -static QemuCond qemu_work_cond; - -/* >= 1 if a thread is inside start_exclusive/end_exclusive. Written - * under qemu_cpu_list_lock, read with atomic operations. - */ -static int pending_cpus; - -void qemu_init_cpu_list(void) -{ - /* This is needed because qemu_init_cpu_list is also called by the - * child process in a fork. */ - pending_cpus = 0; - - qemu_mutex_init(&qemu_cpu_list_lock); - qemu_cond_init(&exclusive_cond); - qemu_cond_init(&exclusive_resume); - qemu_cond_init(&qemu_work_cond); -} - -void cpu_list_lock(void) -{ - qemu_mutex_lock(&qemu_cpu_list_lock); -} - -void cpu_list_unlock(void) -{ - qemu_mutex_unlock(&qemu_cpu_list_lock); -} - -static bool cpu_index_auto_assigned; - -static int cpu_get_free_index(void) -{ - CPUState *some_cpu; - int max_cpu_index = 0; - - cpu_index_auto_assigned = true; - CPU_FOREACH(some_cpu) { - if (some_cpu->cpu_index >= max_cpu_index) { - max_cpu_index = some_cpu->cpu_index + 1; - } - } - return max_cpu_index; -} - -CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); -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) -{ - QEMU_LOCK_GUARD(&qemu_cpu_list_lock); - if (cpu->cpu_index == UNASSIGNED_CPU_INDEX) { - cpu->cpu_index = cpu_get_free_index(); - assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); - } else { - assert(!cpu_index_auto_assigned); - } - QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); - cpu_list_generation_id++; -} - -void cpu_list_remove(CPUState *cpu) -{ - QEMU_LOCK_GUARD(&qemu_cpu_list_lock); - if (!QTAILQ_IN_USE(cpu, node)) { - /* there is nothing to undo since cpu_exec_init() hasn't been called */ - return; - } - - QTAILQ_REMOVE_RCU(&cpus, cpu, node); - cpu->cpu_index = UNASSIGNED_CPU_INDEX; - cpu_list_generation_id++; -} - -CPUState *qemu_get_cpu(int index) -{ - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (cpu->cpu_index == index) { - return cpu; - } - } - - return NULL; -} - -/* current CPU in the current thread. It is only valid inside cpu_exec() */ -__thread CPUState *current_cpu; - -struct qemu_work_item { - QSIMPLEQ_ENTRY(qemu_work_item) node; - run_on_cpu_func func; - run_on_cpu_data data; - bool free, exclusive, done; -}; - -static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) -{ - qemu_mutex_lock(&cpu->work_mutex); - QSIMPLEQ_INSERT_TAIL(&cpu->work_list, wi, node); - wi->done = false; - qemu_mutex_unlock(&cpu->work_mutex); - - qemu_cpu_kick(cpu); -} - -void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, - QemuMutex *mutex) -{ - struct qemu_work_item wi; - - if (qemu_cpu_is_self(cpu)) { - func(cpu, data); - return; - } - - wi.func = func; - wi.data = data; - wi.done = false; - wi.free = false; - wi.exclusive = false; - - queue_work_on_cpu(cpu, &wi); - while (!qatomic_mb_read(&wi.done)) { - CPUState *self_cpu = current_cpu; - - qemu_cond_wait(&qemu_work_cond, mutex); - current_cpu = self_cpu; - } -} - -void async_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) -{ - struct qemu_work_item *wi; - - wi = g_new0(struct qemu_work_item, 1); - wi->func = func; - wi->data = data; - wi->free = true; - - queue_work_on_cpu(cpu, wi); -} - -/* Wait for pending exclusive operations to complete. The CPU list lock - must be held. */ -static inline void exclusive_idle(void) -{ - while (pending_cpus) { - qemu_cond_wait(&exclusive_resume, &qemu_cpu_list_lock); - } -} - -/* Start an exclusive operation. - Must only be called from outside cpu_exec. */ -void start_exclusive(void) -{ - CPUState *other_cpu; - int running_cpus; - - qemu_mutex_lock(&qemu_cpu_list_lock); - exclusive_idle(); - - /* Make all other cpus stop executing. */ - qatomic_set(&pending_cpus, 1); - - /* Write pending_cpus before reading other_cpu->running. */ - smp_mb(); - running_cpus = 0; - CPU_FOREACH(other_cpu) { - if (qatomic_read(&other_cpu->running)) { - other_cpu->has_waiter = true; - running_cpus++; - qemu_cpu_kick(other_cpu); - } - } - - qatomic_set(&pending_cpus, running_cpus + 1); - while (pending_cpus > 1) { - qemu_cond_wait(&exclusive_cond, &qemu_cpu_list_lock); - } - - /* Can release mutex, no one will enter another exclusive - * section until end_exclusive resets pending_cpus to 0. - */ - qemu_mutex_unlock(&qemu_cpu_list_lock); - - current_cpu->in_exclusive_context = true; -} - -/* Finish an exclusive operation. */ -void end_exclusive(void) -{ - current_cpu->in_exclusive_context = false; - - qemu_mutex_lock(&qemu_cpu_list_lock); - qatomic_set(&pending_cpus, 0); - qemu_cond_broadcast(&exclusive_resume); - qemu_mutex_unlock(&qemu_cpu_list_lock); -} - -/* Wait for exclusive ops to finish, and begin cpu execution. */ -void cpu_exec_start(CPUState *cpu) -{ - qatomic_set(&cpu->running, true); - - /* Write cpu->running before reading pending_cpus. */ - smp_mb(); - - /* 1. start_exclusive saw cpu->running == true and pending_cpus >= 1. - * After taking the lock we'll see cpu->has_waiter == true and run---not - * for long because start_exclusive kicked us. cpu_exec_end will - * decrement pending_cpus and signal the waiter. - * - * 2. start_exclusive saw cpu->running == false but pending_cpus >= 1. - * This includes the case when an exclusive item is running now. - * Then we'll see cpu->has_waiter == false and wait for the item to - * complete. - * - * 3. pending_cpus == 0. Then start_exclusive is definitely going to - * see cpu->running == true, and it will kick the CPU. - */ - if (unlikely(qatomic_read(&pending_cpus))) { - QEMU_LOCK_GUARD(&qemu_cpu_list_lock); - if (!cpu->has_waiter) { - /* Not counted in pending_cpus, let the exclusive item - * run. Since we have the lock, just set cpu->running to true - * while holding it; no need to check pending_cpus again. - */ - qatomic_set(&cpu->running, false); - exclusive_idle(); - /* Now pending_cpus is zero. */ - qatomic_set(&cpu->running, true); - } else { - /* Counted in pending_cpus, go ahead and release the - * waiter at cpu_exec_end. - */ - } - } -} - -/* Mark cpu as not executing, and release pending exclusive ops. */ -void cpu_exec_end(CPUState *cpu) -{ - qatomic_set(&cpu->running, false); - - /* Write cpu->running before reading pending_cpus. */ - smp_mb(); - - /* 1. start_exclusive saw cpu->running == true. Then it will increment - * pending_cpus and wait for exclusive_cond. After taking the lock - * we'll see cpu->has_waiter == true. - * - * 2. start_exclusive saw cpu->running == false but here pending_cpus >= 1. - * This includes the case when an exclusive item started after setting - * cpu->running to false and before we read pending_cpus. Then we'll see - * cpu->has_waiter == false and not touch pending_cpus. The next call to - * cpu_exec_start will run exclusive_idle if still necessary, thus waiting - * for the item to complete. - * - * 3. pending_cpus == 0. Then start_exclusive is definitely going to - * see cpu->running == false, and it can ignore this CPU until the - * next cpu_exec_start. - */ - if (unlikely(qatomic_read(&pending_cpus))) { - QEMU_LOCK_GUARD(&qemu_cpu_list_lock); - if (cpu->has_waiter) { - cpu->has_waiter = false; - qatomic_set(&pending_cpus, pending_cpus - 1); - if (pending_cpus == 1) { - qemu_cond_signal(&exclusive_cond); - } - } - } -} - -void async_safe_run_on_cpu(CPUState *cpu, run_on_cpu_func func, - run_on_cpu_data data) -{ - struct qemu_work_item *wi; - - wi = g_new0(struct qemu_work_item, 1); - wi->func = func; - wi->data = data; - wi->free = true; - wi->exclusive = true; - - queue_work_on_cpu(cpu, wi); -} - -void process_queued_cpu_work(CPUState *cpu) -{ - struct qemu_work_item *wi; - - qemu_mutex_lock(&cpu->work_mutex); - if (QSIMPLEQ_EMPTY(&cpu->work_list)) { - qemu_mutex_unlock(&cpu->work_mutex); - return; - } - while (!QSIMPLEQ_EMPTY(&cpu->work_list)) { - wi = QSIMPLEQ_FIRST(&cpu->work_list); - QSIMPLEQ_REMOVE_HEAD(&cpu->work_list, node); - qemu_mutex_unlock(&cpu->work_mutex); - if (wi->exclusive) { - /* Running work items outside the BQL avoids the following deadlock: - * 1) start_exclusive() is called with the BQL taken while another - * CPU is running; 2) cpu_exec in the other CPU tries to takes the - * BQL, so it goes to sleep; start_exclusive() is sleeping too, so - * neither CPU can proceed. - */ - qemu_mutex_unlock_iothread(); - start_exclusive(); - wi->func(cpu, wi->data); - end_exclusive(); - qemu_mutex_lock_iothread(); - } else { - wi->func(cpu, wi->data); - } - qemu_mutex_lock(&cpu->work_mutex); - if (wi->free) { - g_free(wi); - } else { - qatomic_mb_set(&wi->done, true); - } - } - qemu_mutex_unlock(&cpu->work_mutex); - qemu_cond_broadcast(&qemu_work_cond); -} 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..246d0679d4 100644 --- a/crypto/afalg.c +++ b/crypto/afalg.c @@ -59,21 +59,21 @@ 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; } return sbind; } -QCryptoAFAlg * +QCryptoAFAlgo * qcrypto_afalg_comm_alloc(const char *type, const char *name, Error **errp) { - QCryptoAFAlg *afalg; + QCryptoAFAlgo *afalg; - afalg = g_new0(QCryptoAFAlg, 1); - /* initilize crypto API socket */ + afalg = g_new0(QCryptoAFAlgo, 1); + /* initialize crypto API socket */ afalg->opfd = -1; afalg->tfmfd = qcrypto_afalg_socket_bind(type, name, errp); if (afalg->tfmfd == -1) { @@ -93,7 +93,7 @@ error: return NULL; } -void qcrypto_afalg_comm_free(QCryptoAFAlg *afalg) +void qcrypto_afalg_comm_free(QCryptoAFAlgo *afalg) { if (!afalg) { return; @@ -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/afalgpriv.h b/crypto/afalgpriv.h index 5a2393f1b7..3fdcc0f831 100644 --- a/crypto/afalgpriv.h +++ b/crypto/afalgpriv.h @@ -30,9 +30,9 @@ #define ALG_OPTYPE_LEN 4 #define ALG_MSGIV_LEN(len) (sizeof(struct af_alg_iv) + (len)) -typedef struct QCryptoAFAlg QCryptoAFAlg; +typedef struct QCryptoAFAlgo QCryptoAFAlgo; -struct QCryptoAFAlg { +struct QCryptoAFAlgo { QCryptoCipher base; int tfmfd; @@ -46,22 +46,22 @@ struct QCryptoAFAlg { * @type: the type of crypto operation * @name: the name of crypto operation * - * Allocate a QCryptoAFAlg object and bind itself to + * Allocate a QCryptoAFAlgo object and bind itself to * a AF_ALG socket. * * Returns: - * a new QCryptoAFAlg object, or NULL in error. + * a new QCryptoAFAlgo object, or NULL in error. */ -QCryptoAFAlg * +QCryptoAFAlgo * qcrypto_afalg_comm_alloc(const char *type, const char *name, Error **errp); /** * afalg_comm_free: - * @afalg: the QCryptoAFAlg object + * @afalg: the QCryptoAFAlgo object * * Free the @afalg. */ -void qcrypto_afalg_comm_free(QCryptoAFAlg *afalg); +void qcrypto_afalg_comm_free(QCryptoAFAlgo *afalg); #endif diff --git a/crypto/afsplit.c b/crypto/afsplit.c index b1a5a20899..b2e383aa67 100644 --- a/crypto/afsplit.c +++ b/crypto/afsplit.c @@ -40,7 +40,7 @@ static void qcrypto_afsplit_xor(size_t blocklen, } -static int qcrypto_afsplit_hash(QCryptoHashAlgorithm hash, +static int qcrypto_afsplit_hash(QCryptoHashAlgo hash, size_t blocklen, uint8_t *block, Error **errp) @@ -85,7 +85,7 @@ static int qcrypto_afsplit_hash(QCryptoHashAlgorithm hash, } -int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, +int qcrypto_afsplit_encode(QCryptoHashAlgo hash, size_t blocklen, uint32_t stripes, const uint8_t *in, @@ -117,7 +117,7 @@ int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, } -int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash, +int qcrypto_afsplit_decode(QCryptoHashAlgo hash, size_t blocklen, uint32_t stripes, const uint8_t *in, diff --git a/crypto/akcipher-gcrypt.c.inc b/crypto/akcipher-gcrypt.c.inc index abb1fb272e..5a880f6638 100644 --- a/crypto/akcipher-gcrypt.c.inc +++ b/crypto/akcipher-gcrypt.c.inc @@ -32,8 +32,8 @@ typedef struct QCryptoGcryptRSA { QCryptoAkCipher akcipher; gcry_sexp_t key; - QCryptoRSAPaddingAlgorithm padding_alg; - QCryptoHashAlgorithm hash_alg; + QCryptoRSAPaddingAlgo padding_alg; + QCryptoHashAlgo hash_alg; } QCryptoGcryptRSA; static void qcrypto_gcrypt_rsa_free(QCryptoAkCipher *akcipher) @@ -59,7 +59,7 @@ QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts, Error **errp) { switch (opts->alg) { - case QCRYPTO_AKCIPHER_ALG_RSA: + case QCRYPTO_AK_CIPHER_ALGO_RSA: return (QCryptoAkCipher *)qcrypto_gcrypt_rsa_new( &opts->u.rsa, type, key, keylen, errp); @@ -85,7 +85,7 @@ static int qcrypto_gcrypt_parse_rsa_private_key( const uint8_t *key, size_t keylen, Error **errp) { g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse( - QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen, errp); + QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, key, keylen, errp); gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL, u = NULL; bool compute_mul_inv = false; int ret = -1; @@ -178,7 +178,7 @@ static int qcrypto_gcrypt_parse_rsa_public_key(QCryptoGcryptRSA *rsa, { g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse( - QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, key, keylen, errp); + QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, key, keylen, errp); gcry_mpi_t n = NULL, e = NULL; int ret = -1; gcry_error_t err; @@ -241,7 +241,7 @@ static int qcrypto_gcrypt_rsa_encrypt(QCryptoAkCipher *akcipher, err = gcry_sexp_build(&data_sexp, NULL, "(data (flags %s) (value %b))", - QCryptoRSAPaddingAlgorithm_str(rsa->padding_alg), + QCryptoRSAPaddingAlgo_str(rsa->padding_alg), in_len, in); if (gcry_err_code(err) != 0) { error_setg(errp, "Failed to build plaintext: %s/%s", @@ -263,7 +263,7 @@ static int qcrypto_gcrypt_rsa_encrypt(QCryptoAkCipher *akcipher, goto cleanup; } - if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) { + if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALGO_RAW) { cipher_mpi = gcry_sexp_nth_mpi(cipher_sexp_item, 1, GCRYMPI_FMT_USG); if (!cipher_mpi) { error_setg(errp, "Invalid ciphertext result"); @@ -332,7 +332,7 @@ static int qcrypto_gcrypt_rsa_decrypt(QCryptoAkCipher *akcipher, err = gcry_sexp_build(&cipher_sexp, NULL, "(enc-val (flags %s) (rsa (a %b) ))", - QCryptoRSAPaddingAlgorithm_str(rsa->padding_alg), + QCryptoRSAPaddingAlgo_str(rsa->padding_alg), in_len, in); if (gcry_err_code(err) != 0) { error_setg(errp, "Failed to build ciphertext: %s/%s", @@ -348,7 +348,7 @@ static int qcrypto_gcrypt_rsa_decrypt(QCryptoAkCipher *akcipher, } /* S-expression of plaintext: (value plaintext) */ - if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) { + if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALGO_RAW) { data_mpi = gcry_sexp_nth_mpi(data_sexp, 1, GCRYMPI_FMT_USG); if (!data_mpi) { error_setg(errp, "Invalid plaintext result"); @@ -410,14 +410,14 @@ static int qcrypto_gcrypt_rsa_sign(QCryptoAkCipher *akcipher, return ret; } - if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALG_PKCS1) { + if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALGO_PKCS1) { error_setg(errp, "Invalid padding %u", rsa->padding_alg); return ret; } err = gcry_sexp_build(&dgst_sexp, NULL, "(data (flags pkcs1) (hash %s %b))", - QCryptoHashAlgorithm_str(rsa->hash_alg), + QCryptoHashAlgo_str(rsa->hash_alg), in_len, in); if (gcry_err_code(err) != 0) { error_setg(errp, "Failed to build dgst: %s/%s", @@ -482,7 +482,7 @@ static int qcrypto_gcrypt_rsa_verify(QCryptoAkCipher *akcipher, return ret; } - if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALG_PKCS1) { + if (rsa->padding_alg != QCRYPTO_RSA_PADDING_ALGO_PKCS1) { error_setg(errp, "Invalid padding %u", rsa->padding_alg); return ret; } @@ -497,7 +497,7 @@ static int qcrypto_gcrypt_rsa_verify(QCryptoAkCipher *akcipher, err = gcry_sexp_build(&dgst_sexp, NULL, "(data (flags pkcs1) (hash %s %b))", - QCryptoHashAlgorithm_str(rsa->hash_alg), + QCryptoHashAlgo_str(rsa->hash_alg), in2_len, in2); if (gcry_err_code(err) != 0) { error_setg(errp, "Failed to build dgst: %s/%s", @@ -540,13 +540,13 @@ static QCryptoGcryptRSA *qcrypto_gcrypt_rsa_new( rsa->akcipher.driver = &gcrypt_rsa; switch (type) { - case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE: if (qcrypto_gcrypt_parse_rsa_private_key(rsa, key, keylen, errp) != 0) { goto error; } break; - case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC: if (qcrypto_gcrypt_parse_rsa_public_key(rsa, key, keylen, errp) != 0) { goto error; } @@ -568,17 +568,17 @@ error: bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts) { switch (opts->alg) { - case QCRYPTO_AKCIPHER_ALG_RSA: + case QCRYPTO_AK_CIPHER_ALGO_RSA: switch (opts->u.rsa.padding_alg) { - case QCRYPTO_RSA_PADDING_ALG_RAW: + case QCRYPTO_RSA_PADDING_ALGO_RAW: return true; - case QCRYPTO_RSA_PADDING_ALG_PKCS1: + case QCRYPTO_RSA_PADDING_ALGO_PKCS1: switch (opts->u.rsa.hash_alg) { - case QCRYPTO_HASH_ALG_MD5: - case QCRYPTO_HASH_ALG_SHA1: - case QCRYPTO_HASH_ALG_SHA256: - case QCRYPTO_HASH_ALG_SHA512: + case QCRYPTO_HASH_ALGO_MD5: + case QCRYPTO_HASH_ALGO_SHA1: + case QCRYPTO_HASH_ALGO_SHA256: + case QCRYPTO_HASH_ALGO_SHA512: return true; default: diff --git a/crypto/akcipher-nettle.c.inc b/crypto/akcipher-nettle.c.inc index 02699e6e6d..1720f84362 100644 --- a/crypto/akcipher-nettle.c.inc +++ b/crypto/akcipher-nettle.c.inc @@ -33,8 +33,8 @@ typedef struct QCryptoNettleRSA { QCryptoAkCipher akcipher; struct rsa_public_key pub; struct rsa_private_key priv; - QCryptoRSAPaddingAlgorithm padding_alg; - QCryptoHashAlgorithm hash_alg; + QCryptoRSAPaddingAlgo padding_alg; + QCryptoHashAlgo hash_alg; } QCryptoNettleRSA; static void qcrypto_nettle_rsa_free(QCryptoAkCipher *akcipher) @@ -61,7 +61,7 @@ QCryptoAkCipher *qcrypto_akcipher_new(const QCryptoAkCipherOptions *opts, Error **errp) { switch (opts->alg) { - case QCRYPTO_AKCIPHER_ALG_RSA: + case QCRYPTO_AK_CIPHER_ALGO_RSA: return qcrypto_nettle_rsa_new(&opts->u.rsa, type, key, keylen, errp); default: @@ -87,7 +87,7 @@ static int qcrypt_nettle_parse_rsa_private_key(QCryptoNettleRSA *rsa, Error **errp) { g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse( - QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, key, keylen, errp); + QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, key, keylen, errp); if (!rsa_key) { return -1; @@ -137,7 +137,7 @@ static int qcrypt_nettle_parse_rsa_public_key(QCryptoNettleRSA *rsa, Error **errp) { g_autoptr(QCryptoAkCipherRSAKey) rsa_key = qcrypto_akcipher_rsakey_parse( - QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, key, keylen, errp); + QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, key, keylen, errp); if (!rsa_key) { return -1; @@ -184,11 +184,11 @@ static int qcrypto_nettle_rsa_encrypt(QCryptoAkCipher *akcipher, /* Nettle do not support RSA encryption without any padding */ switch (rsa->padding_alg) { - case QCRYPTO_RSA_PADDING_ALG_RAW: + case QCRYPTO_RSA_PADDING_ALGO_RAW: error_setg(errp, "RSA with raw padding is not supported"); break; - case QCRYPTO_RSA_PADDING_ALG_PKCS1: + case QCRYPTO_RSA_PADDING_ALGO_PKCS1: mpz_init(c); if (rsa_encrypt(&rsa->pub, NULL, wrap_nettle_random_func, data_len, (uint8_t *)data, c) != 1) { @@ -223,11 +223,11 @@ static int qcrypto_nettle_rsa_decrypt(QCryptoAkCipher *akcipher, } switch (rsa->padding_alg) { - case QCRYPTO_RSA_PADDING_ALG_RAW: + case QCRYPTO_RSA_PADDING_ALGO_RAW: error_setg(errp, "RSA with raw padding is not supported"); break; - case QCRYPTO_RSA_PADDING_ALG_PKCS1: + case QCRYPTO_RSA_PADDING_ALGO_PKCS1: nettle_mpz_init_set_str_256_u(c, enc_len, enc); if (!rsa_decrypt(&rsa->priv, &data_len, (uint8_t *)data, c)) { error_setg(errp, "Failed to decrypt"); @@ -257,7 +257,7 @@ static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher, * The RSA algorithm cannot be used for signature/verification * without padding. */ - if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) { + if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALGO_RAW) { error_setg(errp, "Try to make signature without padding"); return ret; } @@ -276,19 +276,19 @@ static int qcrypto_nettle_rsa_sign(QCryptoAkCipher *akcipher, mpz_init(s); switch (rsa->hash_alg) { - case QCRYPTO_HASH_ALG_MD5: + case QCRYPTO_HASH_ALGO_MD5: rv = rsa_md5_sign_digest(&rsa->priv, data, s); break; - case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALGO_SHA1: rv = rsa_sha1_sign_digest(&rsa->priv, data, s); break; - case QCRYPTO_HASH_ALG_SHA256: + case QCRYPTO_HASH_ALGO_SHA256: rv = rsa_sha256_sign_digest(&rsa->priv, data, s); break; - case QCRYPTO_HASH_ALG_SHA512: + case QCRYPTO_HASH_ALGO_SHA512: rv = rsa_sha512_sign_digest(&rsa->priv, data, s); break; @@ -324,7 +324,7 @@ static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher, * The RSA algorithm cannot be used for signature/verification * without padding. */ - if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALG_RAW) { + if (rsa->padding_alg == QCRYPTO_RSA_PADDING_ALGO_RAW) { error_setg(errp, "Try to verify signature without padding"); return ret; } @@ -341,19 +341,19 @@ static int qcrypto_nettle_rsa_verify(QCryptoAkCipher *akcipher, nettle_mpz_init_set_str_256_u(s, sig_len, sig); switch (rsa->hash_alg) { - case QCRYPTO_HASH_ALG_MD5: + case QCRYPTO_HASH_ALGO_MD5: rv = rsa_md5_verify_digest(&rsa->pub, data, s); break; - case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALGO_SHA1: rv = rsa_sha1_verify_digest(&rsa->pub, data, s); break; - case QCRYPTO_HASH_ALG_SHA256: + case QCRYPTO_HASH_ALGO_SHA256: rv = rsa_sha256_verify_digest(&rsa->pub, data, s); break; - case QCRYPTO_HASH_ALG_SHA512: + case QCRYPTO_HASH_ALGO_SHA512: rv = rsa_sha512_verify_digest(&rsa->pub, data, s); break; @@ -397,13 +397,13 @@ static QCryptoAkCipher *qcrypto_nettle_rsa_new( rsa_private_key_init(&rsa->priv); switch (type) { - case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE: if (qcrypt_nettle_parse_rsa_private_key(rsa, key, keylen, errp) != 0) { goto error; } break; - case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC: if (qcrypt_nettle_parse_rsa_public_key(rsa, key, keylen, errp) != 0) { goto error; } @@ -425,21 +425,21 @@ error: bool qcrypto_akcipher_supports(QCryptoAkCipherOptions *opts) { switch (opts->alg) { - case QCRYPTO_AKCIPHER_ALG_RSA: + case QCRYPTO_AK_CIPHER_ALGO_RSA: switch (opts->u.rsa.padding_alg) { - case QCRYPTO_RSA_PADDING_ALG_PKCS1: + case QCRYPTO_RSA_PADDING_ALGO_PKCS1: switch (opts->u.rsa.hash_alg) { - case QCRYPTO_HASH_ALG_MD5: - case QCRYPTO_HASH_ALG_SHA1: - case QCRYPTO_HASH_ALG_SHA256: - case QCRYPTO_HASH_ALG_SHA512: + case QCRYPTO_HASH_ALGO_MD5: + case QCRYPTO_HASH_ALGO_SHA1: + case QCRYPTO_HASH_ALGO_SHA256: + case QCRYPTO_HASH_ALGO_SHA512: return true; default: return false; } - case QCRYPTO_RSA_PADDING_ALG_RAW: + case QCRYPTO_RSA_PADDING_ALGO_RAW: default: return false; } diff --git a/crypto/akcipher.c b/crypto/akcipher.c index e4bbc6e5f1..0a0576b792 100644 --- a/crypto/akcipher.c +++ b/crypto/akcipher.c @@ -115,7 +115,7 @@ int qcrypto_akcipher_export_p8info(const QCryptoAkCipherOptions *opts, Error **errp) { switch (opts->alg) { - case QCRYPTO_AKCIPHER_ALG_RSA: + case QCRYPTO_AK_CIPHER_ALGO_RSA: qcrypto_akcipher_rsakey_export_p8info(key, keylen, dst, dst_len); return 0; diff --git a/crypto/akcipherpriv.h b/crypto/akcipherpriv.h index 739f639bcf..3b33e54f08 100644 --- a/crypto/akcipherpriv.h +++ b/crypto/akcipherpriv.h @@ -27,7 +27,7 @@ typedef struct QCryptoAkCipherDriver QCryptoAkCipherDriver; struct QCryptoAkCipher { - QCryptoAkCipherAlgorithm alg; + QCryptoAkCipherAlgo alg; QCryptoAkCipherKeyType type; int max_plaintext_len; int max_ciphertext_len; diff --git a/crypto/block-luks-priv.h b/crypto/block-luks-priv.h index 90a20d432b..8fc967afcb 100644 --- a/crypto/block-luks-priv.h +++ b/crypto/block-luks-priv.h @@ -18,7 +18,6 @@ * */ -#include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/bswap.h" @@ -31,7 +30,6 @@ #include "crypto/random.h" #include "qemu/uuid.h" -#include "qemu/coroutine.h" #include "qemu/bitmap.h" /* diff --git a/crypto/block-luks.c b/crypto/block-luks.c index df2b4105d6..0926ad28f0 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -32,8 +32,8 @@ #include "crypto/random.h" #include "qemu/uuid.h" -#include "qemu/coroutine.h" #include "qemu/bitmap.h" +#include "qemu/range.h" /* * Reference for the LUKS format implemented here is @@ -68,40 +68,51 @@ struct QCryptoBlockLUKSCipherNameMap { static const QCryptoBlockLUKSCipherSizeMap qcrypto_block_luks_cipher_size_map_aes[] = { - { 16, QCRYPTO_CIPHER_ALG_AES_128 }, - { 24, QCRYPTO_CIPHER_ALG_AES_192 }, - { 32, QCRYPTO_CIPHER_ALG_AES_256 }, + { 16, QCRYPTO_CIPHER_ALGO_AES_128 }, + { 24, QCRYPTO_CIPHER_ALGO_AES_192 }, + { 32, QCRYPTO_CIPHER_ALGO_AES_256 }, { 0, 0 }, }; static const QCryptoBlockLUKSCipherSizeMap qcrypto_block_luks_cipher_size_map_cast5[] = { - { 16, QCRYPTO_CIPHER_ALG_CAST5_128 }, + { 16, QCRYPTO_CIPHER_ALGO_CAST5_128 }, { 0, 0 }, }; static const QCryptoBlockLUKSCipherSizeMap qcrypto_block_luks_cipher_size_map_serpent[] = { - { 16, QCRYPTO_CIPHER_ALG_SERPENT_128 }, - { 24, QCRYPTO_CIPHER_ALG_SERPENT_192 }, - { 32, QCRYPTO_CIPHER_ALG_SERPENT_256 }, + { 16, QCRYPTO_CIPHER_ALGO_SERPENT_128 }, + { 24, QCRYPTO_CIPHER_ALGO_SERPENT_192 }, + { 32, QCRYPTO_CIPHER_ALGO_SERPENT_256 }, { 0, 0 }, }; static const QCryptoBlockLUKSCipherSizeMap qcrypto_block_luks_cipher_size_map_twofish[] = { - { 16, QCRYPTO_CIPHER_ALG_TWOFISH_128 }, - { 24, QCRYPTO_CIPHER_ALG_TWOFISH_192 }, - { 32, QCRYPTO_CIPHER_ALG_TWOFISH_256 }, + { 16, QCRYPTO_CIPHER_ALGO_TWOFISH_128 }, + { 24, QCRYPTO_CIPHER_ALGO_TWOFISH_192 }, + { 32, QCRYPTO_CIPHER_ALGO_TWOFISH_256 }, { 0, 0 }, }; +#ifdef CONFIG_CRYPTO_SM4 +static const QCryptoBlockLUKSCipherSizeMap +qcrypto_block_luks_cipher_size_map_sm4[] = { + { 16, QCRYPTO_CIPHER_ALGO_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 }, +#ifdef CONFIG_CRYPTO_SM4 + { "sm4", qcrypto_block_luks_cipher_size_map_sm4}, +#endif }; QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48); @@ -112,25 +123,25 @@ struct QCryptoBlockLUKS { QCryptoBlockLUKSHeader header; /* Main encryption algorithm used for encryption*/ - QCryptoCipherAlgorithm cipher_alg; + QCryptoCipherAlgo cipher_alg; /* Mode of encryption for the selected encryption algorithm */ QCryptoCipherMode cipher_mode; /* Initialization vector generation algorithm */ - QCryptoIVGenAlgorithm ivgen_alg; + QCryptoIVGenAlgo ivgen_alg; /* Hash algorithm used for IV generation*/ - QCryptoHashAlgorithm ivgen_hash_alg; + QCryptoHashAlgo ivgen_hash_alg; /* * Encryption algorithm used for IV generation. * Usually the same as main encryption algorithm */ - QCryptoCipherAlgorithm ivgen_cipher_alg; + QCryptoCipherAlgo ivgen_cipher_alg; /* Hash algorithm used in pbkdf2 function */ - QCryptoHashAlgorithm hash_alg; + QCryptoHashAlgo hash_alg; /* Name of the secret that was used to open the image */ char *secret; @@ -168,7 +179,7 @@ static int qcrypto_block_luks_cipher_name_lookup(const char *name, } static const char * -qcrypto_block_luks_cipher_alg_lookup(QCryptoCipherAlgorithm alg, +qcrypto_block_luks_cipher_alg_lookup(QCryptoCipherAlgo alg, Error **errp) { const QCryptoBlockLUKSCipherNameMap *map = @@ -184,7 +195,7 @@ qcrypto_block_luks_cipher_alg_lookup(QCryptoCipherAlgorithm alg, } error_setg(errp, "Algorithm '%s' not supported", - QCryptoCipherAlgorithm_str(alg)); + QCryptoCipherAlgo_str(alg)); return NULL; } @@ -212,13 +223,13 @@ static int qcrypto_block_luks_name_lookup(const char *name, #define qcrypto_block_luks_hash_name_lookup(name, errp) \ qcrypto_block_luks_name_lookup(name, \ - &QCryptoHashAlgorithm_lookup, \ + &QCryptoHashAlgo_lookup, \ "Hash algorithm", \ errp) #define qcrypto_block_luks_ivgen_name_lookup(name, errp) \ qcrypto_block_luks_name_lookup(name, \ - &QCryptoIVGenAlgorithm_lookup, \ + &QCryptoIVGenAlgo_lookup, \ "IV generator", \ errp) @@ -245,15 +256,15 @@ 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 * size, not AES 128 with truncated digest as might be imagined */ -static QCryptoCipherAlgorithm -qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher, - QCryptoHashAlgorithm hash, +static QCryptoCipherAlgo +qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgo cipher, + QCryptoHashAlgo hash, Error **errp) { size_t digestlen = qcrypto_hash_digest_len(hash); @@ -263,54 +274,54 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher, } switch (cipher) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_AES_128)) { - return QCRYPTO_CIPHER_ALG_AES_128; + QCRYPTO_CIPHER_ALGO_AES_128)) { + return QCRYPTO_CIPHER_ALGO_AES_128; } else if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_AES_192)) { - return QCRYPTO_CIPHER_ALG_AES_192; + QCRYPTO_CIPHER_ALGO_AES_192)) { + return QCRYPTO_CIPHER_ALGO_AES_192; } else if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_AES_256)) { - return QCRYPTO_CIPHER_ALG_AES_256; + QCRYPTO_CIPHER_ALGO_AES_256)) { + return QCRYPTO_CIPHER_ALGO_AES_256; } else { error_setg(errp, "No AES cipher with key size %zu available", digestlen); return 0; } break; - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALGO_SERPENT_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_192: + case QCRYPTO_CIPHER_ALGO_SERPENT_256: if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_SERPENT_128)) { - return QCRYPTO_CIPHER_ALG_SERPENT_128; + QCRYPTO_CIPHER_ALGO_SERPENT_128)) { + return QCRYPTO_CIPHER_ALGO_SERPENT_128; } else if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_SERPENT_192)) { - return QCRYPTO_CIPHER_ALG_SERPENT_192; + QCRYPTO_CIPHER_ALGO_SERPENT_192)) { + return QCRYPTO_CIPHER_ALGO_SERPENT_192; } else if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_SERPENT_256)) { - return QCRYPTO_CIPHER_ALG_SERPENT_256; + QCRYPTO_CIPHER_ALGO_SERPENT_256)) { + return QCRYPTO_CIPHER_ALGO_SERPENT_256; } else { error_setg(errp, "No Serpent cipher with key size %zu available", digestlen); return 0; } break; - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_192: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: + case QCRYPTO_CIPHER_ALGO_TWOFISH_128: + case QCRYPTO_CIPHER_ALGO_TWOFISH_192: + case QCRYPTO_CIPHER_ALGO_TWOFISH_256: if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_TWOFISH_128)) { - return QCRYPTO_CIPHER_ALG_TWOFISH_128; + QCRYPTO_CIPHER_ALGO_TWOFISH_128)) { + return QCRYPTO_CIPHER_ALGO_TWOFISH_128; } else if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_TWOFISH_192)) { - return QCRYPTO_CIPHER_ALG_TWOFISH_192; + QCRYPTO_CIPHER_ALGO_TWOFISH_192)) { + return QCRYPTO_CIPHER_ALGO_TWOFISH_192; } else if (digestlen == qcrypto_cipher_get_key_len( - QCRYPTO_CIPHER_ALG_TWOFISH_256)) { - return QCRYPTO_CIPHER_ALG_TWOFISH_256; + QCRYPTO_CIPHER_ALGO_TWOFISH_256)) { + return QCRYPTO_CIPHER_ALGO_TWOFISH_256; } else { error_setg(errp, "No Twofish cipher with key size %zu available", digestlen); @@ -319,7 +330,7 @@ qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher, break; default: error_setg(errp, "Cipher %s not supported with essiv", - QCryptoCipherAlgorithm_str(cipher)); + QCryptoCipherAlgo_str(cipher)); return 0; } } @@ -394,7 +405,7 @@ qcrypto_block_luks_from_disk_endian(QCryptoBlockLUKSHeader *hdr) } /* - * 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, @@ -424,7 +435,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 @@ -458,12 +469,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) { @@ -495,7 +509,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) return -1; } - if (luks->header.payload_offset_sector < + 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"); @@ -544,7 +558,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) return -1; } - if (start1 + len1 > luks->header.payload_offset_sector) { + if (!detached && start1 + len1 > luks->header.payload_offset_sector) { error_setg(errp, "Keyslot %zu is overlapping with the encrypted payload", i); @@ -559,7 +573,7 @@ qcrypto_block_luks_check_header(const QCryptoBlockLUKS *luks, Error **errp) header_sectors, slot2->stripes); - if (start1 + len1 > start2 && start2 + len2 > start1) { + if (ranges_overlap(start1, len1, start2, len2)) { error_setg(errp, "Keyslots %zu and %zu are overlapping in the header", i, j); @@ -646,7 +660,7 @@ qcrypto_block_luks_parse_header(QCryptoBlockLUKS *luks, Error **errp) return -1; } - if (luks->ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { + if (luks->ivgen_alg == QCRYPTO_IV_GEN_ALGO_ESSIV) { if (!ivhash_name) { error_setg(errp, "Missing IV generator hash specification"); return -1; @@ -707,14 +721,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 @@ -1176,7 +1190,6 @@ qcrypto_block_luks_open(QCryptoBlock *block, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp) { QCryptoBlockLUKS *luks = NULL; @@ -1204,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; } @@ -1249,7 +1262,6 @@ qcrypto_block_luks_open(QCryptoBlock *block, luks->cipher_mode, masterkey, luks->header.master_key_len, - n_threads, errp) < 0) { goto fail; } @@ -1258,6 +1270,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; @@ -1302,26 +1315,27 @@ 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) { luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS; } if (!luks_opts.has_cipher_alg) { - luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256; + luks_opts.cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256; } if (!luks_opts.has_cipher_mode) { luks_opts.cipher_mode = QCRYPTO_CIPHER_MODE_XTS; } if (!luks_opts.has_ivgen_alg) { - luks_opts.ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64; + luks_opts.ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64; } if (!luks_opts.has_hash_alg) { - luks_opts.hash_alg = QCRYPTO_HASH_ALG_SHA256; + luks_opts.hash_alg = QCRYPTO_HASH_ALGO_SHA256; } - if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { + if (luks_opts.ivgen_alg == QCRYPTO_IV_GEN_ALGO_ESSIV) { if (!luks_opts.has_ivgen_hash_alg) { - luks_opts.ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256; + luks_opts.ivgen_hash_alg = QCRYPTO_HASH_ALGO_SHA256; luks_opts.has_ivgen_hash_alg = true; } } @@ -1370,15 +1384,15 @@ qcrypto_block_luks_create(QCryptoBlock *block, } cipher_mode = QCryptoCipherMode_str(luks_opts.cipher_mode); - ivgen_alg = QCryptoIVGenAlgorithm_str(luks_opts.ivgen_alg); + ivgen_alg = QCryptoIVGenAlgo_str(luks_opts.ivgen_alg); if (luks_opts.has_ivgen_hash_alg) { - ivgen_hash_alg = QCryptoHashAlgorithm_str(luks_opts.ivgen_hash_alg); + ivgen_hash_alg = QCryptoHashAlgo_str(luks_opts.ivgen_hash_alg); cipher_mode_spec = g_strdup_printf("%s-%s:%s", cipher_mode, ivgen_alg, ivgen_hash_alg); } else { cipher_mode_spec = g_strdup_printf("%s-%s", cipher_mode, ivgen_alg); } - hash_alg = QCryptoHashAlgorithm_str(luks_opts.hash_alg); + hash_alg = QCryptoHashAlgo_str(luks_opts.hash_alg); if (strlen(cipher_alg) >= QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN) { @@ -1397,7 +1411,7 @@ qcrypto_block_luks_create(QCryptoBlock *block, goto error; } - if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { + if (luks_opts.ivgen_alg == QCRYPTO_IV_GEN_ALGO_ESSIV) { luks->ivgen_cipher_alg = qcrypto_block_luks_essiv_cipher(luks_opts.cipher_alg, luks_opts.ivgen_hash_alg, @@ -1441,7 +1455,7 @@ qcrypto_block_luks_create(QCryptoBlock *block, /* Setup the block device payload encryption objects */ if (qcrypto_block_init_cipher(block, luks_opts.cipher_alg, luks_opts.cipher_mode, masterkey, - luks->header.master_key_len, 1, errp) < 0) { + luks->header.master_key_len, errp) < 0) { goto error; } @@ -1530,19 +1544,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; @@ -1597,13 +1624,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; @@ -1677,7 +1704,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; @@ -1687,14 +1714,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) { @@ -1719,7 +1746,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, @@ -1761,7 +1788,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; @@ -1834,11 +1861,11 @@ qcrypto_block_luks_amend_options(QCryptoBlock *block, QCryptoBlockAmendOptionsLUKS *opts_luks = &options->u.luks; switch (opts_luks->state) { - case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE: + case QCRYPTO_BLOCK_LUKS_KEYSLOT_STATE_ACTIVE: return qcrypto_block_luks_amend_add_keyslot(block, readfunc, writefunc, opaque, opts_luks, force, errp); - case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE: + case QCRYPTO_BLOCK_LUKS_KEYSLOT_STATE_INACTIVE: return qcrypto_block_luks_amend_erase_keyslots(block, readfunc, writefunc, opaque, opts_luks, force, errp); @@ -1859,7 +1886,7 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block, info->u.luks.cipher_alg = luks->cipher_alg; info->u.luks.cipher_mode = luks->cipher_mode; info->u.luks.ivgen_alg = luks->ivgen_alg; - if (info->u.luks.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { + if (info->u.luks.ivgen_alg == QCRYPTO_IV_GEN_ALGO_ESSIV) { info->u.luks.has_ivgen_hash_alg = true; info->u.luks.ivgen_hash_alg = luks->ivgen_hash_alg; } @@ -1868,6 +1895,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-qcow.c b/crypto/block-qcow.c index 4d7cf36a8f..054078b895 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -44,7 +44,6 @@ qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED, static int qcrypto_block_qcow_init(QCryptoBlock *block, const char *keysecret, - size_t n_threads, Error **errp) { char *password; @@ -63,19 +62,19 @@ qcrypto_block_qcow_init(QCryptoBlock *block, memcpy(keybuf, password, MIN(len, sizeof(keybuf))); g_free(password); - block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, + block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALGO_AES_128, QCRYPTO_CIPHER_MODE_CBC); - block->ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_PLAIN64, + block->ivgen = qcrypto_ivgen_new(QCRYPTO_IV_GEN_ALGO_PLAIN64, 0, 0, NULL, 0, errp); if (!block->ivgen) { ret = -ENOTSUP; goto fail; } - ret = qcrypto_block_init_cipher(block, QCRYPTO_CIPHER_ALG_AES_128, + ret = qcrypto_block_init_cipher(block, QCRYPTO_CIPHER_ALGO_AES_128, QCRYPTO_CIPHER_MODE_CBC, keybuf, G_N_ELEMENTS(keybuf), - n_threads, errp); + errp); if (ret < 0) { ret = -ENOTSUP; goto fail; @@ -100,7 +99,6 @@ qcrypto_block_qcow_open(QCryptoBlock *block, QCryptoBlockReadFunc readfunc G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED, unsigned int flags, - size_t n_threads, Error **errp) { if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) { @@ -115,7 +113,7 @@ qcrypto_block_qcow_open(QCryptoBlock *block, return -1; } return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, - n_threads, errp); + errp); } } @@ -135,7 +133,7 @@ qcrypto_block_qcow_create(QCryptoBlock *block, return -1; } /* QCow2 has no special header, since everything is hardwired */ - return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, 1, errp); + return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp); } diff --git a/crypto/block.c b/crypto/block.c index 7bb4b74a37..96c83e60b9 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -20,13 +20,14 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/lockable.h" #include "blockpriv.h" #include "block-qcow.h" #include "block-luks.h" static const QCryptoBlockDriver *qcrypto_block_drivers[] = { - [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow, - [Q_CRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks, + [QCRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow, + [QCRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks, }; @@ -52,11 +53,12 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp) { QCryptoBlock *block = g_new0(QCryptoBlock, 1); + qemu_mutex_init(&block->mutex); + block->format = options->format; if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) || @@ -70,14 +72,12 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, block->driver = qcrypto_block_drivers[options->format]; if (block->driver->open(block, options, optprefix, - readfunc, opaque, flags, n_threads, errp) < 0) + readfunc, opaque, flags, errp) < 0) { g_free(block); return NULL; } - qemu_mutex_init(&block->mutex); - return block; } @@ -87,10 +87,13 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, QCryptoBlockInitFunc initfunc, QCryptoBlockWriteFunc writefunc, void *opaque, + unsigned int flags, Error **errp) { QCryptoBlock *block = g_new0(QCryptoBlock, 1); + qemu_mutex_init(&block->mutex); + block->format = options->format; if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) || @@ -102,6 +105,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) { @@ -109,8 +113,6 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, return NULL; } - qemu_mutex_init(&block->mutex); - return block; } @@ -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; } @@ -225,62 +227,74 @@ QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block) * This function is used only in test with one thread (it's safe to skip * pop/push interface), so it's enough to assert it here: */ - assert(block->n_ciphers <= 1); - return block->ciphers ? block->ciphers[0] : NULL; + assert(block->max_free_ciphers <= 1); + return block->free_ciphers ? block->free_ciphers[0] : NULL; } -static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block) +static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block, + Error **errp) { - QCryptoCipher *cipher; + /* Usually there is a free cipher available */ + WITH_QEMU_LOCK_GUARD(&block->mutex) { + if (block->n_free_ciphers > 0) { + block->n_free_ciphers--; + return block->free_ciphers[block->n_free_ciphers]; + } + } - qemu_mutex_lock(&block->mutex); - - assert(block->n_free_ciphers > 0); - block->n_free_ciphers--; - cipher = block->ciphers[block->n_free_ciphers]; - - qemu_mutex_unlock(&block->mutex); - - return cipher; + /* Otherwise allocate a new cipher */ + return qcrypto_cipher_new(block->alg, block->mode, block->key, + block->nkey, errp); } static void qcrypto_block_push_cipher(QCryptoBlock *block, QCryptoCipher *cipher) { - qemu_mutex_lock(&block->mutex); + QEMU_LOCK_GUARD(&block->mutex); - assert(block->n_free_ciphers < block->n_ciphers); - block->ciphers[block->n_free_ciphers] = cipher; + if (block->n_free_ciphers == block->max_free_ciphers) { + block->max_free_ciphers++; + block->free_ciphers = g_renew(QCryptoCipher *, + block->free_ciphers, + block->max_free_ciphers); + } + + block->free_ciphers[block->n_free_ciphers] = cipher; block->n_free_ciphers++; - - qemu_mutex_unlock(&block->mutex); } int qcrypto_block_init_cipher(QCryptoBlock *block, - QCryptoCipherAlgorithm alg, + QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, - size_t n_threads, Error **errp) + Error **errp) { - size_t i; + QCryptoCipher *cipher; - assert(!block->ciphers && !block->n_ciphers && !block->n_free_ciphers); + assert(!block->free_ciphers && !block->max_free_ciphers && + !block->n_free_ciphers); - block->ciphers = g_new0(QCryptoCipher *, n_threads); + /* Stash away cipher parameters for qcrypto_block_pop_cipher() */ + block->alg = alg; + block->mode = mode; + block->key = g_memdup2(key, nkey); + block->nkey = nkey; - for (i = 0; i < n_threads; i++) { - block->ciphers[i] = qcrypto_cipher_new(alg, mode, key, nkey, errp); - if (!block->ciphers[i]) { - qcrypto_block_free_cipher(block); - return -1; - } - block->n_ciphers++; - block->n_free_ciphers++; + /* + * Create a new cipher to validate the parameters now. This reduces the + * chance of cipher creation failing at I/O time. + */ + cipher = qcrypto_block_pop_cipher(block, errp); + if (!cipher) { + g_free(block->key); + block->key = NULL; + return -1; } + qcrypto_block_push_cipher(block, cipher); return 0; } @@ -289,19 +303,23 @@ void qcrypto_block_free_cipher(QCryptoBlock *block) { size_t i; - if (!block->ciphers) { + g_free(block->key); + block->key = NULL; + + if (!block->free_ciphers) { return; } - assert(block->n_ciphers == block->n_free_ciphers); + /* All popped ciphers were eventually pushed back */ + assert(block->n_free_ciphers == block->max_free_ciphers); - for (i = 0; i < block->n_ciphers; i++) { - qcrypto_cipher_free(block->ciphers[i]); + for (i = 0; i < block->max_free_ciphers; i++) { + qcrypto_cipher_free(block->free_ciphers[i]); } - g_free(block->ciphers); - block->ciphers = NULL; - block->n_ciphers = block->n_free_ciphers = 0; + g_free(block->free_ciphers); + block->free_ciphers = NULL; + block->max_free_ciphers = block->n_free_ciphers = 0; } QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) @@ -309,12 +327,12 @@ QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) /* ivgen should be accessed under mutex. However, this function is used only * in test with one thread, so it's enough to assert it here: */ - assert(block->n_ciphers <= 1); + assert(block->max_free_ciphers <= 1); return block->ivgen; } -QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block) +QCryptoHashAlgo qcrypto_block_get_kdf_hash(QCryptoBlock *block) { return block->kdfhash; } @@ -444,7 +462,10 @@ int qcrypto_block_decrypt_helper(QCryptoBlock *block, Error **errp) { int ret; - QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block, errp); + if (!cipher) { + return -1; + } ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, &block->mutex, sectorsize, offset, buf, @@ -463,7 +484,10 @@ int qcrypto_block_encrypt_helper(QCryptoBlock *block, Error **errp) { int ret; - QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block, errp); + if (!cipher) { + return -1; + } ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, &block->mutex, sectorsize, offset, buf, diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h index 3c7ccea504..edf0b3a3d9 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -32,16 +32,24 @@ struct QCryptoBlock { const QCryptoBlockDriver *driver; void *opaque; - QCryptoCipher **ciphers; - size_t n_ciphers; + /* Cipher parameters */ + QCryptoCipherAlgo alg; + QCryptoCipherMode mode; + uint8_t *key; + size_t nkey; + + QCryptoCipher **free_ciphers; + size_t max_free_ciphers; size_t n_free_ciphers; QCryptoIVGen *ivgen; QemuMutex mutex; - QCryptoHashAlgorithm kdfhash; + QCryptoHashAlgo kdfhash; 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 { @@ -51,7 +59,6 @@ struct QCryptoBlockDriver { QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp); int (*create)(QCryptoBlock *block, @@ -125,10 +132,10 @@ int qcrypto_block_encrypt_helper(QCryptoBlock *block, Error **errp); int qcrypto_block_init_cipher(QCryptoBlock *block, - QCryptoCipherAlgorithm alg, + QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, - size_t n_threads, Error **errp); + Error **errp); void qcrypto_block_free_cipher(QCryptoBlock *block); diff --git a/crypto/cipher-afalg.c b/crypto/cipher-afalg.c index 3df8fc54c0..4980d419c4 100644 --- a/crypto/cipher-afalg.c +++ b/crypto/cipher-afalg.c @@ -18,7 +18,7 @@ static char * -qcrypto_afalg_cipher_format_name(QCryptoCipherAlgorithm alg, +qcrypto_afalg_cipher_format_name(QCryptoCipherAlgo alg, QCryptoCipherMode mode, Error **errp) { @@ -27,22 +27,22 @@ qcrypto_afalg_cipher_format_name(QCryptoCipherAlgorithm alg, const char *mode_name; switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: alg_name = "aes"; break; - case QCRYPTO_CIPHER_ALG_CAST5_128: + case QCRYPTO_CIPHER_ALGO_CAST5_128: alg_name = "cast5"; break; - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALGO_SERPENT_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_192: + case QCRYPTO_CIPHER_ALGO_SERPENT_256: alg_name = "serpent"; break; - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_192: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: + case QCRYPTO_CIPHER_ALGO_TWOFISH_128: + case QCRYPTO_CIPHER_ALGO_TWOFISH_192: + case QCRYPTO_CIPHER_ALGO_TWOFISH_256: alg_name = "twofish"; break; @@ -60,12 +60,12 @@ qcrypto_afalg_cipher_format_name(QCryptoCipherAlgorithm alg, static const struct QCryptoCipherDriver qcrypto_cipher_afalg_driver; QCryptoCipher * -qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg, +qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, Error **errp) { - QCryptoAFAlg *afalg; + QCryptoAFAlgo *afalg; size_t expect_niv; char *name; @@ -119,7 +119,7 @@ qcrypto_afalg_cipher_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp) { - QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); struct af_alg_iv *alg_iv; size_t expect_niv; @@ -143,7 +143,7 @@ qcrypto_afalg_cipher_setiv(QCryptoCipher *cipher, } static int -qcrypto_afalg_cipher_op(QCryptoAFAlg *afalg, +qcrypto_afalg_cipher_op(QCryptoAFAlgo *afalg, const void *in, void *out, size_t len, bool do_encrypt, Error **errp) @@ -202,7 +202,7 @@ qcrypto_afalg_cipher_encrypt(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { - QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); return qcrypto_afalg_cipher_op(afalg, in, out, len, true, errp); } @@ -212,14 +212,14 @@ qcrypto_afalg_cipher_decrypt(QCryptoCipher *cipher, const void *in, void *out, size_t len, Error **errp) { - QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); return qcrypto_afalg_cipher_op(afalg, in, out, len, false, errp); } static void qcrypto_afalg_comm_ctx_free(QCryptoCipher *cipher) { - QCryptoAFAlg *afalg = container_of(cipher, QCryptoAFAlg, base); + QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); qcrypto_afalg_comm_free(afalg); } diff --git a/crypto/cipher-builtin.c.inc b/crypto/cipher-builtin.c.inc index b409089095..da5fcbd9a3 100644 --- a/crypto/cipher-builtin.c.inc +++ b/crypto/cipher-builtin.c.inc @@ -221,13 +221,13 @@ static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = { .cipher_free = qcrypto_cipher_ctx_free, }; -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, +bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: @@ -241,7 +241,7 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, } } -static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, @@ -252,9 +252,9 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, } switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: { QCryptoCipherBuiltinAES *ctx; const QCryptoCipherDriver *drv; @@ -292,7 +292,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, default: error_setg(errp, "Unsupported cipher algorithm %s", - QCryptoCipherAlgorithm_str(alg)); + QCryptoCipherAlgo_str(alg)); return NULL; } diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc index a6a0117717..12eb9ddb5a 100644 --- a/crypto/cipher-gcrypt.c.inc +++ b/crypto/cipher-gcrypt.c.inc @@ -20,26 +20,84 @@ #include -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, +static int qcrypto_cipher_alg_to_gcry_alg(QCryptoCipherAlgo alg) +{ + switch (alg) { + case QCRYPTO_CIPHER_ALGO_DES: + return GCRY_CIPHER_DES; + case QCRYPTO_CIPHER_ALGO_3DES: + return GCRY_CIPHER_3DES; + case QCRYPTO_CIPHER_ALGO_AES_128: + return GCRY_CIPHER_AES128; + case QCRYPTO_CIPHER_ALGO_AES_192: + return GCRY_CIPHER_AES192; + case QCRYPTO_CIPHER_ALGO_AES_256: + return GCRY_CIPHER_AES256; + case QCRYPTO_CIPHER_ALGO_CAST5_128: + return GCRY_CIPHER_CAST5; + case QCRYPTO_CIPHER_ALGO_SERPENT_128: + return GCRY_CIPHER_SERPENT128; + case QCRYPTO_CIPHER_ALGO_SERPENT_192: + return GCRY_CIPHER_SERPENT192; + case QCRYPTO_CIPHER_ALGO_SERPENT_256: + return GCRY_CIPHER_SERPENT256; + case QCRYPTO_CIPHER_ALGO_TWOFISH_128: + return GCRY_CIPHER_TWOFISH128; + case QCRYPTO_CIPHER_ALGO_TWOFISH_256: + return GCRY_CIPHER_TWOFISH; +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALGO_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(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { switch (alg) { - case QCRYPTO_CIPHER_ALG_DES: - case QCRYPTO_CIPHER_ALG_3DES: - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - case QCRYPTO_CIPHER_ALG_CAST5_128: - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: + case QCRYPTO_CIPHER_ALGO_DES: + case QCRYPTO_CIPHER_ALGO_3DES: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: + case QCRYPTO_CIPHER_ALGO_CAST5_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_192: + case QCRYPTO_CIPHER_ALGO_SERPENT_256: + case QCRYPTO_CIPHER_ALGO_TWOFISH_128: + case QCRYPTO_CIPHER_ALGO_TWOFISH_256: +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALGO_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: @@ -170,7 +228,7 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = { .cipher_free = qcrypto_gcrypt_ctx_free, }; -static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, @@ -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)); + QCryptoCipherAlgo_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..b9450d48b0 100644 --- a/crypto/cipher-gnutls.c.inc +++ b/crypto/cipher-gnutls.c.inc @@ -27,7 +27,7 @@ #define QEMU_GNUTLS_XTS #endif -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, +bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { @@ -35,11 +35,11 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - case QCRYPTO_CIPHER_ALG_DES: - case QCRYPTO_CIPHER_ALG_3DES: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: + case QCRYPTO_CIPHER_ALGO_DES: + case QCRYPTO_CIPHER_ALGO_3DES: return true; default: return false; @@ -47,8 +47,8 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, #ifdef QEMU_GNUTLS_XTS case QCRYPTO_CIPHER_MODE_XTS: switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_256: return true; default: return false; @@ -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)); @@ -229,7 +229,7 @@ static struct QCryptoCipherDriver gnutls_driver = { .cipher_free = qcrypto_gnutls_cipher_free, }; -static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, @@ -244,10 +244,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, #ifdef QEMU_GNUTLS_XTS case QCRYPTO_CIPHER_MODE_XTS: switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_128: galg = GNUTLS_CIPHER_AES_128_XTS; break; - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_256: galg = GNUTLS_CIPHER_AES_256_XTS; break; default: @@ -259,19 +259,19 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: switch (alg) { - case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_128: galg = GNUTLS_CIPHER_AES_128_CBC; break; - case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_192: galg = GNUTLS_CIPHER_AES_192_CBC; break; - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_256: galg = GNUTLS_CIPHER_AES_256_CBC; break; - case QCRYPTO_CIPHER_ALG_DES: + case QCRYPTO_CIPHER_ALGO_DES: galg = GNUTLS_CIPHER_DES_CBC; break; - case QCRYPTO_CIPHER_ALG_3DES: + case QCRYPTO_CIPHER_ALGO_3DES: galg = GNUTLS_CIPHER_3DES_CBC; break; default: @@ -284,7 +284,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, if (galg == GNUTLS_CIPHER_UNKNOWN) { error_setg(errp, "Unsupported cipher algorithm %s with %s mode", - QCryptoCipherAlgorithm_str(alg), + QCryptoCipherAlgo_str(alg), QCryptoCipherMode_str(mode)); return NULL; } @@ -310,8 +310,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, } } - if (alg == QCRYPTO_CIPHER_ALG_DES || - alg == QCRYPTO_CIPHER_ALG_3DES) + if (alg == QCRYPTO_CIPHER_ALGO_DES || + alg == QCRYPTO_CIPHER_ALGO_3DES) ctx->blocksize = 8; else ctx->blocksize = 16; diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 24cc61f87b..ae91363772 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,23 +429,50 @@ 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; -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, +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(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { switch (alg) { - case QCRYPTO_CIPHER_ALG_DES: - case QCRYPTO_CIPHER_ALG_3DES: - case QCRYPTO_CIPHER_ALG_AES_128: - case QCRYPTO_CIPHER_ALG_AES_192: - case QCRYPTO_CIPHER_ALG_AES_256: - case QCRYPTO_CIPHER_ALG_CAST5_128: - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_192: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: + case QCRYPTO_CIPHER_ALGO_DES: + case QCRYPTO_CIPHER_ALGO_3DES: + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: + case QCRYPTO_CIPHER_ALGO_CAST5_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_192: + case QCRYPTO_CIPHER_ALGO_SERPENT_256: + case QCRYPTO_CIPHER_ALGO_TWOFISH_128: + case QCRYPTO_CIPHER_ALGO_TWOFISH_192: + case QCRYPTO_CIPHER_ALGO_TWOFISH_256: +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALGO_SM4: +#endif break; default: return false; @@ -459,7 +489,7 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, } } -static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, @@ -480,7 +510,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, } switch (alg) { - case QCRYPTO_CIPHER_ALG_DES: + case QCRYPTO_CIPHER_ALGO_DES: { QCryptoNettleDES *ctx; const QCryptoCipherDriver *drv; @@ -495,8 +525,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_CTR: drv = &qcrypto_nettle_des_driver_ctr; break; - default: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleDES, 1); @@ -506,7 +538,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_3DES: + case QCRYPTO_CIPHER_ALGO_3DES: { QCryptoNettleDES3 *ctx; const QCryptoCipherDriver *drv; @@ -521,8 +553,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_CTR: drv = &qcrypto_nettle_des3_driver_ctr; break; - default: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleDES3, 1); @@ -531,7 +565,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_128: { QCryptoNettleAES128 *ctx = g_new0(QCryptoNettleAES128, 1); @@ -560,7 +594,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_192: { QCryptoNettleAES192 *ctx = g_new0(QCryptoNettleAES192, 1); @@ -589,7 +623,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_AES_256: + case QCRYPTO_CIPHER_ALGO_AES_256: { QCryptoNettleAES256 *ctx = g_new0(QCryptoNettleAES256, 1); @@ -618,7 +652,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_CAST5_128: + case QCRYPTO_CIPHER_ALGO_CAST5_128: { QCryptoNettleCAST128 *ctx; const QCryptoCipherDriver *drv; @@ -633,8 +667,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, case QCRYPTO_CIPHER_MODE_CTR: drv = &qcrypto_nettle_cast128_driver_ctr; break; - default: + case QCRYPTO_CIPHER_MODE_XTS: goto bad_cipher_mode; + default: + g_assert_not_reached(); } ctx = g_new0(QCryptoNettleCAST128, 1); @@ -644,9 +680,9 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_SERPENT_128: - case QCRYPTO_CIPHER_ALG_SERPENT_192: - case QCRYPTO_CIPHER_ALG_SERPENT_256: + case QCRYPTO_CIPHER_ALGO_SERPENT_128: + case QCRYPTO_CIPHER_ALGO_SERPENT_192: + case QCRYPTO_CIPHER_ALGO_SERPENT_256: { QCryptoNettleSerpent *ctx = g_new0(QCryptoNettleSerpent, 1); @@ -673,9 +709,9 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } - case QCRYPTO_CIPHER_ALG_TWOFISH_128: - case QCRYPTO_CIPHER_ALG_TWOFISH_192: - case QCRYPTO_CIPHER_ALG_TWOFISH_256: + case QCRYPTO_CIPHER_ALGO_TWOFISH_128: + case QCRYPTO_CIPHER_ALGO_TWOFISH_192: + case QCRYPTO_CIPHER_ALGO_TWOFISH_256: { QCryptoNettleTwofish *ctx = g_new0(QCryptoNettleTwofish, 1); @@ -701,10 +737,36 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg, return &ctx->base; } +#ifdef CONFIG_CRYPTO_SM4 + case QCRYPTO_CIPHER_ALGO_SM4: + { + QCryptoNettleSm4 *ctx; + const QCryptoCipherDriver *drv; + + switch (mode) { + case QCRYPTO_CIPHER_MODE_ECB: + drv = &qcrypto_nettle_sm4_driver_ecb; + break; + case QCRYPTO_CIPHER_MODE_CBC: + case QCRYPTO_CIPHER_MODE_CTR: + case QCRYPTO_CIPHER_MODE_XTS: + goto bad_cipher_mode; + default: + g_assert_not_reached(); + } + + ctx = g_new0(QCryptoNettleSm4, 1); + ctx->base.driver = drv; + 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", - QCryptoCipherAlgorithm_str(alg)); + QCryptoCipherAlgo_str(alg)); return NULL; } diff --git a/crypto/cipher.c b/crypto/cipher.c index 74b09a5b26..c14a8b8a11 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -25,34 +25,40 @@ #include "cipherpriv.h" -static const size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { - [QCRYPTO_CIPHER_ALG_AES_128] = 16, - [QCRYPTO_CIPHER_ALG_AES_192] = 24, - [QCRYPTO_CIPHER_ALG_AES_256] = 32, - [QCRYPTO_CIPHER_ALG_DES] = 8, - [QCRYPTO_CIPHER_ALG_3DES] = 24, - [QCRYPTO_CIPHER_ALG_CAST5_128] = 16, - [QCRYPTO_CIPHER_ALG_SERPENT_128] = 16, - [QCRYPTO_CIPHER_ALG_SERPENT_192] = 24, - [QCRYPTO_CIPHER_ALG_SERPENT_256] = 32, - [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16, - [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 24, - [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 32, +static const size_t alg_key_len[QCRYPTO_CIPHER_ALGO__MAX] = { + [QCRYPTO_CIPHER_ALGO_AES_128] = 16, + [QCRYPTO_CIPHER_ALGO_AES_192] = 24, + [QCRYPTO_CIPHER_ALGO_AES_256] = 32, + [QCRYPTO_CIPHER_ALGO_DES] = 8, + [QCRYPTO_CIPHER_ALGO_3DES] = 24, + [QCRYPTO_CIPHER_ALGO_CAST5_128] = 16, + [QCRYPTO_CIPHER_ALGO_SERPENT_128] = 16, + [QCRYPTO_CIPHER_ALGO_SERPENT_192] = 24, + [QCRYPTO_CIPHER_ALGO_SERPENT_256] = 32, + [QCRYPTO_CIPHER_ALGO_TWOFISH_128] = 16, + [QCRYPTO_CIPHER_ALGO_TWOFISH_192] = 24, + [QCRYPTO_CIPHER_ALGO_TWOFISH_256] = 32, +#ifdef CONFIG_CRYPTO_SM4 + [QCRYPTO_CIPHER_ALGO_SM4] = 16, +#endif }; -static const size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { - [QCRYPTO_CIPHER_ALG_AES_128] = 16, - [QCRYPTO_CIPHER_ALG_AES_192] = 16, - [QCRYPTO_CIPHER_ALG_AES_256] = 16, - [QCRYPTO_CIPHER_ALG_DES] = 8, - [QCRYPTO_CIPHER_ALG_3DES] = 8, - [QCRYPTO_CIPHER_ALG_CAST5_128] = 8, - [QCRYPTO_CIPHER_ALG_SERPENT_128] = 16, - [QCRYPTO_CIPHER_ALG_SERPENT_192] = 16, - [QCRYPTO_CIPHER_ALG_SERPENT_256] = 16, - [QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16, - [QCRYPTO_CIPHER_ALG_TWOFISH_192] = 16, - [QCRYPTO_CIPHER_ALG_TWOFISH_256] = 16, +static const size_t alg_block_len[QCRYPTO_CIPHER_ALGO__MAX] = { + [QCRYPTO_CIPHER_ALGO_AES_128] = 16, + [QCRYPTO_CIPHER_ALGO_AES_192] = 16, + [QCRYPTO_CIPHER_ALGO_AES_256] = 16, + [QCRYPTO_CIPHER_ALGO_DES] = 8, + [QCRYPTO_CIPHER_ALGO_3DES] = 8, + [QCRYPTO_CIPHER_ALGO_CAST5_128] = 8, + [QCRYPTO_CIPHER_ALGO_SERPENT_128] = 16, + [QCRYPTO_CIPHER_ALGO_SERPENT_192] = 16, + [QCRYPTO_CIPHER_ALGO_SERPENT_256] = 16, + [QCRYPTO_CIPHER_ALGO_TWOFISH_128] = 16, + [QCRYPTO_CIPHER_ALGO_TWOFISH_192] = 16, + [QCRYPTO_CIPHER_ALGO_TWOFISH_256] = 16, +#ifdef CONFIG_CRYPTO_SM4 + [QCRYPTO_CIPHER_ALGO_SM4] = 16, +#endif }; static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { @@ -63,21 +69,21 @@ static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { }; -size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg) +size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgo alg) { assert(alg < G_N_ELEMENTS(alg_key_len)); return alg_block_len[alg]; } -size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg) +size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgo alg) { assert(alg < G_N_ELEMENTS(alg_key_len)); return alg_key_len[alg]; } -size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, +size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { if (alg >= G_N_ELEMENTS(alg_block_len)) { @@ -95,20 +101,20 @@ size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, static bool -qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, +qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg, QCryptoCipherMode mode, size_t nkey, Error **errp) { - if ((unsigned)alg >= QCRYPTO_CIPHER_ALG__MAX) { + if ((unsigned)alg >= QCRYPTO_CIPHER_ALGO__MAX) { error_setg(errp, "Cipher algorithm %d out of range", alg); return false; } if (mode == QCRYPTO_CIPHER_MODE_XTS) { - if (alg == QCRYPTO_CIPHER_ALG_DES || - alg == QCRYPTO_CIPHER_ALG_3DES) { + if (alg == QCRYPTO_CIPHER_ALGO_DES || + alg == QCRYPTO_CIPHER_ALGO_3DES) { error_setg(errp, "XTS mode not compatible with DES/3DES"); return false; } @@ -142,7 +148,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg, #include "cipher-builtin.c.inc" #endif -QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, +QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, Error **errp) diff --git a/crypto/cipherpriv.h b/crypto/cipherpriv.h index 396527857d..64737ce961 100644 --- a/crypto/cipherpriv.h +++ b/crypto/cipherpriv.h @@ -42,7 +42,7 @@ struct QCryptoCipherDriver { #include "afalgpriv.h" extern QCryptoCipher * -qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg, +qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, Error **errp); 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 dab3fe4f24..81367524c3 100644 --- a/crypto/der.c +++ b/crypto/der.c @@ -76,7 +76,7 @@ enum QCryptoDERTagEnc { /** * qcrypto_der_encode_length: * @src_len: the length of source data - * @dst: distination to save the encoded 'length', if dst is NULL, only compute + * @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. * @@ -408,19 +408,6 @@ void qcrypto_der_encode_octet_str(QCryptoEncodeContext *ctx, 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; diff --git a/crypto/der.h b/crypto/der.h index 0e895bbeec..bcfa4a2495 100644 --- a/crypto/der.h +++ b/crypto/der.h @@ -242,28 +242,6 @@ void qcrypto_der_encode_null(QCryptoEncodeContext *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 SEQUNCE 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 SEQUNCE as OCTET STRING. - */ -void qcrypto_der_encode_octet_str_end(QCryptoEncodeContext *ctx); - /** * qcrypto_der_encode_ctx_buffer_len: * @ctx: the encode context. @@ -275,7 +253,7 @@ size_t qcrypto_der_encode_ctx_buffer_len(QCryptoEncodeContext *ctx); /** * qcrypto_der_encode_ctx_flush_and_free: * @ctx: the encode context. - * @dst: the distination to save the encoded data, the length of dst should + * @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. diff --git a/crypto/hash-afalg.c b/crypto/hash-afalg.c index 3ebea39292..8c0ce5b520 100644 --- a/crypto/hash-afalg.c +++ b/crypto/hash-afalg.c @@ -1,6 +1,7 @@ /* * QEMU Crypto af_alg-backend hash/hmac support * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. * * Authors: @@ -20,7 +21,7 @@ #include "hmacpriv.h" static char * -qcrypto_afalg_hash_format_name(QCryptoHashAlgorithm alg, +qcrypto_afalg_hash_format_name(QCryptoHashAlgo alg, bool is_hmac, Error **errp) { @@ -28,25 +29,25 @@ qcrypto_afalg_hash_format_name(QCryptoHashAlgorithm alg, const char *alg_name; switch (alg) { - case QCRYPTO_HASH_ALG_MD5: + case QCRYPTO_HASH_ALGO_MD5: alg_name = "md5"; break; - case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALGO_SHA1: alg_name = "sha1"; break; - case QCRYPTO_HASH_ALG_SHA224: + case QCRYPTO_HASH_ALGO_SHA224: alg_name = "sha224"; break; - case QCRYPTO_HASH_ALG_SHA256: + case QCRYPTO_HASH_ALGO_SHA256: alg_name = "sha256"; break; - case QCRYPTO_HASH_ALG_SHA384: + case QCRYPTO_HASH_ALGO_SHA384: alg_name = "sha384"; break; - case QCRYPTO_HASH_ALG_SHA512: + case QCRYPTO_HASH_ALGO_SHA512: alg_name = "sha512"; break; - case QCRYPTO_HASH_ALG_RIPEMD160: + case QCRYPTO_HASH_ALGO_RIPEMD160: alg_name = "rmd160"; break; @@ -64,12 +65,12 @@ qcrypto_afalg_hash_format_name(QCryptoHashAlgorithm alg, return name; } -static QCryptoAFAlg * -qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgorithm alg, +static QCryptoAFAlgo * +qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, bool is_hmac, Error **errp) { - QCryptoAFAlg *afalg; + QCryptoAFAlgo *afalg; char *name; name = qcrypto_afalg_hash_format_name(alg, is_hmac, errp); @@ -98,91 +99,162 @@ qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgorithm alg, return afalg; } -static QCryptoAFAlg * -qcrypto_afalg_hash_ctx_new(QCryptoHashAlgorithm alg, +static QCryptoAFAlgo * +qcrypto_afalg_hash_ctx_new(QCryptoHashAlgo alg, Error **errp) { return qcrypto_afalg_hash_hmac_ctx_new(alg, NULL, 0, false, errp); } -QCryptoAFAlg * -qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgorithm alg, +QCryptoAFAlgo * +qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp) { return qcrypto_afalg_hash_hmac_ctx_new(alg, key, nkey, true, errp); } +static +QCryptoHash *qcrypto_afalg_hash_new(QCryptoHashAlgo alg, Error **errp) +{ + /* Check if hash algorithm is supported */ + char *alg_name = qcrypto_afalg_hash_format_name(alg, false, NULL); + QCryptoHash *hash; + + if (alg_name == NULL) { + error_setg(errp, "Unknown hash algorithm %d", alg); + return NULL; + } + + g_free(alg_name); + + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = qcrypto_afalg_hash_ctx_new(alg, errp); + if (!hash->opaque) { + free(hash); + return NULL; + } + + return hash; +} + +static +void qcrypto_afalg_hash_free(QCryptoHash *hash) +{ + QCryptoAFAlgo *ctx = hash->opaque; + + if (ctx) { + qcrypto_afalg_comm_free(ctx); + } + + g_free(hash); +} + +/** + * Send data to the kernel's crypto core. + * + * The more_data parameter is used to notify the crypto engine + * that this is an "update" operation, and that more data will + * be provided to calculate the final hash. + */ +static +int qcrypto_afalg_send_to_kernel(QCryptoAFAlgo *afalg, + const struct iovec *iov, + size_t niov, + bool more_data, + Error **errp) +{ + int ret = 0; + int flags = (more_data ? MSG_MORE : 0); + + /* send data to kernel's crypto core */ + ret = iov_send_recv_with_flags(afalg->opfd, flags, iov, niov, + 0, iov_size(iov, niov), true); + if (ret < 0) { + error_setg_errno(errp, errno, "Send data to afalg-core failed"); + ret = -1; + } else { + /* No error, so return 0 */ + ret = 0; + } + + return ret; +} + +static +int qcrypto_afalg_recv_from_kernel(QCryptoAFAlgo *afalg, + QCryptoHashAlgo alg, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + struct iovec outv; + int ret; + const int expected_len = qcrypto_hash_digest_len(alg); + + if (*result_len == 0) { + *result_len = expected_len; + *result = g_new0(uint8_t, *result_len); + } else if (*result_len != expected_len) { + error_setg(errp, + "Result buffer size %zu is not match hash %d", + *result_len, expected_len); + return -1; + } + + /* hash && get result */ + outv.iov_base = *result; + outv.iov_len = *result_len; + ret = iov_send_recv(afalg->opfd, &outv, 1, + 0, iov_size(&outv, 1), false); + if (ret < 0) { + error_setg_errno(errp, errno, "Recv result from afalg-core failed"); + return -1; + } + + return 0; +} + +static +int qcrypto_afalg_hash_update(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + return qcrypto_afalg_send_to_kernel((QCryptoAFAlgo *) hash->opaque, + iov, niov, true, errp); +} + +static +int qcrypto_afalg_hash_finalize(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + return qcrypto_afalg_recv_from_kernel((QCryptoAFAlgo *) hash->opaque, + hash->alg, result, result_len, errp); +} + static int -qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlg *hmac, - QCryptoHashAlgorithm alg, +qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlgo *hmac, + QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, uint8_t **result, size_t *resultlen, Error **errp) { - QCryptoAFAlg *afalg; - struct iovec outv; int ret = 0; - bool is_hmac = (hmac != NULL) ? true : false; - const int expect_len = qcrypto_hash_digest_len(alg); - if (*resultlen == 0) { - *resultlen = expect_len; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != expect_len) { - error_setg(errp, - "Result buffer size %zu is not match hash %d", - *resultlen, expect_len); - return -1; + ret = qcrypto_afalg_send_to_kernel(hmac, iov, niov, false, errp); + if (ret == 0) { + ret = qcrypto_afalg_recv_from_kernel(hmac, alg, result, + resultlen, errp); } - if (is_hmac) { - afalg = hmac; - } else { - afalg = qcrypto_afalg_hash_ctx_new(alg, errp); - if (!afalg) { - return -1; - } - } - - /* send data to kernel's crypto core */ - ret = iov_send_recv(afalg->opfd, iov, niov, - 0, iov_size(iov, niov), true); - if (ret < 0) { - error_setg_errno(errp, errno, "Send data to afalg-core failed"); - goto out; - } - - /* hash && get result */ - outv.iov_base = *result; - outv.iov_len = *resultlen; - ret = iov_send_recv(afalg->opfd, &outv, 1, - 0, iov_size(&outv, 1), false); - if (ret < 0) { - error_setg_errno(errp, errno, "Recv result from afalg-core failed"); - } else { - ret = 0; - } - -out: - if (!is_hmac) { - qcrypto_afalg_comm_free(afalg); - } return ret; } -static int -qcrypto_afalg_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, uint8_t **result, - size_t *resultlen, - Error **errp) -{ - return qcrypto_afalg_hash_hmac_bytesv(NULL, alg, iov, niov, result, - resultlen, errp); -} - static int qcrypto_afalg_hmac_bytesv(QCryptoHmac *hmac, const struct iovec *iov, @@ -197,14 +269,17 @@ qcrypto_afalg_hmac_bytesv(QCryptoHmac *hmac, static void qcrypto_afalg_hmac_ctx_free(QCryptoHmac *hmac) { - QCryptoAFAlg *afalg; + QCryptoAFAlgo *afalg; afalg = hmac->opaque; qcrypto_afalg_comm_free(afalg); } QCryptoHashDriver qcrypto_hash_afalg_driver = { - .hash_bytesv = qcrypto_afalg_hash_bytesv, + .hash_new = qcrypto_afalg_hash_new, + .hash_free = qcrypto_afalg_hash_free, + .hash_update = qcrypto_afalg_hash_update, + .hash_finalize = qcrypto_afalg_hash_finalize }; QCryptoHmacDriver qcrypto_hmac_afalg_driver = { diff --git a/crypto/hash-gcrypt.c b/crypto/hash-gcrypt.c index 829e48258d..af61c4e75d 100644 --- a/crypto/hash-gcrypt.c +++ b/crypto/hash-gcrypt.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2016 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -25,92 +26,115 @@ #include "hashpriv.h" -static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = GCRY_MD_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = GCRY_MD_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = GCRY_MD_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = GCRY_MD_RMD160, +static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GCRY_MD_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GCRY_MD_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GCRY_MD_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GCRY_MD_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GCRY_MD_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GCRY_MD_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GCRY_MD_RMD160, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = GCRY_MD_SM3, +#endif }; -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +gboolean qcrypto_hash_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) && qcrypto_hash_alg_map[alg] != GCRY_MD_NONE) { - return true; + return gcry_md_test_algo(qcrypto_hash_alg_map[alg]) == 0; } return false; } - -static int -qcrypto_gcrypt_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) +static +QCryptoHash *qcrypto_gcrypt_hash_new(QCryptoHashAlgo alg, Error **errp) { - int i, ret; - gcry_md_hd_t md; - unsigned char *digest; + QCryptoHash *hash; + gcry_error_t ret; - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; - } + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = g_new(gcry_md_hd_t, 1); - ret = gcry_md_open(&md, qcrypto_hash_alg_map[alg], 0); - - if (ret < 0) { + ret = gcry_md_open((gcry_md_hd_t *) hash->opaque, + qcrypto_hash_alg_map[alg], 0); + if (ret != 0) { error_setg(errp, "Unable to initialize hash algorithm: %s", gcry_strerror(ret)); - return -1; + g_free(hash->opaque); + g_free(hash); + return NULL; + } + return hash; +} + +static +void qcrypto_gcrypt_hash_free(QCryptoHash *hash) +{ + gcry_md_hd_t *ctx = hash->opaque; + + if (ctx) { + gcry_md_close(*ctx); + g_free(ctx); } - for (i = 0; i < niov; i++) { - gcry_md_write(md, iov[i].iov_base, iov[i].iov_len); - } - - ret = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[alg]); - if (ret <= 0) { - error_setg(errp, - "Unable to get hash length: %s", - gcry_strerror(ret)); - goto error; - } - if (*resultlen == 0) { - *resultlen = ret; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != ret) { - error_setg(errp, - "Result buffer size %zu is smaller than hash %d", - *resultlen, ret); - goto error; - } - - digest = gcry_md_read(md, 0); - if (!digest) { - error_setg(errp, - "No digest produced"); - goto error; - } - memcpy(*result, digest, *resultlen); - - gcry_md_close(md); - return 0; - - error: - gcry_md_close(md); - return -1; + g_free(hash); } +static +int qcrypto_gcrypt_hash_update(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + gcry_md_hd_t *ctx = hash->opaque; + + for (int i = 0; i < niov; i++) { + gcry_md_write(*ctx, iov[i].iov_base, iov[i].iov_len); + } + + return 0; +} + +static +int qcrypto_gcrypt_hash_finalize(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + int ret; + unsigned char *digest; + gcry_md_hd_t *ctx = hash->opaque; + + ret = gcry_md_get_algo_dlen(qcrypto_hash_alg_map[hash->alg]); + if (ret == 0) { + error_setg(errp, "Unable to get hash length"); + return -1; + } + + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } + + /* Digest is freed by gcry_md_close(), copy it */ + digest = gcry_md_read(*ctx, 0); + memcpy(*result, digest, *result_len); + return 0; +} + QCryptoHashDriver qcrypto_hash_lib_driver = { - .hash_bytesv = qcrypto_gcrypt_hash_bytesv, + .hash_new = qcrypto_gcrypt_hash_new, + .hash_update = qcrypto_gcrypt_hash_update, + .hash_finalize = qcrypto_gcrypt_hash_finalize, + .hash_free = qcrypto_gcrypt_hash_free, }; diff --git a/crypto/hash-glib.c b/crypto/hash-glib.c index 82de9db705..809cef98ae 100644 --- a/crypto/hash-glib.c +++ b/crypto/hash-glib.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2016 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -24,17 +25,17 @@ #include "hashpriv.h" -static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = G_CHECKSUM_MD5, - [QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = -1, - [QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = -1, - [QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = -1, +static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = G_CHECKSUM_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = G_CHECKSUM_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = -1, + [QCRYPTO_HASH_ALGO_SHA256] = G_CHECKSUM_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = G_CHECKSUM_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = G_CHECKSUM_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = -1, }; -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +gboolean qcrypto_hash_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) && qcrypto_hash_alg_map[alg] != -1) { @@ -43,58 +44,78 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) return false; } - -static int -qcrypto_glib_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) +static +QCryptoHash *qcrypto_glib_hash_new(QCryptoHashAlgo alg, + Error **errp) { - int i, ret; - GChecksum *cs; + QCryptoHash *hash; - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = g_checksum_new(qcrypto_hash_alg_map[alg]); + + return hash; +} + +static +void qcrypto_glib_hash_free(QCryptoHash *hash) +{ + if (hash->opaque) { + g_checksum_free(hash->opaque); } - cs = g_checksum_new(qcrypto_hash_alg_map[alg]); - - for (i = 0; i < niov; i++) { - g_checksum_update(cs, iov[i].iov_base, iov[i].iov_len); - } - - ret = g_checksum_type_get_length(qcrypto_hash_alg_map[alg]); - if (ret < 0) { - error_setg(errp, "%s", - "Unable to get hash length"); - goto error; - } - if (*resultlen == 0) { - *resultlen = ret; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != ret) { - error_setg(errp, - "Result buffer size %zu is smaller than hash %d", - *resultlen, ret); - goto error; - } - - g_checksum_get_digest(cs, *result, resultlen); - - g_checksum_free(cs); - return 0; - - error: - g_checksum_free(cs); - return -1; + g_free(hash); } +static +int qcrypto_glib_hash_update(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + GChecksum *ctx = hash->opaque; + + for (int i = 0; i < niov; i++) { + g_checksum_update(ctx, iov[i].iov_base, iov[i].iov_len); + } + + return 0; +} + +static +int qcrypto_glib_hash_finalize(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + int ret; + GChecksum *ctx = hash->opaque; + + ret = g_checksum_type_get_length(qcrypto_hash_alg_map[hash->alg]); + if (ret < 0) { + error_setg(errp, "Unable to get hash length"); + *result_len = 0; + return -1; + } + + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } + + g_checksum_get_digest(ctx, *result, result_len); + return 0; +} + QCryptoHashDriver qcrypto_hash_lib_driver = { - .hash_bytesv = qcrypto_glib_hash_bytesv, + .hash_new = qcrypto_glib_hash_new, + .hash_update = qcrypto_glib_hash_update, + .hash_finalize = qcrypto_glib_hash_finalize, + .hash_free = qcrypto_glib_hash_free, }; diff --git a/crypto/hash-gnutls.c b/crypto/hash-gnutls.c index 17911ac5d1..99fbe824ea 100644 --- a/crypto/hash-gnutls.c +++ b/crypto/hash-gnutls.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2021 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -25,17 +26,17 @@ #include "hashpriv.h" -static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160, +static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GNUTLS_DIG_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GNUTLS_DIG_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GNUTLS_DIG_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GNUTLS_DIG_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GNUTLS_DIG_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GNUTLS_DIG_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160, }; -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +gboolean qcrypto_hash_supports(QCryptoHashAlgo alg) { size_t i; const gnutls_digest_algorithm_t *algs; @@ -52,53 +53,93 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) return false; } - -static int -qcrypto_gnutls_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) +static +QCryptoHash *qcrypto_gnutls_hash_new(QCryptoHashAlgo alg, Error **errp) { - int i, ret; - gnutls_hash_hd_t hash; + QCryptoHash *hash; + int ret; - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; - } + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = g_new(gnutls_hash_hd_t, 1); - ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); - if (*resultlen == 0) { - *resultlen = ret; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != ret) { - error_setg(errp, - "Result buffer size %zu is smaller than hash %d", - *resultlen, ret); - return -1; - } - - ret = gnutls_hash_init(&hash, qcrypto_hash_alg_map[alg]); + ret = gnutls_hash_init(hash->opaque, qcrypto_hash_alg_map[alg]); if (ret < 0) { error_setg(errp, "Unable to initialize hash algorithm: %s", gnutls_strerror(ret)); - return -1; + g_free(hash->opaque); + g_free(hash); + return NULL; } - for (i = 0; i < niov; i++) { - gnutls_hash(hash, iov[i].iov_base, iov[i].iov_len); - } + return hash; +} - gnutls_hash_deinit(hash, *result); - return 0; +static +void qcrypto_gnutls_hash_free(QCryptoHash *hash) +{ + gnutls_hash_hd_t *ctx = hash->opaque; + + gnutls_hash_deinit(*ctx, NULL); + g_free(ctx); + g_free(hash); } +static +int qcrypto_gnutls_hash_update(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + int ret = 0; + gnutls_hash_hd_t *ctx = hash->opaque; + + for (int i = 0; i < niov; i++) { + ret = gnutls_hash(*ctx, iov[i].iov_base, iov[i].iov_len); + if (ret != 0) { + error_setg(errp, "Failed to hash data: %s", + gnutls_strerror(ret)); + return -1; + } + } + + return 0; +} + +static +int qcrypto_gnutls_hash_finalize(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + gnutls_hash_hd_t *ctx = hash->opaque; + int ret; + + ret = gnutls_hash_get_len(qcrypto_hash_alg_map[hash->alg]); + if (ret == 0) { + error_setg(errp, "Unable to get hash length"); + return -1; + } + + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } + + gnutls_hash_output(*ctx, *result); + return 0; +} + QCryptoHashDriver qcrypto_hash_lib_driver = { - .hash_bytesv = qcrypto_gnutls_hash_bytesv, + .hash_new = qcrypto_gnutls_hash_new, + .hash_update = qcrypto_gnutls_hash_update, + .hash_finalize = qcrypto_gnutls_hash_finalize, + .hash_free = qcrypto_gnutls_hash_free, }; diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c index 1ca1a41062..53f68301ef 100644 --- a/crypto/hash-nettle.c +++ b/crypto/hash-nettle.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2016 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -25,6 +26,9 @@ #include #include #include +#ifdef CONFIG_CRYPTO_SM3 +#include +#endif typedef void (*qcrypto_nettle_init)(void *ctx); typedef void (*qcrypto_nettle_write)(void *ctx, @@ -42,6 +46,9 @@ union qcrypto_hash_ctx { struct sha384_ctx sha384; struct sha512_ctx sha512; struct ripemd160_ctx ripemd160; +#ifdef CONFIG_CRYPTO_SM3 + struct sm3_ctx sm3; +#endif }; struct qcrypto_hash_alg { @@ -50,51 +57,59 @@ struct qcrypto_hash_alg { qcrypto_nettle_result result; size_t len; } qcrypto_hash_alg_map[] = { - [QCRYPTO_HASH_ALG_MD5] = { + [QCRYPTO_HASH_ALGO_MD5] = { .init = (qcrypto_nettle_init)md5_init, .write = (qcrypto_nettle_write)md5_update, .result = (qcrypto_nettle_result)md5_digest, .len = MD5_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA1] = { + [QCRYPTO_HASH_ALGO_SHA1] = { .init = (qcrypto_nettle_init)sha1_init, .write = (qcrypto_nettle_write)sha1_update, .result = (qcrypto_nettle_result)sha1_digest, .len = SHA1_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA224] = { + [QCRYPTO_HASH_ALGO_SHA224] = { .init = (qcrypto_nettle_init)sha224_init, .write = (qcrypto_nettle_write)sha224_update, .result = (qcrypto_nettle_result)sha224_digest, .len = SHA224_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA256] = { + [QCRYPTO_HASH_ALGO_SHA256] = { .init = (qcrypto_nettle_init)sha256_init, .write = (qcrypto_nettle_write)sha256_update, .result = (qcrypto_nettle_result)sha256_digest, .len = SHA256_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA384] = { + [QCRYPTO_HASH_ALGO_SHA384] = { .init = (qcrypto_nettle_init)sha384_init, .write = (qcrypto_nettle_write)sha384_update, .result = (qcrypto_nettle_result)sha384_digest, .len = SHA384_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA512] = { + [QCRYPTO_HASH_ALGO_SHA512] = { .init = (qcrypto_nettle_init)sha512_init, .write = (qcrypto_nettle_write)sha512_update, .result = (qcrypto_nettle_result)sha512_digest, .len = SHA512_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_RIPEMD160] = { + [QCRYPTO_HASH_ALGO_RIPEMD160] = { .init = (qcrypto_nettle_init)ripemd160_init, .write = (qcrypto_nettle_write)ripemd160_update, .result = (qcrypto_nettle_result)ripemd160_digest, .len = RIPEMD160_DIGEST_SIZE, }, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = { + .init = (qcrypto_nettle_init)sm3_init, + .write = (qcrypto_nettle_write)sm3_update, + .result = (qcrypto_nettle_result)sm3_digest, + .len = SM3_DIGEST_SIZE, + }, +#endif }; -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) +gboolean qcrypto_hash_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map) && qcrypto_hash_alg_map[alg].init != NULL) { @@ -103,59 +118,72 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) return false; } - -static int -qcrypto_nettle_hash_bytesv(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) +static +QCryptoHash *qcrypto_nettle_hash_new(QCryptoHashAlgo alg, Error **errp) { - size_t i; - union qcrypto_hash_ctx ctx; + QCryptoHash *hash; - if (!qcrypto_hash_supports(alg)) { - error_setg(errp, - "Unknown hash algorithm %d", - alg); - return -1; + hash = g_new(QCryptoHash, 1); + hash->alg = alg; + hash->opaque = g_new(union qcrypto_hash_ctx, 1); + + qcrypto_hash_alg_map[alg].init(hash->opaque); + return hash; +} + +static +void qcrypto_nettle_hash_free(QCryptoHash *hash) +{ + union qcrypto_hash_ctx *ctx = hash->opaque; + + g_free(ctx); + g_free(hash); +} + +static +int qcrypto_nettle_hash_update(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + union qcrypto_hash_ctx *ctx = hash->opaque; + + for (int i = 0; i < niov; i++) { + qcrypto_hash_alg_map[hash->alg].write(ctx, + iov[i].iov_len, + iov[i].iov_base); } - qcrypto_hash_alg_map[alg].init(&ctx); - - for (i = 0; i < niov; i++) { - /* Some versions of nettle have functions - * declared with 'int' instead of 'size_t' - * so to be safe avoid writing more than - * UINT_MAX bytes at a time - */ - size_t len = iov[i].iov_len; - uint8_t *base = iov[i].iov_base; - while (len) { - size_t shortlen = MIN(len, UINT_MAX); - qcrypto_hash_alg_map[alg].write(&ctx, len, base); - len -= shortlen; - base += len; - } - } - - if (*resultlen == 0) { - *resultlen = qcrypto_hash_alg_map[alg].len; - *result = g_new0(uint8_t, *resultlen); - } else if (*resultlen != qcrypto_hash_alg_map[alg].len) { - error_setg(errp, - "Result buffer size %zu is smaller than hash %zu", - *resultlen, qcrypto_hash_alg_map[alg].len); - return -1; - } - - qcrypto_hash_alg_map[alg].result(&ctx, *resultlen, *result); - return 0; } +static +int qcrypto_nettle_hash_finalize(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + union qcrypto_hash_ctx *ctx = hash->opaque; + int ret = qcrypto_hash_alg_map[hash->alg].len; + + if (*result_len == 0) { + *result_len = ret; + *result = g_new(uint8_t, *result_len); + } else if (*result_len != ret) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *result_len, ret); + return -1; + } + + qcrypto_hash_alg_map[hash->alg].result(ctx, *result_len, *result); + + return 0; +} QCryptoHashDriver qcrypto_hash_lib_driver = { - .hash_bytesv = qcrypto_nettle_hash_bytesv, + .hash_new = qcrypto_nettle_hash_new, + .hash_update = qcrypto_nettle_hash_update, + .hash_finalize = qcrypto_nettle_hash_finalize, + .hash_free = qcrypto_nettle_hash_free, }; diff --git a/crypto/hash.c b/crypto/hash.c index b0f8228bdc..7513769e42 100644 --- a/crypto/hash.c +++ b/crypto/hash.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -19,53 +20,53 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi-types-crypto.h" #include "crypto/hash.h" #include "hashpriv.h" -static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = 16, - [QCRYPTO_HASH_ALG_SHA1] = 20, - [QCRYPTO_HASH_ALG_SHA224] = 28, - [QCRYPTO_HASH_ALG_SHA256] = 32, - [QCRYPTO_HASH_ALG_SHA384] = 48, - [QCRYPTO_HASH_ALG_SHA512] = 64, - [QCRYPTO_HASH_ALG_RIPEMD160] = 20, +static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = QCRYPTO_HASH_DIGEST_LEN_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = QCRYPTO_HASH_DIGEST_LEN_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = QCRYPTO_HASH_DIGEST_LEN_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = QCRYPTO_HASH_DIGEST_LEN_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = QCRYPTO_HASH_DIGEST_LEN_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = QCRYPTO_HASH_DIGEST_LEN_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = QCRYPTO_HASH_DIGEST_LEN_RIPEMD160, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = QCRYPTO_HASH_DIGEST_LEN_SM3, +#endif }; -size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) +size_t qcrypto_hash_digest_len(QCryptoHashAlgo alg) { assert(alg < G_N_ELEMENTS(qcrypto_hash_alg_size)); return qcrypto_hash_alg_size[alg]; } -int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, +int qcrypto_hash_bytesv(QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, uint8_t **result, size_t *resultlen, Error **errp) { -#ifdef CONFIG_AF_ALG - int ret; - /* - * TODO: - * Maybe we should treat some afalg errors as fatal - */ - ret = qcrypto_hash_afalg_driver.hash_bytesv(alg, iov, niov, - result, resultlen, - NULL); - if (ret == 0) { - return ret; - } -#endif + g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, errp); - return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov, - result, resultlen, - errp); + if (!ctx) { + return -1; + } + + if (qcrypto_hash_updatev(ctx, iov, niov, errp) < 0 || + qcrypto_hash_finalize_bytes(ctx, result, resultlen, errp) < 0) { + return -1; + } + + return 0; } -int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, +int qcrypto_hash_bytes(QCryptoHashAlgo alg, const char *buf, size_t len, uint8_t **result, @@ -77,33 +78,134 @@ int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp); } +int qcrypto_hash_updatev(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp) +{ + QCryptoHashDriver *drv = hash->driver; + + return drv->hash_update(hash, iov, niov, errp); +} + +int qcrypto_hash_update(QCryptoHash *hash, + const char *buf, + size_t len, + Error **errp) +{ + struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; + + return qcrypto_hash_updatev(hash, &iov, 1, errp); +} + +QCryptoHash *qcrypto_hash_new(QCryptoHashAlgo alg, Error **errp) +{ + QCryptoHash *hash = NULL; + + if (!qcrypto_hash_supports(alg)) { + error_setg(errp, "Unsupported hash algorithm %s", + QCryptoHashAlgo_str(alg)); + return NULL; + } + +#ifdef CONFIG_AF_ALG + hash = qcrypto_hash_afalg_driver.hash_new(alg, NULL); + if (hash) { + hash->driver = &qcrypto_hash_afalg_driver; + return hash; + } +#endif + + hash = qcrypto_hash_lib_driver.hash_new(alg, errp); + if (!hash) { + return NULL; + } + + hash->driver = &qcrypto_hash_lib_driver; + return hash; +} + +void qcrypto_hash_free(QCryptoHash *hash) +{ + QCryptoHashDriver *drv; + + if (hash) { + drv = hash->driver; + drv->hash_free(hash); + } +} + +int qcrypto_hash_finalize_bytes(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp) +{ + QCryptoHashDriver *drv = hash->driver; + + return drv->hash_finalize(hash, result, result_len, errp); +} + static const char hex[] = "0123456789abcdef"; -int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, +int qcrypto_hash_finalize_digest(QCryptoHash *hash, + char **digest, + Error **errp) +{ + int ret; + g_autofree uint8_t *result = NULL; + size_t resultlen = 0; + size_t i; + + ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, errp); + if (ret == 0) { + *digest = g_new0(char, (resultlen * 2) + 1); + for (i = 0 ; i < resultlen ; i++) { + (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; + (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; + } + (*digest)[resultlen * 2] = '\0'; + } + + return ret; +} + +int qcrypto_hash_finalize_base64(QCryptoHash *hash, + char **base64, + Error **errp) +{ + int ret; + g_autofree uint8_t *result = NULL; + size_t resultlen = 0; + + ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, errp); + if (ret == 0) { + *base64 = g_base64_encode(result, resultlen); + } + + return ret; +} + +int qcrypto_hash_digestv(QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, char **digest, Error **errp) { - uint8_t *result = NULL; - size_t resultlen = 0; - size_t i; + g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, errp); - if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { + if (!ctx) { return -1; } - *digest = g_new0(char, (resultlen * 2) + 1); - for (i = 0 ; i < resultlen ; i++) { - (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; - (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; + if (qcrypto_hash_updatev(ctx, iov, niov, errp) < 0 || + qcrypto_hash_finalize_digest(ctx, digest, errp) < 0) { + return -1; } - (*digest)[resultlen * 2] = '\0'; - g_free(result); + return 0; } -int qcrypto_hash_digest(QCryptoHashAlgorithm alg, +int qcrypto_hash_digest(QCryptoHashAlgo alg, const char *buf, size_t len, char **digest, @@ -114,25 +216,27 @@ int qcrypto_hash_digest(QCryptoHashAlgorithm alg, return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); } -int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, +int qcrypto_hash_base64v(QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, char **base64, Error **errp) { - uint8_t *result = NULL; - size_t resultlen = 0; + g_autoptr(QCryptoHash) ctx = qcrypto_hash_new(alg, errp); - if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { + if (!ctx) { + return -1; + } + + if (qcrypto_hash_updatev(ctx, iov, niov, errp) < 0 || + qcrypto_hash_finalize_base64(ctx, base64, errp) < 0) { return -1; } - *base64 = g_base64_encode(result, resultlen); - g_free(result); return 0; } -int qcrypto_hash_base64(QCryptoHashAlgorithm alg, +int qcrypto_hash_base64(QCryptoHashAlgo alg, const char *buf, size_t len, char **base64, diff --git a/crypto/hashpriv.h b/crypto/hashpriv.h index cee26ccb47..83b9256886 100644 --- a/crypto/hashpriv.h +++ b/crypto/hashpriv.h @@ -1,6 +1,7 @@ /* * QEMU Crypto hash driver supports * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. * * Authors: @@ -15,15 +16,21 @@ #ifndef QCRYPTO_HASHPRIV_H #define QCRYPTO_HASHPRIV_H +#include "crypto/hash.h" + typedef struct QCryptoHashDriver QCryptoHashDriver; struct QCryptoHashDriver { - int (*hash_bytesv)(QCryptoHashAlgorithm alg, + QCryptoHash *(*hash_new)(QCryptoHashAlgo alg, Error **errp); + int (*hash_update)(QCryptoHash *hash, const struct iovec *iov, size_t niov, - uint8_t **result, - size_t *resultlen, Error **errp); + int (*hash_finalize)(QCryptoHash *hash, + uint8_t **result, + size_t *resultlen, + Error **errp); + void (*hash_free)(QCryptoHash *hash); }; extern QCryptoHashDriver qcrypto_hash_lib_driver; diff --git a/crypto/hmac-gcrypt.c b/crypto/hmac-gcrypt.c index 0c6f979711..5273086eb9 100644 --- a/crypto/hmac-gcrypt.c +++ b/crypto/hmac-gcrypt.c @@ -18,14 +18,17 @@ #include "hmacpriv.h" #include -static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GCRY_MAC_HMAC_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GCRY_MAC_HMAC_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = GCRY_MAC_HMAC_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = GCRY_MAC_HMAC_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = GCRY_MAC_HMAC_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = GCRY_MAC_HMAC_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = GCRY_MAC_HMAC_RMD160, +static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GCRY_MAC_HMAC_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GCRY_MAC_HMAC_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GCRY_MAC_HMAC_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GCRY_MAC_HMAC_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GCRY_MAC_HMAC_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GCRY_MAC_HMAC_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GCRY_MAC_HMAC_RMD160, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = GCRY_MAC_HMAC_SM3, +#endif }; typedef struct QCryptoHmacGcrypt QCryptoHmacGcrypt; @@ -33,17 +36,17 @@ struct QCryptoHmacGcrypt { gcry_mac_hd_t handle; }; -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) +bool qcrypto_hmac_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hmac_alg_map) && qcrypto_hmac_alg_map[alg] != GCRY_MAC_NONE) { - return true; + return gcry_mac_test_algo(qcrypto_hmac_alg_map[alg]) == 0; } return false; } -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, +void *qcrypto_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp) { @@ -52,7 +55,7 @@ void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, if (!qcrypto_hmac_supports(alg)) { error_setg(errp, "Unsupported hmac algorithm %s", - QCryptoHashAlgorithm_str(alg)); + QCryptoHashAlgo_str(alg)); return NULL; } diff --git a/crypto/hmac-glib.c b/crypto/hmac-glib.c index 509bbc74c2..ea80c8d1b2 100644 --- a/crypto/hmac-glib.c +++ b/crypto/hmac-glib.c @@ -17,14 +17,14 @@ #include "crypto/hmac.h" #include "hmacpriv.h" -static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = G_CHECKSUM_MD5, - [QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1, - [QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256, - [QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512, - [QCRYPTO_HASH_ALG_SHA224] = -1, - [QCRYPTO_HASH_ALG_SHA384] = -1, - [QCRYPTO_HASH_ALG_RIPEMD160] = -1, +static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = G_CHECKSUM_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = G_CHECKSUM_SHA1, + [QCRYPTO_HASH_ALGO_SHA256] = G_CHECKSUM_SHA256, + [QCRYPTO_HASH_ALGO_SHA512] = G_CHECKSUM_SHA512, + [QCRYPTO_HASH_ALGO_SHA224] = -1, + [QCRYPTO_HASH_ALGO_SHA384] = -1, + [QCRYPTO_HASH_ALGO_RIPEMD160] = -1, }; typedef struct QCryptoHmacGlib QCryptoHmacGlib; @@ -32,7 +32,7 @@ struct QCryptoHmacGlib { GHmac *ghmac; }; -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) +bool qcrypto_hmac_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hmac_alg_map) && qcrypto_hmac_alg_map[alg] != -1) { @@ -42,7 +42,7 @@ bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) return false; } -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, +void *qcrypto_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp) { @@ -50,7 +50,7 @@ void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, if (!qcrypto_hmac_supports(alg)) { error_setg(errp, "Unsupported hmac algorithm %s", - QCryptoHashAlgorithm_str(alg)); + QCryptoHashAlgo_str(alg)); return NULL; } diff --git a/crypto/hmac-gnutls.c b/crypto/hmac-gnutls.c index 24db383322..822995505c 100644 --- a/crypto/hmac-gnutls.c +++ b/crypto/hmac-gnutls.c @@ -20,14 +20,14 @@ #include "crypto/hmac.h" #include "hmacpriv.h" -static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GNUTLS_MAC_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_MAC_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_MAC_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_MAC_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_MAC_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_MAC_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_MAC_RMD160, +static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GNUTLS_MAC_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GNUTLS_MAC_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GNUTLS_MAC_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GNUTLS_MAC_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GNUTLS_MAC_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GNUTLS_MAC_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_MAC_RMD160, }; typedef struct QCryptoHmacGnutls QCryptoHmacGnutls; @@ -35,7 +35,7 @@ struct QCryptoHmacGnutls { gnutls_hmac_hd_t handle; }; -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) +bool qcrypto_hmac_supports(QCryptoHashAlgo alg) { size_t i; const gnutls_digest_algorithm_t *algs; @@ -52,7 +52,7 @@ bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) return false; } -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, +void *qcrypto_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp) { @@ -61,7 +61,7 @@ void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, if (!qcrypto_hmac_supports(alg)) { error_setg(errp, "Unsupported hmac algorithm %s", - QCryptoHashAlgorithm_str(alg)); + QCryptoHashAlgo_str(alg)); return NULL; } diff --git a/crypto/hmac-nettle.c b/crypto/hmac-nettle.c index 1ad6c4f253..dd5b2ab7a1 100644 --- a/crypto/hmac-nettle.c +++ b/crypto/hmac-nettle.c @@ -38,6 +38,9 @@ struct QCryptoHmacNettle { struct hmac_sha256_ctx sha256_ctx; /* equals hmac_sha224_ctx */ struct hmac_sha512_ctx sha512_ctx; /* equals hmac_sha384_ctx */ struct hmac_ripemd160_ctx ripemd160_ctx; +#ifdef CONFIG_CRYPTO_SM3 + struct hmac_sm3_ctx ctx; +#endif } u; }; @@ -46,52 +49,60 @@ struct qcrypto_nettle_hmac_alg { qcrypto_nettle_hmac_update update; qcrypto_nettle_hmac_digest digest; size_t len; -} qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = { +} qcrypto_hmac_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_md5_set_key, .update = (qcrypto_nettle_hmac_update)hmac_md5_update, .digest = (qcrypto_nettle_hmac_digest)hmac_md5_digest, .len = MD5_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA1] = { + [QCRYPTO_HASH_ALGO_SHA1] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_sha1_set_key, .update = (qcrypto_nettle_hmac_update)hmac_sha1_update, .digest = (qcrypto_nettle_hmac_digest)hmac_sha1_digest, .len = SHA1_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA224] = { + [QCRYPTO_HASH_ALGO_SHA224] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_sha224_set_key, .update = (qcrypto_nettle_hmac_update)hmac_sha224_update, .digest = (qcrypto_nettle_hmac_digest)hmac_sha224_digest, .len = SHA224_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA256] = { + [QCRYPTO_HASH_ALGO_SHA256] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_sha256_set_key, .update = (qcrypto_nettle_hmac_update)hmac_sha256_update, .digest = (qcrypto_nettle_hmac_digest)hmac_sha256_digest, .len = SHA256_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA384] = { + [QCRYPTO_HASH_ALGO_SHA384] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_sha384_set_key, .update = (qcrypto_nettle_hmac_update)hmac_sha384_update, .digest = (qcrypto_nettle_hmac_digest)hmac_sha384_digest, .len = SHA384_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_SHA512] = { + [QCRYPTO_HASH_ALGO_SHA512] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_sha512_set_key, .update = (qcrypto_nettle_hmac_update)hmac_sha512_update, .digest = (qcrypto_nettle_hmac_digest)hmac_sha512_digest, .len = SHA512_DIGEST_SIZE, }, - [QCRYPTO_HASH_ALG_RIPEMD160] = { + [QCRYPTO_HASH_ALGO_RIPEMD160] = { .setkey = (qcrypto_nettle_hmac_setkey)hmac_ripemd160_set_key, .update = (qcrypto_nettle_hmac_update)hmac_ripemd160_update, .digest = (qcrypto_nettle_hmac_digest)hmac_ripemd160_digest, .len = RIPEMD160_DIGEST_SIZE, }, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = { + .setkey = (qcrypto_nettle_hmac_setkey)hmac_sm3_set_key, + .update = (qcrypto_nettle_hmac_update)hmac_sm3_update, + .digest = (qcrypto_nettle_hmac_digest)hmac_sm3_digest, + .len = SM3_DIGEST_SIZE, + }, +#endif }; -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) +bool qcrypto_hmac_supports(QCryptoHashAlgo alg) { if (alg < G_N_ELEMENTS(qcrypto_hmac_alg_map) && qcrypto_hmac_alg_map[alg].setkey != NULL) { @@ -101,7 +112,7 @@ bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) return false; } -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, +void *qcrypto_hmac_ctx_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp) { @@ -109,7 +120,7 @@ void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, if (!qcrypto_hmac_supports(alg)) { error_setg(errp, "Unsupported hmac algorithm %s", - QCryptoHashAlgorithm_str(alg)); + QCryptoHashAlgo_str(alg)); return NULL; } diff --git a/crypto/hmac.c b/crypto/hmac.c index 4de7e8c9cb..422e005182 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -83,7 +83,7 @@ int qcrypto_hmac_digest(QCryptoHmac *hmac, return qcrypto_hmac_digestv(hmac, &iov, 1, digest, errp); } -QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg, +QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp) { diff --git a/crypto/hmacpriv.h b/crypto/hmacpriv.h index 4387ca2587..f339596bd9 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(QCryptoHashAlgo 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); +QCryptoAFAlgo *qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgo alg, + const uint8_t *key, size_t nkey, + Error **errp); extern QCryptoHmacDriver qcrypto_hmac_afalg_driver; #endif diff --git a/crypto/init.c b/crypto/init.c index fb7f1bff10..674d237fa9 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -34,14 +34,11 @@ #include "crypto/random.h" -/* #define DEBUG_GNUTLS */ -#ifdef DEBUG_GNUTLS -static void qcrypto_gnutls_log(int level, const char *str) -{ - fprintf(stderr, "%d: %s", level, str); -} -#endif +/* + * To debug GNUTLS see env vars listed in + * https://gnutls.org/manual/html_node/Debugging-and-auditing.html + */ int qcrypto_init(Error **errp) { #ifdef CONFIG_GNUTLS @@ -53,10 +50,6 @@ int qcrypto_init(Error **errp) gnutls_strerror(ret)); return -1; } -#ifdef DEBUG_GNUTLS - gnutls_global_set_log_level(10); - gnutls_global_set_log_function(qcrypto_gnutls_log); -#endif #endif #ifdef CONFIG_GCRYPT diff --git a/crypto/ivgen.c b/crypto/ivgen.c index 12822f8519..6b7d24d889 100644 --- a/crypto/ivgen.c +++ b/crypto/ivgen.c @@ -27,9 +27,9 @@ #include "ivgen-essiv.h" -QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, - QCryptoCipherAlgorithm cipheralg, - QCryptoHashAlgorithm hash, +QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgo alg, + QCryptoCipherAlgo cipheralg, + QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, Error **errp) { @@ -40,13 +40,13 @@ QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, ivgen->hash = hash; switch (alg) { - case QCRYPTO_IVGEN_ALG_PLAIN: + case QCRYPTO_IV_GEN_ALGO_PLAIN: ivgen->driver = &qcrypto_ivgen_plain; break; - case QCRYPTO_IVGEN_ALG_PLAIN64: + case QCRYPTO_IV_GEN_ALGO_PLAIN64: ivgen->driver = &qcrypto_ivgen_plain64; break; - case QCRYPTO_IVGEN_ALG_ESSIV: + case QCRYPTO_IV_GEN_ALGO_ESSIV: ivgen->driver = &qcrypto_ivgen_essiv; break; default: @@ -73,19 +73,19 @@ int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen, } -QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen) +QCryptoIVGenAlgo qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen) { return ivgen->algorithm; } -QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen) +QCryptoCipherAlgo qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen) { return ivgen->cipher; } -QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen) +QCryptoHashAlgo qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen) { return ivgen->hash; } diff --git a/crypto/ivgenpriv.h b/crypto/ivgenpriv.h index cecdbedfde..e3388d30be 100644 --- a/crypto/ivgenpriv.h +++ b/crypto/ivgenpriv.h @@ -40,9 +40,9 @@ struct QCryptoIVGen { QCryptoIVGenDriver *driver; void *private; - QCryptoIVGenAlgorithm algorithm; - QCryptoCipherAlgorithm cipher; - QCryptoHashAlgorithm hash; + QCryptoIVGenAlgo algorithm; + QCryptoCipherAlgo cipher; + QCryptoHashAlgo hash; }; diff --git a/crypto/meson.build b/crypto/meson.build index 5f03a30d34..735635de1f 100644 --- a/crypto/meson.build +++ b/crypto/meson.build @@ -24,6 +24,10 @@ crypto_ss.add(files( 'rsakey.c', )) +if gnutls.found() + crypto_ss.add(files('x509-utils.c')) +endif + if nettle.found() crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c')) if hogweed.found() @@ -46,11 +50,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-gcrypt.c b/crypto/pbkdf-gcrypt.c index a8d8e64f4d..e89b8b1c76 100644 --- a/crypto/pbkdf-gcrypt.c +++ b/crypto/pbkdf-gcrypt.c @@ -23,37 +23,43 @@ #include "qapi/error.h" #include "crypto/pbkdf.h" -bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +bool qcrypto_pbkdf2_supports(QCryptoHashAlgo hash) { switch (hash) { - case QCRYPTO_HASH_ALG_MD5: - case QCRYPTO_HASH_ALG_SHA1: - case QCRYPTO_HASH_ALG_SHA224: - case QCRYPTO_HASH_ALG_SHA256: - case QCRYPTO_HASH_ALG_SHA384: - case QCRYPTO_HASH_ALG_SHA512: - case QCRYPTO_HASH_ALG_RIPEMD160: - return true; + case QCRYPTO_HASH_ALGO_MD5: + case QCRYPTO_HASH_ALGO_SHA1: + case QCRYPTO_HASH_ALGO_SHA224: + case QCRYPTO_HASH_ALGO_SHA256: + case QCRYPTO_HASH_ALGO_SHA384: + case QCRYPTO_HASH_ALGO_SHA512: + case QCRYPTO_HASH_ALGO_RIPEMD160: +#ifdef CONFIG_CRYPTO_SM3 + case QCRYPTO_HASH_ALGO_SM3: +#endif + return qcrypto_hash_supports(hash); default: return false; } } -int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, +int qcrypto_pbkdf2(QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, const uint8_t *salt, size_t nsalt, uint64_t iterations, uint8_t *out, size_t nout, Error **errp) { - static const int hash_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = GCRY_MD_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = GCRY_MD_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = GCRY_MD_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = GCRY_MD_RMD160, + static const int hash_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GCRY_MD_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GCRY_MD_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GCRY_MD_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GCRY_MD_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GCRY_MD_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GCRY_MD_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GCRY_MD_RMD160, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = GCRY_MD_SM3, +#endif }; int ret; @@ -68,7 +74,7 @@ int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, hash_map[hash] == GCRY_MD_NONE) { error_setg_errno(errp, ENOSYS, "PBKDF does not support hash algorithm %s", - QCryptoHashAlgorithm_str(hash)); + QCryptoHashAlgo_str(hash)); return -1; } diff --git a/crypto/pbkdf-gnutls.c b/crypto/pbkdf-gnutls.c index 2dfbbd382c..f34423f918 100644 --- a/crypto/pbkdf-gnutls.c +++ b/crypto/pbkdf-gnutls.c @@ -23,37 +23,37 @@ #include "qapi/error.h" #include "crypto/pbkdf.h" -bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +bool qcrypto_pbkdf2_supports(QCryptoHashAlgo hash) { switch (hash) { - case QCRYPTO_HASH_ALG_MD5: - case QCRYPTO_HASH_ALG_SHA1: - case QCRYPTO_HASH_ALG_SHA224: - case QCRYPTO_HASH_ALG_SHA256: - case QCRYPTO_HASH_ALG_SHA384: - case QCRYPTO_HASH_ALG_SHA512: - case QCRYPTO_HASH_ALG_RIPEMD160: - return true; + case QCRYPTO_HASH_ALGO_MD5: + case QCRYPTO_HASH_ALGO_SHA1: + case QCRYPTO_HASH_ALGO_SHA224: + case QCRYPTO_HASH_ALGO_SHA256: + case QCRYPTO_HASH_ALGO_SHA384: + case QCRYPTO_HASH_ALGO_SHA512: + case QCRYPTO_HASH_ALGO_RIPEMD160: + return qcrypto_hash_supports(hash); default: return false; } } -int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, +int qcrypto_pbkdf2(QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, const uint8_t *salt, size_t nsalt, uint64_t iterations, uint8_t *out, size_t nout, Error **errp) { - static const int hash_map[QCRYPTO_HASH_ALG__MAX] = { - [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, - [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160, + static const int hash_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GNUTLS_DIG_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GNUTLS_DIG_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GNUTLS_DIG_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GNUTLS_DIG_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GNUTLS_DIG_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GNUTLS_DIG_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160, }; int ret; const gnutls_datum_t gkey = { (unsigned char *)key, nkey }; @@ -70,7 +70,7 @@ int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, hash_map[hash] == GNUTLS_DIG_UNKNOWN) { error_setg_errno(errp, ENOSYS, "PBKDF does not support hash algorithm %s", - QCryptoHashAlgorithm_str(hash)); + QCryptoHashAlgo_str(hash)); return -1; } diff --git a/crypto/pbkdf-nettle.c b/crypto/pbkdf-nettle.c index d6293c25a1..3ef9c1b52c 100644 --- a/crypto/pbkdf-nettle.c +++ b/crypto/pbkdf-nettle.c @@ -25,22 +25,25 @@ #include "crypto/pbkdf.h" -bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash) +bool qcrypto_pbkdf2_supports(QCryptoHashAlgo hash) { switch (hash) { - case QCRYPTO_HASH_ALG_SHA1: - case QCRYPTO_HASH_ALG_SHA224: - case QCRYPTO_HASH_ALG_SHA256: - case QCRYPTO_HASH_ALG_SHA384: - case QCRYPTO_HASH_ALG_SHA512: - case QCRYPTO_HASH_ALG_RIPEMD160: + case QCRYPTO_HASH_ALGO_SHA1: + case QCRYPTO_HASH_ALGO_SHA224: + case QCRYPTO_HASH_ALGO_SHA256: + case QCRYPTO_HASH_ALGO_SHA384: + case QCRYPTO_HASH_ALGO_SHA512: + case QCRYPTO_HASH_ALGO_RIPEMD160: +#ifdef CONFIG_CRYPTO_SM3 + case QCRYPTO_HASH_ALGO_SM3: +#endif return true; default: return false; } } -int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, +int qcrypto_pbkdf2(QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, const uint8_t *salt, size_t nsalt, uint64_t iterations, @@ -55,6 +58,9 @@ int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, struct hmac_sha384_ctx sha384; struct hmac_sha512_ctx sha512; struct hmac_ripemd160_ctx ripemd160; +#ifdef CONFIG_CRYPTO_SM3 + struct hmac_sm3_ctx sm3; +#endif } ctx; if (iterations > UINT_MAX) { @@ -65,52 +71,59 @@ int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, } switch (hash) { - case QCRYPTO_HASH_ALG_MD5: + case QCRYPTO_HASH_ALGO_MD5: hmac_md5_set_key(&ctx.md5, nkey, key); PBKDF2(&ctx.md5, hmac_md5_update, hmac_md5_digest, MD5_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; - case QCRYPTO_HASH_ALG_SHA1: + case QCRYPTO_HASH_ALGO_SHA1: hmac_sha1_set_key(&ctx.sha1, nkey, key); PBKDF2(&ctx.sha1, hmac_sha1_update, hmac_sha1_digest, SHA1_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; - case QCRYPTO_HASH_ALG_SHA224: + case QCRYPTO_HASH_ALGO_SHA224: hmac_sha224_set_key(&ctx.sha224, nkey, key); PBKDF2(&ctx.sha224, hmac_sha224_update, hmac_sha224_digest, SHA224_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; - case QCRYPTO_HASH_ALG_SHA256: + case QCRYPTO_HASH_ALGO_SHA256: hmac_sha256_set_key(&ctx.sha256, nkey, key); PBKDF2(&ctx.sha256, hmac_sha256_update, hmac_sha256_digest, SHA256_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; - case QCRYPTO_HASH_ALG_SHA384: + case QCRYPTO_HASH_ALGO_SHA384: hmac_sha384_set_key(&ctx.sha384, nkey, key); PBKDF2(&ctx.sha384, hmac_sha384_update, hmac_sha384_digest, SHA384_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; - case QCRYPTO_HASH_ALG_SHA512: + case QCRYPTO_HASH_ALGO_SHA512: hmac_sha512_set_key(&ctx.sha512, nkey, key); PBKDF2(&ctx.sha512, hmac_sha512_update, hmac_sha512_digest, SHA512_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; - case QCRYPTO_HASH_ALG_RIPEMD160: + case QCRYPTO_HASH_ALGO_RIPEMD160: hmac_ripemd160_set_key(&ctx.ripemd160, nkey, key); PBKDF2(&ctx.ripemd160, hmac_ripemd160_update, hmac_ripemd160_digest, RIPEMD160_DIGEST_SIZE, iterations, nsalt, salt, nout, out); break; +#ifdef CONFIG_CRYPTO_SM3 + case QCRYPTO_HASH_ALGO_SM3: + hmac_sm3_set_key(&ctx.sm3, nkey, key); + PBKDF2(&ctx.sm3, hmac_sm3_update, hmac_sm3_digest, + SM3_DIGEST_SIZE, iterations, nsalt, salt, nout, out); + break; +#endif default: error_setg_errno(errp, ENOSYS, "PBKDF does not support hash algorithm %s", - QCryptoHashAlgorithm_str(hash)); + QCryptoHashAlgo_str(hash)); return -1; } return 0; diff --git a/crypto/pbkdf-stub.c b/crypto/pbkdf-stub.c index 9c4622e424..9f29d0eed7 100644 --- a/crypto/pbkdf-stub.c +++ b/crypto/pbkdf-stub.c @@ -22,12 +22,12 @@ #include "qapi/error.h" #include "crypto/pbkdf.h" -bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash G_GNUC_UNUSED) +bool qcrypto_pbkdf2_supports(QCryptoHashAlgo hash G_GNUC_UNUSED) { return false; } -int qcrypto_pbkdf2(QCryptoHashAlgorithm hash G_GNUC_UNUSED, +int qcrypto_pbkdf2(QCryptoHashAlgo hash G_GNUC_UNUSED, const uint8_t *key G_GNUC_UNUSED, size_t nkey G_GNUC_UNUSED, const uint8_t *salt G_GNUC_UNUSED, diff --git a/crypto/pbkdf.c b/crypto/pbkdf.c index 8d198c152c..0dd7c3aeaa 100644 --- a/crypto/pbkdf.c +++ b/crypto/pbkdf.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/thread.h" #include "qapi/error.h" #include "crypto/pbkdf.h" #ifndef _WIN32 @@ -85,12 +86,28 @@ static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms, #endif } -uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, - const uint8_t *key, size_t nkey, - const uint8_t *salt, size_t nsalt, - size_t nout, - Error **errp) +typedef struct CountItersData { + QCryptoHashAlgo hash; + const uint8_t *key; + size_t nkey; + const uint8_t *salt; + size_t nsalt; + size_t nout; + uint64_t iterations; + Error **errp; +} CountItersData; + +static void *threaded_qcrypto_pbkdf2_count_iters(void *data) { + CountItersData *iters_data = (CountItersData *) data; + QCryptoHashAlgo hash = iters_data->hash; + const uint8_t *key = iters_data->key; + size_t nkey = iters_data->nkey; + const uint8_t *salt = iters_data->salt; + size_t nsalt = iters_data->nsalt; + size_t nout = iters_data->nout; + Error **errp = iters_data->errp; + uint64_t ret = -1; g_autofree uint8_t *out = g_new(uint8_t, nout); uint64_t iterations = (1 << 15); @@ -114,7 +131,10 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, delta_ms = end_ms - start_ms; - if (delta_ms > 500) { + if (delta_ms == 0) { /* sanity check */ + error_setg(errp, "Unable to get accurate CPU usage"); + goto cleanup; + } else if (delta_ms > 500) { break; } else if (delta_ms < 100) { iterations = iterations * 10; @@ -129,5 +149,24 @@ uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, cleanup: memset(out, 0, nout); - return ret; + iters_data->iterations = ret; + return NULL; +} + +uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgo hash, + const uint8_t *key, size_t nkey, + const uint8_t *salt, size_t nsalt, + size_t nout, + Error **errp) +{ + CountItersData data = { + hash, key, nkey, salt, nsalt, nout, 0, errp + }; + QemuThread thread; + + qemu_thread_create(&thread, "pbkdf2", threaded_qcrypto_pbkdf2_count_iters, + &data, QEMU_THREAD_JOINABLE); + qemu_thread_join(&thread); + + return data.iterations; } diff --git a/crypto/rsakey-builtin.c.inc b/crypto/rsakey-builtin.c.inc index aeeacc8f9b..6337b84c54 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; } @@ -187,10 +183,10 @@ QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse( size_t keylen, Error **errp) { switch (type) { - case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE: return qcrypto_builtin_rsa_private_key_parse(key, keylen, errp); - case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC: return qcrypto_builtin_rsa_public_key_parse(key, keylen, errp); default: diff --git a/crypto/rsakey-nettle.c.inc b/crypto/rsakey-nettle.c.inc index cc49872e78..b7f34b0234 100644 --- a/crypto/rsakey-nettle.c.inc +++ b/crypto/rsakey-nettle.c.inc @@ -145,10 +145,10 @@ QCryptoAkCipherRSAKey *qcrypto_akcipher_rsakey_parse( size_t keylen, Error **errp) { switch (type) { - case QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE: return qcrypto_nettle_rsa_private_key_parse(key, keylen, errp); - case QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC: + case QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC: return qcrypto_nettle_rsa_public_key_parse(key, keylen, errp); default: diff --git a/crypto/secret_common.c b/crypto/secret_common.c index 3441c44ca8..dbda998940 100644 --- a/crypto/secret_common.c +++ b/crypto/secret_common.c @@ -71,7 +71,7 @@ static void qcrypto_secret_decrypt(QCryptoSecretCommon *secret, return; } - aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256, + aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC, key, keylen, errp); @@ -191,15 +191,6 @@ qcrypto_secret_complete(UserCreatable *uc, Error **errp) } -static bool -qcrypto_secret_prop_get_loaded(Object *obj, - Error **errp G_GNUC_UNUSED) -{ - QCryptoSecretCommon *secret = QCRYPTO_SECRET_COMMON(obj); - return secret->rawdata != NULL; -} - - static void qcrypto_secret_prop_set_format(Object *obj, int value, @@ -278,9 +269,6 @@ qcrypto_secret_class_init(ObjectClass *oc, void *data) ucc->complete = qcrypto_secret_complete; - object_class_property_add_bool(oc, "loaded", - qcrypto_secret_prop_get_loaded, - NULL); object_class_property_add_enum(oc, "format", "QCryptoSecretFormat", &QCryptoSecretFormat_lookup, 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/tlscredsanon.c b/crypto/tlscredsanon.c index c0d23a0ef3..476cf89c96 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c @@ -127,37 +127,6 @@ qcrypto_tls_creds_anon_complete(UserCreatable *uc, Error **errp) } -#ifdef CONFIG_GNUTLS - - -static bool -qcrypto_tls_creds_anon_prop_get_loaded(Object *obj, - Error **errp G_GNUC_UNUSED) -{ - QCryptoTLSCredsAnon *creds = QCRYPTO_TLS_CREDS_ANON(obj); - - if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { - return creds->data.server != NULL; - } else { - return creds->data.client != NULL; - } -} - - -#else /* ! CONFIG_GNUTLS */ - - -static bool -qcrypto_tls_creds_anon_prop_get_loaded(Object *obj G_GNUC_UNUSED, - Error **errp G_GNUC_UNUSED) -{ - return false; -} - - -#endif /* ! CONFIG_GNUTLS */ - - static void qcrypto_tls_creds_anon_finalize(Object *obj) { @@ -173,10 +142,6 @@ qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data) UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); ucc->complete = qcrypto_tls_creds_anon_complete; - - object_class_property_add_bool(oc, "loaded", - qcrypto_tls_creds_anon_prop_get_loaded, - NULL); } diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index 546cad1c5a..aa270d7988 100644 --- a/crypto/tlscredspsk.c +++ b/crypto/tlscredspsk.c @@ -206,43 +206,13 @@ qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp) } -#ifdef CONFIG_GNUTLS - - -static bool -qcrypto_tls_creds_psk_prop_get_loaded(Object *obj, - Error **errp G_GNUC_UNUSED) -{ - QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); - - if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { - return creds->data.server != NULL; - } else { - return creds->data.client != NULL; - } -} - - -#else /* ! CONFIG_GNUTLS */ - - -static bool -qcrypto_tls_creds_psk_prop_get_loaded(Object *obj G_GNUC_UNUSED, - Error **errp G_GNUC_UNUSED) -{ - return false; -} - - -#endif /* ! CONFIG_GNUTLS */ - - static void qcrypto_tls_creds_psk_finalize(Object *obj) { QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); qcrypto_tls_creds_psk_unload(creds); + g_free(creds->username); } static void @@ -272,9 +242,6 @@ qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) ucc->complete = qcrypto_tls_creds_psk_complete; - object_class_property_add_bool(oc, "loaded", - qcrypto_tls_creds_psk_prop_get_loaded, - NULL); object_class_property_add_str(oc, "username", qcrypto_tls_creds_psk_prop_get_username, qcrypto_tls_creds_psk_prop_set_username); diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index d14313925d..24ec584922 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -695,33 +695,6 @@ qcrypto_tls_creds_x509_complete(UserCreatable *uc, Error **errp) } -#ifdef CONFIG_GNUTLS - - -static bool -qcrypto_tls_creds_x509_prop_get_loaded(Object *obj, - Error **errp G_GNUC_UNUSED) -{ - QCryptoTLSCredsX509 *creds = QCRYPTO_TLS_CREDS_X509(obj); - - return creds->data != NULL; -} - - -#else /* ! CONFIG_GNUTLS */ - - -static bool -qcrypto_tls_creds_x509_prop_get_loaded(Object *obj G_GNUC_UNUSED, - Error **errp G_GNUC_UNUSED) -{ - return false; -} - - -#endif /* ! CONFIG_GNUTLS */ - - static void qcrypto_tls_creds_x509_prop_set_sanity(Object *obj, bool value, @@ -838,9 +811,6 @@ qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) ucc->complete = qcrypto_tls_creds_x509_complete; - object_class_property_add_bool(oc, "loaded", - qcrypto_tls_creds_x509_prop_get_loaded, - NULL); object_class_property_add_bool(oc, "sanity-check", qcrypto_tls_creds_x509_prop_get_sanity, qcrypto_tls_creds_x509_prop_set_sanity); diff --git a/crypto/tlssession.c b/crypto/tlssession.c index b302d835d2..77286e23f4 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -44,6 +44,13 @@ struct QCryptoTLSSession { QCryptoTLSSessionReadFunc readFunc; void *opaque; char *peername; + + /* + * Allow concurrent reads and writes, so track + * errors separately + */ + Error *rerr; + Error *werr; }; @@ -54,6 +61,9 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) return; } + error_free(session->rerr); + error_free(session->werr); + gnutls_deinit(session->handle); g_free(session->hostname); g_free(session->peername); @@ -67,13 +77,26 @@ static ssize_t qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) { QCryptoTLSSession *session = opaque; + ssize_t ret; if (!session->writeFunc) { errno = EIO; return -1; }; - return session->writeFunc(buf, len, session->opaque); + error_free(session->werr); + session->werr = NULL; + + ret = session->writeFunc(buf, len, session->opaque, &session->werr); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } else { + return ret; + } } @@ -81,13 +104,26 @@ static ssize_t qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) { QCryptoTLSSession *session = opaque; + ssize_t ret; if (!session->readFunc) { errno = EIO; return -1; }; - return session->readFunc(buf, len, session->opaque); + error_free(session->rerr); + session->rerr = NULL; + + ret = session->readFunc(buf, len, session->opaque, &session->rerr); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + errno = EAGAIN; + return -1; + } else if (ret < 0) { + errno = EIO; + return -1; + } else { + return ret; + } } #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" @@ -441,23 +477,25 @@ qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, ssize_t qcrypto_tls_session_write(QCryptoTLSSession *session, const char *buf, - size_t len) + size_t len, + Error **errp) { ssize_t ret = gnutls_record_send(session->handle, buf, len); if (ret < 0) { - switch (ret) { - case GNUTLS_E_AGAIN: - errno = EAGAIN; - break; - case GNUTLS_E_INTERRUPTED: - errno = EINTR; - break; - default: - errno = EIO; - break; + if (ret == GNUTLS_E_AGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else { + if (session->werr) { + error_propagate(errp, session->werr); + session->werr = NULL; + } else { + error_setg(errp, + "Cannot write to TLS channel: %s", + gnutls_strerror(ret)); + } + return -1; } - ret = -1; } return ret; @@ -467,32 +505,42 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, ssize_t qcrypto_tls_session_read(QCryptoTLSSession *session, char *buf, - size_t len) + size_t len, + bool gracefulTermination, + Error **errp) { ssize_t ret = gnutls_record_recv(session->handle, buf, len); if (ret < 0) { - switch (ret) { - case GNUTLS_E_AGAIN: - errno = EAGAIN; - break; - case GNUTLS_E_INTERRUPTED: - errno = EINTR; - break; - case GNUTLS_E_PREMATURE_TERMINATION: - errno = ECONNABORTED; - break; - default: - errno = EIO; - break; + if (ret == GNUTLS_E_AGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) && + gracefulTermination){ + return 0; + } else { + if (session->rerr) { + error_propagate(errp, session->rerr); + session->rerr = NULL; + } else { + error_setg(errp, + "Cannot read from TLS channel: %s", + gnutls_strerror(ret)); + } + return -1; } - ret = -1; } return ret; } +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) @@ -505,11 +553,21 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, ret == GNUTLS_E_AGAIN) { ret = 1; } else { - error_setg(errp, "TLS handshake failed: %s", - gnutls_strerror(ret)); + if (session->rerr || session->werr) { + error_setg(errp, "TLS handshake failed: %s: %s", + gnutls_strerror(ret), + error_get_pretty(session->rerr ? + session->rerr : session->werr)); + } else { + error_setg(errp, "TLS handshake failed: %s", + gnutls_strerror(ret)); + } ret = -1; } } + error_free(session->rerr); + error_free(session->werr); + session->rerr = session->werr = NULL; return ret; } @@ -598,9 +656,10 @@ qcrypto_tls_session_set_callbacks( ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, const char *buf, - size_t len) + size_t len, + Error **errp) { - errno = -EIO; + error_setg(errp, "TLS requires GNUTLS support"); return -1; } @@ -608,13 +667,22 @@ qcrypto_tls_session_write(QCryptoTLSSession *sess, ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, - size_t len) + size_t len, + bool gracefulTermination, + Error **errp) { - errno = -EIO; + error_setg(errp, "TLS requires GNUTLS support"); return -1; } +size_t +qcrypto_tls_session_check_pending(QCryptoTLSSession *session) +{ + return 0; +} + + int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, Error **errp) diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c new file mode 100644 index 0000000000..8bad00a51b --- /dev/null +++ b/crypto/x509-utils.c @@ -0,0 +1,76 @@ +/* + * X.509 certificate related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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 "qapi/error.h" +#include "crypto/x509-utils.h" +#include +#include +#include + +static const int qcrypto_to_gnutls_hash_alg_map[QCRYPTO_HASH_ALGO__MAX] = { + [QCRYPTO_HASH_ALGO_MD5] = GNUTLS_DIG_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = GNUTLS_DIG_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = GNUTLS_DIG_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = GNUTLS_DIG_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = GNUTLS_DIG_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = GNUTLS_DIG_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = GNUTLS_DIG_RMD160, +}; + +int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, + QCryptoHashAlgo alg, + uint8_t *result, + size_t *resultlen, + Error **errp) +{ + int ret = -1; + int hlen; + gnutls_x509_crt_t crt; + gnutls_datum_t datum = {.data = cert, .size = size}; + + if (alg >= G_N_ELEMENTS(qcrypto_to_gnutls_hash_alg_map)) { + error_setg(errp, "Unknown hash algorithm"); + return -1; + } + + if (result == NULL) { + error_setg(errp, "No valid buffer given"); + return -1; + } + + gnutls_x509_crt_init(&crt); + + if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM) != 0) { + error_setg(errp, "Failed to import certificate"); + goto cleanup; + } + + hlen = gnutls_hash_get_len(qcrypto_to_gnutls_hash_alg_map[alg]); + if (*resultlen < hlen) { + error_setg(errp, + "Result buffer size %zu is smaller than hash %d", + *resultlen, hlen); + goto cleanup; + } + + if (gnutls_x509_crt_get_fingerprint(crt, + qcrypto_to_gnutls_hash_alg_map[alg], + result, resultlen) != 0) { + error_setg(errp, "Failed to get fingerprint from certificate"); + goto cleanup; + } + + ret = 0; + + cleanup: + gnutls_x509_crt_deinit(crt); + return ret; +} diff --git a/disas.c b/disas.c deleted file mode 100644 index 94d3b45042..0000000000 --- a/disas.c +++ /dev/null @@ -1,395 +0,0 @@ -/* General "disassemble this chunk" code. Used for debugging. */ -#include "qemu/osdep.h" -#include "disas/dis-asm.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; - -/* Filled in by elfload.c. Simplistic, but will do for now. */ -struct syminfo *syminfos = NULL; - -/* - * Get LENGTH bytes from info's buffer, at host address memaddr. - * Transfer them to myaddr. - */ -static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) -{ - if (memaddr < info->buffer_vma - || memaddr + length > info->buffer_vma + info->buffer_length) { - /* Out of bounds. Use EIO because GDB uses it. */ - return EIO; - } - memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); - return 0; -} - -/* - * Get LENGTH bytes from info's buffer, at target address memaddr. - * Transfer them to myaddr. - */ -static int target_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, - struct disassemble_info *info) -{ - CPUDebug *s = container_of(info, CPUDebug, info); - int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); - return r ? EIO : 0; -} - -/* - * Print an error message. We can assume that this is in response to - * an error return from {host,target}_read_memory. - */ -static void perror_memory(int status, bfd_vma memaddr, - struct disassemble_info *info) -{ - if (status != EIO) { - /* Can't happen. */ - info->fprintf_func(info->stream, "Unknown error %d\n", status); - } else { - /* Address between memaddr and memaddr + len was out of bounds. */ - info->fprintf_func(info->stream, - "Address 0x%" PRIx64 " is out of bounds.\n", - memaddr); - } -} - -/* Print address in hex. */ -static void print_address(bfd_vma addr, struct disassemble_info *info) -{ - info->fprintf_func(info->stream, "0x%" PRIx64, addr); -} - -/* Print address in hex, truncated to the width of a host virtual address. */ -static void host_print_address(bfd_vma addr, struct disassemble_info *info) -{ - print_address((uintptr_t)addr, info); -} - -/* Stub prevents some fruitless earching in optabs disassemblers. */ -static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) -{ - return 1; -} - -static int print_insn_objdump(bfd_vma pc, disassemble_info *info, - const char *prefix) -{ - int i, n = info->buffer_length; - g_autofree uint8_t *buf = g_malloc(n); - - 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]); - } - } else { - info->fprintf_func(info->stream, "unable to read memory"); - } - return n; -} - -static int print_insn_od_host(bfd_vma pc, disassemble_info *info) -{ - return print_insn_objdump(pc, info, "OBJD-H"); -} - -static int print_insn_od_target(bfd_vma pc, disassemble_info *info) -{ - return print_insn_objdump(pc, info, "OBJD-T"); -} - -static void initialize_debug(CPUDebug *s) -{ - memset(s, 0, sizeof(*s)); - s->info.arch = bfd_arch_unknown; - s->info.cap_arch = -1; - s->info.cap_insn_unit = 4; - s->info.cap_insn_split = 4; - s->info.memory_error_func = perror_memory; - s->info.symbol_at_address_func = symbol_at_address; -} - -static void 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 - - CPUClass *cc = CPU_GET_CLASS(cpu); - if (cc->disas_set_info) { - cc->disas_set_info(cpu, &s->info); - } -} - -static void initialize_debug_host(CPUDebug *s) -{ - initialize_debug(s); - - s->info.read_memory_func = host_read_memory; - s->info.print_address_func = host_print_address; -#if HOST_BIG_ENDIAN - s->info.endian = BFD_ENDIAN_BIG; -#else - s->info.endian = BFD_ENDIAN_LITTLE; -#endif -#if defined(CONFIG_TCG_INTERPRETER) - s->info.print_insn = print_insn_tci; -#elif defined(__i386__) - s->info.mach = bfd_mach_i386_i386; - s->info.cap_arch = CS_ARCH_X86; - s->info.cap_mode = CS_MODE_32; - s->info.cap_insn_unit = 1; - s->info.cap_insn_split = 8; -#elif defined(__x86_64__) - s->info.mach = bfd_mach_x86_64; - s->info.cap_arch = CS_ARCH_X86; - s->info.cap_mode = CS_MODE_64; - s->info.cap_insn_unit = 1; - s->info.cap_insn_split = 8; -#elif defined(_ARCH_PPC) - s->info.cap_arch = CS_ARCH_PPC; -# ifdef _ARCH_PPC64 - s->info.cap_mode = CS_MODE_64; -# endif -#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) -#if defined(_ILP32) || (__riscv_xlen == 32) - s->info.print_insn = print_insn_riscv32; -#elif defined(_LP64) - s->info.print_insn = print_insn_riscv64; -#else -#error unsupported RISC-V ABI -#endif -#elif defined(__aarch64__) - s->info.cap_arch = CS_ARCH_ARM64; -#elif defined(__alpha__) - s->info.print_insn = print_insn_alpha; -#elif defined(__sparc__) - s->info.print_insn = print_insn_sparc; - s->info.mach = bfd_mach_sparc_v9b; -#elif defined(__arm__) - /* TCG only generates code for arm mode. */ - s->info.cap_arch = CS_ARCH_ARM; -#elif defined(__MIPSEB__) - s->info.print_insn = print_insn_big_mips; -#elif defined(__MIPSEL__) - s->info.print_insn = print_insn_little_mips; -#elif defined(__m68k__) - s->info.print_insn = print_insn_m68k; -#elif defined(__s390__) - s->info.cap_arch = CS_ARCH_SYSZ; - s->info.cap_insn_unit = 2; - s->info.cap_insn_split = 6; -#elif defined(__hppa__) - s->info.print_insn = print_insn_hppa; -#endif -} - -/* Disassemble this for me please... (debugging). */ -void target_disas(FILE *out, CPUState *cpu, target_ulong code, - target_ulong size) -{ - target_ulong pc; - int count; - CPUDebug s; - - initialize_debug_target(&s, cpu); - s.info.fprintf_func = fprintf; - s.info.stream = out; - s.info.buffer_vma = code; - s.info.buffer_length = size; - - if (s.info.cap_arch >= 0 && cap_disas_target(&s.info, code, size)) { - return; - } - - if (s.info.print_insn == NULL) { - s.info.print_insn = print_insn_od_target; - } - - 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; - if (size < count) { - fprintf(out, - "Disassembler disagrees with translator over instruction " - "decoding\n" - "Please report this to qemu-devel@nongnu.org\n"); - break; - } - } -} - -static int gstring_printf(FILE *stream, const char *fmt, ...) -{ - /* We abuse the FILE parameter to pass a GString. */ - GString *s = (GString *)stream; - int initial_len = s->len; - va_list va; - - va_start(va, fmt); - g_string_append_vprintf(s, fmt, va); - va_end(va); - - return s->len - initial_len; -} - -static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) -{ - /* does nothing */ -} - - -/* - * We should only be dissembling one instruction at a time here. If - * there is left over it usually indicates the front end has read more - * bytes than it needed. - */ -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 = gstring_printf; - s.info.stream = (FILE *)ds; /* abuse this slot */ - s.info.buffer_vma = addr; - s.info.buffer_length = size; - s.info.print_address_func = plugin_print_address; - - if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { - ; /* done */ - } else if (s.info.print_insn) { - s.info.print_insn(addr, &s.info); - } else { - ; /* cannot disassemble -- return empty string */ - } - - /* Return the buffer, freeing the GString container. */ - return g_string_free(ds, false); -} - -/* Disassemble this for me please... (debugging). */ -void disas(FILE *out, const void *code, unsigned long size) -{ - uintptr_t pc; - int count; - CPUDebug s; - - initialize_debug_host(&s); - s.info.fprintf_func = fprintf; - s.info.stream = out; - s.info.buffer = code; - s.info.buffer_vma = (uintptr_t)code; - s.info.buffer_length = size; - - if (s.info.cap_arch >= 0 && cap_disas_host(&s.info, code, size)) { - return; - } - - if (s.info.print_insn == NULL) { - s.info.print_insn = print_insn_od_host; - } - for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { - fprintf(out, "0x%08" PRIxPTR ": ", pc); - count = s.info.print_insn(pc, &s.info); - fprintf(out, "\n"); - if (count < 0) { - break; - } - } - -} - -/* Look up symbol for debugging purpose. Returns "" if unknown. */ -const char *lookup_symbol(target_ulong orig_addr) -{ - const char *symbol = ""; - struct syminfo *s; - - for (s = syminfos; s; s = s->next) { - symbol = s->lookup_symbol(s, orig_addr); - if (symbol[0] != '\0') { - break; - } - } - - 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; - g_autoptr(GString) ds = g_string_new(""); - - initialize_debug_target(&s, cpu); - s.info.fprintf_func = gstring_printf; - s.info.stream = (FILE *)ds; /* abuse this slot */ - - 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" TARGET_FMT_lx - ": Asm output not supported on this arch\n", pc); - return; - } - - for (i = 0; i < nb_insn; i++) { - g_string_append_printf(ds, "0x" TARGET_FMT_lx ": ", 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); -} -#endif diff --git a/disas/cris.c b/disas/cris.c deleted file mode 100644 index 0b0a3fb916..0000000000 --- a/disas/cris.c +++ /dev/null @@ -1,2857 +0,0 @@ -/* Disassembler code for CRIS. - Copyright 2000, 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc. - Contributed by Axis Communications AB, Lund, Sweden. - Written by Hans-Peter Nilsson. - - This file is part of the GNU binutils and GDB, the GNU debugger. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any later - version. - - 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 "target/cris/opcode-cris.h" - -#define CONST_STRNEQ(STR1,STR2) (strncmp ((STR1), (STR2), sizeof (STR2) - 1) == 0) - -/* cris-opc.c -- Table of opcodes for the CRIS processor. - Copyright 2000, 2001, 2004 Free Software Foundation, Inc. - Contributed by Axis Communications AB, Lund, Sweden. - Originally written for GAS 1.38.1 by Mikael Asker. - Reorganized by Hans-Peter Nilsson. - -This file is part of GAS, GDB and the GNU binutils. - -GAS, GDB, and GNU binutils is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2, or (at your -option) any later version. - -GAS, GDB, and GNU binutils are distributed in the hope that they 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 NULL -#define NULL (0) -#endif - -/* This table isn't used for CRISv32 and the size of immediate operands. */ -const struct cris_spec_reg -cris_spec_regs[] = -{ - {"bz", 0, 1, cris_ver_v32p, NULL}, - {"p0", 0, 1, 0, NULL}, - {"vr", 1, 1, 0, NULL}, - {"p1", 1, 1, 0, NULL}, - {"pid", 2, 1, cris_ver_v32p, NULL}, - {"p2", 2, 1, cris_ver_v32p, NULL}, - {"p2", 2, 1, cris_ver_warning, NULL}, - {"srs", 3, 1, cris_ver_v32p, NULL}, - {"p3", 3, 1, cris_ver_v32p, NULL}, - {"p3", 3, 1, cris_ver_warning, NULL}, - {"wz", 4, 2, cris_ver_v32p, NULL}, - {"p4", 4, 2, 0, NULL}, - {"ccr", 5, 2, cris_ver_v0_10, NULL}, - {"exs", 5, 4, cris_ver_v32p, NULL}, - {"p5", 5, 2, cris_ver_v0_10, NULL}, - {"p5", 5, 4, cris_ver_v32p, NULL}, - {"dcr0",6, 2, cris_ver_v0_3, NULL}, - {"eda", 6, 4, cris_ver_v32p, NULL}, - {"p6", 6, 2, cris_ver_v0_3, NULL}, - {"p6", 6, 4, cris_ver_v32p, NULL}, - {"dcr1/mof", 7, 4, cris_ver_v10p, - "Register `dcr1/mof' with ambiguous size specified. Guessing 4 bytes"}, - {"dcr1/mof", 7, 2, cris_ver_v0_3, - "Register `dcr1/mof' with ambiguous size specified. Guessing 2 bytes"}, - {"mof", 7, 4, cris_ver_v10p, NULL}, - {"dcr1",7, 2, cris_ver_v0_3, NULL}, - {"p7", 7, 4, cris_ver_v10p, NULL}, - {"p7", 7, 2, cris_ver_v0_3, NULL}, - {"dz", 8, 4, cris_ver_v32p, NULL}, - {"p8", 8, 4, 0, NULL}, - {"ibr", 9, 4, cris_ver_v0_10, NULL}, - {"ebp", 9, 4, cris_ver_v32p, NULL}, - {"p9", 9, 4, 0, NULL}, - {"irp", 10, 4, cris_ver_v0_10, NULL}, - {"erp", 10, 4, cris_ver_v32p, NULL}, - {"p10", 10, 4, 0, NULL}, - {"srp", 11, 4, 0, NULL}, - {"p11", 11, 4, 0, NULL}, - /* For disassembly use only. Accept at assembly with a warning. */ - {"bar/dtp0", 12, 4, cris_ver_warning, - "Ambiguous register `bar/dtp0' specified"}, - {"nrp", 12, 4, cris_ver_v32p, NULL}, - {"bar", 12, 4, cris_ver_v8_10, NULL}, - {"dtp0",12, 4, cris_ver_v0_3, NULL}, - {"p12", 12, 4, 0, NULL}, - /* For disassembly use only. Accept at assembly with a warning. */ - {"dccr/dtp1",13, 4, cris_ver_warning, - "Ambiguous register `dccr/dtp1' specified"}, - {"ccs", 13, 4, cris_ver_v32p, NULL}, - {"dccr",13, 4, cris_ver_v8_10, NULL}, - {"dtp1",13, 4, cris_ver_v0_3, NULL}, - {"p13", 13, 4, 0, NULL}, - {"brp", 14, 4, cris_ver_v3_10, NULL}, - {"usp", 14, 4, cris_ver_v32p, NULL}, - {"p14", 14, 4, cris_ver_v3p, NULL}, - {"usp", 15, 4, cris_ver_v10, NULL}, - {"spc", 15, 4, cris_ver_v32p, NULL}, - {"p15", 15, 4, cris_ver_v10p, NULL}, - {NULL, 0, 0, cris_ver_version_all, NULL} -}; - -/* Add version specifiers to this table when necessary. - The (now) regular coding of register names suggests a simpler - implementation. */ -const struct cris_support_reg cris_support_regs[] = -{ - {"s0", 0}, - {"s1", 1}, - {"s2", 2}, - {"s3", 3}, - {"s4", 4}, - {"s5", 5}, - {"s6", 6}, - {"s7", 7}, - {"s8", 8}, - {"s9", 9}, - {"s10", 10}, - {"s11", 11}, - {"s12", 12}, - {"s13", 13}, - {"s14", 14}, - {"s15", 15}, - {NULL, 0} -}; - -/* All CRIS opcodes are 16 bits. - - - The match component is a mask saying which bits must match a - particular opcode in order for an instruction to be an instance - of that opcode. - - - The args component is a string containing characters symbolically - matching the operands of an instruction. Used for both assembly - and disassembly. - - Operand-matching characters: - [ ] , space - Verbatim. - A The string "ACR" (case-insensitive). - B Not really an operand. It causes a "BDAP -size,SP" prefix to be - output for the PUSH alias-instructions and recognizes a push- - prefix at disassembly. This letter isn't recognized for v32. - Must be followed by a R or P letter. - ! Non-match pattern, will not match if there's a prefix insn. - b Non-matching operand, used for branches with 16-bit - displacement. Only recognized by the disassembler. - c 5-bit unsigned immediate in bits <4:0>. - C 4-bit unsigned immediate in bits <3:0>. - d At assembly, optionally (as in put other cases before this one) - ".d" or ".D" at the start of the operands, followed by one space - character. At disassembly, nothing. - D General register in bits <15:12> and <3:0>. - f List of flags in bits <15:12> and <3:0>. - i 6-bit signed immediate in bits <5:0>. - I 6-bit unsigned immediate in bits <5:0>. - M Size modifier (B, W or D) for CLEAR instructions. - m Size modifier (B, W or D) in bits <5:4> - N A 32-bit dword, like in the difference between s and y. - This has no effect on bits in the opcode. Can also be expressed - as "[pc+]" in input. - n As N, but PC-relative (to the start of the instruction). - o [-128..127] word offset in bits <7:1> and <0>. Used by 8-bit - branch instructions. - O [-128..127] offset in bits <7:0>. Also matches a comma and a - general register after the expression, in bits <15:12>. Used - only for the BDAP prefix insn (in v32 the ADDOQ insn; same opcode). - P Special register in bits <15:12>. - p Indicates that the insn is a prefix insn. Must be first - character. - Q As O, but don't relax; force an 8-bit offset. - R General register in bits <15:12>. - r General register in bits <3:0>. - S Source operand in bit <10> and a prefix; a 3-operand prefix - without side-effect. - s Source operand in bits <10> and <3:0>, optionally with a - side-effect prefix, except [pc] (the name, not R15 as in ACR) - isn't allowed for v32 and higher. - T Support register in bits <15:12>. - u 4-bit (PC-relative) unsigned immediate word offset in bits <3:0>. - U Relaxes to either u or n, instruction is assumed LAPCQ or LAPC. - Not recognized at disassembly. - x Register-dot-modifier, for example "r5.w" in bits <15:12> and <5:4>. - y Like 's' but do not allow an integer at assembly. - Y The difference s-y; only an integer is allowed. - z Size modifier (B or W) in bit <4>. */ - - -/* Please note the order of the opcodes in this table is significant. - The assembler requires that all instances of the same mnemonic must - be consecutive. If they aren't, the assembler might not recognize - them, or may indicate an internal error. - - The disassembler should not normally care about the order of the - opcodes, but will prefer an earlier alternative if the "match-score" - (see cris-dis.c) is computed as equal. - - It should not be significant for proper execution that this table is - in alphabetical order, but please follow that convention for an easy - overview. */ - -const struct cris_opcode -cris_opcodes[] = -{ - {"abs", 0x06B0, 0x0940, "r,R", 0, SIZE_NONE, 0, - cris_abs_op}, - - {"add", 0x0600, 0x09c0, "m r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - {"add", 0x0A00, 0x01c0, "m s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"add", 0x0A00, 0x01c0, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"add", 0x0a00, 0x05c0, "m S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"add", 0x0A00, 0x01c0, "m s,R", 0, SIZE_FIELD, - cris_ver_v32p, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"addc", 0x0570, 0x0A80, "r,R", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_not_implemented_op}, - - {"addc", 0x09A0, 0x0250, "s,R", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_not_implemented_op}, - - {"addi", 0x0540, 0x0A80, "x,r,A", 0, SIZE_NONE, - cris_ver_v32p, - cris_addi_op}, - - {"addi", 0x0500, 0x0Ac0, "x,r", 0, SIZE_NONE, 0, - cris_addi_op}, - - /* This collates after "addo", but we want to disassemble as "addoq", - not "addo". */ - {"addoq", 0x0100, 0x0E00, "Q,A", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"addo", 0x0940, 0x0280, "m s,R,A", 0, SIZE_FIELD_SIGNED, - cris_ver_v32p, - cris_not_implemented_op}, - - /* This must be located after the insn above, lest we misinterpret - "addo.b -1,r0,acr" as "addo .b-1,r0,acr". FIXME: Sounds like a - parser bug. */ - {"addo", 0x0100, 0x0E00, "O,A", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"addq", 0x0200, 0x0Dc0, "I,R", 0, SIZE_NONE, 0, - cris_quick_mode_add_sub_op}, - - {"adds", 0x0420, 0x0Bc0, "z r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ - {"adds", 0x0820, 0x03c0, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"adds", 0x0820, 0x03c0, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"adds", 0x0820, 0x07c0, "z S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"addu", 0x0400, 0x0be0, "z r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ - {"addu", 0x0800, 0x03e0, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"addu", 0x0800, 0x03e0, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"addu", 0x0800, 0x07e0, "z S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"and", 0x0700, 0x08C0, "m r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - {"and", 0x0B00, 0x00C0, "m s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"and", 0x0B00, 0x00C0, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"and", 0x0B00, 0x04C0, "m S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"andq", 0x0300, 0x0CC0, "i,R", 0, SIZE_NONE, 0, - cris_quick_mode_and_cmp_move_or_op}, - - {"asr", 0x0780, 0x0840, "m r,R", 0, SIZE_NONE, 0, - cris_asr_op}, - - {"asrq", 0x03a0, 0x0c40, "c,R", 0, SIZE_NONE, 0, - cris_asrq_op}, - - {"ax", 0x15B0, 0xEA4F, "", 0, SIZE_NONE, 0, - cris_ax_ei_setf_op}, - - /* FIXME: Should use branch #defines. */ - {"b", 0x0dff, 0x0200, "b", 1, SIZE_NONE, 0, - cris_sixteen_bit_offset_branch_op}, - - {"ba", - BA_QUICK_OPCODE, - 0x0F00+(0xF-CC_A)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - /* Needs to come after the usual "ba o", which might be relaxed to - this one. */ - {"ba", BA_DWORD_OPCODE, - 0xffff & (~BA_DWORD_OPCODE), "n", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"bas", 0x0EBF, 0x0140, "n,P", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"basc", 0x0EFF, 0x0100, "n,P", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"bcc", - BRANCH_QUICK_OPCODE+CC_CC*0x1000, - 0x0f00+(0xF-CC_CC)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bcs", - BRANCH_QUICK_OPCODE+CC_CS*0x1000, - 0x0f00+(0xF-CC_CS)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bdap", - BDAP_INDIR_OPCODE, BDAP_INDIR_Z_BITS, "pm s,R", 0, SIZE_FIELD_SIGNED, - cris_ver_v0_10, - cris_bdap_prefix}, - - {"bdap", - BDAP_QUICK_OPCODE, BDAP_QUICK_Z_BITS, "pO", 0, SIZE_NONE, - cris_ver_v0_10, - cris_quick_mode_bdap_prefix}, - - {"beq", - BRANCH_QUICK_OPCODE+CC_EQ*0x1000, - 0x0f00+(0xF-CC_EQ)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - /* This is deliberately put before "bext" to trump it, even though not - in alphabetical order, since we don't do excluding version checks - for v0..v10. */ - {"bwf", - BRANCH_QUICK_OPCODE+CC_EXT*0x1000, - 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE, - cris_ver_v10, - cris_eight_bit_offset_branch_op}, - - {"bext", - BRANCH_QUICK_OPCODE+CC_EXT*0x1000, - 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE, - cris_ver_v0_3, - cris_eight_bit_offset_branch_op}, - - {"bge", - BRANCH_QUICK_OPCODE+CC_GE*0x1000, - 0x0f00+(0xF-CC_GE)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bgt", - BRANCH_QUICK_OPCODE+CC_GT*0x1000, - 0x0f00+(0xF-CC_GT)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bhi", - BRANCH_QUICK_OPCODE+CC_HI*0x1000, - 0x0f00+(0xF-CC_HI)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bhs", - BRANCH_QUICK_OPCODE+CC_HS*0x1000, - 0x0f00+(0xF-CC_HS)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"biap", BIAP_OPCODE, BIAP_Z_BITS, "pm r,R", 0, SIZE_NONE, - cris_ver_v0_10, - cris_biap_prefix}, - - {"ble", - BRANCH_QUICK_OPCODE+CC_LE*0x1000, - 0x0f00+(0xF-CC_LE)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"blo", - BRANCH_QUICK_OPCODE+CC_LO*0x1000, - 0x0f00+(0xF-CC_LO)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bls", - BRANCH_QUICK_OPCODE+CC_LS*0x1000, - 0x0f00+(0xF-CC_LS)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"blt", - BRANCH_QUICK_OPCODE+CC_LT*0x1000, - 0x0f00+(0xF-CC_LT)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bmi", - BRANCH_QUICK_OPCODE+CC_MI*0x1000, - 0x0f00+(0xF-CC_MI)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bmod", 0x0ab0, 0x0140, "s,R", 0, SIZE_FIX_32, - cris_ver_sim_v0_10, - cris_not_implemented_op}, - - {"bmod", 0x0ab0, 0x0140, "S,D", 0, SIZE_NONE, - cris_ver_sim_v0_10, - cris_not_implemented_op}, - - {"bmod", 0x0ab0, 0x0540, "S,R,r", 0, SIZE_NONE, - cris_ver_sim_v0_10, - cris_not_implemented_op}, - - {"bne", - BRANCH_QUICK_OPCODE+CC_NE*0x1000, - 0x0f00+(0xF-CC_NE)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bound", 0x05c0, 0x0A00, "m r,R", 0, SIZE_NONE, 0, - cris_two_operand_bound_op}, - /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ - {"bound", 0x09c0, 0x0200, "m s,R", 0, SIZE_FIELD, - cris_ver_v0_10, - cris_two_operand_bound_op}, - /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ - {"bound", 0x0dcf, 0x0200, "m Y,R", 0, SIZE_FIELD, 0, - cris_two_operand_bound_op}, - {"bound", 0x09c0, 0x0200, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_two_operand_bound_op}, - {"bound", 0x09c0, 0x0600, "m S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_bound_op}, - - {"bpl", - BRANCH_QUICK_OPCODE+CC_PL*0x1000, - 0x0f00+(0xF-CC_PL)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"break", 0xe930, 0x16c0, "C", 0, SIZE_NONE, - cris_ver_v3p, - cris_break_op}, - - {"bsb", - BRANCH_QUICK_OPCODE+CC_EXT*0x1000, - 0x0f00+(0xF-CC_EXT)*0x1000, "o", 1, SIZE_NONE, - cris_ver_v32p, - cris_eight_bit_offset_branch_op}, - - {"bsr", 0xBEBF, 0x4140, "n", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"bsrc", 0xBEFF, 0x4100, "n", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"bstore", 0x0af0, 0x0100, "s,R", 0, SIZE_FIX_32, - cris_ver_warning, - cris_not_implemented_op}, - - {"bstore", 0x0af0, 0x0100, "S,D", 0, SIZE_NONE, - cris_ver_warning, - cris_not_implemented_op}, - - {"bstore", 0x0af0, 0x0500, "S,R,r", 0, SIZE_NONE, - cris_ver_warning, - cris_not_implemented_op}, - - {"btst", 0x04F0, 0x0B00, "r,R", 0, SIZE_NONE, 0, - cris_btst_nop_op}, - {"btstq", 0x0380, 0x0C60, "c,R", 0, SIZE_NONE, 0, - cris_btst_nop_op}, - - {"bvc", - BRANCH_QUICK_OPCODE+CC_VC*0x1000, - 0x0f00+(0xF-CC_VC)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"bvs", - BRANCH_QUICK_OPCODE+CC_VS*0x1000, - 0x0f00+(0xF-CC_VS)*0x1000, "o", 1, SIZE_NONE, 0, - cris_eight_bit_offset_branch_op}, - - {"clear", 0x0670, 0x3980, "M r", 0, SIZE_NONE, 0, - cris_reg_mode_clear_op}, - - {"clear", 0x0A70, 0x3180, "M y", 0, SIZE_NONE, 0, - cris_none_reg_mode_clear_test_op}, - - {"clear", 0x0A70, 0x3180, "M S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_clear_test_op}, - - {"clearf", 0x05F0, 0x0A00, "f", 0, SIZE_NONE, 0, - cris_clearf_di_op}, - - {"cmp", 0x06C0, 0x0900, "m r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - {"cmp", 0x0Ac0, 0x0100, "m s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"cmp", 0x0Ac0, 0x0100, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"cmpq", 0x02C0, 0x0D00, "i,R", 0, SIZE_NONE, 0, - cris_quick_mode_and_cmp_move_or_op}, - - /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ - {"cmps", 0x08e0, 0x0300, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"cmps", 0x08e0, 0x0300, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ - {"cmpu", 0x08c0, 0x0320, "z s,R" , 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"cmpu", 0x08c0, 0x0320, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"di", 0x25F0, 0xDA0F, "", 0, SIZE_NONE, 0, - cris_clearf_di_op}, - - {"dip", DIP_OPCODE, DIP_Z_BITS, "ps", 0, SIZE_FIX_32, - cris_ver_v0_10, - cris_dip_prefix}, - - {"div", 0x0980, 0x0640, "m R,r", 0, SIZE_FIELD, 0, - cris_not_implemented_op}, - - {"dstep", 0x06f0, 0x0900, "r,R", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"ei", 0x25B0, 0xDA4F, "", 0, SIZE_NONE, 0, - cris_ax_ei_setf_op}, - - {"fidxd", 0x0ab0, 0xf540, "[r]", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"fidxi", 0x0d30, 0xF2C0, "[r]", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"ftagd", 0x1AB0, 0xE540, "[r]", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"ftagi", 0x1D30, 0xE2C0, "[r]", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"halt", 0xF930, 0x06CF, "", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"jas", 0x09B0, 0x0640, "r,P", 0, SIZE_NONE, - cris_ver_v32p, - cris_reg_mode_jump_op}, - - {"jas", 0x0DBF, 0x0240, "N,P", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_reg_mode_jump_op}, - - {"jasc", 0x0B30, 0x04C0, "r,P", 0, SIZE_NONE, - cris_ver_v32p, - cris_reg_mode_jump_op}, - - {"jasc", 0x0F3F, 0x00C0, "N,P", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_reg_mode_jump_op}, - - {"jbrc", 0x69b0, 0x9640, "r", 0, SIZE_NONE, - cris_ver_v8_10, - cris_reg_mode_jump_op}, - - {"jbrc", 0x6930, 0x92c0, "s", 0, SIZE_FIX_32, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jbrc", 0x6930, 0x92c0, "S", 0, SIZE_NONE, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jir", 0xA9b0, 0x5640, "r", 0, SIZE_NONE, - cris_ver_v8_10, - cris_reg_mode_jump_op}, - - {"jir", 0xA930, 0x52c0, "s", 0, SIZE_FIX_32, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jir", 0xA930, 0x52c0, "S", 0, SIZE_NONE, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jirc", 0x29b0, 0xd640, "r", 0, SIZE_NONE, - cris_ver_v8_10, - cris_reg_mode_jump_op}, - - {"jirc", 0x2930, 0xd2c0, "s", 0, SIZE_FIX_32, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jirc", 0x2930, 0xd2c0, "S", 0, SIZE_NONE, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jsr", 0xB9b0, 0x4640, "r", 0, SIZE_NONE, 0, - cris_reg_mode_jump_op}, - - {"jsr", 0xB930, 0x42c0, "s", 0, SIZE_FIX_32, - cris_ver_v0_10, - cris_none_reg_mode_jump_op}, - - {"jsr", 0xBDBF, 0x4240, "N", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"jsr", 0xB930, 0x42c0, "S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_jump_op}, - - {"jsrc", 0x39b0, 0xc640, "r", 0, SIZE_NONE, - cris_ver_v8_10, - cris_reg_mode_jump_op}, - - {"jsrc", 0x3930, 0xc2c0, "s", 0, SIZE_FIX_32, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jsrc", 0x3930, 0xc2c0, "S", 0, SIZE_NONE, - cris_ver_v8_10, - cris_none_reg_mode_jump_op}, - - {"jsrc", 0xBB30, 0x44C0, "r", 0, SIZE_NONE, - cris_ver_v32p, - cris_reg_mode_jump_op}, - - {"jsrc", 0xBF3F, 0x40C0, "N", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_reg_mode_jump_op}, - - {"jump", 0x09b0, 0xF640, "r", 0, SIZE_NONE, 0, - cris_reg_mode_jump_op}, - - {"jump", - JUMP_INDIR_OPCODE, JUMP_INDIR_Z_BITS, "s", 0, SIZE_FIX_32, - cris_ver_v0_10, - cris_none_reg_mode_jump_op}, - - {"jump", - JUMP_INDIR_OPCODE, JUMP_INDIR_Z_BITS, "S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_jump_op}, - - {"jump", 0x09F0, 0x060F, "P", 0, SIZE_NONE, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"jump", - JUMP_PC_INCR_OPCODE_V32, - (0xffff & ~JUMP_PC_INCR_OPCODE_V32), "N", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_none_reg_mode_jump_op}, - - {"jmpu", 0x8930, 0x72c0, "s", 0, SIZE_FIX_32, - cris_ver_v10, - cris_none_reg_mode_jump_op}, - - {"jmpu", 0x8930, 0x72c0, "S", 0, SIZE_NONE, - cris_ver_v10, - cris_none_reg_mode_jump_op}, - - {"lapc", 0x0970, 0x0680, "U,R", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"lapc", 0x0D7F, 0x0280, "dn,R", 0, SIZE_FIX_32, - cris_ver_v32p, - cris_not_implemented_op}, - - {"lapcq", 0x0970, 0x0680, "u,R", 0, SIZE_NONE, - cris_ver_v32p, - cris_addi_op}, - - {"lsl", 0x04C0, 0x0B00, "m r,R", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"lslq", 0x03c0, 0x0C20, "c,R", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"lsr", 0x07C0, 0x0800, "m r,R", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"lsrq", 0x03e0, 0x0C00, "c,R", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"lz", 0x0730, 0x08C0, "r,R", 0, SIZE_NONE, - cris_ver_v3p, - cris_not_implemented_op}, - - {"mcp", 0x07f0, 0x0800, "P,r", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"move", 0x0640, 0x0980, "m r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - {"move", 0x0A40, 0x0180, "m s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"move", 0x0A40, 0x0180, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"move", 0x0630, 0x09c0, "r,P", 0, SIZE_NONE, 0, - cris_move_to_preg_op}, - - {"move", 0x0670, 0x0980, "P,r", 0, SIZE_NONE, 0, - cris_reg_mode_move_from_preg_op}, - - {"move", 0x0BC0, 0x0000, "m R,y", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"move", 0x0BC0, 0x0000, "m D,S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"move", - MOVE_M_TO_PREG_OPCODE, MOVE_M_TO_PREG_ZBITS, - "s,P", 0, SIZE_SPEC_REG, 0, - cris_move_to_preg_op}, - - {"move", 0x0A30, 0x01c0, "S,P", 0, SIZE_NONE, - cris_ver_v0_10, - cris_move_to_preg_op}, - - {"move", 0x0A70, 0x0180, "P,y", 0, SIZE_SPEC_REG, 0, - cris_none_reg_mode_move_from_preg_op}, - - {"move", 0x0A70, 0x0180, "P,S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_move_from_preg_op}, - - {"move", 0x0B70, 0x0480, "r,T", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"move", 0x0F70, 0x0080, "T,r", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"movem", 0x0BF0, 0x0000, "R,y", 0, SIZE_FIX_32, 0, - cris_move_reg_to_mem_movem_op}, - - {"movem", 0x0BF0, 0x0000, "D,S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_move_reg_to_mem_movem_op}, - - {"movem", 0x0BB0, 0x0040, "s,R", 0, SIZE_FIX_32, 0, - cris_move_mem_to_reg_movem_op}, - - {"movem", 0x0BB0, 0x0040, "S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_move_mem_to_reg_movem_op}, - - {"moveq", 0x0240, 0x0D80, "i,R", 0, SIZE_NONE, 0, - cris_quick_mode_and_cmp_move_or_op}, - - {"movs", 0x0460, 0x0B80, "z r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ - {"movs", 0x0860, 0x0380, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"movs", 0x0860, 0x0380, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"movu", 0x0440, 0x0Ba0, "z r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ - {"movu", 0x0840, 0x03a0, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"movu", 0x0840, 0x03a0, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"mstep", 0x07f0, 0x0800, "r,R", 0, SIZE_NONE, - cris_ver_v0_10, - cris_dstep_logshift_mstep_neg_not_op}, - - {"muls", 0x0d00, 0x02c0, "m r,R", 0, SIZE_NONE, - cris_ver_v10p, - cris_muls_op}, - - {"mulu", 0x0900, 0x06c0, "m r,R", 0, SIZE_NONE, - cris_ver_v10p, - cris_mulu_op}, - - {"neg", 0x0580, 0x0A40, "m r,R", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"nop", NOP_OPCODE, NOP_Z_BITS, "", 0, SIZE_NONE, - cris_ver_v0_10, - cris_btst_nop_op}, - - {"nop", NOP_OPCODE_V32, NOP_Z_BITS_V32, "", 0, SIZE_NONE, - cris_ver_v32p, - cris_btst_nop_op}, - - {"not", 0x8770, 0x7880, "r", 0, SIZE_NONE, 0, - cris_dstep_logshift_mstep_neg_not_op}, - - {"or", 0x0740, 0x0880, "m r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - {"or", 0x0B40, 0x0080, "m s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"or", 0x0B40, 0x0080, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"or", 0x0B40, 0x0480, "m S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"orq", 0x0340, 0x0C80, "i,R", 0, SIZE_NONE, 0, - cris_quick_mode_and_cmp_move_or_op}, - - {"pop", 0x0E6E, 0x0191, "!R", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"pop", 0x0e3e, 0x01c1, "!P", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_move_from_preg_op}, - - {"push", 0x0FEE, 0x0011, "BR", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"push", 0x0E7E, 0x0181, "BP", 0, SIZE_NONE, - cris_ver_v0_10, - cris_move_to_preg_op}, - - {"rbf", 0x3b30, 0xc0c0, "y", 0, SIZE_NONE, - cris_ver_v10, - cris_not_implemented_op}, - - {"rbf", 0x3b30, 0xc0c0, "S", 0, SIZE_NONE, - cris_ver_v10, - cris_not_implemented_op}, - - {"rfe", 0x2930, 0xD6CF, "", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"rfg", 0x4930, 0xB6CF, "", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"rfn", 0x5930, 0xA6CF, "", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - {"ret", 0xB67F, 0x4980, "", 1, SIZE_NONE, - cris_ver_v0_10, - cris_reg_mode_move_from_preg_op}, - - {"ret", 0xB9F0, 0x460F, "", 1, SIZE_NONE, - cris_ver_v32p, - cris_reg_mode_move_from_preg_op}, - - {"retb", 0xe67f, 0x1980, "", 1, SIZE_NONE, - cris_ver_v0_10, - cris_reg_mode_move_from_preg_op}, - - {"rete", 0xA9F0, 0x560F, "", 1, SIZE_NONE, - cris_ver_v32p, - cris_reg_mode_move_from_preg_op}, - - {"reti", 0xA67F, 0x5980, "", 1, SIZE_NONE, - cris_ver_v0_10, - cris_reg_mode_move_from_preg_op}, - - {"retn", 0xC9F0, 0x360F, "", 1, SIZE_NONE, - cris_ver_v32p, - cris_reg_mode_move_from_preg_op}, - - {"sbfs", 0x3b70, 0xc080, "y", 0, SIZE_NONE, - cris_ver_v10, - cris_not_implemented_op}, - - {"sbfs", 0x3b70, 0xc080, "S", 0, SIZE_NONE, - cris_ver_v10, - cris_not_implemented_op}, - - {"sa", - 0x0530+CC_A*0x1000, - 0x0AC0+(0xf-CC_A)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"ssb", - 0x0530+CC_EXT*0x1000, - 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE, - cris_ver_v32p, - cris_scc_op}, - - {"scc", - 0x0530+CC_CC*0x1000, - 0x0AC0+(0xf-CC_CC)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"scs", - 0x0530+CC_CS*0x1000, - 0x0AC0+(0xf-CC_CS)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"seq", - 0x0530+CC_EQ*0x1000, - 0x0AC0+(0xf-CC_EQ)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"setf", 0x05b0, 0x0A40, "f", 0, SIZE_NONE, 0, - cris_ax_ei_setf_op}, - - {"sfe", 0x3930, 0xC6CF, "", 0, SIZE_NONE, - cris_ver_v32p, - cris_not_implemented_op}, - - /* Need to have "swf" in front of "sext" so it is the one displayed in - disassembly. */ - {"swf", - 0x0530+CC_EXT*0x1000, - 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE, - cris_ver_v10, - cris_scc_op}, - - {"sext", - 0x0530+CC_EXT*0x1000, - 0x0AC0+(0xf-CC_EXT)*0x1000, "r", 0, SIZE_NONE, - cris_ver_v0_3, - cris_scc_op}, - - {"sge", - 0x0530+CC_GE*0x1000, - 0x0AC0+(0xf-CC_GE)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"sgt", - 0x0530+CC_GT*0x1000, - 0x0AC0+(0xf-CC_GT)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"shi", - 0x0530+CC_HI*0x1000, - 0x0AC0+(0xf-CC_HI)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"shs", - 0x0530+CC_HS*0x1000, - 0x0AC0+(0xf-CC_HS)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"sle", - 0x0530+CC_LE*0x1000, - 0x0AC0+(0xf-CC_LE)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"slo", - 0x0530+CC_LO*0x1000, - 0x0AC0+(0xf-CC_LO)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"sls", - 0x0530+CC_LS*0x1000, - 0x0AC0+(0xf-CC_LS)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"slt", - 0x0530+CC_LT*0x1000, - 0x0AC0+(0xf-CC_LT)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"smi", - 0x0530+CC_MI*0x1000, - 0x0AC0+(0xf-CC_MI)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"sne", - 0x0530+CC_NE*0x1000, - 0x0AC0+(0xf-CC_NE)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"spl", - 0x0530+CC_PL*0x1000, - 0x0AC0+(0xf-CC_PL)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"sub", 0x0680, 0x0940, "m r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - {"sub", 0x0a80, 0x0140, "m s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"sub", 0x0a80, 0x0140, "m S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"sub", 0x0a80, 0x0540, "m S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"subq", 0x0280, 0x0d40, "I,R", 0, SIZE_NONE, 0, - cris_quick_mode_add_sub_op}, - - {"subs", 0x04a0, 0x0b40, "z r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_SIGNED and all necessary changes. */ - {"subs", 0x08a0, 0x0340, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"subs", 0x08a0, 0x0340, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"subs", 0x08a0, 0x0740, "z S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"subu", 0x0480, 0x0b60, "z r,R", 0, SIZE_NONE, 0, - cris_reg_mode_add_sub_cmp_and_or_move_op}, - - /* FIXME: SIZE_FIELD_UNSIGNED and all necessary changes. */ - {"subu", 0x0880, 0x0360, "z s,R", 0, SIZE_FIELD, 0, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"subu", 0x0880, 0x0360, "z S,D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_add_sub_cmp_and_or_move_op}, - - {"subu", 0x0880, 0x0760, "z S,R,r", 0, SIZE_NONE, - cris_ver_v0_10, - cris_three_operand_add_sub_cmp_and_or_op}, - - {"svc", - 0x0530+CC_VC*0x1000, - 0x0AC0+(0xf-CC_VC)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - {"svs", - 0x0530+CC_VS*0x1000, - 0x0AC0+(0xf-CC_VS)*0x1000, "r", 0, SIZE_NONE, 0, - cris_scc_op}, - - /* The insn "swapn" is the same as "not" and will be disassembled as - such, but the swap* family of mnmonics are generally v8-and-higher - only, so count it in. */ - {"swapn", 0x8770, 0x7880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapw", 0x4770, 0xb880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnw", 0xc770, 0x3880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapb", 0x2770, 0xd880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnb", 0xA770, 0x5880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapwb", 0x6770, 0x9880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnwb", 0xE770, 0x1880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapr", 0x1770, 0xe880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnr", 0x9770, 0x6880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapwr", 0x5770, 0xa880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnwr", 0xd770, 0x2880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapbr", 0x3770, 0xc880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnbr", 0xb770, 0x4880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapwbr", 0x7770, 0x8880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"swapnwbr", 0xf770, 0x0880, "r", 0, SIZE_NONE, - cris_ver_v8p, - cris_not_implemented_op}, - - {"test", 0x0640, 0x0980, "m D", 0, SIZE_NONE, - cris_ver_v0_10, - cris_reg_mode_test_op}, - - {"test", 0x0b80, 0xf040, "m y", 0, SIZE_FIELD, 0, - cris_none_reg_mode_clear_test_op}, - - {"test", 0x0b80, 0xf040, "m S", 0, SIZE_NONE, - cris_ver_v0_10, - cris_none_reg_mode_clear_test_op}, - - {"xor", 0x07B0, 0x0840, "r,R", 0, SIZE_NONE, 0, - cris_xor_op}, - - {NULL, 0, 0, NULL, 0, 0, 0, cris_not_implemented_op} -}; - -/* Condition-names, indexed by the CC_* numbers as found in cris.h. */ -const char * const -cris_cc_strings[] = -{ - "hs", - "lo", - "ne", - "eq", - "vc", - "vs", - "pl", - "mi", - "ls", - "hi", - "ge", - "lt", - "gt", - "le", - "a", - /* This is a placeholder. In v0, this would be "ext". In v32, this - is "sb". */ - "wf" -}; - -/* - * Local variables: - * eval: (c-set-style "gnu") - * indent-tabs-mode: t - * End: - */ - - -/* No instruction will be disassembled longer than this. In theory, and - in silicon, address prefixes can be cascaded. In practice, cascading - is not used by GCC, and not supported by the assembler. */ -#ifndef MAX_BYTES_PER_CRIS_INSN -#define MAX_BYTES_PER_CRIS_INSN 8 -#endif - -/* Whether or not to decode prefixes, folding it into the following - instruction. FIXME: Make this optional later. */ -#ifndef PARSE_PREFIX -#define PARSE_PREFIX 1 -#endif - -/* Sometimes we prefix all registers with this character. */ -#define REGISTER_PREFIX_CHAR '$' - -/* Whether or not to trace the following sequence: - sub* X,r%d - bound* Y,r%d - adds.w [pc+r%d.w],pc - - This is the assembly form of a switch-statement in C. - The "sub is optional. If there is none, then X will be zero. - X is the value of the first case, - Y is the number of cases (including default). - - This results in case offsets printed on the form: - case N: -> case_address - where N is an estimation on the corresponding 'case' operand in C, - and case_address is where execution of that case continues after the - sequence presented above. - - The old style of output was to print the offsets as instructions, - which made it hard to follow "case"-constructs in the disassembly, - and caused a lot of annoying warnings about undefined instructions. - - FIXME: Make this optional later. */ -#ifndef TRACE_CASE -#define TRACE_CASE (disdata->trace_case) -#endif - -enum cris_disass_family - { cris_dis_v0_v10, cris_dis_common_v10_v32, cris_dis_v32 }; - -/* Stored in the disasm_info->private_data member. */ -struct cris_disasm_data -{ - /* Whether to print something less confusing if we find something - matching a switch-construct. */ - bfd_boolean trace_case; - - /* Whether this code is flagged as crisv32. FIXME: Should be an enum - that includes "compatible". */ - enum cris_disass_family distype; -}; - -/* Value of first element in switch. */ -static long case_offset = 0; - -/* How many more case-offsets to print. */ -static long case_offset_counter = 0; - -/* Number of case offsets. */ -static long no_of_case_offsets = 0; - -/* Candidate for next case_offset. */ -static long last_immediate = 0; - -static int cris_constraint - (const char *, unsigned, unsigned, struct cris_disasm_data *); - -/* Parse disassembler options and store state in info. FIXME: For the - time being, we abuse static variables. */ - -static void -cris_parse_disassembler_options (struct cris_disasm_data *disdata, - char *disassembler_options, - enum cris_disass_family distype) -{ - /* Default true. */ - disdata->trace_case - = (disassembler_options == NULL - || (strcmp (disassembler_options, "nocase") != 0)); - - disdata->distype = distype; -} - -static const struct cris_spec_reg * -spec_reg_info (unsigned int sreg, enum cris_disass_family distype) -{ - int i; - - for (i = 0; cris_spec_regs[i].name != NULL; i++) - { - if (cris_spec_regs[i].number == sreg) - { - if (distype == cris_dis_v32) - switch (cris_spec_regs[i].applicable_version) - { - case cris_ver_warning: - case cris_ver_version_all: - case cris_ver_v3p: - case cris_ver_v8p: - case cris_ver_v10p: - case cris_ver_v32p: - /* No ambiguous sizes or register names with CRISv32. */ - if (cris_spec_regs[i].warning == NULL) - return &cris_spec_regs[i]; - default: - ; - } - else if (cris_spec_regs[i].applicable_version != cris_ver_v32p) - return &cris_spec_regs[i]; - } - } - - return NULL; -} - -/* Return the number of bits in the argument. */ - -static int -number_of_bits (unsigned int val) -{ - int bits; - - for (bits = 0; val != 0; val &= val - 1) - bits++; - - return bits; -} - -/* Get an entry in the opcode-table. */ - -static const struct cris_opcode * -get_opcode_entry (unsigned int insn, - unsigned int prefix_insn, - struct cris_disasm_data *disdata) -{ - /* For non-prefixed insns, we keep a table of pointers, indexed by the - insn code. Each entry is initialized when found to be NULL. */ - static const struct cris_opcode **opc_table = NULL; - - const struct cris_opcode *max_matchedp = NULL; - const struct cris_opcode **prefix_opc_table = NULL; - - /* We hold a table for each prefix that need to be handled differently. */ - static const struct cris_opcode **dip_prefixes = NULL; - static const struct cris_opcode **bdapq_m1_prefixes = NULL; - static const struct cris_opcode **bdapq_m2_prefixes = NULL; - static const struct cris_opcode **bdapq_m4_prefixes = NULL; - static const struct cris_opcode **rest_prefixes = NULL; - - /* Allocate and clear the opcode-table. */ - if (opc_table == NULL) - { - opc_table = g_new0(const struct cris_opcode *, 65536); - dip_prefixes = g_new0(const struct cris_opcode *, 65536); - bdapq_m1_prefixes = g_new0(const struct cris_opcode *, 65536); - bdapq_m2_prefixes = g_new0(const struct cris_opcode *, 65536); - bdapq_m4_prefixes = g_new0(const struct cris_opcode *, 65536); - rest_prefixes = g_new0(const struct cris_opcode *, 65536); - } - - /* Get the right table if this is a prefix. - This code is connected to cris_constraints in that it knows what - prefixes play a role in recognition of patterns; the necessary - state is reflected by which table is used. If constraints - involving match or non-match of prefix insns are changed, then this - probably needs changing too. */ - if (prefix_insn != NO_CRIS_PREFIX) - { - const struct cris_opcode *popcodep - = (opc_table[prefix_insn] != NULL - ? opc_table[prefix_insn] - : get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata)); - - if (popcodep == NULL) - return NULL; - - if (popcodep->match == BDAP_QUICK_OPCODE) - { - /* Since some offsets are recognized with "push" macros, we - have to have different tables for them. */ - int offset = (prefix_insn & 255); - - if (offset > 127) - offset -= 256; - - switch (offset) - { - case -4: - prefix_opc_table = bdapq_m4_prefixes; - break; - - case -2: - prefix_opc_table = bdapq_m2_prefixes; - break; - - case -1: - prefix_opc_table = bdapq_m1_prefixes; - break; - - default: - prefix_opc_table = rest_prefixes; - break; - } - } - else if (popcodep->match == DIP_OPCODE) - /* We don't allow postincrement when the prefix is DIP, so use a - different table for DIP. */ - prefix_opc_table = dip_prefixes; - else - prefix_opc_table = rest_prefixes; - } - - if (prefix_insn != NO_CRIS_PREFIX - && prefix_opc_table[insn] != NULL) - max_matchedp = prefix_opc_table[insn]; - else if (prefix_insn == NO_CRIS_PREFIX && opc_table[insn] != NULL) - max_matchedp = opc_table[insn]; - else - { - const struct cris_opcode *opcodep; - int max_level_of_match = -1; - - for (opcodep = cris_opcodes; - opcodep->name != NULL; - opcodep++) - { - int level_of_match; - - if (disdata->distype == cris_dis_v32) - { - switch (opcodep->applicable_version) - { - case cris_ver_version_all: - break; - - case cris_ver_v0_3: - case cris_ver_v0_10: - case cris_ver_v3_10: - case cris_ver_sim_v0_10: - case cris_ver_v8_10: - case cris_ver_v10: - case cris_ver_warning: - continue; - - case cris_ver_v3p: - case cris_ver_v8p: - case cris_ver_v10p: - case cris_ver_v32p: - break; - - case cris_ver_v8: - abort (); - default: - abort (); - } - } - else - { - switch (opcodep->applicable_version) - { - case cris_ver_version_all: - case cris_ver_v0_3: - case cris_ver_v3p: - case cris_ver_v0_10: - case cris_ver_v8p: - case cris_ver_v8_10: - case cris_ver_v10: - case cris_ver_sim_v0_10: - case cris_ver_v10p: - case cris_ver_warning: - break; - - case cris_ver_v32p: - continue; - - case cris_ver_v8: - abort (); - default: - abort (); - } - } - - /* We give a double lead for bits matching the template in - cris_opcodes. Not even, because then "move p8,r10" would - be given 2 bits lead over "clear.d r10". When there's a - tie, the first entry in the table wins. This is - deliberate, to avoid a more complicated recognition - formula. */ - if ((opcodep->match & insn) == opcodep->match - && (opcodep->lose & insn) == 0 - && ((level_of_match - = cris_constraint (opcodep->args, - insn, - prefix_insn, - disdata)) - >= 0) - && ((level_of_match - += 2 * number_of_bits (opcodep->match - | opcodep->lose)) - > max_level_of_match)) - { - max_matchedp = opcodep; - max_level_of_match = level_of_match; - - /* If there was a full match, never mind looking - further. */ - if (level_of_match >= 2 * 16) - break; - } - } - /* Fill in the new entry. - - If there are changes to the opcode-table involving prefixes, and - disassembly then does not work correctly, try removing the - else-clause below that fills in the prefix-table. If that - helps, you need to change the prefix_opc_table setting above, or - something related. */ - if (prefix_insn == NO_CRIS_PREFIX) - opc_table[insn] = max_matchedp; - else - prefix_opc_table[insn] = max_matchedp; - } - - return max_matchedp; -} - -/* Return -1 if the constraints of a bitwise-matched instruction say - that there is no match. Otherwise return a nonnegative number - indicating the confidence in the match (higher is better). */ - -static int -cris_constraint (const char *cs, - unsigned int insn, - unsigned int prefix_insn, - struct cris_disasm_data *disdata) -{ - int retval = 0; - int tmp; - int prefix_ok = 0; - const char *s; - - for (s = cs; *s; s++) - switch (*s) - { - case '!': - /* Do not recognize "pop" if there's a prefix and then only for - v0..v10. */ - if (prefix_insn != NO_CRIS_PREFIX - || disdata->distype != cris_dis_v0_v10) - return -1; - break; - - case 'U': - /* Not recognized at disassembly. */ - return -1; - - case 'M': - /* Size modifier for "clear", i.e. special register 0, 4 or 8. - Check that it is one of them. Only special register 12 could - be mismatched, but checking for matches is more logical than - checking for mismatches when there are only a few cases. */ - tmp = ((insn >> 12) & 0xf); - if (tmp != 0 && tmp != 4 && tmp != 8) - return -1; - break; - - case 'm': - if ((insn & 0x30) == 0x30) - return -1; - break; - - case 'S': - /* A prefix operand without side-effect. */ - if (prefix_insn != NO_CRIS_PREFIX && (insn & 0x400) == 0) - { - prefix_ok = 1; - break; - } - else - return -1; - - case 's': - case 'y': - case 'Y': - /* If this is a prefixed insn with postincrement (side-effect), - the prefix must not be DIP. */ - if (prefix_insn != NO_CRIS_PREFIX) - { - if (insn & 0x400) - { - const struct cris_opcode *prefix_opcodep - = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata); - - if (prefix_opcodep->match == DIP_OPCODE) - return -1; - } - - prefix_ok = 1; - } - break; - - case 'B': - /* If we don't fall through, then the prefix is ok. */ - prefix_ok = 1; - - /* A "push" prefix. Check for valid "push" size. - In case of special register, it may be != 4. */ - if (prefix_insn != NO_CRIS_PREFIX) - { - /* Match the prefix insn to BDAPQ. */ - const struct cris_opcode *prefix_opcodep - = get_opcode_entry (prefix_insn, NO_CRIS_PREFIX, disdata); - - if (prefix_opcodep->match == BDAP_QUICK_OPCODE) - { - int pushsize = (prefix_insn & 255); - - if (pushsize > 127) - pushsize -= 256; - - if (s[1] == 'P') - { - unsigned int spec_reg = (insn >> 12) & 15; - const struct cris_spec_reg *sregp - = spec_reg_info (spec_reg, disdata->distype); - - /* For a special-register, the "prefix size" must - match the size of the register. */ - if (sregp && sregp->reg_size == (unsigned int) -pushsize) - break; - } - else if (s[1] == 'R') - { - if ((insn & 0x30) == 0x20 && pushsize == -4) - break; - } - /* FIXME: Should abort here; next constraint letter - *must* be 'P' or 'R'. */ - } - } - return -1; - - case 'D': - retval = (((insn >> 12) & 15) == (insn & 15)); - if (!retval) - return -1; - else - retval += 4; - break; - - case 'P': - { - const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15, disdata->distype); - - /* Since we match four bits, we will give a value of 4-1 = 3 - in a match. If there is a corresponding exact match of a - special register in another pattern, it will get a value of - 4, which will be higher. This should be correct in that an - exact pattern would match better than a general pattern. - - Note that there is a reason for not returning zero; the - pattern for "clear" is partly matched in the bit-pattern - (the two lower bits must be zero), while the bit-pattern - for a move from a special register is matched in the - register constraint. */ - - if (sregp != NULL) - { - retval += 3; - break; - } - else - return -1; - } - } - - if (prefix_insn != NO_CRIS_PREFIX && ! prefix_ok) - return -1; - - return retval; -} - -/* Format number as hex with a leading "0x" into outbuffer. */ - -static char * -format_hex (unsigned long number, - char *outbuffer, - struct cris_disasm_data *disdata) -{ - /* Truncate negative numbers on >32-bit hosts. */ - number &= 0xffffffff; - - sprintf (outbuffer, "0x%lx", number); - - /* Save this value for the "case" support. */ - if (TRACE_CASE) - last_immediate = number; - - return outbuffer + strlen (outbuffer); -} - -/* Format number as decimal into outbuffer. Parameter signedp says - whether the number should be formatted as signed (!= 0) or - unsigned (== 0). */ - -static char * -format_dec (long number, char *outbuffer, int signedp) -{ - last_immediate = number; - sprintf (outbuffer, signedp ? "%ld" : "%lu", number); - - return outbuffer + strlen (outbuffer); -} - -/* Format the name of the general register regno into outbuffer. */ - -static char * -format_reg (struct cris_disasm_data *disdata, - int regno, - char *outbuffer_start, - bfd_boolean with_reg_prefix) -{ - char *outbuffer = outbuffer_start; - - if (with_reg_prefix) - *outbuffer++ = REGISTER_PREFIX_CHAR; - - switch (regno) - { - case 15: - /* For v32, there is no context in which we output PC. */ - if (disdata->distype == cris_dis_v32) - strcpy (outbuffer, "acr"); - else - strcpy (outbuffer, "pc"); - break; - - case 14: - strcpy (outbuffer, "sp"); - break; - - default: - sprintf (outbuffer, "r%d", regno); - break; - } - - return outbuffer_start + strlen (outbuffer_start); -} - -/* Format the name of a support register into outbuffer. */ - -static char * -format_sup_reg (unsigned int regno, - char *outbuffer_start, - bfd_boolean with_reg_prefix) -{ - char *outbuffer = outbuffer_start; - int i; - - if (with_reg_prefix) - *outbuffer++ = REGISTER_PREFIX_CHAR; - - for (i = 0; cris_support_regs[i].name != NULL; i++) - if (cris_support_regs[i].number == regno) - { - sprintf (outbuffer, "%s", cris_support_regs[i].name); - return outbuffer_start + strlen (outbuffer_start); - } - - /* There's supposed to be register names covering all numbers, though - some may be generic names. */ - sprintf (outbuffer, "format_sup_reg-BUG"); - return outbuffer_start + strlen (outbuffer_start); -} - -/* Return the length of an instruction. */ - -static unsigned -bytes_to_skip (unsigned int insn, - const struct cris_opcode *matchedp, - enum cris_disass_family distype, - const struct cris_opcode *prefix_matchedp) -{ - /* Each insn is a word plus "immediate" operands. */ - unsigned to_skip = 2; - const char *template = matchedp->args; - const char *s; - - for (s = template; *s; s++) - if ((*s == 's' || *s == 'N' || *s == 'Y') - && (insn & 0x400) && (insn & 15) == 15 - && prefix_matchedp == NULL) - { - /* Immediate via [pc+], so we have to check the size of the - operand. */ - int mode_size = 1 << ((insn >> 4) & (*template == 'z' ? 1 : 3)); - - if (matchedp->imm_oprnd_size == SIZE_FIX_32) - to_skip += 4; - else if (matchedp->imm_oprnd_size == SIZE_SPEC_REG) - { - const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15, distype); - - /* FIXME: Improve error handling; should have been caught - earlier. */ - if (sregp == NULL) - return 2; - - /* PC is incremented by two, not one, for a byte. Except on - CRISv32, where constants are always DWORD-size for - special registers. */ - to_skip += - distype == cris_dis_v32 ? 4 : (sregp->reg_size + 1) & ~1; - } - else - to_skip += (mode_size + 1) & ~1; - } - else if (*s == 'n') - to_skip += 4; - else if (*s == 'b') - to_skip += 2; - - return to_skip; -} - -/* Print condition code flags. */ - -static char * -print_flags (struct cris_disasm_data *disdata, unsigned int insn, char *cp) -{ - /* Use the v8 (Etrax 100) flag definitions for disassembly. - The differences with v0 (Etrax 1..4) vs. Svinto are: - v0 'd' <=> v8 'm' - v0 'e' <=> v8 'b'. - FIXME: Emit v0..v3 flag names somehow. */ - static const char v8_fnames[] = "cvznxibm"; - static const char v32_fnames[] = "cvznxiup"; - const char *fnames - = disdata->distype == cris_dis_v32 ? v32_fnames : v8_fnames; - - unsigned char flagbits = (((insn >> 8) & 0xf0) | (insn & 15)); - int i; - - for (i = 0; i < 8; i++) - if (flagbits & (1 << i)) - *cp++ = fnames[i]; - - return cp; -} - -/* 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. */ - -static void -print_with_operands (const struct cris_opcode *opcodep, - unsigned int insn, - unsigned char *buffer, - bfd_vma addr, - disassemble_info *info, - /* If a prefix insn was before this insn (and is supposed - to be output as an address), here is a description of - it. */ - const struct cris_opcode *prefix_opcodep, - unsigned int prefix_insn, - unsigned char *prefix_buffer, - bfd_boolean with_reg_prefix) -{ - /* Get a buffer of somewhat reasonable size where we store - intermediate parts of the insn. */ - char temp[sizeof (".d [$r13=$r12-2147483648],$r10") * 2]; - char *tp = temp; - static const char mode_char[] = "bwd?"; - const char *s; - const char *cs; - struct cris_disasm_data *disdata - = (struct cris_disasm_data *) info->private_data; - - /* Print out the name first thing we do. */ - (*info->fprintf_func) (info->stream, "%s", opcodep->name); - - cs = opcodep->args; - s = cs; - - /* Ignore any prefix indicator. */ - if (*s == 'p') - s++; - - if (*s == 'm' || *s == 'M' || *s == 'z') - { - *tp++ = '.'; - - /* Get the size-letter. */ - *tp++ = *s == 'M' - ? (insn & 0x8000 ? 'd' - : insn & 0x4000 ? 'w' : 'b') - : mode_char[(insn >> 4) & (*s == 'z' ? 1 : 3)]; - - /* Ignore the size and the space character that follows. */ - s += 2; - } - - /* Add a space if this isn't a long-branch, because for those will add - the condition part of the name later. */ - if (opcodep->match != (BRANCH_PC_LOW + BRANCH_INCR_HIGH * 256)) - *tp++ = ' '; - - /* Fill in the insn-type if deducible from the name (and there's no - better way). */ - if (opcodep->name[0] == 'j') - { - if (CONST_STRNEQ (opcodep->name, "jsr")) - /* It's "jsr" or "jsrc". */ - info->insn_type = dis_jsr; - else - /* Any other jump-type insn is considered a branch. */ - info->insn_type = dis_branch; - } - - /* We might know some more fields right now. */ - info->branch_delay_insns = opcodep->delayed; - - /* Handle operands. */ - for (; *s; s++) - { - switch (*s) - { - case 'T': - tp = format_sup_reg ((insn >> 12) & 15, tp, with_reg_prefix); - break; - - case 'A': - if (with_reg_prefix) - *tp++ = REGISTER_PREFIX_CHAR; - *tp++ = 'a'; - *tp++ = 'c'; - *tp++ = 'r'; - break; - - case '[': - case ']': - case ',': - *tp++ = *s; - break; - - case '!': - /* Ignore at this point; used at earlier stages to avoid - recognition if there's a prefix at something that in other - ways looks like a "pop". */ - break; - - case 'd': - /* Ignore. This is an optional ".d " on the large one of - relaxable insns. */ - break; - - case 'B': - /* This was the prefix that made this a "push". We've already - handled it by recognizing it, so signal that the prefix is - handled by setting it to NULL. */ - prefix_opcodep = NULL; - break; - - case 'D': - case 'r': - tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); - break; - - case 'R': - tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); - break; - - case 'n': - { - /* Like N but pc-relative to the start of the insn. */ - uint32_t number - = (buffer[2] + buffer[3] * 256 + buffer[4] * 65536 - + buffer[5] * 0x1000000 + addr); - - /* Finish off and output previous formatted bytes. */ - *tp = 0; - if (temp[0]) - (*info->fprintf_func) (info->stream, "%s", temp); - tp = temp; - - (*info->print_address_func) ((bfd_vma) number, info); - } - break; - - case 'u': - { - /* Like n but the offset is bits <3:0> in the instruction. */ - unsigned long number = (buffer[0] & 0xf) * 2 + addr; - - /* Finish off and output previous formatted bytes. */ - *tp = 0; - if (temp[0]) - (*info->fprintf_func) (info->stream, "%s", temp); - tp = temp; - - (*info->print_address_func) ((bfd_vma) number, info); - } - break; - - case 'N': - case 'y': - case 'Y': - case 'S': - case 's': - /* Any "normal" memory operand. */ - if ((insn & 0x400) && (insn & 15) == 15 && prefix_opcodep == NULL) - { - /* We're looking at [pc+], i.e. we need to output an immediate - number, where the size can depend on different things. */ - int32_t number; - int signedp - = ((*cs == 'z' && (insn & 0x20)) - || opcodep->match == BDAP_QUICK_OPCODE); - int nbytes; - - if (opcodep->imm_oprnd_size == SIZE_FIX_32) - nbytes = 4; - else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG) - { - const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15, disdata->distype); - - /* A NULL return should have been as a non-match earlier, - so catch it as an internal error in the error-case - below. */ - if (sregp == NULL) - /* Whatever non-valid size. */ - nbytes = 42; - else - /* PC is always incremented by a multiple of two. - For CRISv32, immediates are always 4 bytes for - special registers. */ - nbytes = disdata->distype == cris_dis_v32 - ? 4 : (sregp->reg_size + 1) & ~1; - } - else - { - int mode_size = 1 << ((insn >> 4) & (*cs == 'z' ? 1 : 3)); - - if (mode_size == 1) - nbytes = 2; - else - nbytes = mode_size; - } - - switch (nbytes) - { - case 1: - number = buffer[2]; - if (signedp && number > 127) - number -= 256; - break; - - case 2: - number = buffer[2] + buffer[3] * 256; - if (signedp && number > 32767) - number -= 65536; - break; - - case 4: - number - = buffer[2] + buffer[3] * 256 + buffer[4] * 65536 - + buffer[5] * 0x1000000; - break; - - default: - strcpy (tp, "bug"); - tp += 3; - number = 42; - } - - if ((*cs == 'z' && (insn & 0x20)) - || (opcodep->match == BDAP_QUICK_OPCODE - && (nbytes <= 2 || buffer[1 + nbytes] == 0))) - tp = format_dec (number, tp, signedp); - else - { - unsigned int highbyte = (number >> 24) & 0xff; - - /* Either output this as an address or as a number. If it's - a dword with the same high-byte as the address of the - insn, assume it's an address, and also if it's a non-zero - non-0xff high-byte. If this is a jsr or a jump, then - it's definitely an address. */ - if (nbytes == 4 - && (highbyte == ((addr >> 24) & 0xff) - || (highbyte != 0 && highbyte != 0xff) - || info->insn_type == dis_branch - || info->insn_type == dis_jsr)) - { - /* Finish off and output previous formatted bytes. */ - *tp = 0; - tp = temp; - if (temp[0]) - (*info->fprintf_func) (info->stream, "%s", temp); - - (*info->print_address_func) ((bfd_vma) number, info); - - info->target = number; - } - else - tp = format_hex (number, tp, disdata); - } - } - else - { - /* Not an immediate number. Then this is a (possibly - prefixed) memory operand. */ - if (info->insn_type != dis_nonbranch) - { - int mode_size - = 1 << ((insn >> 4) - & (opcodep->args[0] == 'z' ? 1 : 3)); - int size; - info->insn_type = dis_dref; - info->flags |= CRIS_DIS_FLAG_MEMREF; - - if (opcodep->imm_oprnd_size == SIZE_FIX_32) - size = 4; - else if (opcodep->imm_oprnd_size == SIZE_SPEC_REG) - { - const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15, disdata->distype); - - /* FIXME: Improve error handling; should have been caught - earlier. */ - if (sregp == NULL) - size = 4; - else - size = sregp->reg_size; - } - else - size = mode_size; - - info->data_size = size; - } - - *tp++ = '['; - - if (prefix_opcodep - /* We don't match dip with a postincremented field - as a side-effect address mode. */ - && ((insn & 0x400) == 0 - || prefix_opcodep->match != DIP_OPCODE)) - { - if (insn & 0x400) - { - tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); - *tp++ = '='; - } - - - /* We mainly ignore the prefix format string when the - address-mode syntax is output. */ - switch (prefix_opcodep->match) - { - case DIP_OPCODE: - /* It's [r], [r+] or [pc+]. */ - if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15) - { - /* It's [pc+]. This cannot possibly be anything - but an address. */ - uint32_t number - = prefix_buffer[2] + prefix_buffer[3] * 256 - + prefix_buffer[4] * 65536 - + prefix_buffer[5] * 0x1000000; - - info->target = (bfd_vma) number; - - /* Finish off and output previous formatted - data. */ - *tp = 0; - tp = temp; - if (temp[0]) - (*info->fprintf_func) (info->stream, "%s", temp); - - (*info->print_address_func) ((bfd_vma) number, info); - } - else - { - /* For a memref in an address, we use target2. - In this case, target is zero. */ - info->flags - |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG - | CRIS_DIS_FLAG_MEM_TARGET2_MEM); - - info->target2 = prefix_insn & 15; - - *tp++ = '['; - tp = format_reg (disdata, prefix_insn & 15, tp, - with_reg_prefix); - if (prefix_insn & 0x400) - *tp++ = '+'; - *tp++ = ']'; - } - break; - - case BDAP_QUICK_OPCODE: - { - int number; - - number = prefix_buffer[0]; - if (number > 127) - number -= 256; - - /* Output "reg+num" or, if num < 0, "reg-num". */ - tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, - with_reg_prefix); - if (number >= 0) - *tp++ = '+'; - tp = format_dec (number, tp, 1); - - info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; - info->target = (prefix_insn >> 12) & 15; - info->target2 = (bfd_vma) number; - break; - } - - case BIAP_OPCODE: - /* Output "r+R.m". */ - tp = format_reg (disdata, prefix_insn & 15, tp, - with_reg_prefix); - *tp++ = '+'; - tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, - with_reg_prefix); - *tp++ = '.'; - *tp++ = mode_char[(prefix_insn >> 4) & 3]; - - info->flags - |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG - | CRIS_DIS_FLAG_MEM_TARGET_IS_REG - - | ((prefix_insn & 0x8000) - ? CRIS_DIS_FLAG_MEM_TARGET2_MULT4 - : ((prefix_insn & 0x8000) - ? CRIS_DIS_FLAG_MEM_TARGET2_MULT2 : 0))); - - /* Is it the casejump? It's a "adds.w [pc+r%d.w],pc". */ - if (insn == 0xf83f && (prefix_insn & ~0xf000) == 0x55f) - /* Then start interpreting data as offsets. */ - case_offset_counter = no_of_case_offsets; - break; - - case BDAP_INDIR_OPCODE: - /* Output "r+s.m", or, if "s" is [pc+], "r+s" or - "r-s". */ - tp = format_reg (disdata, (prefix_insn >> 12) & 15, tp, - with_reg_prefix); - - if ((prefix_insn & 0x400) && (prefix_insn & 15) == 15) - { - int32_t number; - unsigned int nbytes; - - /* It's a value. Get its size. */ - int mode_size = 1 << ((prefix_insn >> 4) & 3); - - if (mode_size == 1) - nbytes = 2; - else - nbytes = mode_size; - - switch (nbytes) - { - case 1: - number = prefix_buffer[2]; - if (number > 127) - number -= 256; - break; - - case 2: - number = prefix_buffer[2] + prefix_buffer[3] * 256; - if (number > 32767) - number -= 65536; - break; - - case 4: - number - = prefix_buffer[2] + prefix_buffer[3] * 256 - + prefix_buffer[4] * 65536 - + prefix_buffer[5] * 0x1000000; - break; - - default: - strcpy (tp, "bug"); - tp += 3; - number = 42; - } - - info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; - info->target2 = (bfd_vma) number; - - /* If the size is dword, then assume it's an - address. */ - if (nbytes == 4) - { - /* Finish off and output previous formatted - bytes. */ - *tp++ = '+'; - *tp = 0; - tp = temp; - (*info->fprintf_func) (info->stream, "%s", temp); - - (*info->print_address_func) ((bfd_vma) number, info); - } - else - { - if (number >= 0) - *tp++ = '+'; - tp = format_dec (number, tp, 1); - } - } - else - { - /* Output "r+[R].m" or "r+[R+].m". */ - *tp++ = '+'; - *tp++ = '['; - tp = format_reg (disdata, prefix_insn & 15, tp, - with_reg_prefix); - if (prefix_insn & 0x400) - *tp++ = '+'; - *tp++ = ']'; - *tp++ = '.'; - *tp++ = mode_char[(prefix_insn >> 4) & 3]; - - info->flags - |= (CRIS_DIS_FLAG_MEM_TARGET2_IS_REG - | CRIS_DIS_FLAG_MEM_TARGET2_MEM - | CRIS_DIS_FLAG_MEM_TARGET_IS_REG - - | (((prefix_insn >> 4) == 2) - ? 0 - : (((prefix_insn >> 4) & 3) == 1 - ? CRIS_DIS_FLAG_MEM_TARGET2_MEM_WORD - : CRIS_DIS_FLAG_MEM_TARGET2_MEM_BYTE))); - } - break; - - default: - (*info->fprintf_func) (info->stream, "?prefix-bug"); - } - - /* To mark that the prefix is used, reset it. */ - prefix_opcodep = NULL; - } - else - { - tp = format_reg (disdata, insn & 15, tp, with_reg_prefix); - - info->flags |= CRIS_DIS_FLAG_MEM_TARGET_IS_REG; - info->target = insn & 15; - - if (insn & 0x400) - *tp++ = '+'; - } - *tp++ = ']'; - } - break; - - case 'x': - tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); - *tp++ = '.'; - *tp++ = mode_char[(insn >> 4) & 3]; - break; - - case 'I': - tp = format_dec (insn & 63, tp, 0); - break; - - case 'b': - { - int where = buffer[2] + buffer[3] * 256; - - if (where > 32767) - where -= 65536; - - where += addr + ((disdata->distype == cris_dis_v32) ? 0 : 4); - - if (insn == BA_PC_INCR_OPCODE) - info->insn_type = dis_branch; - else - info->insn_type = dis_condbranch; - - info->target = (bfd_vma) where; - - *tp = 0; - tp = temp; - (*info->fprintf_func) (info->stream, "%s%s ", - temp, cris_cc_strings[insn >> 12]); - - (*info->print_address_func) ((bfd_vma) where, info); - } - break; - - case 'c': - tp = format_dec (insn & 31, tp, 0); - break; - - case 'C': - tp = format_dec (insn & 15, tp, 0); - break; - - case 'o': - { - long offset = insn & 0xfe; - bfd_vma target; - - if (insn & 1) - offset |= ~0xff; - - if (opcodep->match == BA_QUICK_OPCODE) - info->insn_type = dis_branch; - else - info->insn_type = dis_condbranch; - - target = addr + ((disdata->distype == cris_dis_v32) ? 0 : 2) + offset; - info->target = target; - *tp = 0; - tp = temp; - (*info->fprintf_func) (info->stream, "%s", temp); - (*info->print_address_func) (target, info); - } - break; - - case 'Q': - case 'O': - { - long number = buffer[0]; - - if (number > 127) - number = number - 256; - - tp = format_dec (number, tp, 1); - *tp++ = ','; - tp = format_reg (disdata, (insn >> 12) & 15, tp, with_reg_prefix); - } - break; - - case 'f': - tp = print_flags (disdata, insn, tp); - break; - - case 'i': - tp = format_dec ((insn & 32) ? (insn & 31) | ~31L : insn & 31, tp, 1); - break; - - case 'P': - { - const struct cris_spec_reg *sregp - = spec_reg_info ((insn >> 12) & 15, disdata->distype); - - if (sregp == NULL || sregp->name == NULL) - /* Should have been caught as a non-match earlier. */ - *tp++ = '?'; - else - { - if (with_reg_prefix) - *tp++ = REGISTER_PREFIX_CHAR; - strcpy (tp, sregp->name); - tp += strlen (tp); - } - } - break; - - default: - strcpy (tp, "???"); - tp += 3; - } - } - - *tp = 0; - - if (prefix_opcodep) - (*info->fprintf_func) (info->stream, " (OOPS unused prefix \"%s: %s\")", - prefix_opcodep->name, prefix_opcodep->args); - - (*info->fprintf_func) (info->stream, "%s", temp); - - /* Get info for matching case-tables, if we don't have any active. - We assume that the last constant seen is used; either in the insn - itself or in a "move.d const,rN, sub.d rN,rM"-like sequence. */ - if (TRACE_CASE && case_offset_counter == 0) - { - if (CONST_STRNEQ (opcodep->name, "sub")) - case_offset = last_immediate; - - /* It could also be an "add", if there are negative case-values. */ - else if (CONST_STRNEQ (opcodep->name, "add")) - /* The first case is the negated operand to the add. */ - case_offset = -last_immediate; - - /* A bound insn will tell us the number of cases. */ - else if (CONST_STRNEQ (opcodep->name, "bound")) - no_of_case_offsets = last_immediate + 1; - - /* A jump or jsr or branch breaks the chain of insns for a - case-table, so assume default first-case again. */ - else if (info->insn_type == dis_jsr - || info->insn_type == dis_branch - || info->insn_type == dis_condbranch) - case_offset = 0; - } -} - - -/* Print the CRIS instruction at address memaddr on stream. Returns - length of the instruction, in bytes. Prefix register names with `$' if - WITH_REG_PREFIX. */ - -static int -print_insn_cris_generic (bfd_vma memaddr, - disassemble_info *info, - bfd_boolean with_reg_prefix) -{ - int nbytes; - unsigned int insn; - const struct cris_opcode *matchedp; - int advance = 0; - struct cris_disasm_data *disdata - = (struct cris_disasm_data *) info->private_data; - - /* No instruction will be disassembled as longer than this number of - bytes; stacked prefixes will not be expanded. */ - unsigned char buffer[MAX_BYTES_PER_CRIS_INSN]; - unsigned char *bufp; - int status = 0; - bfd_vma addr; - - /* There will be an "out of range" error after the last instruction. - Reading pairs of bytes in decreasing number, we hope that we will get - at least the amount that we will consume. - - If we can't get any data, or we do not get enough data, we print - the error message. */ - - nbytes = info->buffer_length ? info->buffer_length - : MAX_BYTES_PER_CRIS_INSN; - nbytes = MIN(nbytes, MAX_BYTES_PER_CRIS_INSN); - status = (*info->read_memory_func) (memaddr, buffer, nbytes, info); - - /* If we did not get all we asked for, then clear the rest. - Hopefully this makes a reproducible result in case of errors. */ - if (nbytes != MAX_BYTES_PER_CRIS_INSN) - memset (buffer + nbytes, 0, MAX_BYTES_PER_CRIS_INSN - nbytes); - - addr = memaddr; - bufp = buffer; - - /* Set some defaults for the insn info. */ - info->insn_info_valid = 1; - info->branch_delay_insns = 0; - info->data_size = 0; - info->insn_type = dis_nonbranch; - info->flags = 0; - info->target = 0; - info->target2 = 0; - - /* If we got any data, disassemble it. */ - if (nbytes != 0) - { - matchedp = NULL; - - insn = bufp[0] + bufp[1] * 256; - - /* If we're in a case-table, don't disassemble the offsets. */ - if (TRACE_CASE && case_offset_counter != 0) - { - info->insn_type = dis_noninsn; - advance += 2; - - /* If to print data as offsets, then shortcut here. */ - (*info->fprintf_func) (info->stream, "case %ld%s: -> ", - case_offset + no_of_case_offsets - - case_offset_counter, - case_offset_counter == 1 ? "/default" : - ""); - - (*info->print_address_func) ((bfd_vma) - ((short) (insn) - + (long) (addr - - (no_of_case_offsets - - case_offset_counter) - * 2)), info); - case_offset_counter--; - - /* The default case start (without a "sub" or "add") must be - zero. */ - if (case_offset_counter == 0) - case_offset = 0; - } - else if (insn == 0) - { - /* We're often called to disassemble zeroes. While this is a - valid "bcc .+2" insn, it is also useless enough and enough - of a nuiscance that we will just output "bcc .+2" for it - and signal it as a noninsn. */ - (*info->fprintf_func) (info->stream, - disdata->distype == cris_dis_v32 - ? "bcc ." : "bcc .+2"); - info->insn_type = dis_noninsn; - advance += 2; - } - else - { - const struct cris_opcode *prefix_opcodep = NULL; - unsigned char *prefix_buffer = bufp; - unsigned int prefix_insn = insn; - int prefix_size = 0; - - matchedp = get_opcode_entry (insn, NO_CRIS_PREFIX, disdata); - - /* Check if we're supposed to write out prefixes as address - modes and if this was a prefix. */ - if (matchedp != NULL && PARSE_PREFIX && matchedp->args[0] == 'p') - { - /* If it's a prefix, put it into the prefix vars and get the - main insn. */ - prefix_size = bytes_to_skip (prefix_insn, matchedp, - disdata->distype, NULL); - prefix_opcodep = matchedp; - - insn = bufp[prefix_size] + bufp[prefix_size + 1] * 256; - matchedp = get_opcode_entry (insn, prefix_insn, disdata); - - if (matchedp != NULL) - { - addr += prefix_size; - bufp += prefix_size; - advance += prefix_size; - } - else - { - /* The "main" insn wasn't valid, at least not when - prefixed. Put back things enough to output the - prefix insn only, as a normal insn. */ - matchedp = prefix_opcodep; - insn = prefix_insn; - prefix_opcodep = NULL; - } - } - - if (matchedp == NULL) - { - (*info->fprintf_func) (info->stream, "??0x%x", insn); - advance += 2; - - info->insn_type = dis_noninsn; - } - else - { - advance - += bytes_to_skip (insn, matchedp, disdata->distype, - prefix_opcodep); - - /* The info_type and assorted fields will be set according - to the operands. */ - print_with_operands (matchedp, insn, bufp, addr, info, - prefix_opcodep, prefix_insn, - prefix_buffer, with_reg_prefix); - } - } - } - else - info->insn_type = dis_noninsn; - - /* If we read less than MAX_BYTES_PER_CRIS_INSN, i.e. we got an error - status when reading that much, and the insn decoding indicated a - length exceeding what we read, there is an error. */ - if (status != 0 && (nbytes == 0 || advance > nbytes)) - { - (*info->memory_error_func) (status, memaddr, info); - return -1; - } - - /* Max supported insn size with one folded prefix insn. */ - info->bytes_per_line = MAX_BYTES_PER_CRIS_INSN; - - /* I would like to set this to a fixed value larger than the actual - number of bytes to print in order to avoid spaces between bytes, - but objdump.c (2.9.1) does not like that, so we print 16-bit - chunks, which is the next choice. */ - info->bytes_per_chunk = 2; - - /* Printing bytes in order of increasing addresses makes sense, - especially on a little-endian target. - This is completely the opposite of what you think; setting this to - BFD_ENDIAN_LITTLE will print bytes in order N..0 rather than the 0..N - we want. */ - info->display_endian = BFD_ENDIAN_BIG; - - return advance; -} - -/* Disassemble, prefixing register names with `$'. CRIS v0..v10. */ -static int -print_insn_cris_with_register_prefix (bfd_vma vma, - disassemble_info *info) -{ - struct cris_disasm_data disdata; - info->private_data = &disdata; - cris_parse_disassembler_options (&disdata, info->disassembler_options, - cris_dis_v0_v10); - return print_insn_cris_generic (vma, info, true); -} -/* Disassemble, prefixing register names with `$'. CRIS v32. */ - -static int -print_insn_crisv32_with_register_prefix (bfd_vma vma, - disassemble_info *info) -{ - struct cris_disasm_data disdata; - info->private_data = &disdata; - cris_parse_disassembler_options (&disdata, info->disassembler_options, - cris_dis_v32); - return print_insn_cris_generic (vma, info, true); -} - -#if 0 -/* Disassemble, prefixing register names with `$'. - Common v10 and v32 subset. */ - -static int -print_insn_crisv10_v32_with_register_prefix (bfd_vma vma, - disassemble_info *info) -{ - struct cris_disasm_data disdata; - info->private_data = &disdata; - cris_parse_disassembler_options (&disdata, info->disassembler_options, - cris_dis_common_v10_v32); - return print_insn_cris_generic (vma, info, true); -} - -/* Disassemble, no prefixes on register names. CRIS v0..v10. */ - -static int -print_insn_cris_without_register_prefix (bfd_vma vma, - disassemble_info *info) -{ - struct cris_disasm_data disdata; - info->private_data = &disdata; - cris_parse_disassembler_options (&disdata, info->disassembler_options, - cris_dis_v0_v10); - return print_insn_cris_generic (vma, info, false); -} - -/* Disassemble, no prefixes on register names. CRIS v32. */ - -static int -print_insn_crisv32_without_register_prefix (bfd_vma vma, - disassemble_info *info) -{ - struct cris_disasm_data disdata; - info->private_data = &disdata; - cris_parse_disassembler_options (&disdata, info->disassembler_options, - cris_dis_v32); - return print_insn_cris_generic (vma, info, false); -} - -/* Disassemble, no prefixes on register names. - Common v10 and v32 subset. */ - -static int -print_insn_crisv10_v32_without_register_prefix (bfd_vma vma, - disassemble_info *info) -{ - struct cris_disasm_data disdata; - info->private_data = &disdata; - cris_parse_disassembler_options (&disdata, info->disassembler_options, - cris_dis_common_v10_v32); - return print_insn_cris_generic (vma, info, false); -} -#endif - -int -print_insn_crisv10 (bfd_vma vma, - disassemble_info *info) -{ - return print_insn_cris_with_register_prefix(vma, info); -} - -int -print_insn_crisv32 (bfd_vma vma, - disassemble_info *info) -{ - return print_insn_crisv32_with_register_prefix(vma, info); -} - -/* Return a disassembler-function that prints registers with a `$' prefix, - or one that prints registers without a prefix. - FIXME: We should improve the solution to avoid the multitude of - functions seen above. */ -#if 0 -disassembler_ftype -cris_get_disassembler (bfd *abfd) -{ - /* If there's no bfd in sight, we return what is valid as input in all - contexts if fed back to the assembler: disassembly *with* register - prefix. Unfortunately this will be totally wrong for v32. */ - if (abfd == NULL) - return print_insn_cris_with_register_prefix; - - if (bfd_get_symbol_leading_char (abfd) == 0) - { - if (bfd_get_mach (abfd) == bfd_mach_cris_v32) - return print_insn_crisv32_with_register_prefix; - if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32) - return print_insn_crisv10_v32_with_register_prefix; - - /* We default to v10. This may be specifically specified in the - bfd mach, but is also the default setting. */ - return print_insn_cris_with_register_prefix; - } - - if (bfd_get_mach (abfd) == bfd_mach_cris_v32) - return print_insn_crisv32_without_register_prefix; - if (bfd_get_mach (abfd) == bfd_mach_cris_v10_v32) - return print_insn_crisv10_v32_without_register_prefix; - return print_insn_cris_without_register_prefix; -} -#endif -/* Local variables: - eval: (c-set-style "gnu") - indent-tabs-mode: t - End: */ diff --git a/disas/disas-common.c b/disas/disas-common.c new file mode 100644 index 0000000000..de61f6d8a1 --- /dev/null +++ b/disas/disas-common.c @@ -0,0 +1,104 @@ +/* + * Common routines for disassembly. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "disas/capstone.h" +#include "hw/core/cpu.h" +#include "exec/tswap.h" +#include "disas-internal.h" + + +/* Filled in by elfload.c. Simplistic, but will do for now. */ +struct syminfo *syminfos = NULL; + +/* + * Print an error message. We can assume that this is in response to + * an error return from {host,target}_read_memory. + */ +static void perror_memory(int status, bfd_vma memaddr, + struct disassemble_info *info) +{ + if (status != EIO) { + /* Can't happen. */ + info->fprintf_func(info->stream, "Unknown error %d\n", status); + } else { + /* Address between memaddr and memaddr + len was out of bounds. */ + info->fprintf_func(info->stream, + "Address 0x%" PRIx64 " is out of bounds.\n", + memaddr); + } +} + +/* Print address in hex. */ +static void print_address(bfd_vma addr, struct disassemble_info *info) +{ + info->fprintf_func(info->stream, "0x%" PRIx64, addr); +} + +/* Stub prevents some fruitless earching in optabs disassemblers. */ +static int symbol_at_address(bfd_vma addr, struct disassemble_info *info) +{ + return 1; +} + +void disas_initialize_debug(CPUDebug *s) +{ + memset(s, 0, sizeof(*s)); + s->info.arch = bfd_arch_unknown; + s->info.cap_arch = -1; + s->info.cap_insn_unit = 4; + s->info.cap_insn_split = 4; + s->info.memory_error_func = perror_memory; + s->info.symbol_at_address_func = symbol_at_address; +} + +void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu) +{ + disas_initialize_debug(s); + + s->cpu = cpu; + s->info.print_address_func = print_address; + 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) { + cc->disas_set_info(cpu, &s->info); + } +} + +int disas_gstring_printf(FILE *stream, const char *fmt, ...) +{ + /* We abuse the FILE parameter to pass a GString. */ + GString *s = (GString *)stream; + int initial_len = s->len; + va_list va; + + va_start(va, fmt); + g_string_append_vprintf(s, fmt, va); + va_end(va); + + return s->len - initial_len; +} + +/* Look up symbol for debugging purpose. Returns "" if unknown. */ +const char *lookup_symbol(uint64_t orig_addr) +{ + const char *symbol = ""; + struct syminfo *s; + + for (s = syminfos; s; s = s->next) { + symbol = s->lookup_symbol(s, orig_addr); + if (symbol[0] != '\0') { + break; + } + } + + return symbol; +} diff --git a/disas/disas-host.c b/disas/disas-host.c new file mode 100644 index 0000000000..8146fafe80 --- /dev/null +++ b/disas/disas-host.c @@ -0,0 +1,129 @@ +/* + * Routines for host instruction disassembly. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "disas/capstone.h" +#include "disas-internal.h" + + +/* + * Get LENGTH bytes from info's buffer, at host address memaddr. + * Transfer them to myaddr. + */ +static int host_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + if (memaddr < info->buffer_vma + || memaddr + length > info->buffer_vma + info->buffer_length) { + /* Out of bounds. Use EIO because GDB uses it. */ + return EIO; + } + memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length); + return 0; +} + +/* Print address in hex, truncated to the width of a host virtual address. */ +static void host_print_address(bfd_vma addr, struct disassemble_info *info) +{ + info->fprintf_func(info->stream, "0x%" PRIxPTR, (uintptr_t)addr); +} + +static void initialize_debug_host(CPUDebug *s) +{ + disas_initialize_debug(s); + + s->info.read_memory_func = host_read_memory; + s->info.print_address_func = host_print_address; +#if HOST_BIG_ENDIAN + s->info.endian = BFD_ENDIAN_BIG; +#else + s->info.endian = BFD_ENDIAN_LITTLE; +#endif +#if defined(CONFIG_TCG_INTERPRETER) + s->info.print_insn = print_insn_tci; +#elif defined(__i386__) + s->info.mach = bfd_mach_i386_i386; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_32; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(__x86_64__) + s->info.mach = bfd_mach_x86_64; + s->info.cap_arch = CS_ARCH_X86; + s->info.cap_mode = CS_MODE_64; + s->info.cap_insn_unit = 1; + s->info.cap_insn_split = 8; +#elif defined(_ARCH_PPC) + s->info.cap_arch = CS_ARCH_PPC; +# ifdef _ARCH_PPC64 + s->info.cap_mode = CS_MODE_64; +# endif +#elif defined(__riscv) +#if defined(_ILP32) || (__riscv_xlen == 32) + s->info.print_insn = print_insn_riscv32; +#elif defined(_LP64) + s->info.print_insn = print_insn_riscv64; +#else +#error unsupported RISC-V ABI +#endif +#elif defined(__aarch64__) + s->info.cap_arch = CS_ARCH_ARM64; +#elif defined(__alpha__) + s->info.print_insn = print_insn_alpha; +#elif defined(__sparc__) + s->info.print_insn = print_insn_sparc; + s->info.mach = bfd_mach_sparc_v9b; +#elif defined(__arm__) + /* TCG only generates code for arm mode. */ + s->info.cap_arch = CS_ARCH_ARM; +#elif defined(__MIPSEB__) + s->info.print_insn = print_insn_big_mips; +#elif defined(__MIPSEL__) + s->info.print_insn = print_insn_little_mips; +#elif defined(__m68k__) + s->info.print_insn = print_insn_m68k; +#elif defined(__s390__) + s->info.cap_arch = CS_ARCH_SYSZ; + s->info.cap_insn_unit = 2; + s->info.cap_insn_split = 6; +#elif defined(__hppa__) + s->info.print_insn = print_insn_hppa; +#elif defined(__loongarch__) + s->info.print_insn = print_insn_loongarch; +#endif +} + +/* Disassemble this for me please... (debugging). */ +void disas(FILE *out, const void *code, size_t size) +{ + uintptr_t pc; + int count; + CPUDebug s; + + initialize_debug_host(&s); + s.info.fprintf_func = fprintf; + s.info.stream = out; + 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; + } + + if (s.info.print_insn == NULL) { + s.info.print_insn = print_insn_od_host; + } + for (pc = (uintptr_t)code; size > 0; pc += count, size -= count) { + fprintf(out, "0x%08" PRIxPTR ": ", pc); + count = s.info.print_insn(pc, &s.info); + fprintf(out, "\n"); + if (count < 0) { + break; + } + } +} diff --git a/disas/disas-internal.h b/disas/disas-internal.h new file mode 100644 index 0000000000..ed32e704cc --- /dev/null +++ b/disas/disas-internal.h @@ -0,0 +1,25 @@ +/* + * 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(CPUDebug *s); +void disas_initialize_debug_target(CPUDebug *s, CPUState *cpu); +int disas_gstring_printf(FILE *stream, const char *fmt, ...) + G_GNUC_PRINTF(2, 3); + +int print_insn_od_host(bfd_vma pc, disassemble_info *info); +int print_insn_od_target(bfd_vma pc, disassemble_info *info); + +#endif diff --git a/disas/disas-mon.c b/disas/disas-mon.c new file mode 100644 index 0000000000..37bf16ac79 --- /dev/null +++ b/disas/disas-mon.c @@ -0,0 +1,81 @@ +/* + * 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" + +/* + * Get LENGTH bytes from info's buffer, at target address memaddr. + * Transfer them to myaddr. + */ +static int +virtual_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + CPUDebug *s = container_of(info, CPUDebug, info); + int r = cpu_memory_rw_debug(s->cpu, memaddr, myaddr, length, 0); + return r ? EIO : 0; +} + +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; + } else { + s.info.read_memory_func = virtual_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/disas-target.c b/disas/disas-target.c new file mode 100644 index 0000000000..48f3a365dc --- /dev/null +++ b/disas/disas-target.c @@ -0,0 +1,99 @@ +/* + * Routines for target instruction disassembly. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas/disas.h" +#include "disas/capstone.h" +#include "exec/translator.h" +#include "disas-internal.h" + + +static int translator_read_memory(bfd_vma memaddr, bfd_byte *myaddr, + int length, struct disassemble_info *info) +{ + const DisasContextBase *db = info->application_data; + return translator_st(db, myaddr, memaddr, length) ? 0 : EIO; +} + +void target_disas(FILE *out, CPUState *cpu, const struct DisasContextBase *db) +{ + uint64_t code = db->pc_first; + size_t size = translator_st_len(db); + uint64_t pc; + int count; + CPUDebug s; + + disas_initialize_debug_target(&s, cpu); + s.info.read_memory_func = translator_read_memory; + s.info.application_data = (void *)db; + 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; + } + + if (s.info.print_insn == NULL) { + s.info.print_insn = print_insn_od_target; + } + + for (pc = code; size > 0; pc += count, size -= count) { + 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 " + "decoding\n" + "Please report this to qemu-devel@nongnu.org\n"); + break; + } + } +} + +#ifdef CONFIG_PLUGIN +static void plugin_print_address(bfd_vma addr, struct disassemble_info *info) +{ + /* does nothing */ +} + +/* + * We should only be dissembling one instruction at a time here. If + * there is left over it usually indicates the front end has read more + * bytes than it needed. + */ +char *plugin_disas(CPUState *cpu, const DisasContextBase *db, + uint64_t addr, size_t size) +{ + CPUDebug s; + GString *ds = g_string_new(NULL); + + disas_initialize_debug_target(&s, cpu); + s.info.read_memory_func = translator_read_memory; + s.info.application_data = (void *)db; + 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; + s.info.print_address_func = plugin_print_address; + + if (s.info.cap_arch >= 0 && cap_disas_plugin(&s.info, addr, size)) { + ; /* done */ + } else if (s.info.print_insn) { + s.info.print_insn(addr, &s.info); + } else { + ; /* cannot disassemble -- return empty string */ + } + + /* Return the buffer, freeing the GString container. */ + return g_string_free(ds, false); +} +#endif /* CONFIG_PLUGIN */ 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/m68k.c b/disas/m68k.c index aefaecfbd6..800b4145ac 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -1000,7 +1000,7 @@ print_indexed (int basereg, /* Generate the text for the index register. Where this will be output is not yet determined. */ - sprintf (buf, "%s:%c%s", + snprintf(buf, sizeof(buf), "%s:%c%s", reg_names[(word >> 12) & 0xf], (word & 0x800) ? 'l' : 'w', scales[(word >> 9) & 3]); @@ -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 1977f5cd92..bbfa119783 100644 --- a/disas/meson.build +++ b/disas/meson.build @@ -1,14 +1,23 @@ common_ss.add(when: 'CONFIG_ALPHA_DIS', if_true: files('alpha.c')) -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_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.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_MIPS_DIS', if_true: files('mips.c', 'nanomips.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(when: 'CONFIG_TCG', if_true: files( + 'disas-host.c', + 'disas-target.c', + 'objdump.c' +)) +common_ss.add(files('disas-common.c')) +system_ss.add(files('disas-mon.c')) +specific_ss.add(capstone) diff --git a/disas/microblaze.c b/disas/microblaze.c index 0b89b9c4fa..197327fae4 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -563,10 +563,7 @@ static const struct op_code_struct { }; /* prefix for register names */ -static const char register_prefix[] = "r"; -static const char fsl_register_prefix[] = "rfsl"; -static const char pvr_register_prefix[] = "rpvr"; - +#define register_prefix "r" /* #defines for valid immediate range */ #define MIN_IMM ((int) 0x80000000) @@ -579,149 +576,64 @@ static const char pvr_register_prefix[] = "rpvr"; #include "disas/dis-asm.h" -#define get_field_rd(instr) get_field(instr, RD_MASK, RD_LOW) -#define get_field_r1(instr) get_field(instr, RA_MASK, RA_LOW) -#define get_field_r2(instr) get_field(instr, RB_MASK, RB_LOW) +#define PRIreg register_prefix "%ld" +#define PRIrfsl register_prefix "fsl%ld" +#define PRIpvr register_prefix "pvr%d" +#define PRIimm "%d" + +#define get_field_rd(instr) ((instr & RD_MASK) >> RD_LOW) +#define get_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) +#define get_field_r2(instr) ((instr & RB_MASK) >> RB_LOW) +#define get_field_rfsl(instr) (instr & RFSL_MASK) +#define get_field_imm(instr) ((int16_t)instr) +#define get_field_imm5(instr) ((int)instr & IMM5_MASK) +#define get_field_imm15(instr) ((int)instr & IMM15_MASK) + #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) -/* Local function prototypes. */ - -static char * get_field (long instr, long mask, unsigned short low); -static char * get_field_imm (long instr); -static char * get_field_imm5 (long instr); -static char * get_field_rfsl (long instr); -static char * get_field_imm15 (long instr); -#if 0 -static char * get_field_unsigned_imm (long instr); -#endif - -static char * -get_field (long instr, long mask, unsigned short low) +static int get_field_special(long instr, const struct op_code_struct *op) { - char tmpstr[25]; - sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low)); - return(strdup(tmpstr)); + return ((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask; } -static char * -get_field_imm (long instr) +/* Returns NULL for PVR registers, which should be rendered differently. */ +static const char *get_special_name(int special) { - char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - -static char * -get_field_imm5 (long instr) -{ - char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - -static char * -get_field_rfsl (long instr) -{ - char tmpstr[25]; - sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & RFSL_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - -static char * -get_field_imm15 (long instr) -{ - char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} - -#if 0 -static char * -get_field_unsigned_imm (long instr) -{ - char tmpstr[25]; - sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW)); - return(strdup(tmpstr)); -} -#endif - -/* - char * - get_field_special (instr) - long instr; - { - char tmpstr[25]; - - sprintf(tmpstr, "%s%s", register_prefix, (((instr & IMM_MASK) >> IMM_LOW) & REG_MSR_MASK) == 0 ? "pc" : "msr"); - - return(strdup(tmpstr)); - } -*/ - -static char * -get_field_special(long instr, const struct op_code_struct *op) -{ - char tmpstr[25]; - char spr[6]; - - switch ( (((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ) { - - case REG_MSR_MASK : - strcpy(spr, "msr"); - break; - case REG_PC_MASK : - strcpy(spr, "pc"); - break; - case REG_EAR_MASK : - strcpy(spr, "ear"); - break; - case REG_ESR_MASK : - strcpy(spr, "esr"); - break; - case REG_FSR_MASK : - strcpy(spr, "fsr"); - break; - case REG_BTR_MASK : - strcpy(spr, "btr"); - break; - case REG_EDR_MASK : - strcpy(spr, "edr"); - break; - case REG_PID_MASK : - strcpy(spr, "pid"); - break; - case REG_ZPR_MASK : - strcpy(spr, "zpr"); - break; - case REG_TLBX_MASK : - strcpy(spr, "tlbx"); - break; - case REG_TLBLO_MASK : - strcpy(spr, "tlblo"); - break; - case REG_TLBHI_MASK : - strcpy(spr, "tlbhi"); - break; - case REG_TLBSX_MASK : - strcpy(spr, "tlbsx"); - break; - default : - { - if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { - sprintf(tmpstr, "%s%u", pvr_register_prefix, - (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ - op->immval_mask) ^ REG_PVR_MASK); - return(strdup(tmpstr)); - } else { - strcpy(spr, "pc"); - } - } - break; - } - - sprintf(tmpstr, "%s%s", register_prefix, spr); - return(strdup(tmpstr)); + switch (special) { + case REG_MSR_MASK: + return register_prefix "msr"; + case REG_PC_MASK: + return register_prefix "pc"; + case REG_EAR_MASK: + return register_prefix "ear"; + case REG_ESR_MASK: + return register_prefix "esr"; + case REG_FSR_MASK: + return register_prefix "fsr"; + case REG_BTR_MASK: + return register_prefix "btr"; + case REG_EDR_MASK: + return register_prefix "edr"; + case REG_PID_MASK: + return register_prefix "pid"; + case REG_ZPR_MASK: + return register_prefix "zpr"; + case REG_TLBX_MASK: + return register_prefix "tlbx"; + case REG_TLBLO_MASK: + return register_prefix "tlblo"; + case REG_TLBHI_MASK: + return register_prefix "tlbhi"; + case REG_TLBSX_MASK: + return register_prefix "tlbsx"; + default: + if ((special & 0xE000) == REG_PVR_MASK) { + /* pvr register */ + return NULL; + } + return register_prefix "pc"; + } } static unsigned long @@ -760,185 +672,189 @@ read_insn_microblaze (bfd_vma memaddr, return inst; } +static void print_immval_addr(struct disassemble_info *info, bool immfound, + int immval, unsigned inst, int addend) +{ + if (info->print_address_func && info->symbol_at_address_func) { + if (immfound) { + immval |= get_int_field_imm(inst) & 0x0000ffff; + } else { + immval = (int16_t)get_int_field_imm(inst); + } + immval += addend; + if (immval != 0 && info->symbol_at_address_func(immval, info)) { + info->fprintf_func(info->stream, "\t// "); + info->print_address_func (immval, info); + } else if (addend) { + info->fprintf_func(info->stream, "\t// %x", immval); + } + } +} int -print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) +print_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info) { - fprintf_function fprintf_func = info->fprintf_func; - void * stream = info->stream; - unsigned long inst, prev_inst; - const struct op_code_struct *op, *pop; - int immval = 0; - bfd_boolean immfound = FALSE; - static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ - static int prev_insn_vma = -1; /*init the prev insn vma */ - int curr_insn_vma = info->buffer_vma; + fprintf_function fprintf_func = info->fprintf_func; + void *stream = info->stream; + unsigned long inst, prev_inst; + const struct op_code_struct *op, *pop; + int immval = 0; + bool immfound = false; + static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ + static int prev_insn_vma = -1; /*init the prev insn vma */ + int curr_insn_vma = info->buffer_vma; + int special; + const char *special_name; - info->bytes_per_chunk = 4; + info->bytes_per_chunk = 4; - inst = read_insn_microblaze (memaddr, info, &op); - if (inst == 0) { - return -1; - } - - if (prev_insn_vma == curr_insn_vma) { - if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) { - prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); - if (prev_inst == 0) - return -1; - if (pop->instr == imm) { - immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; - immfound = TRUE; - } - else { - immval = 0; - immfound = FALSE; - } - } - } - /* make curr insn as prev insn */ - prev_insn_addr = memaddr; - prev_insn_vma = curr_insn_vma; - - if (op->name == 0) { - fprintf_func (stream, ".short 0x%04lx", inst); - } - else - { - fprintf_func (stream, "%s", op->name); - - switch (op->inst_type) - { - case INST_TYPE_RD_R1_R2: - fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); - break; - case INST_TYPE_RD_R1_IMM: - fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); - if (info->print_address_func && get_int_field_r1(inst) == 0 && info->symbol_at_address_func) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } - } - break; - case INST_TYPE_RD_R1_IMM5: - fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); - break; - case INST_TYPE_RD_RFSL: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_rfsl(inst)); - break; - case INST_TYPE_R1_RFSL: - fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_rfsl(inst)); - break; - case INST_TYPE_RD_SPECIAL: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op)); - break; - case INST_TYPE_SPECIAL_R1: - fprintf_func(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst)); - break; - case INST_TYPE_RD_R1: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst)); - break; - case INST_TYPE_R1_R2: - fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst)); - break; - case INST_TYPE_R1_IMM: - fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); - /* The non-pc relative instructions are returns, which shouldn't - have a label printed */ - if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET && info->symbol_at_address_func) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - immval += memaddr; - if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } else { - fprintf_func (stream, "\t\t// "); - fprintf_func (stream, "%x", immval); - } - } - break; - case INST_TYPE_RD_IMM: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); - if (info->print_address_func && info->symbol_at_address_func) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - if (op->inst_offset_type == INST_PC_OFFSET) - immval += (int) memaddr; - if (info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } - } - break; - case INST_TYPE_IMM: - fprintf_func(stream, "\t%s", get_field_imm(inst)); - if (info->print_address_func && info->symbol_at_address_func && op->instr != imm) { - if (immfound) - immval |= (get_int_field_imm(inst) & 0x0000ffff); - else { - immval = get_int_field_imm(inst); - if (immval & 0x8000) - immval |= 0xFFFF0000; - } - if (op->inst_offset_type == INST_PC_OFFSET) - immval += (int) memaddr; - if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf_func (stream, "\t// "); - info->print_address_func (immval, info); - } else if (op->inst_offset_type == INST_PC_OFFSET) { - fprintf_func (stream, "\t\t// "); - fprintf_func (stream, "%x", immval); - } - } - break; - case INST_TYPE_RD_R2: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); - break; - case INST_TYPE_R2: - fprintf_func(stream, "\t%s", get_field_r2(inst)); - break; - case INST_TYPE_R1: - fprintf_func(stream, "\t%s", get_field_r1(inst)); - break; - case INST_TYPE_RD_R1_SPECIAL: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); - break; - case INST_TYPE_RD_IMM15: - fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm15(inst)); - break; - /* For tuqula instruction */ - case INST_TYPE_RD: - fprintf_func(stream, "\t%s", get_field_rd(inst)); - break; - case INST_TYPE_RFSL: - fprintf_func(stream, "\t%s", get_field_rfsl(inst)); - break; - default: - /* if the disassembler lags the instruction set */ - fprintf_func (stream, "\tundecoded operands, inst is 0x%04lx", inst); - break; - } + inst = read_insn_microblaze (memaddr, info, &op); + if (inst == 0) { + return -1; } - /* Say how many bytes we consumed? */ - return 4; + if (prev_insn_vma == curr_insn_vma) { + if (memaddr - info->bytes_per_chunk == prev_insn_addr) { + prev_inst = read_insn_microblaze (prev_insn_addr, info, &pop); + if (prev_inst == 0) + return -1; + if (pop->instr == imm) { + immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; + immfound = TRUE; + } + else { + immval = 0; + immfound = FALSE; + } + } + } + /* make curr insn as prev insn */ + prev_insn_addr = memaddr; + prev_insn_vma = curr_insn_vma; + + if (op->name == 0) { + fprintf_func (stream, ".short 0x%04lx", inst); + return 4; + } + + switch (op->inst_type) { + case INST_TYPE_RD_R1_R2: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", " PRIreg, + op->name, get_field_rd(inst), get_field_r1(inst), + get_field_r2(inst)); + break; + case INST_TYPE_RD_R1_IMM: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", " PRIimm, + op->name, get_field_rd(inst), get_field_r1(inst), + get_field_imm(inst)); + if (get_int_field_r1(inst) == 0) { + print_immval_addr(info, immfound, immval, inst, 0); + } + break; + case INST_TYPE_RD_R1_IMM5: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg ", " PRIimm, + op->name, get_field_rd(inst), get_field_r1(inst), + get_field_imm5(inst)); + break; + case INST_TYPE_RD_RFSL: + fprintf_func(stream, "%s\t" PRIreg ", " PRIrfsl, + op->name, get_field_rd(inst), get_field_rfsl(inst)); + break; + case INST_TYPE_R1_RFSL: + fprintf_func(stream, "%s\t" PRIreg ", " PRIrfsl, + op->name, get_field_r1(inst), get_field_rfsl(inst)); + break; + case INST_TYPE_RD_SPECIAL: + special = get_field_special(inst, op); + special_name = get_special_name(special); + if (special_name) { + fprintf_func(stream, "%s\t" PRIreg ", %s", + op->name, get_field_rd(inst), special_name); + } else { + fprintf_func(stream, "%s\t" PRIreg ", " PRIpvr, + op->name, get_field_rd(inst), special ^ REG_PVR_MASK); + } + break; + case INST_TYPE_SPECIAL_R1: + special = get_field_special(inst, op); + special_name = get_special_name(special); + if (special_name) { + fprintf_func(stream, "%s\t%s, " PRIreg, + op->name, special_name, get_field_r1(inst)); + } else { + fprintf_func(stream, "%s\t" PRIpvr ", " PRIreg, + op->name, special ^ REG_PVR_MASK, get_field_r1(inst)); + } + break; + case INST_TYPE_RD_R1: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, + op->name, get_field_rd(inst), get_field_r1(inst)); + break; + case INST_TYPE_R1_R2: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, + op->name, get_field_r1(inst), get_field_r2(inst)); + break; + case INST_TYPE_R1_IMM: + fprintf_func(stream, "%s\t" PRIreg ", " PRIimm, + op->name, get_field_r1(inst), get_field_imm(inst)); + /* + * The non-pc relative instructions are returns, + * which shouldn't have a label printed. + */ + if (op->inst_offset_type == INST_PC_OFFSET) { + print_immval_addr(info, immfound, immval, inst, memaddr); + } + break; + case INST_TYPE_RD_IMM: + fprintf_func(stream, "%s\t" PRIreg ", " PRIimm, + op->name, get_field_rd(inst), get_field_imm(inst)); + print_immval_addr(info, immfound, immval, inst, + op->inst_offset_type == INST_PC_OFFSET + ? memaddr : 0); + break; + case INST_TYPE_IMM: + fprintf_func(stream, "%s\t" PRIimm, + op->name, get_field_imm(inst)); + if (op->instr != imm) { + print_immval_addr(info, immfound, immval, inst, + op->inst_offset_type == INST_PC_OFFSET + ? memaddr : 0); + } + break; + case INST_TYPE_RD_R2: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, + op->name, get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_R2: + fprintf_func(stream, "%s\t" PRIreg, + op->name, get_field_r2(inst)); + break; + case INST_TYPE_R1: + fprintf_func(stream, "%s\t" PRIreg, + op->name, get_field_r1(inst)); + break; + case INST_TYPE_RD_R1_SPECIAL: + fprintf_func(stream, "%s\t" PRIreg ", " PRIreg, + op->name, get_field_rd(inst), get_field_r2(inst)); + break; + case INST_TYPE_RD_IMM15: + fprintf_func(stream, "%s\t" PRIreg ", " PRIimm, + op->name, get_field_rd(inst), get_field_imm15(inst)); + break; + /* For tuqula instruction */ + case INST_TYPE_RD: + fprintf_func(stream, "%s\t" PRIreg, + op->name, get_field_rd(inst)); + break; + case INST_TYPE_RFSL: + fprintf_func(stream, "%s\t" PRIrfsl, + op->name, get_field_rfsl(inst)); + break; + default: + /* if the disassembler lags the instruction set */ + fprintf_func(stream, "%s\tundecoded operands, inst is 0x%04lx", + op->name, inst); + break; + } + return 4; } diff --git a/disas/nanomips.c b/disas/nanomips.c index a0253598dd..db0c297b8d 100644 --- a/disas/nanomips.c +++ b/disas/nanomips.c @@ -36,35 +36,6 @@ typedef uint32_t uint32; typedef uint16_t uint16; typedef uint64_t img_address; -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 struct Dis_info { img_address m_pc; fprintf_function fprintf_func; @@ -72,22 +43,6 @@ typedef struct Dis_info { sigjmp_buf buf; } Dis_info; -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; - #define IMGASSERTONCE(test) @@ -544,58 +499,6 @@ static uint64 extract_op_code_value(const uint16 *data, int size) } -/* - * 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 uint64 extract_code_18_to_0(uint64 instruction) { uint64 value = 0; @@ -16213,6 +16116,51 @@ static char *YIELD(uint64 instruction, Dis_info *info) * */ +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, &SYSCALL_32_ , 0, @@ -21907,6 +21855,58 @@ static const Pool MAJOR[2] = { 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; diff --git a/disas/nios2.c b/disas/nios2.c deleted file mode 100644 index 98ac07d72e..0000000000 --- a/disas/nios2.c +++ /dev/null @@ -1,3514 +0,0 @@ -/* Nios II opcode library for QEMU. - Copyright (C) 2012-2016 Free Software Foundation, Inc. - Contributed by Nigel Gray (ngray@altera.com). - Contributed by Mentor Graphics, 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ - -/* This file resembles a concatenation of the following files from - binutils: - - include/opcode/nios2.h - include/opcode/nios2r1.h - include/opcode/nios2r2.h - opcodes/nios2-opc.c - opcodes/nios2-dis.c - - It has been derived from the original patches which have been - relicensed by the contributors as GPL version 2 for inclusion - in QEMU. */ - -#ifndef _NIOS2_H_ -#define _NIOS2_H_ - -/*#include "bfd.h"*/ -#include "qemu/osdep.h" -#include "disas/dis-asm.h" - - -/**************************************************************************** - * This file contains structures, bit masks and shift counts used - * by the GNU toolchain to define the Nios II instruction set and - * access various opcode fields. - ****************************************************************************/ - -/* Instruction encoding formats. */ -enum iw_format_type { - /* R1 formats. */ - iw_i_type, - iw_r_type, - iw_j_type, - iw_custom_type, - - /* 32-bit R2 formats. */ - iw_L26_type, - iw_F2I16_type, - iw_F2X4I12_type, - iw_F1X4I12_type, - iw_F1X4L17_type, - iw_F3X6L5_type, - iw_F2X6L10_type, - iw_F3X6_type, - iw_F3X8_type, - - /* 16-bit R2 formats. */ - iw_I10_type, - iw_T1I7_type, - iw_T2I4_type, - iw_T1X1I6_type, - iw_X1I7_type, - iw_L5I4X1_type, - iw_T2X1L3_type, - iw_T2X1I3_type, - iw_T3X1_type, - iw_T2X3_type, - iw_F1X1_type, - iw_X2L5_type, - iw_F1I5_type, - iw_F2_type -}; - -/* Identify different overflow situations for error messages. */ -enum overflow_type -{ - call_target_overflow = 0, - branch_target_overflow, - address_offset_overflow, - signed_immed16_overflow, - unsigned_immed16_overflow, - unsigned_immed5_overflow, - signed_immed12_overflow, - custom_opcode_overflow, - enumeration_overflow, - no_overflow -}; - -/* This structure holds information for a particular instruction. - - The args field is a string describing the operands. The following - letters can appear in the args: - c - a 5-bit control register index - d - a 5-bit destination register index - s - a 5-bit left source register index - t - a 5-bit right source register index - D - a 3-bit encoded destination register - S - a 3-bit encoded left source register - T - a 3-bit encoded right source register - i - a 16-bit signed immediate - j - a 5-bit unsigned immediate - k - a (second) 5-bit unsigned immediate - l - a 8-bit custom instruction constant - m - a 26-bit unsigned immediate - o - a 16-bit signed pc-relative offset - u - a 16-bit unsigned immediate - I - a 12-bit signed immediate - M - a 6-bit unsigned immediate - N - a 6-bit unsigned immediate with 2-bit shift - O - a 10-bit signed pc-relative offset with 1-bit shift - P - a 7-bit signed pc-relative offset with 1-bit shift - U - a 7-bit unsigned immediate with 2-bit shift - V - a 5-bit unsigned immediate with 2-bit shift - W - a 4-bit unsigned immediate with 2-bit shift - X - a 4-bit unsigned immediate with 1-bit shift - Y - a 4-bit unsigned immediate - e - an immediate coded as an enumeration for addi.n/subi.n - f - an immediate coded as an enumeration for slli.n/srli.n - g - an immediate coded as an enumeration for andi.n - h - an immediate coded as an enumeration for movi.n - R - a reglist for ldwm/stwm or push.n/pop.n - B - a base register specifier and option list for ldwm/stwm - Literal ',', '(', and ')' characters may also appear in the args as - delimiters. - - Note that the args describe the semantics and assembly-language syntax - of the operands, not their encoding into the instruction word. - - The pinfo field is INSN_MACRO for a macro. Otherwise, it is a collection - of bits describing the instruction, notably any relevant hazard - information. - - When assembling, the match field contains the opcode template, which - is modified by the arguments to produce the actual opcode - that is emitted. If pinfo is INSN_MACRO, then this is 0. - - If pinfo is INSN_MACRO, the mask field stores the macro identifier. - Otherwise this is a bit mask for the relevant portions of the opcode - when disassembling. If the actual opcode anded with the match field - equals the opcode field, then we have found the correct instruction. */ - -struct nios2_opcode -{ - const char *name; /* The name of the instruction. */ - const char *args; /* A string describing the arguments for this - instruction. */ - const char *args_test; /* Like args, but with an extra argument for - the expected opcode. */ - unsigned long num_args; /* The number of arguments the instruction - takes. */ - unsigned size; /* Size in bytes of the instruction. */ - enum iw_format_type format; /* Instruction format. */ - unsigned long match; /* The basic opcode for the instruction. */ - unsigned long mask; /* Mask for the opcode field of the - instruction. */ - unsigned long pinfo; /* Is this a real instruction or instruction - macro? */ - enum overflow_type overflow_msg; /* Used to generate informative - message when fixup overflows. */ -}; - -/* This value is used in the nios2_opcode.pinfo field to indicate that the - instruction is a macro or pseudo-op. This requires special treatment by - the assembler, and is used by the disassembler to determine whether to - check for a nop. */ -#define NIOS2_INSN_MACRO 0x80000000 -#define NIOS2_INSN_MACRO_MOV 0x80000001 -#define NIOS2_INSN_MACRO_MOVI 0x80000002 -#define NIOS2_INSN_MACRO_MOVIA 0x80000004 - -#define NIOS2_INSN_RELAXABLE 0x40000000 -#define NIOS2_INSN_UBRANCH 0x00000010 -#define NIOS2_INSN_CBRANCH 0x00000020 -#define NIOS2_INSN_CALL 0x00000040 - -#define NIOS2_INSN_OPTARG 0x00000080 - -/* Register attributes. */ -#define REG_NORMAL (1<<0) /* Normal registers. */ -#define REG_CONTROL (1<<1) /* Control registers. */ -#define REG_COPROCESSOR (1<<2) /* For custom instructions. */ -#define REG_3BIT (1<<3) /* For R2 CDX instructions. */ -#define REG_LDWM (1<<4) /* For R2 ldwm/stwm. */ -#define REG_POP (1<<5) /* For R2 pop.n/push.n. */ - -struct nios2_reg -{ - const char *name; - const int index; - unsigned long regtype; -}; - -/* Pull in the instruction field accessors, opcodes, and masks. */ -/*#include "nios2r1.h"*/ - -#ifndef _NIOS2R1_H_ -#define _NIOS2R1_H_ - -/* R1 fields. */ -#define IW_R1_OP_LSB 0 -#define IW_R1_OP_SIZE 6 -#define IW_R1_OP_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R1_OP_SIZE)) -#define IW_R1_OP_SHIFTED_MASK (IW_R1_OP_UNSHIFTED_MASK << IW_R1_OP_LSB) -#define GET_IW_R1_OP(W) (((W) >> IW_R1_OP_LSB) & IW_R1_OP_UNSHIFTED_MASK) -#define SET_IW_R1_OP(V) (((V) & IW_R1_OP_UNSHIFTED_MASK) << IW_R1_OP_LSB) - -#define IW_I_A_LSB 27 -#define IW_I_A_SIZE 5 -#define IW_I_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I_A_SIZE)) -#define IW_I_A_SHIFTED_MASK (IW_I_A_UNSHIFTED_MASK << IW_I_A_LSB) -#define GET_IW_I_A(W) (((W) >> IW_I_A_LSB) & IW_I_A_UNSHIFTED_MASK) -#define SET_IW_I_A(V) (((V) & IW_I_A_UNSHIFTED_MASK) << IW_I_A_LSB) - -#define IW_I_B_LSB 22 -#define IW_I_B_SIZE 5 -#define IW_I_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I_B_SIZE)) -#define IW_I_B_SHIFTED_MASK (IW_I_B_UNSHIFTED_MASK << IW_I_B_LSB) -#define GET_IW_I_B(W) (((W) >> IW_I_B_LSB) & IW_I_B_UNSHIFTED_MASK) -#define SET_IW_I_B(V) (((V) & IW_I_B_UNSHIFTED_MASK) << IW_I_B_LSB) - -#define IW_I_IMM16_LSB 6 -#define IW_I_IMM16_SIZE 16 -#define IW_I_IMM16_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I_IMM16_SIZE)) -#define IW_I_IMM16_SHIFTED_MASK (IW_I_IMM16_UNSHIFTED_MASK << IW_I_IMM16_LSB) -#define GET_IW_I_IMM16(W) (((W) >> IW_I_IMM16_LSB) & IW_I_IMM16_UNSHIFTED_MASK) -#define SET_IW_I_IMM16(V) (((V) & IW_I_IMM16_UNSHIFTED_MASK) << IW_I_IMM16_LSB) - -#define IW_R_A_LSB 27 -#define IW_R_A_SIZE 5 -#define IW_R_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_A_SIZE)) -#define IW_R_A_SHIFTED_MASK (IW_R_A_UNSHIFTED_MASK << IW_R_A_LSB) -#define GET_IW_R_A(W) (((W) >> IW_R_A_LSB) & IW_R_A_UNSHIFTED_MASK) -#define SET_IW_R_A(V) (((V) & IW_R_A_UNSHIFTED_MASK) << IW_R_A_LSB) - -#define IW_R_B_LSB 22 -#define IW_R_B_SIZE 5 -#define IW_R_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_B_SIZE)) -#define IW_R_B_SHIFTED_MASK (IW_R_B_UNSHIFTED_MASK << IW_R_B_LSB) -#define GET_IW_R_B(W) (((W) >> IW_R_B_LSB) & IW_R_B_UNSHIFTED_MASK) -#define SET_IW_R_B(V) (((V) & IW_R_B_UNSHIFTED_MASK) << IW_R_B_LSB) - -#define IW_R_C_LSB 17 -#define IW_R_C_SIZE 5 -#define IW_R_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_C_SIZE)) -#define IW_R_C_SHIFTED_MASK (IW_R_C_UNSHIFTED_MASK << IW_R_C_LSB) -#define GET_IW_R_C(W) (((W) >> IW_R_C_LSB) & IW_R_C_UNSHIFTED_MASK) -#define SET_IW_R_C(V) (((V) & IW_R_C_UNSHIFTED_MASK) << IW_R_C_LSB) - -#define IW_R_OPX_LSB 11 -#define IW_R_OPX_SIZE 6 -#define IW_R_OPX_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_OPX_SIZE)) -#define IW_R_OPX_SHIFTED_MASK (IW_R_OPX_UNSHIFTED_MASK << IW_R_OPX_LSB) -#define GET_IW_R_OPX(W) (((W) >> IW_R_OPX_LSB) & IW_R_OPX_UNSHIFTED_MASK) -#define SET_IW_R_OPX(V) (((V) & IW_R_OPX_UNSHIFTED_MASK) << IW_R_OPX_LSB) - -#define IW_R_IMM5_LSB 6 -#define IW_R_IMM5_SIZE 5 -#define IW_R_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_IMM5_SIZE)) -#define IW_R_IMM5_SHIFTED_MASK (IW_R_IMM5_UNSHIFTED_MASK << IW_R_IMM5_LSB) -#define GET_IW_R_IMM5(W) (((W) >> IW_R_IMM5_LSB) & IW_R_IMM5_UNSHIFTED_MASK) -#define SET_IW_R_IMM5(V) (((V) & IW_R_IMM5_UNSHIFTED_MASK) << IW_R_IMM5_LSB) - -#define IW_J_IMM26_LSB 6 -#define IW_J_IMM26_SIZE 26 -#define IW_J_IMM26_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_J_IMM26_SIZE)) -#define IW_J_IMM26_SHIFTED_MASK (IW_J_IMM26_UNSHIFTED_MASK << IW_J_IMM26_LSB) -#define GET_IW_J_IMM26(W) (((W) >> IW_J_IMM26_LSB) & IW_J_IMM26_UNSHIFTED_MASK) -#define SET_IW_J_IMM26(V) (((V) & IW_J_IMM26_UNSHIFTED_MASK) << IW_J_IMM26_LSB) - -#define IW_CUSTOM_A_LSB 27 -#define IW_CUSTOM_A_SIZE 5 -#define IW_CUSTOM_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_A_SIZE)) -#define IW_CUSTOM_A_SHIFTED_MASK (IW_CUSTOM_A_UNSHIFTED_MASK << IW_CUSTOM_A_LSB) -#define GET_IW_CUSTOM_A(W) (((W) >> IW_CUSTOM_A_LSB) & IW_CUSTOM_A_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_A(V) (((V) & IW_CUSTOM_A_UNSHIFTED_MASK) << IW_CUSTOM_A_LSB) - -#define IW_CUSTOM_B_LSB 22 -#define IW_CUSTOM_B_SIZE 5 -#define IW_CUSTOM_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_B_SIZE)) -#define IW_CUSTOM_B_SHIFTED_MASK (IW_CUSTOM_B_UNSHIFTED_MASK << IW_CUSTOM_B_LSB) -#define GET_IW_CUSTOM_B(W) (((W) >> IW_CUSTOM_B_LSB) & IW_CUSTOM_B_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_B(V) (((V) & IW_CUSTOM_B_UNSHIFTED_MASK) << IW_CUSTOM_B_LSB) - -#define IW_CUSTOM_C_LSB 17 -#define IW_CUSTOM_C_SIZE 5 -#define IW_CUSTOM_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_C_SIZE)) -#define IW_CUSTOM_C_SHIFTED_MASK (IW_CUSTOM_C_UNSHIFTED_MASK << IW_CUSTOM_C_LSB) -#define GET_IW_CUSTOM_C(W) (((W) >> IW_CUSTOM_C_LSB) & IW_CUSTOM_C_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_C(V) (((V) & IW_CUSTOM_C_UNSHIFTED_MASK) << IW_CUSTOM_C_LSB) - -#define IW_CUSTOM_READA_LSB 16 -#define IW_CUSTOM_READA_SIZE 1 -#define IW_CUSTOM_READA_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_READA_SIZE)) -#define IW_CUSTOM_READA_SHIFTED_MASK (IW_CUSTOM_READA_UNSHIFTED_MASK << IW_CUSTOM_READA_LSB) -#define GET_IW_CUSTOM_READA(W) (((W) >> IW_CUSTOM_READA_LSB) & IW_CUSTOM_READA_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_READA(V) (((V) & IW_CUSTOM_READA_UNSHIFTED_MASK) << IW_CUSTOM_READA_LSB) - -#define IW_CUSTOM_READB_LSB 15 -#define IW_CUSTOM_READB_SIZE 1 -#define IW_CUSTOM_READB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_READB_SIZE)) -#define IW_CUSTOM_READB_SHIFTED_MASK (IW_CUSTOM_READB_UNSHIFTED_MASK << IW_CUSTOM_READB_LSB) -#define GET_IW_CUSTOM_READB(W) (((W) >> IW_CUSTOM_READB_LSB) & IW_CUSTOM_READB_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_READB(V) (((V) & IW_CUSTOM_READB_UNSHIFTED_MASK) << IW_CUSTOM_READB_LSB) - -#define IW_CUSTOM_READC_LSB 14 -#define IW_CUSTOM_READC_SIZE 1 -#define IW_CUSTOM_READC_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_READC_SIZE)) -#define IW_CUSTOM_READC_SHIFTED_MASK (IW_CUSTOM_READC_UNSHIFTED_MASK << IW_CUSTOM_READC_LSB) -#define GET_IW_CUSTOM_READC(W) (((W) >> IW_CUSTOM_READC_LSB) & IW_CUSTOM_READC_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_READC(V) (((V) & IW_CUSTOM_READC_UNSHIFTED_MASK) << IW_CUSTOM_READC_LSB) - -#define IW_CUSTOM_N_LSB 6 -#define IW_CUSTOM_N_SIZE 8 -#define IW_CUSTOM_N_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_CUSTOM_N_SIZE)) -#define IW_CUSTOM_N_SHIFTED_MASK (IW_CUSTOM_N_UNSHIFTED_MASK << IW_CUSTOM_N_LSB) -#define GET_IW_CUSTOM_N(W) (((W) >> IW_CUSTOM_N_LSB) & IW_CUSTOM_N_UNSHIFTED_MASK) -#define SET_IW_CUSTOM_N(V) (((V) & IW_CUSTOM_N_UNSHIFTED_MASK) << IW_CUSTOM_N_LSB) - -/* R1 opcodes. */ -#define R1_OP_CALL 0 -#define R1_OP_JMPI 1 -#define R1_OP_LDBU 3 -#define R1_OP_ADDI 4 -#define R1_OP_STB 5 -#define R1_OP_BR 6 -#define R1_OP_LDB 7 -#define R1_OP_CMPGEI 8 -#define R1_OP_LDHU 11 -#define R1_OP_ANDI 12 -#define R1_OP_STH 13 -#define R1_OP_BGE 14 -#define R1_OP_LDH 15 -#define R1_OP_CMPLTI 16 -#define R1_OP_INITDA 19 -#define R1_OP_ORI 20 -#define R1_OP_STW 21 -#define R1_OP_BLT 22 -#define R1_OP_LDW 23 -#define R1_OP_CMPNEI 24 -#define R1_OP_FLUSHDA 27 -#define R1_OP_XORI 28 -#define R1_OP_BNE 30 -#define R1_OP_CMPEQI 32 -#define R1_OP_LDBUIO 35 -#define R1_OP_MULI 36 -#define R1_OP_STBIO 37 -#define R1_OP_BEQ 38 -#define R1_OP_LDBIO 39 -#define R1_OP_CMPGEUI 40 -#define R1_OP_LDHUIO 43 -#define R1_OP_ANDHI 44 -#define R1_OP_STHIO 45 -#define R1_OP_BGEU 46 -#define R1_OP_LDHIO 47 -#define R1_OP_CMPLTUI 48 -#define R1_OP_CUSTOM 50 -#define R1_OP_INITD 51 -#define R1_OP_ORHI 52 -#define R1_OP_STWIO 53 -#define R1_OP_BLTU 54 -#define R1_OP_LDWIO 55 -#define R1_OP_RDPRS 56 -#define R1_OP_OPX 58 -#define R1_OP_FLUSHD 59 -#define R1_OP_XORHI 60 - -#define R1_OPX_ERET 1 -#define R1_OPX_ROLI 2 -#define R1_OPX_ROL 3 -#define R1_OPX_FLUSHP 4 -#define R1_OPX_RET 5 -#define R1_OPX_NOR 6 -#define R1_OPX_MULXUU 7 -#define R1_OPX_CMPGE 8 -#define R1_OPX_BRET 9 -#define R1_OPX_ROR 11 -#define R1_OPX_FLUSHI 12 -#define R1_OPX_JMP 13 -#define R1_OPX_AND 14 -#define R1_OPX_CMPLT 16 -#define R1_OPX_SLLI 18 -#define R1_OPX_SLL 19 -#define R1_OPX_WRPRS 20 -#define R1_OPX_OR 22 -#define R1_OPX_MULXSU 23 -#define R1_OPX_CMPNE 24 -#define R1_OPX_SRLI 26 -#define R1_OPX_SRL 27 -#define R1_OPX_NEXTPC 28 -#define R1_OPX_CALLR 29 -#define R1_OPX_XOR 30 -#define R1_OPX_MULXSS 31 -#define R1_OPX_CMPEQ 32 -#define R1_OPX_DIVU 36 -#define R1_OPX_DIV 37 -#define R1_OPX_RDCTL 38 -#define R1_OPX_MUL 39 -#define R1_OPX_CMPGEU 40 -#define R1_OPX_INITI 41 -#define R1_OPX_TRAP 45 -#define R1_OPX_WRCTL 46 -#define R1_OPX_CMPLTU 48 -#define R1_OPX_ADD 49 -#define R1_OPX_BREAK 52 -#define R1_OPX_SYNC 54 -#define R1_OPX_SUB 57 -#define R1_OPX_SRAI 58 -#define R1_OPX_SRA 59 - -/* Some convenience macros for R1 encodings, for use in instruction tables. - MATCH_R1_OPX0(NAME) and MASK_R1_OPX0 are used for R-type instructions - with 3 register operands and constant 0 in the immediate field. - The general forms are MATCH_R1_OPX(NAME, A, B, C) where the arguments specify - constant values and MASK_R1_OPX(A, B, C, N) where the arguments are booleans - that are true if the field should be included in the mask. - */ -#define MATCH_R1_OP(NAME) \ - (SET_IW_R1_OP (R1_OP_##NAME)) -#define MASK_R1_OP \ - IW_R1_OP_SHIFTED_MASK - -#define MATCH_R1_OPX0(NAME) \ - (SET_IW_R1_OP (R1_OP_OPX) | SET_IW_R_OPX (R1_OPX_##NAME)) -#define MASK_R1_OPX0 \ - (IW_R1_OP_SHIFTED_MASK | IW_R_OPX_SHIFTED_MASK | IW_R_IMM5_SHIFTED_MASK) - -#define MATCH_R1_OPX(NAME, A, B, C) \ - (MATCH_R1_OPX0 (NAME) | SET_IW_R_A (A) | SET_IW_R_B (B) | SET_IW_R_C (C)) -#define MASK_R1_OPX(A, B, C, N) \ - (IW_R1_OP_SHIFTED_MASK | IW_R_OPX_SHIFTED_MASK \ - | (A ? IW_R_A_SHIFTED_MASK : 0) \ - | (B ? IW_R_B_SHIFTED_MASK : 0) \ - | (C ? IW_R_C_SHIFTED_MASK : 0) \ - | (N ? IW_R_IMM5_SHIFTED_MASK : 0)) - -/* And here's the match/mask macros for the R1 instruction set. */ -#define MATCH_R1_ADD MATCH_R1_OPX0 (ADD) -#define MASK_R1_ADD MASK_R1_OPX0 -#define MATCH_R1_ADDI MATCH_R1_OP (ADDI) -#define MASK_R1_ADDI MASK_R1_OP -#define MATCH_R1_AND MATCH_R1_OPX0 (AND) -#define MASK_R1_AND MASK_R1_OPX0 -#define MATCH_R1_ANDHI MATCH_R1_OP (ANDHI) -#define MASK_R1_ANDHI MASK_R1_OP -#define MATCH_R1_ANDI MATCH_R1_OP (ANDI) -#define MASK_R1_ANDI MASK_R1_OP -#define MATCH_R1_BEQ MATCH_R1_OP (BEQ) -#define MASK_R1_BEQ MASK_R1_OP -#define MATCH_R1_BGE MATCH_R1_OP (BGE) -#define MASK_R1_BGE MASK_R1_OP -#define MATCH_R1_BGEU MATCH_R1_OP (BGEU) -#define MASK_R1_BGEU MASK_R1_OP -#define MATCH_R1_BGT MATCH_R1_OP (BLT) -#define MASK_R1_BGT MASK_R1_OP -#define MATCH_R1_BGTU MATCH_R1_OP (BLTU) -#define MASK_R1_BGTU MASK_R1_OP -#define MATCH_R1_BLE MATCH_R1_OP (BGE) -#define MASK_R1_BLE MASK_R1_OP -#define MATCH_R1_BLEU MATCH_R1_OP (BGEU) -#define MASK_R1_BLEU MASK_R1_OP -#define MATCH_R1_BLT MATCH_R1_OP (BLT) -#define MASK_R1_BLT MASK_R1_OP -#define MATCH_R1_BLTU MATCH_R1_OP (BLTU) -#define MASK_R1_BLTU MASK_R1_OP -#define MATCH_R1_BNE MATCH_R1_OP (BNE) -#define MASK_R1_BNE MASK_R1_OP -#define MATCH_R1_BR MATCH_R1_OP (BR) -#define MASK_R1_BR MASK_R1_OP | IW_I_A_SHIFTED_MASK | IW_I_B_SHIFTED_MASK -#define MATCH_R1_BREAK MATCH_R1_OPX (BREAK, 0, 0, 0x1e) -#define MASK_R1_BREAK MASK_R1_OPX (1, 1, 1, 0) -#define MATCH_R1_BRET MATCH_R1_OPX (BRET, 0x1e, 0, 0) -#define MASK_R1_BRET MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_CALL MATCH_R1_OP (CALL) -#define MASK_R1_CALL MASK_R1_OP -#define MATCH_R1_CALLR MATCH_R1_OPX (CALLR, 0, 0, 0x1f) -#define MASK_R1_CALLR MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_CMPEQ MATCH_R1_OPX0 (CMPEQ) -#define MASK_R1_CMPEQ MASK_R1_OPX0 -#define MATCH_R1_CMPEQI MATCH_R1_OP (CMPEQI) -#define MASK_R1_CMPEQI MASK_R1_OP -#define MATCH_R1_CMPGE MATCH_R1_OPX0 (CMPGE) -#define MASK_R1_CMPGE MASK_R1_OPX0 -#define MATCH_R1_CMPGEI MATCH_R1_OP (CMPGEI) -#define MASK_R1_CMPGEI MASK_R1_OP -#define MATCH_R1_CMPGEU MATCH_R1_OPX0 (CMPGEU) -#define MASK_R1_CMPGEU MASK_R1_OPX0 -#define MATCH_R1_CMPGEUI MATCH_R1_OP (CMPGEUI) -#define MASK_R1_CMPGEUI MASK_R1_OP -#define MATCH_R1_CMPGT MATCH_R1_OPX0 (CMPLT) -#define MASK_R1_CMPGT MASK_R1_OPX0 -#define MATCH_R1_CMPGTI MATCH_R1_OP (CMPGEI) -#define MASK_R1_CMPGTI MASK_R1_OP -#define MATCH_R1_CMPGTU MATCH_R1_OPX0 (CMPLTU) -#define MASK_R1_CMPGTU MASK_R1_OPX0 -#define MATCH_R1_CMPGTUI MATCH_R1_OP (CMPGEUI) -#define MASK_R1_CMPGTUI MASK_R1_OP -#define MATCH_R1_CMPLE MATCH_R1_OPX0 (CMPGE) -#define MASK_R1_CMPLE MASK_R1_OPX0 -#define MATCH_R1_CMPLEI MATCH_R1_OP (CMPLTI) -#define MASK_R1_CMPLEI MASK_R1_OP -#define MATCH_R1_CMPLEU MATCH_R1_OPX0 (CMPGEU) -#define MASK_R1_CMPLEU MASK_R1_OPX0 -#define MATCH_R1_CMPLEUI MATCH_R1_OP (CMPLTUI) -#define MASK_R1_CMPLEUI MASK_R1_OP -#define MATCH_R1_CMPLT MATCH_R1_OPX0 (CMPLT) -#define MASK_R1_CMPLT MASK_R1_OPX0 -#define MATCH_R1_CMPLTI MATCH_R1_OP (CMPLTI) -#define MASK_R1_CMPLTI MASK_R1_OP -#define MATCH_R1_CMPLTU MATCH_R1_OPX0 (CMPLTU) -#define MASK_R1_CMPLTU MASK_R1_OPX0 -#define MATCH_R1_CMPLTUI MATCH_R1_OP (CMPLTUI) -#define MASK_R1_CMPLTUI MASK_R1_OP -#define MATCH_R1_CMPNE MATCH_R1_OPX0 (CMPNE) -#define MASK_R1_CMPNE MASK_R1_OPX0 -#define MATCH_R1_CMPNEI MATCH_R1_OP (CMPNEI) -#define MASK_R1_CMPNEI MASK_R1_OP -#define MATCH_R1_CUSTOM MATCH_R1_OP (CUSTOM) -#define MASK_R1_CUSTOM MASK_R1_OP -#define MATCH_R1_DIV MATCH_R1_OPX0 (DIV) -#define MASK_R1_DIV MASK_R1_OPX0 -#define MATCH_R1_DIVU MATCH_R1_OPX0 (DIVU) -#define MASK_R1_DIVU MASK_R1_OPX0 -#define MATCH_R1_ERET MATCH_R1_OPX (ERET, 0x1d, 0x1e, 0) -#define MASK_R1_ERET MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_FLUSHD MATCH_R1_OP (FLUSHD) | SET_IW_I_B (0) -#define MASK_R1_FLUSHD MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_FLUSHDA MATCH_R1_OP (FLUSHDA) | SET_IW_I_B (0) -#define MASK_R1_FLUSHDA MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_FLUSHI MATCH_R1_OPX (FLUSHI, 0, 0, 0) -#define MASK_R1_FLUSHI MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_FLUSHP MATCH_R1_OPX (FLUSHP, 0, 0, 0) -#define MASK_R1_FLUSHP MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_INITD MATCH_R1_OP (INITD) | SET_IW_I_B (0) -#define MASK_R1_INITD MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_INITDA MATCH_R1_OP (INITDA) | SET_IW_I_B (0) -#define MASK_R1_INITDA MASK_R1_OP | IW_I_B_SHIFTED_MASK -#define MATCH_R1_INITI MATCH_R1_OPX (INITI, 0, 0, 0) -#define MASK_R1_INITI MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_JMP MATCH_R1_OPX (JMP, 0, 0, 0) -#define MASK_R1_JMP MASK_R1_OPX (0, 1, 1, 1) -#define MATCH_R1_JMPI MATCH_R1_OP (JMPI) -#define MASK_R1_JMPI MASK_R1_OP -#define MATCH_R1_LDB MATCH_R1_OP (LDB) -#define MASK_R1_LDB MASK_R1_OP -#define MATCH_R1_LDBIO MATCH_R1_OP (LDBIO) -#define MASK_R1_LDBIO MASK_R1_OP -#define MATCH_R1_LDBU MATCH_R1_OP (LDBU) -#define MASK_R1_LDBU MASK_R1_OP -#define MATCH_R1_LDBUIO MATCH_R1_OP (LDBUIO) -#define MASK_R1_LDBUIO MASK_R1_OP -#define MATCH_R1_LDH MATCH_R1_OP (LDH) -#define MASK_R1_LDH MASK_R1_OP -#define MATCH_R1_LDHIO MATCH_R1_OP (LDHIO) -#define MASK_R1_LDHIO MASK_R1_OP -#define MATCH_R1_LDHU MATCH_R1_OP (LDHU) -#define MASK_R1_LDHU MASK_R1_OP -#define MATCH_R1_LDHUIO MATCH_R1_OP (LDHUIO) -#define MASK_R1_LDHUIO MASK_R1_OP -#define MATCH_R1_LDW MATCH_R1_OP (LDW) -#define MASK_R1_LDW MASK_R1_OP -#define MATCH_R1_LDWIO MATCH_R1_OP (LDWIO) -#define MASK_R1_LDWIO MASK_R1_OP -#define MATCH_R1_MOV MATCH_R1_OPX (ADD, 0, 0, 0) -#define MASK_R1_MOV MASK_R1_OPX (0, 1, 0, 1) -#define MATCH_R1_MOVHI MATCH_R1_OP (ORHI) | SET_IW_I_A (0) -#define MASK_R1_MOVHI MASK_R1_OP | IW_I_A_SHIFTED_MASK -#define MATCH_R1_MOVI MATCH_R1_OP (ADDI) | SET_IW_I_A (0) -#define MASK_R1_MOVI MASK_R1_OP | IW_I_A_SHIFTED_MASK -#define MATCH_R1_MOVUI MATCH_R1_OP (ORI) | SET_IW_I_A (0) -#define MASK_R1_MOVUI MASK_R1_OP | IW_I_A_SHIFTED_MASK -#define MATCH_R1_MUL MATCH_R1_OPX0 (MUL) -#define MASK_R1_MUL MASK_R1_OPX0 -#define MATCH_R1_MULI MATCH_R1_OP (MULI) -#define MASK_R1_MULI MASK_R1_OP -#define MATCH_R1_MULXSS MATCH_R1_OPX0 (MULXSS) -#define MASK_R1_MULXSS MASK_R1_OPX0 -#define MATCH_R1_MULXSU MATCH_R1_OPX0 (MULXSU) -#define MASK_R1_MULXSU MASK_R1_OPX0 -#define MATCH_R1_MULXUU MATCH_R1_OPX0 (MULXUU) -#define MASK_R1_MULXUU MASK_R1_OPX0 -#define MATCH_R1_NEXTPC MATCH_R1_OPX (NEXTPC, 0, 0, 0) -#define MASK_R1_NEXTPC MASK_R1_OPX (1, 1, 0, 1) -#define MATCH_R1_NOP MATCH_R1_OPX (ADD, 0, 0, 0) -#define MASK_R1_NOP MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_NOR MATCH_R1_OPX0 (NOR) -#define MASK_R1_NOR MASK_R1_OPX0 -#define MATCH_R1_OR MATCH_R1_OPX0 (OR) -#define MASK_R1_OR MASK_R1_OPX0 -#define MATCH_R1_ORHI MATCH_R1_OP (ORHI) -#define MASK_R1_ORHI MASK_R1_OP -#define MATCH_R1_ORI MATCH_R1_OP (ORI) -#define MASK_R1_ORI MASK_R1_OP -#define MATCH_R1_RDCTL MATCH_R1_OPX (RDCTL, 0, 0, 0) -#define MASK_R1_RDCTL MASK_R1_OPX (1, 1, 0, 0) -#define MATCH_R1_RDPRS MATCH_R1_OP (RDPRS) -#define MASK_R1_RDPRS MASK_R1_OP -#define MATCH_R1_RET MATCH_R1_OPX (RET, 0x1f, 0, 0) -#define MASK_R1_RET MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_ROL MATCH_R1_OPX0 (ROL) -#define MASK_R1_ROL MASK_R1_OPX0 -#define MATCH_R1_ROLI MATCH_R1_OPX (ROLI, 0, 0, 0) -#define MASK_R1_ROLI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_ROR MATCH_R1_OPX0 (ROR) -#define MASK_R1_ROR MASK_R1_OPX0 -#define MATCH_R1_SLL MATCH_R1_OPX0 (SLL) -#define MASK_R1_SLL MASK_R1_OPX0 -#define MATCH_R1_SLLI MATCH_R1_OPX (SLLI, 0, 0, 0) -#define MASK_R1_SLLI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_SRA MATCH_R1_OPX0 (SRA) -#define MASK_R1_SRA MASK_R1_OPX0 -#define MATCH_R1_SRAI MATCH_R1_OPX (SRAI, 0, 0, 0) -#define MASK_R1_SRAI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_SRL MATCH_R1_OPX0 (SRL) -#define MASK_R1_SRL MASK_R1_OPX0 -#define MATCH_R1_SRLI MATCH_R1_OPX (SRLI, 0, 0, 0) -#define MASK_R1_SRLI MASK_R1_OPX (0, 1, 0, 0) -#define MATCH_R1_STB MATCH_R1_OP (STB) -#define MASK_R1_STB MASK_R1_OP -#define MATCH_R1_STBIO MATCH_R1_OP (STBIO) -#define MASK_R1_STBIO MASK_R1_OP -#define MATCH_R1_STH MATCH_R1_OP (STH) -#define MASK_R1_STH MASK_R1_OP -#define MATCH_R1_STHIO MATCH_R1_OP (STHIO) -#define MASK_R1_STHIO MASK_R1_OP -#define MATCH_R1_STW MATCH_R1_OP (STW) -#define MASK_R1_STW MASK_R1_OP -#define MATCH_R1_STWIO MATCH_R1_OP (STWIO) -#define MASK_R1_STWIO MASK_R1_OP -#define MATCH_R1_SUB MATCH_R1_OPX0 (SUB) -#define MASK_R1_SUB MASK_R1_OPX0 -#define MATCH_R1_SUBI MATCH_R1_OP (ADDI) -#define MASK_R1_SUBI MASK_R1_OP -#define MATCH_R1_SYNC MATCH_R1_OPX (SYNC, 0, 0, 0) -#define MASK_R1_SYNC MASK_R1_OPX (1, 1, 1, 1) -#define MATCH_R1_TRAP MATCH_R1_OPX (TRAP, 0, 0, 0x1d) -#define MASK_R1_TRAP MASK_R1_OPX (1, 1, 1, 0) -#define MATCH_R1_WRCTL MATCH_R1_OPX (WRCTL, 0, 0, 0) -#define MASK_R1_WRCTL MASK_R1_OPX (0, 1, 1, 0) -#define MATCH_R1_WRPRS MATCH_R1_OPX (WRPRS, 0, 0, 0) -#define MASK_R1_WRPRS MASK_R1_OPX (0, 1, 0, 1) -#define MATCH_R1_XOR MATCH_R1_OPX0 (XOR) -#define MASK_R1_XOR MASK_R1_OPX0 -#define MATCH_R1_XORHI MATCH_R1_OP (XORHI) -#define MASK_R1_XORHI MASK_R1_OP -#define MATCH_R1_XORI MATCH_R1_OP (XORI) -#define MASK_R1_XORI MASK_R1_OP - -#endif /* _NIOS2R1_H */ - -/*#include "nios2r2.h"*/ - -#ifndef _NIOS2R2_H_ -#define _NIOS2R2_H_ - -/* Fields for 32-bit R2 instructions. */ - -#define IW_R2_OP_LSB 0 -#define IW_R2_OP_SIZE 6 -#define IW_R2_OP_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R2_OP_SIZE)) -#define IW_R2_OP_SHIFTED_MASK (IW_R2_OP_UNSHIFTED_MASK << IW_R2_OP_LSB) -#define GET_IW_R2_OP(W) (((W) >> IW_R2_OP_LSB) & IW_R2_OP_UNSHIFTED_MASK) -#define SET_IW_R2_OP(V) (((V) & IW_R2_OP_UNSHIFTED_MASK) << IW_R2_OP_LSB) - -#define IW_L26_IMM26_LSB 6 -#define IW_L26_IMM26_SIZE 26 -#define IW_L26_IMM26_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L26_IMM26_SIZE)) -#define IW_L26_IMM26_SHIFTED_MASK (IW_L26_IMM26_UNSHIFTED_MASK << IW_L26_IMM26_LSB) -#define GET_IW_L26_IMM26(W) (((W) >> IW_L26_IMM26_LSB) & IW_L26_IMM26_UNSHIFTED_MASK) -#define SET_IW_L26_IMM26(V) (((V) & IW_L26_IMM26_UNSHIFTED_MASK) << IW_L26_IMM26_LSB) - -#define IW_F2I16_A_LSB 6 -#define IW_F2I16_A_SIZE 5 -#define IW_F2I16_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2I16_A_SIZE)) -#define IW_F2I16_A_SHIFTED_MASK (IW_F2I16_A_UNSHIFTED_MASK << IW_F2I16_A_LSB) -#define GET_IW_F2I16_A(W) (((W) >> IW_F2I16_A_LSB) & IW_F2I16_A_UNSHIFTED_MASK) -#define SET_IW_F2I16_A(V) (((V) & IW_F2I16_A_UNSHIFTED_MASK) << IW_F2I16_A_LSB) - -#define IW_F2I16_B_LSB 11 -#define IW_F2I16_B_SIZE 5 -#define IW_F2I16_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2I16_B_SIZE)) -#define IW_F2I16_B_SHIFTED_MASK (IW_F2I16_B_UNSHIFTED_MASK << IW_F2I16_B_LSB) -#define GET_IW_F2I16_B(W) (((W) >> IW_F2I16_B_LSB) & IW_F2I16_B_UNSHIFTED_MASK) -#define SET_IW_F2I16_B(V) (((V) & IW_F2I16_B_UNSHIFTED_MASK) << IW_F2I16_B_LSB) - -#define IW_F2I16_IMM16_LSB 16 -#define IW_F2I16_IMM16_SIZE 16 -#define IW_F2I16_IMM16_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2I16_IMM16_SIZE)) -#define IW_F2I16_IMM16_SHIFTED_MASK (IW_F2I16_IMM16_UNSHIFTED_MASK << IW_F2I16_IMM16_LSB) -#define GET_IW_F2I16_IMM16(W) (((W) >> IW_F2I16_IMM16_LSB) & IW_F2I16_IMM16_UNSHIFTED_MASK) -#define SET_IW_F2I16_IMM16(V) (((V) & IW_F2I16_IMM16_UNSHIFTED_MASK) << IW_F2I16_IMM16_LSB) - -/* Common to all three I12-group formats F2X4I12, F1X4I12, F1X4L17. */ -#define IW_I12_X_LSB 28 -#define IW_I12_X_SIZE 4 -#define IW_I12_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I12_X_SIZE)) -#define IW_I12_X_SHIFTED_MASK (IW_I12_X_UNSHIFTED_MASK << IW_I12_X_LSB) -#define GET_IW_I12_X(W) (((W) >> IW_I12_X_LSB) & IW_I12_X_UNSHIFTED_MASK) -#define SET_IW_I12_X(V) (((V) & IW_I12_X_UNSHIFTED_MASK) << IW_I12_X_LSB) - -#define IW_F2X4I12_A_LSB 6 -#define IW_F2X4I12_A_SIZE 5 -#define IW_F2X4I12_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X4I12_A_SIZE)) -#define IW_F2X4I12_A_SHIFTED_MASK (IW_F2X4I12_A_UNSHIFTED_MASK << IW_F2X4I12_A_LSB) -#define GET_IW_F2X4I12_A(W) (((W) >> IW_F2X4I12_A_LSB) & IW_F2X4I12_A_UNSHIFTED_MASK) -#define SET_IW_F2X4I12_A(V) (((V) & IW_F2X4I12_A_UNSHIFTED_MASK) << IW_F2X4I12_A_LSB) - -#define IW_F2X4I12_B_LSB 11 -#define IW_F2X4I12_B_SIZE 5 -#define IW_F2X4I12_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X4I12_B_SIZE)) -#define IW_F2X4I12_B_SHIFTED_MASK (IW_F2X4I12_B_UNSHIFTED_MASK << IW_F2X4I12_B_LSB) -#define GET_IW_F2X4I12_B(W) (((W) >> IW_F2X4I12_B_LSB) & IW_F2X4I12_B_UNSHIFTED_MASK) -#define SET_IW_F2X4I12_B(V) (((V) & IW_F2X4I12_B_UNSHIFTED_MASK) << IW_F2X4I12_B_LSB) - -#define IW_F2X4I12_IMM12_LSB 16 -#define IW_F2X4I12_IMM12_SIZE 12 -#define IW_F2X4I12_IMM12_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X4I12_IMM12_SIZE)) -#define IW_F2X4I12_IMM12_SHIFTED_MASK (IW_F2X4I12_IMM12_UNSHIFTED_MASK << IW_F2X4I12_IMM12_LSB) -#define GET_IW_F2X4I12_IMM12(W) (((W) >> IW_F2X4I12_IMM12_LSB) & IW_F2X4I12_IMM12_UNSHIFTED_MASK) -#define SET_IW_F2X4I12_IMM12(V) (((V) & IW_F2X4I12_IMM12_UNSHIFTED_MASK) << IW_F2X4I12_IMM12_LSB) - -#define IW_F1X4I12_A_LSB 6 -#define IW_F1X4I12_A_SIZE 5 -#define IW_F1X4I12_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4I12_A_SIZE)) -#define IW_F1X4I12_A_SHIFTED_MASK (IW_F1X4I12_A_UNSHIFTED_MASK << IW_F1X4I12_A_LSB) -#define GET_IW_F1X4I12_A(W) (((W) >> IW_F1X4I12_A_LSB) & IW_F1X4I12_A_UNSHIFTED_MASK) -#define SET_IW_F1X4I12_A(V) (((V) & IW_F1X4I12_A_UNSHIFTED_MASK) << IW_F1X4I12_A_LSB) - -#define IW_F1X4I12_X_LSB 11 -#define IW_F1X4I12_X_SIZE 5 -#define IW_F1X4I12_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4I12_X_SIZE)) -#define IW_F1X4I12_X_SHIFTED_MASK (IW_F1X4I12_X_UNSHIFTED_MASK << IW_F1X4I12_X_LSB) -#define GET_IW_F1X4I12_X(W) (((W) >> IW_F1X4I12_X_LSB) & IW_F1X4I12_X_UNSHIFTED_MASK) -#define SET_IW_F1X4I12_X(V) (((V) & IW_F1X4I12_X_UNSHIFTED_MASK) << IW_F1X4I12_X_LSB) - -#define IW_F1X4I12_IMM12_LSB 16 -#define IW_F1X4I12_IMM12_SIZE 12 -#define IW_F1X4I12_IMM12_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4I12_IMM12_SIZE)) -#define IW_F1X4I12_IMM12_SHIFTED_MASK (IW_F1X4I12_IMM12_UNSHIFTED_MASK << IW_F1X4I12_IMM12_LSB) -#define GET_IW_F1X4I12_IMM12(W) (((W) >> IW_F1X4I12_IMM12_LSB) & IW_F1X4I12_IMM12_UNSHIFTED_MASK) -#define SET_IW_F1X4I12_IMM12(V) (((V) & IW_F1X4I12_IMM12_UNSHIFTED_MASK) << IW_F1X4I12_IMM12_LSB) - -#define IW_F1X4L17_A_LSB 6 -#define IW_F1X4L17_A_SIZE 5 -#define IW_F1X4L17_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_A_SIZE)) -#define IW_F1X4L17_A_SHIFTED_MASK (IW_F1X4L17_A_UNSHIFTED_MASK << IW_F1X4L17_A_LSB) -#define GET_IW_F1X4L17_A(W) (((W) >> IW_F1X4L17_A_LSB) & IW_F1X4L17_A_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_A(V) (((V) & IW_F1X4L17_A_UNSHIFTED_MASK) << IW_F1X4L17_A_LSB) - -#define IW_F1X4L17_ID_LSB 11 -#define IW_F1X4L17_ID_SIZE 1 -#define IW_F1X4L17_ID_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_ID_SIZE)) -#define IW_F1X4L17_ID_SHIFTED_MASK (IW_F1X4L17_ID_UNSHIFTED_MASK << IW_F1X4L17_ID_LSB) -#define GET_IW_F1X4L17_ID(W) (((W) >> IW_F1X4L17_ID_LSB) & IW_F1X4L17_ID_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_ID(V) (((V) & IW_F1X4L17_ID_UNSHIFTED_MASK) << IW_F1X4L17_ID_LSB) - -#define IW_F1X4L17_WB_LSB 12 -#define IW_F1X4L17_WB_SIZE 1 -#define IW_F1X4L17_WB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_WB_SIZE)) -#define IW_F1X4L17_WB_SHIFTED_MASK (IW_F1X4L17_WB_UNSHIFTED_MASK << IW_F1X4L17_WB_LSB) -#define GET_IW_F1X4L17_WB(W) (((W) >> IW_F1X4L17_WB_LSB) & IW_F1X4L17_WB_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_WB(V) (((V) & IW_F1X4L17_WB_UNSHIFTED_MASK) << IW_F1X4L17_WB_LSB) - -#define IW_F1X4L17_RS_LSB 13 -#define IW_F1X4L17_RS_SIZE 1 -#define IW_F1X4L17_RS_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_RS_SIZE)) -#define IW_F1X4L17_RS_SHIFTED_MASK (IW_F1X4L17_RS_UNSHIFTED_MASK << IW_F1X4L17_RS_LSB) -#define GET_IW_F1X4L17_RS(W) (((W) >> IW_F1X4L17_RS_LSB) & IW_F1X4L17_RS_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_RS(V) (((V) & IW_F1X4L17_RS_UNSHIFTED_MASK) << IW_F1X4L17_RS_LSB) - -#define IW_F1X4L17_PC_LSB 14 -#define IW_F1X4L17_PC_SIZE 1 -#define IW_F1X4L17_PC_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_PC_SIZE)) -#define IW_F1X4L17_PC_SHIFTED_MASK (IW_F1X4L17_PC_UNSHIFTED_MASK << IW_F1X4L17_PC_LSB) -#define GET_IW_F1X4L17_PC(W) (((W) >> IW_F1X4L17_PC_LSB) & IW_F1X4L17_PC_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_PC(V) (((V) & IW_F1X4L17_PC_UNSHIFTED_MASK) << IW_F1X4L17_PC_LSB) - -#define IW_F1X4L17_RSV_LSB 15 -#define IW_F1X4L17_RSV_SIZE 1 -#define IW_F1X4L17_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_RSV_SIZE)) -#define IW_F1X4L17_RSV_SHIFTED_MASK (IW_F1X4L17_RSV_UNSHIFTED_MASK << IW_F1X4L17_RSV_LSB) -#define GET_IW_F1X4L17_RSV(W) (((W) >> IW_F1X4L17_RSV_LSB) & IW_F1X4L17_RSV_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_RSV(V) (((V) & IW_F1X4L17_RSV_UNSHIFTED_MASK) << IW_F1X4L17_RSV_LSB) - -#define IW_F1X4L17_REGMASK_LSB 16 -#define IW_F1X4L17_REGMASK_SIZE 12 -#define IW_F1X4L17_REGMASK_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X4L17_REGMASK_SIZE)) -#define IW_F1X4L17_REGMASK_SHIFTED_MASK (IW_F1X4L17_REGMASK_UNSHIFTED_MASK << IW_F1X4L17_REGMASK_LSB) -#define GET_IW_F1X4L17_REGMASK(W) (((W) >> IW_F1X4L17_REGMASK_LSB) & IW_F1X4L17_REGMASK_UNSHIFTED_MASK) -#define SET_IW_F1X4L17_REGMASK(V) (((V) & IW_F1X4L17_REGMASK_UNSHIFTED_MASK) << IW_F1X4L17_REGMASK_LSB) - -/* Shared by OPX-group formats F3X6L5, F2X6L10, F3X6. */ -#define IW_OPX_X_LSB 26 -#define IW_OPX_X_SIZE 6 -#define IW_OPX_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_OPX_X_SIZE)) -#define IW_OPX_X_SHIFTED_MASK (IW_OPX_X_UNSHIFTED_MASK << IW_OPX_X_LSB) -#define GET_IW_OPX_X(W) (((W) >> IW_OPX_X_LSB) & IW_OPX_X_UNSHIFTED_MASK) -#define SET_IW_OPX_X(V) (((V) & IW_OPX_X_UNSHIFTED_MASK) << IW_OPX_X_LSB) - -/* F3X6L5 accessors are also used for F3X6 formats. */ -#define IW_F3X6L5_A_LSB 6 -#define IW_F3X6L5_A_SIZE 5 -#define IW_F3X6L5_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_A_SIZE)) -#define IW_F3X6L5_A_SHIFTED_MASK (IW_F3X6L5_A_UNSHIFTED_MASK << IW_F3X6L5_A_LSB) -#define GET_IW_F3X6L5_A(W) (((W) >> IW_F3X6L5_A_LSB) & IW_F3X6L5_A_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_A(V) (((V) & IW_F3X6L5_A_UNSHIFTED_MASK) << IW_F3X6L5_A_LSB) - -#define IW_F3X6L5_B_LSB 11 -#define IW_F3X6L5_B_SIZE 5 -#define IW_F3X6L5_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_B_SIZE)) -#define IW_F3X6L5_B_SHIFTED_MASK (IW_F3X6L5_B_UNSHIFTED_MASK << IW_F3X6L5_B_LSB) -#define GET_IW_F3X6L5_B(W) (((W) >> IW_F3X6L5_B_LSB) & IW_F3X6L5_B_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_B(V) (((V) & IW_F3X6L5_B_UNSHIFTED_MASK) << IW_F3X6L5_B_LSB) - -#define IW_F3X6L5_C_LSB 16 -#define IW_F3X6L5_C_SIZE 5 -#define IW_F3X6L5_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_C_SIZE)) -#define IW_F3X6L5_C_SHIFTED_MASK (IW_F3X6L5_C_UNSHIFTED_MASK << IW_F3X6L5_C_LSB) -#define GET_IW_F3X6L5_C(W) (((W) >> IW_F3X6L5_C_LSB) & IW_F3X6L5_C_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_C(V) (((V) & IW_F3X6L5_C_UNSHIFTED_MASK) << IW_F3X6L5_C_LSB) - -#define IW_F3X6L5_IMM5_LSB 21 -#define IW_F3X6L5_IMM5_SIZE 5 -#define IW_F3X6L5_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X6L5_IMM5_SIZE)) -#define IW_F3X6L5_IMM5_SHIFTED_MASK (IW_F3X6L5_IMM5_UNSHIFTED_MASK << IW_F3X6L5_IMM5_LSB) -#define GET_IW_F3X6L5_IMM5(W) (((W) >> IW_F3X6L5_IMM5_LSB) & IW_F3X6L5_IMM5_UNSHIFTED_MASK) -#define SET_IW_F3X6L5_IMM5(V) (((V) & IW_F3X6L5_IMM5_UNSHIFTED_MASK) << IW_F3X6L5_IMM5_LSB) - -#define IW_F2X6L10_A_LSB 6 -#define IW_F2X6L10_A_SIZE 5 -#define IW_F2X6L10_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_A_SIZE)) -#define IW_F2X6L10_A_SHIFTED_MASK (IW_F2X6L10_A_UNSHIFTED_MASK << IW_F2X6L10_A_LSB) -#define GET_IW_F2X6L10_A(W) (((W) >> IW_F2X6L10_A_LSB) & IW_F2X6L10_A_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_A(V) (((V) & IW_F2X6L10_A_UNSHIFTED_MASK) << IW_F2X6L10_A_LSB) - -#define IW_F2X6L10_B_LSB 11 -#define IW_F2X6L10_B_SIZE 5 -#define IW_F2X6L10_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_B_SIZE)) -#define IW_F2X6L10_B_SHIFTED_MASK (IW_F2X6L10_B_UNSHIFTED_MASK << IW_F2X6L10_B_LSB) -#define GET_IW_F2X6L10_B(W) (((W) >> IW_F2X6L10_B_LSB) & IW_F2X6L10_B_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_B(V) (((V) & IW_F2X6L10_B_UNSHIFTED_MASK) << IW_F2X6L10_B_LSB) - -#define IW_F2X6L10_LSB_LSB 16 -#define IW_F2X6L10_LSB_SIZE 5 -#define IW_F2X6L10_LSB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_LSB_SIZE)) -#define IW_F2X6L10_LSB_SHIFTED_MASK (IW_F2X6L10_LSB_UNSHIFTED_MASK << IW_F2X6L10_LSB_LSB) -#define GET_IW_F2X6L10_LSB(W) (((W) >> IW_F2X6L10_LSB_LSB) & IW_F2X6L10_LSB_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_LSB(V) (((V) & IW_F2X6L10_LSB_UNSHIFTED_MASK) << IW_F2X6L10_LSB_LSB) - -#define IW_F2X6L10_MSB_LSB 21 -#define IW_F2X6L10_MSB_SIZE 5 -#define IW_F2X6L10_MSB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2X6L10_MSB_SIZE)) -#define IW_F2X6L10_MSB_SHIFTED_MASK (IW_F2X6L10_MSB_UNSHIFTED_MASK << IW_F2X6L10_MSB_LSB) -#define GET_IW_F2X6L10_MSB(W) (((W) >> IW_F2X6L10_MSB_LSB) & IW_F2X6L10_MSB_UNSHIFTED_MASK) -#define SET_IW_F2X6L10_MSB(V) (((V) & IW_F2X6L10_MSB_UNSHIFTED_MASK) << IW_F2X6L10_MSB_LSB) - -#define IW_F3X8_A_LSB 6 -#define IW_F3X8_A_SIZE 5 -#define IW_F3X8_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_A_SIZE)) -#define IW_F3X8_A_SHIFTED_MASK (IW_F3X8_A_UNSHIFTED_MASK << IW_F3X8_A_LSB) -#define GET_IW_F3X8_A(W) (((W) >> IW_F3X8_A_LSB) & IW_F3X8_A_UNSHIFTED_MASK) -#define SET_IW_F3X8_A(V) (((V) & IW_F3X8_A_UNSHIFTED_MASK) << IW_F3X8_A_LSB) - -#define IW_F3X8_B_LSB 11 -#define IW_F3X8_B_SIZE 5 -#define IW_F3X8_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_B_SIZE)) -#define IW_F3X8_B_SHIFTED_MASK (IW_F3X8_B_UNSHIFTED_MASK << IW_F3X8_B_LSB) -#define GET_IW_F3X8_B(W) (((W) >> IW_F3X8_B_LSB) & IW_F3X8_B_UNSHIFTED_MASK) -#define SET_IW_F3X8_B(V) (((V) & IW_F3X8_B_UNSHIFTED_MASK) << IW_F3X8_B_LSB) - -#define IW_F3X8_C_LSB 16 -#define IW_F3X8_C_SIZE 5 -#define IW_F3X8_C_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_C_SIZE)) -#define IW_F3X8_C_SHIFTED_MASK (IW_F3X8_C_UNSHIFTED_MASK << IW_F3X8_C_LSB) -#define GET_IW_F3X8_C(W) (((W) >> IW_F3X8_C_LSB) & IW_F3X8_C_UNSHIFTED_MASK) -#define SET_IW_F3X8_C(V) (((V) & IW_F3X8_C_UNSHIFTED_MASK) << IW_F3X8_C_LSB) - -#define IW_F3X8_READA_LSB 21 -#define IW_F3X8_READA_SIZE 1 -#define IW_F3X8_READA_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_READA_SIZE)) -#define IW_F3X8_READA_SHIFTED_MASK (IW_F3X8_READA_UNSHIFTED_MASK << IW_F3X8_READA_LSB) -#define GET_IW_F3X8_READA(W) (((W) >> IW_F3X8_READA_LSB) & IW_F3X8_READA_UNSHIFTED_MASK) -#define SET_IW_F3X8_READA(V) (((V) & IW_F3X8_READA_UNSHIFTED_MASK) << IW_F3X8_READA_LSB) - -#define IW_F3X8_READB_LSB 22 -#define IW_F3X8_READB_SIZE 1 -#define IW_F3X8_READB_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_READB_SIZE)) -#define IW_F3X8_READB_SHIFTED_MASK (IW_F3X8_READB_UNSHIFTED_MASK << IW_F3X8_READB_LSB) -#define GET_IW_F3X8_READB(W) (((W) >> IW_F3X8_READB_LSB) & IW_F3X8_READB_UNSHIFTED_MASK) -#define SET_IW_F3X8_READB(V) (((V) & IW_F3X8_READB_UNSHIFTED_MASK) << IW_F3X8_READB_LSB) - -#define IW_F3X8_READC_LSB 23 -#define IW_F3X8_READC_SIZE 1 -#define IW_F3X8_READC_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_READC_SIZE)) -#define IW_F3X8_READC_SHIFTED_MASK (IW_F3X8_READC_UNSHIFTED_MASK << IW_F3X8_READC_LSB) -#define GET_IW_F3X8_READC(W) (((W) >> IW_F3X8_READC_LSB) & IW_F3X8_READC_UNSHIFTED_MASK) -#define SET_IW_F3X8_READC(V) (((V) & IW_F3X8_READC_UNSHIFTED_MASK) << IW_F3X8_READC_LSB) - -#define IW_F3X8_N_LSB 24 -#define IW_F3X8_N_SIZE 8 -#define IW_F3X8_N_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F3X8_N_SIZE)) -#define IW_F3X8_N_SHIFTED_MASK (IW_F3X8_N_UNSHIFTED_MASK << IW_F3X8_N_LSB) -#define GET_IW_F3X8_N(W) (((W) >> IW_F3X8_N_LSB) & IW_F3X8_N_UNSHIFTED_MASK) -#define SET_IW_F3X8_N(V) (((V) & IW_F3X8_N_UNSHIFTED_MASK) << IW_F3X8_N_LSB) - -/* 16-bit R2 fields. */ - -#define IW_I10_IMM10_LSB 6 -#define IW_I10_IMM10_SIZE 10 -#define IW_I10_IMM10_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_I10_IMM10_SIZE)) -#define IW_I10_IMM10_SHIFTED_MASK (IW_I10_IMM10_UNSHIFTED_MASK << IW_I10_IMM10_LSB) -#define GET_IW_I10_IMM10(W) (((W) >> IW_I10_IMM10_LSB) & IW_I10_IMM10_UNSHIFTED_MASK) -#define SET_IW_I10_IMM10(V) (((V) & IW_I10_IMM10_UNSHIFTED_MASK) << IW_I10_IMM10_LSB) - -#define IW_T1I7_A3_LSB 6 -#define IW_T1I7_A3_SIZE 3 -#define IW_T1I7_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1I7_A3_SIZE)) -#define IW_T1I7_A3_SHIFTED_MASK (IW_T1I7_A3_UNSHIFTED_MASK << IW_T1I7_A3_LSB) -#define GET_IW_T1I7_A3(W) (((W) >> IW_T1I7_A3_LSB) & IW_T1I7_A3_UNSHIFTED_MASK) -#define SET_IW_T1I7_A3(V) (((V) & IW_T1I7_A3_UNSHIFTED_MASK) << IW_T1I7_A3_LSB) - -#define IW_T1I7_IMM7_LSB 9 -#define IW_T1I7_IMM7_SIZE 7 -#define IW_T1I7_IMM7_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1I7_IMM7_SIZE)) -#define IW_T1I7_IMM7_SHIFTED_MASK (IW_T1I7_IMM7_UNSHIFTED_MASK << IW_T1I7_IMM7_LSB) -#define GET_IW_T1I7_IMM7(W) (((W) >> IW_T1I7_IMM7_LSB) & IW_T1I7_IMM7_UNSHIFTED_MASK) -#define SET_IW_T1I7_IMM7(V) (((V) & IW_T1I7_IMM7_UNSHIFTED_MASK) << IW_T1I7_IMM7_LSB) - -#define IW_T2I4_A3_LSB 6 -#define IW_T2I4_A3_SIZE 3 -#define IW_T2I4_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2I4_A3_SIZE)) -#define IW_T2I4_A3_SHIFTED_MASK (IW_T2I4_A3_UNSHIFTED_MASK << IW_T2I4_A3_LSB) -#define GET_IW_T2I4_A3(W) (((W) >> IW_T2I4_A3_LSB) & IW_T2I4_A3_UNSHIFTED_MASK) -#define SET_IW_T2I4_A3(V) (((V) & IW_T2I4_A3_UNSHIFTED_MASK) << IW_T2I4_A3_LSB) - -#define IW_T2I4_B3_LSB 9 -#define IW_T2I4_B3_SIZE 3 -#define IW_T2I4_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2I4_B3_SIZE)) -#define IW_T2I4_B3_SHIFTED_MASK (IW_T2I4_B3_UNSHIFTED_MASK << IW_T2I4_B3_LSB) -#define GET_IW_T2I4_B3(W) (((W) >> IW_T2I4_B3_LSB) & IW_T2I4_B3_UNSHIFTED_MASK) -#define SET_IW_T2I4_B3(V) (((V) & IW_T2I4_B3_UNSHIFTED_MASK) << IW_T2I4_B3_LSB) - -#define IW_T2I4_IMM4_LSB 12 -#define IW_T2I4_IMM4_SIZE 4 -#define IW_T2I4_IMM4_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2I4_IMM4_SIZE)) -#define IW_T2I4_IMM4_SHIFTED_MASK (IW_T2I4_IMM4_UNSHIFTED_MASK << IW_T2I4_IMM4_LSB) -#define GET_IW_T2I4_IMM4(W) (((W) >> IW_T2I4_IMM4_LSB) & IW_T2I4_IMM4_UNSHIFTED_MASK) -#define SET_IW_T2I4_IMM4(V) (((V) & IW_T2I4_IMM4_UNSHIFTED_MASK) << IW_T2I4_IMM4_LSB) - -#define IW_T1X1I6_A3_LSB 6 -#define IW_T1X1I6_A3_SIZE 3 -#define IW_T1X1I6_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1X1I6_A3_SIZE)) -#define IW_T1X1I6_A3_SHIFTED_MASK (IW_T1X1I6_A3_UNSHIFTED_MASK << IW_T1X1I6_A3_LSB) -#define GET_IW_T1X1I6_A3(W) (((W) >> IW_T1X1I6_A3_LSB) & IW_T1X1I6_A3_UNSHIFTED_MASK) -#define SET_IW_T1X1I6_A3(V) (((V) & IW_T1X1I6_A3_UNSHIFTED_MASK) << IW_T1X1I6_A3_LSB) - -#define IW_T1X1I6_IMM6_LSB 9 -#define IW_T1X1I6_IMM6_SIZE 6 -#define IW_T1X1I6_IMM6_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1X1I6_IMM6_SIZE)) -#define IW_T1X1I6_IMM6_SHIFTED_MASK (IW_T1X1I6_IMM6_UNSHIFTED_MASK << IW_T1X1I6_IMM6_LSB) -#define GET_IW_T1X1I6_IMM6(W) (((W) >> IW_T1X1I6_IMM6_LSB) & IW_T1X1I6_IMM6_UNSHIFTED_MASK) -#define SET_IW_T1X1I6_IMM6(V) (((V) & IW_T1X1I6_IMM6_UNSHIFTED_MASK) << IW_T1X1I6_IMM6_LSB) - -#define IW_T1X1I6_X_LSB 15 -#define IW_T1X1I6_X_SIZE 1 -#define IW_T1X1I6_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T1X1I6_X_SIZE)) -#define IW_T1X1I6_X_SHIFTED_MASK (IW_T1X1I6_X_UNSHIFTED_MASK << IW_T1X1I6_X_LSB) -#define GET_IW_T1X1I6_X(W) (((W) >> IW_T1X1I6_X_LSB) & IW_T1X1I6_X_UNSHIFTED_MASK) -#define SET_IW_T1X1I6_X(V) (((V) & IW_T1X1I6_X_UNSHIFTED_MASK) << IW_T1X1I6_X_LSB) - -#define IW_X1I7_IMM7_LSB 6 -#define IW_X1I7_IMM7_SIZE 7 -#define IW_X1I7_IMM7_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X1I7_IMM7_SIZE)) -#define IW_X1I7_IMM7_SHIFTED_MASK (IW_X1I7_IMM7_UNSHIFTED_MASK << IW_X1I7_IMM7_LSB) -#define GET_IW_X1I7_IMM7(W) (((W) >> IW_X1I7_IMM7_LSB) & IW_X1I7_IMM7_UNSHIFTED_MASK) -#define SET_IW_X1I7_IMM7(V) (((V) & IW_X1I7_IMM7_UNSHIFTED_MASK) << IW_X1I7_IMM7_LSB) - -#define IW_X1I7_RSV_LSB 13 -#define IW_X1I7_RSV_SIZE 2 -#define IW_X1I7_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X1I7_RSV_SIZE)) -#define IW_X1I7_RSV_SHIFTED_MASK (IW_X1I7_RSV_UNSHIFTED_MASK << IW_X1I7_RSV_LSB) -#define GET_IW_X1I7_RSV(W) (((W) >> IW_X1I7_RSV_LSB) & IW_X1I7_RSV_UNSHIFTED_MASK) -#define SET_IW_X1I7_RSV(V) (((V) & IW_X1I7_RSV_UNSHIFTED_MASK) << IW_X1I7_RSV_LSB) - -#define IW_X1I7_X_LSB 15 -#define IW_X1I7_X_SIZE 1 -#define IW_X1I7_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X1I7_X_SIZE)) -#define IW_X1I7_X_SHIFTED_MASK (IW_X1I7_X_UNSHIFTED_MASK << IW_X1I7_X_LSB) -#define GET_IW_X1I7_X(W) (((W) >> IW_X1I7_X_LSB) & IW_X1I7_X_UNSHIFTED_MASK) -#define SET_IW_X1I7_X(V) (((V) & IW_X1I7_X_UNSHIFTED_MASK) << IW_X1I7_X_LSB) - -#define IW_L5I4X1_IMM4_LSB 6 -#define IW_L5I4X1_IMM4_SIZE 4 -#define IW_L5I4X1_IMM4_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_IMM4_SIZE)) -#define IW_L5I4X1_IMM4_SHIFTED_MASK (IW_L5I4X1_IMM4_UNSHIFTED_MASK << IW_L5I4X1_IMM4_LSB) -#define GET_IW_L5I4X1_IMM4(W) (((W) >> IW_L5I4X1_IMM4_LSB) & IW_L5I4X1_IMM4_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_IMM4(V) (((V) & IW_L5I4X1_IMM4_UNSHIFTED_MASK) << IW_L5I4X1_IMM4_LSB) - -#define IW_L5I4X1_REGRANGE_LSB 10 -#define IW_L5I4X1_REGRANGE_SIZE 3 -#define IW_L5I4X1_REGRANGE_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_REGRANGE_SIZE)) -#define IW_L5I4X1_REGRANGE_SHIFTED_MASK (IW_L5I4X1_REGRANGE_UNSHIFTED_MASK << IW_L5I4X1_REGRANGE_LSB) -#define GET_IW_L5I4X1_REGRANGE(W) (((W) >> IW_L5I4X1_REGRANGE_LSB) & IW_L5I4X1_REGRANGE_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_REGRANGE(V) (((V) & IW_L5I4X1_REGRANGE_UNSHIFTED_MASK) << IW_L5I4X1_REGRANGE_LSB) - -#define IW_L5I4X1_FP_LSB 13 -#define IW_L5I4X1_FP_SIZE 1 -#define IW_L5I4X1_FP_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_FP_SIZE)) -#define IW_L5I4X1_FP_SHIFTED_MASK (IW_L5I4X1_FP_UNSHIFTED_MASK << IW_L5I4X1_FP_LSB) -#define GET_IW_L5I4X1_FP(W) (((W) >> IW_L5I4X1_FP_LSB) & IW_L5I4X1_FP_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_FP(V) (((V) & IW_L5I4X1_FP_UNSHIFTED_MASK) << IW_L5I4X1_FP_LSB) - -#define IW_L5I4X1_CS_LSB 14 -#define IW_L5I4X1_CS_SIZE 1 -#define IW_L5I4X1_CS_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_CS_SIZE)) -#define IW_L5I4X1_CS_SHIFTED_MASK (IW_L5I4X1_CS_UNSHIFTED_MASK << IW_L5I4X1_CS_LSB) -#define GET_IW_L5I4X1_CS(W) (((W) >> IW_L5I4X1_CS_LSB) & IW_L5I4X1_CS_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_CS(V) (((V) & IW_L5I4X1_CS_UNSHIFTED_MASK) << IW_L5I4X1_CS_LSB) - -#define IW_L5I4X1_X_LSB 15 -#define IW_L5I4X1_X_SIZE 1 -#define IW_L5I4X1_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_L5I4X1_X_SIZE)) -#define IW_L5I4X1_X_SHIFTED_MASK (IW_L5I4X1_X_UNSHIFTED_MASK << IW_L5I4X1_X_LSB) -#define GET_IW_L5I4X1_X(W) (((W) >> IW_L5I4X1_X_LSB) & IW_L5I4X1_X_UNSHIFTED_MASK) -#define SET_IW_L5I4X1_X(V) (((V) & IW_L5I4X1_X_UNSHIFTED_MASK) << IW_L5I4X1_X_LSB) - -#define IW_T2X1L3_A3_LSB 6 -#define IW_T2X1L3_A3_SIZE 3 -#define IW_T2X1L3_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_A3_SIZE)) -#define IW_T2X1L3_A3_SHIFTED_MASK (IW_T2X1L3_A3_UNSHIFTED_MASK << IW_T2X1L3_A3_LSB) -#define GET_IW_T2X1L3_A3(W) (((W) >> IW_T2X1L3_A3_LSB) & IW_T2X1L3_A3_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_A3(V) (((V) & IW_T2X1L3_A3_UNSHIFTED_MASK) << IW_T2X1L3_A3_LSB) - -#define IW_T2X1L3_B3_LSB 9 -#define IW_T2X1L3_B3_SIZE 3 -#define IW_T2X1L3_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_B3_SIZE)) -#define IW_T2X1L3_B3_SHIFTED_MASK (IW_T2X1L3_B3_UNSHIFTED_MASK << IW_T2X1L3_B3_LSB) -#define GET_IW_T2X1L3_B3(W) (((W) >> IW_T2X1L3_B3_LSB) & IW_T2X1L3_B3_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_B3(V) (((V) & IW_T2X1L3_B3_UNSHIFTED_MASK) << IW_T2X1L3_B3_LSB) - -#define IW_T2X1L3_SHAMT_LSB 12 -#define IW_T2X1L3_SHAMT_SIZE 3 -#define IW_T2X1L3_SHAMT_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_SHAMT_SIZE)) -#define IW_T2X1L3_SHAMT_SHIFTED_MASK (IW_T2X1L3_SHAMT_UNSHIFTED_MASK << IW_T2X1L3_SHAMT_LSB) -#define GET_IW_T2X1L3_SHAMT(W) (((W) >> IW_T2X1L3_SHAMT_LSB) & IW_T2X1L3_SHAMT_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_SHAMT(V) (((V) & IW_T2X1L3_SHAMT_UNSHIFTED_MASK) << IW_T2X1L3_SHAMT_LSB) - -#define IW_T2X1L3_X_LSB 15 -#define IW_T2X1L3_X_SIZE 1 -#define IW_T2X1L3_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1L3_X_SIZE)) -#define IW_T2X1L3_X_SHIFTED_MASK (IW_T2X1L3_X_UNSHIFTED_MASK << IW_T2X1L3_X_LSB) -#define GET_IW_T2X1L3_X(W) (((W) >> IW_T2X1L3_X_LSB) & IW_T2X1L3_X_UNSHIFTED_MASK) -#define SET_IW_T2X1L3_X(V) (((V) & IW_T2X1L3_X_UNSHIFTED_MASK) << IW_T2X1L3_X_LSB) - -#define IW_T2X1I3_A3_LSB 6 -#define IW_T2X1I3_A3_SIZE 3 -#define IW_T2X1I3_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_A3_SIZE)) -#define IW_T2X1I3_A3_SHIFTED_MASK (IW_T2X1I3_A3_UNSHIFTED_MASK << IW_T2X1I3_A3_LSB) -#define GET_IW_T2X1I3_A3(W) (((W) >> IW_T2X1I3_A3_LSB) & IW_T2X1I3_A3_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_A3(V) (((V) & IW_T2X1I3_A3_UNSHIFTED_MASK) << IW_T2X1I3_A3_LSB) - -#define IW_T2X1I3_B3_LSB 9 -#define IW_T2X1I3_B3_SIZE 3 -#define IW_T2X1I3_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_B3_SIZE)) -#define IW_T2X1I3_B3_SHIFTED_MASK (IW_T2X1I3_B3_UNSHIFTED_MASK << IW_T2X1I3_B3_LSB) -#define GET_IW_T2X1I3_B3(W) (((W) >> IW_T2X1I3_B3_LSB) & IW_T2X1I3_B3_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_B3(V) (((V) & IW_T2X1I3_B3_UNSHIFTED_MASK) << IW_T2X1I3_B3_LSB) - -#define IW_T2X1I3_IMM3_LSB 12 -#define IW_T2X1I3_IMM3_SIZE 3 -#define IW_T2X1I3_IMM3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_IMM3_SIZE)) -#define IW_T2X1I3_IMM3_SHIFTED_MASK (IW_T2X1I3_IMM3_UNSHIFTED_MASK << IW_T2X1I3_IMM3_LSB) -#define GET_IW_T2X1I3_IMM3(W) (((W) >> IW_T2X1I3_IMM3_LSB) & IW_T2X1I3_IMM3_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_IMM3(V) (((V) & IW_T2X1I3_IMM3_UNSHIFTED_MASK) << IW_T2X1I3_IMM3_LSB) - -#define IW_T2X1I3_X_LSB 15 -#define IW_T2X1I3_X_SIZE 1 -#define IW_T2X1I3_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X1I3_X_SIZE)) -#define IW_T2X1I3_X_SHIFTED_MASK (IW_T2X1I3_X_UNSHIFTED_MASK << IW_T2X1I3_X_LSB) -#define GET_IW_T2X1I3_X(W) (((W) >> IW_T2X1I3_X_LSB) & IW_T2X1I3_X_UNSHIFTED_MASK) -#define SET_IW_T2X1I3_X(V) (((V) & IW_T2X1I3_X_UNSHIFTED_MASK) << IW_T2X1I3_X_LSB) - -#define IW_T3X1_A3_LSB 6 -#define IW_T3X1_A3_SIZE 3 -#define IW_T3X1_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_A3_SIZE)) -#define IW_T3X1_A3_SHIFTED_MASK (IW_T3X1_A3_UNSHIFTED_MASK << IW_T3X1_A3_LSB) -#define GET_IW_T3X1_A3(W) (((W) >> IW_T3X1_A3_LSB) & IW_T3X1_A3_UNSHIFTED_MASK) -#define SET_IW_T3X1_A3(V) (((V) & IW_T3X1_A3_UNSHIFTED_MASK) << IW_T3X1_A3_LSB) - -#define IW_T3X1_B3_LSB 9 -#define IW_T3X1_B3_SIZE 3 -#define IW_T3X1_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_B3_SIZE)) -#define IW_T3X1_B3_SHIFTED_MASK (IW_T3X1_B3_UNSHIFTED_MASK << IW_T3X1_B3_LSB) -#define GET_IW_T3X1_B3(W) (((W) >> IW_T3X1_B3_LSB) & IW_T3X1_B3_UNSHIFTED_MASK) -#define SET_IW_T3X1_B3(V) (((V) & IW_T3X1_B3_UNSHIFTED_MASK) << IW_T3X1_B3_LSB) - -#define IW_T3X1_C3_LSB 12 -#define IW_T3X1_C3_SIZE 3 -#define IW_T3X1_C3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_C3_SIZE)) -#define IW_T3X1_C3_SHIFTED_MASK (IW_T3X1_C3_UNSHIFTED_MASK << IW_T3X1_C3_LSB) -#define GET_IW_T3X1_C3(W) (((W) >> IW_T3X1_C3_LSB) & IW_T3X1_C3_UNSHIFTED_MASK) -#define SET_IW_T3X1_C3(V) (((V) & IW_T3X1_C3_UNSHIFTED_MASK) << IW_T3X1_C3_LSB) - -#define IW_T3X1_X_LSB 15 -#define IW_T3X1_X_SIZE 1 -#define IW_T3X1_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T3X1_X_SIZE)) -#define IW_T3X1_X_SHIFTED_MASK (IW_T3X1_X_UNSHIFTED_MASK << IW_T3X1_X_LSB) -#define GET_IW_T3X1_X(W) (((W) >> IW_T3X1_X_LSB) & IW_T3X1_X_UNSHIFTED_MASK) -#define SET_IW_T3X1_X(V) (((V) & IW_T3X1_X_UNSHIFTED_MASK) << IW_T3X1_X_LSB) - -/* The X field for all three R.N-class instruction formats is represented - here as 4 bits, including the bits defined as constant 0 or 1 that - determine which of the formats T2X3, F1X1, or X2L5 it is. */ -#define IW_R_N_X_LSB 12 -#define IW_R_N_X_SIZE 4 -#define IW_R_N_X_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_R_N_X_SIZE)) -#define IW_R_N_X_SHIFTED_MASK (IW_R_N_X_UNSHIFTED_MASK << IW_R_N_X_LSB) -#define GET_IW_R_N_X(W) (((W) >> IW_R_N_X_LSB) & IW_R_N_X_UNSHIFTED_MASK) -#define SET_IW_R_N_X(V) (((V) & IW_R_N_X_UNSHIFTED_MASK) << IW_R_N_X_LSB) - -#define IW_T2X3_A3_LSB 6 -#define IW_T2X3_A3_SIZE 3 -#define IW_T2X3_A3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X3_A3_SIZE)) -#define IW_T2X3_A3_SHIFTED_MASK (IW_T2X3_A3_UNSHIFTED_MASK << IW_T2X3_A3_LSB) -#define GET_IW_T2X3_A3(W) (((W) >> IW_T2X3_A3_LSB) & IW_T2X3_A3_UNSHIFTED_MASK) -#define SET_IW_T2X3_A3(V) (((V) & IW_T2X3_A3_UNSHIFTED_MASK) << IW_T2X3_A3_LSB) - -#define IW_T2X3_B3_LSB 9 -#define IW_T2X3_B3_SIZE 3 -#define IW_T2X3_B3_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_T2X3_B3_SIZE)) -#define IW_T2X3_B3_SHIFTED_MASK (IW_T2X3_B3_UNSHIFTED_MASK << IW_T2X3_B3_LSB) -#define GET_IW_T2X3_B3(W) (((W) >> IW_T2X3_B3_LSB) & IW_T2X3_B3_UNSHIFTED_MASK) -#define SET_IW_T2X3_B3(V) (((V) & IW_T2X3_B3_UNSHIFTED_MASK) << IW_T2X3_B3_LSB) - -#define IW_F1X1_A_LSB 6 -#define IW_F1X1_A_SIZE 5 -#define IW_F1X1_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X1_A_SIZE)) -#define IW_F1X1_A_SHIFTED_MASK (IW_F1X1_A_UNSHIFTED_MASK << IW_F1X1_A_LSB) -#define GET_IW_F1X1_A(W) (((W) >> IW_F1X1_A_LSB) & IW_F1X1_A_UNSHIFTED_MASK) -#define SET_IW_F1X1_A(V) (((V) & IW_F1X1_A_UNSHIFTED_MASK) << IW_F1X1_A_LSB) - -#define IW_F1X1_RSV_LSB 11 -#define IW_F1X1_RSV_SIZE 1 -#define IW_F1X1_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1X1_RSV_SIZE)) -#define IW_F1X1_RSV_SHIFTED_MASK (IW_F1X1_RSV_UNSHIFTED_MASK << IW_F1X1_RSV_LSB) -#define GET_IW_F1X1_RSV(W) (((W) >> IW_F1X1_RSV_LSB) & IW_F1X1_RSV_UNSHIFTED_MASK) -#define SET_IW_F1X1_RSV(V) (((V) & IW_F1X1_RSV_UNSHIFTED_MASK) << IW_F1X1_RSV_LSB) - -#define IW_X2L5_IMM5_LSB 6 -#define IW_X2L5_IMM5_SIZE 5 -#define IW_X2L5_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X2L5_IMM5_SIZE)) -#define IW_X2L5_IMM5_SHIFTED_MASK (IW_X2L5_IMM5_UNSHIFTED_MASK << IW_X2L5_IMM5_LSB) -#define GET_IW_X2L5_IMM5(W) (((W) >> IW_X2L5_IMM5_LSB) & IW_X2L5_IMM5_UNSHIFTED_MASK) -#define SET_IW_X2L5_IMM5(V) (((V) & IW_X2L5_IMM5_UNSHIFTED_MASK) << IW_X2L5_IMM5_LSB) - -#define IW_X2L5_RSV_LSB 11 -#define IW_X2L5_RSV_SIZE 1 -#define IW_X2L5_RSV_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_X2L5_RSV_SIZE)) -#define IW_X2L5_RSV_SHIFTED_MASK (IW_X2L5_RSV_UNSHIFTED_MASK << IW_X2L5_RSV_LSB) -#define GET_IW_X2L5_RSV(W) (((W) >> IW_X2L5_RSV_LSB) & IW_X2L5_RSV_UNSHIFTED_MASK) -#define SET_IW_X2L5_RSV(V) (((V) & IW_X2L5_RSV_UNSHIFTED_MASK) << IW_X2L5_RSV_LSB) - -#define IW_F1I5_IMM5_LSB 6 -#define IW_F1I5_IMM5_SIZE 5 -#define IW_F1I5_IMM5_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1I5_IMM5_SIZE)) -#define IW_F1I5_IMM5_SHIFTED_MASK (IW_F1I5_IMM5_UNSHIFTED_MASK << IW_F1I5_IMM5_LSB) -#define GET_IW_F1I5_IMM5(W) (((W) >> IW_F1I5_IMM5_LSB) & IW_F1I5_IMM5_UNSHIFTED_MASK) -#define SET_IW_F1I5_IMM5(V) (((V) & IW_F1I5_IMM5_UNSHIFTED_MASK) << IW_F1I5_IMM5_LSB) - -#define IW_F1I5_B_LSB 11 -#define IW_F1I5_B_SIZE 5 -#define IW_F1I5_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F1I5_B_SIZE)) -#define IW_F1I5_B_SHIFTED_MASK (IW_F1I5_B_UNSHIFTED_MASK << IW_F1I5_B_LSB) -#define GET_IW_F1I5_B(W) (((W) >> IW_F1I5_B_LSB) & IW_F1I5_B_UNSHIFTED_MASK) -#define SET_IW_F1I5_B(V) (((V) & IW_F1I5_B_UNSHIFTED_MASK) << IW_F1I5_B_LSB) - -#define IW_F2_A_LSB 6 -#define IW_F2_A_SIZE 5 -#define IW_F2_A_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2_A_SIZE)) -#define IW_F2_A_SHIFTED_MASK (IW_F2_A_UNSHIFTED_MASK << IW_F2_A_LSB) -#define GET_IW_F2_A(W) (((W) >> IW_F2_A_LSB) & IW_F2_A_UNSHIFTED_MASK) -#define SET_IW_F2_A(V) (((V) & IW_F2_A_UNSHIFTED_MASK) << IW_F2_A_LSB) - -#define IW_F2_B_LSB 11 -#define IW_F2_B_SIZE 5 -#define IW_F2_B_UNSHIFTED_MASK (0xffffffffu >> (32 - IW_F2_B_SIZE)) -#define IW_F2_B_SHIFTED_MASK (IW_F2_B_UNSHIFTED_MASK << IW_F2_B_LSB) -#define GET_IW_F2_B(W) (((W) >> IW_F2_B_LSB) & IW_F2_B_UNSHIFTED_MASK) -#define SET_IW_F2_B(V) (((V) & IW_F2_B_UNSHIFTED_MASK) << IW_F2_B_LSB) - -/* R2 opcodes. */ -#define R2_OP_CALL 0 -#define R2_OP_AS_N 1 -#define R2_OP_BR 2 -#define R2_OP_BR_N 3 -#define R2_OP_ADDI 4 -#define R2_OP_LDBU_N 5 -#define R2_OP_LDBU 6 -#define R2_OP_LDB 7 -#define R2_OP_JMPI 8 -#define R2_OP_R_N 9 -#define R2_OP_ANDI_N 11 -#define R2_OP_ANDI 12 -#define R2_OP_LDHU_N 13 -#define R2_OP_LDHU 14 -#define R2_OP_LDH 15 -#define R2_OP_ASI_N 17 -#define R2_OP_BGE 18 -#define R2_OP_LDWSP_N 19 -#define R2_OP_ORI 20 -#define R2_OP_LDW_N 21 -#define R2_OP_CMPGEI 22 -#define R2_OP_LDW 23 -#define R2_OP_SHI_N 25 -#define R2_OP_BLT 26 -#define R2_OP_MOVI_N 27 -#define R2_OP_XORI 28 -#define R2_OP_STZ_N 29 -#define R2_OP_CMPLTI 30 -#define R2_OP_ANDCI 31 -#define R2_OP_OPX 32 -#define R2_OP_PP_N 33 -#define R2_OP_BNE 34 -#define R2_OP_BNEZ_N 35 -#define R2_OP_MULI 36 -#define R2_OP_STB_N 37 -#define R2_OP_CMPNEI 38 -#define R2_OP_STB 39 -#define R2_OP_I12 40 -#define R2_OP_SPI_N 41 -#define R2_OP_BEQ 42 -#define R2_OP_BEQZ_N 43 -#define R2_OP_ANDHI 44 -#define R2_OP_STH_N 45 -#define R2_OP_CMPEQI 46 -#define R2_OP_STH 47 -#define R2_OP_CUSTOM 48 -#define R2_OP_BGEU 50 -#define R2_OP_STWSP_N 51 -#define R2_OP_ORHI 52 -#define R2_OP_STW_N 53 -#define R2_OP_CMPGEUI 54 -#define R2_OP_STW 55 -#define R2_OP_BLTU 58 -#define R2_OP_MOV_N 59 -#define R2_OP_XORHI 60 -#define R2_OP_SPADDI_N 61 -#define R2_OP_CMPLTUI 62 -#define R2_OP_ANDCHI 63 - -#define R2_OPX_WRPIE 0 -#define R2_OPX_ERET 1 -#define R2_OPX_ROLI 2 -#define R2_OPX_ROL 3 -#define R2_OPX_FLUSHP 4 -#define R2_OPX_RET 5 -#define R2_OPX_NOR 6 -#define R2_OPX_MULXUU 7 -#define R2_OPX_ENI 8 -#define R2_OPX_BRET 9 -#define R2_OPX_ROR 11 -#define R2_OPX_FLUSHI 12 -#define R2_OPX_JMP 13 -#define R2_OPX_AND 14 -#define R2_OPX_CMPGE 16 -#define R2_OPX_SLLI 18 -#define R2_OPX_SLL 19 -#define R2_OPX_WRPRS 20 -#define R2_OPX_OR 22 -#define R2_OPX_MULXSU 23 -#define R2_OPX_CMPLT 24 -#define R2_OPX_SRLI 26 -#define R2_OPX_SRL 27 -#define R2_OPX_NEXTPC 28 -#define R2_OPX_CALLR 29 -#define R2_OPX_XOR 30 -#define R2_OPX_MULXSS 31 -#define R2_OPX_CMPNE 32 -#define R2_OPX_INSERT 35 -#define R2_OPX_DIVU 36 -#define R2_OPX_DIV 37 -#define R2_OPX_RDCTL 38 -#define R2_OPX_MUL 39 -#define R2_OPX_CMPEQ 40 -#define R2_OPX_INITI 41 -#define R2_OPX_MERGE 43 -#define R2_OPX_HBREAK 44 -#define R2_OPX_TRAP 45 -#define R2_OPX_WRCTL 46 -#define R2_OPX_CMPGEU 48 -#define R2_OPX_ADD 49 -#define R2_OPX_EXTRACT 51 -#define R2_OPX_BREAK 52 -#define R2_OPX_LDEX 53 -#define R2_OPX_SYNC 54 -#define R2_OPX_LDSEX 55 -#define R2_OPX_CMPLTU 56 -#define R2_OPX_SUB 57 -#define R2_OPX_SRAI 58 -#define R2_OPX_SRA 59 -#define R2_OPX_STEX 61 -#define R2_OPX_STSEX 63 - -#define R2_I12_LDBIO 0 -#define R2_I12_STBIO 1 -#define R2_I12_LDBUIO 2 -#define R2_I12_DCACHE 3 -#define R2_I12_LDHIO 4 -#define R2_I12_STHIO 5 -#define R2_I12_LDHUIO 6 -#define R2_I12_RDPRS 7 -#define R2_I12_LDWIO 8 -#define R2_I12_STWIO 9 -#define R2_I12_LDWM 12 -#define R2_I12_STWM 13 - -#define R2_DCACHE_INITD 0 -#define R2_DCACHE_INITDA 1 -#define R2_DCACHE_FLUSHD 2 -#define R2_DCACHE_FLUSHDA 3 - -#define R2_AS_N_ADD_N 0 -#define R2_AS_N_SUB_N 1 - -#define R2_R_N_AND_N 0 -#define R2_R_N_OR_N 2 -#define R2_R_N_XOR_N 3 -#define R2_R_N_SLL_N 4 -#define R2_R_N_SRL_N 5 -#define R2_R_N_NOT_N 6 -#define R2_R_N_NEG_N 7 -#define R2_R_N_CALLR_N 8 -#define R2_R_N_JMPR_N 10 -#define R2_R_N_BREAK_N 12 -#define R2_R_N_TRAP_N 13 -#define R2_R_N_RET_N 14 - -#define R2_SPI_N_SPINCI_N 0 -#define R2_SPI_N_SPDECI_N 1 - -#define R2_ASI_N_ADDI_N 0 -#define R2_ASI_N_SUBI_N 1 - -#define R2_SHI_N_SLLI_N 0 -#define R2_SHI_N_SRLI_N 1 - -#define R2_PP_N_POP_N 0 -#define R2_PP_N_PUSH_N 1 - -#define R2_STZ_N_STWZ_N 0 -#define R2_STZ_N_STBZ_N 1 - -/* Convenience macros for R2 encodings. */ - -#define MATCH_R2_OP(NAME) \ - (SET_IW_R2_OP (R2_OP_##NAME)) -#define MASK_R2_OP \ - IW_R2_OP_SHIFTED_MASK - -#define MATCH_R2_OPX0(NAME) \ - (SET_IW_R2_OP (R2_OP_OPX) | SET_IW_OPX_X (R2_OPX_##NAME)) -#define MASK_R2_OPX0 \ - (IW_R2_OP_SHIFTED_MASK | IW_OPX_X_SHIFTED_MASK \ - | IW_F3X6L5_IMM5_SHIFTED_MASK) - -#define MATCH_R2_OPX(NAME, A, B, C) \ - (MATCH_R2_OPX0 (NAME) | SET_IW_F3X6L5_A (A) | SET_IW_F3X6L5_B (B) \ - | SET_IW_F3X6L5_C (C)) -#define MASK_R2_OPX(A, B, C, N) \ - (IW_R2_OP_SHIFTED_MASK | IW_OPX_X_SHIFTED_MASK \ - | (A ? IW_F3X6L5_A_SHIFTED_MASK : 0) \ - | (B ? IW_F3X6L5_B_SHIFTED_MASK : 0) \ - | (C ? IW_F3X6L5_C_SHIFTED_MASK : 0) \ - | (N ? IW_F3X6L5_IMM5_SHIFTED_MASK : 0)) - -#define MATCH_R2_I12(NAME) \ - (SET_IW_R2_OP (R2_OP_I12) | SET_IW_I12_X (R2_I12_##NAME)) -#define MASK_R2_I12 \ - (IW_R2_OP_SHIFTED_MASK | IW_I12_X_SHIFTED_MASK ) - -#define MATCH_R2_DCACHE(NAME) \ - (MATCH_R2_I12(DCACHE) | SET_IW_F1X4I12_X (R2_DCACHE_##NAME)) -#define MASK_R2_DCACHE \ - (MASK_R2_I12 | IW_F1X4I12_X_SHIFTED_MASK) - -#define MATCH_R2_R_N(NAME) \ - (SET_IW_R2_OP (R2_OP_R_N) | SET_IW_R_N_X (R2_R_N_##NAME)) -#define MASK_R2_R_N \ - (IW_R2_OP_SHIFTED_MASK | IW_R_N_X_SHIFTED_MASK ) - -/* Match/mask macros for R2 instructions. */ - -#define MATCH_R2_ADD MATCH_R2_OPX0 (ADD) -#define MASK_R2_ADD MASK_R2_OPX0 -#define MATCH_R2_ADDI MATCH_R2_OP (ADDI) -#define MASK_R2_ADDI MASK_R2_OP -#define MATCH_R2_ADD_N (MATCH_R2_OP (AS_N) | SET_IW_T3X1_X (R2_AS_N_ADD_N)) -#define MASK_R2_ADD_N (MASK_R2_OP | IW_T3X1_X_SHIFTED_MASK) -#define MATCH_R2_ADDI_N (MATCH_R2_OP (ASI_N) | SET_IW_T2X1I3_X (R2_ASI_N_ADDI_N)) -#define MASK_R2_ADDI_N (MASK_R2_OP | IW_T2X1I3_X_SHIFTED_MASK) -#define MATCH_R2_AND MATCH_R2_OPX0 (AND) -#define MASK_R2_AND MASK_R2_OPX0 -#define MATCH_R2_ANDCHI MATCH_R2_OP (ANDCHI) -#define MASK_R2_ANDCHI MASK_R2_OP -#define MATCH_R2_ANDCI MATCH_R2_OP (ANDCI) -#define MASK_R2_ANDCI MASK_R2_OP -#define MATCH_R2_ANDHI MATCH_R2_OP (ANDHI) -#define MASK_R2_ANDHI MASK_R2_OP -#define MATCH_R2_ANDI MATCH_R2_OP (ANDI) -#define MASK_R2_ANDI MASK_R2_OP -#define MATCH_R2_ANDI_N MATCH_R2_OP (ANDI_N) -#define MASK_R2_ANDI_N MASK_R2_OP -#define MATCH_R2_AND_N MATCH_R2_R_N (AND_N) -#define MASK_R2_AND_N MASK_R2_R_N -#define MATCH_R2_BEQ MATCH_R2_OP (BEQ) -#define MASK_R2_BEQ MASK_R2_OP -#define MATCH_R2_BEQZ_N MATCH_R2_OP (BEQZ_N) -#define MASK_R2_BEQZ_N MASK_R2_OP -#define MATCH_R2_BGE MATCH_R2_OP (BGE) -#define MASK_R2_BGE MASK_R2_OP -#define MATCH_R2_BGEU MATCH_R2_OP (BGEU) -#define MASK_R2_BGEU MASK_R2_OP -#define MATCH_R2_BGT MATCH_R2_OP (BLT) -#define MASK_R2_BGT MASK_R2_OP -#define MATCH_R2_BGTU MATCH_R2_OP (BLTU) -#define MASK_R2_BGTU MASK_R2_OP -#define MATCH_R2_BLE MATCH_R2_OP (BGE) -#define MASK_R2_BLE MASK_R2_OP -#define MATCH_R2_BLEU MATCH_R2_OP (BGEU) -#define MASK_R2_BLEU MASK_R2_OP -#define MATCH_R2_BLT MATCH_R2_OP (BLT) -#define MASK_R2_BLT MASK_R2_OP -#define MATCH_R2_BLTU MATCH_R2_OP (BLTU) -#define MASK_R2_BLTU MASK_R2_OP -#define MATCH_R2_BNE MATCH_R2_OP (BNE) -#define MASK_R2_BNE MASK_R2_OP -#define MATCH_R2_BNEZ_N MATCH_R2_OP (BNEZ_N) -#define MASK_R2_BNEZ_N MASK_R2_OP -#define MATCH_R2_BR MATCH_R2_OP (BR) -#define MASK_R2_BR MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK | IW_F2I16_B_SHIFTED_MASK -#define MATCH_R2_BREAK MATCH_R2_OPX (BREAK, 0, 0, 0x1e) -#define MASK_R2_BREAK MASK_R2_OPX (1, 1, 1, 0) -#define MATCH_R2_BREAK_N MATCH_R2_R_N (BREAK_N) -#define MASK_R2_BREAK_N MASK_R2_R_N -#define MATCH_R2_BRET MATCH_R2_OPX (BRET, 0x1e, 0, 0) -#define MASK_R2_BRET MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_BR_N MATCH_R2_OP (BR_N) -#define MASK_R2_BR_N MASK_R2_OP -#define MATCH_R2_CALL MATCH_R2_OP (CALL) -#define MASK_R2_CALL MASK_R2_OP -#define MATCH_R2_CALLR MATCH_R2_OPX (CALLR, 0, 0, 0x1f) -#define MASK_R2_CALLR MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_CALLR_N MATCH_R2_R_N (CALLR_N) -#define MASK_R2_CALLR_N MASK_R2_R_N -#define MATCH_R2_CMPEQ MATCH_R2_OPX0 (CMPEQ) -#define MASK_R2_CMPEQ MASK_R2_OPX0 -#define MATCH_R2_CMPEQI MATCH_R2_OP (CMPEQI) -#define MASK_R2_CMPEQI MASK_R2_OP -#define MATCH_R2_CMPGE MATCH_R2_OPX0 (CMPGE) -#define MASK_R2_CMPGE MASK_R2_OPX0 -#define MATCH_R2_CMPGEI MATCH_R2_OP (CMPGEI) -#define MASK_R2_CMPGEI MASK_R2_OP -#define MATCH_R2_CMPGEU MATCH_R2_OPX0 (CMPGEU) -#define MASK_R2_CMPGEU MASK_R2_OPX0 -#define MATCH_R2_CMPGEUI MATCH_R2_OP (CMPGEUI) -#define MASK_R2_CMPGEUI MASK_R2_OP -#define MATCH_R2_CMPGT MATCH_R2_OPX0 (CMPLT) -#define MASK_R2_CMPGT MASK_R2_OPX0 -#define MATCH_R2_CMPGTI MATCH_R2_OP (CMPGEI) -#define MASK_R2_CMPGTI MASK_R2_OP -#define MATCH_R2_CMPGTU MATCH_R2_OPX0 (CMPLTU) -#define MASK_R2_CMPGTU MASK_R2_OPX0 -#define MATCH_R2_CMPGTUI MATCH_R2_OP (CMPGEUI) -#define MASK_R2_CMPGTUI MASK_R2_OP -#define MATCH_R2_CMPLE MATCH_R2_OPX0 (CMPGE) -#define MASK_R2_CMPLE MASK_R2_OPX0 -#define MATCH_R2_CMPLEI MATCH_R2_OP (CMPLTI) -#define MASK_R2_CMPLEI MASK_R2_OP -#define MATCH_R2_CMPLEU MATCH_R2_OPX0 (CMPGEU) -#define MASK_R2_CMPLEU MASK_R2_OPX0 -#define MATCH_R2_CMPLEUI MATCH_R2_OP (CMPLTUI) -#define MASK_R2_CMPLEUI MASK_R2_OP -#define MATCH_R2_CMPLT MATCH_R2_OPX0 (CMPLT) -#define MASK_R2_CMPLT MASK_R2_OPX0 -#define MATCH_R2_CMPLTI MATCH_R2_OP (CMPLTI) -#define MASK_R2_CMPLTI MASK_R2_OP -#define MATCH_R2_CMPLTU MATCH_R2_OPX0 (CMPLTU) -#define MASK_R2_CMPLTU MASK_R2_OPX0 -#define MATCH_R2_CMPLTUI MATCH_R2_OP (CMPLTUI) -#define MASK_R2_CMPLTUI MASK_R2_OP -#define MATCH_R2_CMPNE MATCH_R2_OPX0 (CMPNE) -#define MASK_R2_CMPNE MASK_R2_OPX0 -#define MATCH_R2_CMPNEI MATCH_R2_OP (CMPNEI) -#define MASK_R2_CMPNEI MASK_R2_OP -#define MATCH_R2_CUSTOM MATCH_R2_OP (CUSTOM) -#define MASK_R2_CUSTOM MASK_R2_OP -#define MATCH_R2_DIV MATCH_R2_OPX0 (DIV) -#define MASK_R2_DIV MASK_R2_OPX0 -#define MATCH_R2_DIVU MATCH_R2_OPX0 (DIVU) -#define MASK_R2_DIVU MASK_R2_OPX0 -#define MATCH_R2_ENI MATCH_R2_OPX (ENI, 0, 0, 0) -#define MASK_R2_ENI MASK_R2_OPX (1, 1, 1, 0) -#define MATCH_R2_ERET MATCH_R2_OPX (ERET, 0x1d, 0x1e, 0) -#define MASK_R2_ERET MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_EXTRACT MATCH_R2_OPX (EXTRACT, 0, 0, 0) -#define MASK_R2_EXTRACT MASK_R2_OPX (0, 0, 0, 0) -#define MATCH_R2_FLUSHD MATCH_R2_DCACHE (FLUSHD) -#define MASK_R2_FLUSHD MASK_R2_DCACHE -#define MATCH_R2_FLUSHDA MATCH_R2_DCACHE (FLUSHDA) -#define MASK_R2_FLUSHDA MASK_R2_DCACHE -#define MATCH_R2_FLUSHI MATCH_R2_OPX (FLUSHI, 0, 0, 0) -#define MASK_R2_FLUSHI MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_FLUSHP MATCH_R2_OPX (FLUSHP, 0, 0, 0) -#define MASK_R2_FLUSHP MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_INITD MATCH_R2_DCACHE (INITD) -#define MASK_R2_INITD MASK_R2_DCACHE -#define MATCH_R2_INITDA MATCH_R2_DCACHE (INITDA) -#define MASK_R2_INITDA MASK_R2_DCACHE -#define MATCH_R2_INITI MATCH_R2_OPX (INITI, 0, 0, 0) -#define MASK_R2_INITI MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_INSERT MATCH_R2_OPX (INSERT, 0, 0, 0) -#define MASK_R2_INSERT MASK_R2_OPX (0, 0, 0, 0) -#define MATCH_R2_JMP MATCH_R2_OPX (JMP, 0, 0, 0) -#define MASK_R2_JMP MASK_R2_OPX (0, 1, 1, 1) -#define MATCH_R2_JMPI MATCH_R2_OP (JMPI) -#define MASK_R2_JMPI MASK_R2_OP -#define MATCH_R2_JMPR_N MATCH_R2_R_N (JMPR_N) -#define MASK_R2_JMPR_N MASK_R2_R_N -#define MATCH_R2_LDB MATCH_R2_OP (LDB) -#define MASK_R2_LDB MASK_R2_OP -#define MATCH_R2_LDBIO MATCH_R2_I12 (LDBIO) -#define MASK_R2_LDBIO MASK_R2_I12 -#define MATCH_R2_LDBU MATCH_R2_OP (LDBU) -#define MASK_R2_LDBU MASK_R2_OP -#define MATCH_R2_LDBUIO MATCH_R2_I12 (LDBUIO) -#define MASK_R2_LDBUIO MASK_R2_I12 -#define MATCH_R2_LDBU_N MATCH_R2_OP (LDBU_N) -#define MASK_R2_LDBU_N MASK_R2_OP -#define MATCH_R2_LDEX MATCH_R2_OPX (LDEX, 0, 0, 0) -#define MASK_R2_LDEX MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_LDH MATCH_R2_OP (LDH) -#define MASK_R2_LDH MASK_R2_OP -#define MATCH_R2_LDHIO MATCH_R2_I12 (LDHIO) -#define MASK_R2_LDHIO MASK_R2_I12 -#define MATCH_R2_LDHU MATCH_R2_OP (LDHU) -#define MASK_R2_LDHU MASK_R2_OP -#define MATCH_R2_LDHUIO MATCH_R2_I12 (LDHUIO) -#define MASK_R2_LDHUIO MASK_R2_I12 -#define MATCH_R2_LDHU_N MATCH_R2_OP (LDHU_N) -#define MASK_R2_LDHU_N MASK_R2_OP -#define MATCH_R2_LDSEX MATCH_R2_OPX (LDSEX, 0, 0, 0) -#define MASK_R2_LDSEX MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_LDW MATCH_R2_OP (LDW) -#define MASK_R2_LDW MASK_R2_OP -#define MATCH_R2_LDWIO MATCH_R2_I12 (LDWIO) -#define MASK_R2_LDWIO MASK_R2_I12 -#define MATCH_R2_LDWM MATCH_R2_I12 (LDWM) -#define MASK_R2_LDWM MASK_R2_I12 -#define MATCH_R2_LDWSP_N MATCH_R2_OP (LDWSP_N) -#define MASK_R2_LDWSP_N MASK_R2_OP -#define MATCH_R2_LDW_N MATCH_R2_OP (LDW_N) -#define MASK_R2_LDW_N MASK_R2_OP -#define MATCH_R2_MERGE MATCH_R2_OPX (MERGE, 0, 0, 0) -#define MASK_R2_MERGE MASK_R2_OPX (0, 0, 0, 0) -#define MATCH_R2_MOV MATCH_R2_OPX (ADD, 0, 0, 0) -#define MASK_R2_MOV MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_MOVHI MATCH_R2_OP (ORHI) | SET_IW_F2I16_A (0) -#define MASK_R2_MOVHI MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK -#define MATCH_R2_MOVI MATCH_R2_OP (ADDI) | SET_IW_F2I16_A (0) -#define MASK_R2_MOVI MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK -#define MATCH_R2_MOVUI MATCH_R2_OP (ORI) | SET_IW_F2I16_A (0) -#define MASK_R2_MOVUI MASK_R2_OP | IW_F2I16_A_SHIFTED_MASK -#define MATCH_R2_MOV_N MATCH_R2_OP (MOV_N) -#define MASK_R2_MOV_N MASK_R2_OP -#define MATCH_R2_MOVI_N MATCH_R2_OP (MOVI_N) -#define MASK_R2_MOVI_N MASK_R2_OP -#define MATCH_R2_MUL MATCH_R2_OPX0 (MUL) -#define MASK_R2_MUL MASK_R2_OPX0 -#define MATCH_R2_MULI MATCH_R2_OP (MULI) -#define MASK_R2_MULI MASK_R2_OP -#define MATCH_R2_MULXSS MATCH_R2_OPX0 (MULXSS) -#define MASK_R2_MULXSS MASK_R2_OPX0 -#define MATCH_R2_MULXSU MATCH_R2_OPX0 (MULXSU) -#define MASK_R2_MULXSU MASK_R2_OPX0 -#define MATCH_R2_MULXUU MATCH_R2_OPX0 (MULXUU) -#define MASK_R2_MULXUU MASK_R2_OPX0 -#define MATCH_R2_NEG_N MATCH_R2_R_N (NEG_N) -#define MASK_R2_NEG_N MASK_R2_R_N -#define MATCH_R2_NEXTPC MATCH_R2_OPX (NEXTPC, 0, 0, 0) -#define MASK_R2_NEXTPC MASK_R2_OPX (1, 1, 0, 1) -#define MATCH_R2_NOP MATCH_R2_OPX (ADD, 0, 0, 0) -#define MASK_R2_NOP MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_NOP_N (MATCH_R2_OP (MOV_N) | SET_IW_F2_A (0) | SET_IW_F2_B (0)) -#define MASK_R2_NOP_N (MASK_R2_OP | IW_F2_A_SHIFTED_MASK | IW_F2_B_SHIFTED_MASK) -#define MATCH_R2_NOR MATCH_R2_OPX0 (NOR) -#define MASK_R2_NOR MASK_R2_OPX0 -#define MATCH_R2_NOT_N MATCH_R2_R_N (NOT_N) -#define MASK_R2_NOT_N MASK_R2_R_N -#define MATCH_R2_OR MATCH_R2_OPX0 (OR) -#define MASK_R2_OR MASK_R2_OPX0 -#define MATCH_R2_OR_N MATCH_R2_R_N (OR_N) -#define MASK_R2_OR_N MASK_R2_R_N -#define MATCH_R2_ORHI MATCH_R2_OP (ORHI) -#define MASK_R2_ORHI MASK_R2_OP -#define MATCH_R2_ORI MATCH_R2_OP (ORI) -#define MASK_R2_ORI MASK_R2_OP -#define MATCH_R2_POP_N (MATCH_R2_OP (PP_N) | SET_IW_L5I4X1_X (R2_PP_N_POP_N)) -#define MASK_R2_POP_N (MASK_R2_OP | IW_L5I4X1_X_SHIFTED_MASK) -#define MATCH_R2_PUSH_N (MATCH_R2_OP (PP_N) | SET_IW_L5I4X1_X (R2_PP_N_PUSH_N)) -#define MASK_R2_PUSH_N (MASK_R2_OP | IW_L5I4X1_X_SHIFTED_MASK) -#define MATCH_R2_RDCTL MATCH_R2_OPX (RDCTL, 0, 0, 0) -#define MASK_R2_RDCTL MASK_R2_OPX (1, 1, 0, 0) -#define MATCH_R2_RDPRS MATCH_R2_I12 (RDPRS) -#define MASK_R2_RDPRS MASK_R2_I12 -#define MATCH_R2_RET MATCH_R2_OPX (RET, 0x1f, 0, 0) -#define MASK_R2_RET MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_RET_N (MATCH_R2_R_N (RET_N) | SET_IW_X2L5_IMM5 (0)) -#define MASK_R2_RET_N (MASK_R2_R_N | IW_X2L5_IMM5_SHIFTED_MASK) -#define MATCH_R2_ROL MATCH_R2_OPX0 (ROL) -#define MASK_R2_ROL MASK_R2_OPX0 -#define MATCH_R2_ROLI MATCH_R2_OPX (ROLI, 0, 0, 0) -#define MASK_R2_ROLI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_ROR MATCH_R2_OPX0 (ROR) -#define MASK_R2_ROR MASK_R2_OPX0 -#define MATCH_R2_SLL MATCH_R2_OPX0 (SLL) -#define MASK_R2_SLL MASK_R2_OPX0 -#define MATCH_R2_SLLI MATCH_R2_OPX (SLLI, 0, 0, 0) -#define MASK_R2_SLLI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_SLL_N MATCH_R2_R_N (SLL_N) -#define MASK_R2_SLL_N MASK_R2_R_N -#define MATCH_R2_SLLI_N (MATCH_R2_OP (SHI_N) | SET_IW_T2X1L3_X (R2_SHI_N_SLLI_N)) -#define MASK_R2_SLLI_N (MASK_R2_OP | IW_T2X1L3_X_SHIFTED_MASK) -#define MATCH_R2_SPADDI_N MATCH_R2_OP (SPADDI_N) -#define MASK_R2_SPADDI_N MASK_R2_OP -#define MATCH_R2_SPDECI_N (MATCH_R2_OP (SPI_N) | SET_IW_X1I7_X (R2_SPI_N_SPDECI_N)) -#define MASK_R2_SPDECI_N (MASK_R2_OP | IW_X1I7_X_SHIFTED_MASK) -#define MATCH_R2_SPINCI_N (MATCH_R2_OP (SPI_N) | SET_IW_X1I7_X (R2_SPI_N_SPINCI_N)) -#define MASK_R2_SPINCI_N (MASK_R2_OP | IW_X1I7_X_SHIFTED_MASK) -#define MATCH_R2_SRA MATCH_R2_OPX0 (SRA) -#define MASK_R2_SRA MASK_R2_OPX0 -#define MATCH_R2_SRAI MATCH_R2_OPX (SRAI, 0, 0, 0) -#define MASK_R2_SRAI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_SRL MATCH_R2_OPX0 (SRL) -#define MASK_R2_SRL MASK_R2_OPX0 -#define MATCH_R2_SRLI MATCH_R2_OPX (SRLI, 0, 0, 0) -#define MASK_R2_SRLI MASK_R2_OPX (0, 1, 0, 0) -#define MATCH_R2_SRL_N MATCH_R2_R_N (SRL_N) -#define MASK_R2_SRL_N MASK_R2_R_N -#define MATCH_R2_SRLI_N (MATCH_R2_OP (SHI_N) | SET_IW_T2X1L3_X (R2_SHI_N_SRLI_N)) -#define MASK_R2_SRLI_N (MASK_R2_OP | IW_T2X1L3_X_SHIFTED_MASK) -#define MATCH_R2_STB MATCH_R2_OP (STB) -#define MASK_R2_STB MASK_R2_OP -#define MATCH_R2_STBIO MATCH_R2_I12 (STBIO) -#define MASK_R2_STBIO MASK_R2_I12 -#define MATCH_R2_STB_N MATCH_R2_OP (STB_N) -#define MASK_R2_STB_N MASK_R2_OP -#define MATCH_R2_STBZ_N (MATCH_R2_OP (STZ_N) | SET_IW_T1X1I6_X (R2_STZ_N_STBZ_N)) -#define MASK_R2_STBZ_N (MASK_R2_OP | IW_T1X1I6_X_SHIFTED_MASK) -#define MATCH_R2_STEX MATCH_R2_OPX0 (STEX) -#define MASK_R2_STEX MASK_R2_OPX0 -#define MATCH_R2_STH MATCH_R2_OP (STH) -#define MASK_R2_STH MASK_R2_OP -#define MATCH_R2_STHIO MATCH_R2_I12 (STHIO) -#define MASK_R2_STHIO MASK_R2_I12 -#define MATCH_R2_STH_N MATCH_R2_OP (STH_N) -#define MASK_R2_STH_N MASK_R2_OP -#define MATCH_R2_STSEX MATCH_R2_OPX0 (STSEX) -#define MASK_R2_STSEX MASK_R2_OPX0 -#define MATCH_R2_STW MATCH_R2_OP (STW) -#define MASK_R2_STW MASK_R2_OP -#define MATCH_R2_STWIO MATCH_R2_I12 (STWIO) -#define MASK_R2_STWIO MASK_R2_I12 -#define MATCH_R2_STWM MATCH_R2_I12 (STWM) -#define MASK_R2_STWM MASK_R2_I12 -#define MATCH_R2_STWSP_N MATCH_R2_OP (STWSP_N) -#define MASK_R2_STWSP_N MASK_R2_OP -#define MATCH_R2_STW_N MATCH_R2_OP (STW_N) -#define MASK_R2_STW_N MASK_R2_OP -#define MATCH_R2_STWZ_N MATCH_R2_OP (STZ_N) -#define MASK_R2_STWZ_N MASK_R2_OP -#define MATCH_R2_SUB MATCH_R2_OPX0 (SUB) -#define MASK_R2_SUB MASK_R2_OPX0 -#define MATCH_R2_SUBI MATCH_R2_OP (ADDI) -#define MASK_R2_SUBI MASK_R2_OP -#define MATCH_R2_SUB_N (MATCH_R2_OP (AS_N) | SET_IW_T3X1_X (R2_AS_N_SUB_N)) -#define MASK_R2_SUB_N (MASK_R2_OP | IW_T3X1_X_SHIFTED_MASK) -#define MATCH_R2_SUBI_N (MATCH_R2_OP (ASI_N) | SET_IW_T2X1I3_X (R2_ASI_N_SUBI_N)) -#define MASK_R2_SUBI_N (MASK_R2_OP | IW_T2X1I3_X_SHIFTED_MASK) -#define MATCH_R2_SYNC MATCH_R2_OPX (SYNC, 0, 0, 0) -#define MASK_R2_SYNC MASK_R2_OPX (1, 1, 1, 1) -#define MATCH_R2_TRAP MATCH_R2_OPX (TRAP, 0, 0, 0x1d) -#define MASK_R2_TRAP MASK_R2_OPX (1, 1, 1, 0) -#define MATCH_R2_TRAP_N MATCH_R2_R_N (TRAP_N) -#define MASK_R2_TRAP_N MASK_R2_R_N -#define MATCH_R2_WRCTL MATCH_R2_OPX (WRCTL, 0, 0, 0) -#define MASK_R2_WRCTL MASK_R2_OPX (0, 1, 1, 0) -#define MATCH_R2_WRPIE MATCH_R2_OPX (WRPIE, 0, 0, 0) -#define MASK_R2_WRPIE MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_WRPRS MATCH_R2_OPX (WRPRS, 0, 0, 0) -#define MASK_R2_WRPRS MASK_R2_OPX (0, 1, 0, 1) -#define MATCH_R2_XOR MATCH_R2_OPX0 (XOR) -#define MASK_R2_XOR MASK_R2_OPX0 -#define MATCH_R2_XORHI MATCH_R2_OP (XORHI) -#define MASK_R2_XORHI MASK_R2_OP -#define MATCH_R2_XORI MATCH_R2_OP (XORI) -#define MASK_R2_XORI MASK_R2_OP -#define MATCH_R2_XOR_N MATCH_R2_R_N (XOR_N) -#define MASK_R2_XOR_N MASK_R2_R_N - -#endif /* _NIOS2R2_H */ - - -/* These are the data structures used to hold the instruction information. */ -extern const struct nios2_opcode nios2_r1_opcodes[]; -extern const int nios2_num_r1_opcodes; -extern const struct nios2_opcode nios2_r2_opcodes[]; -extern const int nios2_num_r2_opcodes; -extern struct nios2_opcode *nios2_opcodes; -extern int nios2_num_opcodes; - -/* These are the data structures used to hold the register information. */ -extern const struct nios2_reg nios2_builtin_regs[]; -extern struct nios2_reg *nios2_regs; -extern const int nios2_num_builtin_regs; -extern int nios2_num_regs; - -/* Return the opcode descriptor for a single instruction. */ -extern const struct nios2_opcode * -nios2_find_opcode_hash (unsigned long, unsigned long); - -/* Lookup tables for R2 immediate decodings. */ -extern unsigned int nios2_r2_asi_n_mappings[]; -extern const int nios2_num_r2_asi_n_mappings; -extern unsigned int nios2_r2_shi_n_mappings[]; -extern const int nios2_num_r2_shi_n_mappings; -extern unsigned int nios2_r2_andi_n_mappings[]; -extern const int nios2_num_r2_andi_n_mappings; - -/* Lookup table for 3-bit register decodings. */ -extern int nios2_r2_reg3_mappings[]; -extern const int nios2_num_r2_reg3_mappings; - -/* Lookup table for REG_RANGE value list decodings. */ -extern unsigned long nios2_r2_reg_range_mappings[]; -extern const int nios2_num_r2_reg_range_mappings; - -#endif /* _NIOS2_H */ - -/*#include "sysdep.h" -#include "opcode/nios2.h" -*/ -/* Register string table */ - -const struct nios2_reg nios2_builtin_regs[] = { - /* Standard register names. */ - {"zero", 0, REG_NORMAL}, - {"at", 1, REG_NORMAL}, /* assembler temporary */ - {"r2", 2, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r3", 3, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r4", 4, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r5", 5, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r6", 6, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r7", 7, REG_NORMAL | REG_3BIT | REG_LDWM}, - {"r8", 8, REG_NORMAL | REG_LDWM}, - {"r9", 9, REG_NORMAL | REG_LDWM}, - {"r10", 10, REG_NORMAL | REG_LDWM}, - {"r11", 11, REG_NORMAL | REG_LDWM}, - {"r12", 12, REG_NORMAL | REG_LDWM}, - {"r13", 13, REG_NORMAL | REG_LDWM}, - {"r14", 14, REG_NORMAL | REG_LDWM}, - {"r15", 15, REG_NORMAL | REG_LDWM}, - {"r16", 16, REG_NORMAL | REG_3BIT | REG_LDWM | REG_POP}, - {"r17", 17, REG_NORMAL | REG_3BIT | REG_LDWM | REG_POP}, - {"r18", 18, REG_NORMAL | REG_LDWM | REG_POP}, - {"r19", 19, REG_NORMAL | REG_LDWM | REG_POP}, - {"r20", 20, REG_NORMAL | REG_LDWM | REG_POP}, - {"r21", 21, REG_NORMAL | REG_LDWM | REG_POP}, - {"r22", 22, REG_NORMAL | REG_LDWM | REG_POP}, - {"r23", 23, REG_NORMAL | REG_LDWM | REG_POP}, - {"et", 24, REG_NORMAL}, - {"bt", 25, REG_NORMAL}, - {"gp", 26, REG_NORMAL}, /* global pointer */ - {"sp", 27, REG_NORMAL}, /* stack pointer */ - {"fp", 28, REG_NORMAL | REG_LDWM | REG_POP}, /* frame pointer */ - {"ea", 29, REG_NORMAL}, /* exception return address */ - {"sstatus", 30, REG_NORMAL}, /* saved processor status */ - {"ra", 31, REG_NORMAL | REG_LDWM | REG_POP}, /* return address */ - - /* Alternative names for special registers. */ - {"r0", 0, REG_NORMAL}, - {"r1", 1, REG_NORMAL}, - {"r24", 24, REG_NORMAL}, - {"r25", 25, REG_NORMAL}, - {"r26", 26, REG_NORMAL}, - {"r27", 27, REG_NORMAL}, - {"r28", 28, REG_NORMAL | REG_LDWM | REG_POP}, - {"r29", 29, REG_NORMAL}, - {"r30", 30, REG_NORMAL}, - {"ba", 30, REG_NORMAL}, /* breakpoint return address */ - {"r31", 31, REG_NORMAL | REG_LDWM | REG_POP}, - - /* Control register names. */ - {"status", 0, REG_CONTROL}, - {"estatus", 1, REG_CONTROL}, - {"bstatus", 2, REG_CONTROL}, - {"ienable", 3, REG_CONTROL}, - {"ipending", 4, REG_CONTROL}, - {"cpuid", 5, REG_CONTROL}, - {"ctl6", 6, REG_CONTROL}, - {"exception", 7, REG_CONTROL}, - {"pteaddr", 8, REG_CONTROL}, - {"tlbacc", 9, REG_CONTROL}, - {"tlbmisc", 10, REG_CONTROL}, - {"eccinj", 11, REG_CONTROL}, - {"badaddr", 12, REG_CONTROL}, - {"config", 13, REG_CONTROL}, - {"mpubase", 14, REG_CONTROL}, - {"mpuacc", 15, REG_CONTROL}, - {"ctl16", 16, REG_CONTROL}, - {"ctl17", 17, REG_CONTROL}, - {"ctl18", 18, REG_CONTROL}, - {"ctl19", 19, REG_CONTROL}, - {"ctl20", 20, REG_CONTROL}, - {"ctl21", 21, REG_CONTROL}, - {"ctl22", 22, REG_CONTROL}, - {"ctl23", 23, REG_CONTROL}, - {"ctl24", 24, REG_CONTROL}, - {"ctl25", 25, REG_CONTROL}, - {"ctl26", 26, REG_CONTROL}, - {"ctl27", 27, REG_CONTROL}, - {"ctl28", 28, REG_CONTROL}, - {"ctl29", 29, REG_CONTROL}, - {"ctl30", 30, REG_CONTROL}, - {"ctl31", 31, REG_CONTROL}, - - /* Alternative names for special control registers. */ - {"ctl0", 0, REG_CONTROL}, - {"ctl1", 1, REG_CONTROL}, - {"ctl2", 2, REG_CONTROL}, - {"ctl3", 3, REG_CONTROL}, - {"ctl4", 4, REG_CONTROL}, - {"ctl5", 5, REG_CONTROL}, - {"ctl7", 7, REG_CONTROL}, - {"ctl8", 8, REG_CONTROL}, - {"ctl9", 9, REG_CONTROL}, - {"ctl10", 10, REG_CONTROL}, - {"ctl11", 11, REG_CONTROL}, - {"ctl12", 12, REG_CONTROL}, - {"ctl13", 13, REG_CONTROL}, - {"ctl14", 14, REG_CONTROL}, - {"ctl15", 15, REG_CONTROL}, - - /* Coprocessor register names. */ - {"c0", 0, REG_COPROCESSOR}, - {"c1", 1, REG_COPROCESSOR}, - {"c2", 2, REG_COPROCESSOR}, - {"c3", 3, REG_COPROCESSOR}, - {"c4", 4, REG_COPROCESSOR}, - {"c5", 5, REG_COPROCESSOR}, - {"c6", 6, REG_COPROCESSOR}, - {"c7", 7, REG_COPROCESSOR}, - {"c8", 8, REG_COPROCESSOR}, - {"c9", 9, REG_COPROCESSOR}, - {"c10", 10, REG_COPROCESSOR}, - {"c11", 11, REG_COPROCESSOR}, - {"c12", 12, REG_COPROCESSOR}, - {"c13", 13, REG_COPROCESSOR}, - {"c14", 14, REG_COPROCESSOR}, - {"c15", 15, REG_COPROCESSOR}, - {"c16", 16, REG_COPROCESSOR}, - {"c17", 17, REG_COPROCESSOR}, - {"c18", 18, REG_COPROCESSOR}, - {"c19", 19, REG_COPROCESSOR}, - {"c20", 20, REG_COPROCESSOR}, - {"c21", 21, REG_COPROCESSOR}, - {"c22", 22, REG_COPROCESSOR}, - {"c23", 23, REG_COPROCESSOR}, - {"c24", 24, REG_COPROCESSOR}, - {"c25", 25, REG_COPROCESSOR}, - {"c26", 26, REG_COPROCESSOR}, - {"c27", 27, REG_COPROCESSOR}, - {"c28", 28, REG_COPROCESSOR}, - {"c29", 29, REG_COPROCESSOR}, - {"c30", 30, REG_COPROCESSOR}, - {"c31", 31, REG_COPROCESSOR}, -}; - -#define NIOS2_NUM_REGS \ - ((sizeof nios2_builtin_regs) / (sizeof (nios2_builtin_regs[0]))) -const int nios2_num_builtin_regs = NIOS2_NUM_REGS; - -/* This is not const in order to allow for dynamic extensions to the - built-in instruction set. */ -struct nios2_reg *nios2_regs = (struct nios2_reg *) nios2_builtin_regs; -int nios2_num_regs = NIOS2_NUM_REGS; -#undef NIOS2_NUM_REGS - -/* This is the opcode table used by the Nios II GNU as, disassembler - and GDB. */ -const struct nios2_opcode nios2_r1_opcodes[] = -{ - /* { name, args, args_test, num_args, size, format, - match, mask, pinfo, overflow } */ - {"add", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_ADD, MASK_R1_ADD, 0, no_overflow}, - {"addi", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_ADDI, MASK_R1_ADDI, 0, signed_immed16_overflow}, - {"and", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_AND, MASK_R1_AND, 0, no_overflow}, - {"andhi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ANDHI, MASK_R1_ANDHI, 0, unsigned_immed16_overflow}, - {"andi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ANDI, MASK_R1_ANDI, 0, unsigned_immed16_overflow}, - {"beq", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BEQ, MASK_R1_BEQ, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bge", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGE, MASK_R1_BGE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgeu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGEU, MASK_R1_BGEU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgt", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGT, MASK_R1_BGT, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgtu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BGTU, MASK_R1_BGTU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"ble", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLE, MASK_R1_BLE, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bleu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLEU, MASK_R1_BLEU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"blt", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLT, MASK_R1_BLT, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bltu", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BLTU, MASK_R1_BLTU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bne", "s,t,o", "s,t,o,E", 3, 4, iw_i_type, - MATCH_R1_BNE, MASK_R1_BNE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"br", "o", "o,E", 1, 4, iw_i_type, - MATCH_R1_BR, MASK_R1_BR, NIOS2_INSN_UBRANCH, branch_target_overflow}, - {"break", "j", "j,E", 1, 4, iw_r_type, - MATCH_R1_BREAK, MASK_R1_BREAK, NIOS2_INSN_OPTARG, no_overflow}, - {"bret", "", "E", 0, 4, iw_r_type, - MATCH_R1_BRET, MASK_R1_BRET, 0, no_overflow}, - {"call", "m", "m,E", 1, 4, iw_j_type, - MATCH_R1_CALL, MASK_R1_CALL, NIOS2_INSN_CALL, call_target_overflow}, - {"callr", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_CALLR, MASK_R1_CALLR, 0, no_overflow}, - {"cmpeq", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPEQ, MASK_R1_CMPEQ, 0, no_overflow}, - {"cmpeqi", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPEQI, MASK_R1_CMPEQI, 0, signed_immed16_overflow}, - {"cmpge", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGE, MASK_R1_CMPGE, 0, no_overflow}, - {"cmpgei", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPGEI, MASK_R1_CMPGEI, 0, signed_immed16_overflow}, - {"cmpgeu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGEU, MASK_R1_CMPGEU, 0, no_overflow}, - {"cmpgeui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPGEUI, MASK_R1_CMPGEUI, 0, unsigned_immed16_overflow}, - {"cmpgt", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGT, MASK_R1_CMPGT, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgti", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPGTI, MASK_R1_CMPGTI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpgtu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPGTU, MASK_R1_CMPGTU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgtui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPGTUI, MASK_R1_CMPGTUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmple", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLE, MASK_R1_CMPLE, NIOS2_INSN_MACRO, no_overflow}, - {"cmplei", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPLEI, MASK_R1_CMPLEI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpleu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLEU, MASK_R1_CMPLEU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpleui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPLEUI, MASK_R1_CMPLEUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmplt", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLT, MASK_R1_CMPLT, 0, no_overflow}, - {"cmplti", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPLTI, MASK_R1_CMPLTI, 0, signed_immed16_overflow}, - {"cmpltu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPLTU, MASK_R1_CMPLTU, 0, no_overflow}, - {"cmpltui", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_CMPLTUI, MASK_R1_CMPLTUI, 0, unsigned_immed16_overflow}, - {"cmpne", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_CMPNE, MASK_R1_CMPNE, 0, no_overflow}, - {"cmpnei", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_CMPNEI, MASK_R1_CMPNEI, 0, signed_immed16_overflow}, - {"custom", "l,d,s,t", "l,d,s,t,E", 4, 4, iw_custom_type, - MATCH_R1_CUSTOM, MASK_R1_CUSTOM, 0, custom_opcode_overflow}, - {"div", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_DIV, MASK_R1_DIV, 0, no_overflow}, - {"divu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_DIVU, MASK_R1_DIVU, 0, no_overflow}, - {"eret", "", "E", 0, 4, iw_r_type, - MATCH_R1_ERET, MASK_R1_ERET, 0, no_overflow}, - {"flushd", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_FLUSHD, MASK_R1_FLUSHD, 0, address_offset_overflow}, - {"flushda", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_FLUSHDA, MASK_R1_FLUSHDA, 0, address_offset_overflow}, - {"flushi", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_FLUSHI, MASK_R1_FLUSHI, 0, no_overflow}, - {"flushp", "", "E", 0, 4, iw_r_type, - MATCH_R1_FLUSHP, MASK_R1_FLUSHP, 0, no_overflow}, - {"initd", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_INITD, MASK_R1_INITD, 0, address_offset_overflow}, - {"initda", "i(s)", "i(s),E", 2, 4, iw_i_type, - MATCH_R1_INITDA, MASK_R1_INITDA, 0, address_offset_overflow}, - {"initi", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_INITI, MASK_R1_INITI, 0, no_overflow}, - {"jmp", "s", "s,E", 1, 4, iw_r_type, - MATCH_R1_JMP, MASK_R1_JMP, 0, no_overflow}, - {"jmpi", "m", "m,E", 1, 4, iw_j_type, - MATCH_R1_JMPI, MASK_R1_JMPI, 0, call_target_overflow}, - {"ldb", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDB, MASK_R1_LDB, 0, address_offset_overflow}, - {"ldbio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDBIO, MASK_R1_LDBIO, 0, address_offset_overflow}, - {"ldbu", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDBU, MASK_R1_LDBU, 0, address_offset_overflow}, - {"ldbuio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDBUIO, MASK_R1_LDBUIO, 0, address_offset_overflow}, - {"ldh", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDH, MASK_R1_LDH, 0, address_offset_overflow}, - {"ldhio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDHIO, MASK_R1_LDHIO, 0, address_offset_overflow}, - {"ldhu", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDHU, MASK_R1_LDHU, 0, address_offset_overflow}, - {"ldhuio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDHUIO, MASK_R1_LDHUIO, 0, address_offset_overflow}, - {"ldw", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDW, MASK_R1_LDW, 0, address_offset_overflow}, - {"ldwio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_LDWIO, MASK_R1_LDWIO, 0, address_offset_overflow}, - {"mov", "d,s", "d,s,E", 2, 4, iw_r_type, - MATCH_R1_MOV, MASK_R1_MOV, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"movhi", "t,u", "t,u,E", 2, 4, iw_i_type, - MATCH_R1_MOVHI, MASK_R1_MOVHI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"movi", "t,i", "t,i,E", 2, 4, iw_i_type, - MATCH_R1_MOVI, MASK_R1_MOVI, NIOS2_INSN_MACRO_MOVI, signed_immed16_overflow}, - {"movia", "t,o", "t,o,E", 2, 4, iw_i_type, - MATCH_R1_ORHI, MASK_R1_ORHI, NIOS2_INSN_MACRO_MOVIA, no_overflow}, - {"movui", "t,u", "t,u,E", 2, 4, iw_i_type, - MATCH_R1_MOVUI, MASK_R1_MOVUI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"mul", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MUL, MASK_R1_MUL, 0, no_overflow}, - {"muli", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_MULI, MASK_R1_MULI, 0, signed_immed16_overflow}, - {"mulxss", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MULXSS, MASK_R1_MULXSS, 0, no_overflow}, - {"mulxsu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MULXSU, MASK_R1_MULXSU, 0, no_overflow}, - {"mulxuu", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_MULXUU, MASK_R1_MULXUU, 0, no_overflow}, - {"nextpc", "d", "d,E", 1, 4, iw_r_type, - MATCH_R1_NEXTPC, MASK_R1_NEXTPC, 0, no_overflow}, - {"nop", "", "E", 0, 4, iw_r_type, - MATCH_R1_NOP, MASK_R1_NOP, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"nor", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_NOR, MASK_R1_NOR, 0, no_overflow}, - {"or", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_OR, MASK_R1_OR, 0, no_overflow}, - {"orhi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ORHI, MASK_R1_ORHI, 0, unsigned_immed16_overflow}, - {"ori", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_ORI, MASK_R1_ORI, 0, unsigned_immed16_overflow}, - {"rdctl", "d,c", "d,c,E", 2, 4, iw_r_type, - MATCH_R1_RDCTL, MASK_R1_RDCTL, 0, no_overflow}, - {"rdprs", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_RDPRS, MASK_R1_RDPRS, 0, signed_immed16_overflow}, - {"ret", "", "E", 0, 4, iw_r_type, - MATCH_R1_RET, MASK_R1_RET, 0, no_overflow}, - {"rol", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_ROL, MASK_R1_ROL, 0, no_overflow}, - {"roli", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_ROLI, MASK_R1_ROLI, 0, unsigned_immed5_overflow}, - {"ror", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_ROR, MASK_R1_ROR, 0, no_overflow}, - {"sll", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SLL, MASK_R1_SLL, 0, no_overflow}, - {"slli", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_SLLI, MASK_R1_SLLI, 0, unsigned_immed5_overflow}, - {"sra", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SRA, MASK_R1_SRA, 0, no_overflow}, - {"srai", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_SRAI, MASK_R1_SRAI, 0, unsigned_immed5_overflow}, - {"srl", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SRL, MASK_R1_SRL, 0, no_overflow}, - {"srli", "d,s,j", "d,s,j,E", 3, 4, iw_r_type, - MATCH_R1_SRLI, MASK_R1_SRLI, 0, unsigned_immed5_overflow}, - {"stb", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STB, MASK_R1_STB, 0, address_offset_overflow}, - {"stbio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STBIO, MASK_R1_STBIO, 0, address_offset_overflow}, - {"sth", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STH, MASK_R1_STH, 0, address_offset_overflow}, - {"sthio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STHIO, MASK_R1_STHIO, 0, address_offset_overflow}, - {"stw", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STW, MASK_R1_STW, 0, address_offset_overflow}, - {"stwio", "t,i(s)", "t,i(s),E", 3, 4, iw_i_type, - MATCH_R1_STWIO, MASK_R1_STWIO, 0, address_offset_overflow}, - {"sub", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_SUB, MASK_R1_SUB, 0, no_overflow}, - {"subi", "t,s,i", "t,s,i,E", 3, 4, iw_i_type, - MATCH_R1_SUBI, MASK_R1_SUBI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"sync", "", "E", 0, 4, iw_r_type, - MATCH_R1_SYNC, MASK_R1_SYNC, 0, no_overflow}, - {"trap", "j", "j,E", 1, 4, iw_r_type, - MATCH_R1_TRAP, MASK_R1_TRAP, NIOS2_INSN_OPTARG, no_overflow}, - {"wrctl", "c,s", "c,s,E", 2, 4, iw_r_type, - MATCH_R1_WRCTL, MASK_R1_WRCTL, 0, no_overflow}, - {"wrprs", "d,s", "d,s,E", 2, 4, iw_r_type, - MATCH_R1_WRPRS, MASK_R1_WRPRS, 0, no_overflow}, - {"xor", "d,s,t", "d,s,t,E", 3, 4, iw_r_type, - MATCH_R1_XOR, MASK_R1_XOR, 0, no_overflow}, - {"xorhi", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_XORHI, MASK_R1_XORHI, 0, unsigned_immed16_overflow}, - {"xori", "t,s,u", "t,s,u,E", 3, 4, iw_i_type, - MATCH_R1_XORI, MASK_R1_XORI, 0, unsigned_immed16_overflow} -}; - -#define NIOS2_NUM_R1_OPCODES \ - ((sizeof nios2_r1_opcodes) / (sizeof (nios2_r1_opcodes[0]))) -const int nios2_num_r1_opcodes = NIOS2_NUM_R1_OPCODES; - - -const struct nios2_opcode nios2_r2_opcodes[] = -{ - /* { name, args, args_test, num_args, size, format, - match, mask, pinfo, overflow } */ - {"add", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_ADD, MASK_R2_ADD, 0, no_overflow}, - {"addi", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_ADDI, MASK_R2_ADDI, 0, signed_immed16_overflow}, - {"add.n", "D,S,T", "D,S,T,E", 3, 2, iw_T3X1_type, - MATCH_R2_ADD_N, MASK_R2_ADD_N, 0, no_overflow}, - {"addi.n", "D,S,e", "D,S,e,E", 3, 2, iw_T2X1I3_type, - MATCH_R2_ADDI_N, MASK_R2_ADDI_N, 0, enumeration_overflow}, - {"and", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_AND, MASK_R2_AND, 0, no_overflow}, - {"andchi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDCHI, MASK_R2_ANDCHI, 0, unsigned_immed16_overflow}, - {"andci", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDCI, MASK_R2_ANDCI, 0, unsigned_immed16_overflow}, - {"andhi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDHI, MASK_R2_ANDHI, 0, unsigned_immed16_overflow}, - {"andi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ANDI, MASK_R2_ANDI, 0, unsigned_immed16_overflow}, - {"andi.n", "T,S,g", "T,S,g,E", 3, 2, iw_T2I4_type, - MATCH_R2_ANDI_N, MASK_R2_ANDI_N, 0, enumeration_overflow}, - {"and.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_AND_N, MASK_R2_AND_N, 0, no_overflow}, - {"beq", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BEQ, MASK_R2_BEQ, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"beqz.n", "S,P", "S,P,E", 2, 2, iw_T1I7_type, - MATCH_R2_BEQZ_N, MASK_R2_BEQZ_N, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bge", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGE, MASK_R2_BGE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgeu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGEU, MASK_R2_BGEU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgt", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGT, MASK_R2_BGT, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bgtu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BGTU, MASK_R2_BGTU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"ble", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLE, MASK_R2_BLE, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bleu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLEU, MASK_R2_BLEU, - NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"blt", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLT, MASK_R2_BLT, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bltu", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BLTU, MASK_R2_BLTU, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bne", "s,t,o", "s,t,o,E", 3, 4, iw_F2I16_type, - MATCH_R2_BNE, MASK_R2_BNE, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"bnez.n", "S,P", "S,P,E", 2, 2, iw_T1I7_type, - MATCH_R2_BNEZ_N, MASK_R2_BNEZ_N, NIOS2_INSN_CBRANCH, branch_target_overflow}, - {"br", "o", "o,E", 1, 4, iw_F2I16_type, - MATCH_R2_BR, MASK_R2_BR, NIOS2_INSN_UBRANCH, branch_target_overflow}, - {"break", "j", "j,E", 1, 4, iw_F3X6L5_type, - MATCH_R2_BREAK, MASK_R2_BREAK, NIOS2_INSN_OPTARG, no_overflow}, - {"break.n", "j", "j,E", 1, 2, iw_X2L5_type, - MATCH_R2_BREAK_N, MASK_R2_BREAK_N, NIOS2_INSN_OPTARG, no_overflow}, - {"bret", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_BRET, MASK_R2_BRET, 0, no_overflow}, - {"br.n", "O", "O,E", 1, 2, iw_I10_type, - MATCH_R2_BR_N, MASK_R2_BR_N, NIOS2_INSN_UBRANCH, branch_target_overflow}, - {"call", "m", "m,E", 1, 4, iw_L26_type, - MATCH_R2_CALL, MASK_R2_CALL, NIOS2_INSN_CALL, call_target_overflow}, - {"callr", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_CALLR, MASK_R2_CALLR, 0, no_overflow}, - {"callr.n", "s", "s,E", 1, 2, iw_F1X1_type, - MATCH_R2_CALLR_N, MASK_R2_CALLR_N, 0, no_overflow}, - {"cmpeq", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPEQ, MASK_R2_CMPEQ, 0, no_overflow}, - {"cmpeqi", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPEQI, MASK_R2_CMPEQI, 0, signed_immed16_overflow}, - {"cmpge", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGE, MASK_R2_CMPGE, 0, no_overflow}, - {"cmpgei", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGEI, MASK_R2_CMPGEI, 0, signed_immed16_overflow}, - {"cmpgeu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGEU, MASK_R2_CMPGEU, 0, no_overflow}, - {"cmpgeui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGEUI, MASK_R2_CMPGEUI, 0, unsigned_immed16_overflow}, - {"cmpgt", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGT, MASK_R2_CMPGT, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgti", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGTI, MASK_R2_CMPGTI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpgtu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPGTU, MASK_R2_CMPGTU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpgtui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPGTUI, MASK_R2_CMPGTUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmple", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLE, MASK_R2_CMPLE, NIOS2_INSN_MACRO, no_overflow}, - {"cmplei", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLEI, MASK_R2_CMPLEI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"cmpleu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLEU, MASK_R2_CMPLEU, NIOS2_INSN_MACRO, no_overflow}, - {"cmpleui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLEUI, MASK_R2_CMPLEUI, - NIOS2_INSN_MACRO, unsigned_immed16_overflow}, - {"cmplt", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLT, MASK_R2_CMPLT, 0, no_overflow}, - {"cmplti", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLTI, MASK_R2_CMPLTI, 0, signed_immed16_overflow}, - {"cmpltu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPLTU, MASK_R2_CMPLTU, 0, no_overflow}, - {"cmpltui", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPLTUI, MASK_R2_CMPLTUI, 0, unsigned_immed16_overflow}, - {"cmpne", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_CMPNE, MASK_R2_CMPNE, 0, no_overflow}, - {"cmpnei", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_CMPNEI, MASK_R2_CMPNEI, 0, signed_immed16_overflow}, - {"custom", "l,d,s,t", "l,d,s,t,E", 4, 4, iw_F3X8_type, - MATCH_R2_CUSTOM, MASK_R2_CUSTOM, 0, custom_opcode_overflow}, - {"div", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_DIV, MASK_R2_DIV, 0, no_overflow}, - {"divu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_DIVU, MASK_R2_DIVU, 0, no_overflow}, - {"eni", "j", "j,E", 1, 4, iw_F3X6L5_type, - MATCH_R2_ENI, MASK_R2_ENI, NIOS2_INSN_OPTARG, no_overflow}, - {"eret", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_ERET, MASK_R2_ERET, 0, no_overflow}, - {"extract", "t,s,j,k", "t,s,j,k,E", 4, 4, iw_F2X6L10_type, - MATCH_R2_EXTRACT, MASK_R2_EXTRACT, 0, no_overflow}, - {"flushd", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_FLUSHD, MASK_R2_FLUSHD, 0, address_offset_overflow}, - {"flushda", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_FLUSHDA, MASK_R2_FLUSHDA, 0, address_offset_overflow}, - {"flushi", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_FLUSHI, MASK_R2_FLUSHI, 0, no_overflow}, - {"flushp", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_FLUSHP, MASK_R2_FLUSHP, 0, no_overflow}, - {"initd", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_INITD, MASK_R2_INITD, 0, address_offset_overflow}, - {"initda", "I(s)", "I(s),E", 2, 4, iw_F1X4I12_type, - MATCH_R2_INITDA, MASK_R2_INITDA, 0, address_offset_overflow}, - {"initi", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_INITI, MASK_R2_INITI, 0, no_overflow}, - {"insert", "t,s,j,k", "t,s,j,k,E", 4, 4, iw_F2X6L10_type, - MATCH_R2_INSERT, MASK_R2_INSERT, 0, no_overflow}, - {"jmp", "s", "s,E", 1, 4, iw_F3X6_type, - MATCH_R2_JMP, MASK_R2_JMP, 0, no_overflow}, - {"jmpi", "m", "m,E", 1, 4, iw_L26_type, - MATCH_R2_JMPI, MASK_R2_JMPI, 0, call_target_overflow}, - {"jmpr.n", "s", "s,E", 1, 2, iw_F1X1_type, - MATCH_R2_JMPR_N, MASK_R2_JMPR_N, 0, no_overflow}, - {"ldb", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDB, MASK_R2_LDB, 0, address_offset_overflow}, - {"ldbio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDBIO, MASK_R2_LDBIO, 0, signed_immed12_overflow}, - {"ldbu", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDBU, MASK_R2_LDBU, 0, address_offset_overflow}, - {"ldbuio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDBUIO, MASK_R2_LDBUIO, 0, signed_immed12_overflow}, - {"ldbu.n", "T,Y(S)", "T,Y(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_LDBU_N, MASK_R2_LDBU_N, 0, address_offset_overflow}, - {"ldex", "d,(s)", "d,(s),E", 2, 4, iw_F3X6_type, - MATCH_R2_LDEX, MASK_R2_LDEX, 0, no_overflow}, - {"ldh", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDH, MASK_R2_LDH, 0, address_offset_overflow}, - {"ldhio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDHIO, MASK_R2_LDHIO, 0, signed_immed12_overflow}, - {"ldhu", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDHU, MASK_R2_LDHU, 0, address_offset_overflow}, - {"ldhuio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDHUIO, MASK_R2_LDHUIO, 0, signed_immed12_overflow}, - {"ldhu.n", "T,X(S)", "T,X(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_LDHU_N, MASK_R2_LDHU_N, 0, address_offset_overflow}, - {"ldsex", "d,(s)", "d,(s),E", 2, 4, iw_F3X6_type, - MATCH_R2_LDSEX, MASK_R2_LDSEX, 0, no_overflow}, - {"ldw", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_LDW, MASK_R2_LDW, 0, address_offset_overflow}, - {"ldwio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_LDWIO, MASK_R2_LDWIO, 0, signed_immed12_overflow}, - {"ldwm", "R,B", "R,B,E", 2, 4, iw_F1X4L17_type, - MATCH_R2_LDWM, MASK_R2_LDWM, 0, no_overflow}, - {"ldw.n", "T,W(S)", "T,W(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_LDW_N, MASK_R2_LDW_N, 0, address_offset_overflow}, - {"ldwsp.n", "t,V(s)", "t,V(s),E", 3, 2, iw_F1I5_type, - MATCH_R2_LDWSP_N, MASK_R2_LDWSP_N, 0, address_offset_overflow}, - {"merge", "t,s,j,k", "t,s,j,k,E", 4, 4, iw_F2X6L10_type, - MATCH_R2_MERGE, MASK_R2_MERGE, 0, no_overflow}, - {"mov", "d,s", "d,s,E", 2, 4, iw_F3X6_type, - MATCH_R2_MOV, MASK_R2_MOV, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"mov.n", "d,s", "d,s,E", 2, 2, iw_F2_type, - MATCH_R2_MOV_N, MASK_R2_MOV_N, 0, no_overflow}, - {"movi.n", "D,h", "D,h,E", 2, 2, iw_T1I7_type, - MATCH_R2_MOVI_N, MASK_R2_MOVI_N, 0, enumeration_overflow}, - {"movhi", "t,u", "t,u,E", 2, 4, iw_F2I16_type, - MATCH_R2_MOVHI, MASK_R2_MOVHI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"movi", "t,i", "t,i,E", 2, 4, iw_F2I16_type, - MATCH_R2_MOVI, MASK_R2_MOVI, NIOS2_INSN_MACRO_MOVI, signed_immed16_overflow}, - {"movia", "t,o", "t,o,E", 2, 4, iw_F2I16_type, - MATCH_R2_ORHI, MASK_R2_ORHI, NIOS2_INSN_MACRO_MOVIA, no_overflow}, - {"movui", "t,u", "t,u,E", 2, 4, iw_F2I16_type, - MATCH_R2_MOVUI, MASK_R2_MOVUI, - NIOS2_INSN_MACRO_MOVI, unsigned_immed16_overflow}, - {"mul", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MUL, MASK_R2_MUL, 0, no_overflow}, - {"muli", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_MULI, MASK_R2_MULI, 0, signed_immed16_overflow}, - {"mulxss", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MULXSS, MASK_R2_MULXSS, 0, no_overflow}, - {"mulxsu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MULXSU, MASK_R2_MULXSU, 0, no_overflow}, - {"mulxuu", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_MULXUU, MASK_R2_MULXUU, 0, no_overflow}, - /* The encoding of the neg.n operands is backwards, not - the interpretation -- the first operand is still the - destination and the second the source. */ - {"neg.n", "S,D", "S,D,E", 2, 2, iw_T2X3_type, - MATCH_R2_NEG_N, MASK_R2_NEG_N, 0, no_overflow}, - {"nextpc", "d", "d,E", 1, 4, iw_F3X6_type, - MATCH_R2_NEXTPC, MASK_R2_NEXTPC, 0, no_overflow}, - {"nop", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_NOP, MASK_R2_NOP, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"nop.n", "", "E", 0, 2, iw_F2_type, - MATCH_R2_NOP_N, MASK_R2_NOP_N, NIOS2_INSN_MACRO_MOV, no_overflow}, - {"nor", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_NOR, MASK_R2_NOR, 0, no_overflow}, - {"not.n", "D,S", "D,S,E", 2, 2, iw_T2X3_type, - MATCH_R2_NOT_N, MASK_R2_NOT_N, 0, no_overflow}, - {"or", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_OR, MASK_R2_OR, 0, no_overflow}, - {"orhi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ORHI, MASK_R2_ORHI, 0, unsigned_immed16_overflow}, - {"ori", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_ORI, MASK_R2_ORI, 0, unsigned_immed16_overflow}, - {"or.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_OR_N, MASK_R2_OR_N, 0, no_overflow}, - {"pop.n", "R,W", "R,W,E", 2, 2, iw_L5I4X1_type, - MATCH_R2_POP_N, MASK_R2_POP_N, NIOS2_INSN_OPTARG, no_overflow}, - {"push.n", "R,W", "R,W,E", 2, 2, iw_L5I4X1_type, - MATCH_R2_PUSH_N, MASK_R2_PUSH_N, NIOS2_INSN_OPTARG, no_overflow}, - {"rdctl", "d,c", "d,c,E", 2, 4, iw_F3X6L5_type, - MATCH_R2_RDCTL, MASK_R2_RDCTL, 0, no_overflow}, - {"rdprs", "t,s,I", "t,s,I,E", 3, 4, iw_F2X4I12_type, - MATCH_R2_RDPRS, MASK_R2_RDPRS, 0, signed_immed12_overflow}, - {"ret", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_RET, MASK_R2_RET, 0, no_overflow}, - {"ret.n", "", "E", 0, 2, iw_X2L5_type, - MATCH_R2_RET_N, MASK_R2_RET_N, 0, no_overflow}, - {"rol", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_ROL, MASK_R2_ROL, 0, no_overflow}, - {"roli", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_ROLI, MASK_R2_ROLI, 0, unsigned_immed5_overflow}, - {"ror", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_ROR, MASK_R2_ROR, 0, no_overflow}, - {"sll", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SLL, MASK_R2_SLL, 0, no_overflow}, - {"slli", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_SLLI, MASK_R2_SLLI, 0, unsigned_immed5_overflow}, - {"sll.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_SLL_N, MASK_R2_SLL_N, 0, no_overflow}, - {"slli.n", "D,S,f", "D,S,f,E", 3, 2, iw_T2X1L3_type, - MATCH_R2_SLLI_N, MASK_R2_SLLI_N, 0, enumeration_overflow}, - {"spaddi.n", "D,U", "D,U,E", 2, 2, iw_T1I7_type, - MATCH_R2_SPADDI_N, MASK_R2_SPADDI_N, 0, address_offset_overflow}, - {"spdeci.n", "U", "U,E", 1, 2, iw_X1I7_type, - MATCH_R2_SPDECI_N, MASK_R2_SPDECI_N, 0, address_offset_overflow}, - {"spinci.n", "U", "U,E", 1, 2, iw_X1I7_type, - MATCH_R2_SPINCI_N, MASK_R2_SPINCI_N, 0, address_offset_overflow}, - {"sra", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SRA, MASK_R2_SRA, 0, no_overflow}, - {"srai", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_SRAI, MASK_R2_SRAI, 0, unsigned_immed5_overflow}, - {"srl", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SRL, MASK_R2_SRL, 0, no_overflow}, - {"srli", "d,s,j", "d,s,j,E", 3, 4, iw_F3X6L5_type, - MATCH_R2_SRLI, MASK_R2_SRLI, 0, unsigned_immed5_overflow}, - {"srl.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_SRL_N, MASK_R2_SRL_N, 0, no_overflow}, - {"srli.n", "D,S,f", "D,S,f,E", 3, 2, iw_T2X1L3_type, - MATCH_R2_SRLI_N, MASK_R2_SRLI_N, 0, enumeration_overflow}, - {"stb", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_STB, MASK_R2_STB, 0, address_offset_overflow}, - {"stbio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_STBIO, MASK_R2_STBIO, 0, signed_immed12_overflow}, - {"stb.n", "T,Y(S)", "T,Y(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_STB_N, MASK_R2_STB_N, 0, address_offset_overflow}, - {"stbz.n", "t,M(S)", "t,M(S),E", 3, 2, iw_T1X1I6_type, - MATCH_R2_STBZ_N, MASK_R2_STBZ_N, 0, address_offset_overflow}, - {"stex", "d,t,(s)", "d,t,(s),E", 3, 4, iw_F3X6_type, - MATCH_R2_STEX, MASK_R2_STEX, 0, no_overflow}, - {"sth", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_STH, MASK_R2_STH, 0, address_offset_overflow}, - {"sthio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_STHIO, MASK_R2_STHIO, 0, signed_immed12_overflow}, - {"sth.n", "T,X(S)", "T,X(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_STH_N, MASK_R2_STH_N, 0, address_offset_overflow}, - {"stsex", "d,t,(s)", "d,t,(s),E", 3, 4, iw_F3X6_type, - MATCH_R2_STSEX, MASK_R2_STSEX, 0, no_overflow}, - {"stw", "t,i(s)", "t,i(s),E", 3, 4, iw_F2I16_type, - MATCH_R2_STW, MASK_R2_STW, 0, address_offset_overflow}, - {"stwio", "t,I(s)", "t,I(s),E", 3, 4, iw_F2X4I12_type, - MATCH_R2_STWIO, MASK_R2_STWIO, 0, signed_immed12_overflow}, - {"stwm", "R,B", "R,B,E", 2, 4, iw_F1X4L17_type, - MATCH_R2_STWM, MASK_R2_STWM, 0, no_overflow}, - {"stwsp.n", "t,V(s)", "t,V(s),E", 3, 2, iw_F1I5_type, - MATCH_R2_STWSP_N, MASK_R2_STWSP_N, 0, address_offset_overflow}, - {"stw.n", "T,W(S)", "T,W(S),E", 3, 2, iw_T2I4_type, - MATCH_R2_STW_N, MASK_R2_STW_N, 0, address_offset_overflow}, - {"stwz.n", "t,N(S)", "t,N(S),E", 3, 2, iw_T1X1I6_type, - MATCH_R2_STWZ_N, MASK_R2_STWZ_N, 0, address_offset_overflow}, - {"sub", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_SUB, MASK_R2_SUB, 0, no_overflow}, - {"subi", "t,s,i", "t,s,i,E", 3, 4, iw_F2I16_type, - MATCH_R2_SUBI, MASK_R2_SUBI, NIOS2_INSN_MACRO, signed_immed16_overflow}, - {"sub.n", "D,S,T", "D,S,T,E", 3, 2, iw_T3X1_type, - MATCH_R2_SUB_N, MASK_R2_SUB_N, 0, no_overflow}, - {"subi.n", "D,S,e", "D,S,e,E", 3, 2, iw_T2X1I3_type, - MATCH_R2_SUBI_N, MASK_R2_SUBI_N, 0, enumeration_overflow}, - {"sync", "", "E", 0, 4, iw_F3X6_type, - MATCH_R2_SYNC, MASK_R2_SYNC, 0, no_overflow}, - {"trap", "j", "j,E", 1, 4, iw_F3X6L5_type, - MATCH_R2_TRAP, MASK_R2_TRAP, NIOS2_INSN_OPTARG, no_overflow}, - {"trap.n", "j", "j,E", 1, 2, iw_X2L5_type, - MATCH_R2_TRAP_N, MASK_R2_TRAP_N, NIOS2_INSN_OPTARG, no_overflow}, - {"wrctl", "c,s", "c,s,E", 2, 4, iw_F3X6L5_type, - MATCH_R2_WRCTL, MASK_R2_WRCTL, 0, no_overflow}, - {"wrpie", "d,s", "d,s,E", 2, 4, iw_F3X6L5_type, - MATCH_R2_WRPIE, MASK_R2_WRPIE, 0, no_overflow}, - {"wrprs", "d,s", "d,s,E", 2, 4, iw_F3X6_type, - MATCH_R2_WRPRS, MASK_R2_WRPRS, 0, no_overflow}, - {"xor", "d,s,t", "d,s,t,E", 3, 4, iw_F3X6_type, - MATCH_R2_XOR, MASK_R2_XOR, 0, no_overflow}, - {"xorhi", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_XORHI, MASK_R2_XORHI, 0, unsigned_immed16_overflow}, - {"xori", "t,s,u", "t,s,u,E", 3, 4, iw_F2I16_type, - MATCH_R2_XORI, MASK_R2_XORI, 0, unsigned_immed16_overflow}, - {"xor.n", "D,S,T", "D,S,T,E", 3, 2, iw_T2X3_type, - MATCH_R2_XOR_N, MASK_R2_XOR_N, 0, no_overflow}, -}; - -#define NIOS2_NUM_R2_OPCODES \ - ((sizeof nios2_r2_opcodes) / (sizeof (nios2_r2_opcodes[0]))) -const int nios2_num_r2_opcodes = NIOS2_NUM_R2_OPCODES; - -/* Default to using the R1 instruction tables. */ -struct nios2_opcode *nios2_opcodes = (struct nios2_opcode *) nios2_r1_opcodes; -int nios2_num_opcodes = NIOS2_NUM_R1_OPCODES; -#undef NIOS2_NUM_R1_OPCODES -#undef NIOS2_NUM_R2_OPCODES - -/* Decodings for R2 asi.n (addi.n/subi.n) immediate values. */ -unsigned int nios2_r2_asi_n_mappings[] = - {1, 2, 4, 8, 16, 32, 64, 128}; -const int nios2_num_r2_asi_n_mappings = 8; - -/* Decodings for R2 shi.n (slli.n/srli.n) immediate values. */ -unsigned int nios2_r2_shi_n_mappings[] = - {1, 2, 3, 8, 12, 16, 24, 31}; -const int nios2_num_r2_shi_n_mappings = 8; - -/* Decodings for R2 andi.n immediate values. */ -unsigned int nios2_r2_andi_n_mappings[] = - {1, 2, 3, 4, 8, 0xf, 0x10, 0x1f, - 0x20, 0x3f, 0x7f, 0x80, 0xff, 0x7ff, 0xff00, 0xffff}; -const int nios2_num_r2_andi_n_mappings = 16; - -/* Decodings for R2 3-bit register fields. */ -int nios2_r2_reg3_mappings[] = - {16, 17, 2, 3, 4, 5, 6, 7}; -const int nios2_num_r2_reg3_mappings = 8; - -/* Decodings for R2 push.n/pop.n REG_RANGE value list. */ -unsigned long nios2_r2_reg_range_mappings[] = { - 0x00010000, - 0x00030000, - 0x00070000, - 0x000f0000, - 0x001f0000, - 0x003f0000, - 0x007f0000, - 0x00ff0000 -}; -const int nios2_num_r2_reg_range_mappings = 8; - -/*#include "sysdep.h" -#include "dis-asm.h" -#include "opcode/nios2.h" -#include "libiberty.h" -*/ -/* No symbol table is available when this code runs out in an embedded - system as when it is used for disassembler support in a monitor. */ -#if !defined(EMBEDDED_ENV) -#define SYMTAB_AVAILABLE 1 -/* -#include "elf-bfd.h" -#include "elf/nios2.h" -*/ -#endif - -/* Default length of Nios II instruction in bytes. */ -#define INSNLEN 4 - -/* Data structures used by the opcode hash table. */ -typedef struct _nios2_opcode_hash -{ - const struct nios2_opcode *opcode; - struct _nios2_opcode_hash *next; -} nios2_opcode_hash; - -/* Hash table size. */ -#define OPCODE_HASH_SIZE (IW_R1_OP_UNSHIFTED_MASK + 1) - -/* Extract the opcode from an instruction word. */ -static unsigned int -nios2_r1_extract_opcode (unsigned int x) -{ - return GET_IW_R1_OP (x); -} - -static unsigned int -nios2_r2_extract_opcode (unsigned int x) -{ - return GET_IW_R2_OP (x); -} - -/* We maintain separate hash tables for R1 and R2 opcodes, and pseudo-ops - are stored in a different table than regular instructions. */ - -typedef struct _nios2_disassembler_state -{ - const struct nios2_opcode *opcodes; - const int *num_opcodes; - unsigned int (*extract_opcode) (unsigned int); - nios2_opcode_hash *hash[OPCODE_HASH_SIZE]; - nios2_opcode_hash *ps_hash[OPCODE_HASH_SIZE]; - const struct nios2_opcode *nop; - bfd_boolean init; -} nios2_disassembler_state; - -static nios2_disassembler_state -nios2_r1_disassembler_state = { - nios2_r1_opcodes, - &nios2_num_r1_opcodes, - nios2_r1_extract_opcode, - {}, - {}, - NULL, - 0 -}; - -static nios2_disassembler_state -nios2_r2_disassembler_state = { - nios2_r2_opcodes, - &nios2_num_r2_opcodes, - nios2_r2_extract_opcode, - {}, - {}, - NULL, - 0 -}; - -/* Function to initialize the opcode hash table. */ -static void -nios2_init_opcode_hash (nios2_disassembler_state *state) -{ - unsigned int i; - register const struct nios2_opcode *op; - - for (i = 0; i < OPCODE_HASH_SIZE; i++) - for (op = state->opcodes; op < &state->opcodes[*(state->num_opcodes)]; op++) - { - nios2_opcode_hash *new_hash; - nios2_opcode_hash **bucket = NULL; - - if ((op->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO) - { - if (i == state->extract_opcode (op->match) - && (op->pinfo & (NIOS2_INSN_MACRO_MOV | NIOS2_INSN_MACRO_MOVI) - & 0x7fffffff)) - { - bucket = &(state->ps_hash[i]); - if (strcmp (op->name, "nop") == 0) - state->nop = op; - } - } - else if (i == state->extract_opcode (op->match)) - bucket = &(state->hash[i]); - - if (bucket) - { - new_hash = - (nios2_opcode_hash *) malloc (sizeof (nios2_opcode_hash)); - if (new_hash == NULL) - { - fprintf (stderr, - "error allocating memory...broken disassembler\n"); - abort (); - } - new_hash->opcode = op; - new_hash->next = NULL; - while (*bucket) - bucket = &((*bucket)->next); - *bucket = new_hash; - } - } - state->init = 1; - -#ifdef DEBUG_HASHTABLE - for (i = 0; i < OPCODE_HASH_SIZE; ++i) - { - nios2_opcode_hash *tmp_hash = state->hash[i]; - printf ("index: 0x%02X ops: ", i); - while (tmp_hash != NULL) - { - printf ("%s ", tmp_hash->opcode->name); - tmp_hash = tmp_hash->next; - } - printf ("\n"); - } - - for (i = 0; i < OPCODE_HASH_SIZE; ++i) - { - nios2_opcode_hash *tmp_hash = state->ps_hash[i]; - printf ("index: 0x%02X ops: ", i); - while (tmp_hash != NULL) - { - printf ("%s ", tmp_hash->opcode->name); - tmp_hash = tmp_hash->next; - } - printf ("\n"); - } -#endif /* DEBUG_HASHTABLE */ -} - -/* Return a pointer to an nios2_opcode struct for a given instruction - word OPCODE for bfd machine MACH, or NULL if there is an error. */ -const struct nios2_opcode * -nios2_find_opcode_hash (unsigned long opcode, unsigned long mach) -{ - nios2_opcode_hash *entry; - nios2_disassembler_state *state; - - /* Select the right instruction set, hash tables, and opcode accessor - for the mach variant. */ - if (mach == bfd_mach_nios2r2) - state = &nios2_r2_disassembler_state; - else - state = &nios2_r1_disassembler_state; - - /* Build a hash table to shorten the search time. */ - if (!state->init) - nios2_init_opcode_hash (state); - - /* Check for NOP first. Both NOP and MOV are macros that expand into - an ADD instruction, and we always want to give priority to NOP. */ - if (state->nop->match == (opcode & state->nop->mask)) - return state->nop; - - /* First look in the pseudo-op hashtable. */ - for (entry = state->ps_hash[state->extract_opcode (opcode)]; - entry; entry = entry->next) - if (entry->opcode->match == (opcode & entry->opcode->mask)) - return entry->opcode; - - /* Otherwise look in the main hashtable. */ - for (entry = state->hash[state->extract_opcode (opcode)]; - entry; entry = entry->next) - if (entry->opcode->match == (opcode & entry->opcode->mask)) - return entry->opcode; - - return NULL; -} - -/* There are 32 regular registers, 32 coprocessor registers, - and 32 control registers. */ -#define NUMREGNAMES 32 - -/* Return a pointer to the base of the coprocessor register name array. */ -static struct nios2_reg * -nios2_coprocessor_regs (void) -{ - static struct nios2_reg *cached = NULL; - - if (!cached) - { - int i; - for (i = NUMREGNAMES; i < nios2_num_regs; i++) - if (!strcmp (nios2_regs[i].name, "c0")) - { - cached = nios2_regs + i; - break; - } - assert (cached); - } - return cached; -} - -/* Return a pointer to the base of the control register name array. */ -static struct nios2_reg * -nios2_control_regs (void) -{ - static struct nios2_reg *cached = NULL; - - if (!cached) - { - int i; - for (i = NUMREGNAMES; i < nios2_num_regs; i++) - if (!strcmp (nios2_regs[i].name, "status")) - { - cached = nios2_regs + i; - break; - } - assert (cached); - } - return cached; -} - -/* Helper routine to report internal errors. */ -static void -bad_opcode (const struct nios2_opcode *op) -{ - fprintf (stderr, "Internal error: broken opcode descriptor for `%s %s'\n", - op->name, op->args); - abort (); -} - -/* The function nios2_print_insn_arg uses the character pointed - to by ARGPTR to determine how it print the next token or separator - character in the arguments to an instruction. */ -static int -nios2_print_insn_arg (const char *argptr, - unsigned long opcode, bfd_vma address, - disassemble_info *info, - const struct nios2_opcode *op) -{ - unsigned long i = 0; - struct nios2_reg *reg_base; - - switch (*argptr) - { - case ',': - case '(': - case ')': - (*info->fprintf_func) (info->stream, "%c", *argptr); - break; - - case 'c': - /* Control register index. */ - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_IMM5 (opcode); - break; - case iw_F3X6L5_type: - i = GET_IW_F3X6L5_IMM5 (opcode); - break; - default: - bad_opcode (op); - } - reg_base = nios2_control_regs (); - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - break; - - case 'd': - reg_base = nios2_regs; - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_C (opcode); - break; - case iw_custom_type: - i = GET_IW_CUSTOM_C (opcode); - if (GET_IW_CUSTOM_READC (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F3X6L5_type: - case iw_F3X6_type: - i = GET_IW_F3X6L5_C (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_C (opcode); - if (GET_IW_F3X8_READC (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F2_type: - i = GET_IW_F2_B (opcode); - break; - default: - bad_opcode (op); - } - if (i < NUMREGNAMES) - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - else - (*info->fprintf_func) (info->stream, "unknown"); - break; - - case 's': - reg_base = nios2_regs; - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_A (opcode); - break; - case iw_i_type: - i = GET_IW_I_A (opcode); - break; - case iw_custom_type: - i = GET_IW_CUSTOM_A (opcode); - if (GET_IW_CUSTOM_READA (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F2I16_type: - i = GET_IW_F2I16_A (opcode); - break; - case iw_F2X4I12_type: - i = GET_IW_F2X4I12_A (opcode); - break; - case iw_F1X4I12_type: - i = GET_IW_F1X4I12_A (opcode); - break; - case iw_F1X4L17_type: - i = GET_IW_F1X4L17_A (opcode); - break; - case iw_F3X6L5_type: - case iw_F3X6_type: - i = GET_IW_F3X6L5_A (opcode); - break; - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_A (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_A (opcode); - if (GET_IW_F3X8_READA (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F1X1_type: - i = GET_IW_F1X1_A (opcode); - break; - case iw_F1I5_type: - i = 27; /* Implicit stack pointer reference. */ - break; - case iw_F2_type: - i = GET_IW_F2_A (opcode); - break; - default: - bad_opcode (op); - } - if (i < NUMREGNAMES) - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - else - (*info->fprintf_func) (info->stream, "unknown"); - break; - - case 't': - reg_base = nios2_regs; - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_B (opcode); - break; - case iw_i_type: - i = GET_IW_I_B (opcode); - break; - case iw_custom_type: - i = GET_IW_CUSTOM_B (opcode); - if (GET_IW_CUSTOM_READB (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F2I16_type: - i = GET_IW_F2I16_B (opcode); - break; - case iw_F2X4I12_type: - i = GET_IW_F2X4I12_B (opcode); - break; - case iw_F3X6L5_type: - case iw_F3X6_type: - i = GET_IW_F3X6L5_B (opcode); - break; - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_B (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_B (opcode); - if (GET_IW_F3X8_READB (opcode) == 0) - reg_base = nios2_coprocessor_regs (); - break; - case iw_F1I5_type: - i = GET_IW_F1I5_B (opcode); - break; - case iw_F2_type: - i = GET_IW_F2_B (opcode); - break; - case iw_T1X1I6_type: - i = 0; - break; - default: - bad_opcode (op); - } - if (i < NUMREGNAMES) - (*info->fprintf_func) (info->stream, "%s", reg_base[i].name); - else - (*info->fprintf_func) (info->stream, "unknown"); - break; - - case 'D': - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_A3 (opcode); - break; - case iw_T2X1L3_type: - i = GET_IW_T2X1L3_B3 (opcode); - break; - case iw_T2X1I3_type: - i = GET_IW_T2X1I3_B3 (opcode); - break; - case iw_T3X1_type: - i = GET_IW_T3X1_C3 (opcode); - break; - case iw_T2X3_type: - if (op->num_args == 3) - i = GET_IW_T2X3_A3 (opcode); - else - i = GET_IW_T2X3_B3 (opcode); - break; - default: - bad_opcode (op); - } - i = nios2_r2_reg3_mappings[i]; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[i].name); - break; - - case 'M': - /* 6-bit unsigned immediate with no shift. */ - switch (op->format) - { - case iw_T1X1I6_type: - i = GET_IW_T1X1I6_IMM6 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'N': - /* 6-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_T1X1I6_type: - i = GET_IW_T1X1I6_IMM6 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'S': - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_A3 (opcode); - break; - case iw_T2I4_type: - i = GET_IW_T2I4_A3 (opcode); - break; - case iw_T2X1L3_type: - i = GET_IW_T2X1L3_A3 (opcode); - break; - case iw_T2X1I3_type: - i = GET_IW_T2X1I3_A3 (opcode); - break; - case iw_T3X1_type: - i = GET_IW_T3X1_A3 (opcode); - break; - case iw_T2X3_type: - i = GET_IW_T2X3_A3 (opcode); - break; - case iw_T1X1I6_type: - i = GET_IW_T1X1I6_A3 (opcode); - break; - default: - bad_opcode (op); - } - i = nios2_r2_reg3_mappings[i]; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[i].name); - break; - - case 'T': - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_B3 (opcode); - break; - case iw_T3X1_type: - i = GET_IW_T3X1_B3 (opcode); - break; - case iw_T2X3_type: - i = GET_IW_T2X3_B3 (opcode); - break; - default: - bad_opcode (op); - } - i = nios2_r2_reg3_mappings[i]; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[i].name); - break; - - case 'i': - /* 16-bit signed immediate. */ - switch (op->format) - { - case iw_i_type: - i = (signed) (GET_IW_I_IMM16 (opcode) << 16) >> 16; - break; - case iw_F2I16_type: - i = (signed) (GET_IW_F2I16_IMM16 (opcode) << 16) >> 16; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'I': - /* 12-bit signed immediate. */ - switch (op->format) - { - case iw_F2X4I12_type: - i = (signed) (GET_IW_F2X4I12_IMM12 (opcode) << 20) >> 20; - break; - case iw_F1X4I12_type: - i = (signed) (GET_IW_F1X4I12_IMM12 (opcode) << 20) >> 20; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'u': - /* 16-bit unsigned immediate. */ - switch (op->format) - { - case iw_i_type: - i = GET_IW_I_IMM16 (opcode); - break; - case iw_F2I16_type: - i = GET_IW_F2I16_IMM16 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'U': - /* 7-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_IMM7 (opcode) << 2; - break; - case iw_X1I7_type: - i = GET_IW_X1I7_IMM7 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'V': - /* 5-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_F1I5_type: - i = GET_IW_F1I5_IMM5 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'W': - /* 4-bit unsigned immediate with 2-bit shift. */ - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_IMM4 (opcode) << 2; - break; - case iw_L5I4X1_type: - i = GET_IW_L5I4X1_IMM4 (opcode) << 2; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'X': - /* 4-bit unsigned immediate with 1-bit shift. */ - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_IMM4 (opcode) << 1; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'Y': - /* 4-bit unsigned immediate without shift. */ - switch (op->format) - { - case iw_T2I4_type: - i = GET_IW_T2I4_IMM4 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'o': - /* 16-bit signed immediate address offset. */ - switch (op->format) - { - case iw_i_type: - i = (signed) (GET_IW_I_IMM16 (opcode) << 16) >> 16; - break; - case iw_F2I16_type: - i = (signed) (GET_IW_F2I16_IMM16 (opcode) << 16) >> 16; - break; - default: - bad_opcode (op); - } - address = address + 4 + i; - (*info->print_address_func) (address, info); - break; - - case 'O': - /* 10-bit signed address offset with 1-bit shift. */ - switch (op->format) - { - case iw_I10_type: - i = (signed) (GET_IW_I10_IMM10 (opcode) << 22) >> 21; - break; - default: - bad_opcode (op); - } - address = address + 2 + i; - (*info->print_address_func) (address, info); - break; - - case 'P': - /* 7-bit signed address offset with 1-bit shift. */ - switch (op->format) - { - case iw_T1I7_type: - i = (signed) (GET_IW_T1I7_IMM7 (opcode) << 25) >> 24; - break; - default: - bad_opcode (op); - } - address = address + 2 + i; - (*info->print_address_func) (address, info); - break; - - case 'j': - /* 5-bit unsigned immediate. */ - switch (op->format) - { - case iw_r_type: - i = GET_IW_R_IMM5 (opcode); - break; - case iw_F3X6L5_type: - i = GET_IW_F3X6L5_IMM5 (opcode); - break; - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_MSB (opcode); - break; - case iw_X2L5_type: - i = GET_IW_X2L5_IMM5 (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'k': - /* Second 5-bit unsigned immediate field. */ - switch (op->format) - { - case iw_F2X6L10_type: - i = GET_IW_F2X6L10_LSB (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'l': - /* 8-bit unsigned immediate. */ - switch (op->format) - { - case iw_custom_type: - i = GET_IW_CUSTOM_N (opcode); - break; - case iw_F3X8_type: - i = GET_IW_F3X8_N (opcode); - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'm': - /* 26-bit unsigned immediate. */ - switch (op->format) - { - case iw_j_type: - i = GET_IW_J_IMM26 (opcode); - break; - case iw_L26_type: - i = GET_IW_L26_IMM26 (opcode); - break; - default: - bad_opcode (op); - } - /* This translates to an address because it's only used in call - instructions. */ - address = (address & 0xf0000000) | (i << 2); - (*info->print_address_func) (address, info); - break; - - case 'e': - /* Encoded enumeration for addi.n/subi.n. */ - switch (op->format) - { - case iw_T2X1I3_type: - i = nios2_r2_asi_n_mappings[GET_IW_T2X1I3_IMM3 (opcode)]; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'f': - /* Encoded enumeration for slli.n/srli.n. */ - switch (op->format) - { - case iw_T2X1L3_type: - i = nios2_r2_shi_n_mappings[GET_IW_T2X1I3_IMM3 (opcode)]; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'g': - /* Encoded enumeration for andi.n. */ - switch (op->format) - { - case iw_T2I4_type: - i = nios2_r2_andi_n_mappings[GET_IW_T2I4_IMM4 (opcode)]; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%lu", i); - break; - - case 'h': - /* Encoded enumeration for movi.n. */ - switch (op->format) - { - case iw_T1I7_type: - i = GET_IW_T1I7_IMM7 (opcode); - if (i == 125) - i = 0xff; - else if (i == 126) - i = -2; - else if (i == 127) - i = -1; - break; - default: - bad_opcode (op); - } - (*info->fprintf_func) (info->stream, "%ld", i); - break; - - case 'R': - { - unsigned long reglist = 0; - int dir = 1; - int k, t; - - switch (op->format) - { - case iw_F1X4L17_type: - /* Encoding for ldwm/stwm. */ - i = GET_IW_F1X4L17_REGMASK (opcode); - if (GET_IW_F1X4L17_RS (opcode)) - { - reglist = ((i << 14) & 0x00ffc000); - if (i & (1 << 10)) - reglist |= (1 << 28); - if (i & (1 << 11)) - reglist |= (1 << 31); - } - else - reglist = i << 2; - dir = GET_IW_F1X4L17_REGMASK (opcode) ? 1 : -1; - break; - - case iw_L5I4X1_type: - /* Encoding for push.n/pop.n. */ - reglist |= (1 << 31); - if (GET_IW_L5I4X1_FP (opcode)) - reglist |= (1 << 28); - if (GET_IW_L5I4X1_CS (opcode)) - { - int val = GET_IW_L5I4X1_REGRANGE (opcode); - reglist |= nios2_r2_reg_range_mappings[val]; - } - dir = (op->match == MATCH_R2_POP_N ? 1 : -1); - break; - - default: - bad_opcode (op); - } - - t = 0; - (*info->fprintf_func) (info->stream, "{"); - for (k = (dir == 1 ? 0 : 31); - (dir == 1 && k < 32) || (dir == -1 && k >= 0); - k += dir) - if (reglist & (1 << k)) - { - if (t) - (*info->fprintf_func) (info->stream, ","); - else - t++; - (*info->fprintf_func) (info->stream, "%s", nios2_regs[k].name); - } - (*info->fprintf_func) (info->stream, "}"); - break; - } - - case 'B': - /* Base register and options for ldwm/stwm. */ - switch (op->format) - { - case iw_F1X4L17_type: - if (GET_IW_F1X4L17_ID (opcode) == 0) - (*info->fprintf_func) (info->stream, "--"); - - i = GET_IW_F1X4I12_A (opcode); - (*info->fprintf_func) (info->stream, "(%s)", - nios2_builtin_regs[i].name); - - if (GET_IW_F1X4L17_ID (opcode)) - (*info->fprintf_func) (info->stream, "++"); - if (GET_IW_F1X4L17_WB (opcode)) - (*info->fprintf_func) (info->stream, ",writeback"); - if (GET_IW_F1X4L17_PC (opcode)) - (*info->fprintf_func) (info->stream, ",ret"); - break; - default: - bad_opcode (op); - } - break; - - default: - (*info->fprintf_func) (info->stream, "unknown"); - break; - } - return 0; -} - -/* nios2_disassemble does all the work of disassembling a Nios II - instruction opcode. */ -static int -nios2_disassemble (bfd_vma address, unsigned long opcode, - disassemble_info *info) -{ - const struct nios2_opcode *op; - - info->bytes_per_line = INSNLEN; - info->bytes_per_chunk = INSNLEN; - 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; - - /* Find the major opcode and use this to disassemble - the instruction and its arguments. */ - op = nios2_find_opcode_hash (opcode, info->mach); - - if (op != NULL) - { - const char *argstr = op->args; - (*info->fprintf_func) (info->stream, "%s", op->name); - if (argstr != NULL && *argstr != '\0') - { - (*info->fprintf_func) (info->stream, "\t"); - while (*argstr != '\0') - { - nios2_print_insn_arg (argstr, opcode, address, info, op); - ++argstr; - } - } - /* Tell the caller how far to advance the program counter. */ - info->bytes_per_chunk = op->size; - return op->size; - } - else - { - /* Handle undefined instructions. */ - info->insn_type = dis_noninsn; - (*info->fprintf_func) (info->stream, "0x%lx", opcode); - return INSNLEN; - } -} - - -/* print_insn_nios2 is the main disassemble function for Nios II. - The function diassembler(abfd) (source in disassemble.c) returns a - pointer to this either print_insn_big_nios2 or - print_insn_little_nios2, which in turn call this function when the - bfd machine type is Nios II. print_insn_nios2 reads the - instruction word at the address given, and prints the disassembled - instruction on the stream info->stream using info->fprintf_func. */ - -int print_insn_nios2(bfd_vma address, disassemble_info *info) -{ - bfd_byte buffer[INSNLEN]; - int status; - - status = (*info->read_memory_func)(address, buffer, INSNLEN, info); - if (status == 0) { - unsigned long insn; - if (info->endian == BFD_ENDIAN_BIG) { - insn = (unsigned long) bfd_getb32(buffer); - } else { - insn = (unsigned long) bfd_getl32(buffer); - } - return nios2_disassemble(address, insn, info); - } - - /* We might have a 16-bit R2 instruction at the end of memory. Try that. */ - if (info->mach == bfd_mach_nios2r2) { - status = (*info->read_memory_func)(address, buffer, 2, info); - if (status == 0) { - unsigned long insn; - if (info->endian == BFD_ENDIAN_BIG) { - insn = (unsigned long) bfd_getb16(buffer); - } else { - insn = (unsigned long) bfd_getl16(buffer); - } - return nios2_disassemble(address, insn, info); - } - } - - /* If we got here, we couldn't read anything. */ - (*info->memory_error_func)(status, address, info); - return -1; -} diff --git a/disas/objdump.c b/disas/objdump.c new file mode 100644 index 0000000000..9859f23419 --- /dev/null +++ b/disas/objdump.c @@ -0,0 +1,37 @@ +/* + * Dump disassembly as text, for processing by scripts/disas-objdump.pl. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "disas-internal.h" + + +static int print_insn_objdump(bfd_vma pc, disassemble_info *info, + const char *prefix) +{ + int i, n = info->buffer_length; + g_autofree uint8_t *buf = g_malloc(n); + + 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]); + } + } else { + info->fprintf_func(info->stream, "unable to read memory"); + } + return n; +} + +int print_insn_od_host(bfd_vma pc, disassemble_info *info) +{ + return print_insn_objdump(pc, info, "OBJD-H"); +} + +int print_insn_od_target(bfd_vma pc, disassemble_info *info) +{ + return print_insn_objdump(pc, info, "OBJD-T"); +} 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 d216b9c39b..9c1e332dde 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -18,155 +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_v_r, - rv_codec_v_ldst, - rv_codec_v_i, - rv_codec_vsetvli, - rv_codec_vsetivli, -} 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, @@ -935,51 +797,195 @@ typedef enum { 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_mop_r_0 = 878, + rv_mop_r_1 = 879, + rv_mop_r_2 = 880, + rv_mop_r_3 = 881, + rv_mop_r_4 = 882, + rv_mop_r_5 = 883, + rv_mop_r_6 = 884, + rv_mop_r_7 = 885, + rv_mop_r_8 = 886, + rv_mop_r_9 = 887, + rv_mop_r_10 = 888, + rv_mop_r_11 = 889, + rv_mop_r_12 = 890, + rv_mop_r_13 = 891, + rv_mop_r_14 = 892, + rv_mop_r_15 = 893, + rv_mop_r_16 = 894, + rv_mop_r_17 = 895, + rv_mop_r_18 = 896, + rv_mop_r_19 = 897, + rv_mop_r_20 = 898, + rv_mop_r_21 = 899, + rv_mop_r_22 = 900, + rv_mop_r_23 = 901, + rv_mop_r_24 = 902, + rv_mop_r_25 = 903, + rv_mop_r_26 = 904, + rv_mop_r_27 = 905, + rv_mop_r_28 = 906, + rv_mop_r_29 = 907, + rv_mop_r_30 = 908, + rv_mop_r_31 = 909, + rv_mop_rr_0 = 910, + rv_mop_rr_1 = 911, + rv_mop_rr_2 = 912, + rv_mop_rr_3 = 913, + rv_mop_rr_4 = 914, + rv_mop_rr_5 = 915, + rv_mop_rr_6 = 916, + rv_mop_rr_7 = 917, + rv_c_mop_1 = 918, + rv_c_mop_3 = 919, + rv_c_mop_5 = 920, + rv_c_mop_7 = 921, + rv_c_mop_9 = 922, + rv_c_mop_11 = 923, + rv_c_mop_13 = 924, + rv_c_mop_15 = 925, + rv_op_amoswap_b = 926, + rv_op_amoadd_b = 927, + rv_op_amoxor_b = 928, + rv_op_amoor_b = 929, + rv_op_amoand_b = 930, + rv_op_amomin_b = 931, + rv_op_amomax_b = 932, + rv_op_amominu_b = 933, + rv_op_amomaxu_b = 934, + rv_op_amoswap_h = 935, + rv_op_amoadd_h = 936, + rv_op_amoxor_h = 937, + rv_op_amoor_h = 938, + rv_op_amoand_h = 939, + rv_op_amomin_h = 940, + rv_op_amomax_h = 941, + rv_op_amominu_h = 942, + rv_op_amomaxu_h = 943, + rv_op_amocas_b = 944, + rv_op_amocas_h = 945, + rv_op_wrs_sto = 946, + rv_op_wrs_nto = 947, + rv_op_lpad = 948, + rv_op_sspush = 949, + rv_op_sspopchk = 950, + rv_op_ssrdp = 951, + rv_op_ssamoswap_w = 952, + rv_op_ssamoswap_d = 953, + rv_op_c_sspush = 954, + rv_op_c_sspopchk = 955, } 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; - uint8_t vm; - uint32_t vzimm; -} 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] = { @@ -1003,79 +1009,30 @@ static const char rv_vreg_name_sym[32][4] = { "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" }; -/* 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_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" -#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_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" +/* 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 }; @@ -1105,18 +1062,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 }; @@ -1288,10 +1255,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 }, @@ -1518,20 +1485,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 }, @@ -1539,37 +1512,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 }, @@ -1580,15 +1579,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 }, @@ -1626,9 +1625,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 }, @@ -1645,9 +1644,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 }, @@ -1696,376 +1695,565 @@ const rv_opcode_data opcode_data[] = { { "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 }, - { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle8_v, rv_op_vle8_v, 0 }, - { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle16_v, rv_op_vle16_v, 0 }, - { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle32_v, rv_op_vle32_v, 0 }, - { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle64_v, rv_op_vle64_v, 0 }, - { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse8_v, rv_op_vse8_v, 0 }, - { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse16_v, rv_op_vse16_v, 0 }, - { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse32_v, rv_op_vse32_v, 0 }, - { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vse64_v, rv_op_vse64_v, 0 }, - { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vlm_v, rv_op_vlm_v, 0 }, - { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vsm_v, rv_op_vsm_v, 0 }, - { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse8_v, rv_op_vlse8_v, 0 }, - { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse16_v, rv_op_vlse16_v, 0 }, - { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse32_v, rv_op_vlse32_v, 0 }, - { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vlse64_v, rv_op_vlse64_v, 0 }, - { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse8_v, rv_op_vsse8_v, 0 }, - { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse16_v, rv_op_vsse16_v, 0 }, - { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse32_v, rv_op_vsse32_v, 0 }, - { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, rv_op_vsse64_v, rv_op_vsse64_v, 0 }, - { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei8_v, rv_op_vluxei8_v, 0 }, - { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei16_v, rv_op_vluxei16_v, 0 }, - { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei32_v, rv_op_vluxei32_v, 0 }, - { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vluxei64_v, rv_op_vluxei64_v, 0 }, - { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei8_v, rv_op_vloxei8_v, 0 }, - { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei16_v, rv_op_vloxei16_v, 0 }, - { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei32_v, rv_op_vloxei32_v, 0 }, - { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vloxei64_v, rv_op_vloxei64_v, 0 }, - { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei8_v, rv_op_vsuxei8_v, 0 }, - { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei16_v, rv_op_vsuxei16_v, 0 }, - { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei32_v, rv_op_vsuxei32_v, 0 }, - { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsuxei64_v, rv_op_vsuxei64_v, 0 }, - { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei8_v, rv_op_vsoxei8_v, 0 }, - { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei16_v, rv_op_vsoxei16_v, 0 }, - { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei32_v, rv_op_vsoxei32_v, 0 }, - { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, rv_op_vsoxei64_v, rv_op_vsoxei64_v, 0 }, - { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle8ff_v, rv_op_vle8ff_v, 0 }, - { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle16ff_v, rv_op_vle16ff_v, 0 }, - { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle32ff_v, rv_op_vle32ff_v, 0 }, - { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vle64ff_v, rv_op_vle64ff_v, 0 }, - { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re8_v, rv_op_vl1re8_v, 0 }, - { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re16_v, rv_op_vl1re16_v, 0 }, - { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re32_v, rv_op_vl1re32_v, 0 }, - { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl1re64_v, rv_op_vl1re64_v, 0 }, - { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re8_v, rv_op_vl2re8_v, 0 }, - { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re16_v, rv_op_vl2re16_v, 0 }, - { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re32_v, rv_op_vl2re32_v, 0 }, - { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl2re64_v, rv_op_vl2re64_v, 0 }, - { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re8_v, rv_op_vl4re8_v, 0 }, - { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re16_v, rv_op_vl4re16_v, 0 }, - { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re32_v, rv_op_vl4re32_v, 0 }, - { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl4re64_v, rv_op_vl4re64_v, 0 }, - { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re8_v, rv_op_vl8re8_v, 0 }, - { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re16_v, rv_op_vl8re16_v, 0 }, - { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re32_v, rv_op_vl8re32_v, 0 }, - { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vl8re64_v, rv_op_vl8re64_v, 0 }, - { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs1r_v, rv_op_vs1r_v, 0 }, - { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs2r_v, rv_op_vs2r_v, 0 }, - { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs4r_v, rv_op_vs4r_v, 0 }, - { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, rv_op_vs8r_v, rv_op_vs8r_v, 0 }, - { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vadd_vv, rv_op_vadd_vv, 0 }, - { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vadd_vx, rv_op_vadd_vx, 0 }, - { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vadd_vi, rv_op_vadd_vi, 0 }, - { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsub_vv, rv_op_vsub_vv, 0 }, - { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsub_vx, rv_op_vsub_vx, 0 }, - { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vrsub_vx, rv_op_vrsub_vx, 0 }, - { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vrsub_vi, rv_op_vrsub_vi, 0 }, - { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwaddu_vv, rv_op_vwaddu_vv, 0 }, - { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwaddu_vx, rv_op_vwaddu_vx, 0 }, - { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwadd_vv, rv_op_vwadd_vv, 0 }, - { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwadd_vx, rv_op_vwadd_vx, 0 }, - { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsubu_vv, rv_op_vwsubu_vv, 0 }, - { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsubu_vx, rv_op_vwsubu_vx, 0 }, - { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsub_vv, rv_op_vwsub_vv, 0 }, - { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsub_vx, rv_op_vwsub_vx, 0 }, - { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwaddu_wv, rv_op_vwaddu_wv, 0 }, - { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwaddu_wx, rv_op_vwaddu_wx, 0 }, - { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwadd_wv, rv_op_vwadd_wv, 0 }, - { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwadd_wx, rv_op_vwadd_wx, 0 }, - { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsubu_wv, rv_op_vwsubu_wv, 0 }, - { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsubu_wx, rv_op_vwsubu_wx, 0 }, - { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwsub_wv, rv_op_vwsub_wv, 0 }, - { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwsub_wx, rv_op_vwsub_wx, 0 }, - { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vadc_vvm, rv_op_vadc_vvm, 0 }, - { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vadc_vxm, rv_op_vadc_vxm, 0 }, - { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, rv_op_vadc_vim, rv_op_vadc_vim, 0 }, - { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vmadc_vvm, rv_op_vmadc_vvm, 0 }, - { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vmadc_vxm, rv_op_vmadc_vxm, 0 }, - { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, rv_op_vmadc_vim, rv_op_vmadc_vim, 0 }, - { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vsbc_vvm, rv_op_vsbc_vvm, 0 }, - { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vsbc_vxm, rv_op_vsbc_vxm, 0 }, - { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vmsbc_vvm, rv_op_vmsbc_vvm, 0 }, - { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vmsbc_vxm, rv_op_vmsbc_vxm, 0 }, - { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vand_vv, rv_op_vand_vv, 0 }, - { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vand_vx, rv_op_vand_vx, 0 }, - { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vand_vi, rv_op_vand_vi, 0 }, - { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vor_vv, rv_op_vor_vv, 0 }, - { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vor_vx, rv_op_vor_vx, 0 }, - { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vor_vi, rv_op_vor_vi, 0 }, - { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vxor_vv, rv_op_vxor_vv, 0 }, - { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vxor_vx, rv_op_vxor_vx, 0 }, - { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vxor_vi, rv_op_vxor_vi, 0 }, - { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsll_vv, rv_op_vsll_vv, 0 }, - { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsll_vx, rv_op_vsll_vx, 0 }, - { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vsll_vi, rv_op_vsll_vi, 0 }, - { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsrl_vv, rv_op_vsrl_vv, 0 }, - { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsrl_vx, rv_op_vsrl_vx, 0 }, - { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vsrl_vi, rv_op_vsrl_vi, 0 }, - { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsra_vv, rv_op_vsra_vv, 0 }, - { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsra_vx, rv_op_vsra_vx, 0 }, - { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vsra_vi, rv_op_vsra_vi, 0 }, - { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnsrl_wv, rv_op_vnsrl_wv, 0 }, - { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnsrl_wx, rv_op_vnsrl_wx, 0 }, - { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnsrl_wi, rv_op_vnsrl_wi, 0 }, - { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnsra_wv, rv_op_vnsra_wv, 0 }, - { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnsra_wx, rv_op_vnsra_wx, 0 }, - { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnsra_wi, rv_op_vnsra_wi, 0 }, - { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmseq_vv, rv_op_vmseq_vv, 0 }, - { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmseq_vx, rv_op_vmseq_vx, 0 }, - { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmseq_vi, rv_op_vmseq_vi, 0 }, - { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsne_vv, rv_op_vmsne_vv, 0 }, - { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsne_vx, rv_op_vmsne_vx, 0 }, - { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsne_vi, rv_op_vmsne_vi, 0 }, - { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsltu_vv, rv_op_vmsltu_vv, 0 }, - { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsltu_vx, rv_op_vmsltu_vx, 0 }, - { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmslt_vv, rv_op_vmslt_vv, 0 }, - { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmslt_vx, rv_op_vmslt_vx, 0 }, - { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsleu_vv, rv_op_vmsleu_vv, 0 }, - { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsleu_vx, rv_op_vmsleu_vx, 0 }, - { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsleu_vi, rv_op_vmsleu_vi, 0 }, - { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmsle_vv, rv_op_vmsle_vv, 0 }, - { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsle_vx, rv_op_vmsle_vx, 0 }, - { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsle_vi, rv_op_vmsle_vi, 0 }, - { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsgtu_vx, rv_op_vmsgtu_vx, 0 }, - { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsgtu_vi, rv_op_vmsgtu_vi, 0 }, - { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmsgt_vx, rv_op_vmsgt_vx, 0 }, - { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vmsgt_vi, rv_op_vmsgt_vi, 0 }, - { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vminu_vv, rv_op_vminu_vv, 0 }, - { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vminu_vx, rv_op_vminu_vx, 0 }, - { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmin_vv, rv_op_vmin_vv, 0 }, - { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmin_vx, rv_op_vmin_vx, 0 }, - { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmaxu_vv, rv_op_vmaxu_vv, 0 }, - { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmaxu_vx, rv_op_vmaxu_vx, 0 }, - { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmax_vv, rv_op_vmax_vv, 0 }, - { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmax_vx, rv_op_vmax_vx, 0 }, - { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmul_vv, rv_op_vmul_vv, 0 }, - { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmul_vx, rv_op_vmul_vx, 0 }, - { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmulh_vv, rv_op_vmulh_vv, 0 }, - { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmulh_vx, rv_op_vmulh_vx, 0 }, - { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmulhu_vv, rv_op_vmulhu_vv, 0 }, - { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmulhu_vx, rv_op_vmulhu_vx, 0 }, - { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmulhsu_vv, rv_op_vmulhsu_vv, 0 }, - { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vmulhsu_vx, rv_op_vmulhsu_vx, 0 }, - { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vdivu_vv, rv_op_vdivu_vv, 0 }, - { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vdivu_vx, rv_op_vdivu_vx, 0 }, - { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vdiv_vv, rv_op_vdiv_vv, 0 }, - { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vdiv_vx, rv_op_vdiv_vx, 0 }, - { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vremu_vv, rv_op_vremu_vv, 0 }, - { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vremu_vx, rv_op_vremu_vx, 0 }, - { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vrem_vv, rv_op_vrem_vv, 0 }, - { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vrem_vx, rv_op_vrem_vx, 0 }, - { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwmulu_vv, rv_op_vwmulu_vv, 0 }, - { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwmulu_vx, rv_op_vwmulu_vx, 0 }, - { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwmulsu_vv, rv_op_vwmulsu_vv, 0 }, - { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwmulsu_vx, rv_op_vwmulsu_vx, 0 }, - { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwmul_vv, rv_op_vwmul_vv, 0 }, - { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vwmul_vx, rv_op_vwmul_vx, 0 }, - { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vmacc_vv, rv_op_vmacc_vv, 0 }, - { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vmacc_vx, rv_op_vmacc_vx, 0 }, - { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vnmsac_vv, rv_op_vnmsac_vv, 0 }, - { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vnmsac_vx, rv_op_vnmsac_vx, 0 }, - { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vmadd_vv, rv_op_vmadd_vv, 0 }, - { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vmadd_vx, rv_op_vmadd_vx, 0 }, - { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vnmsub_vv, rv_op_vnmsub_vv, 0 }, - { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vnmsub_vx, rv_op_vnmsub_vx, 0 }, - { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vwmaccu_vv, rv_op_vwmaccu_vv, 0 }, - { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmaccu_vx, rv_op_vwmaccu_vx, 0 }, - { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vwmacc_vv, rv_op_vwmacc_vv, 0 }, - { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmacc_vx, rv_op_vwmacc_vx, 0 }, - { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vwmaccsu_vv, rv_op_vwmaccsu_vv, 0 }, - { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmaccsu_vx, rv_op_vwmaccsu_vx, 0 }, - { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, rv_op_vwmaccus_vx, rv_op_vwmaccus_vx, 0 }, - { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1, NULL, rv_op_vmv_v_v, rv_op_vmv_v_v, 0 }, - { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, rv_op_vmv_v_x, rv_op_vmv_v_x, 0 }, - { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm, NULL, rv_op_vmv_v_i, rv_op_vmv_v_i, 0 }, - { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, rv_op_vmerge_vvm, rv_op_vmerge_vvm, 0 }, - { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, rv_op_vmerge_vxm, rv_op_vmerge_vxm, 0 }, - { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, rv_op_vmerge_vim, rv_op_vmerge_vim, 0 }, - { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsaddu_vv, rv_op_vsaddu_vv, 0 }, - { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsaddu_vx, rv_op_vsaddu_vx, 0 }, - { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vsaddu_vi, rv_op_vsaddu_vi, 0 }, - { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsadd_vv, rv_op_vsadd_vv, 0 }, - { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsadd_vx, rv_op_vsadd_vx, 0 }, - { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, rv_op_vsadd_vi, rv_op_vsadd_vi, 0 }, - { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssubu_vv, rv_op_vssubu_vv, 0 }, - { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssubu_vx, rv_op_vssubu_vx, 0 }, - { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssub_vv, rv_op_vssub_vv, 0 }, - { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssub_vx, rv_op_vssub_vx, 0 }, - { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vaadd_vv, rv_op_vaadd_vv, 0 }, - { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vaadd_vx, rv_op_vaadd_vx, 0 }, - { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vaaddu_vv, rv_op_vaaddu_vv, 0 }, - { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vaaddu_vx, rv_op_vaaddu_vx, 0 }, - { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vasub_vv, rv_op_vasub_vv, 0 }, - { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vasub_vx, rv_op_vasub_vx, 0 }, - { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vasubu_vv, rv_op_vasubu_vv, 0 }, - { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vasubu_vx, rv_op_vasubu_vx, 0 }, - { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vsmul_vv, rv_op_vsmul_vv, 0 }, - { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vsmul_vx, rv_op_vsmul_vx, 0 }, - { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssrl_vv, rv_op_vssrl_vv, 0 }, - { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssrl_vx, rv_op_vssrl_vx, 0 }, - { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vssrl_vi, rv_op_vssrl_vi, 0 }, - { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vssra_vv, rv_op_vssra_vv, 0 }, - { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vssra_vx, rv_op_vssra_vx, 0 }, - { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vssra_vi, rv_op_vssra_vi, 0 }, - { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnclipu_wv, rv_op_vnclipu_wv, 0 }, - { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnclipu_wx, rv_op_vnclipu_wx, 0 }, - { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnclipu_wi, rv_op_vnclipu_wi, 0 }, - { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vnclip_wv, rv_op_vnclip_wv, 0 }, - { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vnclip_wx, rv_op_vnclip_wx, 0 }, - { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vnclip_wi, rv_op_vnclip_wi, 0 }, - { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfadd_vv, rv_op_vfadd_vv, 0 }, - { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfadd_vf, rv_op_vfadd_vf, 0 }, - { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsub_vv, rv_op_vfsub_vv, 0 }, - { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsub_vf, rv_op_vfsub_vf, 0 }, - { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfrsub_vf, rv_op_vfrsub_vf, 0 }, - { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwadd_vv, rv_op_vfwadd_vv, 0 }, - { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwadd_vf, rv_op_vfwadd_vf, 0 }, - { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwadd_wv, rv_op_vfwadd_wv, 0 }, - { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwadd_wf, rv_op_vfwadd_wf, 0 }, - { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwsub_vv, rv_op_vfwsub_vv, 0 }, - { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwsub_vf, rv_op_vfwsub_vf, 0 }, - { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwsub_wv, rv_op_vfwsub_wv, 0 }, - { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwsub_wf, rv_op_vfwsub_wf, 0 }, - { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfmul_vv, rv_op_vfmul_vv, 0 }, - { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfmul_vf, rv_op_vfmul_vf, 0 }, - { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfdiv_vv, rv_op_vfdiv_vv, 0 }, - { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfdiv_vf, rv_op_vfdiv_vf, 0 }, - { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfrdiv_vf, rv_op_vfrdiv_vf, 0 }, - { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwmul_vv, rv_op_vfwmul_vv, 0 }, - { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfwmul_vf, rv_op_vfwmul_vf, 0 }, - { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmacc_vv, rv_op_vfmacc_vv, 0 }, - { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmacc_vf, rv_op_vfmacc_vf, 0 }, - { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmacc_vv, rv_op_vfnmacc_vv, 0 }, - { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmacc_vf, rv_op_vfnmacc_vf, 0 }, - { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmsac_vv, rv_op_vfmsac_vv, 0 }, - { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmsac_vf, rv_op_vfmsac_vf, 0 }, - { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmsac_vv, rv_op_vfnmsac_vv, 0 }, - { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmsac_vf, rv_op_vfnmsac_vf, 0 }, - { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmadd_vv, rv_op_vfmadd_vv, 0 }, - { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmadd_vf, rv_op_vfmadd_vf, 0 }, - { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmadd_vv, rv_op_vfnmadd_vv, 0 }, - { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmadd_vf, rv_op_vfnmadd_vf, 0 }, - { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfmsub_vv, rv_op_vfmsub_vv, 0 }, - { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfmsub_vf, rv_op_vfmsub_vf, 0 }, - { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfnmsub_vv, rv_op_vfnmsub_vv, 0 }, - { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfnmsub_vf, rv_op_vfnmsub_vf, 0 }, - { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwmacc_vv, rv_op_vfwmacc_vv, 0 }, - { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwmacc_vf, rv_op_vfwmacc_vf, 0 }, - { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwnmacc_vv, rv_op_vfwnmacc_vv, 0 }, - { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwnmacc_vf, rv_op_vfwnmacc_vf, 0 }, - { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwmsac_vv, rv_op_vfwmsac_vv, 0 }, - { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwmsac_vf, rv_op_vfwmsac_vf, 0 }, - { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, rv_op_vfwnmsac_vv, rv_op_vfwnmsac_vv, 0 }, - { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, rv_op_vfwnmsac_vf, rv_op_vfwnmsac_vf, 0 }, - { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vfsqrt_v, rv_op_vfsqrt_v, 0 }, - { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vfrsqrt7_v, rv_op_vfrsqrt7_v, 0 }, - { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vfrec7_v, rv_op_vfrec7_v, 0 }, - { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfmin_vv, rv_op_vfmin_vv, 0 }, - { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfmin_vf, rv_op_vfmin_vf, 0 }, - { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfmax_vv, rv_op_vfmax_vv, 0 }, - { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfmax_vf, rv_op_vfmax_vf, 0 }, - { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsgnj_vv, rv_op_vfsgnj_vv, 0 }, - { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsgnj_vf, rv_op_vfsgnj_vf, 0 }, - { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsgnjn_vv, rv_op_vfsgnjn_vv, 0 }, - { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsgnjn_vf, rv_op_vfsgnjn_vf, 0 }, - { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfsgnjx_vv, rv_op_vfsgnjx_vv, 0 }, - { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfsgnjx_vf, rv_op_vfsgnjx_vf, 0 }, - { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfslide1up_vf, rv_op_vfslide1up_vf, 0 }, - { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vfslide1down_vf, rv_op_vfslide1down_vf, 0 }, - { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmfeq_vv, rv_op_vmfeq_vv, 0 }, - { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfeq_vf, rv_op_vmfeq_vf, 0 }, - { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmfne_vv, rv_op_vmfne_vv, 0 }, - { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfne_vf, rv_op_vmfne_vf, 0 }, - { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmflt_vv, rv_op_vmflt_vv, 0 }, - { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmflt_vf, rv_op_vmflt_vf, 0 }, - { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmfle_vv, rv_op_vmfle_vv, 0 }, - { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfle_vf, rv_op_vmfle_vf, 0 }, - { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfgt_vf, rv_op_vmfgt_vf, 0 }, - { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, rv_op_vmfge_vf, rv_op_vmfge_vf, 0 }, - { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfclass_v, rv_op_vfclass_v, 0 }, - { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl, NULL, rv_op_vfmerge_vfm, rv_op_vfmerge_vfm, 0 }, - { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, rv_op_vfmv_v_f, rv_op_vfmv_v_f, 0 }, - { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_xu_f_v, rv_op_vfcvt_xu_f_v, 0 }, - { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_x_f_v, rv_op_vfcvt_x_f_v, 0 }, - { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_f_xu_v, rv_op_vfcvt_f_xu_v, 0 }, - { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_f_x_v, rv_op_vfcvt_f_x_v, 0 }, - { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_rtz_xu_f_v, rv_op_vfcvt_rtz_xu_f_v, 0 }, - { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfcvt_rtz_x_f_v, rv_op_vfcvt_rtz_x_f_v, 0 }, - { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_xu_f_v, rv_op_vfwcvt_xu_f_v, 0 }, - { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_x_f_v, rv_op_vfwcvt_x_f_v, 0 }, - { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_f_xu_v, rv_op_vfwcvt_f_xu_v, 0 }, - { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_f_x_v, rv_op_vfwcvt_f_x_v, 0 }, - { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_f_f_v, rv_op_vfwcvt_f_f_v, 0 }, - { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_rtz_xu_f_v, rv_op_vfwcvt_rtz_xu_f_v, 0 }, - { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfwcvt_rtz_x_f_v, rv_op_vfwcvt_rtz_x_f_v, 0 }, - { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_xu_f_w, rv_op_vfncvt_xu_f_w, 0 }, - { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_x_f_w, rv_op_vfncvt_x_f_w, 0 }, - { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_f_xu_w, rv_op_vfncvt_f_xu_w, 0 }, - { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_f_x_w, rv_op_vfncvt_f_x_w, 0 }, - { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_f_f_w, rv_op_vfncvt_f_f_w, 0 }, - { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_rod_f_f_w, rv_op_vfncvt_rod_f_f_w, 0 }, - { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_rtz_xu_f_w, rv_op_vfncvt_rtz_xu_f_w, 0 }, - { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vfncvt_rtz_x_f_w, rv_op_vfncvt_rtz_x_f_w, 0 }, - { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredsum_vs, rv_op_vredsum_vs, 0 }, - { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredand_vs, rv_op_vredand_vs, 0 }, - { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredor_vs, rv_op_vredor_vs, 0 }, - { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredxor_vs, rv_op_vredxor_vs, 0 }, - { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredminu_vs, rv_op_vredminu_vs, 0 }, - { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredmin_vs, rv_op_vredmin_vs, 0 }, - { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredmaxu_vs, rv_op_vredmaxu_vs, 0 }, - { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vredmax_vs, rv_op_vredmax_vs, 0 }, - { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwredsumu_vs, rv_op_vwredsumu_vs, 0 }, - { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vwredsum_vs, rv_op_vwredsum_vs, 0 }, - { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredusum_vs, rv_op_vfredusum_vs, 0 }, - { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredosum_vs, rv_op_vfredosum_vs, 0 }, - { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredmin_vs, rv_op_vfredmin_vs, 0 }, - { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfredmax_vs, rv_op_vfredmax_vs, 0 }, - { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwredusum_vs, rv_op_vfwredusum_vs, 0 }, - { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vfwredosum_vs, rv_op_vfwredosum_vs, 0 }, - { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmand_mm, rv_op_vmand_mm, 0 }, - { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmnand_mm, rv_op_vmnand_mm, 0 }, - { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmandn_mm, rv_op_vmandn_mm, 0 }, - { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmxor_mm, rv_op_vmxor_mm, 0 }, - { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmor_mm, rv_op_vmor_mm, 0 }, - { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmnor_mm, rv_op_vmnor_mm, 0 }, - { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmorn_mm, rv_op_vmorn_mm, 0 }, - { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vmxnor_mm, rv_op_vmxnor_mm, 0 }, - { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, rv_op_vcpop_m, rv_op_vcpop_m, 0 }, - { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, rv_op_vfirst_m, rv_op_vfirst_m, 0 }, - { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vmsbf_m, rv_op_vmsbf_m, 0 }, - { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vmsif_m, rv_op_vmsif_m, 0 }, - { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vmsof_m, rv_op_vmsof_m, 0 }, - { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_viota_m, rv_op_viota_m, 0 }, - { "vid.v", rv_codec_v_r, rv_fmt_vd_vm, NULL, rv_op_vid_v, rv_op_vid_v, 0 }, - { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2, NULL, rv_op_vmv_x_s, rv_op_vmv_x_s, 0 }, - { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, rv_op_vmv_s_x, rv_op_vmv_s_x, 0 }, - { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, rv_op_vfmv_f_s, rv_op_vfmv_f_s, 0 }, - { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, rv_op_vfmv_s_f, rv_op_vfmv_s_f, 0 }, - { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslideup_vx, rv_op_vslideup_vx, 0 }, - { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vslideup_vi, rv_op_vslideup_vi, 0 }, - { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslide1up_vx, rv_op_vslide1up_vx, 0 }, - { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslidedown_vx, rv_op_vslidedown_vx, 0 }, - { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vslidedown_vi, rv_op_vslidedown_vi, 0 }, - { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vslide1down_vx, rv_op_vslide1down_vx, 0 }, - { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vrgather_vv, rv_op_vrgather_vv, 0 }, - { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, rv_op_vrgatherei16_vv, rv_op_vrgatherei16_vv, 0 }, - { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, rv_op_vrgather_vx, rv_op_vrgather_vx, 0 }, - { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, rv_op_vrgather_vi, rv_op_vrgather_vi, 0 }, - { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, rv_op_vcompress_vm, rv_op_vcompress_vm, 0 }, - { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv1r_v, rv_op_vmv1r_v, 0 }, - { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv2r_v, rv_op_vmv2r_v, 0 }, - { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv4r_v, rv_op_vmv4r_v, 0 }, - { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, rv_op_vmv8r_v, rv_op_vmv8r_v, 0 }, - { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vzext_vf2, rv_op_vzext_vf2, 0 }, - { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vzext_vf4, rv_op_vzext_vf4, 0 }, - { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vzext_vf8, rv_op_vzext_vf8, 0 }, - { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf2, rv_op_vsext_vf2, 0 }, - { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf4, rv_op_vsext_vf4, 0 }, - { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, rv_op_vsext_vf8, rv_op_vsext_vf8, 0 }, - { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, rv_op_vsetvli, rv_op_vsetvli, 0 }, - { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, rv_op_vsetivli, rv_op_vsetivli, 0 }, - { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, rv_op_vsetvl, rv_op_vsetvl, 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 }, + { "mop.r.0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.2", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.3", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.4", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.5", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.6", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.7", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.9", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.10", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.11", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.12", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.13", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.14", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.15", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.16", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.17", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.18", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.19", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.20", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.21", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.22", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.23", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.24", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.25", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.26", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.27", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.28", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.29", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.30", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.r.31", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, + { "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "c.mop.1", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.3", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.5", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.7", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.9", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.11", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.13", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "c.mop.15", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "wrs.sto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "wrs.nto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "lpad", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 }, + { "sspush", rv_codec_r, rv_fmt_rs2, NULL, 0, 0, 0 }, + { "sspopchk", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, + { "ssrdp", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 }, + { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, NULL, rv_op_sspush, + rv_op_sspush, 0 }, + { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk, + rv_op_sspopchk, 0 }, }; /* CSR names */ @@ -2083,7 +2271,9 @@ static const char *csr_name(int csrno) case 0x0009: return "vxsat"; case 0x000a: return "vxrm"; case 0x000f: return "vcsr"; + case 0x0011: return "ssp"; case 0x0015: return "seed"; + case 0x0017: return "jvt"; case 0x0040: return "uscratch"; case 0x0041: return "uepc"; case 0x0042: return "ucause"; @@ -2159,7 +2349,22 @@ static const char *csr_name(int csrno) case 0x0383: return "mibound"; case 0x0384: return "mdbase"; case 0x0385: return "mdbound"; - case 0x03a0: return "pmpcfg3"; + case 0x03a0: return "pmpcfg0"; + case 0x03a1: return "pmpcfg1"; + case 0x03a2: return "pmpcfg2"; + case 0x03a3: return "pmpcfg3"; + case 0x03a4: return "pmpcfg4"; + case 0x03a5: return "pmpcfg5"; + case 0x03a6: return "pmpcfg6"; + case 0x03a7: return "pmpcfg7"; + case 0x03a8: return "pmpcfg8"; + case 0x03a9: return "pmpcfg9"; + case 0x03aa: return "pmpcfg10"; + case 0x03ab: return "pmpcfg11"; + case 0x03ac: return "pmpcfg12"; + case 0x03ad: return "pmpcfg13"; + case 0x03ae: return "pmpcfg14"; + case 0x03af: return "pmpcfg15"; case 0x03b0: return "pmpaddr0"; case 0x03b1: return "pmpaddr1"; case 0x03b2: return "pmpaddr2"; @@ -2173,9 +2378,57 @@ 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 0x03c0: return "pmpaddr16"; + case 0x03c1: return "pmpaddr17"; + case 0x03c2: return "pmpaddr18"; + case 0x03c3: return "pmpaddr19"; + case 0x03c4: return "pmpaddr20"; + case 0x03c5: return "pmpaddr21"; + case 0x03c6: return "pmpaddr22"; + case 0x03c7: return "pmpaddr23"; + case 0x03c8: return "pmpaddr24"; + case 0x03c9: return "pmpaddr25"; + case 0x03ca: return "pmpaddr26"; + case 0x03cb: return "pmpaddr27"; + case 0x03cc: return "pmpaddr28"; + case 0x03cd: return "pmpaddr29"; + case 0x03ce: return "pmpaddr30"; + case 0x03cf: return "pmpaddr31"; + case 0x03d0: return "pmpaddr32"; + case 0x03d1: return "pmpaddr33"; + case 0x03d2: return "pmpaddr34"; + case 0x03d3: return "pmpaddr35"; + case 0x03d4: return "pmpaddr36"; + case 0x03d5: return "pmpaddr37"; + case 0x03d6: return "pmpaddr38"; + case 0x03d7: return "pmpaddr39"; + case 0x03d8: return "pmpaddr40"; + case 0x03d9: return "pmpaddr41"; + case 0x03da: return "pmpaddr42"; + case 0x03db: return "pmpaddr43"; + case 0x03dc: return "pmpaddr44"; + case 0x03dd: return "pmpaddr45"; + case 0x03de: return "pmpaddr46"; + case 0x03df: return "pmpaddr47"; + case 0x03e0: return "pmpaddr48"; + case 0x03e1: return "pmpaddr49"; + case 0x03e2: return "pmpaddr50"; + case 0x03e3: return "pmpaddr51"; + case 0x03e4: return "pmpaddr52"; + case 0x03e5: return "pmpaddr53"; + case 0x03e6: return "pmpaddr54"; + case 0x03e7: return "pmpaddr55"; + case 0x03e8: return "pmpaddr56"; + case 0x03e9: return "pmpaddr57"; + case 0x03ea: return "pmpaddr58"; + case 0x03eb: return "pmpaddr59"; + case 0x03ec: return "pmpaddr60"; + case 0x03ed: return "pmpaddr61"; + case 0x03ee: return "pmpaddr62"; + case 0x03ef: return "pmpaddr63"; case 0x0780: return "mtohost"; case 0x0781: return "mfromhost"; case 0x0782: return "mreset"; @@ -2287,9 +2540,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) { @@ -2306,6 +2559,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; @@ -2324,9 +2595,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; } @@ -2340,13 +2611,26 @@ 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)) { + if (dec->cfg->ext_zcmop) { + if ((((inst >> 2) & 0b111111) == 0b100000) && + (((inst >> 11) & 0b11) == 0b0)) { + unsigned int cmop_code = 0; + cmop_code = ((inst >> 8) & 0b111); + op = rv_c_mop_1 + cmop_code; + if (dec->cfg->ext_zicfiss) { + op = (cmop_code == 0) ? rv_op_c_sspush : op; + op = (cmop_code == 2) ? rv_op_c_sspopchk : op; + } + break; + } + } + 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; @@ -2362,6 +2646,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; } @@ -2372,7 +2667,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; @@ -2392,17 +2687,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; } @@ -2417,6 +2712,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; @@ -2430,9 +2771,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; @@ -2444,17 +2785,17 @@ 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)) { + 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)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + 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; @@ -2465,19 +2806,20 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + 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)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle16_v; break; case 16: op = rv_op_vle16ff_v; break; } @@ -2488,15 +2830,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: - switch (((inst >> 20) & 0b111111111111)) { + 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)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle32_v; break; case 16: op = rv_op_vle32ff_v; break; } @@ -2507,15 +2849,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 7: - switch (((inst >> 20) & 0b111111111111)) { + 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)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vle64_v; break; case 16: op = rv_op_vle64ff_v; break; } @@ -2528,25 +2870,25 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } 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; @@ -2561,7 +2903,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) { @@ -2573,7 +2915,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; @@ -2588,10 +2930,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; @@ -2612,15 +2954,21 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 7: op = rv_op_andi; break; } break; - case 5: op = rv_op_auipc; break; + case 5: + op = rv_op_auipc; + if (dec->cfg->ext_zicfilp && + (((inst >> 7) & 0b11111) == 0b00000)) { + op = rv_op_lpad; + } + 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; @@ -2630,7 +2978,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; @@ -2639,7 +2987,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; @@ -2648,17 +2996,17 @@ 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)) { + 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)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse8_v; break; case 11: op = rv_op_vsm_v; break; } @@ -2668,13 +3016,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse16_v; break; } break; @@ -2684,9 +3033,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse32_v; break; } break; @@ -2696,9 +3045,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 7: - switch (((inst >> 26) & 0b111)) { + switch ((inst >> 26) & 0b111) { case 0: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: op = rv_op_vse64_v; break; } break; @@ -2710,56 +3059,83 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 11: - switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { + switch (((inst >> 24) & 0b11111000) | + ((inst >> 12) & 0b00000111)) { + case 0: op = rv_op_amoadd_b; break; + case 1: op = rv_op_amoadd_h; break; case 2: op = rv_op_amoadd_w; break; case 3: op = rv_op_amoadd_d; break; case 4: op = rv_op_amoadd_q; break; + case 8: op = rv_op_amoswap_b; break; + case 9: op = rv_op_amoswap_h; break; case 10: op = rv_op_amoswap_w; break; 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; case 26: op = rv_op_sc_w; break; case 27: op = rv_op_sc_d; break; case 28: op = rv_op_sc_q; break; + case 32: op = rv_op_amoxor_b; break; + case 33: op = rv_op_amoxor_h; break; 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 40: op = rv_op_amocas_b; break; + case 41: op = rv_op_amocas_h; 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 64: op = rv_op_amoor_b; break; + case 65: op = rv_op_amoor_h; 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; + case 74: op = rv_op_ssamoswap_w; break; + case 75: op = rv_op_ssamoswap_d; break; + case 96: op = rv_op_amoand_b; break; + case 97: op = rv_op_amoand_h; break; case 98: op = rv_op_amoand_w; break; case 99: op = rv_op_amoand_d; break; case 100: op = rv_op_amoand_q; break; + case 128: op = rv_op_amomin_b; break; + case 129: op = rv_op_amomin_h; break; case 130: op = rv_op_amomin_w; break; case 131: op = rv_op_amomin_d; break; case 132: op = rv_op_amomin_q; break; + case 160: op = rv_op_amomax_b; break; + case 161: op = rv_op_amomax_h; break; case 162: op = rv_op_amomax_w; break; case 163: op = rv_op_amomax_d; break; case 164: op = rv_op_amomax_q; break; + case 192: op = rv_op_amominu_b; break; + case 193: op = rv_op_amominu_h; break; case 194: op = rv_op_amominu_w; break; case 195: op = rv_op_amominu_d; break; case 196: op = rv_op_amominu_q; break; + case 224: op = rv_op_amomaxu_b; break; + case 225: op = rv_op_amomaxu_h; break; case 226: op = rv_op_amomaxu_w; break; case 227: op = rv_op_amomaxu_d; break; case 228: op = rv_op_amomaxu_q; break; } 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; @@ -2791,6 +3167,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; @@ -2830,7 +3208,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; @@ -2856,35 +3235,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; @@ -2898,100 +3277,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; @@ -2999,15 +3426,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; @@ -3015,7 +3443,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; @@ -3023,7 +3451,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; @@ -3031,7 +3459,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; @@ -3039,45 +3467,70 @@ 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)) { + switch ((inst >> 12) & 0b111) { case 0: - switch (((inst >> 26) & 0b111111)) { + 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; @@ -3088,10 +3541,20 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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 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 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; @@ -3120,10 +3583,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + 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; @@ -3136,12 +3600,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 9: op = rv_op_vfsgnjn_vv; break; case 10: op = rv_op_vfsgnjx_vv; break; case 16: - switch (((inst >> 15) & 0b11111)) { + switch ((inst >> 15) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_f_s; break; } break; case 18: - switch (((inst >> 15) & 0b11111)) { + 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; @@ -3153,6 +3617,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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; @@ -3163,10 +3628,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + 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; @@ -3194,6 +3660,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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; @@ -3201,7 +3668,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 2: - switch (((inst >> 26) & 0b111111)) { + 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; @@ -3214,30 +3681,42 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + 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)) { + 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)) { + 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; + 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; @@ -3278,7 +3757,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 3: - switch (((inst >> 26) & 0b111111)) { + 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; @@ -3287,8 +3766,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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 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; @@ -3305,7 +3789,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 33: op = rv_op_vsadd_vi; break; case 37: op = rv_op_vsll_vi; break; case 39: - switch (((inst >> 15) & 0b11111)) { + 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; @@ -3320,11 +3804,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + 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; @@ -3337,10 +3823,20 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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 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 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; @@ -3369,10 +3865,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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)) { + 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; @@ -3383,7 +3880,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 14: op = rv_op_vfslide1up_vf; break; case 15: op = rv_op_vfslide1down_vf; break; case 16: - switch (((inst >> 20) & 0b11111)) { + switch ((inst >> 20) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_s_f; break; } break; @@ -3416,6 +3913,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) 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; @@ -3423,15 +3921,17 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: - switch (((inst >> 26) & 0b111111)) { + 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)) { + switch ((inst >> 20) & 0b11111) { case 0: if ((inst >> 25) & 1) op = rv_op_vmv_s_x; break; } break; @@ -3476,15 +3976,15 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } 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; } @@ -3492,7 +3992,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; @@ -3502,32 +4002,35 @@ 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; + case 416: op = rv_op_wrs_nto; break; + case 928: op = rv_op_wrs_sto; 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; @@ -3535,17 +4038,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; @@ -3554,13 +4057,92 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: op = rv_op_csrrw; break; case 2: op = rv_op_csrrs; break; case 3: op = rv_op_csrrc; break; + case 4: + if (dec->cfg->ext_zimop) { + int imm_mop5, imm_mop3, reg_num; + if ((extract32(inst, 22, 10) & 0b1011001111) + == 0b1000000111) { + imm_mop5 = deposit32(deposit32(extract32(inst, 20, 2), + 2, 2, + extract32(inst, 26, 2)), + 4, 1, extract32(inst, 30, 1)); + op = rv_mop_r_0 + imm_mop5; + /* if zicfiss enabled and mop5 is shadow stack */ + if (dec->cfg->ext_zicfiss && + ((imm_mop5 & 0b11100) == 0b11100)) { + /* rs1=0 means ssrdp */ + if ((inst & (0b011111 << 15)) == 0) { + op = rv_op_ssrdp; + } + /* rd=0 means sspopchk */ + reg_num = (inst >> 15) & 0b011111; + if (((inst & (0b011111 << 7)) == 0) && + ((reg_num == 1) || (reg_num == 5))) { + op = rv_op_sspopchk; + } + } + } else if ((extract32(inst, 25, 7) & 0b1011001) + == 0b1000001) { + imm_mop3 = deposit32(extract32(inst, 26, 2), + 2, 1, extract32(inst, 30, 1)); + op = rv_mop_rr_0 + imm_mop3; + /* if zicfiss enabled and mop3 is shadow stack */ + if (dec->cfg->ext_zicfiss && + ((imm_mop3 & 0b111) == 0b111)) { + /* rs1=0 and rd=0 means sspush */ + reg_num = (inst >> 20) & 0b011111; + if (((inst & (0b011111 << 15)) == 0) && + ((inst & (0b011111 << 7)) == 0) && + ((reg_num == 1) || (reg_num == 5))) { + op = rv_op_sspush; + } + } + } + } + break; case 5: op = rv_op_csrrwi; break; case 6: op = rv_op_csrrsi; break; 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; @@ -3661,6 +4243,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; @@ -3868,6 +4465,12 @@ 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; @@ -3883,10 +4486,76 @@ 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); +} + +static uint32_t operand_lpl(rv_inst inst) +{ + return inst >> 12; +} + /* decode operands */ 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) { @@ -4189,6 +4858,12 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) 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); @@ -4196,9 +4871,81 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) break; case rv_codec_vsetivli: dec->rd = operand_rd(inst); - dec->imm = operand_vimm(inst); + dec->imm = extract32(inst, 15, 5); 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; + case rv_codec_lp: + dec->imm = operand_lpl(inst); + break; + case rv_codec_cmop_ss: + dec->rd = rv_ireg_zero; + dec->rs1 = dec->rs2 = operand_crs1(inst); + dec->imm = 0; + break; }; } @@ -4313,7 +5060,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 @@ -4330,228 +5078,256 @@ static size_t inst_length(rv_inst inst) /* format instruction */ -static void append(char *s1, const char *s2, size_t n) +static GString *format_inst(size_t tab, rv_decode *dec) { - size_t l1 = strlen(s1); - if (n - l1 - 1 > 0) { - strncat(s1, s2, n - l1); - } -} - -static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) -{ - char tmp[64]; + const rv_opcode_data *opcode_data = dec->opcode_data; + GString *buf = g_string_sized_new(64); const char *fmt; fmt = opcode_data[dec->op].format; while (*fmt) { switch (*fmt) { case 'O': - append(buf, opcode_data[dec->op].name, buflen); + g_string_append(buf, opcode_data[dec->op].name); break; case '(': - append(buf, "(", buflen); - break; case ',': - append(buf, ",", buflen); - break; case ')': - append(buf, ")", buflen); + case '-': + g_string_append_c(buf, *fmt); break; case 'b': - snprintf(tmp, sizeof(tmp), "%d", dec->bs); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->bs); break; case 'n': - snprintf(tmp, sizeof(tmp), "%d", dec->rnum); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->rnum); break; case '0': - append(buf, rv_ireg_name_sym[dec->rd], buflen); + g_string_append(buf, rv_ireg_name_sym[dec->rd]); break; case '1': - append(buf, rv_ireg_name_sym[dec->rs1], buflen); + g_string_append(buf, rv_ireg_name_sym[dec->rs1]); break; case '2': - append(buf, rv_ireg_name_sym[dec->rs2], buflen); + g_string_append(buf, rv_ireg_name_sym[dec->rs2]); break; case '3': - append(buf, rv_freg_name_sym[dec->rd], buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rd]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rd]); + } break; case '4': - append(buf, rv_freg_name_sym[dec->rs1], buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rs1]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rs1]); + } break; case '5': - append(buf, rv_freg_name_sym[dec->rs2], buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rs2]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rs2]); + } break; case '6': - append(buf, rv_freg_name_sym[dec->rs3], buflen); + if (dec->cfg->ext_zfinx) { + g_string_append(buf, rv_ireg_name_sym[dec->rs3]); + } else { + g_string_append(buf, rv_freg_name_sym[dec->rs3]); + } break; case '7': - snprintf(tmp, sizeof(tmp), "%d", dec->rs1); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->rs1); break; case 'i': - snprintf(tmp, sizeof(tmp), "%d", dec->imm); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%d", dec->imm); break; case 'u': - snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b11111)); - append(buf, tmp, buflen); + g_string_append_printf(buf, "%u", ((uint32_t)dec->imm & 0b111111)); + break; + case 'j': + g_string_append_printf(buf, "%d", dec->imm1); break; case 'o': - snprintf(tmp, sizeof(tmp), "%d", dec->imm); - append(buf, tmp, buflen); - while (strlen(buf) < tab * 2) { - append(buf, " ", buflen); + g_string_append_printf(buf, "%d", dec->imm); + while (buf->len < tab * 2) { + g_string_append_c(buf, ' '); + } + g_string_append_printf(buf, "# 0x%" PRIx64, dec->pc + dec->imm); + break; + case 'U': + fmt++; + g_string_append_printf(buf, "%d", dec->imm >> 12); + if (*fmt == 'o') { + while (buf->len < tab * 2) { + g_string_append_c(buf, ' '); + } + g_string_append_printf(buf, "# 0x%" PRIx64, dec->pc + dec->imm); } - 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) { - append(buf, name, buflen); + g_string_append(buf, name); } else { - snprintf(tmp, sizeof(tmp), "0x%03x", dec->imm & 0xfff); - append(buf, tmp, buflen); + g_string_append_printf(buf, "0x%03x", dec->imm & 0xfff); } break; } case 'r': switch (dec->rm) { case rv_rm_rne: - append(buf, "rne", buflen); + g_string_append(buf, "rne"); break; case rv_rm_rtz: - append(buf, "rtz", buflen); + g_string_append(buf, "rtz"); break; case rv_rm_rdn: - append(buf, "rdn", buflen); + g_string_append(buf, "rdn"); break; case rv_rm_rup: - append(buf, "rup", buflen); + g_string_append(buf, "rup"); break; case rv_rm_rmm: - append(buf, "rmm", buflen); + g_string_append(buf, "rmm"); break; case rv_rm_dyn: - append(buf, "dyn", buflen); + g_string_append(buf, "dyn"); break; default: - append(buf, "inv", buflen); + g_string_append(buf, "inv"); break; } break; case 'p': if (dec->pred & rv_fence_i) { - append(buf, "i", buflen); + g_string_append_c(buf, 'i'); } if (dec->pred & rv_fence_o) { - append(buf, "o", buflen); + g_string_append_c(buf, 'o'); } if (dec->pred & rv_fence_r) { - append(buf, "r", buflen); + g_string_append_c(buf, 'r'); } if (dec->pred & rv_fence_w) { - append(buf, "w", buflen); + g_string_append_c(buf, 'w'); } break; case 's': if (dec->succ & rv_fence_i) { - append(buf, "i", buflen); + g_string_append_c(buf, 'i'); } if (dec->succ & rv_fence_o) { - append(buf, "o", buflen); + g_string_append_c(buf, 'o'); } if (dec->succ & rv_fence_r) { - append(buf, "r", buflen); + g_string_append_c(buf, 'r'); } if (dec->succ & rv_fence_w) { - append(buf, "w", buflen); + g_string_append_c(buf, 'w'); } break; case '\t': - while (strlen(buf) < tab) { - append(buf, " ", buflen); + while (buf->len < tab) { + g_string_append_c(buf, ' '); } break; case 'A': if (dec->aq) { - append(buf, ".aq", buflen); + g_string_append(buf, ".aq"); } break; case 'R': if (dec->rl) { - append(buf, ".rl", buflen); + g_string_append(buf, ".rl"); } break; case 'l': - append(buf, ",v0", buflen); + g_string_append(buf, ",v0"); break; case 'm': if (dec->vm == 0) { - append(buf, ",v0.t", buflen); + g_string_append(buf, ",v0.t"); } break; case 'D': - append(buf, rv_vreg_name_sym[dec->rd], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rd]); break; case 'E': - append(buf, rv_vreg_name_sym[dec->rs1], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rs1]); break; case 'F': - append(buf, rv_vreg_name_sym[dec->rs2], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rs2]); break; case 'G': - append(buf, rv_vreg_name_sym[dec->rs3], buflen); + g_string_append(buf, rv_vreg_name_sym[dec->rs3]); 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); + + g_string_append_printf(buf, "e%d,m", sew); if (flmul) { switch (lmul) { case 3: - sprintf(nbuf, "f2"); + g_string_append(buf, "f2"); break; case 2: - sprintf(nbuf, "f4"); + g_string_append(buf, "f4"); break; case 1: - sprintf(nbuf, "f8"); - break; + g_string_append(buf, "f8"); + break; } - append(buf, nbuf, buflen); } else { - sprintf(nbuf, "%d", 1 << lmul); - append(buf, nbuf, buflen); + g_string_append_printf(buf, "%d", 1 << lmul); } - append(buf, ",", buflen); - append(buf, vta, buflen); - append(buf, ",", buflen); - append(buf, vma, buflen); + g_string_append_c(buf, ','); + g_string_append(buf, vta); + g_string_append_c(buf, ','); + g_string_append(buf, vma); break; } + case 'x': { + switch (dec->rlist) { + case 4: + g_string_append(buf, "{ra}"); + break; + case 5: + g_string_append(buf, "{ra, s0}"); + break; + case 15: + g_string_append(buf, "{ra, s0-s11}"); + break; + default: + g_string_append_printf(buf, "{ra, s0-s%d}", dec->rlist - 5); + break; + } + break; + } + case 'h': + g_string_append(buf, rv_fli_name_const[dec->imm]); + break; default: break; } fmt++; } + + return buf; } /* lift instruction to pseudo-instruction */ 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; @@ -4570,6 +5346,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) @@ -4584,6 +5361,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) @@ -4598,6 +5376,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) @@ -4627,17 +5406,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) +static GString *disasm_inst(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); + 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, 24, &dec); + return format_inst(24, &dec); } #define INST_FMT_2 "%04" PRIx64 " " @@ -4648,7 +5465,6 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) static int print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) { - char buf[128] = { 0 }; bfd_byte packet[2]; rv_inst inst = 0; size_t len = 2; @@ -4672,23 +5488,26 @@ 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); - (*info->fprintf_func)(info->stream, "%s", buf); + g_autoptr(GString) str = + disasm_inst(isa, memaddr, inst, (RISCVCPUConfig *)info->target_info); + (*info->fprintf_func)(info->stream, "%s", str->str); return len; } diff --git a/disas/riscv.h b/disas/riscv.h new file mode 100644 index 0000000000..d211700cb2 --- /dev/null +++ b/disas/riscv.h @@ -0,0 +1,309 @@ +/* + * 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_lp, + rv_codec_cmop_ss, +} 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_rs2 "O\t2" +#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_imm "O\ti" +#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,i,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..2283a09c08 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -193,8 +193,8 @@ any IP's here, except for the $primary_ip variable. -device piix3-usb-uhci -device usb-tablet -name secondary \ -netdev tap,id=hn0,vhost=off,helper=/usr/lib/qemu/qemu-bridge-helper \ -device rtl8139,id=e0,netdev=hn0 \ - -chardev socket,id=red0,host=$primary_ip,port=9003,reconnect=1 \ - -chardev socket,id=red1,host=$primary_ip,port=9004,reconnect=1 \ + -chardev socket,id=red0,host=$primary_ip,port=9003,reconnect-ms=1000 \ + -chardev socket,id=red1,host=$primary_ip,port=9004,reconnect-ms=1000 \ -object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 \ -object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 \ -object filter-rewriter,id=rew0,netdev=hn0,queue=all \ @@ -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 1c1e7b9e11..d8b0445157 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -40,8 +40,8 @@ Those hosts are officially supported, with various accelerators: * - CPU Architecture - Accelerators * - Arm - - kvm (64 bit only), tcg, xen - * - MIPS (little endian only) + - hvf (64 bit only), kvm (64 bit only), tcg, xen + * - MIPS (64 bit little endian only) - kvm, tcg * - PPC - kvm, tcg @@ -52,7 +52,7 @@ Those hosts are officially supported, with various accelerators: * - 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,7 +67,8 @@ 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 @@ -86,6 +87,50 @@ 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. + +Rust build dependencies + QEMU is generally conservative in adding new Rust dependencies, and all + of them are included in the distributed tarballs. One exception is the + bindgen tool, which is too big to package and distribute. The minimum + supported version of bindgen is 0.60.x. For distributions that do not + include bindgen or have an older version, it is recommended to install + a newer version using ``cargo install bindgen-cli``. + + Developers may want to use Cargo-based tools in the QEMU source tree; + this requires Cargo 1.74.0. Note that Cargo is not required 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 ------- @@ -106,6 +151,8 @@ 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/ diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 0b26c01da0..d8dc29d0a4 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -11,6 +11,19 @@ releases, the feature is liable to be removed. Deprecated features may also generate warnings on the console when QEMU starts up, or if activated via a monitor command, however, this is not a mandatory requirement. +As a special exception to this general timeframe, rather than have an +indefinite lifetime, versioned machine types are only intended to be +supported for a period of 6 years, equivalent to 18 QEMU releases. All +versioned machine types will be automatically marked deprecated after an +initial 3 years (9 QEMU releases) has passed, and will then be deleted after +a further 3 year period has passed. It is recommended that a deprecated +machine type is only used for incoming migrations and restore of saved state, +for pre-existing VM deployments. They should be scheduled for updating to a +newer machine type during an appropriate service window. Newly deployed VMs +should exclusively use a non-deprecated machine type, with use of the most +recent version highly recommended. Non-versioned machine types follow the +general feature deprecation policy. + Prior to the 2.10.0 release there was no official policy on how long features would be deprecated prior to their removal, nor any documented list of which features were deprecated. Thus @@ -23,28 +36,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,45 +49,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. - -``-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) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -108,12 +60,35 @@ 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. + +``-runas`` (since 9.1) +---------------------- + +Use ``-run-with user=..`` instead. + + +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 ------------------------------------ @@ -172,6 +147,14 @@ options are removed in favor of using explicit ``blockdev-create`` and ``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for details. +``query-migrationthreads`` (since 9.2) +'''''''''''''''''''''''''''''''''''''' + +To be removed with no replacement, as it reports only a limited set of +threads (for example, it only reports source side of multifd threads, +without reporting any destination threads, or non-multifd source threads). +For debugging purpose, please use ``-name $VM,debug-threads=on`` instead. + Incorrectly typed ``device_add`` arguments (since 6.2) '''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -186,49 +169,73 @@ 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. - Host Architectures ------------------ -BE MIPS (since 7.2) -''''''''''''''''''' +Big endian MIPS since 7.2; 32-bit little endian MIPS since 9.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. +completes. -QEMU API (QAPI) events ----------------------- +Likewise, the little endian variant of 32 bit MIPS is not supported by +Debian 13 ("Trixie") and newer. -``MEM_UNPLUG_ERROR`` (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''' +64 bit little endian MIPS is still a supported host architecture. -Use the more generic event ``DEVICE_UNPLUG_GUEST_ERROR`` instead. +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. + +TCG Plugin support not enabled by default on 32-bit hosts (since 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +While it is still possible to enable TCG plugin support for 32-bit +hosts there are a number of potential pitfalls when instrumenting +64-bit guests. The plugin APIs typically pass most addresses as +uint64_t but practices like encoding that address in a host pointer +for passing as user-data will lose data. As most software analysis +benefits from having plenty of host memory it seems reasonable to +encourage users to use 64 bit builds of QEMU for analysis work +whatever targets they are instrumenting. + +TCG Plugin support not enabled by default with TCI (since 9.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +While the TCG interpreter can interpret the TCG ops used by plugins it +is going to be so much slower it wouldn't make sense for any serious +instrumentation. Due to implementation differences there will also be +anomalies in things like memory instrumentation. + +System emulator CPUs +-------------------- + +``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. + +``Sun-UltraSparc-IIIi+`` and ``Sun-UltraSparc-IV+`` CPU names (since 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The character "+" in device (and thus also CPU) names is not allowed +in the QEMU object model anymore. ``Sun-UltraSparc-IIIi+`` and +``Sun-UltraSparc-IV+`` are currently still supported via a workaround, +but for consistency these will get removed in a future release, too. +Use ``Sun-UltraSparc-IIIi-plus`` and ``Sun-UltraSparc-IV-plus`` instead. System emulator machines ------------------------ @@ -241,13 +248,34 @@ 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.4`` up to ``pc-i440fx-2.12`` (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' 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. +PPC 405 ``ref405ep`` machine (since 9.1) +'''''''''''''''''''''''''''''''''''''''' + +The ``ref405ep`` machine and PPC 405 CPU have no known users, firmware +images are not available, OpenWRT dropped support in 2019, U-Boot in +2017, Linux also is dropping support in 2024. It is time to let go of +this ancient hardware and focus on newer CPUs and platforms. + +Arm ``tacoma-bmc`` machine (since 9.1) +'''''''''''''''''''''''''''''''''''''''' + +The ``tacoma-bmc`` machine was a board including an AST2600 SoC based +BMC and a witherspoon like OpenPOWER system. It was used for bring up +of the AST2600 SoC in labs. It can be easily replaced by the +``rainier-bmc`` machine which is a real product. + +Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (since 9.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Both ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` were added for little endian +CPUs. Big endian support is not tested. Backend options --------------- @@ -273,25 +301,6 @@ Device options Emulated device options ''''''''''''''''''''''' -``-device virtio-blk,scsi=on|off`` (since 5.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 -and later do not support it because the virtio-scsi device was introduced for -full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. - -Note this also applies to ``-device virtio-blk-pci,scsi=on|off``, which is an -alias. - -``-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) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -307,6 +316,43 @@ 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. + + +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 sd-card,spec_version=1`` (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +SD physical layer specification v2.00 supersedes the v1.10 one. +v2.00 is the default since QEMU 3.0.0. Block device options '''''''''''''''''''' @@ -333,6 +379,67 @@ The above, converted to the current supported format:: json:{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"} +``iscsi,password=xxx`` (since 8.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +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. + +``gluster`` backend (since 9.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +According to https://marc.info/?l=fedora-devel-list&m=171934833215726 +the GlusterFS development effectively ended. Unless the development +gains momentum again, the QEMU project will remove the gluster backend +in a future release. + + +Character device options +'''''''''''''''''''''''' + +Backend ``memory`` (since 9.0) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``memory`` is a deprecated synonym for ``ringbuf``. + +``reconnect`` (since 9.2) +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``reconnect`` option only allows specifiying second granularity timeouts, +which is not enough for all types of use cases, use ``reconnect-ms`` instead. + + +Net device options +'''''''''''''''''' + +Stream ``reconnect`` (since 9.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``reconnect`` option only allows specifiying second granularity timeouts, +which is not enough for all types of use cases, use ``reconnect-ms`` instead. + +CPU device properties +''''''''''''''''''''' + +``pcommit`` on x86 (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The PCOMMIT instruction was never included in any physical processor. +It was implemented as a no-op instruction in TCG up to QEMU 9.0, but +only with ``-cpu max`` (which does not guarantee migration compatibility +across versions). + +``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. + + Backwards compatibility ----------------------- @@ -362,38 +469,33 @@ 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. -Tools ------ +RISC-V "virt" board "riscv,delegate" DT property (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' -virtiofsd -''''''''' +The "riscv,delegate" DT property was added in QEMU 7.0 as part of +the AIA APLIC support. The property changed name during the review +process in Linux and the correct name ended up being +"riscv,delegation". Changing the DT property name will break all +available firmwares that are using the current (wrong) name. The +property is kept as is in 9.1, together with "riscv,delegation", to +give more time for firmware developers to change their code. -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 +--------- +``fd:`` URI when used for file migration (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''' -QEMU guest agent ----------------- +The ``fd:`` URI can currently provide a file descriptor that +references either a socket or a plain file. These are two different +types of migration. In order to reduce ambiguity, the ``fd:`` URI +usage of providing a file descriptor to a plain file has been +deprecated in favor of explicitly using the ``file:`` URI with the +file descriptor being passed as an ``fdset``. Refer to the ``add-fd`` +command documentation for details on the ``fdset`` usage. -``--blacklist`` command line option (since 7.2) -''''''''''''''''''''''''''''''''''''''''''''''' +``zero-blocks`` capability (since 9.2) +'''''''''''''''''''''''''''''''''''''' -``--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. - -``blacklist`` config file option (since 7.2) -'''''''''''''''''''''''''''''''''''''''''''' - -The ``blacklist`` config file option has been renamed to ``block-rpcs`` -(to be in sync with the renaming of the corresponding command line -option). +The ``zero-blocks`` capability was part of the block migration which +doesn't exist anymore since it was removed in QEMU v9.1. diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst new file mode 100644 index 0000000000..3028d5fff7 --- /dev/null +++ b/docs/about/emulation.rst @@ -0,0 +1,818 @@ +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 + * - 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 + * - 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 + +.. _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) + * - RISC-V + - System and User-mode + - https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc + * - Xtensa + - System + - Tensilica ISS SIMCALL + +TCG Plugins +----------- + +QEMU TCG plugins provide a way for users to run experiments taking +advantage of the total system control emulation can have over a guest. +It provides a mechanism for plugins to subscribe to events during +translation and execution and optionally callback into the plugin +during these events. TCG plugins are unable to change the system state +only monitor it passively. However they can do this down to an +individual instruction granularity including potentially subscribing +to all load and store operations. + +See the developer section of the manual for details about +:ref:`writing plugins`. + +Usage +~~~~~ + +Any QEMU binary with TCG support has plugins enabled by default. +Earlier releases needed to be explicitly enabled with:: + + configure --enable-plugins + +Once built a program can be run with multiple plugins loaded each with +their own arguments:: + + $QEMU $OTHER_QEMU_ARGS \ + -plugin contrib/plugins/libhowvec.so,inline=on,count=hint \ + -plugin contrib/plugins/libhotblocks.so + +Arguments are plugin specific and can be used to modify their +behaviour. In this case the howvec plugin is being asked to use inline +ops to count and break down the hint instructions by type. + +Linux user-mode emulation also evaluates the environment variable +``QEMU_PLUGIN``:: + + QEMU_PLUGIN="file=contrib/plugins/libhowvec.so,inline=on,count=hint" $QEMU + +QEMU plugins avoid to write directly to stdin/stderr, and use the log provided +by the API (see function ``qemu_plugin_outs``). +To show output, you may use this additional parameter:: + + $QEMU $OTHER_QEMU_ARGS \ + -d plugin \ + -plugin contrib/plugins/libhowvec.so,inline=on,count=hint + +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. There are also some +basic plugins that are used to test and exercise the API during the +``make check-tcg`` target in ``tests/tcg/plugins`` that are never the +less useful for basic analysis. + +Empty +..... + +``tests/tcg/plugins/empty.c`` + +Purely a test plugin for measuring the overhead of the plugins system +itself. Does no instrumentation. + +Basic Blocks +............ + +``tests/tcg/plugins/bb.c`` + +A very basic plugin which will measure execution in coarse 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: + +.. list-table:: Basic Block plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter. + * - idle=true|false + - Dump the current execution stats whenever the guest vCPU idles + +Basic Block Vectors +................... + +``contrib/plugins/bbv.c`` + +The bbv plugin allows you to generate basic block vectors for use with the +`SimPoint `__ analysis tool. + +.. list-table:: Basic block vectors arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - interval=N + - The interval to generate a basic block vector specified by the number of + instructions (Default: N = 100000000) + * - outfile=PATH + - The path to output files. + It will be suffixed with ``.N.bb`` where ``N`` is a vCPU index. + +Example:: + + $ qemu-aarch64 \ + -plugin contrib/plugins/libbbv.so,interval=100,outfile=sha1 \ + tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + $ du sha1.0.bb + 23128 sha1.0.bb + +Instruction +........... + +``tests/tcg/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: + +.. list-table:: Instruction plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter. + * - sizes=true|false + - Give a summary of the instruction sizes for the execution + * - match= + - Only instrument instructions matching the string prefix + +The ``match`` option 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. + +Memory +...... + +``tests/tcg/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: + +.. list-table:: Memory plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - inline=true|false + - Use faster inline addition of a single counter + * - callback=true|false + - Use callbacks on each memory instrumentation. + * - hwaddr=true|false + - Count IO accesses (only for system emulation) + +System Calls +............ + +``tests/tcg/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 + +Behaviour can be tweaked with the following arguments: + +.. list-table:: Syscall plugin arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - print=true|false + - Print the number of times each syscall is called + * - log_writes=true|false + - Log the buffer of each write syscall in hexdump format + +Test inline operations +...................... + +``tests/plugins/inline.c`` + +This plugin is used for testing all inline operations, conditional callbacks and +scoreboard. It prints a per-cpu summary of all events. + + +Hot Blocks +.......... + +``contrib/plugins/hotblocks.c`` + +The hotblocks plugin allows you to examine the where hot paths of +execution are in your program. Once the program has finished you will +get a sorted list of blocks reporting the starting PC, translation +count, number of instructions and execution count. This will work best +with linux-user execution as system emulation tends to generate +re-translations as blocks from different programs get swapped in and +out of system memory. + +Example:: + + $ qemu-aarch64 \ + -plugin contrib/plugins/libhotblocks.so -d plugin \ + ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + collected 903 entries in the hash table + pc, tcount, icount, ecount + 0x0000000041ed10, 1, 5, 66087 + 0x000000004002b0, 1, 4, 66087 + ... + + +Hot Pages +......... + +``contrib/plugins/hotpages.c`` + +Similar to hotblocks but this time tracks memory accesses:: + + $ qemu-aarch64 \ + -plugin contrib/plugins/libhotpages.so -d plugin \ + ./tests/tcg/aarch64-linux-user/sha1 + SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 + Addr, RCPUs, Reads, WCPUs, Writes + 0x000055007fe000, 0x0001, 31747952, 0x0001, 8835161 + 0x000055007ff000, 0x0001, 29001054, 0x0001, 8780625 + 0x00005500800000, 0x0001, 687465, 0x0001, 335857 + 0x0000000048b000, 0x0001, 130594, 0x0001, 355 + 0x0000000048a000, 0x0001, 1826, 0x0001, 11 + +The hotpages plugin can be configured using the following arguments: + +.. list-table:: Hot pages arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - sortby=reads|writes|address + - Log the data sorted by either the number of reads, the number of writes, or + memory address. (Default: entries are sorted by the sum of reads and writes) + * - io=on + - Track IO addresses. Only relevant to full system emulation. (Default: off) + * - pagesize=N + - The page size used. (Default: N = 4096) + +Instruction Distribution +........................ + +``contrib/plugins/howvec.c`` + +This is an instruction classifier so can be used to count different +types of instructions. It has a number of options to refine which get +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:: + + $ qemu-system-aarch64 $(QEMU_ARGS) \ + -append "root=/dev/sda2 systemd.unit=benchmark.service" \ + -smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin + +which will lead to a sorted list after the class breakdown:: + + Instruction Classes: + Class: UDEF not counted + Class: SVE (68 hits) + Class: PCrel addr (47789483 hits) + Class: Add/Sub (imm) (192817388 hits) + Class: Logical (imm) (93852565 hits) + Class: Move Wide (imm) (76398116 hits) + Class: Bitfield (44706084 hits) + Class: Extract (5499257 hits) + Class: Cond Branch (imm) (147202932 hits) + Class: Exception Gen (193581 hits) + Class: NOP not counted + Class: Hints (6652291 hits) + Class: Barriers (8001661 hits) + Class: PSTATE (1801695 hits) + Class: System Insn (6385349 hits) + Class: System Reg counted individually + Class: Branch (reg) (69497127 hits) + Class: Branch (imm) (84393665 hits) + Class: Cmp & Branch (110929659 hits) + Class: Tst & Branch (44681442 hits) + Class: AdvSimd ldstmult (736 hits) + Class: ldst excl (9098783 hits) + Class: Load Reg (lit) (87189424 hits) + Class: ldst noalloc pair (3264433 hits) + Class: ldst pair (412526434 hits) + Class: ldst reg (imm) (314734576 hits) + Class: Loads & Stores (2117774 hits) + Class: Data Proc Reg (223519077 hits) + Class: Scalar FP (31657954 hits) + Individual Instructions: + Instr: mrs x0, sp_el0 (2682661 hits) (op=0xd5384100/ System Reg) + Instr: mrs x1, tpidr_el2 (1789339 hits) (op=0xd53cd041/ System Reg) + Instr: mrs x2, tpidr_el2 (1513494 hits) (op=0xd53cd042/ System Reg) + Instr: mrs x0, tpidr_el2 (1490823 hits) (op=0xd53cd040/ System Reg) + Instr: mrs x1, sp_el0 (933793 hits) (op=0xd5384101/ System Reg) + Instr: mrs x2, sp_el0 (699516 hits) (op=0xd5384102/ System Reg) + Instr: mrs x4, tpidr_el2 (528437 hits) (op=0xd53cd044/ System Reg) + Instr: mrs x30, ttbr1_el1 (480776 hits) (op=0xd538203e/ System Reg) + Instr: msr ttbr1_el1, x30 (480713 hits) (op=0xd518203e/ System Reg) + Instr: msr vbar_el1, x30 (480671 hits) (op=0xd518c01e/ System Reg) + ... + +To find the argument shorthand for the class you need to examine the +source code of the plugin at the moment, specifically the ``*opt`` +argument in the InsnClassExecCount tables. + +Lockstep Execution +.................. + +``contrib/plugins/lockstep.c`` + +This is a debugging tool for developers who want to find out when and +where execution diverges after a subtle change to TCG code generation. +It is not an exact science and results are likely to be mixed once +asynchronous events are introduced. While the use of -icount can +introduce determinism to the execution flow it doesn't always follow +the translation sequence will be exactly the same. Typically this is +caused by a timer firing to service the GUI causing a block to end +early. However in some cases it has proved to be useful in pointing +people at roughly where execution diverges. The only argument you need +for the plugin is a path for the socket the two instances will +communicate over:: + + + $ 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 + +which will eventually report:: + + qemu-system-sparc: warning: nic lance.0 has no peer + @ 0x000000ffd06678 vs 0x000000ffd001e0 (2/1 since last) + @ 0x000000ffd07d9c vs 0x000000ffd06678 (3/1 since last) + Δ insn_count @ 0x000000ffd07d9c (809900609) vs 0x000000ffd06678 (809900612) + previously @ 0x000000ffd06678/10 (809900609 insns) + previously @ 0x000000ffd001e0/4 (809900599 insns) + previously @ 0x000000ffd080ac/2 (809900595 insns) + previously @ 0x000000ffd08098/5 (809900593 insns) + previously @ 0x000000ffd080c0/1 (809900588 insns) + + +Hardware Profile +................ + +``contrib/plugins/hwprofile.c`` + +The hwprofile tool can only be used with system emulation and allows +the user to see what hardware is accessed how often. It has a number of options: + +.. list-table:: Hardware Profile arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - track=[read|write] + - By default the plugin tracks both reads and writes. You can use + this option to limit the tracking to just one class of accesses. + * - source + - Will include a detailed break down of what the guest PC that made the + access was. Not compatible with the pattern option. Example output:: + + cirrus-low-memory @ 0xfffffd00000a0000 + pc:fffffc0000005cdc, 1, 256 + pc:fffffc0000005ce8, 1, 256 + pc:fffffc0000005cec, 1, 256 + + * - pattern + - Instead break down the accesses based on the offset into the HW + region. This can be useful for seeing the most used registers of + a device. Example output:: + + pci0-conf @ 0xfffffd01fe000000 + off:00000004, 1, 1 + off:00000010, 1, 3 + off:00000014, 1, 3 + off:00000018, 1, 2 + off:0000001c, 1, 2 + off:00000020, 1, 2 + ... + + +Execution Log +............. + +``contrib/plugins/execlog.c`` + +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 needs default argument:: + + $ qemu-system-arm $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libexeclog.so -d plugin + +which will output an execution trace following this structure:: + + # vCPU, vAddr, opcode, disassembly[, load/store, memory addr, device]... + 0, 0xa12, 0xf8012400, "movs r4, #0" + 0, 0xa14, 0xf87f42b4, "cmp r4, r6" + 0, 0xa16, 0xd206, "bhs #0xa26" + 0, 0xa18, 0xfff94803, "ldr r0, [pc, #0xc]", load, 0x00010a28, RAM + 0, 0xa1a, 0xf989f000, "bl #0xd30" + 0, 0xd30, 0xfff9b510, "push {r4, lr}", store, 0x20003ee0, RAM, store, 0x20003ee4, RAM + 0, 0xd32, 0xf9893014, "adds r0, #0x14" + 0, 0xd34, 0xf9c8f000, "bl #0x10c8" + 0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM + +Please note that you need to configure QEMU with Capstone support to get disassembly. + +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 + +Cache Modelling +............... + +``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 \ + -d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs + +will report the following:: + + core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate + 0 996695 508 0.0510% 2642799 18617 0.7044% + + address, data misses, instruction + 0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx) + 0x41f395 (_IO_default_xsputn), 49, movb %dl, (%rdi, %rax) + 0x42584d (ptmalloc_init.part.0), 33, movaps %xmm0, (%rax) + 0x454d48 (__tunables_init), 20, cmpb $0, (%r8) + ... + + address, fetch misses, instruction + 0x4160a0 (__vfprintf_internal), 744, movl $1, %ebx + 0x41f0a0 (_IO_setb), 744, endbr64 + 0x415882 (__vfprintf_internal), 744, movq %r12, %rdi + 0x4268a0 (__malloc), 696, andq $0xfffffffffffffff0, %rax + ... + +The plugin has a number of arguments, all of them are optional: + +.. list-table:: Cache modelling arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - limit=N + - Print top N icache and dcache thrashing instructions along with + their address, number of misses, and its disassembly. (default: 32) + * - icachesize=N + iblksize=B + iassoc=A + - Instruction cache configuration arguments. They specify the + cache size, block size, and associativity of the instruction + cache, respectively. (default: N = 16384, B = 64, A = 8) + * - dcachesize=N + - Data cache size (default: 16834) + * - dblksize=B + - Data cache block size (default: 64) + * - dassoc=A + - Data cache associativity (default: 8) + * - evict=POLICY + - Sets the eviction policy to POLICY. Available policies are: + ``lru``, ``fifo``, and ``rand``. The plugin will use + the specified policy for both instruction and data caches. + (default: POLICY = ``lru``) + * - cores=N + - Sets the number of cores for which we maintain separate icache + and dcache. (default: for linux-user, N = 1, for full system + emulation: N = cores available to guest) + * - l2=on + - Simulates a unified L2 cache (stores blocks for both + instructions and data) using the default L2 configuration (cache + size = 2MB, associativity = 16-way, block size = 64B). + * - l2cachesize=N + - L2 cache size (default: 2097152 (2MB)), implies ``l2=on`` + * - l2blksize=B + - L2 cache block size (default: 64), implies ``l2=on`` + * - l2assoc=A + - L2 cache associativity (default: 16), implies ``l2=on`` + +Stop on Trigger +............... + +``contrib/plugins/stoptrigger.c`` + +The stoptrigger plugin allows to setup triggers to stop emulation. +It can be used for research purposes to launch some code and precisely stop it +and understand where its execution flow went. + +Two types of triggers can be configured: a count of instructions to stop at, +or an address to stop at. Multiple triggers can be set at once. + +By default, QEMU will exit with return code 0. A custom return code can be +configured for each trigger using ``:CODE`` syntax. + +For example, to stop at the 20-th instruction with return code 41, at address +0xd4 with return code 0 or at address 0xd8 with return code 42:: + + $ qemu-system-aarch64 $(QEMU_ARGS) \ + -plugin ./contrib/plugins/libstoptrigger.so,icount=20:41,addr=0xd4,addr=0xd8:42 -d plugin + +The plugin will log the reason of exit, for example:: + + 0xd4 reached, exiting + +Limit instructions per second +............................. + +This plugin can limit the number of Instructions Per Second that are executed:: + + # get number of instructions + $ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin /bin/true |& grep total | sed -e 's/.*: //') + # limit speed to execute in 10 seconds + $ time ./build/qemu-x86_64 -plugin ./build/contrib/plugins/libips.so,ips=$(($num_insn/10)) /bin/true + real 10.000s + + +.. list-table:: IPS arguments + :widths: 20 80 + :header-rows: 1 + + * - Option + - Description + * - ips=N + - Maximum number of instructions per cpu that can be executed in one second. + The plugin will sleep when the given number of instructions is reached. + +Other emulation features +------------------------ + +When running system emulation you can also enable deterministic +execution which allows for repeatable record/replay debugging. See +:ref:`Record/Replay` for more details. 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 63df9848fd..ee6455aeee 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -355,13 +355,13 @@ The ``-writeconfig`` option was not able to serialize the entire contents of the QEMU command line. It is thus considered a failed experiment and removed without a replacement. -``loaded`` property of ``secret`` and ``secret_keyring`` objects (removed in 7.1) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +``loaded`` property of secret and TLS credential objects (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' The ``loaded=on`` option in the command line or QMP ``object-add`` either had no effect (if ``loaded`` was the last option) or caused options to be effectively ignored as if they were not given. The property is therefore -useless and should simply be removed. +useless and has been removed. ``opened`` property of ``rng-*`` objects (removed in 7.1) ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -408,6 +408,163 @@ pcspk-audiodev=``. 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. + +``-global migration.decompress-error-check`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Removed along with the ``compression`` migration capability. + +``-device virtio-blk,scsi=on|off`` (since 9.1) +'''''''''''''''''''''''''''''''''''''''''''''' + +The virtio-blk SCSI passthrough feature is a legacy VIRTIO feature. VIRTIO 1.0 +and later do not support it because the virtio-scsi device was introduced for +full SCSI support. Use virtio-scsi instead when SCSI passthrough is required. + +``-fsdev proxy`` and ``-virtfs proxy`` (since 9.2) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The 9p ``proxy`` filesystem backend driver was originally developed to +enhance security by dispatching low level filesystem operations from 9p +server (QEMU process) over to a separate process (the virtfs-proxy-helper +binary). However the proxy backend was much slower than the local backend, +didn't see any development in years, and showed to be less secure, +especially due to the fact that its helper daemon must be run as root. + +Use ``local``, possibly mapping permissions et al by using its 'mapped' +security model option, or switch to ``virtiofs``. The virtiofs daemon +``virtiofsd`` uses vhost to eliminate the high latency costs of the 9p +``proxy`` backend. + +``-portrait`` and ``-rotate`` (since 9.2) +''''''''''''''''''''''''''''''''''''''''' + +The ``-portrait`` and ``-rotate`` options were documented as only +working with the PXA LCD device, and all the machine types using +that display device were removed in 9.2, so these options also +have been dropped. + +These options were intended to simulate a mobile device being +rotated by the user, and had three effects: + +* the display output was rotated by 90, 180 or 270 degrees +* the mouse/trackpad input was rotated the opposite way +* the machine model would signal to the guest about its + orientation + +Of these three things, the input-rotation was coded without being +restricted to boards which supported the full set of device-rotation +handling, so in theory the options were usable on other machine models +to produce an odd effect (rotating input but not display output). But +this was never intended or documented behaviour, so we have dropped +the options along with the machine models they were intended for. + +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 ------------------------------------ @@ -494,6 +651,86 @@ 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``. + +``query-migrate`` return value member ``skipped`` (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``skipped`` of the ``MigrationStats`` struct hasn't been used +for more than 10 years. Removed with no replacement. + +``migrate`` command option ``inc`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + +``migrate`` command option ``blk`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + +``migrate-set-capabilities`` ``block`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Block migration has been removed. For a replacement, see "QMP +invocation for live storage migration with ``blockdev-mirror`` + NBD" +in docs/interop/live-block-operations.rst. + +``migrate-set-parameter`` ``compress-level`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-zlib-level`` or ``multifd-zstd-level`` instead. + +``migrate-set-parameter`` ``compress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate-set-parameter`` ``compress-wait-thread`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Removed with no replacement. + +``migrate-set-parameter`` ``decompress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate-set-capability`` ``compress`` option (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-compression`` instead. + +QEMU Machine Protocol (QMP) events +---------------------------------- + +``MEM_UNPLUG_ERROR`` (removed in 9.1) +''''''''''''''''''''''''''''''''''''' + +MEM_UNPLUG_ERROR has been replaced by the more generic ``DEVICE_UNPLUG_GUEST_ERROR`` event. + +``vcpu`` trace events (removed in 9.1) +'''''''''''''''''''''''''''''''''''''' + +The ability to instrument QEMU helper functions with vCPU-aware trace +points was removed in 7.0. + + Human Monitor Protocol (HMP) commands ------------------------------------- @@ -548,6 +785,73 @@ 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. + +``migrate`` command ``-i`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + +``migrate`` command ``-b`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +Use blockdev-mirror with NBD instead. See "QMP invocation for live +storage migration with ``blockdev-mirror`` + NBD" in +docs/interop/live-block-operations.rst for a detailed explanation. + +``migrate_set_capability`` ``block`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Block migration has been removed. For a replacement, see "QMP +invocation for live storage migration with ``blockdev-mirror`` + NBD" +in docs/interop/live-block-operations.rst. + +``migrate_set_parameter`` ``compress-level`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-zlib-level`` or ``multifd-zstd-level`` instead. + +``migrate_set_parameter`` ``compress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate_set_parameter`` ``compress-wait-thread`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Removed with no replacement. + +``migrate_set_parameter`` ``decompress-threads`` option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-channels`` instead. + +``migrate_set_capability`` ``compress`` option (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Use ``multifd-compression`` instead. + +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 ------------------- @@ -565,9 +869,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) ''''''''''''''''''''''''''''''''''''''''' @@ -584,6 +887,14 @@ The RISC-V no MMU cpus have been removed. The two CPUs: ``rv32imacu-nommu`` and ``rv64imacu-nommu`` can no longer be used. Instead the MMU status can be specified via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. +RISC-V 'any' CPU type ``-cpu any`` (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +The 'any' CPU type was introduced back in 2018 and was around since the +initial RISC-V QEMU port. Its usage was 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. + ``compat`` property of server class POWER CPUs (removed in 6.0) ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -617,6 +928,41 @@ 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. +Nios II CPU (removed in 9.1) +'''''''''''''''''''''''''''' + +QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II +processor IP (see `Intel discontinuance notification`_). + +CRIS CPU architecture (removed in 9.2) +'''''''''''''''''''''''''''''''''''''' + +The CRIS architecture was pulled from Linux in 4.17 and the compiler +was no longer packaged in any distro making it harder to run the +``check-tcg`` tests. + +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 ------------------------ @@ -654,8 +1000,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-2.3`` (removed in 4.0 up to 9.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 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. @@ -679,6 +1025,30 @@ 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. +Nios II ``10m50-ghrd`` and ``nios2-generic-nommu`` machines (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The Nios II architecture was orphan. + +``shix`` (removed in 9.2) +''''''''''''''''''''''''' + +The machine was unmaintained. + +Arm machines ``akita``, ``borzoi``, ``cheetah``, ``connex``, ``mainstone``, ``n800``, ``n810``, ``spitz``, ``terrier``, ``tosa``, ``verdex``, ``z2`` (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +QEMU included models of some machine types where the QEMU code that +emulates their SoCs was very old and unmaintained. This code was +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. + linux-user mode CPUs -------------------- @@ -698,6 +1068,11 @@ The ``ppc64abi32`` architecture has a number of issues which regularly tripped up the CI testing and was suspected to be quite broken. For that reason the maintainers strongly suspected no one actually used it. +``nios2`` CPU (removed in 9.1) +'''''''''''''''''''''''''''''' + +QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II +processor IP (see `Intel discontinuance notification`_). TCG introspection features -------------------------- @@ -738,6 +1113,20 @@ 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``. + +``pvrdma`` and the RDMA subsystem (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''' + +The 'pvrdma' device and the whole RDMA subsystem have been removed. + Related binaries ---------------- @@ -821,3 +1210,35 @@ 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. + +QEMU guest agent +---------------- + +``--blacklist`` command line option (removed in 9.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''' + +``--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. + +``blacklist`` config file option (removed in 9.1) +''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``blacklist`` config file option has been renamed to ``block-rpcs`` +(to be in sync with the renaming of the corresponding command line +option). + +.. _Intel discontinuance notification: https://www.intel.com/content/www/us/en/content-details/781327/intel-is-discontinuing-ip-ordering-codes-listed-in-pdn2312-for-nios-ii-ip.html 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 e33cf3d381..c11a6ead8a 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. @@ -63,10 +53,9 @@ sys.path.insert(0, os.path.join(qemu_docdir, "../scripts")) # If your documentation needs a minimal Sphinx version, state it here. # -# Sphinx 1.5 and earlier can't build our docs because they are too -# picky about the syntax of the argument to the option:: directive -# (see Sphinx bugs #646, #3366). -needs_sphinx = '1.6' +# 3.4.3 is the oldest version of Sphinx that ships on a platform we +# pledge build support for. +needs_sphinx = '3.4.3' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -98,7 +87,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 @@ -174,11 +163,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") @@ -287,29 +275,9 @@ man_pages = [ ('tools/qemu-trace-stap', 'qemu-trace-stap', 'QEMU SystemTap trace tool', [], 1), - ('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 deleted file mode 100644 index 4a94c7d83d..0000000000 --- a/docs/devel/acpi-bits.rst +++ /dev/null @@ -1,141 +0,0 @@ -============================================================================= -ACPI/SMBIOS avocado tests using biosbits -============================================================================= - -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. -There are several advantages to directly testing the bios in a real physical -machine or VM as opposed to indirectly discovering bios issues through the -operating system. For one thing, the OSes tend to hide bios problems from the -end user. The other is that we have more control of what we wanted to test -and how by directly using acpica interpreter on top of the bios on a running -system. 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: https://gitlab.com/qemu-project/biosbits-bits -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 repo. - -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. - - 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) - $ ./tests/venv/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: - :: - - $ ./tests/venv/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: - :: - - $ ./tests/venv/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) should go under separate commits so that the original - test script and the changes we have made are separated and clear. - - 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 - diff --git a/docs/devel/atomics.rst b/docs/devel/atomics.rst index 52baa0736d..95c7b77c01 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\ [#alpha]_. 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. +.. [#alpha] 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 @@ -307,7 +295,7 @@ Acquire/release pairing and the *synchronizes-with* relation ------------------------------------------------------------ Atomic operations other than ``qatomic_set()`` and ``qatomic_read()`` have -either *acquire* or *release* semantics [#rmw]_. This has two effects: +either *acquire* or *release* semantics\ [#rmw]_. This has two effects: .. [#rmw] Read-modify-write operations can have both---acquire applies to the read part, and release to the write. @@ -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/blkdebug.txt b/docs/devel/blkdebug.txt deleted file mode 100644 index 0b0c128d35..0000000000 --- a/docs/devel/blkdebug.txt +++ /dev/null @@ -1,162 +0,0 @@ -Block I/O error injection using blkdebug ----------------------------------------- -Copyright (C) 2014-2015 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. - -The blkdebug block driver is a rule-based error injection engine. It can be -used to exercise error code paths in block drivers including ENOSPC (out of -space) and EIO. - -This document gives an overview of the features available in blkdebug. - -Background ----------- -Block drivers have many error code paths that handle I/O errors. Image formats -are especially complex since metadata I/O errors during cluster allocation or -while updating tables happen halfway through request processing and require -discipline to keep image files consistent. - -Error injection allows test cases to trigger I/O errors at specific points. -This way, all error paths can be tested to make sure they are correct. - -Rules ------ -The blkdebug block driver takes a list of "rules" that tell the error injection -engine when to fail an I/O request. - -Each I/O request is evaluated against the rules. If a rule matches the request -then its "action" is executed. - -Rules can be placed in a configuration file; the configuration file -follows the same .ini-like format used by QEMU's -readconfig option, and -each section of the file represents a rule. - -The following configuration file defines a single rule: - - $ cat blkdebug.conf - [inject-error] - event = "read_aio" - errno = "28" - -This rule fails all aio read requests with ENOSPC (28). Note that the errno -value depends on the host. On Linux, see -/usr/include/asm-generic/errno-base.h for errno values. - -Invoke QEMU as follows: - - $ qemu-system-x86_64 - -drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \ - -device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0 - -Rules support the following attributes: - - event - which type of operation to match (e.g. read_aio, write_aio, - flush_to_os, flush_to_disk). See the "Events" section for - information on events. - - state - (optional) the engine must be in this state number in order for this - rule to match. See the "State transitions" section for information - on states. - - errno - the numeric errno value to return when a request matches this rule. - The errno values depend on the host since the numeric values are not - standardized in the POSIX specification. - - sector - (optional) a sector number that the request must overlap in order to - match this rule - - once - (optional, default "off") only execute this action on the first - matching request - - immediately - (optional, default "off") return a NULL BlockAIOCB - pointer and fail without an errno instead. This - exercises the code path where BlockAIOCB fails and the - caller's BlockCompletionFunc is not invoked. - -Events ------- -Block drivers provide information about the type of I/O request they are about -to make so rules can match specific types of requests. For example, the qcow2 -block driver tells blkdebug when it accesses the L1 table so rules can match -only L1 table accesses and not other metadata or guest data requests. - -The core events are: - - read_aio - guest data read - - write_aio - guest data write - - flush_to_os - write out unwritten block driver state (e.g. cached metadata) - - flush_to_disk - flush the host block device's disk cache - -See qapi/block-core.json:BlkdebugEvent for the full list of events. -You may need to grep block driver source code to understand the -meaning of specific events. - -State transitions ------------------ -There are cases where more power is needed to match a particular I/O request in -a longer sequence of requests. For example: - - write_aio - flush_to_disk - write_aio - -How do we match the 2nd write_aio but not the first? This is where state -transitions come in. - -The error injection engine has an integer called the "state" that always starts -initialized to 1. The state integer is internal to blkdebug and cannot be -observed from outside but rules can interact with it for powerful matching -behavior. - -Rules can be conditional on the current state and they can transition to a new -state. - -When a rule's "state" attribute is non-zero then the current state must equal -the attribute in order for the rule to match. - -For example, to match the 2nd write_aio: - - [set-state] - event = "write_aio" - state = "1" - new_state = "2" - - [inject-error] - event = "write_aio" - state = "2" - errno = "5" - -The first write_aio request matches the set-state rule and transitions from -state 1 to state 2. Once state 2 has been entered, the set-state rule no -longer matches since it requires state 1. But the inject-error rule now -matches the next write_aio request and injects EIO (5). - -State transition rules support the following attributes: - - event - which type of operation to match (e.g. read_aio, write_aio, - flush_to_os, flush_to_disk). See the "Events" section for - information on events. - - state - (optional) the engine must be in this state number in order for this - rule to match - - new_state - transition to this state number - -Suspend and resume ------------------- -Exercising code paths in block drivers may require specific ordering amongst -concurrent requests. The "breakpoint" feature allows requests to be halted on -a blkdebug event and resumed later. This makes it possible to achieve -deterministic ordering when multiple requests are in flight. - -Breakpoints on blkdebug events are associated with a user-defined "tag" string. -This tag serves as an identifier by which the request can be resumed at a later -point. - -See the qemu-io(1) break, resume, remove_break, and wait_break commands for -details. diff --git a/docs/devel/blkverify.txt b/docs/devel/blkverify.txt deleted file mode 100644 index aca826c51c..0000000000 --- a/docs/devel/blkverify.txt +++ /dev/null @@ -1,69 +0,0 @@ -= Block driver correctness testing with blkverify = - -== Introduction == - -This document describes how to use the blkverify protocol to test that a block -driver is operating correctly. - -It is difficult to test and debug block drivers against real guests. Often -processes inside the guest will crash because corrupt sectors were read as part -of the executable. Other times obscure errors are raised by a program inside -the guest. These issues are extremely hard to trace back to bugs in the block -driver. - -Blkverify solves this problem by catching data corruption inside QEMU the first -time bad data is read and reporting the disk sector that is corrupted. - -== How it works == - -The blkverify protocol has two child block devices, the "test" device and the -"raw" device. Read/write operations are mirrored to both devices so their -state should always be in sync. - -The "raw" device is a raw image, a flat file, that has identical starting -contents to the "test" image. The idea is that the "raw" device will handle -read/write operations correctly and not corrupt data. It can be used as a -reference for comparison against the "test" device. - -After a mirrored read operation completes, blkverify will compare the data and -raise an error if it is not identical. This makes it possible to catch the -first instance where corrupt data is read. - -== Example == - -Imagine raw.img has 0xcd repeated throughout its first sector: - - $ ./qemu-io -c 'read -v 0 512' raw.img - 00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ - 00000010: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ - [...] - 000001e0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ - 000001f0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ - read 512/512 bytes at offset 0 - 512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec) - -And test.img is corrupt, its first sector is zeroed when it shouldn't be: - - $ ./qemu-io -c 'read -v 0 512' test.img - 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - [...] - 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ - read 512/512 bytes at offset 0 - 512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec) - -This error is caught by blkverify: - - $ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img - blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0 - -A more realistic scenario is verifying the installation of a guest OS: - - $ ./qemu-img create raw.img 16G - $ ./qemu-img create -f qcow2 test.qcow2 16G - $ ./qemu-system-x86_64 -cdrom debian.iso \ - -drive file=blkverify:raw.img:test.qcow2 - -If the installation is aborted when blkverify detects corruption, use qemu-io -to explore the contents of the disk image at the sector in question. 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 1894721743..d42045a232 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,88 +19,189 @@ 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. -``error_exit $MESSAGE $MORE...`` - Print $MESSAGE to stderr, followed by $MORE... and then exit from the - configure script with non-zero status -``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 +Python virtual environments and the build process +------------------------------------------------- + +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. The only one is currently Meson (outdated in Ubuntu +22.04 and openSUSE Leap). + +In order to include a new or updated wheel, modify and rerun the +``python/scripts/vendor.py`` script. The 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`` @@ -126,7 +211,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 @@ -149,14 +235,10 @@ Subsystem sourcesets: are then turned into static libraries as follows:: libchardev = static_library('chardev', chardev_ss.sources(), - name_suffix: 'fa', build_by_default: false) - chardev = declare_dependency(link_whole: libchardev) - - As of Meson 0.55.1, the special ``.fa`` suffix should be used for everything - that is used with ``link_whole``, to ensure that the link flags are placed - correctly in the command line. + chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false), + dependencies: chardev_ss.dependencies()) Target-independent emulator sourcesets: Various general purpose helper code is compiled only once and @@ -164,26 +246,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, @@ -206,20 +273,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 @@ -266,7 +333,7 @@ into each emulator: ``default-configs/targets/*.mak`` These files mostly define symbols that appear in the ``*-config-target.h`` - file for each emulator [#cfgtarget]_. However, the ``TARGET_ARCH`` + file for each emulator\ [#cfgtarget]_. However, the ``TARGET_ARCH`` and ``TARGET_BASE_ARCH`` will also be used to select the ``hw/`` and ``target/`` subdirectories that are compiled into each target. @@ -281,8 +348,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')) @@ -311,8 +377,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. @@ -359,23 +424,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 ------------------- @@ -387,6 +469,7 @@ Useful make targets Print the value of the variable VAR. Useful for debugging the build system. + Important files for the build system ==================================== @@ -400,8 +483,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 @@ -409,60 +491,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/clocks.rst b/docs/devel/clocks.rst index 675fbeb6ab..3f744f2be1 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. @@ -354,6 +358,12 @@ humans (for instance in debugging), use ``clock_display_freq()``, which returns a prettified string-representation, e.g. "33.3 MHz". The caller must free the string with g_free() after use. +It's also possible to retrieve the clock period from a QTest by +accessing QOM property ``qtest-clock-period`` using a QMP command. +This property is only present when the device is being run under +the ``qtest`` accelerator; it is not available when QEMU is +being run normally. + Calculating expiry deadlines ---------------------------- @@ -502,7 +512,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/crypto.rst b/docs/devel/crypto.rst new file mode 100644 index 0000000000..39b1c910e7 --- /dev/null +++ b/docs/devel/crypto.rst @@ -0,0 +1,10 @@ +.. _crypto-ref: + +==================== +Cryptography in QEMU +==================== + +.. toctree:: + :maxdepth: 2 + + luks-detached-header 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/index-api.rst b/docs/devel/index-api.rst index 60c0d7459d..1c487c152a 100644 --- a/docs/devel/index-api.rst +++ b/docs/devel/index-api.rst @@ -9,6 +9,11 @@ generated from in-code annotations to function prototypes. bitops loads-stores + lockcnt 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 57e8d39d98..0023953be3 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -1,19 +1,14 @@ -QEMU Build and Test System --------------------------- +QEMU Build System +----------------- -Details about how QEMU's build system works and how it is integrated -into our testing infrastructure. You will need to understand some of -the basics if you are adding new files and targets to the build. +Details about how QEMU's build system works. You will need to understand +some of the basics if you are adding new files and targets to the build. .. toctree:: :maxdepth: 3 build-system kconfig - testing - acpi-bits - qtest - ci + docs qapi-code-gen - fuzzing control-flow-integrity diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index e1a93df263..ab9fbc4482 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -8,14 +8,18 @@ Details about QEMU's various subsystems including how to add features to them. qom atomics + rcu 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 + crypto + multiple-iothreads diff --git a/docs/devel/index-process.rst b/docs/devel/index-process.rst index d50dd74c3e..362f97ee30 100644 --- a/docs/devel/index-process.rst +++ b/docs/devel/index-process.rst @@ -1,3 +1,5 @@ +.. _development_process: + QEMU Community Processes ------------------------ 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..a53f1bfda5 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -2,15 +2,36 @@ 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 index-process index-build + testing/index index-api index-internals index-tcg diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 69674d008a..52d4b905f6 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -211,6 +211,8 @@ declares its dependencies in different ways: config SUN4M bool + default y + depends on SPARC && !SPARC64 imply TCX imply CG3 select CS4231 @@ -228,8 +230,16 @@ declares its dependencies in different ways: directives. A device should be listed under ``select`` if the board cannot be started at all without it. It should be listed under ``imply`` if (depending on the QEMU command line) the board may or - may not be started without it. Boards also default to false; they are - enabled by the ``default-configs/*.mak`` for the target they apply to. + may not be started without it. Boards default to true, but also + have a ``depends on`` clause to limit them to the appropriate targets. + For some targets, not all boards may be supported by hardware + virtualization, in which case they also depend on the ``TCG`` symbol, + Other symbols that are commonly used as dependencies for boards + include libraries (such as ``FDT``) or ``TARGET_BIG_ENDIAN`` + (possibly negated). + + Boards are listed for convenience in the ``default-configs/*.mak`` + for the target they apply to. **internal elements** @@ -274,7 +284,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 +292,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 +326,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..9471bac859 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`` ~~~~~~~~~~~~~~~~~~~~ @@ -93,7 +95,7 @@ guest CPU state in case of a guest CPU exception. This is passed to ``cpu_restore_state()``. Therefore the value should either be 0, to indicate that the guest CPU state is already synchronized, or the result of ``GETPC()`` from the top level ``HELPER(foo)`` -function, which is a return address into the generated code [#gpc]_. +function, which is a return address into the generated code\ [#gpc]_. .. [#gpc] Note that ``GETPC()`` should be used with great care: calling it in other functions that are *not* the top level @@ -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/lockcnt.rst b/docs/devel/lockcnt.rst new file mode 100644 index 0000000000..8b43578f6c --- /dev/null +++ b/docs/devel/lockcnt.rst @@ -0,0 +1,278 @@ +Locked Counters (aka ``QemuLockCnt``) +===================================== + +QEMU often uses reference counts to track data structures that are being +accessed and should not be freed. For example, a loop that invoke +callbacks like this is not safe:: + + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (ioh->revents & G_IO_OUT) { + ioh->fd_write(ioh->opaque); + } + } + +``QLIST_FOREACH_SAFE`` protects against deletion of the current node (``ioh``) +by stashing away its ``next`` pointer. However, ``ioh->fd_write`` could +actually delete the next node from the list. The simplest way to +avoid this is to mark the node as deleted, and remove it from the +list in the above loop:: + + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (ioh->deleted) { + QLIST_REMOVE(ioh, next); + g_free(ioh); + } else { + if (ioh->revents & G_IO_OUT) { + ioh->fd_write(ioh->opaque); + } + } + } + +If however this loop must also be reentrant, i.e. it is possible that +``ioh->fd_write`` invokes the loop again, some kind of counting is needed:: + + walking_handlers++; + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (ioh->deleted) { + if (walking_handlers == 1) { + QLIST_REMOVE(ioh, next); + g_free(ioh); + } + } else { + if (ioh->revents & G_IO_OUT) { + ioh->fd_write(ioh->opaque); + } + } + } + walking_handlers--; + +One may think of using the RCU primitives, ``rcu_read_lock()`` and +``rcu_read_unlock()``; effectively, the RCU nesting count would take +the place of the walking_handlers global variable. Indeed, +reference counting and RCU have similar purposes, but their usage in +general is complementary: + +- reference counting is fine-grained and limited to a single data + structure; RCU delays reclamation of *all* RCU-protected data + structures; + +- reference counting works even in the presence of code that keeps + a reference for a long time; RCU critical sections in principle + should be kept short; + +- reference counting is often applied to code that is not thread-safe + but is reentrant; in fact, usage of reference counting in QEMU predates + the introduction of threads by many years. RCU is generally used to + protect readers from other threads freeing memory after concurrent + modifications to a data structure. + +- reclaiming data can be done by a separate thread in the case of RCU; + this can improve performance, but also delay reclamation undesirably. + With reference counting, reclamation is deterministic. + +This file documents ``QemuLockCnt``, an abstraction for using reference +counting in code that has to be both thread-safe and reentrant. + + +``QemuLockCnt`` concepts +------------------------ + +A ``QemuLockCnt`` comprises both a counter and a mutex; it has primitives +to increment and decrement the counter, and to take and release the +mutex. The counter notes how many visits to the data structures are +taking place (the visits could be from different threads, or there could +be multiple reentrant visits from the same thread). The basic rules +governing the counter/mutex pair then are the following: + +- Data protected by the QemuLockCnt must not be freed unless the + counter is zero and the mutex is taken. + +- A new visit cannot be started while the counter is zero and the + mutex is taken. + +Most of the time, the mutex protects all writes to the data structure, +not just frees, though there could be cases where this is not necessary. + +Reads, instead, can be done without taking the mutex, as long as the +readers and writers use the same macros that are used for RCU, for +example ``qatomic_rcu_read``, ``qatomic_rcu_set``, ``QLIST_FOREACH_RCU``, +etc. This is because the reads are done outside a lock and a set +or ``QLIST_INSERT_HEAD`` +can happen concurrently with the read. The RCU API ensures that the +processor and the compiler see all required memory barriers. + +This could be implemented simply by protecting the counter with the +mutex, for example:: + + // (1) + qemu_mutex_lock(&walking_handlers_mutex); + walking_handlers++; + qemu_mutex_unlock(&walking_handlers_mutex); + + ... + + // (2) + qemu_mutex_lock(&walking_handlers_mutex); + if (--walking_handlers == 0) { + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (ioh->deleted) { + QLIST_REMOVE(ioh, next); + g_free(ioh); + } + } + } + qemu_mutex_unlock(&walking_handlers_mutex); + +Here, no frees can happen in the code represented by the ellipsis. +If another thread is executing critical section (2), that part of +the code cannot be entered, because the thread will not be able +to increment the ``walking_handlers`` variable. And of course +during the visit any other thread will see a nonzero value for +``walking_handlers``, as in the single-threaded code. + +Note that it is possible for multiple concurrent accesses to delay +the cleanup arbitrarily; in other words, for the ``walking_handlers`` +counter to never become zero. For this reason, this technique is +more easily applicable if concurrent access to the structure is rare. + +However, critical sections are easy to forget since you have to do +them for each modification of the counter. ``QemuLockCnt`` ensures that +all modifications of the counter take the lock appropriately, and it +can also be more efficient in two ways: + +- it avoids taking the lock for many operations (for example + incrementing the counter while it is non-zero); + +- on some platforms, one can implement ``QemuLockCnt`` to hold the lock + and the mutex in a single word, making the fast path no more expensive + than simply managing a counter using atomic operations (see + :doc:`atomics`). This can be very helpful if concurrent access to + the data structure is expected to be rare. + + +Using the same mutex for frees and writes can still incur some small +inefficiencies; for example, a visit can never start if the counter is +zero and the mutex is taken -- even if the mutex is taken by a write, +which in principle need not block a visit of the data structure. +However, these are usually not a problem if any of the following +assumptions are valid: + +- concurrent access is possible but rare + +- writes are rare + +- writes are frequent, but this kind of write (e.g. appending to a + list) has a very small critical section. + +For example, QEMU uses ``QemuLockCnt`` to manage an ``AioContext``'s list of +bottom halves and file descriptor handlers. Modifications to the list +of file descriptor handlers are rare. Creation of a new bottom half is +frequent and can happen on a fast path; however: 1) it is almost never +concurrent with a visit to the list of bottom halves; 2) it only has +three instructions in the critical path, two assignments and a ``smp_wmb()``. + + +``QemuLockCnt`` API +------------------- + +.. kernel-doc:: include/qemu/lockcnt.h + + +``QemuLockCnt`` usage +--------------------- + +This section explains the typical usage patterns for ``QemuLockCnt`` functions. + +Setting a variable to a non-NULL value can be done between +``qemu_lockcnt_lock`` and ``qemu_lockcnt_unlock``:: + + qemu_lockcnt_lock(&xyz_lockcnt); + if (!xyz) { + new_xyz = g_new(XYZ, 1); + ... + qatomic_rcu_set(&xyz, new_xyz); + } + qemu_lockcnt_unlock(&xyz_lockcnt); + +Accessing the value can be done between ``qemu_lockcnt_inc`` and +``qemu_lockcnt_dec``:: + + qemu_lockcnt_inc(&xyz_lockcnt); + if (xyz) { + XYZ *p = qatomic_rcu_read(&xyz); + ... + /* Accesses can now be done through "p". */ + } + qemu_lockcnt_dec(&xyz_lockcnt); + +Freeing the object can similarly use ``qemu_lockcnt_lock`` and +``qemu_lockcnt_unlock``, but you also need to ensure that the count +is zero (i.e. there is no concurrent visit). Because ``qemu_lockcnt_inc`` +takes the ``QemuLockCnt``'s lock, the count cannot become non-zero while +the object is being freed. Freeing an object looks like this:: + + qemu_lockcnt_lock(&xyz_lockcnt); + if (!qemu_lockcnt_count(&xyz_lockcnt)) { + g_free(xyz); + xyz = NULL; + } + qemu_lockcnt_unlock(&xyz_lockcnt); + +If an object has to be freed right after a visit, you can combine +the decrement, the locking and the check on count as follows:: + + qemu_lockcnt_inc(&xyz_lockcnt); + if (xyz) { + XYZ *p = qatomic_rcu_read(&xyz); + ... + /* Accesses can now be done through "p". */ + } + if (qemu_lockcnt_dec_and_lock(&xyz_lockcnt)) { + g_free(xyz); + xyz = NULL; + qemu_lockcnt_unlock(&xyz_lockcnt); + } + +``QemuLockCnt`` can also be used to access a list as follows:: + + qemu_lockcnt_inc(&io_handlers_lockcnt); + QLIST_FOREACH_RCU(ioh, &io_handlers, pioh) { + if (ioh->revents & G_IO_OUT) { + ioh->fd_write(ioh->opaque); + } + } + + if (qemu_lockcnt_dec_and_lock(&io_handlers_lockcnt)) { + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (ioh->deleted) { + QLIST_REMOVE(ioh, next); + g_free(ioh); + } + } + qemu_lockcnt_unlock(&io_handlers_lockcnt); + } + +Again, the RCU primitives are used because new items can be added to the +list during the walk. ``QLIST_FOREACH_RCU`` ensures that the processor and +the compiler see the appropriate memory barriers. + +An alternative pattern uses ``qemu_lockcnt_dec_if_lock``:: + + qemu_lockcnt_inc(&io_handlers_lockcnt); + QLIST_FOREACH_SAFE_RCU(ioh, &io_handlers, next, pioh) { + if (ioh->deleted) { + if (qemu_lockcnt_dec_if_lock(&io_handlers_lockcnt)) { + QLIST_REMOVE(ioh, next); + g_free(ioh); + qemu_lockcnt_inc_and_unlock(&io_handlers_lockcnt); + } + } else { + if (ioh->revents & G_IO_OUT) { + ioh->fd_write(ioh->opaque); + } + } + } + qemu_lockcnt_dec(&io_handlers_lockcnt); + +Here you can use ``qemu_lockcnt_dec`` instead of ``qemu_lockcnt_dec_and_lock``, +because there is no special task to do if the count goes from 1 to 0. diff --git a/docs/devel/lockcnt.txt b/docs/devel/lockcnt.txt deleted file mode 100644 index a3fb3bc5d8..0000000000 --- a/docs/devel/lockcnt.txt +++ /dev/null @@ -1,277 +0,0 @@ -DOCUMENTATION FOR LOCKED COUNTERS (aka QemuLockCnt) -=================================================== - -QEMU often uses reference counts to track data structures that are being -accessed and should not be freed. For example, a loop that invoke -callbacks like this is not safe: - - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (ioh->revents & G_IO_OUT) { - ioh->fd_write(ioh->opaque); - } - } - -QLIST_FOREACH_SAFE protects against deletion of the current node (ioh) -by stashing away its "next" pointer. However, ioh->fd_write could -actually delete the next node from the list. The simplest way to -avoid this is to mark the node as deleted, and remove it from the -list in the above loop: - - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (ioh->deleted) { - QLIST_REMOVE(ioh, next); - g_free(ioh); - } else { - if (ioh->revents & G_IO_OUT) { - ioh->fd_write(ioh->opaque); - } - } - } - -If however this loop must also be reentrant, i.e. it is possible that -ioh->fd_write invokes the loop again, some kind of counting is needed: - - walking_handlers++; - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (ioh->deleted) { - if (walking_handlers == 1) { - QLIST_REMOVE(ioh, next); - g_free(ioh); - } - } else { - if (ioh->revents & G_IO_OUT) { - ioh->fd_write(ioh->opaque); - } - } - } - walking_handlers--; - -One may think of using the RCU primitives, rcu_read_lock() and -rcu_read_unlock(); effectively, the RCU nesting count would take -the place of the walking_handlers global variable. Indeed, -reference counting and RCU have similar purposes, but their usage in -general is complementary: - -- reference counting is fine-grained and limited to a single data - structure; RCU delays reclamation of *all* RCU-protected data - structures; - -- reference counting works even in the presence of code that keeps - a reference for a long time; RCU critical sections in principle - should be kept short; - -- reference counting is often applied to code that is not thread-safe - but is reentrant; in fact, usage of reference counting in QEMU predates - the introduction of threads by many years. RCU is generally used to - protect readers from other threads freeing memory after concurrent - modifications to a data structure. - -- reclaiming data can be done by a separate thread in the case of RCU; - this can improve performance, but also delay reclamation undesirably. - With reference counting, reclamation is deterministic. - -This file documents QemuLockCnt, an abstraction for using reference -counting in code that has to be both thread-safe and reentrant. - - -QemuLockCnt concepts --------------------- - -A QemuLockCnt comprises both a counter and a mutex; it has primitives -to increment and decrement the counter, and to take and release the -mutex. The counter notes how many visits to the data structures are -taking place (the visits could be from different threads, or there could -be multiple reentrant visits from the same thread). The basic rules -governing the counter/mutex pair then are the following: - -- Data protected by the QemuLockCnt must not be freed unless the - counter is zero and the mutex is taken. - -- A new visit cannot be started while the counter is zero and the - mutex is taken. - -Most of the time, the mutex protects all writes to the data structure, -not just frees, though there could be cases where this is not necessary. - -Reads, instead, can be done without taking the mutex, as long as the -readers and writers use the same macros that are used for RCU, for -example qatomic_rcu_read, qatomic_rcu_set, QLIST_FOREACH_RCU, etc. This is -because the reads are done outside a lock and a set or QLIST_INSERT_HEAD -can happen concurrently with the read. The RCU API ensures that the -processor and the compiler see all required memory barriers. - -This could be implemented simply by protecting the counter with the -mutex, for example: - - // (1) - qemu_mutex_lock(&walking_handlers_mutex); - walking_handlers++; - qemu_mutex_unlock(&walking_handlers_mutex); - - ... - - // (2) - qemu_mutex_lock(&walking_handlers_mutex); - if (--walking_handlers == 0) { - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (ioh->deleted) { - QLIST_REMOVE(ioh, next); - g_free(ioh); - } - } - } - qemu_mutex_unlock(&walking_handlers_mutex); - -Here, no frees can happen in the code represented by the ellipsis. -If another thread is executing critical section (2), that part of -the code cannot be entered, because the thread will not be able -to increment the walking_handlers variable. And of course -during the visit any other thread will see a nonzero value for -walking_handlers, as in the single-threaded code. - -Note that it is possible for multiple concurrent accesses to delay -the cleanup arbitrarily; in other words, for the walking_handlers -counter to never become zero. For this reason, this technique is -more easily applicable if concurrent access to the structure is rare. - -However, critical sections are easy to forget since you have to do -them for each modification of the counter. QemuLockCnt ensures that -all modifications of the counter take the lock appropriately, and it -can also be more efficient in two ways: - -- it avoids taking the lock for many operations (for example - incrementing the counter while it is non-zero); - -- on some platforms, one can implement QemuLockCnt to hold the lock - and the mutex in a single word, making the fast path no more expensive - than simply managing a counter using atomic operations (see - docs/devel/atomics.rst). This can be very helpful if concurrent access to - the data structure is expected to be rare. - - -Using the same mutex for frees and writes can still incur some small -inefficiencies; for example, a visit can never start if the counter is -zero and the mutex is taken---even if the mutex is taken by a write, -which in principle need not block a visit of the data structure. -However, these are usually not a problem if any of the following -assumptions are valid: - -- concurrent access is possible but rare - -- writes are rare - -- writes are frequent, but this kind of write (e.g. appending to a - list) has a very small critical section. - -For example, QEMU uses QemuLockCnt to manage an AioContext's list of -bottom halves and file descriptor handlers. Modifications to the list -of file descriptor handlers are rare. Creation of a new bottom half is -frequent and can happen on a fast path; however: 1) it is almost never -concurrent with a visit to the list of bottom halves; 2) it only has -three instructions in the critical path, two assignments and a smp_wmb(). - - -QemuLockCnt API ---------------- - -The QemuLockCnt API is described in include/qemu/thread.h. - - -QemuLockCnt usage ------------------ - -This section explains the typical usage patterns for QemuLockCnt functions. - -Setting a variable to a non-NULL value can be done between -qemu_lockcnt_lock and qemu_lockcnt_unlock: - - qemu_lockcnt_lock(&xyz_lockcnt); - if (!xyz) { - new_xyz = g_new(XYZ, 1); - ... - qatomic_rcu_set(&xyz, new_xyz); - } - qemu_lockcnt_unlock(&xyz_lockcnt); - -Accessing the value can be done between qemu_lockcnt_inc and -qemu_lockcnt_dec: - - qemu_lockcnt_inc(&xyz_lockcnt); - if (xyz) { - XYZ *p = qatomic_rcu_read(&xyz); - ... - /* Accesses can now be done through "p". */ - } - qemu_lockcnt_dec(&xyz_lockcnt); - -Freeing the object can similarly use qemu_lockcnt_lock and -qemu_lockcnt_unlock, but you also need to ensure that the count -is zero (i.e. there is no concurrent visit). Because qemu_lockcnt_inc -takes the QemuLockCnt's lock, the count cannot become non-zero while -the object is being freed. Freeing an object looks like this: - - qemu_lockcnt_lock(&xyz_lockcnt); - if (!qemu_lockcnt_count(&xyz_lockcnt)) { - g_free(xyz); - xyz = NULL; - } - qemu_lockcnt_unlock(&xyz_lockcnt); - -If an object has to be freed right after a visit, you can combine -the decrement, the locking and the check on count as follows: - - qemu_lockcnt_inc(&xyz_lockcnt); - if (xyz) { - XYZ *p = qatomic_rcu_read(&xyz); - ... - /* Accesses can now be done through "p". */ - } - if (qemu_lockcnt_dec_and_lock(&xyz_lockcnt)) { - g_free(xyz); - xyz = NULL; - qemu_lockcnt_unlock(&xyz_lockcnt); - } - -QemuLockCnt can also be used to access a list as follows: - - qemu_lockcnt_inc(&io_handlers_lockcnt); - QLIST_FOREACH_RCU(ioh, &io_handlers, pioh) { - if (ioh->revents & G_IO_OUT) { - ioh->fd_write(ioh->opaque); - } - } - - if (qemu_lockcnt_dec_and_lock(&io_handlers_lockcnt)) { - QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { - if (ioh->deleted) { - QLIST_REMOVE(ioh, next); - g_free(ioh); - } - } - qemu_lockcnt_unlock(&io_handlers_lockcnt); - } - -Again, the RCU primitives are used because new items can be added to the -list during the walk. QLIST_FOREACH_RCU ensures that the processor and -the compiler see the appropriate memory barriers. - -An alternative pattern uses qemu_lockcnt_dec_if_lock: - - qemu_lockcnt_inc(&io_handlers_lockcnt); - QLIST_FOREACH_SAFE_RCU(ioh, &io_handlers, next, pioh) { - if (ioh->deleted) { - if (qemu_lockcnt_dec_if_lock(&io_handlers_lockcnt)) { - QLIST_REMOVE(ioh, next); - g_free(ioh); - qemu_lockcnt_inc_and_unlock(&io_handlers_lockcnt); - } - } else { - if (ioh->revents & G_IO_OUT) { - ioh->fd_write(ioh->opaque); - } - } - } - qemu_lockcnt_dec(&io_handlers_lockcnt); - -Here you can use qemu_lockcnt_dec instead of qemu_lockcnt_dec_and_lock, -because there is no special task to do if the count goes from 1 to 0. diff --git a/docs/devel/luks-detached-header.rst b/docs/devel/luks-detached-header.rst new file mode 100644 index 0000000000..94ec285c27 --- /dev/null +++ b/docs/devel/luks-detached-header.rst @@ -0,0 +1,182 @@ +================================ +LUKS volume with detached header +================================ + +Introduction +============ + +This document gives an overview of the design of LUKS volume with detached +header and how to use it. + +Background +========== + +The LUKS format has ability to store the header in a separate volume from +the payload. We could extend the LUKS driver in QEMU to support this use +case. + +Normally a LUKS volume has a layout: + +:: + + +-----------------------------------------------+ + | | | | + disk | header | key material | disk payload data | + | | | | + +-----------------------------------------------+ + +With a detached LUKS header, you need 2 disks so getting: + +:: + + +--------------------------+ + disk1 | header | key material | + +--------------------------+ + +---------------------+ + disk2 | disk payload data | + +---------------------+ + +There are a variety of benefits to doing this: + + * Secrecy - the disk2 cannot be identified as containing LUKS + volume since there's no header + * Control - if access to the disk1 is restricted, then even + if someone has access to disk2 they can't unlock + it. Might be useful if you have disks on NFS but + want to restrict which host can launch a VM + instance from it, by dynamically providing access + to the header to a designated host + * Flexibility - your application data volume may be a given + size and it is inconvenient to resize it to + add encryption.You can store the LUKS header + separately and use the existing storage + volume for payload + * Recovery - corruption of a bit in the header may make the + entire payload inaccessible. It might be + convenient to take backups of the header. If + your primary disk header becomes corrupt, you + can unlock the data still by pointing to the + backup detached header + +Architecture +============ + +Take the qcow2 encryption, for example. The architecture of the +LUKS volume with detached header is shown in the diagram below. + +There are two children of the root node: a file and a header. +Data from the disk payload is stored in the file node. The +LUKS header and key material are located in the header node, +as previously mentioned. + +:: + + +-----------------------------+ + Root node | foo[luks] | + +-----------------------------+ + | | + file | header | + | | + +---------------------+ +------------------+ + Child node |payload-format[qcow2]| |header-format[raw]| + +---------------------+ +------------------+ + | | + file | file | + | | + +----------------------+ +---------------------+ + Child node |payload-protocol[file]| |header-protocol[file]| + +----------------------+ +---------------------+ + | | + | | + | | + Host storage Host storage + +Usage +===== + +Create a LUKS disk with a detached header using qemu-img +-------------------------------------------------------- + +Shell commandline:: + + # qemu-img create --object secret,id=sec0,data=abc123 -f luks \ + -o cipher-alg=aes-256,cipher-mode=xts -o key-secret=sec0 \ + -o detached-header=true test-header.img + # qemu-img create -f qcow2 test-payload.qcow2 200G + # qemu-img info 'json:{"driver":"luks","file":{"filename": \ + "test-payload.img"},"header":{"filename":"test-header.img"}}' + +Set up a VM's LUKS volume with a detached header +------------------------------------------------ + +Qemu commandline:: + + # qemu-system-x86_64 ... \ + -object '{"qom-type":"secret","id":"libvirt-3-format-secret", \ + "data":"abc123"}' \ + -blockdev '{"driver":"file","filename":"/path/to/test-header.img", \ + "node-name":"libvirt-1-storage"}' \ + -blockdev '{"node-name":"libvirt-1-format","read-only":false, \ + "driver":"raw","file":"libvirt-1-storage"}' \ + -blockdev '{"driver":"file","filename":"/path/to/test-payload.qcow2", \ + "node-name":"libvirt-2-storage"}' \ + -blockdev '{"node-name":"libvirt-2-format","read-only":false, \ + "driver":"qcow2","file":"libvirt-2-storage"}' \ + -blockdev '{"node-name":"libvirt-3-format","driver":"luks", \ + "file":"libvirt-2-format","header":"libvirt-1-format","key-secret": \ + "libvirt-3-format-secret"}' \ + -device '{"driver":"virtio-blk-pci","bus":XXX,"addr":YYY,"drive": \ + "libvirt-3-format","id":"virtio-disk1"}' + +Add LUKS volume to a VM with a detached header +---------------------------------------------- + +1. object-add the secret for decrypting the cipher stored in + LUKS header above:: + + # virsh qemu-monitor-command vm '{"execute":"object-add", \ + "arguments":{"qom-type":"secret", "id": \ + "libvirt-4-format-secret", "data":"abc123"}}' + +2. block-add the protocol node for LUKS header:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-1-storage", "driver":"file", \ + "filename": "/path/to/test-header.img" }}' + +3. block-add the raw-drived node for LUKS header:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-1-format", "driver":"raw", \ + "file":"libvirt-1-storage"}}' + +4. block-add the protocol node for disk payload image:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-2-storage", "driver":"file", \ + "filename":"/path/to/test-payload.qcow2"}}' + +5. block-add the qcow2-drived format node for disk payload data:: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-2-format", "driver":"qcow2", \ + "file":"libvirt-2-storage"}}' + +6. block-add the luks-drived format node to link the qcow2 disk + with the LUKS header by specifying the field "header":: + + # virsh qemu-monitor-command vm '{"execute":"blockdev-add", \ + "arguments":{"node-name":"libvirt-3-format", "driver":"luks", \ + "file":"libvirt-2-format", "header":"libvirt-1-format", \ + "key-secret":"libvirt-2-format-secret"}}' + +7. hot-plug the virtio-blk device finally:: + + # virsh qemu-monitor-command vm '{"execute":"device_add", \ + "arguments": {"driver":"virtio-blk-pci", \ + "drive": "libvirt-3-format", "id":"virtio-disk2"}} + +TODO +==== + +1. Support the shared detached LUKS header within the VM. diff --git a/docs/devel/maintainers.rst b/docs/devel/maintainers.rst index 5c907d901c..88a613ed74 100644 --- a/docs/devel/maintainers.rst +++ b/docs/devel/maintainers.rst @@ -99,9 +99,9 @@ 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 +community member **in person**\ [#2020]_ so please make appropriate arrangements. -.. [#] In recent pandemic times we have had to exercise some +.. [#2020] 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.rst b/docs/devel/migration.rst deleted file mode 100644 index 3e9656d8e0..0000000000 --- a/docs/devel/migration.rst +++ /dev/null @@ -1,877 +0,0 @@ -========= -Migration -========= - -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 -that, saves the state for each device that the guest is running. -Restoring a guest is just the opposite operation: we need to load the -state of each device. - -For this to work, QEMU has to be launched with the same arguments the -two times. I.e. it can only restore the state in one guest that has -the same devices that the one it was saved (this last requirement can -be relaxed a bit, but for now we can consider that configuration has -to be exactly the same). - -Once that we are able to save/restore a guest, a new functionality is -requested: migration. This means that QEMU is able to start in one -machine and being "migrated" to another machine. I.e. being moved to -another machine. - -Next was the "live migration" functionality. This is important -because some guests run with a lot of state (specially RAM), and it -can take a while to move all state from one machine to another. Live -migration allows the guest to continue running while the state is -transferred. Only while the last part of the state is transferred has -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). - -Transports -========== - -The migration stream is normally just a byte stream that can be passed -over any transport. - -- tcp migration: do the migration using tcp sockets -- unix migration: do the migration using unix sockets -- 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. - -In addition, support is included for migration using RDMA, which -transports the page data using ``RDMA``, where the hardware takes care of -transporting the pages, and the load on the CPU is much lower. While the -internals of RDMA migration are a bit different, this isn't really visible -outside the RAM migration code. - -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 -===================== - -The files, sockets or fd's that carry the migration stream are abstracted by -the ``QEMUFile`` type (see ``migration/qemu-file.h``). In most cases this -is connected to a subtype of ``QIOChannel`` (see ``io/``). - - -Saving the state of one device -============================== - -For most devices, the state is saved in a single call to the migration -infrastructure; these are *non-iterative* devices. The data for these -devices is sent at the end of precopy migration, when the CPUs are paused. -There are also *iterative* devices, which contain a very large amount of -data (e.g. RAM or large tables). See the iterative device section below. - -General advice for device developers ------------------------------------- - -- The migration state saved should reflect the device being modelled rather - than the way your implementation works. That way if you change the implementation - later the migration stream will stay compatible. That model may include - internal state that's not directly visible in a register. - -- When saving a migration stream the device code may walk and check - the state of the device. These checks might fail in various ways (e.g. - discovering internal state is corrupt or that the guest has done something bad). - Consider carefully before asserting/aborting at this point, since the - normal response from users is that *migration broke their VM* since it had - apparently been running fine until then. In these error cases, the device - should log a message indicating the cause of error, and should consider - putting the device into an error state, allowing the rest of the VM to - continue execution. - -- The migration might happen at an inconvenient point, - e.g. right in the middle of the guest reprogramming the device, during - guest reboot or shutdown or while the device is waiting for external IO. - It's strongly preferred that migrations do not fail in this situation, - since in the cloud environment migrations might happen automatically to - VMs that the administrator doesn't directly control. - -- If you do need to fail a migration, ensure that sufficient information - is logged to identify what went wrong. - -- The destination should treat an incoming migration stream as hostile - (which we do to varying degrees in the existing code). Check that offsets - into buffers and the like can't cause overruns. Fail the incoming migration - in the case of a corrupted stream like this. - -- Take care with internal device state or behaviour that might become - migration version dependent. For example, the order of PCI capabilities - is required to stay constant across migration. Another example would - be that a special case handled by subsections (see below) might become - much more common if a default behaviour is changed. - -- The state of the source should not be changed or destroyed by the - outgoing migration. Migrations timing out or being failed by - higher levels of management, or failures of the destination host are - not unusual, and in that case the VM is restarted on the source. - Note that the management layer can validly revert the migration - even though the QEMU level of migration has succeeded as long as it - does it before starting execution on the destination. - -- Buses and devices should be able to explicitly specify addresses when - instantiated, and management tools should use those. For example, - when hot adding USB devices it's important to specify the ports - and addresses, since implicit ordering based on the command line order - may be different on the destination. This can result in the - device state being loaded into the wrong device. - -VMState -------- - -Most device data can be described using the ``VMSTATE`` macros (mostly defined -in ``include/migration/vmstate.h``). - -An example (from hw/input/pckbd.c) - -.. code:: c - - static const VMStateDescription vmstate_kbd = { - .name = "pckbd", - .version_id = 3, - .minimum_version_id = 3, - .fields = (VMStateField[]) { - VMSTATE_UINT8(write_cmd, KBDState), - VMSTATE_UINT8(status, KBDState), - VMSTATE_UINT8(mode, KBDState), - VMSTATE_UINT8(pending, KBDState), - VMSTATE_END_OF_LIST() - } - }; - -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: - -.. code:: c - - vmstate_register(NULL, 0, &vmstate_kbd, s); - -For devices that are ``qdev`` based, we can register the device in the class -init function: - -.. code:: c - - dc->vmsd = &vmstate_kbd_isa; - -The VMState macros take care of ensuring that the device data section -is formatted portably (normally big endian) and make some compile time checks -against the types of the fields in the structures. - -VMState macros can include other VMStateDescriptions to store substructures -(see ``VMSTATE_STRUCT_``), arrays (``VMSTATE_ARRAY_``) and variable length -arrays (``VMSTATE_VARRAY_``). Various other macros exist for special -cases. - -Note that the format on the wire is still very raw; i.e. a VMSTATE_UINT32 -ends up with a 4 byte bigendian representation on the wire; in the future -it might be possible to use a more structured format. - -Legacy way ----------- - -This way is going to disappear as soon as all current users are ported to VMSTATE; -although converting existing code can be tricky, and thus 'soon' is relative. - -Each device has to register two functions, one to save the state and -another to load the state back. - -.. code:: c - - int register_savevm_live(const char *idstr, - int instance_id, - int version_id, - SaveVMHandlers *ops, - void *opaque); - -Two functions in the ``ops`` structure are the ``save_state`` -and ``load_state`` functions. Notice that ``load_state`` receives a version_id -parameter to know what state format is receiving. ``save_state`` doesn't -have a version_id parameter because it always uses the latest version. - -Note that because the VMState macros still save the data in a raw -format, in many cases it's possible to replace legacy code -with a carefully constructed VMState description that matches the -byte layout of the existing code. - -Changing migration data structures ----------------------------------- - -When we migrate a device, we save/load the state as a series -of fields. Sometimes, due to bugs or new functionality, we need to -change the state to store more/different information. Changing the migration -state saved for a device can break migration compatibility unless -care is taken to use the appropriate techniques. In general QEMU tries -to maintain forward migration compatibility (i.e. migrating from -QEMU n->n+1) and there are users who benefit from backward compatibility -as well. - -Subsections ------------ - -The most common structure change is adding new data, e.g. when adding -a newer form of device, or adding that state that you previously -forgot to migrate. This is best solved using a subsection. - -A subsection is "like" a device vmstate, but with a particularity, it -has a Boolean function that tells if that values are needed to be sent -or not. If this functions returns false, the subsection is not sent. -Subsections have a unique name, that is looked for on the receiving -side. - -On the receiving side, if we found a subsection for a device that we -don't understand, we just fail the migration. If we understand all -the subsections, then we load the state with success. There's no check -that a subsection is loaded, so a newer QEMU that knows about a subsection -can (with care) load a stream from an older QEMU that didn't send -the subsection. - -If the new data is only needed in a rare case, then the subsection -can be made conditional on that case and the migration will still -succeed to older QEMUs in most cases. This is OK for data that's -critical, but in some use cases it's preferred that the migration -should succeed even with the data missing. To support this the -subsection can be connected to a device property and from there -to a versioned machine type. - -The 'pre_load' and 'post_load' functions on subsections are only -called if the subsection is loaded. - -One important note is that the outer post_load() function is called "after" -loading all subsections, because a newer subsection could change the same -value that it uses. A flag, and the combination of outer pre_load and -post_load can be used to detect whether a subsection was loaded, and to -fall back on default behaviour when the subsection isn't present. - -Example: - -.. code:: c - - static bool ide_drive_pio_state_needed(void *opaque) - { - IDEState *s = opaque; - - return ((s->status & DRQ_STAT) != 0) - || (s->bus->error_status & BM_STATUS_PIO_RETRY); - } - - const VMStateDescription vmstate_ide_drive_pio_state = { - .name = "ide_drive/pio_state", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = ide_drive_pio_pre_save, - .post_load = ide_drive_pio_post_load, - .needed = ide_drive_pio_state_needed, - .fields = (VMStateField[]) { - VMSTATE_INT32(req_nb_sectors, IDEState), - VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, - vmstate_info_uint8, uint8_t), - VMSTATE_INT32(cur_io_buffer_offset, IDEState), - VMSTATE_INT32(cur_io_buffer_len, IDEState), - VMSTATE_UINT8(end_transfer_fn_idx, IDEState), - VMSTATE_INT32(elementary_transfer_size, IDEState), - VMSTATE_INT32(packet_transfer_size, IDEState), - VMSTATE_END_OF_LIST() - } - }; - - const VMStateDescription vmstate_ide_drive = { - .name = "ide_drive", - .version_id = 3, - .minimum_version_id = 0, - .post_load = ide_drive_post_load, - .fields = (VMStateField[]) { - .... several fields .... - VMSTATE_END_OF_LIST() - }, - .subsections = (const VMStateDescription*[]) { - &vmstate_ide_drive_pio_state, - NULL - } - }; - -Here we have a subsection for the pio state. We only need to -save/send this state when we are in the middle of a pio operation -(that is what ``ide_drive_pio_state_needed()`` checks). If DRQ_STAT is -not enabled, the values on that fields are garbage and don't need to -be sent. - -Connecting subsections to properties ------------------------------------- - -Using a condition function that checks a 'property' to determine whether -to send a subsection allows backward migration compatibility when -new subsections are added, especially when combined with versioned -machine types. - -For example: - - a) Add a new property using ``DEFINE_PROP_BOOL`` - e.g. support-foo and - default it to true. - b) Add an entry to the ``hw_compat_`` for the previous version that sets - the property to false. - c) Add a static bool support_foo function that tests the property. - d) Add a subsection with a .needed set to the support_foo function - e) (potentially) Add an outer pre_load that sets up a default value - for 'foo' to be used if the subsection isn't loaded. - -Now that subsection will not be generated when using an older -machine type and the migration stream will be accepted by older -QEMU versions. - -Not sending existing elements ------------------------------ - -Sometimes members of the VMState are no longer needed: - - - removing them will break migration compatibility - - - making them version dependent and bumping the version will break backward migration - compatibility. - -Adding a dummy field into the migration stream is normally the best way to preserve -compatibility. - -If the field really does need to be removed then: - - a) Add a new property/compatibility/function in the same way for subsections above. - b) replace the VMSTATE macro with the _TEST version of the macro, e.g.: - - ``VMSTATE_UINT32(foo, barstruct)`` - - becomes - - ``VMSTATE_UINT32_TEST(foo, barstruct, pre_version_baz)`` - - Sometime in the future when we no longer care about the ancient versions these can be killed off. - Note that for backward compatibility it's important to fill in the structure with - data that the destination will understand. - -Any difference in the predicates on the source and destination will end up -with different fields being enabled and data being loaded into the wrong -fields; for this reason conditional fields like this are very fragile. - -Versions --------- - -Version numbers are intended for major incompatible changes to the -migration of a device, and using them breaks backward-migration -compatibility; in general most changes can be made by adding Subsections -(see above) or _TEST macros (see above) which won't break compatibility. - -Each version is associated with a series of fields saved. The ``save_state`` always saves -the state as the newer version. But ``load_state`` sometimes is able to -load state from an older version. - -You can see that there are two version fields: - -- ``version_id``: the maximum version_id supported by VMState for that device. -- ``minimum_version_id``: the minimum version_id that VMState is able to understand - for that device. - -VMState is able to read versions from minimum_version_id to version_id. - -There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields, -e.g. - -.. code:: c - - VMSTATE_UINT16_V(ip_id, Slirp, 2), - -only loads that field for versions 2 and newer. - -Saving state will always create a section with the 'version_id' value -and thus can't be loaded by any older QEMU. - -Massaging functions -------------------- - -Sometimes, it is not enough to be able to save the state directly -from one structure, we need to fill the correct values there. One -example is when we are using kvm. Before saving the cpu state, we -need to ask kvm to copy to QEMU the state that it is using. And the -opposite when we are loading the state, we need a way to tell kvm to -load the state for the cpu that we have just loaded from the QEMUFile. - -The functions to do that are inside a vmstate definition, and are called: - -- ``int (*pre_load)(void *opaque);`` - - This function is called before we load the state of one device. - -- ``int (*post_load)(void *opaque, int version_id);`` - - This function is called after we load the state of one device. - -- ``int (*pre_save)(void *opaque);`` - - This function is called before we save the state of one device. - -- ``int (*post_save)(void *opaque);`` - - This function is called after we save the state of one device - (even upon failure, unless the call to pre_save returned an error). - -Example: You can look at hpet.c, that uses the first three functions -to massage the state that is transferred. - -The ``VMSTATE_WITH_TMP`` macro may be useful when the migration -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 -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: - - - memory_region_add_subregion() - - memory_region_del_subregion() - - memory_region_set_readonly() - - memory_region_set_nonvolatile() - - memory_region_set_enabled() - - memory_region_set_address() - - memory_region_set_alias_offset() - -Iterative device migration --------------------------- - -Some devices, such as RAM, Block storage or certain platform devices, -have large amounts of data that would mean that the CPUs would be -paused for too long if they were sent in one section. For these -devices an *iterative* approach is taken. - -The iterative devices generally don't use VMState macros -(although it may be possible in some cases) and instead use -qemu_put_*/qemu_get_* macros to read/write data to the stream. Specialist -versions exist for high bandwidth IO. - - -An iterative device must provide: - - - A ``save_setup`` function that initialises the data structures and - transmits a first section containing information on the device. In the - case of RAM this transmits a list of RAMBlocks and sizes. - - - 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 ``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 ``save_live_complete_precopy`` function that must transmit the - last section for the device containing any remaining data. - - - A ``load_state`` function used to load sections generated by - any of the save functions that generate sections. - - - ``cleanup`` functions for both save and load that are called - at the end of migration. - -Note that the contents of the sections for iterative migration tend -to be open-coded by the devices; care should be taken in parsing -the results and structuring the stream to make them easy to validate. - -Device ordering ---------------- - -There are cases in which the ordering of device loading matters; for -example in some systems where a device may assert an interrupt during loading, -if the interrupt controller is loaded later then it might lose the state. - -Some ordering is implicitly provided by the order in which the machine -definition creates devices, however this is somewhat fragile. - -The ``MigrationPriority`` enum provides a means of explicitly enforcing -ordering. Numerically higher priorities are loaded earlier. -The priority is set by setting the ``priority`` field of the top level -``VMStateDescription`` for the device. - -Stream structure -================ - -The stream tries to be word and endian agnostic, allowing migration between hosts -of different characteristics running the same VM. - - - Header - - - Magic - - Version - - VM configuration section - - - Machine type - - Target page bits - - List of sections - Each section contains a device, or one iteration of a device save. - - - section type - - section id - - ID string (First section of each device) - - instance id (First section of each device) - - version id (First section of each device) - - - - Footer mark - - EOF mark - - VM Description structure - Consisting of a JSON description of the contents for analysis only - -The ``device data`` in each section consists of the data produced -by the code described above. For non-iterative devices they have a single -section; iterative devices have an initial and last section and a set -of parts in between. -Note that there is very little checking by the common code of the integrity -of the ``device data`` contents, that's up to the devices themselves. -The ``footer mark`` provides a little bit of protection for the case where -the receiving side reads more or less data than expected. - -The ``ID string`` is normally unique, having been formed from a bus name -and device address, PCI devices and storage devices hung off PCI controllers -fit this pattern well. Some devices are fixed single instances (e.g. "pc-ram"). -Others (especially either older devices or system devices which for -some reason don't have a bus concept) make use of the ``instance id`` -for otherwise identically named devices. - -Return path ------------ - -Only a unidirectional stream is required for normal migration, however a -``return path`` can be created when bidirectional communication is desired. -This is primarily used by postcopy, but is also used to return a success -flag to the source at the end of migration. - -``qemu_file_get_return_path(QEMUFile* fwdpath)`` gives the QEMUFile* for the return -path. - - Source side - - Forward path - written by migration thread - Return path - opened by main thread, read by return-path thread - - Destination side - - Forward path - read by main thread - 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/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..8f431d52f9 --- /dev/null +++ b/docs/devel/migration/features.rst @@ -0,0 +1,17 @@ +Migration features +================== + +Migration has plenty of features to support different use cases. + +.. toctree:: + :maxdepth: 2 + + postcopy + dirty-limit + vfio + virtio + mapped-ram + CPR + qpl-compression + uadk-compression + qatzip-compression 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/main.rst b/docs/devel/migration/main.rst new file mode 100644 index 0000000000..c2857fc244 --- /dev/null +++ b/docs/devel/migration/main.rst @@ -0,0 +1,602 @@ +=================== +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 +that, saves the state for each device that the guest is running. +Restoring a guest is just the opposite operation: we need to load the +state of each device. + +For this to work, QEMU has to be launched with the same arguments the +two times. I.e. it can only restore the state in one guest that has +the same devices that the one it was saved (this last requirement can +be relaxed a bit, but for now we can consider that configuration has +to be exactly the same). + +Once that we are able to save/restore a guest, a new functionality is +requested: migration. This means that QEMU is able to start in one +machine and being "migrated" to another machine. I.e. being moved to +another machine. + +Next was the "live migration" functionality. This is important +because some guests run with a lot of state (specially RAM), and it +can take a while to move all state from one machine to another. Live +migration allows the guest to continue running while the state is +transferred. Only while the last part of the state is transferred has +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 +========== + +The migration stream is normally just a byte stream that can be passed +over any transport. + +- tcp migration: do the migration using tcp sockets +- unix migration: do the migration using unix sockets +- 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. + + The file migration also supports using a file that has already been + opened. A set of file descriptors is passed to QEMU via an "fdset" + (see add-fd QMP command documentation). This method allows a + management application to have control over the migration file + opening operation. There are, however, strict requirements to this + interface if the multifd capability is enabled: + + - the fdset must contain two file descriptors that are not + duplicates between themselves; + - if the direct-io capability is to be used, exactly one of the + file descriptors must have the O_DIRECT flag set; + - the file must be opened with WRONLY on the migration source side + and RDONLY on the migration destination side. + +- rdma migration: support is included for migration using RDMA, which + transports the page data using ``RDMA``, where the hardware takes + care of transporting the pages, and the load on the CPU is much + lower. While the internals of RDMA migration are a bit different, + this isn't really visible outside the RAM migration code. + +All these migration protocols use the same infrastructure to +save/restore state devices. This infrastructure is shared with the +savevm/loadvm functionality. + +Common infrastructure +===================== + +The files, sockets or fd's that carry the migration stream are abstracted by +the ``QEMUFile`` type (see ``migration/qemu-file.h``). In most cases this +is connected to a subtype of ``QIOChannel`` (see ``io/``). + + +Saving the state of one device +============================== + +For most devices, the state is saved in a single call to the migration +infrastructure; these are *non-iterative* devices. The data for these +devices is sent at the end of precopy migration, when the CPUs are paused. +There are also *iterative* devices, which contain a very large amount of +data (e.g. RAM or large tables). See the iterative device section below. + +General advice for device developers +------------------------------------ + +- The migration state saved should reflect the device being modelled rather + than the way your implementation works. That way if you change the implementation + later the migration stream will stay compatible. That model may include + internal state that's not directly visible in a register. + +- When saving a migration stream the device code may walk and check + the state of the device. These checks might fail in various ways (e.g. + discovering internal state is corrupt or that the guest has done something bad). + Consider carefully before asserting/aborting at this point, since the + normal response from users is that *migration broke their VM* since it had + apparently been running fine until then. In these error cases, the device + should log a message indicating the cause of error, and should consider + putting the device into an error state, allowing the rest of the VM to + continue execution. + +- The migration might happen at an inconvenient point, + e.g. right in the middle of the guest reprogramming the device, during + guest reboot or shutdown or while the device is waiting for external IO. + It's strongly preferred that migrations do not fail in this situation, + since in the cloud environment migrations might happen automatically to + VMs that the administrator doesn't directly control. + +- If you do need to fail a migration, ensure that sufficient information + is logged to identify what went wrong. + +- The destination should treat an incoming migration stream as hostile + (which we do to varying degrees in the existing code). Check that offsets + into buffers and the like can't cause overruns. Fail the incoming migration + in the case of a corrupted stream like this. + +- Take care with internal device state or behaviour that might become + migration version dependent. For example, the order of PCI capabilities + is required to stay constant across migration. Another example would + be that a special case handled by subsections (see below) might become + much more common if a default behaviour is changed. + +- The state of the source should not be changed or destroyed by the + outgoing migration. Migrations timing out or being failed by + higher levels of management, or failures of the destination host are + not unusual, and in that case the VM is restarted on the source. + Note that the management layer can validly revert the migration + even though the QEMU level of migration has succeeded as long as it + does it before starting execution on the destination. + +- Buses and devices should be able to explicitly specify addresses when + instantiated, and management tools should use those. For example, + when hot adding USB devices it's important to specify the ports + and addresses, since implicit ordering based on the command line order + may be different on the destination. This can result in the + device state being loaded into the wrong device. + +VMState +------- + +Most device data can be described using the ``VMSTATE`` macros (mostly defined +in ``include/migration/vmstate.h``). + +An example (from hw/input/pckbd.c) + +.. code:: c + + static const VMStateDescription vmstate_kbd = { + .name = "pckbd", + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { + VMSTATE_UINT8(write_cmd, KBDState), + VMSTATE_UINT8(status, KBDState), + VMSTATE_UINT8(mode, KBDState), + VMSTATE_UINT8(pending, KBDState), + VMSTATE_END_OF_LIST() + } + }; + +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_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: + +.. code:: c + + dc->vmsd = &vmstate_kbd_isa; + +The VMState macros take care of ensuring that the device data section +is formatted portably (normally big endian) and make some compile time checks +against the types of the fields in the structures. + +VMState macros can include other VMStateDescriptions to store substructures +(see ``VMSTATE_STRUCT_``), arrays (``VMSTATE_ARRAY_``) and variable length +arrays (``VMSTATE_VARRAY_``). Various other macros exist for special +cases. + +Note that the format on the wire is still very raw; i.e. a VMSTATE_UINT32 +ends up with a 4 byte bigendian representation on the wire; in the future +it might be possible to use a more structured format. + +Legacy way +---------- + +This way is going to disappear as soon as all current users are ported to VMSTATE; +although converting existing code can be tricky, and thus 'soon' is relative. + +Each device has to register two functions, one to save the state and +another to load the state back. + +.. code:: c + + int register_savevm_live(const char *idstr, + int instance_id, + int version_id, + SaveVMHandlers *ops, + void *opaque); + +Two functions in the ``ops`` structure are the ``save_state`` +and ``load_state`` functions. Notice that ``load_state`` receives a version_id +parameter to know what state format is receiving. ``save_state`` doesn't +have a version_id parameter because it always uses the latest version. + +Note that because the VMState macros still save the data in a raw +format, in many cases it's possible to replace legacy code +with a carefully constructed VMState description that matches the +byte layout of the existing code. + +Changing migration data structures +---------------------------------- + +When we migrate a device, we save/load the state as a series +of fields. Sometimes, due to bugs or new functionality, we need to +change the state to store more/different information. Changing the migration +state saved for a device can break migration compatibility unless +care is taken to use the appropriate techniques. In general QEMU tries +to maintain forward migration compatibility (i.e. migrating from +QEMU n->n+1) and there are users who benefit from backward compatibility +as well. + +Subsections +----------- + +The most common structure change is adding new data, e.g. when adding +a newer form of device, or adding that state that you previously +forgot to migrate. This is best solved using a subsection. + +A subsection is "like" a device vmstate, but with a particularity, it +has a Boolean function that tells if that values are needed to be sent +or not. If this functions returns false, the subsection is not sent. +Subsections have a unique name, that is looked for on the receiving +side. + +On the receiving side, if we found a subsection for a device that we +don't understand, we just fail the migration. If we understand all +the subsections, then we load the state with success. There's no check +that a subsection is loaded, so a newer QEMU that knows about a subsection +can (with care) load a stream from an older QEMU that didn't send +the subsection. + +If the new data is only needed in a rare case, then the subsection +can be made conditional on that case and the migration will still +succeed to older QEMUs in most cases. This is OK for data that's +critical, but in some use cases it's preferred that the migration +should succeed even with the data missing. To support this the +subsection can be connected to a device property and from there +to a versioned machine type. + +The 'pre_load' and 'post_load' functions on subsections are only +called if the subsection is loaded. + +One important note is that the outer post_load() function is called "after" +loading all subsections, because a newer subsection could change the same +value that it uses. A flag, and the combination of outer pre_load and +post_load can be used to detect whether a subsection was loaded, and to +fall back on default behaviour when the subsection isn't present. + +Example: + +.. code:: c + + static bool ide_drive_pio_state_needed(void *opaque) + { + IDEState *s = opaque; + + return ((s->status & DRQ_STAT) != 0) + || (s->bus->error_status & BM_STATUS_PIO_RETRY); + } + + const VMStateDescription vmstate_ide_drive_pio_state = { + .name = "ide_drive/pio_state", + .version_id = 1, + .minimum_version_id = 1, + .pre_save = ide_drive_pio_pre_save, + .post_load = ide_drive_pio_post_load, + .needed = ide_drive_pio_state_needed, + .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), + VMSTATE_INT32(cur_io_buffer_offset, IDEState), + VMSTATE_INT32(cur_io_buffer_len, IDEState), + VMSTATE_UINT8(end_transfer_fn_idx, IDEState), + VMSTATE_INT32(elementary_transfer_size, IDEState), + VMSTATE_INT32(packet_transfer_size, IDEState), + VMSTATE_END_OF_LIST() + } + }; + + const VMStateDescription vmstate_ide_drive = { + .name = "ide_drive", + .version_id = 3, + .minimum_version_id = 0, + .post_load = ide_drive_post_load, + .fields = (const VMStateField[]) { + .... several fields .... + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_ide_drive_pio_state, + NULL + } + }; + +Here we have a subsection for the pio state. We only need to +save/send this state when we are in the middle of a pio operation +(that is what ``ide_drive_pio_state_needed()`` checks). If DRQ_STAT is +not enabled, the values on that fields are garbage and don't need to +be sent. + +Connecting subsections to properties +------------------------------------ + +Using a condition function that checks a 'property' to determine whether +to send a subsection allows backward migration compatibility when +new subsections are added, especially when combined with versioned +machine types. + +For example: + + a) Add a new property using ``DEFINE_PROP_BOOL`` - e.g. support-foo and + default it to true. + b) Add an entry to the ``hw_compat_`` for the previous version that sets + the property to false. + c) Add a static bool support_foo function that tests the property. + d) Add a subsection with a .needed set to the support_foo function + e) (potentially) Add an outer pre_load that sets up a default value + for 'foo' to be used if the subsection isn't loaded. + +Now that subsection will not be generated when using an older +machine type and the migration stream will be accepted by older +QEMU versions. + +Not sending existing elements +----------------------------- + +Sometimes members of the VMState are no longer needed: + + - removing them will break migration compatibility + + - making them version dependent and bumping the version will break backward migration + compatibility. + +Adding a dummy field into the migration stream is normally the best way to preserve +compatibility. + +If the field really does need to be removed then: + + a) Add a new property/compatibility/function in the same way for subsections above. + b) replace the VMSTATE macro with the _TEST version of the macro, e.g.: + + ``VMSTATE_UINT32(foo, barstruct)`` + + becomes + + ``VMSTATE_UINT32_TEST(foo, barstruct, pre_version_baz)`` + + Sometime in the future when we no longer care about the ancient versions these can be killed off. + Note that for backward compatibility it's important to fill in the structure with + data that the destination will understand. + +Any difference in the predicates on the source and destination will end up +with different fields being enabled and data being loaded into the wrong +fields; for this reason conditional fields like this are very fragile. + +Versions +-------- + +Version numbers are intended for major incompatible changes to the +migration of a device, and using them breaks backward-migration +compatibility; in general most changes can be made by adding Subsections +(see above) or _TEST macros (see above) which won't break compatibility. + +Each version is associated with a series of fields saved. The ``save_state`` always saves +the state as the newer version. But ``load_state`` sometimes is able to +load state from an older version. + +You can see that there are two version fields: + +- ``version_id``: the maximum version_id supported by VMState for that device. +- ``minimum_version_id``: the minimum version_id that VMState is able to understand + for that device. + +VMState is able to read versions from minimum_version_id to version_id. + +There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields, +e.g. + +.. code:: c + + VMSTATE_UINT16_V(ip_id, Slirp, 2), + +only loads that field for versions 2 and newer. + +Saving state will always create a section with the 'version_id' value +and thus can't be loaded by any older QEMU. + +Massaging functions +------------------- + +Sometimes, it is not enough to be able to save the state directly +from one structure, we need to fill the correct values there. One +example is when we are using kvm. Before saving the cpu state, we +need to ask kvm to copy to QEMU the state that it is using. And the +opposite when we are loading the state, we need a way to tell kvm to +load the state for the cpu that we have just loaded from the QEMUFile. + +The functions to do that are inside a vmstate definition, and are called: + +- ``int (*pre_load)(void *opaque);`` + + This function is called before we load the state of one device. + +- ``int (*post_load)(void *opaque, int version_id);`` + + This function is called after we load the state of one device. + +- ``int (*pre_save)(void *opaque);`` + + This function is called before we save the state of one device. + +- ``int (*post_save)(void *opaque);`` + + This function is called after we save the state of one device + (even upon failure, unless the call to pre_save returned an error). + +Example: You can look at hpet.c, that uses the first three functions +to massage the state that is transferred. + +The ``VMSTATE_WITH_TMP`` macro may be useful when the migration +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 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 API functions are: + + - memory_region_add_subregion() + - memory_region_del_subregion() + - memory_region_set_readonly() + - memory_region_set_nonvolatile() + - memory_region_set_enabled() + - memory_region_set_address() + - memory_region_set_alias_offset() + - portio_list_set_address() + - portio_list_set_enabled() + +Since the order of device save/restore is not defined, you must +avoid accessing or changing any other device's state in one of these +callbacks. (For instance, don't do anything that calls ``update_irq()`` +in a ``post_load`` hook.) Otherwise, restore will not be deterministic, +and this will break execution record/replay. + +Iterative device migration +-------------------------- + +Some devices, such as RAM or certain platform devices, +have large amounts of data that would mean that the CPUs would be +paused for too long if they were sent in one section. For these +devices an *iterative* approach is taken. + +The iterative devices generally don't use VMState macros +(although it may be possible in some cases) and instead use +qemu_put_*/qemu_get_* macros to read/write data to the stream. Specialist +versions exist for high bandwidth IO. + + +An iterative device must provide: + + - A ``save_setup`` function that initialises the data structures and + transmits a first section containing information on the device. In the + case of RAM this transmits a list of RAMBlocks and sizes. + + - A ``load_setup`` function that initialises the data structures on the + destination. + + - 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 ``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. + + - A ``load_state`` function used to load sections generated by + any of the save functions that generate sections. + + - ``cleanup`` functions for both save and load that are called + at the end of migration. + +Note that the contents of the sections for iterative migration tend +to be open-coded by the devices; care should be taken in parsing +the results and structuring the stream to make them easy to validate. + +Device ordering +--------------- + +There are cases in which the ordering of device loading matters; for +example in some systems where a device may assert an interrupt during loading, +if the interrupt controller is loaded later then it might lose the state. + +Some ordering is implicitly provided by the order in which the machine +definition creates devices, however this is somewhat fragile. + +The ``MigrationPriority`` enum provides a means of explicitly enforcing +ordering. Numerically higher priorities are loaded earlier. +The priority is set by setting the ``priority`` field of the top level +``VMStateDescription`` for the device. + +Stream structure +================ + +The stream tries to be word and endian agnostic, allowing migration between hosts +of different characteristics running the same VM. + + - Header + + - Magic + - Version + - VM configuration section + + - Machine type + - Target page bits + - List of sections + Each section contains a device, or one iteration of a device save. + + - section type + - section id + - ID string (First section of each device) + - instance id (First section of each device) + - version id (First section of each device) + - + - Footer mark + - EOF mark + - VM Description structure + Consisting of a JSON description of the contents for analysis only + +The ``device data`` in each section consists of the data produced +by the code described above. For non-iterative devices they have a single +section; iterative devices have an initial and last section and a set +of parts in between. +Note that there is very little checking by the common code of the integrity +of the ``device data`` contents, that's up to the devices themselves. +The ``footer mark`` provides a little bit of protection for the case where +the receiving side reads more or less data than expected. + +The ``ID string`` is normally unique, having been formed from a bus name +and device address, PCI devices and storage devices hung off PCI controllers +fit this pattern well. Some devices are fixed single instances (e.g. "pc-ram"). +Others (especially either older devices or system devices which for +some reason don't have a bus concept) make use of the ``instance id`` +for otherwise identically named devices. + +Return path +----------- + +Only a unidirectional stream is required for normal migration, however a +``return path`` can be created when bidirectional communication is desired. +This is primarily used by postcopy, but is also used to return a success +flag to the source at the end of migration. + +``qemu_file_get_return_path(QEMUFile* fwdpath)`` gives the QEMUFile* for the return +path. + + Source side + + Forward path - written by migration thread + Return path - opened by main thread, read by return-path thread + + Destination side + + Forward path - read by main thread + Return path - opened by main thread, written by main thread AND postcopy + thread (protected by rp_mutex) + diff --git a/docs/devel/migration/mapped-ram.rst b/docs/devel/migration/mapped-ram.rst new file mode 100644 index 0000000000..b08c2b433c --- /dev/null +++ b/docs/devel/migration/mapped-ram.rst @@ -0,0 +1,142 @@ +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. + +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. + +For best performance enable the ``direct-io`` parameter as well: + + ``migrate_set_parameter direct-io on`` + +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\ [#alternatives]_. 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. + +.. [#alternatives] 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..82e7a848c6 --- /dev/null +++ b/docs/devel/migration/postcopy.rst @@ -0,0 +1,314 @@ +======== +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). + + - 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 **POSTCOPY_PAUSED** + migration 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. + Source QEMU will go into **POSTCOPY_RECOVER_SETUP** state trying to + re-establish the channels. + + - When both sides of QEMU successfully reconnect using a new or fixed up + channel, they will go into **POSTCOPY_RECOVER** state, some handshake + procedure will be needed to properly synchronize the VM states between + the two QEMUs to continue the postcopy migration. For example, there + can be pages sent right during the window when the network is + interrupted, then the handshake will guarantee pages lost in-flight + will be resent again. + + - After a proper handshake synchronization, QEMU will continue the + postcopy migration on both sides and go back to **POSTCOPY_ACTIVE** + state. Postcopy migration will continue. + +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/qatzip-compression.rst b/docs/devel/migration/qatzip-compression.rst new file mode 100644 index 0000000000..862b383164 --- /dev/null +++ b/docs/devel/migration/qatzip-compression.rst @@ -0,0 +1,165 @@ +================== +QATzip Compression +================== +In scenarios with limited network bandwidth, the ``QATzip`` solution can help +users save a lot of host CPU resources by accelerating compression and +decompression through the Intel QuickAssist Technology(``QAT``) hardware. + + +The following test was conducted using 8 multifd channels and 10Gbps network +bandwidth. The results show that, compared to zstd, ``QATzip`` significantly +saves CPU resources on the sender and reduces migration time. Compared to the +uncompressed solution, ``QATzip`` greatly improves the dirty page processing +capability, indicated by the Pages per Second metric, and also reduces the +total migration time. + +:: + + VM Configuration: 16 vCPU and 64G memory + VM Workload: all vCPUs are idle and 54G memory is filled with Silesia data. + QAT Devices: 4 + |-----------|--------|---------|----------|----------|------|------| + |8 Channels |Total |down |throughput|pages per | send | recv | + | |time(ms)|time(ms) |(mbps) |second | cpu %| cpu% | + |-----------|--------|---------|----------|----------|------|------| + |qatzip | 16630| 28| 10467| 2940235| 160| 360| + |-----------|--------|---------|----------|----------|------|------| + |zstd | 20165| 24| 8579| 2391465| 810| 340| + |-----------|--------|---------|----------|----------|------|------| + |none | 46063| 40| 10848| 330240| 45| 85| + |-----------|--------|---------|----------|----------|------|------| + + +QATzip Compression Framework +============================ + +``QATzip`` is a user space library which builds on top of the Intel QuickAssist +Technology to provide extended accelerated compression and decompression +services. + +For more ``QATzip`` introduction, please refer to `QATzip Introduction +`_ + +:: + + +----------------+ + | MultiFd Thread | + +-------+--------+ + | + | compress/decompress + +-------+--------+ + | QATzip library | + +-------+--------+ + | + +-------+--------+ + | QAT library | + +-------+--------+ + | user space + --------+--------------------- + | kernel space + +------+-------+ + | QAT Driver | + +------+-------+ + | + +------+-------+ + | QAT Devices | + +--------------+ + + +QATzip Installation +------------------- + +The ``QATzip`` installation package has been integrated into some Linux +distributions and can be installed directly. For example, the Ubuntu Server +24.04 LTS system can be installed using below command + +.. code-block:: shell + + #apt search qatzip + libqatzip-dev/noble 1.2.0-0ubuntu3 amd64 + Intel QuickAssist user space library development files + + libqatzip3/noble 1.2.0-0ubuntu3 amd64 + Intel QuickAssist user space library + + qatzip/noble,now 1.2.0-0ubuntu3 amd64 [installed] + Compression user-space tool for Intel QuickAssist Technology + + #sudo apt install libqatzip-dev libqatzip3 qatzip + +If your system does not support the ``QATzip`` installation package, you can +use the source code to build and install, please refer to `QATzip source code installation +`_ + +QAT Hardware Deployment +----------------------- + +``QAT`` supports physical functions(PFs) and virtual functions(VFs) for +deployment, and users can configure ``QAT`` resources for migration according +to actual needs. For more details about ``QAT`` deployment, please refer to +`Intel QuickAssist Technology Documentation +`_ + +For more ``QAT`` hardware introduction, please refer to `intel-quick-assist-technology-overview +`_ + +How To Use QATzip Compression +============================= + +1 - Install ``QATzip`` library + +2 - Build ``QEMU`` with ``--enable-qatzip`` parameter + + E.g. configure --target-list=x86_64-softmmu --enable-kvm ``--enable-qatzip`` + +3 - Set ``migrate_set_parameter multifd-compression qatzip`` + +4 - Set ``migrate_set_parameter multifd-qatzip-level comp_level``, the default +comp_level value is 1, and it supports levels from 1 to 9 + +QAT Memory Requirements +======================= + +The user needs to reserve system memory for the QAT memory management to +allocate DMA memory. The size of the reserved system memory depends on the +number of devices used for migration and the number of multifd channels. + +Because memory usage depends on QAT configuration, please refer to `QAT Memory +Driver Queries +`_ +for memory usage calculation. + +.. list-table:: An example of a PF used for migration + :header-rows: 1 + + * - Number of channels + - Sender memory usage + - Receiver memory usage + * - 2 + - 10M + - 10M + * - 4 + - 12M + - 14M + * - 8 + - 16M + - 20M + +How To Choose Between QATzip and QPL +==================================== +Starting from 4th Gen Intel Xeon Scalable processors, codenamed Sapphire Rapids +processor(``SPR``), multiple built-in accelerators are supported including +``QAT`` and ``IAA``. The former can accelerate ``QATzip`` and the latter is +used to accelerate ``QPL``. + +Here are some suggestions: + +1 - If the live migration scenario is limited by network bandwidth and ``QAT`` +hardware resources exceed ``IAA``, use the ``QATzip`` method, which can save a +lot of host CPU resources for compression. + +2 - If the system cannot support shared virtual memory (SVM) technology, use +the ``QATzip`` method because ``QPL`` performance is not good without SVM +support. + +3 - For other scenarios, use the ``QPL`` method first. diff --git a/docs/devel/migration/qpl-compression.rst b/docs/devel/migration/qpl-compression.rst new file mode 100644 index 0000000000..990992d786 --- /dev/null +++ b/docs/devel/migration/qpl-compression.rst @@ -0,0 +1,260 @@ +=============== +QPL Compression +=============== +The Intel Query Processing Library (Intel ``QPL``) is an open-source library to +provide compression and decompression features and it is based on deflate +compression algorithm (RFC 1951). + +The ``QPL`` compression relies on Intel In-Memory Analytics Accelerator(``IAA``) +and Shared Virtual Memory(``SVM``) technology, they are new features supported +from Intel 4th Gen Intel Xeon Scalable processors, codenamed Sapphire Rapids +processor(``SPR``). + +For more ``QPL`` introduction, please refer to `QPL Introduction +`_ + +QPL Compression Framework +========================= + +:: + + +----------------+ +------------------+ + | MultiFD Thread | |accel-config tool | + +-------+--------+ +--------+---------+ + | | + | | + |compress/decompress | + +-------+--------+ | Setup IAA + | QPL library | | Resources + +-------+---+----+ | + | | | + | +-------------+-------+ + | Open IAA | + | Devices +-----+-----+ + | |idxd driver| + | +-----+-----+ + | | + | | + | +-----+-----+ + +-----------+IAA Devices| + Submit jobs +-----------+ + via enqcmd + + +QPL Build And Installation +-------------------------- + +.. code-block:: shell + + $git clone --recursive https://github.com/intel/qpl.git qpl + $mkdir qpl/build + $cd qpl/build + $cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DQPL_LIBRARY_TYPE=SHARED .. + $sudo cmake --build . --target install + +For more details about ``QPL`` installation, please refer to `QPL Installation +`_ + +IAA Device Management +--------------------- + +The number of ``IAA`` devices will vary depending on the Xeon product model. +On a ``SPR`` server, there can be a maximum of 8 ``IAA`` devices, with up to +4 devices per socket. + +By default, all ``IAA`` devices are disabled and need to be configured and +enabled by users manually. + +Check the number of devices through the following command + +.. code-block:: shell + + #lspci -d 8086:0cfe + 6a:02.0 System peripheral: Intel Corporation Device 0cfe + 6f:02.0 System peripheral: Intel Corporation Device 0cfe + 74:02.0 System peripheral: Intel Corporation Device 0cfe + 79:02.0 System peripheral: Intel Corporation Device 0cfe + e7:02.0 System peripheral: Intel Corporation Device 0cfe + ec:02.0 System peripheral: Intel Corporation Device 0cfe + f1:02.0 System peripheral: Intel Corporation Device 0cfe + f6:02.0 System peripheral: Intel Corporation Device 0cfe + +IAA Device Configuration And Enabling +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``accel-config`` tool is used to enable ``IAA`` devices and configure +``IAA`` hardware resources(work queues and engines). One ``IAA`` device +has 8 work queues and 8 processing engines, multiple engines can be assigned +to a work queue via ``group`` attribute. + +For ``accel-config`` installation, please refer to `accel-config installation +`_ + +One example of configuring and enabling an ``IAA`` device. + +.. code-block:: shell + + #accel-config config-engine iax1/engine1.0 -g 0 + #accel-config config-engine iax1/engine1.1 -g 0 + #accel-config config-engine iax1/engine1.2 -g 0 + #accel-config config-engine iax1/engine1.3 -g 0 + #accel-config config-engine iax1/engine1.4 -g 0 + #accel-config config-engine iax1/engine1.5 -g 0 + #accel-config config-engine iax1/engine1.6 -g 0 + #accel-config config-engine iax1/engine1.7 -g 0 + #accel-config config-wq iax1/wq1.0 -g 0 -s 128 -p 10 -b 1 -t 128 -m shared -y user -n app1 -d user + #accel-config enable-device iax1 + #accel-config enable-wq iax1/wq1.0 + +.. note:: + IAX is an early name for IAA + +- The ``IAA`` device index is 1, use ``ls -lh /sys/bus/dsa/devices/iax*`` + command to query the ``IAA`` device index. + +- 8 engines and 1 work queue are configured in group 0, so all compression jobs + submitted to this work queue can be processed by all engines at the same time. + +- Set work queue attributes including the work mode, work queue size and so on. + +- Enable the ``IAA1`` device and work queue 1.0 + +.. note:: + + Set work queue mode to shared mode, since ``QPL`` library only supports + shared mode + +For more detailed configuration, please refer to `IAA Configuration Samples +`_ + +IAA Unit Test +^^^^^^^^^^^^^ + +- Enabling ``IAA`` devices for Xeon platform, please refer to `IAA User Guide + `_ + +- ``IAA`` device driver is Intel Data Accelerator Driver (idxd), it is + recommended that the minimum version of Linux kernel is 5.18. + +- Add ``"intel_iommu=on,sm_on"`` parameter to kernel command line + for ``SVM`` feature enabling. + +Here is an easy way to verify ``IAA`` device driver and ``SVM`` with `iaa_test +`_ + +.. code-block:: shell + + #./test/iaa_test + [ info] alloc wq 0 shared size 128 addr 0x7f26cebe5000 batch sz 0xfffffffe xfer sz 0x80000000 + [ info] test noop: tflags 0x1 num_desc 1 + [ info] preparing descriptor for noop + [ info] Submitted all noop jobs + [ info] verifying task result for 0x16f7e20 + [ info] test with op 0 passed + + +IAA Resources Allocation For Migration +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There is no ``IAA`` resource configuration parameters for migration and +``accel-config`` tool configuration cannot directly specify the ``IAA`` +resources used for migration. + +The multifd migration with ``QPL`` compression method will use all work +queues that are enabled and shared mode. + +.. note:: + + Accessing IAA resources requires ``sudo`` command or ``root`` privileges + by default. Administrators can modify the IAA device node ownership + so that QEMU can use IAA with specified user permissions. + + For example + + #chown -R qemu /dev/iax + +Shared Virtual Memory(SVM) Introduction +======================================= + +An ability for an accelerator I/O device to operate in the same virtual +memory space of applications on host processors. It also implies the +ability to operate from pageable memory, avoiding functional requirements +to pin memory for DMA operations. + +When using ``SVM`` technology, users do not need to reserve memory for the +``IAA`` device and perform pin memory operation. The ``IAA`` device can +directly access data using the virtual address of the process. + +For more ``SVM`` technology, please refer to +`Shared Virtual Addressing (SVA) with ENQCMD +`_ + + +How To Use QPL Compression In Migration +======================================= + +1 - Installation of ``QPL`` library and ``accel-config`` library if using IAA + +2 - Configure and enable ``IAA`` devices and work queues via ``accel-config`` + +3 - Build ``QEMU`` with ``--enable-qpl`` parameter + + E.g. configure --target-list=x86_64-softmmu --enable-kvm ``--enable-qpl`` + +4 - Enable ``QPL`` compression during migration + + Set ``migrate_set_parameter multifd-compression qpl`` when migrating, the + ``QPL`` compression does not support configuring the compression level, it + only supports one compression level. + +The Difference Between QPL And ZLIB +=================================== + +Although both ``QPL`` and ``ZLIB`` are based on the deflate compression +algorithm, and ``QPL`` can support the header and tail of ``ZLIB``, ``QPL`` +is still not fully compatible with the ``ZLIB`` compression in the migration. + +``QPL`` only supports 4K history buffer, and ``ZLIB`` is 32K by default. +``ZLIB`` compresses data that ``QPL`` may not decompress correctly and +vice versa. + +``QPL`` does not support the ``Z_SYNC_FLUSH`` operation in ``ZLIB`` streaming +compression, current ``ZLIB`` implementation uses ``Z_SYNC_FLUSH``, so each +``multifd`` thread has a ``ZLIB`` streaming context, and all page compression +and decompression are based on this stream. ``QPL`` cannot decompress such data +and vice versa. + +The introduction for ``Z_SYNC_FLUSH``, please refer to `Zlib Manual +`_ + +The Best Practices +================== +When user enables the IAA device for ``QPL`` compression, it is recommended +to add ``-mem-prealloc`` parameter to the destination boot parameters. This +parameter can avoid the occurrence of I/O page fault and reduce the overhead +of IAA compression and decompression. + +The example of booting with ``-mem-prealloc`` parameter + +.. code-block:: shell + + $qemu-system-x86_64 --enable-kvm -cpu host --mem-prealloc ... + + +An example about I/O page fault measurement of destination without +``-mem-prealloc``, the ``svm_prq`` indicates the number of I/O page fault +occurrences and processing time. + +.. code-block:: shell + + #echo 1 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #echo 2 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #echo 3 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #echo 4 > /sys/kernel/debug/iommu/intel/dmar_perf_latency + #cat /sys/kernel/debug/iommu/intel/dmar_perf_latency + IOMMU: dmar18 Register Base Address: c87fc000 + <0.1us 0.1us-1us 1us-10us 10us-100us 100us-1ms 1ms-10ms >=10ms min(us) max(us) average(us) + inv_iotlb 0 286 123 0 0 0 0 0 1 0 + inv_devtlb 0 276 133 0 0 0 0 0 2 0 + inv_iec 0 0 0 0 0 0 0 0 0 0 + svm_prq 0 0 25206 364 395 0 0 1 556 9 diff --git a/docs/devel/migration/uadk-compression.rst b/docs/devel/migration/uadk-compression.rst new file mode 100644 index 0000000000..64cadebd21 --- /dev/null +++ b/docs/devel/migration/uadk-compression.rst @@ -0,0 +1,144 @@ +========================================================= +User Space Accelerator Development Kit (UADK) Compression +========================================================= +UADK is a general-purpose user space accelerator framework that uses shared +virtual addressing (SVA) to provide a unified programming interface for +hardware acceleration of cryptographic and compression algorithms. + +UADK includes Unified/User-space-access-intended Accelerator Framework (UACCE), +which enables hardware accelerators from different vendors that support SVA to +adapt to UADK. + +Currently, HiSilicon Kunpeng hardware accelerators have been registered with +UACCE. Through the UADK framework, users can run cryptographic and compression +algorithms using hardware accelerators instead of CPUs, freeing up CPU +computing power and improving computing performance. + +https://github.com/Linaro/uadk/tree/master/docs + +UADK Framework +============== +UADK consists of UACCE, vendors' drivers, and an algorithm layer. UADK requires +the hardware accelerator to support SVA, and the operating system to support +IOMMU and SVA. Hardware accelerators from different vendors are registered as +different character devices with UACCE by using kernel-mode drivers of the +vendors. A user can access the hardware accelerators by performing user-mode +operations on the character devices. + +:: + + +----------------------------------+ + | apps | + +----+------------------------+----+ + | | + | | + +-------+--------+ +-------+-------+ + | scheduler | | alg libraries | + +-------+--------+ +-------+-------+ + | | + | | + | | + | +--------+------+ + | | vendor drivers| + | +-+-------------+ + | | + | | + +--+------------------+--+ + | libwd | + User +----+-------------+-----+ + -------------------------------------------------- + Kernel +--+-----+ +------+ + | uacce | | smmu | + +---+----+ +------+ + | + +---+------------------+ + | vendor kernel driver | + +----------------------+ + -------------------------------------------------- + +----------------------+ + | HW Accelerators | + +----------------------+ + +UADK Installation +----------------- +Build UADK +^^^^^^^^^^ + +.. code-block:: shell + + git clone https://github.com/Linaro/uadk.git + cd uadk + mkdir build + ./autogen.sh + ./configure --prefix=$PWD/build + make + make install + +Without --prefix, UADK will be installed to /usr/local/lib by default. +If get error:"cannot find -lnuma", please install the libnuma-dev + +Run pkg-config libwd to ensure env is setup correctly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* export PKG_CONFIG_PATH=$PWD/build/lib/pkgconfig +* pkg-config libwd --cflags --libs + -I/usr/local/include -L/usr/local/lib -lwd + +* export PKG_CONFIG_PATH is required on demand. + Not required if UADK is installed to /usr/local/lib + +UADK Host Kernel Requirements +----------------------------- +User needs to make sure that ``UACCE`` is already supported in Linux kernel. +The kernel version should be at least v5.9 with SVA (Shared Virtual +Addressing) enabled. + +Kernel Configuration +^^^^^^^^^^^^^^^^^^^^ + +``UACCE`` could be built as module or built-in. + +Here's an example to enable UACCE with hardware accelerator in HiSilicon +Kunpeng platform. + +* CONFIG_IOMMU_SVA_LIB=y +* CONFIG_ARM_SMMU=y +* CONFIG_ARM_SMMU_V3=y +* CONFIG_ARM_SMMU_V3_SVA=y +* CONFIG_PCI_PASID=y +* CONFIG_UACCE=y +* CONFIG_CRYPTO_DEV_HISI_QM=y +* CONFIG_CRYPTO_DEV_HISI_ZIP=y + +Make sure all these above kernel configurations are selected. + +Accelerator dev node permissions +-------------------------------- +Hardware accelerators (eg: HiSilicon Kunpeng Zip accelerator) gets registered to +UADK and char devices are created in dev directory. In order to access resources +on hardware accelerator devices, write permission should be provided to user. + +.. code-block:: shell + + $ sudo chmod 777 /dev/hisi_zip-* + +How To Use UADK Compression In QEMU Migration +--------------------------------------------- +* Make sure UADK is installed as above +* Build ``QEMU`` with ``--enable-uadk`` parameter + + E.g. configure --target-list=aarch64-softmmu --enable-kvm ``--enable-uadk`` + +* Enable ``UADK`` compression during migration + + Set ``migrate_set_parameter multifd-compression uadk`` + +Since UADK uses Shared Virtual Addressing(SVA) and device access virtual memory +directly it is possible that SMMUv3 may encounter page faults while walking the +IO page tables. This may impact the performance. In order to mitigate this, +please make sure to specify ``-mem-prealloc`` parameter to the destination VM +boot parameters. + +Though both UADK and ZLIB are based on the deflate compression algorithm, UADK +is not fully compatible with ZLIB. Hence, please make sure to use ``uadk`` on +both source and destination during migration. 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..d706c27ea7 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 @@ -204,15 +205,10 @@ DESIGN REQUIREMENTS: (Current solution) -We have updated cputlb.c to defer operations when a cross-vCPU -operation with async_run_on_cpu() which ensures each vCPU sees a -coherent state when it next runs its work (in a few instructions -time). - -A new set up operations (tlb_flush_*_all_cpus) take an additional flag -which when set will force synchronisation by setting the source vCPUs -work as "safe work" and exiting the cpu run loop. This ensure by the -time execution restarts all flush operations have completed. +A new set of tlb flush operations (tlb_flush_*_all_cpus_synced) force +synchronisation by setting the source vCPUs work as "safe work" and +exiting the cpu run loop. This ensures that by the time execution +restarts all flush operations have completed. TLB flag updates are all done atomically and are also protected by the corresponding page lock. @@ -226,10 +222,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.rst b/docs/devel/multiple-iothreads.rst new file mode 100644 index 0000000000..d1f3fc4510 --- /dev/null +++ b/docs/devel/multiple-iothreads.rst @@ -0,0 +1,139 @@ +Using Multiple ``IOThread``\ s +============================== + +.. + Copyright (c) 2014-2017 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. + + +This document explains the ``IOThread`` feature and how to write code that runs +outside the BQL. + +The main loop and ``IOThread``\ s +--------------------------------- +QEMU is an event-driven program that can do several things at once using an +event loop. The VNC server and the QMP monitor are both processed from the +same event loop, which monitors their file descriptors until they become +readable and then invokes a callback. + +The default event loop is called the main loop (see ``main-loop.c``). It is +possible to create additional event loop threads using +``-object iothread,id=my-iothread``. + +Side note: The main loop and ``IOThread`` are both event loops but their code is +not shared completely. Sometimes it is useful to remember that although they +are conceptually similar they are currently not interchangeable. + +Why ``IOThread``\ s are useful +------------------------------ +``IOThread``\ s allow the user to control the placement of work. The main loop is a +scalability bottleneck on hosts with many CPUs. Work can be spread across +several ``IOThread``\ s 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 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 +BQL is contended by all vCPU threads and the main loop explain +why it is desirable to place work into ``IOThread``\ s. + +The experimental ``virtio-blk`` data-plane implementation has been benchmarked and +shows these effects: +ftp://public.dhe.ibm.com/linux/pdfs/KVM_Virtualized_IO_Performance_Paper.pdf + +.. _how-to-program: + +How to program for ``IOThread``\ s +---------------------------------- +The main difference between legacy code and new code that can run in an +``IOThread`` is dealing explicitly with the event loop object, ``AioContext`` +(see ``include/block/aio.h``). Code that only works in the main loop +implicitly uses the main loop's ``AioContext``. Code that supports running +in ``IOThread``\ s must be aware of its ``AioContext``. + +AioContext supports the following services: + * File descriptor monitoring (read/write/error on POSIX hosts) + * Event notifiers (inter-thread signalling) + * Timers + * Bottom Halves (BH) deferred callbacks + +There are several old APIs that use the main loop AioContext: + * LEGACY ``qemu_aio_set_fd_handler()`` - monitor a file descriptor + * 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 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 ``IOThread``\ s or the main loop, depending on which ``AioContext`` +instance the caller passes in. + +How to synchronize with an ``IOThread`` +--------------------------------------- +Variables that can be accessed by multiple threads require some form of +synchronization such as ``qemu_mutex_lock()``, ``rcu_read_lock()``, etc. + +``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()``. + +The main loop thread can wait synchronously for a condition using +``AIO_WAIT_WHILE()``. + +``AioContext`` and the block layer +---------------------------------- +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_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. + +Block layer code must therefore expect to run in an ``IOThread`` and avoid using +old APIs that implicitly use the main loop. See +`How to program for IOThreads`_ for information on how to do that. + +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". + +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/multiple-iothreads.txt b/docs/devel/multiple-iothreads.txt deleted file mode 100644 index 343120f2ef..0000000000 --- a/docs/devel/multiple-iothreads.txt +++ /dev/null @@ -1,138 +0,0 @@ -Copyright (c) 2014-2017 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. - - -This document explains the IOThread feature and how to write code that runs -outside the QEMU global mutex. - -The main loop and IOThreads ---------------------------- -QEMU is an event-driven program that can do several things at once using an -event loop. The VNC server and the QMP monitor are both processed from the -same event loop, which monitors their file descriptors until they become -readable and then invokes a callback. - -The default event loop is called the main loop (see main-loop.c). It is -possible to create additional event loop threads using -object -iothread,id=my-iothread. - -Side note: The main loop and IOThread are both event loops but their code is -not shared completely. Sometimes it is useful to remember that although they -are conceptually similar they are currently not interchangeable. - -Why IOThreads are useful ------------------------- -IOThreads allow the user to control the placement of work. The main loop is a -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 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 -why it is desirable to place work into IOThreads. - -The experimental virtio-blk data-plane implementation has been benchmarked and -shows these effects: -ftp://public.dhe.ibm.com/linux/pdfs/KVM_Virtualized_IO_Performance_Paper.pdf - -How to program for IOThreads ----------------------------- -The main difference between legacy code and new code that can run in an -IOThread is dealing explicitly with the event loop object, AioContext -(see include/block/aio.h). Code that only works in the main loop -implicitly uses the main loop's AioContext. Code that supports running -in IOThreads must be aware of its AioContext. - -AioContext supports the following services: - * File descriptor monitoring (read/write/error on POSIX hosts) - * Event notifiers (inter-thread signalling) - * Timers - * Bottom Halves (BH) deferred callbacks - -There are several old APIs that use the main loop AioContext: - * LEGACY qemu_aio_set_fd_handler() - monitor a file descriptor - * 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_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. - -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_poll() - run an event loop iteration - -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 -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: - -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. - -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. - -AioContext and the block layer ------------------------------- -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_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. - -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. - -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_change_aio_context() moves a -BlockDriverState to a different AioContext. 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..583207a8ec 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' :: @@ -894,7 +899,7 @@ Documentation markup ~~~~~~~~~~~~~~~~~~~~ Documentation comments can use most rST markup. In particular, -a ``::`` literal block can be used for examples:: +a ``::`` literal block can be used for pre-formatted text:: # :: # @@ -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,73 +973,113 @@ 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: "Since:", "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 + # TODO: 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. + +"Errors" sections should be formatted as an rST list, each entry +detailing a relevant error condition. For example:: + + # Errors: + # - If @device does not exist, DeviceNotFound + # - Any other error returns a GenericError. + +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. +"TODO" sections are not rendered (they are for developers, not users of +QMP). In other sections, the text is formatted, and rST markup can be +used. + +QMP Examples can be added by using the ``.. qmp-example::`` +directive. In its simplest form, this can be used to contain a single +QMP code block which accepts standard JSON syntax with additional server +directionality indicators (``->`` and ``<-``), and elisions (``...``). + +Optionally, a plaintext title may be provided by using the ``:title:`` +directive option. If the title is omitted, the example title will +default to "Example:". + +A simple QMP example:: + + # .. qmp-example:: + # :title: Using query-block + # + # -> { "execute": "query-block" } + # <- { ... } + +More complex or multi-step examples where exposition is needed before or +between QMP code blocks can be created by using the ``:annotated:`` +directive option. When using this option, nested QMP code blocks must be +entered explicitly with rST's ``::`` syntax. + +Highlighting in non-QMP languages can be accomplished by using the +``.. code-block:: lang`` directive, and non-highlighted text can be +achieved by omitting the language argument. For example:: + # .. qmp-example:: + # :annotated: + # :title: A more complex demonstration + # + # This is a more complex example that can use + # ``arbitrary rST syntax`` in its exposition:: + # + # -> { "execute": "query-block" } + # <- { ... } + # + # Above, lengthy output has been omitted for brevity. + + +Examples of complete definition documentation:: + ## # @BlockStats: # # 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 +1090,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 ... - # } + # .. qmp-example:: # + # -> { "execute": "query-blockstats" } + # <- { + # ... + # } ## { '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 +1269,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 +1468,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 +1521,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 +1635,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 +1783,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 +1866,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 +2035,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 +2074,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 3e34b07c98..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 =============================================== @@ -322,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: @@ -344,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, }; @@ -359,13 +386,36 @@ 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 }, @@ -380,9 +430,32 @@ 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/rcu.rst b/docs/devel/rcu.rst new file mode 100644 index 0000000000..dd07c1d919 --- /dev/null +++ b/docs/devel/rcu.rst @@ -0,0 +1,394 @@ +Using RCU (Read-Copy-Update) for synchronization +================================================ + +Read-copy update (RCU) is a synchronization mechanism that is used to +protect read-mostly data structures. RCU is very efficient and scalable +on the read side (it is wait-free), and thus can make the read paths +extremely fast. + +RCU supports concurrency between a single writer and multiple readers, +thus it is not used alone. Typically, the write-side will use a lock to +serialize multiple updates, but other approaches are possible (e.g., +restricting updates to a single task). In QEMU, when a lock is used, +this will often be the "iothread mutex", also known as the "big QEMU +lock" (BQL). Also, restricting updates to a single task is done in +QEMU using the "bottom half" API. + +RCU is fundamentally a "wait-to-finish" mechanism. The read side marks +sections of code with "critical sections", and the update side will wait +for the execution of all *currently running* critical sections before +proceeding, or before asynchronously executing a callback. + +The key point here is that only the currently running critical sections +are waited for; critical sections that are started **after** the beginning +of the wait do not extend the wait, despite running concurrently with +the updater. This is the reason why RCU is more scalable than, +for example, reader-writer locks. It is so much more scalable that +the system will have a single instance of the RCU mechanism; a single +mechanism can be used for an arbitrary number of "things", without +having to worry about things such as contention or deadlocks. + +How is this possible? The basic idea is to split updates in two phases, +"removal" and "reclamation". During removal, we ensure that subsequent +readers will not be able to get a reference to the old data. After +removal has completed, a critical section will not be able to access +the old data. Therefore, critical sections that begin after removal +do not matter; as soon as all previous critical sections have finished, +there cannot be any readers who hold references to the data structure, +and these can now be safely reclaimed (e.g., freed or unref'ed). + +Here is a picture:: + + thread 1 thread 2 thread 3 + ------------------- ------------------------ ------------------- + enter RCU crit.sec. + | finish removal phase + | begin wait + | | enter RCU crit.sec. + exit RCU crit.sec | | + complete wait | + begin reclamation phase | + exit RCU crit.sec. + + +Note how thread 3 is still executing its critical section when thread 2 +starts reclaiming data. This is possible, because the old version of the +data structure was not accessible at the time thread 3 began executing +that critical section. + + +RCU API +------- + +The core RCU API is small: + +``void rcu_read_lock(void);`` + Used by a reader to inform the reclaimer that the reader is + entering an RCU read-side critical section. + +``void rcu_read_unlock(void);`` + Used by a reader to inform the reclaimer that the reader is + exiting an RCU read-side critical section. Note that RCU + read-side critical sections may be nested and/or overlapping. + +``void synchronize_rcu(void);`` + Blocks until all pre-existing RCU read-side critical sections + on all threads have completed. This marks the end of the removal + phase and the beginning of reclamation phase. + + Note that it would be valid for another update to come while + ``synchronize_rcu`` is running. Because of this, it is better that + the updater releases any locks it may hold before calling + ``synchronize_rcu``. If this is not possible (for example, because + the updater is protected by the BQL), you can use ``call_rcu``. + +``void call_rcu1(struct rcu_head * head, void (*func)(struct rcu_head *head));`` + This function invokes ``func(head)`` after all pre-existing RCU + read-side critical sections on all threads have completed. This + marks the end of the removal phase, with func taking care + asynchronously of the reclamation phase. + + The ``foo`` struct needs to have an ``rcu_head`` structure added, + perhaps as follows:: + + struct foo { + struct rcu_head rcu; + int a; + char b; + long c; + }; + + so that the reclaimer function can fetch the ``struct foo`` address + and free it:: + + call_rcu1(&foo.rcu, foo_reclaim); + + void foo_reclaim(struct rcu_head *rp) + { + struct foo *fp = container_of(rp, struct foo, rcu); + g_free(fp); + } + + ``call_rcu1`` is typically used via either the ``call_rcu`` or + ``g_free_rcu`` macros, which handle the common case where the + ``rcu_head`` member is the first of the struct. + +``void call_rcu(T *p, void (*func)(T *p), field-name);`` + If the ``struct rcu_head`` is the first field in the struct, you can + use this macro instead of ``call_rcu1``. + +``void g_free_rcu(T *p, field-name);`` + This is a special-case version of ``call_rcu`` where the callback + function is ``g_free``. + In the example given in ``call_rcu1``, one could have written simply:: + + g_free_rcu(&foo, rcu); + +``typeof(*p) qatomic_rcu_read(p);`` + ``qatomic_rcu_read()`` is similar to ``qatomic_load_acquire()``, but + it makes some assumptions on the code that calls it. This allows a + more optimized implementation. + + ``qatomic_rcu_read`` assumes that whenever a single RCU critical + section reads multiple shared data, these reads are either + data-dependent or need no ordering. This is almost always the + case when using RCU, because read-side critical sections typically + navigate one or more pointers (the pointers that are changed on + every update) until reaching a data structure of interest, + and then read from there. + + RCU read-side critical sections must use ``qatomic_rcu_read()`` to + read data, unless concurrent writes are prevented by another + synchronization mechanism. + + Furthermore, RCU read-side critical sections should traverse the + data structure in a single direction, opposite to the direction + in which the updater initializes it. + +``void qatomic_rcu_set(p, typeof(*p) v);`` + ``qatomic_rcu_set()`` is similar to ``qatomic_store_release()``, + though it also makes assumptions on the code that calls it in + order to allow a more optimized implementation. + + In particular, ``qatomic_rcu_set()`` suffices for synchronization + with readers, if the updater never mutates a field within a + data item that is already accessible to readers. This is the + case when initializing a new copy of the RCU-protected data + structure; just ensure that initialization of ``*p`` is carried out + before ``qatomic_rcu_set()`` makes the data item visible to readers. + If this rule is observed, writes will happen in the opposite + order as reads in the RCU read-side critical sections (or if + there is just one update), and there will be no need for other + synchronization mechanism to coordinate the accesses. + +The following APIs must be used before RCU is used in a thread: + +``void rcu_register_thread(void);`` + Mark a thread as taking part in the RCU mechanism. Such a thread + will have to report quiescent points regularly, either manually + or through the ``QemuCond``/``QemuSemaphore``/``QemuEvent`` APIs. + +``void rcu_unregister_thread(void);`` + Mark a thread as not taking part anymore in the RCU mechanism. + It is not a problem if such a thread reports quiescent points, + either manually or by using the + ``QemuCond``/``QemuSemaphore``/``QemuEvent`` APIs. + +Note that these APIs are relatively heavyweight, and should **not** be +nested. + +Convenience macros +------------------ + +Two macros are provided that automatically release the read lock at the +end of the scope. + +``RCU_READ_LOCK_GUARD()`` + Takes the lock and will release it at the end of the block it's + used in. + +``WITH_RCU_READ_LOCK_GUARD() { code }`` + Is used at the head of a block to protect the code within the block. + +Note that a ``goto`` out of the guarded block will also drop the lock. + +Differences with Linux +---------------------- + +- Waiting on a mutex is possible, though discouraged, within an RCU critical + section. This is because spinlocks are rarely (if ever) used in userspace + programming; not allowing this would prevent upgrading an RCU read-side + critical section to become an updater. + +- ``qatomic_rcu_read`` and ``qatomic_rcu_set`` replace ``rcu_dereference`` and + ``rcu_assign_pointer``. They take a **pointer** to the variable being accessed. + +- ``call_rcu`` is a macro that has an extra argument (the name of the first + field in the struct, which must be a struct ``rcu_head``), and expects the + type of the callback's argument to be the type of the first argument. + ``call_rcu1`` is the same as Linux's ``call_rcu``. + + +RCU Patterns +------------ + +Many patterns using read-writer locks translate directly to RCU, with +the advantages of higher scalability and deadlock immunity. + +In general, RCU can be used whenever it is possible to create a new +"version" of a data structure every time the updater runs. This may +sound like a very strict restriction, however: + +- the updater does not mean "everything that writes to a data structure", + but rather "everything that involves a reclamation step". See the + array example below + +- in some cases, creating a new version of a data structure may actually + be very cheap. For example, modifying the "next" pointer of a singly + linked list is effectively creating a new version of the list. + +Here are some frequently-used RCU idioms that are worth noting. + + +RCU list processing +^^^^^^^^^^^^^^^^^^^ + +TBD (not yet used in QEMU) + + +RCU reference counting +^^^^^^^^^^^^^^^^^^^^^^ + +Because grace periods are not allowed to complete while there is an RCU +read-side critical section in progress, the RCU read-side primitives +may be used as a restricted reference-counting mechanism. For example, +consider the following code fragment:: + + rcu_read_lock(); + p = qatomic_rcu_read(&foo); + /* do something with p. */ + rcu_read_unlock(); + +The RCU read-side critical section ensures that the value of ``p`` remains +valid until after the ``rcu_read_unlock()``. In some sense, it is acquiring +a reference to ``p`` that is later released when the critical section ends. +The write side looks simply like this (with appropriate locking):: + + qemu_mutex_lock(&foo_mutex); + old = foo; + qatomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + synchronize_rcu(); + free(old); + +If the processing cannot be done purely within the critical section, it +is possible to combine this idiom with a "real" reference count:: + + rcu_read_lock(); + p = qatomic_rcu_read(&foo); + foo_ref(p); + rcu_read_unlock(); + /* do something with p. */ + foo_unref(p); + +The write side can be like this:: + + qemu_mutex_lock(&foo_mutex); + old = foo; + qatomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + synchronize_rcu(); + foo_unref(old); + +or with ``call_rcu``:: + + qemu_mutex_lock(&foo_mutex); + old = foo; + qatomic_rcu_set(&foo, new); + qemu_mutex_unlock(&foo_mutex); + call_rcu(foo_unref, old, rcu); + +In both cases, the write side only performs removal. Reclamation +happens when the last reference to a ``foo`` object is dropped. +Using ``synchronize_rcu()`` is undesirably expensive, because the +last reference may be dropped on the read side. Hence you can +use ``call_rcu()`` instead:: + + foo_unref(struct foo *p) { + if (qatomic_fetch_dec(&p->refcount) == 1) { + call_rcu(foo_destroy, p, rcu); + } + } + + +Note that the same idioms would be possible with reader/writer +locks:: + + read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); + p = foo; p = foo; + /* do something with p. */ foo = new; + read_unlock(&foo_rwlock); free(p); + write_mutex_unlock(&foo_rwlock); + free(p); + + ------------------------------------------------------------------ + + read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); + p = foo; old = foo; + foo_ref(p); foo = new; + read_unlock(&foo_rwlock); foo_unref(old); + /* do something with p. */ write_mutex_unlock(&foo_rwlock); + read_lock(&foo_rwlock); + foo_unref(p); + read_unlock(&foo_rwlock); + +``foo_unref`` could use a mechanism such as bottom halves to move deallocation +out of the write-side critical section. + + +RCU resizable arrays +^^^^^^^^^^^^^^^^^^^^ + +Resizable arrays can be used with RCU. The expensive RCU synchronization +(or ``call_rcu``) only needs to take place when the array is resized. +The two items to take care of are: + +- ensuring that the old version of the array is available between removal + and reclamation; + +- avoiding mismatches in the read side between the array data and the + array size. + +The first problem is avoided simply by not using ``realloc``. Instead, +each resize will allocate a new array and copy the old data into it. +The second problem would arise if the size and the data pointers were +two members of a larger struct:: + + struct mystuff { + ... + int data_size; + int data_alloc; + T *data; + ... + }; + +Instead, we store the size of the array with the array itself:: + + struct arr { + int size; + int alloc; + T data[]; + }; + struct arr *global_array; + + read side: + rcu_read_lock(); + struct arr *array = qatomic_rcu_read(&global_array); + x = i < array->size ? array->data[i] : -1; + rcu_read_unlock(); + return x; + + write side (running under a lock): + if (global_array->size == global_array->alloc) { + /* Creating a new version. */ + new_array = g_malloc(sizeof(struct arr) + + global_array->alloc * 2 * sizeof(T)); + new_array->size = global_array->size; + new_array->alloc = global_array->alloc * 2; + memcpy(new_array->data, global_array->data, + global_array->alloc * sizeof(T)); + + /* Removal phase. */ + old_array = global_array; + qatomic_rcu_set(&global_array, new_array); + synchronize_rcu(); + + /* Reclamation phase. */ + free(old_array); + } + + +References +---------- + +* The `Linux kernel RCU documentation `__ diff --git a/docs/devel/rcu.txt b/docs/devel/rcu.txt deleted file mode 100644 index 2e6cc607a1..0000000000 --- a/docs/devel/rcu.txt +++ /dev/null @@ -1,406 +0,0 @@ -Using RCU (Read-Copy-Update) for synchronization -================================================ - -Read-copy update (RCU) is a synchronization mechanism that is used to -protect read-mostly data structures. RCU is very efficient and scalable -on the read side (it is wait-free), and thus can make the read paths -extremely fast. - -RCU supports concurrency between a single writer and multiple readers, -thus it is not used alone. Typically, the write-side will use a lock to -serialize multiple updates, but other approaches are possible (e.g., -restricting updates to a single task). In QEMU, when a lock is used, -this will often be the "iothread mutex", also known as the "big QEMU -lock" (BQL). Also, restricting updates to a single task is done in -QEMU using the "bottom half" API. - -RCU is fundamentally a "wait-to-finish" mechanism. The read side marks -sections of code with "critical sections", and the update side will wait -for the execution of all *currently running* critical sections before -proceeding, or before asynchronously executing a callback. - -The key point here is that only the currently running critical sections -are waited for; critical sections that are started _after_ the beginning -of the wait do not extend the wait, despite running concurrently with -the updater. This is the reason why RCU is more scalable than, -for example, reader-writer locks. It is so much more scalable that -the system will have a single instance of the RCU mechanism; a single -mechanism can be used for an arbitrary number of "things", without -having to worry about things such as contention or deadlocks. - -How is this possible? The basic idea is to split updates in two phases, -"removal" and "reclamation". During removal, we ensure that subsequent -readers will not be able to get a reference to the old data. After -removal has completed, a critical section will not be able to access -the old data. Therefore, critical sections that begin after removal -do not matter; as soon as all previous critical sections have finished, -there cannot be any readers who hold references to the data structure, -and these can now be safely reclaimed (e.g., freed or unref'ed). - -Here is a picture: - - thread 1 thread 2 thread 3 - ------------------- ------------------------ ------------------- - enter RCU crit.sec. - | finish removal phase - | begin wait - | | enter RCU crit.sec. - exit RCU crit.sec | | - complete wait | - begin reclamation phase | - exit RCU crit.sec. - - -Note how thread 3 is still executing its critical section when thread 2 -starts reclaiming data. This is possible, because the old version of the -data structure was not accessible at the time thread 3 began executing -that critical section. - - -RCU API -======= - -The core RCU API is small: - - void rcu_read_lock(void); - - Used by a reader to inform the reclaimer that the reader is - entering an RCU read-side critical section. - - void rcu_read_unlock(void); - - Used by a reader to inform the reclaimer that the reader is - exiting an RCU read-side critical section. Note that RCU - read-side critical sections may be nested and/or overlapping. - - void synchronize_rcu(void); - - Blocks until all pre-existing RCU read-side critical sections - on all threads have completed. This marks the end of the removal - phase and the beginning of reclamation phase. - - Note that it would be valid for another update to come while - synchronize_rcu is running. Because of this, it is better that - the updater releases any locks it may hold before calling - synchronize_rcu. If this is not possible (for example, because - the updater is protected by the BQL), you can use call_rcu. - - void call_rcu1(struct rcu_head * head, - void (*func)(struct rcu_head *head)); - - This function invokes func(head) after all pre-existing RCU - read-side critical sections on all threads have completed. This - marks the end of the removal phase, with func taking care - asynchronously of the reclamation phase. - - The foo struct needs to have an rcu_head structure added, - perhaps as follows: - - struct foo { - struct rcu_head rcu; - int a; - char b; - long c; - }; - - so that the reclaimer function can fetch the struct foo address - and free it: - - call_rcu1(&foo.rcu, foo_reclaim); - - void foo_reclaim(struct rcu_head *rp) - { - struct foo *fp = container_of(rp, struct foo, rcu); - g_free(fp); - } - - For the common case where the rcu_head member is the first of the - struct, you can use the following macro. - - void call_rcu(T *p, - void (*func)(T *p), - field-name); - void g_free_rcu(T *p, - field-name); - - call_rcu1 is typically used through these macro, in the common case - where the "struct rcu_head" is the first field in the struct. If - the callback function is g_free, in particular, g_free_rcu can be - used. In the above case, one could have written simply: - - g_free_rcu(&foo, rcu); - - typeof(*p) qatomic_rcu_read(p); - - qatomic_rcu_read() is similar to qatomic_load_acquire(), but it makes - some assumptions on the code that calls it. This allows a more - optimized implementation. - - qatomic_rcu_read assumes that whenever a single RCU critical - section reads multiple shared data, these reads are either - data-dependent or need no ordering. This is almost always the - case when using RCU, because read-side critical sections typically - navigate one or more pointers (the pointers that are changed on - every update) until reaching a data structure of interest, - and then read from there. - - RCU read-side critical sections must use qatomic_rcu_read() to - read data, unless concurrent writes are prevented by another - synchronization mechanism. - - Furthermore, RCU read-side critical sections should traverse the - data structure in a single direction, opposite to the direction - in which the updater initializes it. - - void qatomic_rcu_set(p, typeof(*p) v); - - qatomic_rcu_set() is similar to qatomic_store_release(), though it also - makes assumptions on the code that calls it in order to allow a more - optimized implementation. - - In particular, qatomic_rcu_set() suffices for synchronization - with readers, if the updater never mutates a field within a - data item that is already accessible to readers. This is the - case when initializing a new copy of the RCU-protected data - structure; just ensure that initialization of *p is carried out - before qatomic_rcu_set() makes the data item visible to readers. - If this rule is observed, writes will happen in the opposite - order as reads in the RCU read-side critical sections (or if - there is just one update), and there will be no need for other - synchronization mechanism to coordinate the accesses. - -The following APIs must be used before RCU is used in a thread: - - void rcu_register_thread(void); - - Mark a thread as taking part in the RCU mechanism. Such a thread - will have to report quiescent points regularly, either manually - or through the QemuCond/QemuSemaphore/QemuEvent APIs. - - void rcu_unregister_thread(void); - - Mark a thread as not taking part anymore in the RCU mechanism. - It is not a problem if such a thread reports quiescent points, - either manually or by using the QemuCond/QemuSemaphore/QemuEvent - APIs. - -Note that these APIs are relatively heavyweight, and should _not_ be -nested. - -Convenience macros -================== - -Two macros are provided that automatically release the read lock at the -end of the scope. - - RCU_READ_LOCK_GUARD() - - Takes the lock and will release it at the end of the block it's - used in. - - WITH_RCU_READ_LOCK_GUARD() { code } - - Is used at the head of a block to protect the code within the block. - -Note that 'goto'ing out of the guarded block will also drop the lock. - -DIFFERENCES WITH LINUX -====================== - -- Waiting on a mutex is possible, though discouraged, within an RCU critical - section. This is because spinlocks are rarely (if ever) used in userspace - programming; not allowing this would prevent upgrading an RCU read-side - critical section to become an updater. - -- qatomic_rcu_read and qatomic_rcu_set replace rcu_dereference and - rcu_assign_pointer. They take a _pointer_ to the variable being accessed. - -- call_rcu is a macro that has an extra argument (the name of the first - field in the struct, which must be a struct rcu_head), and expects the - type of the callback's argument to be the type of the first argument. - call_rcu1 is the same as Linux's call_rcu. - - -RCU PATTERNS -============ - -Many patterns using read-writer locks translate directly to RCU, with -the advantages of higher scalability and deadlock immunity. - -In general, RCU can be used whenever it is possible to create a new -"version" of a data structure every time the updater runs. This may -sound like a very strict restriction, however: - -- the updater does not mean "everything that writes to a data structure", - but rather "everything that involves a reclamation step". See the - array example below - -- in some cases, creating a new version of a data structure may actually - be very cheap. For example, modifying the "next" pointer of a singly - linked list is effectively creating a new version of the list. - -Here are some frequently-used RCU idioms that are worth noting. - - -RCU list processing -------------------- - -TBD (not yet used in QEMU) - - -RCU reference counting ----------------------- - -Because grace periods are not allowed to complete while there is an RCU -read-side critical section in progress, the RCU read-side primitives -may be used as a restricted reference-counting mechanism. For example, -consider the following code fragment: - - rcu_read_lock(); - p = qatomic_rcu_read(&foo); - /* do something with p. */ - rcu_read_unlock(); - -The RCU read-side critical section ensures that the value of "p" remains -valid until after the rcu_read_unlock(). In some sense, it is acquiring -a reference to p that is later released when the critical section ends. -The write side looks simply like this (with appropriate locking): - - qemu_mutex_lock(&foo_mutex); - old = foo; - qatomic_rcu_set(&foo, new); - qemu_mutex_unlock(&foo_mutex); - synchronize_rcu(); - free(old); - -If the processing cannot be done purely within the critical section, it -is possible to combine this idiom with a "real" reference count: - - rcu_read_lock(); - p = qatomic_rcu_read(&foo); - foo_ref(p); - rcu_read_unlock(); - /* do something with p. */ - foo_unref(p); - -The write side can be like this: - - qemu_mutex_lock(&foo_mutex); - old = foo; - qatomic_rcu_set(&foo, new); - qemu_mutex_unlock(&foo_mutex); - synchronize_rcu(); - foo_unref(old); - -or with call_rcu: - - qemu_mutex_lock(&foo_mutex); - old = foo; - qatomic_rcu_set(&foo, new); - qemu_mutex_unlock(&foo_mutex); - call_rcu(foo_unref, old, rcu); - -In both cases, the write side only performs removal. Reclamation -happens when the last reference to a "foo" object is dropped. -Using synchronize_rcu() is undesirably expensive, because the -last reference may be dropped on the read side. Hence you can -use call_rcu() instead: - - foo_unref(struct foo *p) { - if (qatomic_fetch_dec(&p->refcount) == 1) { - call_rcu(foo_destroy, p, rcu); - } - } - - -Note that the same idioms would be possible with reader/writer -locks: - - read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); - p = foo; p = foo; - /* do something with p. */ foo = new; - read_unlock(&foo_rwlock); free(p); - write_mutex_unlock(&foo_rwlock); - free(p); - - ------------------------------------------------------------------ - - read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock); - p = foo; old = foo; - foo_ref(p); foo = new; - read_unlock(&foo_rwlock); foo_unref(old); - /* do something with p. */ write_mutex_unlock(&foo_rwlock); - read_lock(&foo_rwlock); - foo_unref(p); - read_unlock(&foo_rwlock); - -foo_unref could use a mechanism such as bottom halves to move deallocation -out of the write-side critical section. - - -RCU resizable arrays --------------------- - -Resizable arrays can be used with RCU. The expensive RCU synchronization -(or call_rcu) only needs to take place when the array is resized. -The two items to take care of are: - -- ensuring that the old version of the array is available between removal - and reclamation; - -- avoiding mismatches in the read side between the array data and the - array size. - -The first problem is avoided simply by not using realloc. Instead, -each resize will allocate a new array and copy the old data into it. -The second problem would arise if the size and the data pointers were -two members of a larger struct: - - struct mystuff { - ... - int data_size; - int data_alloc; - T *data; - ... - }; - -Instead, we store the size of the array with the array itself: - - struct arr { - int size; - int alloc; - T data[]; - }; - struct arr *global_array; - - read side: - rcu_read_lock(); - struct arr *array = qatomic_rcu_read(&global_array); - x = i < array->size ? array->data[i] : -1; - rcu_read_unlock(); - return x; - - write side (running under a lock): - if (global_array->size == global_array->alloc) { - /* Creating a new version. */ - new_array = g_malloc(sizeof(struct arr) + - global_array->alloc * 2 * sizeof(T)); - new_array->size = global_array->size; - new_array->alloc = global_array->alloc * 2; - memcpy(new_array->data, global_array->data, - global_array->alloc * sizeof(T)); - - /* Removal phase. */ - old_array = global_array; - qatomic_rcu_set(&global_array, new_array); - synchronize_rcu(); - - /* Reclamation phase. */ - free(old_array); - } - - -SOURCES -======= - -* Documentation/RCU/ from the Linux kernel diff --git a/docs/devel/replay.rst b/docs/devel/replay.rst index 0244be8b9c..40f58d9d4f 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). @@ -202,6 +202,9 @@ into the log. Saving/restoring the VM state ----------------------------- +Record/replay relies on VM state save and restore being complete and +deterministic. + All fields in the device state structure (including virtual timers) should be restored by loadvm to the same values they had before savevm. diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index 7cc6a6b314..adefd59ef9 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 @@ -27,9 +27,7 @@ instantly reset an object, without keeping it in reset state, just call ``resettable_reset()``. These functions take two parameters: a pointer to the object to reset and a reset type. -Several types of reset will be supported. For now only cold reset is defined; -others may be added later. The Resettable interface handles reset types with an -enum: +The Resettable interface handles reset types with an enum ``ResetType``: ``RESET_TYPE_COLD`` Cold reset is supported by every resettable object. In QEMU, it means we reset @@ -37,6 +35,39 @@ enum: from what is a real hardware cold reset. It differs from other resets (like warm or bus resets) which may keep certain parts untouched. +``RESET_TYPE_SNAPSHOT_LOAD`` + This is called for a reset which is being done to put the system into a + clean state prior to loading a snapshot. (This corresponds to a reset + with ``SHUTDOWN_CAUSE_SNAPSHOT_LOAD``.) Almost all devices should treat + this the same as ``RESET_TYPE_COLD``. The main exception is devices which + have some non-deterministic state they want to reinitialize to a different + value on each cold reset, such as RNG seed information, and which they + must not reinitialize on a snapshot-load reset. + +``RESET_TYPE_WAKEUP`` + If the machine supports waking up from a suspended state and needs to reset + its devices during wake-up (from the ``MachineClass::wakeup()`` method), this + reset type should be used for such a request. Devices can utilize this reset + type to differentiate the reset requested during machine wake-up from other + reset requests. For example, RAM content must not be lost during wake-up, and + memory devices like virtio-mem that provide additional RAM must not reset + such state during wake-ups, but might do so during cold resets. However, this + reset type should not be used for wake-up detection, as not every machine + type issues a device reset request during wake-up. + +``RESET_TYPE_S390_CPU_NORMAL`` + This is only used for S390 CPU objects; it clears interrupts, stops + processing, and clears the TLB, but does not touch register contents. + +``RESET_TYPE_S390_CPU_INITIAL`` + This is only used for S390 CPU objects; it does everything + ``RESET_TYPE_S390_CPU_NORMAL`` does and also clears the PSW, prefix, + FPC, timer and control registers. It does not touch gprs, fprs or acrs. + +Devices which implement reset methods must treat any unknown ``ResetType`` +as equivalent to ``RESET_TYPE_COLD``; this will reduce the amount of +existing code we need to change if we add more types in future. + Calling ``resettable_reset()`` is equivalent to calling ``resettable_assert_reset()`` then ``resettable_release_reset()``. It is possible to interleave multiple calls to these three functions. There may @@ -150,25 +181,25 @@ in reset. mydev->var = 0; } - static void mydev_reset_hold(Object *obj) + static void mydev_reset_hold(Object *obj, ResetType type) { MyDevClass *myclass = MYDEV_GET_CLASS(obj); MyDevState *mydev = MYDEV(obj); /* call parent class hold phase */ if (myclass->parent_phases.hold) { - myclass->parent_phases.hold(obj); + myclass->parent_phases.hold(obj, type); } /* set an IO */ qemu_set_irq(mydev->irq, 1); } - static void mydev_reset_exit(Object *obj) + static void mydev_reset_exit(Object *obj, ResetType type) { MyDevClass *myclass = MYDEV_GET_CLASS(obj); MyDevState *mydev = MYDEV(obj); /* call parent class exit phase */ if (myclass->parent_phases.exit) { - myclass->parent_phases.exit(obj); + myclass->parent_phases.exit(obj, type); } /* clear an IO */ qemu_set_irq(mydev->irq, 0); @@ -184,21 +215,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 @@ -256,8 +286,8 @@ every reset child of the given resettable object. All children must be resettable too. Additional parameters (a reset type and an opaque pointer) must be passed to the callback too. -In ``DeviceClass`` and ``BusClass`` the ``ResettableState`` is located -``DeviceState`` and ``BusState`` structure. ``child_foreach()`` is implemented +In ``DeviceClass`` and ``BusClass`` the ``ResettableState`` is located in the +``DeviceState`` and ``BusState`` structures. ``child_foreach()`` is implemented to follow the bus hierarchy; for a bus, it calls the function on every child device; for a device, it calls the function on every bus child. When we reset the main system bus, we reset the whole machine bus tree. @@ -289,3 +319,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 c641d948f1..10b062eec2 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -18,7 +18,7 @@ one-shot fix, the bare minimum we ask is that: * - Check - Reason - * - Patches contain Signed-off-by: Real Name + * - Patches contain Signed-off-by: Your 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` @@ -177,7 +177,7 @@ add an additional line with "Fixes: If your patch fixes a bug in the gitlab bug tracker, please add a line with "Resolves: " to the commit message, too. Gitlab can -close bugs automatically once commits with the "Resolved:" keyword get +close bugs automatically once commits with the "Resolves:" keyword get merged into the master branch of the project. And if your patch addresses a bug in another public bug tracker, you can also use a line with "Buglink: " for reference here, too. @@ -335,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. +The name used with "Signed-off-by" does not need to be your legal name, +nor birth name, nor appear on any government ID. It is the identity you +choose to be known by in the community, but should not be anonymous, +nor misrepresent whom you are. + 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 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 9740a70406..9463692c41 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -3,41 +3,11 @@ Copyright (c) 2019, Linaro Limited Written by Emilio Cota and Alex Bennée +.. _TCG Plugins: + QEMU TCG Plugins ================ -QEMU TCG plugins provide a way for users to run experiments taking -advantage of the total system control emulation can have over a guest. -It provides a mechanism for plugins to subscribe to events during -translation and execution and optionally callback into the plugin -during these events. TCG plugins are unable to change the system state -only monitor it passively. However they can do this down to an -individual instruction granularity including potentially subscribing -to all load and store operations. - -Usage ------ - -Any QEMU binary with TCG support has plugins enabled by default. -Earlier releases needed to be explicitly enabled with:: - - configure --enable-plugins - -Once built a program can be run with multiple plugins loaded each with -their own arguments:: - - $QEMU $OTHER_QEMU_ARGS \ - -plugin contrib/plugin/libhowvec.so,inline=on,count=hint \ - -plugin contrib/plugin/libhotblocks.so - -Arguments are plugin specific and can be used to modify their -behaviour. In this case the howvec plugin is being asked to use inline -ops to count and break down the hint instructions by type. - -Linux user-mode emulation also evaluates the environment variable -``QEMU_PLUGIN``:: - - QEMU_PLUGIN="file=contrib/plugins/libhowvec.so,inline=on,count=hint" $QEMU Writing plugins --------------- @@ -91,11 +61,14 @@ translation event the plugin has an option to enumerate the instructions in a block of instructions and optionally register callbacks to some or all instructions when they are executed. -There is also a facility to add an inline event where code to -increment a counter can be directly inlined with the translation. -Currently only a simple increment is supported. This is not atomic so -can miss counts. If you want absolute precision you should use a -callback which can then ensure atomicity itself. +There is also a facility to add inline instructions doing various operations, +like adding or storing an immediate value. It is also possible to execute a +callback conditionally, with condition being evaluated inline. All those inline +operations are associated to a ``scoreboard``, which is a thread-local storage +automatically expanded when new cores/threads are created and that can be +accessed/modified in a thread-safe way without any lock needed. Combining inline +operations and conditional callbacks offer a more efficient way to instrument +binaries, compared to classic callbacks. Finally when QEMU exits all the registered *atexit* callbacks are invoked. @@ -110,6 +83,55 @@ 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. +However the following assumptions can be made: + +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 --------- @@ -140,445 +162,11 @@ which means callbacks may still occur after the uninstall operation is 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. 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/empty.c - -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 - -The hotblocks plugin allows you to examine the where hot paths of -execution are in your program. Once the program has finished you will -get a sorted list of blocks reporting the starting PC, translation -count, number of instructions and execution count. This will work best -with linux-user execution as system emulation tends to generate -re-translations as blocks from different programs get swapped in and -out of system memory. - -If your program is single-threaded you can use the ``inline`` option for -slightly faster (but not thread safe) counters. - -Example:: - - $ qemu-aarch64 \ - -plugin contrib/plugins/libhotblocks.so -d plugin \ - ./tests/tcg/aarch64-linux-user/sha1 - SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 - collected 903 entries in the hash table - pc, tcount, icount, ecount - 0x0000000041ed10, 1, 5, 66087 - 0x000000004002b0, 1, 4, 66087 - ... - -- contrib/plugins/hotpages.c - -Similar to hotblocks but this time tracks memory accesses:: - - $ qemu-aarch64 \ - -plugin contrib/plugins/libhotpages.so -d plugin \ - ./tests/tcg/aarch64-linux-user/sha1 - SHA1=15dd99a1991e0b3826fede3deffc1feba42278e6 - Addr, RCPUs, Reads, WCPUs, Writes - 0x000055007fe000, 0x0001, 31747952, 0x0001, 8835161 - 0x000055007ff000, 0x0001, 29001054, 0x0001, 8780625 - 0x00005500800000, 0x0001, 687465, 0x0001, 335857 - 0x0000000048b000, 0x0001, 130594, 0x0001, 355 - 0x0000000048a000, 0x0001, 1826, 0x0001, 11 - -The hotpages plugin can be configured using the following arguments: - - * sortby=reads|writes|address - - Log the data sorted by either the number of reads, the number of writes, or - memory address. (Default: entries are sorted by the sum of reads and writes) - - * io=on - - Track IO addresses. Only relevant to full system emulation. (Default: off) - - * pagesize=N - - The page size used. (Default: N = 4096) - -- contrib/plugins/howvec.c - -This is an instruction classifier so can be used to count different -types of instructions. It has a number of options to refine which get -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:: - - $ qemu-system-aarch64 $(QEMU_ARGS) \ - -append "root=/dev/sda2 systemd.unit=benchmark.service" \ - -smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin - -which will lead to a sorted list after the class breakdown:: - - Instruction Classes: - Class: UDEF not counted - Class: SVE (68 hits) - Class: PCrel addr (47789483 hits) - Class: Add/Sub (imm) (192817388 hits) - Class: Logical (imm) (93852565 hits) - Class: Move Wide (imm) (76398116 hits) - Class: Bitfield (44706084 hits) - Class: Extract (5499257 hits) - Class: Cond Branch (imm) (147202932 hits) - Class: Exception Gen (193581 hits) - Class: NOP not counted - Class: Hints (6652291 hits) - Class: Barriers (8001661 hits) - Class: PSTATE (1801695 hits) - Class: System Insn (6385349 hits) - Class: System Reg counted individually - Class: Branch (reg) (69497127 hits) - Class: Branch (imm) (84393665 hits) - Class: Cmp & Branch (110929659 hits) - Class: Tst & Branch (44681442 hits) - Class: AdvSimd ldstmult (736 hits) - Class: ldst excl (9098783 hits) - Class: Load Reg (lit) (87189424 hits) - Class: ldst noalloc pair (3264433 hits) - Class: ldst pair (412526434 hits) - Class: ldst reg (imm) (314734576 hits) - Class: Loads & Stores (2117774 hits) - Class: Data Proc Reg (223519077 hits) - Class: Scalar FP (31657954 hits) - Individual Instructions: - Instr: mrs x0, sp_el0 (2682661 hits) (op=0xd5384100/ System Reg) - Instr: mrs x1, tpidr_el2 (1789339 hits) (op=0xd53cd041/ System Reg) - Instr: mrs x2, tpidr_el2 (1513494 hits) (op=0xd53cd042/ System Reg) - Instr: mrs x0, tpidr_el2 (1490823 hits) (op=0xd53cd040/ System Reg) - Instr: mrs x1, sp_el0 (933793 hits) (op=0xd5384101/ System Reg) - Instr: mrs x2, sp_el0 (699516 hits) (op=0xd5384102/ System Reg) - Instr: mrs x4, tpidr_el2 (528437 hits) (op=0xd53cd044/ System Reg) - Instr: mrs x30, ttbr1_el1 (480776 hits) (op=0xd538203e/ System Reg) - Instr: msr ttbr1_el1, x30 (480713 hits) (op=0xd518203e/ System Reg) - Instr: msr vbar_el1, x30 (480671 hits) (op=0xd518c01e/ System Reg) - ... - -To find the argument shorthand for the class you need to examine the -source code of the plugin at the moment, specifically the ``*opt`` -argument in the InsnClassExecCount tables. - -- contrib/plugins/lockstep.c - -This is a debugging tool for developers who want to find out when and -where execution diverges after a subtle change to TCG code generation. -It is not an exact science and results are likely to be mixed once -asynchronous events are introduced. While the use of -icount can -introduce determinism to the execution flow it doesn't always follow -the translation sequence will be exactly the same. Typically this is -caused by a timer firing to service the GUI causing a block to end -early. However in some cases it has proved to be useful in pointing -people at roughly where execution diverges. The only argument you need -for the plugin is a path for the socket the two instances will -communicate over:: - - - $ 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 - -which will eventually report:: - - qemu-system-sparc: warning: nic lance.0 has no peer - @ 0x000000ffd06678 vs 0x000000ffd001e0 (2/1 since last) - @ 0x000000ffd07d9c vs 0x000000ffd06678 (3/1 since last) - Δ insn_count @ 0x000000ffd07d9c (809900609) vs 0x000000ffd06678 (809900612) - previously @ 0x000000ffd06678/10 (809900609 insns) - previously @ 0x000000ffd001e0/4 (809900599 insns) - previously @ 0x000000ffd080ac/2 (809900595 insns) - previously @ 0x000000ffd08098/5 (809900593 insns) - previously @ 0x000000ffd080c0/1 (809900588 insns) - -- contrib/plugins/hwprofile.c - -The hwprofile tool can only be used with system emulation and allows -the user to see what hardware is accessed how often. It has a number of options: - - * track=read or track=write - - By default the plugin tracks both reads and writes. You can use one - of these options to limit the tracking to just one class of accesses. - - * source - - Will include a detailed break down of what the guest PC that made the - access was. Not compatible with the pattern option. Example output:: - - cirrus-low-memory @ 0xfffffd00000a0000 - pc:fffffc0000005cdc, 1, 256 - pc:fffffc0000005ce8, 1, 256 - pc:fffffc0000005cec, 1, 256 - - * pattern - - Instead break down the accesses based on the offset into the HW - region. This can be useful for seeing the most used registers of a - device. Example output:: - - pci0-conf @ 0xfffffd01fe000000 - off:00000004, 1, 1 - off:00000010, 1, 3 - off:00000014, 1, 3 - off:00000018, 1, 2 - off:0000001c, 1, 2 - off:00000020, 1, 2 - ... - -- contrib/plugins/execlog.c - -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 needs default argument:: - - $ qemu-system-arm $(QEMU_ARGS) \ - -plugin ./contrib/plugins/libexeclog.so -d plugin - -which will output an execution trace following this structure:: - - # vCPU, vAddr, opcode, disassembly[, load/store, memory addr, device]... - 0, 0xa12, 0xf8012400, "movs r4, #0" - 0, 0xa14, 0xf87f42b4, "cmp r4, r6" - 0, 0xa16, 0xd206, "bhs #0xa26" - 0, 0xa18, 0xfff94803, "ldr r0, [pc, #0xc]", load, 0x00010a28, RAM - 0, 0xa1a, 0xf989f000, "bl #0xd30" - 0, 0xd30, 0xfff9b510, "push {r4, lr}", store, 0x20003ee0, RAM, store, 0x20003ee4, RAM - 0, 0xd32, 0xf9893014, "adds r0, #0x14" - 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 - -- 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 \ - -d plugin -D cache.log ./tests/tcg/x86_64-linux-user/float_convs - -will report the following:: - - core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate - 0 996695 508 0.0510% 2642799 18617 0.7044% - - address, data misses, instruction - 0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx) - 0x41f395 (_IO_default_xsputn), 49, movb %dl, (%rdi, %rax) - 0x42584d (ptmalloc_init.part.0), 33, movaps %xmm0, (%rax) - 0x454d48 (__tunables_init), 20, cmpb $0, (%r8) - ... - - address, fetch misses, instruction - 0x4160a0 (__vfprintf_internal), 744, movl $1, %ebx - 0x41f0a0 (_IO_setb), 744, endbr64 - 0x415882 (__vfprintf_internal), 744, movq %r12, %rdi - 0x4268a0 (__malloc), 696, andq $0xfffffffffffffff0, %rax - ... - -The plugin has a number of arguments, all of them are optional: - - * limit=N - - Print top N icache and dcache thrashing instructions along with their - address, number of misses, and its disassembly. (default: 32) - - * icachesize=N - * iblksize=B - * iassoc=A - - Instruction cache configuration arguments. They specify the cache size, block - size, and associativity of the instruction cache, respectively. - (default: N = 16384, B = 64, A = 8) - - * dcachesize=N - * dblksize=B - * dassoc=A - - Data cache configuration arguments. They specify the cache size, block size, - and associativity of the data cache, respectively. - (default: N = 16384, B = 64, A = 8) - - * evict=POLICY - - Sets the eviction policy to POLICY. Available policies are: :code:`lru`, - :code:`fifo`, and :code:`rand`. The plugin will use the specified policy for - both instruction and data caches. (default: POLICY = :code:`lru`) - - * cores=N - - Sets the number of cores for which we maintain separate icache and dcache. - (default: for linux-user, N = 1, for full system emulation: N = cores - available to guest) - - * l2=on - - Simulates a unified L2 cache (stores blocks for both instructions and data) - using the default L2 configuration (cache size = 2MB, associativity = 16-way, - block size = 64B). - - * l2cachesize=N - * l2blksize=B - * l2assoc=A - - L2 cache configuration arguments. They specify the cache size, block size, and - associativity of the L2 cache, respectively. Setting any of the L2 - configuration arguments implies ``l2=on``. - (default: N = 2097152 (2MB), B = 64, A = 16) - -API ---- +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 deleted file mode 100644 index e10c47b5a7..0000000000 --- a/docs/devel/testing.rst +++ /dev/null @@ -1,1477 +0,0 @@ -.. _testing: - -Testing in QEMU -=============== - -This document describes the testing infrastructure in QEMU. - -Testing with "make check" -------------------------- - -The "make check" testing family includes most of the C based tests in QEMU. For -a quick help, run ``make check-help`` from the source tree. - -The usual way to run these tests is: - -.. code:: - - make check - -which includes QAPI schema tests, unit tests, QTests and some iotests. -Different sub-types of "make check" tests will be explained below. - -Before running tests, it is best to build QEMU programs first. Some tests -expect the executables to exist and will fail with obscure messages if they -cannot find them. - -Unit tests -~~~~~~~~~~ - -Unit tests, which can be invoked with ``make check-unit``, are simple C tests -that typically link to individual QEMU object files and exercise them by -calling exported functions. - -If you are writing new code in QEMU, consider adding a unit test, especially -for utility modules that are relatively stateless or have few dependencies. To -add a new unit test: - -1. Create a new source file. For example, ``tests/unit/foo-test.c``. - -2. Write the test. Normally you would include the header file which exports - the module API, then verify the interface behaves as expected from your - test. The test code should be organized with the glib testing framework. - Copying and modifying an existing test is usually a good idea. - -3. Add the test to ``tests/unit/meson.build``. The unit tests are listed in a - dictionary called ``tests``. The values are any additional sources and - dependencies to be linked with the test. For a simple test whose source - is in ``tests/unit/foo-test.c``, it is enough to add an entry like:: - - { - ... - 'foo-test': [], - ... - } - -Since unit tests don't require environment variables, the simplest way to debug -a unit test failure is often directly invoking it or even running it under -``gdb``. However there can still be differences in behavior between ``make`` -invocations and your manual run, due to ``$MALLOC_PERTURB_`` environment -variable (which affects memory reclamation and catches invalid pointers better) -and gtester options. If necessary, you can run - -.. code:: - - make check-unit V=1 - -and copy the actual command line which executes the unit test, then run -it from the command line. - -QTest -~~~~~ - -QTest is a device emulation testing framework. It can be very useful to test -device models; it could also control certain aspects of QEMU (such as virtual -clock stepping), with a special purpose "qtest" protocol. Refer to -:doc:`qtest` for more details. - -QTest cases can be executed with - -.. code:: - - 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 -~~~~~~~~~~~~~~~~~ - -The QAPI schema tests validate the QAPI parser used by QMP, by feeding -predefined input to the parser and comparing the result with the reference -output. - -The input/output data is managed under the ``tests/qapi-schema`` directory. -Each test case includes four files that have a common base name: - - * ``${casename}.json`` - the file contains the JSON input for feeding the - parser - * ``${casename}.out`` - the file contains the expected stdout from the parser - * ``${casename}.err`` - the file contains the expected stderr from the parser - * ``${casename}.exit`` - the expected error code - -Consider adding a new QAPI schema test when you are making a change on the QAPI -parser (either fixing a bug or extending/modifying the syntax). To do this: - -1. Add four files for the new case as explained above. For example: - - ``$EDITOR tests/qapi-schema/foo.{json,out,err,exit}``. - -2. Add the new test in ``tests/Makefile.include``. For example: - - ``qapi-schema += foo.json`` - -check-block -~~~~~~~~~~~ - -``make check-block`` runs a subset of the block layer iotests (the tests that -are in the "auto" group). -See the "QEMU iotests" section below for more information. - -QEMU iotests ------------- - -QEMU iotests, under the directory ``tests/qemu-iotests``, is the testing -framework widely used to test block layer related features. It is higher level -than "make check" tests and 99% of the code is written in bash or Python -scripts. The testing success criteria is golden output comparison, and the -test files are named with numbers. - -To run iotests, make sure QEMU is built successfully, then switch to the -``tests/qemu-iotests`` directory under the build directory, and run ``./check`` -with desired arguments from there. - -By default, "raw" format and "file" protocol is used; all tests will be -executed, except the unsupported ones. You can override the format and protocol -with arguments: - -.. code:: - - # test with qcow2 format - ./check -qcow2 - # or test a different protocol - ./check -nbd - -It's also possible to list test numbers explicitly: - -.. code:: - - # run selected cases with qcow2 format - ./check -qcow2 001 030 153 - -Cache mode can be selected with the "-c" option, which may help reveal bugs -that are specific to certain cache mode. - -More options are supported by the ``./check`` script, run ``./check -h`` for -help. - -Writing a new test case -~~~~~~~~~~~~~~~~~~~~~~~ - -Consider writing a tests case when you are making any changes to the block -layer. An iotest case is usually the choice for that. There are already many -test cases, so it is possible that extending one of them may achieve the goal -and save the boilerplate to create one. (Unfortunately, there isn't a 100% -reliable way to find a related one out of hundreds of tests. One approach is -using ``git grep``.) - -Usually an iotest case consists of two files. One is an executable that -produces output to stdout and stderr, the other is the expected reference -output. They are given the same number in file names. E.g. Test script ``055`` -and reference output ``055.out``. - -In rare cases, when outputs differ between cache mode ``none`` and others, a -``.out.nocache`` file is added. In other cases, when outputs differ between -image formats, more than one ``.out`` files are created ending with the -respective format names, e.g. ``178.out.qcow2`` and ``178.out.raw``. - -There isn't a hard rule about how to write a test script, but a new test is -usually a (copy and) modification of an existing case. There are a few -commonly used ways to create a test: - -* A Bash script. It will make use of several environmental variables related - to the testing procedure, and could source a group of ``common.*`` libraries - for some common helper routines. - -* A Python unittest script. Import ``iotests`` and create a subclass of - ``iotests.QMPTestCase``, then call ``iotests.main`` method. The downside of - this approach is that the output is too scarce, and the script is considered - harder to debug. - -* A simple Python script without using unittest module. This could also import - ``iotests`` for launching QEMU and utilities etc, but it doesn't inherit - from ``iotests.QMPTestCase`` therefore doesn't use the Python unittest - execution. This is a combination of 1 and 2. - -Pick the language per your preference since both Bash and Python have -comparable library support for invoking and interacting with QEMU programs. If -you opt for Python, it is strongly recommended to write Python 3 compatible -code. - -Both Python and Bash frameworks in iotests provide helpers to manage test -images. They can be used to create and clean up images under the test -directory. If no I/O or any protocol specific feature is needed, it is often -more convenient to use the pseudo block driver, ``null-co://``, as the test -image, which doesn't require image creation or cleaning up. Avoid system-wide -devices or files whenever possible, such as ``/dev/null`` or ``/dev/zero``. -Otherwise, image locking implications have to be considered. For example, -another application on the host may have locked the file, possibly leading to a -test failure. If using such devices are explicitly desired, consider adding -``locking=off`` option to disable image locking. - -Debugging a test case -~~~~~~~~~~~~~~~~~~~~~ - -The following options to the ``check`` script can be useful when debugging -a failing test: - -* ``-gdb`` wraps every QEMU invocation in a ``gdbserver``, which waits for a - connection from a gdb client. The options given to ``gdbserver`` (e.g. the - address on which to listen for connections) are taken from the ``$GDB_OPTIONS`` - environment variable. By default (if ``$GDB_OPTIONS`` is empty), it listens on - ``localhost:12345``. - It is possible to connect to it for example with - ``gdb -iex "target remote $addr"``, where ``$addr`` is the address - ``gdbserver`` listens on. - If the ``-gdb`` option is not used, ``$GDB_OPTIONS`` is ignored, - regardless of whether it is set or not. - -* ``-valgrind`` attaches a valgrind instance to QEMU. If it detects - warnings, it will print and save the log in - ``$TEST_DIR/.valgrind``. - The final command line will be ``valgrind --log-file=$TEST_DIR/ - .valgrind --error-exitcode=99 $QEMU ...`` - -* ``-d`` (debug) just increases the logging verbosity, showing - for example the QMP commands and answers. - -* ``-p`` (print) redirects QEMU’s stdout and stderr to the test output, - instead of saving it into a log file in - ``$TEST_DIR/qemu-machine-``. - -Test case groups -~~~~~~~~~~~~~~~~ - -"Tests may belong to one or more test groups, which are defined in the form -of a comment in the test source file. By convention, test groups are listed -in the second line of the test file, after the "#!/..." line, like this: - -.. code:: - - #!/usr/bin/env python3 - # group: auto quick - # - ... - -Another way of defining groups is creating the tests/qemu-iotests/group.local -file. This should be used only for downstream (this file should never appear -in upstream). This file may be used for defining some downstream test groups -or for temporarily disabling tests, like this: - -.. code:: - - # groups for some company downstream process - # - # ci - tests to run on build - # down - our downstream tests, not for upstream - # - # Format of each line is: - # TEST_NAME TEST_GROUP [TEST_GROUP ]... - - 013 ci - 210 disabled - 215 disabled - our-ugly-workaround-test down ci - -Note that the following group names have a special meaning: - -- quick: Tests in this group should finish within a few seconds. - -- auto: Tests in this group are used during "make check" and should be - runnable in any case. That means they should run with every QEMU binary - (also non-x86), with every QEMU configuration (i.e. must not fail if - an optional feature is not compiled in - but reporting a "skip" is ok), - work at least with the qcow2 file format, work with all kind of host - filesystems and users (e.g. "nobody" or "root") and must not take too - much memory and disk space (since CI pipelines tend to fail otherwise). - -- disabled: Tests in this group are disabled and ignored by check. - -.. _container-ref: - -Container based tests ---------------------- - -Introduction -~~~~~~~~~~~~ - -The container testing framework in QEMU utilizes public images to -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 many of the target -names and scripts are prefixed with "docker" the system will -automatically run on whichever is configured. - -The container images are also used to augment the generation of tests -for testing TCG. See :ref:`checktcg-ref` for more details. - -Docker Prerequisites -~~~~~~~~~~~~~~~~~~~~ - -Install "docker" with the system package manager and start the Docker service -on your development machine, then make sure you have the privilege to run -Docker commands. Typically it means setting up passwordless ``sudo docker`` -command or login as root. For example: - -.. code:: - - $ sudo yum install docker - $ # or `apt-get install docker` for Ubuntu, etc. - $ sudo systemctl start docker - $ sudo docker ps - -The last command should print an empty table, to verify the system is ready. - -An alternative method to set up permissions is by adding the current user to -"docker" group and making the docker daemon socket file (by default -``/var/run/docker.sock``) accessible to the group: - -.. code:: - - $ sudo groupadd docker - $ sudo usermod $USER -a -G docker - $ sudo chown :docker /var/run/docker.sock - -Note that any one of above configurations makes it possible for the user to -exploit the whole host with Docker bind mounting or other privileged -operations. So only do it on development machines. - -Podman Prerequisites -~~~~~~~~~~~~~~~~~~~~ - -Install "podman" with the system package manager. - -.. code:: - - $ sudo dnf install podman - $ podman ps - -The last command should print an empty table, to verify the system is ready. - -Quickstart -~~~~~~~~~~ - -From source tree, type ``make docker-help`` to see the help. Testing -can be started without configuring or building QEMU (``configure`` and -``make`` are done in the container, with parameters defined by the -make target): - -.. code:: - - make docker-test-build@centos8 - -This will create a container instance using the ``centos8`` image (the image -is downloaded and initialized automatically), in which the ``test-build`` job -is executed. - -Registry -~~~~~~~~ - -The QEMU project has a container registry hosted by GitLab at -``registry.gitlab.com/qemu-project/qemu`` which will automatically be -used to pull in pre-built layers. This avoids unnecessary strain on -the distro archives created by multiple developers running the same -container build steps over and over again. This can be overridden -locally by using the ``NOCACHE`` build option: - -.. code:: - - make docker-image-debian-arm64-cross NOCACHE=1 - -Images -~~~~~~ - -Along with many other images, the ``centos8`` image is defined in a Dockerfile -in ``tests/docker/dockerfiles/``, called ``centos8.docker``. ``make docker-help`` -command will list all the available images. - -A ``.pre`` script can be added beside the ``.docker`` file, which will be -executed before building the image under the build context directory. This is -mainly used to do necessary host side setup. One such setup is ``binfmt_misc``, -for example, to make qemu-user powered cross build containers work. - -Most of the existing Dockerfiles were written by hand, simply by creating a -a new ``.docker`` file under the ``tests/docker/dockerfiles/`` directory. -This has led to an inconsistent set of packages being present across the -different containers. - -Thus going forward, QEMU is aiming to automatically generate the Dockerfiles -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. - - -Adding new build pre-requisites -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the simple case where the pre-requisite is already known to ``libvirt-ci`` -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 - - * 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. - - * 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 - - * 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. - - -Adding new OS distros -^^^^^^^^^^^^^^^^^^^^^ - -In some cases ``libvirt-ci`` will not know about the OS distro that is -desired to be tested. Before adding a new OS distro, discuss the proposed -addition: - - * Send a mail to qemu-devel, copying people listed in the - MAINTAINERS file for ``Build and test automation``. - - There are limited CI compute resources available to QEMU, so the - cost/benefit tradeoff of adding new OS distros needs to be considered. - - * File an issue at https://gitlab.com/libvirt/libvirt-ci/-/issues - pointing to the qemu-devel mail thread in the archives. - - This alerts other people who might be interested in the work - to avoid duplication, as well as to get feedback from libvirt-ci - maintainers on any tips to ease the addition - -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. - - * Edit the ``mappings.yml`` change to update all the existing package - entries, providing details of the new OS distro - - * 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 - - * 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. - - -Tests -~~~~~ - -Different tests are added to cover various configurations to build and test -QEMU. Docker tests are the executables under ``tests/docker`` named -``test-*``. They are typically shell scripts and are built on top of a shell -library, ``tests/docker/common.rc``, which provides helpers to find the QEMU -source and build it. - -The full list of tests is printed in the ``make docker-help`` help. - -Debugging a Docker test failure -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -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``. -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 - build QEMU and run tests from there, or press Ctrl-D to let the Docker - testing continue. -4. If you press Ctrl-D, the same building and testing procedure will begin, and - will hopefully run into the error again. After that, you will be dropped to - the prompt for debug. - -Options -~~~~~~~ - -Various options can be used to affect how Docker tests are done. The full -list is in the ``make docker`` help text. The frequently used ones are: - -* ``V=1``: the same as in top level ``make``. It will be propagated to the - container and enable verbose output. -* ``J=$N``: the number of parallel tasks in make commands in the container, - similar to the ``-j $N`` option in top level ``make``. (The ``-j`` option in - top level ``make`` will not be propagated into the container.) -* ``DEBUG=1``: enables debug. See the previous "Debugging a Docker test - failure" section. - -Thread Sanitizer ----------------- - -Thread Sanitizer (TSan) is a tool which can detect data races. QEMU supports -building and testing with this tool. - -For more information on TSan: - -https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual - -Thread Sanitizer in Docker -~~~~~~~~~~~~~~~~~~~~~~~~~~ -TSan is currently supported in the ubuntu2004 docker. - -The test-tsan test will build using TSan and then run make check. - -.. code:: - - make docker-test-tsan@ubuntu2004 - -TSan warnings under docker are placed in files located at build/tsan/. - -We recommend using DEBUG=1 to allow launching the test from inside the docker, -and to allow review of the warnings generated by TSan. - -Building and Testing with TSan -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It is possible to build and test with TSan, with a few additional steps. -These steps are normally done automatically in the docker. - -There is a one time patch needed in clang-9 or clang-10 at this time: - -.. code:: - - sed -i 's/^const/static const/g' \ - /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h - -To configure the build for TSan: - -.. code:: - - ../configure --enable-tsan --cc=clang-10 --cxx=clang++-10 \ - --disable-werror --extra-cflags="-O0" - -The runtime behavior of TSAN is controlled by the TSAN_OPTIONS environment -variable. - -More information on the TSAN_OPTIONS can be found here: - -https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags - -For example: - -.. code:: - - export TSAN_OPTIONS=suppressions=/tests/tsan/suppressions.tsan \ - detect_deadlocks=false history_size=7 exitcode=0 \ - log_path=/tsan/tsan_warning - -The above exitcode=0 has TSan continue without error if any warnings are found. -This allows for running the test and then checking the warnings afterwards. -If you want TSan to stop and exit with error on warnings, use exitcode=66. - -TSan Suppressions -~~~~~~~~~~~~~~~~~ -Keep in mind that for any data race warning, although there might be a data race -detected by TSan, there might be no actual bug here. TSan provides several -different mechanisms for suppressing warnings. In general it is recommended -to fix the code if possible to eliminate the data race rather than suppress -the warning. - -A few important files for suppressing warnings are: - -tests/tsan/suppressions.tsan - Has TSan warnings we wish to suppress at runtime. -The comment on each suppression will typically indicate why we are -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 -at compile time for test or debug. -Add flags to configure to enable: - -"--extra-cflags=-fsanitize-blacklist=/tests/tsan/blacklist.tsan" - -More information on the file format can be found here under "Blacklist Format": - -https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags - -TSan Annotations -~~~~~~~~~~~~~~~~ -include/qemu/tsan.h defines annotations. See this file for more descriptions -of the annotations themselves. Annotations can be used to suppress -TSan warnings or give TSan more information so that it can detect proper -relationships between accesses of data. - -Annotation examples can be found here: - -https://github.com/llvm/llvm-project/tree/master/compiler-rt/test/tsan/ - -Good files to start with are: annotate_happens_before.cpp and ignore_race.cpp - -The full set of annotations can be found here: - -https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp - -docker-binfmt-image-debian-% targets ------------------------------------- - -It is possible to combine Debian's bootstrap scripts with a configured -``binfmt_misc`` to bootstrap a number of Debian's distros including -experimental ports not yet supported by a released OS. This can -simplify setting up a rootfs by using docker to contain the foreign -rootfs rather than manually invoking chroot. - -Setting up ``binfmt_misc`` -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can use the script ``qemu-binfmt-conf.sh`` to configure a QEMU -user binary to automatically run binaries for the foreign -architecture. While the scripts will try their best to work with -dynamically linked QEMU's a statically linked one will present less -potential complications when copying into the docker image. Modern -kernels support the ``F`` (fix binary) flag which will open the QEMU -executable on setup and avoids the need to find and re-open in the -chroot environment. This is triggered with the ``--persistent`` flag. - -Example invocation -~~~~~~~~~~~~~~~~~~ - -For example to setup the HPPA ports builds of Debian:: - - make docker-binfmt-image-debian-sid-hppa \ - DEB_TYPE=sid DEB_ARCH=hppa \ - DEB_URL=http://ftp.ports.debian.org/debian-ports/ \ - DEB_KEYRING=/usr/share/keyrings/debian-ports-archive-keyring.gpg \ - EXECUTABLE=(pwd)/qemu-hppa V=1 - -The ``DEB_`` variables are substitutions used by -``debian-boostrap.pre`` which is called to do the initial debootstrap -of the rootfs before it is copied into the container. The second stage -is run as part of the build. The final image will be tagged as -``qemu/debian-sid-hppa``. - -VM testing ----------- - -This test suite contains scripts that bootstrap various guest images that have -necessary packages to build QEMU. The basic usage is documented in ``Makefile`` -help which is displayed with ``make vm-help``. - -Quickstart -~~~~~~~~~~ - -Run ``make vm-help`` to list available make targets. Invoke a specific make -command to run build test in an image. For example, ``make vm-build-freebsd`` -will build the source tree in the FreeBSD image. The command can be executed -from either the source tree or the build dir; if the former, ``./configure`` is -not needed. The command will then generate the test image in ``./tests/vm/`` -under the working directory. - -Note: images created by the scripts accept a well-known RSA key pair for SSH -access, so they SHOULD NOT be exposed to external interfaces if you are -concerned about attackers taking control of the guest and potentially -exploiting a QEMU security bug to compromise the host. - -QEMU binaries -~~~~~~~~~~~~~ - -By default, ``qemu-system-x86_64`` is searched in $PATH to run the guest. If -there isn't one, or if it is older than 2.10, the test won't work. In this case, -provide the QEMU binary in env var: ``QEMU=/path/to/qemu-2.10+``. - -Likewise the path to ``qemu-img`` can be set in QEMU_IMG environment variable. - -Make jobs -~~~~~~~~~ - -The ``-j$X`` option in the make command line is not propagated into the VM, -specify ``J=$X`` to control the make jobs in the guest. - -Debugging -~~~~~~~~~ - -Add ``DEBUG=1`` and/or ``V=1`` to the make command to allow interactive -debugging and verbose output. If this is not enough, see the next section. -``V=1`` will be propagated down into the make jobs in the guest. - -Manual invocation -~~~~~~~~~~~~~~~~~ - -Each guest script is an executable script with the same command line options. -For example to work with the netbsd guest, use ``$QEMU_SRC/tests/vm/netbsd``: - -.. code:: - - $ cd $QEMU_SRC/tests/vm - - # To bootstrap the image - $ ./netbsd --build-image --image /var/tmp/netbsd.img - <...> - - # To run an arbitrary command in guest (the output will not be echoed unless - # --debug is added) - $ ./netbsd --debug --image /var/tmp/netbsd.img uname -a - - # To build QEMU in guest - $ ./netbsd --debug --image /var/tmp/netbsd.img --build-qemu $QEMU_SRC - - # To get to an interactive shell - $ ./netbsd --interactive --image /var/tmp/netbsd.img sh - -Adding new guests -~~~~~~~~~~~~~~~~~ - -Please look at existing guest scripts for how to add new guests. - -Most importantly, create a subclass of BaseVM and implement ``build_image()`` -method and define ``BUILD_SCRIPT``, then finally call ``basevm.main()`` from -the script's ``main()``. - -* Usually in ``build_image()``, a template image is downloaded from a - predefined URL. ``BaseVM._download_with_cache()`` takes care of the cache and - the checksum, so consider using it. - -* Once the image is downloaded, users, SSH server and QEMU build deps should - be set up: - - - Root password set to ``BaseVM.ROOT_PASS`` - - User ``BaseVM.GUEST_USER`` is created, and password set to - ``BaseVM.GUEST_PASS`` - - SSH service is enabled and started on boot, - ``$QEMU_SRC/tests/keys/id_rsa.pub`` is added to ssh's ``authorized_keys`` - file of both root and the normal user - - DHCP client service is enabled and started on boot, so that it can - automatically configure the virtio-net-pci NIC and communicate with QEMU - user net (10.0.2.2) - - Necessary packages are installed to untar the source tarball and build - QEMU - -* Write a proper ``BUILD_SCRIPT`` template, which should be a shell script that - untars a raw virtio-blk block device, which is the tarball data blob of the - QEMU source tree, then configure/build it. Running "make check" is also - recommended. - -Image fuzzer testing --------------------- - -An image fuzzer was added to exercise format drivers. Currently only qcow2 is -supported. To start the fuzzer, run - -.. code:: - - tests/image-fuzzer/runner.py -c '[["qemu-img", "info", "$test_img"]]' /tmp/test qcow2 - -Alternatively, some command different from ``qemu-img info`` can be tested, by -changing the ``-c`` option. - -Integration tests using the Avocado Framework ---------------------------------------------- - -The ``tests/avocado`` directory hosts integration tests. They're usually -higher level tests, and may interact with external resources and with -various guest operating systems. - -These tests are written using the Avocado Testing Framework (which must -be installed separately) in conjunction with a the ``avocado_qemu.Test`` -class, implemented at ``tests/avocado/avocado_qemu``. - -Tests based on ``avocado_qemu.Test`` can easily: - - * Customize the command line arguments given to the convenience - ``self.vm`` attribute (a QEMUMachine instance) - - * Interact with the QEMU monitor, send QMP commands and check - their results - - * Interact with the guest OS, using the convenience console device - (which may be useful to assert the effectiveness and correctness of - command line arguments or QMP commands) - - * Interact with external data files that accompany the test itself - (see ``self.get_data()``) - - * Download (and cache) remote data files, such as firmware and kernel - images - - * Have access to a library of guest OS images (by means of the - ``avocado.utils.vmimage`` library) - - * Make use of various other test related utilities available at the - test class itself and at the utility library: - - - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test - - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html - -Running tests -~~~~~~~~~~~~~ - -You can run the avocado tests simply by executing: - -.. code:: - - 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 -build tree (at ``tests/results``). - -Note: the build environment must be using a Python 3 stack, and have -the ``venv`` and ``pip`` packages installed. If necessary, make sure -``configure`` is called with ``--python=`` and that those modules are -available. On Debian and Ubuntu based systems, depending on the -specific version, they may be on packages named ``python3-venv`` and -``python3-pip``. - -It is also possible to run tests based on tags using the -``make check-avocado`` command and the ``AVOCADO_TAGS`` environment -variable: - -.. code:: - - make check-avocado AVOCADO_TAGS=quick - -Note that tags separated with commas have an AND behavior, while tags -separated by spaces have an OR behavior. For more information on Avocado -tags, see: - - https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/tags.html - -To run a single test file, a couple of them, or a test within a file -using the ``make check-avocado`` command, set the ``AVOCADO_TESTS`` -environment variable with the test files or test names. To run all -tests from a single file, use: - - .. code:: - - make check-avocado AVOCADO_TESTS=$FILEPATH - -The same is valid to run tests from multiple test files: - - .. code:: - - make check-avocado AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' - -To run a single test within a file, use: - - .. code:: - - make check-avocado AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME - -The same is valid to run single tests from multiple test files: - - .. code:: - - make check-avocado AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' - -The scripts installed inside the virtual environment may be used -without an "activation". For instance, the Avocado test runner -may be invoked by running: - - .. code:: - - tests/venv/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 -needed running: - - .. code:: - - make check-venv - -It is also possible to run tests from a single file or a single test within -a test file. To run tests from a single file within the build tree, use: - - .. code:: - - tests/venv/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 - -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 - -Manual Installation -~~~~~~~~~~~~~~~~~~~ - -To manually install Avocado and its dependencies, run: - -.. code:: - - pip install --user avocado-framework - -Alternatively, follow the instructions on this link: - - https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html - -Overview -~~~~~~~~ - -The ``tests/avocado/avocado_qemu`` directory provides the -``avocado_qemu`` Python module, containing the ``avocado_qemu.Test`` -class. Here's a simple usage example: - -.. code:: - - from avocado_qemu import QemuSystemTest - - - class Version(QemuSystemTest): - """ - :avocado: tags=quick - """ - 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)') - -To execute your test, run: - -.. code:: - - avocado run version.py - -Tests may be classified according to a convention by using docstring -directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests -in the current directory, tagged as "quick", run: - -.. code:: - - avocado run -t quick . - -The ``avocado_qemu.Test`` base test class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``avocado_qemu.Test`` class has a number of characteristics that -are worth being mentioned right away. - -First of all, it attempts to give each test a ready to use QEMUMachine -instance, available at ``self.vm``. Because many tests will tweak the -QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) -is left to the test writer. - -The base test class has also support for tests with more than one -QEMUMachine. The way to get machines is through the ``self.get_vm()`` -method which will return a QEMUMachine instance. The ``self.get_vm()`` -method accepts arguments that will be passed to the QEMUMachine creation -and also an optional ``name`` attribute so you can identify a specific -machine and get it more than once through the tests methods. A simple -and hypothetical example follows: - -.. code:: - - from avocado_qemu import QemuSystemTest - - - class MultipleMachines(QemuSystemTest): - def test_multiple_machines(self): - first_machine = self.get_vm() - second_machine = self.get_vm() - self.get_vm(name='third_machine').launch() - - first_machine.launch() - second_machine.launch() - - first_res = first_machine.command( - 'human-monitor-command', - command_line='info version') - - second_res = second_machine.command( - 'human-monitor-command', - command_line='info version') - - third_res = self.get_vm(name='third_machine').command( - 'human-monitor-command', - command_line='info version') - - self.assertEquals(first_res, second_res, third_res) - -At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines -shutdown. - -The ``avocado_qemu.LinuxTest`` base test class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``avocado_qemu.LinuxTest`` is further specialization of the -``avocado_qemu.Test`` class, so it contains all the characteristics of -the later plus some extra features. - -First of all, this base class is intended for tests that need to -interact with a fully booted and operational Linux guest. At this -time, it uses a Fedora 31 guest image. The most basic example looks -like this: - -.. code:: - - from avocado_qemu import LinuxTest - - - class SomeTest(LinuxTest): - - def test(self): - self.launch_and_wait() - self.ssh_command('some_command_to_be_run_in_the_guest') - -Please refer to tests that use ``avocado_qemu.LinuxTest`` under -``tests/avocado`` for more examples. - -QEMUMachine -~~~~~~~~~~~ - -The QEMUMachine API is already widely used in the Python iotests, -device-crash-test and other Python scripts. It's a wrapper around the -execution of a QEMU binary, giving its users: - - * the ability to set command line arguments to be given to the QEMU - binary - - * a ready to use QMP connection and interface, which can be used to - send commands and inspect its results, as well as asynchronous - events - - * convenience methods to set commonly used command line arguments in - a more succinct and intuitive way - -QEMU binary selection -^^^^^^^^^^^^^^^^^^^^^ - -The QEMU binary used for the ``self.vm`` QEMUMachine instance will -primarily depend on the value of the ``qemu_bin`` parameter. If it's -not explicitly set, its default value will be the result of a dynamic -probe in the same source tree. A suitable binary will be one that -targets the architecture matching host machine. - -Based on this description, test writers will usually rely on one of -the following approaches: - -1) Set ``qemu_bin``, and use the given binary - -2) Do not set ``qemu_bin``, and use a QEMU binary named like - "qemu-system-${arch}", either in the current - working directory, or in the current source tree. - -The resulting ``qemu_bin`` value will be preserved in the -``avocado_qemu.Test`` as an attribute with the same name. - -Attribute reference -~~~~~~~~~~~~~~~~~~~ - -Test -^^^^ - -Besides the attributes and methods that are part of the base -``avocado.Test`` class, the following attributes are available on any -``avocado_qemu.Test`` instance. - -vm -'' - -A QEMUMachine instance, initially configured according to the given -``qemu_bin`` parameter. - -arch -'''' - -The architecture can be used on different levels of the stack, e.g. by -the framework or by the test itself. At the framework level, it will -currently influence the selection of a QEMU binary (when one is not -explicitly given). - -Tests are also free to use this attribute value, for their own needs. -A test may, for instance, use the same value when selecting the -architecture of a kernel or disk image to boot a VM with. - -The ``arch`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None``, or, if the test is tagged with one (and only one) -``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. - -cpu -''' - -The cpu model that will be set to all QEMUMachine instances created -by the test. - -The ``cpu`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None ``, or, if the test is tagged with one (and only one) -``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``. - -machine -''''''' - -The machine type that will be set to all QEMUMachine instances created -by the test. - -The ``machine`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None``, or, if the test is tagged with one (and only one) -``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. - -qemu_bin -'''''''' - -The preserved value of the ``qemu_bin`` parameter or the result of the -dynamic probe for a QEMU binary in the current working directory or -source tree. - -LinuxTest -^^^^^^^^^ - -Besides the attributes present on the ``avocado_qemu.Test`` base -class, the ``avocado_qemu.LinuxTest`` adds the following attributes: - -distro -'''''' - -The name of the Linux distribution used as the guest image for the -test. The name should match the **Provider** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_version -'''''''''''''' - -The version of the Linux distribution as the guest image for the -test. The name should match the **Version** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_checksum -''''''''''''''' - -The sha256 hash of the guest image file used for the test. - -If this value is not set in the code or by a test parameter (with the -same name), no validation on the integrity of the image will be -performed. - -Parameter reference -~~~~~~~~~~~~~~~~~~~ - -To understand how Avocado parameters are accessed by tests, and how -they can be passed to tests, please refer to:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters - -Parameter values can be easily seen in the log files, and will look -like the following: - -.. code:: - - PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 - -Test -^^^^ - -arch -'''' - -The architecture that will influence the selection of a QEMU binary -(when one is not explicitly given). - -Tests are also free to use this parameter value, for their own needs. -A test may, for instance, use the same value when selecting the -architecture of a kernel or disk image to boot a VM with. - -This parameter has a direct relation with the ``arch`` attribute. If -not given, it will default to None. - -cpu -''' - -The cpu model that will be set to all QEMUMachine instances created -by the test. - -machine -''''''' - -The machine type that will be set to all QEMUMachine instances created -by the test. - -qemu_bin -'''''''' - -The exact QEMU binary to be used on QEMUMachine. - -LinuxTest -^^^^^^^^^ - -Besides the parameters present on the ``avocado_qemu.Test`` base -class, the ``avocado_qemu.LinuxTest`` adds the following parameters: - -distro -'''''' - -The name of the Linux distribution used as the guest image for the -test. The name should match the **Provider** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_version -'''''''''''''' - -The version of the Linux distribution as the guest image for the -test. The name should match the **Version** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_checksum -''''''''''''''' - -The sha256 hash of the guest image file used for the test. - -If this value is not set in the code or by this parameter no -validation on the integrity of the image will be performed. - -Skipping tests -~~~~~~~~~~~~~~ - -The Avocado framework provides Python decorators which allow for easily skip -tests running under certain conditions. For example, on the lack of a binary -on the test system or when the running environment is a CI system. For further -information about those decorators, please refer to:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests - -While the conditions for skipping tests are often specifics of each one, there -are recurring scenarios identified by the QEMU developers and the use of -environment variables became a kind of standard way to enable/disable tests. - -Here is a list of the most used variables: - -AVOCADO_ALLOW_LARGE_STORAGE -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tests which are going to fetch or produce assets considered *large* are not -going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on -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. - -AVOCADO_ALLOW_UNTRUSTED_CODE -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are tests which will boot a kernel image or firmware that can be -considered not safe to run on the developer's workstation, thus they are -skipped by default. The definition of *not safe* is also arbitrary but -usually it means a blob which either its source or build process aren't -public available. - -You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in -order to allow tests which make use of those kind of assets. - -AVOCADO_TIMEOUT_EXPECTED -^^^^^^^^^^^^^^^^^^^^^^^^ -The Avocado framework has a timeout mechanism which interrupts tests to avoid the -test suite of getting stuck. The timeout value can be set via test parameter or -property defined in the test class, for further details:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout - -Even though the timeout can be set by the test developer, there are some tests -that may not have a well-defined limit of time to finish under certain -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: - -.. code:: - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test(self): - do_something() - -Uninstalling Avocado -~~~~~~~~~~~~~~~~~~~~ - -If you've followed the manual installation instructions above, you can -easily uninstall Avocado. Start by listing the packages you have -installed:: - - pip list --user - -And remove any package you want with:: - - pip uninstall - -If you've used ``make check-avocado``, the Python virtual environment where -Avocado is installed will be cleaned up as part of ``make check-clean``. - -.. _checktcg-ref: - -Testing with "make check-tcg" ------------------------------ - -The check-tcg tests are intended for simple smoke tests of both -linux-user and softmmu TCG functionality. However to build test -programs for guest targets you need to have cross compilers available. -If your distribution supports cross compilers you can do something as -simple as:: - - apt install gcc-aarch64-linux-gnu - -The configure script will automatically pick up their presence. -Sometimes compilers have slightly odd names so the availability of -them can be prompted by passing in the appropriate configure option -for the architecture in question, for example:: - - $(configure) --cross-cc-aarch64=aarch64-cc - -There is also a ``--cross-cc-cflags-ARCH`` flag in case additional -compiler flags are needed to build for a given target. - -If you have the ability to run containers as the user the build system -will automatically use them where no system compiler is available. For -architectures where we also support building QEMU we will generally -use the same container to build tests. However there are a number of -additional containers defined that have a minimal cross-build -environment that is only suitable for building test cases. Sometimes -we may use a bleeding edge distribution for compiler features needed -for test cases that aren't yet in the LTS distros we support for QEMU -itself. - -See :ref:`container-ref` for more details. - -Running subset of tests -~~~~~~~~~~~~~~~~~~~~~~~ - -You can build the tests for one architecture:: - - make build-tcg-tests-$TARGET - -And run with:: - - make run-tcg-tests-$TARGET - -Adding ``V=1`` to the invocation will show the details of how to -invoke QEMU for the test which is useful for debugging tests. - -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) -or just glibc (for linux-user tests). This is because getting a cross -compiler to work with additional libraries can be challenging. - -Other TCG Tests ---------------- - -There are a number of out-of-tree test suites that are used for more -extensive testing of processor features. - -KVM Unit Tests -~~~~~~~~~~~~~~ - -The KVM unit tests are designed to run as a Guest OS under KVM but -there is no reason why they can't exercise the TCG as well. It -provides a minimal OS kernel with hooks for enabling the MMU as well -as reporting test results via a special device:: - - https://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git - -Linux Test Project -~~~~~~~~~~~~~~~~~~ - -The LTP is focused on exercising the syscall interface of a Linux -kernel. It checks that syscalls behave as documented and strives to -exercise as many corner cases as possible. It is a useful test suite -to run to exercise QEMU's linux-user code:: - - https://linux-test-project.github.io/ - -GCC gcov support ----------------- - -``gcov`` is a GCC tool to analyze the testing coverage by -instrumenting the tested code. To use it, configure QEMU with -``--enable-gcov`` option and build. Then run the tests as usual. - -If you want to gather coverage information on a single test the ``make -clean-gcda`` target can be used to delete any existing coverage -information before running a single test. - -You can generate a HTML coverage report by executing ``make -coverage-html`` which will create -``meson-logs/coveragereport/index.html``. - -Further analysis can be conducted by running the ``gcov`` command -directly on the various .gcda output files. Please read the ``gcov`` -documentation for more information. diff --git a/docs/devel/testing/acpi-bits.rst b/docs/devel/testing/acpi-bits.rst new file mode 100644 index 0000000000..9a4d716ebf --- /dev/null +++ b/docs/devel/testing/acpi-bits.rst @@ -0,0 +1,155 @@ +================================== +ACPI/SMBIOS testing 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 were presented `at Plumbers +in 2011 `__ and `at Linux.conf.au in 2012 `__. + +For QEMU, we maintain a fork of bios bits in `gitlab`_, along with all +the dependent submodules. This fork contains numerous fixes, a newer +acpica and changes specific to running these functional QEMU tests using +bits. The author of this document is the current maintainer of the QEMU +fork of bios bits repository. For more information, please see `the +author's FOSDEM presentation `__ on this bios-bits based test framework. + +.. _Plumbers: https://blog.linuxplumbersconf.org/2011/ocw/system/presentations/867/original/bits.pdf +.. _Linux.conf.au: https://www.youtube.com/watch?v=36QIepyUuhg +.. _gitlab: https://gitlab.com/qemu-project/biosbits-bits +.. _FOSDEM: https://fosdem.org/2024/schedule/event/fosdem-2024-2262-exercising-qemu-generated-acpi-smbios-tables-using-biosbits-from-within-a-guest-vm-/ + +********************************* +Description of the test framework +********************************* + +Under the directory ``tests/functional/``, ``test_acpi_bits.py`` is a QEMU +functional test that drives all this. + +A brief description of the various test files follows. + +Under ``tests/functional/`` as the root we have: + +:: + + ├── acpi-bits + │ ├── bits-config + │ │ └── bits-cfg.txt + │ ├── bits-tests + │ ├── smbios.py2 + │ ├── testacpi.py2 + │ └── testcpuid.py2 + ├── test_acpi_bits.py + +* ``tests/functional``: + + ``test_acpi_bits.py``: + This is the main python functional 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 (assuming that the sources are in ".."): + :: + + $ export PYTHONPATH=../python:../tests/functional + $ export QEMU_TEST_QEMU_BINARY=$PWD/qemu-system-x86_64 + $ python3 ../tests/functional/test_acpi_bits.py + + The above will run all acpi-bits functional tests (producing output in + tap format). + + You can inspect the log files in tests/functional/x86_64/test_acpi_bits.*/ + for more information about the run or in order to diagnoze issues. + If you pass V=1 in the environment, more diagnostic logs will be put into + the test log. + +* ``tests/functional/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/functional/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 the test framework by accident when running + tests. + + +Author: Ani Sinha + diff --git a/docs/devel/testing/avocado.rst b/docs/devel/testing/avocado.rst new file mode 100644 index 0000000000..eda76fe2db --- /dev/null +++ b/docs/devel/testing/avocado.rst @@ -0,0 +1,581 @@ +.. _checkavocado-ref: + + +Integration testing with Avocado +================================ + +The ``tests/avocado`` directory hosts integration tests. They're usually +higher level tests, and may interact with external resources and with +various guest operating systems. + +These tests are written using the Avocado Testing Framework (which must be +installed separately) in conjunction with a the ``avocado_qemu.QemuSystemTest`` +class, implemented at ``tests/avocado/avocado_qemu``. + +Tests based on ``avocado_qemu.QemuSystemTest`` can easily: + + * Customize the command line arguments given to the convenience + ``self.vm`` attribute (a QEMUMachine instance) + + * Interact with the QEMU monitor, send QMP commands and check + their results + + * Interact with the guest OS, using the convenience console device + (which may be useful to assert the effectiveness and correctness of + command line arguments or QMP commands) + + * Interact with external data files that accompany the test itself + (see ``self.get_data()``) + + * Download (and cache) remote data files, such as firmware and kernel + images + + * Have access to a library of guest OS images (by means of the + ``avocado.utils.vmimage`` library) + + * Make use of various other test related utilities available at the + test class itself and at the utility library: + + - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test + - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html + +Running tests +------------- + +You can run the avocado tests simply by executing: + +.. code:: + + make check-avocado + +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 +the ``venv`` and ``pip`` packages installed. If necessary, make sure +``configure`` is called with ``--python=`` and that those modules are +available. On Debian and Ubuntu based systems, depending on the +specific version, they may be on packages named ``python3-venv`` and +``python3-pip``. + +It is also possible to run tests based on tags using the +``make check-avocado`` command and the ``AVOCADO_TAGS`` environment +variable: + +.. code:: + + make check-avocado AVOCADO_TAGS=quick + +Note that tags separated with commas have an AND behavior, while tags +separated by spaces have an OR behavior. For more information on Avocado +tags, see: + + https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/tags.html + +To run a single test file, a couple of them, or a test within a file +using the ``make check-avocado`` command, set the ``AVOCADO_TESTS`` +environment variable with the test files or test names. To run all +tests from a single file, use: + + .. code:: + + make check-avocado AVOCADO_TESTS=$FILEPATH + +The same is valid to run tests from multiple test files: + + .. code:: + + make check-avocado AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' + +To run a single test within a file, use: + + .. code:: + + make check-avocado AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME + +The same is valid to run single tests from multiple test files: + + .. code:: + + make check-avocado AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' + +The scripts installed inside the virtual environment may be used +without an "activation". For instance, the Avocado test runner +may be invoked by running: + + .. code:: + + 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 +needed running: + + .. code:: + + make check-venv + +It is also possible to run tests from a single file or a single test within +a test file. To run tests from a single file within the build tree, use: + + .. code:: + + pyvenv/bin/avocado run tests/avocado/$TESTFILE + +To run a single test within a test file, use: + + .. code:: + + 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:: + + pyvenv/bin/avocado list tests/avocado + +Manual Installation +------------------- + +To manually install Avocado and its dependencies, run: + +.. code:: + + pip install --user avocado-framework + +Alternatively, follow the instructions on this link: + + https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html + +Overview +-------- + +The ``tests/avocado/avocado_qemu`` directory provides the +``avocado_qemu`` Python module, containing the ``avocado_qemu.QemuSystemTest`` +class. Here's a simple usage example: + +.. code:: + + from avocado_qemu import QemuSystemTest + + + class Version(QemuSystemTest): + """ + :avocado: tags=quick + """ + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') + +To execute your test, run: + +.. code:: + + avocado run version.py + +Tests may be classified according to a convention by using docstring +directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests +in the current directory, tagged as "quick", run: + +.. code:: + + avocado run -t quick . + +The ``avocado_qemu.QemuSystemTest`` base test class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``avocado_qemu.QemuSystemTest`` class has a number of characteristics +that are worth being mentioned right away. + +First of all, it attempts to give each test a ready to use QEMUMachine +instance, available at ``self.vm``. Because many tests will tweak the +QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) +is left to the test writer. + +The base test class has also support for tests with more than one +QEMUMachine. The way to get machines is through the ``self.get_vm()`` +method which will return a QEMUMachine instance. The ``self.get_vm()`` +method accepts arguments that will be passed to the QEMUMachine creation +and also an optional ``name`` attribute so you can identify a specific +machine and get it more than once through the tests methods. A simple +and hypothetical example follows: + +.. code:: + + from avocado_qemu import QemuSystemTest + + + class MultipleMachines(QemuSystemTest): + def test_multiple_machines(self): + first_machine = self.get_vm() + second_machine = self.get_vm() + self.get_vm(name='third_machine').launch() + + first_machine.launch() + second_machine.launch() + + first_res = first_machine.cmd( + 'human-monitor-command', + command_line='info version') + + second_res = second_machine.cmd( + 'human-monitor-command', + command_line='info version') + + third_res = self.get_vm(name='third_machine').cmd( + 'human-monitor-command', + command_line='info version') + + self.assertEqual(first_res, second_res, third_res) + +At test "tear down", ``avocado_qemu.QemuSystemTest`` handles all the +QEMUMachines shutdown. + +The ``avocado_qemu.LinuxTest`` base test class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``avocado_qemu.LinuxTest`` is further specialization of the +``avocado_qemu.QemuSystemTest`` class, so it contains all the characteristics +of the later plus some extra features. + +First of all, this base class is intended for tests that need to +interact with a fully booted and operational Linux guest. At this +time, it uses a Fedora 31 guest image. The most basic example looks +like this: + +.. code:: + + from avocado_qemu import LinuxTest + + + class SomeTest(LinuxTest): + + def test(self): + self.launch_and_wait() + self.ssh_command('some_command_to_be_run_in_the_guest') + +Please refer to tests that use ``avocado_qemu.LinuxTest`` under +``tests/avocado`` for more examples. + +QEMUMachine +----------- + +The QEMUMachine API is already widely used in the Python iotests, +device-crash-test and other Python scripts. It's a wrapper around the +execution of a QEMU binary, giving its users: + + * the ability to set command line arguments to be given to the QEMU + binary + + * a ready to use QMP connection and interface, which can be used to + send commands and inspect its results, as well as asynchronous + events + + * convenience methods to set commonly used command line arguments in + a more succinct and intuitive way + +QEMU binary selection +^^^^^^^^^^^^^^^^^^^^^ + +The QEMU binary used for the ``self.vm`` QEMUMachine instance will +primarily depend on the value of the ``qemu_bin`` parameter. If it's +not explicitly set, its default value will be the result of a dynamic +probe in the same source tree. A suitable binary will be one that +targets the architecture matching host machine. + +Based on this description, test writers will usually rely on one of +the following approaches: + +1) Set ``qemu_bin``, and use the given binary + +2) Do not set ``qemu_bin``, and use a QEMU binary named like + "qemu-system-${arch}", either in the current + working directory, or in the current source tree. + +The resulting ``qemu_bin`` value will be preserved in the +``avocado_qemu.QemuSystemTest`` as an attribute with the same name. + +Attribute reference +------------------- + +Test +^^^^ + +Besides the attributes and methods that are part of the base +``avocado.Test`` class, the following attributes are available on any +``avocado_qemu.QemuSystemTest`` instance. + +vm +"" + +A QEMUMachine instance, initially configured according to the given +``qemu_bin`` parameter. + +arch +"""" + +The architecture can be used on different levels of the stack, e.g. by +the framework or by the test itself. At the framework level, it will +currently influence the selection of a QEMU binary (when one is not +explicitly given). + +Tests are also free to use this attribute value, for their own needs. +A test may, for instance, use the same value when selecting the +architecture of a kernel or disk image to boot a VM with. + +The ``arch`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None``, or, if the test is tagged with one (and only one) +``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. + +cpu +""" + +The cpu model that will be set to all QEMUMachine instances created +by the test. + +The ``cpu`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None ``, or, if the test is tagged with one (and only one) +``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``. + +machine +""""""" + +The machine type that will be set to all QEMUMachine instances created +by the test. + +The ``machine`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None``, or, if the test is tagged with one (and only one) +``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. + +qemu_bin +"""""""" + +The preserved value of the ``qemu_bin`` parameter or the result of the +dynamic probe for a QEMU binary in the current working directory or +source tree. + +LinuxTest +^^^^^^^^^ + +Besides the attributes present on the ``avocado_qemu.QemuSystemTest`` base +class, the ``avocado_qemu.LinuxTest`` adds the following attributes: + +distro +"""""" + +The name of the Linux distribution used as the guest image for the +test. The name should match the **Provider** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_version +"""""""""""""" + +The version of the Linux distribution as the guest image for the +test. The name should match the **Version** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_checksum +""""""""""""""" + +The sha256 hash of the guest image file used for the test. + +If this value is not set in the code or by a test parameter (with the +same name), no validation on the integrity of the image will be +performed. + +Parameter reference +------------------- + +To understand how Avocado parameters are accessed by tests, and how +they can be passed to tests, please refer to:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters + +Parameter values can be easily seen in the log files, and will look +like the following: + +.. code:: + + PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 + +Test +^^^^ + +arch +"""" + +The architecture that will influence the selection of a QEMU binary +(when one is not explicitly given). + +Tests are also free to use this parameter value, for their own needs. +A test may, for instance, use the same value when selecting the +architecture of a kernel or disk image to boot a VM with. + +This parameter has a direct relation with the ``arch`` attribute. If +not given, it will default to None. + +cpu +""" + +The cpu model that will be set to all QEMUMachine instances created +by the test. + +machine +""""""" + +The machine type that will be set to all QEMUMachine instances created +by the test. + +qemu_bin +"""""""" + +The exact QEMU binary to be used on QEMUMachine. + +LinuxTest +^^^^^^^^^ + +Besides the parameters present on the ``avocado_qemu.QemuSystemTest`` base +class, the ``avocado_qemu.LinuxTest`` adds the following parameters: + +distro +"""""" + +The name of the Linux distribution used as the guest image for the +test. The name should match the **Provider** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_version +"""""""""""""" + +The version of the Linux distribution as the guest image for the +test. The name should match the **Version** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_checksum +""""""""""""""" + +The sha256 hash of the guest image file used for the test. + +If this value is not set in the code or by this parameter no +validation on the integrity of the image will be performed. + +Skipping tests +-------------- + +The Avocado framework provides Python decorators which allow for easily skip +tests running under certain conditions. For example, on the lack of a binary +on the test system or when the running environment is a CI system. For further +information about those decorators, please refer to:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests + +While the conditions for skipping tests are often specifics of each one, there +are recurring scenarios identified by the QEMU developers and the use of +environment variables became a kind of standard way to enable/disable tests. + +Here is a list of the most used variables: + +AVOCADO_ALLOW_LARGE_STORAGE +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Tests which are going to fetch or produce assets considered *large* are not +going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on +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 +considered not safe to run on the developer's workstation, thus they are +skipped by default. The definition of *not safe* is also arbitrary but +usually it means a blob which either its source or build process aren't +public available. + +You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in +order to allow tests which make use of those kind of assets. + +AVOCADO_TIMEOUT_EXPECTED +^^^^^^^^^^^^^^^^^^^^^^^^ +The Avocado framework has a timeout mechanism which interrupts tests to avoid the +test suite of getting stuck. The timeout value can be set via test parameter or +property defined in the test class, for further details:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout + +Even though the timeout can be set by the test developer, there are some tests +that may not have a well-defined limit of time to finish under certain +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. + +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:: + + # 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 +-------------------- + +If you've followed the manual installation instructions above, you can +easily uninstall Avocado. Start by listing the packages you have +installed:: + + pip list --user + +And remove any package you want with:: + + pip uninstall + +If you've used ``make check-avocado``, the Python virtual environment where +Avocado is installed will be cleaned up as part of ``make check-clean``. diff --git a/docs/devel/testing/blkdebug.rst b/docs/devel/testing/blkdebug.rst new file mode 100644 index 0000000000..63887c9aa9 --- /dev/null +++ b/docs/devel/testing/blkdebug.rst @@ -0,0 +1,177 @@ +Block I/O error injection using ``blkdebug`` +============================================ + +.. + Copyright (C) 2014-2015 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. + +The ``blkdebug`` block driver is a rule-based error injection engine. It can be +used to exercise error code paths in block drivers including ``ENOSPC`` (out of +space) and ``EIO``. + +This document gives an overview of the features available in ``blkdebug``. + +Background +---------- +Block drivers have many error code paths that handle I/O errors. Image formats +are especially complex since metadata I/O errors during cluster allocation or +while updating tables happen halfway through request processing and require +discipline to keep image files consistent. + +Error injection allows test cases to trigger I/O errors at specific points. +This way, all error paths can be tested to make sure they are correct. + +Rules +----- +The ``blkdebug`` block driver takes a list of "rules" that tell the error injection +engine when to fail an I/O request. + +Each I/O request is evaluated against the rules. If a rule matches the request +then its "action" is executed. + +Rules can be placed in a configuration file; the configuration file +follows the same .ini-like format used by QEMU's ``-readconfig`` option, and +each section of the file represents a rule. + +The following configuration file defines a single rule:: + + $ cat blkdebug.conf + [inject-error] + event = "read_aio" + errno = "28" + +This rule fails all aio read requests with ``ENOSPC`` (28). Note that the errno +value depends on the host. On Linux, see +``/usr/include/asm-generic/errno-base.h`` for errno values. + +Invoke QEMU as follows:: + + $ qemu-system-x86_64 + -drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \ + -device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0 + +Rules support the following attributes: + +``event`` + which type of operation to match (e.g. ``read_aio``, ``write_aio``, + ``flush_to_os``, ``flush_to_disk``). See `Events`_ for + information on events. + +``state`` + (optional) the engine must be in this state number in order for this + rule to match. See `State transitions`_ for information + on states. + +``errno`` + the numeric errno value to return when a request matches this rule. + The errno values depend on the host since the numeric values are not + standardized in the POSIX specification. + +``sector`` + (optional) a sector number that the request must overlap in order to + match this rule + +``once`` + (optional, default ``off``) only execute this action on the first + matching request + +``immediately`` + (optional, default ``off``) return a NULL ``BlockAIOCB`` + pointer and fail without an errno instead. This + exercises the code path where ``BlockAIOCB`` fails and the + caller's ``BlockCompletionFunc`` is not invoked. + +Events +------ +Block drivers provide information about the type of I/O request they are about +to make so rules can match specific types of requests. For example, the ``qcow2`` +block driver tells ``blkdebug`` when it accesses the L1 table so rules can match +only L1 table accesses and not other metadata or guest data requests. + +The core events are: + +``read_aio`` + guest data read + +``write_aio`` + guest data write + +``flush_to_os`` + write out unwritten block driver state (e.g. cached metadata) + +``flush_to_disk`` + flush the host block device's disk cache + +See ``qapi/block-core.json:BlkdebugEvent`` for the full list of events. +You may need to grep block driver source code to understand the +meaning of specific events. + +State transitions +----------------- +There are cases where more power is needed to match a particular I/O request in +a longer sequence of requests. For example:: + + write_aio + flush_to_disk + write_aio + +How do we match the 2nd ``write_aio`` but not the first? This is where state +transitions come in. + +The error injection engine has an integer called the "state" that always starts +initialized to 1. The state integer is internal to ``blkdebug`` and cannot be +observed from outside but rules can interact with it for powerful matching +behavior. + +Rules can be conditional on the current state and they can transition to a new +state. + +When a rule's "state" attribute is non-zero then the current state must equal +the attribute in order for the rule to match. + +For example, to match the 2nd write_aio:: + + [set-state] + event = "write_aio" + state = "1" + new_state = "2" + + [inject-error] + event = "write_aio" + state = "2" + errno = "5" + +The first ``write_aio`` request matches the ``set-state`` rule and transitions from +state 1 to state 2. Once state 2 has been entered, the ``set-state`` rule no +longer matches since it requires state 1. But the ``inject-error`` rule now +matches the next ``write_aio`` request and injects ``EIO`` (5). + +State transition rules support the following attributes: + +``event`` + which type of operation to match (e.g. ``read_aio``, ``write_aio``, + ``flush_to_os`, ``flush_to_disk``). See `Events`_ for + information on events. + +``state`` + (optional) the engine must be in this state number in order for this + rule to match + +``new_state`` + transition to this state number + +Suspend and resume +------------------ +Exercising code paths in block drivers may require specific ordering amongst +concurrent requests. The "breakpoint" feature allows requests to be halted on +a ``blkdebug`` event and resumed later. This makes it possible to achieve +deterministic ordering when multiple requests are in flight. + +Breakpoints on ``blkdebug`` events are associated with a user-defined ``tag`` string. +This tag serves as an identifier by which the request can be resumed at a later +point. + +See the ``qemu-io(1)`` ``break``, ``resume``, ``remove_break``, and ``wait_break`` +commands for details. diff --git a/docs/devel/testing/blkverify.rst b/docs/devel/testing/blkverify.rst new file mode 100644 index 0000000000..2a71778b5e --- /dev/null +++ b/docs/devel/testing/blkverify.rst @@ -0,0 +1,73 @@ +Block driver correctness testing with ``blkverify`` +=================================================== + +Introduction +------------ + +This document describes how to use the ``blkverify`` protocol to test that a block +driver is operating correctly. + +It is difficult to test and debug block drivers against real guests. Often +processes inside the guest will crash because corrupt sectors were read as part +of the executable. Other times obscure errors are raised by a program inside +the guest. These issues are extremely hard to trace back to bugs in the block +driver. + +``blkverify`` solves this problem by catching data corruption inside QEMU the first +time bad data is read and reporting the disk sector that is corrupted. + +How it works +------------ + +The ``blkverify`` protocol has two child block devices, the "test" device and the +"raw" device. Read/write operations are mirrored to both devices so their +state should always be in sync. + +The "raw" device is a raw image, a flat file, that has identical starting +contents to the "test" image. The idea is that the "raw" device will handle +read/write operations correctly and not corrupt data. It can be used as a +reference for comparison against the "test" device. + +After a mirrored read operation completes, ``blkverify`` will compare the data and +raise an error if it is not identical. This makes it possible to catch the +first instance where corrupt data is read. + +Example +------- + +Imagine raw.img has 0xcd repeated throughout its first sector:: + + $ ./qemu-io -c 'read -v 0 512' raw.img + 00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + 00000010: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + [...] + 000001e0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + 000001f0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + read 512/512 bytes at offset 0 + 512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec) + +And test.img is corrupt, its first sector is zeroed when it shouldn't be:: + + $ ./qemu-io -c 'read -v 0 512' test.img + 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + [...] + 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + read 512/512 bytes at offset 0 + 512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec) + +This error is caught by ``blkverify``:: + + $ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img + blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0 + +A more realistic scenario is verifying the installation of a guest OS:: + + $ ./qemu-img create raw.img 16G + $ ./qemu-img create -f qcow2 test.qcow2 16G + $ ./qemu-system-x86_64 -cdrom debian.iso \ + -drive file=blkverify:raw.img:test.qcow2 + +If the installation is aborted when ``blkverify`` detects corruption, use ``qemu-io`` +to explore the contents of the disk image at the sector in question. diff --git a/docs/devel/ci-definitions.rst.inc b/docs/devel/testing/ci-definitions.rst.inc similarity index 100% rename from docs/devel/ci-definitions.rst.inc rename to docs/devel/testing/ci-definitions.rst.inc diff --git a/docs/devel/ci-jobs.rst.inc b/docs/devel/testing/ci-jobs.rst.inc similarity index 83% rename from docs/devel/ci-jobs.rst.inc rename to docs/devel/testing/ci-jobs.rst.inc index 1f28fec0d0..3756bbe355 100644 --- a/docs/devel/ci-jobs.rst.inc +++ b/docs/devel/testing/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 ~~~~~~~~~~~~~~~~~~~~~~~ @@ -171,9 +182,9 @@ If you've got access to an IBM Z 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 "s390x". -CENTOS_STREAM_8_x86_64_RUNNER_AVAILABLE -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -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/ci-runners.rst.inc b/docs/devel/testing/ci-runners.rst.inc similarity index 90% rename from docs/devel/ci-runners.rst.inc rename to docs/devel/testing/ci-runners.rst.inc index 7817001fb2..67b23d3719 100644 --- a/docs/devel/ci-runners.rst.inc +++ b/docs/devel/testing/ci-runners.rst.inc @@ -41,19 +41,18 @@ those hosts. This would look like:: Build environment ~~~~~~~~~~~~~~~~~ -The ``scripts/ci/setup/build-environment.yml`` Ansible playbook will -set up machines with the environment needed to perform builds and run -QEMU tests. This playbook consists on the installation of various -required packages (and a general package update while at it). It -currently covers a number of different Linux distributions, but it can -be expanded to cover other systems. +The ``scripts/ci/setup/$DISTRO/build-environment.yml`` Ansible +playbook will set up machines with the environment needed to perform +builds and run QEMU tests. This playbook consists on the installation +of various required packages (and a general package update while at +it). The minimum required version of Ansible successfully tested in this playbook is 2.8.0 (a version check is embedded within the playbook itself). To run the playbook, execute:: cd scripts/ci/setup - ansible-playbook -i inventory build-environment.yml + ansible-playbook -i inventory $DISTRO/build-environment.yml Please note that most of the tasks in the playbook require superuser privileges, such as those from the ``root`` account or those obtained diff --git a/docs/devel/ci.rst b/docs/devel/testing/ci.rst similarity index 100% rename from docs/devel/ci.rst rename to docs/devel/testing/ci.rst diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst new file mode 100644 index 0000000000..ae238ed3fc --- /dev/null +++ b/docs/devel/testing/functional.rst @@ -0,0 +1,355 @@ +.. _checkfunctional-ref: + +Functional testing with Python +============================== + +The ``tests/functional`` directory hosts functional tests written in +Python. They are usually higher level tests, and may interact with +external resources and with various guest operating systems. +The functional tests have initially evolved from the Avocado tests, so there +is a lot of similarity to those tests here (see :ref:`checkavocado-ref` for +details about the Avocado tests). + +The tests should be written in the style of the Python `unittest`_ framework, +using stdio for the TAP protocol. The folder ``tests/functional/qemu_test`` +provides classes (e.g. the ``QemuBaseTest``, ``QemuUserTest`` and the +``QemuSystemTest`` classes) and utility functions that help to get your test +into the right shape, e.g. by replacing the 'stdout' python object to redirect +the normal output of your test to stderr instead. + +Note that if you don't use one of the QemuBaseTest based classes for your +test, or if you spawn subprocesses from your test, you have to make sure +that there is no TAP-incompatible output written to stdio, e.g. either by +prefixing every line with a "# " to mark the output as a TAP comment, or +e.g. by capturing the stdout output of subprocesses (redirecting it to +stderr is OK). + +Tests based on ``qemu_test.QemuSystemTest`` can easily: + + * Customize the command line arguments given to the convenience + ``self.vm`` attribute (a QEMUMachine instance) + + * Interact with the QEMU monitor, send QMP commands and check + their results + + * Interact with the guest OS, using the convenience console device + (which may be useful to assert the effectiveness and correctness of + command line arguments or QMP commands) + + * Download (and cache) remote data files, such as firmware and kernel + images + +Running tests +------------- + +You can run the functional tests simply by executing: + +.. code:: + + make check-functional + +It is also possible to run tests for a certain target only, for example +the following line will only run the tests for the x86_64 target: + +.. code:: + + make check-functional-x86_64 + +To run a single test file without the meson test runner, you can also +execute the file directly by specifying two environment variables first, +the PYTHONPATH that has to include the python folder and the tests/functional +folder of the source tree, and QEMU_TEST_QEMU_BINARY that has to point +to the QEMU binary that should be used for the test. The current working +directory should be your build folder. For example:: + + $ export PYTHONPATH=../python:../tests/functional + $ export QEMU_TEST_QEMU_BINARY=$PWD/qemu-system-x86_64 + $ pyvenv/bin/python3 ../tests/functional/test_file.py + +The test framework will automatically purge any scratch files created during +the tests. If needing to debug a failed test, it is possible to keep these +files around on disk by setting ```QEMU_TEST_KEEP_SCRATCH=1``` as an env +variable. Any preserved files will be deleted the next time the test is run +without this variable set. + +Overview +-------- + +The ``tests/functional/qemu_test`` directory provides the ``qemu_test`` +Python module, containing the ``qemu_test.QemuSystemTest`` class. +Here is a simple usage example: + +.. code:: + + #!/usr/bin/env python3 + + from qemu_test import QemuSystemTest + + class Version(QemuSystemTest): + + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') + + if __name__ == '__main__': + QemuSystemTest.main() + +By providing the "hash bang" line at the beginning of the script, marking +the file as executable and by calling into QemuSystemTest.main(), the test +can also be run stand-alone, without a test runner. OTOH when run via a test +runner, the QemuSystemTest.main() function takes care of running the test +functions in the right fassion (e.g. with TAP output that is required by the +meson test runner). + +The ``qemu_test.QemuSystemTest`` base test class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``qemu_test.QemuSystemTest`` class has a number of characteristics +that are worth being mentioned. + +First of all, it attempts to give each test a ready to use QEMUMachine +instance, available at ``self.vm``. Because many tests will tweak the +QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) +is left to the test writer. + +The base test class has also support for tests with more than one +QEMUMachine. The way to get machines is through the ``self.get_vm()`` +method which will return a QEMUMachine instance. The ``self.get_vm()`` +method accepts arguments that will be passed to the QEMUMachine creation +and also an optional ``name`` attribute so you can identify a specific +machine and get it more than once through the tests methods. A simple +and hypothetical example follows: + +.. code:: + + from qemu_test import QemuSystemTest + + class MultipleMachines(QemuSystemTest): + def test_multiple_machines(self): + first_machine = self.get_vm() + second_machine = self.get_vm() + self.get_vm(name='third_machine').launch() + + first_machine.launch() + second_machine.launch() + + first_res = first_machine.cmd( + 'human-monitor-command', + command_line='info version') + + second_res = second_machine.cmd( + 'human-monitor-command', + command_line='info version') + + third_res = self.get_vm(name='third_machine').cmd( + 'human-monitor-command', + command_line='info version') + + self.assertEqual(first_res, second_res, third_res) + +At test "tear down", ``qemu_test.QemuSystemTest`` handles all the QEMUMachines +shutdown. + +QEMUMachine +----------- + +The QEMUMachine API is already widely used in the Python iotests, +device-crash-test and other Python scripts. It's a wrapper around the +execution of a QEMU binary, giving its users: + + * the ability to set command line arguments to be given to the QEMU + binary + + * a ready to use QMP connection and interface, which can be used to + send commands and inspect its results, as well as asynchronous + events + + * convenience methods to set commonly used command line arguments in + a more succinct and intuitive way + +QEMU binary selection +^^^^^^^^^^^^^^^^^^^^^ + +The QEMU binary used for the ``self.vm`` QEMUMachine instance will +primarily depend on the value of the ``qemu_bin`` class attribute. +If it is not explicitly set by the test code, its default value will +be the result the QEMU_TEST_QEMU_BINARY environment variable. + +Debugging hung QEMU +^^^^^^^^^^^^^^^^^^^ + +When test cases go wrong it may be helpful to debug a stalled QEMU +process. While the QEMUMachine class owns the primary QMP monitor +socket, it is possible to request a second QMP monitor be created +by setting the ``QEMU_TEST_QMP_BACKDOOR`` env variable to refer +to a UNIX socket name. The ``qmp-shell`` command can then be +attached to the stalled QEMU to examine its live state. + +Attribute reference +------------------- + +QemuBaseTest +^^^^^^^^^^^^ + +The following attributes are available on any ``qemu_test.QemuBaseTest`` +instance. + +arch +"""" + +The target architecture of the QEMU binary. + +Tests are also free to use this attribute value, for their own needs. +A test may, for instance, use this value when selecting the architecture +of a kernel or disk image to boot a VM with. + +qemu_bin +"""""""" + +The preserved value of the ``QEMU_TEST_QEMU_BINARY`` environment +variable. + +QemuUserTest +^^^^^^^^^^^^ + +The QemuUserTest class can be used for running an executable via the +usermode emulation binaries. + +QemuSystemTest +^^^^^^^^^^^^^^ + +The QemuSystemTest class can be used for running tests via one of the +qemu-system-* binaries. + +vm +"" + +A QEMUMachine instance, initially configured according to the given +``qemu_bin`` parameter. + +cpu +""" + +The cpu model that will be set to all QEMUMachine instances created +by the test. + +machine +""""""" + +The machine type that will be set to all QEMUMachine instances created +by the test. By using the set_machine() function of the QemuSystemTest +class to set this attribute, you can automatically check whether the +machine is available to skip the test in case it is not built into the +QEMU binary. + +Asset handling +-------------- + +Many functional tests download assets (e.g. Linux kernels, initrds, +firmware images, etc.) from the internet to be able to run tests with +them. This imposes additional challenges to the test framework. + +First there is the the problem that some people might not have an +unconstrained internet connection, so such tests should not be run by +default when running ``make check``. To accomplish this situation, +the tests that download files should only be added to the "thorough" +speed mode in the meson.build file, while the "quick" speed mode is +fine for functional tests that can be run without downloading files. +``make check`` then only runs the quick functional tests along with +the other quick tests from the other test suites. If you choose to +run only run ``make check-functional``, the "thorough" tests will be +executed, too. And to run all functional tests along with the others, +you can use something like:: + + make -j$(nproc) check SPEED=thorough + +The second problem with downloading files from the internet are time +constraints. The time for downloading files should not be taken into +account when the test is running and the timeout of the test is ticking +(since downloading can be very slow, depending on the network bandwidth). +This problem is solved by downloading the assets ahead of time, before +the tests are run. This pre-caching is done with the qemu_test.Asset +class. To use it in your test, declare an asset in your test class with +its URL and SHA256 checksum like this:: + + ASSET_somename = ( + ('https://www.qemu.org/assets/images/qemu_head_200.png'), + '34b74cad46ea28a2966c1d04e102510daf1fd73e6582b6b74523940d5da029dd') + +In your test function, you can then get the file name of the cached +asset like this:: + + def test_function(self): + file_path = self.ASSET_somename.fetch() + +The pre-caching will be done automatically when running +``make check-functional`` (but not when running e.g. +``make check-functional-``). In case you just want to download +the assets without running the tests, you can do so by running:: + + make precache-functional + +The cache is populated in the ``~/.cache/qemu/download`` directory by +default, but the location can be changed by setting the +``QEMU_TEST_CACHE_DIR`` environment variable. + +Skipping tests +-------------- + +Since the test framework is based on the common Python unittest framework, +you can use the usual Python decorators which allow for easily skipping +tests running under certain conditions, for example, on the lack of a binary +on the test system or when the running environment is a CI system. For further +information about those decorators, please refer to: + + https://docs.python.org/3/library/unittest.html#skipping-tests-and-expected-failures + +While the conditions for skipping tests are often specifics of each one, there +are recurring scenarios identified by the QEMU developers and the use of +environment variables became a kind of standard way to enable/disable tests. + +Here is a list of the most used variables: + +QEMU_TEST_ALLOW_LARGE_STORAGE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Tests which are going to fetch or produce assets considered *large* are not +going to run unless that ``QEMU_TEST_ALLOW_LARGE_STORAGE=1`` is exported on +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. + +QEMU_TEST_ALLOW_UNTRUSTED_CODE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +There are tests which will boot a kernel image or firmware that can be +considered not safe to run on the developer's workstation, thus they are +skipped by default. The definition of *not safe* is also arbitrary but +usually it means a blob which either its source or build process aren't +public available. + +You should export ``QEMU_TEST_ALLOW_UNTRUSTED_CODE=1`` on the environment in +order to allow tests which make use of those kind of assets. + +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:: + + # 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() + +Tests should not live in this state forever and should either be fixed +or eventually removed. + + +.. _unittest: https://docs.python.org/3/library/unittest.html diff --git a/docs/devel/fuzzing.rst b/docs/devel/testing/fuzzing.rst similarity index 91% rename from docs/devel/fuzzing.rst rename to docs/devel/testing/fuzzing.rst index 715330c856..c3ac084311 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/testing/fuzzing.rst @@ -19,18 +19,14 @@ 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 -such as out-of-bounds accesses, use-after-frees, double-frees etc.:: +Here, enable-asan and enable-ubsan are optional but they allow us to reliably +detect bugs such as out-of-bounds accesses, uses-after-free, double-frees +etc.:: - CC=clang-8 CXX=clang++-8 /path/to/configure --enable-fuzzing \ - --enable-sanitizers + CC=clang-8 CXX=clang++-8 /path/to/configure \ + --enable-fuzzing --enable-asan --enable-ubsan Fuzz targets are built similarly to system targets:: @@ -296,10 +292,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 +303,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/testing/index.rst b/docs/devel/testing/index.rst new file mode 100644 index 0000000000..1171f7db8f --- /dev/null +++ b/docs/devel/testing/index.rst @@ -0,0 +1,18 @@ +Testing QEMU +------------ + +Details about how to test QEMU and how it is integrated into our CI +testing infrastructure. + +.. toctree:: + :maxdepth: 3 + + main + qtest + functional + avocado + acpi-bits + ci + fuzzing + blkdebug + blkverify diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst new file mode 100644 index 0000000000..91f4dc61fb --- /dev/null +++ b/docs/devel/testing/main.rst @@ -0,0 +1,1018 @@ +.. _testing: + +Testing in QEMU +=============== + +QEMU's testing infrastructure is fairly complex as it covers +everything from unit testing and exercising specific sub-systems all +the way to full blown acceptance tests. To get an overview of the +tests you can run ``make check-help`` from either the source or build +tree. + +Most (but not all) tests are also integrated into the meson build +system so can be run directly from the build tree, for example: + +.. code:: + + [./pyvenv/bin/]meson test --suite qemu:softfloat + +will run just the softfloat tests. + +The rest of this document will cover the details for specific test +groups. + +Testing with "make check" +------------------------- + +The "make check" testing family includes most of the C based tests in QEMU. + +The usual way to run these tests is: + +.. code:: + + make check + +which includes QAPI schema tests, unit tests, QTests and some iotests. +Different sub-types of "make check" tests will be explained below. + +Before running tests, it is best to build QEMU programs first. Some tests +expect the executables to exist and will fail with obscure messages if they +cannot find them. + +Unit tests +~~~~~~~~~~ + +Unit tests, which can be invoked with ``make check-unit``, are simple C tests +that typically link to individual QEMU object files and exercise them by +calling exported functions. + +If you are writing new code in QEMU, consider adding a unit test, especially +for utility modules that are relatively stateless or have few dependencies. To +add a new unit test: + +1. Create a new source file. For example, ``tests/unit/foo-test.c``. + +2. Write the test. Normally you would include the header file which exports + the module API, then verify the interface behaves as expected from your + test. The test code should be organized with the glib testing framework. + Copying and modifying an existing test is usually a good idea. + +3. Add the test to ``tests/unit/meson.build``. The unit tests are listed in a + dictionary called ``tests``. The values are any additional sources and + dependencies to be linked with the test. For a simple test whose source + is in ``tests/unit/foo-test.c``, it is enough to add an entry like:: + + { + ... + 'foo-test': [], + ... + } + +Since unit tests don't require environment variables, the simplest way to debug +a unit test failure is often directly invoking it or even running it under +``gdb``. However there can still be differences in behavior between ``make`` +invocations and your manual run, due to ``$MALLOC_PERTURB_`` environment +variable (which affects memory reclamation and catches invalid pointers better) +and gtester options. If necessary, you can run + +.. code:: + + make check-unit V=1 + +and copy the actual command line which executes the unit test, then run +it from the command line. + +QTest +~~~~~ + +QTest is a device emulation testing framework. It can be very useful to test +device models; it could also control certain aspects of QEMU (such as virtual +clock stepping), with a special purpose "qtest" protocol. Refer to +:doc:`qtest` for more details. + +QTest cases can be executed with + +.. code:: + + 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 +~~~~~~~~~~~~~~~~~ + +The QAPI schema tests validate the QAPI parser used by QMP, by feeding +predefined input to the parser and comparing the result with the reference +output. + +The input/output data is managed under the ``tests/qapi-schema`` directory. +Each test case includes four files that have a common base name: + + * ``${casename}.json`` - the file contains the JSON input for feeding the + parser + * ``${casename}.out`` - the file contains the expected stdout from the parser + * ``${casename}.err`` - the file contains the expected stderr from the parser + * ``${casename}.exit`` - the expected error code + +Consider adding a new QAPI schema test when you are making a change on the QAPI +parser (either fixing a bug or extending/modifying the syntax). To do this: + +1. Add four files for the new case as explained above. For example: + + ``$EDITOR tests/qapi-schema/foo.{json,out,err,exit}``. + +2. Add the new test in ``tests/Makefile.include``. For example: + + ``qapi-schema += foo.json`` + +check-block +~~~~~~~~~~~ + +``make check-block`` runs a subset of the block layer iotests (the tests that +are in the "auto" group). +See the "QEMU iotests" section below for more information. + +QEMU iotests +------------ + +QEMU iotests, under the directory ``tests/qemu-iotests``, is the testing +framework widely used to test block layer related features. It is higher level +than "make check" tests and 99% of the code is written in bash or Python +scripts. The testing success criteria is golden output comparison, and the +test files are named with numbers. + +To run iotests, make sure QEMU is built successfully, then switch to the +``tests/qemu-iotests`` directory under the build directory, and run ``./check`` +with desired arguments from there. + +By default, "raw" format and "file" protocol is used; all tests will be +executed, except the unsupported ones. You can override the format and protocol +with arguments: + +.. code:: + + # test with qcow2 format + ./check -qcow2 + # or test a different protocol + ./check -nbd + +It's also possible to list test numbers explicitly: + +.. code:: + + # run selected cases with qcow2 format + ./check -qcow2 001 030 153 + +Cache mode can be selected with the "-c" option, which may help reveal bugs +that are specific to certain cache mode. + +More options are supported by the ``./check`` script, run ``./check -h`` for +help. + +Writing a new test case +~~~~~~~~~~~~~~~~~~~~~~~ + +Consider writing a tests case when you are making any changes to the block +layer. An iotest case is usually the choice for that. There are already many +test cases, so it is possible that extending one of them may achieve the goal +and save the boilerplate to create one. (Unfortunately, there isn't a 100% +reliable way to find a related one out of hundreds of tests. One approach is +using ``git grep``.) + +Usually an iotest case consists of two files. One is an executable that +produces output to stdout and stderr, the other is the expected reference +output. They are given the same number in file names. E.g. Test script ``055`` +and reference output ``055.out``. + +In rare cases, when outputs differ between cache mode ``none`` and others, a +``.out.nocache`` file is added. In other cases, when outputs differ between +image formats, more than one ``.out`` files are created ending with the +respective format names, e.g. ``178.out.qcow2`` and ``178.out.raw``. + +There isn't a hard rule about how to write a test script, but a new test is +usually a (copy and) modification of an existing case. There are a few +commonly used ways to create a test: + +* A Bash script. It will make use of several environmental variables related + to the testing procedure, and could source a group of ``common.*`` libraries + for some common helper routines. + +* A Python unittest script. Import ``iotests`` and create a subclass of + ``iotests.QMPTestCase``, then call ``iotests.main`` method. The downside of + this approach is that the output is too scarce, and the script is considered + harder to debug. + +* A simple Python script without using unittest module. This could also import + ``iotests`` for launching QEMU and utilities etc, but it doesn't inherit + from ``iotests.QMPTestCase`` therefore doesn't use the Python unittest + execution. This is a combination of 1 and 2. + +Pick the language per your preference since both Bash and Python have +comparable library support for invoking and interacting with QEMU programs. If +you opt for Python, it is strongly recommended to write Python 3 compatible +code. + +Both Python and Bash frameworks in iotests provide helpers to manage test +images. They can be used to create and clean up images under the test +directory. If no I/O or any protocol specific feature is needed, it is often +more convenient to use the pseudo block driver, ``null-co://``, as the test +image, which doesn't require image creation or cleaning up. Avoid system-wide +devices or files whenever possible, such as ``/dev/null`` or ``/dev/zero``. +Otherwise, image locking implications have to be considered. For example, +another application on the host may have locked the file, possibly leading to a +test failure. If using such devices are explicitly desired, consider adding +``locking=off`` option to disable image locking. + +Debugging a test case +~~~~~~~~~~~~~~~~~~~~~ + +The following options to the ``check`` script can be useful when debugging +a failing test: + +* ``-gdb`` wraps every QEMU invocation in a ``gdbserver``, which waits for a + connection from a gdb client. The options given to ``gdbserver`` (e.g. the + address on which to listen for connections) are taken from the ``$GDB_OPTIONS`` + environment variable. By default (if ``$GDB_OPTIONS`` is empty), it listens on + ``localhost:12345``. + It is possible to connect to it for example with + ``gdb -iex "target remote $addr"``, where ``$addr`` is the address + ``gdbserver`` listens on. + If the ``-gdb`` option is not used, ``$GDB_OPTIONS`` is ignored, + regardless of whether it is set or not. + +* ``-valgrind`` attaches a valgrind instance to QEMU. If it detects + warnings, it will print and save the log in + ``$TEST_DIR/.valgrind``. + The final command line will be ``valgrind --log-file=$TEST_DIR/ + .valgrind --error-exitcode=99 $QEMU ...`` + +* ``-d`` (debug) just increases the logging verbosity, showing + for example the QMP commands and answers. + +* ``-p`` (print) redirects QEMU’s stdout and stderr to the test output, + instead of saving it into a log file in + ``$TEST_DIR/qemu-machine-``. + +Test case groups +~~~~~~~~~~~~~~~~ + +"Tests may belong to one or more test groups, which are defined in the form +of a comment in the test source file. By convention, test groups are listed +in the second line of the test file, after the "#!/..." line, like this: + +.. code:: + + #!/usr/bin/env python3 + # group: auto quick + # + ... + +Another way of defining groups is creating the tests/qemu-iotests/group.local +file. This should be used only for downstream (this file should never appear +in upstream). This file may be used for defining some downstream test groups +or for temporarily disabling tests, like this: + +.. code:: + + # groups for some company downstream process + # + # ci - tests to run on build + # down - our downstream tests, not for upstream + # + # Format of each line is: + # TEST_NAME TEST_GROUP [TEST_GROUP ]... + + 013 ci + 210 disabled + 215 disabled + our-ugly-workaround-test down ci + +Note that the following group names have a special meaning: + +- quick: Tests in this group should finish within a few seconds. + +- auto: Tests in this group are used during "make check" and should be + runnable in any case. That means they should run with every QEMU binary + (also non-x86), with every QEMU configuration (i.e. must not fail if + an optional feature is not compiled in - but reporting a "skip" is ok), + work at least with the qcow2 file format, work with all kind of host + filesystems and users (e.g. "nobody" or "root") and must not take too + much memory and disk space (since CI pipelines tend to fail otherwise). + +- disabled: Tests in this group are disabled and ignored by check. + +.. _container-ref: + +Container based tests +--------------------- + +Introduction +~~~~~~~~~~~~ + +The container testing framework in QEMU utilizes public images to +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 many of the target +names and scripts are prefixed with "docker" the system will +automatically run on whichever is configured. + +The container images are also used to augment the generation of tests +for testing TCG. See :ref:`checktcg-ref` for more details. + +Docker Prerequisites +~~~~~~~~~~~~~~~~~~~~ + +Install "docker" with the system package manager and start the Docker service +on your development machine, then make sure you have the privilege to run +Docker commands. Typically it means setting up passwordless ``sudo docker`` +command or login as root. For example: + +.. code:: + + $ sudo yum install docker + $ # or `apt-get install docker` for Ubuntu, etc. + $ sudo systemctl start docker + $ sudo docker ps + +The last command should print an empty table, to verify the system is ready. + +An alternative method to set up permissions is by adding the current user to +"docker" group and making the docker daemon socket file (by default +``/var/run/docker.sock``) accessible to the group: + +.. code:: + + $ sudo groupadd docker + $ sudo usermod $USER -a -G docker + $ sudo chown :docker /var/run/docker.sock + +Note that any one of above configurations makes it possible for the user to +exploit the whole host with Docker bind mounting or other privileged +operations. So only do it on development machines. + +Podman Prerequisites +~~~~~~~~~~~~~~~~~~~~ + +Install "podman" with the system package manager. + +.. code:: + + $ sudo dnf install podman + $ podman ps + +The last command should print an empty table, to verify the system is ready. + +Quickstart +~~~~~~~~~~ + +From source tree, type ``make docker-help`` to see the help. Testing +can be started without configuring or building QEMU (``configure`` and +``make`` are done in the container, with parameters defined by the +make target): + +.. code:: + + make docker-test-build@debian + +This will create a container instance using the ``debian`` image (the image +is downloaded and initialized automatically), in which the ``test-build`` job +is executed. + +Registry +~~~~~~~~ + +The QEMU project has a container registry hosted by GitLab at +``registry.gitlab.com/qemu-project/qemu`` which will automatically be +used to pull in pre-built layers. This avoids unnecessary strain on +the distro archives created by multiple developers running the same +container build steps over and over again. This can be overridden +locally by using the ``NOCACHE`` build option: + +.. code:: + + make docker-image-debian-arm64-cross NOCACHE=1 + +Images +~~~~~~ + +Along with many other images, the ``debian`` image is defined in a Dockerfile +in ``tests/docker/dockerfiles/``, called ``debian.docker``. ``make docker-help`` +command will list all the available images. + +A ``.pre`` script can be added beside the ``.docker`` file, which will be +executed before building the image under the build context directory. This is +mainly used to do necessary host side setup. One such setup is ``binfmt_misc``, +for example, to make qemu-user powered cross build containers work. + +Most of the existing Dockerfiles were written by hand, simply by creating a +a new ``.docker`` file under the ``tests/docker/dockerfiles/`` directory. +This has led to an inconsistent set of packages being present across the +different containers. + +Thus going forward, QEMU is aiming to automatically generate the Dockerfiles +using the ``lcitool`` program provided by the ``libvirt-ci`` project: + + https://gitlab.com/libvirt/libvirt-ci + +``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: + + * Edit ``tests/lcitool/projects/qemu.yml`` and add the pre-requisite + + * Run ``make lcitool-refresh`` to re-generate all relevant build environment + manifests + +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 + + * 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 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 ``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. + +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 +^^^^^^^^^^^^^^^^^^^^^ + +In some cases ``libvirt-ci`` will not know about the OS distro that is +desired to be tested. Before adding a new OS distro, discuss the proposed +addition: + + * Send a mail to qemu-devel, copying people listed in the + MAINTAINERS file for ``Build and test automation``. + + There are limited CI compute resources available to QEMU, so the + cost/benefit tradeoff of adding new OS distros needs to be considered. + + * File an issue at https://gitlab.com/libvirt/libvirt-ci/-/issues + pointing to the qemu-devel mail thread in the archives. + + This alerts other people who might be interested in the work + to avoid duplication, as well as to get feedback from libvirt-ci + maintainers on any tips to ease the addition + +Assuming there is agreement to add a new OS distro then + + * Fork the ``libvirt-ci`` project on gitlab + + * 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 ``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 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 + 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. + + +Tests +~~~~~ + +Different tests are added to cover various configurations to build and test +QEMU. Docker tests are the executables under ``tests/docker`` named +``test-*``. They are typically shell scripts and are built on top of a shell +library, ``tests/docker/common.rc``, which provides helpers to find the QEMU +source and build it. + +The full list of tests is printed in the ``make docker-help`` help. + +Debugging a Docker test failure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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-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 + build QEMU and run tests from there, or press Ctrl-D to let the Docker + testing continue. +4. If you press Ctrl-D, the same building and testing procedure will begin, and + will hopefully run into the error again. After that, you will be dropped to + the prompt for debug. + +Options +~~~~~~~ + +Various options can be used to affect how Docker tests are done. The full +list is in the ``make docker`` help text. The frequently used ones are: + +* ``V=1``: the same as in top level ``make``. It will be propagated to the + container and enable verbose output. +* ``J=$N``: the number of parallel tasks in make commands in the container, + similar to the ``-j $N`` option in top level ``make``. (The ``-j`` option in + top level ``make`` will not be propagated into the container.) +* ``DEBUG=1``: enables debug. See the previous "Debugging a Docker test + failure" section. + +Thread Sanitizer +---------------- + +Thread Sanitizer (TSan) is a tool which can detect data races. QEMU supports +building and testing with this tool. + +For more information on TSan: + +https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual + +Thread Sanitizer in 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@ubuntu2204 + +TSan warnings under docker are placed in files located at build/tsan/. + +We recommend using DEBUG=1 to allow launching the test from inside the docker, +and to allow review of the warnings generated by TSan. + +Building and Testing with TSan +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to build and test with TSan, with a few additional steps. +These steps are normally done automatically in the docker. + +TSan is supported for clang and gcc. +One particularity of sanitizers is that all the code, including shared objects +dependencies, should be built with it. +In the case of TSan, any synchronization primitive from glib (GMutex for +instance) will not be recognized, and will lead to false positives. + +To build a tsan version of glib: + +.. code:: + + $ git clone --depth=1 --branch=2.81.0 https://github.com/GNOME/glib.git + $ cd glib + $ CFLAGS="-O2 -g -fsanitize=thread" meson build + $ ninja -C build + +To configure the build for TSan: + +.. code:: + + ../configure --enable-tsan \ + --disable-werror --extra-cflags="-O0" + +When executing qemu, don't forget to point to tsan glib: + +.. code:: + + $ glib_dir=/path/to/glib + $ export LD_LIBRARY_PATH=$glib_dir/build/gio:$glib_dir/build/glib:$glib_dir/build/gmodule:$glib_dir/build/gobject:$glib_dir/build/gthread + # check correct version is used + $ ldd build/qemu-x86_64 | grep glib + $ qemu-system-x86_64 ... + +The runtime behavior of TSAN is controlled by the TSAN_OPTIONS environment +variable. + +More information on the TSAN_OPTIONS can be found here: + +https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags + +For example: + +.. code:: + + export TSAN_OPTIONS=suppressions=/tests/tsan/suppressions.tsan \ + detect_deadlocks=false history_size=7 exitcode=0 \ + log_path=/tsan/tsan_warning + +The above exitcode=0 has TSan continue without error if any warnings are found. +This allows for running the test and then checking the warnings afterwards. +If you want TSan to stop and exit with error on warnings, use exitcode=66. + +TSan Suppressions +~~~~~~~~~~~~~~~~~ +Keep in mind that for any data race warning, although there might be a data race +detected by TSan, there might be no actual bug here. TSan provides several +different mechanisms for suppressing warnings. In general it is recommended +to fix the code if possible to eliminate the data race rather than suppress +the warning. + +A few important files for suppressing warnings are: + +tests/tsan/suppressions.tsan - Has TSan warnings we wish to suppress at runtime. +The comment on each suppression will typically indicate why we are +suppressing it. More information on the file format can be found here: + +https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions + +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/ignore.tsan" + +More information on the file format can be found here under "Blacklist Format": + +https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags + +TSan Annotations +~~~~~~~~~~~~~~~~ +include/qemu/tsan.h defines annotations. See this file for more descriptions +of the annotations themselves. Annotations can be used to suppress +TSan warnings or give TSan more information so that it can detect proper +relationships between accesses of data. + +Annotation examples can be found here: + +https://github.com/llvm/llvm-project/tree/master/compiler-rt/test/tsan/ + +Good files to start with are: annotate_happens_before.cpp and ignore_race.cpp + +The full set of annotations can be found here: + +https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp + +docker-binfmt-image-debian-% targets +------------------------------------ + +It is possible to combine Debian's bootstrap scripts with a configured +``binfmt_misc`` to bootstrap a number of Debian's distros including +experimental ports not yet supported by a released OS. This can +simplify setting up a rootfs by using docker to contain the foreign +rootfs rather than manually invoking chroot. + +Setting up ``binfmt_misc`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the script ``qemu-binfmt-conf.sh`` to configure a QEMU +user binary to automatically run binaries for the foreign +architecture. While the scripts will try their best to work with +dynamically linked QEMU's a statically linked one will present less +potential complications when copying into the docker image. Modern +kernels support the ``F`` (fix binary) flag which will open the QEMU +executable on setup and avoids the need to find and re-open in the +chroot environment. This is triggered with the ``--persistent`` flag. + +Example invocation +~~~~~~~~~~~~~~~~~~ + +For example to setup the HPPA ports builds of Debian:: + + make docker-binfmt-image-debian-sid-hppa \ + DEB_TYPE=sid DEB_ARCH=hppa \ + DEB_URL=http://ftp.ports.debian.org/debian-ports/ \ + DEB_KEYRING=/usr/share/keyrings/debian-ports-archive-keyring.gpg \ + EXECUTABLE=(pwd)/qemu-hppa V=1 + +The ``DEB_`` variables are substitutions used by +``debian-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``. + +VM testing +---------- + +This test suite contains scripts that bootstrap various guest images that have +necessary packages to build QEMU. The basic usage is documented in ``Makefile`` +help which is displayed with ``make vm-help``. + +Quickstart +~~~~~~~~~~ + +Run ``make vm-help`` to list available make targets. Invoke a specific make +command to run build test in an image. For example, ``make vm-build-freebsd`` +will build the source tree in the FreeBSD image. The command can be executed +from either the source tree or the build dir; if the former, ``./configure`` is +not needed. The command will then generate the test image in ``./tests/vm/`` +under the working directory. + +Note: images created by the scripts accept a well-known RSA key pair for SSH +access, so they SHOULD NOT be exposed to external interfaces if you are +concerned about attackers taking control of the guest and potentially +exploiting a QEMU security bug to compromise the host. + +QEMU binaries +~~~~~~~~~~~~~ + +By default, ``qemu-system-x86_64`` is searched in $PATH to run the guest. If +there isn't one, or if it is older than 2.10, the test won't work. In this case, +provide the QEMU binary in env var: ``QEMU=/path/to/qemu-2.10+``. + +Likewise the path to ``qemu-img`` can be set in QEMU_IMG environment variable. + +Make jobs +~~~~~~~~~ + +The ``-j$X`` option in the make command line is not propagated into the VM, +specify ``J=$X`` to control the make jobs in the guest. + +Debugging +~~~~~~~~~ + +Add ``DEBUG=1`` and/or ``V=1`` to the make command to allow interactive +debugging and verbose output. If this is not enough, see the next section. +``V=1`` will be propagated down into the make jobs in the guest. + +Manual invocation +~~~~~~~~~~~~~~~~~ + +Each guest script is an executable script with the same command line options. +For example to work with the netbsd guest, use ``$QEMU_SRC/tests/vm/netbsd``: + +.. code:: + + $ cd $QEMU_SRC/tests/vm + + # To bootstrap the image + $ ./netbsd --build-image --image /var/tmp/netbsd.img + <...> + + # To run an arbitrary command in guest (the output will not be echoed unless + # --debug is added) + $ ./netbsd --debug --image /var/tmp/netbsd.img uname -a + + # To build QEMU in guest + $ ./netbsd --debug --image /var/tmp/netbsd.img --build-qemu $QEMU_SRC + + # To get to an interactive shell + $ ./netbsd --interactive --image /var/tmp/netbsd.img sh + +Adding new guests +~~~~~~~~~~~~~~~~~ + +Please look at existing guest scripts for how to add new guests. + +Most importantly, create a subclass of BaseVM and implement ``build_image()`` +method and define ``BUILD_SCRIPT``, then finally call ``basevm.main()`` from +the script's ``main()``. + +* Usually in ``build_image()``, a template image is downloaded from a + predefined URL. ``BaseVM._download_with_cache()`` takes care of the cache and + the checksum, so consider using it. + +* Once the image is downloaded, users, SSH server and QEMU build deps should + be set up: + + - Root password set to ``BaseVM.ROOT_PASS`` + - User ``BaseVM.GUEST_USER`` is created, and password set to + ``BaseVM.GUEST_PASS`` + - SSH service is enabled and started on boot, + ``$QEMU_SRC/tests/keys/id_rsa.pub`` is added to ssh's ``authorized_keys`` + file of both root and the normal user + - DHCP client service is enabled and started on boot, so that it can + automatically configure the virtio-net-pci NIC and communicate with QEMU + user net (10.0.2.2) + - Necessary packages are installed to untar the source tarball and build + QEMU + +* Write a proper ``BUILD_SCRIPT`` template, which should be a shell script that + untars a raw virtio-blk block device, which is the tarball data blob of the + QEMU source tree, then configure/build it. Running "make check" is also + recommended. + +Image fuzzer testing +-------------------- + +An image fuzzer was added to exercise format drivers. Currently only qcow2 is +supported. To start the fuzzer, run + +.. code:: + + tests/image-fuzzer/runner.py -c '[["qemu-img", "info", "$test_img"]]' /tmp/test qcow2 + +Alternatively, some command different from ``qemu-img info`` can be tested, by +changing the ``-c`` option. + +Functional tests using Python +----------------------------- + +The ``tests/functional`` directory hosts functional tests written in +Python. You can run the functional tests simply by executing: + +.. code:: + + make check-functional + +See :ref:`checkfunctional-ref` for more details. + +Integration tests using the Avocado Framework +--------------------------------------------- + +The ``tests/avocado`` directory hosts integration tests. They're usually +higher level tests, and may interact with external resources and with +various guest operating systems. + +You can run the avocado tests simply by executing: + +.. code:: + + make check-avocado + +See :ref:`checkavocado-ref` for more details. + + +.. _checktcg-ref: + +Testing with "make check-tcg" +----------------------------- + +The check-tcg tests are intended for simple smoke tests of both +linux-user and softmmu TCG functionality. However to build test +programs for guest targets you need to have cross compilers available. +If your distribution supports cross compilers you can do something as +simple as:: + + apt install gcc-aarch64-linux-gnu + +The configure script will automatically pick up their presence. +Sometimes compilers have slightly odd names so the availability of +them can be prompted by passing in the appropriate configure option +for the architecture in question, for example:: + + $(configure) --cross-cc-aarch64=aarch64-cc + +There is also a ``--cross-cc-cflags-ARCH`` flag in case additional +compiler flags are needed to build for a given target. + +If you have the ability to run containers as the user the build system +will automatically use them where no system compiler is available. For +architectures where we also support building QEMU we will generally +use the same container to build tests. However there are a number of +additional containers defined that have a minimal cross-build +environment that is only suitable for building test cases. Sometimes +we may use a bleeding edge distribution for compiler features needed +for test cases that aren't yet in the LTS distros we support for QEMU +itself. + +See :ref:`container-ref` for more details. + +Running subset of tests +~~~~~~~~~~~~~~~~~~~~~~~ + +You can build the tests for one architecture:: + + make build-tcg-tests-$TARGET + +And run with:: + + make run-tcg-tests-$TARGET + +Adding ``V=1`` to the invocation will show the details of how to +invoke QEMU for the test which is useful for debugging tests. + +Running individual tests +~~~~~~~~~~~~~~~~~~~~~~~~ + +Tests can also be run directly from the test build directory. If you +run ``make help`` from the test build directory you will get a list of +all the tests that can be run. Please note that same binaries are used +in multiple tests, for example:: + + make run-plugin-test-mmap-with-libinline.so + +will run the mmap test with the ``libinline.so`` TCG plugin. The +gdbstub tests also re-use the test binaries but while exercising gdb. + +TCG test dependencies +~~~~~~~~~~~~~~~~~~~~~ + +The TCG tests are deliberately very light on dependencies and are +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. + +Other TCG Tests +--------------- + +There are a number of out-of-tree test suites that are used for more +extensive testing of processor features. + +KVM Unit Tests +~~~~~~~~~~~~~~ + +The KVM unit tests are designed to run as a Guest OS under KVM but +there is no reason why they can't exercise the TCG as well. It +provides a minimal OS kernel with hooks for enabling the MMU as well +as reporting test results via a special device:: + + https://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git + +Linux Test Project +~~~~~~~~~~~~~~~~~~ + +The LTP is focused on exercising the syscall interface of a Linux +kernel. It checks that syscalls behave as documented and strives to +exercise as many corner cases as possible. It is a useful test suite +to run to exercise QEMU's linux-user code:: + + https://linux-test-project.github.io/ + +GCC gcov support +---------------- + +``gcov`` is a GCC tool to analyze the testing coverage by +instrumenting the tested code. To use it, configure QEMU with +``--enable-gcov`` option and build. Then run the tests as usual. + +If you want to gather coverage information on a single test the ``make +clean-gcda`` target can be used to delete any existing coverage +information before running a single test. + +You can generate a HTML coverage report by executing ``make +coverage-html`` which will create +``meson-logs/coveragereport/index.html``. + +Further analysis can be conducted by running the ``gcov`` command +directly on the various .gcda output files. Please read the ``gcov`` +documentation for more information. diff --git a/docs/devel/qgraph.rst b/docs/devel/testing/qgraph.rst similarity index 100% rename from docs/devel/qgraph.rst rename to docs/devel/testing/qgraph.rst diff --git a/docs/devel/qtest.rst b/docs/devel/testing/qtest.rst similarity index 98% rename from docs/devel/qtest.rst rename to docs/devel/testing/qtest.rst index 0455aa06ab..c5b8546b3e 100644 --- a/docs/devel/qtest.rst +++ b/docs/devel/testing/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/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 2fefedcd98..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) { @@ -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 56814f02b3..57f55f6c54 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -14,8 +14,10 @@ # = Firmware ## -{ 'include' : 'machine.json' } -{ 'include' : 'block-core.json' } +{ 'pragma': { + 'member-name-exceptions': [ + 'FirmwareArchitecture' # x86_64 + ] } } ## # @FirmwareOSInterface: @@ -60,6 +62,27 @@ { 'enum' : 'FirmwareDevice', 'data' : [ 'flash', 'kernel', 'memory' ] } +## +# @FirmwareArchitecture: +# +# Enumeration of architectures for which Qemu uses additional +# firmware files. +# +# @aarch64: 64-bit Arm. +# +# @arm: 32-bit Arm. +# +# @i386: 32-bit x86. +# +# @loongarch64: 64-bit LoongArch. (since: 7.1) +# +# @x86_64: 64-bit x86. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareArchitecture', + 'data' : [ 'aarch64', 'arm', 'i386', 'loongarch64', 'x86_64' ] } + ## # @FirmwareTarget: # @@ -81,7 +104,7 @@ # Since: 3.0 ## { 'struct' : 'FirmwareTarget', - 'data' : { 'architecture' : 'SysEmuTarget', + 'data' : { 'architecture' : 'FirmwareArchitecture', 'machines' : [ 'str' ] } } ## @@ -200,6 +223,20 @@ 'enrolled-keys', 'requires-smm', 'secure-boot', 'verbose-dynamic', 'verbose-static' ] } +## +# @FirmwareFormat: +# +# Formats that are supported for firmware images. +# +# @raw: Raw disk image format. +# +# @qcow2: The QCOW2 image format. +# +# Since: 3.0 +## +{ 'enum': 'FirmwareFormat', + 'data': [ 'raw', 'qcow2' ] } + ## # @FirmwareFlashFile: # @@ -219,11 +256,11 @@ ## { 'struct' : 'FirmwareFlashFile', 'data' : { 'filename' : 'str', - 'format' : 'BlockdevDriver' } } + 'format' : 'FirmwareFormat' } } ## -# @FirmwareFlashType: +# @FirmwareFlashMode: # # Describes how the firmware build handles code versus variable # persistence. @@ -258,7 +295,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 @@ -267,13 +304,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 @@ -433,205 +470,205 @@ # # Since: 3.0 # -# Examples: +# .. qmp-example:: # -# { -# "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..999e44eae1 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -14,7 +14,11 @@ are useful for making QEMU interoperate with other software. dbus-vmstate dbus-display live-block-operations + nbd + parallels + prl-xml pr-helper + qmp-spec qemu-ga qemu-ga-ref qemu-qmp-ref @@ -23,3 +27,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 135784ab33..6b549ede7c 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 ============================ @@ -929,8 +931,8 @@ Shutdown the guest, by issuing the ``quit`` QMP command:: } -Live disk backup --- ``blockdev-backup`` and the deprecated``drive-backup`` ---------------------------------------------------------------------------- +Live disk backup --- ``blockdev-backup`` and the deprecated ``drive-backup`` +---------------------------------------------------------------------------- The ``blockdev-backup`` (and the deprecated ``drive-backup``) allows you to create a point-in-time snapshot. diff --git a/docs/interop/nbd.rst b/docs/interop/nbd.rst new file mode 100644 index 0000000000..de079d31fd --- /dev/null +++ b/docs/interop/nbd.rst @@ -0,0 +1,89 @@ +QEMU NBD protocol support +========================= + +QEMU supports the NBD protocol, and has an internal NBD client (see +``block/nbd.c``), an internal NBD server (see ``blockdev-nbd.c``), and an +external NBD server tool (see ``qemu-nbd.c``). The common code is placed +in ``nbd/*``. + +The NBD protocol is specified here: +https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md + +The following paragraphs describe some specific properties of NBD +protocol realization in QEMU. + +Metadata namespaces +------------------- + +QEMU supports the ``base:allocation`` metadata context as defined in the +NBD protocol specification, and also defines an additional metadata +namespace ``qemu``. + +``qemu`` namespace +------------------ + +The ``qemu`` namespace currently contains two available metadata context +types. The first is related to exposing the contents of a dirty +bitmap alongside the associated disk contents. That metadata context +is named with the following form:: + + qemu:dirty-bitmap: + +Each dirty-bitmap metadata context defines only one flag for extents +in reply for ``NBD_CMD_BLOCK_STATUS``: + +bit 0: + ``NBD_STATE_DIRTY``, set when the extent is "dirty" + +The second is related to exposing the source of various extents within +the image, with a single metadata context named:: + + qemu:allocation-depth + +In the allocation depth context, the entire 32-bit value represents a +depth of which layer in a thin-provisioned backing chain provided the +data (0 for unallocated, 1 for the active layer, 2 for the first +backing layer, and so forth). + +For ``NBD_OPT_LIST_META_CONTEXT`` the following queries are supported +in addition to the specific ``qemu:allocation-depth`` and +``qemu:dirty-bitmap:``: + +``qemu:`` + returns list of all available metadata contexts in the namespace +``qemu:dirty-bitmap:`` + returns list of all available dirty-bitmap metadata contexts + +Features by version +------------------- + +The following list documents which qemu version first implemented +various features (both as a server exposing the feature, and as a +client taking advantage of the feature when present), to make it +easier to plan for cross-version interoperability. Note that in +several cases, the initial release containing a feature may require +additional patches from the corresponding stable branch to fix bugs in +the operation of that feature. + +2.6 + ``NBD_OPT_STARTTLS`` with TLS X.509 Certificates +2.8 + ``NBD_CMD_WRITE_ZEROES`` +2.10 + ``NBD_OPT_GO``, ``NBD_INFO_BLOCK`` +2.11 + ``NBD_OPT_STRUCTURED_REPLY`` +2.12 + ``NBD_CMD_BLOCK_STATUS`` for ``base:allocation`` +3.0 + ``NBD_OPT_STARTTLS`` with TLS Pre-Shared Keys (PSK), + ``NBD_CMD_BLOCK_STATUS`` for ``qemu:dirty-bitmap:``, ``NBD_CMD_CACHE`` +4.2 + ``NBD_FLAG_CAN_MULTI_CONN`` for shareable read-only exports, + ``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/nbd.txt b/docs/interop/nbd.txt deleted file mode 100644 index f5ca25174a..0000000000 --- a/docs/interop/nbd.txt +++ /dev/null @@ -1,71 +0,0 @@ -QEMU supports the NBD protocol, and has an internal NBD client (see -block/nbd.c), an internal NBD server (see blockdev-nbd.c), and an -external NBD server tool (see qemu-nbd.c). The common code is placed -in nbd/*. - -The NBD protocol is specified here: -https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md - -The following paragraphs describe some specific properties of NBD -protocol realization in QEMU. - -= Metadata namespaces = - -QEMU supports the "base:allocation" metadata context as defined in the -NBD protocol specification, and also defines an additional metadata -namespace "qemu". - -== "qemu" namespace == - -The "qemu" namespace currently contains two available metadata context -types. The first is related to exposing the contents of a dirty -bitmap alongside the associated disk contents. That metadata context -is named with the following form: - - qemu:dirty-bitmap: - -Each dirty-bitmap metadata context defines only one flag for extents -in reply for NBD_CMD_BLOCK_STATUS: - - bit 0: NBD_STATE_DIRTY, set when the extent is "dirty" - -The second is related to exposing the source of various extents within -the image, with a single metadata context named: - - qemu:allocation-depth - -In the allocation depth context, the entire 32-bit value represents a -depth of which layer in a thin-provisioned backing chain provided the -data (0 for unallocated, 1 for the active layer, 2 for the first -backing layer, and so forth). - -For NBD_OPT_LIST_META_CONTEXT the following queries are supported -in addition to the specific "qemu:allocation-depth" and -"qemu:dirty-bitmap:": - -* "qemu:" - returns list of all available metadata contexts in the - namespace. -* "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap - metadata contexts. - -= Features by version = - -The following list documents which qemu version first implemented -various features (both as a server exposing the feature, and as a -client taking advantage of the feature when present), to make it -easier to plan for cross-version interoperability. Note that in -several cases, the initial release containing a feature may require -additional patches from the corresponding stable branch to fix bugs in -the operation of that feature. - -* 2.6: NBD_OPT_STARTTLS with TLS X.509 Certificates -* 2.8: NBD_CMD_WRITE_ZEROES -* 2.10: NBD_OPT_GO, NBD_INFO_BLOCK -* 2.11: NBD_OPT_STRUCTURED_REPLY -* 2.12: NBD_CMD_BLOCK_STATUS for "base:allocation" -* 3.0: NBD_OPT_STARTTLS with TLS Pre-Shared Keys (PSK), -NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE -* 4.2: NBD_FLAG_CAN_MULTI_CONN for shareable read-only exports, -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 diff --git a/docs/interop/parallels.rst b/docs/interop/parallels.rst new file mode 100644 index 0000000000..7b328a40c8 --- /dev/null +++ b/docs/interop/parallels.rst @@ -0,0 +1,240 @@ +Parallels Expandable Image File Format +====================================== + +.. + Copyright (c) 2015 Denis Lunev + Copyright (c) 2015 Vladimir Sementsov-Ogievskiy + + This work is licensed under the terms of the GNU GPL, version 2 or later. + See the COPYING file in the top-level directory. + + +A Parallels expandable image file consists of three consecutive parts: + +* header +* BAT +* data area + +All numbers in a Parallels expandable image are stored in little-endian byte +order. + + +Definitions +----------- + +Sector + A 512-byte data chunk. + +Cluster + A data chunk of the size specified in the image header. + Currently, the default size is 1MiB (2048 sectors). In previous + versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were used. + +BAT + Block Allocation Table, an entity that contains information for + guest-to-host I/O data address translation. + +Header +------ + +The header is placed at the start of an image and contains the following +fields:: + + Bytes: + 0 - 15: magic + Must contain "WithoutFreeSpace" or "WithouFreSpacExt". + + 16 - 19: version + Must be 2. + + 20 - 23: heads + Disk geometry parameter for guest. + + 24 - 27: cylinders + Disk geometry parameter for guest. + + 28 - 31: tracks + Cluster size, in sectors. + + 32 - 35: nb_bat_entries + Disk size, in clusters (BAT size). + + 36 - 43: nb_sectors + Disk size, in sectors. + + For "WithoutFreeSpace" images: + Only the lowest 4 bytes are used. The highest 4 bytes must be + cleared in this case. + + For "WithouFreSpacExt" images, there are no such + restrictions. + + 44 - 47: in_use + Set to 0x746F6E59 when the image is opened by software in R/W + mode; set to 0x312e3276 when the image is closed. + + A zero in this field means that the image was opened by an old + version of the software that doesn't support Format Extension + (see below). + + Other values are not allowed. + + 48 - 51: data_off + An offset, in sectors, from the start of the file to the start of + the data area. + + For "WithoutFreeSpace" images: + - If data_off is zero, the offset is calculated as the end of BAT + table plus some padding to ensure sector size alignment. + - If data_off is non-zero, the offset should be aligned to sector + size. However it is recommended to align it to cluster size for + newly created images. + + For "WithouFreSpacExt" images: + data_off must be non-zero and aligned to cluster size. + + 52 - 55: flags + Miscellaneous flags. + + Bit 0: Empty Image bit. If set, the image should be + considered clear. + + Bits 1-31: Unused. + + 56 - 63: ext_off + Format Extension offset, an offset, in sectors, from the start of + the file to the start of the Format Extension Cluster. + + ext_off must meet the same requirements as cluster offsets + defined by BAT entries (see below). + +BAT +--- + +BAT is placed immediately after the image header. In the file, BAT is a +contiguous array of 32-bit unsigned little-endian integers with +``(bat_entries * 4)`` bytes size. + +Each BAT entry contains an offset from the start of the file to the +corresponding cluster. The offset set in clusters for ``WithouFreSpacExt`` +images and in sectors for ``WithoutFreeSpace`` images. + +If a BAT entry is zero, the corresponding cluster is not allocated and should +be considered as filled with zeroes. + +Cluster offsets specified by BAT entries must meet the following requirements: + +- the value must not be lower than data offset (provided by ``header.data_off`` + or calculated as specified above) +- the value must be lower than the desired file size +- the value must be unique among all BAT entries +- the result of ``(cluster offset - data offset)`` must be aligned to + cluster size + +Data Area +--------- + +The data area is an area from the data offset (provided by ``header.data_off`` +or calculated as specified above) to the end of the file. It represents a +contiguous array of clusters. Most of them are allocated by the BAT, some may +be allocated by the ``ext_off`` field in the header while other may be +allocated by extensions. All clusters allocated by ``ext_off`` and extensions +should meet the same requirements as clusters specified by BAT entries. + + +Format Extension +---------------- + +The Format Extension is an area 1 cluster in size that provides additional +format features. This cluster is addressed by the ext_off field in the header. +The format of the Format Extension area is the following:: + + 0 - 7: magic + Must be 0xAB234CEF23DCEA87 + + 8 - 23: m_CheckSum + The MD5 checksum of the entire Header Extension cluster except + the first 24 bytes. + +The above are followed by feature sections or "extensions". The last +extension must be "End of features" (see below). + +Each feature section has the following format:: + + 0 - 7: magic + The identifier of the feature: + 0x0000000000000000 - End of features + 0x20385FAE252CB34A - Dirty bitmap + + 8 - 15: flags + External flags for extension: + + Bit 0: NECESSARY + If the software cannot load the extension (due to an + unknown magic number or error), the file should not be + changed. If this flag is unset and there is an error on + loading the extension, said extension should be dropped. + + Bit 1: TRANSIT + If there is an unknown extension with this flag set, + said extension should be left as is. + + If neither NECESSARY nor TRANSIT are set, the extension should be + dropped. + + 16 - 19: data_size + The size of the following feature data, in bytes. + + 20 - 23: unused32 + Align header to 8 bytes boundary. + + variable: data (data_size bytes) + +The above is followed by padding to the next 8 bytes boundary, then the +next extension starts. + +The last extension must be "End of features" with all the fields set to 0. + + +Dirty bitmaps feature +--------------------- + +This feature provides a way of storing dirty bitmaps in the image. The fields +of its data area are:: + + 0 - 7: size + The bitmap size, should be equal to disk size in sectors. + + 8 - 23: id + An identifier for backup consistency checking. + + 24 - 27: granularity + Bitmap granularity, in sectors. I.e., the number of sectors + corresponding to one bit of the bitmap. Granularity must be + a power of 2. + + 28 - 31: l1_size + The number of entries in the L1 table of the bitmap. + + variable: L1 offset table (l1_table), size: 8 * l1_size bytes + +The dirty bitmap described by this feature extension is stored in a set of +clusters inside the Parallels image file. The offsets of these clusters are +saved in the L1 offset table specified by the feature extension. Each L1 table +entry is a 64 bit integer as described below: + +Given an offset in bytes into the bitmap data, corresponding L1 entry is:: + + l1_table[offset / cluster_size] + +If an L1 table entry is 0, all bits in the corresponding cluster of the bitmap +are assumed to be 0. + +If an L1 table entry is 1, all bits in the corresponding cluster of the bitmap +are assumed to be 1. + +If an L1 table entry is not 0 or 1, it contains the corresponding cluster +offset (in 512b sectors). Given an offset in bytes into the bitmap data the +offset in bytes into the image file can be obtained as follows:: + + offset = l1_table[offset / cluster_size] * 512 + (offset % cluster_size) diff --git a/docs/interop/parallels.txt b/docs/interop/parallels.txt deleted file mode 100644 index bb3fadf369..0000000000 --- a/docs/interop/parallels.txt +++ /dev/null @@ -1,232 +0,0 @@ -= License = - -Copyright (c) 2015 Denis Lunev -Copyright (c) 2015 Vladimir Sementsov-Ogievskiy - -This work is licensed under the terms of the GNU GPL, version 2 or later. -See the COPYING file in the top-level directory. - -= Parallels Expandable Image File Format = - -A Parallels expandable image file consists of three consecutive parts: - * header - * BAT - * data area - -All numbers in a Parallels expandable image are stored in little-endian byte -order. - - -== Definitions == - - Sector A 512-byte data chunk. - - Cluster A data chunk of the size specified in the image header. - Currently, the default size is 1MiB (2048 sectors). In previous - versions, cluster sizes of 63 sectors, 256 and 252 kilobytes were - used. - - BAT Block Allocation Table, an entity that contains information for - guest-to-host I/O data address translation. - - -== Header == - -The header is placed at the start of an image and contains the following -fields: - -Bytes: - 0 - 15: magic - Must contain "WithoutFreeSpace" or "WithouFreSpacExt". - - 16 - 19: version - Must be 2. - - 20 - 23: heads - Disk geometry parameter for guest. - - 24 - 27: cylinders - Disk geometry parameter for guest. - - 28 - 31: tracks - Cluster size, in sectors. - - 32 - 35: nb_bat_entries - Disk size, in clusters (BAT size). - - 36 - 43: nb_sectors - Disk size, in sectors. - - For "WithoutFreeSpace" images: - Only the lowest 4 bytes are used. The highest 4 bytes must be - cleared in this case. - - For "WithouFreSpacExt" images, there are no such - restrictions. - - 44 - 47: in_use - Set to 0x746F6E59 when the image is opened by software in R/W - mode; set to 0x312e3276 when the image is closed. - - A zero in this field means that the image was opened by an old - version of the software that doesn't support Format Extension - (see below). - - Other values are not allowed. - - 48 - 51: data_off - An offset, in sectors, from the start of the file to the start of - the data area. - - For "WithoutFreeSpace" images: - - If data_off is zero, the offset is calculated as the end of BAT - table plus some padding to ensure sector size alignment. - - If data_off is non-zero, the offset should be aligned to sector - size. However it is recommended to align it to cluster size for - newly created images. - - For "WithouFreSpacExt" images: - data_off must be non-zero and aligned to cluster size. - - 52 - 55: flags - Miscellaneous flags. - - Bit 0: Empty Image bit. If set, the image should be - considered clear. - - Bits 1-31: Unused. - - 56 - 63: ext_off - Format Extension offset, an offset, in sectors, from the start of - the file to the start of the Format Extension Cluster. - - ext_off must meet the same requirements as cluster offsets - defined by BAT entries (see below). - - -== BAT == - -BAT is placed immediately after the image header. In the file, BAT is a -contiguous array of 32-bit unsigned little-endian integers with -(bat_entries * 4) bytes size. - -Each BAT entry contains an offset from the start of the file to the -corresponding cluster. The offset set in clusters for "WithouFreSpacExt" images -and in sectors for "WithoutFreeSpace" images. - -If a BAT entry is zero, the corresponding cluster is not allocated and should -be considered as filled with zeroes. - -Cluster offsets specified by BAT entries must meet the following requirements: - - the value must not be lower than data offset (provided by header.data_off - or calculated as specified above), - - the value must be lower than the desired file size, - - the value must be unique among all BAT entries, - - the result of (cluster offset - data offset) must be aligned to cluster - size. - - -== Data Area == - -The data area is an area from the data offset (provided by header.data_off or -calculated as specified above) to the end of the file. It represents a -contiguous array of clusters. Most of them are allocated by the BAT, some may -be allocated by the ext_off field in the header while other may be allocated by -extensions. All clusters allocated by ext_off and extensions should meet the -same requirements as clusters specified by BAT entries. - - -== Format Extension == - -The Format Extension is an area 1 cluster in size that provides additional -format features. This cluster is addressed by the ext_off field in the header. -The format of the Format Extension area is the following: - - 0 - 7: magic - Must be 0xAB234CEF23DCEA87 - - 8 - 23: m_CheckSum - The MD5 checksum of the entire Header Extension cluster except - the first 24 bytes. - - The above are followed by feature sections or "extensions". The last - extension must be "End of features" (see below). - -Each feature section has the following format: - - 0 - 7: magic - The identifier of the feature: - 0x0000000000000000 - End of features - 0x20385FAE252CB34A - Dirty bitmap - - 8 - 15: flags - External flags for extension: - - Bit 0: NECESSARY - If the software cannot load the extension (due to an - unknown magic number or error), the file should not be - changed. If this flag is unset and there is an error on - loading the extension, said extension should be dropped. - - Bit 1: TRANSIT - If there is an unknown extension with this flag set, - said extension should be left as is. - - If neither NECESSARY nor TRANSIT are set, the extension should be - dropped. - - 16 - 19: data_size - The size of the following feature data, in bytes. - - 20 - 23: unused32 - Align header to 8 bytes boundary. - - variable: data (data_size bytes) - - The above is followed by padding to the next 8 bytes boundary, then the - next extension starts. - - The last extension must be "End of features" with all the fields set to 0. - - -=== Dirty bitmaps feature === - -This feature provides a way of storing dirty bitmaps in the image. The fields -of its data area are: - - 0 - 7: size - The bitmap size, should be equal to disk size in sectors. - - 8 - 23: id - An identifier for backup consistency checking. - - 24 - 27: granularity - Bitmap granularity, in sectors. I.e., the number of sectors - corresponding to one bit of the bitmap. Granularity must be - a power of 2. - - 28 - 31: l1_size - The number of entries in the L1 table of the bitmap. - - variable: L1 offset table (l1_table), size: 8 * l1_size bytes - -The dirty bitmap described by this feature extension is stored in a set of -clusters inside the Parallels image file. The offsets of these clusters are -saved in the L1 offset table specified by the feature extension. Each L1 table -entry is a 64 bit integer as described below: - -Given an offset in bytes into the bitmap data, corresponding L1 entry is - - l1_table[offset / cluster_size] - -If an L1 table entry is 0, all bits in the corresponding cluster of the bitmap -are assumed to be 0. - -If an L1 table entry is 1, all bits in the corresponding cluster of the bitmap -are assumed to be 1. - -If an L1 table entry is not 0 or 1, it contains the corresponding cluster -offset (in 512b sectors). Given an offset in bytes into the bitmap data the -offset in bytes into the image file can be obtained as follows: - - offset = l1_table[offset / cluster_size] * 512 + (offset % cluster_size) diff --git a/docs/interop/prl-xml.rst b/docs/interop/prl-xml.rst new file mode 100644 index 0000000000..5bb63bb93a --- /dev/null +++ b/docs/interop/prl-xml.rst @@ -0,0 +1,192 @@ +Parallels Disk Format +===================== + +.. + Copyright (c) 2015-2017, Virtuozzo, Inc. + Authors: + 2015 Denis Lunev + 2015 Vladimir Sementsov-Ogievskiy + 2016-2017 Klim Kireev + 2016-2017 Edgar Kaziakhmedov + + 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 specification contains minimal information about Parallels Disk Format, +which is enough to properly work with QEMU. Nevertheless, Parallels Cloud Server +and Parallels Desktop are able to add some unspecified nodes to the xml and use +them, but they are for internal work and don't affect functionality. Also it +uses auxiliary xml ``Snapshot.xml``, which allows storage of optional snapshot +information, but this doesn't influence open/read/write functionality. QEMU and +other software should not use fields not covered in this document or the +``Snapshot.xml`` file, and must leave them as is. + +A Parallels disk consists of two parts: the set of snapshots and the disk +descriptor file, which stores information about all files and snapshots. + +Definitions +----------- + +Snapshot + a record of the contents captured at a particular time, capable + of storing current state. A snapshot has a UUID and a parent UUID. + +Snapshot image + an overlay representing the difference between this + snapshot and some earlier snapshot. + +Overlay + an image storing the different sectors between two captured states. + +Root image + a snapshot image with no parent, the root of the snapshot tree. + +Storage + the backing storage for a subset of the virtual disk. When + there is more than one storage in a Parallels disk then that + is referred to as a split image. In this case every storage + covers a specific address space area of the disk and has its + particular root image. Split images are not considered here + and are not supported. Each storage consists of disk + parameters and a list of images. The list of images always + contains a root image and may also contain overlays. The + root image can be an expandable Parallels image file or + plain. Overlays must be expandable. + +Description file + ``DiskDescriptor.xml`` stores information about disk parameters, + snapshots, and storages. + +Top Snapshot + The overlay between actual state and some previous snapshot. + It is not a snapshot in the classical sense because it + serves as the active image that the guest writes to. + +Sector + a 512-byte data chunk. + +Description file +---------------- + +All information is placed in a single XML element +``Parallels_disk_image``. +The element has only one attribute, ``Version``, which must be ``1.0``. + +The schema of ``DiskDescriptor.xml``:: + + + + ... + + + ... + + + ... + + + +``Disk_Parameters`` element +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``Disk_Parameters`` element describes the physical layout of the +virtual disk and some general settings. + +The ``Disk_Parameters`` element MUST contain the following child elements: + +* ``Disk_size`` - number of sectors in the disk, + desired size of the disk. +* ``Cylinders`` - number of the disk cylinders. +* ``Heads`` - number of the disk heads. +* ``Sectors`` - number of the disk sectors per cylinder + (sector size is 512 bytes) + Limitation: The product of the ``Heads``, ``Sectors`` and ``Cylinders`` + values MUST be equal to the value of the Disk_size parameter. +* ``Padding`` - must be 0. Parallels Cloud Server and Parallels Desktop may + use padding set to 1; however this case is not covered + by this specification. QEMU and other software should not open + such disks and should not create them. + +``StorageData`` element +^^^^^^^^^^^^^^^^^^^^^^^ + +This element of the file describes the root image and all snapshot images. + +The ``StorageData`` element consists of the ``Storage`` child element, +as shown below:: + + + + ... + + + +A ``Storage`` element has the following child elements: + +* ``Start`` - start sector of the storage, in case of non split storage + equals to 0. +* ``End`` - number of sector following the last sector, in case of non + split storage equals to ``Disk_size``. +* ``Blocksize`` - storage cluster size, number of sectors per one cluster. + The cluster size for each "Compressed" (see below) image in + a parallels disk must be equal to this field. Note: the cluster + size for a Parallels Expandable Image is in the ``tracks`` field of + its header (see :doc:`parallels`). +* Several ``Image`` child elements. + +Each ``Image`` element has the following child elements: + +* ``GUID`` - image identifier, UUID in curly brackets. + For instance, ``{12345678-9abc-def1-2345-6789abcdef12}.`` + The GUID is used by the Snapshots element to reference images + (see below) +* ``Type`` - image type of the element. It can be: + + * ``Plain`` for raw files. + * ``Compressed`` for expanding disks. + +* ``File`` - path to image file. The path can be relative to + ``DiskDescriptor.xml`` or absolute. + +``Snapshots`` element +^^^^^^^^^^^^^^^^^^^^^ + +The ``Snapshots`` element describes the snapshot relations with the snapshot tree. + +The element contains the set of ``Shot`` child elements, as shown below:: + + + ... /* Optional child element */ + + ... + + + ... + + ... + + +Each ``Shot`` element contains the following child elements: + +* ``GUID`` - an image GUID. +* ``ParentGUID`` - GUID of the image of the parent snapshot. + +The software may traverse snapshots from child to parent using the +```` field as reference. The ``ParentGUID`` of the root +snapshot is ``{00000000-0000-0000-0000-000000000000}``. +There should be only one root snapshot. + +The Top snapshot could be +described via two ways: via the ``TopGUID`` child +element of the ``Snapshots`` element, or via the predefined GUID +``{5fbaabe3-6958-40ff-92a7-860e329aab41}``. If ``TopGUID`` is defined, +the predefined GUID is interpreted as a normal GUID. All snapshot images +(except the Top Snapshot) should be +opened read-only. + +There is another predefined GUID, +``BackupID = {704718e1-2314-44c8-9087-d78ed36b0f4e}``, which is used by +original and some third-party software for backup. QEMU and other +software may operate with images with ``GUID = BackupID`` as usual. +However, it is not recommended to use this +GUID for new disks. The Top snapshot cannot have this GUID. diff --git a/docs/interop/prl-xml.txt b/docs/interop/prl-xml.txt deleted file mode 100644 index 7031f8752c..0000000000 --- a/docs/interop/prl-xml.txt +++ /dev/null @@ -1,158 +0,0 @@ -= License = - -Copyright (c) 2015-2017, Virtuozzo, Inc. -Authors: - 2015 Denis Lunev - 2015 Vladimir Sementsov-Ogievskiy - 2016-2017 Klim Kireev - 2016-2017 Edgar Kaziakhmedov - -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 specification contains minimal information about Parallels Disk Format, -which is enough to proper work with QEMU. Nevertheless, Parallels Cloud Server -and Parallels Desktop are able to add some unspecified nodes to xml and use -them, but they are for internal work and don't affect functionality. Also it -uses auxiliary xml "Snapshot.xml", which allows to store optional snapshot -information, but it doesn't influence open/read/write functionality. QEMU and -other software should not use fields not covered in this document and -Snapshot.xml file and must leave them as is. - -= Parallels Disk Format = - -Parallels disk consists of two parts: the set of snapshots and the disk -descriptor file, which stores information about all files and snapshots. - -== Definitions == - Snapshot a record of the contents captured at a particular time, - capable of storing current state. A snapshot has UUID and - parent UUID. - - Snapshot image an overlay representing the difference between this - snapshot and some earlier snapshot. - - Overlay an image storing the different sectors between two captured - states. - - Root image snapshot image with no parent, the root of snapshot tree. - - Storage the backing storage for a subset of the virtual disk. When - there is more than one storage in a Parallels disk then that - is referred to as a split image. In this case every storage - covers specific address space area of the disk and has its - particular root image. Split images are not considered here - and are not supported. Each storage consists of disk - parameters and a list of images. The list of images always - contains a root image and may also contain overlays. The - root image can be an expandable Parallels image file or - plain. Overlays must be expandable. - - Description DiskDescriptor.xml stores information about disk parameters, - file snapshots, storages. - - Top The overlay between actual state and some previous snapshot. - Snapshot It is not a snapshot in the classical sense because it - serves as the active image that the guest writes to. - - Sector a 512-byte data chunk. - -== Description file == -All information is placed in a single XML element Parallels_disk_image. -The element has only one attribute "Version", that must be 1.0. -Schema of DiskDescriptor.xml: - - - - ... - - - ... - - - ... - - - -== Disk_Parameters element == -The Disk_Parameters element describes the physical layout of the virtual disk -and some general settings. - -The Disk_Parameters element MUST contain the following child elements: - * Disk_size - number of sectors in the disk, - desired size of the disk. - * Cylinders - number of the disk cylinders. - * Heads - number of the disk heads. - * Sectors - number of the disk sectors per cylinder - (sector size is 512 bytes) - Limitation: Product of the Heads, Sectors and Cylinders - values MUST be equal to the value of the Disk_size parameter. - * Padding - must be 0. Parallels Cloud Server and Parallels Desktop may - use padding set to 1, however this case is not covered - by this spec, QEMU and other software should not open - such disks and should not create them. - -== StorageData element == -This element of the file describes the root image and all snapshot images. - -The StorageData element consists of the Storage child element, as shown below: - - - ... - - - -A Storage element has following child elements: - * Start - start sector of the storage, in case of non split storage - equals to 0. - * End - number of sector following the last sector, in case of non - split storage equals to Disk_size. - * Blocksize - storage cluster size, number of sectors per one cluster. - Cluster size for each "Compressed" (see below) image in - parallels disk must be equal to this field. Note: cluster - size for Parallels Expandable Image is in 'tracks' field of - its header (see docs/interop/parallels.txt). - * Several Image child elements. - -Each Image element has following child elements: - * GUID - image identifier, UUID in curly brackets. - For instance, {12345678-9abc-def1-2345-6789abcdef12}. - The GUID is used by the Snapshots element to reference images - (see below) - * 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 - absolute. - -== Snapshots element == -The Snapshots element describes the snapshot relations with the snapshot tree. - -The element contains the set of Shot child elements, as shown below: - - ... /* Optional child element */ - - ... - - - ... - - ... - - -Each Shot element contains the following child elements: - * GUID - an image GUID. - * ParentGUID - GUID of the image of the parent snapshot. - -The software may traverse snapshots from child to parent using -field as reference. ParentGUID of root snapshot is -{00000000-0000-0000-0000-000000000000}. There should be only one root -snapshot. Top snapshot could be described via two ways: via TopGUID child -element of the Snapshots element or via predefined GUID -{5fbaabe3-6958-40ff-92a7-860e329aab41}. If TopGUID is defined, predefined GUID is -interpreted as usual GUID. All snapshot images (except Top Snapshot) should be -opened read-only. There is another predefined GUID, -BackupID = {704718e1-2314-44c8-9087-d78ed36b0f4e}, which is used by original and -some third-party software for backup, QEMU and other software may operate with -images with GUID = BackupID as usual, however, it is not recommended to use this -GUID for new disks. Top snapshot cannot have this GUID. 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 a9183802d1..11f7bae460 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -28,11 +28,30 @@ configuration options on the command line. For the same key, the last option wins, but the lists accumulate (see below for configuration file format). +If an allowed RPCs list is defined in the configuration, then all +RPCs will be blocked by default, except for the allowed list. + +If a blocked RPCs list is defined in the configuration, then all +RPCs will be allowed by default, except for the blocked list. + +If both allowed and blocked RPCs lists are defined in the configuration, +then all RPCs will be blocked by default, then the allowed list will +be applied, followed by the blocked list. + +While filesystems are frozen, all except for a designated safe set +of RPCs will blocked, regardless of what the general configuration +declares. + Options ------- .. program:: qemu-ga +.. option:: -c, --config=PATH + + Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``, + unless overridden by the QGA_CONF environment variable) + .. option:: -m, --method=METHOD Transport method: one of ``unix-listen``, ``virtio-serial``, or @@ -81,8 +100,13 @@ Options .. option:: -b, --block-rpcs=LIST - Comma-separated list of RPCs to disable (no spaces, use ``help`` 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 @@ -126,6 +150,7 @@ fsfreeze-hook string statedir string verbose boolean block-rpcs string list +allow-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.rst b/docs/interop/qmp-spec.rst new file mode 100644 index 0000000000..563344160e --- /dev/null +++ b/docs/interop/qmp-spec.rst @@ -0,0 +1,439 @@ +.. + 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 +=================================== + +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. 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`. + +.. 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 +"Client" is any application communicating with it via QMP. + +JSON data structures, when mentioned in this document, are always in the +following format: + + json-DATA-STRUCTURE-NAME + +Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined +by the `JSON standard `_. + +The server expects its input to be encoded in UTF-8, and sends its +output encoded in ASCII. + +For convenience, json-object members mentioned in this document will +be in a certain order. However, in real protocol usage they can be in +ANY order, thus no particular order should be assumed. On the other +hand, use of json-array elements presumes that preserving order is +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"`` +json-string, and both input forms of strings understand an additional +escape sequence of ``\'`` for a single quote. The server will only use +double quoting on output. + +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 +`Capabilities Negotiation`_). + +The greeting message format is: + +:: + + { "QMP": { "version": json-object, "capabilities": json-array } } + +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. + +Capabilities +------------ + +Currently supported capabilities are: + +``oob`` + the QMP server supports "out-of-band" (OOB) command + execution, as described in section `Out-of-band execution`_. + +Issuing Commands +---------------- + +The format for command execution is: + +:: + + { "execute": json-string, "arguments": json-object, "id": json-value } + +or + +:: + + { "exec-oob": json-string, "arguments": json-object, "id": json-value } + +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 + 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 + 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 + incremented for each successive command works fine. + +The actual commands are documented in the :doc:`qemu-qmp-ref`. + +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 `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 +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``. + +If the client sends in-band commands faster than the server can +execute them, the server will stop reading requests until the request +queue length is reduced to an acceptable range. + +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 the output of ``query-qmp-schema``. + +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 +so that requests and responses can match. Clients should drop all the +responses that have an unknown ``id`` field. + +Success +------- + +The format of a success response is: + +:: + + { "return": json-value, "id": json-value } + +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. + +Error +----- + +The format of an error response is: + +:: + + { "error": { "class": json-string, "desc": json-string }, "id": json-value } + +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. + +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. + +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 +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: + +- 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 :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. + +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). + +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. + +QGA Synchronization +------------------- + +When a client connects to QGA over a transport lacking proper +connection semantics such as virtio-serial, QGA may have read partial +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 +details. + + +QMP Examples +============ + +This section provides some examples of real QMP usage, in all of them +``->`` marks text sent by the Client and ``<-`` marks replies by the Server. + +.. admonition:: Example + + Server greeting + + .. code-block:: QMP + + <- { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3}, + "package": "v3.0.0"}, "capabilities": ["oob"] } } + +.. admonition:: Example + + Capabilities negotiation + + .. code-block:: QMP + + -> { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } } + <- { "return": {}} + +.. admonition:: Example + + Simple 'stop' execution + + .. code-block:: QMP + + -> { "execute": "stop" } + <- { "return": {} } + +.. admonition:: Example + + 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" } } + + +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 +messages are not delivered either. + +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 Command mode where capabilities changes take +effect, all commands (except ``qmp_capabilities``) are allowed and asynchronous +messages are delivered. + +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 (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 +generates an error if a value does not have the expected type for its +key, or if it does not understand a key that the Client included. The +strictness of the Server catches wrong assumptions of Clients about +the Server's schema. Clients can assume that, when such validation +errors occur, they will be reported before the command generated any +side effect. + +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 +- 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, +and may be withdrawn or changed in an incompatible manner in a future +release. + +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". + +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 +versions of QMP without special logic, and downstream extensions are +inherently at odds with that. + +However, we recognize that it is sometimes impossible for downstreams to +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 +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 +ensure compatibility with other downstreams, it is strongly +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`_ other than +to offer additional capabilities. But see below for why even that is +discouraged. + +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 +with downstream names to its output. + +Thus, a client should not be able to distinguish downstream from +upstream as long as it doesn't send input with downstream members, and +properly ignores any downstream members in the output it receives. + +Advice on downstream modifications: + +1. Introducing new commands is okay. If you want to extend an existing + command, consider introducing a new one with the new behaviour + instead. + +2. Introducing new asynchronous messages is okay. If you want to extend + an existing message, consider adding a new one instead. + +3. Introducing new errors for use in new commands is okay. Adding new + errors to existing commands counts as extension, so 1. applies. + +4. New capabilities are strongly discouraged. Capabilities are for + evolving the basic protocol, and multiple diverging basic protocol + dialects are most undesirable. diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt deleted file mode 100644 index b0e8351d5b..0000000000 --- a/docs/interop/qmp-spec.txt +++ /dev/null @@ -1,406 +0,0 @@ - QEMU Machine Protocol Specification - -0. About This Document -====================== - -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. - -1. Introduction -=============== - -This document specifies the QEMU Machine Protocol (QMP), 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. - -2. Protocol Specification -========================= - -This section details the protocol format. For the purpose of this -document, "Server" is either QEMU or the QEMU Guest Agent, and -"Client" is any application communicating with it via QMP. - -JSON data structures, when mentioned in this document, are always in the -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 - -The server expects its input to be encoded in UTF-8, and sends its -output encoded in ASCII. - -For convenience, json-object members mentioned in this document will -be in a certain order. However, in real protocol usage they can be in -ANY order, thus no particular order should be assumed. On the other -hand, use of json-array elements presumes that preserving order is -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" -json-string, and both input forms of strings understand an additional -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 -------------------- - -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'). - -The greeting message format is: - -{ "QMP": { "version": json-object, "capabilities": json-array } } - - Where, - -- 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 - baseline specification; the order of elements in this array has no - particular significance. - -2.2.1 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". - -2.3 Issuing Commands --------------------- - -The format for command execution is: - -{ "execute": json-string, "arguments": json-object, "id": json-value } - -or - -{ "exec-oob": json-string, "arguments": json-object, "id": json-value } - - 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 - 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 - 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 - 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}. - -2.3.1 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 -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 -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". - -If the client sends in-band commands faster than the server can -execute them, the server will stop reading requests until the request -queue length is reduced to an acceptable range. - -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. - -2.4 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 -so that requests and responses can match. Clients should drop all the -responses that have an unknown "id" field. - -2.4.1 success -------------- - -The format of a success response is: - -{ "return": json-value, "id": json-value } - - 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 - -2.4.2 error ------------ - -The format of an error response is: - -{ "error": { "class": json-string, "desc": json-string }, "id": json-value } - - 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 - -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 ------------------------ - -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 -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, - -- 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}. - -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 -------------------------------------------------- - -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). - -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 ------------------------ - -When a client connects to QGA over a transport lacking proper -connection semantics such as virtio-serial, QGA may have read partial -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 -details. - - -3. QMP Examples -=============== - -This section provides some examples of real QMP usage, in all of them -"C" stands for "Client" and "S" stands for "Server". - -3.1 Server greeting -------------------- - -S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3}, - "package": "v3.0.0"}, "capabilities": ["oob"] } } - -3.2 Capabilities negotiation ----------------------------- - -C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } } -S: { "return": {}} - -3.3 Simple 'stop' execution ---------------------------- - -C: { "execute": "stop" } -S: { "return": {} } - -3.4 KVM information -------------------- - -C: { "execute": "query-kvm", "id": "example" } -S: { "return": { "enabled": true, "present": true }, "id": "example"} - -3.5 Parsing error ------------------- - -C: { "execute": } -S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } - -3.6 Powerdown event -------------------- - -S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, - "event": "POWERDOWN" } - -3.7 Out-of-band execution -------------------------- - -C: { "exec-oob": "migrate-pause", "id": 42 } -S: { "id": 42, - "error": { "class": "GenericError", - "desc": "migrate-pause is currently only supported during postcopy-active state" } } - - -4. 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 -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. - -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 -messages are delivered. - -5 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 -that array and enable the capabilities they support. - -The QMP Server performs a type check on the arguments to a command. It -generates an error if a value does not have the expected type for its -key, or if it does not understand a key that the Client included. The -strictness of the Server catches wrong assumptions of Clients about -the Server's schema. Clients can assume that, when such validation -errors occur, they will be reported before the command generated any -side effect. - -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. -- 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, -and may be withdrawn or changed in an incompatible manner in a future -release. - -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 -============================== - -We recommend that downstream consumers of QEMU do *not* modify QMP. -Management tools should be able to support both upstream and downstream -versions of QMP without special logic, and downstream extensions are -inherently at odds with that. - -However, we recognize that it is sometimes impossible for downstreams to -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 -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 -ensure compatibility with other downstreams, it is strongly -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 -to offer additional capabilities. But see below for why even that is -discouraged. - -Section '5 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 -with downstream names to its output. - -Thus, a client should not be able to distinguish downstream from -upstream as long as it doesn't send input with downstream members, and -properly ignores any downstream members in the output it receives. - -Advice on downstream modifications: - -1. Introducing new commands is okay. If you want to extend an existing - command, consider introducing a new one with the new behaviour - instead. - -2. Introducing new asynchronous messages is okay. If you want to extend - an existing message, consider adding a new one instead. - -3. Introducing new errors for use in new commands is okay. Adding new - errors to existing commands counts as extension, so 1. applies. - -4. New capabilities are strongly discouraged. Capabilities are for - evolving the basic protocol, and multiple diverging basic protocol - dialects are most undesirable. 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 3f18ab424e..2e50f2ddfa 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,10 @@ 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 -^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. _memory_region_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 | @@ -153,24 +182,51 @@ A region is: :user address: a 64-bit user address -:mmap offset: 64-bit offset where region starts in the mapped memory +:mmap offset: a 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: a 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 +:region: region is represented by :ref:`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 + +:regions: regions field contains 8 regions of type :ref:`Memory region description `. Log description ^^^^^^^^^^^^^^^ @@ -179,9 +235,9 @@ Log description | log size | log offset | +----------+------------+ -:log size: size of area used for logging +:log size: a 64-bit size of area used for logging -:log offset: offset from start of supplied file descriptor where +:log offset: a 64-bit offset from start of supplied file descriptor where logging starts (i.e. where guest address 0 would be logged) @@ -258,6 +314,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 ----------- @@ -292,7 +384,7 @@ the kernel implementation. The communication consists of the *front-end* sending message requests and the *back-end* sending message replies. Most of the requests don't require -replies. Here is a list of the ones that do: +replies, except for the following requests: * ``VHOST_USER_GET_FEATURES`` * ``VHOST_USER_GET_PROTOCOL_FEATURES`` @@ -315,8 +407,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 +440,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 +571,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 +584,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 +699,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 +723,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 +991,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 +1018,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 +1038,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 +1138,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 +1199,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 +1281,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 +1299,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 +1361,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 +1372,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. @@ -1422,6 +1644,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 ---------------------- @@ -1429,7 +1733,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`` @@ -1444,7 +1748,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 @@ -1459,7 +1763,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 @@ -1482,7 +1786,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 @@ -1496,7 +1800,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 @@ -1510,6 +1814,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..3676f81c4d 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 @@ -47,8 +54,6 @@ if build_docs 'qemu-pr-helper.8': (have_tools ? 'man8' : ''), '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' @@ -93,3 +98,8 @@ if build_docs alias_target('html', sphinxdocs) alias_target('man', sphinxmans) endif + +test('QAPI firmware.json regression tests', qapi_gen, + args: ['-o', meson.current_build_dir() / 'qapi', + meson.current_source_dir() / 'interop/firmware.json'], + suite: ['qapi-schema', 'qapi-interop']) 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/pci_expander_bridge.txt b/docs/pci_expander_bridge.txt index 36750273bb..540191f5e0 100644 --- a/docs/pci_expander_bridge.txt +++ b/docs/pci_expander_bridge.txt @@ -25,7 +25,7 @@ A detailed command line would be: -object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1 -device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd -device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8 -device e1000,bus=bridge2,addr=0x3 --device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 +-device pxb,id=bridge3,bus=pci.0,bus_nr=40 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,bus=bridge3,addr=1 Here you have: - 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes) 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/pvrdma.txt b/docs/pvrdma.txt deleted file mode 100644 index 5c122fe818..0000000000 --- a/docs/pvrdma.txt +++ /dev/null @@ -1,345 +0,0 @@ -Paravirtualized RDMA Device (PVRDMA) -==================================== - - -1. Description -=============== -PVRDMA is the QEMU implementation of VMware's paravirtualized RDMA device. -It works with its Linux Kernel driver AS IS, no need for any special guest -modifications. - -While it complies with the VMware device, it can also communicate with bare -metal RDMA-enabled machines as peers. - -It does not require an RDMA HCA in the host, it can work with Soft-RoCE (rxe). - -It does not require the whole guest RAM to be pinned allowing memory -over-commit and, even if not implemented yet, migration support will be -possible with some HW assistance. - -A project presentation accompany this document: -- https://blog.linuxplumbersconf.org/2017/ocw/system/presentations/4730/original/lpc-2017-pvrdma-marcel-apfelbaum-yuval-shaia.pdf - - - -2. Setup -======== - - -2.1 Guest setup -=============== -Fedora 27+ kernels work out of the box, older distributions -require updating the kernel to 4.14 to include the pvrdma driver. - -However the libpvrdma library needed by User Level Software is still -not available as part of the distributions, so the rdma-core library -needs to be compiled and optionally installed. - -Please follow the instructions at: - https://github.com/linux-rdma/rdma-core.git - - -2.2 Host Setup -============== -The pvrdma backend is an ibdevice interface that can be exposed -either by a Soft-RoCE(rxe) device on machines with no RDMA device, -or an HCA SRIOV function(VF/PF). -Note that ibdevice interfaces can't be shared between pvrdma devices, -each one requiring a separate instance (rxe or SRIOV VF). - - -2.2.1 Soft-RoCE backend(rxe) -=========================== -A stable version of rxe is required, Fedora 27+ or a Linux -Kernel 4.14+ is preferred. - -The rdma_rxe module is part of the Linux Kernel but not loaded by default. -Install the User Level library (librxe) following the instructions from: -https://github.com/SoftRoCE/rxe-dev/wiki/rxe-dev:-Home - -Associate an ETH interface with rxe by running: - rxe_cfg add eth0 -An rxe0 ibdevice interface will be created and can be used as pvrdma backend. - - -2.2.2 RDMA device Virtual Function backend -========================================== -Nothing special is required, the pvrdma device can work not only with -Ethernet Links, but also Infinibands Links. -All is needed is an ibdevice with an active port, for Mellanox cards -will be something like mlx5_6 which can be the backend. - - -2.2.3 QEMU setup -================ -Configure QEMU with --enable-rdma flag, installing -the required RDMA libraries. - - - -3. Usage -======== - - -3.1 VM Memory settings -====================== -Currently the device is working only with memory backed RAM -and it must be mark as "shared": - -m 1G \ - -object memory-backend-ram,id=mb1,size=1G,share \ - -numa node,memdev=mb1 \ - - -3.2 MAD Multiplexer -=================== -MAD Multiplexer is a service that exposes MAD-like interface for VMs in -order to overcome the limitation where only single entity can register with -MAD layer to send and receive RDMA-CM MAD packets. - -To build rdmacm-mux run -# make rdmacm-mux - -Before running the rdmacm-mux make sure that both ib_cm and rdma_cm kernel -modules aren't loaded, otherwise the rdmacm-mux service will fail to start. - -The application accepts 3 command line arguments and exposes a UNIX socket -to pass control and data to it. --d rdma-device-name Name of RDMA device to register with --s unix-socket-path Path to unix socket to listen (default /var/run/rdmacm-mux) --p rdma-device-port Port number of RDMA device to register with (default 1) -The final UNIX socket file name is a concatenation of the 3 arguments so -for example for device mlx5_0 on port 2 this /var/run/rdmacm-mux-mlx5_0-2 -will be created. - -pvrdma requires this service. - -Please refer to contrib/rdmacm-mux for more details. - - -3.3 Service exposed by libvirt daemon -===================================== -The control over the RDMA device's GID table is done by updating the -device's Ethernet function addresses. -Usually the first GID entry is determined by the MAC address, the second by -the first IPv6 address and the third by the IPv4 address. Other entries can -be added by adding more IP addresses. The opposite is the same, i.e. -whenever an address is removed, the corresponding GID entry is removed. -The process is done by the network and RDMA stacks. Whenever an address is -added the ib_core driver is notified and calls the device driver add_gid -function which in turn update the device. -To support this in pvrdma device the device hooks into the create_bind and -destroy_bind HW commands triggered by pvrdma driver in guest. - -Whenever changed is made to the pvrdma port's GID table a special QMP -messages is sent to be processed by libvirt to update the address of the -backend Ethernet device. - -pvrdma requires that libvirt service will be up. - - -3.4 PCI devices settings -======================== -RoCE device exposes two functions - an Ethernet and RDMA. -To support it, pvrdma device is composed of two PCI functions, an Ethernet -device of type vmxnet3 on PCI slot 0 and a PVRDMA device on PCI slot 1. The -Ethernet function can be used for other Ethernet purposes such as IP. - - -3.5 Device parameters -===================== -- netdev: Specifies the Ethernet device function name on the host for - example enp175s0f0. For Soft-RoCE device (rxe) this would be the Ethernet - device used to create it. -- ibdev: The IB device name on host for example rxe0, mlx5_0 etc. -- mad-chardev: The name of the MAD multiplexer char device. -- ibport: In case of multi-port device (such as Mellanox's HCA) this - specify the port to use. If not set 1 will be used. -- dev-caps-max-mr-size: The maximum size of MR. -- dev-caps-max-qp: Maximum number of QPs. -- dev-caps-max-cq: Maximum number of CQs. -- dev-caps-max-mr: Maximum number of MRs. -- dev-caps-max-pd: Maximum number of PDs. -- dev-caps-max-ah: Maximum number of AHs. - -Notes: -- The first 3 parameters are mandatory settings, the rest have their - defaults. -- The last 8 parameters (the ones that prefixed by dev-caps) defines the top - limits but the final values is adjusted by the backend device limitations. -- netdev can be extracted from ibdev's sysfs - (/sys/class/infiniband//device/net/) - - -3.6 Example -=========== -Define bridge device with vmxnet3 network backend: - - - - -
- - -Define pvrdma device: - - - - - - - - - - - - - -4. Implementation details -========================= - - -4.1 Overview -============ -The device acts like a proxy between the Guest Driver and the host -ibdevice interface. -On configuration path: - - For every hardware resource request (PD/QP/CQ/...) the pvrdma will request - a resource from the backend interface, maintaining a 1-1 mapping - between the guest and host. -On data path: - - Every post_send/receive received from the guest will be converted into - a post_send/receive for the backend. The buffers data will not be touched - or copied resulting in near bare-metal performance for large enough buffers. - - Completions from the backend interface will result in completions for - the pvrdma device. - - -4.2 PCI BARs -============ -PCI Bars: - BAR 0 - MSI-X - MSI-X vectors: - (0) Command - used when execution of a command is completed. - (1) Async - not in use. - (2) Completion - used when a completion event is placed in - device's CQ ring. - BAR 1 - Registers - -------------------------------------------------------- - | VERSION | DSR | CTL | REQ | ERR | ICR | IMR | MAC | - -------------------------------------------------------- - DSR - Address of driver/device shared memory used - for the command channel, used for passing: - - General info such as driver version - - Address of 'command' and 'response' - - Address of async ring - - Address of device's CQ ring - - Device capabilities - CTL - Device control operations (activate, reset etc) - IMG - Set interrupt mask - REQ - Command execution register - ERR - Operation status - - BAR 2 - UAR - --------------------------------------------------------- - | QP_NUM | SEND/RECV Flag || CQ_NUM | ARM/POLL Flag | - --------------------------------------------------------- - - Offset 0 used for QP operations (send and recv) - - Offset 4 used for CQ operations (arm and poll) - - -4.3 Major flows -=============== - -4.3.1 Create CQ -=============== - - Guest driver - - Allocates pages for CQ ring - - Creates page directory (pdir) to hold CQ ring's pages - - Initializes CQ ring - - Initializes 'Create CQ' command object (cqe, pdir etc) - - Copies the command to 'command' address - - Writes 0 into REQ register - - Device - - Reads the request object from the 'command' address - - Allocates CQ object and initialize CQ ring based on pdir - - Creates the backend CQ - - Writes operation status to ERR register - - Posts command-interrupt to guest - - Guest driver - - Reads the HW response code from ERR register - -4.3.2 Create QP -=============== - - Guest driver - - Allocates pages for send and receive rings - - Creates page directory(pdir) to hold the ring's pages - - Initializes 'Create QP' command object (max_send_wr, - send_cq_handle, recv_cq_handle, pdir etc) - - Copies the object to 'command' address - - Write 0 into REQ register - - Device - - Reads the request object from 'command' address - - Allocates the QP object and initialize - - Send and recv rings based on pdir - - Send and recv ring state - - Creates the backend QP - - Writes the operation status to ERR register - - Posts command-interrupt to guest - - Guest driver - - Reads the HW response code from ERR register - -4.3.3 Post receive -================== - - Guest driver - - Initializes a wqe and place it on recv ring - - Write to qpn|qp_recv_bit (31) to QP offset in UAR - - Device - - Extracts qpn from UAR - - Walks through the ring and does the following for each wqe - - Prepares the backend CQE context to be used when - receiving completion from backend (wr_id, op_code, emu_cq_num) - - For each sge prepares backend sge - - Calls backend's post_recv - -4.3.4 Process backend events -============================ - - Done by a dedicated thread used to process backend events; - at initialization is attached to the device and creates - the communication channel. - - Thread main loop: - - Polls for completions - - Extracts QEMU _cq_num, wr_id and op_code from context - - Writes CQE to CQ ring - - Writes CQ number to device CQ - - Sends completion-interrupt to guest - - Deallocates context - - Acks the event to backend - - - -5. Limitations -============== -- The device obviously is limited by the Guest Linux Driver features implementation - of the VMware device API. -- Memory registration mechanism requires mremap for every page in the buffer in order - to map it to a contiguous virtual address range. Since this is not the data path - it should not matter much. If the default max mr size is increased, be aware that - memory registration can take up to 0.5 seconds for 1GB of memory. -- The device requires target page size to be the same as the host page size, - otherwise it will fail to init. -- QEMU cannot map guest RAM from a file descriptor if a pvrdma device is attached, - so it can't work with huge pages. The limitation will be addressed in the future, - however QEMU allocates Guest RAM with MADV_HUGEPAGE so if there are enough huge - pages available, QEMU will use them. QEMU will fail to init if the requirements - are not met. - - - -6. Performance -============== -By design the pvrdma device exits on each post-send/receive, so for small buffers -the performance is affected; however for medium buffers it will became close to -bare metal and from 1MB buffers and up it reaches bare metal performance. -(tested with 2 VMs, the pvrdma devices connected to 2 VFs of the same device) - -All the above assumes no memory registration is done on data path. 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..02583f209a --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,5 @@ +# Used by readthedocs.io +# Should be in sync with the "installed" key of pythondeps.toml + +sphinx==5.3.0 +sphinx_rtd_theme==1.1.1 diff --git a/docs/specs/acpi_hw_reduced_hotplug.rst b/docs/specs/acpi_hw_reduced_hotplug.rst index 0bd3f9399f..3acd6fcd8b 100644 --- a/docs/specs/acpi_hw_reduced_hotplug.rst +++ b/docs/specs/acpi_hw_reduced_hotplug.rst @@ -64,7 +64,8 @@ GED IO interface (4 byte access) 0: Memory hotplug event 1: System power down event 2: NVDIMM hotplug event - 3-31: Reserved + 3: CPU hotplug event + 4-31: Reserved **write_access:** diff --git a/docs/specs/edu.rst b/docs/specs/edu.rst new file mode 100644 index 0000000000..ae72737dbb --- /dev/null +++ b/docs/specs/edu.rst @@ -0,0 +1,135 @@ + +EDU device +========== + +.. + Copyright (c) 2014-2015 Jiri Slaby + + 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 +University. Students are given this virtual device and are expected to write a +driver with I/Os, IRQs, DMAs and such. + +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]`` + ``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. + +PCI specs +--------- + +PCI ID: + ``1234:11e8`` + +PCI Region 0: + I/O memory, 1 MB in size. Users are supposed to communicate with the card + through this memory. + +MMIO area spec +-------------- + +Only ``size == 4`` accesses are allowed for addresses ``< 0x80``. +``size == 4`` or ``size == 8`` for the rest. + +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). + +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 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 + register below). + +0x60 (WO) : interrupt raise register + Raise an interrupt. The value will be put to the interrupt status + register (using bitwise OR). + +0x64 (WO) : interrupt acknowledge register + Clear an interrupt. The value will be cleared from the interrupt + status register. This needs to be done from the ISR to stop + generating interrupts. + +0x80 (RW) : DMA source address + Where to perform the DMA from. + +0x88 (RW) : DMA destination address + Where to perform the DMA to. + +0x90 (RW) : DMA transfer count + The size of the area to perform the DMA on. + +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. + +The device supports both INTx and MSI interrupt. By default, INTx is +used. Even if the driver disabled INTx and only uses MSI, it still +needs to update the acknowledge register at the end of the IRQ handler +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) + ; + +:: + + 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/edu.txt b/docs/specs/edu.txt deleted file mode 100644 index 0876310809..0000000000 --- a/docs/specs/edu.txt +++ /dev/null @@ -1,115 +0,0 @@ - -EDU device -========== - -Copyright (c) 2014-2015 Jiri Slaby - -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 -University. Students are given this virtual device and are expected to write a -driver with I/Os, IRQs, DMAs and such. - -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] - - 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. - -PCI specs ---------- - -PCI ID: 1234:11e8 - -PCI Region 0: - I/O memory, 1 MB in size. Users are supposed to communicate with the card - through this memory. - -MMIO area spec --------------- - -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 - -0x04 (RW) : card liveness check - 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 - -0x24 (RO) : interrupt status register - It contains values which raised the interrupt (see interrupt raise - register below). - -0x60 (WO) : interrupt raise register - Raise an interrupt. The value will be put to the interrupt status - register (using bitwise OR). - -0x64 (WO) : interrupt acknowledge register - Clear an interrupt. The value will be cleared from the interrupt - status register. This needs to be done from the ISR to stop - generating interrupts. - -0x80 (RW) : DMA source address - Where to perform the DMA from. - -0x88 (RW) : DMA destination address - Where to perform the DMA to. - -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 - -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. - -The device supports both INTx and MSI interrupt. By default, INTx is -used. Even if the driver disabled INTx and only uses MSI, it still -needs to update the acknowledge register at the end of the IRQ handler -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) - ; - -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.rst b/docs/specs/fw_cfg.rst index 5ad47a901c..31ae31576b 100644 --- a/docs/specs/fw_cfg.rst +++ b/docs/specs/fw_cfg.rst @@ -54,11 +54,11 @@ 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\ [#placement]_ or MMIO) * Width: 8-bit (if IOport), 8/16/32/64-bit (if MMIO) * Endianness: string-preserving -.. [#] +.. [#placement] 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 diff --git a/docs/specs/index.rst b/docs/specs/index.rst index a58d9311cb..ff5a1f03da 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 @@ -21,3 +24,16 @@ guest hardware that is specific to QEMU. acpi_erst sev-guest-firmware fw_cfg + fsi + vmw_pvscsi-spec + edu + ivshmem-spec + pvpanic + spdm + standard-vga + virt-ctlr + vmcoreinfo + vmgenid + rapl-msr + rocker + riscv-iommu diff --git a/docs/specs/ivshmem-spec.rst b/docs/specs/ivshmem-spec.rst new file mode 100644 index 0000000000..2d8e80055b --- /dev/null +++ b/docs/specs/ivshmem-spec.rst @@ -0,0 +1,241 @@ +====================================================== +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 +and the host. In order for all guests to be able to pick up the +shared memory area, it is modeled by QEMU as a PCI device exposing +said memory to the guest as a PCI BAR. + +The device can use a shared memory object on the host directly, or it +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`. + +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 +-------- + +The ivshmem PCI device has two or three BARs: + +- BAR0 holds device registers (256 Byte MMIO) +- BAR1 holds MSI-X table and PBA (only ivshmem-doorbell) +- BAR2 maps the shared memory object + +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. + +- 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 + kernel driver to handle interrupts. Requires the device to be + configured for interrupts, obviously. + +Before QEMU 2.6.0, BAR2 can initially be invalid if the device is +configured for interrupts. It becomes safely accessible only after +the ivshmem server provided the shared memory. These devices have PCI +revision 0 rather than 1. Guest software should wait for the +IVPosition register (described below) to become non-negative before +accessing BAR2. + +Revision 0 of the device is not capable to tell guest software whether +it is configured for interrupts. + +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) + reserved (rev 1) + bit 1..31: reserved + 4 4 read/write 0 Interrupt Status + bit 0: peer interrupt (rev 0) + reserved (rev 1) + bit 1..31: reserved + 8 4 read-only 0 or ID IVPosition + 12 4 write-only N/A Doorbell + bit 0..15: vector + bit 16..31: peer ID + 16 240 none N/A reserved + +Software should only access the registers as specified in column +"Access". Reserved bits should be ignored on read, and preserved on +write. + +In revision 0 of the device, Interrupt Status and Mask Register +together control the legacy INTx interrupt when the device has no +MSI-X capability: INTx is asserted when the bit-wise AND of Status and +Mask is non-zero and the device has no MSI-X capability. Interrupt +Status Register bit 0 becomes 1 when an interrupt request from a peer +is received. Reading the register clears it. + +IVPosition Register: if the device is not configured for interrupts, +this is zero. Else, it is the device's ID (between 0 and 65535). + +Before QEMU 2.6.0, the register may read -1 for a short while after +reset. These devices have PCI revision 0 rather than 1. + +There is no good way for software to find out whether the device is +configured for interrupts. A positive IVPosition means interrupts, +but zero could be either. + +Doorbell Register: writing this register requests to interrupt a peer. +The written value's high 16 bits are the ID of the peer to interrupt, +and its low 16 bits select an interrupt vector. + +If the device is not configured for interrupts, the write is ignored. + +If the interrupt hasn't completed setup, the write is ignored. The +device is not capable to tell guest software whether setup is +complete. Interrupts can regress to this state on migration. + +If the peer with the requested ID isn't connected, or it has fewer +interrupt vectors connected, the write is ignored. The device is not +capable to tell guest software what peers are connected, or how many +interrupt vectors are connected. + +The peer's interrupt for this vector then becomes pending. There is +no way for software to clear the pending bit, and a polling mode of +operation is therefore impossible. + +If the peer is a revision 0 device without MSI-X capability, its +Interrupt Status register is set to 1. This asserts INTx unless +masked by the Interrupt Mask register. The device is not capable to +communicate the interrupt vector to guest software then. + +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 +======================== + +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 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 + new client, +- sends connect notifications for the new client to the other clients + (these contain file descriptors for sending interrupts), +- sends connect notifications for the other clients to the new client, + and +- sends interrupt setup messages to the new client (these contain file + descriptors for receiving interrupts). + +The first client to connect to the server receives ID zero. + +When a client disconnects from the server, the server sends disconnect +notifications to the other clients. + +The next section describes the protocol in detail. + +If the server terminates without sending disconnect notifications for +its connected clients, the clients can elect to continue. They can +communicate with each other normally, but won't receive disconnect +notification on disconnect, and no new clients can connect. There is +no way for the clients to connect to a restarted server. The device +is not capable to tell guest software whether the server is still up. + +Example server code is in contrib/ivshmem-server/. Not to be used in +production. It assumes all clients use the same number of interrupt +vectors. + +A standalone client is in contrib/ivshmem-client/. It can be useful +for debugging. + +The ivshmem Client-Server Protocol +---------------------------------- + +An ivshmem device configured for interrupts connects to an ivshmem +server. This section details the protocol between the two. + +The connection is one-way: the server sends messages to the client. +Each message consists of a single 8 byte little-endian signed number, +and may be accompanied by a file descriptor via SCM_RIGHTS. Both +client and server close the connection on error. + +Note: QEMU currently doesn't close the connection right on error, but +only when the character device is destroyed. + +On connect, the server sends the following messages in order: + +1. The protocol version number, currently zero. The client should + close the connection on receipt of versions it can't handle. + +2. The client's ID. This is unique among all clients of this server. + IDs must be between 0 and 65535, because the Doorbell register + provides only 16 bits for them. + +3. The number -1, accompanied by the file descriptor for the shared + memory. + +4. Connect notifications for existing other clients, if any. This is + a peer ID (number between 0 and 65535 other than the client's ID), + repeated N times. Each repetition is accompanied by one file + descriptor. These are for interrupting the peer with that ID using + vector 0,..,N-1, in order. If the client is configured for fewer + vectors, it closes the extra file descriptors. If it is configured + for more, the extra vectors remain unconnected. + +5. Interrupt setup. This is the client's own ID, repeated N times. + Each repetition is accompanied by one file descriptor. These are + for receiving interrupts from peers using vector 0,..,N-1, in + order. If the client is configured for fewer vectors, it closes + the extra file descriptors. If it is configured for more, the + extra vectors remain unconnected. + +From then on, the server sends these kinds of messages: + +6. Connection / disconnection notification. This is a peer ID. + + - If the number comes with a file descriptor, it's a connection + notification, exactly like in step 4. + + - Else, it's a disconnection notification for the peer with that ID. + +Known bugs: + +* The protocol changed incompatibly in QEMU 2.5. Before, messages + were native endian long, and there was no version number. + +* The protocol is poorly designed. + +The ivshmem Client-Client Protocol +---------------------------------- + +An ivshmem device configured for interrupts receives eventfd file +descriptors for interrupting peers and getting interrupted by peers +from the server, as explained in the previous section. + +To interrupt a peer, the device writes the 8-byte integer 1 in native +byte order to the respective file descriptor. + +To receive an interrupt, the device reads and discards as many 8-byte +integers as it can. diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.txt deleted file mode 100644 index 1beb3a01ec..0000000000 --- a/docs/specs/ivshmem-spec.txt +++ /dev/null @@ -1,258 +0,0 @@ -= 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 -and the host. In order for all guests to be able to pick up the -shared memory area, it is modeled by QEMU as a PCI device exposing -said memory to the guest as a PCI BAR. - -The device can use a shared memory object on the host directly, or it -can obtain one from an ivshmem server. - -In the latter case, the device can additionally interrupt its peers, and -get interrupted by its peers. - - -== 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 device has vendor ID 1af4, device ID 1110, revision 1. Before -QEMU 2.6.0, it had revision 0. - -=== PCI BARs === - -The ivshmem PCI device has two or three BARs: - -- BAR0 holds device registers (256 Byte MMIO) -- BAR1 holds MSI-X table and PBA (only ivshmem-doorbell) -- BAR2 maps the shared memory object - -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). - -- 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 - kernel driver to handle interrupts. Requires the device to be - configured for interrupts, obviously. - -Before QEMU 2.6.0, BAR2 can initially be invalid if the device is -configured for interrupts. It becomes safely accessible only after -the ivshmem server provided the shared memory. These devices have PCI -revision 0 rather than 1. Guest software should wait for the -IVPosition register (described below) to become non-negative before -accessing BAR2. - -Revision 0 of the device is not capable to tell guest software whether -it is configured for interrupts. - -=== 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) - reserved (rev 1) - bit 1..31: reserved - 4 4 read/write 0 Interrupt Status - bit 0: peer interrupt (rev 0) - reserved (rev 1) - bit 1..31: reserved - 8 4 read-only 0 or ID IVPosition - 12 4 write-only N/A Doorbell - bit 0..15: vector - bit 16..31: peer ID - 16 240 none N/A reserved - -Software should only access the registers as specified in column -"Access". Reserved bits should be ignored on read, and preserved on -write. - -In revision 0 of the device, Interrupt Status and Mask Register -together control the legacy INTx interrupt when the device has no -MSI-X capability: INTx is asserted when the bit-wise AND of Status and -Mask is non-zero and the device has no MSI-X capability. Interrupt -Status Register bit 0 becomes 1 when an interrupt request from a peer -is received. Reading the register clears it. - -IVPosition Register: if the device is not configured for interrupts, -this is zero. Else, it is the device's ID (between 0 and 65535). - -Before QEMU 2.6.0, the register may read -1 for a short while after -reset. These devices have PCI revision 0 rather than 1. - -There is no good way for software to find out whether the device is -configured for interrupts. A positive IVPosition means interrupts, -but zero could be either. - -Doorbell Register: writing this register requests to interrupt a peer. -The written value's high 16 bits are the ID of the peer to interrupt, -and its low 16 bits select an interrupt vector. - -If the device is not configured for interrupts, the write is ignored. - -If the interrupt hasn't completed setup, the write is ignored. The -device is not capable to tell guest software whether setup is -complete. Interrupts can regress to this state on migration. - -If the peer with the requested ID isn't connected, or it has fewer -interrupt vectors connected, the write is ignored. The device is not -capable to tell guest software what peers are connected, or how many -interrupt vectors are connected. - -The peer's interrupt for this vector then becomes pending. There is -no way for software to clear the pending bit, and a polling mode of -operation is therefore impossible. - -If the peer is a revision 0 device without MSI-X capability, its -Interrupt Status register is set to 1. This asserts INTx unless -masked by the Interrupt Mask register. The device is not capable to -communicate the interrupt vector to guest software then. - -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 == - -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 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 - new client, -- sends connect notifications for the new client to the other clients - (these contain file descriptors for sending interrupts), -- sends connect notifications for the other clients to the new client, - and -- sends interrupt setup messages to the new client (these contain file - descriptors for receiving interrupts). - -The first client to connect to the server receives ID zero. - -When a client disconnects from the server, the server sends disconnect -notifications to the other clients. - -The next section describes the protocol in detail. - -If the server terminates without sending disconnect notifications for -its connected clients, the clients can elect to continue. They can -communicate with each other normally, but won't receive disconnect -notification on disconnect, and no new clients can connect. There is -no way for the clients to connect to a restarted server. The device -is not capable to tell guest software whether the server is still up. - -Example server code is in contrib/ivshmem-server/. Not to be used in -production. It assumes all clients use the same number of interrupt -vectors. - -A standalone client is in contrib/ivshmem-client/. It can be useful -for debugging. - -=== The ivshmem Client-Server Protocol === - -An ivshmem device configured for interrupts connects to an ivshmem -server. This section details the protocol between the two. - -The connection is one-way: the server sends messages to the client. -Each message consists of a single 8 byte little-endian signed number, -and may be accompanied by a file descriptor via SCM_RIGHTS. Both -client and server close the connection on error. - -Note: QEMU currently doesn't close the connection right on error, but -only when the character device is destroyed. - -On connect, the server sends the following messages in order: - -1. The protocol version number, currently zero. The client should - close the connection on receipt of versions it can't handle. - -2. The client's ID. This is unique among all clients of this server. - IDs must be between 0 and 65535, because the Doorbell register - provides only 16 bits for them. - -3. The number -1, accompanied by the file descriptor for the shared - memory. - -4. Connect notifications for existing other clients, if any. This is - a peer ID (number between 0 and 65535 other than the client's ID), - repeated N times. Each repetition is accompanied by one file - descriptor. These are for interrupting the peer with that ID using - vector 0,..,N-1, in order. If the client is configured for fewer - vectors, it closes the extra file descriptors. If it is configured - for more, the extra vectors remain unconnected. - -5. Interrupt setup. This is the client's own ID, repeated N times. - Each repetition is accompanied by one file descriptor. These are - for receiving interrupts from peers using vector 0,..,N-1, in - order. If the client is configured for fewer vectors, it closes - the extra file descriptors. If it is configured for more, the - extra vectors remain unconnected. - -From then on, the server sends these kinds of messages: - -6. Connection / disconnection notification. This is a peer ID. - - - If the number comes with a file descriptor, it's a connection - notification, exactly like in step 4. - - - Else, it's a disconnection notification for the peer with that ID. - -Known bugs: - -* The protocol changed incompatibly in QEMU 2.5. Before, messages - were native endian long, and there was no version number. - -* The protocol is poorly designed. - -=== The ivshmem Client-Client Protocol === - -An ivshmem device configured for interrupts receives eventfd file -descriptors for interrupting peers and getting interrupted by peers -from the server, as explained in the previous section. - -To interrupt a peer, the device writes the 8-byte integer 1 in native -byte order to the respective file descriptor. - -To receive an interrupt, the device reads and discards as many 8-byte -integers as it can. diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst new file mode 100644 index 0000000000..261b0f359f --- /dev/null +++ b/docs/specs/pci-ids.rst @@ -0,0 +1,106 @@ +================ +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:000c + PCIe Root Port (``-device pcie-root-port``) +1b36:000d + PCI xhci usb host adapter +1b36:000e + PCIe-to-PCI bridge (``-device pcie-pci-bridge``) +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``) +1b36:0014 + PCI RISC-V IOMMU device + +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 e463c4cb3a..0000000000 --- a/docs/specs/pci-ids.txt +++ /dev/null @@ -1,70 +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:1012 vsock device (bug compatibility) - -1af4:1040 Start of ID range for modern virtio devices. The PCI device - to ID is calculated from the virtio device ID by adding the -1af4:10ef 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 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.rst b/docs/specs/pvpanic.rst new file mode 100644 index 0000000000..61a80480ed --- /dev/null +++ b/docs/specs/pvpanic.rst @@ -0,0 +1,69 @@ +PVPANIC DEVICE +============== + +pvpanic device is a simulated device, through which a guest panic +event is sent to qemu, and a QMP event is generated. This allows +management apps (e.g. libvirt) to be notified and respond to the event. + +The management app has the option of waiting for GUEST_PANICKED events, +and/or polling for guest-panicked RunState, to learn when the pvpanic +device has fired a panic event. + +The pvpanic device can be implemented as an ISA device (using IOPORT) or as a +PCI device. + +ISA Interface +------------- + +pvpanic exposes a single I/O port, by default 0x505. On read, the bits +recognized by the device are set. Software should ignore bits it doesn't +recognize. On write, the bits not recognized by the device are ignored. +Software should set only bits both itself and the device recognize. + +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 2 + 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 +line. + +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. + +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/pvpanic.txt b/docs/specs/pvpanic.txt deleted file mode 100644 index 8afcde11cc..0000000000 --- a/docs/specs/pvpanic.txt +++ /dev/null @@ -1,54 +0,0 @@ -PVPANIC DEVICE -============== - -pvpanic device is a simulated device, through which a guest panic -event is sent to qemu, and a QMP event is generated. This allows -management apps (e.g. libvirt) to be notified and respond to the event. - -The management app has the option of waiting for GUEST_PANICKED events, -and/or polling for guest-panicked RunState, to learn when the pvpanic -device has fired a panic event. - -The pvpanic device can be implemented as an ISA device (using IOPORT) or as a -PCI device. - -ISA Interface -------------- - -pvpanic exposes a single I/O port, by default 0x505. On read, the bits -recognized by the device are set. Software should ignore bits it doesn't -recognize. On write, the bits not recognized by the device are ignored. -Software should set only bits both itself and the device recognize. - -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. - -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 -line. - -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. - -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/rapl-msr.rst b/docs/specs/rapl-msr.rst new file mode 100644 index 0000000000..aaf0db9f91 --- /dev/null +++ b/docs/specs/rapl-msr.rst @@ -0,0 +1,154 @@ +================ +RAPL MSR support +================ + +The RAPL interface (Running Average Power Limit) is advertising the accumulated +energy consumption of various power domains (e.g. CPU packages, DRAM, etc.). + +The consumption is reported via MSRs (model specific registers) like +MSR_PKG_ENERGY_STATUS for the CPU package power domain. These MSRs are 64 bits +registers that represent the accumulated energy consumption in micro Joules. + +Thanks to KVM's `MSR filtering `__ functionality, +not all MSRs are handled by KVM. Some of them can now be handled by the +userspace (QEMU); a list of MSRs is given at VM creation time to KVM, and +a userspace exit occurs when they are accessed. + +.. _msr-filter-patch: https://patchwork.kernel.org/project/kvm/patch/20200916202951.23760-7-graf@amazon.com/ + +At the moment the following MSRs are involved: + +.. code:: C + + #define MSR_RAPL_POWER_UNIT 0x00000606 + #define MSR_PKG_POWER_LIMIT 0x00000610 + #define MSR_PKG_ENERGY_STATUS 0x00000611 + #define MSR_PKG_POWER_INFO 0x00000614 + +The ``*_POWER_UNIT``, ``*_POWER_LIMIT``, ``*_POWER INFO`` are part of the RAPL +spec and specify the power limit of the package, provide range of parameter(min +power, max power,..) and also the information of the multiplier for the energy +counter to calculate the power. Those MSRs are populated once at the beginning +by reading the host CPU MSRs and are given back to the guest 1:1 when +requested. + +The MSR_PKG_ENERGY_STATUS is a counter; it represents the total amount of +energy consumed since the last time the register was cleared. If you multiply +it with the UNIT provided above you'll get the power in micro-joules. This +counter is always increasing and it increases more or less faster depending on +the consumption of the package. This counter is supposed to overflow at some +point. + +Each core belonging to the same Package reading the MSR_PKG_ENERGY_STATUS (i.e +"rdmsr 0x611") will retrieve the same value. The value represents the energy +for the whole package. Whatever Core reading it will get the same value and a +core that belongs to PKG-0 will not be able to get the value of PKG-1 and +vice-versa. + +High level implementation +------------------------- + +In order to update the value of the virtual MSR, a QEMU thread is created. +The thread is basically just an infinity loop that does: + +1. Snapshot of the time metrics of all QEMU threads (Time spent scheduled in + Userspace and System) + +2. Snapshot of the actual MSR_PKG_ENERGY_STATUS counter of all packages where + the QEMU threads are running on. + +3. Sleep for 1 second - During this pause the vcpu and other non-vcpu threads + will do what they have to do and so the energy counter will increase. + +4. Repeat 2. and 3. and calculate the delta of every metrics representing the + time spent scheduled for each QEMU thread *and* the energy spent by the + packages during the pause. + +5. Filter the vcpu threads and the non-vcpu threads. + +6. Retrieve the topology of the Virtual Machine. This helps identify which + vCPU is running on which virtual package. + +7. The total energy spent by the non-vcpu threads is divided by the number + of vcpu threads so that each vcpu thread will get an equal part of the + energy spent by the QEMU workers. + +8. Calculate the ratio of energy spent per vcpu threads. + +9. Calculate the energy for each virtual package. + +10. The virtual MSRs are updated for each virtual package. Each vCPU that + belongs to the same package will return the same value when accessing the + the MSR. + +11. Loop back to 1. + +Ratio calculation +----------------- + +In Linux, a process has an execution time associated with it. The scheduler is +dividing the time in clock ticks. The number of clock ticks per second can be +found by the sysconf system call. A typical value of clock ticks per second is +100. So a core can run a process at the maximum of 100 ticks per second. If a +package has 4 cores, 400 ticks maximum can be scheduled on all the cores +of the package for a period of 1 second. + +`/proc/[pid]/stat `__ is a procfs file that can give the executed +time of a process with the [pid] as the process ID. It gives the amount +of ticks the process has been scheduled in userspace (utime) and kernel +space (stime). + +.. _stat: https://man7.org/linux/man-pages/man5/proc.5.html + +By reading those metrics for a thread, one can calculate the ratio of time the +package has spent executing the thread. + +Example: + +A 4 cores package can schedule a maximum of 400 ticks per second with 100 ticks +per second per core. If a thread was scheduled for 100 ticks between a second +on this package, that means my thread has been scheduled for 1/4 of the whole +package. With that, the calculation of the energy spent by the thread on this +package during this whole second is 1/4 of the total energy spent by the +package. + +Usage +----- + +Currently this feature is only working on an Intel CPU that has the RAPL driver +mounted and available in the sysfs. if not, QEMU fails at start-up. + +This feature is activated with -accel +kvm,rapl=true,rapl-helper-socket=/path/sock.sock + +It is important that the socket path is the same as the one +:program:`qemu-vmsr-helper` is listening to. + +qemu-vmsr-helper +---------------- + +The qemu-vmsr-helper is working very much like the qemu-pr-helper. Instead of +making persistent reservation, qemu-vmsr-helper is here to overcome the +CVE-2020-8694 which remove user access to the rapl msr attributes. + +A socket communication is established between QEMU processes that has the RAPL +MSR support activated and the qemu-vmsr-helper. A systemd service and socket +activation is provided in contrib/systemd/qemu-vmsr-helper.(service/socket). + +The systemd socket uses 600, like contrib/systemd/qemu-pr-helper.socket. The +socket can be passed via SCM_RIGHTS by libvirt, or its permissions can be +changed (e.g. 660 and root:kvm for a Debian system for example). Libvirt could +also start a separate helper if needed. All in all, the policy is left to the +user. + +See the qemu-pr-helper documentation or manpage for further details. + +Current Limitations +------------------- + +- Works only on Intel host CPUs because AMD CPUs are using different MSR + addresses. + +- Only the Package Power-Plane (MSR_PKG_ENERGY_STATUS) is reported at the + moment. + diff --git a/docs/specs/riscv-iommu.rst b/docs/specs/riscv-iommu.rst new file mode 100644 index 0000000000..463f4cffb6 --- /dev/null +++ b/docs/specs/riscv-iommu.rst @@ -0,0 +1,90 @@ +.. _riscv-iommu: + +RISC-V IOMMU support for RISC-V machines +======================================== + +QEMU implements a RISC-V IOMMU emulation based on the RISC-V IOMMU spec +version 1.0 `iommu1.0`_. + +The emulation includes a PCI reference device, riscv-iommu-pci, that QEMU +RISC-V boards can use. The 'virt' RISC-V machine is compatible with this +device. + +riscv-iommu-pci reference device +-------------------------------- + +This device implements the RISC-V IOMMU emulation as recommended by the section +"Integrating an IOMMU as a PCIe device" of `iommu1.0`_: a PCI device with base +class 08h, sub-class 06h and programming interface 00h. + +As a reference device it doesn't implement anything outside of the specification, +so it uses a generic default PCI ID given by QEMU: 1b36:0014. + +To include the device in the 'virt' machine: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt -device riscv-iommu-pci,[optional_pci_opts] (...) + +This will add a RISC-V IOMMU PCI device in the board following any additional +PCI parameters (like PCI bus address). The behavior of the RISC-V IOMMU is +defined by the spec but its operation is OS dependent. + +As of this writing the existing Linux kernel support `linux-v8`_, not yet merged, +does not have support for features like VFIO passthrough. The IOMMU emulation +was tested using a public Ventana Micro Systems kernel repository in +`ventana-linux`_. This kernel is based on `linux-v8`_ with additional patches that +enable features like KVM VFIO passthrough with irqbypass. Until the kernel support +is feature complete feel free to use the kernel available in the Ventana Micro Systems +mirror. + +The current Linux kernel support will use the IOMMU device to create IOMMU groups +with any eligible cards available in the system, regardless of factors such as the +order in which the devices are added in the command line. + +This means that these command lines are equivalent as far as the current +IOMMU kernel driver behaves: + +.. code-block:: bash + + $ qemu-system-riscv64 \ + -M virt,aia=aplic-imsic,aia-guests=5 \ + -device riscv-iommu-pci,addr=1.0,vendor-id=0x1efd,device-id=0xedf1 \ + -device e1000e,netdev=net1 -netdev user,id=net1,net=192.168.0.0/24 \ + -device e1000e,netdev=net2 -netdev user,id=net2,net=192.168.200.0/24 \ + (...) + + $ qemu-system-riscv64 \ + -M virt,aia=aplic-imsic,aia-guests=5 \ + -device e1000e,netdev=net1 -netdev user,id=net1,net=192.168.0.0/24 \ + -device e1000e,netdev=net2 -netdev user,id=net2,net=192.168.200.0/24 \ + -device riscv-iommu-pci,addr=1.0,vendor-id=0x1efd,device-id=0xedf1 \ + (...) + +Both will create iommu groups for the two e1000e cards. + +Another thing to notice on `linux-v8`_ and `ventana-linux`_ is that the kernel driver +considers an IOMMU identified as a Rivos device, i.e. it uses Rivos vendor ID. To +use the riscv-iommu-pci device with the existing kernel support we need to emulate +a Rivos PCI IOMMU by setting 'vendor-id' and 'device-id': + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt \ + -device riscv-iommu-pci,vendor-id=0x1efd,device-id=0xedf1 (...) + +Several options are available to control the capabilities of the device, namely: + +- "bus": the bus that the IOMMU device uses +- "ioatc-limit": size of the Address Translation Cache (default to 2Mb) +- "intremap": enable/disable MSI support +- "ats": enable ATS support +- "off" (Out-of-reset translation mode: 'on' for DMA disabled, 'off' for 'BARE' (passthrough)) +- "s-stage": enable s-stage support +- "g-stage": enable g-stage support + +.. _iommu1.0: https://github.com/riscv-non-isa/riscv-iommu/releases/download/v1.0/riscv-iommu.pdf + +.. _linux-v8: https://lore.kernel.org/linux-riscv/cover.1718388908.git.tjeznach@rivosinc.com/ + +.. _ventana-linux: https://github.com/ventanamicro/linux/tree/dev-upstream diff --git a/docs/specs/rocker.rst b/docs/specs/rocker.rst new file mode 100644 index 0000000000..3a7fc6a7e0 --- /dev/null +++ b/docs/specs/rocker.rst @@ -0,0 +1,1015 @@ +Rocker Network Switch Register Programming Guide +************************************************ + +.. + Copyright (c) Scott Feldman + Copyright (c) Neil Horman + Version 0.11, 12/29/2014 + + 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. + +Introduction +============ + +Overview +-------- + +This document describes the hardware/software interface for the Rocker switch +device. The intended audience is authors of OS drivers and device emulation +software. + +Notations and Conventions +------------------------- + +* In register descriptions, [n:m] indicates a range from bit n to bit m, + inclusive. +* Use of leading 0x indicates a hexadecimal number. +* Use of leading 0b indicates a binary number. +* The use of RSVD or Reserved indicates that a bit or field is reserved for + future use. +* Field width is in bytes, unless otherwise noted. +* Register are (R) read-only, (R/W) read/write, (W) write-only, or (COR) clear + on read +* TLV values in network-byte-order are designated with (N). + + +PCI Configuration Registers +=========================== + +PCI Configuration Space +----------------------- + +Each switch instance registers as a PCI device with PCI configuration space:: + + offset width description value + --------------------------------------------- + 0x0 2 Vendor ID 0x1b36 + 0x2 2 Device ID 0x0006 + 0x4 4 Command/Status + 0x8 1 Revision ID 0x01 + 0x9 3 Class code 0x2800 + 0xC 1 Cache line size + 0xD 1 Latency timer + 0xE 1 Header type + 0xF 1 Built-in self test + 0x10 4 Base address low + 0x14 4 Base address high + 0x18-28 Reserved + 0x2C 2 Subsystem vendor ID * + 0x2E 2 Subsystem ID * + 0x30-38 Reserved + 0x3C 1 Interrupt line + 0x3D 1 Interrupt pin 0x00 + 0x3E 1 Min grant 0x00 + 0x3D 1 Max latency 0x00 + 0x40 1 TRDY timeout + 0x41 1 Retry count + 0x42 2 Reserved + + * Assigned by sub-system implementation + +Memory-Mapped Register Space +============================ + +There are two memory-mapped BARs. BAR0 maps device register space and is +0x2000 in size. BAR1 maps MSI-X vector and PBA tables and is also 0x2000 in +size, allowing for 256 MSI-X vectors. + +All registers are 4 or 8 bytes long. It is assumed host software will access 4 +byte registers with one 4-byte access, and 8 byte registers with either two +4-byte accesses or a single 8-byte access. In the case of two 4-byte accesses, +access must be lower and then upper 4-bytes, in that order. + +BAR0 device register space is organized as follows:: + + offset description + ------------------------------------------------------ + 0x0000-0x000f Bogus registers to catch misbehaving + drivers. Writes do nothing. Reads + back as 0xDEADBABE. + 0x0010-0x00ff Test registers + 0x0300-0x03ff General purpose registers + 0x1000-0x1fff Descriptor control + +Holes in register space are reserved. Writes to reserved registers do nothing. +Reads to reserved registers read back as 0. + +No fancy stuff like write-combining is enabled on any of the registers. + +BAR1 MSI-X register space is organized as follows:: + + offset description + ------------------------------------------------------ + 0x0000-0x0fff MSI-X vector table (256 vectors total) + 0x1000-0x1fff MSI-X PBA table + + +Interrupts, DMA, and Endianness +=============================== + +PCI Interrupts +-------------- + +The device supports only MSI-X interrupts. BAR1 memory-mapped region contains +the MSI-X vector and PBA tables, with support for up to 256 MSI-X vectors. + +The vector assignment is:: + + vector description + ----------------------------------------------------- + 0 Command descriptor ring completion + 1 Event descriptor ring completion + 2 Test operation completion + 3 RSVD + 4-255 Tx and Rx descriptor ring completion + Tx vector is even + Rx vector is odd + +A MSI-X vector table entry is 16 bytes:: + + field offset width description + ------------------------------------------------------------- + lower_addr 0x0 4 [31:2] message address[31:2] + [1:0] Rsvd (4 byte alignment + required) + upper_addr 0x4 4 [31:19] Rsvd + [14:0] message address[46:32] + data 0x8 4 message data[31:0] + control 0xc 4 [31:1] Rsvd + [0] mask (0 = enable, + 1 = masked) + +Software should install the Interrupt Service Routine (ISR) before any ports +are enabled or any commands are issued on the command ring. + +DMA Operations +-------------- + +DMA operations are used for packet DMA to/from the CPU, command and event +processing. Command processing includes statistical counters and table dumps, +table insertion/deletion, and more. Event processing provides an async +notification method for device-originating events. Each DMA operation has a +set of control registers to manage a descriptor ring. The descriptor rings are +allocated from contiguous host DMA-able memory and registers specify the rings +base address, size and current head and tail indices. Software always writes +the head, and hardware always writes the tail. + +The higher-order bit of DMA_DESC_COMP_ERR is used to mark hardware completion +of a descriptor. Software will clear this bit when posting a descriptor to the +ring, and hardware will set this bit when the descriptor is complete. + +Descriptor ring sizes must be a power of 2 and range from 2 to 64K entries. +Descriptor rings' base address must be 8-byte aligned. Descriptors must be +packed within ring. Each descriptor in each ring must also be aligned on an 8 +byte boundary. Each descriptor ring will have these registers:: + + DMA_DESC_xxx_BASE_ADDR, offset 0x1000 + (x * 32), 64-bit, (R/W) + DMA_DESC_xxx_SIZE, offset 0x1008 + (x * 32), 32-bit, (R/W) + DMA_DESC_xxx_HEAD, offset 0x100c + (x * 32), 32-bit, (R/W) + DMA_DESC_xxx_TAIL, offset 0x1010 + (x * 32), 32-bit, (R) + DMA_DESC_xxx_CTRL, offset 0x1014 + (x * 32), 32-bit, (W) + DMA_DESC_xxx_CREDITS, offset 0x1018 + (x * 32), 32-bit, (R/W) + DMA_DESC_xxx_RSVD1, offset 0x101c + (x * 32), 32-bit, (R/W) + +Where x is descriptor ring index:: + + index ring + -------------------- + 0 CMD + 1 EVENT + 2 TX (port 0) + 3 RX (port 0) + 4 TX (port 1) + 5 RX (port 1) + . + . + . + 124 TX (port 61) + 125 RX (port 61) + 126 Resv + 127 Resv + +Writing BASE_ADDR or SIZE will reset HEAD and TAIL to zero. HEAD cannot be +written past TAIL. To do so would wrap the ring. An empty ring is when HEAD +== TAIL. A full ring is when HEAD is one position behind TAIL. Both HEAD and +TAIL increment and modulo wrap at the ring size. + +CTRL register bits:: + + bit name description + ------------------------------------------------------------------------ + [0] CTRL_RESET Reset the descriptor ring + [1:31] Reserved + +All descriptor types share some common fields:: + + field width description + ------------------------------------------------------------------- + DMA_DESC_BUF_ADDR 8 Phys addr of desc payload, 8-byte + aligned + DMA_DESC_COOKIE 8 Desc cookie for completion matching, + upper-most bit is reserved + DMA_DESC_BUF_SIZE 2 Desc payload size in bytes + DMA_DESC_TLV_SIZE 2 Desc payload total size in bytes + used for TLVs. Must be <= + DMA_DESC_BUF_SIZE. + DMA_DESC_COMP_ERR 2 Completion status of associated + desc payload. High order bit is + clear on new descs, toggled by + hw for completed items. + +To support forward- and backward-compatibility, descriptor and completion +payloads are specified in TLV format. Fields are packed with Type=field name, +Length=field length, and Value=field value. Software will ignore unknown fields +filled in by the switch. Likewise, the switch will ignore unknown fields +filled in by software. + +Descriptor payload buffer is 8-byte aligned and TLVs are 8-byte aligned. The +value within a TLV is also 8-byte aligned. The (packed, 8 byte) TLV header is:: + + field width description + ----------------------------- + type 4 TLV type + len 2 TLV value length + pad 2 Reserved + +The alignment requirements for descriptors and TLVs are to avoid unaligned +access exceptions in software. Note that the payload for each TLV is also +8 byte aligned. + +Figure 1 shows an example descriptor buffer with two TLVs:: + + <------- 8 bytes -------> + + 8-byte +––––+ +–––––––––––+–––––+–––––+ +–+ + align | type | len | pad | TLV#1 hdr | + +–––––––––––+–––––+–––––+ (len=22) | + | | | + | value | TVL#1 value | + | | (padded to 8-byte | + | +–––––+ alignment) | + | |/////| | + 8-byte +––––+ +–––––––––––+–––––––––––+ | + align | type | len | pad | TLV#2 hdr DESC_BUF_SIZE + +–––––+–––––+–––––+–––––+ (len=2) | + |value|/////////////////| TLV#2 value | + +–––––+/////////////////| | + |///////////////////////| | + |///////////////////////| | + |///////////////////////| | + |////////unused/////////| | + |////////space//////////| | + |///////////////////////| | + |///////////////////////| | + |///////////////////////| | + +–––––––––––––––––––––––+ +–+ + + fig. 1 + +TLVs can be nested within the NEST TLV type. + +Interrupt credits +^^^^^^^^^^^^^^^^^ + +MSI-X vectors used for descriptor ring completions use a credit mechanism for +efficient device, PCIe bus, OS and driver operations. Each descriptor ring has +a credit count which represents the number of outstanding descriptors to be +processed by the driver. As the device marks descriptors complete, the credit +count is incremented. As the driver processes those outstanding descriptors, +it returns credits back to the device. This way, the device knows the driver's +progress and can make decisions about when to fire the next interrupt or not. +When the credit count is zero, and the first descriptors are posted for the +driver, a single interrupt is fired. Once the interrupt is fired, the +interrupt is disabled (auto-masked*). In response to the interrupt, the driver +will process descriptors and PIO write a returned credit value for that +descriptor ring. If the driver returns all credits (the driver caught up with +the device and there is no outstanding work), then the interrupt is unmasked, +but not fired. If only partial credits are returned, the interrupt remains +masked but the device generates an interrupt, signaling the driver that more +outstanding work is available. + +(* this masking is unrelated to the MSI-X interrupt mask register) + +Endianness +---------- + +Device registers are hard-coded to little-endian (LE). The driver should +convert to/from host endianness to LE for device register accesses. + +Descriptors are LE. Descriptor buffer TLVs will have LE type and length +fields, but the value field can either be LE or network-byte-order, depending +on context. TLV values containing network packet data will be in network-byte +order. A TLV value containing a field or mask used to compare against network +packet data is network-byte order. For example, flow match fields (and masks) +are network-byte-order since they're matched directly, byte-by-byte, against +network packet data. All non-network-packet TLV multi-byte values will be LE. + +TLV values in network-byte-order are designated with (N). + + +Test Registers +============== + +Rocker has several test registers to support troubleshooting register access, +interrupt generation, and DMA operations:: + + TEST_REG, offset 0x0010, 32-bit (R/W) + TEST_REG64, offset 0x0018, 64-bit (R/W) + TEST_IRQ, offset 0x0020, 32-bit (R/W) + TEST_DMA_ADDR, offset 0x0028, 64-bit (R/W) + TEST_DMA_SIZE, offset 0x0030, 32-bit (R/W) + TEST_DMA_CTRL, offset 0x0034, 32-bit (R/W) + +Reads to TEST_REG and TEST_REG64 will read a value equal to twice the last +value written to the register. The 32-bit and 64-bit versions are for testing +32-bit and 64-bit host accesses. + +A vector can be written to TEST_IRQ and the device will generate an interrupt +for that vector. + +To test basic DMA operations, allocate a DMA-able host buffer and put the +buffer address into TEST_DMA_ADDR and size into TEST_DMA_SIZE. Then, write to +TEST_DMA_CTRL to manipulate the buffer contents. TEST_DMA_CTRL operations are:: + + operation value description + ----------------------------------------------------------- + TEST_DMA_CTRL_CLEAR 1 clear buffer + TEST_DMA_CTRL_FILL 2 fill buffer bytes with 0x96 + TEST_DMA_CTRL_INVERT 4 invert bytes in buffer + +Various buffer address and sizes should be tested to verify no address boundary +issue exists. In particular, buffers that start on odd-8-byte boundary and/or +span multiple PAGE sizes should be tested. + + +Ports +===== + +Physical and Logical Ports +------------------------------------ + +The switch supports up to 62 physical (front-panel) ports. Register +PORT_PHYS_COUNT returns the actual number of physical ports available:: + + PORT_PHYS_COUNT, offset 0x0304, 32-bit, (R) + +In addition to front-panel ports, the switch supports logical ports for +tunnels. + +Front-panel ports and logical tunnel ports are mapped into a single 32-bit port +space. A special CPU port is assigned port 0. The front-panel ports are +mapped to ports 1-62. A special loopback port is assigned port 63. Logical +tunnel ports are assigned ports 0x0001000-0x0001ffff. +To summarize the port assignments:: + + port mapping + ------------------------------------------------------- + 0 CPU port (for packets to/from host CPU) + 1-62 front-panel physical ports + 63 loopback port + 64-0x0000ffff RSVD + 0x00010000-0x0001ffff logical tunnel ports + 0x00020000-0xffffffff RSVD + +Physical Port Mode +------------------ + +Switch front-panel ports operate in a mode. Currently, the only mode is +OF-DPA. OF-DPA[1] mode is based on OpenFlow Data Plane Abstraction (OF-DPA) +Abstract Switch Specification, Version 1.0, from Broadcom Corporation. To +set/get the mode for front-panel ports, see port settings, below. + +Port Settings +------------- + +Link status for all front-panel ports is available via PORT_PHYS_LINK_STATUS:: + + PORT_PHYS_LINK_STATUS, offset 0x0310, 64-bit, (R) + + Value is port bitmap. Bits 0 and 63 always read 0. Bits 1-62 + read 1 for link UP and 0 for link DOWN for respective front-panel ports. + +Other properties for front-panel ports are available via DMA CMD descriptors:: + + Get PORT_SETTINGS descriptor: + + field width description + ---------------------------------------------- + PORT_SETTINGS 2 CMD_GET + PPORT 4 Physical port # + + Get PORT_SETTINGS completion: + + field width description + ---------------------------------------------- + PPORT 4 Physical port # + SPEED 4 Current port interface speed, in Mbps + DUPLEX 1 1 = Full, 0 = Half + AUTONEG 1 1 = enabled, 0 = disabled + MACADDR 6 Port MAC address + MODE 1 0 = OF-DPA + LEARNING 1 MAC address learning on port + 1 = enabled + 0 = disabled + PHYS_NAME Physical port name (string) + + Set PORT_SETTINGS descriptor: + + field width description + ---------------------------------------------- + PORT_SETTINGS 2 CMD_SET + PPORT 4 Physical port # + SPEED 4 Port interface speed, in Mbps + DUPLEX 1 1 = Full, 0 = Half + AUTONEG 1 1 = enabled, 0 = disabled + MACADDR 6 Port MAC address + MODE 1 0 = OF-DPA + +Port Enable +----------- + +Front-panel ports are initially disabled, which means port ingress and egress +packets will be dropped. To enable or disable a port, use PORT_PHYS_ENABLE:: + + PORT_PHYS_ENABLE: offset 0x0318, 64-bit, (R/W) + + Value is bitmap of first 64 ports. Bits 0 and 63 are ignored + and always read as 0. Write 1 to enable port; write 0 to disable it. + Default is 0. + + +Switch Control +============== + +This section covers switch-wide register settings. + +Control +------- + +This register is used for low level control of the switch:: + + CONTROL: offset 0x0300, 32-bit, (W) + + bit name description + ------------------------------------------------------------------------ + [0] CONTROL_RESET If set, device will perform reset + [1:31] Reserved + +Switch ID +--------- + +The switch has a SWITCH_ID to be used by software to uniquely identify the +switch:: + + SWITCH_ID: offset 0x0320, 64-bit, (R) + + Value is opaque to switch software and no special encoding is implied. + + +Events +====== + +Non-I/O asynchronous events from the device are notified to the host using the +event ring. The TLV structure for events is:: + + field width description + --------------------------------------------------- + TYPE 4 Event type, one of: + 1: LINK_CHANGED + 2: MAC_VLAN_SEEN + INFO Event info (details below) + +Link Changed Event +------------------ + +When link status changes on a physical port, this event is generated:: + + field width description + --------------------------------------------------- + INFO + PPORT 4 Physical port + LINKUP 1 Link status: + 0: down + 1: up + +MAC VLAN Seen Event +------------------- + +When a packet ingresses on a port and the source MAC/VLAN isn't known to the +device, the device will generate this event. In response to the event, the +driver should install to the device the MAC/VLAN on the port into the bridge +table. Once installed, the MAC/VLAN is known on the port and this event will +no longer be generated. + +:: + + field width description + --------------------------------------------------- + INFO + PPORT 4 Physical port + MAC 6 MAC address + VLAN 2 VLAN ID + + +CPU Packet Processing +===================== + +Ingress packets directed to the host CPU for further processing are delivered +in the DMA RX ring. Likewise, host CPU originating packets destined to egress +on switch ports are scheduled by software using the DMA TX ring. + +Tx Packet Processing +-------------------- + +Software schedules packets for egress on switch ports using the DMA TX ring. A +TX descriptor buffer describes the packet location and size in host DMA-able +memory, the destination port, and any hardware-offload functions (such as L3 +payload checksum offload). Software then bumps the descriptor head to signal +hardware of new Tx work. In response, hardware will DMA read Tx descriptors up +to head, DMA read descriptor buffer and packet data, perform offloading +functions, and finally frame packet on wire (network). Once packet processing +is complete, hardware will writeback status to descriptor(s) to signal to +software that Tx is complete and software resources (e.g. skb) backing packet +can be released. + +Figure 2 shows an example 3-fragment packet queued with one Tx descriptor. A +TLV is used for each packet fragment:: + + pkt frag 1 + +–––––––+ +–+ + +–––+ | | + desc buf | | | | + +––––––––+ | | | | + Tx ring +–––+ +–––––+ | | | + +–––––––––+ | | TLVs | +–––––––+ | + | +–––+ +––––––––+ pkt frag 2 | + | desc 0 | | +–––––+ +–––––––+ | + +–––––––––+ | TLVs | +–––+ | | + head+–+ | +––––––––+ | | | + | desc 1 | | +–––––+ +–––––––+ |pkt + +–––––––––+ | TLVs | | | + | | +––––––––+ | pkt frag 3 | + | | | +–––––––+ | + +–––––––––+ +–––+ | | + | | | | | + | | | | | + +–––––––––+ | | | + | | | | | + | | | | | + +–––––––––+ | | | + | | +–––––––+ +–+ + | | + +–––––––––+ + + fig 2. + +The TLVs for Tx descriptor buffer are:: + + field width description + --------------------------------------------------------------------- + PPORT 4 Destination physical port # + TX_OFFLOAD 1 Hardware offload modes: + 0: no offload + 1: insert IP csum (ipv4 only) + 2: insert TCP/UDP csum + 3: L3 csum calc and insert + into csum offset (TX_L3_CSUM_OFF) + 16-bit 1's complement csum value. + IPv4 pseudo-header and IP + already calculated by OS + and inserted. + 4: TSO (TCP Segmentation Offload) + TX_L3_CSUM_OFF 2 For L3 csum offload mode, the offset, + from the beginning of the packet, + of the csum field in the L3 header + TX_TSO_MSS 2 For TSO offload mode, the + Maximum Segment Size in bytes + TX_TSO_HDR_LEN 2 For TSO offload mode, the + length of ethernet, IP, and + TCP/UDP headers, including IP + and TCP options. + TX_FRAGS Packet fragments + TX_FRAG Packet fragment + TX_FRAG_ADDR 8 DMA address of packet fragment + TX_FRAG_LEN 2 Packet fragment length + +Possible status return codes in descriptor on completion are:: + + DESC_COMP_ERR reason + -------------------------------------------------------------------- + 0 OK + -ROCKER_ENXIO address or data read err on desc buf or packet + fragment + -ROCKER_EINVAL bad pport or TSO or csum offloading error + -ROCKER_ENOMEM no memory for internal staging tx fragment + +Rx Packet Processing +-------------------- + +For packets ingressing on switch ports that are not forwarded by the switch but +rather directed to the host CPU for further processing are delivered in the DMA +RX ring. Rx descriptor buffers are allocated by software and placed on the +ring. Hardware will fill Rx descriptor buffers with packet data, write the +completion, and signal to software that a new packet is ready. Since Rx packet +size is not known a-priori, the Rx descriptor buffer must be allocated for +worst-case packet size. A single Rx descriptor will contain the entire Rx +packet data in one RX_FRAG. Other Rx TLVs describe and hardware offloads +performed on the packet, such as checksum validation. + +The TLVs for Rx descriptor buffer are:: + + field width description + --------------------------------------------------- + PPORT 4 Source physical port # + RX_FLAGS 2 Packet parsing flags: + (1 << 0): IPv4 packet + (1 << 1): IPv6 packet + (1 << 2): csum calculated + (1 << 3): IPv4 csum good + (1 << 4): IP fragment + (1 << 5): TCP packet + (1 << 6): UDP packet + (1 << 7): TCP/UDP csum good + (1 << 8): Offload forward + RX_CSUM 2 IP calculated checksum: + IPv4: IP payload csum + IPv6: header and payload csum + (Only valid is RX_FLAGS:csum calc is set) + RX_FRAG_ADDR 8 DMA address of packet fragment + RX_FRAG_MAX_LEN 2 Packet maximum fragment length + RX_FRAG_LEN 2 Actual packet fragment length after receive + +Offload forward RX_FLAG indicates the device has already forwarded the packet +so the host CPU should not also forward the packet. + +Possible status return codes in descriptor on completion are:: + + DESC_COMP_ERR reason + -------------------------------------------------------------------- + 0 OK + -ROCKER_ENXIO address or data read err on desc buf + -ROCKER_ENOMEM no memory for internal staging desc buf + -ROCKER_EMSGSIZE Rx descriptor buffer wasn't big enough to contain + packet data TLV and other TLVs. + + +OF-DPA Mode +=========== + +OF-DPA mode allows the switch to offload flow packet processing functions to +hardware. An OpenFlow controller would communicate with an OpenFlow agent +installed on the switch. The OpenFlow agent would (directly or indirectly) +communicate with the Rocker switch driver, which in turn would program switch +hardware with flow functionality, as defined in OF-DPA. The block diagram is:: + + +–––––––––––––––----–––+ + | OF | + | Remote Controller | + +––––––––+––----–––––––+ + | + | + +––––––––+–––––––––+ + | OF | + | Local Agent | + +––––––––––––––––––+ + | | + | Rocker Driver | + +––––––––––––––––––+ + + +––––––––––––––––––+ + | | + | Rocker Switch | + +––––––––––––––––––+ + +To participate in flow functions, ports must be configure for OF-DPA mode +during switch initialization. + +OF-DPA Flow Table Interface +--------------------------- + +There are commands to add, modify, delete, and get stats of flow table entries. +The commands are issued using the DMA CMD descriptor ring. The following +commands are defined:: + + CMD_ADD: add an entry to flow table + CMD_MOD: modify an entry in flow table + CMD_DEL: delete an entry from flow table + CMD_GET_STATS: get stats for flow entry + +TLVs for add and modify commands are:: + + field width description + ---------------------------------------------------- + OF_DPA_CMD 2 CMD_[ADD|MOD] + OF_DPA_TBL 2 Flow table ID + 0: ingress port + 10: vlan + 20: termination mac + 30: unicast routing + 40: multicast routing + 50: bridging + 60: ACL policy + OF_DPA_PRIORITY 4 Flow priority + OF_DPA_HARDTIME 4 Hard timeout for flow + OF_DPA_IDLETIME 4 Idle timeout for flow + OF_DPA_COOKIE 8 Cookie + +Additional TLVs based on flow table ID: + +Table ID 0: ingress port:: + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + +Table ID 10: vlan:: + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_NEW_VLAN_ID 2 (N) new vlan ID + +Table ID 20: termination mac:: + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_IN_PPORT_MASK 4 ingress physical port number mask + OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd + OF_DPA_DST_MAC 6 (N) destination MAC + OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask + OF_DPA_GOTO_TBL 2 only acceptable values are + unicast or multicast routing + table IDs + OF_DPA_OUT_PPORT 2 if specified, must be + controller, set zero otherwise + +Table ID 30: unicast routing:: + + field width description + ---------------------------------------------------- + OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd + OF_DPA_DST_IP 4 (N) destination IPv4 address. + Must be unicast address + OF_DPA_DST_IP_MASK 4 (N) IP mask. Must be prefix mask + OF_DPA_DST_IPV6 16 (N) destination IPv6 address. + Must be unicast address + OF_DPA_DST_IPV6_MASK 16 (N) IPv6 mask. Must be prefix mask + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_GROUP_ID 4 data for GROUP action must + be an L3 Unicast group entry + +Table ID 40: multicast routing:: + + field width description + ---------------------------------------------------- + OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_SRC_IP 4 (N) source IPv4. Optional, + can contain IPv4 address, + must be completely masked + if not used + OF_DPA_SRC_IP_MASK 4 (N) IP Mask + OF_DPA_DST_IP 4 (N) destination IPv4 address. + Must be multicast address + OF_DPA_SRC_IPV6 16 (N) source IPv6 Address. Optional. + Can contain IPv6 address, + must be completely masked + if not used + OF_DPA_SRC_IPV6_MASK 16 (N) IPv6 mask. + OF_DPA_DST_IPV6 16 (N) destination IPv6 Address. Must + be multicast address + Must be multicast address + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_GROUP_ID 4 data for GROUP action must + be an L3 multicast group entry + +Table ID 50: bridging:: + + field width description + ---------------------------------------------------- + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_TUNNEL_ID 4 tunnel ID + OF_DPA_DST_MAC 6 (N) destination MAC + OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask + OF_DPA_GOTO_TBL 2 goto table ID; zero to drop + OF_DPA_GROUP_ID 4 data for GROUP action must + be a L2 Interface, L2 + Multicast, L2 Flood, + or L2 Overlay group entry + as appropriate + OF_DPA_TUNNEL_LPORT 4 unicast Tenant Bridging + flows specify a tunnel + logical port ID + OF_DPA_OUT_PPORT 2 data for OUTPUT action, + restricted to CONTROLLER, + set to 0 otherwise + +Table ID 60: acl policy:: + + field width description + ---------------------------------------------------- + OF_DPA_IN_PPORT 4 ingress physical port number + OF_DPA_IN_PPORT_MASK 4 ingress physical port number mask + OF_DPA_ETHERTYPE 2 (N) ethertype + OF_DPA_VLAN_ID 2 (N) vlan ID + OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask + OF_DPA_VLAN_PCP 2 (N) vlan Priority Code Point + OF_DPA_VLAN_PCP_MASK 2 (N) vlan Priority Code Point mask + OF_DPA_SRC_MAC 6 (N) source MAC + OF_DPA_SRC_MAC_MASK 6 (N) source MAC mask + OF_DPA_DST_MAC 6 (N) destination MAC + OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask + OF_DPA_TUNNEL_ID 4 tunnel ID + OF_DPA_SRC_IP 4 (N) source IPv4. Optional, + can contain IPv4 address, + must be completely masked + if not used + OF_DPA_SRC_IP_MASK 4 (N) IP Mask + OF_DPA_DST_IP 4 (N) destination IPv4 address. + Must be multicast address + OF_DPA_DST_IP_MASK 4 (N) IP Mask + OF_DPA_SRC_IPV6 16 (N) source IPv6 Address. Optional. + Can contain IPv6 address, + must be completely masked + if not used + OF_DPA_SRC_IPV6_MASK 16 (N) IPv6 mask + OF_DPA_DST_IPV6 16 (N) destination IPv6 Address. Must + be multicast address. + OF_DPA_DST_IPV6_MASK 16 (N) IPv6 mask + OF_DPA_SRC_ARP_IP 4 (N) source IPv4 address in the ARP + payload. Only used if ethertype + == 0x0806. + OF_DPA_SRC_ARP_IP_MASK 4 (N) IP Mask + OF_DPA_IP_PROTO 1 IP protocol + OF_DPA_IP_PROTO_MASK 1 IP protocol mask + OF_DPA_IP_DSCP 1 DSCP + OF_DPA_IP_DSCP_MASK 1 DSCP mask + OF_DPA_IP_ECN 1 ECN + OF_DPA_IP_ECN_MASK 1 ECN mask + OF_DPA_L4_SRC_PORT 2 (N) L4 source port, only for + TCP, UDP, or SCTP + OF_DPA_L4_SRC_PORT_MASK 2 (N) L4 source port mask + OF_DPA_L4_DST_PORT 2 (N) L4 source port, only for + TCP, UDP, or SCTP + OF_DPA_L4_DST_PORT_MASK 2 (N) L4 source port mask + OF_DPA_ICMP_TYPE 1 ICMP type, only if IP + protocol is 1 + OF_DPA_ICMP_TYPE_MASK 1 ICMP type mask + OF_DPA_ICMP_CODE 1 ICMP code + OF_DPA_ICMP_CODE_MASK 1 ICMP code mask + OF_DPA_IPV6_LABEL 4 (N) IPv6 flow label + OF_DPA_IPV6_LABEL_MASK 4 (N) IPv6 flow label mask + OF_DPA_GROUP_ID 4 data for GROUP action + OF_DPA_QUEUE_ID_ACTION 1 write the queue ID + OF_DPA_NEW_QUEUE_ID 1 queue ID + OF_DPA_VLAN_PCP_ACTION 1 write the VLAN priority + OF_DPA_NEW_VLAN_PCP 1 VLAN priority + OF_DPA_IP_DSCP_ACTION 1 write the DSCP + OF_DPA_NEW_IP_DSCP 1 new DSCP + OF_DPA_TUNNEL_LPORT 4 restrct to valid tunnel + logical port, set to 0 + otherwise. + OF_DPA_OUT_PPORT 2 data for OUTPUT action, + restricted to CONTROLLER, + set to 0 otherwise + OF_DPA_CLEAR_ACTIONS 4 if 1 packets matching flow are + dropped (all other instructions + ignored) + +TLVs for flow delete and get stats command are:: + + field width description + --------------------------------------------------- + OF_DPA_CMD 2 CMD_[DEL|GET_STATS] + OF_DPA_COOKIE 8 Cookie + +On completion of get stats command, the descriptor buffer is written back with +the following TLVs:: + + field width description + --------------------------------------------------- + OF_DPA_STAT_DURATION 4 Flow duration + OF_DPA_STAT_RX_PKTS 8 Received packets + OF_DPA_STAT_TX_PKTS 8 Transmit packets + +Possible status return codes in descriptor on completion are:: + + DESC_COMP_ERR command reason + -------------------------------------------------------------------- + 0 all OK + -ROCKER_EFAULT all head or tail index outside + of ring + -ROCKER_ENXIO all address or data read err on + desc buf + -ROCKER_EMSGSIZE GET_STATS cmd descriptor buffer wasn't + big enough to contain write-back + TLVs + -ROCKER_EINVAL all invalid parameters passed in + -ROCKER_EEXIST ADD entry already exists + -ROCKER_ENOSPC ADD no space left in flow table + -ROCKER_ENOENT MOD|DEL|GET_STATS cookie invalid + +Group Table Interface +--------------------- + +There are commands to add, modify, delete, and get stats of group table +entries. The commands are issued using the DMA CMD descriptor ring. The +following commands are defined:: + + CMD_ADD: add an entry to group table + CMD_MOD: modify an entry in group table + CMD_DEL: delete an entry from group table + CMD_GET_STATS: get stats for group entry + +TLVs for add and modify commands are:: + + field width description + ----------------------------------------------------------- + FLOW_GROUP_CMD 2 CMD_[ADD|MOD] + FLOW_GROUP_ID 2 Flow group ID + FLOW_GROUP_TYPE 1 Group type: + 0: L2 interface + 1: L2 rewrite + 2: L3 unicast + 3: L2 multicast + 4: L2 flood + 5: L3 interface + 6: L3 multicast + 7: L3 ECMP + 8: L2 overlay + FLOW_VLAN_ID 2 Vlan ID (types 0, 3, 4, 6) + FLOW_L2_PORT 2 Port (types 0) + FLOW_INDEX 4 Index (all types but 0) + FLOW_OVERLAY_TYPE 1 Overlay sub-type (type 8): + 0: Flood unicast tunnel + 1: Flood multicast tunnel + 2: Multicast unicast tunnel + 3: Multicast multicast tunnel + FLOW_GROUP_ACTION nest + FLOW_GROUP_ID 2 next group ID in chain (all + types except 0) + FLOW_OUT_PORT 4 egress port (types 0, 8) + FLOW_POP_VLAN_TAG 1 strip outer VLAN tag (type 1 + only) + FLOW_VLAN_ID 2 (types 1, 5) + FLOW_SRC_MAC 6 (types 1, 2, 5) + FLOW_DST_MAC 6 (types 1, 2) + +TLVs for flow delete and get stats command are:: + + field width description + ----------------------------------------------------------- + FLOW_GROUP_CMD 2 CMD_[DEL|GET_STATS] + FLOW_GROUP_ID 2 Flow group ID + +On completion of get stats command, the descriptor buffer is written back with +the following TLVs:: + + field width description + --------------------------------------------------- + FLOW_GROUP_ID 2 Flow group ID + FLOW_STAT_DURATION 4 Flow duration + FLOW_STAT_REF_COUNT 4 Flow reference count + FLOW_STAT_BUCKET_COUNT 4 Flow bucket count + +Possible status return codes in descriptor on completion are:: + + DESC_COMP_ERR command reason + -------------------------------------------------------------------- + 0 all OK + -ROCKER_EFAULT all head or tail index outside + of ring + -ROCKER_ENXIO all address or data read err on + desc buf + -ROCKER_ENOSPC GET_STATS cmd descriptor buffer wasn't + big enough to contain write-back + TLVs + -ROCKER_EINVAL ADD|MOD invalid parameters passed in + -ROCKER_EEXIST ADD entry already exists + -ROCKER_ENOSPC ADD no space left in flow table + -ROCKER_ENOENT MOD|DEL|GET_STATS group ID invalid + -ROCKER_EBUSY DEL group reference count non-zero + -ROCKER_ENODEV ADD next group ID doesn't exist + + + +References +========== + +[1] OpenFlow Data Plane Abstraction (OF-DPA) Abstract Switch Specification, +Version 1.0, from Broadcom Corporation, February 21, 2014. diff --git a/docs/specs/rocker.txt b/docs/specs/rocker.txt deleted file mode 100644 index 1857b31703..0000000000 --- a/docs/specs/rocker.txt +++ /dev/null @@ -1,1014 +0,0 @@ -Rocker Network Switch Register Programming Guide -Copyright (c) Scott Feldman -Copyright (c) Neil Horman -Version 0.11, 12/29/2014 - -LICENSE -======= - -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. - -SECTION 1: Introduction -======================= - -Overview --------- - -This document describes the hardware/software interface for the Rocker switch -device. The intended audience is authors of OS drivers and device emulation -software. - -Notations and Conventions -------------------------- - -o In register descriptions, [n:m] indicates a range from bit n to bit m, -inclusive. -o Use of leading 0x indicates a hexadecimal number. -o Use of leading 0b indicates a binary number. -o The use of RSVD or Reserved indicates that a bit or field is reserved for -future use. -o Field width is in bytes, unless otherwise noted. -o Register are (R) read-only, (R/W) read/write, (W) write-only, or (COR) clear -on read -o TLV values in network-byte-order are designated with (N). - - -SECTION 2: PCI Configuration Registers -====================================== - -PCI Configuration Space ------------------------ - -Each switch instance registers as a PCI device with PCI configuration space: - - offset width description value - --------------------------------------------- - 0x0 2 Vendor ID 0x1b36 - 0x2 2 Device ID 0x0006 - 0x4 4 Command/Status - 0x8 1 Revision ID 0x01 - 0x9 3 Class code 0x2800 - 0xC 1 Cache line size - 0xD 1 Latency timer - 0xE 1 Header type - 0xF 1 Built-in self test - 0x10 4 Base address low - 0x14 4 Base address high - 0x18-28 Reserved - 0x2C 2 Subsystem vendor ID * - 0x2E 2 Subsystem ID * - 0x30-38 Reserved - 0x3C 1 Interrupt line - 0x3D 1 Interrupt pin 0x00 - 0x3E 1 Min grant 0x00 - 0x3D 1 Max latency 0x00 - 0x40 1 TRDY timeout - 0x41 1 Retry count - 0x42 2 Reserved - - -* Assigned by sub-system implementation - -SECTION 3: Memory-Mapped Register Space -======================================= - -There are two memory-mapped BARs. BAR0 maps device register space and is -0x2000 in size. BAR1 maps MSI-X vector and PBA tables and is also 0x2000 in -size, allowing for 256 MSI-X vectors. - -All registers are 4 or 8 bytes long. It is assumed host software will access 4 -byte registers with one 4-byte access, and 8 byte registers with either two -4-byte accesses or a single 8-byte access. In the case of two 4-byte accesses, -access must be lower and then upper 4-bytes, in that order. - -BAR0 device register space is organized as follows: - - offset description - ------------------------------------------------------ - 0x0000-0x000f Bogus registers to catch misbehaving - drivers. Writes do nothing. Reads - back as 0xDEADBABE. - 0x0010-0x00ff Test registers - 0x0300-0x03ff General purpose registers - 0x1000-0x1fff Descriptor control - -Holes in register space are reserved. Writes to reserved registers do nothing. -Reads to reserved registers read back as 0. - -No fancy stuff like write-combining is enabled on any of the registers. - -BAR1 MSI-X register space is organized as follows: - - offset description - ------------------------------------------------------ - 0x0000-0x0fff MSI-X vector table (256 vectors total) - 0x1000-0x1fff MSI-X PBA table - - -SECTION 4: Interrupts, DMA, and Endianness -========================================== - -PCI Interrupts --------------- - -The device supports only MSI-X interrupts. BAR1 memory-mapped region contains -the MSI-X vector and PBA tables, with support for up to 256 MSI-X vectors. - -The vector assignment is: - - vector description - ----------------------------------------------------- - 0 Command descriptor ring completion - 1 Event descriptor ring completion - 2 Test operation completion - 3 RSVD - 4-255 Tx and Rx descriptor ring completion - Tx vector is even - Rx vector is odd - -A MSI-X vector table entry is 16 bytes: - - field offset width description - ------------------------------------------------------------- - lower_addr 0x0 4 [31:2] message address[31:2] - [1:0] Rsvd (4 byte alignment - required) - upper_addr 0x4 4 [31:19] Rsvd - [14:0] message address[46:32] - data 0x8 4 message data[31:0] - control 0xc 4 [31:1] Rsvd - [0] mask (0 = enable, - 1 = masked) - -Software should install the Interrupt Service Routine (ISR) before any ports -are enabled or any commands are issued on the command ring. - -DMA Operations --------------- - -DMA operations are used for packet DMA to/from the CPU, command and event -processing. Command processing includes statistical counters and table dumps, -table insertion/deletion, and more. Event processing provides an async -notification method for device-originating events. Each DMA operation has a -set of control registers to manage a descriptor ring. The descriptor rings are -allocated from contiguous host DMA-able memory and registers specify the rings -base address, size and current head and tail indices. Software always writes -the head, and hardware always writes the tail. - -The higher-order bit of DMA_DESC_COMP_ERR is used to mark hardware completion -of a descriptor. Software will clear this bit when posting a descriptor to the -ring, and hardware will set this bit when the descriptor is complete. - -Descriptor ring sizes must be a power of 2 and range from 2 to 64K entries. -Descriptor rings' base address must be 8-byte aligned. Descriptors must be -packed within ring. Each descriptor in each ring must also be aligned on an 8 -byte boundary. Each descriptor ring will have these registers: - - DMA_DESC_xxx_BASE_ADDR, offset 0x1000 + (x * 32), 64-bit, (R/W) - DMA_DESC_xxx_SIZE, offset 0x1008 + (x * 32), 32-bit, (R/W) - DMA_DESC_xxx_HEAD, offset 0x100c + (x * 32), 32-bit, (R/W) - DMA_DESC_xxx_TAIL, offset 0x1010 + (x * 32), 32-bit, (R) - DMA_DESC_xxx_CTRL, offset 0x1014 + (x * 32), 32-bit, (W) - DMA_DESC_xxx_CREDITS, offset 0x1018 + (x * 32), 32-bit, (R/W) - DMA_DESC_xxx_RSVD1, offset 0x101c + (x * 32), 32-bit, (R/W) - -Where x is descriptor ring index: - - index ring - -------------------- - 0 CMD - 1 EVENT - 2 TX (port 0) - 3 RX (port 0) - 4 TX (port 1) - 5 RX (port 1) - . - . - . - 124 TX (port 61) - 125 RX (port 61) - 126 Resv - 127 Resv - -Writing BASE_ADDR or SIZE will reset HEAD and TAIL to zero. HEAD cannot be -written past TAIL. To do so would wrap the ring. An empty ring is when HEAD -== TAIL. A full ring is when HEAD is one position behind TAIL. Both HEAD and -TAIL increment and modulo wrap at the ring size. - -CTRL register bits: - - bit name description - ------------------------------------------------------------------------ - [0] CTRL_RESET Reset the descriptor ring - [1:31] Reserved - -All descriptor types share some common fields: - - field width description - ------------------------------------------------------------------- - DMA_DESC_BUF_ADDR 8 Phys addr of desc payload, 8-byte - aligned - DMA_DESC_COOKIE 8 Desc cookie for completion matching, - upper-most bit is reserved - DMA_DESC_BUF_SIZE 2 Desc payload size in bytes - DMA_DESC_TLV_SIZE 2 Desc payload total size in bytes - used for TLVs. Must be <= - DMA_DESC_BUF_SIZE. - DMA_DESC_COMP_ERR 2 Completion status of associated - desc payload. High order bit is - clear on new descs, toggled by - hw for completed items. - -To support forward- and backward-compatibility, descriptor and completion -payloads are specified in TLV format. Fields are packed with Type=field name, -Length=field length, and Value=field value. Software will ignore unknown fields -filled in by the switch. Likewise, the switch will ignore unknown fields -filled in by software. - -Descriptor payload buffer is 8-byte aligned and TLVs are 8-byte aligned. The -value within a TLV is also 8-byte aligned. The (packed, 8 byte) TLV header is: - - field width description - ----------------------------- - type 4 TLV type - len 2 TLV value length - pad 2 Reserved - -The alignment requirements for descriptors and TLVs are to avoid unaligned -access exceptions in software. Note that the payload for each TLV is also -8 byte aligned. - -Figure 1 shows an example descriptor buffer with two TLVs. - - <------- 8 bytes -------> - - 8-byte +––––+ +–––––––––––+–––––+–––––+ +–+ - align | type | len | pad | TLV#1 hdr | - +–––––––––––+–––––+–––––+ (len=22) | - | | | - | value | TVL#1 value | - | | (padded to 8-byte | - | +–––––+ alignment) | - | |/////| | - 8-byte +––––+ +–––––––––––+–––––––––––+ | - align | type | len | pad | TLV#2 hdr DESC_BUF_SIZE - +–––––+–––––+–––––+–––––+ (len=2) | - |value|/////////////////| TLV#2 value | - +–––––+/////////////////| | - |///////////////////////| | - |///////////////////////| | - |///////////////////////| | - |////////unused/////////| | - |////////space//////////| | - |///////////////////////| | - |///////////////////////| | - |///////////////////////| | - +–––––––––––––––––––––––+ +–+ - - fig. 1 - -TLVs can be nested within the NEST TLV type. - -Interrupt credits -^^^^^^^^^^^^^^^^^ - -MSI-X vectors used for descriptor ring completions use a credit mechanism for -efficient device, PCIe bus, OS and driver operations. Each descriptor ring has -a credit count which represents the number of outstanding descriptors to be -processed by the driver. As the device marks descriptors complete, the credit -count is incremented. As the driver processes those outstanding descriptors, -it returns credits back to the device. This way, the device knows the driver's -progress and can make decisions about when to fire the next interrupt or not. -When the credit count is zero, and the first descriptors are posted for the -driver, a single interrupt is fired. Once the interrupt is fired, the -interrupt is disabled (auto-masked*). In response to the interrupt, the driver -will process descriptors and PIO write a returned credit value for that -descriptor ring. If the driver returns all credits (the driver caught up with -the device and there is no outstanding work), then the interrupt is unmasked, -but not fired. If only partial credits are returned, the interrupt remains -masked but the device generates an interrupt, signaling the driver that more -outstanding work is available. - -(* this masking is unrelated to the MSI-X interrupt mask register) - -Endianness ----------- - -Device registers are hard-coded to little-endian (LE). The driver should -convert to/from host endianness to LE for device register accesses. - -Descriptors are LE. Descriptor buffer TLVs will have LE type and length -fields, but the value field can either be LE or network-byte-order, depending -on context. TLV values containing network packet data will be in network-byte -order. A TLV value containing a field or mask used to compare against network -packet data is network-byte order. For example, flow match fields (and masks) -are network-byte-order since they're matched directly, byte-by-byte, against -network packet data. All non-network-packet TLV multi-byte values will be LE. - -TLV values in network-byte-order are designated with (N). - - -SECTION 5: Test Registers -========================= - -Rocker has several test registers to support troubleshooting register access, -interrupt generation, and DMA operations: - - TEST_REG, offset 0x0010, 32-bit (R/W) - TEST_REG64, offset 0x0018, 64-bit (R/W) - TEST_IRQ, offset 0x0020, 32-bit (R/W) - TEST_DMA_ADDR, offset 0x0028, 64-bit (R/W) - TEST_DMA_SIZE, offset 0x0030, 32-bit (R/W) - TEST_DMA_CTRL, offset 0x0034, 32-bit (R/W) - -Reads to TEST_REG and TEST_REG64 will read a value equal to twice the last -value written to the register. The 32-bit and 64-bit versions are for testing -32-bit and 64-bit host accesses. - -A vector can be written to TEST_IRQ and the device will generate an interrupt -for that vector. - -To test basic DMA operations, allocate a DMA-able host buffer and put the -buffer address into TEST_DMA_ADDR and size into TEST_DMA_SIZE. Then, write to -TEST_DMA_CTRL to manipulate the buffer contents. TEST_DMA_CTRL operations are: - - operation value description - ----------------------------------------------------------- - TEST_DMA_CTRL_CLEAR 1 clear buffer - TEST_DMA_CTRL_FILL 2 fill buffer bytes with 0x96 - TEST_DMA_CTRL_INVERT 4 invert bytes in buffer - -Various buffer address and sizes should be tested to verify no address boundary -issue exists. In particular, buffers that start on odd-8-byte boundary and/or -span multiple PAGE sizes should be tested. - - -SECTION 6: Ports -================ - -Physical and Logical Ports ------------------------------------- - -The switch supports up to 62 physical (front-panel) ports. Register -PORT_PHYS_COUNT returns the actual number of physical ports available: - - PORT_PHYS_COUNT, offset 0x0304, 32-bit, (R) - -In addition to front-panel ports, the switch supports logical ports for -tunnels. - -Front-panel ports and logical tunnel ports are mapped into a single 32-bit port -space. A special CPU port is assigned port 0. The front-panel ports are -mapped to ports 1-62. A special loopback port is assigned port 63. Logical -tunnel ports are assigned ports 0x0001000-0x0001ffff. -To summarize the port assignments: - - port mapping - ------------------------------------------------------- - 0 CPU port (for packets to/from host CPU) - 1-62 front-panel physical ports - 63 loopback port - 64-0x0000ffff RSVD - 0x00010000-0x0001ffff logical tunnel ports - 0x00020000-0xffffffff RSVD - -Physical Port Mode ------------------- - -Switch front-panel ports operate in a mode. Currently, the only mode is -OF-DPA. OF-DPA[1] mode is based on OpenFlow Data Plane Abstraction (OF-DPA) -Abstract Switch Specification, Version 1.0, from Broadcom Corporation. To -set/get the mode for front-panel ports, see port settings, below. - -Port Settings -------------- - -Link status for all front-panel ports is available via PORT_PHYS_LINK_STATUS: - - PORT_PHYS_LINK_STATUS, offset 0x0310, 64-bit, (R) - - Value is port bitmap. Bits 0 and 63 always read 0. Bits 1-62 - read 1 for link UP and 0 for link DOWN for respective front-panel ports. - -Other properties for front-panel ports are available via DMA CMD descriptors: - - Get PORT_SETTINGS descriptor: - - field width description - ---------------------------------------------- - PORT_SETTINGS 2 CMD_GET - PPORT 4 Physical port # - - Get PORT_SETTINGS completion: - - field width description - ---------------------------------------------- - PPORT 4 Physical port # - SPEED 4 Current port interface speed, in Mbps - DUPLEX 1 1 = Full, 0 = Half - AUTONEG 1 1 = enabled, 0 = disabled - MACADDR 6 Port MAC address - MODE 1 0 = OF-DPA - LEARNING 1 MAC address learning on port - 1 = enabled - 0 = disabled - PHYS_NAME Physical port name (string) - - Set PORT_SETTINGS descriptor: - - field width description - ---------------------------------------------- - PORT_SETTINGS 2 CMD_SET - PPORT 4 Physical port # - SPEED 4 Port interface speed, in Mbps - DUPLEX 1 1 = Full, 0 = Half - AUTONEG 1 1 = enabled, 0 = disabled - MACADDR 6 Port MAC address - MODE 1 0 = OF-DPA - -Port Enable ------------ - -Front-panel ports are initially disabled, which means port ingress and egress -packets will be dropped. To enable or disable a port, use PORT_PHYS_ENABLE: - - PORT_PHYS_ENABLE: offset 0x0318, 64-bit, (R/W) - - Value is bitmap of first 64 ports. Bits 0 and 63 are ignored - and always read as 0. Write 1 to enable port; write 0 to disable it. - Default is 0. - - -SECTION 7: Switch Control -========================= - -This section covers switch-wide register settings. - -Control -------- - -This register is used for low level control of the switch. - - CONTROL: offset 0x0300, 32-bit, (W) - - bit name description - ------------------------------------------------------------------------ - [0] CONTROL_RESET If set, device will perform reset - [1:31] Reserved - -Switch ID ---------- - -The switch has a SWITCH_ID to be used by software to uniquely identify the -switch: - - SWITCH_ID: offset 0x0320, 64-bit, (R) - - Value is opaque to switch software and no special encoding is implied. - - -SECTION 8: Events -================= - -Non-I/O asynchronous events from the device are notified to the host using the -event ring. The TLV structure for events is: - - field width description - --------------------------------------------------- - TYPE 4 Event type, one of: - 1: LINK_CHANGED - 2: MAC_VLAN_SEEN - INFO Event info (details below) - -Link Changed Event ------------------- - -When link status changes on a physical port, this event is generated. - - field width description - --------------------------------------------------- - INFO - PPORT 4 Physical port - LINKUP 1 Link status: - 0: down - 1: up - -MAC VLAN Seen Event -------------------- - -When a packet ingresses on a port and the source MAC/VLAN isn't known to the -device, the device will generate this event. In response to the event, the -driver should install to the device the MAC/VLAN on the port into the bridge -table. Once installed, the MAC/VLAN is known on the port and this event will -no longer be generated. - - field width description - --------------------------------------------------- - INFO - PPORT 4 Physical port - MAC 6 MAC address - VLAN 2 VLAN ID - - -SECTION 9: CPU Packet Processing -================================ - -Ingress packets directed to the host CPU for further processing are delivered -in the DMA RX ring. Likewise, host CPU originating packets destined to egress -on switch ports are scheduled by software using the DMA TX ring. - -Tx Packet Processing --------------------- - -Software schedules packets for egress on switch ports using the DMA TX ring. A -TX descriptor buffer describes the packet location and size in host DMA-able -memory, the destination port, and any hardware-offload functions (such as L3 -payload checksum offload). Software then bumps the descriptor head to signal -hardware of new Tx work. In response, hardware will DMA read Tx descriptors up -to head, DMA read descriptor buffer and packet data, perform offloading -functions, and finally frame packet on wire (network). Once packet processing -is complete, hardware will writeback status to descriptor(s) to signal to -software that Tx is complete and software resources (e.g. skb) backing packet -can be released. - -Figure 2 shows an example 3-fragment packet queued with one Tx descriptor. A -TLV is used for each packet fragment. - - pkt frag 1 - +–––––––+ +–+ - +–––+ | | - desc buf | | | | - +––––––––+ | | | | - Tx ring +–––+ +–––––+ | | | - +–––––––––+ | | TLVs | +–––––––+ | - | +–––+ +––––––––+ pkt frag 2 | - | desc 0 | | +–––––+ +–––––––+ | - +–––––––––+ | TLVs | +–––+ | | - head+–+ | +––––––––+ | | | - | desc 1 | | +–––––+ +–––––––+ |pkt - +–––––––––+ | TLVs | | | - | | +––––––––+ | pkt frag 3 | - | | | +–––––––+ | - +–––––––––+ +–––+ | | - | | | | | - | | | | | - +–––––––––+ | | | - | | | | | - | | | | | - +–––––––––+ | | | - | | +–––––––+ +–+ - | | - +–––––––––+ - - fig 2. - -The TLVs for Tx descriptor buffer are: - - field width description - --------------------------------------------------------------------- - PPORT 4 Destination physical port # - TX_OFFLOAD 1 Hardware offload modes: - 0: no offload - 1: insert IP csum (ipv4 only) - 2: insert TCP/UDP csum - 3: L3 csum calc and insert - into csum offset (TX_L3_CSUM_OFF) - 16-bit 1's complement csum value. - IPv4 pseudo-header and IP - already calculated by OS - and inserted. - 4: TSO (TCP Segmentation Offload) - TX_L3_CSUM_OFF 2 For L3 csum offload mode, the offset, - from the beginning of the packet, - of the csum field in the L3 header - TX_TSO_MSS 2 For TSO offload mode, the - Maximum Segment Size in bytes - TX_TSO_HDR_LEN 2 For TSO offload mode, the - length of ethernet, IP, and - TCP/UDP headers, including IP - and TCP options. - TX_FRAGS Packet fragments - TX_FRAG Packet fragment - TX_FRAG_ADDR 8 DMA address of packet fragment - TX_FRAG_LEN 2 Packet fragment length - -Possible status return codes in descriptor on completion are: - - DESC_COMP_ERR reason - -------------------------------------------------------------------- - 0 OK - -ROCKER_ENXIO address or data read err on desc buf or packet - fragment - -ROCKER_EINVAL bad pport or TSO or csum offloading error - -ROCKER_ENOMEM no memory for internal staging tx fragment - -Rx Packet Processing --------------------- - -For packets ingressing on switch ports that are not forwarded by the switch but -rather directed to the host CPU for further processing are delivered in the DMA -RX ring. Rx descriptor buffers are allocated by software and placed on the -ring. Hardware will fill Rx descriptor buffers with packet data, write the -completion, and signal to software that a new packet is ready. Since Rx packet -size is not known a-priori, the Rx descriptor buffer must be allocated for -worst-case packet size. A single Rx descriptor will contain the entire Rx -packet data in one RX_FRAG. Other Rx TLVs describe and hardware offloads -performed on the packet, such as checksum validation. - -The TLVs for Rx descriptor buffer are: - - field width description - --------------------------------------------------- - PPORT 4 Source physical port # - RX_FLAGS 2 Packet parsing flags: - (1 << 0): IPv4 packet - (1 << 1): IPv6 packet - (1 << 2): csum calculated - (1 << 3): IPv4 csum good - (1 << 4): IP fragment - (1 << 5): TCP packet - (1 << 6): UDP packet - (1 << 7): TCP/UDP csum good - (1 << 8): Offload forward - RX_CSUM 2 IP calculated checksum: - IPv4: IP payload csum - IPv6: header and payload csum - (Only valid is RX_FLAGS:csum calc is set) - RX_FRAG_ADDR 8 DMA address of packet fragment - RX_FRAG_MAX_LEN 2 Packet maximum fragment length - RX_FRAG_LEN 2 Actual packet fragment length after receive - -Offload forward RX_FLAG indicates the device has already forwarded the packet -so the host CPU should not also forward the packet. - -Possible status return codes in descriptor on completion are: - - DESC_COMP_ERR reason - -------------------------------------------------------------------- - 0 OK - -ROCKER_ENXIO address or data read err on desc buf - -ROCKER_ENOMEM no memory for internal staging desc buf - -ROCKER_EMSGSIZE Rx descriptor buffer wasn't big enough to contain - packet data TLV and other TLVs. - - -SECTION 10: OF-DPA Mode -====================== - -OF-DPA mode allows the switch to offload flow packet processing functions to -hardware. An OpenFlow controller would communicate with an OpenFlow agent -installed on the switch. The OpenFlow agent would (directly or indirectly) -communicate with the Rocker switch driver, which in turn would program switch -hardware with flow functionality, as defined in OF-DPA. The block diagram is: - - +–––––––––––––––----–––+ - | OF | - | Remote Controller | - +––––––––+––----–––––––+ - | - | - +––––––––+–––––––––+ - | OF | - | Local Agent | - +––––––––––––––––––+ - | | - | Rocker Driver | - +––––––––––––––––––+ - - +––––––––––––––––––+ - | | - | Rocker Switch | - +––––––––––––––––––+ - -To participate in flow functions, ports must be configure for OF-DPA mode -during switch initialization. - -OF-DPA Flow Table Interface ---------------------------- - -There are commands to add, modify, delete, and get stats of flow table entries. -The commands are issued using the DMA CMD descriptor ring. The following -commands are defined: - - CMD_ADD: add an entry to flow table - CMD_MOD: modify an entry in flow table - CMD_DEL: delete an entry from flow table - CMD_GET_STATS: get stats for flow entry - -TLVs for add and modify commands are: - - field width description - ---------------------------------------------------- - OF_DPA_CMD 2 CMD_[ADD|MOD] - OF_DPA_TBL 2 Flow table ID - 0: ingress port - 10: vlan - 20: termination mac - 30: unicast routing - 40: multicast routing - 50: bridging - 60: ACL policy - OF_DPA_PRIORITY 4 Flow priority - OF_DPA_HARDTIME 4 Hard timeout for flow - OF_DPA_IDLETIME 4 Idle timeout for flow - OF_DPA_COOKIE 8 Cookie - -Additional TLVs based on flow table ID: - -Table ID 0: ingress port - - field width description - ---------------------------------------------------- - OF_DPA_IN_PPORT 4 ingress physical port number - OF_DPA_GOTO_TBL 2 goto table ID; zero to drop - -Table ID 10: vlan - - field width description - ---------------------------------------------------- - OF_DPA_IN_PPORT 4 ingress physical port number - OF_DPA_VLAN_ID 2 (N) vlan ID - OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask - OF_DPA_GOTO_TBL 2 goto table ID; zero to drop - OF_DPA_NEW_VLAN_ID 2 (N) new vlan ID - -Table ID 20: termination mac - - field width description - ---------------------------------------------------- - OF_DPA_IN_PPORT 4 ingress physical port number - OF_DPA_IN_PPORT_MASK 4 ingress physical port number mask - OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd - OF_DPA_DST_MAC 6 (N) destination MAC - OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask - OF_DPA_VLAN_ID 2 (N) vlan ID - OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask - OF_DPA_GOTO_TBL 2 only acceptable values are - unicast or multicast routing - table IDs - OF_DPA_OUT_PPORT 2 if specified, must be - controller, set zero otherwise - -Table ID 30: unicast routing - - field width description - ---------------------------------------------------- - OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd - OF_DPA_DST_IP 4 (N) destination IPv4 address. - Must be unicast address - OF_DPA_DST_IP_MASK 4 (N) IP mask. Must be prefix mask - OF_DPA_DST_IPV6 16 (N) destination IPv6 address. - Must be unicast address - OF_DPA_DST_IPV6_MASK 16 (N) IPv6 mask. Must be prefix mask - OF_DPA_GOTO_TBL 2 goto table ID; zero to drop - OF_DPA_GROUP_ID 4 data for GROUP action must - be an L3 Unicast group entry - -Table ID 40: multicast routing - - field width description - ---------------------------------------------------- - OF_DPA_ETHERTYPE 2 (N) must be either 0x0800 or 0x86dd - OF_DPA_VLAN_ID 2 (N) vlan ID - OF_DPA_SRC_IP 4 (N) source IPv4. Optional, - can contain IPv4 address, - must be completely masked - if not used - OF_DPA_SRC_IP_MASK 4 (N) IP Mask - OF_DPA_DST_IP 4 (N) destination IPv4 address. - Must be multicast address - OF_DPA_SRC_IPV6 16 (N) source IPv6 Address. Optional. - Can contain IPv6 address, - must be completely masked - if not used - OF_DPA_SRC_IPV6_MASK 16 (N) IPv6 mask. - OF_DPA_DST_IPV6 16 (N) destination IPv6 Address. Must - be multicast address - Must be multicast address - OF_DPA_GOTO_TBL 2 goto table ID; zero to drop - OF_DPA_GROUP_ID 4 data for GROUP action must - be an L3 multicast group entry - -Table ID 50: bridging - - field width description - ---------------------------------------------------- - OF_DPA_VLAN_ID 2 (N) vlan ID - OF_DPA_TUNNEL_ID 4 tunnel ID - OF_DPA_DST_MAC 6 (N) destination MAC - OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask - OF_DPA_GOTO_TBL 2 goto table ID; zero to drop - OF_DPA_GROUP_ID 4 data for GROUP action must - be a L2 Interface, L2 - Multicast, L2 Flood, - or L2 Overlay group entry - as appropriate - OF_DPA_TUNNEL_LPORT 4 unicast Tenant Bridging - flows specify a tunnel - logical port ID - OF_DPA_OUT_PPORT 2 data for OUTPUT action, - restricted to CONTROLLER, - set to 0 otherwise - -Table ID 60: acl policy - - field width description - ---------------------------------------------------- - OF_DPA_IN_PPORT 4 ingress physical port number - OF_DPA_IN_PPORT_MASK 4 ingress physical port number mask - OF_DPA_ETHERTYPE 2 (N) ethertype - OF_DPA_VLAN_ID 2 (N) vlan ID - OF_DPA_VLAN_ID_MASK 2 (N) vlan ID mask - OF_DPA_VLAN_PCP 2 (N) vlan Priority Code Point - OF_DPA_VLAN_PCP_MASK 2 (N) vlan Priority Code Point mask - OF_DPA_SRC_MAC 6 (N) source MAC - OF_DPA_SRC_MAC_MASK 6 (N) source MAC mask - OF_DPA_DST_MAC 6 (N) destination MAC - OF_DPA_DST_MAC_MASK 6 (N) destination MAC mask - OF_DPA_TUNNEL_ID 4 tunnel ID - OF_DPA_SRC_IP 4 (N) source IPv4. Optional, - can contain IPv4 address, - must be completely masked - if not used - OF_DPA_SRC_IP_MASK 4 (N) IP Mask - OF_DPA_DST_IP 4 (N) destination IPv4 address. - Must be multicast address - OF_DPA_DST_IP_MASK 4 (N) IP Mask - OF_DPA_SRC_IPV6 16 (N) source IPv6 Address. Optional. - Can contain IPv6 address, - must be completely masked - if not used - OF_DPA_SRC_IPV6_MASK 16 (N) IPv6 mask - OF_DPA_DST_IPV6 16 (N) destination IPv6 Address. Must - be multicast address. - OF_DPA_DST_IPV6_MASK 16 (N) IPv6 mask - OF_DPA_SRC_ARP_IP 4 (N) source IPv4 address in the ARP - payload. Only used if ethertype - == 0x0806. - OF_DPA_SRC_ARP_IP_MASK 4 (N) IP Mask - OF_DPA_IP_PROTO 1 IP protocol - OF_DPA_IP_PROTO_MASK 1 IP protocol mask - OF_DPA_IP_DSCP 1 DSCP - OF_DPA_IP_DSCP_MASK 1 DSCP mask - OF_DPA_IP_ECN 1 ECN - OF_DPA_IP_ECN_MASK 1 ECN mask - OF_DPA_L4_SRC_PORT 2 (N) L4 source port, only for - TCP, UDP, or SCTP - OF_DPA_L4_SRC_PORT_MASK 2 (N) L4 source port mask - OF_DPA_L4_DST_PORT 2 (N) L4 source port, only for - TCP, UDP, or SCTP - OF_DPA_L4_DST_PORT_MASK 2 (N) L4 source port mask - OF_DPA_ICMP_TYPE 1 ICMP type, only if IP - protocol is 1 - OF_DPA_ICMP_TYPE_MASK 1 ICMP type mask - OF_DPA_ICMP_CODE 1 ICMP code - OF_DPA_ICMP_CODE_MASK 1 ICMP code mask - OF_DPA_IPV6_LABEL 4 (N) IPv6 flow label - OF_DPA_IPV6_LABEL_MASK 4 (N) IPv6 flow label mask - OF_DPA_GROUP_ID 4 data for GROUP action - OF_DPA_QUEUE_ID_ACTION 1 write the queue ID - OF_DPA_NEW_QUEUE_ID 1 queue ID - OF_DPA_VLAN_PCP_ACTION 1 write the VLAN priority - OF_DPA_NEW_VLAN_PCP 1 VLAN priority - OF_DPA_IP_DSCP_ACTION 1 write the DSCP - OF_DPA_NEW_IP_DSCP 1 new DSCP - OF_DPA_TUNNEL_LPORT 4 restrct to valid tunnel - logical port, set to 0 - otherwise. - OF_DPA_OUT_PPORT 2 data for OUTPUT action, - restricted to CONTROLLER, - set to 0 otherwise - OF_DPA_CLEAR_ACTIONS 4 if 1 packets matching flow are - dropped (all other instructions - ignored) - -TLVs for flow delete and get stats command are: - - field width description - --------------------------------------------------- - OF_DPA_CMD 2 CMD_[DEL|GET_STATS] - OF_DPA_COOKIE 8 Cookie - -On completion of get stats command, the descriptor buffer is written back with -the following TLVs: - - field width description - --------------------------------------------------- - OF_DPA_STAT_DURATION 4 Flow duration - OF_DPA_STAT_RX_PKTS 8 Received packets - OF_DPA_STAT_TX_PKTS 8 Transmit packets - -Possible status return codes in descriptor on completion are: - - DESC_COMP_ERR command reason - -------------------------------------------------------------------- - 0 all OK - -ROCKER_EFAULT all head or tail index outside - of ring - -ROCKER_ENXIO all address or data read err on - desc buf - -ROCKER_EMSGSIZE GET_STATS cmd descriptor buffer wasn't - big enough to contain write-back - TLVs - -ROCKER_EINVAL all invalid parameters passed in - -ROCKER_EEXIST ADD entry already exists - -ROCKER_ENOSPC ADD no space left in flow table - -ROCKER_ENOENT MOD|DEL|GET_STATS cookie invalid - -Group Table Interface ---------------------- - -There are commands to add, modify, delete, and get stats of group table -entries. The commands are issued using the DMA CMD descriptor ring. The -following commands are defined: - - CMD_ADD: add an entry to group table - CMD_MOD: modify an entry in group table - CMD_DEL: delete an entry from group table - CMD_GET_STATS: get stats for group entry - -TLVs for add and modify commands are: - - field width description - ----------------------------------------------------------- - FLOW_GROUP_CMD 2 CMD_[ADD|MOD] - FLOW_GROUP_ID 2 Flow group ID - FLOW_GROUP_TYPE 1 Group type: - 0: L2 interface - 1: L2 rewrite - 2: L3 unicast - 3: L2 multicast - 4: L2 flood - 5: L3 interface - 6: L3 multicast - 7: L3 ECMP - 8: L2 overlay - FLOW_VLAN_ID 2 Vlan ID (types 0, 3, 4, 6) - FLOW_L2_PORT 2 Port (types 0) - FLOW_INDEX 4 Index (all types but 0) - FLOW_OVERLAY_TYPE 1 Overlay sub-type (type 8): - 0: Flood unicast tunnel - 1: Flood multicast tunnel - 2: Multicast unicast tunnel - 3: Multicast multicast tunnel - FLOW_GROUP_ACTION nest - FLOW_GROUP_ID 2 next group ID in chain (all - types except 0) - FLOW_OUT_PORT 4 egress port (types 0, 8) - FLOW_POP_VLAN_TAG 1 strip outer VLAN tag (type 1 - only) - FLOW_VLAN_ID 2 (types 1, 5) - FLOW_SRC_MAC 6 (types 1, 2, 5) - FLOW_DST_MAC 6 (types 1, 2) - -TLVs for flow delete and get stats command are: - - field width description - ----------------------------------------------------------- - FLOW_GROUP_CMD 2 CMD_[DEL|GET_STATS] - FLOW_GROUP_ID 2 Flow group ID - -On completion of get stats command, the descriptor buffer is written back with -the following TLVs: - - field width description - --------------------------------------------------- - FLOW_GROUP_ID 2 Flow group ID - FLOW_STAT_DURATION 4 Flow duration - FLOW_STAT_REF_COUNT 4 Flow reference count - FLOW_STAT_BUCKET_COUNT 4 Flow bucket count - -Possible status return codes in descriptor on completion are: - - DESC_COMP_ERR command reason - -------------------------------------------------------------------- - 0 all OK - -ROCKER_EFAULT all head or tail index outside - of ring - -ROCKER_ENXIO all address or data read err on - desc buf - -ROCKER_ENOSPC GET_STATS cmd descriptor buffer wasn't - big enough to contain write-back - TLVs - -ROCKER_EINVAL ADD|MOD invalid parameters passed in - -ROCKER_EEXIST ADD entry already exists - -ROCKER_ENOSPC ADD no space left in flow table - -ROCKER_ENOENT MOD|DEL|GET_STATS group ID invalid - -ROCKER_EBUSY DEL group reference count non-zero - -ROCKER_ENODEV ADD next group ID doesn't exist - - - -References -========== - -[1] OpenFlow Data Plane Abstraction (OF-DPA) Abstract Switch Specification, -Version 1.0, from Broadcom Corporation, February 21, 2014. diff --git a/docs/specs/spdm.rst b/docs/specs/spdm.rst new file mode 100644 index 0000000000..f7de080ff0 --- /dev/null +++ b/docs/specs/spdm.rst @@ -0,0 +1,134 @@ +====================================================== +QEMU Security Protocols and Data Models (SPDM) Support +====================================================== + +SPDM enables authentication, attestation and key exchange to assist in +providing infrastructure security enablement. It's a standard published +by the `DMTF`_. + +QEMU supports connecting to a SPDM responder implementation. This allows an +external application to emulate the SPDM responder logic for an SPDM device. + +Setting up a SPDM server +======================== + +When using QEMU with SPDM devices QEMU will connect to a server which +implements the SPDM functionality. + +SPDM-Utils +---------- + +You can use `SPDM Utils`_ to emulate a responder. This is the simplest method. + +SPDM-Utils is a Linux applications to manage, test and develop devices +supporting DMTF Security Protocol and Data Model (SPDM). It is written in Rust +and utilises libspdm. + +To use SPDM-Utils you will need to do the following steps. Details are included +in the SPDM-Utils README. + + 1. `Build libspdm`_ + 2. `Build SPDM Utils`_ + 3. `Run it as a server`_ + +spdm-emu +-------- + +You can use `spdm emu`_ to model the +SPDM responder. + +.. code-block:: shell + + $ cd spdm-emu + $ git submodule init; git submodule update --recursive + $ mkdir build; cd build + $ cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Debug -DCRYPTO=openssl .. + $ make -j32 + $ make copy_sample_key # Build certificates, required for SPDM authentication. + +It is worth noting that the certificates should be in compliance with +PCIe r6.1 sec 6.31.3. This means you will need to add the following to +openssl.cnf + +.. code-block:: + + subjectAltName = otherName:2.23.147;UTF8:Vendor=1b36:Device=0010:CC=010802:REV=02:SSVID=1af4:SSID=1100 + 2.23.147 = ASN1:OID:2.23.147 + +and then manually regenerate some certificates with: + +.. code-block:: shell + + $ openssl req -nodes -newkey ec:param.pem -keyout end_responder.key \ + -out end_responder.req -sha384 -batch \ + -subj "/CN=DMTF libspdm ECP384 responder cert" + + $ openssl x509 -req -in end_responder.req -out end_responder.cert \ + -CA inter.cert -CAkey inter.key -sha384 -days 3650 -set_serial 3 \ + -extensions v3_end -extfile ../openssl.cnf + + $ openssl asn1parse -in end_responder.cert -out end_responder.cert.der + + $ cat ca.cert.der inter.cert.der end_responder.cert.der > bundle_responder.certchain.der + +You can use SPDM-Utils instead as it will generate the correct certificates +automatically. + +The responder can then be launched with + +.. code-block:: shell + + $ cd bin + $ ./spdm_responder_emu --trans PCI_DOE + +Connecting an SPDM NVMe device +============================== + +Once a SPDM server is running we can start QEMU and connect to the server. + +For an NVMe device first let's setup a block we can use + +.. code-block:: shell + + $ cd qemu-spdm/linux/image + $ dd if=/dev/zero of=blknvme bs=1M count=2096 # 2GB NNMe Drive + +Then you can add this to your QEMU command line: + +.. code-block:: shell + + -drive file=blknvme,if=none,id=mynvme,format=raw \ + -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323 + +At which point QEMU will try to connect to the SPDM server. + +Note that if using x64-64 you will want to use the q35 machine instead +of the default. So the entire QEMU command might look like this + +.. code-block:: shell + + qemu-system-x86_64 -M q35 \ + --kernel bzImage \ + -drive file=rootfs.ext2,if=virtio,format=raw \ + -append "root=/dev/vda console=ttyS0" \ + -net none -nographic \ + -drive file=blknvme,if=none,id=mynvme,format=raw \ + -device nvme,drive=mynvme,serial=deadbeef,spdm_port=2323 + +.. _DMTF: + https://www.dmtf.org/standards/SPDM + +.. _SPDM Utils: + https://github.com/westerndigitalcorporation/spdm-utils + +.. _spdm emu: + https://github.com/dmtf/spdm-emu + +.. _Build libspdm: + https://github.com/westerndigitalcorporation/spdm-utils?tab=readme-ov-file#build-libspdm + +.. _Build SPDM Utils: + https://github.com/westerndigitalcorporation/spdm-utils?tab=readme-ov-file#build-the-binary + +.. _Run it as a server: + https://github.com/westerndigitalcorporation/spdm-utils#qemu-spdm-device-emulation 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 535912a92b..1ad36ad709 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 ------------- @@ -330,16 +336,16 @@ In case a pSeries machine is emulated, use the following command line: -tpmdev emulator,id=tpm0,chardev=chrtpm \ -device tpm-spapr,tpmdev=tpm0 \ -device spapr-vscsi,id=scsi0,reg=0x00002000 \ - -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ + -device virtio-blk-pci,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \ -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0 In case an Arm virt machine is emulated, use the following command line: .. code-block:: console - qemu-system-aarch64 -machine virt,gic-version=3,accel=kvm \ + 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 \ @@ -348,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'. diff --git a/docs/specs/virt-ctlr.rst b/docs/specs/virt-ctlr.rst new file mode 100644 index 0000000000..ad3edde82d --- /dev/null +++ b/docs/specs/virt-ctlr.rst @@ -0,0 +1,24 @@ +Virtual System Controller +========================= + +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. + +This is a MMIO mapped device using 256 bytes. + +Two 32bit registers are defined: + +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). + +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). + A basic command, no-op (0), is always present and can be used to test the + register access. This command has no effect. diff --git a/docs/specs/virt-ctlr.txt b/docs/specs/virt-ctlr.txt deleted file mode 100644 index 24d38084f7..0000000000 --- a/docs/specs/virt-ctlr.txt +++ /dev/null @@ -1,26 +0,0 @@ -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 specification can evolve, the current state is defined as below. - -This is a MMIO mapped device using 256 bytes. - -Two 32bit registers are defined: - -1- 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) - - 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). - A basic command, no-op (0), is always present and can be used to test the - register access. This command has no effect. 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-static/theme_overrides.css b/docs/sphinx-static/theme_overrides.css index c70ef95128..965ecac54f 100644 --- a/docs/sphinx-static/theme_overrides.css +++ b/docs/sphinx-static/theme_overrides.css @@ -87,6 +87,55 @@ div[class^="highlight"] pre { padding-bottom: 1px; } +/* qmp-example directive styling */ + +.rst-content .admonition-example { + /* do not apply the standard admonition background */ + background-color: transparent; + border: solid #ffd2ed 1px; +} + +.rst-content .admonition-example > .admonition-title:before { + content: "▷"; +} + +.rst-content .admonition-example > .admonition-title { + background-color: #5980a6; +} + +.rst-content .admonition-example > div[class^="highlight"] { + /* make code boxes take up the full width of the admonition w/o margin */ + margin-left: -12px; + margin-right: -12px; + + border-top: 1px solid #ffd2ed; + border-bottom: 1px solid #ffd2ed; + border-left: 0px; + border-right: 0px; +} + +.rst-content .admonition-example > div[class^="highlight"]:nth-child(2) { + /* If a code box is the second element in an example admonition, + * it is the first child after the title. let it sit flush against + * the title. */ + margin-top: -12px; + border-top: 0px; +} + +.rst-content .admonition-example > div[class^="highlight"]:last-child { + /* If a code box is the final element in an example admonition, don't + * render margin below it; let it sit flush with the end of the + * admonition box */ + margin-bottom: -12px; + border-bottom: 0px; +} + +.rst-content .admonition-example .highlight { + background-color: #fffafd; +} + +/* end qmp-example styling */ + @media screen { /* content column 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/depfile.py b/docs/sphinx/depfile.py index afdcbcec6e..e74be6af98 100644 --- a/docs/sphinx/depfile.py +++ b/docs/sphinx/depfile.py @@ -19,7 +19,7 @@ __version__ = '1.0' def get_infiles(env): for x in env.found_docs: - yield env.doc2path(x) + yield str(env.doc2path(x)) yield from ((os.path.join(env.srcdir, dep) for dep in env.dependencies[x])) for mod in sys.modules.values(): 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..a84723be19 100644 --- a/docs/sphinx/hxtool.py +++ b/docs/sphinx/hxtool.py @@ -24,16 +24,10 @@ from docutils import nodes from docutils.statemachine import ViewList from docutils.parsers.rst import directives, Directive from sphinx.errors import ExtensionError +from sphinx.util.docutils import switch_source_input from sphinx.util.nodes import nested_parse_with_titles import sphinx -# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later -# use switch_source_input. Check borrowed from kerneldoc.py. -Use_SSI = sphinx.__version__[:3] >= '1.7' -if Use_SSI: - from sphinx.util.docutils import switch_source_input -else: - from sphinx.ext.autodoc import AutodocReporter __version__ = '1.0' @@ -49,7 +43,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 +72,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 +115,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') @@ -169,16 +179,9 @@ class HxtoolDocDirective(Directive): # of title_styles and section_level that kerneldoc.py does, # because nested_parse_with_titles() does that for us. def do_parse(self, result, node): - if Use_SSI: - with switch_source_input(self.state, result): - nested_parse_with_titles(self.state, result, node) - else: - save = self.state.memo.reporter - self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) - try: - nested_parse_with_titles(self.state, result, node) - finally: - self.state.memo.reporter = save + with switch_source_input(self.state, result): + nested_parse_with_titles(self.state, result, node) + def setup(app): """ Register hxtool-doc directive with Sphinx""" diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index bf44215016..3aa972f2e8 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -38,20 +38,14 @@ from docutils import nodes, statemachine from docutils.statemachine import ViewList from docutils.parsers.rst import directives, Directive -# -# AutodocReporter is only good up to Sphinx 1.7 -# import sphinx +from sphinx.util import logging +from sphinx.util.docutils import switch_source_input -Use_SSI = sphinx.__version__[:3] >= '1.7' -if Use_SSI: - from sphinx.util.docutils import switch_source_input -else: - from sphinx.ext.autodoc import AutodocReporter - -import kernellog __version__ = '1.0' +logger = logging.getLogger('kerneldoc') + class KernelDocDirective(Directive): """Extract kernel-doc comments from the specified file""" @@ -74,6 +68,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 = [] @@ -107,8 +105,7 @@ class KernelDocDirective(Directive): cmd += [filename] try: - kernellog.verbose(env.app, - 'calling kernel-doc \'%s\'' % (" ".join(cmd))) + logger.verbose('calling kernel-doc \'%s\'' % (" ".join(cmd))) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() @@ -118,8 +115,10 @@ class KernelDocDirective(Directive): if p.returncode != 0: sys.stderr.write(err) - kernellog.warn(env.app, - 'kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode)) + logger.warning( + 'kernel-doc \'%s\' failed with return code %d' % + (" ".join(cmd), p.returncode) + ) return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] elif env.config.kerneldoc_verbosity > 0: sys.stderr.write(err) @@ -145,28 +144,20 @@ class KernelDocDirective(Directive): return node.children except Exception as e: # pylint: disable=W0703 - kernellog.warn(env.app, 'kernel-doc \'%s\' processing failed with: %s' % + logger.warning('kernel-doc \'%s\' processing failed with: %s' % (" ".join(cmd), str(e))) return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] def do_parse(self, result, node): - if Use_SSI: - with switch_source_input(self.state, result): - self.state.nested_parse(result, 0, node, match_titles=1) - else: - save = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter - self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) - self.state.memo.title_styles, self.state.memo.section_level = [], 0 - try: - self.state.nested_parse(result, 0, node, match_titles=1) - finally: - self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = save + with switch_source_input(self.state, result): + self.state.nested_parse(result, 0, node, match_titles=1) 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/kernellog.py b/docs/sphinx/kernellog.py deleted file mode 100644 index af924f51a7..0000000000 --- a/docs/sphinx/kernellog.py +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Sphinx has deprecated its older logging interface, but the replacement -# only goes back to 1.6. So here's a wrapper layer to keep around for -# as long as we support 1.4. -# -import sphinx - -if sphinx.__version__[:3] >= '1.6': - UseLogging = True - from sphinx.util import logging - logger = logging.getLogger('kerneldoc') -else: - UseLogging = False - -def warn(app, message): - if UseLogging: - logger.warning(message) - else: - app.warn(message) - -def verbose(app, message): - if UseLogging: - logger.verbose(message) - else: - app.verbose(message) - - diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index d791b59492..5f96b46270 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -26,38 +26,42 @@ https://www.sphinx-doc.org/en/master/development/index.html import os import re +import sys +import textwrap +from typing import List from docutils import nodes +from docutils.parsers.rst import Directive, directives from docutils.statemachine import ViewList -from docutils.parsers.rst import directives, Directive -from sphinx.errors import ExtensionError -from sphinx.util.nodes import nested_parse_with_titles -import sphinx -from qapi.gen import QAPISchemaVisitor from qapi.error import QAPIError, QAPISemError +from qapi.gen import QAPISchemaVisitor from qapi.schema import QAPISchema - -# Sphinx up to 1.6 uses AutodocReporter; 1.7 and later -# use switch_source_input. Check borrowed from kerneldoc.py. -Use_SSI = sphinx.__version__[:3] >= '1.7' -if Use_SSI: - from sphinx.util.docutils import switch_source_input -else: - from sphinx.ext.autodoc import AutodocReporter +from sphinx import addnodes +from sphinx.directives.code import CodeBlock +from sphinx.errors import ExtensionError +from sphinx.util.docutils import switch_source_input +from sphinx.util.nodes import nested_parse_with_titles -__version__ = '1.0' +__version__ = "1.0" -# Function borrowed from pydash, which is under the MIT license -def intersperse(iterable, separator): - """Yield the members of *iterable* interspersed with *separator*.""" - iterable = iter(iterable) - yield next(iterable) - for item in iterable: - yield separator - yield item +def dedent(text: str) -> str: + # Adjust indentation to make description text parse as paragraph. + + lines = text.splitlines(True) + if re.match(r"\s+", lines[0]): + # First line is indented; description started on the line after + # the name. dedent the whole block. + return textwrap.dedent(text) + + # Descr started on same line. Dedent line 2+. + return lines[0] + textwrap.dedent("".join(lines[1:])) + + +# Disable black auto-formatter until re-enabled: +# fmt: off class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): @@ -145,35 +149,29 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): term.extend(self._nodes_for_ifcond(member.ifcond)) return term - def _nodes_for_variant_when(self, variants, variant): + def _nodes_for_variant_when(self, branches, variant): """Return list of Text, literal nodes for variant 'when' clause Return a list of doctree nodes which give text like 'when tagname is variant (If: ...)' suitable for use in - the 'variants' part of a definition list. + the 'branches' part of a definition list. """ term = [nodes.Text(' when '), - nodes.literal('', variants.tag_member.name), + nodes.literal('', branches.tag_member.name), nodes.Text(' is '), nodes.literal('', '"%s"' % variant.name)] if variant.ifcond.is_present(): term.extend(self._nodes_for_ifcond(variant.ifcond)) return term - def _nodes_for_members(self, doc, what, base=None, variants=None): + def _nodes_for_members(self, doc, what, base=None, branches=None): """Return list of doctree nodes for the table of members""" dlnode = nodes.definition_list() for section in doc.args.values(): term = self._nodes_for_one_member(section.member) # 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(', '))) + defn = dedent(section.text) else: defn = [nodes.Text('Not documented')] @@ -184,19 +182,15 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): nodes.literal('', base.doc_type())], None) - 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 branches: + for v in branches.variants: + 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(branches, v)) + dlnode += self._make_dlitem(term, None) if not dlnode.children: return [] @@ -215,7 +209,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) # TODO drop fallbacks when undocumented members are outlawed if section.text: - defn = section.text + defn = dedent(section.text) else: defn = [nodes.Text('Not documented')] @@ -229,15 +223,15 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): section += dlnode return [section] - def _nodes_for_arguments(self, doc, boxed_arg_type): + def _nodes_for_arguments(self, doc, arg_type): """Return list of doctree nodes for the arguments section""" - if boxed_arg_type: + if arg_type and not arg_type.is_implicit(): assert not doc.args section = self._make_section('Arguments') dlnode = nodes.definition_list() dlnode += self._make_dlitem( [nodes.Text('The members of '), - nodes.literal('', boxed_arg_type.name)], + nodes.literal('', arg_type.name)], None) section += dlnode return [section] @@ -249,8 +243,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)], dedent(section.text)) seen_item = True if not seen_item: @@ -268,11 +262,23 @@ 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'): - snode += self._nodes_for_example(section.text) + if section.tag and section.tag == 'TODO': + # Hide TODO: sections + continue + + if not section.tag: + # Sphinx cannot handle sectionless titles; + # Instead, just append the results to the prior section. + container = nodes.container() + self._parse_text_into_node(section.text, container) + nodelist += container.children + continue + + snode = self._make_section(section.tag) + if section.tag.startswith('Example'): + snode += self._nodes_for_example(dedent(section.text)) else: - self._parse_text_into_node(section.text, snode) + self._parse_text_into_node(dedent(section.text), snode) nodelist.append(snode) return nodelist @@ -315,17 +321,18 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): + self._nodes_for_if_section(ifcond)) def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + base, members, branches): doc = self._cur_doc if base and base.is_implicit(): base = None self._add_doc('Object', - self._nodes_for_members(doc, 'Members', base, variants) + self._nodes_for_members(doc, 'Members', base, branches) + self._nodes_for_features(doc) + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type(self, name, info, ifcond, features, + alternatives): doc = self._cur_doc self._add_doc('Alternate', self._nodes_for_members(doc, 'Members') @@ -338,8 +345,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): allow_preconfig, coroutine): doc = self._cur_doc self._add_doc('Command', - self._nodes_for_arguments(doc, - arg_type if boxed else None) + self._nodes_for_arguments(doc, arg_type) + self._nodes_for_features(doc) + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) @@ -347,8 +353,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): def visit_event(self, name, info, ifcond, features, arg_type, boxed): doc = self._cur_doc self._add_doc('Event', - self._nodes_for_arguments(doc, - arg_type if boxed else None) + self._nodes_for_arguments(doc, arg_type) + self._nodes_for_features(doc) + self._nodes_for_sections(doc) + self._nodes_for_if_section(ifcond)) @@ -383,6 +388,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): self._active_headings[level - 1] += snode self._active_headings = self._active_headings[:level] self._active_headings.append(snode) + return snode def _add_node_to_current_heading(self, node): """Add the node to whatever the current active heading is""" @@ -412,13 +418,11 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): # the first line of the block) (heading, _, text) = text.partition('\n') (leader, _, heading) = heading.partition(' ') - self._start_new_heading(heading, len(leader)) + node = self._start_new_heading(heading, len(leader)) if text == '': return - node = self._make_section(None) self._parse_text_into_node(text, node) - self._add_node_to_current_heading(node) self._cur_doc = None def _parse_text_into_node(self, doctext, node): @@ -457,6 +461,10 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): return self._top_node.children +# Turn the black formatter on for the rest of the file. +# fmt: on + + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module @@ -464,34 +472,52 @@ class QAPISchemaGenDepVisitor(QAPISchemaVisitor): that the generated documentation output depends on the input schema file associated with each module in the QAPI input. """ + def __init__(self, env, qapidir): self._env = env self._qapidir = qapidir def visit_module(self, name): if name != "./builtin": - qapifile = self._qapidir + '/' + name + qapifile = self._qapidir + "/" + name self._env.note_dependency(os.path.abspath(qapifile)) super().visit_module(name) -class QAPIDocDirective(Directive): +class NestedDirective(Directive): + def run(self): + raise NotImplementedError + + def do_parse(self, rstlist, node): + """ + Parse rST source lines and add them to the specified node + + Take the list of rST source lines rstlist, parse them as + rST, and add the resulting docutils nodes as children of node. + The nodes are parsed in a way that allows them to include + subheadings (titles) without confusing the rendering of + anything else. + """ + with switch_source_input(self.state, rstlist): + nested_parse_with_titles(self.state, rstlist, node) + + +class QAPIDocDirective(NestedDirective): """Extract documentation from the specified QAPI .json file""" + required_argument = 1 optional_arguments = 1 - option_spec = { - 'qapifile': directives.unchanged_required - } + option_spec = {"qapifile": directives.unchanged_required} has_content = False def new_serialno(self): """Return a unique new ID string suitable for use as a node's ID""" env = self.state.document.settings.env - return 'qapidoc-%d' % env.new_serialno('qapidoc') + return "qapidoc-%d" % env.new_serialno("qapidoc") def run(self): env = self.state.document.settings.env - qapifile = env.config.qapidoc_srctree + '/' + self.arguments[0] + qapifile = env.config.qapidoc_srctree + "/" + self.arguments[0] qapidir = os.path.dirname(qapifile) try: @@ -512,43 +538,112 @@ 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 - Take the list of rST source lines rstlist, parse them as - rST, and add the resulting docutils nodes as children of node. - The nodes are parsed in a way that allows them to include - subheadings (titles) without confusing the rendering of - anything else. - """ - # This is from kerneldoc.py -- it works around an API change in - # Sphinx between 1.6 and 1.7. Unlike kerneldoc.py, we use - # sphinx.util.nodes.nested_parse_with_titles() rather than the - # plain self.state.nested_parse(), and so we can drop the saving - # of title_styles and section_level that kerneldoc.py does, - # because nested_parse_with_titles() does that for us. - if Use_SSI: - with switch_source_input(self.state, rstlist): - nested_parse_with_titles(self.state, rstlist, node) +class QMPExample(CodeBlock, NestedDirective): + """ + Custom admonition for QMP code examples. + + When the :annotated: option is present, the body of this directive + is parsed as normal rST, but with any '::' code blocks set to use + the QMP lexer. Code blocks must be explicitly written by the user, + but this allows for intermingling explanatory paragraphs with + arbitrary rST syntax and code blocks for more involved examples. + + When :annotated: is absent, the directive body is treated as a + simple standalone QMP code block literal. + """ + + required_argument = 0 + optional_arguments = 0 + has_content = True + option_spec = { + "annotated": directives.flag, + "title": directives.unchanged, + } + + def _highlightlang(self) -> addnodes.highlightlang: + """Return the current highlightlang setting for the document""" + node = None + doc = self.state.document + + if hasattr(doc, "findall"): + # docutils >= 0.18.1 + for node in doc.findall(addnodes.highlightlang): + pass else: - save = self.state.memo.reporter - self.state.memo.reporter = AutodocReporter( - rstlist, self.state.memo.reporter) - try: - nested_parse_with_titles(self.state, rstlist, node) - finally: - self.state.memo.reporter = save + for elem in doc.traverse(): + if isinstance(elem, addnodes.highlightlang): + node = elem + + if node: + return node + + # No explicit directive found, use defaults + node = addnodes.highlightlang( + lang=self.env.config.highlight_language, + force=False, + # Yes, Sphinx uses this value to effectively disable line + # numbers and not 0 or None or -1 or something. ¯\_(ツ)_/¯ + linenothreshold=sys.maxsize, + ) + return node + + def admonition_wrap(self, *content) -> List[nodes.Node]: + title = "Example:" + if "title" in self.options: + title = f"{title} {self.options['title']}" + + admon = nodes.admonition( + "", + nodes.title("", title), + *content, + classes=["admonition", "admonition-example"], + ) + return [admon] + + def run_annotated(self) -> List[nodes.Node]: + lang_node = self._highlightlang() + + content_node: nodes.Element = nodes.section() + + # Configure QMP highlighting for "::" blocks, if needed + if lang_node["lang"] != "QMP": + content_node += addnodes.highlightlang( + lang="QMP", + force=False, # "True" ignores lexing errors + linenothreshold=lang_node["linenothreshold"], + ) + + self.do_parse(self.content, content_node) + + # Restore prior language highlighting, if needed + if lang_node["lang"] != "QMP": + content_node += addnodes.highlightlang(**lang_node.attributes) + + return content_node.children + + def run(self) -> List[nodes.Node]: + annotated = "annotated" in self.options + + if annotated: + content_nodes = self.run_annotated() + else: + self.arguments = ["QMP"] + content_nodes = super().run() + + return self.admonition_wrap(*content_nodes) def setup(app): - """ Register qapi-doc directive with Sphinx""" - app.add_config_value('qapidoc_srctree', None, 'env') - app.add_directive('qapi-doc', QAPIDocDirective) + """Register qapi-doc directive with Sphinx""" + app.add_config_value("qapidoc_srctree", None, "env") + app.add_directive("qapi-doc", QAPIDocDirective) + app.add_directive("qmp-example", QMPExample) - return dict( - version=__version__, - parallel_read_safe=True, - parallel_write_safe=True - ) + return { + "version": __version__, + "parallel_read_safe": True, + "parallel_write_safe": True, + } 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 6c5b05128e..d17fe7a4fc 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,11 +1,12 @@ -Aspeed family boards (``*-bmc``, ``ast2500-evb``, ``ast2600-evb``) -================================================================== +Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``tacoma-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +================================================================================================================================================================================================================================================================================================================================================================================================================================== The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the Aspeed SoC : the AST2400 integrating an ARM926EJ-S CPU (400MHz), the -AST2500 with an ARM1176JZS CPU (800MHz) and more recently the AST2600 -with dual cores ARM Cortex-A7 CPUs (1.2GHz). +AST2500 with an ARM1176JZS CPU (800MHz), the AST2600 +with dual cores ARM Cortex-A7 CPUs (1.2GHz) and more recently the AST2700 +with quad cores ARM Cortex-A35 64 bits CPUs (1.6GHz) The SoC comes with RAM, Gigabit ethernet, USB, SD/MMC, USB, SPI, I2C, etc. @@ -14,7 +15,8 @@ AST2400 SoC based machines : - ``palmetto-bmc`` OpenPOWER Palmetto POWER8 BMC - ``quanta-q71l-bmc`` OpenBMC Quanta BMC -- ``supermicrox11-bmc`` Supermicro X11 BMC +- ``supermicrox11-bmc`` Supermicro X11 BMC (ARM926EJ-S) +- ``supermicrox11spi-bmc`` Supermicro X11 SPI BMC (ARM1176) AST2500 SoC based machines : @@ -24,6 +26,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 : @@ -36,6 +40,10 @@ AST2600 SoC based machines : - ``qcom-dc-scm-v1-bmc`` Qualcomm DC-SCM V1 BMC - ``qcom-firework-bmc`` Qualcomm Firework BMC +AST2700 SoC based machines : + +- ``ast2700-evb`` Aspeed AST2700 Evaluation board (Cortex-A35) + Supported devices ----------------- @@ -64,6 +72,7 @@ Supported devices * eMMC Boot Controller (dummy) * PECI Controller (minimal) * I3C Controller + * Internal Bridge Controller (SLI dummy) Missing devices @@ -93,6 +102,13 @@ or directly from the OpenBMC GitHub release repository : https://github.com/openbmc/openbmc/releases +or directly from the ASPEED Forked OpenBMC GitHub release repository : + + https://github.com/AspeedTech-BMC/openbmc/releases + +Booting from a kernel image +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + To boot a kernel directly from a Linux build tree: .. code-block:: bash @@ -102,31 +118,158 @@ 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 : +Booting from a flash image +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The machine options specific to Aspeed to boot from a flash image are : + + * ``execute-in-place`` which emulates the boot from the CE0 flash + 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 default FMC Flash model. FW needs + support for the chip model to boot. + + * ``spi-model`` to change the default SPI Flash model. + +To boot the machine from the flash image, use an MTD drive : .. code-block:: bash $ qemu-system-arm -M romulus-bmc -nic user \ -drive file=obmc-phosphor-image-romulus.static.mtd,format=raw,if=mtd -nographic -Options specific to Aspeed machines are : - - * ``execute-in-place`` which emulates the boot from the CE0 flash - 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. - - * ``spi-model`` to change the SPI Flash model. - -For instance, to start the ``ast2500-evb`` machine with a different -FMC chip and a bigger (64M) SPI chip, use : +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. + +Booting from an eMMC image +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The machine options specific to Aspeed machines to boot from an eMMC +image are : + + * ``boot-emmc`` to set or unset boot from eMMC (AST2600). + +Only the ``ast2600-evb`` and ``rainier-emmc`` machines have support to +boot from an eMMC device. In this case, the machine assumes that the +eMMC image includes special boot partitions. Such an image can be +built this way : + +.. code-block:: bash + + $ dd if=/dev/zero of=mmc-bootarea.img count=2 bs=1M + $ dd if=u-boot-spl.bin of=mmc-bootarea.img conv=notrunc + $ dd if=u-boot.bin of=mmc-bootarea.img conv=notrunc count=64 bs=1K + $ cat mmc-bootarea.img obmc-phosphor-image.wic > mmc.img + $ truncate --size 16GB mmc.img + +Boot the machine ``rainier-emmc`` with : + +.. code-block:: bash + + $ qemu-system-arm -M rainier-bmc \ + -drive file=mmc.img,format=raw,if=sd,index=2 \ + -nographic + +The ``boot-emmc`` option can be set or unset, to change the default +boot mode of machine: SPI or eMMC. This can be useful to boot the +``ast2600-evb`` machine from an eMMC device (default being SPI) or to +boot the ``rainier-bmc`` machine from a flash device (default being +eMMC). + +As an example, here is how to to boot the ``rainier-bmc`` machine from +the flash device with ``boot-emmc=false`` and let the machine use an +eMMC image : + +.. code-block:: bash + + $ qemu-system-arm -M rainier-bmc,boot-emmc=false \ + -drive file=flash.img,format=raw,if=mtd \ + -drive file=mmc.img,format=raw,if=sd,index=2 \ + -nographic + +It should be noted that in this case the eMMC device must not have +boot partitions, otherwise the contents will not be accessible to the +machine. This limitation is due to the use of the ``-drive`` +interface. + +Ideally, one should be able to define the eMMC device and the +associated backend directly on the command line, such as : + +.. code-block:: bash + + -blockdev node-name=emmc0,driver=file,filename=mmc.img \ + -device emmc,bus=sdhci-bus.2,drive=emmc0,boot-partition-size=1048576,boot-config=8 + +This is not yet supported (as of QEMU-10.0). Work is needed to +refactor the sdhci bus model. + +Other booting options +^^^^^^^^^^^^^^^^^^^^^ + +Other machine options specific to Aspeed machines are : + + * ``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 change the boot console and use device ``UART3`` (``/dev/ttyS2`` +under Linux), use : + +.. code-block:: bash + + -M ast2500-evb,bmc-console=uart3 + +Booting the ast2700-evb machine +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Boot the AST2700 machine from the flash image, use an MTD drive : + +.. code-block:: bash + + IMGDIR=ast2700-default + UBOOT_SIZE=$(stat --format=%s -L ${IMGDIR}/u-boot-nodtb.bin) + + $ qemu-system-aarch64 -M ast2700-evb \ + -device loader,force-raw=on,addr=0x400000000,file=${IMGDIR}/u-boot-nodtb.bin \ + -device loader,force-raw=on,addr=$((0x400000000 + ${UBOOT_SIZE})),file=${IMGDIR}/u-boot.dtb \ + -device loader,force-raw=on,addr=0x430000000,file=${IMGDIR}/bl31.bin \ + -device loader,force-raw=on,addr=0x430080000,file=${IMGDIR}/optee/tee-raw.bin \ + -device loader,cpu-num=0,addr=0x430000000 \ + -device loader,cpu-num=1,addr=0x430000000 \ + -device loader,cpu-num=2,addr=0x430000000 \ + -device loader,cpu-num=3,addr=0x430000000 \ + -smp 4 \ + -drive file=${IMGDIR}/image-bmc,format=raw,if=mtd \ + -nographic Aspeed minibmc family boards (``ast1030-evb``) ================================================================== @@ -188,51 +331,3 @@ 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..2adcc4b4c1 --- /dev/null +++ b/docs/system/arm/b-l475e-iot01a.rst @@ -0,0 +1,46 @@ +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 machines support 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) +- STM32L4x5 USARTs, UARTs and LPUART (Serial ports) +- optional 8x8 led display (based on DM163 driver) + +Missing devices +""""""""""""""" + +The B-L475E-IOT01A does *not* support the following devices: + +- 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 00c444042f..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 ================== @@ -443,3 +440,26 @@ 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..90d24c73a1 100644 --- a/docs/system/arm/cubieboard.rst +++ b/docs/system/arm/cubieboard.rst @@ -14,3 +14,6 @@ Emulated devices: - SDHCI - USB controller - SATA controller +- TWI (I2C) controller +- SPI controller +- Watchdog timer diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index e3af79bb8c..38534dcdd3 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -1,41 +1,81 @@ +.. _Arm Emulation: + A-profile CPU architecture support ================================== -QEMU's TCG emulation includes support for the Armv5, Armv6, Armv7 and -Armv8 versions of the A-profile architecture. It also has support for +QEMU's TCG emulation includes support for the Armv5, Armv6, Armv7, +Armv8 and Armv9 versions of the A-profile architecture. It also has support for the following architecture extensions: - FEAT_AA32BF16 (AArch32 BFloat16 instructions) +- FEAT_AA32EL0 (Support for AArch32 at EL0) +- FEAT_AA32EL1 (Support for AArch32 at EL1) +- FEAT_AA32EL2 (Support for AArch32 at EL2) +- FEAT_AA32EL3 (Support for AArch32 at EL3) - FEAT_AA32HPD (AArch32 hierarchical permission disables) - FEAT_AA32I8MM (AArch32 Int8 matrix multiplication instructions) +- FEAT_AA64EL0 (Support for AArch64 at EL0) +- FEAT_AA64EL1 (Support for AArch64 at EL1) +- FEAT_AA64EL2 (Support for AArch64 at EL2) +- FEAT_AA64EL3 (Support for AArch64 at EL3) +- FEAT_AdvSIMD (Advanced SIMD Extension) - FEAT_AES (AESD and AESE instructions) +- FEAT_Armv9_Crypto (Armv9 Cryptographic Extension) +- FEAT_ASID16 (16 bit ASID) - FEAT_BBM at level 2 (Translation table break-before-make levels) - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) +- FEAT_CCIDX (Extended cache index) +- FEAT_CMOW (Control for cache maintenance permission) +- FEAT_CRC32 (CRC32 instructions) +- FEAT_Crypto (Cryptographic Extension) - 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) - FEAT_CSV2_2 (Cache speculation variant 2, version 2) +- FEAT_CSV2_3 (Cache speculation variant 2, version 3) - FEAT_CSV3 (Cache speculation variant 3) - FEAT_DGH (Data gathering hint) - FEAT_DIT (Data Independent Timing instructions) +- FEAT_DoubleLock (Double Lock) - FEAT_DPB (DC CVAP instruction) +- FEAT_DPB2 (DC CVADP instruction) +- FEAT_Debugv8p1 (Debug with VHE) - FEAT_Debugv8p2 (Debug changes for v8.2) - FEAT_Debugv8p4 (Debug changes for v8.4) +- FEAT_Debugv8p8 (Debug changes for v8.8) - FEAT_DotProd (Advanced SIMD dot product instructions) - FEAT_DoubleFault (Double Fault Extension) - FEAT_E0PD (Preventing EL0 access to halves of address maps) -- FEAT_ETS (Enhanced Translation Synchronization) +- FEAT_EBF16 (AArch64 Extended BFloat16 instructions) +- FEAT_ECV (Enhanced Counter Virtualization) +- FEAT_EL0 (Support for execution at EL0) +- FEAT_EL1 (Support for execution at EL1) +- FEAT_EL2 (Support for execution at EL2) +- FEAT_EL3 (Support for execution at EL3) +- FEAT_EPAC (Enhanced pointer authentication) +- FEAT_ETS2 (Enhanced Translation Synchronization) +- FEAT_EVT (Enhanced Virtualization Traps) +- FEAT_F32MM (Single-precision Matrix Multiplication) +- FEAT_F64MM (Double-precision Matrix Multiplication) - FEAT_FCMA (Floating-point complex number instructions) +- FEAT_FGT (Fine-Grained Traps) - FEAT_FHM (Floating-point half-precision multiplication instructions) +- FEAT_FP (Floating Point extensions) - FEAT_FP16 (Half-precision floating-point data processing) +- FEAT_FPAC (Faulting on AUT* instructions) +- FEAT_FPACCOMBINE (Faulting on combined pointer authentication instructions) +- FEAT_FPACC_SPEC (Speculative behavior of 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) @@ -46,20 +86,36 @@ 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_MixedEnd (Mixed-endian support) +- FEAT_MixedEndEL0 (Mixed-endian support at EL0) +- FEAT_MOPS (Standardization of memory operations) - FEAT_MTE (Memory Tagging Extension) - FEAT_MTE2 (Memory Tagging Extension) - FEAT_MTE3 (MTE Asymmetric Fault Handling) +- FEAT_MTE_ASYM_FAULT (Memory tagging asymmetric faults) +- FEAT_MTE_ASYNC (Asynchronous reporting of Tag Check Fault) +- FEAT_NMI (Non-maskable Interrupt) +- 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_PMUv3 (PMU extension version 3) - 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) @@ -74,8 +130,20 @@ the following architecture extensions: - 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_SVE (Scalable Vector Extension) +- FEAT_SVE_AES (Scalable Vector AES instructions) +- FEAT_SVE_BitPerm (Scalable Vector Bit Permutes instructions) +- FEAT_SVE_PMULL128 (Scalable Vector PMULL instructions) +- FEAT_SVE_SHA3 (Scalable Vector SHA3 instructions) +- FEAT_SVE_SM4 (Scalable Vector SM4 instructions) +- FEAT_SVE2 (Scalable Vector Extension version 2) - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) +- FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2) +- FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1) +- FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1) +- FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1) +- 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) @@ -84,12 +152,11 @@ the following architecture extensions: - FEAT_UAO (Unprivileged Access Override control) - FEAT_VHE (Virtualization Host Extensions) - FEAT_VMID16 (16-bit VMID) +- FEAT_WFxT (WFE and WFI instructions with timeout) - FEAT_XNX (Translation table stage 2 Unprivileged Execute-never) -- SVE (The Scalable Vector Extension) -- SVE2 (The Scalable Vector Extension v2) For information on the specifics of these extensions, please refer -to the `Armv8-A Arm Architecture Reference Manual +to the `Arm Architecture Reference Manual for A-profile architecture `_. When a specific named CPU is being emulated, only those features which diff --git a/docs/system/arm/exynos.rst b/docs/system/arm/exynos.rst new file mode 100644 index 0000000000..86894bc02b --- /dev/null +++ b/docs/system/arm/exynos.rst @@ -0,0 +1,9 @@ +Exynos4 boards (``nuri``, ``smdkc210``) +======================================= + +These are machines which use the Samsung Exynos4210 SoC, which has Cortex-A9 CPUs. + +``nuri`` models the Samsung NURI board. + +``smdkc210`` models the Samsung SMDKC210 board. + diff --git a/docs/system/arm/fby35.rst b/docs/system/arm/fby35.rst new file mode 100644 index 0000000000..bf6da6baa2 --- /dev/null +++ b/docs/system/arm/fby35.rst @@ -0,0 +1,47 @@ +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/gumstix.rst b/docs/system/arm/gumstix.rst deleted file mode 100644 index cb373139dc..0000000000 --- a/docs/system/arm/gumstix.rst +++ /dev/null @@ -1,21 +0,0 @@ -Gumstix Connex and Verdex (``connex``, ``verdex``) -================================================== - -These machines model the Gumstix Connex and Verdex boards. -The Connex has a PXA255 CPU and the Verdex has a PXA270. - -Implemented devices: - - * NOR flash - * SMC91C111 ethernet - * Interrupt controller - * DMA - * Timer - * GPIO - * MMC/SD card - * Fast infra-red communications port (FIR) - * LCD controller - * Synchronous serial ports (SPI) - * PCMCIA interface - * I2C - * I2S diff --git a/docs/system/arm/mainstone.rst b/docs/system/arm/mainstone.rst deleted file mode 100644 index 05310f42c7..0000000000 --- a/docs/system/arm/mainstone.rst +++ /dev/null @@ -1,25 +0,0 @@ -Intel Mainstone II board (``mainstone``) -======================================== - -The ``mainstone`` board emulates the Intel Mainstone II development -board, which uses a PXA270 CPU. - -Emulated devices: - -- Flash memory -- Keypad -- MMC controller -- 91C111 ethernet -- PIC -- Timer -- DMA -- GPIO -- FIR -- Serial -- LCD controller -- SSP -- USB controller -- RTC -- PCMCIA -- I2C -- I2S diff --git a/docs/system/arm/mcimx6ul-evk.rst b/docs/system/arm/mcimx6ul-evk.rst new file mode 100644 index 0000000000..8871138ab3 --- /dev/null +++ b/docs/system/arm/mcimx6ul-evk.rst @@ -0,0 +1,5 @@ +NXP MCIMX6UL-EVK (``mcimx6ul-evk``) +=================================== + +The ``mcimx6ul-evk`` machine models the NXP i.MX6UltraLite Evaluation Kit +MCIMX6UL-EVK development board. It has a single Cortex-A7 CPU. diff --git a/docs/system/arm/mcimx7d-sabre.rst b/docs/system/arm/mcimx7d-sabre.rst new file mode 100644 index 0000000000..c5d35af1d4 --- /dev/null +++ b/docs/system/arm/mcimx7d-sabre.rst @@ -0,0 +1,5 @@ +NXP MCIMX7D Sabre (``mcimx7d-sabre``) +===================================== + +The ``mcimx7d-sabre`` machine models the NXP SABRE Board MCIMX7SABRE, +based an an i.MX7Dual SoC. 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/nseries.rst b/docs/system/arm/nseries.rst deleted file mode 100644 index cd9edf5d88..0000000000 --- a/docs/system/arm/nseries.rst +++ /dev/null @@ -1,33 +0,0 @@ -Nokia N800 and N810 tablets (``n800``, ``n810``) -================================================ - -Nokia N800 and N810 internet tablets (known also as RX-34 and RX-44 / -48) emulation supports the following elements: - -- Texas Instruments OMAP2420 System-on-chip (ARM1136 core) - -- RAM and non-volatile OneNAND Flash memories - -- Display connected to EPSON remote framebuffer chip and OMAP on-chip - display controller and a LS041y3 MIPI DBI-C controller - -- TI TSC2301 (in N800) and TI TSC2005 (in N810) touchscreen - controllers driven through SPI bus - -- National Semiconductor LM8323-controlled qwerty keyboard driven - through |I2C| bus - -- Secure Digital card connected to OMAP MMC/SD host - -- Three OMAP on-chip UARTs and on-chip STI debugging console - -- Mentor Graphics \"Inventra\" dual-role USB controller embedded in a - TI TUSB6010 chip - only USB host mode is supported - -- TI TMP105 temperature sensor driven through |I2C| bus - -- TI TWL92230C power management companion with an RTC on - |I2C| bus - -- Nokia RETU and TAHVO multi-purpose chips with an RTC, connected - through CBUS diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index c38df32bde..05059378e5 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -1,5 +1,5 @@ -Nuvoton iBMC boards (``*-bmc``, ``npcm750-evb``, ``quanta-gsj``) -================================================================ +Nuvoton iBMC boards (``kudo-bmc``, ``mori-bmc``, ``npcm750-evb``, ``quanta-gbs-bmc``, ``quanta-gsj``) +===================================================================================================== The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are designed to be used as Baseboard Management Controllers (BMCs) in various @@ -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 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 deleted file mode 100644 index 47ff9b36d4..0000000000 --- a/docs/system/arm/palm.rst +++ /dev/null @@ -1,23 +0,0 @@ -Palm Tungsten|E PDA (``cheetah``) -================================= - -The Palm Tungsten|E PDA (codename \"Cheetah\") emulation includes the -following elements: - -- Texas Instruments OMAP310 System-on-chip (ARM925T core) - -- ROM and RAM memories (ROM firmware image can be loaded with - -option-rom) - -- On-chip LCD controller - -- On-chip Real Time Clock - -- TI TSC2102i touchscreen controller / analog-digital converter / - Audio CODEC, connected through MicroWire and |I2S| busses - -- GPIO-connected matrix keypad - -- Secure Digital card connected to OMAP MMC/SD host - -- Three on-chip UARTs diff --git a/docs/system/arm/raspi.rst b/docs/system/arm/raspi.rst index 922fe375a6..44eec3f1c3 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,12 @@ 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..2bf3fc8d59 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,77 @@ 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 + - CPU topology information + +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. + +0.4 + CPU topology information is present in devicetree. diff --git a/docs/system/arm/stm32.rst b/docs/system/arm/stm32.rst index 508b92cf86..511e3eb9ac 100644 --- a/docs/system/arm/stm32.rst +++ b/docs/system/arm/stm32.rst @@ -1,5 +1,5 @@ -STMicroelectronics STM32 boards (``netduino2``, ``netduinoplus2``, ``stm32vldiscovery``) -======================================================================================== +STMicroelectronics STM32 boards (``netduino2``, ``netduinoplus2``, ``olimex-stm32-h405``, ``stm32vldiscovery``) +=============================================================================================================== The `STM32`_ chips are a family of 32-bit ARM-based microcontroller by STMicroelectronics. @@ -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. @@ -33,6 +36,7 @@ Supported devices * SPI controller * System configuration (SYSCFG) * Timer controller (TIMER) + * Reset and Clock Controller (RCC) (STM32F4 only, reset and enable only) Missing devices --------------- @@ -50,7 +54,7 @@ Missing devices * Power supply configuration (PWR) * Random Number Generator (RNG) * Real-Time Clock (RTC) controller - * Reset and Clock Controller (RCC) + * Reset and Clock Controller (RCC) (other features than reset and enable) * Secure Digital Input/Output (SDIO) interface * USB OTG * Watchdog controller (IWDG, WWDG) 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 20442ea2c1..e67e7f0f7c 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -26,7 +26,7 @@ The virt board supports: - PCI/PCIe devices - Flash memory -- One PL011 UART +- Either one or two PL011 UARTs for the NonSecure World - An RTC - The fw_cfg device that allows a guest to obtain data from QEMU - A PL061 GPIO controller @@ -48,23 +48,44 @@ The virt board supports: - A secure flash memory - 16MB of secure RAM +The second NonSecure UART only exists if a backend is configured +explicitly (e.g. with a second -serial command line option) and +TrustZone emulation is not enabled. + 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 @@ -92,7 +113,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. 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/xlnx-zcu102.rst b/docs/system/arm/xlnx-zcu102.rst new file mode 100644 index 0000000000..534cd1dc88 --- /dev/null +++ b/docs/system/arm/xlnx-zcu102.rst @@ -0,0 +1,19 @@ +Xilinx ZynqMP ZCU102 (``xlnx-zcu102``) +====================================== + +The ``xlnx-zcu102`` board models the Xilinx ZynqMP ZCU102 board. +This board has 4 Cortex-A53 CPUs and 2 Cortex-R5F CPUs. + +Machine-specific options +"""""""""""""""""""""""" + +The following machine-specific options are supported: + +secure + Set ``on``/``off`` to enable/disable emulating a guest CPU which implements the + Arm Security Extensions (TrustZone). The default is ``off``. + +virtualization + Set ``on``/``off`` to enable/disable emulating a guest CPU which implements the + Arm Virtualization Extensions. The default is ``off``. + diff --git a/docs/system/arm/xlnx-zynq.rst b/docs/system/arm/xlnx-zynq.rst new file mode 100644 index 0000000000..ade18a3fe1 --- /dev/null +++ b/docs/system/arm/xlnx-zynq.rst @@ -0,0 +1,47 @@ +Xilinx Zynq board (``xilinx-zynq-a9``) +====================================== +The Zynq 7000 family is based on the AMD SoC architecture. These products +integrate a feature-rich dual or single-core Arm Cortex-A9 MPCore based +processing system (PS) and AMD programmable logic (PL) in a single device. + +More details here: +https://docs.amd.com/r/en-US/ug585-zynq-7000-SoC-TRM/Zynq-7000-SoC-Technical-Reference-Manual + +QEMU xilinx-zynq-a9 board supports following devices: + - A9 MPCORE + - cortex-a9 + - GIC v1 + - Generic timer + - wdt + - OCM 256KB + - SMC SRAM@0xe2000000 64MB + - Zynq SLCR + - SPI x2 + - QSPI + - UART + - TTC x2 + - Gigabit Ethernet Controller x2 + - SD Controller x2 + - XADC + - Arm PrimeCell DMA Controller + - DDR Memory + - USB 2.0 x2 + +Running +""""""" +Direct Linux boot of a generic ARM upstream Linux kernel: + +.. code-block:: bash + + $ qemu-system-aarch64 -M xilinx-zynq-a9 \ + -dtb zynq-zc702.dtb -serial null -serial mon:stdio \ + -display none -m 1024 \ + -initrd rootfs.cpio.gz -kernel zImage + +For configuring the boot-mode provide the following on the command line: + +.. code-block:: bash + + -machine boot-mode=qspi + +Supported values are jtag, sd, qspi, nor. diff --git a/docs/system/arm/xscale.rst b/docs/system/arm/xscale.rst deleted file mode 100644 index d2d5949e10..0000000000 --- a/docs/system/arm/xscale.rst +++ /dev/null @@ -1,35 +0,0 @@ -Sharp XScale-based PDA models (``akita``, ``borzoi``, ``spitz``, ``terrier``, ``tosa``) -======================================================================================= - -The Sharp Zaurus are PDAs based on XScale, able to run Linux ('SL series'). - -The SL-6000 (\"Tosa\"), released in 2005, uses a PXA255 System-on-chip. - -The SL-C3000 (\"Spitz\"), SL-C1000 (\"Akita\"), SL-C3100 (\"Borzoi\") and -SL-C3200 (\"Terrier\") use a PXA270. - -The clamshell PDA models emulation includes the following peripherals: - -- Intel PXA255/PXA270 System-on-chip (ARMv5TE core) - -- NAND Flash memory - not in \"Tosa\" - -- IBM/Hitachi DSCM microdrive in a PXA PCMCIA slot - not in \"Akita\" - -- On-chip OHCI USB controller - not in \"Tosa\" - -- On-chip LCD controller - -- On-chip Real Time Clock - -- TI ADS7846 touchscreen controller on SSP bus - -- Maxim MAX1111 analog-digital converter on |I2C| bus - -- GPIO-connected keyboard controller and LEDs - -- Secure Digital card connected to PXA MMC/SD host - -- Three on-chip UARTs - -- WM8750 audio CODEC on |I2C| and |I2S| busses diff --git a/docs/system/bootindex.rst b/docs/system/bootindex.rst index 8b057f812f..5e1b33ee22 100644 --- a/docs/system/bootindex.rst +++ b/docs/system/bootindex.rst @@ -49,10 +49,11 @@ Limitations ----------- Some firmware has limitations on which devices can be considered for -booting. For instance, the PC BIOS boot specification allows only one -disk to be bootable. If boot from disk fails for some reason, the BIOS +booting. For instance, the x86 PC BIOS boot specification allows only one +disk to be bootable. If boot from disk fails for some reason, the x86 BIOS won't retry booting from other disk. It can still try to boot from -floppy or net, though. +floppy or net, though. In the case of s390x BIOS, the BIOS will try up to +8 total devices, any number of which may be disks or virtio-net devices. Sometimes, firmware cannot map the device path QEMU wants firmware to boot from to a boot method. It doesn't happen for devices the firmware diff --git a/docs/system/cpu-hotplug.rst b/docs/system/cpu-hotplug.rst index 015ce2b6ec..cc50937c36 100644 --- a/docs/system/cpu-hotplug.rst +++ b/docs/system/cpu-hotplug.rst @@ -33,23 +33,23 @@ vCPU hotplug { "return": [ { - "type": "IvyBridge-IBRS-x86_64-cpu", - "vcpus-count": 1, "props": { - "socket-id": 1, - "core-id": 0, + "core-id": 1, + "socket-id": 0, "thread-id": 0 - } + }, + "type": "IvyBridge-IBRS-x86_64-cpu", + "vcpus-count": 1 }, { + "props": { + "core-id": 0, + "socket-id": 0, + "thread-id": 0 + }, "qom-path": "/machine/unattached/device[0]", "type": "IvyBridge-IBRS-x86_64-cpu", - "vcpus-count": 1, - "props": { - "socket-id": 0, - "core-id": 0, - "thread-id": 0 - } + "vcpus-count": 1 } ] } @@ -58,18 +58,18 @@ vCPU hotplug (4) The ``query-hotpluggable-cpus`` command returns an object for CPUs that are present (containing a "qom-path" member) or which may be hot-plugged (no "qom-path" member). From its output in step (3), we - can see that ``IvyBridge-IBRS-x86_64-cpu`` is present in socket 0, - while hot-plugging a CPU into socket 1 requires passing the listed + can see that ``IvyBridge-IBRS-x86_64-cpu`` is present in socket 0 core 0, + while hot-plugging a CPU into socket 0 core 1 requires passing the listed properties to QMP ``device_add``:: - (QEMU) device_add id=cpu-2 driver=IvyBridge-IBRS-x86_64-cpu socket-id=1 core-id=0 thread-id=0 + (QEMU) device_add id=cpu-2 driver=IvyBridge-IBRS-x86_64-cpu socket-id=0 core-id=1 thread-id=0 { "execute": "device_add", "arguments": { - "socket-id": 1, + "core-id": 1, "driver": "IvyBridge-IBRS-x86_64-cpu", "id": "cpu-2", - "core-id": 0, + "socket-id": 0, "thread-id": 0 } } @@ -83,34 +83,32 @@ vCPU hotplug (QEMU) query-cpus-fast { - "execute": "query-cpus-fast", "arguments": {} + "execute": "query-cpus-fast", } { "return": [ { - "qom-path": "/machine/unattached/device[0]", - "target": "x86_64", - "thread-id": 11534, "cpu-index": 0, "props": { - "socket-id": 0, "core-id": 0, + "socket-id": 0, "thread-id": 0 }, - "arch": "x86" + "qom-path": "/machine/unattached/device[0]", + "target": "x86_64", + "thread-id": 28957 }, { - "qom-path": "/machine/peripheral/cpu-2", - "target": "x86_64", - "thread-id": 12106, "cpu-index": 1, "props": { - "socket-id": 1, - "core-id": 0, + "core-id": 1, + "socket-id": 0, "thread-id": 0 }, - "arch": "x86" + "qom-path": "/machine/peripheral/cpu-2", + "target": "x86_64", + "thread-id": 29095 } ] } @@ -123,10 +121,10 @@ From the 'qmp-shell', invoke the QMP ``device_del`` command:: (QEMU) device_del id=cpu-2 { - "execute": "device_del", "arguments": { "id": "cpu-2" } + "execute": "device_del", } { "return": {} 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 0506006056..f19777411c 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -86,10 +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/device-url-syntax.rst.inc b/docs/system/device-url-syntax.rst.inc index 7dbc525fa8..43b5c2596b 100644 --- a/docs/system/device-url-syntax.rst.inc +++ b/docs/system/device-url-syntax.rst.inc @@ -87,8 +87,8 @@ These are specified using a special URL syntax. ``GlusterFS`` GlusterFS is a user space distributed file system. QEMU supports the - use of GlusterFS volumes for hosting VM disk images using TCP, Unix - Domain Sockets and RDMA transport protocols. + use of GlusterFS volumes for hosting VM disk images using TCP and Unix + Domain Sockets transport protocols. Syntax for specifying a VM disk image on GlusterFS volume is diff --git a/docs/system/devices/can.rst b/docs/system/devices/can.rst index 0af3d9912a..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 diff --git a/docs/system/devices/canokey.rst b/docs/system/devices/canokey.rst index cfa6186e48..7f3664963f 100644 --- a/docs/system/devices/canokey.rst +++ b/docs/system/devices/canokey.rst @@ -14,7 +14,7 @@ CanoKey [1]_ is an open-source secure key with supports of All these platform-independent features are in canokey-core [3]_. For different platforms, CanoKey has different implementations, -including both hardware implementions and virtual cards: +including both hardware implementations and virtual cards: * CanoKey STM32 [4]_ * CanoKey Pigeon [5]_ diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index f25783a4ec..882b036f5e 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -111,7 +111,7 @@ 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. @@ -157,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____________________________| |__________| | @@ -187,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 | |_______________| |_____________| |____________| |_____________| | | | | | | | | @@ -208,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 access 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 @@ -218,17 +218,17 @@ Notes: A complex configuration here, might be to use the following HDM decoders in HB0. HDM0 routes CFMW0 requests to RP0 and hence part of CXL Type3 0. HDM1 routes CFMW0 requests from a - different region of the CFMW0 PA range to RP2 and hence part + different region of the CFMW0 PA range to RP1 and hence part of CXL Type 3 1. HDM2 routes yet another PA range from within CFMW0 to be interleaved across RP0 and RP1, providing 2 way interleave of part of the memory provided by CXL Type3 0 and CXL Type 3 1. HDM3 routes those interleaved accesses from CFMW1 that target HB0 to RP 0 and another part of the memory of CXL Type 3 0 (as part of a 2 way interleave at the system level - across for example CXL Type3 0 and CXL Type3 2. + across for example CXL Type3 0 and CXL Type3 2). HDM4 is used to enable system wide 4 way interleave across all the present CXL type3 devices, by interleaving those (interleaved) - requests that HB0 receives from from CFMW1 across RP 0 and + requests that HB0 receives from CFMW1 across RP 0 and RP 1 and hence to yet more regions of the memory of the attached Type3 devices. Note this is a representative subset of the full range of possible HDM decoder configurations in this @@ -247,7 +247,7 @@ Example topology involving a switch:: |<------------------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_____| |__________________________________| |__________| | @@ -272,7 +272,7 @@ Example topology involving a switch:: | Root Port 0 | | Appears in | | PCI topology | - | As 0c:00.0 | + | as 0c:00.0 | |___________x___| | | @@ -300,22 +300,43 @@ Example topology involving a switch:: 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 \ + -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 \ @@ -328,18 +349,18 @@ 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 \ + -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-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-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 \ @@ -354,15 +375,23 @@ An example of 4 devices below a switch suitable for 1, 2 or 4 way interleave:: -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,memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0,size=256M \ + -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,memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1,size=256M \ + -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,memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2,size=256M \ + -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,memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3,size=256M \ + -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 ---------------------------- @@ -382,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 30f841ef62..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. @@ -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,9 +278,15 @@ 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) ------------------------------------------------------------- @@ -320,4 +368,4 @@ controller are: .. code-block:: console - echo 0000:01:00.1 > /sys/bus/pci/drivers/nvme/bind \ No newline at end of file + 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 37cb9b33ae..dc694d23c2 100644 --- a/docs/system/devices/usb.rst +++ b/docs/system/devices/usb.rst @@ -1,5 +1,3 @@ -.. _pcsys_005fusb: - USB emulation ------------- @@ -20,7 +18,7 @@ emulation uses less resources (especially CPU). So if your guest supports XHCI (which should be the case for any operating system released around 2010 or later) we recommend using it: - qemu -device qemu-xhci + |qemu_system| -device qemu-xhci XHCI supports USB 1.1, USB 2.0 and USB 3.0 devices, so this is the only controller you need. With only a single USB controller (and @@ -209,7 +207,7 @@ 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. 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..35259d8ec7 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,13 +91,16 @@ 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 ==================== In order for the daemon to access the VirtIO queues to process the requests it needs access to the guest's address space. This is -achieved via the ``memory-backend-file`` or ``memory-backend-memfd`` -objects. A reference to a file-descriptor which can access this object +achieved via the ``memory-backend-file``, ``memory-backend-memfd``, or +``memory-backend-shm`` objects. +A reference to a file-descriptor which can access this object will be passed via the socket as part of the protocol negotiation. Currently the shared memory object needs to match the size of the main @@ -38,13 +109,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..b7eb0fc0e7 --- /dev/null +++ b/docs/system/devices/virtio-gpu.rst @@ -0,0 +1,123 @@ +.. + 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/ + +Translation of Vulkan API calls is supported since release of `virglrenderer`_ +v1.0.0 using `venus`_ protocol. ``Venus`` virtio-gpu capability set ("capset") +requires host blob support (``hostmem`` and ``blob`` fields) and should +be enabled using ``venus`` field. The ``hostmem`` field specifies the size +of virtio-gpu host memory window. This is typically between 256M and 8G. + +.. parsed-literal:: + -device virtio-gpu-gl,hostmem=8G,blob=true,venus=true + +.. _venus: https://gitlab.freedesktop.org/virgl/venus-protocol/ + +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..748f5094ba 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -25,8 +25,8 @@ support for notifying a guest's operating system when certain types of VMEXITs are about to occur. This allows the guest to selectively share information with the hypervisor to satisfy the requested function. -Launching ---------- +Launching (SEV and SEV-ES) +-------------------------- Boot images (such as bios) must be encrypted before a guest can be booted. The ``MEMORY_ENCRYPT_OP`` ioctl provides commands to encrypt the images: ``LAUNCH_START``, @@ -161,6 +161,72 @@ The value of GCTX.LD is If kernel hashes are not used, or SEV-ES is disabled, use empty blobs for ``kernel_hashes_blob`` and ``vmsas_blob`` as needed. +Launching (SEV-SNP) +------------------- +Boot images (such as bios) must be encrypted before a guest can be booted. The +``MEMORY_ENCRYPT_OP`` ioctl provides commands to encrypt the images: +``SNP_LAUNCH_START``, ``SNP_LAUNCH_UPDATE``, and ``SNP_LAUNCH_FINISH``. These +three commands communicate with SEV-SNP firmware to generate a fresh memory +encryption key for the VM, encrypt the boot images for a successful launch. For +more details on the SEV-SNP firmware interfaces used by these commands please +see the SEV-SNP Firmware ABI. + +``SNP_LAUNCH_START`` is called first to create a cryptographic launch context +within the firmware. To create this context, the guest owner must provide a +guest policy and other parameters as described in the SEV-SNP firmware +specification. The launch parameters should be specified as described in the +QAPI schema for the sev-snp-guest object. + +The ``SNP_LAUNCH_START`` uses the following parameters, which can be configured +by the corresponding parameters documented in the QAPI schema for the +'sev-snp-guest' object. + ++--------+-------+----------+-------------------------------------------------+ +| key | type | default | meaning | ++---------------------------+-------------------------------------------------+ +| policy | hex | 0x30000 | a 64-bit guest policy | ++---------------------------+-------------------------------------------------+ +| guest-visible-workarounds | string| 0 | 16-byte base64 encoded string| +| | | | for guest OS visible | +| | | | workarounds. | ++---------------------------+-------------------------------------------------+ + +``SNP_LAUNCH_UPDATE`` encrypts the memory region using the cryptographic context +created via the ``SNP_LAUNCH_START`` command. If required, this command can be +called multiple times to encrypt different memory regions. The command also +calculates the measurement of the memory contents as it encrypts. + +``SNP_LAUNCH_FINISH`` finalizes the guest launch flow. Optionally, while +finalizing the launch the firmware can perform checks on the launch digest +computing through the ``SNP_LAUNCH_UPDATE``. To perform the check the user must +supply the id block, authentication blob and host data that should be included +in the attestation report. See the SEV-SNP spec for further details. + +The ``SNP_LAUNCH_FINISH`` uses the following parameters, which can be configured +by the corresponding parameters documented in the QAPI schema for the +'sev-snp-guest' object. + ++--------------------+-------+----------+-------------------------------------+ +| key | type | default | meaning | ++--------------------+-------+----------+-------------------------------------+ +| id-block | string| none | base64 encoded ID block | ++--------------------+-------+----------+-------------------------------------+ +| id-auth | string| none | base64 encoded authentication | +| | | | information | ++--------------------+-------+----------+-------------------------------------+ +| author-key-enabled | bool | 0 | auth block contains author key | ++--------------------+-------+----------+-------------------------------------+ +| host_data | string| none | host provided data | ++--------------------+-------+----------+-------------------------------------+ + +To launch a SEV-SNP guest (additional parameters are documented in the QAPI +schema for the 'sev-snp-guest' object):: + + # ${QEMU} \ + -machine ...,confidential-guest-support=sev0 \ + -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1 + + Debugging --------- @@ -183,13 +249,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 +265,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/hyperv.rst b/docs/system/i386/hyperv.rst index 2505dc4c86..1c1de77feb 100644 --- a/docs/system/i386/hyperv.rst +++ b/docs/system/i386/hyperv.rst @@ -262,14 +262,19 @@ Supplementary features ``hv-passthrough`` In some cases (e.g. during development) it may make sense to use QEMU in 'pass-through' mode and give Windows guests all enlightenments currently - supported by KVM. This pass-through mode is enabled by "hv-passthrough" CPU - flag. + supported by KVM. Note: ``hv-passthrough`` flag only enables enlightenments which are known to QEMU (have corresponding 'hv-' flag) and copies ``hv-spinlocks`` and ``hv-vendor-id`` values from KVM to QEMU. ``hv-passthrough`` overrides all other 'hv-' settings on - the command line. Also, enabling this flag effectively prevents migration as the - list of enabled enlightenments may differ between target and destination hosts. + the command line. + + Note: ``hv-passthrough`` does not enable ``hv-syndbg`` which can prevent certain + Windows guests from booting when used without proper configuration. If needed, + ``hv-syndbg`` can be enabled additionally. + + Note: ``hv-passthrough`` effectively prevents migration as the list of enabled + enlightenments may differ between target and destination hosts. ``hv-enforce-cpuid`` By default, KVM allows the guest to use all currently supported Hyper-V @@ -278,6 +283,36 @@ Supplementary features feature alters this behavior and only allows the guest to use exposed Hyper-V enlightenments. +Recommendations +--------------- + +To achieve the best performance of Windows and Hyper-V guests and unless there +are any specific requirements (e.g. migration to older QEMU/KVM versions, +emulating specific Hyper-V version, ...), it is recommended to enable all +currently implemented Hyper-V enlightenments with the following exceptions: + +- ``hv-syndbg``, ``hv-passthrough``, ``hv-enforce-cpuid`` should not be enabled + in production configurations as these are debugging/development features. +- ``hv-reset`` can be avoided as modern Hyper-V versions don't expose it. +- ``hv-evmcs`` can (and should) be enabled on Intel CPUs only. While the feature + is only used in nested configurations (Hyper-V, WSL2), enabling it for regular + Windows guests should not have any negative effects. +- ``hv-no-nonarch-coresharing`` must only be enabled if vCPUs are properly pinned + so no non-architectural core sharing is possible. +- ``hv-vendor-id``, ``hv-version-id-build``, ``hv-version-id-major``, + ``hv-version-id-minor``, ``hv-version-id-spack``, ``hv-version-id-sbranch``, + ``hv-version-id-snumber`` can be left unchanged, guests are not supposed to + behave differently when different Hyper-V version is presented to them. +- ``hv-crash`` must only be enabled if the crash information is consumed via + QAPI by higher levels of the virtualization stack. Enabling this feature + effectively prevents Windows from creating dumps upon crashes. +- ``hv-reenlightenment`` can only be used on hardware which supports TSC + scaling or when guest migration is not needed. +- ``hv-spinlocks`` should be set to e.g. 0xfff when host CPUs are overcommited + (meaning there are other scheduled tasks or guests) and can be left unchanged + from the default value (0xffffffff) otherwise. +- ``hv-avic``/``hv-apicv`` should not be enabled if the hardware does not + support APIC virtualization (Intel APICv, AMD AVIC). Useful links ------------ diff --git a/docs/system/i386/nitro-enclave.rst b/docs/system/i386/nitro-enclave.rst new file mode 100644 index 0000000000..73e3edefe5 --- /dev/null +++ b/docs/system/i386/nitro-enclave.rst @@ -0,0 +1,78 @@ +'nitro-enclave' virtual machine (``nitro-enclave``) +=================================================== + +``nitro-enclave`` is a machine type which emulates an *AWS nitro enclave* +virtual machine. `AWS nitro enclaves`_ is an Amazon EC2 feature that allows +creating isolated execution environments, called enclaves, from Amazon EC2 +instances which are used for processing highly sensitive data. Enclaves have +no persistent storage and no external networking. The enclave VMs are based +on Firecracker microvm with a vhost-vsock device for communication with the +parent EC2 instance that spawned it and a Nitro Secure Module (NSM) device +for cryptographic attestation. The parent instance VM always has CID 3 while +the enclave VM gets a dynamic CID. Enclaves use an EIF (`Enclave Image Format`_) +file which contains the necessary kernel, cmdline and ramdisk(s) to boot. + +In QEMU, ``nitro-enclave`` is a machine type based on ``microvm`` similar to how +AWS nitro enclaves are based on `Firecracker`_ microvm. This is useful for +local testing of EIF files using QEMU instead of running real AWS Nitro Enclaves +which can be difficult for debugging due to its roots in security. The vsock +device emulation is done using vhost-user-vsock which means another process that +can do the userspace emulation, like `vhost-device-vsock`_ from rust-vmm crate, +must be run alongside nitro-enclave for the vsock communication to work. + +``libcbor`` and ``gnutls`` are required dependencies for nitro-enclave machine +support to be added when building QEMU from source. + +.. _AWS nitro enclaves: https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html +.. _Enclave Image Format: https://github.com/aws/aws-nitro-enclaves-image-format +.. _vhost-device-vsock: https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-vsock +.. _Firecracker: https://firecracker-microvm.github.io + +Using the nitro-enclave machine type +------------------------------------ + +Machine-specific options +~~~~~~~~~~~~~~~~~~~~~~~~ + +It supports the following machine-specific options: + +- nitro-enclave.vsock=string (required) (Id of the chardev from '-chardev' option that vhost-user-vsock device will use) +- nitro-enclave.id=string (optional) (Set enclave identifier) +- nitro-enclave.parent-role=string (optional) (Set parent instance IAM role ARN) +- nitro-enclave.parent-id=string (optional) (Set parent instance identifier) + + +Running a nitro-enclave VM +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First, run `vhost-device-vsock`__ (or a similar tool that supports vhost-user-vsock). +The forward-cid option below with value 1 forwards all connections from the enclave +VM to the host machine and the forward-listen (port numbers separated by '+') is used +for forwarding connections from the host machine to the enclave VM. + +__ https://github.com/rust-vmm/vhost-device/tree/main/vhost-device-vsock#using-the-vsock-backend + + $ vhost-device-vsock \ + --vm guest-cid=4,forward-cid=1,forward-listen=9001+9002,socket=/tmp/vhost4.socket + +Now run the necessary applications on the host machine so that the nitro-enclave VM +applications' vsock communication works. For example, the nitro-enclave VM's init +process connects to CID 3 and sends a single byte hello heartbeat (0xB7) to let the +parent VM know that it booted expecting a heartbeat (0xB7) response. So you must run +a AF_VSOCK server on the host machine that listens on port 9000 and sends the heartbeat +after it receives the heartbeat for enclave VM to boot successfully. You should run all +the applications on the host machine that would typically be running in the parent EC2 +VM for successful communication with the enclave VM. + +Then run the nitro-enclave VM using the following command where ``hello.eif`` is +an EIF file you would use to spawn a real AWS nitro enclave virtual machine: + + $ qemu-system-x86_64 -M nitro-enclave,vsock=c,id=hello-world \ + -kernel hello-world.eif -nographic -m 4G --enable-kvm -cpu host \ + -chardev socket,id=c,path=/tmp/vhost4.socket + +In this example, the nitro-enclave VM has CID 4. If there are applications that +connect to the enclave VM, run them on the host machine after enclave VM starts. +You need to modify the applications to connect to CID 1 (instead of the enclave +VM's CID) and use the forward-listen (e.g., 9001+9002) option of vhost-device-vsock +to forward the ports they connect to. 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/i386/xenpvh.rst b/docs/system/i386/xenpvh.rst new file mode 100644 index 0000000000..354250f073 --- /dev/null +++ b/docs/system/i386/xenpvh.rst @@ -0,0 +1,49 @@ +Xen PVH machine (``xenpvh``) +========================================= + +Xen supports a spectrum of types of guests that vary in how they depend +on HW virtualization features, emulation models and paravirtualization. +PVH is a mode that uses HW virtualization features (like HVM) but tries +to avoid emulation models and instead use passthrough or +paravirtualized devices. + +QEMU can be used to provide PV virtio devices on an emulated PCIe controller. +That is the purpose of this minimal machine. + +Supported devices +----------------- + +The x86 Xen PVH QEMU machine provide the following devices: + +- RAM +- GPEX host bridge +- virtio-pci devices + +The idea is to only connect virtio-pci devices but in theory any compatible +PCI device model will work depending on Xen and guest support. + +Running +------- + +The Xen tools will typically construct a command-line and launch QEMU +for you when needed. But here's an example of what it can look like in +case you need to construct one manually: + +.. code-block:: console + + qemu-system-i386 -xen-domid 3 -no-shutdown \ + -chardev socket,id=libxl-cmd,path=/var/run/xen/qmp-libxl-3,server=on,wait=off \ + -mon chardev=libxl-cmd,mode=control \ + -chardev socket,id=libxenstat-cmd,path=/var/run/xen/qmp-libxenstat-3,server=on,wait=off \ + -mon chardev=libxenstat-cmd,mode=control \ + -nodefaults \ + -no-user-config \ + -xen-attach -name g0 \ + -vnc none \ + -display none \ + -device virtio-net-pci,id=nic0,netdev=net0,mac=00:16:3e:5c:81:78 \ + -netdev type=tap,id=net0,ifname=vif3.0-emu,br=xenbr0,script=no,downscript=no \ + -smp 4,maxcpus=4 \ + -nographic \ + -machine xenpvh,ram-low-base=0,ram-low-size=2147483648,ram-high-base=4294967296,ram-high-size=2147483648,pci-ecam-base=824633720832,pci-ecam-size=268435456,pci-mmio-base=4026531840,pci-mmio-size=33554432,pci-mmio-high-base=824902156288,pci-mmio-high-size=68719476736 \ + -m 4096 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 489ea20f8f..0000000000 --- a/docs/system/loongarch/loongson3.rst +++ /dev/null @@ -1,129 +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 -- ACPI GED device -- Fw_cfg device -- PCI/PCIe devices -- Memory device -- CPU device. Type: la464-loongarch-cpu. - -CPU and machine Type --------------------- - -The ``qemu-system-loongarch64`` provides emulation for virt -machine. You can specify the machine type ``virt`` and -cpu type ``la464-loongarch-cpu``. - -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) booting command: - -.. code-block:: bash - - $ qemu-system-loongarch64 -machine virt -m 4G -cpu la464-loongarch-cpu \ - -smp 1 -bios QEMU_EFI.fd -kernel vmlinuz.efi -initrd initrd.img \ - -append "root=/dev/ram rdinit=/sbin/init console=ttyS0,115200" \ - --nographic - -Note: The running speed may be a little slow, as the performance of our -qemu and uefi bios is not perfect, and it is being fixed. - -(2) cross compiler tools: - -.. code-block:: bash - - wget https://github.com/loongson/build-tools/releases/download/ \ - 2022.05.29/loongarch64-clfs-5.0-cross-tools-gcc-full.tar.xz - - tar -vxf loongarch64-clfs-5.0-cross-tools-gcc-full.tar.xz - -(3) qemu compile configure option: - -.. 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 - -(4) uefi bios source code and compile method: - -.. code-block:: bash - - git clone https://github.com/loongson/edk2-LoongarchVirt.git - - cd edk2-LoongarchVirt - - git submodule update --init - - export PATH=$YOUR_COMPILER_PATH/bin:$PATH - - export WORKSPACE=`pwd` - - export PACKAGES_PATH=$WORKSPACE/edk2-LoongarchVirt - - export GCC5_LOONGARCH64_PREFIX=loongarch64-unknown-linux-gnu- - - edk2-LoongarchVirt/edksetup.sh - - make -C edk2-LoongarchVirt/BaseTools - - build --buildtarget=DEBUG --tagname=GCC5 --arch=LOONGARCH64 --platform=OvmfPkg/LoongArchQemu/Loongson.dsc - - build --buildtarget=RELEASE --tagname=GCC5 --arch=LOONGARCH64 --platform=OvmfPkg/LoongArchQemu/Loongson.dsc - -The efi binary file path: - - Build/LoongArchQemu/DEBUG_GCC5/FV/QEMU_EFI.fd - - Build/LoongArchQemu/RELEASE_GCC5/FV/QEMU_EFI.fd - -(5) linux kernel source code and compile method: - -.. code-block:: bash - - git clone https://github.com/loongson/linux.git - - export PATH=$YOUR_COMPILER_PATH/bin:$PATH - - export LD_LIBRARY_PATH=$YOUR_COMPILER_PATH/lib:$LD_LIBRARY_PATH - - export LD_LIBRARY_PATH=$YOUR_COMPILER_PATH/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH - - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- loongson3_defconfig - - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- - - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- install - - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- modules_install - -Note: The branch of linux source code is loongarch-next. - -(6) initrd file: - - 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 diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst new file mode 100644 index 0000000000..172fba079e --- /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 --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/tree/master/OvmfPkg/LoongArchVirt#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 1b8852c27c..2008a67809 100644 --- a/docs/system/multi-process.rst +++ b/docs/system/multi-process.rst @@ -1,3 +1,5 @@ +.. _Multi-process QEMU: + Multi-process QEMU ================== diff --git a/docs/system/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/powermac.rst b/docs/system/ppc/powermac.rst index 04334ba210..3eac81c491 100644 --- a/docs/system/ppc/powermac.rst +++ b/docs/system/ppc/powermac.rst @@ -4,8 +4,8 @@ PowerMac family boards (``g3beige``, ``mac99``) Use the executable ``qemu-system-ppc`` to simulate a complete PowerMac PowerPC system. -- ``g3beige`` Heathrow based PowerMAC -- ``mac99`` Mac99 based PowerMAC +- ``g3beige`` Heathrow based PowerMac +- ``mac99`` Mac99 based PowerMac Supported devices ----------------- diff --git a/docs/system/ppc/powernv.rst b/docs/system/ppc/powernv.rst index c8f9762342..de7a807ac7 100644 --- a/docs/system/ppc/powernv.rst +++ b/docs/system/ppc/powernv.rst @@ -181,7 +181,7 @@ connected to a remote QEMU machine acting as BMC, using these options .. code-block:: bash - -chardev socket,id=ipmi0,host=localhost,port=9002,reconnect=10 \ + -chardev socket,id=ipmi0,host=localhost,port=9002,reconnect-ms=10000 \ -device ipmi-bmc-extern,id=bmc0,chardev=ipmi0 \ -device isa-ipmi-bt,bmc=bmc0,irq=10 \ -nodefaults @@ -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 fa40e57d18..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 @@ -180,3 +181,15 @@ as follows: -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 a876d897b6..bbc51aa7fc 100644 --- a/docs/system/ppc/pseries.rst +++ b/docs/system/ppc/pseries.rst @@ -14,10 +14,19 @@ virtualization capabilities. Supported devices ================= - * Multi processor support for many Power processors generations: POWER7, - POWER7+, POWER8, POWER8NVL, POWER9, and Power10. Support for POWER5+ exists, - but its state is unknown. - * Interrupt Controller, XICS (POWER8) and XIVE (POWER9 and Power10) + * Multi processor support for many Power processors generations: + - POWER7, POWER7+ + - POWER8, POWER8NVL + - POWER9 + - Power10 + - Power11 + - Support for POWER5+ also exists, works with correct kernel/userspace + * Interrupt Controller + - XICS (POWER8) + - XIVE (Supported by below:) + - POWER9 + - Power10 + - Power11 * vPHB PCIe Host bridge. * vscsi and vnet devices, compatible with the same devices available on a PowerVM hypervisor with VIOS managing LPARs. diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc index dfe5d2293d..384e95ba76 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 ^^^^^^^ @@ -731,7 +737,6 @@ Examples |qemu_system| -drive file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img |qemu_system| -drive file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img |qemu_system| -drive file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket - |qemu_system| -drive file=gluster+rdma://1.2.3.4:24007/testvol/a.img |qemu_system| -drive file=gluster://1.2.3.4/testvol/a.img,file.debug=9,file.logfile=/var/log/qemu-gluster.log |qemu_system| 'json:{"driver":"qcow2", "file":{"driver":"gluster", 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..28e5772a2b 100644 --- a/docs/system/replay.rst +++ b/docs/system/replay.rst @@ -24,7 +24,7 @@ Deterministic replay has the following features: * Writes execution log into the file for later replaying for multiple times on different machines. * Supports i386, x86_64, ARM, AArch64, Risc-V, MIPS, MIPS64, S390X, Alpha, - PowerPC, PowerPC64, M68000, Microblaze, OpenRISC, Nios II, SPARC, + PowerPC, PowerPC64, M68000, Microblaze, OpenRISC, SPARC, and Xtensa hardware platforms. * Performs deterministic replay of all operations with keyboard and mouse input devices, serial ports, and network. @@ -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 4b16e41d7f..8e9a2e4dda 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,50 @@ 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. + +riscv-iommu support +------------------- + +The board has support for the riscv-iommu-pci device by using the following +command line: + +.. code-block:: bash + + $ qemu-system-riscv64 -M virt -device riscv-iommu-pci (...) + +Refer to :ref:`riscv-iommu` for more information on how the RISC-V IOMMU support +works. + Machine-specific options ------------------------ @@ -62,6 +106,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] diff --git a/docs/system/s390x/bootdevices.rst b/docs/system/s390x/bootdevices.rst index 1a7a18b43b..97b3914785 100644 --- a/docs/system/s390x/bootdevices.rst +++ b/docs/system/s390x/bootdevices.rst @@ -6,9 +6,7 @@ Booting with bootindex parameter For classical mainframe guests (i.e. LPAR or z/VM installations), you always have to explicitly specify the disk where you want to boot from (or "IPL" from, -in s390x-speak -- IPL means "Initial Program Load"). In particular, there can -also be only one boot device according to the architecture specification, thus -specifying multiple boot devices is not possible (yet). +in s390x-speak -- IPL means "Initial Program Load"). So for booting an s390x guest in QEMU, you should always mark the device where you want to boot from with the ``bootindex`` property, for @@ -17,6 +15,11 @@ example:: qemu-system-s390x -drive if=none,id=dr1,file=guest.qcow2 \ -device virtio-blk,drive=dr1,bootindex=1 +Multiple devices may have a bootindex. The lowest bootindex is assigned to the +device to IPL first. If the IPL fails for the first, the device with the second +lowest bootindex will be tried and so on until IPL is successful or there are no +remaining boot devices to try. + For booting from a CD-ROM ISO image (which needs to include El-Torito boot information in order to be bootable), it is recommended to specify a ``scsi-cd`` device, for example like this:: @@ -76,29 +79,45 @@ 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. +Note that ``0`` can be used to boot the default entry. If the machine +``loadparm`` is not assigned a value, then the default entry is used. + +By default, the machine ``loadparm`` applies to all boot devices. If multiple +devices are assigned a ``bootindex`` and the ``loadparm`` is to be different +between them, an independent ``loadparm`` may be assigned on a per-device basis. + +An example guest using per-device ``loadparm``:: + + qemu-system-s390x -drive if=none,id=dr1,file=primary.qcow2 \ + -device virtio-blk,drive=dr1,bootindex=1 \ + -drive if=none,id=dr2,file=secondary.qcow2 \ + -device virtio-blk,drive=dr2,bootindex=2,loadparm=3 + +In this case, the primary boot device will attempt to IPL using the default +entry (because no ``loadparm`` is specified for this device or for the +machine). If that device fails to boot, the secondary device will attempt to +IPL using entry number 3. + +If a ``loadparm`` is specified on both the machine and a device, the per-device +value will superseded the machine value. Per-device ``loadparm`` values are +only used for devices with an assigned ``bootindex``. The machine ``loadparm`` +is used when attempting to boot without a ``bootindex``. Booting from a network device ----------------------------- -Beside the normal guest firmware (which is loaded from the file ``s390-ccw.img`` -in the data directory of QEMU, or via the ``-bios`` option), QEMU ships with -a small TFTP network bootloader firmware for virtio-net-ccw devices, too. This -firmware is loaded from a file called ``s390-netboot.img`` in the QEMU data -directory. In case you want to load it from a different filename instead, -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 ``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:: +The firmware that ships with QEMU includes a small TFTP network bootloader +for virtio-net-ccw devices. The ``bootindex`` property is especially +important for booting via the network. If you don't specify the ``bootindex`` +property here, the network bootloader won't be taken into consideration and +the network boot will fail. For a successful network boot, try something +like this:: qemu-system-s390x -netdev user,id=n1,tftp=...,bootfile=... \ -device virtio-net-ccw,netdev=n1,bootindex=1 -The network bootloader firmware also has basic support for pxelinux.cfg-style +The network bootloader also has basic support for pxelinux.cfg-style configuration files. See the `PXELINUX Configuration page `__ for details how to set up the configuration file on your TFTP server. 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..9aaa9c414c 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -63,10 +63,6 @@ large amounts of RAM. It also supports 64-bit CPUs. Board-specific documentation ============================ -Unfortunately many of the Arm boards QEMU supports are currently -undocumented; you can get a complete list by running -``qemu-system-aarch64 --machine help``. - .. This table of contents should be kept sorted alphabetically by the title text of each file, which isn't the same ordering @@ -83,29 +79,33 @@ 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/highbank arm/digic arm/cubieboard arm/emcraft-sf2 - arm/highbank + arm/exynos + arm/fby35 arm/musicpal - arm/gumstix - arm/mainstone arm/kzm arm/nrf - arm/nseries arm/nuvoton arm/imx25-pdk + arm/mcimx6ul-evk + arm/mcimx7d-sabre arm/orangepi - arm/palm arm/raspi - arm/xscale arm/collie arm/sx1 arm/stellaris arm/stm32 arm/virt + arm/xenpvh arm/xlnx-versal-virt + arm/xlnx-zynq + arm/xlnx-zcu102 Emulated CPU architecture support ================================= diff --git a/docs/system/target-i386-desc.rst.inc b/docs/system/target-i386-desc.rst.inc index 7d1fffacbe..ae312b1c1e 100644 --- a/docs/system/target-i386-desc.rst.inc +++ b/docs/system/target-i386-desc.rst.inc @@ -36,7 +36,8 @@ The QEMU PC System emulator simulates the following peripherals: - PCI UHCI, OHCI, EHCI or XHCI USB controller and a virtual USB-1.1 hub. -SMP is supported with up to 255 CPUs. +SMP is supported with a large number of virtual CPUs (upper limit is +configuration dependent). QEMU uses the PC BIOS from the Seabios project and the Plex86/Bochs LGPL VGA BIOS. @@ -71,3 +72,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..ab7af1a75d 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -16,8 +14,9 @@ Board-specific documentation .. toctree:: :maxdepth: 1 - i386/microvm i386/pc + i386/microvm + i386/nitro-enclave Architectural features ~~~~~~~~~~~~~~~~~~~~~~ @@ -27,12 +26,12 @@ Architectural features i386/cpu i386/hyperv + i386/xen + i386/xenpvh 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-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..4116cac493 100644 --- a/docs/system/target-sparc.rst +++ b/docs/system/target-sparc.rst @@ -27,6 +27,11 @@ architecture machines: The emulation is somewhat complete. SMP up to 16 CPUs is supported, but Linux limits the number of usable CPUs to 4. +The list of available CPUs can be viewed by starting QEMU with ``-cpu help``. +Optional boolean features can be added with a "+" in front of the feature name, +or disabled with a "-" in front of the name, for example +``-cpu TI-SuperSparc-II,+float128``. + QEMU emulates the following sun4m peripherals: - IOMMU @@ -38,7 +43,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 @@ -55,8 +60,5 @@ OpenBIOS is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. -A sample Linux 2.6 series kernel and ram disk image are available on the -QEMU web site. There are still issues with NetBSD and OpenBSD, but most -kernel versions work. Please note that currently older Solaris kernels -don't work probably due to interface issues between OpenBIOS and -Solaris. +Please note that currently older Solaris kernels don't work; this is probably +due to interface issues between OpenBIOS and Solaris. 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..1e88ae48cd 100644 --- a/docs/tools/index.rst +++ b/docs/tools/index.rst @@ -1,3 +1,5 @@ +.. _Tools: + ----- Tools ----- @@ -13,5 +15,4 @@ command line utilities and other standalone programs. qemu-nbd qemu-pr-helper qemu-trace-stap - virtfs-proxy-helper - virtiofsd + qemu-vmsr-helper diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 15aeddc6d8..3653adb963 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -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 faf6349ea5..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 diff --git a/docs/tools/qemu-vmsr-helper.rst b/docs/tools/qemu-vmsr-helper.rst new file mode 100644 index 0000000000..9ce10b9af9 --- /dev/null +++ b/docs/tools/qemu-vmsr-helper.rst @@ -0,0 +1,89 @@ +================================== +QEMU virtual RAPL MSR helper +================================== + +Synopsis +-------- + +**qemu-vmsr-helper** [*OPTION*] + +Description +----------- + +Implements the virtual RAPL MSR helper for QEMU. + +Accessing the RAPL (Running Average Power Limit) MSR enables the RAPL powercap +driver to advertise and monitor the power consumption or accumulated energy +consumption of different power domains, such as CPU packages, DRAM, and other +components when available. + +However those registers are accessible under privileged access (CAP_SYS_RAWIO). +QEMU can use an external helper to access those privileged registers. + +:program:`qemu-vmsr-helper` 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 section on "RAPL MSR +support" to find out how to configure QEMU to connect to the socket created by +:program:`qemu-vmsr-helper`. + +After connecting to the socket, :program:`qemu-vmsr-helper` can +optionally drop root privileges, except for those capabilities that +are needed for its operation. + +:program:`qemu-vmsr-helper` can also use the systemd socket activation +protocol. In this case, the systemd socket unit should specify a +Unix stream socket, like this:: + + [Socket] + ListenStream=/var/run/qemu-vmsr-helper.sock + +Options +------- + +.. program:: qemu-vmsr-helper + +.. option:: -d, --daemon + + run in the background (and create a PID file) + +.. option:: -q, --quiet + + decrease verbosity + +.. option:: -v, --verbose + + increase verbosity + +.. option:: -f, --pidfile=PATH + + PID file when running as a daemon. By default the PID file + is created in the system runtime state directory, for example + :file:`/var/run/qemu-vmsr-helper.pid`. + +.. option:: -k, --socket=PATH + + path to the socket. By default the socket is created in + the system runtime state directory, for example + :file:`/var/run/qemu-vmsr-helper.sock`. + +.. option:: -T, --trace [[enable=]PATTERN][,events=FILE][,file=FILE] + + .. include:: ../qemu-option-trace.rst.inc + +.. option:: -u, --user=USER + + user to drop privileges to + +.. option:: -g, --group=GROUP + + group to drop privileges to + +.. option:: -h, --help + + Display a help message and exit. + +.. option:: -V, --version + + Display version information and exit. diff --git a/docs/tools/virtfs-proxy-helper.rst b/docs/tools/virtfs-proxy-helper.rst deleted file mode 100644 index 6cdeedf8e9..0000000000 --- a/docs/tools/virtfs-proxy-helper.rst +++ /dev/null @@ -1,72 +0,0 @@ -QEMU 9p virtfs proxy filesystem helper -====================================== - -Synopsis --------- - -**virtfs-proxy-helper** [*OPTIONS*] - -Description ------------ - -Pass-through security model in QEMU 9p server needs root privilege to do -few file operations (like chown, chmod to any mode/uid:gid). There are two -issues in pass-through security model: - -- TOCTTOU vulnerability: Following symbolic links in the server could - provide access to files beyond 9p export path. - -- Running QEMU with root privilege could be a security issue. - -To overcome above issues, following approach is used: A new filesystem -type 'proxy' is introduced. Proxy FS uses chroot + socket combination -for securing the vulnerability known with following symbolic links. -Intention of adding a new filesystem type is to allow qemu to run -in non-root mode, but doing privileged operations using socket IO. - -Proxy helper (a stand alone binary part of qemu) is invoked with -root privileges. Proxy helper chroots into 9p export path and creates -a socket pair or a named socket based on the command line parameter. -QEMU and proxy helper communicate using this socket. QEMU proxy fs -driver sends filesystem request to proxy helper and receives the -response from it. - -The proxy helper is designed so that it can drop root privileges except -for the capabilities needed for doing filesystem operations. - -Options -------- - -The following options are supported: - -.. program:: virtfs-proxy-helper - -.. option:: -h - - Display help and exit - -.. option:: -p, --path PATH - - Path to export for proxy filesystem driver - -.. option:: -f, --fd SOCKET_ID - - Use given file descriptor as socket descriptor for communicating with - qemu proxy fs drier. Usually a helper like libvirt will create - socketpair and pass one of the fds as parameter to this option. - -.. option:: -s, --socket SOCKET_FILE - - Creates named socket file for communicating with qemu proxy fs driver - -.. option:: -u, --uid UID - - uid to give access to named socket file; used in combination with -g. - -.. option:: -g, --gid GID - - gid to give access to named socket file; used in combination with -u. - -.. option:: -n, --nodaemon - - Run as a normal program. By default program will run in daemon mode diff --git a/docs/tools/virtiofsd.rst b/docs/tools/virtiofsd.rst deleted file mode 100644 index 995a754a7b..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 'security.' 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..7a126ee809 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: @@ -131,10 +130,6 @@ Other binaries The binary format is detected automatically. -- user mode (Cris) - - * ``qemu-cris`` TODO. - - user mode (i386) * ``qemu-i386`` TODO. @@ -160,10 +155,6 @@ Other binaries * ``qemu-mipsn32el`` executes 32-bit little endian MIPS binaries (MIPS N32 ABI). -- user mode (NiosII) - - * ``qemu-nios2`` TODO. - - user mode (PowerPC) * ``qemu-ppc64`` TODO. @@ -242,5 +233,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 df117c847f..45e84428ae 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,9 @@ #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 "qemu/range.h" #include #ifdef CONFIG_LZO @@ -99,22 +97,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_array_unref(s->string_table_buf); + 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; } @@ -357,7 +359,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; @@ -574,8 +575,10 @@ static void get_offset_range(hwaddr phys_addr, QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { if (dump_has_filter(s)) { - if (block->target_start >= s->filter_area_begin + s->filter_area_length || - block->target_end <= s->filter_area_begin) { + if (!ranges_overlap(block->target_start, + block->target_end - block->target_start, + s->filter_area_begin, + s->filter_area_length)) { /* This block is out of the range */ continue; } @@ -734,8 +737,9 @@ int64_t dump_filtered_memblock_start(GuestPhysBlock *block, { 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) { + if (!ranges_overlap(block->target_start, + block->target_end - block->target_start, + filter_area_start, filter_area_length)) { return -1; } @@ -773,7 +777,6 @@ static void dump_iterate(DumpState *s, Error **errp) static void dump_end(DumpState *s, Error **errp) { int rc; - ERRP_GUARD(); if (s->elf_section_data_size) { s->elf_section_data = g_malloc0(s->elf_section_data_size); @@ -814,11 +817,15 @@ static void create_vmcore(DumpState *s, Error **errp) 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); @@ -829,7 +836,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; } @@ -838,15 +845,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; } @@ -854,20 +865,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; } @@ -909,13 +928,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) { @@ -987,7 +1006,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; } @@ -1017,7 +1036,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; @@ -1032,7 +1051,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; @@ -1098,7 +1117,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; } @@ -1128,7 +1147,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; @@ -1144,7 +1163,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; @@ -1209,7 +1228,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; } @@ -1217,7 +1236,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; } @@ -1298,8 +1317,8 @@ static bool get_next_page(GuestPhysBlock **blockptr, uint64_t *pfnptr, memcpy(buf + addr % page_size, hbuf, n); addr += n; - if (addr % page_size == 0) { - /* we filled up the page */ + if (addr % page_size == 0 || addr >= block->target_end) { + /* we filled up the page or the current block is finished */ break; } } else { @@ -1385,7 +1404,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); @@ -1404,11 +1423,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; } @@ -1649,7 +1668,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; @@ -1670,33 +1689,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 int validate_start_block(DumpState *s) -{ - GuestPhysBlock *block; - - if (!dump_has_filter(s)) { - return 0; - } - - QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) { - /* This block is out of the range */ - if (block->target_start >= s->filter_area_begin + s->filter_area_length || - block->target_end <= s->filter_area_begin) { - continue; - } - return 0; - } - - return -1; -} - static void get_max_mapnr(DumpState *s) { GuestPhysBlock *last_block; @@ -1780,7 +1779,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(); @@ -1791,6 +1791,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) { @@ -1815,7 +1816,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, s->fd = fd; if (has_filter && !length) { - error_setg(errp, QERR_INVALID_PARAMETER, "length"); + error_setg(errp, "parameter 'length' expects a non-zero size"); goto cleanup; } s->filter_area_begin = begin; @@ -1844,32 +1845,24 @@ static void dump_init(DumpState *s, int fd, bool has_format, goto cleanup; } - /* Is the filter filtering everything? */ - if (validate_start_block(s) == -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 @@ -1879,20 +1872,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); @@ -2026,9 +2019,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 { @@ -2044,8 +2035,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); @@ -2070,17 +2061,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."); @@ -2094,6 +2087,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. @@ -2131,32 +2147,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; } @@ -2169,7 +2182,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; @@ -2179,7 +2192,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; @@ -2207,21 +2220,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 f20b6051b6..0e4fe692ce 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,21 @@ */ #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 "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) { @@ -49,6 +51,7 @@ static size_t write_run(uint64_t base_page, uint64_t page_count, uint64_t addr = base_page << TARGET_PAGE_BITS; uint64_t size = page_count << TARGET_PAGE_BITS; uint64_t len, l; + int eno; size_t total = 0; while (size) { @@ -62,9 +65,10 @@ static size_t write_run(uint64_t base_page, uint64_t page_count, } l = qemu_write_full(fd, buf, len); + eno = errno; cpu_physical_memory_unmap(buf, addr, false, len); if (l != len) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, eno, "win-dump: failed to save memory"); return 0; } @@ -456,7 +460,7 @@ void create_win_dump(DumpState *s, Error **errp) s->written_size = qemu_write_full(s->fd, h, hdr_size); if (s->written_size != hdr_size) { - error_setg(errp, QERR_IO_ERROR); + error_setg_errno(errp, errno, "win-dump: failed to write header"); goto out_restore; } @@ -475,3 +479,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..d0e7f99fb9 100644 --- a/ebpf/ebpf_rss-stub.c +++ b/ebpf/ebpf_rss-stub.c @@ -23,13 +23,21 @@ bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx) return false; } -bool ebpf_rss_load(struct EBPFRSSContext *ctx) +bool ebpf_rss_load(struct EBPFRSSContext *ctx, Error **errp) +{ + return false; +} + +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd, + Error **errp) { return false; } bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, - uint16_t *indirections_table, uint8_t *toeplitz_key) + uint16_t *indirections_table, uint8_t *toeplitz_key, + Error **errp) { return false; } diff --git a/ebpf/ebpf_rss.c b/ebpf/ebpf_rss.c index cee658c158..e793786c17 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,105 @@ #include "ebpf/ebpf_rss.h" #include "ebpf/rss.bpf.skeleton.h" +#include "ebpf/ebpf.h" + #include "trace.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); } -bool ebpf_rss_load(struct EBPFRSSContext *ctx) +static bool ebpf_rss_mmap(struct EBPFRSSContext *ctx, Error **errp) +{ + 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) { + trace_ebpf_rss_mmap_error(ctx, "configuration"); + error_setg(errp, "Unable to map eBPF configuration array"); + 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) { + trace_ebpf_rss_mmap_error(ctx, "toeplitz key"); + error_setg(errp, "Unable to map eBPF toeplitz array"); + 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) { + trace_ebpf_rss_mmap_error(ctx, "indirections table"); + error_setg(errp, "Unable to map eBPF indirection array"); + goto indirection_fail; + } + + trace_ebpf_rss_mmap(ctx, + ctx->mmap_configuration, + ctx->mmap_toeplitz_key, + ctx->mmap_indirections_table); + 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) +{ + 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, Error **errp) { 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"); + trace_ebpf_rss_open_error(ctx); + error_setg(errp, "Unable to open eBPF RSS object"); goto error; } 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"); + trace_ebpf_rss_load_error(ctx); + error_setg(errp, "Unable to load eBPF program"); goto error; } @@ -66,90 +135,146 @@ bool ebpf_rss_load(struct EBPFRSSContext *ctx) ctx->map_toeplitz_key = bpf_map__fd( rss_bpf_ctx->maps.tap_rss_map_toeplitz_key); + trace_ebpf_rss_load(ctx, + ctx->program_fd, + ctx->map_configuration, + ctx->map_indirections_table, + ctx->map_toeplitz_key); + if (!ebpf_rss_mmap(ctx, errp)) { + 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; } -static bool ebpf_rss_set_config(struct EBPFRSSContext *ctx, +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd, + Error **errp) +{ + if (ebpf_rss_is_loaded(ctx)) { + error_setg(errp, "eBPF program is already loaded"); + return false; + } + + if (program_fd < 0) { + error_setg(errp, "eBPF program FD is not open"); + return false; + } + if (config_fd < 0) { + error_setg(errp, "eBPF config FD is not open"); + return false; + } + if (toeplitz_fd < 0) { + error_setg(errp, "eBPF toeplitz FD is not open"); + return false; + } + if (table_fd < 0) { + error_setg(errp, "eBPF indirection FD is not open"); + return false; + } + + ctx->program_fd = program_fd; + ctx->map_configuration = config_fd; + ctx->map_toeplitz_key = toeplitz_fd; + ctx->map_indirections_table = table_fd; + + trace_ebpf_rss_load(ctx, + ctx->program_fd, + ctx->map_configuration, + ctx->map_indirections_table, + ctx->map_toeplitz_key); + + if (!ebpf_rss_mmap(ctx, errp)) { + ctx->program_fd = -1; + ctx->map_configuration = -1; + ctx->map_toeplitz_key = -1; + ctx->map_indirections_table = -1; + return false; + } + + return true; +} + +static void 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; - } - return true; + memcpy(ctx->mmap_configuration, config, sizeof(*config)); } static bool ebpf_rss_set_indirections_table(struct EBPFRSSContext *ctx, uint16_t *indirections_table, - size_t len) + size_t len, + Error **errp) { - 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) { + if (len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { + error_setg(errp, "Indirections table length %zu exceeds limit %d", + 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, +static void 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] = {}; - if (!ebpf_rss_is_loaded(ctx) || toeplitz_key == NULL) { - return false; - } 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; - } - return true; + memcpy(ctx->mmap_toeplitz_key, toe, VIRTIO_NET_RSS_MAX_KEY_SIZE); } bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, - uint16_t *indirections_table, uint8_t *toeplitz_key) + uint16_t *indirections_table, uint8_t *toeplitz_key, + Error **errp) { - if (!ebpf_rss_is_loaded(ctx) || config == NULL || - indirections_table == NULL || toeplitz_key == NULL) { + if (!ebpf_rss_is_loaded(ctx)) { + error_setg(errp, "eBPF program is not loaded"); + return false; + } + if (config == NULL) { + error_setg(errp, "eBPF config table is NULL"); + return false; + } + if (indirections_table == NULL) { + error_setg(errp, "eBPF indirections table is NULL"); + return false; + } + if (toeplitz_key == NULL) { + error_setg(errp, "eBPF toeplitz key is NULL"); return false; } - if (!ebpf_rss_set_config(ctx, config)) { - return false; - } + ebpf_rss_set_config(ctx, config); if (!ebpf_rss_set_indirections_table(ctx, indirections_table, - config->indirections_len)) { + config->indirections_len, + errp)) { return false; } - if (!ebpf_rss_set_toepliz_key(ctx, toeplitz_key)) { - return false; - } + ebpf_rss_set_toepliz_key(ctx, toeplitz_key); + + trace_ebpf_rss_set_data(ctx, config, indirections_table, toeplitz_key); return true; } @@ -160,6 +285,24 @@ void ebpf_rss_unload(struct EBPFRSSContext *ctx) return; } - rss_bpf__destroy(ctx->obj); + trace_ebpf_rss_unload(ctx); + + 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_PROGRAM_ID_RSS, rss_bpf__elf_bytes) diff --git a/ebpf/ebpf_rss.h b/ebpf/ebpf_rss.h index bf3f2572c7..86a5787789 100644 --- a/ebpf/ebpf_rss.h +++ b/ebpf/ebpf_rss.h @@ -14,12 +14,21 @@ #ifndef QEMU_EBPF_RSS_H #define QEMU_EBPF_RSS_H +#include "qapi/error.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 { @@ -34,10 +43,15 @@ void ebpf_rss_init(struct EBPFRSSContext *ctx); bool ebpf_rss_is_loaded(struct EBPFRSSContext *ctx); -bool ebpf_rss_load(struct EBPFRSSContext *ctx); +bool ebpf_rss_load(struct EBPFRSSContext *ctx, Error **errp); + +bool ebpf_rss_load_fds(struct EBPFRSSContext *ctx, int program_fd, + int config_fd, int toeplitz_fd, int table_fd, + Error **errp); bool ebpf_rss_set_all(struct EBPFRSSContext *ctx, struct EBPFRSSConfig *config, - uint16_t *indirections_table, uint8_t *toeplitz_key); + uint16_t *indirections_table, uint8_t *toeplitz_key, + Error **errp); void ebpf_rss_unload(struct EBPFRSSContext *ctx); diff --git a/ebpf/meson.build b/ebpf/meson.build index 2dd0fd8948..bff6156f51 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')) +system_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..647212e5dd 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,850 @@ 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 *)"\ -\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\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\ -\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\ -\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"; + s->data = rss_bpf__elf_bytes(&s->data_sz); + obj->skeleton = s; return 0; err: bpf_object__destroy_skeleton(s); - return -1; + return err; +} + +static inline const void *rss_bpf__elf_bytes(size_t *sz) +{ + static const char data[] __attribute__((__aligned__(8))) = "\ +\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\xb0\x4b\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0d\0\ +\x01\0\x7b\x1a\x48\xff\0\0\0\0\xb7\x09\0\0\0\0\0\0\x63\x9a\x54\xff\0\0\0\0\xbf\ +\xa7\0\0\0\0\0\0\x07\x07\0\0\x54\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\x15\x06\x4e\x02\0\0\0\0\xbf\x78\0\0\0\0\0\0\x15\x08\x4c\x02\0\0\0\0\x71\x61\ +\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\x45\x02\0\0\0\0\xb7\x01\0\0\0\0\0\0\ +\x63\x1a\xc8\xff\0\0\0\0\x7b\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\x63\x1a\xa0\xff\0\0\0\0\x7b\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\x79\xa9\x48\xff\0\0\0\0\ +\x15\x09\x33\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\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\x28\x02\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\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\xd0\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\x18\x02\0\0\0\0\x69\ +\xa1\xd0\xff\0\0\0\0\x15\x01\x16\x02\0\0\0\0\x15\x01\x20\0\x86\xdd\0\0\x55\x01\ +\xf5\0\x08\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x58\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\x79\xa1\x48\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\x04\x02\0\0\0\0\x69\xa1\xd6\ +\xff\0\0\0\0\x57\x01\0\0\x3f\xff\0\0\xb7\x04\0\0\x01\0\0\0\x55\x01\x01\0\0\0\0\ +\0\xb7\x04\0\0\0\0\0\0\x61\xa1\xdc\xff\0\0\0\0\x63\x1a\x64\xff\0\0\0\0\x61\xa1\ +\xe0\xff\0\0\0\0\x63\x1a\x68\xff\0\0\0\0\x71\xa9\xd9\xff\0\0\0\0\x71\xa2\xd0\ +\xff\0\0\0\0\x67\x02\0\0\x02\0\0\0\x57\x02\0\0\x3c\0\0\0\x73\x4a\x5e\xff\0\0\0\ +\0\x05\0\xbb\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x59\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\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\xe3\x01\0\0\0\0\xb7\x03\0\0\x28\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\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\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\x79\xa1\xf0\xff\0\0\0\0\x63\x1a\x7c\xff\0\0\0\0\ +\x77\x01\0\0\x20\0\0\0\x63\x1a\x80\xff\0\0\0\0\x71\xa9\xd6\xff\0\0\0\0\x25\x09\ +\x93\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\x8c\0\0\ +\0\0\0\xb7\x01\0\0\0\0\0\0\x6b\x1a\xfe\xff\0\0\0\0\xb7\x02\0\0\x28\0\0\0\xbf\ +\xa1\0\0\0\0\0\0\x07\x01\0\0\x94\xff\xff\xff\x7b\x1a\x20\xff\0\0\0\0\xbf\xa1\0\ +\0\0\0\0\0\x07\x01\0\0\x84\xff\xff\xff\x7b\x1a\x18\xff\0\0\0\0\xb7\x01\0\0\0\0\ +\0\0\x7b\x1a\x38\xff\0\0\0\0\x7b\x7a\x30\xff\0\0\0\0\x7b\x8a\x28\xff\0\0\0\0\ +\xbf\xa3\0\0\0\0\0\0\x07\x03\0\0\xfe\xff\xff\xff\x79\xa1\x48\xff\0\0\0\0\x7b\ +\x2a\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\x55\0\xb2\x01\0\0\0\0\xbf\x91\0\ +\0\0\0\0\0\x15\x01\x22\0\x3c\0\0\0\x15\x01\x58\0\x2c\0\0\0\x79\xa2\x40\xff\0\0\ +\0\0\x55\x01\x59\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\x79\xa9\x48\xff\0\0\0\0\xbf\x91\0\ +\0\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\x67\ +\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\xa1\x01\0\0\0\0\x71\xa1\xfa\xff\0\0\ +\0\0\x55\x01\x4a\0\x02\0\0\0\x71\xa1\xf9\xff\0\0\0\0\x55\x01\x48\0\x02\0\0\0\ +\x71\xa1\xfb\xff\0\0\0\0\x55\x01\x46\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\x20\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\x67\0\0\0\x20\0\0\0\x77\0\0\0\ +\x20\0\0\0\x55\0\x91\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5d\xff\0\0\0\0\ +\x05\0\x39\0\0\0\0\0\xb7\x08\0\0\x02\0\0\0\xb7\x07\0\0\0\0\0\0\x6b\x7a\xf8\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\xff\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\x30\xff\0\0\0\0\x79\xa8\x28\xff\0\ +\0\0\0\x05\0\x23\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\xf8\xff\xff\xff\x79\xa1\x48\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\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\0\0\x55\0\x6b\x01\0\0\0\0\x71\ +\xa2\xf8\xff\0\0\0\0\x55\x02\x0d\0\xc9\0\0\0\x07\x09\0\0\x02\0\0\0\x79\xa1\x48\ +\xff\0\0\0\0\xbf\x92\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\x67\0\0\0\x20\0\0\0\x77\0\0\0\x20\0\ +\0\0\x55\0\x5f\x01\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5c\xff\0\0\0\0\x05\0\ +\xe1\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x15\x02\xd0\xff\0\0\0\0\x71\xa1\xf9\xff\ +\0\0\0\0\x07\x01\0\0\x02\0\0\0\x05\0\xcd\xff\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\ +\x1a\x5e\xff\0\0\0\0\x79\xa2\x40\xff\0\0\0\0\x71\xa1\xff\xff\0\0\0\0\x67\x01\0\ +\0\x03\0\0\0\x0f\x12\0\0\0\0\0\0\x07\x02\0\0\x08\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\x03\0\0\ +\x01\0\0\0\0\0\0\0\0\x18\0\x1c\x5f\x31\0\0\0\0\0\0\x55\x01\x01\0\0\0\0\0\x05\0\ +\x07\0\0\0\0\0\x79\xa1\x38\xff\0\0\0\0\x07\x01\0\0\x01\0\0\0\x7b\x1a\x38\xff\0\ +\0\0\0\x67\x01\0\0\x20\0\0\0\x77\x01\0\0\x20\0\0\0\x15\x01\x02\0\x0b\0\0\0\x05\ +\0\x84\xff\0\0\0\0\x15\x09\xf8\xff\x87\0\0\0\xbf\x23\0\0\0\0\0\0\x05\0\x01\0\0\ +\0\0\0\x15\x09\x73\xff\x87\0\0\0\x71\xa4\x5e\xff\0\0\0\0\xbf\x32\0\0\0\0\0\0\ +\xbf\x91\0\0\0\0\0\0\x57\x01\0\0\xff\0\0\0\x15\x01\x18\0\0\0\0\0\x57\x04\0\0\ +\xff\0\0\0\x55\x04\x16\0\0\0\0\0\x57\x09\0\0\xff\0\0\0\x15\x09\xb4\0\x11\0\0\0\ +\x55\x09\x13\0\x06\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5b\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\x79\xa1\x48\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\x23\x01\0\0\0\0\x69\xa1\xd0\xff\0\0\0\0\x6b\ +\x1a\x60\xff\0\0\0\0\x69\xa1\xd2\xff\0\0\0\0\x6b\x1a\x62\xff\0\0\0\0\x71\xa1\ +\x58\xff\0\0\0\0\x15\x01\x18\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\x5b\xff\0\0\0\0\x15\x02\x49\0\0\0\0\0\xbf\x12\0\0\ +\0\0\0\0\x57\x02\0\0\x02\0\0\0\x15\x02\x46\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\ +\x63\x1a\xa8\xff\0\0\0\0\x61\xa1\x68\xff\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x69\ +\xa1\x60\xff\0\0\0\0\x6b\x1a\xb0\xff\0\0\0\0\x69\xa1\x62\xff\0\0\0\0\x6b\x1a\ +\xb2\xff\0\0\0\0\x05\0\x9c\0\0\0\0\0\x71\xa1\x59\xff\0\0\0\0\x15\x01\x03\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\x5b\ +\xff\0\0\0\0\x15\x02\x3c\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x10\0\0\0\ +\x15\x02\x39\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\ +\x5c\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\x84\xff\xff\xff\x57\x01\0\0\x80\0\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\x74\xff\xff\xff\x71\xa5\x5d\ +\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\x94\xff\xff\xff\x15\x01\x01\0\0\0\0\0\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\xa8\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\xb0\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\x62\xff\ +\0\0\0\0\x6b\x5a\xca\xff\0\0\0\0\x69\xa5\x60\xff\0\0\0\0\x6b\x5a\xc8\xff\0\0\0\ +\0\x67\x03\0\0\x20\0\0\0\x4f\x43\0\0\0\0\0\0\x7b\x3a\xc0\xff\0\0\0\0\x67\x02\0\ +\0\x20\0\0\0\x4f\x12\0\0\0\0\0\0\x7b\x2a\xb8\xff\0\0\0\0\x05\0\x5f\0\0\0\0\0\ +\x71\xa2\x5a\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\xb4\xff\0\0\0\0\x57\x01\0\0\x01\0\0\0\ +\x15\x01\xc0\0\0\0\0\0\x61\xa1\x64\xff\0\0\0\0\x63\x1a\xa8\xff\0\0\0\0\x61\xa1\ +\x68\xff\0\0\0\0\x63\x1a\xac\xff\0\0\0\0\x05\0\x52\0\0\0\0\0\x71\xa2\x5a\xff\0\ +\0\0\0\x15\x02\x16\0\0\0\0\0\xbf\x12\0\0\0\0\0\0\x57\x02\0\0\x20\0\0\0\x15\x02\ +\x13\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\x64\xff\xff\xff\x71\xa4\x5c\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\x84\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\x74\xff\xff\xff\x71\xa5\x5d\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\x94\ +\xff\xff\xff\x15\x01\xc3\xff\0\0\0\0\x05\0\xc1\xff\0\0\0\0\xbf\x12\0\0\0\0\0\0\ +\x57\x02\0\0\x08\0\0\0\x15\x02\xa0\0\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\ +\x64\xff\xff\xff\x71\xa4\x5c\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\x84\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\xa8\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\xb0\xff\0\0\0\0\xbf\xa2\0\0\0\0\0\0\x07\x02\0\0\xb8\xff\xff\xff\x15\ +\x01\x18\0\0\0\0\0\x71\xa1\x5d\xff\0\0\0\0\x15\x01\x16\0\0\0\0\0\x61\xa1\xa0\ +\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x9c\xff\0\0\0\0\x63\x12\x08\0\0\0\0\ +\0\x61\xa1\x98\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\xa1\x94\xff\0\0\0\0\x05\0\ +\x15\0\0\0\0\0\xb7\x01\0\0\x01\0\0\0\x73\x1a\x5a\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\x79\ +\xa1\x48\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\x72\0\0\0\0\0\x05\0\x4e\ +\xff\0\0\0\0\x61\xa1\x80\xff\0\0\0\0\x63\x12\x0c\0\0\0\0\0\x61\xa1\x7c\xff\0\0\ +\0\0\x63\x12\x08\0\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\x63\x12\x04\0\0\0\0\0\x61\ +\xa1\x74\xff\0\0\0\0\x63\x12\0\0\0\0\0\0\xb7\x01\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\xa2\0\0\0\0\0\0\x07\x02\0\0\ +\xa8\xff\xff\xff\x0f\x12\0\0\0\0\0\0\x71\x24\0\0\0\0\0\0\xbf\x42\0\0\0\0\0\0\ +\x67\x02\0\0\x38\0\0\0\xc7\x02\0\0\x3f\0\0\0\x5f\x32\0\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\x01\0\x24\0\0\0\x05\0\xa9\xff\0\0\0\0\x71\x61\x06\0\0\0\0\0\x71\x63\ +\x07\0\0\0\0\0\x67\x03\0\0\x08\0\0\0\x4f\x13\0\0\0\0\0\0\x67\x02\0\0\x20\0\0\0\ +\x77\x02\0\0\x20\0\0\0\x9f\x32\0\0\0\0\0\0\x63\x2a\x58\xff\0\0\0\0\xbf\xa2\0\0\ +\0\0\0\0\x07\x02\0\0\x58\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\x07\0\0\0\0\0\x71\x61\x08\0\0\0\0\0\x71\x69\x09\0\0\0\0\0\ +\x67\x09\0\0\x08\0\0\0\x4f\x19\0\0\0\0\0\0\x57\x09\0\0\xff\xff\0\0\xbf\x90\0\0\ +\0\0\0\0\x95\0\0\0\0\0\0\0\x69\x09\0\0\0\0\0\0\x05\0\xfb\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\x71\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\x3e\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\x43\x11\0\0\0\0\0\x0e\x28\0\0\0\x01\0\0\0\x4c\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\x52\x11\0\0\x01\0\0\x0f\0\0\0\0\x29\0\0\0\0\0\0\0\x07\0\0\0\x5a\ +\x11\0\0\0\0\0\x07\0\0\0\0\x68\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\x6d\x65\x2f\x71\x2f\x76\x61\x72\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\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\x65\x72\ +\x72\x29\x20\x7b\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\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\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\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\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\x73\x72\x63\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\x64\x73\x74\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\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\ +\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\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\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\x6c\x0c\0\0\x80\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\xc6\0\0\0\0\0\0\0\x37\x02\0\0\x60\x02\0\0\0\ +\x68\x08\0\x10\0\0\0\x37\x02\0\0\x91\x02\0\0\x0b\x80\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\x8c\x08\0\x50\0\0\0\ +\x37\x02\0\0\xe9\x02\0\0\x0b\x90\x08\0\x78\0\0\0\x37\x02\0\0\x29\x03\0\0\x10\ +\x98\x08\0\x80\0\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x88\0\0\0\x37\x02\0\0\x29\x03\ +\0\0\x10\x98\x08\0\x90\0\0\0\x37\x02\0\0\x42\x03\0\0\x16\x9c\x08\0\x98\0\0\0\ +\x37\x02\0\0\x42\x03\0\0\x0d\x9c\x08\0\xb0\0\0\0\x37\x02\0\0\x63\x03\0\0\x0a\ +\x10\x06\0\xd8\0\0\0\x37\x02\0\0\x9a\x03\0\0\x1f\x1c\x06\0\x30\x01\0\0\x37\x02\ +\0\0\xca\x03\0\0\x0f\xac\x04\0\x38\x01\0\0\x37\x02\0\0\xe3\x03\0\0\x0c\x2c\x04\ +\0\x48\x01\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x50\x01\0\0\x37\x02\0\0\xf7\x03\0\0\ +\x0b\x38\x04\0\x78\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\0\x88\x01\0\0\ +\x37\x02\0\0\x3d\x04\0\0\x09\x40\x04\0\x98\x01\0\0\x37\x02\0\0\x4c\x04\0\0\x0d\ +\x50\x04\0\xb0\x01\0\0\x37\x02\0\0\x4c\x04\0\0\x05\x50\x04\0\xd0\x01\0\0\x37\ +\x02\0\0\0\0\0\0\0\0\0\0\xd8\x01\0\0\x37\x02\0\0\x6a\x04\0\0\x0f\x64\x04\0\xf8\ +\x01\0\0\x37\x02\0\0\x3d\x04\0\0\x09\x7c\x04\0\x08\x02\0\0\x37\x02\0\0\x3d\x04\ +\0\0\x09\x7c\x04\0\x10\x02\0\0\x37\x02\0\0\xb4\x04\0\0\x0c\x8c\x04\0\x18\x02\0\ +\0\x37\x02\0\0\xc4\x04\0\0\x09\xc8\x04\0\x38\x02\0\0\x37\x02\0\0\xe0\x04\0\0\ +\x17\xe0\x04\0\x48\x02\0\0\x37\x02\0\0\xfb\x04\0\0\x16\xe8\x04\0\x68\x02\0\0\ +\x37\x02\0\0\xe0\x04\0\0\x17\xe0\x04\0\x70\x02\0\0\x37\x02\0\0\x19\x05\0\0\x0f\ +\xec\x04\0\x98\x02\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xa8\x02\0\0\x37\ +\x02\0\0\x5c\x05\0\0\x0d\xf4\x04\0\xb0\x02\0\0\x37\x02\0\0\x6f\x05\0\0\x22\x0c\ +\x05\0\xb8\x02\0\0\x37\x02\0\0\x6f\x05\0\0\x39\x0c\x05\0\xc8\x02\0\0\x37\x02\0\ +\0\x6f\x05\0\0\x20\x0c\x05\0\xd8\x02\0\0\x37\x02\0\0\xbd\x05\0\0\x1b\x04\x05\0\ +\xe0\x02\0\0\x37\x02\0\0\xbd\x05\0\0\x16\x04\x05\0\xe8\x02\0\0\x37\x02\0\0\xde\ +\x05\0\0\x1b\x08\x05\0\xf0\x02\0\0\x37\x02\0\0\xde\x05\0\0\x16\x08\x05\0\xf8\ +\x02\0\0\x37\x02\0\0\xff\x05\0\0\x1a\x14\x05\0\0\x03\0\0\x37\x02\0\0\x22\x06\0\ +\0\x18\x18\x05\0\x08\x03\0\0\x37\x02\0\0\x22\x06\0\0\x1c\x18\x05\0\x18\x03\0\0\ +\x37\x02\0\0\x6f\x05\0\0\x1d\x0c\x05\0\x30\x03\0\0\x37\x02\0\0\x42\x06\0\0\x17\ +\x20\x05\0\x40\x03\0\0\x37\x02\0\0\x5d\x06\0\0\x18\x28\x05\0\x70\x03\0\0\x37\ +\x02\0\0\x42\x06\0\0\x17\x20\x05\0\x78\x03\0\0\x37\x02\0\0\x7e\x06\0\0\x0f\x2c\ +\x05\0\xa0\x03\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\x34\x05\0\xb0\x03\0\0\x37\x02\0\ +\0\x5c\x05\0\0\x0d\x34\x05\0\xc0\x03\0\0\x37\x02\0\0\xc3\x06\0\0\x1d\x44\x05\0\ +\0\x04\0\0\x37\x02\0\0\xe6\x06\0\0\x1d\x48\x05\0\x40\x04\0\0\x37\x02\0\0\x09\ +\x07\0\0\x1b\x50\x05\0\x48\x04\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\x90\ +\x04\0\0\x37\x02\0\0\x44\x07\0\0\x19\xc4\x02\0\xf8\x04\0\0\x37\x02\0\0\0\0\0\0\ +\0\0\0\0\0\x05\0\0\x37\x02\0\0\x6a\x07\0\0\x0f\xd4\x02\0\x28\x05\0\0\x37\x02\0\ +\0\x5c\x05\0\0\x0d\xdc\x02\0\x38\x05\0\0\x37\x02\0\0\x5c\x05\0\0\x0d\xdc\x02\0\ +\x40\x05\0\0\x37\x02\0\0\xaf\x07\0\0\x0d\xec\x02\0\x68\x05\0\0\x37\x02\0\0\xde\ +\x07\0\0\x20\xf0\x02\0\x90\x05\0\0\x37\x02\0\0\x0a\x08\0\0\x13\xf8\x02\0\xb0\ +\x05\0\0\x37\x02\0\0\x52\x08\0\0\x11\0\x03\0\xc0\x05\0\0\x37\x02\0\0\x52\x08\0\ +\0\x11\0\x03\0\xc8\x05\0\0\x37\x02\0\0\x69\x08\0\0\x19\x10\x03\0\xd0\x05\0\0\ +\x37\x02\0\0\x69\x08\0\0\x34\x10\x03\0\xf8\x05\0\0\x37\x02\0\0\x9f\x08\0\0\x15\ +\x24\x03\0\x08\x06\0\0\x37\x02\0\0\xe0\x08\0\0\x17\x20\x03\0\x30\x06\0\0\x37\ +\x02\0\0\x17\x09\0\0\x15\x30\x03\0\x40\x06\0\0\x37\x02\0\0\x17\x09\0\0\x15\x30\ +\x03\0\x48\x06\0\0\x37\x02\0\0\x32\x09\0\0\x27\x40\x03\0\x70\x06\0\0\x37\x02\0\ +\0\x5d\x09\0\0\x27\x5c\x03\0\x80\x06\0\0\x37\x02\0\0\x8d\x09\0\0\x1c\xc0\x03\0\ +\x88\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x20\xcc\x03\0\x98\x06\0\0\x37\x02\0\0\xc9\ +\x09\0\0\x2f\xcc\x03\0\xa0\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x36\xcc\x03\0\xa8\ +\x06\0\0\x37\x02\0\0\xc9\x09\0\0\x15\xcc\x03\0\x10\x07\0\0\x37\x02\0\0\x05\x0a\ +\0\0\x43\x70\x03\0\x30\x07\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x38\x07\0\0\x37\x02\ +\0\0\x05\x0a\0\0\x17\x70\x03\0\x60\x07\0\0\x37\x02\0\0\x17\x09\0\0\x15\x78\x03\ +\0\x70\x07\0\0\x37\x02\0\0\x17\x09\0\0\x15\x78\x03\0\x78\x07\0\0\x37\x02\0\0\ +\x55\x0a\0\0\x19\x88\x03\0\x80\x07\0\0\x37\x02\0\0\x55\x0a\0\0\x15\x88\x03\0\ +\x88\x07\0\0\x37\x02\0\0\x85\x0a\0\0\x19\x90\x03\0\x90\x07\0\0\x37\x02\0\0\xb5\ +\x0a\0\0\x1b\x8c\x03\0\xc0\x07\0\0\x37\x02\0\0\xf0\x0a\0\0\x19\xa0\x03\0\xd0\ +\x07\0\0\x37\x02\0\0\xf0\x0a\0\0\x19\xa0\x03\0\xd8\x07\0\0\x37\x02\0\0\x0f\x0b\ +\0\0\x2b\xb0\x03\0\xf8\x07\0\0\x37\x02\0\0\x8d\x09\0\0\x1f\xc0\x03\0\x18\x08\0\ +\0\x37\x02\0\0\x3e\x0b\0\0\x21\xe0\x03\0\x30\x08\0\0\x37\x02\0\0\x66\x0b\0\0\ +\x20\xf0\x03\0\x38\x08\0\0\x37\x02\0\0\x66\x0b\0\0\x2c\xf0\x03\0\x48\x08\0\0\ +\x37\x02\0\0\x66\x0b\0\0\x14\xf0\x03\0\x50\x08\0\0\x37\x02\0\0\x96\x0b\0\0\x20\ +\xec\x03\0\x58\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\xa0\x08\0\0\x37\ +\x02\0\0\xbe\x0b\0\0\x38\xcc\x02\0\xc0\x08\0\0\x37\x02\0\0\xbe\x0b\0\0\x05\xcc\ +\x02\0\xd0\x08\0\0\x37\x02\0\0\x2c\x07\0\0\x05\x3c\x02\0\xe8\x08\0\0\x37\x02\0\ +\0\x2c\x07\0\0\x05\x3c\x02\0\0\x09\0\0\x37\x02\0\0\xfc\x0b\0\0\x15\x74\x05\0\ +\x10\x09\0\0\x37\x02\0\0\xfc\x0b\0\0\x1a\x74\x05\0\x28\x09\0\0\x37\x02\0\0\x30\ +\x0c\0\0\x0d\x78\x05\0\x48\x09\0\0\x37\x02\0\0\x5a\x0c\0\0\x1a\x7c\x05\0\x58\ +\x09\0\0\x37\x02\0\0\x78\x0c\0\0\x1b\x84\x05\0\x78\x09\0\0\x37\x02\0\0\x5a\x0c\ +\0\0\x1a\x7c\x05\0\x80\x09\0\0\x37\x02\0\0\x9c\x0c\0\0\x13\x88\x05\0\xa0\x09\0\ +\0\x37\x02\0\0\x52\x08\0\0\x11\x90\x05\0\xb0\x09\0\0\x37\x02\0\0\x52\x08\0\0\ +\x11\x90\x05\0\xb8\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xd8\x09\0\0\x37\x02\0\0\ +\xed\x0c\0\0\x15\x38\x06\0\xe0\x09\0\0\x37\x02\0\0\xed\x0c\0\0\x09\x38\x06\0\ +\xe8\x09\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\x38\x0a\0\0\x37\x02\0\0\x0c\x0d\0\0\ +\x19\x3c\x06\0\x40\x0a\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\x3c\x06\0\x60\x0a\0\0\ +\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\xa8\x0a\0\0\x37\x02\0\0\x6b\x0d\0\0\x1c\ +\xd4\x06\0\xb0\x0a\0\0\x37\x02\0\0\x6b\x0d\0\0\x10\xd4\x06\0\xb8\x0a\0\0\x37\ +\x02\0\0\0\0\0\0\0\0\0\0\x08\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\x19\xd8\x06\0\x10\ +\x0b\0\0\x37\x02\0\0\x0c\x0d\0\0\x20\xd8\x06\0\x30\x0b\0\0\x37\x02\0\0\x91\x0d\ +\0\0\x2d\xe4\x06\0\x40\x0b\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\xe4\x06\0\x50\x0b\0\ +\0\x37\x02\0\0\x91\x0d\0\0\x2d\xe4\x06\0\x80\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\ +\x2d\x10\x07\0\x90\x0b\0\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x10\x07\0\xa0\x0b\0\0\ +\x37\x02\0\0\xc0\x0d\0\0\x2d\x10\x07\0\xc8\x0b\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\ +\xa4\x01\0\x90\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x78\x06\0\x98\x0c\0\0\x37\ +\x02\0\0\xef\x0d\0\0\x27\x78\x06\0\xc0\x0c\0\0\x37\x02\0\0\x18\x0e\0\0\x27\xb4\ +\x06\0\xc8\x0c\0\0\x37\x02\0\0\x18\x0e\0\0\x14\xb4\x06\0\xd0\x0c\0\0\x37\x02\0\ +\0\x2e\x0d\0\0\x05\xa4\x01\0\xe0\x0c\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\ +\xf8\x0c\0\0\x37\x02\0\0\xef\x0d\0\0\x20\x54\x07\0\0\x0d\0\0\x37\x02\0\0\xef\ +\x0d\0\0\x27\x54\x07\0\x20\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\x60\x07\0\x30\ +\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\x60\x07\0\x40\x0d\0\0\x37\x02\0\0\x91\x0d\ +\0\0\x2d\x60\x07\0\x70\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\x2d\x8c\x07\0\x80\x0d\0\ +\0\x37\x02\0\0\xc0\x0d\0\0\x1d\x8c\x07\0\x90\x0d\0\0\x37\x02\0\0\xc0\x0d\0\0\ +\x2d\x8c\x07\0\xb8\x0d\0\0\x37\x02\0\0\x61\x0e\0\0\x27\xd8\x07\0\xc8\x0d\0\0\ +\x37\x02\0\0\x61\x0e\0\0\x14\xd8\x07\0\xd0\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x2d\ +\xdc\x07\0\xe0\x0d\0\0\x37\x02\0\0\x91\x0d\0\0\x1d\xdc\x07\0\xf0\x0d\0\0\x37\ +\x02\0\0\x91\x0d\0\0\x2d\xdc\x07\0\x20\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\ +\x01\0\x70\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x17\xa4\x01\0\x80\x0e\0\0\x37\x02\0\ +\0\xc0\x0d\0\0\x2d\x08\x08\0\x98\x0e\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\ +\xe0\x0e\0\0\x37\x02\0\0\xaa\x0e\0\0\x1a\xac\x05\0\xf0\x0e\0\0\x37\x02\0\0\xc8\ +\x0e\0\0\x1b\xb4\x05\0\0\x0f\0\0\x37\x02\0\0\xaa\x0e\0\0\x1a\xac\x05\0\x08\x0f\ +\0\0\x37\x02\0\0\xec\x0e\0\0\x13\xb8\x05\0\x28\x0f\0\0\x37\x02\0\0\x52\x08\0\0\ +\x11\xc0\x05\0\x38\x0f\0\0\x37\x02\0\0\x52\x08\0\0\x11\xc0\x05\0\x48\x0f\0\0\ +\x37\x02\0\0\x2e\x0d\0\0\x05\xa4\x01\0\x80\x0f\0\0\x37\x02\0\0\x2e\x0d\0\0\x05\ +\xa4\x01\0\x90\x0f\0\0\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\x01\0\x98\x0f\0\0\x37\ +\x02\0\0\x7f\x0f\0\0\x23\xd0\x01\0\xb0\x0f\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xb8\ +\x0f\0\0\x37\x02\0\0\xb3\x0f\0\0\x1b\xe0\x01\0\xd8\x0f\0\0\x37\x02\0\0\xda\x0f\ +\0\0\x11\xf4\x01\0\xf0\x0f\0\0\x37\x02\0\0\x03\x10\0\0\x19\xe4\x01\0\x08\x10\0\ +\0\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x20\x10\0\0\x37\x02\0\0\x31\x10\0\0\ +\x2d\x08\x02\0\x28\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\x68\x10\0\0\ +\x37\x02\0\0\x31\x10\0\0\x27\x08\x02\0\x70\x10\0\0\x37\x02\0\0\x31\x10\0\0\x2d\ +\x08\x02\0\x78\x10\0\0\x37\x02\0\0\xda\x0f\0\0\x11\xf4\x01\0\xa0\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\xf0\x10\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\x58\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\xa8\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\x30\x12\0\0\x37\x02\0\0\x31\x10\0\0\x27\x08\ +\x02\0\x38\x12\0\0\x37\x02\0\0\x31\x10\0\0\x2d\x08\x02\0\x40\x12\0\0\x37\x02\0\ +\0\x3d\x0f\0\0\x3d\xdc\x01\0\x50\x12\0\0\x37\x02\0\0\x3d\x0f\0\0\x05\xdc\x01\0\ +\x60\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x2e\xb0\x08\0\x80\x12\0\0\x37\x02\0\0\x7d\ +\x10\0\0\x24\xb0\x08\0\x98\x12\0\0\x37\x02\0\0\x7d\x10\0\0\x13\xb0\x08\0\xa8\ +\x12\0\0\x37\x02\0\0\0\0\0\0\0\0\0\0\xb0\x12\0\0\x37\x02\0\0\xbc\x10\0\0\x15\ +\xbc\x08\0\xc8\x12\0\0\x37\x02\0\0\x04\x11\0\0\x11\xc8\x08\0\xd0\x12\0\0\x37\ +\x02\0\0\0\0\0\0\0\0\0\0\xf0\x12\0\0\x37\x02\0\0\x1d\x11\0\0\x01\xec\x08\0\x08\ +\x13\0\0\x37\x02\0\0\x1f\x11\0\0\x18\xcc\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\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\x94\x01\0\0\0\0\x03\0\xf0\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x75\x01\0\0\0\0\x03\ +\0\xa8\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xcd\x01\0\0\0\0\x03\0\xd0\x12\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\xbd\0\0\0\0\0\x03\0\xc8\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\ +\x01\0\0\0\0\x03\0\x18\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\x01\0\0\0\0\x03\0\ +\x28\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x24\x01\0\0\0\0\x03\0\xd8\x09\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x2c\x01\0\0\0\0\x03\0\xd8\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf7\ +\x01\0\0\0\0\x03\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x30\x02\0\0\0\0\x03\0\xe8\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe6\x01\0\0\0\0\x03\0\x88\x04\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\xc5\x01\0\0\0\0\x03\0\xf0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\x01\ +\0\0\0\0\x03\0\x60\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xb5\x01\0\0\0\0\x03\0\x18\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\x01\0\0\0\0\x03\0\x30\x08\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x84\x01\0\0\0\0\x03\0\x28\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x8c\x01\ +\0\0\0\0\x03\0\x10\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x28\x02\0\0\0\0\x03\0\x80\ +\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xde\x01\0\0\0\0\x03\0\xf8\x06\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x0c\x01\0\0\0\0\x03\0\xf0\x07\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4c\x01\ +\0\0\0\0\x03\0\xd0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\x01\0\0\0\0\x03\0\x98\ +\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd4\0\0\0\0\0\x03\0\xd8\x08\0\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\x20\x02\0\0\0\0\x03\0\xf0\x08\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xfc\0\0\0\ +\0\0\x03\0\xd8\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x44\x01\0\0\0\0\x03\0\xb8\x09\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\xf4\0\0\0\0\0\x03\0\xa8\x0a\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\xad\x01\0\0\0\0\x03\0\x90\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd6\x01\0\0\0\0\ +\x03\0\x60\x0a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x38\x02\0\0\0\0\x03\0\x88\x0f\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\x18\x02\0\0\0\0\x03\0\xf8\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\0\xa5\x01\0\0\0\0\x03\0\x68\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x5c\x01\0\0\0\0\ +\x03\0\x80\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1c\x01\0\0\0\0\x03\0\xb8\x0b\0\0\0\ +\0\0\0\0\0\0\0\0\0\0\0\xcc\0\0\0\0\0\x03\0\xc8\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ +\xec\0\0\0\0\0\x03\0\xc0\x0b\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x3c\x01\0\0\0\0\x03\0\ +\xc0\x0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\x02\0\0\0\0\x03\0\xb8\x0d\0\0\0\0\0\0\ +\0\0\0\0\0\0\0\0\x7c\x01\0\0\0\0\x03\0\x58\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x34\ +\x01\0\0\0\0\x03\0\x70\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe4\0\0\0\0\0\x03\0\xa8\ +\x0d\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9d\x01\0\0\0\0\x03\0\x08\x0e\0\0\0\0\0\0\0\0\ +\0\0\0\0\0\0\x54\x01\0\0\0\0\x03\0\x20\x0e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xdc\0\0\ +\0\0\0\x03\0\x48\x0f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc4\0\0\0\0\0\x03\0\x80\x0f\0\ +\0\0\0\0\0\0\0\0\0\0\0\0\0\x07\x02\0\0\0\0\x03\0\xa8\x0f\0\0\0\0\0\0\0\0\0\0\0\ +\0\0\0\xee\x01\0\0\0\0\x03\0\x60\x12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x6c\x01\0\0\0\ +\0\x03\0\x08\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\x18\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\x33\0\0\0\x50\0\0\0\0\0\0\0\ +\x01\0\0\0\x34\0\0\0\xb0\x12\0\0\0\0\0\0\x01\0\0\0\x35\0\0\0\x20\x05\0\0\0\0\0\ +\0\x04\0\0\0\x33\0\0\0\x2c\x05\0\0\0\0\0\0\x04\0\0\0\x34\0\0\0\x38\x05\0\0\0\0\ +\0\0\x04\0\0\0\x35\0\0\0\x50\x05\0\0\0\0\0\0\x04\0\0\0\x36\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\x39\x3a\x3b\ +\x3c\x3d\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\x39\x39\0\x4c\x42\x42\x30\x5f\x37\x39\0\x4c\x42\x42\x30\x5f\x34\ +\x39\0\x4c\x42\x42\x30\x5f\x39\x38\0\x4c\x42\x42\x30\x5f\x38\x38\0\x4c\x42\x42\ +\x30\x5f\x37\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\x37\x37\0\x4c\x42\x42\x30\x5f\x35\x37\0\x4c\ +\x42\x42\x30\x5f\x31\x37\0\x4c\x42\x42\x30\x5f\x38\x36\0\x4c\x42\x42\x30\x5f\ +\x36\x36\0\x4c\x42\x42\x30\x5f\x35\x36\0\x4c\x42\x42\x30\x5f\x34\x36\0\x4c\x42\ +\x42\x30\x5f\x39\x35\0\x4c\x42\x42\x30\x5f\x37\x35\0\x4c\x42\x42\x30\x5f\x34\ +\x35\0\x4c\x42\x42\x30\x5f\x31\x30\x35\0\x4c\x42\x42\x30\x5f\x34\0\x4c\x42\x42\ +\x30\x5f\x38\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\x39\x33\0\x4c\x42\x42\ +\x30\x5f\x37\x33\0\x4c\x42\x42\x30\x5f\x36\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\x36\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\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\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\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\x6d\x49\0\0\0\0\ +\0\0\x41\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\x18\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\x88\x3c\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\x58\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\ +\xd0\x13\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\xd8\x13\0\0\0\0\ +\0\0\xe1\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\xb8\x3c\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\xbc\x2a\0\0\0\0\0\0\xa0\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\xf8\x3c\0\0\0\0\0\0\x70\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\x68\x49\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\x60\x37\0\0\0\0\0\0\x28\x05\0\0\0\0\0\0\x01\0\0\0\x32\0\0\0\x08\ +\0\0\0\0\0\0\0\x18\0\0\0\0\0\0\0"; + + *sz = sizeof(data) - 1; + return (const void *)data; +} + +#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..bf3d9b6451 100644 --- a/ebpf/trace-events +++ b/ebpf/trace-events @@ -1,4 +1,10 @@ -# 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" +ebpf_rss_load(void *ctx, int progfd, int cfgfd, int toepfd, int indirfd) "ctx=%p program-fd=%d config-fd=%d toeplitz-fd=%d indirection-fd=%d" +ebpf_rss_load_error(void *ctx) "ctx=%p" +ebpf_rss_mmap(void *ctx, void *cfgptr, void *toepptr, void *indirptr) "ctx=%p config-ptr=%p toeplitz-ptr=%p indirection-ptr=%p" +ebpf_rss_mmap_error(void *ctx, const char *object) "ctx=%p object=%s" +ebpf_rss_open_error(void *ctx) "ctx=%p" +ebpf_rss_set_data(void *ctx, void *cfgptr, void *toepptr, void *indirptr) "ctx=%p config-ptr=%p toeplitz-ptr=%p indirection-ptr=%p" +ebpf_rss_unload(void *ctx) "rss unload ctx=%p" diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 247400031c..cc6e06b976 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; @@ -256,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. */ @@ -287,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)) { @@ -1181,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 * @@ -1294,7 +1373,6 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, break; default: g_assert_not_reached(); - break; } switch (b->cls) { case float_class_normal: @@ -1307,7 +1385,6 @@ static FloatPartsN *partsN(minmax)(FloatPartsN *a, FloatPartsN *b, break; default: g_assert_not_reached(); - break; } } diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index 1610472cfc..9bca03c4ae 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -151,8 +151,8 @@ static void parts64_default_nan(FloatParts64 *p, float_status *status) #else /* * 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. + * S390, SH4, TriCore, and Xtensa. Our other supported targets + * do not have floating-point. */ if (snan_bit_is_one(status)) { /* set all bits other than msb */ @@ -390,107 +390,80 @@ 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) || \ - defined(TARGET_LOONGARCH64) || defined(TARGET_S390X) - /* ARM mandated NaN propagation rules (see FPProcessNaNs()), take - * the first of: - * 1. A if it is signaling - * 2. B if it is signaling - * 3. A (quiet) - * 4. B (quiet) - * A signaling NaN is always quietened before returning it. - */ - /* According to MIPS specifications, if one of the two operands is - * a sNaN, a new qNaN has to be generated. This is done in - * floatXX_silence_nan(). For qNaN inputs the specifications - * says: "When possible, this QNaN result is one of the operand QNaN - * values." In practice it seems that most implementations choose - * the first operand if both operands are qNaN. In short this gives - * the following rules: - * 1. A if it is signaling - * 2. B if it is signaling - * 3. A (quiet) - * 4. B (quiet) - * A signaling NaN is always silenced before returning it. - */ - if (is_snan(a_cls)) { - return 0; - } else if (is_snan(b_cls)) { - return 1; - } else if (is_qnan(a_cls)) { - return 0; - } else { - return 1; - } -#elif defined(TARGET_PPC) || defined(TARGET_M68K) - /* PowerPC propagation rules: - * 1. A if it sNaN or qNaN - * 2. B if it sNaN or qNaN - * A signaling NaN is always silenced before returning it. - */ - /* M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL - * 3.4 FLOATING-POINT INSTRUCTION DETAILS - * If either operand, but not both operands, of an operation is a - * nonsignaling NaN, then that NaN is returned as the result. If both - * operands are nonsignaling NaNs, then the destination operand - * nonsignaling NaN is returned as the result. - * If either operand to an operation is a signaling NaN (SNaN), then the - * SNaN bit is set in the FPSR EXC byte. If the SNaN exception enable bit - * is set in the FPCR ENABLE byte, then the exception is taken and the - * destination is not modified. If the SNaN exception enable bit is not - * set, setting the SNaN bit in the operand to a one converts the SNaN to - * a nonsignaling NaN. The operation then continues as described in the - * preceding paragraph for nonsignaling NaNs. - */ - if (is_nan(a_cls)) { - return 0; - } else { - return 1; - } -#elif defined(TARGET_XTENSA) /* - * Xtensa has two NaN propagation modes. - * Which one is active is controlled by float_status::use_first_nan. + * We guarantee not to require the target to tell us how to + * pick a NaN if we're always returning the default NaN. + * But if we're not in default-NaN mode then the target must + * specify via set_float_2nan_prop_rule(). */ - if (status->use_first_nan) { + assert(!status->default_nan_mode); + + switch (status->float_2nan_prop_rule) { + case float_2nan_prop_s_ab: + if (is_snan(a_cls)) { + return 0; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_qnan(a_cls)) { + return 0; + } else { + return 1; + } + break; + case float_2nan_prop_s_ba: + if (is_snan(b_cls)) { + return 1; + } else if (is_snan(a_cls)) { + return 0; + } else if (is_qnan(b_cls)) { + return 1; + } else { + return 0; + } + break; + case float_2nan_prop_ab: if (is_nan(a_cls)) { return 0; } else { return 1; } - } else { + break; + case float_2nan_prop_ba: if (is_nan(b_cls)) { return 1; } else { return 0; } - } -#else - /* This implements x87 NaN propagation rules: - * SNaN + QNaN => return the QNaN - * two SNaNs => return the one with the larger significand, silenced - * two QNaNs => return the one with the larger significand - * SNaN and a non-NaN => return the SNaN, silenced - * QNaN and a non-NaN => return the QNaN - * - * If we get down to comparing significands and they are the same, - * return the NaN with the positive sign bit (if any). - */ - if (is_snan(a_cls)) { - if (is_snan(b_cls)) { - return aIsLargerSignificand ? 0 : 1; - } - return is_qnan(b_cls) ? 1 : 0; - } else if (is_qnan(a_cls)) { - if (is_snan(b_cls) || !is_qnan(b_cls)) { - return 0; + break; + case float_2nan_prop_x87: + /* + * This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + if (is_snan(a_cls)) { + if (is_snan(b_cls)) { + return aIsLargerSignificand ? 0 : 1; + } + return is_qnan(b_cls) ? 1 : 0; + } else if (is_qnan(a_cls)) { + if (is_snan(b_cls) || !is_qnan(b_cls)) { + return 0; + } else { + return aIsLargerSignificand ? 0 : 1; + } } else { - return aIsLargerSignificand ? 0 : 1; + return 1; } - } else { - return 1; + default: + g_assert_not_reached(); } -#endif } /*---------------------------------------------------------------------------- @@ -624,6 +597,45 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, float_raise(float_flag_invalid | float_flag_invalid_imz, status); } return 3; /* default NaN */ +#elif defined(TARGET_S390X) + if (infzero) { + float_raise(float_flag_invalid | float_flag_invalid_imz, status); + return 3; + } + + if (is_snan(a_cls)) { + return 0; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_snan(c_cls)) { + return 2; + } else if (is_qnan(a_cls)) { + return 0; + } else if (is_qnan(b_cls)) { + return 1; + } else { + return 2; + } +#elif defined(TARGET_SPARC) + /* For (inf,0,nan) return c. */ + if (infzero) { + float_raise(float_flag_invalid | float_flag_invalid_imz, status); + return 2; + } + /* Prefer SNaN over QNaN, order C, B, A. */ + if (is_snan(c_cls)) { + return 2; + } else if (is_snan(b_cls)) { + return 1; + } else if (is_snan(a_cls)) { + return 0; + } else if (is_qnan(c_cls)) { + return 2; + } else if (is_qnan(b_cls)) { + return 1; + } else { + return 0; + } #elif defined(TARGET_XTENSA) /* * For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 108f9cb224..027a8e576d 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -517,6 +517,7 @@ 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; @@ -526,6 +527,7 @@ typedef struct { int frac_size; int frac_shift; bool arm_althp; + bool m68k_denormal; uint64_t round_mask; } FloatFmt; @@ -576,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. */ @@ -593,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, @@ -623,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; @@ -650,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; @@ -852,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) @@ -3113,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) { @@ -3379,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); @@ -3394,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); @@ -3409,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 */ @@ -3503,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) { @@ -3728,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); @@ -3743,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); @@ -3898,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); @@ -3913,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 = { }; @@ -4108,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); @@ -4123,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; diff --git a/fsdev/9p-iov-marshal.c b/fsdev/9p-iov-marshal.c index a1c9beddd2..0c5a1a0fa2 100644 --- a/fsdev/9p-iov-marshal.c +++ b/fsdev/9p-iov-marshal.c @@ -84,9 +84,12 @@ ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset, break; } case 'w': { - uint16_t val, *valp; + uint16_t val = 0, *valp; valp = va_arg(ap, uint16_t *); copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); + if (copied <= 0) { + break; + } if (bswap) { *valp = le16_to_cpu(val); } else { @@ -95,9 +98,12 @@ ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset, break; } case 'd': { - uint32_t val, *valp; + uint32_t val = 0, *valp; valp = va_arg(ap, uint32_t *); copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); + if (copied <= 0) { + break; + } if (bswap) { *valp = le32_to_cpu(val); } else { @@ -106,9 +112,12 @@ ssize_t v9fs_iov_vunmarshal(struct iovec *out_sg, int out_num, size_t offset, break; } case 'q': { - uint64_t val, *valp; + uint64_t val = 0, *valp; valp = va_arg(ap, uint64_t *); copied = v9fs_unpack(&val, out_sg, out_num, offset, sizeof(val)); + if (copied <= 0) { + break; + } if (bswap) { *valp = le64_to_cpu(val); } else { diff --git a/fsdev/meson.build b/fsdev/meson.build index b632b66348..c751d8cb62 100644 --- a/fsdev/meson.build +++ b/fsdev/meson.build @@ -1,18 +1,10 @@ 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 have_virtfs_proxy_helper - executable('virtfs-proxy-helper', - files('virtfs-proxy-helper.c', '9p-marshal.c', '9p-iov-marshal.c'), - dependencies: [qemuutil, libattr, libcap_ng], - install: true, - install_dir: get_option('libexecdir')) +if host_os in ['linux', 'darwin'] + system_ss.add_all(fsdev_ss) endif 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..57877dad0a 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -89,17 +89,6 @@ static FsDriverTable FsDrivers[] = { NULL }, }, - { - .name = "proxy", - .ops = &proxy_ops, - .opts = (const char * []) { - COMMON_FS_DRIVER_OPTIONS, - "socket", - "sock_fd", - "writeout", - NULL - }, - }, }; static int validate_opt(void *opaque, const char *name, const char *value, diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h index 52a5397770..731f1406a8 100644 --- a/fsdev/qemu-fsdev.h +++ b/fsdev/qemu-fsdev.h @@ -18,5 +18,4 @@ int qemu_fsdev_add(QemuOpts *opts, Error **errp); FsDriverEntry *get_fsdev_fsentry(char *id); extern FileOperations local_ops; extern FileOperations synth_ops; -extern FileOperations proxy_ops; #endif diff --git a/fsdev/virtfs-proxy-helper.c b/fsdev/virtfs-proxy-helper.c deleted file mode 100644 index d9511f429c..0000000000 --- a/fsdev/virtfs-proxy-helper.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Helper for QEMU Proxy FS Driver - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_LINUX_MAGIC_H -#include -#endif -#include -#include "qemu/sockets.h" -#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" - -#ifndef XFS_SUPER_MAGIC -#define XFS_SUPER_MAGIC 0x58465342 -#endif -#ifndef EXT2_SUPER_MAGIC -#define EXT2_SUPER_MAGIC 0xEF53 -#endif -#ifndef REISERFS_SUPER_MAGIC -#define REISERFS_SUPER_MAGIC 0x52654973 -#endif -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -static const struct option helper_opts[] = { - {"fd", required_argument, NULL, 'f'}, - {"path", required_argument, NULL, 'p'}, - {"nodaemon", no_argument, NULL, 'n'}, - {"socket", required_argument, NULL, 's'}, - {"uid", required_argument, NULL, 'u'}, - {"gid", required_argument, NULL, 'g'}, - {}, -}; - -static bool is_daemon; -static bool get_version; /* IOC getversion IOCTL supported */ -static char *prog_name; - -static void G_GNUC_PRINTF(2, 3) do_log(int loglevel, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - if (is_daemon) { - vsyslog(LOG_CRIT, format, ap); - } else { - vfprintf(stderr, format, ap); - } - va_end(ap); -} - -static void do_perror(const char *string) -{ - if (is_daemon) { - syslog(LOG_CRIT, "%s:%s", string, strerror(errno)); - } else { - fprintf(stderr, "%s:%s\n", string, strerror(errno)); - } -} - -static int init_capabilities(void) -{ - /* helper needs following capabilities only */ - int cap_list[] = { - CAP_CHOWN, - CAP_DAC_OVERRIDE, - CAP_FOWNER, - CAP_FSETID, - CAP_SETGID, - CAP_MKNOD, - CAP_SETUID, - }; - int i; - - capng_clear(CAPNG_SELECT_BOTH); - for (i = 0; i < ARRAY_SIZE(cap_list); i++) { - if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, - cap_list[i]) < 0) { - do_perror("capng_update"); - return -1; - } - } - if (capng_apply(CAPNG_SELECT_BOTH) < 0) { - do_perror("capng_apply"); - return -1; - } - - /* Prepare effective set for setugid. */ - for (i = 0; i < ARRAY_SIZE(cap_list); i++) { - if (cap_list[i] == CAP_DAC_OVERRIDE) { - continue; - } - - if (capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, - cap_list[i]) < 0) { - do_perror("capng_update"); - return -1; - } - } - return 0; -} - -static int socket_read(int sockfd, void *buff, ssize_t size) -{ - ssize_t retval, total = 0; - - while (size) { - retval = read(sockfd, buff, size); - if (retval == 0) { - return -EIO; - } - if (retval < 0) { - if (errno == EINTR) { - continue; - } - return -errno; - } - size -= retval; - buff += retval; - total += retval; - } - return total; -} - -static int socket_write(int sockfd, void *buff, ssize_t size) -{ - ssize_t retval, total = 0; - - while (size) { - retval = write(sockfd, buff, size); - if (retval < 0) { - if (errno == EINTR) { - continue; - } - return -errno; - } - size -= retval; - buff += retval; - total += retval; - } - return total; -} - -static int read_request(int sockfd, struct iovec *iovec, ProxyHeader *header) -{ - int retval; - - /* - * read the request header. - */ - iovec->iov_len = 0; - retval = socket_read(sockfd, iovec->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - iovec->iov_len = PROXY_HDR_SZ; - retval = proxy_unmarshal(iovec, 0, "dd", &header->type, &header->size); - if (retval < 0) { - return retval; - } - /* - * We can't process message.size > PROXY_MAX_IO_SZ. - * Treat it as fatal error - */ - if (header->size > PROXY_MAX_IO_SZ) { - return -ENOBUFS; - } - retval = socket_read(sockfd, iovec->iov_base + PROXY_HDR_SZ, header->size); - if (retval < 0) { - return retval; - } - iovec->iov_len += header->size; - return 0; -} - -static int send_fd(int sockfd, int fd) -{ - struct msghdr msg; - struct iovec iov; - int retval, data; - struct cmsghdr *cmsg; - union MsgControl msg_control; - - iov.iov_base = &data; - iov.iov_len = sizeof(data); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - /* No ancillary data on error */ - if (fd < 0) { - /* fd is really negative errno if the request failed */ - data = fd; - } else { - data = V9FS_FD_VALID; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); - - cmsg = &msg_control.cmsg; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); - } - - do { - retval = sendmsg(sockfd, &msg, 0); - } while (retval < 0 && errno == EINTR); - if (fd >= 0) { - close(fd); - } - if (retval < 0) { - return retval; - } - return 0; -} - -static int send_status(int sockfd, struct iovec *iovec, int status) -{ - ProxyHeader header; - int retval, msg_size; - - if (status < 0) { - header.type = T_ERROR; - } else { - header.type = T_SUCCESS; - } - header.size = sizeof(status); - /* - * marshal the return status. We don't check error. - * because we are sure we have enough space for the status - */ - msg_size = proxy_marshal(iovec, 0, "ddd", header.type, - header.size, status); - if (msg_size < 0) { - return msg_size; - } - retval = socket_write(sockfd, iovec->iov_base, msg_size); - if (retval < 0) { - return retval; - } - return 0; -} - -/* - * from man 7 capabilities, section - * Effect of User ID Changes on Capabilities: - * If the effective user ID is changed from nonzero to 0, then the permitted - * set is copied to the effective set. If the effective user ID is changed - * from 0 to nonzero, then all capabilities are are cleared from the effective - * set. - * - * The setfsuid/setfsgid man pages warn that changing the effective user ID may - * expose the program to unwanted signals, but this is not true anymore: for an - * unprivileged (without CAP_KILL) program to send a signal, the real or - * effective user ID of the sending process must equal the real or saved user - * ID of the target process. Even when dropping privileges, it is enough to - * keep the saved UID to a "privileged" value and virtfs-proxy-helper won't - * be exposed to signals. So just use setresuid/setresgid. - */ -static int setugid(int uid, int gid, int *suid, int *sgid) -{ - int retval; - - *suid = geteuid(); - *sgid = getegid(); - - if (setresgid(-1, gid, *sgid) == -1) { - return -errno; - } - - if (setresuid(-1, uid, *suid) == -1) { - retval = -errno; - goto err_sgid; - } - - if (uid == 0 && gid == 0) { - /* Linux has already copied the permitted set to the effective set. */ - return 0; - } - - /* - * All capabilities have been cleared from the effective set. However - * we still need DAC_OVERRIDE because we don't change supplementary - * group ids, and hence may be subject to DAC rules. init_capabilities - * left the set of capabilities that we want in libcap-ng's state. - */ - if (capng_apply(CAPNG_SELECT_CAPS) < 0) { - retval = -errno; - do_perror("capng_apply"); - goto err_suid; - } - return 0; - -err_suid: - if (setresuid(-1, *suid, *suid) == -1) { - abort(); - } -err_sgid: - if (setresgid(-1, *sgid, *sgid) == -1) { - abort(); - } - return retval; -} - -/* - * This is used to reset the ugid back with the saved values - * There is nothing much we can do checking error values here. - */ -static void resetugid(int suid, int sgid) -{ - if (setresgid(-1, sgid, sgid) == -1) { - abort(); - } - if (setresuid(-1, suid, suid) == -1) { - abort(); - } -} - -/* - * 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 - * 2) Response or error status - * This function should be called with marshaled response - * send_response constructs header part and error part only. - * send response sends {ProxyHeader,Response} if the request was success - * otherwise sends {ProxyHeader,error status} - */ -static int send_response(int sock, struct iovec *iovec, int size) -{ - int retval; - ProxyHeader header; - - /* - * If response size exceeds available iovec->iov_len, - * we return ENOBUFS - */ - if (size > PROXY_MAX_IO_SZ) { - size = -ENOBUFS; - } - - if (size < 0) { - /* - * In case of error we would not have got the error encoded - * already so encode the error here. - */ - header.type = T_ERROR; - header.size = sizeof(size); - proxy_marshal(iovec, PROXY_HDR_SZ, "d", size); - } else { - header.type = T_SUCCESS; - header.size = size; - } - proxy_marshal(iovec, 0, "dd", header.type, header.size); - retval = socket_write(sock, iovec->iov_base, header.size + PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - return 0; -} - -/* - * gets generation number - * returns -errno on failure and sizeof(generation number) on success - */ -static int do_getversion(struct iovec *iovec, struct iovec *out_iovec) -{ - uint64_t version; - int retval = -ENOTTY; -#ifdef FS_IOC_GETVERSION - int fd; - V9fsString path; -#endif - - - /* no need to issue ioctl */ - if (!get_version) { - version = 0; - retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version); - return retval; - } -#ifdef FS_IOC_GETVERSION - retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path); - if (retval < 0) { - return retval; - } - - fd = open(path.data, O_RDONLY); - if (fd < 0) { - retval = -errno; - goto err_out; - } - if (ioctl(fd, FS_IOC_GETVERSION, &version) < 0) { - retval = -errno; - } else { - retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "q", version); - } - close(fd); -err_out: - v9fs_string_free(&path); -#endif - return retval; -} - -static int do_getxattr(int type, struct iovec *iovec, struct iovec *out_iovec) -{ - int size = 0, offset, retval; - V9fsString path, name, xattr; - - v9fs_string_init(&xattr); - v9fs_string_init(&path); - retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "ds", &size, &path); - if (retval < 0) { - return retval; - } - offset = PROXY_HDR_SZ + retval; - - if (size) { - xattr.data = g_malloc(size); - xattr.size = size; - } - switch (type) { - case T_LGETXATTR: - v9fs_string_init(&name); - retval = proxy_unmarshal(iovec, offset, "s", &name); - if (retval > 0) { - retval = lgetxattr(path.data, name.data, xattr.data, size); - if (retval < 0) { - retval = -errno; - } else { - xattr.size = retval; - } - } - v9fs_string_free(&name); - break; - case T_LLISTXATTR: - retval = llistxattr(path.data, xattr.data, size); - if (retval < 0) { - retval = -errno; - } else { - xattr.size = retval; - } - break; - } - if (retval < 0) { - goto err_out; - } - - if (!size) { - proxy_marshal(out_iovec, PROXY_HDR_SZ, "d", retval); - retval = sizeof(retval); - } else { - retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &xattr); - } -err_out: - v9fs_string_free(&xattr); - v9fs_string_free(&path); - return retval; -} - -static void stat_to_prstat(ProxyStat *pr_stat, struct stat *stat) -{ - memset(pr_stat, 0, sizeof(*pr_stat)); - pr_stat->st_dev = stat->st_dev; - pr_stat->st_ino = stat->st_ino; - pr_stat->st_nlink = stat->st_nlink; - pr_stat->st_mode = stat->st_mode; - pr_stat->st_uid = stat->st_uid; - pr_stat->st_gid = stat->st_gid; - pr_stat->st_rdev = stat->st_rdev; - pr_stat->st_size = stat->st_size; - pr_stat->st_blksize = stat->st_blksize; - pr_stat->st_blocks = stat->st_blocks; - pr_stat->st_atim_sec = stat->st_atim.tv_sec; - pr_stat->st_atim_nsec = stat->st_atim.tv_nsec; - pr_stat->st_mtim_sec = stat->st_mtim.tv_sec; - pr_stat->st_mtim_nsec = stat->st_mtim.tv_nsec; - pr_stat->st_ctim_sec = stat->st_ctim.tv_sec; - pr_stat->st_ctim_nsec = stat->st_ctim.tv_nsec; -} - -static void statfs_to_prstatfs(ProxyStatFS *pr_stfs, struct statfs *stfs) -{ - memset(pr_stfs, 0, sizeof(*pr_stfs)); - pr_stfs->f_type = stfs->f_type; - pr_stfs->f_bsize = stfs->f_bsize; - pr_stfs->f_blocks = stfs->f_blocks; - pr_stfs->f_bfree = stfs->f_bfree; - pr_stfs->f_bavail = stfs->f_bavail; - pr_stfs->f_files = stfs->f_files; - pr_stfs->f_ffree = stfs->f_ffree; - pr_stfs->f_fsid[0] = stfs->f_fsid.__val[0]; - pr_stfs->f_fsid[1] = stfs->f_fsid.__val[1]; - pr_stfs->f_namelen = stfs->f_namelen; - pr_stfs->f_frsize = stfs->f_frsize; -} - -/* - * Gets stat/statfs information and packs in out_iovec structure - * on success returns number of bytes packed in out_iovec structure - * otherwise returns -errno - */ -static int do_stat(int type, struct iovec *iovec, struct iovec *out_iovec) -{ - int retval; - V9fsString path; - ProxyStat pr_stat; - ProxyStatFS pr_stfs; - struct stat st_buf; - struct statfs stfs_buf; - - v9fs_string_init(&path); - retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "s", &path); - if (retval < 0) { - return retval; - } - - switch (type) { - case T_LSTAT: - retval = lstat(path.data, &st_buf); - if (retval < 0) { - retval = -errno; - } else { - stat_to_prstat(&pr_stat, &st_buf); - retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, - "qqqdddqqqqqqqqqq", pr_stat.st_dev, - pr_stat.st_ino, pr_stat.st_nlink, - pr_stat.st_mode, pr_stat.st_uid, - pr_stat.st_gid, pr_stat.st_rdev, - pr_stat.st_size, pr_stat.st_blksize, - pr_stat.st_blocks, - pr_stat.st_atim_sec, pr_stat.st_atim_nsec, - pr_stat.st_mtim_sec, pr_stat.st_mtim_nsec, - pr_stat.st_ctim_sec, pr_stat.st_ctim_nsec); - } - break; - case T_STATFS: - retval = statfs(path.data, &stfs_buf); - if (retval < 0) { - retval = -errno; - } else { - statfs_to_prstatfs(&pr_stfs, &stfs_buf); - retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, - "qqqqqqqqqqq", pr_stfs.f_type, - pr_stfs.f_bsize, pr_stfs.f_blocks, - pr_stfs.f_bfree, pr_stfs.f_bavail, - pr_stfs.f_files, pr_stfs.f_ffree, - pr_stfs.f_fsid[0], pr_stfs.f_fsid[1], - pr_stfs.f_namelen, pr_stfs.f_frsize); - } - break; - } - v9fs_string_free(&path); - return retval; -} - -static int do_readlink(struct iovec *iovec, struct iovec *out_iovec) -{ - char *buffer; - int size, retval; - V9fsString target, path; - - v9fs_string_init(&path); - retval = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &size); - if (retval < 0) { - v9fs_string_free(&path); - return retval; - } - buffer = g_malloc(size); - v9fs_string_init(&target); - retval = readlink(path.data, buffer, size - 1); - if (retval > 0) { - buffer[retval] = '\0'; - v9fs_string_sprintf(&target, "%s", buffer); - retval = proxy_marshal(out_iovec, PROXY_HDR_SZ, "s", &target); - } else { - retval = -errno; - } - g_free(buffer); - v9fs_string_free(&target); - v9fs_string_free(&path); - return retval; -} - -/* - * create other filesystem objects and send 0 on success - * return -errno on error - */ -static int do_create_others(int type, struct iovec *iovec) -{ - dev_t rdev; - int retval = 0; - int offset = PROXY_HDR_SZ; - V9fsString oldpath, path; - int mode, uid, gid, cur_uid, cur_gid; - - v9fs_string_init(&path); - v9fs_string_init(&oldpath); - - retval = proxy_unmarshal(iovec, offset, "dd", &uid, &gid); - if (retval < 0) { - return retval; - } - offset += retval; - retval = setugid(uid, gid, &cur_uid, &cur_gid); - if (retval < 0) { - goto unmarshal_err_out; - } - switch (type) { - case T_MKNOD: - retval = proxy_unmarshal(iovec, offset, "sdq", &path, &mode, &rdev); - if (retval < 0) { - goto err_out; - } - retval = mknod(path.data, mode, rdev); - break; - case T_MKDIR: - retval = proxy_unmarshal(iovec, offset, "sd", &path, &mode); - if (retval < 0) { - goto err_out; - } - retval = g_mkdir(path.data, mode); - break; - case T_SYMLINK: - retval = proxy_unmarshal(iovec, offset, "ss", &oldpath, &path); - if (retval < 0) { - goto err_out; - } - retval = symlink(oldpath.data, path.data); - break; - } - if (retval < 0) { - retval = -errno; - } - -err_out: - resetugid(cur_uid, cur_gid); -unmarshal_err_out: - v9fs_string_free(&path); - v9fs_string_free(&oldpath); - return retval; -} - -/* - * create a file and send fd on success - * return -errno on error - */ -static int do_create(struct iovec *iovec) -{ - int ret; - V9fsString path; - int flags, mode, uid, gid, cur_uid, cur_gid; - - v9fs_string_init(&path); - ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sdddd", - &path, &flags, &mode, &uid, &gid); - if (ret < 0) { - goto unmarshal_err_out; - } - ret = setugid(uid, gid, &cur_uid, &cur_gid); - if (ret < 0) { - goto unmarshal_err_out; - } - ret = open_regular(path.data, flags, mode); - if (ret < 0) { - ret = -errno; - } - - resetugid(cur_uid, cur_gid); -unmarshal_err_out: - v9fs_string_free(&path); - return ret; -} - -/* - * open a file and send fd on success - * return -errno on error - */ -static int do_open(struct iovec *iovec) -{ - int flags, ret; - V9fsString path; - - v9fs_string_init(&path); - ret = proxy_unmarshal(iovec, PROXY_HDR_SZ, "sd", &path, &flags); - if (ret < 0) { - goto err_out; - } - ret = open_regular(path.data, flags, 0); - if (ret < 0) { - ret = -errno; - } -err_out: - v9fs_string_free(&path); - return ret; -} - -/* create unix domain socket and return the descriptor */ -static int proxy_socket(const char *path, uid_t uid, gid_t gid) -{ - int sock, client; - struct sockaddr_un proxy, qemu; - socklen_t size; - - /* requested socket already exists, refuse to start */ - if (!access(path, F_OK)) { - do_log(LOG_CRIT, "socket already exists\n"); - return -1; - } - - if (strlen(path) >= sizeof(proxy.sun_path)) { - do_log(LOG_CRIT, "UNIX domain socket path exceeds %zu characters\n", - sizeof(proxy.sun_path)); - return -1; - } - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) { - do_perror("socket"); - return -1; - } - - /* mask other part of mode bits */ - umask(7); - - proxy.sun_family = AF_UNIX; - strcpy(proxy.sun_path, path); - if (bind(sock, (struct sockaddr *)&proxy, - sizeof(struct sockaddr_un)) < 0) { - do_perror("bind"); - goto error; - } - if (chown(proxy.sun_path, uid, gid) < 0) { - do_perror("chown"); - goto error; - } - if (listen(sock, 1) < 0) { - do_perror("listen"); - goto error; - } - - size = sizeof(qemu); - client = accept(sock, (struct sockaddr *)&qemu, &size); - if (client < 0) { - do_perror("accept"); - goto error; - } - close(sock); - return client; - -error: - close(sock); - return -1; -} - -static void usage(void) -{ - fprintf(stderr, "usage: %s\n" - " -p|--path 9p path to export\n" - " {-f|--fd } socket file descriptor to be used\n" - " {-s|--socket socket file used for communication\n" - " \t-u|--uid -g|--gid } - uid:gid combination to give " - " access to this socket\n" - " \tNote: -s & -f can not be used together\n" - " [-n|--nodaemon] Run as a normal program\n", - prog_name); -} - -static int process_reply(int sock, int type, - struct iovec *out_iovec, int retval) -{ - switch (type) { - case T_OPEN: - case T_CREATE: - if (send_fd(sock, retval) < 0) { - return -1; - } - break; - case T_MKNOD: - case T_MKDIR: - case T_SYMLINK: - case T_LINK: - case T_CHMOD: - case T_CHOWN: - case T_TRUNCATE: - case T_UTIME: - case T_RENAME: - case T_REMOVE: - case T_LSETXATTR: - case T_LREMOVEXATTR: - if (send_status(sock, out_iovec, retval) < 0) { - return -1; - } - break; - case T_LSTAT: - case T_STATFS: - case T_READLINK: - case T_LGETXATTR: - case T_LLISTXATTR: - case T_GETVERSION: - if (send_response(sock, out_iovec, retval) < 0) { - return -1; - } - break; - default: - return -1; - break; - } - return 0; -} - -static int process_requests(int sock) -{ - int flags; - int size = 0; - int retval = 0; - uint64_t offset; - ProxyHeader header; - int mode, uid, gid; - V9fsString name, value; - struct timespec spec[2]; - V9fsString oldpath, path; - struct iovec in_iovec, out_iovec; - - in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - - while (1) { - /* - * initialize the header type, so that we send - * response to proper request type. - */ - header.type = 0; - retval = read_request(sock, &in_iovec, &header); - if (retval < 0) { - goto err_out; - } - - switch (header.type) { - case T_OPEN: - retval = do_open(&in_iovec); - break; - case T_CREATE: - retval = do_create(&in_iovec); - break; - case T_MKNOD: - case T_MKDIR: - case T_SYMLINK: - retval = do_create_others(header.type, &in_iovec); - break; - case T_LINK: - v9fs_string_init(&path); - v9fs_string_init(&oldpath); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, - "ss", &oldpath, &path); - if (retval > 0) { - retval = link(oldpath.data, path.data); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&oldpath); - v9fs_string_free(&path); - break; - case T_LSTAT: - case T_STATFS: - retval = do_stat(header.type, &in_iovec, &out_iovec); - break; - case T_READLINK: - retval = do_readlink(&in_iovec, &out_iovec); - break; - case T_CHMOD: - v9fs_string_init(&path); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, - "sd", &path, &mode); - if (retval > 0) { - retval = chmod(path.data, mode); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - break; - case T_CHOWN: - v9fs_string_init(&path); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sdd", &path, - &uid, &gid); - if (retval > 0) { - retval = lchown(path.data, uid, gid); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - break; - case T_TRUNCATE: - v9fs_string_init(&path); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sq", - &path, &offset); - if (retval > 0) { - retval = truncate(path.data, offset); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - break; - case T_UTIME: - v9fs_string_init(&path); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sqqqq", &path, - &spec[0].tv_sec, &spec[0].tv_nsec, - &spec[1].tv_sec, &spec[1].tv_nsec); - if (retval > 0) { - retval = utimensat(AT_FDCWD, path.data, spec, - AT_SYMLINK_NOFOLLOW); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - break; - case T_RENAME: - v9fs_string_init(&path); - v9fs_string_init(&oldpath); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, - "ss", &oldpath, &path); - if (retval > 0) { - retval = rename(oldpath.data, path.data); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&oldpath); - v9fs_string_free(&path); - break; - case T_REMOVE: - v9fs_string_init(&path); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "s", &path); - if (retval > 0) { - retval = remove(path.data); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - break; - case T_LGETXATTR: - case T_LLISTXATTR: - retval = do_getxattr(header.type, &in_iovec, &out_iovec); - break; - case T_LSETXATTR: - v9fs_string_init(&path); - v9fs_string_init(&name); - v9fs_string_init(&value); - retval = proxy_unmarshal(&in_iovec, PROXY_HDR_SZ, "sssdd", &path, - &name, &value, &size, &flags); - if (retval > 0) { - retval = lsetxattr(path.data, - name.data, value.data, size, flags); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - v9fs_string_free(&name); - v9fs_string_free(&value); - break; - case T_LREMOVEXATTR: - v9fs_string_init(&path); - v9fs_string_init(&name); - retval = proxy_unmarshal(&in_iovec, - PROXY_HDR_SZ, "ss", &path, &name); - if (retval > 0) { - retval = lremovexattr(path.data, name.data); - if (retval < 0) { - retval = -errno; - } - } - v9fs_string_free(&path); - v9fs_string_free(&name); - break; - case T_GETVERSION: - retval = do_getversion(&in_iovec, &out_iovec); - break; - default: - goto err_out; - break; - } - - if (process_reply(sock, header.type, &out_iovec, retval) < 0) { - goto err_out; - } - } -err_out: - g_free(in_iovec.iov_base); - g_free(out_iovec.iov_base); - return -1; -} - -int main(int argc, char **argv) -{ - int sock; - uid_t own_u; - gid_t own_g; - char *rpath = NULL; - char *sock_name = NULL; - struct stat stbuf; - int c, option_index; -#ifdef FS_IOC_GETVERSION - int retval; - struct statfs st_fs; -#endif - - prog_name = g_path_get_basename(argv[0]); - - is_daemon = true; - sock = -1; - own_u = own_g = -1; - while (1) { - option_index = 0; - c = getopt_long(argc, argv, "p:nh?f:s:u:g:", helper_opts, - &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'p': - rpath = g_strdup(optarg); - break; - case 'n': - is_daemon = false; - break; - case 'f': - sock = atoi(optarg); - break; - case 's': - sock_name = g_strdup(optarg); - break; - case 'u': - own_u = atoi(optarg); - break; - case 'g': - own_g = atoi(optarg); - break; - case '?': - case 'h': - default: - usage(); - exit(EXIT_FAILURE); - } - } - - /* Parameter validation */ - if ((sock_name == NULL && sock == -1) || rpath == NULL) { - fprintf(stderr, "socket, socket descriptor or path not specified\n"); - usage(); - return -1; - } - - if (sock_name && sock != -1) { - fprintf(stderr, "both named socket and socket descriptor specified\n"); - usage(); - exit(EXIT_FAILURE); - } - - if (sock_name && (own_u == -1 || own_g == -1)) { - fprintf(stderr, "owner uid:gid not specified, "); - fprintf(stderr, - "owner uid:gid specifies who can access the socket file\n"); - usage(); - exit(EXIT_FAILURE); - } - - if (lstat(rpath, &stbuf) < 0) { - fprintf(stderr, "invalid path \"%s\" specified, %s\n", - rpath, strerror(errno)); - exit(EXIT_FAILURE); - } - - if (!S_ISDIR(stbuf.st_mode)) { - fprintf(stderr, "specified path \"%s\" is not directory\n", rpath); - exit(EXIT_FAILURE); - } - - if (is_daemon) { - if (daemon(0, 0) < 0) { - fprintf(stderr, "daemon call failed\n"); - exit(EXIT_FAILURE); - } - openlog(PROGNAME, LOG_PID, LOG_DAEMON); - } - - do_log(LOG_INFO, "Started\n"); - if (sock_name) { - sock = proxy_socket(sock_name, own_u, own_g); - if (sock < 0) { - goto error; - } - } - - if (chroot(rpath) < 0) { - do_perror("chroot"); - goto error; - } - if (chdir("/") < 0) { - do_perror("chdir"); - goto error; - } - - get_version = false; -#ifdef FS_IOC_GETVERSION - /* check whether underlying FS support IOC_GETVERSION */ - retval = statfs("/", &st_fs); - if (!retval) { - switch (st_fs.f_type) { - case EXT2_SUPER_MAGIC: - case BTRFS_SUPER_MAGIC: - case REISERFS_SUPER_MAGIC: - case XFS_SUPER_MAGIC: - get_version = true; - break; - } - } -#endif - - umask(0); - if (init_capabilities() < 0) { - goto error; - } - - process_requests(sock); -error: - g_free(rpath); - g_free(sock_name); - do_log(LOG_INFO, "Done\n"); - closelog(); - return 0; -} diff --git a/gdb-xml/aarch64-mte.xml b/gdb-xml/aarch64-mte.xml new file mode 100644 index 0000000000..4b70b4f17a --- /dev/null +++ b/gdb-xml/aarch64-mte.xml @@ -0,0 +1,11 @@ + + + + + + + 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..b94378112a --- /dev/null +++ b/gdb-xml/hexagon-core.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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-linux.xml b/gdb-xml/i386-32bit-linux.xml new file mode 100644 index 0000000000..5ffe5616e6 --- /dev/null +++ b/gdb-xml/i386-32bit-linux.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/gdb-xml/i386-64bit-linux.xml b/gdb-xml/i386-64bit-linux.xml new file mode 100644 index 0000000000..0f26990d2f --- /dev/null +++ b/gdb-xml/i386-64bit-linux.xml @@ -0,0 +1,11 @@ + + + + + + + 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-fpu.xml b/gdb-xml/loongarch-fpu.xml index 78e42cf5dd..e81e3382e7 100644 --- a/gdb-xml/loongarch-fpu.xml +++ b/gdb-xml/loongarch-fpu.xml @@ -45,6 +45,13 @@ - + + + + + + + + diff --git a/gdb-xml/loongarch-lasx.xml b/gdb-xml/loongarch-lasx.xml new file mode 100644 index 0000000000..753b982c65 --- /dev/null +++ b/gdb-xml/loongarch-lasx.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/loongarch-lsx.xml b/gdb-xml/loongarch-lsx.xml new file mode 100644 index 0000000000..51af1c6fd5 --- /dev/null +++ b/gdb-xml/loongarch-lsx.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/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/gdbstub.c b/gdbstub/gdbstub.c index be88ca0d71..b1def7e71d 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -20,362 +20,44 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #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 "qemu/error-report.h" #include "trace.h" #include "exec/gdbstub.h" +#include "gdbstub/commands.h" +#include "gdbstub/syscalls.h" #ifdef CONFIG_USER_ONLY -#include "qemu.h" +#include "accel/tcg/vcpu-state.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 +#include "hw/core/cpu.h" -#define MAX_PACKET_LENGTH 4096 - -#include "qemu/sockets.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" -#include "semihosting/semihost.h" -#include "exec/exec-all.h" +#include "exec/replay-core.h" #include "exec/hwaddr.h" -#include "sysemu/replay.h" #include "internals.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; -} - 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)); @@ -394,211 +76,8 @@ static void init_gdbserver_state(void) 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 - -/* - * 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; -} - -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++) { @@ -609,7 +88,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; @@ -654,7 +133,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]; @@ -678,37 +157,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; @@ -728,16 +201,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; @@ -765,7 +241,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; } } @@ -773,7 +249,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; @@ -819,7 +295,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); @@ -851,7 +327,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); @@ -877,79 +353,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; @@ -958,65 +539,101 @@ 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; } } } +void gdb_unregister_coprocessor_all(CPUState *cpu) +{ + /* + * Safe to nuke everything. GDBRegisterState::xml is static const char so + * it won't be freed + */ + g_array_free(cpu->gdb_regs, true); + + cpu->gdb_regs = NULL; + cpu->gdb_num_regs = 0; + cpu->gdb_num_g_regs = 0; +} + 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_breakpoint_remove_all(cpu); @@ -1025,7 +642,7 @@ static void gdb_process_breakpoint_remove_all(GDBProcess *p) } -static void gdb_set_cpu_pc(target_ulong pc) +static void gdb_set_cpu_pc(vaddr pc) { CPUState *cpu = gdbserver_state.c_cpu; @@ -1033,23 +650,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) { @@ -1067,7 +677,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); @@ -1106,24 +716,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) { @@ -1137,10 +737,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++; @@ -1148,13 +756,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 == ';') { @@ -1167,20 +774,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); @@ -1191,14 +799,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); @@ -1210,40 +820,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[] = ",;:="; @@ -1328,55 +934,24 @@ static int cmd_parse_params(const char *data, const char *schema, return 0; } -typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); - -/* - * cmd_startswith -> cmd is compared using startswith - * - * - * schema definitions: - * Each schema parameter entry consists of 2 chars, - * the first char represents the parameter type handling - * the second char represents the delimiter for the next parameter - * - * Currently supported schema types: - * 'l' -> unsigned long (stored in .val_ul) - * 'L' -> unsigned long long (stored in .val_ull) - * 's' -> string (stored in .data) - * 'o' -> single char (stored in .opcode) - * 't' -> thread id (stored in .thread_id) - * '?' -> skip according to delimiter - * - * Currently supported delimiters: - * '?' -> Stop at any delimiter (",;:=\0") - * '0' -> Stop at "\0" - * '.' -> Skip 1 char unless reached "\0" - * Any other value is treated as the delimiter value itself - */ -typedef struct GdbCmdParseEntry { - GdbCmdHandler handler; - const char *cmd; - bool cmd_startswith; - const char *schema; -} GdbCmdParseEntry; - 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, - const GdbCmdParseEntry *cmds, int num_cmds) +static bool process_string_cmd(const char *data, + const GdbCmdParseEntry *cmds, int num_cmds) { int i; g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant)); if (!cmds) { - return -1; + return false; } for (i = 0; i < num_cmds; i++) { const GdbCmdParseEntry *cmd = &cmds[i]; + void *user_ctx = NULL; g_assert(cmd->handler && cmd->cmd); if ((cmd->cmd_startswith && !startswith(data, cmd->cmd)) || @@ -1387,15 +962,20 @@ static int process_string_cmd(void *user_ctx, const char *data, if (cmd->schema) { if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params)) { - return -1; + return false; } } + if (cmd->need_cpu_context) { + user_ctx = (void *)gdbserver_state.g_cpu; + } + + gdbserver_state.allow_stop_reply = cmd->allow_stop_reply; cmd->handler(params, user_ctx); - return 0; + return true; } - return -1; + return false; } static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd) @@ -1409,8 +989,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(""); } } @@ -1421,13 +1001,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; + pid = gdb_get_cmd_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; @@ -1442,10 +1028,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) @@ -1453,29 +1039,29 @@ 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"); + if (gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, - get_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, + gdb_get_cmd_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) { if (params->len) { - gdb_set_cpu_pc(get_param(params, 0)->val_ull); + gdb_set_cpu_pc(gdb_get_cmd_param(params, 0)->val_ull); } gdbserver_state.signal = 0; @@ -1491,7 +1077,7 @@ static void handle_cont_with_sig(GArray *params, void *user_ctx) * omit the addr parameter */ if (params->len) { - signal = get_param(params, 0)->val_ul; + signal = gdb_get_cmd_param(params, 0)->val_ul; } gdbserver_state.signal = gdb_signal_to_target(signal); @@ -1503,27 +1089,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"); + if (gdb_get_cmd_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) { + gdb_put_packet("E22"); return; } - if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { - put_packet("OK"); + if (gdb_get_cmd_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) { + gdb_put_packet("OK"); return; } - cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid, - get_param(params, 1)->thread_id.tid); + pid = gdb_get_cmd_param(params, 1)->thread_id.pid; + tid = gdb_get_cmd_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; } @@ -1531,17 +1124,17 @@ static void handle_set_thread(GArray *params, void *user_ctx) * Note: This command is deprecated and modern gdb's will be using the * vCont command instead. */ - switch (get_param(params, 0)->opcode) { + switch (gdb_get_cmd_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; } } @@ -1551,23 +1144,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(gdbserver_state.c_cpu, - get_param(params, 0)->val_ul, - get_param(params, 1)->val_ull, - get_param(params, 2)->val_ull); + gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, + gdb_get_cmd_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) @@ -1575,23 +1168,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(gdbserver_state.c_cpu, - get_param(params, 0)->val_ul, - get_param(params, 1)->val_ull, - get_param(params, 2)->val_ull); + gdb_get_cmd_param(params, 0)->val_ul, + gdb_get_cmd_param(params, 1)->val_ull, + gdb_get_cmd_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"); } /* @@ -1609,110 +1202,102 @@ 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); + reg_size = strlen(gdb_get_cmd_param(params, 1)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_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_get_cmd_param(params, 0)->val_ull); + 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; } reg_size = gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, - get_param(params, 0)->val_ull); + gdb_get_cmd_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 */ - if (get_param(params, 1)->val_ull > - strlen(get_param(params, 2)->data) / 2) { - put_packet("E22"); + /* gdb_hextomem() reads 2*len bytes */ + if (gdb_get_cmd_param(params, 1)->val_ull > + strlen(gdb_get_cmd_param(params, 2)->data) / 2) { + 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, gdb_get_cmd_param(params, 2)->data, + gdb_get_cmd_param(params, 1)->val_ull); + if (gdb_target_memory_rw_debug(gdbserver_state.g_cpu, + gdb_get_cmd_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 */ - if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { - put_packet("E22"); + /* gdb_memtohex() doubles the required space */ + if (gdb_get_cmd_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) { + gdb_put_packet("E22"); return; } g_byte_array_set_size(gdbserver_state.mem_buf, - get_param(params, 1)->val_ull); + gdb_get_cmd_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, + gdb_get_cmd_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; @@ -1721,95 +1306,43 @@ 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); + len = strlen(gdb_get_cmd_param(params, 0)->data) / 2; + gdb_hextomem(gdbserver_state.mem_buf, gdb_get_cmd_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) { - 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_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(gdb_get_cmd_param(params, 0)->val_ull); } cpu_single_step(gdbserver_state.c_cpu, gdbserver_state.sstep_flags); @@ -1818,35 +1351,35 @@ 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) { + switch (gdb_get_cmd_param(params, 0)->opcode) { case 's': 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) @@ -1857,11 +1390,11 @@ static void handle_v_cont(GArray *params, void *user_ctx) return; } - res = gdb_handle_vcont(get_param(params, 0)->data); + res = gdb_handle_vcont(gdb_get_cmd_param(params, 0)->data); if ((res == -EINVAL) || (res == -ERANGE)) { - put_packet("E22"); + gdb_put_packet("E22"); } else if (res) { - put_packet(""); + gdb_put_packet(""); } } @@ -1875,12 +1408,12 @@ static void handle_v_attach(GArray *params, void *user_ctx) goto cleanup; } - process = gdb_get_process(get_param(params, 0)->val_ul); + process = gdb_get_process(gdb_get_cmd_param(params, 0)->val_ul); if (!process) { goto cleanup; } - cpu = get_first_cpu_in_process(process); + cpu = gdb_get_first_cpu_in_process(process); if (!cpu) { goto cleanup; } @@ -1889,20 +1422,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[] = { @@ -1910,25 +1446,57 @@ static const GdbCmdParseEntry gdb_v_commands_table[] = { { .handler = handle_v_cont_query, .cmd = "Cont?", - .cmd_startswith = 1 + .cmd_startswith = true }, { .handler = handle_v_cont, .cmd = "Cont", - .cmd_startswith = 1, + .cmd_startswith = true, + .allow_stop_reply = true, .schema = "s0" }, { .handler = handle_v_attach, .cmd = "Attach;", - .cmd_startswith = 1, + .cmd_startswith = true, + .allow_stop_reply = true, .schema = "l0" }, { .handler = handle_v_kill, .cmd = "Kill;", - .cmd_startswith = 1 + .cmd_startswith = true }, +#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 = true, + .schema = "s,L,L0" + }, + { + .handler = gdb_handle_v_file_close, + .cmd = "File:close:", + .cmd_startswith = true, + .schema = "l0" + }, + { + .handler = gdb_handle_v_file_pread, + .cmd = "File:pread:", + .cmd_startswith = true, + .schema = "l,L,L0" + }, + { + .handler = gdb_handle_v_file_readlink, + .cmd = "File:readlink:", + .cmd_startswith = true, + .schema = "s0" + }, +#endif }; static void handle_v_commands(GArray *params, void *user_ctx) @@ -1937,10 +1505,10 @@ static void handle_v_commands(GArray *params, void *user_ctx) return; } - if (process_string_cmd(NULL, get_param(params, 0)->data, - gdb_v_commands_table, - ARRAY_SIZE(gdb_v_commands_table))) { - put_packet(""); + if (!process_string_cmd(gdb_get_cmd_param(params, 0)->data, + gdb_v_commands_table, + ARRAY_SIZE(gdb_v_commands_table))) { + gdb_put_packet(""); } } @@ -1958,7 +1526,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) @@ -1969,22 +1537,22 @@ static void handle_set_qemu_sstep(GArray *params, void *user_ctx) return; } - new_sstep_flags = get_param(params, 0)->val_ul; + new_sstep_flags = gdb_get_cmd_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) @@ -1998,22 +1566,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); } @@ -2029,13 +1597,13 @@ static void handle_query_thread_extra(GArray *params, void *user_ctx) CPUState *cpu; if (!params->len || - get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { - put_packet("E22"); + gdb_get_cmd_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) { + gdb_put_packet("E22"); return; } - cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid, - get_param(params, 0)->thread_id.tid); + cpu = gdb_get_cpu(gdb_get_cmd_param(params, 0)->thread_id.pid, + gdb_get_cmd_param(params, 0)->thread_id.tid); if (!cpu) { return; } @@ -2055,51 +1623,26 @@ 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) + +static char **extra_query_flags; + +void gdb_extend_qsupported_features(char *qflags) { - 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; + if (!extra_query_flags) { + extra_query_flags = g_new0(char *, 2); + extra_query_flags[0] = g_strdup(qflags); + } else if (!g_strv_contains((const gchar * const *) extra_query_flags, + qflags)) { + int len = g_strv_length(extra_query_flags); + extra_query_flags = g_realloc_n(extra_query_flags, len + 2, + sizeof(char *)); + extra_query_flags[len] = g_strdup(qflags); } - - 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) { @@ -2111,24 +1654,44 @@ 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 (gdbserver_state.c_cpu->opaque) { +#if defined(CONFIG_USER_ONLY) +#if defined(CONFIG_LINUX) + if (get_task_state(gdbserver_state.c_cpu)) { 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 = gdb_get_cmd_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 } g_string_append(gdbserver_state.str_buf, ";vContSupported+;multiprocess+"); - put_strbuf(); + + if (extra_query_flags) { + int extras = g_strv_length(extra_query_flags); + for (int i = 0; i < extras; i++) { + g_string_append(gdbserver_state.str_buf, extra_query_flags[i]); + } + } + + gdb_put_strbuf(); } static void handle_query_xfer_features(GArray *params, void *user_ctx) @@ -2140,30 +1703,29 @@ 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; + p = gdb_get_cmd_param(params, 0)->data; xml = get_feature_xml(p, &p, process); if (!xml) { - put_packet("E00"); + gdb_put_packet("E00"); return; } - addr = get_param(params, 1)->val_ul; - len = get_param(params, 2)->val_ul; + addr = gdb_get_cmd_param(params, 1)->val_ul; + len = gdb_get_cmd_param(params, 2)->val_ul; total_len = strlen(xml); if (addr > total_len) { - put_packet("E00"); + gdb_put_packet("E00"); return; } @@ -2173,101 +1735,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 - static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2281,11 +1767,65 @@ static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = { { .handler = handle_set_qemu_sstep, .cmd = "qemu.sstep=", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, }; +/** + * extend_table() - extend one of the command tables + * @table: the command table to extend (or NULL) + * @extensions: a list of GdbCmdParseEntry pointers + * + * The entries themselves should be pointers to static const + * GdbCmdParseEntry entries. If the entry is already in the table we + * skip adding it again. + * + * Returns (a potentially freshly allocated) GPtrArray of GdbCmdParseEntry + */ +static GPtrArray *extend_table(GPtrArray *table, GPtrArray *extensions) +{ + if (!table) { + table = g_ptr_array_new(); + } + + for (int i = 0; i < extensions->len; i++) { + gpointer entry = g_ptr_array_index(extensions, i); + if (!g_ptr_array_find(table, entry, NULL)) { + g_ptr_array_add(table, entry); + } + } + + return table; +} + +/** + * process_extended_table() - run through an extended command table + * @table: the command table to check + * @data: parameters + * + * returns true if the command was found and executed + */ +static bool process_extended_table(GPtrArray *table, const char *data) +{ + for (int i = 0; i < table->len; i++) { + const GdbCmdParseEntry *entry = g_ptr_array_index(table, i); + if (process_string_cmd(data, entry, 1)) { + return true; + } + } + return false; +} + + +/* Ptr to GdbCmdParseEntry */ +static GPtrArray *extended_query_table; + +void gdb_extend_query_table(GPtrArray *new_queries) +{ + extended_query_table = extend_table(extended_query_table, new_queries); +} + static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_curr_tid, @@ -2302,26 +1842,26 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_thread_extra, .cmd = "ThreadExtraInfo,", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "t0" }, #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, + .cmd_startswith = true, .schema = "s0" }, #endif { .handler = handle_query_supported, .cmd = "Supported:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }, { @@ -2332,24 +1872,38 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { { .handler = handle_query_xfer_features, .cmd = "Xfer:features:read:", - .cmd_startswith = 1, + .cmd_startswith = true, .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, + .cmd_startswith = true, .schema = "l,l0" }, + { + .handler = gdb_handle_query_xfer_siginfo, + .cmd = "Xfer:siginfo:read::", + .cmd_startswith = true, + .schema = "l,l0" + }, +#endif + { + .handler = gdb_handle_query_xfer_exec_file, + .cmd = "Xfer:exec-file:read:", + .cmd_startswith = true, + .schema = "l:l,l0" + }, #endif { - .handler = handle_query_attached, + .handler = gdb_handle_query_attached, .cmd = "Attached:", - .cmd_startswith = 1 + .cmd_startswith = true }, { - .handler = handle_query_attached, + .handler = gdb_handle_query_attached, .cmd = "Attached", }, { @@ -2358,74 +1912,117 @@ 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 }; +/* Ptr to GdbCmdParseEntry */ +static GPtrArray *extended_set_table; + +void gdb_extend_set_table(GPtrArray *new_set) +{ + extended_set_table = extend_table(extended_set_table, new_set); +} + static const GdbCmdParseEntry gdb_gen_set_table[] = { /* Order is important if has same prefix */ { .handler = handle_set_qemu_sstep, .cmd = "qemu.sstep:", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l0" }, #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, + .cmd_startswith = true, .schema = "l0" }, #endif +#if defined(CONFIG_USER_ONLY) + { + .handler = gdb_handle_set_catch_syscalls, + .cmd = "CatchSyscalls:", + .cmd_startswith = true, + .schema = "s0", + }, +#endif }; static void handle_gen_query(GArray *params, void *user_ctx) { + const char *data; + if (!params->len) { return; } - if (!process_string_cmd(NULL, get_param(params, 0)->data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { + data = gdb_get_cmd_param(params, 0)->data; + + if (process_string_cmd(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(data, gdb_gen_query_table, ARRAY_SIZE(gdb_gen_query_table))) { - put_packet(""); + return; } + + if (extended_query_table && + process_extended_table(extended_query_table, data)) { + return; + } + + /* Can't handle query, return Empty response. */ + gdb_put_packet(""); } static void handle_gen_set(GArray *params, void *user_ctx) { + const char *data; + if (!params->len) { return; } - if (!process_string_cmd(NULL, get_param(params, 0)->data, - gdb_gen_query_set_common_table, - ARRAY_SIZE(gdb_gen_query_set_common_table))) { + data = gdb_get_cmd_param(params, 0)->data; + + if (process_string_cmd(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(data, gdb_gen_set_table, ARRAY_SIZE(gdb_gen_set_table))) { - put_packet(""); + return; } + + if (extended_set_table && + process_extended_table(extended_set_table, data)) { + return; + } + + /* Can't handle set, return Empty response. */ + 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 @@ -2442,14 +2039,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 = true, + .allow_stop_reply = true, }; cmd_parser = &target_halted_cmd_desc; } @@ -2459,7 +2057,8 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry continue_cmd_desc = { .handler = handle_continue, .cmd = "c", - .cmd_startswith = 1, + .cmd_startswith = true, + .allow_stop_reply = true, .schema = "L0" }; cmd_parser = &continue_cmd_desc; @@ -2470,7 +2069,8 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry cont_with_sig_cmd_desc = { .handler = handle_cont_with_sig, .cmd = "C", - .cmd_startswith = 1, + .cmd_startswith = true, + .allow_stop_reply = true, .schema = "l0" }; cmd_parser = &cont_with_sig_cmd_desc; @@ -2481,7 +2081,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry v_cmd_desc = { .handler = handle_v_commands, .cmd = "v", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &v_cmd_desc; @@ -2491,13 +2091,14 @@ 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 = { .handler = handle_detach, .cmd = "D", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "?.l0" }; cmd_parser = &detach_cmd_desc; @@ -2508,7 +2109,8 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry step_cmd_desc = { .handler = handle_step, .cmd = "s", - .cmd_startswith = 1, + .cmd_startswith = true, + .allow_stop_reply = true, .schema = "L0" }; cmd_parser = &step_cmd_desc; @@ -2519,7 +2121,8 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry backward_cmd_desc = { .handler = handle_backward, .cmd = "b", - .cmd_startswith = 1, + .cmd_startswith = true, + .allow_stop_reply = true, .schema = "o0" }; cmd_parser = &backward_cmd_desc; @@ -2528,9 +2131,9 @@ 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, + .cmd_startswith = true, .schema = "L,L,o0" }; cmd_parser = &file_io_cmd_desc; @@ -2541,7 +2144,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry read_all_regs_cmd_desc = { .handler = handle_read_all_regs, .cmd = "g", - .cmd_startswith = 1 + .cmd_startswith = true }; cmd_parser = &read_all_regs_cmd_desc; } @@ -2551,7 +2154,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry write_all_regs_cmd_desc = { .handler = handle_write_all_regs, .cmd = "G", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &write_all_regs_cmd_desc; @@ -2562,7 +2165,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry read_mem_cmd_desc = { .handler = handle_read_mem, .cmd = "m", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L0" }; cmd_parser = &read_mem_cmd_desc; @@ -2573,7 +2176,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry write_mem_cmd_desc = { .handler = handle_write_mem, .cmd = "M", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L,L:s0" }; cmd_parser = &write_mem_cmd_desc; @@ -2584,7 +2187,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry get_reg_cmd_desc = { .handler = handle_get_reg, .cmd = "p", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L0" }; cmd_parser = &get_reg_cmd_desc; @@ -2595,7 +2198,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry set_reg_cmd_desc = { .handler = handle_set_reg, .cmd = "P", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "L?s0" }; cmd_parser = &set_reg_cmd_desc; @@ -2606,7 +2209,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry insert_bp_cmd_desc = { .handler = handle_insert_bp, .cmd = "Z", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l?L?L0" }; cmd_parser = &insert_bp_cmd_desc; @@ -2617,7 +2220,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry remove_bp_cmd_desc = { .handler = handle_remove_bp, .cmd = "z", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "l?L?L0" }; cmd_parser = &remove_bp_cmd_desc; @@ -2628,7 +2231,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry set_thread_cmd_desc = { .handler = handle_set_thread, .cmd = "H", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "o.t0" }; cmd_parser = &set_thread_cmd_desc; @@ -2639,7 +2242,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry thread_alive_cmd_desc = { .handler = handle_thread_alive, .cmd = "T", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "t0" }; cmd_parser = &thread_alive_cmd_desc; @@ -2650,7 +2253,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry gen_query_cmd_desc = { .handler = handle_gen_query, .cmd = "q", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &gen_query_cmd_desc; @@ -2661,7 +2264,7 @@ static int gdb_handle_packet(const char *line_buf) static const GdbCmdParseEntry gen_set_cmd_desc = { .handler = handle_gen_set, .cmd = "Q", - .cmd_startswith = 1, + .cmd_startswith = true, .schema = "s0" }; cmd_parser = &gen_set_cmd_desc; @@ -2669,7 +2272,7 @@ static int gdb_handle_packet(const char *line_buf) break; default: /* put empty packet */ - put_packet(""); + gdb_put_packet(""); break; } @@ -2696,193 +2299,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 (!gdb_attached()) { - 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(); @@ -2897,8 +2325,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 @@ -2910,6 +2348,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); } @@ -3004,12 +2447,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; @@ -3019,499 +2462,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 (!gdb_supports_guest_debug()) { - error_report("gdbstub: current accelerator 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 index eabb0341d1..bf5a5c6302 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -6,12 +6,215 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef _INTERNALS_H_ -#define _INTERNALS_H_ +#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_binary(const char *buf, int len, bool dump); +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); + +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, hwaddr addr, hwaddr len); -int gdb_breakpoint_remove(CPUState *cs, int type, hwaddr addr, hwaddr len); +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); -#endif /* _INTERNALS_H_ */ +/** + * 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 index fc895a2c39..dff741ddd4 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -1,9 +1,37 @@ # # The main gdbstub still relies on per-build definitions of various -# types. The bits pushed to softmmu/user.c try to use guest agnostic +# types. The bits pushed to system/user.c try to use guest agnostic # types such as hwaddr. # -specific_ss.add(files('gdbstub.c')) -softmmu_ss.add(files('softmmu.c')) -user_ss.add(files('user.c')) +# 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, + c_args: '-DCONFIG_USER_ONLY', + build_by_default: false) + +libgdb_system = static_library('gdb_system', + gdb_system_ss.sources() + genh, + build_by_default: false) + +gdb_user = declare_dependency(objects: libgdb_user.extract_all_objects(recursive: false)) +user_ss.add(gdb_user) +gdb_system = declare_dependency(objects: libgdb_system.extract_all_objects(recursive: false)) +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/softmmu.c b/gdbstub/softmmu.c deleted file mode 100644 index f208c6cf15..0000000000 --- a/gdbstub/softmmu.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * gdb server stub - softmmu specific bits - * - * Debug integration depends on support from the individual - * accelerators so most of this involves calling the ops helpers. - * - * Copyright (c) 2022 Linaro Ltd - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "exec/gdbstub.h" -#include "exec/hwaddr.h" -#include "sysemu/cpus.h" -#include "internals.h" - -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, hwaddr addr, hwaddr 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, hwaddr addr, hwaddr 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/syscalls.c b/gdbstub/syscalls.c new file mode 100644 index 0000000000..4ddd5cae06 --- /dev/null +++ b/gdbstub/syscalls.c @@ -0,0 +1,206 @@ +/* + * 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-or-later + */ + +#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 "gdbstub/commands.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 = gdb_get_cmd_param(params, 0)->val_ull; + if (params->len >= 2) { + err = gdb_get_cmd_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 && gdb_get_cmd_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..c9f236e94f --- /dev/null +++ b/gdbstub/system.c @@ -0,0 +1,665 @@ +/* + * 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-or-later + */ + +#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 "gdbstub/commands.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 (!gdb_get_cmd_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(gdb_get_cmd_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, gdb_get_cmd_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 index 03f0c303bf..4fd126a38c 100644 --- a/gdbstub/trace-events +++ b/gdbstub/trace-events @@ -7,7 +7,6 @@ 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" @@ -27,3 +26,7 @@ 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/user-target.c b/gdbstub/user-target.c new file mode 100644 index 0000000000..22bf4008c0 --- /dev/null +++ b/gdbstub/user-target.c @@ -0,0 +1,426 @@ +/* + * Target specific user-mode handling + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2022 Linaro Ltd + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/gdbstub.h" +#include "gdbstub/commands.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 = get_task_state(gdbserver_state.c_cpu); + 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 = gdb_get_cmd_param(params, 0)->val_ul; + len = gdb_get_cmd_param(params, 1)->val_ul; + ts = get_task_state(gdbserver_state.c_cpu); + 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 = gdb_get_cmd_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 = gdb_get_cmd_param(params, 1)->val_ull; + uint64_t mode = gdb_get_cmd_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 = gdb_get_cmd_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 = gdb_get_cmd_param(params, 0)->val_ul; + size_t count = gdb_get_cmd_param(params, 1)->val_ull; + off_t offset = gdb_get_cmd_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 = gdb_get_cmd_param(params, 0)->val_ul; + uint32_t offset = gdb_get_cmd_param(params, 1)->val_ul; + uint32_t length = gdb_get_cmd_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 index 033e5fdd71..0b4bfa9c48 100644 --- a/gdbstub/user.c +++ b/gdbstub/user.c @@ -3,24 +3,708 @@ * * 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: GPL-2.0-or-later + * SPDX-License-Identifier: LGPL-2.0-or-later */ #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/commands.h" +#include "gdbstub/syscalls.h" +#include "gdbstub/user.h" +#include "gdbstub/enums.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, hwaddr addr, hwaddr len) +int gdb_breakpoint_insert(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; @@ -41,7 +725,7 @@ int gdb_breakpoint_insert(CPUState *cs, int type, hwaddr addr, hwaddr len) } } -int gdb_breakpoint_remove(CPUState *cs, int type, hwaddr addr, hwaddr len) +int gdb_breakpoint_remove(CPUState *cs, int type, vaddr addr, vaddr len) { CPUState *cpu; int err = 0; @@ -66,3 +750,129 @@ 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 = gdb_get_cmd_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 = gdb_get_cmd_param(params, 0)->val_ul; + len = gdb_get_cmd_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/genconfig b/genconfig deleted file mode 160000 index 44bab849ce..0000000000 --- a/genconfig +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 44bab849ce87fceafd74703bfcf2b61a1a1b738f 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 754b1e8408..c59cd6637b 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 @@ -174,7 +174,7 @@ ERST .args_type = "", .params = "", .help = "show PIC state", - .cmd = hmp_info_pic, + .cmd_info_hrt = qmp_x_query_interrupt_controllers, }, SRST @@ -182,19 +182,6 @@ SRST Show PIC state. ERST - { - .name = "rdma", - .args_type = "", - .params = "", - .help = "show RDMA state", - .cmd_info_hrt = qmp_x_query_rdma, - }, - -SRST - ``info rdma`` - Show RDMA state. -ERST - { .name = "pci", .args_type = "", @@ -360,21 +347,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 = "", @@ -555,9 +527,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, }, @@ -920,7 +892,7 @@ ERST }, SRST - ``stats`` + ``info stats`` Show runtime-collected statistics ERST @@ -993,3 +965,17 @@ 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 673e39a697..06746f0afc 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", }, @@ -252,6 +252,7 @@ SRST ERST +#ifdef CONFIG_PIXMAN { .name = "screendump", .args_type = "filename:F,format:-fs,device:s?,head:i?", @@ -267,6 +268,7 @@ SRST ``screendump`` *filename* Save screen into PPM image *filename*. ERST +#endif { .name = "logfile", @@ -379,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 @@ -563,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 @@ -903,26 +909,23 @@ ERST { .name = "migrate", - .args_type = "detach:-d,blk:-b,inc:-i,resume:-r,uri:s", - .params = "[-d] [-b] [-i] [-r] uri", + .args_type = "detach:-d,resume:-r,uri:s", + .params = "[-d] [-r] uri", .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -b for migration without shared storage with" - " full copy of disk\n\t\t\t -i for migration without " - "shared storage with incremental copy of disk " - "(base image shared between src and destination)" - "\n\t\t\t -r to resume a paused migration", + "\n\t\t\t -r to resume a paused postcopy migration", .cmd = hmp_migrate, }, SRST -``migrate [-d] [-b] [-i]`` *uri* - Migrate to *uri* (using -d to not wait for completion). +``migrate [-d] [-r]`` *uri* + Migrate the VM to *uri*. - ``-b`` - for migration with full copy of disk - ``-i`` - for migration with incremental copy of disk (base image is shared) + ``-d`` + Start the migration process, but do not wait for its completion. To + query an ongoing migration process, use "info migrate". + ``-r`` + Resume a paused postcopy migration. ERST { @@ -1035,6 +1038,7 @@ SRST migration (or once already in postcopy). ERST +#ifdef CONFIG_REPLICATION { .name = "x_colo_lost_heartbeat", .args_type = "", @@ -1043,6 +1047,7 @@ ERST "a failover or takeover is needed.", .cmd = hmp_x_colo_lost_heartbeat, }, +#endif SRST ``x_colo_lost_heartbeat`` @@ -1066,14 +1071,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" @@ -1096,6 +1103,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 @@ -1277,6 +1287,9 @@ ERST .name = "netdev_add", .args_type = "netdev:O", .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 @@ -1396,7 +1409,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, @@ -1486,6 +1499,7 @@ SRST Inject an MCE on the given CPU (x86 only). ERST +#ifdef CONFIG_POSIX { .name = "getfd", .args_type = "fdname:s", @@ -1501,6 +1515,7 @@ SRST mechanism on unix sockets, it is stored using the name *fdname* for later use by other monitor commands. ERST +#endif { .name = "closefd", @@ -1815,3 +1830,32 @@ SRST 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/bufferiszero.c.inc b/host/include/aarch64/host/bufferiszero.c.inc new file mode 100644 index 0000000000..947ee7ca1f --- /dev/null +++ b/host/include/aarch64/host/bufferiszero.c.inc @@ -0,0 +1,76 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, aarch64 version. + */ + +#ifdef __ARM_NEON +#include + +/* + * Helper for preventing the compiler from reassociating + * chains of binary vector operations. + */ +#define REASSOC_BARRIER(vec0, vec1) asm("" : "+w"(vec0), "+w"(vec1)) + +static bool buffer_is_zero_simd(const void *buf, size_t len) +{ + uint32x4_t t0, t1, t2, t3; + + /* Align head/tail to 16-byte boundaries. */ + const uint32x4_t *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const uint32x4_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); + + /* Unaligned loads at head/tail. */ + t0 = vld1q_u32(buf) | vld1q_u32(buf + len - 16); + + /* Collect a partial block at tail end. */ + t1 = e[-7] | e[-6]; + t2 = e[-5] | e[-4]; + t3 = e[-3] | e[-2]; + t0 |= e[-1]; + REASSOC_BARRIER(t0, t1); + REASSOC_BARRIER(t2, t3); + t0 |= t1; + t2 |= t3; + REASSOC_BARRIER(t0, t2); + t0 |= t2; + + /* + * Loop over complete 128-byte blocks. + * With the head and tail removed, e - p >= 14, so the loop + * must iterate at least once. + */ + do { + /* + * Reduce via UMAXV. Whatever the actual result, + * it will only be zero if all input bytes are zero. + */ + if (unlikely(vmaxvq_u32(t0) != 0)) { + return false; + } + + t0 = p[0] | p[1]; + t1 = p[2] | p[3]; + t2 = p[4] | p[5]; + t3 = p[6] | p[7]; + REASSOC_BARRIER(t0, t1); + REASSOC_BARRIER(t2, t3); + t0 |= t1; + t2 |= t3; + REASSOC_BARRIER(t0, t2); + t0 |= t2; + p += 8; + } while (p < e - 7); + + return vmaxvq_u32(t0) == 0; +} + +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_is_zero_simd, +}; + +#define best_accel() 1 +#else +# include "host/include/generic/host/bufferiszero.c.inc" +#endif 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.inc b/host/include/aarch64/host/load-extract-al16-al8.h.inc new file mode 100644 index 0000000000..bd677c5e26 --- /dev/null +++ b/host/include/aarch64/host/load-extract-al16-al8.h.inc @@ -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.inc b/host/include/aarch64/host/store-insert-al16.h.inc new file mode 100644 index 0000000000..1943155bc6 --- /dev/null +++ b/host/include/aarch64/host/store-insert-al16.h.inc @@ -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/bufferiszero.c.inc b/host/include/generic/host/bufferiszero.c.inc new file mode 100644 index 0000000000..ea0875c24a --- /dev/null +++ b/host/include/generic/host/bufferiszero.c.inc @@ -0,0 +1,10 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, generic version. + */ + +static biz_accel_fn const accel_table[1] = { + buffer_is_zero_int_ge256 +}; + +#define best_accel() 0 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.inc b/host/include/generic/host/load-extract-al16-al8.h.inc new file mode 100644 index 0000000000..d95556130f --- /dev/null +++ b/host/include/generic/host/load-extract-al16-al8.h.inc @@ -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.inc b/host/include/generic/host/store-insert-al16.h.inc new file mode 100644 index 0000000000..4a1662183d --- /dev/null +++ b/host/include/generic/host/store-insert-al16.h.inc @@ -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/bufferiszero.c.inc b/host/include/i386/host/bufferiszero.c.inc new file mode 100644 index 0000000000..74ae98580f --- /dev/null +++ b/host/include/i386/host/bufferiszero.c.inc @@ -0,0 +1,125 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, x86 version. + */ + +#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__) +#include + +/* Helper for preventing the compiler from reassociating + chains of binary vector operations. */ +#define SSE_REASSOC_BARRIER(vec0, vec1) asm("" : "+x"(vec0), "+x"(vec1)) + +/* Note that these vectorized functions may assume len >= 256. */ + +static bool __attribute__((target("sse2"))) +buffer_zero_sse2(const void *buf, size_t len) +{ + /* Unaligned loads at head/tail. */ + __m128i v = *(__m128i_u *)(buf); + __m128i w = *(__m128i_u *)(buf + len - 16); + /* Align head/tail to 16-byte boundaries. */ + const __m128i *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const __m128i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16); + __m128i zero = { 0 }; + + /* Collect a partial block at tail end. */ + v |= e[-1]; w |= e[-2]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-3]; w |= e[-4]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-5]; w |= e[-6]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-7]; v |= w; + + /* + * Loop over complete 128-byte blocks. + * With the head and tail removed, e - p >= 14, so the loop + * must iterate at least once. + */ + do { + v = _mm_cmpeq_epi8(v, zero); + if (unlikely(_mm_movemask_epi8(v) != 0xFFFF)) { + return false; + } + v = p[0]; w = p[1]; + SSE_REASSOC_BARRIER(v, w); + v |= p[2]; w |= p[3]; + SSE_REASSOC_BARRIER(v, w); + v |= p[4]; w |= p[5]; + SSE_REASSOC_BARRIER(v, w); + v |= p[6]; w |= p[7]; + SSE_REASSOC_BARRIER(v, w); + v |= w; + p += 8; + } while (p < e - 7); + + return _mm_movemask_epi8(_mm_cmpeq_epi8(v, zero)) == 0xFFFF; +} + +#ifdef CONFIG_AVX2_OPT +static bool __attribute__((target("avx2"))) +buffer_zero_avx2(const void *buf, size_t len) +{ + /* Unaligned loads at head/tail. */ + __m256i v = *(__m256i_u *)(buf); + __m256i w = *(__m256i_u *)(buf + len - 32); + /* Align head/tail to 32-byte boundaries. */ + const __m256i *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32); + const __m256i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32); + __m256i zero = { 0 }; + + /* Collect a partial block at tail end. */ + v |= e[-1]; w |= e[-2]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-3]; w |= e[-4]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-5]; w |= e[-6]; + SSE_REASSOC_BARRIER(v, w); + v |= e[-7]; v |= w; + + /* Loop over complete 256-byte blocks. */ + for (; p < e - 7; p += 8) { + /* PTEST is not profitable here. */ + v = _mm256_cmpeq_epi8(v, zero); + if (unlikely(_mm256_movemask_epi8(v) != 0xFFFFFFFF)) { + return false; + } + v = p[0]; w = p[1]; + SSE_REASSOC_BARRIER(v, w); + v |= p[2]; w |= p[3]; + SSE_REASSOC_BARRIER(v, w); + v |= p[4]; w |= p[5]; + SSE_REASSOC_BARRIER(v, w); + v |= p[6]; w |= p[7]; + SSE_REASSOC_BARRIER(v, w); + v |= w; + } + + return _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, zero)) == 0xFFFFFFFF; +} +#endif /* CONFIG_AVX2_OPT */ + +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_zero_sse2, +#ifdef CONFIG_AVX2_OPT + buffer_zero_avx2, +#endif +}; + +static unsigned best_accel(void) +{ + unsigned info = cpuinfo_init(); + +#ifdef CONFIG_AVX2_OPT + if (info & CPUINFO_AVX2) { + return 2; + } +#endif + return info & CPUINFO_SSE2 ? 1 : 0; +} + +#else +# include "host/include/generic/host/bufferiszero.c.inc" +#endif diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h new file mode 100644 index 0000000000..9541a64da6 --- /dev/null +++ b/host/include/i386/host/cpuinfo.h @@ -0,0 +1,40 @@ +/* + * 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_OSXSAVE (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_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/bufferiszero.c.inc b/host/include/loongarch64/host/bufferiszero.c.inc new file mode 100644 index 0000000000..69891eac80 --- /dev/null +++ b/host/include/loongarch64/host/bufferiszero.c.inc @@ -0,0 +1,143 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * buffer_is_zero acceleration, loongarch64 version. + */ + +/* + * Builtins for LSX and LASX are introduced by gcc 14 and llvm 18, + * but as yet neither has support for attribute target, so neither + * is able to enable the optimization without globally enabling + * vector support. Since we want runtime detection, use assembly. + */ + +static bool buffer_is_zero_lsx(const void *buf, size_t len) +{ + const void *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16); + const void *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16) - (7 * 16); + const void *l = buf + len; + bool ret; + + asm("vld $vr0,%2,0\n\t" /* first: buf + 0 */ + "vld $vr1,%4,-16\n\t" /* last: buf + len - 16 */ + "vld $vr2,%3,0\n\t" /* e[0] */ + "vld $vr3,%3,16\n\t" /* e[1] */ + "vld $vr4,%3,32\n\t" /* e[2] */ + "vld $vr5,%3,48\n\t" /* e[3] */ + "vld $vr6,%3,64\n\t" /* e[4] */ + "vld $vr7,%3,80\n\t" /* e[5] */ + "vld $vr8,%3,96\n\t" /* e[6] */ + "vor.v $vr0,$vr0,$vr1\n\t" + "vor.v $vr2,$vr2,$vr3\n\t" + "vor.v $vr4,$vr4,$vr5\n\t" + "vor.v $vr6,$vr6,$vr7\n\t" + "vor.v $vr0,$vr0,$vr2\n\t" + "vor.v $vr4,$vr4,$vr6\n\t" + "vor.v $vr0,$vr0,$vr4\n\t" + "vor.v $vr0,$vr0,$vr8\n\t" + "or %0,$r0,$r0\n" /* prepare return false */ + "1:\n\t" + "vsetnez.v $fcc0,$vr0\n\t" + "bcnez $fcc0,2f\n\t" + "vld $vr0,%1,0\n\t" /* p[0] */ + "vld $vr1,%1,16\n\t" /* p[1] */ + "vld $vr2,%1,32\n\t" /* p[2] */ + "vld $vr3,%1,48\n\t" /* p[3] */ + "vld $vr4,%1,64\n\t" /* p[4] */ + "vld $vr5,%1,80\n\t" /* p[5] */ + "vld $vr6,%1,96\n\t" /* p[6] */ + "vld $vr7,%1,112\n\t" /* p[7] */ + "addi.d %1,%1,128\n\t" + "vor.v $vr0,$vr0,$vr1\n\t" + "vor.v $vr2,$vr2,$vr3\n\t" + "vor.v $vr4,$vr4,$vr5\n\t" + "vor.v $vr6,$vr6,$vr7\n\t" + "vor.v $vr0,$vr0,$vr2\n\t" + "vor.v $vr4,$vr4,$vr6\n\t" + "vor.v $vr0,$vr0,$vr4\n\t" + "bltu %1,%3,1b\n\t" + "vsetnez.v $fcc0,$vr0\n\t" + "bcnez $fcc0,2f\n\t" + "ori %0,$r0,1\n" + "2:" + : "=&r"(ret), "+r"(p) + : "r"(buf), "r"(e), "r"(l) + : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + + return ret; +} + +static bool buffer_is_zero_lasx(const void *buf, size_t len) +{ + const void *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32); + const void *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32) - (7 * 32); + const void *l = buf + len; + bool ret; + + asm("xvld $xr0,%2,0\n\t" /* first: buf + 0 */ + "xvld $xr1,%4,-32\n\t" /* last: buf + len - 32 */ + "xvld $xr2,%3,0\n\t" /* e[0] */ + "xvld $xr3,%3,32\n\t" /* e[1] */ + "xvld $xr4,%3,64\n\t" /* e[2] */ + "xvld $xr5,%3,96\n\t" /* e[3] */ + "xvld $xr6,%3,128\n\t" /* e[4] */ + "xvld $xr7,%3,160\n\t" /* e[5] */ + "xvld $xr8,%3,192\n\t" /* e[6] */ + "xvor.v $xr0,$xr0,$xr1\n\t" + "xvor.v $xr2,$xr2,$xr3\n\t" + "xvor.v $xr4,$xr4,$xr5\n\t" + "xvor.v $xr6,$xr6,$xr7\n\t" + "xvor.v $xr0,$xr0,$xr2\n\t" + "xvor.v $xr4,$xr4,$xr6\n\t" + "xvor.v $xr0,$xr0,$xr4\n\t" + "xvor.v $xr0,$xr0,$xr8\n\t" + "or %0,$r0,$r0\n\t" /* prepare return false */ + "bgeu %1,%3,2f\n" + "1:\n\t" + "xvsetnez.v $fcc0,$xr0\n\t" + "bcnez $fcc0,3f\n\t" + "xvld $xr0,%1,0\n\t" /* p[0] */ + "xvld $xr1,%1,32\n\t" /* p[1] */ + "xvld $xr2,%1,64\n\t" /* p[2] */ + "xvld $xr3,%1,96\n\t" /* p[3] */ + "xvld $xr4,%1,128\n\t" /* p[4] */ + "xvld $xr5,%1,160\n\t" /* p[5] */ + "xvld $xr6,%1,192\n\t" /* p[6] */ + "xvld $xr7,%1,224\n\t" /* p[7] */ + "addi.d %1,%1,256\n\t" + "xvor.v $xr0,$xr0,$xr1\n\t" + "xvor.v $xr2,$xr2,$xr3\n\t" + "xvor.v $xr4,$xr4,$xr5\n\t" + "xvor.v $xr6,$xr6,$xr7\n\t" + "xvor.v $xr0,$xr0,$xr2\n\t" + "xvor.v $xr4,$xr4,$xr6\n\t" + "xvor.v $xr0,$xr0,$xr4\n\t" + "bltu %1,%3,1b\n" + "2:\n\t" + "xvsetnez.v $fcc0,$xr0\n\t" + "bcnez $fcc0,3f\n\t" + "ori %0,$r0,1\n" + "3:" + : "=&r"(ret), "+r"(p) + : "r"(buf), "r"(e), "r"(l) + : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + + return ret; +} + +static biz_accel_fn const accel_table[] = { + buffer_is_zero_int_ge256, + buffer_is_zero_lsx, + buffer_is_zero_lasx, +}; + +static unsigned best_accel(void) +{ + unsigned info = cpuinfo_init(); + if (info & CPUINFO_LASX) { + return 2; + } + if (info & CPUINFO_LSX) { + return 1; + } + return 0; +} diff --git a/host/include/loongarch64/host/cpuinfo.h b/host/include/loongarch64/host/cpuinfo.h new file mode 100644 index 0000000000..d7bf27501d --- /dev/null +++ b/host/include/loongarch64/host/cpuinfo.h @@ -0,0 +1,22 @@ +/* + * 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) +#define CPUINFO_LASX (1u << 2) + +/* 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.inc b/host/include/loongarch64/host/load-extract-al16-al8.h.inc new file mode 100644 index 0000000000..d1fb59d8af --- /dev/null +++ b/host/include/loongarch64/host/load-extract-al16-al8.h.inc @@ -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.inc b/host/include/loongarch64/host/store-insert-al16.h.inc new file mode 100644 index 0000000000..919fd8d744 --- /dev/null +++ b/host/include/loongarch64/host/store-insert-al16.h.inc @@ -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/riscv/host/cpuinfo.h b/host/include/riscv/host/cpuinfo.h new file mode 100644 index 0000000000..cdc784e7b6 --- /dev/null +++ b/host/include/riscv/host/cpuinfo.h @@ -0,0 +1,25 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for RISC-V. + */ + +#ifndef HOST_CPUINFO_H +#define HOST_CPUINFO_H + +#define CPUINFO_ALWAYS (1u << 0) /* so cpuinfo is nonzero */ +#define CPUINFO_ZBA (1u << 1) +#define CPUINFO_ZBB (1u << 2) +#define CPUINFO_ZICOND (1u << 3) +#define CPUINFO_ZVE64X (1u << 4) + +/* Initialized with a constructor. */ +extern unsigned cpuinfo; +extern unsigned riscv_lg2_vlenb; + +/* + * 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/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/bufferiszero.c.inc b/host/include/x86_64/host/bufferiszero.c.inc new file mode 100644 index 0000000000..1d3f1fd6f5 --- /dev/null +++ b/host/include/x86_64/host/bufferiszero.c.inc @@ -0,0 +1 @@ +#include "host/include/i386/host/bufferiszero.c.inc" 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.inc b/host/include/x86_64/host/load-extract-al16-al8.h.inc new file mode 100644 index 0000000000..baa506b7b5 --- /dev/null +++ b/host/include/x86_64/host/load-extract-al16-al8.h.inc @@ -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 deleted file mode 100644 index 99d115ff0d..0000000000 --- a/hw/9pfs/9p-proxy.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * 9p Proxy callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * This work is licensed under the terms of the GNU GPL, version 2. 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 -#include -#include "9p.h" -#include "qapi/error.h" -#include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "fsdev/qemu-fsdev.h" -#include "9p-proxy.h" - -typedef struct V9fsProxy { - int sockfd; - QemuMutex mutex; - struct iovec in_iovec; - struct iovec out_iovec; -} V9fsProxy; - -/* - * Return received file descriptor on success in *status. - * errno is also returned on *status (which will be < 0) - * return < 0 on transport error. - */ -static int v9fs_receivefd(int sockfd, int *status) -{ - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - int retval, data, fd; - union MsgControl msg_control; - - iov.iov_base = &data; - iov.iov_len = sizeof(data); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); - - do { - retval = recvmsg(sockfd, &msg, 0); - } while (retval < 0 && errno == EINTR); - if (retval <= 0) { - return retval; - } - /* - * data is set to V9FS_FD_VALID, if ancillary data is sent. If this - * request doesn't need ancillary data (fd) or an error occurred, - * data is set to negative errno value. - */ - if (data != V9FS_FD_VALID) { - *status = data; - return 0; - } - /* - * File descriptor (fd) is sent in the ancillary data. Check if we - * indeed received it. One of the reasons to fail to receive it is if - * we exceeded the maximum number of file descriptors! - */ - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - continue; - } - fd = *((int *)CMSG_DATA(cmsg)); - *status = fd; - return 0; - } - *status = -ENFILE; /* Ancillary data sent but not received */ - return 0; -} - -static ssize_t socket_read(int sockfd, void *buff, size_t size) -{ - ssize_t retval, total = 0; - - while (size) { - retval = read(sockfd, buff, size); - if (retval == 0) { - return -EIO; - } - if (retval < 0) { - if (errno == EINTR) { - continue; - } - return -errno; - } - size -= retval; - buff += retval; - total += retval; - } - return total; -} - -/* Converts proxy_statfs to VFS statfs structure */ -static void prstatfs_to_statfs(struct statfs *stfs, ProxyStatFS *prstfs) -{ - memset(stfs, 0, sizeof(*stfs)); - stfs->f_type = prstfs->f_type; - stfs->f_bsize = prstfs->f_bsize; - stfs->f_blocks = prstfs->f_blocks; - stfs->f_bfree = prstfs->f_bfree; - stfs->f_bavail = prstfs->f_bavail; - stfs->f_files = prstfs->f_files; - stfs->f_ffree = prstfs->f_ffree; -#ifdef CONFIG_DARWIN - /* f_namelen and f_frsize do not exist on Darwin */ - stfs->f_fsid.val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; - stfs->f_fsid.val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; -#else - stfs->f_fsid.__val[0] = prstfs->f_fsid[0] & 0xFFFFFFFFU; - stfs->f_fsid.__val[1] = prstfs->f_fsid[1] >> 32 & 0xFFFFFFFFU; - stfs->f_namelen = prstfs->f_namelen; - stfs->f_frsize = prstfs->f_frsize; -#endif -} - -/* Converts proxy_stat structure to VFS stat structure */ -static void prstat_to_stat(struct stat *stbuf, ProxyStat *prstat) -{ - memset(stbuf, 0, sizeof(*stbuf)); - stbuf->st_dev = prstat->st_dev; - stbuf->st_ino = prstat->st_ino; - stbuf->st_nlink = prstat->st_nlink; - stbuf->st_mode = prstat->st_mode; - stbuf->st_uid = prstat->st_uid; - stbuf->st_gid = prstat->st_gid; - stbuf->st_rdev = prstat->st_rdev; - stbuf->st_size = prstat->st_size; - stbuf->st_blksize = prstat->st_blksize; - stbuf->st_blocks = prstat->st_blocks; - stbuf->st_atime = prstat->st_atim_sec; - stbuf->st_mtime = prstat->st_mtim_sec; - stbuf->st_ctime = prstat->st_ctim_sec; -#ifdef CONFIG_DARWIN - stbuf->st_atimespec.tv_sec = prstat->st_atim_sec; - stbuf->st_mtimespec.tv_sec = prstat->st_mtim_sec; - stbuf->st_ctimespec.tv_sec = prstat->st_ctim_sec; - stbuf->st_atimespec.tv_nsec = prstat->st_atim_nsec; - stbuf->st_mtimespec.tv_nsec = prstat->st_mtim_nsec; - stbuf->st_ctimespec.tv_nsec = prstat->st_ctim_nsec; -#else - stbuf->st_atim.tv_sec = prstat->st_atim_sec; - stbuf->st_mtim.tv_sec = prstat->st_mtim_sec; - stbuf->st_ctim.tv_sec = prstat->st_ctim_sec; - stbuf->st_atim.tv_nsec = prstat->st_atim_nsec; - stbuf->st_mtim.tv_nsec = prstat->st_mtim_nsec; - stbuf->st_ctim.tv_nsec = prstat->st_ctim_nsec; -#endif -} - -/* - * Response contains two parts - * {header, data} - * header.type == T_ERROR, data -> -errno - * header.type == T_SUCCESS, data -> response - * size of errno/response is given by header.size - * returns < 0, on transport error. response is - * valid only if status >= 0. - */ -static int v9fs_receive_response(V9fsProxy *proxy, int type, - int *status, void *response) -{ - int retval; - ProxyHeader header; - struct iovec *reply = &proxy->in_iovec; - - *status = 0; - reply->iov_len = 0; - retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - reply->iov_len = PROXY_HDR_SZ; - retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); - assert(retval == 4 * 2); - /* - * if response size > PROXY_MAX_IO_SZ, read the response but ignore it and - * return -ENOBUFS - */ - if (header.size > PROXY_MAX_IO_SZ) { - int count; - while (header.size > 0) { - count = MIN(PROXY_MAX_IO_SZ, header.size); - count = socket_read(proxy->sockfd, reply->iov_base, count); - if (count < 0) { - return count; - } - header.size -= count; - } - *status = -ENOBUFS; - return 0; - } - - retval = socket_read(proxy->sockfd, - reply->iov_base + PROXY_HDR_SZ, header.size); - if (retval < 0) { - return retval; - } - reply->iov_len += header.size; - /* there was an error during processing request */ - if (header.type == T_ERROR) { - int ret; - ret = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); - assert(ret == 4); - return 0; - } - - switch (type) { - case T_LSTAT: { - ProxyStat prstat; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, - "qqqdddqqqqqqqqqq", &prstat.st_dev, - &prstat.st_ino, &prstat.st_nlink, - &prstat.st_mode, &prstat.st_uid, - &prstat.st_gid, &prstat.st_rdev, - &prstat.st_size, &prstat.st_blksize, - &prstat.st_blocks, - &prstat.st_atim_sec, &prstat.st_atim_nsec, - &prstat.st_mtim_sec, &prstat.st_mtim_nsec, - &prstat.st_ctim_sec, &prstat.st_ctim_nsec); - assert(retval == 8 * 3 + 4 * 3 + 8 * 10); - prstat_to_stat(response, &prstat); - break; - } - case T_STATFS: { - ProxyStatFS prstfs; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, - "qqqqqqqqqqq", &prstfs.f_type, - &prstfs.f_bsize, &prstfs.f_blocks, - &prstfs.f_bfree, &prstfs.f_bavail, - &prstfs.f_files, &prstfs.f_ffree, - &prstfs.f_fsid[0], &prstfs.f_fsid[1], - &prstfs.f_namelen, &prstfs.f_frsize); - assert(retval == 8 * 11); - prstatfs_to_statfs(response, &prstfs); - break; - } - case T_READLINK: { - V9fsString target; - v9fs_string_init(&target); - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &target); - strcpy(response, target.data); - v9fs_string_free(&target); - break; - } - case T_LGETXATTR: - case T_LLISTXATTR: { - V9fsString xattr; - v9fs_string_init(&xattr); - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "s", &xattr); - memcpy(response, xattr.data, xattr.size); - v9fs_string_free(&xattr); - break; - } - case T_GETVERSION: - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "q", response); - assert(retval == 8); - break; - default: - return -1; - } - if (retval < 0) { - *status = retval; - } - return 0; -} - -/* - * return < 0 on transport error. - * *status is valid only if return >= 0 - */ -static int v9fs_receive_status(V9fsProxy *proxy, - struct iovec *reply, int *status) -{ - int retval; - ProxyHeader header; - - *status = 0; - reply->iov_len = 0; - retval = socket_read(proxy->sockfd, reply->iov_base, PROXY_HDR_SZ); - if (retval < 0) { - return retval; - } - reply->iov_len = PROXY_HDR_SZ; - retval = proxy_unmarshal(reply, 0, "dd", &header.type, &header.size); - assert(retval == 4 * 2); - retval = socket_read(proxy->sockfd, - reply->iov_base + PROXY_HDR_SZ, header.size); - if (retval < 0) { - return retval; - } - reply->iov_len += header.size; - retval = proxy_unmarshal(reply, PROXY_HDR_SZ, "d", status); - assert(retval == 4); - return 0; -} - -/* - * Proxy->header and proxy->request written to socket by QEMU process. - * This request read by proxy helper process - * returns 0 on success and -errno on error - */ -static int v9fs_request(V9fsProxy *proxy, int type, void *response, ...) -{ - dev_t rdev; - va_list ap; - int size = 0; - int retval = 0; - uint64_t offset; - ProxyHeader header = { 0, 0}; - struct timespec spec[2]; - int flags, mode, uid, gid; - V9fsString *name, *value; - V9fsString *path, *oldpath; - struct iovec *iovec = NULL, *reply = NULL; - - qemu_mutex_lock(&proxy->mutex); - - if (proxy->sockfd == -1) { - retval = -EIO; - goto err_out; - } - iovec = &proxy->out_iovec; - reply = &proxy->in_iovec; - va_start(ap, response); - switch (type) { - case T_OPEN: - path = va_arg(ap, V9fsString *); - flags = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, flags); - if (retval > 0) { - header.size = retval; - header.type = T_OPEN; - } - break; - case T_CREATE: - path = va_arg(ap, V9fsString *); - flags = va_arg(ap, int); - mode = va_arg(ap, int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdddd", path, - flags, mode, uid, gid); - if (retval > 0) { - header.size = retval; - header.type = T_CREATE; - } - break; - case T_MKNOD: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - rdev = va_arg(ap, long int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsdq", - uid, gid, path, mode, rdev); - if (retval > 0) { - header.size = retval; - header.type = T_MKNOD; - } - break; - case T_MKDIR: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddsd", - uid, gid, path, mode); - if (retval > 0) { - header.size = retval; - header.type = T_MKDIR; - } - break; - case T_SYMLINK: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ddss", - uid, gid, oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_SYMLINK; - } - break; - case T_LINK: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", - oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_LINK; - } - break; - case T_LSTAT: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_LSTAT; - } - break; - case T_READLINK: - path = va_arg(ap, V9fsString *); - size = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, size); - if (retval > 0) { - header.size = retval; - header.type = T_READLINK; - } - break; - case T_STATFS: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_STATFS; - } - break; - case T_CHMOD: - path = va_arg(ap, V9fsString *); - mode = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sd", path, mode); - if (retval > 0) { - header.size = retval; - header.type = T_CHMOD; - } - break; - case T_CHOWN: - path = va_arg(ap, V9fsString *); - uid = va_arg(ap, int); - gid = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sdd", path, uid, gid); - if (retval > 0) { - header.size = retval; - header.type = T_CHOWN; - } - break; - case T_TRUNCATE: - path = va_arg(ap, V9fsString *); - offset = va_arg(ap, uint64_t); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sq", path, offset); - if (retval > 0) { - header.size = retval; - header.type = T_TRUNCATE; - } - break; - case T_UTIME: - path = va_arg(ap, V9fsString *); - spec[0].tv_sec = va_arg(ap, long); - spec[0].tv_nsec = va_arg(ap, long); - spec[1].tv_sec = va_arg(ap, long); - spec[1].tv_nsec = va_arg(ap, long); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sqqqq", path, - spec[0].tv_sec, spec[1].tv_nsec, - spec[1].tv_sec, spec[1].tv_nsec); - if (retval > 0) { - header.size = retval; - header.type = T_UTIME; - } - break; - case T_RENAME: - oldpath = va_arg(ap, V9fsString *); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", oldpath, path); - if (retval > 0) { - header.size = retval; - header.type = T_RENAME; - } - break; - case T_REMOVE: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_REMOVE; - } - break; - case T_LGETXATTR: - size = va_arg(ap, int); - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, - "dss", size, path, name); - if (retval > 0) { - header.size = retval; - header.type = T_LGETXATTR; - } - break; - case T_LLISTXATTR: - size = va_arg(ap, int); - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ds", size, path); - if (retval > 0) { - header.size = retval; - header.type = T_LLISTXATTR; - } - break; - case T_LSETXATTR: - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - value = va_arg(ap, V9fsString *); - size = va_arg(ap, int); - flags = va_arg(ap, int); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "sssdd", - path, name, value, size, flags); - if (retval > 0) { - header.size = retval; - header.type = T_LSETXATTR; - } - break; - case T_LREMOVEXATTR: - path = va_arg(ap, V9fsString *); - name = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "ss", path, name); - if (retval > 0) { - header.size = retval; - header.type = T_LREMOVEXATTR; - } - break; - case T_GETVERSION: - path = va_arg(ap, V9fsString *); - retval = proxy_marshal(iovec, PROXY_HDR_SZ, "s", path); - if (retval > 0) { - header.size = retval; - header.type = T_GETVERSION; - } - break; - default: - error_report("Invalid type %d", type); - retval = -EINVAL; - break; - } - va_end(ap); - - if (retval < 0) { - goto err_out; - } - - /* marshal the header details */ - retval = proxy_marshal(iovec, 0, "dd", header.type, header.size); - assert(retval == 4 * 2); - header.size += PROXY_HDR_SZ; - - retval = qemu_write_full(proxy->sockfd, iovec->iov_base, header.size); - if (retval != header.size) { - goto close_error; - } - - switch (type) { - case T_OPEN: - case T_CREATE: - /* - * A file descriptor is returned as response for - * T_OPEN,T_CREATE on success - */ - if (v9fs_receivefd(proxy->sockfd, &retval) < 0) { - goto close_error; - } - break; - case T_MKNOD: - case T_MKDIR: - case T_SYMLINK: - case T_LINK: - case T_CHMOD: - case T_CHOWN: - case T_RENAME: - case T_TRUNCATE: - case T_UTIME: - case T_REMOVE: - case T_LSETXATTR: - case T_LREMOVEXATTR: - if (v9fs_receive_status(proxy, reply, &retval) < 0) { - goto close_error; - } - break; - case T_LSTAT: - case T_READLINK: - case T_STATFS: - case T_GETVERSION: - if (v9fs_receive_response(proxy, type, &retval, response) < 0) { - goto close_error; - } - break; - case T_LGETXATTR: - case T_LLISTXATTR: - if (!size) { - if (v9fs_receive_status(proxy, reply, &retval) < 0) { - goto close_error; - } - } else { - if (v9fs_receive_response(proxy, type, &retval, response) < 0) { - goto close_error; - } - } - break; - } - -err_out: - qemu_mutex_unlock(&proxy->mutex); - return retval; - -close_error: - close(proxy->sockfd); - proxy->sockfd = -1; - qemu_mutex_unlock(&proxy->mutex); - return -EIO; -} - -static int proxy_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_LSTAT, stbuf, fs_path); - if (retval < 0) { - errno = -retval; - return -1; - } - return retval; -} - -static ssize_t proxy_readlink(FsContext *fs_ctx, V9fsPath *fs_path, - char *buf, size_t bufsz) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_READLINK, buf, fs_path, bufsz); - if (retval < 0) { - errno = -retval; - return -1; - } - return strlen(buf); -} - -static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs) -{ - return close(fs->fd); -} - -static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return closedir(fs->dir.stream); -} - -static int proxy_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - fs->fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, flags); - if (fs->fd < 0) { - errno = -fs->fd; - fs->fd = -1; - } - return fs->fd; -} - -static int proxy_opendir(FsContext *ctx, - V9fsPath *fs_path, V9fsFidOpenState *fs) -{ - int serrno, fd; - - fs->dir.stream = NULL; - fd = v9fs_request(ctx->private, T_OPEN, NULL, fs_path, O_DIRECTORY); - if (fd < 0) { - errno = -fd; - return -1; - } - fs->dir.stream = fdopendir(fd); - if (!fs->dir.stream) { - serrno = errno; - close(fd); - errno = serrno; - return -1; - } - return 0; -} - -static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir.stream); -} - -static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir.stream); -} - -static struct dirent *proxy_readdir(FsContext *ctx, V9fsFidOpenState *fs) -{ - struct dirent *entry; - entry = readdir(fs->dir.stream); -#ifdef CONFIG_DARWIN - if (!entry) { - return NULL; - } - int td; - td = telldir(fs->dir.stream); - /* If telldir fails, fail the entire readdir call */ - if (td < 0) { - return NULL; - } - entry->d_seekoff = td; -#endif - return entry; -} - -static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir.stream, off); -} - -static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; -#ifdef CONFIG_PREADV - ret = preadv(fs->fd, iov, iovcnt, offset); -#else - ret = lseek(fs->fd, offset, SEEK_SET); - if (ret >= 0) { - ret = readv(fs->fd, iov, iovcnt); - } -#endif - return ret; -} - -static ssize_t proxy_pwritev(FsContext *ctx, V9fsFidOpenState *fs, - const struct iovec *iov, - int iovcnt, off_t offset) -{ - ssize_t ret; - -#ifdef CONFIG_PREADV - ret = pwritev(fs->fd, iov, iovcnt, offset); -#else - ret = lseek(fs->fd, offset, SEEK_SET); - if (ret >= 0) { - ret = writev(fs->fd, iov, iovcnt); - } -#endif -#ifdef CONFIG_SYNC_FILE_RANGE - if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { - /* - * 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. - */ - sync_file_range(fs->fd, offset, ret, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); - } -#endif - return ret; -} - -static int proxy_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_CHMOD, NULL, fs_path, - credp->fc_mode); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_mknod(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - retval = v9fs_request(fs_ctx->private, T_MKNOD, NULL, &fullname, - credp->fc_mode, credp->fc_rdev, - credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, - const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - retval = v9fs_request(fs_ctx->private, T_MKDIR, NULL, &fullname, - credp->fc_mode, credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); - } else { - fd = fs->fd; - } - return fstat(fd, stbuf); -} - -static int proxy_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, - int flags, FsCred *credp, V9fsFidOpenState *fs) -{ - V9fsString fullname; - - v9fs_string_init(&fullname); - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - - fs->fd = v9fs_request(fs_ctx->private, T_CREATE, NULL, &fullname, flags, - credp->fc_mode, credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - if (fs->fd < 0) { - errno = -fs->fd; - fs->fd = -1; - } - return fs->fd; -} - -static int proxy_symlink(FsContext *fs_ctx, const char *oldpath, - V9fsPath *dir_path, const char *name, FsCred *credp) -{ - int retval; - V9fsString fullname, target; - - v9fs_string_init(&fullname); - v9fs_string_init(&target); - - v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); - v9fs_string_sprintf(&target, "%s", oldpath); - - retval = v9fs_request(fs_ctx->private, T_SYMLINK, NULL, &target, &fullname, - credp->fc_uid, credp->fc_gid); - v9fs_string_free(&fullname); - v9fs_string_free(&target); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_link(FsContext *ctx, V9fsPath *oldpath, - V9fsPath *dirpath, const char *name) -{ - int retval; - V9fsString newpath; - - v9fs_string_init(&newpath); - v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); - - retval = v9fs_request(ctx->private, T_LINK, NULL, oldpath, &newpath); - v9fs_string_free(&newpath); - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} - -static int proxy_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) -{ - int retval; - - retval = v9fs_request(ctx->private, T_TRUNCATE, NULL, fs_path, size); - if (retval < 0) { - errno = -retval; - return -1; - } - return 0; -} - -static int proxy_rename(FsContext *ctx, const char *oldpath, - const char *newpath) -{ - int retval; - V9fsString oldname, newname; - - v9fs_string_init(&oldname); - v9fs_string_init(&newname); - - v9fs_string_sprintf(&oldname, "%s", oldpath); - v9fs_string_sprintf(&newname, "%s", newpath); - retval = v9fs_request(ctx->private, T_RENAME, NULL, &oldname, &newname); - v9fs_string_free(&oldname); - v9fs_string_free(&newname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) -{ - int retval; - retval = v9fs_request(fs_ctx->private, T_CHOWN, NULL, fs_path, - credp->fc_uid, credp->fc_gid); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_utimensat(FsContext *s, V9fsPath *fs_path, - const struct timespec *buf) -{ - int retval; - retval = v9fs_request(s->private, T_UTIME, NULL, fs_path, - buf[0].tv_sec, buf[0].tv_nsec, - buf[1].tv_sec, buf[1].tv_nsec); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_remove(FsContext *ctx, const char *path) -{ - int retval; - V9fsString name; - v9fs_string_init(&name); - v9fs_string_sprintf(&name, "%s", path); - retval = v9fs_request(ctx->private, T_REMOVE, NULL, &name); - v9fs_string_free(&name); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_fsync(FsContext *ctx, int fid_type, - V9fsFidOpenState *fs, int datasync) -{ - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); - } else { - fd = fs->fd; - } - - if (datasync) { - return qemu_fdatasync(fd); - } else { - return fsync(fd); - } -} - -static int proxy_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) -{ - int retval; - retval = v9fs_request(s->private, T_STATFS, stbuf, fs_path); - if (retval < 0) { - errno = -retval; - return -1; - } - return retval; -} - -static ssize_t proxy_lgetxattr(FsContext *ctx, V9fsPath *fs_path, - const char *name, void *value, size_t size) -{ - int retval; - V9fsString xname; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - retval = v9fs_request(ctx->private, T_LGETXATTR, value, size, fs_path, - &xname); - v9fs_string_free(&xname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static ssize_t proxy_llistxattr(FsContext *ctx, V9fsPath *fs_path, - void *value, size_t size) -{ - int retval; - retval = v9fs_request(ctx->private, T_LLISTXATTR, value, size, fs_path); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, - void *value, size_t size, int flags) -{ - int retval; - V9fsString xname, xvalue; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - - v9fs_string_init(&xvalue); - xvalue.size = size; - xvalue.data = g_malloc(size); - memcpy(xvalue.data, value, size); - - retval = v9fs_request(ctx->private, T_LSETXATTR, value, fs_path, &xname, - &xvalue, size, flags); - v9fs_string_free(&xname); - v9fs_string_free(&xvalue); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_lremovexattr(FsContext *ctx, V9fsPath *fs_path, - const char *name) -{ - int retval; - V9fsString xname; - - v9fs_string_init(&xname); - v9fs_string_sprintf(&xname, "%s", name); - retval = v9fs_request(ctx->private, T_LREMOVEXATTR, NULL, fs_path, &xname); - v9fs_string_free(&xname); - if (retval < 0) { - errno = -retval; - } - return retval; -} - -static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path, - const char *name, V9fsPath *target) -{ - if (dir_path) { - v9fs_path_sprintf(target, "%s/%s", dir_path->data, name); - } else { - v9fs_path_sprintf(target, "%s", name); - } - return 0; -} - -static int proxy_renameat(FsContext *ctx, V9fsPath *olddir, - const char *old_name, V9fsPath *newdir, - const char *new_name) -{ - int ret; - V9fsString old_full_name, new_full_name; - - v9fs_string_init(&old_full_name); - v9fs_string_init(&new_full_name); - - v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); - v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); - - ret = proxy_rename(ctx, old_full_name.data, new_full_name.data); - v9fs_string_free(&old_full_name); - v9fs_string_free(&new_full_name); - return ret; -} - -static int proxy_unlinkat(FsContext *ctx, V9fsPath *dir, - const char *name, int flags) -{ - int ret; - V9fsString fullname; - v9fs_string_init(&fullname); - - v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); - ret = proxy_remove(ctx, fullname.data); - v9fs_string_free(&fullname); - - return ret; -} - -static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, - mode_t st_mode, uint64_t *st_gen) -{ - int err; - - /* Do not try to open special files like device nodes, fifos etc - * we can get fd for regular files and directories only - */ - if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { - errno = ENOTTY; - return -1; - } - err = v9fs_request(fs_ctx->private, T_GETVERSION, st_gen, path); - if (err < 0) { - errno = -err; - err = -1; - } - return err; -} - -static int connect_namedsocket(const char *path, Error **errp) -{ - int sockfd; - struct sockaddr_un helper; - - if (strlen(path) >= sizeof(helper.sun_path)) { - error_setg(errp, "socket name too long"); - return -1; - } - sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sockfd < 0) { - error_setg_errno(errp, errno, "failed to create client socket"); - return -1; - } - strcpy(helper.sun_path, path); - helper.sun_family = AF_UNIX; - if (connect(sockfd, (struct sockaddr *)&helper, sizeof(helper)) < 0) { - error_setg_errno(errp, errno, "failed to connect to '%s'", path); - close(sockfd); - return -1; - } - - /* remove the socket for security reasons */ - unlink(path); - return sockfd; -} - -static void error_append_socket_sockfd_hint(Error *const *errp) -{ - error_append_hint(errp, "Either specify socket=/some/path where /some/path" - " points to a listening AF_UNIX socket or sock_fd=fd" - " where fd is a file descriptor to a connected AF_UNIX" - " socket\n"); -} - -static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp) -{ - const char *socket = qemu_opt_get(opts, "socket"); - const char *sock_fd = qemu_opt_get(opts, "sock_fd"); - - if (!socket && !sock_fd) { - error_setg(errp, "both socket and sock_fd properties are missing"); - error_append_socket_sockfd_hint(errp); - return -1; - } - if (socket && sock_fd) { - error_setg(errp, "both socket and sock_fd properties are set"); - error_append_socket_sockfd_hint(errp); - return -1; - } - if (socket) { - fs->path = g_strdup(socket); - fs->export_flags |= V9FS_PROXY_SOCK_NAME; - } else { - fs->path = g_strdup(sock_fd); - fs->export_flags |= V9FS_PROXY_SOCK_FD; - } - return 0; -} - -static int proxy_init(FsContext *ctx, Error **errp) -{ - V9fsProxy *proxy = g_new(V9fsProxy, 1); - int sock_id; - - if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { - sock_id = connect_namedsocket(ctx->fs_root, errp); - } else { - sock_id = atoi(ctx->fs_root); - if (sock_id < 0) { - error_setg(errp, "socket descriptor not initialized"); - } - } - if (sock_id < 0) { - g_free(proxy); - return -1; - } - g_free(ctx->fs_root); - ctx->fs_root = NULL; - - proxy->in_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - proxy->in_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - proxy->out_iovec.iov_base = g_malloc(PROXY_MAX_IO_SZ + PROXY_HDR_SZ); - proxy->out_iovec.iov_len = PROXY_MAX_IO_SZ + PROXY_HDR_SZ; - - ctx->private = proxy; - proxy->sockfd = sock_id; - qemu_mutex_init(&proxy->mutex); - - ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; - ctx->exops.get_st_gen = proxy_ioc_getversion; - return 0; -} - -static void proxy_cleanup(FsContext *ctx) -{ - V9fsProxy *proxy = ctx->private; - - if (!proxy) { - return; - } - - g_free(proxy->out_iovec.iov_base); - g_free(proxy->in_iovec.iov_base); - if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { - close(proxy->sockfd); - } - g_free(proxy); -} - -FileOperations proxy_ops = { - .parse_opts = proxy_parse_opts, - .init = proxy_init, - .cleanup = proxy_cleanup, - .lstat = proxy_lstat, - .readlink = proxy_readlink, - .close = proxy_close, - .closedir = proxy_closedir, - .open = proxy_open, - .opendir = proxy_opendir, - .rewinddir = proxy_rewinddir, - .telldir = proxy_telldir, - .readdir = proxy_readdir, - .seekdir = proxy_seekdir, - .preadv = proxy_preadv, - .pwritev = proxy_pwritev, - .chmod = proxy_chmod, - .mknod = proxy_mknod, - .mkdir = proxy_mkdir, - .fstat = proxy_fstat, - .open2 = proxy_open2, - .symlink = proxy_symlink, - .link = proxy_link, - .truncate = proxy_truncate, - .rename = proxy_rename, - .chown = proxy_chown, - .utimensat = proxy_utimensat, - .remove = proxy_remove, - .fsync = proxy_fsync, - .statfs = proxy_statfs, - .lgetxattr = proxy_lgetxattr, - .llistxattr = proxy_llistxattr, - .lsetxattr = proxy_lsetxattr, - .lremovexattr = proxy_lremovexattr, - .name_to_path = proxy_name_to_path, - .renameat = proxy_renameat, - .unlinkat = proxy_unlinkat, -}; diff --git a/hw/9pfs/9p-proxy.h b/hw/9pfs/9p-proxy.h deleted file mode 100644 index b84301d001..0000000000 --- a/hw/9pfs/9p-proxy.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 9p Proxy callback - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * M. Mohan Kumar - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ - -#ifndef QEMU_9P_PROXY_H -#define QEMU_9P_PROXY_H - -#define PROXY_MAX_IO_SZ (64 * 1024) -#define V9FS_FD_VALID INT_MAX - -/* - * proxy iovec only support one element and - * marsha/unmarshal doesn't do little endian conversion. - */ -#define proxy_unmarshal(in_sg, offset, fmt, args...) \ - v9fs_iov_unmarshal(in_sg, 1, offset, 0, fmt, ##args) -#define proxy_marshal(out_sg, offset, fmt, args...) \ - v9fs_iov_marshal(out_sg, 1, offset, 0, fmt, ##args) - -union MsgControl { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(int))]; -}; - -typedef struct { - uint32_t type; - uint32_t size; -} ProxyHeader; - -#define PROXY_HDR_SZ (sizeof(ProxyHeader)) - -enum { - T_SUCCESS = 0, - T_ERROR, - T_OPEN, - T_CREATE, - T_MKNOD, - T_MKDIR, - T_SYMLINK, - T_LINK, - T_LSTAT, - T_READLINK, - T_STATFS, - T_CHMOD, - T_CHOWN, - T_TRUNCATE, - T_UTIME, - T_RENAME, - T_REMOVE, - T_LGETXATTR, - T_LLISTXATTR, - T_LSETXATTR, - T_LREMOVEXATTR, - T_GETVERSION, -}; - -typedef struct { - uint64_t st_dev; - uint64_t st_ino; - uint64_t st_nlink; - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - uint64_t st_rdev; - uint64_t st_size; - uint64_t st_blksize; - uint64_t st_blocks; - uint64_t st_atim_sec; - uint64_t st_atim_nsec; - uint64_t st_mtim_sec; - uint64_t st_mtim_nsec; - uint64_t st_ctim_sec; - uint64_t st_ctim_nsec; -} ProxyStat; - -typedef struct { - uint64_t f_type; - uint64_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - uint64_t f_fsid[2]; - uint64_t f_namelen; - uint64_t f_frsize; -} ProxyStatFS; -#endif 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 6b44e5f7a4..51c94b0116 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -48,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) @@ -92,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; @@ -129,7 +126,7 @@ static inline int close_if_special_file(int fd) { struct stat stbuf; - if (fstat(fd, &stbuf) < 0) { + if (qemu_fstat(fd, &stbuf) < 0) { close_preserve_errno(fd); return -1; } @@ -148,8 +145,8 @@ static inline int close_if_special_file(int fd) 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, @@ -160,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)) { diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 072cf67956..578517739a 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" @@ -408,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); } @@ -646,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 @@ -740,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) @@ -1042,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 @@ -1508,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; } @@ -1605,11 +1596,13 @@ static void coroutine_fn v9fs_getattr(void *opaque) retval = -ENOENT; goto out_nofid; } - /* - * Currently we only support BASIC fields in stat, so there is no - * need to look at request_mask. - */ - retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + if ((fidp->fid_type == P9_FID_FILE && fidp->fs.fd != -1) || + (fidp->fid_type == P9_FID_DIR && fidp->fs.dir.stream)) + { + retval = v9fs_co_fstat(pdu, fidp, &stbuf); + } else { + retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); + } if (retval < 0) { goto out; } @@ -2596,6 +2589,11 @@ static void coroutine_fn v9fs_readdir(void *opaque) retval = -EINVAL; goto out_nofid; } + if (fidp->fid_type != P9_FID_DIR) { + warn_report_once("9p: bad client: T_readdir on non-directory stream"); + retval = -ENOTDIR; + goto out; + } if (!fidp->fs.dir.stream) { retval = -EINVAL; goto out; diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 2fce4140d1..5e041e1f60 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); @@ -280,7 +280,6 @@ struct V9fsFidState { uid_t uid; int ref; bool clunked; - QSIMPLEQ_ENTRY(V9fsFidState) next; QSLIST_ENTRY(V9fsFidState) reclaim_next; }; @@ -304,7 +303,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. */ 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..eceffdb81e 100644 --- a/hw/9pfs/meson.build +++ b/hw/9pfs/meson.build @@ -2,7 +2,6 @@ fs_ss = ss.source_set() fs_ss.add(files( '9p-local.c', '9p-posix-acl.c', - '9p-proxy.c', '9p-synth.c', '9p-xattr-user.c', '9p-xattr.c', @@ -13,9 +12,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/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 ab1df8dd2f..79359d911a 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -22,6 +22,7 @@ #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" @@ -62,6 +63,7 @@ typedef struct Xen9pfsDev { int num_rings; Xen9pfsRing *rings; + MemReentrancyGuard mem_reentrancy_guard; } Xen9pfsDev; static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); @@ -243,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); } @@ -326,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); } @@ -341,22 +343,23 @@ static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) 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; } 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) { @@ -448,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); @@ -470,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"); @@ -508,7 +513,7 @@ static void xen_9pfs_alloc(struct XenLegacyDevice *xendev) xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_ORDER); } -struct XenDevOps xen_9pfs_ops = { +static const struct XenDevOps xen_9pfs_ops = { .size = sizeof(Xen9pfsDev), .flags = DEVOPS_FLAG_NEED_GNTDEV, .alloc = xen_9pfs_alloc, @@ -517,3 +522,9 @@ struct XenDevOps xen_9pfs_ops = { .disconnect = xen_9pfs_disconnect, .free = xen_9pfs_free, }; + +static void xen_9pfs_register_backend(void) +{ + xen_be_register("9pfs", &xen_9pfs_ops); +} +xen_backend_init(xen_9pfs_register_backend); diff --git a/hw/Kconfig b/hw/Kconfig index 38233bbb0f..1b4e9bb07f 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 @@ -26,9 +27,7 @@ source nvme/Kconfig source nvram/Kconfig source pci-bridge/Kconfig source pci-host/Kconfig -source pcmcia/Kconfig source pci/Kconfig -source rdma/Kconfig source remote/Kconfig source rtc/Kconfig source scsi/Kconfig @@ -38,23 +37,24 @@ 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 source arm/Kconfig +source cpu/Kconfig source alpha/Kconfig source avr/Kconfig -source cris/Kconfig source hppa/Kconfig source i386/Kconfig source loongarch/Kconfig source m68k/Kconfig source microblaze/Kconfig source mips/Kconfig -source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/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-cpu-hotplug-stub.c b/hw/acpi/acpi-cpu-hotplug-stub.c index 3fc4b14c26..c6c61bb9cd 100644 --- a/hw/acpi/acpi-cpu-hotplug-stub.c +++ b/hw/acpi/acpi-cpu-hotplug-stub.c @@ -19,6 +19,12 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, return; } +void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, + CPUHotplugState *state, hwaddr base_addr) +{ + return; +} + void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list) { return; 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_interface.c b/hw/acpi/acpi_interface.c index c668d361f6..8637ff18fc 100644 --- a/hw/acpi/acpi_interface.c +++ b/hw/acpi/acpi_interface.c @@ -2,6 +2,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/acpi/acpi_aml_interface.h" #include "qemu/module.h" +#include "qemu/queue.h" void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) { @@ -12,6 +13,15 @@ void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event) } } +void qbus_build_aml(BusState *bus, Aml *scope) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + call_dev_aml_func(DEVICE(kid->child), scope); + } +} + static void register_types(void) { static const TypeInfo acpi_dev_if_info = { diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 42feb4d4d7..72282b173e 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; } @@ -534,8 +534,7 @@ void aml_append(Aml *parent_ctx, Aml *child) case AML_NO_OPCODE: break; default: - assert(0); - break; + g_assert_not_reached(); } build_append_array(parent_ctx->buf, buf); build_free_array(buf); @@ -680,7 +679,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) @@ -1938,6 +1937,89 @@ void build_srat_memory(GArray *table_data, uint64_t base, build_append_int_noprefix(table_data, 0, 8); /* Reserved */ } +/* + * ACPI Spec Revision 6.3 + * Table 5-80 Device Handle - PCI + */ +static void build_append_srat_pci_device_handle(GArray *table_data, + uint16_t segment, + uint8_t bus, uint8_t devfn) +{ + /* PCI segment number */ + build_append_int_noprefix(table_data, segment, 2); + /* PCI Bus Device Function */ + build_append_int_noprefix(table_data, bus, 1); + build_append_int_noprefix(table_data, devfn, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 12); +} + +static void build_append_srat_acpi_device_handle(GArray *table_data, + const char *hid, + uint32_t uid) +{ + assert(strlen(hid) == 8); + /* Device Handle - ACPI */ + for (int i = 0; i < 8; i++) { + build_append_int_noprefix(table_data, hid[i], 1); + } + build_append_int_noprefix(table_data, uid, 4); + build_append_int_noprefix(table_data, 0, 4); +} + +/* + * ACPI spec, Revision 6.3 + * 5.2.16.6 Generic Initiator Affinity Structure + * With PCI Device Handle. + */ +void build_srat_pci_generic_initiator(GArray *table_data, uint32_t node, + uint16_t segment, uint8_t bus, + uint8_t devfn) +{ + /* Type */ + build_append_int_noprefix(table_data, 5, 1); + /* Length */ + build_append_int_noprefix(table_data, 32, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 1); + /* Device Handle Type: PCI */ + build_append_int_noprefix(table_data, 1, 1); + /* Proximity Domain */ + build_append_int_noprefix(table_data, node, 4); + /* Device Handle */ + build_append_srat_pci_device_handle(table_data, segment, bus, devfn); + /* Flags - GI Enabled */ + build_append_int_noprefix(table_data, 1, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); +} + +/* + * ACPI spec, Revision 6.5 + * 5.2.16.7 Generic Port Affinity Structure + * With ACPI Device Handle. + */ +void build_srat_acpi_generic_port(GArray *table_data, uint32_t node, + const char *hid, uint32_t uid) +{ + /* Type */ + build_append_int_noprefix(table_data, 6, 1); + /* Length */ + build_append_int_noprefix(table_data, 32, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 1); + /* Device Handle Type: ACPI */ + build_append_int_noprefix(table_data, 0, 1); + /* Proximity Domain */ + build_append_int_noprefix(table_data, node, 4); + /* Device Handle */ + build_append_srat_acpi_device_handle(table_data, hid, uid); + /* Flags - GP Enabled */ + build_append_int_noprefix(table_data, 1, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); +} + /* * ACPI spec 5.2.17 System Locality Distance Information Table * (Revision 2.0 or later) @@ -1994,6 +2076,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 +2165,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; @@ -2159,7 +2294,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 */ diff --git a/hw/acpi/core.c b/hw/acpi/core.c index ac485f6f16..87a96c8b88 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); @@ -559,8 +560,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) { @@ -583,33 +611,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, @@ -696,9 +697,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 { @@ -717,6 +720,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..5cb60ca8bc 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -1,12 +1,12 @@ #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" #include "sysemu/numa.h" -#define ACPI_CPU_HOTPLUG_REG_LEN 12 #define ACPI_CPU_SELECTOR_OFFSET_WR 0 #define ACPI_CPU_FLAGS_OFFSET_RW 4 #define ACPI_CPU_CMD_OFFSET_WR 5 @@ -35,7 +35,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 +296,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 +309,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,9 +338,10 @@ 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 base_addr, const char *res_root, - const char *event_handler_method) + const char *event_handler_method, + AmlRegionSpace rs) { Aml *ifctx; Aml *field; @@ -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); { @@ -368,14 +365,22 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_name_decl("_UID", aml_string("CPU Hotplug resources"))); aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); + assert((rs == AML_SYSTEM_IO) || (rs == AML_SYSTEM_MEMORY)); + crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, + if (rs == AML_SYSTEM_IO) { + aml_append(crs, aml_io(AML_DECODE16, base_addr, base_addr, 1, ACPI_CPU_HOTPLUG_REG_LEN)); + } else if (rs == AML_SYSTEM_MEMORY) { + aml_append(crs, aml_memory32_fixed(base_addr, + ACPI_CPU_HOTPLUG_REG_LEN, AML_READ_WRITE)); + } + aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); /* declare CPU hotplug MMIO region with related access fields */ aml_append(cpu_ctrl_dev, - aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base), + aml_operation_region("PRST", rs, aml_int(base_addr), ACPI_CPU_HOTPLUG_REG_LEN)); field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, @@ -666,9 +671,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 ff14c3f410..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" @@ -59,7 +59,8 @@ static const MemoryRegionOps AcpiCpuHotplug_ops = { }, }; -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; @@ -68,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); @@ -92,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); } } @@ -265,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); @@ -298,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); } @@ -319,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 2bf8c07993..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); @@ -112,7 +182,7 @@ static void cedt_build_cfmws(GArray *table_data, CXLState *cxls) /* 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); } } } @@ -121,7 +191,7 @@ 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)); } diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index aefcc03ad6..a108cfe49b 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" @@ -932,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), @@ -947,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(); @@ -964,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, @@ -977,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); @@ -1020,7 +1030,7 @@ static void erst_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_REDHAT_ACPI_ERST; k->revision = 0x00; k->class_id = PCI_CLASS_OTHERS; - dc->reset = erst_reset; + device_class_set_legacy_reset(dc, erst_reset); dc->vmsd = &erst_vmstate; dc->user_creatable = true; dc->hotpluggable = false; diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index a3d31631fe..663d9cb093 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -25,6 +25,7 @@ static const uint32_t ged_supported_events[] = { ACPI_GED_MEM_HOTPLUG_EVT, ACPI_GED_PWR_DOWN_EVT, ACPI_GED_NVDIMM_HOTPLUG_EVT, + ACPI_GED_CPU_HOTPLUG_EVT, }; /* @@ -107,6 +108,9 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD)); break; + case ACPI_GED_CPU_HOTPLUG_EVT: + aml_append(if_ctx, aml_call0(AML_GED_EVT_CPU_SCAN_METHOD)); + break; case ACPI_GED_PWR_DOWN_EVT: aml_append(if_ctx, aml_notify(aml_name(ACPI_POWER_BUTTON_DEVICE), @@ -197,9 +201,9 @@ static void ged_regs_write(void *opaque, hwaddr addr, uint64_t data, switch (addr) { case ACPI_GED_REG_SLEEP_CTL: - slp_typ = (data >> 2) & 0x07; - slp_en = (data >> 5) & 0x01; - if (slp_en && slp_typ == 5) { + slp_typ = (data >> ACPI_GED_SLP_TYP_POS) & ACPI_GED_SLP_TYP_MASK; + slp_en = !!(data & ACPI_GED_SLP_EN); + if (slp_en && slp_typ == ACPI_GED_SLP_TYP_S5) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } return; @@ -234,6 +238,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } else { acpi_memory_plug_cb(hotplug_dev, &s->memhp_state, dev, errp); } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -248,6 +254,8 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) { acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -261,6 +269,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&s->memhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -272,6 +282,7 @@ static void acpi_ged_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) AcpiGedState *s = ACPI_GED(adev); acpi_memory_ospm_status(&s->memhp_state, list); + acpi_cpu_ospm_status(&s->cpuhp_state, list); } static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) @@ -286,6 +297,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) sel = ACPI_GED_PWR_DOWN_EVT; } else if (ev & ACPI_NVDIMM_HOTPLUG_STATUS) { sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; + } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { + sel = ACPI_GED_CPU_HOTPLUG_EVT; } else { /* Unknown event. Return without generating interrupt. */ warn_report("GED: Unsupported event %d. No irq injected", ev); @@ -312,17 +325,35 @@ 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() } }; +static bool cpuhp_needed(void *opaque) +{ + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + return mc->has_hotpluggable_cpus; +} + +static const VMStateDescription vmstate_cpuhp_state = { + .name = "acpi-ged/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpuhp_needed, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, AcpiGedState), + VMSTATE_END_OF_LIST() + } +}; + 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() } @@ -332,7 +363,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() }, @@ -349,7 +380,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() @@ -360,17 +391,54 @@ 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_cpuhp_state, &vmstate_ghes_state, NULL } }; +static void acpi_ged_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AcpiGedState *s = ACPI_GED(dev); + uint32_t ged_events; + int i; + + ged_events = ctpop32(s->ged_event_bitmap); + + for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) { + uint32_t event = s->ged_event_bitmap & ged_supported_events[i]; + + if (!event) { + continue; + } + + switch (event) { + case ACPI_GED_CPU_HOTPLUG_EVT: + /* initialize CPU Hotplug related regions */ + memory_region_init(&s->container_cpuhp, OBJECT(dev), + "cpuhp container", + ACPI_CPU_HOTPLUG_REG_LEN); + sysbus_init_mmio(sbd, &s->container_cpuhp); + cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), + &s->cpuhp_state, 0); + break; + } + ged_events--; + } + + if (ged_events) { + error_report("Unsupported events specified"); + abort(); + } +} + static void acpi_ged_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -411,6 +479,7 @@ static void acpi_ged_class_init(ObjectClass *class, void *data) dc->desc = "ACPI Generic Event Device"; device_class_set_props(dc, acpi_ged_properties); dc->vmsd = &vmstate_acpi_ged; + dc->realize = acpi_ged_realize; hc->plug = acpi_ged_device_plug_cb; hc->unplug_request = acpi_ged_unplug_request_cb; 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..c15e5b8281 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -34,9 +34,10 @@ #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/acpi/ich9_timer.h" -#include "hw/i386/ich9.h" +#include "hw/southbridge/ich9.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" @@ -108,6 +109,18 @@ static void ich9_smi_writel(void *opaque, hwaddr addr, uint64_t val, } pm->smi_en &= ~pm->smi_en_wmask; pm->smi_en |= (val & pm->smi_en_wmask); + if (pm->swsmi_timer_enabled) { + ich9_pm_update_swsmi_timer(pm, pm->smi_en & + ICH9_PMIO_SMI_EN_SWSMI_EN); + } + if (pm->periodic_timer_enabled) { + ich9_pm_update_periodic_timer(pm, pm->smi_en & + ICH9_PMIO_SMI_EN_PERIODIC_EN); + } + break; + case 4: + pm->smi_sts &= ~pm->smi_sts_wmask; + pm->smi_sts |= (val & pm->smi_sts_wmask); break; } } @@ -153,18 +166,11 @@ static int ich9_pm_post_load(void *opaque, int version_id) .offset = vmstate_offset_pointer(_state, _field, uint8_t), \ } -static bool vmstate_test_use_memhp(void *opaque) -{ - ICH9LPCPMRegs *s = opaque; - return s->acpi_memory_hotplug.is_enabled; -} - static const VMStateDescription vmstate_memhp_state = { .name = "ich9_pm/memhp", .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 +187,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 +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, ICH9LPCPMRegs), VMSTATE_END_OF_LIST() } @@ -218,7 +224,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 +232,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 +245,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 +257,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 +283,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,10 +297,10 @@ 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) { + pm->smi_sts_wmask = 0; + memory_region_init(&pm->io, OBJECT(lpc_pci), "ich9-pm", ICH9_PMIO_SIZE); memory_region_set_enabled(&pm->io, false); memory_region_add_subregion(pci_address_space_io(lpc_pci), @@ -303,7 +309,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 +320,23 @@ 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->swsmi_timer_enabled) { + ich9_pm_swsmi_timer_init(pm); + } - pm->enable_tco = true; - acpi_pm_tco_init(&pm->tco_regs, &pm->io); + if (pm->periodic_timer_enabled) { + ich9_pm_periodic_timer_init(pm); + } - if (pm->use_acpi_hotplug_bridge) { + if (pm->enable_tco) { + acpi_pm_tco_init(&pm->tco_regs, &pm->io); + } + + 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)), @@ -339,11 +351,9 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, legacy_acpi_cpu_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); - if (pm->acpi_memory_hotplug.is_enabled) { - acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), - &pm->acpi_memory_hotplug, - ACPI_MEMORY_HOTPLUG_BASE); - } + acpi_memory_hotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), + &pm->acpi_memory_hotplug, + ACPI_MEMORY_HOTPLUG_BASE); } static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name, @@ -355,21 +365,6 @@ static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &value, errp); } -static bool ich9_pm_get_memory_hotplug_support(Object *obj, Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - - return s->pm.acpi_memory_hotplug.is_enabled; -} - -static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, - Error **errp) -{ - ICH9LPCState *s = ICH9_LPC_DEVICE(obj); - - s->pm.acpi_memory_hotplug.is_enabled = value; -} - static bool ich9_pm_get_cpu_hotplug_legacy(Object *obj, Error **errp) { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); @@ -406,14 +401,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 +433,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); @@ -448,9 +444,6 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) NULL, NULL, pm); object_property_add_uint32_ptr(obj, ACPI_PM_PROP_GPE0_BLK_LEN, &gpe0_len, OBJ_PROP_FLAG_READ); - object_property_add_bool(obj, "memory-hotplug-support", - ich9_pm_get_memory_hotplug_support, - ich9_pm_set_memory_hotplug_support); object_property_add_bool(obj, "cpu-hotplug-legacy", ich9_pm_get_cpu_hotplug_legacy, ich9_pm_set_cpu_hotplug_legacy); @@ -481,12 +474,7 @@ void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && - !lpc->pm.acpi_memory_hotplug.is_enabled) { - error_setg(errp, - "memory hotplug is not enabled: %s.memory-hotplug-support " - "is not set", object_get_typename(OBJECT(lpc))); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { uint64_t negotiated = lpc->smi_negotiated_features; if (negotiated & BIT_ULL(ICH9_LPC_SMI_F_BROADCAST_BIT) && @@ -530,8 +518,7 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - if (lpc->pm.acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_request_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug, dev, errp); @@ -566,8 +553,7 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - if (lpc->pm.acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&lpc->pm.acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && !lpc->pm.cpu_hotplug_legacy) { @@ -581,6 +567,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/ich9_tco.c b/hw/acpi/ich9_tco.c new file mode 100644 index 0000000000..81606219f7 --- /dev/null +++ b/hw/acpi/ich9_tco.c @@ -0,0 +1,275 @@ +/* + * QEMU ICH9 TCO emulation + * + * Copyright (c) 2015 Paulo Alcantara + * + * 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 "sysemu/watchdog.h" +#include "hw/southbridge/ich9.h" +#include "migration/vmstate.h" + +#include "hw/acpi/ich9_tco.h" +#include "trace.h" + +enum { + TCO_RLD_DEFAULT = 0x0000, + TCO_DAT_IN_DEFAULT = 0x00, + TCO_DAT_OUT_DEFAULT = 0x00, + TCO1_STS_DEFAULT = 0x0000, + TCO2_STS_DEFAULT = 0x0000, + TCO1_CNT_DEFAULT = 0x0000, + TCO2_CNT_DEFAULT = 0x0008, + TCO_MESSAGE1_DEFAULT = 0x00, + TCO_MESSAGE2_DEFAULT = 0x00, + TCO_WDCNT_DEFAULT = 0x00, + TCO_TMR_DEFAULT = 0x0004, + SW_IRQ_GEN_DEFAULT = 0x03, +}; + +static inline void tco_timer_reload(TCOIORegs *tr) +{ + int ticks = tr->tco.tmr & TCO_TMR_MASK; + int64_t nsec = (int64_t)ticks * TCO_TICK_NSEC; + + trace_tco_timer_reload(ticks, nsec / 1000000); + tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + nsec; + timer_mod(tr->tco_timer, tr->expire_time); +} + +static inline void tco_timer_stop(TCOIORegs *tr) +{ + tr->expire_time = -1; + timer_del(tr->tco_timer); +} + +static void tco_timer_expired(void *opaque) +{ + TCOIORegs *tr = opaque; + ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs); + ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm); + uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS); + + trace_tco_timer_expired(tr->timeouts_no, + lpc->pin_strap.spkr_hi, + !!(gcs & ICH9_CC_GCS_NO_REBOOT)); + tr->tco.rld = 0; + tr->tco.sts1 |= TCO_TIMEOUT; + if (++tr->timeouts_no == 2) { + tr->tco.sts2 |= TCO_SECOND_TO_STS; + tr->tco.sts2 |= TCO_BOOT_STS; + tr->timeouts_no = 0; + + if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) { + watchdog_perform_action(); + tco_timer_stop(tr); + return; + } + } + + if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) { + ich9_generate_smi(); + } + tr->tco.rld = tr->tco.tmr; + tco_timer_reload(tr); +} + +/* NOTE: values of 0 or 1 will be ignored by ICH */ +static inline int can_start_tco_timer(TCOIORegs *tr) +{ + return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1; +} + +static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr) +{ + uint16_t rld; + uint32_t ret = 0; + + switch (addr) { + case TCO_RLD: + if (tr->expire_time != -1) { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC; + rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK); + } else { + rld = tr->tco.rld; + } + ret = rld; + break; + case TCO_DAT_IN: + ret = tr->tco.din; + break; + case TCO_DAT_OUT: + ret = tr->tco.dout; + break; + case TCO1_STS: + ret = tr->tco.sts1; + break; + case TCO2_STS: + ret = tr->tco.sts2; + break; + case TCO1_CNT: + ret = tr->tco.cnt1; + break; + case TCO2_CNT: + ret = tr->tco.cnt2; + break; + case TCO_MESSAGE1: + ret = tr->tco.msg1; + break; + case TCO_MESSAGE2: + ret = tr->tco.msg2; + break; + case TCO_WDCNT: + ret = tr->tco.wdcnt; + break; + case TCO_TMR: + ret = tr->tco.tmr; + break; + case SW_IRQ_GEN: + ret = tr->sw_irq_gen; + break; + } + 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; + if (can_start_tco_timer(tr)) { + tr->tco.rld = tr->tco.tmr; + tco_timer_reload(tr); + } else { + tr->tco.rld = val; + } + break; + case TCO_DAT_IN: + tr->tco.din = val; + tr->tco.sts1 |= SW_TCO_SMI; + ich9_generate_smi(); + break; + case TCO_DAT_OUT: + tr->tco.dout = val; + tr->tco.sts1 |= TCO_INT_STS; + /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */ + break; + case TCO1_STS: + tr->tco.sts1 = val & TCO1_STS_MASK; + break; + case TCO2_STS: + tr->tco.sts2 = val & TCO2_STS_MASK; + break; + case TCO1_CNT: + val &= TCO1_CNT_MASK; + /* + * once TCO_LOCK bit is set, it can not be cleared by software. a reset + * is required to change this bit from 1 to 0 -- it defaults to 0. + */ + tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK); + if (can_start_tco_timer(tr)) { + tr->tco.rld = tr->tco.tmr; + tco_timer_reload(tr); + } else { + tco_timer_stop(tr); + } + break; + case TCO2_CNT: + tr->tco.cnt2 = val; + break; + case TCO_MESSAGE1: + tr->tco.msg1 = val; + break; + case TCO_MESSAGE2: + tr->tco.msg2 = val; + break; + case TCO_WDCNT: + tr->tco.wdcnt = val; + break; + case TCO_TMR: + tr->tco.tmr = val; + break; + case SW_IRQ_GEN: + tr->sw_irq_gen = val; + break; + } +} + +static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width) +{ + TCOIORegs *tr = opaque; + return tco_ioport_readw(tr, addr); +} + +static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + TCOIORegs *tr = opaque; + tco_ioport_writew(tr, addr, val); +} + +static const MemoryRegionOps tco_io_ops = { + .read = tco_io_readw, + .write = tco_io_writew, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 2, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent) +{ + *tr = (TCOIORegs) { + .tco = { + .rld = TCO_RLD_DEFAULT, + .din = TCO_DAT_IN_DEFAULT, + .dout = TCO_DAT_OUT_DEFAULT, + .sts1 = TCO1_STS_DEFAULT, + .sts2 = TCO2_STS_DEFAULT, + .cnt1 = TCO1_CNT_DEFAULT, + .cnt2 = TCO2_CNT_DEFAULT, + .msg1 = TCO_MESSAGE1_DEFAULT, + .msg2 = TCO_MESSAGE2_DEFAULT, + .wdcnt = TCO_WDCNT_DEFAULT, + .tmr = TCO_TMR_DEFAULT, + }, + .sw_irq_gen = SW_IRQ_GEN_DEFAULT, + .tco_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr), + .expire_time = -1, + .timeouts_no = 0, + }; + memory_region_init_io(&tr->io, memory_region_owner(parent), + &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN); + memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io); +} + +const VMStateDescription vmstate_tco_io_sts = { + .name = "tco io device status", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16(tco.rld, TCOIORegs), + VMSTATE_UINT8(tco.din, TCOIORegs), + VMSTATE_UINT8(tco.dout, TCOIORegs), + VMSTATE_UINT16(tco.sts1, TCOIORegs), + VMSTATE_UINT16(tco.sts2, TCOIORegs), + VMSTATE_UINT16(tco.cnt1, TCOIORegs), + VMSTATE_UINT16(tco.cnt2, TCOIORegs), + VMSTATE_UINT8(tco.msg1, TCOIORegs), + VMSTATE_UINT8(tco.msg2, TCOIORegs), + VMSTATE_UINT8(tco.wdcnt, TCOIORegs), + VMSTATE_UINT16(tco.tmr, TCOIORegs), + VMSTATE_UINT8(sw_irq_gen, TCOIORegs), + VMSTATE_TIMER_PTR(tco_timer, TCOIORegs), + VMSTATE_INT64(expire_time, TCOIORegs), + VMSTATE_UINT8(timeouts_no, TCOIORegs), + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/acpi/ich9_timer.c b/hw/acpi/ich9_timer.c new file mode 100644 index 0000000000..5b1c910156 --- /dev/null +++ b/hw/acpi/ich9_timer.c @@ -0,0 +1,93 @@ +/* + * QEMU ICH9 Timer emulation + * + * Copyright (c) 2024 Dominic Prinz + * + * 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/core/cpu.h" +#include "hw/pci/pci.h" +#include "hw/southbridge/ich9.h" +#include "qemu/timer.h" + +#include "hw/acpi/ich9_timer.h" + +void ich9_pm_update_swsmi_timer(ICH9LPCPMRegs *pm, bool enable) +{ + uint16_t swsmi_rate_sel; + int64_t expire_time; + ICH9LPCState *lpc; + + if (enable) { + lpc = container_of(pm, ICH9LPCState, pm); + swsmi_rate_sel = + (pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_3) & 0xc0) >> 6; + + if (swsmi_rate_sel == 0) { + expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1500000LL; + } else { + expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + 8 * (1 << swsmi_rate_sel) * 1000000LL; + } + + timer_mod(pm->swsmi_timer, expire_time); + } else { + timer_del(pm->swsmi_timer); + } +} + +static void ich9_pm_swsmi_timer_expired(void *opaque) +{ + ICH9LPCPMRegs *pm = opaque; + + pm->smi_sts |= ICH9_PMIO_SMI_STS_SWSMI_STS; + ich9_generate_smi(); + + ich9_pm_update_swsmi_timer(pm, pm->smi_en & ICH9_PMIO_SMI_EN_SWSMI_EN); +} + +void ich9_pm_swsmi_timer_init(ICH9LPCPMRegs *pm) +{ + pm->smi_sts_wmask |= ICH9_PMIO_SMI_STS_SWSMI_STS; + pm->swsmi_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_pm_swsmi_timer_expired, pm); +} + +void ich9_pm_update_periodic_timer(ICH9LPCPMRegs *pm, bool enable) +{ + uint16_t per_smi_sel; + int64_t expire_time; + ICH9LPCState *lpc; + + if (enable) { + lpc = container_of(pm, ICH9LPCState, pm); + per_smi_sel = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1) & 3; + expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + 8 * (1 << (3 - per_smi_sel)) * NANOSECONDS_PER_SECOND; + + timer_mod(pm->periodic_timer, expire_time); + } else { + timer_del(pm->periodic_timer); + } +} + +static void ich9_pm_periodic_timer_expired(void *opaque) +{ + ICH9LPCPMRegs *pm = opaque; + + pm->smi_sts = ICH9_PMIO_SMI_STS_PERIODIC_STS; + ich9_generate_smi(); + + ich9_pm_update_periodic_timer(pm, + pm->smi_en & ICH9_PMIO_SMI_EN_PERIODIC_EN); +} + +void ich9_pm_periodic_timer_init(ICH9LPCPMRegs *pm) +{ + pm->smi_sts_wmask |= ICH9_PMIO_SMI_STS_PERIODIC_STS; + pm->periodic_timer = + timer_new_ns(QEMU_CLOCK_VIRTUAL, ich9_pm_periodic_timer_expired, pm); +} diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 0a7e89a13e..9b974b7274 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; @@ -178,15 +178,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); if (local_err) { trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); - - /* - * Send both MEM_UNPLUG_ERROR and DEVICE_UNPLUG_GUEST_ERROR - * while the deprecation of MEM_UNPLUG_ERROR is - * pending. - */ - 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 +309,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 +322,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 f8c820ca94..c8854f4d48 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -19,20 +19,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', 'ich9_timer.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', 'acpi_interface.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 a3b25a92f3..9ba90806f2 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -670,7 +670,8 @@ 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; @@ -690,6 +691,10 @@ static uint32_t nvdimm_rw_label_data_check(NVDIMMDevice *nvdimm, return ret; } + if (is_write && nvdimm->readonly) { + return NVDIMM_DSM_RET_STATUS_UNSUPPORT; + } + return NVDIMM_DSM_RET_STATUS_SUCCESS; } @@ -713,7 +718,7 @@ static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 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; @@ -752,7 +757,7 @@ static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, 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; @@ -1097,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 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/pci.c b/hw/acpi/pci.c index 20b70dcd81..f88f450af3 100644 --- a/hw/acpi/pci.c +++ b/hw/acpi/pci.c @@ -24,8 +24,14 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qom/object_interfaces.h" +#include "qapi/error.h" +#include "hw/boards.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/pci.h" +#include "hw/pci/pci_bridge.h" +#include "hw/pci/pci_device.h" #include "hw/pci/pcie_host.h" /* @@ -59,3 +65,239 @@ void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, acpi_table_end(linker, &table); } + +typedef struct AcpiGenericInitiator { + /* private */ + Object parent; + + /* public */ + char *pci_dev; + uint32_t node; +} AcpiGenericInitiator; + +typedef struct AcpiGenericInitiatorClass { + ObjectClass parent_class; +} AcpiGenericInitiatorClass; + +#define TYPE_ACPI_GENERIC_INITIATOR "acpi-generic-initiator" + +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_set_description(oc, "pci-dev", + "PCI device to associate with the node"); + object_class_property_add(oc, "node", "int", NULL, + acpi_generic_initiator_set_node, NULL, NULL); + object_class_property_set_description(oc, "node", + "NUMA node associated with the PCI device"); +} + +static int build_acpi_generic_initiator(Object *obj, void *opaque) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + AcpiGenericInitiator *gi; + GArray *table_data = opaque; + int32_t devfn; + uint8_t bus; + 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); + } + + bus = object_property_get_uint(o, "busnr", &error_fatal); + devfn = object_property_get_uint(o, "addr", &error_fatal); + /* devfn is constrained in PCI to be 8 bit but storage is an int32_t */ + assert(devfn >= 0 && devfn < PCI_DEVFN_MAX); + + build_srat_pci_generic_initiator(table_data, gi->node, 0, bus, devfn); + + return 0; +} + +typedef struct AcpiGenericPort { + /* private */ + Object parent; + + /* public */ + char *pci_bus; + uint32_t node; +} AcpiGenericPort; + +typedef struct AcpiGenericPortClass { + ObjectClass parent_class; +} AcpiGenericPortClass; + +#define TYPE_ACPI_GENERIC_PORT "acpi-generic-port" + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(AcpiGenericPort, acpi_generic_port, + ACPI_GENERIC_PORT, OBJECT, + { TYPE_USER_CREATABLE }, + { NULL }) + +OBJECT_DECLARE_SIMPLE_TYPE(AcpiGenericPort, ACPI_GENERIC_PORT) + +static void acpi_generic_port_init(Object *obj) +{ + AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); + + gp->node = MAX_NODES; + gp->pci_bus = NULL; +} + +static void acpi_generic_port_finalize(Object *obj) +{ + AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); + + g_free(gp->pci_bus); +} + +static void acpi_generic_port_set_pci_bus(Object *obj, const char *val, + Error **errp) +{ + AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); + + gp->pci_bus = g_strdup(val); +} + +static void acpi_generic_port_set_node(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + AcpiGenericPort *gp = ACPI_GENERIC_PORT(obj); + 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); + } + + gp->node = value; +} + +static void acpi_generic_port_class_init(ObjectClass *oc, void *data) +{ + object_class_property_add_str(oc, "pci-bus", NULL, + acpi_generic_port_set_pci_bus); + object_class_property_set_description(oc, "pci-bus", + "PCI Bus of the host bridge associated with this GP affinity structure"); + object_class_property_add(oc, "node", "int", NULL, + acpi_generic_port_set_node, NULL, NULL); + object_class_property_set_description(oc, "node", + "The NUMA node like ID to index HMAT/SLIT NUMA properties involving GP"); +} + +static int build_acpi_generic_port(Object *obj, void *opaque) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + const char *hid = "ACPI0016"; + GArray *table_data = opaque; + AcpiGenericPort *gp; + uint32_t uid; + Object *o; + + if (!object_dynamic_cast(obj, TYPE_ACPI_GENERIC_PORT)) { + return 0; + } + + gp = ACPI_GENERIC_PORT(obj); + + if (gp->node >= ms->numa_state->num_nodes) { + error_printf("%s: node %d is invalid.\n", + TYPE_ACPI_GENERIC_PORT, gp->node); + exit(1); + } + + o = object_resolve_path_type(gp->pci_bus, TYPE_PXB_CXL_BUS, NULL); + if (!o) { + error_printf("%s: device must be a CXL host bridge.\n", + TYPE_ACPI_GENERIC_PORT); + exit(1); + } + + uid = object_property_get_uint(o, "acpi_uid", &error_fatal); + build_srat_acpi_generic_port(table_data, gp->node, hid, uid); + + return 0; +} + +void build_srat_generic_affinity_structures(GArray *table_data) +{ + object_child_foreach_recursive(object_get_root(), + build_acpi_generic_initiator, + table_data); + object_child_foreach_recursive(object_get_root(), build_acpi_generic_port, + table_data); +} diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index a2a3738b46..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,7 +166,6 @@ 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 @@ -196,7 +175,7 @@ static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) * 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); } @@ -283,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) { @@ -306,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, @@ -353,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)); @@ -393,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); } @@ -443,6 +371,24 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, 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; @@ -456,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); @@ -531,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; @@ -549,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); @@ -572,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 0a81f1ad93..1de3fe3261 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -20,8 +20,6 @@ */ #include "qemu/osdep.h" -#include "hw/i386/pc.h" -#include "hw/southbridge/piix.h" #include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/i2c/pm_smbus.h" @@ -35,7 +33,6 @@ #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" @@ -45,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 @@ -151,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() @@ -162,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() @@ -172,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) @@ -193,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() } @@ -218,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() } @@ -236,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 @@ -250,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), @@ -272,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 @@ -305,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) @@ -402,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); @@ -487,12 +494,11 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) qemu_add_machine_init_done_notifier(&s->machine_ready); if (xen_enabled()) { - s->use_acpi_hotplug_bridge = false; + 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); } @@ -510,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; } @@ -519,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); } @@ -561,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; @@ -602,9 +608,9 @@ 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), @@ -627,7 +633,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3; k->revision = 0x03; k->class_id = PCI_CLASS_BRIDGE_OTHER; - dc->reset = piix4_pm_reset; + device_class_set_legacy_reset(dc, piix4_pm_reset); dc->desc = "PM"; dc->vmsd = &vmstate_acpi; device_class_set_props(dc, piix4_pm_properties); @@ -641,9 +647,9 @@ 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 = { diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c deleted file mode 100644 index 4783721e4e..0000000000 --- a/hw/acpi/tco.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * QEMU ICH9 TCO emulation - * - * Copyright (c) 2015 Paulo Alcantara - * - * 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 "sysemu/watchdog.h" -#include "hw/i386/ich9.h" -#include "migration/vmstate.h" - -#include "hw/acpi/tco.h" -#include "trace.h" - -enum { - TCO_RLD_DEFAULT = 0x0000, - TCO_DAT_IN_DEFAULT = 0x00, - TCO_DAT_OUT_DEFAULT = 0x00, - TCO1_STS_DEFAULT = 0x0000, - TCO2_STS_DEFAULT = 0x0000, - TCO1_CNT_DEFAULT = 0x0000, - TCO2_CNT_DEFAULT = 0x0008, - TCO_MESSAGE1_DEFAULT = 0x00, - TCO_MESSAGE2_DEFAULT = 0x00, - TCO_WDCNT_DEFAULT = 0x00, - TCO_TMR_DEFAULT = 0x0004, - SW_IRQ_GEN_DEFAULT = 0x03, -}; - -static inline void tco_timer_reload(TCOIORegs *tr) -{ - int ticks = tr->tco.tmr & TCO_TMR_MASK; - int64_t nsec = (int64_t)ticks * TCO_TICK_NSEC; - - trace_tco_timer_reload(ticks, nsec / 1000000); - tr->expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + nsec; - timer_mod(tr->tco_timer, tr->expire_time); -} - -static inline void tco_timer_stop(TCOIORegs *tr) -{ - tr->expire_time = -1; - timer_del(tr->tco_timer); -} - -static void tco_timer_expired(void *opaque) -{ - TCOIORegs *tr = opaque; - ICH9LPCPMRegs *pm = container_of(tr, ICH9LPCPMRegs, tco_regs); - ICH9LPCState *lpc = container_of(pm, ICH9LPCState, pm); - uint32_t gcs = pci_get_long(lpc->chip_config + ICH9_CC_GCS); - - trace_tco_timer_expired(tr->timeouts_no, - lpc->pin_strap.spkr_hi, - !!(gcs & ICH9_CC_GCS_NO_REBOOT)); - tr->tco.rld = 0; - tr->tco.sts1 |= TCO_TIMEOUT; - if (++tr->timeouts_no == 2) { - tr->tco.sts2 |= TCO_SECOND_TO_STS; - tr->tco.sts2 |= TCO_BOOT_STS; - tr->timeouts_no = 0; - - if (!lpc->pin_strap.spkr_hi && !(gcs & ICH9_CC_GCS_NO_REBOOT)) { - watchdog_perform_action(); - tco_timer_stop(tr); - return; - } - } - - if (pm->smi_en & ICH9_PMIO_SMI_EN_TCO_EN) { - ich9_generate_smi(); - } - tr->tco.rld = tr->tco.tmr; - tco_timer_reload(tr); -} - -/* NOTE: values of 0 or 1 will be ignored by ICH */ -static inline int can_start_tco_timer(TCOIORegs *tr) -{ - return !(tr->tco.cnt1 & TCO_TMR_HLT) && tr->tco.tmr > 1; -} - -static uint32_t tco_ioport_readw(TCOIORegs *tr, uint32_t addr) -{ - uint16_t rld; - - switch (addr) { - case TCO_RLD: - if (tr->expire_time != -1) { - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - int64_t elapsed = (tr->expire_time - now) / TCO_TICK_NSEC; - rld = (uint16_t)elapsed | (tr->tco.rld & ~TCO_RLD_MASK); - } else { - rld = tr->tco.rld; - } - return rld; - case TCO_DAT_IN: - return tr->tco.din; - case TCO_DAT_OUT: - return tr->tco.dout; - case TCO1_STS: - return tr->tco.sts1; - case TCO2_STS: - return tr->tco.sts2; - case TCO1_CNT: - return tr->tco.cnt1; - case TCO2_CNT: - return tr->tco.cnt2; - case TCO_MESSAGE1: - return tr->tco.msg1; - case TCO_MESSAGE2: - return tr->tco.msg2; - case TCO_WDCNT: - return tr->tco.wdcnt; - case TCO_TMR: - return tr->tco.tmr; - case SW_IRQ_GEN: - return tr->sw_irq_gen; - } - return 0; -} - -static void tco_ioport_writew(TCOIORegs *tr, uint32_t addr, uint32_t val) -{ - switch (addr) { - case TCO_RLD: - tr->timeouts_no = 0; - if (can_start_tco_timer(tr)) { - tr->tco.rld = tr->tco.tmr; - tco_timer_reload(tr); - } else { - tr->tco.rld = val; - } - break; - case TCO_DAT_IN: - tr->tco.din = val; - tr->tco.sts1 |= SW_TCO_SMI; - ich9_generate_smi(); - break; - case TCO_DAT_OUT: - tr->tco.dout = val; - tr->tco.sts1 |= TCO_INT_STS; - /* TODO: cause an interrupt, as selected by the TCO_INT_SEL bits */ - break; - case TCO1_STS: - tr->tco.sts1 = val & TCO1_STS_MASK; - break; - case TCO2_STS: - tr->tco.sts2 = val & TCO2_STS_MASK; - break; - case TCO1_CNT: - val &= TCO1_CNT_MASK; - /* - * once TCO_LOCK bit is set, it can not be cleared by software. a reset - * is required to change this bit from 1 to 0 -- it defaults to 0. - */ - tr->tco.cnt1 = val | (tr->tco.cnt1 & TCO_LOCK); - if (can_start_tco_timer(tr)) { - tr->tco.rld = tr->tco.tmr; - tco_timer_reload(tr); - } else { - tco_timer_stop(tr); - } - break; - case TCO2_CNT: - tr->tco.cnt2 = val; - break; - case TCO_MESSAGE1: - tr->tco.msg1 = val; - break; - case TCO_MESSAGE2: - tr->tco.msg2 = val; - break; - case TCO_WDCNT: - tr->tco.wdcnt = val; - break; - case TCO_TMR: - tr->tco.tmr = val; - break; - case SW_IRQ_GEN: - tr->sw_irq_gen = val; - break; - } -} - -static uint64_t tco_io_readw(void *opaque, hwaddr addr, unsigned width) -{ - TCOIORegs *tr = opaque; - return tco_ioport_readw(tr, addr); -} - -static void tco_io_writew(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - TCOIORegs *tr = opaque; - tco_ioport_writew(tr, addr, val); -} - -static const MemoryRegionOps tco_io_ops = { - .read = tco_io_readw, - .write = tco_io_writew, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .impl.min_access_size = 1, - .impl.max_access_size = 2, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent) -{ - *tr = (TCOIORegs) { - .tco = { - .rld = TCO_RLD_DEFAULT, - .din = TCO_DAT_IN_DEFAULT, - .dout = TCO_DAT_OUT_DEFAULT, - .sts1 = TCO1_STS_DEFAULT, - .sts2 = TCO2_STS_DEFAULT, - .cnt1 = TCO1_CNT_DEFAULT, - .cnt2 = TCO2_CNT_DEFAULT, - .msg1 = TCO_MESSAGE1_DEFAULT, - .msg2 = TCO_MESSAGE2_DEFAULT, - .wdcnt = TCO_WDCNT_DEFAULT, - .tmr = TCO_TMR_DEFAULT, - }, - .sw_irq_gen = SW_IRQ_GEN_DEFAULT, - .tco_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tco_timer_expired, tr), - .expire_time = -1, - .timeouts_no = 0, - }; - memory_region_init_io(&tr->io, memory_region_owner(parent), - &tco_io_ops, tr, "sm-tco", ICH9_PMIO_TCO_LEN); - memory_region_add_subregion(parent, ICH9_PMIO_TCO_RLD, &tr->io); -} - -const VMStateDescription vmstate_tco_io_sts = { - .name = "tco io device status", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(tco.rld, TCOIORegs), - VMSTATE_UINT8(tco.din, TCOIORegs), - VMSTATE_UINT8(tco.dout, TCOIORegs), - VMSTATE_UINT16(tco.sts1, TCOIORegs), - VMSTATE_UINT16(tco.sts2, TCOIORegs), - VMSTATE_UINT16(tco.cnt1, TCOIORegs), - VMSTATE_UINT16(tco.cnt2, TCOIORegs), - VMSTATE_UINT8(tco.msg1, TCOIORegs), - VMSTATE_UINT8(tco.msg2, TCOIORegs), - VMSTATE_UINT8(tco.wdcnt, TCOIORegs), - VMSTATE_UINT16(tco.tmr, TCOIORegs), - VMSTATE_UINT8(sw_irq_gen, TCOIORegs), - VMSTATE_TIMER_PTR(tco_timer, TCOIORegs), - VMSTATE_INT64(expire_time, TCOIORegs), - VMSTATE_UINT8(timeouts_no, TCOIORegs), - VMSTATE_END_OF_LIST() - } -}; diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index eb60b04f9b..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)" 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/Kconfig b/hw/adc/Kconfig index a825bd3d34..25d2229fb8 100644 --- a/hw/adc/Kconfig +++ b/hw/adc/Kconfig @@ -1,5 +1,2 @@ config STM32F2XX_ADC bool - -config MAX111X - bool diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index 0d29663129..598f2bdf48 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(), } @@ -297,7 +297,7 @@ static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_adc_engine_realize; - dc->reset = aspeed_adc_engine_reset; + device_class_set_legacy_reset(dc, aspeed_adc_engine_reset); device_class_set_props(dc, aspeed_adc_engine_properties); dc->desc = "Aspeed Analog-to-Digital Engine"; dc->vmsd = &vmstate_aspeed_adc_engine; @@ -398,6 +398,15 @@ static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 2; } +static void aspeed_2700_adc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); + + dc->desc = "ASPEED 2700 ADC Controller"; + aac->nr_engines = 2; +} + static const TypeInfo aspeed_adc_info = { .name = TYPE_ASPEED_ADC, .parent = TYPE_SYS_BUS_DEVICE, @@ -430,6 +439,12 @@ static const TypeInfo aspeed_1030_adc_info = { .class_init = aspeed_1030_adc_class_init, /* No change since AST2600 */ }; +static const TypeInfo aspeed_2700_adc_info = { + .name = TYPE_ASPEED_2700_ADC, + .parent = TYPE_ASPEED_ADC, + .class_init = aspeed_2700_adc_class_init, +}; + static void aspeed_adc_register_types(void) { type_register_static(&aspeed_adc_engine_info); @@ -438,6 +453,7 @@ static void aspeed_adc_register_types(void) type_register_static(&aspeed_2500_adc_info); type_register_static(&aspeed_2600_adc_info); type_register_static(&aspeed_1030_adc_info); + type_register_static(&aspeed_2700_adc_info); } type_init(aspeed_adc_register_types); diff --git a/hw/adc/max111x.c b/hw/adc/max111x.c deleted file mode 100644 index e8bf4cccd4..0000000000 --- a/hw/adc/max111x.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Maxim MAX1110/1111 ADC chip emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPLv2. - * - * 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/adc/max111x.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "hw/qdev-properties.h" - -/* Control-byte bitfields */ -#define CB_PD0 (1 << 0) -#define CB_PD1 (1 << 1) -#define CB_SGL (1 << 2) -#define CB_UNI (1 << 3) -#define CB_SEL0 (1 << 4) -#define CB_SEL1 (1 << 5) -#define CB_SEL2 (1 << 6) -#define CB_START (1 << 7) - -#define CHANNEL_NUM(v, b0, b1, b2) \ - ((((v) >> (2 + (b0))) & 4) | \ - (((v) >> (3 + (b1))) & 2) | \ - (((v) >> (4 + (b2))) & 1)) - -static uint32_t max111x_read(MAX111xState *s) -{ - if (!s->tb1) - return 0; - - switch (s->cycle ++) { - case 1: - return s->rb2; - case 2: - return s->rb3; - } - - return 0; -} - -/* Interpret a control-byte */ -static void max111x_write(MAX111xState *s, uint32_t value) -{ - int measure, chan; - - /* Ignore the value if START bit is zero */ - if (!(value & CB_START)) - return; - - s->cycle = 0; - - if (!(value & CB_PD1)) { - s->tb1 = 0; - return; - } - - s->tb1 = value; - - if (s->inputs == 8) - chan = CHANNEL_NUM(value, 1, 0, 2); - else - chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2); - - if (value & CB_SGL) - measure = s->input[chan] - s->com; - else - measure = s->input[chan] - s->input[chan ^ 1]; - - if (!(value & CB_UNI)) - measure ^= 0x80; - - s->rb2 = (measure >> 2) & 0x3f; - s->rb3 = (measure << 6) & 0xc0; - - /* FIXME: When should the IRQ be lowered? */ - qemu_irq_raise(s->interrupt); -} - -static uint32_t max111x_transfer(SSIPeripheral *dev, uint32_t value) -{ - MAX111xState *s = MAX_111X(dev); - max111x_write(s, value); - return max111x_read(s); -} - -static const VMStateDescription vmstate_max111x = { - .name = "max111x", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SSI_PERIPHERAL(parent_obj, MAX111xState), - VMSTATE_UINT8(tb1, MAX111xState), - VMSTATE_UINT8(rb2, MAX111xState), - VMSTATE_UINT8(rb3, MAX111xState), - VMSTATE_INT32_EQUAL(inputs, MAX111xState, NULL), - VMSTATE_INT32(com, MAX111xState), - VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs, - vmstate_info_uint8, uint8_t), - VMSTATE_END_OF_LIST() - } -}; - -static void max111x_input_set(void *opaque, int line, int value) -{ - MAX111xState *s = MAX_111X(opaque); - - assert(line >= 0 && line < s->inputs); - s->input[line] = value; -} - -static int max111x_init(SSIPeripheral *d, int inputs) -{ - DeviceState *dev = DEVICE(d); - MAX111xState *s = MAX_111X(dev); - - qdev_init_gpio_out(dev, &s->interrupt, 1); - qdev_init_gpio_in(dev, max111x_input_set, inputs); - - s->inputs = inputs; - - return 0; -} - -static void max1110_realize(SSIPeripheral *dev, Error **errp) -{ - max111x_init(dev, 8); -} - -static void max1111_realize(SSIPeripheral *dev, Error **errp) -{ - max111x_init(dev, 4); -} - -static void max111x_reset(DeviceState *dev) -{ - MAX111xState *s = MAX_111X(dev); - int i; - - for (i = 0; i < s->inputs; i++) { - s->input[i] = s->reset_input[i]; - } - s->com = 0; - s->tb1 = 0; - s->rb2 = 0; - s->rb3 = 0; - s->cycle = 0; -} - -static Property max1110_properties[] = { - /* Reset values for ADC inputs */ - DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0), - DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0), - DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0), - DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0), - DEFINE_PROP_END_OF_LIST(), -}; - -static Property max1111_properties[] = { - /* Reset values for ADC inputs */ - DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0), - DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0), - DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0), - DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0), - DEFINE_PROP_UINT8("input4", MAX111xState, reset_input[4], 0xb0), - DEFINE_PROP_UINT8("input5", MAX111xState, reset_input[5], 0xa0), - DEFINE_PROP_UINT8("input6", MAX111xState, reset_input[6], 0x90), - DEFINE_PROP_UINT8("input7", MAX111xState, reset_input[7], 0x80), - DEFINE_PROP_END_OF_LIST(), -}; - -static void max111x_class_init(ObjectClass *klass, void *data) -{ - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->transfer = max111x_transfer; - dc->reset = max111x_reset; - dc->vmsd = &vmstate_max111x; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo max111x_info = { - .name = TYPE_MAX_111X, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(MAX111xState), - .class_init = max111x_class_init, - .abstract = true, -}; - -static void max1110_class_init(ObjectClass *klass, void *data) -{ - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = max1110_realize; - device_class_set_props(dc, max1110_properties); -} - -static const TypeInfo max1110_info = { - .name = TYPE_MAX_1110, - .parent = TYPE_MAX_111X, - .class_init = max1110_class_init, -}; - -static void max1111_class_init(ObjectClass *klass, void *data) -{ - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = max1111_realize; - device_class_set_props(dc, max1111_properties); -} - -static const TypeInfo max1111_info = { - .name = TYPE_MAX_1111, - .parent = TYPE_MAX_111X, - .class_init = max1111_class_init, -}; - -static void max111x_register_types(void) -{ - type_register_static(&max111x_info); - type_register_static(&max1110_info); - type_register_static(&max1111_info); -} - -type_init(max111x_register_types) diff --git a/hw/adc/meson.build b/hw/adc/meson.build index b29ac7ccdf..7f7acc1619 100644 --- a/hw/adc/meson.build +++ b/hw/adc/meson.build @@ -1,5 +1,4 @@ -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')) diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index bc6f3f55e6..de8469dae4 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -218,7 +218,7 @@ static void npcm7xx_adc_enter_reset(Object *obj, ResetType type) npcm7xx_adc_reset(s); } -static void npcm7xx_adc_hold_reset(Object *obj) +static void npcm7xx_adc_hold_reset(Object *obj, ResetType type) { NPCM7xxADCState *s = NPCM7XX_ADC(obj); @@ -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..e3b21f9077 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), @@ -288,7 +288,7 @@ static void stm32f2xx_adc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f2xx_adc_reset; + device_class_set_legacy_reset(dc, stm32f2xx_adc_reset); dc->vmsd = &vmstate_stm32f2xx_adc; } diff --git a/hw/adc/zynq-xadc.c b/hw/adc/zynq-xadc.c index 032e19cbd0..26d9a7b9a5 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), @@ -286,7 +286,7 @@ static void zynq_xadc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_zynq_xadc; - dc->reset = zynq_xadc_reset; + device_class_set_legacy_reset(dc, zynq_xadc_reset); } static const TypeInfo zynq_xadc_info = { diff --git a/hw/alpha/Kconfig b/hw/alpha/Kconfig index 9af650c94e..7f3455ce1e 100644 --- a/hw/alpha/Kconfig +++ b/hw/alpha/Kconfig @@ -1,5 +1,7 @@ config DP264 bool + default y + depends on ALPHA imply PCI_DEVICES imply TEST_DEVICES imply E1000_PCI 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 c502c8c62a..52a1fa310b 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -18,7 +18,6 @@ #include "net/net.h" #include "qemu/cutils.h" #include "qemu/datadir.h" -#include "net/net.h" static uint64_t cpu_alpha_superpage_to_phys(void *opaque, uint64_t addr) { @@ -50,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; @@ -124,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); @@ -214,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 17fcde8e1c..1b25e73578 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -1,21 +1,27 @@ config ARM_VIRT bool + default y + depends on ARM imply PCI_DEVICES imply TEST_DEVICES imply VFIO_AMD_XGBE 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 select GPIO_KEY + select DEVICE_TREE select FW_CFG_DMA select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL061 # GPIO select GPIO_PWR @@ -32,22 +38,23 @@ config ARM_VIRT select ACPI_CXL select ACPI_HMAT -config CHEETAH - bool - 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 @@ -60,12 +67,15 @@ config EXYNOS4 config HIGHBANK bool + default y + depends on TCG && ARM select A9MPCORE select A15MPCORE select AHCI select ARM_TIMER # sp804 select ARM_V7M - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL022 # SPI select PL031 # RTC select PL061 # GPIO @@ -74,9 +84,12 @@ config HIGHBANK config INTEGRATOR bool + default y + depends on TCG && ARM select ARM_TIMER select INTEGRATOR_DEBUG - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL041 # audio select PL050 # keyboard/mouse @@ -84,16 +97,18 @@ config INTEGRATOR select PL181 # display select SMC91C111 -config MAINSTONE +config MPS3R bool - select PXA2XX - select PFLASH_CFI01 - select SMC91C111 + default y + depends on TCG && ARM config MUSCA bool + default y + depends on TCG && ARM select ARMSSE - select PL011 + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 select SPLIT_IRQ select UNIMP @@ -103,88 +118,47 @@ config MARVELL_88W8618 config MUSICPAL bool + default y + depends on TCG && ARM select OR_IRQ select BITBANG_I2C select MARVELL_88W8618 select PTIMER select PFLASH_CFI02 - select SERIAL + select SERIAL_MM select WM8750 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 NSERIES +config OLIMEX_STM32_H405 bool - select OMAP - select TMP105 # tempature sensor - select BLIZZARD # LCD/TV controller - select ONENAND - select TSC210X # touchscreen/sensors/audio - select TSC2005 # touchscreen/sensors/keypad - select LM832X # GPIO keyboard chip - select TWL92230 # energy-management - select TUSB6010 + default y + depends on TCG && ARM + select STM32F405_SOC config OMAP bool select FRAMEBUFFER select I2C - select ECC select NAND select PFLASH_CFI01 select SD - select SERIAL - -config PXA2XX - bool - select FRAMEBUFFER - select I2C - select SERIAL - select SD - select SSI - select USB_OHCI - select PCMCIA - -config GUMSTIX - bool - select PFLASH_CFI01 - select SMC91C111 - select PXA2XX - -config TOSA - bool - select ZAURUS # scoop - select MICRODRIVE - select PXA2XX - select LED - -config SPITZ - bool - select ADS7846 # touch-screen controller - select MAX111X # A/D converter - select WM8750 # audio codec - select MAX7310 # GPIO expander - select ZAURUS # scoop - select NAND # memory - select ECC # Error-correcting for NAND - select MICRODRIVE - select PXA2XX - -config Z2 - bool - select PFLASH_CFI01 - select WM8750 - select PL011 # UART - select PXA2XX + select SERIAL_MM config REALVIEW bool + default y + depends on TCG && ARM imply PCI_DEVICES imply PCI_TESTDEV imply I2C_DEVICES @@ -198,7 +172,8 @@ config REALVIEW select WM8750 # audio codec select LSI_SCSI_PCI select PCI - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL031 # RTC select PL041 # audio codec select PL050 # keyboard/mouse @@ -207,67 +182,88 @@ 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 DEVICE_TREE select AHCI select ARM_SMMUV3 select GPIO_KEY select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # 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 select I2C - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL022 # SPI select PL061 # GPIO 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 bool - select PXA2XX + select PXA2XX_TIMER + select SSI config COLLIE bool + default y + depends on TCG && ARM select PFLASH_CFI01 - select ZAURUS # scoop + 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 @@ -275,17 +271,21 @@ 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 DEVICE_TREE select A9MPCORE select A15MPCORE select ARM_MPTIMER select ARM_TIMER # sp804 select LAN9118 select PFLASH_CFI01 - select PL011 # UART + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select PL041 # audio codec select PL181 # display select REALVIEW @@ -294,9 +294,12 @@ config VEXPRESS config ZYNQ bool + default y + depends on TCG && ARM select A9MPCORE select CADENCE # UART select PFLASH_CFI02 + select PL310 # cache controller select PL330 select SDHCI select SSI_M25P80 @@ -310,37 +313,69 @@ 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 SERIAL + select ALLWINNER_I2C + select ALLWINNER_A10_SPI + select AXP2XX_PMU + select SERIAL_MM 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 SERIAL + select ALLWINNER_I2C + select ALLWINNER_WDT + select SERIAL_MM 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_MM + 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 PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select SDHCI select USB_DWC2 + select BCM2835_SPI + select BCM2835_I2C config STM32F100_SOC bool @@ -362,29 +397,58 @@ config STM32F405_SOC bool select ARM_V7M select OR_IRQ + select STM32_RCC select STM32F4XX_SYSCFG select STM32F4XX_EXTI +config B_L475E_IOT01A + bool + default y + depends on TCG && ARM + select STM32L4X5_SOC + imply DM163 + +config STM32L4X5_SOC + bool + select ARM_V7M + select OR_IRQ + select STM32L4X5_EXTI + select STM32L4X5_SYSCFG + select STM32L4X5_RCC + select STM32L4X5_GPIO + select STM32L4X5_USART + config XLNX_ZYNQMP_ARM bool + default y if PIXMAN + depends on TCG && AARCH64 select AHCI select ARM_GIC select CADENCE + select CPU_CLUSTER select DDC select DPCD + select DEVICE_TREE select SDHCI select SSI select SSI_M25P80 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 CPU_CLUSTER + select DEVICE_TREE + select PL011 if !HAVE_RUST # UART + select X_PL011_RUST if HAVE_RUST # UART select CADENCE select VIRTIO_MMIO select UNIMP @@ -393,10 +457,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 @@ -405,13 +475,16 @@ config NPCM7XX select ISL_PMBUS_VR select PL310 # cache controller select PMBUS - select SERIAL + select SERIAL_MM 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 @@ -421,8 +494,10 @@ config FSL_IMX25 config FSL_IMX31 bool + default y + depends on TCG && ARM imply I2C_DEVICES - select SERIAL + select SERIAL_MM select IMX select IMX_I2C select WDT_IMX2 @@ -430,6 +505,7 @@ config FSL_IMX31 config FSL_IMX6 bool + imply PCIE_DEVICES imply I2C_DEVICES select A9MPCORE select IMX @@ -437,16 +513,20 @@ 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 select DPS310 select PCA9552 - select SERIAL + select SERIAL_MM select SMBUS_EEPROM select PCA954X select SSI @@ -458,9 +538,13 @@ config ASPEED_SOC select LED select PMBUS select MAX31785 + select FSI_APB2OPB_ASPEED + select AT24C config MPS2 bool + default y + depends on TCG && ARM imply I2C_DEVICES select ARMSSE select LAN9118 @@ -472,10 +556,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 @@ -494,6 +580,8 @@ config ARM_SMMUV3 config FSL_IMX6UL bool + default y + depends on TCG && ARM imply I2C_DEVICES select A15MPCORE select IMX @@ -505,6 +593,8 @@ config FSL_IMX6UL config MICROBIT bool + default y + depends on TCG && ARM select NRF51_SOC config NRF51_SOC @@ -516,6 +606,8 @@ config NRF51_SOC config EMCRAFT_SF2 bool + default y + depends on TCG && ARM select MSF2 select SSI_M25P80 @@ -523,30 +615,10 @@ config MSF2 bool select ARM_V7M select PTIMER - select SERIAL + select SERIAL_MM select SSI select UNIMP -config ZAURUS - bool - select NAND - select ECC - -config A9MPCORE - bool - select A9_GTIMER - select A9SCU # snoop control unit - select ARM_GIC - select ARM_MPTIMER - -config A15MPCORE - bool - select ARM_GIC - -config ARM11MPCORE - bool - select ARM11SCU - config ARMSSE bool select ARM_V7M @@ -557,6 +629,7 @@ config ARMSSE select CMSDK_APB_DUALTIMER select CMSDK_APB_UART select CMSDK_APB_WATCHDOG + select CPU_CLUSTER select IOTKIT_SECCTL select IOTKIT_SYSCTL select IOTKIT_SYSINFO diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 79082289ea..52327d9210 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -17,23 +17,48 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/module.h" +#include "hw/char/serial-mm.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 +#define AW_A10_SPI0_BASE 0x01c05000 #define AW_A10_EMAC_BASE 0x01c0b000 #define AW_A10_EHCI_BASE 0x01c14000 #define AW_A10_OHCI_BASE 0x01c14400 #define AW_A10_SATA_BASE 0x01c18000 +#define AW_A10_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_report("%s: failed to read BlockBackend data", __func__); + exit(1); + } + + 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 +71,29 @@ 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); - } + object_initialize_child(obj, "spi0", &s->spi0, TYPE_AW_A10_SPI); + + 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 +133,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 +160,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 +192,20 @@ 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)); + + /* SPI */ + sysbus_realize(SYS_BUS_DEVICE(&s->spi0), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0, AW_A10_SPI0_BASE); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0, qdev_get_gpio_in(dev, 10)); + + /* 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 308ed15552..fd7638dbe8 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -24,12 +24,14 @@ #include "qemu/units.h" #include "hw/qdev-core.h" #include "hw/sysbus.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/misc/unimp.h" #include "hw/usb/hcd-ehci.h" #include "hw/loader.h" #include "sysemu/sysemu.h" #include "hw/arm/allwinner-h3.h" +#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, @@ -175,9 +182,8 @@ void allwinner_h3_bootrom_setup(AwH3State *s, BlockBackend *blk) 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; + error_report("%s: failed to read BlockBackend data", __func__); + exit(1); } rom_add_blob("allwinner-h3.bootrom", buffer, rom_size, @@ -225,6 +231,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 +370,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 +432,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..c6f7cab1da --- /dev/null +++ b/hw/arm/allwinner-r40.c @@ -0,0 +1,564 @@ +/* + * 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-mm.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_report("%s: failed to read BlockBackend data", __func__); + exit(1); + } + + /* 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..255346a595 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), @@ -1691,7 +1700,7 @@ static void armsse_class_init(ObjectClass *klass, void *data) dc->realize = armsse_realize; dc->vmsd = &armsse_vmstate; device_class_set_props(dc, info->props); - dc->reset = armsse_reset; + device_class_set_legacy_reset(dc, armsse_reset); iic->check = armsse_idau_check; asc->info = info; } diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 50a9507c0b..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() diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 97fb1916ec..6ca145362c 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" @@ -37,12 +40,22 @@ struct AspeedMachineState { MachineState parent_obj; /* Public */ - AspeedSoCState soc; + AspeedSoCState *soc; + MemoryRegion boot_rom; bool mmio_exec; + uint32_t uart_chosen; char *fmc_model; char *spi_model; + uint32_t hw_strap1; }; +/* 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) | \ @@ -71,6 +84,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 | \ @@ -156,12 +179,18 @@ struct AspeedMachineState { #define AST2600_EVB_HW_STRAP1 0x000000C0 #define AST2600_EVB_HW_STRAP2 0x00000003 +#ifdef TARGET_AARCH64 +/* AST2700 evb hardware value */ +#define AST2700_EVB_HW_STRAP1 0x000000C0 +#define AST2700_EVB_HW_STRAP2 0x00000003 +#endif + /* Tacoma hardware value */ #define TACOMA_BMC_HW_STRAP1 0x00000000 #define TACOMA_BMC_HW_STRAP2 0x00000040 /* Rainier hardware value: (QEMU prototype) */ -#define RAINIER_BMC_HW_STRAP1 0x00422016 +#define RAINIER_BMC_HW_STRAP1 (0x00422016 | SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC) #define RAINIER_BMC_HW_STRAP2 0x80000848 /* Fuji hardware value */ @@ -231,16 +260,14 @@ 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; - /* The block backend size should have already been 'validated' by + /* + * The block backend size should have already been 'validated' by * the creation of the m25p80 object. */ size = blk_getlength(blk); @@ -262,6 +289,24 @@ static void write_boot_rom(DriveInfo *dinfo, hwaddr addr, size_t rom_size, rom_add_blob_fixed("aspeed.boot_rom", storage, rom_size, addr); } +/* + * 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) { @@ -273,28 +318,41 @@ 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); } } -static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) +static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo, bool emmc, + bool boot_emmc) { DeviceState *card; if (!dinfo) { return; } - card = qdev_new(TYPE_SD_CARD); + card = qdev_new(emmc ? TYPE_EMMC : TYPE_SD_CARD); + + /* + * Force the boot properties of the eMMC device only when the + * machine is strapped to boot from eMMC. Without these + * settings, the machine would not boot. + * + * This also allows the machine to use an eMMC device without + * boot areas when booting from the flash device (or -kernel) + * Ideally, the device and its properties should be defined on + * the command line. + */ + if (emmc && boot_emmc) { + qdev_prop_set_uint64(card, "boot-partition-size", 1 * MiB); + qdev_prop_set_uint8(card, "boot-config", 0x1 << 3); + } qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), &error_fatal); qdev_realize_and_unref(card, @@ -305,12 +363,13 @@ static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo) static void connect_serial_hds_to_uarts(AspeedMachineState *bmc) { AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(bmc); - AspeedSoCState *s = &bmc->soc; + 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, amc->uart_default, serial_hd(0)); - for (int i = 1, uart = ASPEED_DEV_UART1; i < sc->uarts_num; i++, uart++) { - if (uart == 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)); @@ -322,36 +381,37 @@ 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); int i; - NICInfo *nd = &nd_table[0]; + DriveInfo *emmc0 = NULL; + bool boot_emmc; - 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 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", bmc->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), "memory", + object_property_set_link(OBJECT(bmc->soc), "memory", OBJECT(get_system_memory()), &error_abort); - object_property_set_link(OBJECT(&bmc->soc), "dram", + object_property_set_link(OBJECT(bmc->soc), "dram", OBJECT(machine->ram), &error_abort); if (machine->kernel_filename) { /* @@ -359,43 +419,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); } connect_serial_hds_to_uarts(bmc); - qdev_realize(DEVICE(&bmc->soc), NULL, &error_abort); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); - 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) { @@ -418,36 +454,43 @@ 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], - drive_get(IF_SD, 0, 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), false, false); } - if (bmc->soc.emmc.num_slots) { - sdhci_attach_drive(&bmc->soc.emmc.slots[0], - drive_get(IF_SD, 0, bmc->soc.sdhci.num_slots)); + boot_emmc = sc->boot_from_emmc(bmc->soc); + + if (bmc->soc->emmc.num_slots) { + emmc0 = drive_get(IF_SD, 0, bmc->soc->sdhci.num_slots); + sdhci_attach_drive(&bmc->soc->emmc.slots[0], emmc0, true, boot_emmc); + } + + if (!bmc->mmio_exec) { + DeviceState *dev = ssi_get_cs(bmc->soc->fmc.spi, 0); + BlockBackend *fmc0 = dev ? m25p80_get_blk(dev) : NULL; + + if (fmc0 && !boot_emmc) { + uint64_t rom_size = memory_region_size(&bmc->soc->spi_boot); + aspeed_install_boot_rom(bmc, fmc0, rom_size); + } else if (emmc0) { + aspeed_install_boot_rom(bmc, blk_by_legacy_dinfo(emmc0), 64 * KiB); + } } 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); - /* The palmetto platform expects a ds3231 RTC but a ds1338 is - * enough to provide basic RTC features. Alarms will be missing */ + /* + * The palmetto platform expects a ds3231 RTC but a ds1338 is + * enough to provide basic RTC features. Alarms will be missing + */ i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 0), "ds1338", 0x68); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 0), 0x50, @@ -464,7 +507,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 @@ -496,7 +539,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, @@ -509,7 +552,7 @@ static void ast2500_evb_i2c_init(AspeedMachineState *bmc) 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, @@ -520,15 +563,44 @@ 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 */ + /* + * 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), @@ -537,7 +609,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); @@ -591,7 +663,7 @@ 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; @@ -620,8 +692,10 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 9), TYPE_TMP105, 0x4a); - /* The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is - * good enough */ + /* + * The witherspoon 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); smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 11), 0x51, @@ -636,7 +710,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), @@ -670,18 +744,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 */ @@ -708,10 +773,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); @@ -724,9 +789,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, @@ -737,8 +802,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); @@ -748,10 +813,10 @@ 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); @@ -764,15 +829,17 @@ static void rainier_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), TYPE_TMP105, 0x48); i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 7), "max31785", 0x52); - 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); + 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 */ @@ -781,11 +848,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); @@ -793,18 +860,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); } @@ -823,7 +890,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++) { @@ -848,45 +915,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); @@ -897,7 +968,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)) { @@ -943,7 +1014,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++) { @@ -957,11 +1028,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 @@ -972,14 +1046,14 @@ static void fby35_i2c_init(AspeedMachineState *bmc) static void qcom_dc_scm_bmc_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + 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; + AspeedSoCState *soc = bmc->soc; I2CSlave *therm_mux, *cpuvr_mux; /* Create the generic DC-SCM hardware */ @@ -1021,7 +1095,10 @@ static void aspeed_set_mmio_exec(Object *obj, bool value, Error **errp) static void aspeed_machine_instance_init(Object *obj) { + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(obj); + ASPEED_MACHINE(obj)->mmio_exec = false; + ASPEED_MACHINE(obj)->hw_strap1 = amc->hw_strap1; } static char *aspeed_get_fmc_model(Object *obj, Error **errp) @@ -1052,6 +1129,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", @@ -1060,6 +1169,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", @@ -1070,10 +1184,43 @@ 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 bool aspeed_machine_ast2600_get_boot_from_emmc(Object *obj, Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + + return !!(bmc->hw_strap1 & SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC); +} + +static void aspeed_machine_ast2600_set_boot_from_emmc(Object *obj, bool value, + Error **errp) +{ + AspeedMachineState *bmc = ASPEED_MACHINE(obj); + + if (value) { + bmc->hw_strap1 |= SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC; + } else { + bmc->hw_strap1 &= ~SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC; + } +} + +static void aspeed_machine_ast2600_class_emmc_init(ObjectClass *oc) +{ + object_class_property_add_bool(oc, "boot-emmc", + aspeed_machine_ast2600_get_boot_from_emmc, + aspeed_machine_ast2600_set_boot_from_emmc); + object_class_property_set_description(oc, "boot-emmc", + "Set or unset boot from EMMC"); } static void aspeed_machine_class_init(ObjectClass *oc, void *data) @@ -1105,8 +1252,7 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) 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) @@ -1122,8 +1268,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, @@ -1141,6 +1286,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) @@ -1156,8 +1320,24 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) 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) @@ -1173,8 +1353,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) @@ -1190,8 +1386,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) @@ -1207,8 +1402,7 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) 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) @@ -1227,8 +1421,8 @@ 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); + aspeed_machine_ast2600_class_emmc_init(oc); }; static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data) @@ -1246,8 +1440,9 @@ 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); + + mc->deprecation_reason = "Please use the similar 'rainier-bmc' machine"; }; static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) @@ -1264,8 +1459,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) @@ -1282,8 +1476,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) @@ -1301,16 +1494,11 @@ 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); + aspeed_machine_ast2600_class_emmc_init(oc); }; -/* 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) { @@ -1328,16 +1516,10 @@ 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); }; -/* On 32-bit hosts, lower RAM to 1G because of the 2047 MB limit */ -#if HOST_LONG_BITS == 32 -#define BLETCHLEY_BMC_RAM_SIZE (1 * GiB) -#else -#define BLETCHLEY_BMC_RAM_SIZE (2 * GiB) -#endif +#define BLETCHLEY_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) { @@ -1354,16 +1536,15 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) amc->macs_mask = ASPEED_MAC2_ON; amc->i2c_init = bletchley_bmc_i2c_init; mc->default_ram_size = BLETCHLEY_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); } -static void fby35_reset(MachineState *state, ShutdownCause reason) +static void fby35_reset(MachineState *state, ResetType type) { AspeedMachineState *bmc = ASPEED_MACHINE(state); - AspeedGPIOState *gpio = &bmc->soc.gpio; + AspeedGPIOState *gpio = &bmc->soc->gpio; - qemu_devices_reset(reason); + qemu_devices_reset(type); /* Board ID: 7 (Class-1, 4 slots) */ object_property_set_bool(OBJECT(gpio), "gpioV4", true, &error_fatal); @@ -1397,6 +1578,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) @@ -1412,26 +1594,30 @@ 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); - object_property_set_link(OBJECT(&bmc->soc), "memory", + 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); + qdev_realize(DEVICE(bmc->soc), NULL, &error_abort); - aspeed_board_init_flashes(&bmc->soc.fmc, - bmc->fmc_model ? bmc->fmc_model : amc->fmc_model, - amc->num_cs, - 0); + 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], - bmc->spi_model ? bmc->spi_model : amc->spi_model, - amc->num_cs, amc->num_cs); + aspeed_board_init_flashes(&bmc->soc->spi[0], + bmc->spi_model ? bmc->spi_model : amc->spi_model, + amc->num_cs, amc->num_cs); - aspeed_board_init_flashes(&bmc->soc.spi[1], - bmc->spi_model ? bmc->spi_model : amc->spi_model, - amc->num_cs, (amc->num_cs * 2)); + aspeed_board_init_flashes(&bmc->soc->spi[1], + bmc->spi_model ? bmc->spi_model : amc->spi_model, + amc->num_cs, (amc->num_cs * 2)); + } if (amc->i2c_init) { amc->i2c_init(bmc); @@ -1445,9 +1631,9 @@ static void aspeed_minibmc_machine_init(MachineState *machine) static void ast1030_evb_i2c_init(AspeedMachineState *bmc) { - AspeedSoCState *soc = &bmc->soc; + AspeedSoCState *soc = bmc->soc; - /* U10 24C08 connects to SDA/SCL Groupt 1 by default */ + /* 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); @@ -1468,13 +1654,43 @@ static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, 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->fmc_model = "w25q80bl"; + amc->spi_model = "w25q256"; amc->num_cs = 2; amc->macs_mask = 0; + aspeed_machine_class_init_cpus_defaults(mc); } +#ifdef TARGET_AARCH64 +static void ast2700_evb_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + + /* LM75 is compatible with TMP105 driver */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 0), + TYPE_TMP105, 0x4d); +} + +static void aspeed_machine_ast2700_evb_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Aspeed AST2700 EVB (Cortex-A35)"; + amc->soc_name = "ast2700-a0"; + amc->hw_strap1 = AST2700_EVB_HW_STRAP1; + amc->hw_strap2 = AST2700_EVB_HW_STRAP2; + amc->fmc_model = "w25q01jvq"; + amc->spi_model = "w25q512jv"; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; + amc->uart_default = ASPEED_DEV_UART12; + amc->i2c_init = ast2700_evb_i2c_init; + mc->default_ram_size = 1 * GiB; + aspeed_machine_class_init_cpus_defaults(mc); +} +#endif + static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, void *data) { @@ -1491,8 +1707,7 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_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); }; static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, @@ -1511,8 +1726,7 @@ static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, amc->macs_mask = ASPEED_MAC2_ON | ASPEED_MAC3_ON; amc->i2c_init = qcom_dc_scm_firework_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 const TypeInfo aspeed_machine_types[] = { @@ -1524,6 +1738,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, @@ -1544,10 +1762,18 @@ 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, @@ -1588,6 +1814,12 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("ast1030-evb"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_minibmc_machine_ast1030_evb_class_init, +#ifdef TARGET_AARCH64 + }, { + .name = MACHINE_TYPE_NAME("ast2700-evb"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_ast2700_evb_class_init, +#endif }, { .name = TYPE_ASPEED_MACHINE, .parent = TYPE_MACHINE, diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index 4d0b9b115f..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, @@ -48,6 +54,7 @@ static const hwaddr aspeed_soc_ast1030_memmap[] = { [ASPEED_DEV_WDT] = 0x7E785000, [ASPEED_DEV_LPC] = 0x7E789000, [ASPEED_DEV_PECI] = 0x7E78B000, + [ASPEED_DEV_I3C] = 0x7E7A0000, [ASPEED_DEV_I2C] = 0x7E7B0000, }; @@ -78,23 +85,31 @@ static const int aspeed_soc_ast1030_irqmap[] = { [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]; @@ -105,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); @@ -119,6 +134,8 @@ static void aspeed_soc_ast1030_init(Object *obj) 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); @@ -151,13 +168,26 @@ static void aspeed_soc_ast1030_init(Object *obj) 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); DeviceState *armv7m; @@ -179,17 +209,17 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) 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_property_set_link(OBJECT(&a->armv7m), "memory", OBJECT(s->memory), &error_abort); - sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); /* Internal SRAM */ sram_name = g_strdup_printf("aspeed.sram.%d", - CPU(s->armv7m.cpu)->cpu_index); + 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); @@ -198,6 +228,14 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) 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)) { @@ -214,12 +252,24 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } 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->armv7m), + 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; @@ -243,19 +293,19 @@ 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 */ @@ -315,17 +365,28 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) } 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; } - aspeed_mmio_map(s, 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 */ @@ -336,42 +397,66 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) 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_ast2400.c b/hw/arm/aspeed_ast2400.c new file mode 100644 index 0000000000..ecc81ecc79 --- /dev/null +++ b/hw/arm/aspeed_ast2400.c @@ -0,0 +1,580 @@ +/* + * 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 "qemu/units.h" +#include "qapi/error.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/char/serial-mm.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#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, + [ASPEED_DEV_EHCI1] = 0x1E6A1000, + [ASPEED_DEV_VIC] = 0x1E6C0000, + [ASPEED_DEV_SDMC] = 0x1E6E0000, + [ASPEED_DEV_SCU] = 0x1E6E2000, + [ASPEED_DEV_HACE] = 0x1E6E3000, + [ASPEED_DEV_XDMA] = 0x1E6E7000, + [ASPEED_DEV_VIDEO] = 0x1E700000, + [ASPEED_DEV_ADC] = 0x1E6E9000, + [ASPEED_DEV_SRAM] = 0x1E720000, + [ASPEED_DEV_SDHCI] = 0x1E740000, + [ASPEED_DEV_GPIO] = 0x1E780000, + [ASPEED_DEV_RTC] = 0x1E781000, + [ASPEED_DEV_TIMER1] = 0x1E782000, + [ASPEED_DEV_WDT] = 0x1E785000, + [ASPEED_DEV_PWM] = 0x1E786000, + [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, + [ASPEED_DEV_UART2] = 0x1E78D000, + [ASPEED_DEV_UART3] = 0x1E78E000, + [ASPEED_DEV_UART4] = 0x1E78F000, + [ASPEED_DEV_UART5] = 0x1E784000, + [ASPEED_DEV_VUART] = 0x1E787000, + [ASPEED_DEV_SDRAM] = 0x40000000, +}; + +static const hwaddr aspeed_soc_ast2500_memmap[] = { + [ASPEED_DEV_SPI_BOOT] = 0x00000000, + [ASPEED_DEV_IOMEM] = 0x1E600000, + [ASPEED_DEV_FMC] = 0x1E620000, + [ASPEED_DEV_SPI1] = 0x1E630000, + [ASPEED_DEV_SPI2] = 0x1E631000, + [ASPEED_DEV_EHCI1] = 0x1E6A1000, + [ASPEED_DEV_EHCI2] = 0x1E6A3000, + [ASPEED_DEV_VIC] = 0x1E6C0000, + [ASPEED_DEV_SDMC] = 0x1E6E0000, + [ASPEED_DEV_SCU] = 0x1E6E2000, + [ASPEED_DEV_HACE] = 0x1E6E3000, + [ASPEED_DEV_XDMA] = 0x1E6E7000, + [ASPEED_DEV_ADC] = 0x1E6E9000, + [ASPEED_DEV_VIDEO] = 0x1E700000, + [ASPEED_DEV_SRAM] = 0x1E720000, + [ASPEED_DEV_SDHCI] = 0x1E740000, + [ASPEED_DEV_GPIO] = 0x1E780000, + [ASPEED_DEV_RTC] = 0x1E781000, + [ASPEED_DEV_TIMER1] = 0x1E782000, + [ASPEED_DEV_WDT] = 0x1E785000, + [ASPEED_DEV_PWM] = 0x1E786000, + [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, + [ASPEED_DEV_UART2] = 0x1E78D000, + [ASPEED_DEV_UART3] = 0x1E78E000, + [ASPEED_DEV_UART4] = 0x1E78F000, + [ASPEED_DEV_UART5] = 0x1E784000, + [ASPEED_DEV_VUART] = 0x1E787000, + [ASPEED_DEV_SDRAM] = 0x80000000, +}; + +static const int aspeed_soc_ast2400_irqmap[] = { + [ASPEED_DEV_UART1] = 9, + [ASPEED_DEV_UART2] = 32, + [ASPEED_DEV_UART3] = 33, + [ASPEED_DEV_UART4] = 34, + [ASPEED_DEV_UART5] = 10, + [ASPEED_DEV_VUART] = 8, + [ASPEED_DEV_FMC] = 19, + [ASPEED_DEV_EHCI1] = 5, + [ASPEED_DEV_EHCI2] = 13, + [ASPEED_DEV_SDMC] = 0, + [ASPEED_DEV_SCU] = 21, + [ASPEED_DEV_ADC] = 31, + [ASPEED_DEV_GPIO] = 20, + [ASPEED_DEV_RTC] = 22, + [ASPEED_DEV_TIMER1] = 16, + [ASPEED_DEV_TIMER2] = 17, + [ASPEED_DEV_TIMER3] = 18, + [ASPEED_DEV_TIMER4] = 35, + [ASPEED_DEV_TIMER5] = 36, + [ASPEED_DEV_TIMER6] = 37, + [ASPEED_DEV_TIMER7] = 38, + [ASPEED_DEV_TIMER8] = 39, + [ASPEED_DEV_WDT] = 27, + [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, + [ASPEED_DEV_SDHCI] = 26, + [ASPEED_DEV_HACE] = 4, +}; + +#define aspeed_soc_ast2500_irqmap 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(&a->vic), sc->irqmap[dev]); +} + +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; + char socname[8]; + char typename[64]; + + if (sscanf(sc->name, "%7s", socname) != 1) { + g_assert_not_reached(); + } + + for (i = 0; i < sc->num_cpus; i++) { + object_initialize_child(obj, "cpu[*]", &a->cpu[i], + aspeed_soc_cpu_type(sc)); + } + + snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); + object_initialize_child(obj, "scu", &s->scu, typename); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", + sc->silicon_rev); + object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), + "hw-strap1"); + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), + "hw-strap2"); + object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), + "hw-prot-key"); + + object_initialize_child(obj, "vic", &a->vic, TYPE_ASPEED_VIC); + + object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); + + snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); + object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); + + snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); + object_initialize_child(obj, "adc", &s->adc, typename); + + snprintf(typename, sizeof(typename), "aspeed.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); + + for (i = 0; i < sc->spis_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); + object_initialize_child(obj, "spi[*]", &s->spi[i], typename); + } + + for (i = 0; i < sc->ehcis_num; i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], + TYPE_PLATFORM_EHCI); + } + + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); + object_initialize_child(obj, "sdmc", &s->sdmc, typename); + object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), + "ram-size"); + + for (i = 0; i < sc->wdts_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); + object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); + } + + for (i = 0; i < sc->macs_num; i++) { + object_initialize_child(obj, "ftgmac100[*]", &s->ftgmac100[i], + 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); + + snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); + object_initialize_child(obj, "gpio", &s->gpio, typename); + + object_initialize_child(obj, "sdc", &s->sdhci, TYPE_ASPEED_SDHCI); + + object_property_set_int(OBJECT(&s->sdhci), "num-slots", 2, &error_abort); + + /* Init sd card slot class here so that they're under the correct parent */ + for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { + object_initialize_child(obj, "sdhci[*]", &s->sdhci.slots[i], + TYPE_SYSBUS_SDHCI); + } + + object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); + + 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_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); + 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 */ + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + ASPEED_SOC_IOMEM_SIZE); + + /* Video engine stub */ + 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++) { + 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 */ + 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(s->memory, + sc->memmap[ASPEED_DEV_SRAM], &s->sram); + + /* SCU */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* VIC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->vic), errp)) { + return; + } + 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; + } + 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)); + + /* Timer */ + object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { + return; + } + 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); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); + } + + /* ADC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { + return; + } + 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 */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + /* I2C */ + object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), + &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]); + 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; + } + 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; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + sc->memmap[ASPEED_DEV_SPI1 + i]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); + } + + /* EHCI */ + for (i = 0; i < sc->ehcis_num; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { + return; + } + 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)); + } + + /* SDMC - SDRAM Memory Controller */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { + return; + } + 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; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + } + + /* RAM */ + if (!aspeed_soc_dram_init(s, errp)) { + return; + } + + /* Net */ + for (i = 0; i < sc->macs_num; i++) { + object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { + return; + } + 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)); + } + + /* XDMA */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { + return; + } + 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)); + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + 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)); + + /* SDHCI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { + return; + } + 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)); + + /* LPC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { + return; + } + 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, + aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); + + /* + * On the AST2400 and AST2500 the one LPC IRQ is shared between all of the + * subdevices. Connect the LPC subdevice IRQs to the LPC controller IRQ (by + * contrast, on the AST2600, the subdevice IRQs are connected straight to + * the GIC). + * + * LPC subdevice IRQ sources are offset from 1 because the shared IRQ output + * to the VIC is at offset 0. + */ + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_1)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_2)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_3)); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, + qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_4)); + + /* HACE */ + object_property_set_link(OBJECT(&s->hace), "dram", OBJECT(s->dram_mr), + &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)); +} + +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->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2400_A1_SILICON_REV; + sc->sram_size = 0x8000; + sc->spis_num = 1; + sc->ehcis_num = 1; + sc->wdts_num = 2; + sc->macs_num = 2; + sc->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 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->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2500_A1_SILICON_REV; + sc->sram_size = 0x9000; + sc->spis_num = 2; + sc->ehcis_num = 2; + sc->wdts_num = 3; + sc->macs_num = 2; + sc->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_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, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast2400_types) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index cd75465c2b..be3eb70cdd 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 */ @@ -74,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, }; @@ -131,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; @@ -154,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); @@ -168,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); @@ -261,6 +270,10 @@ static void aspeed_soc_ast2600_init(Object *obj) 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); + } } /* @@ -276,12 +289,18 @@ 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 */ aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", sc->memmap[ASPEED_DEV_IOMEM], @@ -299,37 +318,39 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* 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(&s->cpu[i]), "neon", false, + object_property_set_bool(OBJECT(&a->cpu[i]), "neon", false, &error_abort); - object_property_set_link(OBJECT(&s->cpu[i]), "memory", + 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); - aspeed_mmio_map(s, 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(&s->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); @@ -342,10 +363,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } /* SRAM */ - sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&s->cpu[0])->cpu_index); - memory_region_init_ram(&s->sram, OBJECT(s), sram_name, 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(s->memory, @@ -379,7 +399,7 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) 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); } @@ -404,8 +424,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } 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); } @@ -431,6 +451,12 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) 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", @@ -465,14 +491,14 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) /* 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; } - aspeed_mmio_map(s, 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 */ @@ -564,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 */ @@ -596,8 +622,8 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) } 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); } @@ -607,17 +633,41 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp) return; } 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 bool aspeed_soc_ast2600_boot_from_emmc(AspeedSoCState *s) +{ + uint32_t hw_strap1 = object_property_get_uint(OBJECT(&s->scu), + "hw-strap1", &error_abort); + return !!(hw_strap1 & SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC); } 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; @@ -625,24 +675,26 @@ 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; + sc->boot_from_emmc = aspeed_soc_ast2600_boot_from_emmc; } -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_ast27x0.c b/hw/arm/aspeed_ast27x0.c new file mode 100644 index 0000000000..63d1fcb086 --- /dev/null +++ b/hw/arm/aspeed_ast27x0.c @@ -0,0 +1,733 @@ +/* + * ASPEED SoC 27x0 family + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * Implementation extracted from the AST2600 and adapted for AST27x0. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/arm/bsa.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "hw/i2c/aspeed_i2c.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "hw/intc/arm_gicv3.h" +#include "qapi/qmp/qlist.h" +#include "qemu/log.h" + +static const hwaddr aspeed_soc_ast2700_memmap[] = { + [ASPEED_DEV_SPI_BOOT] = 0x400000000, + [ASPEED_DEV_SRAM] = 0x10000000, + [ASPEED_DEV_SDMC] = 0x12C00000, + [ASPEED_DEV_SCU] = 0x12C02000, + [ASPEED_DEV_SCUIO] = 0x14C02000, + [ASPEED_DEV_UART0] = 0X14C33000, + [ASPEED_DEV_UART1] = 0X14C33100, + [ASPEED_DEV_UART2] = 0X14C33200, + [ASPEED_DEV_UART3] = 0X14C33300, + [ASPEED_DEV_UART4] = 0X12C1A000, + [ASPEED_DEV_UART5] = 0X14C33400, + [ASPEED_DEV_UART6] = 0X14C33500, + [ASPEED_DEV_UART7] = 0X14C33600, + [ASPEED_DEV_UART8] = 0X14C33700, + [ASPEED_DEV_UART9] = 0X14C33800, + [ASPEED_DEV_UART10] = 0X14C33900, + [ASPEED_DEV_UART11] = 0X14C33A00, + [ASPEED_DEV_UART12] = 0X14C33B00, + [ASPEED_DEV_WDT] = 0x14C37000, + [ASPEED_DEV_VUART] = 0X14C30000, + [ASPEED_DEV_FMC] = 0x14000000, + [ASPEED_DEV_SPI0] = 0x14010000, + [ASPEED_DEV_SPI1] = 0x14020000, + [ASPEED_DEV_SPI2] = 0x14030000, + [ASPEED_DEV_SDRAM] = 0x400000000, + [ASPEED_DEV_MII1] = 0x14040000, + [ASPEED_DEV_MII2] = 0x14040008, + [ASPEED_DEV_MII3] = 0x14040010, + [ASPEED_DEV_ETH1] = 0x14050000, + [ASPEED_DEV_ETH2] = 0x14060000, + [ASPEED_DEV_ETH3] = 0x14070000, + [ASPEED_DEV_EMMC] = 0x12090000, + [ASPEED_DEV_INTC] = 0x12100000, + [ASPEED_DEV_SLI] = 0x12C17000, + [ASPEED_DEV_SLIIO] = 0x14C1E000, + [ASPEED_GIC_DIST] = 0x12200000, + [ASPEED_GIC_REDIST] = 0x12280000, + [ASPEED_DEV_ADC] = 0x14C00000, + [ASPEED_DEV_I2C] = 0x14C0F000, + [ASPEED_DEV_GPIO] = 0x14C0B000, + [ASPEED_DEV_RTC] = 0x12C0F000, +}; + +#define AST2700_MAX_IRQ 256 + +/* Shared Peripheral Interrupt values below are offset by -32 from datasheet */ +static const int aspeed_soc_ast2700_irqmap[] = { + [ASPEED_DEV_UART0] = 132, + [ASPEED_DEV_UART1] = 132, + [ASPEED_DEV_UART2] = 132, + [ASPEED_DEV_UART3] = 132, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_UART5] = 132, + [ASPEED_DEV_UART6] = 132, + [ASPEED_DEV_UART7] = 132, + [ASPEED_DEV_UART8] = 132, + [ASPEED_DEV_UART9] = 132, + [ASPEED_DEV_UART10] = 132, + [ASPEED_DEV_UART11] = 132, + [ASPEED_DEV_UART12] = 132, + [ASPEED_DEV_FMC] = 131, + [ASPEED_DEV_SDMC] = 0, + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_ADC] = 130, + [ASPEED_DEV_XDMA] = 5, + [ASPEED_DEV_EMMC] = 15, + [ASPEED_DEV_GPIO] = 130, + [ASPEED_DEV_RTC] = 13, + [ASPEED_DEV_TIMER1] = 16, + [ASPEED_DEV_TIMER2] = 17, + [ASPEED_DEV_TIMER3] = 18, + [ASPEED_DEV_TIMER4] = 19, + [ASPEED_DEV_TIMER5] = 20, + [ASPEED_DEV_TIMER6] = 21, + [ASPEED_DEV_TIMER7] = 22, + [ASPEED_DEV_TIMER8] = 23, + [ASPEED_DEV_WDT] = 131, + [ASPEED_DEV_PWM] = 131, + [ASPEED_DEV_LPC] = 128, + [ASPEED_DEV_IBT] = 128, + [ASPEED_DEV_I2C] = 130, + [ASPEED_DEV_PECI] = 133, + [ASPEED_DEV_ETH1] = 132, + [ASPEED_DEV_ETH2] = 132, + [ASPEED_DEV_ETH3] = 132, + [ASPEED_DEV_HACE] = 4, + [ASPEED_DEV_KCS] = 128, + [ASPEED_DEV_DP] = 28, + [ASPEED_DEV_I3C] = 131, +}; + +/* GICINT 128 */ +static const int aspeed_soc_ast2700_gic128_intcmap[] = { + [ASPEED_DEV_LPC] = 0, + [ASPEED_DEV_IBT] = 2, + [ASPEED_DEV_KCS] = 4, +}; + +/* GICINT 130 */ +static const int aspeed_soc_ast2700_gic130_intcmap[] = { + [ASPEED_DEV_I2C] = 0, + [ASPEED_DEV_ADC] = 16, + [ASPEED_DEV_GPIO] = 18, +}; + +/* GICINT 131 */ +static const int aspeed_soc_ast2700_gic131_intcmap[] = { + [ASPEED_DEV_I3C] = 0, + [ASPEED_DEV_WDT] = 16, + [ASPEED_DEV_FMC] = 25, + [ASPEED_DEV_PWM] = 29, +}; + +/* GICINT 132 */ +static const int aspeed_soc_ast2700_gic132_intcmap[] = { + [ASPEED_DEV_ETH1] = 0, + [ASPEED_DEV_ETH2] = 1, + [ASPEED_DEV_ETH3] = 2, + [ASPEED_DEV_UART0] = 7, + [ASPEED_DEV_UART1] = 8, + [ASPEED_DEV_UART2] = 9, + [ASPEED_DEV_UART3] = 10, + [ASPEED_DEV_UART5] = 11, + [ASPEED_DEV_UART6] = 12, + [ASPEED_DEV_UART7] = 13, + [ASPEED_DEV_UART8] = 14, + [ASPEED_DEV_UART9] = 15, + [ASPEED_DEV_UART10] = 16, + [ASPEED_DEV_UART11] = 17, + [ASPEED_DEV_UART12] = 18, +}; + +/* GICINT 133 */ +static const int aspeed_soc_ast2700_gic133_intcmap[] = { + [ASPEED_DEV_PECI] = 4, +}; + +/* GICINT 128 ~ 136 */ +struct gic_intc_irq_info { + int irq; + const int *ptr; +}; + +static const struct gic_intc_irq_info aspeed_soc_ast2700_gic_intcmap[] = { + {128, aspeed_soc_ast2700_gic128_intcmap}, + {129, NULL}, + {130, aspeed_soc_ast2700_gic130_intcmap}, + {131, aspeed_soc_ast2700_gic131_intcmap}, + {132, aspeed_soc_ast2700_gic132_intcmap}, + {133, aspeed_soc_ast2700_gic133_intcmap}, + {134, NULL}, + {135, NULL}, + {136, NULL}, +}; + +static qemu_irq aspeed_soc_ast2700_get_irq(AspeedSoCState *s, int dev) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + for (i = 0; i < ARRAY_SIZE(aspeed_soc_ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == aspeed_soc_ast2700_gic_intcmap[i].irq) { + assert(aspeed_soc_ast2700_gic_intcmap[i].ptr); + return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), + aspeed_soc_ast2700_gic_intcmap[i].ptr[dev]); + } + } + + return qdev_get_gpio_in(DEVICE(&a->gic), sc->irqmap[dev]); +} + +static qemu_irq aspeed_soc_ast2700_get_irq_index(AspeedSoCState *s, int dev, + int index) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + for (i = 0; i < ARRAY_SIZE(aspeed_soc_ast2700_gic_intcmap); i++) { + if (sc->irqmap[dev] == aspeed_soc_ast2700_gic_intcmap[i].irq) { + assert(aspeed_soc_ast2700_gic_intcmap[i].ptr); + return qdev_get_gpio_in(DEVICE(&a->intc.orgates[i]), + aspeed_soc_ast2700_gic_intcmap[i].ptr[dev] + index); + } + } + + /* + * Invalid orgate index, device irq should be 128 to 136. + */ + g_assert_not_reached(); +} + +static uint64_t aspeed_ram_capacity_read(void *opaque, hwaddr addr, + unsigned int size) +{ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DRAM read out of ram size, addr:0x%" PRIx64 "\n", + __func__, addr); + return 0; +} + +static void aspeed_ram_capacity_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSoCState *s = ASPEED_SOC(opaque); + ram_addr_t ram_size; + MemTxResult result; + + ram_size = object_property_get_uint(OBJECT(&s->sdmc), "ram-size", + &error_abort); + + assert(ram_size > 0); + + /* + * Emulate ddr capacity hardware behavior. + * If writes the data to the address which is beyond the ram size, + * it would write the data to the "address % ram_size". + */ + result = address_space_write(&s->dram_as, addr % ram_size, + MEMTXATTRS_UNSPECIFIED, &data, 4); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DRAM write failed, addr:0x%" HWADDR_PRIx + ", data :0x%" PRIx64 "\n", + __func__, addr % ram_size, data); + } +} + +static const MemoryRegionOps aspeed_ram_capacity_ops = { + .read = aspeed_ram_capacity_read, + .write = aspeed_ram_capacity_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +/* + * SDMC should be realized first to get correct RAM size and max size + * values + */ +static bool aspeed_soc_ast2700_dram_init(DeviceState *dev, Error **errp) +{ + ram_addr_t ram_size, max_ram_size; + Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + 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", + ram_size); + memory_region_add_subregion(&s->dram_container, 0, s->dram_mr); + address_space_init(&s->dram_as, s->dram_mr, "dram"); + + /* + * Add a memory region beyond the RAM region to emulate + * ddr capacity hardware behavior. + */ + if (ram_size < max_ram_size) { + memory_region_init_io(&a->dram_empty, OBJECT(s), + &aspeed_ram_capacity_ops, s, + "ram-empty", max_ram_size - ram_size); + + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SDRAM] + ram_size, + &a->dram_empty); + } + + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SDRAM], &s->dram_container); + return true; +} + +static void aspeed_soc_ast2700_init(Object *obj) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(obj); + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + char socname[8]; + char typename[64]; + + if (sscanf(sc->name, "%7s", socname) != 1) { + g_assert_not_reached(); + } + + for (i = 0; i < sc->num_cpus; i++) { + object_initialize_child(obj, "cpu[*]", &a->cpu[i], + aspeed_soc_cpu_type(sc)); + } + + object_initialize_child(obj, "gic", &a->gic, gicv3_class_name()); + + object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", + sc->silicon_rev); + object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), + "hw-strap1"); + object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), + "hw-strap2"); + object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), + "hw-prot-key"); + + object_initialize_child(obj, "scuio", &s->scuio, TYPE_ASPEED_2700_SCUIO); + qdev_prop_set_uint32(DEVICE(&s->scuio), "silicon-rev", + sc->silicon_rev); + + snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname); + object_initialize_child(obj, "fmc", &s->fmc, typename); + + for (i = 0; i < sc->spis_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i, socname); + object_initialize_child(obj, "spi[*]", &s->spi[i], typename); + } + + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); + object_initialize_child(obj, "sdmc", &s->sdmc, typename); + object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), + "ram-size"); + + for (i = 0; i < sc->wdts_num; i++) { + snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); + object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); + } + + for (i = 0; i < sc->macs_num; i++) { + object_initialize_child(obj, "ftgmac100[*]", &s->ftgmac100[i], + TYPE_FTGMAC100); + + 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); + } + + object_initialize_child(obj, "sli", &s->sli, TYPE_ASPEED_2700_SLI); + object_initialize_child(obj, "sliio", &s->sliio, TYPE_ASPEED_2700_SLIIO); + object_initialize_child(obj, "intc", &a->intc, TYPE_ASPEED_2700_INTC); + + snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); + object_initialize_child(obj, "adc", &s->adc, typename); + + snprintf(typename, sizeof(typename), "aspeed.i2c-%s", socname); + object_initialize_child(obj, "i2c", &s->i2c, typename); + + snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); + object_initialize_child(obj, "gpio", &s->gpio, typename); + + object_initialize_child(obj, "rtc", &s->rtc, TYPE_ASPEED_RTC); +} + +/* + * ASPEED ast2700 has 0x0 as cluster ID + * + * https://developer.arm.com/documentation/100236/0100/register-descriptions/aarch64-system-registers/multiprocessor-affinity-register--el1 + */ +static uint64_t aspeed_calc_affinity(int cpu) +{ + return (0x0 << ARM_AFF1_SHIFT) | cpu; +} + +static bool aspeed_soc_ast2700_gic_realize(DeviceState *dev, Error **errp) +{ + Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + SysBusDevice *gicbusdev; + DeviceState *gicdev; + QList *redist_region_count; + int i; + + gicbusdev = SYS_BUS_DEVICE(&a->gic); + gicdev = DEVICE(&a->gic); + qdev_prop_set_uint32(gicdev, "revision", 3); + qdev_prop_set_uint32(gicdev, "num-cpu", sc->num_cpus); + qdev_prop_set_uint32(gicdev, "num-irq", AST2700_MAX_IRQ + GIC_INTERNAL); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, sc->num_cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + + if (!sysbus_realize(gicbusdev, errp)) { + return false; + } + sysbus_mmio_map(gicbusdev, 0, sc->memmap[ASPEED_GIC_DIST]); + sysbus_mmio_map(gicbusdev, 1, sc->memmap[ASPEED_GIC_REDIST]); + + for (i = 0; i < sc->num_cpus; i++) { + DeviceState *cpudev = DEVICE(&a->cpu[i]); + int intidbase = AST2700_MAX_IRQ + i * GIC_INTERNAL; + + const int timer_irq[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + int j; + + for (j = 0; j < ARRAY_SIZE(timer_irq); j++) { + qdev_connect_gpio_out(cpudev, j, + qdev_get_gpio_in(gicdev, intidbase + timer_irq[j])); + } + + qemu_irq irq = qdev_get_gpio_in(gicdev, + intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ)); + + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * sc->num_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + } + + return true; +} + +static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) +{ + int i; + Aspeed27x0SoCState *a = ASPEED27X0_SOC(dev); + AspeedSoCState *s = ASPEED_SOC(dev); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc); + g_autofree char *sram_name = NULL; + qemu_irq irq; + + /* Default boot region (SPI memory or ROMs) */ + memory_region_init(&s->spi_boot_container, OBJECT(s), + "aspeed.spi_boot_container", 0x400000000); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SPI_BOOT], + &s->spi_boot_container); + + /* CPU */ + for (i = 0; i < sc->num_cpus; i++) { + object_property_set_int(OBJECT(&a->cpu[i]), "mp-affinity", + aspeed_calc_affinity(i), &error_abort); + + object_property_set_int(OBJECT(&a->cpu[i]), "cntfrq", 1125000000, + &error_abort); + object_property_set_link(OBJECT(&a->cpu[i]), "memory", + OBJECT(s->memory), &error_abort); + + if (!qdev_realize(DEVICE(&a->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + if (!aspeed_soc_ast2700_gic_realize(dev, errp)) { + return; + } + + /* INTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* GICINT orgates -> INTC -> GIC */ + for (i = 0; i < ic->num_ints; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc.orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc), i)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc), i, + qdev_get_gpio_in(DEVICE(&a->gic), + aspeed_soc_ast2700_gic_intcmap[i].irq)); + } + + /* SRAM */ + 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(s->memory, + sc->memmap[ASPEED_DEV_SRAM], &s->sram); + + /* SCU */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* SCU1 */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scuio), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scuio), 0, + sc->memmap[ASPEED_DEV_SCUIO]); + + /* UART */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + /* FMC, The number of CS is set at the board level */ + object_property_set_int(OBJECT(&s->fmc), "dram-base", + sc->memmap[ASPEED_DEV_SDRAM], + &error_abort); + object_property_set_link(OBJECT(&s->fmc), "dram", OBJECT(s->dram_mr), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->fmc), errp)) { + return; + } + 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", + OBJECT(s->dram_mr), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, + sc->memmap[ASPEED_DEV_SPI0 + i]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, + ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); + } + + /* + * SDMC - SDRAM Memory Controller + * The SDMC controller is unlocked at SPL stage. + * At present, only supports to emulate booting + * start from u-boot stage. Set SDMC controller + * unlocked by default. It is a temporarily solution. + */ + object_property_set_bool(OBJECT(&s->sdmc), "unlocked", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sdmc), 0, + sc->memmap[ASPEED_DEV_SDMC]); + + /* RAM */ + if (!aspeed_soc_ast2700_dram_init(dev, errp)) { + return; + } + + /* Net */ + for (i = 0; i < sc->macs_num; i++) { + object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, + &error_abort); + object_property_set_bool(OBJECT(&s->ftgmac100[i]), "dma64", true, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { + return; + } + 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)); + + object_property_set_link(OBJECT(&s->mii[i]), "nic", + OBJECT(&s->ftgmac100[i]), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->mii[i]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->mii[i]), 0, + sc->memmap[ASPEED_DEV_MII1 + i]); + } + + /* 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; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, wdt_offset); + } + + /* SLI */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sli), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sli), 0, sc->memmap[ASPEED_DEV_SLI]); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sliio), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->sliio), 0, + sc->memmap[ASPEED_DEV_SLIIO]); + + /* ADC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { + return; + } + 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)); + + /* I2C */ + object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), + &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++) { + /* + * The AST2700 I2C controller has one source INTC per bus. + * I2C buses interrupt are connected to GICINT130_INTC + * from bit 0 to bit 15. + * I2C bus 0 is connected to GICINT130_INTC at bit 0. + * I2C bus 15 is connected to GICINT130_INTC at bit 15. + */ + irq = aspeed_soc_ast2700_get_irq_index(s, ASPEED_DEV_I2C, i); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c.busses[i]), 0, irq); + } + + /* GPIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + 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)); + + /* RTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { + return; + } + 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)); + + create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); + create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); + create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); + create_unimplemented_device("ast2700.ltpi", 0x30000000, 0x1000000); + create_unimplemented_device("ast2700.io", 0x0, 0x4000000); +} + +static void aspeed_soc_ast2700_class_init(ObjectClass *oc, void *data) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-a35"), + NULL + }; + DeviceClass *dc = DEVICE_CLASS(oc); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; + dc->realize = aspeed_soc_ast2700_realize; + + sc->name = "ast2700-a0"; + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A0_SILICON_REV; + sc->sram_size = 0x20000; + sc->spis_num = 3; + sc->wdts_num = 8; + sc->macs_num = 1; + sc->uarts_num = 13; + sc->num_cpus = 4; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast2700_irqmap; + sc->memmap = aspeed_soc_ast2700_memmap; + sc->get_irq = aspeed_soc_ast2700_get_irq; +} + +static const TypeInfo aspeed_soc_ast27x0_types[] = { + { + .name = TYPE_ASPEED27X0_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed27x0SoCState), + .abstract = true, + }, { + .name = "ast2700-a0", + .parent = TYPE_ASPEED27X0_SOC, + .instance_init = aspeed_soc_ast2700_init, + .class_init = aspeed_soc_ast2700_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0_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.c b/hw/arm/aspeed_soc.c deleted file mode 100644 index b05b9dd416..0000000000 --- a/hw/arm/aspeed_soc.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * 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 "qemu/units.h" -#include "qapi/error.h" -#include "hw/misc/unimp.h" -#include "hw/arm/aspeed_soc.h" -#include "hw/char/serial.h" -#include "qemu/module.h" -#include "qemu/error-report.h" -#include "hw/i2c/aspeed_i2c.h" -#include "net/net.h" -#include "sysemu/sysemu.h" - -#define ASPEED_SOC_IOMEM_SIZE 0x00200000 - -static const hwaddr aspeed_soc_ast2400_memmap[] = { - [ASPEED_DEV_IOMEM] = 0x1E600000, - [ASPEED_DEV_FMC] = 0x1E620000, - [ASPEED_DEV_SPI1] = 0x1E630000, - [ASPEED_DEV_EHCI1] = 0x1E6A1000, - [ASPEED_DEV_VIC] = 0x1E6C0000, - [ASPEED_DEV_SDMC] = 0x1E6E0000, - [ASPEED_DEV_SCU] = 0x1E6E2000, - [ASPEED_DEV_HACE] = 0x1E6E3000, - [ASPEED_DEV_XDMA] = 0x1E6E7000, - [ASPEED_DEV_VIDEO] = 0x1E700000, - [ASPEED_DEV_ADC] = 0x1E6E9000, - [ASPEED_DEV_SRAM] = 0x1E720000, - [ASPEED_DEV_SDHCI] = 0x1E740000, - [ASPEED_DEV_GPIO] = 0x1E780000, - [ASPEED_DEV_RTC] = 0x1E781000, - [ASPEED_DEV_TIMER1] = 0x1E782000, - [ASPEED_DEV_WDT] = 0x1E785000, - [ASPEED_DEV_PWM] = 0x1E786000, - [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, - [ASPEED_DEV_UART2] = 0x1E78D000, - [ASPEED_DEV_UART3] = 0x1E78E000, - [ASPEED_DEV_UART4] = 0x1E78F000, - [ASPEED_DEV_UART5] = 0x1E784000, - [ASPEED_DEV_VUART] = 0x1E787000, - [ASPEED_DEV_SDRAM] = 0x40000000, -}; - -static const hwaddr aspeed_soc_ast2500_memmap[] = { - [ASPEED_DEV_IOMEM] = 0x1E600000, - [ASPEED_DEV_FMC] = 0x1E620000, - [ASPEED_DEV_SPI1] = 0x1E630000, - [ASPEED_DEV_SPI2] = 0x1E631000, - [ASPEED_DEV_EHCI1] = 0x1E6A1000, - [ASPEED_DEV_EHCI2] = 0x1E6A3000, - [ASPEED_DEV_VIC] = 0x1E6C0000, - [ASPEED_DEV_SDMC] = 0x1E6E0000, - [ASPEED_DEV_SCU] = 0x1E6E2000, - [ASPEED_DEV_HACE] = 0x1E6E3000, - [ASPEED_DEV_XDMA] = 0x1E6E7000, - [ASPEED_DEV_ADC] = 0x1E6E9000, - [ASPEED_DEV_VIDEO] = 0x1E700000, - [ASPEED_DEV_SRAM] = 0x1E720000, - [ASPEED_DEV_SDHCI] = 0x1E740000, - [ASPEED_DEV_GPIO] = 0x1E780000, - [ASPEED_DEV_RTC] = 0x1E781000, - [ASPEED_DEV_TIMER1] = 0x1E782000, - [ASPEED_DEV_WDT] = 0x1E785000, - [ASPEED_DEV_PWM] = 0x1E786000, - [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, - [ASPEED_DEV_UART2] = 0x1E78D000, - [ASPEED_DEV_UART3] = 0x1E78E000, - [ASPEED_DEV_UART4] = 0x1E78F000, - [ASPEED_DEV_UART5] = 0x1E784000, - [ASPEED_DEV_VUART] = 0x1E787000, - [ASPEED_DEV_SDRAM] = 0x80000000, -}; - -static const int aspeed_soc_ast2400_irqmap[] = { - [ASPEED_DEV_UART1] = 9, - [ASPEED_DEV_UART2] = 32, - [ASPEED_DEV_UART3] = 33, - [ASPEED_DEV_UART4] = 34, - [ASPEED_DEV_UART5] = 10, - [ASPEED_DEV_VUART] = 8, - [ASPEED_DEV_FMC] = 19, - [ASPEED_DEV_EHCI1] = 5, - [ASPEED_DEV_EHCI2] = 13, - [ASPEED_DEV_SDMC] = 0, - [ASPEED_DEV_SCU] = 21, - [ASPEED_DEV_ADC] = 31, - [ASPEED_DEV_GPIO] = 20, - [ASPEED_DEV_RTC] = 22, - [ASPEED_DEV_TIMER1] = 16, - [ASPEED_DEV_TIMER2] = 17, - [ASPEED_DEV_TIMER3] = 18, - [ASPEED_DEV_TIMER4] = 35, - [ASPEED_DEV_TIMER5] = 36, - [ASPEED_DEV_TIMER6] = 37, - [ASPEED_DEV_TIMER7] = 38, - [ASPEED_DEV_TIMER8] = 39, - [ASPEED_DEV_WDT] = 27, - [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, - [ASPEED_DEV_SDHCI] = 26, - [ASPEED_DEV_HACE] = 4, -}; - -#define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap - -static qemu_irq aspeed_soc_ast2400_get_irq(AspeedSoCState *s, int dev) -{ - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - - return qdev_get_gpio_in(DEVICE(&s->vic), sc->irqmap[dev]); -} - -static void aspeed_soc_init(Object *obj) -{ - AspeedSoCState *s = ASPEED_SOC(obj); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - int i; - char socname[8]; - char typename[64]; - - if (sscanf(sc->name, "%7s", socname) != 1) { - g_assert_not_reached(); - } - - for (i = 0; i < sc->num_cpus; i++) { - object_initialize_child(obj, "cpu[*]", &s->cpu[i], sc->cpu_type); - } - - snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname); - object_initialize_child(obj, "scu", &s->scu, typename); - qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", - sc->silicon_rev); - object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), - "hw-strap1"); - object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu), - "hw-strap2"); - 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, "rtc", &s->rtc, TYPE_ASPEED_RTC); - - snprintf(typename, sizeof(typename), "aspeed.timer-%s", socname); - object_initialize_child(obj, "timerctrl", &s->timerctrl, typename); - - snprintf(typename, sizeof(typename), "aspeed.adc-%s", socname); - object_initialize_child(obj, "adc", &s->adc, typename); - - snprintf(typename, sizeof(typename), "aspeed.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); - - for (i = 0; i < sc->spis_num; i++) { - snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname); - object_initialize_child(obj, "spi[*]", &s->spi[i], typename); - } - - for (i = 0; i < sc->ehcis_num; i++) { - object_initialize_child(obj, "ehci[*]", &s->ehci[i], - TYPE_PLATFORM_EHCI); - } - - snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); - object_initialize_child(obj, "sdmc", &s->sdmc, typename); - object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), - "ram-size"); - - for (i = 0; i < sc->wdts_num; i++) { - snprintf(typename, sizeof(typename), "aspeed.wdt-%s", socname); - object_initialize_child(obj, "wdt[*]", &s->wdt[i], typename); - } - - for (i = 0; i < sc->macs_num; i++) { - object_initialize_child(obj, "ftgmac100[*]", &s->ftgmac100[i], - 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); - - snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname); - object_initialize_child(obj, "gpio", &s->gpio, typename); - - object_initialize_child(obj, "sdc", &s->sdhci, TYPE_ASPEED_SDHCI); - - object_property_set_int(OBJECT(&s->sdhci), "num-slots", 2, &error_abort); - - /* Init sd card slot class here so that they're under the correct parent */ - for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) { - object_initialize_child(obj, "sdhci[*]", &s->sdhci.slots[i], - TYPE_SYSBUS_SDHCI); - } - - object_initialize_child(obj, "lpc", &s->lpc, TYPE_ASPEED_LPC); - - 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) -{ - int i; - AspeedSoCState *s = ASPEED_SOC(dev); - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - Error *err = NULL; - g_autofree char *sram_name = NULL; - - /* IO space */ - aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), "aspeed.io", - sc->memmap[ASPEED_DEV_IOMEM], - ASPEED_SOC_IOMEM_SIZE); - - /* Video engine stub */ - 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++) { - object_property_set_link(OBJECT(&s->cpu[i]), "memory", - OBJECT(s->memory), &error_abort); - if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { - return; - } - } - - /* SRAM */ - sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&s->cpu[0])->cpu_index); - memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, &err); - if (err) { - error_propagate(errp, err); - return; - } - memory_region_add_subregion(s->memory, - sc->memmap[ASPEED_DEV_SRAM], &s->sram); - - /* SCU */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { - return; - } - 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)) { - return; - } - aspeed_mmio_map(s, 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)); - - /* RTC */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->rtc), errp)) { - return; - } - 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)); - - /* Timer */ - object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), - &error_abort); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { - return; - } - 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); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); - } - - /* ADC */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->adc), errp)) { - return; - } - 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 */ - if (!aspeed_soc_uart_realize(s, errp)) { - return; - } - - /* I2C */ - object_property_set_link(OBJECT(&s->i2c), "dram", OBJECT(s->dram_mr), - &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]); - 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; - } - 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)); - - /* SPI */ - for (i = 0; i < sc->spis_num; i++) { - if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { - return; - } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 0, - sc->memmap[ASPEED_DEV_SPI1 + i]); - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->spi[i]), 1, - ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); - } - - /* EHCI */ - for (i = 0; i < sc->ehcis_num; i++) { - if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { - return; - } - 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)); - } - - /* SDMC - SDRAM Memory Controller */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdmc), errp)) { - return; - } - 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]); - - 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; - } - aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->wdt[i]), 0, - sc->memmap[ASPEED_DEV_WDT] + i * awc->offset); - } - - /* RAM */ - if (!aspeed_soc_dram_init(s, errp)) { - return; - } - - /* Net */ - for (i = 0; i < sc->macs_num; i++) { - object_property_set_bool(OBJECT(&s->ftgmac100[i]), "aspeed", true, - &error_abort); - if (!sysbus_realize(SYS_BUS_DEVICE(&s->ftgmac100[i]), errp)) { - return; - } - 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)); - } - - /* XDMA */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->xdma), errp)) { - return; - } - 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)); - - /* GPIO */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { - return; - } - 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)); - - /* SDHCI */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) { - return; - } - 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)); - - /* LPC */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->lpc), errp)) { - return; - } - 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, - aspeed_soc_get_irq(s, ASPEED_DEV_LPC)); - - /* - * On the AST2400 and AST2500 the one LPC IRQ is shared between all of the - * subdevices. Connect the LPC subdevice IRQs to the LPC controller IRQ (by - * contrast, on the AST2600, the subdevice IRQs are connected straight to - * the GIC). - * - * LPC subdevice IRQ sources are offset from 1 because the shared IRQ output - * to the VIC is at offset 0. - */ - sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_1, - qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_1)); - - sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_2, - qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_2)); - - sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_3, - qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_3)); - - sysbus_connect_irq(SYS_BUS_DEVICE(&s->lpc), 1 + aspeed_lpc_kcs_4, - qdev_get_gpio_in(DEVICE(&s->lpc), aspeed_lpc_kcs_4)); - - /* HACE */ - object_property_set_link(OBJECT(&s->hace), "dram", OBJECT(s->dram_mr), - &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)); -} -static Property aspeed_soc_properties[] = { - DEFINE_PROP_LINK("memory", AspeedSoCState, memory, TYPE_MEMORY_REGION, - MemoryRegion *), - DEFINE_PROP_LINK("dram", AspeedSoCState, dram_mr, 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; - /* 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) -{ - AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); - - sc->name = "ast2400-a1"; - sc->cpu_type = ARM_CPU_TYPE_NAME("arm926"); - sc->silicon_rev = AST2400_A1_SILICON_REV; - sc->sram_size = 0x8000; - sc->spis_num = 1; - sc->ehcis_num = 1; - sc->wdts_num = 2; - sc->macs_num = 2; - sc->uarts_num = 5; - 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) -{ - AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); - - sc->name = "ast2500-a1"; - sc->cpu_type = ARM_CPU_TYPE_NAME("arm1176"); - sc->silicon_rev = AST2500_A1_SILICON_REV; - sc->sram_size = 0x9000; - sc->spis_num = 2; - sc->ehcis_num = 2; - sc->wdts_num = 3; - sc->macs_num = 2; - sc->uarts_num = 5; - 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); -}; - -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); -} - -bool aspeed_soc_uart_realize(AspeedSoCState *s, Error **errp) -{ - AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); - SerialMM *smm; - - for (int i = 0, uart = ASPEED_DEV_UART1; 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 i = dev - ASPEED_DEV_UART1; - - 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); -} diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c new file mode 100644 index 0000000000..a5ff33c46d --- /dev/null +++ b/hw/arm/aspeed_soc_common.c @@ -0,0 +1,171 @@ +/* + * 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-mm.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 bool aspeed_soc_boot_from_emmc(AspeedSoCState *s) +{ + return false; +} + +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); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); + + dc->realize = aspeed_soc_realize; + device_class_set_props(dc, aspeed_soc_properties); + sc->boot_from_emmc = aspeed_soc_boot_from_emmc; +} + +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..5002a40f06 --- /dev/null +++ b/hw/arm/b-l475e-iot01a.c @@ -0,0 +1,137 @@ +/* + * B-L475E-IOT01A Discovery Kit machine + * (B-L475E-IOT01A IoT Node) + * + * Copyright (c) 2023-2024 Arnaud Minier + * Copyright (c) 2023-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. + * + * 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/boot.h" +#include "hw/core/split-irq.h" +#include "hw/arm/stm32l4x5_soc.h" +#include "hw/gpio/stm32l4x5_gpio.h" +#include "hw/display/dm163.h" + +/* B-L475E-IOT01A implementation is inspired from netduinoplus2 and arduino */ + +/* + * There are actually 14 input pins in the DM163 device. + * Here the DM163 input pin EN isn't connected to the STM32L4x5 + * GPIOs as the IM120417002 colors shield doesn't actually use + * this pin to drive the RGB matrix. + */ +#define NUM_DM163_INPUTS 13 + +static const unsigned dm163_input[NUM_DM163_INPUTS] = { + 1 * GPIO_NUM_PINS + 2, /* ROW0 PB2 */ + 0 * GPIO_NUM_PINS + 15, /* ROW1 PA15 */ + 0 * GPIO_NUM_PINS + 2, /* ROW2 PA2 */ + 0 * GPIO_NUM_PINS + 7, /* ROW3 PA7 */ + 0 * GPIO_NUM_PINS + 6, /* ROW4 PA6 */ + 0 * GPIO_NUM_PINS + 5, /* ROW5 PA5 */ + 1 * GPIO_NUM_PINS + 0, /* ROW6 PB0 */ + 0 * GPIO_NUM_PINS + 3, /* ROW7 PA3 */ + 0 * GPIO_NUM_PINS + 4, /* SIN (SDA) PA4 */ + 1 * GPIO_NUM_PINS + 1, /* DCK (SCK) PB1 */ + 2 * GPIO_NUM_PINS + 3, /* RST_B (RST) PC3 */ + 2 * GPIO_NUM_PINS + 4, /* LAT_B (LAT) PC4 */ + 2 * GPIO_NUM_PINS + 5, /* SELBK (SB) PC5 */ +}; + +#define TYPE_B_L475E_IOT01A MACHINE_TYPE_NAME("b-l475e-iot01a") +OBJECT_DECLARE_SIMPLE_TYPE(Bl475eMachineState, B_L475E_IOT01A) + +typedef struct Bl475eMachineState { + MachineState parent_obj; + + Stm32l4x5SocState soc; + SplitIRQ gpio_splitters[NUM_DM163_INPUTS]; + DM163State dm163; +} Bl475eMachineState; + +static void bl475e_init(MachineState *machine) +{ + Bl475eMachineState *s = B_L475E_IOT01A(machine); + const Stm32l4x5SocClass *sc; + DeviceState *dev, *gpio_out_splitter; + unsigned gpio, pin; + + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_STM32L4X5XG_SOC); + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); + + sc = STM32L4X5_SOC_GET_CLASS(&s->soc); + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0, + sc->flash_size); + + if (object_class_by_name(TYPE_DM163)) { + object_initialize_child(OBJECT(machine), "dm163", + &s->dm163, TYPE_DM163); + dev = DEVICE(&s->dm163); + qdev_realize(dev, NULL, &error_abort); + + for (unsigned i = 0; i < NUM_DM163_INPUTS; i++) { + object_initialize_child(OBJECT(machine), "gpio-out-splitters[*]", + &s->gpio_splitters[i], TYPE_SPLIT_IRQ); + gpio_out_splitter = DEVICE(&s->gpio_splitters[i]); + qdev_prop_set_uint32(gpio_out_splitter, "num-lines", 2); + qdev_realize(gpio_out_splitter, NULL, &error_fatal); + + qdev_connect_gpio_out(gpio_out_splitter, 0, + qdev_get_gpio_in(DEVICE(&s->soc), dm163_input[i])); + qdev_connect_gpio_out(gpio_out_splitter, 1, + qdev_get_gpio_in(dev, i)); + gpio = dm163_input[i] / GPIO_NUM_PINS; + pin = dm163_input[i] % GPIO_NUM_PINS; + qdev_connect_gpio_out(DEVICE(&s->soc.gpio[gpio]), pin, + qdev_get_gpio_in(DEVICE(gpio_out_splitter), 0)); + } + } +} + +static void bl475e_machine_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + 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 = bl475e_init; + mc->valid_cpu_types = machine_valid_cpu_types; + + /* SRAM pre-allocated as part of the SoC instantiation */ + mc->default_ram_size = 0; +} + +static const TypeInfo bl475e_machine_type[] = { + { + .name = TYPE_B_L475E_IOT01A, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Bl475eMachineState), + .class_init = bl475e_machine_init, + } +}; + +DEFINE_TYPES(bl475e_machine_type) 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 3c2a4160cd..ac153a96b9 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -30,9 +30,12 @@ #define SEPARATE_DMA_IRQ_MAX 10 #define ORGATED_DMA_IRQ_COUNT 4 -static void create_unimp(BCM2835PeripheralState *ps, - UnimplementedDeviceState *uds, - const char *name, hwaddr ofs, hwaddr size) +/* 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); @@ -45,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) */ @@ -81,23 +111,29 @@ 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)); + /* OTP */ + object_initialize_child(obj, "bcm2835-otp", &s->otp, + TYPE_BCM2835_OTP); + /* Property channel */ object_initialize_child(obj, "property", &s->property, 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); + object_property_add_const_link(OBJECT(&s->property), "otp", + OBJECT(&s->otp)); /* Extended Mass Media Controller */ object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI); @@ -108,25 +144,9 @@ static void bcm2835_peripherals_init(Object *obj) /* DMA Channels */ object_initialize_child(obj, "dma", &s->dma, TYPE_BCM2835_DMA); - object_initialize_child(obj, "orgated-dma-irq", - &s->orgated_dma_irq, TYPE_OR_IRQ); - object_property_set_int(OBJECT(&s->orgated_dma_irq), "num-lines", - ORGATED_DMA_IRQ_COUNT, &error_abort); - 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); @@ -142,15 +162,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); @@ -254,11 +356,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; } @@ -268,6 +380,14 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0, qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB)); + /* OTP */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->otp), errp)) { + return; + } + + memory_region_add_subregion(&s->peri_mr, OTP_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->otp), 0)); + /* Property channel */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->property), errp)) { return; @@ -279,14 +399,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: @@ -309,9 +421,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)) { @@ -334,49 +443,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 <= SEPARATE_DMA_IRQ_MAX; 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)); - } - if (!qdev_realize(DEVICE(&s->orgated_dma_irq), NULL, errp)) { - return; - } - for (n = 0; n < ORGATED_DMA_IRQ_COUNT; n++) { - sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), - SEPARATE_DMA_IRQ_MAX + 1 + n, - qdev_get_gpio_in(DEVICE(&s->orgated_dma_irq), n)); - } - qdev_connect_gpio_out(DEVICE(&s->orgated_dma_irq), 0, - qdev_get_gpio_in_named(DEVICE(&s->ic), - BCM2835_IC_GPU_IRQ, - INTERRUPT_DMA0 + SEPARATE_DMA_IRQ_MAX + 1)); - - /* 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)); @@ -400,16 +471,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); create_unimp(s, &s->v3d, "bcm2835-v3d", V3D_OFFSET, 0x1000); @@ -419,21 +523,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..40a379bc36 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -15,31 +15,16 @@ #include "hw/arm/bcm2836.h" #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" - -typedef struct BCM283XClass { - /*< 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; -} BCM283XClass; - -#define BCM283X_CLASS(klass) \ - OBJECT_CLASS_CHECK(BCM283XClass, (klass), TYPE_BCM283X) -#define BCM283X_GET_CLASS(obj) \ - OBJECT_GET_CLASS(BCM283XClass, (obj), TYPE_BCM283X) +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" 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 +40,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 +174,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 +185,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 +199,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 +227,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 8ff315f431..5301d8d318 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" @@ -346,13 +347,13 @@ static void set_kernel_args_old(const struct arm_boot_info *info, WRITE_WORD(p, info->ram_size / 4096); /* ramdisk_size */ WRITE_WORD(p, 0); -#define FLAG_READONLY 1 -#define FLAG_RDLOAD 4 -#define FLAG_RDPROMPT 8 +#define FLAG_READONLY 1 +#define FLAG_RDLOAD 4 +#define FLAG_RDPROMPT 8 /* flags */ WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); /* rootdev */ - WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ + WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ /* video_num_cols */ WRITE_WORD(p, 0); /* video_num_rows */ @@ -637,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; @@ -719,80 +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; - 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; - } - /* 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); @@ -809,7 +767,10 @@ static void do_cpu_reset(void *opaque) info->secondary_cpu_reset_hook(cpu, info); } } - arm_rebuild_hflags(env); + + if (tcg_enabled()) { + arm_rebuild_hflags(env); + } } } @@ -838,14 +799,18 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } elf_header; int data_swab = 0; bool big_endian; - ssize_t ret = -1; + ssize_t ret; Error *err = NULL; load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err); if (err) { + /* + * If the file is not an ELF file we silently return. + * The caller will fall back to try other formats. + */ error_free(err); - return ret; + return -1; } if (elf_is64) { @@ -878,6 +843,8 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, 1, data_swab, as); if (ret <= 0) { /* The header loaded but the image didn't */ + error_report("Couldn't load elf '%s': %s", + info->kernel_filename, load_elf_strerror(ret)); exit(1); } @@ -904,6 +871,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 */ 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 index 90c04bbc33..c9964bd283 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -27,8 +27,8 @@ struct Fby35State { MemoryRegion bic_memory; Clock *bic_sysclk; - AspeedSoCState bmc; - AspeedSoCState bic; + Aspeed2600SoCState bmc; + Aspeed10x0SoCState bic; bool mmio_exec; }; @@ -70,9 +70,10 @@ static void fby35_bmc_write_boot_rom(DriveInfo *dinfo, MemoryRegion *mr, static void fby35_bmc_init(Fby35State *s) { - DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); + 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); @@ -89,40 +90,39 @@ static void fby35_bmc_init(Fby35State *s) &error_abort); object_property_set_int(OBJECT(&s->bmc), "hw-strap2", 0x00000003, &error_abort); - aspeed_soc_uart_set_chr(&s->bmc, ASPEED_DEV_UART5, serial_hd(0)); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(0)); qdev_realize(DEVICE(&s->bmc), NULL, &error_abort); - aspeed_board_init_flashes(&s->bmc.fmc, "n25q00", 2, 0); + aspeed_board_init_flashes(&soc->fmc, "n25q00", 2, 0); /* Install first FMC flash content as a boot rom. */ - if (drive0) { - AspeedSMCFlash *fl = &s->bmc.fmc.flashes[0]; - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); - uint64_t size = memory_region_size(&fl->mmio); + if (!s->mmio_exec) { + DriveInfo *mtd0 = drive_get(IF_MTD, 0, 0); - if (s->mmio_exec) { - memory_region_init_alias(boot_rom, NULL, "aspeed.boot_rom", - &fl->mmio, 0, size); - memory_region_add_subregion(&s->bmc_memory, FBY35_BMC_FIRMWARE_ADDR, - boot_rom); - } else { + if (mtd0) { + uint64_t rom_size = memory_region_size(&soc->spi_boot); - memory_region_init_rom(boot_rom, NULL, "aspeed.boot_rom", - size, &error_abort); - memory_region_add_subregion(&s->bmc_memory, FBY35_BMC_FIRMWARE_ADDR, - boot_rom); - fby35_bmc_write_boot_rom(drive0, boot_rom, FBY35_BMC_FIRMWARE_ADDR, - size, &error_abort); + 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); @@ -130,12 +130,12 @@ static void fby35_bic_init(Fby35State *s) 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(&s->bic, ASPEED_DEV_UART5, serial_hd(1)); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART5, serial_hd(1)); qdev_realize(DEVICE(&s->bic), NULL, &error_abort); - aspeed_board_init_flashes(&s->bic.fmc, "sst25vf032b", 2, 2); - aspeed_board_init_flashes(&s->bic.spi[0], "sst25vf032b", 2, 4); - aspeed_board_init_flashes(&s->bic.spi[1], "sst25vf032b", 2, 6); + 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) 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 deleted file mode 100644 index 3a4bc332c4..0000000000 --- a/hw/arm/gumstix.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Gumstix Platforms - * - * Copyright (c) 2007 by Thorsten Zitterell - * - * Code based on spitz platform by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * 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: - * - * connex: - * ======= - * create image: - * # dd of=flash bs=1k count=16k if=/dev/zero - * # dd of=flash bs=1k conv=notrunc if=u-boot.bin - * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2 - * start it: - * # qemu-system-arm -M connex -pflash flash -monitor null -nographic - * - * verdex: - * ======= - * create image: - * # dd of=flash bs=1k count=32k if=/dev/zero - * # dd of=flash bs=1k conv=notrunc if=u-boot.bin - * # dd of=flash bs=1k conv=notrunc seek=256 if=rootfs.arm_nofpu.jffs2 - * # dd of=flash bs=1k conv=notrunc seek=31744 if=uImage - * start it: - * # qemu-system-arm -M verdex -pflash flash -monitor null -nographic -m 289 - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "hw/arm/pxa.h" -#include "net/net.h" -#include "hw/block/flash.h" -#include "hw/net/smc91c111.h" -#include "hw/boards.h" -#include "exec/address-spaces.h" -#include "sysemu/qtest.h" -#include "cpu.h" - -static const int sector_len = 128 * 1024; - -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); - - dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo && !qtest_enabled()) { - error_report("A flash image must be given with the " - "'pflash' parameter"); - 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); - } - - /* Interrupt line of NIC is connected to GPIO line 36 */ - smc91c111_init(&nd_table[0], 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); - - dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo && !qtest_enabled()) { - error_report("A flash image must be given with the " - "'pflash' parameter"); - 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); - } - - /* Interrupt line of NIC is connected to GPIO line 99 */ - smc91c111_init(&nd_table[0], 0x04000300, - qdev_get_gpio_in(cpu->gpio, 99)); -} - -static void connex_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Gumstix Connex (PXA255)"; - mc->init = connex_init; - mc->ignore_memory_transaction_failures = true; -} - -static const TypeInfo connex_type = { - .name = MACHINE_TYPE_NAME("connex"), - .parent = TYPE_MACHINE, - .class_init = connex_class_init, -}; - -static void verdex_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Gumstix Verdex (PXA270)"; - mc->init = verdex_init; - mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); -} - -static const TypeInfo verdex_type = { - .name = MACHINE_TYPE_NAME("verdex"), - .parent = TYPE_MACHINE, - .class_init = verdex_class_init, -}; - -static void gumstix_machine_init(void) -{ - type_register_static(&connex_type); - type_register_static(&verdex_type); -} - -type_init(gumstix_machine_init) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index f12aacea6b..f103921d49 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(), }, @@ -144,7 +145,7 @@ static void highbank_regs_class_init(ObjectClass *klass, void *data) dc->desc = "Calxeda Highbank registers"; dc->vmsd = &vmstate_highbank_regs; - dc->reset = highbank_regs_reset; + device_class_set_legacy_reset(dc, highbank_regs_reset); } static const TypeInfo highbank_regs_info = { @@ -198,7 +199,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) machine->cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); break; default: - assert(0); + g_assert_not_reached(); } for (n = 0; n < smp_cpus; n++) { @@ -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..fbd140e383 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -16,12 +16,13 @@ #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" #include "net/net.h" #include "hw/net/lan9118.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "sysemu/qtest.h" #include "sysemu/sysemu.h" #include "qemu/cutils.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 deleted file mode 100644 index 8454b65458..0000000000 --- a/hw/arm/mainstone.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * PXA270-based Intel Mainstone platforms. - * - * Copyright (c) 2007 by Armin Kuster or - * - * - * Code based on spitz platform by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * 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/error-report.h" -#include "qapi/error.h" -#include "hw/arm/pxa.h" -#include "hw/arm/boot.h" -#include "net/net.h" -#include "hw/net/smc91c111.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "hw/sysbus.h" -#include "exec/address-spaces.h" -#include "cpu.h" - -/* Device addresses */ -#define MST_FPGA_PHYS 0x08000000 -#define MST_ETH_PHYS 0x10000300 -#define MST_FLASH_0 0x00000000 -#define MST_FLASH_1 0x04000000 - -/* IRQ definitions */ -#define MMC_IRQ 0 -#define USIM_IRQ 1 -#define USBC_IRQ 2 -#define ETHERNET_IRQ 3 -#define AC97_IRQ 4 -#define PEN_IRQ 5 -#define MSINS_IRQ 6 -#define EXBRD_IRQ 7 -#define S0_CD_IRQ 9 -#define S0_STSCHG_IRQ 10 -#define S0_IRQ 11 -#define S1_CD_IRQ 13 -#define S1_STSCHG_IRQ 14 -#define S1_IRQ 15 - -static const struct keymap map[0xE0] = { - [0 ... 0xDF] = { -1, -1 }, - [0x1e] = {0,0}, /* a */ - [0x30] = {0,1}, /* b */ - [0x2e] = {0,2}, /* c */ - [0x20] = {0,3}, /* d */ - [0x12] = {0,4}, /* e */ - [0x21] = {0,5}, /* f */ - [0x22] = {1,0}, /* g */ - [0x23] = {1,1}, /* h */ - [0x17] = {1,2}, /* i */ - [0x24] = {1,3}, /* j */ - [0x25] = {1,4}, /* k */ - [0x26] = {1,5}, /* l */ - [0x32] = {2,0}, /* m */ - [0x31] = {2,1}, /* n */ - [0x18] = {2,2}, /* o */ - [0x19] = {2,3}, /* p */ - [0x10] = {2,4}, /* q */ - [0x13] = {2,5}, /* r */ - [0x1f] = {3,0}, /* s */ - [0x14] = {3,1}, /* t */ - [0x16] = {3,2}, /* u */ - [0x2f] = {3,3}, /* v */ - [0x11] = {3,4}, /* w */ - [0x2d] = {3,5}, /* x */ - [0x34] = {4,0}, /* . */ - [0x15] = {4,2}, /* y */ - [0x2c] = {4,3}, /* z */ - [0x35] = {4,4}, /* / */ - [0xc7] = {5,0}, /* Home */ - [0x2a] = {5,1}, /* shift */ - /* - * There are two matrix positions which map to space, - * but QEMU can only use one of them for the reverse - * mapping, so simply use the second one. - */ - /* [0x39] = {5,2}, space */ - [0x39] = {5,3}, /* space */ - /* - * Matrix position {5,4} and other keys are missing here. - * TODO: Compare with Linux code and test real hardware. - */ - [0x1c] = {5,4}, /* enter */ - [0x0e] = {5,5}, /* backspace */ - [0xc8] = {6,0}, /* up */ - [0xd0] = {6,1}, /* down */ - [0xcb] = {6,2}, /* left */ - [0xcd] = {6,3}, /* right */ -}; - -enum mainstone_model_e { mainstone }; - -#define MAINSTONE_RAM 0x04000000 -#define MAINSTONE_ROM 0x00800000 -#define MAINSTONE_FLASH 0x02000000 - -static struct arm_boot_info mainstone_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, -}; - -static void mainstone_common_init(MemoryRegion *address_space_mem, - 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; - DriveInfo *dinfo; - int i; - 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, - &error_fatal); - memory_region_add_subregion(address_space_mem, 0, 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); - } - } - - mst_irq = sysbus_create_simple("mainstone-fpga", MST_FPGA_PHYS, - qdev_get_gpio_in(mpu->gpio, 0)); - - /* setup keypad */ - pxa27x_register_keypad(mpu->kp, map, 0xe0); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(mpu->mmc, NULL, qdev_get_gpio_in(mst_irq, MMC_IRQ)); - - pxa2xx_pcmcia_set_irq_cb(mpu->pcmcia[0], - qdev_get_gpio_in(mst_irq, S0_IRQ), - qdev_get_gpio_in(mst_irq, S0_CD_IRQ)); - pxa2xx_pcmcia_set_irq_cb(mpu->pcmcia[1], - 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)); - - mainstone_binfo.board_id = arm_id; - arm_load_kernel(mpu->cpu, machine, &mainstone_binfo); -} - -static void mainstone_init(MachineState *machine) -{ - mainstone_common_init(get_system_memory(), machine, mainstone, 0x196); -} - -static void mainstone2_machine_init(MachineClass *mc) -{ - mc->desc = "Mainstone II (PXA27x)"; - mc->init = mainstone_init; - mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5"); -} - -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 92f9f6e000..490234b3b8 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -1,66 +1,75 @@ arm_ss = ss.source_set() -arm_ss.add(files('boot.c'), fdt) +arm_ss.add(files('boot.c')) arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) -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')) 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_eeprom.c', 'fby35.c')) +arm_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files('aspeed_ast27x0.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-stubs.c', + 'xen-pvh.c', +)) + +system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.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_NETDUINO2', if_true: files('netduino2.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_STRONGARM', if_true: files('strongarm.c')) +system_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) +system_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) +system_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) hw_arch += {'arm': arm_ss} diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 284c09c91d..8edf57a66d 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, @@ -427,7 +435,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size, const int *irqs, const PPCExtraData *extradata) { - /* The irq[] array is tx, rx, combined, in that order */ + /* The irq[] array is rx, tx, combined, in that order */ MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); CMSDKAPBUART *uart = opaque; int i = uart - &mms->uart[0]; @@ -439,8 +447,8 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", mmc->apb_periph_frq); sysbus_realize(SYS_BUS_DEVICE(uart), &error_fatal); s = SYS_BUS_DEVICE(uart); - sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[0])); - sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[1])); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqs[1])); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqs[0])); 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)); sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqs[2])); @@ -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); @@ -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, ShutdownCause reason) +static void mps2_machine_reset(MachineState *machine, ResetType type) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -1249,17 +1264,24 @@ static void mps2_machine_reset(MachineState *machine, ShutdownCause reason) * reset see the correct mapping. */ remap_memory(mms, mms->remap); - qemu_devices_reset(reason); + qemu_devices_reset(type); } 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 a86a994dba..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,7 +456,7 @@ 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)); @@ -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..c4999ebce3 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -26,7 +26,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" #include "hw/qdev-clock.h" @@ -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 a6df473ec9..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", @@ -103,9 +98,14 @@ static void emcraft_sf2_s2s010_init(MachineState *machine) 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 6eeee57c9d..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"); @@ -604,11 +597,15 @@ static void musca_init(MachineState *machine) 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..33ece06bbd 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -10,21 +10,22 @@ */ #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" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "qemu/timer.h" #include "hw/ptimer.h" #include "hw/qdev-properties.h" #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() @@ -407,7 +411,7 @@ static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = mv88w8618_pic_reset; + device_class_set_legacy_reset(dc, mv88w8618_pic_reset); dc->vmsd = &mv88w8618_pic_vmsd; } @@ -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() @@ -601,7 +605,7 @@ static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = mv88w8618_pit_reset; + device_class_set_legacy_reset(dc, mv88w8618_pit_reset); dc->vmsd = &mv88w8618_pit_vmsd; } @@ -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), @@ -1026,7 +1030,7 @@ static void musicpal_gpio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = musicpal_gpio_reset; + device_class_set_legacy_reset(dc, musicpal_gpio_reset); dc->vmsd = &musicpal_gpio_vmsd; } @@ -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 83753d53a3..8b1a9a2437 100644 --- a/hw/arm/netduino2.c +++ b/hw/arm/netduino2.c @@ -44,7 +44,7 @@ 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); @@ -54,8 +54,14 @@ static void netduino2_init(MachineState *machine) 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 515c081605..bccd100354 100644 --- a/hw/arm/netduinoplus2.c +++ b/hw/arm/netduinoplus2.c @@ -44,7 +44,7 @@ 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); @@ -55,8 +55,14 @@ static void netduinoplus2_init(MachineState *machine) 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..af04c4b7ec 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -18,14 +18,16 @@ #include "hw/arm/boot.h" #include "hw/arm/npcm7xx.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/loader.h" #include "hw/misc/unimp.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "qapi/error.h" +#include "qemu/bswap.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 +85,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 +226,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; @@ -369,7 +387,7 @@ static void npcm7xx_init_fuses(NPCM7xxState *s) * The initial mask of disabled modules indicates the chip derivative (e.g. * NPCM750 or NPCM730). */ - value = tswap32(nc->disabled_modules); + value = cpu_to_le32(nc->disabled_modules); npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE, sizeof(value)); } @@ -444,6 +462,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); } @@ -461,9 +487,6 @@ 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), - &error_abort); object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", NPCM7XX_GIC_CPU_IF_ADDR, &error_abort); object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true, @@ -643,8 +666,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 +676,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 +700,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 +764,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 +784,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 deleted file mode 100644 index b151113c27..0000000000 --- a/hw/arm/nseries.c +++ /dev/null @@ -1,1469 +0,0 @@ -/* - * Nokia N-series internet tablets. - * - * Copyright (C) 2007 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "cpu.h" -#include "chardev/char.h" -#include "qemu/cutils.h" -#include "qemu/bswap.h" -#include "qemu/hw-version.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "hw/arm/omap.h" -#include "hw/arm/boot.h" -#include "hw/irq.h" -#include "ui/console.h" -#include "hw/boards.h" -#include "hw/i2c/i2c.h" -#include "hw/display/blizzard.h" -#include "hw/input/lm832x.h" -#include "hw/input/tsc2xxx.h" -#include "hw/misc/cbus.h" -#include "hw/sensor/tmp105.h" -#include "hw/qdev-properties.h" -#include "hw/block/flash.h" -#include "hw/hw.h" -#include "hw/loader.h" -#include "hw/sysbus.h" -#include "qemu/log.h" - -/* Nokia N8x0 support */ -struct n800_s { - struct omap_mpu_state_s *mpu; - - struct rfbi_chip_s blizzard; - struct { - void *opaque; - uint32_t (*txrx)(void *opaque, uint32_t value, int len); - uWireSlave *chip; - } ts; - - int keymap[0x80]; - DeviceState *kbd; - - DeviceState *usb; - void *retu; - void *tahvo; - DeviceState *nand; -}; - -/* GPIO pins */ -#define N8X0_TUSB_ENABLE_GPIO 0 -#define N800_MMC2_WP_GPIO 8 -#define N800_UNKNOWN_GPIO0 9 /* out */ -#define N810_MMC2_VIOSD_GPIO 9 -#define N810_HEADSET_AMP_GPIO 10 -#define N800_CAM_TURN_GPIO 12 -#define N810_GPS_RESET_GPIO 12 -#define N800_BLIZZARD_POWERDOWN_GPIO 15 -#define N800_MMC1_WP_GPIO 23 -#define N810_MMC2_VSD_GPIO 23 -#define N8X0_ONENAND_GPIO 26 -#define N810_BLIZZARD_RESET_GPIO 30 -#define N800_UNKNOWN_GPIO2 53 /* out */ -#define N8X0_TUSB_INT_GPIO 58 -#define N8X0_BT_WKUP_GPIO 61 -#define N8X0_STI_GPIO 62 -#define N8X0_CBUS_SEL_GPIO 64 -#define N8X0_CBUS_DAT_GPIO 65 -#define N8X0_CBUS_CLK_GPIO 66 -#define N8X0_WLAN_IRQ_GPIO 87 -#define N8X0_BT_RESET_GPIO 92 -#define N8X0_TEA5761_CS_GPIO 93 -#define N800_UNKNOWN_GPIO 94 -#define N810_TSC_RESET_GPIO 94 -#define N800_CAM_ACT_GPIO 95 -#define N810_GPS_WAKEUP_GPIO 95 -#define N8X0_MMC_CS_GPIO 96 -#define N8X0_WLAN_PWR_GPIO 97 -#define N8X0_BT_HOST_WKUP_GPIO 98 -#define N810_SPEAKER_AMP_GPIO 101 -#define N810_KB_LOCK_GPIO 102 -#define N800_TSC_TS_GPIO 103 -#define N810_TSC_TS_GPIO 106 -#define N8X0_HEADPHONE_GPIO 107 -#define N8X0_RETU_GPIO 108 -#define N800_TSC_KP_IRQ_GPIO 109 -#define N810_KEYBOARD_GPIO 109 -#define N800_BAT_COVER_GPIO 110 -#define N810_SLIDE_GPIO 110 -#define N8X0_TAHVO_GPIO 111 -#define N800_UNKNOWN_GPIO4 112 /* out */ -#define N810_SLEEPX_LED_GPIO 112 -#define N800_TSC_RESET_GPIO 118 /* ? */ -#define N810_AIC33_RESET_GPIO 118 -#define N800_TSC_UNKNOWN_GPIO 119 /* out */ -#define N8X0_TMP105_GPIO 125 - -/* Config */ -#define BT_UART 0 -#define XLDR_LL_UART 1 - -/* Addresses on the I2C bus 0 */ -#define N810_TLV320AIC33_ADDR 0x18 /* Audio CODEC */ -#define N8X0_TCM825x_ADDR 0x29 /* Camera */ -#define N810_LP5521_ADDR 0x32 /* LEDs */ -#define N810_TSL2563_ADDR 0x3d /* Light sensor */ -#define N810_LM8323_ADDR 0x45 /* Keyboard */ -/* Addresses on the I2C bus 1 */ -#define N8X0_TMP105_ADDR 0x48 /* Temperature sensor */ -#define N8X0_MENELAUS_ADDR 0x72 /* Power management */ - -/* Chipselects on GPMC NOR interface */ -#define N8X0_ONENAND_CS 0 -#define N8X0_USB_ASYNC_CS 1 -#define N8X0_USB_SYNC_CS 4 - -#define N8X0_BD_ADDR 0x00, 0x1a, 0x89, 0x9e, 0x3e, 0x81 - -static void n800_mmc_cs_cb(void *opaque, int line, int level) -{ - /* TODO: this seems to actually be connected to the menelaus, to - * which also both MMC slots connect. */ - omap_mmc_enable((struct omap_mmc_s *) opaque, !level); -} - -static void n8x0_gpio_setup(struct n800_s *s) -{ - qdev_connect_gpio_out(s->mpu->gpio, N8X0_MMC_CS_GPIO, - qemu_allocate_irq(n800_mmc_cs_cb, s->mpu->mmc, 0)); - qemu_irq_lower(qdev_get_gpio_in(s->mpu->gpio, N800_BAT_COVER_GPIO)); -} - -#define MAEMO_CAL_HEADER(...) \ - 'C', 'o', 'n', 'F', 0x02, 0x00, 0x04, 0x00, \ - __VA_ARGS__, \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - -static const uint8_t n8x0_cal_wlan_mac[] = { - MAEMO_CAL_HEADER('w', 'l', 'a', 'n', '-', 'm', 'a', 'c') - 0x1c, 0x00, 0x00, 0x00, 0x47, 0xd6, 0x69, 0xb3, - 0x30, 0x08, 0xa0, 0x83, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x89, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, -}; - -static const uint8_t n8x0_cal_bt_id[] = { - MAEMO_CAL_HEADER('b', 't', '-', 'i', 'd', 0, 0, 0) - 0x0a, 0x00, 0x00, 0x00, 0xa3, 0x4b, 0xf6, 0x96, - 0xa8, 0xeb, 0xb2, 0x41, 0x00, 0x00, 0x00, 0x00, - N8X0_BD_ADDR, -}; - -static void n8x0_nand_setup(struct n800_s *s) -{ - char *otp_region; - DriveInfo *dinfo; - - s->nand = qdev_new("onenand"); - qdev_prop_set_uint16(s->nand, "manufacturer_id", NAND_MFR_SAMSUNG); - /* Either 0x40 or 0x48 are OK for the device ID */ - qdev_prop_set_uint16(s->nand, "device_id", 0x48); - qdev_prop_set_uint16(s->nand, "version_id", 0); - qdev_prop_set_int32(s->nand, "shift", 1); - dinfo = drive_get(IF_MTD, 0, 0); - if (dinfo) { - qdev_prop_set_drive_err(s->nand, "drive", blk_by_legacy_dinfo(dinfo), - &error_fatal); - } - sysbus_realize_and_unref(SYS_BUS_DEVICE(s->nand), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(s->nand), 0, - qdev_get_gpio_in(s->mpu->gpio, N8X0_ONENAND_GPIO)); - omap_gpmc_attach(s->mpu->gpmc, N8X0_ONENAND_CS, - sysbus_mmio_get_region(SYS_BUS_DEVICE(s->nand), 0)); - otp_region = onenand_raw_otp(s->nand); - - memcpy(otp_region + 0x000, n8x0_cal_wlan_mac, sizeof(n8x0_cal_wlan_mac)); - memcpy(otp_region + 0x800, n8x0_cal_bt_id, sizeof(n8x0_cal_bt_id)); - /* XXX: in theory should also update the OOB for both pages */ -} - -static qemu_irq n8x0_system_powerdown; - -static void n8x0_powerdown_req(Notifier *n, void *opaque) -{ - qemu_irq_raise(n8x0_system_powerdown); -} - -static Notifier n8x0_system_powerdown_notifier = { - .notify = n8x0_powerdown_req -}; - -static void n8x0_i2c_setup(struct n800_s *s) -{ - DeviceState *dev; - qemu_irq tmp_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TMP105_GPIO); - I2CBus *i2c = omap_i2c_bus(s->mpu->i2c[0]); - - /* Attach a menelaus PM chip */ - dev = DEVICE(i2c_slave_create_simple(i2c, "twl92230", N8X0_MENELAUS_ADDR)); - qdev_connect_gpio_out(dev, 3, - qdev_get_gpio_in(s->mpu->ih[0], - OMAP_INT_24XX_SYS_NIRQ)); - - n8x0_system_powerdown = qdev_get_gpio_in(dev, 3); - qemu_register_powerdown_notifier(&n8x0_system_powerdown_notifier); - - /* Attach a TMP105 PM chip (A0 wired to ground) */ - dev = DEVICE(i2c_slave_create_simple(i2c, TYPE_TMP105, N8X0_TMP105_ADDR)); - qdev_connect_gpio_out(dev, 0, tmp_irq); -} - -/* Touchscreen and keypad controller */ -static MouseTransformInfo n800_pointercal = { - .x = 800, - .y = 480, - .a = { 14560, -68, -3455208, -39, -9621, 35152972, 65536 }, -}; - -static MouseTransformInfo n810_pointercal = { - .x = 800, - .y = 480, - .a = { 15041, 148, -4731056, 171, -10238, 35933380, 65536 }, -}; - -#define RETU_KEYCODE 61 /* F3 */ - -static void n800_key_event(void *opaque, int keycode) -{ - struct n800_s *s = (struct n800_s *) opaque; - int code = s->keymap[keycode & 0x7f]; - - if (code == -1) { - if ((keycode & 0x7f) == RETU_KEYCODE) { - retu_key_event(s->retu, !(keycode & 0x80)); - } - return; - } - - tsc210x_key_event(s->ts.chip, code, !(keycode & 0x80)); -} - -static const int n800_keys[16] = { - -1, - 72, /* Up */ - 63, /* Home (F5) */ - -1, - 75, /* Left */ - 28, /* Enter */ - 77, /* Right */ - -1, - 1, /* Cycle (ESC) */ - 80, /* Down */ - 62, /* Menu (F4) */ - -1, - 66, /* Zoom- (F8) */ - 64, /* FullScreen (F6) */ - 65, /* Zoom+ (F7) */ - -1, -}; - -static void n800_tsc_kbd_setup(struct n800_s *s) -{ - int i; - - /* XXX: are the three pins inverted inside the chip between the - * tsc and the cpu (N4111)? */ - qemu_irq penirq = NULL; /* NC */ - qemu_irq kbirq = qdev_get_gpio_in(s->mpu->gpio, N800_TSC_KP_IRQ_GPIO); - qemu_irq dav = qdev_get_gpio_in(s->mpu->gpio, N800_TSC_TS_GPIO); - - s->ts.chip = tsc2301_init(penirq, kbirq, dav); - s->ts.opaque = s->ts.chip->opaque; - s->ts.txrx = tsc210x_txrx; - - for (i = 0; i < 0x80; i++) { - s->keymap[i] = -1; - } - for (i = 0; i < 0x10; i++) { - if (n800_keys[i] >= 0) { - s->keymap[n800_keys[i]] = i; - } - } - - qemu_add_kbd_event_handler(n800_key_event, s); - - tsc210x_set_transform(s->ts.chip, &n800_pointercal); -} - -static void n810_tsc_setup(struct n800_s *s) -{ - qemu_irq pintdav = qdev_get_gpio_in(s->mpu->gpio, N810_TSC_TS_GPIO); - - s->ts.opaque = tsc2005_init(pintdav); - s->ts.txrx = tsc2005_txrx; - - tsc2005_set_transform(s->ts.opaque, &n810_pointercal); -} - -/* N810 Keyboard controller */ -static void n810_key_event(void *opaque, int keycode) -{ - struct n800_s *s = (struct n800_s *) opaque; - int code = s->keymap[keycode & 0x7f]; - - if (code == -1) { - if ((keycode & 0x7f) == RETU_KEYCODE) { - retu_key_event(s->retu, !(keycode & 0x80)); - } - return; - } - - lm832x_key_event(s->kbd, code, !(keycode & 0x80)); -} - -#define M 0 - -static int n810_keys[0x80] = { - [0x01] = 16, /* Q */ - [0x02] = 37, /* K */ - [0x03] = 24, /* O */ - [0x04] = 25, /* P */ - [0x05] = 14, /* Backspace */ - [0x06] = 30, /* A */ - [0x07] = 31, /* S */ - [0x08] = 32, /* D */ - [0x09] = 33, /* F */ - [0x0a] = 34, /* G */ - [0x0b] = 35, /* H */ - [0x0c] = 36, /* J */ - - [0x11] = 17, /* W */ - [0x12] = 62, /* Menu (F4) */ - [0x13] = 38, /* L */ - [0x14] = 40, /* ' (Apostrophe) */ - [0x16] = 44, /* Z */ - [0x17] = 45, /* X */ - [0x18] = 46, /* C */ - [0x19] = 47, /* V */ - [0x1a] = 48, /* B */ - [0x1b] = 49, /* N */ - [0x1c] = 42, /* Shift (Left shift) */ - [0x1f] = 65, /* Zoom+ (F7) */ - - [0x21] = 18, /* E */ - [0x22] = 39, /* ; (Semicolon) */ - [0x23] = 12, /* - (Minus) */ - [0x24] = 13, /* = (Equal) */ - [0x2b] = 56, /* Fn (Left Alt) */ - [0x2c] = 50, /* M */ - [0x2f] = 66, /* Zoom- (F8) */ - - [0x31] = 19, /* R */ - [0x32] = 29 | M, /* Right Ctrl */ - [0x34] = 57, /* Space */ - [0x35] = 51, /* , (Comma) */ - [0x37] = 72 | M, /* Up */ - [0x3c] = 82 | M, /* Compose (Insert) */ - [0x3f] = 64, /* FullScreen (F6) */ - - [0x41] = 20, /* T */ - [0x44] = 52, /* . (Dot) */ - [0x46] = 77 | M, /* Right */ - [0x4f] = 63, /* Home (F5) */ - [0x51] = 21, /* Y */ - [0x53] = 80 | M, /* Down */ - [0x55] = 28, /* Enter */ - [0x5f] = 1, /* Cycle (ESC) */ - - [0x61] = 22, /* U */ - [0x64] = 75 | M, /* Left */ - - [0x71] = 23, /* I */ -#if 0 - [0x75] = 28 | M, /* KP Enter (KP Enter) */ -#else - [0x75] = 15, /* KP Enter (Tab) */ -#endif -}; - -#undef M - -static void n810_kbd_setup(struct n800_s *s) -{ - qemu_irq kbd_irq = qdev_get_gpio_in(s->mpu->gpio, N810_KEYBOARD_GPIO); - int i; - - for (i = 0; i < 0x80; i++) { - s->keymap[i] = -1; - } - for (i = 0; i < 0x80; i++) { - if (n810_keys[i] > 0) { - s->keymap[n810_keys[i]] = i; - } - } - - qemu_add_kbd_event_handler(n810_key_event, s); - - /* Attach the LM8322 keyboard to the I2C bus, - * should happen in n8x0_i2c_setup and s->kbd be initialised here. */ - s->kbd = DEVICE(i2c_slave_create_simple(omap_i2c_bus(s->mpu->i2c[0]), - TYPE_LM8323, N810_LM8323_ADDR)); - qdev_connect_gpio_out(s->kbd, 0, kbd_irq); -} - -/* LCD MIPI DBI-C controller (URAL) */ -struct mipid_s { - int resp[4]; - int param[4]; - int p; - int pm; - int cmd; - - int sleep; - int booster; - int te; - int selfcheck; - int partial; - int normal; - int vscr; - int invert; - int onoff; - int gamma; - uint32_t id; -}; - -static void mipid_reset(struct mipid_s *s) -{ - s->pm = 0; - s->cmd = 0; - - s->sleep = 1; - s->booster = 0; - s->selfcheck = - (1 << 7) | /* Register loading OK. */ - (1 << 5) | /* The chip is attached. */ - (1 << 4); /* Display glass still in one piece. */ - s->te = 0; - s->partial = 0; - s->normal = 1; - s->vscr = 0; - s->invert = 0; - s->onoff = 1; - s->gamma = 0; -} - -static uint32_t mipid_txrx(void *opaque, uint32_t cmd, int len) -{ - struct mipid_s *s = (struct mipid_s *) opaque; - uint8_t ret; - - if (len > 9) { - hw_error("%s: FIXME: bad SPI word width %i\n", __func__, len); - } - - if (s->p >= ARRAY_SIZE(s->resp)) { - ret = 0; - } else { - ret = s->resp[s->p++]; - } - if (s->pm-- > 0) { - s->param[s->pm] = cmd; - } else { - s->cmd = cmd; - } - - switch (s->cmd) { - case 0x00: /* NOP */ - break; - - case 0x01: /* SWRESET */ - mipid_reset(s); - break; - - case 0x02: /* BSTROFF */ - s->booster = 0; - break; - case 0x03: /* BSTRON */ - s->booster = 1; - break; - - case 0x04: /* RDDID */ - s->p = 0; - s->resp[0] = (s->id >> 16) & 0xff; - s->resp[1] = (s->id >> 8) & 0xff; - s->resp[2] = (s->id >> 0) & 0xff; - break; - - case 0x06: /* RD_RED */ - case 0x07: /* RD_GREEN */ - /* XXX the bootloader sometimes issues RD_BLUE meaning RDDID so - * for the bootloader one needs to change this. */ - case 0x08: /* RD_BLUE */ - s->p = 0; - /* TODO: return first pixel components */ - s->resp[0] = 0x01; - break; - - case 0x09: /* RDDST */ - s->p = 0; - s->resp[0] = s->booster << 7; - s->resp[1] = (5 << 4) | (s->partial << 2) | - (s->sleep << 1) | s->normal; - s->resp[2] = (s->vscr << 7) | (s->invert << 5) | - (s->onoff << 2) | (s->te << 1) | (s->gamma >> 2); - s->resp[3] = s->gamma << 6; - break; - - case 0x0a: /* RDDPM */ - s->p = 0; - s->resp[0] = (s->onoff << 2) | (s->normal << 3) | (s->sleep << 4) | - (s->partial << 5) | (s->sleep << 6) | (s->booster << 7); - break; - case 0x0b: /* RDDMADCTR */ - s->p = 0; - s->resp[0] = 0; - break; - case 0x0c: /* RDDCOLMOD */ - s->p = 0; - s->resp[0] = 5; /* 65K colours */ - break; - case 0x0d: /* RDDIM */ - s->p = 0; - s->resp[0] = (s->invert << 5) | (s->vscr << 7) | s->gamma; - break; - case 0x0e: /* RDDSM */ - s->p = 0; - s->resp[0] = s->te << 7; - break; - case 0x0f: /* RDDSDR */ - s->p = 0; - s->resp[0] = s->selfcheck; - break; - - case 0x10: /* SLPIN */ - s->sleep = 1; - break; - case 0x11: /* SLPOUT */ - s->sleep = 0; - s->selfcheck ^= 1 << 6; /* POFF self-diagnosis Ok */ - break; - - case 0x12: /* PTLON */ - s->partial = 1; - s->normal = 0; - s->vscr = 0; - break; - case 0x13: /* NORON */ - s->partial = 0; - s->normal = 1; - s->vscr = 0; - break; - - case 0x20: /* INVOFF */ - s->invert = 0; - break; - case 0x21: /* INVON */ - s->invert = 1; - break; - - case 0x22: /* APOFF */ - case 0x23: /* APON */ - goto bad_cmd; - - case 0x25: /* WRCNTR */ - if (s->pm < 0) { - s->pm = 1; - } - goto bad_cmd; - - case 0x26: /* GAMSET */ - if (!s->pm) { - s->gamma = ctz32(s->param[0] & 0xf); - if (s->gamma == 32) { - s->gamma = -1; /* XXX: should this be 0? */ - } - } else if (s->pm < 0) { - s->pm = 1; - } - break; - - case 0x28: /* DISPOFF */ - s->onoff = 0; - break; - case 0x29: /* DISPON */ - s->onoff = 1; - break; - - case 0x2a: /* CASET */ - case 0x2b: /* RASET */ - case 0x2c: /* RAMWR */ - case 0x2d: /* RGBSET */ - case 0x2e: /* RAMRD */ - case 0x30: /* PTLAR */ - case 0x33: /* SCRLAR */ - goto bad_cmd; - - case 0x34: /* TEOFF */ - s->te = 0; - break; - case 0x35: /* TEON */ - if (!s->pm) { - s->te = 1; - } else if (s->pm < 0) { - s->pm = 1; - } - break; - - case 0x36: /* MADCTR */ - goto bad_cmd; - - case 0x37: /* VSCSAD */ - s->partial = 0; - s->normal = 0; - s->vscr = 1; - break; - - case 0x38: /* IDMOFF */ - case 0x39: /* IDMON */ - case 0x3a: /* COLMOD */ - goto bad_cmd; - - case 0xb0: /* CLKINT / DISCTL */ - case 0xb1: /* CLKEXT */ - if (s->pm < 0) { - s->pm = 2; - } - break; - - case 0xb4: /* FRMSEL */ - break; - - case 0xb5: /* FRM8SEL */ - case 0xb6: /* TMPRNG / INIESC */ - case 0xb7: /* TMPHIS / NOP2 */ - case 0xb8: /* TMPREAD / MADCTL */ - case 0xba: /* DISTCTR */ - case 0xbb: /* EPVOL */ - goto bad_cmd; - - case 0xbd: /* Unknown */ - s->p = 0; - s->resp[0] = 0; - s->resp[1] = 1; - break; - - case 0xc2: /* IFMOD */ - if (s->pm < 0) { - s->pm = 2; - } - break; - - case 0xc6: /* PWRCTL */ - case 0xc7: /* PPWRCTL */ - case 0xd0: /* EPWROUT */ - case 0xd1: /* EPWRIN */ - case 0xd4: /* RDEV */ - case 0xd5: /* RDRR */ - goto bad_cmd; - - case 0xda: /* RDID1 */ - s->p = 0; - s->resp[0] = (s->id >> 16) & 0xff; - break; - case 0xdb: /* RDID2 */ - s->p = 0; - s->resp[0] = (s->id >> 8) & 0xff; - break; - case 0xdc: /* RDID3 */ - s->p = 0; - s->resp[0] = (s->id >> 0) & 0xff; - break; - - default: - bad_cmd: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: unknown command 0x%02x\n", __func__, s->cmd); - break; - } - - return ret; -} - -static void *mipid_init(void) -{ - struct mipid_s *s = g_malloc0(sizeof(*s)); - - s->id = 0x838f03; - mipid_reset(s); - - return s; -} - -static void n8x0_spi_setup(struct n800_s *s) -{ - void *tsc = s->ts.opaque; - void *mipid = mipid_init(); - - omap_mcspi_attach(s->mpu->mcspi[0], s->ts.txrx, tsc, 0); - omap_mcspi_attach(s->mpu->mcspi[0], mipid_txrx, mipid, 1); -} - -/* This task is normally performed by the bootloader. If we're loading - * a kernel directly, we need to enable the Blizzard ourselves. */ -static void n800_dss_init(struct rfbi_chip_s *chip) -{ - uint8_t *fb_blank; - - chip->write(chip->opaque, 0, 0x2a); /* LCD Width register */ - chip->write(chip->opaque, 1, 0x64); - chip->write(chip->opaque, 0, 0x2c); /* LCD HNDP register */ - chip->write(chip->opaque, 1, 0x1e); - chip->write(chip->opaque, 0, 0x2e); /* LCD Height 0 register */ - chip->write(chip->opaque, 1, 0xe0); - chip->write(chip->opaque, 0, 0x30); /* LCD Height 1 register */ - chip->write(chip->opaque, 1, 0x01); - chip->write(chip->opaque, 0, 0x32); /* LCD VNDP register */ - chip->write(chip->opaque, 1, 0x06); - chip->write(chip->opaque, 0, 0x68); /* Display Mode register */ - chip->write(chip->opaque, 1, 1); /* Enable bit */ - - chip->write(chip->opaque, 0, 0x6c); - chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Input X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Input Y Start Position */ - chip->write(chip->opaque, 1, 0x1f); /* Input X End Position */ - chip->write(chip->opaque, 1, 0x03); /* Input X End Position */ - chip->write(chip->opaque, 1, 0xdf); /* Input Y End Position */ - chip->write(chip->opaque, 1, 0x01); /* Input Y End Position */ - chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Output X Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */ - chip->write(chip->opaque, 1, 0x00); /* Output Y Start Position */ - chip->write(chip->opaque, 1, 0x1f); /* Output X End Position */ - chip->write(chip->opaque, 1, 0x03); /* Output X End Position */ - chip->write(chip->opaque, 1, 0xdf); /* Output Y End Position */ - chip->write(chip->opaque, 1, 0x01); /* Output Y End Position */ - chip->write(chip->opaque, 1, 0x01); /* Input Data Format */ - chip->write(chip->opaque, 1, 0x01); /* Data Source Select */ - - fb_blank = memset(g_malloc(800 * 480 * 2), 0xff, 800 * 480 * 2); - /* Display Memory Data Port */ - chip->block(chip->opaque, 1, fb_blank, 800 * 480 * 2, 800); - g_free(fb_blank); -} - -static void n8x0_dss_setup(struct n800_s *s) -{ - s->blizzard.opaque = s1d13745_init(NULL); - s->blizzard.block = s1d13745_write_block; - s->blizzard.write = s1d13745_write; - s->blizzard.read = s1d13745_read; - - omap_rfbi_attach(s->mpu->dss, 0, &s->blizzard); -} - -static void n8x0_cbus_setup(struct n800_s *s) -{ - qemu_irq dat_out = qdev_get_gpio_in(s->mpu->gpio, N8X0_CBUS_DAT_GPIO); - qemu_irq retu_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_RETU_GPIO); - qemu_irq tahvo_irq = qdev_get_gpio_in(s->mpu->gpio, N8X0_TAHVO_GPIO); - - CBus *cbus = cbus_init(dat_out); - - qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_CLK_GPIO, cbus->clk); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_DAT_GPIO, cbus->dat); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_CBUS_SEL_GPIO, cbus->sel); - - cbus_attach(cbus, s->retu = retu_init(retu_irq, 1)); - cbus_attach(cbus, s->tahvo = tahvo_init(tahvo_irq, 1)); -} - -static void n8x0_usb_setup(struct n800_s *s) -{ - SysBusDevice *dev; - s->usb = qdev_new("tusb6010"); - dev = SYS_BUS_DEVICE(s->usb); - sysbus_realize_and_unref(dev, &error_fatal); - sysbus_connect_irq(dev, 0, - qdev_get_gpio_in(s->mpu->gpio, N8X0_TUSB_INT_GPIO)); - /* Using the NOR interface */ - omap_gpmc_attach(s->mpu->gpmc, N8X0_USB_ASYNC_CS, - sysbus_mmio_get_region(dev, 0)); - omap_gpmc_attach(s->mpu->gpmc, N8X0_USB_SYNC_CS, - sysbus_mmio_get_region(dev, 1)); - qdev_connect_gpio_out(s->mpu->gpio, N8X0_TUSB_ENABLE_GPIO, - qdev_get_gpio_in(s->usb, 0)); /* tusb_pwr */ -} - -/* 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] = { - 0x080f00d8, 0x00d40808, 0x03080808, 0x080800d0, - 0x00dc0808, 0x0b0f0f00, 0x080800b4, 0x00c00808, - 0x08080808, 0x180800c4, 0x00b80000, 0x08080808, - 0x080800bc, 0x00cc0808, 0x08081818, 0x18180128, - 0x01241800, 0x18181818, 0x000000f0, 0x01300000, - 0x00001b0b, 0x1b0f0138, 0x00e0181b, 0x1b031b0b, - 0x180f0078, 0x00740018, 0x0f0f0f1a, 0x00000080, - 0x007c0000, 0x00000000, 0x00000088, 0x00840000, - 0x00000000, 0x00000094, 0x00980300, 0x0f180003, - 0x0000008c, 0x00900f0f, 0x0f0f1b00, 0x0f00009c, - 0x01140000, 0x1b1b0f18, 0x0818013c, 0x01400008, - 0x00001818, 0x000b0110, 0x010c1800, 0x0b030b0f, - 0x181800f4, 0x00f81818, 0x00000018, 0x000000fc, - 0x00401808, 0x00000000, 0x0f1b0030, 0x003c0008, - 0x00000000, 0x00000038, 0x00340000, 0x00000000, - 0x1a080070, 0x00641a1a, 0x08080808, 0x08080060, - 0x005c0808, 0x08080808, 0x08080058, 0x00540808, - 0x08080808, 0x0808006c, 0x00680808, 0x08080808, - 0x000000a8, 0x00b00000, 0x08080808, 0x000000a0, - 0x00a40000, 0x00000000, 0x08ff0050, 0x004c0808, - 0xffffffff, 0xffff0048, 0x0044ffff, 0xffffffff, - 0x000000ac, 0x01040800, 0x08080b0f, 0x18180100, - 0x01081818, 0x0b0b1808, 0x1a0300e4, 0x012c0b1a, - 0x02020018, 0x0b000134, 0x011c0800, 0x0b1b1b00, - 0x0f0000c8, 0x00ec181b, 0x000f0f02, 0x00180118, - 0x01200000, 0x0f0b1b1b, 0x0f0200e8, 0x0000020b, -}; - -static void n800_setup_nolo_tags(void *sram_base) -{ - int i; - uint32_t *p = sram_base + 0x8000; - uint32_t *v = sram_base + 0xa000; - - memset(p, 0, 0x3000); - - strcpy((void *) (p + 0), "QEMU N800"); - - strcpy((void *) (p + 8), "F5"); - - stl_p(p + 10, 0x04f70000); - strcpy((void *) (p + 9), "RX-34"); - - /* RAM size in MB? */ - stl_p(p + 12, 0x80); - - /* Pointer to the list of tags */ - stl_p(p + 13, OMAP2_SRAM_BASE + 0x9000); - - /* The NOLO tags start here */ - p = sram_base + 0x9000; -#define ADD_TAG(tag, len) \ - stw_p((uint16_t *) p + 0, tag); \ - stw_p((uint16_t *) p + 1, len); p++; \ - stl_p(p++, OMAP2_SRAM_BASE | (((void *) v - sram_base) & 0xffff)); - - /* OMAP STI console? Pin out settings? */ - ADD_TAG(0x6e01, 414); - for (i = 0; i < ARRAY_SIZE(n800_pinout); i++) { - stl_p(v++, n800_pinout[i]); - } - - /* Kernel memsize? */ - ADD_TAG(0x6e05, 1); - stl_p(v++, 2); - - /* NOLO serial console */ - ADD_TAG(0x6e02, 4); - stl_p(v++, XLDR_LL_UART); /* UART number (1 - 3) */ - -#if 0 - /* CBUS settings (Retu/AVilma) */ - ADD_TAG(0x6e03, 6); - stw_p((uint16_t *) v + 0, 65); /* CBUS GPIO0 */ - stw_p((uint16_t *) v + 1, 66); /* CBUS GPIO1 */ - stw_p((uint16_t *) v + 2, 64); /* CBUS GPIO2 */ - v += 2; -#endif - - /* Nokia ASIC BB5 (Retu/Tahvo) */ - ADD_TAG(0x6e0a, 4); - stw_p((uint16_t *) v + 0, 111); /* "Retu" interrupt GPIO */ - stw_p((uint16_t *) v + 1, 108); /* "Tahvo" interrupt GPIO */ - v++; - - /* LCD console? */ - ADD_TAG(0x6e04, 4); - stw_p((uint16_t *) v + 0, 30); /* ??? */ - stw_p((uint16_t *) v + 1, 24); /* ??? */ - v++; - -#if 0 - /* LCD settings */ - ADD_TAG(0x6e06, 2); - stw_p((uint16_t *) (v++), 15); /* ??? */ -#endif - - /* I^2C (Menelaus) */ - ADD_TAG(0x6e07, 4); - stl_p(v++, 0x00720000); /* ??? */ - - /* Unknown */ - ADD_TAG(0x6e0b, 6); - stw_p((uint16_t *) v + 0, 94); /* ??? */ - stw_p((uint16_t *) v + 1, 23); /* ??? */ - stw_p((uint16_t *) v + 2, 0); /* ??? */ - v += 2; - - /* OMAP gpio switch info */ - ADD_TAG(0x6e0c, 80); - strcpy((void *) v, "bat_cover"); v += 3; - stw_p((uint16_t *) v + 0, 110); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 1); /* GPIO num ??? */ - v += 2; - strcpy((void *) v, "cam_act"); v += 3; - stw_p((uint16_t *) v + 0, 95); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 32); /* GPIO num ??? */ - v += 2; - strcpy((void *) v, "cam_turn"); v += 3; - stw_p((uint16_t *) v + 0, 12); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 33); /* GPIO num ??? */ - v += 2; - strcpy((void *) v, "headphone"); v += 3; - stw_p((uint16_t *) v + 0, 107); /* GPIO num ??? */ - stw_p((uint16_t *) v + 1, 17); /* GPIO num ??? */ - v += 2; - - /* Bluetooth */ - ADD_TAG(0x6e0e, 12); - stl_p(v++, 0x5c623d01); /* ??? */ - stl_p(v++, 0x00000201); /* ??? */ - stl_p(v++, 0x00000000); /* ??? */ - - /* CX3110x WLAN settings */ - ADD_TAG(0x6e0f, 8); - stl_p(v++, 0x00610025); /* ??? */ - stl_p(v++, 0xffff0057); /* ??? */ - - /* MMC host settings */ - ADD_TAG(0x6e10, 12); - stl_p(v++, 0xffff000f); /* ??? */ - stl_p(v++, 0xffffffff); /* ??? */ - stl_p(v++, 0x00000060); /* ??? */ - - /* OneNAND chip select */ - ADD_TAG(0x6e11, 10); - stl_p(v++, 0x00000401); /* ??? */ - stl_p(v++, 0x0002003a); /* ??? */ - stl_p(v++, 0x00000002); /* ??? */ - - /* TEA5761 sensor settings */ - ADD_TAG(0x6e12, 2); - stl_p(v++, 93); /* GPIO num ??? */ - -#if 0 - /* Unknown tag */ - ADD_TAG(6e09, 0); - - /* Kernel UART / console */ - ADD_TAG(6e12, 0); -#endif - - /* End of the list */ - stl_p(p++, 0x00000000); - stl_p(p++, 0x00000000); -} - -/* This task is normally performed by the bootloader. If we're loading - * a kernel directly, we need to set up GPMC mappings ourselves. */ -static void n800_gpmc_init(struct n800_s *s) -{ - uint32_t config7 = - (0xf << 8) | /* MASKADDRESS */ - (1 << 6) | /* CSVALID */ - (4 << 0); /* BASEADDRESS */ - - cpu_physical_memory_write(0x6800a078, /* GPMC_CONFIG7_0 */ - &config7, sizeof(config7)); -} - -/* Setup sequence done by the bootloader */ -static void n8x0_boot_init(void *opaque) -{ - struct n800_s *s = (struct n800_s *) opaque; - uint32_t buf; - - /* PRCM setup */ -#define omap_writel(addr, val) \ - buf = (val); \ - cpu_physical_memory_write(addr, &buf, sizeof(buf)) - - omap_writel(0x48008060, 0x41); /* PRCM_CLKSRC_CTRL */ - omap_writel(0x48008070, 1); /* PRCM_CLKOUT_CTRL */ - omap_writel(0x48008078, 0); /* PRCM_CLKEMUL_CTRL */ - omap_writel(0x48008090, 0); /* PRCM_VOLTSETUP */ - omap_writel(0x48008094, 0); /* PRCM_CLKSSETUP */ - omap_writel(0x48008098, 0); /* PRCM_POLCTRL */ - omap_writel(0x48008140, 2); /* CM_CLKSEL_MPU */ - omap_writel(0x48008148, 0); /* CM_CLKSTCTRL_MPU */ - omap_writel(0x48008158, 1); /* RM_RSTST_MPU */ - omap_writel(0x480081c8, 0x15); /* PM_WKDEP_MPU */ - omap_writel(0x480081d4, 0x1d4); /* PM_EVGENCTRL_MPU */ - omap_writel(0x480081d8, 0); /* PM_EVEGENONTIM_MPU */ - omap_writel(0x480081dc, 0); /* PM_EVEGENOFFTIM_MPU */ - omap_writel(0x480081e0, 0xc); /* PM_PWSTCTRL_MPU */ - omap_writel(0x48008200, 0x047e7ff7); /* CM_FCLKEN1_CORE */ - omap_writel(0x48008204, 0x00000004); /* CM_FCLKEN2_CORE */ - omap_writel(0x48008210, 0x047e7ff1); /* CM_ICLKEN1_CORE */ - omap_writel(0x48008214, 0x00000004); /* CM_ICLKEN2_CORE */ - omap_writel(0x4800821c, 0x00000000); /* CM_ICLKEN4_CORE */ - omap_writel(0x48008230, 0); /* CM_AUTOIDLE1_CORE */ - omap_writel(0x48008234, 0); /* CM_AUTOIDLE2_CORE */ - omap_writel(0x48008238, 7); /* CM_AUTOIDLE3_CORE */ - omap_writel(0x4800823c, 0); /* CM_AUTOIDLE4_CORE */ - omap_writel(0x48008240, 0x04360626); /* CM_CLKSEL1_CORE */ - omap_writel(0x48008244, 0x00000014); /* CM_CLKSEL2_CORE */ - omap_writel(0x48008248, 0); /* CM_CLKSTCTRL_CORE */ - omap_writel(0x48008300, 0x00000000); /* CM_FCLKEN_GFX */ - omap_writel(0x48008310, 0x00000000); /* CM_ICLKEN_GFX */ - omap_writel(0x48008340, 0x00000001); /* CM_CLKSEL_GFX */ - omap_writel(0x48008400, 0x00000004); /* CM_FCLKEN_WKUP */ - omap_writel(0x48008410, 0x00000004); /* CM_ICLKEN_WKUP */ - omap_writel(0x48008440, 0x00000000); /* CM_CLKSEL_WKUP */ - omap_writel(0x48008500, 0x000000cf); /* CM_CLKEN_PLL */ - omap_writel(0x48008530, 0x0000000c); /* CM_AUTOIDLE_PLL */ - omap_writel(0x48008540, /* CM_CLKSEL1_PLL */ - (0x78 << 12) | (6 << 8)); - omap_writel(0x48008544, 2); /* CM_CLKSEL2_PLL */ - - /* GPMC setup */ - n800_gpmc_init(s); - - /* Video setup */ - n800_dss_init(&s->blizzard); - - /* CPU setup */ - s->mpu->cpu->env.GE = 0x5; - - /* If the machine has a slided keyboard, open it */ - if (s->kbd) { - qemu_irq_raise(qdev_get_gpio_in(s->mpu->gpio, N810_SLIDE_GPIO)); - } -} - -#define OMAP_TAG_NOKIA_BT 0x4e01 -#define OMAP_TAG_WLAN_CX3110X 0x4e02 -#define OMAP_TAG_CBUS 0x4e03 -#define OMAP_TAG_EM_ASIC_BB5 0x4e04 - -static struct omap_gpiosw_info_s { - const char *name; - int line; - int type; -} n800_gpiosw_info[] = { - { - "bat_cover", N800_BAT_COVER_GPIO, - OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, - }, { - "cam_act", N800_CAM_ACT_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY, - }, { - "cam_turn", N800_CAM_TURN_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED, - }, { - "headphone", N8X0_HEADPHONE_GPIO, - OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED, - }, - { NULL } -}, n810_gpiosw_info[] = { - { - "gps_reset", N810_GPS_RESET_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT, - }, { - "gps_wakeup", N810_GPS_WAKEUP_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_OUTPUT, - }, { - "headphone", N8X0_HEADPHONE_GPIO, - OMAP_GPIOSW_TYPE_CONNECTION | OMAP_GPIOSW_INVERTED, - }, { - "kb_lock", N810_KB_LOCK_GPIO, - OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, - }, { - "sleepx_led", N810_SLEEPX_LED_GPIO, - OMAP_GPIOSW_TYPE_ACTIVITY | OMAP_GPIOSW_INVERTED | OMAP_GPIOSW_OUTPUT, - }, { - "slide", N810_SLIDE_GPIO, - OMAP_GPIOSW_TYPE_COVER | OMAP_GPIOSW_INVERTED, - }, - { NULL } -}; - -static struct omap_partition_info_s { - uint32_t offset; - uint32_t size; - int mask; - const char *name; -} n800_part_info[] = { - { 0x00000000, 0x00020000, 0x3, "bootloader" }, - { 0x00020000, 0x00060000, 0x0, "config" }, - { 0x00080000, 0x00200000, 0x0, "kernel" }, - { 0x00280000, 0x00200000, 0x3, "initfs" }, - { 0x00480000, 0x0fb80000, 0x3, "rootfs" }, - - { 0, 0, 0, NULL } -}, 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 } -}; - -static 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 char *tag; - - w = p; - - stw_p(w++, OMAP_TAG_UART); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, (1 << 2) | (1 << 1) | (1 << 0)); /* uint enabled_uarts */ - w++; - -#if 0 - stw_p(w++, OMAP_TAG_SERIAL_CONSOLE); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, XLDR_LL_UART + 1); /* u8 console_uart */ - stw_p(w++, 115200); /* u32 console_speed */ -#endif - - stw_p(w++, OMAP_TAG_LCD); /* u16 tag */ - stw_p(w++, 36); /* u16 len */ - strcpy((void *) w, "QEMU LCD panel"); /* char panel_name[16] */ - w += 8; - strcpy((void *) w, "blizzard"); /* char ctrl_name[16] */ - w += 8; - stw_p(w++, N810_BLIZZARD_RESET_GPIO); /* TODO: n800 s16 nreset_gpio */ - stw_p(w++, 24); /* u8 data_lines */ - - stw_p(w++, OMAP_TAG_CBUS); /* u16 tag */ - stw_p(w++, 8); /* u16 len */ - stw_p(w++, N8X0_CBUS_CLK_GPIO); /* s16 clk_gpio */ - stw_p(w++, N8X0_CBUS_DAT_GPIO); /* s16 dat_gpio */ - stw_p(w++, N8X0_CBUS_SEL_GPIO); /* s16 sel_gpio */ - w++; - - stw_p(w++, OMAP_TAG_EM_ASIC_BB5); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, N8X0_RETU_GPIO); /* s16 retu_irq_gpio */ - stw_p(w++, N8X0_TAHVO_GPIO); /* s16 tahvo_irq_gpio */ - - gpiosw = (model == 810) ? n810_gpiosw_info : n800_gpiosw_info; - for (; gpiosw->name; gpiosw++) { - stw_p(w++, OMAP_TAG_GPIO_SWITCH); /* u16 tag */ - stw_p(w++, 20); /* u16 len */ - strcpy((void *) w, gpiosw->name); /* char name[12] */ - w += 6; - stw_p(w++, gpiosw->line); /* u16 gpio */ - stw_p(w++, gpiosw->type); - stw_p(w++, 0); - stw_p(w++, 0); - } - - stw_p(w++, OMAP_TAG_NOKIA_BT); /* u16 tag */ - stw_p(w++, 12); /* u16 len */ - b = (void *) w; - stb_p(b++, 0x01); /* u8 chip_type (CSR) */ - stb_p(b++, N8X0_BT_WKUP_GPIO); /* u8 bt_wakeup_gpio */ - stb_p(b++, N8X0_BT_HOST_WKUP_GPIO); /* u8 host_wakeup_gpio */ - stb_p(b++, N8X0_BT_RESET_GPIO); /* u8 reset_gpio */ - stb_p(b++, BT_UART + 1); /* u8 bt_uart */ - memcpy(b, &n8x0_bd_addr, 6); /* u8 bd_addr[6] */ - b += 6; - stb_p(b++, 0x02); /* u8 bt_sysclk (38.4) */ - w = (void *) b; - - stw_p(w++, OMAP_TAG_WLAN_CX3110X); /* u16 tag */ - stw_p(w++, 8); /* u16 len */ - stw_p(w++, 0x25); /* u8 chip_type */ - stw_p(w++, N8X0_WLAN_PWR_GPIO); /* s16 power_gpio */ - stw_p(w++, N8X0_WLAN_IRQ_GPIO); /* s16 irq_gpio */ - stw_p(w++, -1); /* s16 spi_cs_gpio */ - - stw_p(w++, OMAP_TAG_MMC); /* u16 tag */ - stw_p(w++, 16); /* u16 len */ - if (model == 810) { - stw_p(w++, 0x23f); /* unsigned flags */ - stw_p(w++, -1); /* s16 power_pin */ - stw_p(w++, -1); /* s16 switch_pin */ - stw_p(w++, -1); /* s16 wp_pin */ - stw_p(w++, 0x240); /* unsigned flags */ - stw_p(w++, 0xc000); /* s16 power_pin */ - stw_p(w++, 0x0248); /* s16 switch_pin */ - stw_p(w++, 0xc000); /* s16 wp_pin */ - } else { - stw_p(w++, 0xf); /* unsigned flags */ - stw_p(w++, -1); /* s16 power_pin */ - stw_p(w++, -1); /* s16 switch_pin */ - stw_p(w++, -1); /* s16 wp_pin */ - stw_p(w++, 0); /* unsigned flags */ - stw_p(w++, 0); /* s16 power_pin */ - stw_p(w++, 0); /* s16 switch_pin */ - stw_p(w++, 0); /* s16 wp_pin */ - } - - stw_p(w++, OMAP_TAG_TEA5761); /* u16 tag */ - stw_p(w++, 4); /* u16 len */ - stw_p(w++, N8X0_TEA5761_CS_GPIO); /* u16 enable_gpio */ - w++; - - partition = (model == 810) ? n810_part_info : n800_part_info; - for (; partition->name; partition++) { - stw_p(w++, OMAP_TAG_PARTITION); /* u16 tag */ - stw_p(w++, 28); /* u16 len */ - strcpy((void *) w, partition->name); /* char name[16] */ - l = (void *) (w + 8); - stl_p(l++, partition->size); /* unsigned int size */ - stl_p(l++, partition->offset); /* unsigned int offset */ - stl_p(l++, partition->mask); /* unsigned int mask_flags */ - w = (void *) l; - } - - stw_p(w++, OMAP_TAG_BOOT_REASON); /* u16 tag */ - stw_p(w++, 12); /* u16 len */ -#if 0 - strcpy((void *) w, "por"); /* char reason_str[12] */ - strcpy((void *) w, "charger"); /* char reason_str[12] */ - strcpy((void *) w, "32wd_to"); /* char reason_str[12] */ - strcpy((void *) w, "sw_rst"); /* char reason_str[12] */ - strcpy((void *) w, "mbus"); /* char reason_str[12] */ - strcpy((void *) w, "unknown"); /* char reason_str[12] */ - strcpy((void *) w, "swdg_to"); /* char reason_str[12] */ - strcpy((void *) w, "sec_vio"); /* char reason_str[12] */ - strcpy((void *) w, "pwr_key"); /* char reason_str[12] */ - strcpy((void *) w, "rtc_alarm"); /* char reason_str[12] */ -#else - strcpy((void *) w, "pwr_key"); /* char reason_str[12] */ -#endif - w += 6; - - tag = (model == 810) ? "RX-44" : "RX-34"; - stw_p(w++, OMAP_TAG_VERSION_STR); /* u16 tag */ - stw_p(w++, 24); /* u16 len */ - strcpy((void *) w, "product"); /* char component[12] */ - w += 6; - strcpy((void *) w, tag); /* char version[12] */ - w += 6; - - stw_p(w++, OMAP_TAG_VERSION_STR); /* u16 tag */ - stw_p(w++, 24); /* u16 len */ - strcpy((void *) w, "hw-build"); /* char component[12] */ - w += 6; - strcpy((void *) w, "QEMU "); - pstrcat((void *) w, 12, qemu_hw_version()); /* char version[12] */ - w += 6; - - tag = (model == 810) ? "1.1.10-qemu" : "1.1.6-qemu"; - stw_p(w++, OMAP_TAG_VERSION_STR); /* u16 tag */ - stw_p(w++, 24); /* u16 len */ - strcpy((void *) w, "nolo"); /* char component[12] */ - w += 6; - strcpy((void *) w, tag); /* char version[12] */ - w += 6; - - return (void *) w - p; -} - -static int n800_atag_setup(const struct arm_boot_info *info, void *p) -{ - return n8x0_atag_setup(p, 800); -} - -static int n810_atag_setup(const struct arm_boot_info *info, void *p) -{ - return n8x0_atag_setup(p, 810); -} - -static void n8x0_init(MachineState *machine, - struct arm_boot_info *binfo, int model) -{ - struct n800_s *s = g_malloc0(sizeof(*s)); - MachineClass *mc = MACHINE_GET_CLASS(machine); - - 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); - } - binfo->ram_size = machine->ram_size; - - memory_region_add_subregion(get_system_memory(), OMAP2_Q2_BASE, - machine->ram); - - s->mpu = omap2420_mpu_init(machine->ram, machine->cpu_type); - - /* Setup peripherals - * - * Believed external peripherals layout in the N810: - * (spi bus 1) - * tsc2005 - * lcd_mipid - * (spi bus 2) - * Conexant cx3110x (WLAN) - * optional: pc2400m (WiMAX) - * (i2c bus 0) - * TLV320AIC33 (audio codec) - * TCM825x (camera by Toshiba) - * lp5521 (clever LEDs) - * tsl2563 (light sensor, hwmon, model 7, rev. 0) - * lm8323 (keypad, manf 00, rev 04) - * (i2c bus 1) - * tmp105 (temperature sensor, hwmon) - * menelaus (pm) - * (somewhere on i2c - maybe N800-only) - * tea5761 (FM tuner) - * (serial 0) - * GPS - * (some serial port) - * csr41814 (Bluetooth) - */ - n8x0_gpio_setup(s); - n8x0_nand_setup(s); - n8x0_i2c_setup(s); - if (model == 800) { - n800_tsc_kbd_setup(s); - } else if (model == 810) { - n810_tsc_setup(s); - n810_kbd_setup(s); - } - n8x0_spi_setup(s); - n8x0_dss_setup(s); - n8x0_cbus_setup(s); - if (machine_usb(machine)) { - n8x0_usb_setup(s); - } - - if (machine->kernel_filename) { - /* Or at the linux loader. */ - arm_load_kernel(s->mpu->cpu, machine, binfo); - - qemu_register_reset(n8x0_boot_init, s); - } - - if (option_rom[0].name && - (machine->boot_config.order[0] == 'n' || !machine->kernel_filename)) { - uint8_t *nolo_tags = g_new(uint8_t, 0x10000); - /* No, wait, better start at the ROM. */ - s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000; - - /* - * This is intended for loading the `secondary.bin' program from - * Nokia images (the NOLO bootloader). The entry point seems - * to be at OMAP2_Q2_BASE + 0x400000. - * - * The `2nd.bin' files contain some kind of earlier boot code and - * for them the entry point needs to be set to OMAP2_SRAM_BASE. - * - * The code above is for loading the `zImage' file from Nokia - * images. - */ - if (load_image_targphys(option_rom[0].name, - OMAP2_Q2_BASE + 0x400000, - machine->ram_size - 0x400000) < 0) { - error_report("Failed to load secondary bootloader %s", - option_rom[0].name); - exit(EXIT_FAILURE); - } - - n800_setup_nolo_tags(nolo_tags); - cpu_physical_memory_write(OMAP2_SRAM_BASE, nolo_tags, 0x10000); - g_free(nolo_tags); - } -} - -static struct arm_boot_info n800_binfo = { - .loader_start = OMAP2_Q2_BASE, - .board_id = 0x4f7, - .atag_board = n800_atag_setup, -}; - -static struct arm_boot_info n810_binfo = { - .loader_start = OMAP2_Q2_BASE, - /* 0x60c and 0x6bf (WiMAX Edition) have been assigned but are not - * used by some older versions of the bootloader and 5555 is used - * instead (including versions that shipped with many devices). */ - .board_id = 0x60c, - .atag_board = n810_atag_setup, -}; - -static void n800_init(MachineState *machine) -{ - n8x0_init(machine, &n800_binfo, 800); -} - -static void n810_init(MachineState *machine) -{ - n8x0_init(machine, &n810_binfo, 810); -} - -static void n800_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Nokia N800 tablet aka. RX-34 (OMAP2420)"; - mc->init = n800_init; - mc->default_boot_order = ""; - mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1136-r2"); - /* Actually two chips of 0x4000000 bytes each */ - mc->default_ram_size = 0x08000000; - mc->default_ram_id = "omap2.dram"; -} - -static const TypeInfo n800_type = { - .name = MACHINE_TYPE_NAME("n800"), - .parent = TYPE_MACHINE, - .class_init = n800_class_init, -}; - -static void n810_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Nokia N810 tablet aka. RX-44 (OMAP2420)"; - mc->init = n810_init; - mc->default_boot_order = ""; - mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1136-r2"); - /* Actually two chips of 0x4000000 bytes each */ - mc->default_ram_size = 0x08000000; - mc->default_ram_id = "omap2.dram"; -} - -static const TypeInfo n810_type = { - .name = MACHINE_TYPE_NAME("n810"), - .parent = TYPE_MACHINE, - .class_init = n810_class_init, -}; - -static void nseries_machine_init(void) -{ - type_register_static(&n800_type); - type_register_static(&n810_type); -} - -type_init(nseries_machine_init) 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..25030c7e40 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) @@ -2169,39 +2170,36 @@ struct omap_uwire_s { uint16_t rxbuf; uint16_t control; uint16_t setup[5]; - - uWireSlave *chip[4]; }; static void omap_uwire_transfer_start(struct omap_uwire_s *s) { int chipselect = (s->control >> 10) & 3; /* INDEX */ - uWireSlave *slave = s->chip[chipselect]; if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */ - if (s->control & (1 << 12)) /* CS_CMD */ - if (slave && slave->send) - slave->send(slave->opaque, - s->txbuf >> (16 - ((s->control >> 5) & 0x1f))); + if (s->control & (1 << 12)) { /* CS_CMD */ + qemu_log_mask(LOG_UNIMP, "uWireSlave TX CS:%d data:0x%04x\n", + chipselect, + s->txbuf >> (16 - ((s->control >> 5) & 0x1f))); + } s->control &= ~(1 << 14); /* CSRB */ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or * a DRQ. When is the level IRQ supposed to be reset? */ } if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */ - if (s->control & (1 << 12)) /* CS_CMD */ - if (slave && slave->receive) - s->rxbuf = slave->receive(slave->opaque); + if (s->control & (1 << 12)) { /* CS_CMD */ + qemu_log_mask(LOG_UNIMP, "uWireSlave RX CS:%d\n", chipselect); + } s->control |= 1 << 15; /* RDRB */ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or * a DRQ. When is the level IRQ supposed to be reset? */ } } -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 +2233,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) { @@ -2321,17 +2319,6 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory, return s; } -void omap_uwire_attach(struct omap_uwire_s *s, - uWireSlave *slave, int chipselect) -{ - if (chipselect < 0 || chipselect > 3) { - error_report("%s: Bad chipselect %i", __func__, chipselect); - exit(-1); - } - - s->chip[chipselect] = slave; -} - /* Pseudonoise Pulse-Width Light Modulator */ struct omap_pwl_s { MemoryRegion iomem; @@ -2351,10 +2338,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 +2360,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 +2400,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 +2431,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 +2455,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 +2562,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 +2646,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 +3018,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 +3064,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 +3157,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 +3255,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 +3391,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 +3482,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 +3492,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 +3574,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 +3598,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 +3633,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 +3696,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 +3776,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 +4045,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 deleted file mode 100644 index 8571eedd73..0000000000 --- a/hw/arm/omap2.c +++ /dev/null @@ -1,2714 +0,0 @@ -/* - * TI OMAP processors emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#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" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/arm/boot.h" -#include "hw/arm/omap.h" -#include "sysemu/sysemu.h" -#include "qemu/timer.h" -#include "chardev/char-fe.h" -#include "hw/block/flash.h" -#include "hw/arm/soc_dma.h" -#include "hw/sysbus.h" -#include "audio/audio.h" - -/* Enhanced Audio Controller (CODEC only) */ -struct omap_eac_s { - qemu_irq irq; - MemoryRegion iomem; - - uint16_t sysconfig; - uint8_t config[4]; - uint8_t control; - uint8_t address; - uint16_t data; - uint8_t vtol; - uint8_t vtsl; - uint16_t mixer; - uint16_t gain[4]; - uint8_t att; - uint16_t max[7]; - - struct { - qemu_irq txdrq; - qemu_irq rxdrq; - uint32_t (*txrx)(void *opaque, uint32_t, int); - void *opaque; - -#define EAC_BUF_LEN 1024 - uint32_t rxbuf[EAC_BUF_LEN]; - int rxoff; - int rxlen; - int rxavail; - uint32_t txbuf[EAC_BUF_LEN]; - int txlen; - int txavail; - - int enable; - int rate; - - uint16_t config[4]; - - /* These need to be moved to the actual codec */ - QEMUSoundCard card; - SWVoiceIn *in_voice; - SWVoiceOut *out_voice; - int hw_enable; - } codec; - - struct { - uint8_t control; - uint16_t config; - } modem, bt; -}; - -static inline void omap_eac_interrupt_update(struct omap_eac_s *s) -{ - qemu_set_irq(s->irq, (s->codec.config[1] >> 14) & 1); /* AURDI */ -} - -static inline void omap_eac_in_dmarequest_update(struct omap_eac_s *s) -{ - qemu_set_irq(s->codec.rxdrq, (s->codec.rxavail || s->codec.rxlen) && - ((s->codec.config[1] >> 12) & 1)); /* DMAREN */ -} - -static inline void omap_eac_out_dmarequest_update(struct omap_eac_s *s) -{ - qemu_set_irq(s->codec.txdrq, s->codec.txlen < s->codec.txavail && - ((s->codec.config[1] >> 11) & 1)); /* DMAWEN */ -} - -static inline void omap_eac_in_refill(struct omap_eac_s *s) -{ - int left = MIN(EAC_BUF_LEN - s->codec.rxlen, s->codec.rxavail) << 2; - int start = ((s->codec.rxoff + s->codec.rxlen) & (EAC_BUF_LEN - 1)) << 2; - int leftwrap = MIN(left, (EAC_BUF_LEN << 2) - start); - int recv = 1; - uint8_t *buf = (uint8_t *) s->codec.rxbuf + start; - - left -= leftwrap; - start = 0; - while (leftwrap && (recv = AUD_read(s->codec.in_voice, buf + start, - leftwrap)) > 0) { /* Be defensive */ - start += recv; - leftwrap -= recv; - } - if (recv <= 0) - s->codec.rxavail = 0; - else - s->codec.rxavail -= start >> 2; - s->codec.rxlen += start >> 2; - - if (recv > 0 && left > 0) { - start = 0; - while (left && (recv = AUD_read(s->codec.in_voice, - (uint8_t *) s->codec.rxbuf + start, - left)) > 0) { /* Be defensive */ - start += recv; - left -= recv; - } - if (recv <= 0) - s->codec.rxavail = 0; - else - s->codec.rxavail -= start >> 2; - s->codec.rxlen += start >> 2; - } -} - -static inline void omap_eac_out_empty(struct omap_eac_s *s) -{ - int left = s->codec.txlen << 2; - int start = 0; - int sent = 1; - - while (left && (sent = AUD_write(s->codec.out_voice, - (uint8_t *) s->codec.txbuf + start, - left)) > 0) { /* Be defensive */ - start += sent; - left -= sent; - } - - if (!sent) { - s->codec.txavail = 0; - omap_eac_out_dmarequest_update(s); - } - - if (start) - s->codec.txlen = 0; -} - -static void omap_eac_in_cb(void *opaque, int avail_b) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - - s->codec.rxavail = avail_b >> 2; - omap_eac_in_refill(s); - /* TODO: possibly discard current buffer if overrun */ - omap_eac_in_dmarequest_update(s); -} - -static void omap_eac_out_cb(void *opaque, int free_b) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - - s->codec.txavail = free_b >> 2; - if (s->codec.txlen) - omap_eac_out_empty(s); - else - omap_eac_out_dmarequest_update(s); -} - -static void omap_eac_enable_update(struct omap_eac_s *s) -{ - s->codec.enable = !(s->codec.config[1] & 1) && /* EACPWD */ - (s->codec.config[1] & 2) && /* AUDEN */ - s->codec.hw_enable; -} - -static const int omap_eac_fsint[4] = { - 8000, - 11025, - 22050, - 44100, -}; - -static const int omap_eac_fsint2[8] = { - 8000, - 11025, - 22050, - 44100, - 48000, - 0, 0, 0, -}; - -static const int omap_eac_fsint3[16] = { - 8000, - 11025, - 16000, - 22050, - 24000, - 32000, - 44100, - 48000, - 0, 0, 0, 0, 0, 0, 0, 0, -}; - -static void omap_eac_rate_update(struct omap_eac_s *s) -{ - int fsint[3]; - - fsint[2] = (s->codec.config[3] >> 9) & 0xf; - fsint[1] = (s->codec.config[2] >> 0) & 0x7; - fsint[0] = (s->codec.config[0] >> 6) & 0x3; - if (fsint[2] < 0xf) - s->codec.rate = omap_eac_fsint3[fsint[2]]; - else if (fsint[1] < 0x7) - s->codec.rate = omap_eac_fsint2[fsint[1]]; - else - s->codec.rate = omap_eac_fsint[fsint[0]]; -} - -static void omap_eac_volume_update(struct omap_eac_s *s) -{ - /* TODO */ -} - -static void omap_eac_format_update(struct omap_eac_s *s) -{ - struct audsettings fmt; - - /* The hardware buffers at most one sample */ - if (s->codec.rxlen) - s->codec.rxlen = 1; - - if (s->codec.in_voice) { - AUD_set_active_in(s->codec.in_voice, 0); - AUD_close_in(&s->codec.card, s->codec.in_voice); - s->codec.in_voice = NULL; - } - if (s->codec.out_voice) { - omap_eac_out_empty(s); - AUD_set_active_out(s->codec.out_voice, 0); - AUD_close_out(&s->codec.card, s->codec.out_voice); - s->codec.out_voice = NULL; - s->codec.txavail = 0; - } - /* Discard what couldn't be written */ - s->codec.txlen = 0; - - omap_eac_enable_update(s); - if (!s->codec.enable) - return; - - omap_eac_rate_update(s); - fmt.endianness = ((s->codec.config[0] >> 8) & 1); /* LI_BI */ - fmt.nchannels = ((s->codec.config[0] >> 10) & 1) ? 2 : 1; /* MN_ST */ - 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 store 16-bit samples - * in the buffers regardless of AGCFR[B8_16] value. */ - fmt.fmt = AUDIO_FORMAT_U16; - - s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice, - "eac.codec.in", s, omap_eac_in_cb, &fmt); - s->codec.out_voice = AUD_open_out(&s->codec.card, s->codec.out_voice, - "eac.codec.out", s, omap_eac_out_cb, &fmt); - - omap_eac_volume_update(s); - - AUD_set_active_in(s->codec.in_voice, 1); - AUD_set_active_out(s->codec.out_voice, 1); -} - -static void omap_eac_reset(struct omap_eac_s *s) -{ - s->sysconfig = 0; - s->config[0] = 0x0c; - s->config[1] = 0x09; - s->config[2] = 0xab; - s->config[3] = 0x03; - s->control = 0x00; - s->address = 0x00; - s->data = 0x0000; - s->vtol = 0x00; - s->vtsl = 0x00; - s->mixer = 0x0000; - s->gain[0] = 0xe7e7; - s->gain[1] = 0x6767; - s->gain[2] = 0x6767; - s->gain[3] = 0x6767; - s->att = 0xce; - s->max[0] = 0; - s->max[1] = 0; - s->max[2] = 0; - s->max[3] = 0; - s->max[4] = 0; - s->max[5] = 0; - s->max[6] = 0; - - s->modem.control = 0x00; - s->modem.config = 0x0000; - s->bt.control = 0x00; - s->bt.config = 0x0000; - s->codec.config[0] = 0x0649; - s->codec.config[1] = 0x0000; - s->codec.config[2] = 0x0007; - s->codec.config[3] = 0x1ffc; - s->codec.rxoff = 0; - s->codec.rxlen = 0; - s->codec.txlen = 0; - s->codec.rxavail = 0; - s->codec.txavail = 0; - - omap_eac_format_update(s); - omap_eac_interrupt_update(s); -} - -static uint64_t omap_eac_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - uint32_t ret; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x000: /* CPCFR1 */ - return s->config[0]; - case 0x004: /* CPCFR2 */ - return s->config[1]; - case 0x008: /* CPCFR3 */ - return s->config[2]; - case 0x00c: /* CPCFR4 */ - return s->config[3]; - - case 0x010: /* CPTCTL */ - return s->control | ((s->codec.rxavail + s->codec.rxlen > 0) << 7) | - ((s->codec.txlen < s->codec.txavail) << 5); - - case 0x014: /* CPTTADR */ - return s->address; - case 0x018: /* CPTDATL */ - return s->data & 0xff; - case 0x01c: /* CPTDATH */ - return s->data >> 8; - case 0x020: /* CPTVSLL */ - return s->vtol; - case 0x024: /* CPTVSLH */ - return s->vtsl | (3 << 5); /* CRDY1 | CRDY2 */ - case 0x040: /* MPCTR */ - return s->modem.control; - case 0x044: /* MPMCCFR */ - return s->modem.config; - case 0x060: /* BPCTR */ - return s->bt.control; - case 0x064: /* BPMCCFR */ - return s->bt.config; - case 0x080: /* AMSCFR */ - return s->mixer; - case 0x084: /* AMVCTR */ - return s->gain[0]; - case 0x088: /* AM1VCTR */ - return s->gain[1]; - case 0x08c: /* AM2VCTR */ - return s->gain[2]; - case 0x090: /* AM3VCTR */ - return s->gain[3]; - case 0x094: /* ASTCTR */ - return s->att; - case 0x098: /* APD1LCR */ - return s->max[0]; - case 0x09c: /* APD1RCR */ - return s->max[1]; - case 0x0a0: /* APD2LCR */ - return s->max[2]; - case 0x0a4: /* APD2RCR */ - return s->max[3]; - case 0x0a8: /* APD3LCR */ - return s->max[4]; - case 0x0ac: /* APD3RCR */ - return s->max[5]; - case 0x0b0: /* APD4R */ - return s->max[6]; - case 0x0b4: /* ADWR */ - /* This should be write-only? Docs list it as read-only. */ - return 0x0000; - case 0x0b8: /* ADRDR */ - if (likely(s->codec.rxlen > 1)) { - ret = s->codec.rxbuf[s->codec.rxoff ++]; - s->codec.rxlen --; - s->codec.rxoff &= EAC_BUF_LEN - 1; - return ret; - } else if (s->codec.rxlen) { - ret = s->codec.rxbuf[s->codec.rxoff ++]; - s->codec.rxlen --; - s->codec.rxoff &= EAC_BUF_LEN - 1; - if (s->codec.rxavail) - omap_eac_in_refill(s); - omap_eac_in_dmarequest_update(s); - return ret; - } - return 0x0000; - case 0x0bc: /* AGCFR */ - return s->codec.config[0]; - case 0x0c0: /* AGCTR */ - return s->codec.config[1] | ((s->codec.config[1] & 2) << 14); - case 0x0c4: /* AGCFR2 */ - return s->codec.config[2]; - case 0x0c8: /* AGCFR3 */ - return s->codec.config[3]; - case 0x0cc: /* MBPDMACTR */ - case 0x0d0: /* MPDDMARR */ - case 0x0d8: /* MPUDMARR */ - case 0x0e4: /* BPDDMARR */ - case 0x0ec: /* BPUDMARR */ - return 0x0000; - - case 0x100: /* VERSION_NUMBER */ - return 0x0010; - - case 0x104: /* SYSCONFIG */ - return s->sysconfig; - - case 0x108: /* SYSSTATUS */ - return 1 | 0xe; /* RESETDONE | stuff */ - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_eac_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_eac_s *s = (struct omap_eac_s *) opaque; - - if (size != 2) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x098: /* APD1LCR */ - case 0x09c: /* APD1RCR */ - case 0x0a0: /* APD2LCR */ - case 0x0a4: /* APD2RCR */ - case 0x0a8: /* APD3LCR */ - case 0x0ac: /* APD3RCR */ - case 0x0b0: /* APD4R */ - case 0x0b8: /* ADRDR */ - case 0x0d0: /* MPDDMARR */ - case 0x0d8: /* MPUDMARR */ - case 0x0e4: /* BPDDMARR */ - case 0x0ec: /* BPUDMARR */ - case 0x100: /* VERSION_NUMBER */ - case 0x108: /* SYSSTATUS */ - OMAP_RO_REG(addr); - return; - - case 0x000: /* CPCFR1 */ - s->config[0] = value & 0xff; - omap_eac_format_update(s); - break; - case 0x004: /* CPCFR2 */ - s->config[1] = value & 0xff; - omap_eac_format_update(s); - break; - case 0x008: /* CPCFR3 */ - s->config[2] = value & 0xff; - omap_eac_format_update(s); - break; - case 0x00c: /* CPCFR4 */ - s->config[3] = value & 0xff; - omap_eac_format_update(s); - break; - - case 0x010: /* CPTCTL */ - /* Assuming TXF and TXE bits are read-only... */ - s->control = value & 0x5f; - omap_eac_interrupt_update(s); - break; - - case 0x014: /* CPTTADR */ - s->address = value & 0xff; - break; - case 0x018: /* CPTDATL */ - s->data &= 0xff00; - s->data |= value & 0xff; - break; - case 0x01c: /* CPTDATH */ - s->data &= 0x00ff; - s->data |= value << 8; - break; - case 0x020: /* CPTVSLL */ - s->vtol = value & 0xf8; - break; - case 0x024: /* CPTVSLH */ - s->vtsl = value & 0x9f; - break; - case 0x040: /* MPCTR */ - s->modem.control = value & 0x8f; - break; - case 0x044: /* MPMCCFR */ - s->modem.config = value & 0x7fff; - break; - case 0x060: /* BPCTR */ - s->bt.control = value & 0x8f; - break; - case 0x064: /* BPMCCFR */ - s->bt.config = value & 0x7fff; - break; - case 0x080: /* AMSCFR */ - s->mixer = value & 0x0fff; - break; - case 0x084: /* AMVCTR */ - s->gain[0] = value & 0xffff; - break; - case 0x088: /* AM1VCTR */ - s->gain[1] = value & 0xff7f; - break; - case 0x08c: /* AM2VCTR */ - s->gain[2] = value & 0xff7f; - break; - case 0x090: /* AM3VCTR */ - s->gain[3] = value & 0xff7f; - break; - case 0x094: /* ASTCTR */ - s->att = value & 0xff; - break; - - case 0x0b4: /* ADWR */ - s->codec.txbuf[s->codec.txlen ++] = value; - if (unlikely(s->codec.txlen == EAC_BUF_LEN || - s->codec.txlen == s->codec.txavail)) { - if (s->codec.txavail) - omap_eac_out_empty(s); - /* Discard what couldn't be written */ - s->codec.txlen = 0; - } - break; - - case 0x0bc: /* AGCFR */ - s->codec.config[0] = value & 0x07ff; - omap_eac_format_update(s); - break; - case 0x0c0: /* AGCTR */ - s->codec.config[1] = value & 0x780f; - omap_eac_format_update(s); - break; - case 0x0c4: /* AGCFR2 */ - s->codec.config[2] = value & 0x003f; - omap_eac_format_update(s); - break; - case 0x0c8: /* AGCFR3 */ - s->codec.config[3] = value & 0xffff; - omap_eac_format_update(s); - break; - case 0x0cc: /* MBPDMACTR */ - case 0x0d4: /* MPDDMAWR */ - case 0x0e0: /* MPUDMAWR */ - case 0x0e8: /* BPDDMAWR */ - case 0x0f0: /* BPUDMAWR */ - break; - - case 0x104: /* SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_eac_reset(s); - s->sysconfig = value & 0x31d; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_eac_ops = { - .read = omap_eac_read, - .write = omap_eac_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, - qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) -{ - struct omap_eac_s *s = g_new0(struct omap_eac_s, 1); - - s->irq = irq; - s->codec.rxdrq = *drq ++; - s->codec.txdrq = *drq; - omap_eac_reset(s); - - AUD_register_card("OMAP EAC", &s->codec.card); - - memory_region_init_io(&s->iomem, NULL, &omap_eac_ops, s, "omap.eac", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} - -/* STI/XTI (emulation interface) console - reverse engineered only */ -struct omap_sti_s { - qemu_irq irq; - MemoryRegion iomem; - MemoryRegion iomem_fifo; - CharBackend chr; - - uint32_t sysconfig; - uint32_t systest; - uint32_t irqst; - uint32_t irqen; - uint32_t clkcontrol; - uint32_t serial_config; -}; - -#define STI_TRACE_CONSOLE_CHANNEL 239 -#define STI_TRACE_CONTROL_CHANNEL 253 - -static inline void omap_sti_interrupt_update(struct omap_sti_s *s) -{ - qemu_set_irq(s->irq, s->irqst & s->irqen); -} - -static void omap_sti_reset(struct omap_sti_s *s) -{ - s->sysconfig = 0; - s->irqst = 0; - s->irqen = 0; - s->clkcontrol = 0; - s->serial_config = 0; - - omap_sti_interrupt_update(s); -} - -static uint64_t omap_sti_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_sti_s *s = (struct omap_sti_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* STI_REVISION */ - return 0x10; - - case 0x10: /* STI_SYSCONFIG */ - return s->sysconfig; - - case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */ - return 0x00; - - case 0x18: /* STI_IRQSTATUS */ - return s->irqst; - - case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */ - return s->irqen; - - case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */ - case 0x28: /* STI_RX_DR / XTI_RXDATA */ - /* TODO */ - return 0; - - case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */ - return s->clkcontrol; - - case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */ - return s->serial_config; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sti_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_sti_s *s = (struct omap_sti_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* STI_REVISION */ - case 0x14: /* STI_SYSSTATUS / STI_RX_STATUS / XTI_SYSSTATUS */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* STI_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_sti_reset(s); - s->sysconfig = value & 0xfe; - break; - - case 0x18: /* STI_IRQSTATUS */ - s->irqst &= ~value; - omap_sti_interrupt_update(s); - break; - - case 0x1c: /* STI_IRQSETEN / STI_IRQCLREN */ - s->irqen = value & 0xffff; - omap_sti_interrupt_update(s); - break; - - case 0x2c: /* STI_CLK_CTRL / XTI_SCLKCRTL */ - s->clkcontrol = value & 0xff; - break; - - case 0x30: /* STI_SERIAL_CFG / XTI_SCONFIG */ - s->serial_config = value & 0xff; - break; - - case 0x24: /* STI_ER / STI_DR / XTI_TRACESELECT */ - case 0x28: /* STI_RX_DR / XTI_RXDATA */ - /* TODO */ - return; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_sti_ops = { - .read = omap_sti_read, - .write = omap_sti_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_sti_fifo_read(void *opaque, hwaddr addr, - unsigned size) -{ - OMAP_BAD_REG(addr); - return 0; -} - -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; - int ch = addr >> 6; - uint8_t byte = value; - - if (size != 1) { - omap_badwidth_write8(opaque, addr, size); - return; - } - - if (ch == STI_TRACE_CONTROL_CHANNEL) { - /* Flush channel value. */ - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(&s->chr, (const uint8_t *) "\r", 1); - } else if (ch == STI_TRACE_CONSOLE_CHANNEL || 1) { - if (value == 0xc0 || value == 0xc3) { - /* Open channel ch. */ - } else if (value == 0x00) { - qemu_chr_fe_write_all(&s->chr, (const uint8_t *) "\n", 1); - } else { - qemu_chr_fe_write_all(&s->chr, &byte, 1); - } - } -} - -static const MemoryRegionOps omap_sti_fifo_ops = { - .read = omap_sti_fifo_read, - .write = omap_sti_fifo_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, - MemoryRegion *sysmem, - hwaddr channel_base, qemu_irq irq, omap_clk clk, - Chardev *chr) -{ - struct omap_sti_s *s = g_new0(struct omap_sti_s, 1); - - s->irq = irq; - omap_sti_reset(s); - - qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null", NULL), - &error_abort); - - memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - memory_region_init_io(&s->iomem_fifo, NULL, &omap_sti_fifo_ops, s, - "omap.sti.fifo", 0x10000); - memory_region_add_subregion(sysmem, channel_base, &s->iomem_fifo); - - return s; -} - -/* L4 Interconnect */ -#define L4TA(n) (n) -#define L4TAO(n) ((n) + 39) - -static const struct omap_l4_region_s omap_l4_region[125] = { - [ 1] = { 0x40800, 0x800, 32 }, /* Initiator agent */ - [ 2] = { 0x41000, 0x1000, 32 }, /* Link agent */ - [ 0] = { 0x40000, 0x800, 32 }, /* Address and protection */ - [ 3] = { 0x00000, 0x1000, 32 | 16 | 8 }, /* System Control and Pinout */ - [ 4] = { 0x01000, 0x1000, 32 | 16 | 8 }, /* L4TAO1 */ - [ 5] = { 0x04000, 0x1000, 32 | 16 }, /* 32K Timer */ - [ 6] = { 0x05000, 0x1000, 32 | 16 | 8 }, /* L4TAO2 */ - [ 7] = { 0x08000, 0x800, 32 }, /* PRCM Region A */ - [ 8] = { 0x08800, 0x800, 32 }, /* PRCM Region B */ - [ 9] = { 0x09000, 0x1000, 32 | 16 | 8 }, /* L4TAO */ - [ 10] = { 0x12000, 0x1000, 32 | 16 | 8 }, /* Test (BCM) */ - [ 11] = { 0x13000, 0x1000, 32 | 16 | 8 }, /* L4TA1 */ - [ 12] = { 0x14000, 0x1000, 32 }, /* Test/emulation (TAP) */ - [ 13] = { 0x15000, 0x1000, 32 | 16 | 8 }, /* L4TA2 */ - [ 14] = { 0x18000, 0x1000, 32 | 16 | 8 }, /* GPIO1 */ - [ 16] = { 0x1a000, 0x1000, 32 | 16 | 8 }, /* GPIO2 */ - [ 18] = { 0x1c000, 0x1000, 32 | 16 | 8 }, /* GPIO3 */ - [ 19] = { 0x1e000, 0x1000, 32 | 16 | 8 }, /* GPIO4 */ - [ 15] = { 0x19000, 0x1000, 32 | 16 | 8 }, /* Quad GPIO TOP */ - [ 17] = { 0x1b000, 0x1000, 32 | 16 | 8 }, /* L4TA3 */ - [ 20] = { 0x20000, 0x1000, 32 | 16 | 8 }, /* WD Timer 1 (Secure) */ - [ 22] = { 0x22000, 0x1000, 32 | 16 | 8 }, /* WD Timer 2 (OMAP) */ - [ 21] = { 0x21000, 0x1000, 32 | 16 | 8 }, /* Dual WD timer TOP */ - [ 23] = { 0x23000, 0x1000, 32 | 16 | 8 }, /* L4TA4 */ - [ 24] = { 0x28000, 0x1000, 32 | 16 | 8 }, /* GP Timer 1 */ - [ 25] = { 0x29000, 0x1000, 32 | 16 | 8 }, /* L4TA7 */ - [ 26] = { 0x48000, 0x2000, 32 | 16 | 8 }, /* Emulation (ARM11ETB) */ - [ 27] = { 0x4a000, 0x1000, 32 | 16 | 8 }, /* L4TA9 */ - [ 28] = { 0x50000, 0x400, 32 | 16 | 8 }, /* Display top */ - [ 29] = { 0x50400, 0x400, 32 | 16 | 8 }, /* Display control */ - [ 30] = { 0x50800, 0x400, 32 | 16 | 8 }, /* Display RFBI */ - [ 31] = { 0x50c00, 0x400, 32 | 16 | 8 }, /* Display encoder */ - [ 32] = { 0x51000, 0x1000, 32 | 16 | 8 }, /* L4TA10 */ - [ 33] = { 0x52000, 0x400, 32 | 16 | 8 }, /* Camera top */ - [ 34] = { 0x52400, 0x400, 32 | 16 | 8 }, /* Camera core */ - [ 35] = { 0x52800, 0x400, 32 | 16 | 8 }, /* Camera DMA */ - [ 36] = { 0x52c00, 0x400, 32 | 16 | 8 }, /* Camera MMU */ - [ 37] = { 0x53000, 0x1000, 32 | 16 | 8 }, /* L4TA11 */ - [ 38] = { 0x56000, 0x1000, 32 | 16 | 8 }, /* sDMA */ - [ 39] = { 0x57000, 0x1000, 32 | 16 | 8 }, /* L4TA12 */ - [ 40] = { 0x58000, 0x1000, 32 | 16 | 8 }, /* SSI top */ - [ 41] = { 0x59000, 0x1000, 32 | 16 | 8 }, /* SSI GDD */ - [ 42] = { 0x5a000, 0x1000, 32 | 16 | 8 }, /* SSI Port1 */ - [ 43] = { 0x5b000, 0x1000, 32 | 16 | 8 }, /* SSI Port2 */ - [ 44] = { 0x5c000, 0x1000, 32 | 16 | 8 }, /* L4TA13 */ - [ 45] = { 0x5e000, 0x1000, 32 | 16 | 8 }, /* USB OTG */ - [ 46] = { 0x5f000, 0x1000, 32 | 16 | 8 }, /* L4TAO4 */ - [ 47] = { 0x60000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER1SDRC) */ - [ 48] = { 0x61000, 0x1000, 32 | 16 | 8 }, /* L4TA14 */ - [ 49] = { 0x62000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER2GPMC) */ - [ 50] = { 0x63000, 0x1000, 32 | 16 | 8 }, /* L4TA15 */ - [ 51] = { 0x64000, 0x1000, 32 | 16 | 8 }, /* Emulation (WIN_TRACER3OCM) */ - [ 52] = { 0x65000, 0x1000, 32 | 16 | 8 }, /* L4TA16 */ - [ 53] = { 0x66000, 0x300, 32 | 16 | 8 }, /* Emulation (WIN_TRACER4L4) */ - [ 54] = { 0x67000, 0x1000, 32 | 16 | 8 }, /* L4TA17 */ - [ 55] = { 0x68000, 0x1000, 32 | 16 | 8 }, /* Emulation (XTI) */ - [ 56] = { 0x69000, 0x1000, 32 | 16 | 8 }, /* L4TA18 */ - [ 57] = { 0x6a000, 0x1000, 16 | 8 }, /* UART1 */ - [ 58] = { 0x6b000, 0x1000, 32 | 16 | 8 }, /* L4TA19 */ - [ 59] = { 0x6c000, 0x1000, 16 | 8 }, /* UART2 */ - [ 60] = { 0x6d000, 0x1000, 32 | 16 | 8 }, /* L4TA20 */ - [ 61] = { 0x6e000, 0x1000, 16 | 8 }, /* UART3 */ - [ 62] = { 0x6f000, 0x1000, 32 | 16 | 8 }, /* L4TA21 */ - [ 63] = { 0x70000, 0x1000, 16 }, /* I2C1 */ - [ 64] = { 0x71000, 0x1000, 32 | 16 | 8 }, /* L4TAO5 */ - [ 65] = { 0x72000, 0x1000, 16 }, /* I2C2 */ - [ 66] = { 0x73000, 0x1000, 32 | 16 | 8 }, /* L4TAO6 */ - [ 67] = { 0x74000, 0x1000, 16 }, /* McBSP1 */ - [ 68] = { 0x75000, 0x1000, 32 | 16 | 8 }, /* L4TAO7 */ - [ 69] = { 0x76000, 0x1000, 16 }, /* McBSP2 */ - [ 70] = { 0x77000, 0x1000, 32 | 16 | 8 }, /* L4TAO8 */ - [ 71] = { 0x24000, 0x1000, 32 | 16 | 8 }, /* WD Timer 3 (DSP) */ - [ 72] = { 0x25000, 0x1000, 32 | 16 | 8 }, /* L4TA5 */ - [ 73] = { 0x26000, 0x1000, 32 | 16 | 8 }, /* WD Timer 4 (IVA) */ - [ 74] = { 0x27000, 0x1000, 32 | 16 | 8 }, /* L4TA6 */ - [ 75] = { 0x2a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 2 */ - [ 76] = { 0x2b000, 0x1000, 32 | 16 | 8 }, /* L4TA8 */ - [ 77] = { 0x78000, 0x1000, 32 | 16 | 8 }, /* GP Timer 3 */ - [ 78] = { 0x79000, 0x1000, 32 | 16 | 8 }, /* L4TA22 */ - [ 79] = { 0x7a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 4 */ - [ 80] = { 0x7b000, 0x1000, 32 | 16 | 8 }, /* L4TA23 */ - [ 81] = { 0x7c000, 0x1000, 32 | 16 | 8 }, /* GP Timer 5 */ - [ 82] = { 0x7d000, 0x1000, 32 | 16 | 8 }, /* L4TA24 */ - [ 83] = { 0x7e000, 0x1000, 32 | 16 | 8 }, /* GP Timer 6 */ - [ 84] = { 0x7f000, 0x1000, 32 | 16 | 8 }, /* L4TA25 */ - [ 85] = { 0x80000, 0x1000, 32 | 16 | 8 }, /* GP Timer 7 */ - [ 86] = { 0x81000, 0x1000, 32 | 16 | 8 }, /* L4TA26 */ - [ 87] = { 0x82000, 0x1000, 32 | 16 | 8 }, /* GP Timer 8 */ - [ 88] = { 0x83000, 0x1000, 32 | 16 | 8 }, /* L4TA27 */ - [ 89] = { 0x84000, 0x1000, 32 | 16 | 8 }, /* GP Timer 9 */ - [ 90] = { 0x85000, 0x1000, 32 | 16 | 8 }, /* L4TA28 */ - [ 91] = { 0x86000, 0x1000, 32 | 16 | 8 }, /* GP Timer 10 */ - [ 92] = { 0x87000, 0x1000, 32 | 16 | 8 }, /* L4TA29 */ - [ 93] = { 0x88000, 0x1000, 32 | 16 | 8 }, /* GP Timer 11 */ - [ 94] = { 0x89000, 0x1000, 32 | 16 | 8 }, /* L4TA30 */ - [ 95] = { 0x8a000, 0x1000, 32 | 16 | 8 }, /* GP Timer 12 */ - [ 96] = { 0x8b000, 0x1000, 32 | 16 | 8 }, /* L4TA31 */ - [ 97] = { 0x90000, 0x1000, 16 }, /* EAC */ - [ 98] = { 0x91000, 0x1000, 32 | 16 | 8 }, /* L4TA32 */ - [ 99] = { 0x92000, 0x1000, 16 }, /* FAC */ - [100] = { 0x93000, 0x1000, 32 | 16 | 8 }, /* L4TA33 */ - [101] = { 0x94000, 0x1000, 32 | 16 | 8 }, /* IPC (MAILBOX) */ - [102] = { 0x95000, 0x1000, 32 | 16 | 8 }, /* L4TA34 */ - [103] = { 0x98000, 0x1000, 32 | 16 | 8 }, /* SPI1 */ - [104] = { 0x99000, 0x1000, 32 | 16 | 8 }, /* L4TA35 */ - [105] = { 0x9a000, 0x1000, 32 | 16 | 8 }, /* SPI2 */ - [106] = { 0x9b000, 0x1000, 32 | 16 | 8 }, /* L4TA36 */ - [107] = { 0x9c000, 0x1000, 16 | 8 }, /* MMC SDIO */ - [108] = { 0x9d000, 0x1000, 32 | 16 | 8 }, /* L4TAO9 */ - [109] = { 0x9e000, 0x1000, 32 | 16 | 8 }, /* MS_PRO */ - [110] = { 0x9f000, 0x1000, 32 | 16 | 8 }, /* L4TAO10 */ - [111] = { 0xa0000, 0x1000, 32 }, /* RNG */ - [112] = { 0xa1000, 0x1000, 32 | 16 | 8 }, /* L4TAO11 */ - [113] = { 0xa2000, 0x1000, 32 }, /* DES3DES */ - [114] = { 0xa3000, 0x1000, 32 | 16 | 8 }, /* L4TAO12 */ - [115] = { 0xa4000, 0x1000, 32 }, /* SHA1MD5 */ - [116] = { 0xa5000, 0x1000, 32 | 16 | 8 }, /* L4TAO13 */ - [117] = { 0xa6000, 0x1000, 32 }, /* AES */ - [118] = { 0xa7000, 0x1000, 32 | 16 | 8 }, /* L4TA37 */ - [119] = { 0xa8000, 0x2000, 32 }, /* PKA */ - [120] = { 0xaa000, 0x1000, 32 | 16 | 8 }, /* L4TA38 */ - [121] = { 0xb0000, 0x1000, 32 }, /* MG */ - [122] = { 0xb1000, 0x1000, 32 | 16 | 8 }, - [123] = { 0xb2000, 0x1000, 32 }, /* HDQ/1-Wire */ - [124] = { 0xb3000, 0x1000, 32 | 16 | 8 }, /* L4TA39 */ -}; - -static const struct omap_l4_agent_info_s omap_l4_agent_info[54] = { - { 0, 0, 3, 2 }, /* L4IA initiatior agent */ - { L4TAO(1), 3, 2, 1 }, /* Control and pinout module */ - { L4TAO(2), 5, 2, 1 }, /* 32K timer */ - { L4TAO(3), 7, 3, 2 }, /* PRCM */ - { L4TA(1), 10, 2, 1 }, /* BCM */ - { L4TA(2), 12, 2, 1 }, /* Test JTAG */ - { L4TA(3), 14, 6, 3 }, /* Quad GPIO */ - { L4TA(4), 20, 4, 3 }, /* WD timer 1/2 */ - { L4TA(7), 24, 2, 1 }, /* GP timer 1 */ - { L4TA(9), 26, 2, 1 }, /* ATM11 ETB */ - { L4TA(10), 28, 5, 4 }, /* Display subsystem */ - { L4TA(11), 33, 5, 4 }, /* Camera subsystem */ - { L4TA(12), 38, 2, 1 }, /* sDMA */ - { L4TA(13), 40, 5, 4 }, /* SSI */ - { L4TAO(4), 45, 2, 1 }, /* USB */ - { L4TA(14), 47, 2, 1 }, /* Win Tracer1 */ - { L4TA(15), 49, 2, 1 }, /* Win Tracer2 */ - { L4TA(16), 51, 2, 1 }, /* Win Tracer3 */ - { L4TA(17), 53, 2, 1 }, /* Win Tracer4 */ - { L4TA(18), 55, 2, 1 }, /* XTI */ - { L4TA(19), 57, 2, 1 }, /* UART1 */ - { L4TA(20), 59, 2, 1 }, /* UART2 */ - { L4TA(21), 61, 2, 1 }, /* UART3 */ - { L4TAO(5), 63, 2, 1 }, /* I2C1 */ - { L4TAO(6), 65, 2, 1 }, /* I2C2 */ - { L4TAO(7), 67, 2, 1 }, /* McBSP1 */ - { L4TAO(8), 69, 2, 1 }, /* McBSP2 */ - { L4TA(5), 71, 2, 1 }, /* WD Timer 3 (DSP) */ - { L4TA(6), 73, 2, 1 }, /* WD Timer 4 (IVA) */ - { L4TA(8), 75, 2, 1 }, /* GP Timer 2 */ - { L4TA(22), 77, 2, 1 }, /* GP Timer 3 */ - { L4TA(23), 79, 2, 1 }, /* GP Timer 4 */ - { L4TA(24), 81, 2, 1 }, /* GP Timer 5 */ - { L4TA(25), 83, 2, 1 }, /* GP Timer 6 */ - { L4TA(26), 85, 2, 1 }, /* GP Timer 7 */ - { L4TA(27), 87, 2, 1 }, /* GP Timer 8 */ - { L4TA(28), 89, 2, 1 }, /* GP Timer 9 */ - { L4TA(29), 91, 2, 1 }, /* GP Timer 10 */ - { L4TA(30), 93, 2, 1 }, /* GP Timer 11 */ - { L4TA(31), 95, 2, 1 }, /* GP Timer 12 */ - { L4TA(32), 97, 2, 1 }, /* EAC */ - { L4TA(33), 99, 2, 1 }, /* FAC */ - { L4TA(34), 101, 2, 1 }, /* IPC */ - { L4TA(35), 103, 2, 1 }, /* SPI1 */ - { L4TA(36), 105, 2, 1 }, /* SPI2 */ - { L4TAO(9), 107, 2, 1 }, /* MMC SDIO */ - { L4TAO(10), 109, 2, 1 }, - { L4TAO(11), 111, 2, 1 }, /* RNG */ - { L4TAO(12), 113, 2, 1 }, /* DES3DES */ - { L4TAO(13), 115, 2, 1 }, /* SHA1MD5 */ - { L4TA(37), 117, 2, 1 }, /* AES */ - { L4TA(38), 119, 2, 1 }, /* PKA */ - { -1, 121, 2, 1 }, - { L4TA(39), 123, 2, 1 }, /* HDQ/1-Wire */ -}; - -#define omap_l4ta(bus, cs) \ - omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TA(cs)) -#define omap_l4tao(bus, cs) \ - omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TAO(cs)) - -/* Power, Reset, and Clock Management */ -struct omap_prcm_s { - qemu_irq irq[3]; - struct omap_mpu_state_s *mpu; - MemoryRegion iomem0; - MemoryRegion iomem1; - - uint32_t irqst[3]; - uint32_t irqen[3]; - - uint32_t sysconfig; - uint32_t voltctrl; - uint32_t scratch[20]; - - uint32_t clksrc[1]; - uint32_t clkout[1]; - uint32_t clkemul[1]; - uint32_t clkpol[1]; - uint32_t clksel[8]; - uint32_t clken[12]; - uint32_t clkctrl[4]; - uint32_t clkidle[7]; - uint32_t setuptime[2]; - - uint32_t wkup[3]; - uint32_t wken[3]; - uint32_t wkst[3]; - uint32_t rst[4]; - uint32_t rstctrl[1]; - uint32_t power[4]; - uint32_t rsttime_wkup; - - uint32_t ev; - uint32_t evtime[2]; - - int dpll_lock, apll_lock[2]; -}; - -static void omap_prcm_int_update(struct omap_prcm_s *s, int dom) -{ - qemu_set_irq(s->irq[dom], s->irqst[dom] & s->irqen[dom]); - /* XXX or is the mask applied before PRCM_IRQSTATUS_* ? */ -} - -static uint64_t omap_prcm_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; - uint32_t ret; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* PRCM_REVISION */ - return 0x10; - - case 0x010: /* PRCM_SYSCONFIG */ - return s->sysconfig; - - case 0x018: /* PRCM_IRQSTATUS_MPU */ - return s->irqst[0]; - - case 0x01c: /* PRCM_IRQENABLE_MPU */ - return s->irqen[0]; - - case 0x050: /* PRCM_VOLTCTRL */ - return s->voltctrl; - case 0x054: /* PRCM_VOLTST */ - return s->voltctrl & 3; - - case 0x060: /* PRCM_CLKSRC_CTRL */ - return s->clksrc[0]; - case 0x070: /* PRCM_CLKOUT_CTRL */ - return s->clkout[0]; - case 0x078: /* PRCM_CLKEMUL_CTRL */ - return s->clkemul[0]; - case 0x080: /* PRCM_CLKCFG_CTRL */ - case 0x084: /* PRCM_CLKCFG_STATUS */ - return 0; - - case 0x090: /* PRCM_VOLTSETUP */ - return s->setuptime[0]; - - case 0x094: /* PRCM_CLKSSETUP */ - return s->setuptime[1]; - - case 0x098: /* PRCM_POLCTRL */ - return s->clkpol[0]; - - case 0x0b0: /* GENERAL_PURPOSE1 */ - case 0x0b4: /* GENERAL_PURPOSE2 */ - case 0x0b8: /* GENERAL_PURPOSE3 */ - case 0x0bc: /* GENERAL_PURPOSE4 */ - case 0x0c0: /* GENERAL_PURPOSE5 */ - case 0x0c4: /* GENERAL_PURPOSE6 */ - case 0x0c8: /* GENERAL_PURPOSE7 */ - case 0x0cc: /* GENERAL_PURPOSE8 */ - case 0x0d0: /* GENERAL_PURPOSE9 */ - case 0x0d4: /* GENERAL_PURPOSE10 */ - case 0x0d8: /* GENERAL_PURPOSE11 */ - case 0x0dc: /* GENERAL_PURPOSE12 */ - case 0x0e0: /* GENERAL_PURPOSE13 */ - case 0x0e4: /* GENERAL_PURPOSE14 */ - case 0x0e8: /* GENERAL_PURPOSE15 */ - case 0x0ec: /* GENERAL_PURPOSE16 */ - case 0x0f0: /* GENERAL_PURPOSE17 */ - case 0x0f4: /* GENERAL_PURPOSE18 */ - case 0x0f8: /* GENERAL_PURPOSE19 */ - case 0x0fc: /* GENERAL_PURPOSE20 */ - return s->scratch[(addr - 0xb0) >> 2]; - - case 0x140: /* CM_CLKSEL_MPU */ - return s->clksel[0]; - case 0x148: /* CM_CLKSTCTRL_MPU */ - return s->clkctrl[0]; - - case 0x158: /* RM_RSTST_MPU */ - return s->rst[0]; - case 0x1c8: /* PM_WKDEP_MPU */ - return s->wkup[0]; - case 0x1d4: /* PM_EVGENCTRL_MPU */ - return s->ev; - case 0x1d8: /* PM_EVEGENONTIM_MPU */ - return s->evtime[0]; - case 0x1dc: /* PM_EVEGENOFFTIM_MPU */ - return s->evtime[1]; - case 0x1e0: /* PM_PWSTCTRL_MPU */ - return s->power[0]; - case 0x1e4: /* PM_PWSTST_MPU */ - return 0; - - case 0x200: /* CM_FCLKEN1_CORE */ - return s->clken[0]; - case 0x204: /* CM_FCLKEN2_CORE */ - return s->clken[1]; - case 0x210: /* CM_ICLKEN1_CORE */ - return s->clken[2]; - case 0x214: /* CM_ICLKEN2_CORE */ - return s->clken[3]; - case 0x21c: /* CM_ICLKEN4_CORE */ - return s->clken[4]; - - case 0x220: /* CM_IDLEST1_CORE */ - /* TODO: check the actual iclk status */ - return 0x7ffffff9; - case 0x224: /* CM_IDLEST2_CORE */ - /* TODO: check the actual iclk status */ - return 0x00000007; - case 0x22c: /* CM_IDLEST4_CORE */ - /* TODO: check the actual iclk status */ - return 0x0000001f; - - case 0x230: /* CM_AUTOIDLE1_CORE */ - return s->clkidle[0]; - case 0x234: /* CM_AUTOIDLE2_CORE */ - return s->clkidle[1]; - case 0x238: /* CM_AUTOIDLE3_CORE */ - return s->clkidle[2]; - case 0x23c: /* CM_AUTOIDLE4_CORE */ - return s->clkidle[3]; - - case 0x240: /* CM_CLKSEL1_CORE */ - return s->clksel[1]; - case 0x244: /* CM_CLKSEL2_CORE */ - return s->clksel[2]; - - case 0x248: /* CM_CLKSTCTRL_CORE */ - return s->clkctrl[1]; - - case 0x2a0: /* PM_WKEN1_CORE */ - return s->wken[0]; - case 0x2a4: /* PM_WKEN2_CORE */ - return s->wken[1]; - - case 0x2b0: /* PM_WKST1_CORE */ - return s->wkst[0]; - case 0x2b4: /* PM_WKST2_CORE */ - return s->wkst[1]; - case 0x2c8: /* PM_WKDEP_CORE */ - return 0x1e; - - case 0x2e0: /* PM_PWSTCTRL_CORE */ - return s->power[1]; - case 0x2e4: /* PM_PWSTST_CORE */ - return 0x000030 | (s->power[1] & 0xfc00); - - case 0x300: /* CM_FCLKEN_GFX */ - return s->clken[5]; - case 0x310: /* CM_ICLKEN_GFX */ - return s->clken[6]; - case 0x320: /* CM_IDLEST_GFX */ - /* TODO: check the actual iclk status */ - return 0x00000001; - case 0x340: /* CM_CLKSEL_GFX */ - return s->clksel[3]; - case 0x348: /* CM_CLKSTCTRL_GFX */ - return s->clkctrl[2]; - case 0x350: /* RM_RSTCTRL_GFX */ - return s->rstctrl[0]; - case 0x358: /* RM_RSTST_GFX */ - return s->rst[1]; - case 0x3c8: /* PM_WKDEP_GFX */ - return s->wkup[1]; - - case 0x3e0: /* PM_PWSTCTRL_GFX */ - return s->power[2]; - case 0x3e4: /* PM_PWSTST_GFX */ - return s->power[2] & 3; - - case 0x400: /* CM_FCLKEN_WKUP */ - return s->clken[7]; - case 0x410: /* CM_ICLKEN_WKUP */ - return s->clken[8]; - case 0x420: /* CM_IDLEST_WKUP */ - /* TODO: check the actual iclk status */ - return 0x0000003f; - case 0x430: /* CM_AUTOIDLE_WKUP */ - return s->clkidle[4]; - case 0x440: /* CM_CLKSEL_WKUP */ - return s->clksel[4]; - case 0x450: /* RM_RSTCTRL_WKUP */ - return 0; - case 0x454: /* RM_RSTTIME_WKUP */ - return s->rsttime_wkup; - case 0x458: /* RM_RSTST_WKUP */ - return s->rst[2]; - case 0x4a0: /* PM_WKEN_WKUP */ - return s->wken[2]; - case 0x4b0: /* PM_WKST_WKUP */ - return s->wkst[2]; - - case 0x500: /* CM_CLKEN_PLL */ - return s->clken[9]; - case 0x520: /* CM_IDLEST_CKGEN */ - ret = 0x0000070 | (s->apll_lock[0] << 9) | (s->apll_lock[1] << 8); - if (!(s->clksel[6] & 3)) - /* Core uses 32-kHz clock */ - ret |= 3 << 0; - else if (!s->dpll_lock) - /* DPLL not locked, core uses ref_clk */ - ret |= 1 << 0; - else - /* Core uses DPLL */ - ret |= 2 << 0; - return ret; - case 0x530: /* CM_AUTOIDLE_PLL */ - return s->clkidle[5]; - case 0x540: /* CM_CLKSEL1_PLL */ - return s->clksel[5]; - case 0x544: /* CM_CLKSEL2_PLL */ - return s->clksel[6]; - - case 0x800: /* CM_FCLKEN_DSP */ - return s->clken[10]; - case 0x810: /* CM_ICLKEN_DSP */ - return s->clken[11]; - case 0x820: /* CM_IDLEST_DSP */ - /* TODO: check the actual iclk status */ - return 0x00000103; - case 0x830: /* CM_AUTOIDLE_DSP */ - return s->clkidle[6]; - case 0x840: /* CM_CLKSEL_DSP */ - return s->clksel[7]; - case 0x848: /* CM_CLKSTCTRL_DSP */ - return s->clkctrl[3]; - case 0x850: /* RM_RSTCTRL_DSP */ - return 0; - case 0x858: /* RM_RSTST_DSP */ - return s->rst[3]; - case 0x8c8: /* PM_WKDEP_DSP */ - return s->wkup[2]; - case 0x8e0: /* PM_PWSTCTRL_DSP */ - return s->power[3]; - case 0x8e4: /* PM_PWSTST_DSP */ - return 0x008030 | (s->power[3] & 0x3003); - - case 0x8f0: /* PRCM_IRQSTATUS_DSP */ - return s->irqst[1]; - case 0x8f4: /* PRCM_IRQENABLE_DSP */ - return s->irqen[1]; - - case 0x8f8: /* PRCM_IRQSTATUS_IVA */ - return s->irqst[2]; - case 0x8fc: /* PRCM_IRQENABLE_IVA */ - return s->irqen[2]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_prcm_apll_update(struct omap_prcm_s *s) -{ - int mode[2]; - - mode[0] = (s->clken[9] >> 6) & 3; - s->apll_lock[0] = (mode[0] == 3); - mode[1] = (s->clken[9] >> 2) & 3; - s->apll_lock[1] = (mode[1] == 3); - /* TODO: update clocks */ - - if (mode[0] == 1 || mode[0] == 2 || mode[1] == 1 || mode[1] == 2) - fprintf(stderr, "%s: bad EN_54M_PLL or bad EN_96M_PLL\n", - __func__); -} - -static void omap_prcm_dpll_update(struct omap_prcm_s *s) -{ - omap_clk dpll = omap_findclk(s->mpu, "dpll"); - omap_clk dpll_x2 = omap_findclk(s->mpu, "dpll"); - omap_clk core = omap_findclk(s->mpu, "core_clk"); - int mode = (s->clken[9] >> 0) & 3; - int mult, div; - - mult = (s->clksel[5] >> 12) & 0x3ff; - div = (s->clksel[5] >> 8) & 0xf; - if (mult == 0 || mult == 1) - mode = 1; /* Bypass */ - - s->dpll_lock = 0; - switch (mode) { - case 0: - fprintf(stderr, "%s: bad EN_DPLL\n", __func__); - break; - case 1: /* Low-power bypass mode (Default) */ - case 2: /* Fast-relock bypass mode */ - omap_clk_setrate(dpll, 1, 1); - omap_clk_setrate(dpll_x2, 1, 1); - break; - case 3: /* Lock mode */ - s->dpll_lock = 1; /* After 20 FINT cycles (ref_clk / (div + 1)). */ - - omap_clk_setrate(dpll, div + 1, mult); - omap_clk_setrate(dpll_x2, div + 1, mult * 2); - break; - } - - switch ((s->clksel[6] >> 0) & 3) { - case 0: - omap_clk_reparent(core, omap_findclk(s->mpu, "clk32-kHz")); - break; - case 1: - omap_clk_reparent(core, dpll); - break; - case 2: - /* Default */ - omap_clk_reparent(core, dpll_x2); - break; - case 3: - fprintf(stderr, "%s: bad CORE_CLK_SRC\n", __func__); - break; - } -} - -static void omap_prcm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_prcm_s *s = (struct omap_prcm_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x000: /* PRCM_REVISION */ - case 0x054: /* PRCM_VOLTST */ - case 0x084: /* PRCM_CLKCFG_STATUS */ - case 0x1e4: /* PM_PWSTST_MPU */ - case 0x220: /* CM_IDLEST1_CORE */ - case 0x224: /* CM_IDLEST2_CORE */ - case 0x22c: /* CM_IDLEST4_CORE */ - case 0x2c8: /* PM_WKDEP_CORE */ - case 0x2e4: /* PM_PWSTST_CORE */ - case 0x320: /* CM_IDLEST_GFX */ - case 0x3e4: /* PM_PWSTST_GFX */ - case 0x420: /* CM_IDLEST_WKUP */ - case 0x520: /* CM_IDLEST_CKGEN */ - case 0x820: /* CM_IDLEST_DSP */ - case 0x8e4: /* PM_PWSTST_DSP */ - OMAP_RO_REG(addr); - return; - - case 0x010: /* PRCM_SYSCONFIG */ - s->sysconfig = value & 1; - break; - - case 0x018: /* PRCM_IRQSTATUS_MPU */ - s->irqst[0] &= ~value; - omap_prcm_int_update(s, 0); - break; - case 0x01c: /* PRCM_IRQENABLE_MPU */ - s->irqen[0] = value & 0x3f; - omap_prcm_int_update(s, 0); - break; - - case 0x050: /* PRCM_VOLTCTRL */ - s->voltctrl = value & 0xf1c3; - break; - - case 0x060: /* PRCM_CLKSRC_CTRL */ - s->clksrc[0] = value & 0xdb; - /* TODO update clocks */ - break; - - case 0x070: /* PRCM_CLKOUT_CTRL */ - s->clkout[0] = value & 0xbbbb; - /* TODO update clocks */ - break; - - case 0x078: /* PRCM_CLKEMUL_CTRL */ - s->clkemul[0] = value & 1; - /* TODO update clocks */ - break; - - case 0x080: /* PRCM_CLKCFG_CTRL */ - break; - - case 0x090: /* PRCM_VOLTSETUP */ - s->setuptime[0] = value & 0xffff; - break; - case 0x094: /* PRCM_CLKSSETUP */ - s->setuptime[1] = value & 0xffff; - break; - - case 0x098: /* PRCM_POLCTRL */ - s->clkpol[0] = value & 0x701; - break; - - case 0x0b0: /* GENERAL_PURPOSE1 */ - case 0x0b4: /* GENERAL_PURPOSE2 */ - case 0x0b8: /* GENERAL_PURPOSE3 */ - case 0x0bc: /* GENERAL_PURPOSE4 */ - case 0x0c0: /* GENERAL_PURPOSE5 */ - case 0x0c4: /* GENERAL_PURPOSE6 */ - case 0x0c8: /* GENERAL_PURPOSE7 */ - case 0x0cc: /* GENERAL_PURPOSE8 */ - case 0x0d0: /* GENERAL_PURPOSE9 */ - case 0x0d4: /* GENERAL_PURPOSE10 */ - case 0x0d8: /* GENERAL_PURPOSE11 */ - case 0x0dc: /* GENERAL_PURPOSE12 */ - case 0x0e0: /* GENERAL_PURPOSE13 */ - case 0x0e4: /* GENERAL_PURPOSE14 */ - case 0x0e8: /* GENERAL_PURPOSE15 */ - case 0x0ec: /* GENERAL_PURPOSE16 */ - case 0x0f0: /* GENERAL_PURPOSE17 */ - case 0x0f4: /* GENERAL_PURPOSE18 */ - case 0x0f8: /* GENERAL_PURPOSE19 */ - case 0x0fc: /* GENERAL_PURPOSE20 */ - s->scratch[(addr - 0xb0) >> 2] = value; - break; - - case 0x140: /* CM_CLKSEL_MPU */ - s->clksel[0] = value & 0x1f; - /* TODO update clocks */ - break; - case 0x148: /* CM_CLKSTCTRL_MPU */ - s->clkctrl[0] = value & 0x1f; - break; - - case 0x158: /* RM_RSTST_MPU */ - s->rst[0] &= ~value; - break; - case 0x1c8: /* PM_WKDEP_MPU */ - s->wkup[0] = value & 0x15; - break; - - case 0x1d4: /* PM_EVGENCTRL_MPU */ - s->ev = value & 0x1f; - break; - case 0x1d8: /* PM_EVEGENONTIM_MPU */ - s->evtime[0] = value; - break; - case 0x1dc: /* PM_EVEGENOFFTIM_MPU */ - s->evtime[1] = value; - break; - - case 0x1e0: /* PM_PWSTCTRL_MPU */ - s->power[0] = value & 0xc0f; - break; - - case 0x200: /* CM_FCLKEN1_CORE */ - s->clken[0] = value & 0xbfffffff; - /* TODO update clocks */ - /* The EN_EAC bit only gets/puts func_96m_clk. */ - break; - case 0x204: /* CM_FCLKEN2_CORE */ - s->clken[1] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x210: /* CM_ICLKEN1_CORE */ - s->clken[2] = value & 0xfffffff9; - /* TODO update clocks */ - /* The EN_EAC bit only gets/puts core_l4_iclk. */ - break; - case 0x214: /* CM_ICLKEN2_CORE */ - s->clken[3] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x21c: /* CM_ICLKEN4_CORE */ - s->clken[4] = value & 0x0000001f; - /* TODO update clocks */ - break; - - case 0x230: /* CM_AUTOIDLE1_CORE */ - s->clkidle[0] = value & 0xfffffff9; - /* TODO update clocks */ - break; - case 0x234: /* CM_AUTOIDLE2_CORE */ - s->clkidle[1] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x238: /* CM_AUTOIDLE3_CORE */ - s->clkidle[2] = value & 0x00000007; - /* TODO update clocks */ - break; - case 0x23c: /* CM_AUTOIDLE4_CORE */ - s->clkidle[3] = value & 0x0000001f; - /* TODO update clocks */ - break; - - case 0x240: /* CM_CLKSEL1_CORE */ - s->clksel[1] = value & 0x0fffbf7f; - /* TODO update clocks */ - break; - - case 0x244: /* CM_CLKSEL2_CORE */ - s->clksel[2] = value & 0x00fffffc; - /* TODO update clocks */ - break; - - case 0x248: /* CM_CLKSTCTRL_CORE */ - s->clkctrl[1] = value & 0x7; - break; - - case 0x2a0: /* PM_WKEN1_CORE */ - s->wken[0] = value & 0x04667ff8; - break; - case 0x2a4: /* PM_WKEN2_CORE */ - s->wken[1] = value & 0x00000005; - break; - - case 0x2b0: /* PM_WKST1_CORE */ - s->wkst[0] &= ~value; - break; - case 0x2b4: /* PM_WKST2_CORE */ - s->wkst[1] &= ~value; - break; - - case 0x2e0: /* PM_PWSTCTRL_CORE */ - s->power[1] = (value & 0x00fc3f) | (1 << 2); - break; - - case 0x300: /* CM_FCLKEN_GFX */ - s->clken[5] = value & 6; - /* TODO update clocks */ - break; - case 0x310: /* CM_ICLKEN_GFX */ - s->clken[6] = value & 1; - /* TODO update clocks */ - break; - case 0x340: /* CM_CLKSEL_GFX */ - s->clksel[3] = value & 7; - /* TODO update clocks */ - break; - case 0x348: /* CM_CLKSTCTRL_GFX */ - s->clkctrl[2] = value & 1; - break; - case 0x350: /* RM_RSTCTRL_GFX */ - s->rstctrl[0] = value & 1; - /* TODO: reset */ - break; - case 0x358: /* RM_RSTST_GFX */ - s->rst[1] &= ~value; - break; - case 0x3c8: /* PM_WKDEP_GFX */ - s->wkup[1] = value & 0x13; - break; - case 0x3e0: /* PM_PWSTCTRL_GFX */ - s->power[2] = (value & 0x00c0f) | (3 << 2); - break; - - case 0x400: /* CM_FCLKEN_WKUP */ - s->clken[7] = value & 0xd; - /* TODO update clocks */ - break; - case 0x410: /* CM_ICLKEN_WKUP */ - s->clken[8] = value & 0x3f; - /* TODO update clocks */ - break; - case 0x430: /* CM_AUTOIDLE_WKUP */ - s->clkidle[4] = value & 0x0000003f; - /* TODO update clocks */ - break; - case 0x440: /* CM_CLKSEL_WKUP */ - s->clksel[4] = value & 3; - /* TODO update clocks */ - break; - case 0x450: /* RM_RSTCTRL_WKUP */ - /* TODO: reset */ - if (value & 2) - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - break; - case 0x454: /* RM_RSTTIME_WKUP */ - s->rsttime_wkup = value & 0x1fff; - break; - case 0x458: /* RM_RSTST_WKUP */ - s->rst[2] &= ~value; - break; - case 0x4a0: /* PM_WKEN_WKUP */ - s->wken[2] = value & 0x00000005; - break; - case 0x4b0: /* PM_WKST_WKUP */ - s->wkst[2] &= ~value; - break; - - case 0x500: /* CM_CLKEN_PLL */ - if (value & 0xffffff30) - fprintf(stderr, "%s: write 0s in CM_CLKEN_PLL for " - "future compatibility\n", __func__); - if ((s->clken[9] ^ value) & 0xcc) { - s->clken[9] &= ~0xcc; - s->clken[9] |= value & 0xcc; - omap_prcm_apll_update(s); - } - if ((s->clken[9] ^ value) & 3) { - s->clken[9] &= ~3; - s->clken[9] |= value & 3; - omap_prcm_dpll_update(s); - } - break; - case 0x530: /* CM_AUTOIDLE_PLL */ - s->clkidle[5] = value & 0x000000cf; - /* TODO update clocks */ - break; - case 0x540: /* CM_CLKSEL1_PLL */ - if (value & 0xfc4000d7) - fprintf(stderr, "%s: write 0s in CM_CLKSEL1_PLL for " - "future compatibility\n", __func__); - if ((s->clksel[5] ^ value) & 0x003fff00) { - s->clksel[5] = value & 0x03bfff28; - omap_prcm_dpll_update(s); - } - /* TODO update the other clocks */ - - s->clksel[5] = value & 0x03bfff28; - break; - case 0x544: /* CM_CLKSEL2_PLL */ - if (value & ~3) - fprintf(stderr, "%s: write 0s in CM_CLKSEL2_PLL[31:2] for " - "future compatibility\n", __func__); - if (s->clksel[6] != (value & 3)) { - s->clksel[6] = value & 3; - omap_prcm_dpll_update(s); - } - break; - - case 0x800: /* CM_FCLKEN_DSP */ - s->clken[10] = value & 0x501; - /* TODO update clocks */ - break; - case 0x810: /* CM_ICLKEN_DSP */ - s->clken[11] = value & 0x2; - /* TODO update clocks */ - break; - case 0x830: /* CM_AUTOIDLE_DSP */ - s->clkidle[6] = value & 0x2; - /* TODO update clocks */ - break; - case 0x840: /* CM_CLKSEL_DSP */ - s->clksel[7] = value & 0x3fff; - /* TODO update clocks */ - break; - case 0x848: /* CM_CLKSTCTRL_DSP */ - s->clkctrl[3] = value & 0x101; - break; - case 0x850: /* RM_RSTCTRL_DSP */ - /* TODO: reset */ - break; - case 0x858: /* RM_RSTST_DSP */ - s->rst[3] &= ~value; - break; - case 0x8c8: /* PM_WKDEP_DSP */ - s->wkup[2] = value & 0x13; - break; - case 0x8e0: /* PM_PWSTCTRL_DSP */ - s->power[3] = (value & 0x03017) | (3 << 2); - break; - - case 0x8f0: /* PRCM_IRQSTATUS_DSP */ - s->irqst[1] &= ~value; - omap_prcm_int_update(s, 1); - break; - case 0x8f4: /* PRCM_IRQENABLE_DSP */ - s->irqen[1] = value & 0x7; - omap_prcm_int_update(s, 1); - break; - - case 0x8f8: /* PRCM_IRQSTATUS_IVA */ - s->irqst[2] &= ~value; - omap_prcm_int_update(s, 2); - break; - case 0x8fc: /* PRCM_IRQENABLE_IVA */ - s->irqen[2] = value & 0x7; - omap_prcm_int_update(s, 2); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_prcm_ops = { - .read = omap_prcm_read, - .write = omap_prcm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_prcm_reset(struct omap_prcm_s *s) -{ - s->sysconfig = 0; - s->irqst[0] = 0; - s->irqst[1] = 0; - s->irqst[2] = 0; - s->irqen[0] = 0; - s->irqen[1] = 0; - s->irqen[2] = 0; - s->voltctrl = 0x1040; - s->ev = 0x14; - s->evtime[0] = 0; - s->evtime[1] = 0; - s->clkctrl[0] = 0; - s->clkctrl[1] = 0; - s->clkctrl[2] = 0; - s->clkctrl[3] = 0; - s->clken[1] = 7; - s->clken[3] = 7; - s->clken[4] = 0; - s->clken[5] = 0; - s->clken[6] = 0; - s->clken[7] = 0xc; - s->clken[8] = 0x3e; - s->clken[9] = 0x0d; - s->clken[10] = 0; - s->clken[11] = 0; - s->clkidle[0] = 0; - s->clkidle[2] = 7; - s->clkidle[3] = 0; - s->clkidle[4] = 0; - s->clkidle[5] = 0x0c; - s->clkidle[6] = 0; - s->clksel[0] = 0x01; - s->clksel[1] = 0x02100121; - s->clksel[2] = 0x00000000; - s->clksel[3] = 0x01; - s->clksel[4] = 0; - s->clksel[7] = 0x0121; - s->wkup[0] = 0x15; - s->wkup[1] = 0x13; - s->wkup[2] = 0x13; - s->wken[0] = 0x04667ff8; - s->wken[1] = 0x00000005; - s->wken[2] = 5; - s->wkst[0] = 0; - s->wkst[1] = 0; - s->wkst[2] = 0; - s->power[0] = 0x00c; - s->power[1] = 4; - s->power[2] = 0x0000c; - s->power[3] = 0x14; - s->rstctrl[0] = 1; - s->rst[3] = 1; - omap_prcm_apll_update(s); - omap_prcm_dpll_update(s); -} - -static void omap_prcm_coldreset(struct omap_prcm_s *s) -{ - s->setuptime[0] = 0; - s->setuptime[1] = 0; - memset(&s->scratch, 0, sizeof(s->scratch)); - s->rst[0] = 0x01; - s->rst[1] = 0x00; - s->rst[2] = 0x01; - s->clken[0] = 0; - s->clken[2] = 0; - s->clkidle[1] = 0; - s->clksel[5] = 0; - s->clksel[6] = 2; - s->clksrc[0] = 0x43; - s->clkout[0] = 0x0303; - s->clkemul[0] = 0; - s->clkpol[0] = 0x100; - s->rsttime_wkup = 0x1002; - - omap_prcm_reset(s); -} - -static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta, - qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int, - struct omap_mpu_state_s *mpu) -{ - struct omap_prcm_s *s = g_new0(struct omap_prcm_s, 1); - - s->irq[0] = mpu_int; - s->irq[1] = dsp_int; - s->irq[2] = iva_int; - s->mpu = mpu; - omap_prcm_coldreset(s); - - memory_region_init_io(&s->iomem0, NULL, &omap_prcm_ops, s, "omap.pcrm0", - omap_l4_region_size(ta, 0)); - memory_region_init_io(&s->iomem1, NULL, &omap_prcm_ops, s, "omap.pcrm1", - omap_l4_region_size(ta, 1)); - omap_l4_attach(ta, 0, &s->iomem0); - omap_l4_attach(ta, 1, &s->iomem1); - - return s; -} - -/* System and Pinout control */ -struct omap_sysctl_s { - struct omap_mpu_state_s *mpu; - MemoryRegion iomem; - - uint32_t sysconfig; - uint32_t devconfig; - uint32_t psaconfig; - uint32_t padconf[0x45]; - uint8_t obs; - uint32_t msuspendmux[5]; -}; - -static uint32_t omap_sysctl_read8(void *opaque, hwaddr addr) -{ - - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - int pad_offset, byte_offset; - int value; - - switch (addr) { - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - pad_offset = (addr - 0x30) >> 2; - byte_offset = (addr - 0x30) & (4 - 1); - - value = s->padconf[pad_offset]; - value = (value >> (byte_offset * 8)) & 0xff; - - return value; - - default: - break; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_sysctl_read(void *opaque, hwaddr addr) -{ - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - - switch (addr) { - case 0x000: /* CONTROL_REVISION */ - return 0x20; - - case 0x010: /* CONTROL_SYSCONFIG */ - return s->sysconfig; - - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - return s->padconf[(addr - 0x30) >> 2]; - - case 0x270: /* CONTROL_DEBOBS */ - return s->obs; - - case 0x274: /* CONTROL_DEVCONF */ - return s->devconfig; - - case 0x28c: /* CONTROL_EMU_SUPPORT */ - return 0; - - case 0x290: /* CONTROL_MSUSPENDMUX_0 */ - return s->msuspendmux[0]; - case 0x294: /* CONTROL_MSUSPENDMUX_1 */ - return s->msuspendmux[1]; - case 0x298: /* CONTROL_MSUSPENDMUX_2 */ - return s->msuspendmux[2]; - case 0x29c: /* CONTROL_MSUSPENDMUX_3 */ - return s->msuspendmux[3]; - case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */ - return s->msuspendmux[4]; - case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */ - return 0; - - case 0x2b8: /* CONTROL_PSA_CTRL */ - return s->psaconfig; - case 0x2bc: /* CONTROL_PSA_CMD */ - case 0x2c0: /* CONTROL_PSA_VALUE */ - return 0; - - case 0x2b0: /* CONTROL_SEC_CTRL */ - return 0x800000f1; - case 0x2d0: /* CONTROL_SEC_EMU */ - return 0x80000015; - case 0x2d4: /* CONTROL_SEC_TAP */ - return 0x8000007f; - case 0x2b4: /* CONTROL_SEC_TEST */ - case 0x2f0: /* CONTROL_SEC_STATUS */ - case 0x2f4: /* CONTROL_SEC_ERR_STATUS */ - /* Secure mode is not present on general-pusrpose device. Outside - * secure mode these values cannot be read or written. */ - return 0; - - case 0x2d8: /* CONTROL_OCM_RAM_PERM */ - return 0xff; - case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */ - case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */ - case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */ - /* No secure mode so no Extended Secure RAM present. */ - return 0; - - case 0x2f8: /* CONTROL_STATUS */ - /* Device Type => General-purpose */ - return 0x0300; - case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */ - - case 0x300: /* CONTROL_RPUB_KEY_H_0 */ - case 0x304: /* CONTROL_RPUB_KEY_H_1 */ - case 0x308: /* CONTROL_RPUB_KEY_H_2 */ - case 0x30c: /* CONTROL_RPUB_KEY_H_3 */ - return 0xdecafbad; - - case 0x310: /* CONTROL_RAND_KEY_0 */ - case 0x314: /* CONTROL_RAND_KEY_1 */ - case 0x318: /* CONTROL_RAND_KEY_2 */ - case 0x31c: /* CONTROL_RAND_KEY_3 */ - case 0x320: /* CONTROL_CUST_KEY_0 */ - case 0x324: /* CONTROL_CUST_KEY_1 */ - case 0x330: /* CONTROL_TEST_KEY_0 */ - case 0x334: /* CONTROL_TEST_KEY_1 */ - case 0x338: /* CONTROL_TEST_KEY_2 */ - case 0x33c: /* CONTROL_TEST_KEY_3 */ - case 0x340: /* CONTROL_TEST_KEY_4 */ - case 0x344: /* CONTROL_TEST_KEY_5 */ - case 0x348: /* CONTROL_TEST_KEY_6 */ - case 0x34c: /* CONTROL_TEST_KEY_7 */ - case 0x350: /* CONTROL_TEST_KEY_8 */ - case 0x354: /* CONTROL_TEST_KEY_9 */ - /* Can only be accessed in secure mode and when C_FieldAccEnable - * bit is set in CONTROL_SEC_CTRL. - * TODO: otherwise an interconnect access error is generated. */ - return 0; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sysctl_write8(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - int pad_offset, byte_offset; - int prev_value; - - switch (addr) { - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - pad_offset = (addr - 0x30) >> 2; - byte_offset = (addr - 0x30) & (4 - 1); - - prev_value = s->padconf[pad_offset]; - prev_value &= ~(0xff << (byte_offset * 8)); - prev_value |= ((value & 0x1f1f1f1f) << (byte_offset * 8)) & 0x1f1f1f1f; - s->padconf[pad_offset] = prev_value; - break; - - default: - OMAP_BAD_REG(addr); - break; - } -} - -static void omap_sysctl_write(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap_sysctl_s *s = (struct omap_sysctl_s *) opaque; - - switch (addr) { - case 0x000: /* CONTROL_REVISION */ - case 0x2a4: /* CONTROL_MSUSPENDMUX_5 */ - case 0x2c0: /* CONTROL_PSA_VALUE */ - case 0x2f8: /* CONTROL_STATUS */ - case 0x2fc: /* CONTROL_GENERAL_PURPOSE_STATUS */ - case 0x300: /* CONTROL_RPUB_KEY_H_0 */ - case 0x304: /* CONTROL_RPUB_KEY_H_1 */ - case 0x308: /* CONTROL_RPUB_KEY_H_2 */ - case 0x30c: /* CONTROL_RPUB_KEY_H_3 */ - case 0x310: /* CONTROL_RAND_KEY_0 */ - case 0x314: /* CONTROL_RAND_KEY_1 */ - case 0x318: /* CONTROL_RAND_KEY_2 */ - case 0x31c: /* CONTROL_RAND_KEY_3 */ - case 0x320: /* CONTROL_CUST_KEY_0 */ - case 0x324: /* CONTROL_CUST_KEY_1 */ - case 0x330: /* CONTROL_TEST_KEY_0 */ - case 0x334: /* CONTROL_TEST_KEY_1 */ - case 0x338: /* CONTROL_TEST_KEY_2 */ - case 0x33c: /* CONTROL_TEST_KEY_3 */ - case 0x340: /* CONTROL_TEST_KEY_4 */ - case 0x344: /* CONTROL_TEST_KEY_5 */ - case 0x348: /* CONTROL_TEST_KEY_6 */ - case 0x34c: /* CONTROL_TEST_KEY_7 */ - case 0x350: /* CONTROL_TEST_KEY_8 */ - case 0x354: /* CONTROL_TEST_KEY_9 */ - OMAP_RO_REG(addr); - return; - - case 0x010: /* CONTROL_SYSCONFIG */ - s->sysconfig = value & 0x1e; - break; - - case 0x030 ... 0x140: /* CONTROL_PADCONF - only used in the POP */ - /* XXX: should check constant bits */ - s->padconf[(addr - 0x30) >> 2] = value & 0x1f1f1f1f; - break; - - case 0x270: /* CONTROL_DEBOBS */ - s->obs = value & 0xff; - break; - - case 0x274: /* CONTROL_DEVCONF */ - s->devconfig = value & 0xffffc7ff; - break; - - case 0x28c: /* CONTROL_EMU_SUPPORT */ - break; - - case 0x290: /* CONTROL_MSUSPENDMUX_0 */ - s->msuspendmux[0] = value & 0x3fffffff; - break; - case 0x294: /* CONTROL_MSUSPENDMUX_1 */ - s->msuspendmux[1] = value & 0x3fffffff; - break; - case 0x298: /* CONTROL_MSUSPENDMUX_2 */ - s->msuspendmux[2] = value & 0x3fffffff; - break; - case 0x29c: /* CONTROL_MSUSPENDMUX_3 */ - s->msuspendmux[3] = value & 0x3fffffff; - break; - case 0x2a0: /* CONTROL_MSUSPENDMUX_4 */ - s->msuspendmux[4] = value & 0x3fffffff; - break; - - case 0x2b8: /* CONTROL_PSA_CTRL */ - s->psaconfig = value & 0x1c; - s->psaconfig |= (value & 0x20) ? 2 : 1; - break; - case 0x2bc: /* CONTROL_PSA_CMD */ - break; - - case 0x2b0: /* CONTROL_SEC_CTRL */ - case 0x2b4: /* CONTROL_SEC_TEST */ - case 0x2d0: /* CONTROL_SEC_EMU */ - case 0x2d4: /* CONTROL_SEC_TAP */ - case 0x2d8: /* CONTROL_OCM_RAM_PERM */ - case 0x2dc: /* CONTROL_OCM_PUB_RAM_ADD */ - case 0x2e0: /* CONTROL_EXT_SEC_RAM_START_ADD */ - case 0x2e4: /* CONTROL_EXT_SEC_RAM_STOP_ADD */ - case 0x2f0: /* CONTROL_SEC_STATUS */ - case 0x2f4: /* CONTROL_SEC_ERR_STATUS */ - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static uint64_t omap_sysctl_readfn(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return omap_sysctl_read8(opaque, addr); - case 2: - return omap_badwidth_read32(opaque, addr); /* TODO */ - case 4: - return omap_sysctl_read(opaque, addr); - default: - g_assert_not_reached(); - } -} - -static void omap_sysctl_writefn(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - switch (size) { - case 1: - omap_sysctl_write8(opaque, addr, value); - break; - case 2: - omap_badwidth_write32(opaque, addr, value); /* TODO */ - break; - case 4: - omap_sysctl_write(opaque, addr, value); - break; - default: - g_assert_not_reached(); - } -} - -static const MemoryRegionOps omap_sysctl_ops = { - .read = omap_sysctl_readfn, - .write = omap_sysctl_writefn, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_sysctl_reset(struct omap_sysctl_s *s) -{ - /* (power-on reset) */ - s->sysconfig = 0; - s->obs = 0; - s->devconfig = 0x0c000000; - s->msuspendmux[0] = 0x00000000; - s->msuspendmux[1] = 0x00000000; - s->msuspendmux[2] = 0x00000000; - s->msuspendmux[3] = 0x00000000; - s->msuspendmux[4] = 0x00000000; - s->psaconfig = 1; - - s->padconf[0x00] = 0x000f0f0f; - s->padconf[0x01] = 0x00000000; - s->padconf[0x02] = 0x00000000; - s->padconf[0x03] = 0x00000000; - s->padconf[0x04] = 0x00000000; - s->padconf[0x05] = 0x00000000; - s->padconf[0x06] = 0x00000000; - s->padconf[0x07] = 0x00000000; - s->padconf[0x08] = 0x08080800; - s->padconf[0x09] = 0x08080808; - s->padconf[0x0a] = 0x08080808; - s->padconf[0x0b] = 0x08080808; - s->padconf[0x0c] = 0x08080808; - s->padconf[0x0d] = 0x08080800; - s->padconf[0x0e] = 0x08080808; - s->padconf[0x0f] = 0x08080808; - s->padconf[0x10] = 0x18181808; /* | 0x07070700 if SBoot3 */ - s->padconf[0x11] = 0x18181818; /* | 0x07070707 if SBoot3 */ - s->padconf[0x12] = 0x18181818; /* | 0x07070707 if SBoot3 */ - s->padconf[0x13] = 0x18181818; /* | 0x07070707 if SBoot3 */ - s->padconf[0x14] = 0x18181818; /* | 0x00070707 if SBoot3 */ - s->padconf[0x15] = 0x18181818; - s->padconf[0x16] = 0x18181818; /* | 0x07000000 if SBoot3 */ - s->padconf[0x17] = 0x1f001f00; - s->padconf[0x18] = 0x1f1f1f1f; - s->padconf[0x19] = 0x00000000; - s->padconf[0x1a] = 0x1f180000; - s->padconf[0x1b] = 0x00001f1f; - s->padconf[0x1c] = 0x1f001f00; - s->padconf[0x1d] = 0x00000000; - s->padconf[0x1e] = 0x00000000; - s->padconf[0x1f] = 0x08000000; - s->padconf[0x20] = 0x08080808; - s->padconf[0x21] = 0x08080808; - s->padconf[0x22] = 0x0f080808; - s->padconf[0x23] = 0x0f0f0f0f; - s->padconf[0x24] = 0x000f0f0f; - s->padconf[0x25] = 0x1f1f1f0f; - s->padconf[0x26] = 0x080f0f1f; - s->padconf[0x27] = 0x070f1808; - s->padconf[0x28] = 0x0f070707; - s->padconf[0x29] = 0x000f0f1f; - s->padconf[0x2a] = 0x0f0f0f1f; - s->padconf[0x2b] = 0x08000000; - s->padconf[0x2c] = 0x0000001f; - s->padconf[0x2d] = 0x0f0f1f00; - s->padconf[0x2e] = 0x1f1f0f0f; - s->padconf[0x2f] = 0x0f1f1f1f; - s->padconf[0x30] = 0x0f0f0f0f; - s->padconf[0x31] = 0x0f1f0f1f; - s->padconf[0x32] = 0x0f0f0f0f; - s->padconf[0x33] = 0x0f1f0f1f; - s->padconf[0x34] = 0x1f1f0f0f; - s->padconf[0x35] = 0x0f0f1f1f; - s->padconf[0x36] = 0x0f0f1f0f; - s->padconf[0x37] = 0x0f0f0f0f; - s->padconf[0x38] = 0x1f18180f; - s->padconf[0x39] = 0x1f1f1f1f; - s->padconf[0x3a] = 0x00001f1f; - s->padconf[0x3b] = 0x00000000; - s->padconf[0x3c] = 0x00000000; - s->padconf[0x3d] = 0x0f0f0f0f; - s->padconf[0x3e] = 0x18000f0f; - s->padconf[0x3f] = 0x00070000; - s->padconf[0x40] = 0x00000707; - s->padconf[0x41] = 0x0f1f0700; - s->padconf[0x42] = 0x1f1f070f; - s->padconf[0x43] = 0x0008081f; - s->padconf[0x44] = 0x00000800; -} - -static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, - omap_clk iclk, struct omap_mpu_state_s *mpu) -{ - struct omap_sysctl_s *s = g_new0(struct omap_sysctl_s, 1); - - s->mpu = mpu; - omap_sysctl_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_sysctl_ops, s, "omap.sysctl", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} - -/* General chip reset */ -static void omap2_mpu_reset(void *opaque) -{ - struct omap_mpu_state_s *mpu = (struct omap_mpu_state_s *) opaque; - - omap_dma_reset(mpu->dma); - omap_prcm_reset(mpu->prcm); - omap_sysctl_reset(mpu->sysc); - omap_gp_timer_reset(mpu->gptimer[0]); - omap_gp_timer_reset(mpu->gptimer[1]); - omap_gp_timer_reset(mpu->gptimer[2]); - omap_gp_timer_reset(mpu->gptimer[3]); - omap_gp_timer_reset(mpu->gptimer[4]); - omap_gp_timer_reset(mpu->gptimer[5]); - omap_gp_timer_reset(mpu->gptimer[6]); - omap_gp_timer_reset(mpu->gptimer[7]); - omap_gp_timer_reset(mpu->gptimer[8]); - omap_gp_timer_reset(mpu->gptimer[9]); - omap_gp_timer_reset(mpu->gptimer[10]); - omap_gp_timer_reset(mpu->gptimer[11]); - omap_synctimer_reset(mpu->synctimer); - omap_sdrc_reset(mpu->sdrc); - omap_gpmc_reset(mpu->gpmc); - omap_dss_reset(mpu->dss); - omap_uart_reset(mpu->uart[0]); - omap_uart_reset(mpu->uart[1]); - omap_uart_reset(mpu->uart[2]); - omap_mmc_reset(mpu->mmc); - omap_mcspi_reset(mpu->mcspi[0]); - omap_mcspi_reset(mpu->mcspi[1]); - cpu_reset(CPU(mpu->cpu)); -} - -static int omap2_validate_addr(struct omap_mpu_state_s *s, - hwaddr addr) -{ - return 1; -} - -static const struct dma_irq_map omap2_dma_irq_map[] = { - { 0, OMAP_INT_24XX_SDMA_IRQ0 }, - { 0, OMAP_INT_24XX_SDMA_IRQ1 }, - { 0, OMAP_INT_24XX_SDMA_IRQ2 }, - { 0, OMAP_INT_24XX_SDMA_IRQ3 }, -}; - -struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, - const char *cpu_type) -{ - struct omap_mpu_state_s *s = g_new0(struct omap_mpu_state_s, 1); - qemu_irq dma_irqs[4]; - DriveInfo *dinfo; - int i; - SysBusDevice *busdev; - struct omap_target_agent_s *ta; - MemoryRegion *sysmem = get_system_memory(); - - /* Core */ - s->mpu_model = omap2420; - s->cpu = ARM_CPU(cpu_create(cpu_type)); - s->sram_size = OMAP242X_SRAM_SIZE; - - s->wakeup = qemu_allocate_irq(omap_mpu_wakeup, s, 0); - - /* Clocks */ - omap_clk_init(s); - - /* Memory-mapped stuff */ - memory_region_init_ram(&s->sram, NULL, "omap2.sram", s->sram_size, - &error_fatal); - memory_region_add_subregion(sysmem, OMAP2_SRAM_BASE, &s->sram); - - s->l4 = omap_l4_init(sysmem, OMAP2_L4_BASE, 54); - - /* Actually mapped at any 2K boundary in the ARM11 private-peripheral if */ - s->ih[0] = qdev_new("omap2-intc"); - qdev_prop_set_uint8(s->ih[0], "revision", 0x21); - omap_intc_set_fclk(OMAP_INTC(s->ih[0]), omap_findclk(s, "mpu_intc_fclk")); - omap_intc_set_iclk(OMAP_INTC(s->ih[0]), omap_findclk(s, "mpu_intc_iclk")); - busdev = SYS_BUS_DEVICE(s->ih[0]); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); - sysbus_connect_irq(busdev, 1, - qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ)); - sysbus_mmio_map(busdev, 0, 0x480fe000); - s->prcm = omap_prcm_init(omap_l4tao(s->l4, 3), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_PRCM_MPU_IRQ), - NULL, NULL, s); - - s->sysc = omap_sysctl_init(omap_l4tao(s->l4, 1), - omap_findclk(s, "omapctrl_iclk"), s); - - for (i = 0; i < 4; i++) { - dma_irqs[i] = qdev_get_gpio_in(s->ih[omap2_dma_irq_map[i].ih], - omap2_dma_irq_map[i].intr); - } - s->dma = omap_dma4_init(0x48056000, dma_irqs, sysmem, s, 256, 32, - omap_findclk(s, "sdma_iclk"), - omap_findclk(s, "sdma_fclk")); - s->port->addr_valid = omap2_validate_addr; - - /* Register SDRAM and SRAM ports for fast DMA transfers. */ - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(sdram), - OMAP2_Q2_BASE, memory_region_size(sdram)); - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->sram), - OMAP2_SRAM_BASE, s->sram_size); - - s->uart[0] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 19), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_UART1_IRQ), - omap_findclk(s, "uart1_fclk"), - omap_findclk(s, "uart1_iclk"), - s->drq[OMAP24XX_DMA_UART1_TX], - s->drq[OMAP24XX_DMA_UART1_RX], - "uart1", - serial_hd(0)); - s->uart[1] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 20), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_UART2_IRQ), - omap_findclk(s, "uart2_fclk"), - omap_findclk(s, "uart2_iclk"), - s->drq[OMAP24XX_DMA_UART2_TX], - s->drq[OMAP24XX_DMA_UART2_RX], - "uart2", - serial_hd(0) ? serial_hd(1) : NULL); - s->uart[2] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 21), - qdev_get_gpio_in(s->ih[0], - OMAP_INT_24XX_UART3_IRQ), - omap_findclk(s, "uart3_fclk"), - omap_findclk(s, "uart3_iclk"), - s->drq[OMAP24XX_DMA_UART3_TX], - s->drq[OMAP24XX_DMA_UART3_RX], - "uart3", - serial_hd(0) && serial_hd(1) ? serial_hd(2) : NULL); - - s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER1), - omap_findclk(s, "wu_gpt1_clk"), - omap_findclk(s, "wu_l4_iclk")); - s->gptimer[1] = omap_gp_timer_init(omap_l4ta(s->l4, 8), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER2), - omap_findclk(s, "core_gpt2_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[2] = omap_gp_timer_init(omap_l4ta(s->l4, 22), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER3), - omap_findclk(s, "core_gpt3_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[3] = omap_gp_timer_init(omap_l4ta(s->l4, 23), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER4), - omap_findclk(s, "core_gpt4_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[4] = omap_gp_timer_init(omap_l4ta(s->l4, 24), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER5), - omap_findclk(s, "core_gpt5_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[5] = omap_gp_timer_init(omap_l4ta(s->l4, 25), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER6), - omap_findclk(s, "core_gpt6_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[6] = omap_gp_timer_init(omap_l4ta(s->l4, 26), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER7), - omap_findclk(s, "core_gpt7_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[7] = omap_gp_timer_init(omap_l4ta(s->l4, 27), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER8), - omap_findclk(s, "core_gpt8_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[8] = omap_gp_timer_init(omap_l4ta(s->l4, 28), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER9), - omap_findclk(s, "core_gpt9_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[9] = omap_gp_timer_init(omap_l4ta(s->l4, 29), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER10), - omap_findclk(s, "core_gpt10_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[10] = omap_gp_timer_init(omap_l4ta(s->l4, 30), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER11), - omap_findclk(s, "core_gpt11_clk"), - omap_findclk(s, "core_l4_iclk")); - s->gptimer[11] = omap_gp_timer_init(omap_l4ta(s->l4, 31), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER12), - omap_findclk(s, "core_gpt12_clk"), - omap_findclk(s, "core_l4_iclk")); - - omap_tap_init(omap_l4ta(s->l4, 2), s); - - s->synctimer = omap_synctimer_init(omap_l4tao(s->l4, 2), s, - omap_findclk(s, "clk32-kHz"), - omap_findclk(s, "core_l4_iclk")); - - s->i2c[0] = qdev_new("omap_i2c"); - qdev_prop_set_uint8(s->i2c[0], "revision", 0x34); - omap_i2c_set_iclk(OMAP_I2C(s->i2c[0]), omap_findclk(s, "i2c1.iclk")); - omap_i2c_set_fclk(OMAP_I2C(s->i2c[0]), omap_findclk(s, "i2c1.fclk")); - busdev = SYS_BUS_DEVICE(s->i2c[0]); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C1_IRQ)); - sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C1_TX]); - sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C1_RX]); - sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 5), 0)); - - s->i2c[1] = qdev_new("omap_i2c"); - qdev_prop_set_uint8(s->i2c[1], "revision", 0x34); - omap_i2c_set_iclk(OMAP_I2C(s->i2c[1]), omap_findclk(s, "i2c2.iclk")); - omap_i2c_set_fclk(OMAP_I2C(s->i2c[1]), omap_findclk(s, "i2c2.fclk")); - busdev = SYS_BUS_DEVICE(s->i2c[1]); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_I2C2_IRQ)); - sysbus_connect_irq(busdev, 1, s->drq[OMAP24XX_DMA_I2C2_TX]); - sysbus_connect_irq(busdev, 2, s->drq[OMAP24XX_DMA_I2C2_RX]); - sysbus_mmio_map(busdev, 0, omap_l4_region_base(omap_l4tao(s->l4, 6), 0)); - - s->gpio = qdev_new("omap2-gpio"); - qdev_prop_set_int32(s->gpio, "mpu_model", s->mpu_model); - omap2_gpio_set_iclk(OMAP2_GPIO(s->gpio), omap_findclk(s, "gpio_iclk")); - omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 0, omap_findclk(s, "gpio1_dbclk")); - omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 1, omap_findclk(s, "gpio2_dbclk")); - omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 2, omap_findclk(s, "gpio3_dbclk")); - omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 3, omap_findclk(s, "gpio4_dbclk")); - if (s->mpu_model == omap2430) { - omap2_gpio_set_fclk(OMAP2_GPIO(s->gpio), 4, - omap_findclk(s, "gpio5_dbclk")); - } - busdev = SYS_BUS_DEVICE(s->gpio); - sysbus_realize_and_unref(busdev, &error_fatal); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK1)); - sysbus_connect_irq(busdev, 3, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK2)); - sysbus_connect_irq(busdev, 6, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK3)); - sysbus_connect_irq(busdev, 9, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPIO_BANK4)); - if (s->mpu_model == omap2430) { - sysbus_connect_irq(busdev, 12, - qdev_get_gpio_in(s->ih[0], - OMAP_INT_243X_GPIO_BANK5)); - } - ta = omap_l4ta(s->l4, 3); - sysbus_mmio_map(busdev, 0, omap_l4_region_base(ta, 1)); - sysbus_mmio_map(busdev, 1, omap_l4_region_base(ta, 0)); - sysbus_mmio_map(busdev, 2, omap_l4_region_base(ta, 2)); - sysbus_mmio_map(busdev, 3, omap_l4_region_base(ta, 4)); - sysbus_mmio_map(busdev, 4, omap_l4_region_base(ta, 5)); - - s->sdrc = omap_sdrc_init(sysmem, 0x68009000); - s->gpmc = omap_gpmc_init(s, 0x6800a000, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPMC_IRQ), - s->drq[OMAP24XX_DMA_GPMC]); - - dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo && !qtest_enabled()) { - warn_report("missing SecureDigital device"); - } - s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), - dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ), - &s->drq[OMAP24XX_DMA_MMC1_TX], - omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk")); - - s->mcspi[0] = omap_mcspi_init(omap_l4ta(s->l4, 35), 4, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MCSPI1_IRQ), - &s->drq[OMAP24XX_DMA_SPI1_TX0], - omap_findclk(s, "spi1_fclk"), - omap_findclk(s, "spi1_iclk")); - s->mcspi[1] = omap_mcspi_init(omap_l4ta(s->l4, 36), 2, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MCSPI2_IRQ), - &s->drq[OMAP24XX_DMA_SPI2_TX0], - omap_findclk(s, "spi2_fclk"), - omap_findclk(s, "spi2_iclk")); - - s->dss = omap_dss_init(omap_l4ta(s->l4, 10), sysmem, 0x68000800, - /* XXX wire M_IRQ_25, D_L2_IRQ_30 and I_IRQ_13 together */ - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_DSS_IRQ), - s->drq[OMAP24XX_DMA_DSS], - omap_findclk(s, "dss_clk1"), omap_findclk(s, "dss_clk2"), - omap_findclk(s, "dss_54m_clk"), - omap_findclk(s, "dss_l3_iclk"), - omap_findclk(s, "dss_l4_iclk")); - - omap_sti_init(omap_l4ta(s->l4, 18), sysmem, 0x54000000, - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_STI), - omap_findclk(s, "emul_ck"), - serial_hd(0) && serial_hd(1) && serial_hd(2) ? - serial_hd(3) : NULL); - - s->eac = omap_eac_init(omap_l4ta(s->l4, 32), - qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_EAC_IRQ), - /* Ten consecutive lines */ - &s->drq[OMAP24XX_DMA_EAC_AC_RD], - omap_findclk(s, "func_96m_clk"), - omap_findclk(s, "core_l4_iclk")); - - /* All register mappings (includin those not currenlty implemented): - * SystemControlMod 48000000 - 48000fff - * SystemControlL4 48001000 - 48001fff - * 32kHz Timer Mod 48004000 - 48004fff - * 32kHz Timer L4 48005000 - 48005fff - * PRCM ModA 48008000 - 480087ff - * PRCM ModB 48008800 - 48008fff - * PRCM L4 48009000 - 48009fff - * TEST-BCM Mod 48012000 - 48012fff - * TEST-BCM L4 48013000 - 48013fff - * TEST-TAP Mod 48014000 - 48014fff - * TEST-TAP L4 48015000 - 48015fff - * GPIO1 Mod 48018000 - 48018fff - * GPIO Top 48019000 - 48019fff - * GPIO2 Mod 4801a000 - 4801afff - * GPIO L4 4801b000 - 4801bfff - * GPIO3 Mod 4801c000 - 4801cfff - * GPIO4 Mod 4801e000 - 4801efff - * WDTIMER1 Mod 48020000 - 48010fff - * WDTIMER Top 48021000 - 48011fff - * WDTIMER2 Mod 48022000 - 48012fff - * WDTIMER L4 48023000 - 48013fff - * WDTIMER3 Mod 48024000 - 48014fff - * WDTIMER3 L4 48025000 - 48015fff - * WDTIMER4 Mod 48026000 - 48016fff - * WDTIMER4 L4 48027000 - 48017fff - * GPTIMER1 Mod 48028000 - 48018fff - * GPTIMER1 L4 48029000 - 48019fff - * GPTIMER2 Mod 4802a000 - 4801afff - * GPTIMER2 L4 4802b000 - 4801bfff - * L4-Config AP 48040000 - 480407ff - * L4-Config IP 48040800 - 48040fff - * L4-Config LA 48041000 - 48041fff - * ARM11ETB Mod 48048000 - 48049fff - * ARM11ETB L4 4804a000 - 4804afff - * DISPLAY Top 48050000 - 480503ff - * DISPLAY DISPC 48050400 - 480507ff - * DISPLAY RFBI 48050800 - 48050bff - * DISPLAY VENC 48050c00 - 48050fff - * DISPLAY L4 48051000 - 48051fff - * CAMERA Top 48052000 - 480523ff - * CAMERA core 48052400 - 480527ff - * CAMERA DMA 48052800 - 48052bff - * CAMERA MMU 48052c00 - 48052fff - * CAMERA L4 48053000 - 48053fff - * SDMA Mod 48056000 - 48056fff - * SDMA L4 48057000 - 48057fff - * SSI Top 48058000 - 48058fff - * SSI GDD 48059000 - 48059fff - * SSI Port1 4805a000 - 4805afff - * SSI Port2 4805b000 - 4805bfff - * SSI L4 4805c000 - 4805cfff - * USB Mod 4805e000 - 480fefff - * USB L4 4805f000 - 480fffff - * WIN_TRACER1 Mod 48060000 - 48060fff - * WIN_TRACER1 L4 48061000 - 48061fff - * WIN_TRACER2 Mod 48062000 - 48062fff - * WIN_TRACER2 L4 48063000 - 48063fff - * WIN_TRACER3 Mod 48064000 - 48064fff - * WIN_TRACER3 L4 48065000 - 48065fff - * WIN_TRACER4 Top 48066000 - 480660ff - * WIN_TRACER4 ETT 48066100 - 480661ff - * WIN_TRACER4 WT 48066200 - 480662ff - * WIN_TRACER4 L4 48067000 - 48067fff - * XTI Mod 48068000 - 48068fff - * XTI L4 48069000 - 48069fff - * UART1 Mod 4806a000 - 4806afff - * UART1 L4 4806b000 - 4806bfff - * UART2 Mod 4806c000 - 4806cfff - * UART2 L4 4806d000 - 4806dfff - * UART3 Mod 4806e000 - 4806efff - * UART3 L4 4806f000 - 4806ffff - * I2C1 Mod 48070000 - 48070fff - * I2C1 L4 48071000 - 48071fff - * I2C2 Mod 48072000 - 48072fff - * I2C2 L4 48073000 - 48073fff - * McBSP1 Mod 48074000 - 48074fff - * McBSP1 L4 48075000 - 48075fff - * McBSP2 Mod 48076000 - 48076fff - * McBSP2 L4 48077000 - 48077fff - * GPTIMER3 Mod 48078000 - 48078fff - * GPTIMER3 L4 48079000 - 48079fff - * GPTIMER4 Mod 4807a000 - 4807afff - * GPTIMER4 L4 4807b000 - 4807bfff - * GPTIMER5 Mod 4807c000 - 4807cfff - * GPTIMER5 L4 4807d000 - 4807dfff - * GPTIMER6 Mod 4807e000 - 4807efff - * GPTIMER6 L4 4807f000 - 4807ffff - * GPTIMER7 Mod 48080000 - 48080fff - * GPTIMER7 L4 48081000 - 48081fff - * GPTIMER8 Mod 48082000 - 48082fff - * GPTIMER8 L4 48083000 - 48083fff - * GPTIMER9 Mod 48084000 - 48084fff - * GPTIMER9 L4 48085000 - 48085fff - * GPTIMER10 Mod 48086000 - 48086fff - * GPTIMER10 L4 48087000 - 48087fff - * GPTIMER11 Mod 48088000 - 48088fff - * GPTIMER11 L4 48089000 - 48089fff - * GPTIMER12 Mod 4808a000 - 4808afff - * GPTIMER12 L4 4808b000 - 4808bfff - * EAC Mod 48090000 - 48090fff - * EAC L4 48091000 - 48091fff - * FAC Mod 48092000 - 48092fff - * FAC L4 48093000 - 48093fff - * MAILBOX Mod 48094000 - 48094fff - * MAILBOX L4 48095000 - 48095fff - * SPI1 Mod 48098000 - 48098fff - * SPI1 L4 48099000 - 48099fff - * SPI2 Mod 4809a000 - 4809afff - * SPI2 L4 4809b000 - 4809bfff - * MMC/SDIO Mod 4809c000 - 4809cfff - * MMC/SDIO L4 4809d000 - 4809dfff - * MS_PRO Mod 4809e000 - 4809efff - * MS_PRO L4 4809f000 - 4809ffff - * RNG Mod 480a0000 - 480a0fff - * RNG L4 480a1000 - 480a1fff - * DES3DES Mod 480a2000 - 480a2fff - * DES3DES L4 480a3000 - 480a3fff - * SHA1MD5 Mod 480a4000 - 480a4fff - * SHA1MD5 L4 480a5000 - 480a5fff - * AES Mod 480a6000 - 480a6fff - * AES L4 480a7000 - 480a7fff - * PKA Mod 480a8000 - 480a9fff - * PKA L4 480aa000 - 480aafff - * MG Mod 480b0000 - 480b0fff - * MG L4 480b1000 - 480b1fff - * HDQ/1-wire Mod 480b2000 - 480b2fff - * HDQ/1-wire L4 480b3000 - 480b3fff - * MPU interrupt 480fe000 - 480fefff - * STI channel base 54000000 - 5400ffff - * IVA RAM 5c000000 - 5c01ffff - * IVA ROM 5c020000 - 5c027fff - * IMG_BUF_A 5c040000 - 5c040fff - * IMG_BUF_B 5c042000 - 5c042fff - * VLCDS 5c048000 - 5c0487ff - * IMX_COEF 5c049000 - 5c04afff - * IMX_CMD 5c051000 - 5c051fff - * VLCDQ 5c053000 - 5c0533ff - * VLCDH 5c054000 - 5c054fff - * SEQ_CMD 5c055000 - 5c055fff - * IMX_REG 5c056000 - 5c0560ff - * VLCD_REG 5c056100 - 5c0561ff - * SEQ_REG 5c056200 - 5c0562ff - * IMG_BUF_REG 5c056300 - 5c0563ff - * SEQIRQ_REG 5c056400 - 5c0564ff - * OCP_REG 5c060000 - 5c060fff - * SYSC_REG 5c070000 - 5c070fff - * MMU_REG 5d000000 - 5d000fff - * sDMA R 68000400 - 680005ff - * sDMA W 68000600 - 680007ff - * Display Control 68000800 - 680009ff - * DSP subsystem 68000a00 - 68000bff - * MPU subsystem 68000c00 - 68000dff - * IVA subsystem 68001000 - 680011ff - * USB 68001200 - 680013ff - * Camera 68001400 - 680015ff - * VLYNQ (firewall) 68001800 - 68001bff - * VLYNQ 68001e00 - 68001fff - * SSI 68002000 - 680021ff - * L4 68002400 - 680025ff - * DSP (firewall) 68002800 - 68002bff - * DSP subsystem 68002e00 - 68002fff - * IVA (firewall) 68003000 - 680033ff - * IVA 68003600 - 680037ff - * GFX 68003a00 - 68003bff - * CMDWR emulation 68003c00 - 68003dff - * SMS 68004000 - 680041ff - * OCM 68004200 - 680043ff - * GPMC 68004400 - 680045ff - * RAM (firewall) 68005000 - 680053ff - * RAM (err login) 68005400 - 680057ff - * ROM (firewall) 68005800 - 68005bff - * ROM (err login) 68005c00 - 68005fff - * GPMC (firewall) 68006000 - 680063ff - * GPMC (err login) 68006400 - 680067ff - * SMS (err login) 68006c00 - 68006fff - * SMS registers 68008000 - 68008fff - * SDRC registers 68009000 - 68009fff - * GPMC registers 6800a000 6800afff - */ - - qemu_register_reset(omap2_mpu_reset, s); - - return s; -} 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 deleted file mode 100644 index 68e11dd1ec..0000000000 --- a/hw/arm/palm.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * PalmOne's (TM) PDAs. - * - * Copyright (C) 2006-2007 Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "audio/audio.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "ui/console.h" -#include "hw/arm/omap.h" -#include "hw/boards.h" -#include "hw/arm/boot.h" -#include "hw/input/tsc2xxx.h" -#include "hw/irq.h" -#include "hw/loader.h" -#include "cpu.h" -#include "qemu/cutils.h" -#include "qom/object.h" - -static uint64_t static_read(void *opaque, hwaddr offset, unsigned size) -{ - uint32_t *val = (uint32_t *)opaque; - uint32_t sizemask = 7 >> size; - - return *val >> ((offset & sizemask) << 3); -} - -static void static_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ -#ifdef SPY - printf("%s: value %08lx written at " PA_FMT "\n", - __func__, value, offset); -#endif -} - -static const MemoryRegionOps static_ops = { - .read = static_read, - .write = static_write, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* Palm Tunsgten|E support */ - -/* Shared GPIOs */ -#define PALMTE_USBDETECT_GPIO 0 -#define PALMTE_USB_OR_DC_GPIO 1 -#define PALMTE_TSC_GPIO 4 -#define PALMTE_PINTDAV_GPIO 6 -#define PALMTE_MMC_WP_GPIO 8 -#define PALMTE_MMC_POWER_GPIO 9 -#define PALMTE_HDQ_GPIO 11 -#define PALMTE_HEADPHONES_GPIO 14 -#define PALMTE_SPEAKER_GPIO 15 -/* MPU private GPIOs */ -#define PALMTE_DC_GPIO 2 -#define PALMTE_MMC_SWITCH_GPIO 4 -#define PALMTE_MMC1_GPIO 6 -#define PALMTE_MMC2_GPIO 7 -#define PALMTE_MMC3_GPIO 11 - -static MouseTransformInfo palmte_pointercal = { - .x = 320, - .y = 320, - .a = { -5909, 8, 22465308, 104, 7644, -1219972, 65536 }, -}; - -static void palmte_microwire_setup(struct omap_mpu_state_s *cpu) -{ - uWireSlave *tsc; - - tsc = tsc2102_init(qdev_get_gpio_in(cpu->gpio, PALMTE_PINTDAV_GPIO)); - - omap_uwire_attach(cpu->microwire, tsc, 0); - omap_mcbsp_i2s_attach(cpu->mcbsp1, tsc210x_codec(tsc)); - - tsc210x_set_transform(tsc, &palmte_pointercal); -} - -static struct { - int row; - int column; -} palmte_keymap[0x80] = { - [0 ... 0x7f] = { -1, -1 }, - [0x3b] = { 0, 0 }, /* F1 -> Calendar */ - [0x3c] = { 1, 0 }, /* F2 -> Contacts */ - [0x3d] = { 2, 0 }, /* F3 -> Tasks List */ - [0x3e] = { 3, 0 }, /* F4 -> Note Pad */ - [0x01] = { 4, 0 }, /* Esc -> Power */ - [0x4b] = { 0, 1 }, /* Left */ - [0x50] = { 1, 1 }, /* Down */ - [0x48] = { 2, 1 }, /* Up */ - [0x4d] = { 3, 1 }, /* Right */ - [0x4c] = { 4, 1 }, /* Centre */ - [0x39] = { 4, 1 }, /* Spc -> Centre */ -}; - -static void palmte_button_event(void *opaque, int keycode) -{ - struct omap_mpu_state_s *cpu = (struct omap_mpu_state_s *) opaque; - - if (palmte_keymap[keycode & 0x7f].row != -1) - omap_mpuio_key(cpu->mpuio, - palmte_keymap[keycode & 0x7f].row, - palmte_keymap[keycode & 0x7f].column, - !(keycode & 0x80)); -} - -/* - * Encapsulation of some GPIO line behaviour for the Palm board - * - * QEMU interface: - * + unnamed GPIO inputs 0..6: for the various miscellaneous input lines - */ - -#define TYPE_PALM_MISC_GPIO "palm-misc-gpio" -OBJECT_DECLARE_SIMPLE_TYPE(PalmMiscGPIOState, PALM_MISC_GPIO) - -struct PalmMiscGPIOState { - SysBusDevice parent_obj; -}; - -static void palmte_onoff_gpios(void *opaque, int line, int level) -{ - switch (line) { - case 0: - printf("%s: current to MMC/SD card %sabled.\n", - __func__, level ? "dis" : "en"); - break; - case 1: - printf("%s: internal speaker amplifier %s.\n", - __func__, level ? "down" : "on"); - break; - - /* These LCD & Audio output signals have not been identified yet. */ - case 2: - case 3: - case 4: - printf("%s: LCD GPIO%i %s.\n", - __func__, line - 1, level ? "high" : "low"); - break; - case 5: - case 6: - printf("%s: Audio GPIO%i %s.\n", - __func__, line - 4, level ? "high" : "low"); - break; - } -} - -static void palm_misc_gpio_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - - qdev_init_gpio_in(dev, palmte_onoff_gpios, 7); -} - -static const TypeInfo palm_misc_gpio_info = { - .name = TYPE_PALM_MISC_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PalmMiscGPIOState), - .instance_init = palm_misc_gpio_init, - /* - * No class init required: device has no internal state so does not - * need to set up reset or vmstate, and has no realize method. - */ -}; - -static void palmte_gpio_setup(struct omap_mpu_state_s *cpu) -{ - DeviceState *misc_gpio; - - misc_gpio = sysbus_create_simple(TYPE_PALM_MISC_GPIO, -1, NULL); - - omap_mmc_handlers(cpu->mmc, - qdev_get_gpio_in(cpu->gpio, PALMTE_MMC_WP_GPIO), - qemu_irq_invert(omap_mpuio_in_get(cpu->mpuio) - [PALMTE_MMC_SWITCH_GPIO])); - - qdev_connect_gpio_out(cpu->gpio, PALMTE_MMC_POWER_GPIO, - qdev_get_gpio_in(misc_gpio, 0)); - qdev_connect_gpio_out(cpu->gpio, PALMTE_SPEAKER_GPIO, - qdev_get_gpio_in(misc_gpio, 1)); - qdev_connect_gpio_out(cpu->gpio, 11, qdev_get_gpio_in(misc_gpio, 2)); - qdev_connect_gpio_out(cpu->gpio, 12, qdev_get_gpio_in(misc_gpio, 3)); - qdev_connect_gpio_out(cpu->gpio, 13, qdev_get_gpio_in(misc_gpio, 4)); - omap_mpuio_out_set(cpu->mpuio, 1, qdev_get_gpio_in(misc_gpio, 5)); - omap_mpuio_out_set(cpu->mpuio, 3, qdev_get_gpio_in(misc_gpio, 6)); - - /* Reset some inputs to initial state. */ - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USBDETECT_GPIO)); - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_USB_OR_DC_GPIO)); - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, 4)); - qemu_irq_lower(qdev_get_gpio_in(cpu->gpio, PALMTE_HEADPHONES_GPIO)); - qemu_irq_lower(omap_mpuio_in_get(cpu->mpuio)[PALMTE_DC_GPIO]); - qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[6]); - qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[7]); - qemu_irq_raise(omap_mpuio_in_get(cpu->mpuio)[11]); -} - -static struct arm_boot_info palmte_binfo = { - .loader_start = OMAP_EMIFF_BASE, - .ram_size = 0x02000000, - .board_id = 0x331, -}; - -static void palmte_init(MachineState *machine) -{ - MemoryRegion *address_space_mem = get_system_memory(); - struct omap_mpu_state_s *mpu; - int flash_size = 0x00800000; - static uint32_t cs0val = 0xffffffff; - static uint32_t cs1val = 0x0000e1a0; - static uint32_t cs2val = 0x0000e1a0; - static uint32_t cs3val = 0xe1a0e1a0; - int rom_size, rom_loaded = 0; - MachineClass *mc = MACHINE_GET_CLASS(machine); - MemoryRegion *flash = g_new(MemoryRegion, 1); - MemoryRegion *cs = g_new(MemoryRegion, 4); - - if (machine->ram_size != mc->default_ram_size) { - char *sz = size_to_str(mc->default_ram_size); - error_report("Invalid RAM size, should be %s", sz); - g_free(sz); - exit(EXIT_FAILURE); - } - - memory_region_add_subregion(address_space_mem, OMAP_EMIFF_BASE, - machine->ram); - - mpu = omap310_mpu_init(machine->ram, machine->cpu_type); - - /* External Flash (EMIFS) */ - memory_region_init_rom(flash, NULL, "palmte.flash", flash_size, - &error_fatal); - memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE, flash); - - memory_region_init_io(&cs[0], NULL, &static_ops, &cs0val, "palmte-cs0", - OMAP_CS0_SIZE - flash_size); - memory_region_add_subregion(address_space_mem, OMAP_CS0_BASE + flash_size, - &cs[0]); - memory_region_init_io(&cs[1], NULL, &static_ops, &cs1val, "palmte-cs1", - OMAP_CS1_SIZE); - memory_region_add_subregion(address_space_mem, OMAP_CS1_BASE, &cs[1]); - memory_region_init_io(&cs[2], NULL, &static_ops, &cs2val, "palmte-cs2", - OMAP_CS2_SIZE); - memory_region_add_subregion(address_space_mem, OMAP_CS2_BASE, &cs[2]); - memory_region_init_io(&cs[3], NULL, &static_ops, &cs3val, "palmte-cs3", - OMAP_CS3_SIZE); - memory_region_add_subregion(address_space_mem, OMAP_CS3_BASE, &cs[3]); - - palmte_microwire_setup(mpu); - - qemu_add_kbd_event_handler(palmte_button_event, mpu); - - palmte_gpio_setup(mpu); - - /* Setup initial (reset) machine state */ - if (nb_option_roms) { - rom_size = get_image_size(option_rom[0].name); - if (rom_size > flash_size) { - fprintf(stderr, "%s: ROM image too big (%x > %x)\n", - __func__, rom_size, flash_size); - rom_size = 0; - } - if (rom_size > 0) { - rom_size = load_image_targphys(option_rom[0].name, OMAP_CS0_BASE, - flash_size); - rom_loaded = 1; - } - if (rom_size < 0) { - fprintf(stderr, "%s: error loading '%s'\n", - __func__, option_rom[0].name); - } - } - - if (!rom_loaded && !machine->kernel_filename && !qtest_enabled()) { - fprintf(stderr, "Kernel or ROM image must be specified\n"); - exit(1); - } - - /* Load the kernel. */ - arm_load_kernel(mpu->cpu, machine, &palmte_binfo); -} - -static void palmte_machine_init(MachineClass *mc) -{ - mc->desc = "Palm Tungsten|E aka. Cheetah PDA (OMAP310)"; - mc->init = palmte_init; - mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t"); - mc->default_ram_size = 0x02000000; - mc->default_ram_id = "omap1.dram"; -} - -DEFINE_MACHINE("cheetah", palmte_machine_init) - -static void palm_register_types(void) -{ - type_register_static(&palm_misc_gpio_info); -} - -type_init(palm_register_types) diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c deleted file mode 100644 index 93dda83d7a..0000000000 --- a/hw/arm/pxa2xx.c +++ /dev/null @@ -1,2386 +0,0 @@ -/* - * Intel XScale PXA255/270 processor support. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "qapi/error.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "hw/arm/pxa.h" -#include "sysemu/sysemu.h" -#include "hw/char/serial.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/ssi/ssi.h" -#include "hw/sd/sd.h" -#include "chardev/char-fe.h" -#include "sysemu/blockdev.h" -#include "sysemu/qtest.h" -#include "sysemu/rtc.h" -#include "qemu/cutils.h" -#include "qemu/log.h" -#include "qom/object.h" -#include "target/arm/cpregs.h" - -static struct { - hwaddr io_base; - int irqn; -} pxa255_serial[] = { - { 0x40100000, PXA2XX_PIC_FFUART }, - { 0x40200000, PXA2XX_PIC_BTUART }, - { 0x40700000, PXA2XX_PIC_STUART }, - { 0x41600000, PXA25X_PIC_HWUART }, - { 0, 0 } -}, pxa270_serial[] = { - { 0x40100000, PXA2XX_PIC_FFUART }, - { 0x40200000, PXA2XX_PIC_BTUART }, - { 0x40700000, PXA2XX_PIC_STUART }, - { 0, 0 } -}; - -typedef struct PXASSPDef { - hwaddr io_base; - int irqn; -} PXASSPDef; - -#if 0 -static PXASSPDef pxa250_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0, 0 } -}; -#endif - -static PXASSPDef pxa255_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0x41400000, PXA25X_PIC_NSSP }, - { 0, 0 } -}; - -#if 0 -static PXASSPDef pxa26x_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0x41400000, PXA25X_PIC_NSSP }, - { 0x41500000, PXA26X_PIC_ASSP }, - { 0, 0 } -}; -#endif - -static PXASSPDef pxa27x_ssp[] = { - { 0x41000000, PXA2XX_PIC_SSP }, - { 0x41700000, PXA27X_PIC_SSP2 }, - { 0x41900000, PXA2XX_PIC_SSP3 }, - { 0, 0 } -}; - -#define PMCR 0x00 /* Power Manager Control register */ -#define PSSR 0x04 /* Power Manager Sleep Status register */ -#define PSPR 0x08 /* Power Manager Scratch-Pad register */ -#define PWER 0x0c /* Power Manager Wake-Up Enable register */ -#define PRER 0x10 /* Power Manager Rising-Edge Detect Enable register */ -#define PFER 0x14 /* Power Manager Falling-Edge Detect Enable register */ -#define PEDR 0x18 /* Power Manager Edge-Detect Status register */ -#define PCFR 0x1c /* Power Manager General Configuration register */ -#define PGSR0 0x20 /* Power Manager GPIO Sleep-State register 0 */ -#define PGSR1 0x24 /* Power Manager GPIO Sleep-State register 1 */ -#define PGSR2 0x28 /* Power Manager GPIO Sleep-State register 2 */ -#define PGSR3 0x2c /* Power Manager GPIO Sleep-State register 3 */ -#define RCSR 0x30 /* Reset Controller Status register */ -#define PSLR 0x34 /* Power Manager Sleep Configuration register */ -#define PTSR 0x38 /* Power Manager Standby Configuration register */ -#define PVCR 0x40 /* Power Manager Voltage Change Control register */ -#define PUCR 0x4c /* Power Manager USIM Card Control/Status register */ -#define PKWR 0x50 /* Power Manager Keyboard Wake-Up Enable register */ -#define PKSR 0x54 /* Power Manager Keyboard Level-Detect Status */ -#define PCMD0 0x80 /* Power Manager I2C Command register File 0 */ -#define PCMD31 0xfc /* Power Manager I2C Command register File 31 */ - -static uint64_t pxa2xx_pm_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case PMCR ... PCMD31: - if (addr & 3) - goto fail; - - return s->pm_regs[addr >> 2]; - default: - fail: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_pm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case PMCR: - /* Clear the write-one-to-clear bits... */ - s->pm_regs[addr >> 2] &= ~(value & 0x2a); - /* ...and set the plain r/w bits */ - s->pm_regs[addr >> 2] &= ~0x15; - s->pm_regs[addr >> 2] |= value & 0x15; - break; - - case PSSR: /* Read-clean registers */ - case RCSR: - case PKSR: - s->pm_regs[addr >> 2] &= ~value; - break; - - default: /* Read-write registers */ - if (!(addr & 3)) { - s->pm_regs[addr >> 2] = value; - break; - } - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_pm_ops = { - .read = pxa2xx_pm_read, - .write = pxa2xx_pm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_pm = { - .name = "pxa2xx_pm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(pm_regs, PXA2xxState, 0x40), - VMSTATE_END_OF_LIST() - } -}; - -#define CCCR 0x00 /* Core Clock Configuration register */ -#define CKEN 0x04 /* Clock Enable register */ -#define OSCC 0x08 /* Oscillator Configuration register */ -#define CCSR 0x0c /* Core Clock Status register */ - -static uint64_t pxa2xx_cm_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case CCCR: - case CKEN: - case OSCC: - return s->cm_regs[addr >> 2]; - - case CCSR: - return s->cm_regs[CCCR >> 2] | (3 << 28); - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_cm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case CCCR: - case CKEN: - s->cm_regs[addr >> 2] = value; - break; - - case OSCC: - s->cm_regs[addr >> 2] &= ~0x6c; - s->cm_regs[addr >> 2] |= value & 0x6e; - if ((value >> 1) & 1) /* OON */ - s->cm_regs[addr >> 2] |= 1 << 0; /* Oscillator is now stable */ - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_cm_ops = { - .read = pxa2xx_cm_read, - .write = pxa2xx_cm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_cm = { - .name = "pxa2xx_cm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(cm_regs, PXA2xxState, 4), - VMSTATE_UINT32(clkcfg, PXA2xxState), - VMSTATE_UINT32(pmnc, PXA2xxState), - VMSTATE_END_OF_LIST() - } -}; - -static uint64_t pxa2xx_clkcfg_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - return s->clkcfg; -} - -static void pxa2xx_clkcfg_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - s->clkcfg = value & 0xf; - if (value & 2) { - printf("%s: CPU frequency change attempt\n", __func__); - } -} - -static void pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - static const char *pwrmode[8] = { - "Normal", "Idle", "Deep-idle", "Standby", - "Sleep", "reserved (!)", "reserved (!)", "Deep-sleep", - }; - - if (value & 8) { - printf("%s: CPU voltage change attempt\n", __func__); - } - switch (value & 7) { - case 0: - /* Do nothing */ - break; - - case 1: - /* Idle */ - if (!(s->cm_regs[CCCR >> 2] & (1U << 31))) { /* CPDIS */ - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); - break; - } - /* Fall through. */ - - case 2: - /* Deep-Idle */ - cpu_interrupt(CPU(s->cpu), CPU_INTERRUPT_HALT); - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - goto message; - - case 3: - s->cpu->env.uncached_cpsr = ARM_CPU_MODE_SVC; - s->cpu->env.daif = PSTATE_A | PSTATE_F | PSTATE_I; - s->cpu->env.cp15.sctlr_ns = 0; - s->cpu->env.cp15.cpacr_el1 = 0; - s->cpu->env.cp15.ttbr0_el[1] = 0; - s->cpu->env.cp15.dacr_ns = 0; - s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */ - s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */ - - /* - * The scratch-pad register is almost universally used - * for storing the return address on suspend. For the - * lack of a resuming bootloader, perform a jump - * directly to that address. - */ - memset(s->cpu->env.regs, 0, 4 * 15); - s->cpu->env.regs[15] = s->pm_regs[PSPR >> 2]; - -#if 0 - buffer = 0xe59ff000; /* ldr pc, [pc, #0] */ - cpu_physical_memory_write(0, &buffer, 4); - buffer = s->pm_regs[PSPR >> 2]; - cpu_physical_memory_write(8, &buffer, 4); -#endif - - /* Suspend */ - cpu_interrupt(current_cpu, CPU_INTERRUPT_HALT); - - goto message; - - default: - message: - printf("%s: machine entered %s mode\n", __func__, - pwrmode[value & 7]); - } -} - -static uint64_t pxa2xx_cppmnc_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - return s->pmnc; -} - -static void pxa2xx_cppmnc_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - s->pmnc = value; -} - -static uint64_t pxa2xx_cpccnt_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - PXA2xxState *s = (PXA2xxState *)ri->opaque; - if (s->pmnc & 1) { - return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } else { - return 0; - } -} - -static const ARMCPRegInfo pxa_cp_reginfo[] = { - /* cp14 crm==1: perf registers */ - { .name = "CPPMNC", .cp = 14, .crn = 0, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = pxa2xx_cppmnc_read, .writefn = pxa2xx_cppmnc_write }, - { .name = "CPCCNT", .cp = 14, .crn = 1, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = pxa2xx_cpccnt_read, .writefn = arm_cp_write_ignore }, - { .name = "CPINTEN", .cp = 14, .crn = 4, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPFLAG", .cp = 14, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPEVTSEL", .cp = 14, .crn = 8, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* cp14 crm==2: performance count registers */ - { .name = "CPPMN0", .cp = 14, .crn = 0, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN1", .cp = 14, .crn = 1, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN2", .cp = 14, .crn = 2, .crm = 2, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "CPPMN3", .cp = 14, .crn = 2, .crm = 3, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* cp14 crn==6: CLKCFG */ - { .name = "CLKCFG", .cp = 14, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = pxa2xx_clkcfg_read, .writefn = pxa2xx_clkcfg_write }, - /* cp14 crn==7: PWRMODE */ - { .name = "PWRMODE", .cp = 14, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_IO, - .readfn = arm_cp_read_zero, .writefn = pxa2xx_pwrmode_write }, -}; - -static void pxa2xx_setup_cp14(PXA2xxState *s) -{ - define_arm_cp_regs_with_opaque(s->cpu, pxa_cp_reginfo, s); -} - -#define MDCNFG 0x00 /* SDRAM Configuration register */ -#define MDREFR 0x04 /* SDRAM Refresh Control register */ -#define MSC0 0x08 /* Static Memory Control register 0 */ -#define MSC1 0x0c /* Static Memory Control register 1 */ -#define MSC2 0x10 /* Static Memory Control register 2 */ -#define MECR 0x14 /* Expansion Memory Bus Config register */ -#define SXCNFG 0x1c /* Synchronous Static Memory Config register */ -#define MCMEM0 0x28 /* PC Card Memory Socket 0 Timing register */ -#define MCMEM1 0x2c /* PC Card Memory Socket 1 Timing register */ -#define MCATT0 0x30 /* PC Card Attribute Socket 0 register */ -#define MCATT1 0x34 /* PC Card Attribute Socket 1 register */ -#define MCIO0 0x38 /* PC Card I/O Socket 0 Timing register */ -#define MCIO1 0x3c /* PC Card I/O Socket 1 Timing register */ -#define MDMRS 0x40 /* SDRAM Mode Register Set Config register */ -#define BOOT_DEF 0x44 /* Boot-time Default Configuration register */ -#define ARB_CNTL 0x48 /* Arbiter Control register */ -#define BSCNTR0 0x4c /* Memory Buffer Strength Control register 0 */ -#define BSCNTR1 0x50 /* Memory Buffer Strength Control register 1 */ -#define LCDBSCNTR 0x54 /* LCD Buffer Strength Control register */ -#define MDMRSLP 0x58 /* Low Power SDRAM Mode Set Config register */ -#define BSCNTR2 0x5c /* Memory Buffer Strength Control register 2 */ -#define BSCNTR3 0x60 /* Memory Buffer Strength Control register 3 */ -#define SA1110 0x64 /* SA-1110 Memory Compatibility register */ - -static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case MDCNFG ... SA1110: - if ((addr & 3) == 0) - return s->mm_regs[addr >> 2]; - /* fall through */ - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_mm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - switch (addr) { - case MDCNFG ... SA1110: - if ((addr & 3) == 0) { - s->mm_regs[addr >> 2] = value; - break; - } - /* fallthrough */ - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_mm_ops = { - .read = pxa2xx_mm_read, - .write = pxa2xx_mm_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_mm = { - .name = "pxa2xx_mm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(mm_regs, PXA2xxState, 0x1a), - VMSTATE_END_OF_LIST() - } -}; - -#define TYPE_PXA2XX_SSP "pxa2xx-ssp" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxSSPState, PXA2XX_SSP) - -/* Synchronous Serial Ports */ -struct PXA2xxSSPState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq; - uint32_t enable; - SSIBus *bus; - - uint32_t sscr[2]; - uint32_t sspsp; - uint32_t ssto; - uint32_t ssitr; - uint32_t sssr; - uint8_t sstsa; - uint8_t ssrsa; - uint8_t ssacd; - - uint32_t rx_fifo[16]; - uint32_t rx_level; - uint32_t rx_start; -}; - -static bool pxa2xx_ssp_vmstate_validate(void *opaque, int version_id) -{ - PXA2xxSSPState *s = opaque; - - return s->rx_start < sizeof(s->rx_fifo); -} - -static const VMStateDescription vmstate_pxa2xx_ssp = { - .name = "pxa2xx-ssp", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(enable, PXA2xxSSPState), - VMSTATE_UINT32_ARRAY(sscr, PXA2xxSSPState, 2), - VMSTATE_UINT32(sspsp, PXA2xxSSPState), - VMSTATE_UINT32(ssto, PXA2xxSSPState), - VMSTATE_UINT32(ssitr, PXA2xxSSPState), - VMSTATE_UINT32(sssr, PXA2xxSSPState), - VMSTATE_UINT8(sstsa, PXA2xxSSPState), - VMSTATE_UINT8(ssrsa, PXA2xxSSPState), - VMSTATE_UINT8(ssacd, PXA2xxSSPState), - VMSTATE_UINT32(rx_level, PXA2xxSSPState), - VMSTATE_UINT32(rx_start, PXA2xxSSPState), - VMSTATE_VALIDATE("fifo is 16 bytes", pxa2xx_ssp_vmstate_validate), - VMSTATE_UINT32_ARRAY(rx_fifo, PXA2xxSSPState, 16), - VMSTATE_END_OF_LIST() - } -}; - -#define SSCR0 0x00 /* SSP Control register 0 */ -#define SSCR1 0x04 /* SSP Control register 1 */ -#define SSSR 0x08 /* SSP Status register */ -#define SSITR 0x0c /* SSP Interrupt Test register */ -#define SSDR 0x10 /* SSP Data register */ -#define SSTO 0x28 /* SSP Time-Out register */ -#define SSPSP 0x2c /* SSP Programmable Serial Protocol register */ -#define SSTSA 0x30 /* SSP TX Time Slot Active register */ -#define SSRSA 0x34 /* SSP RX Time Slot Active register */ -#define SSTSS 0x38 /* SSP Time Slot Status register */ -#define SSACD 0x3c /* SSP Audio Clock Divider register */ - -/* Bitfields for above registers */ -#define SSCR0_SPI(x) (((x) & 0x30) == 0x00) -#define SSCR0_SSP(x) (((x) & 0x30) == 0x10) -#define SSCR0_UWIRE(x) (((x) & 0x30) == 0x20) -#define SSCR0_PSP(x) (((x) & 0x30) == 0x30) -#define SSCR0_SSE (1 << 7) -#define SSCR0_RIM (1 << 22) -#define SSCR0_TIM (1 << 23) -#define SSCR0_MOD (1U << 31) -#define SSCR0_DSS(x) (((((x) >> 16) & 0x10) | ((x) & 0xf)) + 1) -#define SSCR1_RIE (1 << 0) -#define SSCR1_TIE (1 << 1) -#define SSCR1_LBM (1 << 2) -#define SSCR1_MWDS (1 << 5) -#define SSCR1_TFT(x) ((((x) >> 6) & 0xf) + 1) -#define SSCR1_RFT(x) ((((x) >> 10) & 0xf) + 1) -#define SSCR1_EFWR (1 << 14) -#define SSCR1_PINTE (1 << 18) -#define SSCR1_TINTE (1 << 19) -#define SSCR1_RSRE (1 << 20) -#define SSCR1_TSRE (1 << 21) -#define SSCR1_EBCEI (1 << 29) -#define SSITR_INT (7 << 5) -#define SSSR_TNF (1 << 2) -#define SSSR_RNE (1 << 3) -#define SSSR_TFS (1 << 5) -#define SSSR_RFS (1 << 6) -#define SSSR_ROR (1 << 7) -#define SSSR_PINT (1 << 18) -#define SSSR_TINT (1 << 19) -#define SSSR_EOC (1 << 20) -#define SSSR_TUR (1 << 21) -#define SSSR_BCE (1 << 23) -#define SSSR_RW 0x00bc0080 - -static void pxa2xx_ssp_int_update(PXA2xxSSPState *s) -{ - int level = 0; - - level |= s->ssitr & SSITR_INT; - level |= (s->sssr & SSSR_BCE) && (s->sscr[1] & SSCR1_EBCEI); - level |= (s->sssr & SSSR_TUR) && !(s->sscr[0] & SSCR0_TIM); - level |= (s->sssr & SSSR_EOC) && (s->sssr & (SSSR_TINT | SSSR_PINT)); - level |= (s->sssr & SSSR_TINT) && (s->sscr[1] & SSCR1_TINTE); - level |= (s->sssr & SSSR_PINT) && (s->sscr[1] & SSCR1_PINTE); - level |= (s->sssr & SSSR_ROR) && !(s->sscr[0] & SSCR0_RIM); - level |= (s->sssr & SSSR_RFS) && (s->sscr[1] & SSCR1_RIE); - level |= (s->sssr & SSSR_TFS) && (s->sscr[1] & SSCR1_TIE); - qemu_set_irq(s->irq, !!level); -} - -static void pxa2xx_ssp_fifo_update(PXA2xxSSPState *s) -{ - s->sssr &= ~(0xf << 12); /* Clear RFL */ - s->sssr &= ~(0xf << 8); /* Clear TFL */ - s->sssr &= ~SSSR_TFS; - s->sssr &= ~SSSR_TNF; - if (s->enable) { - s->sssr |= ((s->rx_level - 1) & 0xf) << 12; - if (s->rx_level >= SSCR1_RFT(s->sscr[1])) - s->sssr |= SSSR_RFS; - else - s->sssr &= ~SSSR_RFS; - if (s->rx_level) - s->sssr |= SSSR_RNE; - else - s->sssr &= ~SSSR_RNE; - /* TX FIFO is never filled, so it is always in underrun - condition if SSP is enabled */ - s->sssr |= SSSR_TFS; - s->sssr |= SSSR_TNF; - } - - pxa2xx_ssp_int_update(s); -} - -static uint64_t pxa2xx_ssp_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - uint32_t retval; - - switch (addr) { - case SSCR0: - return s->sscr[0]; - case SSCR1: - return s->sscr[1]; - case SSPSP: - return s->sspsp; - case SSTO: - return s->ssto; - case SSITR: - return s->ssitr; - case SSSR: - return s->sssr | s->ssitr; - case SSDR: - if (!s->enable) - return 0xffffffff; - if (s->rx_level < 1) { - printf("%s: SSP Rx Underrun\n", __func__); - return 0xffffffff; - } - s->rx_level --; - retval = s->rx_fifo[s->rx_start ++]; - s->rx_start &= 0xf; - pxa2xx_ssp_fifo_update(s); - return retval; - case SSTSA: - return s->sstsa; - case SSRSA: - return s->ssrsa; - case SSTSS: - return 0; - case SSACD: - return s->ssacd; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_ssp_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - uint32_t value = value64; - - switch (addr) { - case SSCR0: - s->sscr[0] = value & 0xc7ffffff; - s->enable = value & SSCR0_SSE; - if (value & SSCR0_MOD) - printf("%s: Attempt to use network mode\n", __func__); - if (s->enable && SSCR0_DSS(value) < 4) - printf("%s: Wrong data size: %u bits\n", __func__, - SSCR0_DSS(value)); - if (!(value & SSCR0_SSE)) { - s->sssr = 0; - s->ssitr = 0; - s->rx_level = 0; - } - pxa2xx_ssp_fifo_update(s); - break; - - case SSCR1: - s->sscr[1] = value; - if (value & (SSCR1_LBM | SSCR1_EFWR)) - printf("%s: Attempt to use SSP test mode\n", __func__); - pxa2xx_ssp_fifo_update(s); - break; - - case SSPSP: - s->sspsp = value; - break; - - case SSTO: - s->ssto = value; - break; - - case SSITR: - s->ssitr = value & SSITR_INT; - pxa2xx_ssp_int_update(s); - break; - - case SSSR: - s->sssr &= ~(value & SSSR_RW); - pxa2xx_ssp_int_update(s); - break; - - case SSDR: - if (SSCR0_UWIRE(s->sscr[0])) { - if (s->sscr[1] & SSCR1_MWDS) - value &= 0xffff; - else - value &= 0xff; - } else - /* Note how 32bits overflow does no harm here */ - value &= (1 << SSCR0_DSS(s->sscr[0])) - 1; - - /* Data goes from here to the Tx FIFO and is shifted out from - * there directly to the slave, no need to buffer it. - */ - if (s->enable) { - uint32_t readval; - readval = ssi_transfer(s->bus, value); - if (s->rx_level < 0x10) { - s->rx_fifo[(s->rx_start + s->rx_level ++) & 0xf] = readval; - } else { - s->sssr |= SSSR_ROR; - } - } - pxa2xx_ssp_fifo_update(s); - break; - - case SSTSA: - s->sstsa = value; - break; - - case SSRSA: - s->ssrsa = value; - break; - - case SSACD: - s->ssacd = value; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } -} - -static const MemoryRegionOps pxa2xx_ssp_ops = { - .read = pxa2xx_ssp_read, - .write = pxa2xx_ssp_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_ssp_reset(DeviceState *d) -{ - PXA2xxSSPState *s = PXA2XX_SSP(d); - - s->enable = 0; - s->sscr[0] = s->sscr[1] = 0; - s->sspsp = 0; - s->ssto = 0; - s->ssitr = 0; - s->sssr = 0; - s->sstsa = 0; - s->ssrsa = 0; - s->ssacd = 0; - s->rx_start = s->rx_level = 0; -} - -static void pxa2xx_ssp_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - PXA2xxSSPState *s = PXA2XX_SSP(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - sysbus_init_irq(sbd, &s->irq); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_ssp_ops, s, - "pxa2xx-ssp", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - - s->bus = ssi_create_bus(dev, "ssi"); -} - -/* Real-Time Clock */ -#define RCNR 0x00 /* RTC Counter register */ -#define RTAR 0x04 /* RTC Alarm register */ -#define RTSR 0x08 /* RTC Status register */ -#define RTTR 0x0c /* RTC Timer Trim register */ -#define RDCR 0x10 /* RTC Day Counter register */ -#define RYCR 0x14 /* RTC Year Counter register */ -#define RDAR1 0x18 /* RTC Wristwatch Day Alarm register 1 */ -#define RYAR1 0x1c /* RTC Wristwatch Year Alarm register 1 */ -#define RDAR2 0x20 /* RTC Wristwatch Day Alarm register 2 */ -#define RYAR2 0x24 /* RTC Wristwatch Year Alarm register 2 */ -#define SWCR 0x28 /* RTC Stopwatch Counter register */ -#define SWAR1 0x2c /* RTC Stopwatch Alarm register 1 */ -#define SWAR2 0x30 /* RTC Stopwatch Alarm register 2 */ -#define RTCPICR 0x34 /* RTC Periodic Interrupt Counter register */ -#define PIAR 0x38 /* RTC Periodic Interrupt Alarm register */ - -#define TYPE_PXA2XX_RTC "pxa2xx_rtc" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxRTCState, PXA2XX_RTC) - -struct PXA2xxRTCState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - uint32_t rttr; - uint32_t rtsr; - uint32_t rtar; - uint32_t rdar1; - uint32_t rdar2; - uint32_t ryar1; - uint32_t ryar2; - uint32_t swar1; - uint32_t swar2; - uint32_t piar; - uint32_t last_rcnr; - uint32_t last_rdcr; - uint32_t last_rycr; - uint32_t last_swcr; - uint32_t last_rtcpicr; - int64_t last_hz; - int64_t last_sw; - int64_t last_pi; - QEMUTimer *rtc_hz; - QEMUTimer *rtc_rdal1; - QEMUTimer *rtc_rdal2; - QEMUTimer *rtc_swal1; - QEMUTimer *rtc_swal2; - QEMUTimer *rtc_pi; - qemu_irq rtc_irq; -}; - -static inline void pxa2xx_rtc_int_update(PXA2xxRTCState *s) -{ - qemu_set_irq(s->rtc_irq, !!(s->rtsr & 0x2553)); -} - -static void pxa2xx_rtc_hzupdate(PXA2xxRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - s->last_rcnr += ((rt - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - s->last_rdcr += ((rt - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - s->last_hz = rt; -} - -static void pxa2xx_rtc_swupdate(PXA2xxRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - if (s->rtsr & (1 << 12)) - s->last_swcr += (rt - s->last_sw) / 10; - s->last_sw = rt; -} - -static void pxa2xx_rtc_piupdate(PXA2xxRTCState *s) -{ - int64_t rt = qemu_clock_get_ms(rtc_clock); - if (s->rtsr & (1 << 15)) - s->last_swcr += rt - s->last_pi; - s->last_pi = rt; -} - -static inline void pxa2xx_rtc_alarm_update(PXA2xxRTCState *s, - uint32_t rtsr) -{ - if ((rtsr & (1 << 2)) && !(rtsr & (1 << 0))) - timer_mod(s->rtc_hz, s->last_hz + - (((s->rtar - s->last_rcnr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); - else - timer_del(s->rtc_hz); - - if ((rtsr & (1 << 5)) && !(rtsr & (1 << 4))) - timer_mod(s->rtc_rdal1, s->last_hz + - (((s->rdar1 - s->last_rdcr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */ - else - timer_del(s->rtc_rdal1); - - if ((rtsr & (1 << 7)) && !(rtsr & (1 << 6))) - timer_mod(s->rtc_rdal2, s->last_hz + - (((s->rdar2 - s->last_rdcr) * 1000 * - ((s->rttr & 0xffff) + 1)) >> 15)); /* TODO: fixup */ - else - timer_del(s->rtc_rdal2); - - if ((rtsr & 0x1200) == 0x1200 && !(rtsr & (1 << 8))) - timer_mod(s->rtc_swal1, s->last_sw + - (s->swar1 - s->last_swcr) * 10); /* TODO: fixup */ - else - timer_del(s->rtc_swal1); - - if ((rtsr & 0x1800) == 0x1800 && !(rtsr & (1 << 10))) - timer_mod(s->rtc_swal2, s->last_sw + - (s->swar2 - s->last_swcr) * 10); /* TODO: fixup */ - else - timer_del(s->rtc_swal2); - - if ((rtsr & 0xc000) == 0xc000 && !(rtsr & (1 << 13))) - timer_mod(s->rtc_pi, s->last_pi + - (s->piar & 0xffff) - s->last_rtcpicr); - else - timer_del(s->rtc_pi); -} - -static inline void pxa2xx_rtc_hz_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 0); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_rdal1_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 4); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_rdal2_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 6); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_swal1_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 8); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_swal2_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 10); - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static inline void pxa2xx_rtc_pi_tick(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - s->rtsr |= (1 << 13); - pxa2xx_rtc_piupdate(s); - s->last_rtcpicr = 0; - pxa2xx_rtc_alarm_update(s, s->rtsr); - pxa2xx_rtc_int_update(s); -} - -static uint64_t pxa2xx_rtc_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - - switch (addr) { - case RTTR: - return s->rttr; - case RTSR: - return s->rtsr; - case RTAR: - return s->rtar; - case RDAR1: - return s->rdar1; - case RDAR2: - return s->rdar2; - case RYAR1: - return s->ryar1; - case RYAR2: - return s->ryar2; - case SWAR1: - return s->swar1; - case SWAR2: - return s->swar2; - case PIAR: - return s->piar; - case RCNR: - return s->last_rcnr + - ((qemu_clock_get_ms(rtc_clock) - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - case RDCR: - return s->last_rdcr + - ((qemu_clock_get_ms(rtc_clock) - s->last_hz) << 15) / - (1000 * ((s->rttr & 0xffff) + 1)); - case RYCR: - return s->last_rycr; - case SWCR: - if (s->rtsr & (1 << 12)) - return s->last_swcr + - (qemu_clock_get_ms(rtc_clock) - s->last_sw) / 10; - else - return s->last_swcr; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_rtc_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - uint32_t value = value64; - - switch (addr) { - case RTTR: - if (!(s->rttr & (1U << 31))) { - pxa2xx_rtc_hzupdate(s); - s->rttr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - } - break; - - case RTSR: - if ((s->rtsr ^ value) & (1 << 15)) - pxa2xx_rtc_piupdate(s); - - if ((s->rtsr ^ value) & (1 << 12)) - pxa2xx_rtc_swupdate(s); - - if (((s->rtsr ^ value) & 0x4aac) | (value & ~0xdaac)) - pxa2xx_rtc_alarm_update(s, value); - - s->rtsr = (value & 0xdaac) | (s->rtsr & ~(value & ~0xdaac)); - pxa2xx_rtc_int_update(s); - break; - - case RTAR: - s->rtar = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RDAR1: - s->rdar1 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RDAR2: - s->rdar2 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RYAR1: - s->ryar1 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RYAR2: - s->ryar2 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case SWAR1: - pxa2xx_rtc_swupdate(s); - s->swar1 = value; - s->last_swcr = 0; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case SWAR2: - s->swar2 = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case PIAR: - s->piar = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RCNR: - pxa2xx_rtc_hzupdate(s); - s->last_rcnr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RDCR: - pxa2xx_rtc_hzupdate(s); - s->last_rdcr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RYCR: - s->last_rycr = value; - break; - - case SWCR: - pxa2xx_rtc_swupdate(s); - s->last_swcr = value; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - case RTCPICR: - pxa2xx_rtc_piupdate(s); - s->last_rtcpicr = value & 0xffff; - pxa2xx_rtc_alarm_update(s, s->rtsr); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - } -} - -static const MemoryRegionOps pxa2xx_rtc_ops = { - .read = pxa2xx_rtc_read, - .write = pxa2xx_rtc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_rtc_init(Object *obj) -{ - PXA2xxRTCState *s = PXA2XX_RTC(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - struct tm tm; - int wom; - - s->rttr = 0x7fff; - s->rtsr = 0; - - qemu_get_timedate(&tm, 0); - wom = ((tm.tm_mday - 1) / 7) + 1; - - s->last_rcnr = (uint32_t) mktimegm(&tm); - s->last_rdcr = (wom << 20) | ((tm.tm_wday + 1) << 17) | - (tm.tm_hour << 12) | (tm.tm_min << 6) | tm.tm_sec; - s->last_rycr = ((tm.tm_year + 1900) << 9) | - ((tm.tm_mon + 1) << 5) | tm.tm_mday; - s->last_swcr = (tm.tm_hour << 19) | - (tm.tm_min << 13) | (tm.tm_sec << 7); - s->last_rtcpicr = 0; - s->last_hz = s->last_sw = s->last_pi = qemu_clock_get_ms(rtc_clock); - - sysbus_init_irq(dev, &s->rtc_irq); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_rtc_ops, s, - "pxa2xx-rtc", 0x10000); - sysbus_init_mmio(dev, &s->iomem); -} - -static void pxa2xx_rtc_realize(DeviceState *dev, Error **errp) -{ - PXA2xxRTCState *s = PXA2XX_RTC(dev); - s->rtc_hz = timer_new_ms(rtc_clock, pxa2xx_rtc_hz_tick, s); - s->rtc_rdal1 = timer_new_ms(rtc_clock, pxa2xx_rtc_rdal1_tick, s); - s->rtc_rdal2 = timer_new_ms(rtc_clock, pxa2xx_rtc_rdal2_tick, s); - s->rtc_swal1 = timer_new_ms(rtc_clock, pxa2xx_rtc_swal1_tick, s); - s->rtc_swal2 = timer_new_ms(rtc_clock, pxa2xx_rtc_swal2_tick, s); - s->rtc_pi = timer_new_ms(rtc_clock, pxa2xx_rtc_pi_tick, s); -} - -static int pxa2xx_rtc_pre_save(void *opaque) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - - pxa2xx_rtc_hzupdate(s); - pxa2xx_rtc_piupdate(s); - pxa2xx_rtc_swupdate(s); - - return 0; -} - -static int pxa2xx_rtc_post_load(void *opaque, int version_id) -{ - PXA2xxRTCState *s = (PXA2xxRTCState *) opaque; - - pxa2xx_rtc_alarm_update(s, s->rtsr); - - return 0; -} - -static const VMStateDescription vmstate_pxa2xx_rtc_regs = { - .name = "pxa2xx_rtc", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = pxa2xx_rtc_pre_save, - .post_load = pxa2xx_rtc_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(rttr, PXA2xxRTCState), - VMSTATE_UINT32(rtsr, PXA2xxRTCState), - VMSTATE_UINT32(rtar, PXA2xxRTCState), - VMSTATE_UINT32(rdar1, PXA2xxRTCState), - VMSTATE_UINT32(rdar2, PXA2xxRTCState), - VMSTATE_UINT32(ryar1, PXA2xxRTCState), - VMSTATE_UINT32(ryar2, PXA2xxRTCState), - VMSTATE_UINT32(swar1, PXA2xxRTCState), - VMSTATE_UINT32(swar2, PXA2xxRTCState), - VMSTATE_UINT32(piar, PXA2xxRTCState), - VMSTATE_UINT32(last_rcnr, PXA2xxRTCState), - VMSTATE_UINT32(last_rdcr, PXA2xxRTCState), - VMSTATE_UINT32(last_rycr, PXA2xxRTCState), - VMSTATE_UINT32(last_swcr, PXA2xxRTCState), - VMSTATE_UINT32(last_rtcpicr, PXA2xxRTCState), - VMSTATE_INT64(last_hz, PXA2xxRTCState), - VMSTATE_INT64(last_sw, PXA2xxRTCState), - VMSTATE_INT64(last_pi, PXA2xxRTCState), - VMSTATE_END_OF_LIST(), - }, -}; - -static void pxa2xx_rtc_sysbus_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA2xx RTC Controller"; - dc->vmsd = &vmstate_pxa2xx_rtc_regs; - dc->realize = pxa2xx_rtc_realize; -} - -static const TypeInfo pxa2xx_rtc_sysbus_info = { - .name = TYPE_PXA2XX_RTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxRTCState), - .instance_init = pxa2xx_rtc_init, - .class_init = pxa2xx_rtc_sysbus_class_init, -}; - -/* I2C Interface */ - -#define TYPE_PXA2XX_I2C_SLAVE "pxa2xx-i2c-slave" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxI2CSlaveState, PXA2XX_I2C_SLAVE) - -struct PXA2xxI2CSlaveState { - I2CSlave parent_obj; - - PXA2xxI2CState *host; -}; - -struct PXA2xxI2CState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - PXA2xxI2CSlaveState *slave; - I2CBus *bus; - qemu_irq irq; - uint32_t offset; - uint32_t region_size; - - uint16_t control; - uint16_t status; - uint8_t ibmr; - uint8_t data; -}; - -#define IBMR 0x80 /* I2C Bus Monitor register */ -#define IDBR 0x88 /* I2C Data Buffer register */ -#define ICR 0x90 /* I2C Control register */ -#define ISR 0x98 /* I2C Status register */ -#define ISAR 0xa0 /* I2C Slave Address register */ - -static void pxa2xx_i2c_update(PXA2xxI2CState *s) -{ - uint16_t level = 0; - level |= s->status & s->control & (1 << 10); /* BED */ - level |= (s->status & (1 << 7)) && (s->control & (1 << 9)); /* IRF */ - level |= (s->status & (1 << 6)) && (s->control & (1 << 8)); /* ITE */ - level |= s->status & (1 << 9); /* SAD */ - qemu_set_irq(s->irq, !!level); -} - -/* These are only stubs now. */ -static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) -{ - PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); - PXA2xxI2CState *s = slave->host; - - switch (event) { - case I2C_START_SEND: - s->status |= (1 << 9); /* set SAD */ - s->status &= ~(1 << 0); /* clear RWM */ - break; - case I2C_START_RECV: - s->status |= (1 << 9); /* set SAD */ - s->status |= 1 << 0; /* set RWM */ - break; - case I2C_FINISH: - s->status |= (1 << 4); /* set SSD */ - break; - case I2C_NACK: - s->status |= 1 << 1; /* set ACKNAK */ - break; - default: - return -1; - } - pxa2xx_i2c_update(s); - - return 0; -} - -static uint8_t pxa2xx_i2c_rx(I2CSlave *i2c) -{ - PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); - PXA2xxI2CState *s = slave->host; - - if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) { - return 0; - } - - if (s->status & (1 << 0)) { /* RWM */ - s->status |= 1 << 6; /* set ITE */ - } - pxa2xx_i2c_update(s); - - return s->data; -} - -static int pxa2xx_i2c_tx(I2CSlave *i2c, uint8_t data) -{ - PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); - PXA2xxI2CState *s = slave->host; - - if ((s->control & (1 << 14)) || !(s->control & (1 << 6))) { - return 1; - } - - if (!(s->status & (1 << 0))) { /* RWM */ - s->status |= 1 << 7; /* set IRF */ - s->data = data; - } - pxa2xx_i2c_update(s); - - return 1; -} - -static uint64_t pxa2xx_i2c_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxI2CState *s = (PXA2xxI2CState *) opaque; - I2CSlave *slave; - - addr -= s->offset; - switch (addr) { - case ICR: - return s->control; - case ISR: - return s->status | (i2c_bus_busy(s->bus) << 2); - case ISAR: - slave = I2C_SLAVE(s->slave); - return slave->address; - case IDBR: - return s->data; - case IBMR: - if (s->status & (1 << 2)) - s->ibmr ^= 3; /* Fake SCL and SDA pin changes */ - else - s->ibmr = 0; - return s->ibmr; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_i2c_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxI2CState *s = (PXA2xxI2CState *) opaque; - uint32_t value = value64; - int ack; - - addr -= s->offset; - switch (addr) { - case ICR: - s->control = value & 0xfff7; - if ((value & (1 << 3)) && (value & (1 << 6))) { /* TB and IUE */ - /* TODO: slave mode */ - if (value & (1 << 0)) { /* START condition */ - if (s->data & 1) - s->status |= 1 << 0; /* set RWM */ - else - s->status &= ~(1 << 0); /* clear RWM */ - ack = !i2c_start_transfer(s->bus, s->data >> 1, s->data & 1); - } else { - if (s->status & (1 << 0)) { /* RWM */ - s->data = i2c_recv(s->bus); - if (value & (1 << 2)) /* ACKNAK */ - i2c_nack(s->bus); - ack = 1; - } else - ack = !i2c_send(s->bus, s->data); - } - - if (value & (1 << 1)) /* STOP condition */ - i2c_end_transfer(s->bus); - - if (ack) { - if (value & (1 << 0)) /* START condition */ - s->status |= 1 << 6; /* set ITE */ - else - if (s->status & (1 << 0)) /* RWM */ - s->status |= 1 << 7; /* set IRF */ - else - s->status |= 1 << 6; /* set ITE */ - s->status &= ~(1 << 1); /* clear ACKNAK */ - } else { - s->status |= 1 << 6; /* set ITE */ - s->status |= 1 << 10; /* set BED */ - s->status |= 1 << 1; /* set ACKNAK */ - } - } - if (!(value & (1 << 3)) && (value & (1 << 6))) /* !TB and IUE */ - if (value & (1 << 4)) /* MA */ - i2c_end_transfer(s->bus); - pxa2xx_i2c_update(s); - break; - - case ISR: - s->status &= ~(value & 0x07f0); - pxa2xx_i2c_update(s); - break; - - case ISAR: - i2c_slave_set_address(I2C_SLAVE(s->slave), value & 0x7f); - break; - - case IDBR: - s->data = value & 0xff; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - } -} - -static const MemoryRegionOps pxa2xx_i2c_ops = { - .read = pxa2xx_i2c_read, - .write = pxa2xx_i2c_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_i2c_slave = { - .name = "pxa2xx_i2c_slave", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_I2C_SLAVE(parent_obj, PXA2xxI2CSlaveState), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_pxa2xx_i2c = { - .name = "pxa2xx_i2c", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT16(control, PXA2xxI2CState), - VMSTATE_UINT16(status, PXA2xxI2CState), - VMSTATE_UINT8(ibmr, PXA2xxI2CState), - VMSTATE_UINT8(data, PXA2xxI2CState), - VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState, - vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState), - VMSTATE_END_OF_LIST() - } -}; - -static void pxa2xx_i2c_slave_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->event = pxa2xx_i2c_event; - k->recv = pxa2xx_i2c_rx; - k->send = pxa2xx_i2c_tx; -} - -static const TypeInfo pxa2xx_i2c_slave_info = { - .name = TYPE_PXA2XX_I2C_SLAVE, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(PXA2xxI2CSlaveState), - .class_init = pxa2xx_i2c_slave_class_init, -}; - -PXA2xxI2CState *pxa2xx_i2c_init(hwaddr base, - qemu_irq irq, uint32_t region_size) -{ - DeviceState *dev; - SysBusDevice *i2c_dev; - PXA2xxI2CState *s; - I2CBus *i2cbus; - - dev = qdev_new(TYPE_PXA2XX_I2C); - qdev_prop_set_uint32(dev, "size", region_size + 1); - qdev_prop_set_uint32(dev, "offset", base & region_size); - - 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)); - s->slave->host = s; - - return s; -} - -static void pxa2xx_i2c_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - PXA2xxI2CState *s = PXA2XX_I2C(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - s->bus = i2c_init_bus(dev, NULL); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_i2c_ops, s, - "pxa2xx-i2c", s->region_size); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); -} - -I2CBus *pxa2xx_i2c_bus(PXA2xxI2CState *s) -{ - return s->bus; -} - -static Property pxa2xx_i2c_properties[] = { - DEFINE_PROP_UINT32("size", PXA2xxI2CState, region_size, 0x10000), - DEFINE_PROP_UINT32("offset", PXA2xxI2CState, offset, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_i2c_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA2xx I2C Bus Controller"; - dc->vmsd = &vmstate_pxa2xx_i2c; - device_class_set_props(dc, pxa2xx_i2c_properties); -} - -static const TypeInfo pxa2xx_i2c_info = { - .name = TYPE_PXA2XX_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxI2CState), - .instance_init = pxa2xx_i2c_initfn, - .class_init = pxa2xx_i2c_class_init, -}; - -/* PXA Inter-IC Sound Controller */ -static void pxa2xx_i2s_reset(PXA2xxI2SState *i2s) -{ - i2s->rx_len = 0; - i2s->tx_len = 0; - i2s->fifo_len = 0; - i2s->clk = 0x1a; - i2s->control[0] = 0x00; - i2s->control[1] = 0x00; - i2s->status = 0x00; - i2s->mask = 0x00; -} - -#define SACR_TFTH(val) ((val >> 8) & 0xf) -#define SACR_RFTH(val) ((val >> 12) & 0xf) -#define SACR_DREC(val) (val & (1 << 3)) -#define SACR_DPRL(val) (val & (1 << 4)) - -static inline void pxa2xx_i2s_update(PXA2xxI2SState *i2s) -{ - int rfs, tfs; - rfs = SACR_RFTH(i2s->control[0]) < i2s->rx_len && - !SACR_DREC(i2s->control[1]); - tfs = (i2s->tx_len || i2s->fifo_len < SACR_TFTH(i2s->control[0])) && - i2s->enable && !SACR_DPRL(i2s->control[1]); - - qemu_set_irq(i2s->rx_dma, rfs); - qemu_set_irq(i2s->tx_dma, tfs); - - i2s->status &= 0xe0; - if (i2s->fifo_len < 16 || !i2s->enable) - i2s->status |= 1 << 0; /* TNF */ - if (i2s->rx_len) - i2s->status |= 1 << 1; /* RNE */ - if (i2s->enable) - i2s->status |= 1 << 2; /* BSY */ - if (tfs) - i2s->status |= 1 << 3; /* TFS */ - if (rfs) - i2s->status |= 1 << 4; /* RFS */ - if (!(i2s->tx_len && i2s->enable)) - i2s->status |= i2s->fifo_len << 8; /* TFL */ - i2s->status |= MAX(i2s->rx_len, 0xf) << 12; /* RFL */ - - qemu_set_irq(i2s->irq, i2s->status & i2s->mask); -} - -#define SACR0 0x00 /* Serial Audio Global Control register */ -#define SACR1 0x04 /* Serial Audio I2S/MSB-Justified Control register */ -#define SASR0 0x0c /* Serial Audio Interface and FIFO Status register */ -#define SAIMR 0x14 /* Serial Audio Interrupt Mask register */ -#define SAICR 0x18 /* Serial Audio Interrupt Clear register */ -#define SADIV 0x60 /* Serial Audio Clock Divider register */ -#define SADR 0x80 /* Serial Audio Data register */ - -static uint64_t pxa2xx_i2s_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - - switch (addr) { - case SACR0: - return s->control[0]; - case SACR1: - return s->control[1]; - case SASR0: - return s->status; - case SAIMR: - return s->mask; - case SAICR: - return 0; - case SADIV: - return s->clk; - case SADR: - if (s->rx_len > 0) { - s->rx_len --; - pxa2xx_i2s_update(s); - return s->codec_in(s->opaque); - } - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_i2s_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - uint32_t *sample; - - switch (addr) { - case SACR0: - if (value & (1 << 3)) /* RST */ - pxa2xx_i2s_reset(s); - s->control[0] = value & 0xff3d; - if (!s->enable && (value & 1) && s->tx_len) { /* ENB */ - for (sample = s->fifo; s->fifo_len > 0; s->fifo_len --, sample ++) - s->codec_out(s->opaque, *sample); - s->status &= ~(1 << 7); /* I2SOFF */ - } - if (value & (1 << 4)) /* EFWR */ - printf("%s: Attempt to use special function\n", __func__); - s->enable = (value & 9) == 1; /* ENB && !RST*/ - pxa2xx_i2s_update(s); - break; - case SACR1: - s->control[1] = value & 0x0039; - if (value & (1 << 5)) /* ENLBF */ - printf("%s: Attempt to use loopback function\n", __func__); - if (value & (1 << 4)) /* DPRL */ - s->fifo_len = 0; - pxa2xx_i2s_update(s); - break; - case SAIMR: - s->mask = value & 0x0078; - pxa2xx_i2s_update(s); - break; - case SAICR: - s->status &= ~(value & (3 << 5)); - pxa2xx_i2s_update(s); - break; - case SADIV: - s->clk = value & 0x007f; - break; - case SADR: - if (s->tx_len && s->enable) { - s->tx_len --; - pxa2xx_i2s_update(s); - s->codec_out(s->opaque, value); - } else if (s->fifo_len < 16) { - s->fifo[s->fifo_len ++] = value; - pxa2xx_i2s_update(s); - } - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - } -} - -static const MemoryRegionOps pxa2xx_i2s_ops = { - .read = pxa2xx_i2s_read, - .write = pxa2xx_i2s_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_i2s = { - .name = "pxa2xx_i2s", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(control, PXA2xxI2SState, 2), - VMSTATE_UINT32(status, PXA2xxI2SState), - VMSTATE_UINT32(mask, PXA2xxI2SState), - VMSTATE_UINT32(clk, PXA2xxI2SState), - VMSTATE_INT32(enable, PXA2xxI2SState), - VMSTATE_INT32(rx_len, PXA2xxI2SState), - VMSTATE_INT32(tx_len, PXA2xxI2SState), - VMSTATE_INT32(fifo_len, PXA2xxI2SState), - VMSTATE_END_OF_LIST() - } -}; - -static void pxa2xx_i2s_data_req(void *opaque, int tx, int rx) -{ - PXA2xxI2SState *s = (PXA2xxI2SState *) opaque; - uint32_t *sample; - - /* Signal FIFO errors */ - if (s->enable && s->tx_len) - s->status |= 1 << 5; /* TUR */ - if (s->enable && s->rx_len) - s->status |= 1 << 6; /* ROR */ - - /* Should be tx - MIN(tx, s->fifo_len) but we don't really need to - * handle the cases where it makes a difference. */ - s->tx_len = tx - s->fifo_len; - s->rx_len = rx; - /* Note that is s->codec_out wasn't set, we wouldn't get called. */ - if (s->enable) - for (sample = s->fifo; s->fifo_len; s->fifo_len --, sample ++) - s->codec_out(s->opaque, *sample); - pxa2xx_i2s_update(s); -} - -static PXA2xxI2SState *pxa2xx_i2s_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma) -{ - PXA2xxI2SState *s = g_new0(PXA2xxI2SState, 1); - - s->irq = irq; - s->rx_dma = rx_dma; - s->tx_dma = tx_dma; - s->data_req = pxa2xx_i2s_data_req; - - pxa2xx_i2s_reset(s); - - memory_region_init_io(&s->iomem, NULL, &pxa2xx_i2s_ops, s, - "pxa2xx-i2s", 0x100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - vmstate_register(NULL, base, &vmstate_pxa2xx_i2s, s); - - return s; -} - -/* PXA Fast Infra-red Communications Port */ -struct PXA2xxFIrState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq; - qemu_irq rx_dma; - qemu_irq tx_dma; - uint32_t enable; - CharBackend chr; - - uint8_t control[3]; - uint8_t status[2]; - - uint32_t rx_len; - uint32_t rx_start; - uint8_t rx_fifo[64]; -}; - -static void pxa2xx_fir_reset(DeviceState *d) -{ - PXA2xxFIrState *s = PXA2XX_FIR(d); - - s->control[0] = 0x00; - s->control[1] = 0x00; - s->control[2] = 0x00; - s->status[0] = 0x00; - s->status[1] = 0x00; - s->enable = 0; -} - -static inline void pxa2xx_fir_update(PXA2xxFIrState *s) -{ - static const int tresh[4] = { 8, 16, 32, 0 }; - int intr = 0; - if ((s->control[0] & (1 << 4)) && /* RXE */ - s->rx_len >= tresh[s->control[2] & 3]) /* TRIG */ - s->status[0] |= 1 << 4; /* RFS */ - else - s->status[0] &= ~(1 << 4); /* RFS */ - if (s->control[0] & (1 << 3)) /* TXE */ - s->status[0] |= 1 << 3; /* TFS */ - else - s->status[0] &= ~(1 << 3); /* TFS */ - if (s->rx_len) - s->status[1] |= 1 << 2; /* RNE */ - else - s->status[1] &= ~(1 << 2); /* RNE */ - if (s->control[0] & (1 << 4)) /* RXE */ - s->status[1] |= 1 << 0; /* RSY */ - else - s->status[1] &= ~(1 << 0); /* RSY */ - - intr |= (s->control[0] & (1 << 5)) && /* RIE */ - (s->status[0] & (1 << 4)); /* RFS */ - intr |= (s->control[0] & (1 << 6)) && /* TIE */ - (s->status[0] & (1 << 3)); /* TFS */ - intr |= (s->control[2] & (1 << 4)) && /* TRAIL */ - (s->status[0] & (1 << 6)); /* EOC */ - intr |= (s->control[0] & (1 << 2)) && /* TUS */ - (s->status[0] & (1 << 1)); /* TUR */ - intr |= s->status[0] & 0x25; /* FRE, RAB, EIF */ - - qemu_set_irq(s->rx_dma, (s->status[0] >> 4) & 1); - qemu_set_irq(s->tx_dma, (s->status[0] >> 3) & 1); - - qemu_set_irq(s->irq, intr && s->enable); -} - -#define ICCR0 0x00 /* FICP Control register 0 */ -#define ICCR1 0x04 /* FICP Control register 1 */ -#define ICCR2 0x08 /* FICP Control register 2 */ -#define ICDR 0x0c /* FICP Data register */ -#define ICSR0 0x14 /* FICP Status register 0 */ -#define ICSR1 0x18 /* FICP Status register 1 */ -#define ICFOR 0x1c /* FICP FIFO Occupancy Status register */ - -static uint64_t pxa2xx_fir_read(void *opaque, hwaddr addr, - unsigned size) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - uint8_t ret; - - switch (addr) { - case ICCR0: - return s->control[0]; - case ICCR1: - return s->control[1]; - case ICCR2: - return s->control[2]; - case ICDR: - s->status[0] &= ~0x01; - s->status[1] &= ~0x72; - if (s->rx_len) { - s->rx_len --; - ret = s->rx_fifo[s->rx_start ++]; - s->rx_start &= 63; - pxa2xx_fir_update(s); - return ret; - } - printf("%s: Rx FIFO underrun.\n", __func__); - break; - case ICSR0: - return s->status[0]; - case ICSR1: - return s->status[1] | (1 << 3); /* TNF */ - case ICFOR: - return s->rx_len; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - break; - } - return 0; -} - -static void pxa2xx_fir_write(void *opaque, hwaddr addr, - uint64_t value64, unsigned size) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - uint32_t value = value64; - uint8_t ch; - - switch (addr) { - case ICCR0: - s->control[0] = value; - if (!(value & (1 << 4))) /* RXE */ - s->rx_len = s->rx_start = 0; - if (!(value & (1 << 3))) { /* TXE */ - /* Nop */ - } - s->enable = value & 1; /* ITR */ - if (!s->enable) - s->status[0] = 0; - pxa2xx_fir_update(s); - break; - case ICCR1: - s->control[1] = value; - break; - case ICCR2: - s->control[2] = value & 0x3f; - pxa2xx_fir_update(s); - break; - case ICDR: - if (s->control[2] & (1 << 2)) { /* TXP */ - ch = value; - } else { - ch = ~value; - } - if (s->enable && (s->control[0] & (1 << 3))) { /* TXE */ - /* 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); - } - break; - case ICSR0: - s->status[0] &= ~(value & 0x66); - pxa2xx_fir_update(s); - break; - case ICFOR: - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, addr); - } -} - -static const MemoryRegionOps pxa2xx_fir_ops = { - .read = pxa2xx_fir_read, - .write = pxa2xx_fir_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pxa2xx_fir_is_empty(void *opaque) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - return (s->rx_len < 64); -} - -static void pxa2xx_fir_rx(void *opaque, const uint8_t *buf, int size) -{ - PXA2xxFIrState *s = (PXA2xxFIrState *) opaque; - if (!(s->control[0] & (1 << 4))) /* RXE */ - return; - - while (size --) { - s->status[1] |= 1 << 4; /* EOF */ - if (s->rx_len >= 64) { - s->status[1] |= 1 << 6; /* ROR */ - break; - } - - if (s->control[2] & (1 << 3)) /* RXP */ - s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = *(buf ++); - else - s->rx_fifo[(s->rx_start + s->rx_len ++) & 63] = ~*(buf ++); - } - - pxa2xx_fir_update(s); -} - -static void pxa2xx_fir_event(void *opaque, QEMUChrEvent event) -{ -} - -static void pxa2xx_fir_instance_init(Object *obj) -{ - PXA2xxFIrState *s = PXA2XX_FIR(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_fir_ops, s, - "pxa2xx-fir", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - sysbus_init_irq(sbd, &s->rx_dma); - sysbus_init_irq(sbd, &s->tx_dma); -} - -static void pxa2xx_fir_realize(DeviceState *dev, Error **errp) -{ - PXA2xxFIrState *s = PXA2XX_FIR(dev); - - qemu_chr_fe_set_handlers(&s->chr, pxa2xx_fir_is_empty, - pxa2xx_fir_rx, pxa2xx_fir_event, NULL, s, NULL, - true); -} - -static bool pxa2xx_fir_vmstate_validate(void *opaque, int version_id) -{ - PXA2xxFIrState *s = opaque; - - return s->rx_start < ARRAY_SIZE(s->rx_fifo); -} - -static const VMStateDescription pxa2xx_fir_vmsd = { - .name = "pxa2xx-fir", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(enable, PXA2xxFIrState), - VMSTATE_UINT8_ARRAY(control, PXA2xxFIrState, 3), - VMSTATE_UINT8_ARRAY(status, PXA2xxFIrState, 2), - VMSTATE_UINT32(rx_len, PXA2xxFIrState), - VMSTATE_UINT32(rx_start, PXA2xxFIrState), - VMSTATE_VALIDATE("fifo is 64 bytes", pxa2xx_fir_vmstate_validate), - VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxFIrState, 64), - VMSTATE_END_OF_LIST() - } -}; - -static Property pxa2xx_fir_properties[] = { - DEFINE_PROP_CHR("chardev", PXA2xxFIrState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_fir_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = pxa2xx_fir_realize; - dc->vmsd = &pxa2xx_fir_vmsd; - device_class_set_props(dc, pxa2xx_fir_properties); - dc->reset = pxa2xx_fir_reset; -} - -static const TypeInfo pxa2xx_fir_info = { - .name = TYPE_PXA2XX_FIR, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxFIrState), - .class_init = pxa2xx_fir_class_init, - .instance_init = pxa2xx_fir_instance_init, -}; - -static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, qemu_irq rx_dma, - qemu_irq tx_dma, - Chardev *chr) -{ - DeviceState *dev; - SysBusDevice *sbd; - - dev = qdev_new(TYPE_PXA2XX_FIR); - qdev_prop_set_chr(dev, "chardev", chr); - sbd = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sbd, &error_fatal); - sysbus_mmio_map(sbd, 0, base); - sysbus_connect_irq(sbd, 0, irq); - sysbus_connect_irq(sbd, 1, rx_dma); - sysbus_connect_irq(sbd, 2, tx_dma); - return PXA2XX_FIR(dev); -} - -static void pxa2xx_reset(void *opaque, int line, int level) -{ - PXA2xxState *s = (PXA2xxState *) opaque; - - if (level && (s->pm_regs[PCFR >> 2] & 0x10)) { /* GPR_EN */ - cpu_reset(CPU(s->cpu)); - /* TODO: reset peripherals */ - } -} - -/* Initialise a PXA270 integrated chip (ARM based core). */ -PXA2xxState *pxa270_init(MemoryRegion *address_space, - unsigned int sdram_size, const char *cpu_type) -{ - PXA2xxState *s; - int i; - DriveInfo *dinfo; - s = g_new0(PXA2xxState, 1); - - if (strncmp(cpu_type, "pxa27", 5)) { - error_report("Machine requires a PXA27x processor"); - exit(1); - } - - s->cpu = ARM_CPU(cpu_create(cpu_type)); - s->reset = qemu_allocate_irq(pxa2xx_reset, s, 0); - - /* SDRAM & Internal Memory Storage */ - memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size, - &error_fatal); - memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); - memory_region_init_ram(&s->internal, NULL, "pxa270.internal", 0x40000, - &error_fatal); - memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, - &s->internal); - - s->pic = pxa2xx_pic_init(0x40d00000, s->cpu); - - s->dma = pxa27x_dma_init(0x40000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA)); - - sysbus_create_varargs("pxa27x-timer", 0x40a00000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), - qdev_get_gpio_in(s->pic, PXA27X_PIC_OST_4_11), - NULL); - - s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121); - - s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); - dinfo = drive_get(IF_SD, 0, 0); - if (dinfo) { - DeviceState *carddev; - - /* Create and plug in the sd card */ - carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive_err(carddev, "drive", - blk_by_legacy_dinfo(dinfo), &error_fatal); - qdev_realize_and_unref(carddev, qdev_get_child_bus(DEVICE(s->mmc), - "sd-bus"), - &error_fatal); - } else if (!qtest_enabled()) { - warn_report("missing SecureDigital device"); - } - - for (i = 0; pxa270_serial[i].io_base; i++) { - if (serial_hd(i)) { - serial_mm_init(address_space, pxa270_serial[i].io_base, 2, - qdev_get_gpio_in(s->pic, pxa270_serial[i].irqn), - 14857000 / 16, serial_hd(i), - DEVICE_NATIVE_ENDIAN); - } else { - break; - } - } - if (serial_hd(i)) - s->fir = pxa2xx_fir_init(address_space, 0x40800000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hd(i)); - - s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); - - s->cm_base = 0x41300000; - s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ - s->clkcfg = 0x00000009; /* Turbo mode active */ - memory_region_init_io(&s->cm_iomem, NULL, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); - memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - - pxa2xx_setup_cp14(s); - - s->mm_base = 0x48000000; - s->mm_regs[MDMRS >> 2] = 0x00020002; - s->mm_regs[MDREFR >> 2] = 0x03ca4000; - s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ - memory_region_init_io(&s->mm_iomem, NULL, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000); - memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); - - s->pm_base = 0x40f00000; - memory_region_init_io(&s->pm_iomem, NULL, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100); - memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); - - for (i = 0; pxa27x_ssp[i].io_base; i ++); - s->ssp = g_new0(SSIBus *, i); - for (i = 0; pxa27x_ssp[i].io_base; i ++) { - DeviceState *dev; - dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa27x_ssp[i].io_base, - qdev_get_gpio_in(s->pic, pxa27x_ssp[i].irqn)); - s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); - } - - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); - s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); - - sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); - - s->i2c[0] = pxa2xx_i2c_init(0x40301600, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff); - s->i2c[1] = pxa2xx_i2c_init(0x40f00100, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); - - s->i2s = pxa2xx_i2s_init(address_space, 0x40400000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); - - s->kp = pxa27x_keypad_init(address_space, 0x41500000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_KEYPAD)); - - /* GPIO1 resets the processor */ - /* The handler can be overridden by board-specific code */ - qdev_connect_gpio_out(s->gpio, 1, s->reset); - return s; -} - -/* Initialise a PXA255 integrated chip (ARM based core). */ -PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) -{ - PXA2xxState *s; - int i; - DriveInfo *dinfo; - - s = g_new0(PXA2xxState, 1); - - s->cpu = ARM_CPU(cpu_create(ARM_CPU_TYPE_NAME("pxa255"))); - s->reset = qemu_allocate_irq(pxa2xx_reset, s, 0); - - /* SDRAM & Internal Memory Storage */ - memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size, - &error_fatal); - memory_region_add_subregion(address_space, PXA2XX_SDRAM_BASE, &s->sdram); - memory_region_init_ram(&s->internal, NULL, "pxa255.internal", - PXA2XX_INTERNAL_SIZE, &error_fatal); - memory_region_add_subregion(address_space, PXA2XX_INTERNAL_BASE, - &s->internal); - - s->pic = pxa2xx_pic_init(0x40d00000, s->cpu); - - s->dma = pxa255_dma_init(0x40000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_DMA)); - - sysbus_create_varargs("pxa25x-timer", 0x40a00000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 0), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 1), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 2), - qdev_get_gpio_in(s->pic, PXA2XX_PIC_OST_0 + 3), - NULL); - - s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85); - - s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); - dinfo = drive_get(IF_SD, 0, 0); - if (dinfo) { - DeviceState *carddev; - - /* Create and plug in the sd card */ - carddev = qdev_new(TYPE_SD_CARD); - qdev_prop_set_drive_err(carddev, "drive", - blk_by_legacy_dinfo(dinfo), &error_fatal); - qdev_realize_and_unref(carddev, qdev_get_child_bus(DEVICE(s->mmc), - "sd-bus"), - &error_fatal); - } else if (!qtest_enabled()) { - warn_report("missing SecureDigital device"); - } - - for (i = 0; pxa255_serial[i].io_base; i++) { - if (serial_hd(i)) { - serial_mm_init(address_space, pxa255_serial[i].io_base, 2, - qdev_get_gpio_in(s->pic, pxa255_serial[i].irqn), - 14745600 / 16, serial_hd(i), - DEVICE_NATIVE_ENDIAN); - } else { - break; - } - } - if (serial_hd(i)) - s->fir = pxa2xx_fir_init(address_space, 0x40800000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hd(i)); - - s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); - - s->cm_base = 0x41300000; - s->cm_regs[CCCR >> 2] = 0x00000121; /* from datasheet */ - s->cm_regs[CKEN >> 2] = 0x00017def; /* from datasheet */ - - s->clkcfg = 0x00000009; /* Turbo mode active */ - memory_region_init_io(&s->cm_iomem, NULL, &pxa2xx_cm_ops, s, "pxa2xx-cm", 0x1000); - memory_region_add_subregion(address_space, s->cm_base, &s->cm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_cm, s); - - pxa2xx_setup_cp14(s); - - s->mm_base = 0x48000000; - s->mm_regs[MDMRS >> 2] = 0x00020002; - s->mm_regs[MDREFR >> 2] = 0x03ca4000; - s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ - memory_region_init_io(&s->mm_iomem, NULL, &pxa2xx_mm_ops, s, "pxa2xx-mm", 0x1000); - memory_region_add_subregion(address_space, s->mm_base, &s->mm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_mm, s); - - s->pm_base = 0x40f00000; - memory_region_init_io(&s->pm_iomem, NULL, &pxa2xx_pm_ops, s, "pxa2xx-pm", 0x100); - memory_region_add_subregion(address_space, s->pm_base, &s->pm_iomem); - vmstate_register(NULL, 0, &vmstate_pxa2xx_pm, s); - - for (i = 0; pxa255_ssp[i].io_base; i ++); - s->ssp = g_new0(SSIBus *, i); - for (i = 0; pxa255_ssp[i].io_base; i ++) { - DeviceState *dev; - dev = sysbus_create_simple(TYPE_PXA2XX_SSP, pxa255_ssp[i].io_base, - qdev_get_gpio_in(s->pic, pxa255_ssp[i].irqn)); - 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); - - sysbus_create_simple(TYPE_PXA2XX_RTC, 0x40900000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_RTCALARM)); - - s->i2c[0] = pxa2xx_i2c_init(0x40301600, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2C), 0xffff); - s->i2c[1] = pxa2xx_i2c_init(0x40f00100, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_PWRI2C), 0xff); - - s->i2s = pxa2xx_i2s_init(address_space, 0x40400000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_I2S), - qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_I2S)); - - /* GPIO1 resets the processor */ - /* The handler can be overridden by board-specific code */ - qdev_connect_gpio_out(s->gpio, 1, s->reset); - return s; -} - -static void pxa2xx_ssp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = pxa2xx_ssp_reset; - dc->vmsd = &vmstate_pxa2xx_ssp; -} - -static const TypeInfo pxa2xx_ssp_info = { - .name = TYPE_PXA2XX_SSP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxSSPState), - .instance_init = pxa2xx_ssp_init, - .class_init = pxa2xx_ssp_class_init, -}; - -static void pxa2xx_register_types(void) -{ - type_register_static(&pxa2xx_i2c_slave_info); - type_register_static(&pxa2xx_ssp_info); - type_register_static(&pxa2xx_i2c_info); - type_register_static(&pxa2xx_rtc_sysbus_info); - type_register_static(&pxa2xx_fir_info); -} - -type_init(pxa2xx_register_types) diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c deleted file mode 100644 index e7c3d99224..0000000000 --- a/hw/arm/pxa2xx_gpio.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Intel XScale PXA255/270 GPIO controller emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "hw/arm/pxa.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qom/object.h" - -#define PXA2XX_GPIO_BANKS 4 - -#define TYPE_PXA2XX_GPIO "pxa2xx-gpio" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxGPIOInfo, PXA2XX_GPIO) - -struct PXA2xxGPIOInfo { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - qemu_irq irq0, irq1, irqX; - int lines; - int ncpu; - ARMCPU *cpu; - - /* XXX: GNU C vectors are more suitable */ - uint32_t ilevel[PXA2XX_GPIO_BANKS]; - uint32_t olevel[PXA2XX_GPIO_BANKS]; - uint32_t dir[PXA2XX_GPIO_BANKS]; - uint32_t rising[PXA2XX_GPIO_BANKS]; - uint32_t falling[PXA2XX_GPIO_BANKS]; - uint32_t status[PXA2XX_GPIO_BANKS]; - uint32_t gafr[PXA2XX_GPIO_BANKS * 2]; - - uint32_t prev_level[PXA2XX_GPIO_BANKS]; - qemu_irq handler[PXA2XX_GPIO_BANKS * 32]; - qemu_irq read_notify; -}; - -static struct { - enum { - GPIO_NONE, - GPLR, - GPSR, - GPCR, - GPDR, - GRER, - GFER, - GEDR, - GAFR_L, - GAFR_U, - } reg; - int bank; -} pxa2xx_gpio_regs[0x200] = { - [0 ... 0x1ff] = { GPIO_NONE, 0 }, -#define PXA2XX_REG(reg, a0, a1, a2, a3) \ - [a0] = { reg, 0 }, [a1] = { reg, 1 }, [a2] = { reg, 2 }, [a3] = { reg, 3 }, - - PXA2XX_REG(GPLR, 0x000, 0x004, 0x008, 0x100) - PXA2XX_REG(GPSR, 0x018, 0x01c, 0x020, 0x118) - PXA2XX_REG(GPCR, 0x024, 0x028, 0x02c, 0x124) - PXA2XX_REG(GPDR, 0x00c, 0x010, 0x014, 0x10c) - PXA2XX_REG(GRER, 0x030, 0x034, 0x038, 0x130) - PXA2XX_REG(GFER, 0x03c, 0x040, 0x044, 0x13c) - PXA2XX_REG(GEDR, 0x048, 0x04c, 0x050, 0x148) - PXA2XX_REG(GAFR_L, 0x054, 0x05c, 0x064, 0x06c) - PXA2XX_REG(GAFR_U, 0x058, 0x060, 0x068, 0x070) -}; - -static void pxa2xx_gpio_irq_update(PXA2xxGPIOInfo *s) -{ - if (s->status[0] & (1 << 0)) - qemu_irq_raise(s->irq0); - else - qemu_irq_lower(s->irq0); - - if (s->status[0] & (1 << 1)) - qemu_irq_raise(s->irq1); - else - qemu_irq_lower(s->irq1); - - if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3]) - qemu_irq_raise(s->irqX); - else - qemu_irq_lower(s->irqX); -} - -/* Bitmap of pins used as standby and sleep wake-up sources. */ -static const int pxa2xx_gpio_wake[PXA2XX_GPIO_BANKS] = { - 0x8003fe1b, 0x002001fc, 0xec080000, 0x0012007f, -}; - -static void pxa2xx_gpio_set(void *opaque, int line, int level) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - CPUState *cpu = CPU(s->cpu); - int bank; - uint32_t mask; - - if (line >= s->lines) { - printf("%s: No GPIO pin %i\n", __func__, line); - return; - } - - bank = line >> 5; - mask = 1U << (line & 31); - - if (level) { - s->status[bank] |= s->rising[bank] & mask & - ~s->ilevel[bank] & ~s->dir[bank]; - s->ilevel[bank] |= mask; - } else { - s->status[bank] |= s->falling[bank] & mask & - s->ilevel[bank] & ~s->dir[bank]; - s->ilevel[bank] &= ~mask; - } - - if (s->status[bank] & mask) - pxa2xx_gpio_irq_update(s); - - /* Wake-up GPIOs */ - if (cpu->halted && (mask & ~s->dir[bank] & pxa2xx_gpio_wake[bank])) { - cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); - } -} - -static void pxa2xx_gpio_handler_update(PXA2xxGPIOInfo *s) { - uint32_t level, diff; - int i, bit, line; - for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { - level = s->olevel[i] & s->dir[i]; - - for (diff = s->prev_level[i] ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - line = bit + 32 * i; - qemu_set_irq(s->handler[line], (level >> bit) & 1); - } - - s->prev_level[i] = level; - } -} - -static uint64_t pxa2xx_gpio_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - uint32_t ret; - int bank; - if (offset >= 0x200) - return 0; - - bank = pxa2xx_gpio_regs[offset].bank; - switch (pxa2xx_gpio_regs[offset].reg) { - case GPDR: /* GPIO Pin-Direction registers */ - return s->dir[bank]; - - case GPSR: /* GPIO Pin-Output Set registers */ - qemu_log_mask(LOG_GUEST_ERROR, - "pxa2xx GPIO: read from write only register GPSR\n"); - return 0; - - case GPCR: /* GPIO Pin-Output Clear registers */ - qemu_log_mask(LOG_GUEST_ERROR, - "pxa2xx GPIO: read from write only register GPCR\n"); - return 0; - - case GRER: /* GPIO Rising-Edge Detect Enable registers */ - return s->rising[bank]; - - case GFER: /* GPIO Falling-Edge Detect Enable registers */ - return s->falling[bank]; - - case GAFR_L: /* GPIO Alternate Function registers */ - return s->gafr[bank * 2]; - - case GAFR_U: /* GPIO Alternate Function registers */ - return s->gafr[bank * 2 + 1]; - - case GPLR: /* GPIO Pin-Level registers */ - ret = (s->olevel[bank] & s->dir[bank]) | - (s->ilevel[bank] & ~s->dir[bank]); - qemu_irq_raise(s->read_notify); - return ret; - - case GEDR: /* GPIO Edge Detect Status registers */ - return s->status[bank]; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", - __func__, offset); - } - - return 0; -} - -static void pxa2xx_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - int bank; - if (offset >= 0x200) - return; - - bank = pxa2xx_gpio_regs[offset].bank; - switch (pxa2xx_gpio_regs[offset].reg) { - case GPDR: /* GPIO Pin-Direction registers */ - s->dir[bank] = value; - pxa2xx_gpio_handler_update(s); - break; - - case GPSR: /* GPIO Pin-Output Set registers */ - s->olevel[bank] |= value; - pxa2xx_gpio_handler_update(s); - break; - - case GPCR: /* GPIO Pin-Output Clear registers */ - s->olevel[bank] &= ~value; - pxa2xx_gpio_handler_update(s); - break; - - case GRER: /* GPIO Rising-Edge Detect Enable registers */ - s->rising[bank] = value; - break; - - case GFER: /* GPIO Falling-Edge Detect Enable registers */ - s->falling[bank] = value; - break; - - case GAFR_L: /* GPIO Alternate Function registers */ - s->gafr[bank * 2] = value; - break; - - case GAFR_U: /* GPIO Alternate Function registers */ - s->gafr[bank * 2 + 1] = value; - break; - - case GEDR: /* GPIO Edge Detect Status registers */ - s->status[bank] &= ~value; - pxa2xx_gpio_irq_update(s); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", - __func__, offset); - } -} - -static const MemoryRegionOps pxa_gpio_ops = { - .read = pxa2xx_gpio_read, - .write = pxa2xx_gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -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); - 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(pic, PXA2XX_PIC_GPIO_0)); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, - qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_1)); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, - qdev_get_gpio_in(pic, PXA2XX_PIC_GPIO_X)); - - return dev; -} - -static void pxa2xx_gpio_initfn(Object *obj) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - DeviceState *dev = DEVICE(sbd); - PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - - memory_region_init_io(&s->iomem, obj, &pxa_gpio_ops, - s, "pxa2xx-gpio", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq0); - sysbus_init_irq(sbd, &s->irq1); - sysbus_init_irq(sbd, &s->irqX); -} - -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); -} - -/* - * Registers a callback to notify on GPLR reads. This normally - * shouldn't be needed but it is used for the hack on Spitz machines. - */ -void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler) -{ - PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - - s->read_notify = handler; -} - -static const VMStateDescription vmstate_pxa2xx_gpio_regs = { - .name = "pxa2xx-gpio", - .version_id = 1, - .minimum_version_id = 1, - .fields = (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), - VMSTATE_UINT32_ARRAY(rising, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(falling, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(status, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_UINT32_ARRAY(gafr, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS * 2), - VMSTATE_UINT32_ARRAY(prev_level, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property pxa2xx_gpio_properties[] = { - DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), - DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA2xx GPIO controller"; - device_class_set_props(dc, pxa2xx_gpio_properties); - dc->vmsd = &vmstate_pxa2xx_gpio_regs; - dc->realize = pxa2xx_gpio_realize; -} - -static const TypeInfo pxa2xx_gpio_info = { - .name = TYPE_PXA2XX_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxGPIOInfo), - .instance_init = pxa2xx_gpio_initfn, - .class_init = pxa2xx_gpio_class_init, -}; - -static void pxa2xx_gpio_register_types(void) -{ - type_register_static(&pxa2xx_gpio_info); -} - -type_init(pxa2xx_gpio_register_types) diff --git a/hw/arm/pxa2xx_pic.c b/hw/arm/pxa2xx_pic.c deleted file mode 100644 index 47132ab982..0000000000 --- a/hw/arm/pxa2xx_pic.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Intel XScale PXA Programmable Interrupt Controller. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "qemu/log.h" -#include "cpu.h" -#include "hw/arm/pxa.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qom/object.h" -#include "target/arm/cpregs.h" - -#define ICIP 0x00 /* Interrupt Controller IRQ Pending register */ -#define ICMR 0x04 /* Interrupt Controller Mask register */ -#define ICLR 0x08 /* Interrupt Controller Level register */ -#define ICFP 0x0c /* Interrupt Controller FIQ Pending register */ -#define ICPR 0x10 /* Interrupt Controller Pending register */ -#define ICCR 0x14 /* Interrupt Controller Control register */ -#define ICHP 0x18 /* Interrupt Controller Highest Priority register */ -#define IPR0 0x1c /* Interrupt Controller Priority register 0 */ -#define IPR31 0x98 /* Interrupt Controller Priority register 31 */ -#define ICIP2 0x9c /* Interrupt Controller IRQ Pending register 2 */ -#define ICMR2 0xa0 /* Interrupt Controller Mask register 2 */ -#define ICLR2 0xa4 /* Interrupt Controller Level register 2 */ -#define ICFP2 0xa8 /* Interrupt Controller FIQ Pending register 2 */ -#define ICPR2 0xac /* Interrupt Controller Pending register 2 */ -#define IPR32 0xb0 /* Interrupt Controller Priority register 32 */ -#define IPR39 0xcc /* Interrupt Controller Priority register 39 */ - -#define PXA2XX_PIC_SRCS 40 - -#define TYPE_PXA2XX_PIC "pxa2xx_pic" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxPICState, PXA2XX_PIC) - -struct PXA2xxPICState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - - MemoryRegion iomem; - ARMCPU *cpu; - uint32_t int_enabled[2]; - uint32_t int_pending[2]; - uint32_t is_fiq[2]; - uint32_t int_idle; - uint32_t priority[PXA2XX_PIC_SRCS]; -}; - -static void pxa2xx_pic_update(void *opaque) -{ - uint32_t mask[2]; - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - CPUState *cpu = CPU(s->cpu); - - if (cpu->halted) { - mask[0] = s->int_pending[0] & (s->int_enabled[0] | s->int_idle); - mask[1] = s->int_pending[1] & (s->int_enabled[1] | s->int_idle); - if (mask[0] || mask[1]) { - cpu_interrupt(cpu, CPU_INTERRUPT_EXITTB); - } - } - - mask[0] = s->int_pending[0] & s->int_enabled[0]; - mask[1] = s->int_pending[1] & s->int_enabled[1]; - - if ((mask[0] & s->is_fiq[0]) || (mask[1] & s->is_fiq[1])) { - cpu_interrupt(cpu, CPU_INTERRUPT_FIQ); - } else { - cpu_reset_interrupt(cpu, CPU_INTERRUPT_FIQ); - } - - if ((mask[0] & ~s->is_fiq[0]) || (mask[1] & ~s->is_fiq[1])) { - cpu_interrupt(cpu, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); - } -} - -/* Note: Here level means state of the signal on a pin, not - * IRQ/FIQ distinction as in PXA Developer Manual. */ -static void pxa2xx_pic_set_irq(void *opaque, int irq, int level) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - int int_set = (irq >= 32); - irq &= 31; - - if (level) - s->int_pending[int_set] |= 1 << irq; - else - s->int_pending[int_set] &= ~(1 << irq); - - pxa2xx_pic_update(opaque); -} - -static inline uint32_t pxa2xx_pic_highest(PXA2xxPICState *s) { - int i, int_set, irq; - uint32_t bit, mask[2]; - uint32_t ichp = 0x003f003f; /* Both IDs invalid */ - - mask[0] = s->int_pending[0] & s->int_enabled[0]; - mask[1] = s->int_pending[1] & s->int_enabled[1]; - - for (i = PXA2XX_PIC_SRCS - 1; i >= 0; i --) { - irq = s->priority[i] & 0x3f; - if ((s->priority[i] & (1U << 31)) && irq < PXA2XX_PIC_SRCS) { - /* Source peripheral ID is valid. */ - bit = 1 << (irq & 31); - int_set = (irq >= 32); - - if (mask[int_set] & bit & s->is_fiq[int_set]) { - /* FIQ asserted */ - ichp &= 0xffff0000; - ichp |= (1 << 15) | irq; - } - - if (mask[int_set] & bit & ~s->is_fiq[int_set]) { - /* IRQ asserted */ - ichp &= 0x0000ffff; - ichp |= (1U << 31) | (irq << 16); - } - } - } - - return ichp; -} - -static uint64_t pxa2xx_pic_mem_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - - switch (offset) { - case ICIP: /* IRQ Pending register */ - return s->int_pending[0] & ~s->is_fiq[0] & s->int_enabled[0]; - case ICIP2: /* IRQ Pending register 2 */ - return s->int_pending[1] & ~s->is_fiq[1] & s->int_enabled[1]; - case ICMR: /* Mask register */ - return s->int_enabled[0]; - case ICMR2: /* Mask register 2 */ - return s->int_enabled[1]; - case ICLR: /* Level register */ - return s->is_fiq[0]; - case ICLR2: /* Level register 2 */ - return s->is_fiq[1]; - case ICCR: /* Idle mask */ - return (s->int_idle == 0); - case ICFP: /* FIQ Pending register */ - return s->int_pending[0] & s->is_fiq[0] & s->int_enabled[0]; - case ICFP2: /* FIQ Pending register 2 */ - return s->int_pending[1] & s->is_fiq[1] & s->int_enabled[1]; - case ICPR: /* Pending register */ - return s->int_pending[0]; - case ICPR2: /* Pending register 2 */ - return s->int_pending[1]; - case IPR0 ... IPR31: - return s->priority[0 + ((offset - IPR0 ) >> 2)]; - case IPR32 ... IPR39: - return s->priority[32 + ((offset - IPR32) >> 2)]; - case ICHP: /* Highest Priority register */ - return pxa2xx_pic_highest(s); - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pxa2xx_pic_mem_read: bad register offset 0x%" HWADDR_PRIx - "\n", offset); - return 0; - } -} - -static void pxa2xx_pic_mem_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPICState *s = (PXA2xxPICState *) opaque; - - switch (offset) { - case ICMR: /* Mask register */ - s->int_enabled[0] = value; - break; - case ICMR2: /* Mask register 2 */ - s->int_enabled[1] = value; - break; - case ICLR: /* Level register */ - s->is_fiq[0] = value; - break; - case ICLR2: /* Level register 2 */ - s->is_fiq[1] = value; - break; - case ICCR: /* Idle mask */ - s->int_idle = (value & 1) ? 0 : ~0; - break; - case IPR0 ... IPR31: - s->priority[0 + ((offset - IPR0 ) >> 2)] = value & 0x8000003f; - break; - case IPR32 ... IPR39: - s->priority[32 + ((offset - IPR32) >> 2)] = value & 0x8000003f; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "pxa2xx_pic_mem_write: bad register offset 0x%" - HWADDR_PRIx "\n", offset); - return; - } - pxa2xx_pic_update(opaque); -} - -/* Interrupt Controller Coprocessor Space Register Mapping */ -static const int pxa2xx_cp_reg_map[0x10] = { - [0x0 ... 0xf] = -1, - [0x0] = ICIP, - [0x1] = ICMR, - [0x2] = ICLR, - [0x3] = ICFP, - [0x4] = ICPR, - [0x5] = ICHP, - [0x6] = ICIP2, - [0x7] = ICMR2, - [0x8] = ICLR2, - [0x9] = ICFP2, - [0xa] = ICPR2, -}; - -static uint64_t pxa2xx_pic_cp_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - int offset = pxa2xx_cp_reg_map[ri->crn]; - return pxa2xx_pic_mem_read(ri->opaque, offset, 4); -} - -static void pxa2xx_pic_cp_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - int offset = pxa2xx_cp_reg_map[ri->crn]; - pxa2xx_pic_mem_write(ri->opaque, offset, value, 4); -} - -#define REGINFO_FOR_PIC_CP(NAME, CRN) \ - { .name = NAME, .cp = 6, .crn = CRN, .crm = 0, .opc1 = 0, .opc2 = 0, \ - .access = PL1_RW, .type = ARM_CP_IO, \ - .readfn = pxa2xx_pic_cp_read, .writefn = pxa2xx_pic_cp_write } - -static const ARMCPRegInfo pxa_pic_cp_reginfo[] = { - REGINFO_FOR_PIC_CP("ICIP", 0), - REGINFO_FOR_PIC_CP("ICMR", 1), - REGINFO_FOR_PIC_CP("ICLR", 2), - REGINFO_FOR_PIC_CP("ICFP", 3), - REGINFO_FOR_PIC_CP("ICPR", 4), - REGINFO_FOR_PIC_CP("ICHP", 5), - REGINFO_FOR_PIC_CP("ICIP2", 6), - REGINFO_FOR_PIC_CP("ICMR2", 7), - REGINFO_FOR_PIC_CP("ICLR2", 8), - REGINFO_FOR_PIC_CP("ICFP2", 9), - REGINFO_FOR_PIC_CP("ICPR2", 0xa), -}; - -static const MemoryRegionOps pxa2xx_pic_ops = { - .read = pxa2xx_pic_mem_read, - .write = pxa2xx_pic_mem_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int pxa2xx_pic_post_load(void *opaque, int version_id) -{ - pxa2xx_pic_update(opaque); - return 0; -} - -DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu) -{ - DeviceState *dev = qdev_new(TYPE_PXA2XX_PIC); - PXA2xxPICState *s = PXA2XX_PIC(dev); - - s->cpu = cpu; - - s->int_pending[0] = 0; - s->int_pending[1] = 0; - s->int_enabled[0] = 0; - s->int_enabled[1] = 0; - s->is_fiq[0] = 0; - s->is_fiq[1] = 0; - - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - qdev_init_gpio_in(dev, pxa2xx_pic_set_irq, PXA2XX_PIC_SRCS); - - /* Enable IC memory-mapped registers access. */ - 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; -} - -static const VMStateDescription vmstate_pxa2xx_pic_regs = { - .name = "pxa2xx_pic", - .version_id = 0, - .minimum_version_id = 0, - .post_load = pxa2xx_pic_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(int_enabled, PXA2xxPICState, 2), - VMSTATE_UINT32_ARRAY(int_pending, PXA2xxPICState, 2), - VMSTATE_UINT32_ARRAY(is_fiq, PXA2xxPICState, 2), - VMSTATE_UINT32(int_idle, PXA2xxPICState), - VMSTATE_UINT32_ARRAY(priority, PXA2xxPICState, PXA2XX_PIC_SRCS), - VMSTATE_END_OF_LIST(), - }, -}; - -static void pxa2xx_pic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA2xx PIC"; - dc->vmsd = &vmstate_pxa2xx_pic_regs; -} - -static const TypeInfo pxa2xx_pic_info = { - .name = TYPE_PXA2XX_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxPICState), - .class_init = pxa2xx_pic_class_init, -}; - -static void pxa2xx_pic_register_types(void) -{ - type_register_static(&pxa2xx_pic_info); -} - -type_init(pxa2xx_pic_register_types) diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index a7d287b1a8..a7a662f40d 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -18,6 +18,8 @@ #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" @@ -25,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 */ @@ -32,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: @@ -72,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 { @@ -81,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); @@ -99,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; } @@ -200,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) { @@ -252,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; @@ -279,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); @@ -298,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; @@ -320,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 */ @@ -392,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..e3195d5449 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,12 +60,14 @@ #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 +/* + * Generic timer frequency in Hz (which drives both the CPU generic timers + * and the SBSA watchdog-timer). Older (<2.11) versions of the TF-A firmware + * assumed 62.5MHz here. + * + * Starting with Armv8.6 CPU 1GHz timer frequency is mandated. + */ +#define SBSA_GTIMER_HZ 1000000000 enum { SBSA_FLASH, @@ -63,6 +75,7 @@ enum { SBSA_CPUPERIPHS, SBSA_GIC_DIST, SBSA_GIC_REDIST, + SBSA_GIC_ITS, SBSA_SECURE_EC, SBSA_GWDT_WS0, SBSA_GWDT_REFRESH, @@ -80,7 +93,7 @@ enum { SBSA_SECURE_UART_MM, SBSA_SECURE_MEM, SBSA_AHCI, - SBSA_EHCI, + SBSA_XHCI, }; struct SBSAMachineState { @@ -106,6 +119,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 +131,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 +151,33 @@ 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) +{ + const char *intc_nodename = "/intc"; + const char *its_nodename = "/intc/its"; + + qemu_fdt_add_subnode(sms->fdt, intc_nodename); + qemu_fdt_setprop_sized_cells(sms->fdt, intc_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); + + qemu_fdt_add_subnode(sms->fdt, its_nodename); + qemu_fdt_setprop_sized_cells(sms->fdt, its_nodename, "reg", + 2, sbsa_ref_memmap[SBSA_GIC_ITS].base, + 2, sbsa_ref_memmap[SBSA_GIC_ITS].size); } /* @@ -204,7 +216,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", 4); if (ms->numa_state->have_numa_distance) { int size = nb_numa_nodes * nb_numa_nodes * 3 * sizeof(uint32_t); @@ -260,6 +272,16 @@ static void create_fdt(SBSAMachineState *sms) g_free(nodename); } + + /* Add CPU topology description through fdt node topology. */ + qemu_fdt_add_subnode(sms->fdt, "/cpus/topology"); + + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "sockets", ms->smp.sockets); + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "clusters", ms->smp.clusters); + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "cores", ms->smp.cores); + qemu_fdt_setprop_cell(sms->fdt, "/cpus/topology", "threads", ms->smp.threads); + + sbsa_fdt_add_gic_node(sms); } #define SBSA_FLASH_SECTOR_SIZE (256 * KiB) @@ -392,12 +414,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 +452,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 +472,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 +483,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 +510,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, @@ -498,6 +544,7 @@ static void create_wdt(const SBSAMachineState *sms) SysBusDevice *s = SYS_BUS_DEVICE(dev); int irq = sbsa_ref_irqmap[SBSA_GWDT_WS0]; + qdev_prop_set_uint64(dev, "clock-frequency", SBSA_GTIMER_HZ); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, rbase); sysbus_mmio_map(s, 1, cbase); @@ -538,8 +585,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 +593,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,8 +616,9 @@ 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_str(OBJECT(dev), "stage", "nested", &error_abort); object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -596,6 +639,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 +680,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 +719,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); @@ -753,6 +783,8 @@ static void sbsa_ref_init(MachineState *machine) &error_abort); } + object_property_set_int(cpuobj, "cntfrq", SBSA_GTIMER_HZ, &error_abort); + object_property_set_link(cpuobj, "memory", OBJECT(sysmem), &error_abort); @@ -770,7 +802,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 +817,7 @@ static void sbsa_ref_init(MachineState *machine) create_ahci(sms); - create_ehci(sms); + create_xhci(sms); create_pcie(sms); @@ -849,21 +881,35 @@ 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-n2"); + 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->smp_props.clusters_supported = true; 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..3f82728758 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,19 +53,23 @@ 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(int asid, int 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; } -SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, - SMMUTransTableInfo *tt, hwaddr iova) +static SMMUTLBEntry *smmu_iotlb_lookup_all_levels(SMMUState *bs, + SMMUTransCfg *cfg, + SMMUTransTableInfo *tt, + hwaddr iova) { uint8_t tg = (tt->granule_sz - 10) / 2; uint8_t inputsize = 64 - tt->tsz; @@ -78,23 +82,54 @@ 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; } level++; } + return entry; +} + +/** + * smmu_iotlb_lookup - Look up for a TLB entry. + * @bs: SMMU state which includes the TLB instance + * @cfg: Configuration of the translation + * @tt: Translation table info (granule and tsz) + * @iova: IOVA address to lookup + * + * returns a valid entry on success, otherwise NULL. + * In case of nested translation, tt can be updated to include + * the granule of the found entry as it might different from + * the IOVA granule. + */ +SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, + SMMUTransTableInfo *tt, hwaddr iova) +{ + SMMUTLBEntry *entry = NULL; + + entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova); + /* + * For nested translation also try the s2 granule, as the TLB will insert + * it if the size of s2 tlb entry was smaller. + */ + if (!entry && (cfg->stage == SMMU_NESTED) && + (cfg->s2cfg.granule_sz != tt->granule_sz)) { + tt->granule_sz = cfg->s2cfg.granule_sz; + entry = smmu_iotlb_lookup_all_levels(bs, cfg, tt, iova); + } 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,27 +146,49 @@ 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); } -static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, - gpointer user_data) +static gboolean smmu_hash_remove_by_asid_vmid(gpointer key, gpointer value, + gpointer user_data) { - uint16_t asid = *(uint16_t *)user_data; + SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data; SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; - return SMMU_IOTLB_ASID(*iotlb_key) == asid; + return (SMMU_IOTLB_ASID(*iotlb_key) == info->asid) && + (SMMU_IOTLB_VMID(*iotlb_key) == info->vmid); } -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) +{ + int vmid = *(int *)user_data; + SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; + + return SMMU_IOTLB_VMID(*iotlb_key) == vmid; +} + +static gboolean smmu_hash_remove_by_vmid_s1(gpointer key, gpointer value, + gpointer user_data) +{ + int vmid = *(int *)user_data; + SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; + + return (SMMU_IOTLB_VMID(*iotlb_key) == vmid) && + (SMMU_IOTLB_ASID(*iotlb_key) >= 0); +} + +static gboolean smmu_hash_remove_by_asid_vmid_iova(gpointer key, gpointer value, gpointer user_data) { SMMUTLBEntry *iter = (SMMUTLBEntry *)value; @@ -142,19 +199,40 @@ 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) +static gboolean smmu_hash_remove_by_vmid_ipa(gpointer key, gpointer value, + gpointer user_data) +{ + SMMUTLBEntry *iter = (SMMUTLBEntry *)value; + IOMMUTLBEntry *entry = &iter->entry; + SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data; + SMMUIOTLBKey iotlb_key = *(SMMUIOTLBKey *)key; + + if (SMMU_IOTLB_ASID(iotlb_key) >= 0) { + /* This is a stage-1 address. */ + return false; + } + if (info->vmid != SMMU_IOTLB_VMID(iotlb_key)) { + return false; + } + return ((info->iova & ~entry->addr_mask) == entry->iova) || + ((entry->iova & ~info->mask) == info->iova); +} + +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,17 +245,63 @@ 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) +/* + * Similar to smmu_iotlb_inv_iova(), but for Stage-2, ASID is always -1, + * in Stage-1 invalidation ASID = -1, means don't care. + */ +void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, + uint64_t num_pages, uint8_t ttl) { - trace_smmu_iotlb_inv_asid(asid); - g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); + uint8_t granule = tg ? tg * 2 + 10 : 12; + int asid = -1; + + if (ttl && (num_pages == 1)) { + SMMUIOTLBKey key = smmu_get_iotlb_key(asid, vmid, ipa, tg, ttl); + + if (g_hash_table_remove(s->iotlb, &key)) { + return; + } + } + + SMMUIOTLBPageInvInfo info = { + .iova = ipa, + .vmid = vmid, + .mask = (num_pages << granule) - 1}; + + g_hash_table_foreach_remove(s->iotlb, + smmu_hash_remove_by_vmid_ipa, + &info); +} + +void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid) +{ + SMMUIOTLBPageInvInfo info = { + .asid = asid, + .vmid = vmid, + }; + + trace_smmu_iotlb_inv_asid_vmid(asid, vmid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid_vmid, &info); +} + +void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) +{ + trace_smmu_iotlb_inv_vmid(vmid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); +} + +inline void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid) +{ + trace_smmu_iotlb_inv_vmid_s1(vmid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid_s1, &vmid); } /* VMSAv8-64 Translation */ @@ -193,8 +317,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 +373,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) { @@ -264,8 +387,41 @@ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) return NULL; } +/* Translate stage-1 table address using stage-2 page table. */ +static inline int translate_table_addr_ipa(SMMUState *bs, + dma_addr_t *table_addr, + SMMUTransCfg *cfg, + SMMUPTWEventInfo *info) +{ + dma_addr_t addr = *table_addr; + SMMUTLBEntry *cached_entry; + int asid; + + /* + * The translation table walks performed from TTB0 or TTB1 are always + * performed in IPA space if stage 2 translations are enabled. + */ + asid = cfg->asid; + cfg->stage = SMMU_STAGE_2; + cfg->asid = -1; + cached_entry = smmu_translate(bs, cfg, addr, IOMMU_RO, info); + cfg->asid = asid; + cfg->stage = SMMU_NESTED; + + if (cached_entry) { + *table_addr = CACHED_ENTRY_TO_ADDR(cached_entry, addr); + return 0; + } + + info->stage = SMMU_STAGE_2; + info->addr = addr; + info->is_ipa_descriptor = true; + return -EINVAL; +} + /** - * 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 + * @bs: smmu state which includes TLB instance * @cfg: translation config * @iova: iova to translate * @perm: access type @@ -277,12 +433,12 @@ 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(SMMUState *bs, SMMUTransCfg *cfg, + dma_addr_t iova, IOMMUAccessFlags perm, + SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { dma_addr_t baseaddr, indexmask; - int stage = cfg->stage; + SMMUStage stage = cfg->stage; SMMUTransTableInfo *tt = select_tt(cfg, iova); uint8_t level, granule_sz, inputsize, stride; @@ -292,14 +448,15 @@ 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; - baseaddr = extract64(tt->ttb, 0, 48); + indexmask = VMSA_IDXMSK(inputsize, stride, level); + + baseaddr = extract64(tt->ttb, 0, cfg->oas); 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 +467,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)) { @@ -327,6 +484,11 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, goto error; } baseaddr = get_table_pte_address(pte, granule_sz); + if (cfg->stage == SMMU_NESTED) { + if (translate_table_addr_ipa(bs, &baseaddr, cfg, info)) { + goto error; + } + } level++; continue; } else if (is_page_pte(pte, level)) { @@ -342,16 +504,38 @@ 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; goto error; } + /* + * The address output from the translation causes a stage 1 Address + * Size fault if it exceeds the range of the effective IPA size for + * the given CD. + */ + if (gpa >= (1ULL << cfg->oas)) { + info->type = SMMU_PTW_ERR_ADDR_SIZE; + goto error; + } + tlbe->entry.translated_addr = gpa; tlbe->entry.iova = iova & ~mask; tlbe->entry.addr_mask = mask; - tlbe->entry.perm = PTE_AP_TO_PERM(ap); + tlbe->parent_perm = PTE_AP_TO_PERM(ap); + tlbe->entry.perm = tlbe->parent_perm; tlbe->level = level; tlbe->granule = granule_sz; return 0; @@ -359,13 +543,161 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, info->type = SMMU_PTW_ERR_TRANSLATION; error: + info->stage = SMMU_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 SMMUStage stage = SMMU_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, cfg->s2cfg.eff_ps) + + (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_ipa; + } + + 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_ipa; + } + + s2ap = PTE_AP(pte); + if (is_permission_fault_s2(s2ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error_ipa; + } + + /* + * 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_ipa; + } + + tlbe->entry.translated_addr = gpa; + tlbe->entry.iova = ipa & ~mask; + tlbe->entry.addr_mask = mask; + tlbe->parent_perm = s2ap; + tlbe->entry.perm = tlbe->parent_perm; + tlbe->level = level; + tlbe->granule = granule_sz; + return 0; + } + info->type = SMMU_PTW_ERR_TRANSLATION; + +error_ipa: + info->addr = ipa; +error: + info->stage = SMMU_STAGE_2; + tlbe->entry.perm = IOMMU_NONE; + return -EINVAL; +} + +/* + * combine S1 and S2 TLB entries into a single entry. + * As a result the S1 entry is overridden with combined data. + */ +static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2, + dma_addr_t iova, SMMUTransCfg *cfg) +{ + if (tlbe_s2->entry.addr_mask < tlbe->entry.addr_mask) { + tlbe->entry.addr_mask = tlbe_s2->entry.addr_mask; + tlbe->granule = tlbe_s2->granule; + tlbe->level = tlbe_s2->level; + } + + tlbe->entry.translated_addr = CACHED_ENTRY_TO_ADDR(tlbe_s2, + tlbe->entry.translated_addr); + + tlbe->entry.iova = iova & ~tlbe->entry.addr_mask; + /* parent_perm has s2 perm while perm keeps s1 perm. */ + tlbe->parent_perm = tlbe_s2->entry.perm; + return; +} + /** * smmu_ptw - Walk the page tables for an IOVA, according to @cfg * + * @bs: smmu state which includes TLB instance * @cfg: translation configuration * @iova: iova to translate * @perm: tentative access type @@ -374,18 +706,98 @@ error: * * return 0 on success */ -inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, - SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, + IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) { - if (!cfg->aa64) { + int ret; + SMMUTLBEntry tlbe_s2; + dma_addr_t ipa; + + if (cfg->stage == SMMU_STAGE_1) { + return smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info); + } else if (cfg->stage == SMMU_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 = SMMU_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); + /* SMMU_NESTED. */ + ret = smmu_ptw_64_s1(bs, cfg, iova, perm, tlbe, info); + if (ret) { + return ret; + } + + ipa = CACHED_ENTRY_TO_ADDR(tlbe, iova); + ret = smmu_ptw_64_s2(cfg, ipa, perm, &tlbe_s2, info); + if (ret) { + return ret; + } + + combine_tlb(tlbe, &tlbe_s2, iova, cfg); + return 0; +} + +SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, + IOMMUAccessFlags flag, SMMUPTWEventInfo *info) +{ + SMMUTLBEntry *cached_entry = NULL; + SMMUTransTableInfo *tt; + int status; + + /* + * Combined attributes used for TLB lookup, holds the attributes for + * the input stage. + */ + SMMUTransTableInfo tt_combined; + + if (cfg->stage == SMMU_STAGE_2) { + /* Stage2. */ + tt_combined.granule_sz = cfg->s2cfg.granule_sz; + tt_combined.tsz = cfg->s2cfg.tsz; + } else { + /* Select stage1 translation table. */ + tt = select_tt(cfg, addr); + if (!tt) { + info->type = SMMU_PTW_ERR_TRANSLATION; + info->stage = SMMU_STAGE_1; + return NULL; + } + tt_combined.granule_sz = tt->granule_sz; + tt_combined.tsz = tt->tsz; + } + + cached_entry = smmu_iotlb_lookup(bs, cfg, &tt_combined, addr); + if (cached_entry) { + if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & + cached_entry->parent_perm & IOMMU_WO)) { + info->type = SMMU_PTW_ERR_PERMISSION; + info->stage = !(cached_entry->entry.perm & IOMMU_WO) ? + SMMU_STAGE_1 : + SMMU_STAGE_2; + return NULL; + } + return cached_entry; + } + + cached_entry = g_new0(SMMUTLBEntry, 1); + status = smmu_ptw(bs, cfg, addr, flag, cached_entry, info); + if (status) { + g_free(cached_entry); + return NULL; + } + smmu_iotlb_insert(bs, cfg, cached_entry); + return cached_entry; } /** @@ -440,7 +852,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,46 +862,32 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) return &sdev->as; } -IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) +static const PCIIOMMUOps smmu_ops = { + .get_address_space = smmu_find_add_as, +}; + +SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid) { uint8_t bus_n, devfn; SMMUPciBus *smmu_bus; - SMMUDevice *smmu; bus_n = PCI_BUS_NUM(sid); smmu_bus = smmu_find_smmu_pcibus(s, bus_n); if (smmu_bus) { devfn = SMMU_PCI_DEVFN(sid); - smmu = smmu_bus->pbdev[devfn]; - if (smmu) { - return &smmu->iommu; - } + return smmu_bus->pbdev[devfn]; } 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 +918,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, ResetType type) { - 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 +936,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..b6b7399347 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 { @@ -31,38 +32,84 @@ typedef enum SMMUTranslationStatus { SMMU_TRANS_SUCCESS, } SMMUTranslationStatus; +typedef enum SMMUTranslationClass { + SMMU_CLASS_CD, + SMMU_CLASS_TT, + SMMU_CLASS_IN, +} SMMUTranslationClass; + /* 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 +126,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 +373,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 +568,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) { @@ -552,34 +599,18 @@ static inline int oas2bits(int oas_field) case 5: return 48; } - return -1; + + g_assert_not_reached(); } -static inline int pa_range(STE *ste) -{ - int oas_field = MIN(STE_S2PS(ste), SMMU_IDR5_OAS); - - if (!STE_S2AA64(ste)) { - return 40; - } - - return oas2bits(oas_field); -} - -#define MAX_PA(ste) ((1 << pa_range(ste)) - 1) - /* CD fields */ #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 +618,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..4c49b5a885 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,11 @@ #include "smmuv3-internal.h" #include "smmu-internal.h" +#define PTW_RECORD_FAULT(ptw_info, cfg) (((ptw_info).stage == SMMU_STAGE_1 && \ + (cfg)->record_faults) || \ + ((ptw_info).stage == SMMU_STAGE_2 && \ + (cfg)->s2cfg.record_faults)) + /** * smmuv3_trigger_irq - pulse @irq if enabled and update * GERROR register in case of GERROR interrupt @@ -98,20 +104,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 +258,20 @@ 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 if (s->stage && !strcmp("nested", s->stage)) { + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); + 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 +283,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 +315,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,18 +334,42 @@ 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; } +static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, + SMMUTransCfg *cfg, + SMMUEventInfo *event, + IOMMUAccessFlags flag, + SMMUTLBEntry **out_entry, + SMMUTranslationClass class); /* @ssid > 0 not supported yet */ -static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, - CD *buf, SMMUEventInfo *event) +static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg, + uint32_t ssid, CD *buf, SMMUEventInfo *event) { dma_addr_t addr = STE_CTXPTR(ste); - int ret; + int ret, i; + SMMUTranslationStatus status; + SMMUTLBEntry *entry; trace_smmuv3_get_cd(addr); + + if (cfg->stage == SMMU_NESTED) { + status = smmuv3_do_translate(s, addr, cfg, event, + IOMMU_RO, &entry, SMMU_CLASS_CD); + + /* Same PTW faults are reported but with CLASS = CD. */ + if (status != SMMU_TRANS_SUCCESS) { + return -EINVAL; + } + + addr = CACHED_ENTRY_TO_ADDR(entry, addr); + } + /* TODO: guarantee 64-bit single-copy atomicity */ ret = dma_memory_read(&address_space_memory, addr, buf, sizeof(*buf), MEMTXATTRS_UNSPECIFIED); @@ -325,14 +380,175 @@ 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 - cfg->s2cfg.eff_ps); + } + + return (cfg->s2cfg.tsz >= MAX(64 - cfg->s2cfg.eff_ps, 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(SMMUv3State *s, SMMUTransCfg *cfg, + STE *ste) +{ + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); + + 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), oas)); + /* + * For SMMUv3.1 and later, when OAS == IAS == 52, the stage 2 input + * range is further limited to 48 bits unless STE.S2TG indicates a + * 64KB granule. + */ + if (cfg->s2cfg.granule_sz != 16) { + cfg->s2cfg.eff_ps = MIN(cfg->s2cfg.eff_ps, 48); + } + /* + * 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; +} + +static void decode_ste_config(SMMUTransCfg *cfg, uint32_t config) +{ + + if (STE_CFG_ABORT(config)) { + cfg->aborted = true; + return; + } + if (STE_CFG_BYPASS(config)) { + cfg->bypassed = true; + return; + } + + if (STE_CFG_S1_ENABLED(config)) { + cfg->stage = SMMU_STAGE_1; + } + + if (STE_CFG_S2_ENABLED(config)) { + cfg->stage |= SMMU_STAGE_2; + } +} + /* Returns < 0 in case of invalid STE, 0 otherwise */ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, STE *ste, SMMUEventInfo *event) { uint32_t config; + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); + int ret; if (!STE_VALID(ste)) { if (!event->inval_ste_allowed) { @@ -343,19 +559,43 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, config = STE_CONFIG(ste); - if (STE_CFG_ABORT(config)) { - cfg->aborted = true; + decode_ste_config(cfg, config); + + if (cfg->aborted || cfg->bypassed) { return 0; } - if (STE_CFG_BYPASS(config)) { - cfg->bypassed = true; - return 0; + /* + * 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)) { - qemu_log_mask(LOG_UNIMP, "SMMUv3 does not support stage 2 yet\n"); - goto bad_ste; + /* + * 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(oas); + ret = decode_ste_s2_cfg(s, cfg, ste); + if (ret) { + goto bad_ste; + } } if (STE_S1CDMAX(ste) != 0) { @@ -406,7 +646,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 +670,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); @@ -468,10 +711,14 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, return 0; } -static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) +static int decode_cd(SMMUv3State *s, SMMUTransCfg *cfg, + CD *cd, SMMUEventInfo *event) { int ret = -EINVAL; int i; + SMMUTranslationStatus status; + SMMUTLBEntry *entry; + uint8_t oas = FIELD_EX32(s->idr[5], IDR5, OAS); if (!CD_VALID(cd) || !CD_AARCH64(cd)) { goto bad_cd; @@ -488,12 +735,12 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) /* we support only those at the moment */ cfg->aa64 = true; - cfg->stage = 1; cfg->oas = oas2bits(CD_IPS(cd)); - cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); + cfg->oas = MIN(oas2bits(oas), cfg->oas); cfg->tbi = CD_TBI(cd); cfg->asid = CD_ASID(cd); + cfg->affd = CD_AFFD(cd); trace_smmuv3_decode_cd(cfg->oas); @@ -519,11 +766,36 @@ static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) goto bad_cd; } + /* + * An address greater than 48 bits in size can only be output from a + * TTD when, in SMMUv3.1 and later, the effective IPS is 52 and a 64KB + * granule is in use for that translation table + */ + if (tt->granule_sz != 16) { + cfg->oas = MIN(cfg->oas, 48); + } tt->tsz = tsz; tt->ttb = CD_TTB(cd, i); + if (tt->ttb & ~(MAKE_64BIT_MASK(0, cfg->oas))) { goto bad_cd; } + + /* Translate the TTBx, from IPA to PA if nesting is enabled. */ + if (cfg->stage == SMMU_NESTED) { + status = smmuv3_do_translate(s, tt->ttb, cfg, event, IOMMU_RO, + &entry, SMMU_CLASS_TT); + /* + * Same PTW faults are reported but with CLASS = TT. + * If TTBx is larger than the effective stage 1 output addres + * size, it reports C_BAD_CD, which is handled by the above case. + */ + if (status != SMMU_TRANS_SUCCESS) { + return -EINVAL; + } + tt->ttb = CACHED_ENTRY_TO_ADDR(entry, tt->ttb); + } + tt->had = CD_HAD(cd, i); trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz, tt->had); } @@ -558,6 +830,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,16 +843,16 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, return ret; } - if (cfg->aborted || cfg->bypassed) { + if (cfg->aborted || cfg->bypassed || (cfg->stage == SMMU_STAGE_2)) { return 0; } - ret = smmu_get_cd(s, &ste, 0 /* ssid */, &cd, event); + ret = smmu_get_cd(s, &ste, cfg, 0 /* ssid */, &cd, event); if (ret) { return ret; } - return decode_cd(cfg, &cd, event); + return decode_cd(s, cfg, &cd, event); } /** @@ -632,6 +907,133 @@ static void smmuv3_flush_config(SMMUDevice *sdev) g_hash_table_remove(bc->configs, sdev); } +/* Do translation with TLB lookup. */ +static SMMUTranslationStatus smmuv3_do_translate(SMMUv3State *s, hwaddr addr, + SMMUTransCfg *cfg, + SMMUEventInfo *event, + IOMMUAccessFlags flag, + SMMUTLBEntry **out_entry, + SMMUTranslationClass class) +{ + SMMUPTWEventInfo ptw_info = {}; + SMMUState *bs = ARM_SMMU(s); + SMMUTLBEntry *cached_entry = NULL; + int asid, stage; + bool desc_s2_translation = class != SMMU_CLASS_IN; + + /* + * The function uses the argument class to identify which stage is used: + * - CLASS = IN: Means an input translation, determine the stage from STE. + * - CLASS = CD: Means the addr is an IPA of the CD, and it would be + * translated using the stage-2. + * - CLASS = TT: Means the addr is an IPA of the stage-1 translation table + * and it would be translated using the stage-2. + * For the last 2 cases instead of having intrusive changes in the common + * logic, we modify the cfg to be a stage-2 translation only in case of + * nested, and then restore it after. + */ + if (desc_s2_translation) { + asid = cfg->asid; + stage = cfg->stage; + cfg->asid = -1; + cfg->stage = SMMU_STAGE_2; + } + + cached_entry = smmu_translate(bs, cfg, addr, flag, &ptw_info); + + if (desc_s2_translation) { + cfg->asid = asid; + cfg->stage = stage; + } + + if (!cached_entry) { + /* All faults from PTW has S2 field. */ + event->u.f_walk_eabt.s2 = (ptw_info.stage == SMMU_STAGE_2); + /* + * Fault class is set as follows based on "class" input to + * the function and to "ptw_info" from "smmu_translate()" + * For stage-1: + * - EABT => CLASS_TT (hardcoded) + * - other events => CLASS_IN (input to function) + * For stage-2 => CLASS_IN (input to function) + * For nested, for all events: + * - CD fetch => CLASS_CD (input to function) + * - walking stage 1 translation table => CLASS_TT (from + * is_ipa_descriptor or input in case of TTBx) + * - s2 translation => CLASS_IN (input to function) + */ + class = ptw_info.is_ipa_descriptor ? SMMU_CLASS_TT : class; + switch (ptw_info.type) { + case SMMU_PTW_ERR_WALK_EABT: + event->type = SMMU_EVT_F_WALK_EABT; + event->u.f_walk_eabt.rnw = flag & 0x1; + event->u.f_walk_eabt.class = (ptw_info.stage == SMMU_STAGE_2) ? + class : SMMU_CLASS_TT; + event->u.f_walk_eabt.addr2 = ptw_info.addr; + break; + case SMMU_PTW_ERR_TRANSLATION: + if (PTW_RECORD_FAULT(ptw_info, cfg)) { + event->type = SMMU_EVT_F_TRANSLATION; + event->u.f_translation.addr2 = ptw_info.addr; + event->u.f_translation.class = class; + event->u.f_translation.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ADDR_SIZE: + if (PTW_RECORD_FAULT(ptw_info, cfg)) { + event->type = SMMU_EVT_F_ADDR_SIZE; + event->u.f_addr_size.addr2 = ptw_info.addr; + event->u.f_addr_size.class = class; + event->u.f_addr_size.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ACCESS: + if (PTW_RECORD_FAULT(ptw_info, cfg)) { + event->type = SMMU_EVT_F_ACCESS; + event->u.f_access.addr2 = ptw_info.addr; + event->u.f_access.class = class; + event->u.f_access.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_PERMISSION: + if (PTW_RECORD_FAULT(ptw_info, cfg)) { + event->type = SMMU_EVT_F_PERMISSION; + event->u.f_permission.addr2 = ptw_info.addr; + event->u.f_permission.class = class; + event->u.f_permission.rnw = flag & 0x1; + } + break; + default: + g_assert_not_reached(); + } + return SMMU_TRANS_ERROR; + } + *out_entry = cached_entry; + return SMMU_TRANS_SUCCESS; +} + +/* + * Sets the InputAddr for an SMMU_TRANS_ERROR, as it can't be + * set from all contexts, as smmuv3_get_config() can return + * translation faults in case of nested translation (for CD + * and TTBx). But in that case the iova is not known. + */ +static void smmuv3_fixup_event(SMMUEventInfo *event, hwaddr iova) +{ + switch (event->type) { + case SMMU_EVT_F_WALK_EABT: + case SMMU_EVT_F_TRANSLATION: + case SMMU_EVT_F_ADDR_SIZE: + case SMMU_EVT_F_ACCESS: + case SMMU_EVT_F_PERMISSION: + event->u.f_walk_eabt.addr = iova; + break; + default: + break; + } +} + +/* Entry point to SMMU, does everything. */ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, IOMMUAccessFlags flag, int iommu_idx) { @@ -641,12 +1043,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid, .inval_ste_allowed = false}; - SMMUPTWEventInfo ptw_info = {}; SMMUTranslationStatus status; - SMMUState *bs = ARM_SMMU(s); - uint64_t page_mask, aligned_addr; - SMMUTLBEntry *cached_entry = NULL; - SMMUTransTableInfo *tt; SMMUTransCfg *cfg = NULL; IOMMUTLBEntry entry = { .target_as = &address_space_memory, @@ -655,11 +1052,16 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, .addr_mask = ~(hwaddr)0, .perm = IOMMU_NONE, }; + SMMUTLBEntry *cached_entry = NULL; 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,94 +1081,19 @@ 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; - } - status = SMMU_TRANS_ERROR; - goto epilogue; - } - - page_mask = (1ULL << (tt->granule_sz)) - 1; - aligned_addr = addr & ~page_mask; - - cached_entry = smmu_iotlb_lookup(bs, cfg, tt, aligned_addr); - if (cached_entry) { - if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) { - status = SMMU_TRANS_ERROR; - if (cfg->record_faults) { - event.type = SMMU_EVT_F_PERMISSION; - event.u.f_permission.addr = addr; - event.u.f_permission.rnw = flag & 0x1; - } - } else { - status = SMMU_TRANS_SUCCESS; - } - goto epilogue; - } - - cached_entry = g_new0(SMMUTLBEntry, 1); - - if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { - g_free(cached_entry); - switch (ptw_info.type) { - case SMMU_PTW_ERR_WALK_EABT: - event.type = SMMU_EVT_F_WALK_EABT; - event.u.f_walk_eabt.addr = addr; - event.u.f_walk_eabt.rnw = flag & 0x1; - event.u.f_walk_eabt.class = 0x1; - event.u.f_walk_eabt.addr2 = ptw_info.addr; - break; - case SMMU_PTW_ERR_TRANSLATION: - if (cfg->record_faults) { - event.type = SMMU_EVT_F_TRANSLATION; - event.u.f_translation.addr = addr; - event.u.f_translation.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_ADDR_SIZE: - if (cfg->record_faults) { - event.type = SMMU_EVT_F_ADDR_SIZE; - event.u.f_addr_size.addr = addr; - event.u.f_addr_size.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_ACCESS: - if (cfg->record_faults) { - event.type = SMMU_EVT_F_ACCESS; - event.u.f_access.addr = addr; - event.u.f_access.rnw = flag & 0x1; - } - break; - case SMMU_PTW_ERR_PERMISSION: - if (cfg->record_faults) { - event.type = SMMU_EVT_F_PERMISSION; - event.u.f_permission.addr = addr; - event.u.f_permission.rnw = flag & 0x1; - } - break; - default: - g_assert_not_reached(); - } - status = SMMU_TRANS_ERROR; - } else { - smmu_iotlb_insert(bs, cfg, cached_entry); - status = SMMU_TRANS_SUCCESS; - } + status = smmuv3_do_translate(s, addr, cfg, &event, flag, + &cached_entry, SMMU_CLASS_IN); epilogue: qemu_mutex_unlock(&s->mutex); switch (status) { case SMMU_TRANS_SUCCESS: entry.perm = cached_entry->entry.perm; - entry.translated_addr = cached_entry->entry.translated_addr + - (addr & cached_entry->entry.addr_mask); + entry.translated_addr = CACHED_ENTRY_TO_ADDR(cached_entry, addr); entry.addr_mask = cached_entry->entry.addr_mask; trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr, - entry.translated_addr, entry.perm); + entry.translated_addr, entry.perm, + cfg->stage); break; case SMMU_TRANS_DISABLE: entry.perm = flag; @@ -786,6 +1113,7 @@ epilogue: entry.perm); break; case SMMU_TRANS_ERROR: + smmuv3_fixup_event(&event, addr); qemu_log_mask(LOG_GUEST_ERROR, "%s translation failed for iova=0x%"PRIx64" (%s)\n", mr->parent_obj.name, addr, smmu_event_string(event.type)); @@ -803,37 +1131,60 @@ 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 + * @stage: Which stage(1 or 2) is used */ 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, int stage) { SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUEventInfo eventinfo = {.inval_ste_allowed = true}; + SMMUTransCfg *cfg = smmuv3_get_config(sdev, &eventinfo); IOMMUTLBEvent event; uint8_t granule; - if (!tg) { - SMMUEventInfo event = {.inval_ste_allowed = true}; - SMMUTransCfg *cfg = smmuv3_get_config(sdev, &event); - SMMUTransTableInfo *tt; + if (!cfg) { + return; + } - if (!cfg) { - return; - } + /* + * stage is passed from TLB invalidation commands which can be either + * stage-1 or stage-2. + * However, IOMMUTLBEvent only understands IOVA, for stage-1 or stage-2 + * SMMU instances we consider the input address as the IOVA, but when + * nesting is used, we can't mix stage-1 and stage-2 addresses, so for + * nesting only stage-1 is considered the IOVA and would be notified. + */ + if ((stage == SMMU_STAGE_2) && (cfg->stage == SMMU_NESTED)) + return; + + if (!tg) { + SMMUTransTableInfo *tt; if (asid >= 0 && cfg->asid != asid) { return; } - tt = select_tt(cfg, iova); - if (!tt) { + if (vmid >= 0 && cfg->s2cfg.vmid != vmid) { return; } - granule = tt->granule_sz; + + if (stage == SMMU_STAGE_1) { + 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 +1198,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, int stage) { SMMUDevice *sdev; @@ -857,20 +1209,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, stage); 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, stage); } } } -static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd) +static void smmuv3_range_inval(SMMUState *s, Cmd *cmd, SMMUStage stage) { 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 +1231,25 @@ 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, stage); + smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, 1, stage); + if (stage == SMMU_STAGE_1) { + smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, 1, ttl); + } else { + smmu_iotlb_inv_ipa(s, vmid, addr, tg, 1, ttl); + } return; } @@ -903,9 +1265,14 @@ 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, stage); + smmuv3_inv_notifiers_iova(s, asid, vmid, addr, tg, num_pages, stage); + if (stage == SMMU_STAGE_1) { + smmu_iotlb_inv_iova(s, asid, vmid, addr, tg, num_pages, ttl); + } else { + smmu_iotlb_inv_ipa(s, vmid, addr, tg, num_pages, ttl); + } addr += mask + 1; } } @@ -974,20 +1341,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_CFGI_STE: { uint32_t sid = CMD_SID(&cmd); - IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); - SMMUDevice *sdev; + SMMUDevice *sdev = smmu_find_sdev(bs, sid); if (CMD_SSEC(&cmd)) { cmd_error = SMMU_CERROR_ILL; break; } - if (!mr) { + if (!sdev) { break; } trace_smmuv3_cmdq_cfgi_ste(sid); - sdev = container_of(mr, SMMUDevice, iommu); smmuv3_flush_config(sdev); break; @@ -1016,41 +1381,102 @@ static int smmuv3_cmdq_consume(SMMUv3State *s) case SMMU_CMD_CFGI_CD_ALL: { uint32_t sid = CMD_SID(&cmd); - IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); - SMMUDevice *sdev; + SMMUDevice *sdev = smmu_find_sdev(bs, sid); if (CMD_SSEC(&cmd)) { cmd_error = SMMU_CERROR_ILL; break; } - if (!mr) { + if (!sdev) { break; } trace_smmuv3_cmdq_cfgi_cd(sid); - sdev = container_of(mr, SMMUDevice, iommu); smmuv3_flush_config(sdev); break; } case SMMU_CMD_TLBI_NH_ASID: { - uint16_t asid = CMD_ASID(&cmd); + int asid = CMD_ASID(&cmd); + int vmid = -1; + + if (!STAGE1_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + /* + * VMID is only matched when stage 2 is supported, otherwise set it + * to -1 as the value used for stage-1 only VMIDs. + */ + if (STAGE2_SUPPORTED(s)) { + vmid = CMD_VMID(&cmd); + } trace_smmuv3_cmdq_tlbi_nh_asid(asid); smmu_inv_notifiers_all(&s->smmu_state); - smmu_iotlb_inv_asid(bs, asid); + smmu_iotlb_inv_asid_vmid(bs, asid, vmid); break; } case SMMU_CMD_TLBI_NH_ALL: + { + int vmid = -1; + + if (!STAGE1_SUPPORTED(s)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + /* + * If stage-2 is supported, invalidate for this VMID only, otherwise + * invalidate the whole thing. + */ + if (STAGE2_SUPPORTED(s)) { + vmid = CMD_VMID(&cmd); + trace_smmuv3_cmdq_tlbi_nh(vmid); + smmu_iotlb_inv_vmid_s1(bs, vmid); + break; + } + QEMU_FALLTHROUGH; + } case SMMU_CMD_TLBI_NSNH_ALL: - trace_smmuv3_cmdq_tlbi_nh(); + trace_smmuv3_cmdq_tlbi_nsnh(); smmu_inv_notifiers_all(&s->smmu_state); smmu_iotlb_inv_all(bs); 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, SMMU_STAGE_1); + break; + case SMMU_CMD_TLBI_S12_VMALL: + { + int 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, SMMU_STAGE_2); break; case SMMU_CMD_TLBI_EL3_ALL: case SMMU_CMD_TLBI_EL3_VA: @@ -1058,8 +1484,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 +1492,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 +1596,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 +1754,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 +1870,14 @@ static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) } } -static void smmu_reset(DeviceState *dev) +static void smmu_reset_hold(Object *obj, ResetType type) { - 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, type); + } smmuv3_init_regs(s); } @@ -1471,7 +1912,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 +1921,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 +1970,22 @@ 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 + * "nested": Both stage 1 and 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 +1996,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 deleted file mode 100644 index 5aab0b8565..0000000000 --- a/hw/arm/spitz.c +++ /dev/null @@ -1,1279 +0,0 @@ -/* - * PXA270-based Clamshell PDA platforms. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * 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 "qapi/error.h" -#include "hw/arm/pxa.h" -#include "hw/arm/boot.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" -#include "hw/pcmcia.h" -#include "hw/qdev-properties.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "hw/ssi/ssi.h" -#include "hw/block/flash.h" -#include "qemu/timer.h" -#include "qemu/log.h" -#include "hw/arm/sharpsl.h" -#include "ui/console.h" -#include "hw/audio/wm8750.h" -#include "audio/audio.h" -#include "hw/boards.h" -#include "hw/sysbus.h" -#include "hw/adc/max111x.h" -#include "migration/vmstate.h" -#include "exec/address-spaces.h" -#include "cpu.h" -#include "qom/object.h" - -enum spitz_model_e { spitz, akita, borzoi, terrier }; - -struct SpitzMachineClass { - MachineClass parent; - enum spitz_model_e model; - int arm_id; -}; - -struct SpitzMachineState { - MachineState parent; - PXA2xxState *mpu; - DeviceState *mux; - DeviceState *lcdtg; - DeviceState *ads7846; - DeviceState *max1111; - DeviceState *scp0; - DeviceState *scp1; - DeviceState *misc_gpio; -}; - -#define TYPE_SPITZ_MACHINE "spitz-common" -OBJECT_DECLARE_TYPE(SpitzMachineState, SpitzMachineClass, SPITZ_MACHINE) - -#define zaurus_printf(format, ...) \ - fprintf(stderr, "%s: " format, __func__, ##__VA_ARGS__) - -/* Spitz Flash */ -#define FLASH_BASE 0x0c000000 -#define FLASH_ECCLPLB 0x00 /* Line parity 7 - 0 bit */ -#define FLASH_ECCLPUB 0x04 /* Line parity 15 - 8 bit */ -#define FLASH_ECCCP 0x08 /* Column parity 5 - 0 bit */ -#define FLASH_ECCCNTR 0x0c /* ECC byte counter */ -#define FLASH_ECCCLRR 0x10 /* Clear ECC */ -#define FLASH_FLASHIO 0x14 /* Flash I/O */ -#define FLASH_FLASHCTL 0x18 /* Flash Control */ - -#define FLASHCTL_CE0 (1 << 0) -#define FLASHCTL_CLE (1 << 1) -#define FLASHCTL_ALE (1 << 2) -#define FLASHCTL_WP (1 << 3) -#define FLASHCTL_CE1 (1 << 4) -#define FLASHCTL_RYBY (1 << 5) -#define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1) - -#define TYPE_SL_NAND "sl-nand" -OBJECT_DECLARE_SIMPLE_TYPE(SLNANDState, SL_NAND) - -struct SLNANDState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - DeviceState *nand; - uint8_t ctl; - uint8_t manf_id; - uint8_t chip_id; - ECCState ecc; -}; - -static uint64_t sl_read(void *opaque, hwaddr addr, unsigned size) -{ - SLNANDState *s = (SLNANDState *) opaque; - int ryby; - - switch (addr) { -#define BSHR(byte, from, to) ((s->ecc.lp[byte] >> (from - to)) & (1 << to)) - case FLASH_ECCLPLB: - return BSHR(0, 4, 0) | BSHR(0, 5, 2) | BSHR(0, 6, 4) | BSHR(0, 7, 6) | - BSHR(1, 4, 1) | BSHR(1, 5, 3) | BSHR(1, 6, 5) | BSHR(1, 7, 7); - -#define BSHL(byte, from, to) ((s->ecc.lp[byte] << (to - from)) & (1 << to)) - case FLASH_ECCLPUB: - return BSHL(0, 0, 0) | BSHL(0, 1, 2) | BSHL(0, 2, 4) | BSHL(0, 3, 6) | - BSHL(1, 0, 1) | BSHL(1, 1, 3) | BSHL(1, 2, 5) | BSHL(1, 3, 7); - - case FLASH_ECCCP: - return s->ecc.cp; - - case FLASH_ECCCNTR: - return s->ecc.count & 0xff; - - case FLASH_FLASHCTL: - nand_getpins(s->nand, &ryby); - if (ryby) - return s->ctl | FLASHCTL_RYBY; - else - return s->ctl; - - case FLASH_FLASHIO: - if (size == 4) { - return ecc_digest(&s->ecc, nand_getio(s->nand)) | - (ecc_digest(&s->ecc, nand_getio(s->nand)) << 16); - } - return ecc_digest(&s->ecc, nand_getio(s->nand)); - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "sl_read: bad register offset 0x%02" HWADDR_PRIx "\n", - addr); - } - return 0; -} - -static void sl_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SLNANDState *s = (SLNANDState *) opaque; - - switch (addr) { - case FLASH_ECCCLRR: - /* Value is ignored. */ - ecc_reset(&s->ecc); - break; - - case FLASH_FLASHCTL: - s->ctl = value & 0xff & ~FLASHCTL_RYBY; - nand_setpins(s->nand, - s->ctl & FLASHCTL_CLE, - s->ctl & FLASHCTL_ALE, - s->ctl & FLASHCTL_NCE, - s->ctl & FLASHCTL_WP, - 0); - break; - - case FLASH_FLASHIO: - nand_setio(s->nand, ecc_digest(&s->ecc, value & 0xff)); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "sl_write: bad register offset 0x%02" HWADDR_PRIx "\n", - addr); - } -} - -enum { - FLASH_128M, - FLASH_1024M, -}; - -static const MemoryRegionOps sl_ops = { - .read = sl_read, - .write = sl_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void sl_flash_register(PXA2xxState *cpu, int size) -{ - DeviceState *dev; - - dev = qdev_new(TYPE_SL_NAND); - - qdev_prop_set_uint8(dev, "manf_id", NAND_MFR_SAMSUNG); - if (size == FLASH_128M) - qdev_prop_set_uint8(dev, "chip_id", 0x73); - else if (size == FLASH_1024M) - qdev_prop_set_uint8(dev, "chip_id", 0xf1); - - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, FLASH_BASE); -} - -static void sl_nand_init(Object *obj) -{ - SLNANDState *s = SL_NAND(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - - s->ctl = 0; - - memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); - sysbus_init_mmio(dev, &s->iomem); -} - -static void sl_nand_realize(DeviceState *dev, Error **errp) -{ - SLNANDState *s = SL_NAND(dev); - DriveInfo *nand; - - /* FIXME use a qdev drive property instead of drive_get() */ - nand = drive_get(IF_MTD, 0, 0); - s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, - s->manf_id, s->chip_id); -} - -/* Spitz Keyboard */ - -#define SPITZ_KEY_STROBE_NUM 11 -#define SPITZ_KEY_SENSE_NUM 7 - -static const int spitz_gpio_key_sense[SPITZ_KEY_SENSE_NUM] = { - 12, 17, 91, 34, 36, 38, 39 -}; - -static const int spitz_gpio_key_strobe[SPITZ_KEY_STROBE_NUM] = { - 88, 23, 24, 25, 26, 27, 52, 103, 107, 108, 114 -}; - -/* Eighth additional row maps the special keys */ -static int spitz_keymap[SPITZ_KEY_SENSE_NUM + 1][SPITZ_KEY_STROBE_NUM] = { - { 0x1d, 0x02, 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0e, 0x3f, 0x40 }, - { -1 , 0x03, 0x05, 0x13, 0x15, 0x09, 0x17, 0x18, 0x19, 0x41, 0x42 }, - { 0x0f, 0x10, 0x12, 0x14, 0x22, 0x16, 0x24, 0x25, -1 , -1 , -1 }, - { 0x3c, 0x11, 0x1f, 0x21, 0x2f, 0x23, 0x32, 0x26, -1 , 0x36, -1 }, - { 0x3b, 0x1e, 0x20, 0x2e, 0x30, 0x31, 0x34, -1 , 0x1c, 0x2a, -1 }, - { 0x44, 0x2c, 0x2d, 0x0c, 0x39, 0x33, -1 , 0x48, -1 , -1 , 0x38 }, - { 0x37, 0x3d, -1 , 0x45, 0x57, 0x58, 0x4b, 0x50, 0x4d, -1 , -1 }, - { 0x52, 0x43, 0x01, 0x47, 0x49, -1 , -1 , -1 , -1 , -1 , -1 }, -}; - -#define SPITZ_GPIO_AK_INT 13 /* Remote control */ -#define SPITZ_GPIO_SYNC 16 /* Sync button */ -#define SPITZ_GPIO_ON_KEY 95 /* Power button */ -#define SPITZ_GPIO_SWA 97 /* Lid */ -#define SPITZ_GPIO_SWB 96 /* Tablet mode */ - -/* The special buttons are mapped to unused keys */ -static const int spitz_gpiomap[5] = { - SPITZ_GPIO_AK_INT, SPITZ_GPIO_SYNC, SPITZ_GPIO_ON_KEY, - SPITZ_GPIO_SWA, SPITZ_GPIO_SWB, -}; - -#define TYPE_SPITZ_KEYBOARD "spitz-keyboard" -OBJECT_DECLARE_SIMPLE_TYPE(SpitzKeyboardState, SPITZ_KEYBOARD) - -struct SpitzKeyboardState { - SysBusDevice parent_obj; - - qemu_irq sense[SPITZ_KEY_SENSE_NUM]; - qemu_irq gpiomap[5]; - int keymap[0x80]; - uint16_t keyrow[SPITZ_KEY_SENSE_NUM]; - uint16_t strobe_state; - uint16_t sense_state; - - uint16_t pre_map[0x100]; - uint16_t modifiers; - uint16_t imodifiers; - uint8_t fifo[16]; - int fifopos, fifolen; - QEMUTimer *kbdtimer; -}; - -static void spitz_keyboard_sense_update(SpitzKeyboardState *s) -{ - int i; - uint16_t strobe, sense = 0; - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) { - strobe = s->keyrow[i] & s->strobe_state; - if (strobe) { - sense |= 1 << i; - if (!(s->sense_state & (1 << i))) - qemu_irq_raise(s->sense[i]); - } else if (s->sense_state & (1 << i)) - qemu_irq_lower(s->sense[i]); - } - - s->sense_state = sense; -} - -static void spitz_keyboard_strobe(void *opaque, int line, int level) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - - if (level) - s->strobe_state |= 1 << line; - else - s->strobe_state &= ~(1 << line); - spitz_keyboard_sense_update(s); -} - -static void spitz_keyboard_keydown(SpitzKeyboardState *s, int keycode) -{ - int spitz_keycode = s->keymap[keycode & 0x7f]; - if (spitz_keycode == -1) - return; - - /* Handle the additional keys */ - if ((spitz_keycode >> 4) == SPITZ_KEY_SENSE_NUM) { - qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80)); - return; - } - - if (keycode & 0x80) - s->keyrow[spitz_keycode >> 4] &= ~(1 << (spitz_keycode & 0xf)); - else - s->keyrow[spitz_keycode >> 4] |= 1 << (spitz_keycode & 0xf); - - spitz_keyboard_sense_update(s); -} - -#define SPITZ_MOD_SHIFT (1 << 7) -#define SPITZ_MOD_CTRL (1 << 8) -#define SPITZ_MOD_FN (1 << 9) - -#define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c - -static void spitz_keyboard_handler(void *opaque, int keycode) -{ - SpitzKeyboardState *s = opaque; - uint16_t code; - int mapcode; - switch (keycode) { - case 0x2a: /* Left Shift */ - s->modifiers |= 1; - break; - case 0xaa: - s->modifiers &= ~1; - break; - case 0x36: /* Right Shift */ - s->modifiers |= 2; - break; - case 0xb6: - s->modifiers &= ~2; - break; - case 0x1d: /* Control */ - s->modifiers |= 4; - break; - case 0x9d: - s->modifiers &= ~4; - break; - case 0x38: /* Alt */ - s->modifiers |= 8; - break; - case 0xb8: - s->modifiers &= ~8; - break; - } - - code = s->pre_map[mapcode = ((s->modifiers & 3) ? - (keycode | SPITZ_MOD_SHIFT) : - (keycode & ~SPITZ_MOD_SHIFT))]; - - if (code != mapcode) { -#if 0 - if ((code & SPITZ_MOD_SHIFT) && !(s->modifiers & 1)) { - QUEUE_KEY(0x2a | (keycode & 0x80)); - } - if ((code & SPITZ_MOD_CTRL) && !(s->modifiers & 4)) { - QUEUE_KEY(0x1d | (keycode & 0x80)); - } - if ((code & SPITZ_MOD_FN) && !(s->modifiers & 8)) { - QUEUE_KEY(0x38 | (keycode & 0x80)); - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 1)) { - QUEUE_KEY(0x2a | (~keycode & 0x80)); - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 2)) { - QUEUE_KEY(0x36 | (~keycode & 0x80)); - } -#else - if (keycode & 0x80) { - if ((s->imodifiers & 1 ) && !(s->modifiers & 1)) - QUEUE_KEY(0x2a | 0x80); - if ((s->imodifiers & 4 ) && !(s->modifiers & 4)) - QUEUE_KEY(0x1d | 0x80); - if ((s->imodifiers & 8 ) && !(s->modifiers & 8)) - QUEUE_KEY(0x38 | 0x80); - if ((s->imodifiers & 0x10) && (s->modifiers & 1)) - QUEUE_KEY(0x2a); - if ((s->imodifiers & 0x20) && (s->modifiers & 2)) - QUEUE_KEY(0x36); - s->imodifiers = 0; - } else { - if ((code & SPITZ_MOD_SHIFT) && - !((s->modifiers | s->imodifiers) & 1)) { - QUEUE_KEY(0x2a); - s->imodifiers |= 1; - } - if ((code & SPITZ_MOD_CTRL) && - !((s->modifiers | s->imodifiers) & 4)) { - QUEUE_KEY(0x1d); - s->imodifiers |= 4; - } - if ((code & SPITZ_MOD_FN) && - !((s->modifiers | s->imodifiers) & 8)) { - QUEUE_KEY(0x38); - s->imodifiers |= 8; - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 1) && - !(s->imodifiers & 0x10)) { - QUEUE_KEY(0x2a | 0x80); - s->imodifiers |= 0x10; - } - if ((code & SPITZ_MOD_FN) && (s->modifiers & 2) && - !(s->imodifiers & 0x20)) { - QUEUE_KEY(0x36 | 0x80); - s->imodifiers |= 0x20; - } - } -#endif - } - - QUEUE_KEY((code & 0x7f) | (keycode & 0x80)); -} - -static void spitz_keyboard_tick(void *opaque) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - - if (s->fifolen) { - spitz_keyboard_keydown(s, s->fifo[s->fifopos ++]); - s->fifolen --; - if (s->fifopos >= 16) - s->fifopos = 0; - } - - timer_mod(s->kbdtimer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / 32); -} - -static void spitz_keyboard_pre_map(SpitzKeyboardState *s) -{ - int i; - for (i = 0; i < 0x100; i ++) - s->pre_map[i] = i; - s->pre_map[0x02 | SPITZ_MOD_SHIFT] = 0x02 | SPITZ_MOD_SHIFT; /* exclam */ - s->pre_map[0x28 | SPITZ_MOD_SHIFT] = 0x03 | SPITZ_MOD_SHIFT; /* quotedbl */ - s->pre_map[0x04 | SPITZ_MOD_SHIFT] = 0x04 | SPITZ_MOD_SHIFT; /* # */ - s->pre_map[0x05 | SPITZ_MOD_SHIFT] = 0x05 | SPITZ_MOD_SHIFT; /* dollar */ - s->pre_map[0x06 | SPITZ_MOD_SHIFT] = 0x06 | SPITZ_MOD_SHIFT; /* percent */ - s->pre_map[0x08 | SPITZ_MOD_SHIFT] = 0x07 | SPITZ_MOD_SHIFT; /* ampersand */ - s->pre_map[0x28] = 0x08 | SPITZ_MOD_SHIFT; /* ' */ - s->pre_map[0x0a | SPITZ_MOD_SHIFT] = 0x09 | SPITZ_MOD_SHIFT; /* ( */ - s->pre_map[0x0b | SPITZ_MOD_SHIFT] = 0x0a | SPITZ_MOD_SHIFT; /* ) */ - s->pre_map[0x29 | SPITZ_MOD_SHIFT] = 0x0b | SPITZ_MOD_SHIFT; /* tilde */ - s->pre_map[0x03 | SPITZ_MOD_SHIFT] = 0x0c | SPITZ_MOD_SHIFT; /* at */ - s->pre_map[0xd3] = 0x0e | SPITZ_MOD_FN; /* Delete */ - s->pre_map[0x3a] = 0x0f | SPITZ_MOD_FN; /* Caps_Lock */ - s->pre_map[0x07 | SPITZ_MOD_SHIFT] = 0x11 | SPITZ_MOD_FN; /* ^ */ - s->pre_map[0x0d] = 0x12 | SPITZ_MOD_FN; /* equal */ - s->pre_map[0x0d | SPITZ_MOD_SHIFT] = 0x13 | SPITZ_MOD_FN; /* plus */ - s->pre_map[0x1a] = 0x14 | SPITZ_MOD_FN; /* [ */ - s->pre_map[0x1b] = 0x15 | SPITZ_MOD_FN; /* ] */ - s->pre_map[0x1a | SPITZ_MOD_SHIFT] = 0x16 | SPITZ_MOD_FN; /* { */ - s->pre_map[0x1b | SPITZ_MOD_SHIFT] = 0x17 | SPITZ_MOD_FN; /* } */ - s->pre_map[0x27] = 0x22 | SPITZ_MOD_FN; /* semicolon */ - s->pre_map[0x27 | SPITZ_MOD_SHIFT] = 0x23 | SPITZ_MOD_FN; /* colon */ - s->pre_map[0x09 | SPITZ_MOD_SHIFT] = 0x24 | SPITZ_MOD_FN; /* asterisk */ - s->pre_map[0x2b] = 0x25 | SPITZ_MOD_FN; /* backslash */ - s->pre_map[0x2b | SPITZ_MOD_SHIFT] = 0x26 | SPITZ_MOD_FN; /* bar */ - s->pre_map[0x0c | SPITZ_MOD_SHIFT] = 0x30 | SPITZ_MOD_FN; /* _ */ - s->pre_map[0x33 | SPITZ_MOD_SHIFT] = 0x33 | SPITZ_MOD_FN; /* less */ - s->pre_map[0x35] = 0x33 | SPITZ_MOD_SHIFT; /* slash */ - s->pre_map[0x34 | SPITZ_MOD_SHIFT] = 0x34 | SPITZ_MOD_FN; /* greater */ - s->pre_map[0x35 | SPITZ_MOD_SHIFT] = 0x34 | SPITZ_MOD_SHIFT; /* question */ - s->pre_map[0x49] = 0x48 | SPITZ_MOD_FN; /* Page_Up */ - s->pre_map[0x51] = 0x50 | SPITZ_MOD_FN; /* Page_Down */ - - s->modifiers = 0; - s->imodifiers = 0; - s->fifopos = 0; - s->fifolen = 0; -} - -#undef SPITZ_MOD_SHIFT -#undef SPITZ_MOD_CTRL -#undef SPITZ_MOD_FN - -static int spitz_keyboard_post_load(void *opaque, int version_id) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - - /* Release all pressed keys */ - memset(s->keyrow, 0, sizeof(s->keyrow)); - spitz_keyboard_sense_update(s); - s->modifiers = 0; - s->imodifiers = 0; - s->fifopos = 0; - s->fifolen = 0; - - return 0; -} - -static void spitz_keyboard_register(PXA2xxState *cpu) -{ - int i; - DeviceState *dev; - SpitzKeyboardState *s; - - dev = sysbus_create_simple(TYPE_SPITZ_KEYBOARD, -1, NULL); - s = SPITZ_KEYBOARD(dev); - - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) - qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i])); - - for (i = 0; i < 5; i ++) - s->gpiomap[i] = qdev_get_gpio_in(cpu->gpio, spitz_gpiomap[i]); - - if (!graphic_rotate) - s->gpiomap[4] = qemu_irq_invert(s->gpiomap[4]); - - for (i = 0; i < 5; i++) - qemu_set_irq(s->gpiomap[i], 0); - - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++) - qdev_connect_gpio_out(cpu->gpio, spitz_gpio_key_strobe[i], - qdev_get_gpio_in(dev, i)); - - timer_mod(s->kbdtimer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); - - qemu_add_kbd_event_handler(spitz_keyboard_handler, s); -} - -static void spitz_keyboard_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - SpitzKeyboardState *s = SPITZ_KEYBOARD(obj); - int i, j; - - for (i = 0; i < 0x80; i ++) - s->keymap[i] = -1; - for (i = 0; i < SPITZ_KEY_SENSE_NUM + 1; i ++) - for (j = 0; j < SPITZ_KEY_STROBE_NUM; j ++) - if (spitz_keymap[i][j] != -1) - s->keymap[spitz_keymap[i][j]] = (i << 4) | j; - - spitz_keyboard_pre_map(s); - - qdev_init_gpio_in(dev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); - qdev_init_gpio_out(dev, s->sense, SPITZ_KEY_SENSE_NUM); -} - -static void spitz_keyboard_realize(DeviceState *dev, Error **errp) -{ - SpitzKeyboardState *s = SPITZ_KEYBOARD(dev); - s->kbdtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, spitz_keyboard_tick, s); -} - -/* LCD backlight controller */ - -#define LCDTG_RESCTL 0x00 -#define LCDTG_PHACTRL 0x01 -#define LCDTG_DUTYCTRL 0x02 -#define LCDTG_POWERREG0 0x03 -#define LCDTG_POWERREG1 0x04 -#define LCDTG_GPOR3 0x05 -#define LCDTG_PICTRL 0x06 -#define LCDTG_POLCTRL 0x07 - -#define TYPE_SPITZ_LCDTG "spitz-lcdtg" -OBJECT_DECLARE_SIMPLE_TYPE(SpitzLCDTG, SPITZ_LCDTG) - -struct SpitzLCDTG { - SSIPeripheral ssidev; - uint32_t bl_intensity; - uint32_t bl_power; -}; - -static void spitz_bl_update(SpitzLCDTG *s) -{ - if (s->bl_power && s->bl_intensity) - zaurus_printf("LCD Backlight now at %u/63\n", s->bl_intensity); - else - zaurus_printf("LCD Backlight now off\n"); -} - -static inline void spitz_bl_bit5(void *opaque, int line, int level) -{ - SpitzLCDTG *s = opaque; - int prev = s->bl_intensity; - - if (level) - s->bl_intensity &= ~0x20; - else - s->bl_intensity |= 0x20; - - if (s->bl_power && prev != s->bl_intensity) - spitz_bl_update(s); -} - -static inline void spitz_bl_power(void *opaque, int line, int level) -{ - SpitzLCDTG *s = opaque; - s->bl_power = !!level; - spitz_bl_update(s); -} - -static uint32_t spitz_lcdtg_transfer(SSIPeripheral *dev, uint32_t value) -{ - SpitzLCDTG *s = SPITZ_LCDTG(dev); - int addr; - addr = value >> 5; - value &= 0x1f; - - switch (addr) { - case LCDTG_RESCTL: - if (value) - zaurus_printf("LCD in QVGA mode\n"); - else - zaurus_printf("LCD in VGA mode\n"); - break; - - case LCDTG_DUTYCTRL: - s->bl_intensity &= ~0x1f; - s->bl_intensity |= value; - if (s->bl_power) - spitz_bl_update(s); - break; - - case LCDTG_POWERREG0: - /* Set common voltage to M62332FP */ - break; - } - return 0; -} - -static void spitz_lcdtg_realize(SSIPeripheral *ssi, Error **errp) -{ - SpitzLCDTG *s = SPITZ_LCDTG(ssi); - DeviceState *dev = DEVICE(s); - - s->bl_power = 0; - s->bl_intensity = 0x20; - - qdev_init_gpio_in_named(dev, spitz_bl_bit5, "bl_bit5", 1); - qdev_init_gpio_in_named(dev, spitz_bl_power, "bl_power", 1); -} - -/* SSP devices */ - -#define CORGI_SSP_PORT 2 - -#define SPITZ_GPIO_LCDCON_CS 53 -#define SPITZ_GPIO_ADS7846_CS 14 -#define SPITZ_GPIO_MAX1111_CS 20 -#define SPITZ_GPIO_TP_INT 11 - -#define TYPE_CORGI_SSP "corgi-ssp" -OBJECT_DECLARE_SIMPLE_TYPE(CorgiSSPState, CORGI_SSP) - -/* "Demux" the signal based on current chipselect */ -struct CorgiSSPState { - SSIPeripheral ssidev; - SSIBus *bus[3]; - uint32_t enable[3]; -}; - -static uint32_t corgi_ssp_transfer(SSIPeripheral *dev, uint32_t value) -{ - CorgiSSPState *s = CORGI_SSP(dev); - int i; - - for (i = 0; i < 3; i++) { - if (s->enable[i]) { - return ssi_transfer(s->bus[i], value); - } - } - return 0; -} - -static void corgi_ssp_gpio_cs(void *opaque, int line, int level) -{ - CorgiSSPState *s = (CorgiSSPState *)opaque; - assert(line >= 0 && line < 3); - s->enable[line] = !level; -} - -#define MAX1111_BATT_VOLT 1 -#define MAX1111_BATT_TEMP 2 -#define MAX1111_ACIN_VOLT 3 - -#define SPITZ_BATTERY_TEMP 0xe0 /* About 2.9V */ -#define SPITZ_BATTERY_VOLT 0xd0 /* About 4.0V */ -#define SPITZ_CHARGEON_ACIN 0x80 /* About 5.0V */ - -static void corgi_ssp_realize(SSIPeripheral *d, Error **errp) -{ - DeviceState *dev = DEVICE(d); - CorgiSSPState *s = CORGI_SSP(d); - - qdev_init_gpio_in(dev, corgi_ssp_gpio_cs, 3); - s->bus[0] = ssi_create_bus(dev, "ssi0"); - s->bus[1] = ssi_create_bus(dev, "ssi1"); - s->bus[2] = ssi_create_bus(dev, "ssi2"); -} - -static void spitz_ssp_attach(SpitzMachineState *sms) -{ - void *bus; - - sms->mux = ssi_create_peripheral(sms->mpu->ssp[CORGI_SSP_PORT - 1], - TYPE_CORGI_SSP); - - bus = qdev_get_child_bus(sms->mux, "ssi0"); - sms->lcdtg = ssi_create_peripheral(bus, TYPE_SPITZ_LCDTG); - - bus = qdev_get_child_bus(sms->mux, "ssi1"); - sms->ads7846 = ssi_create_peripheral(bus, "ads7846"); - qdev_connect_gpio_out(sms->ads7846, 0, - qdev_get_gpio_in(sms->mpu->gpio, SPITZ_GPIO_TP_INT)); - - bus = qdev_get_child_bus(sms->mux, "ssi2"); - sms->max1111 = qdev_new(TYPE_MAX_1111); - qdev_prop_set_uint8(sms->max1111, "input1" /* BATT_VOLT */, - SPITZ_BATTERY_VOLT); - qdev_prop_set_uint8(sms->max1111, "input2" /* BATT_TEMP */, 0); - qdev_prop_set_uint8(sms->max1111, "input3" /* ACIN_VOLT */, - SPITZ_CHARGEON_ACIN); - ssi_realize_and_unref(sms->max1111, bus, &error_fatal); - - qdev_connect_gpio_out(sms->mpu->gpio, SPITZ_GPIO_LCDCON_CS, - qdev_get_gpio_in(sms->mux, 0)); - qdev_connect_gpio_out(sms->mpu->gpio, SPITZ_GPIO_ADS7846_CS, - qdev_get_gpio_in(sms->mux, 1)); - qdev_connect_gpio_out(sms->mpu->gpio, SPITZ_GPIO_MAX1111_CS, - qdev_get_gpio_in(sms->mux, 2)); -} - -/* CF Microdrive */ - -static void spitz_microdrive_attach(PXA2xxState *cpu, int slot) -{ - PCMCIACardState *md; - DriveInfo *dinfo; - - dinfo = drive_get(IF_IDE, 0, 0); - if (!dinfo || dinfo->media_cd) - return; - md = dscm1xxxx_init(dinfo); - pxa2xx_pcmcia_attach(cpu->pcmcia[slot], md); -} - -/* Wm8750 and Max7310 on I2C */ - -#define AKITA_MAX_ADDR 0x18 -#define SPITZ_WM_ADDRL 0x1b -#define SPITZ_WM_ADDRH 0x1a - -#define SPITZ_GPIO_WM 5 - -static void spitz_wm8750_addr(void *opaque, int line, int level) -{ - I2CSlave *wm = (I2CSlave *) opaque; - if (level) - i2c_slave_set_address(wm, SPITZ_WM_ADDRH); - else - i2c_slave_set_address(wm, SPITZ_WM_ADDRL); -} - -static void spitz_i2c_setup(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)); - - spitz_wm8750_addr(wm, 0, 0); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, - qemu_allocate_irq(spitz_wm8750_addr, wm, 0)); - /* .. and to the sound interface. */ - cpu->i2s->opaque = wm; - cpu->i2s->codec_out = wm8750_dac_dat; - cpu->i2s->codec_in = wm8750_adc_dat; - wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); -} - -static void spitz_akita_i2c_setup(PXA2xxState *cpu) -{ - /* Attach a Max7310 to Akita I2C bus. */ - i2c_slave_create_simple(pxa2xx_i2c_bus(cpu->i2c[0]), "max7310", - AKITA_MAX_ADDR); -} - -/* Other peripherals */ - -/* - * Encapsulation of some miscellaneous GPIO line behaviour for the Spitz boards. - * - * QEMU interface: - * + named GPIO inputs "green-led", "orange-led", "charging", "discharging": - * these currently just print messages that the line has been signalled - * + named GPIO input "adc-temp-on": set to cause the battery-temperature - * value to be passed to the max111x ADC - * + named GPIO output "adc-temp": the ADC value, to be wired up to the max111x - */ -#define TYPE_SPITZ_MISC_GPIO "spitz-misc-gpio" -OBJECT_DECLARE_SIMPLE_TYPE(SpitzMiscGPIOState, SPITZ_MISC_GPIO) - -struct SpitzMiscGPIOState { - SysBusDevice parent_obj; - - qemu_irq adc_value; -}; - -static void spitz_misc_charging(void *opaque, int n, int level) -{ - zaurus_printf("Charging %s.\n", level ? "off" : "on"); -} - -static void spitz_misc_discharging(void *opaque, int n, int level) -{ - zaurus_printf("Discharging %s.\n", level ? "off" : "on"); -} - -static void spitz_misc_green_led(void *opaque, int n, int level) -{ - zaurus_printf("Green LED %s.\n", level ? "off" : "on"); -} - -static void spitz_misc_orange_led(void *opaque, int n, int level) -{ - zaurus_printf("Orange LED %s.\n", level ? "off" : "on"); -} - -static void spitz_misc_adc_temp(void *opaque, int n, int level) -{ - SpitzMiscGPIOState *s = SPITZ_MISC_GPIO(opaque); - int batt_temp = level ? SPITZ_BATTERY_TEMP : 0; - - qemu_set_irq(s->adc_value, batt_temp); -} - -static void spitz_misc_gpio_init(Object *obj) -{ - SpitzMiscGPIOState *s = SPITZ_MISC_GPIO(obj); - DeviceState *dev = DEVICE(obj); - - qdev_init_gpio_in_named(dev, spitz_misc_charging, "charging", 1); - qdev_init_gpio_in_named(dev, spitz_misc_discharging, "discharging", 1); - qdev_init_gpio_in_named(dev, spitz_misc_green_led, "green-led", 1); - qdev_init_gpio_in_named(dev, spitz_misc_orange_led, "orange-led", 1); - qdev_init_gpio_in_named(dev, spitz_misc_adc_temp, "adc-temp-on", 1); - - qdev_init_gpio_out_named(dev, &s->adc_value, "adc-temp", 1); -} - -#define SPITZ_SCP_LED_GREEN 1 -#define SPITZ_SCP_JK_B 2 -#define SPITZ_SCP_CHRG_ON 3 -#define SPITZ_SCP_MUTE_L 4 -#define SPITZ_SCP_MUTE_R 5 -#define SPITZ_SCP_CF_POWER 6 -#define SPITZ_SCP_LED_ORANGE 7 -#define SPITZ_SCP_JK_A 8 -#define SPITZ_SCP_ADC_TEMP_ON 9 -#define SPITZ_SCP2_IR_ON 1 -#define SPITZ_SCP2_AKIN_PULLUP 2 -#define SPITZ_SCP2_BACKLIGHT_CONT 7 -#define SPITZ_SCP2_BACKLIGHT_ON 8 -#define SPITZ_SCP2_MIC_BIAS 9 - -static void spitz_scoop_gpio_setup(SpitzMachineState *sms) -{ - DeviceState *miscdev = sysbus_create_simple(TYPE_SPITZ_MISC_GPIO, -1, NULL); - - sms->misc_gpio = miscdev; - - qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_CHRG_ON, - qdev_get_gpio_in_named(miscdev, "charging", 0)); - qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_JK_B, - qdev_get_gpio_in_named(miscdev, "discharging", 0)); - qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_LED_GREEN, - qdev_get_gpio_in_named(miscdev, "green-led", 0)); - qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_LED_ORANGE, - qdev_get_gpio_in_named(miscdev, "orange-led", 0)); - qdev_connect_gpio_out(sms->scp0, SPITZ_SCP_ADC_TEMP_ON, - qdev_get_gpio_in_named(miscdev, "adc-temp-on", 0)); - qdev_connect_gpio_out_named(miscdev, "adc-temp", 0, - qdev_get_gpio_in(sms->max1111, MAX1111_BATT_TEMP)); - - if (sms->scp1) { - qdev_connect_gpio_out(sms->scp1, SPITZ_SCP2_BACKLIGHT_CONT, - qdev_get_gpio_in_named(sms->lcdtg, "bl_bit5", 0)); - qdev_connect_gpio_out(sms->scp1, SPITZ_SCP2_BACKLIGHT_ON, - qdev_get_gpio_in_named(sms->lcdtg, "bl_power", 0)); - } -} - -#define SPITZ_GPIO_HSYNC 22 -#define SPITZ_GPIO_SD_DETECT 9 -#define SPITZ_GPIO_SD_WP 81 -#define SPITZ_GPIO_ON_RESET 89 -#define SPITZ_GPIO_BAT_COVER 90 -#define SPITZ_GPIO_CF1_IRQ 105 -#define SPITZ_GPIO_CF1_CD 94 -#define SPITZ_GPIO_CF2_IRQ 106 -#define SPITZ_GPIO_CF2_CD 93 - -static int spitz_hsync; - -static void spitz_lcd_hsync_handler(void *opaque, int line, int level) -{ - PXA2xxState *cpu = (PXA2xxState *) opaque; - qemu_set_irq(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_HSYNC), spitz_hsync); - spitz_hsync ^= 1; -} - -static void spitz_reset(void *opaque, int line, int level) -{ - if (level) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } -} - -static void spitz_gpio_setup(PXA2xxState *cpu, int slots) -{ - qemu_irq lcd_hsync; - qemu_irq reset; - - /* - * Bad hack: We toggle the LCD hsync GPIO on every GPIO status - * read to satisfy broken guests that poll-wait for hsync. - * Simulating a real hsync event would be less practical and - * wouldn't guarantee that a guest ever exits the loop. - */ - spitz_hsync = 0; - lcd_hsync = qemu_allocate_irq(spitz_lcd_hsync_handler, cpu, 0); - pxa2xx_gpio_read_notifier(cpu->gpio, lcd_hsync); - pxa2xx_lcd_vsync_notifier(cpu->lcd, lcd_hsync); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(cpu->mmc, - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_WP), - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_DETECT)); - - /* Battery lock always closed */ - qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER)); - - /* Handle reset */ - reset = qemu_allocate_irq(spitz_reset, cpu, 0); - qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, reset); - - /* PCMCIA signals: card's IRQ and Card-Detect */ - if (slots >= 1) - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_IRQ), - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_CD)); - if (slots >= 2) - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_IRQ), - qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_CD)); -} - -/* Board init. */ -#define SPITZ_RAM 0x04000000 -#define SPITZ_ROM 0x00800000 - -static struct arm_boot_info spitz_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, -}; - -static void spitz_common_init(MachineState *machine) -{ - SpitzMachineClass *smc = SPITZ_MACHINE_GET_CLASS(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); - 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); - - /* Setup peripherals */ - spitz_keyboard_register(mpu); - - spitz_ssp_attach(sms); - - sms->scp0 = sysbus_create_simple("scoop", 0x10800000, NULL); - if (model != akita) { - sms->scp1 = sysbus_create_simple("scoop", 0x08800040, NULL); - } else { - sms->scp1 = NULL; - } - - spitz_scoop_gpio_setup(sms); - - spitz_gpio_setup(mpu, (model == akita) ? 1 : 2); - - spitz_i2c_setup(mpu); - - if (model == akita) - spitz_akita_i2c_setup(mpu); - - if (model == terrier) - /* A 6.0 GB microdrive is permanently sitting in CF slot 1. */ - spitz_microdrive_attach(mpu, 1); - else if (model != akita) - /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */ - spitz_microdrive_attach(mpu, 0); - - spitz_binfo.board_id = smc->arm_id; - arm_load_kernel(mpu->cpu, machine, &spitz_binfo); - sl_bootparam_write(SL_PXA_PARAM_BASE); -} - -static void spitz_common_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->block_default_type = IF_IDE; - mc->ignore_memory_transaction_failures = true; - mc->init = spitz_common_init; -} - -static const TypeInfo spitz_common_info = { - .name = TYPE_SPITZ_MACHINE, - .parent = TYPE_MACHINE, - .abstract = true, - .instance_size = sizeof(SpitzMachineState), - .class_size = sizeof(SpitzMachineClass), - .class_init = spitz_common_class_init, -}; - -static void akitapda_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); - - mc->desc = "Sharp SL-C1000 (Akita) PDA (PXA270)"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); - smc->model = akita; - smc->arm_id = 0x2e8; -} - -static const TypeInfo akitapda_type = { - .name = MACHINE_TYPE_NAME("akita"), - .parent = TYPE_SPITZ_MACHINE, - .class_init = akitapda_class_init, -}; - -static void spitzpda_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); - - mc->desc = "Sharp SL-C3000 (Spitz) PDA (PXA270)"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); - smc->model = spitz; - smc->arm_id = 0x2c9; -} - -static const TypeInfo spitzpda_type = { - .name = MACHINE_TYPE_NAME("spitz"), - .parent = TYPE_SPITZ_MACHINE, - .class_init = spitzpda_class_init, -}; - -static void borzoipda_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); - - mc->desc = "Sharp SL-C3100 (Borzoi) PDA (PXA270)"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0"); - smc->model = borzoi; - smc->arm_id = 0x33f; -} - -static const TypeInfo borzoipda_type = { - .name = MACHINE_TYPE_NAME("borzoi"), - .parent = TYPE_SPITZ_MACHINE, - .class_init = borzoipda_class_init, -}; - -static void terrierpda_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - SpitzMachineClass *smc = SPITZ_MACHINE_CLASS(oc); - - mc->desc = "Sharp SL-C3200 (Terrier) PDA (PXA270)"; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5"); - smc->model = terrier; - smc->arm_id = 0x33f; -} - -static const TypeInfo terrierpda_type = { - .name = MACHINE_TYPE_NAME("terrier"), - .parent = TYPE_SPITZ_MACHINE, - .class_init = terrierpda_class_init, -}; - -static void spitz_machine_init(void) -{ - type_register_static(&spitz_common_info); - type_register_static(&akitapda_type); - type_register_static(&spitzpda_type); - type_register_static(&borzoipda_type); - type_register_static(&terrierpda_type); -} - -type_init(spitz_machine_init) - -static bool is_version_0(void *opaque, int version_id) -{ - return version_id == 0; -} - -static const VMStateDescription vmstate_sl_nand_info = { - .name = "sl-nand", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(ctl, SLNANDState), - VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property sl_nand_properties[] = { - DEFINE_PROP_UINT8("manf_id", SLNANDState, manf_id, NAND_MFR_SAMSUNG), - DEFINE_PROP_UINT8("chip_id", SLNANDState, chip_id, 0xf1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sl_nand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_sl_nand_info; - device_class_set_props(dc, sl_nand_properties); - dc->realize = sl_nand_realize; - /* Reason: init() method uses drive_get() */ - dc->user_creatable = false; -} - -static const TypeInfo sl_nand_info = { - .name = TYPE_SL_NAND, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SLNANDState), - .instance_init = sl_nand_init, - .class_init = sl_nand_class_init, -}; - -static const VMStateDescription vmstate_spitz_kbd = { - .name = "spitz-keyboard", - .version_id = 1, - .minimum_version_id = 0, - .post_load = spitz_keyboard_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(sense_state, SpitzKeyboardState), - VMSTATE_UINT16(strobe_state, SpitzKeyboardState), - VMSTATE_UNUSED_TEST(is_version_0, 5), - VMSTATE_END_OF_LIST(), - }, -}; - -static void spitz_keyboard_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_spitz_kbd; - dc->realize = spitz_keyboard_realize; -} - -static const TypeInfo spitz_keyboard_info = { - .name = TYPE_SPITZ_KEYBOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpitzKeyboardState), - .instance_init = spitz_keyboard_init, - .class_init = spitz_keyboard_class_init, -}; - -static const VMStateDescription vmstate_corgi_ssp_regs = { - .name = "corgi-ssp", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_SSI_PERIPHERAL(ssidev, CorgiSSPState), - VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3), - VMSTATE_END_OF_LIST(), - } -}; - -static void corgi_ssp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - - k->realize = corgi_ssp_realize; - k->transfer = corgi_ssp_transfer; - dc->vmsd = &vmstate_corgi_ssp_regs; -} - -static const TypeInfo corgi_ssp_info = { - .name = TYPE_CORGI_SSP, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(CorgiSSPState), - .class_init = corgi_ssp_class_init, -}; - -static const VMStateDescription vmstate_spitz_lcdtg_regs = { - .name = "spitz-lcdtg", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_SSI_PERIPHERAL(ssidev, SpitzLCDTG), - VMSTATE_UINT32(bl_intensity, SpitzLCDTG), - VMSTATE_UINT32(bl_power, SpitzLCDTG), - VMSTATE_END_OF_LIST(), - } -}; - -static void spitz_lcdtg_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - - k->realize = spitz_lcdtg_realize; - k->transfer = spitz_lcdtg_transfer; - dc->vmsd = &vmstate_spitz_lcdtg_regs; -} - -static const TypeInfo spitz_lcdtg_info = { - .name = TYPE_SPITZ_LCDTG, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(SpitzLCDTG), - .class_init = spitz_lcdtg_class_init, -}; - -static const TypeInfo spitz_misc_gpio_info = { - .name = TYPE_SPITZ_MISC_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpitzMiscGPIOState), - .instance_init = spitz_misc_gpio_init, - /* - * No class_init required: device has no internal state so does not - * need to set up reset or vmstate, and does not have a realize method. - */ -}; - -static void spitz_register_types(void) -{ - type_register_static(&corgi_ssp_info); - type_register_static(&spitz_lcdtg_info); - type_register_static(&spitz_keyboard_info); - type_register_static(&sl_nand_info); - type_register_static(&spitz_misc_gpio_info); -} - -type_init(spitz_register_types) diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index a9e96c37f8..376746251e 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; @@ -391,7 +394,7 @@ static void stellaris_sys_reset_enter(Object *obj, ResetType type) s->dcgc[0] = 1; } -static void stellaris_sys_reset_hold(Object *obj) +static void stellaris_sys_reset_hold(Object *obj, ResetType type) { ssys_state *s = STELLARIS_SYS(obj); @@ -399,7 +402,7 @@ static void stellaris_sys_reset_hold(Object *obj) ssys_calculate_system_clock(s, true); } -static void stellaris_sys_reset_exit(Object *obj) +static void stellaris_sys_reset_exit(Object *obj, ResetType type) { } @@ -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, ResetType type) +{ + 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, ResetType type) +{ + 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, ResetType type) { + 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)) { @@ -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..72ae62156f 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -30,6 +30,7 @@ #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" +#define RCC_ADDR 0x40023800 #define SYSCFG_ADD 0x40013800 static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800, 0x40004C00, 0x40005000, 0x40011400, @@ -59,6 +60,8 @@ static void stm32f405_soc_initfn(Object *obj) object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32_RCC); + object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32F4XX_SYSCFG); for (i = 0; i < STM_NUM_USARTS; i++) { @@ -139,9 +142,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); @@ -151,6 +163,14 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) return; } + /* Reset and clock controller */ + dev = DEVICE(&s->rcc); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->rcc), errp)) { + return; + } + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, RCC_ADDR); + /* System configuration controller */ dev = DEVICE(&s->syscfg); if (!sysbus_realize(SYS_BUS_DEVICE(&s->syscfg), errp)) { @@ -267,7 +287,6 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("GPIOH", 0x40021C00, 0x400); create_unimplemented_device("GPIOI", 0x40022000, 0x400); create_unimplemented_device("CRC", 0x40023000, 0x400); - create_unimplemented_device("RCC", 0x40023800, 0x400); create_unimplemented_device("Flash Int", 0x40023C00, 0x400); create_unimplemented_device("BKPSRAM", 0x40024000, 0x400); create_unimplemented_device("DMA1", 0x40026000, 0x400); @@ -279,17 +298,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..16e3505dcb --- /dev/null +++ b/hw/arm/stm32l4x5_soc.c @@ -0,0 +1,494 @@ +/* + * STM32L4x5 SoC family + * + * Copyright (c) 2023-2024 Arnaud Minier + * Copyright (c) 2023-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. + * + * 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/char/stm32l4x5_usart.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 + +#define EXTI_USART1_IRQ 26 +#define EXTI_UART4_IRQ 29 +#define EXTI_LPUART1_IRQ 31 + +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 const hwaddr usart_addr[] = { + 0x40013800, /* "USART1", 0x400 */ + 0x40004400, /* "USART2", 0x400 */ + 0x40004800, /* "USART3", 0x400 */ +}; +static const hwaddr uart_addr[] = { + 0x40004C00, /* "UART4" , 0x400 */ + 0x40005000 /* "UART5" , 0x400 */ +}; + +#define LPUART_BASE_ADDRESS 0x40008000 + +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); + } + + for (int i = 0; i < STM_NUM_USARTS; i++) { + object_initialize_child(obj, "usart[*]", &s->usart[i], + TYPE_STM32L4X5_USART); + } + + for (int i = 0; i < STM_NUM_UARTS; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], + TYPE_STM32L4X5_UART); + } + object_initialize_child(obj, "lpuart1", &s->lpuart, + TYPE_STM32L4X5_LPUART); +} + +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); + qdev_connect_clock_in(DEVICE(&s->syscfg), "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "syscfg-out")); + 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)); + } + } + + qdev_pass_gpios(DEVICE(&s->syscfg), dev_soc, NULL); + + /* 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])); + } + } + + /* Connect SYSCFG to EXTI */ + 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)); + + /* USART devices */ + for (int i = 0; i < STM_NUM_USARTS; i++) { + g_autofree char *name = g_strdup_printf("usart%d-out", i + 1); + dev = DEVICE(&(s->usart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, usart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), + EXTI_USART1_IRQ + i)); + } + + /* UART devices */ + for (int i = 0; i < STM_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d-out", STM_NUM_USARTS + i + 1); + dev = DEVICE(&(s->uart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + i)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), name)); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, uart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), + EXTI_UART4_IRQ + i)); + } + + /* LPUART device*/ + dev = DEVICE(&(s->lpuart)); + qdev_prop_set_chr(dev, "chardev", serial_hd(STM_NUM_USARTS + STM_NUM_UARTS)); + qdev_connect_clock_in(dev, "clk", + qdev_get_clock_out(DEVICE(&(s->rcc)), "lpuart1-out")); + busdev = SYS_BUS_DEVICE(dev); + if (!sysbus_realize(busdev, errp)) { + return; + } + sysbus_mmio_map(busdev, 0, LPUART_BASE_ADDRESS); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(DEVICE(&s->exti), + EXTI_LPUART1_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("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); + /* 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); + /* 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 67675e952f..cc41935160 100644 --- a/hw/arm/stm32vldiscovery.c +++ b/hw/arm/stm32vldiscovery.c @@ -47,7 +47,7 @@ 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); @@ -58,8 +58,14 @@ static void stm32vldiscovery_init(MachineState *machine) 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..612115ab5b 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), @@ -1326,7 +1342,7 @@ static void strongarm_uart_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "StrongARM UART controller"; - dc->reset = strongarm_uart_reset; + device_class_set_legacy_reset(dc, strongarm_uart_reset); dc->vmsd = &vmstate_strongarm_uart_regs; device_class_set_props(dc, strongarm_uart_properties); dc->realize = strongarm_uart_realize; @@ -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), @@ -1573,7 +1595,7 @@ static void strongarm_ssp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "StrongARM SSP controller"; - dc->reset = strongarm_ssp_reset; + device_class_set_legacy_reset(dc, strongarm_ssp_reset); dc->vmsd = &vmstate_strongarm_ssp_regs; } diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c deleted file mode 100644 index d5a6763cf9..0000000000 --- a/hw/arm/tosa.c +++ /dev/null @@ -1,326 +0,0 @@ -/* vim:set shiftwidth=4 ts=4 et: */ -/* - * PXA255 Sharp Zaurus SL-6000 PDA platform - * - * Copyright (c) 2008 Dmitry Baryshkov - * - * Code based on spitz platform by Andrzej Zaborowski - * This code is licensed under the GNU GPL v2. - * - * 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 "qapi/error.h" -#include "sysemu/runstate.h" -#include "hw/arm/pxa.h" -#include "hw/arm/boot.h" -#include "hw/arm/sharpsl.h" -#include "hw/pcmcia.h" -#include "hw/boards.h" -#include "hw/display/tc6393xb.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "hw/ssi/ssi.h" -#include "hw/sysbus.h" -#include "hw/misc/led.h" -#include "exec/address-spaces.h" -#include "qom/object.h" - -#define TOSA_RAM 0x04000000 -#define TOSA_ROM 0x00800000 - -#define TOSA_GPIO_USB_IN (5) -#define TOSA_GPIO_nSD_DETECT (9) -#define TOSA_GPIO_ON_RESET (19) -#define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */ -#define TOSA_GPIO_CF_CD (13) -#define TOSA_GPIO_TC6393XB_INT (15) -#define TOSA_GPIO_JC_CF_IRQ (36) /* CF slot1 Ready */ - -#define TOSA_SCOOP_GPIO_BASE 1 -#define TOSA_GPIO_IR_POWERDWN (TOSA_SCOOP_GPIO_BASE + 2) -#define TOSA_GPIO_SD_WP (TOSA_SCOOP_GPIO_BASE + 3) -#define TOSA_GPIO_PWR_ON (TOSA_SCOOP_GPIO_BASE + 4) - -#define TOSA_SCOOP_JC_GPIO_BASE 1 -#define TOSA_GPIO_BT_LED (TOSA_SCOOP_JC_GPIO_BASE + 0) -#define TOSA_GPIO_NOTE_LED (TOSA_SCOOP_JC_GPIO_BASE + 1) -#define TOSA_GPIO_CHRG_ERR_LED (TOSA_SCOOP_JC_GPIO_BASE + 2) -#define TOSA_GPIO_TC6393XB_L3V_ON (TOSA_SCOOP_JC_GPIO_BASE + 5) -#define TOSA_GPIO_WLAN_LED (TOSA_SCOOP_JC_GPIO_BASE + 7) - -#define DAC_BASE 0x4e -#define DAC_CH1 0 -#define DAC_CH2 1 - -static void tosa_microdrive_attach(PXA2xxState *cpu) -{ - PCMCIACardState *md; - DriveInfo *dinfo; - - dinfo = drive_get(IF_IDE, 0, 0); - if (!dinfo || dinfo->media_cd) - return; - md = dscm1xxxx_init(dinfo); - pxa2xx_pcmcia_attach(cpu->pcmcia[0], md); -} - -/* - * Encapsulation of some GPIO line behaviour for the Tosa board - * - * QEMU interface: - * + named GPIO inputs "leds[0..3]": assert to light LEDs - * + named GPIO input "reset": when asserted, resets the system - */ - -#define TYPE_TOSA_MISC_GPIO "tosa-misc-gpio" -OBJECT_DECLARE_SIMPLE_TYPE(TosaMiscGPIOState, TOSA_MISC_GPIO) - -struct TosaMiscGPIOState { - SysBusDevice parent_obj; -}; - -static void tosa_reset(void *opaque, int line, int level) -{ - if (level) { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } -} - -static void tosa_misc_gpio_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - - qdev_init_gpio_in_named(dev, tosa_reset, "reset", 1); -} - -static void tosa_gpio_setup(PXA2xxState *cpu, - DeviceState *scp0, - DeviceState *scp1, - TC6393xbState *tmio) -{ - DeviceState *misc_gpio; - LEDState *led[4]; - - misc_gpio = sysbus_create_simple(TYPE_TOSA_MISC_GPIO, -1, NULL); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(cpu->mmc, - qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP), - qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT))); - - /* Handle reset */ - qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, - qdev_get_gpio_in_named(misc_gpio, "reset", 0)); - - /* PCMCIA signals: card's IRQ and Card-Detect */ - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_IRQ), - qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_CD)); - - pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ), - NULL); - - led[0] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, - LED_COLOR_BLUE, "bluetooth"); - led[1] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, - LED_COLOR_GREEN, "note"); - led[2] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, - LED_COLOR_AMBER, "charger-error"); - led[3] = led_create_simple(OBJECT(misc_gpio), GPIO_POLARITY_ACTIVE_HIGH, - LED_COLOR_GREEN, "wlan"); - - qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, - qdev_get_gpio_in(DEVICE(led[0]), 0)); - qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, - qdev_get_gpio_in(DEVICE(led[1]), 0)); - qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, - qdev_get_gpio_in(DEVICE(led[2]), 0)); - qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, - qdev_get_gpio_in(DEVICE(led[3]), 0)); - - qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); - - /* UDC Vbus */ - qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_USB_IN)); -} - -static uint32_t tosa_ssp_tansfer(SSIPeripheral *dev, uint32_t value) -{ - fprintf(stderr, "TG: %u %02x\n", value >> 5, value & 0x1f); - return 0; -} - -static void tosa_ssp_realize(SSIPeripheral *dev, Error **errp) -{ - /* Nothing to do. */ -} - -#define TYPE_TOSA_DAC "tosa_dac" -OBJECT_DECLARE_SIMPLE_TYPE(TosaDACState, TOSA_DAC) - -struct TosaDACState { - I2CSlave parent_obj; - - int len; - char buf[3]; -}; - -static int tosa_dac_send(I2CSlave *i2c, uint8_t data) -{ - TosaDACState *s = TOSA_DAC(i2c); - - s->buf[s->len] = data; - if (s->len ++ > 2) { -#ifdef VERBOSE - fprintf(stderr, "%s: message too long (%i bytes)\n", __func__, s->len); -#endif - return 1; - } - - if (s->len == 2) { - fprintf(stderr, "dac: channel %d value 0x%02x\n", - s->buf[0], s->buf[1]); - } - - return 0; -} - -static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event) -{ - TosaDACState *s = TOSA_DAC(i2c); - - s->len = 0; - switch (event) { - case I2C_START_SEND: - break; - case I2C_START_RECV: - printf("%s: recv not supported!!!\n", __func__); - break; - case I2C_FINISH: -#ifdef VERBOSE - if (s->len < 2) - printf("%s: message too short (%i bytes)\n", __func__, s->len); - if (s->len > 2) - printf("%s: message too long\n", __func__); -#endif - break; - default: - break; - } - - return 0; -} - -static uint8_t tosa_dac_recv(I2CSlave *s) -{ - printf("%s: recv not supported!!!\n", __func__); - return 0xff; -} - -static void tosa_tg_init(PXA2xxState *cpu) -{ - I2CBus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); - i2c_slave_create_simple(bus, TYPE_TOSA_DAC, DAC_BASE); - ssi_create_peripheral(cpu->ssp[1], "tosa-ssp"); -} - - -static struct arm_boot_info tosa_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = 0x04000000, -}; - -static void tosa_init(MachineState *machine) -{ - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *rom = g_new(MemoryRegion, 1); - PXA2xxState *mpu; - TC6393xbState *tmio; - DeviceState *scp0, *scp1; - - mpu = pxa255_init(address_space_mem, 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); - - tmio = tc6393xb_init(address_space_mem, 0x10000000, - qdev_get_gpio_in(mpu->gpio, TOSA_GPIO_TC6393XB_INT)); - - scp0 = sysbus_create_simple("scoop", 0x08800000, NULL); - scp1 = sysbus_create_simple("scoop", 0x14800040, NULL); - - tosa_gpio_setup(mpu, scp0, scp1, tmio); - - tosa_microdrive_attach(mpu); - - tosa_tg_init(mpu); - - tosa_binfo.board_id = 0x208; - arm_load_kernel(mpu->cpu, machine, &tosa_binfo); - sl_bootparam_write(SL_PXA_PARAM_BASE); -} - -static void tosapda_machine_init(MachineClass *mc) -{ - mc->desc = "Sharp SL-6000 (Tosa) PDA (PXA255)"; - mc->init = tosa_init; - mc->block_default_type = IF_IDE; - mc->ignore_memory_transaction_failures = true; -} - -DEFINE_MACHINE("tosa", tosapda_machine_init) - -static void tosa_dac_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->event = tosa_dac_event; - k->recv = tosa_dac_recv; - k->send = tosa_dac_send; -} - -static const TypeInfo tosa_dac_info = { - .name = TYPE_TOSA_DAC, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(TosaDACState), - .class_init = tosa_dac_class_init, -}; - -static void tosa_ssp_class_init(ObjectClass *klass, void *data) -{ - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - - k->realize = tosa_ssp_realize; - k->transfer = tosa_ssp_tansfer; -} - -static const TypeInfo tosa_ssp_info = { - .name = "tosa-ssp", - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(SSIPeripheral), - .class_init = tosa_ssp_class_init, -}; - -static const TypeInfo tosa_misc_gpio_info = { - .name = TYPE_TOSA_MISC_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TosaMiscGPIOState), - .instance_init = tosa_misc_gpio_init, - /* - * No class init required: device has no internal state so does not - * need to set up reset or vmstate, and has no realize method. - */ -}; - -static void tosa_register_types(void) -{ - type_register_static(&tosa_dac_info); - type_register_static(&tosa_ssp_info); - type_register_static(&tosa_misc_gpio_info); -} - -type_init(tosa_register_types) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 2dee296c8f..c64ad344bd 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -5,18 +5,20 @@ 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_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_iotlb_inv_asid_vmid(int asid, int vmid) "IOTLB invalidate asid=%d vmid=%d" +smmu_iotlb_inv_vmid(int vmid) "IOTLB invalidate vmid=%d" +smmu_iotlb_inv_vmid_s1(int vmid) "IOTLB invalidate vmid=%d" +smmu_iotlb_inv_iova(int 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(int asid, int 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(int asid, int 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(int asid, int 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)" @@ -36,7 +38,7 @@ smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64 smmuv3_translate_disable(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x bypass (smmu disabled) iova:0x%"PRIx64" is_write=%d" smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x STE bypass iova:0x%"PRIx64" is_write=%d" smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=0x%x abort on iova:0x%"PRIx64" is_write=%d" -smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=0x%x iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" +smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm, int stage) "%s sid=0x%x iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x stage=%d" smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 smmuv3_decode_cd(uint32_t oas) "oas=%d" smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz, bool had) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d had:%d" @@ -45,11 +47,26 @@ 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_cmdq_tlbi_nh(void) "" -smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" +smmuv3_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf, int stage) "vmid=%d asid=%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d stage=%d" +smmuv3_cmdq_tlbi_nh(int vmid) "vmid=%d" +smmuv3_cmdq_tlbi_nsnh(void) "" +smmuv3_cmdq_tlbi_nh_asid(int asid) "asid=%d" +smmuv3_cmdq_tlbi_s12_vmid(int 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, int asid, int vmid, uint64_t iova, uint8_t tg, uint64_t num_pages, int stage) "iommu mr=%s asid=%d vmid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" stage=%d" +# 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" + +# 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 4156111d49..620992c92c 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" @@ -48,15 +48,17 @@ #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/virtio/virtio-acpi.h" +#include "target/arm/multiprocessing.h" #define ARM_SPI_BASE 32 @@ -76,11 +78,11 @@ static void acpi_dsdt_add_cpus(Aml *scope, VirtMachineState *vms) } static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, - uint32_t uart_irq) + uint32_t uart_irq, int uartidx) { - Aml *dev = aml_device("COM0"); + Aml *dev = aml_device("COM%d", uartidx); aml_append(dev, aml_name_decl("_HID", aml_string("ARMH0011"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(uartidx))); Aml *crs = aml_resource_template(); aml_append(crs, aml_memory32_fixed(uart_memmap->base, @@ -93,21 +95,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; @@ -132,32 +119,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) { @@ -192,10 +153,10 @@ static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap, aml_append(dev, aml_name_decl("_CRS", crs)); Aml *aei = aml_resource_template(); - /* Pin 3 for power button */ - const uint32_t pin_list[1] = {3}; + + const uint32_t pin = GPIO_PIN_POWER_BUTTON; aml_append(aei, aml_gpio_int(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH, - AML_EXCLUSIVE, AML_PULL_UP, 0, pin_list, 1, + AML_EXCLUSIVE, AML_PULL_UP, 0, &pin, 1, "GPO0", NULL, 0)); aml_append(dev, aml_name_decl("_AEI", aei)); @@ -247,12 +208,19 @@ static void acpi_dsdt_add_tpm(Aml *scope, VirtMachineState *vms) #define ROOT_COMPLEX_ENTRY_SIZE 36 #define IORT_NODE_OFFSET 48 +/* + * Append an ID mapping entry as described by "Table 4 ID mapping format" in + * "IO Remapping Table System Software on ARM Platforms", Chapter 3. + * Document number: ARM DEN 0049E.f, Apr 2024 + * + * Note that @id_count gets internally subtracted by one, following the spec. + */ static void build_iort_id_mapping(GArray *table_data, uint32_t input_base, uint32_t id_count, uint32_t out_ref) { - /* Table 4 ID mapping format */ build_append_int_noprefix(table_data, input_base, 4); /* Input base */ - build_append_int_noprefix(table_data, id_count, 4); /* Number of IDs */ + /* Number of IDs - The number of IDs in the range minus one */ + build_append_int_noprefix(table_data, id_count - 1, 4); build_append_int_noprefix(table_data, input_base, 4); /* Output base */ build_append_int_noprefix(table_data, out_ref, 4); /* Output Reference */ /* Flags */ @@ -307,7 +275,6 @@ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i, nb_nodes, rc_mapping_count; - const uint32_t iort_node_offset = IORT_NODE_OFFSET; size_t node_size, smmu_offset = 0; AcpiIortIdMapping *idmap; uint32_t id = 0; @@ -344,8 +311,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } /* Append the last RC -> ITS ID mapping */ - if (next_range.input_base < 0xFFFF) { - next_range.id_count = 0xFFFF - next_range.input_base; + if (next_range.input_base < 0x10000) { + next_range.id_count = 0x10000 - next_range.input_base; g_array_append_val(its_idmaps, next_range); } @@ -404,7 +371,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 4); /* output IORT node is the ITS group node (the first node) */ - build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); } /* Table 17 Root Complex Node */ @@ -453,11 +420,11 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) range = &g_array_index(its_idmaps, AcpiIortIdMapping, i); /* output IORT node is the ITS group node (the first node) */ build_iort_id_mapping(table_data, range->input_base, - range->id_count, iort_node_offset); + range->id_count, IORT_NODE_OFFSET); } } else { /* output IORT node is the ITS group node (the first node) */ - build_iort_id_mapping(table_data, 0, 0xFFFF, IORT_NODE_OFFSET); + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); } acpi_table_end(linker, &table); @@ -470,48 +437,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_UART0].base, + .interrupt_type = (1 << 3),/* Bit[3] ARMH GIC interrupt*/ + .pc_interrupt = 0, /* IRQ */ + .interrupt = (vms->irqmap[VIRT_UART0] + 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); } /* @@ -557,6 +510,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } } + build_srat_generic_affinity_structures(table_data); + if (ms->nvdimms_state->is_enabled) { nvdimm_build_srat(table_data); } @@ -572,8 +527,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) @@ -587,7 +542,7 @@ 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); @@ -600,21 +555,21 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * 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 */ @@ -623,7 +578,15 @@ build_gtdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) 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,12 +635,12 @@ 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, - vms->memmap[VIRT_UART].base); + build_append_gas(table_data, AML_AS_SYSTEM_MEMORY, 32, 0, 3, + vms->memmap[VIRT_UART0].base); /* AddressSize[] */ build_append_int_noprefix(table_data, - vms->memmap[VIRT_UART].size, 4); + vms->memmap[VIRT_UART0].size, 4); /* NamespaceString[] */ g_array_append_vals(table_data, name, namespace_length); @@ -694,7 +657,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 */ } @@ -728,9 +691,9 @@ 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 == VIRT_GIC_VERSION_2) { physical_base_address = memmap[VIRT_GIC_CPU].base; @@ -759,7 +722,7 @@ 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 */ @@ -810,10 +773,10 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms, unsigned dsdt_tbl_offset) { - /* ACPI v6.0 */ + /* ACPI v6.3 */ AcpiFadtData fadt = { .rev = 6, - .minor_ver = 0, + .minor_ver = 3, .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI, .xdsdt_tbl_offset = &dsdt_tbl_offset, }; @@ -858,14 +821,19 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) */ scope = aml_scope("\\_SB"); acpi_dsdt_add_cpus(scope, vms); - acpi_dsdt_add_uart(scope, &memmap[VIRT_UART], - (irqmap[VIRT_UART] + ARM_SPI_BASE)); + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], + (irqmap[VIRT_UART0] + ARM_SPI_BASE), 0); + if (vms->second_ns_uart_present) { + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1], + (irqmap[VIRT_UART1] + ARM_SPI_BASE), 1); + } 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, @@ -968,7 +936,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); @@ -1046,7 +1014,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); @@ -1099,7 +1067,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 b871350856..1a381e9a2b 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,50 +63,76 @@ #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 "hvf_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" -#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ - static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ - void *data) \ +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_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - virt_machine_##major##_##minor##_options(mc); \ - mc->desc = "QEMU " # major "." # minor " ARM Virtual Machine"; \ + arm_virt_compat_set(mc); \ + MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ + mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " ARM Virtual Machine"; \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ } \ } \ - static const TypeInfo machvirt_##major##_##minor##_info = { \ - .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ - .parent = TYPE_VIRT_MACHINE, \ - .class_init = virt_##major##_##minor##_class_init, \ - }; \ - static void machvirt_machine_##major##_##minor##_init(void) \ + static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ { \ - type_register_static(&machvirt_##major##_##minor##_info); \ + .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ + .parent = TYPE_VIRT_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ + { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ + type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ } \ - type_init(machvirt_machine_##major##_##minor##_init); + type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); #define DEFINE_VIRT_MACHINE_AS_LATEST(major, minor) \ - DEFINE_VIRT_MACHINE_LATEST(major, minor, true) + DEFINE_VIRT_MACHINE_IMPL(true, major, minor) #define DEFINE_VIRT_MACHINE(major, minor) \ - DEFINE_VIRT_MACHINE_LATEST(major, minor, false) + DEFINE_VIRT_MACHINE_IMPL(false, major, minor) /* Number of external interrupt lines to configure the GIC with */ @@ -143,11 +170,11 @@ static const MemMapEntry base_memmap[] = { [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 }, /* This redistributor space allows up to 2*64kB*123 CPUs */ [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, - [VIRT_UART] = { 0x09000000, 0x00001000 }, + [VIRT_UART0] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_GPIO] = { 0x09030000, 0x00001000 }, - [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 }, + [VIRT_UART1] = { 0x09040000, 0x00001000 }, [VIRT_SMMU] = { 0x09050000, 0x00020000 }, [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN }, [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN }, @@ -174,6 +201,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) */ @@ -184,11 +217,11 @@ static MemMapEntry extended_memmap[] = { }; static const int a15irqmap[] = { - [VIRT_UART] = 1, + [VIRT_UART0] = 1, [VIRT_RTC] = 2, [VIRT_PCIE] = 3, /* ... to 6 */ [VIRT_GPIO] = 7, - [VIRT_SECURE_UART] = 8, + [VIRT_UART1] = 8, [VIRT_ACPI_GED] = 9, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ @@ -196,32 +229,6 @@ 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-a35"), - 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) -{ - 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_randomness(MachineState *ms, const char *node) { struct { @@ -236,6 +243,20 @@ static void create_randomness(MachineState *ms, const char *node) 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) { MachineState *ms = MACHINE(vms); @@ -255,6 +276,17 @@ static void create_fdt(VirtMachineState *vms) qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); qemu_fdt_setprop_string(fdt, "/", "model", "linux,dummy-virt"); + /* + * For QEMU, all DMA is coherent. Advertising this in the root node + * has two benefits: + * + * - It avoids potential bugs where we forget to mark a DMA + * capable device as being dma-coherent + * - It avoids spurious warnings from the Linux kernel about + * devices which can't do DMA at all + */ + qemu_fdt_setprop(fdt, "/", "dma-coherent", NULL, 0); + /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); if (vms->dtb_randomness) { @@ -268,6 +300,8 @@ static void create_fdt(VirtMachineState *vms) } } + qemu_fdt_add_subnode(fdt, "/aliases"); + /* Clock node, for the benefit of the UART. The kernel device tree * binding documentation claims the PL011 node clock properties are * optional but in practice if you omit them the kernel refuses to @@ -353,11 +387,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) @@ -384,7 +436,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; } @@ -411,10 +463,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) { @@ -558,7 +610,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 { @@ -582,7 +635,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); } } @@ -614,7 +668,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); } } @@ -635,13 +690,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; } @@ -679,10 +733,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, @@ -693,6 +747,20 @@ static void create_v2m(VirtMachineState *vms) vms->msi_controller = VIRT_MSI_CTRL_GICV2M; } +/* + * If the CPU has FEAT_NMI, then turn on the NMI support in the GICv3 too. + * It's permitted to have a configuration with NMI in the CPU (and thus the + * GICv3 CPU interface) but not in the distributor/redistributors, but it's + * not very useful. + */ +static bool gicv3_nmi_present(VirtMachineState *vms) +{ + ARMCPU *cpu = ARM_CPU(qemu_get_cpu(0)); + + return tcg_enabled() && cpu_isar_feature(aa64_nmi, cpu) && + (vms->gic_version != VIRT_GIC_VERSION_2); +} + static void create_gic(VirtMachineState *vms, MemoryRegion *mem) { MachineState *ms = MACHINE(vms); @@ -735,14 +803,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) { @@ -751,20 +828,17 @@ 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", vms->virt); } } + + if (gicv3_nmi_present(vms)) { + qdev_prop_set_bit(vms->gic, "has-nmi", true); + } + gicbusdev = SYS_BUS_DEVICE(vms->gic); sysbus_realize_and_unref(gicbusdev, &error_fatal); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); @@ -784,12 +858,12 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) /* 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. + * and the GIC's IRQ/FIQ/VIRQ/VFIQ/NMI/VINMI interrupt outputs to the + * CPU's inputs. */ 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. */ @@ -798,27 +872,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)); @@ -828,6 +903,13 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); sysbus_connect_irq(gicbusdev, i + 3 * smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + + if (vms->gic_version != VIRT_GIC_VERSION_2) { + sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_NMI)); + sysbus_connect_irq(gicbusdev, i + 5 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VINMI)); + } } fdt_add_gic_node(vms); @@ -840,7 +922,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) } static void create_uart(const VirtMachineState *vms, int uart, - MemoryRegion *mem, Chardev *chr) + MemoryRegion *mem, Chardev *chr, bool secure) { char *nodename; hwaddr base = vms->memmap[uart].base; @@ -873,9 +955,13 @@ static void create_uart(const VirtMachineState *vms, int uart, qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, sizeof(clocknames)); - if (uart == VIRT_UART) { + if (uart == VIRT_UART0) { qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", nodename); } else { + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial1", nodename); + } + if (secure) { /* Mark as not usable by the normal world */ qemu_fdt_setprop_string(ms->fdt, nodename, "status", "disabled"); qemu_fdt_setprop_string(ms->fdt, nodename, "secure-status", "okay"); @@ -919,7 +1005,7 @@ static void virt_powerdown_req(Notifier *n, void *opaque) if (s->acpi_dev) { acpi_send_event(s->acpi_dev, ACPI_POWER_DOWN_STATUS); } else { - /* use gpio Pin 3 for power button event */ + /* use gpio Pin for power button event */ qemu_set_irq(qdev_get_gpio_in(gpio_key_dev, 0), 1); } } @@ -928,7 +1014,8 @@ static void create_gpio_keys(char *fdt, DeviceState *pl061_dev, uint32_t phandle) { gpio_key_dev = sysbus_create_simple("gpio-key", -1, - qdev_get_gpio_in(pl061_dev, 3)); + qdev_get_gpio_in(pl061_dev, + GPIO_PIN_POWER_BUTTON)); qemu_fdt_add_subnode(fdt, "/gpio-keys"); qemu_fdt_setprop_string(fdt, "/gpio-keys", "compatible", "gpio-keys"); @@ -939,7 +1026,7 @@ static void create_gpio_keys(char *fdt, DeviceState *pl061_dev, qemu_fdt_setprop_cell(fdt, "/gpio-keys/poweroff", "linux,code", KEY_POWER); qemu_fdt_setprop_cells(fdt, "/gpio-keys/poweroff", - "gpios", phandle, 3, 0); + "gpios", phandle, GPIO_PIN_POWER_BUTTON, 0); } #define SECURE_GPIO_POWEROFF 0 @@ -1322,6 +1409,7 @@ static void create_pcie_irq_map(const MachineState *ms, static void create_smmu(const VirtMachineState *vms, PCIBus *bus) { + VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); char *node; const char compat[] = "arm,smmu-v3"; int irq = vms->irqmap[VIRT_SMMU]; @@ -1336,8 +1424,11 @@ static void create_smmu(const VirtMachineState *vms, return; } - dev = qdev_new("arm-smmuv3"); + dev = qdev_new(TYPE_ARM_SMMUV3); + if (!vmc->no_nested_smmu) { + object_property_set_str(OBJECT(dev), "stage", "nested", &error_fatal); + } object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -1416,6 +1507,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); @@ -1465,15 +1557,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); @@ -1608,9 +1692,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()) { @@ -1618,10 +1704,13 @@ 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); - 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); @@ -1687,7 +1776,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) @@ -1730,8 +1871,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) { @@ -1745,48 +1886,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 @@ -1795,118 +1979,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); } /* @@ -1951,13 +2066,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 { @@ -1992,16 +2108,12 @@ 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); /* * In accelerated mode, the memory map is computed earlier in kvm_type() - * to create a VM with the right number of IPA bits. + * for Linux, or hvf_get_physical_address_range() for macOS to create a + * VM with the right number of IPA bits. */ if (!vms->memmap) { Object *cpuobj; @@ -2009,7 +2121,7 @@ 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. */ @@ -2070,35 +2182,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())) { + if (vms->mte && 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); } @@ -2165,54 +2283,67 @@ static void machvirt_init(MachineState *machine) } if (vms->mte) { - /* Create the memory region only once, but link to all cpus. */ - if (!tag_sysmem) { - /* - * The property exists only if MemTag is supported. - * If it is, we must allocate the ram to back that up. - */ - if (!object_property_find(cpuobj, "tag-memory")) { - error_report("MTE requested, but not supported " - "by the guest CPU"); + if (tcg_enabled()) { + /* Create the memory region only once, but link to all cpus. */ + if (!tag_sysmem) { + /* + * The property exists only if MemTag is supported. + * If it is, we must allocate the ram to back that up. + */ + if (!object_property_find(cpuobj, "tag-memory")) { + error_report("MTE requested, but not supported " + "by the guest CPU"); + exit(1); + } + + tag_sysmem = g_new(MemoryRegion, 1); + memory_region_init(tag_sysmem, OBJECT(machine), + "tag-memory", UINT64_MAX / 32); + + if (vms->secure) { + secure_tag_sysmem = g_new(MemoryRegion, 1); + memory_region_init(secure_tag_sysmem, OBJECT(machine), + "secure-tag-memory", + UINT64_MAX / 32); + + /* As with ram, secure-tag takes precedence over tag. */ + memory_region_add_subregion_overlap(secure_tag_sysmem, + 0, tag_sysmem, -1); + } + } + + object_property_set_link(cpuobj, "tag-memory", + OBJECT(tag_sysmem), &error_abort); + if (vms->secure) { + object_property_set_link(cpuobj, "secure-tag-memory", + OBJECT(secure_tag_sysmem), + &error_abort); + } + } else if (kvm_enabled()) { + if (!kvm_arm_mte_supported()) { + error_report("MTE requested, but not supported by KVM"); exit(1); } - - tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(tag_sysmem, OBJECT(machine), - "tag-memory", UINT64_MAX / 32); - - if (vms->secure) { - secure_tag_sysmem = g_new(MemoryRegion, 1); - memory_region_init(secure_tag_sysmem, OBJECT(machine), - "secure-tag-memory", UINT64_MAX / 32); - - /* As with ram, secure-tag takes precedence over tag. */ - memory_region_add_subregion_overlap(secure_tag_sysmem, 0, - tag_sysmem, -1); - } - } - - object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem), - &error_abort); - if (vms->secure) { - object_property_set_link(cpuobj, "secure-tag-memory", - OBJECT(secure_tag_sysmem), - &error_abort); + kvm_arm_enable_mte(cpuobj, &error_abort); + } else { + error_report("MTE requested, but not supported "); + exit(1); } } 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); @@ -2222,11 +2353,41 @@ static void machvirt_init(MachineState *machine) fdt_add_pmu_nodes(vms); - create_uart(vms, VIRT_UART, sysmem, serial_hd(0)); + /* + * The first UART always exists. If the security extensions are + * enabled, the second UART also always exists. Otherwise, it only exists + * if a backend is configured explicitly via '-serial '. + * This avoids potentially breaking existing user setups that expect + * only one NonSecure UART to be present (for instance, older EDK2 + * binaries). + * + * The nodes end up in the DTB in reverse order of creation, so we must + * create UART0 last to ensure it appears as the first node in the DTB, + * for compatibility with guest software that just iterates through the + * DTB to find the first UART, as older versions of EDK2 do. + * DTB readers that follow the spec, as Linux does, should honour the + * aliases node information and /chosen/stdout-path regardless of + * the order that nodes appear in the DTB. + * + * For similar back-compatibility reasons, if UART1 is the secure UART + * we create it second (and so it appears first in the DTB), because + * that's what QEMU has always done. + */ + if (!vms->secure) { + Chardev *serial1 = serial_hd(1); + + if (serial1) { + vms->second_ns_uart_present = true; + create_uart(vms, VIRT_UART1, sysmem, serial1, false); + } + } + create_uart(vms, VIRT_UART0, sysmem, serial_hd(0), false); + if (vms->secure) { + create_uart(vms, VIRT_UART1, secure_sysmem, serial_hd(1), true); + } if (vms->secure) { create_secure_ram(vms, secure_sysmem, secure_tag_sysmem); - create_uart(vms, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); } if (tag_sysmem) { @@ -2332,6 +2493,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); @@ -2615,7 +2833,7 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } static void virt_memory_plug(HotplugHandler *hotplug_dev, @@ -2635,64 +2853,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) { @@ -2700,10 +2860,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) { @@ -2730,9 +2891,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); } } @@ -2750,12 +2911,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)) { @@ -2771,24 +2931,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, @@ -2814,8 +2970,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))); @@ -2827,6 +2984,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))); @@ -2840,7 +2999,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); } @@ -2876,7 +3035,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 @@ -2886,10 +3045,69 @@ static int virt_kvm_type(MachineState *ms, const char *type_str) return fixed_ipa ? 0 : requested_pa_size; } +static int virt_hvf_get_physical_address_range(MachineState *ms) +{ + VirtMachineState *vms = VIRT_MACHINE(ms); + + int default_ipa_size = hvf_arm_get_default_ipa_bit_size(); + int max_ipa_size = hvf_arm_get_max_ipa_bit_size(); + + /* We freeze the memory map to compute the highest gpa */ + virt_set_memmap(vms, max_ipa_size); + + int requested_ipa_size = 64 - clz64(vms->highest_gpa); + + /* + * If we're <= the default IPA size just use the default. + * If we're above the default but below the maximum, round up to + * the maximum. hvf_arm_get_max_ipa_bit_size() conveniently only + * returns values that are valid ARM PARange values. + */ + if (requested_ipa_size <= default_ipa_size) { + requested_ipa_size = default_ipa_size; + } else if (requested_ipa_size <= max_ipa_size) { + requested_ipa_size = max_ipa_size; + } else { + error_report("-m and ,maxmem option values " + "require an IPA range (%d bits) larger than " + "the one supported by the host (%d bits)", + requested_ipa_size, max_ipa_size); + return -1; + } + + return requested_ipa_size; +} + 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. @@ -2911,9 +3129,15 @@ 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; + mc->hvf_get_physical_address_range = virt_hvf_get_physical_address_range; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; hc->pre_plug = virt_machine_device_pre_plug_cb; @@ -2924,7 +3148,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, @@ -2950,6 +3177,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", @@ -3034,6 +3290,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; @@ -3096,15 +3353,74 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_7_2_options(MachineClass *mc) +static void virt_machine_9_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(7, 2) +DEFINE_VIRT_MACHINE_AS_LATEST(9, 2) + +static void virt_machine_9_1_options(MachineClass *mc) +{ + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_9_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); + /* 9.1 and earlier have only a stage-1 SMMU, not a nested s1+2 one */ + vmc->no_nested_smmu = true; +} +DEFINE_VIRT_MACHINE(9, 1) + +static void virt_machine_9_0_options(MachineClass *mc) +{ + virt_machine_9_1_options(mc); + mc->smbios_memory_device_size = 16 * GiB; + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); +} +DEFINE_VIRT_MACHINE(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) diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c new file mode 100644 index 0000000000..33f0dd5982 --- /dev/null +++ b/hw/arm/xen-pvh.c @@ -0,0 +1,106 @@ +/* + * QEMU ARM Xen PVH Machine + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/qapi-commands-migration.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "hw/xen/xen-pvh-common.h" +#include "hw/xen/arch_hvm.h" + +#define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh") + +/* + * 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_arm_instance_init(Object *obj) +{ + XenPVHMachineState *s = XEN_PVH_MACHINE(obj); + + /* Default values. */ + s->cfg.ram_low = (MemMapEntry) { GUEST_RAM0_BASE, GUEST_RAM0_SIZE }; + s->cfg.ram_high = (MemMapEntry) { GUEST_RAM1_BASE, GUEST_RAM1_SIZE }; + + s->cfg.virtio_mmio_num = NR_VIRTIO_MMIO_DEVICES; + s->cfg.virtio_mmio_irq_base = GUEST_VIRTIO_MMIO_SPI_FIRST; + s->cfg.virtio_mmio = (MemMapEntry) { GUEST_VIRTIO_MMIO_BASE, + VIRTIO_MMIO_DEV_SIZE }; +} + +static void xen_pvh_set_pci_intx_irq(void *opaque, int intx_irq, int level) +{ + XenPVHMachineState *s = XEN_PVH_MACHINE(opaque); + int irq = s->cfg.pci_intx_irq_base + intx_irq; + + if (xendevicemodel_set_irq_level(xen_dmod, xen_domid, irq, level)) { + error_report("xendevicemodel_set_pci_intx_level failed"); + } +} + +static void xen_arm_machine_class_init(ObjectClass *oc, void *data) +{ + XenPVHMachineClass *xpc = XEN_PVH_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Xen PVH ARM machine"; + + /* + * mc->max_cpus holds the MAX value allowed in the -smp command-line opts. + * + * 1. If users don't pass any -smp option: + * ms->smp.cpus will default to 1. + * ms->smp.max_cpus will default to 1. + * + * 2. If users pass -smp X: + * ms->smp.cpus will be set to X. + * ms->smp.max_cpus will also be set to X. + * + * 3. If users pass -smp X,maxcpus=Y: + * ms->smp.cpus will be set to X. + * ms->smp.max_cpus will be set to Y. + * + * In scenarios 2 and 3, if X or Y are set to something larger than + * mc->max_cpus, QEMU will bail out with an error message. + */ + mc->max_cpus = GUEST_MAX_VCPUS; + + /* Xen/ARM does not use buffered IOREQs. */ + xpc->handle_bufioreq = HVM_IOREQSRV_BUFIOREQ_OFF; + + /* PCI INTX delivery. */ + xpc->set_pci_intx_irq = xen_pvh_set_pci_intx_irq; + + /* List of supported features known to work on PVH ARM. */ + xpc->has_pci = true; + xpc->has_tpm = true; + xpc->has_virtio_mmio = true; + + xen_pvh_class_setup_common_props(xpc); +} + +static const TypeInfo xen_arm_machine_type = { + .name = TYPE_XEN_ARM, + .parent = TYPE_XEN_PVH_MACHINE, + .class_init = xen_arm_machine_class_init, + .instance_size = sizeof(XenPVHMachineState), + .instance_init = xen_arm_instance_init, +}; + +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/xen-stubs.c b/hw/arm/xen-stubs.c new file mode 100644 index 0000000000..4ac6a56a96 --- /dev/null +++ b/hw/arm/xen-stubs.c @@ -0,0 +1,32 @@ +/* + * Stubs for unimplemented Xen functions for ARM. + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/qapi-commands-migration.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "hw/xen/xen-hvm-common.h" +#include "hw/xen/arch_hvm.h" + +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) +{ +} diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 3190cc0b8d..fde4d946b7 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" @@ -35,8 +34,12 @@ #include "hw/net/cadence_gem.h" #include "hw/cpu/a9mpcore.h" #include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" #include "sysemu/reset.h" #include "qom/object.h" +#include "exec/tswap.h" +#include "target/arm/cpu-qom.h" +#include "qapi/visitor.h" #define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9") OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) @@ -83,9 +86,13 @@ static const int dma_irqs[8] = { 0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \ 0xe5801000 + (addr) +#define ZYNQ_MAX_CPUS 2 + struct ZynqMachineState { MachineState parent; Clock *ps_clk; + ARMCPU *cpu[ZYNQ_MAX_CPUS]; + uint8_t boot_mode; }; static void zynq_write_board_setup(ARMCPU *cpu, @@ -108,16 +115,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 +168,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); @@ -174,16 +179,37 @@ static inline int zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq, return unit; } +static void zynq_set_boot_mode(Object *obj, const char *str, + Error **errp) +{ + ZynqMachineState *m = ZYNQ_MACHINE(obj); + uint8_t mode = 0; + + if (!strncasecmp(str, "qspi", 4)) { + mode = 1; + } else if (!strncasecmp(str, "sd", 2)) { + mode = 5; + } else if (!strncasecmp(str, "nor", 3)) { + mode = 2; + } else if (!strncasecmp(str, "jtag", 4)) { + mode = 0; + } else { + error_setg(errp, "%s boot mode not supported", str); + return; + } + m->boot_mode = mode; +} + static void zynq_init(MachineState *machine) { ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine); - ARMCPU *cpu; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); DeviceState *dev, *slcr; SysBusDevice *busdev; qemu_irq pic[64]; int n; + unsigned int smp_cpus = machine->smp.cpus; /* max 2GB ram */ if (machine->ram_size > 2 * GiB) { @@ -191,22 +217,19 @@ static void zynq_init(MachineState *machine) exit(EXIT_FAILURE); } - cpu = ARM_CPU(object_new(machine->cpu_type)); + for (n = 0; n < smp_cpus; n++) { + Object *cpuobj = object_new(machine->cpu_type); - /* By default A9 CPUs have EL3 enabled. This board does not - * currently support EL3 so the CPU EL3 property is disabled before - * realization. - */ - if (object_property_find(OBJECT(cpu), "has_el3")) { - object_property_set_bool(OBJECT(cpu), "has_el3", false, &error_fatal); + object_property_set_int(cpuobj, "midr", ZYNQ_BOARD_MIDR, + &error_fatal); + object_property_set_int(cpuobj, "reset-cbar", MPCORE_PERIPHBASE, + &error_fatal); + + qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); + + zynq_machine->cpu[n] = ARM_CPU(cpuobj); } - object_property_set_int(OBJECT(cpu), "midr", ZYNQ_BOARD_MIDR, - &error_fatal); - object_property_set_int(OBJECT(cpu), "reset-cbar", MPCORE_PERIPHBASE, - &error_fatal); - qdev_realize(DEVICE(cpu), NULL, &error_fatal); - /* DDR remapped to address zero. */ memory_region_add_subregion(address_space_mem, 0, machine->ram); @@ -234,16 +257,25 @@ static void zynq_init(MachineState *machine) /* Create slcr, keep a pointer to connect clocks */ slcr = qdev_new("xilinx-zynq_slcr"); qdev_connect_clock_in(slcr, "ps_clk", zynq_machine->ps_clk); + qdev_prop_set_uint8(slcr, "boot-mode", zynq_machine->boot_mode); sysbus_realize_and_unref(SYS_BUS_DEVICE(slcr), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(slcr), 0, 0xF8000000); dev = qdev_new(TYPE_A9MPCORE_PRIV); - qdev_prop_set_uint32(dev, "num-cpu", 1); + qdev_prop_set_uint32(dev, "num-cpu", smp_cpus); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); - sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ)); + zynq_binfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; + sysbus_create_varargs("l2x0", MPCORE_PERIPHBASE + 0x2000, NULL); + for (n = 0; n < smp_cpus; n++) { + /* See "hw/intc/arm_gic.h" for the IRQ line association */ + DeviceState *cpudev = DEVICE(zynq_machine->cpu[n]); + sysbus_connect_irq(busdev, n, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(busdev, smp_cpus + n, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } for (n = 0; n < 64; n++) { pic[n] = qdev_get_gpio_in(dev, n); @@ -278,8 +310,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; @@ -342,25 +374,105 @@ static void zynq_init(MachineState *machine) sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]); sysbus_mmio_map(busdev, 0, 0xF8007000); + /* + * Refer to the ug585-Zynq-7000-TRM manual B.3 (Module Summary) and + * the zynq-7000.dtsi. Add placeholders for unimplemented devices. + */ + create_unimplemented_device("zynq.i2c0", 0xE0004000, 4 * KiB); + create_unimplemented_device("zynq.i2c1", 0xE0005000, 4 * KiB); + create_unimplemented_device("zynq.can0", 0xE0008000, 4 * KiB); + create_unimplemented_device("zynq.can1", 0xE0009000, 4 * KiB); + create_unimplemented_device("zynq.gpio", 0xE000A000, 4 * KiB); + create_unimplemented_device("zynq.smcc", 0xE000E000, 4 * KiB); + + /* Direct Memory Access Controller, PL330, Non-Secure Mode */ + create_unimplemented_device("zynq.dma_ns", 0xF8004000, 4 * KiB); + + /* System Watchdog Timer Registers */ + create_unimplemented_device("zynq.swdt", 0xF8005000, 4 * KiB); + + /* DDR memory controller */ + create_unimplemented_device("zynq.ddrc", 0xF8006000, 4 * KiB); + + /* AXI_HP Interface (AFI) */ + create_unimplemented_device("zynq.axi_hp0", 0xF8008000, 0x28); + create_unimplemented_device("zynq.axi_hp1", 0xF8009000, 0x28); + create_unimplemented_device("zynq.axi_hp2", 0xF800A000, 0x28); + create_unimplemented_device("zynq.axi_hp3", 0xF800B000, 0x28); + + create_unimplemented_device("zynq.efuse", 0xF800d000, 0x20); + + /* Embedded Trace Buffer */ + create_unimplemented_device("zynq.etb", 0xF8801000, 4 * KiB); + + /* Cross Trigger Interface, ETB and TPIU */ + create_unimplemented_device("zynq.cti_etb_tpiu", 0xF8802000, 4 * KiB); + + /* Trace Port Interface Unit */ + create_unimplemented_device("zynq.tpiu", 0xF8803000, 4 * KiB); + + /* CoreSight Trace Funnel */ + create_unimplemented_device("zynq.funnel", 0xF8804000, 4 * KiB); + + /* Instrumentation Trace Macrocell */ + create_unimplemented_device("zynq.itm", 0xF8805000, 4 * KiB); + + /* Cross Trigger Interface, FTM */ + create_unimplemented_device("zynq.cti_ftm", 0xF8809000, 4 * KiB); + + /* Fabric Trace Macrocell */ + create_unimplemented_device("zynq.ftm", 0xF880B000, 4 * KiB); + + /* Cortex A9 Performance Monitoring Unit, CPU */ + create_unimplemented_device("cortex-a9.pmu0", 0xF8891000, 4 * KiB); + create_unimplemented_device("cortex-a9.pmu1", 0xF8893000, 4 * KiB); + + /* Cross Trigger Interface, CPU */ + create_unimplemented_device("zynq.cpu_cti0", 0xF8898000, 4 * KiB); + create_unimplemented_device("zynq.cpu_cti1", 0xF8899000, 4 * KiB); + + /* CoreSight PTM-A9, CPU */ + create_unimplemented_device("cortex-a9.ptm0", 0xF889c000, 4 * KiB); + create_unimplemented_device("cortex-a9.ptm1", 0xF889d000, 4 * KiB); + + /* AMBA NIC301 TrustZone */ + create_unimplemented_device("zynq.trustZone", 0xF8900000, 0x20); + + /* AMBA Network Interconnect Advanced Quality of Service (QoS-301) */ + create_unimplemented_device("zynq.qos301_cpu", 0xF8946000, 0x130); + create_unimplemented_device("zynq.qos301_dmac", 0xF8947000, 0x130); + create_unimplemented_device("zynq.qos301_iou", 0xF8948000, 0x130); + zynq_binfo.ram_size = machine->ram_size; zynq_binfo.board_id = 0xd32; zynq_binfo.loader_start = 0; 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(zynq_machine->cpu[0], 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); + ObjectProperty *prop; mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9"; mc->init = zynq_init; - mc->max_cpus = 1; + mc->max_cpus = ZYNQ_MAX_CPUS; 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"; + prop = object_class_property_add_str(oc, "boot-mode", NULL, + zynq_set_boot_mode); + object_class_property_set_description(oc, "boot-mode", + "Supported boot modes:" + " jtag qspi sd nor"); + object_property_set_default_str(prop, "qspi"); } static const TypeInfo zynq_machine_type = { diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 37fc9b919c..8b12d3e7cb 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_report("'%s' is either abstract or" + " not a subtype of m25p80", s->ospi_model); + exit(1); + } + } + + 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..3a1e2e29f1 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,21 +256,25 @@ 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; + OrIRQState *or_irq; object_initialize_child(OBJECT(s), name, &s->lpd.iou.gem[i], TYPE_CADENCE_GEM); + or_irq = &s->lpd.iou.gem_irq_orgate[i]; + object_initialize_child(OBJECT(s), "gem-irq-orgate[*]", + or_irq, TYPE_OR_IRQ); 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); + object_property_set_int(OBJECT(or_irq), + "num-lines", 2, &error_fatal); + qdev_realize(DEVICE(or_irq), NULL, &error_fatal); + qdev_connect_gpio_out(DEVICE(or_irq), 0, pic[irqs[i]]); + object_property_set_link(OBJECT(dev), "dma", OBJECT(&s->mr_ps), &error_abort); sysbus_realize(SYS_BUS_DEVICE(dev), &error_fatal); @@ -240,7 +282,8 @@ static void versal_create_gems(Versal *s, qemu_irq *pic) mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); memory_region_add_subregion(&s->mr_ps, addrs[i], mr); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[irqs[i]]); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(DEVICE(or_irq), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, qdev_get_gpio_in(DEVICE(or_irq), 1)); g_free(name); } } @@ -310,6 +353,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 +372,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 +385,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 +598,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 +929,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 +971,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 5905a33015..ab2d50e31b 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 @@ -392,6 +394,8 @@ static void xlnx_zynqmp_init(Object *obj) for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { object_initialize_child(obj, "gem[*]", &s->gem[i], TYPE_CADENCE_GEM); + object_initialize_child(obj, "gem-irq-orgate[*]", + &s->gem_irq_orgate[i], TYPE_OR_IRQ); } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { @@ -616,25 +620,26 @@ 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, &error_abort); object_property_set_int(OBJECT(&s->gem[i]), "num-priority-queues", 2, &error_abort); + object_property_set_int(OBJECT(&s->gem_irq_orgate[i]), + "num-lines", 2, &error_fatal); + qdev_realize(DEVICE(&s->gem_irq_orgate[i]), NULL, &error_fatal); + qdev_connect_gpio_out(DEVICE(&s->gem_irq_orgate[i]), 0, gic_spi[gem_intr[i]]); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gem[i]), errp)) { return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem[i]), 0, gem_addr[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem[i]), 0, - gic_spi[gem_intr[i]]); + qdev_get_gpio_in(DEVICE(&s->gem_irq_orgate[i]), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem[i]), 1, + qdev_get_gpio_in(DEVICE(&s->gem_irq_orgate[i]), 1)); } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { diff --git a/hw/arm/z2.c b/hw/arm/z2.c deleted file mode 100644 index 9c1e876207..0000000000 --- a/hw/arm/z2.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * PXA270-based Zipit Z2 device - * - * Copyright (c) 2011 by Vasily Khoruzhick - * - * Code is based on mainstone platform. - * - * This code is licensed under the GNU GPL v2. - * - * 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/arm/pxa.h" -#include "hw/arm/boot.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "hw/ssi/ssi.h" -#include "migration/vmstate.h" -#include "hw/boards.h" -#include "hw/block/flash.h" -#include "ui/console.h" -#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 - -static const struct keymap map[0x100] = { - [0 ... 0xff] = { -1, -1 }, - [0x3b] = {0, 0}, /* Option = F1 */ - [0xc8] = {0, 1}, /* Up */ - [0xd0] = {0, 2}, /* Down */ - [0xcb] = {0, 3}, /* Left */ - [0xcd] = {0, 4}, /* Right */ - [0xcf] = {0, 5}, /* End */ - [0x0d] = {0, 6}, /* KPPLUS */ - [0xc7] = {1, 0}, /* Home */ - [0x10] = {1, 1}, /* Q */ - [0x17] = {1, 2}, /* I */ - [0x22] = {1, 3}, /* G */ - [0x2d] = {1, 4}, /* X */ - [0x1c] = {1, 5}, /* Enter */ - [0x0c] = {1, 6}, /* KPMINUS */ - [0xc9] = {2, 0}, /* PageUp */ - [0x11] = {2, 1}, /* W */ - [0x18] = {2, 2}, /* O */ - [0x23] = {2, 3}, /* H */ - [0x2e] = {2, 4}, /* C */ - [0x38] = {2, 5}, /* LeftAlt */ - [0xd1] = {3, 0}, /* PageDown */ - [0x12] = {3, 1}, /* E */ - [0x19] = {3, 2}, /* P */ - [0x24] = {3, 3}, /* J */ - [0x2f] = {3, 4}, /* V */ - [0x2a] = {3, 5}, /* LeftShift */ - [0x01] = {4, 0}, /* Esc */ - [0x13] = {4, 1}, /* R */ - [0x1e] = {4, 2}, /* A */ - [0x25] = {4, 3}, /* K */ - [0x30] = {4, 4}, /* B */ - [0x1d] = {4, 5}, /* LeftCtrl */ - [0x0f] = {5, 0}, /* Tab */ - [0x14] = {5, 1}, /* T */ - [0x1f] = {5, 2}, /* S */ - [0x26] = {5, 3}, /* L */ - [0x31] = {5, 4}, /* N */ - [0x39] = {5, 5}, /* Space */ - [0x3c] = {6, 0}, /* Stop = F2 */ - [0x15] = {6, 1}, /* Y */ - [0x20] = {6, 2}, /* D */ - [0x0e] = {6, 3}, /* Backspace */ - [0x32] = {6, 4}, /* M */ - [0x33] = {6, 5}, /* Comma */ - [0x3d] = {7, 0}, /* Play = F3 */ - [0x16] = {7, 1}, /* U */ - [0x21] = {7, 2}, /* F */ - [0x2c] = {7, 3}, /* Z */ - [0x27] = {7, 4}, /* Semicolon */ - [0x34] = {7, 5}, /* Dot */ -}; - -#define Z2_RAM_SIZE 0x02000000 -#define Z2_FLASH_BASE 0x00000000 -#define Z2_FLASH_SIZE 0x00800000 - -static struct arm_boot_info z2_binfo = { - .loader_start = PXA2XX_SDRAM_BASE, - .ram_size = Z2_RAM_SIZE, -}; - -#define Z2_GPIO_SD_DETECT 96 -#define Z2_GPIO_AC_IN 0 -#define Z2_GPIO_KEY_ON 1 -#define Z2_GPIO_LCD_CS 88 - -struct ZipitLCD { - SSIPeripheral ssidev; - int32_t selected; - int32_t enabled; - uint8_t buf[3]; - uint32_t cur_reg; - int pos; -}; - -#define TYPE_ZIPIT_LCD "zipit-lcd" -OBJECT_DECLARE_SIMPLE_TYPE(ZipitLCD, ZIPIT_LCD) - -static uint32_t zipit_lcd_transfer(SSIPeripheral *dev, uint32_t value) -{ - ZipitLCD *z = ZIPIT_LCD(dev); - uint16_t val; - if (z->selected) { - z->buf[z->pos] = value & 0xff; - z->pos++; - } - 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__); - } else if (z->cur_reg == 0x10 && val == 0x0000) { - z->enabled = 0; - printf("%s: LCD disabled\n", __func__); - } - break; - default: - DPRINTF("%s: unknown command!\n", __func__); - break; - } - z->pos = 0; - } - return 0; -} - -static void z2_lcd_cs(void *opaque, int line, int level) -{ - ZipitLCD *z2_lcd = opaque; - z2_lcd->selected = !level; -} - -static void zipit_lcd_realize(SSIPeripheral *dev, Error **errp) -{ - ZipitLCD *z = ZIPIT_LCD(dev); - z->selected = 0; - z->enabled = 0; - z->pos = 0; -} - -static const VMStateDescription vmstate_zipit_lcd_state = { - .name = "zipit-lcd", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_SSI_PERIPHERAL(ssidev, ZipitLCD), - VMSTATE_INT32(selected, ZipitLCD), - VMSTATE_INT32(enabled, ZipitLCD), - VMSTATE_BUFFER(buf, ZipitLCD), - VMSTATE_UINT32(cur_reg, ZipitLCD), - VMSTATE_INT32(pos, ZipitLCD), - VMSTATE_END_OF_LIST(), - } -}; - -static void zipit_lcd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - - k->realize = zipit_lcd_realize; - k->transfer = zipit_lcd_transfer; - dc->vmsd = &vmstate_zipit_lcd_state; -} - -static const TypeInfo zipit_lcd_info = { - .name = TYPE_ZIPIT_LCD, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(ZipitLCD), - .class_init = zipit_lcd_class_init, -}; - -#define TYPE_AER915 "aer915" -OBJECT_DECLARE_SIMPLE_TYPE(AER915State, AER915) - -struct AER915State { - I2CSlave parent_obj; - - int len; - uint8_t buf[3]; -}; - -static int aer915_send(I2CSlave *i2c, uint8_t data) -{ - AER915State *s = AER915(i2c); - - s->buf[s->len] = data; - if (s->len++ > 2) { - DPRINTF("%s: message too long (%i bytes)\n", - __func__, s->len); - return 1; - } - - if (s->len == 2) { - DPRINTF("%s: reg %d value 0x%02x\n", __func__, - s->buf[0], s->buf[1]); - } - - return 0; -} - -static int aer915_event(I2CSlave *i2c, enum i2c_event event) -{ - AER915State *s = AER915(i2c); - - 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; - default: - break; - } - - return 0; -} - -static uint8_t aer915_recv(I2CSlave *slave) -{ - AER915State *s = AER915(slave); - int retval = 0x00; - - switch (s->buf[0]) { - /* Return hardcoded battery voltage, - * 0xf0 means ~4.1V - */ - case 0x02: - retval = 0xf0; - break; - /* Return 0x00 for other regs, - * we don't know what they are for, - * anyway they return 0x00 on real hardware. - */ - default: - break; - } - - return retval; -} - -static const VMStateDescription vmstate_aer915_state = { - .name = "aer915", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(len, AER915State), - VMSTATE_BUFFER(buf, AER915State), - VMSTATE_END_OF_LIST(), - } -}; - -static void aer915_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - k->event = aer915_event; - k->recv = aer915_recv; - k->send = aer915_send; - dc->vmsd = &vmstate_aer915_state; -} - -static const TypeInfo aer915_info = { - .name = TYPE_AER915, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(AER915State), - .class_init = aer915_class_init, -}; - -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; - - /* Setup CPU & memory */ - mpu = pxa270_init(address_space_mem, 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); - } - - /* setup keypad */ - pxa27x_register_keypad(mpu->kp, map, 0x100); - - /* MMC/SD host */ - pxa2xx_mmci_handlers(mpu->mmc, - NULL, - qdev_get_gpio_in(mpu->gpio, Z2_GPIO_SD_DETECT)); - - type_register_static(&zipit_lcd_info); - 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)); - mpu->i2s->opaque = wm; - mpu->i2s->codec_out = wm8750_dac_dat; - mpu->i2s->codec_in = wm8750_adc_dat; - wm8750_data_req_set(wm, mpu->i2s->data_req, mpu->i2s); - - qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS, - qemu_allocate_irq(z2_lcd_cs, z2_lcd, 0)); - - z2_binfo.board_id = 0x6dd; - arm_load_kernel(mpu->cpu, machine, &z2_binfo); -} - -static void z2_machine_init(MachineClass *mc) -{ - mc->desc = "Zipit Z2 (PXA27x)"; - mc->init = z2_init; - mc->ignore_memory_transaction_failures = true; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5"); -} - -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 358cf70379..8c0cd38b10 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -20,7 +20,7 @@ #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" @@ -28,43 +28,7 @@ #include "qom/object.h" #include "ac97_int.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 */ @@ -126,11 +90,6 @@ enum { #define BD_IOC (1 << 31) /* Interrupt on Completion */ #define BD_BUP (1 << 30) /* Buffer Underrun Policy */ -#define EACS_VRA 1 -#define EACS_VRM 8 - -#define MUTE_SHIFT 15 - #define REC_MASK 7 enum { REC_MIC = 0, @@ -1141,7 +1100,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), @@ -1189,7 +1148,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, AC97DeviceState), VMSTATE_UINT32(state.glob_cnt, AC97DeviceState), VMSTATE_UINT32(state.glob_sta, AC97DeviceState), @@ -1326,7 +1285,7 @@ void ac97_common_init(AC97LinkState *s, PCIDevice *pci_dev, AddressSpace *as) s->pci_dev = pci_dev; s->as = as; - AUD_register_card("ac97", &s->card); + AUD_register_card("ac97", &s->card, &error_fatal); ac97_on_reset(s); } @@ -1405,7 +1364,7 @@ static void ac97_class_init(ObjectClass *klass, void *data) dc->desc = "Intel 82801AA AC97 Audio"; dc->vmsd = &vmstate_ac97; device_class_set_props(dc, ac97_properties); - dc->reset = ac97_on_device_reset; + device_class_set_legacy_reset(dc, ac97_on_device_reset); } static const TypeInfo ac97_info = { 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..805416372c --- /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, ResetType type) +{ + 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..8321f89c88 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() @@ -164,7 +164,7 @@ static void cs4231_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = cs_reset; + device_class_set_legacy_reset(dc, cs_reset); dc->vmsd = &vmstate_cs4231; } diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 7f17a72a9c..2d69372087 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -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[] = { @@ -699,7 +702,7 @@ static void cs4231a_class_initfn (ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS (klass); dc->realize = cs4231a_realizefn; - dc->reset = cs4231a_reset; + device_class_set_legacy_reset(dc, cs4231a_reset); set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "Crystal Semiconductor CS4231A"; dc->vmsd = &vmstate_cs4231a; diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 6904589814..9a508e7b81 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); } @@ -905,7 +888,7 @@ static void es1370_class_init (ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_SOUND, dc->categories); dc->desc = "ENSONIQ AudioPCI ES1370"; dc->vmsd = &vmstate_es1370; - dc->reset = es1370_on_reset; + device_class_set_legacy_reset(dc, es1370_on_reset); device_class_set_props(dc, es1370_properties); } 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..c340a9481d 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) { @@ -475,8 +487,7 @@ static void hda_audio_setup(HDAAudioStream *st) if (st->output) { if (use_timer) { cb = hda_audio_output_cb; - st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, - hda_audio_output_timer, st); + timer_del(st->buft); } else { cb = hda_audio_compat_output_cb; } @@ -485,8 +496,7 @@ static void hda_audio_setup(HDAAudioStream *st) } else { if (use_timer) { cb = hda_audio_input_cb; - st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, - hda_audio_input_timer, st); + timer_del(st->buft); } else { cb = hda_audio_compat_input_cb; } @@ -676,7 +686,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 +696,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); @@ -709,8 +724,12 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) st->gain_right = QEMU_HDA_AMP_STEPS; st->compat_bpos = sizeof(st->compat_buf); st->output = true; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_output_timer, st); } else { st->output = false; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_input_timer, st); } st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | (1 << AC_FMT_CHAN_SHIFT); @@ -719,7 +738,6 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) break; } } - return 0; } static void hda_audio_exit(HDACodecDevice *hda) @@ -734,9 +752,7 @@ static void hda_audio_exit(HDACodecDevice *hda) if (st->node == NULL) { continue; } - if (a->use_timer) { - timer_del(st->buft); - } + timer_free(st->buft); if (st->output) { AUD_close_out(&a->card, st->voice.out); } else { @@ -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) @@ -891,7 +910,7 @@ static void hda_audio_base_class_init(ObjectClass *klass, void *data) k->command = hda_audio_command; k->stream = hda_audio_stream; set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->reset = hda_audio_reset; + device_class_set_legacy_reset(dc, hda_audio_reset); dc->vmsd = &vmstate_hda_audio; device_class_set_props(dc, hda_audio_properties); } 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 b9ed231fe8..6918e23c5d 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) @@ -1160,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), @@ -1176,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 */ @@ -1233,7 +1231,7 @@ static void intel_hda_class_init(ObjectClass *klass, void *data) k->exit = intel_hda_exit; k->vendor_id = PCI_VENDOR_ID_INTEL; k->class_id = PCI_CLASS_MULTIMEDIA_HD_AUDIO; - dc->reset = intel_hda_reset; + device_class_set_legacy_reset(dc, intel_hda_reset); dc->vmsd = &vmstate_intel_hda; device_class_set_props(dc, intel_hda_properties); } 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..28f9af320d 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), @@ -292,7 +292,7 @@ static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mv88w8618_audio_realize; - dc->reset = mv88w8618_audio_reset; + device_class_set_legacy_reset(dc, mv88w8618_audio_reset); dc->vmsd = &mv88w8618_audio_vmsd; dc->user_creatable = false; } 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..eb96dc2898 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), @@ -639,7 +639,7 @@ static void pl041_device_class_init(ObjectClass *klass, void *data) dc->realize = pl041_realize; set_bit(DEVICE_CATEGORY_SOUND, dc->categories); - dc->reset = pl041_device_reset; + device_class_set_legacy_reset(dc, pl041_device_reset); dc->vmsd = &vmstate_pl041; device_class_set_props(dc, pl041_device_properties); } 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..d18fd9fa05 100644 --- a/hw/audio/soundhw.c +++ b/hw/audio/soundhw.c @@ -83,16 +83,17 @@ 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; if (selected) { - error_setg(&error_fatal, "only one -soundhw option is allowed"); + error_report("only one -soundhw option is allowed"); + exit(1); } 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 +101,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..b8ef572767 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -6,8 +6,54 @@ 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_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..85243e6313 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"; + device_class_set_legacy_reset(dc, 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..c5581d7b3d --- /dev/null +++ b/hw/audio/virtio-snd.c @@ -0,0 +1,1393 @@ +/* + * 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 "qemu/lockable.h" +#include "sysemu/runstate.h" +#include "trace.h" +#include "qapi/error.h" +#include "hw/audio/virtio-snd.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_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 (params->format >= sizeof(supported_formats) * BITS_PER_BYTE || + !(supported_formats & BIT(params->format))) { + error_report("Stream format is not supported."); + return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP); + } + if (params->rate >= sizeof(supported_rates) * BITS_PER_BYTE || + !(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 = 0; /* Conforming to VIRTIO 1.0: always little endian. */ +} + +/* + * 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, max_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; + } + + max_size = iov_size(buffer->elem->in_sg, buffer->elem->in_num); + for (;;) { + if (buffer->size >= max_size) { + return_rx_buffer(stream, buffer); + break; + } + 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->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/Kconfig b/hw/avr/Kconfig index d31298c3cc..b29937be41 100644 --- a/hw/avr/Kconfig +++ b/hw/avr/Kconfig @@ -5,5 +5,8 @@ config AVR_ATMEGA_MCU select AVR_POWER config ARDUINO + bool + default y + depends on AVR select AVR_ATMEGA_MCU select UNIMP 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/Kconfig b/hw/block/Kconfig index 9e8f28f982..a898e04f03 100644 --- a/hw/block/Kconfig +++ b/hw/block/Kconfig @@ -22,15 +22,6 @@ config PFLASH_CFI01 config PFLASH_CFI02 bool -config ECC - bool - -config ONENAND - bool - -config TC58128 - bool - config VIRTIO_BLK bool default y diff --git a/hw/block/block.c b/hw/block/block.c index ff85ec0757..f3e61f5724 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "block/block_int-common.h" #include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "hw/block/block.h" @@ -22,35 +23,69 @@ void blkconf_locked(BlockConf *conf, bool *locked) *locked = dinfo->locked; } +/* + * 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; } @@ -61,9 +96,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, size, buf, 0); + 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; 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 26f965cabc..0000000000 --- a/hw/block/dataplane/virtio-blk.c +++ /dev/null @@ -1,362 +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(); - - /* - * These fields are visible to the IOThread so we rely on implicit barriers - * in aio_context_acquire() on the write side and aio_notify_accept() on - * the read side. - */ - 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 deleted file mode 100644 index 6e0d63842c..0000000000 --- a/hw/block/ecc.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Calculate Error-correcting Codes. Used by NAND Flash controllers - * (not by NAND chips). - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * 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/vmstate.h" -#include "hw/block/flash.h" - -/* - * Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux. - */ -static const uint8_t nand_ecc_precalc_table[] = { - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, - 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, - 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, - 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, - 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, - 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, - 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, - 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, - 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, -}; - -/* Update ECC parity count. */ -uint8_t ecc_digest(ECCState *s, uint8_t sample) -{ - uint8_t idx = nand_ecc_precalc_table[sample]; - - s->cp ^= idx & 0x3f; - if (idx & 0x40) { - s->lp[0] ^= ~s->count; - s->lp[1] ^= s->count; - } - s->count ++; - - return sample; -} - -/* Reinitialise the counters. */ -void ecc_reset(ECCState *s) -{ - s->lp[0] = 0x0000; - s->lp[1] = 0x0000; - s->cp = 0x00; - s->count = 0; -} - -/* Save/restore */ -const VMStateDescription vmstate_ecc_state = { - .name = "ecc-state", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(cp, ECCState), - VMSTATE_UINT16_ARRAY(lp, ECCState, 2), - VMSTATE_UINT16(count, ECCState), - VMSTATE_END_OF_LIST(), - }, -}; 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 fee1ca68a8..5ed3c18c28 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -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; @@ -144,6 +147,8 @@ static void isa_fdc_get_drive_max_chs(FloppyDriveType type, uint8_t *maxc, *maxs = fdf->last_sect; } } + /* fd_formats must contain at least one entry per FloppyDriveType */ + assert(*maxc); (*maxc)--; } @@ -189,6 +194,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; @@ -258,7 +277,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() } @@ -288,7 +307,7 @@ static void isabus_fdc_class_init(ObjectClass *klass, void *data) dc->desc = "virtual floppy controller"; dc->realize = isabus_fdc_realize; dc->fw_name = "fdc"; - dc->reset = fdctrl_external_reset_isa; + device_class_set_legacy_reset(dc, fdctrl_external_reset_isa); dc->vmsd = &vmstate_isa_fdc; adevc->build_dev_aml = build_fdc_aml; device_class_set_props(dc, isa_fdc_properties); diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index 86ea51d003..e1ddbf3d1a 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) @@ -146,11 +148,11 @@ static void sysbus_fdc_common_instance_init(Object *obj) 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); @@ -168,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() } @@ -179,7 +181,7 @@ static void sysbus_fdc_common_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = sysbus_fdc_realize; - dc->reset = fdctrl_external_reset_sysbus; + device_class_set_legacy_reset(dc, fdctrl_external_reset_sysbus); dc->vmsd = &vmstate_sysbus_fdc; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 64ae4a6899..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, diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index dae13ab14d..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) @@ -66,7 +66,7 @@ static int guess_disk_lchs(BlockBackend *blk, 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; } diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 02adc87527..e2e84f8b5f 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" @@ -59,7 +61,8 @@ typedef struct FlashPartInfo { */ uint8_t id[SPI_NOR_MAX_ID_LEN]; uint8_t id_len; - /* there is confusion between manufacturers as to what a sector is. In this + /* + * there is confusion between manufacturers as to what a sector is. In this * device model, a "sector" is the size that is erased by the ERASE_SECTOR * command (opcode 0xd8). */ @@ -166,7 +169,7 @@ typedef struct FlashPartInfo { /* * Spansion read mode command length in bytes, * the mode is currently not supported. -*/ + */ #define SPANSION_CONTINUOUS_READ_MODE_CMD_LEN 1 #define WINBOND_CONTINUOUS_READ_MODE_CMD_LEN 1 @@ -187,7 +190,8 @@ static const FlashPartInfo known_devices[] = { { INFO("at45db081d", 0x1f2500, 0, 64 << 10, 16, ER_4K) }, - /* Atmel EEPROMS - it is assumed, that don't care bit in command + /* + * Atmel EEPROMS - it is assumed, that don't care bit in command * is set to 0. Block protection is not supported. */ { INFO("at25128a-nonjedec", 0x0, 0, 1, 131072, EEPROM) }, @@ -221,7 +225,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) }, @@ -263,15 +268,22 @@ static const FlashPartInfo known_devices[] = { { 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) }, + ER_4K | ER_32K, 2), + .sfdp_read = m25p80_sfdp_mt35xu01g }, + { 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) }, { INFO_STACKED("mt25qu01g", 0x20bb21, 0x1040, 64 << 10, 2048, ER_4K, 2) }, - { INFO_STACKED("mt25ql02g", 0x20ba22, 0x1040, 64 << 10, 4096, ER_4K | ER_32K, 2) }, - { INFO_STACKED("mt25qu02g", 0x20bb22, 0x1040, 64 << 10, 4096, ER_4K | ER_32K, 2) }, + { INFO_STACKED("mt25ql02g", 0x20ba22, 0x1040, 64 << 10, 4096, + ER_4K | ER_32K, 2) }, + { INFO_STACKED("mt25qu02g", 0x20bb22, 0x1040, 64 << 10, 4096, + ER_4K | ER_32K, 2) }, - /* Spansion -- single (large) sector size only, at least + /* + * Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ { INFO("s25sl032p", 0x010215, 0x4d00, 64 << 10, 64, ER_4K) }, @@ -344,13 +356,17 @@ static const FlashPartInfo known_devices[] = { { INFO("w25x64", 0xef3017, 0, 64 << 10, 128, ER_4K) }, { 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("w25q80bl", 0xef4014, 0, 64 << 10, 16, ER_4K), + .sfdp_read = m25p80_sfdp_w25q80bl }, { 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 }, + + /* Microchip */ + { INFO("25csm04", 0x29cc00, 0x100, 64 << 10, 8, 0) }, }; typedef enum { @@ -410,10 +426,16 @@ typedef enum { /* * Micron: 0x35 - enable QPI * Spansion: 0x35 - read control register + * Winbond: 0x35 - quad enable */ RDCR_EQIO = 0x35, RSTQIO = 0xf5, + /* + * Winbond: 0x31 - write status register 2 + */ + WRSR2 = 0x31, + RNVCR = 0xB5, WNVCR = 0xB1, @@ -509,7 +531,6 @@ struct M25P80Class { FlashPartInfo *pi; }; -#define TYPE_M25P80 "m25p80-generic" OBJECT_DECLARE_TYPE(Flash, M25P80Class, M25P80) static inline Manufacturer get_man(Flash *s) @@ -539,7 +560,8 @@ static void blk_sync_complete(void *opaque, int ret) qemu_iovec_destroy(iov); g_free(iov); - /* do nothing. Masters do not directly interact with the backing store, + /* + * do nothing. Masters do not directly interact with the backing store, * only the working copy so no mutexing required. */ } @@ -793,6 +815,11 @@ static void complete_collecting_data(Flash *s) s->four_bytes_address_mode = extract32(s->data[1], 5, 1); } break; + case MAN_WINBOND: + if (s->len > 1) { + s->quad_enable = !!(s->data[1] & 0x02); + } + break; default: break; } @@ -800,6 +827,15 @@ static void complete_collecting_data(Flash *s) s->write_enable = false; } break; + case WRSR2: + switch (get_man(s)) { + case MAN_WINBOND: + s->quad_enable = !!(s->data[0] & 0x02); + break; + default: + break; + } + break; case BRWR: case EXTEND_ADDR_WRITE: s->ear = s->data[0]; @@ -1249,13 +1285,41 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->needed_bytes = 2; s->state = STATE_COLLECTING_VAR_LEN_DATA; break; + case MAN_WINBOND: + 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 WRSR2: + /* + * 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 2 write is disabled!\n"); + break; + } + switch (get_man(s)) { + case MAN_WINBOND: + s->needed_bytes = 1; + s->state = STATE_COLLECTING_DATA; + s->pos = 0; + break; + default: + break; + } + break; case WRDI: s->write_enable = false; if (get_man(s) == MAN_SST) { @@ -1426,6 +1490,12 @@ static void decode_new_cmd(Flash *s, uint32_t value) case MAN_MACRONIX: s->quad_enable = true; break; + case MAN_WINBOND: + s->data[0] = (!!s->quad_enable) << 1; + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + break; default: break; } @@ -1614,8 +1684,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->size, s->storage, 0) < 0) { - 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 { @@ -1682,7 +1752,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() } @@ -1700,7 +1770,7 @@ 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() } @@ -1718,7 +1788,7 @@ static const VMStateDescription vmstate_m25p80_write_protect = { .version_id = 1, .minimum_version_id = 1, .needed = m25p80_wp_level_srwd_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(wp_level, Flash), VMSTATE_BOOL(status_register_write_disabled, Flash), VMSTATE_END_OF_LIST() @@ -1741,7 +1811,7 @@ static const VMStateDescription vmstate_m25p80_block_protect = { .version_id = 1, .minimum_version_id = 1, .needed = m25p80_block_protect_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_BOOL(block_protect0, Flash), VMSTATE_BOOL(block_protect1, Flash), VMSTATE_BOOL(block_protect2, Flash), @@ -1757,7 +1827,7 @@ static const VMStateDescription vmstate_m25p80 = { .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), @@ -1779,7 +1849,7 @@ 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, @@ -1800,7 +1870,7 @@ static void m25p80_class_init(ObjectClass *klass, void *data) k->cs_polarity = SSI_CS_LOW; dc->vmsd = &vmstate_m25p80; device_class_set_props(dc, m25p80_properties); - dc->reset = m25p80_reset; + device_class_set_legacy_reset(dc, m25p80_reset); mc->pi = data; } @@ -1818,7 +1888,7 @@ static void m25p80_register_types(void) type_register_static(&m25p80_info); for (i = 0; i < ARRAY_SIZE(known_devices); ++i) { - TypeInfo ti = { + const TypeInfo ti = { .name = known_devices[i].part_name, .parent = TYPE_M25P80, .class_init = m25p80_class_init, @@ -1829,3 +1899,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 index 77615fa29e..a03a291a09 100644 --- a/hw/block/m25p80_sfdp.c +++ b/hw/block/m25p80_sfdp.c @@ -57,6 +57,79 @@ static const uint8_t sfdp_n25q256a[] = { }; define_sfdp_read(n25q256a); +static const uint8_t sfdp_mt35xu01g[] = { + 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, 0x3f, + 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(mt35xu01g); + +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 @@ -330,3 +403,79 @@ static const uint8_t sfdp_w25q01jvq[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; define_sfdp_read(w25q01jvq); + +static const uint8_t sfdp_w25q80bl[] = { + 0x53, 0x46, 0x44, 0x50, 0x05, 0x01, 0x00, 0xff, + 0x00, 0x05, 0x01, 0x10, 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, 0xf1, 0xff, 0xff, 0xff, 0x7f, 0x00, + 0x44, 0xeb, 0x08, 0x6b, 0x08, 0x3b, 0x42, 0xbb, + 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x0c, 0x20, 0x0f, 0x52, + 0x10, 0xd8, 0x00, 0x00, 0x23, 0x02, 0xa6, 0x00, + 0x81, 0x6c, 0x14, 0xa7, 0xed, 0x61, 0x76, 0x33, + 0x7a, 0x75, 0x7a, 0x75, 0xf7, 0xa2, 0xd5, 0x5c, + 0x00, 0xf7, 0x1d, 0xff, 0xe9, 0x30, 0xc0, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 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(w25q80bl); + +/* + * 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 index df7adfb5ce..35785686a0 100644 --- a/hw/block/m25p80_sfdp.h +++ b/hw/block/m25p80_sfdp.h @@ -16,6 +16,8 @@ #define M25P80_SFDP_MAX_SIZE (1 << 24) uint8_t m25p80_sfdp_n25q256a(uint32_t addr); +uint8_t m25p80_sfdp_mt35xu01g(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); @@ -23,7 +25,9 @@ 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_w25q80bl(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 b434d5654c..16a51bf8e2 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -1,21 +1,18 @@ -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_SSI_M25P80', if_true: files('m25p80_sfdp.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_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_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')) 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')) diff --git a/hw/block/nand.c b/hw/block/nand.c index 1aee1cb2b1..ac0a5d2b42 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), @@ -437,7 +457,7 @@ static void nand_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = nand_realize; - dc->reset = nand_reset; + device_class_set_legacy_reset(dc, nand_reset); dc->vmsd = &vmstate_nand; device_class_set_props(dc, nand_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -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) { @@ -763,11 +778,15 @@ static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) } } -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) { @@ -795,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) @@ -812,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 deleted file mode 100644 index 1fde975024..0000000000 --- a/hw/block/onenand.c +++ /dev/null @@ -1,872 +0,0 @@ -/* - * OneNAND flash memories emulation. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/hw.h" -#include "hw/block/flash.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "sysemu/block-backend.h" -#include "exec/memory.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qemu/error-report.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qom/object.h" - -/* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ -#define PAGE_SHIFT 11 - -/* Fixed */ -#define BLOCK_SHIFT (PAGE_SHIFT + 6) - -#define TYPE_ONE_NAND "onenand" -OBJECT_DECLARE_SIMPLE_TYPE(OneNANDState, ONE_NAND) - -struct OneNANDState { - SysBusDevice parent_obj; - - struct { - uint16_t man; - uint16_t dev; - uint16_t ver; - } id; - int shift; - hwaddr base; - qemu_irq intr; - qemu_irq rdy; - BlockBackend *blk; - BlockBackend *blk_cur; - uint8_t *image; - uint8_t *otp; - uint8_t *current; - MemoryRegion ram; - MemoryRegion mapped_ram; - uint8_t current_direction; - uint8_t *boot[2]; - uint8_t *data[2][2]; - MemoryRegion iomem; - MemoryRegion container; - int cycle; - int otpmode; - - uint16_t addr[8]; - uint16_t unladdr[8]; - int bufaddr; - int count; - uint16_t command; - uint16_t config[2]; - uint16_t status; - uint16_t intstatus; - uint16_t wpstatus; - - ECCState ecc; - - int density_mask; - int secs; - int secs_cur; - int blocks; - uint8_t *blockwp; -}; - -enum { - ONEN_BUF_BLOCK = 0, - ONEN_BUF_BLOCK2 = 1, - ONEN_BUF_DEST_BLOCK = 2, - ONEN_BUF_DEST_PAGE = 3, - ONEN_BUF_PAGE = 7, -}; - -enum { - ONEN_ERR_CMD = 1 << 10, - ONEN_ERR_ERASE = 1 << 11, - ONEN_ERR_PROG = 1 << 12, - ONEN_ERR_LOAD = 1 << 13, -}; - -enum { - ONEN_INT_RESET = 1 << 4, - ONEN_INT_ERASE = 1 << 5, - ONEN_INT_PROG = 1 << 6, - ONEN_INT_LOAD = 1 << 7, - ONEN_INT = 1 << 15, -}; - -enum { - ONEN_LOCK_LOCKTIGHTEN = 1 << 0, - ONEN_LOCK_LOCKED = 1 << 1, - ONEN_LOCK_UNLOCKED = 1 << 2, -}; - -static void onenand_mem_setup(OneNANDState *s) -{ - /* XXX: We should use IO_MEM_ROMD but we broke it earlier... - * Both 0x0000 ... 0x01ff and 0x8000 ... 0x800f can be used to - * write boot commands. Also take note of the BWPS bit. */ - memory_region_init(&s->container, OBJECT(s), "onenand", - 0x10000 << s->shift); - memory_region_add_subregion(&s->container, 0, &s->iomem); - memory_region_init_alias(&s->mapped_ram, OBJECT(s), "onenand-mapped-ram", - &s->ram, 0x0200 << s->shift, - 0xbe00 << s->shift); - memory_region_add_subregion_overlap(&s->container, - 0x0200 << s->shift, - &s->mapped_ram, - 1); -} - -static void onenand_intr_update(OneNANDState *s) -{ - qemu_set_irq(s->intr, ((s->intstatus >> 15) ^ (~s->config[0] >> 6)) & 1); -} - -static int onenand_pre_save(void *opaque) -{ - OneNANDState *s = opaque; - if (s->current == s->otp) { - s->current_direction = 1; - } else if (s->current == s->image) { - s->current_direction = 2; - } else { - s->current_direction = 0; - } - - return 0; -} - -static int onenand_post_load(void *opaque, int version_id) -{ - OneNANDState *s = opaque; - switch (s->current_direction) { - case 0: - break; - case 1: - s->current = s->otp; - break; - case 2: - s->current = s->image; - break; - default: - return -1; - } - onenand_intr_update(s); - return 0; -} - -static const VMStateDescription vmstate_onenand = { - .name = "onenand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = onenand_pre_save, - .post_load = onenand_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT8(current_direction, OneNANDState), - VMSTATE_INT32(cycle, OneNANDState), - VMSTATE_INT32(otpmode, OneNANDState), - VMSTATE_UINT16_ARRAY(addr, OneNANDState, 8), - VMSTATE_UINT16_ARRAY(unladdr, OneNANDState, 8), - VMSTATE_INT32(bufaddr, OneNANDState), - VMSTATE_INT32(count, OneNANDState), - VMSTATE_UINT16(command, OneNANDState), - VMSTATE_UINT16_ARRAY(config, OneNANDState, 2), - VMSTATE_UINT16(status, OneNANDState), - VMSTATE_UINT16(intstatus, OneNANDState), - VMSTATE_UINT16(wpstatus, OneNANDState), - VMSTATE_INT32(secs_cur, OneNANDState), - VMSTATE_PARTIAL_VBUFFER(blockwp, OneNANDState, blocks), - VMSTATE_UINT8(ecc.cp, OneNANDState), - VMSTATE_UINT16_ARRAY(ecc.lp, OneNANDState, 2), - VMSTATE_UINT16(ecc.count, OneNANDState), - VMSTATE_BUFFER_POINTER_UNSAFE(otp, OneNANDState, 0, - ((64 + 2) << PAGE_SHIFT)), - VMSTATE_END_OF_LIST() - } -}; - -/* Hot reset (Reset OneNAND command) or warm reset (RP pin low) */ -static void onenand_reset(OneNANDState *s, int cold) -{ - memset(&s->addr, 0, sizeof(s->addr)); - s->command = 0; - s->count = 1; - s->bufaddr = 0; - s->config[0] = 0x40c0; - s->config[1] = 0x0000; - onenand_intr_update(s); - qemu_irq_raise(s->rdy); - s->status = 0x0000; - s->intstatus = cold ? 0x8080 : 0x8010; - s->unladdr[0] = 0; - s->unladdr[1] = 0; - s->wpstatus = 0x0002; - s->cycle = 0; - s->otpmode = 0; - s->blk_cur = s->blk; - s->current = s->image; - s->secs_cur = s->secs; - - if (cold) { - /* Lock the whole flash */ - memset(s->blockwp, ONEN_LOCK_LOCKED, s->blocks); - - 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__); - } - } -} - -static void onenand_system_reset(DeviceState *dev) -{ - OneNANDState *s = ONE_NAND(dev); - - onenand_reset(s, 1); -} - -static inline int onenand_load_main(OneNANDState *s, int sec, int secn, - void *dest) -{ - 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, - secn << BDRV_SECTOR_BITS, dest, 0) < 0; - } else if (sec + secn > s->secs_cur) { - return 1; - } - - memcpy(dest, s->current + (sec << 9), secn << 9); - - return 0; -} - -static inline int onenand_prog_main(OneNANDState *s, int sec, int secn, - void *src) -{ - int result = 0; - - if (secn > 0) { - uint32_t size = secn << BDRV_SECTOR_BITS; - uint32_t offset = sec << BDRV_SECTOR_BITS; - assert(UINT32_MAX >> BDRV_SECTOR_BITS > sec); - assert(UINT32_MAX >> BDRV_SECTOR_BITS > secn); - const uint8_t *sp = (const uint8_t *)src; - uint8_t *dp = 0; - if (s->blk_cur) { - dp = g_malloc(size); - if (!dp || blk_pread(s->blk_cur, offset, size, dp, 0) < 0) { - result = 1; - } - } else { - if (sec + secn > s->secs_cur) { - result = 1; - } else { - dp = (uint8_t *)s->current + offset; - } - } - if (!result) { - uint32_t i; - for (i = 0; i < size; i++) { - dp[i] &= sp[i]; - } - if (s->blk_cur) { - result = blk_pwrite(s->blk_cur, offset, size, dp, 0) < 0; - } - } - if (dp && s->blk_cur) { - g_free(dp); - } - } - - return result; -} - -static inline int onenand_load_spare(OneNANDState *s, int sec, int secn, - void *dest) -{ - uint8_t buf[512]; - - if (s->blk_cur) { - uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS; - if (blk_pread(s->blk_cur, offset, BDRV_SECTOR_SIZE, buf, 0) < 0) { - return 1; - } - memcpy(dest, buf + ((sec & 31) << 4), secn << 4); - } else if (sec + secn > s->secs_cur) { - return 1; - } else { - memcpy(dest, s->current + (s->secs_cur << 9) + (sec << 4), secn << 4); - } - - return 0; -} - -static inline int onenand_prog_spare(OneNANDState *s, int sec, int secn, - void *src) -{ - int result = 0; - if (secn > 0) { - const uint8_t *sp = (const uint8_t *)src; - uint8_t *dp = 0, *dpp = 0; - uint32_t offset = (s->secs_cur + (sec >> 5)) << BDRV_SECTOR_BITS; - assert(UINT32_MAX >> BDRV_SECTOR_BITS > s->secs_cur + (sec >> 5)); - if (s->blk_cur) { - dp = g_malloc(512); - if (!dp - || blk_pread(s->blk_cur, offset, BDRV_SECTOR_SIZE, dp, 0) < 0) { - result = 1; - } else { - dpp = dp + ((sec & 31) << 4); - } - } else { - if (sec + secn > s->secs_cur) { - result = 1; - } else { - dpp = s->current + (s->secs_cur << 9) + (sec << 4); - } - } - if (!result) { - uint32_t i; - for (i = 0; i < (secn << 4); i++) { - dpp[i] &= sp[i]; - } - if (s->blk_cur) { - result = blk_pwrite(s->blk_cur, offset, BDRV_SECTOR_SIZE, dp, - 0) < 0; - } - } - g_free(dp); - } - return result; -} - -static inline int onenand_erase(OneNANDState *s, int sec, int num) -{ - uint8_t *blankbuf, *tmpbuf; - - blankbuf = g_malloc(512); - tmpbuf = g_malloc(512); - memset(blankbuf, 0xff, 512); - 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, - BDRV_SECTOR_SIZE, blankbuf, 0) < 0) { - goto fail; - } - 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, - BDRV_SECTOR_SIZE, tmpbuf, 0) < 0) { - goto fail; - } - } else { - if (sec + 1 > s->secs_cur) { - goto fail; - } - memcpy(s->current + (sec << 9), blankbuf, 512); - memcpy(s->current + (s->secs_cur << 9) + (sec << 4), - blankbuf, 1 << 4); - } - } - - g_free(tmpbuf); - g_free(blankbuf); - return 0; - -fail: - g_free(tmpbuf); - g_free(blankbuf); - return 1; -} - -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]; \ - buf += (s->bufaddr & 3) << 9; -#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 */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_M() - if (onenand_load_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; - -#if 0 - SETBUF_S() - if (onenand_load_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; -#endif - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_LOAD; - break; - case 0x13: /* Load single/multiple spare sector into buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_S() - if (onenand_load_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_LOAD; - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_LOAD; - break; - case 0x80: /* Program single/multiple sector data unit from buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_M() - if (onenand_prog_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - -#if 0 - SETBUF_S() - if (onenand_prog_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; -#endif - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - case 0x1a: /* Program single/multiple spare area sector from buffer */ - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - - SETBUF_S() - if (onenand_prog_spare(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - /* TODO: if (s->bufaddr & 3) + s->count was > 4 (2k-pages) - * or if (s->bufaddr & 1) + s->count was > 2 (1k-pages) - * then we need two split the read/write into two chunks. - */ - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - case 0x1b: /* Copy-back program */ - SETBUF_S() - - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - if (onenand_load_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - SETADDR(ONEN_BUF_DEST_BLOCK, ONEN_BUF_DEST_PAGE) - if (onenand_prog_main(s, sec, s->count, buf)) - s->status |= ONEN_ERR_CMD | ONEN_ERR_PROG; - - /* TODO: spare areas */ - - s->intstatus |= ONEN_INT | ONEN_INT_PROG; - break; - - case 0x23: /* Unlock NAND array block(s) */ - s->intstatus |= ONEN_INT; - - /* XXX the previous (?) area should be locked automatically */ - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; - } - break; - case 0x27: /* Unlock All NAND array blocks */ - s->intstatus |= ONEN_INT; - - for (b = 0; b < s->blocks; b ++) { - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_UNLOCKED; - } - break; - - case 0x2a: /* Lock NAND array block(s) */ - s->intstatus |= ONEN_INT; - - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_LOCKTIGHTEN) - break; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKED; - } - break; - case 0x2c: /* Lock-tight NAND array block(s) */ - s->intstatus |= ONEN_INT; - - for (b = s->unladdr[0]; b <= s->unladdr[1]; b ++) { - if (b >= s->blocks) { - s->status |= ONEN_ERR_CMD; - break; - } - if (s->blockwp[b] == ONEN_LOCK_UNLOCKED) - continue; - - s->wpstatus = s->blockwp[b] = ONEN_LOCK_LOCKTIGHTEN; - } - break; - - case 0x71: /* Erase-Verify-Read */ - s->intstatus |= ONEN_INT; - break; - case 0x95: /* Multi-block erase */ - qemu_irq_pulse(s->intr); - /* Fall through. */ - case 0x94: /* Block erase */ - sec = ((s->addr[ONEN_BUF_BLOCK] & 0xfff) | - (s->addr[ONEN_BUF_BLOCK] >> 15 ? s->density_mask : 0)) - << (BLOCK_SHIFT - 9); - if (onenand_erase(s, sec, 1 << (BLOCK_SHIFT - 9))) - s->status |= ONEN_ERR_CMD | ONEN_ERR_ERASE; - - s->intstatus |= ONEN_INT | ONEN_INT_ERASE; - break; - case 0xb0: /* Erase suspend */ - break; - case 0x30: /* Erase resume */ - s->intstatus |= ONEN_INT | ONEN_INT_ERASE; - break; - - case 0xf0: /* Reset NAND Flash core */ - onenand_reset(s, 0); - break; - case 0xf3: /* Reset OneNAND */ - onenand_reset(s, 0); - break; - - case 0x65: /* OTP Access */ - s->intstatus |= ONEN_INT; - s->blk_cur = NULL; - s->current = s->otp; - s->secs_cur = 1 << (BLOCK_SHIFT - 9); - s->addr[ONEN_BUF_BLOCK] = 0; - s->otpmode = 1; - break; - - default: - s->status |= ONEN_ERR_CMD; - s->intstatus |= ONEN_INT; - qemu_log_mask(LOG_GUEST_ERROR, "unknown OneNAND command %x\n", - s->command); - } - - onenand_intr_update(s); -} - -static uint64_t onenand_read(void *opaque, hwaddr addr, - unsigned size) -{ - OneNANDState *s = (OneNANDState *) opaque; - int offset = addr >> s->shift; - - switch (offset) { - case 0x0000 ... 0xbffe: - return lduw_le_p(s->boot[0] + addr); - - case 0xf000: /* Manufacturer ID */ - return s->id.man; - case 0xf001: /* Device ID */ - return s->id.dev; - case 0xf002: /* Version ID */ - return s->id.ver; - /* TODO: get the following values from a real chip! */ - case 0xf003: /* Data Buffer size */ - return 1 << PAGE_SHIFT; - case 0xf004: /* Boot Buffer size */ - return 0x200; - case 0xf005: /* Amount of buffers */ - return 1 | (2 << 8); - case 0xf006: /* Technology */ - return 0; - - case 0xf100 ... 0xf107: /* Start addresses */ - return s->addr[offset - 0xf100]; - - case 0xf200: /* Start buffer */ - return (s->bufaddr << 8) | ((s->count - 1) & (1 << (PAGE_SHIFT - 10))); - - case 0xf220: /* Command */ - return s->command; - case 0xf221: /* System Configuration 1 */ - return s->config[0] & 0xffe0; - case 0xf222: /* System Configuration 2 */ - return s->config[1]; - - case 0xf240: /* Controller Status */ - return s->status; - case 0xf241: /* Interrupt */ - return s->intstatus; - case 0xf24c: /* Unlock Start Block Address */ - return s->unladdr[0]; - case 0xf24d: /* Unlock End Block Address */ - return s->unladdr[1]; - case 0xf24e: /* Write Protection Status */ - return s->wpstatus; - - 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 */ - qemu_log_mask(LOG_UNIMP, - "onenand: ECC result registers unimplemented\n"); - return 0x0000; - } - - qemu_log_mask(LOG_GUEST_ERROR, "read of unknown OneNAND register 0x%x\n", - offset); - return 0; -} - -static void onenand_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - OneNANDState *s = (OneNANDState *) opaque; - int offset = addr >> s->shift; - int sec; - - switch (offset) { - case 0x0000 ... 0x01ff: - case 0x8000 ... 0x800f: - if (s->cycle) { - s->cycle = 0; - - if (value == 0x0000) { - SETADDR(ONEN_BUF_BLOCK, ONEN_BUF_PAGE) - onenand_load_main(s, sec, - 1 << (PAGE_SHIFT - 9), s->data[0][0]); - s->addr[ONEN_BUF_PAGE] += 4; - s->addr[ONEN_BUF_PAGE] &= 0xff; - } - break; - } - - switch (value) { - case 0x00f0: /* Reset OneNAND */ - onenand_reset(s, 0); - break; - - case 0x00e0: /* Load Data into Buffer */ - s->cycle = 1; - break; - - 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; - s->boot[0][2 << s->shift] = s->wpstatus & 0xff; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "unknown OneNAND boot command %" PRIx64 "\n", - value); - } - break; - - case 0xf100 ... 0xf107: /* Start addresses */ - s->addr[offset - 0xf100] = value; - break; - - case 0xf200: /* Start buffer */ - s->bufaddr = (value >> 8) & 0xf; - if (PAGE_SHIFT == 11) - s->count = (value & 3) ?: 4; - else if (PAGE_SHIFT == 10) - s->count = (value & 1) ?: 2; - break; - - case 0xf220: /* Command */ - if (s->intstatus & (1 << 15)) - break; - s->command = value; - onenand_command(s); - break; - 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 */ - s->config[1] = value; - break; - - 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 */ - 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 */ - s->unladdr[1] = value & (s->blocks - 1); - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "write to unknown OneNAND register 0x%x\n", - offset); - } -} - -static const MemoryRegionOps onenand_ops = { - .read = onenand_read, - .write = onenand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void onenand_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - OneNANDState *s = ONE_NAND(dev); - uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7)); - void *ram; - Error *local_err = NULL; - - s->base = (hwaddr)-1; - s->rdy = NULL; - s->blocks = size >> BLOCK_SHIFT; - s->secs = size >> 9; - s->blockwp = g_malloc(s->blocks); - s->density_mask = (s->id.dev & 0x08) - ? (1 << (6 + ((s->id.dev >> 4) & 7))) : 0; - memory_region_init_io(&s->iomem, OBJECT(s), &onenand_ops, s, "onenand", - 0x10000 << s->shift); - if (!s->blk) { - s->image = memset(g_malloc(size + (size >> 5)), - 0xff, size + (size >> 5)); - } else { - if (!blk_supports_write_perm(s->blk)) { - error_setg(errp, "Can't use a read-only drive"); - return; - } - blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, - BLK_PERM_ALL, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - s->blk_cur = s->blk; - } - s->otp = memset(g_malloc((64 + 2) << PAGE_SHIFT), - 0xff, (64 + 2) << PAGE_SHIFT); - memory_region_init_ram_nomigrate(&s->ram, OBJECT(s), "onenand.ram", - 0xc000 << s->shift, &error_fatal); - vmstate_register_ram_global(&s->ram); - ram = memory_region_get_ram_ptr(&s->ram); - s->boot[0] = ram + (0x0000 << s->shift); - s->boot[1] = ram + (0x8000 << s->shift); - s->data[0][0] = ram + ((0x0200 + (0 << (PAGE_SHIFT - 1))) << s->shift); - s->data[0][1] = ram + ((0x8010 + (0 << (PAGE_SHIFT - 6))) << s->shift); - s->data[1][0] = ram + ((0x0200 + (1 << (PAGE_SHIFT - 1))) << s->shift); - s->data[1][1] = ram + ((0x8010 + (1 << (PAGE_SHIFT - 6))) << s->shift); - onenand_mem_setup(s); - sysbus_init_irq(sbd, &s->intr); - sysbus_init_mmio(sbd, &s->container); - vmstate_register(VMSTATE_IF(dev), - ((s->shift & 0x7f) << 24) - | ((s->id.man & 0xff) << 16) - | ((s->id.dev & 0xff) << 8) - | (s->id.ver & 0xff), - &vmstate_onenand, s); -} - -static Property onenand_properties[] = { - DEFINE_PROP_UINT16("manufacturer_id", OneNANDState, id.man, 0), - DEFINE_PROP_UINT16("device_id", OneNANDState, id.dev, 0), - DEFINE_PROP_UINT16("version_id", OneNANDState, id.ver, 0), - DEFINE_PROP_INT32("shift", OneNANDState, shift, 0), - DEFINE_PROP_DRIVE("drive", OneNANDState, blk), - DEFINE_PROP_END_OF_LIST(), -}; - -static void onenand_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = onenand_realize; - dc->reset = onenand_system_reset; - device_class_set_props(dc, onenand_properties); -} - -static const TypeInfo onenand_info = { - .name = TYPE_ONE_NAND, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(OneNANDState), - .class_init = onenand_class_init, -}; - -static void onenand_register_types(void) -{ - type_register_static(&onenand_info); -} - -void *onenand_raw_otp(DeviceState *onenand_device) -{ - OneNANDState *s = ONE_NAND(onenand_device); - - return s->otp; -} - -type_init(onenand_register_types) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 0cbc2fb4cb..21a81b44f0 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -45,10 +45,8 @@ #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" #include "qemu/option.h" #include "hw/sysbus.h" #include "migration/vmstate.h" @@ -81,27 +79,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 +251,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; @@ -401,40 +402,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, @@ -495,10 +517,6 @@ static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, break; case 0xe8: /* Write to buffer */ trace_pflash_write(pfl->name, "write to buffer"); - /* FIXME should save @offset, @width for case 1+ */ - qemu_log_mask(LOG_UNIMP, - "%s: Write to buffer emulation is flawed\n", - __func__); pfl->status |= 0x80; /* Ready! */ break; case 0xf0: /* Probe for AMD flash */ @@ -549,7 +567,6 @@ 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++; break; @@ -582,12 +599,10 @@ 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->blk_offset == -1 && pfl->counter) { + pflash_blk_write_start(pfl, offset); + } + if (!pfl->ro && (pfl->blk_offset != -1)) { pflash_data_write(pfl, offset, value, width, be); } else { pfl->status |= 0x10; /* Programming error */ @@ -596,18 +611,9 @@ 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 */ - } + break; } pfl->counter--; @@ -619,20 +625,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 +648,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 +846,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 +869,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 +891,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 +900,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), @@ -931,27 +939,23 @@ static void pflash_cfi01_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pflash_cfi01_system_reset; + device_class_set_legacy_reset(dc, pflash_cfi01_system_reset); dc->realize = pflash_cfi01_realize; device_class_set_props(dc, pflash_cfi01_properties); dc->vmsd = &vmstate_pflash; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } - -static const TypeInfo pflash_cfi01_info = { - .name = TYPE_PFLASH_CFI01, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PFlashCFI01), - .class_init = pflash_cfi01_class_init, +static const TypeInfo pflash_cfi01_types[] = { + { + .name = TYPE_PFLASH_CFI01, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PFlashCFI01), + .class_init = pflash_cfi01_class_init, + }, }; -static void pflash_cfi01_register_types(void) -{ - type_register_static(&pflash_cfi01_info); -} - -type_init(pflash_cfi01_register_types) +DEFINE_TYPES(pflash_cfi01_types) PFlashCFI01 *pflash_cfi01_register(hwaddr base, const char *name, diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 2a99b286b0..8393f261b8 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -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; @@ -974,7 +974,7 @@ static void pflash_cfi02_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pflash_cfi02_realize; - dc->reset = pflash_cfi02_reset; + device_class_set_legacy_reset(dc, pflash_cfi02_reset); dc->unrealize = pflash_cfi02_unrealize; device_class_set_props(dc, pflash_cfi02_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/block/swim.c b/hw/block/swim.c index 333da08ce0..64992eb72e 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() } @@ -469,7 +556,7 @@ static void sysbus_swim_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = sysbus_swim_realize; - dc->reset = sysbus_swim_reset; + device_class_set_legacy_reset(dc, sysbus_swim_reset); dc->vmsd = &vmstate_sysbus_swim; } diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c deleted file mode 100644 index bfc27ad899..0000000000 --- a/hw/block/tc58128.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * TC58128 NAND EEPROM emulation - * - * Copyright (c) 2005 Samuel Tardieu - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * SPDX-License-Identifier: MIT - */ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "hw/sh4/sh.h" -#include "hw/loader.h" -#include "sysemu/qtest.h" -#include "qemu/error-report.h" - -#define CE1 0x0100 -#define CE2 0x0200 -#define RE 0x0400 -#define WE 0x0800 -#define ALE 0x1000 -#define CLE 0x2000 -#define RDY1 0x4000 -#define RDY2 0x8000 -#define RDY(n) ((n) == 0 ? RDY1 : RDY2) - -typedef enum { WAIT, READ1, READ2, READ3 } state_t; - -typedef struct { - uint8_t *flash_contents; - state_t state; - uint32_t address; - uint8_t address_cycle; -} tc58128_dev; - -static tc58128_dev tc58128_devs[2]; - -#define FLASH_SIZE (16 * MiB) - -static void init_dev(tc58128_dev * dev, const char *filename) -{ - int ret, blocks; - - dev->state = WAIT; - dev->flash_contents = g_malloc(FLASH_SIZE); - memset(dev->flash_contents, 0xff, FLASH_SIZE); - if (filename) { - /* 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 (!qtest_enabled()) { - error_report("Could not load flash image %s", filename); - exit(1); - } - } 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); - } - } -} - -static void handle_command(tc58128_dev * dev, uint8_t command) -{ - switch (command) { - case 0xff: - 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; - case 0x01: - 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; - default: - fprintf(stderr, "unknown flash command 0x%02x\n", command); - abort(); - } -} - -static void handle_address(tc58128_dev * dev, uint8_t data) -{ - switch (dev->state) { - 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 */ - abort(); - } - dev->address_cycle++; - break; - default: - abort(); - } -} - -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); -#endif - return dev->flash_contents[dev->address++]; -} - -/* We never mark the device as busy, so interrupts cannot be triggered - XXXXX */ - -static int tc58128_cb(uint16_t porta, uint16_t portb, - uint16_t * periph_pdtra, uint16_t * periph_portadir, - uint16_t * periph_pdtrb, uint16_t * periph_portbdir) -{ - int dev; - - if ((porta & CE1) == 0) - dev = 0; - else if ((porta & CE2) == 0) - dev = 1; - else - 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; - } - - if (porta & CLE) { - /* 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); - } else if ((porta & RE) == 0) { - *periph_portadir |= 0x00ff; - *periph_pdtra &= 0xff00; - *periph_pdtra |= handle_read(&tc58128_devs[dev]); - } else { - abort(); - } - return 1; -} - -static sh7750_io_device tc58128 = { - RE | WE, /* Port A triggers */ - 0, /* Port B triggers */ - tc58128_cb /* Callback */ -}; - -int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2) -{ - 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 2c45a62bd5..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" @@ -83,3 +93,11 @@ m25p80_read_data(void *s, uint32_t pos, uint8_t v) "[%p] Read data 0x%"PRIx32"=0 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 aff4d2b8cb..7996e49821 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -32,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, @@ -53,6 +51,8 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; @@ -81,7 +81,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; @@ -90,32 +90,39 @@ static void vhost_user_blk_set_config(VirtIODevice *vdev, const uint8_t *config) s->blkcfg.wce = blkcfg->wce; } +static int vhost_user_blk_sync_config(DeviceState *dev, Error **errp) +{ + int ret; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VHostUserBlk *s = VHOST_USER_BLK(vdev); + + ret = vhost_dev_get_config(&s->dev, (uint8_t *)&s->blkcfg, + vdev->config_len, errp); + if (ret < 0) { + return ret; + } + + memcpy(vdev->config, &s->blkcfg, vdev->config_len); + virtio_notify_config(vdev); + + return 0; +} + 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; if (!dev->started) { return 0; } - ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg, - vdev->config_len, &local_err); + ret = vhost_user_blk_sync_config(DEVICE(dev->vdev), &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, vdev->config_len); - virtio_notify_config(dev->vdev); - } - return 0; } @@ -328,7 +335,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; @@ -345,15 +351,14 @@ static int vhost_user_blk_connect(DeviceState *dev, Error **errp) 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) @@ -362,7 +367,7 @@ static void vhost_user_blk_disconnect(DeviceState *dev) VHostUserBlk *s = VHOST_USER_BLK(vdev); if (!s->connected) { - return; + goto done; } s->connected = false; @@ -370,6 +375,7 @@ static void vhost_user_blk_disconnect(DeviceState *dev) vhost_dev_cleanup(&s->dev); +done: /* Re-instate the event handler for new connections */ qemu_chr_fe_set_handlers(&s->chardev, NULL, NULL, vhost_user_blk_event, NULL, dev, NULL, true); @@ -405,7 +411,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; @@ -423,7 +429,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, - s->parent_obj.config_len, errp); + VIRTIO_DEVICE(s)->config_len, errp); if (ret < 0) { qemu_chr_fe_disconnect(&s->chardev); vhost_dev_cleanup(&s->dev); @@ -482,7 +488,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) { @@ -558,7 +564,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() }, @@ -585,6 +591,7 @@ static void vhost_user_blk_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, vhost_user_blk_properties); dc->vmsd = &vmstate_vhost_user_blk; + dc->sync_config = vhost_user_blk_sync_config; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vhost_user_blk_device_realize; vdc->unrealize = vhost_user_blk_device_unrealize; diff --git a/hw/block/virtio-blk-common.c b/hw/block/virtio-blk-common.c index ac52d7c176..e2f8e2f6da 100644 --- a/hw/block/virtio-blk-common.c +++ b/hw/block/virtio-blk-common.c @@ -29,6 +29,8 @@ static const VirtIOFeature feature_sizes[] = { .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)}, {} }; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index f717550fdc..9166d7974d 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -12,11 +12,13 @@ */ #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" @@ -25,7 +27,6 @@ #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 @@ -36,6 +37,8 @@ #include "hw/virtio/virtio-blk-common.h" #include "qemu/coroutine.h" +static void virtio_blk_ioeventfd_attach(VirtIOBlock *s); + static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, VirtIOBlockReq *req) { @@ -63,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); } @@ -80,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) { @@ -100,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; @@ -133,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) @@ -141,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) @@ -163,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); @@ -175,64 +170,8 @@ 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__ - -typedef struct { - VirtIOBlockReq *req; - struct sg_io_hdr hdr; -} VirtIOBlockIoctlReq; - -static void virtio_blk_ioctl_complete(void *opaque, int status) -{ - VirtIOBlockIoctlReq *ioctl_req = opaque; - VirtIOBlockReq *req = ioctl_req->req; - VirtIOBlock *s = req->dev; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - struct virtio_scsi_inhdr *scsi; - struct sg_io_hdr *hdr; - - scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - - if (status) { - status = VIRTIO_BLK_S_UNSUPP; - virtio_stl_p(vdev, &scsi->errors, 255); - goto out; - } - - hdr = &ioctl_req->hdr; - /* - * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) - * clear the masked_status field [hence status gets cleared too, see - * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED - * status has occurred. However they do set DRIVER_SENSE in driver_status - * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. - */ - if (hdr->status == 0 && hdr->sb_len_wr > 0) { - hdr->status = CHECK_CONDITION; - } - - virtio_stl_p(vdev, &scsi->errors, - hdr->status | (hdr->msg_status << 8) | - (hdr->host_status << 16) | (hdr->driver_status << 24)); - virtio_stl_p(vdev, &scsi->residual, hdr->resid); - virtio_stl_p(vdev, &scsi->sense_len, hdr->sb_len_wr); - 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); -} - -#endif - static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) { VirtIOBlockReq *req = virtqueue_pop(vq, sizeof(VirtIOBlockReq)); @@ -243,20 +182,14 @@ static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s, VirtQueue *vq) return req; } -static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) +static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { - int status = VIRTIO_BLK_S_OK; - struct virtio_scsi_inhdr *scsi = NULL; + int status; + struct virtio_scsi_inhdr *scsi; VirtIOBlock *blk = req->dev; VirtIODevice *vdev = VIRTIO_DEVICE(blk); VirtQueueElement *elem = &req->elem; -#ifdef __linux__ - int i; - VirtIOBlockIoctlReq *ioctl_req; - BlockAIOCB *acb; -#endif - /* * We require at least one output segment each for the virtio_blk_outhdr * and the SCSI command block. @@ -272,95 +205,16 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) /* * The scsi inhdr is placed in the second-to-last input segment, just * before the regular inhdr. + * + * Just put anything nonzero so that the ioctl fails in the guest. */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; - - if (!virtio_has_feature(blk->host_features, VIRTIO_BLK_F_SCSI)) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - - /* - * No support for bidirection commands yet. - */ - if (elem->out_num > 2 && elem->in_num > 3) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - -#ifdef __linux__ - ioctl_req = g_new0(VirtIOBlockIoctlReq, 1); - ioctl_req->req = req; - ioctl_req->hdr.interface_id = 'S'; - ioctl_req->hdr.cmd_len = elem->out_sg[1].iov_len; - ioctl_req->hdr.cmdp = elem->out_sg[1].iov_base; - ioctl_req->hdr.dxfer_len = 0; - - if (elem->out_num > 2) { - /* - * If there are more than the minimally required 2 output segments - * there is write payload starting from the third iovec. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_TO_DEV; - ioctl_req->hdr.iovec_count = elem->out_num - 2; - - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->out_sg[i + 2].iov_len; - } - - ioctl_req->hdr.dxferp = elem->out_sg + 2; - - } else if (elem->in_num > 3) { - /* - * If we have more than 3 input segments the guest wants to actually - * read data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_FROM_DEV; - ioctl_req->hdr.iovec_count = elem->in_num - 3; - for (i = 0; i < ioctl_req->hdr.iovec_count; i++) { - ioctl_req->hdr.dxfer_len += elem->in_sg[i].iov_len; - } - - ioctl_req->hdr.dxferp = elem->in_sg; - } else { - /* - * Some SCSI commands don't actually transfer any data. - */ - ioctl_req->hdr.dxfer_direction = SG_DXFER_NONE; - } - - ioctl_req->hdr.sbp = elem->in_sg[elem->in_num - 3].iov_base; - ioctl_req->hdr.mx_sb_len = elem->in_sg[elem->in_num - 3].iov_len; - - acb = blk_aio_ioctl(blk->blk, SG_IO, &ioctl_req->hdr, - virtio_blk_ioctl_complete, ioctl_req); - if (!acb) { - g_free(ioctl_req); - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - return -EINPROGRESS; -#else - abort(); -#endif + virtio_stl_p(vdev, &scsi->errors, 255); + status = VIRTIO_BLK_S_UNSUPP; fail: - /* Just put anything nonzero so that the ioctl fails in the guest. */ - if (scsi) { - virtio_stl_p(vdev, &scsi->errors, 255); - } - return status; -} - -static void virtio_blk_handle_scsi(VirtIOBlockReq *req) -{ - int status; - - status = virtio_blk_handle_scsi_req(req); - if (status != -EINPROGRESS) { - virtio_blk_req_complete(req, status); - virtio_blk_free_request(req); - } + virtio_blk_req_complete(req, status); + virtio_blk_free_request(req); } static inline void submit_requests(VirtIOBlock *s, MultiReqBuffer *mrb, @@ -601,6 +455,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 - 1ull); + } 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; @@ -687,6 +878,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; @@ -705,6 +914,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, @@ -761,8 +978,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) { @@ -786,34 +1002,33 @@ void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq) 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)) { @@ -834,67 +1049,82 @@ void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh) if (mrb.num_reqs) { 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 = NULL; 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); } @@ -904,6 +1134,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; @@ -963,6 +1194,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); } @@ -973,9 +1228,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, @@ -990,13 +1243,9 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_GEOMETRY); virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); - if (virtio_has_feature(features, VIRTIO_F_VERSION_1)) { - if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_SCSI)) { - error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); - return 0; - } - } else { + if (!virtio_has_feature(features, VIRTIO_F_VERSION_1)) { virtio_clear_feature(&features, VIRTIO_F_ANY_LAYOUT); + /* Added for historical reasons, removing it could break migration. */ virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); } @@ -1020,7 +1269,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)) { @@ -1043,29 +1292,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); } @@ -1091,8 +1342,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; @@ -1111,22 +1365,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; @@ -1172,6 +1870,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)) { @@ -1195,6 +1901,8 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) 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; @@ -1203,7 +1911,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++) { @@ -1213,7 +1927,13 @@ 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); @@ -1234,12 +1954,12 @@ 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); @@ -1259,7 +1979,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() }, @@ -1272,10 +1992,6 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), DEFINE_PROP_BIT64("config-wce", VirtIOBlock, host_features, VIRTIO_BLK_F_CONFIG_WCE, true), -#ifdef __linux__ - DEFINE_PROP_BIT64("scsi", VirtIOBlock, host_features, - VIRTIO_BLK_F_SCSI, false), -#endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, @@ -1284,6 +2000,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, @@ -1316,8 +2034,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 345b284d70..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,8 +388,6 @@ static void xen_block_realize(XenDevice *xendev, Error **errp) return; } - blk_set_dev_ops(blk, &xen_block_dev_ops, blockdev); - if (conf->discard_granularity == -1) { conf->discard_granularity = conf->physical_block_size; } @@ -256,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", @@ -273,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, @@ -347,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) ? @@ -355,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: @@ -456,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; @@ -469,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; @@ -481,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; } @@ -759,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 { @@ -774,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); @@ -809,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); @@ -820,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/Kconfig b/hw/char/Kconfig index 6b6cf2fc1d..4b73a803bf 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -21,6 +21,10 @@ config SERIAL_ISA depends on ISA_BUS select SERIAL +config SERIAL_MM + bool + select SERIAL + config SERIAL_PCI bool default y if PCI_DEVICES @@ -41,6 +45,9 @@ config VIRTIO_SERIAL config STM32F2XX_USART bool +config STM32L4X5_USART + bool + config CMSDK_APB_UART bool diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c index 5bcf9db0b7..3aff01cd54 100644 --- a/hw/char/avr_usart.c +++ b/hw/char/avr_usart.c @@ -86,7 +86,7 @@ static void update_char_mask(AVRUsartState *usart) usart->char_mask = 0b11111111; break; default: - assert(0); + g_assert_not_reached(); } } @@ -300,7 +300,7 @@ static void avr_usart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = avr_usart_reset; + device_class_set_legacy_reset(dc, avr_usart_reset); device_class_set_props(dc, avr_usart_properties); dc->realize = avr_usart_realize; } diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 96410b1ff8..fca2f27a55 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -138,7 +138,7 @@ static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */ if (s->read_count > 0) { res |= 0x1; /* data in input buffer */ - assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN); + assert(s->read_count <= BCM2835_AUX_RX_FIFO_LEN); res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */ } return res; @@ -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..77d9a2a221 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: @@ -523,7 +525,7 @@ static void cadence_uart_reset_init(Object *obj, ResetType type) s->r[R_TTRIG] = 0x00000020; } -static void cadence_uart_reset_hold(Object *obj) +static void cadence_uart_reset_hold(Object *obj, ResetType type) { CadenceUARTState *s = CADENCE_UART(obj); @@ -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..467e40b715 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), @@ -389,7 +389,7 @@ static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_uart_realize; dc->vmsd = &cmsdk_apb_uart_vmstate; - dc->reset = cmsdk_apb_uart_reset; + device_class_set_legacy_reset(dc, cmsdk_apb_uart_reset); device_class_set_props(dc, cmsdk_apb_uart_properties); } diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 00e5df5517..5b04abec1d 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() @@ -182,7 +182,7 @@ static void digic_uart_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = digic_uart_realize; - dc->reset = digic_uart_reset; + device_class_set_legacy_reset(dc, digic_uart_reset); dc->vmsd = &vmstate_digic_uart; device_class_set_props(dc, digic_uart_properties); } diff --git a/hw/char/escc.c b/hw/char/escc.c index 17a908c59b..b1b1bbed15 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); @@ -284,6 +287,7 @@ static void escc_reset_chn(ESCCChannelState *s) s->rxint = s->txint = 0; s->rxint_under_svc = s->txint_under_svc = 0; s->e0_mode = s->led_mode = s->caps_lock_mode = s->num_lock_mode = 0; + s->sunmouse_dx = s->sunmouse_dy = s->sunmouse_buttons = 0; clear_queue(s); } @@ -650,7 +654,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 +767,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 +786,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 +846,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,60 +946,103 @@ 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; } } -static void sunmouse_event(void *opaque, - int dx, int dy, int dz, int buttons_state) +static void sunmouse_handle_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) { - ESCCChannelState *s = opaque; + ESCCChannelState *s = (ESCCChannelState *)dev; + InputMoveEvent *move; + InputBtnEvent *btn; + static const int bmap[INPUT_BUTTON__MAX] = { + [INPUT_BUTTON_LEFT] = 0x4, + [INPUT_BUTTON_MIDDLE] = 0x2, + [INPUT_BUTTON_RIGHT] = 0x1, + }; + + switch (evt->type) { + case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; + if (move->axis == INPUT_AXIS_X) { + s->sunmouse_dx += move->value; + } else if (move->axis == INPUT_AXIS_Y) { + s->sunmouse_dy -= move->value; + } + break; + + case INPUT_EVENT_KIND_BTN: + btn = evt->u.btn.data; + if (bmap[btn->button]) { + if (btn->down) { + s->sunmouse_buttons |= bmap[btn->button]; + } else { + s->sunmouse_buttons &= ~bmap[btn->button]; + } + /* Indicate we have a supported button event */ + s->sunmouse_buttons |= 0x80; + } + break; + + default: + /* keep gcc happy */ + break; + } +} + +static void sunmouse_sync(DeviceState *dev) +{ + ESCCChannelState *s = (ESCCChannelState *)dev; int ch; - trace_escc_sunmouse_event(dx, dy, buttons_state); + if (s->sunmouse_dx == 0 && s->sunmouse_dy == 0 && + (s->sunmouse_buttons & 0x80) == 0) { + /* Nothing to do after button event filter */ + return; + } + + /* Clear our button event flag */ + s->sunmouse_buttons &= ~0x80; + trace_escc_sunmouse_event(s->sunmouse_dx, s->sunmouse_dy, + s->sunmouse_buttons); ch = 0x80 | 0x7; /* protocol start byte, no buttons pressed */ - - if (buttons_state & MOUSE_EVENT_LBUTTON) { - ch ^= 0x4; - } - if (buttons_state & MOUSE_EVENT_MBUTTON) { - ch ^= 0x2; - } - if (buttons_state & MOUSE_EVENT_RBUTTON) { - ch ^= 0x1; - } - + ch ^= s->sunmouse_buttons; put_queue(s, ch); - ch = dx; - + ch = s->sunmouse_dx; if (ch > 127) { ch = 127; } else if (ch < -127) { ch = -127; } - put_queue(s, ch & 0xff); + s->sunmouse_dx -= ch; - ch = -dy; - + ch = s->sunmouse_dy; if (ch > 127) { ch = 127; } else if (ch < -127) { ch = -127; } - put_queue(s, ch & 0xff); + s->sunmouse_dy -= ch; /* MSC protocol specifies two extra motion bytes */ - put_queue(s, 0); put_queue(s, 0); } +static const QemuInputHandler sunmouse_handler = { + .name = "QEMU Sun Mouse", + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, + .event = sunmouse_handle_event, + .sync = sunmouse_sync, +}; + static void escc_init1(Object *obj) { ESCCState *s = ESCC(obj); @@ -958,8 +1080,8 @@ static void escc_realize(DeviceState *dev, Error **errp) } if (s->chn[0].type == escc_mouse) { - qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0, - "QEMU Sun Mouse"); + s->chn[0].hs = qemu_input_handler_register((DeviceState *)(&s->chn[0]), + &sunmouse_handler); } if (s->chn[1].type == escc_kbd) { s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]), @@ -976,6 +1098,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(), }; @@ -983,7 +1106,7 @@ static void escc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = escc_reset; + device_class_set_legacy_reset(dc, escc_reset); dc->realize = escc_realize; dc->vmsd = &vmstate_escc; device_class_set_props(dc, escc_properties); diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c deleted file mode 100644 index e8c3017724..0000000000 --- a/hw/char/etraxfs_ser.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * QEMU ETRAX System Emulator - * - * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/sysbus.h" -#include "chardev/char-fe.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qom/object.h" - -#define D(x) - -#define RW_TR_CTRL (0x00 / 4) -#define RW_TR_DMA_EN (0x04 / 4) -#define RW_REC_CTRL (0x08 / 4) -#define RW_DOUT (0x1c / 4) -#define RS_STAT_DIN (0x20 / 4) -#define R_STAT_DIN (0x24 / 4) -#define RW_INTR_MASK (0x2c / 4) -#define RW_ACK_INTR (0x30 / 4) -#define R_INTR (0x34 / 4) -#define R_MASKED_INTR (0x38 / 4) -#define R_MAX (0x3c / 4) - -#define STAT_DAV 16 -#define STAT_TR_IDLE 22 -#define STAT_TR_RDY 24 - -#define TYPE_ETRAX_FS_SERIAL "etraxfs-serial" -typedef struct ETRAXSerial ETRAXSerial; -DECLARE_INSTANCE_CHECKER(ETRAXSerial, ETRAX_SERIAL, - TYPE_ETRAX_FS_SERIAL) - -struct ETRAXSerial { - SysBusDevice parent_obj; - - MemoryRegion mmio; - CharBackend chr; - qemu_irq irq; - - int pending_tx; - - uint8_t rx_fifo[16]; - unsigned int rx_fifo_pos; - unsigned int rx_fifo_len; - - /* Control registers. */ - uint32_t regs[R_MAX]; -}; - -static void ser_update_irq(ETRAXSerial *s) -{ - - if (s->rx_fifo_len) { - s->regs[R_INTR] |= 8; - } else { - s->regs[R_INTR] &= ~8; - } - - s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK]; - qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]); -} - -static uint64_t -ser_read(void *opaque, hwaddr addr, unsigned int size) -{ - ETRAXSerial *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) - { - case R_STAT_DIN: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; - if (s->rx_fifo_len) { - r |= 1 << STAT_DAV; - } - r |= 1 << STAT_TR_RDY; - r |= 1 << STAT_TR_IDLE; - break; - case RS_STAT_DIN: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; - if (s->rx_fifo_len) { - r |= 1 << STAT_DAV; - s->rx_fifo_len--; - } - r |= 1 << STAT_TR_RDY; - r |= 1 << STAT_TR_IDLE; - break; - default: - r = s->regs[addr]; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r)); - break; - } - return r; -} - -static void -ser_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - ETRAXSerial *s = opaque; - uint32_t value = val64; - unsigned char ch = val64; - - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); - addr >>= 2; - switch (addr) - { - case RW_DOUT: - /* 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->regs[R_INTR] |= 3; - s->pending_tx = 1; - s->regs[addr] = value; - break; - case RW_ACK_INTR: - if (s->pending_tx) { - value &= ~1; - s->pending_tx = 0; - D(qemu_log("fixedup value=%x r_intr=%x\n", - value, s->regs[R_INTR])); - } - s->regs[addr] = value; - s->regs[R_INTR] &= ~value; - D(printf("r_intr=%x\n", s->regs[R_INTR])); - break; - default: - s->regs[addr] = value; - break; - } - ser_update_irq(s); -} - -static const MemoryRegionOps ser_ops = { - .read = ser_read, - .write = ser_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static Property etraxfs_ser_properties[] = { - DEFINE_PROP_CHR("chardev", ETRAXSerial, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void serial_receive(void *opaque, const uint8_t *buf, int size) -{ - ETRAXSerial *s = opaque; - int i; - - /* Got a byte. */ - if (s->rx_fifo_len >= 16) { - D(qemu_log("WARNING: UART dropped char.\n")); - return; - } - - for (i = 0; i < size; i++) { - s->rx_fifo[s->rx_fifo_pos] = buf[i]; - s->rx_fifo_pos++; - s->rx_fifo_pos &= 15; - s->rx_fifo_len++; - } - - ser_update_irq(s); -} - -static int serial_can_receive(void *opaque) -{ - ETRAXSerial *s = opaque; - - /* Is the receiver enabled? */ - if (!(s->regs[RW_REC_CTRL] & (1 << 3))) { - return 0; - } - - return sizeof(s->rx_fifo) - s->rx_fifo_len; -} - -static void serial_event(void *opaque, QEMUChrEvent event) -{ - -} - -static void etraxfs_ser_reset(DeviceState *d) -{ - ETRAXSerial *s = ETRAX_SERIAL(d); - - /* transmitter begins ready and idle. */ - s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY); - s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); - - s->regs[RW_REC_CTRL] = 0x10000; - -} - -static void etraxfs_ser_init(Object *obj) -{ - ETRAXSerial *s = ETRAX_SERIAL(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->mmio, obj, &ser_ops, s, - "etraxfs-serial", R_MAX * 4); - sysbus_init_mmio(dev, &s->mmio); -} - -static void etraxfs_ser_realize(DeviceState *dev, Error **errp) -{ - ETRAXSerial *s = ETRAX_SERIAL(dev); - - qemu_chr_fe_set_handlers(&s->chr, - serial_can_receive, serial_receive, - serial_event, NULL, s, NULL, true); -} - -static void etraxfs_ser_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = etraxfs_ser_reset; - device_class_set_props(dc, etraxfs_ser_properties); - dc->realize = etraxfs_ser_realize; -} - -static const TypeInfo etraxfs_ser_info = { - .name = TYPE_ETRAX_FS_SERIAL, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ETRAXSerial), - .instance_init = etraxfs_ser_init, - .class_init = etraxfs_ser_class_init, -}; - -static void etraxfs_serial_register_types(void) -{ - type_register_static(&etraxfs_ser_info); -} - -type_init(etraxfs_serial_register_types) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 7b7c56b6ef..d9e732f98b 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -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, @@ -717,7 +717,7 @@ static void exynos4210_uart_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = exynos4210_uart_realize; - dc->reset = exynos4210_uart_reset; + device_class_set_legacy_reset(dc, exynos4210_uart_reset); device_class_set_props(dc, exynos4210_uart_properties); dc->vmsd = &vmstate_exynos4210_uart; } diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index 20b77885c1..d1917b83d8 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -16,6 +16,7 @@ #include "qemu/log.h" #include "trace.h" #include "exec/address-spaces.h" +#include "sysemu/dma.h" #include "hw/char/goldfish_tty.h" #define GOLDFISH_TTY_VERSION 1 @@ -69,7 +70,6 @@ static uint64_t goldfish_tty_read(void *opaque, hwaddr addr, static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) { uint32_t to_copy; - uint8_t *buf; uint8_t data_out[GOLFISH_TTY_BUFFER_SIZE]; int len; uint64_t ptr; @@ -97,8 +97,8 @@ static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) while (len) { to_copy = MIN(GOLFISH_TTY_BUFFER_SIZE, len); - address_space_rw(&address_space_memory, ptr, - MEMTXATTRS_UNSPECIFIED, data_out, to_copy, 0); + dma_memory_read_relaxed(&address_space_memory, ptr, + data_out, to_copy); qemu_chr_fe_write_all(&s->chr, data_out, to_copy); len -= to_copy; @@ -109,9 +109,9 @@ static void goldfish_tty_cmd(GoldfishTTYState *s, uint32_t cmd) len = s->data_len; ptr = s->data_ptr; while (len && !fifo8_is_empty(&s->rx_fifo)) { - buf = (uint8_t *)fifo8_pop_buf(&s->rx_fifo, len, &to_copy); - address_space_rw(&address_space_memory, ptr, - MEMTXATTRS_UNSPECIFIED, buf, to_copy, 1); + const uint8_t *buf = fifo8_pop_bufptr(&s->rx_fifo, len, &to_copy); + + dma_memory_write_relaxed(&address_space_memory, ptr, buf, to_copy); len -= to_copy; ptr += to_copy; @@ -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), @@ -262,7 +262,7 @@ static void goldfish_tty_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); device_class_set_props(dc, goldfish_tty_properties); - dc->reset = goldfish_tty_reset; + device_class_set_legacy_reset(dc, goldfish_tty_reset); dc->realize = goldfish_tty_realize; dc->unrealize = goldfish_tty_unrealize; dc->vmsd = &vmstate_goldfish_tty; diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index 82ff40a530..d0032b4d2a 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" @@ -285,7 +287,7 @@ static void grlib_apbuart_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = grlib_apbuart_realize; - dc->reset = grlib_apbuart_reset; + device_class_set_legacy_reset(dc, grlib_apbuart_reset); device_class_set_props(dc, grlib_apbuart_properties); } diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c index e58181fcf4..589177f85b 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), @@ -546,7 +547,7 @@ static void ibex_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = ibex_uart_reset; + device_class_set_legacy_reset(dc, ibex_uart_reset); dc->realize = ibex_uart_realize; dc->vmsd = &vmstate_ibex_uart; device_class_set_props(dc, ibex_uart_properties); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index ee1375e26d..22c9080b1c 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, @@ -370,7 +449,7 @@ static void imx_serial_class_init(ObjectClass *klass, void *data) dc->realize = imx_serial_realize; dc->vmsd = &vmstate_imx_serial; - dc->reset = imx_serial_reset_at_boot; + device_class_set_legacy_reset(dc, imx_serial_reset_at_boot); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->desc = "i.MX series UART"; device_class_set_props(dc, imx_serial_properties); 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..ad15e28944 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -322,7 +322,7 @@ static void mcf_uart_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = mcf_uart_realize; - dc->reset = mcf_uart_reset; + device_class_set_legacy_reset(dc, mcf_uart_reset); device_class_set_props(dc, mcf_uart_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -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..3c3224c05d 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() @@ -126,7 +126,7 @@ static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = mchp_pfsoc_mmuart_realize; - dc->reset = mchp_pfsoc_mmuart_reset; + device_class_set_legacy_reset(dc, mchp_pfsoc_mmuart_reset); dc->vmsd = &mchp_pfsoc_mmuart_vmstate; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/char/meson.build b/hw/char/meson.build index 7b594f51b8..1750834385 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -1,41 +1,41 @@ -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_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_MM', if_true: files('serial-mm.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_STM32L4X5_USART', if_true: files('stm32l4x5_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..04da3f8d97 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), @@ -313,7 +313,7 @@ static void nrf51_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = nrf51_uart_reset; + device_class_set_legacy_reset(dc, nrf51_uart_reset); dc->realize = nrf51_uart_realize; device_class_set_props(dc, nrf51_uart_properties); dc->vmsd = &nrf51_uart_vmstate; diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index e8da933378..07fb868965 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "chardev/char.h" #include "hw/arm/omap.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "exec/address-spaces.h" /* UARTs */ @@ -28,7 +28,6 @@ struct omap_uart_s { MemoryRegion iomem; hwaddr base; SerialMM *serial; /* TODO */ - struct omap_target_agent_s *ta; omap_clk fclk; qemu_irq irq; @@ -36,8 +35,6 @@ struct omap_uart_s { uint8_t syscontrol; uint8_t wkup; uint8_t cfps; - uint8_t mdr[2]; - uint8_t scr; uint8_t clksel; }; @@ -61,127 +58,8 @@ struct omap_uart_s *omap_uart_init(hwaddr base, s->fclk = fclk; s->irq = irq; s->serial = serial_mm_init(get_system_memory(), base, 2, irq, - omap_clk_getrate(fclk)/16, + omap_clk_getrate(fclk) / 16, chr ?: qemu_chr_new(label, "null", NULL), DEVICE_NATIVE_ENDIAN); return s; } - -static uint64_t omap_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - if (size == 4) { - return omap_badwidth_read8(opaque, addr); - } - - switch (addr) { - case 0x20: /* MDR1 */ - return s->mdr[0]; - case 0x24: /* MDR2 */ - return s->mdr[1]; - case 0x40: /* SCR */ - return s->scr; - case 0x44: /* SSR */ - return 0x0; - case 0x48: /* EBLR (OMAP2) */ - return s->eblr; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - return s->clksel; - case 0x50: /* MVR */ - return 0x30; - case 0x54: /* SYSC (OMAP2) */ - return s->syscontrol; - case 0x58: /* SYSS (OMAP2) */ - return 1; - case 0x5c: /* WER (OMAP2) */ - return s->wkup; - case 0x60: /* CFPS (OMAP2) */ - return s->cfps; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_uart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - if (size == 4) { - omap_badwidth_write8(opaque, addr, value); - return; - } - - switch (addr) { - case 0x20: /* MDR1 */ - s->mdr[0] = value & 0x7f; - break; - case 0x24: /* MDR2 */ - s->mdr[1] = value & 0xff; - break; - case 0x40: /* SCR */ - s->scr = value & 0xff; - break; - case 0x48: /* EBLR (OMAP2) */ - s->eblr = value & 0xff; - break; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - s->clksel = value & 1; - break; - case 0x44: /* SSR */ - case 0x50: /* MVR */ - case 0x58: /* SYSS (OMAP2) */ - OMAP_RO_REG(addr); - break; - case 0x54: /* SYSC (OMAP2) */ - s->syscontrol = value & 0x1d; - if (value & 2) - omap_uart_reset(s); - break; - case 0x5c: /* WER (OMAP2) */ - s->wkup = value & 0x7f; - break; - case 0x60: /* CFPS (OMAP2) */ - s->cfps = value & 0xff; - break; - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_uart_ops = { - .read = omap_uart_read, - .write = omap_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, - struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, - const char *label, Chardev *chr) -{ - hwaddr base = omap_l4_attach(ta, 0, NULL); - struct omap_uart_s *s = omap_uart_init(base, irq, - fclk, iclk, txdma, rxdma, label, chr); - - memory_region_init_io(&s->iomem, NULL, &omap_uart_ops, s, "omap.uart", 0x100); - - s->ta = ta; - - memory_region_add_subregion(sysmem, base + 0x20, &s->iomem); - - 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 1c9ca47820..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/acpi_aml_interface.h" -#include "hw/irq.h" -#include "hw/isa/isa.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,7 +532,7 @@ 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]), diff --git a/hw/char/pl011.c b/hw/char/pl011.c index c076813423..0fd1334fab 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,48 @@ #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_TXE (1 << 8) +#define CR_LBE (1 << 7) +#define CR_UARTEN (1 << 0) + +/* Integer Baud Rate Divider, UARTIBRD */ +#define IBRD_MASK 0xffff + +/* Fractional Baud Rate Divider, UARTFBRD */ +#define FBRD_MASK 0x3f + 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,32 +140,142 @@ static void pl011_update(PL011State *s) } } +static bool pl011_loopback_enabled(PL011State *s) +{ + return !!(s->cr & CR_LBE); +} + +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_rx_fifo(PL011State *s) +{ + s->read_count = 0; + s->read_pos = 0; + + /* Reset FIFO flags */ + s->flags &= ~PL011_FLAG_RXFF; + s->flags |= PL011_FLAG_RXFE; +} + +static inline void pl011_reset_tx_fifo(PL011State *s) +{ + /* Reset FIFO flags */ + s->flags &= ~PL011_FLAG_TXFF; + s->flags |= PL011_FLAG_TXFE; +} + +static void pl011_fifo_rx_put(void *opaque, uint32_t value) +{ + PL011State *s = (PL011State *)opaque; + int slot; + unsigned pipe_depth; + + 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_fifo_rx_put(value, s->read_count); + if (s->read_count == pipe_depth) { + trace_pl011_fifo_rx_full(); + s->flags |= PL011_FLAG_RXFF; + } + if (s->read_count == s->read_trigger) { + s->int_level |= INT_RX; + pl011_update(s); + } +} + +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_fifo_rx_put(s, value); +} + +static void pl011_write_txdata(PL011State *s, uint8_t data) +{ + if (!(s->cr & CR_UARTEN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "PL011 data written to disabled UART\n"); + } + if (!(s->cr & CR_TXE)) { + qemu_log_mask(LOG_GUEST_ERROR, + "PL011 data written to disabled TX UART\n"); + } + + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ + qemu_chr_fe_write_all(&s->chr, &data, 1); + pl011_loopback_tx(s, data); + s->int_level |= INT_TX; + pl011_update(s); +} + +static uint32_t pl011_read_rxdata(PL011State *s) +{ + uint32_t c; + + s->flags &= ~PL011_FLAG_RXFF; + c = s->read_fifo[s->read_pos]; + if (s->read_count > 0) { + s->read_count--; + 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 &= ~INT_RX; + } + trace_pl011_read_fifo(s->read_count); + s->rsr = c >> 8; + pl011_update(s); + qemu_chr_fe_accept_input(&s->chr); + return c; +} + static uint64_t pl011_read(void *opaque, hwaddr offset, unsigned size) { PL011State *s = (PL011State *)opaque; - uint32_t c; uint64_t r; switch (offset >> 2) { case 0: /* UARTDR */ - s->flags &= ~PL011_FLAG_RXFF; - c = s->read_fifo[s->read_pos]; - if (s->read_count > 0) { - s->read_count--; - if (++s->read_pos == 16) - s->read_pos = 0; - } - if (s->read_count == 0) { - s->flags |= PL011_FLAG_RXFE; - } - if (s->read_count == s->read_trigger - 1) - s->int_level &= ~ PL011_INT_RX; - trace_pl011_read_fifo(s->read_count); - s->rsr = c >> 8; - pl011_update(s); - qemu_chr_fe_accept_input(&s->chr); - r = c; + r = pl011_read_rxdata(s); break; case 1: /* UARTRSR */ r = s->rsr; @@ -154,7 +323,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 +334,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 @@ -191,23 +360,66 @@ static void pl011_trace_baudrate_change(const PL011State *s) s->ibrd, s->fbrd); } +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_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 */ - /* ??? Check if transmitter is enabled. */ ch = value; - /* 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_update(s); + pl011_write_txdata(s, ch); break; case 1: /* UARTRSR/UARTECR */ s->rsr = 0; @@ -215,34 +427,36 @@ 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 */ - s->ibrd = value; + s->ibrd = value & IBRD_MASK; pl011_trace_baudrate_change(s); break; case 10: /* UARTFBRD */ - s->fbrd = value; + s->fbrd = value & FBRD_MASK; pl011_trace_baudrate_change(s); 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_rx_fifo(s); + pl011_reset_tx_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,46 +487,30 @@ 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; } -static void pl011_put_fifo(void *opaque, uint32_t value) -{ - PL011State *s = (PL011State *)opaque; - int slot; - - slot = s->read_pos + s->read_count; - if (slot >= 16) - slot -= 16; - 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) { - trace_pl011_put_fifo_full(); - s->flags |= PL011_FLAG_RXFF; - } - if (s->read_count == s->read_trigger) { - s->int_level |= PL011_INT_RX; - pl011_update(s); - } -} - static void pl011_receive(void *opaque, const uint8_t *buf, int size) { - pl011_put_fifo(opaque, *buf); + /* + * 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_fifo_rx_put(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_fifo_rx_put(opaque, DR_BE); + } } static void pl011_clock_update(void *opaque, ClockEvent event) @@ -326,6 +524,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,18 +540,46 @@ 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; + } + + s->ibrd &= IBRD_MASK; + s->fbrd &= FBRD_MASK; + + return 0; +} + static const VMStateDescription vmstate_pl011 = { .name = "pl011", .version_id = 2, .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(readbuff, PL011State), + .post_load = pl011_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UNUSED(sizeof(uint32_t)), VMSTATE_UINT32(flags, PL011State), VMSTATE_UINT32(lcr, PL011State), VMSTATE_UINT32(rsr, PL011State), @@ -359,7 +587,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 +597,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 +624,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 +635,32 @@ 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_rx_fifo(s); + pl011_reset_tx_fifo(s); +} + static void pl011_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pl011_realize; + device_class_set_legacy_reset(dc, 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..7ce0408b0c 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), @@ -331,7 +331,7 @@ static void rsci_class_init(ObjectClass *klass, void *data) dc->realize = rsci_realize; dc->vmsd = &vmstate_rsci; - dc->reset = rsci_reset; + device_class_set_legacy_reset(dc, rsci_reset); device_class_set_props(dc, rsci_properties); } diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index 6577f0e640..0345088e8b 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -24,11 +24,14 @@ #include "qapi/error.h" #include "qemu/log.h" #include "hw/char/riscv_htif.h" -#include "hw/char/serial.h" #include "chardev/char.h" #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 +41,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 +95,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 +132,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 +167,84 @@ 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); + /* + * 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); + 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; + /* + * 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); resp = 0x100 | (uint8_t)payload; } else { qemu_log("HTIF device %d: unknown command\n", device); @@ -157,36 +255,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 +294,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 +326,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 +347,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..4fe1c4d289 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), @@ -346,7 +346,7 @@ static void console_class_init(ObjectClass *klass, void *data) SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); device_class_set_props(dc, console_properties); - dc->reset = console_reset; + device_class_set_legacy_reset(dc, console_reset); dc->vmsd = &vmstate_sclplmconsole; ec->init = console_init; ec->get_send_mask = send_mask; diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index c36b572222..e6d49e819e 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), @@ -262,7 +262,7 @@ static void console_class_init(ObjectClass *klass, void *data) SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); device_class_set_props(dc, console_properties); - dc->reset = console_reset; + device_class_set_legacy_reset(dc, console_reset); dc->vmsd = &vmstate_sclpconsole; ec->init = console_init; ec->get_send_mask = send_mask; diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 141a6cb168..b562ec9d37 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -29,6 +29,7 @@ #include "sysemu/sysemu.h" #include "hw/acpi/acpi_aml_interface.h" #include "hw/char/serial.h" +#include "hw/char/serial-isa.h" #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -106,7 +107,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() } @@ -184,3 +185,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-mm.c b/hw/char/serial-mm.c new file mode 100644 index 0000000000..2f67776b19 --- /dev/null +++ b/hw/char/serial-mm.c @@ -0,0 +1,157 @@ +/* + * QEMU 16550A UART emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix 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 "hw/char/serial-mm.h" +#include "exec/cpu-common.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "hw/qdev-properties.h" + +static uint64_t serial_mm_read(void *opaque, hwaddr addr, unsigned size) +{ + SerialMM *s = SERIAL_MM(opaque); + return serial_io_ops.read(&s->serial, addr >> s->regshift, 1); +} + +static void serial_mm_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + SerialMM *s = SERIAL_MM(opaque); + value &= 255; + serial_io_ops.write(&s->serial, addr >> s->regshift, value, 1); +} + +static const MemoryRegionOps serial_mm_ops[3] = { + [DEVICE_NATIVE_ENDIAN] = { + .read = serial_mm_read, + .write = serial_mm_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.max_access_size = 8, + .impl.max_access_size = 8, + }, + [DEVICE_LITTLE_ENDIAN] = { + .read = serial_mm_read, + .write = serial_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.max_access_size = 8, + .impl.max_access_size = 8, + }, + [DEVICE_BIG_ENDIAN] = { + .read = serial_mm_read, + .write = serial_mm_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid.max_access_size = 8, + .impl.max_access_size = 8, + }, +}; + +static void serial_mm_realize(DeviceState *dev, Error **errp) +{ + SerialMM *smm = SERIAL_MM(dev); + SerialState *s = &smm->serial; + + if (!qdev_realize(DEVICE(s), NULL, errp)) { + return; + } + + memory_region_init_io(&s->io, OBJECT(dev), + &serial_mm_ops[smm->endianness], smm, "serial", + 8 << smm->regshift); + sysbus_init_mmio(SYS_BUS_DEVICE(smm), &s->io); + sysbus_init_irq(SYS_BUS_DEVICE(smm), &smm->serial.irq); +} + +static const VMStateDescription vmstate_serial_mm = { + .name = "serial", + .version_id = 3, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(serial, SerialMM, 0, vmstate_serial, SerialState), + VMSTATE_END_OF_LIST() + } +}; + +SerialMM *serial_mm_init(MemoryRegion *address_space, + hwaddr base, int regshift, + qemu_irq irq, int baudbase, + Chardev *chr, enum device_endian end) +{ + SerialMM *smm = SERIAL_MM(qdev_new(TYPE_SERIAL_MM)); + MemoryRegion *mr; + + qdev_prop_set_uint8(DEVICE(smm), "regshift", regshift); + qdev_prop_set_uint32(DEVICE(smm), "baudbase", baudbase); + qdev_prop_set_chr(DEVICE(smm), "chardev", chr); + qdev_set_legacy_instance_id(DEVICE(smm), base, 2); + qdev_prop_set_uint8(DEVICE(smm), "endianness", end); + sysbus_realize_and_unref(SYS_BUS_DEVICE(smm), &error_fatal); + + sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, irq); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(smm), 0); + memory_region_add_subregion(address_space, base, mr); + + return smm; +} + +static void serial_mm_instance_init(Object *o) +{ + SerialMM *smm = SERIAL_MM(o); + + object_initialize_child(o, "serial", &smm->serial, TYPE_SERIAL); + + qdev_alias_all_properties(DEVICE(&smm->serial), o); +} + +static Property serial_mm_properties[] = { + /* + * Set the spacing between adjacent memory-mapped UART registers. + * Each register will be at (1 << regshift) bytes after the previous one. + */ + DEFINE_PROP_UINT8("regshift", SerialMM, regshift, 0), + DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN), + DEFINE_PROP_END_OF_LIST(), +}; + +static void serial_mm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, serial_mm_properties); + dc->realize = serial_mm_realize; + dc->vmsd = &vmstate_serial_mm; +} + +static const TypeInfo types[] = { + { + .name = TYPE_SERIAL_MM, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = serial_mm_class_init, + .instance_init = serial_mm_instance_init, + .instance_size = sizeof(SerialMM), + }, +}; + +DEFINE_TYPES(types) 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 41b5e61977..b50a8a1313 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, @@ -951,13 +951,6 @@ static void serial_unrealize(DeviceState *dev) qemu_unregister_reset(serial_reset, s); } -/* Change the main reference oscillator frequency. */ -void serial_set_frequency(SerialState *s, uint32_t frequency) -{ - s->baudbase = frequency; - serial_update_parameters(s); -} - const MemoryRegionOps serial_io_ops = { .read = serial_ioport_read, .write = serial_ioport_write, @@ -996,135 +989,9 @@ static const TypeInfo serial_info = { .class_init = serial_class_init, }; -/* Memory mapped interface */ -static uint64_t serial_mm_read(void *opaque, hwaddr addr, - unsigned size) -{ - SerialMM *s = SERIAL_MM(opaque); - return serial_ioport_read(&s->serial, addr >> s->regshift, 1); -} - -static void serial_mm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SerialMM *s = SERIAL_MM(opaque); - value &= 255; - serial_ioport_write(&s->serial, addr >> s->regshift, value, 1); -} - -static const MemoryRegionOps serial_mm_ops[3] = { - [DEVICE_NATIVE_ENDIAN] = { - .read = serial_mm_read, - .write = serial_mm_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid.max_access_size = 8, - .impl.max_access_size = 8, - }, - [DEVICE_LITTLE_ENDIAN] = { - .read = serial_mm_read, - .write = serial_mm_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid.max_access_size = 8, - .impl.max_access_size = 8, - }, - [DEVICE_BIG_ENDIAN] = { - .read = serial_mm_read, - .write = serial_mm_write, - .endianness = DEVICE_BIG_ENDIAN, - .valid.max_access_size = 8, - .impl.max_access_size = 8, - }, -}; - -static void serial_mm_realize(DeviceState *dev, Error **errp) -{ - SerialMM *smm = SERIAL_MM(dev); - SerialState *s = &smm->serial; - - if (!qdev_realize(DEVICE(s), NULL, errp)) { - return; - } - - memory_region_init_io(&s->io, OBJECT(dev), - &serial_mm_ops[smm->endianness], smm, "serial", - 8 << smm->regshift); - sysbus_init_mmio(SYS_BUS_DEVICE(smm), &s->io); - sysbus_init_irq(SYS_BUS_DEVICE(smm), &smm->serial.irq); -} - -static const VMStateDescription vmstate_serial_mm = { - .name = "serial", - .version_id = 3, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(serial, SerialMM, 0, vmstate_serial, SerialState), - VMSTATE_END_OF_LIST() - } -}; - -SerialMM *serial_mm_init(MemoryRegion *address_space, - hwaddr base, int regshift, - qemu_irq irq, int baudbase, - Chardev *chr, enum device_endian end) -{ - SerialMM *smm = SERIAL_MM(qdev_new(TYPE_SERIAL_MM)); - MemoryRegion *mr; - - qdev_prop_set_uint8(DEVICE(smm), "regshift", regshift); - qdev_prop_set_uint32(DEVICE(smm), "baudbase", baudbase); - qdev_prop_set_chr(DEVICE(smm), "chardev", chr); - qdev_set_legacy_instance_id(DEVICE(smm), base, 2); - qdev_prop_set_uint8(DEVICE(smm), "endianness", end); - sysbus_realize_and_unref(SYS_BUS_DEVICE(smm), &error_fatal); - - sysbus_connect_irq(SYS_BUS_DEVICE(smm), 0, irq); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(smm), 0); - memory_region_add_subregion(address_space, base, mr); - - return smm; -} - -static void serial_mm_instance_init(Object *o) -{ - SerialMM *smm = SERIAL_MM(o); - - object_initialize_child(o, "serial", &smm->serial, TYPE_SERIAL); - - qdev_alias_all_properties(DEVICE(&smm->serial), o); -} - -static Property serial_mm_properties[] = { - /* - * Set the spacing between adjacent memory-mapped UART registers. - * Each register will be at (1 << regshift) bytes after the - * previous one. - */ - DEFINE_PROP_UINT8("regshift", SerialMM, regshift, 0), - DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN), - DEFINE_PROP_END_OF_LIST(), -}; - -static void serial_mm_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - device_class_set_props(dc, serial_mm_properties); - dc->realize = serial_mm_realize; - dc->vmsd = &vmstate_serial_mm; -} - -static const TypeInfo serial_mm_info = { - .name = TYPE_SERIAL_MM, - .parent = TYPE_SYS_BUS_DEVICE, - .class_init = serial_mm_class_init, - .instance_init = serial_mm_instance_init, - .instance_size = sizeof(SerialMM), -}; - static void serial_register_types(void) { type_register_static(&serial_info); - type_register_static(&serial_mm_info); } type_init(serial_register_types) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 355886ee3a..429b2562aa 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -459,7 +459,7 @@ static void sh_serial_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, sh_serial_properties); dc->realize = sh_serial_realize; - dc->reset = sh_serial_reset; + device_class_set_legacy_reset(dc, sh_serial_reset); /* Reason: part of SuperH CPU/SoC, needs to be wired up */ dc->user_creatable = false; } diff --git a/hw/char/shakti_uart.c b/hw/char/shakti_uart.c index 98b142c7df..4a71953c9a 100644 --- a/hw/char/shakti_uart.c +++ b/hw/char/shakti_uart.c @@ -165,7 +165,7 @@ static Property shakti_uart_properties[] = { static void shakti_uart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = shakti_uart_reset; + device_class_set_legacy_reset(dc, shakti_uart_reset); dc->realize = shakti_uart_realize; device_class_set_props(dc, shakti_uart_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 1c75f792b3..5ae2a29ed6 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -26,6 +26,8 @@ #include "hw/char/sifive_uart.h" #include "hw/qdev-properties-system.h" +#define TX_INTERRUPT_TRIGGER_DELAY_NS 100 + /* * Not yet implemented: * @@ -64,6 +66,72 @@ static void sifive_uart_update_irq(SiFiveUARTState *s) } } +static gboolean sifive_uart_xmit(void *do_not_use, GIOCondition cond, + void *opaque) +{ + SiFiveUARTState *s = opaque; + int ret; + const uint8_t *characters; + uint32_t numptr = 0; + + /* instant drain the fifo when there's no back-end */ + if (!qemu_chr_fe_backend_connected(&s->chr)) { + fifo8_reset(&s->tx_fifo); + return G_SOURCE_REMOVE; + } + + if (fifo8_is_empty(&s->tx_fifo)) { + return G_SOURCE_REMOVE; + } + + /* Don't pop the FIFO in case the write fails */ + characters = fifo8_peek_bufptr(&s->tx_fifo, + fifo8_num_used(&s->tx_fifo), &numptr); + ret = qemu_chr_fe_write(&s->chr, characters, numptr); + + if (ret >= 0) { + /* We wrote the data, actually pop the fifo */ + fifo8_pop_bufptr(&s->tx_fifo, ret, NULL); + } + + if (!fifo8_is_empty(&s->tx_fifo)) { + guint r = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + sifive_uart_xmit, s); + if (!r) { + fifo8_reset(&s->tx_fifo); + return G_SOURCE_REMOVE; + } + } + + /* Clear the TX Full bit */ + if (!fifo8_is_full(&s->tx_fifo)) { + s->txfifo &= ~SIFIVE_UART_TXFIFO_FULL; + } + + sifive_uart_update_irq(s); + return G_SOURCE_REMOVE; +} + +static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf, + int size) +{ + uint64_t current_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + if (size > fifo8_num_free(&s->tx_fifo)) { + size = fifo8_num_free(&s->tx_fifo); + qemu_log_mask(LOG_GUEST_ERROR, "sifive_uart: TX FIFO overflow"); + } + + fifo8_push_all(&s->tx_fifo, buf, size); + + if (fifo8_is_full(&s->tx_fifo)) { + s->txfifo |= SIFIVE_UART_TXFIFO_FULL; + } + + timer_mod(s->fifo_trigger_handle, current_time + + TX_INTERRUPT_TRIGGER_DELAY_NS); +} + static uint64_t sifive_uart_read(void *opaque, hwaddr addr, unsigned int size) { @@ -82,7 +150,7 @@ sifive_uart_read(void *opaque, hwaddr addr, unsigned int size) return 0x80000000; case SIFIVE_UART_TXFIFO: - return 0; /* Should check tx fifo */ + return s->txfifo; case SIFIVE_UART_IE: return s->ie; case SIFIVE_UART_IP: @@ -106,12 +174,11 @@ sifive_uart_write(void *opaque, hwaddr addr, { SiFiveUARTState *s = opaque; uint32_t value = val64; - unsigned char ch = value; + uint8_t ch = value; switch (addr) { case SIFIVE_UART_TXFIFO: - qemu_chr_fe_write(&s->chr, &ch, 1); - sifive_uart_update_irq(s); + sifive_uart_write_tx_fifo(s, &ch, 1); return; case SIFIVE_UART_IE: s->ie = val64; @@ -131,6 +198,13 @@ sifive_uart_write(void *opaque, hwaddr addr, __func__, (int)addr, (int)value); } +static void fifo_trigger_update(void *opaque) +{ + SiFiveUARTState *s = opaque; + + sifive_uart_xmit(NULL, G_IO_OUT, s); +} + static const MemoryRegionOps sifive_uart_ops = { .read = sifive_uart_read, .write = sifive_uart_write, @@ -197,6 +271,9 @@ static void sifive_uart_realize(DeviceState *dev, Error **errp) { SiFiveUARTState *s = SIFIVE_UART(dev); + s->fifo_trigger_handle = timer_new_ns(QEMU_CLOCK_VIRTUAL, + fifo_trigger_update, s); + qemu_chr_fe_set_handlers(&s->chr, sifive_uart_can_rx, sifive_uart_rx, sifive_uart_event, sifive_uart_be_change, s, NULL, true); @@ -206,15 +283,21 @@ static void sifive_uart_realize(DeviceState *dev, Error **errp) static void sifive_uart_reset_enter(Object *obj, ResetType type) { SiFiveUARTState *s = SIFIVE_UART(obj); + + s->txfifo = 0; s->ie = 0; s->ip = 0; s->txctrl = 0; s->rxctrl = 0; s->div = 0; + s->rx_fifo_len = 0; + + memset(s->rx_fifo, 0, SIFIVE_UART_RX_FIFO_SIZE); + fifo8_create(&s->tx_fifo, SIFIVE_UART_TX_FIFO_SIZE); } -static void sifive_uart_reset_hold(Object *obj) +static void sifive_uart_reset_hold(Object *obj, ResetType type) { SiFiveUARTState *s = SIFIVE_UART(obj); qemu_irq_lower(s->irq); @@ -222,9 +305,9 @@ static void sifive_uart_reset_hold(Object *obj) static const VMStateDescription vmstate_sifive_uart = { .name = TYPE_SIFIVE_UART, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT8_ARRAY(rx_fifo, SiFiveUARTState, SIFIVE_UART_RX_FIFO_SIZE), VMSTATE_UINT8(rx_fifo_len, SiFiveUARTState), @@ -233,6 +316,9 @@ static const VMStateDescription vmstate_sifive_uart = { VMSTATE_UINT32(txctrl, SiFiveUARTState), VMSTATE_UINT32(rxctrl, SiFiveUARTState), VMSTATE_UINT32(div, SiFiveUARTState), + VMSTATE_UINT32(txfifo, SiFiveUARTState), + VMSTATE_FIFO8(tx_fifo, SiFiveUARTState), + VMSTATE_TIMER_PTR(fifo_trigger_handle, SiFiveUARTState), VMSTATE_END_OF_LIST() }, }; @@ -274,7 +360,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 +369,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..17b5b1f15f 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; @@ -223,7 +228,7 @@ static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f2xx_usart_reset; + device_class_set_legacy_reset(dc, stm32f2xx_usart_reset); device_class_set_props(dc, stm32f2xx_usart_properties); dc->realize = stm32f2xx_usart_realize; } diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c new file mode 100644 index 0000000000..3cf200c080 --- /dev/null +++ b/hw/char/stm32l4x5_usart.c @@ -0,0 +1,654 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * 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 STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "chardev/char-fe.h" +#include "chardev/char-serial.h" +#include "migration/vmstate.h" +#include "hw/char/stm32l4x5_usart.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" + + +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) /* Word length (part 2, see M0) */ + FIELD(CR1, EOBIE, 27, 1) /* End of Block interrupt enable */ + FIELD(CR1, RTOIE, 26, 1) /* Receiver timeout interrupt enable */ + FIELD(CR1, DEAT, 21, 5) /* Driver Enable assertion time */ + FIELD(CR1, DEDT, 16, 5) /* Driver Enable de-assertion time */ + FIELD(CR1, OVER8, 15, 1) /* Oversampling mode */ + FIELD(CR1, CMIE, 14, 1) /* Character match interrupt enable */ + FIELD(CR1, MME, 13, 1) /* Mute mode enable */ + FIELD(CR1, M0, 12, 1) /* Word length (part 1, see M1) */ + FIELD(CR1, WAKE, 11, 1) /* Receiver wakeup method */ + FIELD(CR1, PCE, 10, 1) /* Parity control enable */ + FIELD(CR1, PS, 9, 1) /* Parity selection */ + FIELD(CR1, PEIE, 8, 1) /* PE interrupt enable */ + FIELD(CR1, TXEIE, 7, 1) /* TXE interrupt enable */ + FIELD(CR1, TCIE, 6, 1) /* Transmission complete interrupt enable */ + FIELD(CR1, RXNEIE, 5, 1) /* RXNE interrupt enable */ + FIELD(CR1, IDLEIE, 4, 1) /* IDLE interrupt enable */ + FIELD(CR1, TE, 3, 1) /* Transmitter enable */ + FIELD(CR1, RE, 2, 1) /* Receiver enable */ + FIELD(CR1, UESM, 1, 1) /* USART enable in Stop mode */ + FIELD(CR1, UE, 0, 1) /* USART enable */ +REG32(CR2, 0x04) + FIELD(CR2, ADD_1, 28, 4) /* ADD[7:4] */ + FIELD(CR2, ADD_0, 24, 4) /* ADD[3:0] */ + FIELD(CR2, RTOEN, 23, 1) /* Receiver timeout enable */ + FIELD(CR2, ABRMOD, 21, 2) /* Auto baud rate mode */ + FIELD(CR2, ABREN, 20, 1) /* Auto baud rate enable */ + FIELD(CR2, MSBFIRST, 19, 1) /* Most significant bit first */ + FIELD(CR2, DATAINV, 18, 1) /* Binary data inversion */ + FIELD(CR2, TXINV, 17, 1) /* TX pin active level inversion */ + FIELD(CR2, RXINV, 16, 1) /* RX pin active level inversion */ + FIELD(CR2, SWAP, 15, 1) /* Swap RX/TX pins */ + FIELD(CR2, LINEN, 14, 1) /* LIN mode enable */ + FIELD(CR2, STOP, 12, 2) /* STOP bits */ + FIELD(CR2, CLKEN, 11, 1) /* Clock enable */ + FIELD(CR2, CPOL, 10, 1) /* Clock polarity */ + FIELD(CR2, CPHA, 9, 1) /* Clock phase */ + FIELD(CR2, LBCL, 8, 1) /* Last bit clock pulse */ + FIELD(CR2, LBDIE, 6, 1) /* LIN break detection interrupt enable */ + FIELD(CR2, LBDL, 5, 1) /* LIN break detection length */ + FIELD(CR2, ADDM7, 4, 1) /* 7-bit / 4-bit Address Detection */ + +REG32(CR3, 0x08) + /* TCBGTIE only on STM32L496xx/4A6xx devices */ + FIELD(CR3, UCESM, 23, 1) /* USART Clock Enable in Stop Mode */ + FIELD(CR3, WUFIE, 22, 1) /* Wakeup from Stop mode interrupt enable */ + FIELD(CR3, WUS, 20, 2) /* Wakeup from Stop mode interrupt flag selection */ + FIELD(CR3, SCARCNT, 17, 3) /* Smartcard auto-retry count */ + FIELD(CR3, DEP, 15, 1) /* Driver enable polarity selection */ + FIELD(CR3, DEM, 14, 1) /* Driver enable mode */ + FIELD(CR3, DDRE, 13, 1) /* DMA Disable on Reception Error */ + FIELD(CR3, OVRDIS, 12, 1) /* Overrun Disable */ + FIELD(CR3, ONEBIT, 11, 1) /* One sample bit method enable */ + FIELD(CR3, CTSIE, 10, 1) /* CTS interrupt enable */ + FIELD(CR3, CTSE, 9, 1) /* CTS enable */ + FIELD(CR3, RTSE, 8, 1) /* RTS enable */ + FIELD(CR3, DMAT, 7, 1) /* DMA enable transmitter */ + FIELD(CR3, DMAR, 6, 1) /* DMA enable receiver */ + FIELD(CR3, SCEN, 5, 1) /* Smartcard mode enable */ + FIELD(CR3, NACK, 4, 1) /* Smartcard NACK enable */ + FIELD(CR3, HDSEL, 3, 1) /* Half-duplex selection */ + FIELD(CR3, IRLP, 2, 1) /* IrDA low-power */ + FIELD(CR3, IREN, 1, 1) /* IrDA mode enable */ + FIELD(CR3, EIE, 0, 1) /* Error interrupt enable */ +REG32(BRR, 0x0C) + FIELD(BRR, BRR, 0, 16) +REG32(GTPR, 0x10) + FIELD(GTPR, GT, 8, 8) /* Guard time value */ + FIELD(GTPR, PSC, 0, 8) /* Prescaler value */ +REG32(RTOR, 0x14) + FIELD(RTOR, BLEN, 24, 8) /* Block Length */ + FIELD(RTOR, RTO, 0, 24) /* Receiver timeout value */ +REG32(RQR, 0x18) + FIELD(RQR, TXFRQ, 4, 1) /* Transmit data flush request */ + FIELD(RQR, RXFRQ, 3, 1) /* Receive data flush request */ + FIELD(RQR, MMRQ, 2, 1) /* Mute mode request */ + FIELD(RQR, SBKRQ, 1, 1) /* Send break request */ + FIELD(RQR, ABBRRQ, 0, 1) /* Auto baud rate request */ +REG32(ISR, 0x1C) + /* TCBGT only for STM32L475xx/476xx/486xx devices */ + FIELD(ISR, REACK, 22, 1) /* Receive enable acknowledge flag */ + FIELD(ISR, TEACK, 21, 1) /* Transmit enable acknowledge flag */ + FIELD(ISR, WUF, 20, 1) /* Wakeup from Stop mode flag */ + FIELD(ISR, RWU, 19, 1) /* Receiver wakeup from Mute mode */ + FIELD(ISR, SBKF, 18, 1) /* Send break flag */ + FIELD(ISR, CMF, 17, 1) /* Character match flag */ + FIELD(ISR, BUSY, 16, 1) /* Busy flag */ + FIELD(ISR, ABRF, 15, 1) /* Auto Baud rate flag */ + FIELD(ISR, ABRE, 14, 1) /* Auto Baud rate error */ + FIELD(ISR, EOBF, 12, 1) /* End of block flag */ + FIELD(ISR, RTOF, 11, 1) /* Receiver timeout */ + FIELD(ISR, CTS, 10, 1) /* CTS flag */ + FIELD(ISR, CTSIF, 9, 1) /* CTS interrupt flag */ + FIELD(ISR, LBDF, 8, 1) /* LIN break detection flag */ + FIELD(ISR, TXE, 7, 1) /* Transmit data register empty */ + FIELD(ISR, TC, 6, 1) /* Transmission complete */ + FIELD(ISR, RXNE, 5, 1) /* Read data register not empty */ + FIELD(ISR, IDLE, 4, 1) /* Idle line detected */ + FIELD(ISR, ORE, 3, 1) /* Overrun error */ + FIELD(ISR, NF, 2, 1) /* START bit Noise detection flag */ + FIELD(ISR, FE, 1, 1) /* Framing Error */ + FIELD(ISR, PE, 0, 1) /* Parity Error */ +REG32(ICR, 0x20) + FIELD(ICR, WUCF, 20, 1) /* Wakeup from Stop mode clear flag */ + FIELD(ICR, CMCF, 17, 1) /* Character match clear flag */ + FIELD(ICR, EOBCF, 12, 1) /* End of block clear flag */ + FIELD(ICR, RTOCF, 11, 1) /* Receiver timeout clear flag */ + FIELD(ICR, CTSCF, 9, 1) /* CTS clear flag */ + FIELD(ICR, LBDCF, 8, 1) /* LIN break detection clear flag */ + /* TCBGTCF only on STM32L496xx/4A6xx devices */ + FIELD(ICR, TCCF, 6, 1) /* Transmission complete clear flag */ + FIELD(ICR, IDLECF, 4, 1) /* Idle line detected clear flag */ + FIELD(ICR, ORECF, 3, 1) /* Overrun error clear flag */ + FIELD(ICR, NCF, 2, 1) /* Noise detected clear flag */ + FIELD(ICR, FECF, 1, 1) /* Framing error clear flag */ + FIELD(ICR, PECF, 0, 1) /* Parity error clear flag */ +REG32(RDR, 0x24) + FIELD(RDR, RDR, 0, 9) +REG32(TDR, 0x28) + FIELD(TDR, TDR, 0, 9) + +static void stm32l4x5_update_isr(Stm32l4x5UsartBaseState *s) +{ + if (s->cr1 & R_CR1_TE_MASK) { + s->isr |= R_ISR_TEACK_MASK; + } else { + s->isr &= ~R_ISR_TEACK_MASK; + } + + if (s->cr1 & R_CR1_RE_MASK) { + s->isr |= R_ISR_REACK_MASK; + } else { + s->isr &= ~R_ISR_REACK_MASK; + } +} + +static void stm32l4x5_update_irq(Stm32l4x5UsartBaseState *s) +{ + if (((s->isr & R_ISR_WUF_MASK) && (s->cr3 & R_CR3_WUFIE_MASK)) || + ((s->isr & R_ISR_CMF_MASK) && (s->cr1 & R_CR1_CMIE_MASK)) || + ((s->isr & R_ISR_ABRF_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) || + ((s->isr & R_ISR_EOBF_MASK) && (s->cr1 & R_CR1_EOBIE_MASK)) || + ((s->isr & R_ISR_RTOF_MASK) && (s->cr1 & R_CR1_RTOIE_MASK)) || + ((s->isr & R_ISR_CTSIF_MASK) && (s->cr3 & R_CR3_CTSIE_MASK)) || + ((s->isr & R_ISR_LBDF_MASK) && (s->cr2 & R_CR2_LBDIE_MASK)) || + ((s->isr & R_ISR_TXE_MASK) && (s->cr1 & R_CR1_TXEIE_MASK)) || + ((s->isr & R_ISR_TC_MASK) && (s->cr1 & R_CR1_TCIE_MASK)) || + ((s->isr & R_ISR_RXNE_MASK) && (s->cr1 & R_CR1_RXNEIE_MASK)) || + ((s->isr & R_ISR_IDLE_MASK) && (s->cr1 & R_CR1_IDLEIE_MASK)) || + ((s->isr & R_ISR_ORE_MASK) && + ((s->cr1 & R_CR1_RXNEIE_MASK) || (s->cr3 & R_CR3_EIE_MASK))) || + /* TODO: Handle NF ? */ + ((s->isr & R_ISR_FE_MASK) && (s->cr3 & R_CR3_EIE_MASK)) || + ((s->isr & R_ISR_PE_MASK) && (s->cr1 & R_CR1_PEIE_MASK))) { + qemu_irq_raise(s->irq); + trace_stm32l4x5_usart_irq_raised(s->isr); + } else { + qemu_irq_lower(s->irq); + trace_stm32l4x5_usart_irq_lowered(); + } +} + +static int stm32l4x5_usart_base_can_receive(void *opaque) +{ + Stm32l4x5UsartBaseState *s = opaque; + + if (!(s->isr & R_ISR_RXNE_MASK)) { + return 1; + } + + return 0; +} + +static void stm32l4x5_usart_base_receive(void *opaque, const uint8_t *buf, + int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + + if (!((s->cr1 & R_CR1_UE_MASK) && (s->cr1 & R_CR1_RE_MASK))) { + trace_stm32l4x5_usart_receiver_not_enabled( + FIELD_EX32(s->cr1, CR1, UE), FIELD_EX32(s->cr1, CR1, RE)); + return; + } + + /* Check if overrun detection is enabled and if there is an overrun */ + if (!(s->cr3 & R_CR3_OVRDIS_MASK) && (s->isr & R_ISR_RXNE_MASK)) { + /* + * A character has been received while + * the previous has not been read = Overrun. + */ + s->isr |= R_ISR_ORE_MASK; + trace_stm32l4x5_usart_overrun_detected(s->rdr, *buf); + } else { + /* No overrun */ + s->rdr = *buf; + s->isr |= R_ISR_RXNE_MASK; + trace_stm32l4x5_usart_rx(s->rdr); + } + + stm32l4x5_update_irq(s); +} + +/* + * Try to send tx data, and arrange to be called back later if + * we can't (ie the char backend is busy/blocking). + */ +static gboolean usart_transmit(void *do_not_use, GIOCondition cond, + void *opaque) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(opaque); + int ret; + /* TODO: Handle 9 bits transmission */ + uint8_t ch = s->tdr; + + s->watch_tag = 0; + + if (!(s->cr1 & R_CR1_TE_MASK) || (s->isr & R_ISR_TXE_MASK)) { + return G_SOURCE_REMOVE; + } + + ret = qemu_chr_fe_write(&s->chr, &ch, 1); + if (ret <= 0) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + usart_transmit, s); + if (!s->watch_tag) { + /* + * Most common reason to be here is "no chardev backend": + * just insta-drain the buffer, so the serial output + * goes into a void, rather than blocking the guest. + */ + goto buffer_drained; + } + /* Transmit pending */ + trace_stm32l4x5_usart_tx_pending(); + return G_SOURCE_REMOVE; + } + +buffer_drained: + /* Character successfully sent */ + trace_stm32l4x5_usart_tx(ch); + s->isr |= R_ISR_TC_MASK | R_ISR_TXE_MASK; + stm32l4x5_update_irq(s); + return G_SOURCE_REMOVE; +} + +static void usart_cancel_transmit(Stm32l4x5UsartBaseState *s) +{ + if (s->watch_tag) { + g_source_remove(s->watch_tag); + s->watch_tag = 0; + } +} + +static void stm32l4x5_update_params(Stm32l4x5UsartBaseState *s) +{ + int speed, parity, data_bits, stop_bits; + uint32_t value, usart_div; + QEMUSerialSetParams ssp; + + /* Select the parity type */ + if (s->cr1 & R_CR1_PCE_MASK) { + if (s->cr1 & R_CR1_PS_MASK) { + parity = 'O'; + } else { + parity = 'E'; + } + } else { + parity = 'N'; + } + + /* Select the number of stop bits */ + switch (FIELD_EX32(s->cr2, CR2, STOP)) { + case 0: + stop_bits = 1; + break; + case 2: + stop_bits = 2; + break; + default: + qemu_log_mask(LOG_UNIMP, + "UNIMPLEMENTED: fractionnal stop bits; CR2[13:12] = %u", + FIELD_EX32(s->cr2, CR2, STOP)); + return; + } + + /* Select the length of the word */ + switch ((FIELD_EX32(s->cr1, CR1, M1) << 1) | FIELD_EX32(s->cr1, CR1, M0)) { + case 0: + data_bits = 8; + break; + case 1: + data_bits = 9; + break; + case 2: + data_bits = 7; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: invalid word length, CR1.M = 0b11"); + return; + } + + /* Select the baud rate */ + value = FIELD_EX32(s->brr, BRR, BRR); + if (value < 16) { + qemu_log_mask(LOG_GUEST_ERROR, + "UNDEFINED: BRR less than 16: %u", value); + return; + } + + if (FIELD_EX32(s->cr1, CR1, OVER8) == 0) { + /* + * Oversampling by 16 + * BRR = USARTDIV + */ + usart_div = value; + } else { + /* + * Oversampling by 8 + * - BRR[2:0] = USARTDIV[3:0] shifted 1 bit to the right. + * - BRR[3] must be kept cleared. + * - BRR[15:4] = USARTDIV[15:4] + * - The frequency is multiplied by 2 + */ + usart_div = ((value & 0xFFF0) | ((value & 0x0007) << 1)) / 2; + } + + speed = clock_get_hz(s->clk) / usart_div; + + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + + qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + + trace_stm32l4x5_usart_update_params(speed, parity, data_bits, stop_bits); +} + +static void stm32l4x5_usart_base_reset_hold(Object *obj, ResetType type) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); + + s->cr1 = 0x00000000; + s->cr2 = 0x00000000; + s->cr3 = 0x00000000; + s->brr = 0x00000000; + s->gtpr = 0x00000000; + s->rtor = 0x00000000; + s->isr = 0x020000C0; + s->rdr = 0x00000000; + s->tdr = 0x00000000; + + usart_cancel_transmit(s); + stm32l4x5_update_irq(s); +} + +static void usart_update_rqr(Stm32l4x5UsartBaseState *s, uint32_t value) +{ + /* TXFRQ */ + /* Reset RXNE flag */ + if (value & R_RQR_RXFRQ_MASK) { + s->isr &= ~R_ISR_RXNE_MASK; + } + /* MMRQ */ + /* SBKRQ */ + /* ABRRQ */ + stm32l4x5_update_irq(s); +} + +static uint64_t stm32l4x5_usart_base_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + uint64_t retvalue = 0; + + switch (addr) { + case A_CR1: + retvalue = s->cr1; + break; + case A_CR2: + retvalue = s->cr2; + break; + case A_CR3: + retvalue = s->cr3; + break; + case A_BRR: + retvalue = FIELD_EX32(s->brr, BRR, BRR); + break; + case A_GTPR: + retvalue = s->gtpr; + break; + case A_RTOR: + retvalue = s->rtor; + break; + case A_RQR: + /* RQR is a write only register */ + retvalue = 0x00000000; + break; + case A_ISR: + retvalue = s->isr; + break; + case A_ICR: + /* ICR is a clear register */ + retvalue = 0x00000000; + break; + case A_RDR: + retvalue = FIELD_EX32(s->rdr, RDR, RDR); + /* Reset RXNE flag */ + s->isr &= ~R_ISR_RXNE_MASK; + stm32l4x5_update_irq(s); + break; + case A_TDR: + retvalue = FIELD_EX32(s->tdr, TDR, TDR); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } + + trace_stm32l4x5_usart_read(addr, retvalue); + + return retvalue; +} + +static void stm32l4x5_usart_base_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Stm32l4x5UsartBaseState *s = opaque; + const uint32_t value = val64; + + trace_stm32l4x5_usart_write(addr, value); + + switch (addr) { + case A_CR1: + s->cr1 = value; + stm32l4x5_update_params(s); + stm32l4x5_update_isr(s); + stm32l4x5_update_irq(s); + return; + case A_CR2: + s->cr2 = value; + stm32l4x5_update_params(s); + return; + case A_CR3: + s->cr3 = value; + return; + case A_BRR: + s->brr = value; + stm32l4x5_update_params(s); + return; + case A_GTPR: + s->gtpr = value; + return; + case A_RTOR: + s->rtor = value; + return; + case A_RQR: + usart_update_rqr(s, value); + return; + case A_ISR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: ISR is read only !\n", __func__); + return; + case A_ICR: + /* Clear the status flags */ + s->isr &= ~value; + stm32l4x5_update_irq(s); + return; + case A_RDR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RDR is read only !\n", __func__); + return; + case A_TDR: + s->tdr = value; + s->isr &= ~R_ISR_TXE_MASK; + usart_transmit(NULL, G_IO_OUT, s); + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + } +} + +static const MemoryRegionOps stm32l4x5_usart_base_ops = { + .read = stm32l4x5_usart_base_read, + .write = stm32l4x5_usart_base_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 Property stm32l4x5_usart_base_properties[] = { + DEFINE_PROP_CHR("chardev", Stm32l4x5UsartBaseState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stm32l4x5_usart_base_init(Object *obj) +{ + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &stm32l4x5_usart_base_ops, s, + TYPE_STM32L4X5_USART_BASE, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); +} + +static int stm32l4x5_usart_base_post_load(void *opaque, int version_id) +{ + Stm32l4x5UsartBaseState *s = (Stm32l4x5UsartBaseState *)opaque; + + stm32l4x5_update_params(s); + return 0; +} + +static const VMStateDescription vmstate_stm32l4x5_usart_base = { + .name = TYPE_STM32L4X5_USART_BASE, + .version_id = 1, + .minimum_version_id = 1, + .post_load = stm32l4x5_usart_base_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr1, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr2, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(cr3, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(brr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(gtpr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rtor, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(isr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(rdr, Stm32l4x5UsartBaseState), + VMSTATE_UINT32(tdr, Stm32l4x5UsartBaseState), + VMSTATE_CLOCK(clk, Stm32l4x5UsartBaseState), + VMSTATE_END_OF_LIST() + } +}; + + +static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + Stm32l4x5UsartBaseState *s = STM32L4X5_USART_BASE(dev); + if (!clock_has_source(s->clk)) { + error_setg(errp, "USART clock must be wired up by SoC code"); + return; + } + + qemu_chr_fe_set_handlers(&s->chr, stm32l4x5_usart_base_can_receive, + stm32l4x5_usart_base_receive, NULL, NULL, + s, NULL, true); +} + +static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = stm32l4x5_usart_base_reset_hold; + device_class_set_props(dc, stm32l4x5_usart_base_properties); + dc->realize = stm32l4x5_usart_base_realize; + dc->vmsd = &vmstate_stm32l4x5_usart_base; +} + +static void stm32l4x5_usart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_USART; +} + +static void stm32l4x5_uart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_UART; +} + +static void stm32l4x5_lpuart_class_init(ObjectClass *oc, void *data) +{ + Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); + + subc->type = STM32L4x5_LPUART; +} + +static const TypeInfo stm32l4x5_usart_types[] = { + { + .name = TYPE_STM32L4X5_USART_BASE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Stm32l4x5UsartBaseState), + .instance_init = stm32l4x5_usart_base_init, + .class_size = sizeof(Stm32l4x5UsartBaseClass), + .class_init = stm32l4x5_usart_base_class_init, + .abstract = true, + }, { + .name = TYPE_STM32L4X5_USART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_usart_class_init, + }, { + .name = TYPE_STM32L4X5_UART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_uart_class_init, + }, { + .name = TYPE_STM32L4X5_LPUART, + .parent = TYPE_STM32L4X5_USART_BASE, + .class_init = stm32l4x5_lpuart_class_init, + } +}; + +DEFINE_TYPES(stm32l4x5_usart_types) diff --git a/hw/char/trace-events b/hw/char/trace-events index 2ecb36232e..59e1f734a7 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -54,12 +54,12 @@ 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" +pl011_fifo_rx_put(uint32_t c, int read_count) "new char 0x%02x read_count now %d" +pl011_fifo_rx_full(void) "RX FIFO now full, RXFF set" pl011_baudrate_change(unsigned int baudrate, uint64_t clock, uint32_t ibrd, uint32_t fbrd) "new baudrate %u (clk: %" PRIu64 "hz, ibrd: %" PRIu32 ", fbrd: %" PRIu32 ")" # cmsdk-apb-uart.c @@ -105,3 +105,23 @@ 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 + +# stm32l4x5_usart.c +stm32l4x5_usart_read(uint64_t addr, uint32_t data) "USART: Read <0x%" PRIx64 "> -> 0x%" PRIx32 "" +stm32l4x5_usart_write(uint64_t addr, uint32_t data) "USART: Write <0x%" PRIx64 "> <- 0x%" PRIx32 "" +stm32l4x5_usart_rx(uint8_t c) "USART: got character 0x%x from backend" +stm32l4x5_usart_tx(uint8_t c) "USART: character 0x%x sent to backend" +stm32l4x5_usart_tx_pending(void) "USART: character send to backend pending" +stm32l4x5_usart_irq_raised(uint32_t reg) "USART: IRQ raised: 0x%08"PRIx32 +stm32l4x5_usart_irq_lowered(void) "USART: IRQ lowered" +stm32l4x5_usart_overrun_detected(uint8_t current, uint8_t received) "USART: Overrun detected, RDR='0x%x', received 0x%x" +stm32l4x5_usart_receiver_not_enabled(uint8_t ue_bit, uint8_t re_bit) "USART: Receiver not enabled, UE=0x%x, RE=0x%x" +stm32l4x5_usart_update_params(int speed, uint8_t parity, int data, int stop) "USART: speed: %d, parity: %c, data bits: %d, stop bits: %d" + +# 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..f325084f8b 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; @@ -236,7 +234,7 @@ static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xilinx_uartlite_reset; + device_class_set_legacy_reset(dc, xilinx_uartlite_reset); dc->realize = xilinx_uartlite_realize; device_class_set_props(dc, xilinx_uartlite_properties); } diff --git a/hw/core/Kconfig b/hw/core/Kconfig index 9397503656..d1bdf765ee 100644 --- a/hw/core/Kconfig +++ b/hw/core/Kconfig @@ -4,8 +4,14 @@ config EMPTY_SLOT config PTIMER bool +config DEVICE_TREE + bool + # fail the build if libfdt not found + depends on FDT + config FITLOADER bool + depends on DEVICE_TREE config GENERIC_LOADER bool @@ -14,16 +20,21 @@ config GENERIC_LOADER config GUEST_LOADER bool default y - depends on TCG + depends on TCG && DEVICE_TREE config OR_IRQ bool config PLATFORM_BUS bool + depends on DEVICE_TREE config REGISTER bool config SPLIT_IRQ bool + +config EIF + bool + depends on LIBCBOR && GNUTLS 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 d82e44cd1a..cbe7b1bc46 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -13,6 +13,8 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qapi/visitor.h" +#include "sysemu/qtest.h" #include "hw/clock.h" #include "trace.h" @@ -108,7 +110,6 @@ static void clock_propagate_period(Clock *clk, bool call_callbacks) void clock_propagate(Clock *clk) { - assert(clk->source == NULL); trace_clock_propagate(CLOCK_PATH(clk)); clock_propagate_period(clk, true); } @@ -143,16 +144,31 @@ 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_period_prop_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Clock *clk = CLOCK(obj); + uint64_t period = clock_get(clk); + visit_type_uint64(v, name, &period, errp); +} + + static void clock_initfn(Object *obj) { Clock *clk = CLOCK(obj); @@ -161,6 +177,11 @@ static void clock_initfn(Object *obj) clk->divider = 1; QLIST_INIT(&clk->children); + + if (qtest_enabled()) { + object_property_add(obj, "qtest-clock-period", "uint64", + clock_period_prop_get, NULL, NULL, NULL); + } } static void clock_finalizefn(Object *obj) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index b559a6606a..a064e6b997 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -22,18 +22,18 @@ #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 "qemu/lockcnt.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" +#ifdef CONFIG_PLUGIN #include "qemu/plugin.h" +#endif CPUState *cpu_by_arch_id(int64_t id) { @@ -70,14 +70,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 +86,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 +113,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, ResetType type) { - CPUState *cpu = CPU(dev); + CPUState *cpu = CPU(obj); CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -130,16 +130,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()) { - tcg_flush_jmp_cache(cpu); - tcg_flush_softmmu_tlb(cpu); - } + cpu_exec_reset_hold(cpu); } static bool cpu_common_has_work(CPUState *cs) @@ -149,10 +146,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, @@ -196,8 +203,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,45 +217,82 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp) } /* 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 */ +#ifdef CONFIG_PLUGIN + if (tcg_enabled()) { + qemu_plugin_vcpu_exit_hook(cpu); + } +#endif + /* 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; + + /* allocate storage for thread info, initialise condition variables */ + cpu->thread = g_new0(QemuThread, 1); + cpu->halt_cond = g_new0(QemuCond, 1); + qemu_cond_init(cpu->halt_cond); 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); QTAILQ_INIT(&cpu->mem_access_callbacks); cpu_exec_initfn(cpu); + + /* + * Plugin initialization must wait until the cpu start executing + * code, but we must queue this work before the threads are + * created to ensure we don't race. + */ +#ifdef CONFIG_PLUGIN + if (tcg_enabled()) { + cpu->plugin_state = qemu_plugin_create_vcpu_state(); + qemu_plugin_vcpu_init_hook(cpu); + } +#endif } static void cpu_common_finalize(Object *obj) { CPUState *cpu = CPU(obj); +#ifdef CONFIG_PLUGIN + if (tcg_enabled()) { + g_free(cpu->plugin_state); + } +#endif + free_queued_cpu_work(cpu); + /* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */ + if (cpu->gdb_regs) { + g_array_free(cpu->gdb_regs, TRUE); + } + qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); + qemu_cond_destroy(cpu->halt_cond); + g_free(cpu->halt_cond); + g_free(cpu->thread); } static int64_t cpu_common_get_arch_id(CPUState *cpu) @@ -257,9 +300,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 +314,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 +331,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 5eaf2e79e6..2a9a2a4eb5 100644 --- a/hw/core/cpu-sysemu.c +++ b/hw/core/cpu-sysemu.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "hw/core/cpu.h" +#include "exec/tswap.h" #include "hw/core/sysemu-cpu-ops.h" bool cpu_paging_enabled(const CPUState *cpu) @@ -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, diff --git a/hw/core/eif.c b/hw/core/eif.c new file mode 100644 index 0000000000..a7128b71ce --- /dev/null +++ b/hw/core/eif.c @@ -0,0 +1,757 @@ +/* + * EIF (Enclave Image Format) related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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/bswap.h" +#include "qapi/error.h" +#include "crypto/hash.h" +#include "crypto/x509-utils.h" +#include /* for crc32 */ +#include + +#include "hw/core/eif.h" + +#define MAX_SECTIONS 32 + +/* members are ordered according to field order in .eif file */ +typedef struct EifHeader { + uint8_t magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */ + uint16_t version; + uint16_t flags; + uint64_t default_memory; + uint64_t default_cpus; + uint16_t reserved; + uint16_t section_cnt; + uint64_t section_offsets[MAX_SECTIONS]; + uint64_t section_sizes[MAX_SECTIONS]; + uint32_t unused; + uint32_t eif_crc32; +} QEMU_PACKED EifHeader; + +/* members are ordered according to field order in .eif file */ +typedef struct EifSectionHeader { + /* + * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature, + * 5 = metadata + */ + uint16_t section_type; + uint16_t flags; + uint64_t section_size; +} QEMU_PACKED EifSectionHeader; + +enum EifSectionTypes { + EIF_SECTION_INVALID = 0, + EIF_SECTION_KERNEL = 1, + EIF_SECTION_CMDLINE = 2, + EIF_SECTION_RAMDISK = 3, + EIF_SECTION_SIGNATURE = 4, + EIF_SECTION_METADATA = 5, + EIF_SECTION_MAX = 6, +}; + +static const char *section_type_to_string(uint16_t type) +{ + const char *str; + switch (type) { + case EIF_SECTION_INVALID: + str = "invalid"; + break; + case EIF_SECTION_KERNEL: + str = "kernel"; + break; + case EIF_SECTION_CMDLINE: + str = "cmdline"; + break; + case EIF_SECTION_RAMDISK: + str = "ramdisk"; + break; + case EIF_SECTION_SIGNATURE: + str = "signature"; + break; + case EIF_SECTION_METADATA: + str = "metadata"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +static bool read_eif_header(FILE *f, EifHeader *header, uint32_t *crc, + Error **errp) +{ + size_t got; + size_t header_size = sizeof(*header); + + got = fread(header, 1, header_size, f); + if (got != header_size) { + error_setg(errp, "Failed to read EIF header"); + return false; + } + + if (memcmp(header->magic, ".eif", 4) != 0) { + error_setg(errp, "Invalid EIF image. Magic mismatch."); + return false; + } + + /* Exclude header->eif_crc32 field from CRC calculation */ + *crc = crc32(*crc, (uint8_t *)header, header_size - 4); + + header->version = be16_to_cpu(header->version); + header->flags = be16_to_cpu(header->flags); + header->default_memory = be64_to_cpu(header->default_memory); + header->default_cpus = be64_to_cpu(header->default_cpus); + header->reserved = be16_to_cpu(header->reserved); + header->section_cnt = be16_to_cpu(header->section_cnt); + + for (int i = 0; i < MAX_SECTIONS; ++i) { + header->section_offsets[i] = be64_to_cpu(header->section_offsets[i]); + } + + for (int i = 0; i < MAX_SECTIONS; ++i) { + header->section_sizes[i] = be64_to_cpu(header->section_sizes[i]); + if (header->section_sizes[i] > SSIZE_MAX) { + error_setg(errp, "Invalid EIF image. Section size out of bounds"); + return false; + } + } + + header->unused = be32_to_cpu(header->unused); + header->eif_crc32 = be32_to_cpu(header->eif_crc32); + return true; +} + +static bool read_eif_section_header(FILE *f, EifSectionHeader *section_header, + uint32_t *crc, Error **errp) +{ + size_t got; + size_t section_header_size = sizeof(*section_header); + + got = fread(section_header, 1, section_header_size, f); + if (got != section_header_size) { + error_setg(errp, "Failed to read EIF section header"); + return false; + } + + *crc = crc32(*crc, (uint8_t *)section_header, section_header_size); + + section_header->section_type = be16_to_cpu(section_header->section_type); + section_header->flags = be16_to_cpu(section_header->flags); + section_header->section_size = be64_to_cpu(section_header->section_size); + return true; +} + +/* + * Upon success, the caller is responsible for unlinking and freeing *tmp_path. + */ +static bool get_tmp_file(const char *template, char **tmp_path, Error **errp) +{ + int tmp_fd; + + *tmp_path = NULL; + tmp_fd = g_file_open_tmp(template, tmp_path, NULL); + if (tmp_fd < 0 || *tmp_path == NULL) { + error_setg(errp, "Failed to create temporary file for template %s", + template); + return false; + } + + close(tmp_fd); + return true; +} + +static void safe_fclose(FILE *f) +{ + if (f) { + fclose(f); + } +} + +static void safe_unlink(char *f) +{ + if (f) { + unlink(f); + } +} + +/* + * Upon success, the caller is reponsible for unlinking and freeing *kernel_path + */ +static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, + uint8_t *kernel, uint32_t *crc, Error **errp) +{ + size_t got; + FILE *tmp_file = NULL; + + *kernel_path = NULL; + if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) { + goto cleanup; + } + + tmp_file = fopen(*kernel_path, "wb"); + if (tmp_file == NULL) { + error_setg_errno(errp, errno, "Failed to open temporary file %s", + *kernel_path); + goto cleanup; + } + + got = fread(kernel, 1, size, f); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF kernel section data"); + goto cleanup; + } + + got = fwrite(kernel, 1, size, tmp_file); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to write EIF kernel section data to temporary" + " file"); + goto cleanup; + } + + *crc = crc32(*crc, kernel, size); + fclose(tmp_file); + + return true; + + cleanup: + safe_fclose(tmp_file); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path = NULL; + + return false; +} + +static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, + uint32_t *crc, Error **errp) +{ + size_t got = fread(cmdline, 1, size, f); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF cmdline section data"); + return false; + } + + *crc = crc32(*crc, (uint8_t *)cmdline, size); + return true; +} + +static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, + uint8_t *ramdisk, uint32_t *crc, Error **errp) +{ + size_t got; + + got = fread(ramdisk, 1, size, eif); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF ramdisk section data"); + return false; + } + + got = fwrite(ramdisk, 1, size, initrd); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to write EIF ramdisk data to temporary file"); + return false; + } + + *crc = crc32(*crc, ramdisk, size); + return true; +} + +static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size, + uint8_t *sha384, + uint32_t *crc, + Error **errp) +{ + size_t got; + g_autofree uint8_t *sig = NULL; + g_autofree uint8_t *cert = NULL; + cbor_item_t *item = NULL; + cbor_item_t *pcr0 = NULL; + size_t len; + size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + struct cbor_pair *pair; + struct cbor_load_result result; + bool ret = false; + + sig = g_try_malloc(size); + if (!sig) { + error_setg(errp, "Out of memory reading signature section"); + goto cleanup; + } + + got = fread(sig, 1, size, eif); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF signature section data"); + goto cleanup; + } + + *crc = crc32(*crc, sig, size); + + item = cbor_load(sig, size, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + error_setg(errp, "Failed to load signature section data as CBOR"); + goto cleanup; + } + if (!cbor_isa_array(item) || cbor_array_size(item) < 1) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + pcr0 = cbor_array_get(item, 0); + if (!pcr0) { + error_setg(errp, "Failed to get PCR0 signature"); + goto cleanup; + } + if (!cbor_isa_map(pcr0) || cbor_map_size(pcr0) != 2) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + pair = cbor_map_handle(pcr0); + if (!cbor_isa_string(pair->key) || cbor_string_length(pair->key) != 19 || + memcmp(cbor_string_handle(pair->key), "signing_certificate", 19) != 0) { + error_setg(errp, "Invalid signautre CBOR"); + goto cleanup; + } + if (!cbor_isa_array(pair->value)) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + len = cbor_array_size(pair->value); + if (len == 0) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + cert = g_try_malloc(len); + if (!cert) { + error_setg(errp, "Out of memory reading signature section"); + goto cleanup; + } + + for (int i = 0; i < len; ++i) { + cbor_item_t *tmp = cbor_array_get(pair->value, i); + if (!tmp) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + if (!cbor_isa_uint(tmp) || cbor_int_get_width(tmp) != CBOR_INT_8) { + cbor_decref(&tmp); + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + cert[i] = cbor_get_uint8(tmp); + cbor_decref(&tmp); + } + + if (qcrypto_get_x509_cert_fingerprint(cert, len, QCRYPTO_HASH_ALGO_SHA384, + sha384, &hash_len, errp)) { + goto cleanup; + } + + ret = true; + + cleanup: + if (pcr0) { + cbor_decref(&pcr0); + } + if (item) { + cbor_decref(&item); + } + return ret; +} + +/* Expects file to have offset 0 before this function is called */ +static long get_file_size(FILE *f, Error **errp) +{ + long size; + + if (fseek(f, 0, SEEK_END) != 0) { + error_setg_errno(errp, errno, "Failed to seek to the end of file"); + return -1; + } + + size = ftell(f); + if (size == -1) { + error_setg_errno(errp, errno, "Failed to get offset"); + return -1; + } + + if (fseek(f, 0, SEEK_SET) != 0) { + error_setg_errno(errp, errno, "Failed to seek back to the start"); + return -1; + } + + return size; +} + +static bool get_SHA384_digest(GList *list, uint8_t *digest, Error **errp) +{ + size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + size_t list_len = g_list_length(list); + struct iovec *iovec_list = g_new0(struct iovec, list_len); + bool ret = true; + GList *l; + int i; + + for (i = 0, l = list; l != NULL; l = l->next, i++) { + iovec_list[i] = *(struct iovec *) l->data; + } + + if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iovec_list, list_len, + &digest, &digest_len, errp) < 0) { + ret = false; + } + + g_free(iovec_list); + return ret; +} + +static void free_iovec(struct iovec *iov) +{ + if (iov) { + g_free(iov->iov_base); + g_free(iov); + } +} + +/* + * Upon success, the caller is reponsible for unlinking and freeing + * *kernel_path, *initrd_path and freeing *cmdline. + */ +bool read_eif_file(const char *eif_path, const char *machine_initrd, + char **kernel_path, char **initrd_path, char **cmdline, + uint8_t *image_sha384, uint8_t *bootstrap_sha384, + uint8_t *app_sha384, uint8_t *fingerprint_sha384, + bool *signature_found, Error **errp) +{ + FILE *f = NULL; + FILE *machine_initrd_f = NULL; + FILE *initrd_path_f = NULL; + long machine_initrd_size; + uint32_t crc = 0; + EifHeader eif_header; + bool seen_sections[EIF_SECTION_MAX] = {false}; + /* kernel + ramdisks + cmdline sha384 hash */ + GList *iov_PCR0 = NULL; + /* kernel + boot ramdisk + cmdline sha384 hash */ + GList *iov_PCR1 = NULL; + /* application ramdisk(s) hash */ + GList *iov_PCR2 = NULL; + uint8_t *ptr = NULL; + struct iovec *iov_ptr = NULL; + + *signature_found = false; + *kernel_path = *initrd_path = *cmdline = NULL; + + f = fopen(eif_path, "rb"); + if (f == NULL) { + error_setg_errno(errp, errno, "Failed to open %s", eif_path); + goto cleanup; + } + + if (!read_eif_header(f, &eif_header, &crc, errp)) { + goto cleanup; + } + + if (eif_header.version < 4) { + error_setg(errp, "Expected EIF version 4 or greater"); + goto cleanup; + } + + if (eif_header.flags != 0) { + error_setg(errp, "Expected EIF flags to be 0"); + goto cleanup; + } + + if (eif_header.section_cnt > MAX_SECTIONS) { + error_setg(errp, "EIF header section count must not be greater than " + "%d but found %d", MAX_SECTIONS, eif_header.section_cnt); + goto cleanup; + } + + for (int i = 0; i < eif_header.section_cnt; ++i) { + EifSectionHeader hdr; + uint16_t section_type; + + if (eif_header.section_offsets[i] > OFF_MAX) { + error_setg(errp, "Invalid EIF image. Section offset out of bounds"); + goto cleanup; + } + if (fseek(f, eif_header.section_offsets[i], SEEK_SET) != 0) { + error_setg_errno(errp, errno, "Failed to offset to %" PRIu64 " in EIF file", + eif_header.section_offsets[i]); + goto cleanup; + } + + if (!read_eif_section_header(f, &hdr, &crc, errp)) { + goto cleanup; + } + + if (hdr.flags != 0) { + error_setg(errp, "Expected EIF section header flags to be 0"); + goto cleanup; + } + + if (eif_header.section_sizes[i] != hdr.section_size) { + error_setg(errp, "EIF section size mismatch between header and " + "section header: header %" PRIu64 ", section header %" PRIu64, + eif_header.section_sizes[i], + hdr.section_size); + goto cleanup; + } + + section_type = hdr.section_type; + + switch (section_type) { + case EIF_SECTION_KERNEL: + if (seen_sections[EIF_SECTION_KERNEL]) { + error_setg(errp, "Invalid EIF image. More than 1 kernel " + "section"); + goto cleanup; + } + + ptr = g_try_malloc(hdr.section_size); + if (!ptr) { + error_setg(errp, "Out of memory reading kernel section"); + goto cleanup; + } + + iov_ptr = g_malloc(sizeof(struct iovec)); + iov_ptr->iov_base = ptr; + iov_ptr->iov_len = hdr.section_size; + + iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); + iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); + + if (!read_eif_kernel(f, hdr.section_size, kernel_path, ptr, &crc, + errp)) { + goto cleanup; + } + + break; + case EIF_SECTION_CMDLINE: + { + uint64_t size; + uint8_t *cmdline_copy; + if (seen_sections[EIF_SECTION_CMDLINE]) { + error_setg(errp, "Invalid EIF image. More than 1 cmdline " + "section"); + goto cleanup; + } + size = hdr.section_size; + *cmdline = g_try_malloc(size + 1); + if (!*cmdline) { + error_setg(errp, "Out of memory reading command line section"); + goto cleanup; + } + if (!read_eif_cmdline(f, size, *cmdline, &crc, errp)) { + goto cleanup; + } + (*cmdline)[size] = '\0'; + + /* + * We make a copy of '*cmdline' for putting it in iovecs so that + * we can easily free all the iovec entries later as we cannot + * free '*cmdline' which is used by the caller. + */ + cmdline_copy = g_memdup2(*cmdline, size); + + iov_ptr = g_malloc(sizeof(struct iovec)); + iov_ptr->iov_base = cmdline_copy; + iov_ptr->iov_len = size; + + iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); + iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); + break; + } + case EIF_SECTION_RAMDISK: + { + if (!seen_sections[EIF_SECTION_RAMDISK]) { + /* + * If this is the first time we are seeing a ramdisk section, + * we need to create the initrd temporary file. + */ + if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) { + goto cleanup; + } + initrd_path_f = fopen(*initrd_path, "wb"); + if (initrd_path_f == NULL) { + error_setg_errno(errp, errno, "Failed to open file %s", + *initrd_path); + goto cleanup; + } + } + + ptr = g_try_malloc(hdr.section_size); + if (!ptr) { + error_setg(errp, "Out of memory reading initrd section"); + goto cleanup; + } + + iov_ptr = g_malloc(sizeof(struct iovec)); + iov_ptr->iov_base = ptr; + iov_ptr->iov_len = hdr.section_size; + + iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); + /* + * If it's the first ramdisk, we need to hash it into bootstrap + * i.e., iov_PCR1, otherwise we need to hash it into app i.e., + * iov_PCR2. + */ + if (!seen_sections[EIF_SECTION_RAMDISK]) { + iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); + } else { + iov_PCR2 = g_list_append(iov_PCR2, iov_ptr); + } + + if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, ptr, + &crc, errp)) { + goto cleanup; + } + + break; + } + case EIF_SECTION_SIGNATURE: + *signature_found = true; + if (!get_signature_fingerprint_sha384(f, hdr.section_size, + fingerprint_sha384, &crc, + errp)) { + goto cleanup; + } + break; + default: + /* other sections including invalid or unknown sections */ + { + uint8_t *buf; + size_t got; + uint64_t size = hdr.section_size; + buf = g_try_malloc(size); + if (!buf) { + error_setg(errp, "Out of memory reading unknown section"); + goto cleanup; + } + got = fread(buf, 1, size, f); + if ((uint64_t) got != size) { + g_free(buf); + error_setg(errp, "Failed to read EIF %s section data", + section_type_to_string(section_type)); + goto cleanup; + } + crc = crc32(crc, buf, size); + g_free(buf); + break; + } + } + + if (section_type < EIF_SECTION_MAX) { + seen_sections[section_type] = true; + } + } + + if (!seen_sections[EIF_SECTION_KERNEL]) { + error_setg(errp, "Invalid EIF image. No kernel section."); + goto cleanup; + } + if (!seen_sections[EIF_SECTION_CMDLINE]) { + error_setg(errp, "Invalid EIF image. No cmdline section."); + goto cleanup; + } + if (!seen_sections[EIF_SECTION_RAMDISK]) { + error_setg(errp, "Invalid EIF image. No ramdisk section."); + goto cleanup; + } + + if (eif_header.eif_crc32 != crc) { + error_setg(errp, "CRC mismatch. Expected %u but header has %u.", + crc, eif_header.eif_crc32); + goto cleanup; + } + + /* + * Let's append the initrd file from "-initrd" option if any. Although + * we pass the crc pointer to read_eif_ramdisk, it is not useful anymore. + * We have already done the crc mismatch check above this code. + */ + if (machine_initrd) { + machine_initrd_f = fopen(machine_initrd, "rb"); + if (machine_initrd_f == NULL) { + error_setg_errno(errp, errno, "Failed to open initrd file %s", + machine_initrd); + goto cleanup; + } + + machine_initrd_size = get_file_size(machine_initrd_f, errp); + if (machine_initrd_size == -1) { + goto cleanup; + } + + ptr = g_try_malloc(machine_initrd_size); + if (!ptr) { + error_setg(errp, "Out of memory reading initrd file"); + goto cleanup; + } + + iov_ptr = g_malloc(sizeof(struct iovec)); + iov_ptr->iov_base = ptr; + iov_ptr->iov_len = machine_initrd_size; + + iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); + iov_PCR2 = g_list_append(iov_PCR2, iov_ptr); + + if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f, + machine_initrd_size, ptr, &crc, errp)) { + goto cleanup; + } + } + + if (!get_SHA384_digest(iov_PCR0, image_sha384, errp)) { + goto cleanup; + } + if (!get_SHA384_digest(iov_PCR1, bootstrap_sha384, errp)) { + goto cleanup; + } + if (!get_SHA384_digest(iov_PCR2, app_sha384, errp)) { + goto cleanup; + } + + /* + * We only need to free iov_PCR0 entries because iov_PCR1 and + * iov_PCR2 iovec entries are subsets of iov_PCR0 iovec entries. + */ + g_list_free_full(iov_PCR0, (GDestroyNotify) free_iovec); + g_list_free(iov_PCR1); + g_list_free(iov_PCR2); + fclose(f); + fclose(initrd_path_f); + safe_fclose(machine_initrd_f); + return true; + + cleanup: + g_list_free_full(iov_PCR0, (GDestroyNotify) free_iovec); + g_list_free(iov_PCR1); + g_list_free(iov_PCR2); + + safe_fclose(f); + safe_fclose(initrd_path_f); + safe_fclose(machine_initrd_f); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path = NULL; + + safe_unlink(*initrd_path); + g_free(*initrd_path); + *initrd_path = NULL; + + g_free(*cmdline); + *cmdline = NULL; + + return false; +} diff --git a/hw/core/eif.h b/hw/core/eif.h new file mode 100644 index 0000000000..fed3cb5514 --- /dev/null +++ b/hw/core/eif.h @@ -0,0 +1,22 @@ +/* + * EIF (Enclave Image Format) related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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_CORE_EIF_H +#define HW_CORE_EIF_H + +bool read_eif_file(const char *eif_path, const char *machine_initrd, + char **kernel_path, char **initrd_path, + char **kernel_cmdline, uint8_t *image_sha384, + uint8_t *bootstrap_sha384, uint8_t *app_sha384, + uint8_t *fingerprint_sha384, bool *signature_found, + Error **errp); + +#endif + diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index 4f4d77908d..ea8628b892 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -24,14 +24,14 @@ * 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. */ #include "qemu/osdep.h" -#include "hw/core/cpu.h" +#include "exec/tswap.h" #include "sysemu/dma.h" #include "sysemu/reset.h" #include "hw/boards.h" @@ -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..7d5b0038c1 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -26,17 +26,6 @@ #include "hw/irq.h" #include "qom/object.h" -DECLARE_INSTANCE_CHECKER(struct IRQState, IRQ, - TYPE_IRQ) - -struct IRQState { - Object parent_obj; - - qemu_irq_handler handler; - void *opaque; - int n; -}; - void qemu_set_irq(qemu_irq irq, int level) { if (!irq) @@ -45,6 +34,21 @@ void qemu_set_irq(qemu_irq irq, int level) irq->handler(irq->opaque, irq->n, level); } +static void init_irq_fields(IRQState *irq, qemu_irq_handler handler, + void *opaque, int n) +{ + irq->handler = handler; + irq->opaque = opaque; + irq->n = n; +} + +void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, + int n) +{ + object_initialize(irq, sizeof(*irq), TYPE_IRQ); + init_irq_fields(irq, handler, opaque, n); +} + qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, void *opaque, int n) { @@ -68,13 +72,8 @@ 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; - - irq = IRQ(object_new(TYPE_IRQ)); - irq->handler = handler; - irq->opaque = opaque; - irq->n = n; - + IRQState *irq = IRQ(object_new(TYPE_IRQ)); + init_irq_fields(irq, handler, opaque, n); return irq; } @@ -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..7ccc9d5fbc 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; @@ -265,7 +267,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) const char *def_cfg_name; char path[FIT_LOADER_MAX_PATH]; int itb_size, configs, cfg_off, off; - hwaddr kernel_end; + hwaddr kernel_end = 0; int ret; itb = load_device_tree(filename, &itb_size); diff --git a/hw/core/loader.c b/hw/core/loader.c index c142af70ad..af483fdbaa 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 @@ -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)) @@ -299,11 +301,11 @@ 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 -#include "hw/elf_ops.h" +#define elf_sword int32_t +#define bswapSZs bswap32s +#include "hw/elf_ops.h.inc" #undef elfhdr #undef elf_phdr @@ -315,17 +317,17 @@ 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 -#include "hw/elf_ops.h" +#define elf_sword int64_t +#define bswapSZs bswap64s +#define SZ 64 +#include "hw/elf_ops.h.inc" 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; @@ -604,6 +610,7 @@ ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen) r = inflate(&s, Z_FINISH); if (r != Z_OK && r != Z_STREAM_END) { printf ("Error: inflate() returned %d\n", r); + inflateEnd(&s); return -1; } dstbytes = s.next_out - (unsigned char *) dst; @@ -838,17 +845,95 @@ ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz, return ret; } -/* Load a gzip-compressed kernel. */ -ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) -{ - ssize_t bytes; - uint8_t *data; - bytes = load_image_gzipped_buffer(filename, max_sz, &data); - if (bytes != -1) { - rom_add_blob_fixed(filename, data, bytes, addr); - g_free(data); +/* 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; } @@ -973,13 +1058,13 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) ssize_t rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr, + bool has_option_rom, MemoryRegion *mr, AddressSpace *as) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; - ssize_t rc; - int fd = -1; + gsize size; + g_autoptr(GError) gerr = NULL; char devpath[100]; if (as && mr) { @@ -997,10 +1082,10 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, rom->path = g_strdup(file); } - fd = qemu_open(rom->path, O_RDONLY | O_BINARY, NULL); - if (fd == -1) { - fprintf(stderr, "Could not open option rom '%s': %s\n", - rom->path, strerror(errno)); + if (!g_file_get_contents(rom->path, (gchar **) &rom->data, + &size, &gerr)) { + fprintf(stderr, "rom: file %-20s: error %s\n", + rom->name, gerr->message); goto err; } @@ -1009,23 +1094,8 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, rom->fw_file = g_strdup(file); } rom->addr = addr; - rom->romsize = lseek(fd, 0, SEEK_END); - if (rom->romsize == -1) { - fprintf(stderr, "rom: file %-20s: get size error: %s\n", - rom->name, strerror(errno)); - goto err; - } - + rom->romsize = size; rom->datasize = rom->romsize; - rom->data = g_malloc0(rom->datasize); - lseek(fd, 0, SEEK_SET); - rc = read(fd, rom->data, rom->datasize); - if (rc != rom->datasize) { - fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n", - rom->name, rc, rom->datasize); - goto err; - } - close(fd); rom_insert(rom); if (rom->fw_file && fw_cfg) { const char *basename; @@ -1042,7 +1112,7 @@ ssize_t 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; @@ -1054,7 +1124,7 @@ ssize_t 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); } } @@ -1062,9 +1132,6 @@ ssize_t rom_add_file(const char *file, const char *fw_dir, return 0; err: - if (fd != -1) - close(fd); - rom_free(rom); return -1; } @@ -1238,10 +1305,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); } @@ -1395,7 +1462,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; } @@ -1595,7 +1662,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", diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 5cb5eecbfc..8701f00cc7 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); } @@ -81,6 +87,10 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict) monitor_printf(mon, " cluster-id: \"%" PRIu64 "\"\n", c->cluster_id); } + if (c->has_module_id) { + monitor_printf(mon, " module-id: \"%" PRIu64 "\"\n", + c->module_id); + } if (c->has_core_id) { monitor_printf(mon, " core-id: \"%" PRIu64 "\"\n", c->core_id); } @@ -134,3 +144,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..130217da8f 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -8,32 +8,23 @@ */ #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 "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 +35,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 +46,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 +54,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); @@ -74,7 +64,8 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) return head; } -MachineInfoList *qmp_query_machines(Error **errp) +MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, + Error **errp) { GSList *el, *machines = object_class_get_list(TYPE_MACHINE, false); MachineInfoList *mach_list = NULL; @@ -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,32 @@ 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; + } + + if (compat_props && mc->compat_props) { + int i; + info->compat_props = NULL; + CompatPropertyList **tail = &(info->compat_props); + info->has_compat_props = true; + + for (i = 0; i < mc->compat_props->len; i++) { + GlobalProperty *mt_prop = g_ptr_array_index(mc->compat_props, + i); + CompatProperty *prop; + + prop = g_malloc0(sizeof(*prop)); + prop->qom_type = g_strdup(mt_prop->driver); + prop->property = g_strdup(mt_prop->property); + prop->value = g_strdup(mt_prop->value); + + QAPI_LIST_APPEND(tail, prop); + } } QAPI_LIST_PREPEND(mach_list, info); @@ -127,7 +136,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 +148,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 +177,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 +235,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 +252,157 @@ 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; +} + +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); +} + +static int qmp_x_query_intc_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); + if (k->print_info) { + k->print_info(intc, buf); + } else { + g_string_append_printf(buf, + "Interrupt controller information not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +HumanReadableText *qmp_x_query_interrupt_controllers(Error **errp) +{ + g_autoptr(GString) buf = g_string_new(""); + object_child_foreach_recursive(object_get_root(), + qmp_x_query_intc_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..b954eb8490 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) { @@ -41,6 +51,10 @@ static char *cpu_hierarchy_to_string(MachineState *ms) g_string_append_printf(s, " * clusters (%u)", ms->smp.clusters); } + if (mc->smp_props.modules_supported) { + g_string_append_printf(s, " * modules (%u)", ms->smp.modules); + } + g_string_append_printf(s, " * cores (%u)", ms->smp.cores); g_string_append_printf(s, " * threads (%u)", ms->smp.threads); @@ -73,44 +87,80 @@ 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 modules = config->has_modules ? config->modules : 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_modules && config->modules == 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. + * If not supported by the machine, a topology parameter must + * not be set to a value greater than 1. */ - 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 && clusters > 1) { - error_setg(errp, "clusters not supported by this machine's CPU topology"); + if (!mc->smp_props.modules_supported && + config->has_modules && config->modules > 1) { + error_setg(errp, + "modules > 1 not supported by this machine's CPU topology"); return; } + modules = modules > 0 ? modules : 1; - dies = dies > 0 ? dies : 1; + if (!mc->smp_props.clusters_supported && + config->has_clusters && config->clusters > 1) { + error_setg(errp, + "clusters > 1 not supported by this machine's CPU topology"); + return; + } clusters = clusters > 0 ? clusters : 1; + if (!mc->smp_props.dies_supported && + config->has_dies && config->dies > 1) { + error_setg(errp, + "dies > 1 not supported by this machine's CPU topology"); + return; + } + dies = dies > 0 ? dies : 1; + + if (!mc->smp_props.books_supported && + config->has_books && config->books > 1) { + error_setg(errp, + "books > 1 not supported by this machine's CPU topology"); + return; + } + books = books > 0 ? books : 1; + + if (!mc->smp_props.drawers_supported && + config->has_drawers && config->drawers > 1) { + error_setg(errp, + "drawers > 1 not supported by this machine's CPU topology"); + return; + } + drawers = drawers > 0 ? drawers : 1; + /* compute missing values based on the provided ones */ if (cpus == 0 && maxcpus == 0) { sockets = sockets > 0 ? sockets : 1; @@ -124,42 +174,59 @@ 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 * + modules * cores * threads); } else if (cores == 0) { threads = threads > 0 ? threads : 1; - cores = maxcpus / (sockets * dies * clusters * threads); + cores = maxcpus / + (drawers * books * sockets * dies * + clusters * modules * 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 * modules * threads); } else if (sockets == 0) { threads = threads > 0 ? threads : 1; - sockets = maxcpus / (dies * clusters * cores * threads); + sockets = maxcpus / + (drawers * books * dies * clusters * + modules * 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 * modules * cores); } } - maxcpus = maxcpus > 0 ? maxcpus : sockets * dies * clusters * cores * threads; + total_cpus = drawers * books * sockets * dies * + clusters * modules * 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; + ms->smp.modules = modules; ms->smp.cores = cores; 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 +260,140 @@ void machine_parse_smp_config(MachineState *ms, return; } } + +static bool machine_check_topo_support(MachineState *ms, + CpuTopologyLevel topo, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if ((topo == CPU_TOPOLOGY_LEVEL_MODULE && !mc->smp_props.modules_supported) || + (topo == CPU_TOPOLOGY_LEVEL_CLUSTER && !mc->smp_props.clusters_supported) || + (topo == CPU_TOPOLOGY_LEVEL_DIE && !mc->smp_props.dies_supported) || + (topo == CPU_TOPOLOGY_LEVEL_BOOK && !mc->smp_props.books_supported) || + (topo == CPU_TOPOLOGY_LEVEL_DRAWER && !mc->smp_props.drawers_supported)) { + error_setg(errp, + "Invalid topology level: %s. " + "The topology level is not supported by this machine", + CpuTopologyLevel_str(topo)); + return false; + } + + return true; +} + +bool machine_parse_smp_cache(MachineState *ms, + const SmpCachePropertiesList *caches, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + const SmpCachePropertiesList *node; + DECLARE_BITMAP(caches_bitmap, CACHE_LEVEL_AND_TYPE__MAX); + + bitmap_zero(caches_bitmap, CACHE_LEVEL_AND_TYPE__MAX); + for (node = caches; node; node = node->next) { + /* Prohibit users from repeating settings. */ + if (test_bit(node->value->cache, caches_bitmap)) { + error_setg(errp, + "Invalid cache properties: %s. " + "The cache properties are duplicated", + CacheLevelAndType_str(node->value->cache)); + return false; + } + + machine_set_cache_topo_level(ms, node->value->cache, + node->value->topology); + set_bit(node->value->cache, caches_bitmap); + } + + for (int i = 0; i < CACHE_LEVEL_AND_TYPE__MAX; i++) { + const SmpCacheProperties *props = &ms->smp_cache.props[i]; + + /* + * Reject non "default" topology level if the cache isn't + * supported by the machine. + */ + if (props->topology != CPU_TOPOLOGY_LEVEL_DEFAULT && + !mc->smp_props.cache_supported[props->cache]) { + error_setg(errp, + "%s cache topology not supported by this machine", + CacheLevelAndType_str(props->cache)); + return false; + } + + if (!machine_check_topo_support(ms, props->topology, errp)) { + return false; + } + } + return true; +} + +unsigned int machine_topo_get_cores_per_socket(const MachineState *ms) +{ + return ms->smp.cores * ms->smp.modules * 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); +} + +CpuTopologyLevel machine_get_cache_topo_level(const MachineState *ms, + CacheLevelAndType cache) +{ + return ms->smp_cache.props[cache].topology; +} + +void machine_set_cache_topo_level(MachineState *ms, CacheLevelAndType cache, + CpuTopologyLevel level) +{ + ms->smp_cache.props[cache].topology = level; +} + +/* + * When both cache1 and cache2 are configured with specific topology levels + * (not default level), is cache1's topology level higher than cache2? + */ +static bool smp_cache_topo_cmp(const SmpCache *smp_cache, + CacheLevelAndType cache1, + CacheLevelAndType cache2) +{ + /* + * Before comparing, the "default" topology level should be replaced + * with the specific level. + */ + assert(smp_cache->props[cache1].topology != CPU_TOPOLOGY_LEVEL_DEFAULT); + + return smp_cache->props[cache1].topology > smp_cache->props[cache2].topology; +} + +/* + * Currently, we have no way to expose the arch-specific default cache model + * because the cache model is sometimes related to the CPU model (e.g., i386). + * + * We can only check the correctness of the cache topology after the arch loads + * the user-configured cache model from MachineState and consumes the special + * "default" level by replacing it with the specific level. + */ +bool machine_check_smp_cache(const MachineState *ms, Error **errp) +{ + if (smp_cache_topo_cmp(&ms->smp_cache, CACHE_LEVEL_AND_TYPE_L1D, + CACHE_LEVEL_AND_TYPE_L2) || + smp_cache_topo_cmp(&ms->smp_cache, CACHE_LEVEL_AND_TYPE_L1I, + CACHE_LEVEL_AND_TYPE_L2)) { + error_setg(errp, + "Invalid smp cache topology. " + "L2 cache topology level shouldn't be lower than L1 cache"); + return false; + } + + if (smp_cache_topo_cmp(&ms->smp_cache, CACHE_LEVEL_AND_TYPE_L2, + CACHE_LEVEL_AND_TYPE_L3)) { + error_setg(errp, + "Invalid smp cache topology. " + "L3 cache topology level shouldn't be lower than L2 cache"); + return false; + } + + return true; +} diff --git a/hw/core/machine.c b/hw/core/machine.c index 19f42450f5..f29fe95964 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -11,34 +11,78 @@ */ #include "qemu/osdep.h" -#include "qemu/option.h" -#include "qapi/qmp/qerror.h" -#include "sysemu/replay.h" #include "qemu/units.h" +#include "qemu/accel.h" +#include "sysemu/replay.h" #include "hw/boards.h" #include "hw/loader.h" +#include "qemu/error-report.h" #include "qapi/error.h" -#include "qapi/qapi-visit-common.h" #include "qapi/qapi-visit-machine.h" -#include "qapi/visitor.h" +#include "qemu/madvise.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 "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_9_1[] = { + { TYPE_PCI_DEVICE, "x-pcie-ext-tag", "false" }, +}; +const size_t hw_compat_9_1_len = G_N_ELEMENTS(hw_compat_9_1); + +GlobalProperty hw_compat_9_0[] = { + {"arm-cpu", "backcompat-cntfrq", "true" }, + { "scsi-hd", "migrate-emulated-scsi-request", "false" }, + { "scsi-cd", "migrate-emulated-scsi-request", "false" }, + {"vfio-pci", "skip-vsc-check", "false" }, + { "virtio-pci", "x-pcie-pm-no-soft-reset", "off" }, + {"sd-card", "spec_version", "2" }, +}; +const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); + +GlobalProperty hw_compat_8_2[] = { + { "migration", "zero-page-detection", "legacy"}, + { TYPE_VIRTIO_IOMMU_PCI, "granule", "4k" }, + { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "64" }, + { "virtio-gpu-device", "x-scanout-vmstate-version", "1" }, +}; +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" }, + { TYPE_VIRTIO_NET, "host_uso", "off"}, + { TYPE_VIRTIO_NET, "guest_uso4", "off"}, + { TYPE_VIRTIO_NET, "guest_uso6", "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" }, +}; +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" }, @@ -80,6 +124,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); @@ -161,7 +206,6 @@ GlobalProperty hw_compat_3_0[] = {}; const size_t hw_compat_3_0_len = G_N_ELEMENTS(hw_compat_3_0); GlobalProperty hw_compat_2_12[] = { - { "migration", "decompress-error-check", "off" }, { "hda-audio", "use-timer", "false" }, { "cirrus-vga", "global-vmstate", "true" }, { "VGA", "global-vmstate", "true" }, @@ -233,8 +277,6 @@ GlobalProperty hw_compat_2_5[] = { const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); GlobalProperty hw_compat_2_4[] = { - /* Optional because the 'scsi' property is Linux-only */ - { "virtio-blk-device", "scsi", "true", .optional = true }, { "e1000", "extra_mac_registers", "off" }, { "virtio-pci", "x-disable-pcie", "on" }, { "virtio-pci", "migrate-extra", "off" }, @@ -243,33 +285,6 @@ GlobalProperty hw_compat_2_4[] = { }; const size_t hw_compat_2_4_len = G_N_ELEMENTS(hw_compat_2_4); -GlobalProperty hw_compat_2_3[] = { - { "virtio-blk-pci", "any_layout", "off" }, - { "virtio-balloon-pci", "any_layout", "off" }, - { "virtio-serial-pci", "any_layout", "off" }, - { "virtio-9p-pci", "any_layout", "off" }, - { "virtio-rng-pci", "any_layout", "off" }, - { TYPE_PCI_DEVICE, "x-pcie-lnksta-dllla", "off" }, - { "migration", "send-configuration", "off" }, - { "migration", "send-section-footer", "off" }, - { "migration", "store-global-state", "off" }, -}; -const size_t hw_compat_2_3_len = G_N_ELEMENTS(hw_compat_2_3); - -GlobalProperty hw_compat_2_2[] = {}; -const size_t hw_compat_2_2_len = G_N_ELEMENTS(hw_compat_2_2); - -GlobalProperty hw_compat_2_1[] = { - { "intel-hda", "old_msi_addr", "on" }, - { "VGA", "qemu-extended-regs", "off" }, - { "secondary-vga", "qemu-extended-regs", "off" }, - { "virtio-scsi-pci", "any_layout", "off" }, - { "usb-mouse", "usb_version", "1" }, - { "usb-kbd", "usb_version", "1" }, - { "virtio-pci", "virtio-pci-bus-master-bug-migration", "on" }, -}; -const size_t hw_compat_2_1_len = G_N_ELEMENTS(hw_compat_2_1); - MachineState *current_machine; static char *machine_get_kernel(Object *obj, Error **errp) @@ -397,6 +412,10 @@ static void machine_set_dump_guest_core(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); + if (!value && QEMU_MADV_DONTDUMP == QEMU_MADV_INVALID) { + error_setg(errp, "Dumping guest memory cannot be disabled on this host"); + return; + } ms->dump_guest_core = value; } @@ -411,6 +430,10 @@ static void machine_set_mem_merge(Object *obj, bool value, Error **errp) { MachineState *ms = MACHINE(obj); + if (value && QEMU_MADV_MERGEABLE == QEMU_MADV_INVALID) { + error_setg(errp, "Memory merging is not supported on this host"); + return; + } ms->mem_merge = value; } @@ -557,12 +580,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; } @@ -576,11 +598,19 @@ static void machine_set_mem(Object *obj, Visitor *v, const char *name, mem->size = mc->fixup_ram_size(mem->size); } if ((ram_addr_t)mem->size != mem->size) { - error_setg(errp, "ram size too large"); + error_setg(errp, "ram size %llu exceeds permitted maximum %llu", + (unsigned long long)mem->size, + (unsigned long long)RAM_ADDR_MAX); goto out_free; } if (mem->has_max_size) { + if ((ram_addr_t)mem->max_size != mem->max_size) { + error_setg(errp, "ram size %llu exceeds permitted maximum %llu", + (unsigned long long)mem->max_size, + (unsigned long long)RAM_ADDR_MAX); + goto out_free; + } if (mem->max_size < mem->size) { error_setg(errp, "invalid value of maxmem: " "maximum memory size (0x%" PRIx64 ") must be at least " @@ -667,6 +697,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; @@ -677,7 +727,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); @@ -687,8 +737,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); } @@ -753,6 +802,11 @@ void machine_set_cpu_numa_node(MachineState *machine, return; } + if (props->has_module_id && !slot->props.has_module_id) { + error_setg(errp, "module-id is not supported"); + return; + } + if (props->has_cluster_id && !slot->props.has_cluster_id) { error_setg(errp, "cluster-id is not supported"); return; @@ -777,6 +831,11 @@ void machine_set_cpu_numa_node(MachineState *machine, continue; } + if (props->has_module_id && + props->module_id != slot->props.module_id) { + continue; + } + if (props->has_cluster_id && props->cluster_id != slot->props.cluster_id) { continue; @@ -829,9 +888,12 @@ 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, + .has_modules = true, .modules = ms->smp.modules, .has_cores = true, .cores = ms->smp.cores, .has_threads = true, .threads = ms->smp.threads, .has_maxcpus = true, .maxcpus = ms->smp.max_cpus, @@ -855,6 +917,40 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, machine_parse_smp_config(ms, config, errp); } +static void machine_get_smp_cache(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MachineState *ms = MACHINE(obj); + SmpCache *cache = &ms->smp_cache; + SmpCachePropertiesList *head = NULL; + SmpCachePropertiesList **tail = &head; + + for (int i = 0; i < CACHE_LEVEL_AND_TYPE__MAX; i++) { + SmpCacheProperties *node = g_new(SmpCacheProperties, 1); + + node->cache = cache->props[i].cache; + node->topology = cache->props[i].topology; + QAPI_LIST_APPEND(tail, node); + } + + visit_type_SmpCachePropertiesList(v, name, &head, errp); + qapi_free_SmpCachePropertiesList(head); +} + +static void machine_set_smp_cache(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MachineState *ms = MACHINE(obj); + SmpCachePropertiesList *caches; + + if (!visit_type_SmpCachePropertiesList(v, name, &caches, errp)) { + return; + } + + machine_parse_smp_cache(ms, caches, errp); + qapi_free_SmpCachePropertiesList(caches); +} + static void machine_get_boot(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -876,8 +972,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); } } @@ -892,13 +987,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; @@ -914,6 +1009,50 @@ 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 bool create_default_memdev(MachineState *ms, const char *path, + Error **errp) +{ + Object *obj; + MachineClass *mc = MACHINE_GET_CLASS(ms); + bool r = false; + + obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM); + if (path) { + if (!object_property_set_str(obj, "mem-path", path, errp)) { + goto out; + } + } + if (!object_property_set_int(obj, "size", ms->ram_size, errp)) { + goto out; + } + object_property_add_child(object_get_objects_root(), mc->default_ram_id, + obj); + /* Ensure backend's memory region name is equal to mc->default_ram_id */ + if (!object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id", + false, errp)) { + goto out; + } + if (!user_creatable_complete(USER_CREATABLE(obj), errp)) { + goto out; + } + r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp); + +out: + object_unref(obj); + return r; +} + static void machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -921,12 +1060,20 @@ static void machine_class_init(ObjectClass *oc, void *data) /* Default 128 MB as guest ram size */ mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; + /* + * SMBIOS 3.1.0 7.18.5 Memory Device — Extended Size + * use max possible value that could be encoded into + * 'Extended Size' field (2047Tb). + */ + mc->smbios_memory_device_size = 2047 * TiB; /* numa node memory size aligned on 8MB by default. * On Linux, each node's border has to be 8MB aligned */ mc->numa_mem_align_shift = 23; + mc->create_default_memdev = create_default_memdev; + object_class_property_add_str(oc, "kernel", machine_get_kernel, machine_set_kernel); object_class_property_set_description(oc, "kernel", @@ -964,6 +1111,11 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "smp", "CPU topology"); + object_class_property_add(oc, "smp-cache", "SmpCachePropertiesWrapper", + machine_get_smp_cache, machine_set_smp_cache, NULL, NULL); + object_class_property_set_description(oc, "smp-cache", + "Cache properties list for SMP machine"); + object_class_property_add(oc, "phandle-start", "int", machine_get_phandle_start, machine_set_phandle_start, NULL, NULL); @@ -1058,15 +1210,13 @@ static void machine_initfn(Object *obj) container_get(obj, "/peripheral-anon"); ms->dump_guest_core = true; - ms->mem_merge = true; + ms->mem_merge = (QEMU_MADV_MERGEABLE != QEMU_MADV_INVALID); ms->enable_graphics = true; ms->kernel_cmdline = g_strdup(""); ms->ram_size = mc->default_ram_size; 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); @@ -1095,12 +1245,20 @@ 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; + ms->smp.modules = 1; ms->smp.cores = 1; ms->smp.threads = 1; + for (int i = 0; i < CACHE_LEVEL_AND_TYPE__MAX; i++) { + ms->smp_cache.props[i].cache = (CacheLevelAndType)i; + ms->smp_cache.props[i].topology = CPU_TOPOLOGY_LEVEL_DEFAULT; + } + machine_copy_boot_config(ms, &(BootConfiguration){ 0 }); } @@ -1119,6 +1277,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->audiodev); } bool machine_usb(MachineState *machine) @@ -1141,6 +1300,11 @@ bool machine_mem_merge(MachineState *machine) return machine->mem_merge; } +bool machine_require_guest_memfd(MachineState *machine) +{ + return machine->cgs && machine->cgs->require_guest_memfd; +} + static char *cpu_slot_to_string(const CPUArchId *cpu) { GString *s = g_string_new(NULL); @@ -1159,6 +1323,12 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) } g_string_append_printf(s, "cluster-id: %"PRId64, cpu->props.cluster_id); } + if (cpu->props.has_module_id) { + if (s->len) { + g_string_append_printf(s, ", "); + } + g_string_append_printf(s, "module-id: %"PRId64, cpu->props.module_id); + } if (cpu->props.has_core_id) { if (s->len) { g_string_append_printf(s, ", "); @@ -1253,6 +1423,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) { @@ -1268,44 +1477,74 @@ MemoryRegion *machine_consume_memdev(MachineState *machine, return ret; } -static bool create_default_memdev(MachineState *ms, const char *path, Error **errp) +const char *machine_class_default_cpu_type(MachineClass *mc) { - Object *obj; - MachineClass *mc = MACHINE_GET_CLASS(ms); - bool r = false; - - obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM); - if (path) { - if (!object_property_set_str(obj, "mem-path", path, errp)) { - goto out; - } + 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]; } - if (!object_property_set_int(obj, "size", ms->ram_size, errp)) { - goto out; - } - object_property_add_child(object_get_objects_root(), mc->default_ram_id, - obj); - /* Ensure backend's memory region name is equal to mc->default_ram_id */ - if (!object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id", - false, errp)) { - goto out; - } - if (!user_creatable_complete(USER_CREATABLE(obj), errp)) { - goto out; - } - r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp); - -out: - object_unref(obj); - return r; + 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 @@ -1331,13 +1570,19 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error * numa_uses_legacy_mem()) { if (object_property_find(object_get_objects_root(), machine_class->default_ram_id)) { - error_setg(errp, "object name '%s' is reserved for the default" - " RAM backend, it can't be used for any other purposes." - " Change the object's 'id' to something else", + 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)) { + + if (!machine_class->create_default_memdev(current_machine, mem_path, + errp)) { return; } } @@ -1346,6 +1591,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); + } } } @@ -1353,41 +1601,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) { @@ -1435,7 +1651,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)); } @@ -1450,14 +1666,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..9fd0b5aaa5 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -3,7 +3,7 @@ hwcore_ss.add(files( 'bus.c', 'qdev-properties.c', 'qdev.c', - 'reset.c', + 'resetcontainer.c', 'resettable.c', 'vmstate-if.c', # irq.c needed for qdev GPIO handling: @@ -11,47 +11,38 @@ hwcore_ss.add(files( 'clock.c', 'qdev-clock.c', )) -if have_system - hwcore_ss.add(files( - 'hotplug.c', - 'qdev-hotplug.c', - )) -else - hwcore_ss.add(files( - 'hotplug-stubs.c', - )) -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', 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')) +system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls]) -softmmu_ss.add(files( +system_ss.add(files( 'cpu-sysemu.c', 'fw-path-provider.c', 'gpio.c', + 'hotplug.c', 'loader.c', 'machine-hmp-cmds.c', + 'machine-qmp-cmds.c', 'machine.c', 'nmi.c', 'null-machine.c', + 'numa.c', 'qdev-fw.c', + 'qdev-hotplug.c', 'qdev-properties-system.c', + 'reset.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 ea24a5fa8c..1b5f44baea 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; @@ -250,7 +249,7 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, lb_data.initiator = node->initiator; lb_data.target = node->target; - if (node->data_type <= HMATLB_DATA_TYPE_WRITE_LATENCY) { + if (node->data_type <= HMAT_LB_DATA_TYPE_WRITE_LATENCY) { /* Input latency data */ if (!node->has_latency) { @@ -314,7 +313,7 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, numa_info[node->target].lb_info_provided |= BIT(0); } lb_data.data = node->latency; - } else if (node->data_type >= HMATLB_DATA_TYPE_ACCESS_BANDWIDTH) { + } else if (node->data_type >= HMAT_LB_DATA_TYPE_ACCESS_BANDWIDTH) { /* Input bandwidth data */ if (!node->has_bandwidth) { error_setg(errp, "Missing 'bandwidth' option"); @@ -381,7 +380,7 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, } lb_data.data = node->bandwidth; } else { - assert(0); + g_assert_not_reached(); } g_array_append_val(hmat_lb->list, lb_data); @@ -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) { diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index d8f3754e96..b25468e38a 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(), }; @@ -124,7 +124,7 @@ static void or_irq_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = or_irq_reset; + device_class_set_legacy_reset(dc, or_irq_reset); device_class_set_props(dc, or_irq_properties); dc->realize = or_irq_realize; dc->vmsd = &vmstate_or_irq; @@ -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/platform-bus.c b/hw/core/platform-bus.c index b8487b26b6..dc58bf505a 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -145,9 +145,12 @@ static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, * the target device's memory region */ for (off = 0; off < pbus->mmio_size; off += alignment) { - if (!memory_region_find(&pbus->mmio, off, size).mr) { + MemoryRegion *mr = memory_region_find(&pbus->mmio, off, size).mr; + if (!mr) { found_region = true; break; + } else { + memory_region_unref(mr); } } diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index eb5ba1aff7..1d8964d804 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" @@ -83,7 +83,7 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) delta = s->delta = s->limit; } - if (s->period == 0) { + if (s->period == 0 && s->period_frac == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } @@ -309,7 +309,7 @@ void ptimer_run(ptimer_state *s, int oneshot) assert(s->in_transaction); - if (was_disabled && s->period == 0) { + if (was_disabled && s->period == 0 && s->period_frac == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } @@ -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..22ea1ed358 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, @@ -55,6 +58,32 @@ static bool check_prop_still_unset(Object *obj, const char *name, return false; } +bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str, + Error **errp) +{ + int i, len; + + len = strlen(str); + if (len > 8) { + error_setg(errp, "'loadparm' can only contain up to 8 characters"); + return false; + } + + for (i = 0; i < len; i++) { + uint8_t c = qemu_toupper(str[i]); /* mimic HMC */ + + if (qemu_isalnum(c) || c == '.' || c == ' ') { + loadparm[i] = c; + } else { + error_setg(errp, + "invalid character in 'loadparm': '%c' (ASCII 0x%02x)", + c, c); + return false; + } + } + + return true; +} /* --- drive --- */ @@ -105,7 +134,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 +147,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 +168,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 +229,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 +268,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 +469,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 +499,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 +574,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, }; @@ -572,18 +614,14 @@ const PropertyInfo qdev_prop_losttickpolicy = { static void set_blocksize(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - DeviceState *dev = DEVICE(obj); Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value; - Error *local_err = NULL; if (!visit_type_size(v, name, &value, errp)) { return; } - check_block_size(dev->id ? : "", name, value, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!check_block_size(name, value, errp)) { return; } *ptr = value; @@ -643,13 +681,51 @@ const PropertyInfo qdev_prop_fdc_drive_type = { const PropertyInfo qdev_prop_multifd_compression = { .name = "MultiFDCompression", .description = "multifd_compression values, " - "none/zlib/zstd", + "none/zlib/zstd/qpl/uadk/qatzip", .enum_table = &MultiFDCompression_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, .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 +744,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 +755,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 +774,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 +784,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'" @@ -740,39 +816,57 @@ static void set_pci_devfn(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { Property *prop = opaque; + g_autofree GenericAlternate *alt; int32_t value, *ptr = object_field_prop_ptr(obj, prop); unsigned int slot, fn, n; - char *str; + g_autofree char *str = NULL; - if (!visit_type_str(v, name, &str, NULL)) { + if (!visit_start_alternate(v, name, &alt, sizeof(*alt), errp)) { + return; + } + + switch (alt->type) { + case QTYPE_QSTRING: + if (!visit_type_str(v, name, &str, errp)) { + goto out; + } + + if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { + fn = 0; + if (sscanf(str, "%x%n", &slot, &n) != 1) { + goto invalid; + } + } + if (str[n] != '\0' || fn > 7 || slot > 31) { + goto invalid; + } + *ptr = slot << 3 | fn; + break; + + case QTYPE_QNUM: if (!visit_type_int32(v, name, &value, errp)) { - return; + goto out; } if (value < -1 || value > 255) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", "a value between -1 and 255"); - return; + goto out; } *ptr = value; - return; + break; + + default: + error_setg(errp, "Invalid parameter type for '%s', expected int or str", + name ? name : "null"); + goto out; } - if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { - fn = 0; - if (sscanf(str, "%x%n", &slot, &n) != 1) { - goto invalid; - } - } - if (str[n] != '\0' || fn > 7 || slot > 31) { - goto invalid; - } - *ptr = slot << 3 | fn; - g_free(str); - return; + goto out; invalid: error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); - g_free(str); +out: + visit_end_alternate(v, (void **) &alt); } static int print_pci_devfn(Object *obj, Property *prop, char *dest, @@ -912,7 +1006,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 +1028,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 +1067,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 +1081,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 +1183,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 +1225,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(S390CpuEntitlement) != sizeof(int)); + +const PropertyInfo qdev_prop_cpus390entitlement = { + .name = "S390CpuEntitlement", + .description = "low/medium (default)/high", + .enum_table = &S390CpuEntitlement_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..315196bd85 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -2,13 +2,14 @@ #include "hw/qdev-properties.h" #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,103 +545,211 @@ 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 --- */ -static Property *qdev_prop_walk(Property *props, const char *name) +static const Property *qdev_prop_walk(const Property *props, const char *name) { if (!props) { return NULL; @@ -654,10 +763,10 @@ static Property *qdev_prop_walk(Property *props, const char *name) return NULL; } -static Property *qdev_prop_find(DeviceState *dev, const char *name) +static const Property *qdev_prop_find(DeviceState *dev, const char *name) { ObjectClass *class; - Property *prop; + const Property *prop; /* device properties */ class = object_get_class(OBJECT(dev)); @@ -682,7 +791,7 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj, break; default: case -EINVAL: - error_setg(errp, QERR_PROPERTY_VALUE_BAD, + error_setg(errp, "Property '%s.%s' doesn't take value '%s'", object_get_typename(obj), name, value); break; case -ENOENT: @@ -731,7 +840,7 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) { - Property *prop; + const Property *prop; prop = qdev_prop_find(dev, name); object_property_set_str(OBJECT(dev), name, @@ -739,6 +848,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; @@ -840,7 +956,7 @@ const PropertyInfo qdev_prop_size = { /* --- object link property --- */ static ObjectProperty *create_link_property(ObjectClass *oc, const char *name, - Property *prop) + const Property *prop) { return object_class_property_add_link(oc, name, prop->link_type, prop->offset, @@ -853,7 +969,7 @@ const PropertyInfo qdev_prop_link = { .create = create_link_property, }; -void qdev_property_add_static(DeviceState *dev, Property *prop) +void qdev_property_add_static(DeviceState *dev, const Property *prop) { Object *obj = OBJECT(dev); ObjectProperty *op; @@ -864,7 +980,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop) field_prop_getter(prop->info), field_prop_setter(prop->info), prop->info->release, - prop); + (Property *)prop); object_property_set_description(obj, prop->name, prop->info->description); @@ -878,7 +994,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop) } static void qdev_class_add_property(DeviceClass *klass, const char *name, - Property *prop) + const Property *prop) { ObjectClass *oc = OBJECT_CLASS(klass); ObjectProperty *op; @@ -891,7 +1007,7 @@ static void qdev_class_add_property(DeviceClass *klass, const char *name, field_prop_getter(prop->info), field_prop_setter(prop->info), prop->info->release, - prop); + (Property *)prop); } if (prop->set_default) { prop->info->set_default_value(op, prop); @@ -930,7 +1046,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, * Do not use this in new code! QOM Properties added through this interface * will be given names in the "legacy" namespace. */ -static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop) +static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop) { g_autofree char *name = NULL; @@ -942,12 +1058,12 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop) name = g_strdup_printf("legacy-%s", prop->name); object_class_property_add(OBJECT_CLASS(dc), name, "str", prop->info->print ? qdev_get_legacy_property : prop->info->get, - NULL, NULL, prop); + NULL, NULL, (Property *)prop); } -void device_class_set_props(DeviceClass *dc, Property *props) +void device_class_set_props(DeviceClass *dc, const Property *props) { - Property *prop; + const Property *prop; dc->props_ = props; for (prop = props; prop && prop->name; prop++) { @@ -959,16 +1075,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 0145501904..5f13111b77 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -29,7 +29,6 @@ #include "qapi/error.h" #include "qapi/qapi-events-qdev.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -147,31 +146,16 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp) DeviceState *qdev_new(const char *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)); } DeviceState *qdev_try_new(const char *name) { - if (!module_object_class_by_name(name)) { + ObjectClass *oc = module_object_class_by_name(name); + if (!oc) { return NULL; } - return DEVICE(object_new(name)); + return DEVICE(object_new_with_class(oc)); } static QTAILQ_HEAD(, DeviceListener) device_listeners @@ -250,60 +234,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); @@ -384,7 +314,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; } @@ -493,8 +423,6 @@ void qdev_del_unplug_blocker(DeviceState *dev, Error *reason) bool qdev_unplug_blocked(DeviceState *dev, Error **errp) { - ERRP_GUARD(); - if (dev->unplug_blockers) { error_propagate(errp, error_copy(dev->unplug_blockers->data)); return true; @@ -535,7 +463,8 @@ static void device_set_realized(Object *obj, bool value, Error **errp) static int unattached_count; if (dev->hotplugged && !dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj)); + error_setg(errp, "Device '%s' does not support hotplugging", + object_get_typename(obj)); return; } @@ -757,7 +686,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; } @@ -803,57 +732,6 @@ device_vmstate_if_get_id(VMStateIf *obj) return qdev_get_dev_path(dev); } -/** - * device_phases_reset: - * Transition reset method for devices to allow moving - * smoothly from legacy reset method to multi-phases - */ -static void device_phases_reset(DeviceState *dev) -{ - ResettableClass *rc = RESETTABLE_GET_CLASS(dev); - - if (rc->phases.enter) { - rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD); - } - if (rc->phases.hold) { - rc->phases.hold(OBJECT(dev)); - } - if (rc->phases.exit) { - rc->phases.exit(OBJECT(dev)); - } -} - -static void device_transitional_reset(Object *obj) -{ - DeviceClass *dc = DEVICE_GET_CLASS(obj); - - /* - * This will call either @device_phases_reset (for multi-phases transitioned - * devices) or a device's specific method for not-yet transitioned devices. - * In both case, it does not reset children. - */ - if (dc->reset) { - dc->reset(DEVICE(obj)); - } -} - -/** - * device_get_transitional_reset: - * check if the device's class is ready for multi-phase - */ -static ResettableTrFunction device_get_transitional_reset(Object *obj) -{ - DeviceClass *dc = DEVICE_GET_CLASS(obj); - if (dc->reset != device_phases_reset) { - /* - * dc->reset has been overridden by a subclass, - * the device is not ready for multi phase yet. - */ - return device_transitional_reset; - } - return NULL; -} - static void device_class_init(ObjectClass *class, void *data) { DeviceClass *dc = DEVICE_CLASS(class); @@ -875,20 +753,12 @@ static void device_class_init(ObjectClass *class, void *data) rc->child_foreach = device_reset_child_foreach; /* - * @device_phases_reset is put as the default reset method below, allowing - * to do the multi-phase transition from base classes to leaf classes. It - * allows a legacy-reset Device class to extend a multi-phases-reset - * Device class for the following reason: - * + If a base class B has been moved to multi-phase, then it does not - * override this default reset method and may have defined phase methods. - * + A child class C (extending class B) which uses - * device_class_set_parent_reset() (or similar means) to override the - * reset method will still work as expected. @device_phases_reset function - * will be registered as the parent reset method and effectively call - * parent reset phases. + * A NULL legacy_reset implies a three-phase reset device. Devices can + * only be reset using three-phase aware mechanisms, but we still support + * for transitional purposes leaf classes which set the old legacy_reset + * method via device_class_set_legacy_reset(). */ - dc->reset = device_phases_reset; - rc->get_transitional_function = device_get_transitional_reset; + dc->legacy_reset = NULL; object_class_property_add_bool(class, "realized", device_get_realized, device_set_realized); @@ -900,12 +770,30 @@ static void device_class_init(ObjectClass *class, void *data) offsetof(DeviceState, parent_bus), NULL, 0); } -void device_class_set_parent_reset(DeviceClass *dc, - DeviceReset dev_reset, - DeviceReset *parent_reset) +static void do_legacy_reset(Object *obj, ResetType type) { - *parent_reset = dc->reset; - dc->reset = dev_reset; + DeviceClass *dc = DEVICE_GET_CLASS(obj); + + dc->legacy_reset(DEVICE(obj)); +} + +void device_class_set_legacy_reset(DeviceClass *dc, DeviceReset dev_reset) +{ + /* + * A legacy DeviceClass::reset has identical semantics to the + * three-phase "hold" method, with no "enter" or "exit" + * behaviour. Classes that use this legacy function must be leaf + * classes that do not chain up to their parent class reset. + * There is no mechanism for resetting a device that does not + * use the three-phase APIs, so the only place which calls + * the legacy_reset hook is do_legacy_reset(). + */ + ResettableClass *rc = RESETTABLE_CLASS(dc); + + rc->phases.enter = NULL; + rc->phases.hold = do_legacy_reset; + rc->phases.exit = NULL; + dc->legacy_reset = dev_reset; } void device_class_set_parent_realize(DeviceClass *dc, @@ -924,16 +812,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; @@ -945,6 +823,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 d3263b613e..14a2639fbf 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -24,64 +24,154 @@ */ #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; +} + +/* + * 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; bool skip_on_snapshot_load; -} QEMUResetEntry; +}; -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, ResetType type) +{ + LegacyReset *lr = LEGACY_RESET(obj); + + if (type == RESET_TYPE_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) { - QEMUResetEntry *re = g_new0(QEMUResetEntry, 1); + Object *obj = object_new(TYPE_LEGACY_RESET); + LegacyReset *lr = LEGACY_RESET(obj); - re->func = func; - re->opaque = opaque; - re->skip_on_snapshot_load = true; - QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); + 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(ShutdownCause reason) +void qemu_register_resettable(Object *obj) { - QEMUResetEntry *re, *nre; - - /* reset all devices */ - QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { - if (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD && - re->skip_on_snapshot_load) { - continue; - } - 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(ResetType type) +{ + /* Reset the simulation */ + resettable_reset(OBJECT(get_root_reset_container()), type); +} 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 c3df75c6ba..5cdb4a4f8d 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -48,8 +48,6 @@ void resettable_reset(Object *obj, ResetType type) void resettable_assert_reset(Object *obj, ResetType type) { - /* TODO: change this assert when adding support for other reset types */ - assert(type == RESET_TYPE_COLD); trace_resettable_reset_assert_begin(obj, type); assert(!enter_phase_in_progress); @@ -64,8 +62,6 @@ void resettable_assert_reset(Object *obj, ResetType type) void resettable_release_reset(Object *obj, ResetType type) { - /* TODO: change this assert when adding support for other reset types */ - assert(type == RESET_TYPE_COLD); trace_resettable_reset_release_begin(obj, type); assert(!enter_phase_in_progress); @@ -97,20 +93,6 @@ static void resettable_child_foreach(ResettableClass *rc, Object *obj, } } -/** - * resettable_get_tr_func: - * helper to fetch transitional reset callback if any. - */ -static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc, - Object *obj) -{ - ResettableTrFunction tr_func = NULL; - if (rc->get_transitional_function) { - tr_func = rc->get_transitional_function(obj); - } - return tr_func; -} - static void resettable_phase_enter(Object *obj, void *opaque, ResetType type) { ResettableClass *rc = RESETTABLE_GET_CLASS(obj); @@ -150,7 +132,7 @@ static void resettable_phase_enter(Object *obj, void *opaque, ResetType type) if (action_needed) { trace_resettable_phase_enter_exec(obj, obj_typename, type, !!rc->phases.enter); - if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) { + if (rc->phases.enter) { rc->phases.enter(obj, type); } s->hold_phase_pending = true; @@ -175,13 +157,9 @@ static void resettable_phase_hold(Object *obj, void *opaque, ResetType type) /* exec hold phase */ if (s->hold_phase_pending) { s->hold_phase_pending = false; - ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj); trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold); - if (tr_func) { - trace_resettable_transitional_function(obj, obj_typename); - tr_func(obj); - } else if (rc->phases.hold) { - rc->phases.hold(obj); + if (rc->phases.hold) { + rc->phases.hold(obj, type); } } trace_resettable_phase_hold_end(obj, obj_typename, s->count); @@ -203,8 +181,8 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) assert(s->count > 0); 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); + if (rc->phases.exit) { + rc->phases.exit(obj, type); } } s->exit_phase_in_progress = false; diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 05c1da3d31..e64d99c8ed 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -154,16 +154,6 @@ static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, } } -void sysbus_mmio_unmap(SysBusDevice *dev, int n) -{ - assert(n >= 0 && n < dev->num_mmio); - - if (dev->mmio[n].addr != (hwaddr)-1) { - memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory); - dev->mmio[n].addr = (hwaddr)-1; - } -} - void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) { sysbus_mmio_map_common(dev, n, addr, false, 0); @@ -269,7 +259,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 +279,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 +288,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/uboot_image.h b/hw/core/uboot_image.h index 18ac293359..e4dcfb08f0 100644 --- a/hw/core/uboot_image.h +++ b/hw/core/uboot_image.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * (C) Copyright 2008 Semihalf * 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/Kconfig b/hw/cpu/Kconfig index 1767d028ac..baff478e1b 100644 --- a/hw/cpu/Kconfig +++ b/hw/cpu/Kconfig @@ -1,8 +1,17 @@ -config ARM11MPCORE - bool - config A9MPCORE bool + select A9_GTIMER + select A9SCU # snoop control unit + select ARM_GIC + select ARM_MPTIMER config A15MPCORE bool + select ARM_GIC + +config ARM11MPCORE + bool + select ARM11SCU + +config CPU_CLUSTER + bool 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..9d36bf8ae2 100644 --- a/hw/cpu/meson.build +++ b/hw/cpu/meson.build @@ -1,6 +1,7 @@ -softmmu_ss.add(files('core.c', 'cluster.c')) +system_ss.add(files('core.c')) +system_ss.add(when: 'CONFIG_CPU_CLUSTER', if_true: files('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/Kconfig b/hw/cris/Kconfig deleted file mode 100644 index 884ad2cbc0..0000000000 --- a/hw/cris/Kconfig +++ /dev/null @@ -1,9 +0,0 @@ -config AXIS - bool - select ETRAXFS - select PFLASH_CFI02 - select NAND - -config ETRAXFS - bool - select PTIMER diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c deleted file mode 100644 index d82050d927..0000000000 --- a/hw/cris/axis_dev88.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * QEMU model for the AXIS devboard 88. - * - * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB. - * - * 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/units.h" -#include "qapi/error.h" -#include "cpu.h" -#include "hw/sysbus.h" -#include "net/net.h" -#include "hw/block/flash.h" -#include "hw/boards.h" -#include "hw/cris/etraxfs.h" -#include "hw/loader.h" -#include "elf.h" -#include "boot.h" -#include "sysemu/qtest.h" -#include "sysemu/sysemu.h" - -#define D(x) -#define DNAND(x) - -struct nand_state_t -{ - DeviceState *nand; - MemoryRegion iomem; - unsigned int rdy:1; - unsigned int ale:1; - unsigned int cle:1; - unsigned int ce:1; -}; - -static struct nand_state_t nand_state; -static uint64_t nand_read(void *opaque, hwaddr addr, unsigned size) -{ - struct nand_state_t *s = opaque; - uint32_t r; - int rdy; - - r = nand_getio(s->nand); - nand_getpins(s->nand, &rdy); - s->rdy = rdy; - - DNAND(printf("%s addr=%x r=%x\n", __func__, addr, r)); - return r; -} - -static void -nand_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - struct nand_state_t *s = opaque; - int rdy; - - DNAND(printf("%s addr=%x v=%x\n", __func__, addr, (unsigned)value)); - nand_setpins(s->nand, s->cle, s->ale, s->ce, 1, 0); - nand_setio(s->nand, value); - nand_getpins(s->nand, &rdy); - s->rdy = rdy; -} - -static const MemoryRegionOps nand_ops = { - .read = nand_read, - .write = nand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct tempsensor_t -{ - unsigned int shiftreg; - unsigned int count; - enum { - ST_OUT, ST_IN, ST_Z - } state; - - uint16_t regs[3]; -}; - -static void tempsensor_clkedge(struct tempsensor_t *s, - unsigned int clk, unsigned int data_in) -{ - D(printf("%s clk=%d state=%d sr=%x\n", __func__, - clk, s->state, s->shiftreg)); - if (s->count == 0) { - s->count = 16; - s->state = ST_OUT; - } - switch (s->state) { - case ST_OUT: - /* Output reg is clocked at negedge. */ - if (!clk) { - s->count--; - s->shiftreg <<= 1; - if (s->count == 0) { - s->shiftreg = 0; - s->state = ST_IN; - s->count = 16; - } - } - break; - case ST_Z: - if (clk) { - s->count--; - if (s->count == 0) { - s->shiftreg = 0; - s->state = ST_OUT; - s->count = 16; - } - } - break; - case ST_IN: - /* Indata is sampled at posedge. */ - if (clk) { - s->count--; - s->shiftreg <<= 1; - s->shiftreg |= data_in & 1; - if (s->count == 0) { - D(printf("%s cfgreg=%x\n", __func__, s->shiftreg)); - s->regs[0] = s->shiftreg; - s->state = ST_OUT; - s->count = 16; - - if ((s->regs[0] & 0xff) == 0) { - /* 25 degrees celsius. */ - s->shiftreg = 0x0b9f; - } else if ((s->regs[0] & 0xff) == 0xff) { - /* Sensor ID, 0x8100 LM70. */ - s->shiftreg = 0x8100; - } else - printf("Invalid tempsens state %x\n", s->regs[0]); - } - } - break; - } -} - - -#define RW_PA_DOUT 0x00 -#define R_PA_DIN 0x01 -#define RW_PA_OE 0x02 -#define RW_PD_DOUT 0x10 -#define R_PD_DIN 0x11 -#define RW_PD_OE 0x12 - -static struct gpio_state_t -{ - MemoryRegion iomem; - struct nand_state_t *nand; - struct tempsensor_t tempsensor; - uint32_t regs[0x5c / 4]; -} gpio_state; - -static uint64_t gpio_read(void *opaque, hwaddr addr, unsigned size) -{ - struct gpio_state_t *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) - { - case R_PA_DIN: - r = s->regs[RW_PA_DOUT] & s->regs[RW_PA_OE]; - - /* Encode pins from the nand. */ - r |= s->nand->rdy << 7; - break; - case R_PD_DIN: - r = s->regs[RW_PD_DOUT] & s->regs[RW_PD_OE]; - - /* Encode temp sensor pins. */ - r |= (!!(s->tempsensor.shiftreg & 0x10000)) << 4; - break; - - default: - r = s->regs[addr]; - break; - } - return r; - D(printf("%s %x=%x\n", __func__, addr, r)); -} - -static void gpio_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - struct gpio_state_t *s = opaque; - D(printf("%s %x=%x\n", __func__, addr, (unsigned)value)); - - addr >>= 2; - switch (addr) - { - case RW_PA_DOUT: - /* Decode nand pins. */ - s->nand->ale = !!(value & (1 << 6)); - s->nand->cle = !!(value & (1 << 5)); - s->nand->ce = !!(value & (1 << 4)); - - s->regs[addr] = value; - break; - - case RW_PD_DOUT: - /* Temp sensor clk. */ - if ((s->regs[addr] ^ value) & 2) - tempsensor_clkedge(&s->tempsensor, !!(value & 2), - !!(value & 16)); - s->regs[addr] = value; - break; - - default: - s->regs[addr] = value; - break; - } -} - -static const MemoryRegionOps gpio_ops = { - .read = gpio_read, - .write = gpio_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -#define INTMEM_SIZE (128 * KiB) - -static struct cris_load_info li; - -static -void axisdev88_init(MachineState *machine) -{ - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - CRISCPU *cpu; - DeviceState *dev; - SysBusDevice *s; - DriveInfo *nand; - qemu_irq irq[30], nmi[2]; - void *etraxfs_dmac; - struct etraxfs_dma_client *dma_eth; - int i; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_intmem = g_new(MemoryRegion, 1); - - /* init CPUs */ - cpu = CRIS_CPU(cpu_create(machine->cpu_type)); - - memory_region_add_subregion(address_space_mem, 0x40000000, machine->ram); - - /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the - internal memory. */ - memory_region_init_ram(phys_intmem, NULL, "axisdev88.chipram", - INTMEM_SIZE, &error_fatal); - memory_region_add_subregion(address_space_mem, 0x38000000, phys_intmem); - - /* Attach a NAND flash to CS1. */ - nand = drive_get(IF_MTD, 0, 0); - nand_state.nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, - NAND_MFR_STMICRO, 0x39); - memory_region_init_io(&nand_state.iomem, NULL, &nand_ops, &nand_state, - "nand", 0x05000000); - memory_region_add_subregion(address_space_mem, 0x10000000, - &nand_state.iomem); - - gpio_state.nand = &nand_state; - memory_region_init_io(&gpio_state.iomem, NULL, &gpio_ops, &gpio_state, - "gpio", 0x5c); - memory_region_add_subregion(address_space_mem, 0x3001a000, - &gpio_state.iomem); - - - dev = qdev_new("etraxfs-pic"); - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_mmio_map(s, 0, 0x3001c000); - sysbus_connect_irq(s, 0, qdev_get_gpio_in(DEVICE(cpu), CRIS_CPU_IRQ)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in(DEVICE(cpu), CRIS_CPU_NMI)); - for (i = 0; i < 30; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - nmi[0] = qdev_get_gpio_in(dev, 30); - nmi[1] = qdev_get_gpio_in(dev, 31); - - etraxfs_dmac = etraxfs_dmac_init(0x30000000, 10); - for (i = 0; i < 10; i++) { - /* On ETRAX, odd numbered channels are inputs. */ - etraxfs_dmac_connect(etraxfs_dmac, i, irq + 7 + i, i & 1); - } - - /* 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]); - } - - /* 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) { - etraxfs_dmac_connect_client(etraxfs_dmac, 6, &dma_eth[2]); - etraxfs_dmac_connect_client(etraxfs_dmac, 7, &dma_eth[3]); - } - - /* 2 timers. */ - sysbus_create_varargs("etraxfs-timer", 0x3001e000, irq[0x1b], nmi[1], NULL); - sysbus_create_varargs("etraxfs-timer", 0x3005e000, irq[0x1b], nmi[1], NULL); - - for (i = 0; i < 4; i++) { - etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hd(i)); - } - - if (kernel_filename) { - li.image_filename = kernel_filename; - li.cmdline = kernel_cmdline; - li.ram_size = machine->ram_size; - cris_load_image(cpu, &li); - } else if (!qtest_enabled()) { - fprintf(stderr, "Kernel image must be specified\n"); - exit(1); - } -} - -static void axisdev88_machine_init(MachineClass *mc) -{ - mc->desc = "AXIS devboard 88"; - mc->init = axisdev88_init; - mc->is_default = true; - mc->default_cpu_type = CRIS_CPU_TYPE_NAME("crisv32"); - mc->default_ram_id = "axisdev88.ram"; -} - -DEFINE_MACHINE("axis-dev88", axisdev88_machine_init) diff --git a/hw/cris/boot.c b/hw/cris/boot.c deleted file mode 100644 index 9fa09cfd83..0000000000 --- a/hw/cris/boot.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * CRIS image loading. - * - * Copyright (c) 2010 Edgar E. Iglesias, Axis Communications AB. - * - * 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/loader.h" -#include "elf.h" -#include "boot.h" -#include "qemu/cutils.h" -#include "sysemu/reset.h" - -static void main_cpu_reset(void *opaque) -{ - CRISCPU *cpu = opaque; - CPUCRISState *env = &cpu->env; - struct cris_load_info *li; - - li = env->load_info; - - cpu_reset(CPU(cpu)); - - if (!li) { - /* nothing more to do. */ - return; - } - - env->pc = li->entry; - - if (li->image_filename) { - env->regs[8] = 0x56902387; /* RAM boot magic. */ - env->regs[9] = 0x40004000 + li->image_size; - } - - if (li->cmdline) { - /* Let the kernel know we are modifying the cmdline. */ - env->regs[10] = 0x87109563; - env->regs[11] = 0x40000000; - } -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0x80000000LL; -} - -void cris_load_image(CRISCPU *cpu, struct cris_load_info *li) -{ - CPUCRISState *env = &cpu->env; - uint64_t entry; - int kcmdline_len; - int image_size; - - env->load_info = li; - /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis - devboard SDK. */ - image_size = load_elf(li->image_filename, NULL, - translate_kernel_address, NULL, - &entry, NULL, NULL, NULL, 0, EM_CRIS, 0, 0); - li->entry = entry; - if (image_size < 0) { - /* Takes a kimage from the axis devboard SDK. */ - image_size = load_image_targphys(li->image_filename, 0x40004000, - li->ram_size); - li->entry = 0x40004000; - } - - if (image_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", - li->image_filename); - exit(1); - } - - if (li->cmdline && (kcmdline_len = strlen(li->cmdline))) { - if (kcmdline_len > 256) { - fprintf(stderr, "Too long CRIS kernel cmdline (max 256)\n"); - exit(1); - } - pstrcpy_targphys("cmdline", 0x40000000, 256, li->cmdline); - } - qemu_register_reset(main_cpu_reset, cpu); -} diff --git a/hw/cris/boot.h b/hw/cris/boot.h deleted file mode 100644 index 9f1e0e340c..0000000000 --- a/hw/cris/boot.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef HW_CRIS_BOOT_H -#define HW_CRIS_BOOT_H - -struct cris_load_info -{ - const char *image_filename; - const char *cmdline; - int image_size; - ram_addr_t ram_size; - - hwaddr entry; -}; - -void cris_load_image(CRISCPU *cpu, struct cris_load_info *li); - -#endif diff --git a/hw/cris/meson.build b/hw/cris/meson.build deleted file mode 100644 index dc808a4e0f..0000000000 --- a/hw/cris/meson.build +++ /dev/null @@ -1,5 +0,0 @@ -cris_ss = ss.source_set() -cris_ss.add(files('boot.c')) -cris_ss.add(when: 'CONFIG_AXIS', if_true: files('axis_dev88.c')) - -hw_arch += {'cris': cris_ss} diff --git a/hw/cxl/cxl-cdat.c b/hw/cxl/cxl-cdat.c index 3653aa56f0..959a55518e 100644 --- a/hw/cxl/cxl-cdat.c +++ b/hw/cxl/cxl-cdat.c @@ -44,11 +44,12 @@ static void cdat_len_check(CDATSubHeader *hdr, Error **errp) } } -static void ct3_build_cdat(CDATObject *cdat, Error **errp) +static bool 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 */ @@ -57,22 +58,23 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) cdat_header = g_malloc0(sizeof(*cdat_header)); if (!cdat_header) { error_setg(errp, "Failed to allocate CDAT header"); - return; + return false; } - cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, cdat->private); + cdat->built_buf_len = cdat->build_cdat_table(&cdat->built_buf, + cdat->private); - if (!cdat->built_buf_len) { + if (cdat->built_buf_len <= 0) { /* Build later as not all data available yet */ cdat->to_update = true; - return; + return true; } 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; + return false; } /* Entry 0 for CDAT header, starts with Entry 1 */ @@ -94,8 +96,12 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) /* For now, no runtime updates */ cdat_header->sequence = 0; cdat_header->length += sizeof(CDATTableHeader); - sum += cdat_header->revision + cdat_header->sequence + - cdat_header->length; + + 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; @@ -103,80 +109,72 @@ static void ct3_build_cdat(CDATObject *cdat, Error **errp) cdat_st[0].length = sizeof(*cdat_header); cdat->entry_len = 1 + cdat->built_buf_len; cdat->entry = g_steal_pointer(&cdat_st); + return true; } -static void ct3_load_cdat(CDATObject *cdat, Error **errp) +static bool 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, file_size = 0; + int i = 0, ent = 1; + gsize file_size = 0; CDATSubHeader *hdr; - FILE *fp = NULL; + GError *error = NULL; /* Read CDAT file and create its cache */ - fp = fopen(cdat->filename, "r"); - if (!fp) { - error_setg(errp, "CDAT: Unable to open file"); - return; + 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 false; } - - fseek(fp, 0, SEEK_END); - file_size = ftell(fp); - fseek(fp, 0, SEEK_SET); - cdat->buf = g_malloc0(file_size); - - if (fread(cdat->buf, file_size, 1, fp) == 0) { - error_setg(errp, "CDAT: File read failed"); - return; - } - - fclose(fp); - if (file_size < sizeof(CDATTableHeader)) { error_setg(errp, "CDAT: File too short"); - return; + return false; } i = sizeof(CDATTableHeader); num_ent = 1; while (i < file_size) { - hdr = (CDATSubHeader *)(cdat->buf + i); + hdr = (CDATSubHeader *)(buf + i); + if (i + sizeof(CDATSubHeader) > file_size) { + error_setg(errp, "CDAT: Truncated table"); + return false; + } cdat_len_check(hdr, errp); i += hdr->length; + if (i > file_size) { + error_setg(errp, "CDAT: Truncated table"); + return false; + } num_ent++; } if (i != file_size) { - error_setg(errp, "CDAT: File length missmatch"); - return; + error_setg(errp, "CDAT: File length mismatch"); + return false; } - cdat_st = g_malloc0(sizeof(*cdat_st) * num_ent); - if (!cdat_st) { - error_setg(errp, "CDAT: Failed to allocate entry array"); - return; - } + cdat_st = g_new0(CDATEntry, num_ent); /* Set CDAT header, Entry = 0 */ - cdat_st[0].base = cdat->buf; + cdat_st[0].base = buf; cdat_st[0].length = sizeof(CDATTableHeader); i = 0; while (i < cdat_st[0].length) { - sum += cdat->buf[i++]; + sum += buf[i++]; } /* Read CDAT structures */ while (i < file_size) { - hdr = (CDATSubHeader *)(cdat->buf + i); - cdat_len_check(hdr, errp); - + hdr = (CDATSubHeader *)(buf + i); cdat_st[ent].base = hdr; cdat_st[ent].length = hdr->length; - while (cdat->buf + i < - (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { + while (buf + i < (uint8_t *)cdat_st[ent].base + cdat_st[ent].length) { assert(i < file_size); - sum += cdat->buf[i++]; + sum += buf[i++]; } ent++; @@ -187,16 +185,18 @@ static void ct3_load_cdat(CDATObject *cdat, Error **errp) } cdat->entry_len = num_ent; cdat->entry = g_steal_pointer(&cdat_st); + cdat->buf = g_steal_pointer(&buf); + return true; } -void cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) +bool cxl_doe_cdat_init(CXLComponentState *cxl_cstate, Error **errp) { CDATObject *cdat = &cxl_cstate->cdat; if (cdat->filename) { - ct3_load_cdat(cdat, errp); + return ct3_load_cdat(cdat, errp); } else { - ct3_build_cdat(cdat, errp); + return ct3_build_cdat(cdat, errp); } } @@ -218,7 +218,5 @@ void cxl_doe_cdat_release(CXLComponentState *cxl_cstate) cdat->free_cdat_table(cdat->built_buf, cdat->built_buf_len, cdat->private); } - if (cdat->buf) { - free(cdat->buf); - } + g_free(cdat->buf); } diff --git a/hw/cxl/cxl-component-utils.c b/hw/cxl/cxl-component-utils.c index 3edd303a33..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,23 +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, 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, @@ -165,36 +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 * 0x20] = 0xf0000000; + write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * hdm_inc] = + 0xf0000000; } else { - write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * 0x20] = 0xffffffff; + write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_LO + i * hdm_inc] = + 0xffffffff; } - write_msk[R_CXL_HDM_DECODER0_TARGET_LIST_HI + i * 0x20] = 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: @@ -209,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; @@ -220,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], \ @@ -238,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, type); - + 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 } @@ -315,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; @@ -362,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; } @@ -371,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) { @@ -388,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 83ce7a8270..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,18 +181,57 @@ 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 retval; + return cxl_dstate->memdev_status; } static void ro_reg_write(void *opaque, hwaddr offset, uint64_t value, @@ -207,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", @@ -217,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", @@ -236,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..12dee2e467 --- /dev/null +++ b/hw/cxl/cxl-events.c @@ -0,0 +1,262 @@ +/* + * 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; +} + +void cxl_discard_all_event_records(CXLDeviceState *cxlds) +{ + CXLEventLogType log_type; + CXLEventLog *log; + + for (log_type = 0; log_type < CXL_EVENT_TYPE_MAX; log_type++) { + log = &cxlds->event_logs[log_type]; + while (!cxl_event_empty(log)) { + cxl_event_delete_head(cxlds, log_type, log); + } + } +} + +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.c b/hw/cxl/cxl-host.c index 1adf61231a..e9f2543c43 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -26,6 +26,7 @@ static void cxl_fixed_memory_window_config(CXLState *cxl_state, CXLFixedMemoryWindowOptions *object, Error **errp) { + ERRP_GUARD(); g_autofree CXLFixedWindow *fw = g_malloc0(sizeof(*fw)); strList *target; int i; @@ -39,15 +40,9 @@ static void cxl_fixed_memory_window_config(CXLState *cxl_state, 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; @@ -64,6 +59,12 @@ static void cxl_fixed_memory_window_config(CXLState *cxl_state, fw->enc_int_gran = 0; } + 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)); @@ -84,7 +85,7 @@ void cxl_fmws_link_targets(CXLState *cxl_state, 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,33 +98,58 @@ void cxl_fmws_link_targets(CXLState *cxl_state, 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_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 = extract32(cache_mem[R_CXL_HDM_DECODER0_TARGET_LIST_LO], - target_idx * 8, 8); - } else { - *target = extract32(cache_mem[R_CXL_HDM_DECODER0_TARGET_LIST_HI], - (target_idx - 4) * 8, 8); - } - - return true; + return found; } static PCIDevice *cxl_cfmws_find_device(CXLFixedWindow *fw, hwaddr addr) @@ -141,26 +167,33 @@ 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) { - return NULL; - } + 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; + cache_mem = hb_cstate->crb.cache_mem_registers; - target_found = cxl_hdm_find_target(cache_mem, addr, &target); - if (!target_found) { - return NULL; - } + 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; + rp = pcie_find_port_by_pn(hb->bus, target); + if (!rp) { + return NULL; + } } d = pci_bridge_get_sec_bus(PCI_BRIDGE(rp))->devices[0]; @@ -282,7 +315,8 @@ static void machine_set_cxl(Object *obj, Visitor *v, const char *name, static void machine_get_cfmw(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - CXLFixedMemoryWindowOptionsList **list = opaque; + CXLState *state = opaque; + CXLFixedMemoryWindowOptionsList **list = &state->cfmw_list; visit_type_CXLFixedMemoryWindowOptionsList(v, name, list, errp); } diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index bc1bb18844..ce9aa18364 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -8,11 +8,24 @@ */ #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/cxl/cxl_mailbox.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" +#include "qemu/range.h" + +#define CXL_CAPACITY_MULTIPLIER (256 * MiB) +#define CXL_DC_EVENT_LOG_SIZE 8 +#define CXL_NUM_EXTENTS_SUPPORTED 512 +#define CXL_NUM_TAGS_SUPPORTED 0 /* * How to add a new command, example. The command set FOO, with cmd BAR. @@ -20,7 +33,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 +48,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 @@ -47,85 +63,592 @@ enum { #define SET_INTERRUPT_POLICY 0x3 FIRMWARE_UPDATE = 0x02, #define GET_INFO 0x0 + #define TRANSFER 0x1 + #define ACTIVATE 0x2 TIMESTAMP = 0x03, #define GET 0x0 #define SET 0x1 LOGS = 0x04, #define GET_SUPPORTED 0x0 #define GET_LOG 0x1 + FEATURES = 0x05, + #define GET_SUPPORTED 0x0 + #define GET_FEATURE 0x1 + #define SET_FEATURE 0x2 IDENTIFY = 0x40, #define MEMORY_DEVICE 0x0 CCLS = 0x41, #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 + #define GET_SCAN_MEDIA_CAPABILITIES 0x3 + #define SCAN_MEDIA 0x4 + #define GET_SCAN_MEDIA_RESULTS 0x5 + DCD_CONFIG = 0x48, + #define GET_DC_CONFIG 0x0 + #define GET_DYN_CAP_EXT_LIST 0x1 + #define ADD_DYN_CAP_RSP 0x2 + #define RELEASE_DYN_CAP 0x3 + 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; + + if (len_in < sizeof(*in)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + /* 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; + + 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; + + if (len_in < sizeof(*pl) || + len_in < sizeof(*pl) + sizeof(*pl->handle) * pl->nr_recs) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + *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; + + 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; + 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; + + if (len_in < sizeof(*in)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + /* 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; + 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; +} + +#define CXL_FW_SLOTS 2 +#define CXL_FW_SIZE 0x02000000 /* 32 mb */ + +/* 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) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; struct { uint8_t slots_supported; uint8_t slot_info; @@ -138,64 +661,253 @@ 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 (!QEMU_IS_ALIGNED(cxl_dstate->vmem_size, CXL_CAPACITY_MULTIPLIER) || + !QEMU_IS_ALIGNED(cxl_dstate->pmem_size, CXL_CAPACITY_MULTIPLIER) || + !QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER)) { return CXL_MBOX_INTERNAL_ERROR; } - fw_info = (void *)cmd->payload; - memset(fw_info, 0, sizeof(*fw_info)); + fw_info = (void *)payload_out; - fw_info->slots_supported = 2; - fw_info->slot_info = BIT(0) | BIT(3); - fw_info->caps = 0; - pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); + fw_info->slots_supported = CXL_FW_SLOTS; + fw_info->slot_info = (cci->fw.active_slot & 0x7) | + ((cci->fw.staged_slot & 0x7) << 3); + fw_info->caps = BIT(0); /* online update supported */ - *len = 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) -{ - uint64_t time, delta; - uint64_t final_time = 0; - - 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; + if (cci->fw.slot[0]) { + pstrcpy(fw_info->fw_rev1, sizeof(fw_info->fw_rev1), "BWFW VERSION 0"); + } + if (cci->fw.slot[1]) { + pstrcpy(fw_info->fw_rev2, sizeof(fw_info->fw_rev2), "BWFW VERSION 1"); } - /* Then adjust the actual time */ - stq_le_p(cmd->payload, final_time); - *len = 8; + *len_out = sizeof(*fw_info); + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.3.2: Transfer FW (Opcode 0201h) */ +#define CXL_FW_XFER_ALIGNMENT 128 + +#define CXL_FW_XFER_ACTION_FULL 0x0 +#define CXL_FW_XFER_ACTION_INIT 0x1 +#define CXL_FW_XFER_ACTION_CONTINUE 0x2 +#define CXL_FW_XFER_ACTION_END 0x3 +#define CXL_FW_XFER_ACTION_ABORT 0x4 + +static CXLRetCode cmd_firmware_update_transfer(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t action; + uint8_t slot; + uint8_t rsvd1[2]; + uint32_t offset; + uint8_t rsvd2[0x78]; + uint8_t data[]; + } QEMU_PACKED *fw_transfer = (void *)payload_in; + size_t offset, length; + + if (len < sizeof(*fw_transfer)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + if (fw_transfer->action == CXL_FW_XFER_ACTION_ABORT) { + /* + * At this point there aren't any on-going transfers + * running in the bg - this is serialized before this + * call altogether. Just mark the state machine and + * disregard any other input. + */ + cci->fw.transferring = false; + return CXL_MBOX_SUCCESS; + } + + offset = fw_transfer->offset * CXL_FW_XFER_ALIGNMENT; + length = len - sizeof(*fw_transfer); + if (offset + length > CXL_FW_SIZE) { + return CXL_MBOX_INVALID_INPUT; + } + + if (cci->fw.transferring) { + if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL || + fw_transfer->action == CXL_FW_XFER_ACTION_INIT) { + return CXL_MBOX_FW_XFER_IN_PROGRESS; + } + /* + * Abort partitioned package transfer if over 30 secs + * between parts. As opposed to the explicit ABORT action, + * semantically treat this condition as an error - as + * if a part action were passed without a previous INIT. + */ + if (difftime(time(NULL), cci->fw.last_partxfer) > 30.0) { + cci->fw.transferring = false; + return CXL_MBOX_INVALID_INPUT; + } + } else if (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE || + fw_transfer->action == CXL_FW_XFER_ACTION_END) { + return CXL_MBOX_INVALID_INPUT; + } + + /* allow back-to-back retransmission */ + if ((offset != cci->fw.prev_offset || length != cci->fw.prev_len) && + (fw_transfer->action == CXL_FW_XFER_ACTION_CONTINUE || + fw_transfer->action == CXL_FW_XFER_ACTION_END)) { + /* verify no overlaps */ + if (offset < cci->fw.prev_offset + cci->fw.prev_len) { + return CXL_MBOX_FW_XFER_OUT_OF_ORDER; + } + } + + switch (fw_transfer->action) { + case CXL_FW_XFER_ACTION_FULL: /* ignores offset */ + case CXL_FW_XFER_ACTION_END: + if (fw_transfer->slot == 0 || + fw_transfer->slot == cci->fw.active_slot || + fw_transfer->slot > CXL_FW_SLOTS) { + return CXL_MBOX_FW_INVALID_SLOT; + } + + /* mark the slot used upon bg completion */ + break; + case CXL_FW_XFER_ACTION_INIT: + if (offset != 0) { + return CXL_MBOX_INVALID_INPUT; + } + + cci->fw.transferring = true; + cci->fw.prev_offset = offset; + cci->fw.prev_len = length; + break; + case CXL_FW_XFER_ACTION_CONTINUE: + cci->fw.prev_offset = offset; + cci->fw.prev_len = length; + break; + default: + return CXL_MBOX_INVALID_INPUT; + } + + if (fw_transfer->action == CXL_FW_XFER_ACTION_FULL) { + cci->bg.runtime = 10 * 1000UL; + } else { + cci->bg.runtime = 2 * 1000UL; + } + /* keep relevant context for bg completion */ + cci->fw.curr_action = fw_transfer->action; + cci->fw.curr_slot = fw_transfer->slot; + *len_out = 0; + + return CXL_MBOX_BG_STARTED; +} + +static void __do_firmware_xfer(CXLCCI *cci) +{ + switch (cci->fw.curr_action) { + case CXL_FW_XFER_ACTION_FULL: + case CXL_FW_XFER_ACTION_END: + cci->fw.slot[cci->fw.curr_slot - 1] = true; + cci->fw.transferring = false; + break; + case CXL_FW_XFER_ACTION_INIT: + case CXL_FW_XFER_ACTION_CONTINUE: + time(&cci->fw.last_partxfer); + break; + default: + break; + } +} + +/* CXL r3.1 section 8.2.9.3.3: Activate FW (Opcode 0202h) */ +static CXLRetCode cmd_firmware_update_activate(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t action; + uint8_t slot; + } QEMU_PACKED *fw_activate = (void *)payload_in; + QEMU_BUILD_BUG_ON(sizeof(*fw_activate) != 0x2); + + if (fw_activate->slot == 0 || + fw_activate->slot == cci->fw.active_slot || + fw_activate->slot > CXL_FW_SLOTS) { + return CXL_MBOX_FW_INVALID_SLOT; + } + + /* ensure that an actual fw package is there */ + if (!cci->fw.slot[fw_activate->slot - 1]) { + return CXL_MBOX_FW_INVALID_SLOT; + } + + switch (fw_activate->action) { + case 0: /* online */ + cci->fw.active_slot = fw_activate->slot; + break; + case 1: /* reset */ + cci->fw.staged_slot = fw_activate->slot; + break; + default: + return CXL_MBOX_INVALID_INPUT; + } 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.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) { + CXLDeviceState *cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; + uint64_t final_time = cxl_device_get_timestamp(cxl_dstate); + + stq_le_p(payload_out, final_time); + *len_out = 8; + + return CXL_MBOX_SUCCESS; +} + +/* 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 +916,463 @@ 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; - /* - * 8.2.9.4.2 - * The device shall return Invalid Parameter 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) { + get_log = (void *)payload_in; + + if (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; + } + + /* + * 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 Log. + * + * Only valid for there to be one entry per opcode, but the length + offset + * may still be greater than that if the inputs are not valid and so access + * beyond the end of cci->cel_log. + */ + if ((uint64_t)get_log->offset + get_log->length >= sizeof(cci->cel_log)) { + return CXL_MBOX_INVALID_INPUT; } /* 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.6: Features */ +/* + * Get Supported Features output payload + * CXL r3.1 section 8.2.9.6.1 Table 8-96 + */ +typedef struct CXLSupportedFeatureHeader { + uint16_t entries; + uint16_t nsuppfeats_dev; + uint32_t reserved; +} QEMU_PACKED CXLSupportedFeatureHeader; + +/* + * Get Supported Features Supported Feature Entry + * CXL r3.1 section 8.2.9.6.1 Table 8-97 + */ +typedef struct CXLSupportedFeatureEntry { + QemuUUID uuid; + uint16_t feat_index; + uint16_t get_feat_size; + uint16_t set_feat_size; + uint32_t attr_flags; + uint8_t get_feat_version; + uint8_t set_feat_version; + uint16_t set_feat_effects; + uint8_t rsvd[18]; +} QEMU_PACKED CXLSupportedFeatureEntry; + +/* + * Get Supported Features Supported Feature Entry + * CXL rev 3.1 section 8.2.9.6.1 Table 8-97 + */ +/* Supported Feature Entry : attribute flags */ +#define CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE BIT(0) +#define CXL_FEAT_ENTRY_ATTR_FLAG_DEEPEST_RESET_PERSISTENCE_MASK GENMASK(3, 1) +#define CXL_FEAT_ENTRY_ATTR_FLAG_PERSIST_ACROSS_FIRMWARE_UPDATE BIT(4) +#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SELECTION BIT(5) +#define CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_SAVED_SELECTION BIT(6) + +/* Supported Feature Entry : set feature effects */ +#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_COLD_RESET BIT(0) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE BIT(1) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_DATA_CHANGE BIT(2) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_POLICY_CHANGE BIT(3) +#define CXL_FEAT_ENTRY_SFE_IMMEDIATE_LOG_CHANGE BIT(4) +#define CXL_FEAT_ENTRY_SFE_SECURITY_STATE_CHANGE BIT(5) +#define CXL_FEAT_ENTRY_SFE_BACKGROUND_OPERATION BIT(6) +#define CXL_FEAT_ENTRY_SFE_SUPPORT_SECONDARY_MAILBOX BIT(7) +#define CXL_FEAT_ENTRY_SFE_SUPPORT_ABORT_BACKGROUND_OPERATION BIT(8) +#define CXL_FEAT_ENTRY_SFE_CEL_VALID BIT(9) +#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CONV_RESET BIT(10) +#define CXL_FEAT_ENTRY_SFE_CONFIG_CHANGE_CXL_RESET BIT(11) + +enum CXL_SUPPORTED_FEATURES_LIST { + CXL_FEATURE_PATROL_SCRUB = 0, + CXL_FEATURE_ECS, + CXL_FEATURE_MAX +}; + +/* Get Feature CXL 3.1 Spec 8.2.9.6.2 */ +/* + * Get Feature input payload + * CXL r3.1 section 8.2.9.6.2 Table 8-99 + */ +/* Get Feature : Payload in selection */ +enum CXL_GET_FEATURE_SELECTION { + CXL_GET_FEATURE_SEL_CURRENT_VALUE, + CXL_GET_FEATURE_SEL_DEFAULT_VALUE, + CXL_GET_FEATURE_SEL_SAVED_VALUE, + CXL_GET_FEATURE_SEL_MAX +}; + +/* Set Feature CXL 3.1 Spec 8.2.9.6.3 */ +/* + * Set Feature input payload + * CXL r3.1 section 8.2.9.6.3 Table 8-101 + */ +typedef struct CXLSetFeatureInHeader { + QemuUUID uuid; + uint32_t flags; + uint16_t offset; + uint8_t version; + uint8_t rsvd[9]; +} QEMU_PACKED QEMU_ALIGNED(16) CXLSetFeatureInHeader; + +/* Set Feature : Payload in flags */ +#define CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK 0x7 +enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER { + CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER, + CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX +}; +#define CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET BIT(3) + +/* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */ +static const QemuUUID patrol_scrub_uuid = { + .data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33, + 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a) +}; + +typedef struct CXLMemPatrolScrubSetFeature { + CXLSetFeatureInHeader hdr; + CXLMemPatrolScrubWriteAttrs feat_data; +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature; + +/* + * CXL r3.1 section 8.2.9.9.11.2: + * DDR5 Error Check Scrub (ECS) Control Feature + */ +static const QemuUUID ecs_uuid = { + .data = UUID(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba, + 0xb9, 0x69, 0x1e, 0x89, 0x33, 0x86) +}; + +typedef struct CXLMemECSSetFeature { + CXLSetFeatureInHeader hdr; + CXLMemECSWriteAttrs feat_data[]; +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemECSSetFeature; + +/* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */ +static CXLRetCode cmd_features_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 { + uint32_t count; + uint16_t start_index; + uint16_t reserved; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_in = (void *)payload_in; + + struct { + CXLSupportedFeatureHeader hdr; + CXLSupportedFeatureEntry feat_entries[]; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_out = (void *)payload_out; + uint16_t index, req_entries; + uint16_t entry; + + if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + return CXL_MBOX_UNSUPPORTED; + } + if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) || + get_feats_in->start_index >= CXL_FEATURE_MAX) { + return CXL_MBOX_INVALID_INPUT; + } + + req_entries = (get_feats_in->count - + sizeof(CXLSupportedFeatureHeader)) / + sizeof(CXLSupportedFeatureEntry); + req_entries = MIN(req_entries, + (CXL_FEATURE_MAX - get_feats_in->start_index)); + + for (entry = 0, index = get_feats_in->start_index; + entry < req_entries; index++) { + switch (index) { + case CXL_FEATURE_PATROL_SCRUB: + /* Fill supported feature entry for device patrol scrub control */ + get_feats_out->feat_entries[entry++] = + (struct CXLSupportedFeatureEntry) { + .uuid = patrol_scrub_uuid, + .feat_index = index, + .get_feat_size = sizeof(CXLMemPatrolScrubReadAttrs), + .set_feat_size = sizeof(CXLMemPatrolScrubWriteAttrs), + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE, + .get_feat_version = CXL_MEMDEV_PS_GET_FEATURE_VERSION, + .set_feat_version = CXL_MEMDEV_PS_SET_FEATURE_VERSION, + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE | + CXL_FEAT_ENTRY_SFE_CEL_VALID, + }; + break; + case CXL_FEATURE_ECS: + /* Fill supported feature entry for device DDR5 ECS control */ + get_feats_out->feat_entries[entry++] = + (struct CXLSupportedFeatureEntry) { + .uuid = ecs_uuid, + .feat_index = index, + .get_feat_size = sizeof(CXLMemECSReadAttrs), + .set_feat_size = sizeof(CXLMemECSWriteAttrs), + .attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE, + .get_feat_version = CXL_ECS_GET_FEATURE_VERSION, + .set_feat_version = CXL_ECS_SET_FEATURE_VERSION, + .set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE | + CXL_FEAT_ENTRY_SFE_CEL_VALID, + }; + break; + default: + __builtin_unreachable(); + } + } + get_feats_out->hdr.nsuppfeats_dev = CXL_FEATURE_MAX; + get_feats_out->hdr.entries = req_entries; + *len_out = sizeof(CXLSupportedFeatureHeader) + + req_entries * sizeof(CXLSupportedFeatureEntry); + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.6.2: Get Feature (Opcode 0501h) */ +static CXLRetCode cmd_features_get_feature(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; + uint16_t offset; + uint16_t count; + uint8_t selection; + } QEMU_PACKED QEMU_ALIGNED(16) * get_feature; + uint16_t bytes_to_copy = 0; + CXLType3Dev *ct3d; + CXLSetFeatureInfo *set_feat_info; + + if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + return CXL_MBOX_UNSUPPORTED; + } + + ct3d = CXL_TYPE3(cci->d); + get_feature = (void *)payload_in; + + set_feat_info = &ct3d->set_feat_info; + if (qemu_uuid_is_equal(&get_feature->uuid, &set_feat_info->uuid)) { + return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS; + } + + if (get_feature->selection != CXL_GET_FEATURE_SEL_CURRENT_VALUE) { + return CXL_MBOX_UNSUPPORTED; + } + if (get_feature->offset + get_feature->count > cci->payload_max) { + return CXL_MBOX_INVALID_INPUT; + } + + if (qemu_uuid_is_equal(&get_feature->uuid, &patrol_scrub_uuid)) { + if (get_feature->offset >= sizeof(CXLMemPatrolScrubReadAttrs)) { + return CXL_MBOX_INVALID_INPUT; + } + bytes_to_copy = sizeof(CXLMemPatrolScrubReadAttrs) - + get_feature->offset; + bytes_to_copy = MIN(bytes_to_copy, get_feature->count); + memcpy(payload_out, + (uint8_t *)&ct3d->patrol_scrub_attrs + get_feature->offset, + bytes_to_copy); + } else if (qemu_uuid_is_equal(&get_feature->uuid, &ecs_uuid)) { + if (get_feature->offset >= sizeof(CXLMemECSReadAttrs)) { + return CXL_MBOX_INVALID_INPUT; + } + bytes_to_copy = sizeof(CXLMemECSReadAttrs) - get_feature->offset; + bytes_to_copy = MIN(bytes_to_copy, get_feature->count); + memcpy(payload_out, + (uint8_t *)&ct3d->ecs_attrs + get_feature->offset, + bytes_to_copy); + } else { + return CXL_MBOX_UNSUPPORTED; + } + + *len_out = bytes_to_copy; + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.6.3: Set Feature (Opcode 0502h) */ +static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLSetFeatureInHeader *hdr = (void *)payload_in; + CXLMemPatrolScrubWriteAttrs *ps_write_attrs; + CXLMemPatrolScrubSetFeature *ps_set_feature; + CXLMemECSWriteAttrs *ecs_write_attrs; + CXLMemECSSetFeature *ecs_set_feature; + CXLSetFeatureInfo *set_feat_info; + uint16_t bytes_to_copy = 0; + uint8_t data_transfer_flag; + CXLType3Dev *ct3d; + uint16_t count; + + if (len_in < sizeof(*hdr)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + if (!object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + return CXL_MBOX_UNSUPPORTED; + } + ct3d = CXL_TYPE3(cci->d); + set_feat_info = &ct3d->set_feat_info; + + if (!qemu_uuid_is_null(&set_feat_info->uuid) && + !qemu_uuid_is_equal(&hdr->uuid, &set_feat_info->uuid)) { + return CXL_MBOX_FEATURE_TRANSFER_IN_PROGRESS; + } + if (hdr->flags & CXL_SET_FEAT_DATA_SAVED_ACROSS_RESET) { + set_feat_info->data_saved_across_reset = true; + } else { + set_feat_info->data_saved_across_reset = false; + } + + data_transfer_flag = + hdr->flags & CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK; + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER) { + set_feat_info->uuid = hdr->uuid; + set_feat_info->data_size = 0; + } + set_feat_info->data_transfer_flag = data_transfer_flag; + set_feat_info->data_offset = hdr->offset; + bytes_to_copy = len_in - sizeof(CXLSetFeatureInHeader); + + if (bytes_to_copy == 0) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { + if (hdr->version != CXL_MEMDEV_PS_SET_FEATURE_VERSION) { + return CXL_MBOX_UNSUPPORTED; + } + + ps_set_feature = (void *)payload_in; + ps_write_attrs = &ps_set_feature->feat_data; + + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->patrol_scrub_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset, + ps_write_attrs, + bytes_to_copy); + set_feat_info->data_size += bytes_to_copy; + + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) { + ct3d->patrol_scrub_attrs.scrub_cycle &= ~0xFF; + ct3d->patrol_scrub_attrs.scrub_cycle |= + ct3d->patrol_scrub_wr_attrs.scrub_cycle_hr & 0xFF; + ct3d->patrol_scrub_attrs.scrub_flags &= ~0x1; + ct3d->patrol_scrub_attrs.scrub_flags |= + ct3d->patrol_scrub_wr_attrs.scrub_flags & 0x1; + } + } else if (qemu_uuid_is_equal(&hdr->uuid, + &ecs_uuid)) { + if (hdr->version != CXL_ECS_SET_FEATURE_VERSION) { + return CXL_MBOX_UNSUPPORTED; + } + + ecs_set_feature = (void *)payload_in; + ecs_write_attrs = ecs_set_feature->feat_data; + + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->ecs_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + memcpy((uint8_t *)&ct3d->ecs_wr_attrs + hdr->offset, + ecs_write_attrs, + bytes_to_copy); + set_feat_info->data_size += bytes_to_copy; + + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) { + ct3d->ecs_attrs.ecs_log_cap = ct3d->ecs_wr_attrs.ecs_log_cap; + for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) { + ct3d->ecs_attrs.fru_attrs[count].ecs_config = + ct3d->ecs_wr_attrs.fru_attrs[count].ecs_config & 0x1F; + } + } + } else { + return CXL_MBOX_UNSUPPORTED; + } + + if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER || + data_transfer_flag == CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) { + memset(&set_feat_info->uuid, 0, sizeof(QemuUUID)); + if (qemu_uuid_is_equal(&hdr->uuid, &patrol_scrub_uuid)) { + memset(&ct3d->patrol_scrub_wr_attrs, 0, set_feat_info->data_size); + } else if (qemu_uuid_is_equal(&hdr->uuid, &ecs_uuid)) { + memset(&ct3d->ecs_wr_attrs, 0, set_feat_info->data_size); + } + set_feat_info->data_transfer_flag = 0; + set_feat_info->data_saved_across_reset = false; + set_feat_info->data_offset = 0; + set_feat_info->data_size = 0; + } + + return CXL_MBOX_SUCCESS; +} + +/* 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]; @@ -276,203 +1389,1678 @@ static ret_code cmd_identify_memory_device(struct cxl_cmd *cmd, uint16_t inject_poison_limit; uint8_t poison_caps; uint8_t qos_telemetry_caps; + uint16_t dc_event_log_size; } QEMU_PACKED *id; - QEMU_BUILD_BUG_ON(sizeof(*id) != 0x43); - - CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, cxl_dstate); + QEMU_BUILD_BUG_ON(sizeof(*id) != 0x45); + 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)) || + (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, CXL_CAPACITY_MULTIPLIER))) { return CXL_MBOX_INTERNAL_ERROR; } - id = (void *)cmd->payload; - memset(id, 0, sizeof(*id)); + id = (void *)payload_out; - /* 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->static_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); + stw_le_p(&id->dc_event_log_size, CXL_DC_EVENT_LOG_SIZE); - *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; + CXLType3Dev *ct3d = container_of(cxl_dstate, CXLType3Dev, 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)) || + (!QEMU_IS_ALIGNED(ct3d->dc.total_capacity, 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; + uint64_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) { - return CXL_MBOX_SUCCESS; + *len_out = 0; + if (len_in < hdr_len) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; } - 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, plen, set_lsa_payload->offset); + cvc->set_lsa(ct3d, set_lsa_payload->data, len_in, set_lsa_payload->offset); return CXL_MBOX_SUCCESS; } -#define IMMEDIATE_CONFIG_CHANGE (1 << 1) -#define IMMEDIATE_DATA_CHANGE (1 << 2) -#define IMMEDIATE_POLICY_CHANGE (1 << 3) -#define IMMEDIATE_LOG_CHANGE (1 << 4) +/* Perform the actual device zeroing */ +static void __do_sanitization(CXLType3Dev *ct3d) +{ + MemoryRegion *mr; -static struct cxl_cmd cxl_cmd_set[256][256] = { + 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_discard_all_event_records(&ct3d->cxl_dstate); +} + +/* + * 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 (!ranges_overlap(ent->start, ent->length, + query_start, query_length)) { + continue; + } + record_count++; + } + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + QLIST_FOREACH(ent, poison_list, node) { + uint64_t start, stop; + + /* Check for no overlap */ + if (!ranges_overlap(ent->start, ent->length, + query_start, query_length)) { + 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); + } + if (scan_media_running(cci)) { + out->flags |= (1 << 2); + } + + 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; + } + } + /* + * Freeze the list if there is an on-going scan media operation. + */ + if (scan_media_running(cci)) { + /* + * XXX: Spec is ambiguous - is this case considered + * a successful return despite not adding to the list? + */ + goto 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++; +success: + *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->static_mem_size + + ct3d->dc.total_capacity) { + 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; + } + } + + /* + * Freeze the list if there is an on-going scan media operation. + */ + if (scan_media_running(cci)) { + /* + * XXX: Spec is ambiguous - is this case considered + * a successful return despite not removing from the list? + */ + goto success; + } + + 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) { + goto 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); +success: + *len_out = 0; + + return CXL_MBOX_SUCCESS; +} + +/* + * CXL r3.1 section 8.2.9.9.4.4: Get Scan Media Capabilities + */ +static CXLRetCode +cmd_media_get_scan_media_capabilities(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_scan_media_capabilities_pl { + uint64_t pa; + uint64_t length; + } QEMU_PACKED; + + struct get_scan_media_capabilities_out_pl { + uint32_t estimated_runtime_ms; + }; + + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + struct get_scan_media_capabilities_pl *in = (void *)payload_in; + struct get_scan_media_capabilities_out_pl *out = (void *)payload_out; + uint64_t query_start; + uint64_t query_length; + + 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; + + if (query_start + query_length > cxl_dstate->static_mem_size) { + return CXL_MBOX_INVALID_PA; + } + + /* + * Just use 400 nanosecond access/read latency + 100 ns for + * the cost of updating the poison list. For small enough + * chunks return at least 1 ms. + */ + stl_le_p(&out->estimated_runtime_ms, + MAX(1, query_length * (0.0005L / 64))); + + *len_out = sizeof(*out); + return CXL_MBOX_SUCCESS; +} + +static void __do_scan_media(CXLType3Dev *ct3d) +{ + CXLPoison *ent; + unsigned int results_cnt = 0; + + QLIST_FOREACH(ent, &ct3d->scan_media_results, node) { + results_cnt++; + } + + /* only scan media may clear the overflow */ + if (ct3d->poison_list_overflowed && + ct3d->poison_list_cnt == results_cnt) { + cxl_clear_poison_list_overflowed(ct3d); + } + /* scan media has run since last conventional reset */ + ct3d->scan_media_hasrun = true; +} + +/* + * CXL r3.1 section 8.2.9.9.4.5: Scan Media + */ +static CXLRetCode cmd_media_scan_media(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct scan_media_pl { + uint64_t pa; + uint64_t length; + uint8_t flags; + } QEMU_PACKED; + + struct scan_media_pl *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDeviceState *cxl_dstate = &ct3d->cxl_dstate; + uint64_t query_start; + uint64_t query_length; + CXLPoison *ent, *next; + + 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; + + if (query_start + query_length > cxl_dstate->static_mem_size) { + return CXL_MBOX_INVALID_PA; + } + if (ct3d->dc.num_regions && query_start + query_length >= + cxl_dstate->static_mem_size + ct3d->dc.total_capacity) { + return CXL_MBOX_INVALID_PA; + } + + if (in->flags == 0) { /* TODO */ + qemu_log_mask(LOG_UNIMP, + "Scan Media Event Log is unsupported\n"); + } + + /* any previous results are discarded upon a new Scan Media */ + QLIST_FOREACH_SAFE(ent, &ct3d->scan_media_results, node, next) { + QLIST_REMOVE(ent, node); + g_free(ent); + } + + /* kill the poison list - it will be recreated */ + if (ct3d->poison_list_overflowed) { + QLIST_FOREACH_SAFE(ent, &ct3d->poison_list, node, next) { + QLIST_REMOVE(ent, node); + g_free(ent); + ct3d->poison_list_cnt--; + } + } + + /* + * Scan the backup list and move corresponding entries + * into the results list, updating the poison list + * when possible. + */ + QLIST_FOREACH_SAFE(ent, &ct3d->poison_list_bkp, node, next) { + CXLPoison *res; + + if (ent->start >= query_start + query_length || + ent->start + ent->length <= query_start) { + continue; + } + + /* + * If a Get Poison List cmd comes in while this + * scan is being done, it will see the new complete + * list, while setting the respective flag. + */ + if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) { + CXLPoison *p = g_new0(CXLPoison, 1); + + p->start = ent->start; + p->length = ent->length; + p->type = ent->type; + QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); + ct3d->poison_list_cnt++; + } + + res = g_new0(CXLPoison, 1); + res->start = ent->start; + res->length = ent->length; + res->type = ent->type; + QLIST_INSERT_HEAD(&ct3d->scan_media_results, res, node); + + QLIST_REMOVE(ent, node); + g_free(ent); + } + + cci->bg.runtime = MAX(1, query_length * (0.0005L / 64)); + *len_out = 0; + + return CXL_MBOX_BG_STARTED; +} + +/* + * CXL r3.1 section 8.2.9.9.4.6: Get Scan Media Results + */ +static CXLRetCode cmd_media_get_scan_media_results(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_scan_media_results_out_pl { + uint64_t dpa_restart; + uint64_t length; + uint8_t flags; + uint8_t rsvd1; + uint16_t count; + uint8_t rsvd2[0xc]; + struct { + uint64_t addr; + uint32_t length; + uint32_t resv; + } QEMU_PACKED records[]; + } QEMU_PACKED; + + struct get_scan_media_results_out_pl *out = (void *)payload_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLPoisonList *scan_media_results = &ct3d->scan_media_results; + CXLPoison *ent, *next; + uint16_t total_count = 0, record_count = 0, i = 0; + uint16_t out_pl_len; + + if (!ct3d->scan_media_hasrun) { + return CXL_MBOX_UNSUPPORTED; + } + + /* + * Calculate limits, all entries are within the same address range of the + * last scan media call. + */ + QLIST_FOREACH(ent, scan_media_results, node) { + size_t rec_size = record_count * sizeof(out->records[0]); + + if (sizeof(*out) + rec_size < CXL_MAILBOX_MAX_PAYLOAD_SIZE) { + record_count++; + } + total_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_SAFE(ent, scan_media_results, node, next) { + uint64_t start, stop; + + if (i == record_count) { + break; + } + + start = ROUND_DOWN(ent->start, 64ull); + stop = ROUND_DOWN(ent->start, 64ull) + ent->length; + stq_le_p(&out->records[i].addr, start); + stl_le_p(&out->records[i].length, (stop - start) / CXL_CACHE_LINE_SIZE); + i++; + + /* consume the returning entry */ + QLIST_REMOVE(ent, node); + g_free(ent); + } + + stw_le_p(&out->count, record_count); + if (total_count > record_count) { + out->flags = (1 << 0); /* More Media Error Records */ + } + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + +/* + * CXL r3.1 section 8.2.9.9.9.1: Get Dynamic Capacity Configuration + * (Opcode: 4800h) + */ +static CXLRetCode cmd_dcd_get_dyn_cap_config(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); + struct { + uint8_t region_cnt; + uint8_t start_rid; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint8_t num_regions; + uint8_t regions_returned; + uint8_t rsvd1[6]; + struct { + uint64_t base; + uint64_t decode_len; + uint64_t region_len; + uint64_t block_size; + uint32_t dsmadhandle; + uint8_t flags; + uint8_t rsvd2[3]; + } QEMU_PACKED records[]; + } QEMU_PACKED *out = (void *)payload_out; + struct { + uint32_t num_extents_supported; + uint32_t num_extents_available; + uint32_t num_tags_supported; + uint32_t num_tags_available; + } QEMU_PACKED *extra_out; + uint16_t record_count; + uint16_t i; + uint16_t out_pl_len; + uint8_t start_rid; + + start_rid = in->start_rid; + if (start_rid >= ct3d->dc.num_regions) { + return CXL_MBOX_INVALID_INPUT; + } + + record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt); + + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + extra_out = (void *)(payload_out + out_pl_len); + out_pl_len += sizeof(*extra_out); + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + out->num_regions = ct3d->dc.num_regions; + out->regions_returned = record_count; + for (i = 0; i < record_count; i++) { + stq_le_p(&out->records[i].base, + ct3d->dc.regions[start_rid + i].base); + stq_le_p(&out->records[i].decode_len, + ct3d->dc.regions[start_rid + i].decode_len / + CXL_CAPACITY_MULTIPLIER); + stq_le_p(&out->records[i].region_len, + ct3d->dc.regions[start_rid + i].len); + stq_le_p(&out->records[i].block_size, + ct3d->dc.regions[start_rid + i].block_size); + stl_le_p(&out->records[i].dsmadhandle, + ct3d->dc.regions[start_rid + i].dsmadhandle); + out->records[i].flags = ct3d->dc.regions[start_rid + i].flags; + } + /* + * TODO: Assign values once extents and tags are introduced + * to use. + */ + stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED); + stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED - + ct3d->dc.total_extent_count); + stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED); + stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED); + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + +/* + * CXL r3.1 section 8.2.9.9.9.2: + * Get Dynamic Capacity Extent List (Opcode 4801h) + */ +static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(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); + struct { + uint32_t extent_cnt; + uint32_t start_extent_id; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint32_t count; + uint32_t total_extents; + uint32_t generation_num; + uint8_t rsvd[4]; + CXLDCExtentRaw records[]; + } QEMU_PACKED *out = (void *)payload_out; + uint32_t start_extent_id = in->start_extent_id; + CXLDCExtentList *extent_list = &ct3d->dc.extents; + uint16_t record_count = 0, i = 0, record_done = 0; + uint16_t out_pl_len, size; + CXLDCExtent *ent; + + if (start_extent_id > ct3d->dc.total_extent_count) { + return CXL_MBOX_INVALID_INPUT; + } + + record_count = MIN(in->extent_cnt, + ct3d->dc.total_extent_count - start_extent_id); + size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out); + record_count = MIN(record_count, size / sizeof(out->records[0])); + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + + stl_le_p(&out->count, record_count); + stl_le_p(&out->total_extents, ct3d->dc.total_extent_count); + stl_le_p(&out->generation_num, ct3d->dc.ext_list_gen_seq); + + if (record_count > 0) { + CXLDCExtentRaw *out_rec = &out->records[record_done]; + + QTAILQ_FOREACH(ent, extent_list, node) { + if (i++ < start_extent_id) { + continue; + } + stq_le_p(&out_rec->start_dpa, ent->start_dpa); + stq_le_p(&out_rec->len, ent->len); + memcpy(&out_rec->tag, ent->tag, 0x10); + stw_le_p(&out_rec->shared_seq, ent->shared_seq); + + record_done++; + out_rec++; + if (record_done == record_count) { + break; + } + } + } + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + +/* + * Check whether any bit between addr[nr, nr+size) is set, + * return true if any bit is set, otherwise return false + */ +bool test_any_bits_set(const unsigned long *addr, unsigned long nr, + unsigned long size) +{ + unsigned long res = find_next_bit(addr, size + nr, nr); + + return res < nr + size; +} + +CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len) +{ + int i; + CXLDCRegion *region = &ct3d->dc.regions[0]; + + if (dpa < region->base || + dpa >= region->base + ct3d->dc.total_capacity) { + return NULL; + } + + /* + * CXL r3.1 section 9.13.3: Dynamic Capacity Device (DCD) + * + * Regions are used in increasing-DPA order, with Region 0 being used for + * the lowest DPA of Dynamic Capacity and Region 7 for the highest DPA. + * So check from the last region to find where the dpa belongs. Extents that + * cross multiple regions are not allowed. + */ + for (i = ct3d->dc.num_regions - 1; i >= 0; i--) { + region = &ct3d->dc.regions[i]; + if (dpa >= region->base) { + if (dpa + len > region->base + region->len) { + return NULL; + } + return region; + } + } + + return NULL; +} + +void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, + uint64_t dpa, + uint64_t len, + uint8_t *tag, + uint16_t shared_seq) +{ + CXLDCExtent *extent; + + extent = g_new0(CXLDCExtent, 1); + extent->start_dpa = dpa; + extent->len = len; + if (tag) { + memcpy(extent->tag, tag, 0x10); + } + extent->shared_seq = shared_seq; + + QTAILQ_INSERT_TAIL(list, extent, node); +} + +void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, + CXLDCExtent *extent) +{ + QTAILQ_REMOVE(list, extent, node); + g_free(extent); +} + +/* + * Add a new extent to the extent "group" if group exists; + * otherwise, create a new group + * Return value: the extent group where the extent is inserted. + */ +CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, + uint64_t dpa, + uint64_t len, + uint8_t *tag, + uint16_t shared_seq) +{ + if (!group) { + group = g_new0(CXLDCExtentGroup, 1); + QTAILQ_INIT(&group->list); + } + cxl_insert_extent_to_extent_list(&group->list, dpa, len, + tag, shared_seq); + return group; +} + +void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, + CXLDCExtentGroup *group) +{ + QTAILQ_INSERT_TAIL(list, group, node); +} + +void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) +{ + CXLDCExtent *ent, *ent_next; + CXLDCExtentGroup *group = QTAILQ_FIRST(list); + + QTAILQ_REMOVE(list, group, node); + QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { + cxl_remove_extent_from_extent_list(&group->list, ent); + } + g_free(group); +} + +/* + * CXL r3.1 Table 8-168: Add Dynamic Capacity Response Input Payload + * CXL r3.1 Table 8-170: Release Dynamic Capacity Input Payload + */ +typedef struct CXLUpdateDCExtentListInPl { + uint32_t num_entries_updated; + uint8_t flags; + uint8_t rsvd[3]; + /* CXL r3.1 Table 8-169: Updated Extent */ + struct { + uint64_t start_dpa; + uint64_t len; + uint8_t rsvd[8]; + } QEMU_PACKED updated_entries[]; +} QEMU_PACKED CXLUpdateDCExtentListInPl; + +/* + * For the extents in the extent list to operate, check whether they are valid + * 1. The extent should be in the range of a valid DC region; + * 2. The extent should not cross multiple regions; + * 3. The start DPA and the length of the extent should align with the block + * size of the region; + * 4. The address range of multiple extents in the list should not overlap. + */ +static CXLRetCode cxl_detect_malformed_extent_list(CXLType3Dev *ct3d, + const CXLUpdateDCExtentListInPl *in) +{ + uint64_t min_block_size = UINT64_MAX; + CXLDCRegion *region; + CXLDCRegion *lastregion = &ct3d->dc.regions[ct3d->dc.num_regions - 1]; + g_autofree unsigned long *blk_bitmap = NULL; + uint64_t dpa, len; + uint32_t i; + + for (i = 0; i < ct3d->dc.num_regions; i++) { + region = &ct3d->dc.regions[i]; + min_block_size = MIN(min_block_size, region->block_size); + } + + blk_bitmap = bitmap_new((lastregion->base + lastregion->len - + ct3d->dc.regions[0].base) / min_block_size); + + for (i = 0; i < in->num_entries_updated; i++) { + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return CXL_MBOX_INVALID_PA; + } + + dpa -= ct3d->dc.regions[0].base; + if (dpa % region->block_size || len % region->block_size) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + /* the dpa range already covered by some other extents in the list */ + if (test_any_bits_set(blk_bitmap, dpa / min_block_size, + len / min_block_size)) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + bitmap_set(blk_bitmap, dpa / min_block_size, len / min_block_size); + } + + return CXL_MBOX_SUCCESS; +} + +static CXLRetCode cxl_dcd_add_dyn_cap_rsp_dry_run(CXLType3Dev *ct3d, + const CXLUpdateDCExtentListInPl *in) +{ + uint32_t i; + CXLDCExtent *ent; + CXLDCExtentGroup *ext_group; + uint64_t dpa, len; + Range range1, range2; + + for (i = 0; i < in->num_entries_updated; i++) { + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + range_init_nofail(&range1, dpa, len); + + /* + * The host-accepted DPA range must be contained by the first extent + * group in the pending list + */ + ext_group = QTAILQ_FIRST(&ct3d->dc.extents_pending); + if (!cxl_extents_contains_dpa_range(&ext_group->list, dpa, len)) { + return CXL_MBOX_INVALID_PA; + } + + /* to-be-added range should not overlap with range already accepted */ + QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) { + range_init_nofail(&range2, ent->start_dpa, ent->len); + if (range_overlaps_range(&range1, &range2)) { + return CXL_MBOX_INVALID_PA; + } + } + } + return CXL_MBOX_SUCCESS; +} + +/* + * CXL r3.1 section 8.2.9.9.9.3: Add Dynamic Capacity Response (Opcode 4802h) + * An extent is added to the extent list and becomes usable only after the + * response is processed successfully. + */ +static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLUpdateDCExtentListInPl *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCExtentList *extent_list = &ct3d->dc.extents; + uint32_t i; + uint64_t dpa, len; + CXLRetCode ret; + + if (len_in < sizeof(*in)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + if (in->num_entries_updated == 0) { + cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + return CXL_MBOX_SUCCESS; + } + + if (len_in < + sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + /* Adding extents causes exceeding device's extent tracking ability. */ + if (in->num_entries_updated + ct3d->dc.total_extent_count > + CXL_NUM_EXTENTS_SUPPORTED) { + return CXL_MBOX_RESOURCES_EXHAUSTED; + } + + ret = cxl_detect_malformed_extent_list(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + ret = cxl_dcd_add_dyn_cap_rsp_dry_run(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + for (i = 0; i < in->num_entries_updated; i++) { + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); + ct3d->dc.total_extent_count += 1; + ct3_set_region_block_backed(ct3d, dpa, len); + } + /* Remove the first extent group in the pending list */ + cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + + return CXL_MBOX_SUCCESS; +} + +/* + * Copy extent list from src to dst + * Return value: number of extents copied + */ +static uint32_t copy_extent_list(CXLDCExtentList *dst, + const CXLDCExtentList *src) +{ + uint32_t cnt = 0; + CXLDCExtent *ent; + + if (!dst || !src) { + return 0; + } + + QTAILQ_FOREACH(ent, src, node) { + cxl_insert_extent_to_extent_list(dst, ent->start_dpa, ent->len, + ent->tag, ent->shared_seq); + cnt++; + } + return cnt; +} + +static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, + const CXLUpdateDCExtentListInPl *in, CXLDCExtentList *updated_list, + uint32_t *updated_list_size) +{ + CXLDCExtent *ent, *ent_next; + uint64_t dpa, len; + uint32_t i; + int cnt_delta = 0; + CXLRetCode ret = CXL_MBOX_SUCCESS; + + QTAILQ_INIT(updated_list); + copy_extent_list(updated_list, &ct3d->dc.extents); + + for (i = 0; i < in->num_entries_updated; i++) { + Range range; + + dpa = in->updated_entries[i].start_dpa; + len = in->updated_entries[i].len; + + /* Check if the DPA range is not fully backed with valid extents */ + if (!ct3_test_region_block_backed(ct3d, dpa, len)) { + ret = CXL_MBOX_INVALID_PA; + goto free_and_exit; + } + + /* After this point, extent overflow is the only error can happen */ + while (len > 0) { + QTAILQ_FOREACH(ent, updated_list, node) { + range_init_nofail(&range, ent->start_dpa, ent->len); + + if (range_contains(&range, dpa)) { + uint64_t len1, len2 = 0, len_done = 0; + uint64_t ent_start_dpa = ent->start_dpa; + uint64_t ent_len = ent->len; + + len1 = dpa - ent->start_dpa; + /* Found the extent or the subset of an existing extent */ + if (range_contains(&range, dpa + len - 1)) { + len2 = ent_start_dpa + ent_len - dpa - len; + } else { + dpa = ent_start_dpa + ent_len; + } + len_done = ent_len - len1 - len2; + + cxl_remove_extent_from_extent_list(updated_list, ent); + cnt_delta--; + + if (len1) { + cxl_insert_extent_to_extent_list(updated_list, + ent_start_dpa, + len1, NULL, 0); + cnt_delta++; + } + if (len2) { + cxl_insert_extent_to_extent_list(updated_list, + dpa + len, + len2, NULL, 0); + cnt_delta++; + } + + if (cnt_delta + ct3d->dc.total_extent_count > + CXL_NUM_EXTENTS_SUPPORTED) { + ret = CXL_MBOX_RESOURCES_EXHAUSTED; + goto free_and_exit; + } + + len -= len_done; + break; + } + } + } + } +free_and_exit: + if (ret != CXL_MBOX_SUCCESS) { + QTAILQ_FOREACH_SAFE(ent, updated_list, node, ent_next) { + cxl_remove_extent_from_extent_list(updated_list, ent); + } + *updated_list_size = 0; + } else { + *updated_list_size = ct3d->dc.total_extent_count + cnt_delta; + } + + return ret; +} + +/* + * CXL r3.1 section 8.2.9.9.9.4: Release Dynamic Capacity (Opcode 4803h) + */ +static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLUpdateDCExtentListInPl *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCExtentList updated_list; + CXLDCExtent *ent, *ent_next; + uint32_t updated_list_size; + CXLRetCode ret; + + if (len_in < sizeof(*in)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + if (in->num_entries_updated == 0) { + return CXL_MBOX_INVALID_INPUT; + } + + if (len_in < + sizeof(*in) + sizeof(*in->updated_entries) * in->num_entries_updated) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + ret = cxl_detect_malformed_extent_list(ct3d, in); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + ret = cxl_dc_extent_release_dry_run(ct3d, in, &updated_list, + &updated_list_size); + if (ret != CXL_MBOX_SUCCESS) { + return ret; + } + + /* + * If the dry run release passes, the returned updated_list will + * be the updated extent list and we just need to clear the extents + * in the accepted list and copy extents in the updated_list to accepted + * list and update the extent count; + */ + QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { + ct3_clear_region_block_backed(ct3d, ent->start_dpa, ent->len); + cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); + } + copy_extent_list(&ct3d->dc.extents, &updated_list); + QTAILQ_FOREACH_SAFE(ent, &updated_list, node, ent_next) { + ct3_set_region_block_backed(ct3d, ent->start_dpa, ent->len); + cxl_remove_extent_from_extent_list(&updated_list, ent); + } + ct3d->dc.total_extent_count = updated_list_size; + + return CXL_MBOX_SUCCESS; +} + +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 }, + cmd_events_clear_records, ~0, CXL_MBOX_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, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE }, [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", cmd_firmware_update_get_info, 0, 0 }, + [FIRMWARE_UPDATE][TRANSFER] = { "FIRMWARE_UPDATE_TRANSFER", + cmd_firmware_update_transfer, ~0, CXL_MBOX_BACKGROUND_OPERATION }, + [FIRMWARE_UPDATE][ACTIVATE] = { "FIRMWARE_UPDATE_ACTIVATE", + cmd_firmware_update_activate, 2, CXL_MBOX_BACKGROUND_OPERATION }, [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, CXL_MBOX_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 }, + [FEATURES][GET_SUPPORTED] = { "FEATURES_GET_SUPPORTED", + cmd_features_get_supported, 0x8, 0 }, + [FEATURES][GET_FEATURE] = { "FEATURES_GET_FEATURE", + cmd_features_get_feature, 0x15, 0 }, + [FEATURES][SET_FEATURE] = { "FEATURES_SET_FEATURE", + cmd_features_set_feature, + ~0, + (CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE | + CXL_MBOX_IMMEDIATE_POLICY_CHANGE | + CXL_MBOX_IMMEDIATE_LOG_CHANGE | + CXL_MBOX_SECURITY_STATE_CHANGE)}, [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, 8, 0 }, [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, - ~0, IMMEDIATE_CONFIG_CHANGE | IMMEDIATE_DATA_CHANGE }, + ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE }, + [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, + (CXL_MBOX_IMMEDIATE_DATA_CHANGE | + CXL_MBOX_SECURITY_STATE_CHANGE | + CXL_MBOX_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 }, + [MEDIA_AND_POISON][GET_SCAN_MEDIA_CAPABILITIES] = { + "MEDIA_AND_POISON_GET_SCAN_MEDIA_CAPABILITIES", + cmd_media_get_scan_media_capabilities, 16, 0 }, + [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA", + cmd_media_scan_media, 17, CXL_MBOX_BACKGROUND_OPERATION }, + [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = { + "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS", + cmd_media_get_scan_media_results, 0, 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_dcd[256][256] = { + [DCD_CONFIG][GET_DC_CONFIG] = { "DCD_GET_DC_CONFIG", + cmd_dcd_get_dyn_cap_config, 2, 0 }, + [DCD_CONFIG][GET_DYN_CAP_EXT_LIST] = { + "DCD_GET_DYNAMIC_CAPACITY_EXTENT_LIST", cmd_dcd_get_dyn_cap_ext_list, + 8, 0 }, + [DCD_CONFIG][ADD_DYN_CAP_RSP] = { + "DCD_ADD_DYNAMIC_CAPACITY_RESPONSE", cmd_dcd_add_dyn_cap_rsp, + ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE }, + [DCD_CONFIG][RELEASE_DYN_CAP] = { + "DCD_RELEASE_DYNAMIC_CAPACITY", cmd_dcd_release_dyn_cap, + ~0, CXL_MBOX_IMMEDIATE_DATA_CHANGE }, +}; - 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]; +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, 8, + CXL_MBOX_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 }, +}; + +/* + * 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; + CXLDeviceState *cxl_dstate; + + *len_out = 0; + cxl_cmd = &cci->cxl_cmd_set[set][cmd]; h = cxl_cmd->handler; - if (h) { - if (len == cxl_cmd->in || cxl_cmd->in == ~0) { - 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 & CXL_MBOX_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 the media is disabled */ + if (object_dynamic_cast(OBJECT(cci->d), TYPE_CXL_TYPE3)) { + cxl_dstate = &CXL_TYPE3(cci->d)->cxl_dstate; - /* Tell the host we're done */ - ARRAY_FIELD_DP32(cxl_dstate->mbox_reg_state32, CXL_DEV_MAILBOX_CTRL, - DOORBELL, 0); -} - -int cxl_initialize_mailbox(CXLDeviceState *cxl_dstate) -{ - /* CXL 2.0: Table 169 Get Supported Logs Log Entry */ - const char *cel_uuidstr = "0da9c0b5-bf41-4b78-8f79-96b1623b3f17"; - - 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]; - struct cel_log *log = - &cxl_dstate->cel_log[cxl_dstate->cel_size]; - - log->opcode = (set << 8) | cmd; - log->effect = c->effect; - cxl_dstate->cel_size++; + if (cxl_dev_media_disabled(cxl_dstate)) { + 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 || + h == cmd_firmware_update_transfer || + h == cmd_firmware_update_activate) { + return CXL_MBOX_MEDIA_DISABLED; } } } - return qemu_uuid_parse(cel_uuidstr, &cel_uuid); + ret = (*h)(cxl_cmd, pl_in, len_in, pl_out, len_out, cci); + if ((cxl_cmd->effect & CXL_MBOX_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; +} + +static void bg_timercb(void *opaque) +{ + 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 0x0201: /* fw transfer */ + __do_firmware_xfer(cci); + break; + case 0x4400: /* sanitize */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + + __do_sanitization(ct3d); + cxl_dev_enable_media(&ct3d->cxl_dstate); + } + break; + case 0x4304: /* scan media */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + + __do_scan_media(ct3d); + break; + } + default: + __builtin_unreachable(); + break; + } + } else { + /* estimate only */ + cci->bg.complete_pct = + 100 * (now - cci->bg.starttime) / cci->bg.runtime; + 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); + } + } +} + +static void cxl_rebuild_cel(CXLCCI *cci) +{ + cci->cel_size = 0; /* Reset for a fresh build */ + for (int set = 0; set < 256; set++) { + for (int cmd = 0; cmd < 256; cmd++) { + if (cci->cxl_cmd_set[set][cmd].handler) { + const struct cxl_cmd *c = &cci->cxl_cmd_set[set][cmd]; + struct cel_log *log = + &cci->cel_log[cci->cel_size]; + + log->opcode = (set << 8) | cmd; + log->effect = c->effect; + cci->cel_size++; + } + } + } +} + +void cxl_init_cci(CXLCCI *cci, size_t payload_max) +{ + cci->payload_max = payload_max; + cxl_rebuild_cel(cci); + + 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); + + memset(&cci->fw, 0, sizeof(cci->fw)); + cci->fw.active_slot = 1; + cci->fw.slot[cci->fw.active_slot - 1] = true; +} + +static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[256]) +{ + for (int set = 0; set < 256; set++) { + for (int cmd = 0; cmd < 256; cmd++) { + if (cxl_cmds[set][cmd].handler) { + cci->cxl_cmd_set[set][cmd] = cxl_cmds[set][cmd]; + } + } + } +} + +void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256], + size_t payload_max) +{ + cci->payload_max = MAX(payload_max, cci->payload_max); + cxl_copy_cci_commands(cci, cxl_cmd_set); + cxl_rebuild_cel(cci); +} + +void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, + DeviceState *d, size_t payload_max) +{ + cxl_copy_cci_commands(cci, 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) +{ + CXLType3Dev *ct3d = CXL_TYPE3(d); + + cxl_copy_cci_commands(cci, cxl_cmd_set); + if (ct3d->dc.num_regions) { + cxl_copy_cci_commands(cci, cxl_cmd_set_dcd); + } + 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) +{ + cxl_copy_cci_commands(cci, 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) +{ + cxl_copy_cci_commands(cci, 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 cfa95ffd40..3e375f61a9 100644 --- a/hw/cxl/meson.build +++ b/hw/cxl/meson.build @@ -1,13 +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..4f419443ab --- /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"; + device_class_set_legacy_reset(dc, 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..2250c74007 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 @@ -66,9 +66,6 @@ config BOCHS_DISPLAY select VGA select EDID -config BLIZZARD - bool - config FRAMEBUFFER bool @@ -76,7 +73,8 @@ config SM501 bool select I2C select DDC - select SERIAL + select SERIAL_MM + select USB_OHCI_SYSBUS config TCX bool @@ -93,7 +91,7 @@ config VGA config QXL bool - depends on SPICE && PCI + depends on SPICE && PCI && PIXMAN select VGA config VIRTIO_GPU @@ -134,3 +132,11 @@ config MACFB bool select FRAMEBUFFER depends on NUBUS + +config XLNX_DISPLAYPORT + bool + # defaults to "N", enabled by specific boards + depends on PIXMAN + +config DM163 + bool diff --git a/hw/display/artist.c b/hw/display/artist.c index fde050c882..5790b7a64e 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -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), @@ -1491,7 +1491,7 @@ static void artist_class_init(ObjectClass *klass, void *data) dc->realize = artist_realizefn; dc->vmsd = &vmstate_artist; - dc->reset = artist_reset; + device_class_set_legacy_reset(dc, artist_reset); device_class_set_props(dc, artist_properties); } diff --git a/hw/display/ati.c b/hw/display/ati.c index 6e38e00502..593a25328d 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; @@ -717,7 +742,7 @@ static void ati_mm_write(void *opaque, hwaddr addr, if (!s->cursor_guest_mode && (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(t & BIT(31))) { dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16, - s->regs.cur_hv_pos & 0xffff, 1); + s->regs.cur_hv_pos & 0xffff, true); } 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() }; @@ -1022,7 +1055,7 @@ static void ati_vga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - dc->reset = ati_vga_reset; + device_class_set_legacy_reset(dc, ati_vga_reset); device_class_set_props(dc, ati_vga_properties); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); @@ -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 7d786653e8..309bb5ccb6 100644 --- a/hw/display/ati_2d.c +++ b/hw/display/ati_2d.c @@ -92,6 +92,7 @@ void ati_2d_blt(ATIVGAState *s) 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 ? @@ -122,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 + @@ -180,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, - dst_x, dst_y, - s->regs.dst_width, s->regs.dst_height, - filler); - pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, - dst_x, 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 a05277674f..7005d5bfea 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -145,7 +145,7 @@ static bool fb_use_offsets(BCM2835FBConfig *config) * viewport size is larger than the physical screen. (It doesn't * prevent the guest setting this silly viewport setting, though...) */ - return config->xres_virtual > config->xres && + return config->xres_virtual > config->xres || config->yres_virtual > config->yres; } @@ -355,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), @@ -449,7 +449,7 @@ static void bcm2835_fb_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, bcm2835_fb_props); dc->realize = bcm2835_fb_realize; - dc->reset = bcm2835_fb_reset; + device_class_set_legacy_reset(dc, bcm2835_fb_reset); dc->vmsd = &vmstate_bcm2835_fb; } diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c deleted file mode 100644 index 030abbe7d4..0000000000 --- a/hw/display/blizzard.c +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/bitops.h" -#include "ui/console.h" -#include "hw/display/blizzard.h" -#include "ui/pixel_ops.h" - -typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); - -typedef struct { - uint8_t reg; - uint32_t addr; - int swallow; - - int pll; - int pll_range; - int pll_ctrl; - uint8_t pll_mode; - uint8_t clksel; - int memenable; - int memrefresh; - uint8_t timing[3]; - int priority; - - uint8_t lcd_config; - int x; - int y; - int skipx; - int skipy; - uint8_t hndp; - uint8_t vndp; - uint8_t hsync; - uint8_t vsync; - uint8_t pclk; - uint8_t u; - uint8_t v; - uint8_t yrc[2]; - int ix[2]; - int iy[2]; - int ox[2]; - int oy[2]; - - int enable; - int blank; - int bpp; - int invalidate; - int mx[2]; - int my[2]; - uint8_t mode; - uint8_t effect; - uint8_t iformat; - uint8_t source; - QemuConsole *con; - blizzard_fn_t *line_fn_tab[2]; - void *fb; - - uint8_t hssi_config[3]; - uint8_t tv_config; - uint8_t tv_timing[4]; - uint8_t vbi; - uint8_t tv_x; - uint8_t tv_y; - uint8_t tv_test; - uint8_t tv_filter_config; - uint8_t tv_filter_idx; - uint8_t tv_filter_coeff[0x20]; - uint8_t border_r; - uint8_t border_g; - uint8_t border_b; - uint8_t gamma_config; - uint8_t gamma_idx; - uint8_t gamma_lut[0x100]; - uint8_t matrix_ena; - uint8_t matrix_coeff[0x12]; - uint8_t matrix_r; - uint8_t matrix_g; - uint8_t matrix_b; - uint8_t pm; - uint8_t status; - uint8_t rgbgpio_dir; - uint8_t rgbgpio; - uint8_t gpio_dir; - uint8_t gpio; - uint8_t gpio_edge[2]; - uint8_t gpio_irq; - uint8_t gpio_pdown; - - struct { - int x; - int y; - int dx; - int dy; - int len; - int buflen; - void *buf; - void *data; - uint16_t *ptr; - int angle; - int pitch; - blizzard_fn_t line_fn; - } data; -} BlizzardState; - -/* 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 */ - 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 */ - 0, 0, 0, 0, 0, 0, -}; - -static void blizzard_window(BlizzardState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - uint8_t *src, *dst; - int bypp[2]; - int bypl[3]; - int y; - blizzard_fn_t fn = s->data.line_fn; - - if (!fn) - return; - if (s->mx[0] > s->data.x) - s->mx[0] = s->data.x; - if (s->my[0] > s->data.y) - s->my[0] = s->data.y; - if (s->mx[1] < s->data.x + s->data.dx) - s->mx[1] = s->data.x + s->data.dx; - if (s->my[1] < s->data.y + s->data.dy) - s->my[1] = s->data.y + s->data.dy; - - bypp[0] = s->bpp; - bypp[1] = surface_bytes_per_pixel(surface); - bypl[0] = bypp[0] * s->data.pitch; - bypl[1] = bypp[1] * s->x; - bypl[2] = bypp[0] * s->data.dx; - - src = s->data.data; - dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x; - for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1]) - fn(dst, src, bypl[2]); -} - -static int blizzard_transfer_setup(BlizzardState *s) -{ - if (s->source > 3 || !s->bpp || - s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0]) - return 0; - - s->data.angle = s->effect & 3; - s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat]; - s->data.x = s->ix[0]; - s->data.y = s->iy[0]; - s->data.dx = s->ix[1] - s->ix[0] + 1; - s->data.dy = s->iy[1] - s->iy[0] + 1; - s->data.len = s->bpp * s->data.dx * s->data.dy; - s->data.pitch = s->data.dx; - if (s->data.len > s->data.buflen) { - s->data.buf = g_realloc(s->data.buf, s->data.len); - s->data.buflen = s->data.len; - } - s->data.ptr = s->data.buf; - s->data.data = s->data.buf; - s->data.len /= 2; - return 1; -} - -static void blizzard_reset(BlizzardState *s) -{ - s->reg = 0; - s->swallow = 0; - - s->pll = 9; - s->pll_range = 1; - s->pll_ctrl = 0x14; - s->pll_mode = 0x32; - s->clksel = 0x00; - s->memenable = 0; - s->memrefresh = 0x25c; - s->timing[0] = 0x3f; - s->timing[1] = 0x13; - s->timing[2] = 0x21; - s->priority = 0; - - s->lcd_config = 0x74; - s->x = 8; - s->y = 1; - s->skipx = 0; - s->skipy = 0; - s->hndp = 3; - s->vndp = 2; - s->hsync = 1; - s->vsync = 1; - s->pclk = 0x80; - - s->ix[0] = 0; - s->ix[1] = 0; - s->iy[0] = 0; - s->iy[1] = 0; - s->ox[0] = 0; - s->ox[1] = 0; - s->oy[0] = 0; - s->oy[1] = 0; - - s->yrc[0] = 0x00; - s->yrc[1] = 0x30; - s->u = 0; - s->v = 0; - - s->iformat = 3; - s->source = 0; - s->bpp = blizzard_iformat_bpp[s->iformat]; - - s->hssi_config[0] = 0x00; - s->hssi_config[1] = 0x00; - s->hssi_config[2] = 0x01; - s->tv_config = 0x00; - s->tv_timing[0] = 0x00; - s->tv_timing[1] = 0x00; - s->tv_timing[2] = 0x00; - s->tv_timing[3] = 0x00; - s->vbi = 0x10; - s->tv_x = 0x14; - s->tv_y = 0x03; - s->tv_test = 0x00; - s->tv_filter_config = 0x80; - s->tv_filter_idx = 0x00; - s->border_r = 0x10; - s->border_g = 0x80; - s->border_b = 0x80; - s->gamma_config = 0x00; - s->gamma_idx = 0x00; - s->matrix_ena = 0x00; - memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff)); - s->matrix_r = 0x00; - s->matrix_g = 0x00; - s->matrix_b = 0x00; - s->pm = 0x02; - s->status = 0x00; - s->rgbgpio_dir = 0x00; - s->gpio_dir = 0x00; - s->gpio_edge[0] = 0x00; - s->gpio_edge[1] = 0x00; - s->gpio_irq = 0x00; - s->gpio_pdown = 0xff; -} - -static inline void blizzard_invalidate_display(void *opaque) { - BlizzardState *s = (BlizzardState *) opaque; - - s->invalidate = 1; -} - -static uint16_t blizzard_reg_read(void *opaque, uint8_t reg) -{ - BlizzardState *s = (BlizzardState *) opaque; - - switch (reg) { - case 0x00: /* Revision Code */ - return 0xa5; - - case 0x02: /* Configuration Readback */ - return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ - - case 0x04: /* PLL M-Divider */ - return (s->pll - 1) | (1 << 7); - case 0x06: /* PLL Lock Range Control */ - return s->pll_range; - case 0x08: /* PLL Lock Synthesis Control 0 */ - return s->pll_ctrl & 0xff; - case 0x0a: /* PLL Lock Synthesis Control 1 */ - return s->pll_ctrl >> 8; - case 0x0c: /* PLL Mode Control 0 */ - return s->pll_mode; - - case 0x0e: /* Clock-Source Select */ - return s->clksel; - - case 0x10: /* Memory Controller Activate */ - case 0x14: /* Memory Controller Bank 0 Status Flag */ - return s->memenable; - - case 0x18: /* Auto-Refresh Interval Setting 0 */ - return s->memrefresh & 0xff; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ - return s->memrefresh >> 8; - - case 0x1c: /* Power-On Sequence Timing Control */ - return s->timing[0]; - case 0x1e: /* Timing Control 0 */ - return s->timing[1]; - case 0x20: /* Timing Control 1 */ - return s->timing[2]; - - case 0x24: /* Arbitration Priority Control */ - return s->priority; - - case 0x28: /* LCD Panel Configuration */ - return s->lcd_config; - - case 0x2a: /* LCD Horizontal Display Width */ - return s->x >> 3; - case 0x2c: /* LCD Horizontal Non-display Period */ - return s->hndp; - case 0x2e: /* LCD Vertical Display Height 0 */ - return s->y & 0xff; - case 0x30: /* LCD Vertical Display Height 1 */ - return s->y >> 8; - case 0x32: /* LCD Vertical Non-display Period */ - return s->vndp; - case 0x34: /* LCD HS Pulse-width */ - return s->hsync; - case 0x36: /* LCd HS Pulse Start Position */ - return s->skipx >> 3; - case 0x38: /* LCD VS Pulse-width */ - return s->vsync; - case 0x3a: /* LCD VS Pulse Start Position */ - return s->skipy; - - case 0x3c: /* PCLK Polarity */ - return s->pclk; - - 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 */ - return s->hssi_config[1]; - case 0x42: /* High-speed Serial Interface Tx Mode */ - return s->hssi_config[2]; - case 0x44: /* TV Display Configuration */ - return s->tv_config; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ - return s->tv_timing[(reg - 0x46) >> 1]; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ - return s->vbi; - case 0x50: /* TV Horizontal Start Position */ - return s->tv_x; - case 0x52: /* TV Vertical Start Position */ - return s->tv_y; - case 0x54: /* TV Test Pattern Setting */ - return s->tv_test; - case 0x56: /* TV Filter Setting */ - return s->tv_filter_config; - case 0x58: /* TV Filter Coefficient Index */ - return s->tv_filter_idx; - 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 */ - return s->yrc[0]; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ - return s->yrc[1]; - case 0x64: /* U Data Fix */ - return s->u; - case 0x66: /* V Data Fix */ - return s->v; - - case 0x68: /* Display Mode */ - return s->mode; - - case 0x6a: /* Special Effects */ - return s->effect; - - case 0x6c: /* Input Window X Start Position 0 */ - return s->ix[0] & 0xff; - case 0x6e: /* Input Window X Start Position 1 */ - return s->ix[0] >> 3; - case 0x70: /* Input Window Y Start Position 0 */ - return s->ix[0] & 0xff; - case 0x72: /* Input Window Y Start Position 1 */ - return s->ix[0] >> 3; - case 0x74: /* Input Window X End Position 0 */ - return s->ix[1] & 0xff; - case 0x76: /* Input Window X End Position 1 */ - return s->ix[1] >> 3; - case 0x78: /* Input Window Y End Position 0 */ - return s->ix[1] & 0xff; - case 0x7a: /* Input Window Y End Position 1 */ - return s->ix[1] >> 3; - case 0x7c: /* Output Window X Start Position 0 */ - return s->ox[0] & 0xff; - case 0x7e: /* Output Window X Start Position 1 */ - return s->ox[0] >> 3; - case 0x80: /* Output Window Y Start Position 0 */ - return s->oy[0] & 0xff; - case 0x82: /* Output Window Y Start Position 1 */ - return s->oy[0] >> 3; - case 0x84: /* Output Window X End Position 0 */ - return s->ox[1] & 0xff; - case 0x86: /* Output Window X End Position 1 */ - return s->ox[1] >> 3; - case 0x88: /* Output Window Y End Position 0 */ - return s->oy[1] & 0xff; - case 0x8a: /* Output Window Y End Position 1 */ - return s->oy[1] >> 3; - - case 0x8c: /* Input Data Format */ - return s->iformat; - case 0x8e: /* Data Source Select */ - return s->source; - case 0x90: /* Display Memory Data Port */ - return 0; - - case 0xa8: /* Border Color 0 */ - return s->border_r; - case 0xaa: /* Border Color 1 */ - return s->border_g; - case 0xac: /* Border Color 2 */ - return s->border_b; - - case 0xb4: /* Gamma Correction Enable */ - return s->gamma_config; - case 0xb6: /* Gamma Correction Table Index */ - return s->gamma_idx; - case 0xb8: /* Gamma Correction Table Data */ - return s->gamma_lut[s->gamma_idx ++]; - - case 0xba: /* 3x3 Matrix Enable */ - return s->matrix_ena; - case 0xbc ... 0xde: /* Coefficient Registers */ - return s->matrix_coeff[(reg - 0xbc) >> 1]; - case 0xe0: /* 3x3 Matrix Red Offset */ - return s->matrix_r; - case 0xe2: /* 3x3 Matrix Green Offset */ - return s->matrix_g; - case 0xe4: /* 3x3 Matrix Blue Offset */ - return s->matrix_b; - - case 0xe6: /* Power-save */ - return s->pm; - case 0xe8: /* Non-display Period Control / Status */ - return s->status | (1 << 5); - case 0xea: /* RGB Interface Control */ - return s->rgbgpio_dir; - case 0xec: /* RGB Interface Status */ - return s->rgbgpio; - case 0xee: /* General-purpose IO Pins Configuration */ - return s->gpio_dir; - case 0xf0: /* General-purpose IO Pins Status / Control */ - return s->gpio; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ - return s->gpio_edge[0]; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ - return s->gpio_edge[1]; - case 0xf6: /* GPIO Interrupt Status */ - return s->gpio_irq; - case 0xf8: /* GPIO Pull-down Control */ - return s->gpio_pdown; - - default: - fprintf(stderr, "%s: unknown register %02x\n", __func__, reg); - return 0; - } -} - -static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) -{ - BlizzardState *s = (BlizzardState *) opaque; - - switch (reg) { - case 0x04: /* PLL M-Divider */ - s->pll = (value & 0x3f) + 1; - break; - case 0x06: /* PLL Lock Range Control */ - s->pll_range = value & 3; - break; - 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 */ - s->pll_ctrl &= 0x0ff; - s->pll_ctrl |= (value << 8) & 0xf00; - break; - 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 */ - s->clksel = value & 0xff; - break; - - case 0x10: /* Memory Controller Activate */ - s->memenable = value & 1; - break; - case 0x14: /* Memory Controller Bank 0 Status Flag */ - break; - - case 0x18: /* Auto-Refresh Interval Setting 0 */ - s->memrefresh &= 0xf00; - s->memrefresh |= (value << 0) & 0x0ff; - break; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ - s->memrefresh &= 0x0ff; - s->memrefresh |= (value << 8) & 0xf00; - break; - - case 0x1c: /* Power-On Sequence Timing Control */ - s->timing[0] = value & 0x7f; - break; - case 0x1e: /* Timing Control 0 */ - s->timing[1] = value & 0x17; - break; - case 0x20: /* Timing Control 1 */ - s->timing[2] = value & 0x35; - break; - - case 0x24: /* Arbitration Priority Control */ - s->priority = value & 1; - break; - - 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 */ - s->x = value << 3; - break; - case 0x2c: /* LCD Horizontal Non-display Period */ - s->hndp = value & 0xff; - break; - case 0x2e: /* LCD Vertical Display Height 0 */ - s->y &= 0x300; - s->y |= (value << 0) & 0x0ff; - break; - case 0x30: /* LCD Vertical Display Height 1 */ - s->y &= 0x0ff; - s->y |= (value << 8) & 0x300; - break; - case 0x32: /* LCD Vertical Non-display Period */ - s->vndp = value & 0xff; - break; - case 0x34: /* LCD HS Pulse-width */ - s->hsync = value & 0xff; - break; - case 0x36: /* LCD HS Pulse Start Position */ - s->skipx = value & 0xff; - break; - case 0x38: /* LCD VS Pulse-width */ - s->vsync = value & 0xbf; - break; - case 0x3a: /* LCD VS Pulse Start Position */ - s->skipy = value & 0xff; - break; - - 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 */ - s->hssi_config[0] = value; - break; - 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 */ - s->hssi_config[2] = value & 0xbd; - break; - - case 0x44: /* TV Display Configuration */ - s->tv_config = value & 0xfe; - break; - 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 */ - s->vbi = value; - break; - case 0x50: /* TV Horizontal Start Position */ - s->tv_x = value; - break; - case 0x52: /* TV Vertical Start Position */ - s->tv_y = value & 0x7f; - break; - case 0x54: /* TV Test Pattern Setting */ - s->tv_test = value; - break; - case 0x56: /* TV Filter Setting */ - s->tv_filter_config = value & 0xbf; - break; - case 0x58: /* TV Filter Coefficient Index */ - s->tv_filter_idx = value & 0x1f; - break; - 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 */ - s->yrc[0] = value & 0xb0; - break; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ - s->yrc[1] = value & 0x30; - break; - case 0x64: /* U Data Fix */ - s->u = value & 0xff; - break; - case 0x66: /* V Data Fix */ - s->v = value & 0xff; - break; - - case 0x68: /* Display Mode */ - if ((s->mode ^ value) & 3) - s->invalidate = 1; - s->mode = value & 0xb7; - s->enable = value & 1; - s->blank = (value >> 1) & 1; - if (value & (1 << 4)) - fprintf(stderr, "%s: Macrovision enable attempt!\n", __func__); - break; - - case 0x6a: /* Special Effects */ - s->effect = value & 0xfb; - break; - - 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 */ - s->ix[0] &= 0x0ff; - s->ix[0] |= (value << 8) & 0x300; - break; - 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 */ - s->iy[0] &= 0x0ff; - s->iy[0] |= (value << 8) & 0x300; - break; - 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 */ - s->ix[1] &= 0x0ff; - s->ix[1] |= (value << 8) & 0x300; - break; - 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 */ - s->iy[1] &= 0x0ff; - s->iy[1] |= (value << 8) & 0x300; - break; - 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 */ - s->ox[0] &= 0x0ff; - s->ox[0] |= (value << 8) & 0x300; - break; - 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 */ - s->oy[0] &= 0x0ff; - s->oy[0] |= (value << 8) & 0x300; - break; - 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 */ - s->ox[1] &= 0x0ff; - s->ox[1] |= (value << 8) & 0x300; - break; - 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 */ - s->oy[1] &= 0x0ff; - s->oy[1] |= (value << 8) & 0x300; - break; - - 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 */ - s->source = value & 7; - /* Currently all windows will be "destructive overlays". */ - if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] || - s->iy[0] != s->oy[0] || - s->ix[1] != s->ox[1] || - s->iy[1] != s->oy[1])) || - !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) & - (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1)) - fprintf(stderr, "%s: Illegal input/output window positions\n", - __func__); - - blizzard_transfer_setup(s); - break; - - case 0x90: /* Display Memory Data Port */ - if (!s->data.len && !blizzard_transfer_setup(s)) - break; - - *s->data.ptr ++ = value; - if (-- s->data.len == 0) - blizzard_window(s); - break; - - case 0xa8: /* Border Color 0 */ - s->border_r = value; - break; - case 0xaa: /* Border Color 1 */ - s->border_g = value; - break; - case 0xac: /* Border Color 2 */ - s->border_b = value; - break; - - case 0xb4: /* Gamma Correction Enable */ - s->gamma_config = value & 0x87; - break; - case 0xb6: /* Gamma Correction Table Index */ - s->gamma_idx = value; - break; - case 0xb8: /* Gamma Correction Table Data */ - s->gamma_lut[s->gamma_idx ++] = value; - break; - - case 0xba: /* 3x3 Matrix Enable */ - s->matrix_ena = value & 1; - break; - case 0xbc ... 0xde: /* Coefficient Registers */ - s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff); - break; - case 0xe0: /* 3x3 Matrix Red Offset */ - s->matrix_r = value; - break; - case 0xe2: /* 3x3 Matrix Green Offset */ - s->matrix_g = value; - break; - case 0xe4: /* 3x3 Matrix Blue Offset */ - s->matrix_b = value; - break; - - 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 */ - s->status = value & 0x1b; - break; - case 0xea: /* RGB Interface Control */ - s->rgbgpio_dir = value & 0x8f; - break; - case 0xec: /* RGB Interface Status */ - s->rgbgpio = value & 0xcf; - break; - case 0xee: /* General-purpose IO Pins Configuration */ - s->gpio_dir = value; - break; - case 0xf0: /* General-purpose IO Pins Status / Control */ - s->gpio = value; - break; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ - s->gpio_edge[0] = value; - break; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ - s->gpio_edge[1] = value; - break; - case 0xf6: /* GPIO Interrupt Status */ - s->gpio_irq &= value; - break; - case 0xf8: /* GPIO Pull-down Control */ - s->gpio_pdown = value; - break; - - default: - fprintf(stderr, "%s: unknown register %02x\n", __func__, reg); - break; - } -} - -uint16_t s1d13745_read(void *opaque, int dc) -{ - BlizzardState *s = (BlizzardState *) opaque; - uint16_t value = blizzard_reg_read(s, s->reg); - - if (s->swallow -- > 0) - return 0; - if (dc) - s->reg ++; - - return value; -} - -void s1d13745_write(void *opaque, int dc, uint16_t value) -{ - BlizzardState *s = (BlizzardState *) opaque; - - if (s->swallow -- > 0) - return; - if (dc) { - blizzard_reg_write(s, s->reg, value); - - if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8) - s->reg += 2; - } else - s->reg = value & 0xff; -} - -void s1d13745_write_block(void *opaque, int dc, - void *buf, size_t len, int pitch) -{ - BlizzardState *s = (BlizzardState *) opaque; - - while (len > 0) { - if (s->reg == 0x90 && dc && - (s->data.len || blizzard_transfer_setup(s)) && - len >= (s->data.len << 1)) { - len -= s->data.len << 1; - s->data.len = 0; - s->data.data = buf; - if (pitch) - s->data.pitch = pitch; - blizzard_window(s); - s->data.data = s->data.buf; - continue; - } - - s1d13745_write(opaque, dc, *(uint16_t *) buf); - len -= 2; - buf += 2; - } -} - -static void blizzard_update_display(void *opaque) -{ - BlizzardState *s = (BlizzardState *) opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int y, bypp, bypl, bwidth; - uint8_t *src, *dst; - - if (!s->enable) - return; - - if (s->x != surface_width(surface) || s->y != surface_height(surface)) { - s->invalidate = 1; - qemu_console_resize(s->con, s->x, s->y); - surface = qemu_console_surface(s->con); - } - - if (s->invalidate) { - s->invalidate = 0; - - if (s->blank) { - bypp = surface_bytes_per_pixel(surface); - memset(surface_data(surface), 0, bypp * s->x * s->y); - return; - } - - s->mx[0] = 0; - s->mx[1] = s->x; - s->my[0] = 0; - s->my[1] = s->y; - } - - if (s->mx[1] <= s->mx[0]) - return; - - bypp = surface_bytes_per_pixel(surface); - bypl = bypp * s->x; - bwidth = bypp * (s->mx[1] - s->mx[0]); - y = s->my[0]; - src = s->fb + bypl * y + bypp * s->mx[0]; - dst = surface_data(surface) + bypl * y + bypp * s->mx[0]; - for (; y < s->my[1]; y ++, src += bypl, dst += bypl) - memcpy(dst, src, bwidth); - - dpy_gfx_update(s->con, s->mx[0], s->my[0], - s->mx[1] - s->mx[0], y - s->my[0]); - - s->mx[0] = s->x; - s->mx[1] = 0; - s->my[0] = s->y; - s->my[1] = 0; -} - -static void blizzard_draw_line16_32(uint32_t *dest, - const uint16_t *src, unsigned int width) -{ - uint16_t data; - unsigned int r, g, b; - const uint16_t *end = (const void *) src + width; - while (src < end) { - data = *src ++; - b = extract16(data, 0, 5) << 3; - g = extract16(data, 5, 6) << 2; - r = extract16(data, 11, 5) << 3; - *dest++ = rgb_to_pixel32(r, g, b); - } -} - -static void blizzard_draw_line24mode1_32(uint32_t *dest, - const uint8_t *src, unsigned int width) -{ - /* TODO: check if SDL 24-bit planes are not in the same format and - * if so, use memcpy */ - unsigned int r[2], g[2], b[2]; - const uint8_t *end = src + width; - while (src < end) { - g[0] = *src ++; - r[0] = *src ++; - r[1] = *src ++; - b[0] = *src ++; - *dest++ = rgb_to_pixel32(r[0], g[0], b[0]); - b[1] = *src ++; - g[1] = *src ++; - *dest++ = rgb_to_pixel32(r[1], g[1], b[1]); - } -} - -static void blizzard_draw_line24mode2_32(uint32_t *dest, - const uint8_t *src, unsigned int width) -{ - unsigned int r, g, b; - const uint8_t *end = src + width; - while (src < end) { - r = *src ++; - src ++; - b = *src ++; - g = *src ++; - *dest++ = rgb_to_pixel32(r, g, b); - } -} - -/* No rotation */ -static blizzard_fn_t blizzard_draw_fn_32[0x10] = { - NULL, - /* RGB 5:6:5*/ - (blizzard_fn_t) blizzard_draw_line16_32, - /* RGB 6:6:6 mode 1 */ - (blizzard_fn_t) blizzard_draw_line24mode1_32, - /* RGB 8:8:8 mode 1 */ - (blizzard_fn_t) blizzard_draw_line24mode1_32, - NULL, NULL, - /* RGB 6:6:6 mode 2 */ - (blizzard_fn_t) blizzard_draw_line24mode2_32, - /* RGB 8:8:8 mode 2 */ - (blizzard_fn_t) blizzard_draw_line24mode2_32, - /* YUV 4:2:2 */ - NULL, - /* YUV 4:2:0 */ - NULL, - NULL, NULL, NULL, NULL, NULL, NULL, -}; - -/* 90deg, 180deg and 270deg rotation */ -static blizzard_fn_t blizzard_draw_fn_r_32[0x10] = { - /* TODO */ - [0 ... 0xf] = NULL, -}; - -static const GraphicHwOps blizzard_ops = { - .invalidate = blizzard_invalidate_display, - .gfx_update = blizzard_update_display, -}; - -void *s1d13745_init(qemu_irq gpio_int) -{ - BlizzardState *s = g_malloc0(sizeof(*s)); - DisplaySurface *surface; - - s->fb = g_malloc(0x180000); - - s->con = graphic_console_init(NULL, 0, &blizzard_ops, s); - surface = qemu_console_surface(s->con); - - assert(surface_bits_per_pixel(surface) == 32); - - s->line_fn_tab[0] = blizzard_draw_fn_32; - s->line_fn_tab[1] = blizzard_draw_fn_r_32; - - blizzard_reset(s); - - return s; -} 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..95f8f98b99 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), @@ -374,7 +374,7 @@ static void cg3_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = cg3_realizefn; - dc->reset = cg3_reset; + device_class_set_legacy_reset(dc, cg3_reset); dc->vmsd = &vmstate_cg3; device_class_set_props(dc, cg3_properties); } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 6e8c747c46..150883a971 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -39,10 +39,11 @@ #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" @@ -798,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); } /*************************************** @@ -1101,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); line_offset <<= 3; - *pline_offset = line_offset; + params->line_offset = line_offset; - start_addr = (s->vga.cr[0x0c] << 8) + 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); - *pstart_addr = start_addr; - 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) @@ -2041,7 +2041,7 @@ static uint64_t cirrus_vga_mem_read(void *opaque, } else { 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; } @@ -2105,7 +2105,7 @@ static void cirrus_vga_mem_write(void *opaque, } } 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); } } @@ -2739,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), @@ -2777,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), @@ -2925,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/dm163.c b/hw/display/dm163.c new file mode 100644 index 0000000000..75a91f62bd --- /dev/null +++ b/hw/display/dm163.c @@ -0,0 +1,349 @@ +/* + * QEMU DM163 8x3-channel constant current led driver + * driving columns of associated 8x8 RGB matrix. + * + * Copyright (C) 2024 Samuel Tardieu + * Copyright (C) 2024 Arnaud Minier + * Copyright (C) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * The reference used for the DM163 is the following : + * http://www.siti.com.tw/product/spec/LED/DM163.pdf + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/display/dm163.h" +#include "ui/console.h" +#include "trace.h" + +#define LED_SQUARE_SIZE 100 +/* Number of frames a row stays visible after being turned off. */ +#define ROW_PERSISTENCE 3 +#define TURNED_OFF_ROW (COLOR_BUFFER_SIZE - 1) + +static const VMStateDescription vmstate_dm163 = { + .name = TYPE_DM163, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT64_ARRAY(bank0_shift_register, DM163State, 3), + VMSTATE_UINT64_ARRAY(bank1_shift_register, DM163State, 3), + VMSTATE_UINT16_ARRAY(latched_outputs, DM163State, DM163_NUM_LEDS), + VMSTATE_UINT16_ARRAY(outputs, DM163State, DM163_NUM_LEDS), + VMSTATE_UINT8(dck, DM163State), + VMSTATE_UINT8(en_b, DM163State), + VMSTATE_UINT8(lat_b, DM163State), + VMSTATE_UINT8(rst_b, DM163State), + VMSTATE_UINT8(selbk, DM163State), + VMSTATE_UINT8(sin, DM163State), + VMSTATE_UINT8(activated_rows, DM163State), + VMSTATE_UINT32_2DARRAY(buffer, DM163State, COLOR_BUFFER_SIZE, + RGB_MATRIX_NUM_COLS), + VMSTATE_UINT8(last_buffer_idx, DM163State), + VMSTATE_UINT8_ARRAY(buffer_idx_of_row, DM163State, RGB_MATRIX_NUM_ROWS), + VMSTATE_UINT8_ARRAY(row_persistence_delay, DM163State, + RGB_MATRIX_NUM_ROWS), + VMSTATE_END_OF_LIST() + } +}; + +static void dm163_reset_hold(Object *obj, ResetType type) +{ + DM163State *s = DM163(obj); + + s->sin = 0; + s->dck = 0; + s->rst_b = 0; + /* Ensuring the first falling edge of lat_b isn't missed */ + s->lat_b = 1; + s->selbk = 0; + s->en_b = 0; + /* Reset stops the PWM, not the shift and latched registers. */ + memset(s->outputs, 0, sizeof(s->outputs)); + + s->activated_rows = 0; + s->redraw = 0; + trace_dm163_redraw(s->redraw); + for (unsigned i = 0; i < COLOR_BUFFER_SIZE; i++) { + memset(s->buffer[i], 0, sizeof(s->buffer[0])); + } + s->last_buffer_idx = 0; + memset(s->buffer_idx_of_row, TURNED_OFF_ROW, sizeof(s->buffer_idx_of_row)); + memset(s->row_persistence_delay, 0, sizeof(s->row_persistence_delay)); +} + +static void dm163_dck_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + if (new_state && !s->dck) { + /* + * On raising dck, sample selbk to get the bank to use, and + * sample sin for the bit to enter into the bank shift buffer. + */ + uint64_t *sb = + s->selbk ? s->bank1_shift_register : s->bank0_shift_register; + /* Output the outgoing bit on sout */ + const bool sout = (s->selbk ? sb[2] & MAKE_64BIT_MASK(63, 1) : + sb[2] & MAKE_64BIT_MASK(15, 1)) != 0; + qemu_set_irq(s->sout, sout); + /* Enter sin into the shift buffer */ + sb[2] = (sb[2] << 1) | ((sb[1] >> 63) & 1); + sb[1] = (sb[1] << 1) | ((sb[0] >> 63) & 1); + sb[0] = (sb[0] << 1) | s->sin; + } + + s->dck = new_state; + trace_dm163_dck(new_state); +} + +static void dm163_propagate_outputs(DM163State *s) +{ + s->last_buffer_idx = (s->last_buffer_idx + 1) % RGB_MATRIX_NUM_ROWS; + /* Values are output when reset is high and enable is low. */ + if (s->rst_b && !s->en_b) { + memcpy(s->outputs, s->latched_outputs, sizeof(s->outputs)); + } else { + memset(s->outputs, 0, sizeof(s->outputs)); + } + for (unsigned x = 0; x < RGB_MATRIX_NUM_COLS; x++) { + /* Grouping the 3 RGB channels in a pixel value */ + const uint16_t b = extract16(s->outputs[3 * x + 0], 6, 8); + const uint16_t g = extract16(s->outputs[3 * x + 1], 6, 8); + const uint16_t r = extract16(s->outputs[3 * x + 2], 6, 8); + uint32_t rgba = 0; + + trace_dm163_channels(3 * x + 2, r); + trace_dm163_channels(3 * x + 1, g); + trace_dm163_channels(3 * x + 0, b); + + rgba = deposit32(rgba, 0, 8, r); + rgba = deposit32(rgba, 8, 8, g); + rgba = deposit32(rgba, 16, 8, b); + + /* Led values are sent from the last one to the first one */ + s->buffer[s->last_buffer_idx][RGB_MATRIX_NUM_COLS - x - 1] = rgba; + } + for (unsigned row = 0; row < RGB_MATRIX_NUM_ROWS; row++) { + if (s->activated_rows & (1 << row)) { + s->buffer_idx_of_row[row] = s->last_buffer_idx; + s->redraw |= (1 << row); + trace_dm163_redraw(s->redraw); + } + } +} + +static void dm163_en_b_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->en_b = new_state; + dm163_propagate_outputs(s); + trace_dm163_en_b(new_state); +} + +static uint8_t dm163_bank0(const DM163State *s, uint8_t led) +{ + /* + * Bank 0 uses 6 bits per led, so a value may be stored accross + * two uint64_t entries. + */ + const uint8_t low_bit = 6 * led; + const uint8_t low_word = low_bit / 64; + const uint8_t high_word = (low_bit + 5) / 64; + const uint8_t low_shift = low_bit % 64; + + if (low_word == high_word) { + /* Simple case: the value belongs to one entry. */ + return extract64(s->bank0_shift_register[low_word], low_shift, 6); + } + + const uint8_t nb_bits_in_low_word = 64 - low_shift; + const uint8_t nb_bits_in_high_word = 6 - nb_bits_in_low_word; + + const uint64_t bits_in_low_word = \ + extract64(s->bank0_shift_register[low_word], low_shift, + nb_bits_in_low_word); + const uint64_t bits_in_high_word = \ + extract64(s->bank0_shift_register[high_word], 0, + nb_bits_in_high_word); + uint8_t val = 0; + + val = deposit32(val, 0, nb_bits_in_low_word, bits_in_low_word); + val = deposit32(val, nb_bits_in_low_word, nb_bits_in_high_word, + bits_in_high_word); + + return val; +} + +static uint8_t dm163_bank1(const DM163State *s, uint8_t led) +{ + const uint64_t entry = s->bank1_shift_register[led / RGB_MATRIX_NUM_COLS]; + return extract64(entry, 8 * (led % RGB_MATRIX_NUM_COLS), 8); +} + +static void dm163_lat_b_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + if (s->lat_b && !new_state) { + for (int led = 0; led < DM163_NUM_LEDS; led++) { + s->latched_outputs[led] = dm163_bank0(s, led) * dm163_bank1(s, led); + } + dm163_propagate_outputs(s); + } + + s->lat_b = new_state; + trace_dm163_lat_b(new_state); +} + +static void dm163_rst_b_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->rst_b = new_state; + dm163_propagate_outputs(s); + trace_dm163_rst_b(new_state); +} + +static void dm163_selbk_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->selbk = new_state; + trace_dm163_selbk(new_state); +} + +static void dm163_sin_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + s->sin = new_state; + trace_dm163_sin(new_state); +} + +static void dm163_rows_gpio_handler(void *opaque, int line, int new_state) +{ + DM163State *s = opaque; + + if (new_state) { + s->activated_rows |= (1 << line); + s->buffer_idx_of_row[line] = s->last_buffer_idx; + s->redraw |= (1 << line); + trace_dm163_redraw(s->redraw); + } else { + s->activated_rows &= ~(1 << line); + s->row_persistence_delay[line] = ROW_PERSISTENCE; + } + trace_dm163_activated_rows(s->activated_rows); +} + +static void dm163_invalidate_display(void *opaque) +{ + DM163State *s = (DM163State *)opaque; + s->redraw = 0xFF; + trace_dm163_redraw(s->redraw); +} + +static void update_row_persistence_delay(DM163State *s, unsigned row) +{ + if (s->row_persistence_delay[row]) { + s->row_persistence_delay[row]--; + } else { + /* + * If the ROW_PERSISTENCE delay is up, + * the row is turned off. + */ + s->buffer_idx_of_row[row] = TURNED_OFF_ROW; + s->redraw |= (1 << row); + trace_dm163_redraw(s->redraw); + } +} + +static uint32_t *update_display_of_row(DM163State *s, uint32_t *dest, + unsigned row) +{ + for (unsigned _ = 0; _ < LED_SQUARE_SIZE; _++) { + for (int x = RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE - 1; x >= 0; x--) { + /* UI layer guarantees that there's 32 bits per pixel (Mar 2024) */ + *dest++ = s->buffer[s->buffer_idx_of_row[row]][x / LED_SQUARE_SIZE]; + } + } + + dpy_gfx_update(s->console, 0, LED_SQUARE_SIZE * row, + RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, LED_SQUARE_SIZE); + s->redraw &= ~(1 << row); + trace_dm163_redraw(s->redraw); + + return dest; +} + +static void dm163_update_display(void *opaque) +{ + DM163State *s = (DM163State *)opaque; + DisplaySurface *surface = qemu_console_surface(s->console); + uint32_t *dest; + + dest = surface_data(surface); + for (unsigned row = 0; row < RGB_MATRIX_NUM_ROWS; row++) { + update_row_persistence_delay(s, row); + if (!extract8(s->redraw, row, 1)) { + dest += LED_SQUARE_SIZE * LED_SQUARE_SIZE * RGB_MATRIX_NUM_COLS; + continue; + } + dest = update_display_of_row(s, dest, row); + } +} + +static const GraphicHwOps dm163_ops = { + .invalidate = dm163_invalidate_display, + .gfx_update = dm163_update_display, +}; + +static void dm163_realize(DeviceState *dev, Error **errp) +{ + DM163State *s = DM163(dev); + + qdev_init_gpio_in(dev, dm163_rows_gpio_handler, RGB_MATRIX_NUM_ROWS); + qdev_init_gpio_in(dev, dm163_sin_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_dck_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_rst_b_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_lat_b_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_selbk_gpio_handler, 1); + qdev_init_gpio_in(dev, dm163_en_b_gpio_handler, 1); + qdev_init_gpio_out_named(dev, &s->sout, "sout", 1); + + s->console = graphic_console_init(dev, 0, &dm163_ops, s); + qemu_console_resize(s->console, RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, + RGB_MATRIX_NUM_ROWS * LED_SQUARE_SIZE); +} + +static void dm163_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->desc = "DM163"; + dc->vmsd = &vmstate_dm163; + dc->realize = dm163_realize; + rc->phases.hold = dm163_reset_hold; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo dm163_types[] = { + { + .name = TYPE_DM163, + .parent = TYPE_DEVICE, + .instance_size = sizeof(DM163State), + .class_init = dm163_class_init + } +}; + +DEFINE_TYPES(dm163_types) diff --git a/hw/display/dpcd.c b/hw/display/dpcd.c index 64463654a1..108faf7887 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() } @@ -145,7 +145,7 @@ static void dpcd_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->reset = dpcd_reset; + device_class_set_legacy_reset(dc, dpcd_reset); dc->vmsd = &vmstate_dpcd; } diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 34a960a976..f3d82498bf 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); } @@ -1952,8 +1964,9 @@ static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &exynos4210_fimd_vmstate; - dc->reset = exynos4210_fimd_reset; + device_class_set_legacy_reset(dc, 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..fa2f184908 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() } @@ -534,7 +534,7 @@ static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = g364fb_sysbus_realize; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "G364 framebuffer"; - dc->reset = g364fb_sysbus_reset; + device_class_set_legacy_reset(dc, g364fb_sysbus_reset); dc->vmsd = &vmstate_g364fb_sysbus; device_class_set_props(dc, g364fb_sysbus_properties); } diff --git a/hw/display/i2c-ddc.c b/hw/display/i2c-ddc.c index 146489518c..465b00355e 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() @@ -105,7 +105,7 @@ static void i2c_ddc_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); - dc->reset = i2c_ddc_reset; + device_class_set_legacy_reset(dc, i2c_ddc_reset); dc->vmsd = &vmstate_i2c_ddc; device_class_set_props(dc, i2c_ddc_properties); isc->event = i2c_ddc_event; diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index dd5f4696c4..1448488d06 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() } @@ -300,7 +300,7 @@ static void jazz_led_class_init(ObjectClass *klass, void *data) dc->desc = "Jazz LED display", dc->vmsd = &vmstate_jazz_led; - dc->reset = jazz_led_reset; + device_class_set_legacy_reset(dc, jazz_led_reset); dc->realize = jazz_led_realize; } diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 2f8e016566..a5b4a499f3 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() } @@ -798,7 +802,7 @@ static void macfb_sysbus_class_init(ObjectClass *klass, void *data) dc->realize = macfb_sysbus_realize; dc->desc = "SysBus Macintosh framebuffer"; - dc->reset = macfb_sysbus_reset; + device_class_set_legacy_reset(dc, macfb_sysbus_reset); dc->vmsd = &vmstate_macfb_sysbus; device_class_set_props(dc, macfb_sysbus_properties); } @@ -813,7 +817,7 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data) device_class_set_parent_unrealize(dc, macfb_nubus_unrealize, &ndc->parent_unrealize); dc->desc = "Nubus Macintosh framebuffer"; - dc->reset = macfb_nubus_reset; + device_class_set_legacy_reset(dc, macfb_nubus_reset); dc->vmsd = &vmstate_macfb_nubus; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); device_class_set_props(dc, macfb_nubus_properties); diff --git a/hw/display/meson.build b/hw/display/meson.build index 7a725ed80e..20a94973fa 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -1,49 +1,47 @@ 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'), if_false: files('ramfb-stubs.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_EXYNOS4', if_true: files('exynos4210_fimd.c')) +system_ss.add(when: 'CONFIG_FRAMEBUFFER', if_true: files('framebuffer.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_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')) +system_ss.add(when: 'CONFIG_DM163', if_true: files('dm163.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') ) - softmmu_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), + system_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-vga.c'), if_false: files('acpi-vga-stub.c')) endif @@ -56,27 +54,39 @@ if config_all_devices.has_key('CONFIG_QXL') 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')) -softmmu_ss.add(when: [pixman, 'CONFIG_ATI_VGA'], if_true: files('ati.c', 'ati_2d.c', 'ati_dbg.c')) +system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) 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') @@ -87,10 +97,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') @@ -103,15 +121,25 @@ if config_all_devices.has_key('CONFIG_VIRTIO_VGA') 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 virgl.found() and opengl.found() + 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} + endif + + 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')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('acpi-vga-stub.c')) modules += { 'hw-display': hw_display_modules } diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c deleted file mode 100644 index 09e18407b4..0000000000 --- a/hw/display/omap_dss.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* - * OMAP2 Display Subsystem. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "ui/console.h" -#include "hw/arm/omap.h" - -struct omap_dss_s { - qemu_irq irq; - qemu_irq drq; - DisplayState *state; - MemoryRegion iomem_diss1, iomem_disc1, iomem_rfbi1, iomem_venc1, iomem_im3; - - int autoidle; - int control; - int enable; - - struct omap_dss_panel_s { - int enable; - int nx; - int ny; - - int x; - int y; - } dig, lcd; - - struct { - uint32_t idlemode; - uint32_t irqst; - uint32_t irqen; - uint32_t control; - uint32_t config; - uint32_t capable; - uint32_t timing[4]; - int line; - uint32_t bg[2]; - uint32_t trans[2]; - - struct omap_dss_plane_s { - int enable; - int bpp; - int posx; - int posy; - int nx; - int ny; - - hwaddr addr[3]; - - uint32_t attr; - uint32_t tresh; - int rowinc; - int colinc; - int wininc; - } l[3]; - - int invalidate; - uint16_t palette[256]; - } dispc; - - struct { - int idlemode; - uint32_t control; - int enable; - int pixels; - int busy; - int skiplines; - uint16_t rxbuf; - uint32_t config[2]; - uint32_t time[4]; - uint32_t data[6]; - uint16_t vsync; - uint16_t hsync; - struct rfbi_chip_s *chip[2]; - } rfbi; -}; - -static void omap_dispc_interrupt_update(struct omap_dss_s *s) -{ - qemu_set_irq(s->irq, s->dispc.irqst & s->dispc.irqen); -} - -static void omap_rfbi_reset(struct omap_dss_s *s) -{ - s->rfbi.idlemode = 0; - s->rfbi.control = 2; - s->rfbi.enable = 0; - s->rfbi.pixels = 0; - s->rfbi.skiplines = 0; - s->rfbi.busy = 0; - s->rfbi.config[0] = 0x00310000; - s->rfbi.config[1] = 0x00310000; - s->rfbi.time[0] = 0; - s->rfbi.time[1] = 0; - s->rfbi.time[2] = 0; - s->rfbi.time[3] = 0; - s->rfbi.data[0] = 0; - s->rfbi.data[1] = 0; - s->rfbi.data[2] = 0; - s->rfbi.data[3] = 0; - s->rfbi.data[4] = 0; - s->rfbi.data[5] = 0; - s->rfbi.vsync = 0; - s->rfbi.hsync = 0; -} - -void omap_dss_reset(struct omap_dss_s *s) -{ - s->autoidle = 0; - s->control = 0; - s->enable = 0; - - s->dig.enable = 0; - s->dig.nx = 1; - s->dig.ny = 1; - - s->lcd.enable = 0; - s->lcd.nx = 1; - s->lcd.ny = 1; - - s->dispc.idlemode = 0; - s->dispc.irqst = 0; - s->dispc.irqen = 0; - s->dispc.control = 0; - s->dispc.config = 0; - s->dispc.capable = 0x161; - s->dispc.timing[0] = 0; - s->dispc.timing[1] = 0; - s->dispc.timing[2] = 0; - s->dispc.timing[3] = 0; - s->dispc.line = 0; - s->dispc.bg[0] = 0; - s->dispc.bg[1] = 0; - s->dispc.trans[0] = 0; - s->dispc.trans[1] = 0; - - s->dispc.l[0].enable = 0; - s->dispc.l[0].bpp = 0; - s->dispc.l[0].addr[0] = 0; - s->dispc.l[0].addr[1] = 0; - s->dispc.l[0].addr[2] = 0; - s->dispc.l[0].posx = 0; - s->dispc.l[0].posy = 0; - s->dispc.l[0].nx = 1; - s->dispc.l[0].ny = 1; - s->dispc.l[0].attr = 0; - s->dispc.l[0].tresh = 0; - s->dispc.l[0].rowinc = 1; - s->dispc.l[0].colinc = 1; - s->dispc.l[0].wininc = 0; - - omap_rfbi_reset(s); - omap_dispc_interrupt_update(s); -} - -static uint64_t omap_diss_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ - return 0x20; - - case 0x10: /* DSS_SYSCONFIG */ - return s->autoidle; - - case 0x14: /* DSS_SYSSTATUS */ - return 1; /* RESETDONE */ - - 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 */ - /* TODO: fake some values when appropriate s->control bits are set */ - return 0; - - case 0x5c: /* DSS_STATUS */ - return 1 + (s->control & 1); - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_diss_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - 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 */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* DSS_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dss_reset(s); - s->autoidle = value & 1; - break; - - case 0x40: /* DSS_CONTROL */ - s->control = value & 0x3dd; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_diss_ops = { - .read = omap_diss_read, - .write = omap_diss_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_disc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* DISPC_REVISION */ - return 0x20; - - case 0x010: /* DISPC_SYSCONFIG */ - return s->dispc.idlemode; - - case 0x014: /* DISPC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* DISPC_IRQSTATUS */ - return s->dispc.irqst; - - case 0x01c: /* DISPC_IRQENABLE */ - return s->dispc.irqen; - - case 0x040: /* DISPC_CONTROL */ - return s->dispc.control; - - case 0x044: /* DISPC_CONFIG */ - return s->dispc.config; - - case 0x048: /* DISPC_CAPABLE */ - return s->dispc.capable; - - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ - return s->dispc.bg[0]; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ - return s->dispc.bg[1]; - case 0x054: /* DISPC_TRANS_COLOR0 */ - return s->dispc.trans[0]; - case 0x058: /* DISPC_TRANS_COLOR1 */ - return s->dispc.trans[1]; - - case 0x05c: /* DISPC_LINE_STATUS */ - return 0x7ff; - case 0x060: /* DISPC_LINE_NUMBER */ - return s->dispc.line; - - case 0x064: /* DISPC_TIMING_H */ - return s->dispc.timing[0]; - case 0x068: /* DISPC_TIMING_V */ - return s->dispc.timing[1]; - case 0x06c: /* DISPC_POL_FREQ */ - return s->dispc.timing[2]; - case 0x070: /* DISPC_DIVISOR */ - return s->dispc.timing[3]; - - case 0x078: /* DISPC_SIZE_DIG */ - return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1); - case 0x07c: /* DISPC_SIZE_LCD */ - return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1); - - case 0x080: /* DISPC_GFX_BA0 */ - return s->dispc.l[0].addr[0]; - case 0x084: /* DISPC_GFX_BA1 */ - return s->dispc.l[0].addr[1]; - case 0x088: /* DISPC_GFX_POSITION */ - return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx; - case 0x08c: /* DISPC_GFX_SIZE */ - return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1); - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ - return s->dispc.l[0].attr; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ - return s->dispc.l[0].tresh; - case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */ - return 256; - case 0x0ac: /* DISPC_GFX_ROW_INC */ - return s->dispc.l[0].rowinc; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ - return s->dispc.l[0].colinc; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ - return s->dispc.l[0].wininc; - 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 */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_disc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x010: /* DISPC_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dss_reset(s); - s->dispc.idlemode = value & 0x301b; - break; - - case 0x018: /* DISPC_IRQSTATUS */ - s->dispc.irqst &= ~value; - omap_dispc_interrupt_update(s); - break; - - case 0x01c: /* DISPC_IRQENABLE */ - s->dispc.irqen = value & 0xffff; - omap_dispc_interrupt_update(s); - break; - - 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 (!((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 */ - /* XXX: Shadowed fields are: - * s->dispc.config - * s->dispc.capable - * s->dispc.bg[0] - * s->dispc.bg[1] - * s->dispc.trans[0] - * s->dispc.trans[1] - * s->dispc.line - * s->dispc.timing[0] - * s->dispc.timing[1] - * s->dispc.timing[2] - * s->dispc.timing[3] - * s->lcd.nx - * s->lcd.ny - * s->dig.nx - * s->dig.ny - * s->dispc.l[0].addr[0] - * s->dispc.l[0].addr[1] - * s->dispc.l[0].addr[2] - * s->dispc.l[0].posx - * s->dispc.l[0].posy - * s->dispc.l[0].nx - * s->dispc.l[0].ny - * s->dispc.l[0].tresh - * s->dispc.l[0].rowinc - * s->dispc.l[0].colinc - * s->dispc.l[0].wininc - * All they need to be loaded here from their shadow registers. - */ - } - if (value & (1 << 5)) { /* GOLCD */ - /* XXX: Likewise for LCD here. */ - } - s->dispc.invalidate = 1; - break; - - case 0x044: /* DISPC_CONFIG */ - s->dispc.config = value & 0x3fff; - /* XXX: - * bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded - * bits 2:1 (LOADMODE) reset to 2 after set to 3 and palette loaded - */ - s->dispc.invalidate = 1; - break; - - case 0x048: /* DISPC_CAPABLE */ - s->dispc.capable = value & 0x3ff; - break; - - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ - s->dispc.bg[0] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ - s->dispc.bg[1] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x054: /* DISPC_TRANS_COLOR0 */ - s->dispc.trans[0] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x058: /* DISPC_TRANS_COLOR1 */ - s->dispc.trans[1] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - - case 0x060: /* DISPC_LINE_NUMBER */ - s->dispc.line = value & 0x7ff; - break; - - case 0x064: /* DISPC_TIMING_H */ - s->dispc.timing[0] = value & 0x0ff0ff3f; - break; - case 0x068: /* DISPC_TIMING_V */ - s->dispc.timing[1] = value & 0x0ff0ff3f; - break; - case 0x06c: /* DISPC_POL_FREQ */ - s->dispc.timing[2] = value & 0x0003ffff; - break; - 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 */ - 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 */ - s->dispc.invalidate = 1; - break; - case 0x080: /* DISPC_GFX_BA0 */ - s->dispc.l[0].addr[0] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - 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 */ - 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 */ - s->dispc.invalidate = 1; - break; - 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", - __func__); - s->dispc.l[0].enable = value & 1; - s->dispc.l[0].bpp = (value >> 1) & 0xf; - s->dispc.invalidate = 1; - break; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ - s->dispc.l[0].tresh = value & 0x01ff01ff; - break; - case 0x0ac: /* DISPC_GFX_ROW_INC */ - s->dispc.l[0].rowinc = value; - s->dispc.invalidate = 1; - break; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ - s->dispc.l[0].colinc = value; - s->dispc.invalidate = 1; - break; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ - s->dispc.l[0].wininc = value; - break; - 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 */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_disc_ops = { - .read = omap_disc_read, - .write = omap_disc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_rfbi_transfer_stop(struct omap_dss_s *s) -{ - if (!s->rfbi.busy) - return; - - /* TODO: in non-Bypass mode we probably need to just deassert the DRQ. */ - - s->rfbi.busy = 0; -} - -static void omap_rfbi_transfer_start(struct omap_dss_s *s) -{ - void *data; - hwaddr len; - hwaddr data_addr; - int pitch; - static void *bounce_buffer; - static hwaddr bounce_len; - - if (!s->rfbi.enable || s->rfbi.busy) - return; - - 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 */ - return; - /* TODO: check that LCD output is enabled in DISPC. */ - - s->rfbi.busy = 1; - - len = s->rfbi.pixels * 2; - - data_addr = s->dispc.l[0].addr[0]; - data = cpu_physical_memory_map(data_addr, &len, false); - if (data && len != s->rfbi.pixels * 2) { - cpu_physical_memory_unmap(data, len, 0, 0); - data = NULL; - len = s->rfbi.pixels * 2; - } - if (!data) { - if (len > bounce_len) { - bounce_buffer = g_realloc(bounce_buffer, len); - } - data = bounce_buffer; - cpu_physical_memory_read(data_addr, data, len); - } - - /* TODO bpp */ - s->rfbi.pixels = 0; - - /* TODO: negative values */ - pitch = s->dispc.l[0].nx + (s->dispc.l[0].rowinc - 1) / 2; - - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->block(s->rfbi.chip[0]->opaque, 1, data, len, pitch); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->block(s->rfbi.chip[1]->opaque, 1, data, len, pitch); - - if (data != bounce_buffer) { - cpu_physical_memory_unmap(data, len, 0, len); - } - - omap_rfbi_transfer_stop(s); - - /* TODO */ - s->dispc.irqst |= 1; /* FRAMEDONE */ - omap_dispc_interrupt_update(s); -} - -static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* RFBI_REVISION */ - return 0x10; - - case 0x10: /* RFBI_SYSCONFIG */ - return s->rfbi.idlemode; - - case 0x14: /* RFBI_SYSSTATUS */ - return 1 | (s->rfbi.busy << 8); /* RESETDONE */ - - case 0x40: /* RFBI_CONTROL */ - return s->rfbi.control; - - case 0x44: /* RFBI_PIXELCNT */ - return s->rfbi.pixels; - - case 0x48: /* RFBI_LINE_NUMBER */ - return s->rfbi.skiplines; - - case 0x58: /* RFBI_READ */ - case 0x5c: /* RFBI_STATUS */ - return s->rfbi.rxbuf; - - case 0x60: /* RFBI_CONFIG0 */ - return s->rfbi.config[0]; - case 0x64: /* RFBI_ONOFF_TIME0 */ - return s->rfbi.time[0]; - case 0x68: /* RFBI_CYCLE_TIME0 */ - return s->rfbi.time[1]; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ - return s->rfbi.data[0]; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ - return s->rfbi.data[1]; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ - return s->rfbi.data[2]; - - case 0x78: /* RFBI_CONFIG1 */ - return s->rfbi.config[1]; - case 0x7c: /* RFBI_ONOFF_TIME1 */ - return s->rfbi.time[2]; - case 0x80: /* RFBI_CYCLE_TIME1 */ - return s->rfbi.time[3]; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ - return s->rfbi.data[3]; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ - return s->rfbi.data[4]; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ - return s->rfbi.data[5]; - - case 0x90: /* RFBI_VSYNC_WIDTH */ - return s->rfbi.vsync; - case 0x94: /* RFBI_HSYNC_WIDTH */ - return s->rfbi.hsync; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_rfbi_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x10: /* RFBI_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_rfbi_reset(s); - s->rfbi.idlemode = value & 0x19; - break; - - case 0x40: /* RFBI_CONTROL */ - s->rfbi.control = value & 0xf; - s->rfbi.enable = value & 1; - if (value & (1 << 4) && /* ITE */ - !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc)) - omap_rfbi_transfer_start(s); - break; - - case 0x44: /* RFBI_PIXELCNT */ - s->rfbi.pixels = value; - break; - - case 0x48: /* RFBI_LINE_NUMBER */ - s->rfbi.skiplines = value & 0x7ff; - break; - - 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 */ - 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 */ - /* 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. */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) { - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value >> 16); - } - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) { - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value >> 16); - } - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - 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]) - s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 1); - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - - 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]) - s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 0); - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - - case 0x60: /* RFBI_CONFIG0 */ - s->rfbi.config[0] = value & 0x003f1fff; - break; - - case 0x64: /* RFBI_ONOFF_TIME0 */ - s->rfbi.time[0] = value & 0x3fffffff; - break; - case 0x68: /* RFBI_CYCLE_TIME0 */ - s->rfbi.time[1] = value & 0x0fffffff; - break; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ - s->rfbi.data[0] = value & 0x0f1f0f1f; - break; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ - s->rfbi.data[1] = value & 0x0f1f0f1f; - break; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ - s->rfbi.data[2] = value & 0x0f1f0f1f; - break; - case 0x78: /* RFBI_CONFIG1 */ - s->rfbi.config[1] = value & 0x003f1fff; - break; - - case 0x7c: /* RFBI_ONOFF_TIME1 */ - s->rfbi.time[2] = value & 0x3fffffff; - break; - case 0x80: /* RFBI_CYCLE_TIME1 */ - s->rfbi.time[3] = value & 0x0fffffff; - break; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ - s->rfbi.data[3] = value & 0x0f1f0f1f; - break; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ - s->rfbi.data[4] = value & 0x0f1f0f1f; - break; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ - s->rfbi.data[5] = value & 0x0f1f0f1f; - break; - - case 0x90: /* RFBI_VSYNC_WIDTH */ - s->rfbi.vsync = value & 0xffff; - break; - case 0x94: /* RFBI_HSYNC_WIDTH */ - s->rfbi.hsync = value & 0xffff; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_rfbi_ops = { - .read = omap_rfbi_read, - .write = omap_rfbi_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_venc_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 4) { - return omap_badwidth_read32(opaque, 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 */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_venc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, size); - return; - } - - 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 */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_venc_ops = { - .read = omap_venc_read, - .write = omap_venc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_im3_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 4) { - return omap_badwidth_read32(opaque, 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 */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_im3_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_im3_ops = { - .read = omap_im3_read, - .write = omap_im3_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, - MemoryRegion *sysmem, - hwaddr l3_base, - qemu_irq irq, qemu_irq drq, - omap_clk fck1, omap_clk fck2, omap_clk ck54m, - omap_clk ick1, omap_clk ick2) -{ - struct omap_dss_s *s = g_new0(struct omap_dss_s, 1); - - s->irq = irq; - s->drq = drq; - omap_dss_reset(s); - - memory_region_init_io(&s->iomem_diss1, NULL, &omap_diss_ops, s, "omap.diss1", - omap_l4_region_size(ta, 0)); - memory_region_init_io(&s->iomem_disc1, NULL, &omap_disc_ops, s, "omap.disc1", - omap_l4_region_size(ta, 1)); - memory_region_init_io(&s->iomem_rfbi1, NULL, &omap_rfbi_ops, s, "omap.rfbi1", - omap_l4_region_size(ta, 2)); - memory_region_init_io(&s->iomem_venc1, NULL, &omap_venc_ops, s, "omap.venc1", - omap_l4_region_size(ta, 3)); - memory_region_init_io(&s->iomem_im3, NULL, &omap_im3_ops, s, - "omap.im3", 0x1000); - - omap_l4_attach(ta, 0, &s->iomem_diss1); - omap_l4_attach(ta, 1, &s->iomem_disc1); - omap_l4_attach(ta, 2, &s->iomem_rfbi1); - omap_l4_attach(ta, 3, &s->iomem_venc1); - memory_region_add_subregion(sysmem, l3_base, &s->iomem_im3); - -#if 0 - s->state = graphic_console_init(omap_update_display, - omap_invalidate_display, omap_screen_dump, s); -#endif - - return s; -} - -void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip) -{ - if (cs < 0 || cs > 1) - hw_error("%s: wrong CS %i\n", __func__, cs); - s->rfbi.chip[cs] = chip; -} 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 deleted file mode 100644 index eb83d88222..0000000000 --- a/hw/display/pxa2xx_lcd.c +++ /dev/null @@ -1,1451 +0,0 @@ -/* - * Intel XScale PXA255/270 LCDC emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * 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/log.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "ui/console.h" -#include "hw/arm/pxa.h" -#include "ui/pixel_ops.h" -#include "hw/boards.h" -/* FIXME: For graphic_rotate. Should probably be done in common code. */ -#include "sysemu/sysemu.h" -#include "framebuffer.h" - -struct DMAChannel { - uint32_t branch; - uint8_t up; - uint8_t palette[1024]; - uint8_t pbuffer[1024]; - void (*redraw)(PXA2xxLCDState *s, hwaddr addr, - int *miny, int *maxy); - - uint32_t descriptor; - uint32_t source; - uint32_t id; - uint32_t command; -}; - -struct PXA2xxLCDState { - MemoryRegion *sysmem; - MemoryRegion iomem; - MemoryRegionSection fbsection; - qemu_irq irq; - int irqlevel; - - int invalidated; - QemuConsole *con; - int dest_width; - int xres, yres; - int pal_for; - int transp; - enum { - pxa_lcdc_2bpp = 1, - pxa_lcdc_4bpp = 2, - pxa_lcdc_8bpp = 3, - pxa_lcdc_16bpp = 4, - pxa_lcdc_18bpp = 5, - pxa_lcdc_18pbpp = 6, - pxa_lcdc_19bpp = 7, - pxa_lcdc_19pbpp = 8, - pxa_lcdc_24bpp = 9, - pxa_lcdc_25bpp = 10, - } bpp; - - uint32_t control[6]; - uint32_t status[2]; - uint32_t ovl1c[2]; - uint32_t ovl2c[2]; - uint32_t ccr; - uint32_t cmdcr; - uint32_t trgbr; - uint32_t tcr; - uint32_t liidr; - uint8_t bscntr; - - struct DMAChannel dma_ch[7]; - - qemu_irq vsync_cb; - int orientation; -}; - -typedef struct QEMU_PACKED { - uint32_t fdaddr; - uint32_t fsaddr; - uint32_t fidr; - 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 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 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 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 */ - -/* LCD Buffer Strength Control register */ -#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) - -/* Size of a pixel in the QEMU UI output surface, in bytes */ -#define DEST_PIXEL_WIDTH 4 - -/* Line drawing code to handle the various possible guest pixel formats */ - -# define SKIP_PIXEL(to) do { to += deststep; } while (0) -# define COPY_PIXEL(to, from) \ - do { \ - *(uint32_t *) to = from; \ - SKIP_PIXEL(to); \ - } while (0) - -#if HOST_BIG_ENDIAN -# define SWAP_WORDS 1 -#endif - -#define FN_2(x) FN(x + 1) FN(x) -#define FN_4(x) FN_2(x + 2) FN_2(x) - -static void pxa2xx_draw_line2(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *) src; -#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]); -#ifdef SWAP_WORDS - FN_4(12) - FN_4(8) - FN_4(4) - FN_4(0) -#else - FN_4(0) - FN_4(4) - FN_4(8) - FN_4(12) -#endif -#undef FN - width -= 16; - src += 4; - } -} - -static void pxa2xx_draw_line4(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *) src; -#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]); -#ifdef SWAP_WORDS - FN_2(6) - FN_2(4) - FN_2(2) - FN_2(0) -#else - FN_2(0) - FN_2(2) - FN_2(4) - FN_2(6) -#endif -#undef FN - width -= 8; - src += 4; - } -} - -static void pxa2xx_draw_line8(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t *palette = opaque; - uint32_t data; - while (width > 0) { - data = *(uint32_t *) src; -#define FN(x) COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]); -#ifdef SWAP_WORDS - FN(24) - FN(16) - FN(8) - FN(0) -#else - FN(0) - FN(8) - FN(16) - FN(24) -#endif -#undef FN - width -= 4; - src += 4; - } -} - -static void pxa2xx_draw_line16(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - data >>= 5; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x1f) << 3; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - width -= 2; - src += 4; - } -} - -static void pxa2xx_draw_line16t(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x1f) << 3; - data >>= 5; - r = (data & 0x1f) << 3; - data >>= 5; - if (data & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - data >>= 1; - b = (data & 0x1f) << 3; - data >>= 5; - g = (data & 0x1f) << 3; - data >>= 5; - r = (data & 0x1f) << 3; - data >>= 5; - if (data & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - width -= 2; - src += 4; - } -} - -static void pxa2xx_draw_line18(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x3f) << 2; - data >>= 6; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x3f) << 2; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - width -= 1; - src += 4; - } -} - -/* The wicked packed format */ -static void pxa2xx_draw_line18p(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data[3]; - unsigned int r, g, b; - while (width > 0) { - data[0] = *(uint32_t *) src; - src += 4; - data[1] = *(uint32_t *) src; - src += 4; - data[2] = *(uint32_t *) src; - src += 4; -#ifdef SWAP_WORDS - data[0] = bswap32(data[0]); - data[1] = bswap32(data[1]); - data[2] = bswap32(data[2]); -#endif - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = (data[0] & 0x3f) << 2; - data[0] >>= 6; - r = (data[0] & 0x3f) << 2; - data[0] >>= 12; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = ((data[1] & 0xf) << 4) | (data[0] << 2); - data[1] >>= 4; - r = (data[1] & 0x3f) << 2; - data[1] >>= 12; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - b = (data[1] & 0x3f) << 2; - data[1] >>= 6; - g = (data[1] & 0x3f) << 2; - data[1] >>= 6; - r = ((data[2] & 0x3) << 6) | (data[1] << 2); - data[2] >>= 8; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - b = (data[2] & 0x3f) << 2; - data[2] >>= 6; - g = (data[2] & 0x3f) << 2; - data[2] >>= 6; - r = data[2] << 2; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - width -= 4; - } -} - -static void pxa2xx_draw_line19(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x3f) << 2; - data >>= 6; - g = (data & 0x3f) << 2; - data >>= 6; - r = (data & 0x3f) << 2; - data >>= 6; - if (data & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - width -= 1; - src += 4; - } -} - -/* The wicked packed format */ -static void pxa2xx_draw_line19p(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data[3]; - unsigned int r, g, b; - while (width > 0) { - data[0] = *(uint32_t *) src; - src += 4; - data[1] = *(uint32_t *) src; - src += 4; - data[2] = *(uint32_t *) src; - src += 4; -# ifdef SWAP_WORDS - data[0] = bswap32(data[0]); - data[1] = bswap32(data[1]); - data[2] = bswap32(data[2]); -# endif - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = (data[0] & 0x3f) << 2; - data[0] >>= 6; - r = (data[0] & 0x3f) << 2; - data[0] >>= 6; - if (data[0] & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - data[0] >>= 6; - b = (data[0] & 0x3f) << 2; - data[0] >>= 6; - g = ((data[1] & 0xf) << 4) | (data[0] << 2); - data[1] >>= 4; - r = (data[1] & 0x3f) << 2; - data[1] >>= 6; - if (data[1] & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - data[1] >>= 6; - b = (data[1] & 0x3f) << 2; - data[1] >>= 6; - g = (data[1] & 0x3f) << 2; - data[1] >>= 6; - r = ((data[2] & 0x3) << 6) | (data[1] << 2); - data[2] >>= 2; - if (data[2] & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - data[2] >>= 6; - b = (data[2] & 0x3f) << 2; - data[2] >>= 6; - g = (data[2] & 0x3f) << 2; - data[2] >>= 6; - r = data[2] << 2; - data[2] >>= 6; - if (data[2] & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - width -= 4; - } -} - -static void pxa2xx_draw_line24(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = data & 0xff; - data >>= 8; - g = data & 0xff; - data >>= 8; - r = data & 0xff; - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - width -= 1; - src += 4; - } -} - -static void pxa2xx_draw_line24t(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = (data & 0x7f) << 1; - data >>= 7; - g = data & 0xff; - data >>= 8; - r = data & 0xff; - data >>= 8; - if (data & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - width -= 1; - src += 4; - } -} - -static void pxa2xx_draw_line25(void *opaque, uint8_t *dest, const uint8_t *src, - int width, int deststep) -{ - uint32_t data; - unsigned int r, g, b; - while (width > 0) { - data = *(uint32_t *) src; -#ifdef SWAP_WORDS - data = bswap32(data); -#endif - b = data & 0xff; - data >>= 8; - g = data & 0xff; - data >>= 8; - r = data & 0xff; - data >>= 8; - if (data & 1) { - SKIP_PIXEL(dest); - } else { - COPY_PIXEL(dest, rgb_to_pixel32(r, g, b)); - } - width -= 1; - src += 4; - } -} - -/* Overlay planes disabled, no transparency */ -static drawfn pxa2xx_draw_fn_32[16] = { - [0 ... 0xf] = NULL, - [pxa_lcdc_2bpp] = pxa2xx_draw_line2, - [pxa_lcdc_4bpp] = pxa2xx_draw_line4, - [pxa_lcdc_8bpp] = pxa2xx_draw_line8, - [pxa_lcdc_16bpp] = pxa2xx_draw_line16, - [pxa_lcdc_18bpp] = pxa2xx_draw_line18, - [pxa_lcdc_18pbpp] = pxa2xx_draw_line18p, - [pxa_lcdc_24bpp] = pxa2xx_draw_line24, -}; - -/* Overlay planes enabled, transparency used */ -static drawfn pxa2xx_draw_fn_32t[16] = { - [0 ... 0xf] = NULL, - [pxa_lcdc_4bpp] = pxa2xx_draw_line4, - [pxa_lcdc_8bpp] = pxa2xx_draw_line8, - [pxa_lcdc_16bpp] = pxa2xx_draw_line16t, - [pxa_lcdc_19bpp] = pxa2xx_draw_line19, - [pxa_lcdc_19pbpp] = pxa2xx_draw_line19p, - [pxa_lcdc_24bpp] = pxa2xx_draw_line24t, - [pxa_lcdc_25bpp] = pxa2xx_draw_line25, -}; - -#undef COPY_PIXEL -#undef SKIP_PIXEL - -#ifdef SWAP_WORDS -# undef SWAP_WORDS -#endif - -/* Route internal interrupt lines to the global IC */ -static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s) -{ - int level = 0; - level |= (s->status[0] & LCSR0_LDD) && !(s->control[0] & LCCR0_LDM); - level |= (s->status[0] & LCSR0_SOF0) && !(s->control[0] & LCCR0_SOFM0); - level |= (s->status[0] & LCSR0_IU0) && !(s->control[0] & LCCR0_IUM); - level |= (s->status[0] & LCSR0_IU1) && !(s->control[5] & LCCR5_IUM(1)); - level |= (s->status[0] & LCSR0_OU) && !(s->control[0] & LCCR0_OUM); - level |= (s->status[0] & LCSR0_QD) && !(s->control[0] & LCCR0_QDM); - level |= (s->status[0] & LCSR0_EOF0) && !(s->control[0] & LCCR0_EOFM0); - level |= (s->status[0] & LCSR0_BS0) && !(s->control[0] & LCCR0_BSM0); - level |= (s->status[0] & LCSR0_RDST) && !(s->control[0] & LCCR0_RDSTM); - level |= (s->status[0] & LCSR0_CMDINT) && !(s->control[0] & LCCR0_CMDIM); - level |= (s->status[1] & ~s->control[5]); - - qemu_set_irq(s->irq, !!level); - s->irqlevel = level; -} - -/* Set Branch Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_bs_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (ch == 0) { - s->status[0] |= LCSR0_BS0; - unmasked = !(s->control[0] & LCCR0_BSM0); - } else { - s->status[1] |= LCSR1_BS(ch); - unmasked = !(s->control[5] & LCCR5_BSM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set Start Of Frame Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_sof_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (!(s->dma_ch[ch].command & LDCMD_SOFINT)) - return; - - if (ch == 0) { - s->status[0] |= LCSR0_SOF0; - unmasked = !(s->control[0] & LCCR0_SOFM0); - } else { - s->status[1] |= LCSR1_SOF(ch); - unmasked = !(s->control[5] & LCCR5_SOFM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set End Of Frame Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_eof_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (!(s->dma_ch[ch].command & LDCMD_EOFINT)) - return; - - if (ch == 0) { - s->status[0] |= LCSR0_EOF0; - unmasked = !(s->control[0] & LCCR0_EOFM0); - } else { - s->status[1] |= LCSR1_EOF(ch); - unmasked = !(s->control[5] & LCCR5_EOFM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set Bus Error Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_ber_set(PXA2xxLCDState *s, int ch) -{ - s->status[0] |= LCSR0_BERCH(ch) | LCSR0_BER; - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; -} - -/* Load new Frame Descriptors from DMA */ -static void pxa2xx_descriptor_load(PXA2xxLCDState *s) -{ - PXAFrameDescriptor desc; - hwaddr descptr; - int i; - - for (i = 0; i < PXA_LCDDMA_CHANS; i ++) { - s->dma_ch[i].source = 0; - - if (!s->dma_ch[i].up) - continue; - - if (s->dma_ch[i].branch & FBR_BRA) { - descptr = s->dma_ch[i].branch & FBR_SRCADDR; - if (s->dma_ch[i].branch & FBR_BINT) - pxa2xx_dma_bs_set(s, i); - s->dma_ch[i].branch &= ~FBR_BRA; - } else - descptr = s->dma_ch[i].descriptor; - - if (!((descptr >= PXA2XX_SDRAM_BASE && descptr + - sizeof(desc) <= PXA2XX_SDRAM_BASE + current_machine->ram_size) || - (descptr >= PXA2XX_INTERNAL_BASE && descptr + sizeof(desc) <= - PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { - continue; - } - - cpu_physical_memory_read(descptr, &desc, sizeof(desc)); - s->dma_ch[i].descriptor = le32_to_cpu(desc.fdaddr); - s->dma_ch[i].source = le32_to_cpu(desc.fsaddr); - s->dma_ch[i].id = le32_to_cpu(desc.fidr); - s->dma_ch[i].command = le32_to_cpu(desc.ldcmd); - } -} - -static uint64_t pxa2xx_lcdc_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int ch; - - switch (offset) { - case LCCR0: - return s->control[0]; - case LCCR1: - return s->control[1]; - case LCCR2: - return s->control[2]; - case LCCR3: - return s->control[3]; - case LCCR4: - return s->control[4]; - case LCCR5: - return s->control[5]; - - case OVL1C1: - return s->ovl1c[0]; - case OVL1C2: - return s->ovl1c[1]; - case OVL2C1: - return s->ovl2c[0]; - case OVL2C2: - return s->ovl2c[1]; - - case CCR: - return s->ccr; - - case CMDCR: - return s->cmdcr; - - case TRGBR: - return s->trgbr; - case TCR: - return s->tcr; - - case 0x200 ... 0x1000: /* DMA per-channel registers */ - ch = (offset - 0x200) >> 4; - if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) - goto fail; - - switch (offset & 0xf) { - case DMA_FDADR: - return s->dma_ch[ch].descriptor; - case DMA_FSADR: - return s->dma_ch[ch].source; - case DMA_FIDR: - return s->dma_ch[ch].id; - case DMA_LDCMD: - return s->dma_ch[ch].command; - default: - goto fail; - } - - case FBR0: - return s->dma_ch[0].branch; - case FBR1: - return s->dma_ch[1].branch; - case FBR2: - return s->dma_ch[2].branch; - case FBR3: - return s->dma_ch[3].branch; - case FBR4: - return s->dma_ch[4].branch; - case FBR5: - return s->dma_ch[5].branch; - case FBR6: - return s->dma_ch[6].branch; - - case BSCNTR: - return s->bscntr; - - case PRSR: - return 0; - - case LCSR0: - return s->status[0]; - case LCSR1: - return s->status[1]; - case LIIDR: - return s->liidr; - - default: - fail: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", - __func__, offset); - } - - return 0; -} - -static void pxa2xx_lcdc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int ch; - - switch (offset) { - case LCCR0: - /* ACK Quick Disable done */ - if ((s->control[0] & LCCR0_ENB) && !(value & LCCR0_ENB)) - s->status[0] |= LCSR0_QD; - - if (!(s->control[0] & LCCR0_LCDT) && (value & LCCR0_LCDT)) { - qemu_log_mask(LOG_UNIMP, - "%s: internal frame buffer unsupported\n", __func__); - } - if ((s->control[3] & LCCR3_API) && - (value & LCCR0_ENB) && !(value & LCCR0_LCDT)) - s->status[0] |= LCSR0_ABC; - - s->control[0] = value & 0x07ffffff; - pxa2xx_lcdc_int_update(s); - - s->dma_ch[0].up = !!(value & LCCR0_ENB); - s->dma_ch[1].up = (s->ovl1c[0] & OVLC1_EN) || (value & LCCR0_SDS); - break; - - case LCCR1: - s->control[1] = value; - break; - - case LCCR2: - s->control[2] = value; - break; - - case LCCR3: - s->control[3] = value & 0xefffffff; - s->bpp = LCCR3_BPP(value); - break; - - case LCCR4: - s->control[4] = value & 0x83ff81ff; - break; - - case LCCR5: - s->control[5] = value & 0x3f3f3f3f; - break; - - case OVL1C1: - if (!(s->ovl1c[0] & OVLC1_EN) && (value & OVLC1_EN)) { - qemu_log_mask(LOG_UNIMP, "%s: Overlay 1 not supported\n", __func__); - } - s->ovl1c[0] = value & 0x80ffffff; - s->dma_ch[1].up = (value & OVLC1_EN) || (s->control[0] & LCCR0_SDS); - break; - - case OVL1C2: - s->ovl1c[1] = value & 0x000fffff; - break; - - case OVL2C1: - if (!(s->ovl2c[0] & OVLC1_EN) && (value & OVLC1_EN)) { - qemu_log_mask(LOG_UNIMP, "%s: Overlay 2 not supported\n", __func__); - } - s->ovl2c[0] = value & 0x80ffffff; - s->dma_ch[2].up = !!(value & OVLC1_EN); - s->dma_ch[3].up = !!(value & OVLC1_EN); - s->dma_ch[4].up = !!(value & OVLC1_EN); - break; - - case OVL2C2: - s->ovl2c[1] = value & 0x007fffff; - break; - - case CCR: - if (!(s->ccr & CCR_CEN) && (value & CCR_CEN)) { - qemu_log_mask(LOG_UNIMP, - "%s: Hardware cursor unimplemented\n", __func__); - } - s->ccr = value & 0x81ffffe7; - s->dma_ch[5].up = !!(value & CCR_CEN); - break; - - case CMDCR: - s->cmdcr = value & 0xff; - break; - - case TRGBR: - s->trgbr = value & 0x00ffffff; - break; - - case TCR: - s->tcr = value & 0x7fff; - break; - - case 0x200 ... 0x1000: /* DMA per-channel registers */ - ch = (offset - 0x200) >> 4; - if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) - goto fail; - - switch (offset & 0xf) { - case DMA_FDADR: - s->dma_ch[ch].descriptor = value & 0xfffffff0; - break; - - default: - goto fail; - } - break; - - case FBR0: - s->dma_ch[0].branch = value & 0xfffffff3; - break; - case FBR1: - s->dma_ch[1].branch = value & 0xfffffff3; - break; - case FBR2: - s->dma_ch[2].branch = value & 0xfffffff3; - break; - case FBR3: - s->dma_ch[3].branch = value & 0xfffffff3; - break; - case FBR4: - s->dma_ch[4].branch = value & 0xfffffff3; - break; - case FBR5: - s->dma_ch[5].branch = value & 0xfffffff3; - break; - case FBR6: - s->dma_ch[6].branch = value & 0xfffffff3; - break; - - case BSCNTR: - s->bscntr = value & 0xf; - break; - - case PRSR: - break; - - case LCSR0: - s->status[0] &= ~(value & 0xfff); - if (value & LCSR0_BER) - s->status[0] &= ~LCSR0_BERCH(7); - break; - - case LCSR1: - s->status[1] &= ~(value & 0x3e3f3f); - break; - - default: - fail: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", - __func__, offset); - } -} - -static const MemoryRegionOps pxa2xx_lcdc_ops = { - .read = pxa2xx_lcdc_read, - .write = pxa2xx_lcdc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* Load new palette for a given DMA channel, convert to internal format */ -static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, n, format, r, g, b, alpha; - uint32_t *dest; - uint8_t *src; - s->pal_for = LCCR4_PALFOR(s->control[4]); - format = s->pal_for; - - switch (bpp) { - case pxa_lcdc_2bpp: - n = 4; - break; - case pxa_lcdc_4bpp: - n = 16; - break; - case pxa_lcdc_8bpp: - n = 256; - break; - default: - return; - } - - src = (uint8_t *) s->dma_ch[ch].pbuffer; - dest = (uint32_t *) s->dma_ch[ch].palette; - alpha = r = g = b = 0; - - for (i = 0; i < n; i ++) { - switch (format) { - case 0: /* 16 bpp, no transparency */ - alpha = 0; - if (s->control[0] & LCCR0_CMS) { - r = g = b = *(uint16_t *) src & 0xff; - } - else { - r = (*(uint16_t *) src & 0xf800) >> 8; - g = (*(uint16_t *) src & 0x07e0) >> 3; - b = (*(uint16_t *) src & 0x001f) << 3; - } - src += 2; - break; - case 1: /* 16 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xf80000) >> 16; - g = (*(uint32_t *) src & 0x00fc00) >> 8; - b = (*(uint32_t *) src & 0x0000f8); - } - src += 4; - break; - case 2: /* 18 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xfc0000) >> 16; - g = (*(uint32_t *) src & 0x00fc00) >> 8; - b = (*(uint32_t *) src & 0x0000fc); - } - src += 4; - break; - case 3: /* 24 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xff0000) >> 16; - g = (*(uint32_t *) src & 0x00ff00) >> 8; - b = (*(uint32_t *) src & 0x0000ff); - } - src += 4; - break; - } - switch (surface_bits_per_pixel(surface)) { - case 8: - *dest = rgb_to_pixel8(r, g, b) | alpha; - break; - case 15: - *dest = rgb_to_pixel15(r, g, b) | alpha; - break; - case 16: - *dest = rgb_to_pixel16(r, g, b) | alpha; - break; - case 24: - *dest = rgb_to_pixel24(r, g, b) | alpha; - break; - case 32: - *dest = rgb_to_pixel32(r, g, b) | alpha; - break; - } - dest ++; - } -} - -static inline drawfn pxa2xx_drawfn(PXA2xxLCDState *s) -{ - if (s->transp) { - return pxa2xx_draw_fn_32t[s->bpp]; - } else { - return pxa2xx_draw_fn_32[s->bpp]; - } -} - -static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = pxa2xx_drawfn(s); - if (!fn) - return; - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) - src_width *= 3; - else if (s->bpp > pxa_lcdc_16bpp) - src_width *= 4; - else if (s->bpp > pxa_lcdc_8bpp) - src_width *= 2; - - dest_width = s->xres * DEST_PIXEL_WIDTH; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, dest_width, DEST_PIXEL_WIDTH, - s->invalidated, - fn, s->dma_ch[0].palette, miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = pxa2xx_drawfn(s); - if (!fn) - return; - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) - src_width *= 3; - else if (s->bpp > pxa_lcdc_16bpp) - src_width *= 4; - else if (s->bpp > pxa_lcdc_8bpp) - src_width *= 2; - - dest_width = s->yres * DEST_PIXEL_WIDTH; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, DEST_PIXEL_WIDTH, -dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, - miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = pxa2xx_drawfn(s); - if (!fn) { - return; - } - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { - src_width *= 3; - } else if (s->bpp > pxa_lcdc_16bpp) { - src_width *= 4; - } else if (s->bpp > pxa_lcdc_8bpp) { - src_width *= 2; - } - - dest_width = s->xres * DEST_PIXEL_WIDTH; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, -dest_width, -DEST_PIXEL_WIDTH, - s->invalidated, - fn, s->dma_ch[0].palette, miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = pxa2xx_drawfn(s); - if (!fn) { - return; - } - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { - src_width *= 3; - } else if (s->bpp > pxa_lcdc_16bpp) { - src_width *= 4; - } else if (s->bpp > pxa_lcdc_8bpp) { - src_width *= 2; - } - - dest_width = s->yres * DEST_PIXEL_WIDTH; - *miny = 0; - if (s->invalidated) { - framebuffer_update_memory_section(&s->fbsection, s->sysmem, - addr, s->yres, src_width); - } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, - src_width, -DEST_PIXEL_WIDTH, dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, - miny, maxy); -} - -static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) -{ - int width, height; - if (!(s->control[0] & LCCR0_ENB)) - return; - - width = LCCR1_PPL(s->control[1]) + 1; - height = LCCR2_LPP(s->control[2]) + 1; - - if (width != s->xres || height != s->yres) { - if (s->orientation == 90 || s->orientation == 270) { - qemu_console_resize(s->con, height, width); - } else { - qemu_console_resize(s->con, width, height); - } - s->invalidated = 1; - s->xres = width; - s->yres = height; - } -} - -static void pxa2xx_update_display(void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - hwaddr fbptr; - int miny, maxy; - int ch; - if (!(s->control[0] & LCCR0_ENB)) - return; - - pxa2xx_descriptor_load(s); - - pxa2xx_lcdc_resize(s); - miny = s->yres; - maxy = 0; - s->transp = s->dma_ch[2].up || s->dma_ch[3].up; - /* Note: With overlay planes the order depends on LCCR0 bit 25. */ - for (ch = 0; ch < PXA_LCDDMA_CHANS; ch ++) - if (s->dma_ch[ch].up) { - if (!s->dma_ch[ch].source) { - pxa2xx_dma_ber_set(s, ch); - continue; - } - fbptr = s->dma_ch[ch].source; - if (!((fbptr >= PXA2XX_SDRAM_BASE && - fbptr <= PXA2XX_SDRAM_BASE + current_machine->ram_size) || - (fbptr >= PXA2XX_INTERNAL_BASE && - fbptr <= PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { - pxa2xx_dma_ber_set(s, ch); - continue; - } - - if (s->dma_ch[ch].command & LDCMD_PAL) { - cpu_physical_memory_read(fbptr, s->dma_ch[ch].pbuffer, - MAX(LDCMD_LENGTH(s->dma_ch[ch].command), - sizeof(s->dma_ch[ch].pbuffer))); - pxa2xx_palette_parse(s, ch, s->bpp); - } else { - /* Do we need to reparse palette */ - if (LCCR4_PALFOR(s->control[4]) != s->pal_for) - pxa2xx_palette_parse(s, ch, s->bpp); - - /* ACK frame start */ - pxa2xx_dma_sof_set(s, ch); - - s->dma_ch[ch].redraw(s, fbptr, &miny, &maxy); - s->invalidated = 0; - - /* ACK frame completed */ - pxa2xx_dma_eof_set(s, ch); - } - } - - if (s->control[0] & LCCR0_DIS) { - /* ACK last frame completed */ - s->control[0] &= ~LCCR0_ENB; - s->status[0] |= LCSR0_LDD; - } - - if (miny >= 0) { - switch (s->orientation) { - case 0: - dpy_gfx_update(s->con, 0, miny, s->xres, maxy - miny + 1); - break; - case 90: - dpy_gfx_update(s->con, miny, 0, maxy - miny + 1, s->xres); - break; - case 180: - maxy = s->yres - maxy - 1; - miny = s->yres - miny - 1; - dpy_gfx_update(s->con, 0, maxy, s->xres, miny - maxy + 1); - break; - case 270: - maxy = s->yres - maxy - 1; - miny = s->yres - miny - 1; - dpy_gfx_update(s->con, maxy, 0, miny - maxy + 1, s->xres); - break; - } - } - pxa2xx_lcdc_int_update(s); - - qemu_irq_raise(s->vsync_cb); -} - -static void pxa2xx_invalidate_display(void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - s->invalidated = 1; -} - -static void pxa2xx_lcdc_orientation(void *opaque, int angle) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - - switch (angle) { - case 0: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot0; - break; - case 90: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot90; - break; - case 180: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot180; - break; - case 270: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot270; - break; - } - - s->orientation = angle; - s->xres = s->yres = -1; - pxa2xx_lcdc_resize(s); -} - -static const VMStateDescription vmstate_dma_channel = { - .name = "dma_channel", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(branch, struct DMAChannel), - VMSTATE_UINT8(up, struct DMAChannel), - VMSTATE_BUFFER(pbuffer, struct DMAChannel), - VMSTATE_UINT32(descriptor, struct DMAChannel), - VMSTATE_UINT32(source, struct DMAChannel), - VMSTATE_UINT32(id, struct DMAChannel), - VMSTATE_UINT32(command, struct DMAChannel), - VMSTATE_END_OF_LIST() - } -}; - -static int pxa2xx_lcdc_post_load(void *opaque, int version_id) -{ - PXA2xxLCDState *s = opaque; - - s->bpp = LCCR3_BPP(s->control[3]); - s->xres = s->yres = s->pal_for = -1; - - return 0; -} - -static const VMStateDescription vmstate_pxa2xx_lcdc = { - .name = "pxa2xx_lcdc", - .version_id = 0, - .minimum_version_id = 0, - .post_load = pxa2xx_lcdc_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(irqlevel, PXA2xxLCDState), - VMSTATE_INT32(transp, PXA2xxLCDState), - VMSTATE_UINT32_ARRAY(control, PXA2xxLCDState, 6), - VMSTATE_UINT32_ARRAY(status, PXA2xxLCDState, 2), - VMSTATE_UINT32_ARRAY(ovl1c, PXA2xxLCDState, 2), - VMSTATE_UINT32_ARRAY(ovl2c, PXA2xxLCDState, 2), - VMSTATE_UINT32(ccr, PXA2xxLCDState), - VMSTATE_UINT32(cmdcr, PXA2xxLCDState), - VMSTATE_UINT32(trgbr, PXA2xxLCDState), - VMSTATE_UINT32(tcr, PXA2xxLCDState), - VMSTATE_UINT32(liidr, PXA2xxLCDState), - VMSTATE_UINT8(bscntr, PXA2xxLCDState), - VMSTATE_STRUCT_ARRAY(dma_ch, PXA2xxLCDState, 7, 0, - vmstate_dma_channel, struct DMAChannel), - VMSTATE_END_OF_LIST() - } -}; - -static const GraphicHwOps pxa2xx_ops = { - .invalidate = pxa2xx_invalidate_display, - .gfx_update = pxa2xx_update_display, -}; - -PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irq) -{ - PXA2xxLCDState *s; - - s = g_new0(PXA2xxLCDState, 1); - s->invalidated = 1; - s->irq = irq; - s->sysmem = sysmem; - - pxa2xx_lcdc_orientation(s, graphic_rotate); - - memory_region_init_io(&s->iomem, NULL, &pxa2xx_lcdc_ops, s, - "pxa2xx-lcd-controller", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - s->con = graphic_console_init(NULL, 0, &pxa2xx_ops, s); - - vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s); - - return s; -} - -void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler) -{ - s->vsync_cb = handler; -} diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index fcfd40c3ac..335d01edde 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -31,7 +31,7 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) uint8_t *src; int len, i; - if (is_buffer_shared(surface)) { + if (!surface_is_allocated(surface)) { return; } trace_qxl_render_blit(qxl->guest_primary.qxl_stride, @@ -290,7 +290,7 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, return c; fail: - cursor_put(c); + cursor_unref(c); return NULL; } @@ -307,10 +307,6 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) return 1; } - if (!dpy_cursor_define_supported(qxl->vga.con)) { - return 0; - } - if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) { fprintf(stderr, "%s", __func__); qxl_log_cmd_cursor(qxl, cmd, ext->group_id); @@ -336,7 +332,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 6772849dec..0c4b1c9bf2 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); } @@ -300,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(); } @@ -544,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); @@ -1089,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, @@ -1148,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 */ @@ -1323,8 +1301,8 @@ static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, }; uint64_t guest_start; uint64_t guest_end; - int pci_region; - pcibus_t pci_start; + int pci_region = -1; + pcibus_t pci_start = PCI_BAR_UNMAPPED; pcibus_t pci_end; MemoryRegion *mr; intptr_t virt_start; @@ -1566,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) { @@ -1613,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); @@ -2223,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) @@ -2404,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), @@ -2416,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), @@ -2435,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() }, @@ -2448,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), @@ -2468,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 } @@ -2487,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), @@ -2504,7 +2486,7 @@ static void qxl_pci_class_init(ObjectClass *klass, void *data) k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); - dc->reset = qxl_reset_handler; + device_class_set_legacy_reset(dc, qxl_reset_handler); dc->vmsd = &qxl_vmstate; device_class_set_props(dc, qxl_properties); } diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 7894bd5134..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; @@ -162,7 +159,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(PCIQXLDevice, PCI_QXL) * * 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. * 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-stubs.c b/hw/display/ramfb-stubs.c new file mode 100644 index 0000000000..cf64733b10 --- /dev/null +++ b/hw/display/ramfb-stubs.c @@ -0,0 +1,15 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/display/ramfb.h" + +const VMStateDescription ramfb_vmstate = {}; + +void ramfb_display_update(QemuConsole *con, RAMFBState *s) +{ +} + +RAMFBState *ramfb_setup(Error **errp) +{ + error_setg(errp, "ramfb support not available"); + return NULL; +} 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 664fd4046d..16f8cb487c 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), @@ -175,7 +175,7 @@ static void sii9022_class_init(ObjectClass *klass, void *data) k->event = sii9022_event; k->recv = sii9022_rx; k->send = sii9022_tx; - dc->reset = sii9022_reset; + device_class_set_legacy_reset(dc, sii9022_reset); dc->realize = sii9022_realize; dc->vmsd = &vmstate_sii9022; } diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 663c37e7f2..38d005c168 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/char/serial.h" +#include "hw/usb/hcd-ohci.h" +#include "hw/char/serial-mm.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() @@ -2009,22 +2086,26 @@ static void sm501_sysbus_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "SM501 Multimedia Companion"; device_class_set_props(dc, sm501_sysbus_properties); - dc->reset = sm501_reset_sysbus; + device_class_set_legacy_reset(dc, sm501_reset_sysbus); dc->vmsd = &vmstate_sm501_sysbus; } 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), @@ -2099,16 +2181,23 @@ static void sm501_pci_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "SM501 Display Controller"; device_class_set_props(dc, sm501_pci_properties); - dc->reset = sm501_reset_pci; + device_class_set_legacy_reset(dc, sm501_reset_pci); dc->hotpluggable = false; 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 d67b0ad7b5..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" @@ -281,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/tc6393xb.c b/hw/display/tc6393xb.c deleted file mode 100644 index c7beba453b..0000000000 --- a/hw/display/tc6393xb.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * Toshiba TC6393XB I/O Controller. - * Found in Sharp Zaurus SL-6000 (tosa) or some - * Toshiba e-Series PDAs. - * - * Most features are currently unsupported!!! - * - * This code is licensed under the GNU GPL v2. - * - * 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 "qapi/error.h" -#include "qemu/host-utils.h" -#include "hw/irq.h" -#include "hw/display/tc6393xb.h" -#include "exec/memory.h" -#include "hw/block/flash.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "sysemu/blockdev.h" - -#define IRQ_TC6393_NAND 0 -#define IRQ_TC6393_MMC 1 -#define IRQ_TC6393_OHCI 2 -#define IRQ_TC6393_SERIAL 3 -#define IRQ_TC6393_FB 4 - -#define TC6393XB_NR_IRQS 8 - -#define TC6393XB_GPIOS 16 - -#define SCR_REVID 0x08 /* b Revision ID */ -#define SCR_ISR 0x50 /* b Interrupt Status */ -#define SCR_IMR 0x52 /* b Interrupt Mask */ -#define SCR_IRR 0x54 /* b Interrupt Routing */ -#define SCR_GPER 0x60 /* w GP Enable */ -#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ -#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ -#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ -#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ -#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ -#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ -#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ -#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ -#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ -#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ -#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ -#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ -#define SCR_CCR 0x98 /* w Clock Control */ -#define SCR_PLL2CR 0x9a /* w PLL2 Control */ -#define SCR_PLL1CR 0x9c /* l PLL1 Control */ -#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ -#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ -#define SCR_FER 0xe0 /* b Function Enable */ -#define SCR_MCR 0xe4 /* w Mode Control */ -#define SCR_CONFIG 0xfc /* b Configuration Control */ -#define SCR_DEBUG 0xff /* b Debug */ - -#define NAND_CFG_COMMAND 0x04 /* w Command */ -#define NAND_CFG_BASE 0x10 /* l Control Base Address */ -#define NAND_CFG_INTP 0x3d /* b Interrupt Pin */ -#define NAND_CFG_INTE 0x48 /* b Int Enable */ -#define NAND_CFG_EC 0x4a /* b Event Control */ -#define NAND_CFG_ICC 0x4c /* b Internal Clock Control */ -#define NAND_CFG_ECCC 0x5b /* b ECC Control */ -#define NAND_CFG_NFTC 0x60 /* b NAND Flash Transaction Control */ -#define NAND_CFG_NFM 0x61 /* b NAND Flash Monitor */ -#define NAND_CFG_NFPSC 0x62 /* b NAND Flash Power Supply Control */ -#define NAND_CFG_NFDC 0x63 /* b NAND Flash Detect Control */ - -#define NAND_DATA 0x00 /* l Data */ -#define NAND_MODE 0x04 /* b Mode */ -#define NAND_STATUS 0x05 /* b Status */ -#define NAND_ISR 0x06 /* b Interrupt Status */ -#define NAND_IMR 0x07 /* b Interrupt Mask */ - -#define NAND_MODE_WP 0x80 -#define NAND_MODE_CE 0x10 -#define NAND_MODE_ALE 0x02 -#define NAND_MODE_CLE 0x01 -#define NAND_MODE_ECC_MASK 0x60 -#define NAND_MODE_ECC_EN 0x20 -#define NAND_MODE_ECC_READ 0x40 -#define NAND_MODE_ECC_RST 0x60 - -struct TC6393xbState { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq *sub_irqs; - struct { - uint8_t ISR; - uint8_t IMR; - uint8_t IRR; - uint16_t GPER; - uint8_t GPI_SR[3]; - uint8_t GPI_IMR[3]; - uint8_t GPI_EDER[3]; - uint8_t GPI_LIR[3]; - uint8_t GP_IARCR[3]; - uint8_t GP_IARLCR[3]; - uint8_t GPI_BCR[3]; - uint16_t GPA_IARCR; - uint16_t GPA_IARLCR; - uint16_t CCR; - uint16_t PLL2CR; - uint32_t PLL1CR; - uint8_t DIARCR; - uint8_t DBOCR; - uint8_t FER; - uint16_t MCR; - uint8_t CONFIG; - uint8_t DEBUG; - } scr; - uint32_t gpio_dir; - uint32_t gpio_level; - uint32_t prev_level; - qemu_irq handler[TC6393XB_GPIOS]; - qemu_irq *gpio_in; - - struct { - uint8_t mode; - uint8_t isr; - uint8_t imr; - } nand; - int nand_enable; - uint32_t nand_phys; - DeviceState *flash; - ECCState ecc; - - QemuConsole *con; - MemoryRegion vram; - uint16_t *vram_ptr; - uint32_t scr_width, scr_height; /* in pixels */ - qemu_irq l3v; - unsigned blank : 1, - blanked : 1; -}; - -static void tc6393xb_gpio_set(void *opaque, int line, int level) -{ -// TC6393xbState *s = opaque; - - if (line > TC6393XB_GPIOS) { - printf("%s: No GPIO pin %i\n", __func__, line); - return; - } - - // FIXME: how does the chip reflect the GPIO input level change? -} - -static void tc6393xb_gpio_handler_update(TC6393xbState *s) -{ - uint32_t level, diff; - int bit; - - level = s->gpio_level & s->gpio_dir; - level &= MAKE_64BIT_MASK(0, TC6393XB_GPIOS); - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ctz32(diff); - qemu_set_irq(s->handler[bit], (level >> bit) & 1); - } - - s->prev_level = level; -} - -qemu_irq tc6393xb_l3v_get(TC6393xbState *s) -{ - return s->l3v; -} - -static void tc6393xb_l3v(void *opaque, int line, int level) -{ - TC6393xbState *s = opaque; - s->blank = !level; - fprintf(stderr, "L3V: %d\n", level); -} - -static void tc6393xb_sub_irq(void *opaque, int line, int level) { - TC6393xbState *s = opaque; - uint8_t isr = s->scr.ISR; - if (level) - isr |= 1 << line; - else - isr &= ~(1 << line); - s->scr.ISR = isr; - qemu_set_irq(s->irq, isr & s->scr.IMR); -} - -#define SCR_REG_B(N) \ - case SCR_ ##N: return s->scr.N -#define SCR_REG_W(N) \ - case SCR_ ##N: return s->scr.N; \ - case SCR_ ##N + 1: return s->scr.N >> 8; -#define SCR_REG_L(N) \ - case SCR_ ##N: return s->scr.N; \ - case SCR_ ##N + 1: return s->scr.N >> 8; \ - case SCR_ ##N + 2: return s->scr.N >> 16; \ - case SCR_ ##N + 3: return s->scr.N >> 24; -#define SCR_REG_A(N) \ - case SCR_ ##N(0): return s->scr.N[0]; \ - case SCR_ ##N(1): return s->scr.N[1]; \ - case SCR_ ##N(2): return s->scr.N[2] - -static uint32_t tc6393xb_scr_readb(TC6393xbState *s, hwaddr addr) -{ - switch (addr) { - case SCR_REVID: - return 3; - case SCR_REVID+1: - return 0; - SCR_REG_B(ISR); - SCR_REG_B(IMR); - SCR_REG_B(IRR); - SCR_REG_W(GPER); - SCR_REG_A(GPI_SR); - SCR_REG_A(GPI_IMR); - SCR_REG_A(GPI_EDER); - SCR_REG_A(GPI_LIR); - case SCR_GPO_DSR(0): - case SCR_GPO_DSR(1): - case SCR_GPO_DSR(2): - return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff; - case SCR_GPO_DOECR(0): - case SCR_GPO_DOECR(1): - case SCR_GPO_DOECR(2): - return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff; - SCR_REG_A(GP_IARCR); - SCR_REG_A(GP_IARLCR); - SCR_REG_A(GPI_BCR); - SCR_REG_W(GPA_IARCR); - SCR_REG_W(GPA_IARLCR); - SCR_REG_W(CCR); - SCR_REG_W(PLL2CR); - SCR_REG_L(PLL1CR); - SCR_REG_B(DIARCR); - SCR_REG_B(DBOCR); - SCR_REG_B(FER); - SCR_REG_W(MCR); - SCR_REG_B(CONFIG); - SCR_REG_B(DEBUG); - } - fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -#undef SCR_REG_B -#undef SCR_REG_W -#undef SCR_REG_L -#undef SCR_REG_A - -#define SCR_REG_B(N) \ - case SCR_ ##N: s->scr.N = value; return; -#define SCR_REG_W(N) \ - case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ - case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return -#define SCR_REG_L(N) \ - case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ - case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return; \ - case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return; \ - case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return; -#define SCR_REG_A(N) \ - case SCR_ ##N(0): s->scr.N[0] = value; return; \ - case SCR_ ##N(1): s->scr.N[1] = value; return; \ - case SCR_ ##N(2): s->scr.N[2] = value; return - -static void tc6393xb_scr_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) -{ - switch (addr) { - SCR_REG_B(ISR); - SCR_REG_B(IMR); - SCR_REG_B(IRR); - SCR_REG_W(GPER); - SCR_REG_A(GPI_SR); - SCR_REG_A(GPI_IMR); - SCR_REG_A(GPI_EDER); - SCR_REG_A(GPI_LIR); - case SCR_GPO_DSR(0): - case SCR_GPO_DSR(1): - case SCR_GPO_DSR(2): - s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8)); - tc6393xb_gpio_handler_update(s); - return; - case SCR_GPO_DOECR(0): - case SCR_GPO_DOECR(1): - case SCR_GPO_DOECR(2): - s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8)); - tc6393xb_gpio_handler_update(s); - return; - SCR_REG_A(GP_IARCR); - SCR_REG_A(GP_IARLCR); - SCR_REG_A(GPI_BCR); - SCR_REG_W(GPA_IARCR); - SCR_REG_W(GPA_IARLCR); - SCR_REG_W(CCR); - SCR_REG_W(PLL2CR); - SCR_REG_L(PLL1CR); - SCR_REG_B(DIARCR); - SCR_REG_B(DBOCR); - SCR_REG_B(FER); - SCR_REG_W(MCR); - SCR_REG_B(CONFIG); - SCR_REG_B(DEBUG); - } - fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} -#undef SCR_REG_B -#undef SCR_REG_W -#undef SCR_REG_L -#undef SCR_REG_A - -static void tc6393xb_nand_irq(TC6393xbState *s) { - qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND], - (s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr)); -} - -static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, hwaddr addr) { - switch (addr) { - case NAND_CFG_COMMAND: - return s->nand_enable ? 2 : 0; - case NAND_CFG_BASE: - case NAND_CFG_BASE + 1: - case NAND_CFG_BASE + 2: - case NAND_CFG_BASE + 3: - return s->nand_phys >> (addr - NAND_CFG_BASE); - } - fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { - switch (addr) { - case NAND_CFG_COMMAND: - s->nand_enable = (value & 0x2); - return; - case NAND_CFG_BASE: - case NAND_CFG_BASE + 1: - case NAND_CFG_BASE + 2: - case NAND_CFG_BASE + 3: - s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8)); - s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8); - return; - } - fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -static uint32_t tc6393xb_nand_readb(TC6393xbState *s, hwaddr addr) { - switch (addr) { - case NAND_DATA + 0: - case NAND_DATA + 1: - case NAND_DATA + 2: - case NAND_DATA + 3: - return nand_getio(s->flash); - case NAND_MODE: - return s->nand.mode; - case NAND_STATUS: - return 0x14; - case NAND_ISR: - return s->nand.isr; - case NAND_IMR: - return s->nand.imr; - } - fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { -// fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n", -// (uint32_t) addr, value & 0xff); - switch (addr) { - case NAND_DATA + 0: - case NAND_DATA + 1: - case NAND_DATA + 2: - case NAND_DATA + 3: - nand_setio(s->flash, value); - s->nand.isr |= 1; - tc6393xb_nand_irq(s); - return; - case NAND_MODE: - s->nand.mode = value; - nand_setpins(s->flash, - value & NAND_MODE_CLE, - value & NAND_MODE_ALE, - !(value & NAND_MODE_CE), - value & NAND_MODE_WP, - 0); // FIXME: gnd - switch (value & NAND_MODE_ECC_MASK) { - case NAND_MODE_ECC_RST: - ecc_reset(&s->ecc); - break; - case NAND_MODE_ECC_READ: - // FIXME - break; - case NAND_MODE_ECC_EN: - ecc_reset(&s->ecc); - } - return; - case NAND_ISR: - s->nand.isr = value; - tc6393xb_nand_irq(s); - return; - case NAND_IMR: - s->nand.imr = value; - tc6393xb_nand_irq(s); - return; - } - fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i; - uint16_t *data_buffer; - uint8_t *data_display; - - data_buffer = s->vram_ptr; - data_display = surface_data(surface); - for (i = 0; i < s->scr_height; i++) { - int j; - for (j = 0; j < s->scr_width; j++, data_display += 4, data_buffer++) { - uint16_t color = *data_buffer; - uint32_t dest_color = rgb_to_pixel32( - ((color & 0xf800) * 0x108) >> 11, - ((color & 0x7e0) * 0x41) >> 9, - ((color & 0x1f) * 0x21) >> 2 - ); - *(uint32_t *)data_display = dest_color; - } - } - dpy_gfx_update_full(s->con); -} - -static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w; - uint8_t *d; - - if (!full_update) - return; - - w = s->scr_width * surface_bytes_per_pixel(surface); - d = surface_data(surface); - for(i = 0; i < s->scr_height; i++) { - memset(d, 0, w); - d += surface_stride(surface); - } - - dpy_gfx_update_full(s->con); -} - -static void tc6393xb_update_display(void *opaque) -{ - TC6393xbState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int full_update; - - if (s->scr_width == 0 || s->scr_height == 0) - return; - - full_update = 0; - if (s->blanked != s->blank) { - s->blanked = s->blank; - full_update = 1; - } - if (s->scr_width != surface_width(surface) || - s->scr_height != surface_height(surface)) { - qemu_console_resize(s->con, s->scr_width, s->scr_height); - full_update = 1; - } - if (s->blanked) - tc6393xb_draw_blank(s, full_update); - else - tc6393xb_draw_graphic(s, full_update); -} - - -static uint64_t tc6393xb_readb(void *opaque, hwaddr addr, - unsigned size) -{ - TC6393xbState *s = opaque; - - switch (addr >> 8) { - case 0: - return tc6393xb_scr_readb(s, addr & 0xff); - case 1: - return tc6393xb_nand_cfg_readb(s, addr & 0xff); - }; - - if ((addr &~0xff) == s->nand_phys && s->nand_enable) { -// return tc6393xb_nand_readb(s, addr & 0xff); - uint8_t d = tc6393xb_nand_readb(s, addr & 0xff); -// fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d); - return d; - } - -// fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} - -static void tc6393xb_writeb(void *opaque, hwaddr addr, - uint64_t value, unsigned size) { - TC6393xbState *s = opaque; - - switch (addr >> 8) { - case 0: - tc6393xb_scr_writeb(s, addr & 0xff, value); - return; - case 1: - tc6393xb_nand_cfg_writeb(s, addr & 0xff, value); - return; - }; - - if ((addr &~0xff) == s->nand_phys && s->nand_enable) - tc6393xb_nand_writeb(s, addr & 0xff, value); - else - fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n", - (uint32_t) addr, (int)value & 0xff); -} - -static const GraphicHwOps tc6393xb_gfx_ops = { - .gfx_update = tc6393xb_update_display, -}; - -TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) -{ - TC6393xbState *s; - DriveInfo *nand; - static const MemoryRegionOps tc6393xb_ops = { - .read = tc6393xb_readb, - .write = tc6393xb_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - }; - - s = g_new0(TC6393xbState, 1); - s->irq = irq; - s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS); - - s->l3v = qemu_allocate_irq(tc6393xb_l3v, s, 0); - s->blanked = 1; - - s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); - - nand = drive_get(IF_MTD, 0, 0); - s->flash = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, - NAND_MFR_TOSHIBA, 0x76); - - memory_region_init_io(&s->iomem, NULL, &tc6393xb_ops, s, "tc6393xb", 0x10000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - memory_region_init_ram(&s->vram, NULL, "tc6393xb.vram", 0x100000, - &error_fatal); - s->vram_ptr = memory_region_get_ram_ptr(&s->vram); - memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); - s->scr_width = 480; - s->scr_height = 640; - s->con = graphic_console_init(NULL, 0, &tc6393xb_gfx_ops, s); - - return s; -} diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 1b27b64f6d..f000288fcd 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), @@ -892,7 +892,7 @@ static void tcx_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = tcx_realizefn; - dc->reset = tcx_reset; + device_class_set_legacy_reset(dc, tcx_reset); dc->vmsd = &vmstate_tcx; device_class_set_props(dc, tcx_properties); } diff --git a/hw/display/trace-events b/hw/display/trace-events index 0c0ffcbe42..d26d663f96 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -53,9 +53,11 @@ virtio_gpu_cmd_ctx_submit(uint32_t ctx, uint32_t size) "ctx 0x%x, size %d" virtio_gpu_update_cursor(uint32_t scanout, uint32_t x, uint32_t y, const char *type, uint32_t res) "scanout %d, x %d, y %d, %s, res 0x%x" virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x" virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64 +virtio_gpu_inc_inflight_fences(uint32_t inflight) "in-flight+ %u" +virtio_gpu_dec_inflight_fences(uint32_t inflight) "in-flight- %u" +virtio_gpu_cmd_suspended(uint32_t cmd) "cmd 0x%x" # 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" @@ -178,3 +180,17 @@ macfb_ctrl_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%"PRI macfb_sense_read(uint32_t value) "video sense: 0x%"PRIx32 macfb_sense_write(uint32_t value) "video sense: 0x%"PRIx32 macfb_update_mode(uint32_t width, uint32_t height, uint8_t depth) "setting mode to width %"PRId32 " height %"PRId32 " size %d" + +# dm163.c +dm163_redraw(uint8_t redraw) "0x%02x" +dm163_dck(unsigned new_state) "dck : %u" +dm163_en_b(unsigned new_state) "en_b : %u" +dm163_rst_b(unsigned new_state) "rst_b : %u" +dm163_lat_b(unsigned new_state) "lat_b : %u" +dm163_sin(unsigned new_state) "sin : %u" +dm163_selbk(unsigned new_state) "selbk : %u" +dm163_activated_rows(int new_state) "Activated rows : 0x%" PRIx32 "" +dm163_bits_ppi(unsigned dest_width) "dest_width : %u" +dm163_leds(int led, uint32_t value) "led %d: 0x%x" +dm163_channels(int channel, uint8_t value) "channel %d: 0x%x" +dm163_refresh_rate(uint32_t rr) "refresh rate %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 2a5437d803..c025632635 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 * @@ -98,7 +98,7 @@ static void vga_isa_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = vga_isa_realizefn; - dc->reset = vga_isa_reset; + device_class_set_legacy_reset(dc, vga_isa_reset); dc->vmsd = &vmstate_vga_common; device_class_set_props(dc, vga_isa_properties); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c index cd2c46776d..be33204517 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -122,7 +122,7 @@ static void vga_mmio_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = vga_mmio_realizefn; - dc->reset = vga_mmio_reset; + device_class_set_legacy_reset(dc, vga_mmio_reset); dc->vmsd = &vmstate_vga_common; device_class_set_props(dc, vga_mmio_properties); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index df23dbf3a0..6b51019966 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,7 +25,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 "vga_int.h" @@ -61,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() @@ -403,7 +403,7 @@ static void secondary_class_init(ObjectClass *klass, void *data) k->exit = pci_secondary_vga_exit; k->class_id = PCI_CLASS_DISPLAY_OTHER; device_class_set_props(dc, secondary_pci_properties); - dc->reset = pci_secondary_vga_reset; + device_class_set_legacy_reset(dc, pci_secondary_vga_reset); } static const TypeInfo vga_info = { diff --git a/hw/display/vga.c b/hw/display/vga.c index 0cb26a791b..b074b58c90 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -26,7 +26,9 @@ #include "qemu/units.h" #include "sysemu/reset.h" #include "qapi/error.h" +#include "exec/tswap.h" #include "hw/display/vga.h" +#include "hw/i386/x86.h" #include "hw/pci/pci.h" #include "vga_int.h" #include "vga_regs.h" @@ -45,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) * @@ -88,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]; @@ -834,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; @@ -901,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" @@ -1071,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; @@ -1217,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) { @@ -1256,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) { @@ -1270,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; @@ -1280,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; @@ -1360,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; @@ -1473,10 +1483,11 @@ 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; - bool share_surface, force_shadow = false; + bool allocate_surface, force_shadow = false; pixman_format_code_t format; #if HOST_BIG_ENDIAN bool byteswap = !s->big_endian_fb; @@ -1490,31 +1501,6 @@ 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); @@ -1534,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); @@ -1640,29 +1571,106 @@ 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; + bwidth = DIV_ROUND_UP(width * bits, 8); /* scanline length */ + if (hpel) { + bwidth += 4; + } + + region_start = (s->params.start_addr * 4); + region_end = region_start + (ram_addr_t)s->params.line_offset * (height - 1) + bwidth; + if (region_end > s->vbe_size) { + /* + * On wrap around 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; + } + if (s->params.line_compare < height) { + /* split screen mode */ + region_start = 0; + } + + /* + * 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) { + allocate_surface = !dpy_gfx_check_format(s->con, format) + || s->force_shadow || force_shadow; + } else { + allocate_surface = true; + } + + 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 || + allocate_surface != surface_is_allocated(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) + && !surface_is_allocated(surface)) { + /* base address changed (page flip) -> shared display surfaces + * must be updated with the new base address */ + full_update = 1; + } + + if (full_update) { + if (!allocate_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) { + if (surface_is_allocated(surface) && s->cursor_invalidate) { s->cursor_invalidate(s); } #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); - bwidth = DIV_ROUND_UP(width * bits, 8); + addr1 = (s->params.start_addr * 4); y_start = -1; d = surface_data(surface); linesize = surface_stride(surface); y1 = 0; if (!full_update) { - if (s->line_compare < height) { - /* split screen mode */ - region_start = 0; - } snap = memory_region_snapshot_and_clear_dirty(&s->vram, region_start, region_end - region_start, DIRTY_MEMORY_VGA); @@ -1699,8 +1707,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) if (update) { if (y_start < 0) y_start = y; - if (!(is_buffer_shared(surface))) { - vga_draw_line(s, d, addr, width); + if (surface_is_allocated(surface)) { + 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); } @@ -1715,15 +1727,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) { @@ -1746,6 +1762,13 @@ static void vga_draw_blank(VGACommonState *s, int full_update) if (s->last_scr_width <= 0 || s->last_scr_height <= 0) return; + if (!surface_is_allocated(surface)) { + /* unshare buffer, otherwise the blanking corrupts vga vram */ + surface = qemu_create_displaysurface(s->last_scr_width, + s->last_scr_height); + dpy_gfx_replace_surface(s->con, surface); + } + w = s->last_scr_width * surface_bytes_per_pixel(surface); d = surface_data(surface); for(i = 0; i < s->last_scr_height; i++) { @@ -1839,9 +1862,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; @@ -1852,7 +1873,6 @@ void vga_common_reset(VGACommonState *s) s->cursor_start = 0; s->cursor_end = 0; s->cursor_offset = 0; - s->big_endian_fb = s->default_endian_fb; memset(s->invalidated_y_table, '\0', sizeof(s->invalidated_y_table)); memset(s->last_palette, '\0', sizeof(s->last_palette)); memset(s->last_ch_attr, '\0', sizeof(s->last_ch_attr)); @@ -1963,7 +1983,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) { @@ -1979,7 +1999,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) { @@ -2104,7 +2124,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() } @@ -2115,7 +2135,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), @@ -2147,7 +2167,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 } @@ -2224,7 +2244,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) { @@ -2244,11 +2264,9 @@ 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(); + s->big_endian_fb = s->default_endian_fb; + vga_dirty_log_start(s); return true; @@ -2263,11 +2281,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(), }; @@ -2278,9 +2300,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 7cf0d11201..f77c1c1145 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -25,6 +25,7 @@ #ifndef HW_VGA_INT_H #define HW_VGA_INT_H +#include "ui/console.h" #include "exec/ioport.h" #include "exec/memory.h" @@ -56,6 +57,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 +99,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 +114,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; diff --git a/hw/display/vga_regs.h b/hw/display/vga_regs.h index 7fdba34b9b..40e673f164 100644 --- a/hw/display/vga_regs.h +++ b/hw/display/vga_regs.h @@ -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 19c0e20103..14548f1a57 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/sockets.h" #include "hw/qdev-properties.h" #include "hw/virtio/virtio-gpu.h" @@ -30,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 { @@ -77,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; @@ -87,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; @@ -98,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 @@ -160,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); @@ -183,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; @@ -203,9 +245,11 @@ 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); + uint64_t modifier = 0; QemuDmaBuf *dmabuf; if (m->scanout_id >= g->parent_obj.conf.max_outputs) { @@ -218,25 +262,34 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) g->parent_obj.enable = 1; con = g->parent_obj.scanout[m->scanout_id].con; - dmabuf = &g->dmabuf[m->scanout_id]; - if (dmabuf->fd >= 0) { - close(dmabuf->fd); - dmabuf->fd = -1; + dmabuf = g->dmabuf[m->scanout_id]; + + if (dmabuf) { + qemu_dmabuf_close(dmabuf); + dpy_gl_release_dmabuf(con, dmabuf); + g_clear_pointer(&dmabuf, qemu_dmabuf_free); } - dpy_gl_release_dmabuf(con, dmabuf); + if (fd == -1) { dpy_gl_scanout_disable(con); + g->dmabuf[m->scanout_id] = NULL; break; } - *dmabuf = (QemuDmaBuf) { - .fd = fd, - .width = m->fd_width, - .height = m->fd_height, - .stride = m->fd_stride, - .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; + modifier = m2->modifier; + } + + dmabuf = qemu_dmabuf_new(m->width, m->height, + m->fd_stride, 0, 0, + m->fd_width, m->fd_height, + m->fd_drm_fourcc, modifier, + fd, false, m->fd_flags & + VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP); + dpy_gl_scanout_dmabuf(con, dmabuf); + g->dmabuf[m->scanout_id] = dmabuf; break; } case VHOST_USER_GPU_DMABUF_UPDATE: { @@ -259,6 +312,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; @@ -286,6 +340,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) } break; } +#endif default: g_warning("unhandled message %d %d", msg->request, msg->size); } @@ -335,7 +390,7 @@ vhost_user_gpu_chr_read(void *opaque) } msg->request = request; - msg->flags = size; + msg->flags = flags; msg->size = size; if (request == VHOST_USER_GPU_CURSOR_UPDATE || @@ -363,11 +418,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 @@ -451,7 +506,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; @@ -486,6 +541,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); } @@ -494,6 +558,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); } @@ -569,7 +642,7 @@ vhost_user_gpu_device_realize(DeviceState *qdev, Error **errp) static struct vhost_dev *vhost_user_gpu_get_vhost(VirtIODevice *vdev) { VhostUserGPU *g = VHOST_USER_GPU(vdev); - return &g->vhost->dev; + return g->vhost ? &g->vhost->dev : NULL; } static Property vhost_user_gpu_properties[] = { 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 a29f191aa8..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) { } @@ -167,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; } } @@ -206,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)) { @@ -215,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; } @@ -227,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-gl.c b/hw/display/virtio-gpu-gl.c index e06be60dfb..7c0e448b46 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -29,9 +29,14 @@ static void virtio_gpu_gl_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id) { + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g); uint32_t width, height; uint32_t pixels, *data; + if (gl->renderer_state != RS_INITED) { + return; + } + data = virgl_renderer_get_cursor_data(resource_id, &width, &height); if (!data) { return; @@ -65,13 +70,22 @@ static void virtio_gpu_gl_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) return; } - if (!gl->renderer_inited) { - virtio_gpu_virgl_init(g); - gl->renderer_inited = true; - } - if (gl->renderer_reset) { - gl->renderer_reset = false; + switch (gl->renderer_state) { + case RS_RESET: virtio_gpu_virgl_reset(g); + /* fallthrough */ + case RS_START: + if (virtio_gpu_virgl_init(g)) { + gl->renderer_state = RS_INIT_FAILED; + return; + } + + gl->renderer_state = RS_INITED; + break; + case RS_INIT_FAILED: + return; + case RS_INITED: + break; } cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); @@ -98,14 +112,15 @@ static void virtio_gpu_gl_reset(VirtIODevice *vdev) * GL functions must be called with the associated GL context in main * thread, and when the renderer is unblocked. */ - if (gl->renderer_inited && !gl->renderer_reset) { + if (gl->renderer_state == RS_INITED) { virtio_gpu_virgl_reset_scanout(g); - gl->renderer_reset = true; + gl->renderer_state = RS_RESET; } } static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) { + ERRP_GUARD(); VirtIOGPU *g = VIRTIO_GPU(qdev); #if HOST_BIG_ENDIAN @@ -119,13 +134,22 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) } if (!display_opengl) { - error_setg(errp, "opengl is not available"); + error_setg(errp, + "The display backend does not have OpenGL support enabled"); + error_append_hint(errp, + "It can be enabled with '-display BACKEND,gl=on' " + "where BACKEND is the name of the display backend " + "to use.\n"); return; } g->parent_obj.conf.flags |= (1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED); - VIRTIO_GPU_BASE(g)->virtio_config.num_capsets = - virtio_gpu_virgl_get_num_capsets(g); + g->capset_ids = virtio_gpu_virgl_get_capsets(g); + VIRTIO_GPU_BASE(g)->virtio_config.num_capsets = g->capset_ids->len; + +#if VIRGL_VERSION_MAJOR >= 1 + g->parent_obj.conf.flags |= 1 << VIRTIO_GPU_FLAG_CONTEXT_INIT_ENABLED; +#endif virtio_gpu_device_realize(qdev, errp); } @@ -133,9 +157,32 @@ static void virtio_gpu_gl_device_realize(DeviceState *qdev, Error **errp) static Property virtio_gpu_gl_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, parent_obj.conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), + DEFINE_PROP_BIT("venus", VirtIOGPU, parent_obj.conf.flags, + VIRTIO_GPU_FLAG_VENUS_ENABLED, false), DEFINE_PROP_END_OF_LIST(), }; +static void virtio_gpu_gl_device_unrealize(DeviceState *qdev) +{ + VirtIOGPU *g = VIRTIO_GPU(qdev); + VirtIOGPUGL *gl = VIRTIO_GPU_GL(qdev); + + if (gl->renderer_state >= RS_INITED) { +#if VIRGL_VERSION_MAJOR >= 1 + qemu_bh_delete(gl->cmdq_resume_bh); +#endif + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { + timer_free(gl->print_stats); + } + timer_free(gl->fence_poll); + virgl_renderer_cleanup(NULL); + } + + gl->renderer_state = RS_START; + + g_array_unref(g->capset_ids); +} + static void virtio_gpu_gl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -149,6 +196,7 @@ static void virtio_gpu_gl_class_init(ObjectClass *klass, void *data) vgc->update_cursor_data = virtio_gpu_gl_update_cursor_data; vdc->realize = virtio_gpu_gl_device_realize; + vdc->unrealize = virtio_gpu_gl_device_unrealize; vdc->reset = virtio_gpu_gl_reset; device_class_set_props(dc, virtio_gpu_gl_properties); } @@ -170,3 +218,4 @@ static void virtio_register_types(void) type_init(virtio_register_types) module_dep("hw-display-virtio-gpu"); +module_dep("ui-opengl"); 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..c02ec6d37d 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); @@ -161,7 +162,8 @@ static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) struct virtio_gpu_scanout *scanout; scanout = &g->parent_obj.scanout[dmabuf->scanout_id]; - dpy_gl_release_dmabuf(scanout->con, &dmabuf->buf); + dpy_gl_release_dmabuf(scanout->con, dmabuf->buf); + g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next); g_free(dmabuf); } @@ -180,17 +182,10 @@ static VGPUDMABuf } dmabuf = g_new0(VGPUDMABuf, 1); - dmabuf->buf.width = fb->width; - dmabuf->buf.height = fb->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.fourcc = qemu_pixman_to_drm_format(fb->format); - dmabuf->buf.fd = res->dmabuf_fd; - dmabuf->buf.allow_fences = true; - dmabuf->buf.draw_submitted = false; + dmabuf->buf = qemu_dmabuf_new(r->width, r->height, fb->stride, + r->x, r->y, fb->width, fb->height, + qemu_pixman_to_drm_format(fb->format), + 0, res->dmabuf_fd, true, false); dmabuf->scanout_id = scanout_id; QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); @@ -205,6 +200,7 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, { struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id]; VGPUDMABuf *new_primary, *old_primary = NULL; + uint32_t width, height; new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r); if (!new_primary) { @@ -215,11 +211,11 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, old_primary = g->dmabuf.primary[scanout_id]; } + width = qemu_dmabuf_get_width(new_primary->buf); + height = qemu_dmabuf_get_height(new_primary->buf); g->dmabuf.primary[scanout_id] = new_primary; - qemu_console_resize(scanout->con, - new_primary->buf.scanout_width, - new_primary->buf.scanout_height); - dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); + qemu_console_resize(scanout->con, width, height); + dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf); if (old_primary) { virtio_gpu_free_dmabuf(g, old_primary); diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 73cb92c8d5..145a0b3879 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -12,25 +12,217 @@ */ #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 "hw/virtio/virtio-gpu-bswap.h" +#include "hw/virtio/virtio-gpu-pixman.h" + +#include "ui/egl-helpers.h" #include -static struct virgl_renderer_callbacks virtio_gpu_3d_cbs; +struct virtio_gpu_virgl_resource { + struct virtio_gpu_simple_resource base; + MemoryRegion *mr; +}; + +static struct virtio_gpu_virgl_resource * +virtio_gpu_virgl_find_resource(VirtIOGPU *g, uint32_t resource_id) +{ + struct virtio_gpu_simple_resource *res; + + res = virtio_gpu_find_resource(g, resource_id); + if (!res) { + return NULL; + } + + return container_of(res, struct virtio_gpu_virgl_resource, base); +} + +#if VIRGL_RENDERER_CALLBACKS_VERSION >= 4 +static void * +virgl_get_egl_display(G_GNUC_UNUSED void *cookie) +{ + return qemu_egl_display; +} +#endif + +#if VIRGL_VERSION_MAJOR >= 1 +struct virtio_gpu_virgl_hostmem_region { + MemoryRegion mr; + struct VirtIOGPU *g; + bool finish_unmapping; +}; + +static struct virtio_gpu_virgl_hostmem_region * +to_hostmem_region(MemoryRegion *mr) +{ + return container_of(mr, struct virtio_gpu_virgl_hostmem_region, mr); +} + +static void virtio_gpu_virgl_resume_cmdq_bh(void *opaque) +{ + VirtIOGPU *g = opaque; + + virtio_gpu_process_cmdq(g); +} + +static void virtio_gpu_virgl_hostmem_region_free(void *obj) +{ + MemoryRegion *mr = MEMORY_REGION(obj); + struct virtio_gpu_virgl_hostmem_region *vmr; + VirtIOGPUBase *b; + VirtIOGPUGL *gl; + + vmr = to_hostmem_region(mr); + vmr->finish_unmapping = true; + + b = VIRTIO_GPU_BASE(vmr->g); + b->renderer_blocked--; + + /* + * memory_region_unref() is executed from RCU thread context, while + * virglrenderer works only on the main-loop thread that's holding GL + * context. + */ + gl = VIRTIO_GPU_GL(vmr->g); + qemu_bh_schedule(gl->cmdq_resume_bh); +} + +static int +virtio_gpu_virgl_map_resource_blob(VirtIOGPU *g, + struct virtio_gpu_virgl_resource *res, + uint64_t offset) +{ + struct virtio_gpu_virgl_hostmem_region *vmr; + VirtIOGPUBase *b = VIRTIO_GPU_BASE(g); + MemoryRegion *mr; + uint64_t size; + void *data; + int ret; + + if (!virtio_gpu_hostmem_enabled(b->conf)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: hostmem disabled\n", __func__); + return -EOPNOTSUPP; + } + + ret = virgl_renderer_resource_map(res->base.resource_id, &data, &size); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map virgl resource: %s\n", + __func__, strerror(-ret)); + return ret; + } + + vmr = g_new0(struct virtio_gpu_virgl_hostmem_region, 1); + vmr->g = g; + + mr = &vmr->mr; + memory_region_init_ram_ptr(mr, OBJECT(mr), "blob", size, data); + memory_region_add_subregion(&b->hostmem, offset, mr); + memory_region_set_enabled(mr, true); + + /* + * MR could outlive the resource if MR's reference is held outside of + * virtio-gpu. In order to prevent unmapping resource while MR is alive, + * and thus, making the data pointer invalid, we will block virtio-gpu + * command processing until MR is fully unreferenced and freed. + */ + OBJECT(mr)->free = virtio_gpu_virgl_hostmem_region_free; + + res->mr = mr; + + return 0; +} + +static int +virtio_gpu_virgl_unmap_resource_blob(VirtIOGPU *g, + struct virtio_gpu_virgl_resource *res, + bool *cmd_suspended) +{ + struct virtio_gpu_virgl_hostmem_region *vmr; + VirtIOGPUBase *b = VIRTIO_GPU_BASE(g); + MemoryRegion *mr = res->mr; + int ret; + + if (!mr) { + return 0; + } + + vmr = to_hostmem_region(res->mr); + + /* + * Perform async unmapping in 3 steps: + * + * 1. Begin async unmapping with memory_region_del_subregion() + * and suspend/block cmd processing. + * 2. Wait for res->mr to be freed and cmd processing resumed + * asynchronously by virtio_gpu_virgl_hostmem_region_free(). + * 3. Finish the unmapping with final virgl_renderer_resource_unmap(). + */ + if (vmr->finish_unmapping) { + res->mr = NULL; + g_free(vmr); + + ret = virgl_renderer_resource_unmap(res->base.resource_id); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: failed to unmap virgl resource: %s\n", + __func__, strerror(-ret)); + return ret; + } + } else { + *cmd_suspended = true; + + /* render will be unblocked once MR is freed */ + b->renderer_blocked++; + + /* memory region owns self res->mr object and frees it by itself */ + memory_region_set_enabled(mr, false); + memory_region_del_subregion(&b->hostmem, mr); + object_unparent(OBJECT(mr)); + } + + return 0; +} +#endif static void virgl_cmd_create_resource_2d(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { struct virtio_gpu_resource_create_2d c2d; struct virgl_renderer_resource_create_args args; + struct virtio_gpu_virgl_resource *res; VIRTIO_GPU_FILL_CMD(c2d); trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format, c2d.width, c2d.height); + if (c2d.resource_id == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res = virtio_gpu_virgl_find_resource(g, c2d.resource_id); + if (res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n", + __func__, c2d.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res = g_new0(struct virtio_gpu_virgl_resource, 1); + res->base.width = c2d.width; + res->base.height = c2d.height; + res->base.format = c2d.format; + res->base.resource_id = c2d.resource_id; + res->base.dmabuf_fd = -1; + QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next); + args.handle = c2d.resource_id; args.target = 2; args.format = c2d.format; @@ -50,11 +242,35 @@ static void virgl_cmd_create_resource_3d(VirtIOGPU *g, { struct virtio_gpu_resource_create_3d c3d; struct virgl_renderer_resource_create_args args; + struct virtio_gpu_virgl_resource *res; VIRTIO_GPU_FILL_CMD(c3d); trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format, c3d.width, c3d.height, c3d.depth); + if (c3d.resource_id == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res = virtio_gpu_virgl_find_resource(g, c3d.resource_id); + if (res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n", + __func__, c3d.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res = g_new0(struct virtio_gpu_virgl_resource, 1); + res->base.width = c3d.width; + res->base.height = c3d.height; + res->base.format = c3d.format; + res->base.resource_id = c3d.resource_id; + res->base.dmabuf_fd = -1; + QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next); + args.handle = c3d.resource_id; args.target = c3d.target; args.format = c3d.format; @@ -70,15 +286,35 @@ static void virgl_cmd_create_resource_3d(VirtIOGPU *g, } static void virgl_cmd_resource_unref(VirtIOGPU *g, - struct virtio_gpu_ctrl_command *cmd) + struct virtio_gpu_ctrl_command *cmd, + bool *cmd_suspended) { struct virtio_gpu_resource_unref unref; + struct virtio_gpu_virgl_resource *res; struct iovec *res_iovs = NULL; int num_iovs = 0; VIRTIO_GPU_FILL_CMD(unref); trace_virtio_gpu_cmd_res_unref(unref.resource_id); + res = virtio_gpu_virgl_find_resource(g, unref.resource_id); + if (!res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n", + __func__, unref.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + +#if VIRGL_VERSION_MAJOR >= 1 + if (virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended)) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + if (*cmd_suspended) { + return; + } +#endif + virgl_renderer_resource_detach_iov(unref.resource_id, &res_iovs, &num_iovs); @@ -86,6 +322,10 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g, virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs); } virgl_renderer_resource_unref(unref.resource_id); + + QTAILQ_REMOVE(&g->reslist, &res->base, next); + + g_free(res); } static void virgl_cmd_context_create(VirtIOGPU *g, @@ -97,8 +337,24 @@ static void virgl_cmd_context_create(VirtIOGPU *g, trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id, cc.debug_name); - virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, - cc.debug_name); + if (cc.context_init) { + if (!virtio_gpu_context_init_enabled(g->parent_obj.conf)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: context_init disabled", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + +#if VIRGL_VERSION_MAJOR >= 1 + virgl_renderer_context_create_with_flags(cc.hdr.ctx_id, + cc.context_init, + cc.nlen, + cc.debug_name); + return; +#endif + } + + virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen, cc.debug_name); } static void virgl_cmd_context_destroy(VirtIOGPU *g, @@ -144,7 +400,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 +414,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; + +#if VIRGL_VERSION_MAJOR >= 1 + 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 +442,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); @@ -356,19 +622,13 @@ static void virgl_cmd_get_capset_info(VirtIOGPU *g, VIRTIO_GPU_FILL_CMD(info); memset(&resp, 0, sizeof(resp)); - if (info.capset_index == 0) { - resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL; + + if (info.capset_index < g->capset_ids->len) { + resp.capset_id = g_array_index(g->capset_ids, uint32_t, + info.capset_index); virgl_renderer_get_cap_set(resp.capset_id, &resp.capset_max_version, &resp.capset_max_size); - } else if (info.capset_index == 1) { - resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL2; - virgl_renderer_get_cap_set(resp.capset_id, - &resp.capset_max_version, - &resp.capset_max_size); - } else { - resp.capset_max_version = 0; - resp.capset_max_size = 0; } resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO; virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); @@ -398,9 +658,221 @@ static void virgl_cmd_get_capset(VirtIOGPU *g, g_free(resp); } +#if VIRGL_VERSION_MAJOR >= 1 +static void virgl_cmd_resource_create_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virgl_renderer_resource_create_blob_args virgl_args = { 0 }; + g_autofree struct virtio_gpu_virgl_resource *res = NULL; + struct virtio_gpu_resource_create_blob cblob; + struct virgl_renderer_resource_info info; + int ret; + + if (!virtio_gpu_blob_enabled(g->parent_obj.conf)) { + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + VIRTIO_GPU_FILL_CMD(cblob); + virtio_gpu_create_blob_bswap(&cblob); + trace_virtio_gpu_cmd_res_create_blob(cblob.resource_id, cblob.size); + + if (cblob.resource_id == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource id 0 is not allowed\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res = virtio_gpu_virgl_find_resource(g, cblob.resource_id); + if (res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource already exists %d\n", + __func__, cblob.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + res = g_new0(struct virtio_gpu_virgl_resource, 1); + res->base.resource_id = cblob.resource_id; + res->base.blob_size = cblob.size; + res->base.dmabuf_fd = -1; + + if (cblob.blob_mem != VIRTIO_GPU_BLOB_MEM_HOST3D) { + ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob), + cmd, &res->base.addrs, + &res->base.iov, &res->base.iov_cnt); + if (!ret) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + } + + virgl_args.res_handle = cblob.resource_id; + virgl_args.ctx_id = cblob.hdr.ctx_id; + virgl_args.blob_mem = cblob.blob_mem; + virgl_args.blob_id = cblob.blob_id; + virgl_args.blob_flags = cblob.blob_flags; + virgl_args.size = cblob.size; + virgl_args.iovecs = res->base.iov; + virgl_args.num_iovs = res->base.iov_cnt; + + ret = virgl_renderer_resource_create_blob(&virgl_args); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: virgl blob create error: %s\n", + __func__, strerror(-ret)); + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + virtio_gpu_cleanup_mapping(g, &res->base); + return; + } + + ret = virgl_renderer_resource_get_info(cblob.resource_id, &info); + if (ret) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource does not have info %d: %s\n", + __func__, cblob.resource_id, strerror(-ret)); + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + virtio_gpu_cleanup_mapping(g, &res->base); + virgl_renderer_resource_unref(cblob.resource_id); + return; + } + + res->base.dmabuf_fd = info.fd; + + QTAILQ_INSERT_HEAD(&g->reslist, &res->base, next); + res = NULL; +} + +static void virgl_cmd_resource_map_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resource_map_blob mblob; + struct virtio_gpu_virgl_resource *res; + struct virtio_gpu_resp_map_info resp; + int ret; + + VIRTIO_GPU_FILL_CMD(mblob); + virtio_gpu_map_blob_bswap(&mblob); + + res = virtio_gpu_virgl_find_resource(g, mblob.resource_id); + if (!res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n", + __func__, mblob.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + ret = virtio_gpu_virgl_map_resource_blob(g, res, mblob.offset); + if (ret) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + memset(&resp, 0, sizeof(resp)); + resp.hdr.type = VIRTIO_GPU_RESP_OK_MAP_INFO; + virgl_renderer_resource_get_map_info(mblob.resource_id, &resp.map_info); + virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp)); +} + +static void virgl_cmd_resource_unmap_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd, + bool *cmd_suspended) +{ + struct virtio_gpu_resource_unmap_blob ublob; + struct virtio_gpu_virgl_resource *res; + int ret; + + VIRTIO_GPU_FILL_CMD(ublob); + virtio_gpu_unmap_blob_bswap(&ublob); + + res = virtio_gpu_virgl_find_resource(g, ublob.resource_id); + if (!res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n", + __func__, ublob.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + ret = virtio_gpu_virgl_unmap_resource_blob(g, res, cmd_suspended); + if (ret) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } +} + +static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_framebuffer fb = { 0 }; + struct virtio_gpu_virgl_resource *res; + struct virtio_gpu_set_scanout_blob ss; + + VIRTIO_GPU_FILL_CMD(ss); + virtio_gpu_scanout_blob_bswap(&ss); + trace_virtio_gpu_cmd_set_scanout_blob(ss.scanout_id, ss.resource_id, + ss.r.width, ss.r.height, ss.r.x, + ss.r.y); + + if (ss.scanout_id >= g->parent_obj.conf.max_outputs) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d", + __func__, ss.scanout_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; + return; + } + + if (ss.resource_id == 0) { + virtio_gpu_disable_scanout(g, ss.scanout_id); + return; + } + + if (ss.width < 16 || + ss.height < 16 || + ss.r.x + ss.r.width > ss.width || + ss.r.y + ss.r.height > ss.height) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" + " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", + __func__, ss.scanout_id, ss.resource_id, + ss.r.x, ss.r.y, ss.r.width, ss.r.height, + ss.width, ss.height); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + res = virtio_gpu_virgl_find_resource(g, ss.resource_id); + if (!res) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource does not exist %d\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + if (res->base.dmabuf_fd < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource not backed by dmabuf %d\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return; + } + + if (!virtio_gpu_scanout_blob_to_fb(&fb, &ss, res->base.blob_size)) { + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + g->parent_obj.enable = 1; + if (virtio_gpu_update_dmabuf(g, ss.scanout_id, &res->base, &fb, &ss.r)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to update dmabuf\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + virtio_gpu_update_scanout(g, ss.scanout_id, &res->base, &fb, &ss.r); +} +#endif + void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { + bool cmd_suspended = false; + VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr); virgl_renderer_force_ctx_0(); @@ -442,7 +914,7 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, virgl_cmd_resource_flush(g, cmd); break; case VIRTIO_GPU_CMD_RESOURCE_UNREF: - virgl_cmd_resource_unref(g, cmd); + virgl_cmd_resource_unref(g, cmd, &cmd_suspended); break; case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE: /* TODO add security */ @@ -464,12 +936,26 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, case VIRTIO_GPU_CMD_GET_EDID: virtio_gpu_get_edid(g, cmd); break; +#if VIRGL_VERSION_MAJOR >= 1 + case VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB: + virgl_cmd_resource_create_blob(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB: + virgl_cmd_resource_map_blob(g, cmd); + break; + case VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB: + virgl_cmd_resource_unmap_blob(g, cmd, &cmd_suspended); + break; + case VIRTIO_GPU_CMD_SET_SCANOUT_BLOB: + virgl_cmd_set_scanout_blob(g, cmd); + break; +#endif default: cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; break; } - if (cmd->finished) { + if (cmd_suspended || cmd->finished) { return; } if (cmd->error) { @@ -506,7 +992,7 @@ static void virgl_write_fence(void *opaque, uint32_t fence) g_free(cmd); g->inflight--; if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { - fprintf(stderr, "inflight: %3d (-)\r", g->inflight); + trace_virtio_gpu_dec_inflight_fences(g->inflight); } } } @@ -555,6 +1041,7 @@ static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = { static void virtio_gpu_print_stats(void *opaque) { VirtIOGPU *g = opaque; + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g); if (g->stats.requests) { fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n", @@ -569,17 +1056,18 @@ static void virtio_gpu_print_stats(void *opaque) } else { fprintf(stderr, "stats: idle\r"); } - timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); + timer_mod(gl->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); } static void virtio_gpu_fence_poll(void *opaque) { VirtIOGPU *g = opaque; + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g); virgl_renderer_poll(); virtio_gpu_process_cmdq(g); if (!QTAILQ_EMPTY(&g->cmdq) || !QTAILQ_EMPTY(&g->fenceq)) { - timer_mod(g->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10); + timer_mod(gl->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10); } } @@ -606,30 +1094,81 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) int virtio_gpu_virgl_init(VirtIOGPU *g) { int ret; + uint32_t flags = 0; + VirtIOGPUGL *gl = VIRTIO_GPU_GL(g); - 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 +#if VIRGL_VERSION_MAJOR >= 1 + if (virtio_gpu_venus_enabled(g->parent_obj.conf)) { + flags |= VIRGL_RENDERER_VENUS | VIRGL_RENDERER_RENDER_SERVER; + } +#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; } - g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL, - virtio_gpu_fence_poll, g); + gl->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL, + virtio_gpu_fence_poll, g); if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { - g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL, - virtio_gpu_print_stats, g); - timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); + gl->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL, + virtio_gpu_print_stats, g); + timer_mod(gl->print_stats, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); } + +#if VIRGL_VERSION_MAJOR >= 1 + gl->cmdq_resume_bh = aio_bh_new(qemu_get_aio_context(), + virtio_gpu_virgl_resume_cmdq_bh, + g); +#endif + return 0; } -int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g) +static void virtio_gpu_virgl_add_capset(GArray *capset_ids, uint32_t capset_id) { - uint32_t capset2_max_ver, capset2_max_size; - virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2, - &capset2_max_ver, - &capset2_max_size); - - return capset2_max_ver ? 2 : 1; + g_array_append_val(capset_ids, capset_id); +} + +GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g) +{ + uint32_t capset_max_ver, capset_max_size; + GArray *capset_ids; + + capset_ids = g_array_new(false, false, sizeof(uint32_t)); + + /* VIRGL is always supported. */ + virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VIRGL); + + virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2, + &capset_max_ver, + &capset_max_size); + if (capset_max_ver) { + virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VIRGL2); + } + + if (virtio_gpu_venus_enabled(g->parent_obj.conf)) { + virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VENUS, + &capset_max_ver, + &capset_max_size); + if (capset_max_size) { + virtio_gpu_virgl_add_capset(capset_ids, VIRTIO_GPU_CAPSET_VENUS); + } + } + + return capset_ids; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 4e2e0dd53a..7d22d03bbf 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,24 +26,21 @@ #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/memfd.h" #include "qemu/module.h" #include "qapi/error.h" #include "qemu/error-report.h" #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, @@ -111,11 +110,10 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor) s->cursor.pos.x = cursor->pos.x; s->cursor.pos.y = cursor->pos.y; } - dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, - cursor->resource_id ? 1 : 0); + dpy_mouse_set(s->con, cursor->pos.x, cursor->pos.y, cursor->resource_id); } -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,23 +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, - .refresh_rate = b->req_state[scanout].refresh_rate, - }; - - 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) { @@ -242,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)); } @@ -304,12 +285,20 @@ 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); + if (!qemu_pixman_image_new_shareable( + &res->image, + &res->share_handle, + "virtio-gpu res", + pformat, + c2d.width, + c2d.height, + c2d.height ? res->hostmem / c2d.height : 0, + &error_warn)) { + goto end; + } } +end: if (!res->image) { qemu_log_mask(LOG_GUEST_ERROR, "%s: resource creation failed %d %d %d\n", @@ -373,7 +362,7 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, QTAILQ_INSERT_HEAD(&g->reslist, res, next); } -static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) +void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) { struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id]; struct virtio_gpu_simple_resource *res; @@ -395,7 +384,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; @@ -431,18 +421,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); @@ -471,23 +465,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); } } @@ -497,7 +491,7 @@ 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; @@ -559,34 +553,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) @@ -594,10 +579,11 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) pixman_image_unref(data); } -static void virtio_gpu_update_scanout(VirtIOGPU *g, - uint32_t scanout_id, - struct virtio_gpu_simple_resource *res, - struct virtio_gpu_rect *r) +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; struct virtio_gpu_scanout *scanout; @@ -614,9 +600,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, @@ -642,7 +629,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; @@ -650,9 +637,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; @@ -679,17 +669,15 @@ 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; - } + qemu_displaysurface_set_share_handle(scanout->ds, res->share_handle, fb->offset); 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, @@ -733,13 +721,47 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, &fb, res, &ss.r, &cmd->error); } +bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_set_scanout_blob *ss, + uint64_t blob_size) +{ + uint64_t fbend; + + fb->format = virtio_gpu_get_pixman_format(ss->format); + if (!fb->format) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: host couldn't handle guest format %d\n", + __func__, ss->format); + return false; + } + + fb->bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb->format), 8); + fb->width = ss->width; + fb->height = ss->height; + fb->stride = ss->strides[0]; + fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride; + + fbend = fb->offset; + fbend += (uint64_t) fb->stride * ss->r.height; + + if (fbend > blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: fb end out of range\n", + __func__); + return false; + } + + return true; +} + + + static void virtio_gpu_set_scanout_blob(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { struct virtio_gpu_simple_resource *res; struct virtio_gpu_framebuffer fb = { 0 }; struct virtio_gpu_set_scanout_blob ss; - uint64_t fbend; VIRTIO_GPU_FILL_CMD(ss); virtio_gpu_scanout_blob_bswap(&ss); @@ -765,28 +787,7 @@ static void virtio_gpu_set_scanout_blob(VirtIOGPU *g, return; } - fb.format = virtio_gpu_get_pixman_format(ss.format); - if (!fb.format) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: host couldn't handle guest format %d\n", - __func__, ss.format); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - return; - } - - fb.bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb.format), 8); - fb.width = ss.width; - fb.height = ss.height; - fb.stride = ss.strides[0]; - fb.offset = ss.offsets[0] + ss.r.x * fb.bytes_pp + ss.r.y * fb.stride; - - fbend = fb.offset; - fbend += fb.stride * (ss.r.height - 1); - fbend += fb.bytes_pp * ss.r.width; - if (fbend > res->blob_size) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: fb end out of range\n", - __func__); + if (!virtio_gpu_scanout_blob_to_fb(&fb, &ss, res->blob_size)) { cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } @@ -889,8 +890,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; @@ -1046,6 +1047,12 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) /* process command */ vgc->process_cmd(g, cmd); + /* command suspended */ + if (!cmd->finished && !(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) { + trace_virtio_gpu_cmd_suspended(cmd->cmd_hdr.type); + break; + } + QTAILQ_REMOVE(&g->cmdq, cmd, next); if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { g->stats.requests++; @@ -1058,7 +1065,7 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) if (g->stats.max_inflight < g->inflight) { g->stats.max_inflight = g->inflight; } - fprintf(stderr, "inflight: %3d (+)\r", g->inflight); + trace_virtio_gpu_inc_inflight_fences(g->inflight); } } else { g_free(cmd); @@ -1078,7 +1085,7 @@ static void virtio_gpu_process_fenceq(VirtIOGPU *g) g_free(cmd); g->inflight--; if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { - fprintf(stderr, "inflight: %3d (-)\r", g->inflight); + trace_virtio_gpu_dec_inflight_fences(g->inflight); } } } @@ -1117,7 +1124,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) @@ -1158,10 +1165,18 @@ static void virtio_gpu_cursor_bh(void *opaque) virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq); } +static bool scanout_vmstate_after_v2(void *opaque, int version) +{ + struct VirtIOGPUBase *base = container_of(opaque, VirtIOGPUBase, scanout); + struct VirtIOGPU *gpu = container_of(base, VirtIOGPU, parent_obj); + + return gpu->scanout_vmstate_version >= 2; +} + static const VMStateDescription vmstate_virtio_gpu_scanout = { .name = "virtio-gpu-one-scanout", .version_id = 1, - .fields = (VMStateField[]) { + .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), @@ -1172,6 +1187,18 @@ 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_TEST(fb.format, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.stride, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), + VMSTATE_UINT32_TEST(fb.offset, struct virtio_gpu_scanout, + scanout_vmstate_after_v2), VMSTATE_END_OF_LIST() }, }; @@ -1179,7 +1206,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), @@ -1202,6 +1229,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); @@ -1219,12 +1249,40 @@ 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; int i; @@ -1250,16 +1308,20 @@ 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); - if (!res->image) { + + res->hostmem = calc_image_hostmem(pformat, res->width, res->height); + if (!qemu_pixman_image_new_shareable(&res->image, + &res->share_handle, + "virtio-gpu res", + pformat, + res->width, + res->height, + res->height ? res->hostmem / res->height : 0, + &error_warn)) { g_free(res); return -EINVAL; } - res->hostmem = calc_image_hostmem(pformat, res->width, res->height); - res->addrs = g_new(uint64_t, res->iov_cnt); res->iov = g_new(struct iovec, res->iov_cnt); @@ -1271,55 +1333,130 @@ 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); + qemu_displaysurface_set_share_handle(scanout->ds, res->share_handle, 0); + 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); @@ -1336,15 +1473,36 @@ 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_virgl_enabled(g->parent_obj.conf) && + !virtio_gpu_have_udmabuf()) { + error_setg(errp, "need rutabaga or udmabuf for blob resources"); return; } +#ifdef VIRGL_VERSION_MAJOR + #if VIRGL_VERSION_MAJOR < 1 if (virtio_gpu_virgl_enabled(g->parent_obj.conf)) { - error_setg(errp, "blobs and virgl are not compatible (yet)"); + error_setg(errp, "old virglrenderer, blob resources unsupported"); return; } + #endif +#endif + } + + if (virtio_gpu_venus_enabled(g->parent_obj.conf)) { +#ifdef VIRGL_VERSION_MAJOR + #if VIRGL_VERSION_MAJOR >= 1 + if (!virtio_gpu_blob_enabled(g->parent_obj.conf) || + !virtio_gpu_hostmem_enabled(g->parent_obj.conf)) { + error_setg(errp, "venus requires enabled blob and hostmem options"); + return; + } + #else + error_setg(errp, "old virglrenderer, venus unsupported"); + return; + #endif +#endif } if (!virtio_gpu_base_device_realize(qdev, @@ -1356,21 +1514,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)) { @@ -1409,6 +1616,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 @@ -1421,7 +1654,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", @@ -1434,6 +1667,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[] = { @@ -1442,6 +1680,8 @@ 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_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2), DEFINE_PROP_END_OF_LIST(), }; @@ -1455,9 +1695,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 4dcb34c4a7..276f315108 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -88,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), @@ -115,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 @@ -165,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, ResetType type) { - 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, type); + } /* reset vga */ vga_common_reset(&vvga->vga); @@ -203,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 53949d2539..f2d72c3fc7 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -29,7 +29,7 @@ #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" @@ -336,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)) { @@ -550,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 @@ -904,10 +904,8 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address) caps |= SVGA_CAP_RECT_FILL; #endif #ifdef HW_MOUSE_ACCEL - if (dpy_cursor_define_supported(s->vga.con)) { - caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | - SVGA_CAP_CURSOR_BYPASS; - } + caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 | + SVGA_CAP_CURSOR_BYPASS; #endif ret = caps; break; @@ -1167,7 +1165,7 @@ static void vmsvga_reset(DeviceState *dev) s->enable = 0; s->config = 0; s->svgaid = SVGA_ID; - s->cursor.on = 0; + s->cursor.on = false; s->redraw_fifo_last = 0; s->syncing = 0; @@ -1210,7 +1208,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), @@ -1235,7 +1233,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), @@ -1264,7 +1262,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; } @@ -1354,7 +1352,7 @@ static void vmsvga_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_DISPLAY_VGA; k->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; k->subsystem_id = SVGA_PCI_DEVICE_ID; - dc->reset = vmsvga_reset; + device_class_set_legacy_reset(dc, vmsvga_reset); dc->vmsd = &vmstate_vmware_vga; device_class_set_props(dc, vga_vmware_properties); dc->hotpluggable = false; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 260eb38a76..314d378a1b 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -29,6 +29,7 @@ #include "ui/input.h" #include "ui/console.h" +#include "sysemu/sysemu.h" #include "hw/xen/xen-legacy-backend.h" #include "hw/xen/interface/io/fbif.h" @@ -98,8 +99,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,7 +117,7 @@ static void common_unbind(struct common *c) { xen_pv_unbind_evtchn(&c->xendev); if (c->page) { - xenforeignmemory_unmap(xen_fmem, c->page, 1); + qemu_xen_foreignmem_unmap(c->page, 1); c->page = NULL; } } @@ -320,20 +322,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, @@ -488,27 +490,28 @@ static int xenfb_map_fb(struct XenFB *xenfb) } 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; 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; @@ -526,8 +529,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) { @@ -635,7 +638,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) int linesize = surface_stride(surface); uint8_t *data = surface_data(surface); - if (!is_buffer_shared(surface)) { + if (surface_is_allocated(surface)) { switch (xenfb->depth) { case 8: if (bpp == 16) { @@ -753,7 +756,8 @@ static void xenfb_update(void *opaque) xen_pv_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n", xenfb->width, xenfb->height, xenfb->depth, - is_buffer_shared(surface) ? " (shared)" : ""); + surface_is_allocated(surface) + ? " (allocated)" : " (borrowed)"); xenfb->up_fullscreen = 1; } @@ -927,8 +931,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) { @@ -970,7 +974,7 @@ static void fb_event(struct XenLegacyDevice *xendev) /* -------------------------------------------------------------------- */ -struct XenDevOps xen_kbdmouse_ops = { +static const struct XenDevOps xen_kbdmouse_ops = { .size = sizeof(struct XenInput), .init = input_init, .initialise = input_initialise, @@ -979,7 +983,7 @@ struct XenDevOps xen_kbdmouse_ops = { .event = input_event, }; -struct XenDevOps xen_framebuffer_ops = { +const struct XenDevOps xen_framebuffer_ops = { .size = sizeof(struct XenFB), .init = fb_init, .initialise = fb_initialise, @@ -993,3 +997,13 @@ static const GraphicHwOps xenfb_ops = { .gfx_update = xenfb_update, .ui_info = xenfb_ui_info, }; + +static void xen_ui_register_backend(void) +{ + xen_be_register("vkbd", &xen_kbdmouse_ops); + + if (vga_interface_type == VGA_XENFB) { + xen_be_register("vfb", &xen_framebuffer_ops); + } +} +xen_backend_init(xen_ui_register_backend); diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index b0828d65aa..6ab2335499 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); @@ -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,13 +1387,19 @@ 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); dc->realize = xlnx_dp_realize; dc->vmsd = &vmstate_dp; - dc->reset = xlnx_dp_reset; + device_class_set_legacy_reset(dc, 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..9b2fca2c7c 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), @@ -390,7 +390,7 @@ static void bcm2835_dma_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = bcm2835_dma_realize; - dc->reset = bcm2835_dma_reset; + device_class_set_legacy_reset(dc, bcm2835_dma_reset); dc->vmsd = &vmstate_bcm2835_dma; } diff --git a/hw/dma/etraxfs_dma.c b/hw/dma/etraxfs_dma.c deleted file mode 100644 index c4334e87bf..0000000000 --- a/hw/dma/etraxfs_dma.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * QEMU ETRAX DMA Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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/hw.h" -#include "hw/irq.h" -#include "qemu/main-loop.h" -#include "sysemu/runstate.h" -#include "exec/address-spaces.h" - -#include "hw/cris/etraxfs_dma.h" - -#define D(x) - -#define RW_DATA (0x0 / 4) -#define RW_SAVED_DATA (0x58 / 4) -#define RW_SAVED_DATA_BUF (0x5c / 4) -#define RW_GROUP (0x60 / 4) -#define RW_GROUP_DOWN (0x7c / 4) -#define RW_CMD (0x80 / 4) -#define RW_CFG (0x84 / 4) -#define RW_STAT (0x88 / 4) -#define RW_INTR_MASK (0x8c / 4) -#define RW_ACK_INTR (0x90 / 4) -#define R_INTR (0x94 / 4) -#define R_MASKED_INTR (0x98 / 4) -#define RW_STREAM_CMD (0x9c / 4) - -#define DMA_REG_MAX (0x100 / 4) - -/* descriptors */ - -// ------------------------------------------------------------ dma_descr_group -typedef struct dma_descr_group { - uint32_t next; - unsigned eol : 1; - unsigned tol : 1; - unsigned bol : 1; - unsigned : 1; - unsigned intr : 1; - unsigned : 2; - unsigned en : 1; - unsigned : 7; - unsigned dis : 1; - unsigned md : 16; - struct dma_descr_group *up; - union { - struct dma_descr_context *context; - struct dma_descr_group *group; - } down; -} dma_descr_group; - -// ---------------------------------------------------------- dma_descr_context -typedef struct dma_descr_context { - uint32_t next; - unsigned eol : 1; - unsigned : 3; - unsigned intr : 1; - unsigned : 1; - unsigned store_mode : 1; - unsigned en : 1; - unsigned : 7; - unsigned dis : 1; - unsigned md0 : 16; - unsigned md1; - unsigned md2; - unsigned md3; - unsigned md4; - uint32_t saved_data; - uint32_t saved_data_buf; -} dma_descr_context; - -// ------------------------------------------------------------- dma_descr_data -typedef struct dma_descr_data { - uint32_t next; - uint32_t buf; - unsigned eol : 1; - unsigned : 2; - unsigned out_eop : 1; - unsigned intr : 1; - unsigned wait : 1; - unsigned : 2; - unsigned : 3; - unsigned in_eop : 1; - unsigned : 4; - unsigned md : 16; - uint32_t after; -} dma_descr_data; - -/* Constants */ -enum { - regk_dma_ack_pkt = 0x00000100, - regk_dma_anytime = 0x00000001, - regk_dma_array = 0x00000008, - regk_dma_burst = 0x00000020, - regk_dma_client = 0x00000002, - regk_dma_copy_next = 0x00000010, - regk_dma_copy_up = 0x00000020, - regk_dma_data_at_eol = 0x00000001, - regk_dma_dis_c = 0x00000010, - regk_dma_dis_g = 0x00000020, - regk_dma_idle = 0x00000001, - regk_dma_intern = 0x00000004, - regk_dma_load_c = 0x00000200, - regk_dma_load_c_n = 0x00000280, - regk_dma_load_c_next = 0x00000240, - regk_dma_load_d = 0x00000140, - regk_dma_load_g = 0x00000300, - regk_dma_load_g_down = 0x000003c0, - regk_dma_load_g_next = 0x00000340, - regk_dma_load_g_up = 0x00000380, - regk_dma_next_en = 0x00000010, - regk_dma_next_pkt = 0x00000010, - regk_dma_no = 0x00000000, - regk_dma_only_at_wait = 0x00000000, - regk_dma_restore = 0x00000020, - regk_dma_rst = 0x00000001, - regk_dma_running = 0x00000004, - regk_dma_rw_cfg_default = 0x00000000, - regk_dma_rw_cmd_default = 0x00000000, - regk_dma_rw_intr_mask_default = 0x00000000, - regk_dma_rw_stat_default = 0x00000101, - regk_dma_rw_stream_cmd_default = 0x00000000, - regk_dma_save_down = 0x00000020, - regk_dma_save_up = 0x00000020, - regk_dma_set_reg = 0x00000050, - regk_dma_set_w_size1 = 0x00000190, - regk_dma_set_w_size2 = 0x000001a0, - regk_dma_set_w_size4 = 0x000001c0, - regk_dma_stopped = 0x00000002, - regk_dma_store_c = 0x00000002, - regk_dma_store_descr = 0x00000000, - regk_dma_store_g = 0x00000004, - regk_dma_store_md = 0x00000001, - regk_dma_sw = 0x00000008, - regk_dma_update_down = 0x00000020, - regk_dma_yes = 0x00000001 -}; - -enum dma_ch_state -{ - RST = 1, - STOPPED = 2, - RUNNING = 4 -}; - -struct fs_dma_channel -{ - qemu_irq irq; - struct etraxfs_dma_client *client; - - /* Internal status. */ - int stream_cmd_src; - enum dma_ch_state state; - - 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; - - /* Control registers. */ - uint32_t regs[DMA_REG_MAX]; -}; - -struct fs_dma_ctrl -{ - MemoryRegion mmio; - int nr_channels; - struct fs_dma_channel *channels; - - QEMUBH *bh; -}; - -static void DMA_run(void *opaque); -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]; -} - -static inline int channel_stopped(struct fs_dma_ctrl *ctrl, int c) -{ - 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; -} - -static inline int fs_channel(hwaddr addr) -{ - /* 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); - - /* 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); -} - -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); -} -#endif - -static void channel_load_c(struct fs_dma_ctrl *ctrl, int c) -{ - hwaddr addr = channel_reg(ctrl, c, RW_GROUP_DOWN); - - /* Load and decode. FIXME: handle endianness. */ - cpu_physical_memory_read(addr, &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; -} - -static void channel_load_d(struct fs_dma_ctrl *ctrl, int c) -{ - 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)); - 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; -} - -static void channel_store_c(struct fs_dma_ctrl *ctrl, int c) -{ - 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)); - 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); - - /* Encode and store. FIXME: handle endianness. */ - D(printf("%s ch=%d addr=" TARGET_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: */ -} - -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); - - 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; - } - - /* 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; - - 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); - - 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); - } -} - -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]); - - 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])); - - 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]; - - struct dma_context_metadata meta; - bool send_context = true; - - 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)); - - 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; - - 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; - - 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); - } - - 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; - - /* 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; - } - - 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) -{ - uint32_t len; - uint32_t saved_data_buf; - - 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; - - cpu_physical_memory_write (saved_data_buf, buf, len); - saved_data_buf += len; - - 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]; - - 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; - - /* 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); - - channel_store_d(ctrl, c); - D(dump_d(c, &ctrl->channels[c].current_d)); - - 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); - - 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; -} - -static uint32_t dma_rinvalid (void *opaque, hwaddr addr) -{ - hw_error("Unsupported short raccess. reg=" TARGET_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; - - 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; - - default: - r = ctrl->channels[c].regs[addr]; - D(printf ("%s c=%d addr=" TARGET_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); -} - -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; -} - -static void -dma_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct fs_dma_ctrl *ctrl = opaque; - uint32_t value = val64; - int c; - - 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; - - 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_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; - - default: - D(printf ("%s c=%d " TARGET_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 - } -}; - -static int etraxfs_dmac_run(void *opaque) -{ - 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; -} - -int etraxfs_dmac_input(struct etraxfs_dma_client *client, - void *buf, int len, int 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; -} - -void etraxfs_dmac_connect_client(void *opaque, int c, - struct etraxfs_dma_client *cl) -{ - struct fs_dma_ctrl *ctrl = opaque; - cl->ctrl = ctrl; - cl->channel = c; - ctrl->channels[c].client = cl; -} - - -static void DMA_run(void *opaque) -{ - struct fs_dma_ctrl *etraxfs_dmac = opaque; - int p = 1; - - if (runstate_is_running()) - p = etraxfs_dmac_run(etraxfs_dmac); - - if (p) - qemu_bh_schedule_idle(etraxfs_dmac->bh); -} - -void *etraxfs_dmac_init(hwaddr base, int nr_channels) -{ - struct fs_dma_ctrl *ctrl = NULL; - - ctrl = g_malloc0(sizeof *ctrl); - - ctrl->bh = qemu_bh_new(DMA_run, ctrl); - - 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); - - 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..3e6700e53b 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), @@ -599,7 +599,7 @@ static void i8257_class_init(ObjectClass *klass, void *data) IsaDmaClass *idc = ISADMA_CLASS(klass); dc->realize = i8257_realize; - dc->reset = i8257_reset; + device_class_set_legacy_reset(dc, i8257_reset); dc->vmsd = &vmstate_i8257; device_class_set_props(dc, i8257_properties); @@ -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..cc7810beb8 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -1,16 +1,14 @@ -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_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_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..9a8c3c34a0 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; @@ -686,10 +686,7 @@ void omap_dma_reset(struct soc_dma_s *dma) struct omap_dma_s *s = dma->opaque; soc_dma_reset(s->dma); - if (s->model < omap_dma_4) - s->gcr = 0x0004; - else - s->gcr = 0x00010010; + s->gcr = 0x0004; s->ocp = 0x00000000; memset(&s->irqstat, 0, sizeof(s->irqstat)); memset(&s->irqen, 0, sizeof(s->irqen)); @@ -697,8 +694,7 @@ void omap_dma_reset(struct soc_dma_s *dma) s->lcd_ch.condition = 0; s->lcd_ch.interrupts = 0; s->lcd_ch.dual = 0; - if (s->model < omap_dma_4) - omap_dma_enable_3_1_mapping(s); + omap_dma_enable_3_1_mapping(s); for (i = 0; i < s->chans; i ++) { s->ch[i].suspend = 0; s->ch[i].prefetch = 0; @@ -721,10 +717,7 @@ void omap_dma_reset(struct soc_dma_s *dma) s->ch[i].repeat = 0; s->ch[i].auto_init = 0; s->ch[i].link_enabled = 0; - if (s->model < omap_dma_4) - s->ch[i].interrupts = 0x0003; - else - s->ch[i].interrupts = 0x0000; + s->ch[i].interrupts = 0x0003; s->ch[i].status = 0; s->ch[i].cstatus = 0; s->ch[i].active = 0; @@ -1454,10 +1447,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 +1497,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 +1549,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 +1563,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); @@ -1588,7 +1580,6 @@ static void omap_dma_setcaps(struct omap_dma_s *s) case omap_dma_3_1: break; case omap_dma_3_2: - case omap_dma_4: /* XXX Only available for sDMA */ s->caps[0] = (1 << 19) | /* Constant Fill Capability */ @@ -1679,443 +1670,6 @@ struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, return s->dma; } -static void omap_dma_interrupts_4_update(struct omap_dma_s *s) -{ - struct omap_dma_channel_s *ch = s->ch; - uint32_t bmp, bit; - - for (bmp = 0, bit = 1; bit; ch ++, bit <<= 1) - if (ch->status) { - bmp |= bit; - ch->cstatus |= ch->status; - ch->status = 0; - } - if ((s->irqstat[0] |= s->irqen[0] & bmp)) - qemu_irq_raise(s->irq[0]); - if ((s->irqstat[1] |= s->irqen[1] & bmp)) - qemu_irq_raise(s->irq[1]); - if ((s->irqstat[2] |= s->irqen[2] & bmp)) - qemu_irq_raise(s->irq[2]); - if ((s->irqstat[3] |= s->irqen[3] & bmp)) - qemu_irq_raise(s->irq[3]); -} - -static uint64_t omap_dma4_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int irqn = 0, chnum; - struct omap_dma_channel_s *ch; - - if (size == 1) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* DMA4_REVISION */ - return 0x40; - - case 0x14: /* DMA4_IRQSTATUS_L3 */ - irqn ++; - /* fall through */ - case 0x10: /* DMA4_IRQSTATUS_L2 */ - irqn ++; - /* fall through */ - case 0x0c: /* DMA4_IRQSTATUS_L1 */ - irqn ++; - /* fall through */ - case 0x08: /* DMA4_IRQSTATUS_L0 */ - return s->irqstat[irqn]; - - case 0x24: /* DMA4_IRQENABLE_L3 */ - irqn ++; - /* fall through */ - case 0x20: /* DMA4_IRQENABLE_L2 */ - irqn ++; - /* fall through */ - case 0x1c: /* DMA4_IRQENABLE_L1 */ - irqn ++; - /* fall through */ - case 0x18: /* DMA4_IRQENABLE_L0 */ - return s->irqen[irqn]; - - case 0x28: /* DMA4_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x2c: /* DMA4_OCP_SYSCONFIG */ - return s->ocp; - - case 0x64: /* DMA4_CAPS_0 */ - return s->caps[0]; - case 0x6c: /* DMA4_CAPS_2 */ - return s->caps[2]; - case 0x70: /* DMA4_CAPS_3 */ - return s->caps[3]; - case 0x74: /* DMA4_CAPS_4 */ - return s->caps[4]; - - case 0x78: /* DMA4_GCR */ - return s->gcr; - - case 0x80 ... 0xfff: - addr -= 0x80; - chnum = addr / 0x60; - ch = s->ch + chnum; - addr -= chnum * 0x60; - break; - - default: - OMAP_BAD_REG(addr); - return 0; - } - - /* Per-channel registers */ - switch (addr) { - case 0x00: /* DMA4_CCR */ - return (ch->buf_disable << 25) | - (ch->src_sync << 24) | - (ch->prefetch << 23) | - ((ch->sync & 0x60) << 14) | - (ch->bs << 18) | - (ch->transparent_copy << 17) | - (ch->constant_fill << 16) | - (ch->mode[1] << 14) | - (ch->mode[0] << 12) | - (0 << 10) | (0 << 9) | - (ch->suspend << 8) | - (ch->enable << 7) | - (ch->priority << 6) | - (ch->fs << 5) | (ch->sync & 0x1f); - - case 0x04: /* DMA4_CLNK_CTRL */ - return (ch->link_enabled << 15) | ch->link_next_ch; - - case 0x08: /* DMA4_CICR */ - return ch->interrupts; - - case 0x0c: /* DMA4_CSR */ - return ch->cstatus; - - case 0x10: /* DMA4_CSDP */ - return (ch->endian[0] << 21) | - (ch->endian_lock[0] << 20) | - (ch->endian[1] << 19) | - (ch->endian_lock[1] << 18) | - (ch->write_mode << 16) | - (ch->burst[1] << 14) | - (ch->pack[1] << 13) | - (ch->translate[1] << 9) | - (ch->burst[0] << 7) | - (ch->pack[0] << 6) | - (ch->translate[0] << 2) | - (ch->data_type >> 1); - - case 0x14: /* DMA4_CEN */ - return ch->elements; - - case 0x18: /* DMA4_CFN */ - return ch->frames; - - case 0x1c: /* DMA4_CSSA */ - return ch->addr[0]; - - case 0x20: /* DMA4_CDSA */ - return ch->addr[1]; - - case 0x24: /* DMA4_CSEI */ - return ch->element_index[0]; - - case 0x28: /* DMA4_CSFI */ - return ch->frame_index[0]; - - case 0x2c: /* DMA4_CDEI */ - return ch->element_index[1]; - - case 0x30: /* DMA4_CDFI */ - return ch->frame_index[1]; - - case 0x34: /* DMA4_CSAC */ - return ch->active_set.src & 0xffff; - - case 0x38: /* DMA4_CDAC */ - return ch->active_set.dest & 0xffff; - - case 0x3c: /* DMA4_CCEN */ - return ch->active_set.element; - - case 0x40: /* DMA4_CCFN */ - return ch->active_set.frame; - - case 0x44: /* DMA4_COLOR */ - /* XXX only in sDMA */ - return ch->color; - - default: - OMAP_BAD_REG(addr); - return 0; - } -} - -static void omap_dma4_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dma_s *s = (struct omap_dma_s *) opaque; - int chnum, irqn = 0; - struct omap_dma_channel_s *ch; - - if (size == 1) { - omap_badwidth_write16(opaque, addr, value); - return; - } - - switch (addr) { - case 0x14: /* DMA4_IRQSTATUS_L3 */ - irqn ++; - /* fall through */ - case 0x10: /* DMA4_IRQSTATUS_L2 */ - irqn ++; - /* fall through */ - case 0x0c: /* DMA4_IRQSTATUS_L1 */ - irqn ++; - /* fall through */ - case 0x08: /* DMA4_IRQSTATUS_L0 */ - s->irqstat[irqn] &= ~value; - if (!s->irqstat[irqn]) - qemu_irq_lower(s->irq[irqn]); - return; - - case 0x24: /* DMA4_IRQENABLE_L3 */ - irqn ++; - /* fall through */ - case 0x20: /* DMA4_IRQENABLE_L2 */ - irqn ++; - /* fall through */ - case 0x1c: /* DMA4_IRQENABLE_L1 */ - irqn ++; - /* fall through */ - case 0x18: /* DMA4_IRQENABLE_L0 */ - s->irqen[irqn] = value; - return; - - case 0x2c: /* DMA4_OCP_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dma_reset(s->dma); - s->ocp = value & 0x3321; - if (((s->ocp >> 12) & 3) == 3) { /* MIDLEMODE */ - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA power mode\n", - __func__); - } - return; - - case 0x78: /* DMA4_GCR */ - s->gcr = value & 0x00ff00ff; - if ((value & 0xff) == 0x00) { /* MAX_CHANNEL_FIFO_DEPTH */ - qemu_log_mask(LOG_GUEST_ERROR, "%s: wrong FIFO depth in GCR\n", - __func__); - } - return; - - case 0x80 ... 0xfff: - addr -= 0x80; - chnum = addr / 0x60; - ch = s->ch + chnum; - addr -= chnum * 0x60; - break; - - case 0x00: /* DMA4_REVISION */ - case 0x28: /* DMA4_SYSSTATUS */ - case 0x64: /* DMA4_CAPS_0 */ - case 0x6c: /* DMA4_CAPS_2 */ - case 0x70: /* DMA4_CAPS_3 */ - case 0x74: /* DMA4_CAPS_4 */ - OMAP_RO_REG(addr); - return; - - default: - OMAP_BAD_REG(addr); - return; - } - - /* Per-channel registers */ - switch (addr) { - case 0x00: /* DMA4_CCR */ - ch->buf_disable = (value >> 25) & 1; - ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */ - if (ch->buf_disable && !ch->src_sync) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Buffering disable is not allowed in " - "destination synchronised mode\n", __func__); - } - ch->prefetch = (value >> 23) & 1; - ch->bs = (value >> 18) & 1; - ch->transparent_copy = (value >> 17) & 1; - ch->constant_fill = (value >> 16) & 1; - ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); - ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); - ch->suspend = (value & 0x0100) >> 8; - ch->priority = (value & 0x0040) >> 6; - ch->fs = (value & 0x0020) >> 5; - if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: For a packet transfer at least one port " - "must be constant-addressed\n", __func__); - } - ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060); - /* XXX must be 0x01 for CamDMA */ - - if (value & 0x0080) - omap_dma_enable_channel(s, ch); - else - omap_dma_disable_channel(s, ch); - - break; - - case 0x04: /* DMA4_CLNK_CTRL */ - ch->link_enabled = (value >> 15) & 0x1; - ch->link_next_ch = value & 0x1f; - break; - - case 0x08: /* DMA4_CICR */ - ch->interrupts = value & 0x09be; - break; - - case 0x0c: /* DMA4_CSR */ - ch->cstatus &= ~value; - break; - - case 0x10: /* DMA4_CSDP */ - ch->endian[0] =(value >> 21) & 1; - ch->endian_lock[0] =(value >> 20) & 1; - ch->endian[1] =(value >> 19) & 1; - ch->endian_lock[1] =(value >> 18) & 1; - if (ch->endian[0] != ch->endian[1]) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: DMA endianness conversion enable attempt\n", - __func__); - } - ch->write_mode = (value >> 16) & 3; - ch->burst[1] = (value & 0xc000) >> 14; - ch->pack[1] = (value & 0x2000) >> 13; - ch->translate[1] = (value & 0x1e00) >> 9; - ch->burst[0] = (value & 0x0180) >> 7; - ch->pack[0] = (value & 0x0040) >> 6; - ch->translate[0] = (value & 0x003c) >> 2; - if (ch->translate[0] | ch->translate[1]) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: bad MReqAddressTranslate sideband signal\n", - __func__); - } - ch->data_type = 1 << (value & 3); - if ((value & 3) == 3) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: bad data_type for DMA channel\n", __func__); - ch->data_type >>= 1; - } - break; - - case 0x14: /* DMA4_CEN */ - ch->set_update = 1; - ch->elements = value & 0xffffff; - break; - - case 0x18: /* DMA4_CFN */ - ch->frames = value & 0xffff; - ch->set_update = 1; - break; - - case 0x1c: /* DMA4_CSSA */ - ch->addr[0] = (hwaddr) (uint32_t) value; - ch->set_update = 1; - break; - - case 0x20: /* DMA4_CDSA */ - ch->addr[1] = (hwaddr) (uint32_t) value; - ch->set_update = 1; - break; - - case 0x24: /* DMA4_CSEI */ - ch->element_index[0] = (int16_t) value; - ch->set_update = 1; - break; - - case 0x28: /* DMA4_CSFI */ - ch->frame_index[0] = (int32_t) value; - ch->set_update = 1; - break; - - case 0x2c: /* DMA4_CDEI */ - ch->element_index[1] = (int16_t) value; - ch->set_update = 1; - break; - - case 0x30: /* DMA4_CDFI */ - ch->frame_index[1] = (int32_t) value; - ch->set_update = 1; - break; - - case 0x44: /* DMA4_COLOR */ - /* XXX only in sDMA */ - ch->color = value; - break; - - case 0x34: /* DMA4_CSAC */ - case 0x38: /* DMA4_CDAC */ - case 0x3c: /* DMA4_CCEN */ - case 0x40: /* DMA4_CCFN */ - OMAP_RO_REG(addr); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_dma4_ops = { - .read = omap_dma4_read, - .write = omap_dma4_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, - MemoryRegion *sysmem, - struct omap_mpu_state_s *mpu, int fifo, - int chans, omap_clk iclk, omap_clk fclk) -{ - int i; - struct omap_dma_s *s = g_new0(struct omap_dma_s, 1); - - s->model = omap_dma_4; - s->chans = chans; - s->mpu = mpu; - s->clk = fclk; - - s->dma = soc_dma_init(s->chans); - s->dma->freq = omap_clk_getrate(fclk); - s->dma->transfer_fn = omap_dma_transfer_generic; - s->dma->setup_fn = omap_dma_transfer_setup; - s->dma->drq = qemu_allocate_irqs(omap_dma_request, s, 64); - s->dma->opaque = s; - for (i = 0; i < s->chans; i ++) { - s->ch[i].dma = &s->dma->ch[i]; - s->dma->ch[i].opaque = &s->ch[i]; - } - - memcpy(&s->irq, irqs, sizeof(s->irq)); - s->intr_update = omap_dma_interrupts_4_update; - - omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); - omap_dma_reset(s->dma); - omap_dma_clk_update(s, 0, !!s->dma->freq); - - memory_region_init_io(&s->iomem, NULL, &omap_dma4_ops, s, "omap.dma4", 0x1000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - mpu->drq = s->dma->drq; - - return s->dma; -} - struct omap_dma_lcd_channel_s *omap_dma_get_lcdch(struct soc_dma_s *dma) { struct omap_dma_s *s = dma->opaque; diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 2627307cc8..8e76f88a69 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), @@ -421,7 +421,7 @@ static void pl080_class_init(ObjectClass *oc, void *data) dc->vmsd = &vmstate_pl080; dc->realize = pl080_realize; device_class_set_props(dc, pl080_properties); - dc->reset = pl080_reset; + device_class_set_legacy_reset(dc, pl080_reset); } static const TypeInfo pl080_info = { diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index e5d521c329..0668caed7c 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -15,6 +15,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" @@ -139,7 +140,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 +171,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 +195,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 +217,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 +281,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), @@ -317,22 +318,14 @@ typedef struct PL330InsnDesc { static void pl330_hexdump(uint8_t *buf, size_t size) { - unsigned int b, i, len; - char tmpbuf[80]; + g_autoptr(GString) str = g_string_sized_new(64); + size_t b, len; - for (b = 0; b < size; b += 16) { - len = size - b; - if (len > 16) { - len = 16; - } - tmpbuf[0] = '\0'; - for (i = 0; i < len; i++) { - if ((i % 4) == 0) { - strcat(tmpbuf, " "); - } - sprintf(tmpbuf + strlen(tmpbuf), " %02x", buf[b + i]); - } - trace_pl330_hexdump(b, tmpbuf); + for (b = 0; b < size; b += len) { + len = MIN(16, size - b); + g_string_truncate(str, 0); + qemu_hexdump_line(str, buf + b, len, 1, 4); + trace_pl330_hexdump(b, str->str); } } @@ -1373,7 +1366,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 +1377,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 +1402,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 +1418,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 +1427,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 +1449,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 +1488,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; } @@ -1685,7 +1678,7 @@ static void pl330_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pl330_realize; - dc->reset = pl330_reset; + device_class_set_legacy_reset(dc, pl330_reset); device_class_set_props(dc, pl330_properties); dc->vmsd = &vmstate_pl330; } diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c deleted file mode 100644 index fa896f7edf..0000000000 --- a/hw/dma/pxa2xx_dma.c +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Intel XScale PXA255/270 DMA controller. - * - * Copyright (c) 2006 Openedhand Ltd. - * Copyright (c) 2006 Thorsten Zitterell - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/arm/pxa.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "qom/object.h" - -#define PXA255_DMA_NUM_CHANNELS 16 -#define PXA27X_DMA_NUM_CHANNELS 32 - -#define PXA2XX_DMA_NUM_REQUESTS 75 - -typedef struct { - uint32_t descr; - uint32_t src; - uint32_t dest; - uint32_t cmd; - uint32_t state; - int request; -} PXA2xxDMAChannel; - -#define TYPE_PXA2XX_DMA "pxa2xx-dma" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxDMAState, PXA2XX_DMA) - -struct PXA2xxDMAState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - - uint32_t stopintr; - uint32_t eorintr; - uint32_t rasintr; - uint32_t startintr; - uint32_t endintr; - - uint32_t align; - uint32_t pio; - - int channels; - PXA2xxDMAChannel *chan; - - uint8_t req[PXA2XX_DMA_NUM_REQUESTS]; - - /* Flag to avoid recursive DMA invocations. */ - int running; -}; - -#define DCSR0 0x0000 /* DMA Control / Status register for Channel 0 */ -#define DCSR31 0x007c /* DMA Control / Status register for Channel 31 */ -#define DALGN 0x00a0 /* DMA Alignment register */ -#define DPCSR 0x00a4 /* DMA Programmed I/O Control Status register */ -#define DRQSR0 0x00e0 /* DMA DREQ<0> Status register */ -#define DRQSR1 0x00e4 /* DMA DREQ<1> Status register */ -#define DRQSR2 0x00e8 /* DMA DREQ<2> Status register */ -#define DINT 0x00f0 /* DMA Interrupt register */ -#define DRCMR0 0x0100 /* Request to Channel Map register 0 */ -#define DRCMR63 0x01fc /* Request to Channel Map register 63 */ -#define D_CH0 0x0200 /* Channel 0 Descriptor start */ -#define DRCMR64 0x1100 /* Request to Channel Map register 64 */ -#define DRCMR74 0x1128 /* Request to Channel Map register 74 */ - -/* Per-channel register */ -#define DDADR 0x00 -#define DSADR 0x01 -#define DTADR 0x02 -#define DCMD 0x03 - -/* Bit-field masks */ -#define DRCMR_CHLNUM 0x1f -#define DRCMR_MAPVLD (1 << 7) -#define DDADR_STOP (1 << 0) -#define DDADR_BREN (1 << 1) -#define DCMD_LEN 0x1fff -#define DCMD_WIDTH(x) (1 << ((((x) >> 14) & 3) - 1)) -#define DCMD_SIZE(x) (4 << (((x) >> 16) & 3)) -#define DCMD_FLYBYT (1 << 19) -#define DCMD_FLYBYS (1 << 20) -#define DCMD_ENDIRQEN (1 << 21) -#define DCMD_STARTIRQEN (1 << 22) -#define DCMD_CMPEN (1 << 25) -#define DCMD_FLOWTRG (1 << 28) -#define DCMD_FLOWSRC (1 << 29) -#define DCMD_INCTRGADDR (1 << 30) -#define DCMD_INCSRCADDR (1 << 31) -#define DCSR_BUSERRINTR (1 << 0) -#define DCSR_STARTINTR (1 << 1) -#define DCSR_ENDINTR (1 << 2) -#define DCSR_STOPINTR (1 << 3) -#define DCSR_RASINTR (1 << 4) -#define DCSR_REQPEND (1 << 8) -#define DCSR_EORINT (1 << 9) -#define DCSR_CMPST (1 << 10) -#define DCSR_MASKRUN (1 << 22) -#define DCSR_RASIRQEN (1 << 23) -#define DCSR_CLRCMPST (1 << 24) -#define DCSR_SETCMPST (1 << 25) -#define DCSR_EORSTOPEN (1 << 26) -#define DCSR_EORJMPEN (1 << 27) -#define DCSR_EORIRQEN (1 << 28) -#define DCSR_STOPIRQEN (1 << 29) -#define DCSR_NODESCFETCH (1 << 30) -#define DCSR_RUN (1 << 31) - -static inline void pxa2xx_dma_update(PXA2xxDMAState *s, int ch) -{ - if (ch >= 0) { - if ((s->chan[ch].state & DCSR_STOPIRQEN) && - (s->chan[ch].state & DCSR_STOPINTR)) - s->stopintr |= 1 << ch; - else - s->stopintr &= ~(1 << ch); - - if ((s->chan[ch].state & DCSR_EORIRQEN) && - (s->chan[ch].state & DCSR_EORINT)) - s->eorintr |= 1 << ch; - else - s->eorintr &= ~(1 << ch); - - if ((s->chan[ch].state & DCSR_RASIRQEN) && - (s->chan[ch].state & DCSR_RASINTR)) - s->rasintr |= 1 << ch; - else - s->rasintr &= ~(1 << ch); - - if (s->chan[ch].state & DCSR_STARTINTR) - s->startintr |= 1 << ch; - else - s->startintr &= ~(1 << ch); - - if (s->chan[ch].state & DCSR_ENDINTR) - s->endintr |= 1 << ch; - else - s->endintr &= ~(1 << ch); - } - - if (s->stopintr | s->eorintr | s->rasintr | s->startintr | s->endintr) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); -} - -static inline void pxa2xx_dma_descriptor_fetch( - PXA2xxDMAState *s, int ch) -{ - uint32_t desc[4]; - hwaddr daddr = s->chan[ch].descr & ~0xf; - if ((s->chan[ch].descr & DDADR_BREN) && (s->chan[ch].state & DCSR_CMPST)) - daddr += 32; - - cpu_physical_memory_read(daddr, desc, 16); - s->chan[ch].descr = desc[DDADR]; - s->chan[ch].src = desc[DSADR]; - s->chan[ch].dest = desc[DTADR]; - s->chan[ch].cmd = desc[DCMD]; - - if (s->chan[ch].cmd & DCMD_FLOWSRC) - s->chan[ch].src &= ~3; - if (s->chan[ch].cmd & DCMD_FLOWTRG) - s->chan[ch].dest &= ~3; - - if (s->chan[ch].cmd & (DCMD_CMPEN | DCMD_FLYBYS | DCMD_FLYBYT)) - printf("%s: unsupported mode in channel %i\n", __func__, ch); - - if (s->chan[ch].cmd & DCMD_STARTIRQEN) - s->chan[ch].state |= DCSR_STARTINTR; -} - -static void pxa2xx_dma_run(PXA2xxDMAState *s) -{ - int c, srcinc, destinc; - uint32_t n, size; - uint32_t width; - uint32_t length; - uint8_t buffer[32]; - PXA2xxDMAChannel *ch; - - if (s->running ++) - return; - - while (s->running) { - s->running = 1; - for (c = 0; c < s->channels; c ++) { - ch = &s->chan[c]; - - while ((ch->state & DCSR_RUN) && !(ch->state & DCSR_STOPINTR)) { - /* Test for pending requests */ - if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && !ch->request) - break; - - length = ch->cmd & DCMD_LEN; - size = DCMD_SIZE(ch->cmd); - width = DCMD_WIDTH(ch->cmd); - - srcinc = (ch->cmd & DCMD_INCSRCADDR) ? width : 0; - destinc = (ch->cmd & DCMD_INCTRGADDR) ? width : 0; - - while (length) { - size = MIN(length, size); - - for (n = 0; n < size; n += width) { - cpu_physical_memory_read(ch->src, buffer + n, width); - ch->src += srcinc; - } - - for (n = 0; n < size; n += width) { - cpu_physical_memory_write(ch->dest, buffer + n, width); - ch->dest += destinc; - } - - length -= size; - - if ((ch->cmd & (DCMD_FLOWSRC | DCMD_FLOWTRG)) && - !ch->request) { - ch->state |= DCSR_EORINT; - if (ch->state & DCSR_EORSTOPEN) - ch->state |= DCSR_STOPINTR; - if ((ch->state & DCSR_EORJMPEN) && - !(ch->state & DCSR_NODESCFETCH)) - pxa2xx_dma_descriptor_fetch(s, c); - break; - } - } - - ch->cmd = (ch->cmd & ~DCMD_LEN) | length; - - /* Is the transfer complete now? */ - if (!length) { - if (ch->cmd & DCMD_ENDIRQEN) - ch->state |= DCSR_ENDINTR; - - if ((ch->state & DCSR_NODESCFETCH) || - (ch->descr & DDADR_STOP) || - (ch->state & DCSR_EORSTOPEN)) { - ch->state |= DCSR_STOPINTR; - ch->state &= ~DCSR_RUN; - - break; - } - - ch->state |= DCSR_STOPINTR; - break; - } - } - } - - s->running --; - } -} - -static uint64_t pxa2xx_dma_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - unsigned int channel; - - if (size != 4) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad access width %u\n", - __func__, size); - return 5; - } - - switch (offset) { - case DRCMR64 ... DRCMR74: - offset -= DRCMR64 - DRCMR0 - (64 << 2); - /* Fall through */ - case DRCMR0 ... DRCMR63: - channel = (offset - DRCMR0) >> 2; - return s->req[channel]; - - case DRQSR0: - case DRQSR1: - case DRQSR2: - return 0; - - case DCSR0 ... DCSR31: - channel = offset >> 2; - if (s->chan[channel].request) - return s->chan[channel].state | DCSR_REQPEND; - return s->chan[channel].state; - - case DINT: - return s->stopintr | s->eorintr | s->rasintr | - s->startintr | s->endintr; - - case DALGN: - return s->align; - - case DPCSR: - return s->pio; - } - - if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { - channel = (offset - D_CH0) >> 4; - switch ((offset & 0x0f) >> 2) { - case DDADR: - return s->chan[channel].descr; - case DSADR: - return s->chan[channel].src; - case DTADR: - return s->chan[channel].dest; - case DCMD: - return s->chan[channel].cmd; - } - } - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", - __func__, offset); - return 7; -} - -static void pxa2xx_dma_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxDMAState *s = (PXA2xxDMAState *) opaque; - unsigned int channel; - - if (size != 4) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad access width %u\n", - __func__, size); - return; - } - - switch (offset) { - case DRCMR64 ... DRCMR74: - offset -= DRCMR64 - DRCMR0 - (64 << 2); - /* Fall through */ - case DRCMR0 ... DRCMR63: - channel = (offset - DRCMR0) >> 2; - - if (value & DRCMR_MAPVLD) - if ((value & DRCMR_CHLNUM) > s->channels) - hw_error("%s: Bad DMA channel %i\n", - __func__, (unsigned)value & DRCMR_CHLNUM); - - s->req[channel] = value; - break; - - case DRQSR0: - case DRQSR1: - case DRQSR2: - /* Nothing to do */ - break; - - case DCSR0 ... DCSR31: - channel = offset >> 2; - s->chan[channel].state &= 0x0000071f & ~(value & - (DCSR_EORINT | DCSR_ENDINTR | - DCSR_STARTINTR | DCSR_BUSERRINTR)); - s->chan[channel].state |= value & 0xfc800000; - - if (s->chan[channel].state & DCSR_STOPIRQEN) - s->chan[channel].state &= ~DCSR_STOPINTR; - - if (value & DCSR_NODESCFETCH) { - /* No-descriptor-fetch mode */ - if (value & DCSR_RUN) { - s->chan[channel].state &= ~DCSR_STOPINTR; - pxa2xx_dma_run(s); - } - } else { - /* Descriptor-fetch mode */ - if (value & DCSR_RUN) { - s->chan[channel].state &= ~DCSR_STOPINTR; - pxa2xx_dma_descriptor_fetch(s, channel); - pxa2xx_dma_run(s); - } - } - - /* Shouldn't matter as our DMA is synchronous. */ - if (!(value & (DCSR_RUN | DCSR_MASKRUN))) - s->chan[channel].state |= DCSR_STOPINTR; - - if (value & DCSR_CLRCMPST) - s->chan[channel].state &= ~DCSR_CMPST; - if (value & DCSR_SETCMPST) - s->chan[channel].state |= DCSR_CMPST; - - pxa2xx_dma_update(s, channel); - break; - - case DALGN: - s->align = value; - break; - - case DPCSR: - s->pio = value & 0x80000001; - break; - - default: - if (offset >= D_CH0 && offset < D_CH0 + (s->channels << 4)) { - channel = (offset - D_CH0) >> 4; - switch ((offset & 0x0f) >> 2) { - case DDADR: - s->chan[channel].descr = value; - break; - case DSADR: - s->chan[channel].src = value; - break; - case DTADR: - s->chan[channel].dest = value; - break; - case DCMD: - s->chan[channel].cmd = value; - break; - default: - goto fail; - } - - break; - } - fail: - qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", - __func__, offset); - } -} - -static const MemoryRegionOps pxa2xx_dma_ops = { - .read = pxa2xx_dma_read, - .write = pxa2xx_dma_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void pxa2xx_dma_request(void *opaque, int req_num, int on) -{ - PXA2xxDMAState *s = opaque; - int ch; - if (req_num < 0 || req_num >= PXA2XX_DMA_NUM_REQUESTS) - hw_error("%s: Bad DMA request %i\n", __func__, req_num); - - if (!(s->req[req_num] & DRCMR_MAPVLD)) - return; - ch = s->req[req_num] & DRCMR_CHLNUM; - - if (!s->chan[ch].request && on) - s->chan[ch].state |= DCSR_RASINTR; - else - s->chan[ch].state &= ~DCSR_RASINTR; - if (s->chan[ch].request && !on) - s->chan[ch].state |= DCSR_EORINT; - - s->chan[ch].request = on; - if (on) { - pxa2xx_dma_run(s); - pxa2xx_dma_update(s, ch); - } -} - -static void pxa2xx_dma_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - PXA2xxDMAState *s = PXA2XX_DMA(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); - - qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_dma_ops, s, - "pxa2xx.dma", 0x00010000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); -} - -static void pxa2xx_dma_realize(DeviceState *dev, Error **errp) -{ - PXA2xxDMAState *s = PXA2XX_DMA(dev); - int i; - - if (s->channels <= 0) { - error_setg(errp, "channels value invalid"); - return; - } - - s->chan = g_new0(PXA2xxDMAChannel, s->channels); - - for (i = 0; i < s->channels; i ++) - s->chan[i].state = DCSR_STOPINTR; -} - -DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_new("pxa2xx-dma"); - qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); - 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, irq); - - return dev; -} - -DeviceState *pxa255_dma_init(hwaddr base, qemu_irq irq) -{ - DeviceState *dev; - - dev = qdev_new("pxa2xx-dma"); - qdev_prop_set_int32(dev, "channels", PXA27X_DMA_NUM_CHANNELS); - 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, irq); - - return dev; -} - -static bool is_version_0(void *opaque, int version_id) -{ - return version_id == 0; -} - -static const VMStateDescription vmstate_pxa2xx_dma_chan = { - .name = "pxa2xx_dma_chan", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(descr, PXA2xxDMAChannel), - VMSTATE_UINT32(src, PXA2xxDMAChannel), - VMSTATE_UINT32(dest, PXA2xxDMAChannel), - VMSTATE_UINT32(cmd, PXA2xxDMAChannel), - VMSTATE_UINT32(state, PXA2xxDMAChannel), - VMSTATE_INT32(request, PXA2xxDMAChannel), - VMSTATE_END_OF_LIST(), - }, -}; - -static const VMStateDescription vmstate_pxa2xx_dma = { - .name = "pxa2xx_dma", - .version_id = 1, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UNUSED_TEST(is_version_0, 4), - VMSTATE_UINT32(stopintr, PXA2xxDMAState), - VMSTATE_UINT32(eorintr, PXA2xxDMAState), - VMSTATE_UINT32(rasintr, PXA2xxDMAState), - VMSTATE_UINT32(startintr, PXA2xxDMAState), - VMSTATE_UINT32(endintr, PXA2xxDMAState), - VMSTATE_UINT32(align, PXA2xxDMAState), - VMSTATE_UINT32(pio, PXA2xxDMAState), - VMSTATE_BUFFER(req, PXA2xxDMAState), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(chan, PXA2xxDMAState, channels, - vmstate_pxa2xx_dma_chan, PXA2xxDMAChannel), - VMSTATE_END_OF_LIST(), - }, -}; - -static Property pxa2xx_dma_properties[] = { - DEFINE_PROP_INT32("channels", PXA2xxDMAState, channels, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa2xx_dma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA2xx DMA controller"; - dc->vmsd = &vmstate_pxa2xx_dma; - device_class_set_props(dc, pxa2xx_dma_properties); - dc->realize = pxa2xx_dma_realize; -} - -static const TypeInfo pxa2xx_dma_info = { - .name = TYPE_PXA2XX_DMA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxDMAState), - .instance_init = pxa2xx_dma_init, - .class_init = pxa2xx_dma_class_init, -}; - -static void pxa2xx_dma_register_types(void) -{ - type_register_static(&pxa2xx_dma_info); -} - -type_init(pxa2xx_dma_register_types) diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index aa1d323a36..5bf54347ed 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), @@ -707,7 +707,7 @@ static void rc4030_class_init(ObjectClass *klass, void *class_data) dc->realize = rc4030_realize; dc->unrealize = rc4030_unrealize; - dc->reset = rc4030_reset; + device_class_set_legacy_reset(dc, rc4030_reset); dc->vmsd = &vmstate_rc4030; } diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 3a430057f5..d5c52b804f 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -209,9 +209,9 @@ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) dma->enabled_count += level - ch->enable; if (level) - dma->ch_enable_mask |= 1 << ch->num; + dma->ch_enable_mask |= (uint64_t)1 << ch->num; else - dma->ch_enable_mask &= ~(1 << ch->num); + dma->ch_enable_mask &= ~((uint64_t)1 << ch->num); if (level != ch->enable) { soc_dma_ch_freq_update(dma); diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 0ef13c5e9a..9fdba16603 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() } @@ -278,7 +278,7 @@ static void sparc32_dma_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = sparc32_dma_device_reset; + device_class_set_legacy_reset(dc, sparc32_dma_device_reset); dc->vmsd = &vmstate_sparc32_dma_device; } diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 3c47df54e4..4c09790f9a 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -44,3 +44,6 @@ pl330_debug_exec_stall(void) "stall of debug instruction not implemented" pl330_iomem_write(uint32_t offset, uint32_t value) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 pl330_iomem_write_clr(int i) "event interrupt lowered %d" pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08"PRIx32 + +# xilinx_axidma.c +xilinx_axidma_loading_desc_fail(uint32_t res) "error:%u" diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 99e50f65e2..73a480bfbf 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -36,6 +36,7 @@ #include "sysemu/dma.h" #include "hw/stream.h" #include "qom/object.h" +#include "trace.h" #define D(x) @@ -71,8 +72,11 @@ enum { enum { DMASR_HALTED = 1, DMASR_IDLE = 2, + DMASR_SLVERR = 1 << 5, + DMASR_DECERR = 1 << 6, DMASR_IOC_IRQ = 1 << 12, DMASR_DLY_IRQ = 1 << 13, + DMASR_ERR_IRQ = 1 << 14, DMASR_IRQ_MASK = 7 << 12 }; @@ -190,17 +194,34 @@ static inline int streamid_from_addr(hwaddr addr) return sid; } -static void stream_desc_load(struct Stream *s, hwaddr addr) +static MemTxResult stream_desc_load(struct Stream *s, hwaddr addr) { struct SDesc *d = &s->desc; - address_space_read(&s->dma->as, addr, MEMTXATTRS_UNSPECIFIED, d, sizeof *d); + MemTxResult result = address_space_read(&s->dma->as, + addr, MEMTXATTRS_UNSPECIFIED, + d, sizeof *d); + if (result != MEMTX_OK) { + trace_xilinx_axidma_loading_desc_fail(result); + + if (result == MEMTX_DECODE_ERROR) { + s->regs[R_DMASR] |= DMASR_DECERR; + } else { + s->regs[R_DMASR] |= DMASR_SLVERR; + } + + s->regs[R_DMACR] &= ~DMACR_RUNSTOP; + s->regs[R_DMASR] |= DMASR_HALTED; + s->regs[R_DMASR] |= DMASR_ERR_IRQ; + return result; + } /* Convert from LE into host endianness. */ d->buffer_address = le64_to_cpu(d->buffer_address); d->nxtdesc = le64_to_cpu(d->nxtdesc); d->control = le32_to_cpu(d->control); d->status = le32_to_cpu(d->status); + return result; } static void stream_desc_store(struct Stream *s, hwaddr addr) @@ -279,7 +300,9 @@ static void stream_process_mem2s(struct Stream *s, StreamSink *tx_data_dev, } while (1) { - stream_desc_load(s, s->regs[R_CURDESC]); + if (MEMTX_OK != stream_desc_load(s, s->regs[R_CURDESC])) { + break; + } if (s->desc.status & SDESC_STATUS_COMPLETE) { s->regs[R_DMASR] |= DMASR_HALTED; @@ -336,7 +359,9 @@ static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, } while (len) { - stream_desc_load(s, s->regs[R_CURDESC]); + if (MEMTX_OK != stream_desc_load(s, s->regs[R_CURDESC])) { + break; + } if (s->desc.status & SDESC_STATUS_COMPLETE) { s->regs[R_DMASR] |= DMASR_HALTED; @@ -461,7 +486,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; } @@ -514,7 +539,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; @@ -577,10 +602,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); @@ -596,6 +617,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(), }; @@ -603,8 +626,8 @@ static void axidma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = xilinx_axidma_realize, - dc->reset = xilinx_axidma_reset; + dc->realize = xilinx_axidma_realize; + device_class_set_legacy_reset(dc, xilinx_axidma_reset); device_class_set_props(dc, axidma_properties); } diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index 4eb7f66e9f..46f50631ff 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(), }; @@ -824,7 +821,7 @@ static void zdma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = zdma_reset; + device_class_set_legacy_reset(dc, zdma_reset); dc->realize = zdma_realize; device_class_set_props(dc, zdma_props); dc->vmsd = &vmstate_zdma; diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c index f5ad1a0d22..b8544d0731 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, @@ -384,7 +384,7 @@ static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xlnx_zynq_devcfg_reset; + device_class_set_legacy_reset(dc, xlnx_zynq_devcfg_reset); dc->vmsd = &vmstate_xlnx_zynq_devcfg; } diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 1ce52ea5a2..43738c4350 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(), }; @@ -711,7 +719,7 @@ static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_CLASS(klass); - dc->reset = xlnx_csu_dma_reset; + device_class_set_legacy_reset(dc, xlnx_csu_dma_reset); dc->realize = xlnx_csu_dma_realize; dc->vmsd = &vmstate_xlnx_csu_dma; device_class_set_props(dc, xlnx_csu_dma_properties); @@ -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..2657808d37 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -175,24 +175,24 @@ static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc, switch (frag) { case 0: - addr = desc->source_address - + (extract32(desc->address_extension, 16, 12) << 20); + addr = (uint64_t)desc->source_address + + (extract64(desc->address_extension, 16, 16) << 32); break; case 1: - addr = desc->source_address2 - + (extract32(desc->address_extension_23, 0, 12) << 8); + addr = (uint64_t)desc->source_address2 + + (extract64(desc->address_extension_23, 0, 16) << 32); break; case 2: - addr = desc->source_address3 - + (extract32(desc->address_extension_23, 16, 12) << 20); + addr = (uint64_t)desc->source_address3 + + (extract64(desc->address_extension_23, 16, 16) << 32); break; case 3: - addr = desc->source_address4 - + (extract32(desc->address_extension_45, 0, 12) << 8); + addr = (uint64_t)desc->source_address4 + + (extract64(desc->address_extension_45, 0, 16) << 32); break; case 4: - addr = desc->source_address5 - + (extract32(desc->address_extension_45, 16, 12) << 20); + addr = (uint64_t)desc->source_address5 + + (extract64(desc->address_extension_45, 16, 16) << 32); break; default: addr = 0; @@ -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), @@ -598,7 +598,7 @@ static void xlnx_dpdma_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->vmsd = &vmstate_xlnx_dpdma; - dc->reset = xlnx_dpdma_reset; + device_class_set_legacy_reset(dc, xlnx_dpdma_reset); } static const TypeInfo xlnx_dpdma_info = { @@ -614,6 +614,65 @@ static void xlnx_dpdma_register_types(void) type_register_static(&xlnx_dpdma_info); } +static MemTxResult xlnx_dpdma_read_descriptor(XlnxDPDMAState *s, + uint64_t desc_addr, + DPDMADescriptor *desc) +{ + MemTxResult res = dma_memory_read(&address_space_memory, desc_addr, + desc, sizeof(DPDMADescriptor), + MEMTXATTRS_UNSPECIFIED); + if (res) { + return res; + } + + /* Convert from LE into host endianness. */ + desc->control = le32_to_cpu(desc->control); + desc->descriptor_id = le32_to_cpu(desc->descriptor_id); + desc->xfer_size = le32_to_cpu(desc->xfer_size); + desc->line_size_stride = le32_to_cpu(desc->line_size_stride); + desc->timestamp_lsb = le32_to_cpu(desc->timestamp_lsb); + desc->timestamp_msb = le32_to_cpu(desc->timestamp_msb); + desc->address_extension = le32_to_cpu(desc->address_extension); + desc->next_descriptor = le32_to_cpu(desc->next_descriptor); + desc->source_address = le32_to_cpu(desc->source_address); + desc->address_extension_23 = le32_to_cpu(desc->address_extension_23); + desc->address_extension_45 = le32_to_cpu(desc->address_extension_45); + desc->source_address2 = le32_to_cpu(desc->source_address2); + desc->source_address3 = le32_to_cpu(desc->source_address3); + desc->source_address4 = le32_to_cpu(desc->source_address4); + desc->source_address5 = le32_to_cpu(desc->source_address5); + desc->crc = le32_to_cpu(desc->crc); + + return res; +} + +static MemTxResult xlnx_dpdma_write_descriptor(uint64_t desc_addr, + DPDMADescriptor *desc) +{ + DPDMADescriptor tmp_desc = *desc; + + /* Convert from host endianness into LE. */ + tmp_desc.control = cpu_to_le32(tmp_desc.control); + tmp_desc.descriptor_id = cpu_to_le32(tmp_desc.descriptor_id); + tmp_desc.xfer_size = cpu_to_le32(tmp_desc.xfer_size); + tmp_desc.line_size_stride = cpu_to_le32(tmp_desc.line_size_stride); + tmp_desc.timestamp_lsb = cpu_to_le32(tmp_desc.timestamp_lsb); + tmp_desc.timestamp_msb = cpu_to_le32(tmp_desc.timestamp_msb); + tmp_desc.address_extension = cpu_to_le32(tmp_desc.address_extension); + tmp_desc.next_descriptor = cpu_to_le32(tmp_desc.next_descriptor); + tmp_desc.source_address = cpu_to_le32(tmp_desc.source_address); + tmp_desc.address_extension_23 = cpu_to_le32(tmp_desc.address_extension_23); + tmp_desc.address_extension_45 = cpu_to_le32(tmp_desc.address_extension_45); + tmp_desc.source_address2 = cpu_to_le32(tmp_desc.source_address2); + tmp_desc.source_address3 = cpu_to_le32(tmp_desc.source_address3); + tmp_desc.source_address4 = cpu_to_le32(tmp_desc.source_address4); + tmp_desc.source_address5 = cpu_to_le32(tmp_desc.source_address5); + tmp_desc.crc = cpu_to_le32(tmp_desc.crc); + + return dma_memory_write(&address_space_memory, desc_addr, &tmp_desc, + sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); +} + size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, bool one_desc) { @@ -651,8 +710,7 @@ size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, desc_addr = xlnx_dpdma_descriptor_next_address(s, channel); } - if (dma_memory_read(&address_space_memory, desc_addr, &desc, - sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED)) { + if (xlnx_dpdma_read_descriptor(s, desc_addr, &desc)) { s->registers[DPDMA_EISR] |= ((1 << 1) << channel); xlnx_dpdma_update_irq(s); s->operation_finished[channel] = true; @@ -755,8 +813,10 @@ size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel, /* The descriptor need to be updated when it's completed. */ DPRINTF("update the descriptor with the done flag set.\n"); xlnx_dpdma_desc_set_done(&desc); - dma_memory_write(&address_space_memory, desc_addr, &desc, - sizeof(DPDMADescriptor), MEMTXATTRS_UNSPECIFIED); + if (xlnx_dpdma_write_descriptor(desc_addr, &desc)) { + DPRINTF("Can't write the descriptor.\n"); + /* TODO: check hardware behaviour for memory write failure */ + } } if (xlnx_dpdma_desc_completion_interrupt(&desc)) { 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..0e2cc143f1 --- /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; + device_class_set_legacy_reset(dc, 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..50fb1cd467 --- /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; + device_class_set_legacy_reset(dc, 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..83ddb17ae6 --- /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"; + device_class_set_legacy_reset(dc, 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..4f87b28a22 --- /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; + device_class_set_legacy_reset(dc, 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 d2cf3accc8..c423e10f59 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -1,7 +1,3 @@ -config MAX7310 - bool - depends on I2C - config PL061 bool @@ -16,3 +12,13 @@ config GPIO_PWR config SIFIVE_GPIO bool + +config STM32L4X5_GPIO + bool + +config PCF8574 + bool + depends on I2C + +config ZAURUS_SCOOP + bool diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index 1e267dd482..a5b3f454e8 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -227,6 +227,38 @@ REG32(GPIO_INDEX_REG, 0x2AC) FIELD(GPIO_INDEX_REG, COMMAND_SRC_1, 21, 1) FIELD(GPIO_INDEX_REG, INPUT_MASK, 20, 1) +/* AST2700 GPIO Register Address Offsets */ +REG32(GPIO_2700_DEBOUNCE_TIME_1, 0x000) +REG32(GPIO_2700_DEBOUNCE_TIME_2, 0x004) +REG32(GPIO_2700_DEBOUNCE_TIME_3, 0x008) +REG32(GPIO_2700_INT_STATUS_1, 0x100) +REG32(GPIO_2700_INT_STATUS_2, 0x104) +REG32(GPIO_2700_INT_STATUS_3, 0x108) +REG32(GPIO_2700_INT_STATUS_4, 0x10C) +REG32(GPIO_2700_INT_STATUS_5, 0x110) +REG32(GPIO_2700_INT_STATUS_6, 0x114) +REG32(GPIO_2700_INT_STATUS_7, 0x118) +/* GPIOA0 - GPIOAA7 Control Register */ +REG32(GPIO_A0_CONTROL, 0x180) + SHARED_FIELD(GPIO_CONTROL_OUT_DATA, 0, 1) + SHARED_FIELD(GPIO_CONTROL_DIRECTION, 1, 1) + SHARED_FIELD(GPIO_CONTROL_INT_ENABLE, 2, 1) + SHARED_FIELD(GPIO_CONTROL_INT_SENS_0, 3, 1) + SHARED_FIELD(GPIO_CONTROL_INT_SENS_1, 4, 1) + SHARED_FIELD(GPIO_CONTROL_INT_SENS_2, 5, 1) + SHARED_FIELD(GPIO_CONTROL_RESET_TOLERANCE, 6, 1) + SHARED_FIELD(GPIO_CONTROL_DEBOUNCE_1, 7, 1) + SHARED_FIELD(GPIO_CONTROL_DEBOUNCE_2, 8, 1) + SHARED_FIELD(GPIO_CONTROL_INPUT_MASK, 9, 1) + SHARED_FIELD(GPIO_CONTROL_BLINK_COUNTER_1, 10, 1) + SHARED_FIELD(GPIO_CONTROL_BLINK_COUNTER_2, 11, 1) + SHARED_FIELD(GPIO_CONTROL_INT_STATUS, 12, 1) + SHARED_FIELD(GPIO_CONTROL_IN_DATA, 13, 1) + SHARED_FIELD(GPIO_CONTROL_RESERVED, 14, 18) +REG32(GPIO_AA7_CONTROL, 0x4DC) +#define GPIO_2700_MEM_SIZE 0x4E0 +#define GPIO_2700_REG_ARRAY_SIZE (GPIO_2700_MEM_SIZE >> 2) + static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio) { uint32_t falling_edge = 0, rising_edge = 0; @@ -281,7 +313,7 @@ static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs, diff &= mode_mask; if (diff) { for (gpio = 0; gpio < ASPEED_GPIOS_PER_SET; gpio++) { - uint32_t mask = 1 << gpio; + uint32_t mask = 1U << gpio; /* If the gpio needs to be updated... */ if (!(diff & mask)) { @@ -340,7 +372,8 @@ 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, ~s->sets[set_idx].direction); + aspeed_gpio_update(s, &s->sets[set_idx], value, + ~s->sets[set_idx].direction); } /* @@ -559,6 +592,12 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size) return debounce_value; } + if (idx >= agc->reg_table_count) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: idx 0x%" PRIx64 " out of bounds\n", + __func__, idx); + return 0; + } + reg = &agc->reg_table[idx]; if (reg->set_idx >= agc->nr_gpio_sets) { qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" @@ -623,7 +662,6 @@ static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size) static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, uint64_t data, uint32_t size) { - AspeedGPIOState *s = ASPEED_GPIO(opaque); AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); const GPIOSetProperties *props; @@ -635,7 +673,7 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, uint32_t pin_idx = reg_idx_number % ASPEED_GPIOS_PER_SET; uint32_t group_idx = pin_idx / GPIOS_PER_GROUP; uint32_t reg_value = 0; - uint32_t cleared; + uint32_t pending = 0; set = &s->sets[set_idx]; props = &agc->props[set_idx]; @@ -697,16 +735,23 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, FIELD_EX32(data, GPIO_INDEX_REG, INT_SENS_2)); set->int_sens_2 = update_value_control_source(set, set->int_sens_2, reg_value); - /* set interrupt status */ - reg_value = set->int_status; - reg_value = deposit32(reg_value, pin_idx, 1, - FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS)); - cleared = ctpop32(reg_value & set->int_status); - if (s->pending && cleared) { - assert(s->pending >= cleared); - s->pending -= cleared; + /* interrupt status */ + if (FIELD_EX32(data, GPIO_INDEX_REG, INT_STATUS)) { + /* pending is either 1 or 0 for a 1-bit field */ + pending = extract32(set->int_status, pin_idx, 1); + + assert(s->pending >= pending); + + /* No change to s->pending if pending is 0 */ + s->pending -= pending; + + /* + * The write acknowledged the interrupt regardless of whether it + * was pending or not. The post-condition is that it mustn't be + * pending. Unconditionally clear the status bit. + */ + set->int_status = deposit32(set->int_status, pin_idx, 1, 0); } - set->int_status &= ~reg_value; break; case gpio_reg_idx_debounce: reg_value = set->debounce_1; @@ -785,6 +830,12 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, return; } + if (idx >= agc->reg_table_count) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: idx 0x%" PRIx64 " out of bounds\n", + __func__, idx); + return; + } + reg = &agc->reg_table[idx]; if (reg->set_idx >= agc->nr_gpio_sets) { qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" @@ -951,7 +1002,317 @@ static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name, aspeed_gpio_set_pin_level(s, set_idx, pin, level); } -/****************** Setup functions ******************/ +static uint64_t aspeed_gpio_2700_read_control_reg(AspeedGPIOState *s, + uint32_t pin) +{ + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + GPIOSets *set; + uint64_t value = 0; + uint32_t set_idx; + uint32_t pin_idx; + + set_idx = pin / ASPEED_GPIOS_PER_SET; + pin_idx = pin % ASPEED_GPIOS_PER_SET; + + if (set_idx >= agc->nr_gpio_sets) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: set index: %d, out of bounds\n", + __func__, set_idx); + return 0; + } + + set = &s->sets[set_idx]; + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_OUT_DATA, + extract32(set->data_read, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_DIRECTION, + extract32(set->direction, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_ENABLE, + extract32(set->int_enable, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_SENS_0, + extract32(set->int_sens_0, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_SENS_1, + extract32(set->int_sens_1, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_SENS_2, + extract32(set->int_sens_2, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_RESET_TOLERANCE, + extract32(set->reset_tol, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_DEBOUNCE_1, + extract32(set->debounce_1, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_DEBOUNCE_2, + extract32(set->debounce_2, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INPUT_MASK, + extract32(set->input_mask, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_INT_STATUS, + extract32(set->int_status, pin_idx, 1)); + value = SHARED_FIELD_DP32(value, GPIO_CONTROL_IN_DATA, + extract32(set->data_value, pin_idx, 1)); + return value; +} + +static void aspeed_gpio_2700_write_control_reg(AspeedGPIOState *s, + uint32_t pin, uint64_t data) +{ + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + const GPIOSetProperties *props; + GPIOSets *set; + uint32_t set_idx; + uint32_t pin_idx; + uint32_t group_value = 0; + uint32_t pending = 0; + + set_idx = pin / ASPEED_GPIOS_PER_SET; + pin_idx = pin % ASPEED_GPIOS_PER_SET; + + if (set_idx >= agc->nr_gpio_sets) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: set index: %d, out of bounds\n", + __func__, set_idx); + return; + } + + set = &s->sets[set_idx]; + props = &agc->props[set_idx]; + + /* direction */ + group_value = set->direction; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_DIRECTION)); + /* + * where data is the value attempted to be written to the pin: + * pin type | input mask | output mask | expected value + * ------------------------------------------------------------ + * bidirectional | 1 | 1 | data + * input only | 1 | 0 | 0 + * output only | 0 | 1 | 1 + * no pin | 0 | 0 | 0 + * + * which is captured by: + * data = ( data | ~input) & output; + */ + group_value = (group_value | ~props->input) & props->output; + set->direction = update_value_control_source(set, set->direction, + group_value); + + /* out data */ + group_value = set->data_read; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_OUT_DATA)); + group_value &= props->output; + group_value = update_value_control_source(set, set->data_read, + group_value); + set->data_read = group_value; + + /* interrupt enable */ + group_value = set->int_enable; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_ENABLE)); + set->int_enable = update_value_control_source(set, set->int_enable, + group_value); + + /* interrupt sensitivity type 0 */ + group_value = set->int_sens_0; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_SENS_0)); + set->int_sens_0 = update_value_control_source(set, set->int_sens_0, + group_value); + + /* interrupt sensitivity type 1 */ + group_value = set->int_sens_1; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_SENS_1)); + set->int_sens_1 = update_value_control_source(set, set->int_sens_1, + group_value); + + /* interrupt sensitivity type 2 */ + group_value = set->int_sens_2; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_SENS_2)); + set->int_sens_2 = update_value_control_source(set, set->int_sens_2, + group_value); + + /* reset tolerance enable */ + group_value = set->reset_tol; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_RESET_TOLERANCE)); + set->reset_tol = update_value_control_source(set, set->reset_tol, + group_value); + + /* debounce 1 */ + group_value = set->debounce_1; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_DEBOUNCE_1)); + set->debounce_1 = update_value_control_source(set, set->debounce_1, + group_value); + + /* debounce 2 */ + group_value = set->debounce_2; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_DEBOUNCE_2)); + set->debounce_2 = update_value_control_source(set, set->debounce_2, + group_value); + + /* input mask */ + group_value = set->input_mask; + group_value = deposit32(group_value, pin_idx, 1, + SHARED_FIELD_EX32(data, GPIO_CONTROL_INPUT_MASK)); + /* + * feeds into interrupt generation + * 0: read from data value reg will be updated + * 1: read from data value reg will not be updated + */ + set->input_mask = group_value & props->input; + + /* blink counter 1 */ + /* blink counter 2 */ + /* unimplement */ + + /* interrupt status */ + if (SHARED_FIELD_EX32(data, GPIO_CONTROL_INT_STATUS)) { + /* pending is either 1 or 0 for a 1-bit field */ + pending = extract32(set->int_status, pin_idx, 1); + + assert(s->pending >= pending); + + /* No change to s->pending if pending is 0 */ + s->pending -= pending; + + /* + * The write acknowledged the interrupt regardless of whether it + * was pending or not. The post-condition is that it mustn't be + * pending. Unconditionally clear the status bit. + */ + set->int_status = deposit32(set->int_status, pin_idx, 1, 0); + } + + aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); + return; +} + +static uint64_t aspeed_gpio_2700_read(void *opaque, hwaddr offset, + uint32_t size) +{ + AspeedGPIOState *s = ASPEED_GPIO(opaque); + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + GPIOSets *set; + uint64_t value; + uint64_t reg; + uint32_t pin; + uint32_t idx; + + reg = offset >> 2; + + if (reg >= agc->reg_table_count) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%" PRIx64 " out of bounds\n", + __func__, offset); + return 0; + } + + switch (reg) { + case R_GPIO_2700_DEBOUNCE_TIME_1 ... R_GPIO_2700_DEBOUNCE_TIME_3: + idx = reg - R_GPIO_2700_DEBOUNCE_TIME_1; + + if (idx >= ASPEED_GPIO_NR_DEBOUNCE_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: debounce index: %d, out of bounds\n", + __func__, idx); + return 0; + } + + value = (uint64_t) s->debounce_regs[idx]; + break; + case R_GPIO_2700_INT_STATUS_1 ... R_GPIO_2700_INT_STATUS_7: + idx = reg - R_GPIO_2700_INT_STATUS_1; + + if (idx >= agc->nr_gpio_sets) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: interrupt status index: %d, out of bounds\n", + __func__, idx); + return 0; + } + + set = &s->sets[idx]; + value = (uint64_t) set->int_status; + break; + case R_GPIO_A0_CONTROL ... R_GPIO_AA7_CONTROL: + pin = reg - R_GPIO_A0_CONTROL; + + if (pin >= agc->nr_gpio_pins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid pin number: %d\n", + __func__, pin); + return 0; + } + + value = aspeed_gpio_2700_read_control_reg(s, pin); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" + PRIx64"\n", __func__, offset); + return 0; + } + + trace_aspeed_gpio_read(offset, value); + return value; +} + +static void aspeed_gpio_2700_write(void *opaque, hwaddr offset, + uint64_t data, uint32_t size) +{ + AspeedGPIOState *s = ASPEED_GPIO(opaque); + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s); + uint64_t reg; + uint32_t pin; + uint32_t idx; + + trace_aspeed_gpio_write(offset, data); + + reg = offset >> 2; + + if (reg >= agc->reg_table_count) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offset 0x%" PRIx64 " out of bounds\n", + __func__, offset); + return; + } + + switch (reg) { + case R_GPIO_2700_DEBOUNCE_TIME_1 ... R_GPIO_2700_DEBOUNCE_TIME_3: + idx = reg - R_GPIO_2700_DEBOUNCE_TIME_1; + + if (idx >= ASPEED_GPIO_NR_DEBOUNCE_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: debounce index: %d out of bounds\n", + __func__, idx); + return; + } + + s->debounce_regs[idx] = (uint32_t) data; + break; + case R_GPIO_A0_CONTROL ... R_GPIO_AA7_CONTROL: + pin = reg - R_GPIO_A0_CONTROL; + + if (pin >= agc->nr_gpio_pins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid pin number: %d\n", + __func__, pin); + return; + } + + if (SHARED_FIELD_EX32(data, GPIO_CONTROL_RESERVED)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid reserved data: 0x%" + PRIx64"\n", __func__, data); + return; + } + + aspeed_gpio_2700_write_control_reg(s, pin, data); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" + PRIx64"\n", __func__, offset); + break; + } + + return; +} + +/* Setup functions */ static const GPIOSetProperties ast2400_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, [1] = {0xffffffff, 0xffffffff, {"E", "F", "G", "H"} }, @@ -997,6 +1358,16 @@ static GPIOSetProperties ast1030_set_props[ASPEED_GPIO_MAX_NR_SETS] = { [5] = {0x000000ff, 0x00000000, {"U"} }, }; +static GPIOSetProperties ast2700_set_props[ASPEED_GPIO_MAX_NR_SETS] = { + [0] = {0xffffffff, 0xffffffff, {"A", "B", "C", "D"} }, + [1] = {0x0fffffff, 0x0fffffff, {"E", "F", "G", "H"} }, + [2] = {0xffffffff, 0xffffffff, {"I", "J", "K", "L"} }, + [3] = {0xffffffff, 0xffffffff, {"M", "N", "O", "P"} }, + [4] = {0xffffffff, 0xffffffff, {"Q", "R", "S", "T"} }, + [5] = {0xffffffff, 0xffffffff, {"U", "V", "W", "X"} }, + [6] = {0x00ffffff, 0x00ffffff, {"Y", "Z", "AA"} }, +}; + static const MemoryRegionOps aspeed_gpio_ops = { .read = aspeed_gpio_read, .write = aspeed_gpio_write, @@ -1005,6 +1376,14 @@ static const MemoryRegionOps aspeed_gpio_ops = { .valid.max_access_size = 4, }; +static const MemoryRegionOps aspeed_gpio_2700_ops = { + .read = aspeed_gpio_2700_read, + .write = aspeed_gpio_2700_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + static void aspeed_gpio_reset(DeviceState *dev) { AspeedGPIOState *s = ASPEED_GPIO(dev); @@ -1034,8 +1413,8 @@ static void aspeed_gpio_realize(DeviceState *dev, Error **errp) } } - memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s, - TYPE_ASPEED_GPIO, 0x800); + memory_region_init_io(&s->iomem, OBJECT(s), agc->reg_ops, s, + TYPE_ASPEED_GPIO, agc->mem_size); sysbus_init_mmio(sbd, &s->iomem); } @@ -1067,7 +1446,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), @@ -1090,7 +1469,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, @@ -1104,7 +1483,7 @@ static void aspeed_gpio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_gpio_realize; - dc->reset = aspeed_gpio_reset; + device_class_set_legacy_reset(dc, aspeed_gpio_reset); dc->desc = "Aspeed GPIO Controller"; dc->vmsd = &vmstate_aspeed_gpio; } @@ -1117,6 +1496,9 @@ static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 216; agc->nr_gpio_sets = 7; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; + agc->mem_size = 0x1000; + agc->reg_ops = &aspeed_gpio_ops; } static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) @@ -1127,6 +1509,9 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 228; agc->nr_gpio_sets = 8; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; + agc->mem_size = 0x1000; + agc->reg_ops = &aspeed_gpio_ops; } static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data) @@ -1137,6 +1522,9 @@ static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 208; agc->nr_gpio_sets = 7; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; + agc->mem_size = 0x800; + agc->reg_ops = &aspeed_gpio_ops; } static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) @@ -1147,6 +1535,9 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 36; agc->nr_gpio_sets = 2; agc->reg_table = aspeed_1_8v_gpios; + agc->reg_table_count = GPIO_1_8V_REG_ARRAY_SIZE; + agc->mem_size = 0x800; + agc->reg_ops = &aspeed_gpio_ops; } static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) @@ -1157,6 +1548,21 @@ static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) agc->nr_gpio_pins = 151; agc->nr_gpio_sets = 6; agc->reg_table = aspeed_3_3v_gpios; + agc->reg_table_count = GPIO_3_3V_REG_ARRAY_SIZE; + agc->mem_size = 0x1000; + agc->reg_ops = &aspeed_gpio_ops; +} + +static void aspeed_gpio_2700_class_init(ObjectClass *klass, void *data) +{ + AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); + + agc->props = ast2700_set_props; + agc->nr_gpio_pins = 216; + agc->nr_gpio_sets = 7; + agc->reg_table_count = GPIO_2700_REG_ARRAY_SIZE; + agc->mem_size = 0x1000; + agc->reg_ops = &aspeed_gpio_2700_ops; } static const TypeInfo aspeed_gpio_info = { @@ -1203,6 +1609,13 @@ static const TypeInfo aspeed_gpio_ast1030_info = { .instance_init = aspeed_gpio_init, }; +static const TypeInfo aspeed_gpio_ast2700_info = { + .name = TYPE_ASPEED_GPIO "-ast2700", + .parent = TYPE_ASPEED_GPIO, + .class_init = aspeed_gpio_2700_class_init, + .instance_init = aspeed_gpio_init, +}; + static void aspeed_gpio_register_types(void) { type_register_static(&aspeed_gpio_info); @@ -1211,6 +1624,7 @@ static void aspeed_gpio_register_types(void) type_register_static(&aspeed_gpio_ast2600_3_3v_info); type_register_static(&aspeed_gpio_ast2600_1_8v_info); type_register_static(&aspeed_gpio_ast1030_info); + type_register_static(&aspeed_gpio_ast2700_info); } type_init(aspeed_gpio_register_types); diff --git a/hw/gpio/bcm2835_gpio.c b/hw/gpio/bcm2835_gpio.c index c995bba1d9..5a5f1df5e8 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), @@ -325,7 +325,7 @@ static void bcm2835_gpio_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_bcm2835_gpio; dc->realize = &bcm2835_gpio_realize; - dc->reset = &bcm2835_gpio_reset; + device_class_set_legacy_reset(dc, bcm2835_gpio_reset); } static const TypeInfo bcm2835_gpio_info = { diff --git a/hw/gpio/bcm2838_gpio.c b/hw/gpio/bcm2838_gpio.c new file mode 100644 index 0000000000..0a1739fc46 --- /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; + device_class_set_legacy_reset(dc, 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..2fcab9ead6 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() } @@ -91,7 +91,7 @@ static void gpio_key_class_init(ObjectClass *klass, void *data) dc->realize = gpio_key_realize; dc->vmsd = &vmstate_gpio_key; - dc->reset = &gpio_key_reset; + device_class_set_legacy_reset(dc, gpio_key_reset); } static const TypeInfo gpio_key_info = { diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index c7f98b7bb1..27535a577f 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), @@ -333,7 +333,7 @@ static void imx_gpio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx_gpio_realize; - dc->reset = imx_gpio_reset; + device_class_set_legacy_reset(dc, imx_gpio_reset); device_class_set_props(dc, imx_gpio_properties); dc->vmsd = &vmstate_imx_gpio; dc->desc = "i.MX GPIO controller"; diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c deleted file mode 100644 index db6b5e3d76..0000000000 --- a/hw/gpio/max7310.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * MAX7310 8-port GPIO expansion chip. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This file is licensed under GNU GPL. - */ - -#include "qemu/osdep.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qom/object.h" - -#define TYPE_MAX7310 "max7310" -OBJECT_DECLARE_SIMPLE_TYPE(MAX7310State, MAX7310) - -struct MAX7310State { - I2CSlave parent_obj; - - int i2c_command_byte; - int len; - - uint8_t level; - uint8_t direction; - uint8_t polarity; - uint8_t status; - uint8_t command; - qemu_irq handler[8]; - qemu_irq *gpio_in; -}; - -static void max7310_reset(DeviceState *dev) -{ - MAX7310State *s = MAX7310(dev); - - s->level &= s->direction; - s->direction = 0xff; - s->polarity = 0xf0; - s->status = 0x01; - s->command = 0x00; -} - -static uint8_t max7310_rx(I2CSlave *i2c) -{ - MAX7310State *s = MAX7310(i2c); - - switch (s->command) { - case 0x00: /* Input port */ - return s->level ^ s->polarity; - - case 0x01: /* Output port */ - return s->level & ~s->direction; - - case 0x02: /* Polarity inversion */ - return s->polarity; - - case 0x03: /* Configuration */ - return s->direction; - - case 0x04: /* Timeout */ - return s->status; - - case 0xff: /* Reserved */ - return 0xff; - - default: - qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n", - __func__, s->command); - break; - } - return 0xff; -} - -static int max7310_tx(I2CSlave *i2c, uint8_t data) -{ - MAX7310State *s = MAX7310(i2c); - uint8_t diff; - int line; - - if (s->len ++ > 1) { -#ifdef VERBOSE - printf("%s: message too long (%i bytes)\n", __func__, s->len); -#endif - return 1; - } - - if (s->i2c_command_byte) { - s->command = data; - s->i2c_command_byte = 0; - return 0; - } - - switch (s->command) { - case 0x01: /* Output port */ - for (diff = (data ^ s->level) & ~s->direction; diff; - diff &= ~(1 << line)) { - line = ctz32(diff); - if (s->handler[line]) - qemu_set_irq(s->handler[line], (data >> line) & 1); - } - s->level = (s->level & s->direction) | (data & ~s->direction); - break; - - case 0x02: /* Polarity inversion */ - s->polarity = data; - break; - - case 0x03: /* Configuration */ - s->level &= ~(s->direction ^ data); - s->direction = data; - break; - - case 0x04: /* Timeout */ - s->status = data; - break; - - case 0x00: /* Input port - ignore writes */ - break; - default: - qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n", - __func__, s->command); - return 1; - } - - return 0; -} - -static int max7310_event(I2CSlave *i2c, enum i2c_event event) -{ - MAX7310State *s = MAX7310(i2c); - s->len = 0; - - switch (event) { - case I2C_START_SEND: - s->i2c_command_byte = 1; - break; - case I2C_FINISH: -#ifdef VERBOSE - if (s->len == 1) - printf("%s: message too short (%i bytes)\n", __func__, s->len); -#endif - break; - default: - break; - } - - return 0; -} - -static const VMStateDescription vmstate_max7310 = { - .name = "max7310", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_INT32(i2c_command_byte, MAX7310State), - VMSTATE_INT32(len, MAX7310State), - VMSTATE_UINT8(level, MAX7310State), - VMSTATE_UINT8(direction, MAX7310State), - VMSTATE_UINT8(polarity, MAX7310State), - VMSTATE_UINT8(status, MAX7310State), - VMSTATE_UINT8(command, MAX7310State), - VMSTATE_I2C_SLAVE(parent_obj, MAX7310State), - VMSTATE_END_OF_LIST() - } -}; - -static void max7310_gpio_set(void *opaque, int line, int level) -{ - MAX7310State *s = (MAX7310State *) opaque; - assert(line >= 0 && line < ARRAY_SIZE(s->handler)); - - if (level) - s->level |= s->direction & (1 << line); - else - s->level &= ~(s->direction & (1 << line)); -} - -/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols), - * 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); -} - -static void max7310_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - dc->realize = max7310_realize; - k->event = max7310_event; - k->recv = max7310_rx; - k->send = max7310_tx; - dc->reset = max7310_reset; - dc->vmsd = &vmstate_max7310; -} - -static const TypeInfo max7310_info = { - .name = TYPE_MAX7310, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(MAX7310State), - .class_init = max7310_class_init, -}; - -static void max7310_register_types(void) -{ - type_register_static(&max7310_info); -} - -type_init(max7310_register_types) diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index b726e6d27a..74840619c0 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -1,14 +1,20 @@ -softmmu_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c')) -softmmu_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.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_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_SCOOP', 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..a3c1d2fbf4 100644 --- a/hw/gpio/mpc8xxx.c +++ b/hw/gpio/mpc8xxx.c @@ -23,7 +23,6 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "qemu/module.h" #include "qom/object.h" #define TYPE_MPC8XXX_GPIO "mpc8xxx_gpio" @@ -48,7 +47,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), @@ -205,20 +204,17 @@ static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_mpc8xxx_gpio; - dc->reset = mpc8xxx_gpio_reset; + device_class_set_legacy_reset(dc, mpc8xxx_gpio_reset); } -static const TypeInfo mpc8xxx_gpio_info = { - .name = TYPE_MPC8XXX_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MPC8XXXGPIOState), - .instance_init = mpc8xxx_gpio_initfn, - .class_init = mpc8xxx_gpio_class_init, +static const TypeInfo mpc8xxx_gpio_types[] = { + { + .name = TYPE_MPC8XXX_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPC8XXXGPIOState), + .instance_init = mpc8xxx_gpio_initfn, + .class_init = mpc8xxx_gpio_class_init, + }, }; -static void mpc8xxx_gpio_register_types(void) -{ - type_register_static(&mpc8xxx_gpio_info); -} - -type_init(mpc8xxx_gpio_register_types) +DEFINE_TYPES(mpc8xxx_gpio_types) diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 3376901ab1..ba19b9ebad 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -352,7 +352,7 @@ static void npcm7xx_gpio_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_GPIO_ODSC] = s->reset_odsc; } -static void npcm7xx_gpio_hold_reset(Object *obj) +static void npcm7xx_gpio_hold_reset(Object *obj, ResetType type) { NPCM7xxGPIOState *s = NPCM7XX_GPIO(obj); @@ -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..d08c254e36 100644 --- a/hw/gpio/nrf51_gpio.c +++ b/hw/gpio/nrf51_gpio.c @@ -40,7 +40,6 @@ static bool is_connected(uint32_t config, uint32_t level) break; default: g_assert_not_reached(); - break; } return state; @@ -78,6 +77,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 +99,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 +124,8 @@ static void update_state(NRF51GPIOState *s) } update_output_irq(s, i, connected_out, out); } + + qemu_set_irq(s->detect, assert_detect); } /* @@ -269,7 +279,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 +301,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) @@ -298,7 +309,7 @@ static void nrf51_gpio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_nrf51_gpio; - dc->reset = nrf51_gpio_reset; + device_class_set_legacy_reset(dc, nrf51_gpio_reset); dc->desc = "nRF51 GPIO"; } diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index bd0841d57f..a47a2167a6 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; @@ -189,495 +190,17 @@ static void omap_gpio_reset(struct omap_gpio_s *s) s->pins = ~0; } -struct omap2_gpio_s { - qemu_irq irq[2]; - qemu_irq wkup; - qemu_irq *handler; - MemoryRegion iomem; - - uint8_t revision; - uint8_t config[2]; - uint32_t inputs; - uint32_t outputs; - uint32_t dir; - uint32_t level[2]; - uint32_t edge[2]; - uint32_t mask[2]; - uint32_t wumask; - uint32_t ints[2]; - uint32_t debounce; - uint8_t delay; -}; - -struct omap2_gpif_s { - SysBusDevice parent_obj; - - MemoryRegion iomem; - int mpu_model; - void *iclk; - void *fclk[6]; - int modulecount; - struct omap2_gpio_s *modules; - qemu_irq *handler; - int autoidle; - int gpo; -}; - -/* General-Purpose Interface of OMAP2/3 */ -static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, - int line) -{ - qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); -} - -static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line) -{ - if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */ - return; - if (!(s->config[0] & (3 << 3))) /* Force Idle */ - return; - if (!(s->wumask & (1 << line))) - return; - - qemu_irq_raise(s->wkup); -} - -static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s, - uint32_t diff) -{ - int ln; - - s->outputs ^= diff; - diff &= ~s->dir; - while ((ln = ctz32(diff)) != 32) { - qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1); - diff &= ~(1 << ln); - } -} - -static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line) -{ - s->ints[line] |= s->dir & - ((s->inputs & s->level[1]) | (~s->inputs & s->level[0])); - omap2_gpio_module_int_update(s, line); -} - -static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) -{ - s->ints[0] |= 1 << line; - omap2_gpio_module_int_update(s, 0); - s->ints[1] |= 1 << line; - omap2_gpio_module_int_update(s, 1); - omap2_gpio_module_wake(s, line); -} - -static void omap2_gpio_set(void *opaque, int line, int level) -{ - struct omap2_gpif_s *p = opaque; - struct omap2_gpio_s *s = &p->modules[line >> 5]; - - line &= 31; - if (level) { - if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) - omap2_gpio_module_int(s, line); - s->inputs |= 1 << line; - } else { - if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0])) - omap2_gpio_module_int(s, line); - s->inputs &= ~(1 << line); - } -} - -static void omap2_gpio_module_reset(struct omap2_gpio_s *s) -{ - s->config[0] = 0; - s->config[1] = 2; - s->ints[0] = 0; - s->ints[1] = 0; - s->mask[0] = 0; - s->mask[1] = 0; - s->wumask = 0; - s->dir = ~0; - s->level[0] = 0; - s->level[1] = 0; - s->edge[0] = 0; - s->edge[1] = 0; - s->debounce = 0; - s->delay = 0; -} - -static uint32_t omap2_gpio_module_read(void *opaque, hwaddr addr) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - - switch (addr) { - case 0x00: /* GPIO_REVISION */ - return s->revision; - - case 0x10: /* GPIO_SYSCONFIG */ - return s->config[0]; - - case 0x14: /* GPIO_SYSSTATUS */ - return 0x01; - - case 0x18: /* GPIO_IRQSTATUS1 */ - return s->ints[0]; - - case 0x1c: /* GPIO_IRQENABLE1 */ - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - case 0x64: /* GPIO_SETIRQENABLE1 */ - return s->mask[0]; - - case 0x20: /* GPIO_WAKEUPENABLE */ - case 0x80: /* GPIO_CLEARWKUENA */ - case 0x84: /* GPIO_SETWKUENA */ - return s->wumask; - - case 0x28: /* GPIO_IRQSTATUS2 */ - return s->ints[1]; - - case 0x2c: /* GPIO_IRQENABLE2 */ - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - case 0x74: /* GPIO_SETIREQNEABLE2 */ - return s->mask[1]; - - case 0x30: /* GPIO_CTRL */ - return s->config[1]; - - case 0x34: /* GPIO_OE */ - return s->dir; - - case 0x38: /* GPIO_DATAIN */ - return s->inputs; - - case 0x3c: /* GPIO_DATAOUT */ - case 0x90: /* GPIO_CLEARDATAOUT */ - case 0x94: /* GPIO_SETDATAOUT */ - return s->outputs; - - case 0x40: /* GPIO_LEVELDETECT0 */ - return s->level[0]; - - case 0x44: /* GPIO_LEVELDETECT1 */ - return s->level[1]; - - case 0x48: /* GPIO_RISINGDETECT */ - return s->edge[0]; - - case 0x4c: /* GPIO_FALLINGDETECT */ - return s->edge[1]; - - case 0x50: /* GPIO_DEBOUNCENABLE */ - return s->debounce; - - case 0x54: /* GPIO_DEBOUNCINGTIME */ - return s->delay; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap2_gpio_module_write(void *opaque, hwaddr addr, - uint32_t value) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - uint32_t diff; - int ln; - - switch (addr) { - case 0x00: /* GPIO_REVISION */ - case 0x14: /* GPIO_SYSSTATUS */ - case 0x38: /* GPIO_DATAIN */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GPIO_SYSCONFIG */ - if (((value >> 3) & 3) == 3) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Illegal IDLEMODE value: 3\n", __func__); - } - if (value & 2) - omap2_gpio_module_reset(s); - s->config[0] = value & 0x1d; - break; - - case 0x18: /* GPIO_IRQSTATUS1 */ - if (s->ints[0] & value) { - s->ints[0] &= ~value; - omap2_gpio_module_level_update(s, 0); - } - break; - - case 0x1c: /* GPIO_IRQENABLE1 */ - s->mask[0] = value; - omap2_gpio_module_int_update(s, 0); - break; - - case 0x20: /* GPIO_WAKEUPENABLE */ - s->wumask = value; - break; - - case 0x28: /* GPIO_IRQSTATUS2 */ - if (s->ints[1] & value) { - s->ints[1] &= ~value; - omap2_gpio_module_level_update(s, 1); - } - break; - - case 0x2c: /* GPIO_IRQENABLE2 */ - s->mask[1] = value; - omap2_gpio_module_int_update(s, 1); - break; - - case 0x30: /* GPIO_CTRL */ - s->config[1] = value & 7; - break; - - case 0x34: /* GPIO_OE */ - diff = s->outputs & (s->dir ^ value); - s->dir = value; - - value = s->outputs & ~s->dir; - while ((ln = ctz32(diff)) != 32) { - diff &= ~(1 << ln); - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - } - - omap2_gpio_module_level_update(s, 0); - omap2_gpio_module_level_update(s, 1); - break; - - case 0x3c: /* GPIO_DATAOUT */ - omap2_gpio_module_out_update(s, s->outputs ^ value); - break; - - case 0x40: /* GPIO_LEVELDETECT0 */ - s->level[0] = value; - omap2_gpio_module_level_update(s, 0); - omap2_gpio_module_level_update(s, 1); - break; - - case 0x44: /* GPIO_LEVELDETECT1 */ - s->level[1] = value; - omap2_gpio_module_level_update(s, 0); - omap2_gpio_module_level_update(s, 1); - break; - - case 0x48: /* GPIO_RISINGDETECT */ - s->edge[0] = value; - break; - - case 0x4c: /* GPIO_FALLINGDETECT */ - s->edge[1] = value; - break; - - case 0x50: /* GPIO_DEBOUNCENABLE */ - s->debounce = value; - break; - - case 0x54: /* GPIO_DEBOUNCINGTIME */ - s->delay = value; - break; - - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - s->mask[0] &= ~value; - omap2_gpio_module_int_update(s, 0); - break; - - case 0x64: /* GPIO_SETIRQENABLE1 */ - s->mask[0] |= value; - omap2_gpio_module_int_update(s, 0); - break; - - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - s->mask[1] &= ~value; - omap2_gpio_module_int_update(s, 1); - break; - - case 0x74: /* GPIO_SETIREQNEABLE2 */ - s->mask[1] |= value; - omap2_gpio_module_int_update(s, 1); - break; - - case 0x80: /* GPIO_CLEARWKUENA */ - s->wumask &= ~value; - break; - - case 0x84: /* GPIO_SETWKUENA */ - s->wumask |= value; - break; - - case 0x90: /* GPIO_CLEARDATAOUT */ - omap2_gpio_module_out_update(s, s->outputs & value); - break; - - case 0x94: /* GPIO_SETDATAOUT */ - omap2_gpio_module_out_update(s, ~s->outputs & value); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static uint64_t omap2_gpio_module_readp(void *opaque, hwaddr addr, - unsigned size) -{ - return omap2_gpio_module_read(opaque, addr & ~3) >> ((addr & 3) << 3); -} - -static void omap2_gpio_module_writep(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - uint32_t cur = 0; - uint32_t mask = 0xffff; - - if (size == 4) { - omap2_gpio_module_write(opaque, addr, value); - return; - } - - switch (addr & ~3) { - case 0x00: /* GPIO_REVISION */ - case 0x14: /* GPIO_SYSSTATUS */ - case 0x38: /* GPIO_DATAIN */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GPIO_SYSCONFIG */ - case 0x1c: /* GPIO_IRQENABLE1 */ - case 0x20: /* GPIO_WAKEUPENABLE */ - case 0x2c: /* GPIO_IRQENABLE2 */ - case 0x30: /* GPIO_CTRL */ - case 0x34: /* GPIO_OE */ - case 0x3c: /* GPIO_DATAOUT */ - case 0x40: /* GPIO_LEVELDETECT0 */ - case 0x44: /* GPIO_LEVELDETECT1 */ - case 0x48: /* GPIO_RISINGDETECT */ - case 0x4c: /* GPIO_FALLINGDETECT */ - case 0x50: /* GPIO_DEBOUNCENABLE */ - case 0x54: /* GPIO_DEBOUNCINGTIME */ - cur = omap2_gpio_module_read(opaque, addr & ~3) & - ~(mask << ((addr & 3) << 3)); - - /* Fall through. */ - case 0x18: /* GPIO_IRQSTATUS1 */ - case 0x28: /* GPIO_IRQSTATUS2 */ - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - case 0x64: /* GPIO_SETIRQENABLE1 */ - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - case 0x74: /* GPIO_SETIREQNEABLE2 */ - case 0x80: /* GPIO_CLEARWKUENA */ - case 0x84: /* GPIO_SETWKUENA */ - case 0x90: /* GPIO_CLEARDATAOUT */ - case 0x94: /* GPIO_SETDATAOUT */ - value <<= (addr & 3) << 3; - omap2_gpio_module_write(opaque, addr, cur | value); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap2_gpio_module_ops = { - .read = omap2_gpio_module_readp, - .write = omap2_gpio_module_writep, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - 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); - int i; - - for (i = 0; i < s->modulecount; i++) { - omap2_gpio_module_reset(&s->modules[i]); - } - s->autoidle = 0; - s->gpo = 0; -} - -static uint64_t omap2_gpif_top_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap2_gpif_s *s = (struct omap2_gpif_s *) opaque; - - switch (addr) { - case 0x00: /* IPGENERICOCPSPL_REVISION */ - return 0x18; - - case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ - return s->autoidle; - - case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ - return 0x01; - - case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ - return 0x00; - - case 0x40: /* IPGENERICOCPSPL_GPO */ - return s->gpo; - - case 0x50: /* IPGENERICOCPSPL_GPI */ - return 0x00; - } - - OMAP_BAD_REG(addr); - return 0; -} - -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; - - switch (addr) { - case 0x00: /* IPGENERICOCPSPL_REVISION */ - case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ - case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ - case 0x50: /* IPGENERICOCPSPL_GPI */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap2_gpif_reset(DEVICE(s)); - s->autoidle = value & 1; - break; - - case 0x40: /* IPGENERICOCPSPL_GPO */ - s->gpo = value & 1; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap2_gpif_top_ops = { - .read = omap2_gpif_top_read, - .write = omap2_gpif_top_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - 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,65 +213,20 @@ 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"); } } -static void omap2_gpio_realize(DeviceState *dev, Error **errp) -{ - struct omap2_gpif_s *s = OMAP2_GPIO(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - int i; - - if (!s->iclk) { - error_setg(errp, "omap2-gpio: iclk not connected"); - return; - } - - s->modulecount = s->mpu_model < omap2430 ? 4 - : s->mpu_model < omap3430 ? 5 - : 6; - - if (s->mpu_model < omap3430) { - memory_region_init_io(&s->iomem, OBJECT(dev), &omap2_gpif_top_ops, s, - "omap2.gpio", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - } - - s->modules = g_new0(struct omap2_gpio_s, s->modulecount); - s->handler = g_new0(qemu_irq, s->modulecount * 32); - qdev_init_gpio_in(dev, omap2_gpio_set, s->modulecount * 32); - qdev_init_gpio_out(dev, s->handler, s->modulecount * 32); - - for (i = 0; i < s->modulecount; i++) { - struct omap2_gpio_s *m = &s->modules[i]; - - if (!s->fclk[i]) { - error_setg(errp, "omap2-gpio: fclk%d not connected", i); - return; - } - - m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25; - m->handler = &s->handler[i * 32]; - sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */ - sysbus_init_irq(sbd, &m->irq[1]); /* dsp irq */ - sysbus_init_irq(sbd, &m->wkup); - memory_region_init_io(&m->iomem, OBJECT(dev), &omap2_gpio_module_ops, m, - "omap.gpio-module", 0x1000); - sysbus_init_mmio(sbd, &m->iomem); - } -} - -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(), }; @@ -757,7 +235,7 @@ static void omap_gpio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = omap_gpio_realize; - dc->reset = omap_gpif_reset; + device_class_set_legacy_reset(dc, omap_gpif_reset); device_class_set_props(dc, omap_gpio_properties); /* Reason: pointer property "clk" */ dc->user_creatable = false; @@ -766,49 +244,14 @@ 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) -{ - gpio->iclk = clk; -} - -void omap2_gpio_set_fclk(omap2_gpif *gpio, uint8_t i, omap_clk clk) -{ - assert(i <= 5); - gpio->fclk[i] = clk; -} - -static Property omap2_gpio_properties[] = { - DEFINE_PROP_INT32("mpu_model", struct omap2_gpif_s, mpu_model, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap2_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = omap2_gpio_realize; - dc->reset = omap2_gpif_reset; - device_class_set_props(dc, omap2_gpio_properties); - /* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */ - dc->user_creatable = false; -} - -static const TypeInfo omap2_gpio_info = { - .name = TYPE_OMAP2_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct omap2_gpif_s), - .class_init = omap2_gpio_class_init, -}; - static void omap_gpio_register_types(void) { type_register_static(&omap_gpio_info); - type_register_static(&omap2_gpio_info); } type_init(omap_gpio_register_types) 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..59b233339a 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[] = { @@ -416,7 +460,7 @@ static void pca9552_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); - dc->reset = pca9552_reset; + device_class_set_legacy_reset(dc, pca9552_reset); dc->vmsd = &pca9552_vmstate; pc->max_reg = PCA9552_LS3; pc->pin_count = 16; diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c new file mode 100644 index 0000000000..68cc9e1de4 --- /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; + device_class_set_legacy_reset(dc, 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..208efe69ea --- /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; + device_class_set_legacy_reset(dc, 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..d5838b8e98 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), @@ -484,7 +484,7 @@ static void pl061_enter_reset(Object *obj, ResetType type) s->amsel = 0; } -static void pl061_hold_reset(Object *obj) +static void pl061_hold_reset(Object *obj, ResetType type) { PL061State *s = PL061(obj); int i, level; diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c index 78bf29e996..e85c0406a2 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), @@ -378,7 +378,7 @@ static void sifive_gpio_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, sifive_gpio_properties); dc->vmsd = &vmstate_sifive_gpio; dc->realize = sifive_gpio_realize; - dc->reset = sifive_gpio_reset; + device_class_set_legacy_reset(dc, sifive_gpio_reset); dc->desc = "SiFive GPIO"; } diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c new file mode 100644 index 0000000000..30d8d6cba4 --- /dev/null +++ b/hw/gpio/stm32l4x5_gpio.c @@ -0,0 +1,479 @@ +/* + * 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/clock.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, ResetType type) +{ + 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 = 2, + .minimum_version_id = 2, + .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_CLOCK(clk, 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..7342440b95 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -49,19 +49,20 @@ struct ScoopInfo { uint16_t isr; }; -#define SCOOP_MCR 0x00 -#define SCOOP_CDR 0x04 -#define SCOOP_CSR 0x08 -#define SCOOP_CPR 0x0c -#define SCOOP_CCR 0x10 -#define SCOOP_IRR_IRM 0x14 -#define SCOOP_IMR 0x18 -#define SCOOP_ISR 0x1c -#define SCOOP_GPCR 0x20 -#define SCOOP_GPWR 0x24 -#define SCOOP_GPRR 0x28 +#define SCOOP_MCR 0x00 +#define SCOOP_CDR 0x04 +#define SCOOP_CSR 0x08 +#define SCOOP_CPR 0x0c +#define SCOOP_CCR 0x10 +#define SCOOP_IRR_IRM 0x14 +#define SCOOP_IMR 0x18 +#define SCOOP_ISR 0x1c +#define SCOOP_GPCR 0x20 +#define SCOOP_GPWR 0x24 +#define SCOOP_GPRR 0x28 -static inline void scoop_gpio_handler_update(ScoopInfo *s) { +static inline void scoop_gpio_handler_update(ScoopInfo *s) +{ uint32_t level, diff; int bit; level = s->gpio_level & s->gpio_dir; @@ -125,8 +126,9 @@ static void scoop_write(void *opaque, hwaddr addr, break; case SCOOP_CPR: s->power = value; - if (value & 0x80) + if (value & 0x80) { s->power |= 0x8040; + } break; case SCOOP_CCR: s->ccr = value; @@ -145,7 +147,7 @@ static void scoop_write(void *opaque, hwaddr addr, scoop_gpio_handler_update(s); break; case SCOOP_GPWR: - case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ + case SCOOP_GPRR: /* GPRR is probably R/O in real HW */ s->gpio_level = value & s->gpio_dir; scoop_gpio_handler_update(s); break; @@ -166,10 +168,11 @@ static void scoop_gpio_set(void *opaque, int line, int level) { ScoopInfo *s = (ScoopInfo *) opaque; - if (level) + if (level) { s->gpio_level |= (1 << line); - else + } else { s->gpio_level &= ~(1 << line); + } } static void scoop_init(Object *obj) @@ -203,7 +206,7 @@ static int scoop_post_load(void *opaque, int version_id) return 0; } -static bool is_version_0 (void *opaque, int version_id) +static bool is_version_0(void *opaque, int version_id) { return version_id == 0; } @@ -222,7 +225,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), @@ -265,7 +268,7 @@ type_init(scoop_register_types) /* Write the bootloader parameters memory area. */ -#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) +#define MAGIC_CHG(a, b, c, d) ((d << 24) | (c << 16) | (b << 8) | a) static struct QEMU_PACKED sl_param_info { uint32_t comadj_keyword; @@ -286,16 +289,16 @@ static struct QEMU_PACKED sl_param_info { uint32_t phad_keyword; int32_t phadadj; } zaurus_bootparam = { - .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), - .comadj = 125, - .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), - .uuid = { -1 }, - .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), - .touch_xp = -1, - .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), - .adadj = -1, - .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), - .phadadj = 0x01, + .comadj_keyword = MAGIC_CHG('C', 'M', 'A', 'D'), + .comadj = 125, + .uuid_keyword = MAGIC_CHG('U', 'U', 'I', 'D'), + .uuid = { -1 }, + .touch_keyword = MAGIC_CHG('T', 'U', 'C', 'H'), + .touch_xp = -1, + .adadj_keyword = MAGIC_CHG('B', 'V', 'A', 'D'), + .adadj = -1, + .phad_keyword = MAGIC_CHG('P', 'H', 'A', 'D'), + .phadadj = 0x01, }; void sl_bootparam_write(hwaddr ptr) diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig index 5dd8b5b21e..9312c4294a 100644 --- a/hw/hppa/Kconfig +++ b/hw/hppa/Kconfig @@ -1,11 +1,16 @@ config HPPA_B160L bool + default y + depends on HPPA imply PCI_DEVICES imply E1000_PCI + imply USB_OHCI_PCI imply VIRTIO_VGA + select ASTRO select DINO select LASI - select SERIAL + select SERIAL_MM + 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 98a78b84b4..a31dc32a9f 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,16 +13,20 @@ #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" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/char/parallel.h" #include "hw/intc/i8259.h" #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,18 +34,18 @@ #include "qapi/error.h" #include "net/net.h" #include "qemu/log.h" -#include "net/net.h" -#define MIN_SEABIOS_HPPA_VERSION 6 /* require at least this fw version */ +#define MIN_SEABIOS_HPPA_VERSION 12 /* 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); @@ -82,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; @@ -91,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; @@ -119,40 +190,58 @@ 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); val = cpu_to_le64(MIN_SEABIOS_HPPA_VERSION); fw_cfg_add_file(fw_cfg, "/etc/firmware-min-version", - g_memdup(&val, sizeof(val)), sizeof(val)); + g_memdup2(&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)); + g_memdup2(&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)); + g_memdup2(&val, sizeof(val)), sizeof(val)); - val = cpu_to_le64(HPA_POWER_BUTTON); - fw_cfg_add_file(fw_cfg, "/etc/power-button-addr", - g_memdup(&val, sizeof(val)), sizeof(val)); + len = strlen(mc->name) + 1; + fw_cfg_add_file(fw_cfg, "/etc/hppa/machine", + g_memdup2(mc->name, len), len); + + val = cpu_to_le64(soft_power_reg); + fw_cfg_add_file(fw_cfg, "/etc/hppa/power-button-addr", + g_memdup2(&val, sizeof(val)), sizeof(val)); + + val = cpu_to_le64(CPU_HPA + 16); + fw_cfg_add_file(fw_cfg, "/etc/hppa/rtc-addr", + g_memdup2(&val, sizeof(val)), sizeof(val)); + + val = cpu_to_le64(CPU_HPA + 24); + fw_cfg_add_file(fw_cfg, "/etc/hppa/DebugOutputPort", + g_memdup2(&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)), + g_memdup2(qemu_version, sizeof(qemu_version)), sizeof(qemu_version)); + fw_cfg_add_extra_pci_roots(pci_bus, fw_cfg); + return fw_cfg; } @@ -178,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) { @@ -266,84 +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 */ - 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, LASI_PS2KBD_HPA, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), - 0)); - memory_region_add_subregion(addr_space, LASI_PS2KBD_HPA + 0x100, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), - 1)); + /* 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); @@ -414,12 +529,125 @@ static void machine_hppa_init(MachineState *machine) cpu[0]->env.gr[19] = FW_CFG_IO_BASE; } -static void hppa_machine_reset(MachineState *ms, ShutdownCause reason) +/* + * 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, ResetType type) { unsigned int smp_cpus = ms->smp.cpus; int i; - qemu_devices_reset(reason); + qemu_devices_reset(type); /* Start all CPUs at the firmware entry point. * Monarch CPU will initialize firmware, secondary CPUs @@ -458,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; @@ -474,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 }, { } @@ -490,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 57b402b956..ba94bf9f8d 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; @@ -134,7 +138,7 @@ static void synic_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = synic_realize; - dc->reset = synic_reset; + device_class_set_legacy_reset(dc, synic_reset); dc->user_creatable = false; } @@ -369,6 +373,31 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) return ret; } +static int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint) +{ + struct kvm_irq_routing_entry kroute = {}; + int virq; + + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + virq = kvm_irqchip_get_virq(s); + if (virq < 0) { + return virq; + } + + kroute.gsi = virq; + kroute.type = KVM_IRQ_ROUTING_HV_SINT; + kroute.flags = 0; + kroute.u.hv_sint.vcpu = vcpu; + kroute.u.hv_sint.sint = sint; + + kvm_add_routing_entry(s, &kroute); + kvm_irqchip_commit_routes(s); + + return virq; +} + HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintMsgCb cb, void *cb_data) { @@ -947,3 +976,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/hyperv_testdev.c b/hw/hyperv/hyperv_testdev.c index 9a56ddf83f..a630ca7047 100644 --- a/hw/hyperv/hyperv_testdev.c +++ b/hw/hyperv/hyperv_testdev.c @@ -88,8 +88,7 @@ static TestSintRoute *sint_route_find(HypervTestDev *dev, return sint_route; } } - assert(false); - return NULL; + g_assert_not_reached(); } static void sint_route_destroy(HypervTestDev *dev, @@ -187,7 +186,7 @@ static void msg_conn_destroy(HypervTestDev *dev, uint8_t conn_id) return; } } - assert(false); + g_assert_not_reached(); } static void evt_conn_handler(EventNotifier *notifier) @@ -237,7 +236,7 @@ static void evt_conn_destroy(HypervTestDev *dev, uint8_t conn_id) return; } } - assert(false); + g_assert_not_reached(); } static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size) 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..b36bd3d67d 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) @@ -1874,7 +1874,7 @@ static void send_create_gpadl(VMBus *vmbus) } } - assert(false); + g_assert_not_reached(); } static bool complete_create_gpadl(VMBus *vmbus) @@ -1889,8 +1889,7 @@ static bool complete_create_gpadl(VMBus *vmbus) } } - assert(false); - return false; + g_assert_not_reached(); } static void handle_gpadl_teardown(VMBus *vmbus, @@ -1931,7 +1930,7 @@ static void send_teardown_gpadl(VMBus *vmbus) } } - assert(false); + g_assert_not_reached(); } static bool complete_teardown_gpadl(VMBus *vmbus) @@ -1946,8 +1945,7 @@ static bool complete_teardown_gpadl(VMBus *vmbus) } } - assert(false); - return false; + g_assert_not_reached(); } static void handle_open_channel(VMBus *vmbus, vmbus_message_open_channel *msg, @@ -1996,7 +1994,7 @@ static void send_open_channel(VMBus *vmbus) } } - assert(false); + g_assert_not_reached(); } static bool complete_open_channel(VMBus *vmbus) @@ -2020,8 +2018,7 @@ static bool complete_open_channel(VMBus *vmbus) } } - assert(false); - return false; + g_assert_not_reached(); } static void vdev_reset_on_close(VMBusDevice *vdev) @@ -2035,7 +2032,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 +2101,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 +2268,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)); @@ -2362,7 +2359,7 @@ static void vmbus_dev_class_init(ObjectClass *klass, void *data) kdev->bus_type = TYPE_VMBUS; kdev->realize = vmbus_dev_realize; kdev->unrealize = vmbus_dev_unrealize; - kdev->reset = vmbus_dev_reset; + device_class_set_legacy_reset(kdev, vmbus_dev_reset); } static void vmbus_dev_instance_init(Object *obj) @@ -2380,7 +2377,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 +2401,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 +2411,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 +2425,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 +2437,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 +2450,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, ResetType type) { - vmbus_deinit(VMBUS(bus)); + vmbus_deinit(VMBUS(obj)); } static char *vmbus_get_dev_path(DeviceState *dev) @@ -2469,7 +2464,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 +2473,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 +2547,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 +2570,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 +2587,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 +2596,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 +2628,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 +2647,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..16f1d6d40e --- /dev/null +++ b/hw/i2c/allwinner-i2c.c @@ -0,0 +1,478 @@ +/* + * 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, ResetType type) +{ + 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)) { + device_cold_reset(DEVICE(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/arm_sbcon_i2c.c b/hw/i2c/arm_sbcon_i2c.c new file mode 100644 index 0000000000..979ccbe0ed --- /dev/null +++ b/hw/i2c/arm_sbcon_i2c.c @@ -0,0 +1,107 @@ +/* + * ARM SBCon two-wire serial bus interface (I2C bitbang) + * a.k.a. ARM Versatile I2C controller + * + * Copyright (c) 2006-2007 CodeSourcery. + * Copyright (c) 2012 Oskar Andero + * + * This file is derived from hw/realview.c by Paul Brook + * + * 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/i2c/arm_sbcon_i2c.h" +#include "hw/registerfields.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qom/object.h" + + +REG32(CONTROL_GET, 0) +REG32(CONTROL_SET, 0) +REG32(CONTROL_CLR, 4) + +#define SCL BIT(0) +#define SDA BIT(1) + +static uint64_t arm_sbcon_i2c_read(void *opaque, hwaddr offset, + unsigned size) +{ + ArmSbconI2CState *s = opaque; + + switch (offset) { + case A_CONTROL_SET: + return (s->out & 1) | (s->in << 1); + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + return -1; + } +} + +static void arm_sbcon_i2c_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + ArmSbconI2CState *s = opaque; + + switch (offset) { + case A_CONTROL_SET: + s->out |= value & 3; + break; + case A_CONTROL_CLR: + s->out &= ~value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%x\n", __func__, (int)offset); + } + bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0); + s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0); +} + +static const MemoryRegionOps arm_sbcon_i2c_ops = { + .read = arm_sbcon_i2c_read, + .write = arm_sbcon_i2c_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void arm_sbcon_i2c_init(Object *obj) +{ + DeviceState *dev = DEVICE(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, &arm_sbcon_i2c_ops, s, + "arm_sbcon_i2c", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static const TypeInfo arm_sbcon_i2c_info = { + .name = TYPE_ARM_SBCON_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ArmSbconI2CState), + .instance_init = arm_sbcon_i2c_init, +}; + +static void arm_sbcon_i2c_register_types(void) +{ + type_register_static(&arm_sbcon_i2c_info); +} + +type_init(arm_sbcon_i2c_register_types) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index c166fd20fa..3ae22cb052 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -114,7 +114,10 @@ static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, if (!aic->has_dma) { qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA support\n", __func__); value = -1; + break; } + + value = extract64(bus->dma_dram_offset, 0, 32); break; case A_I2CD_DMA_LEN: if (!aic->has_dma) { @@ -137,6 +140,7 @@ static uint64_t aspeed_i2c_bus_old_read(AspeedI2CBus *bus, hwaddr offset, static uint64_t aspeed_i2c_bus_new_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) { @@ -150,9 +154,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, 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: @@ -161,11 +163,24 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, case A_I2CS_DMA_LEN_STS: /* Value is already set, don't do anything. */ break; + case A_I2CC_DMA_ADDR: + value = extract64(bus->dma_dram_offset, 0, 32); + 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; + case A_I2CM_DMA_TX_ADDR_HI: + case A_I2CM_DMA_RX_ADDR_HI: + case A_I2CS_DMA_TX_ADDR_HI: + case A_I2CS_DMA_RX_ADDR_HI: + if (!aic->has_dma64) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA 64 bits support\n", + __func__); + value = -1; + } + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -210,23 +225,23 @@ 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->regs[reg_dma_addr], + result = address_space_read(&s->dram_as, bus->dma_dram_offset, MEMTXATTRS_UNSPECIFIED, data, 1); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n", - __func__, bus->regs[reg_dma_addr]); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DRAM read failed @%" PRIx64 "\n", + __func__, bus->dma_dram_offset); return -1; } - bus->regs[reg_dma_addr]++; + bus->dma_dram_offset++; 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; @@ -236,10 +251,10 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) 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); + TX_COUNT) + 1; if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { - for (i = pool_start; i < pool_tx_count; i++) { + 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, pool_tx_count, @@ -273,7 +288,7 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus, uint8_t pool_start) } SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); } else { - trace_aspeed_i2c_bus_send("BYTE", pool_start, 1, + trace_aspeed_i2c_bus_send("BYTE", 0, 1, bus->regs[reg_byte_buf]); ret = i2c_send(bus->bus, bus->regs[reg_byte_buf]); } @@ -291,12 +306,15 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *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_COUNT); + RX_SIZE) + 1; 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 < pool_rx_count; i++) { pool_base[i] = i2c_recv(bus->bus); @@ -308,7 +326,6 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) 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)) { - uint8_t data; /* 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); @@ -320,14 +337,17 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) data = i2c_recv(bus->bus); 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], + + result = address_space_write(&s->dram_as, bus->dma_dram_offset, MEMTXATTRS_UNSPECIFIED, &data, 1); if (result != MEMTX_OK) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n", - __func__, bus->regs[reg_dma_addr]); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DRAM write failed @%" PRIx64 "\n", + __func__, bus->dma_dram_offset); return; } - bus->regs[reg_dma_addr]++; + + bus->dma_dram_offset++; bus->regs[reg_dma_len]--; /* In new mode, keep track of how many bytes we RXed */ if (aspeed_i2c_is_new_mode(bus->controller)) { @@ -418,7 +438,7 @@ static void aspeed_i2c_bus_cmd_dump(AspeedI2CBus *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); + 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 */ @@ -446,10 +466,8 @@ 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; 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_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); if (!aspeed_i2c_check_sram(bus)) { @@ -483,27 +501,11 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) 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 (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_BUFF_EN)) { - if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, TX_COUNT) - == 1) { - SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, M_TX_CMD, 0); - } else { - /* - * Increase the start index in the TX pool buffer to - * skip the address byte. - */ - pool_start++; - } - } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + 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 { + } 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); } @@ -520,7 +522,7 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) 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)) { + if (aspeed_i2c_bus_send(bus)) { SHARED_ARRAY_FIELD_DP32(bus->regs, reg_intr_sts, TX_NAK, 1); i2c_end_transfer(bus->bus); } else { @@ -550,6 +552,8 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) } 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)) { @@ -649,14 +653,18 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, 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->dma_dram_offset = + deposit64(bus->dma_dram_offset, 0, 32, + 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->dma_dram_offset = + deposit64(bus->dma_dram_offset, 0, 32, + 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; @@ -734,6 +742,56 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, qemu_log_mask(LOG_UNIMP, "%s: Slave mode DMA TX is not implemented\n", __func__); break; + + /* + * The AST2700 support the maximum DRAM size is 8 GB. + * The DRAM offset range is from 0x0_0000_0000 to + * 0x1_FFFF_FFFF and it is enough to use bits [33:0] + * saving the dram offset. + * Therefore, save the high part physical address bit[1:0] + * of Tx/Rx buffer address as dma_dram_offset bit[33:32]. + */ + case A_I2CM_DMA_TX_ADDR_HI: + if (!aic->has_dma64) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA 64 bits support\n", + __func__); + break; + } + bus->regs[R_I2CM_DMA_TX_ADDR_HI] = FIELD_EX32(value, + I2CM_DMA_TX_ADDR_HI, + ADDR_HI); + bus->dma_dram_offset = deposit64(bus->dma_dram_offset, 32, 32, + extract32(value, 0, 2)); + break; + case A_I2CM_DMA_RX_ADDR_HI: + if (!aic->has_dma64) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA 64 bits support\n", + __func__); + break; + } + bus->regs[R_I2CM_DMA_RX_ADDR_HI] = FIELD_EX32(value, + I2CM_DMA_RX_ADDR_HI, + ADDR_HI); + bus->dma_dram_offset = deposit64(bus->dma_dram_offset, 32, 32, + extract32(value, 0, 2)); + break; + case A_I2CS_DMA_TX_ADDR_HI: + qemu_log_mask(LOG_UNIMP, + "%s: Slave mode DMA TX Addr high is not implemented\n", + __func__); + break; + case A_I2CS_DMA_RX_ADDR_HI: + if (!aic->has_dma64) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: No DMA 64 bits support\n", + __func__); + break; + } + bus->regs[R_I2CS_DMA_RX_ADDR_HI] = FIELD_EX32(value, + I2CS_DMA_RX_ADDR_HI, + ADDR_HI); + bus->dma_dram_offset = deposit64(bus->dma_dram_offset, 32, 32, + extract32(value, 0, 2)); + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -824,7 +882,8 @@ static void aspeed_i2c_bus_old_write(AspeedI2CBus *bus, hwaddr offset, break; } - bus->regs[R_I2CD_DMA_ADDR] = value & 0x3ffffffc; + bus->dma_dram_offset = deposit64(bus->dma_dram_offset, 0, 32, + value & 0x3ffffffc); break; case A_I2CD_DMA_LEN: @@ -919,13 +978,48 @@ static const MemoryRegionOps aspeed_i2c_ctrl_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, +static uint64_t aspeed_i2c_share_pool_read(void *opaque, hwaddr offset, unsigned size) { AspeedI2CState *s = opaque; uint64_t ret = 0; int i; + for (i = 0; i < size; i++) { + ret |= (uint64_t) s->share_pool[offset + i] << (8 * i); + } + + return ret; +} + +static void aspeed_i2c_share_pool_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + AspeedI2CState *s = opaque; + int i; + + for (i = 0; i < size; i++) { + s->share_pool[offset + i] = (value >> (8 * i)) & 0xFF; + } +} + +static const MemoryRegionOps aspeed_i2c_share_pool_ops = { + .read = aspeed_i2c_share_pool_read, + .write = aspeed_i2c_share_pool_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static uint64_t aspeed_i2c_bus_pool_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedI2CBus *s = opaque; + uint64_t ret = 0; + int i; + for (i = 0; i < size; i++) { ret |= (uint64_t) s->pool[offset + i] << (8 * i); } @@ -933,10 +1027,10 @@ static uint64_t aspeed_i2c_pool_read(void *opaque, hwaddr offset, return ret; } -static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, +static void aspeed_i2c_bus_pool_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - AspeedI2CState *s = opaque; + AspeedI2CBus *s = opaque; int i; for (i = 0; i < size; i++) { @@ -944,9 +1038,9 @@ static void aspeed_i2c_pool_write(void *opaque, hwaddr offset, } } -static const MemoryRegionOps aspeed_i2c_pool_ops = { - .read = aspeed_i2c_pool_read, - .write = aspeed_i2c_pool_write, +static const MemoryRegionOps aspeed_i2c_bus_pool_ops = { + .read = aspeed_i2c_bus_pool_read, + .write = aspeed_i2c_bus_pool_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, @@ -956,24 +1050,27 @@ static const MemoryRegionOps aspeed_i2c_pool_ops = { static const VMStateDescription aspeed_i2c_bus_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 5, - .minimum_version_id = 5, - .fields = (VMStateField[]) { + .version_id = 6, + .minimum_version_id = 6, + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedI2CBus, ASPEED_I2C_NEW_NUM_REG), + VMSTATE_UINT8_ARRAY(pool, AspeedI2CBus, ASPEED_I2C_BUS_POOL_SIZE), + VMSTATE_UINT64(dma_dram_offset, AspeedI2CBus), VMSTATE_END_OF_LIST() } }; static const VMStateDescription aspeed_i2c_vmstate = { .name = TYPE_ASPEED_I2C, - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { + .version_id = 3, + .minimum_version_id = 3, + .fields = (const VMStateField[]) { VMSTATE_UINT32(intr_status, AspeedI2CState), VMSTATE_STRUCT_ARRAY(busses, AspeedI2CState, ASPEED_I2C_NR_BUSSES, 1, aspeed_i2c_bus_vmstate, AspeedI2CBus), - VMSTATE_UINT8_ARRAY(pool, AspeedI2CState, ASPEED_I2C_MAX_POOL_SIZE), + VMSTATE_UINT8_ARRAY(share_pool, AspeedI2CState, + ASPEED_I2C_SHARE_POOL_SIZE), VMSTATE_END_OF_LIST() } }; @@ -1008,7 +1105,21 @@ static void aspeed_i2c_instance_init(Object *obj) * 0x140 ... 0x17F: Device 5 * 0x180 ... 0x1BF: Device 6 * 0x1C0 ... 0x1FF: Device 7 - * 0x200 ... 0x2FF: Buffer Pool (unused in linux driver) + * 0x200 ... 0x20F: Device 1 buffer (AST2500 unused in linux driver) + * 0x210 ... 0x21F: Device 2 buffer + * 0x220 ... 0x22F: Device 3 buffer + * 0x230 ... 0x23F: Device 4 buffer + * 0x240 ... 0x24F: Device 5 buffer + * 0x250 ... 0x25F: Device 6 buffer + * 0x260 ... 0x26F: Device 7 buffer + * 0x270 ... 0x27F: Device 8 buffer + * 0x280 ... 0x28F: Device 9 buffer + * 0x290 ... 0x29F: Device 10 buffer + * 0x2A0 ... 0x2AF: Device 11 buffer + * 0x2B0 ... 0x2BF: Device 12 buffer + * 0x2C0 ... 0x2CF: Device 13 buffer + * 0x2D0 ... 0x2DF: Device 14 buffer + * 0x2E0 ... 0x2FF: Reserved * 0x300 ... 0x33F: Device 8 * 0x340 ... 0x37F: Device 9 * 0x380 ... 0x3BF: Device 10 @@ -1016,7 +1127,77 @@ static void aspeed_i2c_instance_init(Object *obj) * 0x400 ... 0x43F: Device 12 * 0x440 ... 0x47F: Device 13 * 0x480 ... 0x4BF: Device 14 - * 0x800 ... 0xFFF: Buffer Pool (unused in linux driver) + * 0x800 ... 0xFFF: Buffer Pool (AST2400 unused in linux driver) + * + * Address Definitions (AST2600 and AST1030) + * 0x000 ... 0x07F: Global Register + * 0x080 ... 0x0FF: Device 1 + * 0x100 ... 0x17F: Device 2 + * 0x180 ... 0x1FF: Device 3 + * 0x200 ... 0x27F: Device 4 + * 0x280 ... 0x2FF: Device 5 + * 0x300 ... 0x37F: Device 6 + * 0x380 ... 0x3FF: Device 7 + * 0x400 ... 0x47F: Device 8 + * 0x480 ... 0x4FF: Device 9 + * 0x500 ... 0x57F: Device 10 + * 0x580 ... 0x5FF: Device 11 + * 0x600 ... 0x67F: Device 12 + * 0x680 ... 0x6FF: Device 13 + * 0x700 ... 0x77F: Device 14 + * 0x780 ... 0x7FF: Device 15 (15 and 16 unused in AST1030) + * 0x800 ... 0x87F: Device 16 + * 0xC00 ... 0xC1F: Device 1 buffer + * 0xC20 ... 0xC3F: Device 2 buffer + * 0xC40 ... 0xC5F: Device 3 buffer + * 0xC60 ... 0xC7F: Device 4 buffer + * 0xC80 ... 0xC9F: Device 5 buffer + * 0xCA0 ... 0xCBF: Device 6 buffer + * 0xCC0 ... 0xCDF: Device 7 buffer + * 0xCE0 ... 0xCFF: Device 8 buffer + * 0xD00 ... 0xD1F: Device 9 buffer + * 0xD20 ... 0xD3F: Device 10 buffer + * 0xD40 ... 0xD5F: Device 11 buffer + * 0xD60 ... 0xD7F: Device 12 buffer + * 0xD80 ... 0xD9F: Device 13 buffer + * 0xDA0 ... 0xDBF: Device 14 buffer + * 0xDC0 ... 0xDDF: Device 15 buffer (15 and 16 unused in AST1030) + * 0xDE0 ... 0xDFF: Device 16 buffer + * + * Address Definitions (AST2700) + * 0x000 ... 0x0FF: Global Register + * 0x100 ... 0x17F: Device 0 + * 0x1A0 ... 0x1BF: Device 0 buffer + * 0x200 ... 0x27F: Device 1 + * 0x2A0 ... 0x2BF: Device 1 buffer + * 0x300 ... 0x37F: Device 2 + * 0x3A0 ... 0x3BF: Device 2 buffer + * 0x400 ... 0x47F: Device 3 + * 0x4A0 ... 0x4BF: Device 3 buffer + * 0x500 ... 0x57F: Device 4 + * 0x5A0 ... 0x5BF: Device 4 buffer + * 0x600 ... 0x67F: Device 5 + * 0x6A0 ... 0x6BF: Device 5 buffer + * 0x700 ... 0x77F: Device 6 + * 0x7A0 ... 0x7BF: Device 6 buffer + * 0x800 ... 0x87F: Device 7 + * 0x8A0 ... 0x8BF: Device 7 buffer + * 0x900 ... 0x97F: Device 8 + * 0x9A0 ... 0x9BF: Device 8 buffer + * 0xA00 ... 0xA7F: Device 9 + * 0xAA0 ... 0xABF: Device 9 buffer + * 0xB00 ... 0xB7F: Device 10 + * 0xBA0 ... 0xBBF: Device 10 buffer + * 0xC00 ... 0xC7F: Device 11 + * 0xCA0 ... 0xCBF: Device 11 buffer + * 0xD00 ... 0xD7F: Device 12 + * 0xDA0 ... 0xDBF: Device 12 buffer + * 0xE00 ... 0xE7F: Device 13 + * 0xEA0 ... 0xEBF: Device 13 buffer + * 0xF00 ... 0xF7F: Device 14 + * 0xFA0 ... 0xFBF: Device 14 buffer + * 0x1000 ... 0x107F: Device 15 + * 0x10A0 ... 0x10BF: Device 15 buffer */ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) { @@ -1024,10 +1205,12 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedI2CState *s = ASPEED_I2C(dev); AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(s); + uint32_t reg_offset = aic->reg_size + aic->reg_gap_size; + uint32_t pool_offset = aic->pool_size + aic->pool_gap_size; sysbus_init_irq(sbd, &s->irq); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i2c_ctrl_ops, s, - "aspeed.i2c", 0x1000); + "aspeed.i2c", aic->mem_size); sysbus_init_mmio(sbd, &s->iomem); for (i = 0; i < aic->num_busses; i++) { @@ -1046,13 +1229,23 @@ static void aspeed_i2c_realize(DeviceState *dev, Error **errp) return; } - memory_region_add_subregion(&s->iomem, aic->reg_size * (i + offset), + memory_region_add_subregion(&s->iomem, reg_offset * (i + offset), &s->busses[i].mr); } - memory_region_init_io(&s->pool_iomem, OBJECT(s), &aspeed_i2c_pool_ops, s, - "aspeed.i2c-pool", aic->pool_size); - memory_region_add_subregion(&s->iomem, aic->pool_base, &s->pool_iomem); + if (aic->has_share_pool) { + memory_region_init_io(&s->pool_iomem, OBJECT(s), + &aspeed_i2c_share_pool_ops, s, + "aspeed.i2c-share-pool", aic->pool_size); + memory_region_add_subregion(&s->iomem, aic->pool_base, + &s->pool_iomem); + } else { + for (i = 0; i < aic->num_busses; i++) { + memory_region_add_subregion(&s->iomem, + aic->pool_base + (pool_offset * i), + &s->busses[i].mr_pool); + } + } if (aic->has_dma) { if (!s->dram_mr) { @@ -1076,7 +1269,7 @@ static void aspeed_i2c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &aspeed_i2c_vmstate; - dc->reset = aspeed_i2c_reset; + device_class_set_legacy_reset(dc, aspeed_i2c_reset); device_class_set_props(dc, aspeed_i2c_properties); dc->realize = aspeed_i2c_realize; dc->desc = "Aspeed I2C Controller"; @@ -1103,8 +1296,9 @@ static int aspeed_i2c_bus_new_slave_event(AspeedI2CBus *bus, 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->dma_dram_offset = + deposit64(bus->dma_dram_offset, 0, 32, + 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); @@ -1170,10 +1364,10 @@ static int aspeed_i2c_bus_slave_event(I2CSlave *slave, enum i2c_event event) 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], + bus->dma_dram_offset, MEMTXATTRS_UNSPECIFIED, &data, 1) == MEMTX_OK); - bus->regs[R_I2CC_DMA_ADDR]++; + bus->dma_dram_offset++; 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); @@ -1228,6 +1422,7 @@ static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) AspeedI2CBus *s = ASPEED_I2C_BUS(dev); AspeedI2CClass *aic; g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I2C_BUS ".%d", s->id); + g_autofree char *pool_name = g_strdup_printf("%s.pool", name); if (!s->controller) { error_setg(errp, TYPE_ASPEED_I2C_BUS ": 'controller' link not set"); @@ -1245,6 +1440,10 @@ static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i2c_bus_ops, s, name, aic->reg_size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); + + memory_region_init_io(&s->mr_pool, OBJECT(s), &aspeed_i2c_bus_pool_ops, + s, pool_name, aic->pool_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr_pool); } static Property aspeed_i2c_bus_properties[] = { @@ -1260,7 +1459,7 @@ static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) dc->desc = "Aspeed I2C Bus"; dc->realize = aspeed_i2c_bus_realize; - dc->reset = aspeed_i2c_bus_reset; + device_class_set_legacy_reset(dc, aspeed_i2c_bus_reset); device_class_set_props(dc, aspeed_i2c_bus_properties); } @@ -1279,8 +1478,9 @@ 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[ARRAY_FIELD_EX32(bus->regs, I2CD_FUN_CTRL, - POOL_PAGE_SEL) * 0x100]; + &bus->controller->share_pool[ARRAY_FIELD_EX32(bus->regs, + I2CD_FUN_CTRL, + POOL_PAGE_SEL) * 0x100]; return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; } @@ -1296,9 +1496,11 @@ static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x40; aic->gap = 7; aic->bus_get_irq = aspeed_2400_i2c_bus_get_irq; + aic->has_share_pool = true; aic->pool_size = 0x800; aic->pool_base = 0x800; aic->bus_pool_base = aspeed_2400_i2c_bus_pool_base; + aic->mem_size = 0x1000; } static const TypeInfo aspeed_2400_i2c_info = { @@ -1314,7 +1516,7 @@ static qemu_irq aspeed_2500_i2c_bus_get_irq(AspeedI2CBus *bus) static uint8_t *aspeed_2500_i2c_bus_pool_base(AspeedI2CBus *bus) { - return &bus->controller->pool[bus->id * 0x10]; + return bus->pool; } static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) @@ -1328,11 +1530,12 @@ static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x40; aic->gap = 7; aic->bus_get_irq = aspeed_2500_i2c_bus_get_irq; - aic->pool_size = 0x100; + aic->pool_size = 0x10; aic->pool_base = 0x200; aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; aic->check_sram = true; aic->has_dma = true; + aic->mem_size = 0x1000; } static const TypeInfo aspeed_2500_i2c_info = { @@ -1346,11 +1549,6 @@ static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) return bus->irq; } -static uint8_t *aspeed_2600_i2c_bus_pool_base(AspeedI2CBus *bus) -{ - return &bus->controller->pool[bus->id * 0x20]; -} - static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1362,10 +1560,11 @@ static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x80; aic->gap = -1; /* no gap */ aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; - aic->pool_size = 0x200; + aic->pool_size = 0x20; aic->pool_base = 0xC00; - aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; + aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; aic->has_dma = true; + aic->mem_size = 0x1000; } static const TypeInfo aspeed_2600_i2c_info = { @@ -1385,10 +1584,11 @@ static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) aic->reg_size = 0x80; aic->gap = -1; /* no gap */ aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; - aic->pool_size = 0x200; + aic->pool_size = 0x20; aic->pool_base = 0xC00; - aic->bus_pool_base = aspeed_2600_i2c_bus_pool_base; + aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; aic->has_dma = true; + aic->mem_size = 0x10000; } static const TypeInfo aspeed_1030_i2c_info = { @@ -1397,6 +1597,33 @@ static const TypeInfo aspeed_1030_i2c_info = { .class_init = aspeed_1030_i2c_class_init, }; +static void aspeed_2700_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); + + dc->desc = "ASPEED 2700 I2C Controller"; + + aic->num_busses = 16; + aic->reg_size = 0x80; + aic->reg_gap_size = 0x80; + aic->gap = -1; /* no gap */ + aic->bus_get_irq = aspeed_2600_i2c_bus_get_irq; + aic->pool_size = 0x20; + aic->pool_gap_size = 0xe0; + aic->pool_base = 0x1a0; + aic->bus_pool_base = aspeed_2500_i2c_bus_pool_base; + aic->has_dma = true; + aic->mem_size = 0x2000; + aic->has_dma64 = true; +} + +static const TypeInfo aspeed_2700_i2c_info = { + .name = TYPE_ASPEED_2700_I2C, + .parent = TYPE_ASPEED_I2C, + .class_init = aspeed_2700_i2c_class_init, +}; + static void aspeed_i2c_register_types(void) { type_register_static(&aspeed_i2c_bus_info); @@ -1406,6 +1633,7 @@ static void aspeed_i2c_register_types(void) type_register_static(&aspeed_2500_i2c_info); type_register_static(&aspeed_2600_i2c_info); type_register_static(&aspeed_1030_i2c_info); + type_register_static(&aspeed_2700_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..67bfdef3b4 --- /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); + + device_class_set_legacy_reset(dc, 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 d4ba8146bf..4cf30b2c86 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -50,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() } @@ -64,7 +64,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); QSIMPLEQ_INIT(&bus->pending_masters); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); + vmstate_register_any(NULL, &vmstate_i2c_bus, bus); return bus; } @@ -185,22 +185,39 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, bool is_recv) 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)) { - I2CPendingMaster *node = g_new(struct I2CPendingMaster, 1); - node->bh = bh; - - QSIMPLEQ_INSERT_TAIL(&bus->pending_masters, node, entry); - + /* someone is already controlling the bus; wait for it to release it */ return; } - bus->bh = bh; + 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) @@ -234,16 +251,6 @@ void i2c_end_transfer(I2CBus *bus) g_free(node); } bus->broadcast = false; - - if (!QSIMPLEQ_EMPTY(&bus->pending_masters)) { - I2CPendingMaster *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); - } } int i2c_send(I2CBus *bus, uint8_t data) @@ -352,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..b1d00096ee 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), @@ -314,7 +314,7 @@ static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &exynos4210_i2c_vmstate; - dc->reset = exynos4210_i2c_reset; + device_class_set_legacy_reset(dc, exynos4210_i2c_reset); } static const TypeInfo exynos4210_i2c_type_info = { 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..c565fd5b8a 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), @@ -313,7 +313,7 @@ static void imx_i2c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &imx_i2c_vmstate; - dc->reset = imx_i2c_reset; + device_class_set_legacy_reset(dc, imx_i2c_reset); dc->realize = imx_i2c_realize; dc->desc = "i.MX I2C Controller"; } 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..06fbd18a78 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() @@ -110,7 +110,7 @@ static void microbit_i2c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = µbit_i2c_vmstate; - dc->reset = microbit_i2c_reset; + device_class_set_legacy_reset(dc, microbit_i2c_reset); dc->realize = microbit_i2c_realize; dc->desc = "Microbit I2C controller"; } diff --git a/hw/i2c/mpc_i2c.c b/hw/i2c/mpc_i2c.c index 845392505f..913d044ac1 100644 --- a/hw/i2c/mpc_i2c.c +++ b/hw/i2c/mpc_i2c.c @@ -20,10 +20,10 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" #include "hw/irq.h" -#include "qemu/module.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qom/object.h" +#include "trace.h" /* #define DEBUG_I2C */ @@ -82,7 +82,7 @@ struct MPCI2CState { uint8_t cr; uint8_t sr; uint8_t dr; - uint8_t dfssr; + uint8_t dfsrr; }; static bool mpc_i2c_is_enabled(MPCI2CState *s) @@ -224,8 +224,8 @@ static uint64_t mpc_i2c_read(void *opaque, hwaddr addr, unsigned size) break; } - DPRINTF("%s: addr " TARGET_FMT_plx " %02" PRIx32 "\n", __func__, - addr, value); + trace_mpc_i2c_read(addr, value); + return (uint64_t)value; } @@ -234,8 +234,8 @@ static void mpc_i2c_write(void *opaque, hwaddr addr, { MPCI2CState *s = opaque; - DPRINTF("%s: addr " TARGET_FMT_plx " val %08" PRIx64 "\n", __func__, - addr, value); + trace_mpc_i2c_write(addr, value); + switch (addr) { case MPC_I2C_ADR: s->adr = value & CADR_MASK; @@ -293,7 +293,7 @@ static void mpc_i2c_write(void *opaque, hwaddr addr, } break; case MPC_I2C_DFSRR: - s->dfssr = value; + s->dfsrr = value; break; default: DPRINTF("ERROR: Bad write addr 0x%x\n", (unsigned int)addr); @@ -312,14 +312,14 @@ 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), VMSTATE_UINT8(cr, MPCI2CState), VMSTATE_UINT8(sr, MPCI2CState), VMSTATE_UINT8(dr, MPCI2CState), - VMSTATE_UINT8(dfssr, MPCI2CState), + VMSTATE_UINT8(dfsrr, MPCI2CState), VMSTATE_END_OF_LIST() } }; @@ -329,7 +329,7 @@ static void mpc_i2c_realize(DeviceState *dev, Error **errp) MPCI2CState *i2c = MPC_I2C(dev); sysbus_init_irq(SYS_BUS_DEVICE(dev), &i2c->irq); memory_region_init_io(&i2c->iomem, OBJECT(i2c), &i2c_ops, i2c, - "mpc-i2c", 0x14); + "mpc-i2c", 0x15); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &i2c->iomem); i2c->bus = i2c_init_bus(dev, "i2c"); } @@ -339,21 +339,18 @@ static void mpc_i2c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &mpc_i2c_vmstate ; - dc->reset = mpc_i2c_reset; + device_class_set_legacy_reset(dc, mpc_i2c_reset); dc->realize = mpc_i2c_realize; dc->desc = "MPC I2C Controller"; } -static const TypeInfo mpc_i2c_type_info = { - .name = TYPE_MPC_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MPCI2CState), - .class_init = mpc_i2c_class_init, +static const TypeInfo mpc_i2c_types[] = { + { + .name = TYPE_MPC_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPCI2CState), + .class_init = mpc_i2c_class_init, + }, }; -static void mpc_i2c_register_types(void) -{ - type_register_static(&mpc_i2c_type_info); -} - -type_init(mpc_i2c_register_types) +DEFINE_TYPES(mpc_i2c_types) diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index e7e0ba66fe..22d68fc67d 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -1022,7 +1022,7 @@ static void npcm7xx_smbus_enter_reset(Object *obj, ResetType type) s->rx_cur = 0; } -static void npcm7xx_smbus_hold_reset(Object *obj) +static void npcm7xx_smbus_hold_reset(Object *obj, ResetType type) { NPCM7xxSMBusState *s = NPCM7XX_SMBUS(obj); @@ -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/omap_i2c.c b/hw/i2c/omap_i2c.c index e5d205dda5..e78505ebdd 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -521,7 +521,7 @@ static void omap_i2c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, omap_i2c_properties); - dc->reset = omap_i2c_reset; + device_class_set_legacy_reset(dc, omap_i2c_reset); /* Reason: pointer properties "iclk", "fclk" */ dc->user_creatable = false; dc->realize = omap_i2c_realize; 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 4071a88cfc..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) @@ -463,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); @@ -773,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); @@ -805,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); @@ -845,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); @@ -1087,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; } @@ -1106,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) { @@ -1268,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); @@ -1573,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 */ @@ -1617,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); @@ -1660,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/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index 75d50f1515..7b124a7e33 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -358,7 +358,7 @@ static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = ppc4xx_i2c_reset; + device_class_set_legacy_reset(dc, ppc4xx_i2c_reset); } static const TypeInfo ppc4xx_i2c_type_info = { diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 12c5741f38..e3e96d4a2d 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), @@ -143,7 +143,7 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); dc->realize = smbus_eeprom_realize; - dc->reset = smbus_eeprom_reset; + device_class_set_legacy_reset(dc, smbus_eeprom_reset); sc->receive_byte = eeprom_receive_byte; sc->write_data = eeprom_write_data; dc->vmsd = &vmstate_smbus_eeprom; @@ -151,19 +151,16 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) dc->user_creatable = false; } -static const TypeInfo smbus_eeprom_info = { - .name = TYPE_SMBUS_EEPROM, - .parent = TYPE_SMBUS_DEVICE, - .instance_size = sizeof(SMBusEEPROMDevice), - .class_init = smbus_eeprom_class_initfn, +static const TypeInfo smbus_eeprom_types[] = { + { + .name = TYPE_SMBUS_EEPROM, + .parent = TYPE_SMBUS_DEVICE, + .instance_size = sizeof(SMBusEEPROMDevice), + .class_init = smbus_eeprom_class_initfn, + }, }; -static void smbus_eeprom_register_types(void) -{ - type_register_static(&smbus_eeprom_info); -} - -type_init(smbus_eeprom_register_types) +DEFINE_TYPES(smbus_eeprom_types) void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf) { diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index ee50ba1f2c..208f263ac5 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -27,7 +27,7 @@ #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" @@ -50,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, @@ -80,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); @@ -93,17 +105,17 @@ 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) { - BusChild *kid; ICH9SMBState *s = ICH9_SMB_DEVICE(adev); BusState *bus = BUS(s->smb.smbus); - QTAILQ_FOREACH(kid, &bus->children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } + qbus_build_aml(bus, scope); } static void ich9_smb_class_init(ObjectClass *klass, void *data) @@ -128,28 +140,6 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) adevc->build_dev_aml = build_ich9_smb_aml; } -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; -} - static const TypeInfo ich9_smb_info = { .name = TYPE_ICH9_SMB_DEVICE, .parent = TYPE_PCI_DEVICE, diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c index feb3ec6333..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 { @@ -215,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 af181d43ee..f708a7ace1 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -1,5 +1,12 @@ # 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)" @@ -8,6 +15,17 @@ i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%0 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" @@ -17,6 +35,11 @@ aspeed_i2c_bus_write(uint32_t busid, uint64_t offset, unsigned size, uint64_t va aspeed_i2c_bus_send(const char *mode, int i, int count, uint8_t byte) "%s send %d/%d 0x%02x" aspeed_i2c_bus_recv(const char *mode, int i, int count, uint8_t byte) "%s recv %d/%d 0x%02x" +# mpc_i2c.c + +mpc_i2c_read(uint64_t addr, uint32_t value) "[0x%" PRIx64 "] -> 0x%02" PRIx32 +mpc_i2c_write(uint64_t addr, uint32_t value) "[0x%" PRIx64 "] <- 0x%02" PRIx32 + # npcm7xx_smbus.c npcm7xx_smbus_read(const char *id, uint64_t offset, uint64_t value, unsigned size) "%s offset: 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u" diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c deleted file mode 100644 index 3a04ba3969..0000000000 --- a/hw/i2c/versatile_i2c.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * ARM SBCon two-wire serial bus interface (I2C bitbang) - * a.k.a. ARM Versatile I2C controller - * - * Copyright (c) 2006-2007 CodeSourcery. - * Copyright (c) 2012 Oskar Andero - * - * This file is derived from hw/realview.c by Paul Brook - * - * 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/i2c/arm_sbcon_i2c.h" -#include "hw/registerfields.h" -#include "qemu/log.h" -#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) -REG32(CONTROL_CLR, 4) - -#define SCL BIT(0) -#define SDA BIT(1) - -static uint64_t versatile_i2c_read(void *opaque, hwaddr offset, - unsigned size) -{ - VersatileI2CState *s = (VersatileI2CState *)opaque; - - switch (offset) { - case A_CONTROL_SET: - return (s->out & 1) | (s->in << 1); - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - return -1; - } -} - -static void versatile_i2c_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - VersatileI2CState *s = (VersatileI2CState *)opaque; - - switch (offset) { - case A_CONTROL_SET: - s->out |= value & 3; - break; - case A_CONTROL_CLR: - s->out &= ~value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad offset 0x%x\n", __func__, (int)offset); - } - bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0); - 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, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void versatile_i2c_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - VersatileI2CState *s = VERSATILE_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, - "arm_sbcon_i2c", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); -} - -static const TypeInfo versatile_i2c_info = { - .name = TYPE_VERSATILE_I2C, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VersatileI2CState), - .instance_init = versatile_i2c_init, -}; - -static void versatile_i2c_register_types(void) -{ - type_register_static(&versatile_i2c_info); -} - -type_init(versatile_i2c_register_types) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index c25a2a4174..72f2334d99 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -26,14 +26,13 @@ config PC imply QXL imply SEV imply SGX - imply SGA imply TEST_DEVICES imply TPM_CRB imply TPM_TIS_ISA imply VGA_PCI imply VIRTIO_VGA imply NVDIMM - select FDC_ISA + imply FDC_ISA select I8259 select I8254 select PCKBD @@ -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 @@ -66,36 +66,43 @@ config PC_ACPI config I440FX bool + default y + depends on I386 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 + default y + depends on I386 + 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 config Q35 bool + default y + depends on I386 imply VTD imply AMD_IOMMU imply E1000E_PCI_EXPRESS imply VMPORT imply VMMOUSE + imply IOMMUFD select PC_PCI select PC_ACPI select PCI_EXPRESS_Q35 @@ -107,6 +114,9 @@ config Q35 config MICROVM bool + default y + depends on I386 && FDT + select DEVICE_TREE select SERIAL_ISA # for serial_hds_isa_init() select ISA_BUS select APIC @@ -119,6 +129,16 @@ config MICROVM select USB_XHCI_SYSBUS select I8254 +config NITRO_ENCLAVE + default y + depends on I386 && FDT # for MICROVM + depends on LIBCBOR && GNUTLS # for EIF and VIRTIO_NSM + depends on VHOST_USER # for VHOST_USER_VSOCK + select EIF + select MICROVM + select VHOST_USER_VSOCK + select VIRTIO_NSM + config X86_IOMMU bool depends on PC @@ -138,13 +158,18 @@ config VMMOUSE bool depends on VMPORT +config XEN_EMU + bool + default y + depends on KVM && I386 + config XBOX bool select PC_PCI select PC_ACPI select ACPI_SMBUS select PCI_I440FX - select PIIX3 + select PIIX select IDE_PIIX select DIMM select AC97 diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 44b20f175f..da1b28659a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -27,7 +27,7 @@ #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" @@ -55,10 +55,10 @@ #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" @@ -76,7 +76,6 @@ #include "hw/acpi/hmat.h" #include "hw/acpi/viot.h" -#include "hw/acpi/cxl.h" #include CONFIG_DEVICES @@ -85,7 +84,6 @@ * a little bit, there should be plenty of free space since the DSDT * shrunk by ~1.5k between QEMU 2.0 and QEMU 2.1. */ -#define ACPI_BUILD_LEGACY_CPU_AML_SIZE 97 #define ACPI_BUILD_ALIGN_SIZE 0x1000 #define ACPI_BUILD_TABLE_SIZE 0x20000 @@ -117,8 +115,6 @@ typedef struct AcpiMiscInfo { #ifdef CONFIG_TPM TPMVersion tpm_version; #endif - const unsigned char *dsdt_code; - unsigned dsdt_size; } AcpiMiscInfo; typedef struct FwCfgTPMConfig { @@ -198,21 +194,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; @@ -247,10 +232,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) { @@ -367,9 +348,13 @@ Aml *aml_pci_device_dsm(void) { Aml *params = aml_local(0); Aml *pkg = aml_package(2); - aml_append(pkg, aml_name("BSEL")); - aml_append(pkg, aml_name("ASUN")); + 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)) @@ -378,6 +363,104 @@ Aml *aml_pci_device_dsm(void) 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; @@ -388,164 +471,193 @@ 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); - /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ - int adr = slot << 16 | func; - bool hotpluggbale_slot = false; - bool bridge_in_acpi = false; - bool cold_plugged_bridge = false; + int adr = slot << 16 | PCI_FUNC(devfn); - if (pdev) { - pc = PCI_DEVICE_GET_CLASS(pdev); - dc = DEVICE_GET_CLASS(pdev); + if (is_devfn_ignored_hotplug(devfn, bus)) { + continue; + } - /* - * 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; - - hotpluggbale_slot = bsel && dc->hotpluggable && - !cold_plugged_bridge; - - /* - * allow describing coldplugged bridges in ACPI even if they are not - * on function 0, as they are not unpluggable, for all other devices - * generate description only for function 0 per slot, and for other - * functions if device on function provides its own AML - */ - if (func && !bridge_in_acpi && !get_dev_aml_func(DEVICE(pdev))) { - continue; - } + if (bus->devices[devfn]) { + dev = aml_scope("S%.02X", devfn); } else { - /* - * hotplug is supported only for non-multifunction device - * so generate device description only for function 0 - */ - if (bsel && !func) { - if (pci_bus_is_express(bus) && slot > 0) { - break; - } - /* mark it as empty hotpluggable slot */ - hotpluggbale_slot = true; - } else { - continue; - } + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + } + + /* + * 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 = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); + PCIDevice *pdev = bus->devices[devfn]; + + if (!pdev || is_devfn_ignored_generic(devfn, bus)) { + continue; } /* start to compose PCI device descriptor */ dev = aml_device("S%.02X", devfn); aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - if (bsel) { - /* - * Can't declare _SUN here for every device as it changes 'slot' - * enumeration order in linux kernel, so use another variable for it - */ - aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); - aml_append(dev, aml_pci_device_dsm()); - } - - call_dev_aml_func(DEVICE(pdev), dev); - - bridge_in_acpi = cold_plugged_bridge && pcihp_bridge_en; - if (bridge_in_acpi) { - /* - * device is coldplugged bridge, - * add child device descriptions into its scope - */ - PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); - - build_append_pci_bus_devices(dev, sec_bus, pcihp_bridge_en); - } - - if (hotpluggbale_slot) { - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - /* add _EJ0 to make slot hotpluggable */ - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - - build_append_pcihp_notify_entry(notify_method, slot); + 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)); } /* device descriptor has been composed, add it into parent context */ aml_append(parent_scope, dev); } +} - if (bsel) { - aml_append(parent_scope, notify_method); +static bool build_append_notfication_callback(Aml *parent_scope, + const PCIBus *bus) +{ + Aml *method; + PCIBus *sec; + QObject *bsel; + int nr_notifiers = 0; + GQueue *pcnt_bus_list = g_queue_new(); + + 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); + } } - /* 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. + /* + * 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. */ - if (bsel || pcihp_bridge_en) { - method = aml_method("PCNT", 0, AML_NOTSERIALIZED); + 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)); + /* If bus supports hotplug select it and notify about local events */ + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); + if (bsel) { + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); - aml_append(method, aml_call2("DVNT", aml_name("PCIU"), - aml_int(1))); /* Device Check */ - aml_append(method, aml_call2("DVNT", aml_name("PCID"), - aml_int(3))); /* Eject Request */ - } - - /* 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); + 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, *UUID, *ifctx, *ifctx1; + 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 *rev = aml_arg(1); 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))); @@ -555,29 +667,9 @@ static Aml *aml_pci_pdsm(void) /* get supported functions */ ifctx = aml_if(aml_equal(func, zero)); { - uint8_t byte_list[1] = { 0 }; /* nothing supported yet */ - aml_append(ifctx, aml_store(aml_buffer(1, byte_list), ret)); + build_append_pci_dsm_func0_common(ifctx, ret); + aml_append(ifctx, aml_store(zero, caps)); - - /* - * 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(ret)); - } - aml_append(ifctx, ifctx1); - - ifctx1 = aml_if(aml_lless(rev, aml_int(2))); - { - /* call is for unsupported REV, bail out */ - aml_append(ifctx1, aml_return(ret)); - } - aml_append(ifctx, ifctx1); - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); /* @@ -634,120 +726,45 @@ static Aml *aml_pci_pdsm(void) return method; } -/** - * build_prt_entry: - * @link_name: link name for PCI route entry - * - * build AML package containing a PCI route entry for @link_name - */ -static Aml *build_prt_entry(const char *link_name) -{ - Aml *a_zero = aml_int(0); - Aml *pkg = aml_package(4); - aml_append(pkg, a_zero); - aml_append(pkg, a_zero); - aml_append(pkg, aml_name("%s", link_name)); - aml_append(pkg, a_zero); - return pkg; -} - /* - * initialize_route - Initialize the interrupt routing rule - * through a specific LINK: - * if (lnk_idx == idx) - * route using link 'link_name' - */ -static Aml *initialize_route(Aml *route, const char *link_name, - Aml *lnk_idx, int idx) -{ - Aml *if_ctx = aml_if(aml_equal(lnk_idx, aml_int(idx))); - Aml *pkg = build_prt_entry(link_name); - - aml_append(if_ctx, aml_store(pkg, route)); - - return if_ctx; -} - -/* - * build_prt - Define interrupt rounting rules + * build_prt - Define interrupt routing rules * * 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]". + * The hash function is: (slot + pin) & 3 -> "LNK[D|A|B|C]". * */ static Aml *build_prt(bool is_pci0_prt) { - Aml *method, *while_ctx, *pin, *res; + const int nroutes = 128; + Aml *rt_pkg, *method; + int pin; method = aml_method("_PRT", 0, AML_NOTSERIALIZED); - res = aml_local(0); - pin = aml_local(1); - aml_append(method, aml_store(aml_package(128), res)); - aml_append(method, aml_store(aml_int(0), pin)); + assert(nroutes < 256); + rt_pkg = aml_package(nroutes); - /* while (pin < 128) */ - while_ctx = aml_while(aml_lless(pin, aml_int(128))); - { - Aml *slot = aml_local(2); - Aml *lnk_idx = aml_local(3); - Aml *route = aml_local(4); + for (pin = 0; pin < nroutes; pin++) { + Aml *pkg = aml_package(4); + int slot = pin >> 2; - /* slot = pin >> 2 */ - aml_append(while_ctx, - aml_store(aml_shiftright(pin, aml_int(2), NULL), slot)); - /* lnk_idx = (slot + pin) & 3 */ - aml_append(while_ctx, - aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL), - lnk_idx)); - - /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */ - aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); - if (is_pci0_prt) { - Aml *if_device_1, *if_pin_4, *else_pin_4; - - /* device 1 is the power-management device, needs SCI */ - if_device_1 = aml_if(aml_equal(lnk_idx, aml_int(1))); - { - if_pin_4 = aml_if(aml_equal(pin, aml_int(4))); - { - aml_append(if_pin_4, - aml_store(build_prt_entry("LNKS"), route)); - } - aml_append(if_device_1, if_pin_4); - else_pin_4 = aml_else(); - { - aml_append(else_pin_4, - aml_store(build_prt_entry("LNKA"), route)); - } - aml_append(if_device_1, else_pin_4); - } - aml_append(while_ctx, if_device_1); + aml_append(pkg, aml_int((slot << 16) | 0xFFFF)); + aml_append(pkg, aml_int(pin & 3)); + /* device 1 is the power-management device, needs SCI */ + if (is_pci0_prt && pin == 4) { + aml_append(pkg, aml_name("%s", "LNKS")); } else { - aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx, 1)); + static const char link_name[][5] = {"LNKD", "LNKA", "LNKB", "LNKC"}; + int hash = (slot + pin) & 3; + aml_append(pkg, aml_name("%s", link_name[hash])); } - aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2)); - aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3)); - - /* route[0] = 0x[slot]FFFF */ - aml_append(while_ctx, - aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF), - NULL), - aml_index(route, aml_int(0)))); - /* route[1] = pin & 3 */ - aml_append(while_ctx, - aml_store(aml_and(pin, aml_int(3), NULL), - aml_index(route, aml_int(1)))); - /* res[pin] = route */ - aml_append(while_ctx, aml_store(route, aml_index(res, pin))); - /* pin++ */ - aml_append(while_ctx, aml_increment(pin)); + aml_append(pkg, aml_int(0)); + aml_append(rt_pkg, pkg); } - aml_append(method, while_ctx); - /* return res*/ - aml_append(method, aml_return(res)); + + aml_append(method, aml_return(rt_pkg)); return method; } @@ -1314,8 +1331,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); @@ -1326,8 +1344,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); - Object *q35 = object_resolve_type_unambiguous(TYPE_Q35_HOST_DEVICE); + 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; @@ -1358,8 +1377,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, 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); @@ -1372,9 +1391,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, 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)); @@ -1389,14 +1408,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)); @@ -1443,8 +1462,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, .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"); + build_cpus_aml(dsdt, machine, opts, pc_madt_cpu_entry, + pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02", + AML_SYSTEM_IO); } if (pcms->memhp_io_base && nr_mem) { @@ -1454,11 +1474,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } 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); uint8_t numa_node = pci_bus_numa_node(bus); + uint32_t uid; /* look only for expander root buses */ if (!pci_bus_is_root(bus)) { @@ -1469,6 +1490,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, root_bus_limit = bus_num - 1; } + uid = object_property_get_uint(OBJECT(bus), "acpi_uid", + &error_fatal); scope = aml_scope("\\_SB"); if (pci_bus_is_cxl(bus)) { @@ -1476,17 +1499,15 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } else { dev = aml_device("PC%.02X", bus_num); } - aml_append(dev, aml_name_decl("_UID", aml_int(bus_num))); + aml_append(dev, aml_name_decl("_UID", aml_int(uid))); 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"))); @@ -1679,11 +1700,14 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, Object *pci_host = acpi_get_i386_pci_host(); if (pci_host) { - PCIBus *bus = PCI_HOST_BRIDGE(pci_host)->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); - aml_append(sb_scope, scope); + build_append_pci_bus_devices(ascope, pbus); + if (object_property_find(OBJECT(pbus), ACPI_PCIHP_PROP_BSEL)) { + build_append_pcihp_slots(ascope, pbus); + } + aml_append(sb_scope, ascope); } } @@ -1732,13 +1756,26 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en || pm->pcihp_root_en) { + bool has_pcnt; + + Object *pci_host = acpi_get_i386_pci_host(); + PCIBus *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); - aml_append(method, - aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); - aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); - aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + if (has_pcnt) { + aml_append(method, + aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); + aml_append(method, aml_call0("\\_SB.PCI0.PCNT")); + aml_append(method, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + } aml_append(scope, method); } aml_append(dsdt, scope); @@ -1831,12 +1868,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 }; @@ -1944,6 +1977,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_srat_memory(table_data, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } + build_srat_generic_affinity_structures(table_data); + /* * Entry is required for Windows to enable memory hotplug in OS * and for Linux to enable SWIOTLB when booted with less than @@ -1952,9 +1987,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); } @@ -1962,7 +1998,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) @@ -2220,30 +2256,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 @@ -2263,54 +2292,94 @@ 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. - */ - if (x86_iommu_ir_supported(x86_iommu_get_default())) { - ivhd_table_len += 8; - } - - /* IVHD length */ - build_append_int_noprefix(table_data, ivhd_table_len, 2); - /* DeviceID */ - build_append_int_noprefix(table_data, s->devid, 2); - /* Capability offset */ - build_append_int_noprefix(table_data, s->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 Feature Reporting */ - build_append_int_noprefix(table_data, - (48UL << 30) | /* HATS */ - (48UL << 28) | /* GATS */ - (1UL << 2) | /* GTSup */ - (1UL << 6), /* GASup */ - 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. + * 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())) { - build_append_int_noprefix(table_data, + 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_blob->len + 24, 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->mr_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 Feature Reporting */ + 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); + + /* 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->mr_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); } @@ -2320,7 +2389,6 @@ struct AcpiBuildState { MemoryRegion *table_mr; /* Is table patched? */ uint8_t patched; - void *rsdp; MemoryRegion *rsdp_mr; MemoryRegion *linker_mr; } AcpiBuildState; @@ -2356,17 +2424,15 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); DeviceState *iommu = pcms->iommu; GArray *table_offsets; - unsigned facs, dsdt, rsdt, fadt; + unsigned facs, dsdt, rsdt; AcpiPmInfo pm; AcpiMiscInfo misc; AcpiMcfgInfo mcfg; Range pci_hole = {}, pci_hole64 = {}; uint8_t *u; - size_t aml_len = 0; GArray *tables_blob = tables->table_data; AcpiSlicOem slic_oem = { .id = NULL, .table_id = NULL }; Object *vmgenid_dev; @@ -2412,24 +2478,16 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) build_dsdt(tables_blob, tables->linker, &pm, &misc, &pci_hole, &pci_hole64, machine); - /* Count the size of the DSDT and SSDT, we will need it for legacy - * sizing of ACPI tables. - */ - aml_len += tables_blob->len - dsdt; - /* ACPI tables pointed to by RSDT */ - fadt = tables_blob->len; acpi_add_table(table_offsets, tables_blob); pm.fadt.facs_tbl_offset = &facs; pm.fadt.dsdt_tbl_offset = &dsdt; pm.fadt.xdsdt_tbl_offset = &dsdt; build_fadt(tables_blob, tables->linker, &pm.fadt, oem_id, oem_table_id); - aml_len += tables_blob->len - fadt; 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 { @@ -2537,16 +2595,6 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) .rsdt_tbl_offset = &rsdt, }; build_rsdp(tables->rsdp, tables->linker, &rsdp_data); - if (!pcmc->rsdp_in_ram) { - /* We used to allocate some extra space for RSDP revision 2 but - * only used the RSDP revision 0 space. The extra bytes were - * zeroed out and not used. - * Here we continue wasting those extra 16 bytes to make sure we - * don't break migration for machine types 2.2 and older due to - * RSDP blob size mismatch. - */ - build_append_int_noprefix(tables->rsdp, 0, 16); - } } /* We'll expose it all to Guest so we want to reduce @@ -2556,47 +2604,9 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) * too simple to be enough. 4k turned out to be too small an * alignment very soon, and in fact it is almost impossible to * keep the table size stable for all (max_cpus, max_memory_slots) - * combinations. So the table size is always 64k for pc-i440fx-2.1 - * and we give an error if the table grows beyond that limit. - * - * We still have the problem of migrating from "-M pc-i440fx-2.0". For - * that, we exploit the fact that QEMU 2.1 generates _smaller_ tables - * than 2.0 and we can always pad the smaller tables with zeros. We can - * then use the exact size of the 2.0 tables. - * - * All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration. + * combinations. */ - if (pcmc->legacy_acpi_table_size) { - /* Subtracting aml_len gives the size of fixed tables. Then add the - * size of the PIIX4 DSDT/SSDT in QEMU 2.0. - */ - int legacy_aml_len = - pcmc->legacy_acpi_table_size + - ACPI_BUILD_LEGACY_CPU_AML_SIZE * x86ms->apic_id_limit; - 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) { - /* 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."); - } - 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) { - /* 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."); - } - acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); - } + acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE); acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); @@ -2634,11 +2644,7 @@ static void acpi_build_update(void *build_opaque) acpi_ram_update(build_state->table_mr, tables.table_data); - if (build_state->rsdp) { - memcpy(build_state->rsdp, tables.rsdp->data, acpi_data_len(tables.rsdp)); - } else { - acpi_ram_update(build_state->rsdp_mr, tables.rsdp); - } + 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); @@ -2654,7 +2660,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() }, @@ -2663,7 +2669,6 @@ static const VMStateDescription vmstate_acpi_build = { void acpi_setup(void) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(pcms); AcpiBuildTables tables; AcpiBuildState *build_state; @@ -2725,25 +2730,9 @@ void acpi_setup(void) tables.vmgenid); } - if (!pcmc->rsdp_in_ram) { - /* - * Keep for compatibility with old machine types. - * Though RSDP is small, its contents isn't immutable, so - * we'll update it along with the rest of tables on guest access. - */ - uint32_t rsdp_size = acpi_data_len(tables.rsdp); - - build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); - fw_cfg_add_file_callback(x86ms->fw_cfg, ACPI_BUILD_RSDP_FILE, - acpi_build_update, NULL, build_state, - build_state->rsdp, rsdp_size, true); - build_state->rsdp_mr = NULL; - } else { - build_state->rsdp = NULL; - build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, - build_state, tables.rsdp, - ACPI_BUILD_RSDP_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); diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 4aaafbdd7b..0cc2919bb8 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,24 +93,26 @@ 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); /* Local APIC Address */ build_append_int_noprefix(table_data, APIC_DEFAULT_ADDRESS, 4); - build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ + /* Flags. bit 0: PCAT_COMPAT */ + build_append_int_noprefix(table_data, + x86ms->pic != ON_OFF_AUTO_OFF ? 1 : 0 , 4); 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 +124,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..af0f4da1f6 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -31,6 +31,8 @@ #include "hw/i386/apic_internal.h" #include "trace.h" #include "hw/i386/apic-msidef.h" +#include "hw/qdev-properties.h" +#include "kvm/kvm_i386.h" /* used AMD-Vi MMIO registers */ const char *amdvi_mmio_low[] = { @@ -59,8 +61,9 @@ struct AMDVIAddressSpace { uint8_t bus_num; /* bus number */ uint8_t devfn; /* device function */ AMDVIState *iommu_state; /* AMDVI - one per machine */ - MemoryRegion root; /* AMDVI Root memory map region */ + MemoryRegion root; /* AMDVI Root memory map region */ IOMMUMemoryRegion iommu; /* Device's address translation region */ + MemoryRegion iommu_nodma; /* Alias of shared nodma memory region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */ AddressSpace as; /* device's corresponding address space */ }; @@ -74,6 +77,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 +272,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, @@ -346,12 +359,12 @@ static void amdvi_update_iotlb(AMDVIState *s, uint16_t devid, uint64_t gpa, IOMMUTLBEntry to_cache, uint16_t domid) { - AMDVIIOTLBEntry *entry = g_new(AMDVIIOTLBEntry, 1); - uint64_t *key = g_new(uint64_t, 1); - uint64_t gfn = gpa >> AMDVI_PAGE_SHIFT_4K; - /* don't cache erroneous translations */ if (to_cache.perm != IOMMU_NONE) { + AMDVIIOTLBEntry *entry = g_new(AMDVIIOTLBEntry, 1); + uint64_t *key = g_new(uint64_t, 1); + uint64_t gfn = gpa >> AMDVI_PAGE_SHIFT_4K; + trace_amdvi_cache_update(domid, PCI_BUS_NUM(devid), PCI_SLOT(devid), PCI_FUNC(devid), gpa, to_cache.translated_addr); @@ -419,6 +432,12 @@ static void amdvi_complete_ppr(AMDVIState *s, uint64_t *cmd) trace_amdvi_ppr_exec(); } +static void amdvi_intremap_inval_notify_all(AMDVIState *s, bool global, + uint32_t index, uint32_t mask) +{ + x86_iommu_iec_notify_all(X86_IOMMU_DEVICE(s), global, index, mask); +} + static void amdvi_inval_all(AMDVIState *s, uint64_t *cmd) { if (extract64(cmd[0], 0, 60) || cmd[1]) { @@ -426,6 +445,9 @@ static void amdvi_inval_all(AMDVIState *s, uint64_t *cmd) s->cmdbuf + s->cmdbuf_head); } + /* Notify global invalidation */ + amdvi_intremap_inval_notify_all(s, true, 0, 0); + amdvi_iotlb_reset(s); trace_amdvi_all_inval(); } @@ -474,6 +496,9 @@ static void amdvi_inval_inttable(AMDVIState *s, uint64_t *cmd) return; } + /* Notify global invalidation */ + amdvi_intremap_inval_notify_all(s, true, 0, 0); + trace_amdvi_intr_inval(); } @@ -767,7 +792,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 +1180,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 +1276,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 +1393,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; @@ -1401,6 +1426,7 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) AMDVIState *s = opaque; AMDVIAddressSpace **iommu_as, *amdvi_dev_as; int bus_num = pci_bus_num(bus); + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); iommu_as = s->address_spaces[bus_num]; @@ -1425,13 +1451,13 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) * Memory region relationships looks like (Address range shows * only lower 32 bits to make it short in length...): * - * |-----------------+-------------------+----------| - * | Name | Address range | Priority | - * |-----------------+-------------------+----------+ - * | amdvi_root | 00000000-ffffffff | 0 | - * | amdvi_iommu | 00000000-ffffffff | 1 | - * | amdvi_iommu_ir | fee00000-feefffff | 64 | - * |-----------------+-------------------+----------| + * |--------------------+-------------------+----------| + * | Name | Address range | Priority | + * |--------------------+-------------------+----------+ + * | amdvi-root | 00000000-ffffffff | 0 | + * | amdvi-iommu_nodma | 00000000-ffffffff | 0 | + * | amdvi-iommu_ir | fee00000-feefffff | 1 | + * |--------------------+-------------------+----------| */ memory_region_init_iommu(&amdvi_dev_as->iommu, sizeof(amdvi_dev_as->iommu), @@ -1441,20 +1467,42 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) memory_region_init(&amdvi_dev_as->root, OBJECT(s), "amdvi_root", UINT64_MAX); address_space_init(&amdvi_dev_as->as, &amdvi_dev_as->root, name); - memory_region_init_io(&amdvi_dev_as->iommu_ir, OBJECT(s), - &amdvi_ir_ops, s, "amd_iommu_ir", - AMDVI_INT_ADDR_SIZE); - memory_region_add_subregion_overlap(&amdvi_dev_as->root, - AMDVI_INT_ADDR_FIRST, - &amdvi_dev_as->iommu_ir, - 64); memory_region_add_subregion_overlap(&amdvi_dev_as->root, 0, MEMORY_REGION(&amdvi_dev_as->iommu), - 1); + 0); + + /* Build the DMA Disabled alias to shared memory */ + memory_region_init_alias(&amdvi_dev_as->iommu_nodma, OBJECT(s), + "amdvi-sys", &s->mr_sys, 0, + memory_region_size(&s->mr_sys)); + memory_region_add_subregion_overlap(&amdvi_dev_as->root, 0, + &amdvi_dev_as->iommu_nodma, + 0); + /* Build the Interrupt Remapping alias to shared memory */ + memory_region_init_alias(&amdvi_dev_as->iommu_ir, OBJECT(s), + "amdvi-ir", &s->mr_ir, 0, + memory_region_size(&s->mr_ir)); + memory_region_add_subregion_overlap(MEMORY_REGION(&amdvi_dev_as->iommu), + AMDVI_INT_ADDR_FIRST, + &amdvi_dev_as->iommu_ir, 1); + + if (!x86_iommu->pt_supported) { + memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); + memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), + true); + } else { + memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), + false); + memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, true); + } } 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 +1554,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 +1613,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,39 +1626,53 @@ 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); /* set up MMIO */ - memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", - AMDVI_MMIO_SIZE); + memory_region_init_io(&s->mr_mmio, OBJECT(s), &mmio_mem_ops, s, + "amdvi-mmio", AMDVI_MMIO_SIZE); + memory_region_add_subregion(get_system_memory(), AMDVI_BASE_ADDR, + &s->mr_mmio); - 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); + /* Create the share memory regions by all devices */ + memory_region_init(&s->mr_sys, OBJECT(s), "amdvi-sys", UINT64_MAX); + + /* set up the DMA disabled memory region */ + memory_region_init_alias(&s->mr_nodma, OBJECT(s), + "amdvi-nodma", get_system_memory(), 0, + memory_region_size(get_system_memory())); + memory_region_add_subregion_overlap(&s->mr_sys, 0, + &s->mr_nodma, 0); + + /* set up the Interrupt Remapping memory region */ + memory_region_init_io(&s->mr_ir, OBJECT(s), &amdvi_ir_ops, + s, "amdvi-ir", AMDVI_INT_ADDR_SIZE); + memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST, + &s->mr_ir, 1); + + /* AMD IOMMU with x2APIC mode requires xtsup=on */ + if (x86ms->apic_id_limit > 255 && !s->xtsup) { + error_report("AMD IOMMU with x2APIC confguration requires xtsup=on"); + exit(EXIT_FAILURE); + } + if (s->xtsup) { + if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) { + error_report("AMD IOMMU xtsup=on requires support on the KVM side"); + exit(EXIT_FAILURE); + } + } + + 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 @@ -1603,7 +1690,7 @@ static void amdvi_sysbus_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *dc_class = X86_IOMMU_DEVICE_CLASS(klass); - dc->reset = amdvi_sysbus_reset; + device_class_set_legacy_reset(dc, amdvi_sysbus_reset); dc->vmsd = &vmstate_amdvi_sysbus; dc->hotpluggable = false; dc_class->realize = amdvi_sysbus_realize; @@ -1612,6 +1699,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 +1713,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..e0dac4d9a9 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 */ @@ -353,7 +353,10 @@ struct AMDVIState { uint32_t pprlog_head; /* ppr log head */ uint32_t pprlog_tail; /* ppr log tail */ - MemoryRegion mmio; /* MMIO region */ + MemoryRegion mr_mmio; /* MMIO region */ + MemoryRegion mr_sys; + MemoryRegion mr_nodma; + MemoryRegion mr_ir; uint8_t mmior[AMDVI_MMIO_SIZE]; /* read/write MMIO */ uint8_t w1cmask[AMDVI_MMIO_SIZE]; /* read/write 1 clear mask */ uint8_t romask[AMDVI_MMIO_SIZE]; /* MMIO read/only mask */ @@ -367,6 +370,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 06970ac44a..3e848fb69c 100644 --- a/hw/i386/e820_memory_layout.c +++ b/hw/i386/e820_memory_layout.c @@ -11,22 +11,29 @@ #include "e820_memory_layout.h" static size_t e820_entries; -struct e820_entry *e820_table; +static struct e820_entry *e820_table; +static gboolean e820_done; -int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) +void e820_add_entry(uint64_t address, uint64_t length, uint32_t type) { + assert(!e820_done); + /* 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); e820_table[e820_entries].type = cpu_to_le32(type); e820_entries++; - - return e820_entries; } -int e820_get_num_entries(void) +int e820_get_table(struct e820_entry **table) { + e820_done = true; + + if (table) { + *table = e820_table; + } + return e820_entries; } diff --git a/hw/i386/e820_memory_layout.h b/hw/i386/e820_memory_layout.h index 7c239aa033..b50acfa201 100644 --- a/hw/i386/e820_memory_layout.h +++ b/hw/i386/e820_memory_layout.h @@ -22,13 +22,9 @@ struct e820_entry { uint32_t type; } QEMU_PACKED __attribute((__aligned__(4))); -extern struct e820_entry *e820_table; - -int e820_add_entry(uint64_t address, uint64_t length, uint32_t type); -int e820_get_num_entries(void); +void e820_add_entry(uint64_t address, uint64_t length, uint32_t type); bool e820_get_entry(int index, uint32_t type, uint64_t *address, uint64_t *length); - - +int e820_get_table(struct e820_entry **table); #endif diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index 72a42f3c66..0e4494627c 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}; @@ -47,27 +48,49 @@ const char *fw_cfg_arch_key_name(uint16_t key) return NULL; } -void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg) +/* Add etc/e820 late, once all regions should be present */ +void fw_cfg_add_e820(FWCfgState *fw_cfg) +{ + struct e820_entry *table; + int nr_e820 = e820_get_table(&table); + + fw_cfg_add_file(fw_cfg, "etc/e820", table, nr_e820 * sizeof(*table)); +} + +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); + int nr_e820; + + if (pcmc->smbios_defaults) { + /* These values are guest ABI, do not change */ + smbios_set_defaults("QEMU", mc->desc, mc->name); + } /* 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 */ - mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries()); - for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) { + nr_e820 = e820_get_table(NULL); + mem_array = g_malloc0(sizeof(*mem_array) * nr_e820); + for (i = 0, array_count = 0; i < nr_e820; i++) { uint64_t addr, len; if (e820_get_entry(i, E820_RAM, &addr, &len)) { @@ -76,7 +99,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); @@ -126,9 +149,6 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, #endif fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); - fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, - sizeof(struct e820_entry) * e820_get_num_entries()); - fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg)); /* allocate memory for the NUMA channel: one (64bit) word for the number * of nodes, one word for each VCPU->node and one word for each node to @@ -190,6 +210,7 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) fw_cfg_add_file(fw_cfg, "etc/msr_feature_control", val, sizeof(*val)); } +#ifdef CONFIG_ACPI void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) { /* @@ -216,3 +237,4 @@ void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg) aml_append(dev, aml_name_decl("_CRS", crs)); aml_append(scope, dev); } +#endif diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h index 86ca7c1c0c..e560fd7be8 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 @@ -22,8 +23,10 @@ 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); +void fw_cfg_add_e820(FWCfgState *fw_cfg); #endif 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 d025ef2873..4c0d1d7d47 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -52,7 +52,7 @@ /* * PCI bus number (or SID) is not reliable since the device is usaully - * initalized before guest can configure the PCI bridge + * initialized before guest can configure the PCI bridge * (SECONDARY_BUS_NUMBER). */ struct vtd_as_key { @@ -61,11 +61,17 @@ struct vtd_as_key { uint32_t pasid; }; +/* bus/devfn is PCI device's real BDF not the aliased one */ +struct vtd_hiod_key { + PCIBus *bus; + uint8_t devfn; +}; + struct vtd_iotlb_key { uint64_t gfn; uint32_t pasid; - uint32_t level; uint16_t sid; + uint8_t level; }; static void vtd_address_space_refresh_all(IntelIOMMUState *s); @@ -221,10 +227,11 @@ static gboolean vtd_iotlb_equal(gconstpointer v1, gconstpointer v2) static guint vtd_iotlb_hash(gconstpointer 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 key->gfn | ((key->sid) << VTD_IOTLB_SID_SHIFT) | - (key->level) << VTD_IOTLB_LVL_SHIFT | - (key->pasid) << VTD_IOTLB_PASID_SHIFT; + return (guint)((hash64 >> 32) ^ (hash64 & 0xffffffffU)); } static gboolean vtd_as_equal(gconstpointer v1, gconstpointer v2) @@ -249,6 +256,25 @@ static guint vtd_as_hash(gconstpointer v) return (guint)(value << 8 | key->devfn); } +/* Same implementation as vtd_as_hash() */ +static guint vtd_hiod_hash(gconstpointer v) +{ + return vtd_as_hash(v); +} + +static gboolean vtd_hiod_equal(gconstpointer v1, gconstpointer v2) +{ + const struct vtd_hiod_key *key1 = v1; + const struct vtd_hiod_key *key2 = v2; + + return (key1->bus == key2->bus) && (key1->devfn == key2->devfn); +} + +static void vtd_hiod_destroy(gpointer v) +{ + object_unref(v); +} + static gboolean vtd_hash_remove_by_domain(gpointer key, gpointer value, gpointer user_data) { @@ -332,7 +358,7 @@ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, { struct vtd_iotlb_key key; VTDIOTLBEntry *entry; - int level; + unsigned level; for (level = VTD_SL_PT_LEVEL; level < VTD_SL_PML4_LEVEL; level++) { key.gfn = vtd_get_iotlb_gfn(addr, level); @@ -396,7 +422,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. @@ -468,21 +494,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, - bool is_pasid, uint32_t pasid) + 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) | - VTD_FRCD_PV(pasid) | VTD_FRCD_PP(is_pasid); - 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); @@ -508,17 +525,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, bool is_pasid, - uint32_t pasid) +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"); @@ -538,8 +549,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, is_pasid, pasid); + vtd_record_frcd(s, s->next_frcd_reg, hi, lo); if (fsts_reg & VTD_FSTS_PPF) { error_report_once("There are pending faults already, " @@ -564,6 +574,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. */ @@ -755,6 +799,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; } @@ -779,6 +825,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)) { @@ -1021,18 +1070,35 @@ 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; @@ -1129,7 +1195,7 @@ static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, } } -typedef int (*vtd_page_walk_hook)(IOMMUTLBEvent *event, void *private); +typedef int (*vtd_page_walk_hook)(const IOMMUTLBEvent *event, void *private); /** * Constant information used during page walking @@ -1492,7 +1558,7 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } -static int vtd_sync_shadow_page_hook(IOMMUTLBEvent *event, +static int vtd_sync_shadow_page_hook(const IOMMUTLBEvent *event, void *private) { memory_region_notify_iommu(private, 0, *event); @@ -1530,13 +1596,17 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, 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; } @@ -1620,7 +1690,7 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) { 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); @@ -1638,7 +1708,7 @@ 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 */ @@ -1684,7 +1754,7 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) * """ * * We enable per as memory region (iommu_ir_fault) for catching - * the tranlsation for interrupt range through PASID + PT. + * the translation for interrupt range through PASID + PT. */ if (pt && as->pasid != PCI_NO_PASID) { memory_region_set_enabled(&as->iommu_ir_fault, true); @@ -1693,7 +1763,7 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) } if (take_bql) { - qemu_mutex_unlock_iothread(); + bql_unlock(); } return use_iommu; @@ -2000,7 +2070,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); } } @@ -2082,7 +2152,7 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, * framework will skip MAP notifications if that * happened. */ - vtd_sync_shadow_page_table(vtd_as); + vtd_address_space_sync(vtd_as); } } } @@ -2140,7 +2210,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) 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_as->pasid)) { - vtd_sync_shadow_page_table(vtd_as); + vtd_address_space_sync(vtd_as); } } } @@ -2174,7 +2244,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, * page tables. We just deliver the PSI down to * invalidate caches. */ - IOMMUTLBEvent event = { + const IOMMUTLBEvent event = { .type = IOMMU_NOTIFIER_UNMAP, .entry = { .target_as = &address_space_memory, @@ -2462,15 +2532,51 @@ static bool vtd_get_inv_desc(IntelIOMMUState *s, return true; } +static bool vtd_inv_desc_reserved_check(IntelIOMMUState *s, + VTDInvDesc *inv_desc, + uint64_t mask[4], bool dw, + const char *func_name, + const char *desc_type) +{ + if (s->iq_dw) { + if (inv_desc->val[0] & mask[0] || inv_desc->val[1] & mask[1] || + inv_desc->val[2] & mask[2] || inv_desc->val[3] & mask[3]) { + error_report("%s: invalid %s desc val[3]: 0x%"PRIx64 + " val[2]: 0x%"PRIx64" val[1]=0x%"PRIx64 + " val[0]=0x%"PRIx64" (reserved nonzero)", + func_name, desc_type, inv_desc->val[3], + inv_desc->val[2], inv_desc->val[1], + inv_desc->val[0]); + return false; + } + } else { + if (dw) { + error_report("%s: 256-bit %s desc in 128-bit invalidation queue", + func_name, desc_type); + return false; + } + + if (inv_desc->lo & mask[0] || inv_desc->hi & mask[1]) { + error_report("%s: invalid %s desc: hi=%"PRIx64", lo=%"PRIx64 + " (reserved nonzero)", func_name, desc_type, + inv_desc->hi, inv_desc->lo); + return false; + } + } + + return true; +} + static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { - if ((inv_desc->hi & VTD_INV_DESC_WAIT_RSVD_HI) || - (inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO)) { - error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64 - " (reserved nonzero)", __func__, inv_desc->hi, - inv_desc->lo); + uint64_t mask[4] = {VTD_INV_DESC_WAIT_RSVD_LO, VTD_INV_DESC_WAIT_RSVD_HI, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, + __func__, "wait")) { return false; } + if (inv_desc->lo & VTD_INV_DESC_WAIT_SW) { /* Status Write */ uint32_t status_data = (uint32_t)(inv_desc->lo >> @@ -2504,13 +2610,14 @@ static bool vtd_process_context_cache_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { uint16_t sid, fmask; + uint64_t mask[4] = {VTD_INV_DESC_CC_RSVD, VTD_INV_DESC_ALL_ONE, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; - if ((inv_desc->lo & VTD_INV_DESC_CC_RSVD) || inv_desc->hi) { - error_report_once("%s: invalid cc inv desc: hi=%"PRIx64", lo=%"PRIx64 - " (reserved nonzero)", __func__, inv_desc->hi, - inv_desc->lo); + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, + __func__, "cc inv")) { return false; } + switch (inv_desc->lo & VTD_INV_DESC_CC_G) { case VTD_INV_DESC_CC_DOMAIN: trace_vtd_inv_desc_cc_domain( @@ -2540,12 +2647,11 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) uint16_t domain_id; uint8_t am; hwaddr addr; + uint64_t mask[4] = {VTD_INV_DESC_IOTLB_RSVD_LO, VTD_INV_DESC_IOTLB_RSVD_HI, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; - if ((inv_desc->lo & VTD_INV_DESC_IOTLB_RSVD_LO) || - (inv_desc->hi & VTD_INV_DESC_IOTLB_RSVD_HI)) { - error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64 - ", lo=0x%"PRIx64" (reserved bits unzero)", - __func__, inv_desc->hi, inv_desc->lo); + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, + __func__, "iotlb inv")) { return false; } @@ -2586,6 +2692,14 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { + uint64_t mask[4] = {VTD_INV_DESC_IEC_RSVD, VTD_INV_DESC_ALL_ONE, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, + __func__, "iec inv")) { + return false; + } + trace_vtd_inv_desc_iec(inv_desc->iec.granularity, inv_desc->iec.index, inv_desc->iec.index_mask); @@ -2596,38 +2710,11 @@ static bool vtd_process_inv_iec_desc(IntelIOMMUState *s, return true; } -static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, - VTDInvDesc *inv_desc) +static void do_invalidate_device_tlb(VTDAddressSpace *vtd_dev_as, + bool size, hwaddr addr) { - VTDAddressSpace *vtd_dev_as; - IOMMUTLBEvent event; - hwaddr addr; - uint64_t sz; - uint16_t sid; - bool size; - - addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi); - sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo); - size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi); - - if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) || - (inv_desc->hi & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) { - error_report_once("%s: invalid dev-iotlb inv desc: hi=%"PRIx64 - ", lo=%"PRIx64" (reserved nonzero)", __func__, - inv_desc->hi, inv_desc->lo); - return false; - } - /* - * 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; - } - - /* According to ATS spec table 2.4: + * According to ATS spec table 2.4: * S = 0, bits 15:12 = xxxx range size: 4K * S = 1, bits 15:12 = xxx0 range size: 8K * S = 1, bits 15:12 = xx01 range size: 16K @@ -2635,6 +2722,10 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, * S = 1, bits 15:12 = 0111 range size: 64K * ... */ + + IOMMUTLBEvent event; + uint64_t sz; + if (size) { sz = (VTD_PAGE_SIZE * 2) << cto64(addr >> VTD_PAGE_SHIFT); addr &= ~(sz - 1); @@ -2649,6 +2740,38 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, event.entry.perm = IOMMU_NONE; event.entry.translated_addr = 0; memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); +} + +static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, + VTDInvDesc *inv_desc) +{ + VTDAddressSpace *vtd_dev_as; + hwaddr addr; + uint16_t sid; + bool size; + uint64_t mask[4] = {VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO, + VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI, + VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + + if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, + __func__, "dev-iotlb inv")) { + return false; + } + + addr = VTD_INV_DESC_DEVICE_IOTLB_ADDR(inv_desc->hi); + sid = VTD_INV_DESC_DEVICE_IOTLB_SID(inv_desc->lo); + size = VTD_INV_DESC_DEVICE_IOTLB_SIZE(inv_desc->hi); + + /* + * 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; + } + + do_invalidate_device_tlb(vtd_dev_as, size, addr); done: return true; @@ -2665,7 +2788,7 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) return false; } - desc_type = inv_desc.lo & VTD_INV_DESC_TYPE; + desc_type = VTD_INV_DESC_TYPE(inv_desc.lo); /* FIXME: should update at first or at last? */ s->iq_last_desc_type = desc_type; @@ -2684,17 +2807,6 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; - /* - * TODO: the entity of below two cases will be implemented in future series. - * To make guest (which integrates scalable mode support patch set in - * iommu driver) work, just return true is enough so far. - */ - case VTD_INV_DESC_PC: - break; - - case VTD_INV_DESC_PIOTLB: - break; - case VTD_INV_DESC_WAIT: trace_vtd_inv_desc("wait", inv_desc.hi, inv_desc.lo); if (!vtd_process_wait_desc(s, &inv_desc)) { @@ -2716,6 +2828,17 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + /* + * TODO: the entity of below two cases will be implemented in future series. + * To make guest (which integrates scalable mode support patch set in + * iommu driver) work, just return true is enough so far. + */ + case VTD_INV_DESC_PC: + case VTD_INV_DESC_PIOTLB: + if (s->scalable_mode) { + break; + } + /* fallthrough */ default: error_report_once("%s: invalid inv desc: hi=%"PRIx64", lo=%"PRIx64 " (unknown type)", __func__, inv_desc.hi, @@ -2768,6 +2891,7 @@ static void vtd_handle_iqt_write(IntelIOMMUState *s) if (s->iq_dw && (val & VTD_IQT_QT_256_RSV_BIT)) { error_report_once("%s: RSV bit is set: val=0x%"PRIx64, __func__, val); + vtd_handle_inv_queue_error(s); return; } s->iq_tail = VTD_IQT_QT(s->iq_dw, val); @@ -2868,7 +2992,9 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) /* Invalidation Queue Address Register, 64-bit */ case DMAR_IQA_REG: - val = s->iq | (vtd_get_quad(s, DMAR_IQA_REG) & VTD_IQA_QS); + val = s->iq | + (vtd_get_quad(s, DMAR_IQA_REG) & + (VTD_IQA_QS | VTD_IQA_DW_MASK)); if (size == 4) { val = val & ((1ULL << 32) - 1); } @@ -3244,7 +3370,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), @@ -3291,12 +3417,14 @@ static Property vtd_properties[] = { 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_BOOL("stale-tm", IntelIOMMUState, stale_tm, false), 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}; @@ -3307,7 +3435,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); @@ -3315,32 +3446,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; @@ -3351,7 +3501,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; @@ -3363,7 +3516,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; @@ -3372,29 +3528,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) @@ -3407,16 +3564,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 = {}; @@ -3433,14 +3589,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. */ @@ -3449,7 +3611,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) @@ -3459,9 +3621,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) { @@ -3471,7 +3632,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; @@ -3511,7 +3675,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, @@ -3537,14 +3701,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; } @@ -3731,10 +3894,91 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, return vtd_dev_as; } +static bool vtd_check_hiod(IntelIOMMUState *s, HostIOMMUDevice *hiod, + Error **errp) +{ + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod); + int ret; + + if (!hiodc->get_cap) { + error_setg(errp, ".get_cap() not implemented"); + return false; + } + + /* Common checks */ + ret = hiodc->get_cap(hiod, HOST_IOMMU_DEVICE_CAP_AW_BITS, errp); + if (ret < 0) { + return false; + } + if (s->aw_bits > ret) { + error_setg(errp, "aw-bits %d > host aw-bits %d", s->aw_bits, ret); + return false; + } + + return true; +} + +static bool vtd_dev_set_iommu_device(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *hiod, Error **errp) +{ + IntelIOMMUState *s = opaque; + struct vtd_as_key key = { + .bus = bus, + .devfn = devfn, + }; + struct vtd_as_key *new_key; + + assert(hiod); + + vtd_iommu_lock(s); + + if (g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) { + error_setg(errp, "Host IOMMU device already exist"); + vtd_iommu_unlock(s); + return false; + } + + if (!vtd_check_hiod(s, hiod, errp)) { + vtd_iommu_unlock(s); + return false; + } + + new_key = g_malloc(sizeof(*new_key)); + new_key->bus = bus; + new_key->devfn = devfn; + + object_ref(hiod); + g_hash_table_insert(s->vtd_host_iommu_dev, new_key, hiod); + + vtd_iommu_unlock(s); + + return true; +} + +static void vtd_dev_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) +{ + IntelIOMMUState *s = opaque; + struct vtd_as_key key = { + .bus = bus, + .devfn = devfn, + }; + + vtd_iommu_lock(s); + + if (!g_hash_table_lookup(s->vtd_host_iommu_dev, &key)) { + vtd_iommu_unlock(s); + return; + } + + g_hash_table_remove(s->vtd_host_iommu_dev, &key); + + vtd_iommu_unlock(s); +} + /* 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; @@ -3755,7 +3999,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; @@ -3783,10 +4027,10 @@ 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; + map.size = total - 1; /* Inclusive */ iova_tree_remove(as->iova_tree, map); } @@ -3808,7 +4052,7 @@ static void vtd_address_space_refresh_all(IntelIOMMUState *s) vtd_switch_address_space_all(s); } -static int vtd_replay_hook(IOMMUTLBEvent *event, void *private) +static int vtd_replay_hook(const IOMMUTLBEvent *event, void *private) { memory_region_notify_iommu_one(private, event); return 0; @@ -3820,13 +4064,10 @@ 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" : @@ -3835,7 +4076,7 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) PCI_FUNC(vtd_as->devfn), 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, @@ -3856,30 +4097,10 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) return; } -/* Do the initialization. It will also be called when reset, so pay - * attention when adding new initialization stuff. - */ -static void vtd_init(IntelIOMMUState *s) +static void vtd_cap_init(IntelIOMMUState *s) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); - memset(s->csr, 0, DMAR_REG_SIZE); - memset(s->wmask, 0, DMAR_REG_SIZE); - memset(s->w1cmask, 0, DMAR_REG_SIZE); - memset(s->womask, 0, DMAR_REG_SIZE); - - s->root = 0; - s->root_scalable = false; - s->dmar_enabled = false; - s->intr_enabled = false; - s->iq_head = 0; - s->iq_tail = 0; - s->iq = 0; - s->iq_size = 0; - s->qi_enabled = false; - s->iq_last_desc_type = VTD_INV_DESC_NONE; - s->iq_dw = false; - s->next_frcd_reg = 0; s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS | VTD_CAP_MGAW(s->aw_bits); @@ -3896,27 +4117,6 @@ static void vtd_init(IntelIOMMUState *s) } s->ecap = VTD_ECAP_QI | VTD_ECAP_IRO; - /* - * Rsvd field masks for spte - */ - vtd_spte_rsvd[0] = ~0ULL; - vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits, - x86_iommu->dt_supported); - vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits); - vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits); - vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits); - - vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits, - x86_iommu->dt_supported); - vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits, - x86_iommu->dt_supported); - - if (s->scalable_mode || s->snoop_control) { - vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP; - vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP; - vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP; - } - if (x86_iommu_ir_supported(x86_iommu)) { s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV; if (s->intr_eim == ON_OFF_AUTO_ON) { @@ -3949,6 +4149,56 @@ static void vtd_init(IntelIOMMUState *s) if (s->pasid) { s->ecap |= VTD_ECAP_PASID; } +} + +/* + * Do the initialization. It will also be called when reset, so pay + * attention when adding new initialization stuff. + */ +static void vtd_init(IntelIOMMUState *s) +{ + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); + + memset(s->csr, 0, DMAR_REG_SIZE); + memset(s->wmask, 0, DMAR_REG_SIZE); + memset(s->w1cmask, 0, DMAR_REG_SIZE); + memset(s->womask, 0, DMAR_REG_SIZE); + + s->root = 0; + s->root_scalable = false; + s->dmar_enabled = false; + s->intr_enabled = false; + s->iq_head = 0; + s->iq_tail = 0; + s->iq = 0; + s->iq_size = 0; + s->qi_enabled = false; + s->iq_last_desc_type = VTD_INV_DESC_NONE; + s->iq_dw = false; + s->next_frcd_reg = 0; + + vtd_cap_init(s); + + /* + * Rsvd field masks for spte + */ + vtd_spte_rsvd[0] = ~0ULL; + vtd_spte_rsvd[1] = VTD_SPTE_PAGE_L1_RSVD_MASK(s->aw_bits, + x86_iommu->dt_supported && s->stale_tm); + vtd_spte_rsvd[2] = VTD_SPTE_PAGE_L2_RSVD_MASK(s->aw_bits); + vtd_spte_rsvd[3] = VTD_SPTE_PAGE_L3_RSVD_MASK(s->aw_bits); + vtd_spte_rsvd[4] = VTD_SPTE_PAGE_L4_RSVD_MASK(s->aw_bits); + + vtd_spte_rsvd_large[2] = VTD_SPTE_LPAGE_L2_RSVD_MASK(s->aw_bits, + x86_iommu->dt_supported && s->stale_tm); + vtd_spte_rsvd_large[3] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits, + x86_iommu->dt_supported && s->stale_tm); + + if (s->scalable_mode || s->snoop_control) { + vtd_spte_rsvd[1] &= ~VTD_SPTE_SNP; + vtd_spte_rsvd_large[2] &= ~VTD_SPTE_SNP; + vtd_spte_rsvd_large[3] &= ~VTD_SPTE_SNP; + } vtd_reset_caches(s); @@ -4027,6 +4277,12 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &vtd_as->as; } +static PCIIOMMUOps vtd_iommu_ops = { + .get_address_space = vtd_host_dma_iommu, + .set_iommu_device = vtd_dev_set_iommu_device, + .unset_iommu_device = vtd_dev_unset_iommu_device, +}; + static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); @@ -4042,11 +4298,7 @@ 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"); - return false; - } - if (!kvm_enable_x2apic()) { + 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; @@ -4105,7 +4357,7 @@ 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); @@ -4127,6 +4379,8 @@ static void vtd_realize(DeviceState *dev, Error **errp) qemu_mutex_init(&s->iommu_lock); 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", @@ -4141,16 +4395,15 @@ 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_iotlb_hash, vtd_iotlb_equal, g_free, g_free); s->vtd_address_spaces = g_hash_table_new_full(vtd_as_hash, vtd_as_equal, g_free, g_free); + s->vtd_host_iommu_dev = g_hash_table_new_full(vtd_hiod_hash, vtd_hiod_equal, + g_free, vtd_hiod_destroy); 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); @@ -4161,7 +4414,7 @@ static void vtd_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_CLASS(klass); - dc->reset = vtd_reset; + device_class_set_legacy_reset(dc, vtd_reset); dc->vmsd = &vtd_vmstate; device_class_set_props(dc, vtd_properties); dc->hotpluggable = false; diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index f090e61e11..4323fc5d6d 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -114,9 +114,9 @@ VTD_INTERRUPT_ADDR_FIRST + 1) /* The shift of source_id in the key of IOTLB hash table */ -#define VTD_IOTLB_SID_SHIFT 20 -#define VTD_IOTLB_LVL_SHIFT 28 -#define VTD_IOTLB_PASID_SHIFT 30 +#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 */ @@ -264,10 +264,11 @@ #define VTD_FRCD_FR(val) (((val) & 0xffULL) << 32) #define VTD_FRCD_SID_MASK 0xffffULL #define VTD_FRCD_SID(val) ((val) & VTD_FRCD_SID_MASK) +#define VTD_FRCD_PV(val) (((val) & 0xffffULL) << 40) +#define VTD_FRCD_PP(val) (((val) & 0x1ULL) << 31) /* 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 { @@ -321,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; @@ -346,7 +356,9 @@ union VTDInvDesc { typedef union VTDInvDesc VTDInvDesc; /* Masks for struct VTDInvDesc */ -#define VTD_INV_DESC_TYPE 0xf +#define VTD_INV_DESC_ALL_ONE -1ULL +#define VTD_INV_DESC_TYPE(val) ((((val) >> 5) & 0x70ULL) | \ + ((val) & 0xfULL)) #define VTD_INV_DESC_CC 0x1 /* Context-cache Invalidate Desc */ #define VTD_INV_DESC_IOTLB 0x2 #define VTD_INV_DESC_DEVICE 0x3 @@ -362,7 +374,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_WAIT_IF (1ULL << 4) #define VTD_INV_DESC_WAIT_FN (1ULL << 6) #define VTD_INV_DESC_WAIT_DATA_SHIFT 32 -#define VTD_INV_DESC_WAIT_RSVD_LO 0Xffffff80ULL +#define VTD_INV_DESC_WAIT_RSVD_LO 0Xfffff180ULL #define VTD_INV_DESC_WAIT_RSVD_HI 3ULL /* Masks for Context-cache Invalidation Descriptor */ @@ -373,7 +385,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_CC_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) #define VTD_INV_DESC_CC_SID(val) (((val) >> 32) & 0xffffUL) #define VTD_INV_DESC_CC_FM(val) (((val) >> 48) & 3UL) -#define VTD_INV_DESC_CC_RSVD 0xfffc00000000ffc0ULL +#define VTD_INV_DESC_CC_RSVD 0xfffc00000000f1c0ULL /* Masks for IOTLB Invalidate Descriptor */ #define VTD_INV_DESC_IOTLB_G (3ULL << 4) @@ -383,7 +395,7 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_IOTLB_DID(val) (((val) >> 16) & VTD_DOMAIN_ID_MASK) #define VTD_INV_DESC_IOTLB_ADDR(val) ((val) & ~0xfffULL) #define VTD_INV_DESC_IOTLB_AM(val) ((val) & 0x3fULL) -#define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000ff00ULL +#define VTD_INV_DESC_IOTLB_RSVD_LO 0xffffffff0000f100ULL #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) @@ -396,13 +408,16 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_DEVICE_IOTLB_SIZE(val) ((val) & 0x1) #define VTD_INV_DESC_DEVICE_IOTLB_SID(val) (((val) >> 32) & 0xFFFFULL) #define VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI 0xffeULL -#define VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO 0xffff0000ffe0fff8 +#define VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO 0xffff0000ffe0f1f0 + +/* Masks for Interrupt Entry Invalidate Descriptor */ +#define VTD_INV_DESC_IEC_RSVD 0xffff000007fff1e0ULL /* Rsvd field masks for spte */ #define VTD_SPTE_SNP 0x800ULL -#define VTD_SPTE_PAGE_L1_RSVD_MASK(aw, dt_supported) \ - dt_supported ? \ +#define VTD_SPTE_PAGE_L1_RSVD_MASK(aw, stale_tm) \ + stale_tm ? \ (0x800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \ (0x800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM)) #define VTD_SPTE_PAGE_L2_RSVD_MASK(aw) \ @@ -412,12 +427,12 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_SPTE_PAGE_L4_RSVD_MASK(aw) \ (0x880ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM)) -#define VTD_SPTE_LPAGE_L2_RSVD_MASK(aw, dt_supported) \ - dt_supported ? \ +#define VTD_SPTE_LPAGE_L2_RSVD_MASK(aw, stale_tm) \ + stale_tm ? \ (0x1ff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \ (0x1ff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM)) -#define VTD_SPTE_LPAGE_L3_RSVD_MASK(aw, dt_supported) \ - dt_supported ? \ +#define VTD_SPTE_LPAGE_L3_RSVD_MASK(aw, stale_tm) \ + stale_tm ? \ (0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM | VTD_SL_TM)) : \ (0x3ffff800ULL | ~(VTD_HAW_MASK(aw) | VTD_SL_IGN_COM)) @@ -426,7 +441,7 @@ struct VTDIOTLBPageInvInfo { uint16_t domain_id; uint32_t pasid; uint64_t addr; - uint8_t mask; + uint64_t mask; }; typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; 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/hw/i386/kvm/clock.h b/hw/i386/kvm/clock.h new file mode 100644 index 0000000000..401c7e445b --- /dev/null +++ b/hw/i386/kvm/clock.h @@ -0,0 +1,18 @@ +/* + * QEMU KVM support, paravirtual clock device + * + * Copyright (C) 2011 Siemens AG + * + * Authors: + * Jan Kiszka + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_I386_KVM_CLOCK_H +#define HW_I386_KVM_CLOCK_H + +void kvmclock_create(bool create_always); + +#endif diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 191a26fa57..baa4b39582 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(), @@ -317,7 +303,7 @@ static void kvm_pit_class_init(ObjectClass *klass, void *data) &kpc->parent_realize); k->set_channel_gate = kvm_pit_set_gate; k->get_channel_info = kvm_pit_get_channel_info; - dc->reset = kvm_pit_reset; + device_class_set_legacy_reset(dc, kvm_pit_reset); device_class_set_props(dc, kvm_pit_properties); } diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index d61bae4dc3..9c2fb645fe 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) @@ -145,7 +145,7 @@ static void kvm_i8259_class_init(ObjectClass *klass, void *data) PICCommonClass *k = PIC_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = kvm_pic_reset; + device_class_set_legacy_reset(dc, kvm_pic_reset); device_class_set_parent_realize(dc, kvm_pic_realize, &kpc->parent_realize); k->pre_save = kvm_pic_get; k->post_load = kvm_pic_put; diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index ee7c8ef68b..2907b08164 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) @@ -146,7 +146,7 @@ static void kvm_ioapic_class_init(ObjectClass *klass, void *data) k->realize = kvm_ioapic_realize; k->pre_save = kvm_ioapic_get; k->post_load = kvm_ioapic_put; - dc->reset = kvm_ioapic_reset; + device_class_set_legacy_reset(dc, kvm_ioapic_reset); device_class_set_props(dc, kvm_ioapic_properties); } 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..3483a332a6 --- /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); + + device_class_set_legacy_reset(dc, 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/kvmvapic.c b/hw/i386/kvmvapic.c deleted file mode 100644 index 43f8a8f679..0000000000 --- a/hw/i386/kvmvapic.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * TPR optimization for 32-bit Windows guests (XP and Server 2003) - * - * Copyright (C) 2007-2008 Qumranet Technologies - * Copyright (C) 2012 Jan Kiszka, Siemens AG - * - * 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/module.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpus.h" -#include "sysemu/hw_accel.h" -#include "sysemu/kvm.h" -#include "sysemu/runstate.h" -#include "hw/i386/apic_internal.h" -#include "hw/sysbus.h" -#include "hw/boards.h" -#include "migration/vmstate.h" -#include "qom/object.h" - -#define VAPIC_IO_PORT 0x7e - -#define VAPIC_CPU_SHIFT 7 - -#define ROM_BLOCK_SIZE 512 -#define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1)) - -typedef enum VAPICMode { - VAPIC_INACTIVE = 0, - VAPIC_ACTIVE = 1, - VAPIC_STANDBY = 2, -} VAPICMode; - -typedef struct VAPICHandlers { - uint32_t set_tpr; - uint32_t set_tpr_eax; - uint32_t get_tpr[8]; - uint32_t get_tpr_stack; -} QEMU_PACKED VAPICHandlers; - -typedef struct GuestROMState { - char signature[8]; - uint32_t vaddr; - uint32_t fixup_start; - uint32_t fixup_end; - uint32_t vapic_vaddr; - uint32_t vapic_size; - uint32_t vcpu_shift; - uint32_t real_tpr_addr; - VAPICHandlers up; - VAPICHandlers mp; -} QEMU_PACKED GuestROMState; - -struct VAPICROMState { - SysBusDevice busdev; - MemoryRegion io; - MemoryRegion rom; - uint32_t state; - uint32_t rom_state_paddr; - uint32_t rom_state_vaddr; - uint32_t vapic_paddr; - uint32_t real_tpr_addr; - GuestROMState rom_state; - size_t rom_size; - bool rom_mapped_writable; - VMChangeStateEntry *vmsentry; -}; - -#define TYPE_VAPIC "kvmvapic" -OBJECT_DECLARE_SIMPLE_TYPE(VAPICROMState, VAPIC) - -#define TPR_INSTR_ABS_MODRM 0x1 -#define TPR_INSTR_MATCH_MODRM_REG 0x2 - -typedef struct TPRInstruction { - uint8_t opcode; - uint8_t modrm_reg; - unsigned int flags; - TPRAccess access; - size_t length; - off_t addr_offset; -} TPRInstruction; - -/* must be sorted by length, shortest first */ -static const TPRInstruction tpr_instr[] = { - { /* mov abs to eax */ - .opcode = 0xa1, - .access = TPR_ACCESS_READ, - .length = 5, - .addr_offset = 1, - }, - { /* mov eax to abs */ - .opcode = 0xa3, - .access = TPR_ACCESS_WRITE, - .length = 5, - .addr_offset = 1, - }, - { /* mov r32 to r/m32 */ - .opcode = 0x89, - .flags = TPR_INSTR_ABS_MODRM, - .access = TPR_ACCESS_WRITE, - .length = 6, - .addr_offset = 2, - }, - { /* mov r/m32 to r32 */ - .opcode = 0x8b, - .flags = TPR_INSTR_ABS_MODRM, - .access = TPR_ACCESS_READ, - .length = 6, - .addr_offset = 2, - }, - { /* push r/m32 */ - .opcode = 0xff, - .modrm_reg = 6, - .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, - .access = TPR_ACCESS_READ, - .length = 6, - .addr_offset = 2, - }, - { /* mov imm32, r/m32 (c7/0) */ - .opcode = 0xc7, - .modrm_reg = 0, - .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, - .access = TPR_ACCESS_WRITE, - .length = 10, - .addr_offset = 2, - }, -}; - -static void read_guest_rom_state(VAPICROMState *s) -{ - cpu_physical_memory_read(s->rom_state_paddr, &s->rom_state, - sizeof(GuestROMState)); -} - -static void write_guest_rom_state(VAPICROMState *s) -{ - cpu_physical_memory_write(s->rom_state_paddr, &s->rom_state, - sizeof(GuestROMState)); -} - -static void update_guest_rom_state(VAPICROMState *s) -{ - read_guest_rom_state(s); - - s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr); - s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT); - - write_guest_rom_state(s); -} - -static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - hwaddr paddr; - target_ulong addr; - - if (s->state == VAPIC_ACTIVE) { - return 0; - } - /* - * If there is no prior TPR access instruction we could analyze (which is - * the case after resume from hibernation), we need to scan the possible - * virtual address space for the APIC mapping. - */ - for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) { - paddr = cpu_get_phys_page_debug(cs, addr); - if (paddr != APIC_DEFAULT_ADDRESS) { - continue; - } - s->real_tpr_addr = addr + 0x80; - update_guest_rom_state(s); - return 0; - } - return -1; -} - -static uint8_t modrm_reg(uint8_t modrm) -{ - return (modrm >> 3) & 7; -} - -static bool is_abs_modrm(uint8_t modrm) -{ - return (modrm & 0xc7) == 0x05; -} - -static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr) -{ - return opcode[0] == instr->opcode && - (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) && - (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) || - modrm_reg(opcode[1]) == instr->modrm_reg); -} - -static int evaluate_tpr_instruction(VAPICROMState *s, X86CPU *cpu, - target_ulong *pip, TPRAccess access) -{ - CPUState *cs = CPU(cpu); - const TPRInstruction *instr; - target_ulong ip = *pip; - uint8_t opcode[2]; - uint32_t real_tpr_addr; - int i; - - if ((ip & 0xf0000000ULL) != 0x80000000ULL && - (ip & 0xf0000000ULL) != 0xe0000000ULL) { - return -1; - } - - /* - * Early Windows 2003 SMP initialization contains a - * - * mov imm32, r/m32 - * - * instruction that is patched by TPR optimization. The problem is that - * RSP, used by the patched instruction, is zero, so the guest gets a - * double fault and dies. - */ - if (cpu->env.regs[R_ESP] == 0) { - return -1; - } - - if (kvm_enabled() && !kvm_irqchip_in_kernel()) { - /* - * KVM without kernel-based TPR access reporting will pass an IP that - * points after the accessing instruction. So we need to look backward - * to find the reason. - */ - for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { - instr = &tpr_instr[i]; - if (instr->access != access) { - continue; - } - if (cpu_memory_rw_debug(cs, ip - instr->length, opcode, - sizeof(opcode), 0) < 0) { - return -1; - } - if (opcode_matches(opcode, instr)) { - ip -= instr->length; - goto instruction_ok; - } - } - return -1; - } else { - if (cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0) < 0) { - return -1; - } - for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { - instr = &tpr_instr[i]; - if (opcode_matches(opcode, instr)) { - goto instruction_ok; - } - } - return -1; - } - -instruction_ok: - /* - * Grab the virtual TPR address from the instruction - * and update the cached values. - */ - if (cpu_memory_rw_debug(cs, ip + instr->addr_offset, - (void *)&real_tpr_addr, - sizeof(real_tpr_addr), 0) < 0) { - return -1; - } - real_tpr_addr = le32_to_cpu(real_tpr_addr); - if ((real_tpr_addr & 0xfff) != 0x80) { - return -1; - } - s->real_tpr_addr = real_tpr_addr; - update_guest_rom_state(s); - - *pip = ip; - return 0; -} - -static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip) -{ - CPUState *cs = env_cpu(env); - hwaddr paddr; - uint32_t rom_state_vaddr; - uint32_t pos, patch, offset; - - /* nothing to do if already activated */ - if (s->state == VAPIC_ACTIVE) { - return 0; - } - - /* bail out if ROM init code was not executed (missing ROM?) */ - if (s->state == VAPIC_INACTIVE) { - return -1; - } - - /* find out virtual address of the ROM */ - rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000); - paddr = cpu_get_phys_page_debug(cs, rom_state_vaddr); - if (paddr == -1) { - return -1; - } - paddr += rom_state_vaddr & ~TARGET_PAGE_MASK; - if (paddr != s->rom_state_paddr) { - return -1; - } - read_guest_rom_state(s); - if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) { - return -1; - } - s->rom_state_vaddr = rom_state_vaddr; - - /* fixup addresses in ROM if needed */ - if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) { - return 0; - } - for (pos = le32_to_cpu(s->rom_state.fixup_start); - pos < le32_to_cpu(s->rom_state.fixup_end); - pos += 4) { - cpu_physical_memory_read(paddr + pos - s->rom_state.vaddr, - &offset, sizeof(offset)); - offset = le32_to_cpu(offset); - cpu_physical_memory_read(paddr + offset, &patch, sizeof(patch)); - patch = le32_to_cpu(patch); - patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr); - patch = cpu_to_le32(patch); - cpu_physical_memory_write(paddr + offset, &patch, sizeof(patch)); - } - read_guest_rom_state(s); - s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) - - le32_to_cpu(s->rom_state.vaddr); - - return 0; -} - -/* - * Tries to read the unique processor number from the Kernel Processor Control - * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR - * cannot be accessed or is considered invalid. This also ensures that we are - * not patching the wrong guest. - */ -static int get_kpcr_number(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - struct kpcr { - uint8_t fill1[0x1c]; - uint32_t self; - uint8_t fill2[0x31]; - uint8_t number; - } QEMU_PACKED kpcr; - - if (cpu_memory_rw_debug(CPU(cpu), env->segs[R_FS].base, - (void *)&kpcr, sizeof(kpcr), 0) < 0 || - kpcr.self != env->segs[R_FS].base) { - return -1; - } - return kpcr.number; -} - -static int vapic_enable(VAPICROMState *s, X86CPU *cpu) -{ - int cpu_number = get_kpcr_number(cpu); - hwaddr vapic_paddr; - static const uint8_t enabled = 1; - - if (cpu_number < 0) { - return -1; - } - vapic_paddr = s->vapic_paddr + - (((hwaddr)cpu_number) << VAPIC_CPU_SHIFT); - cpu_physical_memory_write(vapic_paddr + offsetof(VAPICState, enabled), - &enabled, sizeof(enabled)); - apic_enable_vapic(cpu->apic_state, vapic_paddr); - - s->state = VAPIC_ACTIVE; - - return 0; -} - -static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte) -{ - cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1); -} - -static void patch_call(X86CPU *cpu, target_ulong ip, uint32_t target) -{ - uint32_t offset; - - offset = cpu_to_le32(target - ip - 5); - patch_byte(cpu, ip, 0xe8); /* call near */ - cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1); -} - -typedef struct PatchInfo { - VAPICHandlers *handler; - target_ulong ip; -} PatchInfo; - -static void do_patch_instruction(CPUState *cs, run_on_cpu_data data) -{ - X86CPU *x86_cpu = X86_CPU(cs); - PatchInfo *info = (PatchInfo *) data.host_ptr; - VAPICHandlers *handlers = info->handler; - target_ulong ip = info->ip; - uint8_t opcode[2]; - uint32_t imm32 = 0; - - cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); - - switch (opcode[0]) { - case 0x89: /* mov r32 to r/m32 */ - patch_byte(x86_cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ - patch_call(x86_cpu, ip + 1, handlers->set_tpr); - break; - case 0x8b: /* mov r/m32 to r32 */ - patch_byte(x86_cpu, ip, 0x90); - patch_call(x86_cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); - break; - case 0xa1: /* mov abs to eax */ - patch_call(x86_cpu, ip, handlers->get_tpr[0]); - break; - case 0xa3: /* mov eax to abs */ - patch_call(x86_cpu, ip, handlers->set_tpr_eax); - break; - case 0xc7: /* mov imm32, r/m32 (c7/0) */ - patch_byte(x86_cpu, ip, 0x68); /* push imm32 */ - cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); - cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); - patch_call(x86_cpu, ip + 5, handlers->set_tpr); - break; - case 0xff: /* push r/m32 */ - patch_byte(x86_cpu, ip, 0x50); /* push eax */ - patch_call(x86_cpu, ip + 1, handlers->get_tpr_stack); - break; - default: - abort(); - } - - g_free(info); -} - -static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - CPUState *cs = CPU(cpu); - VAPICHandlers *handlers; - PatchInfo *info; - - if (ms->smp.cpus == 1) { - handlers = &s->rom_state.up; - } else { - handlers = &s->rom_state.mp; - } - - info = g_new(PatchInfo, 1); - info->handler = handlers; - info->ip = ip; - - async_safe_run_on_cpu(cs, do_patch_instruction, RUN_ON_CPU_HOST_PTR(info)); -} - -void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, - TPRAccess access) -{ - VAPICROMState *s = VAPIC(dev); - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - cpu_synchronize_state(cs); - - if (evaluate_tpr_instruction(s, cpu, &ip, access) < 0) { - if (s->state == VAPIC_ACTIVE) { - vapic_enable(s, cpu); - } - return; - } - if (update_rom_mapping(s, env, ip) < 0) { - return; - } - if (vapic_enable(s, cpu) < 0) { - return; - } - patch_instruction(s, cpu, ip); -} - -typedef struct VAPICEnableTPRReporting { - DeviceState *apic; - bool enable; -} VAPICEnableTPRReporting; - -static void vapic_do_enable_tpr_reporting(CPUState *cpu, run_on_cpu_data data) -{ - VAPICEnableTPRReporting *info = data.host_ptr; - apic_enable_tpr_access_reporting(info->apic, info->enable); -} - -static void vapic_enable_tpr_reporting(bool enable) -{ - VAPICEnableTPRReporting info = { - .enable = enable, - }; - CPUState *cs; - X86CPU *cpu; - - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - info.apic = cpu->apic_state; - run_on_cpu(cs, vapic_do_enable_tpr_reporting, RUN_ON_CPU_HOST_PTR(&info)); - } -} - -static void vapic_reset(DeviceState *dev) -{ - VAPICROMState *s = VAPIC(dev); - - s->state = VAPIC_INACTIVE; - s->rom_state_paddr = 0; - vapic_enable_tpr_reporting(false); -} - -/* - * Set the IRQ polling hypercalls to the supported variant: - * - vmcall if using KVM in-kernel irqchip - * - 32-bit VAPIC port write otherwise - */ -static int patch_hypercalls(VAPICROMState *s) -{ - hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; - static const uint8_t vmcall_pattern[] = { /* vmcall */ - 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1 - }; - static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */ - 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e - }; - uint8_t alternates[2]; - const uint8_t *pattern; - const uint8_t *patch; - off_t pos; - uint8_t *rom; - - rom = g_malloc(s->rom_size); - cpu_physical_memory_read(rom_paddr, rom, s->rom_size); - - for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) { - if (kvm_irqchip_in_kernel()) { - pattern = outl_pattern; - alternates[0] = outl_pattern[7]; - alternates[1] = outl_pattern[7]; - patch = &vmcall_pattern[5]; - } else { - pattern = vmcall_pattern; - alternates[0] = vmcall_pattern[7]; - alternates[1] = 0xd9; /* AMD's VMMCALL */ - patch = &outl_pattern[5]; - } - if (memcmp(rom + pos, pattern, 7) == 0 && - (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) { - cpu_physical_memory_write(rom_paddr + pos + 5, patch, 3); - /* - * Don't flush the tb here. Under ordinary conditions, the patched - * calls are miles away from the current IP. Under malicious - * conditions, the guest could trick us to crash. - */ - } - } - - g_free(rom); - return 0; -} - -/* - * For TCG mode or the time KVM honors read-only memory regions, we need to - * enable write access to the option ROM so that variables can be updated by - * the guest. - */ -static int vapic_map_rom_writable(VAPICROMState *s) -{ - hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; - MemoryRegionSection section; - MemoryRegion *as; - 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); - object_unparent(OBJECT(&s->rom)); - } - - /* grab RAM memory region (region @rom_paddr may still be pc.rom) */ - section = memory_region_find(as, 0, 1); - - /* read ROM size from RAM region */ - if (rom_paddr + 2 >= memory_region_size(section.mr)) { - return -1; - } - ram = memory_region_get_ram_ptr(section.mr); - rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE; - if (rom_size == 0) { - return -1; - } - s->rom_size = rom_size; - - /* We need to round to avoid creating subpages - * from which we cannot run code. */ - rom_size += rom_paddr & ~TARGET_PAGE_MASK; - rom_paddr &= TARGET_PAGE_MASK; - rom_size = TARGET_PAGE_ALIGN(rom_size); - - 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); - s->rom_mapped_writable = true; - memory_region_unref(section.mr); - - return 0; -} - -static int vapic_prepare(VAPICROMState *s) -{ - if (vapic_map_rom_writable(s) < 0) { - return -1; - } - - if (patch_hypercalls(s) < 0) { - return -1; - } - - vapic_enable_tpr_reporting(true); - - return 0; -} - -static void vapic_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - VAPICROMState *s = opaque; - X86CPU *cpu; - CPUX86State *env; - hwaddr rom_paddr; - - if (!current_cpu) { - return; - } - - cpu_synchronize_state(current_cpu); - cpu = X86_CPU(current_cpu); - env = &cpu->env; - - /* - * The VAPIC supports two PIO-based hypercalls, both via port 0x7E. - * o 16-bit write access: - * Reports the option ROM initialization to the hypervisor. Written - * value is the offset of the state structure in the ROM. - * o 8-bit write access: - * Reactivates the VAPIC after a guest hibernation, i.e. after the - * option ROM content has been re-initialized by a guest power cycle. - * o 32-bit write access: - * Poll for pending IRQs, considering the current VAPIC state. - */ - switch (size) { - case 2: - if (s->state == VAPIC_INACTIVE) { - rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK; - s->rom_state_paddr = rom_paddr + data; - - s->state = VAPIC_STANDBY; - } - if (vapic_prepare(s) < 0) { - s->state = VAPIC_INACTIVE; - s->rom_state_paddr = 0; - break; - } - break; - case 1: - if (kvm_enabled()) { - /* - * Disable triggering instruction in ROM by writing a NOP. - * - * We cannot do this in TCG mode as the reported IP is not - * accurate. - */ - pause_all_vcpus(); - patch_byte(cpu, env->eip - 2, 0x66); - patch_byte(cpu, env->eip - 1, 0x90); - resume_all_vcpus(); - } - - if (s->state == VAPIC_ACTIVE) { - break; - } - if (update_rom_mapping(s, env, env->eip) < 0) { - break; - } - if (find_real_tpr_addr(s, env) < 0) { - break; - } - vapic_enable(s, cpu); - break; - default: - case 4: - if (!kvm_irqchip_in_kernel()) { - apic_poll_irq(cpu->apic_state); - } - break; - } -} - -static uint64_t vapic_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffff; -} - -static const MemoryRegionOps vapic_ops = { - .write = vapic_write, - .read = vapic_read, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void vapic_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - 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); - sysbus_init_ioports(sbd, VAPIC_IO_PORT, 2); - - option_rom[nb_option_roms].name = "kvmvapic.bin"; - option_rom[nb_option_roms].bootindex = -1; - nb_option_roms++; -} - -static void do_vapic_enable(CPUState *cs, run_on_cpu_data data) -{ - VAPICROMState *s = data.host_ptr; - X86CPU *cpu = X86_CPU(cs); - - static const uint8_t enabled = 1; - cpu_physical_memory_write(s->vapic_paddr + offsetof(VAPICState, enabled), - &enabled, sizeof(enabled)); - apic_enable_vapic(cpu->apic_state, s->vapic_paddr); - s->state = VAPIC_ACTIVE; -} - -static void kvmvapic_vm_state_change(void *opaque, bool running, - RunState state) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - VAPICROMState *s = opaque; - uint8_t *zero; - - if (!running) { - return; - } - - if (s->state == VAPIC_ACTIVE) { - if (ms->smp.cpus == 1) { - run_on_cpu(first_cpu, do_vapic_enable, RUN_ON_CPU_HOST_PTR(s)); - } else { - zero = g_malloc0(s->rom_state.vapic_size); - cpu_physical_memory_write(s->vapic_paddr, zero, - s->rom_state.vapic_size); - g_free(zero); - } - } - - qemu_del_vm_change_state_handler(s->vmsentry); - s->vmsentry = NULL; -} - -static int vapic_post_load(void *opaque, int version_id) -{ - VAPICROMState *s = opaque; - - /* - * The old implementation of qemu-kvm did not provide the state - * VAPIC_STANDBY. Reconstruct it. - */ - if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) { - s->state = VAPIC_STANDBY; - } - - if (s->state != VAPIC_INACTIVE) { - if (vapic_prepare(s) < 0) { - return -1; - } - } - - if (!s->vmsentry) { - s->vmsentry = - qemu_add_vm_change_state_handler(kvmvapic_vm_state_change, s); - } - return 0; -} - -static const VMStateDescription vmstate_handlers = { - .name = "kvmvapic-handlers", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(set_tpr, VAPICHandlers), - VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), - VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8), - VMSTATE_UINT32(get_tpr_stack, VAPICHandlers), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_guest_rom = { - .name = "kvmvapic-guest-rom", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UNUSED(8), /* signature */ - VMSTATE_UINT32(vaddr, GuestROMState), - VMSTATE_UINT32(fixup_start, GuestROMState), - VMSTATE_UINT32(fixup_end, GuestROMState), - VMSTATE_UINT32(vapic_vaddr, GuestROMState), - VMSTATE_UINT32(vapic_size, GuestROMState), - VMSTATE_UINT32(vcpu_shift, GuestROMState), - VMSTATE_UINT32(real_tpr_addr, GuestROMState), - VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers), - VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_vapic = { - .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */ - .version_id = 1, - .minimum_version_id = 1, - .post_load = vapic_post_load, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom, - GuestROMState), - VMSTATE_UINT32(state, VAPICROMState), - VMSTATE_UINT32(real_tpr_addr, VAPICROMState), - VMSTATE_UINT32(rom_state_vaddr, VAPICROMState), - VMSTATE_UINT32(vapic_paddr, VAPICROMState), - VMSTATE_UINT32(rom_state_paddr, VAPICROMState), - VMSTATE_END_OF_LIST() - } -}; - -static void vapic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = vapic_reset; - dc->vmsd = &vmstate_vapic; - dc->realize = vapic_realize; -} - -static const TypeInfo vapic_type = { - .name = TYPE_VAPIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(VAPICROMState), - .class_init = vapic_class_init, -}; - -static void vapic_register(void) -{ - type_register_static(&vapic_type); -} - -type_init(vapic_register); diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 213e2e82b3..10bdfde27c 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -1,17 +1,21 @@ i386_ss = ss.source_set() i386_ss.add(files( 'fw_cfg.c', - 'kvmvapic.c', 'e820_memory_layout.c', + 'monitor.c', 'multiboot.c', 'x86.c', + 'x86-cpu.c', )) +i386_ss.add(when: 'CONFIG_APIC', if_true: files('vapic.c')) 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_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) +i386_ss.add(when: 'CONFIG_NITRO_ENCLAVE', if_true: files('nitro_enclave.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) @@ -20,8 +24,8 @@ 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( + 'x86-common.c', 'pc.c', 'pc_sysfw.c', 'acpi-build.c', diff --git a/hw/i386/microvm-dt.c b/hw/i386/microvm-dt.c index b3049e4f9f..fc5db6ed7f 100644 --- a/hw/i386/microvm-dt.c +++ b/hw/i386/microvm-dt.c @@ -34,7 +34,7 @@ #include "qemu/cutils.h" #include "qapi/error.h" #include "sysemu/device_tree.h" -#include "hw/char/serial.h" +#include "hw/char/serial-isa.h" #include "hw/i386/fw_cfg.h" #include "hw/rtc/mc146818rtc.h" #include "hw/sysbus.h" diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index b231ceda9a..86637afa0f 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -32,14 +32,14 @@ #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" #include "hw/intc/i8259.h" #include "hw/timer/i8254.h" #include "hw/rtc/mc146818rtc.h" -#include "hw/char/serial.h" +#include "hw/char/serial-isa.h" #include "hw/display/ramfb.h" #include "hw/i386/topology.h" #include "hw/i386/e820_memory_layout.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) { @@ -278,11 +278,12 @@ static void microvm_devices_init(MicrovmMachineState *mms) default_firmware = x86_machine_is_acpi_enabled(x86ms) ? MICROVM_BIOS_FILENAME : MICROVM_QBOOT_FILENAME; - x86_bios_rom_init(MACHINE(mms), default_firmware, get_system_memory(), true); + x86_bios_rom_init(x86ms, default_firmware, get_system_memory(), true); } static void microvm_memory_init(MicrovmMachineState *mms) { + MicrovmMachineClass *mmc = MICROVM_MACHINE_GET_CLASS(mms); MachineState *machine = MACHINE(mms); X86MachineState *x86ms = X86_MACHINE(mms); MemoryRegion *ram_below_4g, *ram_above_4g; @@ -324,13 +325,11 @@ 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_file(fw_cfg, "etc/e820", e820_table, - sizeof(struct e820_entry) * e820_get_num_entries()); rom_set_fw(fw_cfg); if (machine->kernel_filename != NULL) { - x86_load_linux(x86ms, fw_cfg, 0, true); + mmc->x86_load_linux(x86ms, fw_cfg, 0, true); } if (mms->option_roms) { @@ -391,9 +390,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; @@ -465,7 +463,7 @@ static void microvm_machine_state_init(MachineState *machine) microvm_devices_init(mms); } -static void microvm_machine_reset(MachineState *machine, ShutdownCause reason) +static void microvm_machine_reset(MachineState *machine, ResetType type) { MicrovmMachineState *mms = MICROVM_MACHINE(machine); CPUState *cs; @@ -478,7 +476,7 @@ static void microvm_machine_reset(MachineState *machine, ShutdownCause reason) mms->kernel_cmdline_fixed = true; } - qemu_devices_reset(reason); + qemu_devices_reset(type); CPU_FOREACH(cs) { cpu = X86_CPU(cs); @@ -587,9 +585,11 @@ static void microvm_machine_done(Notifier *notifier, void *data) { MicrovmMachineState *mms = container_of(notifier, MicrovmMachineState, machine_done); + X86MachineState *x86ms = X86_MACHINE(mms); acpi_setup_microvm(mms); dt_setup_microvm(mms); + fw_cfg_add_e820(x86ms->fw_cfg); } static void microvm_powerdown_req(Notifier *notifier, void *data) @@ -638,9 +638,12 @@ GlobalProperty microvm_properties[] = { static void microvm_class_init(ObjectClass *oc, void *data) { X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); + MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + mmc->x86_load_linux = x86_load_linux; + mc->init = microvm_machine_state_init; mc->family = "microvm_i386"; diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c new file mode 100644 index 0000000000..1ebd3564bf --- /dev/null +++ b/hw/i386/monitor.c @@ -0,0 +1,46 @@ +/* + * 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/monitor.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-target.h" +#include "hw/i386/x86.h" +#include "hw/rtc/mc146818rtc.h" + +#include CONFIG_DEVICES + +void qmp_rtc_reset_reinjection(Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(qdev_get_machine()); + +#ifdef CONFIG_MC146818RTC + if (x86ms->rtc) { + rtc_reset_reinjection(MC146818_RTC(x86ms->rtc)); + } +#else + assert(!x86ms->rtc); +#endif +} diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 963e29362e..b2648bff71 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -133,11 +133,11 @@ static void mb_add_mod(MultibootState *s, p = (char *)s->mb_buf + s->offset_mbinfo + MB_MOD_SIZE * s->mb_mods_count; - stl_p(p + MB_MOD_START, start); - stl_p(p + MB_MOD_END, end); - stl_p(p + MB_MOD_CMDLINE, cmdline_phys); + stl_le_p(p + MB_MOD_START, start); + stl_le_p(p + MB_MOD_END, end); + stl_le_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++; @@ -168,9 +168,9 @@ int load_multiboot(X86MachineState *x86ms, /* Ok, let's see if it is a multiboot image. The header is 12x32bit long, so the latest entry may be 8192 - 48. */ for (i = 0; i < (8192 - 48); i += 4) { - if (ldl_p(header+i) == 0x1BADB002) { - uint32_t checksum = ldl_p(header+i+8); - flags = ldl_p(header+i+4); + if (ldl_le_p(header + i) == 0x1BADB002) { + uint32_t checksum = ldl_le_p(header + i + 8); + flags = ldl_le_p(header + i + 4); checksum += flags; checksum += (uint32_t)0x1BADB002; if (!checksum) { @@ -223,11 +223,11 @@ int load_multiboot(X86MachineState *x86ms, mb_kernel_size, (size_t)mh_entry_addr); } else { /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ - uint32_t mh_header_addr = ldl_p(header+i+12); - uint32_t mh_load_end_addr = ldl_p(header+i+20); - uint32_t mh_bss_end_addr = ldl_p(header+i+24); + uint32_t mh_header_addr = ldl_le_p(header + i + 12); + uint32_t mh_load_end_addr = ldl_le_p(header + i + 20); + uint32_t mh_bss_end_addr = ldl_le_p(header + i + 24); - mh_load_addr = ldl_p(header+i+16); + mh_load_addr = ldl_le_p(header + i + 16); if (mh_header_addr < mh_load_addr) { error_report("invalid load_addr address"); exit(1); @@ -239,7 +239,7 @@ int load_multiboot(X86MachineState *x86ms, uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); uint32_t mb_load_size = 0; - mh_entry_addr = ldl_p(header+i+28); + mh_entry_addr = ldl_le_p(header + i + 28); if (mh_load_end_addr) { if (mh_load_end_addr < mh_load_addr) { @@ -353,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); @@ -364,26 +364,25 @@ int load_multiboot(X86MachineState *x86ms, /* Commandline support */ 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)); - - stl_p(bootinfo + MBI_MODS_ADDR, mbs.mb_buf_phys + mbs.offset_mbinfo); - stl_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */ + stl_le_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline)); + stl_le_p(bootinfo + MBI_BOOTLOADER, mb_add_bootloader(&mbs, + bootloader_name)); + stl_le_p(bootinfo + MBI_MODS_ADDR, mbs.mb_buf_phys + mbs.offset_mbinfo); + stl_le_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */ /* the kernel is where we want it to be now */ - stl_p(bootinfo + MBI_FLAGS, MULTIBOOT_FLAGS_MEMORY + stl_le_p(bootinfo + MBI_FLAGS, MULTIBOOT_FLAGS_MEMORY | MULTIBOOT_FLAGS_BOOT_DEVICE | MULTIBOOT_FLAGS_CMDLINE | MULTIBOOT_FLAGS_MODULES | MULTIBOOT_FLAGS_MMAP | MULTIBOOT_FLAGS_BOOTLOADER); - stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */ - stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP); + stl_le_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */ + stl_le_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/nitro_enclave.c b/hw/i386/nitro_enclave.c new file mode 100644 index 0000000000..b6263ae127 --- /dev/null +++ b/hw/i386/nitro_enclave.c @@ -0,0 +1,354 @@ +/* + * AWS nitro-enclave machine + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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 "qom/object_interfaces.h" + +#include "chardev/char.h" +#include "hw/sysbus.h" +#include "hw/core/eif.h" +#include "hw/i386/x86.h" +#include "hw/i386/microvm.h" +#include "hw/i386/nitro_enclave.h" +#include "hw/virtio/virtio-mmio.h" +#include "hw/virtio/virtio-nsm.h" +#include "hw/virtio/vhost-user-vsock.h" +#include "sysemu/hostmem.h" + +static BusState *find_free_virtio_mmio_bus(void) +{ + BusChild *kid; + BusState *bus = sysbus_get_default(); + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + 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; + if (QTAILQ_EMPTY(&mmio_bus->children)) { + return mmio_bus; + } + } + } + + return NULL; +} + +static void vhost_user_vsock_init(NitroEnclaveMachineState *nems) +{ + DeviceState *dev = qdev_new(TYPE_VHOST_USER_VSOCK); + VHostUserVSock *vsock = VHOST_USER_VSOCK(dev); + BusState *bus; + + if (!nems->vsock) { + error_report("A valid chardev id for vhost-user-vsock device must be " + "provided using the 'vsock' machine option"); + exit(1); + } + + bus = find_free_virtio_mmio_bus(); + if (!bus) { + error_report("Failed to find bus for vhost-user-vsock device"); + exit(1); + } + + Chardev *chardev = qemu_chr_find(nems->vsock); + if (!chardev) { + error_report("Failed to find chardev with id %s", nems->vsock); + exit(1); + } + + vsock->conf.chardev.chr = chardev; + + qdev_realize_and_unref(dev, bus, &error_fatal); +} + +static void virtio_nsm_init(NitroEnclaveMachineState *nems) +{ + DeviceState *dev = qdev_new(TYPE_VIRTIO_NSM); + VirtIONSM *vnsm = VIRTIO_NSM(dev); + BusState *bus = find_free_virtio_mmio_bus(); + + if (!bus) { + error_report("Failed to find bus for virtio-nsm device."); + exit(1); + } + + qdev_prop_set_string(dev, "module-id", nems->id); + + qdev_realize_and_unref(dev, bus, &error_fatal); + nems->vnsm = vnsm; +} + +static void nitro_enclave_devices_init(NitroEnclaveMachineState *nems) +{ + vhost_user_vsock_init(nems); + virtio_nsm_init(nems); +} + +static void nitro_enclave_machine_state_init(MachineState *machine) +{ + NitroEnclaveMachineClass *ne_class = + NITRO_ENCLAVE_MACHINE_GET_CLASS(machine); + NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine); + + ne_class->parent_init(machine); + nitro_enclave_devices_init(ne_state); +} + +static void nitro_enclave_machine_reset(MachineState *machine, ResetType type) +{ + NitroEnclaveMachineClass *ne_class = + NITRO_ENCLAVE_MACHINE_GET_CLASS(machine); + NitroEnclaveMachineState *ne_state = NITRO_ENCLAVE_MACHINE(machine); + + ne_class->parent_reset(machine, type); + + memset(ne_state->vnsm->pcrs, 0, sizeof(ne_state->vnsm->pcrs)); + + /* PCR0 */ + ne_state->vnsm->extend_pcr(ne_state->vnsm, 0, ne_state->image_sha384, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + /* PCR1 */ + ne_state->vnsm->extend_pcr(ne_state->vnsm, 1, ne_state->bootstrap_sha384, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + /* PCR2 */ + ne_state->vnsm->extend_pcr(ne_state->vnsm, 2, ne_state->app_sha384, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + /* PCR3 */ + if (ne_state->parent_role) { + ne_state->vnsm->extend_pcr(ne_state->vnsm, 3, + (uint8_t *) ne_state->parent_role, + strlen(ne_state->parent_role)); + } + /* PCR4 */ + if (ne_state->parent_id) { + ne_state->vnsm->extend_pcr(ne_state->vnsm, 4, + (uint8_t *) ne_state->parent_id, + strlen(ne_state->parent_id)); + } + /* PCR8 */ + if (ne_state->signature_found) { + ne_state->vnsm->extend_pcr(ne_state->vnsm, 8, + ne_state->fingerprint_sha384, + QCRYPTO_HASH_DIGEST_LEN_SHA384); + } + + /* First 16 PCRs are locked from boot and reserved for nitro enclave */ + for (int i = 0; i < 16; ++i) { + ne_state->vnsm->lock_pcr(ne_state->vnsm, i); + } +} + +static void nitro_enclave_machine_initfn(Object *obj) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + X86MachineState *x86ms = X86_MACHINE(obj); + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + nems->id = g_strdup("i-234-enc5678"); + + /* AWS nitro enclaves have PCIE and ACPI disabled */ + mms->pcie = ON_OFF_AUTO_OFF; + x86ms->acpi = ON_OFF_AUTO_OFF; +} + +static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg, + int acpi_data_size, bool pvh_enabled) +{ + Error *err = NULL; + char *eif_kernel, *eif_initrd, *eif_cmdline; + MachineState *machine = MACHINE(x86ms); + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(x86ms); + + if (!read_eif_file(machine->kernel_filename, machine->initrd_filename, + &eif_kernel, &eif_initrd, &eif_cmdline, + nems->image_sha384, nems->bootstrap_sha384, + nems->app_sha384, nems->fingerprint_sha384, + &(nems->signature_found), &err)) { + error_report_err(err); + exit(1); + } + + g_free(machine->kernel_filename); + machine->kernel_filename = eif_kernel; + g_free(machine->initrd_filename); + machine->initrd_filename = eif_initrd; + + /* + * If kernel cmdline argument was provided, let's concatenate it to the + * extracted EIF kernel cmdline. + */ + if (machine->kernel_cmdline != NULL) { + char *cmd = g_strdup_printf("%s %s", eif_cmdline, + machine->kernel_cmdline); + g_free(eif_cmdline); + g_free(machine->kernel_cmdline); + machine->kernel_cmdline = cmd; + } else { + machine->kernel_cmdline = eif_cmdline; + } + + x86_load_linux(x86ms, fw_cfg, 0, true); + + unlink(machine->kernel_filename); + unlink(machine->initrd_filename); + return; +} + +static bool create_memfd_backend(MachineState *ms, const char *path, + Error **errp) +{ + Object *obj; + MachineClass *mc = MACHINE_GET_CLASS(ms); + bool r = false; + + obj = object_new(TYPE_MEMORY_BACKEND_MEMFD); + if (!object_property_set_int(obj, "size", ms->ram_size, errp)) { + goto out; + } + object_property_add_child(object_get_objects_root(), mc->default_ram_id, + obj); + + if (!user_creatable_complete(USER_CREATABLE(obj), errp)) { + goto out; + } + r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp); + +out: + object_unref(obj); + return r; +} + +static char *nitro_enclave_get_vsock_chardev_id(Object *obj, Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + return g_strdup(nems->vsock); +} + +static void nitro_enclave_set_vsock_chardev_id(Object *obj, const char *value, + Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + g_free(nems->vsock); + nems->vsock = g_strdup(value); +} + +static char *nitro_enclave_get_id(Object *obj, Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + return g_strdup(nems->id); +} + +static void nitro_enclave_set_id(Object *obj, const char *value, + Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + g_free(nems->id); + nems->id = g_strdup(value); +} + +static char *nitro_enclave_get_parent_role(Object *obj, Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + return g_strdup(nems->parent_role); +} + +static void nitro_enclave_set_parent_role(Object *obj, const char *value, + Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + g_free(nems->parent_role); + nems->parent_role = g_strdup(value); +} + +static char *nitro_enclave_get_parent_id(Object *obj, Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + return g_strdup(nems->parent_id); +} + +static void nitro_enclave_set_parent_id(Object *obj, const char *value, + Error **errp) +{ + NitroEnclaveMachineState *nems = NITRO_ENCLAVE_MACHINE(obj); + + g_free(nems->parent_id); + nems->parent_id = g_strdup(value); +} + +static void nitro_enclave_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc); + NitroEnclaveMachineClass *nemc = NITRO_ENCLAVE_MACHINE_CLASS(oc); + + mmc->x86_load_linux = x86_load_eif; + + mc->family = "nitro_enclave_i386"; + mc->desc = "AWS Nitro Enclave"; + + nemc->parent_init = mc->init; + mc->init = nitro_enclave_machine_state_init; + + nemc->parent_reset = mc->reset; + mc->reset = nitro_enclave_machine_reset; + + mc->create_default_memdev = create_memfd_backend; + + object_class_property_add_str(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID, + nitro_enclave_get_vsock_chardev_id, + nitro_enclave_set_vsock_chardev_id); + object_class_property_set_description(oc, NITRO_ENCLAVE_VSOCK_CHARDEV_ID, + "Set chardev id for vhost-user-vsock " + "device"); + + object_class_property_add_str(oc, NITRO_ENCLAVE_ID, nitro_enclave_get_id, + nitro_enclave_set_id); + object_class_property_set_description(oc, NITRO_ENCLAVE_ID, + "Set enclave identifier"); + + object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ROLE, + nitro_enclave_get_parent_role, + nitro_enclave_set_parent_role); + object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ROLE, + "Set parent instance IAM role ARN"); + + object_class_property_add_str(oc, NITRO_ENCLAVE_PARENT_ID, + nitro_enclave_get_parent_id, + nitro_enclave_set_parent_id); + object_class_property_set_description(oc, NITRO_ENCLAVE_PARENT_ID, + "Set parent instance identifier"); +} + +static const TypeInfo nitro_enclave_machine_info = { + .name = TYPE_NITRO_ENCLAVE_MACHINE, + .parent = TYPE_MICROVM_MACHINE, + .instance_size = sizeof(NitroEnclaveMachineState), + .instance_init = nitro_enclave_machine_initfn, + .class_size = sizeof(NitroEnclaveMachineClass), + .class_init = nitro_enclave_class_init, +}; + +static void nitro_enclave_machine_init(void) +{ + type_register_static(&nitro_enclave_machine_info); +} +type_init(nitro_enclave_machine_init); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index ec5a10534b..317aaca25a 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -24,80 +24,52 @@ #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/serial-isa.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/pci-bridge/pci_expander_bridge.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/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 "hw/cxl/cxl_host.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/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 "target/i386/cpu.h" -#include "qapi/qmp/qerror.h" #include "e820_memory_layout.h" -#include "fw_cfg.h" #include "trace.h" +#include "sev.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. @@ -107,6 +79,38 @@ { "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_9_1[] = { + { "ICH9-LPC", "x-smi-swsmi-timer", "off" }, + { "ICH9-LPC", "x-smi-periodic-timer", "off" }, + { TYPE_INTEL_IOMMU_DEVICE, "stale-tm", "on" }, +}; +const size_t pc_compat_9_1_len = G_N_ELEMENTS(pc_compat_9_1); + +GlobalProperty pc_compat_9_0[] = { + { TYPE_X86_CPU, "x-amd-topoext-features-only", "false" }, + { TYPE_X86_CPU, "x-l1-cache-per-thread", "false" }, + { TYPE_X86_CPU, "guest-phys-bits", "0" }, + { "sev-guest", "legacy-vm-type", "on" }, + { TYPE_X86_CPU, "legacy-multi-node", "on" }, +}; +const size_t pc_compat_9_0_len = G_N_ELEMENTS(pc_compat_9_0); + +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); @@ -269,129 +273,15 @@ GlobalProperty pc_compat_2_4[] = { }; const size_t pc_compat_2_4_len = G_N_ELEMENTS(pc_compat_2_4); -GlobalProperty pc_compat_2_3[] = { - PC_CPU_MODEL_IDS("2.3.0") - { TYPE_X86_CPU, "arat", "off" }, - { "qemu64" "-" TYPE_X86_CPU, "min-level", "4" }, - { "kvm64" "-" TYPE_X86_CPU, "min-level", "5" }, - { "pentium3" "-" TYPE_X86_CPU, "min-level", "2" }, - { "n270" "-" TYPE_X86_CPU, "min-level", "5" }, - { "Conroe" "-" TYPE_X86_CPU, "min-level", "4" }, - { "Penryn" "-" TYPE_X86_CPU, "min-level", "4" }, - { "Nehalem" "-" TYPE_X86_CPU, "min-level", "4" }, - { "n270" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Penryn" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Conroe" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Nehalem" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Westmere" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "SandyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "IvyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Haswell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Haswell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Broadwell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { "Broadwell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, - { TYPE_X86_CPU, "kvm-no-smi-migration", "on" }, -}; -const size_t pc_compat_2_3_len = G_N_ELEMENTS(pc_compat_2_3); - -GlobalProperty pc_compat_2_2[] = { - PC_CPU_MODEL_IDS("2.2.0") - { "kvm64" "-" TYPE_X86_CPU, "vme", "off" }, - { "kvm32" "-" TYPE_X86_CPU, "vme", "off" }, - { "Conroe" "-" TYPE_X86_CPU, "vme", "off" }, - { "Penryn" "-" TYPE_X86_CPU, "vme", "off" }, - { "Nehalem" "-" TYPE_X86_CPU, "vme", "off" }, - { "Westmere" "-" TYPE_X86_CPU, "vme", "off" }, - { "SandyBridge" "-" TYPE_X86_CPU, "vme", "off" }, - { "Haswell" "-" TYPE_X86_CPU, "vme", "off" }, - { "Broadwell" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G1" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G2" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G3" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "vme", "off" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "vme", "off" }, - { "Haswell" "-" TYPE_X86_CPU, "f16c", "off" }, - { "Haswell" "-" TYPE_X86_CPU, "rdrand", "off" }, - { "Broadwell" "-" TYPE_X86_CPU, "f16c", "off" }, - { "Broadwell" "-" TYPE_X86_CPU, "rdrand", "off" }, -}; -const size_t pc_compat_2_2_len = G_N_ELEMENTS(pc_compat_2_2); - -GlobalProperty pc_compat_2_1[] = { - PC_CPU_MODEL_IDS("2.1.0") - { "coreduo" "-" TYPE_X86_CPU, "vmx", "on" }, - { "core2duo" "-" TYPE_X86_CPU, "vmx", "on" }, -}; -const size_t pc_compat_2_1_len = G_N_ELEMENTS(pc_compat_2_1); - -GlobalProperty pc_compat_2_0[] = { - PC_CPU_MODEL_IDS("2.0.0") - { "virtio-scsi-pci", "any_layout", "off" }, - { "PIIX4_PM", "memory-hotplug-support", "off" }, - { "apic", "version", "0x11" }, - { "nec-usb-xhci", "superspeed-ports-first", "off" }, - { "nec-usb-xhci", "force-pcie-endcap", "on" }, - { "pci-serial", "prog_if", "0" }, - { "pci-serial-2x", "prog_if", "0" }, - { "pci-serial-4x", "prog_if", "0" }, - { "virtio-net-pci", "guest_announce", "off" }, - { "ICH9-LPC", "memory-hotplug-support", "off" }, -}; -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); +/* + * @PC_FW_DATA: + * Size of the chunk of memory at the top of RAM for the BIOS ACPI tables + * and other BIOS datastructures. + * + * BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K + * reported to be used at the moment, 32K should be enough for a while. + */ +#define PC_FW_DATA (0x20000 + 0x8000) GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) { @@ -401,7 +291,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; } @@ -416,7 +306,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) { @@ -434,19 +324,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 */ @@ -466,7 +356,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, }; @@ -485,32 +376,38 @@ 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; + int val, nb; FloppyDriveType fd_type[2] = { FLOPPY_DRIVE_TYPE_NONE, FLOPPY_DRIVE_TYPE_NONE }; +#ifdef CONFIG_FDC_ISA /* floppy type */ if (floppy) { - for (i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) { fd_type[i] = isa_fdc_get_drive_type(floppy, i); } } +#endif + 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++; @@ -528,14 +425,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; @@ -596,27 +488,29 @@ static 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++) { @@ -624,36 +518,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; @@ -662,10 +546,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); @@ -674,34 +558,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) @@ -719,15 +587,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 }; -static 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) @@ -746,7 +618,7 @@ void pc_machine_done(Notifier *notifier, void *data) PCMachineState, machine_done); X86MachineState *x86ms = X86_MACHINE(pcms); - cxl_hook_up_pxb_registers(pcms->bus, &pcms->cxl_devices_state, + cxl_hook_up_pxb_registers(pcms->pcibus, &pcms->cxl_devices_state, &error_fatal); if (pcms->cxl_devices_state.is_enabled) { @@ -756,28 +628,22 @@ void pc_machine_done(Notifier *notifier, void *data) /* 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_add_e820(x86ms->fw_cfg); 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 */ @@ -794,12 +660,12 @@ 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); - x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, - pcmc->pvh_enabled); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, pcmc->pvh_enabled); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || @@ -832,7 +698,6 @@ 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; @@ -840,10 +705,8 @@ static void pc_get_device_memory_range(PCMachineState *pcms, 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; - } + /* size device region assuming 1G page max alignment per slot */ + size += (1 * GiB) * machine->ram_slots; *base = addr; *device_mem_size = size; @@ -852,10 +715,12 @@ static void pc_get_device_memory_range(PCMachineState *pcms, 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) { + if (pcmc->has_reserved_memory && + (ms->ram_size < ms->maxram_size)) { pc_get_device_memory_range(pcms, &cxl_base, &size); cxl_base += size; } else { @@ -885,13 +750,39 @@ static uint64_t pc_get_cxl_range_end(PCMachineState *pcms) 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); - /* 32-bit systems don't have hole64 thus return max CPU address */ - if (cpu->phys_bits <= 32) { + 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; } - return pc_pci_hole64_start() + pci_hole64_size - 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; } /* @@ -931,7 +822,6 @@ static hwaddr pc_max_used_gpa(PCMachineState *pcms, uint64_t pci_hole64_size) void pc_memory_init(PCMachineState *pcms, MemoryRegion *system_memory, MemoryRegion *rom_memory, - MemoryRegion **ram_memory, uint64_t pci_hole64_size) { int linux_boot, i; @@ -989,7 +879,6 @@ void pc_memory_init(PCMachineState *pcms, * 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); @@ -1020,13 +909,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; + hwaddr device_mem_base; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported amount of memory slots: %"PRIu64, @@ -1041,19 +928,14 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } - pc_get_device_memory_range(pcms, &machine->device_memory->base, &device_mem_size); + pc_get_device_memory_range(pcms, &device_mem_base, &device_mem_size); - 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 (pcms->cxl_devices_state.is_enabled) { @@ -1086,10 +968,15 @@ void pc_memory_init(PCMachineState *pcms, pc_system_firmware_init(pcms, rom_memory); option_rom_mr = g_malloc(sizeof(*option_rom_mr)); - memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, - &error_fatal); - if (pcmc->pci_enabled) { - memory_region_set_readonly(option_rom_mr, true); + if (machine_require_guest_memfd(machine)) { + memory_region_init_ram_guest_memfd(option_rom_mr, NULL, "pc.rom", + PC_ROM_SIZE, &error_fatal); + } else { + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, + &error_fatal); + if (pcmc->pci_enabled) { + memory_region_set_readonly(option_rom_mr, true); + } } memory_region_add_subregion_overlap(rom_memory, PC_ROM_MIN_VGA, @@ -1101,9 +988,8 @@ 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) { @@ -1118,8 +1004,7 @@ void pc_memory_init(PCMachineState *pcms, } if (linux_boot) { - x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, - pcmc->pvh_enabled); + x86_load_linux(x86ms, fw_cfg, PC_FW_DATA, pcmc->pvh_enabled); } for (i = 0; i < nb_option_roms; i++) { @@ -1197,12 +1082,12 @@ static const MemoryRegionOps ioportF0_io_ops = { }; static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, - bool create_i8042, bool no_vmport) + bool create_i8042, bool no_vmport, Error **errp) { int i; DriveInfo *fd[MAX_FD]; qemu_irq *a20_line; - ISADevice *fdc, *i8042, *port92, *vmmouse; + ISADevice *i8042, *port92, *vmmouse; serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); @@ -1212,14 +1097,20 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, create_fdctrl |= !!fd[i]; } if (create_fdctrl) { - fdc = isa_new(TYPE_ISA_FDC); +#ifdef CONFIG_FDC_ISA + ISADevice *fdc = isa_new(TYPE_ISA_FDC); if (fdc) { isa_realize_and_unref(fdc, isa_bus, &error_fatal); isa_fdc_init_drives(fdc, fd); } +#endif } if (!create_i8042) { + if (!no_vmport) { + error_setg(errp, + "vmport requires the i8042 controller to be enabled"); + } return; } @@ -1238,7 +1129,8 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, 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); @@ -1246,7 +1138,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) { @@ -1254,7 +1146,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); @@ -1268,21 +1159,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); @@ -1292,16 +1181,36 @@ 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(); + } +#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)) { @@ -1314,29 +1223,38 @@ 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); + } + + if (pcms->vmport == ON_OFF_AUTO_AUTO) { + pcms->vmport = (xen_enabled() || !pcms->i8042_enabled) + ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; } /* Super I/O */ pc_superio_init(isa_bus, create_fdctrl, pcms->i8042_enabled, - pcms->vmport != ON_OFF_AUTO_ON); + pcms->vmport != ON_OFF_AUTO_ON, &error_fatal); } 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 */ + if (pci_bus) { + pci_init_nic_devices(pci_bus, mc->default_nic); + } + rom_reset_order_override(); } @@ -1362,16 +1280,13 @@ void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - const PCMachineState *pcms = PC_MACHINE(hotplug_dev); const X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - const uint64_t legacy_align = TARGET_PAGE_SIZE; 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. */ @@ -1392,8 +1307,7 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), - pcmc->enforce_aligned_dimm ? NULL : &legacy_align, errp); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } static void pc_memory_plug(HotplugHandler *hotplug_dev, @@ -1419,7 +1333,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. */ @@ -1456,66 +1370,18 @@ 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. - */ - 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); + /* 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), 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, @@ -1525,17 +1391,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); } @@ -1549,6 +1415,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); } } @@ -1559,9 +1427,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); } } @@ -1572,9 +1441,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))); @@ -1588,9 +1457,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))); @@ -1602,9 +1470,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); } @@ -1612,21 +1480,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) { @@ -1644,6 +1497,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); @@ -1782,26 +1649,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); @@ -1815,6 +1679,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; @@ -1822,10 +1687,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; @@ -1833,21 +1699,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"); - cxl_machine_init(obj, &pcms->cxl_devices_state); + 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, ShutdownCause reason) +static void pc_machine_reset(MachineState *machine, ResetType type) { CPUState *cs; X86CPU *cpu; - qemu_devices_reset(reason); + qemu_devices_reset(type); /* Reset APIC after devices have been reset to cancel * any changes that qemu_devices_reset() might have done. @@ -1862,7 +1734,7 @@ static void pc_machine_reset(MachineState *machine, ShutdownCause reason) static void pc_machine_wakeup(MachineState *machine) { cpu_synchronize_all_states(); - pc_machine_reset(machine, SHUTDOWN_CAUSE_NONE); + pc_machine_reset(machine, RESET_TYPE_WAKEUP); cpu_synchronize_all_post_reset(); } @@ -1888,30 +1760,23 @@ 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); pcmc->pci_enabled = true; pcmc->has_acpi_build = true; - pcmc->rsdp_in_ram = true; pcmc->smbios_defaults = true; - 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->isa_bios_alias = true; pcmc->pvh_enabled = true; pcmc->kvmclock_create_always = 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; - mc->cpu_index_to_instance_props = x86_cpu_index_to_props; - mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; - mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->auto_enable_numa_with_memhp = true; mc->auto_enable_numa_with_memdev = true; mc->has_hotpluggable_cpus = true; @@ -1927,7 +1792,9 @@ 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->smp_props.modules_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, @@ -1935,10 +1802,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); @@ -1962,6 +1825,8 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, PC_MACHINE_I8042, pc_machine_get_i8042, pc_machine_set_i8042); + object_class_property_set_description(oc, PC_MACHINE_I8042, + "Enable/disable Intel 8042 PS/2 controller emulation"); object_class_property_add_bool(oc, "default-bus-bypass-iommu", pc_machine_get_default_bus_bypass_iommu, @@ -1978,6 +1843,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 04f793cca1..2bf6865d40 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -26,46 +26,49 @@ #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/ide/piix.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 "hw/acpi/piix4.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/xen.h" #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 }; @@ -73,28 +76,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; - 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; - DeviceState *i440fx_host; + uint64_t hole64_size = 0; /* * Calculate ram split, for memory below and above 4G. It's a bit @@ -130,6 +152,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 */ } @@ -160,7 +183,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); } @@ -168,33 +191,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; - i440fx_host = qdev_new(host_type); - hole64_size = object_property_get_uint(OBJECT(i440fx_host), + + 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); + + 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); - } else { - pci_memory = NULL; - rom_memory = system_memory; - i440fx_host = NULL; - hole64_size = 0; - } - - pc_guest_info_init(pcms); - - 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); } /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, - rom_memory, &ram_memory, hole64_size); + 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 */ @@ -205,69 +237,87 @@ 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; - const char *type = xen_enabled() ? TYPE_PIIX3_XEN_DEVICE - : TYPE_PIIX3_DEVICE; + DeviceState *dev; + size_t i; - pci_bus = i440fx_init(pci_type, - i440fx_host, - 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); - pci_dev = pci_create_simple_multifunction(pci_bus, -1, true, type); - piix3 = PIIX3_PCI_DEVICE(pci_dev); - piix3->pic = x86ms->gsi; - piix3_devfn = piix3->dev.devfn; - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(piix3), "isa.0")); + 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; - isa_bus = isa_bus_new(NULL, get_system_memory(), system_io, + isa_bus = isa_bus_new(NULL, system_memory, system_io, &error_abort); - i8257_dma_init(isa_bus, 0); + 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); - - assert(pcms->vmport != ON_OFF_AUTO__MAX); - if (pcms->vmport == ON_OFF_AUTO_AUTO) { - pcms->vmport = xen_enabled() ? ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; - } + pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); /* init basic PC hardware */ - pc_basic_device_init(pcms, isa_bus, x86ms->gsi, &rtc_state, true, - 0x4); + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 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, TYPE_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; @@ -283,27 +333,14 @@ 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))) { - PCIDevice *piix4_pm; - + if (piix4_pm) { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); - piix4_pm = pci_new(piix3_devfn + 3, TYPE_PIIX4_PM); - qdev_prop_set_uint32(DEVICE(piix4_pm), "smb_io_base", 0xb100); - qdev_prop_set_bit(DEVICE(piix4_pm), "smm-enabled", - x86_machine_is_smm_enabled(x86ms)); - pci_realize_and_unref(piix4_pm, pci_bus, &error_fatal); - qdev_connect_gpio_out(DEVICE(piix4_pm), 0, x86ms->gsi[9]); 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. */ @@ -315,7 +352,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) { @@ -325,62 +362,57 @@ static void pc_init1(MachineState *machine, } } -/* 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 - * one, and implement any pc-*-2.4 (and newer) compat code in - * hw_compat_*, pc_compat_*, or * pc_*_machine_options(). - */ +typedef enum PCSouthBridgeOption { + PC_SOUTH_BRIDGE_OPTION_PIIX3, + PC_SOUTH_BRIDGE_OPTION_PIIX4, + PC_SOUTH_BRIDGE_OPTION_MAX, +} PCSouthBridgeOption; -static void pc_compat_2_3_fn(MachineState *machine) +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) { - X86MachineState *x86ms = X86_MACHINE(machine); - if (kvm_enabled()) { - x86ms->smm = ON_OFF_AUTO_OFF; + 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_compat_2_2_fn(MachineState *machine) +static void pc_set_south_bridge(Object *obj, int value, Error **errp) { - pc_compat_2_3_fn(machine); -} + PCMachineState *pcms = PC_MACHINE(obj); -static void pc_compat_2_1_fn(MachineState *machine) -{ - pc_compat_2_2_fn(machine); - x86_cpu_change_kvm_default("svm", NULL); -} + if (value < 0) { + error_setg(errp, "Value can't be negative"); + return; + } -static void pc_compat_2_0_fn(MachineState *machine) -{ - pc_compat_2_1_fn(machine); -} + if (value >= PCSouthBridgeOption_lookup.size) { + error_setg(errp, "Value too big"); + return; + } -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); + pcms->south_bridge = PCSouthBridgeOption_lookup.array[value]; } #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 @@ -390,9 +422,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) @@ -405,447 +435,355 @@ static void pc_xen_hvm_init(MachineState *machine) } pc_xen_hvm_init_pci(machine); - xen_igd_reserve_slot(pcms->bus); - pci_create_simple(pcms->bus, -1, "xen-platform"); + xen_igd_reserve_slot(pcms->pcibus); + pci_create_simple(pcms->pcibus, -1, "xen-platform"); } #endif -#define DEFINE_I440FX_MACHINE(suffix, name, compatfn, optionfn) \ - static void pc_init_##suffix(MachineState *machine) \ - { \ - void (*compat)(MachineState *m) = (compatfn); \ - if (compat) { \ - compat(machine); \ - } \ - pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \ - TYPE_I440FX_PCI_DEVICE); \ - } \ - DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) +static void pc_i440fx_init(MachineState *machine) +{ + pc_init1(machine, TYPE_I440FX_PCI_DEVICE); +} + +#define DEFINE_I440FX_MACHINE(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_i440fx, "pc-i440fx", pc_i440fx_init, major, minor); 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_floppy = !module_object_class_by_name(TYPE_ISA_FDC); + 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_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_9_2_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(v7_2, "pc-i440fx-7.2", NULL, - pc_i440fx_7_2_machine_options); +DEFINE_I440FX_MACHINE(9, 2); -static void pc_i440fx_7_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_9_1_options(MachineClass *m) { - pc_i440fx_7_2_machine_options(m); + pc_i440fx_machine_9_2_options(m); m->alias = NULL; m->is_default = false; + compat_props_add(m->compat_props, hw_compat_9_1, hw_compat_9_1_len); + compat_props_add(m->compat_props, pc_compat_9_1, pc_compat_9_1_len); +} + +DEFINE_I440FX_MACHINE(9, 1); + +static void pc_i440fx_machine_9_0_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_machine_9_1_options(m); + m->smbios_memory_device_size = 16 * GiB; + + compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); + compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); + pcmc->isa_bios_alias = false; +} + +DEFINE_I440FX_MACHINE(9, 0); + +static void pc_i440fx_machine_8_2_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_machine_9_0_options(m); + + 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(8, 2); + +static void pc_i440fx_machine_8_1_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_machine_8_2_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(8, 1); + +static void pc_i440fx_machine_8_0_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_machine_8_1_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(8, 0); + +static void pc_i440fx_machine_7_2_options(MachineClass *m) +{ + pc_i440fx_machine_8_0_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(7, 2) + +static void pc_i440fx_machine_7_1_options(MachineClass *m) +{ + pc_i440fx_machine_7_2_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, - pc_i440fx_7_1_machine_options); +DEFINE_I440FX_MACHINE(7, 1); -static void pc_i440fx_7_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_7_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_7_1_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_7_1_options(m); 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); } -DEFINE_I440FX_MACHINE(v7_0, "pc-i440fx-7.0", NULL, - pc_i440fx_7_0_machine_options); +DEFINE_I440FX_MACHINE(7, 0); -static void pc_i440fx_6_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_6_2_options(MachineClass *m) { - pc_i440fx_7_0_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_7_0_options(m); 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); } -DEFINE_I440FX_MACHINE(v6_2, "pc-i440fx-6.2", NULL, - pc_i440fx_6_2_machine_options); +DEFINE_I440FX_MACHINE(6, 2); -static void pc_i440fx_6_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_6_1_options(MachineClass *m) { - pc_i440fx_6_2_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_6_2_options(m); 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; } -DEFINE_I440FX_MACHINE(v6_1, "pc-i440fx-6.1", NULL, - pc_i440fx_6_1_machine_options); +DEFINE_I440FX_MACHINE(6, 1); -static void pc_i440fx_6_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_6_0_options(MachineClass *m) { - pc_i440fx_6_1_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_6_1_options(m); 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); } -DEFINE_I440FX_MACHINE(v6_0, "pc-i440fx-6.0", NULL, - pc_i440fx_6_0_machine_options); +DEFINE_I440FX_MACHINE(6, 0); -static void pc_i440fx_5_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_5_2_options(MachineClass *m) { - pc_i440fx_6_0_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_6_0_options(m); 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); } -DEFINE_I440FX_MACHINE(v5_2, "pc-i440fx-5.2", NULL, - pc_i440fx_5_2_machine_options); +DEFINE_I440FX_MACHINE(5, 2); -static void pc_i440fx_5_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_5_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_5_2_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_5_2_options(m); 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; pcmc->pci_root_uid = 1; } -DEFINE_I440FX_MACHINE(v5_1, "pc-i440fx-5.1", NULL, - pc_i440fx_5_1_machine_options); +DEFINE_I440FX_MACHINE(5, 1); -static void pc_i440fx_5_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_5_0_options(MachineClass *m) { - pc_i440fx_5_1_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_5_1_options(m); 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); m->auto_enable_numa_with_memdev = false; } -DEFINE_I440FX_MACHINE(v5_0, "pc-i440fx-5.0", NULL, - pc_i440fx_5_0_machine_options); +DEFINE_I440FX_MACHINE(5, 0); -static void pc_i440fx_4_2_machine_options(MachineClass *m) +static void pc_i440fx_machine_4_2_options(MachineClass *m) { - pc_i440fx_5_0_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_5_0_options(m); compat_props_add(m->compat_props, hw_compat_4_2, hw_compat_4_2_len); compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); } -DEFINE_I440FX_MACHINE(v4_2, "pc-i440fx-4.2", NULL, - pc_i440fx_4_2_machine_options); +DEFINE_I440FX_MACHINE(4, 2); -static void pc_i440fx_4_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_4_1_options(MachineClass *m) { - pc_i440fx_4_2_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_4_2_options(m); 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); } -DEFINE_I440FX_MACHINE(v4_1, "pc-i440fx-4.1", NULL, - pc_i440fx_4_1_machine_options); +DEFINE_I440FX_MACHINE(4, 1); -static void pc_i440fx_4_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_4_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_4_1_machine_options(m); - m->alias = NULL; - m->is_default = false; + pc_i440fx_machine_4_1_options(m); 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); } -DEFINE_I440FX_MACHINE(v4_0, "pc-i440fx-4.0", NULL, - pc_i440fx_4_0_machine_options); +DEFINE_I440FX_MACHINE(4, 0); -static void pc_i440fx_3_1_machine_options(MachineClass *m) +static void pc_i440fx_machine_3_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_4_0_machine_options(m); - m->is_default = false; + pc_i440fx_machine_4_0_options(m); 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); } -DEFINE_I440FX_MACHINE(v3_1, "pc-i440fx-3.1", NULL, - pc_i440fx_3_1_machine_options); +DEFINE_I440FX_MACHINE(3, 1); -static void pc_i440fx_3_0_machine_options(MachineClass *m) +static void pc_i440fx_machine_3_0_options(MachineClass *m) { - pc_i440fx_3_1_machine_options(m); + pc_i440fx_machine_3_1_options(m); compat_props_add(m->compat_props, hw_compat_3_0, hw_compat_3_0_len); compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); } -DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, - pc_i440fx_3_0_machine_options); +DEFINE_I440FX_MACHINE(3, 0); -static void pc_i440fx_2_12_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_12_options(MachineClass *m) { - pc_i440fx_3_0_machine_options(m); + pc_i440fx_machine_3_0_options(m); compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } -DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, - pc_i440fx_2_12_machine_options); +DEFINE_I440FX_MACHINE(2, 12); -static void pc_i440fx_2_11_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_11_options(MachineClass *m) { - pc_i440fx_2_12_machine_options(m); + pc_i440fx_machine_2_12_options(m); 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); } -DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", NULL, - pc_i440fx_2_11_machine_options); +DEFINE_I440FX_MACHINE(2, 11); -static void pc_i440fx_2_10_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_10_options(MachineClass *m) { - pc_i440fx_2_11_machine_options(m); + pc_i440fx_machine_2_11_options(m); compat_props_add(m->compat_props, hw_compat_2_10, hw_compat_2_10_len); compat_props_add(m->compat_props, pc_compat_2_10, pc_compat_2_10_len); m->auto_enable_numa_with_memhp = false; } -DEFINE_I440FX_MACHINE(v2_10, "pc-i440fx-2.10", NULL, - pc_i440fx_2_10_machine_options); +DEFINE_I440FX_MACHINE(2, 10); -static void pc_i440fx_2_9_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_9_options(MachineClass *m) { - pc_i440fx_2_10_machine_options(m); + pc_i440fx_machine_2_10_options(m); compat_props_add(m->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); } -DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", NULL, - pc_i440fx_2_9_machine_options); +DEFINE_I440FX_MACHINE(2, 9); -static void pc_i440fx_2_8_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_8_options(MachineClass *m) { - pc_i440fx_2_9_machine_options(m); + pc_i440fx_machine_2_9_options(m); compat_props_add(m->compat_props, hw_compat_2_8, hw_compat_2_8_len); compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } -DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", NULL, - pc_i440fx_2_8_machine_options); +DEFINE_I440FX_MACHINE(2, 8); -static void pc_i440fx_2_7_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_7_options(MachineClass *m) { - pc_i440fx_2_8_machine_options(m); + pc_i440fx_machine_2_8_options(m); compat_props_add(m->compat_props, hw_compat_2_7, hw_compat_2_7_len); compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } -DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, - pc_i440fx_2_7_machine_options); +DEFINE_I440FX_MACHINE(2, 7); -static void pc_i440fx_2_6_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_6_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_2_7_machine_options(m); + pc_i440fx_machine_2_7_options(m); pcmc->legacy_cpu_hotplug = true; x86mc->fwcfg_dma_enabled = false; compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } -DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL, - pc_i440fx_2_6_machine_options); +DEFINE_I440FX_MACHINE(2, 6); -static void pc_i440fx_2_5_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_5_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - pc_i440fx_2_6_machine_options(m); + pc_i440fx_machine_2_6_options(m); x86mc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } -DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL, - pc_i440fx_2_5_machine_options); +DEFINE_I440FX_MACHINE(2, 5); -static void pc_i440fx_2_4_machine_options(MachineClass *m) +static void pc_i440fx_machine_2_4_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_i440fx_2_5_machine_options(m); + pc_i440fx_machine_2_5_options(m); m->hw_version = "2.4.0"; pcmc->broken_reserved_end = true; compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } -DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL, - pc_i440fx_2_4_machine_options) - -static void pc_i440fx_2_3_machine_options(MachineClass *m) -{ - pc_i440fx_2_4_machine_options(m); - m->hw_version = "2.3.0"; - 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); -} - -DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3_fn, - pc_i440fx_2_3_machine_options); - -static void pc_i440fx_2_2_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_3_machine_options(m); - m->hw_version = "2.2.0"; - m->default_machine_opts = "firmware=bios-256k.bin,suppress-vmdesc=on"; - 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; -} - -DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2_fn, - pc_i440fx_2_2_machine_options); - -static void pc_i440fx_2_1_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_2_machine_options(m); - m->hw_version = "2.1.0"; - m->default_display = NULL; - compat_props_add(m->compat_props, hw_compat_2_1, hw_compat_2_1_len); - compat_props_add(m->compat_props, pc_compat_2_1, pc_compat_2_1_len); - pcmc->smbios_uuid_encoded = false; - pcmc->enforce_aligned_dimm = false; -} - -DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1_fn, - pc_i440fx_2_1_machine_options); - -static void pc_i440fx_2_0_machine_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_2_1_machine_options(m); - m->hw_version = "2.0.0"; - compat_props_add(m->compat_props, pc_compat_2_0, pc_compat_2_0_len); - pcmc->smbios_legacy_mode = true; - pcmc->has_reserved_memory = false; - /* This value depends on the actual DSDT and SSDT compiled into - * the source QEMU; unfortunately it depends on the binary and - * not on the machine type, so we cannot make pc-i440fx-1.7 work on - * both QEMU 1.7 and QEMU 2.0. - * - * Large variations cause migration to fail for more than one - * consecutive value of the "-smp" maxcpus option. - * - * For small variations of the kind caused by different iasl versions, - * the 4k rounding usually leaves slack. However, there could be still - * one or two values that break. For QEMU 1.7 and QEMU 2.0 the - * slack is only ~10 bytes before one "-smp maxcpus" value breaks! - * - * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on - * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. - */ - pcmc->legacy_acpi_table_size = 6652; - pcmc->acpi_data_size = 0x10000; -} - -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); +DEFINE_I440FX_MACHINE(2, 4); #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) @@ -861,8 +799,10 @@ 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_floppy = !module_object_class_by_name(TYPE_ISA_FDC); + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); } DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, @@ -870,20 +810,20 @@ DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, #endif #ifdef CONFIG_XEN -static void xenfv_4_2_machine_options(MachineClass *m) +static void xenfv_machine_4_2_options(MachineClass *m) { - pc_i440fx_4_2_machine_options(m); + pc_i440fx_machine_4_2_options(m); m->desc = "Xen Fully-virtualized PC"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; } DEFINE_PC_MACHINE(xenfv_4_2, "xenfv-4.2", pc_xen_hvm_init, - xenfv_4_2_machine_options); + xenfv_machine_4_2_options); -static void xenfv_3_1_machine_options(MachineClass *m) +static void xenfv_machine_3_1_options(MachineClass *m) { - pc_i440fx_3_1_machine_options(m); + pc_i440fx_machine_3_1_options(m); m->desc = "Xen Fully-virtualized PC"; m->alias = "xenfv"; m->max_cpus = HVM_MAX_VCPUS; @@ -891,5 +831,5 @@ static void xenfv_3_1_machine_options(MachineClass *m) } DEFINE_PC_MACHINE(xenfv, "xenfv-3.1", pc_xen_hvm_init, - xenfv_3_1_machine_options); + xenfv_machine_3_1_options); #endif diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index f522874add..8319b6d45e 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,22 +129,15 @@ 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); @@ -140,6 +145,8 @@ static void pc_q35_init(MachineState *machine) 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 * also known as MMCFG). @@ -182,62 +189,58 @@ 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(); - } - - 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); + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); } /* create pci host bus */ - q35_host = Q35_HOST_DEVICE(qdev_new(TYPE_Q35_HOST_DEVICE)); + phb = OBJECT(qdev_new(TYPE_Q35_HOST_DEVICE)); - if (pcmc->pci_enabled) { - pci_hole64_size = object_property_get_uint(OBJECT(q35_host), - PCI_HOST_PROP_PCI_HOLE64_SIZE, - &error_abort); - } + pci_hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); /* allocate ram and load rom/bios */ - pc_memory_init(pcms, get_system_memory(), rom_memory, &ram_memory, - pci_hole64_size); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + pc_memory_init(pcms, system_memory, pci_memory, pci_hole64_size); - 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, + 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); + object_property_set_bool(phb, PCI_HOST_PROP_SMM_RANGES, + x86_machine_is_smm_enabled(x86ms), 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, @@ -256,80 +259,65 @@ 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]); } - assert(pcms->vmport != ON_OFF_AUTO__MAX); - if (pcms->vmport == ON_OFF_AUTO_AUTO) { - pcms->vmport = ON_OFF_AUTO_ON; - } - /* 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, @@ -338,173 +326,217 @@ static void pc_q35_init(MachineState *machine) } } -#define DEFINE_Q35_MACHINE(suffix, name, compatfn, optionfn) \ - static void pc_init_##suffix(MachineState *machine) \ - { \ - void (*compat)(MachineState *m) = (compatfn); \ - if (compat) { \ - compat(machine); \ - } \ - pc_q35_init(machine); \ - } \ - DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn) +#define DEFINE_Q35_MACHINE(major, minor) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, major, minor); +#define DEFINE_Q35_MACHINE_BUGFIX(major, minor, micro) \ + DEFINE_PC_VER_MACHINE(pc_q35, "pc-q35", pc_q35_init, major, minor, micro); 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_machine_9_2_options(MachineClass *m) +{ + pc_q35_machine_options(m); + m->alias = "q35"; +} + +DEFINE_Q35_MACHINE(9, 2); + +static void pc_q35_machine_9_1_options(MachineClass *m) +{ + pc_q35_machine_9_2_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_9_1, hw_compat_9_1_len); + compat_props_add(m->compat_props, pc_compat_9_1, pc_compat_9_1_len); +} + +DEFINE_Q35_MACHINE(9, 1); + +static void pc_q35_machine_9_0_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_machine_9_1_options(m); + m->smbios_memory_device_size = 16 * GiB; + compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); + compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); + pcmc->isa_bios_alias = false; +} + +DEFINE_Q35_MACHINE(9, 0); + +static void pc_q35_machine_8_2_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_machine_9_0_options(m); + 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(8, 2); + +static void pc_q35_machine_8_1_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_machine_8_2_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(8, 1); + +static void pc_q35_machine_8_0_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_q35_machine_8_1_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; } -static void pc_q35_7_2_machine_options(MachineClass *m) +DEFINE_Q35_MACHINE(8, 0); + +static void pc_q35_machine_7_2_options(MachineClass *m) { - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_machine_options(m); - m->alias = "q35"; - pcmc->default_cpu_version = 1; + pc_q35_machine_8_0_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); +DEFINE_Q35_MACHINE(7, 2); -static void pc_q35_7_1_machine_options(MachineClass *m) +static void pc_q35_machine_7_1_options(MachineClass *m) { - pc_q35_7_2_machine_options(m); - m->alias = NULL; + pc_q35_machine_7_2_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, - pc_q35_7_1_machine_options); +DEFINE_Q35_MACHINE(7, 1); -static void pc_q35_7_0_machine_options(MachineClass *m) +static void pc_q35_machine_7_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_7_1_machine_options(m); - m->alias = NULL; + pc_q35_machine_7_1_options(m); 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); } -DEFINE_Q35_MACHINE(v7_0, "pc-q35-7.0", NULL, - pc_q35_7_0_machine_options); +DEFINE_Q35_MACHINE(7, 0); -static void pc_q35_6_2_machine_options(MachineClass *m) +static void pc_q35_machine_6_2_options(MachineClass *m) { - pc_q35_7_0_machine_options(m); - m->alias = NULL; + pc_q35_machine_7_0_options(m); 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); } -DEFINE_Q35_MACHINE(v6_2, "pc-q35-6.2", NULL, - pc_q35_6_2_machine_options); +DEFINE_Q35_MACHINE(6, 2); -static void pc_q35_6_1_machine_options(MachineClass *m) +static void pc_q35_machine_6_1_options(MachineClass *m) { - pc_q35_6_2_machine_options(m); - m->alias = NULL; + pc_q35_machine_6_2_options(m); 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; } -DEFINE_Q35_MACHINE(v6_1, "pc-q35-6.1", NULL, - pc_q35_6_1_machine_options); +DEFINE_Q35_MACHINE(6, 1); -static void pc_q35_6_0_machine_options(MachineClass *m) +static void pc_q35_machine_6_0_options(MachineClass *m) { - pc_q35_6_1_machine_options(m); - m->alias = NULL; + pc_q35_machine_6_1_options(m); 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); } -DEFINE_Q35_MACHINE(v6_0, "pc-q35-6.0", NULL, - pc_q35_6_0_machine_options); +DEFINE_Q35_MACHINE(6, 0); -static void pc_q35_5_2_machine_options(MachineClass *m) +static void pc_q35_machine_5_2_options(MachineClass *m) { - pc_q35_6_0_machine_options(m); - m->alias = NULL; + pc_q35_machine_6_0_options(m); 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); } -DEFINE_Q35_MACHINE(v5_2, "pc-q35-5.2", NULL, - pc_q35_5_2_machine_options); +DEFINE_Q35_MACHINE(5, 2); -static void pc_q35_5_1_machine_options(MachineClass *m) +static void pc_q35_machine_5_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_5_2_machine_options(m); - m->alias = NULL; + pc_q35_machine_5_2_options(m); 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; pcmc->pci_root_uid = 1; } -DEFINE_Q35_MACHINE(v5_1, "pc-q35-5.1", NULL, - pc_q35_5_1_machine_options); +DEFINE_Q35_MACHINE(5, 1); -static void pc_q35_5_0_machine_options(MachineClass *m) +static void pc_q35_machine_5_0_options(MachineClass *m) { - pc_q35_5_1_machine_options(m); - m->alias = NULL; + pc_q35_machine_5_1_options(m); 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); m->auto_enable_numa_with_memdev = false; } -DEFINE_Q35_MACHINE(v5_0, "pc-q35-5.0", NULL, - pc_q35_5_0_machine_options); +DEFINE_Q35_MACHINE(5, 0); -static void pc_q35_4_2_machine_options(MachineClass *m) +static void pc_q35_machine_4_2_options(MachineClass *m) { - pc_q35_5_0_machine_options(m); - m->alias = NULL; + pc_q35_machine_5_0_options(m); compat_props_add(m->compat_props, hw_compat_4_2, hw_compat_4_2_len); compat_props_add(m->compat_props, pc_compat_4_2, pc_compat_4_2_len); } -DEFINE_Q35_MACHINE(v4_2, "pc-q35-4.2", NULL, - pc_q35_4_2_machine_options); +DEFINE_Q35_MACHINE(4, 2); -static void pc_q35_4_1_machine_options(MachineClass *m) +static void pc_q35_machine_4_1_options(MachineClass *m) { - pc_q35_4_2_machine_options(m); - m->alias = NULL; + pc_q35_machine_4_2_options(m); 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); } -DEFINE_Q35_MACHINE(v4_1, "pc-q35-4.1", NULL, - pc_q35_4_1_machine_options); +DEFINE_Q35_MACHINE(4, 1); -static void pc_q35_4_0_1_machine_options(MachineClass *m) +static void pc_q35_machine_4_0_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_4_1_machine_options(m); - m->alias = NULL; + pc_q35_machine_4_1_options(m); pcmc->default_cpu_version = CPU_VERSION_LEGACY; /* * This is the default machine for the 4.0-stable branch. It is basically @@ -515,150 +547,133 @@ static void pc_q35_4_0_1_machine_options(MachineClass *m) compat_props_add(m->compat_props, pc_compat_4_0, pc_compat_4_0_len); } -DEFINE_Q35_MACHINE(v4_0_1, "pc-q35-4.0.1", NULL, - pc_q35_4_0_1_machine_options); +DEFINE_Q35_MACHINE_BUGFIX(4, 0, 1); -static void pc_q35_4_0_machine_options(MachineClass *m) +static void pc_q35_machine_4_0_options(MachineClass *m) { - pc_q35_4_0_1_machine_options(m); + pc_q35_machine_4_0_1_options(m); m->default_kernel_irqchip_split = true; - m->alias = NULL; /* Compat props are applied by the 4.0.1 machine */ } -DEFINE_Q35_MACHINE(v4_0, "pc-q35-4.0", NULL, - pc_q35_4_0_machine_options); +DEFINE_Q35_MACHINE(4, 0); -static void pc_q35_3_1_machine_options(MachineClass *m) +static void pc_q35_machine_3_1_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_4_0_machine_options(m); + pc_q35_machine_4_0_options(m); m->default_kernel_irqchip_split = false; 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); } -DEFINE_Q35_MACHINE(v3_1, "pc-q35-3.1", NULL, - pc_q35_3_1_machine_options); +DEFINE_Q35_MACHINE(3, 1); -static void pc_q35_3_0_machine_options(MachineClass *m) +static void pc_q35_machine_3_0_options(MachineClass *m) { - pc_q35_3_1_machine_options(m); + pc_q35_machine_3_1_options(m); compat_props_add(m->compat_props, hw_compat_3_0, hw_compat_3_0_len); compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); } -DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, - pc_q35_3_0_machine_options); +DEFINE_Q35_MACHINE(3, 0); -static void pc_q35_2_12_machine_options(MachineClass *m) +static void pc_q35_machine_2_12_options(MachineClass *m) { - pc_q35_3_0_machine_options(m); + pc_q35_machine_3_0_options(m); compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } -DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, - pc_q35_2_12_machine_options); +DEFINE_Q35_MACHINE(2, 12); -static void pc_q35_2_11_machine_options(MachineClass *m) +static void pc_q35_machine_2_11_options(MachineClass *m) { - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_q35_2_12_machine_options(m); - pcmc->default_nic_model = "e1000"; + pc_q35_machine_2_12_options(m); + 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); } -DEFINE_Q35_MACHINE(v2_11, "pc-q35-2.11", NULL, - pc_q35_2_11_machine_options); +DEFINE_Q35_MACHINE(2, 11); -static void pc_q35_2_10_machine_options(MachineClass *m) +static void pc_q35_machine_2_10_options(MachineClass *m) { - pc_q35_2_11_machine_options(m); + pc_q35_machine_2_11_options(m); compat_props_add(m->compat_props, hw_compat_2_10, hw_compat_2_10_len); compat_props_add(m->compat_props, pc_compat_2_10, pc_compat_2_10_len); m->auto_enable_numa_with_memhp = false; } -DEFINE_Q35_MACHINE(v2_10, "pc-q35-2.10", NULL, - pc_q35_2_10_machine_options); +DEFINE_Q35_MACHINE(2, 10); -static void pc_q35_2_9_machine_options(MachineClass *m) +static void pc_q35_machine_2_9_options(MachineClass *m) { - pc_q35_2_10_machine_options(m); + pc_q35_machine_2_10_options(m); compat_props_add(m->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); } -DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL, - pc_q35_2_9_machine_options); +DEFINE_Q35_MACHINE(2, 9); -static void pc_q35_2_8_machine_options(MachineClass *m) +static void pc_q35_machine_2_8_options(MachineClass *m) { - pc_q35_2_9_machine_options(m); + pc_q35_machine_2_9_options(m); compat_props_add(m->compat_props, hw_compat_2_8, hw_compat_2_8_len); compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } -DEFINE_Q35_MACHINE(v2_8, "pc-q35-2.8", NULL, - pc_q35_2_8_machine_options); +DEFINE_Q35_MACHINE(2, 8); -static void pc_q35_2_7_machine_options(MachineClass *m) +static void pc_q35_machine_2_7_options(MachineClass *m) { - pc_q35_2_8_machine_options(m); + pc_q35_machine_2_8_options(m); m->max_cpus = 255; compat_props_add(m->compat_props, hw_compat_2_7, hw_compat_2_7_len); compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } -DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, - pc_q35_2_7_machine_options); +DEFINE_Q35_MACHINE(2, 7); -static void pc_q35_2_6_machine_options(MachineClass *m) +static void pc_q35_machine_2_6_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_2_7_machine_options(m); + pc_q35_machine_2_7_options(m); pcmc->legacy_cpu_hotplug = true; x86mc->fwcfg_dma_enabled = false; compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } -DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, - pc_q35_2_6_machine_options); +DEFINE_Q35_MACHINE(2, 6); -static void pc_q35_2_5_machine_options(MachineClass *m) +static void pc_q35_machine_2_5_options(MachineClass *m) { X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - pc_q35_2_6_machine_options(m); + pc_q35_machine_2_6_options(m); x86mc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } -DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL, - pc_q35_2_5_machine_options); +DEFINE_Q35_MACHINE(2, 5); -static void pc_q35_2_4_machine_options(MachineClass *m) +static void pc_q35_machine_2_4_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - pc_q35_2_5_machine_options(m); + pc_q35_machine_2_5_options(m); m->hw_version = "2.4.0"; pcmc->broken_reserved_end = true; compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } -DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL, - pc_q35_2_4_machine_options); +DEFINE_Q35_MACHINE(2, 4); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index c8d9e71b88..ef80281d28 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -40,12 +40,10 @@ #define FLASH_SECTOR_SIZE 4096 -static void pc_isa_bios_init(MemoryRegion *rom_memory, - MemoryRegion *flash_mem, - int ram_size) +static void pc_isa_bios_init(PCMachineState *pcms, MemoryRegion *isa_bios, + MemoryRegion *rom_memory, MemoryRegion *flash_mem) { int isa_bios_size; - MemoryRegion *isa_bios; uint64_t flash_size; void *flash_ptr, *isa_bios_ptr; @@ -53,9 +51,13 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, /* map the last 128KB of the BIOS in ISA space */ isa_bios_size = MIN(flash_size, 128 * KiB); - isa_bios = g_malloc(sizeof(*isa_bios)); - memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, - &error_fatal); + if (machine_require_guest_memfd(MACHINE(pcms))) { + memory_region_init_ram_guest_memfd(isa_bios, NULL, "isa-bios", + isa_bios_size, &error_fatal); + } else { + memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, + &error_fatal); + } memory_region_add_subregion_overlap(rom_memory, 0x100000 - isa_bios_size, isa_bios, @@ -68,7 +70,9 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, ((uint8_t*)flash_ptr) + (flash_size - isa_bios_size), isa_bios_size); - memory_region_set_readonly(isa_bios, true); + if (!machine_require_guest_memfd(current_machine)) { + memory_region_set_readonly(isa_bios, true); + } } static PFlashCFI01 *pc_pflash_create(PCMachineState *pcms, @@ -107,17 +111,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; } } @@ -139,6 +141,8 @@ void pc_system_flash_cleanup_unused(PCMachineState *pcms) static void pc_system_flash_map(PCMachineState *pcms, MemoryRegion *rom_memory) { + X86MachineState *x86ms = X86_MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); hwaddr total_size = 0; int i; BlockBackend *blk; @@ -151,6 +155,8 @@ static void pc_system_flash_map(PCMachineState *pcms, assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + hwaddr gpa; + system_flash = pcms->flash[i]; blk = pflash_cfi01_get_blk(system_flash); if (!blk) { @@ -180,21 +186,26 @@ static void pc_system_flash_map(PCMachineState *pcms, } total_size += size; + gpa = 0x100000000ULL - total_size; /* where the flash is mapped */ qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks", size / FLASH_SECTOR_SIZE); sysbus_realize_and_unref(SYS_BUS_DEVICE(system_flash), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, - 0x100000000ULL - total_size); + sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, gpa); if (i == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); - pc_isa_bios_init(rom_memory, flash_mem, size); + if (pcmc->isa_bios_alias) { + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, flash_mem, + true); + } else { + pc_isa_bios_init(pcms, &x86ms->isa_bios, rom_memory, flash_mem); + } /* Encrypt the pflash boot ROM */ if (sev_enabled()) { flash_ptr = memory_region_get_ram_ptr(flash_mem); flash_size = memory_region_size(flash_mem); - x86_firmware_configure(flash_ptr, flash_size); + x86_firmware_configure(gpa, flash_ptr, flash_size); } } } @@ -208,7 +219,7 @@ void pc_system_firmware_init(PCMachineState *pcms, BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; if (!pcmc->pci_enabled) { - x86_bios_rom_init(MACHINE(pcms), "bios.bin", rom_memory, true); + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); return; } @@ -229,7 +240,7 @@ void pc_system_firmware_init(PCMachineState *pcms, if (!pflash_blk[0]) { /* Machine property pflash0 not set, use ROM mode */ - x86_bios_rom_init(MACHINE(pcms), "bios.bin", rom_memory, false); + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false); } else { if (kvm_enabled() && !kvm_readonly_mem_enabled()) { /* @@ -247,7 +258,7 @@ void pc_system_firmware_init(PCMachineState *pcms, pc_system_flash_cleanup_unused(pcms); } -void x86_firmware_configure(void *ptr, int size) +void x86_firmware_configure(hwaddr gpa, void *ptr, int size) { int ret; @@ -258,12 +269,16 @@ void x86_firmware_configure(void *ptr, int size) pc_system_parse_ovmf_flash(ptr, size); if (sev_enabled()) { + + /* Copy the SEV metadata table (if it exists) */ + pc_system_parse_sev_metadata(ptr, size); + ret = sev_es_save_reset_vector(ptr, size); if (ret) { error_report("failed to locate and/or save reset vector"); exit(1); } - sev_encrypt_flash(ptr, size, &error_fatal); + sev_encrypt_flash(gpa, ptr, size, &error_fatal); } } diff --git a/hw/i386/port92.c b/hw/i386/port92.c index e1379a4f98..1b03b34f1d 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() } @@ -101,7 +102,7 @@ static void port92_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = port92_realizefn; - dc->reset = port92_reset; + device_class_set_legacy_reset(dc, port92_reset); dc->vmsd = &vmstate_port92_isa; /* * Reason: unlike ordinary ISA devices, this one needs additional diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 26833eb233..38ff75e9f3 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -32,7 +32,12 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState)); } +bool check_sgx_support(void) +{ + return false; +} + 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 09d9c7c73d..4900dd414a 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,11 +157,12 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) { SGXInfo *info = NULL; uint32_t eax, ebx, ecx, edx; - uint64_t size = 0; + Error *local_err = NULL; - int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); + int fd = qemu_open("/dev/sgx_vepc", O_RDWR, &local_err); if (fd < 0) { - error_setg(errp, "SGX is not enabled in KVM"); + error_append_hint(&local_err, "SGX is not enabled in KVM"); + error_propagate(errp, local_err); return NULL; } @@ -175,8 +176,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 +223,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 +239,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 +253,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,15 +260,28 @@ 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 check_sgx_support(void) +{ + if (!object_dynamic_cast(qdev_get_machine(), TYPE_PC_MACHINE)) { + return false; + } + return true; } bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size) { - PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + PCMachineState *pcms = + (PCMachineState *)object_dynamic_cast(qdev_get_machine(), + TYPE_PC_MACHINE); SGXEPCDevice *epc; - if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) { + if (!pcms || pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) { return true; } @@ -288,7 +298,6 @@ 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) { @@ -302,16 +311,15 @@ void pc_machine_init_sgx_epc(PCMachineState *pcms) &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 04fd71bfc4..53c02d7ac8 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -121,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/vapic.c b/hw/i386/vapic.c new file mode 100644 index 0000000000..ef7f8b967f --- /dev/null +++ b/hw/i386/vapic.c @@ -0,0 +1,870 @@ +/* + * TPR optimization for 32-bit Windows guests (XP and Server 2003) + * + * Copyright (C) 2007-2008 Qumranet Technologies + * Copyright (C) 2012 Jan Kiszka, Siemens AG + * + * 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/module.h" +#include "sysemu/sysemu.h" +#include "sysemu/cpus.h" +#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" +#include "migration/vmstate.h" +#include "qom/object.h" + +#define VAPIC_IO_PORT 0x7e + +#define VAPIC_CPU_SHIFT 7 + +#define ROM_BLOCK_SIZE 512 +#define ROM_BLOCK_MASK (~(ROM_BLOCK_SIZE - 1)) + +typedef enum VAPICMode { + VAPIC_INACTIVE = 0, + VAPIC_ACTIVE = 1, + VAPIC_STANDBY = 2, +} VAPICMode; + +typedef struct VAPICHandlers { + uint32_t set_tpr; + uint32_t set_tpr_eax; + uint32_t get_tpr[8]; + uint32_t get_tpr_stack; +} QEMU_PACKED VAPICHandlers; + +typedef struct GuestROMState { + char signature[8]; + uint32_t vaddr; + uint32_t fixup_start; + uint32_t fixup_end; + uint32_t vapic_vaddr; + uint32_t vapic_size; + uint32_t vcpu_shift; + uint32_t real_tpr_addr; + VAPICHandlers up; + VAPICHandlers mp; +} QEMU_PACKED GuestROMState; + +struct VAPICROMState { + SysBusDevice busdev; + + MemoryRegion io; + MemoryRegion rom; + uint32_t state; + uint32_t rom_state_paddr; + uint32_t rom_state_vaddr; + uint32_t vapic_paddr; + uint32_t real_tpr_addr; + GuestROMState rom_state; + size_t rom_size; + bool rom_mapped_writable; + VMChangeStateEntry *vmsentry; +}; + +#define TYPE_VAPIC "kvmvapic" +OBJECT_DECLARE_SIMPLE_TYPE(VAPICROMState, VAPIC) + +#define TPR_INSTR_ABS_MODRM 0x1 +#define TPR_INSTR_MATCH_MODRM_REG 0x2 + +typedef struct TPRInstruction { + uint8_t opcode; + uint8_t modrm_reg; + unsigned int flags; + TPRAccess access; + size_t length; + off_t addr_offset; +} TPRInstruction; + +/* must be sorted by length, shortest first */ +static const TPRInstruction tpr_instr[] = { + { /* mov abs to eax */ + .opcode = 0xa1, + .access = TPR_ACCESS_READ, + .length = 5, + .addr_offset = 1, + }, + { /* mov eax to abs */ + .opcode = 0xa3, + .access = TPR_ACCESS_WRITE, + .length = 5, + .addr_offset = 1, + }, + { /* mov r32 to r/m32 */ + .opcode = 0x89, + .flags = TPR_INSTR_ABS_MODRM, + .access = TPR_ACCESS_WRITE, + .length = 6, + .addr_offset = 2, + }, + { /* mov r/m32 to r32 */ + .opcode = 0x8b, + .flags = TPR_INSTR_ABS_MODRM, + .access = TPR_ACCESS_READ, + .length = 6, + .addr_offset = 2, + }, + { /* push r/m32 */ + .opcode = 0xff, + .modrm_reg = 6, + .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, + .access = TPR_ACCESS_READ, + .length = 6, + .addr_offset = 2, + }, + { /* mov imm32, r/m32 (c7/0) */ + .opcode = 0xc7, + .modrm_reg = 0, + .flags = TPR_INSTR_ABS_MODRM | TPR_INSTR_MATCH_MODRM_REG, + .access = TPR_ACCESS_WRITE, + .length = 10, + .addr_offset = 2, + }, +}; + +static void read_guest_rom_state(VAPICROMState *s) +{ + cpu_physical_memory_read(s->rom_state_paddr, &s->rom_state, + sizeof(GuestROMState)); +} + +static void write_guest_rom_state(VAPICROMState *s) +{ + cpu_physical_memory_write(s->rom_state_paddr, &s->rom_state, + sizeof(GuestROMState)); +} + +static void update_guest_rom_state(VAPICROMState *s) +{ + read_guest_rom_state(s); + + s->rom_state.real_tpr_addr = cpu_to_le32(s->real_tpr_addr); + s->rom_state.vcpu_shift = cpu_to_le32(VAPIC_CPU_SHIFT); + + write_guest_rom_state(s); +} + +static int find_real_tpr_addr(VAPICROMState *s, CPUX86State *env) +{ + CPUState *cs = env_cpu(env); + hwaddr paddr; + target_ulong addr; + + if (s->state == VAPIC_ACTIVE) { + return 0; + } + /* + * If there is no prior TPR access instruction we could analyze (which is + * the case after resume from hibernation), we need to scan the possible + * virtual address space for the APIC mapping. + */ + for (addr = 0xfffff000; addr >= 0x80000000; addr -= TARGET_PAGE_SIZE) { + paddr = cpu_get_phys_page_debug(cs, addr); + if (paddr != APIC_DEFAULT_ADDRESS) { + continue; + } + s->real_tpr_addr = addr + 0x80; + update_guest_rom_state(s); + return 0; + } + return -1; +} + +static uint8_t modrm_reg(uint8_t modrm) +{ + return (modrm >> 3) & 7; +} + +static bool is_abs_modrm(uint8_t modrm) +{ + return (modrm & 0xc7) == 0x05; +} + +static bool opcode_matches(uint8_t *opcode, const TPRInstruction *instr) +{ + return opcode[0] == instr->opcode && + (!(instr->flags & TPR_INSTR_ABS_MODRM) || is_abs_modrm(opcode[1])) && + (!(instr->flags & TPR_INSTR_MATCH_MODRM_REG) || + modrm_reg(opcode[1]) == instr->modrm_reg); +} + +static int evaluate_tpr_instruction(VAPICROMState *s, X86CPU *cpu, + target_ulong *pip, TPRAccess access) +{ + CPUState *cs = CPU(cpu); + const TPRInstruction *instr; + target_ulong ip = *pip; + uint8_t opcode[2]; + uint32_t real_tpr_addr; + int i; + + if ((ip & 0xf0000000ULL) != 0x80000000ULL && + (ip & 0xf0000000ULL) != 0xe0000000ULL) { + return -1; + } + + /* + * Early Windows 2003 SMP initialization contains a + * + * mov imm32, r/m32 + * + * instruction that is patched by TPR optimization. The problem is that + * RSP, used by the patched instruction, is zero, so the guest gets a + * double fault and dies. + */ + if (cpu->env.regs[R_ESP] == 0) { + return -1; + } + + if (kvm_enabled() && !kvm_irqchip_in_kernel()) { + /* + * KVM without kernel-based TPR access reporting will pass an IP that + * points after the accessing instruction. So we need to look backward + * to find the reason. + */ + for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { + instr = &tpr_instr[i]; + if (instr->access != access) { + continue; + } + if (cpu_memory_rw_debug(cs, ip - instr->length, opcode, + sizeof(opcode), 0) < 0) { + return -1; + } + if (opcode_matches(opcode, instr)) { + ip -= instr->length; + goto instruction_ok; + } + } + return -1; + } else { + if (cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0) < 0) { + return -1; + } + for (i = 0; i < ARRAY_SIZE(tpr_instr); i++) { + instr = &tpr_instr[i]; + if (opcode_matches(opcode, instr)) { + goto instruction_ok; + } + } + return -1; + } + +instruction_ok: + /* + * Grab the virtual TPR address from the instruction + * and update the cached values. + */ + if (cpu_memory_rw_debug(cs, ip + instr->addr_offset, + (void *)&real_tpr_addr, + sizeof(real_tpr_addr), 0) < 0) { + return -1; + } + real_tpr_addr = le32_to_cpu(real_tpr_addr); + if ((real_tpr_addr & 0xfff) != 0x80) { + return -1; + } + s->real_tpr_addr = real_tpr_addr; + update_guest_rom_state(s); + + *pip = ip; + return 0; +} + +static int update_rom_mapping(VAPICROMState *s, CPUX86State *env, target_ulong ip) +{ + CPUState *cs = env_cpu(env); + hwaddr paddr; + uint32_t rom_state_vaddr; + uint32_t pos, patch, offset; + + /* nothing to do if already activated */ + if (s->state == VAPIC_ACTIVE) { + return 0; + } + + /* bail out if ROM init code was not executed (missing ROM?) */ + if (s->state == VAPIC_INACTIVE) { + return -1; + } + + /* find out virtual address of the ROM */ + rom_state_vaddr = s->rom_state_paddr + (ip & 0xf0000000); + paddr = cpu_get_phys_page_debug(cs, rom_state_vaddr); + if (paddr == -1) { + return -1; + } + paddr += rom_state_vaddr & ~TARGET_PAGE_MASK; + if (paddr != s->rom_state_paddr) { + return -1; + } + read_guest_rom_state(s); + if (memcmp(s->rom_state.signature, "kvm aPiC", 8) != 0) { + return -1; + } + s->rom_state_vaddr = rom_state_vaddr; + + /* fixup addresses in ROM if needed */ + if (rom_state_vaddr == le32_to_cpu(s->rom_state.vaddr)) { + return 0; + } + for (pos = le32_to_cpu(s->rom_state.fixup_start); + pos < le32_to_cpu(s->rom_state.fixup_end); + pos += 4) { + cpu_physical_memory_read(paddr + pos - s->rom_state.vaddr, + &offset, sizeof(offset)); + offset = le32_to_cpu(offset); + cpu_physical_memory_read(paddr + offset, &patch, sizeof(patch)); + patch = le32_to_cpu(patch); + patch += rom_state_vaddr - le32_to_cpu(s->rom_state.vaddr); + patch = cpu_to_le32(patch); + cpu_physical_memory_write(paddr + offset, &patch, sizeof(patch)); + } + read_guest_rom_state(s); + s->vapic_paddr = paddr + le32_to_cpu(s->rom_state.vapic_vaddr) - + le32_to_cpu(s->rom_state.vaddr); + + return 0; +} + +/* + * Tries to read the unique processor number from the Kernel Processor Control + * Region (KPCR) of 32-bit Windows XP and Server 2003. Returns -1 if the KPCR + * cannot be accessed or is considered invalid. This also ensures that we are + * not patching the wrong guest. + */ +static int get_kpcr_number(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + struct kpcr { + uint8_t fill1[0x1c]; + uint32_t self; + uint8_t fill2[0x31]; + uint8_t number; + } QEMU_PACKED kpcr; + + if (cpu_memory_rw_debug(CPU(cpu), env->segs[R_FS].base, + (void *)&kpcr, sizeof(kpcr), 0) < 0 || + kpcr.self != env->segs[R_FS].base) { + return -1; + } + return kpcr.number; +} + +static int vapic_enable(VAPICROMState *s, X86CPU *cpu) +{ + int cpu_number = get_kpcr_number(cpu); + hwaddr vapic_paddr; + static const uint8_t enabled = 1; + + if (cpu_number < 0) { + return -1; + } + vapic_paddr = s->vapic_paddr + + (((hwaddr)cpu_number) << VAPIC_CPU_SHIFT); + cpu_physical_memory_write(vapic_paddr + offsetof(VAPICState, enabled), + &enabled, sizeof(enabled)); + apic_enable_vapic(cpu->apic_state, vapic_paddr); + + s->state = VAPIC_ACTIVE; + + return 0; +} + +static void patch_byte(X86CPU *cpu, target_ulong addr, uint8_t byte) +{ + cpu_memory_rw_debug(CPU(cpu), addr, &byte, 1, 1); +} + +static void patch_call(X86CPU *cpu, target_ulong ip, uint32_t target) +{ + uint32_t offset; + + offset = cpu_to_le32(target - ip - 5); + patch_byte(cpu, ip, 0xe8); /* call near */ + cpu_memory_rw_debug(CPU(cpu), ip + 1, (void *)&offset, sizeof(offset), 1); +} + +typedef struct PatchInfo { + VAPICHandlers *handler; + target_ulong ip; +} PatchInfo; + +static void do_patch_instruction(CPUState *cs, run_on_cpu_data data) +{ + X86CPU *x86_cpu = X86_CPU(cs); + PatchInfo *info = (PatchInfo *) data.host_ptr; + VAPICHandlers *handlers = info->handler; + target_ulong ip = info->ip; + uint8_t opcode[2]; + uint32_t imm32 = 0; + + cpu_memory_rw_debug(cs, ip, opcode, sizeof(opcode), 0); + + switch (opcode[0]) { + case 0x89: /* mov r32 to r/m32 */ + patch_byte(x86_cpu, ip, 0x50 + modrm_reg(opcode[1])); /* push reg */ + patch_call(x86_cpu, ip + 1, handlers->set_tpr); + break; + case 0x8b: /* mov r/m32 to r32 */ + patch_byte(x86_cpu, ip, 0x90); + patch_call(x86_cpu, ip + 1, handlers->get_tpr[modrm_reg(opcode[1])]); + break; + case 0xa1: /* mov abs to eax */ + patch_call(x86_cpu, ip, handlers->get_tpr[0]); + break; + case 0xa3: /* mov eax to abs */ + patch_call(x86_cpu, ip, handlers->set_tpr_eax); + break; + case 0xc7: /* mov imm32, r/m32 (c7/0) */ + patch_byte(x86_cpu, ip, 0x68); /* push imm32 */ + cpu_memory_rw_debug(cs, ip + 6, (void *)&imm32, sizeof(imm32), 0); + cpu_memory_rw_debug(cs, ip + 1, (void *)&imm32, sizeof(imm32), 1); + patch_call(x86_cpu, ip + 5, handlers->set_tpr); + break; + case 0xff: /* push r/m32 */ + patch_byte(x86_cpu, ip, 0x50); /* push eax */ + patch_call(x86_cpu, ip + 1, handlers->get_tpr_stack); + break; + default: + abort(); + } + + g_free(info); +} + +static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + CPUState *cs = CPU(cpu); + VAPICHandlers *handlers; + PatchInfo *info; + + if (ms->smp.cpus == 1) { + handlers = &s->rom_state.up; + } else { + handlers = &s->rom_state.mp; + } + + info = g_new(PatchInfo, 1); + info->handler = handlers; + info->ip = ip; + + async_safe_run_on_cpu(cs, do_patch_instruction, RUN_ON_CPU_HOST_PTR(info)); +} + +void vapic_report_tpr_access(DeviceState *dev, CPUState *cs, target_ulong ip, + TPRAccess access) +{ + VAPICROMState *s = VAPIC(dev); + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + cpu_synchronize_state(cs); + + if (evaluate_tpr_instruction(s, cpu, &ip, access) < 0) { + if (s->state == VAPIC_ACTIVE) { + vapic_enable(s, cpu); + } + return; + } + if (update_rom_mapping(s, env, ip) < 0) { + return; + } + if (vapic_enable(s, cpu) < 0) { + return; + } + patch_instruction(s, cpu, ip); +} + +typedef struct VAPICEnableTPRReporting { + DeviceState *apic; + bool enable; +} VAPICEnableTPRReporting; + +static void vapic_do_enable_tpr_reporting(CPUState *cpu, run_on_cpu_data data) +{ + VAPICEnableTPRReporting *info = data.host_ptr; + apic_enable_tpr_access_reporting(info->apic, info->enable); +} + +static void vapic_enable_tpr_reporting(bool enable) +{ + VAPICEnableTPRReporting info = { + .enable = enable, + }; + CPUState *cs; + X86CPU *cpu; + + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); + info.apic = cpu->apic_state; + run_on_cpu(cs, vapic_do_enable_tpr_reporting, RUN_ON_CPU_HOST_PTR(&info)); + } +} + +static void vapic_reset(DeviceState *dev) +{ + VAPICROMState *s = VAPIC(dev); + + s->state = VAPIC_INACTIVE; + s->rom_state_paddr = 0; + vapic_enable_tpr_reporting(false); +} + +/* + * Set the IRQ polling hypercalls to the supported variant: + * - vmcall if using KVM in-kernel irqchip + * - 32-bit VAPIC port write otherwise + */ +static int patch_hypercalls(VAPICROMState *s) +{ + hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; + static const uint8_t vmcall_pattern[] = { /* vmcall */ + 0xb8, 0x1, 0, 0, 0, 0xf, 0x1, 0xc1 + }; + static const uint8_t outl_pattern[] = { /* nop; outl %eax,0x7e */ + 0xb8, 0x1, 0, 0, 0, 0x90, 0xe7, 0x7e + }; + uint8_t alternates[2]; + const uint8_t *pattern; + const uint8_t *patch; + off_t pos; + uint8_t *rom; + + rom = g_malloc(s->rom_size); + cpu_physical_memory_read(rom_paddr, rom, s->rom_size); + + for (pos = 0; pos < s->rom_size - sizeof(vmcall_pattern); pos++) { + if (kvm_irqchip_in_kernel()) { + pattern = outl_pattern; + alternates[0] = outl_pattern[7]; + alternates[1] = outl_pattern[7]; + patch = &vmcall_pattern[5]; + } else { + pattern = vmcall_pattern; + alternates[0] = vmcall_pattern[7]; + alternates[1] = 0xd9; /* AMD's VMMCALL */ + patch = &outl_pattern[5]; + } + if (memcmp(rom + pos, pattern, 7) == 0 && + (rom[pos + 7] == alternates[0] || rom[pos + 7] == alternates[1])) { + cpu_physical_memory_write(rom_paddr + pos + 5, patch, 3); + /* + * Don't flush the tb here. Under ordinary conditions, the patched + * calls are miles away from the current IP. Under malicious + * conditions, the guest could trick us to crash. + */ + } + } + + g_free(rom); + return 0; +} + +/* + * For TCG mode or the time KVM honors read-only memory regions, we need to + * enable write access to the option ROM so that variables can be updated by + * the guest. + */ +static int vapic_map_rom_writable(VAPICROMState *s) +{ + hwaddr rom_paddr = s->rom_state_paddr & ROM_BLOCK_MASK; + MemoryRegionSection section; + MemoryRegion *mr = get_system_memory(); + size_t rom_size; + uint8_t *ram; + + if (s->rom_mapped_writable) { + 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(mr, 0, 1); + + /* read ROM size from RAM region */ + if (rom_paddr + 2 >= memory_region_size(section.mr)) { + return -1; + } + ram = memory_region_get_ram_ptr(section.mr); + rom_size = ram[rom_paddr + 2] * ROM_BLOCK_SIZE; + if (rom_size == 0) { + return -1; + } + s->rom_size = rom_size; + + /* We need to round to avoid creating subpages + * from which we cannot run code. */ + rom_size += rom_paddr & ~TARGET_PAGE_MASK; + rom_paddr &= TARGET_PAGE_MASK; + rom_size = TARGET_PAGE_ALIGN(rom_size); + + memory_region_init_alias(&s->rom, OBJECT(s), "kvmvapic-rom", section.mr, + rom_paddr, rom_size); + memory_region_add_subregion_overlap(mr, rom_paddr, &s->rom, 1000); + s->rom_mapped_writable = true; + memory_region_unref(section.mr); + + return 0; +} + +static int vapic_prepare(VAPICROMState *s) +{ + if (vapic_map_rom_writable(s) < 0) { + return -1; + } + + if (patch_hypercalls(s) < 0) { + return -1; + } + + vapic_enable_tpr_reporting(true); + + return 0; +} + +static void vapic_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + VAPICROMState *s = opaque; + X86CPU *cpu; + CPUX86State *env; + hwaddr rom_paddr; + + if (!current_cpu) { + return; + } + + cpu_synchronize_state(current_cpu); + cpu = X86_CPU(current_cpu); + env = &cpu->env; + + /* + * The VAPIC supports two PIO-based hypercalls, both via port 0x7E. + * o 16-bit write access: + * Reports the option ROM initialization to the hypervisor. Written + * value is the offset of the state structure in the ROM. + * o 8-bit write access: + * Reactivates the VAPIC after a guest hibernation, i.e. after the + * option ROM content has been re-initialized by a guest power cycle. + * o 32-bit write access: + * Poll for pending IRQs, considering the current VAPIC state. + */ + switch (size) { + case 2: + if (s->state == VAPIC_INACTIVE) { + rom_paddr = (env->segs[R_CS].base + env->eip) & ROM_BLOCK_MASK; + s->rom_state_paddr = rom_paddr + data; + + s->state = VAPIC_STANDBY; + } + if (vapic_prepare(s) < 0) { + s->state = VAPIC_INACTIVE; + s->rom_state_paddr = 0; + break; + } + break; + case 1: + if (kvm_enabled()) { + /* + * Disable triggering instruction in ROM by writing a NOP. + * + * We cannot do this in TCG mode as the reported IP is not + * accurate. + */ + pause_all_vcpus(); + patch_byte(cpu, env->eip - 2, 0x66); + patch_byte(cpu, env->eip - 1, 0x90); + resume_all_vcpus(); + } + + if (s->state == VAPIC_ACTIVE) { + break; + } + if (update_rom_mapping(s, env, env->eip) < 0) { + break; + } + if (find_real_tpr_addr(s, env) < 0) { + break; + } + vapic_enable(s, cpu); + break; + default: + case 4: + if (!kvm_irqchip_in_kernel()) { + apic_poll_irq(cpu->apic_state); + } + break; + } +} + +static uint64_t vapic_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0xffffffff; +} + +static const MemoryRegionOps vapic_ops = { + .write = vapic_write, + .read = vapic_read, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void vapic_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + VAPICROMState *s = VAPIC(dev); + + memory_region_init_io(&s->io, OBJECT(s), &vapic_ops, s, "kvmvapic", 2); + 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"; + option_rom[nb_option_roms].bootindex = -1; + nb_option_roms++; +} + +static void do_vapic_enable(CPUState *cs, run_on_cpu_data data) +{ + VAPICROMState *s = data.host_ptr; + X86CPU *cpu = X86_CPU(cs); + + static const uint8_t enabled = 1; + cpu_physical_memory_write(s->vapic_paddr + offsetof(VAPICState, enabled), + &enabled, sizeof(enabled)); + apic_enable_vapic(cpu->apic_state, s->vapic_paddr); + s->state = VAPIC_ACTIVE; +} + +static void vapic_vm_state_change(void *opaque, bool running, RunState state) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + VAPICROMState *s = opaque; + uint8_t *zero; + + if (!running) { + return; + } + + if (s->state == VAPIC_ACTIVE) { + if (ms->smp.cpus == 1) { + run_on_cpu(first_cpu, do_vapic_enable, RUN_ON_CPU_HOST_PTR(s)); + } else { + zero = g_malloc0(s->rom_state.vapic_size); + cpu_physical_memory_write(s->vapic_paddr, zero, + s->rom_state.vapic_size); + g_free(zero); + } + } + + qemu_del_vm_change_state_handler(s->vmsentry); + s->vmsentry = NULL; +} + +static int vapic_post_load(void *opaque, int version_id) +{ + VAPICROMState *s = opaque; + + /* + * The old implementation of qemu-kvm did not provide the state + * VAPIC_STANDBY. Reconstruct it. + */ + if (s->state == VAPIC_INACTIVE && s->rom_state_paddr != 0) { + s->state = VAPIC_STANDBY; + } + + if (s->state != VAPIC_INACTIVE) { + if (vapic_prepare(s) < 0) { + return -1; + } + } + + if (!s->vmsentry) { + s->vmsentry = + qemu_add_vm_change_state_handler(vapic_vm_state_change, s); + } + return 0; +} + +static const VMStateDescription vmstate_handlers = { + .name = "kvmvapic-handlers", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(set_tpr, VAPICHandlers), + VMSTATE_UINT32(set_tpr_eax, VAPICHandlers), + VMSTATE_UINT32_ARRAY(get_tpr, VAPICHandlers, 8), + VMSTATE_UINT32(get_tpr_stack, VAPICHandlers), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_guest_rom = { + .name = "kvmvapic-guest-rom", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UNUSED(8), /* signature */ + VMSTATE_UINT32(vaddr, GuestROMState), + VMSTATE_UINT32(fixup_start, GuestROMState), + VMSTATE_UINT32(fixup_end, GuestROMState), + VMSTATE_UINT32(vapic_vaddr, GuestROMState), + VMSTATE_UINT32(vapic_size, GuestROMState), + VMSTATE_UINT32(vcpu_shift, GuestROMState), + VMSTATE_UINT32(real_tpr_addr, GuestROMState), + VMSTATE_STRUCT(up, GuestROMState, 0, vmstate_handlers, VAPICHandlers), + VMSTATE_STRUCT(mp, GuestROMState, 0, vmstate_handlers, VAPICHandlers), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vapic = { + .name = "kvm-tpr-opt", /* compatible with qemu-kvm VAPIC */ + .version_id = 1, + .minimum_version_id = 1, + .post_load = vapic_post_load, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT(rom_state, VAPICROMState, 0, vmstate_guest_rom, + GuestROMState), + VMSTATE_UINT32(state, VAPICROMState), + VMSTATE_UINT32(real_tpr_addr, VAPICROMState), + VMSTATE_UINT32(rom_state_vaddr, VAPICROMState), + VMSTATE_UINT32(vapic_paddr, VAPICROMState), + VMSTATE_UINT32(rom_state_paddr, VAPICROMState), + VMSTATE_END_OF_LIST() + } +}; + +static void vapic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, vapic_reset); + dc->vmsd = &vmstate_vapic; + dc->realize = vapic_realize; +} + +static const TypeInfo vapic_type = { + .name = TYPE_VAPIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VAPICROMState), + .class_init = vapic_class_init, +}; + +static void vapic_register(void) +{ + type_register_static(&vapic_type); +} + +type_init(vapic_register); diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index a56c185f15..76130cd46d 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"); @@ -310,7 +327,7 @@ static void vmmouse_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = vmmouse_realizefn; - dc->reset = vmmouse_reset; + device_class_set_legacy_reset(dc, vmmouse_reset); dc->vmsd = &vmstate_vmmouse; device_class_set_props(dc, vmmouse_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c new file mode 100644 index 0000000000..dc031af662 --- /dev/null +++ b/hw/i386/x86-common.c @@ -0,0 +1,1051 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019, 2024 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/error-report.h" +#include "qemu/cutils.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qapi/error.h" +#include "sysemu/numa.h" +#include "sysemu/sysemu.h" +#include "sysemu/xen.h" +#include "trace.h" + +#include "hw/i386/x86.h" +#include "target/i386/cpu.h" +#include "hw/rtc/mc146818rtc.h" +#include "target/i386/sev.h" + +#include "hw/acpi/cpu_hotplug.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "multiboot.h" +#include "elf.h" +#include "standard-headers/asm-x86/bootparam.h" +#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; + +static void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) +{ + Object *cpu = object_new(MACHINE(x86ms)->cpu_type); + + if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) { + goto out; + } + qdev_realize(DEVICE(cpu), NULL, errp); + +out: + object_unref(cpu); +} + +void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) +{ + int i; + const CPUArchIdList *possible_cpus; + MachineState *ms = MACHINE(x86ms); + MachineClass *mc = MACHINE_GET_CLASS(x86ms); + + x86_cpu_set_default_version(default_cpu_version); + + /* + * Calculates the limit to CPU APIC ID values + * + * Limit for the APIC ID value, so that all + * CPU APIC IDs are < x86ms->apic_id_limit. + * + * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). + */ + x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, + ms->smp.max_cpus - 1) + 1; + + /* + * Can we support APIC ID 255 or higher? With KVM, that requires + * both in-kernel lapic and X2APIC userspace API. + * + * 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() && 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 *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. + */ + mc146818rtc_set_cmos_data(rtc, 0x5f, 0); + } else { + mc146818rtc_set_cmos_data(rtc, 0x5f, cpus_count - 1); + } +} + +static int x86_apic_cmp(const void *a, const void *b) +{ + CPUArchId *apic_a = (CPUArchId *)a; + CPUArchId *apic_b = (CPUArchId *)b; + + return apic_a->arch_id - apic_b->arch_id; +} + +/* + * returns pointer to CPUArchId descriptor that matches CPU's apic_id + * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no + * entry corresponding to CPU's apic_id returns NULL. + */ +static CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) +{ + 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), + x86_apic_cmp); + if (found_cpu && idx) { + *idx = found_cpu - ms->possible_cpus->cpus; + } + return found_cpu; +} + +void x86_cpu_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *found_cpu; + Error *local_err = NULL; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + if (x86ms->acpi_dev) { + hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); + if (local_err) { + goto out; + } + } + + /* increment the number of CPUs */ + x86ms->boot_cpus++; + if (x86ms->rtc) { + x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + } + if (x86ms->fw_cfg) { + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); + } + + found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); + found_cpu->cpu = CPU(dev); +out: + error_propagate(errp, local_err); +} + +void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx = -1; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + if (!x86ms->acpi_dev) { + error_setg(errp, "CPU hot unplug not supported without ACPI"); + return; + } + + x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); + assert(idx != -1); + if (idx == 0) { + error_setg(errp, "Boot CPU is unpluggable"); + return; + } + + hotplug_handler_unplug_request(x86ms->acpi_dev, dev, + errp); +} + +void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + CPUArchId *found_cpu; + Error *local_err = NULL; + X86CPU *cpu = X86_CPU(dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + + hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); + if (local_err) { + goto out; + } + + found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); + found_cpu->cpu = NULL; + qdev_unrealize(dev); + + /* decrement the number of CPUs */ + x86ms->boot_cpus--; + /* Update the number of CPUs in CMOS */ + x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); + out: + error_propagate(errp, local_err); +} + +void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + int idx; + CPUState *cs; + CPUArchId *cpu_slot; + X86CPUTopoIDs topo_ids; + X86CPU *cpu = X86_CPU(dev); + CPUX86State *env = &cpu->env; + MachineState *ms = MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(hotplug_dev); + unsigned int smp_cores = ms->smp.cores; + unsigned int smp_threads = ms->smp.threads; + X86CPUTopoInfo topo_info; + + if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { + error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", + ms->cpu_type); + return; + } + + if (x86ms->acpi_dev) { + Error *local_err = NULL; + + hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + init_topo_info(&topo_info, x86ms); + + if (ms->smp.modules > 1) { + env->nr_modules = ms->smp.modules; + set_bit(CPU_TOPOLOGY_LEVEL_MODULE, env->avail_cpu_topo); + } + + if (ms->smp.dies > 1) { + env->nr_dies = ms->smp.dies; + set_bit(CPU_TOPOLOGY_LEVEL_DIE, env->avail_cpu_topo); + } + + /* + * If APIC ID is not set, + * set it based on socket/die/module/core/thread properties. + */ + if (cpu->apic_id == UNASSIGNED_APIC_ID) { + /* + * die-id was optional in QEMU 4.0 and older, so keep it optional + * if there's only one die per socket. + */ + if (cpu->die_id < 0 && ms->smp.dies == 1) { + cpu->die_id = 0; + } + + /* + * module-id was optional in QEMU 9.0 and older, so keep it optional + * if there's only one module per die. + */ + if (cpu->module_id < 0 && ms->smp.modules == 1) { + cpu->module_id = 0; + } + + if (cpu->socket_id < 0) { + error_setg(errp, "CPU socket-id is not set"); + return; + } else if (cpu->socket_id > ms->smp.sockets - 1) { + error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", + cpu->socket_id, ms->smp.sockets - 1); + return; + } + if (cpu->die_id < 0) { + error_setg(errp, "CPU die-id is not set"); + return; + } else if (cpu->die_id > ms->smp.dies - 1) { + error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", + cpu->die_id, ms->smp.dies - 1); + return; + } + if (cpu->module_id < 0) { + error_setg(errp, "CPU module-id is not set"); + return; + } else if (cpu->module_id > ms->smp.modules - 1) { + error_setg(errp, "Invalid CPU module-id: %u must be in range 0:%u", + cpu->module_id, ms->smp.modules - 1); + return; + } + if (cpu->core_id < 0) { + error_setg(errp, "CPU core-id is not set"); + return; + } else if (cpu->core_id > (smp_cores - 1)) { + error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", + cpu->core_id, smp_cores - 1); + return; + } + if (cpu->thread_id < 0) { + error_setg(errp, "CPU thread-id is not set"); + return; + } else if (cpu->thread_id > (smp_threads - 1)) { + error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", + cpu->thread_id, smp_threads - 1); + return; + } + + topo_ids.pkg_id = cpu->socket_id; + topo_ids.die_id = cpu->die_id; + topo_ids.module_id = cpu->module_id; + topo_ids.core_id = cpu->core_id; + topo_ids.smt_id = cpu->thread_id; + cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); + } + + cpu_slot = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); + if (!cpu_slot) { + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + + error_setg(errp, + "Invalid CPU [socket: %u, die: %u, module: %u, core: %u, thread: %u]" + " with APIC ID %" PRIu32 ", valid index range 0:%d", + topo_ids.pkg_id, topo_ids.die_id, topo_ids.module_id, + topo_ids.core_id, topo_ids.smt_id, cpu->apic_id, + ms->possible_cpus->len - 1); + return; + } + + if (cpu_slot->cpu) { + error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", + idx, cpu->apic_id); + return; + } + + /* if 'address' properties socket-id/core-id/thread-id are not set, set them + * so that machine_query_hotpluggable_cpus would show correct values + */ + /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() + * once -smp refactoring is complete and there will be CPU private + * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ + x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); + if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { + error_setg(errp, "property socket-id: %u doesn't match set apic-id:" + " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, + topo_ids.pkg_id); + return; + } + cpu->socket_id = topo_ids.pkg_id; + + if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { + error_setg(errp, "property die-id: %u doesn't match set apic-id:" + " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); + return; + } + cpu->die_id = topo_ids.die_id; + + if (cpu->module_id != -1 && cpu->module_id != topo_ids.module_id) { + error_setg(errp, "property module-id: %u doesn't match set apic-id:" + " 0x%x (module-id: %u)", cpu->module_id, cpu->apic_id, + topo_ids.module_id); + return; + } + cpu->module_id = topo_ids.module_id; + + if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { + error_setg(errp, "property core-id: %u doesn't match set apic-id:" + " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, + topo_ids.core_id); + return; + } + cpu->core_id = topo_ids.core_id; + + if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { + error_setg(errp, "property thread-id: %u doesn't match set apic-id:" + " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, + topo_ids.smt_id); + return; + } + cpu->thread_id = topo_ids.smt_id; + + /* + * 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; + } + + cs = CPU(cpu); + cs->cpu_index = idx; + + numa_cpu_pre_plug(cpu_slot, dev, errp); +} + +static long get_file_size(FILE *f) +{ + long where, size; + + /* XXX: on Unix systems, using fstat() probably makes more sense */ + + where = ftell(f); + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, where, SEEK_SET); + + return size; +} + +void gsi_handler(void *opaque, int n, int level) +{ + GSIState *s = opaque; + + trace_x86_gsi_interrupt(n, level); + switch (n) { + case 0 ... ISA_NUM_IRQS - 1: + if (s->i8259_irq[n]) { + /* Under KVM, Kernel will forward to both PIC and IOAPIC */ + qemu_set_irq(s->i8259_irq[n], 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 + ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: + qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); + break; + } +} + +void ioapic_init_gsi(GSIState *gsi_state, Object *parent) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + assert(parent); + if (kvm_ioapic_in_kernel()) { + dev = qdev_new(TYPE_KVM_IOAPIC); + } else { + dev = qdev_new(TYPE_IOAPIC); + } + 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); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); + } +} + +DeviceState *ioapic_init_secondary(GSIState *gsi_state) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + dev = qdev_new(TYPE_IOAPIC); + d = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(d, &error_fatal); + sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); + } + return dev; +} + +/* + * The entry point into the kernel for PVH boot is different from + * the native entry point. The PVH entry is defined by the x86/HVM + * direct boot ABI and is available in an ELFNOTE in the kernel binary. + * + * This function is passed to load_elf() when it is called from + * load_elfboot() which then additionally checks for an ELF Note of + * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to + * parse the PVH entry address from the ELF Note. + * + * Due to trickery in elf_opts.h, load_elf() is actually available as + * load_elf32() or load_elf64() and this routine needs to be able + * to deal with being called as 32 or 64 bit. + * + * The address of the PVH entry point is saved to the 'pvh_start_addr' + * global variable. (although the entry point is 32-bit, the kernel + * binary can be either 32-bit or 64-bit). + */ +static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) +{ + size_t *elf_note_data_addr; + + /* Check if ELF Note header passed in is valid */ + if (arg1 == NULL) { + return 0; + } + + if (is64) { + struct elf64_note *nhdr64 = (struct elf64_note *)arg1; + uint64_t nhdr_size64 = sizeof(struct elf64_note); + uint64_t phdr_align = *(uint64_t *)arg2; + uint64_t nhdr_namesz = nhdr64->n_namesz; + + elf_note_data_addr = + ((void *)nhdr64) + nhdr_size64 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + + pvh_start_addr = *elf_note_data_addr; + } else { + struct elf32_note *nhdr32 = (struct elf32_note *)arg1; + uint32_t nhdr_size32 = sizeof(struct elf32_note); + uint32_t phdr_align = *(uint32_t *)arg2; + uint32_t nhdr_namesz = nhdr32->n_namesz; + + elf_note_data_addr = + ((void *)nhdr32) + nhdr_size32 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + + pvh_start_addr = *(uint32_t *)elf_note_data_addr; + } + + return pvh_start_addr; +} + +static bool load_elfboot(const char *kernel_filename, + int kernel_file_size, + uint8_t *header, + size_t pvh_xen_start_addr, + FWCfgState *fw_cfg) +{ + uint32_t flags = 0; + uint32_t mh_load_addr = 0; + uint32_t elf_kernel_size = 0; + uint64_t elf_entry; + uint64_t elf_low, elf_high; + int kernel_size; + + if (ldl_le_p(header) != 0x464c457f) { + return false; /* no elfboot */ + } + + bool elf_is64 = header[EI_CLASS] == ELFCLASS64; + flags = elf_is64 ? + ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; + + if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ + error_report("elfboot unsupported flags = %x", flags); + exit(1); + } + + uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; + kernel_size = load_elf(kernel_filename, read_pvh_start_addr, + NULL, &elf_note_type, &elf_entry, + &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, + 0, 0); + + if (kernel_size < 0) { + error_report("Error while loading elf kernel"); + exit(1); + } + mh_load_addr = elf_low; + elf_kernel_size = elf_high - elf_low; + + if (pvh_start_addr == 0) { + error_report("Error loading uncompressed kernel without PVH ELF Note"); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); + + return true; +} + +void x86_load_linux(X86MachineState *x86ms, + FWCfgState *fw_cfg, + int acpi_data_size, + bool pvh_enabled) +{ + bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; + uint16_t protocol; + int setup_size, kernel_size, cmdline_size; + int dtb_size, setup_data_offset; + uint32_t initrd_max; + uint8_t header[8192], *setup, *kernel; + hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; + FILE *f; + char *vmode; + MachineState *machine = MACHINE(x86ms); + struct setup_data *setup_data; + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + const char *dtb_filename = machine->dtb; + const char *kernel_cmdline = machine->kernel_cmdline; + SevKernelLoaderContext sev_load_ctx = {}; + + /* Align to 16 bytes as a paranoia measure */ + cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; + + /* load the kernel header */ + f = fopen(kernel_filename, "rb"); + if (!f) { + fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + kernel_size = get_file_size(f); + if (!kernel_size || + fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != + MIN(ARRAY_SIZE(header), kernel_size)) { + fprintf(stderr, "qemu: could not load kernel '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + /* + * kernel protocol version. + * Please see https://www.kernel.org/doc/Documentation/x86/boot.txt + */ + if (ldl_le_p(header + 0x202) == 0x53726448) /* Magic signature "HdrS" */ { + protocol = lduw_le_p(header + 0x206); + } else { + /* + * This could be a multiboot kernel. If it is, let's stop treating it + * like a Linux kernel. + * Note: some multiboot images could be in the ELF format (the same of + * PVH), so we try multiboot first since we check the multiboot magic + * header before to load it. + */ + if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, + kernel_cmdline, kernel_size, header)) { + return; + } + /* + * Check if the file is an uncompressed kernel file (ELF) and load it, + * saving the PVH entry point used by the x86/HVM direct boot ABI. + * If load_elfboot() is successful, populate the fw_cfg info. + */ + if (pvh_enabled && + load_elfboot(kernel_filename, kernel_size, + header, pvh_start_addr, fw_cfg)) { + fclose(f); + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + + setup = g_memdup2(header, sizeof(header)); + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, + setup, sizeof(header)); + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + x86ms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, + initrd_size); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "pvh.bin"; + nb_option_roms++; + + return; + } + protocol = 0; + } + + if (protocol < 0x200 || !(header[0x211] & 0x01)) { + /* Low kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x10000; + } else if (protocol < 0x202) { + /* High but ancient kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x100000; + } else { + /* High and recent kernel */ + real_addr = 0x10000; + cmdline_addr = 0x20000; + prot_addr = 0x100000; + } + + /* highest address for loading the initrd */ + if (protocol >= 0x20c && + lduw_le_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { + /* + * Linux has supported initrd up to 4 GB for a very long time (2007, + * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), + * though it only sets initrd_max to 2 GB to "work around bootloader + * bugs". Luckily, QEMU firmware(which does something like bootloader) + * has supported this. + * + * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can + * be loaded into any address. + * + * In addition, initrd_max is uint32_t simply because QEMU doesn't + * support the 64-bit boot protocol (specifically the ext_ramdisk_image + * field). + * + * Therefore here just limit initrd_max to UINT32_MAX simply as well. + */ + initrd_max = UINT32_MAX; + } else if (protocol >= 0x203) { + initrd_max = ldl_le_p(header + 0x22c); + } else { + initrd_max = 0x37ffffff; + } + + if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; + } + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + sev_load_ctx.cmdline_data = (char *)kernel_cmdline; + sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; + + if (protocol >= 0x202) { + stl_le_p(header + 0x228, cmdline_addr); + } else { + stw_le_p(header + 0x20, 0xA33F); + stw_le_p(header + 0x22, cmdline_addr - real_addr); + } + + /* handle vga= parameter */ + vmode = strstr(kernel_cmdline, "vga="); + if (vmode) { + unsigned int video_mode; + const char *end; + int ret; + /* skip "vga=" */ + vmode += 4; + if (!strncmp(vmode, "normal", 6)) { + video_mode = 0xffff; + } else if (!strncmp(vmode, "ext", 3)) { + video_mode = 0xfffe; + } else if (!strncmp(vmode, "ask", 3)) { + video_mode = 0xfffd; + } else { + ret = qemu_strtoui(vmode, &end, 0, &video_mode); + if (ret != 0 || (*end && *end != ' ')) { + fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); + exit(1); + } + } + stw_le_p(header + 0x1fa, video_mode); + } + + /* loader type */ + /* + * High nybble = B reserved for QEMU; low nybble is revision number. + * If this code is substantially changed, you may want to consider + * incrementing the revision. + */ + if (protocol >= 0x200) { + header[0x210] = 0xB0; + } + /* heap */ + if (protocol >= 0x201) { + header[0x211] |= 0x80; /* CAN_USE_HEAP */ + stw_le_p(header + 0x224, cmdline_addr - real_addr - 0x200); + } + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + if (protocol < 0x200) { + fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); + exit(1); + } + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + x86ms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); + sev_load_ctx.initrd_data = initrd_data; + sev_load_ctx.initrd_size = initrd_size; + + stl_le_p(header + 0x218, initrd_addr); + stl_le_p(header + 0x21c, initrd_size); + } + + /* load kernel and setup */ + setup_size = header[0x1f1]; + if (setup_size == 0) { + setup_size = 4; + } + setup_size = (setup_size + 1) * 512; + if (setup_size > kernel_size) { + fprintf(stderr, "qemu: invalid kernel header\n"); + exit(1); + } + kernel_size -= setup_size; + + setup = g_malloc(setup_size); + kernel = g_malloc(kernel_size); + fseek(f, 0, SEEK_SET); + if (fread(setup, 1, setup_size, f) != setup_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + if (fread(kernel, 1, kernel_size, f) != kernel_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + fclose(f); + + /* append dtb to kernel */ + if (dtb_filename) { + if (protocol < 0x209) { + fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); + exit(1); + } + + dtb_size = get_image_size(dtb_filename); + if (dtb_size <= 0) { + fprintf(stderr, "qemu: error reading dtb %s: %s\n", + dtb_filename, strerror(errno)); + exit(1); + } + + setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); + kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; + kernel = g_realloc(kernel, kernel_size); + + stq_le_p(header + 0x250, prot_addr + setup_data_offset); + + setup_data = (struct setup_data *)(kernel + setup_data_offset); + setup_data->next = 0; + setup_data->type = cpu_to_le32(SETUP_DTB); + setup_data->len = cpu_to_le32(dtb_size); + + load_image_size(dtb_filename, setup_data->data, dtb_size); + } + + /* + * If we're starting an encrypted VM, it will be OVMF based, which uses the + * efi stub for booting and doesn't require any values to be placed in the + * kernel header. We therefore don't update the header so the hash of the + * kernel on the other side of the fw_cfg interface matches the hash of the + * file the user passed in. + */ + if (!sev_enabled()) { + memcpy(setup, header, MIN(sizeof(header), setup_size)); + } + + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); + sev_load_ctx.kernel_data = (char *)kernel; + sev_load_ctx.kernel_size = kernel_size; + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); + sev_load_ctx.setup_data = (char *)setup; + sev_load_ctx.setup_size = setup_size; + + if (sev_enabled()) { + sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "linuxboot.bin"; + if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { + option_rom[nb_option_roms].name = "linuxboot_dma.bin"; + } + nb_option_roms++; +} + +void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, + MemoryRegion *bios, bool read_only) +{ + uint64_t bios_size = memory_region_size(bios); + uint64_t isa_bios_size = MIN(bios_size, 128 * KiB); + + memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(isa_memory, 1 * MiB - isa_bios_size, + isa_bios, 1); + memory_region_set_readonly(isa_bios, read_only); +} + +void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, + MemoryRegion *rom_memory, bool isapc_ram_fw) +{ + const char *bios_name; + char *filename; + int bios_size; + ssize_t ret; + + /* BIOS load */ + bios_name = MACHINE(x86ms)->firmware ?: default_firmware; + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } + if (bios_size <= 0 || + (bios_size % 65536) != 0) { + goto bios_error; + } + if (machine_require_guest_memfd(MACHINE(x86ms))) { + memory_region_init_ram_guest_memfd(&x86ms->bios, NULL, "pc.bios", + bios_size, &error_fatal); + } else { + memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", + bios_size, &error_fatal); + } + if (sev_enabled()) { + /* + * The concept of a "reset" simply doesn't exist for + * confidential computing guests, we have to destroy and + * re-launch them instead. So there is no need to register + * the firmware as rom to properly re-initialize on reset. + * Just go for a straight file load instead. + */ + void *ptr = memory_region_get_ram_ptr(&x86ms->bios); + load_image_size(filename, ptr, bios_size); + x86_firmware_configure(0x100000000ULL - bios_size, ptr, bios_size); + } else { + memory_region_set_readonly(&x86ms->bios, !isapc_ram_fw); + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); + if (ret != 0) { + goto bios_error; + } + } + g_free(filename); + + if (!machine_require_guest_memfd(MACHINE(x86ms))) { + /* map the last 128KB of the BIOS in ISA space */ + x86_isa_bios_init(&x86ms->isa_bios, rom_memory, &x86ms->bios, + !isapc_ram_fw); + } + + /* map all the bios at the top of memory */ + memory_region_add_subregion(rom_memory, + (uint32_t)(-bios_size), + &x86ms->bios); + return; + +bios_error: + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); + exit(1); +} diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c new file mode 100644 index 0000000000..7170e9082e --- /dev/null +++ b/hw/i386/x86-cpu.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019, 2024 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 "sysemu/whpx.h" +#include "sysemu/cpu-timers.h" +#include "trace.h" + +#include "hw/i386/x86.h" +#include "target/i386/cpu.h" +#include "hw/intc/i8259.h" +#include "hw/irq.h" +#include "sysemu/kvm.h" + +/* TSC handling */ +uint64_t cpu_get_tsc(CPUX86State *env) +{ +#ifdef XBOX + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 733333333, + NANOSECONDS_PER_SECOND); +#else + return cpus_get_elapsed_ticks(); +#endif +} + +/* IRQ handling */ +static void pic_irq_request(void *opaque, int irq, int level) +{ + CPUState *cs = first_cpu; + X86CPU *cpu = X86_CPU(cs); + + trace_x86_pic_interrupt(irq, level); + if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && + !whpx_apic_in_platform()) { + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); + if (apic_accept_pic_intr(cpu->apic_state)) { + apic_deliver_pic_intr(cpu->apic_state, level); + } + } + } else { + if (level) { + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +qemu_irq x86_allocate_cpu_irq(void) +{ + return qemu_allocate_irq(pic_irq_request, NULL, 0); +} + +int cpu_get_pic_interrupt(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + int intno; + + if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { + intno = apic_get_interrupt(cpu->apic_state); + if (intno >= 0) { + return intno; + } + /* read the irq from the PIC */ + if (!apic_accept_pic_intr(cpu->apic_state)) { + return -1; + } + } + + intno = pic_read_irq(isa_pic); + return intno; +} + +DeviceState *cpu_get_current_apic(void) +{ + if (current_cpu) { + X86CPU *cpu = X86_CPU(current_cpu); + return cpu->apic_state; + } else { + return NULL; + } +} 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 018fbb8b0a..01fc5e6562 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -22,53 +22,37 @@ */ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/cutils.h" #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" #include "qapi/visitor.h" #include "sysemu/qtest.h" -#include "sysemu/whpx.h" #include "sysemu/numa.h" -#include "sysemu/replay.h" -#include "sysemu/sysemu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/xen.h" #include "trace.h" +#include "hw/acpi/aml-build.h" #include "hw/i386/x86.h" -#include "target/i386/cpu.h" #include "hw/i386/topology.h" -#include "hw/i386/fw_cfg.h" -#include "hw/intc/i8259.h" -#include "hw/rtc/mc146818rtc.h" -#include "target/i386/sev.h" -#include "hw/acpi/cpu_hotplug.h" -#include "hw/irq.h" #include "hw/nmi.h" -#include "hw/loader.h" -#include "multiboot.h" -#include "elf.h" -#include "standard-headers/asm-x86/bootparam.h" -#include CONFIG_DEVICES #include "kvm/kvm_i386.h" -/* Physical Address of PVH entry point read from kernel ELF NOTE */ -static size_t pvh_start_addr; -inline void init_topo_info(X86CPUTopoInfo *topo_info, - const X86MachineState *x86ms) +void init_topo_info(X86CPUTopoInfo *topo_info, + const X86MachineState *x86ms) { MachineState *ms = MACHINE(x86ms); topo_info->dies_per_pkg = ms->smp.dies; - topo_info->cores_per_die = ms->smp.cores; + /* + * Though smp.modules means the number of modules in one cluster, + * i386 doesn't support cluster level so that the smp.clusters + * always defaults to 1, therefore using smp.modules directly is + * fine here. + */ + topo_info->modules_per_die = ms->smp.modules; + topo_info->cores_per_module = ms->smp.cores; topo_info->threads_per_core = ms->smp.threads; } @@ -90,346 +74,7 @@ uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, return x86_apicid_from_cpu_idx(&topo_info, cpu_index); } - -void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) -{ - Object *cpu = object_new(MACHINE(x86ms)->cpu_type); - - if (!object_property_set_uint(cpu, "apic-id", apic_id, errp)) { - goto out; - } - qdev_realize(DEVICE(cpu), NULL, errp); - -out: - object_unref(cpu); -} - -void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) -{ - int i; - const CPUArchIdList *possible_cpus; - MachineState *ms = MACHINE(x86ms); - MachineClass *mc = MACHINE_GET_CLASS(x86ms); - - x86_cpu_set_default_version(default_cpu_version); - - /* - * Calculates the limit to CPU APIC ID values - * - * Limit for the APIC ID value, so that all - * CPU APIC IDs are < x86ms->apic_id_limit. - * - * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). - */ - x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, - ms->smp.max_cpus - 1) + 1; - - /* - * Can we support APIC ID 255 or higher? - * - * Under Xen: yes. - * With userspace emulated lapic: no - * With KVM's in-kernel lapic: only if X2APIC API is enabled. - */ - if (x86ms->apic_id_limit > 255 && !xen_enabled() && - (!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); - } - - 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) -{ - 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); - } else { - rtc_set_memory(rtc, 0x5f, cpus_count - 1); - } -} - -static int x86_apic_cmp(const void *a, const void *b) -{ - CPUArchId *apic_a = (CPUArchId *)a; - CPUArchId *apic_b = (CPUArchId *)b; - - return apic_a->arch_id - apic_b->arch_id; -} - -/* - * returns pointer to CPUArchId descriptor that matches CPU's apic_id - * in ms->possible_cpus->cpus, if ms->possible_cpus->cpus has no - * entry corresponding to CPU's apic_id returns NULL. - */ -CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) -{ - 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), - x86_apic_cmp); - if (found_cpu && idx) { - *idx = found_cpu - ms->possible_cpus->cpus; - } - return found_cpu; -} - -void x86_cpu_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - CPUArchId *found_cpu; - Error *local_err = NULL; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - if (x86ms->acpi_dev) { - hotplug_handler_plug(x86ms->acpi_dev, dev, &local_err); - if (local_err) { - goto out; - } - } - - /* increment the number of CPUs */ - x86ms->boot_cpus++; - if (x86ms->rtc) { - x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - } - if (x86ms->fw_cfg) { - fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); - } - - found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = OBJECT(dev); -out: - error_propagate(errp, local_err); -} - -void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - int idx = -1; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - if (!x86ms->acpi_dev) { - error_setg(errp, "CPU hot unplug not supported without ACPI"); - return; - } - - x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, &idx); - assert(idx != -1); - if (idx == 0) { - error_setg(errp, "Boot CPU is unpluggable"); - return; - } - - hotplug_handler_unplug_request(x86ms->acpi_dev, dev, - errp); -} - -void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - CPUArchId *found_cpu; - Error *local_err = NULL; - X86CPU *cpu = X86_CPU(dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - - hotplug_handler_unplug(x86ms->acpi_dev, dev, &local_err); - if (local_err) { - goto out; - } - - found_cpu = x86_find_cpu_slot(MACHINE(x86ms), cpu->apic_id, NULL); - found_cpu->cpu = NULL; - qdev_unrealize(dev); - - /* decrement the number of CPUs */ - x86ms->boot_cpus--; - /* Update the number of CPUs in CMOS */ - x86_rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); - fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); - out: - error_propagate(errp, local_err); -} - -void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - int idx; - CPUState *cs; - CPUArchId *cpu_slot; - X86CPUTopoIDs topo_ids; - X86CPU *cpu = X86_CPU(dev); - CPUX86State *env = &cpu->env; - MachineState *ms = MACHINE(hotplug_dev); - X86MachineState *x86ms = X86_MACHINE(hotplug_dev); - unsigned int smp_cores = ms->smp.cores; - unsigned int smp_threads = ms->smp.threads; - X86CPUTopoInfo topo_info; - - if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) { - error_setg(errp, "Invalid CPU type, expected cpu type: '%s'", - ms->cpu_type); - return; - } - - if (x86ms->acpi_dev) { - Error *local_err = NULL; - - hotplug_handler_pre_plug(HOTPLUG_HANDLER(x86ms->acpi_dev), dev, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } - - init_topo_info(&topo_info, x86ms); - - env->nr_dies = ms->smp.dies; - - /* - * If APIC ID is not set, - * set it based on socket/die/core/thread properties. - */ - if (cpu->apic_id == UNASSIGNED_APIC_ID) { - int max_socket = (ms->smp.max_cpus - 1) / - smp_threads / smp_cores / ms->smp.dies; - - /* - * die-id was optional in QEMU 4.0 and older, so keep it optional - * if there's only one die per socket. - */ - if (cpu->die_id < 0 && ms->smp.dies == 1) { - cpu->die_id = 0; - } - - if (cpu->socket_id < 0) { - error_setg(errp, "CPU socket-id is not set"); - return; - } else if (cpu->socket_id > max_socket) { - error_setg(errp, "Invalid CPU socket-id: %u must be in range 0:%u", - cpu->socket_id, max_socket); - return; - } - if (cpu->die_id < 0) { - error_setg(errp, "CPU die-id is not set"); - return; - } else if (cpu->die_id > ms->smp.dies - 1) { - error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", - cpu->die_id, ms->smp.dies - 1); - return; - } - if (cpu->core_id < 0) { - error_setg(errp, "CPU core-id is not set"); - return; - } else if (cpu->core_id > (smp_cores - 1)) { - error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u", - cpu->core_id, smp_cores - 1); - return; - } - if (cpu->thread_id < 0) { - error_setg(errp, "CPU thread-id is not set"); - return; - } else if (cpu->thread_id > (smp_threads - 1)) { - error_setg(errp, "Invalid CPU thread-id: %u must be in range 0:%u", - cpu->thread_id, smp_threads - 1); - return; - } - - topo_ids.pkg_id = cpu->socket_id; - topo_ids.die_id = cpu->die_id; - topo_ids.core_id = cpu->core_id; - topo_ids.smt_id = cpu->thread_id; - cpu->apic_id = x86_apicid_from_topo_ids(&topo_info, &topo_ids); - } - - 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" - " APIC ID %" PRIu32 ", valid index range 0:%d", - topo_ids.pkg_id, topo_ids.die_id, topo_ids.core_id, topo_ids.smt_id, - cpu->apic_id, ms->possible_cpus->len - 1); - return; - } - - if (cpu_slot->cpu) { - error_setg(errp, "CPU[%d] with APIC ID %" PRIu32 " exists", - idx, cpu->apic_id); - return; - } - - /* if 'address' properties socket-id/core-id/thread-id are not set, set them - * so that machine_query_hotpluggable_cpus would show correct values - */ - /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() - * once -smp refactoring is complete and there will be CPU private - * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, &topo_info, &topo_ids); - if (cpu->socket_id != -1 && cpu->socket_id != topo_ids.pkg_id) { - error_setg(errp, "property socket-id: %u doesn't match set apic-id:" - " 0x%x (socket-id: %u)", cpu->socket_id, cpu->apic_id, - topo_ids.pkg_id); - return; - } - cpu->socket_id = topo_ids.pkg_id; - - if (cpu->die_id != -1 && cpu->die_id != topo_ids.die_id) { - error_setg(errp, "property die-id: %u doesn't match set apic-id:" - " 0x%x (die-id: %u)", cpu->die_id, cpu->apic_id, topo_ids.die_id); - return; - } - cpu->die_id = topo_ids.die_id; - - if (cpu->core_id != -1 && cpu->core_id != topo_ids.core_id) { - error_setg(errp, "property core-id: %u doesn't match set apic-id:" - " 0x%x (core-id: %u)", cpu->core_id, cpu->apic_id, - topo_ids.core_id); - return; - } - cpu->core_id = topo_ids.core_id; - - if (cpu->thread_id != -1 && cpu->thread_id != topo_ids.smt_id) { - error_setg(errp, "property thread-id: %u doesn't match set apic-id:" - " 0x%x (thread-id: %u)", cpu->thread_id, cpu->apic_id, - topo_ids.smt_id); - return; - } - cpu->thread_id = topo_ids.smt_id; - - if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && - !kvm_hv_vpindex_settable()) { - error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); - return; - } - - cs = CPU(cpu); - cs->cpu_index = idx; - - numa_cpu_pre_plug(cpu_slot, dev, errp); -} - -CpuInstanceProperties +static CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -439,7 +84,7 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) return possible_cpus->cpus[cpu_index].props; } -int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) +static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { X86CPUTopoIDs topo_ids; X86MachineState *x86ms = X86_MACHINE(ms); @@ -453,7 +98,7 @@ int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) return topo_ids.pkg_id % ms->numa_state->num_nodes; } -const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) +static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { X86MachineState *x86ms = X86_MACHINE(ms); unsigned int max_cpus = ms->smp.max_cpus; @@ -490,6 +135,10 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->cpus[i].props.has_die_id = true; ms->possible_cpus->cpus[i].props.die_id = topo_ids.die_id; } + if (ms->smp.modules > 1) { + ms->possible_cpus->cpus[i].props.has_module_id = true; + ms->possible_cpus->cpus[i].props.module_id = topo_ids.module_id; + } ms->possible_cpus->cpus[i].props.has_core_id = true; ms->possible_cpus->cpus[i].props.core_id = topo_ids.core_id; ms->possible_cpus->cpus[i].props.has_thread_id = true; @@ -506,686 +155,14 @@ 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); - } - } -} - -static long get_file_size(FILE *f) -{ - long where, size; - - /* XXX: on Unix systems, using fstat() probably makes more sense */ - - where = ftell(f); - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, where, SEEK_SET); - - return size; -} - -/* TSC handling */ -uint64_t cpu_get_tsc(CPUX86State *env) -{ -#ifdef XBOX - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 733333333, - NANOSECONDS_PER_SECOND); -#else - return cpus_get_elapsed_ticks(); -#endif -} - -/* IRQ handling */ -static void pic_irq_request(void *opaque, int irq, int level) -{ - CPUState *cs = first_cpu; - X86CPU *cpu = X86_CPU(cs); - - trace_x86_pic_interrupt(irq, level); - if (cpu->apic_state && !kvm_irqchip_in_kernel() && - !whpx_apic_in_platform()) { - CPU_FOREACH(cs) { - cpu = X86_CPU(cs); - if (apic_accept_pic_intr(cpu->apic_state)) { - apic_deliver_pic_intr(cpu->apic_state, level); - } - } - } else { - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + cpu_interrupt(cs, CPU_INTERRUPT_NMI); } } } -qemu_irq x86_allocate_cpu_irq(void) -{ - return qemu_allocate_irq(pic_irq_request, NULL, 0); -} - -int cpu_get_pic_interrupt(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - int intno; - - if (!kvm_irqchip_in_kernel() && !whpx_apic_in_platform()) { - intno = apic_get_interrupt(cpu->apic_state); - if (intno >= 0) { - return intno; - } - /* read the irq from the PIC */ - if (!apic_accept_pic_intr(cpu->apic_state)) { - return -1; - } - } - - intno = pic_read_irq(isa_pic); - return intno; -} - -DeviceState *cpu_get_current_apic(void) -{ - if (current_cpu) { - X86CPU *cpu = X86_CPU(current_cpu); - return cpu->apic_state; - } else { - return NULL; - } -} - -void gsi_handler(void *opaque, int n, int level) -{ - GSIState *s = opaque; - - trace_x86_gsi_interrupt(n, level); - switch (n) { - case 0 ... ISA_NUM_IRQS - 1: - if (s->i8259_irq[n]) { - /* Under KVM, Kernel will forward to both PIC and IOAPIC */ - qemu_set_irq(s->i8259_irq[n], level); - } - /* fall through */ - case ISA_NUM_IRQS ... IOAPIC_NUM_PINS - 1: - qemu_set_irq(s->ioapic_irq[n], level); - break; - case IO_APIC_SECONDARY_IRQBASE - ... IO_APIC_SECONDARY_IRQBASE + IOAPIC_NUM_PINS - 1: - qemu_set_irq(s->ioapic2_irq[n - IO_APIC_SECONDARY_IRQBASE], level); - break; - } -} - -void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - assert(parent_name); - if (kvm_ioapic_in_kernel()) { - dev = qdev_new(TYPE_KVM_IOAPIC); - } else { - dev = qdev_new(TYPE_IOAPIC); - } - object_property_add_child(object_resolve_path(parent_name, NULL), - "ioapic", OBJECT(dev)); - d = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i); - } -} - -DeviceState *ioapic_init_secondary(GSIState *gsi_state) -{ - DeviceState *dev; - SysBusDevice *d; - unsigned int i; - - dev = qdev_new(TYPE_IOAPIC); - d = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(d, &error_fatal); - sysbus_mmio_map(d, 0, IO_APIC_SECONDARY_ADDRESS); - - for (i = 0; i < IOAPIC_NUM_PINS; i++) { - gsi_state->ioapic2_irq[i] = qdev_get_gpio_in(dev, i); - } - return dev; -} - -struct setup_data { - uint64_t next; - uint32_t type; - uint32_t len; - uint8_t data[]; -} __attribute__((packed)); - - -/* - * The entry point into the kernel for PVH boot is different from - * the native entry point. The PVH entry is defined by the x86/HVM - * direct boot ABI and is available in an ELFNOTE in the kernel binary. - * - * This function is passed to load_elf() when it is called from - * load_elfboot() which then additionally checks for an ELF Note of - * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to - * parse the PVH entry address from the ELF Note. - * - * Due to trickery in elf_opts.h, load_elf() is actually available as - * load_elf32() or load_elf64() and this routine needs to be able - * to deal with being called as 32 or 64 bit. - * - * The address of the PVH entry point is saved to the 'pvh_start_addr' - * global variable. (although the entry point is 32-bit, the kernel - * binary can be either 32-bit or 64-bit). - */ -static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) -{ - size_t *elf_note_data_addr; - - /* Check if ELF Note header passed in is valid */ - if (arg1 == NULL) { - return 0; - } - - if (is64) { - struct elf64_note *nhdr64 = (struct elf64_note *)arg1; - uint64_t nhdr_size64 = sizeof(struct elf64_note); - uint64_t phdr_align = *(uint64_t *)arg2; - uint64_t nhdr_namesz = nhdr64->n_namesz; - - elf_note_data_addr = - ((void *)nhdr64) + nhdr_size64 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - - pvh_start_addr = *elf_note_data_addr; - } else { - struct elf32_note *nhdr32 = (struct elf32_note *)arg1; - uint32_t nhdr_size32 = sizeof(struct elf32_note); - uint32_t phdr_align = *(uint32_t *)arg2; - uint32_t nhdr_namesz = nhdr32->n_namesz; - - elf_note_data_addr = - ((void *)nhdr32) + nhdr_size32 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - - pvh_start_addr = *(uint32_t *)elf_note_data_addr; - } - - return pvh_start_addr; -} - -static bool load_elfboot(const char *kernel_filename, - int kernel_file_size, - uint8_t *header, - size_t pvh_xen_start_addr, - FWCfgState *fw_cfg) -{ - uint32_t flags = 0; - uint32_t mh_load_addr = 0; - uint32_t elf_kernel_size = 0; - uint64_t elf_entry; - uint64_t elf_low, elf_high; - int kernel_size; - - if (ldl_p(header) != 0x464c457f) { - return false; /* no elfboot */ - } - - bool elf_is64 = header[EI_CLASS] == ELFCLASS64; - flags = elf_is64 ? - ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; - - if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ - error_report("elfboot unsupported flags = %x", flags); - exit(1); - } - - uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; - kernel_size = load_elf(kernel_filename, read_pvh_start_addr, - NULL, &elf_note_type, &elf_entry, - &elf_low, &elf_high, NULL, 0, I386_ELF_MACHINE, - 0, 0); - - if (kernel_size < 0) { - error_report("Error while loading elf kernel"); - exit(1); - } - mh_load_addr = elf_low; - elf_kernel_size = elf_high - elf_low; - - if (pvh_start_addr == 0) { - error_report("Error loading uncompressed kernel without PVH ELF Note"); - exit(1); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); - - return true; -} - -void x86_load_linux(X86MachineState *x86ms, - FWCfgState *fw_cfg, - int acpi_data_size, - bool pvh_enabled) -{ - bool linuxboot_dma_enabled = X86_MACHINE_GET_CLASS(x86ms)->fwcfg_dma_enabled; - uint16_t protocol; - int setup_size, kernel_size, cmdline_size; - int dtb_size, setup_data_offset; - uint32_t initrd_max; - uint8_t header[8192], *setup, *kernel; - hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; - FILE *f; - char *vmode; - MachineState *machine = MACHINE(x86ms); - struct setup_data *setup_data; - const char *kernel_filename = machine->kernel_filename; - const char *initrd_filename = machine->initrd_filename; - const char *dtb_filename = machine->dtb; - const char *kernel_cmdline = machine->kernel_cmdline; - SevKernelLoaderContext sev_load_ctx = {}; - - /* Align to 16 bytes as a paranoia measure */ - cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; - - /* load the kernel header */ - f = fopen(kernel_filename, "rb"); - if (!f) { - fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - kernel_size = get_file_size(f); - if (!kernel_size || - fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != - MIN(ARRAY_SIZE(header), kernel_size)) { - fprintf(stderr, "qemu: could not load kernel '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - /* kernel protocol version */ - if (ldl_p(header + 0x202) == 0x53726448) { - protocol = lduw_p(header + 0x206); - } else { - /* - * This could be a multiboot kernel. If it is, let's stop treating it - * like a Linux kernel. - * Note: some multiboot images could be in the ELF format (the same of - * PVH), so we try multiboot first since we check the multiboot magic - * header before to load it. - */ - if (load_multiboot(x86ms, fw_cfg, f, kernel_filename, initrd_filename, - kernel_cmdline, kernel_size, header)) { - return; - } - /* - * Check if the file is an uncompressed kernel file (ELF) and load it, - * saving the PVH entry point used by the x86/HVM direct boot ABI. - * If load_elfboot() is successful, populate the fw_cfg info. - */ - if (pvh_enabled && - load_elfboot(kernel_filename, kernel_size, - header, pvh_start_addr, fw_cfg)) { - fclose(f); - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, - header, sizeof(header)); - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - x86ms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, - initrd_size); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "pvh.bin"; - nb_option_roms++; - - return; - } - protocol = 0; - } - - if (protocol < 0x200 || !(header[0x211] & 0x01)) { - /* Low kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x10000; - } else if (protocol < 0x202) { - /* High but ancient kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x100000; - } else { - /* High and recent kernel */ - real_addr = 0x10000; - cmdline_addr = 0x20000; - prot_addr = 0x100000; - } - - /* highest address for loading the initrd */ - if (protocol >= 0x20c && - lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { - /* - * Linux has supported initrd up to 4 GB for a very long time (2007, - * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), - * though it only sets initrd_max to 2 GB to "work around bootloader - * bugs". Luckily, QEMU firmware(which does something like bootloader) - * has supported this. - * - * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can - * be loaded into any address. - * - * In addition, initrd_max is uint32_t simply because QEMU doesn't - * support the 64-bit boot protocol (specifically the ext_ramdisk_image - * field). - * - * Therefore here just limit initrd_max to UINT32_MAX simply as well. - */ - initrd_max = UINT32_MAX; - } else if (protocol >= 0x203) { - initrd_max = ldl_p(header + 0x22c); - } else { - initrd_max = 0x37ffffff; - } - - if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { - initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; - } - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - sev_load_ctx.cmdline_data = (char *)kernel_cmdline; - sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1; - - if (protocol >= 0x202) { - stl_p(header + 0x228, cmdline_addr); - } else { - stw_p(header + 0x20, 0xA33F); - stw_p(header + 0x22, cmdline_addr - real_addr); - } - - /* handle vga= parameter */ - vmode = strstr(kernel_cmdline, "vga="); - if (vmode) { - unsigned int video_mode; - const char *end; - int ret; - /* skip "vga=" */ - vmode += 4; - if (!strncmp(vmode, "normal", 6)) { - video_mode = 0xffff; - } else if (!strncmp(vmode, "ext", 3)) { - video_mode = 0xfffe; - } else if (!strncmp(vmode, "ask", 3)) { - video_mode = 0xfffd; - } else { - ret = qemu_strtoui(vmode, &end, 0, &video_mode); - if (ret != 0 || (*end && *end != ' ')) { - fprintf(stderr, "qemu: invalid 'vga=' kernel parameter.\n"); - exit(1); - } - } - stw_p(header + 0x1fa, video_mode); - } - - /* loader type */ - /* - * High nybble = B reserved for QEMU; low nybble is revision number. - * If this code is substantially changed, you may want to consider - * incrementing the revision. - */ - if (protocol >= 0x200) { - header[0x210] = 0xB0; - } - /* heap */ - if (protocol >= 0x201) { - header[0x211] |= 0x80; /* CAN_USE_HEAP */ - stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); - } - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - if (protocol < 0x200) { - fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); - exit(1); - } - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - x86ms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); - sev_load_ctx.initrd_data = initrd_data; - sev_load_ctx.initrd_size = initrd_size; - - stl_p(header + 0x218, initrd_addr); - stl_p(header + 0x21c, initrd_size); - } - - /* load kernel and setup */ - setup_size = header[0x1f1]; - if (setup_size == 0) { - setup_size = 4; - } - setup_size = (setup_size + 1) * 512; - if (setup_size > kernel_size) { - fprintf(stderr, "qemu: invalid kernel header\n"); - exit(1); - } - kernel_size -= setup_size; - - setup = g_malloc(setup_size); - kernel = g_malloc(kernel_size); - fseek(f, 0, SEEK_SET); - if (fread(setup, 1, setup_size, f) != setup_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - if (fread(kernel, 1, kernel_size, f) != kernel_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - fclose(f); - - /* append dtb to kernel */ - if (dtb_filename) { - if (protocol < 0x209) { - fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); - exit(1); - } - - dtb_size = get_image_size(dtb_filename); - if (dtb_size <= 0) { - fprintf(stderr, "qemu: error reading dtb %s: %s\n", - dtb_filename, strerror(errno)); - exit(1); - } - - setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); - kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; - kernel = g_realloc(kernel, kernel_size); - - stq_p(header + 0x250, prot_addr + setup_data_offset); - - setup_data = (struct setup_data *)(kernel + setup_data_offset); - setup_data->next = 0; - setup_data->type = cpu_to_le32(SETUP_DTB); - setup_data->len = cpu_to_le32(dtb_size); - - load_image_size(dtb_filename, setup_data->data, dtb_size); - } - - /* - * If we're starting an encrypted VM, it will be OVMF based, which uses the - * efi stub for booting and doesn't require any values to be placed in the - * kernel header. We therefore don't update the header so the hash of the - * kernel on the other side of the fw_cfg interface matches the hash of the - * file the user passed in. - */ - if (!sev_enabled()) { - memcpy(setup, header, MIN(sizeof(header), setup_size)); - } - - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); - sev_load_ctx.kernel_data = (char *)kernel; - sev_load_ctx.kernel_size = kernel_size; - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - sev_load_ctx.setup_data = (char *)setup; - sev_load_ctx.setup_size = setup_size; - - if (sev_enabled()) { - sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "linuxboot.bin"; - if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { - option_rom[nb_option_roms].name = "linuxboot_dma.bin"; - } - nb_option_roms++; -} - -void x86_bios_rom_init(MachineState *ms, const char *default_firmware, - MemoryRegion *rom_memory, bool isapc_ram_fw) -{ - const char *bios_name; - char *filename; - MemoryRegion *bios, *isa_bios; - int bios_size, isa_bios_size; - ssize_t ret; - - /* BIOS load */ - bios_name = ms->firmware ?: default_firmware; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } - if (bios_size <= 0 || - (bios_size % 65536) != 0) { - goto bios_error; - } - bios = g_malloc(sizeof(*bios)); - memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); - if (sev_enabled()) { - /* - * The concept of a "reset" simply doesn't exist for - * confidential computing guests, we have to destroy and - * re-launch them instead. So there is no need to register - * the firmware as rom to properly re-initialize on reset. - * Just go for a straight file load instead. - */ - void *ptr = memory_region_get_ram_ptr(bios); - load_image_size(filename, ptr, bios_size); - x86_firmware_configure(ptr, bios_size); - } else { - if (!isapc_ram_fw) { - memory_region_set_readonly(bios, true); - } - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - goto bios_error; - } - } - g_free(filename); - - /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = MIN(bios_size, 128 * KiB); - isa_bios = g_malloc(sizeof(*isa_bios)); - memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(rom_memory, - 0x100000 - isa_bios_size, - isa_bios, - 1); - if (!isapc_ram_fw) { - memory_region_set_readonly(isa_bios, true); - } - - /* map all the bios at the top of memory */ - memory_region_add_subregion(rom_memory, - (uint32_t)(-bios_size), - bios); - return; - -bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); -} - bool x86_machine_is_smm_enabled(const X86MachineState *x86ms) { bool smm_available = false; @@ -1265,7 +242,7 @@ static void x86_machine_get_pit(Object *obj, Visitor *v, const char *name, static void x86_machine_set_pit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - X86MachineState *x86ms = X86_MACHINE(obj);; + X86MachineState *x86ms = X86_MACHINE(obj); visit_type_OnOffAuto(v, name, &x86ms->pit, errp); } @@ -1370,6 +347,16 @@ static void machine_set_sgx_epc(Object *obj, Visitor *v, const char *name, qapi_free_SgxEPCList(list); } +static int x86_kvm_type(MachineState *ms, const char *vm_type) +{ + /* + * No x86 machine has a kvm-type property. If one is added that has + * it, it should call kvm_get_vm_type() directly or not use it at all. + */ + assert(vm_type == NULL); + return kvm_enabled() ? kvm_get_vm_type(ms) : 0; +} + static void x86_machine_initfn(Object *obj) { X86MachineState *x86ms = X86_MACHINE(obj); @@ -1394,6 +381,7 @@ static void x86_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = x86_cpu_index_to_props; mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; + mc->kvm_type = x86_kvm_type; x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/hw/i386/xen/meson.build b/hw/i386/xen/meson.build index be84130300..c73c62b8e3 100644 --- a/hw/i386/xen/meson.build +++ b/hw/i386/xen/meson.build @@ -1,7 +1,12 @@ 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', + 'xen-pvh.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 e4293d6d66..d3df488c48 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -10,52 +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/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 @@ -74,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 */ @@ -142,7 +70,7 @@ 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); @@ -222,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. @@ -236,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)) { @@ -297,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) { @@ -354,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; @@ -361,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) { @@ -401,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) { @@ -417,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)); @@ -430,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; } @@ -445,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) { @@ -462,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); }; } } @@ -676,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); } @@ -695,11 +457,12 @@ static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) int128_get64(section->size)); } -static void xen_log_global_start(MemoryListener *listener) +static bool xen_log_global_start(MemoryListener *listener, Error **errp) { if (xen_enabled()) { xen_in_migration = true; } + return true; } static void xen_log_global_stop(MemoryListener *listener) @@ -707,7 +470,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, @@ -716,280 +479,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; @@ -1021,9 +513,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]; @@ -1033,226 +525,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) { @@ -1312,133 +584,58 @@ 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) +static bool xen_check_stubdomain(struct xs_handle *xsh) { - void *addr = NULL; - xen_pfn_t ioreq_pfn; - xen_pfn_t bufioreq_pfn; - evtchn_port_t bufioreq_evtchn; - int rc; + char *dm_path = g_strdup_printf( + "/local/domain/%d/image/device-model-domid", xen_domid); + char *val; + int32_t dm_domid; + bool is_stubdom = false; - /* - * 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); + val = xs_read(xsh, 0, dm_path, NULL); + if (val) { + if (sscanf(val, "%d", &dm_domid) == 1) { + is_stubdom = dm_domid != 0; } + free(val); } - 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; + g_free(dm_path); + return is_stubdom; } 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, + HVM_IOREQSRV_BUFIOREQ_ATOMIC, + &xen_memory_listener); - state->xenstore = xs_daemon_open(); - if (state->xenstore == NULL) { - perror("xen: xenstore open"); - goto err; - } + xen_is_stubdomain = xen_check_stubdomain(state->xenstore); - xen_create_ioreq_server(xen_domid, &state->ioservid); + QLIST_INIT(&xen_physmap); + xen_read_physmap(state); - state->exit.notify = xen_exit_notifier; - qemu_add_exit_notifier(&state->exit); + suspend.notify = xen_suspend_notifier; + qemu_register_suspend_notifier(&suspend); - 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; @@ -1449,70 +646,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; @@ -1523,72 +658,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) { @@ -1602,8 +693,65 @@ void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) void qmp_xen_set_global_dirty_log(bool enable, Error **errp) { if (enable) { - memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); + memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, errp); } else { 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-mapcache.c b/hw/i386/xen/xen-mapcache.c deleted file mode 100644 index a2f93096e7..0000000000 --- a/hw/i386/xen/xen-mapcache.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright (C) 2011 Citrix Ltd. - * - * 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/units.h" -#include "qemu/error-report.h" - -#include - -#include "hw/xen/xen-legacy-backend.h" -#include "qemu/bitmap.h" - -#include "sysemu/runstate.h" -#include "sysemu/xen-mapcache.h" -#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 */ -#else -# define MCACHE_BUCKET_SHIFT 20 -# define MCACHE_MAX_SIZE (1UL<<35) /* 32GB Cap */ -#endif -#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) - -/* This is the size of the virtual address space reserve to QEMU that will not - * be use by MapCache. - * From empirical tests I observed that qemu use 75MB more than the - * max_mcache_size. - */ -#define NON_MCACHE_MEMORY_SIZE (80 * MiB) - -typedef struct MapCacheEntry { - hwaddr paddr_index; - uint8_t *vaddr_base; - unsigned long *valid_mapping; - uint32_t lock; -#define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0) - uint8_t flags; - hwaddr size; - struct MapCacheEntry *next; -} MapCacheEntry; - -typedef struct MapCacheRev { - uint8_t *vaddr_req; - hwaddr paddr_index; - hwaddr size; - QTAILQ_ENTRY(MapCacheRev) next; - bool dma; -} MapCacheRev; - -typedef struct MapCache { - MapCacheEntry *entry; - unsigned long nr_buckets; - QTAILQ_HEAD(, MapCacheRev) locked_entries; - - /* For most cases (>99.9%), the page address is the same. */ - MapCacheEntry *last_entry; - unsigned long max_mcache_size; - unsigned int mcache_bucket_shift; - - phys_offset_to_gaddr_t phys_offset_to_gaddr; - QemuMutex lock; - void *opaque; -} MapCache; - -static MapCache *mapcache; - -static inline void mapcache_lock(void) -{ - qemu_mutex_lock(&mapcache->lock); -} - -static inline void mapcache_unlock(void) -{ - qemu_mutex_unlock(&mapcache->lock); -} - -static inline int test_bits(int nr, int size, const unsigned long *addr) -{ - unsigned long res = find_next_zero_bit(addr, size + nr, nr); - if (res >= nr + size) - return 1; - else - return 0; -} - -void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) -{ - unsigned long size; - struct rlimit rlimit_as; - - mapcache = g_new0(MapCache, 1); - - mapcache->phys_offset_to_gaddr = f; - mapcache->opaque = opaque; - qemu_mutex_init(&mapcache->lock); - - QTAILQ_INIT(&mapcache->locked_entries); - - if (geteuid() == 0) { - rlimit_as.rlim_cur = RLIM_INFINITY; - rlimit_as.rlim_max = RLIM_INFINITY; - mapcache->max_mcache_size = MCACHE_MAX_SIZE; - } else { - getrlimit(RLIMIT_AS, &rlimit_as); - rlimit_as.rlim_cur = rlimit_as.rlim_max; - - if (rlimit_as.rlim_max != RLIM_INFINITY) { - warn_report("QEMU's maximum size of virtual" - " memory is not infinity"); - } - if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) { - mapcache->max_mcache_size = rlimit_as.rlim_max - - NON_MCACHE_MEMORY_SIZE; - } else { - mapcache->max_mcache_size = MCACHE_MAX_SIZE; - } - } - - setrlimit(RLIMIT_AS, &rlimit_as); - - mapcache->nr_buckets = - (((mapcache->max_mcache_size >> XC_PAGE_SHIFT) + - (1UL << (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)) - 1) >> - (MCACHE_BUCKET_SHIFT - XC_PAGE_SHIFT)); - - 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); - mapcache->entry = g_malloc0(size); -} - -static void xen_remap_bucket(MapCacheEntry *entry, - void *vaddr, - hwaddr size, - hwaddr address_index, - bool dummy) -{ - uint8_t *vaddr_base; - xen_pfn_t *pfns; - int *err; - unsigned int i; - hwaddr nb_pfn = size >> XC_PAGE_SHIFT; - - trace_xen_remap_bucket(address_index); - - pfns = g_new0(xen_pfn_t, nb_pfn); - err = g_new0(int, nb_pfn); - - if (entry->vaddr_base != NULL) { - if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { - ram_block_notify_remove(entry->vaddr_base, entry->size, - entry->size); - } - - /* - * If an entry is being replaced by another mapping and we're using - * MAP_FIXED flag for it - there is possibility of a race for vaddr - * address with another thread doing an mmap call itself - * (see man 2 mmap). To avoid that we skip explicit unmapping here - * and allow the kernel to destroy the previous mappings by replacing - * them in mmap call later. - * - * Non-identical replacements are not allowed therefore. - */ - assert(!vaddr || (entry->vaddr_base == vaddr && entry->size == size)); - - if (!vaddr && munmap(entry->vaddr_base, entry->size) != 0) { - perror("unmap fails"); - exit(-1); - } - } - g_free(entry->valid_mapping); - entry->valid_mapping = NULL; - - for (i = 0; i < nb_pfn; i++) { - pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; - } - - /* - * If the caller has requested the mapping at a specific address use - * MAP_FIXED to make sure it's honored. - */ - if (!dummy) { - vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, - PROT_READ | PROT_WRITE, - vaddr ? MAP_FIXED : 0, - nb_pfn, pfns, err); - if (vaddr_base == NULL) { - perror("xenforeignmemory_map2"); - exit(-1); - } - } else { - /* - * We create dummy mappings where we are unable to create a foreign - * mapping immediately due to certain circumstances (i.e. on resume now) - */ - vaddr_base = mmap(vaddr, size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_SHARED | (vaddr ? MAP_FIXED : 0), - -1, 0); - if (vaddr_base == MAP_FAILED) { - perror("mmap"); - exit(-1); - } - } - - if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { - ram_block_notify_add(vaddr_base, size, size); - } - - entry->vaddr_base = vaddr_base; - entry->paddr_index = address_index; - entry->size = size; - entry->valid_mapping = g_new0(unsigned long, - BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); - - if (dummy) { - entry->flags |= XEN_MAPCACHE_ENTRY_DUMMY; - } else { - entry->flags &= ~(XEN_MAPCACHE_ENTRY_DUMMY); - } - - bitmap_zero(entry->valid_mapping, nb_pfn); - for (i = 0; i < nb_pfn; i++) { - if (!err[i]) { - bitmap_set(entry->valid_mapping, i, 1); - } - } - - g_free(pfns); - g_free(err); -} - -static uint8_t *xen_map_cache_unlocked(hwaddr phys_addr, hwaddr size, - uint8_t lock, bool dma) -{ - MapCacheEntry *entry, *pentry = NULL, - *free_entry = NULL, *free_pentry = NULL; - hwaddr address_index; - hwaddr address_offset; - hwaddr cache_size = size; - hwaddr test_bit_size; - bool translated G_GNUC_UNUSED = false; - bool dummy = false; - -tryagain: - address_index = phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = phys_addr & (MCACHE_BUCKET_SIZE - 1); - - trace_xen_map_cache(phys_addr); - - /* test_bit_size is always a multiple of XC_PAGE_SIZE */ - if (size) { - test_bit_size = size + (phys_addr & (XC_PAGE_SIZE - 1)); - - if (test_bit_size % XC_PAGE_SIZE) { - test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); - } - } else { - test_bit_size = XC_PAGE_SIZE; - } - - if (mapcache->last_entry != NULL && - mapcache->last_entry->paddr_index == address_index && - !lock && !size && - 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); - return mapcache->last_entry->vaddr_base + address_offset; - } - - /* size is always a multiple of MCACHE_BUCKET_SIZE */ - if (size) { - cache_size = size + address_offset; - if (cache_size % MCACHE_BUCKET_SIZE) { - cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); - } - } else { - cache_size = MCACHE_BUCKET_SIZE; - } - - entry = &mapcache->entry[address_index % mapcache->nr_buckets]; - - while (entry && (lock || entry->lock) && entry->vaddr_base && - (entry->paddr_index != address_index || entry->size != cache_size || - !test_bits(address_offset >> XC_PAGE_SHIFT, - test_bit_size >> XC_PAGE_SHIFT, - entry->valid_mapping))) { - if (!free_entry && !entry->lock) { - free_entry = entry; - free_pentry = pentry; - } - pentry = entry; - entry = entry->next; - } - if (!entry && free_entry) { - entry = free_entry; - pentry = free_pentry; - } - if (!entry) { - entry = g_new0(MapCacheEntry, 1); - pentry->next = entry; - xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); - } else if (!entry->lock) { - if (!entry->vaddr_base || entry->paddr_index != address_index || - entry->size != cache_size || - !test_bits(address_offset >> XC_PAGE_SHIFT, - test_bit_size >> XC_PAGE_SHIFT, - entry->valid_mapping)) { - xen_remap_bucket(entry, NULL, cache_size, address_index, dummy); - } - } - - if(!test_bits(address_offset >> XC_PAGE_SHIFT, - test_bit_size >> XC_PAGE_SHIFT, - entry->valid_mapping)) { - mapcache->last_entry = NULL; -#ifdef XEN_COMPAT_PHYSMAP - if (!translated && mapcache->phys_offset_to_gaddr) { - phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size); - translated = true; - goto tryagain; - } -#endif - if (!dummy && runstate_check(RUN_STATE_INMIGRATE)) { - dummy = true; - goto tryagain; - } - trace_xen_map_cache_return(NULL); - return NULL; - } - - mapcache->last_entry = entry; - if (lock) { - 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); - abort(); - } - reventry->dma = dma; - reventry->vaddr_req = mapcache->last_entry->vaddr_base + address_offset; - reventry->paddr_index = mapcache->last_entry->paddr_index; - reventry->size = entry->size; - QTAILQ_INSERT_HEAD(&mapcache->locked_entries, reventry, next); - } - - trace_xen_map_cache_return(mapcache->last_entry->vaddr_base + address_offset); - return mapcache->last_entry->vaddr_base + address_offset; -} - -uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, - uint8_t lock, bool dma) -{ - uint8_t *p; - - mapcache_lock(); - p = xen_map_cache_unlocked(phys_addr, size, lock, dma); - mapcache_unlock(); - return p; -} - -ram_addr_t xen_ram_addr_from_mapcache(void *ptr) -{ - MapCacheEntry *entry = NULL; - MapCacheRev *reventry; - hwaddr paddr_index; - hwaddr size; - ram_addr_t raddr; - int found = 0; - - mapcache_lock(); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - if (reventry->vaddr_req == ptr) { - paddr_index = reventry->paddr_index; - size = reventry->size; - found = 1; - break; - } - } - if (!found) { - fprintf(stderr, "%s, could not find %p\n", __func__, ptr); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, - reventry->vaddr_req); - } - abort(); - return 0; - } - - entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; - while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { - entry = entry->next; - } - if (!entry) { - DPRINTF("Trying to find address %p that is not in the mapcache!\n", ptr); - raddr = 0; - } else { - raddr = (reventry->paddr_index << MCACHE_BUCKET_SHIFT) + - ((unsigned long) ptr - (unsigned long) entry->vaddr_base); - } - mapcache_unlock(); - return raddr; -} - -static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) -{ - MapCacheEntry *entry = NULL, *pentry = NULL; - MapCacheRev *reventry; - hwaddr paddr_index; - hwaddr size; - int found = 0; - - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - if (reventry->vaddr_req == buffer) { - paddr_index = reventry->paddr_index; - size = reventry->size; - found = 1; - break; - } - } - if (!found) { - DPRINTF("%s, could not find %p\n", __func__, buffer); - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - DPRINTF(" "TARGET_FMT_plx" -> %p is present\n", reventry->paddr_index, reventry->vaddr_req); - } - return; - } - QTAILQ_REMOVE(&mapcache->locked_entries, reventry, next); - g_free(reventry); - - if (mapcache->last_entry != NULL && - mapcache->last_entry->paddr_index == paddr_index) { - mapcache->last_entry = NULL; - } - - entry = &mapcache->entry[paddr_index % mapcache->nr_buckets]; - while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { - pentry = entry; - entry = entry->next; - } - if (!entry) { - DPRINTF("Trying to unmap address %p that is not in the mapcache!\n", buffer); - return; - } - entry->lock--; - if (entry->lock > 0 || pentry == NULL) { - return; - } - - pentry->next = entry->next; - ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); - if (munmap(entry->vaddr_base, entry->size) != 0) { - perror("unmap fails"); - exit(-1); - } - g_free(entry->valid_mapping); - g_free(entry); -} - -void xen_invalidate_map_cache_entry(uint8_t *buffer) -{ - mapcache_lock(); - xen_invalidate_map_cache_entry_unlocked(buffer); - mapcache_unlock(); -} - -void xen_invalidate_map_cache(void) -{ - unsigned long i; - MapCacheRev *reventry; - - /* Flush pending AIO before destroying the mapcache */ - bdrv_drain_all(); - - mapcache_lock(); - - QTAILQ_FOREACH(reventry, &mapcache->locked_entries, next) { - 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); - } - - for (i = 0; i < mapcache->nr_buckets; i++) { - MapCacheEntry *entry = &mapcache->entry[i]; - - if (entry->vaddr_base == NULL) { - continue; - } - if (entry->lock > 0) { - continue; - } - - if (munmap(entry->vaddr_base, entry->size) != 0) { - perror("unmap fails"); - exit(-1); - } - - entry->paddr_index = 0; - entry->vaddr_base = NULL; - entry->size = 0; - g_free(entry->valid_mapping); - entry->valid_mapping = NULL; - } - - mapcache->last_entry = NULL; - - mapcache_unlock(); -} - -static uint8_t *xen_replace_cache_entry_unlocked(hwaddr old_phys_addr, - hwaddr new_phys_addr, - hwaddr size) -{ - MapCacheEntry *entry; - hwaddr address_index, address_offset; - hwaddr test_bit_size, cache_size = size; - - address_index = old_phys_addr >> MCACHE_BUCKET_SHIFT; - address_offset = old_phys_addr & (MCACHE_BUCKET_SIZE - 1); - - assert(size); - /* test_bit_size is always a multiple of XC_PAGE_SIZE */ - test_bit_size = size + (old_phys_addr & (XC_PAGE_SIZE - 1)); - if (test_bit_size % XC_PAGE_SIZE) { - test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); - } - cache_size = size + address_offset; - if (cache_size % MCACHE_BUCKET_SIZE) { - cache_size += MCACHE_BUCKET_SIZE - (cache_size % MCACHE_BUCKET_SIZE); - } - - entry = &mapcache->entry[address_index % mapcache->nr_buckets]; - while (entry && !(entry->paddr_index == address_index && - entry->size == cache_size)) { - 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); - 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); - - 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); - return NULL; - } - - return entry->vaddr_base + address_offset; -} - -uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, - hwaddr new_phys_addr, - hwaddr size) -{ - uint8_t *p; - - mapcache_lock(); - p = xen_replace_cache_entry_unlocked(old_phys_addr, new_phys_addr, size); - mapcache_unlock(); - return p; -} diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c new file mode 100644 index 0000000000..f1f02d3311 --- /dev/null +++ b/hw/i386/xen/xen-pvh.c @@ -0,0 +1,124 @@ +/* + * QEMU Xen PVH x86 Machine + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * Written by Edgar E. Iglesias + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "hw/xen/arch_hvm.h" +#include +#include "hw/xen/xen-pvh-common.h" + +#define TYPE_XEN_PVH_X86 MACHINE_TYPE_NAME("xenpvh") +OBJECT_DECLARE_SIMPLE_TYPE(XenPVHx86State, XEN_PVH_X86) + +struct XenPVHx86State { + /*< private >*/ + XenPVHMachineState parent; + + DeviceState **cpu; +}; + +static DeviceState *xen_pvh_cpu_new(MachineState *ms, + int64_t apic_id) +{ + Object *cpu = object_new(ms->cpu_type); + + object_property_add_child(OBJECT(ms), "cpu[*]", cpu); + object_property_set_uint(cpu, "apic-id", apic_id, &error_fatal); + qdev_realize(DEVICE(cpu), NULL, &error_fatal); + object_unref(cpu); + + return DEVICE(cpu); +} + +static void xen_pvh_init(MachineState *ms) +{ + XenPVHx86State *xp = XEN_PVH_X86(ms); + int i; + + /* Create dummy cores. This will indirectly create the APIC MSI window. */ + xp->cpu = g_malloc(sizeof xp->cpu[0] * ms->smp.max_cpus); + for (i = 0; i < ms->smp.max_cpus; i++) { + xp->cpu[i] = xen_pvh_cpu_new(ms, i); + } +} + +static void xen_pvh_instance_init(Object *obj) +{ + XenPVHMachineState *s = XEN_PVH_MACHINE(obj); + + /* Default values. */ + s->cfg.ram_low = (MemMapEntry) { 0x0, 0x80000000U }; + s->cfg.ram_high = (MemMapEntry) { 0xC000000000ULL, 0x4000000000ULL }; + s->cfg.pci_intx_irq_base = 16; +} + +/* + * Deliver INTX interrupts to Xen guest. + */ +static void xen_pvh_set_pci_intx_irq(void *opaque, int irq, int level) +{ + /* + * Since QEMU emulates all of the swizziling + * We don't want Xen to do any additional swizzling in + * xen_set_pci_intx_level() so we always set device to 0. + */ + if (xen_set_pci_intx_level(xen_domid, 0, 0, 0, irq, level)) { + error_report("xendevicemodel_set_pci_intx_level failed"); + } +} + +static void xen_pvh_machine_class_init(ObjectClass *oc, void *data) +{ + XenPVHMachineClass *xpc = XEN_PVH_MACHINE_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Xen PVH x86 machine"; + mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; + + /* mc->max_cpus holds the MAX value allowed in the -smp cmd-line opts. */ + mc->max_cpus = HVM_MAX_VCPUS; + + /* We have an implementation specific init to create CPU objects. */ + xpc->init = xen_pvh_init; + + /* Enable buffered IOREQs. */ + xpc->handle_bufioreq = HVM_IOREQSRV_BUFIOREQ_ATOMIC; + + /* + * PCI INTX routing. + * + * We describe the mapping between the 4 INTX interrupt and GSIs + * using xen_set_pci_link_route(). xen_pvh_set_pci_intx_irq is + * used to deliver the interrupt. + */ + xpc->set_pci_intx_irq = xen_pvh_set_pci_intx_irq; + xpc->set_pci_link_route = xen_set_pci_link_route; + + /* List of supported features known to work on PVH x86. */ + xpc->has_pci = true; + + xen_pvh_class_setup_common_props(xpc); +} + +static const TypeInfo xen_pvh_x86_machine_type = { + .name = TYPE_XEN_PVH_X86, + .parent = TYPE_XEN_PVH_MACHINE, + .class_init = xen_pvh_machine_class_init, + .instance_init = xen_pvh_instance_init, + .instance_size = sizeof(XenPVHx86State), +}; + +static void xen_pvh_machine_register_types(void) +{ + type_register_static(&xen_pvh_x86_machine_type); +} + +type_init(xen_pvh_machine_register_types) 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..ec0e536e85 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; } @@ -542,7 +595,7 @@ static void xen_platform_class_init(ObjectClass *klass, void *data) k->revision = 1; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "XEN platform pci device"; - dc->reset = platform_reset; + device_class_set_legacy_reset(dc, platform_reset); dc->vmsd = &vmstate_xen_platform; } 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..2e22b677da 100644 --- a/hw/ide/Kconfig +++ b/hw/ide/Kconfig @@ -1,51 +1,52 @@ 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 - depends on PCMCIA config AHCI bool - select IDE_QDEV + select IDE_BUS + select IDE_DEV config AHCI_ICH9 bool @@ -56,4 +57,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 new file mode 100644 index 0000000000..7e63ea2310 --- /dev/null +++ b/hw/ide/ahci-internal.h @@ -0,0 +1,386 @@ +/* + * QEMU AHCI Emulation + * + * Copyright (c) 2010 qiaochong@loongson.cn + * Copyright (c) 2010 Roland Elek + * Copyright (c) 2010 Sebastian Herbszt + * Copyright (c) 2010 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 . + * + */ + +#ifndef HW_IDE_AHCI_INTERNAL_H +#define HW_IDE_AHCI_INTERNAL_H + +#include "hw/ide/ahci.h" +#include "hw/pci/pci_device.h" +#include "ide-internal.h" + +#define AHCI_MEM_BAR_SIZE 0x1000 +#define AHCI_MAX_PORTS 32 +#define AHCI_MAX_SG 168 /* hardware max is 64K */ +#define AHCI_DMA_BOUNDARY 0xffffffff +#define AHCI_USE_CLUSTERING 0 +#define AHCI_MAX_CMDS 32 +#define AHCI_CMD_SZ 32 +#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ) +#define AHCI_RX_FIS_SZ 256 +#define AHCI_CMD_TBL_CDB 0x40 +#define AHCI_CMD_TBL_HDR_SZ 0x80 +#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16)) +#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS) +#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \ + AHCI_RX_FIS_SZ) + +#define AHCI_IRQ_ON_SG (1U << 31) +#define AHCI_CMD_ATAPI (1 << 5) +#define AHCI_CMD_WRITE (1 << 6) +#define AHCI_CMD_PREFETCH (1 << 7) +#define AHCI_CMD_RESET (1 << 8) +#define AHCI_CMD_CLR_BUSY (1 << 10) + +#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ +#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ +#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ + +/* global controller registers */ +enum AHCIHostReg { + AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */ + 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 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: firmware/os handoff ctrl & status */ + AHCI_HOST_REG__COUNT = 11 +}; + +/* HOST_CTL bits */ +#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ +#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ +#define HOST_CTL_AHCI_EN (1U << 31) /* AHCI enabled */ + +/* HOST_CAP bits */ +#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ +#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ +#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ +#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ +#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ +#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ + +/* registers for each SATA port */ +enum AHCIPortReg { + AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */ + AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */ + AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */ + AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */ + AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */ + AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */ + AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */ + /* RESERVED */ + AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */ + AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */ + AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */ + AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */ + AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */ + AHCI_PORT_REG_SCR_ACT = 13, /* PxSACT: SATA phy register: SActive */ + AHCI_PORT_REG_CMD_ISSUE = 14, /* PxCI: command issue */ + AHCI_PORT_REG_SCR_NOTIF = 15, /* PxSNTF: SATA phy register: SNotification */ + AHCI_PORT_REG_FIS_CTL = 16, /* PxFBS: Port multiplier switching ctl */ + AHCI_PORT_REG_DEV_SLEEP = 17, /* PxDEVSLP: device sleep control */ + /* RESERVED */ + AHCI_PORT_REG_VENDOR_1 = 28, /* PxVS: Vendor Specific */ + AHCI_PORT_REG_VENDOR_2 = 29, + AHCI_PORT_REG_VENDOR_3 = 30, + AHCI_PORT_REG_VENDOR_4 = 31, + AHCI_PORT_REG__COUNT = 32 +}; + +/* Port interrupt bit descriptors */ +enum AHCIPortIRQ { + AHCI_PORT_IRQ_BIT_DHRS = 0, + AHCI_PORT_IRQ_BIT_PSS = 1, + AHCI_PORT_IRQ_BIT_DSS = 2, + AHCI_PORT_IRQ_BIT_SDBS = 3, + AHCI_PORT_IRQ_BIT_UFS = 4, + AHCI_PORT_IRQ_BIT_DPS = 5, + AHCI_PORT_IRQ_BIT_PCS = 6, + AHCI_PORT_IRQ_BIT_DMPS = 7, + /* RESERVED */ + AHCI_PORT_IRQ_BIT_PRCS = 22, + AHCI_PORT_IRQ_BIT_IPMS = 23, + AHCI_PORT_IRQ_BIT_OFS = 24, + /* RESERVED */ + AHCI_PORT_IRQ_BIT_INFS = 26, + AHCI_PORT_IRQ_BIT_IFS = 27, + AHCI_PORT_IRQ_BIT_HBDS = 28, + AHCI_PORT_IRQ_BIT_HBFS = 29, + AHCI_PORT_IRQ_BIT_TFES = 30, + AHCI_PORT_IRQ_BIT_CPDS = 31, + AHCI_PORT_IRQ__COUNT = 32 +}; + + +/* PORT_IRQ_{STAT,MASK} bits */ +#define PORT_IRQ_COLD_PRES (1U << 31) /* cold presence detect */ +#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ +#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ +#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ +#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ +#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ + /* reserved */ +#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ +#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ +#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ + /* reserved */ +#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ +#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ +#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ +#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ +#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ +#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ +#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ +#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ + +#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ + PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ + PORT_IRQ_UNK_FIS) +#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ + PORT_IRQ_HBUS_DATA_ERR) +#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) + +/* PORT_CMD bits */ +#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ +#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ +#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ +#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ +#define PORT_CMD_CLO (1 << 3) /* Command list override */ +#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ +#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ +#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ + +#define PORT_CMD_ICC_MASK (0xfU << 28) /* i/f ICC state mask */ +#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ +#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ +#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define PORT_CMD_RO_MASK 0x007dffe0 /* Which CMD bits are read only? */ + +/* ap->flags bits */ +#define AHCI_FLAG_NO_NCQ (1 << 24) +#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */ +#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */ +#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */ +#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */ + +#define ATA_SRST (1 << 2) /* software reset */ + +#define STATE_RUN 0 +#define STATE_RESET 1 + +#define SATA_SCR_SSTATUS_DET_NODEV 0x0 +#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3 + +#define SATA_SCR_SSTATUS_SPD_NODEV 0x00 +#define SATA_SCR_SSTATUS_SPD_GEN1 0x10 + +#define SATA_SCR_SSTATUS_IPM_NODEV 0x000 +#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100 + +#define AHCI_SCR_SCTL_DET 0xf + +#define SATA_FIS_TYPE_REGISTER_H2D 0x27 +#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80 +#define SATA_FIS_TYPE_REGISTER_D2H 0x34 +#define SATA_FIS_TYPE_PIO_SETUP 0x5f +#define SATA_FIS_TYPE_SDB 0xA1 + +#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f +#define AHCI_CMD_HDR_PRDT_LEN 16 + +#define SATA_SIGNATURE_CDROM 0xeb140101 +#define SATA_SIGNATURE_DISK 0x00000101 + +#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c + +#define AHCI_PORT_REGS_START_ADDR 0x100 +#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f +#define AHCI_PORT_ADDR_OFFSET_LEN 0x80 + +#define AHCI_NUM_COMMAND_SLOTS 31 +#define AHCI_SUPPORTED_SPEED 20 +#define AHCI_SUPPORTED_SPEED_GEN1 1 +#define AHCI_VERSION_1_0 0x10000 + +#define AHCI_PROGMODE_MAJOR_REV_1 1 + +#define AHCI_COMMAND_TABLE_ACMD 0x40 + +#define AHCI_PRDT_SIZE_MASK 0x3fffff + +#define IDE_FEATURE_DMA 1 + +#define READ_FPDMA_QUEUED 0x60 +#define WRITE_FPDMA_QUEUED 0x61 +#define NCQ_NON_DATA 0x63 +#define RECEIVE_FPDMA_QUEUED 0x65 +#define SEND_FPDMA_QUEUED 0x64 + +#define NCQ_FIS_FUA_MASK 0x80 +#define NCQ_FIS_RARC_MASK 0x01 + +#define RES_FIS_DSFIS 0x00 +#define RES_FIS_PSFIS 0x20 +#define RES_FIS_RFIS 0x40 +#define RES_FIS_SDBFIS 0x58 +#define RES_FIS_UFIS 0x60 + +#define SATA_CAP_SIZE 0x8 +#define SATA_CAP_REV 0x2 +#define SATA_CAP_BAR 0x4 + +typedef struct AHCIPortRegs { + uint32_t lst_addr; + uint32_t lst_addr_hi; + uint32_t fis_addr; + uint32_t fis_addr_hi; + uint32_t irq_stat; + uint32_t irq_mask; + uint32_t cmd; + uint32_t unused0; + uint32_t tfdata; + uint32_t sig; + uint32_t scr_stat; + uint32_t scr_ctl; + uint32_t scr_err; + uint32_t scr_act; + uint32_t cmd_issue; + uint32_t reserved; +} AHCIPortRegs; + +typedef struct AHCICmdHdr { + uint16_t opts; + uint16_t prdtl; + uint32_t status; + uint64_t tbl_addr; + uint32_t reserved[4]; +} QEMU_PACKED AHCICmdHdr; + +typedef struct AHCI_SG { + uint64_t addr; + uint32_t reserved; + uint32_t flags_size; +} QEMU_PACKED AHCI_SG; + +typedef struct NCQTransferState { + AHCIDevice *drive; + BlockAIOCB *aiocb; + AHCICmdHdr *cmdh; + QEMUSGList sglist; + BlockAcctCookie acct; + uint32_t sector_count; + uint64_t lba; + uint8_t tag; + uint8_t cmd; + uint8_t slot; + bool used; + bool halt; +} NCQTransferState; + +struct AHCIDevice { + IDEDMA dma; + IDEBus port; + int port_no; + uint32_t port_state; + uint32_t finished; + AHCIPortRegs port_regs; + struct AHCIState *hba; + QEMUBH *check_bh; + uint8_t *lst; + uint8_t *res_fis; + bool done_first_drq; + int32_t busy_slot; + bool init_d2h_sent; + AHCICmdHdr *cur_cmd; + NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; + MemReentrancyGuard mem_reentrancy_guard; +}; + +extern const VMStateDescription vmstate_ahci; + +#define VMSTATE_AHCI(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(AHCIState), \ + .vmsd = &vmstate_ahci, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, AHCIState), \ +} + +/** + * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2), + * but some fields have been re-mapped and re-purposed, as seen in + * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED") + * + * cmd_fis[3], feature 7:0, becomes sector count 7:0. + * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit. + * cmd_fis[11], feature 15:8, becomes sector count 15:8. + * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0) + * cmd_fis[13], count 15:8, becomes the priority value (7:6) + * bytes 16-19 become an le32 "auxiliary" field. + */ +typedef struct NCQFrame { + uint8_t fis_type; + uint8_t c; + uint8_t command; + uint8_t sector_count_low; /* (feature 7:0) */ + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t fua; /* (device 7:0) */ + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t sector_count_high; /* (feature 15:8) */ + uint8_t tag; /* (count 0:7) */ + uint8_t prio; /* (count 15:8) */ + uint8_t icc; + uint8_t control; + uint8_t aux0; + uint8_t aux1; + uint8_t aux2; + uint8_t aux3; +} QEMU_PACKED NCQFrame; + +typedef struct SDBFIS { + uint8_t type; + uint8_t flags; + uint8_t status; + uint8_t error; + uint32_t payload; +} QEMU_PACKED SDBFIS; + +void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as); +void ahci_init(AHCIState *s, DeviceState *qdev); +void ahci_uninit(AHCIState *s); + +void ahci_reset(AHCIState *s); + +#endif /* HW_IDE_AHCI_INTERNAL_H */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 7ce001cacd..0eb24304ee 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; } @@ -927,7 +948,6 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, uint64_t sum = 0; int off_idx = -1; int64_t off_pos = -1; - int tbl_entry_size; IDEBus *bus = &ad->port; BusState *qbus = BUS(bus); @@ -955,6 +975,8 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, /* Get entries in the PRDT, init a qemu sglist accordingly */ if (prdtl > 0) { AHCI_SG *tbl = (AHCI_SG *)prdt; + int tbl_entry_size = 0; + sum = 0; for (i = 0; i < prdtl; i++) { tbl_entry_size = prdt_tbl_entry_size(&tbl[i]); @@ -997,7 +1019,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 +1028,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 +1106,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 +1140,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 +1230,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 +1250,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 +1327,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 +1350,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 +1365,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 +1374,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 +1398,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 +1551,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 +1616,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 +1649,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 +1688,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 +1703,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 +1820,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 +1829,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 +1864,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(), }; @@ -1804,7 +1879,7 @@ static void sysbus_ahci_class_init(ObjectClass *klass, void *data) dc->realize = sysbus_ahci_realize; dc->vmsd = &vmstate_sysbus_ahci; device_class_set_props(dc, sysbus_ahci_properties); - dc->reset = sysbus_ahci_reset; + device_class_set_legacy_reset(dc, sysbus_ahci_reset); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1823,25 +1898,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/ahci_internal.h b/hw/ide/ahci_internal.h deleted file mode 100644 index 109de9e2d1..0000000000 --- a/hw/ide/ahci_internal.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * QEMU AHCI Emulation - * - * Copyright (c) 2010 qiaochong@loongson.cn - * Copyright (c) 2010 Roland Elek - * Copyright (c) 2010 Sebastian Herbszt - * Copyright (c) 2010 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 . - * - */ - -#ifndef HW_IDE_AHCI_INTERNAL_H -#define HW_IDE_AHCI_INTERNAL_H - -#include "hw/ide/ahci.h" -#include "hw/ide/internal.h" -#include "hw/pci/pci.h" - -#define AHCI_MEM_BAR_SIZE 0x1000 -#define AHCI_MAX_PORTS 32 -#define AHCI_MAX_SG 168 /* hardware max is 64K */ -#define AHCI_DMA_BOUNDARY 0xffffffff -#define AHCI_USE_CLUSTERING 0 -#define AHCI_MAX_CMDS 32 -#define AHCI_CMD_SZ 32 -#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ) -#define AHCI_RX_FIS_SZ 256 -#define AHCI_CMD_TBL_CDB 0x40 -#define AHCI_CMD_TBL_HDR_SZ 0x80 -#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16)) -#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS) -#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \ - AHCI_RX_FIS_SZ) - -#define AHCI_IRQ_ON_SG (1U << 31) -#define AHCI_CMD_ATAPI (1 << 5) -#define AHCI_CMD_WRITE (1 << 6) -#define AHCI_CMD_PREFETCH (1 << 7) -#define AHCI_CMD_RESET (1 << 8) -#define AHCI_CMD_CLR_BUSY (1 << 10) - -#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ -#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ -#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ - -/* global controller registers */ -enum AHCIHostReg { - AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */ - 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_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__COUNT = 11 -}; - -/* HOST_CTL bits */ -#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ -#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ -#define HOST_CTL_AHCI_EN (1U << 31) /* AHCI enabled */ - -/* HOST_CAP bits */ -#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ -#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ -#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ -#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ -#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ -#define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ - -/* registers for each SATA port */ -enum AHCIPortReg { - AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */ - AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */ - AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */ - AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */ - AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */ - AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */ - AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */ - /* RESERVED */ - AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */ - AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */ - AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */ - AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */ - AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */ - AHCI_PORT_REG_SCR_ACT = 13, /* PxSACT: SATA phy register: SActive */ - AHCI_PORT_REG_CMD_ISSUE = 14, /* PxCI: command issue */ - AHCI_PORT_REG_SCR_NOTIF = 15, /* PxSNTF: SATA phy register: SNotification */ - AHCI_PORT_REG_FIS_CTL = 16, /* PxFBS: Port multiplier switching ctl */ - AHCI_PORT_REG_DEV_SLEEP = 17, /* PxDEVSLP: device sleep control */ - /* RESERVED */ - AHCI_PORT_REG_VENDOR_1 = 28, /* PxVS: Vendor Specific */ - AHCI_PORT_REG_VENDOR_2 = 29, - AHCI_PORT_REG_VENDOR_3 = 30, - AHCI_PORT_REG_VENDOR_4 = 31, - AHCI_PORT_REG__COUNT = 32 -}; - -/* Port interrupt bit descriptors */ -enum AHCIPortIRQ { - AHCI_PORT_IRQ_BIT_DHRS = 0, - AHCI_PORT_IRQ_BIT_PSS = 1, - AHCI_PORT_IRQ_BIT_DSS = 2, - AHCI_PORT_IRQ_BIT_SDBS = 3, - AHCI_PORT_IRQ_BIT_UFS = 4, - AHCI_PORT_IRQ_BIT_DPS = 5, - AHCI_PORT_IRQ_BIT_PCS = 6, - AHCI_PORT_IRQ_BIT_DMPS = 7, - /* RESERVED */ - AHCI_PORT_IRQ_BIT_PRCS = 22, - AHCI_PORT_IRQ_BIT_IPMS = 23, - AHCI_PORT_IRQ_BIT_OFS = 24, - /* RESERVED */ - AHCI_PORT_IRQ_BIT_INFS = 26, - AHCI_PORT_IRQ_BIT_IFS = 27, - AHCI_PORT_IRQ_BIT_HBDS = 28, - AHCI_PORT_IRQ_BIT_HBFS = 29, - AHCI_PORT_IRQ_BIT_TFES = 30, - AHCI_PORT_IRQ_BIT_CPDS = 31, - AHCI_PORT_IRQ__COUNT = 32 -}; - - -/* PORT_IRQ_{STAT,MASK} bits */ -#define PORT_IRQ_COLD_PRES (1U << 31) /* cold presence detect */ -#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ -#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ -#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ -#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ -#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ - /* reserved */ -#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ -#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ -#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ - /* reserved */ -#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ -#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ -#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ -#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ -#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ -#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ -#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ -#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ - -#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ - PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ - PORT_IRQ_UNK_FIS) -#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ - PORT_IRQ_HBUS_DATA_ERR) -#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ - PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ - PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) - -/* PORT_CMD bits */ -#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ -#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ -#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ -#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ -#define PORT_CMD_CLO (1 << 3) /* Command list override */ -#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ -#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ -#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ - -#define PORT_CMD_ICC_MASK (0xfU << 28) /* i/f ICC state mask */ -#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ -#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ -#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ - -#define PORT_CMD_RO_MASK 0x007dffe0 /* Which CMD bits are read only? */ - -/* ap->flags bits */ -#define AHCI_FLAG_NO_NCQ (1 << 24) -#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */ -#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */ -#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */ -#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */ - -#define ATA_SRST (1 << 2) /* software reset */ - -#define STATE_RUN 0 -#define STATE_RESET 1 - -#define SATA_SCR_SSTATUS_DET_NODEV 0x0 -#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3 - -#define SATA_SCR_SSTATUS_SPD_NODEV 0x00 -#define SATA_SCR_SSTATUS_SPD_GEN1 0x10 - -#define SATA_SCR_SSTATUS_IPM_NODEV 0x000 -#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100 - -#define AHCI_SCR_SCTL_DET 0xf - -#define SATA_FIS_TYPE_REGISTER_H2D 0x27 -#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80 -#define SATA_FIS_TYPE_REGISTER_D2H 0x34 -#define SATA_FIS_TYPE_PIO_SETUP 0x5f -#define SATA_FIS_TYPE_SDB 0xA1 - -#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f -#define AHCI_CMD_HDR_PRDT_LEN 16 - -#define SATA_SIGNATURE_CDROM 0xeb140101 -#define SATA_SIGNATURE_DISK 0x00000101 - -#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c - -#define AHCI_PORT_REGS_START_ADDR 0x100 -#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f -#define AHCI_PORT_ADDR_OFFSET_LEN 0x80 - -#define AHCI_NUM_COMMAND_SLOTS 31 -#define AHCI_SUPPORTED_SPEED 20 -#define AHCI_SUPPORTED_SPEED_GEN1 1 -#define AHCI_VERSION_1_0 0x10000 - -#define AHCI_PROGMODE_MAJOR_REV_1 1 - -#define AHCI_COMMAND_TABLE_ACMD 0x40 - -#define AHCI_PRDT_SIZE_MASK 0x3fffff - -#define IDE_FEATURE_DMA 1 - -#define READ_FPDMA_QUEUED 0x60 -#define WRITE_FPDMA_QUEUED 0x61 -#define NCQ_NON_DATA 0x63 -#define RECEIVE_FPDMA_QUEUED 0x65 -#define SEND_FPDMA_QUEUED 0x64 - -#define NCQ_FIS_FUA_MASK 0x80 -#define NCQ_FIS_RARC_MASK 0x01 - -#define RES_FIS_DSFIS 0x00 -#define RES_FIS_PSFIS 0x20 -#define RES_FIS_RFIS 0x40 -#define RES_FIS_SDBFIS 0x58 -#define RES_FIS_UFIS 0x60 - -#define SATA_CAP_SIZE 0x8 -#define SATA_CAP_REV 0x2 -#define SATA_CAP_BAR 0x4 - -typedef struct AHCIPortRegs { - uint32_t lst_addr; - uint32_t lst_addr_hi; - uint32_t fis_addr; - uint32_t fis_addr_hi; - uint32_t irq_stat; - uint32_t irq_mask; - uint32_t cmd; - uint32_t unused0; - uint32_t tfdata; - uint32_t sig; - uint32_t scr_stat; - uint32_t scr_ctl; - uint32_t scr_err; - uint32_t scr_act; - uint32_t cmd_issue; - uint32_t reserved; -} AHCIPortRegs; - -typedef struct AHCICmdHdr { - uint16_t opts; - uint16_t prdtl; - uint32_t status; - uint64_t tbl_addr; - uint32_t reserved[4]; -} QEMU_PACKED AHCICmdHdr; - -typedef struct AHCI_SG { - uint64_t addr; - uint32_t reserved; - uint32_t flags_size; -} QEMU_PACKED AHCI_SG; - -typedef struct NCQTransferState { - AHCIDevice *drive; - BlockAIOCB *aiocb; - AHCICmdHdr *cmdh; - QEMUSGList sglist; - BlockAcctCookie acct; - uint32_t sector_count; - uint64_t lba; - uint8_t tag; - uint8_t cmd; - uint8_t slot; - bool used; - bool halt; -} NCQTransferState; - -struct AHCIDevice { - IDEDMA dma; - IDEBus port; - int port_no; - uint32_t port_state; - uint32_t finished; - AHCIPortRegs port_regs; - struct AHCIState *hba; - QEMUBH *check_bh; - uint8_t *lst; - uint8_t *res_fis; - bool done_first_drq; - int32_t busy_slot; - bool init_d2h_sent; - AHCICmdHdr *cur_cmd; - NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; -}; - -struct AHCIPCIState { - /*< private >*/ - PCIDevice parent_obj; - /*< public >*/ - - AHCIState ahci; -}; - -extern const VMStateDescription vmstate_ahci; - -#define VMSTATE_AHCI(_field, _state) { \ - .name = (stringify(_field)), \ - .size = sizeof(AHCIState), \ - .vmsd = &vmstate_ahci, \ - .flags = VMS_STRUCT, \ - .offset = vmstate_offset_value(_state, _field, AHCIState), \ -} - -/** - * NCQFrame is the same as a Register H2D FIS (described in SATA 3.2), - * but some fields have been re-mapped and re-purposed, as seen in - * SATA 3.2 section 13.6.4.1 ("READ FPDMA QUEUED") - * - * cmd_fis[3], feature 7:0, becomes sector count 7:0. - * cmd_fis[7], device 7:0, uses bit 7 as the Force Unit Access bit. - * cmd_fis[11], feature 15:8, becomes sector count 15:8. - * cmd_fis[12], count 7:0, becomes the NCQ TAG (7:3) and RARC bit (0) - * cmd_fis[13], count 15:8, becomes the priority value (7:6) - * bytes 16-19 become an le32 "auxiliary" field. - */ -typedef struct NCQFrame { - uint8_t fis_type; - uint8_t c; - uint8_t command; - uint8_t sector_count_low; /* (feature 7:0) */ - uint8_t lba0; - uint8_t lba1; - uint8_t lba2; - uint8_t fua; /* (device 7:0) */ - uint8_t lba3; - uint8_t lba4; - uint8_t lba5; - uint8_t sector_count_high; /* (feature 15:8) */ - uint8_t tag; /* (count 0:7) */ - uint8_t prio; /* (count 15:8) */ - uint8_t icc; - uint8_t control; - uint8_t aux0; - uint8_t aux1; - uint8_t aux2; - uint8_t aux3; -} QEMU_PACKED NCQFrame; - -typedef struct SDBFIS { - uint8_t type; - uint8_t flags; - uint8_t status; - uint8_t error; - uint32_t payload; -} QEMU_PACKED SDBFIS; - -void ahci_realize(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); -void ahci_init(AHCIState *s, DeviceState *qdev); -void ahci_uninit(AHCIState *s); - -void ahci_reset(AHCIState *s); - -#endif /* HW_IDE_AHCI_INTERNAL_H */ diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 0a9aa6f009..e82959dc2d 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -24,9 +24,11 @@ */ #include "qemu/osdep.h" -#include "hw/ide/internal.h" +#include "qemu/cutils.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) @@ -178,7 +180,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 +192,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 +255,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; @@ -263,7 +265,7 @@ void ide_atapi_cmd_reply_end(IDEState *s) byte_count_limit--; size = byte_count_limit; } - s->lcyl = size; + s->lcyl = size & 0xff; s->hcyl = size >> 8; s->elementary_transfer_size = size; /* we cannot transmit more than one sector at a time */ @@ -293,7 +295,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 */ @@ -339,7 +341,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 +385,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; } @@ -1308,14 +1310,9 @@ void ide_atapi_cmd(IDEState *s) trace_ide_atapi_cmd(s, s->io_buffer[0]); if (trace_event_get_state_backends(TRACE_IDE_ATAPI_CMD_PACKET)) { - /* Each pretty-printed byte needs two bytes and a space; */ - char *ppacket = g_malloc(ATAPI_PACKET_SIZE * 3 + 1); - int i; - for (i = 0; i < ATAPI_PACKET_SIZE; i++) { - sprintf(ppacket + (i * 3), "%02x ", buf[i]); - } - trace_ide_atapi_cmd_packet(s, s->lcyl | (s->hcyl << 8), ppacket); - g_free(ppacket); + g_autoptr(GString) str = + qemu_hexdump_line(NULL, buf, ATAPI_PACKET_SIZE, 1, 0); + trace_ide_atapi_cmd_packet(s, s->lcyl | (s->hcyl << 8), str->str); } /* 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..6b02fc81ec 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]); } } @@ -323,7 +323,7 @@ static void cmd646_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - dc->reset = cmd646_reset; + device_class_set_legacy_reset(dc, cmd646_reset); dc->vmsd = &vmstate_ide_pci; k->realize = pci_cmd646_ide_realize; k->exit = pci_cmd646_ide_exitfn; diff --git a/hw/ide/core.c b/hw/ide/core.c index be6a270a71..0160bbc7ba 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 @@ -81,6 +82,18 @@ static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval) static void ide_dummy_transfer_stop(IDEState *s); static void ide_security_cmd(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; @@ -325,52 +338,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; @@ -520,6 +533,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() */ @@ -527,7 +541,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; @@ -538,9 +553,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) @@ -661,7 +676,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) @@ -780,7 +795,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) @@ -844,7 +859,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) @@ -914,7 +929,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; } @@ -1014,7 +1029,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) @@ -1052,7 +1067,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 @@ -1062,7 +1077,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); } } @@ -1113,7 +1128,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) @@ -1138,13 +1153,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) @@ -1160,7 +1175,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)); @@ -1202,7 +1217,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) @@ -1213,7 +1228,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) @@ -1272,7 +1287,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); @@ -1334,7 +1349,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; } } @@ -1447,7 +1462,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) { @@ -1616,11 +1631,24 @@ static bool cmd_read_native_max(IDEState *s, uint8_t cmd) /* Refuse if no sectors are addressable (e.g. medium not inserted) */ if (s->nb_sectors == 0) { ide_abort_command(s); - return true; - } + } else { + /* + * Save the active drive parameters, which may have been + * limited from their native counterparts by, e.g., INITIALIZE + * DEVICE PARAMETERS or SET MAX ADDRESS. + */ + const int aheads = s->heads; + const int asectors = s->sectors; - ide_cmd_lba48_transform(s, lba48); - ide_set_sector(s, s->nb_sectors - 1); + s->heads = s->drive_heads; + s->sectors = s->drive_sectors; + + ide_cmd_lba48_transform(s, lba48); + ide_set_sector(s, s->nb_sectors - 1); + + s->heads = aheads; + s->sectors = asectors; + } return true; } @@ -1637,7 +1665,7 @@ 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_set_irq(s->bus); + ide_bus_set_irq(s->bus); } else { ide_abort_command(s); } @@ -1656,6 +1684,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; @@ -1696,7 +1731,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); @@ -1731,7 +1766,7 @@ 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; } @@ -1756,7 +1791,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; @@ -1797,7 +1832,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; } @@ -1836,7 +1871,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; } @@ -1860,7 +1895,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; } @@ -1943,7 +1978,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: @@ -1984,7 +2019,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: @@ -2023,7 +2058,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: @@ -2133,13 +2168,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) { @@ -2156,7 +2191,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; } @@ -2174,7 +2209,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); } } @@ -2205,7 +2240,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; @@ -2291,7 +2326,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) || @@ -2381,7 +2416,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); @@ -2393,12 +2428,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; @@ -2409,7 +2452,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; @@ -2420,12 +2463,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; @@ -2439,7 +2490,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); @@ -2467,7 +2518,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; @@ -2514,24 +2565,24 @@ static void ide_security_cmd(IDEState *s) s->error = 0; s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + ide_bus_set_irq(s->bus); } 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); @@ -2581,24 +2632,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 = s->drive_heads = heads; - s->sectors = s->drive_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; @@ -2606,26 +2654,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_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: @@ -2640,14 +2688,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; } @@ -2717,7 +2765,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 @@ -2765,7 +2813,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); @@ -2777,7 +2825,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; @@ -2785,10 +2833,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); @@ -2904,7 +2959,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() @@ -2916,7 +2971,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() @@ -2930,7 +2985,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), @@ -2948,7 +3003,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), @@ -2971,7 +3026,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, @@ -2984,7 +3039,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), @@ -2997,12 +3052,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..b311450c12 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); @@ -173,7 +176,7 @@ static void ich_ahci_class_init(ObjectClass *klass, void *data) k->revision = 0x02; k->class_id = PCI_CLASS_STORAGE_SATA; dc->vmsd = &vmstate_ich9_ahci; - dc->reset = pci_ich9_reset; + device_class_set_legacy_reset(dc, pci_ich9_reset); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } 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/ide-dev.c b/hw/ide/ide-dev.c new file mode 100644 index 0000000000..b2639b1e65 --- /dev/null +++ b/hw/ide/ide-dev.c @@ -0,0 +1,272 @@ +/* + * IDE device functions + * + * 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 "qapi/qapi-types-block.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "hw/ide/ide-dev.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "sysemu/sysemu.h" +#include "qapi/visitor.h" +#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_qdev_realize(DeviceState *qdev, Error **errp) +{ + IDEDevice *dev = IDE_DEVICE(qdev); + IDEDeviceClass *dc = IDE_DEVICE_GET_CLASS(dev); + IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus); + + if (dev->unit == -1) { + dev->unit = bus->master ? 1 : 0; + } + + if (dev->unit >= bus->max_units) { + error_setg(errp, "Can't create IDE unit %d, bus supports only %d units", + dev->unit, bus->max_units); + return; + } + + switch (dev->unit) { + case 0: + if (bus->master) { + error_setg(errp, "IDE unit %d is in use", dev->unit); + return; + } + bus->master = dev; + break; + case 1: + if (bus->slave) { + error_setg(errp, "IDE unit %d is in use", dev->unit); + return; + } + bus->slave = dev; + break; + default: + error_setg(errp, "Invalid IDE unit %d", dev->unit); + return; + } + dc->realize(dev, 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; + int ret; + + if (!dev->conf.blk) { + if (kind != IDE_CD) { + error_setg(errp, "No drive specified"); + return; + } else { + /* Anonymous BlockBackend for an empty drive */ + dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); + ret = blk_attach_dev(dev->conf.blk, &dev->qdev); + assert(ret == 0); + } + } + + if (dev->conf.discard_granularity == -1) { + dev->conf.discard_granularity = 512; + } else if (dev->conf.discard_granularity && + dev->conf.discard_granularity != 512) { + error_setg(errp, "discard_granularity must be 512 for ide"); + return; + } + + if (!blkconf_blocksizes(&dev->conf, errp)) { + return; + } + + if (dev->conf.logical_block_size != 512) { + error_setg(errp, "logical_block_size must be 512 for IDE"); + return; + } + + blkconf_locked(&dev->conf, &dev->locked); + + if (kind != IDE_CD) { + if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, + errp)) { + return; + } + } + if (!blkconf_apply_backend_options(&dev->conf, kind == IDE_CD, + kind != IDE_CD, errp)) { + return; + } + + if (ide_init_drive(s, dev, kind, errp) < 0) { + return; + } + + if (!dev->version) { + dev->version = g_strdup(s->version); + } + if (!dev->serial) { + dev->serial = g_strdup(s->drive_serial_str); + } + + add_boot_device_path(dev->conf.bootindex, &dev->qdev, + dev->unit ? "/disk@1" : "/disk@0"); + + add_boot_device_lchs(&dev->qdev, dev->unit ? "/disk@1" : "/disk@0", + dev->conf.lcyls, + dev->conf.lheads, + dev->conf.lsecs); +} + +static void ide_dev_get_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + IDEDevice *d = IDE_DEVICE(obj); + + visit_type_int32(v, name, &d->conf.bootindex, errp); +} + +static void ide_dev_set_bootindex(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + IDEDevice *d = IDE_DEVICE(obj); + int32_t boot_index; + Error *local_err = NULL; + + if (!visit_type_int32(v, name, &boot_index, errp)) { + return; + } + /* check whether bootindex is present in fw_boot_order list */ + check_boot_index(boot_index, &local_err); + if (local_err) { + goto out; + } + /* change bootindex to a new one */ + d->conf.bootindex = boot_index; + + if (d->unit != -1) { + add_boot_device_path(d->conf.bootindex, &d->qdev, + d->unit ? "/disk@1" : "/disk@0"); + } +out: + error_propagate(errp, local_err); +} + +static void ide_dev_instance_init(Object *obj) +{ + object_property_add(obj, "bootindex", "int32", + ide_dev_get_bootindex, + ide_dev_set_bootindex, NULL, NULL); + object_property_set_int(obj, "bootindex", -1, NULL); +} + +static void ide_hd_realize(IDEDevice *dev, Error **errp) +{ + ide_dev_initfn(dev, IDE_HD, errp); +} + +static void ide_cd_realize(IDEDevice *dev, Error **errp) +{ + ide_dev_initfn(dev, IDE_CD, errp); +} + +static Property ide_hd_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_UINT16("rotation_rate", IDEDrive, dev.rotation_rate, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ide_hd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); + + k->realize = ide_hd_realize; + dc->fw_name = "drive"; + dc->desc = "virtual IDE disk"; + device_class_set_props(dc, ide_hd_properties); +} + +static const TypeInfo ide_hd_info = { + .name = "ide-hd", + .parent = TYPE_IDE_DEVICE, + .instance_size = sizeof(IDEDrive), + .class_init = ide_hd_class_init, +}; + +static Property ide_cd_properties[] = { + DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ide_cd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); + + k->realize = ide_cd_realize; + dc->fw_name = "drive"; + dc->desc = "virtual IDE CD-ROM"; + device_class_set_props(dc, ide_cd_properties); +} + +static const TypeInfo ide_cd_info = { + .name = "ide-cd", + .parent = TYPE_IDE_DEVICE, + .instance_size = sizeof(IDEDrive), + .class_init = ide_cd_class_init, +}; + +static void ide_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->realize = ide_qdev_realize; + set_bit(DEVICE_CATEGORY_STORAGE, k->categories); + k->bus_type = TYPE_IDE_BUS; + device_class_set_props(k, ide_props); +} + +static const TypeInfo ide_device_type_info = { + .name = TYPE_IDE_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(IDEDevice), + .abstract = true, + .class_size = sizeof(IDEDeviceClass), + .class_init = ide_device_class_init, + .instance_init = ide_dev_instance_init, +}; + +static void ide_register_types(void) +{ + type_register_static(&ide_hd_info); + type_register_static(&ide_cd_info); + type_register_static(&ide_device_type_info); +} + +type_init(ide_register_types) diff --git a/hw/ide/ide-internal.h b/hw/ide/ide-internal.h new file mode 100644 index 0000000000..0d64805da2 --- /dev/null +++ b/hw/ide/ide-internal.h @@ -0,0 +1,448 @@ +#ifndef HW_IDE_INTERNAL_H +#define HW_IDE_INTERNAL_H + +/* + * QEMU IDE Emulation -- internal header file + * only files in hw/ide/ are supposed to include this file. + * non-internal declarations are in hw/include/ide-*.h + */ + +#include "hw/ide/ide-bus.h" + +/* debug IDE devices */ +#define USE_DMA_CDROM + +/* Device/Head ("select") Register */ +#define ATA_DEV_SELECT 0x10 +/* ATA1,3: Defined as '1'. + * ATA2: Reserved. + * ATA3-7: obsolete. */ +#define ATA_DEV_ALWAYS_ON 0xA0 +#define ATA_DEV_LBA 0x40 +#define ATA_DEV_LBA_MSB 0x0F /* LBA 24:27 */ +#define ATA_DEV_HS 0x0F /* HS 3:0 */ + + +/* 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 + +/* 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 */ + +/* Bits of HD_NSECTOR */ +#define CD 0x01 +#define IO 0x02 +#define REL 0x04 +#define TAG_MASK 0xf8 + +/* Bits of Device Control register */ +#define IDE_CTRL_HOB 0x80 +#define IDE_CTRL_RESET 0x04 +#define IDE_CTRL_DISABLE_IRQ 0x02 + +/* ACS-2 T13/2015-D Table B.2 Command codes */ +#define WIN_NOP 0x00 +/* reserved 0x01..0x02 */ +#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 +/* 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_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_READDMA_QUEUED_EXT 0x26 /* 48-Bit, obsolete since ACS2 */ +#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */ +/* reserved 0x28 */ +#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_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_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 */ +/* 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_ONCE 0x41 /* 28-Bit - w/o retries, obsolete since ATA5 */ +#define WIN_VERIFY_EXT 0x42 /* 48-Bit */ +/* reserved 0x43..0x44 */ +/* WRITE UNCORRECTABLE EXT 0x45 */ +/* reserved 0x46 */ +/* READ LOG DMA EXT 0x47 */ +/* reserved 0x48..0x4F */ +/* obsolete since ATA4 0x50 */ +/* CONFIGURE STREAM 0x51 */ +/* reserved 0x52..0x56 */ +/* WRITE LOG DMA EXT 0x57 */ +/* reserved 0x58..0x5A */ +/* TRUSTED NON DATA 0x5B */ +/* TRUSTED RECEIVE 0x5C */ +/* TRUSTED RECEIVE DMA 0x5D */ +/* TRUSTED SEND 0x5E */ +/* TRUSTED SEND DMA 0x5F */ +/* READ FPDMA QUEUED 0x60 */ +/* WRITE FPDMA QUEUED 0x61 */ +/* reserved 0x62->0x6F */ +#define WIN_SEEK 0x70 /* obsolete since ATA7 */ +/* reserved 0x71-0x7F */ +/* vendor specific 0x80-0x86 */ +#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */ +/* vendor specific 0x88-0x8F */ +#define WIN_DIAGNOSE 0x90 +#define WIN_SPECIFY 0x91 /* set drive geometry translation, obsolete since ATA6 */ +#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 */ +#define WIN_STANDBY2 0x96 /* retired in ATA4 */ +#define WIN_SETIDLE2 0x97 /* retired in ATA4 */ +#define WIN_CHECKPOWERMODE2 0x98 /* retired in ATA4 */ +#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_QUEUED_SERVICE 0xA2 /* obsolete since ACS2 */ +/* reserved 0xA3..0xAF */ +#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 +/* reserved 0xBC..0xBF */ +#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_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers, obsolete since ACS2 */ +#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_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 */ +/* WRITE MULTIPLE FUA EXT 0xCE */ +/* reserved 0xCF..0xDO */ +/* CHECK MEDIA CARD TYPE 0xD1 */ +/* reserved for media card pass through 0xD2..0xD4 */ +/* reserved 0xD5..0xD9 */ +#define WIN_GETMEDIASTATUS 0xDA /* obsolete since ATA8 */ +/* 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 */ +/* READ BUFFER DMA 0xE9 */ +#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */ +/* WRITE BUFFER DMA 0xEB */ +#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 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 CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP; not specified in T13! */ +#define WIN_SECURITY_DISABLE 0xF6 +/* vendor specific 0xF7 */ +#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 */ +#define MAX_MULT_SECTORS 16 + +#define IDE_DMA_BUF_SECTORS 256 + +/* feature values for Data Set Management */ +#define DSM_TRIM 0x01 + +#if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS) +#error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS" +#endif + +/* ATAPI defines */ + +#define ATAPI_PACKET_SIZE 12 + +/* 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_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_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 +/* 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 +/* This seems to be a SCSI specific CD-ROM opcode + * to play data at track/index */ +#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 ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */ +#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */ +#define ATAPI_INT_REASON_REL 0x04 +#define ATAPI_INT_REASON_TAG 0xf8 + +/* same constants as bochs */ +#define ASC_NO_SEEK_COMPLETE 0x02 +#define ASC_ILLEGAL_OPCODE 0x20 +#define ASC_LOGICAL_BLOCK_OOR 0x21 +#define ASC_INV_FIELD_IN_CMD_PACKET 0x24 +#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28 +#define ASC_INCOMPATIBLE_FORMAT 0x30 +#define ASC_MEDIUM_NOT_PRESENT 0x3a +#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 +#define ASC_DATA_PHASE_ERROR 0x4b +#define ASC_MEDIA_REMOVAL_PREVENTED 0x53 + +#define CFA_NO_ERROR 0x00 +#define CFA_MISC_ERROR 0x09 +#define CFA_INVALID_COMMAND 0x20 +#define CFA_INVALID_ADDRESS 0x21 +#define CFA_ADDRESS_OVERFLOW 0x2f + +#define SMART_READ_DATA 0xd0 +#define SMART_READ_THRESH 0xd1 +#define SMART_ATTR_AUTOSAVE 0xd2 +#define SMART_SAVE_ATTR 0xd3 +#define SMART_EXECUTE_OFFLINE 0xd4 +#define SMART_READ_LOG 0xd5 +#define SMART_WRITE_LOG 0xd6 +#define SMART_ENABLE 0xd8 +#define SMART_DISABLE 0xd9 +#define SMART_STATUS 0xda + +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) + +typedef struct IDEBufferedRequest { + QLIST_ENTRY(IDEBufferedRequest) list; + QEMUIOVector qiov; + QEMUIOVector *original_qiov; + BlockCompletionFunc *original_cb; + void *original_opaque; + bool orphaned; +} IDEBufferedRequest; + +/* These are used for the error_status field of IDEBus */ +#define IDE_RETRY_MASK 0xf8 +#define IDE_RETRY_DMA 0x08 +#define IDE_RETRY_PIO 0x10 +#define IDE_RETRY_ATAPI 0x20 /* reused IDE_RETRY_READ bit */ +#define IDE_RETRY_READ 0x20 +#define IDE_RETRY_FLUSH 0x40 +#define IDE_RETRY_TRIM 0x80 +#define IDE_RETRY_HBA 0x100 + +#define IS_IDE_RETRY_DMA(_status) \ + ((_status) & IDE_RETRY_DMA) + +#define IS_IDE_RETRY_PIO(_status) \ + ((_status) & IDE_RETRY_PIO) + +/* + * The method of the IDE_RETRY_ATAPI determination is to use a previously + * impossible bit combination as a new status value. + */ +#define IS_IDE_RETRY_ATAPI(_status) \ + (((_status) & IDE_RETRY_MASK) == IDE_RETRY_ATAPI) + +static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd) +{ + switch (dma_cmd) { + case IDE_DMA_READ: + return IDE_RETRY_DMA | IDE_RETRY_READ; + case IDE_DMA_WRITE: + return IDE_RETRY_DMA; + case IDE_DMA_TRIM: + return IDE_RETRY_DMA | IDE_RETRY_TRIM; + case IDE_DMA_ATAPI: + return IDE_RETRY_ATAPI; + default: + break; + } + return 0; +} + +static inline IDEState *ide_bus_active_if(IDEBus *bus) +{ + return bus->ifs + bus->unit; +} + +/* hw/ide/core.c */ +extern const VMStateDescription vmstate_ide_bus; + +#define VMSTATE_IDE_BUS(_field, _state) \ + VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_bus, IDEBus) + +#define VMSTATE_IDE_BUS_ARRAY(_field, _state, _num) \ + VMSTATE_STRUCT_ARRAY(_field, _state, _num, 1, vmstate_ide_bus, IDEBus) + +extern const VMStateDescription vmstate_ide_drive; + +#define VMSTATE_IDE_DRIVES(_field, _state) \ + VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState) + +#define VMSTATE_IDE_DRIVE(_field, _state) \ + VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) + +void ide_bus_reset(IDEBus *bus); +int64_t ide_get_sector(IDEState *s); +void ide_set_sector(IDEState *s, int64_t sector_num); + +void ide_start_dma(IDEState *s, BlockCompletionFunc *cb); +void dma_buf_commit(IDEState *s, uint32_t tx_bytes); +void ide_dma_error(IDEState *s); +void ide_abort_command(IDEState *s); + +void ide_atapi_cmd_ok(IDEState *s); +void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc); +void ide_atapi_dma_restart(IDEState *s); +void ide_atapi_io_error(IDEState *s, int ret); + +void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val); +uint32_t ide_ioport_read(void *opaque, uint32_t addr1); +uint32_t ide_status_read(void *opaque, uint32_t addr); +void ide_ctrl_write(void *opaque, uint32_t addr, uint32_t val); +void ide_data_writew(void *opaque, uint32_t addr, uint32_t val); +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, 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_bus_set_irq(IDEBus *bus); +void ide_bus_register_restart_cb(IDEBus *bus); + +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); +bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func); +void ide_transfer_stop(IDEState *s); +void ide_set_inactive(IDEState *s, bool more); +BlockAIOCB *ide_issue_trim( + int64_t offset, QEMUIOVector *qiov, + BlockCompletionFunc *cb, void *cb_opaque, void *opaque); +BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, + QEMUIOVector *iov, int nb_sectors, + BlockCompletionFunc *cb, void *opaque); +void ide_cancel_dma_sync(IDEState *s); + +/* hw/ide/atapi.c */ +void ide_atapi_cmd(IDEState *s); +void ide_atapi_cmd_reply_end(IDEState *s); + +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..211ebc9ba7 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(), }; @@ -118,7 +114,7 @@ static void isa_ide_class_initfn(ObjectClass *klass, void *data) dc->realize = isa_ide_realizefn; dc->fw_name = "ide"; - dc->reset = isa_ide_reset; + device_class_set_legacy_reset(dc, isa_ide_reset); device_class_set_props(dc, isa_ide_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/ide/macio.c b/hw/ide/macio.c index e604466acb..99477a3d13 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "hw/irq.h" #include "hw/ppc/mac_dbdma.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -32,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 @@ -59,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"); @@ -118,9 +119,6 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) return; done: - dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len, - io->dir, io->dma_len); - if (ret < 0) { block_acct_failed(blk_get_stats(s->blk), &s->acct); } else { @@ -135,7 +133,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"); @@ -159,7 +157,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; } @@ -201,9 +199,6 @@ static void pmac_ide_transfer_cb(void *opaque, int ret) return; done: - dma_memory_unmap(&address_space_memory, io->dma_mem, io->dma_len, - io->dir, io->dma_len); - if (s->dma_cmd == IDE_DMA_READ || s->dma_cmd == IDE_DMA_WRITE) { if (ret < 0) { block_acct_failed(blk_get_stats(s->blk), &s->acct); @@ -219,7 +214,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"); @@ -250,7 +245,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); @@ -360,7 +355,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), @@ -419,7 +414,8 @@ 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, + qdev_get_gpio_in(dev, MACIO_IDE_PMAC_IDE_IRQ)); /* Register DMA callbacks */ s->dma.ops = &dbdma_ops; @@ -455,8 +451,8 @@ static void macio_ide_initfn(Object *obj) sysbus_init_mmio(d, &s->mem); sysbus_init_irq(d, &s->real_ide_irq); sysbus_init_irq(d, &s->real_dma_irq); - s->dma_irq = qemu_allocate_irq(pmac_ide_irq, s, 0); - s->ide_irq = qemu_allocate_irq(pmac_ide_irq, s, 1); + + qdev_init_gpio_in(DEVICE(obj), pmac_ide_irq, MACIO_IDE_PMAC_NIRQS); object_property_add_link(obj, "dbdma", TYPE_MAC_DBDMA, (Object **) &s->dbdma, @@ -474,7 +470,7 @@ static void macio_ide_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = macio_ide_realizefn; - dc->reset = macio_ide_reset; + device_class_set_legacy_reset(dc, macio_ide_reset); device_class_set_props(dc, macio_ide_properties); dc->vmsd = &vmstate_pmac; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -500,14 +496,15 @@ 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]); } } } void macio_ide_register_dma(MACIOIDEState *s) { - DBDMA_register_channel(s->dbdma, s->channel, s->dma_irq, + DBDMA_register_channel(s->dbdma, s->channel, + qdev_get_gpio_in(DEVICE(s), MACIO_IDE_PMAC_DMA_IRQ), pmac_ide_transfer, pmac_ide_flush, s); } diff --git a/hw/ide/meson.build b/hw/ide/meson.build index ddcb3b28d2..90ea861423 100644 --- a/hw/ide/meson.build +++ b/hw/ide/meson.build @@ -1,14 +1,15 @@ -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')) diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c deleted file mode 100644 index 56c5be3655..0000000000 --- a/hw/ide/microdrive.c +++ /dev/null @@ -1,643 +0,0 @@ -/* - * QEMU IDE Emulation: microdrive (CF / PCMCIA) - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2006 Openedhand Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/pcmcia.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "sysemu/dma.h" - -#include "hw/ide/internal.h" -#include "qom/object.h" - -#define TYPE_MICRODRIVE "microdrive" -OBJECT_DECLARE_SIMPLE_TYPE(MicroDriveState, MICRODRIVE) - -/***********************************************************/ -/* CF-ATA Microdrive */ - -#define METADATA_SIZE 0x20 - -/* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */ - -struct MicroDriveState { - /*< private >*/ - PCMCIACardState parent_obj; - /*< public >*/ - - IDEBus bus; - uint32_t attr_base; - uint32_t io_base; - - /* Card state */ - uint8_t opt; - uint8_t stat; - uint8_t pins; - - uint8_t ctrl; - uint16_t io; - uint8_t cycle; -}; - -/* 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, -}; -enum md_cstat { - 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, -}; -enum md_ctrl { - CTRL_IEN = 0x02, - CTRL_SRST = 0x04, -}; - -static inline void md_interrupt_update(MicroDriveState *s) -{ - PCMCIACardState *card = PCMCIA_CARD(s); - - if (card->slot == NULL) { - return; - } - - qemu_set_irq(card->slot->irq, - !(s->stat & STAT_INT) && /* Inverted */ - !(s->ctrl & (CTRL_IEN | CTRL_SRST)) && - !(s->opt & OPT_SRESET)); -} - -static void md_set_irq(void *opaque, int irq, int level) -{ - MicroDriveState *s = opaque; - - if (level) { - s->stat |= STAT_INT; - } else { - s->stat &= ~STAT_INT; - } - - md_interrupt_update(s); -} - -static void md_reset(DeviceState *dev) -{ - MicroDriveState *s = MICRODRIVE(dev); - - s->opt = OPT_MODE_MMAP; - s->stat = 0; - s->pins = 0; - s->cycle = 0; - s->ctrl = 0; - ide_bus_reset(&s->bus); -} - -static uint8_t md_attr_read(PCMCIACardState *card, uint32_t at) -{ - MicroDriveState *s = MICRODRIVE(card); - PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card); - - if (at < s->attr_base) { - if (at < pcc->cis_len) { - return pcc->cis[at]; - } else { - return 0x00; - } - } - - at -= s->attr_base; - - switch (at) { - case 0x00: /* Configuration Option Register */ - return s->opt; - 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 */ - return (s->pins & PINS_CRDY) | 0x0c; - case 0x06: /* Socket and Copy Register */ - return 0x00; -#ifdef VERBOSE - default: - printf("%s: Bad attribute space register %02x\n", __func__, at); -#endif - } - - return 0; -} - -static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value) -{ - MicroDriveState *s = MICRODRIVE(card); - - at -= s->attr_base; - - switch (at) { - case 0x00: /* Configuration Option Register */ - s->opt = value & 0xcf; - if (value & OPT_SRESET) { - device_cold_reset(DEVICE(s)); - } - md_interrupt_update(s); - break; - case 0x02: /* Card Configuration Status Register */ - if ((s->stat ^ value) & STAT_PWRDWN) { - s->pins |= PINS_CRDY; - } - s->stat &= 0x82; - s->stat |= value & 0x74; - md_interrupt_update(s); - /* Word 170 in Identify Device must be equal to STAT_XE */ - break; - case 0x04: /* Pin Replacement Register */ - s->pins &= PINS_CRDY; - s->pins |= value & PINS_MRDY; - break; - case 0x06: /* Socket and Copy Register */ - break; - default: - printf("%s: Bad attribute space register %02x\n", __func__, at); - } -} - -static uint16_t md_common_read(PCMCIACardState *card, uint32_t at) -{ - MicroDriveState *s = MICRODRIVE(card); - IDEState *ifs; - uint16_t ret; - at -= s->io_base; - - switch (s->opt & OPT_MODE) { - case OPT_MODE_MMAP: - if ((at & ~0x3ff) == 0x400) { - at = 0; - } - break; - case OPT_MODE_IOMAP16: - at &= 0xf; - break; - case OPT_MODE_IOMAP1: - if ((at & ~0xf) == 0x3f0) { - at -= 0x3e8; - } else if ((at & ~0xf) == 0x1f0) { - at -= 0x1f0; - } - break; - case OPT_MODE_IOMAP2: - if ((at & ~0xf) == 0x370) { - at -= 0x368; - } else if ((at & ~0xf) == 0x170) { - at -= 0x170; - } - } - - switch (at) { - case 0x0: /* Even RD Data */ - case 0x8: - return ide_data_readw(&s->bus, 0); - - /* TODO: 8-bit accesses */ - if (s->cycle) { - ret = s->io >> 8; - } else { - s->io = ide_data_readw(&s->bus, 0); - ret = s->io & 0xff; - } - s->cycle = !s->cycle; - return ret; - case 0x9: /* Odd RD Data */ - return s->io >> 8; - case 0xd: /* Error */ - return ide_ioport_read(&s->bus, 0x1); - case 0xe: /* Alternate Status */ - ifs = idebus_active_if(&s->bus); - if (ifs->blk) { - return ifs->status; - } else { - return 0; - } - case 0xf: /* Device Address */ - ifs = idebus_active_if(&s->bus); - return 0xc2 | ((~ifs->select << 2) & 0x3c); - default: - return ide_ioport_read(&s->bus, at); - } - - return 0; -} - -static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value) -{ - MicroDriveState *s = MICRODRIVE(card); - at -= s->io_base; - - switch (s->opt & OPT_MODE) { - case OPT_MODE_MMAP: - if ((at & ~0x3ff) == 0x400) { - at = 0; - } - break; - case OPT_MODE_IOMAP16: - at &= 0xf; - break; - case OPT_MODE_IOMAP1: - if ((at & ~0xf) == 0x3f0) { - at -= 0x3e8; - } else if ((at & ~0xf) == 0x1f0) { - at -= 0x1f0; - } - break; - case OPT_MODE_IOMAP2: - if ((at & ~0xf) == 0x370) { - at -= 0x368; - } else if ((at & ~0xf) == 0x170) { - at -= 0x170; - } - } - - switch (at) { - case 0x0: /* Even WR Data */ - case 0x8: - ide_data_writew(&s->bus, 0, value); - break; - - /* TODO: 8-bit accesses */ - if (s->cycle) { - ide_data_writew(&s->bus, 0, s->io | (value << 8)); - } else { - s->io = value & 0xff; - } - s->cycle = !s->cycle; - break; - case 0x9: - s->io = value & 0xff; - s->cycle = !s->cycle; - break; - case 0xd: /* Features */ - ide_ioport_write(&s->bus, 0x1, value); - break; - case 0xe: /* Device Control */ - s->ctrl = value; - if (value & CTRL_SRST) { - device_cold_reset(DEVICE(s)); - } - md_interrupt_update(s); - break; - default: - if (s->stat & STAT_PWRDWN) { - s->pins |= PINS_CRDY; - s->stat &= ~STAT_PWRDWN; - } - ide_ioport_write(&s->bus, at, value); - } -} - -static const VMStateDescription vmstate_microdrive = { - .name = "microdrive", - .version_id = 3, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(opt, MicroDriveState), - VMSTATE_UINT8(stat, MicroDriveState), - VMSTATE_UINT8(pins, MicroDriveState), - VMSTATE_UINT8(ctrl, MicroDriveState), - VMSTATE_UINT16(io, MicroDriveState), - VMSTATE_UINT8(cycle, MicroDriveState), - VMSTATE_IDE_BUS(bus, MicroDriveState), - VMSTATE_IDE_DRIVES(bus.ifs, MicroDriveState), - VMSTATE_END_OF_LIST() - } -}; - -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 */ - [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 */ - [0x014] = CISTPL_ENDMARK, - - [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) */ - [0x024] = 0x00, - [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 */ - [0x032] = 'I', - [0x034] = 'B', - [0x036] = 'M', - [0x038] = 0x00, - [0x03a] = 'm', - [0x03c] = 'i', - [0x03e] = 'c', - [0x040] = 'r', - [0x042] = 'o', - [0x044] = 'd', - [0x046] = 'r', - [0x048] = 'i', - [0x04a] = 'v', - [0x04c] = 'e', - [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 */ - - [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 */ - - [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 */ - - [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 */ - [0x0a0] = 0x1e, - [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 */ - - [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 */ - - [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 */ - [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 */ - - [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 */ - - [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 */ - [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 */ - - [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 */ - - [0x142] = CISTPL_NO_LINK, /* No Link */ - [0x144] = 0x00, /* Tuple length = 0 bytes */ - - [0x146] = CISTPL_END, /* Tuple End */ -}; - -#define TYPE_DSCM1XXXX "dscm1xxxx" - -static int dscm1xxxx_attach(PCMCIACardState *card) -{ - MicroDriveState *md = MICRODRIVE(card); - PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card); - - md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8); - md->io_base = 0x0; - - device_cold_reset(DEVICE(md)); - md_interrupt_update(md); - - return 0; -} - -static int dscm1xxxx_detach(PCMCIACardState *card) -{ - MicroDriveState *md = MICRODRIVE(card); - - device_cold_reset(DEVICE(md)); - return 0; -} - -PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo) -{ - MicroDriveState *md; - - md = MICRODRIVE(object_new(TYPE_DSCM1XXXX)); - qdev_realize(DEVICE(md), NULL, &error_fatal); - - if (dinfo != NULL) { - ide_create_drive(&md->bus, 0, dinfo); - } - md->bus.ifs[0].drive_kind = IDE_CFATA; - md->bus.ifs[0].mdata_size = METADATA_SIZE; - md->bus.ifs[0].mdata_storage = g_malloc0(METADATA_SIZE); - - return PCMCIA_CARD(md); -} - -static void dscm1xxxx_class_init(ObjectClass *oc, void *data) -{ - PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc); - DeviceClass *dc = DEVICE_CLASS(oc); - - pcc->cis = dscm1xxxx_cis; - pcc->cis_len = sizeof(dscm1xxxx_cis); - - pcc->attach = dscm1xxxx_attach; - pcc->detach = dscm1xxxx_detach; - /* Reason: Needs to be wired-up in code, see dscm1xxxx_init() */ - dc->user_creatable = false; -} - -static const TypeInfo dscm1xxxx_type_info = { - .name = TYPE_DSCM1XXXX, - .parent = TYPE_MICRODRIVE, - .class_init = dscm1xxxx_class_init, -}; - -static void microdrive_realize(DeviceState *dev, Error **errp) -{ - MicroDriveState *md = MICRODRIVE(dev); - - ide_init2(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); -} - -static void microdrive_init(Object *obj) -{ - MicroDriveState *md = MICRODRIVE(obj); - - ide_bus_init(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1); -} - -static void microdrive_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc); - - pcc->attr_read = md_attr_read; - pcc->attr_write = md_attr_write; - pcc->common_read = md_common_read; - pcc->common_write = md_common_write; - pcc->io_read = md_common_read; - pcc->io_write = md_common_write; - - dc->realize = microdrive_realize; - dc->reset = md_reset; - dc->vmsd = &vmstate_microdrive; -} - -static const TypeInfo microdrive_type_info = { - .name = TYPE_MICRODRIVE, - .parent = TYPE_PCMCIA_CARD, - .instance_size = sizeof(MicroDriveState), - .instance_init = microdrive_init, - .abstract = true, - .class_init = microdrive_class_init, -}; - -static void microdrive_register_types(void) -{ - type_register_static(µdrive_type_info); - type_register_static(&dscm1xxxx_type_info); -} - -type_init(microdrive_register_types) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index fb2ebd4847..53d22fb37f 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() }; @@ -156,7 +151,7 @@ static void mmio_ide_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = mmio_ide_realizefn; - dc->reset = mmio_ide_reset; + device_class_set_legacy_reset(dc, mmio_ide_reset); device_class_set_props(dc, mmio_ide_properties); dc->vmsd = &vmstate_ide_mmio; } @@ -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..a008fe7316 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) { @@ -145,7 +237,7 @@ static int32_t bmdma_prepare_buf(const IDEDMA *dma, int32_t limit) /* end of table (with a fail safe of one page) */ if (bm->cur_prd_last || (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) { - return s->sg.size; + break; } pci_dma_read(pci_dev, bm->cur_addr, &prd, 8); bm->cur_addr += 8; @@ -174,10 +266,7 @@ static int32_t bmdma_prepare_buf(const IDEDMA *dma, int32_t limit) s->io_buffer_size += l; } } - - qemu_sglist_destroy(&s->sg); - s->io_buffer_size = 0; - return -1; + return s->sg.size; } /* return 0 if buffer completed */ @@ -295,7 +384,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 +400,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 +499,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 +513,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 +524,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 +533,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 +560,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 +583,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 +607,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 83f5d5b0c7..b702bbd0d7 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -28,16 +28,11 @@ */ #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) @@ -81,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; } } @@ -123,10 +118,10 @@ static void piix_ide_reset(DeviceState *dev) 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_byte(pci_conf + 0x20, 0x01); /* BMIBA: 20-23h */ + 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; @@ -136,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; + } } } @@ -192,7 +183,8 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - dc->reset = piix_ide_reset; + device_class_set_legacy_reset(dc, piix_ide_reset); + dc->vmsd = &vmstate_ide_pci; k->realize = pci_piix_ide_realize; k->exit = pci_piix_ide_exitfn; #ifdef XBOX @@ -220,7 +212,8 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - dc->reset = piix_ide_reset; + device_class_set_legacy_reset(dc, 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; diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c deleted file mode 100644 index 0dc0d83638..0000000000 --- a/hw/ide/qdev.c +++ /dev/null @@ -1,373 +0,0 @@ -/* - * 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 "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 "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); - -static Property ide_props[] = { - DEFINE_PROP_UINT32("unit", IDEDevice, unit, -1), - 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); - IDEDeviceClass *dc = IDE_DEVICE_GET_CLASS(dev); - IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus); - - if (dev->unit == -1) { - dev->unit = bus->master ? 1 : 0; - } - - if (dev->unit >= bus->max_units) { - error_setg(errp, "Can't create IDE unit %d, bus supports only %d units", - dev->unit, bus->max_units); - return; - } - - switch (dev->unit) { - case 0: - if (bus->master) { - error_setg(errp, "IDE unit %d is in use", dev->unit); - return; - } - bus->master = dev; - break; - case 1: - if (bus->slave) { - error_setg(errp, "IDE unit %d is in use", dev->unit); - return; - } - bus->slave = dev; - break; - default: - error_setg(errp, "Invalid IDE unit %d", dev->unit); - return; - } - 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) -{ - IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); - IDEState *s = bus->ifs + dev->unit; - int ret; - - if (!dev->conf.blk) { - if (kind != IDE_CD) { - error_setg(errp, "No drive specified"); - return; - } else { - /* Anonymous BlockBackend for an empty drive */ - dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); - ret = blk_attach_dev(dev->conf.blk, &dev->qdev); - assert(ret == 0); - } - } - - if (dev->conf.discard_granularity == -1) { - dev->conf.discard_granularity = 512; - } else if (dev->conf.discard_granularity && - dev->conf.discard_granularity != 512) { - error_setg(errp, "discard_granularity must be 512 for ide"); - return; - } - - if (!blkconf_blocksizes(&dev->conf, errp)) { - return; - } - - if (dev->conf.logical_block_size != 512) { - error_setg(errp, "logical_block_size must be 512 for IDE"); - return; - } - - blkconf_locked(&dev->conf, &dev->locked); - - if (kind != IDE_CD) { - if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, - errp)) { - return; - } - } - if (!blkconf_apply_backend_options(&dev->conf, kind == IDE_CD, - kind != IDE_CD, 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) { - return; - } - - if (!dev->version) { - dev->version = g_strdup(s->version); - } - if (!dev->serial) { - dev->serial = g_strdup(s->drive_serial_str); - } - - add_boot_device_path(dev->conf.bootindex, &dev->qdev, - dev->unit ? "/disk@1" : "/disk@0"); - - add_boot_device_lchs(&dev->qdev, dev->unit ? "/disk@1" : "/disk@0", - dev->conf.lcyls, - dev->conf.lheads, - dev->conf.lsecs); -} - -static void ide_dev_get_bootindex(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - IDEDevice *d = IDE_DEVICE(obj); - - visit_type_int32(v, name, &d->conf.bootindex, errp); -} - -static void ide_dev_set_bootindex(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - IDEDevice *d = IDE_DEVICE(obj); - int32_t boot_index; - Error *local_err = NULL; - - if (!visit_type_int32(v, name, &boot_index, errp)) { - return; - } - /* check whether bootindex is present in fw_boot_order list */ - check_boot_index(boot_index, &local_err); - if (local_err) { - goto out; - } - /* change bootindex to a new one */ - d->conf.bootindex = boot_index; - - if (d->unit != -1) { - add_boot_device_path(d->conf.bootindex, &d->qdev, - d->unit ? "/disk@1" : "/disk@0"); - } -out: - error_propagate(errp, local_err); -} - -static void ide_dev_instance_init(Object *obj) -{ - object_property_add(obj, "bootindex", "int32", - ide_dev_get_bootindex, - ide_dev_set_bootindex, NULL, NULL); - object_property_set_int(obj, "bootindex", -1, NULL); -} - -static void ide_hd_realize(IDEDevice *dev, Error **errp) -{ - ide_dev_initfn(dev, IDE_HD, errp); -} - -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), - DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", - IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), - DEFINE_PROP_UINT16("rotation_rate", IDEDrive, dev.rotation_rate, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ide_hd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); - - k->realize = ide_hd_realize; - dc->fw_name = "drive"; - dc->desc = "virtual IDE disk"; - device_class_set_props(dc, ide_hd_properties); -} - -static const TypeInfo ide_hd_info = { - .name = "ide-hd", - .parent = TYPE_IDE_DEVICE, - .instance_size = sizeof(IDEDrive), - .class_init = ide_hd_class_init, -}; - -static Property ide_cd_properties[] = { - DEFINE_IDE_DEV_PROPERTIES(), - DEFINE_PROP_END_OF_LIST(), -}; - -static void ide_cd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); - - k->realize = ide_cd_realize; - dc->fw_name = "drive"; - dc->desc = "virtual IDE CD-ROM"; - device_class_set_props(dc, ide_cd_properties); -} - -static const TypeInfo ide_cd_info = { - .name = "ide-cd", - .parent = TYPE_IDE_DEVICE, - .instance_size = sizeof(IDEDrive), - .class_init = ide_cd_class_init, -}; - -static void ide_device_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->realize = ide_qdev_realize; - set_bit(DEVICE_CATEGORY_STORAGE, k->categories); - k->bus_type = TYPE_IDE_BUS; - device_class_set_props(k, ide_props); -} - -static const TypeInfo ide_device_type_info = { - .name = TYPE_IDE_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(IDEDevice), - .abstract = true, - .class_size = sizeof(IDEDeviceClass), - .class_init = ide_device_class_init, - .instance_init = ide_dev_instance_init, -}; - -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); -} - -type_init(ide_register_types) diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 46204f10d7..ce8a1e4cba 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]); } } @@ -302,7 +300,7 @@ static void sii3112_pci_class_init(ObjectClass *klass, void *data) pd->class_id = PCI_CLASS_STORAGE_RAID; pd->revision = 1; pd->realize = sii3112_pci_realize; - dc->reset = sii3112_reset; + device_class_set_legacy_reset(dc, sii3112_reset); dc->desc = "SiI3112A SATA controller"; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } 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 e1a429405d..c88eb6c025 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); } @@ -215,11 +250,13 @@ static void via_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - dc->reset = via_ide_reset; + device_class_set_legacy_reset(dc, via_ide_reset); dc->vmsd = &vmstate_ide_pci; /* 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; diff --git a/hw/input/Kconfig b/hw/input/Kconfig index 55865bb386..a116cb82df 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -1,13 +1,6 @@ config ADB bool -config ADS7846 - bool - -config LM832X - bool - depends on I2C - config PCKBD bool select PS2 @@ -20,10 +13,7 @@ config PL050 config PS2 bool -config STELLARIS_INPUT - bool - -config TSC2005 +config STELLARIS_GAMEPAD bool config VIRTIO_INPUT @@ -41,8 +31,5 @@ config VHOST_USER_INPUT default y depends on VIRTIO_INPUT && VHOST_USER -config TSC210X - bool - config LASIPS2 select PS2 diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index a9088c910c..3649d03ef2 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, @@ -387,7 +387,7 @@ static void adb_kbd_class_init(ObjectClass *oc, void *data) adc->devreq = adb_kbd_request; adc->devhasdata = adb_kbd_has_data; - dc->reset = adb_kbd_reset; + device_class_set_legacy_reset(dc, adb_kbd_reset); dc->vmsd = &vmstate_adb_kbd; } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index e6b341f028..77b280d242 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -38,6 +38,7 @@ struct MouseState { ADBDevice parent_obj; /*< private >*/ + QemuInputHandlerState *hs; int buttons_state, last_buttons_state; int dx, dy, dz; }; @@ -51,17 +52,57 @@ struct ADBMouseClass { DeviceRealize parent_realize; }; -static void adb_mouse_event(void *opaque, - int dx1, int dy1, int dz1, int buttons_state) -{ - MouseState *s = opaque; +#define ADB_MOUSE_BUTTON_LEFT 0x01 +#define ADB_MOUSE_BUTTON_RIGHT 0x02 - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; +static void adb_mouse_handle_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + MouseState *s = (MouseState *)dev; + InputMoveEvent *move; + InputBtnEvent *btn; + static const int bmap[INPUT_BUTTON__MAX] = { + [INPUT_BUTTON_LEFT] = ADB_MOUSE_BUTTON_LEFT, + [INPUT_BUTTON_RIGHT] = ADB_MOUSE_BUTTON_RIGHT, + }; + + switch (evt->type) { + case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; + if (move->axis == INPUT_AXIS_X) { + s->dx += move->value; + } else if (move->axis == INPUT_AXIS_Y) { + s->dy += move->value; + } + break; + + case INPUT_EVENT_KIND_BTN: + btn = evt->u.btn.data; + if (bmap[btn->button]) { + if (btn->down) { + s->buttons_state |= bmap[btn->button]; + } else { + s->buttons_state &= ~bmap[btn->button]; + } + } + break; + + default: + /* keep gcc happy */ + break; + } } +static const QemuInputHandler adb_mouse_handler = { + .name = "QEMU ADB Mouse", + .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL, + .event = adb_mouse_handle_event, + /* + * We do not need the .sync handler because unlike e.g. PS/2 where async + * mouse events are sent over the serial port, an ADB mouse is constantly + * polled by the host via the adb_mouse_poll() callback. + */ +}; static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) { @@ -94,10 +135,10 @@ static int adb_mouse_poll(ADBDevice *d, uint8_t *obuf) dx &= 0x7f; dy &= 0x7f; - if (!(s->buttons_state & MOUSE_EVENT_LBUTTON)) { + if (!(s->buttons_state & ADB_MOUSE_BUTTON_LEFT)) { dy |= 0x80; } - if (!(s->buttons_state & MOUSE_EVENT_RBUTTON)) { + if (!(s->buttons_state & ADB_MOUSE_BUTTON_RIGHT)) { dx |= 0x80; } @@ -217,7 +258,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), @@ -236,7 +277,7 @@ static void adb_mouse_realizefn(DeviceState *dev, Error **errp) amc->parent_realize(dev, errp); - qemu_add_mouse_event_handler(adb_mouse_event, s, 0, "QEMU ADB Mouse"); + s->hs = qemu_input_handler_register(dev, &adb_mouse_handler); } static void adb_mouse_initfn(Object *obj) @@ -258,7 +299,7 @@ static void adb_mouse_class_init(ObjectClass *oc, void *data) adc->devreq = adb_mouse_request; adc->devhasdata = adb_mouse_has_data; - dc->reset = adb_mouse_reset; + device_class_set_legacy_reset(dc, adb_mouse_reset); dc->vmsd = &vmstate_adb_mouse; } diff --git a/hw/input/adb.c b/hw/input/adb.c index 84331b9fce..aff7130fd0 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, ResetType type) { - 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 deleted file mode 100644 index 1d4e04a2dc..0000000000 --- a/hw/input/ads7846.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * TI ADS7846 / TSC2046 chip emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - * - * 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/irq.h" -#include "hw/ssi/ssi.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "ui/console.h" -#include "qom/object.h" - -struct ADS7846State { - SSIPeripheral ssidev; - qemu_irq interrupt; - - int input[8]; - int pressure; - int noise; - - int cycle; - int output; -}; - -#define TYPE_ADS7846 "ads7846" -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 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)) - -static void ads7846_int_update(ADS7846State *s) -{ - if (s->interrupt) - qemu_set_irq(s->interrupt, s->pressure == 0); -} - -static uint32_t ads7846_transfer(SSIPeripheral *dev, uint32_t value) -{ - ADS7846State *s = ADS7846(dev); - - switch (s->cycle ++) { - case 0: - if (!(value & CB_START)) { - s->cycle = 0; - break; - } - - s->output = s->input[(value >> 4) & 7]; - - /* Imitate the ADC noise, some drivers expect this. */ - s->noise = (s->noise + 3) & 7; - switch ((value >> 4) & 7) { - case 1: s->output += s->noise ^ 2; break; - case 3: s->output += s->noise ^ 0; break; - case 4: s->output += s->noise ^ 7; break; - case 5: s->output += s->noise ^ 5; break; - } - - if (value & CB_MODE) - s->output >>= 4; /* 8 bits instead of 12 */ - - break; - case 1: - s->cycle = 0; - break; - } - return s->output; -} - -static void ads7846_ts_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - ADS7846State *s = opaque; - - if (buttons_state) { - x = 0x7fff - x; - s->input[1] = ADS_XPOS(x, y); - s->input[3] = ADS_Z1POS(x, y); - s->input[4] = ADS_Z2POS(x, y); - s->input[5] = ADS_YPOS(x, y); - } - - if (s->pressure == !buttons_state) { - s->pressure = !!buttons_state; - - ads7846_int_update(s); - } -} - -static int ads7856_post_load(void *opaque, int version_id) -{ - ADS7846State *s = opaque; - - s->pressure = 0; - ads7846_int_update(s); - return 0; -} - -static const VMStateDescription vmstate_ads7846 = { - .name = "ads7846", - .version_id = 1, - .minimum_version_id = 1, - .post_load = ads7856_post_load, - .fields = (VMStateField[]) { - VMSTATE_SSI_PERIPHERAL(ssidev, ADS7846State), - VMSTATE_INT32_ARRAY(input, ADS7846State, 8), - VMSTATE_INT32(noise, ADS7846State), - VMSTATE_INT32(cycle, ADS7846State), - VMSTATE_INT32(output, ADS7846State), - VMSTATE_END_OF_LIST() - } -}; - -static void ads7846_realize(SSIPeripheral *d, Error **errp) -{ - DeviceState *dev = DEVICE(d); - ADS7846State *s = ADS7846(d); - - 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 */ - - /* We want absolute coordinates */ - qemu_add_mouse_event_handler(ads7846_ts_event, s, 1, - "QEMU ADS7846-driven Touchscreen"); - - ads7846_int_update(s); - - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_ads7846, s); -} - -static void ads7846_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); - - k->realize = ads7846_realize; - k->transfer = ads7846_transfer; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo ads7846_info = { - .name = TYPE_ADS7846, - .parent = TYPE_SSI_PERIPHERAL, - .instance_size = sizeof(ADS7846State), - .class_init = ads7846_class_init, -}; - -static void ads7846_register_types(void) -{ - type_register_static(&ads7846_info); -} - -type_init(ads7846_register_types) 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 ea7c07a2ba..d9f8c36778 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -39,7 +39,7 @@ static const VMStateDescription vmstate_lasips2_port = { .name = "lasips2-port", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(control, LASIPS2Port), VMSTATE_UINT8(buf, LASIPS2Port), VMSTATE_BOOL(loopback_rbne, LASIPS2Port), @@ -51,7 +51,7 @@ static const VMStateDescription vmstate_lasips2 = { .name = "lasips2", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(int_status, LASIPS2State), VMSTATE_STRUCT(kbd_port.parent_obj, LASIPS2State, 1, vmstate_lasips2_port, LASIPS2Port), @@ -351,6 +351,11 @@ 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; } @@ -397,6 +402,11 @@ 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); } diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c deleted file mode 100644 index 19a646d9bb..0000000000 --- a/hw/input/lm832x.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/input/lm832x.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qemu/timer.h" -#include "ui/console.h" -#include "qom/object.h" - -OBJECT_DECLARE_SIMPLE_TYPE(LM823KbdState, LM8323) - -struct LM823KbdState { - I2CSlave parent_obj; - - uint8_t i2c_dir; - uint8_t i2c_cycle; - uint8_t reg; - - qemu_irq nirq; - uint16_t model; - - struct { - qemu_irq out[2]; - int in[2][2]; - } mux; - - uint8_t config; - uint8_t status; - uint8_t acttime; - uint8_t error; - uint8_t clock; - - struct { - uint16_t pull; - uint16_t mask; - uint16_t dir; - uint16_t level; - qemu_irq out[16]; - } gpio; - - struct { - uint8_t dbnctime; - uint8_t size; - uint8_t start; - uint8_t len; - uint8_t fifo[16]; - } kbd; - - struct { - uint16_t file[256]; - uint8_t faddr; - uint8_t addr[3]; - QEMUTimer *tm[3]; - } pwm; -}; - -#define INT_KEYPAD (1 << 0) -#define INT_ERROR (1 << 3) -#define INT_NOINIT (1 << 4) -#define INT_PWMEND(n) (1 << (5 + n)) - -#define ERR_BADPAR (1 << 0) -#define ERR_CMDUNK (1 << 1) -#define ERR_KEYOVR (1 << 2) -#define ERR_FIFOOVR (1 << 6) - -static void lm_kbd_irq_update(LM823KbdState *s) -{ - qemu_set_irq(s->nirq, !s->status); -} - -static void lm_kbd_gpio_update(LM823KbdState *s) -{ -} - -static void lm_kbd_reset(DeviceState *dev) -{ - LM823KbdState *s = LM8323(dev); - - s->config = 0x80; - s->status = INT_NOINIT; - s->acttime = 125; - s->kbd.dbnctime = 3; - s->kbd.size = 0x33; - s->clock = 0x08; - - lm_kbd_irq_update(s); - lm_kbd_gpio_update(s); -} - -static void lm_kbd_error(LM823KbdState *s, int err) -{ - s->error |= err; - s->status |= INT_ERROR; - lm_kbd_irq_update(s); -} - -static void lm_kbd_pwm_tick(LM823KbdState *s, int line) -{ -} - -static void lm_kbd_pwm_start(LM823KbdState *s, int line) -{ - lm_kbd_pwm_tick(s, line); -} - -static void lm_kbd_pwm0_tick(void *opaque) -{ - lm_kbd_pwm_tick(opaque, 0); -} -static void lm_kbd_pwm1_tick(void *opaque) -{ - lm_kbd_pwm_tick(opaque, 1); -} -static void lm_kbd_pwm2_tick(void *opaque) -{ - lm_kbd_pwm_tick(opaque, 2); -} - -enum { - LM832x_CMD_READ_ID = 0x80, /* Read chip ID. */ - LM832x_CMD_WRITE_CFG = 0x81, /* Set configuration item. */ - LM832x_CMD_READ_INT = 0x82, /* Get interrupt status. */ - LM832x_CMD_RESET = 0x83, /* Reset, same as external one */ - LM823x_CMD_WRITE_PULL_DOWN = 0x84, /* Select GPIO pull-up/down. */ - LM832x_CMD_WRITE_PORT_SEL = 0x85, /* Select GPIO in/out. */ - LM832x_CMD_WRITE_PORT_STATE = 0x86, /* Set GPIO pull-up/down. */ - LM832x_CMD_READ_PORT_SEL = 0x87, /* Get GPIO in/out. */ - LM832x_CMD_READ_PORT_STATE = 0x88, /* Get GPIO pull-up/down. */ - LM832x_CMD_READ_FIFO = 0x89, /* Read byte from FIFO. */ - LM832x_CMD_RPT_READ_FIFO = 0x8a, /* Read FIFO (no increment). */ - LM832x_CMD_SET_ACTIVE = 0x8b, /* Set active time. */ - LM832x_CMD_READ_ERROR = 0x8c, /* Get error status. */ - LM832x_CMD_READ_ROTATOR = 0x8e, /* Read rotator status. */ - LM832x_CMD_SET_DEBOUNCE = 0x8f, /* Set debouncing time. */ - LM832x_CMD_SET_KEY_SIZE = 0x90, /* Set keypad size. */ - LM832x_CMD_READ_KEY_SIZE = 0x91, /* Get keypad size. */ - LM832x_CMD_READ_CFG = 0x92, /* Get configuration item. */ - LM832x_CMD_WRITE_CLOCK = 0x93, /* Set clock config. */ - LM832x_CMD_READ_CLOCK = 0x94, /* Get clock config. */ - LM832x_CMD_PWM_WRITE = 0x95, /* Write PWM script. */ - LM832x_CMD_PWM_START = 0x96, /* Start PWM engine. */ - LM832x_CMD_PWM_STOP = 0x97, /* Stop PWM engine. */ - LM832x_GENERAL_ERROR = 0xff, /* There was one error. - Previously was represented by -1 - This is not a command */ -}; - -#define LM832x_MAX_KPX 8 -#define LM832x_MAX_KPY 12 - -static uint8_t lm_kbd_read(LM823KbdState *s, int reg, int byte) -{ - int ret; - - switch (reg) { - case LM832x_CMD_READ_ID: - ret = 0x0400; - break; - - case LM832x_CMD_READ_INT: - ret = s->status; - if (!(s->status & INT_NOINIT)) { - s->status = 0; - lm_kbd_irq_update(s); - } - break; - - case LM832x_CMD_READ_PORT_SEL: - ret = s->gpio.dir; - break; - case LM832x_CMD_READ_PORT_STATE: - ret = s->gpio.mask; - break; - - case LM832x_CMD_READ_FIFO: - if (s->kbd.len <= 1) - return 0x00; - - /* Example response from the two commands after a INT_KEYPAD - * interrupt caused by the key 0x3c being pressed: - * RPT_READ_FIFO: 55 bc 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 - * READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 - * RPT_READ_FIFO: bc 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 01 - * - * 55 is the code of the key release event serviced in the previous - * interrupt handling. - * - * TODO: find out whether the FIFO is advanced a single character - * before reading every byte or the whole size of the FIFO at the - * last LM832x_CMD_READ_FIFO. This affects LM832x_CMD_RPT_READ_FIFO - * output in cases where there are more than one event in the FIFO. - * Assume 0xbc and 0x3c events are in the FIFO: - * RPT_READ_FIFO: 55 bc 3c 00 4e ff 0a 50 08 00 29 d9 08 01 c9 - * READ_FIFO: bc 3c 00 00 4e ff 0a 50 08 00 29 d9 08 01 c9 - * Does RPT_READ_FIFO now return 0xbc and 0x3c or only 0x3c? - */ - s->kbd.start ++; - s->kbd.start &= sizeof(s->kbd.fifo) - 1; - s->kbd.len --; - - return s->kbd.fifo[s->kbd.start]; - case LM832x_CMD_RPT_READ_FIFO: - if (byte >= s->kbd.len) - return 0x00; - - return s->kbd.fifo[(s->kbd.start + byte) & (sizeof(s->kbd.fifo) - 1)]; - - case LM832x_CMD_READ_ERROR: - return s->error; - - case LM832x_CMD_READ_ROTATOR: - return 0; - - case LM832x_CMD_READ_KEY_SIZE: - return s->kbd.size; - - case LM832x_CMD_READ_CFG: - return s->config & 0xf; - - case LM832x_CMD_READ_CLOCK: - return (s->clock & 0xfc) | 2; - - default: - lm_kbd_error(s, ERR_CMDUNK); - fprintf(stderr, "%s: unknown command %02x\n", __func__, reg); - return 0x00; - } - - return ret >> (byte << 3); -} - -static void lm_kbd_write(LM823KbdState *s, int reg, int byte, uint8_t value) -{ - switch (reg) { - case LM832x_CMD_WRITE_CFG: - s->config = value; - /* This must be done whenever s->mux.in is updated (never). */ - if ((s->config >> 1) & 1) /* MUX1EN */ - qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 0) & 1]); - if ((s->config >> 3) & 1) /* MUX2EN */ - qemu_set_irq(s->mux.out[0], s->mux.in[0][(s->config >> 2) & 1]); - /* TODO: check that this is issued only following the chip reset - * and not in the middle of operation and that it is followed by - * the GPIO ports re-resablishing through WRITE_PORT_SEL and - * WRITE_PORT_STATE (using a timer perhaps) and otherwise output - * warnings. */ - s->status = 0; - lm_kbd_irq_update(s); - s->kbd.len = 0; - s->kbd.start = 0; - s->reg = LM832x_GENERAL_ERROR; - break; - - case LM832x_CMD_RESET: - if (value == 0xaa) - lm_kbd_reset(DEVICE(s)); - else - lm_kbd_error(s, ERR_BADPAR); - s->reg = LM832x_GENERAL_ERROR; - break; - - case LM823x_CMD_WRITE_PULL_DOWN: - if (!byte) - s->gpio.pull = value; - else { - s->gpio.pull |= value << 8; - lm_kbd_gpio_update(s); - s->reg = LM832x_GENERAL_ERROR; - } - break; - case LM832x_CMD_WRITE_PORT_SEL: - if (!byte) - s->gpio.dir = value; - else { - s->gpio.dir |= value << 8; - lm_kbd_gpio_update(s); - s->reg = LM832x_GENERAL_ERROR; - } - break; - case LM832x_CMD_WRITE_PORT_STATE: - if (!byte) - s->gpio.mask = value; - else { - s->gpio.mask |= value << 8; - lm_kbd_gpio_update(s); - s->reg = LM832x_GENERAL_ERROR; - } - break; - - case LM832x_CMD_SET_ACTIVE: - s->acttime = value; - s->reg = LM832x_GENERAL_ERROR; - break; - - case LM832x_CMD_SET_DEBOUNCE: - s->kbd.dbnctime = value; - s->reg = LM832x_GENERAL_ERROR; - if (!value) - lm_kbd_error(s, ERR_BADPAR); - break; - - case LM832x_CMD_SET_KEY_SIZE: - s->kbd.size = value; - s->reg = LM832x_GENERAL_ERROR; - if ( - (value & 0xf) < 3 || (value & 0xf) > LM832x_MAX_KPY || - (value >> 4) < 3 || (value >> 4) > LM832x_MAX_KPX) - lm_kbd_error(s, ERR_BADPAR); - break; - - case LM832x_CMD_WRITE_CLOCK: - s->clock = value; - s->reg = LM832x_GENERAL_ERROR; - if ((value & 3) && (value & 3) != 3) { - lm_kbd_error(s, ERR_BADPAR); - fprintf(stderr, "%s: invalid clock setting in RCPWM\n", - __func__); - } - /* TODO: Validate that the command is only issued once */ - break; - - case LM832x_CMD_PWM_WRITE: - if (byte == 0) { - if (!(value & 3) || (value >> 2) > 59) { - lm_kbd_error(s, ERR_BADPAR); - s->reg = LM832x_GENERAL_ERROR; - break; - } - - s->pwm.faddr = value; - s->pwm.file[s->pwm.faddr] = 0; - } else if (byte == 1) { - s->pwm.file[s->pwm.faddr] |= value << 8; - } else if (byte == 2) { - s->pwm.file[s->pwm.faddr] |= value << 0; - s->reg = LM832x_GENERAL_ERROR; - } - break; - case LM832x_CMD_PWM_START: - s->reg = LM832x_GENERAL_ERROR; - if (!(value & 3) || (value >> 2) > 59) { - lm_kbd_error(s, ERR_BADPAR); - break; - } - - s->pwm.addr[(value & 3) - 1] = value >> 2; - lm_kbd_pwm_start(s, (value & 3) - 1); - break; - case LM832x_CMD_PWM_STOP: - s->reg = LM832x_GENERAL_ERROR; - if (!(value & 3)) { - lm_kbd_error(s, ERR_BADPAR); - break; - } - - timer_del(s->pwm.tm[(value & 3) - 1]); - break; - - case LM832x_GENERAL_ERROR: - lm_kbd_error(s, ERR_BADPAR); - break; - default: - lm_kbd_error(s, ERR_CMDUNK); - fprintf(stderr, "%s: unknown command %02x\n", __func__, reg); - break; - } -} - -static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event) -{ - LM823KbdState *s = LM8323(i2c); - - switch (event) { - case I2C_START_RECV: - case I2C_START_SEND: - s->i2c_cycle = 0; - s->i2c_dir = (event == I2C_START_SEND); - break; - - default: - break; - } - - return 0; -} - -static uint8_t lm_i2c_rx(I2CSlave *i2c) -{ - LM823KbdState *s = LM8323(i2c); - - return lm_kbd_read(s, s->reg, s->i2c_cycle ++); -} - -static int lm_i2c_tx(I2CSlave *i2c, uint8_t data) -{ - LM823KbdState *s = LM8323(i2c); - - if (!s->i2c_cycle) - s->reg = data; - else - lm_kbd_write(s, s->reg, s->i2c_cycle - 1, data); - s->i2c_cycle ++; - - return 0; -} - -static int lm_kbd_post_load(void *opaque, int version_id) -{ - LM823KbdState *s = opaque; - - lm_kbd_irq_update(s); - lm_kbd_gpio_update(s); - - return 0; -} - -static const VMStateDescription vmstate_lm_kbd = { - .name = "LM8323", - .version_id = 0, - .minimum_version_id = 0, - .post_load = lm_kbd_post_load, - .fields = (VMStateField[]) { - VMSTATE_I2C_SLAVE(parent_obj, LM823KbdState), - VMSTATE_UINT8(i2c_dir, LM823KbdState), - VMSTATE_UINT8(i2c_cycle, LM823KbdState), - VMSTATE_UINT8(reg, LM823KbdState), - VMSTATE_UINT8(config, LM823KbdState), - VMSTATE_UINT8(status, LM823KbdState), - VMSTATE_UINT8(acttime, LM823KbdState), - VMSTATE_UINT8(error, LM823KbdState), - VMSTATE_UINT8(clock, LM823KbdState), - VMSTATE_UINT16(gpio.pull, LM823KbdState), - VMSTATE_UINT16(gpio.mask, LM823KbdState), - VMSTATE_UINT16(gpio.dir, LM823KbdState), - VMSTATE_UINT16(gpio.level, LM823KbdState), - VMSTATE_UINT8(kbd.dbnctime, LM823KbdState), - VMSTATE_UINT8(kbd.size, LM823KbdState), - VMSTATE_UINT8(kbd.start, LM823KbdState), - VMSTATE_UINT8(kbd.len, LM823KbdState), - VMSTATE_BUFFER(kbd.fifo, LM823KbdState), - VMSTATE_UINT16_ARRAY(pwm.file, LM823KbdState, 256), - VMSTATE_UINT8(pwm.faddr, LM823KbdState), - VMSTATE_BUFFER(pwm.addr, LM823KbdState), - VMSTATE_TIMER_PTR_ARRAY(pwm.tm, LM823KbdState, 3), - VMSTATE_END_OF_LIST() - } -}; - - -static void lm8323_realize(DeviceState *dev, Error **errp) -{ - LM823KbdState *s = LM8323(dev); - - s->model = 0x8323; - s->pwm.tm[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm0_tick, s); - s->pwm.tm[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm1_tick, s); - s->pwm.tm[2] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm2_tick, s); - qdev_init_gpio_out(dev, &s->nirq, 1); -} - -void lm832x_key_event(DeviceState *dev, int key, int state) -{ - LM823KbdState *s = LM8323(dev); - - if ((s->status & INT_ERROR) && (s->error & ERR_FIFOOVR)) - return; - - if (s->kbd.len >= sizeof(s->kbd.fifo)) { - lm_kbd_error(s, ERR_FIFOOVR); - return; - } - - s->kbd.fifo[(s->kbd.start + s->kbd.len ++) & (sizeof(s->kbd.fifo) - 1)] = - key | (state << 7); - - /* We never set ERR_KEYOVR because we support multiple keys fine. */ - s->status |= INT_KEYPAD; - lm_kbd_irq_update(s); -} - -static void lm8323_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - - dc->reset = lm_kbd_reset; - dc->realize = lm8323_realize; - k->event = lm_i2c_event; - k->recv = lm_i2c_rx; - k->send = lm_i2c_tx; - dc->vmsd = &vmstate_lm_kbd; -} - -static const TypeInfo lm8323_info = { - .name = TYPE_LM8323, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(LM823KbdState), - .class_init = lm8323_class_init, -}; - -static void lm832x_register_types(void) -{ - type_register_static(&lm8323_info); -} - -type_init(lm832x_register_types) diff --git a/hw/input/meson.build b/hw/input/meson.build index 8deb011d4a..90a214962c 100644 --- a/hw/input/meson.build +++ b/hw/input/meson.build @@ -1,18 +1,12 @@ -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_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')) -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_LASIPS2', if_true: files('lasips2.c')) diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index b92b63bedc..04c1b3cbf9 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -510,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() } @@ -552,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), @@ -619,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 @@ -745,7 +745,7 @@ static const VMStateDescription vmstate_kbd_mmio = { .name = "pckbd-mmio", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(kbd, MMIOKBDState, 0, vmstate_kbd, KBDState), VMSTATE_END_OF_LIST() } @@ -756,7 +756,7 @@ 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; + device_class_set_legacy_reset(dc, i8042_mmio_reset); dc->vmsd = &vmstate_kbd_mmio; device_class_set_props(dc, i8042_mmio_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); @@ -777,16 +777,11 @@ void i8042_isa_mouse_fake_event(ISAKBDState *isa) ps2_mouse_fake_event(&s->ps2mouse); } -void i8042_setup_a20_line(ISADevice *dev, qemu_irq a20_out) -{ - qdev_connect_gpio_out_named(DEVICE(dev), I8042_A20_LINE, 0, a20_out); -} - 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() } @@ -952,7 +947,7 @@ static void i8042_class_initfn(ObjectClass *klass, void *data) AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); device_class_set_props(dc, i8042_properties); - dc->reset = i8042_reset; + device_class_set_legacy_reset(dc, i8042_reset); dc->realize = i8042_realizefn; dc->vmsd = &vmstate_kbd_isa; adevc->build_dev_aml = i8042_build_aml; diff --git a/hw/input/pl050.c b/hw/input/pl050.c index ec5e19285e..6519e260ed 100644 --- a/hw/input/pl050.c +++ b/hw/input/pl050.c @@ -30,7 +30,7 @@ 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), diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 05cf7111e3..d6f834443d 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -402,6 +402,9 @@ 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) { keycode = qemu_input_map_qcode_to_atset1[qcode]; @@ -497,6 +500,9 @@ 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) { keycode = qemu_input_map_qcode_to_atset2[qcode]; @@ -1001,12 +1007,18 @@ void ps2_write_mouse(PS2MouseState *s, int val) } } -static void ps2_reset(DeviceState *dev) +static void ps2_reset_hold(Object *obj, ResetType type) { - PS2State *s = PS2_DEVICE(dev); + PS2State *s = PS2_DEVICE(obj); s->write_cmd = -1; ps2_reset_queue(s); +} + +static void ps2_reset_exit(Object *obj, ResetType type) +{ + PS2State *s = PS2_DEVICE(obj); + ps2_lower_irq(s); } @@ -1036,13 +1048,16 @@ static void ps2_common_post_load(PS2State *s) q->cwptr = ccount ? (q->rptr + ccount) & (PS2_BUFFER_SIZE - 1) : -1; } -static void ps2_kbd_reset(DeviceState *dev) +static void ps2_kbd_reset_hold(Object *obj, ResetType type) { - PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev); - PS2KbdState *s = PS2_KBD_DEVICE(dev); + PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); + PS2KbdState *s = PS2_KBD_DEVICE(obj); trace_ps2_kbd_reset(s); - ps2dc->parent_reset(dev); + + if (ps2dc->parent_phases.hold) { + ps2dc->parent_phases.hold(obj, type); + } s->scan_enabled = 1; s->translate = 0; @@ -1050,13 +1065,16 @@ static void ps2_kbd_reset(DeviceState *dev) s->modifiers = 0; } -static void ps2_mouse_reset(DeviceState *dev) +static void ps2_mouse_reset_hold(Object *obj, ResetType type) { - PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(dev); - PS2MouseState *s = PS2_MOUSE_DEVICE(dev); + PS2DeviceClass *ps2dc = PS2_DEVICE_GET_CLASS(obj); + PS2MouseState *s = PS2_MOUSE_DEVICE(obj); trace_ps2_mouse_reset(s); - ps2dc->parent_reset(dev); + + if (ps2dc->parent_phases.hold) { + ps2dc->parent_phases.hold(obj, type); + } s->mouse_status = 0; s->mouse_resolution = 0; @@ -1075,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), @@ -1106,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() } @@ -1123,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() } @@ -1140,7 +1158,7 @@ static bool ps2_keyboard_cqueue_needed(void *opaque) static const VMStateDescription vmstate_ps2_keyboard_cqueue = { .name = "ps2kbd/command_reply_queue", .needed = ps2_keyboard_cqueue_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_INT32(parent_obj.queue.cwptr, PS2KbdState), VMSTATE_END_OF_LIST() } @@ -1165,7 +1183,7 @@ static const VMStateDescription vmstate_ps2_keyboard = { .version_id = 3, .minimum_version_id = 2, .post_load = ps2_kbd_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, PS2KbdState, 0, vmstate_ps2_common, PS2State), VMSTATE_INT32(scan_enabled, PS2KbdState), @@ -1173,7 +1191,7 @@ static const VMStateDescription vmstate_ps2_keyboard = { 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, @@ -1196,7 +1214,7 @@ static const VMStateDescription vmstate_ps2_mouse = { .version_id = 2, .minimum_version_id = 2, .post_load = ps2_mouse_post_load, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT(parent_obj, PS2MouseState, 0, vmstate_ps2_common, PS2State), VMSTATE_UINT8(mouse_status, PS2MouseState), @@ -1213,7 +1231,7 @@ 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, @@ -1224,7 +1242,7 @@ static void ps2_kbd_realize(DeviceState *dev, Error **errp) 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, @@ -1239,10 +1257,12 @@ static void ps2_mouse_realize(DeviceState *dev, Error **errp) 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; - device_class_set_parent_reset(dc, ps2_kbd_reset, &ps2dc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, ps2_kbd_reset_hold, NULL, + &ps2dc->parent_phases); dc->vmsd = &vmstate_ps2_keyboard; } @@ -1256,11 +1276,12 @@ static const TypeInfo ps2_kbd_info = { 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; - device_class_set_parent_reset(dc, ps2_mouse_reset, - &ps2dc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, ps2_mouse_reset_hold, NULL, + &ps2dc->parent_phases); dc->vmsd = &vmstate_ps2_mouse; } @@ -1281,8 +1302,10 @@ static void ps2_init(Object *obj) static void ps2_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - dc->reset = ps2_reset; + rc->phases.hold = ps2_reset_hold; + rc->phases.exit = ps2_reset_exit; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } diff --git a/hw/input/pxa2xx_keypad.c b/hw/input/pxa2xx_keypad.c deleted file mode 100644 index 3dd03e8c9f..0000000000 --- a/hw/input/pxa2xx_keypad.c +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Intel PXA27X Keypad Controller emulation. - * - * Copyright (c) 2007 MontaVista Software, Inc - * Written by Armin Kuster - * or - * - * This code is licensed under the GPLv2. - * - * 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/log.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "hw/arm/pxa.h" -#include "ui/console.h" - -/* - * Keypad - */ -#define KPC 0x00 /* Keypad Interface Control register */ -#define KPDK 0x08 /* Keypad Interface Direct Key register */ -#define KPREC 0x10 /* Keypad Interface Rotary Encoder register */ -#define KPMK 0x18 /* Keypad Interface Matrix Key register */ -#define KPAS 0x20 /* Keypad Interface Automatic Scan register */ -#define KPASMKP0 0x28 /* Keypad Interface Automatic Scan Multiple - Key Presser register 0 */ -#define KPASMKP1 0x30 /* Keypad Interface Automatic Scan Multiple - Key Presser register 1 */ -#define KPASMKP2 0x38 /* Keypad Interface Automatic Scan Multiple - Key Presser register 2 */ -#define KPASMKP3 0x40 /* Keypad Interface Automatic Scan Multiple - Key Presser register 3 */ -#define KPKDI 0x48 /* Keypad Interface Key Debounce Interval - register */ - -/* Keypad defines */ -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ -#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ -#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ -#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ -#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ -#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ -#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ -#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ -#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */ -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ -#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */ -#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */ -#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */ -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ - -#define KPDK_DKP (0x1 << 31) -#define KPDK_DK7 (0x1 << 7) -#define KPDK_DK6 (0x1 << 6) -#define KPDK_DK5 (0x1 << 5) -#define KPDK_DK4 (0x1 << 4) -#define KPDK_DK3 (0x1 << 3) -#define KPDK_DK2 (0x1 << 2) -#define KPDK_DK1 (0x1 << 1) -#define KPDK_DK0 (0x1 << 0) - -#define KPREC_OF1 (0x1 << 31) -#define KPREC_UF1 (0x1 << 30) -#define KPREC_OF0 (0x1 << 15) -#define KPREC_UF0 (0x1 << 14) - -#define KPMK_MKP (0x1 << 31) -#define KPAS_SO (0x1 << 31) -#define KPASMKPx_SO (0x1 << 31) - - -#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) - -#define PXAKBD_MAXROW 8 -#define PXAKBD_MAXCOL 8 - -struct PXA2xxKeyPadState { - MemoryRegion iomem; - qemu_irq irq; - const struct keymap *map; - int pressed_cnt; - int alt_code; - - uint32_t kpc; - uint32_t kpdk; - uint32_t kprec; - uint32_t kpmk; - uint32_t kpas; - uint32_t kpasmkp[4]; - uint32_t kpkdi; -}; - -static void pxa27x_keypad_find_pressed_key(PXA2xxKeyPadState *kp, int *row, int *col) -{ - int i; - for (i = 0; i < 4; i++) - { - *col = i * 2; - for (*row = 0; *row < 8; (*row)++) { - if (kp->kpasmkp[i] & (1 << *row)) - return; - } - *col = i * 2 + 1; - for (*row = 0; *row < 8; (*row)++) { - if (kp->kpasmkp[i] & (1 << (*row + 16))) - return; - } - } -} - -static void pxa27x_keyboard_event (PXA2xxKeyPadState *kp, int keycode) -{ - int row, col, rel, assert_irq = 0; - uint32_t val; - - if (keycode == 0xe0) { - kp->alt_code = 1; - return; - } - - if(!(kp->kpc & KPC_ME)) /* skip if not enabled */ - return; - - rel = (keycode & 0x80) ? 1 : 0; /* key release from qemu */ - keycode &= ~0x80; /* strip qemu key release bit */ - if (kp->alt_code) { - keycode |= 0x80; - kp->alt_code = 0; - } - - row = kp->map[keycode].row; - col = kp->map[keycode].column; - if (row == -1 || col == -1) { - return; - } - - val = KPASMKPx_MKC(row, col); - if (rel) { - if (kp->kpasmkp[col / 2] & val) { - kp->kpasmkp[col / 2] &= ~val; - kp->pressed_cnt--; - assert_irq = 1; - } - } else { - if (!(kp->kpasmkp[col / 2] & val)) { - kp->kpasmkp[col / 2] |= val; - kp->pressed_cnt++; - assert_irq = 1; - } - } - kp->kpas = ((kp->pressed_cnt & 0x1f) << 26) | (0xf << 4) | 0xf; - if (kp->pressed_cnt == 1) { - kp->kpas &= ~((0xf << 4) | 0xf); - if (rel) { - pxa27x_keypad_find_pressed_key(kp, &row, &col); - } - kp->kpas |= ((row & 0xf) << 4) | (col & 0xf); - } - - if (!(kp->kpc & (KPC_AS | KPC_ASACT))) - assert_irq = 0; - - if (assert_irq && (kp->kpc & KPC_MIE)) { - kp->kpc |= KPC_MI; - qemu_irq_raise(kp->irq); - } -} - -static uint64_t pxa2xx_keypad_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - uint32_t tmp; - - switch (offset) { - case KPC: - tmp = s->kpc; - if(tmp & KPC_MI) - s->kpc &= ~(KPC_MI); - if(tmp & KPC_DI) - s->kpc &= ~(KPC_DI); - qemu_irq_lower(s->irq); - return tmp; - case KPDK: - return s->kpdk; - case KPREC: - tmp = s->kprec; - if(tmp & KPREC_OF1) - s->kprec &= ~(KPREC_OF1); - if(tmp & KPREC_UF1) - s->kprec &= ~(KPREC_UF1); - if(tmp & KPREC_OF0) - s->kprec &= ~(KPREC_OF0); - if(tmp & KPREC_UF0) - s->kprec &= ~(KPREC_UF0); - return tmp; - case KPMK: - tmp = s->kpmk; - if(tmp & KPMK_MKP) - s->kpmk &= ~(KPMK_MKP); - return tmp; - case KPAS: - return s->kpas; - case KPASMKP0: - return s->kpasmkp[0]; - case KPASMKP1: - return s->kpasmkp[1]; - case KPASMKP2: - return s->kpasmkp[2]; - case KPASMKP3: - return s->kpasmkp[3]; - case KPKDI: - return s->kpkdi; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad read offset 0x%"HWADDR_PRIx"\n", - __func__, offset); - } - - return 0; -} - -static void pxa2xx_keypad_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxKeyPadState *s = (PXA2xxKeyPadState *) opaque; - - switch (offset) { - case KPC: - s->kpc = value; - if (s->kpc & KPC_AS) { - s->kpc &= ~(KPC_AS); - } - break; - case KPDK: - s->kpdk = value; - break; - case KPREC: - s->kprec = value; - break; - case KPMK: - s->kpmk = value; - break; - case KPAS: - s->kpas = value; - break; - case KPASMKP0: - s->kpasmkp[0] = value; - break; - case KPASMKP1: - s->kpasmkp[1] = value; - break; - case KPASMKP2: - s->kpasmkp[2] = value; - break; - case KPASMKP3: - s->kpasmkp[3] = value; - break; - case KPKDI: - s->kpkdi = value; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Bad write offset 0x%"HWADDR_PRIx"\n", - __func__, offset); - } -} - -static const MemoryRegionOps pxa2xx_keypad_ops = { - .read = pxa2xx_keypad_read, - .write = pxa2xx_keypad_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static const VMStateDescription vmstate_pxa2xx_keypad = { - .name = "pxa2xx_keypad", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(kpc, PXA2xxKeyPadState), - VMSTATE_UINT32(kpdk, PXA2xxKeyPadState), - VMSTATE_UINT32(kprec, PXA2xxKeyPadState), - VMSTATE_UINT32(kpmk, PXA2xxKeyPadState), - VMSTATE_UINT32(kpas, PXA2xxKeyPadState), - VMSTATE_UINT32_ARRAY(kpasmkp, PXA2xxKeyPadState, 4), - VMSTATE_UINT32(kpkdi, PXA2xxKeyPadState), - VMSTATE_END_OF_LIST() - } -}; - -PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq) -{ - PXA2xxKeyPadState *s; - - s = g_new0(PXA2xxKeyPadState, 1); - s->irq = irq; - - memory_region_init_io(&s->iomem, NULL, &pxa2xx_keypad_ops, s, - "pxa2xx-keypad", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - vmstate_register(NULL, 0, &vmstate_pxa2xx_keypad, s); - - return s; -} - -void pxa27x_register_keypad(PXA2xxKeyPadState *kp, - const struct keymap *map, int size) -{ - if(!map || size < 0x80) { - fprintf(stderr, "%s - No PXA keypad map defined\n", __func__); - exit(-1); - } - - kp->map = map; - qemu_add_kbd_event_handler((QEMUPutKBDEvent *) pxa27x_keyboard_event, kp); -} 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 29001a827d..1484625565 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -46,9 +46,6 @@ ps2_mouse_reset(void *opaque) "%p" hid_kbd_queue_full(void) "queue full" hid_kbd_queue_empty(void) "queue empty" -# tsc2005.c -tsc2005_sense(const char *state) "touchscreen sense %s" - # virtio-input.c virtio_input_queue_full(void) "queue full" diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c deleted file mode 100644 index 14698ce109..0000000000 --- a/hw/input/tsc2005.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * TI TSC2005 emulator. - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (C) 2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "qemu/timer.h" -#include "sysemu/reset.h" -#include "ui/console.h" -#include "hw/input/tsc2xxx.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "trace.h" - -#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) - -typedef struct { - qemu_irq pint; /* Combination of the nPENIRQ and DAV signals */ - QEMUTimer *timer; - uint16_t model; - - int32_t x, y; - bool pressure; - - uint8_t reg, state; - bool irq, command; - uint16_t data, dav; - - bool busy; - bool enabled; - bool host_mode; - int8_t function; - int8_t nextfunction; - bool precision; - bool nextprecision; - uint16_t filter; - uint8_t pin_func; - uint16_t timing[2]; - uint8_t noise; - bool reset; - bool pdst; - bool pnd0; - uint16_t temp_thr[2]; - uint16_t aux_thr[2]; - - int32_t tr[8]; -} TSC2005State; - -enum { - TSC_MODE_XYZ_SCAN = 0x0, - TSC_MODE_XY_SCAN, - TSC_MODE_X, - TSC_MODE_Y, - TSC_MODE_Z, - TSC_MODE_AUX, - TSC_MODE_TEMP1, - TSC_MODE_TEMP2, - TSC_MODE_AUX_SCAN, - TSC_MODE_X_TEST, - TSC_MODE_Y_TEST, - TSC_MODE_TS_TEST, - TSC_MODE_RESERVED, - TSC_MODE_XX_DRV, - TSC_MODE_YY_DRV, - TSC_MODE_YX_DRV, -}; - -static const uint16_t mode_regs[16] = { - 0xf000, /* X, Y, Z scan */ - 0xc000, /* X, Y scan */ - 0x8000, /* X */ - 0x4000, /* Y */ - 0x3000, /* Z */ - 0x0800, /* AUX */ - 0x0400, /* TEMP1 */ - 0x0200, /* TEMP2 */ - 0x0800, /* AUX scan */ - 0x0040, /* X test */ - 0x0020, /* Y test */ - 0x0080, /* Short-circuit test */ - 0x0000, /* Reserved */ - 0x0000, /* X+, X- drivers */ - 0x0000, /* Y+, Y- drivers */ - 0x0000, /* Y+, X- drivers */ -}; - -#define X_TRANSFORM(s) \ - ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) -#define Y_TRANSFORM(s) \ - ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) -#define Z1_TRANSFORM(s) \ - ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) -#define Z2_TRANSFORM(s) \ - ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) - -#define AUX_VAL (700 << 4) /* +/- 3 at 12-bit */ -#define TEMP1_VAL (1264 << 4) /* +/- 5 at 12-bit */ -#define TEMP2_VAL (1531 << 4) /* +/- 5 at 12-bit */ - -static uint16_t tsc2005_read(TSC2005State *s, int reg) -{ - uint16_t ret; - - switch (reg) { - case 0x0: /* X */ - s->dav &= ~mode_regs[TSC_MODE_X]; - return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + - (s->noise & 3); - case 0x1: /* Y */ - s->dav &= ~mode_regs[TSC_MODE_Y]; - s->noise ++; - return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ - (s->noise & 3); - case 0x2: /* Z1 */ - s->dav &= 0xdfff; - return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - - (s->noise & 3); - case 0x3: /* Z2 */ - s->dav &= 0xefff; - return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | - (s->noise & 3); - - case 0x4: /* AUX */ - s->dav &= ~mode_regs[TSC_MODE_AUX]; - return TSC_CUT_RESOLUTION(AUX_VAL, s->precision); - - case 0x5: /* TEMP1 */ - s->dav &= ~mode_regs[TSC_MODE_TEMP1]; - return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - - (s->noise & 5); - case 0x6: /* TEMP2 */ - s->dav &= 0xdfff; - s->dav &= ~mode_regs[TSC_MODE_TEMP2]; - return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ - (s->noise & 3); - - case 0x7: /* Status */ - ret = s->dav | (s->reset << 7) | (s->pdst << 2) | 0x0; - s->dav &= ~(mode_regs[TSC_MODE_X_TEST] | mode_regs[TSC_MODE_Y_TEST] | - mode_regs[TSC_MODE_TS_TEST]); - s->reset = true; - return ret; - - case 0x8: /* AUX high treshold */ - return s->aux_thr[1]; - case 0x9: /* AUX low treshold */ - return s->aux_thr[0]; - - case 0xa: /* TEMP high treshold */ - return s->temp_thr[1]; - case 0xb: /* TEMP low treshold */ - return s->temp_thr[0]; - - case 0xc: /* CFR0 */ - return (s->pressure << 15) | ((!s->busy) << 14) | - (s->nextprecision << 13) | s->timing[0]; - case 0xd: /* CFR1 */ - return s->timing[1]; - case 0xe: /* CFR2 */ - return (s->pin_func << 14) | s->filter; - - case 0xf: /* Function select status */ - return s->function >= 0 ? 1 << s->function : 0; - } - - /* Never gets here */ - return 0xffff; -} - -static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) -{ - switch (reg) { - case 0x8: /* AUX high treshold */ - s->aux_thr[1] = data; - break; - case 0x9: /* AUX low treshold */ - s->aux_thr[0] = data; - break; - - case 0xa: /* TEMP high treshold */ - s->temp_thr[1] = data; - break; - case 0xb: /* TEMP low treshold */ - s->temp_thr[0] = data; - break; - - case 0xc: /* CFR0 */ - s->host_mode = (data >> 15) != 0; - if (s->enabled != !(data & 0x4000)) { - s->enabled = !(data & 0x4000); - trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); - if (s->busy && !s->enabled) - timer_del(s->timer); - s->busy = s->busy && s->enabled; - } - s->nextprecision = (data >> 13) & 1; - s->timing[0] = data & 0x1fff; - if ((s->timing[0] >> 11) == 3) { - qemu_log_mask(LOG_GUEST_ERROR, - "tsc2005_write: illegal conversion clock setting\n"); - } - break; - case 0xd: /* CFR1 */ - s->timing[1] = data & 0xf07; - break; - case 0xe: /* CFR2 */ - s->pin_func = (data >> 14) & 3; - s->filter = data & 0x3fff; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: write into read-only register 0x%x\n", - __func__, reg); - } -} - -/* This handles most of the chip's logic. */ -static void tsc2005_pin_update(TSC2005State *s) -{ - int64_t expires; - bool pin_state; - - switch (s->pin_func) { - case 0: - pin_state = !s->pressure && !!s->dav; - break; - case 1: - case 3: - default: - pin_state = !s->dav; - break; - case 2: - pin_state = !s->pressure; - } - - if (pin_state != s->irq) { - s->irq = pin_state; - qemu_set_irq(s->pint, s->irq); - } - - switch (s->nextfunction) { - case TSC_MODE_XYZ_SCAN: - case TSC_MODE_XY_SCAN: - if (!s->host_mode && s->dav) - s->enabled = false; - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_AUX_SCAN: - break; - - case TSC_MODE_X: - case TSC_MODE_Y: - case TSC_MODE_Z: - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_AUX: - case TSC_MODE_TEMP1: - case TSC_MODE_TEMP2: - case TSC_MODE_X_TEST: - case TSC_MODE_Y_TEST: - case TSC_MODE_TS_TEST: - if (s->dav) - s->enabled = false; - break; - - case TSC_MODE_RESERVED: - case TSC_MODE_XX_DRV: - case TSC_MODE_YY_DRV: - case TSC_MODE_YX_DRV: - default: - return; - } - - if (!s->enabled || s->busy) - return; - - s->busy = true; - s->precision = s->nextprecision; - s->function = s->nextfunction; - s->pdst = !s->pnd0; /* Synchronised on internal clock */ - expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND >> 7); - timer_mod(s->timer, expires); -} - -static void tsc2005_reset(TSC2005State *s) -{ - s->state = 0; - s->pin_func = 0; - s->enabled = false; - s->busy = false; - s->nextprecision = false; - s->nextfunction = 0; - s->timing[0] = 0; - s->timing[1] = 0; - s->irq = false; - s->dav = 0; - s->reset = false; - s->pdst = true; - s->pnd0 = false; - s->function = -1; - s->temp_thr[0] = 0x000; - s->temp_thr[1] = 0xfff; - s->aux_thr[0] = 0x000; - s->aux_thr[1] = 0xfff; - - tsc2005_pin_update(s); -} - -static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) -{ - TSC2005State *s = opaque; - uint32_t ret = 0; - - switch (s->state ++) { - case 0: - if (value & 0x80) { - /* Command */ - if (value & (1 << 1)) - tsc2005_reset(s); - else { - s->nextfunction = (value >> 3) & 0xf; - s->nextprecision = (value >> 2) & 1; - if (s->enabled != !(value & 1)) { - s->enabled = !(value & 1); - trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); - if (s->busy && !s->enabled) - timer_del(s->timer); - s->busy = s->busy && s->enabled; - } - tsc2005_pin_update(s); - } - - s->state = 0; - } else if (value) { - /* Data transfer */ - s->reg = (value >> 3) & 0xf; - s->pnd0 = (value >> 1) & 1; - s->command = value & 1; - - if (s->command) { - /* Read */ - s->data = tsc2005_read(s, s->reg); - tsc2005_pin_update(s); - } else - s->data = 0; - } else - s->state = 0; - break; - - case 1: - if (s->command) - ret = (s->data >> 8) & 0xff; - else - s->data |= value << 8; - break; - - case 2: - if (s->command) - ret = s->data & 0xff; - else { - s->data |= value; - tsc2005_write(s, s->reg, s->data); - tsc2005_pin_update(s); - } - - s->state = 0; - break; - } - - return ret; -} - -uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len) -{ - uint32_t ret = 0; - - len &= ~7; - while (len > 0) { - len -= 8; - ret |= tsc2005_txrx_word(opaque, (value >> len) & 0xff) << len; - } - - return ret; -} - -static void tsc2005_timer_tick(void *opaque) -{ - TSC2005State *s = opaque; - - /* Timer ticked -- a set of conversions has been finished. */ - - if (!s->busy) - return; - - s->busy = false; - s->dav |= mode_regs[s->function]; - s->function = -1; - tsc2005_pin_update(s); -} - -static void tsc2005_touchscreen_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - TSC2005State *s = opaque; - int p = s->pressure; - - if (buttons_state) { - s->x = x; - s->y = y; - } - s->pressure = !!buttons_state; - - /* - * Note: We would get better responsiveness in the guest by - * signaling TS events immediately, but for now we simulate - * the first conversion delay for sake of correctness. - */ - if (p != s->pressure) - tsc2005_pin_update(s); -} - -static int tsc2005_post_load(void *opaque, int version_id) -{ - TSC2005State *s = (TSC2005State *) opaque; - - s->busy = timer_pending(s->timer); - tsc2005_pin_update(s); - - return 0; -} - -static const VMStateDescription vmstate_tsc2005 = { - .name = "tsc2005", - .version_id = 2, - .minimum_version_id = 2, - .post_load = tsc2005_post_load, - .fields = (VMStateField []) { - VMSTATE_BOOL(pressure, TSC2005State), - VMSTATE_BOOL(irq, TSC2005State), - VMSTATE_BOOL(command, TSC2005State), - VMSTATE_BOOL(enabled, TSC2005State), - VMSTATE_BOOL(host_mode, TSC2005State), - VMSTATE_BOOL(reset, TSC2005State), - VMSTATE_BOOL(pdst, TSC2005State), - VMSTATE_BOOL(pnd0, TSC2005State), - VMSTATE_BOOL(precision, TSC2005State), - VMSTATE_BOOL(nextprecision, TSC2005State), - VMSTATE_UINT8(reg, TSC2005State), - VMSTATE_UINT8(state, TSC2005State), - VMSTATE_UINT16(data, TSC2005State), - VMSTATE_UINT16(dav, TSC2005State), - VMSTATE_UINT16(filter, TSC2005State), - VMSTATE_INT8(nextfunction, TSC2005State), - VMSTATE_INT8(function, TSC2005State), - VMSTATE_INT32(x, TSC2005State), - VMSTATE_INT32(y, TSC2005State), - VMSTATE_TIMER_PTR(timer, TSC2005State), - VMSTATE_UINT8(pin_func, TSC2005State), - VMSTATE_UINT16_ARRAY(timing, TSC2005State, 2), - VMSTATE_UINT8(noise, TSC2005State), - VMSTATE_UINT16_ARRAY(temp_thr, TSC2005State, 2), - VMSTATE_UINT16_ARRAY(aux_thr, TSC2005State, 2), - VMSTATE_INT32_ARRAY(tr, TSC2005State, 8), - VMSTATE_END_OF_LIST() - } -}; - -void *tsc2005_init(qemu_irq pintdav) -{ - TSC2005State *s; - - s = g_new0(TSC2005State, 1); - s->x = 400; - s->y = 240; - s->pressure = false; - s->precision = s->nextprecision = false; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc2005_timer_tick, s); - s->pint = pintdav; - s->model = 0x2005; - - 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; - - tsc2005_reset(s); - - qemu_add_mouse_event_handler(tsc2005_touchscreen_event, s, 1, - "QEMU TSC2005-driven Touchscreen"); - - qemu_register_reset((void *) tsc2005_reset, s); - vmstate_register(NULL, 0, &vmstate_tsc2005, s); - - return s; -} - -/* - * Use tslib generated calibration data to generate ADC input values - * from the touchscreen. Assuming 12-bit precision was used during - * tslib calibration. - */ -void tsc2005_set_transform(void *opaque, MouseTransformInfo *info) -{ - TSC2005State *s = (TSC2005State *) opaque; - - /* This version assumes touchscreen X & Y axis are parallel or - * perpendicular to LCD's X & Y axis in some way. */ - if (abs(info->a[0]) > abs(info->a[1])) { - s->tr[0] = 0; - s->tr[1] = -info->a[6] * info->x; - s->tr[2] = info->a[0]; - s->tr[3] = -info->a[2] / info->a[0]; - s->tr[4] = info->a[6] * info->y; - s->tr[5] = 0; - s->tr[6] = info->a[4]; - s->tr[7] = -info->a[5] / info->a[4]; - } else { - s->tr[0] = info->a[6] * info->y; - s->tr[1] = 0; - s->tr[2] = info->a[1]; - s->tr[3] = -info->a[2] / info->a[1]; - s->tr[4] = 0; - s->tr[5] = -info->a[6] * info->x; - s->tr[6] = info->a[3]; - s->tr[7] = -info->a[5] / info->a[3]; - } - - s->tr[0] >>= 11; - s->tr[1] >>= 11; - s->tr[3] <<= 4; - s->tr[4] >>= 11; - s->tr[5] >>= 11; - s->tr[7] <<= 4; -} diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c deleted file mode 100644 index df7313db5d..0000000000 --- a/hw/input/tsc210x.c +++ /dev/null @@ -1,1258 +0,0 @@ -/* - * TI TSC2102 (touchscreen/sensors/audio controller) emulator. - * TI TSC2301 (touchscreen/sensors/keypad). - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (C) 2008 Nokia Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/hw.h" -#include "audio/audio.h" -#include "qemu/timer.h" -#include "qemu/log.h" -#include "sysemu/reset.h" -#include "ui/console.h" -#include "hw/arm/omap.h" /* For I2SCodec */ -#include "hw/input/tsc2xxx.h" -#include "hw/irq.h" -#include "migration/vmstate.h" - -#define TSC_DATA_REGISTERS_PAGE 0x0 -#define TSC_CONTROL_REGISTERS_PAGE 0x1 -#define TSC_AUDIO_REGISTERS_PAGE 0x2 - -#define TSC_VERBOSE - -#define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - resolution[p])) - -typedef struct { - qemu_irq pint; - qemu_irq kbint; - qemu_irq davint; - QEMUTimer *timer; - QEMUSoundCard card; - uWireSlave chip; - I2SCodec codec; - uint8_t in_fifo[16384]; - uint8_t out_fifo[16384]; - uint16_t model; - - int32_t x, y; - bool pressure; - - uint8_t page, offset; - uint16_t dav; - - bool state; - bool irq; - bool command; - bool busy; - bool enabled; - bool host_mode; - uint8_t function, nextfunction; - uint8_t precision, nextprecision; - uint8_t filter; - uint8_t pin_func; - uint8_t ref; - uint8_t timing; - uint8_t noise; - - uint16_t audio_ctrl1; - uint16_t audio_ctrl2; - uint16_t audio_ctrl3; - uint16_t pll[3]; - uint16_t volume; - int64_t volume_change; - bool softstep; - uint16_t dac_power; - int64_t powerdown; - uint16_t filter_data[0x14]; - - const char *name; - SWVoiceIn *adc_voice[1]; - SWVoiceOut *dac_voice[1]; - int i2s_rx_rate; - int i2s_tx_rate; - - int tr[8]; - - struct { - uint16_t down; - uint16_t mask; - int scan; - int debounce; - int mode; - int intr; - } kb; - int64_t now; /* Time at migration */ -} TSC210xState; - -static const int resolution[4] = { 12, 8, 10, 12 }; - -#define TSC_MODE_NO_SCAN 0x0 -#define TSC_MODE_XY_SCAN 0x1 -#define TSC_MODE_XYZ_SCAN 0x2 -#define TSC_MODE_X 0x3 -#define TSC_MODE_Y 0x4 -#define TSC_MODE_Z 0x5 -#define TSC_MODE_BAT1 0x6 -#define TSC_MODE_BAT2 0x7 -#define TSC_MODE_AUX 0x8 -#define TSC_MODE_AUX_SCAN 0x9 -#define TSC_MODE_TEMP1 0xa -#define TSC_MODE_PORT_SCAN 0xb -#define TSC_MODE_TEMP2 0xc -#define TSC_MODE_XX_DRV 0xd -#define TSC_MODE_YY_DRV 0xe -#define TSC_MODE_YX_DRV 0xf - -static const uint16_t mode_regs[16] = { - 0x0000, /* No scan */ - 0x0600, /* X, Y scan */ - 0x0780, /* X, Y, Z scan */ - 0x0400, /* X */ - 0x0200, /* Y */ - 0x0180, /* Z */ - 0x0040, /* BAT1 */ - 0x0030, /* BAT2 */ - 0x0010, /* AUX */ - 0x0010, /* AUX scan */ - 0x0004, /* TEMP1 */ - 0x0070, /* Port scan */ - 0x0002, /* TEMP2 */ - 0x0000, /* X+, X- drivers */ - 0x0000, /* Y+, Y- drivers */ - 0x0000, /* Y+, X- drivers */ -}; - -#define X_TRANSFORM(s) \ - ((s->y * s->tr[0] - s->x * s->tr[1]) / s->tr[2] + s->tr[3]) -#define Y_TRANSFORM(s) \ - ((s->y * s->tr[4] - s->x * s->tr[5]) / s->tr[6] + s->tr[7]) -#define Z1_TRANSFORM(s) \ - ((400 - ((s)->x >> 7) + ((s)->pressure << 10)) << 4) -#define Z2_TRANSFORM(s) \ - ((4000 + ((s)->y >> 7) - ((s)->pressure << 10)) << 4) - -#define BAT1_VAL 0x8660 -#define BAT2_VAL 0x0000 -#define AUX1_VAL 0x35c0 -#define AUX2_VAL 0xffff -#define TEMP1_VAL 0x8c70 -#define TEMP2_VAL 0xa5b0 - -#define TSC_POWEROFF_DELAY 50 -#define TSC_SOFTSTEP_DELAY 50 - -static void tsc210x_reset(TSC210xState *s) -{ - s->state = false; - s->pin_func = 2; - s->enabled = false; - s->busy = false; - s->nextfunction = 0; - s->ref = 0; - s->timing = 0; - s->irq = false; - s->dav = 0; - - s->audio_ctrl1 = 0x0000; - s->audio_ctrl2 = 0x4410; - s->audio_ctrl3 = 0x0000; - s->pll[0] = 0x1004; - s->pll[1] = 0x0000; - s->pll[2] = 0x1fff; - s->volume = 0xffff; - s->dac_power = 0x8540; - s->softstep = true; - s->volume_change = 0; - s->powerdown = 0; - s->filter_data[0x00] = 0x6be3; - s->filter_data[0x01] = 0x9666; - s->filter_data[0x02] = 0x675d; - s->filter_data[0x03] = 0x6be3; - s->filter_data[0x04] = 0x9666; - s->filter_data[0x05] = 0x675d; - s->filter_data[0x06] = 0x7d83; - s->filter_data[0x07] = 0x84ee; - s->filter_data[0x08] = 0x7d83; - s->filter_data[0x09] = 0x84ee; - s->filter_data[0x0a] = 0x6be3; - s->filter_data[0x0b] = 0x9666; - s->filter_data[0x0c] = 0x675d; - s->filter_data[0x0d] = 0x6be3; - s->filter_data[0x0e] = 0x9666; - s->filter_data[0x0f] = 0x675d; - s->filter_data[0x10] = 0x7d83; - s->filter_data[0x11] = 0x84ee; - s->filter_data[0x12] = 0x7d83; - s->filter_data[0x13] = 0x84ee; - - s->i2s_tx_rate = 0; - s->i2s_rx_rate = 0; - - s->kb.scan = 1; - s->kb.debounce = 0; - s->kb.mask = 0x0000; - s->kb.mode = 3; - s->kb.intr = 0; - - qemu_set_irq(s->pint, !s->irq); - qemu_set_irq(s->davint, !s->dav); - qemu_irq_raise(s->kbint); -} - -typedef struct { - int rate; - int dsor; - int fsref; -} TSC210xRateInfo; - -/* { rate, dsor, fsref } */ -static const TSC210xRateInfo tsc2102_rates[] = { - /* Fsref / 6.0 */ - { 7350, 63, 1 }, - { 8000, 63, 0 }, - /* Fsref / 6.0 */ - { 7350, 54, 1 }, - { 8000, 54, 0 }, - /* Fsref / 5.0 */ - { 8820, 45, 1 }, - { 9600, 45, 0 }, - /* Fsref / 4.0 */ - { 11025, 36, 1 }, - { 12000, 36, 0 }, - /* Fsref / 3.0 */ - { 14700, 27, 1 }, - { 16000, 27, 0 }, - /* Fsref / 2.0 */ - { 22050, 18, 1 }, - { 24000, 18, 0 }, - /* Fsref / 1.5 */ - { 29400, 9, 1 }, - { 32000, 9, 0 }, - /* Fsref */ - { 44100, 0, 1 }, - { 48000, 0, 0 }, - - { 0, 0, 0 }, -}; - -static inline void tsc210x_out_flush(TSC210xState *s, int len) -{ - uint8_t *data = s->codec.out.fifo + s->codec.out.start; - uint8_t *end = data + len; - - while (data < end) - data += AUD_write(s->dac_voice[0], data, end - data) ?: (end - data); - - s->codec.out.len -= len; - if (s->codec.out.len) - memmove(s->codec.out.fifo, end, s->codec.out.len); - s->codec.out.start = 0; -} - -static void tsc210x_audio_out_cb(TSC210xState *s, int free_b) -{ - if (s->codec.out.len >= free_b) { - tsc210x_out_flush(s, free_b); - return; - } - - s->codec.out.size = MIN(free_b, 16384); - qemu_irq_raise(s->codec.tx_start); -} - -static void tsc2102_audio_rate_update(TSC210xState *s) -{ - const TSC210xRateInfo *rate; - - s->codec.tx_rate = 0; - s->codec.rx_rate = 0; - if (s->dac_power & (1 << 15)) /* PWDNC */ - return; - - for (rate = tsc2102_rates; rate->rate; rate ++) - if (rate->dsor == (s->audio_ctrl1 & 0x3f) && /* DACFS */ - rate->fsref == ((s->audio_ctrl3 >> 13) & 1))/* REFFS */ - break; - if (!rate->rate) { - printf("%s: unknown sampling rate configured\n", __func__); - return; - } - - s->codec.tx_rate = rate->rate; -} - -static void tsc2102_audio_output_update(TSC210xState *s) -{ - int enable; - struct audsettings fmt; - - if (s->dac_voice[0]) { - tsc210x_out_flush(s, s->codec.out.len); - s->codec.out.size = 0; - AUD_set_active_out(s->dac_voice[0], 0); - AUD_close_out(&s->card, s->dac_voice[0]); - s->dac_voice[0] = NULL; - } - s->codec.cts = 0; - - enable = - (~s->dac_power & (1 << 15)) && /* PWDNC */ - (~s->dac_power & (1 << 10)); /* DAPWDN */ - if (!enable || !s->codec.tx_rate) - return; - - /* Force our own sampling rate even in slave DAC mode */ - fmt.endianness = 0; - fmt.nchannels = 2; - fmt.freq = s->codec.tx_rate; - fmt.fmt = AUDIO_FORMAT_S16; - - s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], - "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); - if (s->dac_voice[0]) { - s->codec.cts = 1; - AUD_set_active_out(s->dac_voice[0], 1); - } -} - -static uint16_t tsc2102_data_register_read(TSC210xState *s, int reg) -{ - switch (reg) { - case 0x00: /* X */ - s->dav &= 0xfbff; - return TSC_CUT_RESOLUTION(X_TRANSFORM(s), s->precision) + - (s->noise & 3); - - case 0x01: /* Y */ - s->noise ++; - s->dav &= 0xfdff; - return TSC_CUT_RESOLUTION(Y_TRANSFORM(s), s->precision) ^ - (s->noise & 3); - - case 0x02: /* Z1 */ - s->dav &= 0xfeff; - return TSC_CUT_RESOLUTION(Z1_TRANSFORM(s), s->precision) - - (s->noise & 3); - - case 0x03: /* Z2 */ - s->dav &= 0xff7f; - return TSC_CUT_RESOLUTION(Z2_TRANSFORM(s), s->precision) | - (s->noise & 3); - - case 0x04: /* KPData */ - if ((s->model & 0xff00) == 0x2300) { - if (s->kb.intr && (s->kb.mode & 2)) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } - return s->kb.down; - } - - return 0xffff; - - case 0x05: /* BAT1 */ - s->dav &= 0xffbf; - return TSC_CUT_RESOLUTION(BAT1_VAL, s->precision) + - (s->noise & 6); - - case 0x06: /* BAT2 */ - s->dav &= 0xffdf; - return TSC_CUT_RESOLUTION(BAT2_VAL, s->precision); - - case 0x07: /* AUX1 */ - s->dav &= 0xffef; - return TSC_CUT_RESOLUTION(AUX1_VAL, s->precision); - - case 0x08: /* AUX2 */ - s->dav &= 0xfff7; - return 0xffff; - - case 0x09: /* TEMP1 */ - s->dav &= 0xfffb; - return TSC_CUT_RESOLUTION(TEMP1_VAL, s->precision) - - (s->noise & 5); - - case 0x0a: /* TEMP2 */ - s->dav &= 0xfffd; - return TSC_CUT_RESOLUTION(TEMP2_VAL, s->precision) ^ - (s->noise & 3); - - case 0x0b: /* DAC */ - s->dav &= 0xfffe; - return 0xffff; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_data_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static uint16_t tsc2102_control_register_read( - TSC210xState *s, int reg) -{ - switch (reg) { - case 0x00: /* TSC ADC */ - return (s->pressure << 15) | ((!s->busy) << 14) | - (s->nextfunction << 10) | (s->nextprecision << 8) | s->filter; - - case 0x01: /* Status / Keypad Control */ - if ((s->model & 0xff00) == 0x2100) - return (s->pin_func << 14) | ((!s->enabled) << 13) | - (s->host_mode << 12) | ((!!s->dav) << 11) | s->dav; - else - return (s->kb.intr << 15) | ((s->kb.scan || !s->kb.down) << 14) | - (s->kb.debounce << 11); - - case 0x02: /* DAC Control */ - if ((s->model & 0xff00) == 0x2300) - return s->dac_power & 0x8000; - else - goto bad_reg; - - case 0x03: /* Reference */ - return s->ref; - - case 0x04: /* Reset */ - return 0xffff; - - case 0x05: /* Configuration */ - return s->timing; - - case 0x06: /* Secondary configuration */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - return ((!s->dav) << 15) | ((s->kb.mode & 1) << 14) | s->pll[2]; - - case 0x10: /* Keypad Mask */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - return s->kb.mask; - - default: - bad_reg: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_control_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static uint16_t tsc2102_audio_register_read(TSC210xState *s, int reg) -{ - int l_ch, r_ch; - uint16_t val; - - switch (reg) { - case 0x00: /* Audio Control 1 */ - return s->audio_ctrl1; - - case 0x01: - return 0xff00; - - case 0x02: /* DAC Volume Control */ - return s->volume; - - case 0x03: - return 0x8b00; - - case 0x04: /* Audio Control 2 */ - l_ch = 1; - r_ch = 1; - if (s->softstep && !(s->dac_power & (1 << 10))) { - l_ch = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) > - s->volume_change + TSC_SOFTSTEP_DELAY); - r_ch = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) > - s->volume_change + TSC_SOFTSTEP_DELAY); - } - - return s->audio_ctrl2 | (l_ch << 3) | (r_ch << 2); - - case 0x05: /* Stereo DAC Power Control */ - return 0x2aa0 | s->dac_power | - (((s->dac_power & (1 << 10)) && - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) > - s->powerdown + TSC_POWEROFF_DELAY)) << 6); - - case 0x06: /* Audio Control 3 */ - val = s->audio_ctrl3 | 0x0001; - s->audio_ctrl3 &= 0xff3f; - return val; - - case 0x07: /* LCH_BASS_BOOST_N0 */ - case 0x08: /* LCH_BASS_BOOST_N1 */ - case 0x09: /* LCH_BASS_BOOST_N2 */ - case 0x0a: /* LCH_BASS_BOOST_N3 */ - case 0x0b: /* LCH_BASS_BOOST_N4 */ - case 0x0c: /* LCH_BASS_BOOST_N5 */ - case 0x0d: /* LCH_BASS_BOOST_D1 */ - case 0x0e: /* LCH_BASS_BOOST_D2 */ - case 0x0f: /* LCH_BASS_BOOST_D4 */ - case 0x10: /* LCH_BASS_BOOST_D5 */ - case 0x11: /* RCH_BASS_BOOST_N0 */ - case 0x12: /* RCH_BASS_BOOST_N1 */ - case 0x13: /* RCH_BASS_BOOST_N2 */ - case 0x14: /* RCH_BASS_BOOST_N3 */ - case 0x15: /* RCH_BASS_BOOST_N4 */ - case 0x16: /* RCH_BASS_BOOST_N5 */ - case 0x17: /* RCH_BASS_BOOST_D1 */ - case 0x18: /* RCH_BASS_BOOST_D2 */ - case 0x19: /* RCH_BASS_BOOST_D4 */ - case 0x1a: /* RCH_BASS_BOOST_D5 */ - return s->filter_data[reg - 0x07]; - - case 0x1b: /* PLL Programmability 1 */ - return s->pll[0]; - - case 0x1c: /* PLL Programmability 2 */ - return s->pll[1]; - - case 0x1d: /* Audio Control 4 */ - return (!s->softstep) << 14; - - default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_audio_register_read: " - "no such register: 0x%02x\n", reg); -#endif - return 0xffff; - } -} - -static void tsc2102_data_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* X */ - case 0x01: /* Y */ - case 0x02: /* Z1 */ - case 0x03: /* Z2 */ - case 0x05: /* BAT1 */ - case 0x06: /* BAT2 */ - case 0x07: /* AUX1 */ - case 0x08: /* AUX2 */ - case 0x09: /* TEMP1 */ - case 0x0a: /* TEMP2 */ - return; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "tsc2102_data_register_write: " - "no such register: 0x%02x\n", reg); - } -} - -static void tsc2102_control_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* TSC ADC */ - s->host_mode = value >> 15; - s->enabled = !(value & 0x4000); - if (s->busy && !s->enabled) - timer_del(s->timer); - s->busy = s->busy && s->enabled; - s->nextfunction = (value >> 10) & 0xf; - s->nextprecision = (value >> 8) & 3; - s->filter = value & 0xff; - return; - - case 0x01: /* Status / Keypad Control */ - if ((s->model & 0xff00) == 0x2100) - s->pin_func = value >> 14; - else { - s->kb.scan = (value >> 14) & 1; - s->kb.debounce = (value >> 11) & 7; - if (s->kb.intr && s->kb.scan) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } - } - return; - - case 0x02: /* DAC Control */ - if ((s->model & 0xff00) == 0x2300) { - s->dac_power &= 0x7fff; - s->dac_power |= 0x8000 & value; - } else - goto bad_reg; - break; - - case 0x03: /* Reference */ - s->ref = value & 0x1f; - return; - - case 0x04: /* Reset */ - if (value == 0xbb00) { - if (s->busy) - timer_del(s->timer); - tsc210x_reset(s); -#ifdef TSC_VERBOSE - } else { - fprintf(stderr, "tsc2102_control_register_write: " - "wrong value written into RESET\n"); -#endif - } - return; - - case 0x05: /* Configuration */ - s->timing = value & 0x3f; -#ifdef TSC_VERBOSE - if (value & ~0x3f) - fprintf(stderr, "tsc2102_control_register_write: " - "wrong value written into CONFIG\n"); -#endif - return; - - case 0x06: /* Secondary configuration */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - s->kb.mode = value >> 14; - s->pll[2] = value & 0x3ffff; - return; - - case 0x10: /* Keypad Mask */ - if ((s->model & 0xff00) == 0x2100) - goto bad_reg; - s->kb.mask = value; - return; - - default: - bad_reg: - qemu_log_mask(LOG_GUEST_ERROR, "tsc2102_control_register_write: " - "no such register: 0x%02x\n", reg); - } -} - -static void tsc2102_audio_register_write( - TSC210xState *s, int reg, uint16_t value) -{ - switch (reg) { - case 0x00: /* Audio Control 1 */ - s->audio_ctrl1 = value & 0x0f3f; -#ifdef TSC_VERBOSE - if ((value & ~0x0f3f) || ((value & 7) != ((value >> 3) & 7))) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 1\n"); -#endif - tsc2102_audio_rate_update(s); - tsc2102_audio_output_update(s); - return; - - case 0x01: -#ifdef TSC_VERBOSE - if (value != 0xff00) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into reg 0x01\n"); -#endif - return; - - case 0x02: /* DAC Volume Control */ - s->volume = value; - s->volume_change = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - return; - - case 0x03: -#ifdef TSC_VERBOSE - if (value != 0x8b00) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into reg 0x03\n"); -#endif - return; - - case 0x04: /* Audio Control 2 */ - s->audio_ctrl2 = value & 0xf7f2; -#ifdef TSC_VERBOSE - if (value & ~0xf7fd) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 2\n"); -#endif - return; - - case 0x05: /* Stereo DAC Power Control */ - if ((value & ~s->dac_power) & (1 << 10)) - s->powerdown = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - s->dac_power = value & 0x9543; -#ifdef TSC_VERBOSE - if ((value & ~0x9543) != 0x2aa0) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Power\n"); -#endif - tsc2102_audio_rate_update(s); - tsc2102_audio_output_update(s); - return; - - case 0x06: /* Audio Control 3 */ - s->audio_ctrl3 &= 0x00c0; - s->audio_ctrl3 |= value & 0xf800; -#ifdef TSC_VERBOSE - if (value & ~0xf8c7) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 3\n"); -#endif - tsc2102_audio_output_update(s); - return; - - case 0x07: /* LCH_BASS_BOOST_N0 */ - case 0x08: /* LCH_BASS_BOOST_N1 */ - case 0x09: /* LCH_BASS_BOOST_N2 */ - case 0x0a: /* LCH_BASS_BOOST_N3 */ - case 0x0b: /* LCH_BASS_BOOST_N4 */ - case 0x0c: /* LCH_BASS_BOOST_N5 */ - case 0x0d: /* LCH_BASS_BOOST_D1 */ - case 0x0e: /* LCH_BASS_BOOST_D2 */ - case 0x0f: /* LCH_BASS_BOOST_D4 */ - case 0x10: /* LCH_BASS_BOOST_D5 */ - case 0x11: /* RCH_BASS_BOOST_N0 */ - case 0x12: /* RCH_BASS_BOOST_N1 */ - case 0x13: /* RCH_BASS_BOOST_N2 */ - case 0x14: /* RCH_BASS_BOOST_N3 */ - case 0x15: /* RCH_BASS_BOOST_N4 */ - case 0x16: /* RCH_BASS_BOOST_N5 */ - case 0x17: /* RCH_BASS_BOOST_D1 */ - case 0x18: /* RCH_BASS_BOOST_D2 */ - case 0x19: /* RCH_BASS_BOOST_D4 */ - case 0x1a: /* RCH_BASS_BOOST_D5 */ - s->filter_data[reg - 0x07] = value; - return; - - case 0x1b: /* PLL Programmability 1 */ - s->pll[0] = value & 0xfffc; -#ifdef TSC_VERBOSE - if (value & ~0xfffc) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into PLL 1\n"); -#endif - return; - - case 0x1c: /* PLL Programmability 2 */ - s->pll[1] = value & 0xfffc; -#ifdef TSC_VERBOSE - if (value & ~0xfffc) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into PLL 2\n"); -#endif - return; - - case 0x1d: /* Audio Control 4 */ - s->softstep = !(value & 0x4000); -#ifdef TSC_VERBOSE - if (value & ~0x4000) - fprintf(stderr, "tsc2102_audio_register_write: " - "wrong value written into Audio 4\n"); -#endif - return; - - default: - qemu_log_mask(LOG_GUEST_ERROR, "tsc2102_audio_register_write: " - "no such register: 0x%02x\n", reg); - } -} - -/* This handles most of the chip logic. */ -static void tsc210x_pin_update(TSC210xState *s) -{ - int64_t expires; - bool pin_state; - - switch (s->pin_func) { - case 0: - pin_state = s->pressure; - break; - case 1: - pin_state = !!s->dav; - break; - case 2: - default: - pin_state = s->pressure && !s->dav; - } - - if (!s->enabled) - pin_state = false; - - if (pin_state != s->irq) { - s->irq = pin_state; - qemu_set_irq(s->pint, !s->irq); - } - - switch (s->nextfunction) { - case TSC_MODE_XY_SCAN: - case TSC_MODE_XYZ_SCAN: - if (!s->pressure) - return; - break; - - case TSC_MODE_X: - case TSC_MODE_Y: - case TSC_MODE_Z: - if (!s->pressure) - return; - /* Fall through */ - case TSC_MODE_BAT1: - case TSC_MODE_BAT2: - case TSC_MODE_AUX: - case TSC_MODE_TEMP1: - case TSC_MODE_TEMP2: - if (s->dav) - s->enabled = false; - break; - - case TSC_MODE_AUX_SCAN: - case TSC_MODE_PORT_SCAN: - break; - - case TSC_MODE_NO_SCAN: - case TSC_MODE_XX_DRV: - case TSC_MODE_YY_DRV: - case TSC_MODE_YX_DRV: - default: - return; - } - - if (!s->enabled || s->busy || s->dav) - return; - - s->busy = true; - s->precision = s->nextprecision; - s->function = s->nextfunction; - expires = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (NANOSECONDS_PER_SECOND >> 10); - timer_mod(s->timer, expires); -} - -static uint16_t tsc210x_read(TSC210xState *s) -{ - uint16_t ret = 0x0000; - - if (!s->command) - fprintf(stderr, "tsc210x_read: SPI underrun!\n"); - - switch (s->page) { - case TSC_DATA_REGISTERS_PAGE: - ret = tsc2102_data_register_read(s, s->offset); - if (!s->dav) - qemu_irq_raise(s->davint); - break; - case TSC_CONTROL_REGISTERS_PAGE: - ret = tsc2102_control_register_read(s, s->offset); - break; - case TSC_AUDIO_REGISTERS_PAGE: - ret = tsc2102_audio_register_read(s, s->offset); - break; - default: - hw_error("tsc210x_read: wrong memory page\n"); - } - - tsc210x_pin_update(s); - - /* Allow sequential reads. */ - s->offset ++; - s->state = false; - return ret; -} - -static void tsc210x_write(TSC210xState *s, uint16_t value) -{ - /* - * This is a two-state state machine for reading - * command and data every second time. - */ - if (!s->state) { - s->command = (value >> 15) != 0; - s->page = (value >> 11) & 0x0f; - s->offset = (value >> 5) & 0x3f; - s->state = true; - } else { - if (s->command) - fprintf(stderr, "tsc210x_write: SPI overrun!\n"); - else - switch (s->page) { - case TSC_DATA_REGISTERS_PAGE: - tsc2102_data_register_write(s, s->offset, value); - break; - case TSC_CONTROL_REGISTERS_PAGE: - tsc2102_control_register_write(s, s->offset, value); - break; - case TSC_AUDIO_REGISTERS_PAGE: - tsc2102_audio_register_write(s, s->offset, value); - break; - default: - hw_error("tsc210x_write: wrong memory page\n"); - } - - tsc210x_pin_update(s); - s->state = false; - } -} - -uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len) -{ - TSC210xState *s = opaque; - uint32_t ret = 0; - - if (len != 16) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: bad SPI word width %i\n", __func__, len); - return 0; - } - - /* TODO: sequential reads etc - how do we make sure the host doesn't - * unintentionally read out a conversion result from a register while - * transmitting the command word of the next command? */ - if (!value || (s->state && s->command)) - ret = tsc210x_read(s); - if (value || (s->state && !s->command)) - tsc210x_write(s, value); - - return ret; -} - -static void tsc210x_timer_tick(void *opaque) -{ - TSC210xState *s = opaque; - - /* Timer ticked -- a set of conversions has been finished. */ - - if (!s->busy) - return; - - s->busy = false; - s->dav |= mode_regs[s->function]; - tsc210x_pin_update(s); - qemu_irq_lower(s->davint); -} - -static void tsc210x_touchscreen_event(void *opaque, - int x, int y, int z, int buttons_state) -{ - TSC210xState *s = opaque; - int p = s->pressure; - - if (buttons_state) { - s->x = x; - s->y = y; - } - s->pressure = !!buttons_state; - - /* - * Note: We would get better responsiveness in the guest by - * signaling TS events immediately, but for now we simulate - * the first conversion delay for sake of correctness. - */ - if (p != s->pressure) - tsc210x_pin_update(s); -} - -static void tsc210x_i2s_swallow(TSC210xState *s) -{ - if (s->dac_voice[0]) - tsc210x_out_flush(s, s->codec.out.len); - else - s->codec.out.len = 0; -} - -static void tsc210x_i2s_set_rate(TSC210xState *s, int in, int out) -{ - s->i2s_tx_rate = out; - s->i2s_rx_rate = in; -} - -static int tsc210x_pre_save(void *opaque) -{ - TSC210xState *s = (TSC210xState *) opaque; - s->now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - return 0; -} - -static int tsc210x_post_load(void *opaque, int version_id) -{ - TSC210xState *s = (TSC210xState *) opaque; - int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - - if (s->function >= ARRAY_SIZE(mode_regs)) { - return -EINVAL; - } - if (s->nextfunction >= ARRAY_SIZE(mode_regs)) { - return -EINVAL; - } - if (s->precision >= ARRAY_SIZE(resolution)) { - return -EINVAL; - } - if (s->nextprecision >= ARRAY_SIZE(resolution)) { - return -EINVAL; - } - - s->volume_change -= s->now; - s->volume_change += now; - s->powerdown -= s->now; - s->powerdown += now; - - s->busy = timer_pending(s->timer); - qemu_set_irq(s->pint, !s->irq); - qemu_set_irq(s->davint, !s->dav); - - return 0; -} - -static VMStateField vmstatefields_tsc210x[] = { - VMSTATE_BOOL(enabled, TSC210xState), - VMSTATE_BOOL(host_mode, TSC210xState), - VMSTATE_BOOL(irq, TSC210xState), - VMSTATE_BOOL(command, TSC210xState), - VMSTATE_BOOL(pressure, TSC210xState), - VMSTATE_BOOL(softstep, TSC210xState), - VMSTATE_BOOL(state, TSC210xState), - VMSTATE_UINT16(dav, TSC210xState), - VMSTATE_INT32(x, TSC210xState), - VMSTATE_INT32(y, TSC210xState), - VMSTATE_UINT8(offset, TSC210xState), - VMSTATE_UINT8(page, TSC210xState), - VMSTATE_UINT8(filter, TSC210xState), - VMSTATE_UINT8(pin_func, TSC210xState), - VMSTATE_UINT8(ref, TSC210xState), - VMSTATE_UINT8(timing, TSC210xState), - VMSTATE_UINT8(noise, TSC210xState), - VMSTATE_UINT8(function, TSC210xState), - VMSTATE_UINT8(nextfunction, TSC210xState), - VMSTATE_UINT8(precision, TSC210xState), - VMSTATE_UINT8(nextprecision, TSC210xState), - VMSTATE_UINT16(audio_ctrl1, TSC210xState), - VMSTATE_UINT16(audio_ctrl2, TSC210xState), - VMSTATE_UINT16(audio_ctrl3, TSC210xState), - VMSTATE_UINT16_ARRAY(pll, TSC210xState, 3), - VMSTATE_UINT16(volume, TSC210xState), - VMSTATE_UINT16(dac_power, TSC210xState), - VMSTATE_INT64(volume_change, TSC210xState), - VMSTATE_INT64(powerdown, TSC210xState), - VMSTATE_INT64(now, TSC210xState), - VMSTATE_UINT16_ARRAY(filter_data, TSC210xState, 0x14), - VMSTATE_TIMER_PTR(timer, TSC210xState), - VMSTATE_END_OF_LIST() -}; - -static const VMStateDescription vmstate_tsc2102 = { - .name = "tsc2102", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = tsc210x_pre_save, - .post_load = tsc210x_post_load, - .fields = vmstatefields_tsc210x, -}; - -static const VMStateDescription vmstate_tsc2301 = { - .name = "tsc2301", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = tsc210x_pre_save, - .post_load = tsc210x_post_load, - .fields = vmstatefields_tsc210x, -}; - -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"; - - 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 TSC2102-driven Touchscreen"); - - AUD_register_card(s->name, &s->card); - - qemu_register_reset((void *) tsc210x_reset, s); - vmstate_register(NULL, 0, &vmstate_tsc2102, s); - - return &s->chip; -} - -uWireSlave *tsc2301_init(qemu_irq penirq, qemu_irq kbirq, qemu_irq dav) -{ - TSC210xState *s; - - s = g_new0(TSC210xState, 1); - s->x = 400; - s->y = 240; - s->pressure = 0; - s->precision = s->nextprecision = 0; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tsc210x_timer_tick, s); - s->pint = penirq; - s->kbint = kbirq; - s->davint = 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); - - return &s->chip; -} - -I2SCodec *tsc210x_codec(uWireSlave *chip) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; - - return &s->codec; -} - -/* - * Use tslib generated calibration data to generate ADC input values - * from the touchscreen. Assuming 12-bit precision was used during - * tslib calibration. - */ -void tsc210x_set_transform(uWireSlave *chip, - MouseTransformInfo *info) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; -#if 0 - int64_t ltr[8]; - - ltr[0] = (int64_t) info->a[1] * info->y; - ltr[1] = (int64_t) info->a[4] * info->x; - ltr[2] = (int64_t) info->a[1] * info->a[3] - - (int64_t) info->a[4] * info->a[0]; - ltr[3] = (int64_t) info->a[2] * info->a[4] - - (int64_t) info->a[5] * info->a[1]; - ltr[4] = (int64_t) info->a[0] * info->y; - ltr[5] = (int64_t) info->a[3] * info->x; - ltr[6] = (int64_t) info->a[4] * info->a[0] - - (int64_t) info->a[1] * info->a[3]; - ltr[7] = (int64_t) info->a[2] * info->a[3] - - (int64_t) info->a[5] * info->a[0]; - - /* Avoid integer overflow */ - s->tr[0] = ltr[0] >> 11; - s->tr[1] = ltr[1] >> 11; - s->tr[2] = muldiv64(ltr[2], 1, info->a[6]); - s->tr[3] = muldiv64(ltr[3], 1 << 4, ltr[2]); - s->tr[4] = ltr[4] >> 11; - s->tr[5] = ltr[5] >> 11; - s->tr[6] = muldiv64(ltr[6], 1, info->a[6]); - s->tr[7] = muldiv64(ltr[7], 1 << 4, ltr[6]); -#else - - /* This version assumes touchscreen X & Y axis are parallel or - * perpendicular to LCD's X & Y axis in some way. */ - if (abs(info->a[0]) > abs(info->a[1])) { - s->tr[0] = 0; - s->tr[1] = -info->a[6] * info->x; - s->tr[2] = info->a[0]; - s->tr[3] = -info->a[2] / info->a[0]; - s->tr[4] = info->a[6] * info->y; - s->tr[5] = 0; - s->tr[6] = info->a[4]; - s->tr[7] = -info->a[5] / info->a[4]; - } else { - s->tr[0] = info->a[6] * info->y; - s->tr[1] = 0; - s->tr[2] = info->a[1]; - s->tr[3] = -info->a[2] / info->a[1]; - s->tr[4] = 0; - s->tr[5] = -info->a[6] * info->x; - s->tr[6] = info->a[3]; - s->tr[7] = -info->a[5] / info->a[3]; - } - - s->tr[0] >>= 11; - s->tr[1] >>= 11; - s->tr[3] <<= 4; - s->tr[4] >>= 11; - s->tr[5] >>= 11; - s->tr[7] <<= 4; -#endif -} - -void tsc210x_key_event(uWireSlave *chip, int key, int down) -{ - TSC210xState *s = (TSC210xState *) chip->opaque; - - if (down) - s->kb.down |= 1 << key; - else - s->kb.down &= ~(1 << key); - - if (down && (s->kb.down & ~s->kb.mask) && !s->kb.intr) { - s->kb.intr = 1; - qemu_irq_lower(s->kbint); - } else if (s->kb.intr && !(s->kb.down & ~s->kb.mask) && - !(s->kb.mode & 1)) { - s->kb.intr = 0; - qemu_irq_raise(s->kbint); - } -} 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..dd405bdb5d 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 @@ -85,11 +87,16 @@ config GOLDFISH_PIC config M68K_IRQC bool -config NIOS2_VIC +config LOONGSON_IPI_COMMON bool +config LOONGSON_IPI + bool + select LOONGSON_IPI_COMMON + config LOONGARCH_IPI bool + select LOONGSON_IPI_COMMON config LOONGARCH_PCH_PIC bool diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index d0bf8d545b..c0f30092cd 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -142,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), @@ -191,7 +191,7 @@ static void aw_a10_pic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = aw_a10_pic_reset; + device_class_set_legacy_reset(dc, aw_a10_pic_reset); dc->desc = "allwinner a10 pic"; dc->vmsd = &vmstate_aw_a10_pic; } 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 7ebd165c4e..edd4131f55 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); @@ -305,6 +290,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; @@ -315,10 +301,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; @@ -331,6 +320,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) @@ -393,7 +386,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() @@ -407,7 +400,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), @@ -430,7 +423,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 } @@ -461,6 +454,7 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, APICCommonState *s = APIC_COMMON(obj); DeviceState *dev = DEVICE(obj); uint32_t value; + Error *local_err = NULL; if (dev->realized) { qdev_prop_set_after_realize(dev, name, errp); @@ -471,6 +465,15 @@ 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(&local_err, + "APIC ID %d requires x2APIC feature in CPU", + value); + error_append_hint(&local_err, "Try x2apic=on in -cpu.\n"); + error_propagate(errp, local_err); + return; + } + s->initial_apic_id = value; s->id = (uint8_t)value; } @@ -489,7 +492,7 @@ static void apic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = apic_reset_common; + device_class_set_legacy_reset(dc, apic_reset_common); device_class_set_props(dc, apic_properties_common); dc->realize = apic_common_realize; dc->unrealize = apic_common_unrealize; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 7a34bc0998..2a48f0da2f 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) { @@ -1263,9 +1263,14 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, trace_gic_enable_irq(irq + i); } GIC_DIST_SET_ENABLED(irq + i, cm); - /* If a raised level triggered IRQ enabled then mark - is as pending. */ - if (GIC_DIST_TEST_LEVEL(irq + i, mask) + /* + * If a raised level triggered IRQ enabled then mark + * it as pending on 11MPCore. For other GIC revisions we + * handle the "level triggered and line asserted" check + * at the other end in gic_test_pending(). + */ + if (s->revision == REV_11MPCORE + && GIC_DIST_TEST_LEVEL(irq + i, mask) && !GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) { DPRINTF("Set %d pending mask %x\n", irq + i, mask); GIC_DIST_SET_PENDING(irq + i, mask); @@ -1308,12 +1313,15 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for (i = 0; i < 8; i++) { if (value & (1 << i)) { + int mask = (irq < GIC_INTERNAL) ? (1 << cpu) + : GIC_DIST_TARGET(irq + i); + if (s->security_extn && !attrs.secure && !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - GIC_DIST_SET_PENDING(irq + i, GIC_DIST_TARGET(irq + i)); + GIC_DIST_SET_PENDING(irq + i, mask); } } } else if (offset < 0x300) { @@ -1333,7 +1341,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); } @@ -1407,6 +1415,13 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, value = ALL_CPU_MASK; } s->irq_target[irq] = value & ALL_CPU_MASK; + if (irq >= GIC_INTERNAL && s->irq_state[irq].pending) { + /* + * Changing the target of an interrupt that is currently + * pending updates the set of CPUs it is pending on. + */ + s->irq_state[irq].pending = value & ALL_CPU_MASK; + } } } else if (offset < 0xf00) { /* Interrupt Configuration. */ @@ -1658,7 +1673,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, *data = s->h_apr[gic_get_vcpu_real_id(cpu)]; } else if (gic_cpu_ns_access(s, cpu, attrs)) { /* NS view of GICC_APR is the top half of GIC_NSAPR */ - *data = gic_apr_ns_view(s, regno, cpu); + *data = gic_apr_ns_view(s, cpu, regno); } else { *data = s->apr[regno][cpu]; } @@ -1746,7 +1761,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, s->h_apr[gic_get_vcpu_real_id(cpu)] = value; } else if (gic_cpu_ns_access(s, cpu, attrs)) { /* NS view of GICC_APR is the top half of GIC_NSAPR */ - gic_apr_write_ns_view(s, regno, cpu, value); + gic_apr_write_ns_view(s, cpu, regno, value); } else { s->apr[regno][cpu] = value; } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 7b44d5625b..53fb2c4e2d 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, ResetType type) { - 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..e2a73337b1 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, ResetType type) { - 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, type); + } 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; } } @@ -546,17 +547,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); } - } else if (kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { + } else { error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); error_append_hint(errp, "Perhaps the host CPU does not support GICv2?\n"); - } else if (ret != -ENODEV && ret != -ENOTSUP) { - /* - * Very ancient kernel without KVM_CAP_DEVICE_CTRL: assume that - * ENODEV or ENOTSUP mean "can't create GICv2 with KVM_CREATE_DEVICE", - * and that we will get a GICv2 via KVM_CREATE_IRQCHIP. - */ - error_setg_errno(errp, -ret, "error creating in-kernel VGIC"); return; } @@ -593,6 +587,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 +595,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.c b/hw/intc/arm_gicv3.c index 0b8f79a122..58e18fff54 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -21,7 +21,7 @@ #include "hw/intc/arm_gicv3.h" #include "gicv3_internal.h" -static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) +static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio, bool nmi) { /* Return true if this IRQ at this priority should take * precedence over the current recorded highest priority @@ -30,14 +30,23 @@ static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio) * is the same as this one (a property which the calling code * relies on). */ - if (prio < cs->hppi.prio) { - return true; + if (prio != cs->hppi.prio) { + return prio < cs->hppi.prio; } + + /* + * The same priority IRQ with non-maskable property should signal to + * the CPU as it have the priority higher than the labelled 0x80 or 0x00. + */ + if (nmi != cs->hppi.nmi) { + return nmi; + } + /* If multiple pending interrupts have the same priority then it is an * IMPDEF choice which of them to signal to the CPU. We choose to * signal the one with the lowest interrupt number. */ - if (prio == cs->hppi.prio && irq <= cs->hppi.irq) { + if (irq <= cs->hppi.irq) { return true; } return false; @@ -129,6 +138,40 @@ static uint32_t gicr_int_pending(GICv3CPUState *cs) return pend; } +static bool gicv3_get_priority(GICv3CPUState *cs, bool is_redist, int irq, + uint8_t *prio) +{ + uint32_t nmi = 0x0; + + if (is_redist) { + nmi = extract32(cs->gicr_inmir0, irq, 1); + } else { + nmi = *gic_bmp_ptr32(cs->gic->nmi, irq); + nmi = nmi & (1 << (irq & 0x1f)); + } + + if (nmi) { + /* DS = 0 & Non-secure NMI */ + if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + ((is_redist && extract32(cs->gicr_igroupr0, irq, 1)) || + (!is_redist && gicv3_gicd_group_test(cs->gic, irq)))) { + *prio = 0x80; + } else { + *prio = 0x0; + } + + return true; + } + + if (is_redist) { + *prio = cs->gicr_ipriorityr[irq]; + } else { + *prio = cs->gic->gicd_ipriority[irq]; + } + + return false; +} + /* Update the interrupt status after state in a redistributor * or CPU interface has changed, but don't tell the CPU i/f. */ @@ -141,6 +184,7 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) uint8_t prio; int i; uint32_t pend; + bool nmi = false; /* Find out which redistributor interrupts are eligible to be * signaled to the CPU interface. @@ -152,10 +196,11 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) if (!(pend & (1 << i))) { continue; } - prio = cs->gicr_ipriorityr[i]; - if (irqbetter(cs, i, prio)) { + nmi = gicv3_get_priority(cs, true, i, &prio); + if (irqbetter(cs, i, prio, nmi)) { cs->hppi.irq = i; cs->hppi.prio = prio; + cs->hppi.nmi = nmi; seenbetter = true; } } @@ -168,9 +213,10 @@ static void gicv3_redist_update_noirqset(GICv3CPUState *cs) if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable && (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) && (cs->hpplpi.prio != 0xff)) { - if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) { + if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio, cs->hpplpi.nmi)) { cs->hppi.irq = cs->hpplpi.irq; cs->hppi.prio = cs->hpplpi.prio; + cs->hppi.nmi = cs->hpplpi.nmi; cs->hppi.grp = cs->hpplpi.grp; seenbetter = true; } @@ -213,6 +259,7 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) int i; uint8_t prio; uint32_t pend = 0; + bool nmi = false; assert(start >= GIC_INTERNAL); assert(len > 0); @@ -240,10 +287,11 @@ static void gicv3_update_noirqset(GICv3State *s, int start, int len) */ continue; } - prio = s->gicd_ipriority[i]; - if (irqbetter(cs, i, prio)) { + nmi = gicv3_get_priority(cs, false, i, &prio); + if (irqbetter(cs, i, prio, nmi)) { cs->hppi.irq = i; cs->hppi.prio = prio; + cs->hppi.nmi = nmi; cs->seenbetter = true; } } @@ -293,6 +341,7 @@ void gicv3_full_update_noirqset(GICv3State *s) for (i = 0; i < s->num_cpu; i++) { s->cpu[i].hppi.prio = 0xff; + s->cpu[i].hppi.nmi = false; } /* Note that we can guarantee that these functions will not diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 351843db4a..bd50a1b079 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,19 +157,37 @@ 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() } }; +static bool gicv3_cpu_nmi_needed(void *opaque) +{ + GICv3CPUState *cs = opaque; + + return cs->gic->nmi_support; +} + +static const VMStateDescription vmstate_gicv3_cpu_nmi = { + .name = "arm_gicv3_cpu/nmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_cpu_nmi_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(gicr_inmir0, GICv3CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_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,10 +210,11 @@ 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, + &vmstate_gicv3_cpu_nmi, NULL } }; @@ -231,12 +251,30 @@ 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() } }; +static bool gicv3_nmi_needed(void *opaque) +{ + GICv3State *cs = opaque; + + return cs->nmi_support; +} + +const VMStateDescription vmstate_gicv3_gicd_nmi = { + .name = "arm_gicv3/gicd_nmi", + .version_id = 1, + .minimum_version_id = 1, + .needed = gicv3_nmi_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(nmi, GICv3State, GICV3_BMP_SIZE), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", .version_id = 1, @@ -245,7 +283,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,8 +301,9 @@ 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, + &vmstate_gicv3_gicd_nmi, NULL } }; @@ -298,6 +337,12 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->cpu[i].parent_vfiq); } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_nmi); + } + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->cpu[i].parent_vnmi); + } memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); @@ -450,9 +495,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, ResetType type) { - GICv3State *s = ARM_GICV3_COMMON(dev); + GICv3State *s = ARM_GICV3_COMMON(obj); int i; for (i = 0; i < s->num_cpu; i++) { @@ -491,8 +536,11 @@ static void arm_gicv3_common_reset(DeviceState *dev) memset(cs->gicr_ipriorityr, 0, sizeof(cs->gicr_ipriorityr)); cs->hppi.prio = 0xff; + cs->hppi.nmi = false; cs->hpplpi.prio = 0xff; + cs->hpplpi.nmi = false; cs->hppvlpi.prio = 0xff; + cs->hppvlpi.nmi = false; /* State in the CPU interface must *not* be reset here, because it * is part of the CPU's reset domain, not the GIC device's. @@ -562,6 +610,7 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0), + DEFINE_PROP_BOOL("has-nmi", GICv3State, nmi_support, 0), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), /* * Compatibility property: force 8 bits of physical priority, even @@ -578,9 +627,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 +657,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 b17b29288c..ea1d1b3455 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -21,6 +21,9 @@ #include "hw/irq.h" #include "cpu.h" #include "target/arm/cpregs.h" +#include "target/arm/cpu-features.h" +#include "sysemu/tcg.h" +#include "sysemu/qtest.h" /* * Special case return value from hppvi_index(); must be larger than @@ -144,7 +147,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) @@ -155,6 +158,10 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs) int i; int aprmax = ich_num_aprs(cs); + if (cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI) { + return 0x0; + } + for (i = 0; i < aprmax; i++) { uint32_t apr = cs->ich_apr[GICV3_G0][i] | cs->ich_apr[GICV3_G1NS][i]; @@ -189,6 +196,7 @@ static int hppvi_index(GICv3CPUState *cs) * correct behaviour. */ int prio = 0xff; + bool nmi = false; if (!(cs->ich_vmcr_el2 & (ICH_VMCR_EL2_VENG0 | ICH_VMCR_EL2_VENG1))) { /* Both groups disabled, definitely nothing to do */ @@ -197,6 +205,7 @@ static int hppvi_index(GICv3CPUState *cs) for (i = 0; i < cs->num_list_regs; i++) { uint64_t lr = cs->ich_lr_el2[i]; + bool thisnmi; int thisprio; if (ich_lr_state(lr) != ICH_LR_EL2_STATE_PENDING) { @@ -215,10 +224,12 @@ static int hppvi_index(GICv3CPUState *cs) } } + thisnmi = lr & ICH_LR_EL2_NMI; thisprio = ich_lr_prio(lr); - if (thisprio < prio) { + if ((thisprio < prio) || ((thisprio == prio) && (thisnmi & (!nmi)))) { prio = thisprio; + nmi = thisnmi; idx = i; } } @@ -287,6 +298,7 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) * equivalent of these checks. */ int grp; + bool is_nmi; uint32_t mask, prio, rprio, vpmr; if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) { @@ -299,10 +311,11 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) */ prio = ich_lr_prio(lr); + is_nmi = lr & ICH_LR_EL2_NMI; vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT, ICH_VMCR_EL2_VPMR_LENGTH); - if (prio >= vpmr) { + if (!is_nmi && prio >= vpmr) { /* Priority mask masks this interrupt */ return false; } @@ -324,6 +337,11 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr) return true; } + if ((prio & mask) == (rprio & mask) && is_nmi && + !(cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI)) { + return true; + } + return false; } @@ -463,6 +481,7 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) int idx; int irqlevel = 0; int fiqlevel = 0; + int nmilevel = 0; idx = hppvi_index(cs); trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx, @@ -480,9 +499,17 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) uint64_t lr = cs->ich_lr_el2[idx]; if (icv_hppi_can_preempt(cs, lr)) { - /* Virtual interrupts are simple: G0 are always FIQ, and G1 IRQ */ + /* + * Virtual interrupts are simple: G0 are always FIQ, and G1 are + * IRQ or NMI which depends on the ICH_LR_EL2.NMI to have + * non-maskable property. + */ if (lr & ICH_LR_EL2_GROUP) { - irqlevel = 1; + if (lr & ICH_LR_EL2_NMI) { + nmilevel = 1; + } else { + irqlevel = 1; + } } else { fiqlevel = 1; } @@ -492,6 +519,7 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs) trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel); qemu_set_irq(cs->parent_vfiq, fiqlevel); qemu_set_irq(cs->parent_virq, irqlevel); + qemu_set_irq(cs->parent_vnmi, nmilevel); } static void gicv3_cpuif_virt_update(GICv3CPUState *cs) @@ -548,7 +576,11 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); - cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->ich_apr[grp][regno] = value & (0xFFFFFFFFU | ICV_AP1R_EL1_NMI); + } else { + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_virt_irq_fiq_update(cs); return; @@ -695,7 +727,11 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio = ich_highest_active_virt_prio(cs); + uint64_t prio = ich_highest_active_virt_prio(cs); + + if (cs->ich_apr[GICV3_G1NS][0] & ICV_AP1R_EL1_NMI) { + prio |= ICV_RPR_EL1_NMI; + } trace_gicv3_icv_rpr_read(gicv3_redist_affid(cs), prio); return prio; @@ -734,13 +770,19 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp) */ uint32_t mask = icv_gprio_mask(cs, grp); int prio = ich_lr_prio(cs->ich_lr_el2[idx]) & mask; + bool nmi = cs->ich_lr_el2[idx] & ICH_LR_EL2_NMI; int aprbit = prio >> (8 - cs->vprebits); int regno = aprbit / 32; int regbit = aprbit % 32; cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; cs->ich_lr_el2[idx] |= ICH_LR_EL2_STATE_ACTIVE_BIT; - cs->ich_apr[grp][regno] |= (1 << regbit); + + if (nmi) { + cs->ich_apr[grp][regno] |= ICV_AP1R_EL1_NMI; + } else { + cs->ich_apr[grp][regno] |= (1U << regbit); + } } static void icv_activate_vlpi(GICv3CPUState *cs) @@ -751,7 +793,7 @@ static void icv_activate_vlpi(GICv3CPUState *cs) int regno = aprbit / 32; int regbit = aprbit % 32; - cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit); + cs->ich_apr[cs->hppvlpi.grp][regno] |= (1U << regbit); gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0); } @@ -761,6 +803,7 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; int idx = hppvi_index(cs); uint64_t intid = INTID_SPURIOUS; + int el = arm_current_el(env); if (idx == HPPVI_INDEX_VLPI) { if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) { @@ -770,11 +813,16 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) } else if (idx >= 0) { uint64_t lr = cs->ich_lr_el2[idx]; int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + bool nmi = env->cp15.sctlr_el[el] & SCTLR_NMI && lr & ICH_LR_EL2_NMI; if (thisgrp == grp && icv_hppi_can_preempt(cs, lr)) { intid = ich_lr_vintid(lr); if (!gicv3_intid_is_special(intid)) { - icv_activate_irq(cs, idx, grp); + if (!nmi) { + icv_activate_irq(cs, idx, grp); + } else { + intid = INTID_NMI; + } } else { /* Interrupt goes from Pending to Invalid */ cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; @@ -793,6 +841,42 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri) return intid; } +static uint64_t icv_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + int idx = hppvi_index(cs); + uint64_t intid = INTID_SPURIOUS; + + if (idx >= 0 && idx != HPPVI_INDEX_VLPI) { + uint64_t lr = cs->ich_lr_el2[idx]; + int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0; + + if ((thisgrp == GICV3_G1NS) && icv_hppi_can_preempt(cs, lr)) { + intid = ich_lr_vintid(lr); + if (!gicv3_intid_is_special(intid)) { + if (lr & ICH_LR_EL2_NMI) { + icv_activate_irq(cs, idx, GICV3_G1NS); + } else { + intid = INTID_SPURIOUS; + } + } else { + /* Interrupt goes from Pending to Invalid */ + cs->ich_lr_el2[idx] &= ~ICH_LR_EL2_STATE_PENDING_BIT; + /* + * We will now return the (bogus) ID from the list register, + * as per the pseudocode. + */ + } + } + } + + trace_gicv3_icv_nmiar1_read(gicv3_redist_affid(cs), intid); + + gicv3_cpuif_virt_update(cs); + + return intid; +} + static uint32_t icc_fullprio_mask(GICv3CPUState *cs) { /* @@ -801,7 +885,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) @@ -830,6 +914,23 @@ static int icc_highest_active_prio(GICv3CPUState *cs) */ int i; + if (cs->nmi_support) { + /* + * If an NMI is active this takes precedence over anything else + * for priority purposes; the NMI bit is only in the AP1R0 bit. + * We return here the effective priority of the NMI, which is + * either 0x0 or 0x80. Callers will need to check NMI again for + * purposes of either setting the RPR register bits or for + * prioritization of NMI vs non-NMI. + */ + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + return 0; + } + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + return (cs->gic->gicd_ctlr & GICD_CTLR_DS) ? 0 : 0x80; + } + } + for (i = 0; i < icc_num_aprs(cs); i++) { uint32_t apr = cs->icc_apr[GICV3_G0][i] | cs->icc_apr[GICV3_G1][i] | cs->icc_apr[GICV3_G1NS][i]; @@ -896,12 +997,24 @@ static bool icc_hppi_can_preempt(GICv3CPUState *cs) */ int rprio; uint32_t mask; + ARMCPU *cpu = ARM_CPU(cs->cpu); + CPUARMState *env = &cpu->env; if (icc_no_enabled_hppi(cs)) { return false; } - if (cs->hppi.prio >= cs->icc_pmr_el1) { + if (cs->hppi.nmi) { + if (!(cs->gic->gicd_ctlr & GICD_CTLR_DS) && + cs->hppi.grp == GICV3_G1NS) { + if (cs->icc_pmr_el1 < 0x80) { + return false; + } + if (arm_is_secure(env) && cs->icc_pmr_el1 == 0x80) { + return false; + } + } + } else if (cs->hppi.prio >= cs->icc_pmr_el1) { /* Priority mask masks this interrupt */ return false; } @@ -921,6 +1034,12 @@ static bool icc_hppi_can_preempt(GICv3CPUState *cs) return true; } + if (cs->hppi.nmi && (cs->hppi.prio & mask) == (rprio & mask)) { + if (!(cs->icc_apr[cs->hppi.grp][0] & ICC_AP1R_EL1_NMI)) { + return true; + } + } + return false; } @@ -929,10 +1048,11 @@ void gicv3_cpuif_update(GICv3CPUState *cs) /* Tell the CPU about its highest priority pending interrupt */ int irqlevel = 0; int fiqlevel = 0; + int nmilevel = 0; 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); @@ -967,6 +1087,8 @@ void gicv3_cpuif_update(GICv3CPUState *cs) if (isfiq) { fiqlevel = 1; + } else if (cs->hppi.nmi) { + nmilevel = 1; } else { irqlevel = 1; } @@ -976,6 +1098,7 @@ void gicv3_cpuif_update(GICv3CPUState *cs) qemu_set_irq(cs->parent_fiq, fiqlevel); qemu_set_irq(cs->parent_irq, irqlevel); + qemu_set_irq(cs->parent_nmi, nmilevel); } static uint64_t icc_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri) @@ -1042,8 +1165,13 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq) int aprbit = prio >> (8 - cs->prebits); int regno = aprbit / 32; int regbit = aprbit % 32; + bool nmi = cs->hppi.nmi; - cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit); + if (nmi) { + cs->icc_apr[cs->hppi.grp][regno] |= ICC_AP1R_EL1_NMI; + } else { + cs->icc_apr[cs->hppi.grp][regno] |= (1U << regbit); + } if (irq < GIC_INTERNAL) { cs->gicr_iactiver0 = deposit32(cs->gicr_iactiver0, irq, 1, 1); @@ -1065,7 +1193,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; } @@ -1102,7 +1230,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; } @@ -1157,6 +1285,7 @@ static uint64_t icc_iar0_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); + int el = arm_current_el(env); uint64_t intid; if (icv_access(env, HCR_IMO)) { @@ -1170,13 +1299,44 @@ static uint64_t icc_iar1_read(CPUARMState *env, const ARMCPRegInfo *ri) } if (!gicv3_intid_is_special(intid)) { - icc_activate_irq(cs, intid); + if (cs->hppi.nmi && env->cp15.sctlr_el[el] & SCTLR_NMI) { + intid = INTID_NMI; + } else { + icc_activate_irq(cs, intid); + } } trace_gicv3_icc_iar1_read(gicv3_redist_affid(cs), intid); return intid; } +static uint64_t icc_nmiar1_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + GICv3CPUState *cs = icc_cs_from_env(env); + uint64_t intid; + + if (icv_access(env, HCR_IMO)) { + return icv_nmiar1_read(env, ri); + } + + if (!icc_hppi_can_preempt(cs)) { + intid = INTID_SPURIOUS; + } else { + intid = icc_hppir1_value(cs, env); + } + + if (!gicv3_intid_is_special(intid)) { + if (!cs->hppi.nmi) { + intid = INTID_SPURIOUS; + } else { + icc_activate_irq(cs, intid); + } + } + + trace_gicv3_icc_nmiar1_read(gicv3_redist_affid(cs), intid); + return intid; +} + static void icc_drop_prio(GICv3CPUState *cs, int grp) { /* Drop the priority of the currently active interrupt in @@ -1203,6 +1363,12 @@ static void icc_drop_prio(GICv3CPUState *cs, int grp) if (!*papr) { continue; } + + if (i == 0 && cs->nmi_support && (*papr & ICC_AP1R_EL1_NMI)) { + *papr &= (~ICC_AP1R_EL1_NMI); + break; + } + /* Clear the lowest set bit */ *papr &= *papr - 1; break; @@ -1237,6 +1403,15 @@ static int icc_highest_active_group(GICv3CPUState *cs) */ int i; + if (cs->nmi_support) { + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + return GICV3_G1; + } + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + return GICV3_G1NS; + } + } + for (i = 0; i < ARRAY_SIZE(cs->icc_apr[0]); i++) { int g0ctz = ctz32(cs->icc_apr[GICV3_G0][i]); int g1ctz = ctz32(cs->icc_apr[GICV3_G1][i]); @@ -1327,7 +1502,7 @@ static void icv_increment_eoicount(GICv3CPUState *cs) ICH_HCR_EL2_EOICOUNT_LENGTH, eoicount + 1); } -static int icv_drop_prio(GICv3CPUState *cs) +static int icv_drop_prio(GICv3CPUState *cs, bool *nmi) { /* Drop the priority of the currently active virtual interrupt * (favouring group 0 if there is a set active bit at @@ -1349,6 +1524,12 @@ static int icv_drop_prio(GICv3CPUState *cs) continue; } + if (i == 0 && cs->nmi_support && (*papr1 & ICV_AP1R_EL1_NMI)) { + *papr1 &= (~ICV_AP1R_EL1_NMI); + *nmi = true; + return 0xff; + } + /* We can't just use the bit-twiddling hack icc_drop_prio() does * because we need to return the bit number we cleared so * it can be compared against the list register's priority field. @@ -1408,6 +1589,7 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, int irq = value & 0xffffff; int grp = ri->crm == 8 ? GICV3_G0 : GICV3_G1NS; int idx, dropprio; + bool nmi = false; trace_gicv3_icv_eoir_write(ri->crm == 8 ? 0 : 1, gicv3_redist_affid(cs), value); @@ -1420,8 +1602,8 @@ static void icv_eoir_write(CPUARMState *env, const ARMCPRegInfo *ri, * error checks" (because that lets us avoid scanning the AP * registers twice). */ - dropprio = icv_drop_prio(cs); - if (dropprio == 0xff) { + dropprio = icv_drop_prio(cs, &nmi); + if (dropprio == 0xff && !nmi) { /* No active interrupt. It is CONSTRAINED UNPREDICTABLE * whether the list registers are checked in this * situation; we choose not to. @@ -1432,16 +1614,26 @@ 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); + bool thisnmi = lr & ICH_LR_EL2_NMI; - if (thisgrp == grp && lr_gprio == dropprio) { - if (!icv_eoi_split(env, cs)) { - /* Priority drop and deactivate not split: deactivate irq now */ + if (thisgrp == grp && (lr_gprio == dropprio || (thisnmi & nmi))) { + 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); } } @@ -1682,7 +1874,11 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, return; } - cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->icc_apr[grp][regno] = value & (0xFFFFFFFFU | ICC_AP1R_EL1_NMI); + } else { + cs->icc_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_update(cs); } @@ -1772,7 +1968,7 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); - int prio; + uint64_t prio; if (icv_access(env, HCR_FMO | HCR_IMO)) { return icv_rpr_read(env, ri); @@ -1792,6 +1988,22 @@ static uint64_t icc_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri) } } + if (cs->nmi_support) { + /* NMI info is reported in the high bits of RPR */ + if (arm_feature(env, ARM_FEATURE_EL3) && !arm_is_secure(env)) { + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NMI; + } + } else { + if (cs->icc_apr[GICV3_G1NS][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NSNMI; + } + if (cs->icc_apr[GICV3_G1][0] & ICC_AP1R_EL1_NMI) { + prio |= ICC_RPR_EL1_NMI; + } + } + } + trace_gicv3_icc_rpr_read(gicv3_redist_affid(cs), prio); return prio; } @@ -2376,6 +2588,7 @@ static const ARMCPRegInfo gicv3_cpuif_reginfo[] = { .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 12, .opc2 = 6, .type = ARM_CP_IO | ARM_CP_NO_RAW, .access = PL1_RW, .accessfn = gicv3_fiq_access, + .fgt = FGT_ICC_IGRPENN_EL1, .readfn = icc_igrpen_read, .writefn = icc_igrpen_write, }, @@ -2384,6 +2597,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, }, @@ -2469,6 +2683,15 @@ static const ARMCPRegInfo gicv3_cpuif_icc_apxr23_reginfo[] = { }, }; +static const ARMCPRegInfo gicv3_cpuif_gicv3_nmi_reginfo[] = { + { .name = "ICC_NMIAR1_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 12, .crm = 9, .opc2 = 5, + .type = ARM_CP_IO | ARM_CP_NO_RAW, + .access = PL1_R, .accessfn = gicv3_irq_access, + .readfn = icc_nmiar1_read, + }, +}; + static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); @@ -2490,7 +2713,11 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); - cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + if (cs->nmi_support) { + cs->ich_apr[grp][regno] = value & (0xFFFFFFFFU | ICV_AP1R_EL1_NMI); + } else { + cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU; + } gicv3_cpuif_virt_irq_fiq_update(cs); } @@ -2607,6 +2834,11 @@ static void ich_lr_write(CPUARMState *env, const ARMCPRegInfo *ri, 8 - cs->vpribits, 0); } + /* Enforce RES0 bit in NMI field when FEAT_GICv3_NMI is not implemented */ + if (!cs->nmi_support) { + value &= ~ICH_LR_EL2_NMI; + } + cs->ich_lr_el2[regno] = value; gicv3_cpuif_virt_update(cs); } @@ -2671,6 +2903,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, @@ -2678,6 +2911,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, @@ -2685,6 +2919,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, @@ -2716,6 +2951,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, @@ -2726,6 +2962,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, @@ -2733,6 +2970,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, @@ -2743,6 +2981,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, @@ -2750,6 +2989,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, @@ -2757,6 +2997,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, @@ -2764,6 +3005,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, @@ -2810,9 +3052,24 @@ 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); + /* + * If the CPU implements FEAT_NMI and FEAT_GICv3 it must also + * implement FEAT_GICv3_NMI, which is the CPU interface part + * of NMI support. This is distinct from whether the GIC proper + * (redistributors and distributor) have NMI support. In QEMU + * that is a property of the GIC device in s->nmi_support; + * cs->nmi_support indicates the CPU interface's support. + */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + cs->nmi_support = true; + define_arm_cp_regs(cpu, gicv3_cpuif_gicv3_nmi_reginfo); + } + /* * The CPU implementation specifies the number of supported * bits of physical priority. For backwards compatibility @@ -2883,6 +3140,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, @@ -2905,6 +3163,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..d8207acb22 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -89,6 +89,29 @@ static int gicd_ns_access(GICv3State *s, int irq) return extract32(s->gicd_nsacr[irq / 16], (irq % 16) * 2, 2); } +static void gicd_write_bitmap_reg(GICv3State *s, MemTxAttrs attrs, + uint32_t *bmp, maskfn *maskfn, + int offset, uint32_t val) +{ + /* + * Helper routine to implement writing to a "set" register + * (GICD_INMIR, etc). + * Semantics implemented here: + * RAZ/WI for SGIs, PPIs, unimplemented IRQs + * Bits corresponding to Group 0 or Secure Group 1 interrupts RAZ/WI. + * offset should be the offset in bytes of the register from the start + * of its group. + */ + int irq = offset * 8; + + if (irq < GIC_INTERNAL || irq >= s->num_irq) { + return; + } + val &= mask_group_and_nsacr(s, attrs, maskfn, irq); + *gic_bmp_ptr32(bmp, irq) = val; + gicv3_update(s, irq, 32); +} + static void gicd_write_set_bitmap_reg(GICv3State *s, MemTxAttrs attrs, uint32_t *bmp, maskfn *maskfn, @@ -389,10 +412,11 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, * by GICD_TYPER.IDbits) * MBIS == 0 (message-based SPIs not supported) * SecurityExtn == 1 if security extns supported + * NMI = 1 if Non-maskable interrupt property is 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, @@ -402,6 +426,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, bool dvis = s->revision >= 4; *data = (1 << 25) | (1 << 24) | (dvis << 18) | (sec_extn << 10) | + (s->nmi_support << GICD_TYPER_NMI_SHIFT) | (s->lpi_enable << GICD_TYPER_LPIS_SHIFT) | (0xf << 19) | itlinesnumber; return true; @@ -543,6 +568,11 @@ static bool gicd_readl(GICv3State *s, hwaddr offset, /* RAZ/WI since affinity routing is always enabled */ *data = 0; return true; + case GICD_INMIR ... GICD_INMIR + 0x7f: + *data = (!s->nmi_support) ? 0 : + gicd_read_bitmap_reg(s, attrs, s->nmi, NULL, + offset - GICD_INMIR); + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; @@ -564,7 +594,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: @@ -752,6 +782,12 @@ static bool gicd_writel(GICv3State *s, hwaddr offset, case GICD_SPENDSGIR ... GICD_SPENDSGIR + 0xf: /* RAZ/WI since affinity routing is always enabled */ return true; + case GICD_INMIR ... GICD_INMIR + 0x7f: + if (s->nmi_support) { + gicd_write_bitmap_reg(s, attrs, s->nmi, NULL, + offset - GICD_INMIR, value); + } + return true; case GICD_IROUTER ... GICD_IROUTER + 0x1fdf: { uint64_t r; @@ -773,7 +809,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 +874,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 +915,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..bf31158470 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, ResetType type) { - 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, type); + } /* 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..0b97362cd2 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, ResetType type) { - 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..35539c099f 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, ResetType type) { - 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, type); + } 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..9ea6b8e218 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, ResetType type) { - 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, type); + } if (s->migration_blocker) { DPRINTF("Cannot put kernel gic state, no kernel interface\n"); @@ -803,6 +805,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + if (s->nmi_support) { + error_setg(errp, "NMI is not supported with the in-kernel GIC"); + return; + } + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); for (i = 0; i < s->num_cpu; i++) { @@ -876,8 +883,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 +896,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 +904,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 c92ceecc16..90b238fac0 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -35,6 +35,15 @@ static int gicr_ns_access(GICv3CPUState *cs, int irq) return extract32(cs->gicr_nsacr, irq * 2, 2); } +static void gicr_write_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, + uint32_t *reg, uint32_t val) +{ + /* Helper routine to implement writing to a "set" register */ + val &= mask_group(cs, attrs); + *reg = val; + gicv3_redist_update(cs); +} + static void gicr_write_set_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs, uint32_t *reg, uint32_t val) { @@ -111,6 +120,7 @@ static void update_for_one_lpi(GICv3CPUState *cs, int irq, ((prio == hpp->prio) && (irq <= hpp->irq))) { hpp->irq = irq; hpp->prio = prio; + hpp->nmi = false; /* LPIs and vLPIs are always non-secure Grp1 interrupts */ hpp->grp = GICV3_G1NS; } @@ -147,6 +157,7 @@ static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase, int i, bit; hpp->prio = 0xff; + hpp->nmi = false; for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1); @@ -232,6 +243,7 @@ static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs) if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) { cs->hppvlpi.prio = 0xff; + cs->hppvlpi.nmi = false; return; } @@ -406,6 +418,10 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, *data = value; return MEMTX_OK; } + case GICR_INMIR0: + *data = cs->gic->nmi_support ? + gicr_read_bitmap_reg(cs, attrs, cs->gicr_inmir0) : 0; + return MEMTX_OK; case GICR_ICFGR0: case GICR_ICFGR1: { @@ -494,7 +510,7 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, /* Only the ProcessorSleep bit is writable. When the guest sets * 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. @@ -555,6 +571,12 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset, gicv3_redist_update(cs); return MEMTX_OK; } + case GICR_INMIR0: + if (cs->gic->nmi_support) { + gicr_write_bitmap_reg(cs, attrs, &cs->gicr_inmir0, value); + } + return MEMTX_OK; + case GICR_ICFGR0: /* Register is all RAZ/WI or RAO/WI bits */ return MEMTX_OK; @@ -601,7 +623,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 +690,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 +749,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 +808,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 1f7763964c..98f3cf59bc 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); @@ -2466,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; } @@ -2506,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), @@ -2551,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), @@ -2565,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 } @@ -2580,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() }; @@ -2648,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) @@ -2690,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 @@ -2721,7 +2737,7 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_nvic; device_class_set_props(dc, props_nvic); - dc->reset = armv7m_nvic_reset; + device_class_set_legacy_reset(dc, armv7m_nvic_reset); dc->realize = armv7m_nvic_realize; } diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c new file mode 100644 index 0000000000..126b711b94 --- /dev/null +++ b/hw/intc/aspeed_intc.c @@ -0,0 +1,361 @@ +/* + * ASPEED INTC Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/intc/aspeed_intc.h" +#include "hw/irq.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/registerfields.h" +#include "qapi/error.h" + +/* INTC Registers */ +REG32(GICINT128_EN, 0x1000) +REG32(GICINT128_STATUS, 0x1004) +REG32(GICINT129_EN, 0x1100) +REG32(GICINT129_STATUS, 0x1104) +REG32(GICINT130_EN, 0x1200) +REG32(GICINT130_STATUS, 0x1204) +REG32(GICINT131_EN, 0x1300) +REG32(GICINT131_STATUS, 0x1304) +REG32(GICINT132_EN, 0x1400) +REG32(GICINT132_STATUS, 0x1404) +REG32(GICINT133_EN, 0x1500) +REG32(GICINT133_STATUS, 0x1504) +REG32(GICINT134_EN, 0x1600) +REG32(GICINT134_STATUS, 0x1604) +REG32(GICINT135_EN, 0x1700) +REG32(GICINT135_STATUS, 0x1704) +REG32(GICINT136_EN, 0x1800) +REG32(GICINT136_STATUS, 0x1804) + +#define GICINT_STATUS_BASE R_GICINT128_STATUS + +static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + trace_aspeed_intc_update_irq(irq, level); + qemu_set_irq(s->output_pins[irq], level); +} + +/* + * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. + * Utilize "address & 0x0f00" to get the irq and irq output pin index + * The value of irq should be 0 to num_ints. + * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. + */ +static void aspeed_intc_set_irq(void *opaque, int irq, int level) +{ + AspeedINTCState *s = (AspeedINTCState *)opaque; + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t status_addr = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); + uint32_t select = 0; + uint32_t enable; + int i; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + trace_aspeed_intc_set_irq(irq, level); + enable = s->enable[irq]; + + if (!level) { + return; + } + + for (i = 0; i < aic->num_lines; i++) { + if (s->orgates[irq].levels[i]) { + if (enable & BIT(i)) { + select |= BIT(i); + } + } + } + + if (!select) { + return; + } + + trace_aspeed_intc_select(select); + + if (s->mask[irq] || s->regs[status_addr]) { + /* + * a. mask is not 0 means in ISR mode + * sources interrupt routine are executing. + * b. status register value is not 0 means previous + * source interrupt does not be executed, yet. + * + * save source interrupt to pending variable. + */ + s->pending[irq] |= select; + trace_aspeed_intc_pending_irq(irq, s->pending[irq]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status register + */ + s->regs[status_addr] = select; + trace_aspeed_intc_trigger_irq(irq, s->regs[status_addr]); + aspeed_intc_update(s, irq, 1); + } +} + +static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + uint32_t addr = offset >> 2; + uint32_t value = 0; + + if (addr >= ASPEED_INTC_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + value = s->regs[addr]; + trace_aspeed_intc_read(offset, size, value); + + return value; +} + +static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t addr = offset >> 2; + uint32_t old_enable; + uint32_t change; + uint32_t irq; + + if (addr >= ASPEED_INTC_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_aspeed_intc_write(offset, size, data); + + switch (addr) { + case R_GICINT128_EN: + case R_GICINT129_EN: + case R_GICINT130_EN: + case R_GICINT131_EN: + case R_GICINT132_EN: + case R_GICINT133_EN: + case R_GICINT134_EN: + case R_GICINT135_EN: + case R_GICINT136_EN: + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* + * These registers are used for enable sources interrupt and + * mask and unmask source interrupt while executing source ISR. + */ + + /* disable all source interrupt */ + if (!data && !s->enable[irq]) { + s->regs[addr] = data; + return; + } + + old_enable = s->enable[irq]; + s->enable[irq] |= data; + + /* enable new source interrupt */ + if (old_enable != s->enable[irq]) { + trace_aspeed_intc_enable(s->enable[irq]); + s->regs[addr] = data; + return; + } + + /* mask and unmask source interrupt */ + change = s->regs[addr] ^ data; + if (change & data) { + s->mask[irq] &= ~change; + trace_aspeed_intc_unmask(change, s->mask[irq]); + } else { + s->mask[irq] |= change; + trace_aspeed_intc_mask(change, s->mask[irq]); + } + s->regs[addr] = data; + break; + case R_GICINT128_STATUS: + case R_GICINT129_STATUS: + case R_GICINT130_STATUS: + case R_GICINT131_STATUS: + case R_GICINT132_STATUS: + case R_GICINT133_STATUS: + case R_GICINT134_STATUS: + case R_GICINT135_STATUS: + case R_GICINT136_STATUS: + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* clear status */ + s->regs[addr] &= ~data; + + /* + * These status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + if (data == 0xffffffff) { + return; + } + + /* All source ISR execution are done */ + if (!s->regs[addr]) { + trace_aspeed_intc_all_isr_done(irq); + if (s->pending[irq]) { + /* + * handle pending source interrupt + * notify firmware which source interrupt are pending + * by setting status register + */ + s->regs[addr] = s->pending[irq]; + s->pending[irq] = 0; + trace_aspeed_intc_trigger_irq(irq, s->regs[addr]); + aspeed_intc_update(s, irq, 1); + } else { + /* clear irq */ + trace_aspeed_intc_clear_irq(irq, 0); + aspeed_intc_update(s, irq, 0); + } + } + break; + default: + s->regs[addr] = data; + break; + } + + return; +} + +static const MemoryRegionOps aspeed_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void aspeed_intc_instance_init(Object *obj) +{ + AspeedINTCState *s = ASPEED_INTC(obj); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + int i; + + assert(aic->num_ints <= ASPEED_INTC_NR_INTS); + for (i = 0; i < aic->num_ints; i++) { + object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i], + TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->orgates[i]), "num-lines", + aic->num_lines, &error_abort); + } +} + +static void aspeed_intc_reset(DeviceState *dev) +{ + AspeedINTCState *s = ASPEED_INTC(dev); + + memset(s->regs, 0, sizeof(s->regs)); + memset(s->enable, 0, sizeof(s->enable)); + memset(s->mask, 0, sizeof(s->mask)); + memset(s->pending, 0, sizeof(s->pending)); +} + +static void aspeed_intc_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedINTCState *s = ASPEED_INTC(dev); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + int i; + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, + TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); + + sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); + + for (i = 0; i < aic->num_ints; i++) { + if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { + return; + } + sysbus_init_irq(sbd, &s->output_pins[i]); + } +} + +static void aspeed_intc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "ASPEED INTC Controller"; + dc->realize = aspeed_intc_realize; + device_class_set_legacy_reset(dc, aspeed_intc_reset); + dc->vmsd = NULL; +} + +static const TypeInfo aspeed_intc_info = { + .name = TYPE_ASPEED_INTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_init = aspeed_intc_instance_init, + .instance_size = sizeof(AspeedINTCState), + .class_init = aspeed_intc_class_init, + .class_size = sizeof(AspeedINTCClass), + .abstract = true, +}; + +static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 INTC Controller"; + aic->num_lines = 32; + aic->num_ints = 9; +} + +static const TypeInfo aspeed_2700_intc_info = { + .name = TYPE_ASPEED_2700_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700_intc_class_init, +}; + +static void aspeed_intc_register_types(void) +{ + type_register_static(&aspeed_intc_info); + type_register_static(&aspeed_2700_intc_info); +} + +type_init(aspeed_intc_register_types); diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c index 5ba06c5262..55fe51a667 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), @@ -343,7 +343,7 @@ static void aspeed_vic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_vic_realize; - dc->reset = aspeed_vic_reset; + device_class_set_legacy_reset(dc, aspeed_vic_reset); dc->desc = "ASPEED Interrupt Controller (New)"; dc->vmsd = &vmstate_aspeed_vic; } diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 4513fad16f..4a42fcf60d 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), @@ -223,7 +223,7 @@ static void bcm2835_ic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = bcm2835_ic_reset; + device_class_set_legacy_reset(dc, bcm2835_ic_reset); dc->vmsd = &vmstate_bcm2835_ic; } diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index b0589df188..197a0e2ccf 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), @@ -388,7 +388,7 @@ static void bcm2836_control_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = bcm2836_control_reset; + device_class_set_legacy_reset(dc, bcm2836_control_reset); dc->vmsd = &vmstate_bcm2836_control; } diff --git a/hw/intc/etraxfs_pic.c b/hw/intc/etraxfs_pic.c deleted file mode 100644 index bd37d1cca0..0000000000 --- a/hw/intc/etraxfs_pic.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * QEMU ETRAX Interrupt Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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 "qemu/module.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "qom/object.h" - -#define D(x) - -#define R_RW_MASK 0 -#define R_R_VECT 1 -#define R_R_MASKED_VECT 2 -#define R_R_NMI 3 -#define R_R_GURU 4 -#define R_MAX 5 - -#define TYPE_ETRAX_FS_PIC "etraxfs-pic" -DECLARE_INSTANCE_CHECKER(struct etrax_pic, ETRAX_FS_PIC, - TYPE_ETRAX_FS_PIC) - -struct etrax_pic -{ - SysBusDevice parent_obj; - - MemoryRegion mmio; - qemu_irq parent_irq; - qemu_irq parent_nmi; - uint32_t regs[R_MAX]; -}; - -static void pic_update(struct etrax_pic *fs) -{ - uint32_t vector = 0; - int i; - - fs->regs[R_R_MASKED_VECT] = fs->regs[R_R_VECT] & fs->regs[R_RW_MASK]; - - /* The ETRAX interrupt controller signals interrupts to the core - through an interrupt request wire and an irq vector bus. If - multiple interrupts are simultaneously active it chooses vector - 0x30 and lets the sw choose the priorities. */ - if (fs->regs[R_R_MASKED_VECT]) { - uint32_t mv = fs->regs[R_R_MASKED_VECT]; - for (i = 0; i < 31; i++) { - if (mv & 1) { - vector = 0x31 + i; - /* Check for multiple interrupts. */ - if (mv > 1) - vector = 0x30; - break; - } - mv >>= 1; - } - } - - qemu_set_irq(fs->parent_irq, vector); -} - -static uint64_t -pic_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct etrax_pic *fs = opaque; - uint32_t rval; - - rval = fs->regs[addr >> 2]; - D(printf("%s %x=%x\n", __func__, addr, rval)); - return rval; -} - -static void pic_write(void *opaque, hwaddr addr, - uint64_t value, unsigned int size) -{ - struct etrax_pic *fs = opaque; - D(printf("%s addr=%x val=%x\n", __func__, addr, value)); - - if (addr == R_RW_MASK) { - fs->regs[R_RW_MASK] = value; - pic_update(fs); - } -} - -static const MemoryRegionOps pic_ops = { - .read = pic_read, - .write = pic_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void nmi_handler(void *opaque, int irq, int level) -{ - struct etrax_pic *fs = (void *)opaque; - uint32_t mask; - - mask = 1 << irq; - if (level) - fs->regs[R_R_NMI] |= mask; - else - fs->regs[R_R_NMI] &= ~mask; - - qemu_set_irq(fs->parent_nmi, !!fs->regs[R_R_NMI]); -} - -static void irq_handler(void *opaque, int irq, int level) -{ - struct etrax_pic *fs = (void *)opaque; - - if (irq >= 30) { - nmi_handler(opaque, irq, level); - return; - } - - irq -= 1; - fs->regs[R_R_VECT] &= ~(1 << irq); - fs->regs[R_R_VECT] |= (!!level << irq); - pic_update(fs); -} - -static void etraxfs_pic_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - struct etrax_pic *s = ETRAX_FS_PIC(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - qdev_init_gpio_in(dev, irq_handler, 32); - sysbus_init_irq(sbd, &s->parent_irq); - sysbus_init_irq(sbd, &s->parent_nmi); - - memory_region_init_io(&s->mmio, obj, &pic_ops, s, - "etraxfs-pic", R_MAX * 4); - sysbus_init_mmio(sbd, &s->mmio); -} - -static const TypeInfo etraxfs_pic_info = { - .name = TYPE_ETRAX_FS_PIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct etrax_pic), - .instance_init = etraxfs_pic_init, -}; - -static void etraxfs_pic_register_types(void) -{ - type_register_static(&etraxfs_pic_info); -} - -type_init(etraxfs_pic_register_types) diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index a289510bdb..afecef1e15 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; } } @@ -334,7 +334,7 @@ static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_combiner_reset; + device_class_set_legacy_reset(dc, exynos4210_combiner_reset); device_class_set_props(dc, exynos4210_combiner_properties); dc->vmsd = &vmstate_exynos4210_combiner; } diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 8d29b40ca1..8ddbf554c6 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -280,6 +280,8 @@ static inline void gic_set_active(GICState *s, int irq, int cpu) static inline void gic_clear_active(GICState *s, int irq, int cpu) { + unsigned int cm; + if (gic_is_vcpu(cpu)) { uint32_t *entry = gic_get_lr_entry(s, irq, cpu); GICH_LR_CLEAR_ACTIVE(*entry); @@ -301,11 +303,13 @@ static inline void gic_clear_active(GICState *s, int irq, int cpu) * the GIC is secure. */ if (!s->security_extn || GIC_DIST_TEST_GROUP(phys_irq, 1 << rcpu)) { - GIC_DIST_CLEAR_ACTIVE(phys_irq, 1 << rcpu); + cm = phys_irq < GIC_INTERNAL ? 1 << rcpu : ALL_CPU_MASK; + GIC_DIST_CLEAR_ACTIVE(phys_irq, cm); } } } else { - GIC_DIST_CLEAR_ACTIVE(irq, 1 << cpu); + cm = irq < GIC_INTERNAL ? 1 << cpu : ALL_CPU_MASK; + GIC_DIST_CLEAR_ACTIVE(irq, cm); } } diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 29d5cdc1b6..bc9f518fe8 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -52,6 +52,8 @@ #define GICD_SGIR 0x0F00 #define GICD_CPENDSGIR 0x0F10 #define GICD_SPENDSGIR 0x0F20 +#define GICD_INMIR 0x0F80 +#define GICD_INMIRnE 0x3B00 #define GICD_IROUTER 0x6000 #define GICD_IDREGS 0xFFD0 @@ -68,6 +70,7 @@ #define GICD_CTLR_E1NWF (1U << 7) #define GICD_CTLR_RWP (1U << 31) +#define GICD_TYPER_NMI_SHIFT 9 #define GICD_TYPER_LPIS_SHIFT 17 /* 16 bits EventId */ @@ -109,6 +112,7 @@ #define GICR_ICFGR1 (GICR_SGI_OFFSET + 0x0C04) #define GICR_IGRPMODR0 (GICR_SGI_OFFSET + 0x0D00) #define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00) +#define GICR_INMIR0 (GICR_SGI_OFFSET + 0x0F80) /* VLPI redistributor registers, offsets from VLPI_base */ #define GICR_VPROPBASER (GICR_VLPI_OFFSET + 0x70) @@ -190,6 +194,10 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICC_CTLR_EL3_A3V (1U << 15) #define ICC_CTLR_EL3_NDS (1U << 17) +#define ICC_AP1R_EL1_NMI (1ULL << 63) +#define ICC_RPR_EL1_NSNMI (1ULL << 62) +#define ICC_RPR_EL1_NMI (1ULL << 63) + #define ICH_VMCR_EL2_VENG0_SHIFT 0 #define ICH_VMCR_EL2_VENG0 (1U << ICH_VMCR_EL2_VENG0_SHIFT) #define ICH_VMCR_EL2_VENG1_SHIFT 1 @@ -238,6 +246,7 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICH_LR_EL2_PRIORITY_SHIFT 48 #define ICH_LR_EL2_PRIORITY_LENGTH 8 #define ICH_LR_EL2_PRIORITY_MASK (0xffULL << ICH_LR_EL2_PRIORITY_SHIFT) +#define ICH_LR_EL2_NMI (1ULL << 59) #define ICH_LR_EL2_GROUP (1ULL << 60) #define ICH_LR_EL2_HW (1ULL << 61) #define ICH_LR_EL2_STATE_SHIFT 62 @@ -269,6 +278,9 @@ FIELD(GICR_VPENDBASER, VALID, 63, 1) #define ICH_VTR_EL2_PREBITS_SHIFT 26 #define ICH_VTR_EL2_PRIBITS_SHIFT 29 +#define ICV_AP1R_EL1_NMI (1ULL << 63) +#define ICV_RPR_EL1_NMI (1ULL << 63) + /* ITS Registers */ FIELD(GITS_BASER, SIZE, 0, 8) @@ -507,6 +519,7 @@ FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH) /* Special interrupt IDs */ #define INTID_SECURE 1020 #define INTID_NONSECURE 1021 +#define INTID_NMI 1022 #define INTID_SPURIOUS 1023 /* Functions internal to the emulated GICv3 */ diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index dfd53275f6..166a3cba1e 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -12,7 +12,6 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "qemu/log.h" #include "trace.h" #include "hw/intc/intc.h" @@ -39,11 +38,12 @@ static bool goldfish_pic_get_statistics(InterruptStatsProvider *obj, return true; } -static void goldfish_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void goldfish_pic_print_info(InterruptStatsProvider *obj, GString *buf) { GoldfishPICState *s = GOLDFISH_PIC(obj); - monitor_printf(mon, "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n", - s->idx, s->pending, s->enabled); + g_string_append_printf(buf, + "goldfish-pic.%d: pending=0x%08x enabled=0x%08x\n", + s->idx, s->pending, s->enabled); } static void goldfish_pic_update(GoldfishPICState *s) @@ -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() @@ -191,7 +191,7 @@ static void goldfish_pic_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); - dc->reset = goldfish_pic_reset; + device_class_set_legacy_reset(dc, goldfish_pic_reset); dc->realize = goldfish_pic_realize; dc->vmsd = &vmstate_goldfish_pic; ic->get_statistics = goldfish_pic_get_statistics; diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index 3bfe2544b7..37ac63fd80 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->reset = grlib_irqmp_reset; + dc->realize = grlib_irqmp_realize; + device_class_set_legacy_reset(dc, 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 13048a2735..729498f1df 100644 --- a/hw/intc/heathrow_pic.c +++ b/hw/intc/heathrow_pic.c @@ -141,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), @@ -154,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() @@ -188,7 +188,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->reset = heathrow_reset; + device_class_set_legacy_reset(dc, heathrow_reset); dc->vmsd = &vmstate_heathrow; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index 9405080595..ede7fe1835 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; @@ -170,15 +170,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); @@ -228,6 +227,7 @@ static void pic_reset(DeviceState *dev) PICCommonState *s = PIC_COMMON(dev); s->elcr = 0; + s->ltim = 0; pic_init_reset(s); } @@ -247,10 +247,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; @@ -357,10 +354,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); } @@ -412,7 +407,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; @@ -424,12 +419,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); @@ -450,7 +445,7 @@ static void i8259_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_parent_realize(dc, pic_realize, &k->parent_realize); - dc->reset = pic_reset; + device_class_set_legacy_reset(dc, pic_reset); } static const TypeInfo i8259_info = { diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index f7033778bb..6f7ae2b741 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -28,7 +28,6 @@ #include "hw/isa/i8259_internal.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "qapi/error.h" static int irq_level[16]; @@ -51,7 +50,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) @@ -134,25 +133,44 @@ static bool pic_get_statistics(InterruptStatsProvider *obj, return true; } -static void pic_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void pic_print_info(InterruptStatsProvider *obj, GString *buf) { PICCommonState *s = PIC_COMMON(obj); pic_dispatch_pre_save(s); - monitor_printf(mon, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " - "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", - s->master ? 0 : 1, s->irr, s->imr, s->isr, s->priority_add, - s->irq_base, s->read_reg_select, s->elcr, - s->special_fully_nested_mode); + g_string_append_printf(buf, "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d " + "irq_base=%02x rr_sel=%d elcr=%02x fnm=%d\n", + s->master ? 0 : 1, s->irr, s->imr, s->isr, + s->priority_add, + s->irq_base, s->read_reg_select, s->elcr, + 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), @@ -170,6 +188,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..e1c9ce769d 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), @@ -346,7 +346,7 @@ static void imx_avic_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_imx_avic; - dc->reset = imx_avic_reset; + device_class_set_legacy_reset(dc, imx_avic_reset); dc->desc = "i.MX Advanced Vector Interrupt Controller"; } diff --git a/hw/intc/imx_gpcv2.c b/hw/intc/imx_gpcv2.c index 237d5f97eb..9e5cf28371 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() }, @@ -106,7 +106,7 @@ static void imx_gpcv2_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = imx_gpcv2_reset; + device_class_set_legacy_reset(dc, imx_gpcv2_reset); dc->vmsd = &vmstate_imx_gpcv2; dc->desc = "i.MX GPCv2 Module"; } diff --git a/hw/intc/ioapic-stub.c b/hw/intc/ioapic-stub.c new file mode 100644 index 0000000000..4dcd86248d --- /dev/null +++ b/hw/intc/ioapic-stub.c @@ -0,0 +1,29 @@ +/* + * ioapic.c IOAPIC emulation logic + * + * Copyright (c) 2004-2005 Fabrice Bellard + * + * Split the ioapic logic from apic.c + * Xiantao Zhang + * + * 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 "hw/intc/ioapic.h" + +void ioapic_eoi_broadcast(int vector) +{ +} diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 264262959d..e73c8d4f07 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 = { @@ -494,7 +493,7 @@ static void ioapic_class_init(ObjectClass *klass, void *data) * migration, otherwise first 24 gsi routes will be invalid. */ k->post_load = ioapic_update_kvm_routes; - dc->reset = ioapic_reset_common; + device_class_set_legacy_reset(dc, ioapic_reset_common); device_class_set_props(dc, ioapic_properties); } diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index aa5f760871..769896353a 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -23,10 +23,9 @@ #include "qapi/error.h" #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, @@ -59,59 +58,62 @@ static bool ioapic_get_statistics(InterruptStatsProvider *obj, return true; } -static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap) +static void ioapic_irr_dump(GString *buf, const char *name, uint32_t bitmap) { int i; - monitor_printf(mon, "%-10s ", name); + g_string_append_printf(buf, "%-10s ", name); if (bitmap == 0) { - monitor_printf(mon, "(none)\n"); + g_string_append_printf(buf, "(none)\n"); return; } for (i = 0; i < IOAPIC_NUM_PINS; i++) { if (bitmap & (1 << i)) { - monitor_printf(mon, "%-2u ", i); + g_string_append_printf(buf, "%-2u ", i); } } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } -static void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) +static void ioapic_print_redtbl(GString *buf, IOAPICCommonState *s) { static const char *delm_str[] = { "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"}; uint32_t remote_irr = 0; int i; - monitor_printf(mon, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x", - s->version, s->id, s->ioregsel); + g_string_append_printf(buf, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x", + s->version, s->id, s->ioregsel); if (s->ioregsel) { - monitor_printf(mon, " (redir[%u])\n", - (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1); + g_string_append_printf(buf, " (redir[%u])\n", + (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1); } else { - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } for (i = 0; i < IOAPIC_NUM_PINS; i++) { uint64_t entry = s->ioredtbl[i]; uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >> IOAPIC_LVT_DELIV_MODE_SHIFT); - monitor_printf(mon, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64 - " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", - i, entry, - (entry >> IOAPIC_LVT_DEST_SHIFT) & - (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf), - entry & IOAPIC_VECTOR_MASK, - entry & IOAPIC_LVT_POLARITY ? "active-lo" : "active-hi", - entry & IOAPIC_LVT_TRIGGER_MODE ? "level" : "edge", - entry & IOAPIC_LVT_MASKED ? "masked" : "", - delm_str[delm], - entry & IOAPIC_LVT_DEST_MODE ? "logical" : "physical"); + g_string_append_printf(buf, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64 + " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", + i, entry, + (entry >> IOAPIC_LVT_DEST_SHIFT) & + (entry & IOAPIC_LVT_DEST_MODE ? 0xff : 0xf), + entry & IOAPIC_VECTOR_MASK, + entry & IOAPIC_LVT_POLARITY + ? "active-lo" : "active-hi", + entry & IOAPIC_LVT_TRIGGER_MODE + ? "level" : "edge", + entry & IOAPIC_LVT_MASKED ? "masked" : "", + delm_str[delm], + entry & IOAPIC_LVT_DEST_MODE + ? "logical" : "physical"); remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ? (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0; } - ioapic_irr_dump(mon, " IRR", s->irr); - ioapic_irr_dump(mon, " Remote IRR", remote_irr); + ioapic_irr_dump(buf, " IRR", s->irr); + ioapic_irr_dump(buf, " Remote IRR", remote_irr); } void ioapic_reset_common(DeviceState *dev) @@ -152,6 +154,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,18 +165,20 @@ 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++; } -static void ioapic_print_info(InterruptStatsProvider *obj, - Monitor *mon) +static void ioapic_print_info(InterruptStatsProvider *obj, GString *buf) { IOAPICCommonState *s = IOAPIC_COMMON(obj); ioapic_dispatch_pre_save(s); - ioapic_print_redtbl(mon, s); + ioapic_print_redtbl(buf, s); } static const VMStateDescription vmstate_ioapic_common = { @@ -182,7 +187,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 4b8ec3f28a..97d1af5ccc 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) @@ -56,14 +57,9 @@ static void extioi_setirq(void *opaque, int irq, int level) LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); trace_loongarch_extioi_setirq(irq, level); if (level) { - /* - * s->isr should be used in vmstate structure, - * but it not support 'unsigned long', - * so we have to switch it. - */ - set_bit(irq, (unsigned long *)s->isr); + set_bit32(irq, s->isr); } else { - clear_bit(irq, (unsigned long *)s->isr); + clear_bit32(irq, s->isr); } extioi_update_irq(s, irq, level); } @@ -96,7 +92,7 @@ static MemTxResult extioi_readw(void *opaque, hwaddr addr, uint64_t *data, index = (offset - EXTIOI_COREISR_START) >> 2; /* using attrs to get current cpu index */ cpu = attrs.requester_id; - *data = s->coreisr[cpu][index]; + *data = s->cpu[cpu].coreisr[index]; break; case EXTIOI_COREMAP_START ... EXTIOI_COREMAP_END - 1: index = (offset - EXTIOI_COREMAP_START) >> 2; @@ -129,12 +125,69 @@ static inline void extioi_enable_irq(LoongArchExtIOI *s, int index,\ } } +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; + val = val >> 8; + + if (!(s->status & BIT(EXTIOI_ENABLE_CPU_ENCODE))) { + cpu = ctz32(cpu); + cpu = (cpu >= 4) ? 0 : cpu; + } + + if (s->sw_coremap[irq + i] == cpu) { + continue; + } + + if (notify && test_bit32(irq + i, 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); @@ -152,20 +205,7 @@ static MemTxResult 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; @@ -189,9 +229,9 @@ static MemTxResult extioi_writew(void *opaque, hwaddr addr, index = (offset - EXTIOI_COREISR_START) >> 2; /* using attrs to get current cpu index */ cpu = attrs.requester_id; - old_data = s->coreisr[cpu][index]; - s->coreisr[cpu][index] = old_data & ~val; - /* write 1 to clear interrrupt */ + 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) { @@ -204,33 +244,8 @@ static MemTxResult 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; @@ -248,65 +263,189 @@ static const MemoryRegionOps extioi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static const VMStateDescription vmstate_loongarch_extioi = { - .name = TYPE_LOONGARCH_EXTIOI, +static MemTxResult extioi_virt_readw(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + + switch (addr) { + case EXTIOI_VIRT_FEATURES: + *data = s->features; + break; + case EXTIOI_VIRT_CONFIG: + *data = s->status; + break; + default: + g_assert_not_reached(); + } + + return MEMTX_OK; +} + +static MemTxResult extioi_virt_writew(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(opaque); + + switch (addr) { + case EXTIOI_VIRT_FEATURES: + return MEMTX_ACCESS_ERROR; + + case EXTIOI_VIRT_CONFIG: + /* + * extioi features can only be set at disabled status + */ + if ((s->status & BIT(EXTIOI_ENABLE)) && val) { + return MEMTX_ACCESS_ERROR; + } + + s->status = val & s->features; + break; + default: + g_assert_not_reached(); + } + return MEMTX_OK; +} + +static const MemoryRegionOps extioi_virt_ops = { + .read_with_attrs = extioi_virt_readw, + .write_with_attrs = extioi_virt_writew, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +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); + + if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { + memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops, + s, "extioi_virt", EXTIOI_VIRT_SIZE); + sysbus_init_mmio(sbd, &s->virt_extend); + s->features |= EXTIOI_VIRT_HAS_FEATURES; + } else { + s->status |= BIT(EXTIOI_ENABLE); + } + + 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 void loongarch_extioi_reset(DeviceState *d) +{ + LoongArchExtIOI *s = LOONGARCH_EXTIOI(d); + + s->status = 0; +} + +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 = 3, + .minimum_version_id = 3, + .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_UINT32(features, LoongArchExtIOI), + VMSTATE_UINT32(status, LoongArchExtIOI), 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_BIT("has-virtualization-extension", LoongArchExtIOI, features, + EXTIOI_HAS_VIRT_EXTENSION, 0), + 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_legacy_reset(dc, loongarch_extioi_reset); + 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 aa4bf9eb74..2ae1a42c46 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -1,273 +1,68 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * LoongArch ipi interrupt support + * LoongArch IPI interrupt support * - * Copyright (C) 2021 Loongson Technology Corporation Limited + * Copyright (C) 2024 Loongson Technology Corporation Limited */ #include "qemu/osdep.h" -#include "hw/sysbus.h" +#include "hw/boards.h" #include "hw/intc/loongarch_ipi.h" -#include "hw/irq.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "exec/address-spaces.h" -#include "hw/loongarch/virt.h" -#include "migration/vmstate.h" -#include "target/loongarch/internals.h" -#include "trace.h" +#include "target/loongarch/cpu.h" -static uint64_t loongarch_ipi_readl(void *opaque, hwaddr addr, unsigned size) +static AddressSpace *get_iocsr_as(CPUState *cpu) { - IPICore *s = opaque; - uint64_t ret = 0; - int index = 0; - - addr &= 0xff; - switch (addr) { - case CORE_STATUS_OFF: - ret = s->status; - break; - case CORE_EN_OFF: - ret = s->en; - break; - case CORE_SET_OFF: - ret = 0; - break; - case CORE_CLEAR_OFF: - ret = 0; - break; - case CORE_BUF_20 ... CORE_BUF_38 + 4: - index = (addr - CORE_BUF_20) >> 2; - ret = s->buf[index]; - break; - default: - qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); - break; - } - - trace_loongarch_ipi_read(size, (uint64_t)addr, ret); - return ret; + return LOONGARCH_CPU(cpu)->env.address_space_iocsr; } -static void send_ipi_data(CPULoongArchState *env, target_ulong val, target_ulong addr) +static int archid_cmp(const void *a, const void *b) { - int i, mask = 0, data = 0; + CPUArchId *archid_a = (CPUArchId *)a; + CPUArchId *archid_b = (CPUArchId *)b; - /* - * 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, - MEMTXATTRS_UNSPECIFIED, NULL); - for (i = 0; i < 4; i++) { - /* get mask for byte writing */ - if (val & (0x1 << (27 + i))) { - mask |= 0xff << (i * 8); - } - } - } - - data &= mask; - data |= (val >> 32) & ~mask; - address_space_stl(&env->address_space_iocsr, addr, - data, MEMTXATTRS_UNSPECIFIED, NULL); + return archid_a->arch_id - archid_b->arch_id; } -static void ipi_send(uint64_t val) +static CPUArchId *find_cpu_by_archid(MachineState *ms, uint32_t id) { - int cpuid, data; - CPULoongArchState *env; - CPUState *cs; - LoongArchCPU *cpu; + CPUArchId apic_id, *found_cpu; - cpuid = (val >> 16) & 0x3ff; - /* IPI status vector */ - data = 1 << (val & 0x1f); - cs = qemu_get_cpu(cpuid); - cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - address_space_stl(&env->address_space_iocsr, 0x1008, - data, MEMTXATTRS_UNSPECIFIED, NULL); + 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 void mail_send(uint64_t val) +static CPUState *loongarch_cpu_by_arch_id(int64_t arch_id) { - int cpuid; - hwaddr addr; - CPULoongArchState *env; - CPUState *cs; - LoongArchCPU *cpu; + MachineState *machine = MACHINE(qdev_get_machine()); + CPUArchId *archid; - cpuid = (val >> 16) & 0x3ff; - addr = 0x1020 + (val & 0x1c); - cs = qemu_get_cpu(cpuid); - cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - send_ipi_data(env, val, addr); + archid = find_cpu_by_archid(machine, arch_id); + if (archid) { + return CPU(archid->cpu); + } + + return NULL; } -static void any_send(uint64_t val) -{ - int cpuid; - hwaddr addr; - CPULoongArchState *env; - - cpuid = (val >> 16) & 0x3ff; - addr = val & 0xffff; - CPUState *cs = qemu_get_cpu(cpuid); - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - env = &cpu->env; - send_ipi_data(env, val, addr); -} - -static void loongarch_ipi_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - IPICore *s = opaque; - int index = 0; - - addr &= 0xff; - trace_loongarch_ipi_write(size, (uint64_t)addr, val); - switch (addr) { - case CORE_STATUS_OFF: - qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); - break; - case CORE_EN_OFF: - s->en = val; - break; - case CORE_SET_OFF: - s->status |= val; - if (s->status != 0 && (s->status & s->en) != 0) { - qemu_irq_raise(s->irq); - } - break; - case CORE_CLEAR_OFF: - s->status &= ~val; - if (s->status == 0 && s->en != 0) { - qemu_irq_lower(s->irq); - } - break; - case CORE_BUF_20 ... CORE_BUF_38 + 4: - index = (addr - CORE_BUF_20) >> 2; - s->buf[index] = val; - break; - case IOCSR_IPI_SEND: - ipi_send(val); - break; - default: - qemu_log_mask(LOG_UNIMP, "invalid write: %x", (uint32_t)addr); - break; - } -} - -static const MemoryRegionOps loongarch_ipi_ops = { - .read = loongarch_ipi_readl, - .write = loongarch_ipi_writel, - .impl.min_access_size = 4, - .impl.max_access_size = 4, - .valid.min_access_size = 4, - .valid.max_access_size = 8, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -/* mail send and any send only support writeq */ -static void loongarch_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - addr &= 0xfff; - switch (addr) { - case MAIL_SEND_OFFSET: - mail_send(val); - break; - case ANY_SEND_OFFSET: - any_send(val); - break; - default: - break; - } -} - -static const MemoryRegionOps loongarch_ipi64_ops = { - .write = 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_init(Object *obj) -{ - 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")) { - 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", 0x48); - sysbus_init_mmio(sbd, &s->ipi_iocsr_mem[cpu]); - - memory_region_init_io(&s->ipi64_iocsr_mem[cpu], obj, &loongarch_ipi64_ops, - &lams->ipi_core[cpu], "loongarch_ipi64_iocsr", 0x118); - sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem[cpu]); - qdev_init_gpio_out(DEVICE(obj), &lams->ipi_core[cpu].irq, 1); - } -} - -static const VMStateDescription vmstate_ipi_core = { - .name = "ipi-single", - .version_id = 0, - .minimum_version_id = 0, - .fields = (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_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), - VMSTATE_END_OF_LIST() - } -}; - static void loongarch_ipi_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); - dc->vmsd = &vmstate_loongarch_ipi; + licc->get_iocsr_as = get_iocsr_as; + licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; } -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, +static const TypeInfo loongarch_ipi_types[] = { + { + .name = TYPE_LOONGARCH_IPI, + .parent = TYPE_LOONGSON_IPI_COMMON, + .class_init = loongarch_ipi_class_init, + } }; -static void loongarch_ipi_register_types(void) -{ - type_register_static(&loongarch_ipi_info); -} - -type_init(loongarch_ipi_register_types) +DEFINE_TYPES(loongarch_ipi_types) diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index b36d6d76e4..ecf3ed0267 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -32,7 +32,7 @@ static void loongarch_msi_mem_write(void *opaque, hwaddr addr, */ 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); } @@ -49,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); @@ -59,12 +81,11 @@ 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(), }; @@ -72,6 +93,8 @@ 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); } diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 3380b09807..b958180591 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -6,12 +6,16 @@ */ #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) { @@ -26,7 +30,11 @@ static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) 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 = ctz64(val); s->intisr &= ~MAKE_64BIT_MASK(irq, 1); @@ -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->reset = loongarch_pch_pic_reset; + dc->realize = loongarch_pch_pic_realize; + device_class_set_legacy_reset(dc, 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_ipi.c b/hw/intc/loongson_ipi.c new file mode 100644 index 0000000000..4e08f03510 --- /dev/null +++ b/hw/intc/loongson_ipi.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson ipi interrupt support + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/intc/loongson_ipi.h" +#include "qapi/error.h" +#include "target/mips/cpu.h" + +static AddressSpace *get_iocsr_as(CPUState *cpu) +{ + if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) { + return &MIPS_CPU(cpu)->env.iocsr.as; + } + + return NULL; +} + +static const MemoryRegionOps loongson_ipi_core_ops = { + .read_with_attrs = loongson_ipi_core_readl, + .write_with_attrs = loongson_ipi_core_writel, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongson_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongsonIPICommonState *sc = LOONGSON_IPI_COMMON(dev); + LoongsonIPIState *s = LOONGSON_IPI(dev); + LoongsonIPIClass *lic = LOONGSON_IPI_GET_CLASS(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + Error *local_err = NULL; + + lic->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + s->ipi_mmio_mem = g_new0(MemoryRegion, sc->num_cpu); + for (unsigned i = 0; i < sc->num_cpu; i++) { + g_autofree char *name = g_strdup_printf("loongson_ipi_cpu%d_mmio", i); + + memory_region_init_io(&s->ipi_mmio_mem[i], OBJECT(dev), + &loongson_ipi_core_ops, &sc->cpu[i], name, 0x48); + sysbus_init_mmio(sbd, &s->ipi_mmio_mem[i]); + } +} + +static void loongson_ipi_unrealize(DeviceState *dev) +{ + LoongsonIPIState *s = LOONGSON_IPI(dev); + LoongsonIPIClass *k = LOONGSON_IPI_GET_CLASS(dev); + + g_free(s->ipi_mmio_mem); + + k->parent_unrealize(dev); +} + +static void loongson_ipi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongsonIPIClass *lic = LOONGSON_IPI_CLASS(klass); + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + + device_class_set_parent_realize(dc, loongson_ipi_realize, + &lic->parent_realize); + device_class_set_parent_unrealize(dc, loongson_ipi_unrealize, + &lic->parent_unrealize); + licc->get_iocsr_as = get_iocsr_as; + licc->cpu_by_arch_id = cpu_by_arch_id; +} + +static const TypeInfo loongson_ipi_types[] = { + { + .name = TYPE_LOONGSON_IPI, + .parent = TYPE_LOONGSON_IPI_COMMON, + .instance_size = sizeof(LoongsonIPIState), + .class_size = sizeof(LoongsonIPIClass), + .class_init = loongson_ipi_class_init, + } +}; + +DEFINE_TYPES(loongson_ipi_types) diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c new file mode 100644 index 0000000000..a6ce0181f6 --- /dev/null +++ b/hw/intc/loongson_ipi_common.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson IPI interrupt common support + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/intc/loongson_ipi_common.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "trace.h" + +MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + IPICore *s = opaque; + uint64_t ret = 0; + int index = 0; + + addr &= 0xff; + switch (addr) { + case CORE_STATUS_OFF: + ret = s->status; + break; + case CORE_EN_OFF: + ret = s->en; + break; + case CORE_SET_OFF: + ret = 0; + break; + case CORE_CLEAR_OFF: + ret = 0; + break; + case CORE_BUF_20 ... CORE_BUF_38 + 4: + index = (addr - CORE_BUF_20) >> 2; + ret = s->buf[index]; + break; + default: + qemu_log_mask(LOG_UNIMP, "invalid read: %x", (uint32_t)addr); + break; + } + + trace_loongson_ipi_read(size, (uint64_t)addr, ret); + *data = ret; + + return MEMTX_OK; +} + +static MemTxResult loongson_ipi_iocsr_readl(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + LoongsonIPICommonState *ipi = opaque; + IPICore *s; + + if (attrs.requester_id >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + + s = &ipi->cpu[attrs.requester_id]; + return loongson_ipi_core_readl(s, addr, data, size, attrs); +} + +static MemTxResult send_ipi_data(LoongsonIPICommonState *ipi, CPUState *cpu, + uint64_t val, hwaddr addr, MemTxAttrs attrs) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + int i, mask = 0, data = 0; + AddressSpace *iocsr_as = licc->get_iocsr_as(cpu); + + if (!iocsr_as) { + return MEMTX_DECODE_ERROR; + } + + /* + * 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_le(iocsr_as, addr, attrs, NULL); + for (i = 0; i < 4; i++) { + /* get mask for byte writing */ + if (val & (0x1 << (27 + i))) { + mask |= 0xff << (i * 8); + } + } + } + + data &= mask; + data |= (val >> 32) & ~mask; + address_space_stl_le(iocsr_as, addr, data, attrs, NULL); + + return MEMTX_OK; +} + +static MemTxResult mail_send(LoongsonIPICommonState *ipi, + uint64_t val, MemTxAttrs attrs) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + uint32_t cpuid; + hwaddr addr; + CPUState *cs; + + cpuid = extract32(val, 16, 10); + cs = licc->cpu_by_arch_id(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; + return send_ipi_data(ipi, cs, val, addr, attrs); +} + +static MemTxResult any_send(LoongsonIPICommonState *ipi, + uint64_t val, MemTxAttrs attrs) +{ + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + uint32_t cpuid; + hwaddr addr; + CPUState *cs; + + cpuid = extract32(val, 16, 10); + cs = licc->cpu_by_arch_id(cpuid); + if (cs == NULL) { + return MEMTX_DECODE_ERROR; + } + + /* override requester_id */ + addr = val & 0xffff; + attrs.requester_id = cs->cpu_index; + return send_ipi_data(ipi, cs, val, addr, attrs); +} + +MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + IPICore *s = opaque; + LoongsonIPICommonState *ipi = s->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(ipi); + int index = 0; + uint32_t cpuid; + uint8_t vector; + CPUState *cs; + + addr &= 0xff; + trace_loongson_ipi_write(size, (uint64_t)addr, val); + switch (addr) { + case CORE_STATUS_OFF: + qemu_log_mask(LOG_GUEST_ERROR, "can not be written"); + break; + case CORE_EN_OFF: + s->en = val; + break; + case CORE_SET_OFF: + s->status |= val; + if (s->status != 0 && (s->status & s->en) != 0) { + qemu_irq_raise(s->irq); + } + break; + case CORE_CLEAR_OFF: + s->status &= ~val; + if (s->status == 0 && s->en != 0) { + qemu_irq_lower(s->irq); + } + break; + case CORE_BUF_20 ... CORE_BUF_38 + 4: + index = (addr - CORE_BUF_20) >> 2; + s->buf[index] = val; + break; + case IOCSR_IPI_SEND: + cpuid = extract32(val, 16, 10); + /* IPI status vector */ + vector = extract8(val, 0, 5); + cs = licc->cpu_by_arch_id(cpuid); + if (cs == NULL || cs->cpu_index >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + loongson_ipi_core_writel(&ipi->cpu[cs->cpu_index], 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 MemTxResult loongson_ipi_iocsr_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) +{ + LoongsonIPICommonState *ipi = opaque; + IPICore *s; + + if (attrs.requester_id >= ipi->num_cpu) { + return MEMTX_DECODE_ERROR; + } + + s = &ipi->cpu[attrs.requester_id]; + return loongson_ipi_core_writel(s, addr, val, size, attrs); +} + +static const MemoryRegionOps loongson_ipi_iocsr_ops = { + .read_with_attrs = loongson_ipi_iocsr_readl, + .write_with_attrs = loongson_ipi_iocsr_writel, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 8, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* mail send and any send only support writeq */ +static MemTxResult loongson_ipi_writeq(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + LoongsonIPICommonState *ipi = opaque; + MemTxResult ret = MEMTX_OK; + + addr &= 0xfff; + switch (addr) { + case MAIL_SEND_OFFSET: + ret = mail_send(ipi, val, attrs); + break; + case ANY_SEND_OFFSET: + ret = any_send(ipi, val, attrs); + break; + default: + break; + } + + return ret; +} + +static const MemoryRegionOps loongson_ipi64_ops = { + .write_with_attrs = loongson_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 loongson_ipi_common_realize(DeviceState *dev, Error **errp) +{ + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(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; + } + + memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), + &loongson_ipi_iocsr_ops, + s, "loongson_ipi_iocsr", 0x48); + + /* loongson_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), + &loongson_ipi64_ops, + s, "loongson_ipi64_iocsr", 0x118); + sysbus_init_mmio(sbd, &s->ipi64_iocsr_mem); + + s->cpu = g_new0(IPICore, s->num_cpu); + for (i = 0; i < s->num_cpu; i++) { + s->cpu[i].ipi = s; + + qdev_init_gpio_out(dev, &s->cpu[i].irq, 1); + } +} + +static void loongson_ipi_common_unrealize(DeviceState *dev) +{ + LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); + + g_free(s->cpu); +} + +static const VMStateDescription vmstate_ipi_core = { + .name = "ipi-single", + .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, IPI_MBX_NUM * 2), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_loongson_ipi_common = { + .name = "loongson_ipi", + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, LoongsonIPICommonState, + num_cpu, vmstate_ipi_core, + IPICore), + VMSTATE_END_OF_LIST() + } +}; + +static Property ipi_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); + + device_class_set_parent_realize(dc, loongson_ipi_common_realize, + &licc->parent_realize); + device_class_set_parent_unrealize(dc, loongson_ipi_common_unrealize, + &licc->parent_unrealize); + device_class_set_props(dc, ipi_common_properties); + dc->vmsd = &vmstate_loongson_ipi_common; +} + +static const TypeInfo loongarch_ipi_common_types[] = { + { + .name = TYPE_LOONGSON_IPI_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongsonIPICommonState), + .class_size = sizeof(LoongsonIPICommonClass), + .class_init = loongson_ipi_common_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(loongarch_ipi_common_types) 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..b4471e185a 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #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" @@ -26,16 +26,16 @@ static bool m68k_irqc_get_statistics(InterruptStatsProvider *obj, return true; } -static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void m68k_irqc_print_info(InterruptStatsProvider *obj, GString *buf) { M68KIRQCState *s = M68K_IRQC(obj); - monitor_printf(mon, "m68k-irqc: ipr=0x%x\n", s->ipr); + g_string_append_printf(buf, "m68k-irqc: ipr=0x%x\n", s->ipr); } 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,20 +79,27 @@ 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; + device_class_set_legacy_reset(dc, m68k_irqc_reset); dc->vmsd = &vmstate_m68k_irqc; ic->get_statistics = m68k_irqc_get_statistics; ic->print_info = m68k_irqc_print_info; diff --git a/hw/intc/meson.build b/hw/intc/meson.build index bcbf22ff51..6bfdc4eb33 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_ASPEED_SOC', if_true: files('aspeed_intc.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'), if_false: files('ioapic-stub.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,9 +67,9 @@ 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_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) +specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.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 deleted file mode 100644 index cf63212a88..0000000000 --- a/hw/intc/nios2_vic.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Vectored Interrupt Controller for nios2 processor - * - * Copyright (c) 2022 Neuroblade - * - * Interface: - * QOM property "cpu": link to the Nios2 CPU (must be set) - * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines - * IRQ should be connected to nios2 IRQ0. - * - * Reference: "Embedded Peripherals IP User Guide - * for Intel® Quartus® Prime Design Suite: 21.4" - * Chapter 38 "Vectored Interrupt Controller Core" - * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" - -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/bitops.h" -#include "qemu/log.h" -#include "qom/object.h" -#include "hw/intc/nios2_vic.h" -#include "cpu.h" - - -enum { - INT_CONFIG0 = 0, - INT_CONFIG31 = 31, - INT_ENABLE = 32, - INT_ENABLE_SET = 33, - INT_ENABLE_CLR = 34, - INT_PENDING = 35, - INT_RAW_STATUS = 36, - SW_INTERRUPT = 37, - SW_INTERRUPT_SET = 38, - SW_INTERRUPT_CLR = 39, - VIC_CONFIG = 40, - VIC_STATUS = 41, - VEC_TBL_BASE = 42, - VEC_TBL_ADDR = 43, - CSR_COUNT /* Last! */ -}; - -/* Requested interrupt level (INT_CONFIG[0:5]) */ -static inline uint32_t vic_int_config_ril(const Nios2VIC *vic, int irq_num) -{ - return extract32(vic->int_config[irq_num], 0, 6); -} - -/* Requested NMI (INT_CONFIG[6]) */ -static inline uint32_t vic_int_config_rnmi(const Nios2VIC *vic, int irq_num) -{ - return extract32(vic->int_config[irq_num], 6, 1); -} - -/* Requested register set (INT_CONFIG[7:12]) */ -static inline uint32_t vic_int_config_rrs(const Nios2VIC *vic, int irq_num) -{ - return extract32(vic->int_config[irq_num], 7, 6); -} - -static inline uint32_t vic_config_vec_size(const Nios2VIC *vic) -{ - return 1 << (2 + extract32(vic->vic_config, 0, 3)); -} - -static inline uint32_t vic_int_pending(const Nios2VIC *vic) -{ - return (vic->int_raw_status | vic->sw_int) & vic->int_enable; -} - -static void vic_update_irq(Nios2VIC *vic) -{ - Nios2CPU *cpu = NIOS2_CPU(vic->cpu); - uint32_t pending = vic_int_pending(vic); - int irq = -1; - int max_ril = 0; - /* Note that if RIL is 0 for an interrupt it is effectively disabled */ - - vic->vec_tbl_addr = 0; - vic->vic_status = 0; - - if (pending == 0) { - qemu_irq_lower(vic->output_int); - return; - } - - for (int i = 0; i < NIOS2_VIC_MAX_IRQ; i++) { - if (pending & BIT(i)) { - int ril = vic_int_config_ril(vic, i); - if (ril > max_ril) { - irq = i; - max_ril = ril; - } - } - } - - if (irq < 0) { - qemu_irq_lower(vic->output_int); - return; - } - - vic->vec_tbl_addr = irq * vic_config_vec_size(vic) + vic->vec_tbl_base; - vic->vic_status = irq | BIT(31); - - /* - * In hardware, the interface between the VIC and the CPU is via the - * External Interrupt Controller interface, where the interrupt controller - * presents the CPU with a packet of data containing: - * - Requested Handler Address (RHA): 32 bits - * - Requested Register Set (RRS) : 6 bits - * - Requested Interrupt Level (RIL) : 6 bits - * - Requested NMI flag (RNMI) : 1 bit - * In our emulation, we implement this by writing the data directly to - * fields in the CPU object and then raising the IRQ line to tell - * the CPU that we've done so. - */ - - cpu->rha = vic->vec_tbl_addr; - cpu->ril = max_ril; - cpu->rrs = vic_int_config_rrs(vic, irq); - cpu->rnmi = vic_int_config_rnmi(vic, irq); - - qemu_irq_raise(vic->output_int); -} - -static void vic_set_irq(void *opaque, int irq_num, int level) -{ - Nios2VIC *vic = opaque; - - vic->int_raw_status = deposit32(vic->int_raw_status, irq_num, 1, !!level); - vic_update_irq(vic); -} - -static void nios2_vic_reset(DeviceState *dev) -{ - Nios2VIC *vic = NIOS2_VIC(dev); - - memset(&vic->int_config, 0, sizeof(vic->int_config)); - vic->vic_config = 0; - vic->int_raw_status = 0; - vic->int_enable = 0; - vic->sw_int = 0; - vic->vic_status = 0; - vic->vec_tbl_base = 0; - vic->vec_tbl_addr = 0; -} - -static uint64_t nios2_vic_csr_read(void *opaque, hwaddr offset, unsigned size) -{ - Nios2VIC *vic = opaque; - int index = offset / 4; - - switch (index) { - case INT_CONFIG0 ... INT_CONFIG31: - return vic->int_config[index - INT_CONFIG0]; - case INT_ENABLE: - return vic->int_enable; - case INT_PENDING: - return vic_int_pending(vic); - case INT_RAW_STATUS: - return vic->int_raw_status; - case SW_INTERRUPT: - return vic->sw_int; - case VIC_CONFIG: - return vic->vic_config; - case VIC_STATUS: - return vic->vic_status; - case VEC_TBL_BASE: - return vic->vec_tbl_base; - case VEC_TBL_ADDR: - return vic->vec_tbl_addr; - default: - return 0; - } -} - -static void nios2_vic_csr_write(void *opaque, hwaddr offset, uint64_t value, - unsigned size) -{ - Nios2VIC *vic = opaque; - int index = offset / 4; - - switch (index) { - case INT_CONFIG0 ... INT_CONFIG31: - vic->int_config[index - INT_CONFIG0] = value; - break; - case INT_ENABLE: - vic->int_enable = value; - break; - case INT_ENABLE_SET: - vic->int_enable |= value; - break; - case INT_ENABLE_CLR: - vic->int_enable &= ~value; - break; - case SW_INTERRUPT: - vic->sw_int = value; - break; - case SW_INTERRUPT_SET: - vic->sw_int |= value; - break; - case SW_INTERRUPT_CLR: - vic->sw_int &= ~value; - break; - case VIC_CONFIG: - vic->vic_config = value; - break; - case VEC_TBL_BASE: - vic->vec_tbl_base = value; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "nios2-vic: write to invalid CSR address %#" - HWADDR_PRIx "\n", offset); - } - - vic_update_irq(vic); -} - -static const MemoryRegionOps nios2_vic_csr_ops = { - .read = nios2_vic_csr_read, - .write = nios2_vic_csr_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { .min_access_size = 4, .max_access_size = 4 } -}; - -static void nios2_vic_realize(DeviceState *dev, Error **errp) -{ - Nios2VIC *vic = NIOS2_VIC(dev); - - if (!vic->cpu) { - /* This is a programming error in the code using this device */ - error_setg(errp, "nios2-vic 'cpu' link property was not set"); - return; - } - - sysbus_init_irq(SYS_BUS_DEVICE(dev), &vic->output_int); - qdev_init_gpio_in(dev, vic_set_irq, NIOS2_VIC_MAX_IRQ); - - memory_region_init_io(&vic->csr, OBJECT(dev), &nios2_vic_csr_ops, vic, - "nios2.vic.csr", CSR_COUNT * sizeof(uint32_t)); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &vic->csr); -} - -static Property nios2_vic_properties[] = { - DEFINE_PROP_LINK("cpu", Nios2VIC, cpu, TYPE_CPU, CPUState *), - DEFINE_PROP_END_OF_LIST() -}; - -static const VMStateDescription nios2_vic_vmstate = { - .name = "nios2-vic", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]){ - VMSTATE_UINT32_ARRAY(int_config, Nios2VIC, 32), - VMSTATE_UINT32(vic_config, Nios2VIC), - VMSTATE_UINT32(int_raw_status, Nios2VIC), - VMSTATE_UINT32(int_enable, Nios2VIC), - VMSTATE_UINT32(sw_int, Nios2VIC), - VMSTATE_UINT32(vic_status, Nios2VIC), - VMSTATE_UINT32(vec_tbl_base, Nios2VIC), - VMSTATE_UINT32(vec_tbl_addr, Nios2VIC), - VMSTATE_END_OF_LIST() - }, -}; - -static void nios2_vic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = nios2_vic_reset; - dc->realize = nios2_vic_realize; - dc->vmsd = &nios2_vic_vmstate; - device_class_set_props(dc, nios2_vic_properties); -} - -static const TypeInfo nios2_vic_info = { - .name = TYPE_NIOS2_VIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Nios2VIC), - .class_init = nios2_vic_class_init, -}; - -static void nios2_vic_register_types(void) -{ - type_register_static(&nios2_vic_info); -} - -type_init(nios2_vic_register_types); diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index d7183d035e..a98358d92e 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; @@ -50,8 +50,6 @@ struct omap_intr_handler_s { int level_only; uint32_t size; - uint8_t revision; - /* state */ uint32_t new_agr[2]; int sir_intr[2]; @@ -60,7 +58,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 +66,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 +86,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 +107,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]; @@ -133,30 +131,10 @@ 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; - uint32_t rise; - - struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; - int n = irq & 31; - - if (req) { - rise = ~bank->inputs & (1 << n); - if (rise) { - bank->irqs |= bank->inputs |= rise; - omap_inth_update(ih, 0); - omap_inth_update(ih, 1); - } - } else - bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; -} - 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 +212,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 +314,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 +344,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 +358,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(), }; @@ -406,7 +384,7 @@ static void omap_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = omap_inth_reset; + device_class_set_legacy_reset(dc, omap_inth_reset); device_class_set_props(dc, omap_intc_properties); /* Reason: pointer property "clk" */ dc->user_creatable = false; @@ -414,277 +392,16 @@ static void omap_intc_class_init(ObjectClass *klass, void *data) } static const TypeInfo omap_intc_info = { - .name = "omap-intc", - .parent = TYPE_OMAP_INTC, + .name = TYPE_OMAP_INTC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(OMAPIntcState), .instance_init = omap_intc_init, .class_init = omap_intc_class_init, }; -static uint64_t omap2_inth_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int offset = addr; - int bank_no, line_no; - struct omap_intr_handler_bank_s *bank = NULL; - - if ((offset & 0xf80) == 0x80) { - bank_no = (offset & 0x60) >> 5; - if (bank_no < s->nbanks) { - offset &= ~0x60; - bank = &s->bank[bank_no]; - } else { - OMAP_BAD_REG(addr); - return 0; - } - } - - switch (offset) { - case 0x00: /* INTC_REVISION */ - return s->revision; - - case 0x10: /* INTC_SYSCONFIG */ - return (s->autoidle >> 2) & 1; - - case 0x14: /* INTC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* INTC_SIR_IRQ */ - return s->sir_intr[0]; - - case 0x44: /* INTC_SIR_FIQ */ - return s->sir_intr[1]; - - case 0x48: /* INTC_CONTROL */ - return (!s->mask) << 2; /* GLOBALMASK */ - - case 0x4c: /* INTC_PROTECTION */ - return 0; - - case 0x50: /* INTC_IDLE */ - return s->autoidle & 3; - - /* Per-bank registers */ - case 0x80: /* INTC_ITR */ - return bank->inputs; - - case 0x84: /* INTC_MIR */ - return bank->mask; - - case 0x88: /* INTC_MIR_CLEAR */ - case 0x8c: /* INTC_MIR_SET */ - return 0; - - case 0x90: /* INTC_ISR_SET */ - return bank->swi; - - case 0x94: /* INTC_ISR_CLEAR */ - return 0; - - case 0x98: /* INTC_PENDING_IRQ */ - return bank->irqs & ~bank->mask & ~bank->fiq; - - case 0x9c: /* INTC_PENDING_FIQ */ - return bank->irqs & ~bank->mask & bank->fiq; - - /* Per-line registers */ - case 0x100 ... 0x300: /* INTC_ILR */ - bank_no = (offset - 0x100) >> 7; - if (bank_no > s->nbanks) - break; - bank = &s->bank[bank_no]; - line_no = (offset & 0x7f) >> 2; - return (bank->priority[line_no] << 2) | - ((bank->fiq >> line_no) & 1); - } - OMAP_BAD_REG(addr); - return 0; -} - -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; - int offset = addr; - int bank_no, line_no; - struct omap_intr_handler_bank_s *bank = NULL; - - if ((offset & 0xf80) == 0x80) { - bank_no = (offset & 0x60) >> 5; - if (bank_no < s->nbanks) { - offset &= ~0x60; - bank = &s->bank[bank_no]; - } else { - OMAP_BAD_REG(addr); - return; - } - } - - switch (offset) { - case 0x10: /* INTC_SYSCONFIG */ - s->autoidle &= 4; - s->autoidle |= (value & 1) << 2; - if (value & 2) { /* SOFTRESET */ - omap_inth_reset(DEVICE(s)); - } - return; - - case 0x48: /* INTC_CONTROL */ - s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ - if (value & 2) { /* NEWFIQAGR */ - qemu_set_irq(s->parent_intr[1], 0); - s->new_agr[1] = ~0; - omap_inth_update(s, 1); - } - if (value & 1) { /* NEWIRQAGR */ - qemu_set_irq(s->parent_intr[0], 0); - s->new_agr[0] = ~0; - omap_inth_update(s, 0); - } - return; - - case 0x4c: /* INTC_PROTECTION */ - /* TODO: Make a bitmap (or sizeof(char)map) of access privileges - * for every register, see Chapter 3 and 4 for privileged mode. */ - if (value & 1) - fprintf(stderr, "%s: protection mode enable attempt\n", - __func__); - return; - - case 0x50: /* INTC_IDLE */ - s->autoidle &= ~3; - s->autoidle |= value & 3; - return; - - /* Per-bank registers */ - case 0x84: /* INTC_MIR */ - bank->mask = value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x88: /* INTC_MIR_CLEAR */ - bank->mask &= ~value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x8c: /* INTC_MIR_SET */ - bank->mask |= value; - return; - - case 0x90: /* INTC_ISR_SET */ - bank->irqs |= bank->swi |= value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x94: /* INTC_ISR_CLEAR */ - bank->swi &= ~value; - bank->irqs = bank->swi & bank->inputs; - return; - - /* Per-line registers */ - case 0x100 ... 0x300: /* INTC_ILR */ - bank_no = (offset - 0x100) >> 7; - if (bank_no > s->nbanks) - break; - bank = &s->bank[bank_no]; - line_no = (offset & 0x7f) >> 2; - bank->priority[line_no] = (value >> 2) & 0x3f; - bank->fiq &= ~(1 << line_no); - bank->fiq |= (value & 1) << line_no; - return; - - case 0x00: /* INTC_REVISION */ - case 0x14: /* INTC_SYSSTATUS */ - case 0x40: /* INTC_SIR_IRQ */ - case 0x44: /* INTC_SIR_FIQ */ - case 0x80: /* INTC_ITR */ - case 0x98: /* INTC_PENDING_IRQ */ - case 0x9c: /* INTC_PENDING_FIQ */ - OMAP_RO_REG(addr); - return; - } - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap2_inth_mem_ops = { - .read = omap2_inth_read, - .write = omap2_inth_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void omap2_intc_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - struct omap_intr_handler_s *s = OMAP_INTC(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - s->level_only = 1; - s->nbanks = 3; - sysbus_init_irq(sbd, &s->parent_intr[0]); - sysbus_init_irq(sbd, &s->parent_intr[1]); - qdev_init_gpio_in(dev, omap_set_intr_noedge, s->nbanks * 32); - memory_region_init_io(&s->mmio, obj, &omap2_inth_mem_ops, s, - "omap2-intc", 0x1000); - sysbus_init_mmio(sbd, &s->mmio); -} - -static void omap2_intc_realize(DeviceState *dev, Error **errp) -{ - struct omap_intr_handler_s *s = OMAP_INTC(dev); - - if (!s->iclk) { - error_setg(errp, "omap2-intc: iclk not connected"); - return; - } - if (!s->fclk) { - error_setg(errp, "omap2-intc: fclk not connected"); - return; - } -} - -static Property omap2_intc_properties[] = { - DEFINE_PROP_UINT8("revision", struct omap_intr_handler_s, - revision, 0x21), - DEFINE_PROP_END_OF_LIST(), -}; - -static void omap2_intc_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->reset = omap_inth_reset; - device_class_set_props(dc, omap2_intc_properties); - /* Reason: pointer property "iclk", "fclk" */ - dc->user_creatable = false; - dc->realize = omap2_intc_realize; -} - -static const TypeInfo omap2_intc_info = { - .name = "omap2-intc", - .parent = TYPE_OMAP_INTC, - .instance_init = omap2_intc_init, - .class_init = omap2_intc_class_init, -}; - -static const TypeInfo omap_intc_type_info = { - .name = TYPE_OMAP_INTC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(omap_intr_handler), - .abstract = true, -}; - static void omap_intc_register_types(void) { - type_register_static(&omap_intc_type_info); type_register_static(&omap_intc_info); - type_register_static(&omap2_intc_info); } type_init(omap_intc_register_types) 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 c757adbe53..2ead4b9ba0 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -41,7 +41,6 @@ #include "hw/pci/msi.h" #include "qapi/error.h" #include "qemu/bitops.h" -#include "qapi/qmp/qerror.h" #include "qemu/module.h" #include "qemu/timer.h" #include "qemu/error-report.h" @@ -610,11 +609,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; @@ -1035,13 +1031,14 @@ static void openpic_cpu_write_internal(void *opaque, hwaddr addr, s_IRQ = IRQ_get_next(opp, &dst->servicing); /* Check queued interrupts. */ n_IRQ = IRQ_get_next(opp, &dst->raised); - src = &opp->src[n_IRQ]; - if (n_IRQ != -1 && - (s_IRQ == -1 || - IVPR_PRIORITY(src->ivpr) > dst->servicing.priority)) { - DPRINTF("Raise OpenPIC INT output cpu %d irq %d", - idx, n_IRQ); - qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]); + if (n_IRQ != -1) { + src = &opp->src[n_IRQ]; + if (s_IRQ == -1 || + IVPR_PRIORITY(src->ivpr) > dst->servicing.priority) { + DPRINTF("Raise OpenPIC INT output cpu %d irq %d", + idx, n_IRQ); + qemu_irq_raise(opp->dst[idx].irqs[OPENPIC_OUTPUT_INT]); + } } break; default: @@ -1394,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), @@ -1406,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), @@ -1421,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), @@ -1435,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() @@ -1446,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() } @@ -1471,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), @@ -1538,9 +1535,7 @@ static void openpic_realize(DeviceState *dev, Error **errp) }; if (opp->nb_cpus > MAX_CPU) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, - TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus, - (uint64_t)0, (uint64_t)MAX_CPU); + error_setg(errp, "property 'nb_cpus' can be at most %d", MAX_CPU); return; } @@ -1623,7 +1618,7 @@ static void openpic_class_init(ObjectClass *oc, void *data) dc->realize = openpic_realize; device_class_set_props(dc, openpic_properties); - dc->reset = openpic_reset; + device_class_set_legacy_reset(dc, openpic_reset); dc->vmsd = &vmstate_openpic; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 557dd0c2bf..838c6b9d99 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -274,7 +274,7 @@ static void kvm_openpic_class_init(ObjectClass *oc, void *data) dc->realize = kvm_openpic_realize; device_class_set_props(dc, kvm_openpic_properties); - dc->reset = kvm_openpic_reset; + device_class_set_legacy_reset(dc, kvm_openpic_reset); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c index cd88443601..a5e2d76315 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), @@ -277,7 +277,7 @@ static void pl190_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pl190_reset; + device_class_set_legacy_reset(dc, pl190_reset); dc->vmsd = &vmstate_pl190; } diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index c7b75ed12e..5bacbce6a4 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -15,9 +15,9 @@ #include "sysemu/cpus.h" #include "sysemu/dma.h" #include "sysemu/reset.h" -#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" @@ -83,28 +83,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 * @@ -208,6 +186,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) { @@ -224,9 +238,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) { @@ -241,12 +253,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; } @@ -257,16 +277,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; } @@ -274,12 +305,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); } @@ -478,6 +523,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)); @@ -932,7 +987,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)) { @@ -1338,6 +1393,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; @@ -1346,8 +1445,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; } @@ -1692,16 +1791,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, @@ -1727,7 +1830,7 @@ static const MemoryRegionOps pnv_xive_pc_ops = { }; static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx, - Monitor *mon) + GString *buf) { uint8_t eq_blk = xive_get_field32(NVT_W1_EQ_BLOCK, nvt->w1); uint32_t eq_idx = xive_get_field32(NVT_W1_EQ_INDEX, nvt->w1); @@ -1736,12 +1839,12 @@ static void xive_nvt_pic_print_info(XiveNVT *nvt, uint32_t nvt_idx, return; } - monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x\n", nvt_idx, - eq_blk, eq_idx, - xive_get_field32(NVT_W4_IPB, nvt->w4)); + g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x\n", + nvt_idx, eq_blk, eq_idx, + xive_get_field32(NVT_W4_IPB, nvt->w4)); } -void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) +void pnv_xive_pic_print_info(PnvXive *xive, GString *buf) { XiveRouter *xrtr = XIVE_ROUTER(xive); uint8_t blk = pnv_xive_block_id(xive); @@ -1754,39 +1857,40 @@ void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) int i; uint64_t xive_nvt_per_subpage; - monitor_printf(mon, "XIVE[%x] #%d Source %08x .. %08x\n", chip_id, blk, - srcno0, srcno0 + nr_ipis - 1); - xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); + g_string_append_printf(buf, "XIVE[%x] #%d Source %08x .. %08x\n", + chip_id, blk, srcno0, srcno0 + nr_ipis - 1); + xive_source_pic_print_info(&xive->ipi_source, srcno0, buf); - monitor_printf(mon, "XIVE[%x] #%d EAT %08x .. %08x\n", chip_id, blk, - srcno0, srcno0 + nr_ipis - 1); + g_string_append_printf(buf, "XIVE[%x] #%d EAT %08x .. %08x\n", + chip_id, blk, srcno0, srcno0 + nr_ipis - 1); for (i = 0; i < nr_ipis; i++) { if (xive_router_get_eas(xrtr, blk, i, &eas)) { break; } if (!xive_eas_is_masked(&eas)) { - xive_eas_pic_print_info(&eas, i, mon); + xive_eas_pic_print_info(&eas, i, buf); } } - monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d ENDT\n", chip_id, blk); i = 0; while (!xive_router_get_end(xrtr, blk, i, &end)) { - xive_end_pic_print_info(&end, i++, mon); + xive_end_pic_print_info(&end, i++, buf); } - monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d END Escalation EAT\n", + chip_id, blk); i = 0; while (!xive_router_get_end(xrtr, blk, i, &end)) { - xive_end_eas_pic_print_info(&end, i++, mon); + xive_end_eas_pic_print_info(&end, i++, buf); } - monitor_printf(mon, "XIVE[%x] #%d NVTT %08x .. %08x\n", chip_id, blk, - 0, XIVE_NVT_COUNT - 1); + g_string_append_printf(buf, "XIVE[%x] #%d NVTT %08x .. %08x\n", + chip_id, blk, 0, XIVE_NVT_COUNT - 1); xive_nvt_per_subpage = pnv_xive_vst_per_subpage(xive, VST_TSEL_VPDT); for (i = 0; i < XIVE_NVT_COUNT; i += xive_nvt_per_subpage) { while (!xive_router_get_nvt(xrtr, blk, i, &nvt)) { - xive_nvt_pic_print_info(&nvt, i++, mon); + xive_nvt_pic_print_info(&nvt, i++, buf); } } } @@ -1887,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, @@ -1922,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, @@ -1987,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 f22ce5ca59..834d32287b 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -13,9 +13,9 @@ #include "target/ppc/cpu.h" #include "sysemu/cpus.h" #include "sysemu/dma.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" #include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/xive2.h" @@ -25,6 +25,7 @@ #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/qtest.h" #include @@ -32,6 +33,16 @@ #undef XIVE2_DEBUG +/* XIVE Sync or Flush Notification Block */ +typedef struct XiveSfnBlock { + uint8_t bytes[32]; +} XiveSfnBlock; + +/* XIVE Thread Sync or Flush Notification Area */ +typedef struct XiveThreadNA { + XiveSfnBlock topo[16]; +} XiveThreadNA; + /* * Virtual structures table (VST) */ @@ -45,16 +56,16 @@ typedef struct XiveVstInfo { static const XiveVstInfo vst_infos[] = { - [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, - [VST_ESB] = { "ESB", 1, 16 }, - [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, + [VST_EAS] = { "EAT", sizeof(Xive2Eas), 16 }, + [VST_ESB] = { "ESB", 1, 16 }, + [VST_END] = { "ENDT", sizeof(Xive2End), 16 }, - [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, - [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, - [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, + [VST_NVP] = { "NVPT", sizeof(Xive2Nvp), 16 }, + [VST_NVG] = { "NVGT", sizeof(Xive2Nvgc), 16 }, + [VST_NVC] = { "NVCT", sizeof(Xive2Nvgc), 16 }, - [VST_IC] = { "IC", 1 /* ? */ , 16 }, /* Topology # */ - [VST_SYNC] = { "SYNC", 1 /* ? */ , 16 }, /* Topology # */ + [VST_IC] = { "IC", 1, /* ? */ 16 }, /* Topology # */ + [VST_SYNC] = { "SYNC", sizeof(XiveThreadNA), 16 }, /* Topology # */ /* * This table contains the backing store pages for the interrupt @@ -162,7 +173,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; } @@ -184,7 +197,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; } @@ -202,6 +217,20 @@ static uint64_t pnv_xive2_vst_addr_indirect(PnvXive2 *xive, uint32_t type, return pnv_xive2_vst_addr_direct(xive, type, vsd, (idx % vst_per_page)); } +static uint8_t pnv_xive2_nvc_table_compress_shift(PnvXive2 *xive) +{ + uint8_t shift = GETFIELD(PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + return shift > 8 ? 0 : shift; +} + +static uint8_t pnv_xive2_nvg_table_compress_shift(PnvXive2 *xive) +{ + uint8_t shift = GETFIELD(PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + return shift > 8 ? 0 : shift; +} + static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, uint32_t idx) { @@ -215,6 +244,11 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, } vsd = xive->vsds[type][blk]; + if (vsd == 0) { + xive2_error(xive, "VST: vsd == 0 block id %d for VST %s %d !?", + blk, info->name, idx); + return 0; + } /* Remote VST access */ if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { @@ -223,6 +257,12 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0; } + if (type == VST_NVG) { + idx >>= pnv_xive2_nvg_table_compress_shift(xive); + } else if (type == VST_NVC) { + idx >>= pnv_xive2_nvc_table_compress_shift(xive); + } + if (VSD_INDIRECT & vsd) { return pnv_xive2_vst_addr_indirect(xive, type, vsd, idx); } @@ -235,12 +275,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; } @@ -251,16 +299,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; } @@ -306,40 +365,115 @@ static int pnv_xive2_write_end(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_end_update(PnvXive2 *xive) +static inline int pnv_xive2_get_current_pir(PnvXive2 *xive) { - uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - int i; + if (!qtest_enabled()) { + PowerPCCPU *cpu = POWERPC_CPU(current_cpu); + return ppc_cpu_pir(cpu); + } + return 0; +} + +/* + * After SW injects a Queue Sync or Cache Flush operation, HW will notify + * SW of the completion of the operation by writing a byte of all 1's (0xff) + * to a specific memory location. The memory location is calculated by first + * looking up a base address in the SYNC VSD using the Topology ID of the + * originating thread as the "block" number. This points to a + * 64k block of memory that is further divided into 128 512 byte chunks of + * memory, which is indexed by the thread id of the requesting thread. + * Finally, this 512 byte chunk of memory is divided into 16 32 byte + * chunks which are indexed by the topology id of the targeted IC's chip. + * The values below are the offsets into that 32 byte chunk of memory for + * each type of cache flush or queue sync operation. + */ +#define PNV_XIVE2_QUEUE_IPI 0x00 +#define PNV_XIVE2_QUEUE_HW 0x01 +#define PNV_XIVE2_QUEUE_NXC 0x02 +#define PNV_XIVE2_QUEUE_INT 0x03 +#define PNV_XIVE2_QUEUE_OS 0x04 +#define PNV_XIVE2_QUEUE_POOL 0x05 +#define PNV_XIVE2_QUEUE_HARD 0x06 +#define PNV_XIVE2_CACHE_ENDC 0x08 +#define PNV_XIVE2_CACHE_ESBC 0x09 +#define PNV_XIVE2_CACHE_EASC 0x0a +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10 +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15 +#define PNV_XIVE2_CACHE_NXC 0x18 + +static int pnv_xive2_inject_notify(PnvXive2 *xive, int type) +{ + uint64_t addr; + int pir = pnv_xive2_get_current_pir(xive); + int thread_nr = PNV10_PIR2THREAD(pir); + int thread_topo_id = PNV10_PIR2CHIP(pir); + int ic_topo_id = xive->chip->chip_id; + uint64_t offset = ic_topo_id * sizeof(XiveSfnBlock); + uint8_t byte = 0xff; + MemTxResult result; + + /* Retrieve the address of requesting thread's notification area */ + addr = pnv_xive2_vst_addr(xive, VST_SYNC, thread_topo_id, thread_nr); + + if (!addr) { + xive2_error(xive, "VST: no SYNC entry %x/%x !?", + thread_topo_id, thread_nr); + return -1; + } + + address_space_stb(&address_space_memory, addr + offset + type, byte, + MEMTXATTRS_UNSPECIFIED, &result); + assert(result == MEMTX_OK); + + return 0; +} + +static int pnv_xive2_end_update(PnvXive2 *xive, uint8_t watch_engine) +{ + uint8_t blk; + uint32_t idx; + int i, spec_reg, data_reg; uint64_t endc_watch[4]; + assert(watch_engine < ARRAY_SIZE(endc_watch)); + + spec_reg = (VC_ENDC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (VC_ENDC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, xive->vc_regs[spec_reg]); + idx = GETFIELD(VC_ENDC_WATCH_INDEX, xive->vc_regs[spec_reg]); + for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { - endc_watch[i] = - cpu_to_be64(xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i]); + endc_watch[i] = cpu_to_be64(xive->vc_regs[data_reg + i]); } return pnv_xive2_vst_write(xive, VST_END, blk, idx, endc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_end_cache_load(PnvXive2 *xive) +static void pnv_xive2_end_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(VC_ENDC_WATCH_INDEX, - xive->vc_regs[(VC_ENDC_WATCH0_SPEC >> 3)]); + uint8_t blk; + uint32_t idx; uint64_t endc_watch[4] = { 0 }; - int i; + int i, spec_reg, data_reg; + + assert(watch_engine < ARRAY_SIZE(endc_watch)); + + spec_reg = (VC_ENDC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (VC_ENDC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + blk = GETFIELD(VC_ENDC_WATCH_BLOCK_ID, xive->vc_regs[spec_reg]); + idx = GETFIELD(VC_ENDC_WATCH_INDEX, xive->vc_regs[spec_reg]); if (pnv_xive2_vst_read(xive, VST_END, blk, idx, endc_watch)) { xive2_error(xive, "VST: no END entry %x/%x !?", blk, idx); } for (i = 0; i < ARRAY_SIZE(endc_watch); i++) { - xive->vc_regs[(VC_ENDC_WATCH0_DATA0 >> 3) + i] = - be64_to_cpu(endc_watch[i]); + xive->vc_regs[data_reg + i] = be64_to_cpu(endc_watch[i]); } } @@ -356,40 +490,92 @@ static int pnv_xive2_write_nvp(Xive2Router *xrtr, uint8_t blk, uint32_t idx, word_number); } -static int pnv_xive2_nvp_update(PnvXive2 *xive) +static int pnv_xive2_get_nvgc(Xive2Router *xrtr, bool crowd, + uint8_t blk, uint32_t idx, + Xive2Nvgc *nvgc) { - uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - int i; + return pnv_xive2_vst_read(PNV_XIVE2(xrtr), crowd ? VST_NVC : VST_NVG, + blk, idx, nvgc); +} + +static int pnv_xive2_write_nvgc(Xive2Router *xrtr, bool crowd, + uint8_t blk, uint32_t idx, + Xive2Nvgc *nvgc) +{ + return pnv_xive2_vst_write(PNV_XIVE2(xrtr), crowd ? VST_NVC : VST_NVG, + blk, idx, nvgc, + XIVE_VST_WORD_ALL); +} + +static int pnv_xive2_nxc_to_table_type(uint8_t nxc_type, uint32_t *table_type) +{ + switch (nxc_type) { + case PC_NXC_WATCH_NXC_NVP: + *table_type = VST_NVP; + break; + case PC_NXC_WATCH_NXC_NVG: + *table_type = VST_NVG; + break; + case PC_NXC_WATCH_NXC_NVC: + *table_type = VST_NVC; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid table type for nxc operation\n"); + return -1; + } + return 0; +} + +static int pnv_xive2_nxc_update(PnvXive2 *xive, uint8_t watch_engine) +{ + uint8_t blk, nxc_type; + uint32_t idx, table_type = -1; + int i, spec_reg, data_reg; uint64_t nxc_watch[4]; + assert(watch_engine < ARRAY_SIZE(nxc_watch)); + + spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + nxc_type = GETFIELD(PC_NXC_WATCH_NXC_TYPE, xive->pc_regs[spec_reg]); + blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); + idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); + + assert(!pnv_xive2_nxc_to_table_type(nxc_type, &table_type)); + for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { - nxc_watch[i] = - cpu_to_be64(xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i]); + nxc_watch[i] = cpu_to_be64(xive->pc_regs[data_reg + i]); } - return pnv_xive2_vst_write(xive, VST_NVP, blk, idx, nxc_watch, + return pnv_xive2_vst_write(xive, table_type, blk, idx, nxc_watch, XIVE_VST_WORD_ALL); } -static void pnv_xive2_nvp_cache_load(PnvXive2 *xive) +static void pnv_xive2_nxc_cache_load(PnvXive2 *xive, uint8_t watch_engine) { - uint8_t blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); - uint32_t idx = GETFIELD(PC_NXC_WATCH_INDEX, - xive->pc_regs[(PC_NXC_WATCH0_SPEC >> 3)]); + uint8_t blk, nxc_type; + uint32_t idx, table_type = -1; uint64_t nxc_watch[4] = { 0 }; - int i; + int i, spec_reg, data_reg; - if (pnv_xive2_vst_read(xive, VST_NVP, blk, idx, nxc_watch)) { - xive2_error(xive, "VST: no NVP entry %x/%x !?", blk, idx); + assert(watch_engine < ARRAY_SIZE(nxc_watch)); + + spec_reg = (PC_NXC_WATCH0_SPEC + watch_engine * 0x40) >> 3; + data_reg = (PC_NXC_WATCH0_DATA0 + watch_engine * 0x40) >> 3; + nxc_type = GETFIELD(PC_NXC_WATCH_NXC_TYPE, xive->pc_regs[spec_reg]); + blk = GETFIELD(PC_NXC_WATCH_BLOCK_ID, xive->pc_regs[spec_reg]); + idx = GETFIELD(PC_NXC_WATCH_INDEX, xive->pc_regs[spec_reg]); + + assert(!pnv_xive2_nxc_to_table_type(nxc_type, &table_type)); + + if (pnv_xive2_vst_read(xive, table_type, blk, idx, nxc_watch)) { + xive2_error(xive, "VST: no NXC entry %x/%x in %s table!?", + blk, idx, vst_infos[table_type].name); } for (i = 0; i < ARRAY_SIZE(nxc_watch); i++) { - xive->pc_regs[(PC_NXC_WATCH0_DATA0 >> 3) + i] = - be64_to_cpu(nxc_watch[i]); + xive->pc_regs[data_reg + i] = be64_to_cpu(nxc_watch[i]); } } @@ -496,6 +682,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)); @@ -547,6 +744,7 @@ static int pnv_xive2_stt_set_data(PnvXive2 *xive, uint64_t val) case CQ_TAR_NVPG: case CQ_TAR_ESB: case CQ_TAR_END: + case CQ_TAR_NVC: xive->tables[tsel][entry] = val; break; default: @@ -607,6 +805,9 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, * entries provisioned by FW (such as skiboot) and resize the * ESB window accordingly. */ + if (memory_region_is_mapped(&xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->esb_mmio, &xsrc->esb_mmio); + } if (!(VSD_INDIRECT & vsd)) { memory_region_set_size(&xsrc->esb_mmio, vst_tsize * SBE_PER_BYTE * (1ull << xsrc->esb_shift)); @@ -622,6 +823,9 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, /* * Backing store pages for the END. */ + if (memory_region_is_mapped(&end_xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->end_mmio, &end_xsrc->esb_mmio); + } if (!(VSD_INDIRECT & vsd)) { memory_region_set_size(&end_xsrc->esb_mmio, (vst_tsize / info->size) * (1ull << end_xsrc->esb_shift)); @@ -646,13 +850,10 @@ static void pnv_xive2_vst_set_exclusive(PnvXive2 *xive, uint8_t type, * Both PC and VC sub-engines are configured as each use the Virtual * Structure Tables */ -static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) +static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd, + uint8_t type, uint8_t blk) { uint8_t mode = GETFIELD(VSD_MODE, vsd); - uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, - xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); - uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, - xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; if (type > VST_ERQ) { @@ -687,6 +888,16 @@ static void pnv_xive2_vst_set_data(PnvXive2 *xive, uint64_t vsd) } } +static void pnv_xive2_vc_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t type = GETFIELD(VC_VSD_TABLE_SELECT, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(VC_VSD_TABLE_ADDRESS, + xive->vc_regs[VC_VSD_TABLE_ADDR >> 3]); + + pnv_xive2_vst_set_data(xive, vsd, type, blk); +} + /* * MMIO handlers */ @@ -930,12 +1141,70 @@ static const MemoryRegionOps pnv_xive2_ic_cq_ops = { }, }; +static uint8_t pnv_xive2_cache_watch_assign(uint64_t engine_mask, + uint64_t *state) +{ + uint8_t val = 0xFF; + int i; + + for (i = 3; i >= 0; i--) { + if (BIT(i) & engine_mask) { + if (!(BIT(i) & *state)) { + *state |= BIT(i); + val = 3 - i; + break; + } + } + } + return val; +} + +static void pnv_xive2_cache_watch_release(uint64_t *state, uint8_t watch_engine) +{ + uint8_t engine_bit = 3 - watch_engine; + + if (*state & BIT(engine_bit)) { + *state &= ~BIT(engine_bit); + } +} + +static uint8_t pnv_xive2_endc_cache_watch_assign(PnvXive2 *xive) +{ + uint64_t engine_mask = GETFIELD(VC_ENDC_CFG_CACHE_WATCH_ASSIGN, + xive->vc_regs[VC_ENDC_CFG >> 3]); + uint64_t state = xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3]; + uint8_t val; + + /* + * We keep track of which engines are currently busy in the + * VC_ENDC_WATCH_ASSIGN register directly. When the firmware reads + * the register, we don't return its value but the ID of an engine + * it can use. + * There are 4 engines. 0xFF means no engine is available. + */ + val = pnv_xive2_cache_watch_assign(engine_mask, &state); + if (val != 0xFF) { + xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3] = state; + } + return val; +} + +static void pnv_xive2_endc_cache_watch_release(PnvXive2 *xive, + uint8_t watch_engine) +{ + uint64_t state = xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3]; + + pnv_xive2_cache_watch_release(&state, watch_engine); + xive->vc_regs[VC_ENDC_WATCH_ASSIGN >> 3] = state; +} + static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint64_t val = 0; uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -954,6 +1223,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) */ @@ -962,24 +1235,44 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, val = xive->vc_regs[reg]; break; + case VC_ENDC_WATCH_ASSIGN: + val = pnv_xive2_endc_cache_watch_assign(xive); + break; + + case VC_ENDC_CFG: + val = xive->vc_regs[reg]; + break; + /* * END cache updates */ case VC_ENDC_WATCH0_SPEC: + case VC_ENDC_WATCH1_SPEC: + case VC_ENDC_WATCH2_SPEC: + case VC_ENDC_WATCH3_SPEC: + watch_engine = (offset - VC_ENDC_WATCH0_SPEC) >> 6; xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT); + pnv_xive2_endc_cache_watch_release(xive, watch_engine); val = xive->vc_regs[reg]; break; case VC_ENDC_WATCH0_DATA0: + case VC_ENDC_WATCH1_DATA0: + case VC_ENDC_WATCH2_DATA0: + case VC_ENDC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the * SPEC register */ - pnv_xive2_end_cache_load(xive); + watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; + pnv_xive2_end_cache_load(xive, watch_engine); val = xive->vc_regs[reg]; break; case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + case VC_ENDC_WATCH1_DATA1 ... VC_ENDC_WATCH1_DATA3: + case VC_ENDC_WATCH2_DATA1 ... VC_ENDC_WATCH2_DATA3: + case VC_ENDC_WATCH3_DATA1 ... VC_ENDC_WATCH3_DATA3: val = xive->vc_regs[reg]; break; @@ -1025,6 +1318,7 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, { PnvXive2 *xive = PNV_XIVE2(opaque); uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1033,7 +1327,7 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, case VC_VSD_TABLE_ADDR: break; case VC_VSD_TABLE_DATA: - pnv_xive2_vst_set_data(xive, val); + pnv_xive2_vc_vst_set_data(xive, val); break; /* @@ -1045,6 +1339,13 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* ESB update */ break; + case VC_ESBC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_ESBC); + break; + + case VC_ESBC_CFG: + break; + /* * EAS cache updates (not modeled) */ @@ -1054,19 +1355,36 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* EAS update */ break; + case VC_EASC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_EASC); + break; + + case VC_ENDC_CFG: + break; + /* * END cache updates */ case VC_ENDC_WATCH0_SPEC: + case VC_ENDC_WATCH1_SPEC: + case VC_ENDC_WATCH2_SPEC: + case VC_ENDC_WATCH3_SPEC: val &= ~VC_ENDC_WATCH_CONFLICT; /* HW will set this bit */ break; case VC_ENDC_WATCH0_DATA1 ... VC_ENDC_WATCH0_DATA3: + case VC_ENDC_WATCH1_DATA1 ... VC_ENDC_WATCH1_DATA3: + case VC_ENDC_WATCH2_DATA1 ... VC_ENDC_WATCH2_DATA3: + case VC_ENDC_WATCH3_DATA1 ... VC_ENDC_WATCH3_DATA3: break; case VC_ENDC_WATCH0_DATA0: + case VC_ENDC_WATCH1_DATA0: + case VC_ENDC_WATCH2_DATA0: + case VC_ENDC_WATCH3_DATA0: /* writing to DATA0 triggers the cache write */ + watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; xive->vc_regs[reg] = val; - pnv_xive2_end_update(xive); + pnv_xive2_end_update(xive, watch_engine); break; @@ -1075,6 +1393,10 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID; break; + case VC_ENDC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_ENDC); + break; + /* * Indirect invalidation */ @@ -1116,12 +1438,43 @@ static const MemoryRegionOps pnv_xive2_ic_vc_ops = { }, }; +static uint8_t pnv_xive2_nxc_cache_watch_assign(PnvXive2 *xive) +{ + uint64_t engine_mask = GETFIELD(PC_NXC_PROC_CONFIG_WATCH_ASSIGN, + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3]); + uint64_t state = xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3]; + uint8_t val; + + /* + * We keep track of which engines are currently busy in the + * PC_NXC_WATCH_ASSIGN register directly. When the firmware reads + * the register, we don't return its value but the ID of an engine + * it can use. + * There are 4 engines. 0xFF means no engine is available. + */ + val = pnv_xive2_cache_watch_assign(engine_mask, &state); + if (val != 0xFF) { + xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3] = state; + } + return val; +} + +static void pnv_xive2_nxc_cache_watch_release(PnvXive2 *xive, + uint8_t watch_engine) +{ + uint64_t state = xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3]; + + pnv_xive2_cache_watch_release(&state, watch_engine); + xive->pc_regs[PC_NXC_WATCH_ASSIGN >> 3] = state; +} + static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint64_t val = -1; uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* @@ -1132,24 +1485,44 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, val = xive->pc_regs[reg]; break; + case PC_NXC_WATCH_ASSIGN: + val = pnv_xive2_nxc_cache_watch_assign(xive); + break; + + case PC_NXC_PROC_CONFIG: + val = xive->pc_regs[reg]; + break; + /* * cache updates */ case PC_NXC_WATCH0_SPEC: + case PC_NXC_WATCH1_SPEC: + case PC_NXC_WATCH2_SPEC: + case PC_NXC_WATCH3_SPEC: + watch_engine = (offset - PC_NXC_WATCH0_SPEC) >> 6; xive->pc_regs[reg] &= ~(PC_NXC_WATCH_FULL | PC_NXC_WATCH_CONFLICT); + pnv_xive2_nxc_cache_watch_release(xive, watch_engine); val = xive->pc_regs[reg]; break; case PC_NXC_WATCH0_DATA0: + case PC_NXC_WATCH1_DATA0: + case PC_NXC_WATCH2_DATA0: + case PC_NXC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the * SPEC register */ - pnv_xive2_nvp_cache_load(xive); + watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; + pnv_xive2_nxc_cache_load(xive, watch_engine); val = xive->pc_regs[reg]; break; case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + case PC_NXC_WATCH1_DATA1 ... PC_NXC_WATCH1_DATA3: + case PC_NXC_WATCH2_DATA1 ... PC_NXC_WATCH2_DATA3: + case PC_NXC_WATCH3_DATA1 ... PC_NXC_WATCH3_DATA3: val = xive->pc_regs[reg]; break; @@ -1173,36 +1546,66 @@ static uint64_t pnv_xive2_ic_pc_read(void *opaque, hwaddr offset, return val; } +static void pnv_xive2_pc_vst_set_data(PnvXive2 *xive, uint64_t vsd) +{ + uint8_t type = GETFIELD(PC_VSD_TABLE_SELECT, + xive->pc_regs[PC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(PC_VSD_TABLE_ADDRESS, + xive->pc_regs[PC_VSD_TABLE_ADDR >> 3]); + + pnv_xive2_vst_set_data(xive, vsd, type, blk); +} + static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); uint32_t reg = offset >> 3; + uint8_t watch_engine; switch (offset) { /* - * VSD table settings. Only taken into account in the VC - * sub-engine because the Xive2Router model combines both VC and PC - * sub-engines + * VSD table settings. + * The Xive2Router model combines both VC and PC sub-engines. We + * allow to configure the tables through both, for the rare cases + * where a table only really needs to be configured for one of + * them (e.g. the NVG table for the presenter). It assumes that + * firmware passes the same address to the VC and PC when tables + * are defined for both, which seems acceptable. */ case PC_VSD_TABLE_ADDR: + break; case PC_VSD_TABLE_DATA: + pnv_xive2_pc_vst_set_data(xive, val); + break; + + case PC_NXC_PROC_CONFIG: break; /* * cache updates */ case PC_NXC_WATCH0_SPEC: + case PC_NXC_WATCH1_SPEC: + case PC_NXC_WATCH2_SPEC: + case PC_NXC_WATCH3_SPEC: val &= ~PC_NXC_WATCH_CONFLICT; /* HW will set this bit */ break; case PC_NXC_WATCH0_DATA1 ... PC_NXC_WATCH0_DATA3: + case PC_NXC_WATCH1_DATA1 ... PC_NXC_WATCH1_DATA3: + case PC_NXC_WATCH2_DATA1 ... PC_NXC_WATCH2_DATA3: + case PC_NXC_WATCH3_DATA1 ... PC_NXC_WATCH3_DATA3: break; case PC_NXC_WATCH0_DATA0: + case PC_NXC_WATCH1_DATA0: + case PC_NXC_WATCH2_DATA0: + case PC_NXC_WATCH3_DATA0: /* writing to DATA0 triggers the cache write */ + watch_engine = (offset - PC_NXC_WATCH0_DATA0) >> 6; xive->pc_regs[reg] = val; - pnv_xive2_nvp_update(xive); + pnv_xive2_nxc_update(xive, watch_engine); break; /* case PC_NXC_FLUSH_CTRL: */ @@ -1210,6 +1613,10 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID; break; + case PC_NXC_FLUSH_INJECT: + pnv_xive2_inject_notify(xive, PNV_XIVE2_CACHE_NXC); + break; + /* * Indirect invalidation */ @@ -1264,6 +1671,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); } @@ -1275,6 +1685,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) { /* @@ -1282,6 +1693,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: @@ -1296,7 +1708,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; @@ -1499,13 +1913,19 @@ static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { /* * Sync MMIO page (write only) */ -#define PNV_XIVE2_SYNC_IPI 0x000 -#define PNV_XIVE2_SYNC_HW 0x080 -#define PNV_XIVE2_SYNC_NxC 0x100 -#define PNV_XIVE2_SYNC_INT 0x180 -#define PNV_XIVE2_SYNC_OS_ESC 0x200 -#define PNV_XIVE2_SYNC_POOL_ESC 0x280 -#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_IPI 0x000 +#define PNV_XIVE2_SYNC_HW 0x080 +#define PNV_XIVE2_SYNC_NxC 0x100 +#define PNV_XIVE2_SYNC_INT 0x180 +#define PNV_XIVE2_SYNC_OS_ESC 0x200 +#define PNV_XIVE2_SYNC_POOL_ESC 0x280 +#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80 static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, unsigned size) @@ -1517,22 +1937,72 @@ static uint64_t pnv_xive2_ic_sync_read(void *opaque, hwaddr offset, return -1; } +/* + * The sync MMIO space spans two pages. The lower page is use for + * queue sync "poll" requests while the upper page is used for queue + * sync "inject" requests. Inject requests require the HW to write + * a byte of all 1's to a predetermined location in memory in order + * to signal completion of the request. Both pages have the same + * layout, so it is easiest to handle both with a single function. + */ static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + int inject_type; + hwaddr pg_offset_mask = (1ull << xive->ic_shift) - 1; - switch (offset) { + /* adjust offset for inject page */ + hwaddr adj_offset = offset & pg_offset_mask; + + switch (adj_offset) { case PNV_XIVE2_SYNC_IPI: + inject_type = PNV_XIVE2_QUEUE_IPI; + break; case PNV_XIVE2_SYNC_HW: + inject_type = PNV_XIVE2_QUEUE_HW; + break; case PNV_XIVE2_SYNC_NxC: + inject_type = PNV_XIVE2_QUEUE_NXC; + break; case PNV_XIVE2_SYNC_INT: + inject_type = PNV_XIVE2_QUEUE_INT; + break; case PNV_XIVE2_SYNC_OS_ESC: + inject_type = PNV_XIVE2_QUEUE_OS; + break; case PNV_XIVE2_SYNC_POOL_ESC: + inject_type = PNV_XIVE2_QUEUE_POOL; + break; case PNV_XIVE2_SYNC_HARD_ESC: + inject_type = PNV_XIVE2_QUEUE_HARD; + break; + case PNV_XIVE2_SYNC_NXC_LD_LCL_NCO: + inject_type = PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO; + break; + case PNV_XIVE2_SYNC_NXC_LD_LCL_CO: + inject_type = PNV_XIVE2_QUEUE_NXC_LD_LCL_CO; + break; + case PNV_XIVE2_SYNC_NXC_ST_LCL_NCI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI; + break; + case PNV_XIVE2_SYNC_NXC_ST_LCL_CI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_LCL_CI; + break; + case PNV_XIVE2_SYNC_NXC_ST_RMT_NCI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI; + break; + case PNV_XIVE2_SYNC_NXC_ST_RMT_CI: + inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_CI; break; default: xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset); + return; + } + + /* Write Queue Sync notification byte if writing to sync inject page */ + if ((offset & ~pg_offset_mask) != 0) { + pnv_xive2_inject_notify(xive, inject_type); } } @@ -1560,6 +2030,18 @@ static uint32_t pnv_xive2_ic_tm_get_pir(PnvXive2 *xive, hwaddr offset) 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; @@ -1582,14 +2064,17 @@ static uint64_t pnv_xive2_ic_tm_indirect_read(void *opaque, hwaddr offset, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + 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; @@ -1599,13 +2084,16 @@ static void pnv_xive2_ic_tm_indirect_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) { PnvXive2 *xive = PNV_XIVE2(opaque); + 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); } } @@ -1614,11 +2102,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, }, }; @@ -1626,17 +2114,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) { @@ -1644,16 +2121,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); } @@ -1663,15 +2131,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); } @@ -1776,6 +2236,12 @@ static void pnv_xive2_reset(void *dev) xive->cq_regs[CQ_XIVE_CFG >> 3] |= SETFIELD(CQ_XIVE_CFG_HYP_HARD_BLOCK_ID, 0ull, xive->chip->chip_id); + /* VC and PC cache watch assign mechanism */ + xive->vc_regs[VC_ENDC_CFG >> 3] = + SETFIELD(VC_ENDC_CFG_CACHE_WATCH_ASSIGN, 0ull, 0b0111); + xive->pc_regs[PC_NXC_PROC_CONFIG >> 3] = + SETFIELD(PC_NXC_PROC_CONFIG_WATCH_ASSIGN, 0ull, 0b0111); + /* Set default page size to 64k */ xive->ic_shift = xive->esb_shift = xive->end_shift = 16; xive->nvc_shift = xive->nvpg_shift = xive->tm_shift = 16; @@ -1958,12 +2424,15 @@ static void pnv_xive2_class_init(ObjectClass *klass, void *data) xrc->write_end = pnv_xive2_write_end; xrc->get_nvp = pnv_xive2_get_nvp; xrc->write_nvp = pnv_xive2_write_nvp; + xrc->get_nvgc = pnv_xive2_get_nvgc; + xrc->write_nvgc = pnv_xive2_write_nvgc; xrc->get_config = pnv_xive2_get_config; xrc->get_block_id = pnv_xive2_get_block_id; xnc->notify = pnv_xive2_notify; xpc->match_nvt = pnv_xive2_match_nvt; + xpc->get_config = pnv_xive2_presenter_get_config; }; static const TypeInfo pnv_xive2_info = { @@ -1986,33 +2455,6 @@ static void pnv_xive2_register_types(void) type_init(pnv_xive2_register_types) -static void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, - Monitor *mon) -{ - uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); - uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); - - if (!xive2_nvp_is_valid(nvp)) { - return; - } - - monitor_printf(mon, " %08x end:%02x/%04x IPB:%02x", - nvp_idx, eq_blk, eq_idx, - xive_get_field32(NVP2_W2_IPB, nvp->w2)); - /* - * When the NVP is HW controlled, more fields are updated - */ - if (xive2_nvp_is_hw(nvp)) { - monitor_printf(mon, " CPPR:%02x", - xive_get_field32(NVP2_W2_CPPR, nvp->w2)); - if (xive2_nvp_is_co(nvp)) { - monitor_printf(mon, " CO:%04x", - xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); - } - } - monitor_printf(mon, "\n"); -} - /* * If the table is direct, we can compute the number of PQ entries * provisioned by FW. @@ -2064,7 +2506,7 @@ static uint64_t pnv_xive2_vst_per_subpage(PnvXive2 *xive, uint32_t type) return (1ull << page_shift) / info->size; } -void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) +void pnv_xive2_pic_print_info(PnvXive2 *xive, GString *buf) { Xive2Router *xrtr = XIVE2_ROUTER(xive); uint8_t blk = pnv_xive2_block_id(xive); @@ -2074,42 +2516,62 @@ void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon) Xive2Eas eas; Xive2End end; Xive2Nvp nvp; + Xive2Nvgc nvgc; int i; - uint64_t xive_nvp_per_subpage; + uint64_t entries_per_subpage; - monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, - srcno0 + nr_esbs - 1); - xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); + g_string_append_printf(buf, "XIVE[%x] Source %08x .. %08x\n", + blk, srcno0, srcno0 + nr_esbs - 1); + xive_source_pic_print_info(&xive->ipi_source, srcno0, buf); - monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0, - srcno0 + nr_esbs - 1); + g_string_append_printf(buf, "XIVE[%x] EAT %08x .. %08x\n", + blk, srcno0, srcno0 + nr_esbs - 1); for (i = 0; i < nr_esbs; i++) { if (xive2_router_get_eas(xrtr, blk, i, &eas)) { break; } if (!xive2_eas_is_masked(&eas)) { - xive2_eas_pic_print_info(&eas, i, mon); + xive2_eas_pic_print_info(&eas, i, buf); } } - monitor_printf(mon, "XIVE[%x] #%d END Escalation EAT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d END Escalation EAT\n", + chip_id, blk); i = 0; while (!xive2_router_get_end(xrtr, blk, i, &end)) { - xive2_end_eas_pic_print_info(&end, i++, mon); + xive2_end_eas_pic_print_info(&end, i++, buf); } - monitor_printf(mon, "XIVE[%x] #%d ENDT\n", chip_id, blk); + g_string_append_printf(buf, "XIVE[%x] #%d ENDT\n", chip_id, blk); i = 0; while (!xive2_router_get_end(xrtr, blk, i, &end)) { - xive2_end_pic_print_info(&end, i++, mon); + xive2_end_pic_print_info(&end, i++, buf); } - monitor_printf(mon, "XIVE[%x] #%d NVPT %08x .. %08x\n", chip_id, blk, - 0, XIVE2_NVP_COUNT - 1); - xive_nvp_per_subpage = pnv_xive2_vst_per_subpage(xive, VST_NVP); - for (i = 0; i < XIVE2_NVP_COUNT; i += xive_nvp_per_subpage) { + g_string_append_printf(buf, "XIVE[%x] #%d NVPT %08x .. %08x\n", + chip_id, blk, 0, XIVE2_NVP_COUNT - 1); + entries_per_subpage = pnv_xive2_vst_per_subpage(xive, VST_NVP); + for (i = 0; i < XIVE2_NVP_COUNT; i += entries_per_subpage) { while (!xive2_router_get_nvp(xrtr, blk, i, &nvp)) { - xive2_nvp_pic_print_info(&nvp, i++, mon); + xive2_nvp_pic_print_info(&nvp, i++, buf); + } + } + + g_string_append_printf(buf, "XIVE[%x] #%d NVGT %08x .. %08x\n", + chip_id, blk, 0, XIVE2_NVP_COUNT - 1); + entries_per_subpage = pnv_xive2_vst_per_subpage(xive, VST_NVG); + for (i = 0; i < XIVE2_NVP_COUNT; i += entries_per_subpage) { + while (!xive2_router_get_nvgc(xrtr, false, blk, i, &nvgc)) { + xive2_nvgc_pic_print_info(&nvgc, i++, buf); + } + } + + g_string_append_printf(buf, "XIVE[%x] #%d NVCT %08x .. %08x\n", + chip_id, blk, 0, XIVE2_NVP_COUNT - 1); + entries_per_subpage = pnv_xive2_vst_per_subpage(xive, VST_NVC); + for (i = 0; i < XIVE2_NVP_COUNT; i += entries_per_subpage) { + while (!xive2_router_get_nvgc(xrtr, true, blk, i, &nvgc)) { + xive2_nvgc_pic_print_info(&nvgc, i++, buf); } } } diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index 0c096e4adb..e8b87b3d2c 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -232,6 +232,14 @@ #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 cache flush inject register */ +#define X_VC_ESBC_FLUSH_INJECT 0x142 +#define VC_ESBC_FLUSH_INJECT 0x210 + +/* 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 @@ -246,6 +254,10 @@ #define VC_EASC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(32, 35) #define VC_EASC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(36, 63) /* 28-bit */ +/* EASC flush inject register */ +#define X_VC_EASC_FLUSH_INJECT 0x162 +#define VC_EASC_FLUSH_INJECT 0x310 + /* * VC2 */ @@ -266,6 +278,10 @@ #define VC_ENDC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define VC_ENDC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* ENDC flush inject register */ +#define X_VC_ENDC_FLUSH_INJECT 0x182 +#define VC_ENDC_FLUSH_INJECT 0x410 + /* ENDC Sync done */ #define X_VC_ENDC_SYNC_DONE 0x184 #define VC_ENDC_SYNC_DONE 0x420 @@ -279,6 +295,15 @@ #define VC_ENDC_SYNC_QUEUE_HARD PPC_BIT(6) #define VC_QUEUE_COUNT 7 +/* ENDC cache watch assign */ +#define X_VC_ENDC_WATCH_ASSIGN 0x186 +#define VC_ENDC_WATCH_ASSIGN 0x430 + +/* ENDC configuration register */ +#define X_VC_ENDC_CFG 0x188 +#define VC_ENDC_CFG 0x440 +#define VC_ENDC_CFG_CACHE_WATCH_ASSIGN PPC_BITMASK(32, 35) + /* ENDC cache watch specification 0 */ #define X_VC_ENDC_WATCH0_SPEC 0x1A0 #define VC_ENDC_WATCH0_SPEC 0x500 @@ -298,6 +323,42 @@ #define VC_ENDC_WATCH0_DATA2 0x530 #define VC_ENDC_WATCH0_DATA3 0x538 +/* ENDC cache watch 1 */ +#define X_VC_ENDC_WATCH1_SPEC 0x1A8 +#define VC_ENDC_WATCH1_SPEC 0x540 +#define X_VC_ENDC_WATCH1_DATA0 0x1AC +#define X_VC_ENDC_WATCH1_DATA1 0x1AD +#define X_VC_ENDC_WATCH1_DATA2 0x1AE +#define X_VC_ENDC_WATCH1_DATA3 0x1AF +#define VC_ENDC_WATCH1_DATA0 0x560 +#define VC_ENDC_WATCH1_DATA1 0x568 +#define VC_ENDC_WATCH1_DATA2 0x570 +#define VC_ENDC_WATCH1_DATA3 0x578 + +/* ENDC cache watch 2 */ +#define X_VC_ENDC_WATCH2_SPEC 0x1B0 +#define VC_ENDC_WATCH2_SPEC 0x580 +#define X_VC_ENDC_WATCH2_DATA0 0x1B4 +#define X_VC_ENDC_WATCH2_DATA1 0x1B5 +#define X_VC_ENDC_WATCH2_DATA2 0x1B6 +#define X_VC_ENDC_WATCH2_DATA3 0x1B7 +#define VC_ENDC_WATCH2_DATA0 0x5A0 +#define VC_ENDC_WATCH2_DATA1 0x5A8 +#define VC_ENDC_WATCH2_DATA2 0x5B0 +#define VC_ENDC_WATCH2_DATA3 0x5B8 + +/* ENDC cache watch 3 */ +#define X_VC_ENDC_WATCH3_SPEC 0x1B8 +#define VC_ENDC_WATCH3_SPEC 0x5C0 +#define X_VC_ENDC_WATCH3_DATA0 0x1BC +#define X_VC_ENDC_WATCH3_DATA1 0x1BD +#define X_VC_ENDC_WATCH3_DATA2 0x1BE +#define X_VC_ENDC_WATCH3_DATA3 0x1BF +#define VC_ENDC_WATCH3_DATA0 0x5E0 +#define VC_ENDC_WATCH3_DATA1 0x5E8 +#define VC_ENDC_WATCH3_DATA2 0x5F0 +#define VC_ENDC_WATCH3_DATA3 0x5F8 + /* * PC LSB1 */ @@ -354,6 +415,21 @@ #define PC_NXC_FLUSH_POLL_BLOCK_ID_MASK PPC_BITMASK(36, 39) #define PC_NXC_FLUSH_POLL_OFFSET_MASK PPC_BITMASK(40, 63) /* 24-bit */ +/* NxC Cache flush inject */ +#define X_PC_NXC_FLUSH_INJECT 0x282 +#define PC_NXC_FLUSH_INJECT 0x410 + +/* NxC Cache watch assign */ +#define X_PC_NXC_WATCH_ASSIGN 0x286 +#define PC_NXC_WATCH_ASSIGN 0x430 + +/* NxC Proc config */ +#define X_PC_NXC_PROC_CONFIG 0x28A +#define PC_NXC_PROC_CONFIG 0x450 +#define PC_NXC_PROC_CONFIG_WATCH_ASSIGN PPC_BITMASK(0, 3) +#define PC_NXC_PROC_CONFIG_NVG_TABLE_COMPRESS PPC_BITMASK(32, 35) +#define PC_NXC_PROC_CONFIG_NVC_TABLE_COMPRESS PPC_BITMASK(36, 39) + /* NxC Cache Watch 0 Specification */ #define X_PC_NXC_WATCH0_SPEC 0x2A0 #define PC_NXC_WATCH0_SPEC 0x500 @@ -377,6 +453,42 @@ #define PC_NXC_WATCH0_DATA2 0x530 #define PC_NXC_WATCH0_DATA3 0x538 +/* NxC Cache Watch 1 */ +#define X_PC_NXC_WATCH1_SPEC 0x2A8 +#define PC_NXC_WATCH1_SPEC 0x540 +#define X_PC_NXC_WATCH1_DATA0 0x2AC +#define X_PC_NXC_WATCH1_DATA1 0x2AD +#define X_PC_NXC_WATCH1_DATA2 0x2AE +#define X_PC_NXC_WATCH1_DATA3 0x2AF +#define PC_NXC_WATCH1_DATA0 0x560 +#define PC_NXC_WATCH1_DATA1 0x568 +#define PC_NXC_WATCH1_DATA2 0x570 +#define PC_NXC_WATCH1_DATA3 0x578 + +/* NxC Cache Watch 2 */ +#define X_PC_NXC_WATCH2_SPEC 0x2B0 +#define PC_NXC_WATCH2_SPEC 0x580 +#define X_PC_NXC_WATCH2_DATA0 0x2B4 +#define X_PC_NXC_WATCH2_DATA1 0x2B5 +#define X_PC_NXC_WATCH2_DATA2 0x2B6 +#define X_PC_NXC_WATCH2_DATA3 0x2B7 +#define PC_NXC_WATCH2_DATA0 0x5A0 +#define PC_NXC_WATCH2_DATA1 0x5A8 +#define PC_NXC_WATCH2_DATA2 0x5B0 +#define PC_NXC_WATCH2_DATA3 0x5B8 + +/* NxC Cache Watch 3 */ +#define X_PC_NXC_WATCH3_SPEC 0x2B8 +#define PC_NXC_WATCH3_SPEC 0x5C0 +#define X_PC_NXC_WATCH3_DATA0 0x2BC +#define X_PC_NXC_WATCH3_DATA1 0x2BD +#define X_PC_NXC_WATCH3_DATA2 0x2BE +#define X_PC_NXC_WATCH3_DATA3 0x2BF +#define PC_NXC_WATCH3_DATA0 0x5E0 +#define PC_NXC_WATCH3_DATA1 0x5E8 +#define PC_NXC_WATCH3_DATA2 0x5F0 +#define PC_NXC_WATCH3_DATA3 0x5F8 + /* * TCTXT Registers */ @@ -405,6 +517,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 dcf5de5d43..f2a224f3aa 100644 --- a/hw/intc/ppc-uic.c +++ b/hw/intc/ppc-uic.c @@ -269,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), @@ -286,7 +286,7 @@ static void ppc_uic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = ppc_uic_reset; + device_class_set_legacy_reset(dc, ppc_uic_reset); dc->realize = ppc_uic_realize; dc->vmsd = &ppc_uic_vmstate; device_class_set_props(dc, ppc_uic_properties); diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index eee04643cb..e9f0536b1c 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -64,13 +64,13 @@ 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); /* Compute the relative hartid w.r.t the socket */ hartid = hartid - mtimer->hartid_base; mtimer->timecmp[hartid] = value; - if (mtimer->timecmp[hartid] <= rtc_r) { + if (mtimer->timecmp[hartid] <= rtc) { /* * If we're setting an MTIMECMP value in the "past", * immediately raise the timer interrupt @@ -81,7 +81,7 @@ static void riscv_aclint_mtimer_write_timecmp(RISCVAclintMTimerState *mtimer, /* otherwise, set up the future timer interrupt */ qemu_irq_lower(mtimer->timer_irqs[hartid]); - diff = mtimer->timecmp[hartid] - rtc_r; + diff = mtimer->timecmp[hartid] - rtc; /* back to ns (note args switched in muldiv64) */ uint64_t ns_diff = muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); @@ -130,8 +130,8 @@ 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); @@ -173,8 +173,8 @@ 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); @@ -208,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; @@ -220,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", @@ -231,8 +232,8 @@ 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; } @@ -292,7 +293,7 @@ static void riscv_aclint_mtimer_realize(DeviceState *dev, Error **errp) 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); @@ -320,7 +321,7 @@ static const VMStateDescription vmstate_riscv_mtimer = { .name = "riscv_mtimer", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(timecmp, RISCVAclintMTimerState, num_harts, 0, vmstate_info_uint64, uint64_t), @@ -372,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); @@ -407,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); @@ -430,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); @@ -545,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..4a262c82f0 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,18 +150,51 @@ #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 bool riscv_aplic_irq_rectified_val(RISCVAPLICState *aplic, + uint32_t irq) +{ + uint32_t sourcecfg, sm, raw_input, irq_inverted; + + if (!irq || aplic->num_irqs <= irq) { + return false; + } + + sourcecfg = aplic->sourcecfg[irq]; + if (sourcecfg & APLIC_SOURCECFG_D) { + return false; + } + + sm = sourcecfg & APLIC_SOURCECFG_SM_MASK; + if (sm == APLIC_SOURCECFG_SM_INACTIVE) { + return false; + } + + 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; + + return !!(raw_input ^ irq_inverted); +} + static uint32_t riscv_aplic_read_input_word(RISCVAPLICState *aplic, uint32_t word) { - uint32_t i, irq, ret = 0; + uint32_t i, irq, rectified_val, ret = 0; for (i = 0; i < 32; i++) { irq = word * 32 + i; - if (!irq || aplic->num_irqs <= irq) { - continue; - } - ret |= ((aplic->state[irq] & APLIC_ISTATE_INPUT) ? 1 : 0) << i; + rectified_val = riscv_aplic_irq_rectified_val(aplic, irq); + ret |= rectified_val << i; } return ret; @@ -207,13 +242,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 +499,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; } @@ -665,6 +713,10 @@ static void riscv_aplic_write(void *opaque, hwaddr addr, uint64_t value, (aplic->sourcecfg[irq] == 0)) { riscv_aplic_set_pending_raw(aplic, irq, false); riscv_aplic_set_enabled_raw(aplic, irq, false); + } else { + if (riscv_aplic_irq_rectified_val(aplic, irq)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + } } } else if (aplic->mmode && aplic->msimode && (addr == APLIC_MMSICFGADDR)) { @@ -688,13 +740,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 +853,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 +891,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 +919,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), @@ -957,16 +1015,19 @@ DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, qdev_prop_set_bit(dev, "msimode", msimode); 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 (parent) { riscv_aplic_add_child(parent, dev); } + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + if (!is_kvm_aia(msimode)) { + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); + } + 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 4d4d5b50ca..9ef65d4012 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 @@ -54,7 +55,7 @@ static uint32_t riscv_imsic_topei(RISCVIMSICState *imsic, uint32_t page) (imsic->eithreshold[page] <= imsic->num_irqs)) ? imsic->eithreshold[page] : imsic->num_irqs; for (i = 1; i < max_irq; i++) { - if ((imsic->eistate[base + i] & IMSIC_EISTATE_ENPEND) == + if ((qatomic_read(&imsic->eistate[base + i]) & IMSIC_EISTATE_ENPEND) == IMSIC_EISTATE_ENPEND) { return (i << IMSIC_TOPEI_IID_SHIFT) | i; } @@ -65,10 +66,24 @@ static uint32_t riscv_imsic_topei(RISCVIMSICState *imsic, uint32_t page) static void riscv_imsic_update(RISCVIMSICState *imsic, uint32_t page) { + uint32_t base = page * imsic->num_irqs; + + /* + * Lower the interrupt line if necessary, then evaluate the current + * IMSIC state. + * This sequence ensures that any race between evaluating the eistate and + * updating the interrupt line will not result in an incorrectly + * deactivated connected CPU IRQ line. + * If multiple interrupts are pending, this sequence functions identically + * to qemu_irq_pulse. + */ + + if (qatomic_fetch_and(&imsic->eistate[base], ~IMSIC_EISTATE_ENPEND)) { + qemu_irq_lower(imsic->external_irqs[page]); + } if (imsic->eidelivery[page] && riscv_imsic_topei(imsic, page)) { qemu_irq_raise(imsic->external_irqs[page]); - } else { - qemu_irq_lower(imsic->external_irqs[page]); + qatomic_or(&imsic->eistate[base], IMSIC_EISTATE_ENPEND); } } @@ -124,12 +139,11 @@ static int riscv_imsic_topei_rmw(RISCVIMSICState *imsic, uint32_t page, topei >>= IMSIC_TOPEI_IID_SHIFT; base = page * imsic->num_irqs; if (topei) { - imsic->eistate[base + topei] &= ~IMSIC_EISTATE_PENDING; + qatomic_and(&imsic->eistate[base + topei], ~IMSIC_EISTATE_PENDING); } - - riscv_imsic_update(imsic, page); } + riscv_imsic_update(imsic, page); return 0; } @@ -138,7 +152,7 @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, uint32_t num, bool pend, target_ulong *val, target_ulong new_val, target_ulong wr_mask) { - uint32_t i, base; + uint32_t i, base, prev; target_ulong mask; uint32_t state = (pend) ? IMSIC_EISTATE_PENDING : IMSIC_EISTATE_ENABLED; @@ -156,10 +170,6 @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, if (val) { *val = 0; - for (i = 0; i < xlen; i++) { - mask = (target_ulong)1 << i; - *val |= (imsic->eistate[base + i] & state) ? mask : 0; - } } for (i = 0; i < xlen; i++) { @@ -171,10 +181,15 @@ static int riscv_imsic_eix_rmw(RISCVIMSICState *imsic, mask = (target_ulong)1 << i; if (wr_mask & mask) { if (new_val & mask) { - imsic->eistate[base + i] |= state; + prev = qatomic_fetch_or(&imsic->eistate[base + i], state); } else { - imsic->eistate[base + i] &= ~state; + prev = qatomic_fetch_and(&imsic->eistate[base + i], ~state); } + } else { + prev = qatomic_read(&imsic->eistate[base + i]); + } + if (val && (prev & state)) { + *val |= mask; } } @@ -283,18 +298,32 @@ 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) { if (value && (value < imsic->num_irqs)) { - imsic->eistate[(page * imsic->num_irqs) + value] |= - IMSIC_EISTATE_PENDING; + qatomic_or(&imsic->eistate[(page * imsic->num_irqs) + value], + IMSIC_EISTATE_PENDING); + + /* Update CPU external interrupt status */ + riscv_imsic_update(imsic, page); } } - /* Update CPU external interrupt status */ - riscv_imsic_update(imsic, page); - return; err: @@ -316,14 +345,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, @@ -369,7 +400,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), @@ -413,7 +444,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..c3d2b8d765 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; @@ -361,15 +361,77 @@ bool ais_needed(void *opaque) return s->ais_supported; } +static bool ais_needed_v(void *opaque, int version_id) +{ + return ais_needed(opaque); +} + +static bool qemu_s390_flic_full_state_needed(void *opaque) +{ + QEMUS390FLICState *s = opaque; + + return s->migrate_all_state; +} + +static bool qemu_s390_flic_state_needed(void *opaque) +{ + return ais_needed(opaque) || qemu_s390_flic_full_state_needed(opaque); +} + +static const VMStateDescription vmstate_qemu_s390_flic_io = { + .name = "qemu-s390-flic-io", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16(id, QEMUS390FlicIO), + VMSTATE_UINT16(nr, QEMUS390FlicIO), + VMSTATE_UINT32(parm, QEMUS390FlicIO), + VMSTATE_UINT32(word, QEMUS390FlicIO), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_qemu_s390_flic_full = { + .name = "qemu-s390-flic-full", + .version_id = 1, + .minimum_version_id = 1, + .needed = qemu_s390_flic_full_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(pending, QEMUS390FLICState), + VMSTATE_UINT32(service_param, QEMUS390FLICState), + VMSTATE_QLIST_V(io[0], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[1], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[2], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[3], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[4], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[5], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[6], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_QLIST_V(io[7], QEMUS390FLICState, 1, + vmstate_qemu_s390_flic_io, QEMUS390FlicIO, next), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription qemu_s390_flic_vmstate = { .name = "qemu-s390-flic", .version_id = 1, .minimum_version_id = 1, - .needed = ais_needed, - .fields = (VMStateField[]) { - VMSTATE_UINT8(simm, QEMUS390FLICState), - VMSTATE_UINT8(nimm, QEMUS390FLICState), + .needed = qemu_s390_flic_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_TEST(simm, QEMUS390FLICState, ais_needed_v), + VMSTATE_UINT8_TEST(nimm, QEMUS390FLICState, ais_needed_v), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_qemu_s390_flic_full, + NULL } }; @@ -383,12 +445,19 @@ static void qemu_s390_flic_instance_init(Object *obj) } } +static Property qemu_s390_flic_properties[] = { + DEFINE_PROP_BOOL("migrate-all-state", QEMUS390FLICState, + migrate_all_state, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); - dc->reset = qemu_s390_flic_reset; + device_class_set_props(dc, qemu_s390_flic_properties); + device_class_set_legacy_reset(dc, qemu_s390_flic_reset); dc->vmsd = &qemu_s390_flic_vmstate; fsc->register_io_adapter = qemu_s390_register_io_adapter; fsc->io_adapter_map = qemu_s390_io_adapter_map; @@ -405,6 +474,8 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) static Property s390_flic_common_properties[] = { DEFINE_PROP_UINT32("adapter_routes_max_batch", S390FLICState, adapter_routes_max_batch, ADAPTER_ROUTES_MAX_GSI), + DEFINE_PROP_BOOL("migration-enabled", S390FLICState, + migration_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -457,7 +528,9 @@ type_init(qemu_s390_flic_register_types) static bool adapter_info_so_needed(void *opaque) { - return css_migration_enabled(); + S390FLICState *fs = s390_get_flic(); + + return fs->migration_enabled; } const VMStateDescription vmstate_adapter_info_so = { @@ -465,7 +538,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 +548,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 +557,7 @@ const VMStateDescription vmstate_adapter_info = { */ VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription * []) { + .subsections = (const VMStateDescription * const []) { &vmstate_adapter_info_so, NULL } @@ -495,7 +568,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..7930d72390 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -324,6 +324,34 @@ static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, return r ? -errno : 0; } +static int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) +{ + struct kvm_irq_routing_entry kroute = {}; + int virq; + + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + + virq = kvm_irqchip_get_virq(s); + if (virq < 0) { + return virq; + } + + kroute.gsi = virq; + kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; + kroute.flags = 0; + kroute.u.adapter.summary_addr = adapter->summary_addr; + kroute.u.adapter.ind_addr = adapter->ind_addr; + kroute.u.adapter.summary_offset = adapter->summary_offset; + kroute.u.adapter.ind_offset = adapter->ind_offset; + kroute.u.adapter.adapter_id = adapter->adapter_id; + + kvm_add_routing_entry(s, &kroute); + + return virq; +} + static int kvm_s390_add_adapter_routes(S390FLICState *fs, AdapterRoutes *routes) { @@ -380,7 +408,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 +553,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 +565,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 +578,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 +590,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,11 +674,12 @@ 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; + device_class_set_legacy_reset(dc, kvm_s390_flic_reset); fsc->register_io_adapter = kvm_s390_register_io_adapter; fsc->io_adapter_map = kvm_s390_io_adapter_map; fsc->add_adapter_routes = kvm_s390_add_adapter_routes; diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index c2dfacf028..ed74490dba 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,9 +188,14 @@ 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; - - if (((plic->num_priorities + 1) & plic->num_priorities) == 0) { + uint32_t irq = (addr - plic->priority_base) >> 2; + if (irq == 0) { + /* IRQ 0 source prioority is reserved */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid source priority write 0x%" + HWADDR_PRIx "\n", __func__, addr); + return; + } else 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 @@ -193,7 +208,7 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t 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); @@ -281,7 +296,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; @@ -290,11 +305,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); @@ -305,8 +322,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; @@ -317,11 +335,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++; } } @@ -331,8 +354,10 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) { SiFivePLICState *s = opaque; - sifive_plic_set_pending(s, irq, level > 0); - sifive_plic_update(s); + if (level > 0) { + sifive_plic_set_pending(s, irq, true); + sifive_plic_update(s); + } } static void sifive_plic_realize(DeviceState *dev, Error **errp) @@ -346,6 +371,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); @@ -362,7 +392,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. @@ -370,8 +401,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; } } @@ -382,7 +413,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), @@ -402,8 +433,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), @@ -418,7 +451,7 @@ static void sifive_plic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = sifive_plic_reset; + device_class_set_legacy_reset(dc, sifive_plic_reset); device_class_set_props(dc, sifive_plic_properties); dc->realize = sifive_plic_realize; dc->vmsd = &vmstate_sifive_plic; @@ -476,11 +509,11 @@ DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, CPUState *cpu = qemu_get_cpu(cpu_num); if (plic->addr_config[i].mode == PLICMode_M) { - qdev_connect_gpio_out(dev, num_harts - plic->hartid_base + cpu_num, + qdev_connect_gpio_out(dev, cpu_num - hartid_base + num_harts, qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT)); } if (plic->addr_config[i].mode == PLICMode_S) { - qdev_connect_gpio_out(dev, cpu_num, + 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..f83709a857 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -24,7 +24,6 @@ #include "qemu/osdep.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "qemu/module.h" #include "hw/sysbus.h" #include "hw/intc/intc.h" @@ -353,7 +352,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 +363,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), @@ -401,17 +400,17 @@ static bool slavio_intctl_get_statistics(InterruptStatsProvider *obj, } #endif -static void slavio_intctl_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void slavio_intctl_print_info(InterruptStatsProvider *obj, GString *buf) { SLAVIO_INTCTLState *s = SLAVIO_INTCTL(obj); int i; for (i = 0; i < MAX_CPUS; i++) { - monitor_printf(mon, "per-cpu %d: pending 0x%08x\n", i, - s->slaves[i].intreg_pending); + g_string_append_printf(buf, "per-cpu %d: pending 0x%08x\n", i, + s->slaves[i].intreg_pending); } - monitor_printf(mon, "master: pending 0x%08x, disabled 0x%08x\n", - s->intregm_pending, s->intregm_disabled); + g_string_append_printf(buf, "master: pending 0x%08x, disabled 0x%08x\n", + s->intregm_pending, s->intregm_disabled); } static void slavio_intctl_init(Object *obj) @@ -447,7 +446,7 @@ static void slavio_intctl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); - dc->reset = slavio_intctl_reset; + device_class_set_legacy_reset(dc, slavio_intctl_reset); dc->vmsd = &vmstate_intctl; #ifdef DEBUG_IRQ_COUNT ic->get_statistics = slavio_intctl_get_statistics; diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index dc641cc604..283a6b8fd2 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -16,7 +16,6 @@ #include "sysemu/cpus.h" #include "sysemu/reset.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_cpu_core.h" @@ -27,7 +26,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 @@ -132,7 +131,7 @@ static int spapr_xive_target_to_end(uint32_t target, uint8_t prio, * structure dumping only the information related to the OS EQ. */ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, - Monitor *mon) + GString *buf) { uint64_t qaddr_base = xive_end_qaddr(end); uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); @@ -142,11 +141,11 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6); uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); - monitor_printf(mon, "%3d/%d % 6d/%5d @%"PRIx64" ^%d", - spapr_xive_nvt_to_target(0, nvt), - priority, qindex, qentries, qaddr_base, qgen); + g_string_append_printf(buf, "%3d/%d % 6d/%5d @%"PRIx64" ^%d", + spapr_xive_nvt_to_target(0, nvt), + priority, qindex, qentries, qaddr_base, qgen); - xive_end_queue_pic_print_info(end, 6, mon); + xive_end_queue_pic_print_info(end, 6, buf); } /* @@ -156,7 +155,7 @@ static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, #define spapr_xive_in_kernel(xive) \ (kvm_irqchip_in_kernel() && (xive)->fd != -1) -static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) +static void spapr_xive_pic_print_info(SpaprXive *xive, GString *buf) { XiveSource *xsrc = &xive->source; int i; @@ -171,7 +170,7 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) } } - monitor_printf(mon, " LISN PQ EISN CPU/PRIO EQ\n"); + g_string_append_printf(buf, " LISN PQ EISN CPU/PRIO EQ\n"); for (i = 0; i < xive->nr_irqs; i++) { uint8_t pq = xive_source_esb_get(xsrc, i); @@ -181,13 +180,13 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) continue; } - monitor_printf(mon, " %08x %s %c%c%c %s %08x ", i, - xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_source_is_asserted(xsrc, i) ? 'A' : ' ', - xive_eas_is_masked(eas) ? "M" : " ", - (int) xive_get_field64(EAS_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %s %c%c%c %s %08x ", i, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_source_is_asserted(xsrc, i) ? 'A' : ' ', + xive_eas_is_masked(eas) ? "M" : " ", + (int) xive_get_field64(EAS_END_DATA, eas->w)); if (!xive_eas_is_masked(eas)) { uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); @@ -197,10 +196,11 @@ static void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) end = &xive->endt[end_idx]; if (xive_end_is_valid(end)) { - spapr_xive_end_pic_print_info(xive, end, mon); + spapr_xive_end_pic_print_info(xive, end, buf); } + } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } } @@ -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), @@ -684,7 +699,7 @@ static void spapr_xive_set_irq(SpaprInterruptController *intc, int irq, int val) } } -static void spapr_xive_print_info(SpaprInterruptController *intc, Monitor *mon) +static void spapr_xive_print_info(SpaprInterruptController *intc, GString *buf) { SpaprXive *xive = SPAPR_XIVE(intc); CPUState *cs; @@ -692,10 +707,9 @@ static void spapr_xive_print_info(SpaprInterruptController *intc, Monitor *mon) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, mon); + xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, buf); } - - spapr_xive_pic_print_info(xive, mon); + spapr_xive_pic_print_info(xive, buf); } static void spapr_xive_dt(SpaprInterruptController *intc, uint32_t nr_servers, @@ -832,6 +846,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..7a86197fc9 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; @@ -720,7 +720,7 @@ int kvmppc_xive_connect(SpaprInterruptController *intc, uint32_t nr_servers, { SpaprXive *xive = SPAPR_XIVE(intc); XiveSource *xsrc = &xive->source; - size_t esb_len = xive_source_esb_len(xsrc); + uint64_t esb_len = xive_source_esb_len(xsrc); size_t tima_len = 4ull << TM_SHIFT; CPUState *cs; int fd; @@ -824,7 +824,7 @@ void kvmppc_xive_disconnect(SpaprInterruptController *intc) { SpaprXive *xive = SPAPR_XIVE(intc); XiveSource *xsrc; - size_t esb_len; + uint64_t esb_len; assert(xive->fd != -1); diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 6fbc2045e6..3dcf147198 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" @@ -78,6 +79,19 @@ aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" aspeed_vic_update_irq(int flags) "Raising IRQ: %d" aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +# aspeed_intc.c +aspeed_intc_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_set_irq(int irq, int level) "Set IRQ %d: %d" +aspeed_intc_clear_irq(int irq, int level) "Clear IRQ %d: %d" +aspeed_intc_update_irq(int irq, int level) "Update IRQ: %d: %d" +aspeed_intc_pending_irq(int irq, uint32_t value) "Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(int irq, uint32_t value) "Trigger IRQ: %d: 0x%x" +aspeed_intc_all_isr_done(int irq) "All source ISR execution are done: %d" +aspeed_intc_enable(uint32_t value) "Enable: 0x%x" +aspeed_intc_select(uint32_t value) "Select: 0x%x" +aspeed_intc_mask(uint32_t change, uint32_t value) "Mask: 0x%x: 0x%x" +aspeed_intc_unmask(uint32_t change, uint32_t value) "UnMask: 0x%x: 0x%x" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" @@ -115,6 +129,7 @@ gicv3_cpuif_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f gicv3_icc_generate_sgi(uint32_t cpuid, int irq, int irm, uint32_t aff, uint32_t targetlist) "GICv3 CPU i/f 0x%x generating SGI %d IRM %d target affinity 0x%xxx targetlist 0x%x" gicv3_icc_iar0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR0 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_iar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_IAR1 read cpu 0x%x value 0x%" PRIx64 +gicv3_icc_nmiar1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_NMIAR1 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_EOIR%d write cpu 0x%x value 0x%" PRIx64 gicv3_icc_hppir0_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR0 read cpu 0x%x value 0x%" PRIx64 gicv3_icc_hppir1_read(uint32_t cpu, uint64_t val) "GICv3 ICC_HPPIR1 read cpu 0x%x value 0x%" PRIx64 @@ -150,6 +165,7 @@ gicv3_icv_rpr_read(uint32_t cpu, uint64_t val) "GICv3 ICV_RPR read cpu 0x%x valu gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d read cpu 0x%x value 0x%" PRIx64 gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64 gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64 +gicv3_icv_nmiar1_read(uint32_t cpu, uint64_t val) "GICv3 ICV_NMIAR1 read cpu 0x%x value 0x%" PRIx64 gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64 gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d" gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d" @@ -264,8 +280,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 @@ -288,10 +304,9 @@ sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PR sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx" 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 - +# loongson_ipi.c +loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 +loongson_ipi_write(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 # loongarch_pch_pic.c loongarch_pch_pic_irq_handler(int irq, int level) "irq %d level %d" loongarch_pch_pic_low_readw(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%" PRIx64 diff --git a/hw/intc/xics.c b/hw/intc/xics.c index dcd021af66..e893363dc9 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -35,13 +35,13 @@ #include "qemu/module.h" #include "qapi/visitor.h" #include "migration/vmstate.h" -#include "monitor/monitor.h" #include "hw/intc/intc.h" #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) +void icp_pic_print_info(ICPState *icp, GString *buf) { int cpu_index; @@ -62,17 +62,17 @@ void icp_pic_print_info(ICPState *icp, Monitor *mon) icp_synchronize_state(icp); } - monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", - cpu_index, icp->xirr, icp->xirr_owner, - icp->pending_priority, icp->mfrr); + g_string_append_printf(buf, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", + cpu_index, icp->xirr, icp->xirr_owner, + icp->pending_priority, icp->mfrr); } -void ics_pic_print_info(ICSState *ics, Monitor *mon) +void ics_pic_print_info(ICSState *ics, GString *buf) { uint32_t i; - monitor_printf(mon, "ICS %4x..%4x %p\n", - ics->offset, ics->offset + ics->nr_irqs - 1, ics); + g_string_append_printf(buf, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); if (!ics->irqs) { return; @@ -88,11 +88,11 @@ void ics_pic_print_info(ICSState *ics, Monitor *mon) if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { continue; } - monitor_printf(mon, " %4x %s %02x %02x\n", - ics->offset + i, - (irq->flags & XICS_FLAGS_IRQ_LSI) ? - "LSI" : "MSI", - irq->priority, irq->status); + g_string_append_printf(buf, " %4x %s %02x %02x\n", + ics->offset + i, + (irq->flags & XICS_FLAGS_IRQ_LSI) ? + "LSI" : "MSI", + irq->priority, irq->status); } } @@ -273,7 +273,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), @@ -335,8 +335,6 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } } - - vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } static void icp_unrealize(DeviceState *dev) @@ -564,9 +562,9 @@ static void ics_reset_irq(ICSIRQState *irq) irq->saved_priority = 0xff; } -static void ics_reset(DeviceState *dev) +static void ics_reset_hold(Object *obj, ResetType type) { - ICSState *ics = ICS(dev); + ICSState *ics = ICS(obj); g_autofree uint8_t *flags = g_malloc(ics->nr_irqs); int i; @@ -584,7 +582,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); } @@ -593,7 +591,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) @@ -651,7 +649,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), @@ -667,7 +665,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), @@ -688,16 +686,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/xics_spapr.c b/hw/intc/xics_spapr.c index 37b2d99977..a0d97bdefe 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -395,7 +395,7 @@ static void xics_spapr_set_irq(SpaprInterruptController *intc, int irq, int val) ics_set_irq(ics, srcno, val); } -static void xics_spapr_print_info(SpaprInterruptController *intc, Monitor *mon) +static void xics_spapr_print_info(SpaprInterruptController *intc, GString *buf) { ICSState *ics = ICS_SPAPR(intc); CPUState *cs; @@ -403,10 +403,9 @@ static void xics_spapr_print_info(SpaprInterruptController *intc, Monitor *mon) CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - icp_pic_print_info(spapr_cpu_state(cpu)->icp, mon); + icp_pic_print_info(spapr_cpu_state(cpu)->icp, buf); } - - ics_pic_print_info(ics, mon); + ics_pic_print_info(ics, buf); } static int xics_spapr_post_load(SpaprInterruptController *intc, int version_id) 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 a986b96843..245e4d181a 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -17,9 +17,9 @@ #include "sysemu/reset.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#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" @@ -74,33 +74,48 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) if (regs[TM_NSR] & mask) { uint8_t cppr = regs[TM_PIPR]; + uint8_t alt_ring; + uint8_t *alt_regs; + + /* POOL interrupt uses IPB in QW2, POOL ring */ + if ((ring == TM_QW3_HV_PHYS) && (nsr & (TM_QW3_NSR_HE_POOL << 6))) { + alt_ring = TM_QW2_HV_POOL; + } else { + alt_ring = ring; + } + alt_regs = &tctx->regs[alt_ring]; regs[TM_CPPR] = cppr; /* Reset the pending buffer bit */ - regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); - regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); + alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); /* Drop Exception bit */ regs[TM_NSR] &= ~mask; - trace_xive_tctx_accept(tctx->cs->cpu_index, ring, - regs[TM_IPB], regs[TM_PIPR], + trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring, + alt_regs[TM_IPB], regs[TM_PIPR], regs[TM_CPPR], regs[TM_NSR]); } - return (nsr << 8) | regs[TM_CPPR]; + return ((uint64_t)nsr << 8) | regs[TM_CPPR]; } static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring) { + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *alt_regs = &tctx->regs[alt_ring]; uint8_t *regs = &tctx->regs[ring]; - if (regs[TM_PIPR] < regs[TM_CPPR]) { + if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) { switch (ring) { case TM_QW1_OS: regs[TM_NSR] |= TM_QW1_NSR_EO; break; + case TM_QW2_HV_POOL: + alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6); + break; case TM_QW3_HV_PHYS: regs[TM_NSR] |= (TM_QW3_NSR_HE_PHYS << 6); break; @@ -108,26 +123,27 @@ static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring) g_assert_not_reached(); } trace_xive_tctx_notify(tctx->cs->cpu_index, ring, - regs[TM_IPB], regs[TM_PIPR], - regs[TM_CPPR], regs[TM_NSR]); + regs[TM_IPB], alt_regs[TM_PIPR], + alt_regs[TM_CPPR], alt_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); } } -void xive_tctx_reset_os_signal(XiveTCTX *tctx) +void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring) { /* - * Lower the External interrupt. Used when pulling an OS - * context. It is necessary to avoid catching it in the hypervisor - * context. It should be raised again when re-pushing the OS - * context. + * Lower the External interrupt. Used when pulling a context. It is + * necessary to avoid catching it in the higher privilege context. It + * should be raised again when re-pushing the lower privilege context. */ - qemu_irq_lower(xive_tctx_output(tctx, TM_QW1_OS)); + qemu_irq_lower(xive_tctx_output(tctx, ring)); } static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; + uint8_t pipr_min; + uint8_t ring_min; trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, regs[TM_IPB], regs[TM_PIPR], @@ -139,8 +155,37 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) tctx->regs[ring + TM_CPPR] = cppr; + /* + * Recompute the PIPR based on local pending interrupts. The PHYS + * ring must take the minimum of both the PHYS and POOL PIPR values. + */ + pipr_min = ipb_to_pipr(regs[TM_IPB]); + ring_min = ring; + + /* PHYS updates also depend on POOL values */ + if (ring == TM_QW3_HV_PHYS) { + uint8_t *pool_regs = &tctx->regs[TM_QW2_HV_POOL]; + + /* POOL values only matter if POOL ctx is valid */ + if (pool_regs[TM_WORD2] & 0x80) { + + uint8_t pool_pipr = ipb_to_pipr(pool_regs[TM_IPB]); + + /* + * Determine highest priority interrupt and + * remember which ring has it. + */ + if (pool_pipr < pipr_min) { + pipr_min = pool_pipr; + ring_min = TM_QW2_HV_POOL; + } + } + } + + regs[TM_PIPR] = pipr_min; + /* CPPR has changed, check if we need to raise a pending exception */ - xive_tctx_notify(tctx, ring); + xive_tctx_notify(tctx, ring_min); } void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb) @@ -179,6 +224,17 @@ static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, return qw2w2; } +static uint64_t xive_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + uint8_t qw3b8_prev = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; + uint8_t qw3b8; + + qw3b8 = qw3b8_prev & ~TM_QW3B8_VT; + tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8; + return qw3b8; +} + static void xive_tm_vt_push(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { @@ -207,14 +263,14 @@ static uint64_t xive_tm_vt_poll(XivePresenter *xptr, XiveTCTX *tctx, static const uint8_t xive_tm_hw_view[] = { 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */ 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */ - 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ + 0, 0, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 3, 3, 3, 0, /* QW-3 PHYS */ }; static const uint8_t xive_tm_hv_view[] = { 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */ 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */ - 0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ + 0, 0, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 0, 0, 0, 0, /* QW-3 PHYS */ }; @@ -249,7 +305,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 +322,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 +352,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; @@ -341,6 +397,19 @@ static void xive_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, xive_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); } +static void xive_tctx_set_lgs(XiveTCTX *tctx, uint8_t ring, uint8_t lgs) +{ + uint8_t *regs = &tctx->regs[ring]; + + regs[TM_LGS] = lgs; +} + +static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive_tctx_set_lgs(tctx, TM_QW1_OS, value & 0xff); +} + /* * Adjust the IPB to allow a CPU to process event queues of other * priorities during one physical interrupt cycle. @@ -400,7 +469,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, qw1w2_new = xive_set_field32(TM_QW1W2_VO, qw1w2, 0); xive_tctx_set_os_cam(tctx, qw1w2_new); - xive_tctx_reset_os_signal(tctx); + xive_tctx_reset_signal(tctx, TM_QW1_OS); return qw1w2; } @@ -461,6 +530,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. @@ -481,30 +557,107 @@ static const XiveTmOp xive_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, xive_tm_push_os_ctx, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, xive_tm_vt_poll }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, + NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive_tm_push_os_ctx, + NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, + NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, + NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, + xive_tm_vt_poll }, /* 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, xive_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, xive_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 }, + { 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, + xive_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, + xive_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 }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, + xive_tm_pull_phys_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_QW1_OS + TM_WORD2, 8, xive2_tm_push_os_ctx, + NULL }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs, + 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 }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, xive2_tm_set_hv_target, + NULL }, + + /* 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_G2, 4, NULL, + xive2_tm_pull_os_ctx }, + { 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_G2, 4, NULL, + xive_tm_pull_pool_ctx }, + { 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 }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, xive2_tm_pull_os_ctx_ol, + NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, NULL, + xive_tm_pull_phys_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, + xive_tm_pull_phys_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, + NULL }, +}; + +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 +678,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 +687,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 +701,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 +726,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 +740,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 +751,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; } @@ -628,7 +781,7 @@ static const char * const xive_tctx_ring_names[] = { xpc->in_kernel ? xpc->in_kernel(xptr) : false; \ })) -void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon) +void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf) { int cpu_index; int i; @@ -652,13 +805,20 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon) } } - monitor_printf(mon, "CPU[%04x]: QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR" - " W2\n", cpu_index); + if (xive_presenter_get_config(tctx->xptr) & XIVE_PRESENTER_GEN1_TIMA_OS) { + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR" + " W2\n", cpu_index); + } else { + g_string_append_printf(buf, "CPU[%04x]: " + "QW NSR CPPR IPB LSMFB - LGS T PIPR" + " W2\n", cpu_index); + } for (i = 0; i < XIVE_TM_RING_COUNT; i++) { char *s = xive_tctx_ring_print(&tctx->regs[i * XIVE_TM_RING_SIZE]); - monitor_printf(mon, "CPU[%04x]: %4s %s\n", cpu_index, - xive_tctx_ring_names[i], s); + g_string_append_printf(buf, "CPU[%04x]: %4s %s\n", + cpu_index, xive_tctx_ring_names[i], s); g_free(s); } } @@ -671,6 +831,10 @@ void xive_tctx_reset(XiveTCTX *tctx) tctx->regs[TM_QW1_OS + TM_LSMFB] = 0xFF; tctx->regs[TM_QW1_OS + TM_ACK_CNT] = 0xFF; tctx->regs[TM_QW1_OS + TM_AGE] = 0xFF; + if (!(xive_presenter_get_config(tctx->xptr) & + XIVE_PRESENTER_GEN1_TIMA_OS)) { + tctx->regs[TM_QW1_OS + TM_OGEN] = 2; + } /* * Initialize PIPR to 0xFF to avoid phantom interrupts when the @@ -757,7 +921,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 +1298,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, }, }; @@ -1166,22 +1330,20 @@ void xive_source_set_irq(void *opaque, int srcno, int val) } } -void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon) +void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, GString *buf) { - int i; - - for (i = 0; i < xsrc->nr_irqs; i++) { + for (unsigned i = 0; i < xsrc->nr_irqs; i++) { uint8_t pq = xive_source_esb_get(xsrc, i); if (pq == XIVE_ESB_OFF) { continue; } - monitor_printf(mon, " %08x %s %c%c%c\n", i + offset, - xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_source_is_asserted(xsrc, i) ? 'A' : ' '); + g_string_append_printf(buf, " %08x %s %c%c%c\n", i + offset, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_source_is_asserted(xsrc, i) ? 'A' : ' '); } } @@ -1191,14 +1353,13 @@ 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) { XiveSource *xsrc = XIVE_SOURCE(dev); - size_t esb_len = xive_source_esb_len(xsrc); + uint64_t esb_len = xive_source_esb_len(xsrc); assert(xsrc->xive); @@ -1231,7 +1392,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 +1407,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(), @@ -1277,7 +1443,7 @@ static const TypeInfo xive_source_info = { * XiveEND helpers */ -void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) +void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf) { uint64_t qaddr_base = xive_end_qaddr(end); uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0); @@ -1288,7 +1454,7 @@ void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) /* * print out the [ (qindex - (width - 1)) .. (qindex + 1)] window */ - monitor_printf(mon, " [ "); + g_string_append_printf(buf, " [ "); qindex = (qindex - (width - 1)) & (qentries - 1); for (i = 0; i < width; i++) { uint64_t qaddr = qaddr_base + (qindex << 2); @@ -1300,14 +1466,14 @@ void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) HWADDR_PRIx "\n", qaddr); return; } - monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "", - be32_to_cpu(qdata)); + g_string_append_printf(buf, "%s%08x ", i == width - 1 ? "^" : "", + be32_to_cpu(qdata)); qindex = (qindex + 1) & (qentries - 1); } - monitor_printf(mon, "]"); + g_string_append_c(buf, ']'); } -void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) +void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf) { uint64_t qaddr_base = xive_end_qaddr(end); uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); @@ -1326,26 +1492,27 @@ void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) pq = xive_get_field32(END_W1_ESn, end->w1); - monitor_printf(mon, " %08x %c%c %c%c%c%c%c%c%c%c prio:%d nvt:%02x/%04x", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_end_is_valid(end) ? 'v' : '-', - xive_end_is_enqueue(end) ? 'q' : '-', - xive_end_is_notify(end) ? 'n' : '-', - xive_end_is_backlog(end) ? 'b' : '-', - xive_end_is_escalate(end) ? 'e' : '-', - xive_end_is_uncond_escalation(end) ? 'u' : '-', - xive_end_is_silent_escalation(end) ? 's' : '-', - xive_end_is_firmware(end) ? 'f' : '-', - priority, nvt_blk, nvt_idx); + g_string_append_printf(buf, + " %08x %c%c %c%c%c%c%c%c%c%c prio:%d nvt:%02x/%04x", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_end_is_valid(end) ? 'v' : '-', + xive_end_is_enqueue(end) ? 'q' : '-', + xive_end_is_notify(end) ? 'n' : '-', + xive_end_is_backlog(end) ? 'b' : '-', + xive_end_is_escalate(end) ? 'e' : '-', + xive_end_is_uncond_escalation(end) ? 'u' : '-', + xive_end_is_silent_escalation(end) ? 's' : '-', + xive_end_is_firmware(end) ? 'f' : '-', + priority, nvt_blk, nvt_idx); if (qaddr_base) { - monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", - qaddr_base, qindex, qentries, qgen); - xive_end_queue_pic_print_info(end, 6, mon); + g_string_append_printf(buf, " eq:@%08"PRIx64"% 6d/%5d ^%d", + qaddr_base, qindex, qentries, qgen); + xive_end_queue_pic_print_info(end, 6, buf); } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } static void xive_end_enqueue(XiveEND *end, uint32_t data) @@ -1374,8 +1541,7 @@ static void xive_end_enqueue(XiveEND *end, uint32_t data) end->w1 = xive_set_field32(END_W1_PAGE_OFF, end->w1, qindex); } -void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, - Monitor *mon) +void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf) { XiveEAS *eas = (XiveEAS *) &end->w4; uint8_t pq; @@ -1386,15 +1552,15 @@ void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, pq = xive_get_field32(END_W1_ESe, end->w1); - monitor_printf(mon, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive_eas_is_valid(eas) ? 'V' : ' ', - xive_eas_is_masked(eas) ? 'M' : ' ', - (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive_eas_is_valid(eas) ? 'V' : ' ', + xive_eas_is_masked(eas) ? 'M' : ' ', + (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); } /* @@ -1473,6 +1639,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 +1729,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 +1792,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 +1802,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 +1948,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 +1999,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 +2012,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 = { @@ -1864,17 +2038,17 @@ static const TypeInfo xive_router_info = { } }; -void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon) +void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, GString *buf) { if (!xive_eas_is_valid(eas)) { return; } - monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n", - lisn, xive_eas_is_masked(eas) ? "M" : " ", - (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %s end:%02x/%04x data:%08x\n", + lisn, xive_eas_is_masked(eas) ? "M" : " ", + (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); } /* @@ -1961,11 +2135,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..d1df35e9b3 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -15,7 +15,6 @@ #include "sysemu/cpus.h" #include "sysemu/dma.h" #include "hw/qdev-properties.h" -#include "monitor/monitor.h" #include "hw/ppc/xive.h" #include "hw/ppc/xive2.h" #include "hw/ppc/xive2_regs.h" @@ -27,21 +26,57 @@ uint32_t xive2_router_get_config(Xive2Router *xrtr) return xrc->get_config(xrtr); } -void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, Monitor *mon) +static int xive2_router_get_block_id(Xive2Router *xrtr) +{ + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); + + return xrc->get_block_id(xrtr); +} + +static uint64_t xive2_nvp_reporting_addr(Xive2Nvp *nvp) +{ + uint64_t cache_addr; + + cache_addr = xive_get_field32(NVP2_W6_REPORTING_LINE, nvp->w6) << 24 | + xive_get_field32(NVP2_W7_REPORTING_LINE, nvp->w7); + cache_addr <<= 8; /* aligned on a cache line pair */ + return cache_addr; +} + +static uint32_t xive2_nvgc_get_backlog(Xive2Nvgc *nvgc, uint8_t priority) +{ + uint32_t val = 0; + uint8_t *ptr, i; + + if (priority > 7) { + return 0; + } + + /* + * The per-priority backlog counters are 24-bit and the structure + * is stored in big endian + */ + ptr = (uint8_t *)&nvgc->w2 + priority * 3; + for (i = 0; i < 3; i++, ptr++) { + val = (val << 8) + *ptr; + } + return val; +} + +void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) { if (!xive2_eas_is_valid(eas)) { return; } - monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n", - lisn, xive2_eas_is_masked(eas) ? "M" : " ", - (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %s end:%02x/%04x data:%08x\n", + lisn, xive2_eas_is_masked(eas) ? "M" : " ", + (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } -void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, - Monitor *mon) +void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf) { uint64_t qaddr_base = xive2_end_qaddr(end); uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); @@ -52,7 +87,7 @@ void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, /* * print out the [ (qindex - (width - 1)) .. (qindex + 1)] window */ - monitor_printf(mon, " [ "); + g_string_append_printf(buf, " [ "); qindex = (qindex - (width - 1)) & (qentries - 1); for (i = 0; i < width; i++) { uint64_t qaddr = qaddr_base + (qindex << 2); @@ -64,14 +99,14 @@ void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, HWADDR_PRIx "\n", qaddr); return; } - monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "", - be32_to_cpu(qdata)); + g_string_append_printf(buf, "%s%08x ", i == width - 1 ? "^" : "", + be32_to_cpu(qdata)); qindex = (qindex + 1) & (qentries - 1); } - monitor_printf(mon, "]"); + g_string_append_printf(buf, "]"); } -void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) +void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) { uint64_t qaddr_base = xive2_end_qaddr(end); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); @@ -90,33 +125,37 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon) pq = xive_get_field32(END2_W1_ESn, end->w1); - monitor_printf(mon, - " %08x %c%c %c%c%c%c%c%c%c%c%c%c prio:%d nvp:%02x/%04x", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive2_end_is_valid(end) ? 'v' : '-', - xive2_end_is_enqueue(end) ? 'q' : '-', - xive2_end_is_notify(end) ? 'n' : '-', - xive2_end_is_backlog(end) ? 'b' : '-', - xive2_end_is_escalate(end) ? 'e' : '-', - xive2_end_is_escalate_end(end) ? 'N' : '-', - xive2_end_is_uncond_escalation(end) ? 'u' : '-', - xive2_end_is_silent_escalation(end) ? 's' : '-', - xive2_end_is_firmware1(end) ? 'f' : '-', - xive2_end_is_firmware2(end) ? 'F' : '-', - priority, nvp_blk, nvp_idx); + g_string_append_printf(buf, + " %08x %c%c %c%c%c%c%c%c%c%c%c%c%c %c%c " + "prio:%d nvp:%02x/%04x", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive2_end_is_valid(end) ? 'v' : '-', + xive2_end_is_enqueue(end) ? 'q' : '-', + xive2_end_is_notify(end) ? 'n' : '-', + xive2_end_is_backlog(end) ? 'b' : '-', + xive2_end_is_precluded_escalation(end) ? 'p' : '-', + xive2_end_is_escalate(end) ? 'e' : '-', + xive2_end_is_escalate_end(end) ? 'N' : '-', + xive2_end_is_uncond_escalation(end) ? 'u' : '-', + xive2_end_is_silent_escalation(end) ? 's' : '-', + xive2_end_is_firmware1(end) ? 'f' : '-', + xive2_end_is_firmware2(end) ? 'F' : '-', + xive2_end_is_ignore(end) ? 'i' : '-', + xive2_end_is_crowd(end) ? 'c' : '-', + priority, nvp_blk, nvp_idx); if (qaddr_base) { - monitor_printf(mon, " eq:@%08"PRIx64"% 6d/%5d ^%d", - qaddr_base, qindex, qentries, qgen); - xive2_end_queue_pic_print_info(end, 6, mon); + g_string_append_printf(buf, " eq:@%08"PRIx64"% 6d/%5d ^%d", + qaddr_base, qindex, qentries, qgen); + xive2_end_queue_pic_print_info(end, 6, buf); } - monitor_printf(mon, "\n"); + g_string_append_c(buf, '\n'); } void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, - Monitor *mon) + GString *buf) { Xive2Eas *eas = (Xive2Eas *) &end->w4; uint8_t pq; @@ -127,15 +166,64 @@ void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, pq = xive_get_field32(END2_W1_ESe, end->w1); - monitor_printf(mon, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", - end_idx, - pq & XIVE_ESB_VAL_P ? 'P' : '-', - pq & XIVE_ESB_VAL_Q ? 'Q' : '-', - xive2_eas_is_valid(eas) ? 'v' : ' ', - xive2_eas_is_masked(eas) ? 'M' : ' ', - (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), - (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), - (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); + g_string_append_printf(buf, " %08x %c%c %c%c end:%02x/%04x data:%08x\n", + end_idx, + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xive2_eas_is_valid(eas) ? 'v' : ' ', + xive2_eas_is_masked(eas) ? 'M' : ' ', + (uint8_t) xive_get_field64(EAS2_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS2_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); +} + +void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf) +{ + uint8_t eq_blk = xive_get_field32(NVP2_W5_VP_END_BLOCK, nvp->w5); + uint32_t eq_idx = xive_get_field32(NVP2_W5_VP_END_INDEX, nvp->w5); + uint64_t cache_line = xive2_nvp_reporting_addr(nvp); + + if (!xive2_nvp_is_valid(nvp)) { + return; + } + + g_string_append_printf(buf, " %08x end:%02x/%04x IPB:%02x PGoFirst:%02x", + nvp_idx, eq_blk, eq_idx, + xive_get_field32(NVP2_W2_IPB, nvp->w2), + xive_get_field32(NVP2_W0_PGOFIRST, nvp->w0)); + if (cache_line) { + g_string_append_printf(buf, " reporting CL:%016"PRIx64, cache_line); + } + + /* + * When the NVP is HW controlled, more fields are updated + */ + if (xive2_nvp_is_hw(nvp)) { + g_string_append_printf(buf, " CPPR:%02x", + xive_get_field32(NVP2_W2_CPPR, nvp->w2)); + if (xive2_nvp_is_co(nvp)) { + g_string_append_printf(buf, " CO:%04x", + xive_get_field32(NVP2_W1_CO_THRID, nvp->w1)); + } + } + g_string_append_c(buf, '\n'); +} + +void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, GString *buf) +{ + uint8_t i; + + if (!xive2_nvgc_is_valid(nvgc)) { + return; + } + + g_string_append_printf(buf, " %08x PGoNext:%02x bklog: ", nvgc_idx, + xive_get_field32(NVGC2_W0_PGONEXT, nvgc->w0)); + for (i = 0; i <= XIVE_PRIORITY_MAX; i++) { + g_string_append_printf(buf, "[%d]=0x%x ", + i, xive2_nvgc_get_backlog(nvgc, i)); + } + g_string_append_printf(buf, "\n"); } static void xive2_end_enqueue(Xive2End *end, uint32_t data) @@ -182,13 +270,14 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) * the NVP by changing the H bit while the context is enabled */ -static void xive2_tctx_save_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx) +static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx, + uint8_t ring) { CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; uint32_t pir = env->spr_cb[SPR_PIR].default_value; Xive2Nvp nvp; - uint8_t *regs = &tctx->regs[TM_QW1_OS]; + uint8_t *regs = &tctx->regs[ring]; if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", @@ -233,44 +322,190 @@ static void xive2_tctx_save_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 1); } -static void xive2_os_cam_decode(uint32_t cam, uint8_t *nvp_blk, - uint32_t *nvp_idx, bool *vo, bool *ho) +static void xive2_cam_decode(uint32_t cam, uint8_t *nvp_blk, + uint32_t *nvp_idx, bool *valid, bool *hw) { *nvp_blk = xive2_nvp_blk(cam); *nvp_idx = xive2_nvp_idx(cam); - *vo = !!(cam & TM2_QW1W2_VO); - *ho = !!(cam & TM2_QW1W2_HO); + *valid = !!(cam & TM2_W2_VALID); + *hw = !!(cam & TM2_W2_HW); +} + +/* + * Encode the HW CAM line with 7bit or 8bit thread id. The thread id + * width and block id width is configurable at the IC level. + * + * chipid << 24 | 0000 0000 0000 0000 1 threadid (7Bit) + * chipid << 24 | 0000 0000 0000 0001 threadid (8Bit) + */ +static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; + uint32_t pir = env->spr_cb[SPR_PIR].default_value; + uint8_t blk = xive2_router_get_block_id(xrtr); + uint8_t tid_shift = + xive2_router_get_config(xrtr) & XIVE2_THREADID_8BITS ? 8 : 7; + uint8_t tid_mask = (1 << tid_shift) - 1; + + return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); +} + +static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size, uint8_t ring) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t target_ringw2 = xive_tctx_word2(&tctx->regs[ring]); + uint32_t cam = be32_to_cpu(target_ringw2); + uint8_t nvp_blk; + uint32_t nvp_idx; + uint8_t cur_ring; + bool valid; + bool do_save; + + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save); + + if (!valid) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", + nvp_blk, nvp_idx); + } + + /* Invalidate CAM line of requested ring and all lower rings */ + for (cur_ring = TM_QW0_USER; cur_ring <= ring; + cur_ring += XIVE_TM_RING_SIZE) { + uint32_t ringw2 = xive_tctx_word2(&tctx->regs[cur_ring]); + uint32_t ringw2_new = xive_set_field32(TM2_QW1W2_VO, ringw2, 0); + memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); + } + + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { + xive2_tctx_save_ctx(xrtr, tctx, nvp_blk, nvp_idx, ring); + } + + /* + * Lower external interrupt line of requested ring and below except for + * USER, which doesn't exist. + */ + for (cur_ring = TM_QW1_OS; cur_ring <= ring; + cur_ring += XIVE_TM_RING_SIZE) { + xive_tctx_reset_signal(tctx, cur_ring); + } + return target_ringw2; } uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) +{ + return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW1_OS); +} + +#define REPORT_LINE_GEN1_SIZE 16 + +static void xive2_tm_report_line_gen1(XiveTCTX *tctx, uint8_t *data, + uint8_t size) +{ + uint8_t *regs = tctx->regs; + + g_assert(size == REPORT_LINE_GEN1_SIZE); + memset(data, 0, size); + /* + * See xive architecture for description of what is saved. It is + * hand-picked information to fit in 16 bytes. + */ + data[0x0] = regs[TM_QW3_HV_PHYS + TM_NSR]; + data[0x1] = regs[TM_QW3_HV_PHYS + TM_CPPR]; + data[0x2] = regs[TM_QW3_HV_PHYS + TM_IPB]; + data[0x3] = regs[TM_QW2_HV_POOL + TM_IPB]; + data[0x4] = regs[TM_QW1_OS + TM_ACK_CNT]; + data[0x5] = regs[TM_QW3_HV_PHYS + TM_LGS]; + data[0x6] = 0xFF; + data[0x7] = regs[TM_QW3_HV_PHYS + TM_WORD2] & 0x80; + data[0x7] |= (regs[TM_QW2_HV_POOL + TM_WORD2] & 0x80) >> 1; + data[0x7] |= (regs[TM_QW1_OS + TM_WORD2] & 0x80) >> 2; + data[0x7] |= (regs[TM_QW3_HV_PHYS + TM_WORD2] & 0x3); + data[0x8] = regs[TM_QW1_OS + TM_NSR]; + data[0x9] = regs[TM_QW1_OS + TM_CPPR]; + data[0xA] = regs[TM_QW1_OS + TM_IPB]; + data[0xB] = regs[TM_QW1_OS + TM_LGS]; + if (regs[TM_QW0_USER + TM_WORD2] & 0x80) { + /* + * Logical server extension, except VU bit replaced by EB bit + * from NSR + */ + data[0xC] = regs[TM_QW0_USER + TM_WORD2]; + data[0xC] &= ~0x80; + data[0xC] |= regs[TM_QW0_USER + TM_NSR] & 0x80; + data[0xD] = regs[TM_QW0_USER + TM_WORD2 + 1]; + data[0xE] = regs[TM_QW0_USER + TM_WORD2 + 2]; + data[0xF] = regs[TM_QW0_USER + TM_WORD2 + 3]; + } +} + +static void xive2_tm_pull_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, + unsigned size, uint8_t ring) { Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); - uint32_t qw1w2_new; - uint32_t cam = be32_to_cpu(qw1w2); + uint32_t hw_cam, nvp_idx, xive2_cfg, reserved; uint8_t nvp_blk; - uint32_t nvp_idx; - bool vo; - bool do_save; + Xive2Nvp nvp; + uint64_t phys_addr; + MemTxResult result; - xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_save); + hw_cam = xive2_tctx_hw_cam_line(xptr, tctx); + nvp_blk = xive2_nvp_blk(hw_cam); + nvp_idx = xive2_nvp_idx(hw_cam); - if (!vo) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", nvp_blk, nvp_idx); + return; } - /* Invalidate CAM line */ - qw1w2_new = xive_set_field32(TM2_QW1W2_VO, qw1w2, 0); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2_new, 4); - - if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { - xive2_tctx_save_os_ctx(xrtr, tctx, nvp_blk, nvp_idx); + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; } - xive_tctx_reset_os_signal(tctx); - return qw1w2; + xive2_cfg = xive2_router_get_config(xrtr); + + phys_addr = xive2_nvp_reporting_addr(&nvp) + 0x80; /* odd line */ + if (xive2_cfg & XIVE2_GEN1_TIMA_OS) { + uint8_t pull_ctxt[REPORT_LINE_GEN1_SIZE]; + + xive2_tm_report_line_gen1(tctx, pull_ctxt, REPORT_LINE_GEN1_SIZE); + result = dma_memory_write(&address_space_memory, phys_addr, + pull_ctxt, REPORT_LINE_GEN1_SIZE, + MEMTXATTRS_UNSPECIFIED); + assert(result == MEMTX_OK); + } else { + result = dma_memory_write(&address_space_memory, phys_addr, + &tctx->regs, sizeof(tctx->regs), + MEMTXATTRS_UNSPECIFIED); + assert(result == MEMTX_OK); + reserved = 0xFFFFFFFF; + result = dma_memory_write(&address_space_memory, phys_addr + 12, + &reserved, sizeof(reserved), + MEMTXATTRS_UNSPECIFIED); + assert(result == MEMTX_OK); + } + + /* the rest is similar to pull context to registers */ + xive2_tm_pull_ctx(xptr, tctx, offset, size, ring); +} + +void xive2_tm_pull_os_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_pull_ctx_ol(xptr, tctx, offset, value, size, TM_QW1_OS); +} + + +void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_pull_ctx_ol(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS); } static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, @@ -362,17 +597,31 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - uint32_t cam = value; - uint32_t qw1w2 = cpu_to_be32(cam); + uint32_t cam; + uint32_t qw1w2; + uint64_t qw1dw1; uint8_t nvp_blk; uint32_t nvp_idx; bool vo; bool do_restore; - xive2_os_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_restore); - /* First update the thead context */ - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); + switch (size) { + case 4: + cam = value; + qw1w2 = cpu_to_be32(cam); + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); + break; + case 8: + cam = value >> 32; + qw1dw1 = cpu_to_be64(value); + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1dw1, 8); + break; + default: + g_assert_not_reached(); + } + + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_restore); /* Check the interrupt pending bits */ if (vo) { @@ -381,6 +630,19 @@ void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } +static void xive2_tctx_set_target(XiveTCTX *tctx, uint8_t ring, uint8_t target) +{ + uint8_t *regs = &tctx->regs[ring]; + + regs[TM_T] = target; +} + +void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_set_target(tctx, TM_QW3_HV_PHYS, value & 0xff); +} + /* * XIVE Router (aka. Virtualization Controller or IVRE) */ @@ -443,31 +705,22 @@ int xive2_router_write_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, return xrc->write_nvp(xrtr, nvp_blk, nvp_idx, nvp, word_number); } -static int xive2_router_get_block_id(Xive2Router *xrtr) +int xive2_router_get_nvgc(Xive2Router *xrtr, bool crowd, + uint8_t nvgc_blk, uint32_t nvgc_idx, + Xive2Nvgc *nvgc) { Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); - return xrc->get_block_id(xrtr); + return xrc->get_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, nvgc); } -/* - * Encode the HW CAM line with 7bit or 8bit thread id. The thread id - * width and block id width is configurable at the IC level. - * - * chipid << 24 | 0000 0000 0000 0000 1 threadid (7Bit) - * chipid << 24 | 0000 0000 0000 0001 threadid (8Bit) - */ -static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) +int xive2_router_write_nvgc(Xive2Router *xrtr, bool crowd, + uint8_t nvgc_blk, uint32_t nvgc_idx, + Xive2Nvgc *nvgc) { - Xive2Router *xrtr = XIVE2_ROUTER(xptr); - CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; - uint32_t pir = env->spr_cb[SPR_PIR].default_value; - uint8_t blk = xive2_router_get_block_id(xrtr); - uint8_t tid_shift = - xive2_router_get_config(xrtr) & XIVE2_THREADID_8BITS ? 8 : 7; - uint8_t tid_mask = (1 << tid_shift) - 1; + Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); - return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); + return xrc->write_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, nvgc); } /* @@ -542,7 +795,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 +874,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 */ @@ -651,7 +904,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } found = xive_presenter_notify(xrtr->xfb, format, nvp_blk, nvp_idx, - xive_get_field32(END2_W6_IGNORE, end.w7), + xive2_end_is_ignore(&end), priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7)); @@ -702,7 +955,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 +1207,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..48cd3ae94b 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(), } @@ -536,7 +536,7 @@ static void xlnx_pmu_io_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xlnx_pmu_io_intc_reset; + device_class_set_legacy_reset(dc, xlnx_pmu_io_intc_reset); dc->realize = xlnx_pmu_io_intc_realize; dc->vmsd = &vmstate_xlnx_pmu_io_intc; device_class_set_props(dc, xlnx_pmu_io_intc_properties); diff --git a/hw/intc/xlnx-zynqmp-ipi.c b/hw/intc/xlnx-zynqmp-ipi.c index adc1179014..7241377298 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(), } @@ -359,7 +359,7 @@ static void xlnx_zynqmp_ipi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xlnx_zynqmp_ipi_reset; + device_class_set_legacy_reset(dc, xlnx_zynqmp_ipi_reset); dc->realize = xlnx_zynqmp_ipi_realize; dc->vmsd = &vmstate_zynqmp_pmu_ipi; } 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 a83e7243d6..7b36d51494 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -68,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; @@ -102,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) diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index b2ed70b9da..f52b32e590 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -67,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; @@ -101,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 @@ -135,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); 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 d0991ab7f9..56865df7db 100644 --- a/hw/ipmi/smbus_ipmi.c +++ b/hw/ipmi/smbus_ipmi.c @@ -299,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), diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig index 18b5c6bf3f..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,17 +34,9 @@ config PC87312 select I8254 select I8257 select MC146818RTC - select SERIAL_ISA - select PARALLEL - select FDC_ISA select IDE_ISA -config PIIX3 - bool - select I8257 - select ISA_BUS - -config PIIX4 +config PIIX bool # For historical reasons, SuperIO devices are created in the board # for PIIX4. @@ -51,10 +51,10 @@ config PIIX4 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 @@ -62,14 +62,10 @@ config VT82C686 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 @@ -77,5 +73,5 @@ config LPC_ICH9 # 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 f0c035c2f5..92654b358c 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -24,7 +24,6 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "hw/isa/isa.h" -#include "hw/acpi/acpi_aml_interface.h" static ISABus *isabus; @@ -53,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. @@ -82,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) @@ -100,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]; @@ -115,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); } @@ -144,7 +156,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; } @@ -173,6 +185,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; @@ -196,15 +213,6 @@ ISADevice *isa_vga_init(ISABus *bus) } } -void isa_build_aml(ISABus *bus, Aml *scope) -{ - BusChild *kid; - - QTAILQ_FOREACH(kid, &bus->parent_obj.children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } -} - static void isabus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -231,7 +239,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..cff756e791 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -16,11 +16,13 @@ #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/serial.h" +#include "hw/char/parallel-isa.h" +#include "hw/char/serial-isa.h" #include "trace.h" static void isa_superio_realize(DeviceState *dev, Error **errp) @@ -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 6c44cc9767..dabd1217dd 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -40,10 +40,10 @@ #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/acpi/ich9_timer.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" #include "sysemu/runstate.h" @@ -52,12 +52,11 @@ #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. @@ -162,6 +161,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)); @@ -177,6 +177,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; } @@ -256,7 +257,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; @@ -272,7 +273,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); @@ -283,7 +284,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; @@ -303,6 +304,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); } @@ -404,14 +420,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; @@ -437,8 +452,6 @@ void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) sizeof lpc->smi_features_ok, true); } - - ich9_lpc_reset(DEVICE(lpc)); } /* APM */ @@ -519,6 +532,15 @@ ich9_lpc_pmcon_update(ICH9LPCState *lpc) uint16_t gen_pmcon_1 = pci_get_word(lpc->d.config + ICH9_LPC_GEN_PMCON_1); uint16_t wmask; + if (lpc->pm.swsmi_timer_enabled) { + ich9_pm_update_swsmi_timer( + &lpc->pm, lpc->pm.smi_en & ICH9_PMIO_SMI_EN_SWSMI_EN); + } + if (lpc->pm.periodic_timer_enabled) { + ich9_pm_update_periodic_timer( + &lpc->pm, lpc->pm.smi_en & ICH9_PMIO_SMI_EN_PERIODIC_EN); + } + if (gen_pmcon_1 & ICH9_LPC_GEN_PMCON_1_SMI_LOCK) { wmask = pci_get_word(lpc->d.wmask + ICH9_LPC_GEN_PMCON_1); wmask &= ~ICH9_LPC_GEN_PMCON_1_SMI_LOCK; @@ -660,6 +682,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, @@ -676,8 +703,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))) { @@ -706,8 +734,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); @@ -720,11 +746,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); - i8257_dma_init(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) @@ -739,7 +777,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() } @@ -759,7 +797,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), @@ -773,7 +811,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), @@ -781,7 +819,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 @@ -789,14 +827,19 @@ 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, ICH9_LPC_SMI_F_CPU_HOTPLUG_BIT, true), DEFINE_PROP_BIT64("x-smi-cpu-hotunplug", ICH9LPCState, smi_host_features, ICH9_LPC_SMI_F_CPU_HOT_UNPLUG_BIT, true), + DEFINE_PROP_BOOL("x-smi-swsmi-timer", ICH9LPCState, + pm.swsmi_timer_enabled, true), + DEFINE_PROP_BOOL("x-smi-periodic-timer", ICH9LPCState, + pm.periodic_timer_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -810,9 +853,7 @@ static void ich9_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) { Aml *field; - BusChild *kid; - ICH9LPCState *s = ICH9_LPC_DEVICE(adev); - BusState *bus = BUS(s->isa_bus); + BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0"); Aml *sb_scope = aml_scope("\\_SB"); /* ICH9 PCI to ISA irq remapping */ @@ -832,9 +873,7 @@ static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(sb_scope, field); aml_append(scope, sb_scope); - QTAILQ_FOREACH(kid, &bus->children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } + qbus_build_aml(bus, scope); } static void ich9_lpc_class_init(ObjectClass *klass, void *data) @@ -846,7 +885,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) AcpiDevAmlIfClass *amldevc = ACPI_DEV_AML_IF_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->reset = ich9_lpc_reset; + device_class_set_legacy_reset(dc, ich9_lpc_reset); k->realize = ich9_lpc_realize; dc->vmsd = &vmstate_ich9_lpc; device_class_set_props(dc, ich9_lpc_properties); @@ -865,9 +904,9 @@ 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; } 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..f67155498d 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; + device_class_set_legacy_reset(dc, 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..b4a402f61b --- /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; + device_class_set_legacy_reset(dc, 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 eabad7ba58..0000000000 --- a/hw/isa/piix3.c +++ /dev/null @@ -1,448 +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 "qapi/error.h" -#include "hw/dma/i8257.h" -#include "hw/southbridge/piix.h" -#include "hw/irq.h" -#include "hw/isa/isa.h" -#include "hw/xen/xen.h" -#include "sysemu/runstate.h" -#include "migration/vmstate.h" -#include "hw/acpi/acpi_aml_interface.h" - -#define XEN_PIIX_NUM_PIRQS 128ULL - -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); -} - -/* - * 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; -} - -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) -{ - 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(address + i - PIIX_PIRQCA, v); - } - } - - piix3_write_config(dev, address, val, len); -} - -static void piix3_reset(DeviceState *dev) -{ - PIIX3State *d = PIIX3_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 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, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void pci_piix3_realize(PCIDevice *dev, Error **errp) -{ - PIIX3State *d = PIIX3_PCI_DEVICE(dev); - ISABus *isa_bus; - - 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, - "piix3-reset-control", 1); - memory_region_add_subregion_overlap(pci_address_space_io(dev), - PIIX_RCR_IOPORT, &d->rcr_mem, 1); - - i8257_dma_init(isa_bus, 0); -} - -static void build_pci_isa_aml(AcpiDevAmlIf *adev, Aml *scope) -{ - Aml *field; - BusChild *kid; - Aml *sb_scope = aml_scope("\\_SB"); - BusState *bus = qdev_get_child_bus(DEVICE(adev), "isa.0"); - - /* 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); - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - call_dev_aml_func(DEVICE(kid->child), scope); - } -} - -static void pci_piix3_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); - - dc->reset = piix3_reset; - dc->desc = "ISA bridge"; - dc->vmsd = &vmstate_piix3; - dc->hotpluggable = false; - 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; - adevc->build_dev_aml = build_pci_isa_aml; -} - -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 }, - { TYPE_ACPI_DEV_AML_IF }, - { }, - }, -}; - -static void piix3_realize(PCIDevice *dev, Error **errp) -{ - ERRP_GUARD(); - PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); - PCIBus *pci_bus = pci_get_bus(dev); - - pci_piix3_realize(dev, errp); - if (*errp) { - return; - } - - 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); -} - -static void piix3_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_write = piix3_write_config; - k->realize = piix3_realize; -} - -static const TypeInfo piix3_info = { - .name = TYPE_PIIX3_DEVICE, - .parent = TYPE_PIIX3_PCI_DEVICE, - .class_init = piix3_class_init, -}; - -static void piix3_xen_realize(PCIDevice *dev, Error **errp) -{ - ERRP_GUARD(); - PIIX3State *piix3 = PIIX3_PCI_DEVICE(dev); - PCIBus *pci_bus = pci_get_bus(dev); - - pci_piix3_realize(dev, errp); - if (*errp) { - return; - } - - /* - * 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(pci_bus, xen_piix3_set_irq, xen_pci_slot_get_pirq, - piix3, XEN_PIIX_NUM_PIRQS); -} - -static void piix3_xen_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->config_write = piix3_write_config_xen; - k->realize = piix3_xen_realize; -} - -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) diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c deleted file mode 100644 index 8fc1db6dc9..0000000000 --- a/hw/isa/piix4.c +++ /dev/null @@ -1,327 +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/ide/piix.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 "hw/acpi/piix4.h" -#include "hw/usb/hcd-uhci.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; - PCIIDEState ide; - UHCIState uhci; - PIIX4PMState pm; - /* 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 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; - } -} - -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; - - d->rcr = 0; -} - -static int piix4_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_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); - PCIBus *pci_bus = pci_get_bus(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); - - /* IDE */ - qdev_prop_set_int32(DEVICE(&s->ide), "addr", dev->devfn + 1); - if (!qdev_realize(DEVICE(&s->ide), BUS(pci_bus), errp)) { - return; - } - - /* USB */ - qdev_prop_set_int32(DEVICE(&s->uhci), "addr", dev->devfn + 2); - if (!qdev_realize(DEVICE(&s->uhci), BUS(pci_bus), errp)) { - return; - } - - /* ACPI controller */ - qdev_prop_set_int32(DEVICE(&s->pm), "addr", dev->devfn + 3); - if (!qdev_realize(DEVICE(&s->pm), BUS(pci_bus), errp)) { - return; - } - qdev_connect_gpio_out(DEVICE(&s->pm), 0, s->isa[9]); - - pci_bus_irqs(pci_bus, piix4_set_irq, pci_slot_get_pirq, s, PIIX_NUM_PIRQS); -} - -static void piix4_init(Object *obj) -{ - PIIX4State *s = PIIX4_PCI_DEVICE(obj); - - object_initialize_child(obj, "rtc", &s->rtc, TYPE_MC146818_RTC); - object_initialize_child(obj, "ide", &s->ide, TYPE_PIIX4_IDE); - object_initialize_child(obj, "uhci", &s->uhci, "piix4-usb-uhci"); - - object_initialize_child(obj, "pm", &s->pm, TYPE_PIIX4_PM); - qdev_prop_set_uint32(DEVICE(&s->pm), "smb_io_base", 0x1100); - qdev_prop_set_bit(DEVICE(&s->pm), "smm-enabled", 0); -} - -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) 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 3f9bd0c04d..6f44b381a5 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -15,6 +15,9 @@ #include "qemu/osdep.h" #include "hw/isa/vt82c686.h" +#include "hw/block/fdc.h" +#include "hw/char/parallel-isa.h" +#include "hw/char/serial-isa.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" #include "hw/ide/pci.h" @@ -82,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), @@ -229,7 +232,7 @@ static void via_pm_class_init(ObjectClass *klass, void *data) k->device_id = info->device_id; k->class_id = PCI_CLASS_BRIDGE_OTHER; k->revision = 0x40; - dc->reset = via_pm_reset; + device_class_set_legacy_reset(dc, via_pm_reset); /* Reason: part of VIA south bridge, does not exist stand alone */ dc->user_creatable = false; dc->vmsd = &vmstate_acpi; @@ -323,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 = { @@ -368,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); @@ -395,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); @@ -424,7 +461,7 @@ static void vt82c686b_superio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); - dc->reset = vt82c686b_superio_reset; + device_class_set_legacy_reset(dc, vt82c686b_superio_reset); sc->serial.count = 2; sc->parallel.count = 1; sc->ide.count = 0; /* emulated by via-ide */ @@ -465,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); @@ -513,20 +565,13 @@ 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); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); - dc->reset = vt8231_superio_reset; + device_class_set_legacy_reset(dc, 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; @@ -547,14 +592,17 @@ OBJECT_DECLARE_SIMPLE_TYPE(ViaISAState, VIA_ISA) struct ViaISAState { PCIDevice dev; + + IRQState i8259_irq; qemu_irq cpu_intr; - qemu_irq *isa_irqs; + qemu_irq *isa_irqs_in; + uint16_t irq_state[ISA_NUM_IRQS]; ViaSuperIOState via_sio; - RTCState rtc; + MC146818RtcState rtc; PCIIDEState ide; UHCIState uhci[2]; ViaPMState pm; - PCIDevice ac97; + ViaAC97State ac97; PCIDevice mc97; }; @@ -562,7 +610,7 @@ 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() } @@ -592,10 +640,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) @@ -609,12 +717,12 @@ 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); - isa_irq = qemu_allocate_irqs(via_isa_request_i8259_irq, s, 1); + qdev_init_gpio_out_named(dev, &s->cpu_intr, "intr", 1); + qdev_init_gpio_in_named(dev, via_isa_pirq, "pirq", PCI_NUM_PINS); + qemu_init_irq(&s->i8259_irq, via_isa_request_i8259_irq, s, 0); isa_bus = isa_bus_new(dev, pci_address_space(d), pci_address_space_io(d), errp); @@ -622,10 +730,10 @@ static void via_isa_realize(PCIDevice *d, Error **errp) return; } - s->isa_irqs = i8259_init(isa_bus, *isa_irq); - isa_bus_irqs(isa_bus, s->isa_irqs); + s->isa_irqs_in = i8259_init(isa_bus, &s->i8259_irq); + isa_bus_register_input_irqs(isa_bus, s->isa_irqs_in); i8254_pit_init(isa_bus, 0x40, 0, NULL); - i8257_dma_init(isa_bus, 0); + i8257_dma_init(OBJECT(d), isa_bus, 0); /* RTC */ qdev_prop_set_int32(DEVICE(&s->rtc), "base_year", 2000); @@ -650,6 +758,10 @@ static void via_isa_realize(PCIDevice *d, Error **errp) 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++) { @@ -732,7 +844,7 @@ static void vt82c686b_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VIA_82C686B_ISA; k->class_id = PCI_CLASS_BRIDGE_ISA; k->revision = 0x40; - dc->reset = vt82c686b_isa_reset; + device_class_set_legacy_reset(dc, vt82c686b_isa_reset); dc->desc = "ISA bridge"; dc->vmsd = &vmstate_via; /* Reason: part of VIA VT82C686 southbridge, needs to be wired up */ @@ -772,6 +884,7 @@ 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 */ @@ -796,7 +909,7 @@ static void vt8231_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_VIA_8231_ISA; k->class_id = PCI_CLASS_BRIDGE_ISA; k->revision = 0x10; - dc->reset = vt8231_isa_reset; + device_class_set_legacy_reset(dc, vt8231_isa_reset); dc->desc = "ISA bridge"; dc->vmsd = &vmstate_via; /* Reason: part of VIA VT8231 southbridge, needs to be wired up */ diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 17d15b6c90..fe1c6feac1 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -1,13 +1,14 @@ config LOONGARCH_VIRT bool + default y + depends on LOONGARCH64 && FDT + select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE - imply VIRTIO_VGA imply PCI_DEVICES imply NVDIMM - select ISA_BUS - select SERIAL - select SERIAL_ISA + imply TPM_TIS_SYSBUS + select SERIAL_MM select VIRTIO_PCI select PLATFORM_BUS select LOONGARCH_IPI @@ -20,3 +21,5 @@ config LOONGARCH_VIRT 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 index 7d5f5a757d..50709bda0f 100644 --- a/hw/loongarch/acpi-build.c +++ b/hw/loongarch/acpi-build.c @@ -7,6 +7,7 @@ #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" @@ -22,7 +23,6 @@ /* Supported chipsets: */ #include "hw/pci-host/ls7a.h" #include "hw/loongarch/virt.h" -#include "hw/acpi/aml-build.h" #include "hw/acpi/utils.h" #include "hw/acpi/pci.h" @@ -31,9 +31,11 @@ #include "hw/acpi/generic_event_device.h" #include "hw/pci-host/gpex.h" +#include "sysemu/sysemu.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 @@ -104,12 +106,15 @@ build_facs(GArray *table_data) /* build MADT */ static void -build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) +build_madt(GArray *table_data, BIOSLinker *linker, + LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); - int i; - AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; + MachineState *ms = MACHINE(lvms); + 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 = lvms->oem_id, + .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); @@ -117,13 +122,15 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) build_append_int_noprefix(table_data, 0, 4); build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ - for (i = 0; i < ms->smp.cpus; i++) { + 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 + 1, 4); /* ACPI Processor ID */ - build_append_int_noprefix(table_data, i, 4); /* Core ID */ + 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 */ } @@ -159,23 +166,30 @@ build_madt(GArray *table_data, BIOSLinker *linker, LoongArchMachineState *lams) static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { - uint64_t i; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); - MachineState *ms = MACHINE(lams); - AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lams->oem_id, - .oem_table_id = lams->oem_table_id }; + int i, arch_id, node_id; + hwaddr len, base, gap; + NodeInfo *numa_info; + int nodes, nb_numa_nodes = machine->numa_state->num_nodes; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(lvms); + const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); + AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id, + .oem_table_id = lvms->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 < ms->smp.cpus; ++i) { + 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, 0, 1); - build_append_int_noprefix(table_data, i, 1); /* APIC ID */ + 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 */ @@ -184,21 +198,88 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } - build_srat_memory(table_data, VIRT_LOWMEM_BASE, VIRT_LOWMEM_SIZE, - 0, MEM_AFFINITY_ENABLED); + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + numa_info = machine->numa_state->nodes; + nodes = nb_numa_nodes; + if (!nodes) { + nodes = 1; + } - build_srat_memory(table_data, VIRT_HIGHMEM_BASE, machine->ram_size - VIRT_LOWMEM_SIZE, - 0, MEM_AFFINITY_ENABLED); + for (i = 0; i < nodes; i++) { + if (nb_numa_nodes) { + len = numa_info[i].node_mem; + } else { + len = machine->ram_size; + } - if (ms->device_memory) { - build_srat_memory(table_data, ms->device_memory->base, - memory_region_size(&ms->device_memory->mr), - 0, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); + /* + * memory for the node splited into two part + * lowram: [base, +gap) + * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) + */ + if (len >= gap) { + build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED); + len -= gap; + base = VIRT_HIGHMEM_BASE; + gap = machine->ram_size - VIRT_LOWMEM_SIZE; + } + + if (len) { + build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); + base += len; + gap -= len; + } + } + + if (machine->device_memory) { + build_srat_memory(table_data, machine->device_memory->base, + memory_region_size(&machine->device_memory->mr), + nodes - 1, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } acpi_table_end(linker, &table); } +/* + * Serial Port Console Redirection Table (SPCR) + * https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table + */ +static void +spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine) +{ + LoongArchVirtMachineState *lvms; + 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 = VIRT_UART_BASE, + .interrupt_type = 0, /* Interrupt not supported */ + .pc_interrupt = 0, + .interrupt = VIRT_UART_IRQ, + .baud_rate = 7, /* 115200 */ + .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, + }; + + lvms = LOONGARCH_VIRT_MACHINE(machine); + build_spcr(table_data, linker, &serial, 2, lvms->oem_id, + lvms->oem_table_id); +} + typedef struct AcpiBuildState { /* Copy of table in RAM (for patching). */ @@ -210,23 +291,27 @@ struct AcpiBuildState { MemoryRegion *linker_mr; } AcpiBuildState; -static void build_uart_device_aml(Aml *table) +static void build_uart_device_aml(Aml *table, int index) { Aml *dev; Aml *crs; Aml *pkg0, *pkg1, *pkg2; - uint32_t uart_irq = VIRT_UART_IRQ; + Aml *scope; + uint32_t uart_irq; + uint64_t base; - Aml *scope = aml_scope("_SB"); - dev = aml_device("COMA"); + uart_irq = VIRT_UART_IRQ + index; + base = VIRT_UART_BASE + index * VIRT_UART_SIZE; + scope = aml_scope("_SB"); + dev = aml_device("COM%d", index); 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("_UID", aml_int(index))); 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, base, 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)); @@ -248,22 +333,23 @@ static void build_la_ged_aml(Aml *dsdt, MachineState *machine) { uint32_t event; - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); build_ged_aml(dsdt, "\\_SB."GED_DEVICE, - HOTPLUG_HANDLER(lams->acpi_ged), + HOTPLUG_HANDLER(lvms->acpi_ged), VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, VIRT_GED_EVT_ADDR); - event = object_property_get_uint(OBJECT(lams->acpi_ged), + event = object_property_get_uint(OBJECT(lvms->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) +static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms) { struct GPEXConfig cfg = { .mmio64.base = VIRT_PCI_MEM_BASE, @@ -272,15 +358,55 @@ static void build_pci_device_aml(Aml *scope, LoongArchMachineState *lams) .pio.size = VIRT_PCI_IO_SIZE, .ecam.base = VIRT_PCI_CFG_BASE, .ecam.size = VIRT_PCI_CFG_SIZE, - .irq = PCH_PIC_IRQ_OFFSET + VIRT_DEVICE_IRQS, - .bus = lams->pci_bus, + .irq = VIRT_GSI_BASE + VIRT_DEVICE_IRQS, + .bus = lvms->pci_bus, }; acpi_dsdt_add_gpex(scope, &cfg); } +static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms) +{ + Aml *dev, *crs; + MemoryRegion *flash_mem; + + hwaddr flash0_base; + hwaddr flash0_size; + + hwaddr flash1_base; + hwaddr flash1_size; + + flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + + flash_mem = pflash_cfi01_get_memory(lvms->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) +static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms) { PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; @@ -318,18 +444,21 @@ static void acpi_dsdt_add_tpm(Aml *scope, LoongArchMachineState *vms) static void build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) { + int i; 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 }; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); + AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id, + .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); - build_uart_device_aml(dsdt); - build_pci_device_aml(dsdt, lams); + for (i = 0; i < VIRT_UART_COUNT; i++) + build_uart_device_aml(dsdt, i); + build_pci_device_aml(dsdt, lvms); build_la_ged_aml(dsdt, machine); + build_flash_aml(dsdt, lvms); #ifdef CONFIG_TPM - acpi_dsdt_add_tpm(dsdt, lams); + acpi_dsdt_add_tpm(dsdt, lvms); #endif /* System State Package */ scope = aml_scope("\\"); @@ -348,7 +477,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) static void acpi_build(AcpiBuildTables *tables, MachineState *machine) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); GArray *table_offsets; AcpiFadtData fadt_data; unsigned facs, rsdt, dsdt; @@ -382,13 +511,32 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) 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); + lvms->oem_id, lvms->oem_table_id); acpi_add_table(table_offsets, tables_blob); - build_madt(tables_blob, tables->linker, lams); + build_madt(tables_blob, tables->linker, lvms); + + acpi_add_table(table_offsets, tables_blob); + build_pptt(tables_blob, tables->linker, machine, + lvms->oem_id, lvms->oem_table_id); acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); + acpi_add_table(table_offsets, tables_blob); + spcr_setup(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, lvms->oem_id, + lvms->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, + lvms->oem_id, lvms->oem_table_id); + } + } acpi_add_table(table_offsets, tables_blob); { @@ -396,8 +544,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) .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); + build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, + lvms->oem_table_id); } #ifdef CONFIG_TPM @@ -405,8 +553,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) 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); + tables->tcpalog, lvms->oem_id, + lvms->oem_table_id); } #endif /* Add tables supplied by user (if any) */ @@ -420,13 +568,13 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) /* 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); + lvms->oem_id, lvms->oem_table_id); /* RSDP is in FSEG memory, so allocate it separately */ { AcpiRsdpData rsdp_data = { .revision = 0, - .oem_id = lams->oem_id, + .oem_id = lvms->oem_id, .xsdt_tbl_offset = NULL, .rsdt_tbl_offset = &rsdt, }; @@ -442,7 +590,7 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) " 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->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); @@ -497,23 +645,31 @@ 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() }, }; -void loongarch_acpi_setup(LoongArchMachineState *lams) +static bool loongarch_is_acpi_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->acpi == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +void loongarch_acpi_setup(LoongArchVirtMachineState *lvms) { AcpiBuildTables tables; AcpiBuildState *build_state; - if (!lams->fw_cfg) { + if (!lvms->fw_cfg) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; } - if (!loongarch_is_acpi_enabled(lams)) { + if (!loongarch_is_acpi_enabled(lvms)) { ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); return; } @@ -521,7 +677,7 @@ void loongarch_acpi_setup(LoongArchMachineState *lams) build_state = g_malloc0(sizeof *build_state); acpi_build_tables_init(&tables); - acpi_build(&tables, MACHINE(lams)); + acpi_build(&tables, MACHINE(lvms)); /* Now expose it all to Guest */ build_state->table_mr = acpi_add_rom_blob(acpi_build_update, @@ -537,6 +693,9 @@ void loongarch_acpi_setup(LoongArchMachineState *lams) build_state, tables.rsdp, ACPI_BUILD_RSDP_FILE); + fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, + acpi_data_len(tables.tcpalog)); + qemu_register_reset(acpi_build_reset, build_state); acpi_build_reset(build_state); vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c new file mode 100644 index 0000000000..f258eefe9a --- /dev/null +++ b/hw/loongarch/boot.c @@ -0,0 +1,338 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch boot helper functions. + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "target/loongarch/cpu.h" +#include "hw/loongarch/virt.h" +#include "hw/loader.h" +#include "elf.h" +#include "qemu/error-report.h" +#include "sysemu/reset.h" +#include "sysemu/qtest.h" + +struct memmap_entry *memmap_table; +unsigned memmap_entries; + +ram_addr_t initrd_offset; +uint64_t initrd_size; + +static const unsigned int slave_boot_code[] = { + /* Configure reset ebase. */ + 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ + + /* Disable interrupt. */ + 0x0380100c, /* ori $t0, $zero,0x4 */ + 0x04000180, /* csrxchg $zero, $t0, LOONGARCH_CSR_CRMD */ + + /* Clear mailbox. */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ + 0x06481da0, /* iocsrwr.d $zero, $t1 */ + + /* Enable IPI interrupt. */ + 0x1400002c, /* lu12i.w $t0, 1(0x1) */ + 0x0400118c, /* csrxchg $t0, $t0, LOONGARCH_CSR_ECFG */ + 0x02fffc0c, /* addi.d $t0, $r0,-1(0xfff) */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038011ad, /* ori $t1, $t1, CORE_EN_OFF */ + 0x064819ac, /* iocsrwr.w $t0, $t1 */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ + + /* Wait for wakeup <.L11>: */ + 0x06488000, /* idle 0x0 */ + 0x03400000, /* andi $zero, $zero, 0x0 */ + 0x064809ac, /* iocsrrd.w $t0, $t1 */ + 0x43fff59f, /* beqz $t0, -12(0x7ffff4) # 48 <.L11> */ + + /* Read and clear IPI interrupt. */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x064809ac, /* iocsrrd.w $t0, $t1 */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038031ad, /* ori $t1, $t1, CORE_CLEAR_OFF */ + 0x064819ac, /* iocsrwr.w $t0, $t1 */ + + /* Disable IPI interrupt. */ + 0x1400002c, /* lu12i.w $t0, 1(0x1) */ + 0x04001180, /* csrxchg $zero, $t0, LOONGARCH_CSR_ECFG */ + + /* Read mail buf and jump to specified entry */ + 0x1400002d, /* lu12i.w $t1, 1(0x1) */ + 0x038081ad, /* ori $t1, $t1, CORE_BUF_20 */ + 0x06480dac, /* iocsrrd.d $t0, $t1 */ + 0x00150181, /* move $ra, $t0 */ + 0x4c000020, /* jirl $zero, $ra,0 */ +}; + +static inline void *guidcpy(void *dst, const void *src) +{ + return memcpy(dst, src, sizeof(efi_guid_t)); +} + +static void init_efi_boot_memmap(struct efi_system_table *systab, + void *p, void *start) +{ + unsigned i; + struct efi_boot_memmap *boot_memmap = p; + efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; + + /* efi_configuration_table 1 */ + guidcpy(&systab->tables[0].guid, &tbl_guid); + systab->tables[0].table = (struct efi_configuration_table *)(p - start); + systab->nr_tables = 1; + + boot_memmap->desc_size = sizeof(efi_memory_desc_t); + boot_memmap->desc_ver = 1; + boot_memmap->map_size = 0; + + efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap); + for (i = 0; i < memmap_entries; i++) { + map = (void *)boot_memmap + sizeof(*map); + map[i].type = memmap_table[i].type; + map[i].phys_addr = ROUND_UP(memmap_table[i].address, 64 * KiB); + map[i].num_pages = ROUND_DOWN(memmap_table[i].address + + memmap_table[i].length - map[i].phys_addr, 64 * KiB); + p += sizeof(efi_memory_desc_t); + } +} + +static void init_efi_initrd_table(struct efi_system_table *systab, + void *p, void *start) +{ + efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; + struct efi_initrd *initrd_table = p; + + /* efi_configuration_table 2 */ + guidcpy(&systab->tables[1].guid, &tbl_guid); + systab->tables[1].table = (struct efi_configuration_table *)(p - start); + systab->nr_tables = 2; + + initrd_table->base = initrd_offset; + initrd_table->size = initrd_size; +} + +static void init_efi_fdt_table(struct efi_system_table *systab) +{ + efi_guid_t tbl_guid = DEVICE_TREE_GUID; + + /* efi_configuration_table 3 */ + guidcpy(&systab->tables[2].guid, &tbl_guid); + systab->tables[2].table = (void *)FDT_BASE; + systab->nr_tables = 3; +} + +static void init_systab(struct loongarch_boot_info *info, void *p, void *start) +{ + void *bp_tables_start; + struct efi_system_table *systab = p; + + info->a2 = p - start; + + systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; + systab->hdr.revision = EFI_SPECIFICATION_VERSION; + systab->hdr.revision = sizeof(struct efi_system_table), + systab->fw_revision = FW_VERSION << 16 | FW_PATCHLEVEL << 8; + systab->runtime = 0; + systab->boottime = 0; + systab->nr_tables = 0; + + p += ROUND_UP(sizeof(struct efi_system_table), 64 * KiB); + + systab->tables = p; + bp_tables_start = p; + + init_efi_boot_memmap(systab, p, start); + p += ROUND_UP(sizeof(struct efi_boot_memmap) + + sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); + init_efi_initrd_table(systab, p, start); + p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); + init_efi_fdt_table(systab); + + systab->tables = (struct efi_configuration_table *)(bp_tables_start - start); +} + +static void init_cmdline(struct loongarch_boot_info *info, void *p, void *start) +{ + hwaddr cmdline_addr = p - start; + + info->a0 = 1; + info->a1 = cmdline_addr; + + g_strlcpy(p, info->kernel_cmdline, COMMAND_LINE_SIZE); +} + +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(struct loongarch_boot_info *info) +{ + uint64_t kernel_entry, kernel_low, kernel_high; + ssize_t kernel_size; + + kernel_size = load_elf(info->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", + info->kernel_filename, + load_elf_strerror(kernel_size)); + exit(1); + } + + if (info->initrd_filename) { + initrd_size = get_image_size(info->initrd_filename); + if (initrd_size > 0) { + initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); + + if (initrd_offset + initrd_size > info->ram_size) { + error_report("memory too small for initial ram disk '%s'", + info->initrd_filename); + exit(1); + } + + initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, + info->ram_size - initrd_offset); + } + + if (initrd_size == (target_ulong)-1) { + error_report("could not load initial ram disk '%s'", + info->initrd_filename); + exit(1); + } + } else { + initrd_size = 0; + } + + return kernel_entry; +} + +static void reset_load_elf(void *opaque) +{ + LoongArchCPU *cpu = opaque; + CPULoongArchState *env = &cpu->env; + + cpu_reset(CPU(cpu)); + if (env->load_elf) { + if (cpu == LOONGARCH_CPU(first_cpu)) { + env->gpr[4] = env->boot_info->a0; + env->gpr[5] = env->boot_info->a1; + env->gpr[6] = env->boot_info->a2; + } + cpu_set_pc(CPU(cpu), env->elf_address); + } +} + +static void fw_cfg_add_kernel_info(struct loongarch_boot_info *info, + 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, + info->kernel_filename, + false); + + if (info->initrd_filename) { + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + info->initrd_filename, false); + } + + if (info->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(info->kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + info->kernel_cmdline); + } +} + +static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms, + struct loongarch_boot_info *info) +{ + fw_cfg_add_kernel_info(info, lvms->fw_cfg); +} + +static void init_boot_rom(struct loongarch_boot_info *info, void *p) +{ + void *start = p; + + init_cmdline(info, p, start); + p += COMMAND_LINE_SIZE; + + init_systab(info, p, start); +} + +static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) +{ + void *p, *bp; + int64_t kernel_addr = VIRT_FLASH0_BASE; + LoongArchCPU *lacpu; + CPUState *cs; + + if (info->kernel_filename) { + kernel_addr = load_kernel_info(info); + } else { + if(!qtest_enabled()) { + warn_report("No kernel provided, booting from flash drive."); + } + } + + /* Load cmdline and system tables at [0 - 1 MiB] */ + p = g_malloc0(1 * MiB); + bp = p; + init_boot_rom(info, p); + rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory); + + /* Load slave boot code at pflash0 . */ + void *boot_code = g_malloc0(VIRT_FLASH0_SIZE); + memcpy(boot_code, &slave_boot_code, sizeof(slave_boot_code)); + rom_add_blob_fixed("boot_code", boot_code, VIRT_FLASH0_SIZE, VIRT_FLASH0_BASE); + + CPU_FOREACH(cs) { + lacpu = LOONGARCH_CPU(cs); + lacpu->env.load_elf = true; + if (cs == first_cpu) { + lacpu->env.elf_address = kernel_addr; + } else { + lacpu->env.elf_address = VIRT_FLASH0_BASE; + } + lacpu->env.boot_info = info; + } + + g_free(boot_code); + g_free(bp); +} + +void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + int i; + + /* register reset function */ + for (i = 0; i < ms->smp.cpus; i++) { + qemu_register_reset(reset_load_elf, LOONGARCH_CPU(qemu_get_cpu(i))); + } + + info->kernel_filename = ms->kernel_filename; + info->kernel_cmdline = ms->kernel_cmdline; + info->initrd_filename = ms->initrd_filename; + + if (lvms->bios_loaded) { + loongarch_firmware_boot(lvms, info); + } else { + loongarch_direct_kernel_boot(info); + } +} diff --git a/hw/loongarch/fw_cfg.c b/hw/loongarch/fw_cfg.c index f15a17416c..35aeb2decb 100644 --- a/hw/loongarch/fw_cfg.c +++ b/hw/loongarch/fw_cfg.c @@ -17,7 +17,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, 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 *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms) { FWCfgState *fw_cfg; int max_cpus = ms->smp.max_cpus; diff --git a/hw/loongarch/fw_cfg.h b/hw/loongarch/fw_cfg.h index 7c0de4db4a..27ee68286e 100644 --- a/hw/loongarch/fw_cfg.h +++ b/hw/loongarch/fw_cfg.h @@ -11,5 +11,5 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" -FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); +FWCfgState *virt_fw_cfg_init(ram_addr_t ram_size, MachineState *ms); #endif diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build index c0421502ab..005f017e21 100644 --- a/hw/loongarch/meson.build +++ b/hw/loongarch/meson.build @@ -1,8 +1,9 @@ loongarch_ss = ss.source_set() loongarch_ss.add(files( - 'fw_cfg.c', + 'boot.c', )) -loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: [files('virt.c'), fdt]) +common_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('fw_cfg.c')) +loongarch_ss.add(when: 'CONFIG_LOONGARCH_VIRT', if_true: files('virt.c')) 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 index 958be74fa1..9a635d1d3d 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -9,7 +9,9 @@ #include "qemu/datadir.h" #include "qapi/error.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" +#include "sysemu/kvm.h" +#include "sysemu/tcg.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" #include "sysemu/runstate.h" @@ -42,42 +44,307 @@ #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 "hw/virtio/virtio-iommu.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" -static void fdt_add_rtc_node(LoongArchMachineState *lams) +static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->veiointc == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + +static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto veiointc = lvms->veiointc; + + visit_type_OnOffAuto(v, name, &veiointc, errp); +} + +static void virt_set_veiointc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &lvms->veiointc, errp); +} + +static PFlashCFI01 *virt_flash_create1(LoongArchVirtMachineState *lvms, + 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(lvms), name, OBJECT(dev)); + object_property_add_alias(OBJECT(lvms), alias_prop_name, + OBJECT(dev), "drive"); + return PFLASH_CFI01(dev); +} + +static void virt_flash_create(LoongArchVirtMachineState *lvms) +{ + lvms->flash[0] = virt_flash_create1(lvms, "virt.flash0", "pflash0"); + lvms->flash[1] = virt_flash_create1(lvms, "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(LoongArchVirtMachineState *lvms, + MemoryRegion *sysmem) +{ + PFlashCFI01 *flash0 = lvms->flash[0]; + PFlashCFI01 *flash1 = lvms->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_cpuic_node(LoongArchVirtMachineState *lvms, + uint32_t *cpuintc_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + + *cpuintc_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/cpuic"); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *cpuintc_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,cpu-interrupt-controller"); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + g_free(nodename); +} + +static void fdt_add_eiointc_node(LoongArchVirtMachineState *lvms, + uint32_t *cpuintc_phandle, + uint32_t *eiointc_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + hwaddr extioi_base = APIC_BASE; + hwaddr extioi_size = EXTIOI_SIZE; + + *eiointc_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/eiointc@%" PRIx64, extioi_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *eiointc_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,ls2k2000-eiointc"); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *cpuintc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", 3); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0x0, + extioi_base, 0x0, extioi_size); + g_free(nodename); +} + +static void fdt_add_pch_pic_node(LoongArchVirtMachineState *lvms, + uint32_t *eiointc_phandle, + uint32_t *pch_pic_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + hwaddr pch_pic_base = VIRT_PCH_REG_BASE; + hwaddr pch_pic_size = VIRT_PCH_REG_SIZE; + + *pch_pic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/platic@%" PRIx64, pch_pic_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_pic_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,pch-pic-1.0"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, + pch_pic_base, 0, pch_pic_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 2); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *eiointc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,pic-base-vec", 0); + g_free(nodename); +} + +static void fdt_add_pch_msi_node(LoongArchVirtMachineState *lvms, + uint32_t *eiointc_phandle, + uint32_t *pch_msi_phandle) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + hwaddr pch_msi_base = VIRT_PCH_MSI_ADDR_LOW; + hwaddr pch_msi_size = VIRT_PCH_MSI_SIZE; + + *pch_msi_phandle = qemu_fdt_alloc_phandle(ms->fdt); + nodename = g_strdup_printf("/msi@%" PRIx64, pch_msi_base); + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", *pch_msi_phandle); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "loongson,pch-msi-1.0"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", + 0, pch_msi_base, + 0, pch_msi_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *eiointc_phandle); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-base-vec", + VIRT_PCH_PIC_IRQ_NUM); + qemu_fdt_setprop_cell(ms->fdt, nodename, "loongson,msi-num-vecs", + EXTIOI_IRQS - VIRT_PCH_PIC_IRQ_NUM); + g_free(nodename); +} + +static void fdt_add_flash_node(LoongArchVirtMachineState *lvms) +{ + MachineState *ms = MACHINE(lvms); + char *nodename; + MemoryRegion *flash_mem; + + hwaddr flash0_base; + hwaddr flash0_size; + + hwaddr flash1_base; + hwaddr flash1_size; + + flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); + flash0_base = flash_mem->addr; + flash0_size = memory_region_size(flash_mem); + + flash_mem = pflash_cfi01_get_memory(lvms->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(LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle) { char *nodename; hwaddr base = VIRT_RTC_REG_BASE; hwaddr size = VIRT_RTC_LEN; - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); 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_string(ms->fdt, nodename, "compatible", + "loongson,ls7a-rtc"); qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", + VIRT_RTC_IRQ - VIRT_GSI_BASE , 0x4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *pch_pic_phandle); g_free(nodename); } -static void fdt_add_uart_node(LoongArchMachineState *lams) +static void fdt_add_ged_reset(LoongArchVirtMachineState *lvms) +{ + char *name; + uint32_t ged_handle; + MachineState *ms = MACHINE(lvms); + hwaddr base = VIRT_GED_REG_ADDR; + hwaddr size = ACPI_GED_REG_COUNT; + + ged_handle = qemu_fdt_alloc_phandle(ms->fdt); + name = g_strdup_printf("/ged@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, name); + qemu_fdt_setprop_string(ms->fdt, name, "compatible", "syscon"); + qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0x0, base, 0x0, size); + /* 8 bit registers */ + qemu_fdt_setprop_cell(ms->fdt, name, "reg-shift", 0); + qemu_fdt_setprop_cell(ms->fdt, name, "reg-io-width", 1); + qemu_fdt_setprop_cell(ms->fdt, name, "phandle", ged_handle); + ged_handle = qemu_fdt_get_phandle(ms->fdt, name); + g_free(name); + + 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", ged_handle); + qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_RESET); + qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_RESET_VALUE); + g_free(name); + + 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", ged_handle); + qemu_fdt_setprop_cell(ms->fdt, name, "offset", ACPI_GED_REG_SLEEP_CTL); + qemu_fdt_setprop_cell(ms->fdt, name, "value", ACPI_GED_SLP_EN | + (ACPI_GED_SLP_TYP_S5 << ACPI_GED_SLP_TYP_POS)); + g_free(name); +} + +static void fdt_add_uart_node(LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, hwaddr base, + int irq, bool chosen) { char *nodename; - hwaddr base = VIRT_UART_BASE; hwaddr size = VIRT_UART_SIZE; - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); 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); + if (chosen) + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupts", irq, 0x4); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + *pch_pic_phandle); g_free(nodename); } -static void create_fdt(LoongArchMachineState *lams) +static void create_fdt(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); + uint8_t rng_seed[32]; - ms->fdt = create_device_tree(&lams->fdt_size); + ms->fdt = create_device_tree(&lvms->fdt_size); if (!ms->fdt) { error_report("create_device_tree() failed"); exit(1); @@ -89,12 +356,16 @@ static void create_fdt(LoongArchMachineState *lams) 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"); + + /* 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)); } -static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) +static void fdt_add_cpu_nodes(const LoongArchVirtMachineState *lvms) { int num; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); int smp_cpus = ms->smp.cpus; qemu_fdt_add_subnode(ms->fdt, "/cpus"); @@ -105,11 +376,16 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) 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)); @@ -143,11 +419,11 @@ static void fdt_add_cpu_nodes(const LoongArchMachineState *lams) } } -static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) +static void fdt_add_fw_cfg_node(const LoongArchVirtMachineState *lvms) { char *nodename; hwaddr base = VIRT_FWCFG_BASE; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -159,7 +435,62 @@ static void fdt_add_fw_cfg_node(const LoongArchMachineState *lams) g_free(nodename); } -static void fdt_add_pcie_node(const LoongArchMachineState *lams) +static void fdt_add_pcie_irq_map_node(const LoongArchVirtMachineState *lvms, + char *nodename, + uint32_t *pch_pic_phandle) +{ + int pin, dev; + uint32_t irq_map_stride = 0; + uint32_t full_irq_map[GPEX_NUM_IRQS *GPEX_NUM_IRQS * 10] = {}; + uint32_t *irq_map = full_irq_map; + const MachineState *ms = MACHINE(lvms); + + /* 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 * 0x8; + + for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { + int irq_nr = 16 + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + int i = 0; + + /* Fill PCI address cells */ + irq_map[i] = cpu_to_be32(devfn << 8); + i += 3; + + /* Fill PCI Interrupt cells */ + irq_map[i] = cpu_to_be32(pin + 1); + i += 1; + + /* Fill interrupt controller phandle and cells */ + irq_map[i++] = cpu_to_be32(*pch_pic_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(ms->fdt, nodename, "interrupt-map", full_irq_map, + GPEX_NUM_IRQS * GPEX_NUM_IRQS * + irq_map_stride * sizeof(uint32_t)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + +static void fdt_add_pcie_node(const LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, + uint32_t *pch_msi_phandle) { char *nodename; hwaddr base_mmio = VIRT_PCI_MEM_BASE; @@ -170,7 +501,7 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams) hwaddr size_pcie = VIRT_PCI_CFG_SIZE; hwaddr base = base_pcie; - const MachineState *ms = MACHINE(lams); + const MachineState *ms = MACHINE(lvms); nodename = g_strdup_printf("/pcie@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -190,84 +521,115 @@ static void fdt_add_pcie_node(const LoongArchMachineState *lams) 2, base_pio, 2, size_pio, 1, FDT_PCI_RANGE_MMIO, 2, base_mmio, 2, base_mmio, 2, size_mmio); + qemu_fdt_setprop_cells(ms->fdt, nodename, "msi-map", + 0, *pch_msi_phandle, 0, 0x10000); + + fdt_add_pcie_irq_map_node(lvms, nodename, pch_pic_phandle); + g_free(nodename); } -static void fdt_add_irqchip_node(LoongArchMachineState *lams) +static void fdt_add_memory_node(MachineState *ms, + uint64_t base, uint64_t size, int node_id) { - MachineState *ms = MACHINE(lams); - char *nodename; - uint32_t irqchip_phandle; + char *nodename = g_strdup_printf("/memory@%" PRIx64, base); - 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_cells(ms->fdt, nodename, "reg", base >> 32, base, + size >> 32, size); + qemu_fdt_setprop_string(ms->fdt, nodename, "device_type", "memory"); - qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", - "loongarch,ls7a"); + if (ms->numa_state && ms->numa_state->num_nodes) { + qemu_fdt_setprop_cell(ms->fdt, nodename, "numa-node-id", node_id); + } - 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); } -#define PM_BASE 0x10080000 -#define PM_SIZE 0x100 -#define PM_CTRL 0x10 - -static void virt_build_smbios(LoongArchMachineState *lams) +static void fdt_add_memory_nodes(MachineState *ms) { - MachineState *ms = MACHINE(lams); - MachineClass *mc = MACHINE_GET_CLASS(lams); + hwaddr base, size, ram_size, gap; + int i, nb_numa_nodes, nodes; + NodeInfo *numa_info; + + ram_size = ms->ram_size; + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + nodes = nb_numa_nodes = ms->numa_state->num_nodes; + numa_info = ms->numa_state->nodes; + if (!nodes) { + nodes = 1; + } + + for (i = 0; i < nodes; i++) { + if (nb_numa_nodes) { + size = numa_info[i].node_mem; + } else { + size = ram_size; + } + + /* + * memory for the node splited into two part + * lowram: [base, +gap) + * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) + */ + if (size >= gap) { + fdt_add_memory_node(ms, base, gap, i); + size -= gap; + base = VIRT_HIGHMEM_BASE; + gap = ram_size - VIRT_LOWMEM_SIZE; + } + + if (size) { + fdt_add_memory_node(ms, base, size, i); + base += size; + gap -= size; + } + } +} + +static void virt_build_smbios(LoongArchVirtMachineState *lvms) +{ + MachineState *ms = MACHINE(lvms); + MachineClass *mc = MACHINE_GET_CLASS(lvms); uint8_t *smbios_tables, *smbios_anchor; size_t smbios_tables_len, smbios_anchor_len; const char *product = "QEMU Virtual Machine"; - if (!lams->fw_cfg) { + if (!lvms->fw_cfg) { return; } - smbios_set_defaults("QEMU", product, mc->name, false, - true, SMBIOS_ENTRY_POINT_TYPE_64); + smbios_set_defaults("QEMU", product, mc->name); - smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len, + 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", + fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-tables", smbios_tables, smbios_tables_len); - fw_cfg_add_file(lams->fw_cfg, "etc/smbios/smbios-anchor", + fw_cfg_add_file(lvms->fw_cfg, "etc/smbios/smbios-anchor", smbios_anchor, smbios_anchor_len); } } -static void virt_machine_done(Notifier *notifier, void *data) +static void virt_done(Notifier *notifier, void *data) { - LoongArchMachineState *lams = container_of(notifier, - LoongArchMachineState, machine_done); - virt_build_smbios(lams); - loongarch_acpi_setup(lams); + LoongArchVirtMachineState *lvms = container_of(notifier, + LoongArchVirtMachineState, machine_done); + virt_build_smbios(lvms); + loongarch_acpi_setup(lvms); } -struct memmap_entry { - uint64_t address; - uint64_t length; - uint32_t type; - uint32_t reserved; -}; +static void virt_powerdown_req(Notifier *notifier, void *opaque) +{ + LoongArchVirtMachineState *s; -static struct memmap_entry *memmap_table; -static unsigned memmap_entries; + s = container_of(notifier, LoongArchVirtMachineState, powerdown_notifier); + acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); +} static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) { @@ -285,80 +647,11 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) memmap_entries++; } -/* - * 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; - const char *kernel_cmdline; - const char *initrd_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 DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState *lams) +static DeviceState *create_acpi_ged(DeviceState *pch_pic, + LoongArchVirtMachineState *lvms) { DeviceState *dev; - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); uint32_t event = ACPI_GED_PWR_DOWN_EVT; if (ms->ram_slots) { @@ -366,6 +659,7 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState } 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); @@ -375,8 +669,7 @@ static DeviceState *create_acpi_ged(DeviceState *pch_pic, LoongArchMachineState 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 - PCH_PIC_IRQ_OFFSET)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + qdev_get_gpio_in(pch_pic, VIRT_SCI_IRQ - VIRT_GSI_BASE)); return dev; } @@ -395,7 +688,7 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) sysbus = SYS_BUS_DEVICE(dev); for (i = 0; i < VIRT_PLATFORM_BUS_NUM_IRQS; i++) { - irq = VIRT_PLATFORM_BUS_IRQ - PCH_PIC_IRQ_OFFSET + i; + irq = VIRT_PLATFORM_BUS_IRQ - VIRT_GSI_BASE + i; sysbus_connect_irq(sysbus, i, qdev_get_gpio_in(pch_pic, irq)); } @@ -405,20 +698,24 @@ static DeviceState *create_platform_bus(DeviceState *pch_pic) return dev; } -static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState *lams) +static void virt_devices_init(DeviceState *pch_pic, + LoongArchVirtMachineState *lvms, + uint32_t *pch_pic_phandle, + uint32_t *pch_msi_phandle) { + MachineClass *mc = MACHINE_GET_CLASS(lvms); DeviceState *gpex_dev; SysBusDevice *d; PCIBus *pci_bus; MemoryRegion *ecam_alias, *ecam_reg, *pio_alias, *pio_reg; - MemoryRegion *mmio_alias, *mmio_reg, *pm_mem; + 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; + lvms->pci_bus = pci_bus; /* Map only part size_ecam bytes of ECAM space */ ecam_alias = g_new0(MemoryRegion, 1); @@ -450,22 +747,24 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * 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 - PCH_PIC_IRQ_OFFSET), - 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - fdt_add_uart_node(lams); + /* Add pcie node */ + fdt_add_pcie_node(lvms, pch_pic_phandle, pch_msi_phandle); + + /* + * Create uart fdt node in reverse order so that they appear + * in the finished device tree lowest address first + */ + for (i = VIRT_UART_COUNT; i --> 0;) { + hwaddr base = VIRT_UART_BASE + i * VIRT_UART_SIZE; + int irq = VIRT_UART_IRQ + i - VIRT_GSI_BASE; + serial_mm_init(get_system_memory(), base, 0, + qdev_get_gpio_in(pch_pic, irq), + 115200, serial_hd(i), DEVICE_LITTLE_ENDIAN); + fdt_add_uart_node(lvms, pch_pic_phandle, base, irq, i == 0); + } /* 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); - } + pci_init_nic_devices(pci_bus, mc->default_nic); /* * There are some invalid guest memory access. @@ -474,76 +773,110 @@ static void loongarch_devices_init(DeviceState *pch_pic, LoongArchMachineState * 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 - PCH_PIC_IRQ_OFFSET)); - fdt_add_rtc_node(lams); + VIRT_RTC_IRQ - VIRT_GSI_BASE)); + fdt_add_rtc_node(lvms, pch_pic_phandle); + fdt_add_ged_reset(lvms); - 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); /* acpi ged */ - lams->acpi_ged = create_acpi_ged(pch_pic, lams); + lvms->acpi_ged = create_acpi_ged(pch_pic, lvms); /* platform bus */ - lams->platform_bus_dev = create_platform_bus(pch_pic); + lvms->platform_bus_dev = create_platform_bus(pch_pic); } -static void loongarch_irq_init(LoongArchMachineState *lams) +static void virt_irq_init(LoongArchVirtMachineState *lvms) { - MachineState *ms = MACHINE(lams); + MachineState *ms = MACHINE(lvms); 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); + int cpu, pin, i, start, num; + uint32_t cpuintc_phandle, eiointc_phandle, pch_pic_phandle, pch_msi_phandle; /* - * The connection of interrupts: - * +-----+ +---------+ +-------+ - * | IPI |--> | CPUINTC | <-- | Timer | - * +-----+ +---------+ +-------+ - * ^ - * | - * +---------+ - * | EIOINTC | - * +---------+ - * ^ ^ - * | | - * +---------+ +---------+ - * | PCH-PIC | | PCH-MSI | - * +---------+ +---------+ - * ^ ^ ^ - * | | | - * +--------+ +---------+ +---------+ - * | UARTs | | Devices | | Devices | - * +--------+ +---------+ +---------+ + * Extended IRQ model. + * | + * +-----------+ +-------------|--------+ +-----------+ + * | IPI/Timer | --> | CPUINTC(0-3)|(4-255) | <-- | IPI/Timer | + * +-----------+ +-------------|--------+ +-----------+ + * ^ | + * | + * +---------+ + * | EIOINTC | + * +---------+ + * ^ ^ + * | | + * +---------+ +---------+ + * | PCH-PIC | | PCH-MSI | + * +---------+ +---------+ + * ^ ^ ^ + * | | | + * +--------+ +---------+ +---------+ + * | UARTs | | Devices | | Devices | + * +--------+ +---------+ +---------+ + * + * Virt extended IRQ model. + * + * +-----+ +---------------+ +-------+ + * | IPI |--> | CPUINTC(0-255)| <-- | Timer | + * +-----+ +---------------+ +-------+ + * ^ + * | + * +-----------+ + * | V-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(&lvms->system_iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + + /* Add cpu interrupt-controller */ + fdt_add_cpuic_node(lvms, &cpuintc_phandle); + 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 = &lvms->as_iocsr; /* 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 * 2)); - memory_region_add_subregion(&env->system_iocsr, MAIL_SEND_ADDR, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), - cpu * 2 + 1)); - /* extioi iocsr memory region */ - memory_region_add_subregion(&env->system_iocsr, APIC_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), - cpu)); + env->ipistate = ipi; + } + + /* Create EXTIOI device */ + extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); + qdev_prop_set_uint32(extioi, "num-cpu", ms->smp.cpus); + if (virt_is_veiointc_enabled(lvms)) { + qdev_prop_set_bit(extioi, "has-virtualization-extension", true); + } + sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); + memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); + if (virt_is_veiointc_enabled(lvms)) { + memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); } /* @@ -558,7 +891,12 @@ static void loongarch_irq_init(LoongArchMachineState *lams) } } + /* Add Extend I/O Interrupt Controller node */ + fdt_add_eiointc_node(lvms, &cpuintc_phandle, &eiointc_phandle); + 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, @@ -570,32 +908,64 @@ static void loongarch_irq_init(LoongArchMachineState *lams) VIRT_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++) { + /* 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)); } + /* Add PCH PIC node */ + fdt_add_pch_pic_node(lvms, &eiointc_phandle, &pch_pic_phandle); + pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); - qdev_prop_set_uint32(pch_msi, "msi_irq_base", PCH_MSI_IRQ_START); + 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 < PCH_MSI_IRQ_NUM; i++) { - /* Connect 192 pch_msi irqs to extioi */ + 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 + PCH_MSI_IRQ_START)); + qdev_get_gpio_in(extioi, i + start)); } - loongarch_devices_init(pch_pic, lams); + /* Add PCH MSI node */ + fdt_add_pch_msi_node(lvms, &eiointc_phandle, &pch_msi_phandle); + + virt_devices_init(pch_pic, lvms, &pch_pic_phandle, &pch_msi_phandle); } -static void loongarch_firmware_init(LoongArchMachineState *lams) +static void virt_firmware_init(LoongArchVirtMachineState *lvms) { - char *filename = MACHINE(lams)->firmware; + char *filename = MACHINE(lvms)->firmware; char *bios_name = NULL; - int bios_size; + int bios_size, i; + BlockBackend *pflash_blk0; + MemoryRegion *mr; + + lvms->bios_loaded = false; + + /* Map legacy -drive if=pflash to machine properties */ + for (i = 0; i < ARRAY_SIZE(lvms->flash); i++) { + pflash_cfi01_legacy_drive(lvms->flash[i], + drive_get(IF_PFLASH, 0, i)); + } + + virt_flash_map(lvms, get_system_memory()); + + pflash_blk0 = pflash_cfi01_get_blk(lvms->flash[0]); + + if (pflash_blk0) { + if (filename) { + error_report("cannot use both '-bios' and '-drive if=pflash'" + "options at once"); + exit(1); + } + lvms->bios_loaded = true; + return; + } - lams->bios_loaded = false; if (filename) { bios_name = qemu_find_file(QEMU_FILE_TYPE_BIOS, filename); if (!bios_name) { @@ -603,128 +973,229 @@ static void loongarch_firmware_init(LoongArchMachineState *lams) exit(1); } - bios_size = load_image_targphys(bios_name, VIRT_BIOS_BASE, VIRT_BIOS_SIZE); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(lvms->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); - - memory_region_init_ram(&lams->bios, NULL, "loongarch.bios", - VIRT_BIOS_SIZE, &error_fatal); - memory_region_set_readonly(&lams->bios, true); - memory_region_add_subregion(get_system_memory(), VIRT_BIOS_BASE, &lams->bios); - 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); + lvms->bios_loaded = true; } } -static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg) +static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { - /* - * 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); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); + uint64_t features; - 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) -{ - fw_cfg_add_kernel_info(lams->fw_cfg); -} - -static void loongarch_direct_kernel_boot(LoongArchMachineState *lams) -{ - MachineState *machine = MACHINE(lams); - int64_t kernel_addr = 0; - LoongArchCPU *lacpu; - int i; - - 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; + switch (addr) { + case MISC_FUNC_REG: + if (!virt_is_veiointc_enabled(lvms)) { + return MEMTX_OK; } + + features = address_space_ldl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + attrs, NULL); + if (val & BIT_ULL(IOCSRM_EXTIOI_EN)) { + features |= BIT(EXTIOI_ENABLE); + } + if (val & BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE)) { + features |= BIT(EXTIOI_ENABLE_INT_ENCODE); + } + + address_space_stl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + features, attrs, NULL); + break; + default: + g_assert_not_reached(); } + + return MEMTX_OK; } -static void loongarch_init(MachineState *machine) +static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, + uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(opaque); + uint64_t ret = 0; + int features; + + switch (addr) { + case VERSION_REG: + ret = 0x11ULL; + break; + case FEATURE_REG: + ret = BIT(IOCSRF_MSI) | BIT(IOCSRF_EXTIOI) | BIT(IOCSRF_CSRIPI); + if (kvm_enabled()) { + ret |= BIT(IOCSRF_VM); + } + break; + case VENDOR_REG: + ret = 0x6e6f73676e6f6f4cULL; /* "Loongson" */ + break; + case CPUNAME_REG: + ret = 0x303030354133ULL; /* "3A5000" */ + break; + case MISC_FUNC_REG: + if (!virt_is_veiointc_enabled(lvms)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + break; + } + + features = address_space_ldl(&lvms->as_iocsr, + EXTIOI_VIRT_BASE + EXTIOI_VIRT_CONFIG, + attrs, NULL); + if (features & BIT(EXTIOI_ENABLE)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_EN); + } + if (features & BIT(EXTIOI_ENABLE_INT_ENCODE)) { + ret |= BIT_ULL(IOCSRM_EXTIOI_INT_ENCODE); + } + break; + default: + g_assert_not_reached(); + } + + *data = ret; + return MEMTX_OK; +} + +static const MemoryRegionOps virt_iocsr_misc_ops = { + .read_with_attrs = virt_iocsr_misc_read, + .write_with_attrs = virt_iocsr_misc_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 fw_cfg_add_memory(MachineState *ms) +{ + hwaddr base, size, ram_size, gap; + int nb_numa_nodes, nodes; + NodeInfo *numa_info; + + ram_size = ms->ram_size; + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + nodes = nb_numa_nodes = ms->numa_state->num_nodes; + numa_info = ms->numa_state->nodes; + if (!nodes) { + nodes = 1; + } + + /* add fw_cfg memory map of node0 */ + if (nb_numa_nodes) { + size = numa_info[0].node_mem; + } else { + size = ram_size; + } + + if (size >= gap) { + memmap_add_entry(base, gap, 1); + size -= gap; + base = VIRT_HIGHMEM_BASE; + } + + if (size) { + memmap_add_entry(base, size, 1); + base += size; + } + + if (nodes < 2) { + return; + } + + /* add fw_cfg memory map of other nodes */ + if (numa_info[0].node_mem < gap && ram_size > gap) { + /* + * memory map for the maining nodes splited into two part + * lowram: [base, +(gap - numa_info[0].node_mem)) + * highram: [VIRT_HIGHMEM_BASE, +(ram_size - gap)) + */ + memmap_add_entry(base, gap - numa_info[0].node_mem, 1); + size = ram_size - gap; + base = VIRT_HIGHMEM_BASE; + } else { + size = ram_size - numa_info[0].node_mem; + } + + if (size) + memmap_add_entry(base, size, 1); +} + +static void virt_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; MemoryRegion *address_space_mem = get_system_memory(); - LoongArchMachineState *lams = LOONGARCH_MACHINE(machine); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); int i; - hwaddr fdt_base; + hwaddr base, size, ram_size = machine->ram_size; + const CPUArchIdList *possible_cpus; + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUState *cpu; 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); + create_fdt(lvms); + + /* Create IOCSR space */ + memory_region_init_io(&lvms->system_iocsr, OBJECT(machine), NULL, + machine, "iocsr", UINT64_MAX); + address_space_init(&lvms->as_iocsr, &lvms->system_iocsr, "IOCSR"); + memory_region_init_io(&lvms->iocsr_mem, OBJECT(machine), + &virt_iocsr_misc_ops, + machine, "iocsr_misc", 0x428); + memory_region_add_subregion(&lvms->system_iocsr, 0, &lvms->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(lvms); + fdt_add_memory_nodes(machine); + fw_cfg_add_memory(machine); + + /* Node0 memory */ + size = ram_size; + base = VIRT_LOWMEM_BASE; + if (size > VIRT_LOWMEM_SIZE) { + size = VIRT_LOWMEM_SIZE; } - if (ram_size < 1 * GiB) { - error_report("ram_size must be greater than 1G."); - exit(1); + memory_region_init_alias(&lvms->lowmem, NULL, "loongarch.lowram", + machine->ram, base, size); + memory_region_add_subregion(address_space_mem, base, &lvms->lowmem); + base += size; + if (ram_size - size) { + base = VIRT_HIGHMEM_BASE; + memory_region_init_alias(&lvms->highmem, NULL, "loongarch.highram", + machine->ram, VIRT_LOWMEM_BASE + size, ram_size - size); + memory_region_add_subregion(address_space_mem, base, &lvms->highmem); + base += ram_size - size; } - create_fdt(lams); - /* Init CPUs */ - for (i = 0; i < machine->smp.cpus; i++) { - cpu_create(machine->cpu_type); - } - fdt_add_cpu_nodes(lams); - /* 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; - memmap_add_entry(0, 256 * MiB, 1); - 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); - memmap_add_entry(0x90000000, highram_size, 1); /* initialize device memory address space */ if (machine->ram_size < machine->maxram_size) { - machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { @@ -739,61 +1210,35 @@ static void loongarch_init(MachineState *machine) "%d bytes", TARGET_PAGE_SIZE); exit(EXIT_FAILURE); } - /* device memory base is the top of high memory address. */ - machine->device_memory->base = 0x90000000 + highram_size; - machine->device_memory->base = - ROUND_UP(machine->device_memory->base, 1 * GiB); - - memory_region_init(&machine->device_memory->mr, OBJECT(lams), - "device-memory", device_mem_size); - memory_region_add_subregion(address_space_mem, machine->device_memory->base, - &machine->device_memory->mr); + machine_memory_devices_init(machine, base, device_mem_size); } - /* Add isa io region */ - memory_region_init_alias(&lams->isa_io, NULL, "isa-io", - get_system_io(), 0, VIRT_ISA_IO_SIZE); - memory_region_add_subregion(address_space_mem, VIRT_ISA_IO_BASE, - &lams->isa_io); /* load the BIOS image. */ - loongarch_firmware_init(lams); + virt_firmware_init(lvms); /* 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", + lvms->fw_cfg = virt_fw_cfg_init(ram_size, machine); + rom_set_fw(lvms->fw_cfg); + if (lvms->fw_cfg != NULL) { + fw_cfg_add_file(lvms->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); - } else { - loongarch_direct_kernel_boot(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); - } + fdt_add_fw_cfg_node(lvms); + fdt_add_flash_node(lvms); + /* Initialize the IO interrupt subsystem */ - loongarch_irq_init(lams); - fdt_add_irqchip_node(lams); - platform_bus_add_all_fdt_nodes(machine->fdt, "/intc", + virt_irq_init(lvms); + platform_bus_add_all_fdt_nodes(machine->fdt, "/platic", 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); - fdt_add_pcie_node(lams); + lvms->machine_done.notify = virt_done; + qemu_add_machine_init_done_notifier(&lvms->machine_done); + /* connect powerdown request */ + lvms->powerdown_notifier.notify = virt_powerdown_req; + qemu_register_powerdown_notifier(&lvms->powerdown_notifier); + /* * 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 @@ -801,43 +1246,44 @@ static void loongarch_init(MachineState *machine) * 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); + qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); + rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, + &address_space_memory); + qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, + rom_ptr_for_as(&address_space_memory, FDT_BASE, lvms->fdt_size)); + + lvms->bootinfo.ram_size = ram_size; + loongarch_load_kernel(machine, &lvms->bootinfo); } -bool loongarch_is_acpi_enabled(LoongArchMachineState *lams) +static void virt_get_acpi(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { - 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; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + OnOffAuto acpi = lvms->acpi; visit_type_OnOffAuto(v, name, &acpi, errp); } -static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name, +static void virt_set_acpi(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); - visit_type_OnOffAuto(v, name, &lams->acpi, errp); + visit_type_OnOffAuto(v, name, &lvms->acpi, errp); } -static void loongarch_machine_initfn(Object *obj) +static void virt_initfn(Object *obj) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(obj); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_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); + if (tcg_enabled()) { + lvms->veiointc = ON_OFF_AUTO_OFF; + } + lvms->acpi = ON_OFF_AUTO_AUTO; + lvms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); + lvms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); + virt_flash_create(lvms); } static bool memhp_type_supported(DeviceState *dev) @@ -850,10 +1296,10 @@ static bool memhp_type_supported(DeviceState *dev) 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); + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), errp); } -static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, +static void virt_device_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { @@ -864,14 +1310,14 @@ static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev, static void virt_mem_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); /* the acpi ged is always exist */ - hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev, + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); } -static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, +static void virt_device_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { @@ -882,14 +1328,14 @@ static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev, static void virt_mem_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp); - pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams)); + hotplug_handler_unplug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); + pc_dimm_unplug(PC_DIMM(dev), MACHINE(lvms)); qdev_unrealize(dev); } -static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, +static void virt_device_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (memhp_type_supported(dev)) { @@ -900,81 +1346,145 @@ static void virt_machine_device_unplug(HotplugHandler *hotplug_dev, static void virt_mem_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - pc_dimm_plug(PC_DIMM(dev), MACHINE(lams)); - hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged), + pc_dimm_plug(PC_DIMM(dev), MACHINE(lvms)); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &error_abort); } -static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev, +static void virt_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev); - MachineClass *mc = MACHINE_GET_CLASS(lams); + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); + MachineClass *mc = MACHINE_GET_CLASS(lvms); + PlatformBusDevice *pbus; 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)); + if (lvms->platform_bus_dev) { + pbus = PLATFORM_BUS_DEVICE(lvms->platform_bus_dev); + platform_bus_link_device(pbus, 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) +static HotplugHandler *virt_get_hotplug_handler(MachineState *machine, + DeviceState *dev) { MachineClass *mc = MACHINE_GET_CLASS(machine); if (device_is_dynamic_sysbus(mc, dev) || + object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI) || memhp_type_supported(dev)) { return HOTPLUG_HANDLER(machine); } return NULL; } -static void loongarch_class_init(ObjectClass *oc, void *data) +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 socket_id; + + if (ms->numa_state->num_nodes) { + socket_id = ms->possible_cpus->cpus[idx].props.socket_id; + return socket_id % ms->numa_state->num_nodes; + } else { + return 0; + } +} + +static void virt_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->init = virt_init; mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("la464"); mc->default_ram_id = "loongarch.ram"; - mc->max_cpus = LOONGARCH_MAX_VCPUS; + mc->desc = "QEMU LoongArch Virtual Machine"; + 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->get_hotplug_handler = virt_machine_get_hotplug_handler; - 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; + 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_get_hotplug_handler; + mc->default_nic = "virtio-net-pci"; + hc->plug = virt_device_plug_cb; + hc->pre_plug = virt_device_pre_plug; + hc->unplug_request = virt_device_unplug_request; + hc->unplug = virt_device_unplug; object_class_property_add(oc, "acpi", "OnOffAuto", - loongarch_get_acpi, loongarch_set_acpi, + virt_get_acpi, virt_set_acpi, NULL, NULL); object_class_property_set_description(oc, "acpi", "Enable ACPI"); + object_class_property_add(oc, "v-eiointc", "OnOffAuto", + virt_get_veiointc, virt_set_veiointc, + NULL, NULL); + object_class_property_set_description(oc, "v-eiointc", + "Enable Virt Extend I/O Interrupt Controller."); 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[] = { +static const TypeInfo virt_machine_types[] = { { - .name = TYPE_LOONGARCH_MACHINE, + .name = TYPE_LOONGARCH_VIRT_MACHINE, .parent = TYPE_MACHINE, - .instance_size = sizeof(LoongArchMachineState), - .class_init = loongarch_class_init, - .instance_init = loongarch_machine_initfn, + .instance_size = sizeof(LoongArchVirtMachineState), + .class_init = virt_class_init, + .instance_init = virt_initfn, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } @@ -982,4 +1492,4 @@ static const TypeInfo loongarch_machine_types[] = { } }; -DEFINE_TYPES(loongarch_machine_types) +DEFINE_TYPES(virt_machine_types) diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig index f839f8a030..0092cda4e9 100644 --- a/hw/m68k/Kconfig +++ b/hw/m68k/Kconfig @@ -1,20 +1,28 @@ config AN5206 bool + default y + depends on M68K select COLDFIRE select PTIMER config MCF5208 bool + default y + depends on M68K select COLDFIRE select PTIMER config NEXTCUBE bool + default y + depends on M68K select FRAMEBUFFER select ESCC config Q800 bool + default y + depends on M68K select MAC_VIA select NUBUS select MACFB @@ -23,9 +31,14 @@ config Q800 select ESP select DP8393X select OR_IRQ + select DJMEMC + select IOSB + select ASC config M68K_VIRT bool + default y + depends on M68K select M68K_IRQC select VIRT_CTRL select GOLDFISH_PIC 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 a3d37e3c80..0b3e7c4ea0 100644 --- a/hw/m68k/bootinfo.h +++ b/hw/m68k/bootinfo.h @@ -1,5 +1,5 @@ /* - * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note + * SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note * * Bootinfo tags from linux bootinfo.h and bootinfo-mac.h: * This is an easily parsable and extendable structure containing all @@ -14,45 +14,44 @@ #define BOOTINFO0(base, id) \ do { \ - stw_p(base, id); \ + stw_be_p(base, id); \ base += 2; \ - stw_p(base, sizeof(struct bi_record)); \ + stw_be_p(base, sizeof(struct bi_record)); \ base += 2; \ } while (0) #define BOOTINFO1(base, id, value) \ do { \ - stw_p(base, id); \ + stw_be_p(base, id); \ base += 2; \ - stw_p(base, sizeof(struct bi_record) + 4); \ + stw_be_p(base, sizeof(struct bi_record) + 4); \ base += 2; \ - stl_p(base, value); \ + stl_be_p(base, value); \ base += 4; \ } while (0) #define BOOTINFO2(base, id, value1, value2) \ do { \ - stw_p(base, id); \ + stw_be_p(base, id); \ base += 2; \ - stw_p(base, sizeof(struct bi_record) + 8); \ + stw_be_p(base, sizeof(struct bi_record) + 8); \ base += 2; \ - stl_p(base, value1); \ + stl_be_p(base, value1); \ base += 4; \ - stl_p(base, value2); \ + stl_be_p(base, value2); \ base += 4; \ } while (0) #define BOOTINFOSTR(base, id, string) \ do { \ - int i; \ - stw_p(base, id); \ + stw_be_p(base, id); \ base += 2; \ - stw_p(base, \ + stw_be_p(base, \ (sizeof(struct bi_record) + strlen(string) + \ 1 /* null termination */ + 3 /* padding */) & ~3); \ base += 2; \ - for (i = 0; string[i]; i++) { \ - stb_p(base++, string[i]); \ + for (unsigned i_ = 0; string[i_]; i_++) { \ + stb_p(base++, string[i_]); \ } \ stb_p(base++, 0); \ base = QEMU_ALIGN_PTR_UP(base, 4); \ @@ -60,17 +59,16 @@ #define BOOTINFODATA(base, id, data, len) \ do { \ - int i; \ - stw_p(base, id); \ + stw_be_p(base, id); \ base += 2; \ - stw_p(base, \ + stw_be_p(base, \ (sizeof(struct bi_record) + len + \ 2 /* length field */ + 3 /* padding */) & ~3); \ base += 2; \ - stw_p(base, len); \ + stw_be_p(base, len); \ base += 2; \ - for (i = 0; i < len; ++i) { \ - stb_p(base++, data[i]); \ + for (unsigned i_ = 0; i_ < len; ++i_) { \ + stb_p(base++, data[i_]); \ } \ base = QEMU_ALIGN_PTR_UP(base, 4); \ } while (0) diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 2ab1b4f059..7247cdbe5e 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,21 +594,27 @@ 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; - dc->reset = m5206_mbar_reset; + device_class_set_legacy_reset(dc, m5206_mbar_reset); } static const TypeInfo mcf5206_mbar_info = { diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index be1033f84f..e37cd50d18 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -4,6 +4,14 @@ * Copyright (c) 2007 CodeSourcery. * * This code is licensed under the GPL + * + * This file models both the MCF5208 SoC, and the + * MCF5208EVB evaluation board. For details see + * + * "MCF5208 Reference Manual" + * https://www.nxp.com/docs/en/reference-manual/MCF5208RM.pdf + * "M5208EVB-RevB 32-bit Microcontroller User Manual" + * https://www.nxp.com/docs/en/reference-manual/M5208EVBUM.pdf */ #include "qemu/osdep.h" @@ -40,6 +48,8 @@ #define PCSR_PRE_SHIFT 8 #define PCSR_PRE_MASK 0x0f00 +#define RCR_SOFTRST 0x80 + typedef struct { MemoryRegion iomem; qemu_irq irq; @@ -156,7 +166,7 @@ static uint64_t m5208_sys_read(void *opaque, hwaddr addr, { int n; for (n = 0; n < 32; n++) { - if (current_machine->ram_size < (2u << n)) { + if (current_machine->ram_size < (2ULL << n)) { break; } } @@ -185,12 +195,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 +254,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 +309,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); @@ -318,7 +359,7 @@ static void mcf5208evb_init(MachineState *machine) /* Initial PC is always at offset 4 in firmware binaries */ ptr = rom_ptr(0x4, 4); assert(ptr != NULL); - env->pc = ldl_p(ptr); + env->pc = ldl_be_p(ptr); } /* Load kernel. */ diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 4cd30188c0..9fc30b03ba 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,14 +174,22 @@ 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; + device_class_set_legacy_reset(dc, mcf_intc_reset); } static const TypeInfo mcf_intc_gate_info = { @@ -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..08886d432c 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("FDCSR Write: %x\n", value); + DPRINTF("FD read @ %x\n", (unsigned int)addr); + val = 0x40 | 0x04 | 0x2 | 0x1; + break; - if (value == 0x0) { + 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: %"PRIx64 "\n", val); + 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: %"PRIx64 "\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%"PRIx64 " 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,76 +565,97 @@ 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: - DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)value); + DPRINTF("DMA write @ %x w/ %x\n", (unsigned)addr, (unsigned)val); } } -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) +{ + 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), @@ -927,7 +959,7 @@ static void next_pc_class_init(ObjectClass *klass, void *data) dc->desc = "NeXT Peripheral Controller"; dc->realize = next_pc_realize; - dc->reset = next_pc_reset; + device_class_set_legacy_reset(dc, next_pc_reset); device_class_set_props(dc, next_pc_properties); dc->vmsd = &next_pc_vmstate; } @@ -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); @@ -1009,7 +1036,7 @@ static void next_cube_init(MachineState *machine) /* Initial PC is always at offset 4 in firmware binaries */ ptr = rom_ptr(0x01000004, 4); g_assert(ptr != NULL); - env->pc = ldl_p(ptr); + env->pc = ldl_be_p(ptr); if (env->pc >= 0x01020000) { error_report("'%s' does not seem to be a valid firmware image.", bios_name); @@ -1023,10 +1050,12 @@ static void next_cube_init(MachineState *machine) /* TODO: */ /* Network */ /* SCSI */ + next_scsi_init(pcdev); /* 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..dacc26413f 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 @@ -68,7 +68,6 @@ struct NextKBDState { uint16_t shift; }; -static void queue_code(void *opaque, int code); /* lots of magic numbers here */ static uint32_t kbd_read_byte(void *opaque, hwaddr addr) @@ -166,68 +165,70 @@ static const MemoryRegionOps kbd_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void nextkbd_event(void *opaque, int ch) -{ - /* - * Will want to set vars for caps/num lock - * if (ch & 0x80) -> key release - * there's also e0 escaped scancodes that might need to be handled - */ - queue_code(opaque, ch); -} +static const int qcode_to_nextkbd_keycode[] = { + [Q_KEY_CODE_ESC] = 0x49, + [Q_KEY_CODE_1] = 0x4a, + [Q_KEY_CODE_2] = 0x4b, + [Q_KEY_CODE_3] = 0x4c, + [Q_KEY_CODE_4] = 0x4d, + [Q_KEY_CODE_5] = 0x50, + [Q_KEY_CODE_6] = 0x4f, + [Q_KEY_CODE_7] = 0x4e, + [Q_KEY_CODE_8] = 0x1e, + [Q_KEY_CODE_9] = 0x1f, + [Q_KEY_CODE_0] = 0x20, + [Q_KEY_CODE_MINUS] = 0x1d, + [Q_KEY_CODE_EQUAL] = 0x1c, + [Q_KEY_CODE_BACKSPACE] = 0x1b, -static const unsigned char next_keycodes[128] = { - 0x00, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x50, 0x4F, - 0x4E, 0x1E, 0x1F, 0x20, 0x1D, 0x1C, 0x1B, 0x00, - 0x42, 0x43, 0x44, 0x45, 0x48, 0x47, 0x46, 0x06, - 0x07, 0x08, 0x00, 0x00, 0x2A, 0x00, 0x39, 0x3A, - 0x3B, 0x3C, 0x3D, 0x40, 0x3F, 0x3E, 0x2D, 0x2C, - 0x2B, 0x26, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34, - 0x35, 0x37, 0x36, 0x2e, 0x2f, 0x30, 0x00, 0x00, - 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 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 + [Q_KEY_CODE_Q] = 0x42, + [Q_KEY_CODE_W] = 0x43, + [Q_KEY_CODE_E] = 0x44, + [Q_KEY_CODE_R] = 0x45, + [Q_KEY_CODE_T] = 0x48, + [Q_KEY_CODE_Y] = 0x47, + [Q_KEY_CODE_U] = 0x46, + [Q_KEY_CODE_I] = 0x06, + [Q_KEY_CODE_O] = 0x07, + [Q_KEY_CODE_P] = 0x08, + [Q_KEY_CODE_RET] = 0x2a, + [Q_KEY_CODE_A] = 0x39, + [Q_KEY_CODE_S] = 0x3a, + + [Q_KEY_CODE_D] = 0x3b, + [Q_KEY_CODE_F] = 0x3c, + [Q_KEY_CODE_G] = 0x3d, + [Q_KEY_CODE_H] = 0x40, + [Q_KEY_CODE_J] = 0x3f, + [Q_KEY_CODE_K] = 0x3e, + [Q_KEY_CODE_L] = 0x2d, + [Q_KEY_CODE_SEMICOLON] = 0x2c, + [Q_KEY_CODE_APOSTROPHE] = 0x2b, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x26, + [Q_KEY_CODE_Z] = 0x31, + [Q_KEY_CODE_X] = 0x32, + [Q_KEY_CODE_C] = 0x33, + [Q_KEY_CODE_V] = 0x34, + + [Q_KEY_CODE_B] = 0x35, + [Q_KEY_CODE_N] = 0x37, + [Q_KEY_CODE_M] = 0x36, + [Q_KEY_CODE_COMMA] = 0x2e, + [Q_KEY_CODE_DOT] = 0x2f, + [Q_KEY_CODE_SLASH] = 0x30, + + [Q_KEY_CODE_SPC] = 0x38, }; -static void queue_code(void *opaque, int code) +static void nextkbd_put_keycode(NextKBDState *s, int keycode) { - NextKBDState *s = NEXTKBD(opaque); KBDQueue *q = &s->queue; - int key = code & KD_KEYMASK; - int release = code & 0x80; - static int ext; - - if (code == 0xE0) { - ext = 1; - } - - if (code == 0x2A || code == 0x1D || code == 0x36) { - if (code == 0x2A) { - s->shift = KD_LSHIFT; - } else if (code == 0x36) { - s->shift = KD_RSHIFT; - ext = 0; - } else if (code == 0x1D && !ext) { - s->shift = KD_LCOMM; - } else if (code == 0x1D && ext) { - ext = 0; - s->shift = KD_RCOMM; - } - return; - } else if (code == (0x2A | 0x80) || code == (0x1D | 0x80) || - code == (0x36 | 0x80)) { - s->shift = 0; - return; - } if (q->count >= KBD_QUEUE_SIZE) { return; } - q->data[q->wptr] = next_keycodes[key] | release; - + q->data[q->wptr] = keycode; if (++q->wptr == KBD_QUEUE_SIZE) { q->wptr = 0; } @@ -241,6 +242,53 @@ static void queue_code(void *opaque, int code) /* s->update_irq(s->update_arg, 1); */ } +static void nextkbd_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) +{ + NextKBDState *s = NEXTKBD(dev); + int qcode, keycode; + bool key_down = evt->u.key.data->down; + + qcode = qemu_input_key_value_to_qcode(evt->u.key.data->key); + if (qcode >= ARRAY_SIZE(qcode_to_nextkbd_keycode)) { + return; + } + + /* Shift key currently has no keycode, so handle separately */ + if (qcode == Q_KEY_CODE_SHIFT) { + if (key_down) { + s->shift |= KD_LSHIFT; + } else { + s->shift &= ~KD_LSHIFT; + } + } + + if (qcode == Q_KEY_CODE_SHIFT_R) { + if (key_down) { + s->shift |= KD_RSHIFT; + } else { + s->shift &= ~KD_RSHIFT; + } + } + + keycode = qcode_to_nextkbd_keycode[qcode]; + if (!keycode) { + return; + } + + /* If key release event, create keyboard break code */ + if (!key_down) { + keycode |= 0x80; + } + + nextkbd_put_keycode(s, keycode); +} + +static const QemuInputHandler nextkbd_handler = { + .name = "QEMU NeXT Keyboard", + .mask = INPUT_EVENT_MASK_KEY, + .event = nextkbd_event, +}; + static void nextkbd_reset(DeviceState *dev) { NextKBDState *nks = NEXTKBD(dev); @@ -256,7 +304,7 @@ static void nextkbd_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->mr, OBJECT(dev), &kbd_ops, s, "next.kbd", 0x1000); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mr); - qemu_add_kbd_event_handler(nextkbd_event, s); + qemu_input_handler_register(dev, &nextkbd_handler); } static const VMStateDescription nextkbd_vmstate = { @@ -271,7 +319,7 @@ static void nextkbd_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); dc->vmsd = &nextkbd_vmstate; dc->realize = nextkbd_realize; - dc->reset = nextkbd_reset; + device_class_set_legacy_reset(dc, nextkbd_reset); } static const TypeInfo nextkbd_info = { diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c new file mode 100644 index 0000000000..e2ae7c3201 --- /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, ResetType type) +{ + 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 9d52ca6613..556604e1dc 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -28,7 +28,6 @@ #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" @@ -38,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" @@ -57,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 @@ -79,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 @@ -86,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) { @@ -359,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; @@ -369,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; @@ -384,15 +265,14 @@ 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); @@ -404,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: @@ -475,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); @@ -516,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; @@ -539,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)); } @@ -580,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); @@ -598,11 +569,11 @@ 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; @@ -641,11 +612,10 @@ static void q800_init(MachineState *machine) 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(param_ptr, BI_COMMAND_LINE, @@ -687,11 +657,15 @@ static void q800_init(MachineState *machine) } 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) { @@ -710,22 +684,44 @@ static void q800_init(MachineState *machine) ptr = rom_ptr(MACROM_ADDR, bios_size); assert(ptr != NULL); - stl_phys(cs->as, 0, ldl_p(ptr)); /* reset initial SP */ + stl_phys(cs->as, 0, ldl_be_p(ptr)); /* reset initial SP */ stl_phys(cs->as, 4, - MACROM_ADDR + ldl_p(ptr + 4)); /* reset initial PC */ + MACROM_ADDR + ldl_be_p(ptr + 4)); /* reset initial PC */ } } } +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", "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", "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" }, @@ -734,26 +730,38 @@ 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 da5eafd275..ea5c4a5a57 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 * @@ -23,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" @@ -154,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); /* @@ -198,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; @@ -239,9 +239,20 @@ static void virt_init(MachineState *machine) param_ptr = param_blob; BOOTINFO1(param_ptr, BI_MACHTYPE, MACH_VIRT); - BOOTINFO1(param_ptr, BI_FPUTYPE, FPU_68040); - BOOTINFO1(param_ptr, BI_MMUTYPE, MMU_68040); - BOOTINFO1(param_ptr, BI_CPUTYPE, CPU_68040); + 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(param_ptr, BI_VIRT_QEMU_VERSION, @@ -324,64 +335,115 @@ static void virt_machine_register_types(void) type_init(virt_machine_register_types) -#define DEFINE_VIRT_MACHINE(major, minor, latest) \ - static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - virt_machine_##major##_##minor##_options(mc); \ - mc->desc = "QEMU " # major "." # minor " M68K Virtual Machine"; \ + MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ + mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " M68K Virtual Machine"; \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ } \ } \ - static const TypeInfo machvirt_##major##_##minor##_info = { \ - .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ - .parent = MACHINE_TYPE_NAME("virt"), \ - .class_init = virt_##major##_##minor##_class_init, \ - }; \ - static void machvirt_machine_##major##_##minor##_init(void) \ + static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ { \ - type_register_static(&machvirt_##major##_##minor##_info); \ + .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ + .parent = MACHINE_TYPE_NAME("virt"), \ + .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ + { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ + type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ } \ - type_init(machvirt_machine_##major##_##minor##_init); + type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); + +#define DEFINE_VIRT_MACHINE_AS_LATEST(major, minor) \ + DEFINE_VIRT_MACHINE_IMPL(true, major, minor) +#define DEFINE_VIRT_MACHINE(major, minor) \ + DEFINE_VIRT_MACHINE_IMPL(false, major, minor) + +static void virt_machine_9_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(9, 2) + +static void virt_machine_9_1_options(MachineClass *mc) +{ + virt_machine_9_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); +} +DEFINE_VIRT_MACHINE(9, 1) + +static void virt_machine_9_0_options(MachineClass *mc) +{ + virt_machine_9_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); +} +DEFINE_VIRT_MACHINE(9, 0) + +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) + +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, true) +DEFINE_VIRT_MACHINE(7, 2) 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) +DEFINE_VIRT_MACHINE(7, 1) static void virt_machine_7_0_options(MachineClass *mc) { virt_machine_7_1_options(mc); compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_VIRT_MACHINE(7, 0, false) +DEFINE_VIRT_MACHINE(7, 0) static void virt_machine_6_2_options(MachineClass *mc) { virt_machine_7_0_options(mc); compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); } -DEFINE_VIRT_MACHINE(6, 2, false) +DEFINE_VIRT_MACHINE(6, 2) static void virt_machine_6_1_options(MachineClass *mc) { virt_machine_6_2_options(mc); compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); } -DEFINE_VIRT_MACHINE(6, 1, false) +DEFINE_VIRT_MACHINE(6, 1) static void virt_machine_6_0_options(MachineClass *mc) { virt_machine_6_1_options(mc); compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); } -DEFINE_VIRT_MACHINE(6, 0, false) +DEFINE_VIRT_MACHINE(6, 0) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 255590201a..5cf754b38f 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1,22 +1,37 @@ +/* + * 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" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" #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 +#define CXL_CAPACITY_MULTIPLIER (256 * MiB) /* Default CDAT entries for a memory region */ enum { @@ -29,36 +44,33 @@ enum { CT3_CDAT_NUM_ENTRIES }; -static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, - int dsmad_handle, MemoryRegion *mr) +static void ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, + int dsmad_handle, uint64_t size, + bool is_pmem, bool is_dynamic, + uint64_t dpa_base) { - g_autofree CDATDsmas *dsmas = NULL; - g_autofree CDATDslbis *dslbis0 = NULL; - g_autofree CDATDslbis *dslbis1 = NULL; - g_autofree CDATDslbis *dslbis2 = NULL; - g_autofree CDATDslbis *dslbis3 = NULL; - g_autofree CDATDsemts *dsemts = NULL; + CDATDsmas *dsmas; + CDATDslbis *dslbis0; + CDATDslbis *dslbis1; + CDATDslbis *dslbis2; + CDATDslbis *dslbis3; + CDATDsemts *dsemts; dsmas = g_malloc(sizeof(*dsmas)); - if (!dsmas) { - return -ENOMEM; - } *dsmas = (CDATDsmas) { .header = { .type = CDAT_TYPE_DSMAS, .length = sizeof(*dsmas), }, .DSMADhandle = dsmad_handle, - .flags = CDAT_DSMAS_FLAG_NV, - .DPA_base = 0, - .DPA_length = int128_get64(mr->size), + .flags = (is_pmem ? CDAT_DSMAS_FLAG_NV : 0) | + (is_dynamic ? CDAT_DSMAS_FLAG_DYNAMIC_CAP : 0), + .DPA_base = dpa_base, + .DPA_length = size, }; /* For now, no memory side cache, plausiblish numbers */ dslbis0 = g_malloc(sizeof(*dslbis0)); - if (!dslbis0) { - return -ENOMEM; - } *dslbis0 = (CDATDslbis) { .header = { .type = CDAT_TYPE_DSLBIS, @@ -72,9 +84,6 @@ static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, }; dslbis1 = g_malloc(sizeof(*dslbis1)); - if (!dslbis1) { - return -ENOMEM; - } *dslbis1 = (CDATDslbis) { .header = { .type = CDAT_TYPE_DSLBIS, @@ -88,9 +97,6 @@ static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, }; dslbis2 = g_malloc(sizeof(*dslbis2)); - if (!dslbis2) { - return -ENOMEM; - } *dslbis2 = (CDATDslbis) { .header = { .type = CDAT_TYPE_DSLBIS, @@ -104,9 +110,6 @@ static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, }; dslbis3 = g_malloc(sizeof(*dslbis3)); - if (!dslbis3) { - return -ENOMEM; - } *dslbis3 = (CDATDslbis) { .header = { .type = CDAT_TYPE_DSLBIS, @@ -120,62 +123,116 @@ static int ct3_build_cdat_entries_for_mr(CDATSubHeader **cdat_table, }; dsemts = g_malloc(sizeof(*dsemts)); - if (!dsemts) { - return -ENOMEM; - } *dsemts = (CDATDsemts) { .header = { .type = CDAT_TYPE_DSEMTS, .length = sizeof(*dsemts), }, .DSMAS_handle = dsmad_handle, - /* Reserved - the non volatile from DSMAS matters */ - .EFI_memory_type_attr = 2, + /* + * 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 = int128_get64(mr->size), + .DPA_length = size, }; /* Header always at start of structure */ - cdat_table[CT3_CDAT_DSMAS] = g_steal_pointer(&dsmas); - cdat_table[CT3_CDAT_DSLBIS0] = g_steal_pointer(&dslbis0); - cdat_table[CT3_CDAT_DSLBIS1] = g_steal_pointer(&dslbis1); - cdat_table[CT3_CDAT_DSLBIS2] = g_steal_pointer(&dslbis2); - cdat_table[CT3_CDAT_DSLBIS3] = g_steal_pointer(&dslbis3); - cdat_table[CT3_CDAT_DSEMTS] = g_steal_pointer(&dsemts); - - return 0; + 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; - MemoryRegion *nonvolatile_mr; CXLType3Dev *ct3d = priv; + MemoryRegion *volatile_mr = NULL, *nonvolatile_mr = NULL; + MemoryRegion *dc_mr = NULL; + uint64_t vmr_size = 0, pmr_size = 0; int dsmad_handle = 0; - int rc; + int cur_ent = 0; + int len = 0; - if (!ct3d->hostmem) { + if (!ct3d->hostpmem && !ct3d->hostvmem && !ct3d->dc.num_regions) { return 0; } - nonvolatile_mr = host_memory_backend_get_memory(ct3d->hostmem); - if (!nonvolatile_mr) { - return -EINVAL; + if (ct3d->hostvmem) { + volatile_mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (!volatile_mr) { + return -EINVAL; + } + len += CT3_CDAT_NUM_ENTRIES; + vmr_size = memory_region_size(volatile_mr); } - table = g_malloc0(CT3_CDAT_NUM_ENTRIES * sizeof(*table)); - if (!table) { - return -ENOMEM; + if (ct3d->hostpmem) { + nonvolatile_mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (!nonvolatile_mr) { + return -EINVAL; + } + len += CT3_CDAT_NUM_ENTRIES; + pmr_size = memory_region_size(nonvolatile_mr); } - rc = ct3_build_cdat_entries_for_mr(table, dsmad_handle++, nonvolatile_mr); - if (rc < 0) { - return rc; + if (ct3d->dc.num_regions) { + if (!ct3d->dc.host_dc) { + return -EINVAL; + } + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + if (!dc_mr) { + return -EINVAL; + } + len += CT3_CDAT_NUM_ENTRIES * ct3d->dc.num_regions; } + table = g_malloc0(len * sizeof(*table)); + + /* Now fill them in */ + if (volatile_mr) { + ct3_build_cdat_entries_for_mr(table, dsmad_handle++, vmr_size, + false, false, 0); + cur_ent = CT3_CDAT_NUM_ENTRIES; + } + + if (nonvolatile_mr) { + uint64_t base = vmr_size; + ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, + pmr_size, true, false, base); + cur_ent += CT3_CDAT_NUM_ENTRIES; + } + + if (dc_mr) { + int i; + uint64_t region_base = vmr_size + pmr_size; + + /* + * We assume the dynamic capacity to be volatile for now. + * Non-volatile dynamic capacity will be added if needed in the + * future. + */ + for (i = 0; i < ct3d->dc.num_regions; i++) { + ct3_build_cdat_entries_for_mr(&(table[cur_ent]), + dsmad_handle++, + ct3d->dc.regions[i].len, + false, true, region_base); + ct3d->dc.regions[i].dsmadhandle = dsmad_handle - 1; + + cur_ent += CT3_CDAT_NUM_ENTRIES; + region_base += ct3d->dc.regions[i].len; + } + } + + assert(len == cur_ent); + *cdat_table = g_steal_pointer(&table); - return CT3_CDAT_NUM_ENTRIES; + return len; } static void ct3_free_cdat_table(CDATSubHeader **cdat_table, int num, void *priv) @@ -250,6 +307,7 @@ static void ct3d_config_write(PCIDevice *pci_dev, uint32_t addr, uint32_t val, 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); } /* @@ -262,21 +320,54 @@ 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 if (ct3d->hostpmem) { + range1_size_hi = ct3d->hostpmem->size >> 32; + range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | + (ct3d->hostpmem->size & 0xF0000000); + } else { + /* + * For DCD with no static memory, set memory active, memory class bits. + * No range is set. + */ + range1_size_hi = 0; + range1_size_lo = (2 << 5) | (2 << 2) | 0x3; + } 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, @@ -295,20 +386,107 @@ static void build_dvsecs(CXLType3Dev *ct3d) cxl_component_create_dvsec(cxl_cstate, CXL2_TYPE3_DEVICE, 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, @@ -319,6 +497,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); @@ -327,8 +506,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; } @@ -336,42 +610,221 @@ 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); + } +} + +/* + * TODO: dc region configuration will be updated once host backend and address + * space support is added for DCD. + */ +static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) +{ + int i; + uint64_t region_base = 0; + uint64_t region_len; + uint64_t decode_len; + uint64_t blk_size = 2 * MiB; + CXLDCRegion *region; + MemoryRegion *mr; + uint64_t dc_size; + + mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(mr); + region_len = DIV_ROUND_UP(dc_size, ct3d->dc.num_regions); + + if (dc_size % (ct3d->dc.num_regions * CXL_CAPACITY_MULTIPLIER) != 0) { + error_setg(errp, + "backend size is not multiple of region len: 0x%" PRIx64, + region_len); + return false; + } + if (region_len % CXL_CAPACITY_MULTIPLIER != 0) { + error_setg(errp, "DC region size is unaligned to 0x%" PRIx64, + CXL_CAPACITY_MULTIPLIER); + return false; + } + decode_len = region_len; + + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + region_base += memory_region_size(mr); + } + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + region_base += memory_region_size(mr); + } + if (region_base % CXL_CAPACITY_MULTIPLIER != 0) { + error_setg(errp, "DC region base not aligned to 0x%" PRIx64, + CXL_CAPACITY_MULTIPLIER); + return false; + } + + for (i = 0, region = &ct3d->dc.regions[0]; + i < ct3d->dc.num_regions; + i++, region++, region_base += region_len) { + *region = (CXLDCRegion) { + .base = region_base, + .decode_len = decode_len, + .len = region_len, + .block_size = blk_size, + /* dsmad_handle set when creating CDAT table entries */ + .flags = 0, + }; + ct3d->dc.total_capacity += region->len; + region->blk_bitmap = bitmap_new(region->len / region->block_size); + } + QTAILQ_INIT(&ct3d->dc.extents); + QTAILQ_INIT(&ct3d->dc.extents_pending); + + return true; +} + +static void cxl_destroy_dc_regions(CXLType3Dev *ct3d) +{ + CXLDCExtent *ent, *ent_next; + CXLDCExtentGroup *group, *group_next; + int i; + CXLDCRegion *region; + + QTAILQ_FOREACH_SAFE(ent, &ct3d->dc.extents, node, ent_next) { + cxl_remove_extent_from_extent_list(&ct3d->dc.extents, ent); + } + + QTAILQ_FOREACH_SAFE(group, &ct3d->dc.extents_pending, node, group_next) { + QTAILQ_REMOVE(&ct3d->dc.extents_pending, group, node); + QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { + cxl_remove_extent_from_extent_list(&group->list, ent); + } + g_free(group); + } + + for (i = 0; i < ct3d->dc.num_regions; i++) { + region = &ct3d->dc.regions[i]; + g_free(region->blk_bitmap); } } 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 + && !ct3d->dc.num_regions) { + 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; + } + if (host_memory_backend_is_mapped(ct3d->hostvmem)) { + error_setg(errp, "memory backend %s can't be used multiple times.", + object_get_canonical_path_component(OBJECT(ct3d->hostvmem))); + 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.static_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"); + if (ct3d->hostpmem) { + MemoryRegion *pmr; + char *p_name; + + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + if (!pmr) { + error_setg(errp, "persistent memdev must have backing device"); + return false; + } + if (host_memory_backend_is_mapped(ct3d->hostpmem)) { + error_setg(errp, "memory backend %s can't be used multiple times.", + object_get_canonical_path_component(OBJECT(ct3d->hostpmem))); + 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.static_mem_size += memory_region_size(pmr); + g_free(p_name); } - address_space_init(&ct3d->hostmem_as, mr, name); - g_free(name); - ct3d->cxl_dstate.pmem_size = ct3d->hostmem->size; + ct3d->dc.total_capacity = 0; + if (ct3d->dc.num_regions > 0) { + MemoryRegion *dc_mr; + char *dc_name; - if (!ct3d->lsa) { - error_setg(errp, "lsa property must be set"); - return false; + if (!ct3d->dc.host_dc) { + error_setg(errp, "dynamic capacity must have a backing device"); + return false; + } + + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + if (!dc_mr) { + error_setg(errp, "dynamic capacity must have a backing device"); + return false; + } + + if (host_memory_backend_is_mapped(ct3d->dc.host_dc)) { + error_setg(errp, "memory backend %s can't be used multiple times.", + object_get_canonical_path_component(OBJECT(ct3d->dc.host_dc))); + return false; + } + /* + * Set DC regions as volatile for now, non-volatile support can + * be added in the future if needed. + */ + memory_region_set_nonvolatile(dc_mr, false); + memory_region_set_enabled(dc_mr, true); + host_memory_backend_set_mapped(ct3d->dc.host_dc, true); + if (ds->id) { + dc_name = g_strdup_printf("cxl-dcd-dpa-dc-space:%s", ds->id); + } else { + dc_name = g_strdup("cxl-dcd-dpa-dc-space"); + } + address_space_init(&ct3d->dc.host_dc_as, dc_mr, dc_name); + g_free(dc_name); + + if (!cxl_create_dc_regions(ct3d, errp)) { + error_append_hint(errp, "setup DC regions failed"); + return false; + } } return true; @@ -384,20 +837,23 @@ static DOEProtocol doe_cdat_prot[] = { 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 = 1; - int i; + unsigned short msix_num = 6; + int i, rc; + uint16_t count; + + 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); if (ct3d->sn != UI64_NULL) { @@ -420,25 +876,79 @@ 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) Initailization */ - msix_init_exclusive_bar(pci_dev, msix_num, 4, NULL); + /* 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 Initailization */ + /* 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 (!cxl_doe_cdat_init(cxl_cstate, 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); + + /* Set default value for patrol scrub attributes */ + ct3d->patrol_scrub_attrs.scrub_cycle_cap = + CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT | + CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT; + ct3d->patrol_scrub_attrs.scrub_cycle = + CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT | + (CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT << 8); + ct3d->patrol_scrub_attrs.scrub_flags = CXL_MEMDEV_PS_ENABLE_DEFAULT; + + /* Set default value for DDR5 ECS read attributes */ + ct3d->ecs_attrs.ecs_log_cap = CXL_ECS_LOG_ENTRY_TYPE_DEFAULT; + for (count = 0; count < CXL_ECS_NUM_MEDIA_FRUS; count++) { + ct3d->ecs_attrs.fru_attrs[count].ecs_cap = + CXL_ECS_REALTIME_REPORT_CAP_DEFAULT; + ct3d->ecs_attrs.fru_attrs[count].ecs_config = + CXL_ECS_THRESHOLD_COUNT_DEFAULT | + (CXL_ECS_MODE_DEFAULT << 3); + /* Reserved */ + ct3d->ecs_attrs.fru_attrs[count].ecs_flags = 0; + } + + 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->dc.host_dc) { + cxl_destroy_dc_regions(ct3d); + address_space_destroy(&ct3d->dc.host_dc_as); + } + 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) @@ -447,88 +957,241 @@ 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->dc.host_dc) { + cxl_destroy_dc_regions(ct3d); + address_space_destroy(&ct3d->dc.host_dc_as); + } + if (ct3d->hostpmem) { + address_space_destroy(&ct3d->hostpmem_as); + } + if (ct3d->hostvmem) { + address_space_destroy(&ct3d->hostvmem_as); + } +} + +/* + * Mark the DPA range [dpa, dap + len - 1] to be backed and accessible. This + * happens when a DC extent is added and accepted by the host. + */ +void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len) +{ + CXLDCRegion *region; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return; + } + + bitmap_set(region->blk_bitmap, (dpa - region->base) / region->block_size, + len / region->block_size); +} + +/* + * Check whether the DPA range [dpa, dpa + len - 1] is backed with DC extents. + * Used when validating read/write to dc regions + */ +bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len) +{ + CXLDCRegion *region; + uint64_t nbits; + long nr; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return false; + } + + nr = (dpa - region->base) / region->block_size; + nbits = DIV_ROUND_UP(len, region->block_size); + /* + * if bits between [dpa, dpa + len) are all 1s, meaning the DPA range is + * backed with DC extents, return true; else return false. + */ + return find_next_zero_bit(region->blk_bitmap, nr + nbits, nr) == nr + nbits; +} + +/* + * Mark the DPA range [dpa, dap + len - 1] to be unbacked and inaccessible. + * This happens when a dc extent is released by the host. + */ +void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len) +{ + CXLDCRegion *region; + uint64_t nbits; + long nr; + + region = cxl_find_dc_region(ct3d, dpa, len); + if (!region) { + return; + } + + nr = (dpa - region->base) / region->block_size; + nbits = len / region->block_size; + bitmap_clear(region->blk_bitmap, nr, nbits); } -/* 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, *dc_mr = NULL; + uint64_t vmr_size = 0, pmr_size = 0, dc_size = 0; + + if (ct3d->hostvmem) { + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + vmr_size = memory_region_size(vmr); + } + if (ct3d->hostpmem) { + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + pmr_size = memory_region_size(pmr); + } + if (ct3d->dc.host_dc) { + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(dc_mr); } - 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 && !dc_mr) { + 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 >= vmr_size + pmr_size + dc_size) { + return -EINVAL; + } - return true; + if (*dpa_offset < vmr_size) { + *as = &ct3d->hostvmem_as; + } else if (*dpa_offset < vmr_size + pmr_size) { + *as = &ct3d->hostpmem_as; + *dpa_offset -= vmr_size; + } else { + if (!ct3_test_region_block_backed(ct3d, *dpa_offset, size)) { + return -ENODEV; + } + + *as = &ct3d->dc.host_dc_as; + *dpa_offset -= (vmr_size + pmr_size); + } + + 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 (cxl_dev_media_disabled(&ct3d->cxl_dstate)) { + 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 (cxl_dev_media_disabled(&ct3d->cxl_dstate)) { 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) @@ -537,17 +1200,40 @@ static void ct3d_reset(DeviceState *dev) uint32_t *reg_state = ct3d->cxl_cstate.crb.cache_mem_registers; uint32_t *write_msk = ct3d->cxl_cstate.crb.cache_mem_regs_write_mask; + pcie_cap_fill_link_ep_usp(PCI_DEVICE(dev), ct3d->width, ct3d->speed); 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_UINT8("num-dc-regions", CXLType3Dev, dc.num_regions, 0), + DEFINE_PROP_LINK("volatile-dc-memdev", CXLType3Dev, dc.host_dc, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_PCIE_LINK_SPEED("x-speed", CXLType3Dev, + speed, PCIE_LINK_SPEED_32), + DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLType3Dev, + width, PCIE_LINK_WIDTH_16), DEFINE_PROP_END_OF_LIST(), }; @@ -555,6 +1241,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); } @@ -572,6 +1262,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); @@ -587,6 +1281,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); @@ -600,6 +1298,837 @@ 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, *dc_mr = NULL; + AddressSpace *as; + uint64_t vmr_size = 0, pmr_size = 0, dc_size = 0; + + if (ct3d->hostvmem) { + vmr = host_memory_backend_get_memory(ct3d->hostvmem); + vmr_size = memory_region_size(vmr); + } + if (ct3d->hostpmem) { + pmr = host_memory_backend_get_memory(ct3d->hostpmem); + pmr_size = memory_region_size(pmr); + } + if (ct3d->dc.host_dc) { + dc_mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + dc_size = memory_region_size(dc_mr); + } + + if (!vmr && !pmr && !dc_mr) { + return false; + } + + if (dpa_offset + CXL_CACHE_LINE_SIZE > vmr_size + pmr_size + dc_size) { + return false; + } + + if (dpa_offset < vmr_size) { + as = &ct3d->hostvmem_as; + } else if (dpa_offset < vmr_size + pmr_size) { + as = &ct3d->hostpmem_as; + dpa_offset -= vmr_size; + } else { + as = &ct3d->dc.host_dc_as; + dpa_offset -= (vmr_size + pmr_size); + } + + 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 cxl_clear_poison_list_overflowed(CXLType3Dev *ct3d) +{ + ct3d->poison_list_overflowed = false; + ct3d->poison_list_overflow_ts = 0; +} + +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 + p->length) && (start + length > p->start)) { + error_setg(errp, + "Overlap with existing poisoned region not supported"); + return; + } + } + + p = g_new0(CXLPoison, 1); + p->length = length; + p->start = start; + /* Different from injected via the mbox */ + p->type = CXL_POISON_TYPE_INTERNAL; + + if (ct3d->poison_list_cnt < CXL_POISON_LIST_LIMIT) { + QLIST_INSERT_HEAD(&ct3d->poison_list, p, node); + ct3d->poison_list_cnt++; + } else { + if (!ct3d->poison_list_overflowed) { + cxl_set_poison_list_overflowed(ct3d); + } + QLIST_INSERT_HEAD(&ct3d->poison_list_bkp, p, node); + } +} + +/* 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; + 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); + } +} + +/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */ +static const QemuUUID dynamic_capacity_uuid = { + .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f, + 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a), +}; + +typedef enum CXLDCEventType { + DC_EVENT_ADD_CAPACITY = 0x0, + DC_EVENT_RELEASE_CAPACITY = 0x1, + DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2, + DC_EVENT_REGION_CONFIG_UPDATED = 0x3, + DC_EVENT_ADD_CAPACITY_RSP = 0x4, + DC_EVENT_CAPACITY_RELEASED = 0x5, +} CXLDCEventType; + +/* + * Check whether the range [dpa, dpa + len - 1] has overlaps with extents in + * the list. + * Return value: return true if has overlaps; otherwise, return false + */ +static bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len) +{ + CXLDCExtent *ent; + Range range1, range2; + + if (!list) { + return false; + } + + range_init_nofail(&range1, dpa, len); + QTAILQ_FOREACH(ent, list, node) { + range_init_nofail(&range2, ent->start_dpa, ent->len); + if (range_overlaps_range(&range1, &range2)) { + return true; + } + } + return false; +} + +/* + * Check whether the range [dpa, dpa + len - 1] is contained by extents in + * the list. + * Will check multiple extents containment once superset release is added. + * Return value: return true if range is contained; otherwise, return false + */ +bool cxl_extents_contains_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len) +{ + CXLDCExtent *ent; + Range range1, range2; + + if (!list) { + return false; + } + + range_init_nofail(&range1, dpa, len); + QTAILQ_FOREACH(ent, list, node) { + range_init_nofail(&range2, ent->start_dpa, ent->len); + if (range_contains_range(&range2, &range1)) { + return true; + } + } + return false; +} + +static bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, + uint64_t dpa, uint64_t len) +{ + CXLDCExtentGroup *group; + + if (!list) { + return false; + } + + QTAILQ_FOREACH(group, list, node) { + if (cxl_extents_overlaps_dpa_range(&group->list, dpa, len)) { + return true; + } + } + return false; +} + +/* + * The main function to process dynamic capacity event with extent list. + * Currently DC extents add/release requests are processed. + */ +static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, + uint16_t hid, CXLDCEventType type, uint8_t rid, + CxlDynamicCapacityExtentList *records, Error **errp) +{ + Object *obj; + CXLEventDynamicCapacity dCap = {}; + CXLEventRecordHdr *hdr = &dCap.hdr; + CXLType3Dev *dcd; + uint8_t flags = 1 << CXL_EVENT_TYPE_INFO; + uint32_t num_extents = 0; + CxlDynamicCapacityExtentList *list; + CXLDCExtentGroup *group = NULL; + g_autofree CXLDCExtentRaw *extents = NULL; + uint8_t enc_log = CXL_EVENT_TYPE_DYNAMIC_CAP; + uint64_t dpa, offset, len, block_size; + g_autofree unsigned long *blk_bitmap = NULL; + int i; + + obj = object_resolve_path_type(path, TYPE_CXL_TYPE3, NULL); + if (!obj) { + error_setg(errp, "Unable to resolve CXL type 3 device"); + return; + } + + dcd = CXL_TYPE3(obj); + if (!dcd->dc.num_regions) { + error_setg(errp, "No dynamic capacity support from the device"); + return; + } + + + if (rid >= dcd->dc.num_regions) { + error_setg(errp, "region id is too large"); + return; + } + block_size = dcd->dc.regions[rid].block_size; + blk_bitmap = bitmap_new(dcd->dc.regions[rid].len / block_size); + + /* Sanity check and count the extents */ + list = records; + while (list) { + offset = list->value->offset; + len = list->value->len; + dpa = offset + dcd->dc.regions[rid].base; + + if (len == 0) { + error_setg(errp, "extent with 0 length is not allowed"); + return; + } + + if (offset % block_size || len % block_size) { + error_setg(errp, "dpa or len is not aligned to region block size"); + return; + } + + if (offset + len > dcd->dc.regions[rid].len) { + error_setg(errp, "extent range is beyond the region end"); + return; + } + + /* No duplicate or overlapped extents are allowed */ + if (test_any_bits_set(blk_bitmap, offset / block_size, + len / block_size)) { + error_setg(errp, "duplicate or overlapped extents are detected"); + return; + } + bitmap_set(blk_bitmap, offset / block_size, len / block_size); + + if (type == DC_EVENT_RELEASE_CAPACITY) { + if (cxl_extent_groups_overlaps_dpa_range(&dcd->dc.extents_pending, + dpa, len)) { + error_setg(errp, + "cannot release extent with pending DPA range"); + return; + } + if (!ct3_test_region_block_backed(dcd, dpa, len)) { + error_setg(errp, + "cannot release extent with non-existing DPA range"); + return; + } + } else if (type == DC_EVENT_ADD_CAPACITY) { + if (cxl_extents_overlaps_dpa_range(&dcd->dc.extents, dpa, len)) { + error_setg(errp, + "cannot add DPA already accessible to the same LD"); + return; + } + if (cxl_extent_groups_overlaps_dpa_range(&dcd->dc.extents_pending, + dpa, len)) { + error_setg(errp, + "cannot add DPA again while still pending"); + return; + } + } + list = list->next; + num_extents++; + } + + /* Create extent list for event being passed to host */ + i = 0; + list = records; + extents = g_new0(CXLDCExtentRaw, num_extents); + while (list) { + offset = list->value->offset; + len = list->value->len; + dpa = dcd->dc.regions[rid].base + offset; + + extents[i].start_dpa = dpa; + extents[i].len = len; + memset(extents[i].tag, 0, 0x10); + extents[i].shared_seq = 0; + if (type == DC_EVENT_ADD_CAPACITY) { + group = cxl_insert_extent_to_extent_group(group, + extents[i].start_dpa, + extents[i].len, + extents[i].tag, + extents[i].shared_seq); + } + + list = list->next; + i++; + } + if (group) { + cxl_extent_group_list_insert_tail(&dcd->dc.extents_pending, group); + } + + /* + * CXL r3.1 section 8.2.9.2.1.6: Dynamic Capacity Event Record + * + * All Dynamic Capacity event records shall set the Event Record Severity + * field in the Common Event Record Format to Informational Event. All + * Dynamic Capacity related events shall be logged in the Dynamic Capacity + * Event Log. + */ + cxl_assign_event_header(hdr, &dynamic_capacity_uuid, flags, sizeof(dCap), + cxl_device_get_timestamp(&dcd->cxl_dstate)); + + dCap.type = type; + /* FIXME: for now, validity flag is cleared */ + dCap.validity_flags = 0; + stw_le_p(&dCap.host_id, hid); + /* only valid for DC_REGION_CONFIG_UPDATED event */ + dCap.updated_region_id = 0; + for (i = 0; i < num_extents; i++) { + memcpy(&dCap.dynamic_capacity_extent, &extents[i], + sizeof(CXLDCExtentRaw)); + + dCap.flags = 0; + if (i < num_extents - 1) { + /* Set "More" flag */ + dCap.flags |= BIT(0); + } + + if (cxl_event_insert(&dcd->cxl_dstate, enc_log, + (CXLEventRecordRaw *)&dCap)) { + cxl_event_irq_assert(dcd); + } + } +} + +void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, + CxlExtentSelectionPolicy sel_policy, + uint8_t region, const char *tag, + CxlDynamicCapacityExtentList *extents, + Error **errp) +{ + switch (sel_policy) { + case CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE: + qmp_cxl_process_dynamic_capacity_prescriptive(path, host_id, + DC_EVENT_ADD_CAPACITY, + region, extents, errp); + return; + default: + error_setg(errp, "Selection policy not supported"); + return; + } +} + +void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, + CxlExtentRemovalPolicy removal_policy, + bool has_forced_removal, + bool forced_removal, + bool has_sanitize_on_release, + bool sanitize_on_release, + uint8_t region, + const char *tag, + CxlDynamicCapacityExtentList *extents, + Error **errp) +{ + CXLDCEventType type = DC_EVENT_RELEASE_CAPACITY; + + if (has_forced_removal && forced_removal) { + /* TODO: enable forced removal in the future */ + type = DC_EVENT_FORCED_RELEASE_CAPACITY; + error_setg(errp, "Forced removal not supported yet"); + return; + } + + switch (removal_policy) { + case CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE: + qmp_cxl_process_dynamic_capacity_prescriptive(path, host_id, type, + region, extents, errp); + return; + default: + error_setg(errp, "Removal policy not supported"); + return; + } +} + static void ct3_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -608,7 +2137,7 @@ 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; @@ -617,13 +2146,14 @@ static void ct3_class_init(ObjectClass *oc, void *data) pc->config_read = ct3d_config_read; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "CXL PMEM Device (Type 3)"; - dc->reset = ct3d_reset; + dc->desc = "CXL Memory Device (Type 3)"; + device_class_set_legacy_reset(dc, 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..c1a5e4a7c1 --- /dev/null +++ b/hw/mem/cxl_type3_stubs.c @@ -0,0 +1,94 @@ +/* + * 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"); +} + +void qmp_cxl_add_dynamic_capacity(const char *path, + uint16_t host_id, + CxlExtentSelectionPolicy sel_policy, + uint8_t region, + const char *tag, + CxlDynamicCapacityExtentList *extents, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} + +void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, + CxlExtentRemovalPolicy removal_policy, + bool has_forced_removal, + bool forced_removal, + bool has_sanitize_on_release, + bool sanitize_on_release, + uint8_t region, + const char *tag, + CxlDynamicCapacityExtentList *extents, + Error **errp) +{ + error_setg(errp, "CXL Type 3 support is not compiled in"); +} diff --git a/hw/mem/memory-device-stubs.c b/hw/mem/memory-device-stubs.c new file mode 100644 index 0000000000..15fd93ff67 --- /dev/null +++ b/hw/mem/memory-device-stubs.c @@ -0,0 +1,22 @@ +#include "qemu/osdep.h" +#include "hw/mem/memory-device.h" + +MemoryDeviceInfoList *qmp_memory_device_list(void) +{ + return NULL; +} + +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/hw/mem/memory-device.c b/hw/mem/memory-device.c index d9f8301711..a5f279adcc 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); } } @@ -247,26 +345,53 @@ uint64_t get_plugged_memory_size(void) } void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, - const uint64_t *legacy_align, Error **errp) + Error **errp) { const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); Error *local_err = NULL; 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; } - if (legacy_align) { - align = *legacy_align; - } else { - if (mdc->get_min_alignment) { - align = mdc->get_min_alignment(md); - } - align = MAX(align, memory_region_get_alignment(mr)); + 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 (mdc->get_min_alignment) { + align = mdc->get_min_alignment(md); + } + align = MAX(align, memory_region_get_alignment(mr)); addr = mdc->get_addr(md); addr = memory_device_get_free_addr(ms, !addr ? NULL : &addr, align, memory_region_size(mr), &local_err); @@ -285,9 +410,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 +428,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 +442,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 +457,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 +482,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..1c1c6da24b 100644 --- a/hw/mem/meson.build +++ b/hw/mem/meson.build @@ -4,7 +4,9 @@ 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(when: 'CONFIG_MEM_DEVICE', if_false: files('memory-device-stubs.c')) +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 31080c22c9..1631a7d13f 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -154,6 +154,9 @@ static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp) 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..27919ca45d 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -44,8 +44,7 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) return host_memory_backend_get_memory(dimm->hostmem); } -void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, - const uint64_t *legacy_align, Error **errp) +void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp) { Error *local_err = NULL; int slot; @@ -70,8 +69,7 @@ void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, &error_abort); trace_mhp_pc_dimm_assigned_slot(slot); - memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, legacy_align, - errp); + memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, errp); } void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine) @@ -81,6 +79,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 +92,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 +257,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 43ed36fa15..d2d646de23 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -27,8 +27,6 @@ subdir('nvram') subdir('pci') subdir('pci-bridge') subdir('pci-host') -subdir('pcmcia') -subdir('rdma') subdir('rtc') subdir('scsi') subdir('sd') @@ -37,6 +35,7 @@ subdir('smbios') subdir('ssi') subdir('timer') subdir('tpm') +subdir('ufs') subdir('usb') subdir('vfio') subdir('virtio') @@ -44,18 +43,17 @@ subdir('watchdog') subdir('xbox') subdir('xen') subdir('xenpv') +subdir('fsi') subdir('alpha') subdir('arm') subdir('avr') -subdir('cris') subdir('hppa') subdir('i386') subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') -subdir('nios2') subdir('openrisc') subdir('ppc') subdir('remote') diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig index e2697ced9c..b0214b2c8b 100644 --- a/hw/microblaze/Kconfig +++ b/hw/microblaze/Kconfig @@ -1,5 +1,7 @@ config PETALOGIX_S3ADSP1800 bool + default y + depends on MICROBLAZE select PFLASH_CFI01 select XILINX select XILINX_AXI @@ -8,8 +10,10 @@ config PETALOGIX_S3ADSP1800 config PETALOGIX_ML605 bool + default y + depends on MICROBLAZE select PFLASH_CFI01 - select SERIAL + select SERIAL_MM select SSI_M25P80 select XILINX select XILINX_AXI @@ -18,4 +22,6 @@ config PETALOGIX_ML605 config XLNX_ZYNQMP_PMU bool + default y + depends on MICROBLAZE select XLNX_ZYNQMP diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 25ad54754e..ed61e483ee 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -140,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..61e47d8398 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -34,7 +34,7 @@ #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/qdev-properties.h" #include "exec/address-spaces.h" #include "hw/ssi/ssi.h" @@ -90,7 +90,7 @@ petalogix_ml605_init(MachineState *machine) object_property_set_int(OBJECT(cpu), "use-fpu", 1, &error_abort); object_property_set_bool(OBJECT(cpu), "dcache-writeback", true, &error_abort); - object_property_set_bool(OBJECT(cpu), "endianness", true, &error_abort); + object_property_set_bool(OBJECT(cpu), "little-endian", true, &error_abort); qdev_realize(DEVICE(cpu), NULL, &error_abort); /* Attach emulated BRAM through the LMB. */ @@ -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); @@ -213,7 +213,12 @@ petalogix_ml605_init(MachineState *machine) static void petalogix_ml605_machine_init(MachineClass *mc) { - mc->desc = "PetaLogix linux refdesign for xilinx ml605 little endian"; +#if TARGET_BIG_ENDIAN + mc->desc = "PetaLogix linux refdesign for xilinx ml605 (big endian)"; + mc->deprecation_reason = "big endian support is not tested"; +#else + mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; +#endif mc->init = petalogix_ml605_init; } diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 9d959d1ad8..6c0f5c6c65 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -55,6 +55,9 @@ #define ETHLITE_IRQ 1 #define UARTLITE_IRQ 3 +#define TYPE_PETALOGIX_S3ADSP1800_MACHINE \ + MACHINE_TYPE_NAME("petalogix-s3adsp1800") + static void petalogix_s3adsp1800_init(MachineState *machine) { @@ -71,6 +74,8 @@ petalogix_s3adsp1800_init(MachineState *machine) cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); object_property_set_str(OBJECT(cpu), "version", "7.10.d", &error_abort); + object_property_set_bool(OBJECT(cpu), "little-endian", + !TARGET_BIG_ENDIAN, &error_abort); qdev_realize(DEVICE(cpu), NULL, &error_abort); /* Attach emulated BRAM through the LMB. */ @@ -100,8 +105,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,16 +119,15 @@ 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); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]); - create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000); + create_unimplemented_device("xps_gpio", GPIO_BASEADDR, 0x10000); microblaze_load_kernel(cpu, ddr_base, ram_size, machine->initrd_filename, @@ -128,11 +135,21 @@ petalogix_s3adsp1800_init(MachineState *machine) NULL); } -static void petalogix_s3adsp1800_machine_init(MachineClass *mc) +static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; mc->is_default = true; } -DEFINE_MACHINE("petalogix-s3adsp1800", petalogix_s3adsp1800_machine_init) +static const TypeInfo petalogix_s3adsp1800_machine_types[] = { + { + .name = TYPE_PETALOGIX_S3ADSP1800_MACHINE, + .parent = TYPE_MACHINE, + .class_init = petalogix_s3adsp1800_machine_class_init, + }, +}; + +DEFINE_TYPES(petalogix_s3adsp1800_machine_types) diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 5a2016672a..567aad47bf 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -90,7 +90,7 @@ static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) object_property_set_bool(OBJECT(&s->cpu), "use-pcmp-instr", true, &error_abort); object_property_set_bool(OBJECT(&s->cpu), "use-mmu", false, &error_abort); - object_property_set_bool(OBJECT(&s->cpu), "endianness", true, + object_property_set_bool(OBJECT(&s->cpu), "little-endian", true, &error_abort); object_property_set_str(OBJECT(&s->cpu), "version", "8.40.b", &error_abort); @@ -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; } @@ -179,9 +181,13 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine) static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) { - mc->desc = "Xilinx ZynqMP PMU machine"; +#if TARGET_BIG_ENDIAN + mc->desc = "Xilinx ZynqMP PMU machine (big endian)"; + mc->deprecation_reason = "big endian support is not tested"; +#else + mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; +#endif mc->init = xlnx_zynqmp_pmu_init; } DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) - diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index 725525358d..b09c89a017 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -1,15 +1,29 @@ config MALTA bool - select ISA_SUPERIO + default y + depends on MIPS + imply PCNET_PCI + imply PCI_DEVICES + imply TEST_DEVICES + select FDC37M81X + select GT64120 + select MIPS_CPS + select PIIX + select PFLASH_CFI01 + select SERIAL_MM + select SMBUS_EEPROM config MIPSSIM bool - select ISA_BUS - select SERIAL_ISA + default y + depends on MIPS + select SERIAL_MM select MIPSNET config JAZZ bool + default y + depends on MIPS64 select ISA_BUS select RC4030 select I8259 @@ -23,39 +37,59 @@ config JAZZ select FDC_SYSBUS select MC146818RTC select PCKBD - select SERIAL + select SERIAL_MM select PARALLEL select DS1225Y select JAZZ_LED config FULOONG bool + default y + depends on MIPS64 && !TARGET_BIG_ENDIAN + imply PCI_DEVICES + imply TEST_DEVICES + imply ATI_VGA + imply RTL8139_PCI select PCI_BONITO + select SMBUS_EEPROM + select VT82C686 config LOONGSON3V bool + default y + depends on MIPS64 && !TARGET_BIG_ENDIAN + imply PCI_DEVICES + imply TEST_DEVICES + imply VIRTIO_PCI + imply VIRTIO_NET imply VIRTIO_VGA imply QXL if SPICE - select SERIAL + imply USB_OHCI_PCI + select SERIAL_MM select GOLDFISH_RTC + select LOONGSON_IPI 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 + default y + depends on MIPS64 && !TARGET_BIG_ENDIAN && FDT + imply PCI_DEVICES + imply TEST_DEVICES + select DEVICE_TREE select FITLOADER select MIPS_CPS select PCI_EXPRESS_XILINX select AHCI_ICH9 - select SERIAL + select SERIAL_MM config FW_CFG_MIPS bool diff --git a/hw/mips/bootloader.c b/hw/mips/bootloader.c index f5f42f2bf2..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,14 +243,14 @@ 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, +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, @@ -192,7 +277,7 @@ void bl_gen_jump_kernel(uint32_t **p, 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); @@ -203,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 edda87e23c..1ced1e337a 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -22,9 +22,9 @@ #include "elf.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.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" @@ -323,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; @@ -515,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] = { @@ -542,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"); @@ -677,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; @@ -702,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")); @@ -769,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, diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 2b436700ce..13046628cd 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,25 @@ 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; + + object_property_set_bool(OBJECT(cpu), "big-endian", s->cpu_is_bigendian, + &error_abort); /* 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 +95,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 +120,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 +133,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 +145,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); @@ -183,6 +170,7 @@ static Property mips_cps_properties[] = { DEFINE_PROP_UINT32("num-vp", MIPSCPSState, num_vp, 1), DEFINE_PROP_UINT32("num-irq", MIPSCPSState, num_irq, 256), DEFINE_PROP_STRING("cpu-type", MIPSCPSState, cpu_type), + DEFINE_PROP_BOOL("cpu-big-endian", MIPSCPSState, cpu_is_bigendian, false), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/mips/fuloong2e.c b/hw/mips/fuloong2e.c index 34befa5dd5..7fd8296ccb 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" @@ -179,7 +178,7 @@ static void write_bootloader(CPUMIPSState *env, uint8_t *base, /* Second part of the bootloader */ p = (uint32_t *)(base + 0x040); - bl_gen_jump_kernel(&p, + bl_gen_jump_kernel((void **)&p, true, ENVP_VADDR - 64, true, 2, true, ENVP_VADDR, true, ENVP_VADDR + 8, @@ -202,19 +201,9 @@ static void main_cpu_reset(void *opaque) /* 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) @@ -240,7 +229,7 @@ static void mips_fuloong2e_init(MachineState *machine) clock_set_hz(cpuclk, 533080000); /* ~533 MHz */ /* init CPUs */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); + cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk, false); env = &cpu->env; qemu_register_reset(main_cpu_reset, cpu); @@ -295,14 +284,22 @@ static void mips_fuloong2e_init(MachineState *machine) pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); /* South bridge -> IP5 */ - pci_dev = pci_create_simple_multifunction(pci_bus, - PCI_DEVFN(FULOONG2E_VIA_SLOT, 0), - true, TYPE_VT82C686B_ISA); + 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]); + qdev_connect_gpio_out_named(DEVICE(pci_dev), "intr", 0, env->irq[5]); dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); pci_ide_create_devs(PCI_DEVICE(dev)); @@ -337,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/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c deleted file mode 100644 index 19d0d9889f..0000000000 --- a/hw/mips/gt64xxx_pci.c +++ /dev/null @@ -1,1234 +0,0 @@ -/* - * QEMU GT64120 PCI host - * - * Copyright (c) 2006,2007 Aurelien Jarno - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "qemu/units.h" -#include "qemu/log.h" -#include "hw/pci/pci.h" -#include "hw/pci/pci_host.h" -#include "migration/vmstate.h" -#include "hw/intc/i8259.h" -#include "hw/irq.h" -#include "trace.h" -#include "qom/object.h" - -#define GT_REGS (0x1000 >> 2) - -/* CPU Configuration */ -#define GT_CPU (0x000 >> 2) -#define GT_MULTI (0x120 >> 2) - -/* CPU Address Decode */ -#define GT_SCS10LD (0x008 >> 2) -#define GT_SCS10HD (0x010 >> 2) -#define GT_SCS32LD (0x018 >> 2) -#define GT_SCS32HD (0x020 >> 2) -#define GT_CS20LD (0x028 >> 2) -#define GT_CS20HD (0x030 >> 2) -#define GT_CS3BOOTLD (0x038 >> 2) -#define GT_CS3BOOTHD (0x040 >> 2) -#define GT_PCI0IOLD (0x048 >> 2) -#define GT_PCI0IOHD (0x050 >> 2) -#define GT_PCI0M0LD (0x058 >> 2) -#define GT_PCI0M0HD (0x060 >> 2) -#define GT_PCI0M1LD (0x080 >> 2) -#define GT_PCI0M1HD (0x088 >> 2) -#define GT_PCI1IOLD (0x090 >> 2) -#define GT_PCI1IOHD (0x098 >> 2) -#define GT_PCI1M0LD (0x0a0 >> 2) -#define GT_PCI1M0HD (0x0a8 >> 2) -#define GT_PCI1M1LD (0x0b0 >> 2) -#define GT_PCI1M1HD (0x0b8 >> 2) -#define GT_ISD (0x068 >> 2) - -#define GT_SCS10AR (0x0d0 >> 2) -#define GT_SCS32AR (0x0d8 >> 2) -#define GT_CS20R (0x0e0 >> 2) -#define GT_CS3BOOTR (0x0e8 >> 2) - -#define GT_PCI0IOREMAP (0x0f0 >> 2) -#define GT_PCI0M0REMAP (0x0f8 >> 2) -#define GT_PCI0M1REMAP (0x100 >> 2) -#define GT_PCI1IOREMAP (0x108 >> 2) -#define GT_PCI1M0REMAP (0x110 >> 2) -#define GT_PCI1M1REMAP (0x118 >> 2) - -/* CPU Error Report */ -#define GT_CPUERR_ADDRLO (0x070 >> 2) -#define GT_CPUERR_ADDRHI (0x078 >> 2) -#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */ -#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */ -#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */ - -/* CPU Sync Barrier */ -#define GT_PCI0SYNC (0x0c0 >> 2) -#define GT_PCI1SYNC (0x0c8 >> 2) - -/* SDRAM and Device Address Decode */ -#define GT_SCS0LD (0x400 >> 2) -#define GT_SCS0HD (0x404 >> 2) -#define GT_SCS1LD (0x408 >> 2) -#define GT_SCS1HD (0x40c >> 2) -#define GT_SCS2LD (0x410 >> 2) -#define GT_SCS2HD (0x414 >> 2) -#define GT_SCS3LD (0x418 >> 2) -#define GT_SCS3HD (0x41c >> 2) -#define GT_CS0LD (0x420 >> 2) -#define GT_CS0HD (0x424 >> 2) -#define GT_CS1LD (0x428 >> 2) -#define GT_CS1HD (0x42c >> 2) -#define GT_CS2LD (0x430 >> 2) -#define GT_CS2HD (0x434 >> 2) -#define GT_CS3LD (0x438 >> 2) -#define GT_CS3HD (0x43c >> 2) -#define GT_BOOTLD (0x440 >> 2) -#define GT_BOOTHD (0x444 >> 2) -#define GT_ADERR (0x470 >> 2) - -/* SDRAM Configuration */ -#define GT_SDRAM_CFG (0x448 >> 2) -#define GT_SDRAM_OPMODE (0x474 >> 2) -#define GT_SDRAM_BM (0x478 >> 2) -#define GT_SDRAM_ADDRDECODE (0x47c >> 2) - -/* SDRAM Parameters */ -#define GT_SDRAM_B0 (0x44c >> 2) -#define GT_SDRAM_B1 (0x450 >> 2) -#define GT_SDRAM_B2 (0x454 >> 2) -#define GT_SDRAM_B3 (0x458 >> 2) - -/* Device Parameters */ -#define GT_DEV_B0 (0x45c >> 2) -#define GT_DEV_B1 (0x460 >> 2) -#define GT_DEV_B2 (0x464 >> 2) -#define GT_DEV_B3 (0x468 >> 2) -#define GT_DEV_BOOT (0x46c >> 2) - -/* ECC */ -#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */ -#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */ -#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */ -#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */ -#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */ - -/* DMA Record */ -#define GT_DMA0_CNT (0x800 >> 2) -#define GT_DMA1_CNT (0x804 >> 2) -#define GT_DMA2_CNT (0x808 >> 2) -#define GT_DMA3_CNT (0x80c >> 2) -#define GT_DMA0_SA (0x810 >> 2) -#define GT_DMA1_SA (0x814 >> 2) -#define GT_DMA2_SA (0x818 >> 2) -#define GT_DMA3_SA (0x81c >> 2) -#define GT_DMA0_DA (0x820 >> 2) -#define GT_DMA1_DA (0x824 >> 2) -#define GT_DMA2_DA (0x828 >> 2) -#define GT_DMA3_DA (0x82c >> 2) -#define GT_DMA0_NEXT (0x830 >> 2) -#define GT_DMA1_NEXT (0x834 >> 2) -#define GT_DMA2_NEXT (0x838 >> 2) -#define GT_DMA3_NEXT (0x83c >> 2) -#define GT_DMA0_CUR (0x870 >> 2) -#define GT_DMA1_CUR (0x874 >> 2) -#define GT_DMA2_CUR (0x878 >> 2) -#define GT_DMA3_CUR (0x87c >> 2) - -/* DMA Channel Control */ -#define GT_DMA0_CTRL (0x840 >> 2) -#define GT_DMA1_CTRL (0x844 >> 2) -#define GT_DMA2_CTRL (0x848 >> 2) -#define GT_DMA3_CTRL (0x84c >> 2) - -/* DMA Arbiter */ -#define GT_DMA_ARB (0x860 >> 2) - -/* Timer/Counter */ -#define GT_TC0 (0x850 >> 2) -#define GT_TC1 (0x854 >> 2) -#define GT_TC2 (0x858 >> 2) -#define GT_TC3 (0x85c >> 2) -#define GT_TC_CONTROL (0x864 >> 2) - -/* PCI Internal */ -#define GT_PCI0_CMD (0xc00 >> 2) -#define GT_PCI0_TOR (0xc04 >> 2) -#define GT_PCI0_BS_SCS10 (0xc08 >> 2) -#define GT_PCI0_BS_SCS32 (0xc0c >> 2) -#define GT_PCI0_BS_CS20 (0xc10 >> 2) -#define GT_PCI0_BS_CS3BT (0xc14 >> 2) -#define GT_PCI1_IACK (0xc30 >> 2) -#define GT_PCI0_IACK (0xc34 >> 2) -#define GT_PCI0_BARE (0xc3c >> 2) -#define GT_PCI0_PREFMBR (0xc40 >> 2) -#define GT_PCI0_SCS10_BAR (0xc48 >> 2) -#define GT_PCI0_SCS32_BAR (0xc4c >> 2) -#define GT_PCI0_CS20_BAR (0xc50 >> 2) -#define GT_PCI0_CS3BT_BAR (0xc54 >> 2) -#define GT_PCI0_SSCS10_BAR (0xc58 >> 2) -#define GT_PCI0_SSCS32_BAR (0xc5c >> 2) -#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2) -#define GT_PCI1_CMD (0xc80 >> 2) -#define GT_PCI1_TOR (0xc84 >> 2) -#define GT_PCI1_BS_SCS10 (0xc88 >> 2) -#define GT_PCI1_BS_SCS32 (0xc8c >> 2) -#define GT_PCI1_BS_CS20 (0xc90 >> 2) -#define GT_PCI1_BS_CS3BT (0xc94 >> 2) -#define GT_PCI1_BARE (0xcbc >> 2) -#define GT_PCI1_PREFMBR (0xcc0 >> 2) -#define GT_PCI1_SCS10_BAR (0xcc8 >> 2) -#define GT_PCI1_SCS32_BAR (0xccc >> 2) -#define GT_PCI1_CS20_BAR (0xcd0 >> 2) -#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2) -#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2) -#define GT_PCI1_SSCS32_BAR (0xcdc >> 2) -#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2) -#define GT_PCI1_CFGADDR (0xcf0 >> 2) -#define GT_PCI1_CFGDATA (0xcf4 >> 2) -#define GT_PCI0_CFGADDR (0xcf8 >> 2) -#define GT_PCI0_CFGDATA (0xcfc >> 2) - -/* Interrupts */ -#define GT_INTRCAUSE (0xc18 >> 2) -#define GT_INTRMASK (0xc1c >> 2) -#define GT_PCI0_ICMASK (0xc24 >> 2) -#define GT_PCI0_SERR0MASK (0xc28 >> 2) -#define GT_CPU_INTSEL (0xc70 >> 2) -#define GT_PCI0_INTSEL (0xc74 >> 2) -#define GT_HINTRCAUSE (0xc98 >> 2) -#define GT_HINTRMASK (0xc9c >> 2) -#define GT_PCI0_HICMASK (0xca4 >> 2) -#define GT_PCI1_SERR1MASK (0xca8 >> 2) - -#define PCI_MAPPING_ENTRY(regname) \ - hwaddr regname ##_start; \ - hwaddr regname ##_length; \ - MemoryRegion regname ##_mem - -#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120" - -OBJECT_DECLARE_SIMPLE_TYPE(GT64120State, GT64120_PCI_HOST_BRIDGE) - -struct GT64120State { - PCIHostState parent_obj; - - uint32_t regs[GT_REGS]; - PCI_MAPPING_ENTRY(PCI0IO); - PCI_MAPPING_ENTRY(PCI0M0); - PCI_MAPPING_ENTRY(PCI0M1); - PCI_MAPPING_ENTRY(ISD); - MemoryRegion pci0_mem; - AddressSpace pci0_mem_as; -}; - -/* Adjust range to avoid touching space which isn't mappable via PCI */ -/* - * XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000 - * 0x1fc00000 - 0x1fd00000 - */ -static void check_reserved_space(hwaddr *start, hwaddr *length) -{ - hwaddr begin = *start; - hwaddr end = *start + *length; - - if (end >= 0x1e000000LL && end < 0x1f100000LL) { - end = 0x1e000000LL; - } - if (begin >= 0x1e000000LL && begin < 0x1f100000LL) { - begin = 0x1f100000LL; - } - if (end >= 0x1fc00000LL && end < 0x1fd00000LL) { - end = 0x1fc00000LL; - } - if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL) { - begin = 0x1fd00000LL; - } - /* XXX: This is broken when a reserved range splits the requested range */ - if (end >= 0x1f100000LL && begin < 0x1e000000LL) { - end = 0x1e000000LL; - } - if (end >= 0x1fd00000LL && begin < 0x1fc00000LL) { - end = 0x1fc00000LL; - } - - *start = begin; - *length = end - begin; -} - -static void gt64120_isd_mapping(GT64120State *s) -{ - /* Bits 14:0 of ISD map to bits 35:21 of the start address. */ - hwaddr start = ((hwaddr)s->regs[GT_ISD] << 21) & 0xFFFE00000ull; - hwaddr length = 0x1000; - - if (s->ISD_length) { - memory_region_del_subregion(get_system_memory(), &s->ISD_mem); - } - check_reserved_space(&start, &length); - length = 0x1000; - /* Map new address */ - trace_gt64120_isd_remap(s->ISD_length, s->ISD_start, length, start); - s->ISD_start = start; - s->ISD_length = length; - memory_region_add_subregion(get_system_memory(), s->ISD_start, &s->ISD_mem); -} - -static void gt64120_pci_mapping(GT64120State *s) -{ - /* Update PCI0IO mapping */ - if ((s->regs[GT_PCI0IOLD] & 0x7f) <= s->regs[GT_PCI0IOHD]) { - /* Unmap old IO address */ - if (s->PCI0IO_length) { - memory_region_del_subregion(get_system_memory(), &s->PCI0IO_mem); - object_unparent(OBJECT(&s->PCI0IO_mem)); - } - /* Map new IO address */ - s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21; - s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) - - (s->regs[GT_PCI0IOLD] & 0x7f)) << 21; - if (s->PCI0IO_length) { - memory_region_init_alias(&s->PCI0IO_mem, OBJECT(s), "pci0-io", - get_system_io(), 0, s->PCI0IO_length); - memory_region_add_subregion(get_system_memory(), s->PCI0IO_start, - &s->PCI0IO_mem); - } - } - - /* Update PCI0M0 mapping */ - if ((s->regs[GT_PCI0M0LD] & 0x7f) <= s->regs[GT_PCI0M0HD]) { - /* Unmap old MEM address */ - if (s->PCI0M0_length) { - memory_region_del_subregion(get_system_memory(), &s->PCI0M0_mem); - object_unparent(OBJECT(&s->PCI0M0_mem)); - } - /* Map new mem address */ - s->PCI0M0_start = s->regs[GT_PCI0M0LD] << 21; - s->PCI0M0_length = ((s->regs[GT_PCI0M0HD] + 1) - - (s->regs[GT_PCI0M0LD] & 0x7f)) << 21; - if (s->PCI0M0_length) { - memory_region_init_alias(&s->PCI0M0_mem, OBJECT(s), "pci0-mem0", - &s->pci0_mem, s->PCI0M0_start, - s->PCI0M0_length); - memory_region_add_subregion(get_system_memory(), s->PCI0M0_start, - &s->PCI0M0_mem); - } - } - - /* Update PCI0M1 mapping */ - if ((s->regs[GT_PCI0M1LD] & 0x7f) <= s->regs[GT_PCI0M1HD]) { - /* Unmap old MEM address */ - if (s->PCI0M1_length) { - memory_region_del_subregion(get_system_memory(), &s->PCI0M1_mem); - object_unparent(OBJECT(&s->PCI0M1_mem)); - } - /* Map new mem address */ - s->PCI0M1_start = s->regs[GT_PCI0M1LD] << 21; - s->PCI0M1_length = ((s->regs[GT_PCI0M1HD] + 1) - - (s->regs[GT_PCI0M1LD] & 0x7f)) << 21; - if (s->PCI0M1_length) { - memory_region_init_alias(&s->PCI0M1_mem, OBJECT(s), "pci0-mem1", - &s->pci0_mem, s->PCI0M1_start, - s->PCI0M1_length); - memory_region_add_subregion(get_system_memory(), s->PCI0M1_start, - &s->PCI0M1_mem); - } - } -} - -static int gt64120_post_load(void *opaque, int version_id) -{ - GT64120State *s = opaque; - - gt64120_isd_mapping(s); - gt64120_pci_mapping(s); - - return 0; -} - -static const VMStateDescription vmstate_gt64120 = { - .name = "gt64120", - .version_id = 1, - .minimum_version_id = 1, - .post_load = gt64120_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, GT64120State, GT_REGS), - VMSTATE_END_OF_LIST() - } -}; - -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); - if (!(s->regs[GT_CPU] & 0x00001000)) { - val = bswap32(val); - } - - switch (saddr) { - - /* CPU Configuration */ - case GT_CPU: - s->regs[GT_CPU] = val; - break; - case GT_MULTI: - /* Read-only register as only one GT64xxx is present on the CPU bus */ - break; - - /* CPU Address Decode */ - case GT_PCI0IOLD: - s->regs[GT_PCI0IOLD] = val & 0x00007fff; - s->regs[GT_PCI0IOREMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI0M0LD: - s->regs[GT_PCI0M0LD] = val & 0x00007fff; - s->regs[GT_PCI0M0REMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI0M1LD: - s->regs[GT_PCI0M1LD] = val & 0x00007fff; - s->regs[GT_PCI0M1REMAP] = val & 0x000007ff; - gt64120_pci_mapping(s); - break; - case GT_PCI1IOLD: - s->regs[GT_PCI1IOLD] = val & 0x00007fff; - s->regs[GT_PCI1IOREMAP] = val & 0x000007ff; - break; - case GT_PCI1M0LD: - s->regs[GT_PCI1M0LD] = val & 0x00007fff; - s->regs[GT_PCI1M0REMAP] = val & 0x000007ff; - break; - case GT_PCI1M1LD: - s->regs[GT_PCI1M1LD] = val & 0x00007fff; - s->regs[GT_PCI1M1REMAP] = val & 0x000007ff; - break; - case GT_PCI0M0HD: - case GT_PCI0M1HD: - case GT_PCI0IOHD: - s->regs[saddr] = val & 0x0000007f; - gt64120_pci_mapping(s); - break; - case GT_PCI1IOHD: - case GT_PCI1M0HD: - case GT_PCI1M1HD: - s->regs[saddr] = val & 0x0000007f; - break; - case GT_ISD: - s->regs[saddr] = val & 0x00007fff; - gt64120_isd_mapping(s); - break; - - case GT_PCI0IOREMAP: - case GT_PCI0M0REMAP: - case GT_PCI0M1REMAP: - case GT_PCI1IOREMAP: - case GT_PCI1M0REMAP: - case GT_PCI1M1REMAP: - s->regs[saddr] = val & 0x000007ff; - break; - - /* CPU Error Report */ - case GT_CPUERR_ADDRLO: - case GT_CPUERR_ADDRHI: - case GT_CPUERR_DATALO: - case GT_CPUERR_DATAHI: - case GT_CPUERR_PARITY: - /* Read-only registers, do nothing */ - qemu_log_mask(LOG_GUEST_ERROR, - "gt64120: Read-only register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - - /* CPU Sync Barrier */ - case GT_PCI0SYNC: - case GT_PCI1SYNC: - /* Read-only registers, do nothing */ - qemu_log_mask(LOG_GUEST_ERROR, - "gt64120: Read-only register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - - /* SDRAM and Device Address Decode */ - case GT_SCS0LD: - case GT_SCS0HD: - case GT_SCS1LD: - case GT_SCS1HD: - case GT_SCS2LD: - case GT_SCS2HD: - case GT_SCS3LD: - case GT_SCS3HD: - case GT_CS0LD: - case GT_CS0HD: - case GT_CS1LD: - case GT_CS1HD: - case GT_CS2LD: - case GT_CS2HD: - case GT_CS3LD: - case GT_CS3HD: - case GT_BOOTLD: - case GT_BOOTHD: - case GT_ADERR: - /* SDRAM Configuration */ - case GT_SDRAM_CFG: - case GT_SDRAM_OPMODE: - case GT_SDRAM_BM: - case GT_SDRAM_ADDRDECODE: - /* Accept and ignore SDRAM interleave configuration */ - s->regs[saddr] = val; - break; - - /* Device Parameters */ - case GT_DEV_B0: - case GT_DEV_B1: - case GT_DEV_B2: - case GT_DEV_B3: - case GT_DEV_BOOT: - /* Not implemented */ - qemu_log_mask(LOG_UNIMP, - "gt64120: Unimplemented device register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - - /* ECC */ - case GT_ECC_ERRDATALO: - case GT_ECC_ERRDATAHI: - case GT_ECC_MEM: - case GT_ECC_CALC: - case GT_ECC_ERRADDR: - /* Read-only registers, do nothing */ - qemu_log_mask(LOG_GUEST_ERROR, - "gt64120: Read-only register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - - /* DMA Record */ - case GT_DMA0_CNT: - case GT_DMA1_CNT: - case GT_DMA2_CNT: - case GT_DMA3_CNT: - case GT_DMA0_SA: - case GT_DMA1_SA: - case GT_DMA2_SA: - case GT_DMA3_SA: - case GT_DMA0_DA: - case GT_DMA1_DA: - case GT_DMA2_DA: - case GT_DMA3_DA: - case GT_DMA0_NEXT: - case GT_DMA1_NEXT: - case GT_DMA2_NEXT: - case GT_DMA3_NEXT: - case GT_DMA0_CUR: - case GT_DMA1_CUR: - case GT_DMA2_CUR: - case GT_DMA3_CUR: - - /* DMA Channel Control */ - case GT_DMA0_CTRL: - case GT_DMA1_CTRL: - case GT_DMA2_CTRL: - case GT_DMA3_CTRL: - - /* DMA Arbiter */ - case GT_DMA_ARB: - /* Not implemented */ - qemu_log_mask(LOG_UNIMP, - "gt64120: Unimplemented DMA register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - - /* Timer/Counter */ - case GT_TC0: - case GT_TC1: - case GT_TC2: - case GT_TC3: - case GT_TC_CONTROL: - /* Not implemented */ - qemu_log_mask(LOG_UNIMP, - "gt64120: Unimplemented timer register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - - /* PCI Internal */ - case GT_PCI0_CMD: - case GT_PCI1_CMD: - s->regs[saddr] = val & 0x0401fc0f; - break; - case GT_PCI0_TOR: - case GT_PCI0_BS_SCS10: - case GT_PCI0_BS_SCS32: - case GT_PCI0_BS_CS20: - case GT_PCI0_BS_CS3BT: - case GT_PCI1_IACK: - case GT_PCI0_IACK: - case GT_PCI0_BARE: - case GT_PCI0_PREFMBR: - case GT_PCI0_SCS10_BAR: - case GT_PCI0_SCS32_BAR: - case GT_PCI0_CS20_BAR: - case GT_PCI0_CS3BT_BAR: - case GT_PCI0_SSCS10_BAR: - case GT_PCI0_SSCS32_BAR: - case GT_PCI0_SCS3BT_BAR: - case GT_PCI1_TOR: - case GT_PCI1_BS_SCS10: - case GT_PCI1_BS_SCS32: - case GT_PCI1_BS_CS20: - case GT_PCI1_BS_CS3BT: - case GT_PCI1_BARE: - case GT_PCI1_PREFMBR: - case GT_PCI1_SCS10_BAR: - case GT_PCI1_SCS32_BAR: - case GT_PCI1_CS20_BAR: - case GT_PCI1_CS3BT_BAR: - case GT_PCI1_SSCS10_BAR: - case GT_PCI1_SSCS32_BAR: - case GT_PCI1_SCS3BT_BAR: - case GT_PCI1_CFGADDR: - case GT_PCI1_CFGDATA: - /* not implemented */ - qemu_log_mask(LOG_UNIMP, - "gt64120: Unimplemented PCI register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - 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); - } - break; - - /* Interrupts */ - case GT_INTRCAUSE: - /* not really implemented */ - s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe)); - s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe); - trace_gt64120_write_intreg("INTRCAUSE", size, val); - break; - case GT_INTRMASK: - s->regs[saddr] = val & 0x3c3ffffe; - trace_gt64120_write_intreg("INTRMASK", size, val); - break; - case GT_PCI0_ICMASK: - s->regs[saddr] = val & 0x03fffffe; - trace_gt64120_write_intreg("ICMASK", size, val); - break; - case GT_PCI0_SERR0MASK: - s->regs[saddr] = val & 0x0000003f; - trace_gt64120_write_intreg("SERR0MASK", size, val); - break; - - /* Reserved when only PCI_0 is configured. */ - case GT_HINTRCAUSE: - case GT_CPU_INTSEL: - case GT_PCI0_INTSEL: - case GT_HINTRMASK: - case GT_PCI0_HICMASK: - case GT_PCI1_SERR1MASK: - /* not implemented */ - break; - - /* SDRAM Parameters */ - case GT_SDRAM_B0: - case GT_SDRAM_B1: - case GT_SDRAM_B2: - case GT_SDRAM_B3: - /* - * We don't simulate electrical parameters of the SDRAM. - * Accept, but ignore the values. - */ - s->regs[saddr] = val; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "gt64120: Illegal register write " - "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", - saddr << 2, size, size << 1, val); - break; - } -} - -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; - - switch (saddr) { - - /* CPU Configuration */ - case GT_MULTI: - /* - * Only one GT64xxx is present on the CPU bus, return - * the initial value. - */ - val = s->regs[saddr]; - break; - - /* CPU Error Report */ - case GT_CPUERR_ADDRLO: - case GT_CPUERR_ADDRHI: - case GT_CPUERR_DATALO: - case GT_CPUERR_DATAHI: - case GT_CPUERR_PARITY: - /* Emulated memory has no error, always return the initial values. */ - val = s->regs[saddr]; - break; - - /* CPU Sync Barrier */ - case GT_PCI0SYNC: - case GT_PCI1SYNC: - /* - * Reading those register should empty all FIFO on the PCI - * bus, which are not emulated. The return value should be - * a random value that should be ignored. - */ - val = 0xc000ffee; - break; - - /* ECC */ - case GT_ECC_ERRDATALO: - case GT_ECC_ERRDATAHI: - case GT_ECC_MEM: - case GT_ECC_CALC: - case GT_ECC_ERRADDR: - /* Emulated memory has no error, always return the initial values. */ - val = s->regs[saddr]; - break; - - case GT_CPU: - case GT_SCS10LD: - case GT_SCS10HD: - case GT_SCS32LD: - case GT_SCS32HD: - case GT_CS20LD: - case GT_CS20HD: - case GT_CS3BOOTLD: - case GT_CS3BOOTHD: - case GT_SCS10AR: - case GT_SCS32AR: - case GT_CS20R: - case GT_CS3BOOTR: - case GT_PCI0IOLD: - case GT_PCI0M0LD: - case GT_PCI0M1LD: - case GT_PCI1IOLD: - case GT_PCI1M0LD: - case GT_PCI1M1LD: - case GT_PCI0IOHD: - case GT_PCI0M0HD: - case GT_PCI0M1HD: - case GT_PCI1IOHD: - case GT_PCI1M0HD: - case GT_PCI1M1HD: - case GT_PCI0IOREMAP: - case GT_PCI0M0REMAP: - case GT_PCI0M1REMAP: - case GT_PCI1IOREMAP: - case GT_PCI1M0REMAP: - case GT_PCI1M1REMAP: - case GT_ISD: - val = s->regs[saddr]; - break; - case GT_PCI0_IACK: - /* Read the IRQ number */ - val = pic_read_irq(isa_pic); - break; - - /* SDRAM and Device Address Decode */ - case GT_SCS0LD: - case GT_SCS0HD: - case GT_SCS1LD: - case GT_SCS1HD: - case GT_SCS2LD: - case GT_SCS2HD: - case GT_SCS3LD: - case GT_SCS3HD: - case GT_CS0LD: - case GT_CS0HD: - case GT_CS1LD: - case GT_CS1HD: - case GT_CS2LD: - case GT_CS2HD: - case GT_CS3LD: - case GT_CS3HD: - case GT_BOOTLD: - case GT_BOOTHD: - case GT_ADERR: - val = s->regs[saddr]; - break; - - /* SDRAM Configuration */ - case GT_SDRAM_CFG: - case GT_SDRAM_OPMODE: - case GT_SDRAM_BM: - case GT_SDRAM_ADDRDECODE: - val = s->regs[saddr]; - break; - - /* SDRAM Parameters */ - case GT_SDRAM_B0: - case GT_SDRAM_B1: - case GT_SDRAM_B2: - case GT_SDRAM_B3: - /* - * We don't simulate electrical parameters of the SDRAM. - * Just return the last written value. - */ - val = s->regs[saddr]; - break; - - /* Device Parameters */ - case GT_DEV_B0: - case GT_DEV_B1: - case GT_DEV_B2: - case GT_DEV_B3: - case GT_DEV_BOOT: - val = s->regs[saddr]; - break; - - /* DMA Record */ - case GT_DMA0_CNT: - case GT_DMA1_CNT: - case GT_DMA2_CNT: - case GT_DMA3_CNT: - case GT_DMA0_SA: - case GT_DMA1_SA: - case GT_DMA2_SA: - case GT_DMA3_SA: - case GT_DMA0_DA: - case GT_DMA1_DA: - case GT_DMA2_DA: - case GT_DMA3_DA: - case GT_DMA0_NEXT: - case GT_DMA1_NEXT: - case GT_DMA2_NEXT: - case GT_DMA3_NEXT: - case GT_DMA0_CUR: - case GT_DMA1_CUR: - case GT_DMA2_CUR: - case GT_DMA3_CUR: - val = s->regs[saddr]; - break; - - /* DMA Channel Control */ - case GT_DMA0_CTRL: - case GT_DMA1_CTRL: - case GT_DMA2_CTRL: - case GT_DMA3_CTRL: - val = s->regs[saddr]; - break; - - /* DMA Arbiter */ - case GT_DMA_ARB: - val = s->regs[saddr]; - break; - - /* Timer/Counter */ - case GT_TC0: - case GT_TC1: - case GT_TC2: - case GT_TC3: - case GT_TC_CONTROL: - val = s->regs[saddr]; - break; - - /* 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); - } - break; - - case GT_PCI0_CMD: - case GT_PCI0_TOR: - case GT_PCI0_BS_SCS10: - case GT_PCI0_BS_SCS32: - case GT_PCI0_BS_CS20: - case GT_PCI0_BS_CS3BT: - case GT_PCI1_IACK: - case GT_PCI0_BARE: - case GT_PCI0_PREFMBR: - case GT_PCI0_SCS10_BAR: - case GT_PCI0_SCS32_BAR: - case GT_PCI0_CS20_BAR: - case GT_PCI0_CS3BT_BAR: - case GT_PCI0_SSCS10_BAR: - case GT_PCI0_SSCS32_BAR: - case GT_PCI0_SCS3BT_BAR: - case GT_PCI1_CMD: - case GT_PCI1_TOR: - case GT_PCI1_BS_SCS10: - case GT_PCI1_BS_SCS32: - case GT_PCI1_BS_CS20: - case GT_PCI1_BS_CS3BT: - case GT_PCI1_BARE: - case GT_PCI1_PREFMBR: - case GT_PCI1_SCS10_BAR: - case GT_PCI1_SCS32_BAR: - case GT_PCI1_CS20_BAR: - case GT_PCI1_CS3BT_BAR: - case GT_PCI1_SSCS10_BAR: - case GT_PCI1_SSCS32_BAR: - case GT_PCI1_SCS3BT_BAR: - case GT_PCI1_CFGADDR: - case GT_PCI1_CFGDATA: - val = s->regs[saddr]; - break; - - /* Interrupts */ - case GT_INTRCAUSE: - val = s->regs[saddr]; - trace_gt64120_read_intreg("INTRCAUSE", size, val); - break; - case GT_INTRMASK: - val = s->regs[saddr]; - trace_gt64120_read_intreg("INTRMASK", size, val); - break; - case GT_PCI0_ICMASK: - val = s->regs[saddr]; - trace_gt64120_read_intreg("ICMASK", size, val); - break; - case GT_PCI0_SERR0MASK: - val = s->regs[saddr]; - trace_gt64120_read_intreg("SERR0MASK", size, val); - break; - - /* Reserved when only PCI_0 is configured. */ - case GT_HINTRCAUSE: - case GT_CPU_INTSEL: - case GT_PCI0_INTSEL: - case GT_HINTRMASK: - case GT_PCI0_HICMASK: - case GT_PCI1_SERR1MASK: - val = s->regs[saddr]; - break; - - default: - val = s->regs[saddr]; - qemu_log_mask(LOG_GUEST_ERROR, - "gt64120: Illegal register read " - "reg:0x%03x size:%u value:0x%0*x\n", - saddr << 2, size, size << 1, val); - break; - } - - if (!(s->regs[GT_CPU] & 0x00001000)) { - val = bswap32(val); - } - trace_gt64120_read(addr, val); - - return val; -} - -static const MemoryRegionOps isd_mem_ops = { - .read = gt64120_readl, - .write = gt64120_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void gt64120_reset(DeviceState *dev) -{ - GT64120State *s = GT64120_PCI_HOST_BRIDGE(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_MULTI] = 0x00000003; - - /* CPU Address decode */ - s->regs[GT_SCS10LD] = 0x00000000; - s->regs[GT_SCS10HD] = 0x00000007; - s->regs[GT_SCS32LD] = 0x00000008; - s->regs[GT_SCS32HD] = 0x0000000f; - s->regs[GT_CS20LD] = 0x000000e0; - s->regs[GT_CS20HD] = 0x00000070; - s->regs[GT_CS3BOOTLD] = 0x000000f8; - s->regs[GT_CS3BOOTHD] = 0x0000007f; - - s->regs[GT_PCI0IOLD] = 0x00000080; - s->regs[GT_PCI0IOHD] = 0x0000000f; - s->regs[GT_PCI0M0LD] = 0x00000090; - s->regs[GT_PCI0M0HD] = 0x0000001f; - s->regs[GT_ISD] = 0x000000a0; - s->regs[GT_PCI0M1LD] = 0x00000790; - s->regs[GT_PCI0M1HD] = 0x0000001f; - s->regs[GT_PCI1IOLD] = 0x00000100; - s->regs[GT_PCI1IOHD] = 0x0000000f; - s->regs[GT_PCI1M0LD] = 0x00000110; - s->regs[GT_PCI1M0HD] = 0x0000001f; - s->regs[GT_PCI1M1LD] = 0x00000120; - s->regs[GT_PCI1M1HD] = 0x0000002f; - - s->regs[GT_SCS10AR] = 0x00000000; - s->regs[GT_SCS32AR] = 0x00000008; - s->regs[GT_CS20R] = 0x000000e0; - s->regs[GT_CS3BOOTR] = 0x000000f8; - - s->regs[GT_PCI0IOREMAP] = 0x00000080; - s->regs[GT_PCI0M0REMAP] = 0x00000090; - s->regs[GT_PCI0M1REMAP] = 0x00000790; - s->regs[GT_PCI1IOREMAP] = 0x00000100; - s->regs[GT_PCI1M0REMAP] = 0x00000110; - s->regs[GT_PCI1M1REMAP] = 0x00000120; - - /* CPU Error Report */ - s->regs[GT_CPUERR_ADDRLO] = 0x00000000; - s->regs[GT_CPUERR_ADDRHI] = 0x00000000; - s->regs[GT_CPUERR_DATALO] = 0xffffffff; - s->regs[GT_CPUERR_DATAHI] = 0xffffffff; - s->regs[GT_CPUERR_PARITY] = 0x000000ff; - - /* CPU Sync Barrier */ - s->regs[GT_PCI0SYNC] = 0x00000000; - s->regs[GT_PCI1SYNC] = 0x00000000; - - /* SDRAM and Device Address Decode */ - s->regs[GT_SCS0LD] = 0x00000000; - s->regs[GT_SCS0HD] = 0x00000007; - s->regs[GT_SCS1LD] = 0x00000008; - s->regs[GT_SCS1HD] = 0x0000000f; - s->regs[GT_SCS2LD] = 0x00000010; - s->regs[GT_SCS2HD] = 0x00000017; - s->regs[GT_SCS3LD] = 0x00000018; - s->regs[GT_SCS3HD] = 0x0000001f; - s->regs[GT_CS0LD] = 0x000000c0; - s->regs[GT_CS0HD] = 0x000000c7; - s->regs[GT_CS1LD] = 0x000000c8; - s->regs[GT_CS1HD] = 0x000000cf; - s->regs[GT_CS2LD] = 0x000000d0; - s->regs[GT_CS2HD] = 0x000000df; - s->regs[GT_CS3LD] = 0x000000f0; - s->regs[GT_CS3HD] = 0x000000fb; - s->regs[GT_BOOTLD] = 0x000000fc; - s->regs[GT_BOOTHD] = 0x000000ff; - s->regs[GT_ADERR] = 0xffffffff; - - /* SDRAM Configuration */ - s->regs[GT_SDRAM_CFG] = 0x00000200; - s->regs[GT_SDRAM_OPMODE] = 0x00000000; - s->regs[GT_SDRAM_BM] = 0x00000007; - s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002; - - /* SDRAM Parameters */ - s->regs[GT_SDRAM_B0] = 0x00000005; - s->regs[GT_SDRAM_B1] = 0x00000005; - s->regs[GT_SDRAM_B2] = 0x00000005; - s->regs[GT_SDRAM_B3] = 0x00000005; - - /* ECC */ - s->regs[GT_ECC_ERRDATALO] = 0x00000000; - s->regs[GT_ECC_ERRDATAHI] = 0x00000000; - s->regs[GT_ECC_MEM] = 0x00000000; - s->regs[GT_ECC_CALC] = 0x00000000; - s->regs[GT_ECC_ERRADDR] = 0x00000000; - - /* Device Parameters */ - s->regs[GT_DEV_B0] = 0x386fffff; - s->regs[GT_DEV_B1] = 0x386fffff; - s->regs[GT_DEV_B2] = 0x386fffff; - s->regs[GT_DEV_B3] = 0x386fffff; - s->regs[GT_DEV_BOOT] = 0x146fffff; - - /* DMA registers are all zeroed at reset */ - - /* Timer/Counter */ - s->regs[GT_TC0] = 0xffffffff; - s->regs[GT_TC1] = 0x00ffffff; - s->regs[GT_TC2] = 0x00ffffff; - s->regs[GT_TC3] = 0x00ffffff; - 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_TOR] = 0x0000070f; - s->regs[GT_PCI0_BS_SCS10] = 0x00fff000; - s->regs[GT_PCI0_BS_SCS32] = 0x00fff000; - s->regs[GT_PCI0_BS_CS20] = 0x01fff000; - s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000; - s->regs[GT_PCI1_IACK] = 0x00000000; - s->regs[GT_PCI0_IACK] = 0x00000000; - s->regs[GT_PCI0_BARE] = 0x0000000f; - s->regs[GT_PCI0_PREFMBR] = 0x00000040; - s->regs[GT_PCI0_SCS10_BAR] = 0x00000000; - s->regs[GT_PCI0_SCS32_BAR] = 0x01000000; - s->regs[GT_PCI0_CS20_BAR] = 0x1c000000; - s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000; - 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_TOR] = 0x0000070f; - s->regs[GT_PCI1_BS_SCS10] = 0x00fff000; - s->regs[GT_PCI1_BS_SCS32] = 0x00fff000; - s->regs[GT_PCI1_BS_CS20] = 0x01fff000; - s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000; - s->regs[GT_PCI1_BARE] = 0x0000000f; - s->regs[GT_PCI1_PREFMBR] = 0x00000040; - s->regs[GT_PCI1_SCS10_BAR] = 0x00000000; - s->regs[GT_PCI1_SCS32_BAR] = 0x01000000; - s->regs[GT_PCI1_CS20_BAR] = 0x1c000000; - s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000; - s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000; - s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000; - s->regs[GT_PCI1_CFGADDR] = 0x00000000; - s->regs[GT_PCI1_CFGDATA] = 0x00000000; - s->regs[GT_PCI0_CFGADDR] = 0x00000000; - - /* Interrupt registers are all zeroed at reset */ - - gt64120_isd_mapping(s); - gt64120_pci_mapping(s); -} - -static void gt64120_realize(DeviceState *dev, Error **errp) -{ - GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); - PCIHostState *phb = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&s->ISD_mem, OBJECT(dev), &isd_mem_ops, s, - "gt64120-isd", 0x1000); - memory_region_init(&s->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB); - address_space_init(&s->pci0_mem_as, &s->pci0_mem, "pci0-mem"); - phb->bus = pci_root_bus_new(dev, "pci", - &s->pci0_mem, - get_system_io(), - PCI_DEVFN(18, 0), TYPE_PCI_BUS); - - pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); -} - -static void gt64120_pci_realize(PCIDevice *d, Error **errp) -{ - /* FIXME: Malta specific hw assumptions ahead */ - pci_set_word(d->config + PCI_COMMAND, 0); - pci_set_word(d->config + PCI_STATUS, - PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); - pci_config_set_prog_interface(d->config, 0); - pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); - pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); - pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); - pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); - pci_set_byte(d->config + 0x3d, 0x01); -} - -static void gt64120_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - k->realize = gt64120_pci_realize; - k->vendor_id = PCI_VENDOR_ID_MARVELL; - k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; - k->revision = 0x10; - k->class_id = PCI_CLASS_BRIDGE_HOST; - /* - * 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 gt64120_pci_info = { - .name = "gt64120_pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = gt64120_pci_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void gt64120_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - dc->realize = gt64120_realize; - dc->reset = gt64120_reset; - dc->vmsd = &vmstate_gt64120; -} - -static const TypeInfo gt64120_info = { - .name = TYPE_GT64120_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(GT64120State), - .class_init = gt64120_class_init, -}; - -static void gt64120_pci_register_types(void) -{ - type_register_static(>64120_info); - type_register_static(>64120_pci_info); -} - -type_init(gt64120_pci_register_types) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 6aefe9a61b..0e43c9f0ba 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -26,10 +26,9 @@ #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" +#include "hw/char/serial-mm.h" #include "hw/char/parallel.h" #include "hw/isa/isa.h" #include "hw/block/fdc.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, "dp83932"); + 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; @@ -138,12 +183,12 @@ static void mips_jazz_init(MachineState *machine, MemoryRegion *rtc = 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); @@ -173,7 +212,8 @@ static void mips_jazz_init(MachineState *machine, * ext_clk[jazz_model].pll_mult); /* init CPUs */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); + cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk, + TARGET_BIG_ENDIAN); env = &cpu->env; qemu_register_reset(main_cpu_reset, cpu); @@ -249,10 +289,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 +329,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); diff --git a/hw/mips/loongson3_bootp.c b/hw/mips/loongson3_bootp.c index f99af22932..b97b81903b 100644 --- a/hw/mips/loongson3_bootp.c +++ b/hw/mips/loongson3_bootp.c @@ -25,8 +25,6 @@ #include "hw/boards.h" #include "hw/mips/loongson3_bootp.h" -#define LOONGSON3_CORE_PER_NODE 4 - static void init_cpu_info(void *g_cpuinfo, uint64_t cpu_freq) { struct efi_cpuinfo_loongson *c = g_cpuinfo; @@ -148,4 +146,5 @@ void init_reset_system(struct efi_reset_system_t *reset) reset->Shutdown = cpu_to_le64(0xffffffffbfc000a8); reset->ResetCold = cpu_to_le64(0xffffffffbfc00080); reset->ResetWarm = cpu_to_le64(0xffffffffbfc00080); + reset->DoSuspend = cpu_to_le64(0xffffffffbfc000d0); } diff --git a/hw/mips/loongson3_bootp.h b/hw/mips/loongson3_bootp.h index d525ab745a..9091265df7 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]; @@ -200,6 +200,8 @@ struct boot_params { struct efi_reset_system_t reset_system; }; +#define LOONGSON3_CORE_PER_NODE 4 + /* Overall MMIO & Memory layout */ enum { VIRT_LOWMEM, @@ -211,6 +213,7 @@ enum { VIRT_BIOS_ROM, VIRT_UART, VIRT_LIOINTC, + VIRT_IPI, VIRT_PCIE_MMIO, VIRT_HIGHMEM }; diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 25534288dd..f3b6326cc5 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -29,15 +29,14 @@ #include "qemu/datadir.h" #include "qapi/error.h" #include "elf.h" -#include "kvm_mips.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.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" #include "hw/intc/i8259.h" +#include "hw/intc/loongson_ipi.h" #include "hw/loader.h" #include "hw/isa/superio.h" #include "hw/pci/msi.h" @@ -76,6 +75,7 @@ const MemMapEntry virt_memmap[] = { [VIRT_PCIE_ECAM] = { 0x1a000000, 0x2000000 }, [VIRT_BIOS_ROM] = { 0x1fc00000, 0x200000 }, [VIRT_UART] = { 0x1fe001e0, 0x8 }, + [VIRT_IPI] = { 0x3ff01000, 0x400 }, [VIRT_LIOINTC] = { 0x3ff01400, 0x64 }, [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 }, [VIRT_HIGHMEM] = { 0x80000000, 0x0 }, /* Variable */ @@ -97,6 +97,7 @@ struct LoongsonMachineState { MemoryRegion *pio_alias; MemoryRegion *mmio_alias; MemoryRegion *ecam_alias; + MemoryRegion *core_iocsr[LOONGSON_MAX_VCPUS]; }; typedef struct LoongsonMachineState LoongsonMachineState; @@ -129,6 +130,9 @@ static void loongson3_pm_write(void *opaque, hwaddr addr, case 0x00: qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; + case 0x01: + qemu_system_suspend_request(); + return; case 0xff: qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); return; @@ -252,6 +256,17 @@ static void init_boot_rom(void) 0x240D00FF, /* li t1, 0xff */ 0xA18D0000, /* sb t1, (t0) */ 0x1000FFFF, /* 1: b 1b */ + 0x00000000, /* nop */ + /* Suspend */ + 0x3C0C9000, /* dli t0, 0x9000000010080010 */ + 0x358C0000, + 0x000C6438, + 0x358C1008, + 0x000C6438, + 0x358C0010, + 0x240D0001, /* li t1, 0x01 */ + 0xA18D0000, /* sb t1, (t0) */ + 0x03e00008, /* jr ra */ 0x00000000 /* nop */ }; @@ -267,6 +282,7 @@ static void fw_cfg_boot_set(void *opaque, const char *boot_device, static void fw_conf_init(unsigned long ram_size) { + static const uint8_t suspend[6] = {128, 0, 0, 129, 128, 128}; FWCfgState *fw_cfg; hwaddr cfg_addr = virt_memmap[VIRT_FW_CFG].base; @@ -276,6 +292,10 @@ static void fw_conf_init(unsigned long ram_size) fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); fw_cfg_add_i32(fw_cfg, FW_CFG_MACHINE_VERSION, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_CPU_FREQ, get_cpu_freq_hz()); + + fw_cfg_add_file(fw_cfg, "etc/system-states", + g_memdup2(suspend, sizeof(suspend)), sizeof(suspend)); + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } @@ -336,8 +356,8 @@ static uint64_t load_kernel(CPUMIPSState *env) kernel_size = load_elf(loaderparams.kernel_filename, NULL, cpu_mips_kseg0_to_phys, NULL, - (uint64_t *)&kernel_entry, - (uint64_t *)&kernel_low, (uint64_t *)&kernel_high, + &kernel_entry, + &kernel_low, &kernel_high, NULL, 0, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", @@ -406,6 +426,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 +467,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) @@ -471,23 +488,26 @@ static void mips_loongson3_virt_init(MachineState *machine) Clock *cpuclk; CPUMIPSState *env; DeviceState *liointc; + DeviceState *ipi = NULL; char *filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; ram_addr_t ram_size = machine->ram_size; + LoongsonMachineState *s = LOONGSON_MACHINE(machine); MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *iomem = g_new(MemoryRegion, 1); + MemoryRegion *iocsr = g_new(MemoryRegion, 1); /* TODO: TCG will support all CPU types */ if (!kvm_enabled()) { 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 { @@ -513,6 +533,19 @@ static void mips_loongson3_virt_init(MachineState *machine) create_unimplemented_device("mmio fallback 0", 0x10000000, 256 * MiB); create_unimplemented_device("mmio fallback 1", 0x30000000, 256 * MiB); + memory_region_init(iocsr, OBJECT(machine), "loongson3.iocsr", UINT32_MAX); + + /* IPI controller is in kernel for KVM */ + if (!kvm_enabled()) { + ipi = qdev_new(TYPE_LOONGSON_IPI); + qdev_prop_set_uint32(ipi, "num-cpu", machine->smp.cpus); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); + memory_region_add_subregion(iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + } + liointc = qdev_new("loongson.liointc"); sysbus_realize_and_unref(SYS_BUS_DEVICE(liointc), &error_fatal); @@ -529,22 +562,41 @@ static void mips_loongson3_virt_init(MachineState *machine) clock_set_hz(cpuclk, DEF_LOONGSON3_FREQ); for (i = 0; i < machine->smp.cpus; i++) { + int node = i / LOONGSON3_CORE_PER_NODE; + int core = i % LOONGSON3_CORE_PER_NODE; int ip; /* init CPUs */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); + cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk, false); /* Init internal devices */ cpu_mips_irq_init_cpu(cpu); cpu_mips_clock_init(cpu); qemu_register_reset(main_cpu_reset, cpu); - if (i >= 4) { + if (!kvm_enabled()) { + hwaddr base = ((hwaddr)node << 44) + virt_memmap[VIRT_IPI].base; + base += core * 0x100; + qdev_connect_gpio_out(ipi, i, cpu->env.irq[6]); + sysbus_mmio_map(SYS_BUS_DEVICE(ipi), i + 2, base); + } + + if (ase_lcsr_available(&MIPS_CPU(cpu)->env)) { + MemoryRegion *core_iocsr = g_new(MemoryRegion, 1); + g_autofree char *name = g_strdup_printf("core%d_iocsr", i); + memory_region_init_alias(core_iocsr, OBJECT(cpu), name, + iocsr, 0, UINT32_MAX); + memory_region_add_subregion(&MIPS_CPU(cpu)->env.iocsr.mr, + 0, core_iocsr); + s->core_iocsr[i] = core_iocsr; + } + + if (node > 0) { continue; /* Only node-0 can be connected to LIOINTC */ } for (ip = 0; ip < 4 ; ip++) { - int pin = i * 4 + ip; + int pin = core * LOONGSON3_CORE_PER_NODE + ip; sysbus_connect_irq(SYS_BUS_DEVICE(liointc), pin, cpu->env.irq[ip + 2]); } @@ -558,6 +610,7 @@ static void mips_loongson3_virt_init(MachineState *machine) machine->ram, 0, virt_memmap[VIRT_LOWMEM].size); memory_region_init_io(iomem, NULL, &loongson3_pm_ops, NULL, "loongson3_pm", virt_memmap[VIRT_PM].size); + qemu_register_wakeup_support(); memory_region_add_subregion(address_space_mem, virt_memmap[VIRT_LOWMEM].base, ram); @@ -617,8 +670,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 c0a2e0ab04..198da5ba3d 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -26,21 +26,21 @@ #include "qemu/units.h" #include "qemu/bitops.h" #include "qemu/datadir.h" +#include "qemu/cutils.h" #include "qemu/guest-random.h" #include "hw/clock.h" #include "hw/southbridge/piix.h" #include "hw/isa/superio.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "net/net.h" #include "hw/boards.h" #include "hw/i2c/smbus_eeprom.h" #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/pci/pci_bus.h" #include "qemu/log.h" -#include "hw/mips/bios.h" #include "hw/ide/pci.h" #include "hw/irq.h" #include "hw/loader.h" @@ -53,11 +53,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) @@ -70,6 +72,9 @@ #define RESET_ADDRESS 0x1fc00000ULL #define FLASH_SIZE 0x400000 +#define BIOS_SIZE (4 * MiB) + +#define PIIX4_PCI_DEVFN PCI_DEVFN(10, 0) typedef struct { MemoryRegion iomem; @@ -88,6 +93,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 +117,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 +131,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 +213,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 +473,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 +492,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 +613,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 +698,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 +716,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); } /* @@ -875,54 +782,7 @@ static void write_bootloader(uint8_t *base, uint64_t run_addr, * */ - /* Bus endianess is always reversed */ -#if TARGET_BIG_ENDIAN -#define cpu_to_gt32 cpu_to_le32 -#else -#define cpu_to_gt32 cpu_to_be32 -#endif - - /* 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 MEM-to-PCI0 mapping */ - /* 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 - - 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); + bl_setup_gt64120_jump_kernel((void **)&p, run_addr, kernel_entry); /* YAMON subroutines */ p = (uint32_t *) (base + 0x800); @@ -966,7 +826,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, @@ -992,15 +851,18 @@ 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) +static GString *rng_seed_hex_new(void) { - 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]); - } + return qemu_hexdump_line(NULL, rng_seed, sizeof(rng_seed), 0, 0); +} + +static void reinitialize_rng_seed(void *opaque) +{ + g_autoptr(GString) hex = rng_seed_hex_new(); + memcpy(opaque, hex->str, hex->len); } /* Kernel */ @@ -1009,25 +871,15 @@ 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); - uint8_t rng_seed[32]; - char rng_seed_hex[sizeof(rng_seed) * 2 + 1]; size_t rng_seed_prom_offset; -#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, &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", @@ -1037,19 +889,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 */ @@ -1090,7 +933,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); @@ -1105,14 +948,13 @@ 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); + { + g_autoptr(GString) hex = rng_seed_hex_new(); + prom_set(prom_buf, prom_index++, "%s", hex->str); + } prom_set(prom_buf, prom_index++, NULL); @@ -1140,6 +982,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; @@ -1157,11 +1024,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, @@ -1172,7 +1034,8 @@ static void create_cpu_without_cps(MachineState *ms, MaltaState *s, int i; for (i = 0; i < ms->smp.cpus; i++) { - cpu = mips_cpu_create_with_clock(ms->cpu_type, s->cpuclk); + cpu = mips_cpu_create_with_clock(ms->cpu_type, s->cpuclk, + TARGET_BIG_ENDIAN); /* Init internal devices */ cpu_mips_irq_init_cpu(cpu); @@ -1192,7 +1055,9 @@ 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_bool(OBJECT(&s->cps), "cpu-big-endian", + TARGET_BIG_ENDIAN, &error_abort); + 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); @@ -1236,7 +1101,6 @@ void mips_malta_init(MachineState *machine) I2CBus *smbus; DriveInfo *dinfo; int fl_idx = 0; - int be; MaltaState *s; PCIDevice *piix4; DeviceState *dev; @@ -1273,12 +1137,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 */ @@ -1290,18 +1148,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; @@ -1318,20 +1171,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. */ @@ -1391,18 +1232,16 @@ 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 */ - piix4 = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(10, 0), true, - TYPE_PIIX4_PCI_DEVICE); + 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")); diff --git a/hw/mips/meson.build b/hw/mips/meson.build index dd0101ad4d..fcbee53bb3 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -1,15 +1,15 @@ 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')) +common_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')) -mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: [files('boston.c'), fdt]) +mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c')) endif hw_arch += {'mips': mips_ss} 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..5f4835a38d 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 "hw/char/serial-mm.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); @@ -158,7 +160,8 @@ mips_mipssim_init(MachineState *machine) #endif /* Init CPUs. */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk); + cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk, + TARGET_BIG_ENDIAN); env = &cpu->env; reset_info = g_new0(ResetData, 1); @@ -204,7 +207,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 +228,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..1f1baa5dde 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 @@ -69,7 +74,6 @@ config IVSHMEM_DEVICE config ECCMEMCTL bool - select ECC config IMX bool @@ -77,6 +81,9 @@ config IMX select SSI select USB_EHCI_SYSBUS +config STM32_RCC + bool + config STM32F2XX_SYSCFG bool @@ -86,6 +93,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 +178,9 @@ config SIFIVE_TEST config SIFIVE_E_PRCI bool +config SIFIVE_E_AON + bool + config SIFIVE_U_OTP bool @@ -174,4 +193,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..a40d5072de 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() @@ -134,7 +134,7 @@ static void a9_scu_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, a9_scu_properties); dc->vmsd = &vmstate_a9_scu; - dc->reset = a9_scu_reset; + device_class_set_legacy_reset(dc, a9_scu_reset); dc->realize = a9_scu_realize; } 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..022f63ddf3 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), @@ -262,7 +262,7 @@ static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_cpucfg_reset; + device_class_set_legacy_reset(dc, allwinner_cpucfg_reset); dc->vmsd = &allwinner_cpucfg_vmstate; } diff --git a/hw/misc/allwinner-h3-ccu.c b/hw/misc/allwinner-h3-ccu.c index 18d1074545..92e579a991 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() } @@ -222,7 +222,7 @@ static void allwinner_h3_ccu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_h3_ccu_reset; + device_class_set_legacy_reset(dc, allwinner_h3_ccu_reset); dc->vmsd = &allwinner_h3_ccu_vmstate; } diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index 1d37cf422c..eeab0dc5d2 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), @@ -336,7 +336,7 @@ static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_h3_dramc_reset; + device_class_set_legacy_reset(dc, allwinner_h3_dramc_reset); dc->vmsd = &allwinner_h3_dramc_vmstate; dc->realize = allwinner_h3_dramc_realize; device_class_set_props(dc, allwinner_h3_dramc_properties); diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c index 1d07efa880..40059e8cb0 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() } @@ -120,7 +120,7 @@ static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_h3_sysctrl_reset; + device_class_set_legacy_reset(dc, allwinner_h3_sysctrl_reset); dc->vmsd = &allwinner_h3_sysctrl_vmstate; } diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c new file mode 100644 index 0000000000..005a15b2da --- /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); + + device_class_set_legacy_reset(dc, 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..3ae4890037 --- /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); + + device_class_set_legacy_reset(dc, 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..19ff17d24a 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), @@ -148,7 +148,7 @@ static void allwinner_sid_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_sid_reset; + device_class_set_legacy_reset(dc, allwinner_sid_reset); dc->vmsd = &allwinner_sid_vmstate; device_class_set_props(dc, allwinner_sid_properties); } diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c new file mode 100644 index 0000000000..a20b0b4c5c --- /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); + + device_class_set_legacy_reset(dc, 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 5f9c742e50..5b766277d6 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -34,6 +34,7 @@ #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" @@ -144,7 +145,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, s->data_pos = 0; } -static struct AppleSMCData *applesmc_find_key(AppleSMCState *s) +static const struct AppleSMCData *applesmc_find_key(AppleSMCState *s) { struct AppleSMCData *d; @@ -160,7 +161,7 @@ static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { AppleSMCState *s = opaque; - struct AppleSMCData *d; + const struct AppleSMCData *d; smc_debug("DATA received: 0x%02x\n", (uint8_t)val); switch (s->cmd) { @@ -268,22 +269,10 @@ static void applesmc_add_key(AppleSMCState *s, const char *key, static void qdev_applesmc_isa_reset(DeviceState *dev) { AppleSMCState *s = APPLE_SMC(dev); - struct AppleSMCData *d, *next; - /* Remove existing entries */ - QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { - QLIST_REMOVE(d, node); - } s->status = 0x00; s->status_1e = 0x00; s->last_ret = 0x00; - - applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); - applesmc_add_key(s, "OSK0", 32, s->osk); - applesmc_add_key(s, "OSK1", 32, s->osk + 32); - applesmc_add_key(s, "NATJ", 1, "\0"); - applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); } static const MemoryRegionOps applesmc_data_io_ops = { @@ -341,7 +330,24 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) } QLIST_INIT(&s->data_def); - qdev_applesmc_isa_reset(dev); + applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); + applesmc_add_key(s, "OSK0", 32, s->osk); + applesmc_add_key(s, "OSK1", 32, s->osk + 32); + applesmc_add_key(s, "NATJ", 1, "\0"); + applesmc_add_key(s, "MSSP", 1, "\0"); + applesmc_add_key(s, "MSSD", 1, "\0x3"); +} + +static void applesmc_unrealize(DeviceState *dev) +{ + AppleSMCState *s = APPLE_SMC(dev); + struct AppleSMCData *d, *next; + + /* Remove existing entries */ + QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { + QLIST_REMOVE(d, node); + g_free(d); + } } static Property applesmc_isa_properties[] = { @@ -376,7 +382,8 @@ static void qdev_applesmc_class_init(ObjectClass *klass, void *data) AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); dc->realize = applesmc_isa_realize; - dc->reset = qdev_applesmc_isa_reset; + dc->unrealize = applesmc_unrealize; + device_class_set_legacy_reset(dc, 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; diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 75c3eb8982..1902ebd3bc 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), @@ -184,7 +184,7 @@ static void l2x0_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_l2x0; device_class_set_props(dc, l2x0_properties); - dc->reset = l2x0_priv_reset; + device_class_set_legacy_reset(dc, l2x0_priv_reset); } static const TypeInfo l2x0_info = { diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 42d4693854..9c4dce350a 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; } } } @@ -640,7 +640,7 @@ static void arm_sysctl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = arm_sysctl_realize; - dc->reset = arm_sysctl_reset; + device_class_set_legacy_reset(dc, arm_sysctl_reset); dc->vmsd = &vmstate_arm_sysctl; device_class_set_props(dc, arm_sysctl_properties); } diff --git a/hw/misc/armsse-cpu-pwrctrl.c b/hw/misc/armsse-cpu-pwrctrl.c index 42fc38879f..2d3a0ac29c 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() }, @@ -129,7 +129,7 @@ static void pwrctrl_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pwrctrl_reset; + device_class_set_legacy_reset(dc, pwrctrl_reset); dc->vmsd = &pwrctrl_vmstate; } diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c index 0be7f0fc87..91c49108b0 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() @@ -180,7 +180,7 @@ static void armsse_mhu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = armsse_mhu_reset; + device_class_set_legacy_reset(dc, armsse_mhu_reset); dc->vmsd = &armsse_mhu_vmstate; } diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 69175e972d..bc1d66ad80 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -1,6 +1,7 @@ /* * ASPEED Hash and Crypto Engine * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (C) 2021 IBM Corp. * * Joel Stanley @@ -68,15 +69,15 @@ static const struct { uint32_t mask; - QCryptoHashAlgorithm algo; + QCryptoHashAlgo algo; } hash_algo_map[] = { - { HASH_ALGO_MD5, QCRYPTO_HASH_ALG_MD5 }, - { HASH_ALGO_SHA1, QCRYPTO_HASH_ALG_SHA1 }, - { HASH_ALGO_SHA224, QCRYPTO_HASH_ALG_SHA224 }, - { HASH_ALGO_SHA256, QCRYPTO_HASH_ALG_SHA256 }, - { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA512, QCRYPTO_HASH_ALG_SHA512 }, - { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA384, QCRYPTO_HASH_ALG_SHA384 }, - { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA256, QCRYPTO_HASH_ALG_SHA256 }, + { HASH_ALGO_MD5, QCRYPTO_HASH_ALGO_MD5 }, + { HASH_ALGO_SHA1, QCRYPTO_HASH_ALGO_SHA1 }, + { HASH_ALGO_SHA224, QCRYPTO_HASH_ALGO_SHA224 }, + { HASH_ALGO_SHA256, QCRYPTO_HASH_ALGO_SHA256 }, + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA512, QCRYPTO_HASH_ALGO_SHA512 }, + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA384, QCRYPTO_HASH_ALGO_SHA384 }, + { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA256, QCRYPTO_HASH_ALGO_SHA256 }, }; static int hash_algo_lookup(uint32_t reg) @@ -151,48 +152,28 @@ static int reconstruct_iov(AspeedHACEState *s, struct iovec *iov, int id, return iov_count; } -/** - * Generate iov for accumulative mode. - * - * @param s aspeed hace state object - * @param iov iov of the current request - * @param id index of the current iov - * @param req_len length of the current request - * - * @return count of iov - */ -static int gen_acc_mode_iov(AspeedHACEState *s, struct iovec *iov, int id, - hwaddr *req_len) -{ - uint32_t pad_offset; - uint32_t total_msg_len; - s->total_req_len += *req_len; - - if (has_padding(s, &iov[id], *req_len, &total_msg_len, &pad_offset)) { - if (s->iov_count) { - return reconstruct_iov(s, iov, id, &pad_offset); - } - - *req_len -= s->total_req_len - total_msg_len; - s->total_req_len = 0; - iov[id].iov_len = *req_len; - } else { - s->iov_cache[s->iov_count].iov_base = iov->iov_base; - s->iov_cache[s->iov_count].iov_len = *req_len; - ++s->iov_count; - } - - return id + 1; -} - static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { struct iovec iov[ASPEED_HACE_MAX_SG]; + uint32_t total_msg_len; + uint32_t pad_offset; g_autofree uint8_t *digest_buf = NULL; size_t digest_len = 0; - int niov = 0; + bool sg_acc_mode_final_request = false; int i; + void *haddr; + Error *local_err = NULL; + + if (acc_mode && s->hash_ctx == NULL) { + s->hash_ctx = qcrypto_hash_new(algo, &local_err); + if (s->hash_ctx == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash failed : %s", + error_get_pretty(local_err)); + error_free(local_err); + return; + } + } if (sg_mode) { uint32_t len = 0; @@ -217,12 +198,24 @@ 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); + s->total_req_len += plen; + if (has_padding(s, &iov[i], plen, &total_msg_len, + &pad_offset)) { + /* Padding being present indicates the final request */ + sg_acc_mode_final_request = true; + iov[i].iov_len = pad_offset; + } else { + iov[i].iov_len = plen; + } } else { iov[i].iov_len = plen; } @@ -230,10 +223,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) { @@ -243,21 +240,42 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, * required to check whether cache is empty. If no, we should * combine cached iov and the current iov. */ - uint32_t total_msg_len; - uint32_t pad_offset; s->total_req_len += len; if (has_padding(s, iov, len, &total_msg_len, &pad_offset)) { - niov = reconstruct_iov(s, iov, 0, &pad_offset); + i = reconstruct_iov(s, iov, 0, &pad_offset); } } } - if (niov) { - i = niov; - } + if (acc_mode) { + if (qcrypto_hash_updatev(s->hash_ctx, iov, i, &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash update failed : %s", + error_get_pretty(local_err)); + error_free(local_err); + return; + } - if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf, &digest_len, NULL) < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); + if (sg_acc_mode_final_request) { + if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf, + &digest_len, &local_err)) { + qemu_log_mask(LOG_GUEST_ERROR, + "qcrypto hash finalize failed : %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + } + + qcrypto_hash_free(s->hash_ctx); + + s->hash_ctx = NULL; + s->iov_count = 0; + s->total_req_len = 0; + } + } else if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf, + &digest_len, &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash bytesv failed : %s", + error_get_pretty(local_err)); + error_free(local_err); return; } @@ -388,6 +406,11 @@ static void aspeed_hace_reset(DeviceState *dev) { struct AspeedHACEState *s = ASPEED_HACE(dev); + if (s->hash_ctx != NULL) { + qcrypto_hash_free(s->hash_ctx); + s->hash_ctx = NULL; + } + memset(s->regs, 0, sizeof(s->regs)); s->iov_count = 0; s->total_req_len = 0; @@ -424,7 +447,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), @@ -437,7 +460,7 @@ static void aspeed_hace_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_hace_realize; - dc->reset = aspeed_hace_reset; + device_class_set_legacy_reset(dc, aspeed_hace_reset); device_class_set_props(dc, aspeed_hace_properties); dc->vmsd = &vmstate_aspeed_hace; } diff --git a/hw/misc/aspeed_i3c.c b/hw/misc/aspeed_i3c.c index f54f5da522..371ee7dba8 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; } @@ -334,7 +334,7 @@ static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data) dc->desc = "Aspeed I3C Device"; dc->realize = aspeed_i3c_device_realize; - dc->reset = aspeed_i3c_device_reset; + device_class_set_legacy_reset(dc, aspeed_i3c_device_reset); device_class_set_props(dc, aspeed_i3c_device_properties); } @@ -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), @@ -362,7 +362,7 @@ static void aspeed_i3c_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_i3c_realize; - dc->reset = aspeed_i3c_reset; + device_class_set_legacy_reset(dc, aspeed_i3c_reset); dc->desc = "Aspeed I3C Controller"; dc->vmsd = &vmstate_aspeed_i3c; } diff --git a/hw/misc/aspeed_lpc.c b/hw/misc/aspeed_lpc.c index 2dddb27c35..f2d4ca6f43 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(), @@ -464,7 +464,7 @@ static void aspeed_lpc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_lpc_realize; - dc->reset = aspeed_lpc_reset; + device_class_set_legacy_reset(dc, aspeed_lpc_reset); dc->desc = "Aspeed LPC Controller", dc->vmsd = &vmstate_aspeed_lpc; device_class_set_props(dc, aspeed_lpc_properties); diff --git a/hw/misc/aspeed_peci.c b/hw/misc/aspeed_peci.c index 93cc672e96..9025b35f83 100644 --- a/hw/misc/aspeed_peci.c +++ b/hw/misc/aspeed_peci.c @@ -135,7 +135,7 @@ 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; + device_class_set_legacy_reset(dc, aspeed_peci_reset); dc->desc = "Aspeed PECI Controller"; } diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index c6f328e3be..f5eb2a0e37 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -130,7 +130,7 @@ 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(), } @@ -147,7 +147,7 @@ static void aspeed_sbc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_sbc_realize; - dc->reset = aspeed_sbc_reset; + device_class_set_legacy_reset(dc, aspeed_sbc_reset); dc->vmsd = &vmstate_aspeed_sbc; device_class_set_props(dc, aspeed_sbc_properties); } diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 8335364906..2c919349cf 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -134,6 +134,48 @@ #define AST2600_CLK TO_REG(0x40) +#define AST2700_SILICON_REV TO_REG(0x00) +#define AST2700_HW_STRAP1 TO_REG(0x10) +#define AST2700_HW_STRAP1_CLR TO_REG(0x14) +#define AST2700_HW_STRAP1_LOCK TO_REG(0x20) +#define AST2700_HW_STRAP1_SEC1 TO_REG(0x24) +#define AST2700_HW_STRAP1_SEC2 TO_REG(0x28) +#define AST2700_HW_STRAP1_SEC3 TO_REG(0x2C) + +#define AST2700_SCU_CLK_SEL_1 TO_REG(0x280) +#define AST2700_SCU_HPLL_PARAM TO_REG(0x300) +#define AST2700_SCU_HPLL_EXT_PARAM TO_REG(0x304) +#define AST2700_SCU_DPLL_PARAM TO_REG(0x308) +#define AST2700_SCU_DPLL_EXT_PARAM TO_REG(0x30c) +#define AST2700_SCU_MPLL_PARAM TO_REG(0x310) +#define AST2700_SCU_MPLL_EXT_PARAM TO_REG(0x314) +#define AST2700_SCU_D1CLK_PARAM TO_REG(0x320) +#define AST2700_SCU_D2CLK_PARAM TO_REG(0x330) +#define AST2700_SCU_CRT1CLK_PARAM TO_REG(0x340) +#define AST2700_SCU_CRT2CLK_PARAM TO_REG(0x350) +#define AST2700_SCU_MPHYCLK_PARAM TO_REG(0x360) +#define AST2700_SCU_FREQ_CNTR TO_REG(0x3b0) +#define AST2700_SCU_CPU_SCRATCH_0 TO_REG(0x780) +#define AST2700_SCU_CPU_SCRATCH_1 TO_REG(0x784) + +#define AST2700_SCUIO_CLK_STOP_CTL_1 TO_REG(0x240) +#define AST2700_SCUIO_CLK_STOP_CLR_1 TO_REG(0x244) +#define AST2700_SCUIO_CLK_STOP_CTL_2 TO_REG(0x260) +#define AST2700_SCUIO_CLK_STOP_CLR_2 TO_REG(0x264) +#define AST2700_SCUIO_CLK_SEL_1 TO_REG(0x280) +#define AST2700_SCUIO_CLK_SEL_2 TO_REG(0x284) +#define AST2700_SCUIO_HPLL_PARAM TO_REG(0x300) +#define AST2700_SCUIO_HPLL_EXT_PARAM TO_REG(0x304) +#define AST2700_SCUIO_APLL_PARAM TO_REG(0x310) +#define AST2700_SCUIO_APLL_EXT_PARAM TO_REG(0x314) +#define AST2700_SCUIO_DPLL_PARAM TO_REG(0x320) +#define AST2700_SCUIO_DPLL_EXT_PARAM TO_REG(0x324) +#define AST2700_SCUIO_DPLL_PARAM_READ TO_REG(0x328) +#define AST2700_SCUIO_DPLL_EXT_PARAM_READ TO_REG(0x32c) +#define AST2700_SCUIO_UARTCLK_GEN TO_REG(0x330) +#define AST2700_SCUIO_HUARTCLK_GEN TO_REG(0x334) +#define AST2700_SCUIO_CLK_DUTY_MEAS_RST TO_REG(0x388) + #define SCU_IO_REGION_SIZE 0x1000 static const uint32_t ast2400_a0_resets[ASPEED_SCU_NR_REGS] = { @@ -244,6 +286,25 @@ static uint32_t aspeed_1030_scu_get_apb_freq(AspeedSCUState *s) / asc->apb_divider; } +static uint32_t aspeed_2700_scu_get_apb_freq(AspeedSCUState *s) +{ + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); + uint32_t hpll = asc->calc_hpll(s, s->regs[AST2700_SCU_HPLL_PARAM]); + + return hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[AST2700_SCU_CLK_SEL_1]) + 1) + / asc->apb_divider; +} + +static uint32_t aspeed_2700_scuio_get_apb_freq(AspeedSCUState *s) +{ + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s); + uint32_t hpll = asc->calc_hpll(s, s->regs[AST2700_SCUIO_HPLL_PARAM]); + + return hpll / + (SCUIO_AST2700_CLK_GET_PCLK_DIV(s->regs[AST2700_SCUIO_CLK_SEL_1]) + 1) + / asc->apb_divider; +} + static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); @@ -258,7 +319,8 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) switch (reg) { case RNG_DATA: - /* On hardware, RNG_DATA works regardless of + /* + * On hardware, RNG_DATA works regardless of * the state of the enable bit in RNG_CTRL */ s->regs[RNG_DATA] = aspeed_scu_get_random(); @@ -494,6 +556,9 @@ static uint32_t aspeed_silicon_revs[] = { AST2600_A3_SILICON_REV, AST1030_A0_SILICON_REV, AST1030_A1_SILICON_REV, + AST2700_A0_SILICON_REV, + AST2720_A0_SILICON_REV, + AST2750_A0_SILICON_REV, }; bool is_supported_silicon_rev(uint32_t silicon_rev) @@ -531,7 +596,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() } @@ -549,7 +614,7 @@ static void aspeed_scu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_scu_realize; - dc->reset = aspeed_scu_reset; + device_class_set_legacy_reset(dc, aspeed_scu_reset); dc->desc = "ASPEED System Control Unit"; dc->vmsd = &vmstate_aspeed_scu; device_class_set_props(dc, aspeed_scu_properties); @@ -766,7 +831,7 @@ static void aspeed_2600_scu_class_init(ObjectClass *klass, void *data) AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); dc->desc = "ASPEED 2600 System Control Unit"; - dc->reset = aspeed_ast2600_scu_reset; + device_class_set_legacy_reset(dc, aspeed_ast2600_scu_reset); asc->resets = ast2600_a3_resets; asc->calc_hpll = aspeed_2600_scu_calc_hpll; asc->get_apb = aspeed_2600_scu_get_apb_freq; @@ -783,6 +848,243 @@ static const TypeInfo aspeed_2600_scu_info = { .class_init = aspeed_2600_scu_class_init, }; +static uint64_t aspeed_ast2700_scu_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_aspeed_ast2700_scu_read(offset, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_ast2700_scu_write(void *opaque, hwaddr offset, + uint64_t data64, unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + /* Truncate here so bitwise operations below behave as expected */ + uint32_t data = data64; + + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_aspeed_ast2700_scu_write(offset, size, data); + + switch (reg) { + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + s->regs[reg] = data; +} + +static const MemoryRegionOps aspeed_ast2700_scu_ops = { + .read = aspeed_ast2700_scu_read, + .write = aspeed_ast2700_scu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .valid.unaligned = false, +}; + +static const uint32_t ast2700_a0_resets[ASPEED_AST2700_SCU_NR_REGS] = { + [AST2700_SILICON_REV] = AST2700_A0_SILICON_REV, + [AST2700_HW_STRAP1] = 0x00000800, + [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, + [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, + [AST2700_HW_STRAP1_SEC1] = 0x000000FF, + [AST2700_HW_STRAP1_SEC2] = 0x00000000, + [AST2700_HW_STRAP1_SEC3] = 0x1000408F, + [AST2700_SCU_HPLL_PARAM] = 0x0000009f, + [AST2700_SCU_HPLL_EXT_PARAM] = 0x8000004f, + [AST2700_SCU_DPLL_PARAM] = 0x0080009f, + [AST2700_SCU_DPLL_EXT_PARAM] = 0x8000004f, + [AST2700_SCU_MPLL_PARAM] = 0x00000040, + [AST2700_SCU_MPLL_EXT_PARAM] = 0x80000000, + [AST2700_SCU_D1CLK_PARAM] = 0x00050002, + [AST2700_SCU_D2CLK_PARAM] = 0x00050002, + [AST2700_SCU_CRT1CLK_PARAM] = 0x00050002, + [AST2700_SCU_CRT2CLK_PARAM] = 0x00050002, + [AST2700_SCU_MPHYCLK_PARAM] = 0x0000004c, + [AST2700_SCU_FREQ_CNTR] = 0x000375eb, + [AST2700_SCU_CPU_SCRATCH_0] = 0x00000000, + [AST2700_SCU_CPU_SCRATCH_1] = 0x00000004, +}; + +static void aspeed_ast2700_scu_reset(DeviceState *dev) +{ + AspeedSCUState *s = ASPEED_SCU(dev); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); + + memcpy(s->regs, asc->resets, asc->nr_regs * 4); +} + +static void aspeed_2700_scu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); + + dc->desc = "ASPEED 2700 System Control Unit"; + device_class_set_legacy_reset(dc, aspeed_ast2700_scu_reset); + asc->resets = ast2700_a0_resets; + asc->calc_hpll = aspeed_2600_scu_calc_hpll; + asc->get_apb = aspeed_2700_scu_get_apb_freq; + asc->apb_divider = 4; + asc->nr_regs = ASPEED_AST2700_SCU_NR_REGS; + asc->clkin_25Mhz = true; + asc->ops = &aspeed_ast2700_scu_ops; +} + +static uint64_t aspeed_ast2700_scuio_read(void *opaque, hwaddr offset, + unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return 0; + } + + switch (reg) { + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled read at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_aspeed_ast2700_scuio_read(offset, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_ast2700_scuio_write(void *opaque, hwaddr offset, + uint64_t data64, unsigned size) +{ + AspeedSCUState *s = ASPEED_SCU(opaque); + int reg = TO_REG(offset); + /* Truncate here so bitwise operations below behave as expected */ + uint32_t data = data64; + bool updated = false; + + if (reg >= ASPEED_AST2700_SCU_NR_REGS) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + return; + } + + trace_aspeed_ast2700_scuio_write(offset, size, data); + + switch (reg) { + case AST2700_SCUIO_CLK_STOP_CTL_1: + case AST2700_SCUIO_CLK_STOP_CTL_2: + s->regs[reg] |= data; + updated = true; + break; + case AST2700_SCUIO_CLK_STOP_CLR_1: + case AST2700_SCUIO_CLK_STOP_CLR_2: + s->regs[reg - 1] ^= data; + updated = true; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + if (!updated) { + s->regs[reg] = data; + } +} + +static const MemoryRegionOps aspeed_ast2700_scuio_ops = { + .read = aspeed_ast2700_scuio_read, + .write = aspeed_ast2700_scuio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .valid.unaligned = false, +}; + +static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { + [AST2700_SILICON_REV] = 0x06000003, + [AST2700_HW_STRAP1] = 0x00000504, + [AST2700_HW_STRAP1_CLR] = 0xFFF0FFF0, + [AST2700_HW_STRAP1_LOCK] = 0x00000FFF, + [AST2700_HW_STRAP1_SEC1] = 0x000000FF, + [AST2700_HW_STRAP1_SEC2] = 0x00000000, + [AST2700_HW_STRAP1_SEC3] = 0x1000408F, + [AST2700_SCUIO_CLK_STOP_CTL_1] = 0xffff8400, + [AST2700_SCUIO_CLK_STOP_CTL_2] = 0x00005f30, + [AST2700_SCUIO_CLK_SEL_1] = 0x86900000, + [AST2700_SCUIO_CLK_SEL_2] = 0x00400000, + [AST2700_SCUIO_HPLL_PARAM] = 0x10000027, + [AST2700_SCUIO_HPLL_EXT_PARAM] = 0x80000014, + [AST2700_SCUIO_APLL_PARAM] = 0x1000001f, + [AST2700_SCUIO_APLL_EXT_PARAM] = 0x8000000f, + [AST2700_SCUIO_DPLL_PARAM] = 0x106e42ce, + [AST2700_SCUIO_DPLL_EXT_PARAM] = 0x80000167, + [AST2700_SCUIO_DPLL_PARAM_READ] = 0x106e42ce, + [AST2700_SCUIO_DPLL_EXT_PARAM_READ] = 0x80000167, + [AST2700_SCUIO_UARTCLK_GEN] = 0x00014506, + [AST2700_SCUIO_HUARTCLK_GEN] = 0x000145c0, + [AST2700_SCUIO_CLK_DUTY_MEAS_RST] = 0x0c9100d2, +}; + +static void aspeed_2700_scuio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); + + dc->desc = "ASPEED 2700 System Control Unit I/O"; + device_class_set_legacy_reset(dc, aspeed_ast2700_scu_reset); + asc->resets = ast2700_a0_resets_io; + asc->calc_hpll = aspeed_2600_scu_calc_hpll; + asc->get_apb = aspeed_2700_scuio_get_apb_freq; + asc->apb_divider = 2; + asc->nr_regs = ASPEED_AST2700_SCU_NR_REGS; + asc->clkin_25Mhz = true; + asc->ops = &aspeed_ast2700_scuio_ops; +} + +static const TypeInfo aspeed_2700_scu_info = { + .name = TYPE_ASPEED_2700_SCU, + .parent = TYPE_ASPEED_SCU, + .instance_size = sizeof(AspeedSCUState), + .class_init = aspeed_2700_scu_class_init, +}; + +static const TypeInfo aspeed_2700_scuio_info = { + .name = TYPE_ASPEED_2700_SCUIO, + .parent = TYPE_ASPEED_SCU, + .instance_size = sizeof(AspeedSCUState), + .class_init = aspeed_2700_scuio_class_init, +}; + static const uint32_t ast1030_a1_resets[ASPEED_AST2600_SCU_NR_REGS] = { [AST2600_SYS_RST_CTRL] = 0xFFC3FED8, [AST2600_SYS_RST_CTRL2] = 0x09FFFFFC, @@ -817,7 +1119,7 @@ static void aspeed_1030_scu_class_init(ObjectClass *klass, void *data) AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); dc->desc = "ASPEED 1030 System Control Unit"; - dc->reset = aspeed_ast1030_scu_reset; + device_class_set_legacy_reset(dc, aspeed_ast1030_scu_reset); asc->resets = ast1030_a1_resets; asc->calc_hpll = aspeed_2600_scu_calc_hpll; asc->get_apb = aspeed_1030_scu_get_apb_freq; @@ -841,6 +1143,8 @@ static void aspeed_scu_register_types(void) type_register_static(&aspeed_2500_scu_info); type_register_static(&aspeed_2600_scu_info); type_register_static(&aspeed_1030_scu_info); + type_register_static(&aspeed_2700_scu_info); + type_register_static(&aspeed_2700_scuio_info); } type_init(aspeed_scu_register_types); diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index d2a3931033..4bc9faf691 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" @@ -28,6 +27,7 @@ #define PROT_SOFTLOCKED 0x00 #define PROT_KEY_UNLOCK 0xFC600309 +#define PROT_2700_KEY_UNLOCK 0x1688A8A8 #define PROT_KEY_HARDLOCK 0xDEADDEAD /* AST2600 */ /* Configuration Register */ @@ -55,6 +55,46 @@ #define R_DRAM_TIME (0x8c / 4) #define R_ECC_ERR_INJECT (0xb4 / 4) +/* AST2700 Register */ +#define R_2700_PROT (0x00 / 4) +#define R_INT_STATUS (0x04 / 4) +#define R_INT_CLEAR (0x08 / 4) +#define R_INT_MASK (0x0c / 4) +#define R_MAIN_CONF (0x10 / 4) +#define R_MAIN_CONTROL (0x14 / 4) +#define R_MAIN_STATUS (0x18 / 4) +#define R_ERR_STATUS (0x1c / 4) +#define R_ECC_FAIL_STATUS (0x78 / 4) +#define R_ECC_FAIL_ADDR (0x7c / 4) +#define R_ECC_TESTING_CONTROL (0x80 / 4) +#define R_PROT_REGION_LOCK_STATUS (0x94 / 4) +#define R_TEST_FAIL_ADDR (0xd4 / 4) +#define R_TEST_FAIL_D0 (0xd8 / 4) +#define R_TEST_FAIL_D1 (0xdc / 4) +#define R_TEST_FAIL_D2 (0xe0 / 4) +#define R_TEST_FAIL_D3 (0xe4 / 4) +#define R_DBG_STATUS (0xf4 / 4) +#define R_PHY_INTERFACE_STATUS (0xf8 / 4) +#define R_GRAPHIC_MEM_BASE_ADDR (0x10c / 4) +#define R_PORT0_INTERFACE_MONITOR0 (0x240 / 4) +#define R_PORT0_INTERFACE_MONITOR1 (0x244 / 4) +#define R_PORT0_INTERFACE_MONITOR2 (0x248 / 4) +#define R_PORT1_INTERFACE_MONITOR0 (0x2c0 / 4) +#define R_PORT1_INTERFACE_MONITOR1 (0x2c4 / 4) +#define R_PORT1_INTERFACE_MONITOR2 (0x2c8 / 4) +#define R_PORT2_INTERFACE_MONITOR0 (0x340 / 4) +#define R_PORT2_INTERFACE_MONITOR1 (0x344 / 4) +#define R_PORT2_INTERFACE_MONITOR2 (0x348 / 4) +#define R_PORT3_INTERFACE_MONITOR0 (0x3c0 / 4) +#define R_PORT3_INTERFACE_MONITOR1 (0x3c4 / 4) +#define R_PORT3_INTERFACE_MONITOR2 (0x3c8 / 4) +#define R_PORT4_INTERFACE_MONITOR0 (0x440 / 4) +#define R_PORT4_INTERFACE_MONITOR1 (0x444 / 4) +#define R_PORT4_INTERFACE_MONITOR2 (0x448 / 4) +#define R_PORT5_INTERFACE_MONITOR0 (0x4c0 / 4) +#define R_PORT5_INTERFACE_MONITOR1 (0x4c4 / 4) +#define R_PORT5_INTERFACE_MONITOR2 (0x4c8 / 4) + /* * Configuration register Ox4 (for Aspeed AST2400 SOC) * @@ -77,10 +117,6 @@ #define ASPEED_SDMC_VGA_32MB 0x2 #define ASPEED_SDMC_VGA_64MB 0x3 #define ASPEED_SDMC_DRAM_SIZE(x) (x & 0x3) -#define ASPEED_SDMC_DRAM_64MB 0x0 -#define ASPEED_SDMC_DRAM_128MB 0x1 -#define ASPEED_SDMC_DRAM_256MB 0x2 -#define ASPEED_SDMC_DRAM_512MB 0x3 #define ASPEED_SDMC_READONLY_MASK \ (ASPEED_SDMC_RESERVED | ASPEED_SDMC_VGA_COMPAT | \ @@ -101,22 +137,24 @@ #define ASPEED_SDMC_CACHE_ENABLE (1 << 10) /* differs from AST2400 */ #define ASPEED_SDMC_DRAM_TYPE (1 << 4) /* differs from AST2400 */ -/* DRAM size definitions differs */ -#define ASPEED_SDMC_AST2500_128MB 0x0 -#define ASPEED_SDMC_AST2500_256MB 0x1 -#define ASPEED_SDMC_AST2500_512MB 0x2 -#define ASPEED_SDMC_AST2500_1024MB 0x3 - -#define ASPEED_SDMC_AST2600_256MB 0x0 -#define ASPEED_SDMC_AST2600_512MB 0x1 -#define ASPEED_SDMC_AST2600_1024MB 0x2 -#define ASPEED_SDMC_AST2600_2048MB 0x3 - #define ASPEED_SDMC_AST2500_READONLY_MASK \ (ASPEED_SDMC_HW_VERSION(0xf) | ASPEED_SDMC_CACHE_INITIAL_DONE | \ ASPEED_SDMC_AST2500_RESERVED | ASPEED_SDMC_VGA_COMPAT | \ ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB)) +/* + * Main Configuration register Ox10 (for Aspeed AST2700 SOC and higher) + * + */ +#define ASPEED_SDMC_AST2700_RESERVED 0xFFFF2082 /* 31:16, 13, 7, 1 */ +#define ASPEED_SDMC_AST2700_DATA_SCRAMBLE (1 << 8) +#define ASPEED_SDMC_AST2700_ECC_ENABLE (1 << 6) +#define ASPEED_SDMC_AST2700_PAGE_MATCHING_ENABLE (1 << 5) +#define ASPEED_SDMC_AST2700_DRAM_SIZE(x) ((x & 0x7) << 2) + +#define ASPEED_SDMC_AST2700_READONLY_MASK \ + (ASPEED_SDMC_AST2700_RESERVED) + static uint64_t aspeed_sdmc_read(void *opaque, hwaddr addr, unsigned size) { AspeedSDMCState *s = ASPEED_SDMC(opaque); @@ -232,7 +270,13 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) AspeedSDMCState *s = ASPEED_SDMC(dev); AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); - assert(asc->max_ram_size < 4 * GiB); /* 32-bit address bus */ + assert(asc->max_ram_size < 4 * GiB || asc->is_bus64bit); + + if (!s->ram_size) { + error_setg(errp, "RAM size is not set"); + return; + } + s->max_ram_size = asc->max_ram_size; memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sdmc_ops, s, @@ -242,9 +286,9 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_sdmc = { .name = "aspeed.sdmc", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSDMCState, ASPEED_SDMC_NR_REGS), VMSTATE_END_OF_LIST() } @@ -252,6 +296,7 @@ static const VMStateDescription vmstate_aspeed_sdmc = { static Property aspeed_sdmc_properties[] = { DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), + DEFINE_PROP_BOOL("unlocked", AspeedSDMCState, unlocked, false), DEFINE_PROP_END_OF_LIST(), }; @@ -259,7 +304,7 @@ static void aspeed_sdmc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_sdmc_realize; - dc->reset = aspeed_sdmc_reset; + device_class_set_legacy_reset(dc, aspeed_sdmc_reset); dc->desc = "ASPEED SDRAM Memory Controller"; dc->vmsd = &vmstate_aspeed_sdmc; device_class_set_props(dc, aspeed_sdmc_properties); @@ -312,7 +357,8 @@ static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg, uint32_t data) { if (reg == R_PROT) { - s->regs[reg] = (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; + s->regs[reg] = + (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; return; } @@ -370,7 +416,8 @@ static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg, uint32_t data) { if (reg == R_PROT) { - s->regs[reg] = (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; + s->regs[reg] = + (data == PROT_KEY_UNLOCK) ? PROT_UNLOCKED : PROT_SOFTLOCKED; return; } @@ -450,8 +497,9 @@ static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg, } if (s->regs[R_PROT] == PROT_HARDLOCKED) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: SDMC is locked until system reset!\n", - __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SDMC is locked until system reset!\n", + __func__); return; } @@ -513,12 +561,144 @@ static const TypeInfo aspeed_2600_sdmc_info = { .class_init = aspeed_2600_sdmc_class_init, }; +static void aspeed_2700_sdmc_reset(DeviceState *dev) +{ + AspeedSDMCState *s = ASPEED_SDMC(dev); + AspeedSDMCClass *asc = ASPEED_SDMC_GET_CLASS(s); + + memset(s->regs, 0, sizeof(s->regs)); + + /* Set ram size bit and defaults values */ + s->regs[R_MAIN_CONF] = asc->compute_conf(s, 0); + + if (s->unlocked) { + s->regs[R_2700_PROT] = PROT_UNLOCKED; + } +} + +static uint32_t aspeed_2700_sdmc_compute_conf(AspeedSDMCState *s, uint32_t data) +{ + uint32_t fixed_conf = ASPEED_SDMC_AST2700_PAGE_MATCHING_ENABLE | + ASPEED_SDMC_AST2700_DRAM_SIZE(aspeed_sdmc_get_ram_bits(s)); + + /* Make sure readonly bits are kept */ + data &= ~ASPEED_SDMC_AST2700_READONLY_MASK; + + return data | fixed_conf; +} + +static void aspeed_2700_sdmc_write(AspeedSDMCState *s, uint32_t reg, + uint32_t data) +{ + /* Unprotected registers */ + switch (reg) { + case R_INT_STATUS: + case R_INT_CLEAR: + case R_INT_MASK: + case R_ERR_STATUS: + case R_ECC_FAIL_STATUS: + case R_ECC_FAIL_ADDR: + case R_PROT_REGION_LOCK_STATUS: + case R_TEST_FAIL_ADDR: + case R_TEST_FAIL_D0: + case R_TEST_FAIL_D1: + case R_TEST_FAIL_D2: + case R_TEST_FAIL_D3: + case R_DBG_STATUS: + case R_PHY_INTERFACE_STATUS: + case R_GRAPHIC_MEM_BASE_ADDR: + case R_PORT0_INTERFACE_MONITOR0: + case R_PORT0_INTERFACE_MONITOR1: + case R_PORT0_INTERFACE_MONITOR2: + case R_PORT1_INTERFACE_MONITOR0: + case R_PORT1_INTERFACE_MONITOR1: + case R_PORT1_INTERFACE_MONITOR2: + case R_PORT2_INTERFACE_MONITOR0: + case R_PORT2_INTERFACE_MONITOR1: + case R_PORT2_INTERFACE_MONITOR2: + case R_PORT3_INTERFACE_MONITOR0: + case R_PORT3_INTERFACE_MONITOR1: + case R_PORT3_INTERFACE_MONITOR2: + case R_PORT4_INTERFACE_MONITOR0: + case R_PORT4_INTERFACE_MONITOR1: + case R_PORT4_INTERFACE_MONITOR2: + case R_PORT5_INTERFACE_MONITOR0: + case R_PORT5_INTERFACE_MONITOR1: + case R_PORT5_INTERFACE_MONITOR2: + s->regs[reg] = data; + return; + } + + if (s->regs[R_2700_PROT] == PROT_HARDLOCKED) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SDMC is locked until system reset!\n", + __func__); + return; + } + + if (reg != R_2700_PROT && s->regs[R_2700_PROT] == PROT_SOFTLOCKED) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SDMC is locked! (write to MCR%02x blocked)\n", + __func__, reg * 4); + return; + } + + switch (reg) { + case R_2700_PROT: + if (data == PROT_2700_KEY_UNLOCK) { + data = PROT_UNLOCKED; + } else if (data == PROT_KEY_HARDLOCK) { + data = PROT_HARDLOCKED; + } else { + data = PROT_SOFTLOCKED; + } + break; + case R_MAIN_CONF: + data = aspeed_2700_sdmc_compute_conf(s, data); + break; + case R_MAIN_STATUS: + /* Will never return 'busy'. */ + data &= ~PHY_BUSY_STATE; + break; + default: + break; + } + + s->regs[reg] = data; +} + +static const uint64_t + aspeed_2700_ram_sizes[] = { 256 * MiB, 512 * MiB, 1024 * MiB, + 2048 * MiB, 4096 * MiB, 8192 * MiB, 0}; + +static void aspeed_2700_sdmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); + + dc->desc = "ASPEED 2700 SDRAM Memory Controller"; + device_class_set_legacy_reset(dc, aspeed_2700_sdmc_reset); + + asc->is_bus64bit = true; + asc->max_ram_size = 8 * GiB; + asc->compute_conf = aspeed_2700_sdmc_compute_conf; + asc->write = aspeed_2700_sdmc_write; + asc->valid_ram_sizes = aspeed_2700_ram_sizes; +} + +static const TypeInfo aspeed_2700_sdmc_info = { + .name = TYPE_ASPEED_2700_SDMC, + .parent = TYPE_ASPEED_SDMC, + .class_init = aspeed_2700_sdmc_class_init, +}; + static void aspeed_sdmc_register_types(void) { type_register_static(&aspeed_sdmc_info); type_register_static(&aspeed_2400_sdmc_info); type_register_static(&aspeed_2500_sdmc_info); type_register_static(&aspeed_2600_sdmc_info); + type_register_static(&aspeed_2700_sdmc_info); } type_init(aspeed_sdmc_register_types); diff --git a/hw/misc/aspeed_sli.c b/hw/misc/aspeed_sli.c new file mode 100644 index 0000000000..fe720ead50 --- /dev/null +++ b/hw/misc/aspeed_sli.c @@ -0,0 +1,177 @@ +/* + * ASPEED SLI Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "hw/qdev-properties.h" +#include "hw/misc/aspeed_sli.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define SLI_REGION_SIZE 0x500 +#define TO_REG(addr) ((addr) >> 2) + +static uint64_t aspeed_sli_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + trace_aspeed_sli_read(addr, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_sli_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + trace_aspeed_sli_write(addr, size, data); + s->regs[reg] = data; +} + +static uint64_t aspeed_sliio_read(void *opaque, hwaddr addr, unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + trace_aspeed_sliio_read(addr, size, s->regs[reg]); + return s->regs[reg]; +} + +static void aspeed_sliio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AspeedSLIState *s = ASPEED_SLI(opaque); + int reg = TO_REG(addr); + + if (reg >= ARRAY_SIZE(s->regs)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return; + } + + trace_aspeed_sliio_write(addr, size, data); + s->regs[reg] = data; +} + +static const MemoryRegionOps aspeed_sli_ops = { + .read = aspeed_sli_read, + .write = aspeed_sli_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const MemoryRegionOps aspeed_sliio_ops = { + .read = aspeed_sliio_read, + .write = aspeed_sliio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void aspeed_sli_realize(DeviceState *dev, Error **errp) +{ + AspeedSLIState *s = ASPEED_SLI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sli_ops, s, + TYPE_ASPEED_SLI, SLI_REGION_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_sliio_realize(DeviceState *dev, Error **errp) +{ + AspeedSLIState *s = ASPEED_SLI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_sliio_ops, s, + TYPE_ASPEED_SLI, SLI_REGION_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void aspeed_sli_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Aspeed SLI Controller"; + dc->realize = aspeed_sli_realize; +} + +static const TypeInfo aspeed_sli_info = { + .name = TYPE_ASPEED_SLI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AspeedSLIState), + .class_init = aspeed_sli_class_init, + .abstract = true, +}; + +static void aspeed_2700_sli_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AST2700 SLI Controller"; +} + +static void aspeed_2700_sliio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AST2700 I/O SLI Controller"; + dc->realize = aspeed_sliio_realize; +} + +static const TypeInfo aspeed_2700_sli_info = { + .name = TYPE_ASPEED_2700_SLI, + .parent = TYPE_ASPEED_SLI, + .class_init = aspeed_2700_sli_class_init, +}; + +static const TypeInfo aspeed_2700_sliio_info = { + .name = TYPE_ASPEED_2700_SLIIO, + .parent = TYPE_ASPEED_SLI, + .class_init = aspeed_2700_sliio_class_init, +}; + +static void aspeed_sli_register_types(void) +{ + type_register_static(&aspeed_sli_info); + type_register_static(&aspeed_2700_sli_info); + type_register_static(&aspeed_2700_sliio_info); +} + +type_init(aspeed_sli_register_types); diff --git a/hw/misc/aspeed_xdma.c b/hw/misc/aspeed_xdma.c index 1c21577c98..1dd32f72f4 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(), }, @@ -222,7 +222,7 @@ static void aspeed_xdma_class_init(ObjectClass *classp, void *data) DeviceClass *dc = DEVICE_CLASS(classp); dc->realize = aspeed_xdma_realize; - dc->reset = aspeed_xdma_reset; + device_class_set_legacy_reset(dc, aspeed_xdma_reset); dc->vmsd = &aspeed_xdma_vmstate; } 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/avr_power.c b/hw/misc/avr_power.c index a5412f2cfe..ac7b96f53e 100644 --- a/hw/misc/avr_power.c +++ b/hw/misc/avr_power.c @@ -94,7 +94,7 @@ static void avr_mask_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = avr_mask_reset; + device_class_set_legacy_reset(dc, avr_mask_reset); } static const TypeInfo avr_mask_info = { 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..63e1045abf 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() } @@ -135,7 +135,7 @@ static void pll_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pll_reset; + device_class_set_legacy_reset(dc, pll_reset); dc->vmsd = &pll_vmstate; } @@ -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() } @@ -239,7 +239,7 @@ static void pll_channel_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pll_channel_reset; + device_class_set_legacy_reset(dc, pll_channel_reset); dc->vmsd = &pll_channel_vmstate; } @@ -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() @@ -360,7 +360,7 @@ static void clock_mux_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = clock_mux_reset; + device_class_set_legacy_reset(dc, clock_mux_reset); dc->vmsd = &clock_mux_vmstate; } @@ -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() } @@ -788,7 +788,7 @@ static void cprman_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = cprman_realize; - dc->reset = cprman_reset; + device_class_set_legacy_reset(dc, cprman_reset); dc->vmsd = &cprman_vmstate; device_class_set_props(dc, cprman_properties); } diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 1e4e061bc1..ed6dbea191 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), @@ -319,7 +319,7 @@ static void bcm2835_mbox_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = bcm2835_mbox_realize; - dc->reset = bcm2835_mbox_reset; + device_class_set_legacy_reset(dc, bcm2835_mbox_reset); dc->vmsd = &vmstate_bcm2835_mbox; } diff --git a/hw/misc/bcm2835_mphi.c b/hw/misc/bcm2835_mphi.c index 0428e10ba5..7309cf22fc 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), @@ -171,7 +171,7 @@ static void mphi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mphi_realize; - dc->reset = mphi_reset; + device_class_set_legacy_reset(dc, mphi_reset); dc->vmsd = &vmstate_mphi_state; } diff --git a/hw/misc/bcm2835_powermgt.c b/hw/misc/bcm2835_powermgt.c index 976f3d34e5..d88689a0a5 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), @@ -140,7 +140,7 @@ static void bcm2835_powermgt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = bcm2835_powermgt_reset; + device_class_set_legacy_reset(dc, bcm2835_powermgt_reset); dc->vmsd = &vmstate_bcm2835_powermgt; } diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 890ae7bae5..8ca3128f29 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -12,22 +12,20 @@ #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 */ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) { - uint32_t tag; - uint32_t bufsize; uint32_t tot_len; - size_t resplen; - uint32_t tmp; - int n; - uint32_t offset, length, color; /* * Copy the current state of the framebuffer config; we will update @@ -46,95 +44,109 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* @(addr + 4) : Buffer response code */ value = s->addr + 8; while (value + 8 <= s->addr + tot_len) { - tag = ldl_le_phys(&s->dma_as, value); - bufsize = ldl_le_phys(&s->dma_as, value + 4); + uint32_t tag = ldl_le_phys(&s->dma_as, value); + uint32_t bufsize = ldl_le_phys(&s->dma_as, value + 4); /* @(value + 8) : Request/response indicator */ - resplen = 0; + size_t 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 */ - /* Assume that whatever device they asked for exists, - * and we'll just claim we set it to the desired 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. */ - tmp = ldl_le_phys(&s->dma_as, value + 16); - stl_le_phys(&s->dma_as, value + 16, (tmp & 1)); + uint32_t state = ldl_le_phys(&s->dma_as, value + 16); + stl_le_phys(&s->dma_as, value + 16, (state & 1)); resplen = 8; break; + } /* 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,148 +155,266 @@ 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 */ - offset = ldl_le_phys(&s->dma_as, value + 12); - length = ldl_le_phys(&s->dma_as, value + 16); - n = 0; - while (n < length - offset) { - color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2)); - stl_le_phys(&s->dma_as, - s->fbdev->vcram_base + ((offset + n) << 2), color); - n++; + case RPI_FWREQ_FRAMEBUFFER_SET_PALETTE: + { + uint32_t offset = ldl_le_phys(&s->dma_as, value + 12); + uint32_t length = ldl_le_phys(&s->dma_as, value + 16); + int resp; + + if (offset > 255 || length < 1 || length > 256) { + resp = 1; /* invalid request */ + } else { + for (uint32_t e = 0; e < length; e++) { + uint32_t color = ldl_le_phys(&s->dma_as, value + 20 + (e << 2)); + stl_le_phys(&s->dma_as, + s->fbdev->vcram_base + ((offset + e) << 2), color); + } + resp = 0; } - stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 12, resp); resplen = 4; break; - case 0x00040013: /* Get number of displays */ + } + case RPI_FWREQ_FRAMEBUFFER_GET_NUM_DISPLAYS: stl_le_phys(&s->dma_as, value + 12, 1); resplen = 4; break; - case 0x00060001: /* Get DMA channels */ + 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; + + /* Customer OTP */ + + case RPI_FWREQ_GET_CUSTOMER_OTP: + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 8 + 4 * number; + + for (uint32_t n = start_num; n < start_num + number && + n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { + uint32_t otp_row = bcm2835_otp_get_row(s->otp, + BCM2835_OTP_CUSTOMER_OTP + n); + stl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2), otp_row); + } + break; + } + case RPI_FWREQ_SET_CUSTOMER_OTP: + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 4; + + /* Magic numbers to permanently lock customer OTP */ + if (start_num == BCM2835_OTP_LOCK_NUM1 && + number == BCM2835_OTP_LOCK_NUM2) { + bcm2835_otp_set_row(s->otp, + BCM2835_OTP_ROW_32, + BCM2835_OTP_ROW_32_LOCK); + break; + } + + /* If row 32 has the lock bit, don't allow further writes */ + if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & + BCM2835_OTP_ROW_32_LOCK) { + break; + } + + for (uint32_t n = start_num; n < start_num + number && + n < BCM2835_OTP_CUSTOMER_OTP_LEN; n++) { + uint32_t otp_row = ldl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2)); + bcm2835_otp_set_row(s->otp, + BCM2835_OTP_CUSTOMER_OTP + n, otp_row); + } + break; + } + + /* Device-specific private key */ + case RPI_FWREQ_GET_PRIVATE_KEY: + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 8 + 4 * number; + + for (uint32_t n = start_num; n < start_num + number && + n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { + uint32_t otp_row = bcm2835_otp_get_row(s->otp, + BCM2835_OTP_PRIVATE_KEY + n); + stl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2), otp_row); + } + break; + } + case RPI_FWREQ_SET_PRIVATE_KEY: + { + uint32_t start_num = ldl_le_phys(&s->dma_as, value + 12); + uint32_t number = ldl_le_phys(&s->dma_as, value + 16); + + resplen = 4; + + /* If row 32 has the lock bit, don't allow further writes */ + if (bcm2835_otp_get_row(s->otp, BCM2835_OTP_ROW_32) & + BCM2835_OTP_ROW_32_LOCK) { + break; + } + + for (uint32_t n = start_num; n < start_num + number && + n < BCM2835_OTP_PRIVATE_KEY_LEN; n++) { + uint32_t otp_row = ldl_le_phys(&s->dma_as, + value + 20 + ((n - start_num) << 2)); + bcm2835_otp_set_row(s->otp, + BCM2835_OTP_PRIVATE_KEY + n, otp_row); + } + break; + } default: qemu_log_mask(LOG_UNIMP, "bcm2835_property: unhandled tag 0x%08x\n", tag); @@ -368,7 +498,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), @@ -382,6 +512,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); } @@ -405,6 +542,9 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) s->dma_mr = MEMORY_REGION(obj); address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_PROPERTY "-memory"); + obj = object_property_get_link(OBJECT(dev), "otp", &error_abort); + s->otp = BCM2835_OTP(obj); + /* TODO: connect to MAC address of USB NIC device, once we emulate it */ qemu_macaddr_default_if_unset(&s->macaddr); @@ -413,6 +553,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..06f40817df 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() @@ -127,7 +127,7 @@ static void bcm2835_rng_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = bcm2835_rng_reset; + device_class_set_legacy_reset(dc, bcm2835_rng_reset); dc->vmsd = &vmstate_bcm2835_rng; } diff --git a/hw/misc/bcm2835_thermal.c b/hw/misc/bcm2835_thermal.c index c6f3b1ad60..1c1b0671cc 100644 --- a/hw/misc/bcm2835_thermal.c +++ b/hw/misc/bcm2835_thermal.c @@ -80,8 +80,10 @@ static void bcm2835_thermal_write(void *opaque, hwaddr addr, static const MemoryRegionOps bcm2835_thermal_ops = { .read = bcm2835_thermal_read, .write = bcm2835_thermal_write, + .impl.min_access_size = 4, .impl.max_access_size = 4, .valid.min_access_size = 4, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -105,7 +107,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() } @@ -116,7 +118,7 @@ static void bcm2835_thermal_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = bcm2835_thermal_realize; - dc->reset = bcm2835_thermal_reset; + device_class_set_legacy_reset(dc, bcm2835_thermal_reset); dc->vmsd = &bcm2835_thermal_vmstate; } diff --git a/hw/misc/cbus.c b/hw/misc/cbus.c deleted file mode 100644 index 653e8ddcd5..0000000000 --- a/hw/misc/cbus.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma / - * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/misc/cbus.h" -#include "sysemu/runstate.h" - -//#define DEBUG - -typedef struct { - void *opaque; - void (*io)(void *opaque, int rw, int reg, uint16_t *val); - int addr; -} CBusSlave; - -typedef struct { - CBus cbus; - - int sel; - int dat; - int clk; - int bit; - int dir; - uint16_t val; - qemu_irq dat_out; - - int addr; - int reg; - int rw; - enum { - cbus_address, - cbus_value, - } cycle; - - CBusSlave *slave[8]; -} CBusPriv; - -static void cbus_io(CBusPriv *s) -{ - if (s->slave[s->addr]) - s->slave[s->addr]->io(s->slave[s->addr]->opaque, - s->rw, s->reg, &s->val); - else - hw_error("%s: bad slave address %i\n", __func__, s->addr); -} - -static void cbus_cycle(CBusPriv *s) -{ - switch (s->cycle) { - case cbus_address: - s->addr = (s->val >> 6) & 7; - s->rw = (s->val >> 5) & 1; - s->reg = (s->val >> 0) & 0x1f; - - s->cycle = cbus_value; - s->bit = 15; - s->dir = !s->rw; - s->val = 0; - - if (s->rw) - cbus_io(s); - break; - - case cbus_value: - if (!s->rw) - cbus_io(s); - - s->cycle = cbus_address; - s->bit = 8; - s->dir = 1; - s->val = 0; - break; - } -} - -static void cbus_clk(void *opaque, int line, int level) -{ - CBusPriv *s = (CBusPriv *) opaque; - - if (!s->sel && level && !s->clk) { - if (s->dir) - s->val |= s->dat << (s->bit --); - else - qemu_set_irq(s->dat_out, (s->val >> (s->bit --)) & 1); - - if (s->bit < 0) - cbus_cycle(s); - } - - s->clk = level; -} - -static void cbus_dat(void *opaque, int line, int level) -{ - CBusPriv *s = (CBusPriv *) opaque; - - s->dat = level; -} - -static void cbus_sel(void *opaque, int line, int level) -{ - CBusPriv *s = (CBusPriv *) opaque; - - if (!level) { - s->dir = 1; - s->bit = 8; - s->val = 0; - } - - s->sel = level; -} - -CBus *cbus_init(qemu_irq dat) -{ - CBusPriv *s = g_malloc0(sizeof(*s)); - - s->dat_out = dat; - s->cbus.clk = qemu_allocate_irq(cbus_clk, s, 0); - s->cbus.dat = qemu_allocate_irq(cbus_dat, s, 0); - s->cbus.sel = qemu_allocate_irq(cbus_sel, s, 0); - - s->sel = 1; - s->clk = 0; - s->dat = 0; - - return &s->cbus; -} - -void cbus_attach(CBus *bus, void *slave_opaque) -{ - CBusSlave *slave = (CBusSlave *) slave_opaque; - CBusPriv *s = (CBusPriv *) bus; - - s->slave[slave->addr] = slave; -} - -/* Retu/Vilma */ -typedef struct { - uint16_t irqst; - uint16_t irqen; - uint16_t cc[2]; - int channel; - uint16_t result[16]; - uint16_t sample; - uint16_t status; - - struct { - uint16_t cal; - } rtc; - - int is_vilma; - qemu_irq irq; - CBusSlave cbus; -} CBusRetu; - -static void retu_interrupt_update(CBusRetu *s) -{ - qemu_set_irq(s->irq, s->irqst & ~s->irqen); -} - -#define RETU_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ -#define RETU_REG_IDR 0x01 /* (T) Interrupt ID */ -#define RETU_REG_IMR 0x02 /* (RW) Interrupt mask */ -#define RETU_REG_RTCDSR 0x03 /* (RW) RTC seconds register */ -#define RETU_REG_RTCHMR 0x04 /* (RO) RTC hours and minutes reg */ -#define RETU_REG_RTCHMAR 0x05 /* (RW) RTC hours and minutes set reg */ -#define RETU_REG_RTCCALR 0x06 /* (RW) RTC calibration register */ -#define RETU_REG_ADCR 0x08 /* (RW) ADC result register */ -#define RETU_REG_ADCSCR 0x09 /* (RW) ADC sample control register */ -#define RETU_REG_AFCR 0x0a /* (RW) AFC register */ -#define RETU_REG_ANTIFR 0x0b /* (RW) AntiF register */ -#define RETU_REG_CALIBR 0x0c /* (RW) CalibR register*/ -#define RETU_REG_CCR1 0x0d /* (RW) Common control register 1 */ -#define RETU_REG_CCR2 0x0e /* (RW) Common control register 2 */ -#define RETU_REG_RCTRL_CLR 0x0f /* (T) Regulator clear register */ -#define RETU_REG_RCTRL_SET 0x10 /* (T) Regulator set register */ -#define RETU_REG_TXCR 0x11 /* (RW) TxC register */ -#define RETU_REG_STATUS 0x16 /* (RO) Status register */ -#define RETU_REG_WATCHDOG 0x17 /* (RW) Watchdog register */ -#define RETU_REG_AUDTXR 0x18 /* (RW) Audio Codec Tx register */ -#define RETU_REG_AUDPAR 0x19 /* (RW) AudioPA register */ -#define RETU_REG_AUDRXR1 0x1a /* (RW) Audio receive register 1 */ -#define RETU_REG_AUDRXR2 0x1b /* (RW) Audio receive register 2 */ -#define RETU_REG_SGR1 0x1c /* (RW) */ -#define RETU_REG_SCR1 0x1d /* (RW) */ -#define RETU_REG_SGR2 0x1e /* (RW) */ -#define RETU_REG_SCR2 0x1f /* (RW) */ - -/* Retu Interrupt sources */ -enum { - retu_int_pwr = 0, /* Power button */ - retu_int_char = 1, /* Charger */ - retu_int_rtcs = 2, /* Seconds */ - retu_int_rtcm = 3, /* Minutes */ - retu_int_rtcd = 4, /* Days */ - retu_int_rtca = 5, /* Alarm */ - retu_int_hook = 6, /* Hook */ - retu_int_head = 7, /* Headset */ - retu_int_adcs = 8, /* ADC sample */ -}; - -/* Retu ADC channel wiring */ -enum { - retu_adc_bsi = 1, /* BSI */ - retu_adc_batt_temp = 2, /* Battery temperature */ - retu_adc_chg_volt = 3, /* Charger voltage */ - retu_adc_head_det = 4, /* Headset detection */ - retu_adc_hook_det = 5, /* Hook detection */ - retu_adc_rf_gp = 6, /* RF GP */ - retu_adc_tx_det = 7, /* Wideband Tx detection */ - retu_adc_batt_volt = 8, /* Battery voltage */ - retu_adc_sens = 10, /* Light sensor */ - retu_adc_sens_temp = 11, /* Light sensor temperature */ - retu_adc_bbatt_volt = 12, /* Backup battery voltage */ - retu_adc_self_temp = 13, /* RETU temperature */ -}; - -static inline uint16_t retu_read(CBusRetu *s, int reg) -{ -#ifdef DEBUG - printf("RETU read at %02x\n", reg); -#endif - - switch (reg) { - case RETU_REG_ASICR: - return 0x0215 | (s->is_vilma << 7); - - case RETU_REG_IDR: /* TODO: Or is this ffs(s->irqst)? */ - return s->irqst; - - case RETU_REG_IMR: - return s->irqen; - - case RETU_REG_RTCDSR: - case RETU_REG_RTCHMR: - case RETU_REG_RTCHMAR: - /* TODO */ - return 0x0000; - - case RETU_REG_RTCCALR: - return s->rtc.cal; - - case RETU_REG_ADCR: - return (s->channel << 10) | s->result[s->channel]; - case RETU_REG_ADCSCR: - return s->sample; - - case RETU_REG_AFCR: - case RETU_REG_ANTIFR: - case RETU_REG_CALIBR: - /* TODO */ - return 0x0000; - - case RETU_REG_CCR1: - return s->cc[0]; - case RETU_REG_CCR2: - return s->cc[1]; - - case RETU_REG_RCTRL_CLR: - case RETU_REG_RCTRL_SET: - case RETU_REG_TXCR: - /* TODO */ - return 0x0000; - - case RETU_REG_STATUS: - return s->status; - - case RETU_REG_WATCHDOG: - case RETU_REG_AUDTXR: - case RETU_REG_AUDPAR: - case RETU_REG_AUDRXR1: - case RETU_REG_AUDRXR2: - case RETU_REG_SGR1: - case RETU_REG_SCR1: - case RETU_REG_SGR2: - case RETU_REG_SCR2: - /* TODO */ - return 0x0000; - - default: - hw_error("%s: bad register %02x\n", __func__, reg); - } -} - -static inline void retu_write(CBusRetu *s, int reg, uint16_t val) -{ -#ifdef DEBUG - printf("RETU write of %04x at %02x\n", val, reg); -#endif - - switch (reg) { - case RETU_REG_IDR: - s->irqst ^= val; - retu_interrupt_update(s); - break; - - case RETU_REG_IMR: - s->irqen = val; - retu_interrupt_update(s); - break; - - case RETU_REG_RTCDSR: - case RETU_REG_RTCHMAR: - /* TODO */ - break; - - case RETU_REG_RTCCALR: - s->rtc.cal = val; - break; - - case RETU_REG_ADCR: - s->channel = (val >> 10) & 0xf; - s->irqst |= 1 << retu_int_adcs; - retu_interrupt_update(s); - break; - case RETU_REG_ADCSCR: - s->sample &= ~val; - break; - - case RETU_REG_AFCR: - case RETU_REG_ANTIFR: - case RETU_REG_CALIBR: - - case RETU_REG_CCR1: - s->cc[0] = val; - break; - case RETU_REG_CCR2: - s->cc[1] = val; - break; - - case RETU_REG_RCTRL_CLR: - case RETU_REG_RCTRL_SET: - /* TODO */ - break; - - case RETU_REG_WATCHDOG: - if (val == 0 && (s->cc[0] & 2)) - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - break; - - case RETU_REG_TXCR: - case RETU_REG_AUDTXR: - case RETU_REG_AUDPAR: - case RETU_REG_AUDRXR1: - case RETU_REG_AUDRXR2: - case RETU_REG_SGR1: - case RETU_REG_SCR1: - case RETU_REG_SGR2: - case RETU_REG_SCR2: - /* TODO */ - break; - - default: - hw_error("%s: bad register %02x\n", __func__, reg); - } -} - -static void retu_io(void *opaque, int rw, int reg, uint16_t *val) -{ - CBusRetu *s = (CBusRetu *) opaque; - - if (rw) - *val = retu_read(s, reg); - else - retu_write(s, reg, *val); -} - -void *retu_init(qemu_irq irq, int vilma) -{ - CBusRetu *s = g_malloc0(sizeof(*s)); - - s->irq = irq; - s->irqen = 0xffff; - s->irqst = 0x0000; - s->status = 0x0020; - s->is_vilma = !!vilma; - s->rtc.cal = 0x01; - s->result[retu_adc_bsi] = 0x3c2; - s->result[retu_adc_batt_temp] = 0x0fc; - s->result[retu_adc_chg_volt] = 0x165; - s->result[retu_adc_head_det] = 123; - s->result[retu_adc_hook_det] = 1023; - s->result[retu_adc_rf_gp] = 0x11; - s->result[retu_adc_tx_det] = 0x11; - s->result[retu_adc_batt_volt] = 0x250; - s->result[retu_adc_sens] = 2; - s->result[retu_adc_sens_temp] = 0x11; - s->result[retu_adc_bbatt_volt] = 0x3d0; - s->result[retu_adc_self_temp] = 0x330; - - s->cbus.opaque = s; - s->cbus.io = retu_io; - s->cbus.addr = 1; - - return &s->cbus; -} - -void retu_key_event(void *retu, int state) -{ - CBusSlave *slave = (CBusSlave *) retu; - CBusRetu *s = (CBusRetu *) slave->opaque; - - s->irqst |= 1 << retu_int_pwr; - retu_interrupt_update(s); - - if (state) - s->status &= ~(1 << 5); - else - s->status |= 1 << 5; -} - -#if 0 -static void retu_head_event(void *retu, int state) -{ - CBusSlave *slave = (CBusSlave *) retu; - CBusRetu *s = (CBusRetu *) slave->opaque; - - if ((s->cc[0] & 0x500) == 0x500) { /* TODO: Which bits? */ - /* TODO: reissue the interrupt every 100ms or so. */ - s->irqst |= 1 << retu_int_head; - retu_interrupt_update(s); - } - - if (state) - s->result[retu_adc_head_det] = 50; - else - s->result[retu_adc_head_det] = 123; -} - -static void retu_hook_event(void *retu, int state) -{ - CBusSlave *slave = (CBusSlave *) retu; - CBusRetu *s = (CBusRetu *) slave->opaque; - - if ((s->cc[0] & 0x500) == 0x500) { - /* TODO: reissue the interrupt every 100ms or so. */ - s->irqst |= 1 << retu_int_hook; - retu_interrupt_update(s); - } - - if (state) - s->result[retu_adc_hook_det] = 50; - else - s->result[retu_adc_hook_det] = 123; -} -#endif - -/* Tahvo/Betty */ -typedef struct { - uint16_t irqst; - uint16_t irqen; - uint8_t charger; - uint8_t backlight; - uint16_t usbr; - uint16_t power; - - int is_betty; - qemu_irq irq; - CBusSlave cbus; -} CBusTahvo; - -static void tahvo_interrupt_update(CBusTahvo *s) -{ - qemu_set_irq(s->irq, s->irqst & ~s->irqen); -} - -#define TAHVO_REG_ASICR 0x00 /* (RO) ASIC ID & revision */ -#define TAHVO_REG_IDR 0x01 /* (T) Interrupt ID */ -#define TAHVO_REG_IDSR 0x02 /* (RO) Interrupt status */ -#define TAHVO_REG_IMR 0x03 /* (RW) Interrupt mask */ -#define TAHVO_REG_CHAPWMR 0x04 /* (RW) Charger PWM */ -#define TAHVO_REG_LEDPWMR 0x05 /* (RW) LED PWM */ -#define TAHVO_REG_USBR 0x06 /* (RW) USB control */ -#define TAHVO_REG_RCR 0x07 /* (RW) Some kind of power management */ -#define TAHVO_REG_CCR1 0x08 /* (RW) Common control register 1 */ -#define TAHVO_REG_CCR2 0x09 /* (RW) Common control register 2 */ -#define TAHVO_REG_TESTR1 0x0a /* (RW) Test register 1 */ -#define TAHVO_REG_TESTR2 0x0b /* (RW) Test register 2 */ -#define TAHVO_REG_NOPR 0x0c /* (RW) Number of periods */ -#define TAHVO_REG_FRR 0x0d /* (RO) FR */ - -static inline uint16_t tahvo_read(CBusTahvo *s, int reg) -{ -#ifdef DEBUG - printf("TAHVO read at %02x\n", reg); -#endif - - switch (reg) { - case TAHVO_REG_ASICR: - return 0x0021 | (s->is_betty ? 0x0b00 : 0x0300); /* 22 in N810 */ - - case TAHVO_REG_IDR: - case TAHVO_REG_IDSR: /* XXX: what does this do? */ - return s->irqst; - - case TAHVO_REG_IMR: - return s->irqen; - - case TAHVO_REG_CHAPWMR: - return s->charger; - - case TAHVO_REG_LEDPWMR: - return s->backlight; - - case TAHVO_REG_USBR: - return s->usbr; - - case TAHVO_REG_RCR: - return s->power; - - case TAHVO_REG_CCR1: - case TAHVO_REG_CCR2: - case TAHVO_REG_TESTR1: - case TAHVO_REG_TESTR2: - case TAHVO_REG_NOPR: - case TAHVO_REG_FRR: - return 0x0000; - - default: - hw_error("%s: bad register %02x\n", __func__, reg); - } -} - -static inline void tahvo_write(CBusTahvo *s, int reg, uint16_t val) -{ -#ifdef DEBUG - printf("TAHVO write of %04x at %02x\n", val, reg); -#endif - - switch (reg) { - case TAHVO_REG_IDR: - s->irqst ^= val; - tahvo_interrupt_update(s); - break; - - case TAHVO_REG_IMR: - s->irqen = val; - tahvo_interrupt_update(s); - break; - - case TAHVO_REG_CHAPWMR: - s->charger = val; - break; - - case TAHVO_REG_LEDPWMR: - if (s->backlight != (val & 0x7f)) { - s->backlight = val & 0x7f; - printf("%s: LCD backlight now at %i / 127\n", - __func__, s->backlight); - } - break; - - case TAHVO_REG_USBR: - s->usbr = val; - break; - - case TAHVO_REG_RCR: - s->power = val; - break; - - case TAHVO_REG_CCR1: - case TAHVO_REG_CCR2: - case TAHVO_REG_TESTR1: - case TAHVO_REG_TESTR2: - case TAHVO_REG_NOPR: - case TAHVO_REG_FRR: - break; - - default: - hw_error("%s: bad register %02x\n", __func__, reg); - } -} - -static void tahvo_io(void *opaque, int rw, int reg, uint16_t *val) -{ - CBusTahvo *s = (CBusTahvo *) opaque; - - if (rw) - *val = tahvo_read(s, reg); - else - tahvo_write(s, reg, *val); -} - -void *tahvo_init(qemu_irq irq, int betty) -{ - CBusTahvo *s = g_malloc0(sizeof(*s)); - - s->irq = irq; - s->irqen = 0xffff; - s->irqst = 0x0000; - s->is_betty = !!betty; - - s->cbus.opaque = s; - s->cbus.io = tahvo_io; - s->cbus.addr = 2; - - return &s->cbus; -} diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index ab6de69ce7..c5c562fd93 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -12,6 +12,7 @@ #include "hw/qdev-properties.h" #include "qemu/module.h" #include "qom/object.h" +#include "sysemu/runstate.h" #define TYPE_ISA_DEBUG_EXIT_DEVICE "isa-debug-exit" OBJECT_DECLARE_SIMPLE_TYPE(ISADebugExitState, ISA_DEBUG_EXIT_DEVICE) @@ -32,7 +33,8 @@ static uint64_t debug_exit_read(void *opaque, hwaddr addr, unsigned size) static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { - exit((val << 1) | 1); + qemu_system_shutdown_request_with_code(SHUTDOWN_CAUSE_GUEST_SHUTDOWN, + (val << 1) | 1); } static const MemoryRegionOps debug_exit_ops = { diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c new file mode 100644 index 0000000000..96d5efb5e3 --- /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, ResetType type) +{ + 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..0f68fbe1b6 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), @@ -335,7 +335,7 @@ static void ecc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ecc_realize; - dc->reset = ecc_reset; + device_class_set_legacy_reset(dc, ecc_reset); dc->vmsd = &vmstate_ecc; device_class_set_props(dc, ecc_properties); } diff --git a/hw/misc/edu.c b/hw/misc/edu.c index e935c418d4..504178b4a2 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -23,9 +23,9 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu/units.h" #include "hw/pci/pci.h" -#include "hw/hw.h" #include "hw/pci/msi.h" #include "qemu/timer.h" #include "qom/object.h" @@ -103,25 +103,25 @@ static void edu_lower_irq(EduState *edu, uint32_t val) } } -static bool within(uint64_t addr, uint64_t start, uint64_t end) +static void edu_check_range(uint64_t xfer_start, uint64_t xfer_size, + uint64_t dma_start, uint64_t dma_size) { - return start <= addr && addr < end; -} + uint64_t xfer_end = xfer_start + xfer_size; + uint64_t dma_end = dma_start + dma_size; -static void edu_check_range(uint64_t addr, uint64_t size1, uint64_t start, - uint64_t size2) -{ - uint64_t end1 = addr + size1; - uint64_t end2 = start + size2; - - if (within(addr, start, end2) && - end1 > addr && within(end1, start, end2)) { + /* + * 1. ensure we aren't overflowing + * 2. ensure that xfer is within dma address range + */ + if (dma_end >= dma_start && xfer_end >= xfer_start && + xfer_start >= dma_start && xfer_end <= dma_end) { return; } - hw_error("EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 - " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", - addr, end1 - 1, start, end2 - 1); + qemu_log_mask(LOG_GUEST_ERROR, + "EDU: DMA range 0x%016"PRIx64"-0x%016"PRIx64 + " out of bounds (0x%016"PRIx64"-0x%016"PRIx64")!", + xfer_start, xfer_end - 1, dma_start, dma_end - 1); } static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) @@ -129,7 +129,9 @@ static dma_addr_t edu_clamp_addr(const EduState *edu, dma_addr_t addr) dma_addr_t res = addr & edu->dma_mask; if (addr != res) { - printf("EDU: clamping DMA %#.16"PRIx64" to %#.16"PRIx64"!\n", addr, res); + qemu_log_mask(LOG_GUEST_ERROR, + "EDU: clamping DMA 0x%016"PRIx64" to 0x%016"PRIx64"!", + addr, res); } return res; @@ -267,6 +269,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 +353,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..886d10bbab 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() } @@ -145,7 +145,7 @@ static void exynos4210_clk_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_clk_reset; + device_class_set_legacy_reset(dc, exynos4210_clk_reset); dc->vmsd = &exynos4210_clk_vmstate; } diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index e24139c630..9d3c2e817d 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() } @@ -502,7 +502,7 @@ static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_pmu_reset; + device_class_set_legacy_reset(dc, exynos4210_pmu_reset); dc->vmsd = &exynos4210_pmu_vmstate; } diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c index 1b9e8347a1..a741cf176b 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 * @@ -217,6 +217,8 @@ static const MemoryRegionOps exynos4210_rng_ops = { .read = exynos4210_rng_read, .write = exynos4210_rng_write, .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, }; static void exynos4210_rng_reset(DeviceState *dev) @@ -243,7 +245,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), @@ -257,7 +259,7 @@ static void exynos4210_rng_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_rng_reset; + device_class_set_legacy_reset(dc, exynos4210_rng_reset); dc->vmsd = &exynos4210_rng_vmstate; } 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..9654d23f19 100644 --- a/hw/misc/imx25_ccm.c +++ b/hw/misc/imx25_ccm.c @@ -91,7 +91,7 @@ static const char *imx25_ccm_reg_name(uint32_t reg) case IMX25_CCM_LPIMR1_REG: return "lpimr1"; default: - sprintf(unknown, "[%u ?]", reg); + snprintf(unknown, sizeof(unknown), "[%u ?]", reg); return unknown; } } @@ -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() }, @@ -297,7 +297,7 @@ static void imx25_ccm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); - dc->reset = imx25_ccm_reset; + device_class_set_legacy_reset(dc, imx25_ccm_reset); dc->vmsd = &vmstate_imx25_ccm; dc->desc = "i.MX25 Clock Control Module"; diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c index ad30a4b2c0..93130b24e5 100644 --- a/hw/misc/imx31_ccm.c +++ b/hw/misc/imx31_ccm.c @@ -89,7 +89,7 @@ static const char *imx31_ccm_reg_name(uint32_t reg) case IMX31_CCM_PDR2_REG: return "PDR2"; default: - sprintf(unknown, "[%u ?]", reg); + snprintf(unknown, sizeof(unknown), "[%u ?]", reg); return unknown; } } @@ -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() }, @@ -324,7 +324,7 @@ static void imx31_ccm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); - dc->reset = imx31_ccm_reset; + device_class_set_legacy_reset(dc, imx31_ccm_reset); dc->vmsd = &vmstate_imx31_ccm; dc->desc = "i.MX31 Clock Control Module"; diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 4c830fd89a..7d522ed7c5 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) { @@ -96,7 +85,7 @@ static const char *imx6_ccm_reg_name(uint32_t reg) case CCM_CMEOR: return "CMEOR"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -235,7 +224,7 @@ static const char *imx6_analog_reg_name(uint32_t reg) case USB_ANALOG_DIGPROG: return "USB_ANALOG_DIGPROG"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -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; } @@ -312,10 +301,9 @@ static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev) default: /* We should never get there */ g_assert_not_reached(); - break; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_periph_clk(freq); return freq; } @@ -327,7 +315,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 +327,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 +339,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 +373,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 +382,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 +471,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 +482,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 +578,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 +589,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: @@ -760,7 +746,7 @@ static void imx6_ccm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); - dc->reset = imx6_ccm_reset; + device_class_set_legacy_reset(dc, imx6_ccm_reset); dc->vmsd = &vmstate_imx6_ccm; dc->desc = "i.MX6 Clock Control Module"; diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c index 7b0e968804..dc6a2b92ba 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 @@ -68,7 +68,7 @@ static const char *imx6_src_reg_name(uint32_t reg) case SRC_GPR10: return "SRC_GPR10"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -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", @@ -291,7 +291,7 @@ static void imx6_src_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx6_src_realize; - dc->reset = imx6_src_reset; + device_class_set_legacy_reset(dc, imx6_src_reset); dc->vmsd = &vmstate_imx6_src; dc->desc = "i.MX6 System Reset Controller"; } diff --git a/hw/misc/imx6ul_ccm.c b/hw/misc/imx6ul_ccm.c index a65d031455..c836dfe494 100644 --- a/hw/misc/imx6ul_ccm.c +++ b/hw/misc/imx6ul_ccm.c @@ -143,7 +143,7 @@ static const char *imx6ul_ccm_reg_name(uint32_t reg) case CCM_CMEOR: return "CMEOR"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -274,7 +274,7 @@ static const char *imx6ul_analog_reg_name(uint32_t reg) case USB_ANALOG_DIGPROG: return "USB_ANALOG_DIGPROG"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -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); @@ -915,7 +909,7 @@ static void imx6ul_ccm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); - dc->reset = imx6ul_ccm_reset; + device_class_set_legacy_reset(dc, imx6ul_ccm_reset); dc->vmsd = &vmstate_imx6ul_ccm; dc->desc = "i.MX6UL Clock Control Module"; diff --git a/hw/misc/imx7_ccm.c b/hw/misc/imx7_ccm.c index 075159e497..c3ecfd78c1 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) @@ -236,7 +267,7 @@ static void imx7_ccm_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); - dc->reset = imx7_ccm_reset; + device_class_set_legacy_reset(dc, imx7_ccm_reset); dc->vmsd = &vmstate_imx7_ccm; dc->desc = "i.MX7 Clock Control Module"; @@ -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() @@ -266,7 +297,7 @@ static void imx7_analog_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = imx7_analog_reset; + device_class_set_legacy_reset(dc, imx7_analog_reset); dc->vmsd = &vmstate_imx7_analog; dc->desc = "i.MX7 Analog Module"; } diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index ee7698bd9c..070d55339e 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); + device_class_set_legacy_reset(dc, 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..35341c6819 --- /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: + snprintf(unknown, sizeof(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; + device_class_set_legacy_reset(dc, 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..0cbf28db5d 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), @@ -257,7 +259,7 @@ static void imx_rngc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx_rngc_realize; - dc->reset = imx_rngc_reset; + device_class_set_legacy_reset(dc, imx_rngc_reset); dc->desc = RNGC_NAME, dc->vmsd = &vmstate_imx_rngc; } diff --git a/hw/misc/iosb.c b/hw/misc/iosb.c new file mode 100644 index 0000000000..31927eaedb --- /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, ResetType type) +{ + 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 b5a9e30a2c..6e22f2aad6 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -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 @@ -824,7 +824,7 @@ static void iotkit_secctl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &iotkit_secctl_vmstate; - dc->reset = iotkit_secctl_reset; + device_class_set_legacy_reset(dc, iotkit_secctl_reset); dc->realize = iotkit_secctl_realize; device_class_set_props(dc, iotkit_secctl_props); } diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index 7147e2f84e..c1b357e6b7 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) @@ -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 @@ -851,7 +850,7 @@ static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &iotkit_sysctl_vmstate; - dc->reset = iotkit_sysctl_reset; + device_class_set_legacy_reset(dc, iotkit_sysctl_reset); device_class_set_props(dc, iotkit_sysctl_props); dc->realize = iotkit_sysctl_realize; } diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 8270db53cd..5ce3fc0949 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) { @@ -996,7 +991,7 @@ static void ivshmem_common_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_IVSHMEM; k->class_id = PCI_CLASS_MEMORY_RAM; k->revision = 1; - dc->reset = ivshmem_reset; + device_class_set_legacy_reset(dc, ivshmem_reset); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "Inter-VM shared memory"; } @@ -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..5dc209cf8d 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; } @@ -254,7 +267,7 @@ static void lasi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = lasi_reset; + device_class_set_legacy_reset(dc, lasi_reset); dc->vmsd = &vmstate_lasi; } diff --git a/hw/misc/led.c b/hw/misc/led.c index f6d6d68bce..4bb6ce8d29 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() } @@ -114,7 +114,7 @@ static void led_class_init(ObjectClass *klass, void *data) dc->desc = "LED"; dc->vmsd = &vmstate_led; - dc->reset = led_reset; + device_class_set_legacy_reset(dc, led_reset); dc->realize = led_realize; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); device_class_set_props(dc, led_properties); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index f42c12755a..af2b2b1af3 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. @@ -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)); } @@ -490,7 +495,6 @@ static void via1_rtc_update(MOS6522Q800VIA1State *v1s) break; default: g_assert_not_reached(); - break; } return; } @@ -551,7 +555,6 @@ static void via1_rtc_update(MOS6522Q800VIA1State *v1s) break; default: g_assert_not_reached(); - break; } return; } @@ -697,6 +700,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 +873,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 +1033,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 +1070,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 +1087,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 +1201,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, ResetType type) { - 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, type); + } ms->timers[0].frequency = VIA_TIMER_FREQ; ms->timers[1].frequency = VIA_TIMER_FREQ; @@ -993,6 +1221,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) @@ -1059,7 +1290,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 +1316,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 +1330,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 +1357,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, ResetType type) { - 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, type); + } ms->timers[0].frequency = VIA_TIMER_FREQ; ms->timers[1].frequency = VIA_TIMER_FREQ; @@ -1173,7 +1409,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 +1419,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 0d4c13319a..1db7ebf3e2 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -27,10 +27,7 @@ #include "hw/irq.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" @@ -490,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), @@ -567,7 +564,7 @@ static void cuda_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = cuda_realize; - dc->reset = cuda_reset; + device_class_set_legacy_reset(dc, cuda_reset); dc->vmsd = &vmstate_cuda; device_class_set_props(dc, cuda_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); @@ -589,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, ResetType type) { - 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, type); + } ms->timers[0].frequency = CUDA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; @@ -602,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 c8ac5633b2..7cad62819a 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -28,6 +28,7 @@ #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" @@ -167,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() @@ -193,7 +194,7 @@ static void macio_gpio_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - dc->reset = macio_gpio_reset; + device_class_set_legacy_reset(dc, macio_gpio_reset); dc->vmsd = &vmstate_macio_gpio; nc->nmi_monitor_handler = macio_gpio_nmi; } diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index efcc02609f..74c2cb3462 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) @@ -922,7 +922,7 @@ static void mac_dbdma_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = mac_dbdma_realize; - dc->reset = mac_dbdma_reset; + device_class_set_legacy_reset(dc, mac_dbdma_reset); dc->vmsd = &vmstate_dbdma; } diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 08dbdd7fc0..3f449f91c0 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -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,11 +211,11 @@ 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", MACIO_NVRAM_SIZE); qdev_prop_set_uint32(dev, "it_shift", 4); @@ -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 70562ed8d0..4b451e0af3 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -31,12 +31,8 @@ #include "qemu/osdep.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" @@ -672,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() @@ -683,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), @@ -702,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 } @@ -741,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); } } @@ -775,7 +770,7 @@ static void pmu_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pmu_realize; - dc->reset = pmu_reset; + device_class_set_legacy_reset(dc, pmu_reset); dc->vmsd = &vmstate_pmu; device_class_set_props(dc, pmu_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); @@ -797,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, ResetType type) { - 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, type); + } ms->timers[0].frequency = VIA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; @@ -814,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 95268eddc0..d02d96e403 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -1,79 +1,82 @@ -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_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_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,34 +85,45 @@ 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_STM32_RCC', if_true: files('stm32_rcc.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', @@ -117,24 +131,24 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_sbc.c', 'aspeed_sdmc.c', 'aspeed_xdma.c', - 'aspeed_peci.c')) + 'aspeed_peci.c', + 'aspeed_sli.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..04256aacdc 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, @@ -235,7 +235,7 @@ static void mips_gcr_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, mips_gcr_properties); dc->vmsd = &vmstate_mips_gcr; - dc->reset = mips_gcr_reset; + device_class_set_legacy_reset(dc, mips_gcr_reset); dc->realize = mips_gcr_realize; } diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 4a94c87054..2f7fb8167f 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() }, @@ -174,7 +174,7 @@ static void mips_cpc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mips_cpc_realize; - dc->reset = mips_cpc_reset; + device_class_set_legacy_reset(dc, mips_cpc_reset); dc->vmsd = &vmstate_mips_cpc; device_class_set_props(dc, mips_cpc_properties); } diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index badef5c214..c5214c8b30 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(), }; @@ -563,7 +547,7 @@ static void mips_itu_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, mips_itu_properties); dc->realize = mips_itu_realize; - dc->reset = mips_itu_reset; + device_class_set_legacy_reset(dc, mips_itu_reset); } static const TypeInfo mips_itu_info = { diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index fe38c44426..515f62e687 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" @@ -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, ResetType type) { - 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..20359254ba 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), @@ -335,7 +335,7 @@ static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) dc->vmsd = &mps2_fpgaio_vmstate; dc->realize = mps2_fpgaio_realize; - dc->reset = mps2_fpgaio_reset; + device_class_set_legacy_reset(dc, mps2_fpgaio_reset); device_class_set_props(dc, mps2_fpgaio_properties); } diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index b3b42a792c..d45ff77bd6 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 } }; @@ -376,7 +481,7 @@ static void mps2_scc_class_init(ObjectClass *klass, void *data) dc->realize = mps2_scc_realize; dc->vmsd = &mps2_scc_vmstate; - dc->reset = mps2_scc_reset; + device_class_set_legacy_reset(dc, mps2_scc_reset); device_class_set_props(dc, mps2_scc_properties); } @@ -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..b8dde198c6 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() } @@ -142,7 +142,7 @@ static void msf2_sysreg_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_msf2_sysreg; - dc->reset = msf2_sysreg_reset; + device_class_set_legacy_reset(dc, msf2_sysreg_reset); device_class_set_props(dc, msf2_sysreg_properties); dc->realize = msf2_sysreg_realize; } diff --git a/hw/misc/mst_fpga.c b/hw/misc/mst_fpga.c deleted file mode 100644 index 2aaadfa966..0000000000 --- a/hw/misc/mst_fpga.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * PXA270-based Intel Mainstone platforms. - * FPGA driver - * - * Copyright (c) 2007 by Armin Kuster or - * - * - * This code is licensed under the GNU GPL v2. - * - * 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/irq.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qom/object.h" - -/* Mainstone FPGA for extern irqs */ -#define FPGA_GPIO_PIN 0 -#define MST_NUM_IRQS 16 -#define MST_LEDDAT1 0x10 -#define MST_LEDDAT2 0x14 -#define MST_LEDCTRL 0x40 -#define MST_GPSWR 0x60 -#define MST_MSCWR1 0x80 -#define MST_MSCWR2 0x84 -#define MST_MSCWR3 0x88 -#define MST_MSCRD 0x90 -#define MST_INTMSKENA 0xc0 -#define MST_INTSETCLR 0xd0 -#define MST_PCMCIA0 0xe0 -#define MST_PCMCIA1 0xe4 - -#define MST_PCMCIAx_READY (1 << 10) -#define MST_PCMCIAx_nCD (1 << 5) - -#define MST_PCMCIA_CD0_IRQ 9 -#define MST_PCMCIA_CD1_IRQ 13 - -#define TYPE_MAINSTONE_FPGA "mainstone-fpga" -OBJECT_DECLARE_SIMPLE_TYPE(mst_irq_state, MAINSTONE_FPGA) - -struct mst_irq_state { - SysBusDevice parent_obj; - - MemoryRegion iomem; - - qemu_irq parent; - - uint32_t prev_level; - uint32_t leddat1; - uint32_t leddat2; - uint32_t ledctrl; - uint32_t gpswr; - uint32_t mscwr1; - uint32_t mscwr2; - uint32_t mscwr3; - uint32_t mscrd; - uint32_t intmskena; - uint32_t intsetclr; - uint32_t pcmcia0; - uint32_t pcmcia1; -}; - -static void -mst_fpga_set_irq(void *opaque, int irq, int level) -{ - mst_irq_state *s = (mst_irq_state *)opaque; - uint32_t oldint = s->intsetclr & s->intmskena; - - if (level) - s->prev_level |= 1u << irq; - else - s->prev_level &= ~(1u << irq); - - switch(irq) { - case MST_PCMCIA_CD0_IRQ: - if (level) - s->pcmcia0 &= ~MST_PCMCIAx_nCD; - else - s->pcmcia0 |= MST_PCMCIAx_nCD; - break; - case MST_PCMCIA_CD1_IRQ: - if (level) - s->pcmcia1 &= ~MST_PCMCIAx_nCD; - else - s->pcmcia1 |= MST_PCMCIAx_nCD; - break; - } - - if ((s->intmskena & (1u << irq)) && level) - s->intsetclr |= 1u << irq; - - if (oldint != (s->intsetclr & s->intmskena)) - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); -} - - -static uint64_t -mst_fpga_readb(void *opaque, hwaddr addr, unsigned size) -{ - mst_irq_state *s = (mst_irq_state *) opaque; - - switch (addr) { - case MST_LEDDAT1: - return s->leddat1; - case MST_LEDDAT2: - return s->leddat2; - case MST_LEDCTRL: - return s->ledctrl; - case MST_GPSWR: - return s->gpswr; - case MST_MSCWR1: - return s->mscwr1; - case MST_MSCWR2: - return s->mscwr2; - case MST_MSCWR3: - return s->mscwr3; - case MST_MSCRD: - return s->mscrd; - case MST_INTMSKENA: - return s->intmskena; - case MST_INTSETCLR: - return s->intsetclr; - case MST_PCMCIA0: - return s->pcmcia0; - case MST_PCMCIA1: - return s->pcmcia1; - default: - printf("Mainstone - mst_fpga_readb: Bad register offset " - "0x" TARGET_FMT_plx "\n", addr); - } - return 0; -} - -static void -mst_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - mst_irq_state *s = (mst_irq_state *) opaque; - value &= 0xffffffff; - - switch (addr) { - case MST_LEDDAT1: - s->leddat1 = value; - break; - case MST_LEDDAT2: - s->leddat2 = value; - break; - case MST_LEDCTRL: - s->ledctrl = value; - break; - case MST_GPSWR: - s->gpswr = value; - break; - case MST_MSCWR1: - s->mscwr1 = value; - break; - case MST_MSCWR2: - s->mscwr2 = value; - break; - case MST_MSCWR3: - s->mscwr3 = value; - break; - case MST_MSCRD: - s->mscrd = value; - break; - case MST_INTMSKENA: /* Mask interrupt */ - s->intmskena = (value & 0xFEEFF); - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); - break; - case MST_INTSETCLR: /* clear or set interrupt */ - s->intsetclr = (value & 0xFEEFF); - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); - break; - /* For PCMCIAx allow the to change only power and reset */ - case MST_PCMCIA0: - s->pcmcia0 = (value & 0x1f) | (s->pcmcia0 & ~0x1f); - break; - case MST_PCMCIA1: - s->pcmcia1 = (value & 0x1f) | (s->pcmcia1 & ~0x1f); - break; - default: - printf("Mainstone - mst_fpga_writeb: Bad register offset " - "0x" TARGET_FMT_plx "\n", addr); - } -} - -static const MemoryRegionOps mst_fpga_ops = { - .read = mst_fpga_readb, - .write = mst_fpga_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int mst_fpga_post_load(void *opaque, int version_id) -{ - mst_irq_state *s = (mst_irq_state *) opaque; - - qemu_set_irq(s->parent, s->intsetclr & s->intmskena); - return 0; -} - -static void mst_fpga_init(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - mst_irq_state *s = MAINSTONE_FPGA(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - s->pcmcia0 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; - s->pcmcia1 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD; - - sysbus_init_irq(sbd, &s->parent); - - /* alloc the external 16 irqs */ - qdev_init_gpio_in(dev, mst_fpga_set_irq, MST_NUM_IRQS); - - memory_region_init_io(&s->iomem, obj, &mst_fpga_ops, s, - "fpga", 0x00100000); - sysbus_init_mmio(sbd, &s->iomem); -} - -static const VMStateDescription vmstate_mst_fpga_regs = { - .name = "mainstone_fpga", - .version_id = 0, - .minimum_version_id = 0, - .post_load = mst_fpga_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(prev_level, mst_irq_state), - VMSTATE_UINT32(leddat1, mst_irq_state), - VMSTATE_UINT32(leddat2, mst_irq_state), - VMSTATE_UINT32(ledctrl, mst_irq_state), - VMSTATE_UINT32(gpswr, mst_irq_state), - VMSTATE_UINT32(mscwr1, mst_irq_state), - VMSTATE_UINT32(mscwr2, mst_irq_state), - VMSTATE_UINT32(mscwr3, mst_irq_state), - VMSTATE_UINT32(mscrd, mst_irq_state), - VMSTATE_UINT32(intmskena, mst_irq_state), - VMSTATE_UINT32(intsetclr, mst_irq_state), - VMSTATE_UINT32(pcmcia0, mst_irq_state), - VMSTATE_UINT32(pcmcia1, mst_irq_state), - VMSTATE_END_OF_LIST(), - }, -}; - -static void mst_fpga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "Mainstone II FPGA"; - dc->vmsd = &vmstate_mst_fpga_regs; -} - -static const TypeInfo mst_fpga_info = { - .name = TYPE_MAINSTONE_FPGA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(mst_irq_state), - .instance_init = mst_fpga_init, - .class_init = mst_fpga_class_init, -}; - -static void mst_fpga_register_types(void) -{ - type_register_static(&mst_fpga_info); -} - -type_init(mst_fpga_register_types) diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c index bc2b879feb..2098c85ee0 100644 --- a/hw/misc/npcm7xx_clk.c +++ b/hw/misc/npcm7xx_clk.c @@ -873,20 +873,13 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetType type) QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - switch (type) { - case RESET_TYPE_COLD: - memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); - s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - npcm7xx_clk_update_all_clocks(s); - return; - } - + memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); + s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + npcm7xx_clk_update_all_clocks(s); /* * A small number of registers need to be reset on a core domain reset, * but no such reset type exists yet. */ - qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.", - __func__, type); } static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) @@ -976,7 +969,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 +979,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 +990,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 +1001,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..c4c4e246d7 100644 --- a/hw/misc/npcm7xx_gcr.c +++ b/hw/misc/npcm7xx_gcr.c @@ -159,14 +159,10 @@ static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type) QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values)); - switch (type) { - case RESET_TYPE_COLD: - memcpy(s->regs, cold_reset_values, sizeof(s->regs)); - s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; - s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; - s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; - break; - } + memcpy(s->regs, cold_reset_values, sizeof(s->regs)); + s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron; + s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr; + s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3; } static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp) @@ -227,7 +223,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..9fcc69fe5c 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -467,7 +467,7 @@ static void npcm7xx_mft_enter_reset(Object *obj, ResetType type) npcm7xx_mft_reset(s); } -static void npcm7xx_mft_hold_reset(Object *obj) +static void npcm7xx_mft_hold_reset(Object *obj, ResetType type) { NPCM7xxMFTState *s = NPCM7XX_MFT(obj); @@ -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..f7f77e30a2 100644 --- a/hw/misc/npcm7xx_pwm.c +++ b/hw/misc/npcm7xx_pwm.c @@ -468,7 +468,7 @@ static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type) s->piir = 0x00000000; } -static void npcm7xx_pwm_hold_reset(Object *obj) +static void npcm7xx_pwm_hold_reset(Object *obj, ResetType type) { NPCM7xxPWMState *s = NPCM7XX_PWM(obj); int i; @@ -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..2d67f3f766 100644 --- a/hw/misc/nrf51_rng.c +++ b/hw/misc/nrf51_rng.c @@ -107,25 +107,25 @@ static void rng_write(void *opaque, hwaddr offset, break; case NRF51_RNG_REG_SHORTS: s->shortcut_stop_on_valrdy = - (value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0; + (value & BIT(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0; break; case NRF51_RNG_REG_INTEN: s->interrupt_enabled = - (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0; + (value & BIT(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0; break; case NRF51_RNG_REG_INTENSET: - if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { + if (value & BIT(NRF51_RNG_REG_INTEN_VALRDY)) { s->interrupt_enabled = 1; } break; case NRF51_RNG_REG_INTENCLR: - if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { + if (value & BIT(NRF51_RNG_REG_INTEN_VALRDY)) { s->interrupt_enabled = 0; } break; case NRF51_RNG_REG_CONFIG: s->filter_enabled = - (value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0; + (value & BIT(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0; break; default: @@ -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), @@ -247,7 +247,7 @@ static void nrf51_rng_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, nrf51_rng_properties); dc->vmsd = &vmstate_rng; - dc->reset = nrf51_rng_reset; + device_class_set_legacy_reset(dc, nrf51_rng_reset); } static const TypeInfo nrf51_rng_info = { diff --git a/hw/misc/omap_clk.c b/hw/misc/omap_clk.c index c77ca2fc74..0157c9be75 100644 --- a/hw/misc/omap_clk.c +++ b/hw/misc/omap_clk.c @@ -35,9 +35,6 @@ struct clk { #define CLOCK_IN_OMAP730 (1 << 11) #define CLOCK_IN_OMAP1510 (1 << 12) #define CLOCK_IN_OMAP16XX (1 << 13) -#define CLOCK_IN_OMAP242X (1 << 14) -#define CLOCK_IN_OMAP243X (1 << 15) -#define CLOCK_IN_OMAP343X (1 << 16) uint32_t flags; int id; @@ -59,8 +56,7 @@ static struct clk xtal_osc12m = { static struct clk xtal_osc32k = { .name = "xtal_osc_32k", .rate = 32768, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | - CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_ref = { @@ -507,449 +503,10 @@ static struct clk i2c_ick = { static struct clk clk32k = { .name = "clk32-kHz", .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, + ALWAYS_ENABLED, .parent = &xtal_osc32k, }; -static struct clk ref_clk = { - .name = "ref_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 12000000, /* 12 MHz or 13 MHz or 19.2 MHz */ - /*.parent = sys.xtalin */ -}; - -static struct clk apll_96m = { - .name = "apll_96m", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 96000000, - /*.parent = ref_clk */ -}; - -static struct clk apll_54m = { - .name = "apll_54m", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 54000000, - /*.parent = ref_clk */ -}; - -static struct clk sys_clk = { - .name = "sys_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 32768, - /*.parent = sys.xtalin */ -}; - -static struct clk sleep_clk = { - .name = "sleep_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 32768, - /*.parent = sys.xtalin */ -}; - -static struct clk dpll_ck = { - .name = "dpll", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .parent = &ref_clk, -}; - -static struct clk dpll_x2_ck = { - .name = "dpll_x2", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .parent = &ref_clk, -}; - -static struct clk wdt1_sys_clk = { - .name = "wdt1_sys_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X | ALWAYS_ENABLED, - .rate = 32768, - /*.parent = sys.xtalin */ -}; - -static struct clk func_96m_clk = { - .name = "func_96m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 1, - .parent = &apll_96m, -}; - -static struct clk func_48m_clk = { - .name = "func_48m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 2, - .parent = &apll_96m, -}; - -static struct clk func_12m_clk = { - .name = "func_12m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 8, - .parent = &apll_96m, -}; - -static struct clk func_54m_clk = { - .name = "func_54m_clk", - .flags = CLOCK_IN_OMAP242X, - .divisor = 1, - .parent = &apll_54m, -}; - -static struct clk sys_clkout = { - .name = "clkout", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk sys_clkout2 = { - .name = "clkout2", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_clk = { - .name = "core_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &dpll_x2_ck, /* Switchable between dpll_ck and clk32k */ -}; - -static struct clk l3_clk = { - .name = "l3_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk core_l4_iclk = { - .name = "core_l4_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk wu_l4_iclk = { - .name = "wu_l4_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk core_l3_iclk = { - .name = "core_l3_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk core_l4_usb_clk = { - .name = "core_l4_usb_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk wu_gpt1_clk = { - .name = "wu_gpt1_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk wu_32k_clk = { - .name = "wu_32k_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk uart1_fclk = { - .name = "uart1_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, -}; - -static struct clk uart1_iclk = { - .name = "uart1_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk uart2_fclk = { - .name = "uart2_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, -}; - -static struct clk uart2_iclk = { - .name = "uart2_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk uart3_fclk = { - .name = "uart3_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, -}; - -static struct clk uart3_iclk = { - .name = "uart3_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk mpu_fclk = { - .name = "mpu_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk mpu_iclk = { - .name = "mpu_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk int_m_fclk = { - .name = "int_m_fclk", - .alias = "mpu_intc_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk int_m_iclk = { - .name = "int_m_iclk", - .alias = "mpu_intc_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, -}; - -static struct clk core_gpt2_clk = { - .name = "core_gpt2_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt3_clk = { - .name = "core_gpt3_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt4_clk = { - .name = "core_gpt4_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt5_clk = { - .name = "core_gpt5_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt6_clk = { - .name = "core_gpt6_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt7_clk = { - .name = "core_gpt7_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt8_clk = { - .name = "core_gpt8_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt9_clk = { - .name = "core_gpt9_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt10_clk = { - .name = "core_gpt10_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt11_clk = { - .name = "core_gpt11_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk core_gpt12_clk = { - .name = "core_gpt12_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, -}; - -static struct clk mcbsp1_clk = { - .name = "mcbsp1_cg", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 2, - .parent = &func_96m_clk, -}; - -static struct clk mcbsp2_clk = { - .name = "mcbsp2_cg", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .divisor = 2, - .parent = &func_96m_clk, -}; - -static struct clk emul_clk = { - .name = "emul_ck", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_54m_clk, -}; - -static struct clk sdma_fclk = { - .name = "sdma_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &l3_clk, -}; - -static struct clk sdma_iclk = { - .name = "sdma_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l3_iclk, /* core_l4_iclk for the configuration port */ -}; - -static struct clk i2c1_fclk = { - .name = "i2c1.fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_12m_clk, - .divisor = 1, -}; - -static struct clk i2c1_iclk = { - .name = "i2c1.iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk i2c2_fclk = { - .name = "i2c2.fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_12m_clk, - .divisor = 1, -}; - -static struct clk i2c2_iclk = { - .name = "i2c2.iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk gpio_dbclk[5] = { - { - .name = "gpio1_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio2_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio3_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio4_dbclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, { - .name = "gpio5_dbclk", - .flags = CLOCK_IN_OMAP243X, - .parent = &wu_32k_clk, - }, -}; - -static struct clk gpio_iclk = { - .name = "gpio_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &wu_l4_iclk, -}; - -static struct clk mmc_fck = { - .name = "mmc_fclk", - .flags = CLOCK_IN_OMAP242X, - .parent = &func_96m_clk, -}; - -static struct clk mmc_ick = { - .name = "mmc_iclk", - .flags = CLOCK_IN_OMAP242X, - .parent = &core_l4_iclk, -}; - -static struct clk spi_fclk[3] = { - { - .name = "spi1_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, - }, { - .name = "spi2_fclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, - }, { - .name = "spi3_fclk", - .flags = CLOCK_IN_OMAP243X, - .parent = &func_48m_clk, - }, -}; - -static struct clk dss_clk[2] = { - { - .name = "dss_clk1", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_clk, - }, { - .name = "dss_clk2", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &sys_clk, - }, -}; - -static struct clk dss_54m_clk = { - .name = "dss_54m_clk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &func_54m_clk, -}; - -static struct clk dss_l3_iclk = { - .name = "dss_l3_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l3_iclk, -}; - -static struct clk dss_l4_iclk = { - .name = "dss_l4_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, -}; - -static struct clk spi_iclk[3] = { - { - .name = "spi1_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, - }, { - .name = "spi2_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, - }, { - .name = "spi3_iclk", - .flags = CLOCK_IN_OMAP243X, - .parent = &core_l4_iclk, - }, -}; - -static struct clk omapctrl_clk = { - .name = "omapctrl_iclk", - .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, - /* XXX Should be in WKUP domain */ - .parent = &core_l4_iclk, -}; - static struct clk *onchip_clks[] = { /* OMAP 1 */ @@ -1019,80 +576,6 @@ static struct clk *onchip_clks[] = { &i2c_fck, &i2c_ick, - /* OMAP 2 */ - - &ref_clk, - &apll_96m, - &apll_54m, - &sys_clk, - &sleep_clk, - &dpll_ck, - &dpll_x2_ck, - &wdt1_sys_clk, - &func_96m_clk, - &func_48m_clk, - &func_12m_clk, - &func_54m_clk, - &sys_clkout, - &sys_clkout2, - &core_clk, - &l3_clk, - &core_l4_iclk, - &wu_l4_iclk, - &core_l3_iclk, - &core_l4_usb_clk, - &wu_gpt1_clk, - &wu_32k_clk, - &uart1_fclk, - &uart1_iclk, - &uart2_fclk, - &uart2_iclk, - &uart3_fclk, - &uart3_iclk, - &mpu_fclk, - &mpu_iclk, - &int_m_fclk, - &int_m_iclk, - &core_gpt2_clk, - &core_gpt3_clk, - &core_gpt4_clk, - &core_gpt5_clk, - &core_gpt6_clk, - &core_gpt7_clk, - &core_gpt8_clk, - &core_gpt9_clk, - &core_gpt10_clk, - &core_gpt11_clk, - &core_gpt12_clk, - &mcbsp1_clk, - &mcbsp2_clk, - &emul_clk, - &sdma_fclk, - &sdma_iclk, - &i2c1_fclk, - &i2c1_iclk, - &i2c2_fclk, - &i2c2_iclk, - &gpio_dbclk[0], - &gpio_dbclk[1], - &gpio_dbclk[2], - &gpio_dbclk[3], - &gpio_iclk, - &mmc_fck, - &mmc_ick, - &spi_fclk[0], - &spi_iclk[0], - &spi_fclk[1], - &spi_iclk[1], - &spi_fclk[2], - &spi_iclk[2], - &dss_clk[0], - &dss_clk[1], - &dss_54m_clk, - &dss_l3_iclk, - &dss_l4_iclk, - &omapctrl_clk, - NULL }; @@ -1230,12 +713,6 @@ void omap_clk_init(struct omap_mpu_state_s *mpu) flag = CLOCK_IN_OMAP310; else if (cpu_is_omap1510(mpu)) flag = CLOCK_IN_OMAP1510; - else if (cpu_is_omap2410(mpu) || cpu_is_omap2420(mpu)) - flag = CLOCK_IN_OMAP242X; - else if (cpu_is_omap2430(mpu)) - flag = CLOCK_IN_OMAP243X; - else if (cpu_is_omap3430(mpu)) - flag = CLOCK_IN_OMAP243X; else return; diff --git a/hw/misc/omap_gpmc.c b/hw/misc/omap_gpmc.c deleted file mode 100644 index 10de7a5523..0000000000 --- a/hw/misc/omap_gpmc.c +++ /dev/null @@ -1,898 +0,0 @@ -/* - * TI OMAP general purpose memory controller emulation. - * - * Copyright (C) 2007-2009 Nokia Corporation - * Original code written by Andrzej Zaborowski - * Enhancements for OMAP3 and NAND support written by Juha Riihimäki - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/irq.h" -#include "hw/block/flash.h" -#include "hw/arm/omap.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" - -/* General-Purpose Memory Controller */ -struct omap_gpmc_s { - qemu_irq irq; - qemu_irq drq; - MemoryRegion iomem; - int accept_256; - - uint8_t revision; - uint8_t sysconfig; - uint16_t irqst; - uint16_t irqen; - uint16_t lastirq; - uint16_t timeout; - uint16_t config; - struct omap_gpmc_cs_file_s { - uint32_t config[7]; - MemoryRegion *iomem; - MemoryRegion container; - MemoryRegion nandiomem; - DeviceState *dev; - } cs_file[8]; - int ecc_cs; - int ecc_ptr; - uint32_t ecc_cfg; - ECCState ecc[9]; - struct prefetch { - uint32_t config1; /* GPMC_PREFETCH_CONFIG1 */ - uint32_t transfercount; /* GPMC_PREFETCH_CONFIG2:TRANSFERCOUNT */ - int startengine; /* GPMC_PREFETCH_CONTROL:STARTENGINE */ - int fifopointer; /* GPMC_PREFETCH_STATUS:FIFOPOINTER */ - int count; /* GPMC_PREFETCH_STATUS:COUNTVALUE */ - MemoryRegion iomem; - uint8_t fifo[64]; - } prefetch; -}; - -#define OMAP_GPMC_8BIT 0 -#define OMAP_GPMC_16BIT 1 -#define OMAP_GPMC_NOR 0 -#define OMAP_GPMC_NAND 2 - -static int omap_gpmc_devtype(struct omap_gpmc_cs_file_s *f) -{ - return (f->config[0] >> 10) & 3; -} - -static int omap_gpmc_devsize(struct omap_gpmc_cs_file_s *f) -{ - /* devsize field is really 2 bits but we ignore the high - * bit to ensure consistent behaviour if the guest sets - * it (values 2 and 3 are reserved in the TRM) - */ - return (f->config[0] >> 12) & 1; -} - -/* Extract the chip-select value from the prefetch config1 register */ -static int prefetch_cs(uint32_t config1) -{ - return (config1 >> 24) & 7; -} - -static int prefetch_threshold(uint32_t config1) -{ - return (config1 >> 8) & 0x7f; -} - -static void omap_gpmc_int_update(struct omap_gpmc_s *s) -{ - /* The TRM is a bit unclear, but it seems to say that - * the TERMINALCOUNTSTATUS bit is set only on the - * transition when the prefetch engine goes from - * active to inactive, whereas the FIFOEVENTSTATUS - * bit is held high as long as the fifo has at - * least THRESHOLD bytes available. - * So we do the latter here, but TERMINALCOUNTSTATUS - * is set elsewhere. - */ - if (s->prefetch.fifopointer >= prefetch_threshold(s->prefetch.config1)) { - s->irqst |= 1; - } - if ((s->irqen & s->irqst) != s->lastirq) { - s->lastirq = s->irqen & s->irqst; - qemu_set_irq(s->irq, s->lastirq); - } -} - -static void omap_gpmc_dma_update(struct omap_gpmc_s *s, int value) -{ - if (s->prefetch.config1 & 4) { - qemu_set_irq(s->drq, value); - } -} - -/* Access functions for when a NAND-like device is mapped into memory: - * all addresses in the region behave like accesses to the relevant - * GPMC_NAND_DATA_i register (which is actually implemented to call these) - */ -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; - uint64_t v; - nand_setpins(f->dev, 0, 0, 0, 1, 0); - switch (omap_gpmc_devsize(f)) { - case OMAP_GPMC_8BIT: - v = nand_getio(f->dev); - if (size == 1) { - return v; - } - v |= (nand_getio(f->dev) << 8); - if (size == 2) { - return v; - } - v |= (nand_getio(f->dev) << 16); - v |= (nand_getio(f->dev) << 24); - return v; - case OMAP_GPMC_16BIT: - v = nand_getio(f->dev); - if (size == 1) { - /* 8 bit read from 16 bit device : probably a guest bug */ - return v & 0xff; - } - if (size == 2) { - return v; - } - v |= (nand_getio(f->dev) << 16); - return v; - default: - abort(); - } -} - -static void omap_nand_setio(DeviceState *dev, uint64_t value, - int nandsize, int size) -{ - /* Write the specified value to the NAND device, respecting - * both size of the NAND device and size of the write access. - */ - switch (nandsize) { - case OMAP_GPMC_8BIT: - switch (size) { - case 1: - nand_setio(dev, value & 0xff); - break; - case 2: - nand_setio(dev, value & 0xff); - nand_setio(dev, (value >> 8) & 0xff); - break; - case 4: - default: - nand_setio(dev, value & 0xff); - nand_setio(dev, (value >> 8) & 0xff); - nand_setio(dev, (value >> 16) & 0xff); - nand_setio(dev, (value >> 24) & 0xff); - break; - } - break; - case OMAP_GPMC_16BIT: - switch (size) { - case 1: - /* writing to a 16bit device with 8bit access is probably a guest - * bug; pass the value through anyway. - */ - case 2: - nand_setio(dev, value & 0xffff); - break; - case 4: - default: - nand_setio(dev, value & 0xffff); - nand_setio(dev, (value >> 16) & 0xffff); - break; - } - break; - } -} - -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; - nand_setpins(f->dev, 0, 0, 0, 1, 0); - omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); -} - -static const MemoryRegionOps omap_nand_ops = { - .read = omap_nand_read, - .write = omap_nand_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void fill_prefetch_fifo(struct omap_gpmc_s *s) -{ - /* Fill the prefetch FIFO by reading data from NAND. - * We do this synchronously, unlike the hardware which - * will do this asynchronously. We refill when the - * FIFO has THRESHOLD bytes free, and we always refill - * as much data as possible starting at the top end - * of the FIFO. - * (We have to refill at THRESHOLD rather than waiting - * for the FIFO to empty to allow for the case where - * the FIFO size isn't an exact multiple of THRESHOLD - * and we're doing DMA transfers.) - * This means we never need to handle wrap-around in - * the fifo-reading code, and the next byte of data - * to read is always fifo[63 - fifopointer]. - */ - int fptr; - int cs = prefetch_cs(s->prefetch.config1); - int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); - int bytes; - /* Don't believe the bit of the OMAP TRM that says that COUNTVALUE - * and TRANSFERCOUNT are in units of 16 bit words for 16 bit NAND. - * Instead believe the bit that says it is always a byte count. - */ - bytes = 64 - s->prefetch.fifopointer; - if (bytes > s->prefetch.count) { - bytes = s->prefetch.count; - } - if (is16bit) { - bytes &= ~1; - } - - s->prefetch.count -= bytes; - s->prefetch.fifopointer += bytes; - fptr = 64 - s->prefetch.fifopointer; - /* Move the existing data in the FIFO so it sits just - * before what we're about to read in - */ - while (fptr < (64 - bytes)) { - s->prefetch.fifo[fptr] = s->prefetch.fifo[fptr + bytes]; - fptr++; - } - while (fptr < 64) { - if (is16bit) { - uint32_t v = omap_nand_read(&s->cs_file[cs], 0, 2); - s->prefetch.fifo[fptr++] = v & 0xff; - s->prefetch.fifo[fptr++] = (v >> 8) & 0xff; - } else { - s->prefetch.fifo[fptr++] = omap_nand_read(&s->cs_file[cs], 0, 1); - } - } - if (s->prefetch.startengine && (s->prefetch.count == 0)) { - /* This was the final transfer: raise TERMINALCOUNTSTATUS */ - s->irqst |= 2; - s->prefetch.startengine = 0; - } - /* If there are any bytes in the FIFO at this point then - * we must raise a DMA request (either this is a final part - * transfer, or we filled the FIFO in which case we certainly - * have THRESHOLD bytes available) - */ - if (s->prefetch.fifopointer != 0) { - omap_gpmc_dma_update(s, 1); - } - omap_gpmc_int_update(s); -} - -/* Access functions for a NAND-like device when the prefetch/postwrite - * engine is enabled -- all addresses in the region behave alike: - * data is read or written to the FIFO. - */ -static uint64_t omap_gpmc_prefetch_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - uint32_t data; - if (s->prefetch.config1 & 1) { - /* The TRM doesn't define the behaviour if you read from the - * FIFO when the prefetch engine is in write mode. We choose - * to always return zero. - */ - return 0; - } - /* Note that trying to read an empty fifo repeats the last byte */ - if (s->prefetch.fifopointer) { - s->prefetch.fifopointer--; - } - data = s->prefetch.fifo[63 - s->prefetch.fifopointer]; - if (s->prefetch.fifopointer == - (64 - prefetch_threshold(s->prefetch.config1))) { - /* We've drained THRESHOLD bytes now. So deassert the - * DMA request, then refill the FIFO (which will probably - * assert it again.) - */ - omap_gpmc_dma_update(s, 0); - fill_prefetch_fifo(s); - } - omap_gpmc_int_update(s); - return data; -} - -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; - int cs = prefetch_cs(s->prefetch.config1); - if ((s->prefetch.config1 & 1) == 0) { - /* The TRM doesn't define the behaviour of writing to the - * FIFO when the prefetch engine is in read mode. We - * choose to ignore the write. - */ - return; - } - if (s->prefetch.count == 0) { - /* The TRM doesn't define the behaviour of writing to the - * FIFO if the transfer is complete. We choose to ignore. - */ - return; - } - /* The only reason we do any data buffering in postwrite - * mode is if we are talking to a 16 bit NAND device, in - * which case we need to buffer the first byte of the - * 16 bit word until the other byte arrives. - */ - int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); - if (is16bit) { - /* fifopointer alternates between 64 (waiting for first - * byte of word) and 63 (waiting for second byte) - */ - if (s->prefetch.fifopointer == 64) { - s->prefetch.fifo[0] = value; - s->prefetch.fifopointer--; - } else { - value = (value << 8) | s->prefetch.fifo[0]; - omap_nand_write(&s->cs_file[cs], 0, value, 2); - s->prefetch.count--; - s->prefetch.fifopointer = 64; - } - } else { - /* Just write the byte : fifopointer remains 64 at all times */ - omap_nand_write(&s->cs_file[cs], 0, value, 1); - s->prefetch.count--; - } - if (s->prefetch.count == 0) { - /* Final transfer: raise TERMINALCOUNTSTATUS */ - s->irqst |= 2; - s->prefetch.startengine = 0; - } - omap_gpmc_int_update(s); -} - -static const MemoryRegionOps omap_prefetch_ops = { - .read = omap_gpmc_prefetch_read, - .write = omap_gpmc_prefetch_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl.min_access_size = 1, - .impl.max_access_size = 1, -}; - -static MemoryRegion *omap_gpmc_cs_memregion(struct omap_gpmc_s *s, int cs) -{ - /* Return the MemoryRegion* to map/unmap for this chipselect */ - struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; - if (omap_gpmc_devtype(f) == OMAP_GPMC_NOR) { - return f->iomem; - } - if ((s->prefetch.config1 & 0x80) && - (prefetch_cs(s->prefetch.config1) == cs)) { - /* The prefetch engine is enabled for this CS: map the FIFO */ - return &s->prefetch.iomem; - } - return &f->nandiomem; -} - -static void omap_gpmc_cs_map(struct omap_gpmc_s *s, int cs) -{ - struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; - uint32_t mask = (f->config[6] >> 8) & 0xf; - uint32_t base = f->config[6] & 0x3f; - uint32_t size; - - if (!f->iomem && !f->dev) { - return; - } - - if (!(f->config[6] & (1 << 6))) { - /* Do nothing unless CSVALID */ - return; - } - - /* TODO: check for overlapping regions and report access errors */ - if (mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf - && !(s->accept_256 && !mask)) { - fprintf(stderr, "%s: invalid chip-select mask address (0x%x)\n", - __func__, mask); - } - - base <<= 24; - size = (0x0fffffff & ~(mask << 24)) + 1; - /* TODO: rather than setting the size of the mapping (which should be - * constant), the mask should cause wrapping of the address space, so - * that the same memory becomes accessible at every size bytes - * starting from base. */ - memory_region_init(&f->container, NULL, "omap-gpmc-file", size); - memory_region_add_subregion(&f->container, 0, - omap_gpmc_cs_memregion(s, cs)); - memory_region_add_subregion(get_system_memory(), base, - &f->container); -} - -static void omap_gpmc_cs_unmap(struct omap_gpmc_s *s, int cs) -{ - struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; - if (!(f->config[6] & (1 << 6))) { - /* Do nothing unless CSVALID */ - return; - } - if (!f->iomem && !f->dev) { - return; - } - memory_region_del_subregion(get_system_memory(), &f->container); - memory_region_del_subregion(&f->container, omap_gpmc_cs_memregion(s, cs)); - object_unparent(OBJECT(&f->container)); -} - -void omap_gpmc_reset(struct omap_gpmc_s *s) -{ - int i; - - s->sysconfig = 0; - s->irqst = 0; - s->irqen = 0; - omap_gpmc_int_update(s); - for (i = 0; i < 8; i++) { - /* This has to happen before we change any of the config - * used to determine which memory regions are mapped or unmapped. - */ - omap_gpmc_cs_unmap(s, i); - } - s->timeout = 0; - s->config = 0xa00; - s->prefetch.config1 = 0x00004000; - s->prefetch.transfercount = 0x00000000; - s->prefetch.startengine = 0; - s->prefetch.fifopointer = 0; - s->prefetch.count = 0; - for (i = 0; i < 8; i ++) { - s->cs_file[i].config[1] = 0x101001; - s->cs_file[i].config[2] = 0x020201; - s->cs_file[i].config[3] = 0x10031003; - s->cs_file[i].config[4] = 0x10f1111; - s->cs_file[i].config[5] = 0; - s->cs_file[i].config[6] = 0xf00; - /* In theory we could probe attached devices for some CFG1 - * bits here, but we just retain them across resets as they - * were set initially by omap_gpmc_attach(). - */ - if (i == 0) { - s->cs_file[i].config[0] &= 0x00433e00; - s->cs_file[i].config[6] |= 1 << 6; /* CSVALID */ - omap_gpmc_cs_map(s, i); - } else { - s->cs_file[i].config[0] &= 0x00403c00; - } - } - s->ecc_cs = 0; - s->ecc_ptr = 0; - s->ecc_cfg = 0x3fcff000; - for (i = 0; i < 9; i ++) - ecc_reset(&s->ecc[i]); -} - -static int gpmc_wordaccess_only(hwaddr addr) -{ - /* Return true if the register offset is to a register that - * only permits word width accesses. - * Non-word accesses are only OK for GPMC_NAND_DATA/ADDRESS/COMMAND - * for any chipselect. - */ - if (addr >= 0x60 && addr <= 0x1d4) { - int cs = (addr - 0x60) / 0x30; - addr -= cs * 0x30; - if (addr >= 0x7c && addr < 0x88) { - /* GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA */ - return 0; - } - } - return 1; -} - -static uint64_t omap_gpmc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - if (size != 4 && gpmc_wordaccess_only(addr)) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - return s->revision; - - case 0x010: /* GPMC_SYSCONFIG */ - return s->sysconfig; - - case 0x014: /* GPMC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* GPMC_IRQSTATUS */ - return s->irqst; - - case 0x01c: /* GPMC_IRQENABLE */ - return s->irqen; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - return s->timeout; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - return 0; - - case 0x050: /* GPMC_CONFIG */ - return s->config; - - case 0x054: /* GPMC_STATUS */ - return 0x001; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - return f->config[0]; - case 0x64: /* GPMC_CONFIG2 */ - return f->config[1]; - case 0x68: /* GPMC_CONFIG3 */ - return f->config[2]; - case 0x6c: /* GPMC_CONFIG4 */ - return f->config[3]; - case 0x70: /* GPMC_CONFIG5 */ - return f->config[4]; - case 0x74: /* GPMC_CONFIG6 */ - return f->config[5]; - case 0x78: /* GPMC_CONFIG7 */ - return f->config[6]; - case 0x84 ... 0x87: /* GPMC_NAND_DATA */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - return omap_nand_read(f, 0, size); - } - return 0; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - return s->prefetch.config1; - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - return s->prefetch.transfercount; - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - return s->prefetch.startengine; - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - /* NB: The OMAP3 TRM is inconsistent about whether the GPMC - * FIFOTHRESHOLDSTATUS bit should be set when - * FIFOPOINTER > FIFOTHRESHOLD or when it is >= FIFOTHRESHOLD. - * Apparently the underlying functional spec from which the TRM was - * created states that the behaviour is ">=", and this also - * makes more conceptual sense. - */ - return (s->prefetch.fifopointer << 24) | - ((s->prefetch.fifopointer >= - ((s->prefetch.config1 >> 8) & 0x7f) ? 1 : 0) << 16) | - s->prefetch.count; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - return s->ecc_cs; - case 0x1f8: /* GPMC_ECC_CONTROL */ - return s->ecc_ptr; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - return s->ecc_cfg; - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - cs = (addr & 0x1f) >> 2; - /* TODO: check correctness */ - return - ((s->ecc[cs].cp & 0x07) << 0) | - ((s->ecc[cs].cp & 0x38) << 13) | - ((s->ecc[cs].lp[0] & 0x1ff) << 3) | - ((s->ecc[cs].lp[1] & 0x1ff) << 19); - - case 0x230: /* GPMC_TESTMODE_CTRL */ - return 0; - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - return 0x00000000; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpmc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - if (size != 4 && gpmc_wordaccess_only(addr)) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - case 0x014: /* GPMC_SYSSTATUS */ - case 0x054: /* GPMC_STATUS */ - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - OMAP_RO_REG(addr); - break; - - case 0x010: /* GPMC_SYSCONFIG */ - if ((value >> 3) == 0x3) - fprintf(stderr, "%s: bad SDRAM idle mode %"PRIi64"\n", - __func__, value >> 3); - if (value & 2) - omap_gpmc_reset(s); - s->sysconfig = value & 0x19; - break; - - case 0x018: /* GPMC_IRQSTATUS */ - s->irqst &= ~value; - omap_gpmc_int_update(s); - break; - - case 0x01c: /* GPMC_IRQENABLE */ - s->irqen = value & 0xf03; - omap_gpmc_int_update(s); - break; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - s->timeout = value & 0x1ff1; - break; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - break; - - case 0x050: /* GPMC_CONFIG */ - s->config = value & 0xf13; - break; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - f->config[0] = value & 0xffef3e13; - break; - case 0x64: /* GPMC_CONFIG2 */ - f->config[1] = value & 0x001f1f8f; - break; - case 0x68: /* GPMC_CONFIG3 */ - f->config[2] = value & 0x001f1f8f; - break; - case 0x6c: /* GPMC_CONFIG4 */ - f->config[3] = value & 0x1f8f1f8f; - break; - case 0x70: /* GPMC_CONFIG5 */ - f->config[4] = value & 0x0f1f1f1f; - break; - case 0x74: /* GPMC_CONFIG6 */ - f->config[5] = value & 0x00000fcf; - break; - case 0x78: /* GPMC_CONFIG7 */ - if ((f->config[6] ^ value) & 0xf7f) { - omap_gpmc_cs_unmap(s, cs); - f->config[6] = value & 0x00000f7f; - omap_gpmc_cs_map(s, cs); - } - break; - case 0x7c ... 0x7f: /* GPMC_NAND_COMMAND */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - nand_setpins(f->dev, 1, 0, 0, 1, 0); /* CLE */ - omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); - } - break; - case 0x80 ... 0x83: /* GPMC_NAND_ADDRESS */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - nand_setpins(f->dev, 0, 1, 0, 1, 0); /* ALE */ - omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); - } - break; - case 0x84 ... 0x87: /* GPMC_NAND_DATA */ - if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { - omap_nand_write(f, 0, value, size); - } - break; - default: - goto bad_reg; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - if (!s->prefetch.startengine) { - uint32_t newconfig1 = value & 0x7f8f7fbf; - uint32_t changed; - changed = newconfig1 ^ s->prefetch.config1; - if (changed & (0x80 | 0x7000000)) { - /* Turning the engine on or off, or mapping it somewhere else. - * cs_map() and cs_unmap() check the prefetch config and - * overall CSVALID bits, so it is sufficient to unmap-and-map - * both the old cs and the new one. Note that we adhere to - * the "unmap/change config/map" order (and not unmap twice - * if newcs == oldcs), otherwise we'll try to delete the wrong - * memory region. - */ - int oldcs = prefetch_cs(s->prefetch.config1); - int newcs = prefetch_cs(newconfig1); - omap_gpmc_cs_unmap(s, oldcs); - if (oldcs != newcs) { - omap_gpmc_cs_unmap(s, newcs); - } - s->prefetch.config1 = newconfig1; - omap_gpmc_cs_map(s, oldcs); - if (oldcs != newcs) { - omap_gpmc_cs_map(s, newcs); - } - } else { - s->prefetch.config1 = newconfig1; - } - } - break; - - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - if (!s->prefetch.startengine) { - s->prefetch.transfercount = value & 0x3fff; - } - break; - - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - if (s->prefetch.startengine != (value & 1)) { - s->prefetch.startengine = value & 1; - if (s->prefetch.startengine) { - /* Prefetch engine start */ - s->prefetch.count = s->prefetch.transfercount; - if (s->prefetch.config1 & 1) { - /* Write */ - s->prefetch.fifopointer = 64; - } else { - /* Read */ - s->prefetch.fifopointer = 0; - fill_prefetch_fifo(s); - } - } else { - /* Prefetch engine forcibly stopped. The TRM - * doesn't define the behaviour if you do this. - * We clear the prefetch count, which means that - * we permit no more writes, and don't read any - * more data from NAND. The CPU can still drain - * the FIFO of unread data. - */ - s->prefetch.count = 0; - } - omap_gpmc_int_update(s); - } - break; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - s->ecc_cs = 0x8f; - break; - case 0x1f8: /* GPMC_ECC_CONTROL */ - if (value & (1 << 8)) - for (cs = 0; cs < 9; cs ++) - ecc_reset(&s->ecc[cs]); - s->ecc_ptr = value & 0xf; - if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { - s->ecc_ptr = 0; - s->ecc_cs &= ~1; - } - break; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - s->ecc_cfg = value & 0x3fcff1ff; - break; - case 0x230: /* GPMC_TESTMODE_CTRL */ - if (value & 7) - fprintf(stderr, "%s: test mode enable attempt\n", __func__); - break; - - default: - bad_reg: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_gpmc_ops = { - .read = omap_gpmc_read, - .write = omap_gpmc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, - hwaddr base, - qemu_irq irq, qemu_irq drq) -{ - int cs; - struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1); - - memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000); - memory_region_add_subregion(get_system_memory(), base, &s->iomem); - - s->irq = irq; - s->drq = drq; - s->accept_256 = cpu_is_omap3630(mpu); - s->revision = cpu_class_omap3(mpu) ? 0x50 : 0x20; - s->lastirq = 0; - omap_gpmc_reset(s); - - /* We have to register a different IO memory handler for each - * chip select region in case a NAND device is mapped there. We - * make the region the worst-case size of 256MB and rely on the - * container memory region in cs_map to chop it down to the actual - * guest-requested size. - */ - for (cs = 0; cs < 8; cs++) { - memory_region_init_io(&s->cs_file[cs].nandiomem, NULL, - &omap_nand_ops, - &s->cs_file[cs], - "omap-nand", - 256 * 1024 * 1024); - } - - memory_region_init_io(&s->prefetch.iomem, NULL, &omap_prefetch_ops, s, - "omap-gpmc-prefetch", 256 * 1024 * 1024); - return s; -} - -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem) -{ - struct omap_gpmc_cs_file_s *f; - assert(iomem); - - if (cs < 0 || cs >= 8) { - fprintf(stderr, "%s: bad chip-select %i\n", __func__, cs); - exit(-1); - } - f = &s->cs_file[cs]; - - omap_gpmc_cs_unmap(s, cs); - f->config[0] &= ~(0xf << 10); - f->iomem = iomem; - omap_gpmc_cs_map(s, cs); -} - -void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand) -{ - struct omap_gpmc_cs_file_s *f; - assert(nand); - - if (cs < 0 || cs >= 8) { - fprintf(stderr, "%s: bad chip-select %i\n", __func__, cs); - exit(-1); - } - f = &s->cs_file[cs]; - - omap_gpmc_cs_unmap(s, cs); - f->config[0] &= ~(0xf << 10); - f->config[0] |= (OMAP_GPMC_NAND << 10); - f->dev = nand; - if (nand_getbuswidth(f->dev) == 16) { - f->config[0] |= OMAP_GPMC_16BIT << 12; - } - omap_gpmc_cs_map(s, cs); -} diff --git a/hw/misc/omap_l4.c b/hw/misc/omap_l4.c deleted file mode 100644 index 54aeaecd69..0000000000 --- a/hw/misc/omap_l4.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * TI OMAP L4 interconnect emulation. - * - * Copyright (C) 2007-2009 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/arm/omap.h" - -struct omap_l4_s { - MemoryRegion *address_space; - hwaddr base; - int ta_num; - struct omap_target_agent_s ta[]; -}; - -struct omap_l4_s *omap_l4_init(MemoryRegion *address_space, - hwaddr base, int ta_num) -{ - struct omap_l4_s *bus = g_malloc0( - sizeof(*bus) + ta_num * sizeof(*bus->ta)); - - bus->address_space = address_space; - bus->ta_num = ta_num; - bus->base = base; - - return bus; -} - -hwaddr omap_l4_region_base(struct omap_target_agent_s *ta, - int region) -{ - return ta->bus->base + ta->start[region].offset; -} - -hwaddr omap_l4_region_size(struct omap_target_agent_s *ta, - int region) -{ - return ta->start[region].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; - - if (size != 2) { - return omap_badwidth_read16(opaque, addr); - } - - switch (addr) { - case 0x00: /* COMPONENT */ - return s->component; - - case 0x20: /* AGENT_CONTROL */ - return s->control; - - case 0x28: /* AGENT_STATUS */ - return s->status; - } - - OMAP_BAD_REG(addr); - return 0; -} - -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; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* COMPONENT */ - case 0x28: /* AGENT_STATUS */ - OMAP_RO_REG(addr); - break; - - case 0x20: /* AGENT_CONTROL */ - s->control = value & 0x01000700; - if (value & 1) /* OCP_RESET */ - s->status &= ~1; /* REQ_TIMEOUT */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_l4ta_ops = { - .read = omap_l4ta_read, - .write = omap_l4ta_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, - const struct omap_l4_region_s *regions, - const struct omap_l4_agent_info_s *agents, - int cs) -{ - int i; - struct omap_target_agent_s *ta = NULL; - const struct omap_l4_agent_info_s *info = NULL; - - for (i = 0; i < bus->ta_num; i ++) - if (agents[i].ta == cs) { - ta = &bus->ta[i]; - info = &agents[i]; - break; - } - if (!ta) { - fprintf(stderr, "%s: bad target agent (%i)\n", __func__, cs); - exit(-1); - } - - ta->bus = bus; - ta->start = ®ions[info->region]; - ta->regions = info->regions; - - ta->component = ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - ta->status = 0x00000000; - ta->control = 0x00000200; /* XXX 01000200 for L4TAO */ - - memory_region_init_io(&ta->iomem, NULL, &omap_l4ta_ops, ta, "omap.l4ta", - omap_l4_region_size(ta, info->ta_region)); - omap_l4_attach(ta, info->ta_region, &ta->iomem); - - return ta; -} - -hwaddr omap_l4_attach(struct omap_target_agent_s *ta, - int region, MemoryRegion *mr) -{ - hwaddr base; - - if (region < 0 || region >= ta->regions) { - fprintf(stderr, "%s: bad io region (%i)\n", __func__, region); - exit(-1); - } - - base = ta->bus->base + ta->start[region].offset; - if (mr) { - memory_region_add_subregion(ta->bus->address_space, base, mr); - } - - return base; -} diff --git a/hw/misc/omap_sdrc.c b/hw/misc/omap_sdrc.c deleted file mode 100644 index f2f72f6810..0000000000 --- a/hw/misc/omap_sdrc.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * TI OMAP SDRAM controller emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "hw/arm/omap.h" - -/* SDRAM Controller Subsystem */ -struct omap_sdrc_s { - MemoryRegion iomem; - uint8_t config; -}; - -void omap_sdrc_reset(struct omap_sdrc_s *s) -{ - s->config = 0x10; -} - -static uint64_t omap_sdrc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* SDRC_REVISION */ - return 0x20; - - case 0x10: /* SDRC_SYSCONFIG */ - return s->config; - - case 0x14: /* SDRC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* SDRC_CS_CFG */ - case 0x44: /* SDRC_SHARING */ - case 0x48: /* SDRC_ERR_ADDR */ - case 0x4c: /* SDRC_ERR_TYPE */ - case 0x60: /* SDRC_DLLA_SCTRL */ - case 0x64: /* SDRC_DLLA_STATUS */ - case 0x68: /* SDRC_DLLB_CTRL */ - case 0x6c: /* SDRC_DLLB_STATUS */ - case 0x70: /* SDRC_POWER */ - case 0x80: /* SDRC_MCFG_0 */ - case 0x84: /* SDRC_MR_0 */ - case 0x88: /* SDRC_EMR1_0 */ - case 0x8c: /* SDRC_EMR2_0 */ - case 0x90: /* SDRC_EMR3_0 */ - case 0x94: /* SDRC_DCDL1_CTRL */ - case 0x98: /* SDRC_DCDL2_CTRL */ - case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ - case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ - case 0xa4: /* SDRC_RFR_CTRL_0 */ - case 0xa8: /* SDRC_MANUAL_0 */ - case 0xb0: /* SDRC_MCFG_1 */ - case 0xb4: /* SDRC_MR_1 */ - case 0xb8: /* SDRC_EMR1_1 */ - case 0xbc: /* SDRC_EMR2_1 */ - case 0xc0: /* SDRC_EMR3_1 */ - case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ - case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ - case 0xd4: /* SDRC_RFR_CTRL_1 */ - case 0xd8: /* SDRC_MANUAL_1 */ - return 0x00; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sdrc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* SDRC_REVISION */ - case 0x14: /* SDRC_SYSSTATUS */ - case 0x48: /* SDRC_ERR_ADDR */ - case 0x64: /* SDRC_DLLA_STATUS */ - case 0x6c: /* SDRC_DLLB_STATUS */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* SDRC_SYSCONFIG */ - if ((value >> 3) != 0x2) - fprintf(stderr, "%s: bad SDRAM idle mode %i\n", - __func__, (unsigned)value >> 3); - if (value & 2) - omap_sdrc_reset(s); - s->config = value & 0x18; - break; - - case 0x40: /* SDRC_CS_CFG */ - case 0x44: /* SDRC_SHARING */ - case 0x4c: /* SDRC_ERR_TYPE */ - case 0x60: /* SDRC_DLLA_SCTRL */ - case 0x68: /* SDRC_DLLB_CTRL */ - case 0x70: /* SDRC_POWER */ - case 0x80: /* SDRC_MCFG_0 */ - case 0x84: /* SDRC_MR_0 */ - case 0x88: /* SDRC_EMR1_0 */ - case 0x8c: /* SDRC_EMR2_0 */ - case 0x90: /* SDRC_EMR3_0 */ - case 0x94: /* SDRC_DCDL1_CTRL */ - case 0x98: /* SDRC_DCDL2_CTRL */ - case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ - case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ - case 0xa4: /* SDRC_RFR_CTRL_0 */ - case 0xa8: /* SDRC_MANUAL_0 */ - case 0xb0: /* SDRC_MCFG_1 */ - case 0xb4: /* SDRC_MR_1 */ - case 0xb8: /* SDRC_EMR1_1 */ - case 0xbc: /* SDRC_EMR2_1 */ - case 0xc0: /* SDRC_EMR3_1 */ - case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ - case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ - case 0xd4: /* SDRC_RFR_CTRL_1 */ - case 0xd8: /* SDRC_MANUAL_1 */ - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_sdrc_ops = { - .read = omap_sdrc_read, - .write = omap_sdrc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem, - hwaddr base) -{ - struct omap_sdrc_s *s = g_new0(struct omap_sdrc_s, 1); - - omap_sdrc_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_sdrc_ops, s, "omap.sdrc", 0x1000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - return s; -} diff --git a/hw/misc/omap_tap.c b/hw/misc/omap_tap.c deleted file mode 100644 index 3f595e8df7..0000000000 --- a/hw/misc/omap_tap.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * TI OMAP TEST-Chip-level TAP emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" - -/* TEST-Chip-level TAP */ -static uint64_t omap_tap_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x204: /* IDCODE_reg */ - switch (s->mpu_model) { - case omap2420: - case omap2422: - case omap2423: - return 0x5b5d902f; /* ES 2.2 */ - case omap2430: - return 0x5b68a02f; /* ES 2.2 */ - case omap3430: - return 0x1b7ae02f; /* ES 2 */ - default: - hw_error("%s: Bad mpu model\n", __func__); - } - - case 0x208: /* PRODUCTION_ID_reg for OMAP2 */ - case 0x210: /* PRODUCTION_ID_reg for OMAP3 */ - switch (s->mpu_model) { - case omap2420: - return 0x000254f0; /* POP ESHS2.1.1 in N91/93/95, ES2 in N800 */ - case omap2422: - return 0x000400f0; - case omap2423: - return 0x000800f0; - case omap2430: - return 0x000000f0; - case omap3430: - return 0x000000f0; - default: - hw_error("%s: Bad mpu model\n", __func__); - } - - case 0x20c: - switch (s->mpu_model) { - case omap2420: - case omap2422: - case omap2423: - return 0xcafeb5d9; /* ES 2.2 */ - case omap2430: - return 0xcafeb68a; /* ES 2.2 */ - case omap3430: - return 0xcafeb7ae; /* ES 2 */ - default: - hw_error("%s: Bad mpu model\n", __func__); - } - - case 0x218: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - case 0x21c: /* DIE_ID_reg */ - return 0x54 << 24; - case 0x220: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - case 0x224: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_tap_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_tap_ops = { - .read = omap_tap_read, - .write = omap_tap_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void omap_tap_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu) -{ - memory_region_init_io(&mpu->tap_iomem, NULL, &omap_tap_ops, mpu, "omap.tap", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &mpu->tap_iomem); -} diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 03845c8de3..0b5f236a20 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; @@ -338,7 +337,7 @@ static void pci_testdev_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_OTHERS; dc->desc = "PCI Test Device"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->reset = qdev_pci_testdev_reset; + device_class_set_legacy_reset(dc, qdev_pci_testdev_reset); device_class_set_props(dc, pci_testdev_properties); } diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index ccec50f61b..9a923b7869 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -21,7 +21,6 @@ #include "hw/misc/pvpanic.h" #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) @@ -102,7 +101,7 @@ static void build_pvpanic_isa_aml(AcpiDevAmlIf *adev, Aml *scope) static Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505), DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, - PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), + PVPANIC_EVENTS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 99cf7e2041..106d03ccd6 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -20,8 +20,8 @@ #include "migration/vmstate.h" #include "hw/misc/pvpanic.h" #include "qom/object.h" -#include "hw/pci/pci.h" -#include "standard-headers/linux/pvpanic.h" +#include "hw/pci/pci_device.h" +#include "standard-headers/misc/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,14 +48,14 @@ 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); } static Property pvpanic_pci_properties[] = { DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, - PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), + PVPANIC_EVENTS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index 1540e9091a..3b893340c0 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -21,13 +21,13 @@ #include "hw/qdev-properties.h" #include "hw/misc/pvpanic.h" #include "qom/object.h" -#include "standard-headers/linux/pvpanic.h" +#include "standard-headers/misc/pvpanic.h" static void handle_event(int event) { static bool logged; - if (event & ~(PVPANIC_PANICKED | PVPANIC_CRASH_LOADED) && !logged) { + if (event & ~PVPANIC_EVENTS && !logged) { qemu_log_mask(LOG_GUEST_ERROR, "pvpanic: unknown event %#x.\n", event); logged = true; } @@ -41,6 +41,11 @@ static void handle_event(int event) qemu_system_guest_crashloaded(NULL); return; } + + if (event & PVPANIC_SHUTDOWN) { + qemu_system_guest_pvshutdown(); + return; + } } /* return supported events on read */ 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..f819fc10e6 --- /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); + + device_class_set_legacy_reset(dc, 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 6d7fdb040a..8965f5c22a 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -210,13 +210,6 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); dinfo = drive_get(IF_PFLASH, 0, 0); - if (!dinfo) { - dinfo = drive_get(IF_NONE, 0, 0); - if (dinfo) { - warn_report("using \"-drive if=none\" for the OTP is deprecated, " - "use \"-drive if=pflash\" instead."); - } - } if (dinfo) { int ret; uint64_t perm; diff --git a/hw/misc/sifive_u_prci.c b/hw/misc/sifive_u_prci.c index 5d9d446ee8..cafe6a66f4 100644 --- a/hw/misc/sifive_u_prci.c +++ b/hw/misc/sifive_u_prci.c @@ -151,7 +151,7 @@ static void sifive_u_prci_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = sifive_u_prci_realize; - dc->reset = sifive_u_prci_reset; + device_class_set_legacy_reset(dc, sifive_u_prci_reset); } static const TypeInfo sifive_u_prci_info = { diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c index e8eb71570a..c7905942fb 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), @@ -487,7 +487,7 @@ static void slavio_misc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = slavio_misc_reset; + device_class_set_legacy_reset(dc, slavio_misc_reset); dc->vmsd = &vmstate_misc; } diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c new file mode 100644 index 0000000000..26672b5b24 --- /dev/null +++ b/hw/misc/stm32_rcc.c @@ -0,0 +1,162 @@ +/* + * STM32 RCC (only reset and enable registers are implemented) + * + * Copyright (c) 2024 Román Cárdenas + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/stm32_rcc.h" + +static void stm32_rcc_reset(DeviceState *dev) +{ + STM32RccState *s = STM32_RCC(dev); + + for (int i = 0; i < STM32_RCC_NREGS; i++) { + s->regs[i] = 0; + } +} + +static uint64_t stm32_rcc_read(void *opaque, hwaddr addr, unsigned int size) +{ + STM32RccState *s = STM32_RCC(opaque); + + uint32_t value = 0; + if (addr > STM32_RCC_DCKCFGR2) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, addr); + } else { + value = s->regs[addr >> 2]; + } + trace_stm32_rcc_read(addr, value); + return value; +} + +static void stm32_rcc_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + STM32RccState *s = STM32_RCC(opaque); + uint32_t value = val64; + uint32_t prev_value, new_value, irq_offset; + + trace_stm32_rcc_write(value, addr); + + if (addr > STM32_RCC_DCKCFGR2) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, addr); + return; + } + + switch (addr) { + case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR: + prev_value = s->regs[addr / 4]; + s->regs[addr / 4] = value; + + irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32; + for (int i = 0; i < 32; i++) { + new_value = extract32(value, i, 1); + if (extract32(prev_value, i, 1) && !new_value) { + trace_stm32_rcc_pulse_reset(irq_offset + i, new_value); + qemu_set_irq(s->reset_irq[irq_offset + i], new_value); + } + } + return; + case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR: + prev_value = s->regs[addr / 4]; + s->regs[addr / 4] = value; + + irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32; + for (int i = 0; i < 32; i++) { + new_value = extract32(value, i, 1); + if (!extract32(prev_value, i, 1) && new_value) { + trace_stm32_rcc_pulse_enable(irq_offset + i, new_value); + qemu_set_irq(s->enable_irq[irq_offset + i], new_value); + } + } + return; + default: + qemu_log_mask( + LOG_UNIMP, + "%s: The RCC peripheral only supports enable and reset in QEMU\n", + __func__ + ); + s->regs[addr >> 2] = value; + } +} + +static const MemoryRegionOps stm32_rcc_ops = { + .read = stm32_rcc_read, + .write = stm32_rcc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void stm32_rcc_init(Object *obj) +{ + STM32RccState *s = STM32_RCC(obj); + + memory_region_init_io(&s->mmio, obj, &stm32_rcc_ops, s, + TYPE_STM32_RCC, STM32_RCC_PERIPHERAL_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_out(DEVICE(obj), s->reset_irq, STM32_RCC_NIRQS); + qdev_init_gpio_out(DEVICE(obj), s->enable_irq, STM32_RCC_NIRQS); + + for (int i = 0; i < STM32_RCC_NIRQS; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->reset_irq[i]); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->enable_irq[i]); + } +} + +static const VMStateDescription vmstate_stm32_rcc = { + .name = TYPE_STM32_RCC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, STM32RccState, STM32_RCC_NREGS), + VMSTATE_END_OF_LIST() + } +}; + +static void stm32_rcc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_stm32_rcc; + device_class_set_legacy_reset(dc, stm32_rcc_reset); +} + +static const TypeInfo stm32_rcc_info = { + .name = TYPE_STM32_RCC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(STM32RccState), + .instance_init = stm32_rcc_init, + .class_init = stm32_rcc_class_init, +}; + +static void stm32_rcc_register_types(void) +{ + type_register_static(&stm32_rcc_info); +} + +type_init(stm32_rcc_register_types) diff --git a/hw/misc/stm32f2xx_syscfg.c b/hw/misc/stm32f2xx_syscfg.c index 04c22c2850..6c7b722274 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: @@ -142,7 +142,7 @@ static void stm32f2xx_syscfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f2xx_syscfg_reset; + device_class_set_legacy_reset(dc, stm32f2xx_syscfg_reset); } static const TypeInfo stm32f2xx_syscfg_info = { diff --git a/hw/misc/stm32f4xx_exti.c b/hw/misc/stm32f4xx_exti.c index 02e7810046..efd996df94 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), @@ -168,7 +168,7 @@ static void stm32f4xx_exti_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f4xx_exti_reset; + device_class_set_legacy_reset(dc, stm32f4xx_exti_reset); dc->vmsd = &vmstate_stm32f4xx_exti; } diff --git a/hw/misc/stm32f4xx_syscfg.c b/hw/misc/stm32f4xx_syscfg.c index f960e4ea1e..7d0f3eb5f5 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, @@ -151,7 +151,7 @@ static void stm32f4xx_syscfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f4xx_syscfg_reset; + device_class_set_legacy_reset(dc, stm32f4xx_syscfg_reset); dc->vmsd = &vmstate_stm32f4xx_syscfg; } diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c new file mode 100644 index 0000000000..e281841dcf --- /dev/null +++ b/hw/misc/stm32l4x5_exti.c @@ -0,0 +1,293 @@ +/* + * 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_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, ResetType type) +{ + 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; + s->irq_levels[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 (level == extract32(s->irq_levels[bank], irq, 1)) { + /* No change in IRQ line state: do nothing */ + return; + } + s->irq_levels[bank] = deposit32(s->irq_levels[bank], irq, 1, level); + + /* If the interrupt is masked, pr won't be raised */ + if (!extract32(s->imr[bank], irq, 1)) { + return; + } + + /* In case of a direct line interrupt */ + if (extract32(exti_romask[bank], irq, 1)) { + qemu_set_irq(s->irq[oirq], level); + return; + } + + /* In case of a configurable interrupt */ + if ((level && extract32(s->rtsr[bank], irq, 1)) || + (!level && extract32(s->ftsr[bank], irq, 1))) { + + s->pr[bank] |= 1 << irq; + qemu_irq_pulse(s->irq[oirq]); + } +} + +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_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_LINES); +} + +static const VMStateDescription vmstate_stm32l4x5_exti = { + .name = TYPE_STM32L4X5_EXTI, + .version_id = 2, + .minimum_version_id = 2, + .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_UINT32_ARRAY(irq_levels, 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..59d428fa66 --- /dev/null +++ b/hw/misc/stm32l4x5_rcc.c @@ -0,0 +1,1474 @@ +/* + * 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, ResetType type) +{ + RccClockMuxState *s = RCC_CLOCK_MUX(obj); + clock_mux_update(s, true); +} + +static void clock_mux_reset_exit(Object *obj, ResetType type) +{ + 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, ResetType type) +{ + RccPllState *s = RCC_PLL(obj); + pll_update(s, true); +} + +static void pll_reset_exit(Object *obj, ResetType type) +{ + 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); + if (val > 0b100) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid MCOPRE value: 0x%"PRIx32"\n", + __func__, val); + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false); + } else { + clock_mux_set_factor(&s->clock_muxes[RCC_CLOCK_MUX_MCO], + 1, 1 << val); + } + + /* MCOSEL */ + val = FIELD_EX32(s->cfgr, CFGR, MCOSEL); + if (val > 0b111) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid MCOSEL value: 0x%"PRIx32"\n", + __func__, val); + clock_mux_set_enable(&s->clock_muxes[RCC_CLOCK_MUX_MCO], false); + } else { + 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, ResetType type) +{ + 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..a947a9e036 --- /dev/null +++ b/hw/misc/stm32l4x5_syscfg.c @@ -0,0 +1,282 @@ +/* + * 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/clock.h" +#include "hw/qdev-clock.h" +#include "qapi/error.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, ResetType type) +{ + 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); + s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0); +} + +static void stm32l4x5_syscfg_realize(DeviceState *dev, Error **errp) +{ + Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(dev); + if (!clock_has_source(s->clk)) { + error_setg(errp, "SYSCFG: clk input must be connected"); + return; + } +} + +static const VMStateDescription vmstate_stm32l4x5_syscfg = { + .name = TYPE_STM32L4X5_SYSCFG, + .version_id = 2, + .minimum_version_id = 2, + .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_CLOCK(clk, 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; + dc->realize = stm32l4x5_syscfg_realize; + 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 c18bc0605e..b9fbcb0924 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" @@ -70,6 +93,10 @@ 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 +aspeed_ast2700_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scu_read(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scuio_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_ast2700_scuio_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" @@ -92,6 +119,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 @@ -125,6 +156,12 @@ npcm7xx_pwm_write(const char *id, uint64_t offset, uint32_t value) "%s offset: 0 npcm7xx_pwm_update_freq(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Freq: old_freq: %u, new_freq: %u" npcm7xx_pwm_update_duty(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Duty: old_duty: %u, new_duty: %u" +# stm32_rcc.c +stm32_rcc_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +stm32_rcc_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" +stm32_rcc_pulse_enable(int line, int level) "Enable: %d to %d" +stm32_rcc_pulse_reset(int line, int level) "Reset: %d to %d" + # stm32f4xx_syscfg.c stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interrupt: GPIO: %d, Line: %d; Level: %d" stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d" @@ -132,10 +169,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" @@ -169,6 +231,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" @@ -176,6 +253,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" @@ -244,7 +325,9 @@ 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, unsigned size, uint32_t value) "AHB PnP read addr:0x%03"PRIx64" size:%u data:0x%08x" @@ -254,10 +337,6 @@ grlib_apb_pnp_read(uint64_t addr, unsigned size, uint32_t value) "APB PnP read a 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 @@ -274,3 +353,18 @@ 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" + +# aspeed_sli.c +aspeed_sli_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_sli_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_sliio_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_sliio_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 + diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 30481e1c90..66a46a7b9f 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), @@ -599,7 +599,7 @@ static void tz_mpc_class_init(ObjectClass *klass, void *data) dc->realize = tz_mpc_realize; dc->vmsd = &tz_mpc_vmstate; - dc->reset = tz_mpc_reset; + device_class_set_legacy_reset(dc, tz_mpc_reset); device_class_set_props(dc, tz_mpc_properties); } diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index acbe94400b..82ccaa014a 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), @@ -292,7 +292,7 @@ static void tz_msc_class_init(ObjectClass *klass, void *data) dc->realize = tz_msc_realize; dc->vmsd = &tz_msc_vmstate; - dc->reset = tz_msc_reset; + device_class_set_legacy_reset(dc, tz_msc_reset); device_class_set_props(dc, tz_msc_properties); } diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 36495c68e7..922dcf7f63 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), @@ -332,7 +332,7 @@ static void tz_ppc_class_init(ObjectClass *klass, void *data) dc->realize = tz_ppc_realize; dc->vmsd = &tz_ppc_vmstate; - dc->reset = tz_ppc_reset; + device_class_set_legacy_reset(dc, tz_ppc_reset); device_class_set_props(dc, tz_ppc_properties); } diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c index e75d1e7e17..aa00d6c574 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() } @@ -129,7 +129,7 @@ static void virt_ctrl_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - dc->reset = virt_ctrl_reset; + device_class_set_legacy_reset(dc, virt_ctrl_reset); dc->realize = virt_ctrl_realize; dc->vmsd = &vmstate_virt_ctrl; } 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..3fc838bd54 --- /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, ResetType type) +{ + 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..94f85814c8 --- /dev/null +++ b/hw/misc/xlnx-versal-cfu.c @@ -0,0 +1,571 @@ +/* + * 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_finalize(Object *obj) +{ + XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); + + fifo32_destroy(&s->fdro_data); +} + +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); + + device_class_set_legacy_reset(dc, 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, + .instance_finalize = cfu_fdro_finalize, + .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..f143900d5b 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)); } } @@ -310,7 +311,7 @@ static void crl_reset_enter(Object *obj, ResetType type) } } -static void crl_reset_hold(Object *obj) +static void crl_reset_hold(Object *obj, ResetType type) { XlnxVersalCRL *s = XLNX_VERSAL_CRL(obj); @@ -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..e469c04d76 100644 --- a/hw/misc/xlnx-versal-pmc-iou-slcr.c +++ b/hw/misc/xlnx-versal-pmc-iou-slcr.c @@ -1350,7 +1350,7 @@ static void xlnx_versal_pmc_iou_slcr_reset_init(Object *obj, ResetType type) } } -static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj) +static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj, ResetType type) { XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj); @@ -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..86905479b8 --- /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); + + s->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, &s->reg_array->mem); + sysbus_init_irq(sbd, &s->irq); + + s->prng = g_rand_new(); +} + +static void trng_finalize(Object *obj) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); + + register_finalize_block(s->reg_array); + g_rand_free(s->prng); + s->prng = NULL; +} + +static void trng_reset_hold(Object *obj, ResetType type) +{ + 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; + 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, + .instance_finalize = trng_finalize, +}; + +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..ad839ce7e9 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -137,7 +137,7 @@ static void xram_ctrl_reset_enter(Object *obj, ResetType type) ARRAY_FIELD_DP32(s->regs, XRAM_IMP, SIZE, s->cfg.encoded_size); } -static void xram_ctrl_reset_hold(Object *obj) +static void xram_ctrl_reset_hold(Object *obj, ResetType type) { XlnxXramCtrl *s = XLNX_XRAM_CTRL(obj); @@ -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..87e4a14067 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" @@ -151,7 +150,7 @@ static void zynqmp_apu_reset_enter(Object *obj, ResetType type) s->cpu_in_wfi = 0; } -static void zynqmp_apu_reset_hold(Object *obj) +static void zynqmp_apu_reset_hold(Object *obj, ResetType type) { XlnxZynqMPAPUCtrl *s = XLNX_ZYNQMP_APU_CTRL(obj); @@ -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..e5aba56f69 100644 --- a/hw/misc/xlnx-zynqmp-crf.c +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -191,7 +191,7 @@ static void crf_reset_enter(Object *obj, ResetType type) } } -static void crf_reset_hold(Object *obj) +static void crf_reset_hold(Object *obj, ResetType type) { XlnxZynqMPCRF *s = XLNX_ZYNQMP_CRF(obj); ir_update_irq(s); @@ -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..ad814c3a79 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -24,6 +24,8 @@ #include "hw/registerfields.h" #include "hw/qdev-clock.h" #include "qom/object.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" #ifndef ZYNQ_SLCR_ERR_DEBUG #define ZYNQ_SLCR_ERR_DEBUG 0 @@ -121,6 +123,7 @@ REG32(RST_REASON, 0x250) REG32(REBOOT_STATUS, 0x258) REG32(BOOT_MODE, 0x25c) + FIELD(BOOT_MODE, BOOT_MODE, 0, 4) REG32(APU_CTRL, 0x300) REG32(WDT_CLK_SEL, 0x304) @@ -195,6 +198,7 @@ struct ZynqSLCRState { Clock *ps_clk; Clock *uart0_ref_clk; Clock *uart1_ref_clk; + uint8_t boot_mode; }; /* @@ -285,7 +289,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()) */ @@ -371,7 +375,7 @@ static void zynq_slcr_reset_init(Object *obj, ResetType type) s->regs[R_FPGA_RST_CTRL] = 0x01F33F0F; s->regs[R_RST_REASON] = 0x00000040; - s->regs[R_BOOT_MODE] = 0x00000001; + s->regs[R_BOOT_MODE] = s->boot_mode & R_BOOT_MODE_BOOT_MODE_MASK; /* 0x700 - 0x7D4 */ for (i = 0; i < 54; i++) { @@ -416,7 +420,7 @@ static void zynq_slcr_reset_init(Object *obj, ResetType type) s->regs[R_DDRIOB + 12] = 0x00000021; } -static void zynq_slcr_reset_hold(Object *obj) +static void zynq_slcr_reset_hold(Object *obj, ResetType type) { ZynqSLCRState *s = ZYNQ_SLCR(obj); @@ -425,7 +429,7 @@ static void zynq_slcr_reset_hold(Object *obj) zynq_slcr_propagate_clocks(s); } -static void zynq_slcr_reset_exit(Object *obj) +static void zynq_slcr_reset_exit(Object *obj, ResetType type) { ZynqSLCRState *s = ZYNQ_SLCR(obj); @@ -588,6 +592,15 @@ static const ClockPortInitArray zynq_slcr_clocks = { QDEV_CLOCK_END }; +static void zynq_slcr_realize(DeviceState *dev, Error **errp) +{ + ZynqSLCRState *s = ZYNQ_SLCR(dev); + + if (s->boot_mode > 0xF) { + error_setg(errp, "Invalid boot mode %d specified", s->boot_mode); + } +} + static void zynq_slcr_init(Object *obj) { ZynqSLCRState *s = ZYNQ_SLCR(obj); @@ -603,22 +616,29 @@ 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() } }; +static Property zynq_slcr_props[] = { + DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static void zynq_slcr_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_zynq_slcr; + dc->realize = zynq_slcr_realize; rc->phases.enter = zynq_slcr_reset_init; rc->phases.hold = zynq_slcr_reset_hold; rc->phases.exit = zynq_slcr_reset_exit; + device_class_set_props(dc, zynq_slcr_props); } static const TypeInfo zynq_slcr_info = { 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 c3fed5fcbe..cdae74f503 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -671,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); } @@ -768,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); } } @@ -824,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); } @@ -850,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), @@ -880,7 +881,7 @@ static void allwinner_sun8i_emac_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = allwinner_sun8i_emac_realize; - dc->reset = allwinner_sun8i_emac_reset; + device_class_set_legacy_reset(dc, allwinner_sun8i_emac_reset); dc->vmsd = &vmstate_aw_emac; device_class_set_props(dc, allwinner_sun8i_emac_properties); } diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index ddddf35c45..c104c2588e 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; } @@ -349,7 +349,7 @@ static void aw_emac_write(void *opaque, hwaddr offset, uint64_t value, "allwinner_emac: TX length > fifo data length\n"); } if (len > 0) { - data = fifo8_pop_buf(fifo, len, &ret); + data = fifo8_pop_bufptr(fifo, len, &ret); qemu_send_packet(nc, data, ret); aw_emac_tx_reset(s, chan); /* Raise TX interrupt */ @@ -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), @@ -520,7 +521,7 @@ static void aw_emac_class_init(ObjectClass *klass, void *data) dc->realize = aw_emac_realize; device_class_set_props(dc, aw_emac_properties); - dc->reset = aw_emac_reset; + device_class_set_legacy_reset(dc, aw_emac_reset); dc->vmsd = &vmstate_aw_emac; } diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 24b3a0ff66..526739887c 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -23,11 +23,12 @@ */ #include "qemu/osdep.h" -#include /* For crc32 */ +#include /* for crc32 */ #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(), }; @@ -1701,7 +1809,7 @@ static void gem_class_init(ObjectClass *klass, void *data) dc->realize = gem_realize; device_class_set_props(dc, gem_properties); dc->vmsd = &vmstate_cadence_gem; - dc->reset = gem_reset; + device_class_set_legacy_reset(dc, gem_reset); } static const TypeInfo gem_info = { diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index 94b3a534f8..38434d3a04 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), @@ -299,7 +299,7 @@ static void kvaser_pci_class_init(ObjectClass *klass, void *data) k->class_id = 0x00ff00; dc->desc = "Kvaser PCICANx"; dc->vmsd = &vmstate_kvaser_pci; - dc->reset = kvaser_pci_reset; + device_class_set_legacy_reset(dc, kvaser_pci_reset); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index 29dc696f7c..21659b7afb 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), @@ -243,7 +243,7 @@ static void mioe3680_pci_class_init(ObjectClass *klass, void *data) dc->desc = "Mioe3680 PCICANx"; dc->vmsd = &vmstate_mioe3680_pci; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->reset = mioe3680_pci_reset; + device_class_set_legacy_reset(dc, mioe3680_pci_reset); } static const TypeInfo mioe3680_pci_info = { diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index e8e57f4f33..af21dc6855 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), @@ -244,7 +244,7 @@ static void pcm3680i_pci_class_init(ObjectClass *klass, void *data) dc->desc = "Pcm3680i PCICANx"; dc->vmsd = &vmstate_pcm3680i_pci; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->reset = pcm3680i_pci_reset; + device_class_set_legacy_reset(dc, pcm3680i_pci_reset); } static const TypeInfo pcm3680i_pci_info = { diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 73201f9139..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; @@ -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/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..65f1f82303 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), @@ -257,7 +257,7 @@ static void ctucan_pci_class_init(ObjectClass *klass, void *data) dc->desc = "CTU CAN PCI"; dc->vmsd = &vmstate_ctucan_pci; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->reset = ctucan_pci_reset; + device_class_set_legacy_reset(dc, ctucan_pci_reset); } static const TypeInfo ctucan_pci_info = { 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..e148bd7b46 --- /dev/null +++ b/hw/net/can/xlnx-versal-canfd.c @@ -0,0 +1,2081 @@ +/* + * 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 void canfd_update_irq(XlnxVersalCANFDState *s) +{ + const bool irq = (s->regs[R_INTERRUPT_STATUS_REGISTER] & + s->regs[R_INTERRUPT_ENABLE_REGISTER]) != 0; + 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; + uint32_t id_reg_val = 0; + bool is_rtr = false; + + frame->flags = 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); + + id_reg_val = s->regs[reg_num]; + if (FIELD_EX32(id_reg_val, TB_ID_REGISTER, IDE)) { + frame->can_id = (FIELD_EX32(id_reg_val, TB_ID_REGISTER, ID) << 18) | + (FIELD_EX32(id_reg_val, TB_ID_REGISTER, ID_EXT)) | + QEMU_CAN_EFF_FLAG; + if (FIELD_EX32(id_reg_val, TB_ID_REGISTER, RTR_RRS)) { + is_rtr = true; + } + } else { + frame->can_id = FIELD_EX32(id_reg_val, TB_ID_REGISTER, ID); + if (FIELD_EX32(id_reg_val, TB_ID_REGISTER, SRR_RTR_RRS)) { + is_rtr = true; + } + } + + if (FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, FDF)) { + frame->flags |= QEMU_CAN_FRMF_TYPE_FD; + + if (FIELD_EX32(dlc_reg_val, TB0_DLC_REGISTER, BRS)) { + frame->flags |= QEMU_CAN_FRMF_BRS; + } + } else { + if (is_rtr) { + frame->can_id |= QEMU_CAN_RTR_FLAG; + } + } + + frame->can_dlc = can_dlc2len(dlc_value); + + for (j = 0; j < frame->can_dlc; j++) { + val = 8 * (3 - 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 uint32_t frame_to_reg_id(const qemu_can_frame *frame) +{ + uint32_t id_reg_val = 0; + const bool is_canfd_frame = frame->flags & QEMU_CAN_FRMF_TYPE_FD; + const bool is_rtr = !is_canfd_frame && (frame->can_id & QEMU_CAN_RTR_FLAG); + + if (frame->can_id & QEMU_CAN_EFF_FLAG) { + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, ID, + (frame->can_id & QEMU_CAN_EFF_MASK) >> 18); + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, ID_EXT, + frame->can_id & QEMU_CAN_EFF_MASK); + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, IDE, 1); + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, SRR_RTR_RRS, 1); + if (is_rtr) { + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, RTR_RRS, 1); + } + } else { + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, ID, + frame->can_id & QEMU_CAN_SFF_MASK); + if (is_rtr) { + id_reg_val |= FIELD_DP32(0, RB_ID_REGISTER, SRR_RTR_RRS, 1); + } + } + + return id_reg_val; +} + +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; + 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_to_reg_id(frame); + + dlc_reg_val = FIELD_DP32(0, RB_DLC_REGISTER, DLC, can_len2dlc(dlc)); + + if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) { + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, FDF, 1); + + if (frame->flags & QEMU_CAN_FRMF_BRS) { + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, BRS, 1); + } + if (frame->flags & QEMU_CAN_FRMF_ESI) { + dlc_reg_val |= FIELD_DP32(0, RB_DLC_REGISTER, ESI, 1); + } + } + + 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_BYTES0, + frame->data[i]); + break; + case 1: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES1, + frame->data[i]); + break; + case 2: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES2, + frame->data[i]); + break; + case 3: + data_reg_val |= FIELD_DP32(0, RB_DW0_REGISTER, DATA_BYTES3, + 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]; + const uint32_t reg_id = frame_to_reg_id(frame); + + for (i = 0; i < 32; i++) { + if (acceptance_filter_status & 0x1) { + uint32_t msg_id_masked = s->regs[R_AFMR_REGISTER + 2 * i] & + reg_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 (store_index > s->cfg.rx0_fifo - 1) { + store_index -= s->cfg.rx0_fifo; + } + + 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 (store_index > s->cfg.rx1_fifo - 1) { + store_index -= s->cfg.rx1_fifo; + } + + 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 (store_index > s->cfg.tx_fifo - 1) { + store_index -= s->cfg.tx_fifo; + } + + 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; + + if (tx_reg_1->can_id == tx_reg_2->can_id) { + return (tx_reg_1->reg_num < tx_reg_2->reg_num) ? -1 : 1; + } + return (tx_reg_1->can_id < tx_reg_2->can_id) ? -1 : 1; +} + +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); + + device_class_set_legacy_reset(dc, 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 e93e6c5e19..58f1432bb3 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)) { /* @@ -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, @@ -961,7 +1006,7 @@ static void xlnx_zynqmp_can_reset_init(Object *obj, ResetType type) ptimer_transaction_commit(s->can_timer); } -static void xlnx_zynqmp_can_reset_hold(Object *obj) +static void xlnx_zynqmp_can_reset_hold(Object *obj, ResetType type) { XlnxZynqMPCANState *s = XLNX_ZYNQMP_CAN(obj); unsigned int i; @@ -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..c0977308ba 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -20,13 +20,14 @@ #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" #include "qapi/error.h" #include "qemu/module.h" #include "qemu/timer.h" -#include +#include /* for crc32 */ #include "qom/object.h" #include "trace.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() @@ -975,7 +946,7 @@ static void dp8393x_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->realize = dp8393x_realize; - dc->reset = dp8393x_reset; + device_class_set_legacy_reset(dc, dp8393x_reset); dc->vmsd = &vmstate_dp8393x; device_class_set_props(dc, dp8393x_properties); } diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 0dfdf47313..5012b96464 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, ResetType type) { - 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,7 +561,7 @@ 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); @@ -799,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 @@ -840,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); @@ -906,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; @@ -923,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; @@ -942,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; } @@ -963,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); @@ -1028,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]) @@ -1059,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) { @@ -1135,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) @@ -1193,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, @@ -1211,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) }; @@ -1238,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) }; @@ -1271,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), @@ -1414,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 */ @@ -1442,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); @@ -1456,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); @@ -1481,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; @@ -1506,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), @@ -1522,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() } @@ -1534,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), @@ -1556,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. */ @@ -1623,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, @@ -1735,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); @@ -1745,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, @@ -1776,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; @@ -1788,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 59e050742b..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,287 +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_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_FUNC_MASK 0x0000000C /* PCI Function Mask */ #define E1000_STATUS_FUNC_SHIFT 2 #define E1000_STATUS_FUNC_0 0x00000000 /* Function 0 */ @@ -827,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 */ @@ -837,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 */ @@ -857,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 */ @@ -983,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 7523e9f5d2..843892ce09 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, @@ -319,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; @@ -351,7 +352,6 @@ e1000e_init_net_peer(E1000EState *s, PCIDevice *pci_dev, uint8_t *macaddr) 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); } } @@ -512,11 +512,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, ResetType type) { - E1000EState *s = E1000E(dev); + E1000EState *s = E1000E(obj); - trace_e1000e_cb_qdev_reset(); + trace_e1000e_cb_qdev_reset_hold(); e1000e_core_reset(&s->core); @@ -553,11 +553,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), @@ -581,7 +587,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() @@ -602,7 +608,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), @@ -630,12 +636,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, @@ -645,6 +650,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() } }; @@ -663,12 +671,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; @@ -679,8 +689,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 c71d82ce1d..2e4c50ddba 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,23 +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(); - /* Clear msi_causes_pending to fire MSI eventually */ - timer->core->msi_causes_pending = 0; - 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); + } } } @@ -174,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); } @@ -282,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; } @@ -361,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); @@ -397,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) { @@ -493,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; @@ -527,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])); @@ -540,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])) { @@ -576,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: @@ -586,8 +561,7 @@ e1000e_rss_calc_hash(E1000ECore *core, type = NetPktRssIpV6Ex; break; default: - assert(false); - return 0; + g_assert_not_reached(); } return net_rx_pkt_calc_rss_hash(pkt, type, (uint8_t *) &core->mac[RSSRK]); @@ -625,23 +599,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 @@ -650,13 +640,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); } @@ -668,7 +661,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); @@ -722,7 +715,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; } } @@ -740,7 +734,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; @@ -789,24 +783,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]; @@ -815,13 +809,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; @@ -831,7 +825,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]); @@ -846,23 +840,22 @@ e1000e_ring_free_descr_num(E1000ECore *core, const E1000E_RingInfo *r) } g_assert_not_reached(); - return 0; } 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; @@ -875,7 +868,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 } }; @@ -887,13 +880,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 } }; @@ -909,7 +902,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)) { @@ -934,10 +927,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); @@ -1009,92 +1004,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; } } } @@ -1103,7 +1061,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; @@ -1124,20 +1082,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; } } @@ -1166,7 +1125,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; @@ -1178,8 +1138,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)) { @@ -1195,24 +1155,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; } @@ -1221,50 +1182,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) @@ -1272,71 +1233,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], @@ -1344,20 +1300,21 @@ 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]); } } @@ -1365,12 +1322,12 @@ struct NetRxPkt *pkt, const E1000E_RSSInfo *rss_info, static inline void e1000e_pci_dma_write_rx_desc(E1000ECore *core, dma_addr_t addr, - uint8_t *desc, dma_addr_t len) + 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 = (struct e1000_rx_desc *) desc; + struct e1000_rx_desc *d = &desc->legacy; size_t offset = offsetof(struct e1000_rx_desc, status); uint8_t status = d->status; @@ -1383,8 +1340,7 @@ e1000e_pci_dma_write_rx_desc(E1000ECore *core, dma_addr_t addr, } } else { if (core->mac[RCTL] & E1000_RCTL_DTYP_PS) { - union e1000_rx_desc_packet_split *d = - (union e1000_rx_desc_packet_split *) desc; + 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; @@ -1397,8 +1353,7 @@ e1000e_pci_dma_write_rx_desc(E1000ECore *core, dma_addr_t addr, pci_dma_write(dev, addr + offset, &status, sizeof(status)); } } else { - union e1000_rx_desc_extended *d = - (union e1000_rx_desc_extended *) desc; + 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; @@ -1414,32 +1369,32 @@ e1000e_pci_dma_write_rx_desc(E1000ECore *core, dma_addr_t addr, } } -typedef struct e1000e_ba_state_st { +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]; @@ -1448,13 +1403,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; @@ -1470,28 +1425,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; @@ -1500,18 +1441,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; @@ -1521,7 +1463,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); @@ -1542,7 +1485,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; @@ -1550,7 +1493,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; @@ -1559,7 +1502,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; @@ -1578,7 +1521,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) { @@ -1597,8 +1540,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; @@ -1614,8 +1559,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); } } @@ -1623,8 +1568,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; @@ -1636,7 +1583,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)); } } @@ -1648,9 +1595,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); - e1000e_pci_dma_write_rx_desc(core, 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); @@ -1663,27 +1610,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); } } -/* Min. octets in an ethernet frame sans FCS */ -#define MIN_BUF_SIZE 60 - ssize_t e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) { - static const int maximum_ethernet_hdr_len = (14 + 4); + 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; @@ -1699,29 +1645,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. */ @@ -1730,21 +1675,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); @@ -1757,32 +1701,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; @@ -1791,13 +1735,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 { @@ -1815,12 +1759,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); } @@ -1854,7 +1798,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 { @@ -1887,7 +1831,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) { @@ -1996,27 +1940,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; } @@ -2030,14 +1965,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 @@ -2069,7 +2003,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; @@ -2161,33 +2094,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) { @@ -2209,40 +2126,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 @@ -2268,19 +2203,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, @@ -2433,17 +2368,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) @@ -2506,29 +2443,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 @@ -2549,9 +2484,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)) { @@ -2564,7 +2496,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 @@ -2613,27 +2545,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; } @@ -2653,28 +2569,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; } @@ -2889,7 +2828,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); } } @@ -2907,6 +2846,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[] = { @@ -2922,7 +2890,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), @@ -2950,7 +2930,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), @@ -2991,7 +2970,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), @@ -3031,7 +3009,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), @@ -3058,16 +3035,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, @@ -3083,27 +3053,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, @@ -3126,22 +3094,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), @@ -3149,7 +3105,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), @@ -3185,7 +3140,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), @@ -3214,6 +3168,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, @@ -3246,18 +3212,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, @@ -3268,10 +3236,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, @@ -3280,7 +3250,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 */ @@ -3336,39 +3306,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, @@ -3381,15 +3329,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, @@ -3408,10 +3352,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); } @@ -3421,29 +3362,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), @@ -3500,8 +3448,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; @@ -3510,9 +3457,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; @@ -3523,24 +3477,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); } @@ -3556,10 +3515,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 3fdc34f753..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,13 +243,14 @@ 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); e1000x_inc_reg_if_not_full(mac, GPRC); /* TOR - Total Octets Received: @@ -223,8 +258,21 @@ e1000x_update_rx_total_stats(uint32_t *mac, * Address> field through the field, inclusively. * Always include FCS length (4) in size. */ - e1000x_grow_8reg_if_not_full(mac, TORL, data_size + 4); - e1000x_grow_8reg_if_not_full(mac, GORCL, data_size + 4); + 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 @@ -264,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..c8a88b9813 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -6,10 +6,12 @@ * Portions of the code are copies from grub / etherboot eepro100.c * and linux e100.c. * + * SPDX-License-Identifier: GPL-2.0-or-later + * * 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) version 3 or any later version. + * (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 @@ -42,7 +44,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 +1774,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 +1876,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 +1887,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 deleted file mode 100644 index 1b82aec794..0000000000 --- a/hw/net/etraxfs_eth.c +++ /dev/null @@ -1,688 +0,0 @@ -/* - * QEMU ETRAX Ethernet Controller. - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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/sysbus.h" -#include "net/net.h" -#include "hw/cris/etraxfs.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "trace.h" -#include "qom/object.h" - -#define D(x) - -/* Advertisement control register. */ -#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ -#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ -#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ -#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ - -/* - * The MDIO extensions in the TDK PHY model were reversed engineered from the - * linux driver (PHYID and Diagnostics reg). - * TODO: Add friendly names for the register nums. - */ -struct qemu_phy -{ - uint32_t regs[32]; - - int link; - - unsigned int (*read)(struct qemu_phy *phy, unsigned int req); - void (*write)(struct qemu_phy *phy, unsigned int req, unsigned int data); -}; - -static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) -{ - int regnum; - unsigned r = 0; - - regnum = req & 0x1f; - - switch (regnum) { - case 1: - if (!phy->link) { - break; - } - /* MR1. */ - /* Speeds and modes. */ - r |= (1 << 13) | (1 << 14); - r |= (1 << 11) | (1 << 12); - r |= (1 << 5); /* Autoneg complete. */ - r |= (1 << 3); /* Autoneg able. */ - r |= (1 << 2); /* link. */ - break; - case 5: - /* Link partner ability. - We are kind; always agree with whatever best mode - the guest advertises. */ - r = 1 << 14; /* Success. */ - /* Copy advertised modes. */ - r |= phy->regs[4] & (15 << 5); - /* Autoneg support. */ - r |= 1; - break; - case 18: - { - /* Diagnostics reg. */ - int duplex = 0; - int speed_100 = 0; - - if (!phy->link) { - break; - } - - /* Are we advertising 100 half or 100 duplex ? */ - speed_100 = !!(phy->regs[4] & ADVERTISE_100HALF); - speed_100 |= !!(phy->regs[4] & ADVERTISE_100FULL); - - /* Are we advertising 10 duplex or 100 duplex ? */ - duplex = !!(phy->regs[4] & ADVERTISE_100FULL); - duplex |= !!(phy->regs[4] & ADVERTISE_10FULL); - r = (speed_100 << 10) | (duplex << 11); - } - break; - - default: - r = phy->regs[regnum]; - break; - } - trace_mdio_phy_read(regnum, r); - return r; -} - -static void -tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) -{ - int regnum; - - regnum = req & 0x1f; - trace_mdio_phy_write(regnum, data); - switch (regnum) { - default: - phy->regs[regnum] = data; - break; - } -} - -static void -tdk_reset(struct qemu_phy *phy) -{ - phy->regs[0] = 0x3100; - /* PHY Id. */ - phy->regs[2] = 0x0300; - phy->regs[3] = 0xe400; - /* Autonegotiation advertisement reg. */ - phy->regs[4] = 0x01E1; - phy->link = 1; -} - -struct qemu_mdio -{ - /* bus. */ - int mdc; - int mdio; - - /* decoder. */ - enum { - PREAMBLE, - SOF, - OPC, - ADDR, - REQ, - TURNAROUND, - DATA - } state; - unsigned int drive; - - unsigned int cnt; - unsigned int addr; - unsigned int opc; - unsigned int req; - unsigned int data; - - struct qemu_phy *devs[32]; -}; - -static void -mdio_attach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = phy; -} - -#ifdef USE_THIS_DEAD_CODE -static void -mdio_detach(struct qemu_mdio *bus, struct qemu_phy *phy, unsigned int addr) -{ - bus->devs[addr & 0x1f] = NULL; -} -#endif - -static void mdio_read_req(struct qemu_mdio *bus) -{ - struct qemu_phy *phy; - - phy = bus->devs[bus->addr]; - if (phy && phy->read) { - bus->data = phy->read(phy, bus->req); - } else { - bus->data = 0xffff; - } -} - -static void mdio_write_req(struct qemu_mdio *bus) -{ - struct qemu_phy *phy; - - phy = bus->devs[bus->addr]; - if (phy && phy->write) { - phy->write(phy, bus->req, bus->data); - } -} - -static void mdio_cycle(struct qemu_mdio *bus) -{ - bus->cnt++; - - trace_mdio_bitbang(bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive); -#if 0 - if (bus->mdc) { - printf("%d", bus->mdio); - } -#endif - switch (bus->state) { - case PREAMBLE: - if (bus->mdc) { - if (bus->cnt >= (32 * 2) && !bus->mdio) { - bus->cnt = 0; - bus->state = SOF; - bus->data = 0; - } - } - break; - case SOF: - if (bus->mdc) { - if (bus->mdio != 1) { - printf("WARNING: no SOF\n"); - } - if (bus->cnt == 1*2) { - bus->cnt = 0; - bus->opc = 0; - bus->state = OPC; - } - } - break; - case OPC: - if (bus->mdc) { - bus->opc <<= 1; - bus->opc |= bus->mdio & 1; - if (bus->cnt == 2*2) { - bus->cnt = 0; - bus->addr = 0; - bus->state = ADDR; - } - } - break; - case ADDR: - if (bus->mdc) { - bus->addr <<= 1; - bus->addr |= bus->mdio & 1; - - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->req = 0; - bus->state = REQ; - } - } - break; - case REQ: - if (bus->mdc) { - bus->req <<= 1; - bus->req |= bus->mdio & 1; - if (bus->cnt == 5*2) { - bus->cnt = 0; - bus->state = TURNAROUND; - } - } - break; - case TURNAROUND: - if (bus->mdc && bus->cnt == 2*2) { - bus->mdio = 0; - bus->cnt = 0; - - if (bus->opc == 2) { - bus->drive = 1; - mdio_read_req(bus); - bus->mdio = bus->data & 1; - } - bus->state = DATA; - } - break; - case DATA: - if (!bus->mdc) { - if (bus->drive) { - bus->mdio = !!(bus->data & (1 << 15)); - bus->data <<= 1; - } - } else { - if (!bus->drive) { - bus->data <<= 1; - bus->data |= bus->mdio; - } - if (bus->cnt == 16 * 2) { - bus->cnt = 0; - bus->state = PREAMBLE; - if (!bus->drive) { - mdio_write_req(bus); - } - bus->drive = 0; - } - } - break; - default: - break; - } -} - -/* ETRAX-FS Ethernet MAC block starts here. */ - -#define RW_MA0_LO 0x00 -#define RW_MA0_HI 0x01 -#define RW_MA1_LO 0x02 -#define RW_MA1_HI 0x03 -#define RW_GA_LO 0x04 -#define RW_GA_HI 0x05 -#define RW_GEN_CTRL 0x06 -#define RW_REC_CTRL 0x07 -#define RW_TR_CTRL 0x08 -#define RW_CLR_ERR 0x09 -#define RW_MGM_CTRL 0x0a -#define R_STAT 0x0b -#define FS_ETH_MAX_REGS 0x17 - -#define TYPE_ETRAX_FS_ETH "etraxfs-eth" -OBJECT_DECLARE_SIMPLE_TYPE(ETRAXFSEthState, ETRAX_FS_ETH) - -struct ETRAXFSEthState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - NICState *nic; - NICConf conf; - - /* Two addrs in the filter. */ - uint8_t macaddr[2][6]; - uint32_t regs[FS_ETH_MAX_REGS]; - - struct etraxfs_dma_client *dma_out; - struct etraxfs_dma_client *dma_in; - - /* MDIO bus. */ - struct qemu_mdio mdio_bus; - unsigned int phyaddr; - int duplex_mismatch; - - /* PHY. */ - struct qemu_phy phy; -}; - -static void eth_validate_duplex(ETRAXFSEthState *eth) -{ - struct qemu_phy *phy; - unsigned int phy_duplex; - unsigned int mac_duplex; - int new_mm = 0; - - phy = eth->mdio_bus.devs[eth->phyaddr]; - phy_duplex = !!(phy->read(phy, 18) & (1 << 11)); - mac_duplex = !!(eth->regs[RW_REC_CTRL] & 128); - - if (mac_duplex != phy_duplex) { - new_mm = 1; - } - - if (eth->regs[RW_GEN_CTRL] & 1) { - if (new_mm != eth->duplex_mismatch) { - if (new_mm) { - printf("HW: WARNING ETH duplex mismatch MAC=%d PHY=%d\n", - mac_duplex, phy_duplex); - } else { - printf("HW: ETH duplex ok.\n"); - } - } - eth->duplex_mismatch = new_mm; - } -} - -static uint64_t -eth_read(void *opaque, hwaddr addr, unsigned int size) -{ - ETRAXFSEthState *eth = opaque; - uint32_t r = 0; - - addr >>= 2; - - switch (addr) { - case R_STAT: - r = eth->mdio_bus.mdio & 1; - break; - default: - r = eth->regs[addr]; - D(printf("%s %x\n", __func__, addr * 4)); - break; - } - return r; -} - -static void eth_update_ma(ETRAXFSEthState *eth, int ma) -{ - int reg; - int i = 0; - - ma &= 1; - - reg = RW_MA0_LO; - if (ma) { - reg = RW_MA1_LO; - } - - eth->macaddr[ma][i++] = eth->regs[reg]; - eth->macaddr[ma][i++] = eth->regs[reg] >> 8; - eth->macaddr[ma][i++] = eth->regs[reg] >> 16; - eth->macaddr[ma][i++] = eth->regs[reg] >> 24; - eth->macaddr[ma][i++] = eth->regs[reg + 1]; - eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; - - D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, - eth->macaddr[ma][0], eth->macaddr[ma][1], - eth->macaddr[ma][2], eth->macaddr[ma][3], - eth->macaddr[ma][4], eth->macaddr[ma][5])); -} - -static void -eth_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - ETRAXFSEthState *eth = opaque; - uint32_t value = val64; - - addr >>= 2; - switch (addr) { - case RW_MA0_LO: - case RW_MA0_HI: - eth->regs[addr] = value; - eth_update_ma(eth, 0); - break; - case RW_MA1_LO: - case RW_MA1_HI: - eth->regs[addr] = value; - eth_update_ma(eth, 1); - break; - - case RW_MGM_CTRL: - /* Attach an MDIO/PHY abstraction. */ - if (value & 2) { - eth->mdio_bus.mdio = value & 1; - } - if (eth->mdio_bus.mdc != (value & 4)) { - mdio_cycle(ð->mdio_bus); - eth_validate_duplex(eth); - } - eth->mdio_bus.mdc = !!(value & 4); - eth->regs[addr] = value; - break; - - case RW_REC_CTRL: - eth->regs[addr] = value; - eth_validate_duplex(eth); - break; - - default: - eth->regs[addr] = value; - D(printf("%s %x %x\n", __func__, addr, value)); - break; - } -} - -/* The ETRAX FS has a groupt address table (GAT) which works like a k=1 bloom - filter dropping group addresses we have not joined. The filter has 64 - bits (m). The has function is a simple nible xor of the group addr. */ -static int eth_match_groupaddr(ETRAXFSEthState *eth, const unsigned char *sa) -{ - unsigned int hsh; - int m_individual = eth->regs[RW_REC_CTRL] & 4; - int match; - - /* First bit on the wire of a MAC address signals multicast or - physical address. */ - if (!m_individual && !(sa[0] & 1)) { - return 0; - } - - /* Calculate the hash index for the GA registers. */ - hsh = 0; - hsh ^= (*sa) & 0x3f; - hsh ^= ((*sa) >> 6) & 0x03; - ++sa; - hsh ^= ((*sa) << 2) & 0x03c; - hsh ^= ((*sa) >> 4) & 0xf; - ++sa; - hsh ^= ((*sa) << 4) & 0x30; - hsh ^= ((*sa) >> 2) & 0x3f; - ++sa; - hsh ^= (*sa) & 0x3f; - hsh ^= ((*sa) >> 6) & 0x03; - ++sa; - hsh ^= ((*sa) << 2) & 0x03c; - hsh ^= ((*sa) >> 4) & 0xf; - ++sa; - hsh ^= ((*sa) << 4) & 0x30; - hsh ^= ((*sa) >> 2) & 0x3f; - - hsh &= 63; - if (hsh > 31) { - match = eth->regs[RW_GA_HI] & (1 << (hsh - 32)); - } else { - match = eth->regs[RW_GA_LO] & (1 << hsh); - } - D(printf("hsh=%x ga=%x.%x mtch=%d\n", hsh, - eth->regs[RW_GA_HI], eth->regs[RW_GA_LO], match)); - return match; -} - -static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - unsigned char sa_bcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); - int use_ma0 = eth->regs[RW_REC_CTRL] & 1; - int use_ma1 = eth->regs[RW_REC_CTRL] & 2; - int r_bcast = eth->regs[RW_REC_CTRL] & 8; - - if (size < 12) { - return -1; - } - - D(printf("%x.%x.%x.%x.%x.%x ma=%d %d bc=%d\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], - use_ma0, use_ma1, r_bcast)); - - /* Does the frame get through the address filters? */ - if ((!use_ma0 || memcmp(buf, eth->macaddr[0], 6)) - && (!use_ma1 || memcmp(buf, eth->macaddr[1], 6)) - && (!r_bcast || memcmp(buf, sa_bcast, 6)) - && !eth_match_groupaddr(eth, buf)) { - return size; - } - - /* FIXME: Find another way to pass on the fake csum. */ - etraxfs_dmac_input(eth->dma_in, (void *)buf, size + 4, 1); - - return size; -} - -static int eth_tx_push(void *opaque, unsigned char *buf, int len, bool eop) -{ - ETRAXFSEthState *eth = opaque; - - D(printf("%s buf=%p len=%d\n", __func__, buf, len)); - qemu_send_packet(qemu_get_queue(eth->nic), buf, len); - return len; -} - -static void eth_set_link(NetClientState *nc) -{ - ETRAXFSEthState *eth = qemu_get_nic_opaque(nc); - D(printf("%s %d\n", __func__, nc->link_down)); - eth->phy.link = !nc->link_down; -} - -static const MemoryRegionOps eth_ops = { - .read = eth_read, - .write = eth_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static NetClientInfo net_etraxfs_info = { - .type = NET_CLIENT_DRIVER_NIC, - .size = sizeof(NICState), - .receive = eth_receive, - .link_status_changed = eth_set_link, -}; - -static void etraxfs_eth_reset(DeviceState *dev) -{ - ETRAXFSEthState *s = ETRAX_FS_ETH(dev); - - memset(s->regs, 0, sizeof(s->regs)); - memset(s->macaddr, 0, sizeof(s->macaddr)); - s->duplex_mismatch = 0; - - s->mdio_bus.mdc = 0; - s->mdio_bus.mdio = 0; - s->mdio_bus.state = 0; - s->mdio_bus.drive = 0; - s->mdio_bus.cnt = 0; - s->mdio_bus.addr = 0; - s->mdio_bus.opc = 0; - s->mdio_bus.req = 0; - s->mdio_bus.data = 0; - - tdk_reset(&s->phy); -} - -static void etraxfs_eth_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - ETRAXFSEthState *s = ETRAX_FS_ETH(dev); - - if (!s->dma_out || !s->dma_in) { - error_setg(errp, "Unconnected ETRAX-FS Ethernet MAC"); - return; - } - - s->dma_out->client.push = eth_tx_push; - s->dma_out->client.opaque = s; - s->dma_in->client.opaque = s; - s->dma_in->client.pull = NULL; - - memory_region_init_io(&s->mmio, OBJECT(dev), ð_ops, s, - "etraxfs-eth", 0x5c); - sysbus_init_mmio(sbd, &s->mmio); - - 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); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->phy.read = tdk_read; - s->phy.write = tdk_write; - mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); -} - -static Property etraxfs_eth_properties[] = { - DEFINE_PROP_UINT32("phyaddr", ETRAXFSEthState, phyaddr, 1), - DEFINE_NIC_PROPERTIES(ETRAXFSEthState, conf), - DEFINE_PROP_END_OF_LIST(), -}; - -static void etraxfs_eth_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = etraxfs_eth_realize; - dc->reset = etraxfs_eth_reset; - device_class_set_props(dc, etraxfs_eth_properties); - /* Reason: dma_out, dma_in are not user settable */ - dc->user_creatable = false; -} - - -/* Instantiate an ETRAXFS Ethernet MAC. */ -DeviceState * -etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, - struct etraxfs_dma_client *dma_out, - struct etraxfs_dma_client *dma_in) -{ - DeviceState *dev; - qemu_check_nic_model(nd, "fseth"); - - dev = qdev_new("etraxfs-eth"); - qdev_set_nic_properties(dev, nd); - qdev_prop_set_uint32(dev, "phyaddr", phyaddr); - - /* - * TODO: QOM design, define a QOM interface for "I am an etraxfs - * DMA client" (which replaces the current 'struct - * etraxfs_dma_client' ad-hoc interface), implement it on the - * ethernet device, and then have QOM link properties on the DMA - * controller device so that you can pass the interface - * implementations to it. - */ - ETRAX_FS_ETH(dev)->dma_out = dma_out; - ETRAX_FS_ETH(dev)->dma_in = dma_in; - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - - return dev; -} - -static const TypeInfo etraxfs_eth_info = { - .name = TYPE_ETRAX_FS_ETH, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ETRAXFSEthState), - .class_init = etraxfs_eth_class_init, -}; - -static void etraxfs_eth_register_types(void) -{ - type_register_static(&etraxfs_eth_info); -} - -type_init(etraxfs_eth_register_types) diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index b75d8e3dce..d8076e7be4 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -29,13 +29,13 @@ #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" #include "registers.h" #include "qapi/error.h" #include "qemu/log.h" -#include "qemu/module.h" /* #define HEX_DUMP */ /* #define DEBUG_REGISTER */ @@ -99,7 +99,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 +276,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 +339,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 +390,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); @@ -423,23 +424,20 @@ static void etsec_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = etsec_realize; - dc->reset = etsec_reset; + device_class_set_legacy_reset(dc, etsec_reset); device_class_set_props(dc, etsec_properties); /* Supported by ppce500 machine */ dc->user_creatable = true; } -static const TypeInfo etsec_info = { - .name = TYPE_ETSEC_COMMON, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(eTSEC), - .class_init = etsec_class_init, - .instance_init = etsec_instance_init, +static const TypeInfo etsec_types[] = { + { + .name = TYPE_ETSEC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(eTSEC), + .class_init = etsec_class_init, + .instance_init = etsec_instance_init, + }, }; -static void etsec_register_types(void) -{ - type_register_static(&etsec_info); -} - -type_init(etsec_register_types) +DEFINE_TYPES(etsec_types) diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h index 3c625c955c..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 */ diff --git a/hw/net/fsl_etsec/miim.c b/hw/net/fsl_etsec/miim.c index 6bba01c82a..4e9169907a 100644 --- a/hw/net/fsl_etsec/miim.c +++ b/hw/net/fsl_etsec/miim.c @@ -23,18 +23,12 @@ */ #include "qemu/osdep.h" +#include "hw/net/mii.h" #include "etsec.h" #include "registers.h" /* #define DEBUG_MIIM */ -#define MIIM_CONTROL 0 -#define MIIM_STATUS 1 -#define MIIM_PHY_ID_1 2 -#define MIIM_PHY_ID_2 3 -#define MIIM_T2_STATUS 10 -#define MIIM_EXT_STATUS 15 - static void miim_read_cycle(eTSEC *etsec) { uint8_t phy; @@ -46,14 +40,14 @@ static void miim_read_cycle(eTSEC *etsec) addr = etsec->regs[MIIMADD].value & 0x1F; switch (addr) { - case MIIM_CONTROL: + case MII_BMCR: value = etsec->phy_control; break; - case MIIM_STATUS: + case MII_BMSR: value = etsec->phy_status; break; - case MIIM_T2_STATUS: - value = 0x1800; /* Local and remote receivers OK */ + case MII_STAT1000: + value = MII_STAT1000_LOK | MII_STAT1000_ROK; break; default: value = 0x0; @@ -83,8 +77,8 @@ static void miim_write_cycle(eTSEC *etsec) #endif switch (addr) { - case MIIM_CONTROL: - etsec->phy_control = value & ~(0x8100); + case MII_BMCR: + etsec->phy_control = value & ~(MII_BMCR_RESET | MII_BMCR_FD); break; default: break; @@ -140,8 +134,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..478356ee3e 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -24,8 +24,7 @@ #include "hw/qdev-properties.h" #include "migration/vmstate.h" -/* For crc32 */ -#include +#include /* for crc32 */ /* * FTGMAC100 registers @@ -56,6 +55,16 @@ #define FTGMAC100_PHYDATA 0x64 #define FTGMAC100_FCR 0x68 +/* + * FTGMAC100 registers high + * + * values below are offset by - FTGMAC100_REG_HIGH_OFFSET from datasheet + * because its memory region is start at FTGMAC100_REG_HIGH_OFFSET + */ +#define FTGMAC100_NPTXR_BADR_HIGH (0x17C - FTGMAC100_REG_HIGH_OFFSET) +#define FTGMAC100_HPTXR_BADR_HIGH (0x184 - FTGMAC100_REG_HIGH_OFFSET) +#define FTGMAC100_RXR_BADR_HIGH (0x18C - FTGMAC100_REG_HIGH_OFFSET) + /* * Interrupt status register & interrupt enable register */ @@ -165,6 +174,8 @@ #define FTGMAC100_TXDES1_TX2FIC (1 << 30) #define FTGMAC100_TXDES1_TXIC (1 << 31) +#define FTGMAC100_TXDES2_TXBUF_BADR_HI(x) (((x) >> 16) & 0x7) + /* * Receive descriptor */ @@ -198,13 +209,15 @@ #define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) #define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) +#define FTGMAC100_RXDES2_RXBUF_BADR_HI(x) (((x) >> 16) & 0x7) + /* * Receive and transmit Buffer Descriptor */ typedef struct { uint32_t des0; uint32_t des1; - uint32_t des2; /* not used by HW */ + uint32_t des2; /* used by HW 64 bits DMA */ uint32_t des3; } FTGMAC100Desc; @@ -238,7 +251,8 @@ typedef struct { */ #define FTGMAC100_MAX_FRAME_SIZE 9220 -/* Limits depending on the type of the frame +/* + * Limits depending on the type of the frame * * 9216 for Jumbo frames (+ 4 for VLAN) * 1518 for other frames (+ 4 for VLAN) @@ -514,12 +528,13 @@ out: return frame_size; } -static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, - uint32_t tx_descriptor) +static void ftgmac100_do_tx(FTGMAC100State *s, uint64_t tx_ring, + uint64_t tx_descriptor) { int frame_size = 0; uint8_t *ptr = s->frame; - uint32_t addr = tx_descriptor; + uint64_t addr = tx_descriptor; + uint64_t buf_addr = 0; uint32_t flags = 0; while (1) { @@ -533,8 +548,10 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, break; } - /* record transmit flags as they are valid only on the first - * segment */ + /* + * record transmit flags as they are valid only on the first + * segment + */ if (bd.des0 & FTGMAC100_TXDES0_FTS) { flags = bd.des1; } @@ -556,7 +573,12 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, len = sizeof(s->frame) - frame_size; } - if (dma_memory_read(&address_space_memory, bd.des3, + buf_addr = bd.des3; + if (s->dma64) { + buf_addr = deposit64(buf_addr, 32, 32, + FTGMAC100_TXDES2_TXBUF_BADR_HI(bd.des2)); + } + if (dma_memory_read(&address_space_memory, buf_addr, ptr, len, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to read packet @ 0x%x\n", __func__, bd.des3); @@ -639,7 +661,8 @@ static bool ftgmac100_can_receive(NetClientState *nc) */ static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) { - /* Polling times : + /* + * Polling times : * * Speed TIME_SEL=0 TIME_SEL=1 * @@ -722,9 +745,9 @@ static uint64_t ftgmac100_read(void *opaque, hwaddr addr, unsigned size) case FTGMAC100_MATH1: return s->math[1]; case FTGMAC100_RXR_BADR: - return s->rx_ring; + return extract64(s->rx_ring, 0, 32); case FTGMAC100_NPTXR_BADR: - return s->tx_ring; + return extract64(s->tx_ring, 0, 32); case FTGMAC100_ITC: return s->itc; case FTGMAC100_DBLAC: @@ -795,9 +818,8 @@ static void ftgmac100_write(void *opaque, hwaddr addr, HWADDR_PRIx "\n", __func__, value); return; } - - s->rx_ring = value; - s->rx_descriptor = s->rx_ring; + s->rx_ring = deposit64(s->rx_ring, 0, 32, value); + s->rx_descriptor = deposit64(s->rx_descriptor, 0, 32, value); break; case FTGMAC100_RBSR: /* DMA buffer size */ @@ -810,8 +832,8 @@ static void ftgmac100_write(void *opaque, hwaddr addr, HWADDR_PRIx "\n", __func__, value); return; } - s->tx_ring = value; - s->tx_descriptor = s->tx_ring; + s->tx_ring = deposit64(s->tx_ring, 0, 32, value); + s->tx_descriptor = deposit64(s->tx_descriptor, 0, 32, value); break; case FTGMAC100_NPTXPD: /* Trigger transmit */ @@ -910,6 +932,60 @@ static void ftgmac100_write(void *opaque, hwaddr addr, ftgmac100_update_irq(s); } +static uint64_t ftgmac100_high_read(void *opaque, hwaddr addr, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + uint64_t val = 0; + + switch (addr) { + case FTGMAC100_NPTXR_BADR_HIGH: + val = extract64(s->tx_ring, 32, 32); + break; + case FTGMAC100_HPTXR_BADR_HIGH: + /* High Priority Transmit Ring Base High Address */ + qemu_log_mask(LOG_UNIMP, "%s: read to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + case FTGMAC100_RXR_BADR_HIGH: + val = extract64(s->rx_ring, 32, 32); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + return val; +} + +static void ftgmac100_high_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FTGMAC100State *s = FTGMAC100(opaque); + + switch (addr) { + case FTGMAC100_NPTXR_BADR_HIGH: + s->tx_ring = deposit64(s->tx_ring, 32, 32, value); + s->tx_descriptor = deposit64(s->tx_descriptor, 32, 32, value); + break; + case FTGMAC100_HPTXR_BADR_HIGH: + /* High Priority Transmit Ring Base High Address */ + qemu_log_mask(LOG_UNIMP, "%s: write to unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + case FTGMAC100_RXR_BADR_HIGH: + s->rx_ring = deposit64(s->rx_ring, 32, 32, value); + s->rx_descriptor = deposit64(s->rx_descriptor, 32, 32, value); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + + ftgmac100_update_irq(s); +} + static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len) { unsigned mcast_idx; @@ -953,9 +1029,9 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); FTGMAC100Desc bd; uint32_t flags = 0; - uint32_t addr; + uint64_t addr; uint32_t crc; - uint32_t buf_addr; + uint64_t buf_addr = 0; uint8_t *crc_ptr; uint32_t buf_len; size_t size = len; @@ -968,21 +1044,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. */ @@ -1028,7 +1096,12 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, if (size < 4) { buf_len += size - 4; } + buf_addr = bd.des3; + if (s->dma64) { + buf_addr = deposit64(buf_addr, 32, 32, + FTGMAC100_RXDES2_RXBUF_BADR_HI(bd.des2)); + } if (first && proto == ETH_P_VLAN && buf_len >= 18) { bd.des1 = lduw_be_p(buf + 14) | FTGMAC100_RXDES1_VLANTAG_AVAIL; @@ -1082,6 +1155,14 @@ static const MemoryRegionOps ftgmac100_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const MemoryRegionOps ftgmac100_high_ops = { + .read = ftgmac100_high_read, + .write = ftgmac100_high_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void ftgmac100_cleanup(NetClientState *nc) { FTGMAC100State *s = FTGMAC100(qemu_get_nic_opaque(nc)); @@ -1111,31 +1192,42 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) s->rxdes0_edorr = FTGMAC100_RXDES0_EDORR; } - memory_region_init_io(&s->iomem, OBJECT(dev), &ftgmac100_ops, s, - TYPE_FTGMAC100, 0x2000); - sysbus_init_mmio(sbd, &s->iomem); + memory_region_init(&s->iomem_container, OBJECT(s), + TYPE_FTGMAC100 ".container", FTGMAC100_MEM_SIZE); + sysbus_init_mmio(sbd, &s->iomem_container); + + memory_region_init_io(&s->iomem, OBJECT(s), &ftgmac100_ops, s, + TYPE_FTGMAC100 ".regs", FTGMAC100_REG_MEM_SIZE); + memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + + if (s->dma64) { + memory_region_init_io(&s->iomem_high, OBJECT(s), &ftgmac100_high_ops, + s, TYPE_FTGMAC100 ".regs.high", + FTGMAC100_REG_HIGH_MEM_SIZE); + memory_region_add_subregion(&s->iomem_container, + FTGMAC100_REG_HIGH_OFFSET, + &s->iomem_high); + } + sysbus_init_irq(sbd, &s->irq); 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); } static const VMStateDescription vmstate_ftgmac100 = { .name = TYPE_FTGMAC100, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(irq_state, FTGMAC100State), VMSTATE_UINT32(isr, FTGMAC100State), VMSTATE_UINT32(ier, FTGMAC100State), VMSTATE_UINT32(rx_enabled, FTGMAC100State), - VMSTATE_UINT32(rx_ring, FTGMAC100State), VMSTATE_UINT32(rbsr, FTGMAC100State), - VMSTATE_UINT32(tx_ring, FTGMAC100State), - VMSTATE_UINT32(rx_descriptor, FTGMAC100State), - VMSTATE_UINT32(tx_descriptor, FTGMAC100State), VMSTATE_UINT32_ARRAY(math, FTGMAC100State, 2), VMSTATE_UINT32(itc, FTGMAC100State), VMSTATE_UINT32(aptcr, FTGMAC100State), @@ -1154,6 +1246,10 @@ static const VMStateDescription vmstate_ftgmac100 = { VMSTATE_UINT32(phy_int_mask, FTGMAC100State), VMSTATE_UINT32(txdes0_edotr, FTGMAC100State), VMSTATE_UINT32(rxdes0_edorr, FTGMAC100State), + VMSTATE_UINT64(rx_ring, FTGMAC100State), + VMSTATE_UINT64(tx_ring, FTGMAC100State), + VMSTATE_UINT64(rx_descriptor, FTGMAC100State), + VMSTATE_UINT64(tx_descriptor, FTGMAC100State), VMSTATE_END_OF_LIST() } }; @@ -1161,6 +1257,7 @@ static const VMStateDescription vmstate_ftgmac100 = { static Property ftgmac100_properties[] = { DEFINE_PROP_BOOL("aspeed", FTGMAC100State, aspeed, false), DEFINE_NIC_PROPERTIES(FTGMAC100State, conf), + DEFINE_PROP_BOOL("dma64", FTGMAC100State, dma64, false), DEFINE_PROP_END_OF_LIST(), }; @@ -1169,7 +1266,7 @@ static void ftgmac100_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_ftgmac100; - dc->reset = ftgmac100_reset; + device_class_set_legacy_reset(dc, ftgmac100_reset); device_class_set_props(dc, ftgmac100_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->realize = ftgmac100_realize; @@ -1311,7 +1408,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() @@ -1329,7 +1426,7 @@ static void aspeed_mii_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_aspeed_mii; - dc->reset = aspeed_mii_reset; + device_class_set_legacy_reset(dc, aspeed_mii_reset); dc->realize = aspeed_mii_realize; dc->desc = "Aspeed MII controller"; device_class_set_props(dc, aspeed_mii_properties); diff --git a/hw/net/i82596.c b/hw/net/i82596.c index ec21e2699a..ee919dab3c 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -15,10 +15,11 @@ #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" -#include /* For crc32 */ +#include /* for crc32 */ #if defined(ENABLE_DEBUG) #define DBG(x) x @@ -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) @@ -285,7 +282,7 @@ static void command_loop(I82596State *s) case CmdDump: case CmdDiagnose: printf("FIXME Command %d !!\n", cmd & 7); - assert(0); + g_assert_not_reached(); } /* update status */ @@ -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..b92bba402e --- /dev/null +++ b/hw/net/igb.c @@ -0,0 +1,647 @@ +/* + * 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)); + } +} + +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, ResetType type) +{ + 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..5dffa12c64 --- /dev/null +++ b/hw/net/igb_core.c @@ -0,0 +1,4551 @@ +/* + * 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: + g_assert_not_reached(); + } + + 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(); +} + +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..4dc4c31da2 --- /dev/null +++ b/hw/net/igb_regs.h @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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..21a97d4d61 --- /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, ResetType type) +{ + 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 8c11b237de..6294d29202 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -33,15 +33,14 @@ #include "net/eth.h" #include "trace.h" -/* For crc32 */ -#include +#include /* for crc32 */ #define IMX_MAX_DESC 1024 static const char *imx_default_reg_name(IMXFECState *s, uint32_t index) { static char tmp[20]; - sprintf(tmp, "index %d", index); + snprintf(tmp, sizeof(tmp), "index %d", index); return tmp; } @@ -195,7 +194,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 +205,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 +216,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 +281,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 +350,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); @@ -1068,9 +1083,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 +1179,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 +1333,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 +1342,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(), }; @@ -1335,7 +1353,7 @@ static void imx_eth_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_imx_eth; - dc->reset = imx_eth_reset; + device_class_set_legacy_reset(dc, imx_eth_reset); device_class_set_props(dc, imx_eth_properties); dc->realize = imx_eth_realize; dc->desc = "i.MX FEC/ENET Ethernet Controller"; diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index f1cba55967..db28a0ef30 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" @@ -23,8 +22,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -/* For crc32 */ -#include +#include /* for crc32 */ #include "qom/object.h" //#define DEBUG_LAN9118 @@ -32,12 +30,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 +149,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 +171,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 +187,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 +276,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 +549,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; } @@ -798,8 +798,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; @@ -848,7 +862,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; } } @@ -876,7 +891,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); } } @@ -1209,7 +1225,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, @@ -1324,7 +1341,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; } @@ -1362,7 +1380,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++) { @@ -1388,7 +1407,7 @@ static void lan9118_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = lan9118_reset; + device_class_set_legacy_reset(dc, lan9118_reset); device_class_set_props(dc, lan9118_properties); dc->vmsd = &vmstate_lan9118; dc->realize = lan9118_realize; @@ -1408,14 +1427,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..269615b452 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() } @@ -151,7 +151,7 @@ static void lance_class_init(ObjectClass *klass, void *data) dc->realize = lance_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->fw_name = "ethernet"; - dc->reset = lance_reset; + device_class_set_legacy_reset(dc, lance_reset); dc->vmsd = &vmstate_lance; device_class_set_props(dc, lance_properties); } diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index e37f7fabe9..183fab8712 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 */ @@ -167,7 +170,7 @@ static void lasi_82596_class_init(ObjectClass *klass, void *data) dc->realize = lasi_82596_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->fw_name = "ethernet"; - dc->reset = lasi_82596_reset; + device_class_set_legacy_reset(dc, lasi_82596_reset); dc->vmsd = &vmstate_lasi_82596; dc->user_creatable = false; device_class_set_props(dc, lasi_82596_properties); diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 8aa27bd322..037cd2028e 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -16,8 +16,7 @@ #include "hw/net/mii.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" -/* For crc32 */ -#include +#include /* for crc32 */ //#define DEBUG_FEC 1 @@ -571,7 +570,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 +642,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); } @@ -672,7 +672,7 @@ static void mcf_fec_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->realize = mcf_fec_realize; dc->desc = "MCF Fast Ethernet Controller network device"; - dc->reset = mcf_fec_reset; + device_class_set_legacy_reset(dc, mcf_fec_reset); device_class_set_props(dc, mcf_fec_properties); } diff --git a/hw/net/meson.build b/hw/net/meson.build index ebac261542..00a9e9dd51 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -1,72 +1,72 @@ -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_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..31bbd6fb89 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); } @@ -277,7 +278,7 @@ static void mipsnet_class_init(ObjectClass *klass, void *data) dc->realize = mipsnet_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "MIPS Simulator network device"; - dc->reset = mipsnet_sysbus_reset; + device_class_set_legacy_reset(dc, mipsnet_sysbus_reset); dc->vmsd = &vmstate_mipsnet; device_class_set_props(dc, mipsnet_properties); } diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index db3a04deb1..d28fc6c570 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -530,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); } @@ -556,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), @@ -570,7 +571,7 @@ static void msf2_emac_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = msf2_emac_realize; - dc->reset = msf2_emac_reset; + device_class_set_legacy_reset(dc, msf2_emac_reset); dc->vmsd = &vmstate_msf2_emac; device_class_set_props(dc, msf2_emac_properties); } 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 3f31d04efb..b482c5f3af 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -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; @@ -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..f87b6f046c 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,38 +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; -} - -size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt) -{ - assert(pkt); - return pkt->l3hdr_off; + *hasip4 = pkt->hasip4; + *hasip6 = pkt->hasip6; + *l4hdr_proto = pkt->l4hdr_info.proto; } size_t net_rx_pkt_get_l4_hdr_offset(struct NetRxPkt *pkt) @@ -247,11 +231,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,65 +312,64 @@ 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); break; default: - assert(false); - break; + g_assert_not_reached(); } net_toeplitz_key_init(&key_data, key); @@ -406,7 +384,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 +395,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 +406,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; } @@ -442,13 +420,6 @@ struct iovec *net_rx_pkt_get_iovec(struct NetRxPkt *pkt) return pkt->vec; } -uint16_t net_rx_pkt_get_iovec_len(struct NetRxPkt *pkt) -{ - assert(pkt); - - return pkt->vec_len; -} - void net_rx_pkt_set_vhdr(struct NetRxPkt *pkt, struct virtio_net_hdr *vhdr) { @@ -465,6 +436,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 +450,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 +465,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 +496,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 +510,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 +538,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 +618,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..ea077f5fdb 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,34 +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); - -/** -* fetches L3 header offset -* -* @pkt: packet -* -*/ -size_t net_rx_pkt_get_l3_hdr_offset(struct NetRxPkt *pkt); + bool *hasip4, bool *hasip6, + EthL4HdrProto *l4hdr_proto); /** * fetches L4 header offset @@ -119,15 +111,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 +197,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 +215,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 @@ -284,15 +259,6 @@ net_rx_pkt_attach_data(struct NetRxPkt *pkt, const void *data, */ struct iovec *net_rx_pkt_get_iovec(struct NetRxPkt *pkt); -/** -* returns io vector length that holds the attached data -* -* @pkt: packet -* @ret: IOVec length -* -*/ -uint16_t net_rx_pkt_get_iovec_len(struct NetRxPkt *pkt); - /** * prints rx packet data if debug is enabled * @@ -322,6 +288,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..1f79b82b77 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_vnet_hdr_len(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_vnet_hdr_len(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..7307a13400 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -29,8 +29,7 @@ #include "qemu/osdep.h" -/* For crc32 */ -#include +#include /* for crc32 */ #include "hw/irq.h" #include "hw/qdev-clock.h" @@ -98,6 +97,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 +113,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 +443,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 +684,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 +820,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 +836,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), @@ -840,7 +858,7 @@ static void npcm7xx_emc_class_init(ObjectClass *klass, void *data) dc->desc = "NPCM7xx EMC Controller"; dc->realize = npcm7xx_emc_realize; dc->unrealize = npcm7xx_emc_unrealize; - dc->reset = npcm7xx_emc_reset; + device_class_set_legacy_reset(dc, npcm7xx_emc_reset); dc->vmsd = &vmstate_npcm7xx_emc; device_class_set_props(dc, npcm7xx_emc_properties); } diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c new file mode 100644 index 0000000000..685905f9e2 --- /dev/null +++ b/hw/net/npcm_gmac.c @@ -0,0 +1,941 @@ +/* + * 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)) { + trace_npcm_gmac_tx_desc_owner(DEVICE(gmac)->canonical_path, + 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; + device_class_set_legacy_reset(dc, 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..2c0ebda100 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) @@ -754,7 +755,7 @@ static void open_eth_class_init(ObjectClass *klass, void *data) dc->realize = sysbus_open_eth_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "Opencores 10/100 Mbit Ethernet"; - dc->reset = qdev_open_eth_reset; + device_class_set_legacy_reset(dc, qdev_open_eth_reset); device_class_set_props(dc, open_eth_properties); } diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 95d27102aa..6190b76916 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() @@ -269,7 +269,7 @@ static void pcnet_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_AMD_LANCE; k->revision = 0x10; k->class_id = PCI_CLASS_NETWORK_ETHERNET; - dc->reset = pci_reset; + device_class_set_legacy_reset(dc, pci_reset); dc->vmsd = &vmstate_pci_pcnet; device_class_set_props(dc, pcnet_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index e63e524913..ad675ab29d 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -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/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 281d43e6cf..5e74acc969 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" @@ -134,11 +134,6 @@ RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) return list; } -uint32_t rocker_fp_ports(Rocker *r) -{ - return r->fp_ports; -} - static uint32_t rocker_get_pport_by_tx_ring(Rocker *r, DescRing *ring) { @@ -815,7 +810,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 +852,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 +871,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 +890,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 +982,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) { @@ -1060,7 +1055,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 +1110,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 +1131,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 +1160,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 +1169,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) { @@ -1494,7 +1489,7 @@ static void rocker_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_NETWORK_OTHER; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "Rocker Switch"; - dc->reset = rocker_reset; + device_class_set_legacy_reset(dc, rocker_reset); device_class_set_props(dc, rocker_properties); dc->vmsd = &rocker_vmsd; } diff --git a/hw/net/rocker/rocker.h b/hw/net/rocker/rocker.h index f85354d9d1..6e0962f47a 100644 --- a/hw/net/rocker/rocker.h +++ b/hw/net/rocker/rocker.h @@ -72,7 +72,6 @@ DECLARE_INSTANCE_CHECKER(Rocker, ROCKER, TYPE_ROCKER) Rocker *rocker_find(const char *name); -uint32_t rocker_fp_ports(Rocker *r); int rocker_event_link_changed(Rocker *r, uint32_t pport, bool link_up); int rocker_event_mac_vlan_seen(Rocker *r, uint32_t pport, uint8_t *addr, uint16_t vlan_id); 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..3378f63110 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, @@ -1635,8 +1635,8 @@ static int of_dpa_cmd_add_multicast_routing(OfDpaFlow *flow, return ROCKER_OK; } -static int of_dpa_cmd_add_acl_ip(OfDpaFlowKey *key, OfDpaFlowKey *mask, - RockerTlv **flow_tlvs) +static void of_dpa_cmd_add_acl_ip(OfDpaFlowKey *key, OfDpaFlowKey *mask, + RockerTlv **flow_tlvs) { key->width = FLOW_KEY_WIDTH(ip.tos); @@ -1669,8 +1669,6 @@ static int of_dpa_cmd_add_acl_ip(OfDpaFlowKey *key, OfDpaFlowKey *mask, mask->ip.tos |= rocker_tlv_get_u8(flow_tlvs[ROCKER_TLV_OF_DPA_IP_ECN_MASK]) << 6; } - - return ROCKER_OK; } static int of_dpa_cmd_add_acl(OfDpaFlow *flow, RockerTlv **flow_tlvs) @@ -1689,7 +1687,6 @@ static int of_dpa_cmd_add_acl(OfDpaFlow *flow, RockerTlv **flow_tlvs) ACL_MODE_ANY_VLAN, ACL_MODE_ANY_TENANT, } mode = ACL_MODE_UNKNOWN; - int err = ROCKER_OK; if (!flow_tlvs[ROCKER_TLV_OF_DPA_IN_PPORT] || !flow_tlvs[ROCKER_TLV_OF_DPA_ETHERTYPE]) { @@ -1776,14 +1773,10 @@ static int of_dpa_cmd_add_acl(OfDpaFlow *flow, RockerTlv **flow_tlvs) switch (ntohs(key->eth.type)) { case 0x0800: case 0x86dd: - err = of_dpa_cmd_add_acl_ip(key, mask, flow_tlvs); + of_dpa_cmd_add_acl_ip(key, mask, flow_tlvs); break; } - if (err) { - return err; - } - if (flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]) { action->write.group_id = rocker_tlv_get_le32(flow_tlvs[ROCKER_TLV_OF_DPA_GROUP_ID]); @@ -2348,23 +2341,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 +2389,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 +2489,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 +2518,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 eb679d7c40..bc56075c0d 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -48,12 +48,10 @@ * 2011-Mar-22 Benjamin Poirier: Implemented VLAN offloading */ -/* For crc32 */ - #include "qemu/osdep.h" -#include +#include /* for crc32 */ -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "sysemu/dma.h" @@ -100,7 +98,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, @@ -826,7 +824,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 }; @@ -938,17 +935,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)) { @@ -2750,7 +2736,11 @@ static void rtl8139_io_writeb(void *opaque, uint8_t addr, uint32_t val) } break; - + case RxConfig: + DPRINTF("RxConfig write(b) val=0x%02x\n", val); + rtl8139_RxConfig_write(s, + (rtl8139_RxConfig_read(s) & 0xFFFFFF00) | val); + break; default: DPRINTF("not implemented write(b) addr=0x%x val=0x%02x\n", addr, val); @@ -3162,7 +3152,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() } }; @@ -3185,7 +3175,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), @@ -3269,7 +3259,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 } @@ -3400,7 +3390,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; @@ -3436,7 +3427,7 @@ static void rtl8139_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_REALTEK_8139; k->revision = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ k->class_id = PCI_CLASS_NETWORK_ETHERNET; - dc->reset = rtl8139_reset; + device_class_set_legacy_reset(dc, rtl8139_reset); dc->vmsd = &vmstate_rtl8139; device_class_set_props(dc, rtl8139_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index ad778cd8fc..180ba5c791 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -17,8 +17,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" -/* For crc32 */ -#include +#include /* for crc32 */ #include "qom/object.h" /* Number of 2k memory pages available. */ @@ -62,7 +61,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 +360,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 +782,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. */ } @@ -798,7 +798,7 @@ static void smc91c111_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = smc91c111_realize; - dc->reset = smc91c111_reset; + device_class_set_legacy_reset(dc, smc91c111_reset); dc->vmsd = &vmstate_smc91c111; device_class_set_props(dc, smc91c111_properties); } @@ -817,14 +817,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..8af33d91b6 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, @@ -769,6 +770,12 @@ static target_ulong h_change_logical_lan_mac(PowerPCCPU *cpu, SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); int i; + if (!dev) { + hcall_dprintf("H_CHANGE_LOGICAL_LAN_MAC called when " + "no NIC is present\n"); + return H_PARAMETER; + } + for (i = 0; i < ETH_ALEN; i++) { dev->nicconf.macaddr.a[ETH_ALEN - i - 1] = macaddr & 0xff; macaddr >>= 8; @@ -799,7 +806,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 +819,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 +831,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 +843,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..9ebff296c4 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -15,7 +15,7 @@ #include "net/net.h" #include "qemu/log.h" #include "qemu/module.h" -#include +#include /* for crc32 */ #include "qom/object.h" //#define DEBUG_STELLARIS_ENET 1 @@ -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); } @@ -506,7 +507,7 @@ static void stellaris_enet_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = stellaris_enet_realize; - dc->reset = stellaris_enet_reset; + device_class_set_legacy_reset(dc, stellaris_enet_reset); device_class_set_props(dc, stellaris_enet_properties); dc->vmsd = &vmstate_stellaris_enet; } diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 3684a4d733..67087e9842 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), @@ -1429,7 +1467,7 @@ static void sungem_class_init(ObjectClass *klass, void *data) k->revision = 0x01; k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_sungem; - dc->reset = sungem_reset; + device_class_set_legacy_reset(dc, sungem_reset); device_class_set_props(dc, sungem_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index fc34905f87..0e6c655a5b 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)), @@ -958,7 +948,7 @@ static void sunhme_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_SUN_HME; k->class_id = PCI_CLASS_NETWORK_ETHERNET; dc->vmsd = &vmstate_hme; - dc->reset = sunhme_reset; + device_class_set_legacy_reset(dc, sunhme_reset); device_class_set_props(dc, sunhme_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/trace-events b/hw/net/trace-events index 4c0ec3fda1..d0f1d8c0fb 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -10,11 +10,6 @@ allwinner_sun8i_emac_set_link(bool active) "Set link: active=%u" allwinner_sun8i_emac_read(uint64_t offset, uint64_t val) "MMIO read: offset=0x%" PRIx64 " value=0x%" PRIx64 allwinner_sun8i_emac_write(uint64_t offset, uint64_t val) "MMIO write: offset=0x%" PRIx64 " value=0x%" PRIx64 -# etraxfs_eth.c -mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" -mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" -mdio_bitbang(bool mdc, bool mdio, int state, uint16_t cnt, unsigned int drive) "bitbang mdc=%u mdio=%u state=%d cnt=%u drv=%d" - # lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" @@ -61,7 +56,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 +101,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 +151,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 +160,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 +172,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 +194,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 +226,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 +239,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 +260,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 +349,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 @@ -370,9 +394,11 @@ virtio_net_announce_notify(void) "" virtio_net_announce_timer(int round) "%d" virtio_net_handle_announce(int round) "%d" virtio_net_post_load_device(void) -virtio_net_rss_disable(void) -virtio_net_rss_error(const char *msg, uint32_t value) "%s, value 0x%08x" -virtio_net_rss_enable(uint32_t p1, uint16_t p2, uint8_t p3) "hashes 0x%x, table of %d, key of %d" +virtio_net_rss_load(void *nic, size_t nfds, void *fds) "nic=%p nfds=%zu fds=%p" +virtio_net_rss_attach_ebpf(void *nic, int prog_fd) "nic=%p prog-fd=%d" +virtio_net_rss_disable(void *nic) "nic=%p" +virtio_net_rss_error(void *nic, const char *msg, uint32_t value) "nic=%p msg=%s, value 0x%08x" +virtio_net_rss_enable(void *nic, uint32_t p1, uint16_t p2, uint8_t p3) "nic=%p hashes 0x%x, table of %d, key of %d" # tulip.c tulip_reg_write(uint64_t addr, const char *name, int size, uint64_t val) "addr 0x%02"PRIx64" (%s) size %d value 0x%08"PRIx64 @@ -438,6 +464,26 @@ 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_gmac_tx_desc_owner(const char* name, uint32_t desc_addr) "%s: TX Descriptor @0x%04" PRIX32 " is owned by software" + +# 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 +500,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 c2b3b1bdfa..9df3e17162 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), @@ -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, }; @@ -983,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); } @@ -1020,12 +1021,12 @@ 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; device_class_set_props(dc, tulip_properties); - dc->reset = tulip_qdev_reset; + device_class_set_legacy_reset(dc, tulip_qdev_reset); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 9f7daae99c..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; @@ -113,3 +122,8 @@ int vhost_net_virtqueue_restart(VirtIODevice *vdev, NetClientState *nc, { 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 043058ff43..891f235a0a 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -48,6 +48,9 @@ static const int kernel_feature_bits[] = { VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_NET_F_RSC_EXT, VIRTIO_NET_F_HASH_REPORT, VHOST_INVALID_FEATURE_BIT }; @@ -55,6 +58,7 @@ static const int kernel_feature_bits[] = { /* Features supported by others. */ static const int user_feature_bits[] = { VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, @@ -76,8 +80,13 @@ static const int user_feature_bits[] = { VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_PACKED, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, VIRTIO_NET_F_RSS, + VIRTIO_NET_F_RSC_EXT, 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, @@ -144,6 +153,157 @@ 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 void vhost_net_disable_notifiers_nvhosts(VirtIODevice *dev, + NetClientState *ncs, int data_queue_pairs, int nvhosts) +{ + VirtIONet *n = VIRTIO_NET(dev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); + struct vhost_net *net; + struct vhost_dev *hdev; + int r, i, j; + NetClientState *peer; + + /* + * 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 < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queue_pairs); + } + + net = get_vhost_net(peer); + hdev = &net->dev; + for (j = 0; j < hdev->nvqs; j++) { + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), + hdev->vq_index + j, + false); + if (r < 0) { + error_report("vhost %d VQ %d notifier cleanup failed: %d", + i, j, -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 < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queue_pairs); + } + + net = get_vhost_net(peer); + hdev = &net->dev; + for (j = 0; j < hdev->nvqs; j++) { + virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), + hdev->vq_index + j); + } + virtio_device_release_ioeventfd(dev); + } +} + +static int vhost_net_enable_notifiers(VirtIODevice *dev, + NetClientState *ncs, int data_queue_pairs, int cvq) +{ + VirtIONet *n = VIRTIO_NET(dev); + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); + int nvhosts = data_queue_pairs + cvq; + struct vhost_net *net; + struct vhost_dev *hdev; + int r, i, j, k; + NetClientState *peer; + + /* + * We will pass the notifiers to the kernel, make sure that QEMU + * doesn't interfere. + */ + for (i = 0; i < nvhosts; i++) { + r = virtio_device_grab_ioeventfd(dev); + if (r < 0) { + error_report("vhost %d binding does not support host notifiers", i); + for (k = 0; k < i; k++) { + virtio_device_release_ioeventfd(dev); + } + 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 < nvhosts; i++) { + if (i < data_queue_pairs) { + peer = qemu_get_peer(ncs, i); + } else { + peer = qemu_get_peer(ncs, n->max_queue_pairs); + } + + net = get_vhost_net(peer); + hdev = &net->dev; + + for (j = 0; j < hdev->nvqs; j++) { + r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), + hdev->vq_index + j, + true); + if (r < 0) { + error_report("vhost %d VQ %d notifier binding failed: %d", + i, j, -r); + memory_region_transaction_commit(); + vhost_dev_disable_notifiers_nvqs(hdev, dev, j); + goto fail_nvhosts; + } + } + } + + memory_region_transaction_commit(); + + return 0; +fail_nvhosts: + vhost_net_disable_notifiers_nvhosts(dev, ncs, data_queue_pairs, i); + /* + * This for loop starts from i+1, not i, because the i-th ioeventfd + * has already been released in vhost_dev_disable_notifiers_nvqs(). + */ + for (k = i + 1; k < nvhosts; k++) { + virtio_device_release_ioeventfd(dev); + } + + return r; +} + +/* + * Stop processing guest IO notifications in qemu. + * Start processing them in vhost in kernel. + */ +static void vhost_net_disable_notifiers(VirtIODevice *dev, + NetClientState *ncs, int data_queue_pairs, int cvq) +{ + vhost_net_disable_notifiers_nvhosts(dev, ncs, data_queue_pairs, + data_queue_pairs + cvq); +} + static int vhost_net_get_fd(NetClientState *backend) { switch (backend->info->type) { @@ -254,11 +414,6 @@ static int vhost_net_start_one(struct vhost_net *net, } } - r = vhost_dev_enable_notifiers(&net->dev, dev); - if (r < 0) { - goto fail_notifiers; - } - r = vhost_dev_start(&net->dev, dev, false); if (r < 0) { goto fail_start; @@ -301,8 +456,8 @@ 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) { @@ -310,8 +465,6 @@ fail: } vhost_dev_stop(&net->dev, dev, false); fail_start: - vhost_dev_disable_notifiers(&net->dev, dev); -fail_notifiers: return r; } @@ -333,7 +486,6 @@ static void vhost_net_stop_one(struct vhost_net *net, if (net->nc->info->stop) { net->nc->info->stop(net->nc); } - vhost_dev_disable_notifiers(&net->dev, dev); } int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, @@ -378,10 +530,16 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } } + r = vhost_net_enable_notifiers(dev, ncs, data_queue_pairs, cvq); + if (r < 0) { + error_report("Error enabling host notifiers: %d", -r); + goto err; + } + r = k->set_guest_notifiers(qbus->parent, total_notifiers, true); if (r < 0) { error_report("Error binding guest notifier: %d", -r); - goto err; + goto err_host_notifiers; } for (i = 0; i < nvhosts; i++) { @@ -396,19 +554,19 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, r = vhost_set_vring_enable(peer, peer->vring_enable); if (r < 0) { - goto err_start; + goto err_guest_notifiers; } } r = vhost_net_start_one(get_vhost_net(peer), dev); if (r < 0) { - goto err_start; + goto err_guest_notifiers; } } return 0; -err_start: +err_guest_notifiers: while (--i >= 0) { peer = qemu_get_peer(ncs, i < data_queue_pairs ? i : n->max_queue_pairs); @@ -419,6 +577,8 @@ err_start: fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", e); fflush(stderr); } +err_host_notifiers: + vhost_net_disable_notifiers(dev, ncs, data_queue_pairs, cvq); err: return r; } @@ -450,6 +610,8 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, fflush(stderr); } assert(r >= 0); + + vhost_net_disable_notifiers(dev, ncs, data_queue_pairs, cvq); } void vhost_net_cleanup(struct vhost_net *net) @@ -478,6 +640,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; @@ -489,6 +660,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: @@ -514,6 +691,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) { @@ -602,8 +789,8 @@ err_start: if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) { file.fd = VHOST_FILE_UNBIND; file.index = idx; - int r = vhost_net_set_backend(&net->dev, &file); - assert(r >= 0); + int ret = vhost_net_set_backend(&net->dev, &file); + assert(ret >= 0); } vhost_dev_stop(&net->dev, vdev, false); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 4abd49e298..6e8c51a2db 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -40,17 +40,17 @@ #include "migration/misc.h" #include "standard-headers/linux/ethtool.h" #include "sysemu/sysemu.h" +#include "sysemu/replay.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 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 @@ -168,20 +168,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); } } @@ -207,7 +211,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); } } @@ -357,7 +361,8 @@ static void virtio_net_vnet_endian_status(VirtIONet *n, uint8_t status) * can't do it, we fallback onto fixing the headers in the core * virtio-net code. */ - n->needs_vnet_hdr_swap = virtio_net_set_vnet_endian(vdev, n->nic->ncs, + n->needs_vnet_hdr_swap = n->has_vnet_hdr && + virtio_net_set_vnet_endian(vdev, n->nic->ncs, queue_pairs, true); } else if (virtio_net_started(n, vdev->status)) { /* After using the device, we need to reset the network backend to @@ -413,7 +418,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) timer_mod(q->tx_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); } else { - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); } } else { if (q->tx_timer) { @@ -457,8 +462,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 */ @@ -597,40 +601,6 @@ static void virtio_net_queue_enable(VirtIODevice *vdev, uint32_t queue_index) } } -static void virtio_net_reset(VirtIODevice *vdev) -{ - VirtIONet *n = VIRTIO_NET(vdev); - int i; - - /* Reset back to compatibility mode */ - n->promisc = 1; - n->allmulti = 0; - n->alluni = 0; - n->nomulti = 0; - n->nouni = 0; - n->nobcast = 0; - /* multiqueue is disabled by default */ - n->curr_queue_pairs = 1; - timer_del(n->announce_timer.tm); - n->announce_timer.round = 0; - n->status &= ~VIRTIO_NET_S_ANNOUNCE; - - /* Flush any MAC and VLAN filter table state */ - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.multi_overflow = 0; - n->mac_table.uni_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); - qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); - memset(n->vlans, 0, MAX_VLAN >> 3); - - /* Flush any async TX */ - for (i = 0; i < n->max_queue_pairs; i++) { - flush_or_purge_queued_packets(qemu_get_subqueue(n->nic, i)); - } -} - static void peer_test_vnet_hdr(VirtIONet *n) { NetClientState *nc = qemu_get_queue(n->nic); @@ -656,6 +626,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) { @@ -673,6 +652,7 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, n->guest_hdr_len = n->mergeable_rx_bufs ? sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); + n->rss_data.populate_hash = false; } for (i = 0; i < n->max_queue_pairs; i++) { @@ -793,6 +773,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); } @@ -801,6 +785,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; } @@ -816,6 +806,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; } @@ -841,22 +846,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); @@ -980,11 +989,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); } @@ -1221,18 +1234,6 @@ static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, } } -static void virtio_net_detach_epbf_rss(VirtIONet *n); - -static void virtio_net_disable_rss(VirtIONet *n) -{ - if (n->rss_data.enabled) { - trace_virtio_net_rss_disable(); - } - n->rss_data.enabled = false; - - virtio_net_detach_epbf_rss(n); -} - static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd) { NetClientState *nc = qemu_get_peer(qemu_get_queue(nic), 0); @@ -1240,6 +1241,7 @@ static bool virtio_net_attach_ebpf_to_backend(NICState *nic, int prog_fd) return false; } + trace_virtio_net_rss_attach_ebpf(nic, prog_fd); return nc->info->set_steering_ebpf(nc, prog_fd); } @@ -1253,7 +1255,7 @@ static void rss_data_to_rss_config(struct VirtioNetRssData *data, config->default_queue = data->default_queue; } -static bool virtio_net_attach_epbf_rss(VirtIONet *n) +static bool virtio_net_attach_ebpf_rss(VirtIONet *n) { struct EBPFRSSConfig config = {}; @@ -1264,7 +1266,8 @@ static bool virtio_net_attach_epbf_rss(VirtIONet *n) rss_data_to_rss_config(&n->rss_data, &config); if (!ebpf_rss_set_all(&n->ebpf_rss, &config, - n->rss_data.indirections_table, n->rss_data.key)) { + n->rss_data.indirections_table, n->rss_data.key, + NULL)) { return false; } @@ -1275,19 +1278,92 @@ static bool virtio_net_attach_epbf_rss(VirtIONet *n) return true; } -static void virtio_net_detach_epbf_rss(VirtIONet *n) +static void virtio_net_detach_ebpf_rss(VirtIONet *n) { virtio_net_attach_ebpf_to_backend(n->nic, -1); } -static bool virtio_net_load_ebpf(VirtIONet *n) +static void virtio_net_commit_rss_config(VirtIONet *n) { - if (!virtio_net_attach_ebpf_to_backend(n->nic, -1)) { - /* backend does't support steering ebpf */ + if (n->rss_data.enabled) { + n->rss_data.enabled_software_rss = n->rss_data.populate_hash; + if (n->rss_data.populate_hash) { + virtio_net_detach_ebpf_rss(n); + } else if (!virtio_net_attach_ebpf_rss(n)) { + if (get_vhost_net(qemu_get_queue(n->nic)->peer)) { + warn_report("Can't load eBPF RSS for vhost"); + } else { + warn_report("Can't load eBPF RSS - fallback to software RSS"); + n->rss_data.enabled_software_rss = true; + } + } + + trace_virtio_net_rss_enable(n, + n->rss_data.hash_types, + n->rss_data.indirections_len, + sizeof(n->rss_data.key)); + } else { + virtio_net_detach_ebpf_rss(n); + trace_virtio_net_rss_disable(n); + } +} + +static void virtio_net_disable_rss(VirtIONet *n) +{ + if (!n->rss_data.enabled) { + return; + } + + n->rss_data.enabled = false; + virtio_net_commit_rss_config(n); +} + +static bool virtio_net_load_ebpf_fds(VirtIONet *n, Error **errp) +{ + int fds[EBPF_RSS_MAX_FDS] = { [0 ... EBPF_RSS_MAX_FDS - 1] = -1}; + int ret = true; + int i = 0; + + 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; } - return ebpf_rss_load(&n->ebpf_rss); + for (i = 0; i < n->nr_ebpf_rss_fds; i++) { + fds[i] = monitor_fd_param(monitor_cur(), n->ebpf_rss_fds[i], errp); + if (fds[i] < 0) { + ret = false; + goto exit; + } + } + + ret = ebpf_rss_load_fds(&n->ebpf_rss, fds[0], fds[1], fds[2], fds[3], errp); + +exit: + if (!ret) { + 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)) { + trace_virtio_net_rss_load(n, n->nr_ebpf_rss_fds, n->ebpf_rss_fds); + if (n->ebpf_rss_fds) { + ret = virtio_net_load_ebpf_fds(n, errp); + } else { + ret = ebpf_rss_load(&n->ebpf_rss, errp); + } + } + + return ret; } static void virtio_net_unload_ebpf(VirtIONet *n) @@ -1330,17 +1406,17 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types); n->rss_data.indirections_len = virtio_lduw_p(vdev, &cfg.indirection_table_mask); - n->rss_data.indirections_len++; if (!do_rss) { - n->rss_data.indirections_len = 1; + n->rss_data.indirections_len = 0; } - if (!is_power_of_2(n->rss_data.indirections_len)) { - err_msg = "Invalid size of indirection table"; + if (n->rss_data.indirections_len >= VIRTIO_NET_RSS_MAX_TABLE_LEN) { + err_msg = "Too large indirection table"; err_value = n->rss_data.indirections_len; goto error; } - if (n->rss_data.indirections_len > VIRTIO_NET_RSS_MAX_TABLE_LEN) { - err_msg = "Too large indirection table"; + n->rss_data.indirections_len++; + if (!is_power_of_2(n->rss_data.indirections_len)) { + err_msg = "Invalid size of indirection table"; err_value = n->rss_data.indirections_len; goto error; } @@ -1408,31 +1484,10 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, goto error; } n->rss_data.enabled = true; - - if (!n->rss_data.populate_hash) { - if (!virtio_net_attach_epbf_rss(n)) { - /* EBPF must be loaded for vhost */ - if (get_vhost_net(qemu_get_queue(n->nic)->peer)) { - warn_report("Can't load eBPF RSS for vhost"); - goto error; - } - /* fallback to software RSS */ - warn_report("Can't load eBPF RSS - fallback to software RSS"); - n->rss_data.enabled_software_rss = true; - } - } else { - /* use software RSS for hash populating */ - /* and detach eBPF if was loaded before */ - virtio_net_detach_epbf_rss(n); - n->rss_data.enabled_software_rss = true; - } - - trace_virtio_net_rss_enable(n->rss_data.hash_types, - n->rss_data.indirections_len, - temp.b); + virtio_net_commit_rss_config(n); return queue_pairs; error: - trace_virtio_net_rss_error(err_msg, err_value); + trace_virtio_net_rss_error(n, err_msg, err_value); virtio_net_disable_rss(n); return 0; } @@ -1592,24 +1647,28 @@ static bool virtio_net_can_receive(NetClientState *nc) static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) { + int opaque; + unsigned int in_bytes; VirtIONet *n = q->n; - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(q->rx_vq, 1); - /* To avoid a race condition where the guest has made some buffers - * available after the above check but before notification was - * enabled, check for available buffers again. - */ - if (virtio_queue_empty(q->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(q->rx_vq, bufsize, 0))) { + while (virtio_queue_empty(q->rx_vq) || n->mergeable_rx_bufs) { + opaque = virtqueue_get_avail_bytes(q->rx_vq, &in_bytes, NULL, + bufsize, 0); + /* Buffer is enough, disable notifiaction */ + if (bufsize <= in_bytes) { + break; + } + + if (virtio_queue_enable_notification_and_check(q->rx_vq, opaque)) { + /* Guest has added some buffers, try again */ + continue; + } else { return 0; } } virtio_queue_set_notification(q->rx_vq, 0); + return 1; } @@ -1636,38 +1695,44 @@ static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) * cache. */ static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, - uint8_t *buf, size_t size) + size_t *hdr_len, const uint8_t *buf, + size_t buf_size, size_t *buf_offset) { + size_t csum_size = ETH_HLEN + sizeof(struct ip_header) + + sizeof(struct udp_header); + + buf += *buf_offset; + buf_size -= *buf_offset; + if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ - (size > 27 && size < 1500) && /* normal sized MTU */ + (buf_size >= csum_size && buf_size < 1500) && /* normal sized MTU */ (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ (buf[23] == 17) && /* ip.protocol == UDP */ (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ - net_checksum_calculate(buf, size, CSUM_UDP); + memcpy((uint8_t *)hdr + *hdr_len, buf, csum_size); + net_checksum_calculate((uint8_t *)hdr + *hdr_len, csum_size, CSUM_UDP); hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; + *hdr_len += csum_size; + *buf_offset += csum_size; } } -static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, - const void *buf, size_t size) +static size_t receive_header(VirtIONet *n, struct virtio_net_hdr *hdr, + const void *buf, size_t buf_size, + size_t *buf_offset) { - if (n->has_vnet_hdr) { - /* FIXME this cast is evil */ - void *wbuf = (void *)buf; - work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, - size - n->host_hdr_len); + size_t hdr_len = n->guest_hdr_len; - if (n->needs_vnet_hdr_swap) { - virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); - } - iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); - } else { - struct virtio_net_hdr hdr = { - .flags = 0, - .gso_type = VIRTIO_NET_HDR_GSO_NONE - }; - iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr); + memcpy(hdr, buf, sizeof(struct virtio_net_hdr)); + + *buf_offset = n->host_hdr_len; + work_around_broken_dhclient(hdr, &hdr_len, buf, buf_size, buf_offset); + + if (n->needs_vnet_hdr_swap) { + virtio_net_hdr_swap(VIRTIO_DEVICE(n), hdr); } + + return hdr_len; } static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) @@ -1721,61 +1786,77 @@ 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; } -static void virtio_set_packet_hash(const uint8_t *buf, uint8_t report, - uint32_t hash) -{ - struct virtio_net_hdr_v1_hash *hdr = (void *)buf; - hdr->hash_value = hash; - hdr->hash_report = report; -} - static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, - size_t size) + size_t size, + struct virtio_net_hdr_v1_hash *hdr) { VirtIONet *n = qemu_get_nic_opaque(nc); unsigned int index = nc->queue_index, new_index = index; 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, @@ -1787,21 +1868,19 @@ 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) { - virtio_set_packet_hash(buf, VIRTIO_NET_HASH_REPORT_NONE, 0); + hdr->hash_value = VIRTIO_NET_HASH_REPORT_NONE; + hdr->hash_report = 0; } return n->rss_data.redirect ? n->rss_data.default_queue : -1; } @@ -1809,7 +1888,8 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, hash = net_rx_pkt_calc_rss_hash(pkt, net_hash_type, n->rss_data.key); if (n->rss_data.populate_hash) { - virtio_set_packet_hash(buf, reports[net_hash_type], hash); + hdr->hash_value = hash; + hdr->hash_report = reports[net_hash_type]; } if (n->rss_data.redirect) { @@ -1820,31 +1900,41 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, return (index == new_index) ? -1 : new_index; } +typedef struct Header { + struct virtio_net_hdr_v1_hash virtio_net; + struct eth_header eth; + struct ip_header ip; + struct udp_header udp; +} Header; + static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, - size_t size, bool no_rss) + size_t size) { VirtIONet *n = qemu_get_nic_opaque(nc); - VirtIONetQueue *q = virtio_net_get_subqueue(nc); + VirtIONetQueue *q; VirtIODevice *vdev = VIRTIO_DEVICE(n); VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; size_t lens[VIRTQUEUE_MAX_SIZE]; struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; - struct virtio_net_hdr_mrg_rxbuf mhdr; + Header hdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset, j; ssize_t err; + memset(&hdr.virtio_net, 0, sizeof(hdr.virtio_net)); + + if (n->rss_data.enabled && n->rss_data.enabled_software_rss) { + int index = virtio_net_process_rss(nc, buf, size, &hdr.virtio_net); + if (index >= 0) { + nc = qemu_get_subqueue(n->nic, index % n->curr_queue_pairs); + } + } + if (!virtio_net_can_receive(nc)) { return -1; } - if (!no_rss && n->rss_data.enabled && n->rss_data.enabled_software_rss) { - int index = virtio_net_process_rss(nc, buf, size); - if (index >= 0) { - NetClientState *nc2 = qemu_get_subqueue(n->nic, index); - return virtio_net_receive_rcu(nc2, buf, size, true); - } - } + q = virtio_net_get_subqueue(nc); /* hdr_len refers to the header we supply to the guest */ if (!virtio_net_has_buffers(q, size + n->guest_hdr_len - n->host_hdr_len)) { @@ -1899,19 +1989,18 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, if (n->mergeable_rx_bufs) { mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg), sg, elem->in_num, - offsetof(typeof(mhdr), num_buffers), - sizeof(mhdr.num_buffers)); + offsetof(typeof(hdr), + virtio_net.hdr.num_buffers), + sizeof(hdr.virtio_net.hdr.num_buffers)); } - receive_header(n, sg, elem->in_num, buf, size); - if (n->rss_data.populate_hash) { - offset = sizeof(mhdr); - iov_from_buf(sg, elem->in_num, offset, - buf + offset, n->host_hdr_len - sizeof(mhdr)); - } - offset = n->host_hdr_len; - total += n->guest_hdr_len; - guest_offset = n->guest_hdr_len; + guest_offset = n->has_vnet_hdr ? + receive_header(n, (struct virtio_net_hdr *)&hdr, + buf, size, &offset) : + n->guest_hdr_len; + + iov_from_buf(sg, elem->in_num, 0, &hdr, guest_offset); + total += guest_offset; } else { guest_offset = 0; } @@ -1937,10 +2026,11 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, } if (mhdr_cnt) { - virtio_stw_p(vdev, &mhdr.num_buffers, i); + virtio_stw_p(vdev, &hdr.virtio_net.hdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, - &mhdr.num_buffers, sizeof mhdr.num_buffers); + &hdr.virtio_net.hdr.num_buffers, + sizeof hdr.virtio_net.hdr.num_buffers); } for (j = 0; j < i; j++) { @@ -1968,7 +2058,22 @@ static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf, { RCU_READ_LOCK_GUARD(); - return virtio_net_receive_rcu(nc, buf, size, false); + return virtio_net_receive_rcu(nc, buf, size); +} + +/* + * Accessors to read and write the IP packet data length field. This + * is a potentially unaligned network-byte-order 16 bit unsigned integer + * pointed to by unit->ip_len. + */ +static uint16_t read_unit_ip_len(VirtioNetRscUnit *unit) +{ + return lduw_be_p(unit->ip_plen); +} + +static void write_unit_ip_len(VirtioNetRscUnit *unit, uint16_t l) +{ + stw_be_p(unit->ip_plen, l); } static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain, @@ -1985,7 +2090,7 @@ static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain, unit->ip_plen = &ip->ip_len; unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen); unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; - unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen; + unit->payload = read_unit_ip_len(unit) - ip_hdrlen - unit->tcp_hdrlen; } static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain, @@ -2002,9 +2107,9 @@ 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; + unit->payload = read_unit_ip_len(unit) - unit->tcp_hdrlen; } static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain, @@ -2051,7 +2156,7 @@ static void virtio_net_rsc_purge(void *opq) chain->stat.timer++; if (!QTAILQ_EMPTY(&chain->buffers)) { timer_mod(chain->drain_timer, - qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + chain->n->rsc_timeout); } } @@ -2153,7 +2258,7 @@ static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain, VirtioNetRscUnit *o_unit; o_unit = &seg->unit; - o_ip_len = htons(*o_unit->ip_plen); + o_ip_len = read_unit_ip_len(o_unit); nseq = htonl(n_unit->tcp->th_seq); oseq = htonl(o_unit->tcp->th_seq); @@ -2189,7 +2294,7 @@ coalesce: o_unit->payload += n_unit->payload; /* update new data len */ /* update field in ip header */ - *o_unit->ip_plen = htons(o_ip_len + n_unit->payload); + write_unit_ip_len(o_unit, o_ip_len + n_unit->payload); /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced for windows guest, while this may change the behavior for linux @@ -2287,7 +2392,7 @@ static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain, chain->stat.empty_cache++; virtio_net_rsc_cache_buf(chain, nc, buf, size); timer_mod(chain->drain_timer, - qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + chain->n->rsc_timeout); return size; } @@ -2471,7 +2576,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) @@ -2525,7 +2630,7 @@ static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n, chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD; chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; } - chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST, + chain->drain_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, virtio_net_rsc_purge, chain); memset(&chain->stat, 0, sizeof(chain->stat)); @@ -2600,7 +2705,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) */ virtio_queue_set_notification(q->tx_vq, 0); if (q->tx_bh) { - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); } else { timer_mod(q->tx_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); @@ -2630,7 +2735,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 vhdr; elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement)); if (!elem) { @@ -2641,32 +2746,25 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) out_sg = elem->out_sg; if (out_num < 1) { virtio_error(vdev, "virtio-net header not in first element"); - virtqueue_detach_element(q->tx_vq, elem, 0); - g_free(elem); - return -EINVAL; + goto detach; } - if (n->has_vnet_hdr) { - if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) < - n->guest_hdr_len) { + if (n->needs_vnet_hdr_swap) { + if (iov_to_buf(out_sg, out_num, 0, &vhdr, sizeof(vhdr)) < + sizeof(vhdr)) { virtio_error(vdev, "virtio-net header incorrect"); - virtqueue_detach_element(q->tx_vq, elem, 0); - g_free(elem); - return -EINVAL; + goto detach; } - if (n->needs_vnet_hdr_swap) { - virtio_net_hdr_swap(vdev, (void *) &mhdr); - sg2[0].iov_base = &mhdr; - sg2[0].iov_len = n->guest_hdr_len; - out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, - out_sg, out_num, - n->guest_hdr_len, -1); - if (out_num == VIRTQUEUE_MAX_SIZE) { - goto drop; - } - out_num += 1; - out_sg = sg2; + virtio_net_hdr_swap(vdev, &vhdr); + sg2[0].iov_base = &vhdr; + sg2[0].iov_len = sizeof(vhdr); + out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1, out_sg, out_num, + sizeof(vhdr), -1); + if (out_num == VIRTQUEUE_MAX_SIZE) { + goto drop; } + out_num += 1; + out_sg = sg2; } /* * If host wants to see the guest header as is, we can @@ -2675,6 +2773,10 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) */ assert(n->host_hdr_len <= n->guest_hdr_len); if (n->host_hdr_len != n->guest_hdr_len) { + if (iov_size(out_sg, out_num) < n->guest_hdr_len) { + virtio_error(vdev, "virtio-net header is invalid"); + goto detach; + } unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg), out_sg, out_num, 0, n->host_hdr_len); @@ -2683,6 +2785,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) n->guest_hdr_len, -1); out_num = sg_num; out_sg = sg; + + if (out_num < 1) { + virtio_error(vdev, "virtio-net nothing to send"); + goto detach; + } } ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index), @@ -2703,6 +2810,11 @@ drop: } } return num_packets; + +detach: + virtqueue_detach_element(q->tx_vq, elem, 0); + g_free(elem); + return -EINVAL; } static void virtio_net_tx_timer(void *opaque); @@ -2741,6 +2853,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; @@ -2755,7 +2871,7 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) return; } virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); } static void virtio_net_tx_timer(void *opaque) @@ -2838,7 +2954,7 @@ static void virtio_net_tx_bh(void *opaque) /* If we flush a full burst of packets, assume there are * more coming and immediately reschedule */ if (ret >= n->tx_burst) { - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); q->tx_waiting = 1; return; } @@ -2852,7 +2968,7 @@ static void virtio_net_tx_bh(void *opaque) return; } else if (ret > 0) { virtio_queue_set_notification(q->tx_vq, 0); - qemu_bh_schedule(q->tx_bh); + replay_bh_schedule_event(q->tx_bh); q->tx_waiting = 1; } } @@ -2875,7 +2991,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; @@ -2947,6 +3064,15 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) virtio_net_set_queue_pairs(n); } +static int virtio_net_pre_load_queues(VirtIODevice *vdev) +{ + virtio_net_set_multiqueue(VIRTIO_NET(vdev), + virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_RSS) || + virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MQ)); + + return 0; +} + static int virtio_net_post_load_device(void *opaque, int version_id) { VirtIONet *n = opaque; @@ -3007,26 +3133,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) } } - if (n->rss_data.enabled) { - n->rss_data.enabled_software_rss = n->rss_data.populate_hash; - if (!n->rss_data.populate_hash) { - if (!virtio_net_attach_epbf_rss(n)) { - if (get_vhost_net(qemu_get_queue(n->nic)->peer)) { - warn_report("Can't post-load eBPF RSS for vhost"); - } else { - warn_report("Can't post-load eBPF RSS - " - "fallback to software RSS"); - n->rss_data.enabled_software_rss = true; - } - } - } - - trace_virtio_net_rss_enable(n->rss_data.hash_types, - n->rss_data.indirections_len, - sizeof(n->rss_data.key)); - } else { - trace_virtio_net_rss_disable(); - } + virtio_net_commit_rss_config(n); return 0; } @@ -3049,7 +3156,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() }, @@ -3127,7 +3234,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, @@ -3164,7 +3271,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() }, @@ -3198,7 +3305,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() }, @@ -3214,7 +3321,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), @@ -3235,7 +3342,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, @@ -3279,8 +3386,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 } @@ -3301,7 +3408,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. @@ -3315,6 +3422,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); } @@ -3324,7 +3440,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. @@ -3338,8 +3454,17 @@ 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) @@ -3417,7 +3542,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; @@ -3429,7 +3554,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); @@ -3437,7 +3562,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) { @@ -3447,11 +3572,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, @@ -3542,8 +3668,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); } @@ -3567,12 +3693,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; } @@ -3614,9 +3740,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), n->net_conf.tx_queue_size); - for (i = 0; i < n->max_queue_pairs; i++) { - virtio_net_add_queue(n, i); - } + virtio_net_add_queue(n, 0); n->ctrl_vq = virtio_add_queue(vdev, 64, virtio_net_handle_ctrl); qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); @@ -3632,10 +3756,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++) { @@ -3644,9 +3770,6 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) peer_test_vnet_hdr(n); if (peer_has_vnet_hdr(n)) { - for (i = 0; i < n->max_queue_pairs; i++) { - qemu_using_vnet_hdr(qemu_get_subqueue(n->nic, i)->peer, true); - } n->host_hdr_len = sizeof(struct virtio_net_hdr); } else { n->host_hdr_len = 0; @@ -3670,15 +3793,31 @@ 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); + Error *err = NULL; + if (!virtio_net_load_ebpf(n, &err)) { + /* + * If user explicitly gave QEMU RSS FDs to use, then + * failing to use them must be considered a fatal + * error. If no RSS FDs were provided, QEMU is trying + * eBPF on a "best effort" basis only, so report a + * warning and allow fallback to software RSS. + */ + if (n->ebpf_rss_fds) { + error_propagate(errp, err); + } else { + warn_report("unable to load eBPF RSS: %s", + error_get_pretty(err)); + error_free(err); + } + } } } @@ -3706,7 +3845,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); } @@ -3726,13 +3865,49 @@ static void virtio_net_device_unrealize(DeviceState *dev) virtio_cleanup(vdev); } +static void virtio_net_reset(VirtIODevice *vdev) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i; + + /* Reset back to compatibility mode */ + n->promisc = 1; + n->allmulti = 0; + n->alluni = 0; + n->nomulti = 0; + n->nouni = 0; + n->nobcast = 0; + /* multiqueue is disabled by default */ + n->curr_queue_pairs = 1; + timer_del(n->announce_timer.tm); + n->announce_timer.round = 0; + n->status &= ~VIRTIO_NET_S_ANNOUNCE; + + /* Flush any MAC and VLAN filter table state */ + n->mac_table.in_use = 0; + n->mac_table.first_multi = 0; + n->mac_table.multi_overflow = 0; + n->mac_table.uni_overflow = 0; + memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); + memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); + qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); + memset(n->vlans, 0, MAX_VLAN >> 3); + + /* Flush any async TX */ + for (i = 0; i < n->max_queue_pairs; i++) { + flush_or_purge_queued_packets(qemu_get_subqueue(n->nic, i)); + } + + virtio_net_disable_rss(n); +} + static void virtio_net_instance_init(Object *obj) { VirtIONet *n = VIRTIO_NET(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, @@ -3778,8 +3953,23 @@ static bool dev_unplug_pending(void *opaque) static struct vhost_dev *virtio_net_get_vhost(VirtIODevice *vdev) { VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc = qemu_get_queue(n->nic); - struct vhost_net *net = get_vhost_net(nc->peer); + NetClientState *nc; + struct vhost_net *net; + + if (!n->nic) { + return NULL; + } + + nc = qemu_get_queue(n->nic); + if (!nc) { + return NULL; + } + + net = get_vhost_net(nc->peer); + if (!net) { + return NULL; + } + return &net->dev; } @@ -3787,7 +3977,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() }, @@ -3840,6 +4030,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, @@ -3859,6 +4051,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(), }; @@ -3884,10 +4082,12 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; vdc->legacy_features |= (0x1 << VIRTIO_NET_F_GSO); + vdc->pre_load_queues = virtio_net_pre_load_queues; vdc->post_load = virtio_net_post_load_virtio; 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 56559cda24..8aa8c46228 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,24 +439,23 @@ 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: g_assert_not_reached(); - return false; } return true; @@ -651,9 +649,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 +675,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 +847,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 +888,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 +898,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 +915,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 +1159,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 +1340,8 @@ static void vmxnet3_update_features(VMXNET3State *s) s->lro_supported, s->lro_supported, 0, + 0, + 0, 0); } } @@ -1441,7 +1440,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 +1523,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 +1888,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 +1980,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 +1992,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 +2077,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; @@ -2093,8 +2090,6 @@ static void vmxnet3_net_init(VMXNET3State *s) if (s->peer_has_vhdr) { qemu_set_vnet_hdr_len(qemu_get_queue(s->nic)->peer, sizeof(struct virtio_net_hdr)); - - qemu_using_vnet_hdr(qemu_get_queue(s->nic)->peer, 1); } qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); @@ -2309,7 +2304,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() @@ -2319,7 +2314,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), @@ -2332,7 +2327,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), @@ -2350,7 +2345,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, @@ -2366,7 +2361,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), @@ -2384,7 +2379,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), @@ -2402,9 +2397,8 @@ static int vmxnet3_post_load(void *opaque, int version_id) { VMXNET3State *s = opaque; - 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) { vmxnet3_use_msix_vectors(s, VMXNET3_MAX_INTRS); @@ -2421,7 +2415,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), @@ -2435,7 +2429,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), @@ -2471,7 +2465,7 @@ static const VMStateDescription vmstate_vmxnet3 = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmxstate_vmxnet3_mcast_list, NULL } @@ -2517,7 +2511,7 @@ static void vmxnet3_class_init(ObjectClass *class, void *data) device_class_set_parent_realize(dc, vmxnet3_realize, &vc->parent_dc_realize); dc->desc = "VMWare Paravirtualized Ethernet v3"; - dc->reset = vmxnet3_qdev_reset; + device_class_set_legacy_reset(dc, vmxnet3_qdev_reset); dc->vmsd = &vmstate_vmxnet3; device_class_set_props(dc, vmxnet3_properties); set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); 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 7d92c2d022..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,136 +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); - qemu_set_info_str(qemu_get_queue(netdev->nic), - "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..faf27947b0 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; @@ -848,7 +847,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) axienet_eth_rx_notify(s); enet_update_irq(s); - return size; + return s->rxpos; } static size_t @@ -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); @@ -1014,7 +1014,7 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data) dc->realize = xilinx_enet_realize; device_class_set_props(dc, xilinx_enet_properties); - dc->reset = xilinx_axienet_reset; + device_class_set_legacy_reset(dc, xilinx_axienet_reset); } static void xilinx_enet_control_stream_class_init(ObjectClass *klass, diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 6e09f7e422..bd81290808 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); } @@ -262,7 +263,7 @@ static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = xilinx_ethlite_realize; - dc->reset = xilinx_ethlite_reset; + device_class_set_legacy_reset(dc, xilinx_ethlite_reset); device_class_set_props(dc, xilinx_ethlite_properties); } diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c deleted file mode 100644 index 91383fb097..0000000000 --- a/hw/nios2/10m50_devboard.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Altera 10M50 Nios2 GHRD - * - * Copyright (c) 2016 Marek Vasut - * - * Based on LabX device code - * - * Copyright (c) 2012 Chris Wulff - * - * 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 "hw/sysbus.h" -#include "hw/char/serial.h" -#include "hw/intc/nios2_vic.h" -#include "hw/qdev-properties.h" -#include "sysemu/sysemu.h" -#include "hw/boards.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "qemu/config-file.h" - -#include "boot.h" - -struct Nios2MachineState { - MachineState parent_obj; - - MemoryRegion phys_tcm; - MemoryRegion phys_tcm_alias; - MemoryRegion phys_ram; - MemoryRegion phys_ram_alias; - - bool vic; -}; - -#define TYPE_NIOS2_MACHINE MACHINE_TYPE_NAME("10m50-ghrd") -OBJECT_DECLARE_TYPE(Nios2MachineState, MachineClass, NIOS2_MACHINE) - -#define BINARY_DEVICE_TREE_FILE "10m50-devboard.dtb" - -static void nios2_10m50_ghrd_init(MachineState *machine) -{ - Nios2MachineState *nms = NIOS2_MACHINE(machine); - Nios2CPU *cpu; - DeviceState *dev; - MemoryRegion *address_space_mem = get_system_memory(); - ram_addr_t tcm_base = 0x0; - ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */ - ram_addr_t ram_base = 0x08000000; - ram_addr_t ram_size = 0x08000000; - qemu_irq irq[32]; - int i; - - /* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */ - memory_region_init_ram(&nms->phys_tcm, NULL, "nios2.tcm", tcm_size, - &error_abort); - memory_region_init_alias(&nms->phys_tcm_alias, NULL, "nios2.tcm.alias", - &nms->phys_tcm, 0, tcm_size); - memory_region_add_subregion(address_space_mem, tcm_base, &nms->phys_tcm); - memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base, - &nms->phys_tcm_alias); - - /* Physical DRAM with alias at 0xc0000000 */ - memory_region_init_ram(&nms->phys_ram, NULL, "nios2.ram", ram_size, - &error_abort); - memory_region_init_alias(&nms->phys_ram_alias, NULL, "nios2.ram.alias", - &nms->phys_ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, ram_base, &nms->phys_ram); - memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base, - &nms->phys_ram_alias); - - /* Create CPU. We need to set eic_present between init and realize. */ - cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU)); - - /* Enable the External Interrupt Controller within the CPU. */ - cpu->eic_present = nms->vic; - - /* Configure new exception vectors. */ - cpu->reset_addr = 0xd4000000; - cpu->exception_addr = 0xc8000120; - cpu->fast_tlb_miss_addr = 0xc0000100; - - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); - - if (nms->vic) { - DeviceState *dev = qdev_new(TYPE_NIOS2_VIC); - MemoryRegion *dev_mr; - qemu_irq cpu_irq; - - object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - - cpu_irq = qdev_get_gpio_in_named(DEVICE(cpu), "EIC", 0); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, cpu_irq); - for (int i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in(dev, i); - } - - dev_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); - memory_region_add_subregion(address_space_mem, 0x18002000, dev_mr); - } else { - for (i = 0; i < 32; i++) { - irq[i] = qdev_get_gpio_in_named(DEVICE(cpu), "IRQ", i); - } - } - - /* Register: Altera 16550 UART */ - serial_mm_init(address_space_mem, 0xf8001600, 2, irq[1], 115200, - serial_hd(0), DEVICE_NATIVE_ENDIAN); - - /* Register: Timer sys_clk_timer */ - dev = qdev_new("ALTR.timer"); - qdev_prop_set_uint32(dev, "clock-frequency", 75 * 1000000); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xf8001440); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[0]); - - /* Register: Timer sys_clk_timer_1 */ - dev = qdev_new("ALTR.timer"); - qdev_prop_set_uint32(dev, "clock-frequency", 75 * 1000000); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xe0000880); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[5]); - - nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename, - BINARY_DEVICE_TREE_FILE, NULL); -} - -static bool get_vic(Object *obj, Error **errp) -{ - Nios2MachineState *nms = NIOS2_MACHINE(obj); - return nms->vic; -} - -static void set_vic(Object *obj, bool value, Error **errp) -{ - Nios2MachineState *nms = NIOS2_MACHINE(obj); - nms->vic = value; -} - -static void nios2_10m50_ghrd_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Altera 10M50 GHRD Nios II design"; - mc->init = nios2_10m50_ghrd_init; - mc->is_default = true; - - object_class_property_add_bool(oc, "vic", get_vic, set_vic); - object_class_property_set_description(oc, "vic", - "Set on/off to enable/disable the Vectored Interrupt Controller"); -} - -static const TypeInfo nios2_10m50_ghrd_type_info = { - .name = TYPE_NIOS2_MACHINE, - .parent = TYPE_MACHINE, - .instance_size = sizeof(Nios2MachineState), - .class_init = nios2_10m50_ghrd_class_init, -}; - -static void nios2_10m50_ghrd_type_init(void) -{ - type_register_static(&nios2_10m50_ghrd_type_info); -} -type_init(nios2_10m50_ghrd_type_init); diff --git a/hw/nios2/Kconfig b/hw/nios2/Kconfig deleted file mode 100644 index 4748ae27b6..0000000000 --- a/hw/nios2/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config NIOS2_10M50 - bool - select NIOS2 - select SERIAL - select ALTERA_TIMER - select NIOS2_VIC - -config NIOS2_GENERIC_NOMMU - bool - select NIOS2 - -config NIOS2 - bool diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c deleted file mode 100644 index b30a7b1efb..0000000000 --- a/hw/nios2/boot.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Nios2 kernel loader - * - * Copyright (c) 2016 Marek Vasut - * - * Based on microblaze kernel loader - * - * Copyright (c) 2012 Peter Crosthwaite - * Copyright (c) 2012 PetaLogix - * Copyright (c) 2009 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 "qemu/units.h" -#include "qemu/datadir.h" -#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" -#include "hw/loader.h" -#include "elf.h" - -#include "boot.h" - -#include - -#define NIOS2_MAGIC 0x534f494e - -static struct nios2_boot_info { - void (*machine_cpu_reset)(Nios2CPU *); - uint32_t bootstrap_pc; - uint32_t cmdline; - uint32_t initrd_start; - uint32_t initrd_end; - uint32_t fdt; -} boot_info; - -static void main_cpu_reset(void *opaque) -{ - Nios2CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - CPUNios2State *env = &cpu->env; - - cpu_reset(CPU(cpu)); - - env->regs[R_ARG0] = NIOS2_MAGIC; - env->regs[R_ARG1] = boot_info.initrd_start; - env->regs[R_ARG2] = boot_info.fdt; - env->regs[R_ARG3] = boot_info.cmdline; - - cpu_set_pc(cs, boot_info.bootstrap_pc); - if (boot_info.machine_cpu_reset) { - boot_info.machine_cpu_reset(cpu); - } -} - -static uint64_t translate_kernel_address(void *opaque, uint64_t addr) -{ - return addr - 0xc0000000LL; -} - -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); - } - if (!fdt) { - 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); - if (r < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); - } - } - - if (bi.initrd_start) { - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - translate_kernel_address(NULL, bi.initrd_start)); - - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - translate_kernel_address(NULL, bi.initrd_end)); - } - - cpu_physical_memory_write(bi.fdt, fdt, fdt_size); - - /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ - machine->fdt = fdt; - - return fdt_size; -} - -void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, - uint32_t ramsize, - const char *initrd_filename, - const char *dtb_filename, - void (*machine_cpu_reset)(Nios2CPU *)) -{ - const char *kernel_filename; - const char *kernel_cmdline; - const char *dtb_arg; - char *filename = NULL; - - kernel_filename = current_machine->kernel_filename; - kernel_cmdline = current_machine->kernel_cmdline; - dtb_arg = current_machine->dtb; - /* default to pcbios dtb as passed by machine_init */ - if (!dtb_arg) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); - } - - boot_info.machine_cpu_reset = machine_cpu_reset; - qemu_register_reset(main_cpu_reset, cpu); - - 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); - if ((uint32_t)entry == 0xc0000000) { - /* - * The Nios II processor reference guide documents that the - * kernel is placed at virtual memory address 0xc0000000, - * and we've got something that points there. Reload it - * and adjust the entry to get the address in physical RAM. - */ - kernel_size = load_elf(kernel_filename, NULL, - translate_kernel_address, NULL, - &entry, NULL, NULL, NULL, - big_endian, EM_ALTERA_NIOS2, 0, 0); - boot_info.bootstrap_pc = ddr_base + 0xc0000000 + - (entry & 0x07ffffff); - } else { - /* Use the entry point in the ELF image. */ - boot_info.bootstrap_pc = (uint32_t)entry; - } - - /* If it wasn't an ELF image, try an u-boot image. */ - if (kernel_size < 0) { - hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; - - kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, - NULL, NULL); - boot_info.bootstrap_pc = uentry; - high = loadaddr + kernel_size; - } - - /* Not an ELF image nor an u-boot image, try a RAW image. */ - if (kernel_size < 0) { - kernel_size = load_image_targphys(kernel_filename, ddr_base, - ramsize); - boot_info.bootstrap_pc = ddr_base; - high = ddr_base + kernel_size; - } - - high = ROUND_UP(high, 1 * MiB); - - /* If initrd is available, it goes after the kernel, aligned to 1M. */ - if (initrd_filename) { - int initrd_size; - uint32_t initrd_offset; - - boot_info.initrd_start = high; - initrd_offset = boot_info.initrd_start - ddr_base; - - initrd_size = load_ramdisk(initrd_filename, - boot_info.initrd_start, - ramsize - initrd_offset); - if (initrd_size < 0) { - initrd_size = load_image_targphys(initrd_filename, - boot_info.initrd_start, - ramsize - initrd_offset); - } - if (initrd_size < 0) { - error_report("could not load initrd '%s'", - initrd_filename); - exit(EXIT_FAILURE); - } - high += initrd_size; - } - high = ROUND_UP(high, 4); - boot_info.initrd_end = high; - - /* Device tree must be placed right after initrd (if available) */ - boot_info.fdt = high; - fdt_size = nios2_load_dtb(boot_info, ramsize, kernel_cmdline, - /* Preference a -dtb argument */ - dtb_arg ? dtb_arg : filename); - high += fdt_size; - - /* Kernel command is at the end, 4k aligned. */ - boot_info.cmdline = ROUND_UP(high, 4 * KiB); - if (kernel_cmdline && strlen(kernel_cmdline)) { - pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); - } - } - g_free(filename); -} diff --git a/hw/nios2/boot.h b/hw/nios2/boot.h deleted file mode 100644 index 59b9fbfc62..0000000000 --- a/hw/nios2/boot.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef NIOS2_BOOT_H -#define NIOS2_BOOT_H - -#include "cpu.h" - -void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, uint32_t ramsize, - const char *initrd_filename, const char *dtb_filename, - void (*machine_cpu_reset)(Nios2CPU *)); - -#endif /* NIOS2_BOOT_H */ diff --git a/hw/nios2/generic_nommu.c b/hw/nios2/generic_nommu.c deleted file mode 100644 index 48edb3ae37..0000000000 --- a/hw/nios2/generic_nommu.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Generic simulator target with no MMU or devices. This emulation is - * compatible with the libgloss qemu-hosted.ld linker script for using - * QEMU as an instruction set simulator. - * - * Copyright (c) 2018-2019 Mentor Graphics - * - * Copyright (c) 2016 Marek Vasut - * - * Based on LabX device code - * - * Copyright (c) 2012 Chris Wulff - * - * 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 "hw/char/serial.h" -#include "hw/boards.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "qemu/config-file.h" - -#include "boot.h" - -#define BINARY_DEVICE_TREE_FILE "generic-nommu.dtb" - -static void nios2_generic_nommu_init(MachineState *machine) -{ - Nios2CPU *cpu; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *phys_tcm = g_new(MemoryRegion, 1); - MemoryRegion *phys_tcm_alias = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram = g_new(MemoryRegion, 1); - MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1); - ram_addr_t tcm_base = 0x0; - ram_addr_t tcm_size = 0x1000; /* 1 kiB, but QEMU limit is 4 kiB */ - ram_addr_t ram_base = 0x10000000; - ram_addr_t ram_size = 0x08000000; - - /* Physical TCM (tb_ram_1k) with alias at 0xc0000000 */ - memory_region_init_ram(phys_tcm, NULL, "nios2.tcm", tcm_size, - &error_abort); - memory_region_init_alias(phys_tcm_alias, NULL, "nios2.tcm.alias", - phys_tcm, 0, tcm_size); - memory_region_add_subregion(address_space_mem, tcm_base, phys_tcm); - memory_region_add_subregion(address_space_mem, 0xc0000000 + tcm_base, - phys_tcm_alias); - - /* Physical DRAM with alias at 0xc0000000 */ - memory_region_init_ram(phys_ram, NULL, "nios2.ram", ram_size, - &error_abort); - memory_region_init_alias(phys_ram_alias, NULL, "nios2.ram.alias", - phys_ram, 0, ram_size); - memory_region_add_subregion(address_space_mem, ram_base, phys_ram); - memory_region_add_subregion(address_space_mem, 0xc0000000 + ram_base, - phys_ram_alias); - - cpu = NIOS2_CPU(cpu_create(TYPE_NIOS2_CPU)); - - /* Remove MMU */ - cpu->mmu_present = false; - - /* Reset vector is the first 32 bytes of RAM. */ - cpu->reset_addr = ram_base; - - /* The interrupt vector comes right after reset. */ - cpu->exception_addr = ram_base + 0x20; - - /* - * The linker script does have a TLB miss memory region declared, - * but this should never be used with no MMU. - */ - cpu->fast_tlb_miss_addr = 0x7fff400; - - nios2_load_kernel(cpu, ram_base, ram_size, machine->initrd_filename, - BINARY_DEVICE_TREE_FILE, NULL); -} - -static void nios2_generic_nommu_machine_init(struct MachineClass *mc) -{ - mc->desc = "Generic NOMMU Nios II design"; - mc->init = nios2_generic_nommu_init; -} - -DEFINE_MACHINE("nios2-generic-nommu", nios2_generic_nommu_machine_init); diff --git a/hw/nios2/meson.build b/hw/nios2/meson.build deleted file mode 100644 index 22277bd6c5..0000000000 --- a/hw/nios2/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -nios2_ss = ss.source_set() -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')) - -hw_arch += {'nios2': nios2_ss} 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..26fbcf29a2 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,9 +31,17 @@ 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; + if (nd->slot < 0 || nd->slot >= NUBUS_SLOT_NB) { + error_setg(errp, + "'slot' value %d out of range (must be between 0 and %d)", + nd->slot, NUBUS_SLOT_NB - 1); + return; + } + /* Super */ slot_offset = nd->slot * NUBUS_SUPER_SLOT_SIZE; @@ -76,15 +85,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..7a98731c45 --- /dev/null +++ b/hw/nubus/nubus-virtio-mmio.c @@ -0,0 +1,104 @@ +/* + * 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 "qapi/error.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) +{ + ERRP_GUARD(); + 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 749a6938dd..ec75419566 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 @@ -40,10 +40,20 @@ * sriov_vi_flexible= \ * sriov_max_vi_per_vf= \ * sriov_max_vq_per_vf= \ + * atomic.dn=, \ + * atomic.awun, \ + * atomic.awupf, \ * 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 @@ -196,6 +206,7 @@ #include "sysemu/hostmem.h" #include "hw/pci/msix.h" #include "hw/pci/pcie_sriov.h" +#include "sysemu/spdm-socket.h" #include "migration/vmstate.h" #include "nvme.h" @@ -212,7 +223,6 @@ #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 @@ -238,6 +248,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] = { @@ -245,10 +257,13 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { [NVME_ERROR_RECOVERY] = NVME_FEAT_CAP_CHANGE | NVME_FEAT_CAP_NS, [NVME_VOLATILE_WRITE_CACHE] = NVME_FEAT_CAP_CHANGE, [NVME_NUMBER_OF_QUEUES] = NVME_FEAT_CAP_CHANGE, + [NVME_WRITE_ATOMICITY] = NVME_FEAT_CAP_CHANGE, [NVME_ASYNCHRONOUS_EVENT_CONF] = NVME_FEAT_CAP_CHANGE, [NVME_TIMESTAMP] = NVME_FEAT_CAP_CHANGE, [NVME_HOST_BEHAVIOR_SUPPORT] = NVME_FEAT_CAP_CHANGE, [NVME_COMMAND_SET_PROFILE] = NVME_FEAT_CAP_CHANGE, + [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] = { @@ -266,6 +281,8 @@ static const uint32_t nvme_cse_acs[256] = { [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]; @@ -279,6 +296,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] = { @@ -297,12 +316,66 @@ 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) { @@ -376,6 +449,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; @@ -449,7 +585,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) @@ -469,7 +605,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) @@ -514,24 +650,33 @@ 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; } + + /* vfs does not implement intx */ + if (pci_is_vf(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); @@ -546,7 +691,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); @@ -570,7 +715,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); @@ -595,7 +740,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. */ @@ -759,7 +904,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; @@ -910,7 +1055,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; @@ -1306,26 +1451,26 @@ 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); } @@ -1333,22 +1478,18 @@ static inline void nvme_blk_write(BlockBackend *blk, int64_t offset, static void nvme_update_cq_eventidx(const NvmeCQueue *cq) { - uint32_t v = cpu_to_le32(cq->head); + trace_pci_nvme_update_cq_eventidx(cq->cqid, cq->head); - //not in 7.2: trace_pci_nvme_update_cq_eventidx(cq->cqid, cq->head); - - pci_dma_write(PCI_DEVICE(cq->ctrl), cq->ei_addr, &v, sizeof(v)); + stl_le_pci_dma(PCI_DEVICE(cq->ctrl), cq->ei_addr, cq->head, + MEMTXATTRS_UNSPECIFIED); } static void nvme_update_cq_head(NvmeCQueue *cq) { - uint32_t v; + ldl_le_pci_dma(PCI_DEVICE(cq->ctrl), cq->db_addr, &cq->head, + MEMTXATTRS_UNSPECIFIED); - pci_dma_read(&cq->ctrl->parent_obj, cq->db_addr, &v, sizeof(v)); - - cq->head = le32_to_cpu(v); - - trace_pci_nvme_shadow_doorbell_cq(cq->cqid, cq->head); + trace_pci_nvme_update_cq_head(cq->cqid, cq->head); } static void nvme_post_cqes(void *opaque) @@ -1376,8 +1517,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); @@ -1385,9 +1526,16 @@ static void nvme_post_cqes(void *opaque) stl_le_p(&n->bar.csts, NVME_CSTS_FAILED); break; } + QTAILQ_REMOVE(&cq->req_list, req, entry); + nvme_inc_cq_tail(cq); nvme_sg_unmap(&req->sg); + + if (QTAILQ_EMPTY(&sq->req_list) && !nvme_sq_empty(sq)) { + qemu_bh_schedule(sq->bh); + } + QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); } if (cq->tail != cq->head) { @@ -1518,9 +1666,16 @@ static void nvme_smart_event(NvmeCtrl *n, uint8_t event) static void nvme_clear_events(NvmeCtrl *n, uint8_t event_type) { + NvmeAsyncEvent *event, *next; + n->aer_mask &= ~(1 << event_type); - if (!QTAILQ_EMPTY(&n->aer_queue)) { - nvme_process_aers(n); + + QTAILQ_FOREACH_SAFE(event, &n->aer_queue, entry, next) { + if (event->result.event_type == event_type) { + QTAILQ_REMOVE(&n->aer_queue, event, entry); + n->aer_queued--; + g_free(event); + } } } @@ -1620,6 +1775,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: @@ -1627,6 +1783,10 @@ static void nvme_aio_err(NvmeRequest *req, int ret) break; } + if (ret == -ECANCELED) { + status = NVME_CMD_ABORT_REQ; + } + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); error_setg_errno(&local_err, -ret, "aio failed"); @@ -1680,7 +1840,7 @@ static uint16_t nvme_check_zone_state_for_write(NvmeZone *zone) trace_pci_nvme_err_zone_is_read_only(zslba); return NVME_ZONE_READ_ONLY; default: - assert(false); + g_assert_not_reached(); } return NVME_INTERNAL_DEV_ERROR; @@ -1734,7 +1894,7 @@ static uint16_t nvme_check_zone_state_for_read(NvmeZone *zone) trace_pci_nvme_err_zone_is_offline(zone->d.zslba); return NVME_ZONE_OFFLINE; default: - assert(false); + g_assert_not_reached(); } return NVME_INTERNAL_DEV_ERROR; @@ -1998,11 +2158,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; @@ -2079,10 +2234,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); } } @@ -2250,7 +2405,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; } } @@ -2259,7 +2414,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; } @@ -2308,7 +2463,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; } @@ -2465,6 +2620,7 @@ next: done: iocb->aiocb = NULL; iocb->common.cb(iocb->common.opaque, iocb->ret); + g_free(iocb->range); qemu_aio_unref(iocb); } @@ -2514,6 +2670,7 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req) uint64_t slba = le64_to_cpu(rw->slba); uint32_t nlb = le16_to_cpu(rw->nlb) + 1; size_t len = nvme_l2b(ns, nlb); + size_t data_len = len; int64_t offset = nvme_l2b(ns, slba); uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control)); uint32_t reftag = le32_to_cpu(rw->reftag); @@ -2533,7 +2690,11 @@ static uint16_t nvme_verify(NvmeCtrl *n, NvmeRequest *req) } } - if (len > n->page_size << n->params.vsl) { + if (nvme_ns_ext(ns) && !(NVME_ID_CTRL_CTRATT_MEM(n->id_ctrl.ctratt))) { + data_len += nvme_m2b(ns, nlb); + } + + if (data_len > (n->page_size << n->params.vsl)) { return NVME_INVALID_FIELD | NVME_DNR; } @@ -2569,6 +2730,7 @@ typedef struct NvmeCopyAIOCB { BlockAIOCB common; BlockAIOCB *aiocb; NvmeRequest *req; + NvmeCtrl *n; int ret; void *ranges; @@ -2587,6 +2749,8 @@ typedef struct NvmeCopyAIOCB { uint64_t slba; NvmeZone *zone; + NvmeNamespace *sns; + uint32_t tcl; } NvmeCopyAIOCB; static void nvme_copy_cancel(BlockAIOCB *aiocb) @@ -2633,13 +2797,19 @@ static void nvme_copy_done(NvmeCopyAIOCB *iocb) 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, - uint16_t *apptag, - uint16_t *appmask, - uint64_t *reftag) +static void nvme_copy_source_range_parse_format0_2(void *ranges, + int idx, uint64_t *slba, + uint32_t *nlb, + uint32_t *snsid, + uint16_t *apptag, + uint16_t *appmask, + uint64_t *reftag) { - NvmeCopySourceRangeFormat0 *_ranges = ranges; + NvmeCopySourceRangeFormat0_2 *_ranges = ranges; + + if (snsid) { + *snsid = le32_to_cpu(_ranges[idx].sparams); + } if (slba) { *slba = le64_to_cpu(_ranges[idx].slba); @@ -2662,13 +2832,19 @@ static void nvme_copy_source_range_parse_format0(void *ranges, int idx, } } -static void nvme_copy_source_range_parse_format1(void *ranges, int idx, - uint64_t *slba, uint32_t *nlb, - uint16_t *apptag, - uint16_t *appmask, - uint64_t *reftag) +static void nvme_copy_source_range_parse_format1_3(void *ranges, int idx, + uint64_t *slba, + uint32_t *nlb, + uint32_t *snsid, + uint16_t *apptag, + uint16_t *appmask, + uint64_t *reftag) { - NvmeCopySourceRangeFormat1 *_ranges = ranges; + NvmeCopySourceRangeFormat1_3 *_ranges = ranges; + + if (snsid) { + *snsid = le32_to_cpu(_ranges[idx].sparams); + } if (slba) { *slba = le64_to_cpu(_ranges[idx].slba); @@ -2700,18 +2876,20 @@ static void nvme_copy_source_range_parse_format1(void *ranges, int idx, static void nvme_copy_source_range_parse(void *ranges, int idx, uint8_t format, uint64_t *slba, uint32_t *nlb, - uint16_t *apptag, uint16_t *appmask, - uint64_t *reftag) + uint32_t *snsid, uint16_t *apptag, + uint16_t *appmask, uint64_t *reftag) { switch (format) { case NVME_COPY_FORMAT_0: - nvme_copy_source_range_parse_format0(ranges, idx, slba, nlb, apptag, - appmask, reftag); + case NVME_COPY_FORMAT_2: + nvme_copy_source_range_parse_format0_2(ranges, idx, slba, nlb, snsid, + apptag, appmask, reftag); break; case NVME_COPY_FORMAT_1: - nvme_copy_source_range_parse_format1(ranges, idx, slba, nlb, apptag, - appmask, reftag); + case NVME_COPY_FORMAT_3: + nvme_copy_source_range_parse_format1_3(ranges, idx, slba, nlb, snsid, + apptag, appmask, reftag); break; default: @@ -2719,15 +2897,34 @@ 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, NULL); + copy_len += nlb; + } + iocb->tcl = copy_len; + 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; NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *dns = req->ns; uint32_t nlb; nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); if (ret < 0) { iocb->ret = ret; @@ -2736,8 +2933,8 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret) goto out; } - if (ns->params.zoned) { - nvme_advance_zone_wp(ns, iocb->zone, nlb); + if (dns->params.zoned) { + nvme_advance_zone_wp(dns, iocb->zone, nlb); } iocb->idx++; @@ -2750,25 +2947,25 @@ static void nvme_copy_out_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *dns = req->ns; uint32_t nlb; size_t mlen; uint8_t *mbounce; - if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { + if (ret < 0 || iocb->ret < 0 || !dns->lbaf.ms) { goto out; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, NULL, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); - mlen = nvme_m2b(ns, nlb); - mbounce = iocb->bounce + nvme_l2b(ns, nlb); + mlen = nvme_m2b(dns, nlb); + mbounce = iocb->bounce + nvme_l2b(dns, nlb); qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, mbounce, mlen); - iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_moff(ns, iocb->slba), + iocb->aiocb = blk_aio_pwritev(dns->blkconf.blk, nvme_moff(dns, iocb->slba), &iocb->iov, 0, nvme_copy_out_completed_cb, iocb); @@ -2782,12 +2979,15 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *sns = iocb->sns; + NvmeNamespace *dns = req->ns; + NvmeCopyCmd *copy = NULL; + uint8_t *mbounce = NULL; uint32_t nlb; uint64_t slba; uint16_t apptag, appmask; uint64_t reftag; - size_t len; + size_t len, mlen; uint16_t status; if (ret < 0) { @@ -2798,43 +2998,51 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, - &nlb, &apptag, &appmask, &reftag); - len = nvme_l2b(ns, nlb); + &nlb, NULL, &apptag, &appmask, &reftag); trace_pci_nvme_copy_out(iocb->slba, nlb); - if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { - NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + len = nvme_l2b(sns, nlb); + + if (NVME_ID_NS_DPS_TYPE(sns->id_ns.dps)) { + copy = (NvmeCopyCmd *)&req->cmd; uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); - uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); - size_t mlen = nvme_m2b(ns, nlb); - uint8_t *mbounce = iocb->bounce + nvme_l2b(ns, nlb); + mlen = nvme_m2b(sns, nlb); + mbounce = iocb->bounce + nvme_l2b(sns, nlb); - status = nvme_dif_mangle_mdata(ns, mbounce, mlen, slba); + status = nvme_dif_mangle_mdata(sns, mbounce, mlen, slba); if (status) { goto invalid; } - status = nvme_dif_check(ns, iocb->bounce, len, mbounce, mlen, prinfor, + status = nvme_dif_check(sns, iocb->bounce, len, mbounce, mlen, prinfor, slba, apptag, appmask, &reftag); if (status) { goto invalid; } + } + + if (NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + copy = (NvmeCopyCmd *)&req->cmd; + uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); + + mlen = nvme_m2b(dns, nlb); + mbounce = iocb->bounce + nvme_l2b(dns, nlb); apptag = le16_to_cpu(copy->apptag); appmask = le16_to_cpu(copy->appmask); if (prinfow & NVME_PRINFO_PRACT) { - status = nvme_check_prinfo(ns, prinfow, iocb->slba, iocb->reftag); + status = nvme_check_prinfo(dns, prinfow, iocb->slba, iocb->reftag); if (status) { goto invalid; } - nvme_dif_pract_generate_dif(ns, iocb->bounce, len, mbounce, mlen, + nvme_dif_pract_generate_dif(dns, iocb->bounce, len, mbounce, mlen, apptag, &iocb->reftag); } else { - status = nvme_dif_check(ns, iocb->bounce, len, mbounce, mlen, + status = nvme_dif_check(dns, iocb->bounce, len, mbounce, mlen, prinfow, iocb->slba, apptag, appmask, &iocb->reftag); if (status) { @@ -2843,13 +3051,13 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) } } - status = nvme_check_bounds(ns, iocb->slba, nlb); + status = nvme_check_bounds(dns, iocb->slba, nlb); if (status) { goto invalid; } - if (ns->params.zoned) { - status = nvme_check_zone_write(ns, iocb->zone, iocb->slba, nlb); + if (dns->params.zoned) { + status = nvme_check_zone_write(dns, iocb->zone, iocb->slba, nlb); if (status) { goto invalid; } @@ -2862,7 +3070,10 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, iocb->bounce, len); - iocb->aiocb = blk_aio_pwritev(ns->blkconf.blk, nvme_l2b(ns, iocb->slba), + block_acct_start(blk_get_stats(dns->blkconf.blk), &iocb->acct.write, 0, + BLOCK_ACCT_WRITE); + + iocb->aiocb = blk_aio_pwritev(dns->blkconf.blk, nvme_l2b(dns, iocb->slba), &iocb->iov, 0, nvme_copy_out_cb, iocb); return; @@ -2877,23 +3088,22 @@ out: static void nvme_copy_in_cb(void *opaque, int ret) { NvmeCopyAIOCB *iocb = opaque; - NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *sns = iocb->sns; uint64_t slba; uint32_t nlb; - if (ret < 0 || iocb->ret < 0 || !ns->lbaf.ms) { + if (ret < 0 || iocb->ret < 0 || !sns->lbaf.ms) { goto out; } nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, - &nlb, NULL, NULL, NULL); + &nlb, NULL, NULL, NULL, NULL); qemu_iovec_reset(&iocb->iov); - qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(ns, nlb), - nvme_m2b(ns, nlb)); + qemu_iovec_add(&iocb->iov, iocb->bounce + nvme_l2b(sns, nlb), + nvme_m2b(sns, nlb)); - iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_moff(ns, slba), + iocb->aiocb = blk_aio_preadv(sns->blkconf.blk, nvme_moff(sns, slba), &iocb->iov, 0, nvme_copy_in_completed_cb, iocb); return; @@ -2902,14 +3112,78 @@ out: nvme_copy_in_completed_cb(iocb, ret); } +static inline bool nvme_csi_supports_copy(uint8_t csi) +{ + return csi == NVME_CSI_NVM || csi == NVME_CSI_ZONED; +} + +static inline bool nvme_copy_ns_format_match(NvmeNamespace *sns, + NvmeNamespace *dns) +{ + return sns->lbaf.ds == dns->lbaf.ds && sns->lbaf.ms == dns->lbaf.ms; +} + +static bool nvme_copy_matching_ns_format(NvmeNamespace *sns, NvmeNamespace *dns, + bool pi_enable) +{ + if (!nvme_csi_supports_copy(sns->csi) || + !nvme_csi_supports_copy(dns->csi)) { + return false; + } + + if (!pi_enable && !nvme_copy_ns_format_match(sns, dns)) { + return false; + } + + if (pi_enable && (!nvme_copy_ns_format_match(sns, dns) || + sns->id_ns.dps != dns->id_ns.dps)) { + return false; + } + + return true; +} + +static inline bool nvme_copy_corresp_pi_match(NvmeNamespace *sns, + NvmeNamespace *dns) +{ + return sns->lbaf.ms == 0 && + ((dns->lbaf.ms == 8 && dns->pif == 0) || + (dns->lbaf.ms == 16 && dns->pif == 1)); +} + +static bool nvme_copy_corresp_pi_format(NvmeNamespace *sns, NvmeNamespace *dns, + bool sns_pi_en) +{ + if (!nvme_csi_supports_copy(sns->csi) || + !nvme_csi_supports_copy(dns->csi)) { + return false; + } + + if (!sns_pi_en && !nvme_copy_corresp_pi_match(sns, dns)) { + return false; + } + + if (sns_pi_en && !nvme_copy_corresp_pi_match(dns, sns)) { + return false; + } + + return true; +} + static void nvme_do_copy(NvmeCopyAIOCB *iocb) { NvmeRequest *req = iocb->req; - NvmeNamespace *ns = req->ns; + NvmeNamespace *sns; + NvmeNamespace *dns = req->ns; + NvmeCopyCmd *copy = (NvmeCopyCmd *)&req->cmd; + uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); + uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); uint64_t slba; uint32_t nlb; size_t len; uint16_t status; + uint32_t dnsid = le32_to_cpu(req->cmd.nsid); + uint32_t snsid = dnsid; if (iocb->ret < 0) { goto done; @@ -2919,40 +3193,124 @@ static void nvme_do_copy(NvmeCopyAIOCB *iocb) goto done; } - nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, &slba, - &nlb, NULL, NULL, NULL); - len = nvme_l2b(ns, nlb); + if (iocb->format == 2 || iocb->format == 3) { + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, + &slba, &nlb, &snsid, NULL, NULL, NULL); + if (snsid != dnsid) { + if (snsid == NVME_NSID_BROADCAST || + !nvme_nsid_valid(iocb->n, snsid)) { + status = NVME_INVALID_NSID | NVME_DNR; + goto invalid; + } + iocb->sns = nvme_ns(iocb->n, snsid); + if (unlikely(!iocb->sns)) { + status = NVME_INVALID_FIELD | NVME_DNR; + goto invalid; + } + } else { + if (((slba + nlb) > iocb->slba) && + ((slba + nlb) < (iocb->slba + iocb->tcl))) { + status = NVME_CMD_OVERLAP_IO_RANGE | NVME_DNR; + goto invalid; + } + } + } else { + nvme_copy_source_range_parse(iocb->ranges, iocb->idx, iocb->format, + &slba, &nlb, NULL, NULL, NULL, NULL); + } + + sns = iocb->sns; + if ((snsid == dnsid) && NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + ((prinfor & NVME_PRINFO_PRACT) != (prinfow & NVME_PRINFO_PRACT))) { + status = NVME_INVALID_FIELD | NVME_DNR; + goto invalid; + } else if (snsid != dnsid) { + if (!NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + !NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if (!nvme_copy_matching_ns_format(sns, dns, false)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + if (NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if ((prinfor & NVME_PRINFO_PRACT) != + (prinfow & NVME_PRINFO_PRACT)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } else { + if (!nvme_copy_matching_ns_format(sns, dns, true)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + } + + if (!NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if (!(prinfow & NVME_PRINFO_PRACT)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } else { + if (!nvme_copy_corresp_pi_format(sns, dns, false)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + } + + if (NVME_ID_NS_DPS_TYPE(sns->id_ns.dps) && + !NVME_ID_NS_DPS_TYPE(dns->id_ns.dps)) { + if (!(prinfor & NVME_PRINFO_PRACT)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } else { + if (!nvme_copy_corresp_pi_format(sns, dns, true)) { + status = NVME_CMD_INCOMP_NS_OR_FMT | NVME_DNR; + goto invalid; + } + } + } + } + len = nvme_l2b(sns, nlb); trace_pci_nvme_copy_source_range(slba, nlb); - if (nlb > le16_to_cpu(ns->id_ns.mssrl)) { + if (nlb > le16_to_cpu(sns->id_ns.mssrl)) { status = NVME_CMD_SIZE_LIMIT | NVME_DNR; goto invalid; } - status = nvme_check_bounds(ns, slba, nlb); + status = nvme_check_bounds(sns, slba, nlb); if (status) { goto invalid; } - if (NVME_ERR_REC_DULBE(ns->features.err_rec)) { - status = nvme_check_dulbe(ns, slba, nlb); + if (NVME_ERR_REC_DULBE(sns->features.err_rec)) { + status = nvme_check_dulbe(sns, slba, nlb); if (status) { goto invalid; } } - if (ns->params.zoned) { - status = nvme_check_zone_read(ns, slba, nlb); + if (sns->params.zoned) { + status = nvme_check_zone_read(sns, slba, nlb); if (status) { goto invalid; } } + g_free(iocb->bounce); + iocb->bounce = g_malloc_n(le16_to_cpu(sns->id_ns.mssrl), + sns->lbasz + sns->lbaf.ms); + qemu_iovec_reset(&iocb->iov); qemu_iovec_add(&iocb->iov, iocb->bounce, len); - iocb->aiocb = blk_aio_preadv(ns->blkconf.blk, nvme_l2b(ns, slba), + block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, 0, + BLOCK_ACCT_READ); + + iocb->aiocb = blk_aio_preadv(sns->blkconf.blk, nvme_l2b(sns, slba), &iocb->iov, 0, nvme_copy_in_cb, iocb); return; @@ -2971,9 +3329,7 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) nvme_misc_cb, req); uint16_t nr = copy->nr + 1; uint8_t format = copy->control[0] & 0xf; - uint16_t prinfor = ((copy->control[0] >> 4) & 0xf); - uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); - size_t len = sizeof(NvmeCopySourceRangeFormat0); + size_t len = sizeof(NvmeCopySourceRangeFormat0_2); uint16_t status; @@ -2982,13 +3338,9 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) iocb->ranges = NULL; iocb->zone = NULL; - if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) && - ((prinfor & NVME_PRINFO_PRACT) != (prinfow & NVME_PRINFO_PRACT))) { - status = NVME_INVALID_FIELD | NVME_DNR; - goto invalid; - } - - if (!(n->id_ctrl.ocfs & (1 << format))) { + if (!(n->id_ctrl.ocfs & (1 << format)) || + ((format == 2 || format == 3) && + !(n->features.hbs.cdfe & (1 << format)))) { trace_pci_nvme_err_copy_invalid_format(format); status = NVME_INVALID_FIELD | NVME_DNR; goto invalid; @@ -2999,14 +3351,14 @@ static uint16_t nvme_copy(NvmeCtrl *n, NvmeRequest *req) goto invalid; } - if ((ns->pif == 0x0 && format != 0x0) || - (ns->pif != 0x0 && format != 0x1)) { + if ((ns->pif == 0x0 && (format != 0x0 && format != 0x2)) || + (ns->pif != 0x0 && (format != 0x1 && format != 0x3))) { status = NVME_INVALID_FORMAT | NVME_DNR; goto invalid; } if (ns->pif) { - len = sizeof(NvmeCopySourceRangeFormat1); + len = sizeof(NvmeCopySourceRangeFormat1_3); } iocb->format = format; @@ -3031,23 +3383,24 @@ 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->ret = 0; iocb->nr = nr; iocb->idx = 0; iocb->reftag = le32_to_cpu(copy->reftag); iocb->reftag |= (uint64_t)le32_to_cpu(copy->cdw3) << 32; - iocb->bounce = g_malloc_n(le16_to_cpu(ns->id_ns.mssrl), - ns->lbasz + ns->lbaf.ms); qemu_iovec_init(&iocb->iov, 1); - block_acct_start(blk_get_stats(ns->blkconf.blk), &iocb->acct.read, 0, - BLOCK_ACCT_READ); - block_acct_start(blk_get_stats(ns->blkconf.blk), &iocb->acct.write, 0, - BLOCK_ACCT_WRITE); - req->aiocb = &iocb->common; + iocb->sns = req->ns; + iocb->n = n; + iocb->bounce = NULL; nvme_do_copy(iocb); return NVME_NO_COMPLETE; @@ -3082,7 +3435,11 @@ static uint16_t nvme_compare(NvmeCtrl *n, NvmeRequest *req) len += nvme_m2b(ns, nlb); } - status = nvme_check_mdts(n, len); + if (NVME_ID_CTRL_CTRATT_MEM(n->id_ctrl.ctratt)) { + status = nvme_check_mdts(n, data_len); + } else { + status = nvme_check_mdts(n, len); + } if (status) { return status; } @@ -3146,7 +3503,6 @@ static void nvme_flush_cancel(BlockAIOCB *acb) 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); @@ -3260,7 +3616,7 @@ static uint16_t nvme_read(NvmeCtrl *n, NvmeRequest *req) BlockBackend *blk = ns->blkconf.blk; uint16_t status; - if (nvme_ns_ext(ns)) { + if (nvme_ns_ext(ns) && !(NVME_ID_CTRL_CTRATT_MEM(n->id_ctrl.ctratt))) { mapped_size += nvme_m2b(ns, nlb); if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { @@ -3312,7 +3668,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: @@ -3320,6 +3676,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) { @@ -3337,7 +3728,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, BlockBackend *blk = ns->blkconf.blk; uint16_t status; - if (nvme_ns_ext(ns)) { + if (nvme_ns_ext(ns) && !(NVME_ID_CTRL_CTRATT_MEM(n->id_ctrl.ctratt))) { mapped_size += nvme_m2b(ns, nlb); if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) { @@ -3429,6 +3820,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); @@ -3445,7 +3838,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, @@ -3981,7 +4374,7 @@ static bool nvme_zone_matches_filter(uint32_t zafs, NvmeZone *zl) static uint16_t nvme_zone_mgmt_recv(NvmeCtrl *n, NvmeRequest *req) { - NvmeCmd *cmd = (NvmeCmd *)&req->cmd; + NvmeCmd *cmd = &req->cmd; NvmeNamespace *ns = req->ns; /* cdw12 is zero-based number of dwords to return. Convert to bytes */ uint32_t data_size = (le32_to_cpu(cmd->cdw12) + 1) << 2; @@ -4045,14 +4438,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; @@ -4086,6 +4479,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_malloc0(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 >> 16) + 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; @@ -4094,10 +4613,6 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_io_cmd(nvme_cid(req), nsid, nvme_sqid(req), req->cmd.opcode, nvme_io_opc_str(req->cmd.opcode)); - if (!nvme_nsid_valid(n, nsid)) { - return NVME_INVALID_NSID | NVME_DNR; - } - /* * In the base NVM command set, Flush may apply to all namespaces * (indicated by NSID being set to FFFFFFFFh). But if that feature is used @@ -4117,10 +4632,15 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) * device only supports namespace types that includes the NVM Flush command * (NVM and Zoned), so always do an NVM Flush. */ + if (req->cmd.opcode == NVME_CMD_FLUSH) { return nvme_flush(n, req); } + if (!nvme_nsid_valid(n, nsid) || nsid == NVME_NSID_BROADCAST) { + return NVME_INVALID_NSID | NVME_DNR; + } + ns = nvme_ns(n, nsid); if (unlikely(!ns)) { return NVME_INVALID_FIELD | NVME_DNR; @@ -4162,8 +4682,12 @@ 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); + g_assert_not_reached(); } return NVME_INVALID_OPCODE | NVME_DNR; @@ -4318,7 +4842,8 @@ static void nvme_init_sq(NvmeSQueue *sq, NvmeCtrl *n, uint64_t dma_addr, QTAILQ_INSERT_TAIL(&(sq->req_list), &sq->io_req[i], entry); } - sq->bh = qemu_bh_new(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); @@ -4386,8 +4911,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]; } @@ -4401,6 +4926,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; @@ -4427,10 +4953,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); @@ -4452,6 +4979,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) { @@ -4577,6 +5146,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; @@ -4589,13 +5364,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; @@ -4624,6 +5400,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; @@ -4632,6 +5418,7 @@ 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; @@ -4642,8 +5429,8 @@ static void nvme_free_cq(NvmeCQueue *cq, NvmeCtrl *n) event_notifier_set_handler(&cq->notifier, NULL); event_notifier_cleanup(&cq->notifier); } - if (msix_enabled(&n->parent_obj)) { - msix_vector_unuse(&n->parent_obj, cq->vector); + if (msix_enabled(pci) && cq->irq_enabled) { + msix_vector_unuse(pci, cq->vector); } if (cq->cqid) { g_free(cq); @@ -4681,9 +5468,12 @@ 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) { - if (msix_enabled(&n->parent_obj)) { - msix_vector_use(&n->parent_obj, vector); + PCIDevice *pci = PCI_DEVICE(n); + + if (msix_enabled(pci) && irq_enabled) { + msix_vector_use(pci, vector); } + cq->ctrl = n; cq->cqid = cqid; cq->size = size; @@ -4705,7 +5495,8 @@ static void nvme_init_cq(NvmeCQueue *cq, NvmeCtrl *n, uint64_t dma_addr, } } n->cq[cqid] = cq; - cq->bh = qemu_bh_new(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) @@ -4717,10 +5508,18 @@ 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 (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; @@ -4733,7 +5532,7 @@ 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; } @@ -4889,14 +5688,14 @@ 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; + uint8_t num_sec_ctrl = n->nr_sec_ctrls; 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, + if (n->sec_ctrl_list[i].scid >= min_id) { + list.numcntl = MIN(num_sec_ctrl - i, 127); + memcpy(&list.sec, n->sec_ctrl_list + i, list.numcntl * sizeof(NvmeSecCtrlEntry)); break; } @@ -4907,6 +5706,33 @@ static uint16_t nvme_identify_sec_ctrl_list(NvmeCtrl *n, NvmeRequest *req) return nvme_c2h(n, (uint8_t *)&list, sizeof(list), req); } +static uint16_t nvme_identify_ns_ind(NvmeCtrl *n, NvmeRequest *req, bool alloc) +{ + NvmeNamespace *ns; + NvmeIdentify *c = (NvmeIdentify *)&req->cmd; + uint32_t nsid = le32_to_cpu(c->nsid); + + trace_pci_nvme_identify_ns_ind(nsid); + + if (!nvme_nsid_valid(n, nsid) || nsid == NVME_NSID_BROADCAST) { + return NVME_INVALID_NSID | NVME_DNR; + } + + ns = nvme_ns(n, nsid); + if (unlikely(!ns)) { + if (alloc) { + ns = nvme_subsys_ns(n->subsys, nsid); + if (!ns) { + return nvme_rpt_empty_id_struct(n, req); + } + } else { + return nvme_rpt_empty_id_struct(n, req); + } + } + + return nvme_c2h(n, (uint8_t *)&ns->id_ns_ind, sizeof(NvmeIdNsInd), req); +} + static uint16_t nvme_identify_ns_csi(NvmeCtrl *n, NvmeRequest *req, bool active) { @@ -5038,6 +5864,26 @@ static uint16_t nvme_identify_nslist_csi(NvmeCtrl *n, NvmeRequest *req, return nvme_c2h(n, list, data_len, req); } +static uint16_t nvme_endurance_group_list(NvmeCtrl *n, NvmeRequest *req) +{ + uint16_t list[NVME_CONTROLLER_LIST_SIZE] = {}; + uint16_t *nr_ids = &list[0]; + uint16_t *ids = &list[1]; + uint16_t endgid = le32_to_cpu(req->cmd.cdw11) & 0xffff; + + /* + * The current nvme-subsys only supports Endurance Group #1. + */ + if (!endgid) { + *nr_ids = 1; + ids[0] = 1; + } else { + *nr_ids = 0; + } + + return nvme_c2h(n, list, sizeof(list), req); +} + static uint16_t nvme_identify_ns_descr_list(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns; @@ -5049,6 +5895,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; @@ -5077,6 +5927,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; @@ -5129,6 +5987,10 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) 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_IND_NS: + return nvme_identify_ns_ind(n, req, false); + case NVME_ID_CNS_CS_IND_NS_ALLOCATED: + return nvme_identify_ns_ind(n, req, true); case NVME_ID_CNS_CS_NS_PRESENT: return nvme_identify_ns_csi(n, req, false); case NVME_ID_CNS_CTRL: @@ -5141,6 +6003,8 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) return nvme_identify_nslist(n, req, false); case NVME_ID_CNS_CS_NS_ACTIVE_LIST: return nvme_identify_nslist_csi(n, req, true); + case NVME_ID_CNS_ENDURANCE_GROUP_LIST: + return nvme_endurance_group_list(n, req); case NVME_ID_CNS_CS_NS_PRESENT_LIST: return nvme_identify_nslist_csi(n, req, false); case NVME_ID_CNS_NS_DESCR_LIST: @@ -5156,12 +6020,40 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeRequest *req) static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *req) { uint16_t sqid = le32_to_cpu(req->cmd.cdw10) & 0xffff; + uint16_t cid = (le32_to_cpu(req->cmd.cdw10) >> 16) & 0xffff; + NvmeSQueue *sq = n->sq[sqid]; + NvmeRequest *r, *next; + int i; req->cqe.result = 1; if (nvme_check_sqid(n, sqid)) { return NVME_INVALID_FIELD | NVME_DNR; } + if (sqid == 0) { + for (i = 0; i < n->outstanding_aers; i++) { + NvmeRequest *re = n->aer_reqs[i]; + if (re->cqe.cid == cid) { + memmove(n->aer_reqs + i, n->aer_reqs + i + 1, + (n->outstanding_aers - i - 1) * sizeof(NvmeRequest *)); + n->outstanding_aers--; + re->status = NVME_CMD_ABORT_REQ; + req->cqe.result = 0; + nvme_enqueue_req_completion(&n->admin_cq, re); + return NVME_SUCCESS; + } + } + } + + QTAILQ_FOREACH_SAFE(r, &sq->out_req_list, entry, next) { + if (r->cqe.cid == cid) { + if (r->aiocb) { + blk_aio_cancel_async(r->aiocb); + } + break; + } + } + return NVME_SUCCESS; } @@ -5207,18 +6099,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, @@ -5316,6 +6287,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; } @@ -5349,6 +6347,22 @@ defaults: 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; + } + break; + + case NVME_WRITE_ATOMICITY: + result = n->dn; + break; default: result = nvme_feature_default[fid]; break; @@ -5356,7 +6370,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) @@ -5374,6 +6388,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; @@ -5386,6 +6445,8 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) uint8_t save = NVME_SETFEAT_SAVE(dw10); uint16_t status; int i; + NvmeIdCtrl *id = &n->id_ctrl; + NvmeAtomic *atomic = &n->atomic; trace_pci_nvme_setfeat(nvme_cid(req), nsid, fid, save, dw11); @@ -5533,6 +6594,27 @@ 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); + case NVME_WRITE_ATOMICITY: + + n->dn = 0x1 & dw11; + + if (n->dn) { + atomic->atomic_max_write_size = le16_to_cpu(id->awupf) + 1; + } else { + atomic->atomic_max_write_size = le16_to_cpu(id->awun) + 1; + } + + if (atomic->atomic_max_write_size == 1) { + atomic->atomic_writes = 0; + } else { + atomic->atomic_writes = 1; + } + break; default: return NVME_FEAT_NOT_CHANGEABLE | NVME_DNR; } @@ -5711,7 +6793,6 @@ static void nvme_format_cancel(BlockAIOCB *aiocb) 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, @@ -5976,6 +7057,7 @@ static uint16_t nvme_assign_virt_res_to_sec(NvmeCtrl *n, NvmeRequest *req, 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; @@ -5985,9 +7067,9 @@ static uint16_t nvme_virt_set_state(NvmeCtrl *n, uint16_t cntlid, bool online) return NVME_INVALID_CTRL_ID | NVME_DNR; } - if (!pci_is_vf(&n->parent_obj)) { + if (!pci_is_vf(pci)) { vf_index = le16_to_cpu(sctrl->vfn) - 1; - sn = NVME(pcie_sriov_get_vf_at_index(&n->parent_obj, vf_index)); + sn = NVME(pcie_sriov_get_vf_at_index(pci, vf_index)); } if (online) { @@ -6045,6 +7127,7 @@ static uint16_t nvme_virt_mngmt(NvmeCtrl *n, NvmeRequest *req) 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; @@ -6071,8 +7154,7 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req) */ sq->db_addr = dbs_addr + (i << 3); sq->ei_addr = eis_addr + (i << 3); - pci_dma_write(&n->parent_obj, sq->db_addr, &sq->tail, - sizeof(sq->tail)); + 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)) { @@ -6085,8 +7167,7 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req) /* 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); - pci_dma_write(&n->parent_obj, cq->db_addr, &cq->head, - sizeof(cq->head)); + 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)) { @@ -6101,6 +7182,61 @@ static uint16_t nvme_dbbuf_config(NvmeCtrl *n, const NvmeRequest *req) 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, @@ -6149,8 +7285,12 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) 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); + g_assert_not_reached(); } return NVME_INVALID_OPCODE | NVME_DNR; @@ -6158,22 +7298,93 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) static void nvme_update_sq_eventidx(const NvmeSQueue *sq) { - uint32_t v = cpu_to_le32(sq->tail); + trace_pci_nvme_update_sq_eventidx(sq->sqid, sq->tail); - pci_dma_write(&sq->ctrl->parent_obj, sq->ei_addr, &v, sizeof(v)); - - trace_pci_nvme_eventidx_sq(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) { - uint32_t v; + ldl_le_pci_dma(PCI_DEVICE(sq->ctrl), sq->db_addr, &sq->tail, + MEMTXATTRS_UNSPECIFIED); - pci_dma_read(&sq->ctrl->parent_obj, sq->db_addr, &v, sizeof(v)); + trace_pci_nvme_update_sq_tail(sq->sqid, sq->tail); +} - sq->tail = le32_to_cpu(v); +#define NVME_ATOMIC_NO_START 0 +#define NVME_ATOMIC_START_ATOMIC 1 +#define NVME_ATOMIC_START_NONATOMIC 2 - trace_pci_nvme_shadow_doorbell_sq(sq->sqid, sq->tail); +static int nvme_atomic_write_check(NvmeCtrl *n, NvmeCmd *cmd, + NvmeAtomic *atomic) +{ + NvmeRwCmd *rw = (NvmeRwCmd *)cmd; + uint64_t slba = le64_to_cpu(rw->slba); + uint32_t nlb = (uint32_t)le16_to_cpu(rw->nlb); + uint64_t elba = slba + nlb; + bool cmd_atomic_wr = true; + int i; + + if ((cmd->opcode == NVME_CMD_READ) || ((cmd->opcode == NVME_CMD_WRITE) && + ((rw->nlb + 1) > atomic->atomic_max_write_size))) { + cmd_atomic_wr = false; + } + + /* + * Walk the queues to see if there are any atomic conflicts. + */ + for (i = 1; i < n->params.max_ioqpairs + 1; i++) { + NvmeSQueue *sq; + NvmeRequest *req; + NvmeRwCmd *req_rw; + uint64_t req_slba; + uint32_t req_nlb; + uint64_t req_elba; + + sq = n->sq[i]; + if (!sq) { + continue; + } + + /* + * Walk all the requests on a given queue. + */ + QTAILQ_FOREACH(req, &sq->out_req_list, entry) { + req_rw = (NvmeRwCmd *)&req->cmd; + + if (((req_rw->opcode == NVME_CMD_WRITE) || + (req_rw->opcode == NVME_CMD_READ)) && + (cmd->nsid == req->ns->params.nsid)) { + req_slba = le64_to_cpu(req_rw->slba); + req_nlb = (uint32_t)le16_to_cpu(req_rw->nlb); + req_elba = req_slba + req_nlb; + + if (cmd_atomic_wr) { + if ((elba >= req_slba) && (slba <= req_elba)) { + return NVME_ATOMIC_NO_START; + } + } else { + if (req->atomic_write && ((elba >= req_slba) && + (slba <= req_elba))) { + return NVME_ATOMIC_NO_START; + } + } + } + } + } + if (cmd_atomic_wr) { + return NVME_ATOMIC_START_ATOMIC; + } + return NVME_ATOMIC_START_NONATOMIC; +} + +static NvmeAtomic *nvme_get_atomic(NvmeCtrl *n, NvmeCmd *cmd) +{ + if (n->atomic.atomic_writes) { + return &n->atomic; + } + return NULL; } static void nvme_process_sq(void *opaque) @@ -6192,13 +7403,36 @@ static void nvme_process_sq(void *opaque) } while (!(nvme_sq_empty(sq) || QTAILQ_EMPTY(&sq->req_list))) { - addr = sq->dma_addr + sq->head * n->sqe_size; + NvmeAtomic *atomic; + bool cmd_is_atomic; + + 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(); stl_le_p(&n->bar.csts, NVME_CSTS_FAILED); break; } + + atomic = nvme_get_atomic(n, &cmd); + + cmd_is_atomic = false; + if (sq->sqid && atomic) { + int ret; + + ret = nvme_atomic_write_check(n, &cmd, atomic); + switch (ret) { + case NVME_ATOMIC_NO_START: + qemu_bh_schedule(sq->bh); + return; + case NVME_ATOMIC_START_ATOMIC: + cmd_is_atomic = true; + break; + case NVME_ATOMIC_START_NONATOMIC: + default: + break; + } + } nvme_inc_sq_head(sq); req = QTAILQ_FIRST(&sq->req_list); @@ -6208,6 +7442,10 @@ static void nvme_process_sq(void *opaque) req->cqe.cid = cmd.cid; memcpy(&req->cmd, &cmd, sizeof(NvmeCmd)); + if (sq->sqid && atomic) { + req->atomic_write = cmd_is_atomic; + } + status = sq->sqid ? nvme_io_cmd(n, req) : nvme_admin_cmd(n, req); if (status != NVME_NO_COMPLETE) { @@ -6239,7 +7477,7 @@ static void nvme_update_msixcap_ts(PCIDevice *pci_dev, uint32_t table_size) static void nvme_activate_virt_res(NvmeCtrl *n) { - PCIDevice *pci_dev = &n->parent_obj; + PCIDevice *pci_dev = PCI_DEVICE(n); NvmePriCtrlCap *cap = &n->pri_ctrl_cap; NvmeSecCtrlEntry *sctrl; @@ -6262,7 +7500,7 @@ static void nvme_activate_virt_res(NvmeCtrl *n) static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) { - PCIDevice *pci_dev = &n->parent_obj; + PCIDevice *pci_dev = PCI_DEVICE(n); NvmeSecCtrlEntry *sctrl; NvmeNamespace *ns; int i; @@ -6295,14 +7533,10 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) 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]; + for (i = 0; i < n->nr_sec_ctrls; i++) { + sctrl = &n->sec_ctrl_list[i]; nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); } - - if (rst != NVME_RESET_CONTROLLER) { - pcie_sriov_pf_disable_vfs(pci_dev); - } } if (rst != NVME_RESET_CONTROLLER) { @@ -6315,6 +7549,8 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) n->outstanding_aers = 0; n->qs_created = false; + n->dn = n->params.atomic_dn; /* Set Disable Normal */ + nvme_update_msixcap_ts(pci_dev, n->conf_msix_qsize); if (pci_is_vf(pci_dev)) { @@ -6379,11 +7615,9 @@ static int nvme_start_ctrl(NvmeCtrl *n) uint32_t page_size = 1 << page_bits; NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); - if (pci_is_vf(&n->parent_obj) && !sctrl->scs) { + 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), - sctrl->scs ? "ONLINE" : - "OFFLINE"); + le16_to_cpu(sctrl->nvq)); return -1; } if (unlikely(n->cq[0])) { @@ -6419,34 +7653,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; @@ -6459,8 +7665,6 @@ 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); @@ -6494,6 +7698,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); @@ -6517,7 +7722,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"); @@ -6530,7 +7735,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"); @@ -6755,7 +7960,7 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size) return 0; } - if (pci_is_vf(&n->parent_obj) && !nvme_sctrl(n)->scs && + 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; @@ -6776,6 +7981,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))) { @@ -6789,7 +7995,6 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) /* Completion queue doorbell write */ uint16_t new_head = val & 0xffff; - int start_sqs; NvmeCQueue *cq; qid = (addr - (0x1000 + (1 << 2))) >> 3; @@ -6802,7 +8007,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." @@ -6840,18 +8045,14 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) trace_pci_nvme_mmio_doorbell_cq(cq->cqid, new_head); - start_sqs = nvme_cq_full(cq) ? 1 : 0; + /* scheduled deferred cqe posting if queue was previously full */ + if (nvme_cq_full(cq)) { + qemu_bh_schedule(cq->bh); + } + cq->head = new_head; if (!qid && n->dbbuf_enabled) { - pci_dma_write(&n->parent_obj, cq->db_addr, &cq->head, - sizeof(cq->head)); - } - if (start_sqs) { - NvmeSQueue *sq; - QTAILQ_FOREACH(sq, &cq->sq_list, entry) { - qemu_bh_schedule(sq->bh); - } - qemu_bh_schedule(cq->bh); + stl_le_pci_dma(pci, cq->db_addr, cq->head, MEMTXATTRS_UNSPECIFIED); } if (cq->tail == cq->head) { @@ -6917,8 +8118,7 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val) * including ones that run on Linux, are not updating Admin Queues, * so we can't trust reading it for an appropriate sq tail. */ - pci_dma_write(&n->parent_obj, sq->db_addr, &sq->tail, - sizeof(sq->tail)); + stl_le_pci_dma(pci, sq->db_addr, sq->tail, MEMTXATTRS_UNSPECIFIED); } qemu_bh_schedule(sq->bh); @@ -6932,7 +8132,7 @@ static void nvme_mmio_write(void *opaque, hwaddr addr, uint64_t data, trace_pci_nvme_mmio_write(addr, data, size); - if (pci_is_vf(&n->parent_obj) && !nvme_sctrl(n)->scs && + 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; @@ -6978,7 +8178,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; @@ -6992,38 +8192,48 @@ 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 (params->mqes < 1) { + error_setg(errp, "mqes property cannot be less than 1"); + 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); @@ -7032,64 +8242,58 @@ 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; - } - - if (params->sriov_max_vfs > NVME_MAX_VFS) { - error_setg(errp, "sriov_max_vfs must be between 0 and %d", - NVME_MAX_VFS); - return; + return false; } if (params->cmb_size_mb) { error_setg(errp, "CMB is not supported with SR-IOV"); - return; + return false; } if (n->pmr.dev) { error_setg(errp, "PMR is not supported with SR-IOV"); - return; + 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; + 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; + 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; + 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; + 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; + return false; } if (params->sriov_max_vi_per_vf && @@ -7097,7 +8301,7 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) 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; + return false; } if (params->sriov_max_vq_per_vf && @@ -7106,20 +8310,25 @@ static void nvme_check_constraints(NvmeCtrl *n, Error **errp) 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; + return false; } } + + return true; } static void nvme_init_state(NvmeCtrl *n) { NvmePriCtrlCap *cap = &n->pri_ctrl_cap; - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; NvmeSecCtrlEntry *sctrl; + PCIDevice *pci = PCI_DEVICE(n); + NvmeAtomic *atomic = &n->atomic; + NvmeIdCtrl *id = &n->id_ctrl; uint8_t max_vfs; int i; - if (pci_is_vf(&n->parent_obj)) { + if (pci_is_vf(pci)) { sctrl = nvme_sctrl(n); max_vfs = 0; n->conf_ioqpairs = sctrl->nvq ? le16_to_cpu(sctrl->nvq) - 1 : 0; @@ -7138,9 +8347,9 @@ static void nvme_init_state(NvmeCtrl *n) n->aer_reqs = g_new0(NvmeRequest *, n->params.aerl + 1); QTAILQ_INIT(&n->aer_queue); - list->numcntl = cpu_to_le16(max_vfs); + n->nr_sec_ctrls = max_vfs; for (i = 0; i < max_vfs; i++) { - sctrl = &list->sec[i]; + sctrl = &list[i]; sctrl->pcid = cpu_to_le16(n->cntlid); sctrl->vfn = cpu_to_le16(i + 1); } @@ -7148,7 +8357,7 @@ static void nvme_init_state(NvmeCtrl *n) cap->cntlid = cpu_to_le16(n->cntlid); cap->crt = NVME_CRT_VQ | NVME_CRT_VI; - if (pci_is_vf(&n->parent_obj)) { + 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 - @@ -7161,7 +8370,7 @@ static void nvme_init_state(NvmeCtrl *n) cap->vqfrt / MAX(max_vfs, 1); } - if (pci_is_vf(&n->parent_obj)) { + if (pci_is_vf(pci)) { cap->viprt = cpu_to_le16(n->conf_msix_qsize); } else { cap->viprt = cpu_to_le16(n->params.msix_qsize - @@ -7173,6 +8382,29 @@ static void nvme_init_state(NvmeCtrl *n) cpu_to_le16(n->params.sriov_max_vi_per_vf) : cap->vifrt / MAX(max_vfs, 1); } + + /* Atomic Write */ + id->awun = cpu_to_le16(n->params.atomic_awun); + id->awupf = cpu_to_le16(n->params.atomic_awupf); + n->dn = n->params.atomic_dn; + + if (id->awun || id->awupf) { + if (id->awupf > id->awun) { + id->awupf = 0; + } + + if (n->dn) { + atomic->atomic_max_write_size = id->awupf + 1; + } else { + atomic->atomic_max_write_size = id->awun + 1; + } + + if (atomic->atomic_max_write_size == 1) { + atomic->atomic_writes = 0; + } else { + atomic->atomic_writes = 1; + } + } } static void nvme_init_cmb(NvmeCtrl *n, PCIDevice *pci_dev) @@ -7217,13 +8449,18 @@ static void nvme_init_pmr(NvmeCtrl *n, PCIDevice *pci_dev) memory_region_set_enabled(&n->pmr.dev->mr, false); } -static uint64_t nvme_bar_size(unsigned total_queues, unsigned total_irqs, - unsigned *msix_table_offset, - unsigned *msix_pba_offset) +static uint64_t nvme_mbar_size(unsigned total_queues, unsigned total_irqs, + unsigned *msix_table_offset, + unsigned *msix_pba_offset) { - uint64_t bar_size, msix_table_size, msix_pba_size; + 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) { @@ -7238,11 +8475,10 @@ static uint64_t nvme_bar_size(unsigned total_queues, unsigned total_irqs, *msix_pba_offset = bar_size; } - msix_pba_size = QEMU_ALIGN_UP(total_irqs, 64) / 8; - bar_size += msix_pba_size; + bar_size += QEMU_ALIGN_UP(total_irqs, 64) / 8; - bar_size = pow2ceil(bar_size); - return bar_size; +out: + return pow2ceil(bar_size); } static void nvme_init_sriov(NvmeCtrl *n, PCIDevice *pci_dev, uint16_t offset) @@ -7250,7 +8486,7 @@ 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_bar_size(le16_to_cpu(cap->vqfrsm), + uint64_t bar_size = nvme_mbar_size(le16_to_cpu(cap->vqfrsm), le16_to_cpu(cap->vifrsm), NULL, NULL); @@ -7284,16 +8520,37 @@ static int nvme_add_pm_capability(PCIDevice *pci_dev, uint8_t offset) return 0; } -static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) +static bool pcie_doe_spdm_rsp(DOECap *doe_cap) { + void *req = pcie_doe_get_write_mbox_ptr(doe_cap); + uint32_t req_len = pcie_doe_get_obj_len(req) * 4; + void *rsp = doe_cap->read_mbox; + uint32_t rsp_len = SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE; + + uint32_t recvd = spdm_socket_rsp(doe_cap->spdm_socket, + SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE, + req, req_len, rsp, rsp_len); + doe_cap->read_mbox_len += DIV_ROUND_UP(recvd, 4); + + return recvd != 0; +} + +static DOEProtocol doe_spdm_prot[] = { + { PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_CMA, pcie_doe_spdm_rsp }, + { PCI_VENDOR_ID_PCI_SIG, PCI_SIG_DOE_SECURED_CMA, pcie_doe_spdm_rsp }, + { } +}; + +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, msix_pba_offset; + unsigned msix_table_offset = 0, msix_pba_offset = 0; + unsigned nr_vectors; int ret; - Error *err = NULL; - - pci_conf[PCI_INTERRUPT_PIN] = 1; + pci_conf[PCI_INTERRUPT_PIN] = pci_is_vf(pci_dev) ? 0 : 1; pci_config_set_prog_interface(pci_conf, 0x2); if (n->params.use_intel_id) { @@ -7309,38 +8566,81 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) pcie_endpoint_cap_init(pci_dev, 0x80); pcie_cap_flr_init(pci_dev); if (n->params.sriov_max_vfs) { - pcie_ari_init(pci_dev, 0x100, 1); + pcie_ari_init(pci_dev, 0x100); } - /* add one to max_ioqpairs to account for the admin queue pair */ - bar_size = nvme_bar_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 { + 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->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); + 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 */ + if (!pci_is_vf(pci_dev)) { + nr_vectors = n->params.msix_qsize; + bar_size = nvme_mbar_size(n->params.max_ioqpairs + 1, + nr_vectors, &msix_table_offset, + &msix_pba_offset); } else { - error_propagate(errp, err); - return ret; + NvmeCtrl *pn = NVME(pcie_sriov_get_pf(pci_dev)); + NvmePriCtrlCap *cap = &pn->pri_ctrl_cap; + + nr_vectors = le16_to_cpu(cap->vifrsm); + bar_size = nvme_mbar_size(le16_to_cpu(cap->vqfrsm), nr_vectors, + &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, nr_vectors, + &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); + pcie_cap_deverr_init(pci_dev); + + /* DOE Initialisation */ + if (pci_dev->spdm_port) { + uint16_t doe_offset = n->params.sriov_max_vfs ? + PCI_CONFIG_SPACE_SIZE + PCI_ARI_SIZEOF + : PCI_CONFIG_SPACE_SIZE; + + pcie_doe_init(pci_dev, &pci_dev->doe_spdm, doe_offset, + doe_spdm_prot, true, 0); + + pci_dev->doe_spdm.spdm_socket = spdm_socket_connect(pci_dev->spdm_port, + errp); + + if (pci_dev->doe_spdm.spdm_socket < 0) { + return false; + } + } + if (n->params.cmb_size_mb) { nvme_init_cmb(n, pci_dev); } @@ -7353,7 +8653,7 @@ static int nvme_init_pci(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) nvme_init_sriov(n, pci_dev, 0x120); } - return 0; + return true; } static void nvme_init_subnqn(NvmeCtrl *n) @@ -7375,6 +8675,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) 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)); @@ -7385,7 +8686,11 @@ 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; + if (n->params.ctratt.mem) { + ctratt |= NVME_CTRATT_MEM; + } id->rab = 6; @@ -7402,7 +8707,8 @@ 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 | NVME_OACS_DBBUF); + cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT | NVME_OACS_DBBUF | + NVME_OACS_DIRECTIVES); id->cntrltype = 0x1; /* @@ -7425,12 +8731,13 @@ 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 | - NVME_ONCS_COMPARE | NVME_ONCS_COPY); + NVME_ONCS_COMPARE | NVME_ONCS_COPY | + NVME_ONCS_NVMCSA | NVME_ONCS_NVMAFC); /* * NOTE: If this device ever supports a command set that does NOT use 0x0 @@ -7441,8 +8748,10 @@ 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); + id->ocfs = cpu_to_le16(NVME_OCFS_COPY_FORMAT_0 | NVME_OCFS_COPY_FORMAT_1 | + NVME_OCFS_COPY_FORMAT_2 | NVME_OCFS_COPY_FORMAT_3); + id->sgls = cpu_to_le32(NVME_CTRL_SGLS_SUPPORT_NO_ALIGN | + NVME_CTRL_SGLS_MPTR_SGL); nvme_init_subnqn(n); @@ -7452,9 +8761,18 @@ 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; + } } - NVME_CAP_SET_MQES(cap, 0x7ff); + id->ctratt = cpu_to_le32(ctratt); + + NVME_CAP_SET_MQES(cap, n->params.mqes); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NVM); @@ -7468,7 +8786,7 @@ 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(&n->parent_obj) && !sctrl->scs) { + if (pci_is_vf(pci_dev) && !sctrl->scs) { stl_le_p(&n->bar.csts, NVME_CSTS_FAILED); } } @@ -7506,34 +8824,43 @@ 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)); if (pci_is_vf(pci_dev)) { /* * VFs derive settings from the parent. PF's lifespan exceeds - * that of VF's, so it's safe to share params.serial. + * 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; + + /* + * Assigning this link (strong link) causes an `object_unref` later in + * `object_release_link_property`. Increment the refcount to balance + * this out. + */ + object_ref(OBJECT(pn->subsys)); } - nvme_check_constraints(n, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!nvme_check_params(n, errp)) { return; } - qbus_init(&n->bus, sizeof(NvmeBus), TYPE_NVME_BUS, - &pci_dev->qdev, n->parent_obj.qdev.id); + 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)) { + if (!nvme_init_pci(n, pci_dev, errp)) { return; } nvme_init_ctrl(n, pci_dev); @@ -7578,6 +8905,11 @@ static void nvme_exit(PCIDevice *pci_dev) g_free(n->cmb.buf); } + if (pci_dev->doe_spdm.spdm_socket > 0) { + spdm_socket_close(pci_dev->doe_spdm.spdm_socket, + SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE); + } + if (n->pmr.dev) { host_memory_backend_set_mapped(n->pmr.dev, false); } @@ -7586,7 +8918,12 @@ static void nvme_exit(PCIDevice *pci_dev) pcie_sriov_pf_exit(pci_dev); } - msix_uninit(pci_dev, &n->bar0, &n->bar0); + if (n->params.msix_exclusive_bar && !pci_is_vf(pci_dev)) { + msix_uninit_exclusive_bar(pci_dev); + } else { + msix_uninit(pci_dev, &n->bar0, &n->bar0); + } + memory_region_del_subregion(&n->bar0, &n->iomem); } @@ -7611,15 +8948,23 @@ static Property nvme_props[] = { 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_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_UINT32("sriov_max_vi_per_vf", NvmeCtrl, + params.sriov_max_vi_per_vf, 0), + DEFINE_PROP_UINT32("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_UINT16("mqes", NvmeCtrl, params.mqes, 0x7ff), + DEFINE_PROP_UINT16("spdm_port", PCIDevice, spdm_port, 0), + DEFINE_PROP_BOOL("ctratt.mem", NvmeCtrl, params.ctratt.mem, false), + DEFINE_PROP_BOOL("atomic.dn", NvmeCtrl, params.atomic_dn, 0), + DEFINE_PROP_UINT16("atomic.awun", NvmeCtrl, params.atomic_awun, 0), + DEFINE_PROP_UINT16("atomic.awupf", NvmeCtrl, params.atomic_awupf, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -7674,36 +9019,40 @@ static void nvme_pci_reset(DeviceState *qdev) nvme_ctrl_reset(n, NVME_RESET_FUNCTION); } -static void nvme_sriov_pre_write_ctrl(PCIDevice *dev, uint32_t address, - uint32_t val, int len) +static void nvme_sriov_post_write_config(PCIDevice *dev, uint16_t old_num_vfs) { NvmeCtrl *n = NVME(dev); NvmeSecCtrlEntry *sctrl; - uint16_t sriov_cap = dev->exp.sriov_cap; - uint32_t off = address - sriov_cap; - int i, num_vfs; + int i; - if (!sriov_cap) { - return; - } - - if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (!(val & PCI_SRIOV_CTRL_VFE)) { - num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); - for (i = 0; i < num_vfs; i++) { - sctrl = &n->sec_ctrl_list.sec[i]; - nvme_virt_set_state(n, le16_to_cpu(sctrl->scid), false); - } - } + for (i = pcie_sriov_num_vfs(dev); i < old_num_vfs; i++) { + sctrl = &n->sec_ctrl_list[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) { - nvme_sriov_pre_write_ctrl(dev, address, val, len); + uint16_t old_num_vfs = pcie_sriov_num_vfs(dev); + + if (pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) { + pcie_doe_write_config(&dev->doe_spdm, address, val, len); + } 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 uint32_t nvme_pci_read_config(PCIDevice *dev, uint32_t address, int len) +{ + uint32_t val; + if (dev->spdm_port && pcie_find_capability(dev, PCI_EXT_CAP_ID_DOE)) { + if (pcie_doe_read_config(&dev->doe_spdm, address, len, &val)) { + return val; + } + } + return pci_default_read_config(dev, address, len); } static const VMStateDescription nvme_vmstate = { @@ -7718,6 +9067,7 @@ static void nvme_class_init(ObjectClass *oc, void *data) pc->realize = nvme_realize; pc->config_write = nvme_pci_write_config; + pc->config_read = nvme_pci_read_config; pc->exit = nvme_exit; pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->revision = 2; @@ -7726,7 +9076,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; + device_class_set_legacy_reset(dc, nvme_pci_reset); } static void nvme_instance_init(Object *obj) diff --git a/hw/nvme/dif.c b/hw/nvme/dif.c index 63c44c86ab..2805128498 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); @@ -575,11 +575,6 @@ uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req) uint8_t *mbuf, *end; int16_t pil = ns->lbaf.ms - nvme_pi_tuple_size(ns); - status = nvme_check_prinfo(ns, prinfo, slba, reftag); - if (status) { - goto err; - } - flags = 0; ctx->mdata.bounce = g_malloc0(mlen); 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 62a1f97be0..526e15aa80 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" @@ -28,6 +30,7 @@ void nvme_ns_init_format(NvmeNamespace *ns) { NvmeIdNs *id_ns = &ns->id_ns; + NvmeIdNsNvm *id_ns_nvm = &ns->id_ns_nvm; BlockDriverInfo bdi; int npdg, ret; int64_t nlbas; @@ -53,6 +56,8 @@ void nvme_ns_init_format(NvmeNamespace *ns) } id_ns->npda = id_ns->npdg = npdg - 1; + id_ns_nvm->npdal = npdg; + id_ns_nvm->npdgl = npdg; } static int nvme_ns_init(NvmeNamespace *ns, Error **errp) @@ -60,6 +65,7 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) static uint64_t ns_count; NvmeIdNs *id_ns = &ns->id_ns; NvmeIdNsNvm *id_ns_nvm = &ns->id_ns_nvm; + NvmeIdNsInd *id_ns_ind = &ns->id_ns_ind; uint8_t ds; uint16_t ms; int i; @@ -70,10 +76,12 @@ static int nvme_ns_init(NvmeNamespace *ns, Error **errp) ns->id_ns.dlfeat = 0x1; /* support DULBE and I/O optimization fields */ - id_ns->nsfeat |= (0x4 | 0x10); + id_ns->nsfeat |= (NVME_ID_NS_NSFEAT_DAE | NVME_ID_NS_NSFEAT_OPTPERF_ALL); if (ns->params.shared) { - id_ns->nmic |= NVME_NMIC_NS_SHARED; + id_ns->nmic |= NVME_ID_NS_IND_NMIC_SHRNS; + id_ns_ind->nmic = NVME_ID_NS_IND_NMIC_SHRNS; + id_ns_ind->nstat = NVME_ID_NS_IND_NSTAT_NRDY; } /* Substitute a missing EUI-64 by an autogenerated one */ @@ -87,6 +95,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 +114,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 +127,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 +386,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 +584,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 +674,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 +703,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) @@ -561,6 +743,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)) { @@ -591,6 +775,9 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) if (subsys) { subsys->namespaces[nsid] = ns; + ns->id_ns.endgid = cpu_to_le16(0x1); + ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); + if (ns->params.detached) { return; } @@ -606,6 +793,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) return; } + } nvme_attach_ns(n, ns); @@ -617,6 +805,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), @@ -644,6 +833,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 7adf042ec3..7242206910 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -19,7 +19,7 @@ #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" @@ -27,6 +27,15 @@ #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); @@ -45,17 +54,68 @@ typedef struct NvmeBus { 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; NvmeBus bus; 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; @@ -96,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; @@ -125,8 +214,17 @@ typedef struct NvmeNamespaceParams { uint32_t numzrwa; uint64_t zrwas; uint64_t zrwafg; + + struct { + char *ruhs; + } fdp; } NvmeNamespaceParams; +typedef struct NvmeAtomic { + uint32_t atomic_max_write_size; + bool atomic_writes; +} NvmeAtomic; + typedef struct NvmeNamespace { DeviceState parent_obj; BlockConf blkconf; @@ -135,6 +233,7 @@ typedef struct NvmeNamespace { int64_t moff; NvmeIdNs id_ns; NvmeIdNsNvm id_ns_nvm; + NvmeIdNsInd id_ns_ind; NvmeLBAF lbaf; unsigned int nlbaf; size_t lbasz; @@ -167,10 +266,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) @@ -274,6 +381,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); @@ -314,6 +427,7 @@ typedef struct NvmeRequest { NvmeCmd cmd; BlockAcctCookie acct; NvmeSg sg; + bool atomic_write; QTAILQ_ENTRY(NvmeRequest)entry; } NvmeRequest; @@ -340,7 +454,9 @@ 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"; @@ -412,6 +528,7 @@ typedef struct NvmeParams { uint32_t num_queues; /* deprecated since 5.1 */ uint32_t max_ioqpairs; uint16_t msix_qsize; + uint16_t mqes; uint32_t cmb_size_mb; uint8_t aerl; uint32_t aer_max_queued; @@ -422,11 +539,20 @@ typedef struct NvmeParams { bool auto_transition_zones; bool legacy_cmb; bool ioeventfd; - uint8_t sriov_max_vfs; + uint16_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; + uint32_t sriov_max_vq_per_vf; + uint32_t sriov_max_vi_per_vf; + bool msix_exclusive_bar; + + struct { + bool mem; + } ctratt; + + uint16_t atomic_awun; + uint16_t atomic_awupf; + bool atomic_dn; } NvmeParams; typedef struct NvmeCtrl { @@ -442,8 +568,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 max_q_ents; uint8_t outstanding_aers; uint32_t irq_status; @@ -504,11 +628,14 @@ typedef struct NvmeCtrl { } features; NvmePriCtrlCap pri_ctrl_cap; - NvmeSecCtrlList sec_ctrl_list; + uint32_t nr_sec_ctrls; + NvmeSecCtrlEntry *sec_ctrl_list; struct { uint16_t vqrfap; uint16_t virfap; } next_pri_ctrl_cap; /* These override pri_ctrl_cap after reset */ + uint32_t dn; /* Disable Normal */ + NvmeAtomic atomic; } NvmeCtrl; typedef enum NvmeResetType { @@ -554,7 +681,7 @@ static inline NvmeSecCtrlEntry *nvme_sctrl(NvmeCtrl *n) 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 &pf->sec_ctrl_list[pcie_sriov_vf_number(pci_dev)]; } return NULL; @@ -563,12 +690,12 @@ static inline NvmeSecCtrlEntry *nvme_sctrl(NvmeCtrl *n) static inline NvmeSecCtrlEntry *nvme_sctrl_for_cntlid(NvmeCtrl *n, uint16_t cntlid) { - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *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]; + for (i = 0; i < n->nr_sec_ctrls; i++) { + if (le16_to_cpu(list[i].scid) == cntlid) { + return &list[i]; } } diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 9d2643678b..77deaf2c2c 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -7,20 +7,23 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "nvme.h" +#define NVME_DEFAULT_RU_SIZE (96 * MiB) + static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num) { NvmeSubsystem *subsys = n->subsys; - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; NvmeSecCtrlEntry *sctrl; int i, cnt = 0; for (i = start; i < ARRAY_SIZE(subsys->ctrls) && cnt < num; i++) { if (!subsys->ctrls[i]) { - sctrl = &list->sec[cnt]; + sctrl = &list[cnt]; sctrl->scid = cpu_to_le16(i); subsys->ctrls[i] = SUBSYS_SLOT_RSVD; cnt++; @@ -33,12 +36,12 @@ static int nvme_subsys_reserve_cntlids(NvmeCtrl *n, int start, int num) static void nvme_subsys_unreserve_cntlids(NvmeCtrl *n) { NvmeSubsystem *subsys = n->subsys; - NvmeSecCtrlList *list = &n->sec_ctrl_list; + NvmeSecCtrlEntry *list = n->sec_ctrl_list; NvmeSecCtrlEntry *sctrl; int i, cntlid; for (i = 0; i < n->params.sriov_max_vfs; i++) { - sctrl = &list->sec[i]; + sctrl = &list[i]; cntlid = le16_to_cpu(sctrl->scid); if (cntlid) { @@ -58,6 +61,8 @@ int nvme_subsys_register_ctrl(NvmeCtrl *n, Error **errp) if (pci_is_vf(&n->parent_obj)) { cntlid = le16_to_cpu(sctrl->scid); } else { + n->sec_ctrl_list = g_new0(NvmeSecCtrlEntry, num_vfs); + for (cntlid = 0; cntlid < ARRAY_SIZE(subsys->ctrls); cntlid++) { if (!subsys->ctrls[cntlid]) { break; @@ -109,13 +114,97 @@ void nvme_subsys_unregister_ctrl(NvmeSubsystem *subsys, NvmeCtrl *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) @@ -124,11 +213,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 fccb79f489..6be0bfa1c1 100644 --- a/hw/nvme/trace-events +++ b/hw/nvme/trace-events @@ -56,6 +56,7 @@ pci_nvme_identify(uint16_t cid, uint8_t cns, uint16_t ctrlid, uint8_t csi) "cid 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_ns_ind(uint32_t nsid) "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"" @@ -84,8 +85,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_eventidx_cq(uint16_t cqid, uint16_t new_eventidx) "cqid %"PRIu16" new_eventidx %"PRIu16"" -pci_nvme_eventidx_sq(uint16_t sqid, uint16_t new_eventidx) "sqid %"PRIu16" new_eventidx %"PRIu16"" +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"" @@ -102,8 +103,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_shadow_doorbell_cq(uint16_t cqid, uint16_t new_shadow_doorbell) "cqid %"PRIu16" new_shadow_doorbell %"PRIu16"" -pci_nvme_shadow_doorbell_sq(uint16_t sqid, uint16_t new_shadow_doorbell) "sqid %"PRIu16" new_shadow_doorbell %"PRIu16"" +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"" @@ -117,6 +118,7 @@ pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32", sl 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" @@ -167,6 +169,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"" @@ -186,7 +189,7 @@ 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, const char *state) "nvme_start_ctrl failed due to ctrl state: vi=%u vq=%u %s" +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" diff --git a/hw/nvram/bcm2835_otp.c b/hw/nvram/bcm2835_otp.c new file mode 100644 index 0000000000..c4aed28472 --- /dev/null +++ b/hw/nvram/bcm2835_otp.c @@ -0,0 +1,187 @@ +/* + * BCM2835 One-Time Programmable (OTP) Memory + * + * The OTP implementation is mostly a stub except for the OTP rows + * which are accessed directly by other peripherals such as the mailbox. + * + * The OTP registers are unimplemented due to lack of documentation. + * + * Copyright (c) 2024 Rayhan Faizel + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/nvram/bcm2835_otp.h" +#include "migration/vmstate.h" + +/* OTP rows are 1-indexed */ +uint32_t bcm2835_otp_get_row(BCM2835OTPState *s, unsigned int row) +{ + assert(row <= BCM2835_OTP_ROW_COUNT && row >= 1); + + return s->otp_rows[row - 1]; +} + +void bcm2835_otp_set_row(BCM2835OTPState *s, unsigned int row, + uint32_t value) +{ + assert(row <= BCM2835_OTP_ROW_COUNT && row >= 1); + + /* Real OTP rows work as e-fuses */ + s->otp_rows[row - 1] |= value; +} + +static uint64_t bcm2835_otp_read(void *opaque, hwaddr addr, unsigned size) +{ + switch (addr) { + case BCM2835_OTP_BOOTMODE_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BOOTMODE_REG\n"); + break; + case BCM2835_OTP_CONFIG_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CONFIG_REG\n"); + break; + case BCM2835_OTP_CTRL_LO_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_LO_REG\n"); + break; + case BCM2835_OTP_CTRL_HI_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_HI_REG\n"); + break; + case BCM2835_OTP_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_STATUS_REG\n"); + break; + case BCM2835_OTP_BITSEL_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BITSEL_REG\n"); + break; + case BCM2835_OTP_DATA_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_DATA_REG\n"); + break; + case BCM2835_OTP_ADDR_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_ADDR_REG\n"); + break; + case BCM2835_OTP_WRITE_DATA_READ_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_WRITE_DATA_READ_REG\n"); + break; + case BCM2835_OTP_INIT_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_INIT_STATUS_REG\n"); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } + + return 0; +} + +static void bcm2835_otp_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + switch (addr) { + case BCM2835_OTP_BOOTMODE_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BOOTMODE_REG\n"); + break; + case BCM2835_OTP_CONFIG_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CONFIG_REG\n"); + break; + case BCM2835_OTP_CTRL_LO_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_LO_REG\n"); + break; + case BCM2835_OTP_CTRL_HI_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_CTRL_HI_REG\n"); + break; + case BCM2835_OTP_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_STATUS_REG\n"); + break; + case BCM2835_OTP_BITSEL_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_BITSEL_REG\n"); + break; + case BCM2835_OTP_DATA_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_DATA_REG\n"); + break; + case BCM2835_OTP_ADDR_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_ADDR_REG\n"); + break; + case BCM2835_OTP_WRITE_DATA_READ_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_WRITE_DATA_READ_REG\n"); + break; + case BCM2835_OTP_INIT_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "bcm2835_otp: BCM2835_OTP_INIT_STATUS_REG\n"); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps bcm2835_otp_ops = { + .read = bcm2835_otp_read, + .write = bcm2835_otp_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void bcm2835_otp_realize(DeviceState *dev, Error **errp) +{ + BCM2835OTPState *s = BCM2835_OTP(dev); + memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_otp_ops, s, + TYPE_BCM2835_OTP, 0x80); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + + memset(s->otp_rows, 0x00, sizeof(s->otp_rows)); +} + +static const VMStateDescription vmstate_bcm2835_otp = { + .name = TYPE_BCM2835_OTP, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(otp_rows, BCM2835OTPState, BCM2835_OTP_ROW_COUNT), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_otp_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_otp_realize; + dc->vmsd = &vmstate_bcm2835_otp; +} + +static const TypeInfo bcm2835_otp_info = { + .name = TYPE_BCM2835_OTP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835OTPState), + .class_init = bcm2835_otp_class_init, +}; + +static void bcm2835_otp_register_types(void) +{ + type_register_static(&bcm2835_otp_info); +} + +type_init(bcm2835_otp_register_types) 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 1081e2cc0d..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), @@ -321,7 +321,7 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) /* Output DO is tristate, read results in 1. */ eeprom->eedo = 1; logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(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 2d4d8b952f..ec748e58e7 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 @@ -87,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; } @@ -104,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); } @@ -128,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); @@ -151,19 +195,12 @@ static void at24c_eeprom_realize(DeviceState *dev, Error **errp) } ee->mem = g_malloc0(ee->rsize); -} - -static -void at24c_eeprom_reset(DeviceState *state) -{ - EEPROMState *ee = AT24C_EE(state); - - ee->changed = false; - ee->cur = 0; - ee->haveaddr = 0; - 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); @@ -173,10 +210,33 @@ void at24c_eeprom_reset(DeviceState *state) } 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 +void at24c_eeprom_reset(DeviceState *state) +{ + EEPROMState *ee = AT24C_EE(state); + + ee->changed = false; + ee->cur = 0; + ee->haveaddr = 0; } 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() @@ -194,7 +254,7 @@ void at24c_eeprom_class_init(ObjectClass *klass, void *data) k->send = &at24c_eeprom_send; device_class_set_props(dc, at24c_eeprom_props); - dc->reset = at24c_eeprom_reset; + device_class_set_legacy_reset(dc, at24c_eeprom_reset); } static diff --git a/hw/nvram/fw_cfg-acpi.c b/hw/nvram/fw_cfg-acpi.c new file mode 100644 index 0000000000..2e6ef89b98 --- /dev/null +++ b/hw/nvram/fw_cfg-acpi.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 371a45dfe2..b644577734 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" @@ -201,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) { @@ -656,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() }, @@ -668,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), @@ -680,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, @@ -877,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) @@ -1142,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); @@ -1155,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); @@ -1163,17 +1165,12 @@ 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; } -FWCfgState *fw_cfg_init_io(uint32_t iobase) -{ - return fw_cfg_init_io_dma(iobase, 0, NULL); -} - FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, uint32_t data_width, hwaddr dma_addr, AddressSpace *dma_as) @@ -1258,7 +1255,7 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = fw_cfg_reset; + device_class_set_legacy_reset(dc, fw_cfg_reset); dc->vmsd = &vmstate_fw_cfg; device_class_set_props(dc, fw_cfg_properties); diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 3d9ddda217..e47e52a677 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -24,14 +24,18 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/nvram/chrp_nvram.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 +#include /* for adler32 */ #define DEF_SYSTEM_SIZE 0xc10 @@ -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() }; @@ -115,7 +147,7 @@ static void macio_nvram_class_init(ObjectClass *oc, void *data) dc->realize = macio_nvram_realizefn; dc->unrealize = macio_nvram_unrealizefn; - dc->reset = macio_nvram_reset; + device_class_set_legacy_reset(dc, macio_nvram_reset); dc->vmsd = &vmstate_macio_nvram; device_class_set_props(dc, macio_nvram_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/nvram/meson.build b/hw/nvram/meson.build index f5ee9f6b88..10f3639db6 100644 --- a/hw/nvram/meson.build +++ b/hw/nvram/meson.build @@ -1,23 +1,21 @@ -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_RASPI', if_true: files('bcm2835_otp.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..b1f81752a3 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), @@ -381,7 +378,7 @@ static void nrf51_nvm_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, nrf51_nvm_properties); dc->vmsd = &vmstate_nvm; dc->realize = nrf51_nvm_realize; - dc->reset = nrf51_nvm_reset; + device_class_set_legacy_reset(dc, nrf51_nvm_reset); } static const TypeInfo nrf51_nvm_info = { diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 2d72f30442..bfd8aa367e 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -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 c6b484cc85..1bc58e90ad 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 @@ -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, ResetType type) { - XlnxBBRam *s = XLNX_BBRAM(dev); + XlnxBBRam *s = XLNX_BBRAM(obj); unsigned int i; for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { @@ -455,9 +456,8 @@ static void bbram_ctrl_init(Object *obj) { XlnxBBRam *s = XLNX_BBRAM(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, ARRAY_SIZE(bbram_ctrl_regs_info), s->regs_info, s->regs, @@ -465,10 +465,17 @@ static void bbram_ctrl_init(Object *obj) XLNX_BBRAM_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq_bbram); } +static void bbram_ctrl_finalize(Object *obj) +{ + XlnxBBRam *s = XLNX_BBRAM(obj); + + register_finalize_block(s->reg_array); +} + static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -507,7 +514,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 +529,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); @@ -535,6 +543,7 @@ static const TypeInfo bbram_ctrl_info = { .instance_size = sizeof(XlnxBBRam), .class_init = bbram_ctrl_class_init, .instance_init = bbram_ctrl_init, + .instance_finalize = bbram_ctrl_finalize, }; static void bbram_ctrl_register_types(void) diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index fdfffaab99..f7b849f7de 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -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..8252a5cabe 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, ResetType type) { - 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) { @@ -711,9 +712,8 @@ static void efuse_ctrl_init(Object *obj) { XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), efuse_ctrl_regs_info, ARRAY_SIZE(efuse_ctrl_regs_info), s->regs_info, s->regs, @@ -721,15 +721,23 @@ static void efuse_ctrl_init(Object *obj) XLNX_VERSAL_EFUSE_CTRL_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq_efuse_imr); } +static void efuse_ctrl_finalize(Object *obj) +{ + XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); + + register_finalize_block(s->reg_array); + 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..4e2d1b9d1e 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, ResetType type) { - 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) { @@ -802,9 +803,8 @@ static void zynqmp_efuse_init(Object *obj) { XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), zynqmp_efuse_regs_info, ARRAY_SIZE(zynqmp_efuse_regs_info), s->regs_info, s->regs, @@ -812,15 +812,22 @@ static void zynqmp_efuse_init(Object *obj) ZYNQMP_EFUSE_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq); } +static void zynqmp_efuse_finalize(Object *obj) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); + + register_finalize_block(s->reg_array); +} + 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 +844,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); @@ -851,6 +859,7 @@ static const TypeInfo efuse_info = { .instance_size = sizeof(XlnxZynqMPEFuse), .class_init = zynqmp_efuse_class_init, .instance_init = zynqmp_efuse_init, + .instance_finalize = zynqmp_efuse_finalize, }; static void efuse_register_types(void) diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig index 97af258b55..0702f622a5 100644 --- a/hw/openrisc/Kconfig +++ b/hw/openrisc/Kconfig @@ -1,18 +1,24 @@ config OR1K_SIM bool - select SERIAL + default y + depends on OPENRISC + select DEVICE_TREE + select SERIAL_MM select OPENCORES_ETH select OMPIC select SPLIT_IRQ config OR1K_VIRT bool + default y + depends on OPENRISC imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES + select DEVICE_TREE select PCI select PCI_EXPRESS_GENERIC_BRIDGE select GOLDFISH_RTC - select SERIAL + select SERIAL_MM select SIFIVE_TEST select VIRTIO_MMIO diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 007e80cd5a..55475aa6d6 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -15,6 +15,7 @@ #include "sysemu/device_tree.h" #include "sysemu/qtest.h" #include "sysemu/reset.h" +#include "qemu/error-report.h" #include diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 10163b391b..87aa353323 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -29,7 +29,8 @@ /* Tick Timer global state to allow all cores to be in sync */ typedef struct OR1KTimerState { uint32_t ttcr; - uint64_t last_clk; + uint32_t ttcr_offset; + uint64_t clk_offset; } OR1KTimerState; static OR1KTimerState *or1k_timer; @@ -37,6 +38,8 @@ static OR1KTimerState *or1k_timer; void cpu_openrisc_count_set(OpenRISCCPU *cpu, uint32_t val) { or1k_timer->ttcr = val; + or1k_timer->ttcr_offset = val; + or1k_timer->clk_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } uint32_t cpu_openrisc_count_get(OpenRISCCPU *cpu) @@ -53,9 +56,8 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu) return; } now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - or1k_timer->ttcr += (uint32_t)((now - or1k_timer->last_clk) - / TIMER_PERIOD); - or1k_timer->last_clk = now; + or1k_timer->ttcr = or1k_timer->ttcr_offset + + DIV_ROUND_UP(now - or1k_timer->clk_offset, TIMER_PERIOD); } /* Update the next timeout time as difference between ttmr and ttcr */ @@ -69,7 +71,7 @@ void cpu_openrisc_timer_update(OpenRISCCPU *cpu) } cpu_openrisc_count_update(cpu); - now = or1k_timer->last_clk; + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if ((cpu->env.ttmr & TTMR_TP) <= (or1k_timer->ttcr & TTMR_TP)) { wait = TTMR_TP - (or1k_timer->ttcr & TTMR_TP) + 1; @@ -110,7 +112,8 @@ static void openrisc_timer_cb(void *opaque) case TIMER_NONE: break; case TIMER_INTR: - or1k_timer->ttcr = 0; + /* Zero the count by applying a negative offset to the counter */ + or1k_timer->ttcr_offset -= (cpu->env.ttmr & TTMR_TP); break; case TIMER_SHOT: cpu_openrisc_count_stop(cpu); @@ -137,17 +140,18 @@ static void openrisc_count_reset(void *opaque) /* 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); + OpenRISCCPU *cpu = opaque; + cpu_openrisc_count_set(cpu, 0); } static const VMStateDescription vmstate_or1k_timer = { .name = "or1k_timer", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { + .version_id = 2, + .minimum_version_id = 2, + .fields = (const VMStateField[]) { VMSTATE_UINT32(ttcr, OR1KTimerState), - VMSTATE_UINT64(last_clk, OR1KTimerState), + VMSTATE_UINT32(ttcr_offset, OR1KTimerState), + VMSTATE_UINT64(clk_offset, OR1KTimerState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/openrisc/meson.build b/hw/openrisc/meson.build index 2dbc6365bb..82f1f0ef1c 100644 --- a/hw/openrisc/meson.build +++ b/hw/openrisc/meson.build @@ -1,7 +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]) +openrisc_ss.add(when: 'CONFIG_OR1K_SIM', if_true: files('openrisc_sim.c')) +openrisc_ss.add(when: 'CONFIG_OR1K_VIRT', if_true: files('virt.c')) hw_arch += {'openrisc': openrisc_ss} diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 35da123aef..42f002985b 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -24,7 +24,7 @@ #include "cpu.h" #include "hw/irq.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "net/net.h" #include "hw/openrisc/boot.h" #include "hw/qdev-properties.h" @@ -170,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; @@ -178,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); @@ -248,7 +250,7 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, void *fdt = state->fdt; char *nodename; qemu_irq serial_irq; - char alias[sizeof("uart0")]; + char alias[sizeof("serial0")]; int i; if (num_cpus > 1) { @@ -263,7 +265,7 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, serial_irq = get_cpu_irq(cpus, 0, irq_pin); } serial_mm_init(get_system_memory(), base, 0, serial_irq, 115200, - serial_hd(OR1KSIM_UART_COUNT - uart_idx - 1), + serial_hd(uart_idx), DEVICE_NATIVE_ENDIAN); /* Add device tree node for serial. */ @@ -275,10 +277,13 @@ static void openrisc_sim_serial_init(Or1ksimState *state, hwaddr base, qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", OR1KSIM_CLK_MHZ); qemu_fdt_setprop(fdt, nodename, "big-endian", NULL, 0); - /* The /chosen node is created during fdt creation. */ - qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); - snprintf(alias, sizeof(alias), "uart%d", uart_idx); + if (uart_idx == 0) { + /* The /chosen node is created during fdt creation. */ + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + } + snprintf(alias, sizeof(alias), "serial%d", uart_idx); qemu_fdt_setprop_string(fdt, "/aliases", alias, nodename); + g_free(nodename); } @@ -313,12 +318,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, @@ -326,11 +329,22 @@ static void openrisc_sim_init(MachineState *machine) smp_cpus, cpus, OR1KSIM_OMPIC_IRQ); } - for (n = 0; n < OR1KSIM_UART_COUNT; ++n) + /* + * We create the UART nodes starting with the highest address and + * working downwards, because in QEMU the DTB nodes end up in the + * DTB in reverse order of creation. Correctly-written guest software + * will not care about the node order (it will look at stdout-path + * or the alias nodes), but for the benefit of guest software which + * just looks for the first UART node in the DTB, make sure the + * lowest-address UART (which is QEMU's first serial port) appears + * first in the DTB. + */ + for (n = OR1KSIM_UART_COUNT - 1; n >= 0; n--) { openrisc_sim_serial_init(state, or1ksim_memmap[OR1KSIM_UART].base + or1ksim_memmap[OR1KSIM_UART].size * n, or1ksim_memmap[OR1KSIM_UART].size, smp_cpus, cpus, OR1KSIM_UART_IRQ, n); + } load_addr = openrisc_load_kernel(ram_size, kernel_filename, &boot_info.bootstrap_pc); diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index f8a68a6a6b..47d2c9bd3c 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -14,7 +14,7 @@ #include "exec/address-spaces.h" #include "hw/irq.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/core/split-irq.h" #include "hw/openrisc/boot.h" #include "hw/misc/sifive_test.h" diff --git a/hw/pci-bridge/Kconfig b/hw/pci-bridge/Kconfig index 02614f49aa..449ec98643 100644 --- a/hw/pci-bridge/Kconfig +++ b/hw/pci-bridge/Kconfig @@ -1,8 +1,18 @@ +config PCI_BRIDGE + bool + default y if PCI_DEVICES + depends on PCI + config PCIE_PORT bool 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 index a361e519d0..c347ac06f3 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -13,9 +13,12 @@ #include "hw/pci/msi.h" #include "hw/pci/pcie.h" #include "hw/pci/pcie_port.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/cxl/cxl.h" #include "qapi/error.h" -typedef struct CXLDownStreamPort { +typedef struct CXLDownstreamPort { /*< private >*/ PCIESlot parent_obj; @@ -23,9 +26,6 @@ typedef struct CXLDownStreamPort { CXLComponentState cxl_cstate; } CXLDownstreamPort; -#define TYPE_CXL_DSP "cxl-downstream" -DECLARE_INSTANCE_CHECKER(CXLDownstreamPort, CXL_DSP, TYPE_CXL_DSP) - #define CXL_DOWNSTREAM_PORT_MSI_OFFSET 0x70 #define CXL_DOWNSTREAM_PORT_MSI_NR_VECTOR 1 #define CXL_DOWNSTREAM_PORT_EXP_OFFSET 0x90 @@ -42,7 +42,7 @@ static void latch_registers(CXLDownstreamPort *dsp) CXL2_DOWNSTREAM_PORT); } -/* TODO: Look at sharing this code acorss all CXL port types */ +/* 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) { @@ -98,7 +98,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_DOWNSTREAM_PORT, EXTENSIONS_PORT_DVSEC_LENGTH, EXTENSIONS_PORT_DVSEC, @@ -111,9 +111,9 @@ static void build_dvsecs(CXLComponentState *cxl) .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ }; cxl_component_create_dvsec(cxl, CXL2_DOWNSTREAM_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 *)&(CXLDVSECPortGPF){ .rsvd = 0, @@ -212,12 +212,20 @@ static void cxl_dsp_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } +static Property cxl_dsp_props[] = { + DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot, + speed, PCIE_LINK_SPEED_64), + DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, + width, PCIE_LINK_WIDTH_16), + DEFINE_PROP_END_OF_LIST() +}; + static void cxl_dsp_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); - k->is_bridge = true; + device_class_set_props(dc, cxl_dsp_props); k->config_write = cxl_dsp_config_write; k->realize = cxl_dsp_realize; k->exit = cxl_dsp_exitfn; @@ -226,7 +234,7 @@ static void cxl_dsp_class_init(ObjectClass *oc, void *data) k->revision = 0; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "CXL Switch Downstream Port"; - dc->reset = cxl_dsp_reset; + device_class_set_legacy_reset(dc, cxl_dsp_reset); } static const TypeInfo cxl_dsp_info = { diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index fb213fa06e..5e2156d7ba 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -22,13 +22,19 @@ #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/qdev-properties-system.h" #include "hw/sysbus.h" #include "qapi/error.h" #include "hw/cxl/cxl.h" #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 +53,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 +108,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 +130,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 +176,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 +187,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, ResetType type) { - 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, type); + } latch_registers(crp); } @@ -156,6 +207,10 @@ static Property gen_rp_props[] = { -1), DEFINE_PROP_SIZE("pref64-reserve", CXLRootPort, res_reserve.mem_pref_64, -1), + DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot, + speed, PCIE_LINK_SPEED_64), + DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, + width, PCIE_LINK_WIDTH_32), DEFINE_PROP_END_OF_LIST() }; @@ -181,16 +236,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 +267,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 +278,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 index 9b8b57df9d..55f8b0053f 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -11,26 +11,26 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.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_DVSEC_OFFSET \ +#define CXL_UPSTREAM_PORT_SN_OFFSET \ (CXL_UPSTREAM_PORT_AER_OFFSET + PCI_ERR_SIZEOF) - -typedef struct CXLUpstreamPort { - /*< private >*/ - PCIEPort parent_obj; - - /*< public >*/ - CXLComponentState cxl_cstate; - DOECap doe_cdat; -} CXLUpstreamPort; +#define CXL_UPSTREAM_PORT_DVSEC_OFFSET \ + (CXL_UPSTREAM_PORT_SN_OFFSET + PCI_EXT_CAP_DSN_SIZEOF) CXLComponentState *cxl_usp_to_cstate(CXLUpstreamPort *usp) { @@ -101,6 +101,7 @@ static void cxl_usp_reset(DeviceState *qdev) pci_bridge_reset(qdev); pcie_cap_deverr_reset(d); + pcie_cap_fill_link_ep_usp(d, usp->width, usp->speed); latch_registers(usp); } @@ -108,7 +109,7 @@ static void build_dvsecs(CXLComponentState *cxl) { uint8_t *dvsec; - dvsec = (uint8_t *)&(CXLDVSECPortExtensions){ + dvsec = (uint8_t *)&(CXLDVSECPortExt){ .status = 0x1, /* Port Power Management Init Complete */ }; cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_PORT, @@ -122,9 +123,9 @@ static void build_dvsecs(CXLComponentState *cxl) .rcvd_mod_ts_data_phase1 = 0xef, /* WTF? */ }; cxl_component_create_dvsec(cxl, CXL2_UPSTREAM_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, @@ -193,8 +194,8 @@ enum { static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv) { - g_autofree CDATSslbis *sslbis_latency = NULL; - g_autofree CDATSslbis *sslbis_bandwidth = NULL; + CDATSslbis *sslbis_latency; + CDATSslbis *sslbis_bandwidth; CXLUpstreamPort *us = CXL_USP(priv); PCIBus *bus = &PCI_BRIDGE(us)->sec_bus; int devfn, sslbis_size, i; @@ -229,16 +230,13 @@ static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv) sslbis_size = sizeof(CDATSslbis) + sizeof(*sslbis_latency->sslbe) * count; sslbis_latency = g_malloc(sslbis_size); - if (!sslbis_latency) { - return -ENOMEM; - } *sslbis_latency = (CDATSslbis) { .sslbis_header = { .header = { .type = CDAT_TYPE_SSLBIS, .length = sslbis_size, }, - .data_type = HMATLB_DATA_TYPE_ACCESS_LATENCY, + .data_type = HMAT_LB_DATA_TYPE_ACCESS_LATENCY, .entry_base_unit = 10000, }, }; @@ -252,17 +250,14 @@ static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv) } sslbis_bandwidth = g_malloc(sslbis_size); - if (!sslbis_bandwidth) { - return 0; - } *sslbis_bandwidth = (CDATSslbis) { .sslbis_header = { .header = { .type = CDAT_TYPE_SSLBIS, .length = sslbis_size, }, - .data_type = HMATLB_DATA_TYPE_ACCESS_BANDWIDTH, - .entry_base_unit = 1000, + .data_type = HMAT_LB_DATA_TYPE_ACCESS_BANDWIDTH, + .entry_base_unit = 1024, }, }; @@ -274,14 +269,11 @@ static int build_cdat_table(CDATSubHeader ***cdat_table, void *priv) }; } - *cdat_table = g_malloc0(sizeof(*cdat_table) * CXL_USP_CDAT_NUM_ENTRIES); - if (!*cdat_table) { - return -ENOMEM; - } + *cdat_table = g_new0(CDATSubHeader *, CXL_USP_CDAT_NUM_ENTRIES); /* Header always at start of structure */ - (*cdat_table)[CXL_USP_CDAT_SSLBIS_LAT] = g_steal_pointer(&sslbis_latency); - (*cdat_table)[CXL_USP_CDAT_SSLBIS_BW] = g_steal_pointer(&sslbis_bandwidth); + (*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; } @@ -299,6 +291,7 @@ static void free_default_cdat_table(CDATSubHeader **cdat_table, int num, 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; @@ -329,7 +322,9 @@ static void cxl_usp_realize(PCIDevice *d, Error **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); @@ -345,7 +340,9 @@ static void cxl_usp_realize(PCIDevice *d, Error **errp) 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 (!cxl_doe_cdat_init(cxl_cstate, errp)) { + goto err_cap; + } return; @@ -366,7 +363,12 @@ static void cxl_usp_exitfn(PCIDevice *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_PCIE_LINK_SPEED("x-speed", CXLUpstreamPort, + speed, PCIE_LINK_SPEED_32), + DEFINE_PROP_PCIE_LINK_WIDTH("x-width", CXLUpstreamPort, + width, PCIE_LINK_WIDTH_16), DEFINE_PROP_END_OF_LIST() }; @@ -375,7 +377,6 @@ static void cxl_upstream_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); - k->is_bridge = true; k->config_write = cxl_usp_write_config; k->config_read = cxl_usp_read_config; k->realize = cxl_usp_realize; @@ -385,7 +386,7 @@ static void cxl_upstream_class_init(ObjectClass *oc, void *data) k->revision = 0; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "CXL Switch Upstream Port"; - dc->reset = cxl_usp_reset; + device_class_set_legacy_reset(dc, cxl_usp_reset); device_class_set_props(dc, cxl_upstream_props); } 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..00d2fbd7cf 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,14 +92,13 @@ 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; k->realize = i82801b11_bridge_realize; k->config_write = pci_bridge_write_config; dc->vmsd = &i82801b11_bridge_dev_vmstate; - dc->reset = pci_bridge_reset; + device_class_set_legacy_reset(dc, pci_bridge_reset); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } 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 243ceeda50..2e0eb0d233 100644 --- a/hw/pci-bridge/meson.build +++ b/hw/pci-bridge/meson.build @@ -1,18 +1,15 @@ pci_ss = ss.source_set() -pci_ss.add(files('pci_bridge_dev.c')) +pci_ss.add(when: 'CONFIG_PCI_BRIDGE', if_true: 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_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', '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) - -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('pci_expander_bridge_stubs.c')) +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..8e7f926621 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,9 +253,8 @@ 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_legacy_reset(dc, qdev_pci_bridge_dev_reset); device_class_set_props(dc, pci_bridge_dev_properties); dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index c9e817aa58..07d411cff5 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -15,6 +15,7 @@ #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" @@ -37,7 +38,6 @@ DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS, DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS, TYPE_PXB_PCIE_BUS) -#define TYPE_PXB_CXL_BUS "pxb-cxl-bus" DECLARE_INSTANCE_CHECKER(PXBBus, PXB_CXL_BUS, TYPE_PXB_CXL_BUS) @@ -49,25 +49,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; @@ -80,26 +63,46 @@ 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; } +static void prop_pxb_uid_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t uid = pci_bus_num(PCI_BUS(obj)); + + visit_type_uint32(v, name, &uid, errp); +} + static void pxb_bus_class_init(ObjectClass *class, void *data) { PCIBusClass *pbc = PCI_BUS_CLASS(class); pbc->bus_num = pxb_bus_num; pbc->numa_node = pxb_bus_numa_node; + + object_class_property_add(class, "acpi_uid", "uint32", + prop_pxb_uid_get, NULL, NULL, NULL); + object_class_property_set_description(class, "acpi_uid", + "ACPI Unique ID used to distinguish this PCI Host Bridge / ACPI00016"); } static const TypeInfo pxb_bus_info = { @@ -147,7 +150,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); @@ -156,7 +159,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) { @@ -205,8 +208,8 @@ static void pxb_cxl_realize(DeviceState *dev, Error **errp) */ void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp) { - PXBDev *pxb = PXB_CXL_DEV(pci_bridge_get_device(bus)); - CXLHost *cxl = pxb->cxl.cxl_host_bridge; + 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; @@ -272,7 +275,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); @@ -290,15 +293,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) @@ -310,10 +330,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b) 0; } -static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type, +static bool 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; @@ -322,13 +342,13 @@ static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type, if (ms->numa_state == NULL) { error_setg(errp, "NUMA is not supported by this machine-type"); - return; + return false; } if (pxb->numa_node != NUMA_NODE_UNASSIGNED && pxb->numa_node >= ms->numa_state->num_nodes) { error_setg(errp, "Illegal numa node %d", pxb->numa_node); - return; + return false; } if (dev->qdev.id && *dev->qdev.id) { @@ -341,7 +361,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"); @@ -374,12 +394,13 @@ static void pxb_dev_realize_common(PCIDevice *dev, enum BusType type, pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); pxb_dev_list = g_list_insert_sorted(pxb_dev_list, pxb, pxb_compare); - return; + return true; err_register_bus: object_unref(OBJECT(bds)); object_unparent(OBJECT(bus)); object_unref(OBJECT(ds)); + return false; } static void pxb_dev_realize(PCIDevice *dev, Error **errp) @@ -394,7 +415,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); } @@ -425,7 +446,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, @@ -457,15 +478,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 }, @@ -481,10 +501,17 @@ static void pxb_cxl_dev_realize(PCIDevice *dev, Error **errp) return; } - pxb_dev_realize_common(dev, CXL, errp); - pxb_dev_reset(DEVICE(dev)); + if (!pxb_dev_realize_common(dev, CXL, errp)) { + return; + } + 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); @@ -498,18 +525,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; + device_class_set_legacy_reset(dc, 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/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 1cd917a459..6e8d7d9478 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; @@ -153,7 +152,7 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) k->config_write = pcie_pci_bridge_write_config; dc->vmsd = &pcie_pci_bridge_dev_vmstate; device_class_set_props(dc, pcie_pci_bridge_dev_properties); - dc->reset = &pcie_pci_bridge_reset; + device_class_set_legacy_reset(dc, pcie_pci_bridge_reset); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); hc->plug = pci_bridge_dev_plug_cb; hc->unplug = pci_bridge_dev_unplug_cb; diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 460e48269d..09a34786bc 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, ResetType type) { - 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..5fe090df6c 100644 --- a/hw/pci-bridge/simba.c +++ b/hw/pci-bridge/simba.c @@ -77,9 +77,8 @@ 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; + device_class_set_legacy_reset(dc, 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..473e2dd950 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; @@ -168,7 +167,7 @@ static void xio3130_downstream_class_init(ObjectClass *klass, void *data) k->revision = XIO3130_REVISION; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "TI X3130 Downstream Port of PCI Express Switch"; - dc->reset = xio3130_downstream_reset; + device_class_set_legacy_reset(dc, xio3130_downstream_reset); dc->vmsd = &vmstate_xio3130_downstream; device_class_set_props(dc, xio3130_downstream_props); } diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index 5ff46ef050..fb1547b74a 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; @@ -137,7 +136,7 @@ static void xio3130_upstream_class_init(ObjectClass *klass, void *data) k->revision = XIO3130_REVISION; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "TI X3130 Upstream Port of PCI Express Switch"; - dc->reset = xio3130_upstream_reset; + device_class_set_legacy_reset(dc, xio3130_upstream_reset); dc->vmsd = &vmstate_xio3130_upstream; } 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..379095b356 --- /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); + + device_class_set_legacy_reset(dc, 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); + + device_class_set_legacy_reset(dc, 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..1516d0074d 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, ResetType type) { - 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..c3fc37b904 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; @@ -393,6 +395,7 @@ static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) { DesignwarePCIERoot *root = DESIGNWARE_PCIE_ROOT(dev); DesignwarePCIEHost *host = designware_pcie_root_to_host(root); + MemoryRegion *host_mem = get_system_memory(); MemoryRegion *address_space = &host->pci.memory; PCIBridge *br = PCI_BRIDGE(dev); DesignwarePCIEViewport *viewport; @@ -433,7 +436,7 @@ static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) viewport->cr[0] = DESIGNWARE_PCIE_ATU_TYPE_MEM; source = &host->pci.address_space_root; - destination = get_system_memory(); + destination = host_mem; direction = "Inbound"; /* @@ -458,7 +461,7 @@ static void designware_pcie_root_realize(PCIDevice *dev, Error **errp) destination = &host->pci.memory; direction = "Outbound"; - source = get_system_memory(); + source = host_mem; /* * Configure MemoryRegion implementing CPU -> PCI memory @@ -488,7 +491,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 +506,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 +532,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 +544,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 +560,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 +573,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,13 +603,12 @@ 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; k->config_write = designware_pcie_root_config_write; - dc->reset = pci_bridge_reset; + device_class_set_legacy_reset(dc, pci_bridge_reset); /* * PCI-facing part of the host bridge, not usable without the * host-facing part, which can't be device_add'ed, yet. @@ -664,6 +666,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 +701,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 +712,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 +721,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, @@ -746,28 +753,23 @@ static void designware_pcie_host_init(Object *obj) qdev_prop_set_bit(DEVICE(root), "multifunction", false); } -static const TypeInfo designware_pcie_root_info = { - .name = TYPE_DESIGNWARE_PCIE_ROOT, - .parent = TYPE_PCI_BRIDGE, - .instance_size = sizeof(DesignwarePCIERoot), - .class_init = designware_pcie_root_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_PCIE_DEVICE }, - { } +static const TypeInfo designware_pcie_types[] = { + { + .name = TYPE_DESIGNWARE_PCIE_HOST, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(DesignwarePCIEHost), + .instance_init = designware_pcie_host_init, + .class_init = designware_pcie_host_class_init, + }, { + .name = TYPE_DESIGNWARE_PCIE_ROOT, + .parent = TYPE_PCI_BRIDGE, + .instance_size = sizeof(DesignwarePCIERoot), + .class_init = designware_pcie_root_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }, }; -static const TypeInfo designware_pcie_host_info = { - .name = TYPE_DESIGNWARE_PCIE_HOST, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(DesignwarePCIEHost), - .instance_init = designware_pcie_host_init, - .class_init = designware_pcie_host_class_init, -}; - -static void designware_pcie_register(void) -{ - type_register_static(&designware_pcie_root_info); - type_register_static(&designware_pcie_host_info); -} -type_init(designware_pcie_register) +DEFINE_TYPES(designware_pcie_types) diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index f257c24e64..283fc0dc57 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); @@ -498,7 +502,7 @@ static void dino_pcihost_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = dino_pcihost_reset; + device_class_set_legacy_reset(dc, dino_pcihost_reset); dc->realize = dino_pcihost_realize; dc->unrealize = dino_pcihost_unrealize; device_class_set_props(dc, dino_pcihost_properties); diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 7c7316bc96..e8b4c64c5f 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -7,7 +7,8 @@ #include "hw/pci/pcie_host.h" #include "hw/acpi/cxl.h" -static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) +static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, + Aml *scope, uint8_t bus_num) { Aml *method, *crs; int i, slot_no; @@ -20,7 +21,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) Aml *pkg = aml_package(4); aml_append(pkg, aml_int((slot_no << 16) | 0xFFFF)); aml_append(pkg, aml_int(i)); - aml_append(pkg, aml_name("GSI%d", gsi)); + aml_append(pkg, aml_name("L%.02X%X", bus_num, gsi)); aml_append(pkg, aml_int(0)); aml_append(rt_pkg, pkg); } @@ -30,7 +31,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) /* Create GSI link device */ for (i = 0; i < PCI_NUM_PINS; i++) { uint32_t irqs = irq + i; - Aml *dev_gsi = aml_device("GSI%d", i); + Aml *dev_gsi = aml_device("L%.02X%X", bus_num, i); aml_append(dev_gsi, aml_name_decl("_HID", aml_string("PNP0C0F"))); aml_append(dev_gsi, aml_name_decl("_UID", aml_int(i))); crs = aml_resource_template(); @@ -45,7 +46,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq) aml_append(dev_gsi, aml_name_decl("_CRS", crs)); method = aml_method("_SRS", 1, AML_NOTSERIALIZED); aml_append(dev_gsi, method); - aml_append(dev, dev_gsi); + aml_append(scope, dev_gsi); } } @@ -140,6 +141,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) QLIST_FOREACH(bus, &bus->child, sibling) { uint8_t bus_num = pci_bus_num(bus); uint8_t numa_node = pci_bus_numa_node(bus); + uint32_t uid; bool is_cxl = pci_bus_is_cxl(bus); if (!pci_bus_is_root(bus)) { @@ -155,6 +157,8 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) nr_pcie_buses = bus_num; } + uid = object_property_get_uint(OBJECT(bus), "acpi_uid", + &error_fatal); dev = aml_device("PC%.02X", bus_num); if (is_cxl) { struct Aml *pkg = aml_package(2); @@ -167,17 +171,17 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) aml_append(dev, aml_name_decl("_CID", aml_string("PNP0A03"))); } aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); - aml_append(dev, aml_name_decl("_UID", aml_int(bus_num))); + aml_append(dev, aml_name_decl("_UID", aml_int(uid))); aml_append(dev, aml_name_decl("_STR", aml_unicode("pxb Device"))); aml_append(dev, aml_name_decl("_CCA", aml_int(1))); if (numa_node != NUMA_NODE_UNASSIGNED) { aml_append(dev, aml_name_decl("_PXM", aml_int(numa_node))); } - acpi_dsdt_add_pci_route_table(dev, cfg->irq); + acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, bus_num); /* - * 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. */ @@ -205,7 +209,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) aml_append(dev, aml_name_decl("_STR", aml_unicode("PCIe 0 Device"))); aml_append(dev, aml_name_decl("_CCA", aml_int(1))); - acpi_dsdt_add_pci_route_table(dev, cfg->irq); + acpi_dsdt_add_pci_route_table(dev, cfg->irq, scope, 0); method = aml_method("_CBA", 0, AML_NOTSERIALIZED); aml_append(method, aml_return(aml_int(cfg->ecam.base))); @@ -281,3 +285,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 95945ac0f4..8e589ff2c9 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -25,7 +25,7 @@ #include "qemu/osdep.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" @@ -91,7 +91,7 @@ static void grackle_init(Object *obj) static void grackle_pci_realize(PCIDevice *d, Error **errp) { - d->config[0x09] = 0x01; + d->config[PCI_CLASS_PROG] = 0x01; } static void grackle_pci_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c new file mode 100644 index 0000000000..14fc803d27 --- /dev/null +++ b/hw/pci-host/gt64120.c @@ -0,0 +1,1307 @@ +/* + * QEMU GT64120 PCI host + * + * (Datasheet GT-64120 Rev 1.4 from Sep 14, 1999) + * + * Copyright (c) 2006,2007 Aurelien Jarno + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/units.h" +#include "qemu/log.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" +#include "trace.h" +#include "qom/object.h" + +#define GT_REGS (0x1000 >> 2) + +/* CPU Configuration */ +#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) +#define GT_SCS32LD (0x018 >> 2) +#define GT_SCS32HD (0x020 >> 2) +#define GT_CS20LD (0x028 >> 2) +#define GT_CS20HD (0x030 >> 2) +#define GT_CS3BOOTLD (0x038 >> 2) +#define GT_CS3BOOTHD (0x040 >> 2) +#define GT_PCI0IOLD (0x048 >> 2) +#define GT_PCI0IOHD (0x050 >> 2) +#define GT_PCI0M0LD (0x058 >> 2) +#define GT_PCI0M0HD (0x060 >> 2) +#define GT_PCI0M1LD (0x080 >> 2) +#define GT_PCI0M1HD (0x088 >> 2) +#define GT_PCI1IOLD (0x090 >> 2) +#define GT_PCI1IOHD (0x098 >> 2) +#define GT_PCI1M0LD (0x0a0 >> 2) +#define GT_PCI1M0HD (0x0a8 >> 2) +#define GT_PCI1M1LD (0x0b0 >> 2) +#define GT_PCI1M1HD (0x0b8 >> 2) +#define GT_ISD (0x068 >> 2) + +#define GT_SCS10AR (0x0d0 >> 2) +#define GT_SCS32AR (0x0d8 >> 2) +#define GT_CS20R (0x0e0 >> 2) +#define GT_CS3BOOTR (0x0e8 >> 2) + +#define GT_PCI0IOREMAP (0x0f0 >> 2) +#define GT_PCI0M0REMAP (0x0f8 >> 2) +#define GT_PCI0M1REMAP (0x100 >> 2) +#define GT_PCI1IOREMAP (0x108 >> 2) +#define GT_PCI1M0REMAP (0x110 >> 2) +#define GT_PCI1M1REMAP (0x118 >> 2) + +/* CPU Error Report */ +#define GT_CPUERR_ADDRLO (0x070 >> 2) +#define GT_CPUERR_ADDRHI (0x078 >> 2) +#define GT_CPUERR_DATALO (0x128 >> 2) /* GT-64120A only */ +#define GT_CPUERR_DATAHI (0x130 >> 2) /* GT-64120A only */ +#define GT_CPUERR_PARITY (0x138 >> 2) /* GT-64120A only */ + +/* CPU Sync Barrier */ +#define GT_PCI0SYNC (0x0c0 >> 2) +#define GT_PCI1SYNC (0x0c8 >> 2) + +/* SDRAM and Device Address Decode */ +#define GT_SCS0LD (0x400 >> 2) +#define GT_SCS0HD (0x404 >> 2) +#define GT_SCS1LD (0x408 >> 2) +#define GT_SCS1HD (0x40c >> 2) +#define GT_SCS2LD (0x410 >> 2) +#define GT_SCS2HD (0x414 >> 2) +#define GT_SCS3LD (0x418 >> 2) +#define GT_SCS3HD (0x41c >> 2) +#define GT_CS0LD (0x420 >> 2) +#define GT_CS0HD (0x424 >> 2) +#define GT_CS1LD (0x428 >> 2) +#define GT_CS1HD (0x42c >> 2) +#define GT_CS2LD (0x430 >> 2) +#define GT_CS2HD (0x434 >> 2) +#define GT_CS3LD (0x438 >> 2) +#define GT_CS3HD (0x43c >> 2) +#define GT_BOOTLD (0x440 >> 2) +#define GT_BOOTHD (0x444 >> 2) +#define GT_ADERR (0x470 >> 2) + +/* SDRAM Configuration */ +#define GT_SDRAM_CFG (0x448 >> 2) +#define GT_SDRAM_OPMODE (0x474 >> 2) +#define GT_SDRAM_BM (0x478 >> 2) +#define GT_SDRAM_ADDRDECODE (0x47c >> 2) + +/* SDRAM Parameters */ +#define GT_SDRAM_B0 (0x44c >> 2) +#define GT_SDRAM_B1 (0x450 >> 2) +#define GT_SDRAM_B2 (0x454 >> 2) +#define GT_SDRAM_B3 (0x458 >> 2) + +/* Device Parameters */ +#define GT_DEV_B0 (0x45c >> 2) +#define GT_DEV_B1 (0x460 >> 2) +#define GT_DEV_B2 (0x464 >> 2) +#define GT_DEV_B3 (0x468 >> 2) +#define GT_DEV_BOOT (0x46c >> 2) + +/* ECC */ +#define GT_ECC_ERRDATALO (0x480 >> 2) /* GT-64120A only */ +#define GT_ECC_ERRDATAHI (0x484 >> 2) /* GT-64120A only */ +#define GT_ECC_MEM (0x488 >> 2) /* GT-64120A only */ +#define GT_ECC_CALC (0x48c >> 2) /* GT-64120A only */ +#define GT_ECC_ERRADDR (0x490 >> 2) /* GT-64120A only */ + +/* DMA Record */ +#define GT_DMA0_CNT (0x800 >> 2) +#define GT_DMA1_CNT (0x804 >> 2) +#define GT_DMA2_CNT (0x808 >> 2) +#define GT_DMA3_CNT (0x80c >> 2) +#define GT_DMA0_SA (0x810 >> 2) +#define GT_DMA1_SA (0x814 >> 2) +#define GT_DMA2_SA (0x818 >> 2) +#define GT_DMA3_SA (0x81c >> 2) +#define GT_DMA0_DA (0x820 >> 2) +#define GT_DMA1_DA (0x824 >> 2) +#define GT_DMA2_DA (0x828 >> 2) +#define GT_DMA3_DA (0x82c >> 2) +#define GT_DMA0_NEXT (0x830 >> 2) +#define GT_DMA1_NEXT (0x834 >> 2) +#define GT_DMA2_NEXT (0x838 >> 2) +#define GT_DMA3_NEXT (0x83c >> 2) +#define GT_DMA0_CUR (0x870 >> 2) +#define GT_DMA1_CUR (0x874 >> 2) +#define GT_DMA2_CUR (0x878 >> 2) +#define GT_DMA3_CUR (0x87c >> 2) + +/* DMA Channel Control */ +#define GT_DMA0_CTRL (0x840 >> 2) +#define GT_DMA1_CTRL (0x844 >> 2) +#define GT_DMA2_CTRL (0x848 >> 2) +#define GT_DMA3_CTRL (0x84c >> 2) + +/* DMA Arbiter */ +#define GT_DMA_ARB (0x860 >> 2) + +/* Timer/Counter */ +#define GT_TC0 (0x850 >> 2) +#define GT_TC1 (0x854 >> 2) +#define GT_TC2 (0x858 >> 2) +#define GT_TC3 (0x85c >> 2) +#define GT_TC_CONTROL (0x864 >> 2) + +/* PCI Internal */ +#define GT_PCI0_CMD (0xc00 >> 2) +#define GT_PCI0_TOR (0xc04 >> 2) +#define GT_PCI0_BS_SCS10 (0xc08 >> 2) +#define GT_PCI0_BS_SCS32 (0xc0c >> 2) +#define GT_PCI0_BS_CS20 (0xc10 >> 2) +#define GT_PCI0_BS_CS3BT (0xc14 >> 2) +#define GT_PCI1_IACK (0xc30 >> 2) +#define GT_PCI0_IACK (0xc34 >> 2) +#define GT_PCI0_BARE (0xc3c >> 2) +#define GT_PCI0_PREFMBR (0xc40 >> 2) +#define GT_PCI0_SCS10_BAR (0xc48 >> 2) +#define GT_PCI0_SCS32_BAR (0xc4c >> 2) +#define GT_PCI0_CS20_BAR (0xc50 >> 2) +#define GT_PCI0_CS3BT_BAR (0xc54 >> 2) +#define GT_PCI0_SSCS10_BAR (0xc58 >> 2) +#define GT_PCI0_SSCS32_BAR (0xc5c >> 2) +#define GT_PCI0_SCS3BT_BAR (0xc64 >> 2) +#define GT_PCI1_CMD (0xc80 >> 2) +#define GT_PCI1_TOR (0xc84 >> 2) +#define GT_PCI1_BS_SCS10 (0xc88 >> 2) +#define GT_PCI1_BS_SCS32 (0xc8c >> 2) +#define GT_PCI1_BS_CS20 (0xc90 >> 2) +#define GT_PCI1_BS_CS3BT (0xc94 >> 2) +#define GT_PCI1_BARE (0xcbc >> 2) +#define GT_PCI1_PREFMBR (0xcc0 >> 2) +#define GT_PCI1_SCS10_BAR (0xcc8 >> 2) +#define GT_PCI1_SCS32_BAR (0xccc >> 2) +#define GT_PCI1_CS20_BAR (0xcd0 >> 2) +#define GT_PCI1_CS3BT_BAR (0xcd4 >> 2) +#define GT_PCI1_SSCS10_BAR (0xcd8 >> 2) +#define GT_PCI1_SSCS32_BAR (0xcdc >> 2) +#define GT_PCI1_SCS3BT_BAR (0xce4 >> 2) +#define GT_PCI1_CFGADDR (0xcf0 >> 2) +#define GT_PCI1_CFGDATA (0xcf4 >> 2) +#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) +#define GT_PCI0_ICMASK (0xc24 >> 2) +#define GT_PCI0_SERR0MASK (0xc28 >> 2) +#define GT_CPU_INTSEL (0xc70 >> 2) +#define GT_PCI0_INTSEL (0xc74 >> 2) +#define GT_HINTRCAUSE (0xc98 >> 2) +#define GT_HINTRMASK (0xc9c >> 2) +#define GT_PCI0_HICMASK (0xca4 >> 2) +#define GT_PCI1_SERR1MASK (0xca8 >> 2) + +#define PCI_MAPPING_ENTRY(regname) \ + hwaddr regname ##_start; \ + hwaddr regname ##_length; \ + MemoryRegion regname ##_mem + +#define TYPE_GT64120_PCI_HOST_BRIDGE "gt64120" + +OBJECT_DECLARE_SIMPLE_TYPE(GT64120State, GT64120_PCI_HOST_BRIDGE) + +struct GT64120State { + PCIHostState parent_obj; + + uint32_t regs[GT_REGS]; + PCI_MAPPING_ENTRY(PCI0IO); + PCI_MAPPING_ENTRY(PCI0M0); + PCI_MAPPING_ENTRY(PCI0M1); + 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 */ +/* + * XXX: Hardcoded values for Malta: 0x1e000000 - 0x1f100000 + * 0x1fc00000 - 0x1fd00000 + */ +static void check_reserved_space(hwaddr *start, hwaddr *length) +{ + hwaddr begin = *start; + hwaddr end = *start + *length; + + if (end >= 0x1e000000LL && end < 0x1f100000LL) { + end = 0x1e000000LL; + } + if (begin >= 0x1e000000LL && begin < 0x1f100000LL) { + begin = 0x1f100000LL; + } + if (end >= 0x1fc00000LL && end < 0x1fd00000LL) { + end = 0x1fc00000LL; + } + if (begin >= 0x1fc00000LL && begin < 0x1fd00000LL) { + begin = 0x1fd00000LL; + } + /* XXX: This is broken when a reserved range splits the requested range */ + if (end >= 0x1f100000LL && begin < 0x1e000000LL) { + end = 0x1e000000LL; + } + if (end >= 0x1fd00000LL && begin < 0x1fc00000LL) { + end = 0x1fc00000LL; + } + + *start = begin; + *length = end - begin; +} + +static void gt64120_isd_mapping(GT64120State *s) +{ + /* Bits 14:0 of ISD map to bits 35:21 of the start address. */ + 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); + } + check_reserved_space(&start, &length); + length = 0x1000; + /* Map new address */ + trace_gt64120_isd_remap(s->ISD_length, s->ISD_start, length, start); + 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 */ + if (s->PCI0IO_length) { + memory_region_del_subregion(get_system_memory(), &s->PCI0IO_mem); + object_unparent(OBJECT(&s->PCI0IO_mem)); + } + /* Map new IO address */ + s->PCI0IO_start = s->regs[GT_PCI0IOLD] << 21; + s->PCI0IO_length = ((s->regs[GT_PCI0IOHD] + 1) - + (s->regs[GT_PCI0IOLD] & 0x7f)) << 21; + if (s->PCI0IO_length) { + memory_region_init_alias(&s->PCI0IO_mem, OBJECT(s), "pci0-io", + get_system_io(), 0, s->PCI0IO_length); + memory_region_add_subregion(get_system_memory(), s->PCI0IO_start, + &s->PCI0IO_mem); + } + } + + /* Update PCI0M0 mapping */ + if ((s->regs[GT_PCI0M0LD] & 0x7f) <= s->regs[GT_PCI0M0HD]) { + /* Unmap old MEM address */ + if (s->PCI0M0_length) { + memory_region_del_subregion(get_system_memory(), &s->PCI0M0_mem); + object_unparent(OBJECT(&s->PCI0M0_mem)); + } + /* Map new mem address */ + s->PCI0M0_start = s->regs[GT_PCI0M0LD] << 21; + s->PCI0M0_length = ((s->regs[GT_PCI0M0HD] + 1) - + (s->regs[GT_PCI0M0LD] & 0x7f)) << 21; + if (s->PCI0M0_length) { + memory_region_init_alias(&s->PCI0M0_mem, OBJECT(s), "pci0-mem0", + &s->pci0_mem, s->PCI0M0_start, + s->PCI0M0_length); + memory_region_add_subregion(get_system_memory(), s->PCI0M0_start, + &s->PCI0M0_mem); + } + } + + /* Update PCI0M1 mapping */ + if ((s->regs[GT_PCI0M1LD] & 0x7f) <= s->regs[GT_PCI0M1HD]) { + /* Unmap old MEM address */ + if (s->PCI0M1_length) { + memory_region_del_subregion(get_system_memory(), &s->PCI0M1_mem); + object_unparent(OBJECT(&s->PCI0M1_mem)); + } + /* Map new mem address */ + s->PCI0M1_start = s->regs[GT_PCI0M1LD] << 21; + s->PCI0M1_length = ((s->regs[GT_PCI0M1HD] + 1) - + (s->regs[GT_PCI0M1LD] & 0x7f)) << 21; + if (s->PCI0M1_length) { + memory_region_init_alias(&s->PCI0M1_mem, OBJECT(s), "pci0-mem1", + &s->pci0_mem, s->PCI0M1_start, + s->PCI0M1_length); + memory_region_add_subregion(get_system_memory(), s->PCI0M1_start, + &s->PCI0M1_mem); + } + } + + memory_region_transaction_commit(); +} + +static int gt64120_post_load(void *opaque, int version_id) +{ + GT64120State *s = opaque; + + gt64120_isd_mapping(s); + gt64120_pci_mapping(s); + + return 0; +} + +static const VMStateDescription vmstate_gt64120 = { + .name = "gt64120", + .version_id = 1, + .minimum_version_id = 1, + .post_load = gt64120_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, GT64120State, GT_REGS), + VMSTATE_END_OF_LIST() + } +}; + +static void gt64120_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + GT64120State *s = opaque; + uint32_t saddr = addr >> 2; + + trace_gt64120_write(addr, val); + if (!(s->regs[GT_CPU] & 0x00001000)) { + val = bswap32(val); + } + + switch (saddr) { + + /* CPU Configuration */ + case GT_CPU: + s->regs[GT_CPU] = val; + break; + case GT_MULTI: + /* Read-only register as only one GT64xxx is present on the CPU bus */ + break; + + /* CPU Address Decode */ + case GT_PCI0IOLD: + s->regs[GT_PCI0IOLD] = val & 0x00007fff; + s->regs[GT_PCI0IOREMAP] = val & 0x000007ff; + gt64120_pci_mapping(s); + break; + case GT_PCI0M0LD: + s->regs[GT_PCI0M0LD] = val & 0x00007fff; + s->regs[GT_PCI0M0REMAP] = val & 0x000007ff; + gt64120_pci_mapping(s); + break; + case GT_PCI0M1LD: + s->regs[GT_PCI0M1LD] = val & 0x00007fff; + s->regs[GT_PCI0M1REMAP] = val & 0x000007ff; + gt64120_pci_mapping(s); + break; + case GT_PCI1IOLD: + s->regs[GT_PCI1IOLD] = val & 0x00007fff; + s->regs[GT_PCI1IOREMAP] = val & 0x000007ff; + break; + case GT_PCI1M0LD: + s->regs[GT_PCI1M0LD] = val & 0x00007fff; + s->regs[GT_PCI1M0REMAP] = val & 0x000007ff; + break; + case GT_PCI1M1LD: + s->regs[GT_PCI1M1LD] = val & 0x00007fff; + s->regs[GT_PCI1M1REMAP] = val & 0x000007ff; + break; + case GT_PCI0M0HD: + case GT_PCI0M1HD: + case GT_PCI0IOHD: + s->regs[saddr] = val & 0x0000007f; + gt64120_pci_mapping(s); + break; + case GT_PCI1IOHD: + case GT_PCI1M0HD: + case GT_PCI1M1HD: + s->regs[saddr] = val & 0x0000007f; + break; + case GT_ISD: + s->regs[saddr] = val & 0x00007fff; + gt64120_isd_mapping(s); + break; + + case GT_PCI0IOREMAP: + case GT_PCI0M0REMAP: + case GT_PCI0M1REMAP: + case GT_PCI1IOREMAP: + case GT_PCI1M0REMAP: + case GT_PCI1M1REMAP: + s->regs[saddr] = val & 0x000007ff; + break; + + /* CPU Error Report */ + case GT_CPUERR_ADDRLO: + case GT_CPUERR_ADDRHI: + case GT_CPUERR_DATALO: + case GT_CPUERR_DATAHI: + case GT_CPUERR_PARITY: + /* Read-only registers, do nothing */ + qemu_log_mask(LOG_GUEST_ERROR, + "gt64120: Read-only register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + + /* CPU Sync Barrier */ + case GT_PCI0SYNC: + case GT_PCI1SYNC: + /* Read-only registers, do nothing */ + qemu_log_mask(LOG_GUEST_ERROR, + "gt64120: Read-only register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + + /* SDRAM and Device Address Decode */ + case GT_SCS0LD: + case GT_SCS0HD: + case GT_SCS1LD: + case GT_SCS1HD: + case GT_SCS2LD: + case GT_SCS2HD: + case GT_SCS3LD: + case GT_SCS3HD: + case GT_CS0LD: + case GT_CS0HD: + case GT_CS1LD: + case GT_CS1HD: + case GT_CS2LD: + case GT_CS2HD: + case GT_CS3LD: + case GT_CS3HD: + case GT_BOOTLD: + case GT_BOOTHD: + case GT_ADERR: + /* SDRAM Configuration */ + case GT_SDRAM_CFG: + case GT_SDRAM_OPMODE: + case GT_SDRAM_BM: + case GT_SDRAM_ADDRDECODE: + /* Accept and ignore SDRAM interleave configuration */ + s->regs[saddr] = val; + break; + + /* Device Parameters */ + case GT_DEV_B0: + case GT_DEV_B1: + case GT_DEV_B2: + case GT_DEV_B3: + case GT_DEV_BOOT: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, + "gt64120: Unimplemented device register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + + /* ECC */ + case GT_ECC_ERRDATALO: + case GT_ECC_ERRDATAHI: + case GT_ECC_MEM: + case GT_ECC_CALC: + case GT_ECC_ERRADDR: + /* Read-only registers, do nothing */ + qemu_log_mask(LOG_GUEST_ERROR, + "gt64120: Read-only register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + + /* DMA Record */ + case GT_DMA0_CNT: + case GT_DMA1_CNT: + case GT_DMA2_CNT: + case GT_DMA3_CNT: + case GT_DMA0_SA: + case GT_DMA1_SA: + case GT_DMA2_SA: + case GT_DMA3_SA: + case GT_DMA0_DA: + case GT_DMA1_DA: + case GT_DMA2_DA: + case GT_DMA3_DA: + case GT_DMA0_NEXT: + case GT_DMA1_NEXT: + case GT_DMA2_NEXT: + case GT_DMA3_NEXT: + case GT_DMA0_CUR: + case GT_DMA1_CUR: + case GT_DMA2_CUR: + case GT_DMA3_CUR: + + /* DMA Channel Control */ + case GT_DMA0_CTRL: + case GT_DMA1_CTRL: + case GT_DMA2_CTRL: + case GT_DMA3_CTRL: + + /* DMA Arbiter */ + case GT_DMA_ARB: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, + "gt64120: Unimplemented DMA register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + + /* Timer/Counter */ + case GT_TC0: + case GT_TC1: + case GT_TC2: + case GT_TC3: + case GT_TC_CONTROL: + /* Not implemented */ + qemu_log_mask(LOG_UNIMP, + "gt64120: Unimplemented timer register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + + /* PCI Internal */ + 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: + case GT_PCI0_BS_SCS32: + case GT_PCI0_BS_CS20: + case GT_PCI0_BS_CS3BT: + case GT_PCI1_IACK: + case GT_PCI0_IACK: + case GT_PCI0_BARE: + case GT_PCI0_PREFMBR: + case GT_PCI0_SCS10_BAR: + case GT_PCI0_SCS32_BAR: + case GT_PCI0_CS20_BAR: + case GT_PCI0_CS3BT_BAR: + case GT_PCI0_SSCS10_BAR: + case GT_PCI0_SSCS32_BAR: + case GT_PCI0_SCS3BT_BAR: + case GT_PCI1_TOR: + case GT_PCI1_BS_SCS10: + case GT_PCI1_BS_SCS32: + case GT_PCI1_BS_CS20: + case GT_PCI1_BS_CS3BT: + case GT_PCI1_BARE: + case GT_PCI1_PREFMBR: + case GT_PCI1_SCS10_BAR: + case GT_PCI1_SCS32_BAR: + case GT_PCI1_CS20_BAR: + case GT_PCI1_CS3BT_BAR: + case GT_PCI1_SSCS10_BAR: + case GT_PCI1_SSCS32_BAR: + case GT_PCI1_SCS3BT_BAR: + case GT_PCI1_CFGADDR: + case GT_PCI1_CFGDATA: + /* not implemented */ + qemu_log_mask(LOG_UNIMP, + "gt64120: Unimplemented PCI register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + case GT_PCI0_CFGADDR: + case GT_PCI0_CFGDATA: + /* Mapped via in gt64120_pci_mapping() */ + g_assert_not_reached(); + + /* Interrupts */ + case GT_INTRCAUSE: + /* not really implemented */ + s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe)); + s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe); + trace_gt64120_write_intreg("INTRCAUSE", size, val); + break; + case GT_INTRMASK: + s->regs[saddr] = val & 0x3c3ffffe; + trace_gt64120_write_intreg("INTRMASK", size, val); + break; + case GT_PCI0_ICMASK: + s->regs[saddr] = val & 0x03fffffe; + trace_gt64120_write_intreg("ICMASK", size, val); + break; + case GT_PCI0_SERR0MASK: + s->regs[saddr] = val & 0x0000003f; + trace_gt64120_write_intreg("SERR0MASK", size, val); + break; + + /* Reserved when only PCI_0 is configured. */ + case GT_HINTRCAUSE: + case GT_CPU_INTSEL: + case GT_PCI0_INTSEL: + case GT_HINTRMASK: + case GT_PCI0_HICMASK: + case GT_PCI1_SERR1MASK: + /* not implemented */ + break; + + /* SDRAM Parameters */ + case GT_SDRAM_B0: + case GT_SDRAM_B1: + case GT_SDRAM_B2: + case GT_SDRAM_B3: + /* + * We don't simulate electrical parameters of the SDRAM. + * Accept, but ignore the values. + */ + s->regs[saddr] = val; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "gt64120: Illegal register write " + "reg:0x%03x size:%u value:0x%0*" PRIx64 "\n", + saddr << 2, size, size << 1, val); + break; + } +} + +static uint64_t gt64120_readl(void *opaque, + hwaddr addr, unsigned size) +{ + GT64120State *s = opaque; + uint32_t val; + uint32_t saddr = addr >> 2; + + switch (saddr) { + + /* CPU Configuration */ + case GT_MULTI: + /* + * Only one GT64xxx is present on the CPU bus, return + * the initial value. + */ + val = s->regs[saddr]; + break; + + /* CPU Error Report */ + case GT_CPUERR_ADDRLO: + case GT_CPUERR_ADDRHI: + case GT_CPUERR_DATALO: + case GT_CPUERR_DATAHI: + case GT_CPUERR_PARITY: + /* Emulated memory has no error, always return the initial values. */ + val = s->regs[saddr]; + break; + + /* CPU Sync Barrier */ + case GT_PCI0SYNC: + case GT_PCI1SYNC: + /* + * Reading those register should empty all FIFO on the PCI + * bus, which are not emulated. The return value should be + * a random value that should be ignored. + */ + val = 0xc000ffee; + break; + + /* ECC */ + case GT_ECC_ERRDATALO: + case GT_ECC_ERRDATAHI: + case GT_ECC_MEM: + case GT_ECC_CALC: + case GT_ECC_ERRADDR: + /* Emulated memory has no error, always return the initial values. */ + val = s->regs[saddr]; + break; + + case GT_CPU: + case GT_SCS10LD: + case GT_SCS10HD: + case GT_SCS32LD: + case GT_SCS32HD: + case GT_CS20LD: + case GT_CS20HD: + case GT_CS3BOOTLD: + case GT_CS3BOOTHD: + case GT_SCS10AR: + case GT_SCS32AR: + case GT_CS20R: + case GT_CS3BOOTR: + case GT_PCI0IOLD: + case GT_PCI0M0LD: + case GT_PCI0M1LD: + case GT_PCI1IOLD: + case GT_PCI1M0LD: + case GT_PCI1M1LD: + case GT_PCI0IOHD: + case GT_PCI0M0HD: + case GT_PCI0M1HD: + case GT_PCI1IOHD: + case GT_PCI1M0HD: + case GT_PCI1M1HD: + case GT_PCI0IOREMAP: + case GT_PCI0M0REMAP: + case GT_PCI0M1REMAP: + case GT_PCI1IOREMAP: + case GT_PCI1M0REMAP: + case GT_PCI1M1REMAP: + case GT_ISD: + val = s->regs[saddr]; + break; + case GT_PCI0_IACK: + /* Read the IRQ number */ + val = pic_read_irq(isa_pic); + break; + + /* SDRAM and Device Address Decode */ + case GT_SCS0LD: + case GT_SCS0HD: + case GT_SCS1LD: + case GT_SCS1HD: + case GT_SCS2LD: + case GT_SCS2HD: + case GT_SCS3LD: + case GT_SCS3HD: + case GT_CS0LD: + case GT_CS0HD: + case GT_CS1LD: + case GT_CS1HD: + case GT_CS2LD: + case GT_CS2HD: + case GT_CS3LD: + case GT_CS3HD: + case GT_BOOTLD: + case GT_BOOTHD: + case GT_ADERR: + val = s->regs[saddr]; + break; + + /* SDRAM Configuration */ + case GT_SDRAM_CFG: + case GT_SDRAM_OPMODE: + case GT_SDRAM_BM: + case GT_SDRAM_ADDRDECODE: + val = s->regs[saddr]; + break; + + /* SDRAM Parameters */ + case GT_SDRAM_B0: + case GT_SDRAM_B1: + case GT_SDRAM_B2: + case GT_SDRAM_B3: + /* + * We don't simulate electrical parameters of the SDRAM. + * Just return the last written value. + */ + val = s->regs[saddr]; + break; + + /* Device Parameters */ + case GT_DEV_B0: + case GT_DEV_B1: + case GT_DEV_B2: + case GT_DEV_B3: + case GT_DEV_BOOT: + val = s->regs[saddr]; + break; + + /* DMA Record */ + case GT_DMA0_CNT: + case GT_DMA1_CNT: + case GT_DMA2_CNT: + case GT_DMA3_CNT: + case GT_DMA0_SA: + case GT_DMA1_SA: + case GT_DMA2_SA: + case GT_DMA3_SA: + case GT_DMA0_DA: + case GT_DMA1_DA: + case GT_DMA2_DA: + case GT_DMA3_DA: + case GT_DMA0_NEXT: + case GT_DMA1_NEXT: + case GT_DMA2_NEXT: + case GT_DMA3_NEXT: + case GT_DMA0_CUR: + case GT_DMA1_CUR: + case GT_DMA2_CUR: + case GT_DMA3_CUR: + val = s->regs[saddr]; + break; + + /* DMA Channel Control */ + case GT_DMA0_CTRL: + case GT_DMA1_CTRL: + case GT_DMA2_CTRL: + case GT_DMA3_CTRL: + val = s->regs[saddr]; + break; + + /* DMA Arbiter */ + case GT_DMA_ARB: + val = s->regs[saddr]; + break; + + /* Timer/Counter */ + case GT_TC0: + case GT_TC1: + case GT_TC2: + case GT_TC3: + case GT_TC_CONTROL: + val = s->regs[saddr]; + break; + + /* PCI Internal */ + case GT_PCI0_CFGADDR: + case GT_PCI0_CFGDATA: + /* Mapped via in gt64120_pci_mapping() */ + g_assert_not_reached(); + + case GT_PCI0_CMD: + case GT_PCI0_TOR: + case GT_PCI0_BS_SCS10: + case GT_PCI0_BS_SCS32: + case GT_PCI0_BS_CS20: + case GT_PCI0_BS_CS3BT: + case GT_PCI1_IACK: + case GT_PCI0_BARE: + case GT_PCI0_PREFMBR: + case GT_PCI0_SCS10_BAR: + case GT_PCI0_SCS32_BAR: + case GT_PCI0_CS20_BAR: + case GT_PCI0_CS3BT_BAR: + case GT_PCI0_SSCS10_BAR: + case GT_PCI0_SSCS32_BAR: + case GT_PCI0_SCS3BT_BAR: + case GT_PCI1_CMD: + case GT_PCI1_TOR: + case GT_PCI1_BS_SCS10: + case GT_PCI1_BS_SCS32: + case GT_PCI1_BS_CS20: + case GT_PCI1_BS_CS3BT: + case GT_PCI1_BARE: + case GT_PCI1_PREFMBR: + case GT_PCI1_SCS10_BAR: + case GT_PCI1_SCS32_BAR: + case GT_PCI1_CS20_BAR: + case GT_PCI1_CS3BT_BAR: + case GT_PCI1_SSCS10_BAR: + case GT_PCI1_SSCS32_BAR: + case GT_PCI1_SCS3BT_BAR: + case GT_PCI1_CFGADDR: + case GT_PCI1_CFGDATA: + val = s->regs[saddr]; + break; + + /* Interrupts */ + case GT_INTRCAUSE: + val = s->regs[saddr]; + trace_gt64120_read_intreg("INTRCAUSE", size, val); + break; + case GT_INTRMASK: + val = s->regs[saddr]; + trace_gt64120_read_intreg("INTRMASK", size, val); + break; + case GT_PCI0_ICMASK: + val = s->regs[saddr]; + trace_gt64120_read_intreg("ICMASK", size, val); + break; + case GT_PCI0_SERR0MASK: + val = s->regs[saddr]; + trace_gt64120_read_intreg("SERR0MASK", size, val); + break; + + /* Reserved when only PCI_0 is configured. */ + case GT_HINTRCAUSE: + case GT_CPU_INTSEL: + case GT_PCI0_INTSEL: + case GT_HINTRMASK: + case GT_PCI0_HICMASK: + case GT_PCI1_SERR1MASK: + val = s->regs[saddr]; + break; + + default: + val = s->regs[saddr]; + qemu_log_mask(LOG_GUEST_ERROR, + "gt64120: Illegal register read " + "reg:0x%03x size:%u value:0x%0*x\n", + saddr << 2, size, size << 1, val); + break; + } + + if (!(s->regs[GT_CPU] & 0x00001000)) { + val = bswap32(val); + } + trace_gt64120_read(addr, val); + + return val; +} + +static const MemoryRegionOps isd_mem_ops = { + .read = gt64120_readl, + .write = gt64120_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void gt64120_reset(DeviceState *dev) +{ + GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); + + /* FIXME: Malta specific hw assumptions ahead */ + + /* CPU Configuration */ + s->regs[GT_CPU] = s->cpu_little_endian ? R_GT_CPU_Endianness_MASK : 0; + s->regs[GT_MULTI] = 0x00000003; + + /* CPU Address decode */ + s->regs[GT_SCS10LD] = 0x00000000; + s->regs[GT_SCS10HD] = 0x00000007; + s->regs[GT_SCS32LD] = 0x00000008; + s->regs[GT_SCS32HD] = 0x0000000f; + s->regs[GT_CS20LD] = 0x000000e0; + s->regs[GT_CS20HD] = 0x00000070; + s->regs[GT_CS3BOOTLD] = 0x000000f8; + s->regs[GT_CS3BOOTHD] = 0x0000007f; + + s->regs[GT_PCI0IOLD] = 0x00000080; + s->regs[GT_PCI0IOHD] = 0x0000000f; + s->regs[GT_PCI0M0LD] = 0x00000090; + s->regs[GT_PCI0M0HD] = 0x0000001f; + s->regs[GT_ISD] = 0x000000a0; + s->regs[GT_PCI0M1LD] = 0x00000790; + s->regs[GT_PCI0M1HD] = 0x0000001f; + s->regs[GT_PCI1IOLD] = 0x00000100; + s->regs[GT_PCI1IOHD] = 0x0000000f; + s->regs[GT_PCI1M0LD] = 0x00000110; + s->regs[GT_PCI1M0HD] = 0x0000001f; + s->regs[GT_PCI1M1LD] = 0x00000120; + s->regs[GT_PCI1M1HD] = 0x0000002f; + + s->regs[GT_SCS10AR] = 0x00000000; + s->regs[GT_SCS32AR] = 0x00000008; + s->regs[GT_CS20R] = 0x000000e0; + s->regs[GT_CS3BOOTR] = 0x000000f8; + + s->regs[GT_PCI0IOREMAP] = 0x00000080; + s->regs[GT_PCI0M0REMAP] = 0x00000090; + s->regs[GT_PCI0M1REMAP] = 0x00000790; + s->regs[GT_PCI1IOREMAP] = 0x00000100; + s->regs[GT_PCI1M0REMAP] = 0x00000110; + s->regs[GT_PCI1M1REMAP] = 0x00000120; + + /* CPU Error Report */ + s->regs[GT_CPUERR_ADDRLO] = 0x00000000; + s->regs[GT_CPUERR_ADDRHI] = 0x00000000; + s->regs[GT_CPUERR_DATALO] = 0xffffffff; + s->regs[GT_CPUERR_DATAHI] = 0xffffffff; + s->regs[GT_CPUERR_PARITY] = 0x000000ff; + + /* CPU Sync Barrier */ + s->regs[GT_PCI0SYNC] = 0x00000000; + s->regs[GT_PCI1SYNC] = 0x00000000; + + /* SDRAM and Device Address Decode */ + s->regs[GT_SCS0LD] = 0x00000000; + s->regs[GT_SCS0HD] = 0x00000007; + s->regs[GT_SCS1LD] = 0x00000008; + s->regs[GT_SCS1HD] = 0x0000000f; + s->regs[GT_SCS2LD] = 0x00000010; + s->regs[GT_SCS2HD] = 0x00000017; + s->regs[GT_SCS3LD] = 0x00000018; + s->regs[GT_SCS3HD] = 0x0000001f; + s->regs[GT_CS0LD] = 0x000000c0; + s->regs[GT_CS0HD] = 0x000000c7; + s->regs[GT_CS1LD] = 0x000000c8; + s->regs[GT_CS1HD] = 0x000000cf; + s->regs[GT_CS2LD] = 0x000000d0; + s->regs[GT_CS2HD] = 0x000000df; + s->regs[GT_CS3LD] = 0x000000f0; + s->regs[GT_CS3HD] = 0x000000fb; + s->regs[GT_BOOTLD] = 0x000000fc; + s->regs[GT_BOOTHD] = 0x000000ff; + s->regs[GT_ADERR] = 0xffffffff; + + /* SDRAM Configuration */ + s->regs[GT_SDRAM_CFG] = 0x00000200; + s->regs[GT_SDRAM_OPMODE] = 0x00000000; + s->regs[GT_SDRAM_BM] = 0x00000007; + s->regs[GT_SDRAM_ADDRDECODE] = 0x00000002; + + /* SDRAM Parameters */ + s->regs[GT_SDRAM_B0] = 0x00000005; + s->regs[GT_SDRAM_B1] = 0x00000005; + s->regs[GT_SDRAM_B2] = 0x00000005; + s->regs[GT_SDRAM_B3] = 0x00000005; + + /* ECC */ + s->regs[GT_ECC_ERRDATALO] = 0x00000000; + s->regs[GT_ECC_ERRDATAHI] = 0x00000000; + s->regs[GT_ECC_MEM] = 0x00000000; + s->regs[GT_ECC_CALC] = 0x00000000; + s->regs[GT_ECC_ERRADDR] = 0x00000000; + + /* Device Parameters */ + s->regs[GT_DEV_B0] = 0x386fffff; + s->regs[GT_DEV_B1] = 0x386fffff; + s->regs[GT_DEV_B2] = 0x386fffff; + s->regs[GT_DEV_B3] = 0x386fffff; + s->regs[GT_DEV_BOOT] = 0x146fffff; + + /* DMA registers are all zeroed at reset */ + + /* Timer/Counter */ + s->regs[GT_TC0] = 0xffffffff; + s->regs[GT_TC1] = 0x00ffffff; + s->regs[GT_TC2] = 0x00ffffff; + s->regs[GT_TC3] = 0x00ffffff; + s->regs[GT_TC_CONTROL] = 0x00000000; + + /* PCI Internal */ + 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; + s->regs[GT_PCI0_BS_CS20] = 0x01fff000; + s->regs[GT_PCI0_BS_CS3BT] = 0x00fff000; + s->regs[GT_PCI1_IACK] = 0x00000000; + s->regs[GT_PCI0_IACK] = 0x00000000; + s->regs[GT_PCI0_BARE] = 0x0000000f; + s->regs[GT_PCI0_PREFMBR] = 0x00000040; + s->regs[GT_PCI0_SCS10_BAR] = 0x00000000; + s->regs[GT_PCI0_SCS32_BAR] = 0x01000000; + s->regs[GT_PCI0_CS20_BAR] = 0x1c000000; + s->regs[GT_PCI0_CS3BT_BAR] = 0x1f000000; + s->regs[GT_PCI0_SSCS10_BAR] = 0x00000000; + s->regs[GT_PCI0_SSCS32_BAR] = 0x01000000; + s->regs[GT_PCI0_SCS3BT_BAR] = 0x1f000000; + 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; + s->regs[GT_PCI1_BS_CS20] = 0x01fff000; + s->regs[GT_PCI1_BS_CS3BT] = 0x00fff000; + s->regs[GT_PCI1_BARE] = 0x0000000f; + s->regs[GT_PCI1_PREFMBR] = 0x00000040; + s->regs[GT_PCI1_SCS10_BAR] = 0x00000000; + s->regs[GT_PCI1_SCS32_BAR] = 0x01000000; + s->regs[GT_PCI1_CS20_BAR] = 0x1c000000; + s->regs[GT_PCI1_CS3BT_BAR] = 0x1f000000; + s->regs[GT_PCI1_SSCS10_BAR] = 0x00000000; + s->regs[GT_PCI1_SSCS32_BAR] = 0x01000000; + s->regs[GT_PCI1_SCS3BT_BAR] = 0x1f000000; + s->regs[GT_PCI1_CFGADDR] = 0x00000000; + s->regs[GT_PCI1_CFGDATA] = 0x00000000; + s->regs[GT_PCI0_CFGADDR] = 0x00000000; + + /* Interrupt registers are all zeroed at reset */ + + gt64120_isd_mapping(s); + gt64120_pci_mapping(s); + gt64120_update_pci_cfgdata_mapping(s); +} + +static void gt64120_realize(DeviceState *dev, Error **errp) +{ + GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(dev); + + memory_region_init_io(&s->ISD_mem, OBJECT(dev), &isd_mem_ops, s, + "gt64120-isd", 0x1000); + memory_region_init(&s->pci0_mem, OBJECT(dev), "pci0-mem", 4 * GiB); + address_space_init(&s->pci0_mem_as, &s->pci0_mem, "pci0-mem"); + phb->bus = pci_root_bus_new(dev, "pci", + &s->pci0_mem, + get_system_io(), + PCI_DEVFN(18, 0), TYPE_PCI_BUS); + + pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "gt64120_pci"); + 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) +{ + /* Values from chapter 17.16 "PCI Configuration" */ + + pci_set_long(d->wmask + PCI_BASE_ADDRESS_0, 0xfffff008); /* SCS[1:0] */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_1, 0xfffff008); /* SCS[3:2] */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_2, 0xfffff008); /* CS[2:0] */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_3, 0xfffff008); /* CS[3], BootCS */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_4, 0xfffff000); /* ISD MMIO */ + pci_set_long(d->wmask + PCI_BASE_ADDRESS_5, 0xfffff001); /* ISD I/O */ +} + +static void gt64120_pci_reset_hold(Object *obj, ResetType type) +{ + PCIDevice *d = PCI_DEVICE(obj); + + /* Values from chapter 17.16 "PCI Configuration" */ + + pci_set_word(d->config + PCI_COMMAND, 0); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + pci_config_set_prog_interface(d->config, 0); + + pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); + + pci_set_byte(d->config + 0x3d, 0x01); +} + +static void gt64120_pci_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = gt64120_pci_reset_hold; + k->realize = gt64120_pci_realize; + k->vendor_id = PCI_VENDOR_ID_MARVELL; + k->device_id = PCI_DEVICE_ID_MARVELL_GT6412X; + k->revision = 0x10; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * 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 gt64120_pci_info = { + .name = "gt64120_pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = gt64120_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +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; + device_class_set_legacy_reset(dc, gt64120_reset); + dc->vmsd = &vmstate_gt64120; +} + +static const TypeInfo gt64120_info = { + .name = TYPE_GT64120_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(GT64120State), + .class_init = gt64120_class_init, +}; + +static void gt64120_pci_register_types(void) +{ + type_register_static(>64120_info); + type_register_static(>64120_pci_info); +} + +type_init(gt64120_pci_register_types) diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index d5426ef4a5..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,81 +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"); - } -} - -PCIBus *i440fx_init(const char *pci_type, - DeviceState *dev, - 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) -{ - PCIBus *b; - PCIDevice *d; - PCIHostState *s; - PCII440FXState *f; - unsigned i; - I440FXState *i440fx; - - 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); + d = pci_create_simple(b, 0, s->pci_type); f = I440FX_PCI_DEVICE(d); - 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); @@ -286,20 +291,21 @@ PCIBus *i440fx_init(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; @@ -307,8 +313,6 @@ PCIBus *i440fx_init(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) @@ -346,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 e832babc9d..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,9 +31,10 @@ 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', diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index cc9c4d6d3b..421c287eb0 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,10 +91,11 @@ 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); + qdev_init_gpio_out(dev, s->irq, ARRAY_SIZE(s->irq)); } static Property mv64361_pcihost_props[] = { @@ -547,6 +542,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 +880,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) @@ -932,7 +929,7 @@ static void mv64361_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mv64361_realize; - dc->reset = mv64361_reset; + device_class_set_legacy_reset(dc, mv64361_reset); } static const TypeInfo mv64361_type_info = { 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 index 0b26b43736..d4c118d443 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -25,7 +25,7 @@ * 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 seting + * 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 @@ -62,6 +62,15 @@ static bool pnv_parent_fixup(Object *parent, BusState *parent_bus, 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 @@ -79,7 +88,7 @@ static bool pnv_phb_user_device_init(PnvPHB *phb, Error **errp) return false; } - parent = pnv_chip_add_phb(chip, phb, errp); + parent = pnv_phb_user_get_parent(chip, phb, errp); if (!parent) { return false; } @@ -199,14 +208,16 @@ static void pnv_phb_class_init(ObjectClass *klass, void *data) dc->user_creatable = true; } -static void pnv_phb_root_port_reset(DeviceState *dev) +static void pnv_phb_root_port_reset_hold(Object *obj, ResetType type) { - PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev); - PnvPHBRootPort *phb_rp = PNV_PHB_ROOT_PORT(dev); - PCIDevice *d = PCI_DEVICE(dev); + 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; - rpc->parent_reset(dev); + if (rpc->parent_phases.hold) { + rpc->parent_phases.hold(obj, type); + } if (phb_rp->version == 3) { return; @@ -300,6 +311,7 @@ static Property pnv_phb_root_port_properties[] = { 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); @@ -308,9 +320,8 @@ static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, pnv_phb_root_port_properties); device_class_set_parent_realize(dc, pnv_phb_root_port_realize, &rpc->parent_realize); - device_class_set_parent_reset(dc, pnv_phb_root_port_reset, - &rpc->parent_reset); - dc->reset = &pnv_phb_root_port_reset; + 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; diff --git a/hw/pci-host/pnv_phb.h b/hw/pci-host/pnv_phb.h index 58ebd6dd0f..eb429d529f 100644 --- a/hw/pci-host/pnv_phb.h +++ b/hw/pci-host/pnv_phb.h @@ -12,9 +12,9 @@ #include "hw/pci/pcie_host.h" #include "hw/pci/pcie_port.h" +#include "hw/ppc/pnv.h" #include "qom/object.h" -typedef struct PnvChip PnvChip; typedef struct PnvPhb4PecState PnvPhb4PecState; struct PnvPHB { diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 9054c393a2..2a74dbe45f 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -16,6 +16,7 @@ #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" @@ -756,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"); @@ -967,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); @@ -1011,7 +1016,7 @@ void pnv_phb3_bus_init(DeviceState *dev, PnvPHB3 *phb) object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id, &error_abort); - pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb); + pci_setup_iommu(pci->bus, &pnv_phb3_iommu_ops, phb); } static void pnv_phb3_realize(DeviceState *dev, Error **errp) diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index 2f4112907b..77d673da54 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -13,7 +13,6 @@ #include "hw/pci-host/pnv_phb3.h" #include "hw/ppc/pnv.h" #include "hw/pci/msi.h" -#include "monitor/monitor.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" @@ -228,22 +227,19 @@ static void phb3_msi_resend(ICSState *ics) } } -static void phb3_msi_reset(DeviceState *dev) +static void phb3_msi_reset_hold(Object *obj, ResetType type) { - 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, type); + } 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 +268,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 +280,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 +288,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; @@ -320,13 +315,13 @@ static void pnv_phb3_msi_register_types(void) type_init(pnv_phb3_msi_register_types); -void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon) +void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, GString *buf) { ICSState *ics = ICS(msi); int i; - monitor_printf(mon, "ICS %4x..%4x %p\n", - ics->offset, ics->offset + ics->nr_irqs - 1, ics); + g_string_append_printf(buf, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); for (i = 0; i < ics->nr_irqs; i++) { uint64_t ive; @@ -339,12 +334,12 @@ void pnv_phb3_msi_pic_print_info(Phb3MsiState *msi, Monitor *mon) continue; } - monitor_printf(mon, " %4x %c%c server=%04x prio=%02x gen=%d\n", - ics->offset + i, - GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-', - GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-', - (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2, - (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive), - (uint32_t) GETFIELD(IODA2_IVT_GEN, ive)); + g_string_append_printf(buf, " %4x %c%c server=%04x prio=%02x gen=%d\n", + ics->offset + i, + GETFIELD(IODA2_IVT_P, ive) ? 'P' : '-', + GETFIELD(IODA2_IVT_Q, ive) ? 'Q' : '-', + (uint32_t) GETFIELD(IODA2_IVT_SERVER, ive) >> 2, + (uint32_t) GETFIELD(IODA2_IVT_PRIORITY, ive), + (uint32_t) GETFIELD(IODA2_IVT_GEN, ive)); } } diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index ccbde841fc..99991008c1 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -10,7 +10,6 @@ #include "qemu/log.h" #include "qapi/visitor.h" #include "qapi/error.h" -#include "monitor/monitor.h" #include "target/ppc/cpu.h" #include "hw/pci-host/pnv_phb4_regs.h" #include "hw/pci-host/pnv_phb4.h" @@ -133,13 +132,13 @@ static void pnv_phb4_rc_config_write(PnvPHB4 *phb, unsigned off, 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; } @@ -155,13 +154,13 @@ static uint64_t pnv_phb4_rc_config_read(PnvPHB4 *phb, unsigned off, 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; } @@ -207,7 +206,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) && @@ -391,7 +390,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; @@ -855,7 +854,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]; } @@ -1000,7 +999,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; @@ -1009,7 +1008,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; @@ -1019,7 +1019,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; @@ -1030,7 +1030,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: @@ -1039,30 +1039,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 @@ -1086,7 +1091,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]; } @@ -1095,10 +1100,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; @@ -1107,7 +1111,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; @@ -1117,20 +1121,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 @@ -1408,7 +1417,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]); } @@ -1497,7 +1506,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); @@ -1518,6 +1527,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); @@ -1557,7 +1570,7 @@ void pnv_phb4_bus_init(DeviceState *dev, PnvPHB4 *phb) object_property_set_int(OBJECT(pci->bus), "chip-id", phb->chip_id, &error_abort); - pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb); + pci_setup_iommu(pci->bus, &pnv_phb4_iommu_ops, phb); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; } @@ -1787,17 +1800,19 @@ static void pnv_phb4_register_types(void) type_init(pnv_phb4_register_types); -void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon) +void pnv_phb4_pic_print_info(PnvPHB4 *phb, GString *buf) { uint64_t notif_port = phb->regs[PHB_INT_NOTIFY_ADDR >> 3] & ~PHB_INT_NOTIFY_ADDR_64K; uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3]; bool abt = !!(phb->regs[PHB_CTRLR >> 3] & PHB_CTRLR_IRQ_ABT_MODE); - monitor_printf(mon, "PHB4[%x:%x] Source %08x .. %08x %s @%"HWADDR_PRIx"\n", - phb->chip_id, phb->phb_id, - offset, offset + phb->xsrc.nr_irqs - 1, - abt ? "ABT" : "", - notif_port); - xive_source_pic_print_info(&phb->xsrc, 0, mon); + g_string_append_printf(buf, + "PHB4[%x:%x] Source %08x .. %08x " + "%s @%"HWADDR_PRIx"\n", + phb->chip_id, phb->phb_id, + offset, offset + phb->xsrc.nr_irqs - 1, + abt ? "ABT" : "", + notif_port); + xive_source_pic_print_info(&phb->xsrc, 0, buf); } diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index 9871f462cd..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,9 +135,50 @@ 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) +{ + 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); @@ -127,8 +192,9 @@ static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, &error_fatal); if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) { - return; + return NULL; } + return phb; } static void pnv_pec_realize(DeviceState *dev, Error **errp) @@ -147,8 +213,9 @@ static void pnv_pec_realize(DeviceState *dev, Error **errp) /* Create PHBs if running with defaults */ if (defaults_enabled()) { + g_assert(pec->num_phbs <= MAX_PHBS_PER_PEC); for (i = 0; i < pec->num_phbs; i++) { - pnv_pec_default_phb_realize(pec, i, errp); + pec->phbs[i] = pnv_pec_default_phb_realize(pec, i, errp); } } @@ -196,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); @@ -206,7 +276,8 @@ 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; 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..07924bce28 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) @@ -520,11 +524,11 @@ static void ppc440_pcix_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ppc440_pcix_realize; - dc->reset = ppc440_pcix_reset; + device_class_set_legacy_reset(dc, ppc440_pcix_reset); } 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 96% rename from hw/ppc/ppc4xx_pci.c rename to hw/pci-host/ppc4xx_pci.c index 8642b96455..b6c6c8993c 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/pci-host/ppc4xx_pci.c @@ -24,12 +24,11 @@ #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" @@ -46,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 @@ -276,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), @@ -289,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() @@ -300,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), @@ -321,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]); @@ -333,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); @@ -367,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, @@ -386,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..b70631045a 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,10 +19,9 @@ #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" #include "hw/pci-host/ppce500.h" #include "qom/object.h" @@ -189,7 +188,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 +267,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 +378,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 +391,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 +404,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 +434,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,9 +472,9 @@ 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"); + pci_create_simple(b, 0, TYPE_PPC_E500_PCI_BRIDGE); memory_region_init(&s->container, OBJECT(h), "pci-container", PCIE500_ALL_SIZE); memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_be_ops, h, @@ -504,17 +507,6 @@ static void e500_host_bridge_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static const TypeInfo e500_host_bridge_info = { - .name = TYPE_PPC_E500_PCI_BRIDGE, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PPCE500PCIBridgeState), - .class_init = e500_host_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - static Property pcihost_properties[] = { DEFINE_PROP_UINT32("first_slot", PPCE500PCIState, first_slot, 0x11), DEFINE_PROP_UINT32("first_pin_irq", PPCE500PCIState, first_pin_irq, 0x1), @@ -531,17 +523,23 @@ static void e500_pcihost_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_ppce500_pci; } -static const TypeInfo e500_pcihost_info = { - .name = TYPE_PPC_E500_PCI_HOST_BRIDGE, - .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(PPCE500PCIState), - .class_init = e500_pcihost_class_init, +static const TypeInfo e500_pci_types[] = { + { + .name = TYPE_PPC_E500_PCI_BRIDGE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PPCE500PCIBridgeState), + .class_init = e500_host_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, + { + .name = TYPE_PPC_E500_PCI_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PPCE500PCIState), + .class_init = e500_pcihost_class_init, + }, }; -static void e500_pci_register_types(void) -{ - type_register_static(&e500_pcihost_info); - type_register_static(&e500_host_bridge_info); -} - -type_init(e500_pci_register_types) +DEFINE_TYPES(e500_pci_types) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 20da121374..f3e713318e 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,11 +175,12 @@ 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, mch.above_4g_mem_size, 0), + DEFINE_PROP_BOOL(PCI_HOST_PROP_SMM_RANGES, Q35PCIHost, + mch.has_smm_ranges, true), DEFINE_PROP_BOOL("x-pci-hole64-fix", Q35PCIHost, pci_hole64_fix, true), DEFINE_PROP_END_OF_LIST(), }; @@ -221,6 +216,7 @@ static void q35_host_initfn(Object *obj) /* mch's object_initialize resets the default value, set it again */ qdev_prop_set_uint64(DEVICE(s), PCI_HOST_PROP_PCI_HOLE64_SIZE, Q35_PCI_HOST_HOLE64_SIZE_DEFAULT); + object_property_add(obj, PCI_HOST_PROP_PCI_HOLE_START, "uint32", q35_host_get_pci_hole_start, NULL, NULL, NULL); @@ -240,19 +236,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 +279,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, @@ -484,6 +479,10 @@ static void mch_write_config(PCIDevice *d, mch_update_pciexbar(mch); } + if (!mch->has_smm_ranges) { + return; + } + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, MCH_HOST_BRIDGE_SMRAM_SIZE)) { mch_update_smram(mch); @@ -502,10 +501,13 @@ static void mch_write_config(PCIDevice *d, static void mch_update(MCHPCIState *mch) { mch_update_pciexbar(mch); + mch_update_pam(mch); - mch_update_smram(mch); - mch_update_ext_tseg_mbytes(mch); - mch_update_smbase_smram(mch); + if (mch->has_smm_ranges) { + mch_update_smram(mch); + mch_update_ext_tseg_mbytes(mch); + mch_update_smbase_smram(mch); + } /* * pci hole goes from end-of-low-ram to io-apic. @@ -528,7 +530,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. @@ -546,19 +548,21 @@ static void mch_reset(DeviceState *qdev) pci_set_quad(d->config + MCH_HOST_BRIDGE_PCIEXBAR, MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT); - d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; - d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT; - d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; - d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; + if (mch->has_smm_ranges) { + d->config[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_DEFAULT; + d->config[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_DEFAULT; + d->wmask[MCH_HOST_BRIDGE_SMRAM] = MCH_HOST_BRIDGE_SMRAM_WMASK; + d->wmask[MCH_HOST_BRIDGE_ESMRAMC] = MCH_HOST_BRIDGE_ESMRAMC_WMASK; - if (mch->ext_tseg_mbytes > 0) { - pci_set_word(d->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, - MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); + if (mch->ext_tseg_mbytes > 0) { + pci_set_word(d->config + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); + } + + d->config[MCH_HOST_BRIDGE_F_SMBASE] = 0; + d->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0xff; } - d->config[MCH_HOST_BRIDGE_F_SMBASE] = 0; - d->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0xff; - mch_update(mch); } @@ -574,8 +578,21 @@ 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); + + /* PAM */ + 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(&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); + } + + if (!mch->has_smm_ranges) { + return; + } /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", @@ -643,25 +660,6 @@ 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], - 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], - PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); - } -} - -uint64_t mch_mcfg_base(void) -{ - bool ambiguous; - Object *o = object_resolve_path_type("", TYPE_MCH_PCI_DEVICE, &ambiguous); - if (!o) { - return 0; - } - return MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT; } static Property mch_props[] = { @@ -678,7 +676,7 @@ static void mch_class_init(ObjectClass *klass, void *data) k->realize = mch_realize; k->config_write = mch_write_config; - dc->reset = mch_reset; + device_class_set_legacy_reset(dc, mch_reset); device_class_set_props(dc, mch_props); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->desc = "Host bridge"; 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..1707feb951 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); @@ -502,7 +504,7 @@ static void sabre_class_init(ObjectClass *klass, void *data) SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = sabre_realize; - dc->reset = sabre_reset; + device_class_set_legacy_reset(dc, sabre_reset); device_class_set_props(dc, sabre_properties); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->fw_name = "pci"; 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 aebd44d265..e4c1abd871 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -26,7 +26,7 @@ #include "hw/irq.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" @@ -128,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) @@ -277,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 @@ -297,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..d257acee17 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 @@ -508,7 +509,7 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pci_vpb_realize; - dc->reset = pci_vpb_reset; + device_class_set_legacy_reset(dc, pci_vpb_reset); dc->vmsd = &pci_vpb_vmstate; device_class_set_props(dc, pci_vpb_properties); } diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 38d5901a45..24f691ea82 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -298,10 +298,9 @@ 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; + device_class_set_legacy_reset(dc, pci_bridge_reset); k->config_read = xilinx_pcie_root_config_read; k->config_write = xilinx_pcie_root_config_write; /* 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 5aff7ed1c6..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' @@ -14,8 +16,7 @@ pci_ss.add(files( # CONFIG_PCI_EXPRESS=n. pci_ss.add(files('pcie.c', 'pcie_aer.c')) pci_ss.add(files('pcie_doe.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) +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 058d1d1ef1..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) @@ -306,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; } @@ -317,7 +320,6 @@ bool msi_is_masked(const PCIDevice *dev, unsigned int vector) void msi_set_mask(PCIDevice *dev, int vector, bool mask, Error **errp) { - ERRP_GUARD(); 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; @@ -415,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 9e70fcd6fa..487e49834e 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -26,6 +26,8 @@ #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) @@ -124,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; } @@ -639,6 +648,7 @@ undo: } dev->msix_vector_use_notifier = NULL; dev->msix_vector_release_notifier = NULL; + dev->msix_vector_poll_notifier = NULL; return ret; } @@ -675,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, @@ -683,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..fdfe44435c --- /dev/null +++ b/hw/pci/pci-hmp-cmds.c @@ -0,0 +1,249 @@ +/* + * 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")) { + if (addr != PCI_BAR_UNMAPPED) { + monitor_printf(mon, "I/O at 0x%04" PRIx64 + " [0x%04" PRIx64 "]\n", + addr, addr + size - 1); + } else { + monitor_printf(mon, "I/O (not mapped)\n"); + } + } else { + if (addr != PCI_BAR_UNMAPPED) { + 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); + } else { + monitor_printf(mon, "%d bit%s memory (not mapped)\n", + region->value->mem_type_64 ? 64 : 32, + region->value->prefetch ? " prefetchable" : ""); + } + } + } + + 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..3397d0c82e 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"); @@ -45,14 +46,12 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) /* kvm-all wants this */ MSIMessage pci_get_msi_message(PCIDevice *dev, int vector) { - g_assert(false); - return (MSIMessage){}; + g_assert_not_reached(); } uint16_t pci_requester_id(PCIDevice *dev) { - g_assert(false); - return 0; + g_assert_not_reached(); } /* Required by ahci.c */ diff --git a/hw/pci/pci.c b/hw/pci/pci.c index e358a2fb4c..976461a24d 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,15 +62,28 @@ 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, ResetType type); +static bool pcie_has_upstream_port(PCIDevice *dev); + +static void prop_pci_busnr_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t busnr = pci_dev_bus_num(PCI_DEVICE(obj)); + + visit_type_uint8(v, name, &busnr, errp); +} + +static const PropertyInfo prop_pci_busnr = { + .name = "busnr", + .get = prop_pci_busnr_get, +}; static Property pci_props[] = { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), - DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, -1), + DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX), DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), @@ -78,6 +94,15 @@ 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_SIZE32("x-max-bounce-buffer-size", PCIDevice, + max_bounce_buffer_size, DEFAULT_MAX_BOUNCE_BUFFER_SIZE), + DEFINE_PROP_BIT("x-pcie-ext-tag", PCIDevice, cap_present, + QEMU_PCIE_EXT_TAG_BITNR, true), + { .name = "busnr", .info = &prop_pci_busnr }, DEFINE_PROP_END_OF_LIST() }; @@ -85,7 +110,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 +119,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 +165,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 +220,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 +276,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 **); @@ -248,7 +289,7 @@ 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; #endif -static QLIST_HEAD(, PCIHostState) pci_host_bridges; +PCIHostStateList pci_host_bridges; int pci_bar(PCIDevice *d, int reg) { @@ -287,8 +328,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; @@ -326,6 +372,17 @@ 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); @@ -375,6 +432,7 @@ static void pci_do_device_reset(PCIDevice *dev) msi_reset(dev); msix_reset(dev); + pcie_sriov_pf_reset(dev); } /* @@ -383,18 +441,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, ResetType type) { - PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus); + PCIBus *bus = PCI_BUS(obj); int i; for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { @@ -468,15 +526,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 */ @@ -490,32 +547,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; } @@ -526,16 +579,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; @@ -543,21 +601,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; } @@ -581,7 +639,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]); } @@ -597,7 +655,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; @@ -619,9 +676,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, @@ -643,7 +699,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, @@ -684,7 +740,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, @@ -704,7 +760,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, @@ -856,7 +912,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 */ @@ -992,6 +1048,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); @@ -1084,6 +1143,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, @@ -1095,9 +1169,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); @@ -1127,9 +1202,15 @@ 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. + */ + 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), @@ -1147,6 +1228,8 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, "bus master container", UINT64_MAX); address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_container_region, pci_dev->name); + pci_dev->bus_master_as.max_bounce_buffer_size = + pci_dev->max_bounce_buffer_size; if (phase_check(PHASE_MACHINE_READY)) { pci_init_bus_master(pci_dev); @@ -1159,7 +1242,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); @@ -1176,7 +1259,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); @@ -1228,6 +1311,17 @@ static void pci_qdev_unrealize(DeviceState *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, @@ -1383,9 +1477,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) { @@ -1543,7 +1635,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) @@ -1603,8 +1695,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); @@ -1651,7 +1747,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 */ @@ -1667,13 +1763,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"}, @@ -1781,7 +1870,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; @@ -1793,273 +1882,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) @@ -2101,7 +1966,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; } @@ -2111,7 +1976,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; @@ -2187,6 +2052,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; @@ -2196,7 +2063,36 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) bool is_default_rom; uint16_t class_id; - if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) { + /* + * 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 != UINT32_MAX && !is_power_of_2(pci_dev->romsize)) { error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize); return; } @@ -2228,6 +2124,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 " @@ -2271,8 +2186,8 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) 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; @@ -2282,9 +2197,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) @@ -2293,17 +2213,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) @@ -2416,16 +2337,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) { /* @@ -2452,57 +2378,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 == UINT32_MAX) { + 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 != UINT_MAX) { + 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); @@ -2589,44 +2515,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; @@ -2771,6 +2659,10 @@ static void pci_device_class_init(ObjectClass *klass, void *data) k->unrealize = pci_qdev_unrealize; k->bus_type = TYPE_PCI_BUS; device_class_set_props(k, pci_props); + object_class_property_set_description( + klass, "x-max-bounce-buffer-size", + "Maximum buffer size allocated for bounce buffers used for mapped " + "access to indirect DMA memory"); } static void pci_device_class_base_init(ObjectClass *klass, void *data) @@ -2786,13 +2678,29 @@ static void pci_device_class_base_init(ObjectClass *klass, void *data) } } -AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) +/* + * Get IOMMU root bus, aliased bus and devfn of a PCI device + * + * IOMMU root bus is needed by all call sites to call into iommu_ops. + * For call sites which don't need aliased BDF, passing NULL to + * aliased_[bus|devfn] is allowed. + * + * @piommu_bus: return root #PCIBus backed by an IOMMU for the PCI device. + * + * @aliased_bus: return aliased #PCIBus of the PCI device, optional. + * + * @aliased_devfn: return aliased devfn of the PCI device, optional. + */ +static void pci_device_get_iommu_bus_devfn(PCIDevice *dev, + PCIBus **piommu_bus, + PCIBus **aliased_bus, + int *aliased_devfn) { PCIBus *bus = pci_get_bus(dev); PCIBus *iommu_bus = bus; - uint8_t devfn = dev->devfn; + int 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); /* @@ -2831,22 +2739,86 @@ 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); + + assert(0 <= devfn && devfn < PCI_DEVFN_MAX); + assert(iommu_bus); + + if (pci_bus_bypass_iommu(bus) || !iommu_bus->iommu_ops) { + iommu_bus = NULL; + } + + *piommu_bus = iommu_bus; + + if (aliased_bus) { + *aliased_bus = bus; + } + + if (aliased_devfn) { + *aliased_devfn = devfn; + } +} + +AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); + if (iommu_bus) { + 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) +bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, + Error **errp) { - bus->iommu_fn = fn; + PCIBus *iommu_bus, *aliased_bus; + int aliased_devfn; + + /* set_iommu_device requires device's direct BDF instead of aliased BDF */ + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, + &aliased_bus, &aliased_devfn); + if (iommu_bus && iommu_bus->iommu_ops->set_iommu_device) { + hiod->aliased_bus = aliased_bus; + hiod->aliased_devfn = aliased_devfn; + return iommu_bus->iommu_ops->set_iommu_device(pci_get_bus(dev), + iommu_bus->iommu_opaque, + dev->devfn, hiod, errp); + } + return true; +} + +void pci_device_unset_iommu_device(PCIDevice *dev) +{ + PCIBus *iommu_bus; + + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, NULL, NULL); + if (iommu_bus && iommu_bus->iommu_ops->unset_iommu_device) { + return iommu_bus->iommu_ops->unset_iommu_device(pci_get_bus(dev), + iommu_bus->iommu_opaque, + dev->devfn); + } +} + +void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) +{ + /* + * 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; @@ -2854,7 +2826,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..2c7bb1a525 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)); } } @@ -380,12 +380,20 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename) sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn; sec_bus->address_space_mem = &br->address_space_mem; memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX); + address_space_init(&br->as_mem, &br->address_space_mem, + "pci_bridge_pci_mem"); 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); + address_space_init(&br->as_io, &br->address_space_io, "pci_bridge_pci_io"); + 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 +402,10 @@ 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); + address_space_destroy(&s->as_mem); + address_space_destroy(&s->as_io); + pci_bridge_region_del(s, &s->windows); + pci_bridge_region_cleanup(s, &s->windows); /* object_unparent() is called automatically during device deletion */ } @@ -467,11 +477,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..0b455c8654 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 @@ -63,7 +86,13 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) * Specification, Revision 1.1., or subsequent PCI Express Base * Specification revisions. */ - pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER); + uint32_t devcap = PCI_EXP_DEVCAP_RBER; + + if (dev->cap_present & QEMU_PCIE_EXT_TAG) { + devcap = PCI_EXP_DEVCAP_RBER | PCI_EXP_DEVCAP_EXT_TAG; + } + + pci_set_long(exp_cap + PCI_EXP_DEVCAP, devcap); pci_set_long(exp_cap + PCI_EXP_LNKCAP, (port << PCI_EXP_LNKCAP_PN_SHIFT) | @@ -82,6 +111,73 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) pci_set_word(cmask + PCI_EXP_LNKSTA, 0); } +/* Includes setting the target speed default */ +static void pcie_cap_fill_lnk(uint8_t *exp_cap, PCIExpLinkWidth width, + PCIExpLinkSpeed speed) +{ + /* Clear and fill LNKCAP from what was configured above */ + pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP, + PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, + QEMU_PCI_EXP_LNKCAP_MLW(width) | + QEMU_PCI_EXP_LNKCAP_MLS(speed)); + + if (speed > QEMU_PCI_EXP_LNK_2_5GT) { + /* + * Target Link Speed defaults to the highest link speed supported by + * the component. 2.5GT/s devices are permitted to hardwire to zero. + */ + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKCTL2, + PCI_EXP_LNKCTL2_TLS); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKCTL2, + QEMU_PCI_EXP_LNKCAP_MLS(speed) & + PCI_EXP_LNKCTL2_TLS); + } + + /* + * 2.5 & 5.0GT/s can be fully described by LNKCAP, but 8.0GT/s is + * actually a reference to the highest bit supported in this register. + * We assume the device supports all link speeds. + */ + if (speed > QEMU_PCI_EXP_LNK_5GT) { + pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP2, ~0U); + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_2_5GB | + PCI_EXP_LNKCAP2_SLS_5_0GB | + PCI_EXP_LNKCAP2_SLS_8_0GB); + if (speed > QEMU_PCI_EXP_LNK_8GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_16_0GB); + } + if (speed > QEMU_PCI_EXP_LNK_16GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_32_0GB); + } + if (speed > QEMU_PCI_EXP_LNK_32GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_64_0GB); + } + } +} + +void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width, + PCIExpLinkSpeed speed) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + + /* + * For an end point or USP need to set the current status as well + * as the capabilities. + */ + pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, + QEMU_PCI_EXP_LNKSTA_NLW(width) | + QEMU_PCI_EXP_LNKSTA_CLS(speed)); + + pcie_cap_fill_lnk(exp_cap, width, speed); +} + static void pcie_cap_fill_slot_lnk(PCIDevice *dev) { PCIESlot *s = (PCIESlot *)object_dynamic_cast(OBJECT(dev), TYPE_PCIE_SLOT); @@ -92,13 +188,6 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev) return; } - /* Clear and fill LNKCAP from what was configured above */ - pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP, - PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); - pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, - QEMU_PCI_EXP_LNKCAP_MLW(s->width) | - QEMU_PCI_EXP_LNKCAP_MLS(s->speed)); - /* * Link bandwidth notification is required for all root ports and * downstream ports supporting links wider than x1 or multiple link @@ -121,34 +210,9 @@ static void pcie_cap_fill_slot_lnk(PCIDevice *dev) pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, PCI_EXP_LNKCAP_DLLLARC); /* the PCI_EXP_LNKSTA_DLLLA will be set in the hotplug function */ - - /* - * Target Link Speed defaults to the highest link speed supported by - * the component. 2.5GT/s devices are permitted to hardwire to zero. - */ - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKCTL2, - PCI_EXP_LNKCTL2_TLS); - pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKCTL2, - QEMU_PCI_EXP_LNKCAP_MLS(s->speed) & - PCI_EXP_LNKCTL2_TLS); } - /* - * 2.5 & 5.0GT/s can be fully described by LNKCAP, but 8.0GT/s is - * actually a reference to the highest bit supported in this register. - * We assume the device supports all link speeds. - */ - if (s->speed > QEMU_PCI_EXP_LNK_5GT) { - pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP2, ~0U); - pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, - PCI_EXP_LNKCAP2_SLS_2_5GB | - PCI_EXP_LNKCAP2_SLS_5_0GB | - PCI_EXP_LNKCAP2_SLS_8_0GB); - if (s->speed > QEMU_PCI_EXP_LNK_8GT) { - pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, - PCI_EXP_LNKCAP2_SLS_16_0GB); - } - } + pcie_cap_fill_lnk(exp_cap, s->width, s->speed); } int pcie_cap_init(PCIDevice *dev, uint8_t offset, @@ -269,6 +333,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 +444,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 +466,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 +651,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 +682,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 +705,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 +725,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 +754,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 +770,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 +794,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 +860,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 +884,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 +1136,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 eff62f3945..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,7 +324,7 @@ 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. */ } @@ -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_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..9f978ba164 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -92,16 +92,6 @@ static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c, return s; } -PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot) -{ - struct PCIEChassis *c; - c = pcie_chassis_find(chassis_number); - if (!c) { - return NULL; - } - return pcie_chassis_find_slot_with_chassis(c, slot); -} - int pcie_chassis_add_slot(struct PCIESlot *slot) { struct PCIEChassis *c; @@ -161,6 +151,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 +208,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 +223,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 e71f3a7483..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); } @@ -486,8 +542,9 @@ static const MemoryRegionOps shpc_mmio_ops = { .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); @@ -497,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; } @@ -553,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); @@ -601,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; @@ -722,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/Kconfig b/hw/pcmcia/Kconfig deleted file mode 100644 index 41f2df9136..0000000000 --- a/hw/pcmcia/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -config PCMCIA - bool diff --git a/hw/pcmcia/meson.build b/hw/pcmcia/meson.build deleted file mode 100644 index 51f2512b8e..0000000000 --- a/hw/pcmcia/meson.build +++ /dev/null @@ -1,2 +0,0 @@ -softmmu_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c')) -softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx.c')) diff --git a/hw/pcmcia/pcmcia.c b/hw/pcmcia/pcmcia.c deleted file mode 100644 index 03d13e7d67..0000000000 --- a/hw/pcmcia/pcmcia.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * PCMCIA emulation - * - * Copyright 2013 SUSE LINUX Products GmbH - */ - -#include "qemu/osdep.h" -#include "qemu/module.h" -#include "hw/pcmcia.h" - -static const TypeInfo pcmcia_card_type_info = { - .name = TYPE_PCMCIA_CARD, - .parent = TYPE_DEVICE, - .instance_size = sizeof(PCMCIACardState), - .abstract = true, - .class_size = sizeof(PCMCIACardClass), -}; - -static void pcmcia_register_types(void) -{ - type_register_static(&pcmcia_card_type_info); -} - -type_init(pcmcia_register_types) diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c deleted file mode 100644 index fcca7e571b..0000000000 --- a/hw/pcmcia/pxa2xx.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Intel XScale PXA255/270 PC Card and CompactFlash Interface. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * 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/irq.h" -#include "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/module.h" -#include "hw/pcmcia.h" -#include "hw/arm/pxa.h" - -struct PXA2xxPCMCIAState { - SysBusDevice parent_obj; - - PCMCIASocket slot; - MemoryRegion container_mem; - MemoryRegion common_iomem; - MemoryRegion attr_iomem; - MemoryRegion iomem; - - qemu_irq irq; - qemu_irq cd_irq; - - PCMCIACardState *card; -}; - -static uint64_t pxa2xx_pcmcia_common_read(void *opaque, - hwaddr offset, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - return pcc->common_read(s->card, offset); - } - - return 0; -} - -static void pxa2xx_pcmcia_common_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->common_write(s->card, offset, value); - } -} - -static uint64_t pxa2xx_pcmcia_attr_read(void *opaque, - hwaddr offset, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - return pcc->attr_read(s->card, offset); - } - - return 0; -} - -static void pxa2xx_pcmcia_attr_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->attr_write(s->card, offset, value); - } -} - -static uint64_t pxa2xx_pcmcia_io_read(void *opaque, - hwaddr offset, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - return pcc->io_read(s->card, offset); - } - - return 0; -} - -static void pxa2xx_pcmcia_io_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->io_write(s->card, offset, value); - } -} - -static const MemoryRegionOps pxa2xx_pcmcia_common_ops = { - .read = pxa2xx_pcmcia_common_read, - .write = pxa2xx_pcmcia_common_write, - .endianness = DEVICE_NATIVE_ENDIAN -}; - -static const MemoryRegionOps pxa2xx_pcmcia_attr_ops = { - .read = pxa2xx_pcmcia_attr_read, - .write = pxa2xx_pcmcia_attr_write, - .endianness = DEVICE_NATIVE_ENDIAN -}; - -static const MemoryRegionOps pxa2xx_pcmcia_io_ops = { - .read = pxa2xx_pcmcia_io_read, - .write = pxa2xx_pcmcia_io_write, - .endianness = DEVICE_NATIVE_ENDIAN -}; - -static void pxa2xx_pcmcia_set_irq(void *opaque, int line, int level) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - if (!s->irq) - return; - - 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); - PXA2xxPCMCIAState *s = PXA2XX_PCMCIA(obj); - - memory_region_init(&s->container_mem, obj, "container", 0x10000000); - sysbus_init_mmio(sbd, &s->container_mem); - - /* Socket I/O Memory Space */ - memory_region_init_io(&s->iomem, obj, &pxa2xx_pcmcia_io_ops, s, - "pxa2xx-pcmcia-io", 0x04000000); - memory_region_add_subregion(&s->container_mem, 0x00000000, - &s->iomem); - - /* Then next 64 MB is reserved */ - - /* Socket Attribute Memory Space */ - memory_region_init_io(&s->attr_iomem, obj, &pxa2xx_pcmcia_attr_ops, s, - "pxa2xx-pcmcia-attribute", 0x04000000); - memory_region_add_subregion(&s->container_mem, 0x08000000, - &s->attr_iomem); - - /* Socket Common Memory Space */ - memory_region_init_io(&s->common_iomem, obj, &pxa2xx_pcmcia_common_ops, s, - "pxa2xx-pcmcia-common", 0x04000000); - memory_region_add_subregion(&s->container_mem, 0x0c000000, - &s->common_iomem); - - s->slot.irq = qemu_allocate_irq(pxa2xx_pcmcia_set_irq, s, 0); - - object_property_add_link(obj, "card", TYPE_PCMCIA_CARD, - (Object **)&s->card, - NULL, /* read-only property */ - 0); -} - -/* Insert a new card into a slot */ -int pxa2xx_pcmcia_attach(void *opaque, PCMCIACardState *card) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (s->slot.attached) { - return -EEXIST; - } - - if (s->cd_irq) { - qemu_irq_raise(s->cd_irq); - } - - s->card = card; - pcc = PCMCIA_CARD_GET_CLASS(s->card); - - s->slot.attached = true; - s->card->slot = &s->slot; - pcc->attach(s->card); - - return 0; -} - -/* Eject card from the slot */ -int pxa2xx_pcmcia_detach(void *opaque) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - PCMCIACardClass *pcc; - - if (!s->slot.attached) { - return -ENOENT; - } - - pcc = PCMCIA_CARD_GET_CLASS(s->card); - pcc->detach(s->card); - s->card->slot = NULL; - s->card = NULL; - - s->slot.attached = false; - - if (s->irq) { - qemu_irq_lower(s->irq); - } - if (s->cd_irq) { - qemu_irq_lower(s->cd_irq); - } - - return 0; -} - -/* Who to notify on card events */ -void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq) -{ - PXA2xxPCMCIAState *s = (PXA2xxPCMCIAState *) opaque; - s->irq = irq; - s->cd_irq = cd_irq; -} - -static const TypeInfo pxa2xx_pcmcia_type_info = { - .name = TYPE_PXA2XX_PCMCIA, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(PXA2xxPCMCIAState), - .instance_init = pxa2xx_pcmcia_initfn, -}; - -static void pxa2xx_pcmcia_register_types(void) -{ - type_register_static(&pxa2xx_pcmcia_type_info); -} - -type_init(pxa2xx_pcmcia_register_types) diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index b8d2522f45..b44d91bebb 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -1,13 +1,16 @@ config PSERIES bool + default y + depends on PPC64 && FDT + 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 @@ -22,6 +25,8 @@ config SPAPR_RNG config POWERNV bool + default y + depends on PPC64 && FDT imply PCI_DEVICES imply TEST_DEVICES select ISA_IPMI_BT @@ -32,55 +37,79 @@ config POWERNV select XIVE select FDT_PPC select PCI_POWERNV + select PCA9552 + select PCA9554 + select SERIAL_ISA + select SSI + select SSI_M25P80 + select PNV_SPI config PPC405 bool + default y + depends on PPC select M48T59 select PFLASH_CFI02 select PPC4XX - select SERIAL + select SERIAL_MM config PPC440 bool + default y + depends on PPC && FDT imply PCI_DEVICES imply TEST_DEVICES imply E1000_PCI select PCI_EXPRESS + select PPC440_PCIX select PPC4XX - select SERIAL + select SERIAL_MM select FDT_PPC config PPC4XX bool select BITBANG_I2C - select PCI + select PPC4XX_PCI select PPC_UIC config SAM460EX bool + default y + depends on PPC && FDT select PFLASH_CFI01 select IDE_SII3112 select M41T80 select PPC440 - select SERIAL + select SERIAL_MM select SM501 select SMBUS_EEPROM select USB_EHCI_SYSBUS - select USB_OHCI + select USB_OHCI_SYSBUS select FDT_PPC +config AMIGAONE + bool + default y + depends on PPC + imply ATI_VGA + select ARTICIA + select VT82C686 + select SMBUS_EEPROM + config PEGASOS2 bool + default y + depends on PPC imply ATI_VGA select MV64361 select VT82C686 select SMBUS_EEPROM select VOF -# This should come with VT82C686 - select ACPI_X86 config PREP bool + default y + depends on PPC imply PCI_DEVICES imply TEST_DEVICES select CS4231A @@ -97,6 +126,8 @@ config RS6000_MC config MAC_OLDWORLD bool + default y + depends on PPC imply PCI_DEVICES imply SUNGEM imply TEST_DEVICES @@ -108,9 +139,12 @@ config MAC_OLDWORLD config MAC_NEWWORLD bool + default y + depends on PPC imply PCI_DEVICES imply SUNGEM imply TEST_DEVICES + imply USB_OHCI_PCI select ADB select MACIO select MACIO_GPIO @@ -128,24 +162,32 @@ config E500 select PFLASH_CFI01 select PLATFORM_BUS select PPCE500_PCI - select SERIAL + select SDHCI + select SERIAL_MM select MPC_I2C select FDT_PPC select DS1338 + select UNIMP config E500PLAT bool + default y + depends on PPC && FDT select E500 config MPC8544DS bool + default y + depends on PPC && FDT select E500 config VIRTEX bool + default y + depends on PPC && FDT select PPC4XX select PFLASH_CFI01 - select SERIAL + select SERIAL_MM select XILINX select XILINX_ETHLITE select FDT_PPC @@ -155,6 +197,7 @@ config FW_CFG_PPC bool config FDT_PPC + select DEVICE_TREE bool config VOF diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c new file mode 100644 index 0000000000..900f93c15e --- /dev/null +++ b/hw/ppc/amigaone.c @@ -0,0 +1,178 @@ +/* + * 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_named(DEVICE(via), "intr", 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 2fe496677c..46261223f3 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -24,7 +24,7 @@ #include "net/net.h" #include "qemu/config-file.h" #include "hw/block/flash.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/pci/pci.h" #include "sysemu/block-backend-io.h" #include "sysemu/sysemu.h" @@ -48,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 @@ -66,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) @@ -198,11 +203,29 @@ static void dt_i2c_create(void *fdt, const char *soc, const char *mpic, qemu_fdt_setprop_cells(fdt, i2c, "cell-index", 0); qemu_fdt_setprop_cells(fdt, i2c, "interrupts", irq0, 0x2); qemu_fdt_setprop_phandle(fdt, i2c, "interrupt-parent", mpic); + qemu_fdt_setprop_cell(fdt, i2c, "#size-cells", 0); + qemu_fdt_setprop_cell(fdt, i2c, "#address-cells", 1); qemu_fdt_setprop_string(fdt, "/aliases", alias, i2c); 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; @@ -220,7 +243,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; @@ -352,7 +375,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; @@ -478,7 +501,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); @@ -553,6 +576,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); @@ -618,9 +645,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); @@ -634,9 +660,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); @@ -683,7 +714,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); @@ -692,12 +723,21 @@ 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) +static hwaddr booke206_page_size_to_tlb(uint64_t size) { return 63 - clz64(size / KiB); } +void booke206_set_tlb(ppcmas_tlb_t *tlb, target_ulong va, hwaddr pa, + hwaddr len) +{ + tlb->mas1 = booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT; + tlb->mas1 |= MAS1_VALID; + tlb->mas2 = va & TARGET_PAGE_MASK; + tlb->mas7_3 = pa & TARGET_PAGE_MASK; + tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; +} + static int booke206_initial_map_tsize(CPUPPCState *env) { struct boot_info *bi = env->load_info; @@ -723,22 +763,6 @@ static uint64_t mmubooke_initial_mapsize(CPUPPCState *env) return (1ULL << 10 << tsize); } -static void mmubooke_create_initial_mapping(CPUPPCState *env) -{ - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); - hwaddr size; - int ps; - - ps = booke206_initial_map_tsize(env); - size = (ps << MAS1_TSIZE_SHIFT); - tlb->mas1 = MAS1_VALID | size; - tlb->mas2 = 0; - tlb->mas7_3 = 0; - tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX; - - env->tlb_dirty = true; -} - static void ppce500_cpu_reset_sec(void *opaque) { PowerPCCPU *cpu = opaque; @@ -755,6 +779,8 @@ static void ppce500_cpu_reset(void *opaque) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; struct boot_info *bi = env->load_info; + uint64_t map_size = mmubooke_initial_mapsize(env); + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 0); cpu_reset(cs); @@ -765,11 +791,15 @@ static void ppce500_cpu_reset(void *opaque) env->gpr[4] = 0; env->gpr[5] = 0; env->gpr[6] = EPAPR_MAGIC; - env->gpr[7] = mmubooke_initial_mapsize(env); + env->gpr[7] = map_size; env->gpr[8] = 0; env->gpr[9] = 0; env->nip = bi->entry; - mmubooke_create_initial_mapping(env); + /* create initial mapping */ + booke206_set_tlb(tlb, 0, 0, map_size); +#ifdef CONFIG_KVM + env->tlb_dirty = true; +#endif } static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, @@ -801,8 +831,9 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, } static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, - IrqLines *irqs, Error **errp) + Error **errp) { +#ifdef CONFIG_KVM DeviceState *dev; CPUState *cs; @@ -823,6 +854,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, @@ -837,7 +871,7 @@ static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, Error *err = NULL; if (kvm_kernel_irqchip_allowed()) { - dev = ppce500_init_mpic_kvm(pmc, irqs, &err); + dev = ppce500_init_mpic_kvm(pmc, &err); } if (kvm_kernel_irqchip_required() && !dev) { error_reportf_err(err, @@ -869,6 +903,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; @@ -883,7 +918,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; @@ -919,7 +954,7 @@ 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) { @@ -938,7 +973,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; @@ -959,8 +993,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; @@ -982,16 +1015,41 @@ 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); sysbus_connect_irq(s, 0, qdev_get_gpio_in(mpicdev, MPC8544_I2C_IRQ)); memory_region_add_subregion(ccsr_addr_space, MPC8544_I2C_REGS_OFFSET, sysbus_mmio_get_region(s, 0)); - i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); + i2c = I2C_BUS(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"); @@ -1002,7 +1060,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); @@ -1014,15 +1072,13 @@ void ppce500_init(MachineState *machine) memory_region_add_subregion(ccsr_addr_space, MPC8544_PCI_REGS_OFFSET, sysbus_mmio_get_region(s, 0)); - pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); if (!pci_bus) printf("couldn't create PCI controller!\n"); 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 */ @@ -1217,7 +1273,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 68f754ce50..01db102625 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -27,6 +27,7 @@ struct PPCE500MachineClass { int mpic_version; bool has_mpc8xxx_gpio; + bool has_esdhc; hwaddr platform_bus_base; hwaddr platform_bus_size; int platform_bus_first_irq; @@ -40,8 +41,6 @@ struct PPCE500MachineClass { void ppce500_init(MachineState *machine); -hwaddr booke206_page_size_to_tlb(uint64_t size); - #define TYPE_PPCE500_MACHINE "ppce500-base-machine" OBJECT_DECLARE_TYPE(PPCE500MachineState, PPCE500MachineClass, PPCE500_MACHINE) diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 5bb1c603da..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,6 +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_esdhc = true; pmc->platform_bus_base = 0xf00000000ULL; pmc->platform_bus_size = 128 * MiB; pmc->platform_bus_first_irq = 5; @@ -101,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_newworld.c b/hw/ppc/mac_newworld.c index 601ea518f8..9d249a506c 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -77,7 +77,7 @@ #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) @@ -132,6 +132,7 @@ static void ppc_core99_reset(void *opaque) static void ppc_core99_init(MachineState *machine) { Core99MachineState *core99_machine = CORE99_MACHINE(machine); + MachineClass *mc = MACHINE_GET_CLASS(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; @@ -430,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"); } @@ -443,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) { @@ -466,8 +467,7 @@ static void ppc_core99_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); @@ -571,13 +571,14 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); - mc->desc = "Mac99 based PowerMAC"; + mc->desc = "Mac99 based PowerMac"; mc->init = ppc_core99_init; mc->block_default_type = IF_IDE; /* SMP is not supported currently */ 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 558c639202..eef3261002 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -81,12 +81,14 @@ 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) { const char *bios_name = machine->firmware ?: PROM_FILENAME; + MachineClass *mc = MACHINE_GET_CLASS(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; @@ -102,7 +104,7 @@ static void ppc_heathrow_init(MachineState *machine) DeviceState *dev, *pic_dev, *grackle_dev; BusState *adb_bus; 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 = kvm_enabled() ? kvmppc_get_tbfreq() : TBFREQ; @@ -245,6 +247,12 @@ static void ppc_heathrow_init(MachineState *machine) qdev_prop_set_chr(dev, "chrA", serial_hd(0)); qdev_prop_set_chr(dev, "chrB", serial_hd(1)); + 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)); + } + pci_realize_and_unref(PCI_DEVICE(macio), pci_bus, &error_fatal); pic_dev = DEVICE(object_resolve_path_component(macio, "pic")); @@ -269,9 +277,7 @@ 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)); @@ -303,8 +309,7 @@ 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); @@ -406,7 +411,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); - mc->desc = "Heathrow based PowerMAC"; + mc->desc = "Heathrow based PowerMac"; mc->init = ppc_heathrow_init; mc->block_default_type = IF_IDE; /* SMP is not supported currently */ @@ -419,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 c927337da0..7cd9189869 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -3,9 +3,7 @@ ppc_ss.add(files( 'ppc.c', 'ppc_booke.c', )) -ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: [files( - 'fdt.c', -), fdt]) +ppc_ss.add(when: 'CONFIG_FDT_PPC', if_true: files('fdt.c')) ppc_ss.add(when: 'CONFIG_FW_CFG_PPC', if_true: files('fw_cfg.c')) # IBM pSeries (sPAPR) @@ -15,6 +13,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,26 +29,32 @@ 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_adu.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( @@ -57,10 +62,9 @@ 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_devs.c', - 'ppc4xx_pci.c', 'ppc4xx_sdram.c')) ppc_ss.add(when: 'CONFIG_SAM460EX', if_true: files('sam460ex.c')) # PReP @@ -81,6 +85,8 @@ ppc_ss.add(when: 'CONFIG_E500', if_true: files( )) # 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..e3c51458e6 100644 --- a/hw/ppc/mpc8544_guts.c +++ b/hw/ppc/mpc8544_guts.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "qemu/module.h" #include "qemu/log.h" #include "sysemu/runstate.h" #include "cpu.h" @@ -29,6 +28,12 @@ #define MPC8544_GUTS_RSTCR_RESET 0x02 #define MPC8544_GUTS_ADDR_PORPLLSR 0x00 +REG32(GUTS_PORPLLSR, 0x00) + FIELD(GUTS_PORPLLSR, E500_1_RATIO, 24, 6) + FIELD(GUTS_PORPLLSR, E500_0_RATIO, 16, 6) + FIELD(GUTS_PORPLLSR, DDR_RATIO, 9, 5) + FIELD(GUTS_PORPLLSR, PLAT_RATIO, 1, 5) + #define MPC8544_GUTS_ADDR_PORBMSR 0x04 #define MPC8544_GUTS_ADDR_PORIMPSCR 0x08 #define MPC8544_GUTS_ADDR_PORDEVSR 0x0C @@ -71,11 +76,16 @@ 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) { + case MPC8544_GUTS_ADDR_PORPLLSR: + value = FIELD_DP32(value, GUTS_PORPLLSR, E500_1_RATIO, 6); /* 3:1 */ + value = FIELD_DP32(value, GUTS_PORPLLSR, E500_0_RATIO, 6); /* 3:1 */ + value = FIELD_DP32(value, GUTS_PORPLLSR, DDR_RATIO, 12); /* 12:1 */ + value = FIELD_DP32(value, GUTS_PORPLLSR, PLAT_RATIO, 6); /* 6:1 */ + break; case MPC8544_GUTS_ADDR_PVR: value = env->spr[SPR_PVR]; break; @@ -130,16 +140,13 @@ static void mpc8544_guts_initfn(Object *obj) sysbus_init_mmio(d, &s->iomem); } -static const TypeInfo mpc8544_guts_info = { - .name = TYPE_MPC8544_GUTS, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(GutsState), - .instance_init = mpc8544_guts_initfn, +static const TypeInfo mpc8544_guts_types[] = { + { + .name = TYPE_MPC8544_GUTS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(GutsState), + .instance_init = mpc8544_guts_initfn, + }, }; -static void mpc8544_guts_register_types(void) -{ - type_register_static(&mpc8544_guts_info); -} - -type_init(mpc8544_guts_register_types) +DEFINE_TYPES(mpc8544_guts_types) diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 7dd5219736..b7130903d6 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -61,6 +61,7 @@ static void mpc8544ds_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") diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index cc44d5e339..47553348b1 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -15,7 +15,6 @@ #include "sysemu/kvm.h" #include "migration/blocker.h" #include "exec/confidential-guest-support.h" -#include "hw/ppc/pef.h" #define TYPE_PEF_GUEST "pef-guest" OBJECT_DECLARE_SIMPLE_TYPE(PefGuest, PEF_GUEST) @@ -63,7 +62,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; @@ -93,7 +92,7 @@ static int kvmppc_svm_off(Error **errp) #endif } -int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +static int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) { return 0; @@ -107,7 +106,7 @@ int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return kvmppc_svm_init(cgs, errp); } -int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp) +static int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp) { if (!object_dynamic_cast(OBJECT(cgs), TYPE_PEF_GUEST)) { return 0; @@ -131,6 +130,10 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest, static void pef_guest_class_init(ObjectClass *oc, void *data) { + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = pef_kvm_init; + klass->kvm_reset = pef_kvm_reset; } static void pef_guest_init(Object *obj) diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index f46d4bf51d..16abeaac82 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -10,11 +10,11 @@ #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" #include "hw/irq.h" +#include "hw/or-irq.h" #include "hw/pci-host/mv64361.h" #include "hw/isa/vt82c686.h" #include "hw/ide/pci.h" @@ -45,6 +45,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) @@ -72,13 +74,20 @@ OBJECT_DECLARE_TYPE(Pegasos2MachineState, MachineClass, PEGASOS2_MACHINE) struct Pegasos2MachineState { MachineState parent_obj; + PowerPCCPU *cpu; DeviceState *mv; + IRQState pci_irqs[PCI_NUM_PINS]; + OrIRQState orirq[PCI_NUM_PINS]; + 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 +103,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 +121,13 @@ static void pegasos2_init(MachineState *machine) CPUPPCState *env; MemoryRegion *rom = g_new(MemoryRegion, 1); PCIBus *pci_bus; - PCIDevice *dev, *via; + 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 */ @@ -156,22 +177,34 @@ static void pegasos2_init(MachineState *machine) /* Marvell Discovery II system controller */ pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, 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); /* VIA VT8231 South Bridge (multifunction PCI device) */ - via = pci_create_simple_multifunction(pci_bus, PCI_DEVFN(12, 0), true, - TYPE_VT8231_ISA); - object_property_add_alias(OBJECT(machine), "rtc-time", - object_resolve_path_component(OBJECT(via), - "rtc"), - "date"); - qdev_connect_gpio_out(DEVICE(via), 0, - qdev_get_gpio_in_named(pm->mv, "gpp", 31)); + via = OBJECT(pci_new_multifunction(PCI_DEVFN(12, 0), TYPE_VT8231_ISA)); - dev = PCI_DEVICE(object_resolve_path_component(OBJECT(via), "ide")); + /* 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_named(DEVICE(via), "intr", 0, + qdev_get_gpio_in_named(pm->mv, "gpp", 31)); + + dev = PCI_DEVICE(object_resolve_path_component(via, "ide")); pci_ide_create_devs(dev); - dev = PCI_DEVICE(object_resolve_path_component(OBJECT(via), "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); @@ -179,6 +212,31 @@ static void pegasos2_init(MachineState *machine) /* other PC hardware */ pci_vga_init(pci_bus); + /* PCI interrupt routing: lines from pci.0 and pci.1 are ORed */ + for (int h = 0; h < 2; h++) { + DeviceState *pd; + g_autofree const char *pn = g_strdup_printf("pcihost%d", h); + + pd = DEVICE(object_resolve_path_component(OBJECT(pm->mv), pn)); + assert(pd); + for (i = 0; i < PCI_NUM_PINS; i++) { + OrIRQState *ori = &pm->orirq[i]; + + if (h == 0) { + g_autofree const char *n = g_strdup_printf("pci-orirq[%d]", i); + + object_initialize_child_with_props(OBJECT(pm), n, + ori, sizeof(*ori), + TYPE_OR_IRQ, &error_fatal, + "num-lines", "2", NULL); + qdev_realize(DEVICE(ori), NULL, &error_fatal); + qemu_init_irq(&pm->pci_irqs[i], pegasos2_pci_irq, pm, i); + qdev_connect_gpio_out(DEVICE(ori), 0, &pm->pci_irqs[i]); + } + qdev_connect_gpio_out(pd, i, qdev_get_gpio_in(DEVICE(ori), h)); + } + } + if (machine->kernel_filename) { sz = load_elf(machine->kernel_filename, NULL, NULL, NULL, &pm->kernel_entry, &pm->kernel_addr, NULL, NULL, 1, @@ -196,6 +254,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."); } @@ -241,14 +313,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, ShutdownCause reason) +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, ResetType type) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); void *fdt; uint64_t d[2]; int sz; - qemu_devices_reset(reason); + qemu_devices_reset(type); if (!pm->vof) { return; /* Firmware should set up machine so nothing to do */ } @@ -266,8 +344,20 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) 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); @@ -282,9 +372,13 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) 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); @@ -312,6 +406,11 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) 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); @@ -329,6 +428,7 @@ static void pegasos2_machine_reset(MachineState *machine, ShutdownCause reason) machine->fdt = fdt; pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); + pm->cpu->vhyp_class = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(pm->cpu->vhyp); } enum pegasos2_rtas_tokens { @@ -456,7 +556,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"); @@ -501,9 +601,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; @@ -564,7 +665,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); @@ -688,6 +789,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; @@ -715,11 +823,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); } @@ -874,6 +990,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"); @@ -896,7 +1013,7 @@ static void *build_fdt(MachineState *machine, int *fdt_size) cpu->env.icache_line_size); qemu_fdt_setprop_cell(fdt, cp, "i-cache-line-size", cpu->env.icache_line_size); - if (cpu->env.id_tlbs) { + if (ppc_is_split_tlb(cpu)) { qemu_fdt_setprop_cell(fdt, cp, "i-tlb-sets", cpu->env.nb_ways); qemu_fdt_setprop_cell(fdt, cp, "i-tlb-size", cpu->env.tlb_per_way); qemu_fdt_setprop_cell(fdt, cp, "d-tlb-sets", cpu->env.nb_ways); @@ -943,6 +1060,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 3d01e26f84..f0f0d7567d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -38,20 +38,22 @@ #include "hw/loader.h" #include "hw/nmi.h" #include "qapi/visitor.h" -#include "monitor/monitor.h" #include "hw/intc/intc.h" #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" #include "hw/isa/isa.h" -#include "hw/char/serial.h" +#include "hw/char/serial-isa.h" #include "hw/rtc/mc146818rtc.h" #include @@ -130,7 +132,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); @@ -138,32 +140,34 @@ 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); - g_autofree uint32_t *servers_prop = g_new(uint32_t, smt_threads); + PnvChipClass *pnv_cc = PNV_CHIP_GET_CLASS(chip); + uint32_t *servers_prop; int i; + uint32_t pir, tir; 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); + pnv_cc->get_pir_tir(chip, pc->hwid, 0, &pir, &tir); + + /* Only one DT node per (big) core */ + g_assert(tir == 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]))); @@ -233,33 +237,55 @@ 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); + if (pc->big_core) { + servers_prop = g_new(uint32_t, smt_threads * 2); + for (i = 0; i < smt_threads; i++) { + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i * 2] = cpu_to_be32(pir); + + pnv_cc->get_pir_tir(chip, pc->hwid + 1, i, &pir, NULL); + servers_prop[i * 2 + 1] = cpu_to_be32(pir); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(*servers_prop) * smt_threads + * 2))); + } else { + servers_prop = g_new(uint32_t, smt_threads); + for (i = 0; i < smt_threads; i++) { + pnv_cc->get_pir_tir(chip, pc->hwid, i, &pir, NULL); + servers_prop[i] = cpu_to_be32(pir); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(*servers_prop) * smt_threads))); } - _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", - servers_prop, sizeof(*servers_prop) * smt_threads))); + g_free(servers_prop); + + 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) { - uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + uint32_t pir; + uint64_t addr; char *name; const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; uint32_t irange[2], i, rsize; uint64_t *reg; int offset; + pcc->get_pir_tir(chip, hwid, 0, &pir, NULL); + addr = PNV_ICP_BASE(chip) | (pir << 12); + irange[0] = cpu_to_be32(pir); irange[1] = cpu_to_be32(nr_threads); 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); } @@ -281,74 +307,31 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, g_free(reg); } -static PnvPhb4PecState *pnv_phb4_get_pec(PnvChip *chip, PnvPHB4 *phb, - Error **errp) +/* + * Adds a PnvPHB to the chip on P8. + * Implemented here, like for defaults PHBs + */ +PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb) { - PnvPHB *phb_base = phb->phb_base; - PnvPhb4PecState *pecs = NULL; - int chip_id = phb->chip_id; - int index = phb->phb_id; - int i, j; + Pnv8Chip *chip8 = PNV8_CHIP(chip); - if (phb_base->version == 4) { - Pnv9Chip *chip9 = PNV9_CHIP(chip); + phb->chip = chip; - pecs = chip9->pecs; - } else if (phb_base->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)) { - return pec; - } - } - } - error_setg(errp, - "pnv-phb4 chip-id %d index %d didn't match any existing PEC", - chip_id, index); - - return NULL; + chip8->phbs[chip8->num_phbs] = phb; + chip8->num_phbs++; + return chip; } /* - * Adds a PnvPHB to the chip. Returns the parent obj of the - * PHB which varies with each version (phb version 3 is parented - * by the chip, version 4 and 5 are parented by the PEC - * device). - * - * TODO: for version 3 we're still parenting the PHB with the - * chip. We should parent with a (so far not implemented) - * PHB3 PEC device. + * 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. */ -Object *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp) -{ - if (phb->version == 3) { - Pnv8Chip *chip8 = PNV8_CHIP(chip); - - phb->chip = chip; - - chip8->phbs[chip8->num_phbs] = phb; - chip8->num_phbs++; - - return OBJECT(chip); - } - - phb->pec = pnv_phb4_get_pec(chip, PNV_PHB4(phb->backend), errp); - - return OBJECT(phb->pec); -} +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) { @@ -362,11 +345,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) { @@ -374,6 +361,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"; @@ -386,8 +402,16 @@ 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 (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { @@ -397,6 +421,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"; @@ -409,8 +467,16 @@ 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 (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } } if (chip->ram_size) { @@ -643,13 +709,13 @@ static void pnv_powerdown_notify(Notifier *n, void *opaque) } } -static void pnv_reset(MachineState *machine, ShutdownCause reason) +static void pnv_reset(MachineState *machine, ResetType type) { PnvMachineState *pnv = PNV_MACHINE(machine); IPMIBmc *bmc; void *fdt; - qemu_devices_reset(reason); + qemu_devices_reset(type); /* * The machine should provide by default an internal BMC simulator. @@ -670,21 +736,27 @@ static void pnv_reset(MachineState *machine, ShutdownCause reason) } } - fdt = pnv_dt_create(machine); - - /* Pack resulting tree */ - _FDT((fdt_pack(fdt))); + if (machine->fdt) { + fdt = machine->fdt; + } else { + fdt = pnv_dt_create(machine); + /* Pack resulting tree */ + _FDT((fdt_pack(fdt))); + } qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(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; + /* Update machine->fdt with latest fdt */ + if (machine->fdt != 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) @@ -692,7 +764,8 @@ static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(chip); qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_EXTERNAL); - qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); + qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq); + return pnv_lpc_isa_create(&chip8->lpc, true, errp); } @@ -701,25 +774,48 @@ static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) Pnv8Chip *chip8 = PNV8_CHIP(chip); qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip8->psi), PSIHB_IRQ_LPC_I2C); - qdev_connect_gpio_out(DEVICE(&chip8->lpc), 0, irq); + qdev_connect_gpio_out_named(DEVICE(&chip8->lpc), "LPCHC", 0, irq); + return pnv_lpc_isa_create(&chip8->lpc, false, errp); } static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) { Pnv9Chip *chip9 = PNV9_CHIP(chip); - qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip9->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip9->lpc), "SERIRQ", 3, irq); - qdev_connect_gpio_out(DEVICE(&chip9->lpc), 0, irq); return pnv_lpc_isa_create(&chip9->lpc, false, errp); } static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) { Pnv10Chip *chip10 = PNV10_CHIP(chip); - qemu_irq irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip10->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip10->lpc), "SERIRQ", 3, irq); - qdev_connect_gpio_out(DEVICE(&chip10->lpc), 0, irq); return pnv_lpc_isa_create(&chip10->lpc, false, errp); } @@ -728,45 +824,44 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } -static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) +static void pnv_chip_power8_pic_print_info(PnvChip *chip, GString *buf) { Pnv8Chip *chip8 = PNV8_CHIP(chip); int i; - ics_pic_print_info(&chip8->psi.ics, mon); + ics_pic_print_info(&chip8->psi.ics, buf); 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); + pnv_phb3_msi_pic_print_info(&phb3->msis, buf); + ics_pic_print_info(&phb3->lsis, buf); } } static int pnv_chip_power9_pic_print_info_child(Object *child, void *opaque) { - Monitor *mon = opaque; + GString *buf = opaque; PnvPHB *phb = (PnvPHB *) object_dynamic_cast(child, TYPE_PNV_PHB); if (!phb) { return 0; } - pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), mon); + pnv_phb4_pic_print_info(PNV_PHB4(phb->backend), buf); return 0; } -static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) +static void pnv_chip_power9_pic_print_info(PnvChip *chip, GString *buf) { Pnv9Chip *chip9 = PNV9_CHIP(chip); - pnv_xive_pic_print_info(&chip9->xive, mon); - pnv_psi_pic_print_info(&chip9->psi, mon); - + pnv_xive_pic_print_info(&chip9->xive, buf); + pnv_psi_pic_print_info(&chip9->psi, buf); object_child_foreach_recursive(OBJECT(chip), - pnv_chip_power9_pic_print_info_child, mon); + pnv_chip_power9_pic_print_info_child, buf); } static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip, @@ -806,15 +901,14 @@ static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq) isa_realize_and_unref(dev, bus, &error_fatal); } -static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) +static void pnv_chip_power10_pic_print_info(PnvChip *chip, GString *buf) { Pnv10Chip *chip10 = PNV10_CHIP(chip); - pnv_xive2_pic_print_info(&chip10->xive, mon); - pnv_psi_pic_print_info(&chip10->psi, mon); - + pnv_xive2_pic_print_info(&chip10->xive, buf); + pnv_psi_pic_print_info(&chip10->psi, buf); object_child_foreach_recursive(OBJECT(chip), - pnv_chip_power9_pic_print_info_child, mon); + pnv_chip_power9_pic_print_info_child, buf); } /* Always give the first 1GB to chip 0 else we won't boot */ @@ -841,6 +935,8 @@ 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); + int max_smt_threads = pmc->max_smt_threads; char *fw_filename; long fw_size; uint64_t chip_ram_start = 0; @@ -850,7 +946,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); } @@ -861,6 +958,14 @@ static void pnv_init(MachineState *machine) g_free(sz); exit(EXIT_FAILURE); } + + /* checks for invalid option combinations */ + if (machine->dtb && (strlen(machine->kernel_cmdline) != 0)) { + error_report("-append and -dtb cannot be used together, as passed" + " command line is ignored in case of custom dtb"); + exit(EXIT_FAILURE); + } + memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* @@ -912,6 +1017,21 @@ static void pnv_init(MachineState *machine) } } + /* load dtb if passed */ + if (machine->dtb) { + int fdt_size; + + warn_report("with manually passed dtb, some options like '-append'" + " will get ignored and the dtb passed will be used as-is"); + + /* read the file 'machine->dtb', and load it into 'fdt' buffer */ + machine->fdt = load_device_tree(machine->dtb, &fdt_size); + if (!machine->fdt) { + error_report("Could not load dtb '%s'", machine->dtb); + exit(1); + } + } + /* MSIs are supported on this platform */ msi_nonbroken = true; @@ -935,8 +1055,52 @@ static void pnv_init(MachineState *machine) exit(1); } + /* Set lpar-per-core mode if lpar-per-thread is not supported */ + if (!pmc->has_lpar_per_thread) { + pnv->lpar_per_core = true; + } + pnv->num_chips = machine->smp.max_cpus / (machine->smp.cores * machine->smp.threads); + + if (pnv->big_core) { + if (machine->smp.threads % 2 == 1) { + error_report("Cannot support %d threads with big-core option " + "because it must be an even number", + machine->smp.threads); + exit(1); + } + max_smt_threads *= 2; + } + + if (machine->smp.threads > max_smt_threads) { + error_report("Cannot support more than %d threads/core " + "on %s machine", max_smt_threads, mc->desc); + if (pmc->max_smt_threads == 4) { + error_report("(use big-core=on for 8 threads per core)"); + } + exit(1); + } + + if (pnv->big_core) { + /* + * powernv models PnvCore as a SMT4 core. Big-core requires 2xPnvCore + * per core, so adjust topology here. pnv_dt_core() processor + * device-tree and TCG SMT code make the 2 cores appear as one big core + * from software point of view. pnv pervasive models and xscoms tend to + * see the big core as 2 small core halves. + */ + machine->smp.cores *= 2; + machine->smp.threads /= 2; + } + + 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..., @@ -970,6 +1134,10 @@ static void pnv_init(MachineState *machine) &error_fatal); object_property_set_int(chip, "nr-threads", machine->smp.threads, &error_fatal); + object_property_set_bool(chip, "big-core", pnv->big_core, + &error_fatal); + object_property_set_bool(chip, "lpar-per-core", pnv->lpar_per_core, + &error_fatal); /* * The POWER8 machine use the XICS interrupt interface. * Propagate the XICS fabric to the chip and its controllers. @@ -1017,6 +1185,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); + } } /* @@ -1025,9 +1200,16 @@ 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 void pnv_get_pir_tir_p8(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - return (chip->chip_id << 7) | (core_id << 3); + if (pir) { + *pir = (chip->chip_id << 7) | (core_id << 3) | thread_id; + } + if (tir) { + *tir = thread_id; + } } static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1064,9 +1246,9 @@ static void pnv_chip_power8_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) } static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, - Monitor *mon) + GString *buf) { - icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); + icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), buf); } /* @@ -1079,14 +1261,61 @@ 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 void pnv_get_pir_tir_p9(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - return (chip->chip_id << 8) | (core_id << 2); + if (chip->big_core) { + /* Big-core interleaves thread ID between small-cores */ + thread_id <<= 1; + thread_id |= core_id & 1; + core_id >>= 1; + + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 3) | thread_id; + } + } else { + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; + } + } + if (tir) { + *tir = 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 void pnv_get_pir_tir_p10(PnvChip *chip, + uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir) { - return (chip->chip_id << 8) | (core_id << 2); + if (chip->big_core) { + /* Big-core interleaves thread ID between small-cores */ + thread_id <<= 1; + thread_id |= core_id & 1; + core_id >>= 1; + + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 3) | thread_id; + } + } else { + if (pir) { + *pir = (chip->chip_id << 8) | (core_id << 2) | thread_id; + } + } + if (tir) { + *tir = thread_id; + } } static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1128,9 +1357,9 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) } static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, - Monitor *mon) + GString *buf) { - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); } static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu, @@ -1172,9 +1401,9 @@ static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) } static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, - Monitor *mon) + GString *buf) { - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); } /* @@ -1255,10 +1484,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++) { @@ -1266,8 +1494,11 @@ 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; - PnvICPState *icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); + uint32_t pir; + PnvICPState *icp; + + pcc->get_pir_tir(chip, core_hwid, j, &pir, NULL); + icp = PNV_ICP(xics_icp_get(chip8->xics, pir)); memory_region_add_subregion(&chip8->icp_mmio, pir << 12, &icp->mmio); @@ -1287,12 +1518,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) { @@ -1301,11 +1527,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, @@ -1336,7 +1562,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), @@ -1384,7 +1610,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->get_pir_tir = pnv_get_pir_tir_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; @@ -1408,7 +1634,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->get_pir_tir = pnv_get_pir_tir_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; @@ -1432,7 +1658,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->get_pir_tir = pnv_get_pir_tir_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; @@ -1455,6 +1681,7 @@ static void pnv_chip_power9_instance_init(Object *obj) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; + object_initialize_child(obj, "adu", &chip9->adu, TYPE_PNV_ADU); object_initialize_child(obj, "xive", &chip9->xive, TYPE_PNV_XIVE); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip9->xive), "xive-fabric"); @@ -1463,6 +1690,8 @@ 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); @@ -1476,17 +1705,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); @@ -1504,7 +1738,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); @@ -1546,14 +1781,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) { @@ -1561,6 +1792,15 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) return; } + /* ADU */ + object_property_set_link(OBJECT(&chip9->adu), "lpc", OBJECT(&chip9->lpc), + &error_abort); + if (!qdev_realize(DEVICE(&chip9->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_ADU_BASE, + &chip9->adu.xscom_regs); + pnv_chip_quad_realize(chip9, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1585,12 +1825,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, @@ -1607,13 +1847,26 @@ 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), @@ -1628,7 +1881,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) 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(&chip9->psi), PSIHB9_IRQ_PSU)); + DEVICE(psi9), PSIHB9_IRQ_PSU)); /* HOMER */ object_property_set_link(OBJECT(&chip9->homer), "chip", OBJECT(chip), @@ -1649,6 +1902,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) @@ -1661,10 +1937,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->get_pir_tir = pnv_get_pir_tir_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; @@ -1676,6 +1953,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); @@ -1688,14 +1967,19 @@ static void pnv_chip_power10_instance_init(Object *obj) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); int i; + object_initialize_child(obj, "adu", &chip10->adu, TYPE_PNV_ADU); object_initialize_child(obj, "xive", &chip10->xive, TYPE_PNV_XIVE2); object_property_add_alias(obj, "xive-fabric", OBJECT(&chip10->xive), "xive-fabric"); object_initialize_child(obj, "psi", &chip10->psi, TYPE_PNV10_PSI); object_initialize_child(obj, "lpc", &chip10->lpc, TYPE_PNV10_LPC); + object_initialize_child(obj, "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; @@ -1703,6 +1987,15 @@ 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); + } + + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_initialize_child(obj, "pib_spic[*]", &chip10->pib_spic[i], + TYPE_PNV_SPI); + } } static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) @@ -1716,10 +2009,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); } } @@ -1757,14 +2054,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) { @@ -1772,6 +2065,15 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + /* ADU */ + object_property_set_link(OBJECT(&chip10->adu), "lpc", OBJECT(&chip10->lpc), + &error_abort); + if (!qdev_realize(DEVICE(&chip10->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_ADU_BASE, + &chip10->adu.xscom_regs); + pnv_chip_power10_quad_realize(chip10, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1822,6 +2124,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; @@ -1861,12 +2176,96 @@ 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)); + } + /* PIB SPI Controller */ + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "spic_num", + i, &error_fatal); + /* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */ + object_property_set_int(OBJECT(&chip10->pib_spic[i]), "transfer_len", + (i == 2) ? 1 : 4, &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT + (&chip10->pib_spic[i])), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PIB_SPIC_BASE + + i * PNV10_XSCOM_PIB_SPIC_SIZE, + &chip10->pib_spic[i].xscom_spic_regs); + } +} + +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) @@ -1879,10 +2278,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->chip_cfam_id = 0x220da04980000000ull; /* P10 DD2.0 (with NX) */ k->cores_mask = POWER10_CORE_MASK; - k->core_pir = pnv_chip_core_pir_p10; + k->get_pir_tir = pnv_get_pir_tir_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; @@ -1894,12 +2294,15 @@ 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); } -static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) +static void pnv_chip_core_sanitize(PnvMachineState *pnv, PnvChip *chip, + Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); int cores_max; @@ -1920,6 +2323,17 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) } chip->cores_mask &= pcc->cores_mask; + /* Ensure small-cores a paired up in big-core mode */ + if (pnv->big_core) { + uint64_t even_cores = chip->cores_mask & 0x5555555555555555ULL; + uint64_t odd_cores = chip->cores_mask & 0xaaaaaaaaaaaaaaaaULL; + + if (even_cores ^ (odd_cores >> 1)) { + error_setg(errp, "warning: unpaired cores in big-core mode !"); + return; + } + } + /* now that we have a sane layout, let check the number of cores */ cores_max = ctpop64(chip->cores_mask); if (chip->nr_cores > cores_max) { @@ -1931,11 +2345,12 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(pnv); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); int i, core_hwid; - PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); if (!object_class_by_name(typename)) { error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename); @@ -1943,7 +2358,7 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) } /* Cores */ - pnv_chip_core_sanitize(chip, &error); + pnv_chip_core_sanitize(pnv, chip, &error); if (error) { error_propagate(errp, error); return; @@ -1970,12 +2385,19 @@ 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_bool(OBJECT(pnv_core), "big-core", chip->big_core, + &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "quirk-tb-big-core", + pmc->quirk_tb_big_core, &error_fatal); + object_property_set_bool(OBJECT(pnv_core), "lpar-per-core", + chip->lpar_per_core, &error_fatal); object_property_set_link(OBJECT(pnv_core), "chip", OBJECT(chip), &error_abort); + qdev_realize(DEVICE(pnv_core), NULL, &error_fatal); /* Each core has an XSCOM MMIO region */ @@ -2007,6 +2429,8 @@ static Property pnv_chip_properties[] = { DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), DEFINE_PROP_UINT32("nr-threads", PnvChip, nr_threads, 1), + DEFINE_PROP_BOOL("big-core", PnvChip, big_core, false), + DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), DEFINE_PROP_END_OF_LIST(), }; @@ -2020,6 +2444,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; @@ -2037,6 +2476,21 @@ PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) return NULL; } +static void pnv_chip_foreach_cpu(PnvChip *chip, + void (*fn)(PnvChip *chip, PowerPCCPU *cpu, void *opaque), + void *opaque) +{ + int i, j; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + + for (j = 0; j < CPU_CORE(pc)->nr_threads; j++) { + fn(chip, pc->threads[j], opaque); + } + } +} + static ICSState *pnv_ics_get(XICSFabric *xi, int irq) { PnvMachineState *pnv = PNV_MACHINE(xi); @@ -2105,23 +2559,25 @@ static ICPState *pnv_icp_get(XICSFabric *xi, int pir) return cpu ? ICP(pnv_cpu_state(cpu)->intc) : NULL; } -static void pnv_pic_print_info(InterruptStatsProvider *obj, - Monitor *mon) +static void pnv_pic_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + void *opaque) +{ + PNV_CHIP_GET_CLASS(chip)->intc_print_info(chip, cpu, opaque); +} + +static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) { PnvMachineState *pnv = PNV_MACHINE(obj); int i; - CPUState *cs; - - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - - /* XXX: loop on each chip/core/thread instead of CPU_FOREACH() */ - PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu, - mon); - } for (i = 0; i < pnv->num_chips; i++) { - PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], mon); + PnvChip *chip = pnv->chips[i]; + + /* First CPU presenters */ + pnv_chip_foreach_cpu(chip, pnv_pic_intc_print_info, buf); + + /* Then other devices, PHB, PSI, XIVE */ + PNV_CHIP_GET_CLASS(chip)->pic_print_info(chip, buf); } } @@ -2183,6 +2639,46 @@ static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, return total_count; } +static bool pnv_machine_get_big_core(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + return pnv->big_core; +} + +static void pnv_machine_set_big_core(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + pnv->big_core = value; +} + +static bool pnv_machine_get_lpar_per_core(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + return pnv->lpar_per_core; +} + +static void pnv_machine_set_lpar_per_core(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + pnv->lpar_per_core = value; +} + +static bool pnv_machine_get_hb(Object *obj, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + return !!pnv->fw_load_addr; +} + +static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) +{ + PnvMachineState *pnv = PNV_MACHINE(obj); + + if (value) { + pnv->fw_load_addr = 0x8000000; + } +} + static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -2205,6 +2701,9 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 8; + /* POWER8 is always lpar-per-core mode */ + pmc->has_lpar_per_thread = false; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } @@ -2222,21 +2721,33 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) }; mc->desc = "IBM PowerNV (Non-Virtualized) POWER9"; - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0"); + 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; - mc->alias = "powernv"; - pmc->compat = compat; pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; pmc->dt_power_mgt = pnv_dt_power_mgt; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); + + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); } -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); @@ -2248,12 +2759,16 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) { TYPE_PNV_PHB_ROOT_PORT, "version", "5" }, }; - mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; 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->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; + pmc->quirk_tb_big_core = true; pmc->dt_power_mgt = pnv_dt_power_mgt; xfc->match_nvt = pnv10_xive_match_nvt; @@ -2261,26 +2776,44 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } -static bool pnv_machine_get_hb(Object *obj, Error **errp) +static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) { - PnvMachineState *pnv = PNV_MACHINE(obj); + MachineClass *mc = MACHINE_CLASS(oc); - return !!pnv->fw_load_addr; + pnv_machine_p10_common_class_init(oc, data); + mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; + + /* + * This is the parent of POWER10 Rainier class, so properies go here + * rather than common init (which would add them to both parent and + * child which is invalid). + */ + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); } -static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) +static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) { - PnvMachineState *pnv = PNV_MACHINE(obj); + MachineClass *mc = MACHINE_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); - if (value) { - pnv->fw_load_addr = 0x8000000; - } + pnv_machine_p10_common_class_init(oc, data); + mc->desc = "IBM PowerNV (Non-Virtualized) POWER10 Rainier"; + pmc->i2c_init = pnv_rainier_i2c_init; } 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); @@ -2304,14 +2837,32 @@ static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) */ env->spr[SPR_SRR1] |= SRR1_WAKESCOM; } + if (arg.host_int == 1) { + cpu_resume(cs); + } +} + +/* + * Send a SRESET (NMI) interrupt to the CPU, and resume execution if it was + * paused. + */ +void pnv_cpu_do_nmi_resume(CPUState *cs) +{ + async_run_on_cpu(cs, pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(1)); +} + +static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) +{ + async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(0)); } static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) { - CPUState *cs; + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; - CPU_FOREACH(cs) { - async_run_on_cpu(cs, pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_NULL); + for (i = 0; i < pnv->num_chips; i++) { + pnv_chip_foreach_cpu(pnv->chips[i], pnv_cpu_do_nmi, NULL); } } @@ -2366,6 +2917,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_adu.c b/hw/ppc/pnv_adu.c new file mode 100644 index 0000000000..f636dedf79 --- /dev/null +++ b/hw/ppc/pnv_adu.c @@ -0,0 +1,218 @@ +/* + * QEMU PowerPC PowerNV ADU unit + * + * The ADU unit actually implements XSCOM, which is the bridge between MMIO + * and PIB. However it also includes control and status registers and other + * functions that are exposed as PIB (xscom) registers. + * + * To keep things simple, pnv_xscom.c remains the XSCOM bridge + * implementation, and pnv_adu.c implements the ADU registers and other + * functions. + * + * Copyright (c) 2024, 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_adu.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_xscom.h" +#include "trace.h" + +#define ADU_LPC_BASE_REG 0x40 +#define ADU_LPC_CMD_REG 0x41 +#define ADU_LPC_DATA_REG 0x42 +#define ADU_LPC_STATUS_REG 0x43 + +static uint64_t pnv_adu_xscom_read(void *opaque, hwaddr addr, unsigned width) +{ + PnvADU *adu = PNV_ADU(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case 0x18: /* Receive status reg */ + case 0x12: /* log register */ + case 0x13: /* error register */ + break; + case ADU_LPC_BASE_REG: + /* + * LPC Address Map in Pervasive ADU Workbook + * + * return PNV10_LPCM_BASE(chip) & PPC_BITMASK(8, 31); + * XXX: implement as class property, or get from LPC? + */ + qemu_log_mask(LOG_UNIMP, "ADU: LPC_BASE_REG is not implemented\n"); + break; + case ADU_LPC_CMD_REG: + val = adu->lpc_cmd_reg; + break; + case ADU_LPC_DATA_REG: + val = adu->lpc_data_reg; + break; + case ADU_LPC_STATUS_REG: + val = PPC_BIT(0); /* ack / done */ + break; + + default: + qemu_log_mask(LOG_UNIMP, "ADU Unimplemented read register: Ox%08x\n", + offset); + } + + trace_pnv_adu_xscom_read(addr, val); + + return val; +} + +static bool lpc_cmd_read(PnvADU *adu) +{ + return !!(adu->lpc_cmd_reg & PPC_BIT(0)); +} + +static bool lpc_cmd_write(PnvADU *adu) +{ + return !lpc_cmd_read(adu); +} + +static uint32_t lpc_cmd_addr(PnvADU *adu) +{ + return (adu->lpc_cmd_reg & PPC_BITMASK(32, 63)) >> PPC_BIT_NR(63); +} + +static uint32_t lpc_cmd_size(PnvADU *adu) +{ + return (adu->lpc_cmd_reg & PPC_BITMASK(5, 11)) >> PPC_BIT_NR(11); +} + +static void pnv_adu_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + PnvADU *adu = PNV_ADU(opaque); + uint32_t offset = addr >> 3; + + trace_pnv_adu_xscom_write(addr, val); + + switch (offset) { + case 0x18: /* Receive status reg */ + case 0x12: /* log register */ + case 0x13: /* error register */ + break; + + case ADU_LPC_BASE_REG: + qemu_log_mask(LOG_UNIMP, + "ADU: Changing LPC_BASE_REG is not implemented\n"); + break; + + case ADU_LPC_CMD_REG: + adu->lpc_cmd_reg = val; + if (lpc_cmd_read(adu)) { + uint32_t lpc_addr = lpc_cmd_addr(adu); + uint32_t lpc_size = lpc_cmd_size(adu); + uint64_t data = 0; + + if (!is_power_of_2(lpc_size) || lpc_size > sizeof(data)) { + qemu_log_mask(LOG_GUEST_ERROR, "ADU: Unsupported LPC access " + "size:%" PRId32 "\n", lpc_size); + break; + } + + pnv_lpc_opb_read(adu->lpc, lpc_addr, (void *)&data, lpc_size); + + /* + * ADU access is performed within 8-byte aligned sectors. Smaller + * access sizes don't get formatted to the least significant byte, + * but rather appear in the data reg at the same offset as the + * address in memory. This shifts them into that position. + */ + adu->lpc_data_reg = be64_to_cpu(data) >> ((lpc_addr & 7) * 8); + } + break; + + case ADU_LPC_DATA_REG: + adu->lpc_data_reg = val; + if (lpc_cmd_write(adu)) { + uint32_t lpc_addr = lpc_cmd_addr(adu); + uint32_t lpc_size = lpc_cmd_size(adu); + uint64_t data; + + if (!is_power_of_2(lpc_size) || lpc_size > sizeof(data)) { + qemu_log_mask(LOG_GUEST_ERROR, "ADU: Unsupported LPC access " + "size:%" PRId32 "\n", lpc_size); + break; + } + + data = cpu_to_be64(val) >> ((lpc_addr & 7) * 8); /* See above */ + pnv_lpc_opb_write(adu->lpc, lpc_addr, (void *)&data, lpc_size); + } + break; + + case ADU_LPC_STATUS_REG: + qemu_log_mask(LOG_UNIMP, + "ADU: Changing LPC_STATUS_REG is not implemented\n"); + break; + + default: + qemu_log_mask(LOG_UNIMP, "ADU Unimplemented write register: Ox%08x\n", + offset); + } +} + +const MemoryRegionOps pnv_adu_xscom_ops = { + .read = pnv_adu_xscom_read, + .write = pnv_adu_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_adu_realize(DeviceState *dev, Error **errp) +{ + PnvADU *adu = PNV_ADU(dev); + + assert(adu->lpc); + + /* XScom regions for ADU registers */ + pnv_xscom_region_init(&adu->xscom_regs, OBJECT(dev), + &pnv_adu_xscom_ops, adu, "xscom-adu", + PNV9_XSCOM_ADU_SIZE); +} + +static Property pnv_adu_properties[] = { + DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_adu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_adu_realize; + dc->desc = "PowerNV ADU"; + device_class_set_props(dc, pnv_adu_properties); + dc->user_creatable = false; +} + +static const TypeInfo pnv_adu_type_info = { + .name = TYPE_PNV_ADU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvADU), + .class_init = pnv_adu_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } }, +}; + +static void pnv_adu_register_types(void) +{ + type_register_static(&pnv_adu_type_info); +} + +type_init(pnv_adu_register_types); 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..1e41fe557a --- /dev/null +++ b/hw/ppc/pnv_chiptod.c @@ -0,0 +1,585 @@ +/* + * 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 { + PnvCore *pc = chiptod->slave_pc_target; + + /* + * 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 (pc->tod_state.tb_ready_for_tod) { + pc->tod_state.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 9ee79192dd..e6b02294b1 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" @@ -57,9 +58,15 @@ static void pnv_core_cpu_reset(PnvCore *pc, PowerPCCPU *cpu) env->nip = 0x10; env->msr |= MSR_HVB; /* Hypervisor mode */ env->spr[SPR_HRMOR] = pc->hrmor; + if (pc->big_core) { + /* Clear "small core" bit on Power9/10 (this is set in default PVR) */ + env->spr[SPR_PVR] &= ~PPC_BIT(51); + } hreg_compute_hflags(env); ppc_maybe_interrupt(env); + cpu_ppc_tb_reset(env); + pcc->intc_reset(pc->chip, cpu); } @@ -84,8 +91,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; @@ -94,8 +101,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 = { @@ -115,6 +124,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) { @@ -133,9 +144,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; @@ -151,8 +165,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); } } @@ -166,12 +180,129 @@ 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 +#define PNV10_XSCOM_EC_CORE_THREAD_INFO 0x413 +#define PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS 0x449 +#define PNV10_XSCOM_EC_CORE_RAS_STATUS 0x454 + +static uint64_t pnv_core_power10_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + PnvCore *pc = PNV_CORE(opaque); + int nr_threads = CPU_CORE(pc)->nr_threads; + int i; + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case PNV10_XSCOM_EC_CORE_THREAD_STATE: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + + if (cs->halted) { + val |= PPC_BIT(56 + i); + } + } + if (pc->lpar_per_core) { + val |= PPC_BIT(62); + } + break; + case PNV10_XSCOM_EC_CORE_THREAD_INFO: + break; + case PNV10_XSCOM_EC_CORE_RAS_STATUS: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUPPCState *env = &cpu->env; + if (env->quiesced) { + val |= PPC_BIT(0 + 8 * i) | PPC_BIT(1 + 8 * i); + } + } + 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) +{ + PnvCore *pc = PNV_CORE(opaque); + int nr_threads = CPU_CORE(pc)->nr_threads; + int i; + uint32_t offset = addr >> 3; + + switch (offset) { + case PNV10_XSCOM_EC_CORE_DIRECT_CONTROLS: + for (i = 0; i < nr_threads; i++) { + PowerPCCPU *cpu = pc->threads[i]; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + if (val & PPC_BIT(7 + 8 * i)) { /* stop */ + val &= ~PPC_BIT(7 + 8 * i); + cpu_pause(cs); + env->quiesced = true; + } + if (val & PPC_BIT(6 + 8 * i)) { /* start */ + val &= ~PPC_BIT(6 + 8 * i); + env->quiesced = false; + cpu_resume(cs); + } + if (val & PPC_BIT(4 + 8 * i)) { /* sreset */ + val &= ~PPC_BIT(4 + 8 * i); + env->quiesced = false; + pnv_cpu_do_nmi_resume(cs); + } + if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */ + env->quiesced = false; + /* + * Hardware has very particular cases for where clear maint + * must be used and where start must be used to resume a + * thread. These are not modelled exactly, just treat + * this and start the same. + */ + val &= ~PPC_BIT(3 + 8 * i); + cpu_resume(cs); + } + } + if (val) { + qemu_log_mask(LOG_UNIMP, "%s: unimp bits in DIRECT_CONTROLS " + "0x%016" PRIx64 "\n", __func__, val); + } + break; + + 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 */ - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; + int core_hwid; + ppc_spr_t *pir_spr = &env->spr_cb[SPR_PIR]; + ppc_spr_t *tir_spr = &env->spr_cb[SPR_TIR]; + uint32_t pir, tir; Error *local_err = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(pc->chip); @@ -185,14 +316,24 @@ 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; + pcc->get_pir_tir(pc->chip, core_hwid, thread_index, &pir, &tir); + pir_spr->default_value = pir; + tir_spr->default_value = tir; + + env->chip_index = pc->chip->chip_id; + + if (pc->big_core) { + /* 2 "small cores" get the same core index for SMT operations */ + env->core_index = core_hwid >> 1; + } else { + env->core_index = core_hwid; + } + + if (pc->lpar_per_core) { + cpu_ppc_set_1lpar(cpu); + } /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); @@ -225,31 +366,36 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { PowerPCCPU *cpu; + PnvCPUState *pnv_cpu; obj = object_new(typename); cpu = POWERPC_CPU(obj); pc->threads[i] = POWERPC_CPU(obj); + if (cc->nr_threads > 1) { + cpu->env.has_smt_siblings = true; + } snprintf(name, sizeof(name), "thread[%d]", i); object_property_add_child(OBJECT(pc), name, obj); cpu->machine_data = g_new0(PnvCPUState, 1); + pnv_cpu = pnv_cpu_state(cpu); + pnv_cpu->pnv_core = pc; object_unref(obj); } 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; @@ -290,8 +436,12 @@ 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_BOOL("big-core", PnvCore, big_core, false), + DEFINE_PROP_BOOL("quirk-tb-big-core", PnvCore, tod_state.big_core_quirk, + false), + DEFINE_PROP_BOOL("lpar-per-core", PnvCore, lpar_per_core, false), DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; @@ -301,6 +451,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) @@ -308,14 +459,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) @@ -347,7 +499,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"), }; @@ -359,8 +511,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; @@ -371,15 +523,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; @@ -388,14 +540,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, @@ -403,14 +555,142 @@ 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) +{ + PnvQuad *eq = PNV_QUAD(opaque); + 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 & ~PPC_BITMASK32(16, 19)) { + case P10_QME_SSH_HYP: + val = 0; + if (eq->special_wakeup_done) { + val |= PPC_BIT(1); /* SPWU DONE */ + val |= PPC_BIT(4); /* SSH SPWU DONE */ + } + break; + 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) +{ + PnvQuad *eq = PNV_QUAD(opaque); + uint32_t offset = addr >> 3; + bool set; + int i; + + switch (offset & ~PPC_BITMASK32(16, 19)) { + case P10_QME_SPWU_HYP: + set = !!(val & PPC_BIT(0)); + eq->special_wakeup_done = set; + for (i = 0; i < 4; i++) { + /* These bits select cores in the quad */ + if (offset & PPC_BIT32(16 + i)) { + eq->special_wakeup[i] = set; + } + } + break; + 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[] = { @@ -418,25 +698,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..8c203d2059 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" @@ -63,6 +64,7 @@ enum { #define LPC_HC_IRQSER_START_4CLK 0x00000000 #define LPC_HC_IRQSER_START_6CLK 0x01000000 #define LPC_HC_IRQSER_START_8CLK 0x02000000 +#define LPC_HC_IRQSER_AUTO_CLEAR 0x00800000 #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ #define LPC_HC_IRQSTAT 0x38 #define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ @@ -234,16 +236,16 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, * TODO: rework to use address_space_stq() and address_space_ldq() * instead. */ -static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, - int sz) +bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz) { /* XXX Handle access size limits and FW read caching here */ return !address_space_read(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, data, sz); } -static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, - int sz) +bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz) { /* XXX Handle access size limits here */ return !address_space_write(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, @@ -275,7 +277,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) } if (cmd & ECCB_CTL_READ) { - success = opb_read(lpc, opb_addr, data, sz); + success = pnv_lpc_opb_read(lpc, opb_addr, data, sz); if (success) { lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | (((uint64_t)data[0]) << 24 | @@ -292,7 +294,7 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) data[2] = lpc->eccb_data_reg >> 8; data[3] = lpc->eccb_data_reg; - success = opb_write(lpc, opb_addr, data, sz); + success = pnv_lpc_opb_write(lpc, opb_addr, data, sz); lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; } /* XXX Which error bit (if any) to signal OPB error ? */ @@ -419,32 +421,96 @@ static const MemoryRegionOps pnv_lpc_mmio_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +/* Program the POWER9 LPC irq to PSI serirq routing table */ +static void pnv_lpc_eval_serirq_routes(PnvLpcController *lpc) +{ + int irq; + + if (!lpc->psi_has_serirq) { + if ((lpc->opb_irq_route0 & PPC_BITMASK32(8, 13)) || + (lpc->opb_irq_route1 & PPC_BITMASK32(4, 31))) { + qemu_log_mask(LOG_GUEST_ERROR, + "OPB: setting serirq routing on POWER8 system, ignoring.\n"); + } + return; + } + + /* + * Each of the ISA irqs is routed to one of the 4 SERIRQ irqs with 2 + * bits, split across 2 OPB registers. + */ + for (irq = 0; irq <= 13; irq++) { + int serirq = extract32(lpc->opb_irq_route1, + PPC_BIT32_NR(5 + irq * 2), 2); + lpc->irq_to_serirq_route[irq] = serirq; + } + + for (irq = 14; irq < ISA_NUM_IRQS; irq++) { + int serirq = extract32(lpc->opb_irq_route0, + PPC_BIT32_NR(9 + (irq - 14) * 2), 2); + lpc->irq_to_serirq_route[irq] = serirq; + } +} + static void pnv_lpc_eval_irqs(PnvLpcController *lpc) { - bool lpc_to_opb_irq = false; + uint32_t active_irqs = 0; + + if (lpc->lpc_hc_irqstat & PPC_BITMASK32(16, 31)) { + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented irqs in IRQSTAT: " + "0x%08"PRIx32"\n", lpc->lpc_hc_irqstat); + } - /* Update LPC controller to OPB line */ if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { - uint32_t irqs; - - irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; - lpc_to_opb_irq = (irqs != 0); + active_irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; } - /* We don't honor the polarity register, it's pointless and unused - * anyway - */ - if (lpc_to_opb_irq) { - lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; - } else { - lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; - } - - /* Update OPB internal latch */ - lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; - /* Reflect the interrupt */ - qemu_set_irq(lpc->psi_irq, lpc->opb_irq_stat != 0); + if (!lpc->psi_has_serirq) { + /* + * POWER8 ORs all irqs together (also with LPCHC internal interrupt + * sources) and outputs a single line that raises the PSI LPCHC irq + * which then latches an OPB IRQ status register that sends the irq + * to PSI. + * + * We don't honor the polarity register, it's pointless and unused + * anyway + */ + if (active_irqs) { + lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; + } else { + lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; + } + + /* Update OPB internal latch */ + lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; + + qemu_set_irq(lpc->psi_irq_lpchc, lpc->opb_irq_stat != 0); + } else { + /* + * POWER9 and POWER10 have routing fields in OPB master registers that + * send LPC irqs to 4 output lines that raise the PSI SERIRQ irqs. + * These don't appear to get latched into an OPB register like the + * LPCHC irqs. + * + * POWER9 LPC controller internal irqs still go via the OPB + * and LPCHC PSI irqs like P8, but we have no such internal sources + * modelled yet. + */ + bool serirq_out[4] = { false, false, false, false }; + int irq; + + for (irq = 0; irq < ISA_NUM_IRQS; irq++) { + if (active_irqs & (LPC_HC_IRQ_SERIRQ0 >> irq)) { + serirq_out[lpc->irq_to_serirq_route[irq]] = true; + } + } + + qemu_set_irq(lpc->psi_irq_serirq[0], serirq_out[0]); + qemu_set_irq(lpc->psi_irq_serirq[1], serirq_out[1]); + qemu_set_irq(lpc->psi_irq_serirq[2], serirq_out[2]); + qemu_set_irq(lpc->psi_irq_serirq[3], serirq_out[3]); + } } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) @@ -504,7 +570,14 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, pnv_lpc_eval_irqs(lpc); break; case LPC_HC_IRQSTAT: - lpc->lpc_hc_irqstat &= ~val; + /* + * This register is write-to-clear for the IRQSER (LPC device IRQ) + * status. However if the device has not de-asserted its interrupt + * that will just raise this IRQ status bit again. Model this by + * keeping track of the inputs and only clearing if the inputs are + * deasserted. + */ + lpc->lpc_hc_irqstat &= ~(val & ~lpc->lpc_hc_irq_inputs); pnv_lpc_eval_irqs(lpc); break; case LPC_HC_ERROR_ADDRESS: @@ -535,10 +608,10 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) uint64_t val = 0xfffffffffffffffful; switch (addr) { - case OPB_MASTER_LS_ROUTE0: /* TODO */ + case OPB_MASTER_LS_ROUTE0: val = lpc->opb_irq_route0; break; - case OPB_MASTER_LS_ROUTE1: /* TODO */ + case OPB_MASTER_LS_ROUTE1: val = lpc->opb_irq_route1; break; case OPB_MASTER_LS_IRQ_STAT: @@ -567,11 +640,15 @@ static void opb_master_write(void *opaque, hwaddr addr, PnvLpcController *lpc = opaque; switch (addr) { - case OPB_MASTER_LS_ROUTE0: /* TODO */ + case OPB_MASTER_LS_ROUTE0: lpc->opb_irq_route0 = val; + pnv_lpc_eval_serirq_routes(lpc); + pnv_lpc_eval_irqs(lpc); break; - case OPB_MASTER_LS_ROUTE1: /* TODO */ + case OPB_MASTER_LS_ROUTE1: lpc->opb_irq_route1 = val; + pnv_lpc_eval_serirq_routes(lpc); + pnv_lpc_eval_irqs(lpc); break; case OPB_MASTER_LS_IRQ_STAT: lpc->opb_irq_stat &= ~val; @@ -656,6 +733,8 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); Error *local_err = NULL; + object_property_set_bool(OBJECT(lpc), "psi-serirq", true, &error_abort); + plc->parent_realize(dev, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -665,6 +744,9 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) /* P9 uses a MMIO region */ memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops, lpc, "lpcm", PNV9_LPCM_SIZE); + + /* P9 LPC routes ISA irqs to 4 PSI SERIRQ lines */ + qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4); } static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) @@ -733,20 +815,29 @@ 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_named(dev, &lpc->psi_irq_lpchc, "LPCHC", 1); } +static Property pnv_lpc_properties[] = { + DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + device_class_set_props(dc, pnv_lpc_properties); dc->realize = pnv_lpc_realize; dc->desc = "PowerNV LPC Controller"; dc->user_creatable = false; @@ -792,18 +883,34 @@ static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) } if (pnv->cpld_irqstate != old_state) { - qemu_set_irq(lpc->psi_irq, pnv->cpld_irqstate != 0); + qemu_set_irq(lpc->psi_irq_lpchc, pnv->cpld_irqstate != 0); } } static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) { PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t irq_bit = LPC_HC_IRQ_SERIRQ0 >> n; - /* The Naples HW latches the 1 levels, clearing is done by SW */ if (level) { - lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; + lpc->lpc_hc_irq_inputs |= irq_bit; + + /* + * The LPC HC in Naples and later latches LPC IRQ into a bit field in + * the IRQSTAT register, and that drives the PSI IRQ to the IC. + * Software clears this bit manually (see LPC_HC_IRQSTAT handler). + */ + lpc->lpc_hc_irqstat |= irq_bit; pnv_lpc_eval_irqs(lpc); + } else { + lpc->lpc_hc_irq_inputs &= ~irq_bit; + + /* POWER9 adds an auto-clear mode that clears IRQSTAT bits on EOI */ + if (lpc->psi_has_serirq && + (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_AUTO_CLEAR)) { + lpc->lpc_hc_irqstat &= ~irq_bit; + pnv_lpc_eval_irqs(lpc); + } } } @@ -834,9 +941,10 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) handler = pnv_lpc_isa_irq_handler; } + /* POWER has a 17th irq, QEMU only implements the 16 regular device irqs */ 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..780fa69dde --- /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, "xscom-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_psi.c b/hw/ppc/pnv_psi.c index 98045ed3d2..37c56882b8 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -18,13 +18,13 @@ */ #include "qemu/osdep.h" +#include "exec/address-spaces.h" #include "hw/irq.h" #include "target/ppc/cpu.h" #include "qemu/log.h" #include "qemu/module.h" #include "sysemu/reset.h" #include "qapi/error.h" -#include "monitor/monitor.h" #include "hw/ppc/fdt.h" @@ -120,8 +120,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 +737,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 +773,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 +863,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; } @@ -883,7 +897,7 @@ static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV PSI Controller POWER9"; dc->realize = pnv_psi_power9_realize; - dc->reset = pnv_psi_power9_reset; + device_class_set_legacy_reset(dc, pnv_psi_power9_reset); ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE; @@ -935,7 +949,7 @@ static void pnv_psi_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV PSI Controller"; device_class_set_props(dc, pnv_psi_properties); - dc->reset = pnv_psi_reset; + device_class_set_legacy_reset(dc, pnv_psi_reset); dc->user_creatable = false; } @@ -962,14 +976,14 @@ static void pnv_psi_register_types(void) type_init(pnv_psi_register_types); -void pnv_psi_pic_print_info(Pnv9Psi *psi9, Monitor *mon) +void pnv_psi_pic_print_info(Pnv9Psi *psi9, GString *buf) { PnvPsi *psi = PNV_PSI(psi9); uint32_t offset = (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); - monitor_printf(mon, "PSIHB Source %08x .. %08x\n", - offset, offset + psi9->source.nr_irqs - 1); - xive_source_pic_print_info(&psi9->source, offset, mon); + g_string_append_printf(buf, "PSIHB Source %08x .. %08x\n", + offset, offset + psi9->source.nr_irqs - 1); + xive_source_pic_print_info(&psi9->source, offset, buf); } diff --git a/hw/ppc/pnv_sbe.c b/hw/ppc/pnv_sbe.c index 1c7812a135..74cee4eea7 100644 --- a/hw/ppc/pnv_sbe.c +++ b/hw/ppc/pnv_sbe.c @@ -381,7 +381,7 @@ static void pnv_sbe_realize(DeviceState *dev, Error **errp) psc->xscom_mbox_ops, sbe, "xscom-sbe-mbox", psc->xscom_mbox_size); - qdev_init_gpio_out(DEVICE(dev), &sbe->psi_irq, 1); + qdev_init_gpio_out(dev, &sbe->psi_irq, 1); sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe); } diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index 79f10de57f..d192bbe2c2 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; } } @@ -77,11 +75,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case PRD_P9_IPOLL_REG_MASK: case PRD_P9_IPOLL_REG_STATUS: - /* P9 xscom reset */ - case 0x0090018: /* Receive status reg */ - case 0x0090012: /* log register */ - case 0x0090013: /* error register */ - /* P8 xscom reset */ case 0x2020007: /* ADU stuff, log register */ case 0x2020009: /* ADU stuff, error register */ @@ -121,10 +114,6 @@ static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val) case 0x1010c03: /* PIBAM FIR MASK */ case 0x1010c04: /* PIBAM FIR MASK */ case 0x1010c05: /* PIBAM FIR MASK */ - /* P9 xscom reset */ - case 0x0090018: /* Receive status reg */ - case 0x0090012: /* log register */ - case 0x0090013: /* error register */ /* P8 xscom reset */ case 0x2020007: /* ADU stuff, log register */ @@ -220,15 +209,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); diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index fbdc48911e..b86b5847de 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" @@ -44,13 +45,9 @@ void ppc_set_irq(PowerPCCPU *cpu, int irq, int level) { 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; @@ -62,15 +59,13 @@ void ppc_set_irq(PowerPCCPU *cpu, int irq, int level) if (old_pending != env->pending_interrupts) { ppc_maybe_interrupt(env); - kvmppc_set_interrupt(cpu, irq, level); + if (kvm_enabled()) { + kvmppc_set_interrupt(cpu, irq, level); + } } 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 */ @@ -272,7 +267,6 @@ static void power9_set_irq(void *opaque, int pin, int level) break; default: g_assert_not_reached(); - return; } } @@ -319,7 +313,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: @@ -339,7 +333,7 @@ void store_40x_dbcr0(CPUPPCState *env, uint32_t val) break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* PowerPC 40x internal IRQ controller */ @@ -490,10 +484,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) @@ -505,7 +521,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; @@ -516,7 +533,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; @@ -534,8 +552,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); } @@ -543,23 +560,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) @@ -572,7 +590,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; @@ -583,7 +602,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; @@ -592,23 +612,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) @@ -630,14 +661,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) @@ -690,64 +720,79 @@ 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); + + /* BookE timers stop when reaching 0. */ + 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; @@ -792,16 +837,16 @@ 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; @@ -813,17 +858,17 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, 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. @@ -831,49 +876,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) @@ -883,14 +932,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); } } @@ -899,9 +951,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) @@ -911,29 +966,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) @@ -946,8 +988,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 @@ -1029,7 +1077,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() @@ -1037,7 +1085,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; @@ -1050,16 +1098,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) @@ -1135,9 +1208,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) { @@ -1163,14 +1234,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; } } @@ -1223,9 +1295,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: @@ -1449,6 +1519,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; @@ -1469,5 +1545,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_boards.c b/hw/ppc/ppc405_boards.c index 4092ebc1ab..347428e633 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -350,6 +350,7 @@ static void ppc405_machine_class_init(ObjectClass *oc, void *data) mc->init = ppc405_init; mc->default_ram_size = 128 * MiB; mc->default_ram_id = "ppc405.ram"; + mc->deprecation_reason = "machine is old and unmaintained"; } static const TypeInfo ppc405_machine_type = { @@ -456,7 +457,7 @@ static void ref405ep_fpga_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = ref405ep_fpga_realize; - dc->reset = ref405ep_fpga_reset; + device_class_set_legacy_reset(dc, ref405ep_fpga_reset); /* Reason: only works as part of a ppc405 board */ dc->user_creatable = false; } diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index c973cfb04e..58cbd0507a 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -32,14 +32,12 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "ppc405.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "qemu/timer.h" #include "sysemu/reset.h" #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" /*****************************************************************************/ @@ -121,7 +119,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_pob_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } @@ -198,7 +196,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_opba_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } @@ -304,7 +302,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_dma_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } @@ -494,7 +492,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_ocm_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } @@ -728,7 +726,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_gpt_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } @@ -977,7 +975,7 @@ static void ppc405_cpc_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = ppc405_cpc_realize; - dc->reset = ppc405_cpc_reset; + device_class_set_legacy_reset(dc, ppc405_cpc_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; device_class_set_props(dc, ppc405_cpc_properties); diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h index 7c24db8504..909373fb38 100644 --- a/hw/ppc/ppc440.h +++ b/hw/ppc/ppc440.h @@ -18,6 +18,5 @@ void ppc4xx_cpr_init(CPUPPCState *env); void ppc4xx_sdr_init(CPUPPCState *env); 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 81d71adf34..a55f108434 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -13,20 +13,19 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "qemu/error-report.h" #include "qemu/datadir.h" #include "qemu/error-report.h" +#include "exec/page-protection.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/char/serial-mm.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" @@ -98,16 +97,6 @@ static int bamboo_load_device_tree(MachineState *machine, 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", clock_freq); qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", @@ -121,29 +110,6 @@ static int bamboo_load_device_tree(MachineState *machine, return 0; } -/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa) -{ - ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; - - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0x80000000 */ - tlb->EPN = va & TARGET_PAGE_MASK; - tlb->RPN = pa & TARGET_PAGE_MASK; - tlb->PID = 0; - - tlb = &env->tlb.tlbe[1]; - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0xffffffff */ - tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->PID = 0; -} - static void main_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -154,14 +120,16 @@ static void main_cpu_reset(void *opaque) env->gpr[3] = FDT_ADDR; env->nip = entry; - /* Create a mapping for the kernel. */ - mmubooke_create_initial_mapping(env, 0, 0); + /* Create a mapping spanning the 32bit addr space. */ + booke_set_tlb(&env->tlb.tlbe[0], 0, 0, 1U << 31); + booke_set_tlb(&env->tlb.tlbe[1], 0x80000000, 0x80000000, 1U << 31); } static void bamboo_init(MachineState *machine) { const char *kernel_filename = machine->kernel_filename; 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); @@ -173,7 +141,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; @@ -210,8 +183,7 @@ static void bamboo_init(MachineState *machine) 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]), @@ -241,14 +213,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. */ @@ -297,6 +266,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 651263926e..1312aa2080 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -14,9 +14,11 @@ #include "qemu/log.h" #include "hw/irq.h" #include "hw/ppc/ppc4xx.h" +#include "hw/pci-host/ppc4xx.h" #include "hw/qdev-properties.h" #include "hw/pci/pci.h" #include "sysemu/reset.h" +#include "cpu.h" #include "ppc440.h" /*****************************************************************************/ @@ -72,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; @@ -192,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) @@ -202,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) @@ -769,15 +729,17 @@ void ppc4xx_dma_init(CPUPPCState *env, int dcr_base) */ #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; @@ -795,9 +757,6 @@ struct PPC460EXPCIEState { uint32_t cfg; }; -#define DCRN_PCIE0_BASE 0x100 -#define DCRN_PCIE1_BASE 0x120 - enum { PEGPL_CFGBAH = 0x0, PEGPL_CFGBAL, @@ -826,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; } @@ -1000,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(), }; @@ -1057,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 c1d111465d..db8f6b9497 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -242,7 +242,7 @@ 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; + device_class_set_legacy_reset(dc, ppc4xx_mal_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; device_class_set_props(dc, ppc4xx_mal_properties); @@ -332,7 +332,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_plb_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } @@ -518,7 +518,7 @@ 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; + device_class_set_legacy_reset(dc, ppc405_ebc_reset); /* Reason: only works as function of a ppc4xx SoC */ dc->user_creatable = false; } diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index 8d7137faf3..2ee21f1ca7 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -33,6 +33,7 @@ #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" @@ -192,17 +193,13 @@ static inline hwaddr sdram_ddr_base(uint32_t bcr) static hwaddr sdram_ddr_size(uint32_t bcr) { - hwaddr size; - int sh; + int sh = (bcr >> 17) & 0x7; - sh = (bcr >> 17) & 0x7; if (sh == 7) { - size = -1; - } else { - size = (4 * MiB) << sh; + return -1; } - return size; + return (4 * MiB) << sh; } static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn) @@ -440,7 +437,7 @@ 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; + device_class_set_legacy_reset(dc, 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); @@ -504,7 +501,7 @@ static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size) bcr = 0x8000; break; default: - error_report("invalid RAM size " TARGET_FMT_plx, ram_size); + error_report("invalid RAM size " HWADDR_FMT_plx, ram_size); return 0; } bcr |= ram_base >> 2 & 0xffe00000; @@ -520,13 +517,10 @@ static inline hwaddr sdram_ddr2_base(uint32_t bcr) static hwaddr sdram_ddr2_size(uint32_t bcr) { - hwaddr size; int sh; sh = 1024 - ((bcr >> 6) & 0x3ff); - size = 8 * MiB * sh; - - return size; + return 8 * MiB * sh; } static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn) @@ -728,7 +722,7 @@ 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; + device_class_set_legacy_reset(dc, 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); diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index ca22da196a..c8849e66ff 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -31,6 +31,16 @@ #include "hw/loader.h" #include "kvm_ppc.h" +void booke_set_tlb(ppcemb_tlb_t *tlb, target_ulong va, hwaddr pa, + target_ulong size) +{ + tlb->attr = 0; + tlb->prot = PAGE_RWX << 4 | PAGE_VALID; + tlb->size = size; + tlb->EPN = va & TARGET_PAGE_MASK; + tlb->RPN = pa & TARGET_PAGE_MASK; + tlb->PID = 0; +} /* Timer Control Register */ diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index d57b199797..93b16320d4 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -33,6 +33,7 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/hw_accel.h" +#include "hw/ppc/ppc.h" #include "e500.h" #include "qom/object.h" @@ -70,29 +71,12 @@ static void spin_reset(DeviceState *dev) } } -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa, - hwaddr len) -{ - ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1); - hwaddr size; - - size = (booke206_page_size_to_tlb(len) << MAS1_TSIZE_SHIFT); - tlb->mas1 = MAS1_VALID | size; - 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; - env->tlb_dirty = true; -} - 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; + hwaddr map_start, map_size = 64 * MiB; + ppcmas_tlb_t *tlb = booke206_get_tlbm(env, 1, 0, 1); cpu_synchronize_state(cs); stl_p(&curspin->pir, env->spr[SPR_BOOKE_PIR]); @@ -106,7 +90,12 @@ static void spin_kick(CPUState *cs, run_on_cpu_data data) env->gpr[9] = 0; map_start = ldq_p(&curspin->addr) & ~(map_size - 1); - mmubooke_create_initial_mapping(env, 0, map_start, map_size); + /* create initial mapping */ + booke206_set_tlb(tlb, 0, map_start, map_size); + tlb->mas2 |= MAS2_M; +#ifdef CONFIG_KVM + env->tlb_dirty = true; +#endif cs->halted = 0; cs->exception_index = -1; @@ -190,7 +179,7 @@ static void ppce500_spin_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = spin_reset; + device_class_set_legacy_reset(dc, spin_reset); } static const TypeInfo ppce500_spin_info = { diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index ec8d9584fb..fb58c312ac 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/rtc/m48t59.h" -#include "hw/char/serial.h" #include "hw/block/fdc.h" #include "net/net.h" #include "hw/isa/isa.h" @@ -45,7 +44,7 @@ #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 @@ -68,6 +67,7 @@ static void ppc_prep_reset(void *opaque) PowerPCCPU *cpu = opaque; cpu_reset(CPU(cpu)); + cpu_ppc_tb_reset(&cpu->env); } @@ -212,14 +212,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"); @@ -230,6 +229,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; @@ -240,11 +240,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; @@ -272,9 +277,9 @@ static void ibm_40p_init(MachineState *machine) /* PCI -> ISA bridge */ 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, qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); - qdev_realize_and_unref(i82378_dev, BUS(pci_bus), &error_fatal); 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 +309,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 +334,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 +400,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 +427,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 5a56f155f5..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 { @@ -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..07b0b664d9 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -3,10 +3,12 @@ * * Copyright (c) 2017 Hervé Poussineau * + * SPDX-License-Identifier: GPL-2.0-or-later + * * 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) version 3 or any later version. + * (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 @@ -143,7 +145,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 +166,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 +201,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 4a22ce3761..78e2a46e75 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -21,17 +21,20 @@ #include "kvm_ppc.h" #include "sysemu/device_tree.h" #include "sysemu/block-backend.h" +#include "exec/page-protection.h" #include "hw/loader.h" #include "elf.h" #include "exec/memory.h" #include "ppc440.h" +#include "hw/pci-host/ppc4xx.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" #include "hw/sysbus.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.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" @@ -45,6 +48,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 @@ -207,38 +213,6 @@ static int sam460ex_load_device_tree(MachineState *machine, return fdt_size; } -/* Create reset TLB entries for BookE, mapping only the flash memory. */ -static void mmubooke_create_initial_mapping_uboot(CPUPPCState *env) -{ - ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; - - /* on reset the flash is mapped by a shadow TLB, - * but since we don't implement them we need to use - * the same values U-Boot will use to avoid a fault. - */ - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 0x10000000; /* up to 0xffffffff */ - tlb->EPN = 0xf0000000 & TARGET_PAGE_MASK; - tlb->RPN = (0xf0000000 & TARGET_PAGE_MASK) | 0x4; - tlb->PID = 0; -} - -/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa) -{ - ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; - - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1 << 31; /* up to 0x80000000 */ - tlb->EPN = va & TARGET_PAGE_MASK; - tlb->RPN = pa & TARGET_PAGE_MASK; - tlb->PID = 0; -} - static void main_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -247,31 +221,37 @@ static void main_cpu_reset(void *opaque) cpu_reset(CPU(cpu)); - /* either we have a kernel to boot or we jump to U-Boot */ + /* + * On reset the flash is mapped by a shadow TLB, but since we + * don't implement them we need to use the same values U-Boot + * will use to avoid a fault. + * either we have a kernel to boot or we jump to U-Boot + */ if (bi->entry != UBOOT_ENTRY) { env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = FDT_ADDR; env->nip = bi->entry; /* Create a mapping for the kernel. */ - mmubooke_create_initial_mapping(env, 0, 0); + booke_set_tlb(&env->tlb.tlbe[0], 0, 0, 1 << 31); env->gpr[6] = tswap32(EPAPR_MAGIC); env->gpr[7] = (16 * MiB) - 8; /* bi->ima_size; */ } else { env->nip = UBOOT_ENTRY; - mmubooke_create_initial_mapping_uboot(env); + /* Create a mapping for U-Boot. */ + booke_set_tlb(&env->tlb.tlbe[0], 0xf0000000, 0xf0000000, 0x10000000); + env->tlb.tlbe[0].RPN |= 4; } } static void sam460ex_init(MachineState *machine) { - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *isa = g_new(MemoryRegion, 1); MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1); DeviceState *uic[4]; int i; PCIBus *pci_bus; + USBBus *usb_bus; PowerPCCPU *cpu; CPUPPCState *env; I2CBus *i2c; @@ -389,8 +369,8 @@ static void sam460ex_init(MachineState *machine) /* MAL */ dev = qdev_new(TYPE_PPC4xx_MAL); - qdev_prop_set_uint32(dev, "txc-num", 4); - qdev_prop_set_uint32(dev, "rxc-num", 16); + 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); @@ -406,7 +386,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, @@ -418,39 +399,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); @@ -516,6 +520,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 66b414d2e9..0d4efaa0c0 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -35,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" @@ -61,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" @@ -72,6 +75,7 @@ #include "hw/virtio/vhost-scsi-common.h" #include "exec/ram_addr.h" +#include "exec/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -84,9 +88,6 @@ #include "hw/ppc/spapr_tpm_proxy.h" #include "hw/ppc/spapr_nvdimm.h" #include "hw/ppc/spapr_numa.h" -#include "hw/ppc/pef.h" - -#include "monitor/monitor.h" #include @@ -131,40 +132,6 @@ static bool spapr_is_thread0_in_vcore(SpaprMachineState *spapr, return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0; } -static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque) -{ - /* Dummy entries correspond to unused ICPState objects in older QEMUs, - * and newer QEMUs don't even have them. In both cases, we don't want - * to send anything on the wire. - */ - return false; -} - -static const VMStateDescription pre_2_10_vmstate_dummy_icp = { - .name = "icp/server", - .version_id = 1, - .minimum_version_id = 1, - .needed = pre_2_10_vmstate_dummy_icp_needed, - .fields = (VMStateField[]) { - VMSTATE_UNUSED(4), /* uint32_t xirr */ - VMSTATE_UNUSED(1), /* uint8_t pending_priority */ - VMSTATE_UNUSED(1), /* uint8_t mfrr */ - VMSTATE_END_OF_LIST() - }, -}; - -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); -} - -static void pre_2_10_vmstate_unregister_dummy_icp(int i) -{ - vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, - (void *)(uintptr_t) i); -} - int spapr_max_server_number(SpaprMachineState *spapr) { MachineState *ms = MACHINE(spapr); @@ -210,29 +177,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 */ @@ -242,6 +220,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; @@ -257,6 +265,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; } @@ -284,6 +296,32 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); } +static void spapr_dt_pi_features(SpaprMachineState *spapr, + PowerPCCPU *cpu, + void *fdt, int offset) +{ + uint8_t pi_features[] = { 1, 0, + 0x00 }; + + if (kvm_enabled() && ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, + 0, cpu->compat_pvr)) { + /* + * POWER9 and later CPUs with KVM run in LPAR-per-thread mode where + * all threads are essentially independent CPUs, and msgsndp does not + * work (because it is physically-addressed) and therefore is + * emulated by KVM, so disable it here to ensure XIVE will be used. + * This is both KVM and CPU implementation-specific behaviour so a KVM + * cap would be cleanest, but for now this works. If KVM ever permits + * native msgsndp execution by guests, a cap could be added at that + * time. + */ + pi_features[2] |= 0x08; /* 4: No msgsndp */ + } + + _FDT((fdt_setprop(fdt, offset, "ibm,pi-features", pi_features, + sizeof(pi_features)))); +} + static hwaddr spapr_node0_size(MachineState *machine) { if (machine->numa_state->num_nodes) { @@ -545,10 +583,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; } @@ -591,7 +627,6 @@ static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt) { MachineState *machine = MACHINE(spapr); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); hwaddr mem_start, node_size; int i, nb_nodes = machine->numa_state->num_nodes; NodeInfo *nodes = machine->numa_state->nodes; @@ -633,7 +668,6 @@ static int spapr_dt_memory(SpaprMachineState *spapr, void *fdt) if (spapr_ovec_test(spapr->ov5_cas, OV5_DRCONF_MEMORY)) { int ret; - g_assert(smc->dr_lmb_enabled); ret = spapr_dt_dynamic_reconfiguration_memory(spapr, fdt); if (ret) { return ret; @@ -667,7 +701,7 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; int i; - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, index); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_CPU, env->core_index); if (drc) { drc_index = spapr_drc_index(drc); _FDT((fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", drc_index))); @@ -748,6 +782,8 @@ static void spapr_dt_cpu(CPUState *cs, void *fdt, int offset, spapr_dt_pa_features(spapr, cpu, fdt, offset); + spapr_dt_pi_features(spapr, cpu, fdt, offset); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", cs->cpu_index / vcpus_per_socket))); @@ -780,6 +816,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; @@ -809,21 +865,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); @@ -858,16 +900,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 */ @@ -1015,7 +1064,6 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) { MachineState *machine = MACHINE(spapr); SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); - uint8_t rng_seed[32]; int chosen; _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); @@ -1093,8 +1141,7 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) spapr_dt_ov5_platform_support(spapr, fdt, chosen); } - qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); - _FDT(fdt_setprop(fdt, chosen, "rng-seed", rng_seed, sizeof(rng_seed))); + _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")); } @@ -1114,7 +1161,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))); @@ -1203,9 +1250,7 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) spapr_dt_cpus(fdt, spapr); /* ibm,drc-indexes and friends */ - if (smc->dr_lmb_enabled) { - root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_LMB; - } + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_LMB; if (smc->dr_phb_enabled) { root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PHB; } @@ -1272,7 +1317,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)); @@ -1315,6 +1360,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) { @@ -1327,33 +1387,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; - - /* Check if partition table is properly aligned */ - if (patb & MAKE_64BIT_MASK(0, pats + 12)) { - 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(); } - - /* 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; } #define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) @@ -1576,7 +1619,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; } @@ -1623,7 +1666,7 @@ void spapr_check_mmu_mode(bool guest_radix) } } -static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) +static void spapr_machine_reset(MachineState *machine, ResetType type) { SpaprMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; @@ -1631,8 +1674,19 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) void *fdt; int rc; - pef_kvm_reset(machine->cgs, &error_fatal); + if (type != RESET_TYPE_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); + } + + if (machine->cgs) { + confidential_guest_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() && @@ -1649,12 +1703,12 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) spapr_setup_hpt(spapr); } - qemu_devices_reset(reason); + qemu_devices_reset(type); 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 @@ -1726,7 +1780,7 @@ static void spapr_machine_reset(MachineState *machine, ShutdownCause reason) /* 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) @@ -1863,7 +1917,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, @@ -1877,7 +1931,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() @@ -1933,7 +1987,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() @@ -1952,7 +2006,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() }, @@ -1970,7 +2024,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() }, @@ -2000,7 +2054,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, @@ -2038,7 +2092,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), @@ -2053,7 +2107,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), @@ -2063,7 +2117,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, @@ -2082,11 +2136,13 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_fwnmi, &vmstate_spapr_fwnmi, &vmstate_spapr_cap_rpt_invalidate, + &vmstate_spapr_cap_ail_mode_3, + &vmstate_spapr_cap_nested_papr, NULL } }; -static int htab_save_setup(QEMUFile *f, void *opaque) +static int htab_save_setup(QEMUFile *f, void *opaque, Error **errp) { SpaprMachineState *spapr = opaque; @@ -2165,7 +2221,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); @@ -2236,7 +2292,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); @@ -2453,6 +2509,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; @@ -2523,10 +2580,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 " @@ -2534,7 +2600,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" @@ -2590,7 +2656,6 @@ static void spapr_init_cpus(SpaprMachineState *spapr) { MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *type = spapr_get_cpu_core_type(machine->cpu_type); const CPUArchIdList *possible_cpus; unsigned int smp_cpus = machine->smp.cpus; @@ -2619,17 +2684,6 @@ static void spapr_init_cpus(SpaprMachineState *spapr) boot_cores_nr = possible_cpus->len; } - 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. - */ - pre_2_10_vmstate_register_dummy_icp(i); - } - } - for (i = 0; i < possible_cpus->len; i++) { int core_id = i * smp_threads; @@ -2732,6 +2786,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); @@ -2746,7 +2801,9 @@ static void spapr_machine_init(MachineState *machine) /* * if Secure VM (PEF) support is configured, then initialize it */ - pef_kvm_init(machine->cgs, &error_fatal); + if (machine->cgs) { + confidential_guest_kvm_init(machine->cgs, &error_fatal); + } msi_nonbroken = true; @@ -2803,10 +2860,8 @@ static void spapr_machine_init(MachineState *machine) spapr->ov5 = spapr_ovec_new(); spapr->ov5_cas = spapr_ovec_new(); - if (smc->dr_lmb_enabled) { - spapr_ovec_set(spapr->ov5, OV5_DRCONF_MEMORY); - spapr_validate_node_memory(machine, &error_fatal); - } + spapr_ovec_set(spapr->ov5, OV5_DRCONF_MEMORY); + spapr_validate_node_memory(machine, &error_fatal); spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); @@ -2836,8 +2891,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); @@ -2865,12 +2918,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 @@ -2889,24 +2941,11 @@ 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"); - } + spapr_create_lmb_dr_connectors(spapr); if (mc->nvdimm_supported) { spapr_create_nvdimm_dr_connectors(spapr); @@ -2946,21 +2985,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); } @@ -2975,15 +3005,13 @@ static void spapr_machine_init(MachineState *machine) } if (machine->usb) { - if (smc->use_ohci_by_default) { - pci_create_simple(phb->bus, -1, "pci-ohci"); - } else { - pci_create_simple(phb->bus, -1, "nec-usb-xhci"); - } + pci_create_simple(phb->bus, -1, "nec-usb-xhci"); 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"); } @@ -3075,7 +3103,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". @@ -3093,7 +3121,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; } /* @@ -3178,8 +3206,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) { @@ -3437,8 +3465,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 */ @@ -3558,7 +3585,6 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev) static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - const SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); PCDIMMDevice *dimm = PC_DIMM(dev); @@ -3567,11 +3593,6 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Object *memdev; hwaddr pagesize; - if (!smc->dr_lmb_enabled) { - error_setg(errp, "Memory hotplug not supported for this machine"); - return; - } - size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &local_err); if (local_err) { error_propagate(errp, local_err); @@ -3595,7 +3616,7 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - pc_dimm_pre_plug(dimm, MACHINE(hotplug_dev), NULL, errp); + pc_dimm_pre_plug(dimm, MACHINE(hotplug_dev), errp); } struct SpaprDimmState { @@ -3681,7 +3702,6 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) SpaprDrc *drc; uint32_t nr_lmbs; uint64_t size, addr_start, addr; - g_autofree char *qapi_error = NULL; int i; if (!dev) { @@ -3718,17 +3738,9 @@ void spapr_memory_unplug_rollback(SpaprMachineState *spapr, DeviceState *dev) /* * Tell QAPI that something happened and the memory - * hotunplug wasn't successful. Keep sending - * MEM_UNPLUG_ERROR even while sending - * DEVICE_UNPLUG_GUEST_ERROR until the deprecation of - * MEM_UNPLUG_ERROR is due. + * hotunplug wasn't successful. */ - qapi_error = g_strdup_printf("Memory hotunplug rejected by the guest " - "for device %s", dev->id); - - 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); } @@ -3837,21 +3849,9 @@ void spapr_core_release(DeviceState *dev) static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) { MachineState *ms = MACHINE(hotplug_dev); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); CPUCore *cc = CPU_CORE(dev); CPUArchId *core_slot = spapr_find_cpu_slot(ms, cc->core_id, NULL); - if (smc->pre_2_10_has_unused_icps) { - SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); - int i; - - for (i = 0; i < cc->nr_threads; i++) { - CPUState *cs = CPU(sc->threads[i]); - - pre_2_10_vmstate_register_dummy_icp(cs->cpu_index); - } - } - assert(core_slot); core_slot->cpu = NULL; qdev_unrealize(dev); @@ -3932,10 +3932,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(spapr); - 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; @@ -3969,7 +3967,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 @@ -3983,12 +3981,6 @@ 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]); - pre_2_10_vmstate_unregister_dummy_icp(cs->cpu_index); - } - } } static void spapr_core_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, @@ -4100,7 +4092,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); } @@ -4309,7 +4300,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); @@ -4363,8 +4354,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. @@ -4409,9 +4399,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; } @@ -4436,14 +4423,13 @@ static ICPState *spapr_icp_get(XICSFabric *xi, int vcpu_id) return cpu ? spapr_cpu_state(cpu)->icp : NULL; } -static void spapr_pic_print_info(InterruptStatsProvider *obj, - Monitor *mon) +static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); - spapr_irq_print_info(spapr, mon); - monitor_printf(mon, "irqchip: %s\n", - kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); + spapr_irq_print_info(spapr, buf); + g_string_append_printf(buf, "irqchip: %s\n", + kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); } /* @@ -4602,13 +4588,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 = ""; @@ -4628,9 +4611,8 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) hc->unplug_request = spapr_machine_device_unplug_request; hc->unplug = spapr_machine_device_unplug; - 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; @@ -4668,10 +4650,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; @@ -4712,36 +4702,111 @@ static void spapr_machine_latest_class_options(MachineClass *mc) mc->is_default = true; } -#define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \ - static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_SPAPR_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - spapr_machine_##suffix##_class_options(mc); \ + MACHINE_VER_SYM(class_options, spapr, __VA_ARGS__)(mc); \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ spapr_machine_latest_class_options(mc); \ } \ } \ - static const TypeInfo spapr_machine_##suffix##_info = { \ - .name = MACHINE_TYPE_NAME("pseries-" verstr), \ - .parent = TYPE_SPAPR_MACHINE, \ - .class_init = spapr_machine_##suffix##_class_init, \ - }; \ - static void spapr_machine_register_##suffix(void) \ + static const TypeInfo MACHINE_VER_SYM(info, spapr, __VA_ARGS__) = \ { \ - type_register(&spapr_machine_##suffix##_info); \ + .name = MACHINE_VER_TYPE_NAME("pseries", __VA_ARGS__), \ + .parent = TYPE_SPAPR_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, spapr, __VA_ARGS__)(void) \ + { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ + type_register(&MACHINE_VER_SYM(info, spapr, __VA_ARGS__)); \ } \ - type_init(spapr_machine_register_##suffix) + type_init(MACHINE_VER_SYM(register, spapr, __VA_ARGS__)) + +#define DEFINE_SPAPR_MACHINE_AS_LATEST(major, minor) \ + DEFINE_SPAPR_MACHINE_IMPL(true, major, minor) +#define DEFINE_SPAPR_MACHINE(major, minor) \ + DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) + +/* + * pseries-9.2 + */ +static void spapr_machine_9_2_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(9, 2); + +/* + * pseries-9.1 + */ +static void spapr_machine_9_1_class_options(MachineClass *mc) +{ + spapr_machine_9_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); +} + +DEFINE_SPAPR_MACHINE(9, 1); + +/* + * pseries-9.0 + */ +static void spapr_machine_9_0_class_options(MachineClass *mc) +{ + spapr_machine_9_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); +} + +DEFINE_SPAPR_MACHINE(9, 0); + +/* + * 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); + +/* + * 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); + +/* + * 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); /* * pseries-7.2 */ static void spapr_machine_7_2_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + 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", true); +DEFINE_SPAPR_MACHINE(7, 2); /* * pseries-7.1 @@ -4752,7 +4817,7 @@ static void spapr_machine_7_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_7_1, hw_compat_7_1_len); } -DEFINE_SPAPR_MACHINE(7_1, "7.1", false); +DEFINE_SPAPR_MACHINE(7, 1); /* * pseries-7.0 @@ -4763,7 +4828,7 @@ static void spapr_machine_7_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_SPAPR_MACHINE(7_0, "7.0", false); +DEFINE_SPAPR_MACHINE(7, 0); /* * pseries-6.2 @@ -4774,7 +4839,7 @@ static void spapr_machine_6_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); } -DEFINE_SPAPR_MACHINE(6_2, "6.2", false); +DEFINE_SPAPR_MACHINE(6, 2); /* * pseries-6.1 @@ -4789,7 +4854,7 @@ static void spapr_machine_6_1_class_options(MachineClass *mc) mc->smp_props.prefer_sockets = true; } -DEFINE_SPAPR_MACHINE(6_1, "6.1", false); +DEFINE_SPAPR_MACHINE(6, 1); /* * pseries-6.0 @@ -4800,7 +4865,7 @@ static void spapr_machine_6_0_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); } -DEFINE_SPAPR_MACHINE(6_0, "6.0", false); +DEFINE_SPAPR_MACHINE(6, 0); /* * pseries-5.2 @@ -4811,7 +4876,7 @@ static void spapr_machine_5_2_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_SPAPR_MACHINE(5_2, "5.2", false); +DEFINE_SPAPR_MACHINE(5, 2); /* * pseries-5.1 @@ -4825,7 +4890,7 @@ static void spapr_machine_5_1_class_options(MachineClass *mc) smc->pre_5_2_numa_associativity = true; } -DEFINE_SPAPR_MACHINE(5_1, "5.1", false); +DEFINE_SPAPR_MACHINE(5, 1); /* * pseries-5.0 @@ -4844,7 +4909,7 @@ static void spapr_machine_5_0_class_options(MachineClass *mc) smc->pre_5_1_assoc_refpoints = true; } -DEFINE_SPAPR_MACHINE(5_0, "5.0", false); +DEFINE_SPAPR_MACHINE(5, 0); /* * pseries-4.2 @@ -4861,7 +4926,7 @@ static void spapr_machine_4_2_class_options(MachineClass *mc) mc->nvdimm_supported = false; } -DEFINE_SPAPR_MACHINE(4_2, "4.2", false); +DEFINE_SPAPR_MACHINE(4, 2); /* * pseries-4.1 @@ -4881,7 +4946,7 @@ static void spapr_machine_4_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_SPAPR_MACHINE(4_1, "4.1", false); +DEFINE_SPAPR_MACHINE(4, 1); /* * pseries-4.0 @@ -4889,16 +4954,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) @@ -4912,7 +4973,7 @@ static void spapr_machine_4_0_class_options(MachineClass *mc) smc->pre_4_1_migration = true; } -DEFINE_SPAPR_MACHINE(4_0, "4.0", false); +DEFINE_SPAPR_MACHINE(4, 0); /* * pseries-3.1 @@ -4934,7 +4995,7 @@ static void spapr_machine_3_1_class_options(MachineClass *mc) smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; } -DEFINE_SPAPR_MACHINE(3_1, "3.1", false); +DEFINE_SPAPR_MACHINE(3, 1); /* * pseries-3.0 @@ -4952,282 +5013,7 @@ static void spapr_machine_3_0_class_options(MachineClass *mc) smc->irq = &spapr_irq_xics_legacy; } -DEFINE_SPAPR_MACHINE(3_0, "3.0", false); - -/* - * pseries-2.12 - */ -static void spapr_machine_2_12_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - { TYPE_POWERPC_CPU, "pre-3.0-migration", "on" }, - { TYPE_SPAPR_CPU_CORE, "pre-3.0-migration", "on" }, - }; - - spapr_machine_3_0_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - - /* 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 - * yet. Postpone this to machine init (see default_caps_with_cpu()). - */ - smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 0; -} - -DEFINE_SPAPR_MACHINE(2_12, "2.12", false); - -static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_2_12_class_options(mc); - smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; - smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; - smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; -} - -DEFINE_SPAPR_MACHINE(2_12_sxxm, "2.12-sxxm", false); - -/* - * pseries-2.11 - */ - -static void spapr_machine_2_11_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(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); -} - -DEFINE_SPAPR_MACHINE(2_11, "2.11", false); - -/* - * pseries-2.10 - */ - -static void spapr_machine_2_10_class_options(MachineClass *mc) -{ - spapr_machine_2_11_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); -} - -DEFINE_SPAPR_MACHINE(2_10, "2.10", false); - -/* - * pseries-2.9 - */ - -static void spapr_machine_2_9_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - { TYPE_POWERPC_CPU, "pre-2.10-migration", "on" }, - }; - - spapr_machine_2_10_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - smc->pre_2_10_has_unused_icps = true; - smc->resize_hpt_default = SPAPR_RESIZE_HPT_DISABLED; -} - -DEFINE_SPAPR_MACHINE(2_9, "2.9", false); - -/* - * pseries-2.8 - */ - -static void spapr_machine_2_8_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_SPAPR_PCI_HOST_BRIDGE, "pcie-extended-configuration-space", "off" }, - }; - - spapr_machine_2_9_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - mc->numa_mem_align_shift = 23; -} - -DEFINE_SPAPR_MACHINE(2_8, "2.8", false); - -/* - * pseries-2.7 - */ - -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) -{ - /* Legacy PHB placement for pseries-2.7 and earlier machine types */ - const uint64_t base_buid = 0x800000020000000ULL; - const hwaddr phb_spacing = 0x1000000000ULL; /* 64 GiB */ - const hwaddr mmio_offset = 0xa0000000; /* 2 GiB + 512 MiB */ - const hwaddr pio_offset = 0x80000000; /* 2 GiB */ - const uint32_t max_index = 255; - const hwaddr phb0_alignment = 0x10000000000ULL; /* 1 TiB */ - - uint64_t ram_top = MACHINE(spapr)->ram_size; - hwaddr phb0_base, phb_base; - int i; - - /* Do we have device memory? */ - if (MACHINE(spapr)->maxram_size > ram_top) { - /* Can't just use maxram_size, because there may be an - * alignment gap between normal and device memory regions - */ - ram_top = MACHINE(spapr)->device_memory->base + - memory_region_size(&MACHINE(spapr)->device_memory->mr); - } - - phb0_base = QEMU_ALIGN_UP(ram_top, phb0_alignment); - - if (index > max_index) { - error_setg(errp, "\"index\" for PAPR PHB is too large (max %u)", - max_index); - return false; - } - - *buid = base_buid + index; - for (i = 0; i < n_dma; ++i) { - liobns[i] = SPAPR_PCI_LIOBN(index, i); - } - - phb_base = phb0_base + index * phb_spacing; - *pio = phb_base + pio_offset; - *mmio32 = phb_base + mmio_offset; - /* - * We don't set the 64-bit MMIO window, relying on the PHB's - * fallback behaviour of automatically splitting a large "32-bit" - * window into contiguous 32-bit and 64-bit windows - */ - - *nv2gpa = 0; - *nv2atsd = 0; - return true; -} - -static void spapr_machine_2_7_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - { TYPE_SPAPR_PCI_HOST_BRIDGE, "mem_win_size", "0xf80000000", }, - { TYPE_SPAPR_PCI_HOST_BRIDGE, "mem64_win_size", "0", }, - { TYPE_POWERPC_CPU, "pre-2.8-migration", "on", }, - { TYPE_SPAPR_PCI_HOST_BRIDGE, "pre-2.8-migration", "on", }, - }; - - spapr_machine_2_8_class_options(mc); - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power7_v2.3"); - mc->default_machine_opts = "modern-hotplug-events=off"; - compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - smc->phb_placement = phb_placement_2_7; -} - -DEFINE_SPAPR_MACHINE(2_7, "2.7", false); - -/* - * pseries-2.6 - */ - -static void spapr_machine_2_6_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_SPAPR_PCI_HOST_BRIDGE, "ddw", "off" }, - }; - - spapr_machine_2_7_class_options(mc); - mc->has_hotpluggable_cpus = false; - compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} - -DEFINE_SPAPR_MACHINE(2_6, "2.6", false); - -/* - * pseries-2.5 - */ - -static void spapr_machine_2_5_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - { "spapr-vlan", "use-rx-buffer-pools", "off" }, - }; - - spapr_machine_2_6_class_options(mc); - smc->use_ohci_by_default = true; - compat_props_add(mc->compat_props, hw_compat_2_5, hw_compat_2_5_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} - -DEFINE_SPAPR_MACHINE(2_5, "2.5", false); - -/* - * pseries-2.4 - */ - -static void spapr_machine_2_4_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_2_5_class_options(mc); - smc->dr_lmb_enabled = false; - compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); -} - -DEFINE_SPAPR_MACHINE(2_4, "2.4", false); - -/* - * pseries-2.3 - */ - -static void spapr_machine_2_3_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { "spapr-pci-host-bridge", "dynamic-reconfiguration", "off" }, - }; - spapr_machine_2_4_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_3, hw_compat_2_3_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} -DEFINE_SPAPR_MACHINE(2_3, "2.3", false); - -/* - * pseries-2.2 - */ - -static void spapr_machine_2_2_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_SPAPR_PCI_HOST_BRIDGE, "mem_win_size", "0x20000000" }, - }; - - spapr_machine_2_3_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_2, hw_compat_2_2_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - mc->default_machine_opts = "modern-hotplug-events=off,suppress-vmdesc=on"; -} -DEFINE_SPAPR_MACHINE(2_2, "2.2", false); - -/* - * pseries-2.1 - */ - -static void spapr_machine_2_1_class_options(MachineClass *mc) -{ - spapr_machine_2_2_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_1, hw_compat_2_1_len); -} -DEFINE_SPAPR_MACHINE(2_1, "2.1", false); +DEFINE_SPAPR_MACHINE(3, 0); static void spapr_machine_register_types(void) { diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index b4283055c1..2f74923560 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"); } } @@ -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,10 +969,12 @@ 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); SPAPR_CAP_MIG_STATE(rpt_invalidate, SPAPR_CAP_RPT_INVALIDATE); +SPAPR_CAP_MIG_STATE(ail_mode_3, SPAPR_CAP_AIL_MODE_3); void spapr_caps_init(SpaprMachineState *spapr) { diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 8a4861f45a..135f86a622 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 } @@ -191,9 +197,7 @@ static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc) { CPUPPCState *env = &cpu->env; - if (!sc->pre_3_0_migration) { - vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); - } + vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); spapr_irq_cpu_intc_destroy(SPAPR_MACHINE(qdev_get_machine()), cpu); cpu_ppc_tb_free(env); qdev_unrealize(DEVICE(cpu)); @@ -243,8 +247,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 +258,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 +270,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); @@ -275,10 +283,8 @@ static bool spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, return false; } - if (!sc->pre_3_0_migration) { - vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state, - cpu->machine_data); - } + vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state, + cpu->machine_data); return true; } @@ -290,21 +296,26 @@ static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) g_autofree char *id = NULL; CPUState *cs; PowerPCCPU *cpu; + CPUPPCState *env; obj = object_new(scc->cpu_type); cs = CPU(obj); cpu = POWERPC_CPU(obj); + env = &cpu->env; /* * 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; } + env->chip_index = sc->node_id; + env->core_index = cc->core_id; + cpu->node_id = sc->node_id; id = g_strdup_printf("thread[%d]", i); @@ -335,9 +346,15 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) qemu_register_reset(spapr_cpu_core_reset_handler, sc); sc->threads = g_new0(PowerPCCPU *, cc->nr_threads); 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)) { + PowerPCCPU *cpu; + + cpu = spapr_create_vcpu(sc, i, errp); + sc->threads[i] = cpu; + if (cpu && cc->nr_threads > 1) { + cpu->env.has_smt_siblings = true; + } + + if (!cpu || !spapr_realize_vcpu(cpu, spapr, sc, i, errp)) { spapr_cpu_core_unrealize(dev); return; } @@ -346,8 +363,6 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) static Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID), - DEFINE_PROP_BOOL("pre-3.0-migration", SpaprCpuCore, pre_3_0_migration, - false), DEFINE_PROP_END_OF_LIST() }; @@ -358,7 +373,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) dc->realize = spapr_cpu_core_realize; dc->unrealize = spapr_cpu_core_unrealize; - dc->reset = spapr_cpu_core_reset; + device_class_set_legacy_reset(dc, spapr_cpu_core_reset); device_class_set_props(dc, spapr_cpu_core_properties); scc->cpu_type = data; } @@ -382,16 +397,16 @@ 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"), + DEFINE_SPAPR_CPU_CORE_TYPE("power11_v2.0"), #ifdef CONFIG_KVM DEFINE_SPAPR_CPU_CORE_TYPE("host"), #endif 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..4dbf8e2e2e 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -645,8 +645,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, /* we shouldn't be signaling hotplug events for resources * that don't support them */ - g_assert(false); - return; + g_assert_not_reached(); } if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) { @@ -899,7 +898,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 +919,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 925ff523cc..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); } @@ -787,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, @@ -816,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); @@ -854,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; } @@ -1461,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) { @@ -1495,363 +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); - ppc_maybe_interrupt(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); - ppc_maybe_interrupt(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 */ @@ -1900,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 @@ -1920,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 63e34d457a..7836dc71fc 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() @@ -283,7 +283,7 @@ static const VMStateDescription vmstate_spapr_tce_table = { .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), @@ -296,7 +296,7 @@ static const VMStateDescription vmstate_spapr_tce_table = { VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_spapr_tce_table_ex, NULL } @@ -672,7 +672,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = spapr_tce_table_realize; - dc->reset = spapr_tce_reset; + device_class_set_legacy_reset(dc, spapr_tce_reset); dc->unrealize = spapr_tce_table_unrealize; /* Reason: This is just an internal device for handling the hypercalls */ dc->user_creatable = false; diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index a0d1e1298e..aebd7eaabb 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, @@ -263,12 +265,12 @@ static void spapr_set_irq(void *opaque, int irq, int level) sicc->set_irq(spapr->active_intc, irq, level); } -void spapr_irq_print_info(SpaprMachineState *spapr, Monitor *mon) +void spapr_irq_print_info(SpaprMachineState *spapr, GString *buf) { SpaprInterruptControllerClass *sicc = SPAPR_INTC_GET_CLASS(spapr->active_intc); - sicc->print_info(spapr->active_intc, mon); + sicc->print_info(spapr->active_intc, buf); } void spapr_irq_dt(SpaprMachineState *spapr, uint32_t nr_servers, @@ -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..7def8eb73b --- /dev/null +++ b/hw/ppc/spapr_nested.c @@ -0,0 +1,1916 @@ +#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_P11: + 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_DW(GSB_VCPU_SPR_DPDES, dpdes), + 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; + } + + /* P11 capabilities */ + if (ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_10_P11, 0, + spapr->max_compat_pvr)) { + env->gpr[4] |= H_GUEST_CAPABILITIES_P11_MODE; + } + + /* 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_P11, 0, + spapr->max_compat_pvr)) { + env->gpr[5] = H_GUEST_CAP_P11_MODE_BMAP; + } else 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 04a64cada3..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; @@ -496,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), @@ -517,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); } @@ -528,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), @@ -541,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, @@ -589,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) { @@ -664,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 || @@ -699,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; @@ -877,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 7b7618d5da..7e24084673 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -39,7 +39,6 @@ #include "trace.h" #include "qemu/error-report.h" #include "qemu/module.h" -#include "qapi/qmp/qerror.h" #include "hw/ppc/fdt.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_bus.h" @@ -780,6 +779,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; @@ -1234,10 +1237,6 @@ static void add_drcs(SpaprPhbState *phb, PCIBus *bus) int i; uint8_t chassis; - if (!phb->dr_enabled) { - return; - } - chassis = chassis_from_bus(bus); if (pci_bus_is_root(bus)) { @@ -1257,10 +1256,6 @@ static void remove_drcs(SpaprPhbState *phb, PCIBus *bus) int i; uint8_t chassis; - if (!phb->dr_enabled) { - return; - } - chassis = chassis_from_bus(bus); for (i = PCI_SLOT_MAX * PCI_FUNC_MAX - 1; i >= 0; i--) { @@ -1361,7 +1356,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); @@ -1444,9 +1438,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); @@ -1544,23 +1536,11 @@ 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); - if (!phb->dr_enabled) { - /* if this is a hotplug operation initiated by the user - * we need to let them know it's not enabled - */ - if (plugged_dev->hotplugged) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, - object_get_typename(OBJECT(phb))); - return; - } - } - - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(plugged_dev)) { if (!bridge_has_valid_chassis_nr(OBJECT(plugged_dev), errp)) { return; } @@ -1589,21 +1569,12 @@ 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); - /* - * If DR is disabled we don't need to do anything in the case of - * hotplug or coldplug callbacks. - */ - if (!phb->dr_enabled) { - return; - } - g_assert(drc); - if (pc->is_bridge) { + if (IS_PCI_BRIDGE(plugged_dev)) { spapr_pci_bridge_plug(phb, PCI_BRIDGE(plugged_dev)); } @@ -1646,7 +1617,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 @@ -1661,7 +1631,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; } @@ -1676,17 +1646,10 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, PCIDevice *pdev = PCI_DEVICE(plugged_dev); SpaprDrc *drc = drc_from_dev(phb, pdev); - if (!phb->dr_enabled) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, - object_get_typename(OBJECT(phb))); - return; - } - g_assert(drc); 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; @@ -1694,7 +1657,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; } @@ -1762,8 +1725,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; @@ -1835,9 +1796,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; @@ -1853,30 +1814,15 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) assert(sphb->index != (uint32_t)-1); /* checked in spapr_phb_pre_plug() */ - if (sphb->mem64_win_size != 0) { - if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) { - error_setg(errp, "32-bit memory window of size 0x%"HWADDR_PRIx - " (max 2 GiB)", sphb->mem_win_size); - return; - } - - /* 64-bit window defaults to identity mapping */ - sphb->mem64_win_pciaddr = sphb->mem64_win_addr; - } else if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) { - /* - * For compatibility with old configuration, if no 64-bit MMIO - * window is specified, but the ordinary (32-bit) memory - * window is specified as > 2GiB, we treat it as a 2GiB 32-bit - * window, with a 64-bit MMIO window following on immediately - * afterwards - */ - sphb->mem64_win_size = sphb->mem_win_size - SPAPR_PCI_MEM32_WIN_SIZE; - sphb->mem64_win_addr = sphb->mem_win_addr + SPAPR_PCI_MEM32_WIN_SIZE; - sphb->mem64_win_pciaddr = - SPAPR_PCI_MEM_WIN_BUS_OFFSET + SPAPR_PCI_MEM32_WIN_SIZE; - sphb->mem_win_size = SPAPR_PCI_MEM32_WIN_SIZE; + if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) { + error_setg(errp, "32-bit memory window of size 0x%"HWADDR_PRIx + " (max 2 GiB)", sphb->mem_win_size); + return; } + /* 64-bit window defaults to identity mapping */ + sphb->mem64_win_pciaddr = sphb->mem64_win_addr; + if (spapr_pci_find_phb(spapr, sphb->buid)) { SpaprPhbState *s; @@ -1987,7 +1933,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); @@ -2074,14 +2020,8 @@ void spapr_phb_dma_reset(SpaprPhbState *sphb) 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); @@ -2101,8 +2041,6 @@ static Property spapr_phb_properties[] = { SPAPR_PCI_MEM64_WIN_SIZE), DEFINE_PROP_UINT64("io_win_size", SpaprPhbState, io_win_size, SPAPR_PCI_IO_WIN_SIZE), - DEFINE_PROP_BOOL("dynamic-reconfiguration", SpaprPhbState, dr_enabled, - true), /* Default DMA window is 0..1GB */ DEFINE_PROP_UINT64("dma_win_addr", SpaprPhbState, dma_win_addr, 0), DEFINE_PROP_UINT64("dma_win_size", SpaprPhbState, dma_win_size, 0x40000000), @@ -2113,12 +2051,8 @@ static Property spapr_phb_properties[] = { (1ULL << 12) | (1ULL << 16) | (1ULL << 21) | (1ULL << 24)), DEFINE_PROP_UINT32("numa_node", SpaprPhbState, numa_node, -1), - DEFINE_PROP_BOOL("pre-2.8-migration", SpaprPhbState, - 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(), @@ -2128,7 +2062,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() @@ -2139,7 +2073,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), @@ -2154,20 +2088,6 @@ static int spapr_pci_pre_save(void *opaque) gpointer key, value; int i; - if (sphb->pre_2_8_migration) { - sphb->mig_liobn = sphb->dma_liobn[0]; - sphb->mig_mem_win_addr = sphb->mem_win_addr; - sphb->mig_mem_win_size = sphb->mem_win_size; - sphb->mig_io_win_addr = sphb->io_win_addr; - sphb->mig_io_win_size = sphb->io_win_size; - - if ((sphb->mem64_win_size != 0) - && (sphb->mem64_win_addr - == (sphb->mem_win_addr + sphb->mem_win_size))) { - sphb->mig_mem_win_size += sphb->mem64_win_size; - } - } - g_free(sphb->msi_devs); sphb->msi_devs = NULL; sphb->msi_devs_num = g_hash_table_size(sphb->msi); @@ -2202,10 +2122,9 @@ static int spapr_pci_post_load(void *opaque, int version_id) int i; for (i = 0; i < sphb->msi_devs_num; ++i) { - key = g_memdup(&sphb->msi_devs[i].key, - sizeof(sphb->msi_devs[i].key)); - value = g_memdup(&sphb->msi_devs[i].value, - sizeof(sphb->msi_devs[i].value)); + key = g_memdup2(&sphb->msi_devs[i].key, sizeof(sphb->msi_devs[i].key)); + value = g_memdup2(&sphb->msi_devs[i].value, + sizeof(sphb->msi_devs[i].value)); g_hash_table_insert(sphb->msi, key, value); } g_free(sphb->msi_devs); @@ -2215,13 +2134,6 @@ static int spapr_pci_post_load(void *opaque, int version_id) return 0; } -static bool pre_2_8_migration(void *opaque, int version_id) -{ - SpaprPhbState *sphb = opaque; - - return sphb->pre_2_8_migration; -} - static const VMStateDescription vmstate_spapr_pci = { .name = "spapr_pci", .version_id = 2, @@ -2229,13 +2141,8 @@ 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), - VMSTATE_UINT64_TEST(mig_mem_win_size, SpaprPhbState, pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_io_win_addr, SpaprPhbState, pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_io_win_size, SpaprPhbState, pre_2_8_migration), VMSTATE_STRUCT_ARRAY(lsi_table, SpaprPhbState, PCI_NUM_PINS, 0, vmstate_spapr_pci_lsi, SpaprPciLsi), VMSTATE_INT32(msi_devs_num, SpaprPhbState), @@ -2263,7 +2170,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->realize = spapr_phb_realize; dc->unrealize = spapr_phb_unrealize; device_class_set_props(dc, spapr_phb_properties); - dc->reset = spapr_phb_reset; + device_class_set_legacy_reset(dc, spapr_phb_reset); dc->vmsd = &vmstate_spapr_pci; /* Supported by TYPE_SPAPR_MACHINE */ dc->user_creatable = true; @@ -2367,7 +2274,6 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, }; SpaprTceTable *tcet; SpaprDrc *drc; - Error *err = NULL; /* Start populating the FDT */ _FDT(bus_off = fdt_add_subnode(fdt, 0, phb->dtbusname)); @@ -2448,12 +2354,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 2a8a11be1d..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) { - g_autofree uint32_t *npus = g_new(uint32_t, 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 3f664ea02c..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" @@ -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_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_softmmu.c deleted file mode 100644 index 5170a33369..0000000000 --- a/hw/ppc/spapr_softmmu.c +++ /dev/null @@ -1,613 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "qemu/memalign.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) -{ - /* - * hash value/pteg group index is normalized by HPT mask - */ - if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { - return false; - } - return true; -} - -static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - target_ulong pteh = args[2]; - target_ulong ptel = args[3]; - unsigned apshift; - target_ulong raddr; - target_ulong slot; - const ppc_hash_pte64_t *hptes; - - apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel); - if (!apshift) { - /* Bad page size encoding */ - return H_PARAMETER; - } - - raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1); - - if (is_ram_address(spapr, raddr)) { - /* Regular RAM - should have WIMG=0010 */ - if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { - return H_PARAMETER; - } - } else { - target_ulong wimg_flags; - /* Looks like an IO address */ - /* FIXME: What WIMG combinations could be sensible for IO? - * For now we allow WIMG=010x, but are there others? */ - /* FIXME: Should we check against registered IO addresses? */ - wimg_flags = (ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)); - - if (wimg_flags != HPTE64_R_I && - wimg_flags != (HPTE64_R_I | HPTE64_R_M)) { - return H_PARAMETER; - } - } - - pteh &= ~0x60ULL; - - if (!valid_ptex(cpu, ptex)) { - return H_PARAMETER; - } - - slot = ptex & 7ULL; - ptex = ptex & ~7ULL; - - if (likely((flags & H_EXACT) == 0)) { - hptes = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); - for (slot = 0; slot < 8; slot++) { - if (!(ppc_hash64_hpte0(cpu, hptes, slot) & HPTE64_V_VALID)) { - break; - } - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); - if (slot == 8) { - return H_PTEG_FULL; - } - } else { - hptes = ppc_hash64_map_hptes(cpu, ptex + slot, 1); - if (ppc_hash64_hpte0(cpu, hptes, 0) & HPTE64_V_VALID) { - ppc_hash64_unmap_hptes(cpu, hptes, ptex + slot, 1); - return H_PTEG_FULL; - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); - } - - spapr_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel); - - args[0] = ptex + slot; - return H_SUCCESS; -} - -typedef enum { - REMOVE_SUCCESS = 0, - REMOVE_NOT_FOUND = 1, - REMOVE_PARM = 2, - REMOVE_HW = 3, -} RemoveResult; - -static RemoveResult remove_hpte(PowerPCCPU *cpu - , target_ulong ptex, - target_ulong avpn, - target_ulong flags, - target_ulong *vp, target_ulong *rp) -{ - const ppc_hash_pte64_t *hptes; - target_ulong v, r; - - if (!valid_ptex(cpu, ptex)) { - return REMOVE_PARM; - } - - hptes = ppc_hash64_map_hptes(cpu, ptex, 1); - v = ppc_hash64_hpte0(cpu, hptes, 0); - r = ppc_hash64_hpte1(cpu, hptes, 0); - ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); - - if ((v & HPTE64_V_VALID) == 0 || - ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || - ((flags & H_ANDCOND) && (v & avpn) != 0)) { - return REMOVE_NOT_FOUND; - } - *vp = v; - *rp = r; - spapr_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0); - ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); - return REMOVE_SUCCESS; -} - -static target_ulong h_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - target_ulong avpn = args[2]; - RemoveResult ret; - - ret = remove_hpte(cpu, ptex, avpn, flags, - &args[0], &args[1]); - - switch (ret) { - case REMOVE_SUCCESS: - check_tlb_flush(env, true); - return H_SUCCESS; - - case REMOVE_NOT_FOUND: - return H_NOT_FOUND; - - case REMOVE_PARM: - return H_PARAMETER; - - case REMOVE_HW: - return H_HARDWARE; - } - - g_assert_not_reached(); -} - -#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL -#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL -#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL -#define H_BULK_REMOVE_END 0xc000000000000000ULL -#define H_BULK_REMOVE_CODE 0x3000000000000000ULL -#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL -#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL -#define H_BULK_REMOVE_PARM 0x2000000000000000ULL -#define H_BULK_REMOVE_HW 0x3000000000000000ULL -#define H_BULK_REMOVE_RC 0x0c00000000000000ULL -#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL -#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL -#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL -#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL -#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL - -#define H_BULK_REMOVE_MAX_BATCH 4 - -static target_ulong h_bulk_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - int i; - target_ulong rc = H_SUCCESS; - - for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { - target_ulong *tsh = &args[i*2]; - target_ulong tsl = args[i*2 + 1]; - target_ulong v, r, ret; - - if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { - break; - } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) { - return H_PARAMETER; - } - - *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; - *tsh |= H_BULK_REMOVE_RESPONSE; - - if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) { - *tsh |= H_BULK_REMOVE_PARM; - return H_PARAMETER; - } - - ret = remove_hpte(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, - (*tsh & H_BULK_REMOVE_FLAGS) >> 26, - &v, &r); - - *tsh |= ret << 60; - - switch (ret) { - case REMOVE_SUCCESS: - *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; - break; - - case REMOVE_PARM: - rc = H_PARAMETER; - goto exit; - - case REMOVE_HW: - rc = H_HARDWARE; - goto exit; - } - } - exit: - check_tlb_flush(env, true); - - return rc; -} - -static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - CPUPPCState *env = &cpu->env; - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - target_ulong avpn = args[2]; - const ppc_hash_pte64_t *hptes; - target_ulong v, r; - - if (!valid_ptex(cpu, ptex)) { - return H_PARAMETER; - } - - hptes = ppc_hash64_map_hptes(cpu, ptex, 1); - v = ppc_hash64_hpte0(cpu, hptes, 0); - r = ppc_hash64_hpte1(cpu, hptes, 0); - ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); - - if ((v & HPTE64_V_VALID) == 0 || - ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { - return H_NOT_FOUND; - } - - r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | - HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); - r |= (flags << 55) & HPTE64_R_PP0; - r |= (flags << 48) & HPTE64_R_KEY_HI; - r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); - spapr_store_hpte(cpu, ptex, - (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); - ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); - /* Flush the tlb */ - check_tlb_flush(env, true); - /* Don't need a memory barrier, due to qemu's global lock */ - spapr_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r); - return H_SUCCESS; -} - -static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong flags = args[0]; - target_ulong ptex = args[1]; - int i, ridx, n_entries = 1; - const ppc_hash_pte64_t *hptes; - - if (!valid_ptex(cpu, ptex)) { - return H_PARAMETER; - } - - if (flags & H_READ_4) { - /* Clear the two low order bits */ - ptex &= ~(3ULL); - n_entries = 4; - } - - hptes = ppc_hash64_map_hptes(cpu, ptex, n_entries); - for (i = 0, ridx = 0; i < n_entries; i++) { - args[ridx++] = ppc_hash64_hpte0(cpu, hptes, i); - args[ridx++] = ppc_hash64_hpte1(cpu, hptes, i); - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, n_entries); - - return H_SUCCESS; -} - -struct SpaprPendingHpt { - /* These fields are read-only after initialization */ - int shift; - QemuThread thread; - - /* These fields are protected by the BQL */ - bool complete; - - /* These fields are private to the preparation thread if - * !complete, otherwise protected by the BQL */ - int ret; - void *hpt; -}; - -static void free_pending_hpt(SpaprPendingHpt *pending) -{ - if (pending->hpt) { - qemu_vfree(pending->hpt); - } - - g_free(pending); -} - -static void *hpt_prepare_thread(void *opaque) -{ - SpaprPendingHpt *pending = opaque; - size_t size = 1ULL << pending->shift; - - pending->hpt = qemu_try_memalign(size, size); - if (pending->hpt) { - memset(pending->hpt, 0, size); - pending->ret = H_SUCCESS; - } else { - pending->ret = H_NO_MEM; - } - - qemu_mutex_lock_iothread(); - - if (SPAPR_MACHINE(qdev_get_machine())->pending_hpt == pending) { - /* Ready to go */ - pending->complete = true; - } else { - /* We've been cancelled, clean ourselves up */ - free_pending_hpt(pending); - } - - qemu_mutex_unlock_iothread(); - return NULL; -} - -/* Must be called with BQL held */ -static void cancel_hpt_prepare(SpaprMachineState *spapr) -{ - SpaprPendingHpt *pending = spapr->pending_hpt; - - /* Let the thread know it's cancelled */ - spapr->pending_hpt = NULL; - - if (!pending) { - /* Nothing to do */ - return; - } - - if (!pending->complete) { - /* thread will clean itself up */ - return; - } - - free_pending_hpt(pending); -} - -target_ulong softmmu_resize_hpt_prepare(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong shift) -{ - SpaprPendingHpt *pending = spapr->pending_hpt; - - if (pending) { - /* something already in progress */ - if (pending->shift == shift) { - /* and it's suitable */ - if (pending->complete) { - return pending->ret; - } else { - return H_LONG_BUSY_ORDER_100_MSEC; - } - } - - /* not suitable, cancel and replace */ - cancel_hpt_prepare(spapr); - } - - if (!shift) { - /* nothing to do */ - return H_SUCCESS; - } - - /* start new prepare */ - - pending = g_new0(SpaprPendingHpt, 1); - pending->shift = shift; - pending->ret = H_HARDWARE; - - qemu_thread_create(&pending->thread, "sPAPR HPT prepare", - hpt_prepare_thread, pending, QEMU_THREAD_DETACHED); - - spapr->pending_hpt = pending; - - /* In theory we could estimate the time more accurately based on - * the new size, but there's not much point */ - return H_LONG_BUSY_ORDER_100_MSEC; -} - -static uint64_t new_hpte_load0(void *htab, uint64_t pteg, int slot) -{ - uint8_t *addr = htab; - - addr += pteg * HASH_PTEG_SIZE_64; - addr += slot * HASH_PTE_SIZE_64; - return ldq_p(addr); -} - -static void new_hpte_store(void *htab, uint64_t pteg, int slot, - uint64_t pte0, uint64_t pte1) -{ - uint8_t *addr = htab; - - addr += pteg * HASH_PTEG_SIZE_64; - addr += slot * HASH_PTE_SIZE_64; - - stq_p(addr, pte0); - stq_p(addr + HPTE64_DW1, pte1); -} - -static int rehash_hpte(PowerPCCPU *cpu, - const ppc_hash_pte64_t *hptes, - void *old_hpt, uint64_t oldsize, - void *new_hpt, uint64_t newsize, - uint64_t pteg, int slot) -{ - uint64_t old_hash_mask = (oldsize >> 7) - 1; - uint64_t new_hash_mask = (newsize >> 7) - 1; - target_ulong pte0 = ppc_hash64_hpte0(cpu, hptes, slot); - target_ulong pte1; - uint64_t avpn; - unsigned base_pg_shift; - uint64_t hash, new_pteg, replace_pte0; - - if (!(pte0 & HPTE64_V_VALID) || !(pte0 & HPTE64_V_BOLTED)) { - return H_SUCCESS; - } - - pte1 = ppc_hash64_hpte1(cpu, hptes, slot); - - base_pg_shift = ppc_hash64_hpte_page_shift_noslb(cpu, pte0, pte1); - assert(base_pg_shift); /* H_ENTER shouldn't allow a bad encoding */ - avpn = HPTE64_V_AVPN_VAL(pte0) & ~(((1ULL << base_pg_shift) - 1) >> 23); - - if (pte0 & HPTE64_V_SECONDARY) { - pteg = ~pteg; - } - - if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_256M) { - uint64_t offset, vsid; - - /* We only have 28 - 23 bits of offset in avpn */ - offset = (avpn & 0x1f) << 23; - vsid = avpn >> 5; - /* We can find more bits from the pteg value */ - if (base_pg_shift < 23) { - offset |= ((vsid ^ pteg) & old_hash_mask) << base_pg_shift; - } - - hash = vsid ^ (offset >> base_pg_shift); - } else if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_1T) { - uint64_t offset, vsid; - - /* We only have 40 - 23 bits of seg_off in avpn */ - offset = (avpn & 0x1ffff) << 23; - vsid = avpn >> 17; - if (base_pg_shift < 23) { - offset |= ((vsid ^ (vsid << 25) ^ pteg) & old_hash_mask) - << base_pg_shift; - } - - hash = vsid ^ (vsid << 25) ^ (offset >> base_pg_shift); - } else { - error_report("rehash_pte: Bad segment size in HPTE"); - return H_HARDWARE; - } - - new_pteg = hash & new_hash_mask; - if (pte0 & HPTE64_V_SECONDARY) { - assert(~pteg == (hash & old_hash_mask)); - new_pteg = ~new_pteg; - } else { - assert(pteg == (hash & old_hash_mask)); - } - assert((oldsize != newsize) || (pteg == new_pteg)); - replace_pte0 = new_hpte_load0(new_hpt, new_pteg, slot); - /* - * Strictly speaking, we don't need all these tests, since we only - * ever rehash bolted HPTEs. We might in future handle non-bolted - * HPTEs, though so make the logic correct for those cases as - * well. - */ - if (replace_pte0 & HPTE64_V_VALID) { - assert(newsize < oldsize); - if (replace_pte0 & HPTE64_V_BOLTED) { - if (pte0 & HPTE64_V_BOLTED) { - /* Bolted collision, nothing we can do */ - return H_PTEG_FULL; - } else { - /* Discard this hpte */ - return H_SUCCESS; - } - } - } - - new_hpte_store(new_hpt, new_pteg, slot, pte0, pte1); - return H_SUCCESS; -} - -static int rehash_hpt(PowerPCCPU *cpu, - void *old_hpt, uint64_t oldsize, - void *new_hpt, uint64_t newsize) -{ - uint64_t n_ptegs = oldsize >> 7; - uint64_t pteg; - int slot; - int rc; - - for (pteg = 0; pteg < n_ptegs; pteg++) { - hwaddr ptex = pteg * HPTES_PER_GROUP; - const ppc_hash_pte64_t *hptes - = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); - - if (!hptes) { - return H_HARDWARE; - } - - for (slot = 0; slot < HPTES_PER_GROUP; slot++) { - rc = rehash_hpte(cpu, hptes, old_hpt, oldsize, new_hpt, newsize, - pteg, slot); - if (rc != H_SUCCESS) { - ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); - return rc; - } - } - ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); - } - - return H_SUCCESS; -} - -target_ulong softmmu_resize_hpt_commit(PowerPCCPU *cpu, - SpaprMachineState *spapr, - target_ulong flags, - target_ulong shift) -{ - SpaprPendingHpt *pending = spapr->pending_hpt; - int rc; - size_t newsize; - - if (flags != 0) { - return H_PARAMETER; - } - - if (!pending || (pending->shift != shift)) { - /* no matching prepare */ - return H_CLOSED; - } - - if (!pending->complete) { - /* prepare has not completed */ - return H_BUSY; - } - - /* Shouldn't have got past PREPARE without an HPT */ - g_assert(spapr->htab_shift); - - newsize = 1ULL << pending->shift; - rc = rehash_hpt(cpu, spapr->htab, HTAB_SIZE(spapr), - pending->hpt, newsize); - if (rc == H_SUCCESS) { - qemu_vfree(spapr->htab); - spapr->htab = pending->hpt; - spapr->htab_shift = pending->shift; - - push_sregs_to_kvm_pr(spapr); - - pending->hpt = NULL; /* so it's not free()d */ - } - - /* Clean up */ - spapr->pending_hpt = NULL; - free_pending_hpt(pending); - - return rc; -} - -static void hypercall_register_types(void) -{ - /* hcall-pft */ - spapr_register_hypercall(H_ENTER, h_enter); - spapr_register_hypercall(H_REMOVE, h_remove); - spapr_register_hypercall(H_PROTECT, h_protect); - spapr_register_hypercall(H_READ, h_read); - - /* hcall-bulk */ - spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); - -} - -type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_vhyp_mmu.c b/hw/ppc/spapr_vhyp_mmu.c new file mode 100644 index 0000000000..2d41d7f77b --- /dev/null +++ b/hw/ppc/spapr_vhyp_mmu.c @@ -0,0 +1,611 @@ +/* + * 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" + +static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + target_ulong pteh = args[2]; + target_ulong ptel = args[3]; + unsigned apshift; + target_ulong raddr; + target_ulong slot; + const ppc_hash_pte64_t *hptes; + + apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel); + if (!apshift) { + /* Bad page size encoding */ + return H_PARAMETER; + } + + raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1); + + if (is_ram_address(spapr, raddr)) { + /* Regular RAM - should have WIMG=0010 */ + if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { + return H_PARAMETER; + } + } else { + target_ulong wimg_flags; + /* Looks like an IO address */ + /* FIXME: What WIMG combinations could be sensible for IO? + * For now we allow WIMG=010x, but are there others? */ + /* FIXME: Should we check against registered IO addresses? */ + wimg_flags = (ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)); + + if (wimg_flags != HPTE64_R_I && + wimg_flags != (HPTE64_R_I | HPTE64_R_M)) { + return H_PARAMETER; + } + } + + pteh &= ~0x60ULL; + + if (!ppc_hash64_valid_ptex(cpu, ptex)) { + return H_PARAMETER; + } + + slot = ptex & 7ULL; + ptex = ptex & ~7ULL; + + if (likely((flags & H_EXACT) == 0)) { + hptes = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); + for (slot = 0; slot < 8; slot++) { + if (!(ppc_hash64_hpte0(cpu, hptes, slot) & HPTE64_V_VALID)) { + break; + } + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + if (slot == 8) { + return H_PTEG_FULL; + } + } else { + hptes = ppc_hash64_map_hptes(cpu, ptex + slot, 1); + if (ppc_hash64_hpte0(cpu, hptes, 0) & HPTE64_V_VALID) { + ppc_hash64_unmap_hptes(cpu, hptes, ptex + slot, 1); + return H_PTEG_FULL; + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); + } + + spapr_store_hpte(cpu, ptex + slot, pteh | HPTE64_V_HPTE_DIRTY, ptel); + + args[0] = ptex + slot; + return H_SUCCESS; +} + +typedef enum { + REMOVE_SUCCESS = 0, + REMOVE_NOT_FOUND = 1, + REMOVE_PARM = 2, + REMOVE_HW = 3, +} RemoveResult; + +static RemoveResult remove_hpte(PowerPCCPU *cpu + , target_ulong ptex, + target_ulong avpn, + target_ulong flags, + target_ulong *vp, target_ulong *rp) +{ + const ppc_hash_pte64_t *hptes; + target_ulong v, r; + + if (!ppc_hash64_valid_ptex(cpu, ptex)) { + return REMOVE_PARM; + } + + hptes = ppc_hash64_map_hptes(cpu, ptex, 1); + v = ppc_hash64_hpte0(cpu, hptes, 0); + r = ppc_hash64_hpte1(cpu, hptes, 0); + ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); + + if ((v & HPTE64_V_VALID) == 0 || + ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || + ((flags & H_ANDCOND) && (v & avpn) != 0)) { + return REMOVE_NOT_FOUND; + } + *vp = v; + *rp = r; + spapr_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0); + ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); + return REMOVE_SUCCESS; +} + +static target_ulong h_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + target_ulong avpn = args[2]; + RemoveResult ret; + + ret = remove_hpte(cpu, ptex, avpn, flags, + &args[0], &args[1]); + + switch (ret) { + case REMOVE_SUCCESS: + check_tlb_flush(env, true); + return H_SUCCESS; + + case REMOVE_NOT_FOUND: + return H_NOT_FOUND; + + case REMOVE_PARM: + return H_PARAMETER; + + case REMOVE_HW: + return H_HARDWARE; + } + + g_assert_not_reached(); +} + +#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL +#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL +#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL +#define H_BULK_REMOVE_END 0xc000000000000000ULL +#define H_BULK_REMOVE_CODE 0x3000000000000000ULL +#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL +#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL +#define H_BULK_REMOVE_PARM 0x2000000000000000ULL +#define H_BULK_REMOVE_HW 0x3000000000000000ULL +#define H_BULK_REMOVE_RC 0x0c00000000000000ULL +#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL +#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL +#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL +#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL +#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL + +#define H_BULK_REMOVE_MAX_BATCH 4 + +static target_ulong h_bulk_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + int i; + target_ulong rc = H_SUCCESS; + + for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { + target_ulong *tsh = &args[i*2]; + target_ulong tsl = args[i*2 + 1]; + target_ulong v, r, ret; + + if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { + break; + } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) { + return H_PARAMETER; + } + + *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; + *tsh |= H_BULK_REMOVE_RESPONSE; + + if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) { + *tsh |= H_BULK_REMOVE_PARM; + return H_PARAMETER; + } + + ret = remove_hpte(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, + (*tsh & H_BULK_REMOVE_FLAGS) >> 26, + &v, &r); + + *tsh |= ret << 60; + + switch (ret) { + case REMOVE_SUCCESS: + *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; + break; + + case REMOVE_PARM: + rc = H_PARAMETER; + goto exit; + + case REMOVE_HW: + rc = H_HARDWARE; + goto exit; + } + } + exit: + check_tlb_flush(env, true); + + return rc; +} + +static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUPPCState *env = &cpu->env; + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + target_ulong avpn = args[2]; + const ppc_hash_pte64_t *hptes; + target_ulong v, r; + + if (!ppc_hash64_valid_ptex(cpu, ptex)) { + return H_PARAMETER; + } + + hptes = ppc_hash64_map_hptes(cpu, ptex, 1); + v = ppc_hash64_hpte0(cpu, hptes, 0); + r = ppc_hash64_hpte1(cpu, hptes, 0); + ppc_hash64_unmap_hptes(cpu, hptes, ptex, 1); + + if ((v & HPTE64_V_VALID) == 0 || + ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { + return H_NOT_FOUND; + } + + r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | + HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); + r |= (flags << 55) & HPTE64_R_PP0; + r |= (flags << 48) & HPTE64_R_KEY_HI; + r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); + spapr_store_hpte(cpu, ptex, + (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); + ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); + /* Flush the tlb */ + check_tlb_flush(env, true); + /* Don't need a memory barrier, due to qemu's global lock */ + spapr_store_hpte(cpu, ptex, v | HPTE64_V_HPTE_DIRTY, r); + return H_SUCCESS; +} + +static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong ptex = args[1]; + int i, ridx, n_entries = 1; + const ppc_hash_pte64_t *hptes; + + if (!ppc_hash64_valid_ptex(cpu, ptex)) { + return H_PARAMETER; + } + + if (flags & H_READ_4) { + /* Clear the two low order bits */ + ptex &= ~(3ULL); + n_entries = 4; + } + + hptes = ppc_hash64_map_hptes(cpu, ptex, n_entries); + for (i = 0, ridx = 0; i < n_entries; i++) { + args[ridx++] = ppc_hash64_hpte0(cpu, hptes, i); + args[ridx++] = ppc_hash64_hpte1(cpu, hptes, i); + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, n_entries); + + return H_SUCCESS; +} + +struct SpaprPendingHpt { + /* These fields are read-only after initialization */ + int shift; + QemuThread thread; + + /* These fields are protected by the BQL */ + bool complete; + + /* These fields are private to the preparation thread if + * !complete, otherwise protected by the BQL */ + int ret; + void *hpt; +}; + +static void free_pending_hpt(SpaprPendingHpt *pending) +{ + if (pending->hpt) { + qemu_vfree(pending->hpt); + } + + g_free(pending); +} + +static void *hpt_prepare_thread(void *opaque) +{ + SpaprPendingHpt *pending = opaque; + size_t size = 1ULL << pending->shift; + + pending->hpt = qemu_try_memalign(size, size); + if (pending->hpt) { + memset(pending->hpt, 0, size); + pending->ret = H_SUCCESS; + } else { + pending->ret = H_NO_MEM; + } + + bql_lock(); + + if (SPAPR_MACHINE(qdev_get_machine())->pending_hpt == pending) { + /* Ready to go */ + pending->complete = true; + } else { + /* We've been cancelled, clean ourselves up */ + free_pending_hpt(pending); + } + + bql_unlock(); + return NULL; +} + +/* Must be called with BQL held */ +static void cancel_hpt_prepare(SpaprMachineState *spapr) +{ + SpaprPendingHpt *pending = spapr->pending_hpt; + + /* Let the thread know it's cancelled */ + spapr->pending_hpt = NULL; + + if (!pending) { + /* Nothing to do */ + return; + } + + if (!pending->complete) { + /* thread will clean itself up */ + return; + } + + free_pending_hpt(pending); +} + +target_ulong vhyp_mmu_resize_hpt_prepare(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong shift) +{ + SpaprPendingHpt *pending = spapr->pending_hpt; + + if (pending) { + /* something already in progress */ + if (pending->shift == shift) { + /* and it's suitable */ + if (pending->complete) { + return pending->ret; + } else { + return H_LONG_BUSY_ORDER_100_MSEC; + } + } + + /* not suitable, cancel and replace */ + cancel_hpt_prepare(spapr); + } + + if (!shift) { + /* nothing to do */ + return H_SUCCESS; + } + + /* start new prepare */ + + pending = g_new0(SpaprPendingHpt, 1); + pending->shift = shift; + pending->ret = H_HARDWARE; + + qemu_thread_create(&pending->thread, "sPAPR HPT prepare", + hpt_prepare_thread, pending, QEMU_THREAD_DETACHED); + + spapr->pending_hpt = pending; + + /* In theory we could estimate the time more accurately based on + * the new size, but there's not much point */ + return H_LONG_BUSY_ORDER_100_MSEC; +} + +static uint64_t new_hpte_load0(void *htab, uint64_t pteg, int slot) +{ + uint8_t *addr = htab; + + addr += pteg * HASH_PTEG_SIZE_64; + addr += slot * HASH_PTE_SIZE_64; + return ldq_p(addr); +} + +static void new_hpte_store(void *htab, uint64_t pteg, int slot, + uint64_t pte0, uint64_t pte1) +{ + uint8_t *addr = htab; + + addr += pteg * HASH_PTEG_SIZE_64; + addr += slot * HASH_PTE_SIZE_64; + + stq_p(addr, pte0); + stq_p(addr + HPTE64_DW1, pte1); +} + +static int rehash_hpte(PowerPCCPU *cpu, + const ppc_hash_pte64_t *hptes, + void *old_hpt, uint64_t oldsize, + void *new_hpt, uint64_t newsize, + uint64_t pteg, int slot) +{ + uint64_t old_hash_mask = (oldsize >> 7) - 1; + uint64_t new_hash_mask = (newsize >> 7) - 1; + target_ulong pte0 = ppc_hash64_hpte0(cpu, hptes, slot); + target_ulong pte1; + uint64_t avpn; + unsigned base_pg_shift; + uint64_t hash, new_pteg, replace_pte0; + + if (!(pte0 & HPTE64_V_VALID) || !(pte0 & HPTE64_V_BOLTED)) { + return H_SUCCESS; + } + + pte1 = ppc_hash64_hpte1(cpu, hptes, slot); + + base_pg_shift = ppc_hash64_hpte_page_shift_noslb(cpu, pte0, pte1); + assert(base_pg_shift); /* H_ENTER shouldn't allow a bad encoding */ + avpn = HPTE64_V_AVPN_VAL(pte0) & ~(((1ULL << base_pg_shift) - 1) >> 23); + + if (pte0 & HPTE64_V_SECONDARY) { + pteg = ~pteg; + } + + if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_256M) { + uint64_t offset, vsid; + + /* We only have 28 - 23 bits of offset in avpn */ + offset = (avpn & 0x1f) << 23; + vsid = avpn >> 5; + /* We can find more bits from the pteg value */ + if (base_pg_shift < 23) { + offset |= ((vsid ^ pteg) & old_hash_mask) << base_pg_shift; + } + + hash = vsid ^ (offset >> base_pg_shift); + } else if ((pte0 & HPTE64_V_SSIZE) == HPTE64_V_SSIZE_1T) { + uint64_t offset, vsid; + + /* We only have 40 - 23 bits of seg_off in avpn */ + offset = (avpn & 0x1ffff) << 23; + vsid = avpn >> 17; + if (base_pg_shift < 23) { + offset |= ((vsid ^ (vsid << 25) ^ pteg) & old_hash_mask) + << base_pg_shift; + } + + hash = vsid ^ (vsid << 25) ^ (offset >> base_pg_shift); + } else { + error_report("rehash_pte: Bad segment size in HPTE"); + return H_HARDWARE; + } + + new_pteg = hash & new_hash_mask; + if (pte0 & HPTE64_V_SECONDARY) { + assert(~pteg == (hash & old_hash_mask)); + new_pteg = ~new_pteg; + } else { + assert(pteg == (hash & old_hash_mask)); + } + assert((oldsize != newsize) || (pteg == new_pteg)); + replace_pte0 = new_hpte_load0(new_hpt, new_pteg, slot); + /* + * Strictly speaking, we don't need all these tests, since we only + * ever rehash bolted HPTEs. We might in future handle non-bolted + * HPTEs, though so make the logic correct for those cases as + * well. + */ + if (replace_pte0 & HPTE64_V_VALID) { + assert(newsize < oldsize); + if (replace_pte0 & HPTE64_V_BOLTED) { + if (pte0 & HPTE64_V_BOLTED) { + /* Bolted collision, nothing we can do */ + return H_PTEG_FULL; + } else { + /* Discard this hpte */ + return H_SUCCESS; + } + } + } + + new_hpte_store(new_hpt, new_pteg, slot, pte0, pte1); + return H_SUCCESS; +} + +static int rehash_hpt(PowerPCCPU *cpu, + void *old_hpt, uint64_t oldsize, + void *new_hpt, uint64_t newsize) +{ + uint64_t n_ptegs = oldsize >> 7; + uint64_t pteg; + int slot; + int rc; + + for (pteg = 0; pteg < n_ptegs; pteg++) { + hwaddr ptex = pteg * HPTES_PER_GROUP; + const ppc_hash_pte64_t *hptes + = ppc_hash64_map_hptes(cpu, ptex, HPTES_PER_GROUP); + + if (!hptes) { + return H_HARDWARE; + } + + for (slot = 0; slot < HPTES_PER_GROUP; slot++) { + rc = rehash_hpte(cpu, hptes, old_hpt, oldsize, new_hpt, newsize, + pteg, slot); + if (rc != H_SUCCESS) { + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + return rc; + } + } + ppc_hash64_unmap_hptes(cpu, hptes, ptex, HPTES_PER_GROUP); + } + + return H_SUCCESS; +} + +target_ulong vhyp_mmu_resize_hpt_commit(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong flags, + target_ulong shift) +{ + SpaprPendingHpt *pending = spapr->pending_hpt; + int rc; + size_t newsize; + + if (flags != 0) { + return H_PARAMETER; + } + + if (!pending || (pending->shift != shift)) { + /* no matching prepare */ + return H_CLOSED; + } + + if (!pending->complete) { + /* prepare has not completed */ + return H_BUSY; + } + + /* Shouldn't have got past PREPARE without an HPT */ + g_assert(spapr->htab_shift); + + newsize = 1ULL << pending->shift; + rc = rehash_hpt(cpu, spapr->htab, HTAB_SIZE(spapr), + pending->hpt, newsize); + if (rc == H_SUCCESS) { + qemu_vfree(spapr->htab); + spapr->htab = pending->hpt; + spapr->htab_shift = pending->shift; + + push_sregs_to_kvm_pr(spapr); + + pending->hpt = NULL; /* so it's not free()d */ + } + + /* Clean up */ + spapr->pending_hpt = NULL; + free_pending_hpt(pending); + + return rc; +} + +static void hypercall_register_types(void) +{ + /* hcall-pft */ + spapr_register_hypercall(H_ENTER, h_enter); + spapr_register_hypercall(H_REMOVE, h_remove); + spapr_register_hypercall(H_PROTECT, h_protect); + spapr_register_hypercall(H_READ, h_read); + + /* hcall-bulk */ + spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); + +} + +type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 9d4fec2c04..6a5a7f57c7 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), @@ -634,7 +635,7 @@ static void vio_spapr_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = spapr_vio_busdev_realize; - k->reset = spapr_vio_busdev_reset; + device_class_set_legacy_reset(k, spapr_vio_busdev_reset); k->bus_type = TYPE_SPAPR_VIO_BUS; } diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index 09f29be0b9..c02eaacfed 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -28,7 +28,7 @@ target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr, void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt) { - char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + g_autofree char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); vof_build_dt(fdt, spapr->vof); diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index f670e8906c..1f125ce841 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -95,6 +95,14 @@ 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_adu.c +pnv_adu_xscom_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_adu_xscom_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 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 @@ -146,18 +154,6 @@ 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 diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 13cace229b..f378e5c4a9 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -25,9 +25,10 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" #include "qemu/units.h" +#include "exec/page-protection.h" #include "cpu.h" #include "hw/sysbus.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" @@ -43,7 +44,6 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" #include "hw/qdev-properties.h" -#include "ppc405.h" #include @@ -67,29 +67,6 @@ static struct boot_info void *vfdt; } boot_info; -/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ -static void mmubooke_create_initial_mapping(CPUPPCState *env, - target_ulong va, - hwaddr pa) -{ - ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; - - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0x80000000 */ - tlb->EPN = va & TARGET_PAGE_MASK; - tlb->RPN = pa & TARGET_PAGE_MASK; - tlb->PID = 0; - - tlb = &env->tlb.tlbe[1]; - tlb->attr = 0; - tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); - tlb->size = 1U << 31; /* up to 0xffffffff */ - tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; - tlb->PID = 0; -} - static PowerPCCPU *ppc440_init_xilinx(const char *cpu_type, uint32_t sysclk) { PowerPCCPU *cpu; @@ -139,8 +116,9 @@ static void main_cpu_reset(void *opaque) env->gpr[3] = bi->fdt; env->nip = bi->bootstrap_pc; - /* Create a mapping for the kernel. */ - mmubooke_create_initial_mapping(env, 0, 0); + /* Create a mapping spanning the 32bit addr space. */ + booke_set_tlb(&env->tlb.tlbe[0], 0, 0, 1U << 31); + booke_set_tlb(&env->tlb.tlbe[1], 0x80000000, 0x80000000, 1U << 31); env->gpr[6] = tswap32(EPAPR_MAGIC); env->gpr[7] = bi->ima_size; } @@ -157,7 +135,7 @@ static int xilinx_load_device_tree(MachineState *machine, 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) { diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 18c3f92317..b5b6514d79 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -646,7 +646,7 @@ static void vof_dt_memory_available(void *fdt, GArray *claimed, uint64_t base) mem0_reg = fdt_getprop(fdt, offset, "reg", &proplen); g_assert(mem0_reg && proplen == sizeof(uint32_t) * (ac + sc)); if (sc == 2) { - mem0_end = be64_to_cpu(*(uint64_t *)(mem0_reg + sizeof(uint32_t) * ac)); + mem0_end = ldq_be_p(mem0_reg + sizeof(uint32_t) * ac); } else { mem0_end = be32_to_cpu(*(uint32_t *)(mem0_reg + sizeof(uint32_t) * ac)); } @@ -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 deleted file mode 100644 index 8e2211288f..0000000000 --- a/hw/rdma/Kconfig +++ /dev/null @@ -1,3 +0,0 @@ -config VMW_PVRDMA - default y if PCI_DEVICES - depends on PVRDMA && PCI && MSI_NONBROKEN diff --git a/hw/rdma/meson.build b/hw/rdma/meson.build deleted file mode 100644 index 7325f40c32..0000000000 --- a/hw/rdma/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -specific_ss.add(when: 'CONFIG_VMW_PVRDMA', if_true: files( - 'rdma.c', - 'rdma_backend.c', - 'rdma_rm.c', - 'rdma_utils.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.c b/hw/rdma/rdma.c deleted file mode 100644 index 7bec0d0d2c..0000000000 --- a/hw/rdma/rdma.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * RDMA device interface - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * - * 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/rdma/rdma.h" -#include "qemu/module.h" - -static const TypeInfo rdma_hmp_info = { - .name = INTERFACE_RDMA_PROVIDER, - .parent = TYPE_INTERFACE, - .class_size = sizeof(RdmaProviderClass), -}; - -static void rdma_register_types(void) -{ - type_register_static(&rdma_hmp_info); -} - -type_init(rdma_register_types) diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c deleted file mode 100644 index 6dcdfbbbe2..0000000000 --- a/hw/rdma/rdma_backend.c +++ /dev/null @@ -1,1401 +0,0 @@ -/* - * QEMU paravirtual RDMA - Generic RDMA backend - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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/qapi-events-rdma.h" - -#include - -#include "contrib/rdmacm-mux/rdmacm-mux.h" -#include "trace.h" -#include "rdma_utils.h" -#include "rdma_rm.h" -#include "rdma_backend.h" - -#define THR_NAME_LEN 16 -#define THR_POLL_TO 5000 - -#define MAD_HDR_SIZE sizeof(struct ibv_grh) - -typedef struct BackendCtx { - void *up_ctx; - struct ibv_sge sge; /* Used to save MAD recv buffer */ - RdmaBackendQP *backend_qp; /* To maintain recv buffers */ - RdmaBackendSRQ *backend_srq; -} BackendCtx; - -struct backend_umad { - struct ib_user_mad hdr; - char mad[RDMA_MAX_PRIVATE_DATA]; -}; - -static void (*comp_handler)(void *ctx, struct ibv_wc *wc); - -static void dummy_comp_handler(void *ctx, struct ibv_wc *wc) -{ - rdma_error_report("No completion handler is registered"); -} - -static inline void complete_work(enum ibv_wc_status status, uint32_t vendor_err, - void *ctx) -{ - struct ibv_wc wc = {}; - - wc.status = status; - wc.vendor_err = vendor_err; - - comp_handler(ctx, &wc); -} - -static void free_cqe_ctx(gpointer data, gpointer user_data) -{ - BackendCtx *bctx; - RdmaDeviceResources *rdma_dev_res = user_data; - unsigned long cqe_ctx_id = GPOINTER_TO_INT(data); - - bctx = rdma_rm_get_cqe_ctx(rdma_dev_res, cqe_ctx_id); - if (bctx) { - rdma_rm_dealloc_cqe_ctx(rdma_dev_res, cqe_ctx_id); - qatomic_dec(&rdma_dev_res->stats.missing_cqe); - } - g_free(bctx); -} - -static void clean_recv_mads(RdmaBackendDev *backend_dev) -{ - unsigned long cqe_ctx_id; - - do { - cqe_ctx_id = rdma_protected_gqueue_pop_int64(&backend_dev-> - recv_mads_list); - if (cqe_ctx_id != -ENOENT) { - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - free_cqe_ctx(GINT_TO_POINTER(cqe_ctx_id), - backend_dev->rdma_dev_res); - } - } while (cqe_ctx_id != -ENOENT); -} - -static int rdma_poll_cq(RdmaDeviceResources *rdma_dev_res, struct ibv_cq *ibcq) -{ - int i, ne, total_ne = 0; - BackendCtx *bctx; - struct ibv_wc wc[2]; - RdmaProtectedGSList *cqe_ctx_list; - - WITH_QEMU_LOCK_GUARD(&rdma_dev_res->lock) { - do { - ne = ibv_poll_cq(ibcq, ARRAY_SIZE(wc), wc); - - trace_rdma_poll_cq(ne, ibcq); - - for (i = 0; i < ne; i++) { - bctx = rdma_rm_get_cqe_ctx(rdma_dev_res, wc[i].wr_id); - if (unlikely(!bctx)) { - rdma_error_report("No matching ctx for req %"PRId64, - wc[i].wr_id); - continue; - } - - comp_handler(bctx->up_ctx, &wc[i]); - - if (bctx->backend_qp) { - cqe_ctx_list = &bctx->backend_qp->cqe_ctx_list; - } else { - cqe_ctx_list = &bctx->backend_srq->cqe_ctx_list; - } - - rdma_protected_gslist_remove_int32(cqe_ctx_list, wc[i].wr_id); - rdma_rm_dealloc_cqe_ctx(rdma_dev_res, wc[i].wr_id); - g_free(bctx); - } - total_ne += ne; - } while (ne > 0); - qatomic_sub(&rdma_dev_res->stats.missing_cqe, total_ne); - } - - if (ne < 0) { - rdma_error_report("ibv_poll_cq fail, rc=%d, errno=%d", ne, errno); - } - - rdma_dev_res->stats.completions += total_ne; - - return total_ne; -} - -static void *comp_handler_thread(void *arg) -{ - RdmaBackendDev *backend_dev = (RdmaBackendDev *)arg; - int rc; - struct ibv_cq *ev_cq; - void *ev_ctx; - int flags; - GPollFD pfds[1]; - - /* Change to non-blocking mode */ - flags = fcntl(backend_dev->channel->fd, F_GETFL); - rc = fcntl(backend_dev->channel->fd, F_SETFL, flags | O_NONBLOCK); - if (rc < 0) { - rdma_error_report("Failed to change backend channel FD to non-blocking"); - return NULL; - } - - pfds[0].fd = backend_dev->channel->fd; - pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; - - backend_dev->comp_thread.is_running = true; - - while (backend_dev->comp_thread.run) { - do { - rc = qemu_poll_ns(pfds, 1, THR_POLL_TO * (int64_t)SCALE_MS); - if (!rc) { - backend_dev->rdma_dev_res->stats.poll_cq_ppoll_to++; - } - } while (!rc && backend_dev->comp_thread.run); - - if (backend_dev->comp_thread.run) { - rc = ibv_get_cq_event(backend_dev->channel, &ev_cq, &ev_ctx); - if (unlikely(rc)) { - rdma_error_report("ibv_get_cq_event fail, rc=%d, errno=%d", rc, - errno); - continue; - } - - rc = ibv_req_notify_cq(ev_cq, 0); - if (unlikely(rc)) { - rdma_error_report("ibv_req_notify_cq fail, rc=%d, errno=%d", rc, - errno); - } - - backend_dev->rdma_dev_res->stats.poll_cq_from_bk++; - rdma_poll_cq(backend_dev->rdma_dev_res, ev_cq); - - ibv_ack_cq_events(ev_cq, 1); - } - } - - backend_dev->comp_thread.is_running = false; - - qemu_thread_exit(0); - - return NULL; -} - -static inline void disable_rdmacm_mux_async(RdmaBackendDev *backend_dev) -{ - qatomic_set(&backend_dev->rdmacm_mux.can_receive, 0); -} - -static inline void enable_rdmacm_mux_async(RdmaBackendDev *backend_dev) -{ - qatomic_set(&backend_dev->rdmacm_mux.can_receive, sizeof(RdmaCmMuxMsg)); -} - -static inline int rdmacm_mux_can_process_async(RdmaBackendDev *backend_dev) -{ - return qatomic_read(&backend_dev->rdmacm_mux.can_receive); -} - -static int rdmacm_mux_check_op_status(CharBackend *mad_chr_be) -{ - RdmaCmMuxMsg msg = {}; - int ret; - - ret = qemu_chr_fe_read_all(mad_chr_be, (uint8_t *)&msg, sizeof(msg)); - if (ret != sizeof(msg)) { - rdma_error_report("Got invalid message from mux: size %d, expecting %d", - ret, (int)sizeof(msg)); - return -EIO; - } - - trace_rdmacm_mux_check_op_status(msg.hdr.msg_type, msg.hdr.op_code, - msg.hdr.err_code); - - if (msg.hdr.msg_type != RDMACM_MUX_MSG_TYPE_RESP) { - rdma_error_report("Got invalid message type %d", msg.hdr.msg_type); - return -EIO; - } - - if (msg.hdr.err_code != RDMACM_MUX_ERR_CODE_OK) { - rdma_error_report("Operation failed in mux, error code %d", - msg.hdr.err_code); - return -EIO; - } - - return 0; -} - -static int rdmacm_mux_send(RdmaBackendDev *backend_dev, RdmaCmMuxMsg *msg) -{ - int rc = 0; - - msg->hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ; - trace_rdmacm_mux("send", msg->hdr.msg_type, msg->hdr.op_code); - disable_rdmacm_mux_async(backend_dev); - rc = qemu_chr_fe_write(backend_dev->rdmacm_mux.chr_be, - (const uint8_t *)msg, sizeof(*msg)); - if (rc != sizeof(*msg)) { - enable_rdmacm_mux_async(backend_dev); - rdma_error_report("Failed to send request to rdmacm_mux (rc=%d)", rc); - return -EIO; - } - - rc = rdmacm_mux_check_op_status(backend_dev->rdmacm_mux.chr_be); - if (rc) { - rdma_error_report("Failed to execute rdmacm_mux request %d (rc=%d)", - msg->hdr.op_code, rc); - } - - enable_rdmacm_mux_async(backend_dev); - - return 0; -} - -static void stop_backend_thread(RdmaBackendThread *thread) -{ - thread->run = false; - while (thread->is_running) { - sleep(THR_POLL_TO / SCALE_US / 2); - } -} - -static void start_comp_thread(RdmaBackendDev *backend_dev) -{ - char thread_name[THR_NAME_LEN] = {}; - - stop_backend_thread(&backend_dev->comp_thread); - - snprintf(thread_name, sizeof(thread_name), "rdma_comp_%s", - ibv_get_device_name(backend_dev->ib_dev)); - backend_dev->comp_thread.run = true; - qemu_thread_create(&backend_dev->comp_thread.thread, thread_name, - comp_handler_thread, backend_dev, QEMU_THREAD_DETACHED); -} - -void rdma_backend_register_comp_handler(void (*handler)(void *ctx, - struct ibv_wc *wc)) -{ - comp_handler = handler; -} - -void rdma_backend_unregister_comp_handler(void) -{ - rdma_backend_register_comp_handler(dummy_comp_handler); -} - -int rdma_backend_query_port(RdmaBackendDev *backend_dev, - struct ibv_port_attr *port_attr) -{ - int rc; - - rc = ibv_query_port(backend_dev->context, backend_dev->port_num, port_attr); - if (rc) { - rdma_error_report("ibv_query_port fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq) -{ - int polled; - - rdma_dev_res->stats.poll_cq_from_guest++; - polled = rdma_poll_cq(rdma_dev_res, cq->ibcq); - if (!polled) { - rdma_dev_res->stats.poll_cq_from_guest_empty++; - } -} - -static GHashTable *ah_hash; - -static struct ibv_ah *create_ah(RdmaBackendDev *backend_dev, struct ibv_pd *pd, - uint8_t sgid_idx, union ibv_gid *dgid) -{ - GBytes *ah_key = g_bytes_new(dgid, sizeof(*dgid)); - struct ibv_ah *ah = g_hash_table_lookup(ah_hash, ah_key); - - if (ah) { - trace_rdma_create_ah_cache_hit(be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); - g_bytes_unref(ah_key); - } else { - struct ibv_ah_attr ah_attr = { - .is_global = 1, - .port_num = backend_dev->port_num, - .grh.hop_limit = 1, - }; - - ah_attr.grh.dgid = *dgid; - ah_attr.grh.sgid_index = sgid_idx; - - ah = ibv_create_ah(pd, &ah_attr); - if (ah) { - g_hash_table_insert(ah_hash, ah_key, ah); - } else { - g_bytes_unref(ah_key); - rdma_error_report("Failed to create AH for gid <0x%" PRIx64", 0x%"PRIx64">", - be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); - } - - trace_rdma_create_ah_cache_miss(be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); - } - - return ah; -} - -static void destroy_ah_hash_key(gpointer data) -{ - g_bytes_unref(data); -} - -static void destroy_ah_hast_data(gpointer data) -{ - struct ibv_ah *ah = data; - - ibv_destroy_ah(ah); -} - -static void ah_cache_init(void) -{ - ah_hash = g_hash_table_new_full(g_bytes_hash, g_bytes_equal, - destroy_ah_hash_key, destroy_ah_hast_data); -} - -#ifdef LEGACY_RDMA_REG_MR -static int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, - struct ibv_sge *sge, uint8_t num_sge, - uint64_t *total_length) -{ - RdmaRmMR *mr; - int idx; - - for (idx = 0; idx < num_sge; idx++) { - mr = rdma_rm_get_mr(rdma_dev_res, sge[idx].lkey); - if (unlikely(!mr)) { - rdma_error_report("Invalid lkey 0x%x", sge[idx].lkey); - return VENDOR_ERR_INVLKEY | sge[idx].lkey; - } - - sge[idx].addr = (uintptr_t)mr->virt + sge[idx].addr - mr->start; - sge[idx].lkey = rdma_backend_mr_lkey(&mr->backend_mr); - - *total_length += sge[idx].length; - } - - return 0; -} -#else -static inline int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, - struct ibv_sge *sge, uint8_t num_sge, - uint64_t *total_length) -{ - int idx; - - for (idx = 0; idx < num_sge; idx++) { - *total_length += sge[idx].length; - } - return 0; -} -#endif - -static void trace_mad_message(const char *title, char *buf, int len) -{ - int i; - char *b = g_malloc0(len * 3 + 1); - char b1[4]; - - for (i = 0; i < len; i++) { - sprintf(b1, "%.2X ", buf[i] & 0x000000FF); - strcat(b, b1); - } - - trace_rdma_mad_message(title, len, b); - - g_free(b); -} - -static int mad_send(RdmaBackendDev *backend_dev, uint8_t sgid_idx, - union ibv_gid *sgid, struct ibv_sge *sge, uint32_t num_sge) -{ - RdmaCmMuxMsg msg = {}; - char *hdr, *data; - int ret; - - if (num_sge != 2) { - return -EINVAL; - } - - msg.hdr.op_code = RDMACM_MUX_OP_CODE_MAD; - memcpy(msg.hdr.sgid.raw, sgid->raw, sizeof(msg.hdr.sgid)); - - msg.umad_len = sge[0].length + sge[1].length; - - if (msg.umad_len > sizeof(msg.umad.mad)) { - return -ENOMEM; - } - - msg.umad.hdr.addr.qpn = htobe32(1); - msg.umad.hdr.addr.grh_present = 1; - msg.umad.hdr.addr.gid_index = sgid_idx; - memcpy(msg.umad.hdr.addr.gid, sgid->raw, sizeof(msg.umad.hdr.addr.gid)); - msg.umad.hdr.addr.hop_limit = 0xFF; - - hdr = rdma_pci_dma_map(backend_dev->dev, sge[0].addr, sge[0].length); - if (!hdr) { - return -ENOMEM; - } - data = rdma_pci_dma_map(backend_dev->dev, sge[1].addr, sge[1].length); - if (!data) { - rdma_pci_dma_unmap(backend_dev->dev, hdr, sge[0].length); - return -ENOMEM; - } - - memcpy(&msg.umad.mad[0], hdr, sge[0].length); - memcpy(&msg.umad.mad[sge[0].length], data, sge[1].length); - - rdma_pci_dma_unmap(backend_dev->dev, data, sge[1].length); - rdma_pci_dma_unmap(backend_dev->dev, hdr, sge[0].length); - - trace_mad_message("send", msg.umad.mad, msg.umad_len); - - ret = rdmacm_mux_send(backend_dev, &msg); - if (ret) { - rdma_error_report("Failed to send MAD to rdma_umadmux (%d)", ret); - return -EIO; - } - - return 0; -} - -void rdma_backend_post_send(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, - uint8_t sgid_idx, union ibv_gid *sgid, - union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey, - void *ctx) -{ - BackendCtx *bctx; - uint32_t bctx_id; - int rc; - struct ibv_send_wr wr = {}, *bad_wr; - - if (!qp->ibqp) { /* This field is not initialized for QP0 and QP1 */ - if (qp_type == IBV_QPT_SMI) { - rdma_error_report("Got QP0 request"); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); - } else if (qp_type == IBV_QPT_GSI) { - rc = mad_send(backend_dev, sgid_idx, sgid, sge, num_sge); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_MAD_SEND, ctx); - backend_dev->rdma_dev_res->stats.mad_tx_err++; - } else { - complete_work(IBV_WC_SUCCESS, 0, ctx); - backend_dev->rdma_dev_res->stats.mad_tx++; - } - } - return; - } - - bctx = g_malloc0(sizeof(*bctx)); - bctx->up_ctx = ctx; - bctx->backend_qp = qp; - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto err_free_bctx; - } - - rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); - - rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, - &backend_dev->rdma_dev_res->stats.tx_len); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - goto err_dealloc_cqe_ctx; - } - - if (qp_type == IBV_QPT_UD) { - wr.wr.ud.ah = create_ah(backend_dev, qp->ibpd, sgid_idx, dgid); - if (!wr.wr.ud.ah) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - wr.wr.ud.remote_qpn = dqpn; - wr.wr.ud.remote_qkey = dqkey; - } - - wr.num_sge = num_sge; - wr.opcode = IBV_WR_SEND; - wr.send_flags = IBV_SEND_SIGNALED; - wr.sg_list = sge; - wr.wr_id = bctx_id; - - rc = ibv_post_send(qp->ibqp, &wr, &bad_wr); - if (rc) { - rdma_error_report("ibv_post_send fail, qpn=0x%x, rc=%d, errno=%d", - qp->ibqp->qp_num, rc, errno); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - backend_dev->rdma_dev_res->stats.tx++; - - return; - -err_dealloc_cqe_ctx: - backend_dev->rdma_dev_res->stats.tx_err++; - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); - -err_free_bctx: - g_free(bctx); -} - -static unsigned int save_mad_recv_buffer(RdmaBackendDev *backend_dev, - struct ibv_sge *sge, uint32_t num_sge, - void *ctx) -{ - BackendCtx *bctx; - int rc; - uint32_t bctx_id; - - if (num_sge != 1) { - rdma_error_report("Invalid num_sge (%d), expecting 1", num_sge); - return VENDOR_ERR_INV_NUM_SGE; - } - - if (sge[0].length < RDMA_MAX_PRIVATE_DATA + sizeof(struct ibv_grh)) { - rdma_error_report("Too small buffer for MAD"); - return VENDOR_ERR_INV_MAD_BUFF; - } - - bctx = g_malloc0(sizeof(*bctx)); - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - g_free(bctx); - return VENDOR_ERR_NOMEM; - } - - bctx->up_ctx = ctx; - bctx->sge = *sge; - - rdma_protected_gqueue_append_int64(&backend_dev->recv_mads_list, bctx_id); - - return 0; -} - -void rdma_backend_post_recv(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, void *ctx) -{ - BackendCtx *bctx; - uint32_t bctx_id; - int rc; - struct ibv_recv_wr wr = {}, *bad_wr; - - if (!qp->ibqp) { /* This field does not get initialized for QP0 and QP1 */ - if (qp_type == IBV_QPT_SMI) { - rdma_error_report("Got QP0 request"); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); - } - if (qp_type == IBV_QPT_GSI) { - rc = save_mad_recv_buffer(backend_dev, sge, num_sge, ctx); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - backend_dev->rdma_dev_res->stats.mad_rx_bufs_err++; - } else { - backend_dev->rdma_dev_res->stats.mad_rx_bufs++; - } - } - return; - } - - bctx = g_malloc0(sizeof(*bctx)); - bctx->up_ctx = ctx; - bctx->backend_qp = qp; - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto err_free_bctx; - } - - rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); - - rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, - &backend_dev->rdma_dev_res->stats.rx_bufs_len); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - goto err_dealloc_cqe_ctx; - } - - wr.num_sge = num_sge; - wr.sg_list = sge; - wr.wr_id = bctx_id; - rc = ibv_post_recv(qp->ibqp, &wr, &bad_wr); - if (rc) { - rdma_error_report("ibv_post_recv fail, qpn=0x%x, rc=%d, errno=%d", - qp->ibqp->qp_num, rc, errno); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - backend_dev->rdma_dev_res->stats.rx_bufs++; - - return; - -err_dealloc_cqe_ctx: - backend_dev->rdma_dev_res->stats.rx_bufs_err++; - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); - -err_free_bctx: - g_free(bctx); -} - -void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, - RdmaBackendSRQ *srq, struct ibv_sge *sge, - uint32_t num_sge, void *ctx) -{ - BackendCtx *bctx; - uint32_t bctx_id; - int rc; - struct ibv_recv_wr wr = {}, *bad_wr; - - bctx = g_malloc0(sizeof(*bctx)); - bctx->up_ctx = ctx; - bctx->backend_srq = srq; - - rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); - if (unlikely(rc)) { - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto err_free_bctx; - } - - rdma_protected_gslist_append_int32(&srq->cqe_ctx_list, bctx_id); - - rc = build_host_sge_array(backend_dev->rdma_dev_res, sge, num_sge, - &backend_dev->rdma_dev_res->stats.rx_bufs_len); - if (rc) { - complete_work(IBV_WC_GENERAL_ERR, rc, ctx); - goto err_dealloc_cqe_ctx; - } - - wr.num_sge = num_sge; - wr.sg_list = sge; - wr.wr_id = bctx_id; - rc = ibv_post_srq_recv(srq->ibsrq, &wr, &bad_wr); - if (rc) { - rdma_error_report("ibv_post_srq_recv fail, srqn=0x%x, rc=%d, errno=%d", - srq->ibsrq->handle, rc, errno); - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto err_dealloc_cqe_ctx; - } - - qatomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); - backend_dev->rdma_dev_res->stats.rx_bufs++; - backend_dev->rdma_dev_res->stats.rx_srq++; - - return; - -err_dealloc_cqe_ctx: - backend_dev->rdma_dev_res->stats.rx_bufs_err++; - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); - -err_free_bctx: - g_free(bctx); -} - -int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd) -{ - pd->ibpd = ibv_alloc_pd(backend_dev->context); - - if (!pd->ibpd) { - rdma_error_report("ibv_alloc_pd fail, errno=%d", errno); - return -EIO; - } - - return 0; -} - -void rdma_backend_destroy_pd(RdmaBackendPD *pd) -{ - if (pd->ibpd) { - ibv_dealloc_pd(pd->ibpd); - } -} - -int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, - size_t length, uint64_t guest_start, int access) -{ -#ifdef LEGACY_RDMA_REG_MR - mr->ibmr = ibv_reg_mr(pd->ibpd, addr, length, access); -#else - mr->ibmr = ibv_reg_mr_iova(pd->ibpd, addr, length, guest_start, access); -#endif - if (!mr->ibmr) { - rdma_error_report("ibv_reg_mr fail, errno=%d", errno); - return -EIO; - } - - mr->ibpd = pd->ibpd; - - return 0; -} - -void rdma_backend_destroy_mr(RdmaBackendMR *mr) -{ - if (mr->ibmr) { - ibv_dereg_mr(mr->ibmr); - } -} - -int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq, - int cqe) -{ - int rc; - - cq->ibcq = ibv_create_cq(backend_dev->context, cqe + 1, NULL, - backend_dev->channel, 0); - if (!cq->ibcq) { - rdma_error_report("ibv_create_cq fail, errno=%d", errno); - return -EIO; - } - - rc = ibv_req_notify_cq(cq->ibcq, 0); - if (rc) { - rdma_warn_report("ibv_req_notify_cq fail, rc=%d, errno=%d", rc, errno); - } - - cq->backend_dev = backend_dev; - - return 0; -} - -void rdma_backend_destroy_cq(RdmaBackendCQ *cq) -{ - if (cq->ibcq) { - ibv_destroy_cq(cq->ibcq); - } -} - -int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, - RdmaBackendPD *pd, RdmaBackendCQ *scq, - RdmaBackendCQ *rcq, RdmaBackendSRQ *srq, - uint32_t max_send_wr, uint32_t max_recv_wr, - uint32_t max_send_sge, uint32_t max_recv_sge) -{ - struct ibv_qp_init_attr attr = {}; - - qp->ibqp = 0; - - switch (qp_type) { - case IBV_QPT_GSI: - return 0; - - case IBV_QPT_RC: - /* fall through */ - case IBV_QPT_UD: - /* do nothing */ - break; - - default: - rdma_error_report("Unsupported QP type %d", qp_type); - return -EIO; - } - - attr.qp_type = qp_type; - attr.send_cq = scq->ibcq; - attr.recv_cq = rcq->ibcq; - attr.cap.max_send_wr = max_send_wr; - attr.cap.max_recv_wr = max_recv_wr; - attr.cap.max_send_sge = max_send_sge; - attr.cap.max_recv_sge = max_recv_sge; - if (srq) { - attr.srq = srq->ibsrq; - } - - qp->ibqp = ibv_create_qp(pd->ibpd, &attr); - if (!qp->ibqp) { - rdma_error_report("ibv_create_qp fail, errno=%d", errno); - return -EIO; - } - - rdma_protected_gslist_init(&qp->cqe_ctx_list); - - qp->ibpd = pd->ibpd; - - /* TODO: Query QP to get max_inline_data and save it to be used in send */ - - return 0; -} - -int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint32_t qkey) -{ - struct ibv_qp_attr attr = {}; - int rc, attr_mask; - - attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT; - attr.qp_state = IBV_QPS_INIT; - attr.pkey_index = 0; - attr.port_num = backend_dev->port_num; - - switch (qp_type) { - case IBV_QPT_RC: - attr_mask |= IBV_QP_ACCESS_FLAGS; - trace_rdma_backend_rc_qp_state_init(qp->ibqp->qp_num); - break; - - case IBV_QPT_UD: - attr.qkey = qkey; - attr_mask |= IBV_QP_QKEY; - trace_rdma_backend_ud_qp_state_init(qp->ibqp->qp_num, qkey); - break; - - default: - rdma_error_report("Unsupported QP type %d", qp_type); - return -EIO; - } - - rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); - if (rc) { - rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - uint32_t rq_psn, uint32_t qkey, bool use_qkey) -{ - struct ibv_qp_attr attr = {}; - union ibv_gid ibv_gid = { - .global.interface_id = dgid->global.interface_id, - .global.subnet_prefix = dgid->global.subnet_prefix - }; - int rc, attr_mask; - - attr.qp_state = IBV_QPS_RTR; - attr_mask = IBV_QP_STATE; - - qp->sgid_idx = sgid_idx; - - switch (qp_type) { - case IBV_QPT_RC: - attr.path_mtu = IBV_MTU_1024; - attr.dest_qp_num = dqpn; - attr.max_dest_rd_atomic = 1; - attr.min_rnr_timer = 12; - attr.ah_attr.port_num = backend_dev->port_num; - attr.ah_attr.is_global = 1; - attr.ah_attr.grh.hop_limit = 1; - attr.ah_attr.grh.dgid = ibv_gid; - attr.ah_attr.grh.sgid_index = qp->sgid_idx; - attr.rq_psn = rq_psn; - - attr_mask |= IBV_QP_AV | IBV_QP_PATH_MTU | IBV_QP_DEST_QPN | - IBV_QP_RQ_PSN | IBV_QP_MAX_DEST_RD_ATOMIC | - IBV_QP_MIN_RNR_TIMER; - - trace_rdma_backend_rc_qp_state_rtr(qp->ibqp->qp_num, - be64_to_cpu(ibv_gid.global. - subnet_prefix), - be64_to_cpu(ibv_gid.global. - interface_id), - qp->sgid_idx, dqpn, rq_psn); - break; - - case IBV_QPT_UD: - if (use_qkey) { - attr.qkey = qkey; - attr_mask |= IBV_QP_QKEY; - } - trace_rdma_backend_ud_qp_state_rtr(qp->ibqp->qp_num, use_qkey ? qkey : - 0); - break; - } - - rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); - if (rc) { - rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, - uint32_t sq_psn, uint32_t qkey, bool use_qkey) -{ - struct ibv_qp_attr attr = {}; - int rc, attr_mask; - - attr.qp_state = IBV_QPS_RTS; - attr.sq_psn = sq_psn; - attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN; - - switch (qp_type) { - case IBV_QPT_RC: - attr.timeout = 14; - attr.retry_cnt = 7; - attr.rnr_retry = 7; - attr.max_rd_atomic = 1; - - attr_mask |= IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | - IBV_QP_MAX_QP_RD_ATOMIC; - trace_rdma_backend_rc_qp_state_rts(qp->ibqp->qp_num, sq_psn); - break; - - case IBV_QPT_UD: - if (use_qkey) { - attr.qkey = qkey; - attr_mask |= IBV_QP_QKEY; - } - trace_rdma_backend_ud_qp_state_rts(qp->ibqp->qp_num, sq_psn, - use_qkey ? qkey : 0); - break; - } - - rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); - if (rc) { - rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - return 0; -} - -int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr) -{ - if (!qp->ibqp) { - attr->qp_state = IBV_QPS_RTS; - return 0; - } - - return ibv_query_qp(qp->ibqp, attr, attr_mask, init_attr); -} - -void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res) -{ - if (qp->ibqp) { - ibv_destroy_qp(qp->ibqp); - } - g_slist_foreach(qp->cqe_ctx_list.list, free_cqe_ctx, dev_res); - rdma_protected_gslist_destroy(&qp->cqe_ctx_list); -} - -int rdma_backend_create_srq(RdmaBackendSRQ *srq, RdmaBackendPD *pd, - uint32_t max_wr, uint32_t max_sge, - uint32_t srq_limit) -{ - struct ibv_srq_init_attr srq_init_attr = {}; - - srq_init_attr.attr.max_wr = max_wr; - srq_init_attr.attr.max_sge = max_sge; - srq_init_attr.attr.srq_limit = srq_limit; - - srq->ibsrq = ibv_create_srq(pd->ibpd, &srq_init_attr); - if (!srq->ibsrq) { - rdma_error_report("ibv_create_srq failed, errno=%d", errno); - return -EIO; - } - - rdma_protected_gslist_init(&srq->cqe_ctx_list); - - return 0; -} - -int rdma_backend_query_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr) -{ - if (!srq->ibsrq) { - return -EINVAL; - } - - return ibv_query_srq(srq->ibsrq, srq_attr); -} - -int rdma_backend_modify_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr, - int srq_attr_mask) -{ - if (!srq->ibsrq) { - return -EINVAL; - } - - return ibv_modify_srq(srq->ibsrq, srq_attr, srq_attr_mask); -} - -void rdma_backend_destroy_srq(RdmaBackendSRQ *srq, RdmaDeviceResources *dev_res) -{ - if (srq->ibsrq) { - ibv_destroy_srq(srq->ibsrq); - } - g_slist_foreach(srq->cqe_ctx_list.list, free_cqe_ctx, dev_res); - rdma_protected_gslist_destroy(&srq->cqe_ctx_list); -} - -#define CHK_ATTR(req, dev, member, fmt) ({ \ - trace_rdma_check_dev_attr(#member, dev.member, req->member); \ - if (req->member > dev.member) { \ - rdma_warn_report("%s = "fmt" is higher than host device capability "fmt, \ - #member, req->member, dev.member); \ - req->member = dev.member; \ - } \ -}) - -static int init_device_caps(RdmaBackendDev *backend_dev, - struct ibv_device_attr *dev_attr) -{ - struct ibv_device_attr bk_dev_attr; - int rc; - - rc = ibv_query_device(backend_dev->context, &bk_dev_attr); - if (rc) { - rdma_error_report("ibv_query_device fail, rc=%d, errno=%d", rc, errno); - return -EIO; - } - - dev_attr->max_sge = MAX_SGE; - dev_attr->max_srq_sge = MAX_SGE; - - CHK_ATTR(dev_attr, bk_dev_attr, max_mr_size, "%" PRId64); - CHK_ATTR(dev_attr, bk_dev_attr, max_qp, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_sge, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_cq, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_mr, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_pd, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_qp_rd_atom, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_qp_init_rd_atom, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_ah, "%d"); - CHK_ATTR(dev_attr, bk_dev_attr, max_srq, "%d"); - - return 0; -} - -static inline void build_mad_hdr(struct ibv_grh *grh, union ibv_gid *sgid, - union ibv_gid *my_gid, int paylen) -{ - grh->paylen = htons(paylen); - grh->sgid = *sgid; - grh->dgid = *my_gid; -} - -static void process_incoming_mad_req(RdmaBackendDev *backend_dev, - RdmaCmMuxMsg *msg) -{ - unsigned long cqe_ctx_id; - BackendCtx *bctx; - char *mad; - - trace_mad_message("recv", msg->umad.mad, msg->umad_len); - - cqe_ctx_id = rdma_protected_gqueue_pop_int64(&backend_dev->recv_mads_list); - if (cqe_ctx_id == -ENOENT) { - rdma_warn_report("No more free MADs buffers, waiting for a while"); - sleep(THR_POLL_TO); - return; - } - - bctx = rdma_rm_get_cqe_ctx(backend_dev->rdma_dev_res, cqe_ctx_id); - if (unlikely(!bctx)) { - rdma_error_report("No matching ctx for req %ld", cqe_ctx_id); - backend_dev->rdma_dev_res->stats.mad_rx_err++; - return; - } - - mad = rdma_pci_dma_map(backend_dev->dev, bctx->sge.addr, - bctx->sge.length); - if (!mad || bctx->sge.length < msg->umad_len + MAD_HDR_SIZE) { - backend_dev->rdma_dev_res->stats.mad_rx_err++; - complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_INV_MAD_BUFF, - bctx->up_ctx); - } else { - struct ibv_wc wc = {}; - memset(mad, 0, bctx->sge.length); - build_mad_hdr((struct ibv_grh *)mad, - (union ibv_gid *)&msg->umad.hdr.addr.gid, &msg->hdr.sgid, - msg->umad_len); - memcpy(&mad[MAD_HDR_SIZE], msg->umad.mad, msg->umad_len); - rdma_pci_dma_unmap(backend_dev->dev, mad, bctx->sge.length); - - wc.byte_len = msg->umad_len; - wc.status = IBV_WC_SUCCESS; - wc.wc_flags = IBV_WC_GRH; - backend_dev->rdma_dev_res->stats.mad_rx++; - comp_handler(bctx->up_ctx, &wc); - } - - g_free(bctx); - rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, cqe_ctx_id); -} - -static inline int rdmacm_mux_can_receive(void *opaque) -{ - RdmaBackendDev *backend_dev = (RdmaBackendDev *)opaque; - - return rdmacm_mux_can_process_async(backend_dev); -} - -static void rdmacm_mux_read(void *opaque, const uint8_t *buf, int size) -{ - RdmaBackendDev *backend_dev = (RdmaBackendDev *)opaque; - RdmaCmMuxMsg *msg = (RdmaCmMuxMsg *)buf; - - trace_rdmacm_mux("read", msg->hdr.msg_type, msg->hdr.op_code); - - if (msg->hdr.msg_type != RDMACM_MUX_MSG_TYPE_REQ && - msg->hdr.op_code != RDMACM_MUX_OP_CODE_MAD) { - rdma_error_report("Error: Not a MAD request, skipping"); - return; - } - process_incoming_mad_req(backend_dev, msg); -} - -static int mad_init(RdmaBackendDev *backend_dev, CharBackend *mad_chr_be) -{ - int ret; - - backend_dev->rdmacm_mux.chr_be = mad_chr_be; - - ret = qemu_chr_fe_backend_connected(backend_dev->rdmacm_mux.chr_be); - if (!ret) { - rdma_error_report("Missing chardev for MAD multiplexer"); - return -EIO; - } - - rdma_protected_gqueue_init(&backend_dev->recv_mads_list); - - enable_rdmacm_mux_async(backend_dev); - - qemu_chr_fe_set_handlers(backend_dev->rdmacm_mux.chr_be, - rdmacm_mux_can_receive, rdmacm_mux_read, NULL, - NULL, backend_dev, NULL, true); - - return 0; -} - -static void mad_stop(RdmaBackendDev *backend_dev) -{ - clean_recv_mads(backend_dev); -} - -static void mad_fini(RdmaBackendDev *backend_dev) -{ - disable_rdmacm_mux_async(backend_dev); - qemu_chr_fe_disconnect(backend_dev->rdmacm_mux.chr_be); - rdma_protected_gqueue_destroy(&backend_dev->recv_mads_list); -} - -int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev, - union ibv_gid *gid) -{ - union ibv_gid sgid; - int ret; - int i = 0; - - do { - ret = ibv_query_gid(backend_dev->context, backend_dev->port_num, i, - &sgid); - i++; - } while (!ret && (memcmp(&sgid, gid, sizeof(*gid)))); - - trace_rdma_backend_get_gid_index(be64_to_cpu(gid->global.subnet_prefix), - be64_to_cpu(gid->global.interface_id), - i - 1); - - return ret ? ret : i - 1; -} - -int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid) -{ - RdmaCmMuxMsg msg = {}; - int ret; - - trace_rdma_backend_gid_change("add", be64_to_cpu(gid->global.subnet_prefix), - be64_to_cpu(gid->global.interface_id)); - - msg.hdr.op_code = RDMACM_MUX_OP_CODE_REG; - memcpy(msg.hdr.sgid.raw, gid->raw, sizeof(msg.hdr.sgid)); - - ret = rdmacm_mux_send(backend_dev, &msg); - if (ret) { - rdma_error_report("Failed to register GID to rdma_umadmux (%d)", ret); - return -EIO; - } - - qapi_event_send_rdma_gid_status_changed(ifname, true, - gid->global.subnet_prefix, - gid->global.interface_id); - - return ret; -} - -int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid) -{ - RdmaCmMuxMsg msg = {}; - int ret; - - trace_rdma_backend_gid_change("del", be64_to_cpu(gid->global.subnet_prefix), - be64_to_cpu(gid->global.interface_id)); - - msg.hdr.op_code = RDMACM_MUX_OP_CODE_UNREG; - memcpy(msg.hdr.sgid.raw, gid->raw, sizeof(msg.hdr.sgid)); - - ret = rdmacm_mux_send(backend_dev, &msg); - if (ret) { - rdma_error_report("Failed to unregister GID from rdma_umadmux (%d)", - ret); - return -EIO; - } - - qapi_event_send_rdma_gid_status_changed(ifname, false, - gid->global.subnet_prefix, - gid->global.interface_id); - - return 0; -} - -int rdma_backend_init(RdmaBackendDev *backend_dev, PCIDevice *pdev, - RdmaDeviceResources *rdma_dev_res, - const char *backend_device_name, uint8_t port_num, - struct ibv_device_attr *dev_attr, CharBackend *mad_chr_be) -{ - int i; - int ret = 0; - int num_ibv_devices; - struct ibv_device **dev_list; - - memset(backend_dev, 0, sizeof(*backend_dev)); - - backend_dev->dev = pdev; - backend_dev->port_num = port_num; - backend_dev->rdma_dev_res = rdma_dev_res; - - rdma_backend_register_comp_handler(dummy_comp_handler); - - dev_list = ibv_get_device_list(&num_ibv_devices); - if (!dev_list) { - rdma_error_report("Failed to get IB devices list"); - return -EIO; - } - - if (num_ibv_devices == 0) { - rdma_error_report("No IB devices were found"); - ret = -ENXIO; - goto out_free_dev_list; - } - - if (backend_device_name) { - for (i = 0; dev_list[i]; ++i) { - if (!strcmp(ibv_get_device_name(dev_list[i]), - backend_device_name)) { - break; - } - } - - backend_dev->ib_dev = dev_list[i]; - if (!backend_dev->ib_dev) { - rdma_error_report("Failed to find IB device %s", - backend_device_name); - ret = -EIO; - goto out_free_dev_list; - } - } else { - backend_dev->ib_dev = *dev_list; - } - - rdma_info_report("uverb device %s", backend_dev->ib_dev->dev_name); - - backend_dev->context = ibv_open_device(backend_dev->ib_dev); - if (!backend_dev->context) { - rdma_error_report("Failed to open IB device %s", - ibv_get_device_name(backend_dev->ib_dev)); - ret = -EIO; - goto out; - } - - backend_dev->channel = ibv_create_comp_channel(backend_dev->context); - if (!backend_dev->channel) { - rdma_error_report("Failed to create IB communication channel"); - ret = -EIO; - goto out_close_device; - } - - ret = init_device_caps(backend_dev, dev_attr); - if (ret) { - rdma_error_report("Failed to initialize device capabilities"); - ret = -EIO; - goto out_destroy_comm_channel; - } - - - ret = mad_init(backend_dev, mad_chr_be); - if (ret) { - rdma_error_report("Failed to initialize mad"); - ret = -EIO; - goto out_destroy_comm_channel; - } - - backend_dev->comp_thread.run = false; - backend_dev->comp_thread.is_running = false; - - ah_cache_init(); - - goto out_free_dev_list; - -out_destroy_comm_channel: - ibv_destroy_comp_channel(backend_dev->channel); - -out_close_device: - ibv_close_device(backend_dev->context); - -out_free_dev_list: - ibv_free_device_list(dev_list); - -out: - return ret; -} - - -void rdma_backend_start(RdmaBackendDev *backend_dev) -{ - start_comp_thread(backend_dev); -} - -void rdma_backend_stop(RdmaBackendDev *backend_dev) -{ - mad_stop(backend_dev); - stop_backend_thread(&backend_dev->comp_thread); -} - -void rdma_backend_fini(RdmaBackendDev *backend_dev) -{ - mad_fini(backend_dev); - g_hash_table_destroy(ah_hash); - ibv_destroy_comp_channel(backend_dev->channel); - ibv_close_device(backend_dev->context); -} diff --git a/hw/rdma/rdma_backend.h b/hw/rdma/rdma_backend.h deleted file mode 100644 index 225af481e0..0000000000 --- a/hw/rdma/rdma_backend.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * RDMA device: Definitions of Backend Device functions - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 RDMA_BACKEND_H -#define RDMA_BACKEND_H - -#include "qapi/error.h" -#include "chardev/char-fe.h" - -#include "rdma_rm_defs.h" -#include "rdma_backend_defs.h" - -/* Vendor Errors */ -#define VENDOR_ERR_FAIL_BACKEND 0x201 -#define VENDOR_ERR_TOO_MANY_SGES 0x202 -#define VENDOR_ERR_NOMEM 0x203 -#define VENDOR_ERR_QP0 0x204 -#define VENDOR_ERR_INV_NUM_SGE 0x205 -#define VENDOR_ERR_MAD_SEND 0x206 -#define VENDOR_ERR_INVLKEY 0x207 -#define VENDOR_ERR_MR_SMALL 0x208 -#define VENDOR_ERR_INV_MAD_BUFF 0x209 -#define VENDOR_ERR_INV_GID_IDX 0x210 - -/* Add definition for QP0 and QP1 as there is no userspace enums for them */ -enum ibv_special_qp_type { - IBV_QPT_SMI = 0, - IBV_QPT_GSI = 1, -}; - -static inline uint32_t rdma_backend_qpn(const RdmaBackendQP *qp) -{ - return qp->ibqp ? qp->ibqp->qp_num : 1; -} - -static inline uint32_t rdma_backend_mr_lkey(const RdmaBackendMR *mr) -{ - return mr->ibmr ? mr->ibmr->lkey : 0; -} - -static inline uint32_t rdma_backend_mr_rkey(const RdmaBackendMR *mr) -{ - return mr->ibmr ? mr->ibmr->rkey : 0; -} - -int rdma_backend_init(RdmaBackendDev *backend_dev, PCIDevice *pdev, - RdmaDeviceResources *rdma_dev_res, - const char *backend_device_name, uint8_t port_num, - struct ibv_device_attr *dev_attr, - CharBackend *mad_chr_be); -void rdma_backend_fini(RdmaBackendDev *backend_dev); -int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid); -int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname, - union ibv_gid *gid); -int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev, - union ibv_gid *gid); -void rdma_backend_start(RdmaBackendDev *backend_dev); -void rdma_backend_stop(RdmaBackendDev *backend_dev); -void rdma_backend_register_comp_handler(void (*handler)(void *ctx, - struct ibv_wc *wc)); -void rdma_backend_unregister_comp_handler(void); - -int rdma_backend_query_port(RdmaBackendDev *backend_dev, - struct ibv_port_attr *port_attr); -int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd); -void rdma_backend_destroy_pd(RdmaBackendPD *pd); - -int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, - size_t length, uint64_t guest_start, int access); -void rdma_backend_destroy_mr(RdmaBackendMR *mr); - -int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq, - int cqe); -void rdma_backend_destroy_cq(RdmaBackendCQ *cq); -void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq); - -int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, - RdmaBackendPD *pd, RdmaBackendCQ *scq, - RdmaBackendCQ *rcq, RdmaBackendSRQ *srq, - uint32_t max_send_wr, uint32_t max_recv_wr, - uint32_t max_send_sge, uint32_t max_recv_sge); -int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint32_t qkey); -int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - uint32_t rq_psn, uint32_t qkey, bool use_qkey); -int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, - uint32_t sq_psn, uint32_t qkey, bool use_qkey); -int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr); -void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res); - -void rdma_backend_post_send(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, - uint8_t sgid_idx, union ibv_gid *sgid, - union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey, - void *ctx); -void rdma_backend_post_recv(RdmaBackendDev *backend_dev, - RdmaBackendQP *qp, uint8_t qp_type, - struct ibv_sge *sge, uint32_t num_sge, void *ctx); - -int rdma_backend_create_srq(RdmaBackendSRQ *srq, RdmaBackendPD *pd, - uint32_t max_wr, uint32_t max_sge, - uint32_t srq_limit); -int rdma_backend_query_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr); -int rdma_backend_modify_srq(RdmaBackendSRQ *srq, struct ibv_srq_attr *srq_attr, - int srq_attr_mask); -void rdma_backend_destroy_srq(RdmaBackendSRQ *srq, - RdmaDeviceResources *dev_res); -void rdma_backend_post_srq_recv(RdmaBackendDev *backend_dev, - RdmaBackendSRQ *srq, struct ibv_sge *sge, - uint32_t num_sge, void *ctx); - -#endif diff --git a/hw/rdma/rdma_backend_defs.h b/hw/rdma/rdma_backend_defs.h deleted file mode 100644 index 4e6c0ad695..0000000000 --- a/hw/rdma/rdma_backend_defs.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * RDMA device: Definitions of Backend Device structures - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 RDMA_BACKEND_DEFS_H -#define RDMA_BACKEND_DEFS_H - -#include "qemu/thread.h" -#include "chardev/char-fe.h" -#include -#include "contrib/rdmacm-mux/rdmacm-mux.h" -#include "rdma_utils.h" - -typedef struct RdmaDeviceResources RdmaDeviceResources; - -typedef struct RdmaBackendThread { - QemuThread thread; - bool run; /* Set by thread manager to let thread know it should exit */ - bool is_running; /* Set by the thread to report its status */ -} RdmaBackendThread; - -typedef struct RdmaCmMux { - CharBackend *chr_be; - int can_receive; -} RdmaCmMux; - -typedef struct RdmaBackendDev { - RdmaBackendThread comp_thread; - PCIDevice *dev; - RdmaDeviceResources *rdma_dev_res; - struct ibv_device *ib_dev; - struct ibv_context *context; - struct ibv_comp_channel *channel; - uint8_t port_num; - RdmaProtectedGQueue recv_mads_list; - RdmaCmMux rdmacm_mux; -} RdmaBackendDev; - -typedef struct RdmaBackendPD { - struct ibv_pd *ibpd; -} RdmaBackendPD; - -typedef struct RdmaBackendMR { - struct ibv_pd *ibpd; - struct ibv_mr *ibmr; -} RdmaBackendMR; - -typedef struct RdmaBackendCQ { - RdmaBackendDev *backend_dev; - struct ibv_cq *ibcq; -} RdmaBackendCQ; - -typedef struct RdmaBackendQP { - struct ibv_pd *ibpd; - struct ibv_qp *ibqp; - uint8_t sgid_idx; - RdmaProtectedGSList cqe_ctx_list; -} RdmaBackendQP; - -typedef struct RdmaBackendSRQ { - struct ibv_srq *ibsrq; - RdmaProtectedGSList cqe_ctx_list; -} RdmaBackendSRQ; - -#endif diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c deleted file mode 100644 index cfd85de3e6..0000000000 --- a/hw/rdma/rdma_rm.c +++ /dev/null @@ -1,816 +0,0 @@ -/* - * QEMU paravirtual RDMA - Resource Manager Implementation - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 "cpu.h" -#include "monitor/monitor.h" - -#include "trace.h" -#include "rdma_utils.h" -#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", - dev_res->stats.tx); - g_string_append_printf(buf, "\ttx_len : %" PRId64 "\n", - dev_res->stats.tx_len); - g_string_append_printf(buf, "\ttx_err : %" PRId64 "\n", - dev_res->stats.tx_err); - g_string_append_printf(buf, "\trx_bufs : %" PRId64 "\n", - dev_res->stats.rx_bufs); - g_string_append_printf(buf, "\trx_srq : %" PRId64 "\n", - dev_res->stats.rx_srq); - g_string_append_printf(buf, "\trx_bufs_len : %" PRId64 "\n", - dev_res->stats.rx_bufs_len); - g_string_append_printf(buf, "\trx_bufs_err : %" PRId64 "\n", - dev_res->stats.rx_bufs_err); - g_string_append_printf(buf, "\tcomps : %" PRId64 "\n", - dev_res->stats.completions); - g_string_append_printf(buf, "\tmissing_comps : %" PRId32 "\n", - dev_res->stats.missing_cqe); - g_string_append_printf(buf, "\tpoll_cq (bk) : %" PRId64 "\n", - dev_res->stats.poll_cq_from_bk); - g_string_append_printf(buf, "\tpoll_cq_ppoll_to : %" PRId64 "\n", - dev_res->stats.poll_cq_ppoll_to); - g_string_append_printf(buf, "\tpoll_cq (fe) : %" PRId64 "\n", - dev_res->stats.poll_cq_from_guest); - g_string_append_printf(buf, "\tpoll_cq_empty : %" PRId64 "\n", - dev_res->stats.poll_cq_from_guest_empty); - g_string_append_printf(buf, "\tmad_tx : %" PRId64 "\n", - dev_res->stats.mad_tx); - g_string_append_printf(buf, "\tmad_tx_err : %" PRId64 "\n", - dev_res->stats.mad_tx_err); - g_string_append_printf(buf, "\tmad_rx : %" PRId64 "\n", - dev_res->stats.mad_rx); - g_string_append_printf(buf, "\tmad_rx_err : %" PRId64 "\n", - dev_res->stats.mad_rx_err); - g_string_append_printf(buf, "\tmad_rx_bufs : %" PRId64 "\n", - dev_res->stats.mad_rx_bufs); - g_string_append_printf(buf, "\tmad_rx_bufs_err : %" PRId64 "\n", - dev_res->stats.mad_rx_bufs_err); - g_string_append_printf(buf, "\tPDs : %" PRId32 "\n", - dev_res->pd_tbl.used); - g_string_append_printf(buf, "\tMRs : %" PRId32 "\n", - dev_res->mr_tbl.used); - g_string_append_printf(buf, "\tUCs : %" PRId32 "\n", - dev_res->uc_tbl.used); - g_string_append_printf(buf, "\tQPs : %" PRId32 "\n", - dev_res->qp_tbl.used); - g_string_append_printf(buf, "\tCQs : %" PRId32 "\n", - dev_res->cq_tbl.used); - g_string_append_printf(buf, "\tCEQ_CTXs : %" PRId32 "\n", - dev_res->cqe_ctx_tbl.used); -} - -static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl, - uint32_t tbl_sz, uint32_t res_sz) -{ - tbl->tbl = g_malloc(tbl_sz * res_sz); - - strncpy(tbl->name, name, MAX_RM_TBL_NAME); - tbl->name[MAX_RM_TBL_NAME - 1] = 0; - - tbl->bitmap = bitmap_new(tbl_sz); - tbl->tbl_sz = tbl_sz; - tbl->res_sz = res_sz; - tbl->used = 0; - qemu_mutex_init(&tbl->lock); -} - -static inline void res_tbl_free(RdmaRmResTbl *tbl) -{ - if (!tbl->bitmap) { - return; - } - qemu_mutex_destroy(&tbl->lock); - g_free(tbl->tbl); - g_free(tbl->bitmap); -} - -static inline void *rdma_res_tbl_get(RdmaRmResTbl *tbl, uint32_t handle) -{ - trace_rdma_res_tbl_get(tbl->name, handle); - - if ((handle < tbl->tbl_sz) && (test_bit(handle, tbl->bitmap))) { - return tbl->tbl + handle * tbl->res_sz; - } else { - rdma_error_report("Table %s, invalid handle %d", tbl->name, handle); - return NULL; - } -} - -static inline void *rdma_res_tbl_alloc(RdmaRmResTbl *tbl, uint32_t *handle) -{ - qemu_mutex_lock(&tbl->lock); - - *handle = find_first_zero_bit(tbl->bitmap, tbl->tbl_sz); - if (*handle > tbl->tbl_sz) { - rdma_error_report("Table %s, failed to allocate, bitmap is full", - tbl->name); - qemu_mutex_unlock(&tbl->lock); - return NULL; - } - - set_bit(*handle, tbl->bitmap); - - tbl->used++; - - qemu_mutex_unlock(&tbl->lock); - - memset(tbl->tbl + *handle * tbl->res_sz, 0, tbl->res_sz); - - trace_rdma_res_tbl_alloc(tbl->name, *handle); - - return tbl->tbl + *handle * tbl->res_sz; -} - -static inline void rdma_res_tbl_dealloc(RdmaRmResTbl *tbl, uint32_t handle) -{ - trace_rdma_res_tbl_dealloc(tbl->name, handle); - - QEMU_LOCK_GUARD(&tbl->lock); - - if (handle < tbl->tbl_sz) { - clear_bit(handle, tbl->bitmap); - tbl->used--; - } - -} - -int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t *pd_handle, uint32_t ctx_handle) -{ - RdmaRmPD *pd; - int ret = -ENOMEM; - - pd = rdma_res_tbl_alloc(&dev_res->pd_tbl, pd_handle); - if (!pd) { - goto out; - } - - ret = rdma_backend_create_pd(backend_dev, &pd->backend_pd); - if (ret) { - ret = -EIO; - goto out_tbl_dealloc; - } - - pd->ctx_handle = ctx_handle; - - return 0; - -out_tbl_dealloc: - rdma_res_tbl_dealloc(&dev_res->pd_tbl, *pd_handle); - -out: - return ret; -} - -RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) -{ - return rdma_res_tbl_get(&dev_res->pd_tbl, pd_handle); -} - -void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) -{ - RdmaRmPD *pd = rdma_rm_get_pd(dev_res, pd_handle); - - if (pd) { - rdma_backend_destroy_pd(&pd->backend_pd); - rdma_res_tbl_dealloc(&dev_res->pd_tbl, pd_handle); - } -} - -int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint64_t guest_start, uint64_t guest_length, - void *host_virt, int access_flags, uint32_t *mr_handle, - uint32_t *lkey, uint32_t *rkey) -{ - RdmaRmMR *mr; - int ret = 0; - RdmaRmPD *pd; - - pd = rdma_rm_get_pd(dev_res, pd_handle); - if (!pd) { - return -EINVAL; - } - - mr = rdma_res_tbl_alloc(&dev_res->mr_tbl, mr_handle); - if (!mr) { - return -ENOMEM; - } - trace_rdma_rm_alloc_mr(*mr_handle, host_virt, guest_start, guest_length, - access_flags); - - if (host_virt) { - mr->virt = host_virt; - mr->start = guest_start; - mr->length = guest_length; - mr->virt += (mr->start & (TARGET_PAGE_SIZE - 1)); - - ret = rdma_backend_create_mr(&mr->backend_mr, &pd->backend_pd, mr->virt, - mr->length, guest_start, access_flags); - if (ret) { - ret = -EIO; - goto out_dealloc_mr; - } -#ifdef LEGACY_RDMA_REG_MR - /* We keep mr_handle in lkey so send and recv get get mr ptr */ - *lkey = *mr_handle; -#else - *lkey = rdma_backend_mr_lkey(&mr->backend_mr); -#endif - } - - *rkey = -1; - - mr->pd_handle = pd_handle; - - return 0; - -out_dealloc_mr: - rdma_res_tbl_dealloc(&dev_res->mr_tbl, *mr_handle); - - return ret; -} - -RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) -{ - return rdma_res_tbl_get(&dev_res->mr_tbl, mr_handle); -} - -void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) -{ - RdmaRmMR *mr = rdma_rm_get_mr(dev_res, mr_handle); - - if (mr) { - rdma_backend_destroy_mr(&mr->backend_mr); - trace_rdma_rm_dealloc_mr(mr_handle, mr->start); - if (mr->start) { - mr->virt -= (mr->start & (TARGET_PAGE_SIZE - 1)); - munmap(mr->virt, mr->length); - } - rdma_res_tbl_dealloc(&dev_res->mr_tbl, mr_handle); - } -} - -int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn, - uint32_t *uc_handle) -{ - RdmaRmUC *uc; - - /* TODO: Need to make sure pfn is between bar start address and - * bsd+RDMA_BAR2_UAR_SIZE - if (pfn > RDMA_BAR2_UAR_SIZE) { - rdma_error_report("pfn out of range (%d > %d)", pfn, - RDMA_BAR2_UAR_SIZE); - return -ENOMEM; - } - */ - - uc = rdma_res_tbl_alloc(&dev_res->uc_tbl, uc_handle); - if (!uc) { - return -ENOMEM; - } - - return 0; -} - -RdmaRmUC *rdma_rm_get_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) -{ - return rdma_res_tbl_get(&dev_res->uc_tbl, uc_handle); -} - -void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) -{ - RdmaRmUC *uc = rdma_rm_get_uc(dev_res, uc_handle); - - if (uc) { - rdma_res_tbl_dealloc(&dev_res->uc_tbl, uc_handle); - } -} - -RdmaRmCQ *rdma_rm_get_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) -{ - return rdma_res_tbl_get(&dev_res->cq_tbl, cq_handle); -} - -int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t cqe, uint32_t *cq_handle, void *opaque) -{ - int rc; - RdmaRmCQ *cq; - - cq = rdma_res_tbl_alloc(&dev_res->cq_tbl, cq_handle); - if (!cq) { - return -ENOMEM; - } - - cq->opaque = opaque; - cq->notify = CNT_CLEAR; - - rc = rdma_backend_create_cq(backend_dev, &cq->backend_cq, cqe); - if (rc) { - rc = -EIO; - goto out_dealloc_cq; - } - - return 0; - -out_dealloc_cq: - rdma_rm_dealloc_cq(dev_res, *cq_handle); - - return rc; -} - -void rdma_rm_req_notify_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle, - bool notify) -{ - RdmaRmCQ *cq; - - cq = rdma_rm_get_cq(dev_res, cq_handle); - if (!cq) { - return; - } - - if (cq->notify != CNT_SET) { - cq->notify = notify ? CNT_ARM : CNT_CLEAR; - } -} - -void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) -{ - RdmaRmCQ *cq; - - cq = rdma_rm_get_cq(dev_res, cq_handle); - if (!cq) { - return; - } - - rdma_backend_destroy_cq(&cq->backend_cq); - - rdma_res_tbl_dealloc(&dev_res->cq_tbl, cq_handle); -} - -RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn) -{ - GBytes *key = g_bytes_new(&qpn, sizeof(qpn)); - - RdmaRmQP *qp = g_hash_table_lookup(dev_res->qp_hash, key); - - g_bytes_unref(key); - - if (!qp) { - rdma_error_report("Invalid QP handle %d", qpn); - } - - return qp; -} - -int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint8_t qp_type, uint32_t max_send_wr, - uint32_t max_send_sge, uint32_t send_cq_handle, - uint32_t max_recv_wr, uint32_t max_recv_sge, - uint32_t recv_cq_handle, void *opaque, uint32_t *qpn, - uint8_t is_srq, uint32_t srq_handle) -{ - int rc; - RdmaRmQP *qp; - RdmaRmCQ *scq, *rcq; - RdmaRmPD *pd; - RdmaRmSRQ *srq = NULL; - uint32_t rm_qpn; - - pd = rdma_rm_get_pd(dev_res, pd_handle); - if (!pd) { - return -EINVAL; - } - - scq = rdma_rm_get_cq(dev_res, send_cq_handle); - rcq = rdma_rm_get_cq(dev_res, recv_cq_handle); - - if (!scq || !rcq) { - rdma_error_report("Invalid send_cqn or recv_cqn (%d, %d)", - send_cq_handle, recv_cq_handle); - return -EINVAL; - } - - if (is_srq) { - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - rdma_error_report("Invalid srqn %d", srq_handle); - return -EINVAL; - } - - srq->recv_cq_handle = recv_cq_handle; - } - - if (qp_type == IBV_QPT_GSI) { - scq->notify = CNT_SET; - rcq->notify = CNT_SET; - } - - qp = rdma_res_tbl_alloc(&dev_res->qp_tbl, &rm_qpn); - if (!qp) { - return -ENOMEM; - } - - qp->qpn = rm_qpn; - qp->qp_state = IBV_QPS_RESET; - qp->qp_type = qp_type; - qp->send_cq_handle = send_cq_handle; - qp->recv_cq_handle = recv_cq_handle; - qp->opaque = opaque; - qp->is_srq = is_srq; - - rc = rdma_backend_create_qp(&qp->backend_qp, qp_type, &pd->backend_pd, - &scq->backend_cq, &rcq->backend_cq, - is_srq ? &srq->backend_srq : NULL, - max_send_wr, max_recv_wr, max_send_sge, - max_recv_sge); - - if (rc) { - rc = -EIO; - goto out_dealloc_qp; - } - - *qpn = rdma_backend_qpn(&qp->backend_qp); - trace_rdma_rm_alloc_qp(rm_qpn, *qpn, qp_type); - g_hash_table_insert(dev_res->qp_hash, g_bytes_new(qpn, sizeof(*qpn)), qp); - - return 0; - -out_dealloc_qp: - rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); - - return rc; -} - -int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - enum ibv_qp_state qp_state, uint32_t qkey, - uint32_t rq_psn, uint32_t sq_psn) -{ - RdmaRmQP *qp; - int ret; - - qp = rdma_rm_get_qp(dev_res, qp_handle); - if (!qp) { - return -EINVAL; - } - - if (qp->qp_type == IBV_QPT_SMI) { - rdma_error_report("Got QP0 request"); - return -EPERM; - } else if (qp->qp_type == IBV_QPT_GSI) { - return 0; - } - - trace_rdma_rm_modify_qp(qp_handle, attr_mask, qp_state, sgid_idx); - - if (attr_mask & IBV_QP_STATE) { - qp->qp_state = qp_state; - - if (qp->qp_state == IBV_QPS_INIT) { - ret = rdma_backend_qp_state_init(backend_dev, &qp->backend_qp, - qp->qp_type, qkey); - if (ret) { - return -EIO; - } - } - - if (qp->qp_state == IBV_QPS_RTR) { - /* Get backend gid index */ - sgid_idx = rdma_rm_get_backend_gid_index(dev_res, backend_dev, - sgid_idx); - if (sgid_idx <= 0) { /* TODO check also less than bk.max_sgid */ - rdma_error_report("Failed to get bk sgid_idx for sgid_idx %d", - sgid_idx); - return -EIO; - } - - ret = rdma_backend_qp_state_rtr(backend_dev, &qp->backend_qp, - qp->qp_type, sgid_idx, dgid, dqpn, - rq_psn, qkey, - attr_mask & IBV_QP_QKEY); - if (ret) { - return -EIO; - } - } - - if (qp->qp_state == IBV_QPS_RTS) { - ret = rdma_backend_qp_state_rts(&qp->backend_qp, qp->qp_type, - sq_psn, qkey, - attr_mask & IBV_QP_QKEY); - if (ret) { - return -EIO; - } - } - } - - return 0; -} - -int rdma_rm_query_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr) -{ - RdmaRmQP *qp; - - qp = rdma_rm_get_qp(dev_res, qp_handle); - if (!qp) { - return -EINVAL; - } - - return rdma_backend_query_qp(&qp->backend_qp, attr, attr_mask, init_attr); -} - -void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle) -{ - RdmaRmQP *qp; - GBytes *key; - - key = g_bytes_new(&qp_handle, sizeof(qp_handle)); - qp = g_hash_table_lookup(dev_res->qp_hash, key); - g_hash_table_remove(dev_res->qp_hash, key); - g_bytes_unref(key); - - if (!qp) { - return; - } - - rdma_backend_destroy_qp(&qp->backend_qp, dev_res); - - rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); -} - -RdmaRmSRQ *rdma_rm_get_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle) -{ - return rdma_res_tbl_get(&dev_res->srq_tbl, srq_handle); -} - -int rdma_rm_alloc_srq(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint32_t max_wr, uint32_t max_sge, uint32_t srq_limit, - uint32_t *srq_handle, void *opaque) -{ - RdmaRmSRQ *srq; - RdmaRmPD *pd; - int rc; - - pd = rdma_rm_get_pd(dev_res, pd_handle); - if (!pd) { - return -EINVAL; - } - - srq = rdma_res_tbl_alloc(&dev_res->srq_tbl, srq_handle); - if (!srq) { - return -ENOMEM; - } - - rc = rdma_backend_create_srq(&srq->backend_srq, &pd->backend_pd, - max_wr, max_sge, srq_limit); - if (rc) { - rc = -EIO; - goto out_dealloc_srq; - } - - srq->opaque = opaque; - - return 0; - -out_dealloc_srq: - rdma_res_tbl_dealloc(&dev_res->srq_tbl, *srq_handle); - - return rc; -} - -int rdma_rm_query_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr) -{ - RdmaRmSRQ *srq; - - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - return -EINVAL; - } - - return rdma_backend_query_srq(&srq->backend_srq, srq_attr); -} - -int rdma_rm_modify_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr, int srq_attr_mask) -{ - RdmaRmSRQ *srq; - - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - return -EINVAL; - } - - if ((srq_attr_mask & IBV_SRQ_LIMIT) && - (srq_attr->srq_limit == 0)) { - return -EINVAL; - } - - if ((srq_attr_mask & IBV_SRQ_MAX_WR) && - (srq_attr->max_wr == 0)) { - return -EINVAL; - } - - return rdma_backend_modify_srq(&srq->backend_srq, srq_attr, - srq_attr_mask); -} - -void rdma_rm_dealloc_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle) -{ - RdmaRmSRQ *srq; - - srq = rdma_rm_get_srq(dev_res, srq_handle); - if (!srq) { - return; - } - - rdma_backend_destroy_srq(&srq->backend_srq, dev_res); - rdma_res_tbl_dealloc(&dev_res->srq_tbl, srq_handle); -} - -void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) -{ - void **cqe_ctx; - - cqe_ctx = rdma_res_tbl_get(&dev_res->cqe_ctx_tbl, cqe_ctx_id); - if (!cqe_ctx) { - return NULL; - } - - return *cqe_ctx; -} - -int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, - void *ctx) -{ - void **cqe_ctx; - - cqe_ctx = rdma_res_tbl_alloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); - if (!cqe_ctx) { - return -ENOMEM; - } - - *cqe_ctx = ctx; - - return 0; -} - -void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) -{ - rdma_res_tbl_dealloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); -} - -int rdma_rm_add_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, union ibv_gid *gid, int gid_idx) -{ - int rc; - - rc = rdma_backend_add_gid(backend_dev, ifname, gid); - if (rc) { - return -EINVAL; - } - - memcpy(&dev_res->port.gid_tbl[gid_idx].gid, gid, sizeof(*gid)); - - return 0; -} - -int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, int gid_idx) -{ - int rc; - - if (!dev_res->port.gid_tbl[gid_idx].gid.global.interface_id) { - return 0; - } - - rc = rdma_backend_del_gid(backend_dev, ifname, - &dev_res->port.gid_tbl[gid_idx].gid); - if (rc) { - return -EINVAL; - } - - memset(dev_res->port.gid_tbl[gid_idx].gid.raw, 0, - sizeof(dev_res->port.gid_tbl[gid_idx].gid)); - dev_res->port.gid_tbl[gid_idx].backend_gid_index = -1; - - return 0; -} - -int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res, - RdmaBackendDev *backend_dev, int sgid_idx) -{ - if (unlikely(sgid_idx < 0 || sgid_idx >= MAX_PORT_GIDS)) { - rdma_error_report("Got invalid sgid_idx %d", sgid_idx); - return -EINVAL; - } - - if (unlikely(dev_res->port.gid_tbl[sgid_idx].backend_gid_index == -1)) { - dev_res->port.gid_tbl[sgid_idx].backend_gid_index = - rdma_backend_get_gid_index(backend_dev, - &dev_res->port.gid_tbl[sgid_idx].gid); - } - - return dev_res->port.gid_tbl[sgid_idx].backend_gid_index; -} - -static void destroy_qp_hash_key(gpointer data) -{ - g_bytes_unref(data); -} - -static void init_ports(RdmaDeviceResources *dev_res) -{ - int i; - - memset(&dev_res->port, 0, sizeof(dev_res->port)); - - dev_res->port.state = IBV_PORT_DOWN; - for (i = 0; i < MAX_PORT_GIDS; i++) { - dev_res->port.gid_tbl[i].backend_gid_index = -1; - } -} - -static void fini_ports(RdmaDeviceResources *dev_res, - RdmaBackendDev *backend_dev, const char *ifname) -{ - int i; - - dev_res->port.state = IBV_PORT_DOWN; - for (i = 0; i < MAX_PORT_GIDS; i++) { - rdma_rm_del_gid(dev_res, backend_dev, ifname, i); - } -} - -int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr) -{ - dev_res->qp_hash = g_hash_table_new_full(g_bytes_hash, g_bytes_equal, - destroy_qp_hash_key, NULL); - if (!dev_res->qp_hash) { - return -ENOMEM; - } - - res_tbl_init("PD", &dev_res->pd_tbl, dev_attr->max_pd, sizeof(RdmaRmPD)); - res_tbl_init("CQ", &dev_res->cq_tbl, dev_attr->max_cq, sizeof(RdmaRmCQ)); - res_tbl_init("MR", &dev_res->mr_tbl, dev_attr->max_mr, sizeof(RdmaRmMR)); - res_tbl_init("QP", &dev_res->qp_tbl, dev_attr->max_qp, sizeof(RdmaRmQP)); - res_tbl_init("CQE_CTX", &dev_res->cqe_ctx_tbl, dev_attr->max_qp * - dev_attr->max_qp_wr, sizeof(void *)); - res_tbl_init("UC", &dev_res->uc_tbl, MAX_UCS, sizeof(RdmaRmUC)); - res_tbl_init("SRQ", &dev_res->srq_tbl, dev_attr->max_srq, - sizeof(RdmaRmSRQ)); - - init_ports(dev_res); - - qemu_mutex_init(&dev_res->lock); - - memset(&dev_res->stats, 0, sizeof(dev_res->stats)); - qatomic_set(&dev_res->stats.missing_cqe, 0); - - return 0; -} - -void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname) -{ - qemu_mutex_destroy(&dev_res->lock); - - fini_ports(dev_res, backend_dev, ifname); - - res_tbl_free(&dev_res->srq_tbl); - res_tbl_free(&dev_res->uc_tbl); - res_tbl_free(&dev_res->cqe_ctx_tbl); - res_tbl_free(&dev_res->qp_tbl); - res_tbl_free(&dev_res->mr_tbl); - res_tbl_free(&dev_res->cq_tbl); - res_tbl_free(&dev_res->pd_tbl); - - if (dev_res->qp_hash) { - g_hash_table_destroy(dev_res->qp_hash); - } -} diff --git a/hw/rdma/rdma_rm.h b/hw/rdma/rdma_rm.h deleted file mode 100644 index d69a917795..0000000000 --- a/hw/rdma/rdma_rm.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * RDMA device: Definitions of Resource Manager functions - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 RDMA_RM_H -#define RDMA_RM_H - -#include "qapi/error.h" -#include "rdma_backend_defs.h" -#include "rdma_rm_defs.h" - -int rdma_rm_init(RdmaDeviceResources *dev_res, - struct ibv_device_attr *dev_attr); -void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname); - -int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t *pd_handle, uint32_t ctx_handle); -RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle); -void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle); - -int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint64_t guest_start, uint64_t guest_length, - void *host_virt, int access_flags, uint32_t *mr_handle, - uint32_t *lkey, uint32_t *rkey); -RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle); -void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle); - -int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn, - uint32_t *uc_handle); -RdmaRmUC *rdma_rm_get_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle); -void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle); - -int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t cqe, uint32_t *cq_handle, void *opaque); -RdmaRmCQ *rdma_rm_get_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle); -void rdma_rm_req_notify_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle, - bool notify); -void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle); - -int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint8_t qp_type, uint32_t max_send_wr, - uint32_t max_send_sge, uint32_t send_cq_handle, - uint32_t max_recv_wr, uint32_t max_recv_sge, - uint32_t recv_cq_handle, void *opaque, uint32_t *qpn, - uint8_t is_srq, uint32_t srq_handle); -RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn); -int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, - union ibv_gid *dgid, uint32_t dqpn, - enum ibv_qp_state qp_state, uint32_t qkey, - uint32_t rq_psn, uint32_t sq_psn); -int rdma_rm_query_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, struct ibv_qp_attr *attr, - int attr_mask, struct ibv_qp_init_attr *init_attr); -void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle); - -RdmaRmSRQ *rdma_rm_get_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle); -int rdma_rm_alloc_srq(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint32_t max_wr, uint32_t max_sge, uint32_t srq_limit, - uint32_t *srq_handle, void *opaque); -int rdma_rm_query_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr); -int rdma_rm_modify_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle, - struct ibv_srq_attr *srq_attr, int srq_attr_mask); -void rdma_rm_dealloc_srq(RdmaDeviceResources *dev_res, uint32_t srq_handle); - -int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, - void *ctx); -void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); -void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); - -int rdma_rm_add_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, union ibv_gid *gid, int gid_idx); -int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - const char *ifname, int gid_idx); -int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res, - RdmaBackendDev *backend_dev, int sgid_idx); -static inline union ibv_gid *rdma_rm_get_gid(RdmaDeviceResources *dev_res, - int sgid_idx) -{ - return &dev_res->port.gid_tbl[sgid_idx].gid; -} -void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf); - -#endif diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h deleted file mode 100644 index 534f2f74d3..0000000000 --- a/hw/rdma/rdma_rm_defs.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * RDMA device: Definitions of Resource Manager structures - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 RDMA_RM_DEFS_H -#define RDMA_RM_DEFS_H - -#include "rdma_backend_defs.h" - -#define MAX_PORTS 1 /* Do not change - we support only one port */ -#define MAX_PORT_GIDS 255 -#define MAX_GIDS MAX_PORT_GIDS -#define MAX_PORT_PKEYS 1 -#define MAX_PKEYS MAX_PORT_PKEYS -#define MAX_UCS 512 -#define MAX_MR_SIZE (1UL << 27) -#define MAX_QP 1024 -#define MAX_SGE 4 -#define MAX_CQ 2048 -#define MAX_MR 1024 -#define MAX_PD 1024 -#define MAX_QP_RD_ATOM 16 -#define MAX_QP_INIT_RD_ATOM 16 -#define MAX_AH 64 -#define MAX_SRQ 512 - -#define MAX_RM_TBL_NAME 16 -#define MAX_CONSEQ_EMPTY_POLL_CQ 4096 /* considered as error above this */ - -typedef struct RdmaRmResTbl { - char name[MAX_RM_TBL_NAME]; - QemuMutex lock; - unsigned long *bitmap; - size_t tbl_sz; - size_t res_sz; - void *tbl; - uint32_t used; /* number of used entries in the table */ -} RdmaRmResTbl; - -typedef struct RdmaRmPD { - RdmaBackendPD backend_pd; - uint32_t ctx_handle; -} RdmaRmPD; - -typedef enum CQNotificationType { - CNT_CLEAR, - CNT_ARM, - CNT_SET, -} CQNotificationType; - -typedef struct RdmaRmCQ { - RdmaBackendCQ backend_cq; - void *opaque; - CQNotificationType notify; -} RdmaRmCQ; - -/* MR (DMA region) */ -typedef struct RdmaRmMR { - RdmaBackendMR backend_mr; - void *virt; - uint64_t start; - size_t length; - uint32_t pd_handle; - uint32_t lkey; - uint32_t rkey; -} RdmaRmMR; - -typedef struct RdmaRmUC { - uint64_t uc_handle; -} RdmaRmUC; - -typedef struct RdmaRmQP { - RdmaBackendQP backend_qp; - void *opaque; - uint32_t qp_type; - uint32_t qpn; - uint32_t send_cq_handle; - uint32_t recv_cq_handle; - enum ibv_qp_state qp_state; - uint8_t is_srq; -} RdmaRmQP; - -typedef struct RdmaRmSRQ { - RdmaBackendSRQ backend_srq; - uint32_t recv_cq_handle; - void *opaque; -} RdmaRmSRQ; - -typedef struct RdmaRmGid { - union ibv_gid gid; - int backend_gid_index; -} RdmaRmGid; - -typedef struct RdmaRmPort { - RdmaRmGid gid_tbl[MAX_PORT_GIDS]; - enum ibv_port_state state; -} RdmaRmPort; - -typedef struct RdmaRmStats { - uint64_t tx; - uint64_t tx_len; - uint64_t tx_err; - uint64_t rx_bufs; - uint64_t rx_bufs_len; - uint64_t rx_bufs_err; - uint64_t rx_srq; - uint64_t completions; - uint64_t mad_tx; - uint64_t mad_tx_err; - uint64_t mad_rx; - uint64_t mad_rx_err; - uint64_t mad_rx_bufs; - uint64_t mad_rx_bufs_err; - uint64_t poll_cq_from_bk; - uint64_t poll_cq_from_guest; - uint64_t poll_cq_from_guest_empty; - uint64_t poll_cq_ppoll_to; - uint32_t missing_cqe; -} RdmaRmStats; - -struct RdmaDeviceResources { - RdmaRmPort port; - RdmaRmResTbl pd_tbl; - RdmaRmResTbl mr_tbl; - RdmaRmResTbl uc_tbl; - RdmaRmResTbl qp_tbl; - RdmaRmResTbl cq_tbl; - RdmaRmResTbl cqe_ctx_tbl; - RdmaRmResTbl srq_tbl; - GHashTable *qp_hash; /* Keeps mapping between real and emulated */ - QemuMutex lock; - RdmaRmStats stats; -}; - -#endif diff --git a/hw/rdma/rdma_utils.c b/hw/rdma/rdma_utils.c deleted file mode 100644 index 5a7ef63ad2..0000000000 --- a/hw/rdma/rdma_utils.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * QEMU paravirtual RDMA - Generic RDMA backend - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 "trace.h" -#include "rdma_utils.h" - -void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len) -{ - void *p; - dma_addr_t pci_len = len; - - if (!addr) { - rdma_error_report("addr is NULL"); - return NULL; - } - - p = pci_dma_map(dev, addr, &pci_len, DMA_DIRECTION_TO_DEVICE); - if (!p) { - rdma_error_report("pci_dma_map fail, addr=0x%"PRIx64", len=%"PRId64, - addr, pci_len); - return NULL; - } - - if (pci_len != len) { - rdma_pci_dma_unmap(dev, p, pci_len); - return NULL; - } - - trace_rdma_pci_dma_map(addr, p, pci_len); - - return p; -} - -void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len) -{ - trace_rdma_pci_dma_unmap(buffer); - if (buffer) { - pci_dma_unmap(dev, buffer, len, DMA_DIRECTION_TO_DEVICE, 0); - } -} - -void rdma_protected_gqueue_init(RdmaProtectedGQueue *list) -{ - qemu_mutex_init(&list->lock); - list->list = g_queue_new(); -} - -void rdma_protected_gqueue_destroy(RdmaProtectedGQueue *list) -{ - if (list->list) { - g_queue_free_full(list->list, g_free); - qemu_mutex_destroy(&list->lock); - list->list = NULL; - } -} - -void rdma_protected_gqueue_append_int64(RdmaProtectedGQueue *list, - int64_t value) -{ - qemu_mutex_lock(&list->lock); - g_queue_push_tail(list->list, g_memdup(&value, sizeof(value))); - qemu_mutex_unlock(&list->lock); -} - -int64_t rdma_protected_gqueue_pop_int64(RdmaProtectedGQueue *list) -{ - int64_t *valp; - int64_t val; - - qemu_mutex_lock(&list->lock); - - valp = g_queue_pop_head(list->list); - qemu_mutex_unlock(&list->lock); - - if (!valp) { - return -ENOENT; - } - - val = *valp; - g_free(valp); - return val; -} - -void rdma_protected_gslist_init(RdmaProtectedGSList *list) -{ - qemu_mutex_init(&list->lock); -} - -void rdma_protected_gslist_destroy(RdmaProtectedGSList *list) -{ - if (list->list) { - g_slist_free(list->list); - qemu_mutex_destroy(&list->lock); - list->list = NULL; - } -} - -void rdma_protected_gslist_append_int32(RdmaProtectedGSList *list, - int32_t value) -{ - qemu_mutex_lock(&list->lock); - list->list = g_slist_prepend(list->list, GINT_TO_POINTER(value)); - qemu_mutex_unlock(&list->lock); -} - -void rdma_protected_gslist_remove_int32(RdmaProtectedGSList *list, - int32_t value) -{ - qemu_mutex_lock(&list->lock); - list->list = g_slist_remove(list->list, GINT_TO_POINTER(value)); - qemu_mutex_unlock(&list->lock); -} diff --git a/hw/rdma/rdma_utils.h b/hw/rdma/rdma_utils.h deleted file mode 100644 index 0c6414e7e0..0000000000 --- a/hw/rdma/rdma_utils.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * RDMA device: Debug utilities - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 RDMA_UTILS_H -#define RDMA_UTILS_H - -#include "qemu/error-report.h" -#include "hw/pci/pci.h" -#include "sysemu/dma.h" - -#define rdma_error_report(fmt, ...) \ - error_report("%s: " fmt, "rdma", ## __VA_ARGS__) -#define rdma_warn_report(fmt, ...) \ - warn_report("%s: " fmt, "rdma", ## __VA_ARGS__) -#define rdma_info_report(fmt, ...) \ - info_report("%s: " fmt, "rdma", ## __VA_ARGS__) - -typedef struct RdmaProtectedGQueue { - QemuMutex lock; - GQueue *list; -} RdmaProtectedGQueue; - -typedef struct RdmaProtectedGSList { - QemuMutex lock; - GSList *list; -} RdmaProtectedGSList; - -void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len); -void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len); -void rdma_protected_gqueue_init(RdmaProtectedGQueue *list); -void rdma_protected_gqueue_destroy(RdmaProtectedGQueue *list); -void rdma_protected_gqueue_append_int64(RdmaProtectedGQueue *list, - int64_t value); -int64_t rdma_protected_gqueue_pop_int64(RdmaProtectedGQueue *list); -void rdma_protected_gslist_init(RdmaProtectedGSList *list); -void rdma_protected_gslist_destroy(RdmaProtectedGSList *list); -void rdma_protected_gslist_append_int32(RdmaProtectedGSList *list, - int32_t value); -void rdma_protected_gslist_remove_int32(RdmaProtectedGSList *list, - int32_t value); - -static inline void addrconf_addr_eui48(uint8_t *eui, const char *addr) -{ - memcpy(eui, addr, 3); - eui[3] = 0xFF; - eui[4] = 0xFE; - memcpy(eui + 5, addr + 3, 3); - eui[0] ^= 2; -} - -#endif diff --git a/hw/rdma/trace-events b/hw/rdma/trace-events deleted file mode 100644 index c23175120e..0000000000 --- a/hw/rdma/trace-events +++ /dev/null @@ -1,31 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# rdma_backend.c -rdma_check_dev_attr(const char *name, int max_bk, int max_fe) "%s: be=%d, fe=%d" -rdma_create_ah_cache_hit(uint64_t subnet, uint64_t if_id) "subnet=0x%"PRIx64",if_id=0x%"PRIx64 -rdma_create_ah_cache_miss(uint64_t subnet, uint64_t if_id) "subnet=0x%"PRIx64",if_id=0x%"PRIx64 -rdma_poll_cq(int ne, void *ibcq) "Got %d completion(s) from cq %p" -rdmacm_mux(const char *title, int msg_type, int op_code) "%s: msg_type=%d, op_code=%d" -rdmacm_mux_check_op_status(int msg_type, int op_code, int err_code) "resp: msg_type=%d, op_code=%d, err_code=%d" -rdma_mad_message(const char *title, int len, char *data) "mad %s (%d): %s" -rdma_backend_rc_qp_state_init(uint32_t qpn) "RC QP 0x%x switch to INIT" -rdma_backend_ud_qp_state_init(uint32_t qpn, uint32_t qkey) "UD QP 0x%x switch to INIT, qkey=0x%x" -rdma_backend_rc_qp_state_rtr(uint32_t qpn, uint64_t subnet, uint64_t ifid, uint8_t sgid_idx, uint32_t dqpn, uint32_t rq_psn) "RC QP 0x%x switch to RTR, subnet = 0x%"PRIx64", ifid = 0x%"PRIx64 ", sgid_idx=%d, dqpn=0x%x, rq_psn=0x%x" -rdma_backend_ud_qp_state_rtr(uint32_t qpn, uint32_t qkey) "UD QP 0x%x switch to RTR, qkey=0x%x" -rdma_backend_rc_qp_state_rts(uint32_t qpn, uint32_t sq_psn) "RC QP 0x%x switch to RTS, sq_psn=0x%x, " -rdma_backend_ud_qp_state_rts(uint32_t qpn, uint32_t sq_psn, uint32_t qkey) "UD QP 0x%x switch to RTS, sq_psn=0x%x, qkey=0x%x" -rdma_backend_get_gid_index(uint64_t subnet, uint64_t ifid, int gid_idx) "subnet=0x%"PRIx64", ifid=0x%"PRIx64 ", gid_idx=%d" -rdma_backend_gid_change(const char *op, uint64_t subnet, uint64_t ifid) "%s subnet=0x%"PRIx64", ifid=0x%"PRIx64 - -# rdma_rm.c -rdma_res_tbl_get(char *name, uint32_t handle) "tbl %s, handle %d" -rdma_res_tbl_alloc(char *name, uint32_t handle) "tbl %s, handle %d" -rdma_res_tbl_dealloc(char *name, uint32_t handle) "tbl %s, handle %d" -rdma_rm_alloc_mr(uint32_t mr_handle, void *host_virt, uint64_t guest_start, uint64_t guest_length, int access_flags) "mr_handle=%d, host_virt=%p, guest_start=0x%"PRIx64", length=%" PRId64", access_flags=0x%x" -rdma_rm_dealloc_mr(uint32_t mr_handle, uint64_t guest_start) "mr_handle=%d, guest_start=0x%"PRIx64 -rdma_rm_alloc_qp(uint32_t rm_qpn, uint32_t backend_qpn, uint8_t qp_type) "rm_qpn=%d, backend_qpn=0x%x, qp_type=%d" -rdma_rm_modify_qp(uint32_t qpn, uint32_t attr_mask, int qp_state, uint8_t sgid_idx) "qpn=0x%x, attr_mask=0x%x, qp_state=%d, sgid_idx=%d" - -# rdma_utils.c -rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRIu64")" -rdma_pci_dma_unmap(void *vaddr) "%p" diff --git a/hw/rdma/trace.h b/hw/rdma/trace.h deleted file mode 100644 index b3fa8ebc51..0000000000 --- a/hw/rdma/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-hw_rdma.h" diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h deleted file mode 100644 index d08965d3e2..0000000000 --- a/hw/rdma/vmw/pvrdma.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * QEMU VMWARE paravirtual RDMA device definitions - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 PVRDMA_PVRDMA_H -#define PVRDMA_PVRDMA_H - -#include "qemu/units.h" -#include "qemu/notify.h" -#include "hw/pci/pci.h" -#include "hw/pci/msix.h" -#include "chardev/char-fe.h" -#include "hw/net/vmxnet3_defs.h" - -#include "../rdma_backend_defs.h" -#include "../rdma_rm_defs.h" - -#include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" -#include "pvrdma_dev_ring.h" -#include "qom/object.h" - -/* BARs */ -#define RDMA_MSIX_BAR_IDX 0 -#define RDMA_REG_BAR_IDX 1 -#define RDMA_UAR_BAR_IDX 2 -#define RDMA_BAR0_MSIX_SIZE (16 * KiB) -#define RDMA_BAR1_REGS_SIZE 64 -#define RDMA_BAR2_UAR_SIZE (0x1000 * MAX_UCS) /* each uc gets page */ - -/* MSIX */ -#define RDMA_MAX_INTRS 3 -#define RDMA_MSIX_TABLE 0x0000 -#define RDMA_MSIX_PBA 0x2000 - -/* Interrupts Vectors */ -#define INTR_VEC_CMD_RING 0 -#define INTR_VEC_CMD_ASYNC_EVENTS 1 -#define INTR_VEC_CMD_COMPLETION_Q 2 - -/* HW attributes */ -#define PVRDMA_HW_NAME "pvrdma" -#define PVRDMA_HW_VERSION 17 -#define PVRDMA_FW_VERSION 14 - -/* Some defaults */ -#define PVRDMA_PKEY 0xFFFF - -typedef struct DSRInfo { - dma_addr_t dma; - struct pvrdma_device_shared_region *dsr; - - union pvrdma_cmd_req *req; - union pvrdma_cmd_resp *rsp; - - PvrdmaRingState *async_ring_state; - PvrdmaRing async; - - PvrdmaRingState *cq_ring_state; - PvrdmaRing cq; -} DSRInfo; - -typedef struct PVRDMADevStats { - uint64_t commands; - uint64_t regs_reads; - uint64_t regs_writes; - uint64_t uar_writes; - uint64_t interrupts; -} PVRDMADevStats; - -struct PVRDMADev { - PCIDevice parent_obj; - MemoryRegion msix; - MemoryRegion regs; - uint32_t regs_data[RDMA_BAR1_REGS_SIZE]; - MemoryRegion uar; - uint32_t uar_data[RDMA_BAR2_UAR_SIZE]; - DSRInfo dsr_info; - int interrupt_mask; - struct ibv_device_attr dev_attr; - uint64_t node_guid; - char *backend_eth_device_name; - char *backend_device_name; - uint8_t backend_port_num; - RdmaBackendDev backend_dev; - RdmaDeviceResources rdma_dev_res; - CharBackend mad_chr; - VMXNET3State *func0; - Notifier shutdown_notifier; - PVRDMADevStats stats; -}; -typedef struct PVRDMADev PVRDMADev; -DECLARE_INSTANCE_CHECKER(PVRDMADev, PVRDMA_DEV, - PVRDMA_HW_NAME) - -static inline int get_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t *val) -{ - int idx = addr >> 2; - - if (idx >= RDMA_BAR1_REGS_SIZE) { - return -EINVAL; - } - - *val = dev->regs_data[idx]; - - return 0; -} - -static inline int set_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t val) -{ - int idx = addr >> 2; - - if (idx >= RDMA_BAR1_REGS_SIZE) { - return -EINVAL; - } - - dev->regs_data[idx] = val; - - return 0; -} - -static inline void post_interrupt(PVRDMADev *dev, unsigned vector) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - - if (likely(!dev->interrupt_mask)) { - dev->stats.interrupts++; - msix_notify(pci_dev, vector); - } -} - -int pvrdma_exec_cmd(PVRDMADev *dev); - -#endif diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c deleted file mode 100644 index 89db963c46..0000000000 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * QEMU paravirtual RDMA - Command channel - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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/pci/pci.h" -#include "hw/pci/pci_ids.h" - -#include "../rdma_backend.h" -#include "../rdma_rm.h" -#include "../rdma_utils.h" - -#include "trace.h" -#include "pvrdma.h" -#include "standard-headers/rdma/vmw_pvrdma-abi.h" - -static void *pvrdma_map_to_pdir(PCIDevice *pdev, uint64_t pdir_dma, - uint32_t nchunks, size_t length) -{ - uint64_t *dir, *tbl; - int tbl_idx, dir_idx, addr_idx; - void *host_virt = NULL, *curr_page; - - if (!nchunks) { - rdma_error_report("Got nchunks=0"); - return NULL; - } - - length = ROUND_UP(length, TARGET_PAGE_SIZE); - if (nchunks * TARGET_PAGE_SIZE != length) { - rdma_error_report("Invalid nchunks/length (%u, %lu)", nchunks, - (unsigned long)length); - return NULL; - } - - dir = rdma_pci_dma_map(pdev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to page directory"); - return NULL; - } - - tbl = rdma_pci_dma_map(pdev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to page table 0"); - goto out_unmap_dir; - } - - curr_page = rdma_pci_dma_map(pdev, (dma_addr_t)tbl[0], TARGET_PAGE_SIZE); - if (!curr_page) { - rdma_error_report("Failed to map the page 0"); - goto out_unmap_tbl; - } - - host_virt = mremap(curr_page, 0, length, MREMAP_MAYMOVE); - if (host_virt == MAP_FAILED) { - host_virt = NULL; - rdma_error_report("Failed to remap memory for host_virt"); - goto out_unmap_tbl; - } - trace_pvrdma_map_to_pdir_host_virt(curr_page, host_virt); - - rdma_pci_dma_unmap(pdev, curr_page, TARGET_PAGE_SIZE); - - dir_idx = 0; - tbl_idx = 1; - addr_idx = 1; - while (addr_idx < nchunks) { - if (tbl_idx == TARGET_PAGE_SIZE / sizeof(uint64_t)) { - tbl_idx = 0; - dir_idx++; - rdma_pci_dma_unmap(pdev, tbl, TARGET_PAGE_SIZE); - tbl = rdma_pci_dma_map(pdev, dir[dir_idx], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to page table %d", dir_idx); - goto out_unmap_host_virt; - } - } - - curr_page = rdma_pci_dma_map(pdev, (dma_addr_t)tbl[tbl_idx], - TARGET_PAGE_SIZE); - if (!curr_page) { - rdma_error_report("Failed to map to page %d, dir %d", tbl_idx, - dir_idx); - goto out_unmap_host_virt; - } - - mremap(curr_page, 0, TARGET_PAGE_SIZE, MREMAP_MAYMOVE | MREMAP_FIXED, - host_virt + TARGET_PAGE_SIZE * addr_idx); - - trace_pvrdma_map_to_pdir_next_page(addr_idx, curr_page, host_virt + - TARGET_PAGE_SIZE * addr_idx); - - rdma_pci_dma_unmap(pdev, curr_page, TARGET_PAGE_SIZE); - - addr_idx++; - - tbl_idx++; - } - - goto out_unmap_tbl; - -out_unmap_host_virt: - munmap(host_virt, length); - host_virt = NULL; - -out_unmap_tbl: - rdma_pci_dma_unmap(pdev, tbl, TARGET_PAGE_SIZE); - -out_unmap_dir: - rdma_pci_dma_unmap(pdev, dir, TARGET_PAGE_SIZE); - - return host_virt; -} - -static int query_port(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - 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 = {}; - - if (cmd->port_num > MAX_PORTS) { - return -EINVAL; - } - - if (rdma_backend_query_port(&dev->backend_dev, - (struct ibv_port_attr *)&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; - 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; - resp->attrs.pkey_tbl_len = MIN(MAX_PORT_PKEYS, attrs.pkey_tbl_len); - resp->attrs.active_width = 1; - resp->attrs.active_speed = 1; - - return 0; -} - -static int query_pkey(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_query_pkey *cmd = &req->query_pkey; - struct pvrdma_cmd_query_pkey_resp *resp = &rsp->query_pkey_resp; - - if (cmd->port_num > MAX_PORTS) { - return -EINVAL; - } - - if (cmd->index > MAX_PKEYS) { - return -EINVAL; - } - - memset(resp, 0, sizeof(*resp)); - - resp->pkey = PVRDMA_PKEY; - - return 0; -} - -static int create_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - 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; -} - -static int destroy_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_pd *cmd = &req->destroy_pd; - - rdma_rm_dealloc_pd(&dev->rdma_dev_res, cmd->pd_handle); - - return 0; -} - -static int create_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_mr *cmd = &req->create_mr; - struct pvrdma_cmd_create_mr_resp *resp = &rsp->create_mr_resp; - PCIDevice *pci_dev = PCI_DEVICE(dev); - void *host_virt = NULL; - int rc = 0; - - memset(resp, 0, sizeof(*resp)); - - if (!(cmd->flags & PVRDMA_MR_FLAG_DMA)) { - host_virt = pvrdma_map_to_pdir(pci_dev, cmd->pdir_dma, cmd->nchunks, - cmd->length); - if (!host_virt) { - rdma_error_report("Failed to map to pdir"); - return -EINVAL; - } - } - - rc = rdma_rm_alloc_mr(&dev->rdma_dev_res, cmd->pd_handle, cmd->start, - cmd->length, host_virt, cmd->access_flags, - &resp->mr_handle, &resp->lkey, &resp->rkey); - if (rc && host_virt) { - munmap(host_virt, cmd->length); - } - - return rc; -} - -static int destroy_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_mr *cmd = &req->destroy_mr; - - rdma_rm_dealloc_mr(&dev->rdma_dev_res, cmd->mr_handle); - - return 0; -} - -static int create_cq_ring(PCIDevice *pci_dev , PvrdmaRing **ring, - uint64_t pdir_dma, uint32_t nchunks, uint32_t cqe) -{ - uint64_t *dir = NULL, *tbl = NULL; - PvrdmaRing *r; - int rc = -EINVAL; - char ring_name[MAX_RING_NAME_SZ]; - - if (!nchunks || nchunks > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid nchunks: %d", nchunks); - return rc; - } - - dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to CQ page directory"); - goto out; - } - - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to CQ page table"); - goto out; - } - - r = g_malloc(sizeof(*r)); - *ring = r; - - r->ring_state = (PvrdmaRingState *) - 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"); - goto out_free_ring; - } - - sprintf(ring_name, "cq_ring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(r, ring_name, pci_dev, &r->ring_state[1], - cqe, sizeof(struct pvrdma_cqe), - /* first page is ring state */ - (dma_addr_t *)&tbl[1], nchunks - 1); - if (rc) { - goto out_unmap_ring_state; - } - - goto out; - -out_unmap_ring_state: - /* ring_state was in slot 1, not 0 so need to jump back */ - rdma_pci_dma_unmap(pci_dev, --r->ring_state, TARGET_PAGE_SIZE); - -out_free_ring: - g_free(r); - -out: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - - return rc; -} - -static void destroy_cq_ring(PvrdmaRing *ring) -{ - pvrdma_ring_free(ring); - /* ring_state was in slot 1, not 0 so need to jump back */ - rdma_pci_dma_unmap(ring->dev, --ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); -} - -static int create_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_cq *cmd = &req->create_cq; - struct pvrdma_cmd_create_cq_resp *resp = &rsp->create_cq_resp; - PvrdmaRing *ring = NULL; - int rc; - - memset(resp, 0, sizeof(*resp)); - - resp->cqe = cmd->cqe; - - rc = create_cq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, cmd->nchunks, - cmd->cqe); - if (rc) { - return rc; - } - - rc = rdma_rm_alloc_cq(&dev->rdma_dev_res, &dev->backend_dev, cmd->cqe, - &resp->cq_handle, ring); - if (rc) { - destroy_cq_ring(ring); - } - - resp->cqe = cmd->cqe; - - return rc; -} - -static int destroy_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_cq *cmd = &req->destroy_cq; - RdmaRmCQ *cq; - PvrdmaRing *ring; - - cq = rdma_rm_get_cq(&dev->rdma_dev_res, cmd->cq_handle); - if (!cq) { - rdma_error_report("Got invalid CQ handle"); - return -EINVAL; - } - - ring = (PvrdmaRing *)cq->opaque; - destroy_cq_ring(ring); - - rdma_rm_dealloc_cq(&dev->rdma_dev_res, cmd->cq_handle); - - return 0; -} - -static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, - PvrdmaRing **rings, uint32_t scqe, uint32_t smax_sge, - uint32_t spages, uint32_t rcqe, uint32_t rmax_sge, - uint32_t rpages, uint8_t is_srq) -{ - uint64_t *dir = NULL, *tbl = NULL; - PvrdmaRing *sr, *rr; - int rc = -EINVAL; - char ring_name[MAX_RING_NAME_SZ]; - uint32_t wqe_sz; - - if (!spages || spages > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid send page count for QP ring: %d", - spages); - return rc; - } - - if (!is_srq && (!rpages || rpages > PVRDMA_MAX_FAST_REG_PAGES)) { - rdma_error_report("Got invalid recv page count for QP ring: %d", - rpages); - return rc; - } - - dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to QP page directory"); - goto out; - } - - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to QP page table"); - goto out; - } - - if (!is_srq) { - sr = g_malloc(2 * sizeof(*rr)); - rr = &sr[1]; - } else { - sr = g_malloc(sizeof(*sr)); - } - - *rings = sr; - - /* Create send ring */ - sr->ring_state = (PvrdmaRingState *) - 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; - } - - wqe_sz = pow2ceil(sizeof(struct pvrdma_sq_wqe_hdr) + - sizeof(struct pvrdma_sge) * smax_sge - 1); - - sprintf(ring_name, "qp_sring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(sr, ring_name, pci_dev, sr->ring_state, - scqe, wqe_sz, (dma_addr_t *)&tbl[1], spages); - if (rc) { - goto out_unmap_ring_state; - } - - if (!is_srq) { - /* Create recv ring */ - rr->ring_state = &sr->ring_state[1]; - wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge) * rmax_sge - 1); - sprintf(ring_name, "qp_rring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(rr, ring_name, pci_dev, rr->ring_state, - rcqe, wqe_sz, (dma_addr_t *)&tbl[1 + spages], - rpages); - if (rc) { - goto out_free_sr; - } - } - - goto out; - -out_free_sr: - pvrdma_ring_free(sr); - -out_unmap_ring_state: - rdma_pci_dma_unmap(pci_dev, sr->ring_state, TARGET_PAGE_SIZE); - -out_free_sr_mem: - g_free(sr); - -out: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - - return rc; -} - -static void destroy_qp_rings(PvrdmaRing *ring, uint8_t is_srq) -{ - pvrdma_ring_free(&ring[0]); - if (!is_srq) { - pvrdma_ring_free(&ring[1]); - } - - rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); -} - -static int create_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_qp *cmd = &req->create_qp; - struct pvrdma_cmd_create_qp_resp *resp = &rsp->create_qp_resp; - PvrdmaRing *rings = NULL; - int rc; - - memset(resp, 0, sizeof(*resp)); - - rc = create_qp_rings(PCI_DEVICE(dev), cmd->pdir_dma, &rings, - cmd->max_send_wr, cmd->max_send_sge, cmd->send_chunks, - cmd->max_recv_wr, cmd->max_recv_sge, - cmd->total_chunks - cmd->send_chunks - 1, cmd->is_srq); - if (rc) { - return rc; - } - - rc = rdma_rm_alloc_qp(&dev->rdma_dev_res, cmd->pd_handle, cmd->qp_type, - cmd->max_send_wr, cmd->max_send_sge, - cmd->send_cq_handle, cmd->max_recv_wr, - cmd->max_recv_sge, cmd->recv_cq_handle, rings, - &resp->qpn, cmd->is_srq, cmd->srq_handle); - if (rc) { - destroy_qp_rings(rings, cmd->is_srq); - return rc; - } - - resp->max_send_wr = cmd->max_send_wr; - resp->max_recv_wr = cmd->max_recv_wr; - resp->max_send_sge = cmd->max_send_sge; - resp->max_recv_sge = cmd->max_recv_sge; - resp->max_inline_data = cmd->max_inline_data; - - return 0; -} - -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; -} - -static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - 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; -} - -static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_qp *cmd = &req->destroy_qp; - RdmaRmQP *qp; - PvrdmaRing *ring; - - qp = rdma_rm_get_qp(&dev->rdma_dev_res, cmd->qp_handle); - if (!qp) { - return -EINVAL; - } - - ring = (PvrdmaRing *)qp->opaque; - destroy_qp_rings(ring, qp->is_srq); - rdma_rm_dealloc_qp(&dev->rdma_dev_res, cmd->qp_handle); - - return 0; -} - -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; -} - -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; -} - -static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - 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; -} - -static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_uc *cmd = &req->destroy_uc; - - rdma_rm_dealloc_uc(&dev->rdma_dev_res, cmd->ctx_handle); - - return 0; -} - -static int create_srq_ring(PCIDevice *pci_dev, PvrdmaRing **ring, - uint64_t pdir_dma, uint32_t max_wr, - uint32_t max_sge, uint32_t nchunks) -{ - uint64_t *dir = NULL, *tbl = NULL; - PvrdmaRing *r; - int rc = -EINVAL; - char ring_name[MAX_RING_NAME_SZ]; - uint32_t wqe_sz; - - if (!nchunks || nchunks > PVRDMA_MAX_FAST_REG_PAGES) { - rdma_error_report("Got invalid page count for SRQ ring: %d", - nchunks); - return rc; - } - - dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); - if (!dir) { - rdma_error_report("Failed to map to SRQ page directory"); - goto out; - } - - tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); - if (!tbl) { - rdma_error_report("Failed to map to SRQ page table"); - goto out; - } - - r = g_malloc(sizeof(*r)); - *ring = r; - - r->ring_state = (PvrdmaRingState *) - 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; - } - - wqe_sz = pow2ceil(sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge) * max_sge - 1); - sprintf(ring_name, "srq_ring_%" PRIx64, pdir_dma); - rc = pvrdma_ring_init(r, ring_name, pci_dev, &r->ring_state[1], max_wr, - wqe_sz, (dma_addr_t *)&tbl[1], nchunks - 1); - if (rc) { - goto out_unmap_ring_state; - } - - goto out; - -out_unmap_ring_state: - rdma_pci_dma_unmap(pci_dev, r->ring_state, TARGET_PAGE_SIZE); - -out_free_ring_mem: - g_free(r); - -out: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - - return rc; -} - -static void destroy_srq_ring(PvrdmaRing *ring) -{ - pvrdma_ring_free(ring); - rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); -} - -static int create_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_create_srq *cmd = &req->create_srq; - struct pvrdma_cmd_create_srq_resp *resp = &rsp->create_srq_resp; - PvrdmaRing *ring = NULL; - int rc; - - memset(resp, 0, sizeof(*resp)); - - rc = create_srq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, - cmd->attrs.max_wr, cmd->attrs.max_sge, - cmd->nchunks); - if (rc) { - return rc; - } - - rc = rdma_rm_alloc_srq(&dev->rdma_dev_res, cmd->pd_handle, - cmd->attrs.max_wr, cmd->attrs.max_sge, - cmd->attrs.srq_limit, &resp->srqn, ring); - if (rc) { - destroy_srq_ring(ring); - return rc; - } - - return 0; -} - -static int query_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_query_srq *cmd = &req->query_srq; - struct pvrdma_cmd_query_srq_resp *resp = &rsp->query_srq_resp; - - memset(resp, 0, sizeof(*resp)); - - return rdma_rm_query_srq(&dev->rdma_dev_res, cmd->srq_handle, - (struct ibv_srq_attr *)&resp->attrs); -} - -static int modify_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_modify_srq *cmd = &req->modify_srq; - - /* Only support SRQ limit */ - if (!(cmd->attr_mask & IBV_SRQ_LIMIT) || - (cmd->attr_mask & IBV_SRQ_MAX_WR)) - return -EINVAL; - - return rdma_rm_modify_srq(&dev->rdma_dev_res, cmd->srq_handle, - (struct ibv_srq_attr *)&cmd->attrs, - cmd->attr_mask); -} - -static int destroy_srq(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp) -{ - struct pvrdma_cmd_destroy_srq *cmd = &req->destroy_srq; - RdmaRmSRQ *srq; - PvrdmaRing *ring; - - srq = rdma_rm_get_srq(&dev->rdma_dev_res, cmd->srq_handle); - if (!srq) { - return -EINVAL; - } - - ring = (PvrdmaRing *)srq->opaque; - destroy_srq_ring(ring); - rdma_rm_dealloc_srq(&dev->rdma_dev_res, cmd->srq_handle); - - return 0; -} - -struct cmd_handler { - uint32_t cmd; - uint32_t ack; - int (*exec)(PVRDMADev *dev, union pvrdma_cmd_req *req, - union pvrdma_cmd_resp *rsp); -}; - -static struct cmd_handler cmd_handlers[] = { - {PVRDMA_CMD_QUERY_PORT, PVRDMA_CMD_QUERY_PORT_RESP, query_port}, - {PVRDMA_CMD_QUERY_PKEY, PVRDMA_CMD_QUERY_PKEY_RESP, query_pkey}, - {PVRDMA_CMD_CREATE_PD, PVRDMA_CMD_CREATE_PD_RESP, create_pd}, - {PVRDMA_CMD_DESTROY_PD, PVRDMA_CMD_DESTROY_PD_RESP_NOOP, destroy_pd}, - {PVRDMA_CMD_CREATE_MR, PVRDMA_CMD_CREATE_MR_RESP, create_mr}, - {PVRDMA_CMD_DESTROY_MR, PVRDMA_CMD_DESTROY_MR_RESP_NOOP, destroy_mr}, - {PVRDMA_CMD_CREATE_CQ, PVRDMA_CMD_CREATE_CQ_RESP, create_cq}, - {PVRDMA_CMD_RESIZE_CQ, PVRDMA_CMD_RESIZE_CQ_RESP, NULL}, - {PVRDMA_CMD_DESTROY_CQ, PVRDMA_CMD_DESTROY_CQ_RESP_NOOP, destroy_cq}, - {PVRDMA_CMD_CREATE_QP, PVRDMA_CMD_CREATE_QP_RESP, create_qp}, - {PVRDMA_CMD_MODIFY_QP, PVRDMA_CMD_MODIFY_QP_RESP, modify_qp}, - {PVRDMA_CMD_QUERY_QP, PVRDMA_CMD_QUERY_QP_RESP, query_qp}, - {PVRDMA_CMD_DESTROY_QP, PVRDMA_CMD_DESTROY_QP_RESP, destroy_qp}, - {PVRDMA_CMD_CREATE_UC, PVRDMA_CMD_CREATE_UC_RESP, create_uc}, - {PVRDMA_CMD_DESTROY_UC, PVRDMA_CMD_DESTROY_UC_RESP_NOOP, destroy_uc}, - {PVRDMA_CMD_CREATE_BIND, PVRDMA_CMD_CREATE_BIND_RESP_NOOP, create_bind}, - {PVRDMA_CMD_DESTROY_BIND, PVRDMA_CMD_DESTROY_BIND_RESP_NOOP, destroy_bind}, - {PVRDMA_CMD_CREATE_SRQ, PVRDMA_CMD_CREATE_SRQ_RESP, create_srq}, - {PVRDMA_CMD_QUERY_SRQ, PVRDMA_CMD_QUERY_SRQ_RESP, query_srq}, - {PVRDMA_CMD_MODIFY_SRQ, PVRDMA_CMD_MODIFY_SRQ_RESP, modify_srq}, - {PVRDMA_CMD_DESTROY_SRQ, PVRDMA_CMD_DESTROY_SRQ_RESP, destroy_srq}, -}; - -int pvrdma_exec_cmd(PVRDMADev *dev) -{ - int err = 0xFFFF; - DSRInfo *dsr_info; - - 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"); - goto out; - } - - if (!cmd_handlers[dsr_info->req->hdr.cmd].exec) { - rdma_error_report("Unsupported command (not implemented yet)"); - goto out; - } - - err = cmd_handlers[dsr_info->req->hdr.cmd].exec(dev, dsr_info->req, - dsr_info->rsp); - dsr_info->rsp->hdr.response = dsr_info->req->hdr.response; - dsr_info->rsp->hdr.ack = cmd_handlers[dsr_info->req->hdr.cmd].ack; - dsr_info->rsp->hdr.err = err < 0 ? -err : 0; - - trace_pvrdma_exec_cmd(dsr_info->req->hdr.cmd, dsr_info->rsp->hdr.err); - - dev->stats.commands++; - -out: - set_reg_val(dev, PVRDMA_REG_ERR, err); - post_interrupt(dev, INTR_VEC_CMD_RING); - - return (err == 0) ? 0 : -EINVAL; -} diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c deleted file mode 100644 index 598e6adc5e..0000000000 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * QEMU paravirtual RDMA - Device rings - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 "hw/pci/pci.h" -#include "cpu.h" -#include "qemu/cutils.h" - -#include "trace.h" - -#include "../rdma_utils.h" -#include "pvrdma_dev_ring.h" - -int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, - PvrdmaRingState *ring_state, uint32_t max_elems, - size_t elem_sz, dma_addr_t *tbl, uint32_t npages) -{ - int i; - int rc = 0; - - pstrcpy(ring->name, MAX_RING_NAME_SZ, name); - ring->dev = dev; - ring->ring_state = ring_state; - ring->max_elems = max_elems; - ring->elem_sz = elem_sz; - /* TODO: Give a moment to think if we want to redo driver settings - qatomic_set(&ring->ring_state->prod_tail, 0); - qatomic_set(&ring->ring_state->cons_head, 0); - */ - ring->npages = npages; - ring->pages = g_new0(void *, npages); - - for (i = 0; i < npages; i++) { - if (!tbl[i]) { - rdma_error_report("npages=%d but tbl[%d] is NULL", npages, i); - continue; - } - - ring->pages[i] = rdma_pci_dma_map(dev, tbl[i], TARGET_PAGE_SIZE); - if (!ring->pages[i]) { - rc = -ENOMEM; - rdma_error_report("Failed to map to page %d in ring %s", i, name); - goto out_free; - } - memset(ring->pages[i], 0, TARGET_PAGE_SIZE); - } - - goto out; - -out_free: - while (i--) { - rdma_pci_dma_unmap(dev, ring->pages[i], TARGET_PAGE_SIZE); - } - g_free(ring->pages); - -out: - return rc; -} - -void *pvrdma_ring_next_elem_read(PvrdmaRing *ring) -{ - unsigned int idx, offset; - const uint32_t tail = qatomic_read(&ring->ring_state->prod_tail); - const uint32_t head = qatomic_read(&ring->ring_state->cons_head); - - if (tail & ~((ring->max_elems << 1) - 1) || - head & ~((ring->max_elems << 1) - 1) || - tail == head) { - trace_pvrdma_ring_next_elem_read_no_data(ring->name); - return NULL; - } - - idx = head & (ring->max_elems - 1); - offset = idx * ring->elem_sz; - return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); -} - -void pvrdma_ring_read_inc(PvrdmaRing *ring) -{ - uint32_t idx = qatomic_read(&ring->ring_state->cons_head); - - idx = (idx + 1) & ((ring->max_elems << 1) - 1); - qatomic_set(&ring->ring_state->cons_head, idx); -} - -void *pvrdma_ring_next_elem_write(PvrdmaRing *ring) -{ - unsigned int idx, offset; - const uint32_t tail = qatomic_read(&ring->ring_state->prod_tail); - const uint32_t head = qatomic_read(&ring->ring_state->cons_head); - - if (tail & ~((ring->max_elems << 1) - 1) || - head & ~((ring->max_elems << 1) - 1) || - tail == (head ^ ring->max_elems)) { - rdma_error_report("CQ is full"); - return NULL; - } - - idx = tail & (ring->max_elems - 1); - offset = idx * ring->elem_sz; - return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); -} - -void pvrdma_ring_write_inc(PvrdmaRing *ring) -{ - uint32_t idx = qatomic_read(&ring->ring_state->prod_tail); - - idx = (idx + 1) & ((ring->max_elems << 1) - 1); - qatomic_set(&ring->ring_state->prod_tail, idx); -} - -void pvrdma_ring_free(PvrdmaRing *ring) -{ - if (!ring) { - return; - } - - if (!ring->pages) { - return; - } - - while (ring->npages--) { - rdma_pci_dma_unmap(ring->dev, ring->pages[ring->npages], - TARGET_PAGE_SIZE); - } - - g_free(ring->pages); - ring->pages = NULL; -} diff --git a/hw/rdma/vmw/pvrdma_dev_ring.h b/hw/rdma/vmw/pvrdma_dev_ring.h deleted file mode 100644 index d231588ce0..0000000000 --- a/hw/rdma/vmw/pvrdma_dev_ring.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * QEMU VMWARE paravirtual RDMA ring utilities - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 PVRDMA_DEV_RING_H -#define PVRDMA_DEV_RING_H - - -#define MAX_RING_NAME_SZ 32 - -typedef struct PvrdmaRingState { - int prod_tail; /* producer tail */ - int cons_head; /* consumer head */ -} PvrdmaRingState; - -typedef struct PvrdmaRing { - char name[MAX_RING_NAME_SZ]; - PCIDevice *dev; - uint32_t max_elems; - size_t elem_sz; - PvrdmaRingState *ring_state; /* used only for unmap */ - int npages; - void **pages; -} PvrdmaRing; - -int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, - PvrdmaRingState *ring_state, uint32_t max_elems, - size_t elem_sz, dma_addr_t *tbl, uint32_t npages); -void *pvrdma_ring_next_elem_read(PvrdmaRing *ring); -void pvrdma_ring_read_inc(PvrdmaRing *ring); -void *pvrdma_ring_next_elem_write(PvrdmaRing *ring); -void pvrdma_ring_write_inc(PvrdmaRing *ring); -void pvrdma_ring_free(PvrdmaRing *ring); - -#endif diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c deleted file mode 100644 index 4fc6712025..0000000000 --- a/hw/rdma/vmw/pvrdma_main.c +++ /dev/null @@ -1,719 +0,0 @@ -/* - * QEMU paravirtual RDMA - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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" -#include "hw/pci/pci.h" -#include "hw/pci/pci_ids.h" -#include "hw/pci/msi.h" -#include "hw/pci/msix.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "cpu.h" -#include "trace.h" -#include "monitor/monitor.h" -#include "hw/rdma/rdma.h" - -#include "../rdma_rm.h" -#include "../rdma_backend.h" -#include "../rdma_utils.h" - -#include -#include "pvrdma.h" -#include "standard-headers/rdma/vmw_pvrdma-abi.h" -#include "sysemu/runstate.h" -#include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" -#include "pvrdma_qp_ops.h" - -static Property pvrdma_dev_properties[] = { - DEFINE_PROP_STRING("netdev", PVRDMADev, backend_eth_device_name), - DEFINE_PROP_STRING("ibdev", PVRDMADev, backend_device_name), - DEFINE_PROP_UINT8("ibport", PVRDMADev, backend_port_num, 1), - DEFINE_PROP_UINT64("dev-caps-max-mr-size", PVRDMADev, dev_attr.max_mr_size, - MAX_MR_SIZE), - DEFINE_PROP_INT32("dev-caps-max-qp", PVRDMADev, dev_attr.max_qp, MAX_QP), - DEFINE_PROP_INT32("dev-caps-max-cq", PVRDMADev, dev_attr.max_cq, MAX_CQ), - DEFINE_PROP_INT32("dev-caps-max-mr", PVRDMADev, dev_attr.max_mr, MAX_MR), - DEFINE_PROP_INT32("dev-caps-max-pd", PVRDMADev, dev_attr.max_pd, MAX_PD), - DEFINE_PROP_INT32("dev-caps-qp-rd-atom", PVRDMADev, dev_attr.max_qp_rd_atom, - MAX_QP_RD_ATOM), - DEFINE_PROP_INT32("dev-caps-max-qp-init-rd-atom", PVRDMADev, - dev_attr.max_qp_init_rd_atom, MAX_QP_INIT_RD_ATOM), - DEFINE_PROP_INT32("dev-caps-max-ah", PVRDMADev, dev_attr.max_ah, MAX_AH), - DEFINE_PROP_INT32("dev-caps-max-srq", PVRDMADev, dev_attr.max_srq, MAX_SRQ), - DEFINE_PROP_CHR("mad-chardev", PVRDMADev, mad_chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pvrdma_format_statistics(RdmaProvider *obj, GString *buf) -{ - PVRDMADev *dev = PVRDMA_DEV(obj); - PCIDevice *pdev = PCI_DEVICE(dev); - - g_string_append_printf(buf, "%s, %x.%x\n", - pdev->name, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn)); - g_string_append_printf(buf, "\tcommands : %" PRId64 "\n", - dev->stats.commands); - g_string_append_printf(buf, "\tregs_reads : %" PRId64 "\n", - dev->stats.regs_reads); - g_string_append_printf(buf, "\tregs_writes : %" PRId64 "\n", - dev->stats.regs_writes); - g_string_append_printf(buf, "\tuar_writes : %" PRId64 "\n", - dev->stats.uar_writes); - g_string_append_printf(buf, "\tinterrupts : %" PRId64 "\n", - dev->stats.interrupts); - rdma_format_device_counters(&dev->rdma_dev_res, buf); -} - -static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring, - void *ring_state) -{ - pvrdma_ring_free(ring); - rdma_pci_dma_unmap(pci_dev, ring_state, TARGET_PAGE_SIZE); -} - -static int init_dev_ring(PvrdmaRing *ring, PvrdmaRingState **ring_state, - const char *name, PCIDevice *pci_dev, - dma_addr_t dir_addr, uint32_t num_pages) -{ - uint64_t *dir, *tbl; - int rc = 0; - - if (!num_pages) { - rdma_error_report("Ring pages count must be strictly positive"); - 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; - } - 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); - rc = -ENOMEM; - goto out_free_dir; - } - - *ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); - if (!*ring_state) { - rdma_error_report("Failed to map to ring state (ring %s)", name); - rc = -ENOMEM; - goto out_free_tbl; - } - /* RX ring is the second */ - (*ring_state)++; - rc = pvrdma_ring_init(ring, name, pci_dev, - (PvrdmaRingState *)*ring_state, - (num_pages - 1) * TARGET_PAGE_SIZE / - sizeof(struct pvrdma_cqne), - sizeof(struct pvrdma_cqne), - (dma_addr_t *)&tbl[1], (dma_addr_t)num_pages - 1); - if (rc) { - rc = -ENOMEM; - goto out_free_ring_state; - } - - goto out_free_tbl; - -out_free_ring_state: - rdma_pci_dma_unmap(pci_dev, *ring_state, TARGET_PAGE_SIZE); - -out_free_tbl: - rdma_pci_dma_unmap(pci_dev, tbl, TARGET_PAGE_SIZE); - -out_free_dir: - rdma_pci_dma_unmap(pci_dev, dir, TARGET_PAGE_SIZE); - -out: - return rc; -} - -static void free_dsr(PVRDMADev *dev) -{ - PCIDevice *pci_dev = PCI_DEVICE(dev); - - if (!dev->dsr_info.dsr) { - return; - } - - free_dev_ring(pci_dev, &dev->dsr_info.async, - dev->dsr_info.async_ring_state); - - free_dev_ring(pci_dev, &dev->dsr_info.cq, dev->dsr_info.cq_ring_state); - - rdma_pci_dma_unmap(pci_dev, dev->dsr_info.req, - sizeof(union pvrdma_cmd_req)); - - rdma_pci_dma_unmap(pci_dev, dev->dsr_info.rsp, - sizeof(union pvrdma_cmd_resp)); - - rdma_pci_dma_unmap(pci_dev, dev->dsr_info.dsr, - sizeof(struct pvrdma_device_shared_region)); - - dev->dsr_info.dsr = NULL; -} - -static int load_dsr(PVRDMADev *dev) -{ - int rc = 0; - PCIDevice *pci_dev = PCI_DEVICE(dev); - DSRInfo *dsr_info; - struct pvrdma_device_shared_region *dsr; - - free_dsr(dev); - - /* Map to DSR */ - dev->dsr_info.dsr = rdma_pci_dma_map(pci_dev, dev->dsr_info.dma, - sizeof(struct pvrdma_device_shared_region)); - if (!dev->dsr_info.dsr) { - rdma_error_report("Failed to map to DSR"); - rc = -ENOMEM; - goto out; - } - - /* Shortcuts */ - dsr_info = &dev->dsr_info; - dsr = dsr_info->dsr; - - /* Map to command slot */ - dsr_info->req = rdma_pci_dma_map(pci_dev, dsr->cmd_slot_dma, - sizeof(union pvrdma_cmd_req)); - if (!dsr_info->req) { - rdma_error_report("Failed to map to command slot address"); - rc = -ENOMEM; - goto out_free_dsr; - } - - /* Map to response slot */ - dsr_info->rsp = rdma_pci_dma_map(pci_dev, dsr->resp_slot_dma, - sizeof(union pvrdma_cmd_resp)); - if (!dsr_info->rsp) { - rdma_error_report("Failed to map to response slot address"); - rc = -ENOMEM; - goto out_free_req; - } - - /* Map to CQ notification ring */ - rc = init_dev_ring(&dsr_info->cq, &dsr_info->cq_ring_state, "dev_cq", - pci_dev, dsr->cq_ring_pages.pdir_dma, - dsr->cq_ring_pages.num_pages); - if (rc) { - rc = -ENOMEM; - goto out_free_rsp; - } - - /* Map to event notification ring */ - rc = init_dev_ring(&dsr_info->async, &dsr_info->async_ring_state, - "dev_async", pci_dev, dsr->async_ring_pages.pdir_dma, - dsr->async_ring_pages.num_pages); - if (rc) { - rc = -ENOMEM; - goto out_free_rsp; - } - - goto out; - -out_free_rsp: - rdma_pci_dma_unmap(pci_dev, dsr_info->rsp, sizeof(union pvrdma_cmd_resp)); - -out_free_req: - rdma_pci_dma_unmap(pci_dev, dsr_info->req, sizeof(union pvrdma_cmd_req)); - -out_free_dsr: - rdma_pci_dma_unmap(pci_dev, dsr_info->dsr, - sizeof(struct pvrdma_device_shared_region)); - dsr_info->dsr = NULL; - -out: - return rc; -} - -static void init_dsr_dev_caps(PVRDMADev *dev) -{ - struct pvrdma_device_shared_region *dsr; - - if (!dev->dsr_info.dsr) { - /* Buggy or malicious guest driver */ - rdma_error_report("Can't initialized DSR"); - return; - } - - dsr = dev->dsr_info.dsr; - dsr->caps.fw_ver = PVRDMA_FW_VERSION; - dsr->caps.mode = PVRDMA_DEVICE_MODE_ROCE; - dsr->caps.gid_types |= PVRDMA_GID_TYPE_FLAG_ROCE_V1; - dsr->caps.max_uar = RDMA_BAR2_UAR_SIZE; - dsr->caps.max_mr_size = dev->dev_attr.max_mr_size; - dsr->caps.max_qp = dev->dev_attr.max_qp; - dsr->caps.max_qp_wr = dev->dev_attr.max_qp_wr; - dsr->caps.max_sge = dev->dev_attr.max_sge; - dsr->caps.max_cq = dev->dev_attr.max_cq; - dsr->caps.max_cqe = dev->dev_attr.max_cqe; - dsr->caps.max_mr = dev->dev_attr.max_mr; - dsr->caps.max_pd = dev->dev_attr.max_pd; - dsr->caps.max_ah = dev->dev_attr.max_ah; - dsr->caps.max_srq = dev->dev_attr.max_srq; - dsr->caps.max_srq_wr = dev->dev_attr.max_srq_wr; - dsr->caps.max_srq_sge = dev->dev_attr.max_srq_sge; - dsr->caps.gid_tbl_len = MAX_GIDS; - dsr->caps.sys_image_guid = 0; - dsr->caps.node_guid = dev->node_guid; - dsr->caps.phys_port_cnt = MAX_PORTS; - dsr->caps.max_pkeys = MAX_PKEYS; -} - -static void uninit_msix(PCIDevice *pdev, int used_vectors) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - int i; - - for (i = 0; i < used_vectors; i++) { - msix_vector_unuse(pdev, i); - } - - msix_uninit(pdev, &dev->msix, &dev->msix); -} - -static int init_msix(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - int i; - int rc; - - rc = msix_init(pdev, RDMA_MAX_INTRS, &dev->msix, RDMA_MSIX_BAR_IDX, - RDMA_MSIX_TABLE, &dev->msix, RDMA_MSIX_BAR_IDX, - RDMA_MSIX_PBA, 0, NULL); - - if (rc < 0) { - rdma_error_report("Failed to initialize MSI-X"); - return rc; - } - - for (i = 0; i < RDMA_MAX_INTRS; i++) { - msix_vector_use(PCI_DEVICE(dev), i); - } - - return 0; -} - -static void pvrdma_fini(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - notifier_remove(&dev->shutdown_notifier); - - pvrdma_qp_ops_fini(); - - rdma_backend_stop(&dev->backend_dev); - - rdma_rm_fini(&dev->rdma_dev_res, &dev->backend_dev, - dev->backend_eth_device_name); - - rdma_backend_fini(&dev->backend_dev); - - free_dsr(dev); - - if (msix_enabled(pdev)) { - uninit_msix(pdev, RDMA_MAX_INTRS); - } - - rdma_info_report("Device %s %x.%x is down", pdev->name, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); -} - -static void pvrdma_stop(PVRDMADev *dev) -{ - rdma_backend_stop(&dev->backend_dev); -} - -static void pvrdma_start(PVRDMADev *dev) -{ - rdma_backend_start(&dev->backend_dev); -} - -static void activate_device(PVRDMADev *dev) -{ - pvrdma_start(dev); - set_reg_val(dev, PVRDMA_REG_ERR, 0); -} - -static int unquiesce_device(PVRDMADev *dev) -{ - return 0; -} - -static void reset_device(PVRDMADev *dev) -{ - pvrdma_stop(dev); -} - -static uint64_t pvrdma_regs_read(void *opaque, hwaddr addr, unsigned size) -{ - PVRDMADev *dev = opaque; - uint32_t val; - - dev->stats.regs_reads++; - - if (get_reg_val(dev, addr, &val)) { - rdma_error_report("Failed to read REG value from address 0x%x", - (uint32_t)addr); - return -EINVAL; - } - - trace_pvrdma_regs_read(addr, val); - - return val; -} - -static void pvrdma_regs_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - PVRDMADev *dev = opaque; - - dev->stats.regs_writes++; - - if (set_reg_val(dev, addr, val)) { - rdma_error_report("Failed to set REG value, addr=0x%"PRIx64 ", val=0x%"PRIx64, - addr, val); - return; - } - - switch (addr) { - case PVRDMA_REG_DSRLOW: - trace_pvrdma_regs_write(addr, val, "DSRLOW", ""); - dev->dsr_info.dma = val; - break; - case PVRDMA_REG_DSRHIGH: - trace_pvrdma_regs_write(addr, val, "DSRHIGH", ""); - dev->dsr_info.dma |= val << 32; - load_dsr(dev); - init_dsr_dev_caps(dev); - break; - case PVRDMA_REG_CTL: - switch (val) { - case PVRDMA_DEVICE_CTL_ACTIVATE: - trace_pvrdma_regs_write(addr, val, "CTL", "ACTIVATE"); - activate_device(dev); - break; - case PVRDMA_DEVICE_CTL_UNQUIESCE: - trace_pvrdma_regs_write(addr, val, "CTL", "UNQUIESCE"); - unquiesce_device(dev); - break; - case PVRDMA_DEVICE_CTL_RESET: - trace_pvrdma_regs_write(addr, val, "CTL", "URESET"); - reset_device(dev); - break; - } - break; - case PVRDMA_REG_IMR: - trace_pvrdma_regs_write(addr, val, "INTR_MASK", ""); - dev->interrupt_mask = val; - break; - case PVRDMA_REG_REQUEST: - if (val == 0) { - trace_pvrdma_regs_write(addr, val, "REQUEST", ""); - pvrdma_exec_cmd(dev); - } - break; - default: - break; - } -} - -static const MemoryRegionOps regs_ops = { - .read = pvrdma_regs_read, - .write = pvrdma_regs_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = sizeof(uint32_t), - .max_access_size = sizeof(uint32_t), - }, -}; - -static uint64_t pvrdma_uar_read(void *opaque, hwaddr addr, unsigned size) -{ - return 0xffffffff; -} - -static void pvrdma_uar_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - PVRDMADev *dev = opaque; - - dev->stats.uar_writes++; - - switch (addr & 0xFFF) { /* Mask with 0xFFF as each UC gets page */ - case PVRDMA_UAR_QP_OFFSET: - if (val & PVRDMA_UAR_QP_SEND) { - trace_pvrdma_uar_write(addr, val, "QP", "SEND", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_qp_send(dev, val & PVRDMA_UAR_HANDLE_MASK); - } - if (val & PVRDMA_UAR_QP_RECV) { - trace_pvrdma_uar_write(addr, val, "QP", "RECV", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_qp_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); - } - break; - case PVRDMA_UAR_CQ_OFFSET: - if (val & PVRDMA_UAR_CQ_ARM) { - trace_pvrdma_uar_write(addr, val, "CQ", "ARM", - val & PVRDMA_UAR_HANDLE_MASK, - !!(val & PVRDMA_UAR_CQ_ARM_SOL)); - rdma_rm_req_notify_cq(&dev->rdma_dev_res, - val & PVRDMA_UAR_HANDLE_MASK, - !!(val & PVRDMA_UAR_CQ_ARM_SOL)); - } - if (val & PVRDMA_UAR_CQ_ARM_SOL) { - trace_pvrdma_uar_write(addr, val, "CQ", "ARMSOL - not supported", 0, - 0); - } - if (val & PVRDMA_UAR_CQ_POLL) { - trace_pvrdma_uar_write(addr, val, "CQ", "POLL", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_cq_poll(&dev->rdma_dev_res, val & PVRDMA_UAR_HANDLE_MASK); - } - break; - case PVRDMA_UAR_SRQ_OFFSET: - if (val & PVRDMA_UAR_SRQ_RECV) { - trace_pvrdma_uar_write(addr, val, "QP", "SRQ", - val & PVRDMA_UAR_HANDLE_MASK, 0); - pvrdma_srq_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); - } - break; - default: - rdma_error_report("Unsupported command, addr=0x%"PRIx64", val=0x%"PRIx64, - addr, val); - break; - } -} - -static const MemoryRegionOps uar_ops = { - .read = pvrdma_uar_read, - .write = pvrdma_uar_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = sizeof(uint32_t), - .max_access_size = sizeof(uint32_t), - }, -}; - -static void init_pci_config(PCIDevice *pdev) -{ - pdev->config[PCI_INTERRUPT_PIN] = 1; -} - -static void init_bars(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - /* BAR 0 - MSI-X */ - memory_region_init(&dev->msix, OBJECT(dev), "pvrdma-msix", - RDMA_BAR0_MSIX_SIZE); - pci_register_bar(pdev, RDMA_MSIX_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->msix); - - /* BAR 1 - Registers */ - memset(&dev->regs_data, 0, sizeof(dev->regs_data)); - memory_region_init_io(&dev->regs, OBJECT(dev), ®s_ops, dev, - "pvrdma-regs", sizeof(dev->regs_data)); - pci_register_bar(pdev, RDMA_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->regs); - - /* BAR 2 - UAR */ - memset(&dev->uar_data, 0, sizeof(dev->uar_data)); - memory_region_init_io(&dev->uar, OBJECT(dev), &uar_ops, dev, "rdma-uar", - sizeof(dev->uar_data)); - pci_register_bar(pdev, RDMA_UAR_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, - &dev->uar); -} - -static void init_regs(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - set_reg_val(dev, PVRDMA_REG_VERSION, PVRDMA_HW_VERSION); - set_reg_val(dev, PVRDMA_REG_ERR, 0xFFFF); -} - -static void init_dev_caps(PVRDMADev *dev) -{ - size_t pg_tbl_bytes = TARGET_PAGE_SIZE * - (TARGET_PAGE_SIZE / sizeof(uint64_t)); - size_t wr_sz = MAX(sizeof(struct pvrdma_sq_wqe_hdr), - sizeof(struct pvrdma_rq_wqe_hdr)); - - dev->dev_attr.max_qp_wr = pg_tbl_bytes / - (wr_sz + sizeof(struct pvrdma_sge) * - dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; - /* First page is ring state ^^^^ */ - - dev->dev_attr.max_cqe = pg_tbl_bytes / sizeof(struct pvrdma_cqe) - - TARGET_PAGE_SIZE; /* First page is ring state */ - - dev->dev_attr.max_srq_wr = pg_tbl_bytes / - ((sizeof(struct pvrdma_rq_wqe_hdr) + - sizeof(struct pvrdma_sge)) * - dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; -} - -static int pvrdma_check_ram_shared(Object *obj, void *opaque) -{ - bool *shared = opaque; - - if (object_dynamic_cast(obj, "memory-backend-ram")) { - *shared = object_property_get_bool(obj, "share", NULL); - } - - return 0; -} - -static void pvrdma_shutdown_notifier(Notifier *n, void *opaque) -{ - PVRDMADev *dev = container_of(n, PVRDMADev, shutdown_notifier); - PCIDevice *pci_dev = PCI_DEVICE(dev); - - pvrdma_fini(pci_dev); -} - -static void pvrdma_realize(PCIDevice *pdev, Error **errp) -{ - int rc = 0; - PVRDMADev *dev = PVRDMA_DEV(pdev); - Object *memdev_root; - bool ram_shared = false; - PCIDevice *func0; - - rdma_info_report("Initializing device %s %x.%x", pdev->name, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - - if (TARGET_PAGE_SIZE != qemu_real_host_page_size()) { - error_setg(errp, "Target page size must be the same as host page size"); - return; - } - - func0 = pci_get_function_0(pdev); - /* Break if not vmxnet3 device in slot 0 */ - if (strcmp(object_get_typename(OBJECT(func0)), TYPE_VMXNET3)) { - error_setg(errp, "Device on %x.0 must be %s", PCI_SLOT(pdev->devfn), - TYPE_VMXNET3); - return; - } - dev->func0 = VMXNET3(func0); - - addrconf_addr_eui48((unsigned char *)&dev->node_guid, - (const char *)&dev->func0->conf.macaddr.a); - - memdev_root = object_resolve_path("/objects", NULL); - if (memdev_root) { - object_child_foreach(memdev_root, pvrdma_check_ram_shared, &ram_shared); - } - if (!ram_shared) { - error_setg(errp, "Only shared memory backed ram is supported"); - return; - } - - dev->dsr_info.dsr = NULL; - - init_pci_config(pdev); - - init_bars(pdev); - - init_regs(pdev); - - rc = init_msix(pdev); - if (rc) { - goto out; - } - - rc = rdma_backend_init(&dev->backend_dev, pdev, &dev->rdma_dev_res, - dev->backend_device_name, dev->backend_port_num, - &dev->dev_attr, &dev->mad_chr); - if (rc) { - goto out; - } - - init_dev_caps(dev); - - rc = rdma_rm_init(&dev->rdma_dev_res, &dev->dev_attr); - if (rc) { - goto out; - } - - rc = pvrdma_qp_ops_init(); - if (rc) { - goto out; - } - - memset(&dev->stats, 0, sizeof(dev->stats)); - - dev->shutdown_notifier.notify = pvrdma_shutdown_notifier; - qemu_register_shutdown_notifier(&dev->shutdown_notifier); - -#ifdef LEGACY_RDMA_REG_MR - rdma_info_report("Using legacy reg_mr"); -#else - rdma_info_report("Using iova reg_mr"); -#endif - -out: - if (rc) { - pvrdma_fini(pdev); - error_append_hint(errp, "Device failed to load\n"); - } -} - -static void pvrdma_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - RdmaProviderClass *ir = RDMA_PROVIDER_CLASS(klass); - - k->realize = pvrdma_realize; - k->vendor_id = PCI_VENDOR_ID_VMWARE; - k->device_id = PCI_DEVICE_ID_VMWARE_PVRDMA; - k->revision = 0x00; - k->class_id = PCI_CLASS_NETWORK_OTHER; - - dc->desc = "RDMA Device"; - device_class_set_props(dc, pvrdma_dev_properties); - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - - ir->format_statistics = pvrdma_format_statistics; -} - -static const TypeInfo pvrdma_info = { - .name = PVRDMA_HW_NAME, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PVRDMADev), - .class_init = pvrdma_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { INTERFACE_RDMA_PROVIDER }, - { } - } -}; - -static void register_types(void) -{ - type_register_static(&pvrdma_info); -} - -type_init(register_types) diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c deleted file mode 100644 index bd7cbf2bdf..0000000000 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * QEMU paravirtual RDMA - QP implementation - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 "../rdma_utils.h" -#include "../rdma_rm.h" -#include "../rdma_backend.h" - -#include "trace.h" - -#include "pvrdma.h" -#include "standard-headers/rdma/vmw_pvrdma-abi.h" -#include "pvrdma_qp_ops.h" - -typedef struct CompHandlerCtx { - PVRDMADev *dev; - uint32_t cq_handle; - struct pvrdma_cqe cqe; -} CompHandlerCtx; - -/* Send Queue WQE */ -typedef struct PvrdmaSqWqe { - struct pvrdma_sq_wqe_hdr hdr; - struct pvrdma_sge sge[]; -} PvrdmaSqWqe; - -/* Recv Queue WQE */ -typedef struct PvrdmaRqWqe { - struct pvrdma_rq_wqe_hdr hdr; - struct pvrdma_sge sge[]; -} PvrdmaRqWqe; - -/* - * 1. Put CQE on send CQ ring - * 2. Put CQ number on dsr completion ring - * 3. Interrupt host - */ -static int pvrdma_post_cqe(PVRDMADev *dev, uint32_t cq_handle, - struct pvrdma_cqe *cqe, struct ibv_wc *wc) -{ - struct pvrdma_cqe *cqe1; - struct pvrdma_cqne *cqne; - PvrdmaRing *ring; - RdmaRmCQ *cq = rdma_rm_get_cq(&dev->rdma_dev_res, cq_handle); - - if (unlikely(!cq)) { - return -EINVAL; - } - - ring = (PvrdmaRing *)cq->opaque; - - /* Step #1: Put CQE on CQ ring */ - cqe1 = pvrdma_ring_next_elem_write(ring); - if (unlikely(!cqe1)) { - return -EINVAL; - } - - memset(cqe1, 0, sizeof(*cqe1)); - cqe1->wr_id = cqe->wr_id; - cqe1->qp = cqe->qp ? cqe->qp : wc->qp_num; - cqe1->opcode = cqe->opcode; - cqe1->status = wc->status; - cqe1->byte_len = wc->byte_len; - cqe1->src_qp = wc->src_qp; - cqe1->wc_flags = wc->wc_flags; - cqe1->vendor_err = wc->vendor_err; - - trace_pvrdma_post_cqe(cq_handle, cq->notify, cqe1->wr_id, cqe1->qp, - cqe1->opcode, cqe1->status, cqe1->byte_len, - cqe1->src_qp, cqe1->wc_flags, cqe1->vendor_err); - - pvrdma_ring_write_inc(ring); - - /* Step #2: Put CQ number on dsr completion ring */ - cqne = pvrdma_ring_next_elem_write(&dev->dsr_info.cq); - if (unlikely(!cqne)) { - return -EINVAL; - } - - cqne->info = cq_handle; - pvrdma_ring_write_inc(&dev->dsr_info.cq); - - if (cq->notify != CNT_CLEAR) { - if (cq->notify == CNT_ARM) { - cq->notify = CNT_CLEAR; - } - post_interrupt(dev, INTR_VEC_CMD_COMPLETION_Q); - } - - return 0; -} - -static void pvrdma_qp_ops_comp_handler(void *ctx, struct ibv_wc *wc) -{ - CompHandlerCtx *comp_ctx = (CompHandlerCtx *)ctx; - - pvrdma_post_cqe(comp_ctx->dev, comp_ctx->cq_handle, &comp_ctx->cqe, wc); - - g_free(ctx); -} - -static void complete_with_error(uint32_t vendor_err, void *ctx) -{ - struct ibv_wc wc = {}; - - wc.status = IBV_WC_GENERAL_ERR; - wc.vendor_err = vendor_err; - - pvrdma_qp_ops_comp_handler(ctx, &wc); -} - -void pvrdma_qp_ops_fini(void) -{ - rdma_backend_unregister_comp_handler(); -} - -int pvrdma_qp_ops_init(void) -{ - rdma_backend_register_comp_handler(pvrdma_qp_ops_comp_handler); - - return 0; -} - -void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) -{ - RdmaRmQP *qp; - PvrdmaSqWqe *wqe; - PvrdmaRing *ring; - int sgid_idx; - union ibv_gid *sgid; - - qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle); - if (unlikely(!qp)) { - return; - } - - ring = (PvrdmaRing *)qp->opaque; - - wqe = (struct PvrdmaSqWqe *)pvrdma_ring_next_elem_read(ring); - while (wqe) { - CompHandlerCtx *comp_ctx; - - /* Prepare CQE */ - comp_ctx = g_new(CompHandlerCtx, 1); - comp_ctx->dev = dev; - comp_ctx->cq_handle = qp->send_cq_handle; - comp_ctx->cqe.wr_id = wqe->hdr.wr_id; - comp_ctx->cqe.qp = qp_handle; - comp_ctx->cqe.opcode = IBV_WC_SEND; - - sgid = rdma_rm_get_gid(&dev->rdma_dev_res, wqe->hdr.wr.ud.av.gid_index); - if (!sgid) { - rdma_error_report("Failed to get gid for idx %d", - wqe->hdr.wr.ud.av.gid_index); - complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx); - continue; - } - - sgid_idx = rdma_rm_get_backend_gid_index(&dev->rdma_dev_res, - &dev->backend_dev, - wqe->hdr.wr.ud.av.gid_index); - if (sgid_idx <= 0) { - rdma_error_report("Failed to get bk sgid_idx for sgid_idx %d", - wqe->hdr.wr.ud.av.gid_index); - complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx); - continue; - } - - if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { - rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, - dev->dev_attr.max_sge); - complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); - continue; - } - - rdma_backend_post_send(&dev->backend_dev, &qp->backend_qp, qp->qp_type, - (struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge, - sgid_idx, sgid, - (union ibv_gid *)wqe->hdr.wr.ud.av.dgid, - wqe->hdr.wr.ud.remote_qpn, - wqe->hdr.wr.ud.remote_qkey, comp_ctx); - - pvrdma_ring_read_inc(ring); - - wqe = pvrdma_ring_next_elem_read(ring); - } -} - -void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) -{ - RdmaRmQP *qp; - PvrdmaRqWqe *wqe; - PvrdmaRing *ring; - - qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle); - if (unlikely(!qp)) { - return; - } - - ring = &((PvrdmaRing *)qp->opaque)[1]; - - wqe = (struct PvrdmaRqWqe *)pvrdma_ring_next_elem_read(ring); - while (wqe) { - CompHandlerCtx *comp_ctx; - - /* Prepare CQE */ - comp_ctx = g_new(CompHandlerCtx, 1); - comp_ctx->dev = dev; - comp_ctx->cq_handle = qp->recv_cq_handle; - comp_ctx->cqe.wr_id = wqe->hdr.wr_id; - comp_ctx->cqe.qp = qp_handle; - comp_ctx->cqe.opcode = IBV_WC_RECV; - - if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { - rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, - dev->dev_attr.max_sge); - complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); - continue; - } - - rdma_backend_post_recv(&dev->backend_dev, &qp->backend_qp, qp->qp_type, - (struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge, - comp_ctx); - - pvrdma_ring_read_inc(ring); - - wqe = pvrdma_ring_next_elem_read(ring); - } -} - -void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle) -{ - RdmaRmSRQ *srq; - PvrdmaRqWqe *wqe; - PvrdmaRing *ring; - - srq = rdma_rm_get_srq(&dev->rdma_dev_res, srq_handle); - if (unlikely(!srq)) { - return; - } - - ring = (PvrdmaRing *)srq->opaque; - - wqe = (struct PvrdmaRqWqe *)pvrdma_ring_next_elem_read(ring); - while (wqe) { - CompHandlerCtx *comp_ctx; - - /* Prepare CQE */ - comp_ctx = g_new(CompHandlerCtx, 1); - comp_ctx->dev = dev; - comp_ctx->cq_handle = srq->recv_cq_handle; - comp_ctx->cqe.wr_id = wqe->hdr.wr_id; - comp_ctx->cqe.qp = 0; - comp_ctx->cqe.opcode = IBV_WC_RECV; - - if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { - rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, - dev->dev_attr.max_sge); - complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); - continue; - } - - rdma_backend_post_srq_recv(&dev->backend_dev, &srq->backend_srq, - (struct ibv_sge *)&wqe->sge[0], - wqe->hdr.num_sge, - comp_ctx); - - pvrdma_ring_read_inc(ring); - - wqe = pvrdma_ring_next_elem_read(ring); - } - -} - -void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) -{ - RdmaRmCQ *cq; - - cq = rdma_rm_get_cq(dev_res, cq_handle); - if (!cq) { - return; - } - - rdma_backend_poll_cq(dev_res, &cq->backend_cq); -} diff --git a/hw/rdma/vmw/pvrdma_qp_ops.h b/hw/rdma/vmw/pvrdma_qp_ops.h deleted file mode 100644 index bf2b15c5ce..0000000000 --- a/hw/rdma/vmw/pvrdma_qp_ops.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * QEMU VMWARE paravirtual RDMA QP Operations - * - * Copyright (C) 2018 Oracle - * Copyright (C) 2018 Red Hat Inc - * - * Authors: - * Yuval Shaia - * Marcel Apfelbaum - * - * 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 PVRDMA_QP_OPS_H -#define PVRDMA_QP_OPS_H - -#include "pvrdma.h" - -int pvrdma_qp_ops_init(void); -void pvrdma_qp_ops_fini(void); -void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle); -void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle); -void pvrdma_srq_recv(PVRDMADev *dev, uint32_t srq_handle); -void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle); - -#endif diff --git a/hw/rdma/vmw/trace-events b/hw/rdma/vmw/trace-events deleted file mode 100644 index a6c77e1e10..0000000000 --- a/hw/rdma/vmw/trace-events +++ /dev/null @@ -1,17 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# pvrdma_main.c -pvrdma_regs_read(uint64_t addr, uint64_t val) "pvrdma.regs[0x%"PRIx64"]=0x%"PRIx64 -pvrdma_regs_write(uint64_t addr, uint64_t val, const char *reg_name, const char *val_name) "pvrdma.regs[0x%"PRIx64"]=0x%"PRIx64" (%s %s)" -pvrdma_uar_write(uint64_t addr, uint64_t val, const char *reg_name, const char *val_name, int val1, int val2) "uar[0x%"PRIx64"]=0x%"PRIx64" (cls=%s, op=%s, obj=%d, val=%d)" - -# pvrdma_cmd.c -pvrdma_map_to_pdir_host_virt(void *vfirst, void *vremaped) "mremap %p -> %p" -pvrdma_map_to_pdir_next_page(int page_idx, void *vnext, void *vremaped) "mremap [%d] %p -> %p" -pvrdma_exec_cmd(int cmd, int err) "cmd=%d, err=%d" - -# pvrdma_dev_ring.c -pvrdma_ring_next_elem_read_no_data(char *ring_name) "pvrdma_ring %s is empty" - -# pvrdma_qp_ops.c -pvrdma_post_cqe(uint32_t cq_handle, int notify, uint64_t wr_id, uint64_t qpn, uint32_t op_code, uint32_t status, uint32_t byte_len, uint32_t src_qp, uint32_t wc_flags, uint32_t vendor_err) "cq_handle=%d, notify=%d, wr_id=0x%"PRIx64", qpn=0x%"PRIx64", opcode=%d, status=%d, byte_len=%d, src_qp=%d, wc_flags=%d, vendor_err=%d" diff --git a/hw/rdma/vmw/trace.h b/hw/rdma/vmw/trace.h deleted file mode 100644 index 3ebc9fb7ad..0000000000 --- a/hw/rdma/vmw/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-hw_rdma_vmw.h" diff --git a/hw/remote/iohub.c b/hw/remote/iohub.c index 40dfee4bad..988d3285cc 100644 --- a/hw/remote/iohub.c +++ b/hw/remote/iohub.c @@ -33,19 +33,6 @@ void remote_iohub_init(RemoteIOHubState *iohub) } } -void remote_iohub_finalize(RemoteIOHubState *iohub) -{ - int pirq; - - for (pirq = 0; pirq < REMOTE_IOHUB_NB_PIRQS; pirq++) { - qemu_set_fd_handler(event_notifier_get_fd(&iohub->resamplefds[pirq]), - NULL, NULL, NULL); - event_notifier_cleanup(&iohub->irqfds[pirq]); - event_notifier_cleanup(&iohub->resamplefds[pirq]); - qemu_mutex_destroy(&iohub->irq_level_lock[pirq]); - } -} - int remote_iohub_map_irq(PCIDevice *pci_dev, int intx) { return pci_dev->devfn; diff --git a/hw/remote/iommu.c b/hw/remote/iommu.c index 1391dd712c..7c56aad0fc 100644 --- a/hw/remote/iommu.c +++ b/hw/remote/iommu.c @@ -100,6 +100,10 @@ static void remote_iommu_finalize(Object *obj) 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; @@ -108,7 +112,7 @@ void remote_iommu_setup(PCIBus *pci_bus) iommu = REMOTE_IOMMU(object_new(TYPE_REMOTE_IOMMU)); - pci_setup_iommu(pci_bus, remote_iommu_find_add_as, iommu); + pci_setup_iommu(pci_bus, &remote_iommu_ops, iommu); object_property_add_child(OBJECT(pci_bus), "remote-iommu", OBJECT(iommu)); diff --git a/hw/remote/machine.c b/hw/remote/machine.c index 75d550daae..fdc6c441bb 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -22,7 +22,6 @@ #include "hw/remote/iohub.h" #include "hw/remote/iommu.h" #include "hw/qdev-core.h" -#include "hw/remote/iommu.h" #include "hw/remote/vfio-user-obj.h" #include "hw/pci/msi.h" @@ -63,8 +62,9 @@ static void remote_machine_init(MachineState *machine) } else { remote_iohub_init(&s->iohub); - pci_bus_irqs(pci_host->bus, remote_iohub_set_irq, remote_iohub_map_irq, + 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)); diff --git a/hw/remote/meson.build b/hw/remote/meson.build index ab25c04906..41eb4971d9 100644 --- a/hw/remote/meson.build +++ b/hw/remote/meson.build @@ -7,11 +7,12 @@ 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: files('vfio-user-obj.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/message.c b/hw/remote/message.c index 50f6bf2d49..38ae6c75b4 100644 --- a/hw/remote/message.c +++ b/hw/remote/message.c @@ -215,13 +215,10 @@ fail: static void process_device_reset_msg(QIOChannel *ioc, PCIDevice *dev, Error **errp) { - DeviceClass *dc = DEVICE_GET_CLASS(dev); DeviceState *s = DEVICE(dev); MPQemuMsg ret = { 0 }; - if (dc->reset) { - dc->reset(s); - } + device_cold_reset(s); ret.cmd = MPQEMU_CMD_RET; 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..302a0a4d4d 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); @@ -210,7 +206,7 @@ static void pci_proxy_dev_class_init(ObjectClass *klass, void *data) k->config_read = pci_proxy_read_config; k->config_write = pci_proxy_write_config; - dc->reset = proxy_device_reset; + device_class_set_legacy_reset(dc, proxy_device_reset); device_class_set_props(dc, proxy_properties); } 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/stubs/vfio-user-obj.c b/hw/remote/vfio-user-obj-stub.c similarity index 100% rename from stubs/vfio-user-obj.c rename to hw/remote/vfio-user-obj-stub.c diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 4e36bb8bcf..8dbafafb9e 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -30,6 +30,11 @@ * * 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" @@ -61,9 +66,12 @@ 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. + * 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, ...) \ { \ @@ -273,7 +281,7 @@ static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf, while (bytes > 0) { len = (bytes > pci_access_width) ? pci_access_width : bytes; if (is_write) { - memcpy(&val, ptr, len); + val = ldn_le_p(ptr, len); pci_host_config_write_common(o->pci_dev, offset, pci_config_size(o->pci_dev), val, len); @@ -281,7 +289,7 @@ static ssize_t vfu_object_cfg_access(vfu_ctx_t *vfu_ctx, char * const buf, } else { val = pci_host_config_read_common(o->pci_dev, offset, pci_config_size(o->pci_dev), len); - memcpy(ptr, &val, len); + stn_le_p(ptr, len, val); trace_vfu_cfg_read(offset, val); } offset += len; @@ -392,7 +400,7 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, } if (release_lock) { - qemu_mutex_unlock_iothread(); + bql_unlock(); release_lock = false; } @@ -665,8 +673,8 @@ 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, vfu_object_map_irq, pci_bus, - max_bdf); + 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) @@ -678,7 +686,7 @@ static int vfu_object_device_reset(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type) return 0; } - qdev_reset_all(DEVICE(o->pci_dev)); + device_cold_reset(DEVICE(o->pci_dev)); return 0; } @@ -719,7 +727,6 @@ static void vfu_object_machine_done(Notifier *notifier, void *data) */ static void vfu_object_init_ctx(VfuObject *o, Error **errp) { - ERRP_GUARD(); DeviceState *dev = NULL; vfu_pci_type_t pci_type = VFU_PCI_TYPE_CONVENTIONAL; int ret; diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 79ff61c464..2e88467c4a 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -1,17 +1,25 @@ +config RISCV_IOMMU + bool + config RISCV_NUMA bool config IBEX bool +# RISC-V machines in alphabetical order + config MICROCHIP_PFSOC bool + default y + depends on RISCV64 select CADENCE_SDHCI + select CPU_CLUSTER + select DEVICE_TREE select MCHP_PFSOC_DMC select MCHP_PFSOC_IOSCB select MCHP_PFSOC_MMUART select MCHP_PFSOC_SYSREG - select MSI_NONBROKEN select RISCV_ACLINT select SIFIVE_PDMA select SIFIVE_PLIC @@ -19,52 +27,68 @@ config MICROCHIP_PFSOC config OPENTITAN bool + default y + depends on RISCV32 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 + default y + depends on RISCV32 || RISCV64 imply PCI_DEVICES imply VIRTIO_VGA imply TEST_DEVICES imply TPM_TIS_SYSBUS + select DEVICE_TREE select RISCV_NUMA select GOLDFISH_RTC - select MSI_NONBROKEN select PCI select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select SERIAL + select SERIAL_MM select RISCV_ACLINT select RISCV_APLIC + select RISCV_IOMMU 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 + default y + depends on RISCV64 + select RISCV_ACLINT + select SHAKTI_UART + select SIFIVE_PLIC + select UNIMP config SIFIVE_E bool - select MSI_NONBROKEN + default y + depends on RISCV32 || RISCV64 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 + default y + depends on RISCV32 || RISCV64 select CADENCE - select MSI_NONBROKEN + select CPU_CLUSTER + select DEVICE_TREE select RISCV_ACLINT select SIFIVE_GPIO select SIFIVE_PDMA @@ -80,8 +104,10 @@ config SIFIVE_U config SPIKE bool + default y + depends on RISCV32 || RISCV64 + select DEVICE_TREE 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 ebd351c840..2e319168db 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -36,7 +36,8 @@ 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; } /* @@ -75,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, @@ -104,58 +147,85 @@ 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 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 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, + hwaddr *firmware_load_addr, symbol_fn_t sym_cb) { 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, 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { + *firmware_load_addr = firmware_entry; return firmware_end; } firmware_size = load_image_targphys_as(firmware_filename, - firmware_load_addr, + *firmware_load_addr, current_machine->ram_size, NULL); if (firmware_size > 0) { - return firmware_load_addr + firmware_size; + return *firmware_load_addr + firmware_size; } error_report("could not load firmware '%s'", 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_u64(fdt, "/chosen", "linux,initrd-start", start); + qemu_fdt_setprop_u64(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 @@ -167,64 +237,84 @@ 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) -{ - ssize_t 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. @@ -232,11 +322,18 @@ uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) * 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, 2 * 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); @@ -244,31 +341,35 @@ uint64_t riscv_load_fdt(hwaddr dram_base, uint64_t mem_size, void *fdt) &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, rom_ptr_for_as(&address_space_memory, fdt_addr, fdtsize)); - - return fdt_addr; } -void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, - hwaddr rom_size, uint32_t reset_vec_size, +void riscv_rom_copy_firmware_info(MachineState *machine, + RISCVHartArrayState *harts, + hwaddr rom_base, hwaddr rom_size, + uint32_t reset_vec_size, uint64_t kernel_entry) { + struct fw_dynamic_info32 dinfo32; struct fw_dynamic_info dinfo; size_t dinfo_len; - if (sizeof(dinfo.magic) == 4) { - dinfo.magic = cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE); - dinfo.version = cpu_to_le32(FW_DYNAMIC_INFO_VERSION); - dinfo.next_mode = cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S); - dinfo.next_addr = cpu_to_le32(kernel_entry); + if (riscv_is_32bit(harts)) { + dinfo32.magic = cpu_to_le32(FW_DYNAMIC_INFO_MAGIC_VALUE); + dinfo32.version = cpu_to_le32(FW_DYNAMIC_INFO_VERSION); + dinfo32.next_mode = cpu_to_le32(FW_DYNAMIC_INFO_NEXT_MODE_S); + dinfo32.next_addr = cpu_to_le32(kernel_entry); + dinfo32.options = 0; + dinfo32.boot_hart = 0; + dinfo_len = sizeof(dinfo32); } else { dinfo.magic = cpu_to_le64(FW_DYNAMIC_INFO_MAGIC_VALUE); dinfo.version = cpu_to_le64(FW_DYNAMIC_INFO_VERSION); dinfo.next_mode = cpu_to_le64(FW_DYNAMIC_INFO_NEXT_MODE_S); dinfo.next_addr = cpu_to_le64(kernel_entry); + dinfo.options = 0; + dinfo.boot_hart = 0; + dinfo_len = sizeof(dinfo); } - dinfo.options = 0; - dinfo.boot_hart = 0; - dinfo_len = sizeof(dinfo); /** * copy the dynamic firmware info. This information is specific to @@ -280,7 +381,10 @@ void riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, exit(1); } - rom_add_blob_fixed_as("mrom.finfo", &dinfo, dinfo_len, + rom_add_blob_fixed_as("mrom.finfo", + riscv_is_32bit(harts) ? + (void *)&dinfo32 : (void *)&dinfo, + dinfo_len, rom_base + reset_vec_size, &address_space_memory); } @@ -321,13 +425,24 @@ 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]); } rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), rom_base, &address_space_memory); - riscv_rom_copy_firmware_info(machine, rom_base, rom_size, sizeof(reset_vec), + riscv_rom_copy_firmware_info(machine, harts, + rom_base, rom_size, + sizeof(reset_vec), kernel_entry); } diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index ab6cae57ea..adbef8a9b2 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -1,5 +1,5 @@ riscv_ss = ss.source_set() -riscv_ss.add(files('boot.c'), fdt) +riscv_ss.add(files('boot.c')) riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c')) riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) @@ -9,5 +9,7 @@ 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')) +riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files('riscv-iommu.c', 'riscv-iommu-pci.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index a821263d4f..f9a3b43d2e 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -86,58 +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_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_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 }, + [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) @@ -199,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); @@ -303,6 +305,9 @@ 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", @@ -405,17 +410,8 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_USB].size); /* GEMs */ - - 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); - } + 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); @@ -456,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)); /* 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), @@ -606,34 +613,21 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) /* Load the firmware */ firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, - firmware_load_addr, NULL); + &firmware_load_addr, NULL); if (kernel_as_payload) { 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, @@ -655,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 edf6750f54..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); } } @@ -209,8 +209,8 @@ int64_t riscv_numa_get_default_cpu_node_id(const MachineState *ms, int idx) if (ms->numa_state->num_nodes > ms->smp.cpus) { error_report("Number of NUMA nodes (%d)" - " cannot exceed the number of available CPUs (%d).", - ms->numa_state->num_nodes, ms->smp.max_cpus); + " 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) { diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index be7ff1eea0..e2830e9dc2 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -22,55 +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, 0x8000 }, - [IBEX_DEV_RAM] = { 0x10000000, 0x20000 }, - [IBEX_DEV_FLASH] = { 0x20000000, 0x100000 }, - [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_LC_CTRL] = { 0x40140000, 0x1000 }, - [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) { @@ -89,27 +98,29 @@ static void opentitan_board_init(MachineState *machine) memmap[IBEX_DEV_RAM].base, machine->ram); if (machine->firmware) { - riscv_load_firmware(machine->firmware, memmap[IBEX_DEV_RAM].base, NULL); + hwaddr firmware_load_addr = memmap[IBEX_DEV_RAM].base; + riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL); } 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); @@ -165,10 +176,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); @@ -219,7 +228,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; @@ -265,8 +274,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", @@ -289,12 +298,12 @@ 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[] = { @@ -312,17 +321,19 @@ static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) 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/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h new file mode 100644 index 0000000000..6359ae0353 --- /dev/null +++ b/hw/riscv/riscv-iommu-bits.h @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright © 2022-2023 Rivos Inc. + * Copyright © 2023 FORTH-ICS/CARV + * Copyright © 2023 RISC-V IOMMU Task Group + * + * RISC-V IOMMU - Register Layout and Data Structures. + * + * Based on the IOMMU spec version 1.0, 3/2023 + * https://github.com/riscv-non-isa/riscv-iommu + */ + +#ifndef HW_RISCV_IOMMU_BITS_H +#define HW_RISCV_IOMMU_BITS_H + +#define RISCV_IOMMU_SPEC_DOT_VER 0x010 + +#ifndef GENMASK_ULL +#define GENMASK_ULL(h, l) (((~0ULL) >> (63 - (h) + (l))) << (l)) +#endif + +/* + * struct riscv_iommu_fq_record - Fault/Event Queue Record + * See section 3.2 for more info. + */ +struct riscv_iommu_fq_record { + uint64_t hdr; + uint64_t _reserved; + uint64_t iotval; + uint64_t iotval2; +}; +/* Header fields */ +#define RISCV_IOMMU_FQ_HDR_CAUSE GENMASK_ULL(11, 0) +#define RISCV_IOMMU_FQ_HDR_PID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_FQ_HDR_PV BIT_ULL(32) +#define RISCV_IOMMU_FQ_HDR_TTYPE GENMASK_ULL(39, 34) +#define RISCV_IOMMU_FQ_HDR_DID GENMASK_ULL(63, 40) + +/* + * struct riscv_iommu_pq_record - PCIe Page Request record + * For more infos on the PCIe Page Request queue see chapter 3.3. + */ +struct riscv_iommu_pq_record { + uint64_t hdr; + uint64_t payload; +}; +/* Header fields */ +#define RISCV_IOMMU_PREQ_HDR_PID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_PREQ_HDR_PV BIT_ULL(32) +#define RISCV_IOMMU_PREQ_HDR_PRIV BIT_ULL(33) +#define RISCV_IOMMU_PREQ_HDR_EXEC BIT_ULL(34) +#define RISCV_IOMMU_PREQ_HDR_DID GENMASK_ULL(63, 40) +/* Payload fields */ +#define RISCV_IOMMU_PREQ_PAYLOAD_M GENMASK_ULL(2, 0) + +/* Common field positions */ +#define RISCV_IOMMU_PPN_FIELD GENMASK_ULL(53, 10) +#define RISCV_IOMMU_QUEUE_LOGSZ_FIELD GENMASK_ULL(4, 0) +#define RISCV_IOMMU_QUEUE_INDEX_FIELD GENMASK_ULL(31, 0) +#define RISCV_IOMMU_QUEUE_ENABLE BIT(0) +#define RISCV_IOMMU_QUEUE_INTR_ENABLE BIT(1) +#define RISCV_IOMMU_QUEUE_MEM_FAULT BIT(8) +#define RISCV_IOMMU_QUEUE_OVERFLOW BIT(9) +#define RISCV_IOMMU_QUEUE_ACTIVE BIT(16) +#define RISCV_IOMMU_QUEUE_BUSY BIT(17) +#define RISCV_IOMMU_ATP_PPN_FIELD GENMASK_ULL(43, 0) +#define RISCV_IOMMU_ATP_MODE_FIELD GENMASK_ULL(63, 60) + +/* 5.3 IOMMU Capabilities (64bits) */ +#define RISCV_IOMMU_REG_CAP 0x0000 +#define RISCV_IOMMU_CAP_VERSION GENMASK_ULL(7, 0) +#define RISCV_IOMMU_CAP_SV32 BIT_ULL(8) +#define RISCV_IOMMU_CAP_SV39 BIT_ULL(9) +#define RISCV_IOMMU_CAP_SV48 BIT_ULL(10) +#define RISCV_IOMMU_CAP_SV57 BIT_ULL(11) +#define RISCV_IOMMU_CAP_SV32X4 BIT_ULL(16) +#define RISCV_IOMMU_CAP_SV39X4 BIT_ULL(17) +#define RISCV_IOMMU_CAP_SV48X4 BIT_ULL(18) +#define RISCV_IOMMU_CAP_SV57X4 BIT_ULL(19) +#define RISCV_IOMMU_CAP_MSI_FLAT BIT_ULL(22) +#define RISCV_IOMMU_CAP_MSI_MRIF BIT_ULL(23) +#define RISCV_IOMMU_CAP_ATS BIT_ULL(25) +#define RISCV_IOMMU_CAP_T2GPA BIT_ULL(26) +#define RISCV_IOMMU_CAP_IGS GENMASK_ULL(29, 28) +#define RISCV_IOMMU_CAP_DBG BIT_ULL(31) +#define RISCV_IOMMU_CAP_PAS GENMASK_ULL(37, 32) +#define RISCV_IOMMU_CAP_PD8 BIT_ULL(38) +#define RISCV_IOMMU_CAP_PD17 BIT_ULL(39) +#define RISCV_IOMMU_CAP_PD20 BIT_ULL(40) + +/* 5.4 Features control register (32bits) */ +#define RISCV_IOMMU_REG_FCTL 0x0008 +#define RISCV_IOMMU_FCTL_BE BIT(0) +#define RISCV_IOMMU_FCTL_WSI BIT(1) +#define RISCV_IOMMU_FCTL_GXL BIT(2) + +/* 5.5 Device-directory-table pointer (64bits) */ +#define RISCV_IOMMU_REG_DDTP 0x0010 +#define RISCV_IOMMU_DDTP_MODE GENMASK_ULL(3, 0) +#define RISCV_IOMMU_DDTP_BUSY BIT_ULL(4) +#define RISCV_IOMMU_DDTP_PPN RISCV_IOMMU_PPN_FIELD + +enum riscv_iommu_ddtp_modes { + RISCV_IOMMU_DDTP_MODE_OFF = 0, + RISCV_IOMMU_DDTP_MODE_BARE = 1, + RISCV_IOMMU_DDTP_MODE_1LVL = 2, + RISCV_IOMMU_DDTP_MODE_2LVL = 3, + RISCV_IOMMU_DDTP_MODE_3LVL = 4, + RISCV_IOMMU_DDTP_MODE_MAX = 4 +}; + +/* 5.6 Command Queue Base (64bits) */ +#define RISCV_IOMMU_REG_CQB 0x0018 +#define RISCV_IOMMU_CQB_LOG2SZ RISCV_IOMMU_QUEUE_LOGSZ_FIELD +#define RISCV_IOMMU_CQB_PPN RISCV_IOMMU_PPN_FIELD + +/* 5.7 Command Queue head (32bits) */ +#define RISCV_IOMMU_REG_CQH 0x0020 + +/* 5.8 Command Queue tail (32bits) */ +#define RISCV_IOMMU_REG_CQT 0x0024 + +/* 5.9 Fault Queue Base (64bits) */ +#define RISCV_IOMMU_REG_FQB 0x0028 +#define RISCV_IOMMU_FQB_LOG2SZ RISCV_IOMMU_QUEUE_LOGSZ_FIELD +#define RISCV_IOMMU_FQB_PPN RISCV_IOMMU_PPN_FIELD + +/* 5.10 Fault Queue Head (32bits) */ +#define RISCV_IOMMU_REG_FQH 0x0030 + +/* 5.11 Fault Queue tail (32bits) */ +#define RISCV_IOMMU_REG_FQT 0x0034 + +/* 5.12 Page Request Queue base (64bits) */ +#define RISCV_IOMMU_REG_PQB 0x0038 +#define RISCV_IOMMU_PQB_LOG2SZ RISCV_IOMMU_QUEUE_LOGSZ_FIELD +#define RISCV_IOMMU_PQB_PPN RISCV_IOMMU_PPN_FIELD + +/* 5.13 Page Request Queue head (32bits) */ +#define RISCV_IOMMU_REG_PQH 0x0040 + +/* 5.14 Page Request Queue tail (32bits) */ +#define RISCV_IOMMU_REG_PQT 0x0044 + +/* 5.15 Command Queue CSR (32bits) */ +#define RISCV_IOMMU_REG_CQCSR 0x0048 +#define RISCV_IOMMU_CQCSR_CQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_CQCSR_CIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_CQCSR_CQMF RISCV_IOMMU_QUEUE_MEM_FAULT +#define RISCV_IOMMU_CQCSR_CMD_TO BIT(9) +#define RISCV_IOMMU_CQCSR_CMD_ILL BIT(10) +#define RISCV_IOMMU_CQCSR_FENCE_W_IP BIT(11) +#define RISCV_IOMMU_CQCSR_CQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_CQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +/* 5.16 Fault Queue CSR (32bits) */ +#define RISCV_IOMMU_REG_FQCSR 0x004C +#define RISCV_IOMMU_FQCSR_FQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_FQCSR_FIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_FQCSR_FQMF RISCV_IOMMU_QUEUE_MEM_FAULT +#define RISCV_IOMMU_FQCSR_FQOF RISCV_IOMMU_QUEUE_OVERFLOW +#define RISCV_IOMMU_FQCSR_FQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_FQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +/* 5.17 Page Request Queue CSR (32bits) */ +#define RISCV_IOMMU_REG_PQCSR 0x0050 +#define RISCV_IOMMU_PQCSR_PQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_PQCSR_PIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_PQCSR_PQMF RISCV_IOMMU_QUEUE_MEM_FAULT +#define RISCV_IOMMU_PQCSR_PQOF RISCV_IOMMU_QUEUE_OVERFLOW +#define RISCV_IOMMU_PQCSR_PQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_PQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +/* 5.18 Interrupt Pending Status (32bits) */ +#define RISCV_IOMMU_REG_IPSR 0x0054 +#define RISCV_IOMMU_IPSR_CIP BIT(0) +#define RISCV_IOMMU_IPSR_FIP BIT(1) +#define RISCV_IOMMU_IPSR_PIP BIT(3) + +enum { + RISCV_IOMMU_INTR_CQ, + RISCV_IOMMU_INTR_FQ, + RISCV_IOMMU_INTR_PM, + RISCV_IOMMU_INTR_PQ, + RISCV_IOMMU_INTR_COUNT +}; + +/* 5.24 Translation request IOVA (64bits) */ +#define RISCV_IOMMU_REG_TR_REQ_IOVA 0x0258 + +/* 5.25 Translation request control (64bits) */ +#define RISCV_IOMMU_REG_TR_REQ_CTL 0x0260 +#define RISCV_IOMMU_TR_REQ_CTL_GO_BUSY BIT_ULL(0) +#define RISCV_IOMMU_TR_REQ_CTL_NW BIT_ULL(3) +#define RISCV_IOMMU_TR_REQ_CTL_PID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_TR_REQ_CTL_DID GENMASK_ULL(63, 40) + +/* 5.26 Translation request response (64bits) */ +#define RISCV_IOMMU_REG_TR_RESPONSE 0x0268 +#define RISCV_IOMMU_TR_RESPONSE_FAULT BIT_ULL(0) +#define RISCV_IOMMU_TR_RESPONSE_S BIT_ULL(9) +#define RISCV_IOMMU_TR_RESPONSE_PPN RISCV_IOMMU_PPN_FIELD + +/* 5.27 Interrupt cause to vector (64bits) */ +#define RISCV_IOMMU_REG_ICVEC 0x02F8 +#define RISCV_IOMMU_ICVEC_CIV GENMASK_ULL(3, 0) +#define RISCV_IOMMU_ICVEC_FIV GENMASK_ULL(7, 4) +#define RISCV_IOMMU_ICVEC_PMIV GENMASK_ULL(11, 8) +#define RISCV_IOMMU_ICVEC_PIV GENMASK_ULL(15, 12) + +/* 5.28 MSI Configuration table (32 * 64bits) */ +#define RISCV_IOMMU_REG_MSI_CONFIG 0x0300 + +#define RISCV_IOMMU_REG_SIZE 0x1000 + +#define RISCV_IOMMU_DDTE_VALID BIT_ULL(0) +#define RISCV_IOMMU_DDTE_PPN RISCV_IOMMU_PPN_FIELD + +/* Struct riscv_iommu_dc - Device Context - section 2.1 */ +struct riscv_iommu_dc { + uint64_t tc; + uint64_t iohgatp; + uint64_t ta; + uint64_t fsc; + uint64_t msiptp; + uint64_t msi_addr_mask; + uint64_t msi_addr_pattern; + uint64_t _reserved; +}; + +/* Translation control fields */ +#define RISCV_IOMMU_DC_TC_V BIT_ULL(0) +#define RISCV_IOMMU_DC_TC_EN_ATS BIT_ULL(1) +#define RISCV_IOMMU_DC_TC_EN_PRI BIT_ULL(2) +#define RISCV_IOMMU_DC_TC_T2GPA BIT_ULL(3) +#define RISCV_IOMMU_DC_TC_DTF BIT_ULL(4) +#define RISCV_IOMMU_DC_TC_PDTV BIT_ULL(5) +#define RISCV_IOMMU_DC_TC_PRPR BIT_ULL(6) +#define RISCV_IOMMU_DC_TC_GADE BIT_ULL(7) +#define RISCV_IOMMU_DC_TC_SADE BIT_ULL(8) +#define RISCV_IOMMU_DC_TC_DPE BIT_ULL(9) +#define RISCV_IOMMU_DC_TC_SBE BIT_ULL(10) +#define RISCV_IOMMU_DC_TC_SXL BIT_ULL(11) + +/* Second-stage (aka G-stage) context fields */ +#define RISCV_IOMMU_DC_IOHGATP_PPN RISCV_IOMMU_ATP_PPN_FIELD +#define RISCV_IOMMU_DC_IOHGATP_GSCID GENMASK_ULL(59, 44) +#define RISCV_IOMMU_DC_IOHGATP_MODE RISCV_IOMMU_ATP_MODE_FIELD + +enum riscv_iommu_dc_iohgatp_modes { + RISCV_IOMMU_DC_IOHGATP_MODE_BARE = 0, + RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4 = 8, + RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4 = 8, + RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4 = 9, + RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4 = 10 +}; + +/* Translation attributes fields */ +#define RISCV_IOMMU_DC_TA_PSCID GENMASK_ULL(31, 12) + +/* First-stage context fields */ +#define RISCV_IOMMU_DC_FSC_PPN RISCV_IOMMU_ATP_PPN_FIELD +#define RISCV_IOMMU_DC_FSC_MODE RISCV_IOMMU_ATP_MODE_FIELD + +/* Generic I/O MMU command structure - check section 3.1 */ +struct riscv_iommu_command { + uint64_t dword0; + uint64_t dword1; +}; + +#define RISCV_IOMMU_CMD_OPCODE GENMASK_ULL(6, 0) +#define RISCV_IOMMU_CMD_FUNC GENMASK_ULL(9, 7) + +#define RISCV_IOMMU_CMD_IOTINVAL_OPCODE 1 +#define RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA 0 +#define RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA 1 +#define RISCV_IOMMU_CMD_IOTINVAL_AV BIT_ULL(10) +#define RISCV_IOMMU_CMD_IOTINVAL_PSCID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_CMD_IOTINVAL_PSCV BIT_ULL(32) +#define RISCV_IOMMU_CMD_IOTINVAL_GV BIT_ULL(33) +#define RISCV_IOMMU_CMD_IOTINVAL_GSCID GENMASK_ULL(59, 44) + +#define RISCV_IOMMU_CMD_IOFENCE_OPCODE 2 +#define RISCV_IOMMU_CMD_IOFENCE_FUNC_C 0 +#define RISCV_IOMMU_CMD_IOFENCE_AV BIT_ULL(10) +#define RISCV_IOMMU_CMD_IOFENCE_DATA GENMASK_ULL(63, 32) + +#define RISCV_IOMMU_CMD_IODIR_OPCODE 3 +#define RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT 0 +#define RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT 1 +#define RISCV_IOMMU_CMD_IODIR_PID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_CMD_IODIR_DV BIT_ULL(33) +#define RISCV_IOMMU_CMD_IODIR_DID GENMASK_ULL(63, 40) + +/* 3.1.4 I/O MMU PCIe ATS */ +#define RISCV_IOMMU_CMD_ATS_OPCODE 4 +#define RISCV_IOMMU_CMD_ATS_FUNC_INVAL 0 +#define RISCV_IOMMU_CMD_ATS_FUNC_PRGR 1 +#define RISCV_IOMMU_CMD_ATS_PID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_CMD_ATS_PV BIT_ULL(32) +#define RISCV_IOMMU_CMD_ATS_DSV BIT_ULL(33) +#define RISCV_IOMMU_CMD_ATS_RID GENMASK_ULL(55, 40) +#define RISCV_IOMMU_CMD_ATS_DSEG GENMASK_ULL(63, 56) +/* dword1 is the ATS payload, two different payload types for INVAL and PRGR */ + +/* ATS.PRGR payload */ +#define RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE GENMASK_ULL(47, 44) + +enum riscv_iommu_dc_fsc_atp_modes { + RISCV_IOMMU_DC_FSC_MODE_BARE = 0, + RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 = 8, + RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39 = 8, + RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48 = 9, + RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57 = 10, + RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8 = 1, + RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17 = 2, + RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20 = 3 +}; + +enum riscv_iommu_fq_causes { + RISCV_IOMMU_FQ_CAUSE_INST_FAULT = 1, + RISCV_IOMMU_FQ_CAUSE_RD_ADDR_MISALIGNED = 4, + RISCV_IOMMU_FQ_CAUSE_RD_FAULT = 5, + RISCV_IOMMU_FQ_CAUSE_WR_ADDR_MISALIGNED = 6, + RISCV_IOMMU_FQ_CAUSE_WR_FAULT = 7, + RISCV_IOMMU_FQ_CAUSE_INST_FAULT_S = 12, + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S = 13, + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S = 15, + RISCV_IOMMU_FQ_CAUSE_INST_FAULT_VS = 20, + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS = 21, + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS = 23, + RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED = 256, + RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT = 257, + RISCV_IOMMU_FQ_CAUSE_DDT_INVALID = 258, + RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED = 259, + RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED = 260, + RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT = 261, + RISCV_IOMMU_FQ_CAUSE_MSI_INVALID = 262, + RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED = 263, + RISCV_IOMMU_FQ_CAUSE_MRIF_FAULT = 264, + RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT = 265, + RISCV_IOMMU_FQ_CAUSE_PDT_INVALID = 266, + RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED = 267, + RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED = 268, + RISCV_IOMMU_FQ_CAUSE_PDT_CORRUPTED = 269, + RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED = 270, + RISCV_IOMMU_FQ_CAUSE_MRIF_CORRUIPTED = 271, + RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR = 272, + RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT = 273, + RISCV_IOMMU_FQ_CAUSE_PT_CORRUPTED = 274 +}; + +/* MSI page table pointer */ +#define RISCV_IOMMU_DC_MSIPTP_PPN RISCV_IOMMU_ATP_PPN_FIELD +#define RISCV_IOMMU_DC_MSIPTP_MODE RISCV_IOMMU_ATP_MODE_FIELD +#define RISCV_IOMMU_DC_MSIPTP_MODE_OFF 0 +#define RISCV_IOMMU_DC_MSIPTP_MODE_FLAT 1 + +/* Translation attributes fields */ +#define RISCV_IOMMU_PC_TA_V BIT_ULL(0) +#define RISCV_IOMMU_PC_TA_RESERVED GENMASK_ULL(63, 32) + +/* First stage context fields */ +#define RISCV_IOMMU_PC_FSC_PPN GENMASK_ULL(43, 0) +#define RISCV_IOMMU_PC_FSC_RESERVED GENMASK_ULL(59, 44) + +enum riscv_iommu_fq_ttypes { + RISCV_IOMMU_FQ_TTYPE_NONE = 0, + RISCV_IOMMU_FQ_TTYPE_UADDR_INST_FETCH = 1, + RISCV_IOMMU_FQ_TTYPE_UADDR_RD = 2, + RISCV_IOMMU_FQ_TTYPE_UADDR_WR = 3, + RISCV_IOMMU_FQ_TTYPE_TADDR_INST_FETCH = 5, + RISCV_IOMMU_FQ_TTYPE_TADDR_RD = 6, + RISCV_IOMMU_FQ_TTYPE_TADDR_WR = 7, + RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ = 8, + RISCV_IOMMU_FW_TTYPE_PCIE_MSG_REQ = 9, +}; + +/* Header fields */ +#define RISCV_IOMMU_PREQ_HDR_PID GENMASK_ULL(31, 12) +#define RISCV_IOMMU_PREQ_HDR_PV BIT_ULL(32) +#define RISCV_IOMMU_PREQ_HDR_PRIV BIT_ULL(33) +#define RISCV_IOMMU_PREQ_HDR_EXEC BIT_ULL(34) +#define RISCV_IOMMU_PREQ_HDR_DID GENMASK_ULL(63, 40) + +/* Payload fields */ +#define RISCV_IOMMU_PREQ_PAYLOAD_R BIT_ULL(0) +#define RISCV_IOMMU_PREQ_PAYLOAD_W BIT_ULL(1) +#define RISCV_IOMMU_PREQ_PAYLOAD_L BIT_ULL(2) +#define RISCV_IOMMU_PREQ_PAYLOAD_M GENMASK_ULL(2, 0) +#define RISCV_IOMMU_PREQ_PRG_INDEX GENMASK_ULL(11, 3) +#define RISCV_IOMMU_PREQ_UADDR GENMASK_ULL(63, 12) + + +/* + * struct riscv_iommu_msi_pte - MSI Page Table Entry + */ +struct riscv_iommu_msi_pte { + uint64_t pte; + uint64_t mrif_info; +}; + +/* Fields on pte */ +#define RISCV_IOMMU_MSI_PTE_V BIT_ULL(0) +#define RISCV_IOMMU_MSI_PTE_M GENMASK_ULL(2, 1) + +#define RISCV_IOMMU_MSI_PTE_M_MRIF 1 +#define RISCV_IOMMU_MSI_PTE_M_BASIC 3 + +/* When M == 1 (MRIF mode) */ +#define RISCV_IOMMU_MSI_PTE_MRIF_ADDR GENMASK_ULL(53, 7) +/* When M == 3 (basic mode) */ +#define RISCV_IOMMU_MSI_PTE_PPN RISCV_IOMMU_PPN_FIELD +#define RISCV_IOMMU_MSI_PTE_C BIT_ULL(63) + +/* Fields on mrif_info */ +#define RISCV_IOMMU_MSI_MRIF_NID GENMASK_ULL(9, 0) +#define RISCV_IOMMU_MSI_MRIF_NPPN RISCV_IOMMU_PPN_FIELD +#define RISCV_IOMMU_MSI_MRIF_NID_MSB BIT_ULL(60) + +#endif /* _RISCV_IOMMU_BITS_H_ */ diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c new file mode 100644 index 0000000000..a42242532d --- /dev/null +++ b/hw/riscv/riscv-iommu-pci.c @@ -0,0 +1,202 @@ +/* + * QEMU emulation of an RISC-V IOMMU + * + * Copyright (C) 2022-2023 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 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/pci/msi.h" +#include "hw/pci/msix.h" +#include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/riscv_hart.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/host-utils.h" +#include "qom/object.h" + +#include "cpu_bits.h" +#include "riscv-iommu.h" +#include "riscv-iommu-bits.h" + +/* RISC-V IOMMU PCI Device Emulation */ +#define RISCV_PCI_CLASS_SYSTEM_IOMMU 0x0806 + +/* + * 4 MSIx vectors for ICVEC, one for MRIF. The spec mentions in + * the "Placement and data flow" section that: + * + * "The interfaces related to recording an incoming MSI in a memory-resident + * interrupt file (MRIF) are implementation-specific. The partitioning of + * responsibility between the IOMMU and the IO bridge for recording the + * incoming MSI in an MRIF and generating the associated notice MSI are + * implementation-specific." + * + * We're making a design decision to create the MSIx for MRIF in the + * IOMMU MSIx emulation. + */ +#define RISCV_IOMMU_PCI_MSIX_VECTORS 5 + +/* + * 4 vectors that can be used by civ, fiv, pmiv and piv. Number of + * vectors is represented by 2^N, where N = number of writable bits + * in each cause. For 4 vectors we'll write 0b11 (3) in each reg. + */ +#define RISCV_IOMMU_PCI_ICVEC_VECTORS 0x3333 + +typedef struct RISCVIOMMUStatePci { + PCIDevice pci; /* Parent PCIe device state */ + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + MemoryRegion bar0; /* PCI BAR (including MSI-x config) */ + RISCVIOMMUState iommu; /* common IOMMU state */ +} RISCVIOMMUStatePci; + +/* interrupt delivery callback */ +static void riscv_iommu_pci_notify(RISCVIOMMUState *iommu, unsigned vector) +{ + RISCVIOMMUStatePci *s = container_of(iommu, RISCVIOMMUStatePci, iommu); + + if (msix_enabled(&(s->pci))) { + msix_notify(&(s->pci), vector); + } +} + +static void riscv_iommu_pci_realize(PCIDevice *dev, Error **errp) +{ + RISCVIOMMUStatePci *s = DO_UPCAST(RISCVIOMMUStatePci, pci, dev); + RISCVIOMMUState *iommu = &s->iommu; + uint8_t *pci_conf = dev->config; + Error *err = NULL; + + pci_set_word(pci_conf + PCI_VENDOR_ID, s->vendor_id); + pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, s->vendor_id); + pci_set_word(pci_conf + PCI_DEVICE_ID, s->device_id); + pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, s->device_id); + pci_set_byte(pci_conf + PCI_REVISION_ID, s->revision); + + /* Set device id for trace / debug */ + DEVICE(iommu)->id = g_strdup_printf("%02x:%02x.%01x", + pci_dev_bus_num(dev), PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + qdev_realize(DEVICE(iommu), NULL, errp); + + memory_region_init(&s->bar0, OBJECT(s), "riscv-iommu-bar0", + QEMU_ALIGN_UP(memory_region_size(&iommu->regs_mr), TARGET_PAGE_SIZE)); + memory_region_add_subregion(&s->bar0, 0, &iommu->regs_mr); + + pcie_endpoint_cap_init(dev, 0); + + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64, &s->bar0); + + int ret = msix_init(dev, RISCV_IOMMU_PCI_MSIX_VECTORS, + &s->bar0, 0, RISCV_IOMMU_REG_MSI_CONFIG, + &s->bar0, 0, RISCV_IOMMU_REG_MSI_CONFIG + 256, 0, &err); + + if (ret == -ENOTSUP) { + /* + * MSI-x is not supported by the platform. + * Driver should use timer/polling based notification handlers. + */ + warn_report_err(err); + } else if (ret < 0) { + error_propagate(errp, err); + return; + } else { + /* Mark all ICVEC MSIx vectors as used */ + for (int i = 0; i < RISCV_IOMMU_PCI_MSIX_VECTORS; i++) { + msix_vector_use(dev, i); + } + + iommu->notify = riscv_iommu_pci_notify; + } + + PCIBus *bus = pci_device_root_bus(dev); + if (!bus) { + error_setg(errp, "can't find PCIe root port for %02x:%02x.%x", + pci_bus_num(pci_get_bus(dev)), PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + return; + } + + riscv_iommu_pci_setup_iommu(iommu, bus, errp); +} + +static void riscv_iommu_pci_exit(PCIDevice *pci_dev) +{ + pci_setup_iommu(pci_device_root_bus(pci_dev), NULL, NULL); +} + +static const VMStateDescription riscv_iommu_vmstate = { + .name = "riscv-iommu", + .unmigratable = 1 +}; + +static void riscv_iommu_pci_init(Object *obj) +{ + RISCVIOMMUStatePci *s = RISCV_IOMMU_PCI(obj); + RISCVIOMMUState *iommu = &s->iommu; + + object_initialize_child(obj, "iommu", iommu, TYPE_RISCV_IOMMU); + qdev_alias_all_properties(DEVICE(iommu), obj); + + iommu->icvec_avail_vectors = RISCV_IOMMU_PCI_ICVEC_VECTORS; +} + +static Property riscv_iommu_pci_properties[] = { + DEFINE_PROP_UINT16("vendor-id", RISCVIOMMUStatePci, vendor_id, + PCI_VENDOR_ID_REDHAT), + DEFINE_PROP_UINT16("device-id", RISCVIOMMUStatePci, device_id, + PCI_DEVICE_ID_REDHAT_RISCV_IOMMU), + DEFINE_PROP_UINT8("revision", RISCVIOMMUStatePci, revision, 0x01), + DEFINE_PROP_END_OF_LIST(), +}; + +static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = riscv_iommu_pci_realize; + k->exit = riscv_iommu_pci_exit; + k->class_id = RISCV_PCI_CLASS_SYSTEM_IOMMU; + dc->desc = "RISCV-IOMMU DMA Remapping device"; + dc->vmsd = &riscv_iommu_vmstate; + dc->hotpluggable = false; + dc->user_creatable = true; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + device_class_set_props(dc, riscv_iommu_pci_properties); +} + +static const TypeInfo riscv_iommu_pci = { + .name = TYPE_RISCV_IOMMU_PCI, + .parent = TYPE_PCI_DEVICE, + .class_init = riscv_iommu_pci_class_init, + .instance_init = riscv_iommu_pci_init, + .instance_size = sizeof(RISCVIOMMUStatePci), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { }, + }, +}; + +static void riscv_iommu_register_pci_types(void) +{ + type_register_static(&riscv_iommu_pci); +} + +type_init(riscv_iommu_register_pci_types); diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c new file mode 100644 index 0000000000..bbc95425b3 --- /dev/null +++ b/hw/riscv/riscv-iommu.c @@ -0,0 +1,2416 @@ +/* + * QEMU emulation of an RISC-V IOMMU + * + * Copyright (C) 2021-2023, 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 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 "qom/object.h" +#include "hw/pci/pci_bus.h" +#include "hw/pci/pci_device.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/riscv_hart.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/timer.h" + +#include "cpu_bits.h" +#include "riscv-iommu.h" +#include "riscv-iommu-bits.h" +#include "trace.h" + +#define LIMIT_CACHE_CTX (1U << 7) +#define LIMIT_CACHE_IOT (1U << 20) + +/* Physical page number coversions */ +#define PPN_PHYS(ppn) ((ppn) << TARGET_PAGE_BITS) +#define PPN_DOWN(phy) ((phy) >> TARGET_PAGE_BITS) + +typedef struct RISCVIOMMUContext RISCVIOMMUContext; +typedef struct RISCVIOMMUEntry RISCVIOMMUEntry; + +/* Device assigned I/O address space */ +struct RISCVIOMMUSpace { + IOMMUMemoryRegion iova_mr; /* IOVA memory region for attached device */ + AddressSpace iova_as; /* IOVA address space for attached device */ + RISCVIOMMUState *iommu; /* Managing IOMMU device state */ + uint32_t devid; /* Requester identifier, AKA device_id */ + bool notifier; /* IOMMU unmap notifier enabled */ + QLIST_ENTRY(RISCVIOMMUSpace) list; +}; + +/* Device translation context state. */ +struct RISCVIOMMUContext { + uint64_t devid:24; /* Requester Id, AKA device_id */ + uint64_t process_id:20; /* Process ID. PASID for PCIe */ + uint64_t tc; /* Translation Control */ + uint64_t ta; /* Translation Attributes */ + uint64_t satp; /* S-Stage address translation and protection */ + uint64_t gatp; /* G-Stage address translation and protection */ + uint64_t msi_addr_mask; /* MSI filtering - address mask */ + uint64_t msi_addr_pattern; /* MSI filtering - address pattern */ + uint64_t msiptp; /* MSI redirection page table pointer */ +}; + +/* Address translation cache entry */ +struct RISCVIOMMUEntry { + uint64_t iova:44; /* IOVA Page Number */ + uint64_t pscid:20; /* Process Soft-Context identifier */ + uint64_t phys:44; /* Physical Page Number */ + uint64_t gscid:16; /* Guest Soft-Context identifier */ + uint64_t perm:2; /* IOMMU_RW flags */ +}; + +/* IOMMU index for transactions without process_id specified. */ +#define RISCV_IOMMU_NOPROCID 0 + +static uint8_t riscv_iommu_get_icvec_vector(uint32_t icvec, uint32_t vec_type) +{ + switch (vec_type) { + case RISCV_IOMMU_INTR_CQ: + return icvec & RISCV_IOMMU_ICVEC_CIV; + case RISCV_IOMMU_INTR_FQ: + return (icvec & RISCV_IOMMU_ICVEC_FIV) >> 4; + case RISCV_IOMMU_INTR_PM: + return (icvec & RISCV_IOMMU_ICVEC_PMIV) >> 8; + case RISCV_IOMMU_INTR_PQ: + return (icvec & RISCV_IOMMU_ICVEC_PIV) >> 12; + default: + g_assert_not_reached(); + } +} + +static void riscv_iommu_notify(RISCVIOMMUState *s, int vec_type) +{ + const uint32_t fctl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FCTL); + uint32_t ipsr, icvec, vector; + + if (fctl & RISCV_IOMMU_FCTL_WSI || !s->notify) { + return; + } + + icvec = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_ICVEC); + ipsr = riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, (1 << vec_type), 0); + + if (!(ipsr & (1 << vec_type))) { + vector = riscv_iommu_get_icvec_vector(icvec, vec_type); + s->notify(s, vector); + trace_riscv_iommu_notify_int_vector(vec_type, vector); + } +} + +static void riscv_iommu_fault(RISCVIOMMUState *s, + struct riscv_iommu_fq_record *ev) +{ + uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); + uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQH) & s->fq_mask; + uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQT) & s->fq_mask; + uint32_t next = (tail + 1) & s->fq_mask; + uint32_t devid = get_field(ev->hdr, RISCV_IOMMU_FQ_HDR_DID); + + trace_riscv_iommu_flt(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid), + PCI_FUNC(devid), ev->hdr, ev->iotval); + + if (!(ctrl & RISCV_IOMMU_FQCSR_FQON) || + !!(ctrl & (RISCV_IOMMU_FQCSR_FQOF | RISCV_IOMMU_FQCSR_FQMF))) { + return; + } + + if (head == next) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, + RISCV_IOMMU_FQCSR_FQOF, 0); + } else { + dma_addr_t addr = s->fq_addr + tail * sizeof(*ev); + if (dma_memory_write(s->target_as, addr, ev, sizeof(*ev), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, + RISCV_IOMMU_FQCSR_FQMF, 0); + } else { + riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_FQT, next); + } + } + + if (ctrl & RISCV_IOMMU_FQCSR_FIE) { + riscv_iommu_notify(s, RISCV_IOMMU_INTR_FQ); + } +} + +static void riscv_iommu_pri(RISCVIOMMUState *s, + struct riscv_iommu_pq_record *pr) +{ + uint32_t ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR); + uint32_t head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQH) & s->pq_mask; + uint32_t tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQT) & s->pq_mask; + uint32_t next = (tail + 1) & s->pq_mask; + uint32_t devid = get_field(pr->hdr, RISCV_IOMMU_PREQ_HDR_DID); + + trace_riscv_iommu_pri(s->parent_obj.id, PCI_BUS_NUM(devid), PCI_SLOT(devid), + PCI_FUNC(devid), pr->payload); + + if (!(ctrl & RISCV_IOMMU_PQCSR_PQON) || + !!(ctrl & (RISCV_IOMMU_PQCSR_PQOF | RISCV_IOMMU_PQCSR_PQMF))) { + return; + } + + if (head == next) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, + RISCV_IOMMU_PQCSR_PQOF, 0); + } else { + dma_addr_t addr = s->pq_addr + tail * sizeof(*pr); + if (dma_memory_write(s->target_as, addr, pr, sizeof(*pr), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, + RISCV_IOMMU_PQCSR_PQMF, 0); + } else { + riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_PQT, next); + } + } + + if (ctrl & RISCV_IOMMU_PQCSR_PIE) { + riscv_iommu_notify(s, RISCV_IOMMU_INTR_PQ); + } +} + +/* + * Discards all bits from 'val' whose matching bits in the same + * positions in the mask 'ext' are zeros, and packs the remaining + * bits from 'val' contiguously at the least-significant end of the + * result, keeping the same bit order as 'val' and filling any + * other bits at the most-significant end of the result with zeros. + * + * For example, for the following 'val' and 'ext', the return 'ret' + * will be: + * + * val = a b c d e f g h + * ext = 1 0 1 0 0 1 1 0 + * ret = 0 0 0 0 a c f g + * + * This function, taken from the riscv-iommu 1.0 spec, section 2.3.3 + * "Process to translate addresses of MSIs", is similar to bit manip + * function PEXT (Parallel bits extract) from x86. + */ +static uint64_t riscv_iommu_pext_u64(uint64_t val, uint64_t ext) +{ + uint64_t ret = 0; + uint64_t rot = 1; + + while (ext) { + if (ext & 1) { + if (val & 1) { + ret |= rot; + } + rot <<= 1; + } + val >>= 1; + ext >>= 1; + } + + return ret; +} + +/* Check if GPA matches MSI/MRIF pattern. */ +static bool riscv_iommu_msi_check(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + dma_addr_t gpa) +{ + if (!s->enable_msi) { + return false; + } + + if (get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE) != + RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) { + return false; /* Invalid MSI/MRIF mode */ + } + + if ((PPN_DOWN(gpa) ^ ctx->msi_addr_pattern) & ~ctx->msi_addr_mask) { + return false; /* GPA not in MSI range defined by AIA IMSIC rules. */ + } + + return true; +} + +/* + * RISCV IOMMU Address Translation Lookup - Page Table Walk + * + * Note: Code is based on get_physical_address() from target/riscv/cpu_helper.c + * Both implementation can be merged into single helper function in future. + * Keeping them separate for now, as error reporting and flow specifics are + * sufficiently different for separate implementation. + * + * @s : IOMMU Device State + * @ctx : Translation context for device id and process address space id. + * @iotlb : translation data: physical address and access mode. + * @return : success or fault cause code. + */ +static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + IOMMUTLBEntry *iotlb) +{ + dma_addr_t addr, base; + uint64_t satp, gatp, pte; + bool en_s, en_g; + struct { + unsigned char step; + unsigned char levels; + unsigned char ptidxbits; + unsigned char ptesize; + } sc[2]; + /* Translation stage phase */ + enum { + S_STAGE = 0, + G_STAGE = 1, + } pass; + MemTxResult ret; + + satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); + gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + + en_s = satp != RISCV_IOMMU_DC_FSC_MODE_BARE; + en_g = gatp != RISCV_IOMMU_DC_IOHGATP_MODE_BARE; + + /* + * Early check for MSI address match when IOVA == GPA. + * Note that the (!en_s) condition means that the MSI + * page table may only be used when guest pages are + * mapped using the g-stage page table, whether single- + * or two-stage paging is enabled. It's unavoidable though, + * because the spec mandates that we do a first-stage + * translation before we check the MSI page table, which + * means we can't do an early MSI check unless we have + * strictly !en_s. + */ + if (!en_s && (iotlb->perm & IOMMU_WO) && + riscv_iommu_msi_check(s, ctx, iotlb->iova)) { + iotlb->target_as = &s->trap_as; + iotlb->translated_addr = iotlb->iova; + iotlb->addr_mask = ~TARGET_PAGE_MASK; + return 0; + } + + /* Exit early for pass-through mode. */ + if (!(en_s || en_g)) { + iotlb->translated_addr = iotlb->iova; + iotlb->addr_mask = ~TARGET_PAGE_MASK; + /* Allow R/W in pass-through mode */ + iotlb->perm = IOMMU_RW; + return 0; + } + + /* S/G translation parameters. */ + for (pass = 0; pass < 2; pass++) { + uint32_t sv_mode; + + sc[pass].step = 0; + if (pass ? (s->fctl & RISCV_IOMMU_FCTL_GXL) : + (ctx->tc & RISCV_IOMMU_DC_TC_SXL)) { + /* 32bit mode for GXL/SXL == 1 */ + switch (pass ? gatp : satp) { + case RISCV_IOMMU_DC_IOHGATP_MODE_BARE: + sc[pass].levels = 0; + sc[pass].ptidxbits = 0; + sc[pass].ptesize = 0; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV32X4: + sv_mode = pass ? RISCV_IOMMU_CAP_SV32X4 : RISCV_IOMMU_CAP_SV32; + if (!(s->cap & sv_mode)) { + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + sc[pass].levels = 2; + sc[pass].ptidxbits = 10; + sc[pass].ptesize = 4; + break; + default: + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + } else { + /* 64bit mode for GXL/SXL == 0 */ + switch (pass ? gatp : satp) { + case RISCV_IOMMU_DC_IOHGATP_MODE_BARE: + sc[pass].levels = 0; + sc[pass].ptidxbits = 0; + sc[pass].ptesize = 0; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4: + sv_mode = pass ? RISCV_IOMMU_CAP_SV39X4 : RISCV_IOMMU_CAP_SV39; + if (!(s->cap & sv_mode)) { + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + sc[pass].levels = 3; + sc[pass].ptidxbits = 9; + sc[pass].ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4: + sv_mode = pass ? RISCV_IOMMU_CAP_SV48X4 : RISCV_IOMMU_CAP_SV48; + if (!(s->cap & sv_mode)) { + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + sc[pass].levels = 4; + sc[pass].ptidxbits = 9; + sc[pass].ptesize = 8; + break; + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4: + sv_mode = pass ? RISCV_IOMMU_CAP_SV57X4 : RISCV_IOMMU_CAP_SV57; + if (!(s->cap & sv_mode)) { + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + sc[pass].levels = 5; + sc[pass].ptidxbits = 9; + sc[pass].ptesize = 8; + break; + default: + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + } + }; + + /* S/G stages translation tables root pointers */ + gatp = PPN_PHYS(get_field(ctx->gatp, RISCV_IOMMU_ATP_PPN_FIELD)); + satp = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_ATP_PPN_FIELD)); + addr = (en_s && en_g) ? satp : iotlb->iova; + base = en_g ? gatp : satp; + pass = en_g ? G_STAGE : S_STAGE; + + do { + const unsigned widened = (pass && !sc[pass].step) ? 2 : 0; + const unsigned va_bits = widened + sc[pass].ptidxbits; + const unsigned va_skip = TARGET_PAGE_BITS + sc[pass].ptidxbits * + (sc[pass].levels - 1 - sc[pass].step); + const unsigned idx = (addr >> va_skip) & ((1 << va_bits) - 1); + const dma_addr_t pte_addr = base + idx * sc[pass].ptesize; + const bool ade = + ctx->tc & (pass ? RISCV_IOMMU_DC_TC_GADE : RISCV_IOMMU_DC_TC_SADE); + + /* Address range check before first level lookup */ + if (!sc[pass].step) { + const uint64_t va_mask = (1ULL << (va_skip + va_bits)) - 1; + if ((addr & va_mask) != addr) { + return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED; + } + } + + /* Read page table entry */ + if (sc[pass].ptesize == 4) { + uint32_t pte32 = 0; + ret = ldl_le_dma(s->target_as, pte_addr, &pte32, + MEMTXATTRS_UNSPECIFIED); + pte = pte32; + } else { + ret = ldq_le_dma(s->target_as, pte_addr, &pte, + MEMTXATTRS_UNSPECIFIED); + } + if (ret != MEMTX_OK) { + return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT + : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; + } + + sc[pass].step++; + hwaddr ppn = pte >> PTE_PPN_SHIFT; + + if (!(pte & PTE_V)) { + break; /* Invalid PTE */ + } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { + base = PPN_PHYS(ppn); /* Inner PTE, continue walking */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { + break; /* Reserved leaf PTE flags: PTE_W */ + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { + break; /* Reserved leaf PTE flags: PTE_W + PTE_X */ + } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { + break; /* Misaligned PPN */ + } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { + break; /* Read access check failed */ + } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { + break; /* Write access check failed */ + } else if ((iotlb->perm & IOMMU_RO) && !ade && !(pte & PTE_A)) { + break; /* Access bit not set */ + } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { + break; /* Dirty bit not set */ + } else { + /* Leaf PTE, translation completed. */ + sc[pass].step = sc[pass].levels; + base = PPN_PHYS(ppn) | (addr & ((1ULL << va_skip) - 1)); + /* Update address mask based on smallest translation granularity */ + iotlb->addr_mask &= (1ULL << va_skip) - 1; + /* Continue with S-Stage translation? */ + if (pass && sc[0].step != sc[0].levels) { + pass = S_STAGE; + addr = iotlb->iova; + continue; + } + /* Translation phase completed (GPA or SPA) */ + iotlb->translated_addr = base; + iotlb->perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) + : IOMMU_RO; + + /* Check MSI GPA address match */ + if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) && + riscv_iommu_msi_check(s, ctx, base)) { + /* Trap MSI writes and return GPA address. */ + iotlb->target_as = &s->trap_as; + iotlb->addr_mask = ~TARGET_PAGE_MASK; + return 0; + } + + /* Continue with G-Stage translation? */ + if (!pass && en_g) { + pass = G_STAGE; + addr = base; + base = gatp; + sc[pass].step = 0; + continue; + } + + return 0; + } + + if (sc[pass].step == sc[pass].levels) { + break; /* Can't find leaf PTE */ + } + + /* Continue with G-Stage translation? */ + if (!pass && en_g) { + pass = G_STAGE; + addr = base; + base = gatp; + sc[pass].step = 0; + } + } while (1); + + return (iotlb->perm & IOMMU_WO) ? + (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : + RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) : + (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS : + RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S); +} + +static void riscv_iommu_report_fault(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx, + uint32_t fault_type, uint32_t cause, + bool pv, + uint64_t iotval, uint64_t iotval2) +{ + struct riscv_iommu_fq_record ev = { 0 }; + + if (ctx->tc & RISCV_IOMMU_DC_TC_DTF) { + switch (cause) { + case RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED: + case RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT: + case RISCV_IOMMU_FQ_CAUSE_DDT_INVALID: + case RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED: + case RISCV_IOMMU_FQ_CAUSE_DDT_CORRUPTED: + case RISCV_IOMMU_FQ_CAUSE_INTERNAL_DP_ERROR: + case RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT: + break; + default: + /* DTF prevents reporting a fault for this given cause */ + return; + } + } + + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_CAUSE, cause); + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_TTYPE, fault_type); + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_DID, ctx->devid); + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PV, true); + + if (pv) { + ev.hdr = set_field(ev.hdr, RISCV_IOMMU_FQ_HDR_PID, ctx->process_id); + } + + ev.iotval = iotval; + ev.iotval2 = iotval2; + + riscv_iommu_fault(s, &ev); +} + +/* Redirect MSI write for given GPA. */ +static MemTxResult riscv_iommu_msi_write(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx, uint64_t gpa, uint64_t data, + unsigned size, MemTxAttrs attrs) +{ + MemTxResult res; + dma_addr_t addr; + uint64_t intn; + uint32_t n190; + uint64_t pte[2]; + int fault_type = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; + int cause; + + /* Interrupt File Number */ + intn = riscv_iommu_pext_u64(PPN_DOWN(gpa), ctx->msi_addr_mask); + if (intn >= 256) { + /* Interrupt file number out of range */ + res = MEMTX_ACCESS_ERROR; + cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; + goto err; + } + + /* fetch MSI PTE */ + addr = PPN_PHYS(get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_PPN)); + addr = addr | (intn * sizeof(pte)); + res = dma_memory_read(s->target_as, addr, &pte, sizeof(pte), + MEMTXATTRS_UNSPECIFIED); + if (res != MEMTX_OK) { + if (res == MEMTX_DECODE_ERROR) { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_PT_CORRUPTED; + } else { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; + } + goto err; + } + + le64_to_cpus(&pte[0]); + le64_to_cpus(&pte[1]); + + if (!(pte[0] & RISCV_IOMMU_MSI_PTE_V) || (pte[0] & RISCV_IOMMU_MSI_PTE_C)) { + /* + * The spec mentions that: "If msipte.C == 1, then further + * processing to interpret the PTE is implementation + * defined.". We'll abort with cause = 262 for this + * case too. + */ + res = MEMTX_ACCESS_ERROR; + cause = RISCV_IOMMU_FQ_CAUSE_MSI_INVALID; + goto err; + } + + switch (get_field(pte[0], RISCV_IOMMU_MSI_PTE_M)) { + case RISCV_IOMMU_MSI_PTE_M_BASIC: + /* MSI Pass-through mode */ + addr = PPN_PHYS(get_field(pte[0], RISCV_IOMMU_MSI_PTE_PPN)); + + trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid), + PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid), + gpa, addr); + + res = dma_memory_write(s->target_as, addr, &data, size, attrs); + if (res != MEMTX_OK) { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT; + goto err; + } + + return MEMTX_OK; + case RISCV_IOMMU_MSI_PTE_M_MRIF: + /* MRIF mode, continue. */ + break; + default: + res = MEMTX_ACCESS_ERROR; + cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED; + goto err; + } + + /* + * Report an error for interrupt identities exceeding the maximum allowed + * for an IMSIC interrupt file (2047) or destination address is not 32-bit + * aligned. See IOMMU Specification, Chapter 2.3. MSI page tables. + */ + if ((data > 2047) || (gpa & 3)) { + res = MEMTX_ACCESS_ERROR; + cause = RISCV_IOMMU_FQ_CAUSE_MSI_MISCONFIGURED; + goto err; + } + + /* MSI MRIF mode, non atomic pending bit update */ + + /* MRIF pending bit address */ + addr = get_field(pte[0], RISCV_IOMMU_MSI_PTE_MRIF_ADDR) << 9; + addr = addr | ((data & 0x7c0) >> 3); + + trace_riscv_iommu_msi(s->parent_obj.id, PCI_BUS_NUM(ctx->devid), + PCI_SLOT(ctx->devid), PCI_FUNC(ctx->devid), + gpa, addr); + + /* MRIF pending bit mask */ + data = 1ULL << (data & 0x03f); + res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs); + if (res != MEMTX_OK) { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; + goto err; + } + + intn = intn | data; + res = dma_memory_write(s->target_as, addr, &intn, sizeof(intn), attrs); + if (res != MEMTX_OK) { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT; + goto err; + } + + /* Get MRIF enable bits */ + addr = addr + sizeof(intn); + res = dma_memory_read(s->target_as, addr, &intn, sizeof(intn), attrs); + if (res != MEMTX_OK) { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_LOAD_FAULT; + goto err; + } + + if (!(intn & data)) { + /* notification disabled, MRIF update completed. */ + return MEMTX_OK; + } + + /* Send notification message */ + addr = PPN_PHYS(get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NPPN)); + n190 = get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID) | + (get_field(pte[1], RISCV_IOMMU_MSI_MRIF_NID_MSB) << 10); + + res = dma_memory_write(s->target_as, addr, &n190, sizeof(n190), attrs); + if (res != MEMTX_OK) { + cause = RISCV_IOMMU_FQ_CAUSE_MSI_WR_FAULT; + goto err; + } + + trace_riscv_iommu_mrif_notification(s->parent_obj.id, n190, addr); + + return MEMTX_OK; + +err: + riscv_iommu_report_fault(s, ctx, fault_type, cause, + !!ctx->process_id, 0, 0); + return res; +} + +/* + * Check device context configuration as described by the + * riscv-iommu spec section "Device-context configuration + * checks". + */ +static bool riscv_iommu_validate_device_ctx(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx) +{ + uint32_t fsc_mode, msi_mode; + uint64_t gatp; + + if (!(s->cap & RISCV_IOMMU_CAP_ATS) && + (ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS || + ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI || + ctx->tc & RISCV_IOMMU_DC_TC_PRPR)) { + return false; + } + + if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS) && + (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA || + ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI)) { + return false; + } + + if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI) && + ctx->tc & RISCV_IOMMU_DC_TC_PRPR) { + return false; + } + + if (!(s->cap & RISCV_IOMMU_CAP_T2GPA) && + ctx->tc & RISCV_IOMMU_DC_TC_T2GPA) { + return false; + } + + if (s->cap & RISCV_IOMMU_CAP_MSI_FLAT) { + msi_mode = get_field(ctx->msiptp, RISCV_IOMMU_DC_MSIPTP_MODE); + + if (msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_OFF && + msi_mode != RISCV_IOMMU_DC_MSIPTP_MODE_FLAT) { + return false; + } + } + + gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); + if (ctx->tc & RISCV_IOMMU_DC_TC_T2GPA && + gatp == RISCV_IOMMU_DC_IOHGATP_MODE_BARE) { + return false; + } + + fsc_mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); + + if (ctx->tc & RISCV_IOMMU_DC_TC_PDTV) { + switch (fsc_mode) { + case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8: + if (!(s->cap & RISCV_IOMMU_CAP_PD8)) { + return false; + } + break; + case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD17: + if (!(s->cap & RISCV_IOMMU_CAP_PD17)) { + return false; + } + break; + case RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20: + if (!(s->cap & RISCV_IOMMU_CAP_PD20)) { + return false; + } + break; + } + } else { + /* DC.tc.PDTV is 0 */ + if (ctx->tc & RISCV_IOMMU_DC_TC_DPE) { + return false; + } + + if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) { + if (fsc_mode == RISCV_IOMMU_CAP_SV32 && + !(s->cap & RISCV_IOMMU_CAP_SV32)) { + return false; + } + } else { + switch (fsc_mode) { + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39: + if (!(s->cap & RISCV_IOMMU_CAP_SV39)) { + return false; + } + break; + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48: + if (!(s->cap & RISCV_IOMMU_CAP_SV48)) { + return false; + } + break; + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57: + if (!(s->cap & RISCV_IOMMU_CAP_SV57)) { + return false; + } + break; + } + } + } + + /* + * CAP_END is always zero (only one endianess). FCTL_BE is + * always zero (little-endian accesses). Thus TC_SBE must + * always be LE, i.e. zero. + */ + if (ctx->tc & RISCV_IOMMU_DC_TC_SBE) { + return false; + } + + return true; +} + +/* + * Validate process context (PC) according to section + * "Process-context configuration checks". + */ +static bool riscv_iommu_validate_process_ctx(RISCVIOMMUState *s, + RISCVIOMMUContext *ctx) +{ + uint32_t mode; + + if (get_field(ctx->ta, RISCV_IOMMU_PC_TA_RESERVED)) { + return false; + } + + if (get_field(ctx->satp, RISCV_IOMMU_PC_FSC_RESERVED)) { + return false; + } + + mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); + switch (mode) { + case RISCV_IOMMU_DC_FSC_MODE_BARE: + /* sv39 and sv32 modes have the same value (8) */ + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39: + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48: + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57: + break; + default: + return false; + } + + if (ctx->tc & RISCV_IOMMU_DC_TC_SXL) { + if (mode == RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV32 && + !(s->cap & RISCV_IOMMU_CAP_SV32)) { + return false; + } + } else { + switch (mode) { + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39: + if (!(s->cap & RISCV_IOMMU_CAP_SV39)) { + return false; + } + break; + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48: + if (!(s->cap & RISCV_IOMMU_CAP_SV48)) { + return false; + } + break; + case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57: + if (!(s->cap & RISCV_IOMMU_CAP_SV57)) { + return false; + } + break; + } + } + + return true; +} + +/* + * RISC-V IOMMU Device Context Loopkup - Device Directory Tree Walk + * + * @s : IOMMU Device State + * @ctx : Device Translation Context with devid and process_id set. + * @return : success or fault code. + */ +static int riscv_iommu_ctx_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx) +{ + const uint64_t ddtp = s->ddtp; + unsigned mode = get_field(ddtp, RISCV_IOMMU_DDTP_MODE); + dma_addr_t addr = PPN_PHYS(get_field(ddtp, RISCV_IOMMU_DDTP_PPN)); + struct riscv_iommu_dc dc; + /* Device Context format: 0: extended (64 bytes) | 1: base (32 bytes) */ + const int dc_fmt = !s->enable_msi; + const size_t dc_len = sizeof(dc) >> dc_fmt; + int depth; + uint64_t de; + + switch (mode) { + case RISCV_IOMMU_DDTP_MODE_OFF: + return RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED; + + case RISCV_IOMMU_DDTP_MODE_BARE: + /* mock up pass-through translation context */ + ctx->gatp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD, + RISCV_IOMMU_DC_IOHGATP_MODE_BARE); + ctx->satp = set_field(0, RISCV_IOMMU_ATP_MODE_FIELD, + RISCV_IOMMU_DC_FSC_MODE_BARE); + + ctx->tc = RISCV_IOMMU_DC_TC_V; + if (s->enable_ats) { + ctx->tc |= RISCV_IOMMU_DC_TC_EN_ATS; + } + + ctx->ta = 0; + ctx->msiptp = 0; + return 0; + + case RISCV_IOMMU_DDTP_MODE_1LVL: + depth = 0; + break; + + case RISCV_IOMMU_DDTP_MODE_2LVL: + depth = 1; + break; + + case RISCV_IOMMU_DDTP_MODE_3LVL: + depth = 2; + break; + + default: + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + + /* + * Check supported device id width (in bits). + * See IOMMU Specification, Chapter 6. Software guidelines. + * - if extended device-context format is used: + * 1LVL: 6, 2LVL: 15, 3LVL: 24 + * - if base device-context format is used: + * 1LVL: 7, 2LVL: 16, 3LVL: 24 + */ + if (ctx->devid >= (1 << (depth * 9 + 6 + (dc_fmt && depth != 2)))) { + return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED; + } + + /* Device directory tree walk */ + for (; depth-- > 0; ) { + /* + * Select device id index bits based on device directory tree level + * and device context format. + * See IOMMU Specification, Chapter 2. Data Structures. + * - if extended device-context format is used: + * device index: [23:15][14:6][5:0] + * - if base device-context format is used: + * device index: [23:16][15:7][6:0] + */ + const int split = depth * 9 + 6 + dc_fmt; + addr |= ((ctx->devid >> split) << 3) & ~TARGET_PAGE_MASK; + if (dma_memory_read(s->target_as, addr, &de, sizeof(de), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT; + } + le64_to_cpus(&de); + if (!(de & RISCV_IOMMU_DDTE_VALID)) { + /* invalid directory entry */ + return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID; + } + if (de & ~(RISCV_IOMMU_DDTE_PPN | RISCV_IOMMU_DDTE_VALID)) { + /* reserved bits set */ + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + addr = PPN_PHYS(get_field(de, RISCV_IOMMU_DDTE_PPN)); + } + + /* index into device context entry page */ + addr |= (ctx->devid * dc_len) & ~TARGET_PAGE_MASK; + + memset(&dc, 0, sizeof(dc)); + if (dma_memory_read(s->target_as, addr, &dc, dc_len, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + return RISCV_IOMMU_FQ_CAUSE_DDT_LOAD_FAULT; + } + + /* Set translation context. */ + ctx->tc = le64_to_cpu(dc.tc); + ctx->gatp = le64_to_cpu(dc.iohgatp); + ctx->satp = le64_to_cpu(dc.fsc); + ctx->ta = le64_to_cpu(dc.ta); + ctx->msiptp = le64_to_cpu(dc.msiptp); + ctx->msi_addr_mask = le64_to_cpu(dc.msi_addr_mask); + ctx->msi_addr_pattern = le64_to_cpu(dc.msi_addr_pattern); + + if (!(ctx->tc & RISCV_IOMMU_DC_TC_V)) { + return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID; + } + + if (!riscv_iommu_validate_device_ctx(s, ctx)) { + return RISCV_IOMMU_FQ_CAUSE_DDT_MISCONFIGURED; + } + + /* FSC field checks */ + mode = get_field(ctx->satp, RISCV_IOMMU_DC_FSC_MODE); + addr = PPN_PHYS(get_field(ctx->satp, RISCV_IOMMU_DC_FSC_PPN)); + + if (!(ctx->tc & RISCV_IOMMU_DC_TC_PDTV)) { + if (ctx->process_id != RISCV_IOMMU_NOPROCID) { + /* PID is disabled */ + return RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED; + } + if (mode > RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57) { + /* Invalid translation mode */ + return RISCV_IOMMU_FQ_CAUSE_DDT_INVALID; + } + return 0; + } + + if (ctx->process_id == RISCV_IOMMU_NOPROCID) { + if (!(ctx->tc & RISCV_IOMMU_DC_TC_DPE)) { + /* No default process_id enabled, set BARE mode */ + ctx->satp = 0ULL; + return 0; + } else { + /* Use default process_id #0 */ + ctx->process_id = 0; + } + } + + if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) { + /* No S-Stage translation, done. */ + return 0; + } + + /* FSC.TC.PDTV enabled */ + if (mode > RISCV_IOMMU_DC_FSC_PDTP_MODE_PD20) { + /* Invalid PDTP.MODE */ + return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED; + } + + for (depth = mode - RISCV_IOMMU_DC_FSC_PDTP_MODE_PD8; depth-- > 0; ) { + /* + * Select process id index bits based on process directory tree + * level. See IOMMU Specification, 2.2. Process-Directory-Table. + */ + const int split = depth * 9 + 8; + addr |= ((ctx->process_id >> split) << 3) & ~TARGET_PAGE_MASK; + if (dma_memory_read(s->target_as, addr, &de, sizeof(de), + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; + } + le64_to_cpus(&de); + if (!(de & RISCV_IOMMU_PC_TA_V)) { + return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; + } + addr = PPN_PHYS(get_field(de, RISCV_IOMMU_PC_FSC_PPN)); + } + + /* Leaf entry in PDT */ + addr |= (ctx->process_id << 4) & ~TARGET_PAGE_MASK; + if (dma_memory_read(s->target_as, addr, &dc.ta, sizeof(uint64_t) * 2, + MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + return RISCV_IOMMU_FQ_CAUSE_PDT_LOAD_FAULT; + } + + /* Use FSC and TA from process directory entry. */ + ctx->ta = le64_to_cpu(dc.ta); + ctx->satp = le64_to_cpu(dc.fsc); + + if (!(ctx->ta & RISCV_IOMMU_PC_TA_V)) { + return RISCV_IOMMU_FQ_CAUSE_PDT_INVALID; + } + + if (!riscv_iommu_validate_process_ctx(s, ctx)) { + return RISCV_IOMMU_FQ_CAUSE_PDT_MISCONFIGURED; + } + + return 0; +} + +/* Translation Context cache support */ +static gboolean riscv_iommu_ctx_equal(gconstpointer v1, gconstpointer v2) +{ + RISCVIOMMUContext *c1 = (RISCVIOMMUContext *) v1; + RISCVIOMMUContext *c2 = (RISCVIOMMUContext *) v2; + return c1->devid == c2->devid && + c1->process_id == c2->process_id; +} + +static guint riscv_iommu_ctx_hash(gconstpointer v) +{ + RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) v; + /* + * Generate simple hash of (process_id, devid) + * assuming 24-bit wide devid. + */ + return (guint)(ctx->devid) + ((guint)(ctx->process_id) << 24); +} + +static void riscv_iommu_ctx_inval_devid_procid(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value; + RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data; + if (ctx->tc & RISCV_IOMMU_DC_TC_V && + ctx->devid == arg->devid && + ctx->process_id == arg->process_id) { + ctx->tc &= ~RISCV_IOMMU_DC_TC_V; + } +} + +static void riscv_iommu_ctx_inval_devid(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value; + RISCVIOMMUContext *arg = (RISCVIOMMUContext *) data; + if (ctx->tc & RISCV_IOMMU_DC_TC_V && + ctx->devid == arg->devid) { + ctx->tc &= ~RISCV_IOMMU_DC_TC_V; + } +} + +static void riscv_iommu_ctx_inval_all(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUContext *ctx = (RISCVIOMMUContext *) value; + if (ctx->tc & RISCV_IOMMU_DC_TC_V) { + ctx->tc &= ~RISCV_IOMMU_DC_TC_V; + } +} + +static void riscv_iommu_ctx_inval(RISCVIOMMUState *s, GHFunc func, + uint32_t devid, uint32_t process_id) +{ + GHashTable *ctx_cache; + RISCVIOMMUContext key = { + .devid = devid, + .process_id = process_id, + }; + ctx_cache = g_hash_table_ref(s->ctx_cache); + g_hash_table_foreach(ctx_cache, func, &key); + g_hash_table_unref(ctx_cache); +} + +/* Find or allocate translation context for a given {device_id, process_id} */ +static RISCVIOMMUContext *riscv_iommu_ctx(RISCVIOMMUState *s, + unsigned devid, unsigned process_id, + void **ref) +{ + GHashTable *ctx_cache; + RISCVIOMMUContext *ctx; + RISCVIOMMUContext key = { + .devid = devid, + .process_id = process_id, + }; + + ctx_cache = g_hash_table_ref(s->ctx_cache); + ctx = g_hash_table_lookup(ctx_cache, &key); + + if (ctx && (ctx->tc & RISCV_IOMMU_DC_TC_V)) { + *ref = ctx_cache; + return ctx; + } + + ctx = g_new0(RISCVIOMMUContext, 1); + ctx->devid = devid; + ctx->process_id = process_id; + + int fault = riscv_iommu_ctx_fetch(s, ctx); + if (!fault) { + if (g_hash_table_size(ctx_cache) >= LIMIT_CACHE_CTX) { + g_hash_table_unref(ctx_cache); + ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, + riscv_iommu_ctx_equal, + g_free, NULL); + g_hash_table_ref(ctx_cache); + g_hash_table_unref(qatomic_xchg(&s->ctx_cache, ctx_cache)); + } + g_hash_table_add(ctx_cache, ctx); + *ref = ctx_cache; + return ctx; + } + + g_hash_table_unref(ctx_cache); + *ref = NULL; + + riscv_iommu_report_fault(s, ctx, RISCV_IOMMU_FQ_TTYPE_UADDR_RD, + fault, !!process_id, 0, 0); + + g_free(ctx); + return NULL; +} + +static void riscv_iommu_ctx_put(RISCVIOMMUState *s, void *ref) +{ + if (ref) { + g_hash_table_unref((GHashTable *)ref); + } +} + +/* Find or allocate address space for a given device */ +static AddressSpace *riscv_iommu_space(RISCVIOMMUState *s, uint32_t devid) +{ + RISCVIOMMUSpace *as; + + /* FIXME: PCIe bus remapping for attached endpoints. */ + devid |= s->bus << 8; + + QLIST_FOREACH(as, &s->spaces, list) { + if (as->devid == devid) { + break; + } + } + + if (as == NULL) { + char name[64]; + as = g_new0(RISCVIOMMUSpace, 1); + + as->iommu = s; + as->devid = devid; + + snprintf(name, sizeof(name), "riscv-iommu-%04x:%02x.%d-iova", + PCI_BUS_NUM(as->devid), PCI_SLOT(as->devid), PCI_FUNC(as->devid)); + + /* IOVA address space, untranslated addresses */ + memory_region_init_iommu(&as->iova_mr, sizeof(as->iova_mr), + TYPE_RISCV_IOMMU_MEMORY_REGION, + OBJECT(as), "riscv_iommu", UINT64_MAX); + address_space_init(&as->iova_as, MEMORY_REGION(&as->iova_mr), name); + + QLIST_INSERT_HEAD(&s->spaces, as, list); + + trace_riscv_iommu_new(s->parent_obj.id, PCI_BUS_NUM(as->devid), + PCI_SLOT(as->devid), PCI_FUNC(as->devid)); + } + return &as->iova_as; +} + +/* Translation Object cache support */ +static gboolean riscv_iommu_iot_equal(gconstpointer v1, gconstpointer v2) +{ + RISCVIOMMUEntry *t1 = (RISCVIOMMUEntry *) v1; + RISCVIOMMUEntry *t2 = (RISCVIOMMUEntry *) v2; + return t1->gscid == t2->gscid && t1->pscid == t2->pscid && + t1->iova == t2->iova; +} + +static guint riscv_iommu_iot_hash(gconstpointer v) +{ + RISCVIOMMUEntry *t = (RISCVIOMMUEntry *) v; + return (guint)t->iova; +} + +/* GV: 1 PSCV: 1 AV: 1 */ +static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid && + iot->pscid == arg->pscid && + iot->iova == arg->iova) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 PSCV: 1 AV: 0 */ +static void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid && + iot->pscid == arg->pscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 GVMA: 1 */ +static void riscv_iommu_iot_inval_gscid_gpa(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid) { + /* simplified cache, no GPA matching */ + iot->perm = IOMMU_NONE; + } +} + +/* GV: 1 GVMA: 0 */ +static void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; + if (iot->gscid == arg->gscid) { + iot->perm = IOMMU_NONE; + } +} + +/* GV: 0 */ +static void riscv_iommu_iot_inval_all(gpointer key, gpointer value, + gpointer data) +{ + RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; + iot->perm = IOMMU_NONE; +} + +/* caller should keep ref-count for iot_cache object */ +static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx, + GHashTable *iot_cache, hwaddr iova) +{ + RISCVIOMMUEntry key = { + .gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID), + .pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID), + .iova = PPN_DOWN(iova), + }; + return g_hash_table_lookup(iot_cache, &key); +} + +/* caller should keep ref-count for iot_cache object */ +static void riscv_iommu_iot_update(RISCVIOMMUState *s, + GHashTable *iot_cache, RISCVIOMMUEntry *iot) +{ + if (!s->iot_limit) { + return; + } + + if (g_hash_table_size(s->iot_cache) >= s->iot_limit) { + iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, + riscv_iommu_iot_equal, + g_free, NULL); + g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache)); + } + g_hash_table_add(iot_cache, iot); +} + +static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, + uint32_t gscid, uint32_t pscid, hwaddr iova) +{ + GHashTable *iot_cache; + RISCVIOMMUEntry key = { + .gscid = gscid, + .pscid = pscid, + .iova = PPN_DOWN(iova), + }; + + iot_cache = g_hash_table_ref(s->iot_cache); + g_hash_table_foreach(iot_cache, func, &key); + g_hash_table_unref(iot_cache); +} + +static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, + IOMMUTLBEntry *iotlb, bool enable_cache) +{ + RISCVIOMMUEntry *iot; + IOMMUAccessFlags perm; + bool enable_pid; + bool enable_pri; + GHashTable *iot_cache; + int fault; + + iot_cache = g_hash_table_ref(s->iot_cache); + /* + * TC[32] is reserved for custom extensions, used here to temporarily + * enable automatic page-request generation for ATS queries. + */ + enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & BIT_ULL(32)); + enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV); + + /* Check for ATS request. */ + if (iotlb->perm == IOMMU_NONE) { + /* Check if ATS is disabled. */ + if (!(ctx->tc & RISCV_IOMMU_DC_TC_EN_ATS)) { + enable_pri = false; + fault = RISCV_IOMMU_FQ_CAUSE_TTYPE_BLOCKED; + goto done; + } + } + + iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova); + perm = iot ? iot->perm : IOMMU_NONE; + if (perm != IOMMU_NONE) { + iotlb->translated_addr = PPN_PHYS(iot->phys); + iotlb->addr_mask = ~TARGET_PAGE_MASK; + iotlb->perm = perm; + fault = 0; + goto done; + } + + /* Translate using device directory / page table information. */ + fault = riscv_iommu_spa_fetch(s, ctx, iotlb); + + if (!fault && iotlb->target_as == &s->trap_as) { + /* Do not cache trapped MSI translations */ + goto done; + } + + /* + * We made an implementation choice to not cache identity-mapped + * translations, as allowed by the specification, to avoid + * translation cache evictions for other devices sharing the + * IOMMU hardware model. + */ + if (!fault && iotlb->translated_addr != iotlb->iova && enable_cache) { + iot = g_new0(RISCVIOMMUEntry, 1); + iot->iova = PPN_DOWN(iotlb->iova); + iot->phys = PPN_DOWN(iotlb->translated_addr); + iot->gscid = get_field(ctx->gatp, RISCV_IOMMU_DC_IOHGATP_GSCID); + iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); + iot->perm = iotlb->perm; + riscv_iommu_iot_update(s, iot_cache, iot); + } + +done: + g_hash_table_unref(iot_cache); + + if (enable_pri && fault) { + struct riscv_iommu_pq_record pr = {0}; + if (enable_pid) { + pr.hdr = set_field(RISCV_IOMMU_PREQ_HDR_PV, + RISCV_IOMMU_PREQ_HDR_PID, ctx->process_id); + } + pr.hdr = set_field(pr.hdr, RISCV_IOMMU_PREQ_HDR_DID, ctx->devid); + pr.payload = (iotlb->iova & TARGET_PAGE_MASK) | + RISCV_IOMMU_PREQ_PAYLOAD_M; + riscv_iommu_pri(s, &pr); + return fault; + } + + if (fault) { + unsigned ttype = RISCV_IOMMU_FQ_TTYPE_PCIE_ATS_REQ; + + if (iotlb->perm & IOMMU_RW) { + ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_WR; + } else if (iotlb->perm & IOMMU_RO) { + ttype = RISCV_IOMMU_FQ_TTYPE_UADDR_RD; + } + + riscv_iommu_report_fault(s, ctx, ttype, fault, enable_pid, + iotlb->iova, iotlb->translated_addr); + return fault; + } + + return 0; +} + +/* IOMMU Command Interface */ +static MemTxResult riscv_iommu_iofence(RISCVIOMMUState *s, bool notify, + uint64_t addr, uint32_t data) +{ + /* + * ATS processing in this implementation of the IOMMU is synchronous, + * no need to wait for completions here. + */ + if (!notify) { + return MEMTX_OK; + } + + return dma_memory_write(s->target_as, addr, &data, sizeof(data), + MEMTXATTRS_UNSPECIFIED); +} + +static void riscv_iommu_ats(RISCVIOMMUState *s, + struct riscv_iommu_command *cmd, IOMMUNotifierFlag flag, + IOMMUAccessFlags perm, + void (*trace_fn)(const char *id)) +{ + RISCVIOMMUSpace *as = NULL; + IOMMUNotifier *n; + IOMMUTLBEvent event; + uint32_t pid; + uint32_t devid; + const bool pv = cmd->dword0 & RISCV_IOMMU_CMD_ATS_PV; + + if (cmd->dword0 & RISCV_IOMMU_CMD_ATS_DSV) { + /* Use device segment and requester id */ + devid = get_field(cmd->dword0, + RISCV_IOMMU_CMD_ATS_DSEG | RISCV_IOMMU_CMD_ATS_RID); + } else { + devid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_RID); + } + + pid = get_field(cmd->dword0, RISCV_IOMMU_CMD_ATS_PID); + + QLIST_FOREACH(as, &s->spaces, list) { + if (as->devid == devid) { + break; + } + } + + if (!as || !as->notifier) { + return; + } + + event.type = flag; + event.entry.perm = perm; + event.entry.target_as = s->target_as; + + IOMMU_NOTIFIER_FOREACH(n, &as->iova_mr) { + if (!pv || n->iommu_idx == pid) { + event.entry.iova = n->start; + event.entry.addr_mask = n->end - n->start; + trace_fn(as->iova_mr.parent_obj.name); + memory_region_notify_iommu_one(n, &event); + } + } +} + +static void riscv_iommu_ats_inval(RISCVIOMMUState *s, + struct riscv_iommu_command *cmd) +{ + return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_DEVIOTLB_UNMAP, IOMMU_NONE, + trace_riscv_iommu_ats_inval); +} + +static void riscv_iommu_ats_prgr(RISCVIOMMUState *s, + struct riscv_iommu_command *cmd) +{ + unsigned resp_code = get_field(cmd->dword1, + RISCV_IOMMU_CMD_ATS_PRGR_RESP_CODE); + + /* Using the access flag to carry response code information */ + IOMMUAccessFlags perm = resp_code ? IOMMU_NONE : IOMMU_RW; + return riscv_iommu_ats(s, cmd, IOMMU_NOTIFIER_MAP, perm, + trace_riscv_iommu_ats_prgr); +} + +static void riscv_iommu_process_ddtp(RISCVIOMMUState *s) +{ + uint64_t old_ddtp = s->ddtp; + uint64_t new_ddtp = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_DDTP); + unsigned new_mode = get_field(new_ddtp, RISCV_IOMMU_DDTP_MODE); + unsigned old_mode = get_field(old_ddtp, RISCV_IOMMU_DDTP_MODE); + bool ok = false; + + /* + * Check for allowed DDTP.MODE transitions: + * {OFF, BARE} -> {OFF, BARE, 1LVL, 2LVL, 3LVL} + * {1LVL, 2LVL, 3LVL} -> {OFF, BARE} + */ + if (new_mode == old_mode || + new_mode == RISCV_IOMMU_DDTP_MODE_OFF || + new_mode == RISCV_IOMMU_DDTP_MODE_BARE) { + ok = true; + } else if (new_mode == RISCV_IOMMU_DDTP_MODE_1LVL || + new_mode == RISCV_IOMMU_DDTP_MODE_2LVL || + new_mode == RISCV_IOMMU_DDTP_MODE_3LVL) { + ok = old_mode == RISCV_IOMMU_DDTP_MODE_OFF || + old_mode == RISCV_IOMMU_DDTP_MODE_BARE; + } + + if (ok) { + /* clear reserved and busy bits, report back sanitized version */ + new_ddtp = set_field(new_ddtp & RISCV_IOMMU_DDTP_PPN, + RISCV_IOMMU_DDTP_MODE, new_mode); + } else { + new_ddtp = old_ddtp; + } + s->ddtp = new_ddtp; + + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_DDTP, new_ddtp); +} + +/* Command function and opcode field. */ +#define RISCV_IOMMU_CMD(func, op) (((func) << 7) | (op)) + +static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) +{ + struct riscv_iommu_command cmd; + MemTxResult res; + dma_addr_t addr; + uint32_t tail, head, ctrl; + uint64_t cmd_opcode; + GHFunc func; + + ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); + tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask; + head = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQH) & s->cq_mask; + + /* Check for pending error or queue processing disabled */ + if (!(ctrl & RISCV_IOMMU_CQCSR_CQON) || + !!(ctrl & (RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CQMF))) { + return; + } + + while (tail != head) { + addr = s->cq_addr + head * sizeof(cmd); + res = dma_memory_read(s->target_as, addr, &cmd, sizeof(cmd), + MEMTXATTRS_UNSPECIFIED); + + if (res != MEMTX_OK) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, + RISCV_IOMMU_CQCSR_CQMF, 0); + goto fault; + } + + trace_riscv_iommu_cmd(s->parent_obj.id, cmd.dword0, cmd.dword1); + + cmd_opcode = get_field(cmd.dword0, + RISCV_IOMMU_CMD_OPCODE | RISCV_IOMMU_CMD_FUNC); + + switch (cmd_opcode) { + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOFENCE_FUNC_C, + RISCV_IOMMU_CMD_IOFENCE_OPCODE): + res = riscv_iommu_iofence(s, + cmd.dword0 & RISCV_IOMMU_CMD_IOFENCE_AV, cmd.dword1 << 2, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOFENCE_DATA)); + + if (res != MEMTX_OK) { + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, + RISCV_IOMMU_CQCSR_CQMF, 0); + goto fault; + } + break; + + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA, + RISCV_IOMMU_CMD_IOTINVAL_OPCODE): + if (cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV) { + /* illegal command arguments IOTINVAL.GVMA & PSCV == 1 */ + goto cmd_ill; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) { + /* invalidate all cache mappings */ + func = riscv_iommu_iot_inval_all; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) { + /* invalidate cache matching GSCID */ + func = riscv_iommu_iot_inval_gscid; + } else { + /* invalidate cache matching GSCID and ADDR (GPA) */ + func = riscv_iommu_iot_inval_gscid_gpa; + } + riscv_iommu_iot_inval(s, func, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), 0, + cmd.dword1 << 2 & TARGET_PAGE_MASK); + break; + + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA, + RISCV_IOMMU_CMD_IOTINVAL_OPCODE): + if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_GV)) { + /* invalidate all cache mappings, simplified model */ + func = riscv_iommu_iot_inval_all; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_PSCV)) { + /* invalidate cache matching GSCID, simplified model */ + func = riscv_iommu_iot_inval_gscid; + } else if (!(cmd.dword0 & RISCV_IOMMU_CMD_IOTINVAL_AV)) { + /* invalidate cache matching GSCID and PSCID */ + func = riscv_iommu_iot_inval_pscid; + } else { + /* invalidate cache matching GSCID and PSCID and ADDR (IOVA) */ + func = riscv_iommu_iot_inval_pscid_iova; + } + riscv_iommu_iot_inval(s, func, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_GSCID), + get_field(cmd.dword0, RISCV_IOMMU_CMD_IOTINVAL_PSCID), + cmd.dword1 << 2 & TARGET_PAGE_MASK); + break; + + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_DDT, + RISCV_IOMMU_CMD_IODIR_OPCODE): + if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) { + /* invalidate all device context cache mappings */ + func = riscv_iommu_ctx_inval_all; + } else { + /* invalidate all device context matching DID */ + func = riscv_iommu_ctx_inval_devid; + } + riscv_iommu_ctx_inval(s, func, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0); + break; + + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_IODIR_FUNC_INVAL_PDT, + RISCV_IOMMU_CMD_IODIR_OPCODE): + if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) { + /* illegal command arguments IODIR_PDT & DV == 0 */ + goto cmd_ill; + } else { + func = riscv_iommu_ctx_inval_devid_procid; + } + riscv_iommu_ctx_inval(s, func, + get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), + get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID)); + break; + + /* ATS commands */ + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_INVAL, + RISCV_IOMMU_CMD_ATS_OPCODE): + if (!s->enable_ats) { + goto cmd_ill; + } + + riscv_iommu_ats_inval(s, &cmd); + break; + + case RISCV_IOMMU_CMD(RISCV_IOMMU_CMD_ATS_FUNC_PRGR, + RISCV_IOMMU_CMD_ATS_OPCODE): + if (!s->enable_ats) { + goto cmd_ill; + } + + riscv_iommu_ats_prgr(s, &cmd); + break; + + default: + cmd_ill: + /* Invalid instruction, do not advance instruction index. */ + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, + RISCV_IOMMU_CQCSR_CMD_ILL, 0); + goto fault; + } + + /* Advance and update head pointer after command completes. */ + head = (head + 1) & s->cq_mask; + riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_CQH, head); + } + return; + +fault: + if (ctrl & RISCV_IOMMU_CQCSR_CIE) { + riscv_iommu_notify(s, RISCV_IOMMU_INTR_CQ); + } +} + +static void riscv_iommu_process_cq_control(RISCVIOMMUState *s) +{ + uint64_t base; + uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); + uint32_t ctrl_clr; + bool enable = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQEN); + bool active = !!(ctrl_set & RISCV_IOMMU_CQCSR_CQON); + + if (enable && !active) { + base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_CQB); + s->cq_mask = (2ULL << get_field(base, RISCV_IOMMU_CQB_LOG2SZ)) - 1; + s->cq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_CQB_PPN)); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~s->cq_mask); + stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQH], 0); + stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_CQT], 0); + ctrl_set = RISCV_IOMMU_CQCSR_CQON; + ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQMF | + RISCV_IOMMU_CQCSR_CMD_ILL | RISCV_IOMMU_CQCSR_CMD_TO | + RISCV_IOMMU_CQCSR_FENCE_W_IP; + } else if (!enable && active) { + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQT], ~0); + ctrl_set = 0; + ctrl_clr = RISCV_IOMMU_CQCSR_BUSY | RISCV_IOMMU_CQCSR_CQON; + } else { + ctrl_set = 0; + ctrl_clr = RISCV_IOMMU_CQCSR_BUSY; + } + + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_CQCSR, ctrl_set, ctrl_clr); +} + +static void riscv_iommu_process_fq_control(RISCVIOMMUState *s) +{ + uint64_t base; + uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); + uint32_t ctrl_clr; + bool enable = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQEN); + bool active = !!(ctrl_set & RISCV_IOMMU_FQCSR_FQON); + + if (enable && !active) { + base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_FQB); + s->fq_mask = (2ULL << get_field(base, RISCV_IOMMU_FQB_LOG2SZ)) - 1; + s->fq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_FQB_PPN)); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~s->fq_mask); + stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQH], 0); + stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_FQT], 0); + ctrl_set = RISCV_IOMMU_FQCSR_FQON; + ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQMF | + RISCV_IOMMU_FQCSR_FQOF; + } else if (!enable && active) { + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQH], ~0); + ctrl_set = 0; + ctrl_clr = RISCV_IOMMU_FQCSR_BUSY | RISCV_IOMMU_FQCSR_FQON; + } else { + ctrl_set = 0; + ctrl_clr = RISCV_IOMMU_FQCSR_BUSY; + } + + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_FQCSR, ctrl_set, ctrl_clr); +} + +static void riscv_iommu_process_pq_control(RISCVIOMMUState *s) +{ + uint64_t base; + uint32_t ctrl_set = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR); + uint32_t ctrl_clr; + bool enable = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQEN); + bool active = !!(ctrl_set & RISCV_IOMMU_PQCSR_PQON); + + if (enable && !active) { + base = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_PQB); + s->pq_mask = (2ULL << get_field(base, RISCV_IOMMU_PQB_LOG2SZ)) - 1; + s->pq_addr = PPN_PHYS(get_field(base, RISCV_IOMMU_PQB_PPN)); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~s->pq_mask); + stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQH], 0); + stl_le_p(&s->regs_rw[RISCV_IOMMU_REG_PQT], 0); + ctrl_set = RISCV_IOMMU_PQCSR_PQON; + ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQMF | + RISCV_IOMMU_PQCSR_PQOF; + } else if (!enable && active) { + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQH], ~0); + ctrl_set = 0; + ctrl_clr = RISCV_IOMMU_PQCSR_BUSY | RISCV_IOMMU_PQCSR_PQON; + } else { + ctrl_set = 0; + ctrl_clr = RISCV_IOMMU_PQCSR_BUSY; + } + + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_PQCSR, ctrl_set, ctrl_clr); +} + +static void riscv_iommu_process_dbg(RISCVIOMMUState *s) +{ + uint64_t iova = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_IOVA); + uint64_t ctrl = riscv_iommu_reg_get64(s, RISCV_IOMMU_REG_TR_REQ_CTL); + unsigned devid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_DID); + unsigned pid = get_field(ctrl, RISCV_IOMMU_TR_REQ_CTL_PID); + RISCVIOMMUContext *ctx; + void *ref; + + if (!(ctrl & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY)) { + return; + } + + ctx = riscv_iommu_ctx(s, devid, pid, &ref); + if (ctx == NULL) { + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, + RISCV_IOMMU_TR_RESPONSE_FAULT | + (RISCV_IOMMU_FQ_CAUSE_DMA_DISABLED << 10)); + } else { + IOMMUTLBEntry iotlb = { + .iova = iova, + .perm = ctrl & RISCV_IOMMU_TR_REQ_CTL_NW ? IOMMU_RO : IOMMU_RW, + .addr_mask = ~0, + .target_as = NULL, + }; + int fault = riscv_iommu_translate(s, ctx, &iotlb, false); + if (fault) { + iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10); + } else { + iova = iotlb.translated_addr & ~iotlb.addr_mask; + iova >>= TARGET_PAGE_BITS; + iova &= RISCV_IOMMU_TR_RESPONSE_PPN; + + /* We do not support superpages (> 4kbs) for now */ + iova &= ~RISCV_IOMMU_TR_RESPONSE_S; + } + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova); + } + + riscv_iommu_reg_mod64(s, RISCV_IOMMU_REG_TR_REQ_CTL, 0, + RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); + riscv_iommu_ctx_put(s, ref); +} + +typedef void riscv_iommu_process_fn(RISCVIOMMUState *s); + +static void riscv_iommu_update_icvec(RISCVIOMMUState *s, uint64_t data) +{ + uint64_t icvec = 0; + + icvec |= MIN(data & RISCV_IOMMU_ICVEC_CIV, + s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_CIV); + + icvec |= MIN(data & RISCV_IOMMU_ICVEC_FIV, + s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_FIV); + + icvec |= MIN(data & RISCV_IOMMU_ICVEC_PMIV, + s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PMIV); + + icvec |= MIN(data & RISCV_IOMMU_ICVEC_PIV, + s->icvec_avail_vectors & RISCV_IOMMU_ICVEC_PIV); + + trace_riscv_iommu_icvec_write(data, icvec); + + riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_ICVEC, icvec); +} + +static void riscv_iommu_update_ipsr(RISCVIOMMUState *s, uint64_t data) +{ + uint32_t cqcsr, fqcsr, pqcsr; + uint32_t ipsr_set = 0; + uint32_t ipsr_clr = 0; + + if (data & RISCV_IOMMU_IPSR_CIP) { + cqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); + + if (cqcsr & RISCV_IOMMU_CQCSR_CIE && + (cqcsr & RISCV_IOMMU_CQCSR_FENCE_W_IP || + cqcsr & RISCV_IOMMU_CQCSR_CMD_ILL || + cqcsr & RISCV_IOMMU_CQCSR_CMD_TO || + cqcsr & RISCV_IOMMU_CQCSR_CQMF)) { + ipsr_set |= RISCV_IOMMU_IPSR_CIP; + } else { + ipsr_clr |= RISCV_IOMMU_IPSR_CIP; + } + } else { + ipsr_clr |= RISCV_IOMMU_IPSR_CIP; + } + + if (data & RISCV_IOMMU_IPSR_FIP) { + fqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_FQCSR); + + if (fqcsr & RISCV_IOMMU_FQCSR_FIE && + (fqcsr & RISCV_IOMMU_FQCSR_FQOF || + fqcsr & RISCV_IOMMU_FQCSR_FQMF)) { + ipsr_set |= RISCV_IOMMU_IPSR_FIP; + } else { + ipsr_clr |= RISCV_IOMMU_IPSR_FIP; + } + } else { + ipsr_clr |= RISCV_IOMMU_IPSR_FIP; + } + + if (data & RISCV_IOMMU_IPSR_PIP) { + pqcsr = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_PQCSR); + + if (pqcsr & RISCV_IOMMU_PQCSR_PIE && + (pqcsr & RISCV_IOMMU_PQCSR_PQOF || + pqcsr & RISCV_IOMMU_PQCSR_PQMF)) { + ipsr_set |= RISCV_IOMMU_IPSR_PIP; + } else { + ipsr_clr |= RISCV_IOMMU_IPSR_PIP; + } + } else { + ipsr_clr |= RISCV_IOMMU_IPSR_PIP; + } + + riscv_iommu_reg_mod32(s, RISCV_IOMMU_REG_IPSR, ipsr_set, ipsr_clr); +} + +/* + * Write the resulting value of 'data' for the reg specified + * by 'reg_addr', after considering read-only/read-write/write-clear + * bits, in the pointer 'dest'. + * + * The result is written in little-endian. + */ +static void riscv_iommu_write_reg_val(RISCVIOMMUState *s, + void *dest, hwaddr reg_addr, + int size, uint64_t data) +{ + uint64_t ro = ldn_le_p(&s->regs_ro[reg_addr], size); + uint64_t wc = ldn_le_p(&s->regs_wc[reg_addr], size); + uint64_t rw = ldn_le_p(&s->regs_rw[reg_addr], size); + + stn_le_p(dest, size, ((rw & ro) | (data & ~ro)) & ~(data & wc)); +} + +static MemTxResult riscv_iommu_mmio_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size, + MemTxAttrs attrs) +{ + riscv_iommu_process_fn *process_fn = NULL; + RISCVIOMMUState *s = opaque; + uint32_t regb = addr & ~3; + uint32_t busy = 0; + uint64_t val = 0; + + if ((addr & (size - 1)) != 0) { + /* Unsupported MMIO alignment or access size */ + return MEMTX_ERROR; + } + + if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) { + /* Unsupported MMIO access location. */ + return MEMTX_ACCESS_ERROR; + } + + /* Track actionable MMIO write. */ + switch (regb) { + case RISCV_IOMMU_REG_DDTP: + case RISCV_IOMMU_REG_DDTP + 4: + process_fn = riscv_iommu_process_ddtp; + regb = RISCV_IOMMU_REG_DDTP; + busy = RISCV_IOMMU_DDTP_BUSY; + break; + + case RISCV_IOMMU_REG_CQT: + process_fn = riscv_iommu_process_cq_tail; + break; + + case RISCV_IOMMU_REG_CQCSR: + process_fn = riscv_iommu_process_cq_control; + busy = RISCV_IOMMU_CQCSR_BUSY; + break; + + case RISCV_IOMMU_REG_FQCSR: + process_fn = riscv_iommu_process_fq_control; + busy = RISCV_IOMMU_FQCSR_BUSY; + break; + + case RISCV_IOMMU_REG_PQCSR: + process_fn = riscv_iommu_process_pq_control; + busy = RISCV_IOMMU_PQCSR_BUSY; + break; + + case RISCV_IOMMU_REG_ICVEC: + case RISCV_IOMMU_REG_IPSR: + /* + * ICVEC and IPSR have special read/write procedures. We'll + * call their respective helpers and exit. + */ + riscv_iommu_write_reg_val(s, &val, addr, size, data); + + /* + * 'val' is stored as LE. Switch to host endianess + * before using it. + */ + val = le64_to_cpu(val); + + if (regb == RISCV_IOMMU_REG_ICVEC) { + riscv_iommu_update_icvec(s, val); + } else { + riscv_iommu_update_ipsr(s, val); + } + + return MEMTX_OK; + + case RISCV_IOMMU_REG_TR_REQ_CTL: + process_fn = riscv_iommu_process_dbg; + regb = RISCV_IOMMU_REG_TR_REQ_CTL; + busy = RISCV_IOMMU_TR_REQ_CTL_GO_BUSY; + break; + + default: + break; + } + + /* + * Registers update might be not synchronized with core logic. + * If system software updates register when relevant BUSY bit + * is set IOMMU behavior of additional writes to the register + * is UNSPECIFIED. + */ + riscv_iommu_write_reg_val(s, &s->regs_rw[addr], addr, size, data); + + /* Busy flag update, MSB 4-byte register. */ + if (busy) { + uint32_t rw = ldl_le_p(&s->regs_rw[regb]); + stl_le_p(&s->regs_rw[regb], rw | busy); + } + + if (process_fn) { + process_fn(s); + } + + return MEMTX_OK; +} + +static MemTxResult riscv_iommu_mmio_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, MemTxAttrs attrs) +{ + RISCVIOMMUState *s = opaque; + uint64_t val = -1; + uint8_t *ptr; + + if ((addr & (size - 1)) != 0) { + /* Unsupported MMIO alignment. */ + return MEMTX_ERROR; + } + + if (addr + size > RISCV_IOMMU_REG_MSI_CONFIG) { + return MEMTX_ACCESS_ERROR; + } + + ptr = &s->regs_rw[addr]; + val = ldn_le_p(ptr, size); + + *data = val; + + return MEMTX_OK; +} + +static const MemoryRegionOps riscv_iommu_mmio_ops = { + .read_with_attrs = riscv_iommu_mmio_read, + .write_with_attrs = riscv_iommu_mmio_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + .unaligned = false, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + } +}; + +/* + * Translations matching MSI pattern check are redirected to "riscv-iommu-trap" + * memory region as untranslated address, for additional MSI/MRIF interception + * by IOMMU interrupt remapping implementation. + * Note: Device emulation code generating an MSI is expected to provide a valid + * memory transaction attributes with requested_id set. + */ +static MemTxResult riscv_iommu_trap_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size, MemTxAttrs attrs) +{ + RISCVIOMMUState* s = (RISCVIOMMUState *)opaque; + RISCVIOMMUContext *ctx; + MemTxResult res; + void *ref; + uint32_t devid = attrs.requester_id; + + if (attrs.unspecified) { + return MEMTX_ACCESS_ERROR; + } + + /* FIXME: PCIe bus remapping for attached endpoints. */ + devid |= s->bus << 8; + + ctx = riscv_iommu_ctx(s, devid, 0, &ref); + if (ctx == NULL) { + res = MEMTX_ACCESS_ERROR; + } else { + res = riscv_iommu_msi_write(s, ctx, addr, data, size, attrs); + } + riscv_iommu_ctx_put(s, ref); + return res; +} + +static MemTxResult riscv_iommu_trap_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, MemTxAttrs attrs) +{ + return MEMTX_ACCESS_ERROR; +} + +static const MemoryRegionOps riscv_iommu_trap_ops = { + .read_with_attrs = riscv_iommu_trap_read, + .write_with_attrs = riscv_iommu_trap_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + .unaligned = true, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + } +}; + +static void riscv_iommu_realize(DeviceState *dev, Error **errp) +{ + RISCVIOMMUState *s = RISCV_IOMMU(dev); + + s->cap = s->version & RISCV_IOMMU_CAP_VERSION; + if (s->enable_msi) { + s->cap |= RISCV_IOMMU_CAP_MSI_FLAT | RISCV_IOMMU_CAP_MSI_MRIF; + } + if (s->enable_ats) { + s->cap |= RISCV_IOMMU_CAP_ATS; + } + if (s->enable_s_stage) { + s->cap |= RISCV_IOMMU_CAP_SV32 | RISCV_IOMMU_CAP_SV39 | + RISCV_IOMMU_CAP_SV48 | RISCV_IOMMU_CAP_SV57; + } + if (s->enable_g_stage) { + s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 | + RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; + } + /* Enable translation debug interface */ + s->cap |= RISCV_IOMMU_CAP_DBG; + + /* Report QEMU target physical address space limits */ + s->cap = set_field(s->cap, RISCV_IOMMU_CAP_PAS, + TARGET_PHYS_ADDR_SPACE_BITS); + + /* TODO: method to report supported PID bits */ + s->pid_bits = 8; /* restricted to size of MemTxAttrs.pid */ + s->cap |= RISCV_IOMMU_CAP_PD8; + + /* Out-of-reset translation mode: OFF (DMA disabled) BARE (passthrough) */ + s->ddtp = set_field(0, RISCV_IOMMU_DDTP_MODE, s->enable_off ? + RISCV_IOMMU_DDTP_MODE_OFF : RISCV_IOMMU_DDTP_MODE_BARE); + + /* register storage */ + s->regs_rw = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + s->regs_ro = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + s->regs_wc = g_new0(uint8_t, RISCV_IOMMU_REG_SIZE); + + /* Mark all registers read-only */ + memset(s->regs_ro, 0xff, RISCV_IOMMU_REG_SIZE); + + /* + * Register complete MMIO space, including MSI/PBA registers. + * Note, PCIDevice implementation will add overlapping MR for MSI/PBA, + * managed directly by the PCIDevice implementation. + */ + memory_region_init_io(&s->regs_mr, OBJECT(dev), &riscv_iommu_mmio_ops, s, + "riscv-iommu-regs", RISCV_IOMMU_REG_SIZE); + + /* Set power-on register state */ + stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_CAP], s->cap); + stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_FCTL], 0); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FCTL], + ~(RISCV_IOMMU_FCTL_BE | RISCV_IOMMU_FCTL_WSI)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_DDTP], + ~(RISCV_IOMMU_DDTP_PPN | RISCV_IOMMU_DDTP_MODE)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQB], + ~(RISCV_IOMMU_CQB_LOG2SZ | RISCV_IOMMU_CQB_PPN)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQB], + ~(RISCV_IOMMU_FQB_LOG2SZ | RISCV_IOMMU_FQB_PPN)); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQB], + ~(RISCV_IOMMU_PQB_LOG2SZ | RISCV_IOMMU_PQB_PPN)); + stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQMF | + RISCV_IOMMU_CQCSR_CMD_TO | RISCV_IOMMU_CQCSR_CMD_ILL); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_CQCSR], RISCV_IOMMU_CQCSR_CQON | + RISCV_IOMMU_CQCSR_BUSY); + stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQMF | + RISCV_IOMMU_FQCSR_FQOF); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_FQCSR], RISCV_IOMMU_FQCSR_FQON | + RISCV_IOMMU_FQCSR_BUSY); + stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQMF | + RISCV_IOMMU_PQCSR_PQOF); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_PQCSR], RISCV_IOMMU_PQCSR_PQON | + RISCV_IOMMU_PQCSR_BUSY); + stl_le_p(&s->regs_wc[RISCV_IOMMU_REG_IPSR], ~0); + stl_le_p(&s->regs_ro[RISCV_IOMMU_REG_ICVEC], 0); + stq_le_p(&s->regs_rw[RISCV_IOMMU_REG_DDTP], s->ddtp); + /* If debug registers enabled. */ + if (s->cap & RISCV_IOMMU_CAP_DBG) { + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_IOVA], 0); + stq_le_p(&s->regs_ro[RISCV_IOMMU_REG_TR_REQ_CTL], + RISCV_IOMMU_TR_REQ_CTL_GO_BUSY); + } + + /* Memory region for downstream access, if specified. */ + if (s->target_mr) { + s->target_as = g_new0(AddressSpace, 1); + address_space_init(s->target_as, s->target_mr, + "riscv-iommu-downstream"); + } else { + /* Fallback to global system memory. */ + s->target_as = &address_space_memory; + } + + /* Memory region for untranslated MRIF/MSI writes */ + memory_region_init_io(&s->trap_mr, OBJECT(dev), &riscv_iommu_trap_ops, s, + "riscv-iommu-trap", ~0ULL); + address_space_init(&s->trap_as, &s->trap_mr, "riscv-iommu-trap-as"); + + /* Device translation context cache */ + s->ctx_cache = g_hash_table_new_full(riscv_iommu_ctx_hash, + riscv_iommu_ctx_equal, + g_free, NULL); + + s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, + riscv_iommu_iot_equal, + g_free, NULL); + + s->iommus.le_next = NULL; + s->iommus.le_prev = NULL; + QLIST_INIT(&s->spaces); +} + +static void riscv_iommu_unrealize(DeviceState *dev) +{ + RISCVIOMMUState *s = RISCV_IOMMU(dev); + + g_hash_table_unref(s->iot_cache); + g_hash_table_unref(s->ctx_cache); +} + +static Property riscv_iommu_properties[] = { + DEFINE_PROP_UINT32("version", RISCVIOMMUState, version, + RISCV_IOMMU_SPEC_DOT_VER), + DEFINE_PROP_UINT32("bus", RISCVIOMMUState, bus, 0x0), + DEFINE_PROP_UINT32("ioatc-limit", RISCVIOMMUState, iot_limit, + LIMIT_CACHE_IOT), + DEFINE_PROP_BOOL("intremap", RISCVIOMMUState, enable_msi, TRUE), + DEFINE_PROP_BOOL("ats", RISCVIOMMUState, enable_ats, TRUE), + DEFINE_PROP_BOOL("off", RISCVIOMMUState, enable_off, TRUE), + DEFINE_PROP_BOOL("s-stage", RISCVIOMMUState, enable_s_stage, TRUE), + DEFINE_PROP_BOOL("g-stage", RISCVIOMMUState, enable_g_stage, TRUE), + DEFINE_PROP_LINK("downstream-mr", RISCVIOMMUState, target_mr, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void riscv_iommu_class_init(ObjectClass *klass, void* data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* internal device for riscv-iommu-{pci/sys}, not user-creatable */ + dc->user_creatable = false; + dc->realize = riscv_iommu_realize; + dc->unrealize = riscv_iommu_unrealize; + device_class_set_props(dc, riscv_iommu_properties); +} + +static const TypeInfo riscv_iommu_info = { + .name = TYPE_RISCV_IOMMU, + .parent = TYPE_DEVICE, + .instance_size = sizeof(RISCVIOMMUState), + .class_init = riscv_iommu_class_init, +}; + +static const char *IOMMU_FLAG_STR[] = { + "NA", + "RO", + "WR", + "RW", +}; + +/* RISC-V IOMMU Memory Region - Address Translation Space */ +static IOMMUTLBEntry riscv_iommu_memory_region_translate( + IOMMUMemoryRegion *iommu_mr, hwaddr addr, + IOMMUAccessFlags flag, int iommu_idx) +{ + RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr); + RISCVIOMMUContext *ctx; + void *ref; + IOMMUTLBEntry iotlb = { + .iova = addr, + .target_as = as->iommu->target_as, + .addr_mask = ~0ULL, + .perm = flag, + }; + + ctx = riscv_iommu_ctx(as->iommu, as->devid, iommu_idx, &ref); + if (ctx == NULL) { + /* Translation disabled or invalid. */ + iotlb.addr_mask = 0; + iotlb.perm = IOMMU_NONE; + } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) { + /* Translation disabled or fault reported. */ + iotlb.addr_mask = 0; + iotlb.perm = IOMMU_NONE; + } + + /* Trace all dma translations with original access flags. */ + trace_riscv_iommu_dma(as->iommu->parent_obj.id, PCI_BUS_NUM(as->devid), + PCI_SLOT(as->devid), PCI_FUNC(as->devid), iommu_idx, + IOMMU_FLAG_STR[flag & IOMMU_RW], iotlb.iova, + iotlb.translated_addr); + + riscv_iommu_ctx_put(as->iommu, ref); + + return iotlb; +} + +static int riscv_iommu_memory_region_notify( + IOMMUMemoryRegion *iommu_mr, IOMMUNotifierFlag old, + IOMMUNotifierFlag new, Error **errp) +{ + RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr); + + if (old == IOMMU_NOTIFIER_NONE) { + as->notifier = true; + trace_riscv_iommu_notifier_add(iommu_mr->parent_obj.name); + } else if (new == IOMMU_NOTIFIER_NONE) { + as->notifier = false; + trace_riscv_iommu_notifier_del(iommu_mr->parent_obj.name); + } + + return 0; +} + +static inline bool pci_is_iommu(PCIDevice *pdev) +{ + return pci_get_word(pdev->config + PCI_CLASS_DEVICE) == 0x0806; +} + +static AddressSpace *riscv_iommu_find_as(PCIBus *bus, void *opaque, int devfn) +{ + RISCVIOMMUState *s = (RISCVIOMMUState *) opaque; + PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn); + AddressSpace *as = NULL; + + if (pdev && pci_is_iommu(pdev)) { + return s->target_as; + } + + /* Find first registered IOMMU device */ + while (s->iommus.le_prev) { + s = *(s->iommus.le_prev); + } + + /* Find first matching IOMMU */ + while (s != NULL && as == NULL) { + as = riscv_iommu_space(s, PCI_BUILD_BDF(pci_bus_num(bus), devfn)); + s = s->iommus.le_next; + } + + return as ? as : &address_space_memory; +} + +static const PCIIOMMUOps riscv_iommu_ops = { + .get_address_space = riscv_iommu_find_as, +}; + +void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, + Error **errp) +{ + if (bus->iommu_ops && + bus->iommu_ops->get_address_space == riscv_iommu_find_as) { + /* Allow multiple IOMMUs on the same PCIe bus, link known devices */ + RISCVIOMMUState *last = (RISCVIOMMUState *)bus->iommu_opaque; + QLIST_INSERT_AFTER(last, iommu, iommus); + } else if (!bus->iommu_ops && !bus->iommu_opaque) { + pci_setup_iommu(bus, &riscv_iommu_ops, iommu); + } else { + error_setg(errp, "can't register secondary IOMMU for PCI bus #%d", + pci_bus_num(bus)); + } +} + +static int riscv_iommu_memory_region_index(IOMMUMemoryRegion *iommu_mr, + MemTxAttrs attrs) +{ + return attrs.unspecified ? RISCV_IOMMU_NOPROCID : (int)attrs.pid; +} + +static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr) +{ + RISCVIOMMUSpace *as = container_of(iommu_mr, RISCVIOMMUSpace, iova_mr); + return 1 << as->iommu->pid_bits; +} + +static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = riscv_iommu_memory_region_translate; + imrc->notify_flag_changed = riscv_iommu_memory_region_notify; + imrc->attrs_to_index = riscv_iommu_memory_region_index; + imrc->num_indexes = riscv_iommu_memory_region_index_len; +} + +static const TypeInfo riscv_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_RISCV_IOMMU_MEMORY_REGION, + .class_init = riscv_iommu_memory_region_init, +}; + +static void riscv_iommu_register_mr_types(void) +{ + type_register_static(&riscv_iommu_memory_region_info); + type_register_static(&riscv_iommu_info); +} + +type_init(riscv_iommu_register_mr_types); diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h new file mode 100644 index 0000000000..da3f03440c --- /dev/null +++ b/hw/riscv/riscv-iommu.h @@ -0,0 +1,130 @@ +/* + * QEMU emulation of an RISC-V IOMMU + * + * Copyright (C) 2022-2023 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 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_RISCV_IOMMU_STATE_H +#define HW_RISCV_IOMMU_STATE_H + +#include "qom/object.h" +#include "hw/riscv/iommu.h" + +struct RISCVIOMMUState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + uint32_t version; /* Reported interface version number */ + uint32_t pid_bits; /* process identifier width */ + uint32_t bus; /* PCI bus mapping for non-root endpoints */ + + uint64_t cap; /* IOMMU supported capabilities */ + uint64_t fctl; /* IOMMU enabled features */ + uint64_t icvec_avail_vectors; /* Available interrupt vectors in ICVEC */ + + bool enable_off; /* Enable out-of-reset OFF mode (DMA disabled) */ + bool enable_msi; /* Enable MSI remapping */ + bool enable_ats; /* Enable ATS support */ + bool enable_s_stage; /* Enable S/VS-Stage translation */ + bool enable_g_stage; /* Enable G-Stage translation */ + + /* IOMMU Internal State */ + uint64_t ddtp; /* Validated Device Directory Tree Root Pointer */ + + dma_addr_t cq_addr; /* Command queue base physical address */ + dma_addr_t fq_addr; /* Fault/event queue base physical address */ + dma_addr_t pq_addr; /* Page request queue base physical address */ + + uint32_t cq_mask; /* Command queue index bit mask */ + uint32_t fq_mask; /* Fault/event queue index bit mask */ + uint32_t pq_mask; /* Page request queue index bit mask */ + + /* interrupt notifier */ + void (*notify)(RISCVIOMMUState *iommu, unsigned vector); + + /* IOMMU State Machine */ + QemuThread core_proc; /* Background processing thread */ + QemuCond core_cond; /* Background processing wake up signal */ + unsigned core_exec; /* Processing thread execution actions */ + + /* IOMMU target address space */ + AddressSpace *target_as; + MemoryRegion *target_mr; + + /* MSI / MRIF access trap */ + AddressSpace trap_as; + MemoryRegion trap_mr; + + GHashTable *ctx_cache; /* Device translation Context Cache */ + + GHashTable *iot_cache; /* IO Translated Address Cache */ + unsigned iot_limit; /* IO Translation Cache size limit */ + + /* MMIO Hardware Interface */ + MemoryRegion regs_mr; + uint8_t *regs_rw; /* register state (user write) */ + uint8_t *regs_wc; /* write-1-to-clear mask */ + uint8_t *regs_ro; /* read-only mask */ + + QLIST_ENTRY(RISCVIOMMUState) iommus; + QLIST_HEAD(, RISCVIOMMUSpace) spaces; +}; + +void riscv_iommu_pci_setup_iommu(RISCVIOMMUState *iommu, PCIBus *bus, + Error **errp); + +/* private helpers */ + +/* Register helper functions */ +static inline uint32_t riscv_iommu_reg_mod32(RISCVIOMMUState *s, + unsigned idx, uint32_t set, uint32_t clr) +{ + uint32_t val = ldl_le_p(s->regs_rw + idx); + stl_le_p(s->regs_rw + idx, (val & ~clr) | set); + return val; +} + +static inline void riscv_iommu_reg_set32(RISCVIOMMUState *s, unsigned idx, + uint32_t set) +{ + stl_le_p(s->regs_rw + idx, set); +} + +static inline uint32_t riscv_iommu_reg_get32(RISCVIOMMUState *s, unsigned idx) +{ + return ldl_le_p(s->regs_rw + idx); +} + +static inline uint64_t riscv_iommu_reg_mod64(RISCVIOMMUState *s, unsigned idx, + uint64_t set, uint64_t clr) +{ + uint64_t val = ldq_le_p(s->regs_rw + idx); + stq_le_p(s->regs_rw + idx, (val & ~clr) | set); + return val; +} + +static inline void riscv_iommu_reg_set64(RISCVIOMMUState *s, unsigned idx, + uint64_t set) +{ + stq_le_p(s->regs_rw + idx, set); +} + +static inline uint64_t riscv_iommu_reg_get64(RISCVIOMMUState *s, + unsigned idx) +{ + return ldq_le_p(s->regs_rw + idx); +} +#endif diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index e43cc9445c..2dccc1eff2 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; @@ -45,12 +45,7 @@ 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); - } + hwaddr firmware_load_addr = shakti_c_memmap[SHAKTI_C_RAM].base; /* Initialize SoC */ object_initialize_child(OBJECT(mstate), "soc", &sms->soc, @@ -62,16 +57,14 @@ static void shakti_c_machine_state_init(MachineState *mstate) shakti_c_memmap[SHAKTI_C_RAM].base, mstate->ram); + if (mstate->firmware) { + riscv_load_firmware(mstate->firmware, &firmware_load_addr, NULL); + } + /* ROM reset vector */ - riscv_setup_rom_reset_vec(mstate, &sms->soc.cpus, - shakti_c_memmap[SHAKTI_C_RAM].base, + riscv_setup_rom_reset_vec(mstate, &sms->soc.cpus, firmware_load_addr, shakti_c_memmap[SHAKTI_C_ROM].base, shakti_c_memmap[SHAKTI_C_ROM].size, 0, 0); - if (mstate->firmware) { - riscv_load_firmware(mstate->firmware, - shakti_c_memmap[SHAKTI_C_RAM].base, - NULL); - } } static void shakti_c_machine_instance_init(Object *obj) @@ -81,9 +74,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..5a1959f2a9 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -35,7 +35,6 @@ #include "hw/boards.h" #include "hw/loader.h" #include "hw/sysbus.h" -#include "hw/char/serial.h" #include "hw/misc/unimp.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.h" @@ -45,6 +44,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 +114,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 +185,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 +224,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 +254,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 b139824aab..c5e74126b1 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -43,7 +43,6 @@ #include "hw/irq.h" #include "hw/loader.h" #include "hw/sysbus.h" -#include "hw/char/serial.h" #include "hw/cpu/cluster.h" #include "hw/misc/unimp.h" #include "hw/sd/sd.h" @@ -94,9 +93,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 +111,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 +170,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 +178,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 +191,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 +275,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 +498,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) @@ -530,8 +514,9 @@ static void sifive_u_machine_init(MachineState *machine) SiFiveUState *s = RISCV_U_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *flash0 = g_new(MemoryRegion, 1); - target_ulong start_addr = memmap[SIFIVE_U_DEV_DRAM].base; + hwaddr 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 +548,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 +586,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,16 +604,15 @@ 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; } - /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ - machine->fdt = s->fdt; - /* reset vector */ uint32_t reset_vec[12] = { s->msel, /* MSEL pin state */ @@ -669,7 +645,8 @@ static void sifive_u_machine_init(MachineState *machine) rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), memmap[SIFIVE_U_DEV_MROM].base, &address_space_memory); - riscv_rom_copy_firmware_info(machine, memmap[SIFIVE_U_DEV_MROM].base, + riscv_rom_copy_firmware_info(machine, &s->soc.u_cpus, + memmap[SIFIVE_U_DEV_MROM].base, memmap[SIFIVE_U_DEV_MROM].size, sizeof(reset_vec), kernel_entry); @@ -694,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); @@ -810,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); @@ -914,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 1e1d752c00..fceb91d946 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, @@ -50,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); @@ -78,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); } @@ -96,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); @@ -113,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); @@ -147,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 + @@ -167,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; } } @@ -190,11 +197,15 @@ 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; + hwaddr firmware_load_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)) { @@ -256,29 +267,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, + &firmware_load_addr, + 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 @@ -287,39 +315,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])); - - /* 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); - - /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ - machine->fdt = s->fdt; + 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 the reset vector */ - riscv_setup_rom_reset_vec(machine, &s->soc[0], memmap[SPIKE_DRAM].base, + riscv_setup_rom_reset_vec(machine, &s->soc[0], firmware_load_addr, memmap[SPIKE_MROM].base, memmap[SPIKE_MROM].size, kernel_entry, 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) @@ -339,7 +353,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/trace-events b/hw/riscv/trace-events new file mode 100644 index 0000000000..0527c56c91 --- /dev/null +++ b/hw/riscv/trace-events @@ -0,0 +1,17 @@ +# See documentation at docs/devel/tracing.rst + +# riscv-iommu.c +riscv_iommu_new(const char *id, unsigned b, unsigned d, unsigned f) "%s: device attached %04x:%02x.%d" +riscv_iommu_flt(const char *id, unsigned b, unsigned d, unsigned f, uint64_t reason, uint64_t iova) "%s: fault %04x:%02x.%u reason: 0x%"PRIx64" iova: 0x%"PRIx64 +riscv_iommu_pri(const char *id, unsigned b, unsigned d, unsigned f, uint64_t iova) "%s: page request %04x:%02x.%u iova: 0x%"PRIx64 +riscv_iommu_dma(const char *id, unsigned b, unsigned d, unsigned f, unsigned pasid, const char *dir, uint64_t iova, uint64_t phys) "%s: translate %04x:%02x.%u #%u %s 0x%"PRIx64" -> 0x%"PRIx64 +riscv_iommu_msi(const char *id, unsigned b, unsigned d, unsigned f, uint64_t iova, uint64_t phys) "%s: translate %04x:%02x.%u MSI 0x%"PRIx64" -> 0x%"PRIx64 +riscv_iommu_mrif_notification(const char *id, uint32_t nid, uint64_t phys) "%s: sent MRIF notification 0x%x to 0x%"PRIx64 +riscv_iommu_cmd(const char *id, uint64_t l, uint64_t u) "%s: command 0x%"PRIx64" 0x%"PRIx64 +riscv_iommu_notifier_add(const char *id) "%s: dev-iotlb notifier added" +riscv_iommu_notifier_del(const char *id) "%s: dev-iotlb notifier removed" +riscv_iommu_notify_int_vector(uint32_t cause, uint32_t vector) "Interrupt cause 0x%x sent via vector 0x%x" +riscv_iommu_icvec_write(uint32_t orig, uint32_t actual) "ICVEC write: incoming 0x%x actual 0x%x" +riscv_iommu_ats(const char *id, unsigned b, unsigned d, unsigned f, uint64_t iova) "%s: translate request %04x:%02x.%u iova: 0x%"PRIx64 +riscv_iommu_ats_inval(const char *id) "%s: dev-iotlb invalidate" +riscv_iommu_ats_prgr(const char *id) "%s: dev-iotlb page request group response" diff --git a/hw/riscv/trace.h b/hw/riscv/trace.h new file mode 100644 index 0000000000..8c0e3ca1f3 --- /dev/null +++ b/hw/riscv/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_riscv.h" diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c new file mode 100644 index 0000000000..36d6a3a412 --- /dev/null +++ b/hw/riscv/virt-acpi-build.c @@ -0,0 +1,819 @@ +/* + * 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_plic_aplic(Aml *scope, uint8_t socket_count, + uint64_t mmio_base, uint64_t mmio_size, + const char *hid) +{ + uint64_t plic_aplic_addr; + uint32_t gsi_base; + uint8_t socket; + + for (socket = 0; socket < socket_count; socket++) { + plic_aplic_addr = mmio_base + mmio_size * socket; + gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket; + Aml *dev = aml_device("IC%.02X", socket); + aml_append(dev, aml_name_decl("_HID", aml_string("%s", hid))); + aml_append(dev, aml_name_decl("_UID", aml_int(socket))); + aml_append(dev, aml_name_decl("_GSB", aml_int(gsi_base))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(plic_aplic_addr, mmio_size, + AML_READ_WRITE)); + aml_append(dev, aml_name_decl("_CRS", crs)); + 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("RSCV0003"))); + 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); + + if (s->aia_type == VIRT_AIA_TYPE_NONE) { + acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_PLIC].base, + memmap[VIRT_PLIC].size, "RSCV0001"); + } else { + acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_APLIC_S].base, + memmap[VIRT_APLIC_S].size, "RSCV0002"); + } + + 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 a5bc7353b4..45a8c4f819 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -27,50 +27,46 @@ #include "hw/loader.h" #include "hw/sysbus.h" #include "hw/qdev-properties.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.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/iommu.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 }, @@ -223,75 +219,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]); - qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible", + qemu_fdt_setprop_string(ms->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(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, @@ -299,10 +311,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" }; @@ -318,18 +330,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, @@ -340,10 +349,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); @@ -362,16 +371,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); } @@ -385,39 +394,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, @@ -426,66 +431,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_cell(mc->fdt, plic_name, - "#address-cells", FDT_PLIC_ADDR_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; @@ -496,80 +505,32 @@ 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; + static const char * const imsic_compat[2] = { + "qemu,imsics", "riscv,imsics" + }; - *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; @@ -580,42 +541,129 @@ 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/interrupt-controller@%lx", + (unsigned long)base_addr); + qemu_fdt_add_subnode(ms->fdt, imsic_name); + qemu_fdt_setprop_string_array(ms->fdt, imsic_name, "compatible", + (char **)&imsic_compat, + ARRAY_SIZE(imsic_compat)); + + 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)); + +} + +/* Caller must free string after use */ +static char *fdt_get_aplic_nodename(unsigned long aplic_addr) +{ + return g_strdup_printf("/soc/interrupt-controller@%lx", aplic_addr); +} + +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 = fdt_get_aplic_nodename(aplic_addr); + g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2); + MachineState *ms = MACHINE(s); + static const char * const aplic_compat[2] = { + "qemu,aplic", "riscv,aplic" + }; + + 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); + } + + qemu_fdt_add_subnode(ms->fdt, aplic_name); + qemu_fdt_setprop_string_array(ms->fdt, aplic_name, "compatible", + (char **)&aplic_compat, + ARRAY_SIZE(aplic_compat)); + qemu_fdt_setprop_cell(ms->fdt, aplic_name, "#address-cells", + FDT_APLIC_ADDR_CELLS); + 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,delegation", + aplic_child_phandle, 0x1, + VIRT_IRQCHIP_NUM_SOURCES); + /* + * DEPRECATED_9.1: Compat property kept temporarily + * to allow old firmwares to work with AIA. Do *not* + * use 'riscv,delegate' in new code: use + * 'riscv,delegation' instead. + */ + 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, @@ -624,149 +672,100 @@ 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; 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); - 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); + 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); if (!socket) { - platform_bus_add_all_fdt_nodes(mc->fdt, aplic_name, + g_autofree char *aplic_name = fdt_get_aplic_nodename(aplic_addr); + 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) { - char *pmu_name; - MachineState *mc = MACHINE(s); + g_autofree char *pmu_name = g_strdup_printf("/pmu"); + MachineState *ms = MACHINE(s); RISCVCPU hart = s->soc[0].harts[0]; - pmu_name = g_strdup_printf("/soc/pmu"); - qemu_fdt_add_subnode(mc->fdt, pmu_name); - qemu_fdt_setprop_string(mc->fdt, pmu_name, "compatible", "riscv,pmu"); - riscv_pmu_generate_fdt_node(mc->fdt, hart.cfg.pmu_num, pmu_name); - - g_free(pmu_name); + 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]); } } @@ -776,65 +775,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); } } @@ -842,30 +855,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, @@ -875,8 +887,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, @@ -884,201 +895,270 @@ 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("/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); + 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("/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); + 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/serial@%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", + 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_fw_cfg(RISCVVirtState *s, const MemMapEntry *memmap) { - char *nodename; - MachineState *mc = MACHINE(s); + 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); - nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); - qemu_fdt_add_subnode(mc->fdt, nodename); - qemu_fdt_setprop_string(mc->fdt, nodename, + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "qemu,fw-cfg-mmio"); - qemu_fdt_setprop_sized_cells(mc->fdt, nodename, "reg", + qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg", 2, base, 2, size); - qemu_fdt_setprop(mc->fdt, nodename, "dma-coherent", NULL, 0); - g_free(nodename); + qemu_fdt_setprop(ms->fdt, nodename, "dma-coherent", NULL, 0); } -static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap, - uint64_t mem_size, const char *cmdline, bool is_32_bit) +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 create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) +{ + const char comp[] = "riscv,pci-iommu"; + 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/iommu@%x", pci_node, bdf); + iommu_phandle = qemu_fdt_alloc_phandle(fdt); + qemu_fdt_add_subnode(fdt, iommu_node); + + qemu_fdt_setprop(fdt, iommu_node, "compatible", comp, sizeof(comp)); + 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, iommu_node, "reg", + bdf << 8, 0, 0, 0, 0); + 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; - uint8_t rng_seed[32]; - 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); create_fdt_fw_cfg(s, memmap); create_fdt_pmu(s); - -update_bootargs: - if (cmdline && *cmdline) { - qemu_fdt_setprop_string(mc->fdt, "/chosen", "bootargs", cmdline); - } - - /* Pass seed to RNG */ - qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed)); - qemu_fdt_setprop(mc->fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); } 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); @@ -1109,17 +1189,18 @@ 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; FWCfgState *fw_cfg; 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); return fw_cfg; } @@ -1128,7 +1209,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); @@ -1147,8 +1228,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; } @@ -1159,16 +1238,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 */ @@ -1181,29 +1264,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) @@ -1232,16 +1315,65 @@ 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); + + 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, machine_done); const MemMapEntry *memmap = virt_memmap; MachineState *machine = MACHINE(s); - target_ulong start_addr = memmap[VIRT_DRAM].base; + hwaddr 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, @@ -1259,73 +1391,42 @@ 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; + } } - /* - * 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); - - if (drive_get(IF_PFLASH, 0, 1)) { - /* - * S-mode FW like EDK2 will be kept in second plash (unit 1). - * When both kernel, initrd and pflash options are provided in the - * command line, the kernel and initrd will be copied to the fw_cfg - * table and opensbi will jump to the flash address which is the - * entry point of S-mode FW. It is the job of the S-mode FW to load - * the kernel and initrd using fw_cfg table. - * - * If only pflash is given but not -kernel, then it is the job of - * of the S-mode firmware to locate and load the kernel. - * In either case, the next_addr for opensbi will be the flash address. - */ - riscv_setup_firmware_boot(machine); - kernel_entry = virt_memmap[VIRT_FLASH].base + - virt_memmap[VIRT_FLASH].size / 2; - } else 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); - /* 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, @@ -1340,6 +1441,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) @@ -1348,20 +1455,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); @@ -1379,10 +1493,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", @@ -1391,23 +1503,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, @@ -1415,21 +1526,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 */ @@ -1457,6 +1567,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 */ @@ -1474,6 +1592,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); @@ -1484,6 +1604,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); @@ -1491,29 +1618,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 */ @@ -1522,9 +1639,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); @@ -1532,15 +1656,20 @@ 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) { RISCVVirtState *s = RISCV_VIRT_MACHINE(obj); - char val[32]; - sprintf(val, "%d", s->aia_guests); - return g_strdup(val); + return g_strdup_printf("%d", s->aia_guests); } static void virt_set_aia_guests(Object *obj, const char *val, Error **errp) @@ -1594,28 +1723,51 @@ 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) || + object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) { return HOTPLUG_HANDLER(machine); } + return NULL; } @@ -1632,11 +1784,18 @@ 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))); + } + + if (object_dynamic_cast(OBJECT(dev), TYPE_RISCV_IOMMU_PCI)) { + create_fdt_iommu(s, pci_get_bdf(PCI_DEVICE(dev))); + } } static void virt_machine_class_init(ObjectClass *oc, void *data) { - char str[128]; MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -1644,11 +1803,15 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->init = virt_machine_init; mc->max_cpus = VIRT_CPUS_MAX; mc->default_cpu_type = TYPE_RISCV_CPU_BASE; + mc->block_default_type = IF_VIRTIO; + mc->no_cdrom = 1; mc->pci_allow_0_address = true; mc->possible_cpu_arch_ids = riscv_numa_possible_cpu_arch_ids; 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; @@ -1663,22 +1826,33 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) 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", virt_get_aia_guests, virt_set_aia_guests); - sprintf(str, "Set number of guest MMIO pages for AIA IMSIC. Valid value " - "should be between 0 and %d.", VIRT_IRQCHIP_MAX_GUESTS); - object_class_property_set_description(oc, "aia-guests", str); + { + g_autofree char *str = + g_strdup_printf("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/Kconfig b/hw/rtc/Kconfig index d0d8dda084..2fe04ec1d0 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -14,10 +14,6 @@ config M48T59 config PL031 bool -config TWL92230 - bool - depends on I2C - config MC146818RTC depends on ISA_BUS bool diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 7e493f0e79..1057d6a57f 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() } @@ -320,7 +320,7 @@ static void allwinner_rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_rtc_reset; + device_class_set_legacy_reset(dc, allwinner_rtc_reset); dc->vmsd = &allwinner_rtc_vmstate; device_class_set_props(dc, allwinner_rtc_properties); } diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c index f6da7b666d..3cddf43eea 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() } }; @@ -163,7 +162,7 @@ static void aspeed_rtc_class_init(ObjectClass *klass, void *data) dc->realize = aspeed_rtc_realize; dc->vmsd = &vmstate_aspeed_rtc; - dc->reset = aspeed_rtc_reset; + device_class_set_legacy_reset(dc, aspeed_rtc_reset); } static const TypeInfo aspeed_rtc_info = { diff --git a/hw/rtc/ds1338.c b/hw/rtc/ds1338.c index 36d8121ddd..c993182ae4 100644 --- a/hw/rtc/ds1338.c +++ b/hw/rtc/ds1338.c @@ -14,9 +14,9 @@ #include "hw/i2c/i2c.h" #include "migration/vmstate.h" #include "qemu/bcd.h" -#include "qemu/module.h" #include "qom/object.h" #include "sysemu/rtc.h" +#include "trace.h" /* Size of NVRAM including both the user-accessible area and the * secondary register area. @@ -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), @@ -126,6 +126,9 @@ static uint8_t ds1338_recv(I2CSlave *i2c) uint8_t res; res = s->nvram[s->ptr]; + + trace_ds1338_recv(s->ptr, res); + inc_regptr(s); return res; } @@ -134,6 +137,8 @@ static int ds1338_send(I2CSlave *i2c, uint8_t data) { DS1338State *s = DS1338(i2c); + trace_ds1338_send(s->ptr, data); + if (s->addr_byte) { s->ptr = data & (NVRAM_SIZE - 1); s->addr_byte = false; @@ -223,20 +228,17 @@ static void ds1338_class_init(ObjectClass *klass, void *data) k->event = ds1338_event; k->recv = ds1338_recv; k->send = ds1338_send; - dc->reset = ds1338_reset; + device_class_set_legacy_reset(dc, ds1338_reset); dc->vmsd = &vmstate_ds1338; } -static const TypeInfo ds1338_info = { - .name = TYPE_DS1338, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(DS1338State), - .class_init = ds1338_class_init, +static const TypeInfo ds1338_types[] = { + { + .name = TYPE_DS1338, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(DS1338State), + .class_init = ds1338_class_init, + }, }; -static void ds1338_register_types(void) -{ - type_register_static(&ds1338_info); -} - -type_init(ds1338_register_types) +DEFINE_TYPES(ds1338_types) diff --git a/hw/rtc/exynos4210_rtc.c b/hw/rtc/exynos4210_rtc.c index d1620c7a2a..ca28a45672 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; @@ -596,7 +596,7 @@ static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_rtc_reset; + device_class_set_legacy_reset(dc, exynos4210_rtc_reset); dc->vmsd = &vmstate_exynos4210_rtc_state; } diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index 19a56402a0..a6dfbf89f3 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -242,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), @@ -298,7 +298,7 @@ static void goldfish_rtc_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, goldfish_rtc_properties); dc->realize = goldfish_rtc_realize; - dc->reset = goldfish_rtc_reset; + device_class_set_legacy_reset(dc, goldfish_rtc_reset); dc->vmsd = &goldfish_rtc_vmstate; } diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index 1f9e38a735..c9c3cd84da 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Loongarch LS7A Real Time Clock emulation + * LoongArch LS7A Real Time Clock emulation * * Copyright (C) 2021 Loongson Technology Corporation Limited */ @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/irq.h" -#include "include/hw/register.h" +#include "hw/register.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" @@ -454,7 +454,7 @@ 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_UINT32_ARRAY(toymatch, LS7ARtcState, TIMER_NUMS), @@ -469,7 +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; + device_class_set_legacy_reset(dc, ls7a_rtc_reset); dc->desc = "ls7a rtc"; } diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index e61f7ec370..6e9723fdf1 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; }; @@ -120,7 +120,7 @@ static void m48txx_isa_class_init(ObjectClass *klass, void *data) NvramClass *nc = NVRAM_CLASS(klass); dc->realize = m48t59_isa_realize; - dc->reset = m48t59_reset_isa; + device_class_set_legacy_reset(dc, m48t59_reset_isa); device_class_set_props(dc, m48t59_isa_properties); nc->read = m48txx_isa_read; nc->write = m48txx_isa_write; diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 74345d9d90..48846d8df4 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), @@ -629,7 +629,7 @@ static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) NvramClass *nc = NVRAM_CLASS(klass); dc->realize = m48t59_realize; - dc->reset = m48t59_reset_sysbus; + device_class_set_legacy_reset(dc, m48t59_reset_sysbus); device_class_set_props(dc, m48t59_sysbus_properties); dc->vmsd = &vmstate_m48t59; nc->read = m48txx_sysbus_read; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index af5607ad04..d155444c4e 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -27,6 +27,7 @@ #include "qemu/module.h" #include "qemu/bcd.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 @@ -76,19 +71,19 @@ #define RTC_ISA_BASE 0x70 -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); +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(RTCState *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); @@ -96,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); @@ -109,29 +104,21 @@ static void rtc_coalesced_timer_update(RTCState *s) } } -static QLIST_HEAD(, RTCState) rtc_devices = - QLIST_HEAD_INITIALIZER(rtc_devices); - -#ifdef TARGET_I386 -void qmp_rtc_reset_reinjection(Error **errp) +void rtc_reset_reinjection(MC146818RtcState *rtc) { - RTCState *s; - - QLIST_FOREACH(s, &rtc_devices, link) { - s->irq_coalesced = 0; - } + rtc->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; @@ -145,15 +132,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; @@ -170,8 +150,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; @@ -247,7 +227,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; @@ -268,7 +248,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; @@ -319,7 +299,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; @@ -330,7 +310,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; @@ -423,7 +403,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; @@ -452,7 +432,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; @@ -576,7 +556,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; @@ -585,7 +565,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; @@ -597,7 +577,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]); @@ -620,9 +600,9 @@ static void rtc_get_time(RTCState *s, struct tm *tm) #endif } -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); @@ -632,7 +612,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; @@ -660,7 +640,7 @@ static void rtc_set_cmos(RTCState *s, const struct tm *tm) #endif } -static void rtc_update_time(RTCState *s) +static void rtc_update_time(MC146818RtcState *s) { struct tm ret; time_t guest_sec; @@ -676,7 +656,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; @@ -705,7 +685,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; @@ -770,23 +750,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 < sizeof(s->cmos_data)) 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 < sizeof(s->cmos_data)); 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); @@ -801,7 +779,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); @@ -810,7 +788,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); @@ -841,7 +819,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; } @@ -850,8 +828,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() } }; @@ -862,23 +840,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 } @@ -888,8 +866,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 = { @@ -904,7 +883,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); @@ -913,7 +892,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; @@ -940,12 +919,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: @@ -975,14 +952,14 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) object_property_add_tm(OBJECT(s), "date", rtc_get_date); qdev_init_gpio_out(dev, &s->irq, 1); - 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); @@ -998,21 +975,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_UINT16("iobase", RTCState, io_base, RTC_ISA_BASE), - 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) */ @@ -1029,18 +1006,32 @@ static void rtc_reset_enter(Object *obj, ResetType type) s->irq_coalesced = 0; s->irq_reinject_on_ack_count = 0; } + + // xbox bios wants this bit pattern set to mark the data as valid +#ifdef XBOX + uint8_t bits = 0x55; + for (int i = 0x10; i < 0x70; i++) { + mc146818rtc_set_cmos_data(s, i, bits); + bits = ~bits; + } + bits = 0x55; + for (int i = 0x80; i < 0x100; i++) { + mc146818rtc_set_cmos_data(s, i, bits); + bits = ~bits; + } +#endif } -static void rtc_reset_hold(Object *obj) +static void rtc_reset_hold(Object *obj, ResetType type) { - RTCState *s = MC146818_RTC(obj); + MC146818RtcState *s = MC146818_RTC(obj); qemu_irq_lower(s->irq); } static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope) { - RTCState *s = MC146818_RTC(adev); + MC146818RtcState *s = MC146818_RTC(adev); Aml *dev; Aml *crs; @@ -1078,7 +1069,7 @@ 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 }, diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index dc33973384..8ecc2d792c 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -1,17 +1,15 @@ -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_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/trace-events b/hw/rtc/trace-events index ebb311a5b0..8012afe102 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -22,6 +22,10 @@ pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks" aspeed_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 aspeed_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 +# ds1338.c +ds1338_recv(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] -> 0x%02" PRIx8 +ds1338_send(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] <- 0x%02" PRIx8 + # m48t59.c m48txx_nvram_io_read(uint64_t addr, uint64_t value) "io read addr:0x%04" PRIx64 " value:0x%02" PRIx64 m48txx_nvram_io_write(uint64_t addr, uint64_t value) "io write addr:0x%04" PRIx64 " value:0x%02" PRIx64 diff --git a/hw/rtc/twl92230.c b/hw/rtc/twl92230.c deleted file mode 100644 index e8d5eda3fc..0000000000 --- a/hw/rtc/twl92230.c +++ /dev/null @@ -1,882 +0,0 @@ -/* - * TI TWL92230C energy-management companion device for the OMAP24xx. - * Aka. Menelaus (N4200 MENELAUS1_V2.2) - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "hw/i2c/i2c.h" -#include "hw/irq.h" -#include "migration/qemu-file-types.h" -#include "migration/vmstate.h" -#include "sysemu/sysemu.h" -#include "sysemu/rtc.h" -#include "qemu/bcd.h" -#include "qemu/module.h" -#include "qom/object.h" - -#define VERBOSE 1 - -#define TYPE_TWL92230 "twl92230" -OBJECT_DECLARE_SIMPLE_TYPE(MenelausState, TWL92230) - -struct MenelausState { - I2CSlave parent_obj; - - int firstbyte; - uint8_t reg; - - uint8_t vcore[5]; - uint8_t dcdc[3]; - uint8_t ldo[8]; - uint8_t sleep[2]; - uint8_t osc; - uint8_t detect; - uint16_t mask; - uint16_t status; - uint8_t dir; - uint8_t inputs; - uint8_t outputs; - uint8_t bbsms; - uint8_t pull[4]; - uint8_t mmc_ctrl[3]; - uint8_t mmc_debounce; - struct { - uint8_t ctrl; - uint16_t comp; - QEMUTimer *hz_tm; - int64_t next; - struct tm tm; - struct tm new; - struct tm alm; - int sec_offset; - int alm_sec; - int next_comp; - } rtc; - uint16_t rtc_next_vmstate; - qemu_irq out[4]; - uint8_t pwrbtn_state; -}; - -static inline void menelaus_update(MenelausState *s) -{ - qemu_set_irq(s->out[3], s->status & ~s->mask); -} - -static inline void menelaus_rtc_start(MenelausState *s) -{ - s->rtc.next += qemu_clock_get_ms(rtc_clock); - timer_mod(s->rtc.hz_tm, s->rtc.next); -} - -static inline void menelaus_rtc_stop(MenelausState *s) -{ - timer_del(s->rtc.hz_tm); - s->rtc.next -= qemu_clock_get_ms(rtc_clock); - if (s->rtc.next < 1) - s->rtc.next = 1; -} - -static void menelaus_rtc_update(MenelausState *s) -{ - qemu_get_timedate(&s->rtc.tm, s->rtc.sec_offset); -} - -static void menelaus_alm_update(MenelausState *s) -{ - if ((s->rtc.ctrl & 3) == 3) - s->rtc.alm_sec = qemu_timedate_diff(&s->rtc.alm) - s->rtc.sec_offset; -} - -static void menelaus_rtc_hz(void *opaque) -{ - MenelausState *s = (MenelausState *) opaque; - - s->rtc.next_comp --; - s->rtc.alm_sec --; - s->rtc.next += 1000; - timer_mod(s->rtc.hz_tm, s->rtc.next); - 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 */ - else if (((s->rtc.ctrl >> 3) & 3) == 2 && !s->rtc.tm.tm_min) - s->status |= 1 << 8; /* RTCTMR */ - else if (!s->rtc.tm.tm_hour) - s->status |= 1 << 8; /* RTCTMR */ - } else - 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 */ - /* TODO: wake-up */ - } - if (s->rtc.next_comp <= 0) { - s->rtc.next -= muldiv64((int16_t) s->rtc.comp, 1000, 0x8000); - s->rtc.next_comp = 3600; - } - menelaus_update(s); -} - -static void menelaus_reset(I2CSlave *i2c) -{ - MenelausState *s = TWL92230(i2c); - - s->reg = 0x00; - - 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[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[5] = 0x00; - s->ldo[6] = 0x00; - s->ldo[7] = 0x00; - s->sleep[0] = 0x00; - s->sleep[1] = 0x00; - s->osc = 0x01; - s->detect = 0x09; - s->mask = 0x0fff; - s->status = 0; - s->dir = 0x07; - s->outputs = 0x00; - s->bbsms = 0x00; - s->pull[0] = 0x00; - s->pull[1] = 0x00; - s->pull[2] = 0x00; - s->pull[3] = 0x00; - s->mmc_ctrl[0] = 0x03; - s->mmc_ctrl[1] = 0xc0; - s->mmc_ctrl[2] = 0x00; - s->mmc_debounce = 0x05; - - if (s->rtc.ctrl & 1) - menelaus_rtc_stop(s); - s->rtc.ctrl = 0x00; - s->rtc.comp = 0x0000; - s->rtc.next = 1000; - s->rtc.sec_offset = 0; - s->rtc.next_comp = 1800; - s->rtc.alm_sec = 1800; - s->rtc.alm.tm_sec = 0x00; - s->rtc.alm.tm_min = 0x00; - s->rtc.alm.tm_hour = 0x00; - s->rtc.alm.tm_mday = 0x01; - s->rtc.alm.tm_mon = 0x00; - s->rtc.alm.tm_year = 2004; - menelaus_update(s); -} - -static void menelaus_gpio_set(void *opaque, int line, int level) -{ - MenelausState *s = (MenelausState *) opaque; - - if (line < 3) { - /* No interrupt generated */ - s->inputs &= ~(1 << line); - s->inputs |= level << line; - return; - } - - if (!s->pwrbtn_state && level) { - 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 - -static uint8_t menelaus_read(void *opaque, uint8_t addr) -{ - MenelausState *s = (MenelausState *) opaque; - - switch (addr) { - case MENELAUS_REV: - return 0x22; - - case MENELAUS_VCORE_CTRL1 ... MENELAUS_VCORE_CTRL5: - return s->vcore[addr - MENELAUS_VCORE_CTRL1]; - - case MENELAUS_DCDC_CTRL1 ... MENELAUS_DCDC_CTRL3: - return s->dcdc[addr - MENELAUS_DCDC_CTRL1]; - - case MENELAUS_LDO_CTRL1 ... MENELAUS_LDO_CTRL8: - return s->ldo[addr - MENELAUS_LDO_CTRL1]; - - case MENELAUS_SLEEP_CTRL1: - case MENELAUS_SLEEP_CTRL2: - return s->sleep[addr - MENELAUS_SLEEP_CTRL1]; - - case MENELAUS_DEVICE_OFF: - return 0; - - case MENELAUS_OSC_CTRL: - return s->osc | (1 << 7); /* CLK32K_GOOD */ - - case MENELAUS_DETECT_CTRL: - return s->detect; - - case MENELAUS_INT_MASK1: - return (s->mask >> 0) & 0xff; - case MENELAUS_INT_MASK2: - return (s->mask >> 8) & 0xff; - - case MENELAUS_INT_STATUS1: - return (s->status >> 0) & 0xff; - case MENELAUS_INT_STATUS2: - return (s->status >> 8) & 0xff; - - case MENELAUS_INT_ACK1: - case MENELAUS_INT_ACK2: - return 0; - - case MENELAUS_GPIO_CTRL: - return s->dir; - case MENELAUS_GPIO_IN: - return s->inputs | (~s->dir & s->outputs); - case MENELAUS_GPIO_OUT: - return s->outputs; - - case MENELAUS_BBSMS: - return s->bbsms; - - case MENELAUS_RTC_CTRL: - return s->rtc.ctrl; - case MENELAUS_RTC_UPDATE: - return 0x00; - case MENELAUS_RTC_SEC: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_sec); - case MENELAUS_RTC_MIN: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_min); - case MENELAUS_RTC_HR: - menelaus_rtc_update(s); - 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 */ - else - return to_bcd(s->rtc.tm.tm_hour); - case MENELAUS_RTC_DAY: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_mday); - case MENELAUS_RTC_MON: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_mon + 1); - case MENELAUS_RTC_YR: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_year - 2000); - case MENELAUS_RTC_WKDAY: - menelaus_rtc_update(s); - return to_bcd(s->rtc.tm.tm_wday); - case MENELAUS_RTC_AL_SEC: - return to_bcd(s->rtc.alm.tm_sec); - 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 */ - return to_bcd((s->rtc.alm.tm_hour % 12) + 1) | - (!!(s->rtc.alm.tm_hour >= 12) << 7);/* AL_PM_nAM */ - else - return to_bcd(s->rtc.alm.tm_hour); - case MENELAUS_RTC_AL_DAY: - return to_bcd(s->rtc.alm.tm_mday); - case MENELAUS_RTC_AL_MON: - return to_bcd(s->rtc.alm.tm_mon + 1); - case MENELAUS_RTC_AL_YR: - return to_bcd(s->rtc.alm.tm_year - 2000); - case MENELAUS_RTC_COMP_MSB: - return (s->rtc.comp >> 8) & 0xff; - case MENELAUS_RTC_COMP_LSB: - return (s->rtc.comp >> 0) & 0xff; - - case MENELAUS_S1_PULL_EN: - return s->pull[0]; - case MENELAUS_S1_PULL_DIR: - return s->pull[1]; - case MENELAUS_S2_PULL_EN: - return s->pull[2]; - case MENELAUS_S2_PULL_DIR: - return s->pull[3]; - - case MENELAUS_MCT_CTRL1 ... MENELAUS_MCT_CTRL3: - return s->mmc_ctrl[addr - MENELAUS_MCT_CTRL1]; - case MENELAUS_MCT_PIN_ST: - /* TODO: return the real Card Detect */ - return 0; - case MENELAUS_DEBOUNCE1: - return s->mmc_debounce; - - default: -#ifdef VERBOSE - printf("%s: unknown register %02x\n", __func__, addr); -#endif - break; - } - return 0; -} - -static void menelaus_write(void *opaque, uint8_t addr, uint8_t value) -{ - MenelausState *s = (MenelausState *) opaque; - int line; - struct tm tm; - - switch (addr) { - case MENELAUS_VCORE_CTRL1: - s->vcore[0] = (value & 0xe) | MIN(value & 0x1f, 0x12); - break; - case MENELAUS_VCORE_CTRL2: - s->vcore[1] = value; - break; - case MENELAUS_VCORE_CTRL3: - s->vcore[2] = MIN(value & 0x1f, 0x12); - break; - case MENELAUS_VCORE_CTRL4: - s->vcore[3] = MIN(value & 0x1f, 0x12); - break; - case MENELAUS_VCORE_CTRL5: - s->vcore[4] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - - case MENELAUS_DCDC_CTRL1: - s->dcdc[0] = value & 0x3f; - break; - case MENELAUS_DCDC_CTRL2: - s->dcdc[1] = value & 0x07; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_DCDC_CTRL3: - s->dcdc[2] = value & 0x07; - break; - - case MENELAUS_LDO_CTRL1: - s->ldo[0] = value; - break; - case MENELAUS_LDO_CTRL2: - s->ldo[1] = value & 0x7f; - /* XXX - * auto set to 0x7e on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL3: - s->ldo[2] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL4: - s->ldo[3] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL5: - s->ldo[4] = value & 3; - /* XXX - * auto set to 3 on M_Active, nRESWARM - * auto set to 0 on M_WaitOn, M_Backup - */ - break; - case MENELAUS_LDO_CTRL6: - s->ldo[5] = value & 3; - break; - case MENELAUS_LDO_CTRL7: - s->ldo[6] = value & 3; - break; - case MENELAUS_LDO_CTRL8: - s->ldo[7] = value & 3; - break; - - case MENELAUS_SLEEP_CTRL1: - case MENELAUS_SLEEP_CTRL2: - s->sleep[addr - MENELAUS_SLEEP_CTRL1] = value; - break; - - case MENELAUS_DEVICE_OFF: - if (value & 1) { - menelaus_reset(I2C_SLAVE(s)); - } - break; - - case MENELAUS_OSC_CTRL: - s->osc = value & 7; - break; - - case MENELAUS_DETECT_CTRL: - s->detect = value & 0x7f; - break; - - case MENELAUS_INT_MASK1: - s->mask &= 0xf00; - s->mask |= value << 0; - menelaus_update(s); - break; - case MENELAUS_INT_MASK2: - s->mask &= 0x0ff; - s->mask |= value << 8; - menelaus_update(s); - break; - - case MENELAUS_INT_ACK1: - s->status &= ~(((uint16_t) value) << 0); - menelaus_update(s); - break; - case MENELAUS_INT_ACK2: - s->status &= ~(((uint16_t) value) << 8); - menelaus_update(s); - break; - - case MENELAUS_GPIO_CTRL: - for (line = 0; line < 3; line ++) { - if (((s->dir ^ value) >> line) & 1) { - qemu_set_irq(s->out[line], - ((s->outputs & ~s->dir) >> line) & 1); - } - } - s->dir = value & 0x67; - break; - case MENELAUS_GPIO_OUT: - for (line = 0; line < 3; line ++) { - if ((((s->outputs ^ value) & ~s->dir) >> line) & 1) { - qemu_set_irq(s->out[line], (s->outputs >> line) & 1); - } - } - s->outputs = value & 0x07; - break; - - case MENELAUS_BBSMS: - s->bbsms = 0x0d; - break; - - case MENELAUS_RTC_CTRL: - if ((s->rtc.ctrl ^ value) & 1) { /* RTC_EN */ - if (value & 1) - menelaus_rtc_start(s); - else - menelaus_rtc_stop(s); - } - s->rtc.ctrl = value & 0x1f; - menelaus_alm_update(s); - break; - case MENELAUS_RTC_UPDATE: - menelaus_rtc_update(s); - memcpy(&tm, &s->rtc.tm, sizeof(tm)); - switch (value & 0xf) { - case 0: - break; - case 1: - tm.tm_sec = s->rtc.new.tm_sec; - break; - case 2: - tm.tm_min = s->rtc.new.tm_min; - break; - case 3: - if (s->rtc.new.tm_hour > 23) - goto rtc_badness; - tm.tm_hour = s->rtc.new.tm_hour; - break; - case 4: - if (s->rtc.new.tm_mday < 1) - goto rtc_badness; - /* TODO check range */ - tm.tm_mday = s->rtc.new.tm_mday; - break; - case 5: - if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11) - goto rtc_badness; - tm.tm_mon = s->rtc.new.tm_mon; - break; - case 6: - tm.tm_year = s->rtc.new.tm_year; - break; - case 7: - /* TODO set .tm_mday instead */ - tm.tm_wday = s->rtc.new.tm_wday; - break; - case 8: - if (s->rtc.new.tm_hour > 23) - goto rtc_badness; - if (s->rtc.new.tm_mday < 1) - goto rtc_badness; - if (s->rtc.new.tm_mon < 0 || s->rtc.new.tm_mon > 11) - goto rtc_badness; - tm.tm_sec = s->rtc.new.tm_sec; - tm.tm_min = s->rtc.new.tm_min; - tm.tm_hour = s->rtc.new.tm_hour; - tm.tm_mday = s->rtc.new.tm_mday; - tm.tm_mon = s->rtc.new.tm_mon; - tm.tm_year = s->rtc.new.tm_year; - break; - rtc_badness: - default: - fprintf(stderr, "%s: bad RTC_UPDATE value %02x\n", - __func__, value); - s->status |= 1 << 10; /* RTCERR */ - menelaus_update(s); - } - s->rtc.sec_offset = qemu_timedate_diff(&tm); - break; - case MENELAUS_RTC_SEC: - s->rtc.tm.tm_sec = from_bcd(value & 0x7f); - break; - case MENELAUS_RTC_MIN: - 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 */ - MIN(from_bcd(value & 0x3f), 12) + ((value >> 7) ? 11 : -1) : - from_bcd(value & 0x3f); - break; - case MENELAUS_RTC_DAY: - s->rtc.tm.tm_mday = from_bcd(value); - break; - case MENELAUS_RTC_MON: - s->rtc.tm.tm_mon = MAX(1, from_bcd(value)) - 1; - break; - case MENELAUS_RTC_YR: - s->rtc.tm.tm_year = 2000 + from_bcd(value); - break; - case MENELAUS_RTC_WKDAY: - s->rtc.tm.tm_mday = from_bcd(value); - break; - case MENELAUS_RTC_AL_SEC: - s->rtc.alm.tm_sec = from_bcd(value & 0x7f); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_MIN: - s->rtc.alm.tm_min = from_bcd(value & 0x7f); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_HR: - 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); - break; - case MENELAUS_RTC_AL_DAY: - s->rtc.alm.tm_mday = from_bcd(value); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_MON: - s->rtc.alm.tm_mon = MAX(1, from_bcd(value)) - 1; - menelaus_alm_update(s); - break; - case MENELAUS_RTC_AL_YR: - s->rtc.alm.tm_year = 2000 + from_bcd(value); - menelaus_alm_update(s); - break; - case MENELAUS_RTC_COMP_MSB: - s->rtc.comp &= 0xff; - s->rtc.comp |= value << 8; - break; - case MENELAUS_RTC_COMP_LSB: - s->rtc.comp &= 0xff << 8; - s->rtc.comp |= value; - break; - - case MENELAUS_S1_PULL_EN: - s->pull[0] = value; - break; - case MENELAUS_S1_PULL_DIR: - s->pull[1] = value & 0x1f; - break; - case MENELAUS_S2_PULL_EN: - s->pull[2] = value; - break; - case MENELAUS_S2_PULL_DIR: - s->pull[3] = value & 0x1f; - break; - - case MENELAUS_MCT_CTRL1: - s->mmc_ctrl[0] = value & 0x7f; - break; - case MENELAUS_MCT_CTRL2: - s->mmc_ctrl[1] = value; - /* TODO update Card Detect interrupts */ - break; - case MENELAUS_MCT_CTRL3: - s->mmc_ctrl[2] = value & 0xf; - break; - case MENELAUS_DEBOUNCE1: - s->mmc_debounce = value & 0x3f; - break; - - default: -#ifdef VERBOSE - printf("%s: unknown register %02x\n", __func__, addr); -#endif - break; - } -} - -static int menelaus_event(I2CSlave *i2c, enum i2c_event event) -{ - MenelausState *s = TWL92230(i2c); - - if (event == I2C_START_SEND) - s->firstbyte = 1; - - return 0; -} - -static int menelaus_tx(I2CSlave *i2c, uint8_t data) -{ - MenelausState *s = TWL92230(i2c); - - /* Interpret register address byte */ - if (s->firstbyte) { - s->reg = data; - s->firstbyte = 0; - } else - menelaus_write(s, s->reg ++, data); - - return 0; -} - -static uint8_t menelaus_rx(I2CSlave *i2c) -{ - MenelausState *s = TWL92230(i2c); - - return menelaus_read(s, s->reg ++); -} - -/* Save restore 32 bit int as uint16_t - This is a Big hack, but it is how the old state did it. - Or we broke compatibility in the state, or we can't use struct tm - */ - -static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size, - const VMStateField *field) -{ - int *v = pv; - *v = qemu_get_be16(f); - return 0; -} - -static int put_int32_as_uint16(QEMUFile *f, void *pv, size_t size, - const VMStateField *field, JSONWriter *vmdesc) -{ - int *v = pv; - qemu_put_be16(f, *v); - - return 0; -} - -static const VMStateInfo vmstate_hack_int32_as_uint16 = { - .name = "int32_as_uint16", - .get = get_int32_as_uint16, - .put = put_int32_as_uint16, -}; - -#define VMSTATE_UINT16_HACK(_f, _s) \ - VMSTATE_SINGLE(_f, _s, 0, vmstate_hack_int32_as_uint16, int32_t) - - -static const VMStateDescription vmstate_menelaus_tm = { - .name = "menelaus_tm", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT16_HACK(tm_sec, struct tm), - VMSTATE_UINT16_HACK(tm_min, struct tm), - VMSTATE_UINT16_HACK(tm_hour, struct tm), - VMSTATE_UINT16_HACK(tm_mday, struct tm), - VMSTATE_UINT16_HACK(tm_min, struct tm), - VMSTATE_UINT16_HACK(tm_year, struct tm), - VMSTATE_END_OF_LIST() - } -}; - -static int menelaus_pre_save(void *opaque) -{ - MenelausState *s = opaque; - /* Should be <= 1000 */ - s->rtc_next_vmstate = s->rtc.next - qemu_clock_get_ms(rtc_clock); - - return 0; -} - -static int menelaus_post_load(void *opaque, int version_id) -{ - MenelausState *s = opaque; - - 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 */ - menelaus_rtc_start(s); - return 0; -} - -static const VMStateDescription vmstate_menelaus = { - .name = "menelaus", - .version_id = 0, - .minimum_version_id = 0, - .pre_save = menelaus_pre_save, - .post_load = menelaus_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(firstbyte, MenelausState), - VMSTATE_UINT8(reg, MenelausState), - VMSTATE_UINT8_ARRAY(vcore, MenelausState, 5), - VMSTATE_UINT8_ARRAY(dcdc, MenelausState, 3), - VMSTATE_UINT8_ARRAY(ldo, MenelausState, 8), - VMSTATE_UINT8_ARRAY(sleep, MenelausState, 2), - VMSTATE_UINT8(osc, MenelausState), - VMSTATE_UINT8(detect, MenelausState), - VMSTATE_UINT16(mask, MenelausState), - VMSTATE_UINT16(status, MenelausState), - VMSTATE_UINT8(dir, MenelausState), - VMSTATE_UINT8(inputs, MenelausState), - VMSTATE_UINT8(outputs, MenelausState), - VMSTATE_UINT8(bbsms, MenelausState), - VMSTATE_UINT8_ARRAY(pull, MenelausState, 4), - VMSTATE_UINT8_ARRAY(mmc_ctrl, MenelausState, 3), - VMSTATE_UINT8(mmc_debounce, MenelausState), - VMSTATE_UINT8(rtc.ctrl, MenelausState), - VMSTATE_UINT16(rtc.comp, MenelausState), - VMSTATE_UINT16(rtc_next_vmstate, MenelausState), - VMSTATE_STRUCT(rtc.new, MenelausState, 0, vmstate_menelaus_tm, - struct tm), - VMSTATE_STRUCT(rtc.alm, MenelausState, 0, vmstate_menelaus_tm, - struct tm), - VMSTATE_UINT8(pwrbtn_state, MenelausState), - VMSTATE_I2C_SLAVE(parent_obj, MenelausState), - VMSTATE_END_OF_LIST() - } -}; - -static void twl92230_realize(DeviceState *dev, Error **errp) -{ - MenelausState *s = TWL92230(dev); - - s->rtc.hz_tm = timer_new_ms(rtc_clock, menelaus_rtc_hz, s); - /* Three output pins plus one interrupt pin. */ - qdev_init_gpio_out(dev, s->out, 4); - - /* Three input pins plus one power-button pin. */ - qdev_init_gpio_in(dev, menelaus_gpio_set, 4); - - menelaus_reset(I2C_SLAVE(dev)); -} - -static void twl92230_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - - dc->realize = twl92230_realize; - sc->event = menelaus_event; - sc->recv = menelaus_rx; - sc->send = menelaus_tx; - dc->vmsd = &vmstate_menelaus; -} - -static const TypeInfo twl92230_info = { - .name = TYPE_TWL92230, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(MenelausState), - .class_init = twl92230_class_init, -}; - -static void twl92230_register_types(void) -{ - type_register_static(&twl92230_info); -} - -type_init(twl92230_register_types) diff --git a/hw/rtc/xlnx-zynqmp-rtc.c b/hw/rtc/xlnx-zynqmp-rtc.c index 3e7d61a41c..f37df09cfb 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(), @@ -255,7 +255,7 @@ static void rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = rtc_reset; + device_class_set_legacy_reset(dc, rtc_reset); dc->vmsd = &vmstate_rtc; } diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig index 2b297c5a6a..aa9242d1ef 100644 --- a/hw/rx/Kconfig +++ b/hw/rx/Kconfig @@ -7,4 +7,7 @@ config RX62N_MCU config RX_GDBSIM bool + default y + depends on RX && FDT + select DEVICE_TREE select RX62N_MCU diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 47c17026c7..bb4746c556 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -20,6 +20,7 @@ #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" 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..82afdaa9dc 100644 --- a/hw/s390x/Kconfig +++ b/hw/s390x/Kconfig @@ -1,12 +1,18 @@ config S390_CCW_VIRTIO bool + default y + depends on S390X imply VIRTIO_PCI imply TERMINAL3270 imply VFIO_AP imply VFIO_CCW imply WDT_DIAG288 - select PCI + imply PCI_BRIDGE + 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..30f2fb486f 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -13,6 +13,10 @@ #include "ccw-device.h" #include "hw/qdev-properties.h" #include "qemu/module.h" +#include "ipl.h" +#include "qapi/visitor.h" +#include "qemu/ctype.h" +#include "qapi/error.h" static void ccw_device_refill_ids(CcwDevice *dev) { @@ -31,11 +35,52 @@ static void ccw_device_refill_ids(CcwDevice *dev) dev->subch_id.valid = true; } -static void ccw_device_realize(CcwDevice *dev, Error **errp) +static bool ccw_device_realize(CcwDevice *dev, Error **errp) { ccw_device_refill_ids(dev); + return true; } +static void ccw_device_get_loadparm(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CcwDevice *dev = CCW_DEVICE(obj); + char *str = g_strndup((char *) dev->loadparm, sizeof(dev->loadparm)); + + visit_type_str(v, name, &str, errp); + g_free(str); +} + +static void ccw_device_set_loadparm(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + CcwDevice *dev = CCW_DEVICE(obj); + char *val; + int index; + + index = object_property_get_int(obj, "bootindex", NULL); + + if (index < 0) { + error_setg(errp, "LOADPARM is only valid for boot devices!"); + } + + if (!visit_type_str(v, name, &val, errp)) { + return; + } + + s390_ipl_fmt_loadparm(dev->loadparm, val, errp); +} + +const PropertyInfo ccw_loadparm = { + .name = "ccw_loadparm", + .description = "Up to 8 chars in set of [A-Za-z0-9. ] to pass" + " to the guest loader/kernel", + .get = ccw_device_get_loadparm, + .set = ccw_device_set_loadparm, +}; + static Property ccw_device_properties[] = { DEFINE_PROP_CSS_DEV_ID("devno", CcwDevice, devno), DEFINE_PROP_CSS_DEV_ID_RO("dev_id", CcwDevice, dev_id), @@ -43,9 +88,9 @@ static Property ccw_device_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ccw_device_reset(DeviceState *d) +static void ccw_device_reset_hold(Object *obj, ResetType type) { - CcwDevice *ccw_dev = CCW_DEVICE(d); + CcwDevice *ccw_dev = CCW_DEVICE(obj); css_reset_sch(ccw_dev->sch); } @@ -54,11 +99,12 @@ static void ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCWDeviceClass *k = CCW_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); k->realize = ccw_device_realize; k->refill_ids = ccw_device_refill_ids; device_class_set_props(dc, ccw_device_properties); - dc->reset = ccw_device_reset; + rc->phases.hold = ccw_device_reset_hold; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; } @@ -66,7 +112,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/ccw-device.h b/hw/s390x/ccw-device.h index 6dff95225d..4439feb140 100644 --- a/hw/s390x/ccw-device.h +++ b/hw/s390x/ccw-device.h @@ -26,6 +26,8 @@ struct CcwDevice { CssDevId dev_id; /* The actual busid of the virtual subchannel. */ CssDevId subch_id; + /* If set, use this loadparm value when device is boot target */ + uint8_t loadparm[8]; }; typedef struct CcwDevice CcwDevice; @@ -36,7 +38,7 @@ extern const VMStateDescription vmstate_ccw_dev; struct CCWDeviceClass { DeviceClass parent_class; void (*unplug)(HotplugHandler *, DeviceState *, Error **); - void (*realize)(CcwDevice *, Error **); + bool (*realize)(CcwDevice *, Error **); void (*refill_ids)(CcwDevice *); }; @@ -49,4 +51,9 @@ static inline CcwDevice *to_ccw_dev_fast(DeviceState *d) OBJECT_DECLARE_TYPE(CcwDevice, CCWDeviceClass, CCW_DEVICE) +extern const PropertyInfo ccw_loadparm; + +#define DEFINE_PROP_CCW_LOADPARM(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, ccw_loadparm, typeof(uint8_t[8])) + #endif diff --git a/hw/s390x/cpu-topology.c b/hw/s390x/cpu-topology.c new file mode 100644 index 0000000000..7d4e1f5472 --- /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) +{ + S390CpuPolarization 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, + S390CpuEntitlement 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, S390CpuEntitlement 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..8657ff7bf4 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, ResetType type) { /* 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 95d1b3a3ce..b2d5327dbf 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -23,6 +23,8 @@ #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-ccw.h" +bool css_migration_enabled = true; + typedef struct CrwContainer { CRW crw; QTAILQ_ENTRY(CrwContainer) sibling; @@ -32,7 +34,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 +45,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 +61,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 +79,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 +94,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 +115,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 +129,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 +142,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 +154,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 +170,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), @@ -180,7 +182,7 @@ static const VMStateDescription vmstate_orb = { static bool vmstate_schdev_orb_needed(void *opaque) { - return css_migration_enabled(); + return css_migration_enabled; } static const VMStateDescription vmstate_schdev_orb = { @@ -188,7 +190,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 +209,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 +225,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 +266,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 +280,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 +295,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 +332,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), @@ -388,7 +390,7 @@ static int subch_dev_post_load(void *opaque, int version_id) css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s); } - if (css_migration_enabled()) { + if (css_migration_enabled) { /* No compat voodoo to do ;) */ return 0; } @@ -412,7 +414,9 @@ static int subch_dev_post_load(void *opaque, int version_id) void css_register_vmstate(void) { - vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); + if (css_migration_enabled) { + vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); + } } IndAddr *get_indicator(hwaddr ind_addr, int len) @@ -644,8 +648,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; diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index faa51aa4c7..2b0332c20e 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 @@ -468,7 +467,7 @@ static void init_event_facility_class(ObjectClass *klass, void *data) SCLPEventFacilityClass *k = EVENT_FACILITY_CLASS(dc); dc->realize = realize_event_facility; - dc->reset = reset_event_facility; + device_class_set_legacy_reset(dc, reset_event_facility); dc->vmsd = &vmstate_event_facility; set_bit(DEVICE_CATEGORY_MISC, dc->categories); k->command_handler = command_handler; @@ -524,16 +523,7 @@ static void register_types(void) type_init(register_types) -BusState *sclp_get_event_facility_bus(void) +BusState *sclp_get_event_facility_bus(SCLPEventFacility *ef) { - Object *busobj; - SCLPEventsBus *sbus; - - busobj = object_resolve_path_type("", TYPE_SCLP_EVENTS_BUS, NULL); - sbus = OBJECT_CHECK(SCLPEventsBus, busobj, TYPE_SCLP_EVENTS_BUS); - if (!sbus) { - return NULL; - } - - return &sbus->qbus; + return BUS(&ef->sbus); } diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 8612684d48..30734661ad 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" @@ -34,8 +34,8 @@ #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "qemu/ctype.h" #include "standard-headers/linux/virtio_ids.h" -#include "exec/exec-all.h" #define KERN_IMAGE_START 0x010000UL #define LINUX_MAGIC_ADDR 0x010008UL @@ -46,6 +46,7 @@ #define INITRD_PARM_START 0x010408UL #define PARMFILE_START 0x001000UL #define ZIPL_IMAGE_START 0x009000UL +#define BIOS_MAX_SIZE 0x300000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) static bool iplb_extended_needed(void *opaque) @@ -55,12 +56,19 @@ static bool iplb_extended_needed(void *opaque) return ipl->iplbext_migration; } +/* Place the IPLB chain immediately before the BIOS in memory */ +static uint64_t find_iplb_chain_addr(uint64_t bios_addr, uint16_t count) +{ + return (bios_addr & TARGET_PAGE_MASK) + - (count * sizeof(IplParameterBlock)); +} + static const VMStateDescription vmstate_iplb_extended = { .name = "ipl/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 +78,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 +94,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), @@ -145,7 +153,14 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * even if an external kernel has been defined. */ if (!ipl->kernel || ipl->enforce_bios) { - uint64_t fwbase = (MIN(ms->ram_size, 0x80000000U) - 0x200000) & ~0xffffUL; + uint64_t fwbase; + + if (ms->ram_size < BIOS_MAX_SIZE) { + error_setg(errp, "not enough RAM to load the BIOS file"); + return; + } + + fwbase = (MIN(ms->ram_size, 0x80000000U) - BIOS_MAX_SIZE) & ~0xffffUL; bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->firmware); if (bios_filename == NULL) { @@ -253,8 +268,8 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) */ romptr = rom_ptr(INITRD_PARM_START, 16); if (romptr) { - stq_p(romptr, initrd_offset); - stq_p(romptr + 1, initrd_size); + stq_be_p(romptr, initrd_offset); + stq_be_p(romptr + 1, initrd_size); } } } @@ -281,7 +296,6 @@ static Property s390_ipl_properties[] = { DEFINE_PROP_STRING("initrd", S390IPLState, initrd), DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline), DEFINE_PROP_STRING("firmware", S390IPLState, firmware), - DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw), DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false), DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration, true), @@ -391,174 +405,162 @@ static CcwDevice *s390_get_ccw_device(DeviceState *dev_st, int *devtype) return ccw_dev; } -static bool s390_gen_initial_iplb(S390IPLState *ipl) +static uint64_t s390_ipl_map_iplb_chain(IplParameterBlock *iplb_chain) +{ + S390IPLState *ipl = get_ipl_device(); + uint16_t count = be16_to_cpu(ipl->qipl.chain_len); + uint64_t len = sizeof(IplParameterBlock) * count; + uint64_t chain_addr = find_iplb_chain_addr(ipl->bios_start_addr, count); + + cpu_physical_memory_write(chain_addr, iplb_chain, len); + return chain_addr; +} + +void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp) +{ + /* Initialize the loadparm with spaces */ + memset(loadparm, ' ', LOADPARM_LEN); + qdev_prop_sanitize_s390x_loadparm(loadparm, str, errp); +} + +void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp) +{ + int i; + + /* Initialize the loadparm with EBCDIC spaces (0x40) */ + memset(ebcdic_lp, '@', LOADPARM_LEN); + for (i = 0; i < LOADPARM_LEN && ascii_lp[i]; i++) { + ebcdic_lp[i] = ascii2ebcdic[(uint8_t) ascii_lp[i]]; + } +} + +static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb) { - DeviceState *dev_st; CcwDevice *ccw_dev = NULL; SCSIDevice *sd; int devtype; - - dev_st = get_boot_device(0); - if (dev_st) { - ccw_dev = s390_get_ccw_device(dev_st, &devtype); - } + uint8_t *lp; + g_autofree void *scsi_lp = NULL; /* * Currently allow IPL only from CCW devices. */ + ccw_dev = s390_get_ccw_device(dev_st, &devtype); if (ccw_dev) { + lp = ccw_dev->loadparm; + switch (devtype) { case CCW_DEVTYPE_SCSI: sd = SCSI_DEVICE(dev_st); - ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); - ipl->iplb.blk0_len = + scsi_lp = object_property_get_str(OBJECT(sd), "loadparm", NULL); + if (scsi_lp && strlen(scsi_lp) > 0) { + lp = scsi_lp; + } + iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN); + iplb->blk0_len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN); - ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI; - ipl->iplb.scsi.lun = cpu_to_be32(sd->lun); - ipl->iplb.scsi.target = cpu_to_be16(sd->id); - ipl->iplb.scsi.channel = cpu_to_be16(sd->channel); - ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno); - ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3; + iplb->pbt = S390_IPL_TYPE_QEMU_SCSI; + iplb->scsi.lun = cpu_to_be32(sd->lun); + iplb->scsi.target = cpu_to_be16(sd->id); + iplb->scsi.channel = cpu_to_be16(sd->channel); + iplb->scsi.devno = cpu_to_be16(ccw_dev->sch->devno); + iplb->scsi.ssid = ccw_dev->sch->ssid & 3; break; case CCW_DEVTYPE_VFIO: - ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); - ipl->iplb.pbt = S390_IPL_TYPE_CCW; - ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); - ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; + iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); + iplb->pbt = S390_IPL_TYPE_CCW; + iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno); + iplb->ccw.ssid = ccw_dev->sch->ssid & 3; break; case CCW_DEVTYPE_VIRTIO_NET: - ipl->netboot = true; - /* Fall through to CCW_DEVTYPE_VIRTIO case */ case CCW_DEVTYPE_VIRTIO: - ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); - ipl->iplb.blk0_len = + iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN); + iplb->blk0_len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN); - ipl->iplb.pbt = S390_IPL_TYPE_CCW; - ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno); - ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3; + iplb->pbt = S390_IPL_TYPE_CCW; + iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno); + iplb->ccw.ssid = ccw_dev->sch->ssid & 3; break; } - if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) { - ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID; + /* If the device loadparm is empty use the global machine loadparm */ + if (memcmp(lp, NO_LOADPARM, 8) == 0) { + lp = S390_CCW_MACHINE(qdev_get_machine())->loadparm; } + s390_ipl_convert_loadparm((char *)lp, iplb->loadparm); + iplb->flags |= DIAG308_FLAGS_LP_VALID; + return true; } return false; } -int s390_ipl_set_loadparm(uint8_t *loadparm) +void s390_rebuild_iplb(uint16_t dev_index, IplParameterBlock *iplb) { - MachineState *machine = MACHINE(qdev_get_machine()); - char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL); - - if (lp) { - int i; - - /* lp is an uppercase string without leading/embedded spaces */ - for (i = 0; i < 8 && lp[i]; i++) { - loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; - } - - if (i < 8) { - memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */ - } - - g_free(lp); - return 0; - } - - return -1; -} - -static int load_netboot_image(Error **errp) -{ - MachineState *ms = MACHINE(qdev_get_machine()); S390IPLState *ipl = get_ipl_device(); - char *netboot_filename; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *mr = NULL; - void *ram_ptr = NULL; - int img_size = -1; + uint16_t index; + index = ipl->rebuilt_iplb ? ipl->iplb_index : dev_index; - mr = memory_region_find(sysmem, 0, 1).mr; - if (!mr) { - error_setg(errp, "Failed to find memory region at address 0"); - return -1; - } - - ram_ptr = memory_region_get_ram_ptr(mr); - if (!ram_ptr) { - error_setg(errp, "No RAM found"); - goto unref_mr; - } - - netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw); - if (netboot_filename == NULL) { - error_setg(errp, "Could not find network bootloader '%s'", - ipl->netboot_fw); - goto unref_mr; - } - - img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL, - &ipl->start_addr, - NULL, NULL, NULL, 1, EM_S390, 0, 0, NULL, - false); - - if (img_size < 0) { - img_size = load_image_size(netboot_filename, ram_ptr, ms->ram_size); - ipl->start_addr = KERN_IMAGE_START; - } - - if (img_size < 0) { - error_setg(errp, "Failed to load network bootloader"); - } - - g_free(netboot_filename); - -unref_mr: - memory_region_unref(mr); - return img_size; + ipl->rebuilt_iplb = s390_build_iplb(get_boot_device(index), iplb); + ipl->iplb_index = index; } -static bool is_virtio_ccw_device_of_type(IplParameterBlock *iplb, - int virtio_id) +static bool s390_init_all_iplbs(S390IPLState *ipl) { - uint8_t cssid; - uint8_t ssid; - uint16_t devno; - uint16_t schid; - SubchDev *sch = NULL; + int iplb_num = 0; + IplParameterBlock iplb_chain[7]; + DeviceState *dev_st = get_boot_device(0); + Object *machine = qdev_get_machine(); - if (iplb->pbt != S390_IPL_TYPE_CCW) { + /* + * Parse the boot devices. Generate an IPLB for only the first boot device + * which will later be set with DIAG308. + */ + if (!dev_st) { + ipl->qipl.chain_len = 0; return false; } - devno = be16_to_cpu(iplb->ccw.devno); - ssid = iplb->ccw.ssid & 3; - - for (schid = 0; schid < MAX_SCHID; schid++) { - for (cssid = 0; cssid < MAX_CSSID; cssid++) { - sch = css_find_subch(1, cssid, ssid, schid); - - if (sch && sch->devno == devno) { - return sch->id.cu_model == virtio_id; - } - } + /* If no machine loadparm was defined fill it with spaces */ + if (memcmp(S390_CCW_MACHINE(machine)->loadparm, NO_LOADPARM, 8) == 0) { + object_property_set_str(machine, "loadparm", " ", NULL); } - return false; -} -static bool is_virtio_net_device(IplParameterBlock *iplb) -{ - return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_NET); -} + iplb_num = 1; + s390_build_iplb(dev_st, &ipl->iplb); -static bool is_virtio_scsi_device(IplParameterBlock *iplb) -{ - return is_virtio_ccw_device_of_type(iplb, VIRTIO_ID_SCSI); + /* Index any fallback boot devices */ + while (get_boot_device(iplb_num)) { + iplb_num++; + } + + if (iplb_num > MAX_BOOT_DEVS) { + warn_report("Excess boot devices defined! %d boot devices found, " + "but only the first %d will be considered.", + iplb_num, MAX_BOOT_DEVS); + + iplb_num = MAX_BOOT_DEVS; + } + + ipl->qipl.chain_len = cpu_to_be16(iplb_num - 1); + + /* + * Build fallback IPLBs for any boot devices above index 0, up to a + * maximum amount as defined in ipl.h + */ + if (iplb_num > 1) { + /* Start at 1 because the IPLB for boot index 0 is not chained */ + for (int i = 1; i < iplb_num; i++) { + dev_st = get_boot_device(i); + s390_build_iplb(dev_st, &iplb_chain[i - 1]); + } + + ipl->qipl.next_iplb = cpu_to_be64(s390_ipl_map_iplb_chain(iplb_chain)); + } + + return iplb_num; } static void update_machine_ipl_properties(IplParameterBlock *iplb) @@ -578,7 +580,7 @@ static void update_machine_ipl_properties(IplParameterBlock *iplb) ascii_loadparm[i] = 0; object_property_set_str(machine, "loadparm", ascii_loadparm, &err); } else { - object_property_set_str(machine, "loadparm", "", &err); + object_property_set_str(machine, "loadparm", " ", &err); } if (err) { warn_report_err(err); @@ -600,7 +602,7 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb) ipl->iplb = *iplb; ipl->iplb_valid = true; } - ipl->netboot = is_virtio_net_device(iplb); + update_machine_ipl_properties(iplb); } @@ -627,32 +629,14 @@ IplParameterBlock *s390_ipl_get_iplb(void) void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type) { S390IPLState *ipl = get_ipl_device(); - if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) { /* use CPU 0 for full resets */ ipl->reset_cpu_index = 0; } else { ipl->reset_cpu_index = cs->cpu_index; } + ipl->reset_type = reset_type; - - if (reset_type == S390_RESET_REIPL && - ipl->iplb_valid && - !ipl->netboot && - ipl->iplb.pbt == S390_IPL_TYPE_CCW && - is_virtio_scsi_device(&ipl->iplb)) { - CcwDevice *ccw_dev = s390_get_ccw_device(get_boot_device(0), NULL); - - if (ccw_dev && - cpu_to_be16(ccw_dev->sch->devno) == ipl->iplb.ccw.devno && - (ccw_dev->sch->ssid & 3) == ipl->iplb.ccw.ssid) { - /* - * this is the original boot device's SCSI - * so restore IPL parameter info from it - */ - ipl->iplb_valid = s390_gen_initial_iplb(ipl); - } - } if (reset_type == S390_RESET_MODIFIED_CLEAR || reset_type == S390_RESET_LOAD_NORMAL || reset_type == S390_RESET_PV) { @@ -703,7 +687,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 +696,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; } @@ -745,13 +728,11 @@ void s390_ipl_prepare_cpu(S390CPU *cpu) if (!ipl->kernel || ipl->iplb_valid) { cpu->env.psw.addr = ipl->bios_start_addr; if (!ipl->iplb_valid) { - ipl->iplb_valid = s390_gen_initial_iplb(ipl); + ipl->iplb_valid = s390_init_all_iplbs(ipl); + } else { + ipl->qipl.chain_len = 0; } } - if (ipl->netboot) { - load_netboot_image(&error_fatal); - ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr); - } s390_ipl_set_boot_menu(ipl); s390_ipl_prepare_qipl(cpu); } @@ -772,7 +753,7 @@ static void s390_ipl_class_init(ObjectClass *klass, void *data) dc->realize = s390_ipl_realize; device_class_set_props(dc, s390_ipl_properties); - dc->reset = s390_ipl_reset; + device_class_set_legacy_reset(dc, s390_ipl_reset); dc->vmsd = &vmstate_ipl; set_bit(DEVICE_CATEGORY_MISC, dc->categories); /* Reason: Loads the ROMs and thus can only be used one time - internally */ diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 7fc86e7905..d7d0b7bfd2 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -16,98 +16,17 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "hw/qdev-core.h" +#include "hw/s390x/ipl/qipl.h" #include "qom/object.h" -struct IPLBlockPVComp { - uint64_t tweak_pref; - uint64_t addr; - uint64_t size; -} QEMU_PACKED; -typedef struct IPLBlockPVComp IPLBlockPVComp; - -struct IPLBlockPV { - uint8_t reserved18[87]; /* 0x18 */ - uint8_t version; /* 0x6f */ - uint32_t reserved70; /* 0x70 */ - uint32_t num_comp; /* 0x74 */ - uint64_t pv_header_addr; /* 0x78 */ - uint64_t pv_header_len; /* 0x80 */ - struct IPLBlockPVComp components[0]; -} QEMU_PACKED; -typedef struct IPLBlockPV IPLBlockPV; - -struct IplBlockCcw { - uint8_t reserved0[85]; - uint8_t ssid; - uint16_t devno; - uint8_t vm_flags; - uint8_t reserved3[3]; - uint32_t vm_parm_len; - uint8_t nss_name[8]; - uint8_t vm_parm[64]; - uint8_t reserved4[8]; -} QEMU_PACKED; -typedef struct IplBlockCcw IplBlockCcw; - -struct IplBlockFcp { - uint8_t reserved1[305 - 1]; - uint8_t opt; - uint8_t reserved2[3]; - uint16_t reserved3; - uint16_t devno; - uint8_t reserved4[4]; - uint64_t wwpn; - uint64_t lun; - uint32_t bootprog; - uint8_t reserved5[12]; - uint64_t br_lba; - uint32_t scp_data_len; - uint8_t reserved6[260]; - uint8_t scp_data[0]; -} QEMU_PACKED; -typedef struct IplBlockFcp IplBlockFcp; - -struct IplBlockQemuScsi { - uint32_t lun; - uint16_t target; - uint16_t channel; - uint8_t reserved0[77]; - uint8_t ssid; - uint16_t devno; -} QEMU_PACKED; -typedef struct IplBlockQemuScsi IplBlockQemuScsi; - #define DIAG308_FLAGS_LP_VALID 0x80 +#define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */ -union IplParameterBlock { - struct { - uint32_t len; - uint8_t reserved0[3]; - uint8_t version; - uint32_t blk0_len; - uint8_t pbt; - uint8_t flags; - uint16_t reserved01; - uint8_t loadparm[8]; - union { - IplBlockCcw ccw; - IplBlockFcp fcp; - IPLBlockPV pv; - IplBlockQemuScsi scsi; - }; - } QEMU_PACKED; - struct { - uint8_t reserved1[110]; - uint16_t devno; - uint8_t reserved2[88]; - uint8_t reserved_ext[4096 - 200]; - } QEMU_PACKED; -} QEMU_PACKED; -typedef union IplParameterBlock IplParameterBlock; - -int s390_ipl_set_loadparm(uint8_t *loadparm); +void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp); +void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp); +void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb); 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); @@ -131,27 +50,6 @@ void s390_ipl_clear_reset_request(void); #define QIPL_FLAG_BM_OPTS_CMD 0x80 #define QIPL_FLAG_BM_OPTS_ZIPL 0x40 -/* - * The QEMU IPL Parameters will be stored at absolute address - * 204 (0xcc) which means it is 32-bit word aligned but not - * double-word aligned. - * Placement of data fields in this area must account for - * their alignment needs. E.g., netboot_start_address must - * 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 definition - * in pc-bios/s390-ccw/iplb.h. - */ -struct QemuIplParameters { - uint8_t qipl_flags; - uint8_t reserved1[3]; - uint64_t netboot_start_addr; - uint32_t boot_menu_timeout; - uint8_t reserved2[12]; -} QEMU_PACKED; -typedef struct QemuIplParameters QemuIplParameters; - #define TYPE_S390_IPL "s390-ipl" OBJECT_DECLARE_SIMPLE_TYPE(S390IPLState, S390_IPL) @@ -168,7 +66,8 @@ struct S390IPLState { bool enforce_bios; bool iplb_valid; bool iplb_valid_pv; - bool netboot; + bool rebuilt_iplb; + uint16_t iplb_index; /* reset related properties don't have to be migrated or reset */ enum s390_reset reset_type; int reset_cpu_index; @@ -178,7 +77,6 @@ struct S390IPLState { char *initrd; char *cmdline; char *firmware; - char *netboot_fw; uint8_t cssid; uint8_t ssid; uint16_t devno; @@ -276,11 +174,14 @@ static inline bool iplb_valid_pv(IplParameterBlock *iplb) static inline bool iplb_valid(IplParameterBlock *iplb) { + uint32_t len = be32_to_cpu(iplb->len); + switch (iplb->pbt) { case S390_IPL_TYPE_FCP: - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_FCP_LEN; + return len >= S390_IPLB_MIN_FCP_LEN; case S390_IPL_TYPE_CCW: - return be32_to_cpu(iplb->len) >= S390_IPLB_MIN_CCW_LEN; + return len >= S390_IPLB_MIN_CCW_LEN; + case S390_IPL_TYPE_QEMU_SCSI: default: return false; } diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index f291016fee..482fd13420 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -22,8 +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 8dfe92d8df..0000000000 --- a/hw/s390x/pv.c +++ /dev/null @@ -1,286 +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" -#include "target/s390x/kvm/kvm_s390x.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) -{ - 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_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); -} - -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; -} - -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; -}; - -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 e2d86d96e7..3c09750550 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -71,54 +71,56 @@ IOInstEnding s390_ccw_store(SubchDev *sch) return ret; } -static void s390_ccw_get_dev_info(S390CCWDevice *cdev, +static bool s390_ccw_get_dev_info(S390CCWDevice *cdev, char *sysfsdev, 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"); error_append_hint(errp, "Use -device vfio-ccw,sysfsdev=PATH_TO_DEVICE\n"); - return; + return false; } if (!realpath(sysfsdev, dev_path)) { error_setg_errno(errp, errno, "Host device '%s' not found", sysfsdev); - return; + return false; } 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; + return false; } cdev->hostid.cssid = cssid; cdev->hostid.ssid = ssid; cdev->hostid.devid = devid; cdev->hostid.valid = true; + return true; } -static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) +static bool s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) { CcwDevice *ccw_dev = CCW_DEVICE(cdev); CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); DeviceState *parent = DEVICE(ccw_dev); SubchDev *sch; int ret; - Error *err = NULL; - s390_ccw_get_dev_info(cdev, sysfsdev, &err); - if (err) { - goto out_err_propagate; + if (!s390_ccw_get_dev_info(cdev, sysfsdev, errp)) { + return false; } - sch = css_create_sch(ccw_dev->devno, &err); + sch = css_create_sch(ccw_dev->devno, errp); if (!sch) { goto out_mdevid_free; } @@ -129,19 +131,18 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) ccw_dev->sch = sch; ret = css_sch_build_schib(sch, &cdev->hostid); if (ret) { - error_setg_errno(&err, -ret, "%s: Failed to build initial schib", + error_setg_errno(errp, -ret, "%s: Failed to build initial schib", __func__); goto out_err; } - ck->realize(ccw_dev, &err); - if (err) { + if (!ck->realize(ccw_dev, errp)) { goto out_err; } css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, parent->hotplugged, 1); - return; + return true; out_err: css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); @@ -149,8 +150,7 @@ out_err: g_free(sch); out_mdevid_free: g_free(cdev->mdevid); -out_err_propagate: - error_propagate(errp, err); + return false; } static void s390_ccw_unrealize(S390CCWDevice *cdev) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 977e7daa15..40b2567aa7 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -24,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) { @@ -130,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; } @@ -150,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); @@ -177,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; } @@ -551,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; @@ -639,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; @@ -670,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; @@ -821,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)); @@ -1045,7 +1054,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *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)); @@ -1098,7 +1107,7 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } } else { - DPRINTF("zPCI interpretation facilities missing.\n"); + trace_s390_pcihost("zPCI interpretation missing"); pbdev->interp = false; pbdev->forwarding_assist = false; } @@ -1111,6 +1120,11 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, 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 */ @@ -1256,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); @@ -1292,7 +1323,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); - dc->reset = s390_pcihost_reset; + device_class_set_legacy_reset(dc, s390_pcihost_reset); dc->realize = s390_pcihost_realize; dc->unrealize = s390_pcihost_unrealize; hc->pre_plug = s390_pcihost_pre_plug; @@ -1475,7 +1506,7 @@ static void s390_pci_device_class_init(ObjectClass *klass, void *data) dc->desc = "zpci device"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->reset = s390_pci_device_reset; + device_class_set_legacy_reset(dc, s390_pci_device_reset); dc->bus_type = TYPE_S390_PCI_BUS; dc->realize = s390_pci_device_realize; device_class_set_props(dc, s390_pci_device_properties); diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 7cc4bcf850..41655082da 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -13,25 +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) { @@ -63,26 +55,26 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) uint64_t resume_token; rc = 0; - if (lduw_p(&rrb->request.hdr.len) != 32) { + if (lduw_be_p(&rrb->request.hdr.len) != 32) { res_code = CLP_RC_LEN; rc = -EINVAL; goto out; } - if ((ldl_p(&rrb->request.fmt) & CLP_MASK_FMT) != 0) { + if ((ldl_be_p(&rrb->request.fmt) & CLP_MASK_FMT) != 0) { res_code = CLP_RC_FMT; rc = -EINVAL; goto out; } - if ((ldl_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || - ldq_p(&rrb->request.reserved1) != 0) { + if ((ldl_be_p(&rrb->request.fmt) & ~CLP_MASK_FMT) != 0 || + ldq_be_p(&rrb->request.reserved1) != 0) { res_code = CLP_RC_RESNOT0; rc = -EINVAL; goto out; } - resume_token = ldq_p(&rrb->request.resume_token); + resume_token = ldq_be_p(&rrb->request.resume_token); if (resume_token) { pbdev = s390_pci_find_dev_by_idx(s, resume_token); @@ -95,13 +87,13 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) pbdev = s390_pci_find_next_avail_dev(s, NULL); } - if (lduw_p(&rrb->response.hdr.len) < 48) { + if (lduw_be_p(&rrb->response.hdr.len) < 48) { res_code = CLP_RC_8K; rc = -EINVAL; goto out; } - initial_l2 = lduw_p(&rrb->response.hdr.len); + initial_l2 = lduw_be_p(&rrb->response.hdr.len); if ((initial_l2 - LIST_PCI_HDR_LEN) % sizeof(ClpFhListEntry) != 0) { res_code = CLP_RC_LEN; @@ -110,34 +102,33 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) goto out; } - stl_p(&rrb->response.fmt, 0); - stq_p(&rrb->response.reserved1, 0); - stl_p(&rrb->response.mdd, FH_MASK_SHM); - stw_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); + stl_be_p(&rrb->response.fmt, 0); + stq_be_p(&rrb->response.reserved1, 0); + stl_be_p(&rrb->response.mdd, FH_MASK_SHM); + stw_be_p(&rrb->response.max_fn, PCI_MAX_FUNCTIONS); rrb->response.flags = UID_CHECKING_ENABLED; rrb->response.entry_size = sizeof(ClpFhListEntry); i = 0; g_l2 = LIST_PCI_HDR_LEN; while (g_l2 < initial_l2 && pbdev) { - stw_p(&rrb->response.fh_list[i].device_id, + stw_be_p(&rrb->response.fh_list[i].device_id, pci_get_word(pbdev->pdev->config + PCI_DEVICE_ID)); - stw_p(&rrb->response.fh_list[i].vendor_id, + stw_be_p(&rrb->response.fh_list[i].vendor_id, pci_get_word(pbdev->pdev->config + PCI_VENDOR_ID)); /* Ignore RESERVED devices. */ - stl_p(&rrb->response.fh_list[i].config, + stl_be_p(&rrb->response.fh_list[i].config, pbdev->state == ZPCI_FS_STANDBY ? 0 : 1 << 31); - stl_p(&rrb->response.fh_list[i].fid, pbdev->fid); - stl_p(&rrb->response.fh_list[i].fh, pbdev->fh); + stl_be_p(&rrb->response.fh_list[i].fid, pbdev->fid); + stl_be_p(&rrb->response.fh_list[i].fh, pbdev->fh); 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, - 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), - ldl_p(&rrb->response.fh_list[i].fh)); + trace_s390_pci_list_entry(g_l2, + lduw_be_p(&rrb->response.fh_list[i].vendor_id), + lduw_be_p(&rrb->response.fh_list[i].device_id), + ldl_be_p(&rrb->response.fh_list[i].fid), + ldl_be_p(&rrb->response.fh_list[i].fh)); pbdev = s390_pci_find_next_avail_dev(s, pbdev); i++; } @@ -147,13 +138,13 @@ static int list_pci(ClpReqRspListPci *rrb, uint8_t *cc) } else { resume_token = pbdev->fh & FH_MASK_INDEX; } - stq_p(&rrb->response.resume_token, resume_token); - stw_p(&rrb->response.hdr.len, g_l2); - stw_p(&rrb->response.hdr.rsp, CLP_RC_OK); + stq_be_p(&rrb->response.resume_token, resume_token); + stw_be_p(&rrb->response.hdr.len, g_l2); + stw_be_p(&rrb->response.hdr.rsp, CLP_RC_OK); out: if (rc) { - DPRINTF("list pci failed rc 0x%x\n", rc); - stw_p(&rrb->response.hdr.rsp, res_code); + trace_s390_pci_list(rc); + stw_be_p(&rrb->response.hdr.rsp, res_code); } return rc; } @@ -181,7 +172,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) return 0; } reqh = (ClpReqHdr *)buffer; - req_len = lduw_p(&reqh->len); + req_len = lduw_be_p(&reqh->len); if (req_len < 16 || req_len > 8184 || (req_len % 8 != 0)) { s390_program_interrupt(env, PGM_OPERAND, ra); return 0; @@ -193,7 +184,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) return 0; } resh = (ClpRspHdr *)(buffer + req_len); - res_len = lduw_p(&resh->len); + res_len = lduw_be_p(&resh->len); if (res_len < 8 || res_len > 8176 || (res_len % 8 != 0)) { s390_program_interrupt(env, PGM_OPERAND, ra); return 0; @@ -210,11 +201,11 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) } if (req_len != 32) { - stw_p(&resh->rsp, CLP_RC_LEN); + stw_be_p(&resh->rsp, CLP_RC_LEN); goto out; } - switch (lduw_p(&reqh->cmd)) { + switch (lduw_be_p(&reqh->cmd)) { case CLP_LIST_PCI: { ClpReqRspListPci *rrb = (ClpReqRspListPci *)buffer; list_pci(rrb, &cc); @@ -224,9 +215,9 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) ClpReqSetPci *reqsetpci = (ClpReqSetPci *)reqh; ClpRspSetPci *ressetpci = (ClpRspSetPci *)resh; - pbdev = s390_pci_find_dev_by_fh(s, ldl_p(&reqsetpci->fh)); + pbdev = s390_pci_find_dev_by_fh(s, ldl_be_p(&reqsetpci->fh)); if (!pbdev) { - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FH); goto out; } @@ -234,17 +225,17 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) case CLP_SET_ENABLE_PCI_FN: switch (reqsetpci->ndas) { case 0: - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_DMAAS); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_DMAAS); goto out; case 1: break; default: - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_RES); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_RES); goto out; } if (pbdev->fh & FH_MASK_ENABLE) { - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); goto out; } @@ -258,29 +249,29 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) /* 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); + stw_be_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); - stw_p(&ressetpci->hdr.rsp, CLP_RC_OK); + stl_be_p(&ressetpci->fh, pbdev->fh); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; case CLP_SET_DISABLE_PCI_FN: if (!(pbdev->fh & FH_MASK_ENABLE)) { - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + stw_be_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); + stl_be_p(&ressetpci->fh, pbdev->fh); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_OK); break; default: - DPRINTF("unknown set pci command\n"); - stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); + trace_s390_pci_unknown("set-pci", reqsetpci->oc); + stw_be_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP); break; } break; @@ -289,23 +280,23 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) ClpReqQueryPci *reqquery = (ClpReqQueryPci *)reqh; ClpRspQueryPci *resquery = (ClpRspQueryPci *)resh; - pbdev = s390_pci_find_dev_by_fh(s, ldl_p(&reqquery->fh)); + pbdev = s390_pci_find_dev_by_fh(s, ldl_be_p(&reqquery->fh)); if (!pbdev) { - DPRINTF("query pci no pci dev\n"); - stw_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH); + trace_s390_pci_nodev("query", ldl_be_p(&reqquery->fh)); + stw_be_p(&resquery->hdr.rsp, CLP_RC_SETPCIFN_FH); goto out; } - stq_p(&resquery->sdma, pbdev->zpci_fn.sdma); - stq_p(&resquery->edma, pbdev->zpci_fn.edma); - stw_p(&resquery->pchid, pbdev->zpci_fn.pchid); - stw_p(&resquery->vfn, pbdev->zpci_fn.vfn); + stq_be_p(&resquery->sdma, pbdev->zpci_fn.sdma); + stq_be_p(&resquery->edma, pbdev->zpci_fn.edma); + stw_be_p(&resquery->pchid, pbdev->zpci_fn.pchid); + stw_be_p(&resquery->vfn, pbdev->zpci_fn.vfn); resquery->flags = pbdev->zpci_fn.flags; resquery->pfgid = pbdev->zpci_fn.pfgid; resquery->pft = pbdev->zpci_fn.pft; resquery->fmbl = pbdev->zpci_fn.fmbl; - stl_p(&resquery->fid, pbdev->zpci_fn.fid); - stl_p(&resquery->uid, pbdev->zpci_fn.uid); + stl_be_p(&resquery->fid, pbdev->zpci_fn.fid); + stl_be_p(&resquery->uid, pbdev->zpci_fn.uid); memcpy(resquery->pfip, pbdev->zpci_fn.pfip, CLP_PFIP_NR_SEGMENTS); memcpy(resquery->util_str, pbdev->zpci_fn.util_str, CLP_UTIL_STR_LEN); @@ -313,16 +304,16 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) uint32_t data = pci_get_long(pbdev->pdev->config + PCI_BASE_ADDRESS_0 + (i * 4)); - stl_p(&resquery->bar[i], data); + stl_be_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, - ldl_p(&resquery->bar[i]), + trace_s390_pci_bar(i, + ldl_be_p(&resquery->bar[i]), pbdev->pdev->io_regions[i].size, resquery->bar_size[i]); } - stw_p(&resquery->hdr.rsp, CLP_RC_OK); + stw_be_p(&resquery->hdr.rsp, CLP_RC_OK); break; } case CLP_QUERY_PCI_FNGRP: { @@ -335,23 +326,23 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) if (!group) { /* We do not allow access to unknown groups */ /* The group must have been obtained with a vfio device */ - stw_p(&resgrp->hdr.rsp, CLP_RC_QUERYPCIFG_PFGID); + stw_be_p(&resgrp->hdr.rsp, CLP_RC_QUERYPCIFG_PFGID); goto out; } resgrp->fr = group->zpci_group.fr; - stq_p(&resgrp->dasm, group->zpci_group.dasm); - stq_p(&resgrp->msia, group->zpci_group.msia); - stw_p(&resgrp->mui, group->zpci_group.mui); - stw_p(&resgrp->i, group->zpci_group.i); - stw_p(&resgrp->maxstbl, group->zpci_group.maxstbl); + stq_be_p(&resgrp->dasm, group->zpci_group.dasm); + stq_be_p(&resgrp->msia, group->zpci_group.msia); + stw_be_p(&resgrp->mui, group->zpci_group.mui); + stw_be_p(&resgrp->i, group->zpci_group.i); + stw_be_p(&resgrp->maxstbl, group->zpci_group.maxstbl); resgrp->version = group->zpci_group.version; resgrp->dtsm = group->zpci_group.dtsm; - stw_p(&resgrp->hdr.rsp, CLP_RC_OK); + stw_be_p(&resgrp->hdr.rsp, CLP_RC_OK); break; } default: - DPRINTF("unknown clp command\n"); - stw_p(&resh->rsp, CLP_RC_CMD); + trace_s390_pci_unknown("clp", lduw_be_p(&reqh->cmd)); + stw_be_p(&resh->rsp, CLP_RC_CMD); break; } @@ -458,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; } @@ -499,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; @@ -558,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; } @@ -607,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; @@ -640,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 && @@ -663,15 +656,44 @@ 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; @@ -697,7 +719,7 @@ int rpcit_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("rpcit no pci dev\n"); + trace_s390_pci_nodev("rpcit", fh); setcc(cpu, ZPCI_PCI_LS_INVAL_HANDLE); return 0; } @@ -742,6 +764,21 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) 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) { if (dma_avail > 0 || entry.perm == IOMMU_NONE) { @@ -759,6 +796,11 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) } } } + 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: @@ -811,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; } @@ -827,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; @@ -872,7 +914,7 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, for (i = 0; i < len / 8; i++) { result = memory_region_dispatch_write(mr, offset + i * 8, - ldq_p(buffer + i * 8), + ldq_be_p(buffer + i * 8), MO_64, MEMTXATTRS_UNSPECIFIED); if (result != MEMTX_OK) { s390_program_interrupt(env, PGM_OPERAND, ra); @@ -893,13 +935,13 @@ specification_error: static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) { int ret, len; - uint8_t isc = FIB_DATA_ISC(ldl_p(&fib.data)); + uint8_t isc = FIB_DATA_ISC(ldl_be_p(&fib.data)); pbdev->routes.adapter.adapter_id = css_get_adapter_id( CSS_IO_ADAPTER_PCI, isc); - pbdev->summary_ind = get_indicator(ldq_p(&fib.aisb), sizeof(uint64_t)); - len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_p(&fib.data))) * sizeof(unsigned long); - pbdev->indicator = get_indicator(ldq_p(&fib.aibv), len); + pbdev->summary_ind = get_indicator(ldq_be_p(&fib.aisb), sizeof(uint64_t)); + len = BITS_TO_LONGS(FIB_DATA_NOI(ldl_be_p(&fib.data))) * sizeof(unsigned long); + pbdev->indicator = get_indicator(ldq_be_p(&fib.aibv), len); ret = map_indicator(&pbdev->routes.adapter, pbdev->summary_ind); if (ret) { @@ -911,15 +953,15 @@ static int reg_irqs(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib) goto out; } - pbdev->routes.adapter.summary_addr = ldq_p(&fib.aisb); - pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_p(&fib.data)); - pbdev->routes.adapter.ind_addr = ldq_p(&fib.aibv); - pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_p(&fib.data)); + pbdev->routes.adapter.summary_addr = ldq_be_p(&fib.aisb); + pbdev->routes.adapter.summary_offset = FIB_DATA_AISBO(ldl_be_p(&fib.data)); + pbdev->routes.adapter.ind_addr = ldq_be_p(&fib.aibv); + pbdev->routes.adapter.ind_offset = FIB_DATA_AIBVO(ldl_be_p(&fib.data)); pbdev->isc = isc; - pbdev->noi = FIB_DATA_NOI(ldl_p(&fib.data)); - pbdev->sum = FIB_DATA_SUM(ldl_p(&fib.data)); + pbdev->noi = FIB_DATA_NOI(ldl_be_p(&fib.data)); + pbdev->sum = FIB_DATA_SUM(ldl_be_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); @@ -944,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; } @@ -952,9 +994,9 @@ static int reg_ioat(CPUS390XState *env, S390PCIBusDevice *pbdev, ZpciFib fib, uintptr_t ra) { S390PCIIOMMU *iommu = pbdev->iommu; - uint64_t pba = ldq_p(&fib.pba); - uint64_t pal = ldq_p(&fib.pal); - uint64_t g_iota = ldq_p(&fib.iota); + uint64_t pba = ldq_be_p(&fib.pba); + uint64_t pal = ldq_be_p(&fib.pal); + uint64_t g_iota = ldq_be_p(&fib.iota); uint8_t dt = (g_iota >> 2) & 0x7; uint8_t t = (g_iota >> 11) & 0x1; @@ -1087,7 +1129,7 @@ static int mpcifc_reg_int_interp(S390PCIBusDevice *pbdev, ZpciFib *fib) rc = s390_pci_kvm_aif_enable(pbdev, fib, pbdev->forwarding_assist); if (rc) { - DPRINTF("Failed to enable interrupt forwarding\n"); + trace_s390_pci_kvm_aif("enable"); return rc; } @@ -1100,7 +1142,7 @@ static int mpcifc_dereg_int_interp(S390PCIBusDevice *pbdev, ZpciFib *fib) rc = s390_pci_kvm_aif_disable(pbdev); if (rc) { - DPRINTF("Failed to disable interrupt forwarding\n"); + trace_s390_pci_kvm_aif("disable"); return rc; } @@ -1133,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; } @@ -1247,7 +1289,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, } break; case ZPCI_MOD_FC_SET_MEASURE: { - uint64_t fmb_addr = ldq_p(&fib.fmb_addr); + uint64_t fmb_addr = ldq_be_p(&fib.fmb_addr); if (fmb_addr & FMBK_MASK) { cc = ZPCI_PCI_LS_ERR; @@ -1357,17 +1399,17 @@ int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, return 0; } - stq_p(&fib.pba, pbdev->iommu->pba); - stq_p(&fib.pal, pbdev->iommu->pal); - stq_p(&fib.iota, pbdev->iommu->g_iota); - stq_p(&fib.aibv, pbdev->routes.adapter.ind_addr); - stq_p(&fib.aisb, pbdev->routes.adapter.summary_addr); - stq_p(&fib.fmb_addr, pbdev->fmb_addr); + stq_be_p(&fib.pba, pbdev->iommu->pba); + stq_be_p(&fib.pal, pbdev->iommu->pal); + stq_be_p(&fib.iota, pbdev->iommu->g_iota); + stq_be_p(&fib.aibv, pbdev->routes.adapter.ind_addr); + stq_be_p(&fib.aisb, pbdev->routes.adapter.summary_addr); + stq_be_p(&fib.fmb_addr, pbdev->fmb_addr); data = ((uint32_t)pbdev->isc << 28) | ((uint32_t)pbdev->noi << 16) | ((uint32_t)pbdev->routes.adapter.ind_offset << 8) | ((uint32_t)pbdev->sum << 7) | pbdev->routes.adapter.summary_offset; - stl_p(&fib.data, data); + stl_be_p(&fib.data, data); out: if (s390_cpu_virt_mem_write(cpu, fiba, ar, (uint8_t *)&fib, sizeof(fib))) { diff --git a/hw/s390x/s390-pci-kvm.c b/hw/s390x/s390-pci-kvm.c index 9134fe185f..9eef4fc3ec 100644 --- a/hw/s390x/s390-pci-kvm.c +++ b/hw/s390x/s390-pci-kvm.c @@ -14,10 +14,11 @@ #include #include "kvm/kvm_s390x.h" -#include "hw/s390x/pv.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) @@ -27,6 +28,7 @@ bool s390_pci_kvm_interp_allowed(void) 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, @@ -38,15 +40,43 @@ int s390_pci_kvm_aif_enable(S390PCIBusDevice *pbdev, ZpciFib *fib, bool assist) .u.reg_aen.flags = (assist) ? 0 : KVM_S390_ZPCIOP_REGAEN_HOST }; - return kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); + 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 }; - return kvm_vm_ioctl(kvm_state, KVM_S390_ZPCI_OP, &args); + 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 5f0adb0b4a..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,17 @@ 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, @@ -276,38 +293,11 @@ 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, - uint32_t argsz) +static struct vfio_device_info *get_device_info(S390PCIBusDevice *pbdev) { - struct vfio_device_info *info = g_malloc0(argsz); - VFIOPCIDevice *vfio_pci; - int fd; + VFIOPCIDevice *vfio_pci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); - 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); - g_free(info); - return NULL; - } - - if (info->argsz > argsz) { - argsz = info->argsz; - info = g_realloc(info, argsz); - goto retry; - } - - return info; + return vfio_get_device_info(vfio_pci->vbasedev.fd); } /* @@ -322,7 +312,7 @@ bool s390_pci_get_host_fh(S390PCIBusDevice *pbdev, uint32_t *fh) assert(fh); - info = get_device_info(pbdev, sizeof(*info)); + info = get_device_info(pbdev); if (!info) { return false; } @@ -343,7 +333,7 @@ void s390_pci_get_clp_info(S390PCIBusDevice *pbdev) { g_autofree struct vfio_device_info *info = NULL; - info = get_device_info(pbdev, sizeof(*info)); + info = get_device_info(pbdev); if (!info) { return; } diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 5024faf411..bf22d6863e 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" @@ -22,6 +23,7 @@ #include "sysemu/kvm.h" #include "migration/qemu-file-types.h" #include "migration/register.h" +#include "trace.h" #define S390_SKEYS_BUFFER_SIZE (128 * KiB) /* Room for 128k storage keys */ #define S390_SKEYS_SAVE_FLAG_EOS 0x01 @@ -53,6 +55,32 @@ void s390_skeys_init(void) qdev_realize(DEVICE(obj), NULL, &error_fatal); } +int s390_skeys_get(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + S390SKeysClass *kc = S390_SKEYS_GET_CLASS(ks); + int rc; + + rc = kc->get_skeys(ks, start_gfn, count, keys); + if (rc) { + trace_s390_skeys_get_nonzero(rc); + } + return rc; +} + +int s390_skeys_set(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys) +{ + S390SKeysClass *kc = S390_SKEYS_GET_CLASS(ks); + int rc; + + rc = kc->set_skeys(ks, start_gfn, count, keys); + if (rc) { + trace_s390_skeys_set_nonzero(rc); + } + return rc; +} + static void write_keys(FILE *f, uint8_t *keys, uint64_t startgfn, uint64_t count, Error **errp) { @@ -152,7 +180,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 +460,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-kvm.c b/hw/s390x/s390-stattrib-kvm.c index 24cd01382e..eeaa811098 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -17,6 +17,7 @@ #include "sysemu/kvm.h" #include "exec/ram_addr.h" #include "kvm/kvm_s390x.h" +#include "qapi/error.h" Object *kvm_s390_stattrib_create(void) { @@ -137,14 +138,21 @@ static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) } } -static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val) +static int kvm_s390_stattrib_set_migrationmode(S390StAttribState *sa, bool val, + Error **errp) { struct kvm_device_attr attr = { .group = KVM_S390_VM_MIGRATION, .attr = val, .addr = 0, }; - return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); + int r; + + r = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); + if (r) { + error_setg_errno(errp, -r, "setting KVM_S390_VM_MIGRATION failed"); + } + return r; } static long long kvm_s390_stattrib_get_dirtycount(S390StAttribState *sa) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 9eda1c3b2a..c4259b5327 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -13,11 +13,13 @@ #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" #include "qapi/error.h" #include "qapi/qmp/qdict.h" +#include "cpu.h" /* 512KiB cover 2GB of guest memory */ #define CMMA_BLOCK_SIZE (512 * KiB) @@ -59,11 +61,13 @@ void hmp_migrationmode(Monitor *mon, const QDict *qdict) S390StAttribState *sas = s390_get_stattrib_device(); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); uint64_t what = qdict_get_int(qdict, "mode"); + Error *local_err = NULL; int r; - r = sac->set_migrationmode(sas, what); + r = sac->set_migrationmode(sas, what, &local_err); if (r < 0) { - monitor_printf(mon, "Error: %s", strerror(-r)); + monitor_printf(mon, "Error: %s", error_get_pretty(local_err)); + error_free(local_err); } } @@ -165,7 +169,7 @@ static int cmma_load(QEMUFile *f, void *opaque, int version_id) return ret; } -static int cmma_save_setup(QEMUFile *f, void *opaque) +static int cmma_save_setup(QEMUFile *f, void *opaque, Error **errp) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); @@ -174,7 +178,7 @@ static int cmma_save_setup(QEMUFile *f, void *opaque) * Signal that we want to start a migration, thus needing PGSTE dirty * tracking. */ - res = sac->set_migrationmode(sas, 1); + res = sac->set_migrationmode(sas, true, errp); if (res) { return res; } @@ -182,17 +186,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 +213,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); @@ -261,7 +263,7 @@ static void cmma_save_cleanup(void *opaque) { S390StAttribState *sas = S390_STATTRIB(opaque); S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); - sac->set_migrationmode(sas, 0); + sac->set_migrationmode(sas, false, NULL); } static bool cmma_active(void *opaque) @@ -294,7 +296,8 @@ static long long qemu_s390_get_dirtycount_stub(S390StAttribState *sa) { return 0; } -static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value) +static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value, + Error **errp) { return 0; } @@ -332,6 +335,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 +353,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 +372,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 2e64ffab45..fe03f716f3 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -14,6 +14,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "exec/ram_addr.h" +#include "exec/confidential-guest-support.h" +#include "hw/boards.h" #include "hw/s390x/s390-virtio-hcall.h" #include "hw/s390x/sclp.h" #include "hw/s390x/s390_flic.h" @@ -41,28 +43,15 @@ #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" +#include CONFIG_DEVICES static Error *pv_mig_blocker; -S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) -{ - static MachineState *ms; - - if (!ms) { - ms = MACHINE(qdev_get_machine()); - g_assert(ms->possible_cpus); - } - - /* CPU address corresponds to the core_id and the index */ - if (cpu_addr >= ms->possible_cpus->len) { - return NULL; - } - return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu); -} - static S390CPU *s390x_new_cpu(const char *typename, uint32_t core_id, Error **errp) { @@ -108,6 +97,7 @@ static const char *const reset_dev_types[] = { "s390-flic", "diag288", TYPE_S390_PCI_HOST_BRIDGE, + TYPE_AP_BRIDGE, }; static void subsystem_reset(void) @@ -115,20 +105,33 @@ 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) { uint64_t subch_id = args[0]; - uint64_t queue = args[1]; + uint64_t data = args[1]; SubchDev *sch; + VirtIODevice *vdev; int cssid, ssid, schid, m; + uint16_t vq_idx = data; if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) { return -EINVAL; @@ -137,12 +140,19 @@ static int virtio_ccw_hcall_notify(const uint64_t *args) if (!sch || !css_subch_visible(sch)) { return -EINVAL; } - if (queue >= VIRTIO_QUEUE_MAX) { + + vdev = virtio_ccw_get_vdev(sch); + if (vq_idx >= VIRTIO_QUEUE_MAX || !virtio_queue_get_num(vdev, vq_idx)) { return -EINVAL; } - virtio_queue_notify(virtio_ccw_get_vdev(sch), queue); - return 0; + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + virtio_queue_set_shadow_avail_idx(virtio_get_queue(vdev, vq_idx), + (data >> 16) & 0xFFFF); + } + + virtio_queue_notify(vdev, vq_idx); + return 0; } static int virtio_ccw_hcall_early_printk(const uint64_t *args) @@ -187,11 +197,10 @@ static void s390_memory_init(MemoryRegion *ram) static void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *firmware, - const char *netboot_fw, bool enforce_bios) + bool enforce_bios) { Object *new = object_new(TYPE_S390_IPL); DeviceState *dev = DEVICE(new); - char *netboot_fw_prop; if (kernel_filename) { qdev_prop_set_string(dev, "kernel", kernel_filename); @@ -202,11 +211,6 @@ static void s390_init_ipl_dev(const char *kernel_filename, qdev_prop_set_string(dev, "cmdline", kernel_cmdline); qdev_prop_set_string(dev, "firmware", firmware); qdev_prop_set_bit(dev, "enforce_bios", enforce_bios); - netboot_fw_prop = object_property_get_str(new, "netboot_fw", &error_abort); - if (!strlen(netboot_fw_prop)) { - qdev_prop_set_string(dev, "netboot_fw", netboot_fw); - } - g_free(netboot_fw_prop); object_property_add_child(qdev_get_machine(), TYPE_S390_IPL, new); object_unref(new); @@ -215,40 +219,41 @@ 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; + int cnt = 0; - 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"))) { + g_autofree char *childname = g_strdup_printf("%s[%d]", name, cnt++); + object_property_add_child(OBJECT(bus), childname, OBJECT(dev)); qdev_realize_and_unref(dev, bus, &error_fatal); } } -static void s390_create_sclpconsole(const char *type, Chardev *chardev) +static void s390_create_sclpconsole(SCLPDevice *sclp, + const char *type, Chardev *chardev) { + SCLPEventFacility *ef = sclp->event_facility; + BusState *ev_fac_bus = sclp_get_event_facility_bus(ef); DeviceState *dev; dev = qdev_new(type); + object_property_add_child(OBJECT(ef), type, OBJECT(dev)); qdev_prop_set_chr(dev, "chardev", chardev); - qdev_realize_and_unref(dev, sclp_get_event_facility_bus(), &error_fatal); + qdev_realize_and_unref(dev, ev_fac_bus, &error_fatal); } static void ccw_init(MachineState *machine) { + MachineClass *mc = MACHINE_GET_CLASS(machine); + S390CcwMachineState *ms = S390_CCW_MACHINE(machine); int ret; VirtualCssBus *css_bus; DeviceState *dev; - s390_sclp_init(); + ms->sclp = SCLP(object_new(TYPE_SCLP)); + object_property_add_child(OBJECT(machine), TYPE_SCLP, OBJECT(ms->sclp)); + qdev_realize_and_unref(DEVICE(ms->sclp), NULL, &error_fatal); + /* init memory + setup max page size. Required for the CPU model */ s390_memory_init(machine->ram); @@ -256,7 +261,9 @@ static void ccw_init(MachineState *machine) s390_init_cpus(machine); /* Need CPU model to be determined before we can set up PV */ - s390_pv_init(machine->cgs, &error_fatal); + if (machine->cgs) { + confidential_guest_kvm_init(machine->cgs, &error_fatal); + } s390_flic_init(); @@ -271,7 +278,7 @@ static void ccw_init(MachineState *machine) s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, machine->firmware ?: "s390-ccw.img", - "s390-netboot.img", true); + true); dev = qdev_new(TYPE_S390_PCI_HOST_BRIDGE); object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, @@ -284,21 +291,19 @@ static void ccw_init(MachineState *machine) s390_enable_css_support(s390_cpu_addr2state(0)); ret = css_create_css_image(VIRTUAL_CSSID, true); - assert(ret == 0); - if (css_migration_enabled()) { - css_register_vmstate(); - } + + css_register_vmstate(); /* 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)) { - s390_create_sclpconsole("sclpconsole", serial_hd(0)); + s390_create_sclpconsole(ms->sclp, "sclpconsole", serial_hd(0)); } if (serial_hd(1)) { - s390_create_sclpconsole("sclplmconsole", serial_hd(1)); + s390_create_sclpconsole(ms->sclp, "sclplmconsole", serial_hd(1)); } /* init the TOD clock */ @@ -308,11 +313,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(); @@ -329,10 +342,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); } @@ -355,11 +369,10 @@ static int s390_machine_protect(S390CcwMachineState *ms) error_setg(&pv_mig_blocker, "protected VMs are currently not migratable."); - rc = migrate_add_blocker(pv_mig_blocker, &local_err); + 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; } @@ -367,8 +380,7 @@ 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; } @@ -381,7 +393,7 @@ static int s390_machine_protect(S390CcwMachineState *ms) } /* Set SE header and unpack */ - rc = s390_ipl_prepare_pv_header(); + rc = s390_ipl_prepare_pv_header(&local_err); if (rc) { goto out_err; } @@ -400,6 +412,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; } @@ -419,7 +434,7 @@ static void s390_pv_prepare_reset(S390CcwMachineState *ms) s390_pv_prep_reset(); } -static void s390_machine_reset(MachineState *machine, ShutdownCause reason) +static void s390_machine_reset(MachineState *machine, ResetType type) { S390CcwMachineState *ms = S390_CCW_MACHINE(machine); enum s390_reset reset_type; @@ -437,11 +452,21 @@ static void s390_machine_reset(MachineState *machine, ShutdownCause reason) 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(reason); + /* + * Device reset includes CPU clear resets so this has to be + * done AFTER the unprotect call above. + */ + qemu_devices_reset(type); s390_crypto_reset(); /* configure and start the ipl CPU only */ @@ -551,11 +576,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; @@ -688,28 +722,12 @@ static void machine_set_loadparm(Object *obj, Visitor *v, { 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 */ - - if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') || - (c == ' ')) { - ms->loadparm[i] = c; - } else { - error_setg(errp, "LOADPARM: invalid character '%c' (ASCII 0x%02x)", - c, c); - return; - } - } - - for (; i < sizeof(ms->loadparm); i++) { - ms->loadparm[i] = ' '; /* pad right with spaces */ - } + s390_ipl_fmt_loadparm(ms->loadparm, val, errp); } static void ccw_machine_class_init(ObjectClass *oc, void *data) @@ -721,7 +739,6 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) 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; @@ -733,6 +750,8 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) 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; @@ -743,6 +762,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) 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, @@ -788,49 +808,134 @@ static const TypeInfo ccw_machine_info = { }, }; -bool css_migration_enabled(void) -{ - return get_machine_class()->css_migration_enabled; -} - -#define DEFINE_CCW_MACHINE(suffix, verstr, latest) \ - static void ccw_machine_##suffix##_class_init(ObjectClass *oc, \ - void *data) \ +#define DEFINE_CCW_MACHINE_IMPL(latest, ...) \ + static void MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ - ccw_machine_##suffix##_class_options(mc); \ - mc->desc = "Virtual s390x machine (version " verstr ")"; \ + MACHINE_VER_SYM(class_options, ccw, __VA_ARGS__)(mc); \ + mc->desc = "Virtual s390x machine (version " MACHINE_VER_STR(__VA_ARGS__) ")"; \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "s390-ccw-virtio"; \ mc->is_default = true; \ } \ } \ - static void ccw_machine_##suffix##_instance_init(Object *obj) \ + static void MACHINE_VER_SYM(instance_init, ccw, __VA_ARGS__)(Object *obj) \ { \ MachineState *machine = MACHINE(obj); \ - current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ - ccw_machine_##suffix##_instance_options(machine); \ + current_mc = S390_CCW_MACHINE_CLASS(MACHINE_GET_CLASS(machine)); \ + MACHINE_VER_SYM(instance_options, ccw, __VA_ARGS__)(machine); \ } \ - static const TypeInfo ccw_machine_##suffix##_info = { \ - .name = MACHINE_TYPE_NAME("s390-ccw-virtio-" verstr), \ - .parent = TYPE_S390_CCW_MACHINE, \ - .class_init = ccw_machine_##suffix##_class_init, \ - .instance_init = ccw_machine_##suffix##_instance_init, \ - }; \ - static void ccw_machine_register_##suffix(void) \ + static const TypeInfo MACHINE_VER_SYM(info, ccw, __VA_ARGS__) = \ { \ - type_register_static(&ccw_machine_##suffix##_info); \ + .name = MACHINE_VER_TYPE_NAME("s390-ccw-virtio", __VA_ARGS__), \ + .parent = TYPE_S390_CCW_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__), \ + .instance_init = MACHINE_VER_SYM(instance_init, ccw, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, ccw, __VA_ARGS__)(void) \ + { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ + type_register_static(&MACHINE_VER_SYM(info, ccw, __VA_ARGS__)); \ } \ - type_init(ccw_machine_register_##suffix) + type_init(MACHINE_VER_SYM(register, ccw, __VA_ARGS__)) + +#define DEFINE_CCW_MACHINE_AS_LATEST(major, minor) \ + DEFINE_CCW_MACHINE_IMPL(true, major, minor) + +#define DEFINE_CCW_MACHINE(major, minor) \ + DEFINE_CCW_MACHINE_IMPL(false, major, minor) + + +static void ccw_machine_9_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_9_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(9, 2); + +static void ccw_machine_9_1_instance_options(MachineState *machine) +{ + ccw_machine_9_2_instance_options(machine); +} + +static void ccw_machine_9_1_class_options(MachineClass *mc) +{ + ccw_machine_9_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); +} +DEFINE_CCW_MACHINE(9, 1); + +static void ccw_machine_9_0_instance_options(MachineState *machine) +{ + ccw_machine_9_1_instance_options(machine); +} + +static void ccw_machine_9_0_class_options(MachineClass *mc) +{ + static GlobalProperty compat[] = { + { TYPE_QEMU_S390_FLIC, "migrate-all-state", "off", }, + }; + + ccw_machine_9_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_0, hw_compat_9_0_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); +} +DEFINE_CCW_MACHINE(9, 0); + +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); + +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); + +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); 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", true); +DEFINE_CCW_MACHINE(7, 2); static void ccw_machine_7_1_instance_options(MachineState *machine) { @@ -854,7 +959,7 @@ static void ccw_machine_7_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); s390mc->max_threads = S390_MAX_CPUS; } -DEFINE_CCW_MACHINE(7_1, "7.1", false); +DEFINE_CCW_MACHINE(7, 1); static void ccw_machine_7_0_instance_options(MachineState *machine) { @@ -869,7 +974,7 @@ static void ccw_machine_7_0_class_options(MachineClass *mc) ccw_machine_7_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_7_0, hw_compat_7_0_len); } -DEFINE_CCW_MACHINE(7_0, "7.0", false); +DEFINE_CCW_MACHINE(7, 0); static void ccw_machine_6_2_instance_options(MachineState *machine) { @@ -884,7 +989,7 @@ static void ccw_machine_6_2_class_options(MachineClass *mc) ccw_machine_7_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_2, hw_compat_6_2_len); } -DEFINE_CCW_MACHINE(6_2, "6.2", false); +DEFINE_CCW_MACHINE(6, 2); static void ccw_machine_6_1_instance_options(MachineState *machine) { @@ -902,7 +1007,7 @@ static void ccw_machine_6_1_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_6_1, hw_compat_6_1_len); mc->smp_props.prefer_sockets = true; } -DEFINE_CCW_MACHINE(6_1, "6.1", false); +DEFINE_CCW_MACHINE(6, 1); static void ccw_machine_6_0_instance_options(MachineState *machine) { @@ -917,7 +1022,7 @@ static void ccw_machine_6_0_class_options(MachineClass *mc) ccw_machine_6_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_6_0, hw_compat_6_0_len); } -DEFINE_CCW_MACHINE(6_0, "6.0", false); +DEFINE_CCW_MACHINE(6, 0); static void ccw_machine_5_2_instance_options(MachineState *machine) { @@ -929,7 +1034,7 @@ static void ccw_machine_5_2_class_options(MachineClass *mc) ccw_machine_6_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len); } -DEFINE_CCW_MACHINE(5_2, "5.2", false); +DEFINE_CCW_MACHINE(5, 2); static void ccw_machine_5_1_instance_options(MachineState *machine) { @@ -941,7 +1046,7 @@ static void ccw_machine_5_1_class_options(MachineClass *mc) ccw_machine_5_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_1, hw_compat_5_1_len); } -DEFINE_CCW_MACHINE(5_1, "5.1", false); +DEFINE_CCW_MACHINE(5, 1); static void ccw_machine_5_0_instance_options(MachineState *machine) { @@ -953,7 +1058,7 @@ static void ccw_machine_5_0_class_options(MachineClass *mc) ccw_machine_5_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_5_0, hw_compat_5_0_len); } -DEFINE_CCW_MACHINE(5_0, "5.0", false); +DEFINE_CCW_MACHINE(5, 0); static void ccw_machine_4_2_instance_options(MachineState *machine) { @@ -966,7 +1071,7 @@ static void ccw_machine_4_2_class_options(MachineClass *mc) mc->fixup_ram_size = s390_fixup_ram_size; compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); } -DEFINE_CCW_MACHINE(4_2, "4.2", false); +DEFINE_CCW_MACHINE(4, 2); static void ccw_machine_4_1_instance_options(MachineState *machine) { @@ -980,7 +1085,7 @@ static void ccw_machine_4_1_class_options(MachineClass *mc) ccw_machine_4_2_class_options(mc); compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len); } -DEFINE_CCW_MACHINE(4_1, "4.1", false); +DEFINE_CCW_MACHINE(4, 1); static void ccw_machine_4_0_instance_options(MachineState *machine) { @@ -994,7 +1099,7 @@ static void ccw_machine_4_0_class_options(MachineClass *mc) ccw_machine_4_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len); } -DEFINE_CCW_MACHINE(4_0, "4.0", false); +DEFINE_CCW_MACHINE(4, 0); static void ccw_machine_3_1_instance_options(MachineState *machine) { @@ -1010,7 +1115,7 @@ static void ccw_machine_3_1_class_options(MachineClass *mc) ccw_machine_4_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); } -DEFINE_CCW_MACHINE(3_1, "3.1", false); +DEFINE_CCW_MACHINE(3, 1); static void ccw_machine_3_0_instance_options(MachineState *machine) { @@ -1025,7 +1130,7 @@ static void ccw_machine_3_0_class_options(MachineClass *mc) ccw_machine_3_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); } -DEFINE_CCW_MACHINE(3_0, "3.0", false); +DEFINE_CCW_MACHINE(3, 0); static void ccw_machine_2_12_instance_options(MachineState *machine) { @@ -1039,7 +1144,9 @@ static void ccw_machine_2_12_class_options(MachineClass *mc) ccw_machine_3_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); } -DEFINE_CCW_MACHINE(2_12, "2.12", false); +DEFINE_CCW_MACHINE(2, 12); + +#ifdef CONFIG_S390X_LEGACY_CPUS static void ccw_machine_2_11_instance_options(MachineState *machine) { @@ -1060,7 +1167,7 @@ static void ccw_machine_2_11_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_11, "2.11", false); +DEFINE_CCW_MACHINE(2, 11); static void ccw_machine_2_10_instance_options(MachineState *machine) { @@ -1072,7 +1179,7 @@ static void ccw_machine_2_10_class_options(MachineClass *mc) ccw_machine_2_11_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); } -DEFINE_CCW_MACHINE(2_10, "2.10", false); +DEFINE_CCW_MACHINE(2, 10); static void ccw_machine_2_9_instance_options(MachineState *machine) { @@ -1086,17 +1193,17 @@ static void ccw_machine_2_9_instance_options(MachineState *machine) static void ccw_machine_2_9_class_options(MachineClass *mc) { - S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); static GlobalProperty compat[] = { { TYPE_S390_STATTRIB, "migration-enabled", "off", }, + { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", }, }; ccw_machine_2_10_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); - s390mc->css_migration_enabled = false; + css_migration_enabled = false; } -DEFINE_CCW_MACHINE(2_9, "2.9", false); +DEFINE_CCW_MACHINE(2, 9); static void ccw_machine_2_8_instance_options(MachineState *machine) { @@ -1113,7 +1220,7 @@ static void ccw_machine_2_8_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_8, "2.8", false); +DEFINE_CCW_MACHINE(2, 8); static void ccw_machine_2_7_instance_options(MachineState *machine) { @@ -1128,7 +1235,7 @@ static void ccw_machine_2_7_class_options(MachineClass *mc) ccw_machine_2_8_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); } -DEFINE_CCW_MACHINE(2_7, "2.7", false); +DEFINE_CCW_MACHINE(2, 7); static void ccw_machine_2_6_instance_options(MachineState *machine) { @@ -1148,7 +1255,7 @@ static void ccw_machine_2_6_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_6, "2.6", false); +DEFINE_CCW_MACHINE(2, 6); static void ccw_machine_2_5_instance_options(MachineState *machine) { @@ -1160,7 +1267,7 @@ static void ccw_machine_2_5_class_options(MachineClass *mc) ccw_machine_2_6_class_options(mc); compat_props_add(mc->compat_props, hw_compat_2_5, hw_compat_2_5_len); } -DEFINE_CCW_MACHINE(2_5, "2.5", false); +DEFINE_CCW_MACHINE(2, 5); static void ccw_machine_2_4_instance_options(MachineState *machine) { @@ -1185,7 +1292,9 @@ static void ccw_machine_2_4_class_options(MachineClass *mc) compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } -DEFINE_CCW_MACHINE(2_4, "2.4", false); +DEFINE_CCW_MACHINE(2, 4); + +#endif static void ccw_machine_register_types(void) { diff --git a/hw/s390x/s390-virtio-hcall.h b/hw/s390x/s390-virtio-hcall.h index 9800c4b351..3ae6d6ae3a 100644 --- a/hw/s390x/s390-virtio-hcall.h +++ b/hw/s390x/s390-virtio-hcall.h @@ -13,6 +13,7 @@ #define HW_S390_VIRTIO_HCALL_H #include "standard-headers/asm-s390/virtio-ccw.h" +#include "cpu.h" /* The only thing that we need from the old kvm_virtio.h file */ #define KVM_S390_VIRTIO_NOTIFY 0 @@ -20,4 +21,5 @@ typedef int (*s390_virtio_fn)(const uint64_t *args); void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn); int s390_virtio_hypercall(CPUS390XState *env); + #endif /* HW_S390_VIRTIO_HCALL_H */ diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index eff74479f4..8757626b5c 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -20,13 +20,15 @@ #include "hw/s390x/event-facility.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/ipl.h" +#include "hw/s390x/cpu-topology.h" +#include "hw/s390x/s390-virtio-ccw.h" -static inline SCLPDevice *get_sclp_device(void) +static SCLPDevice *get_sclp_device(void) { static SCLPDevice *sclp; if (!sclp) { - sclp = SCLP(object_resolve_path_type("", TYPE_SCLP, NULL)); + sclp = S390_CCW_MACHINE(qdev_get_machine())->sclp; } return sclp; } @@ -108,7 +110,6 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) MachineState *machine = MACHINE(qdev_get_machine()); int cpu_count; int rnsize, rnmax; - IplParameterBlock *ipib = s390_ipl_get_iplb(); int required_len = SCCB_REQ_LEN(ReadInfo, machine->possible_cpus->len); int offset_cpu = s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB) ? offsetof(ReadInfo, entries) : @@ -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); @@ -165,12 +170,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb) read_info->rnmax2 = cpu_to_be64(rnmax); } - if (ipib && ipib->flags & DIAG308_FLAGS_LP_VALID) { - memcpy(&read_info->loadparm, &ipib->loadparm, - sizeof(read_info->loadparm)); - } else { - s390_ipl_set_loadparm(read_info->loadparm); - } + s390_ipl_convert_loadparm((char *)S390_CCW_MACHINE(machine)->loadparm, + read_info->loadparm); sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_READ_COMPLETION); } @@ -264,9 +265,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 +292,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; @@ -372,16 +374,6 @@ void sclp_service_interrupt(uint32_t sccb) } /* qemu object creation and initialization functions */ - -void s390_sclp_init(void) -{ - Object *new = object_new(TYPE_SCLP); - - object_property_add_child(qdev_get_machine(), TYPE_SCLP, new); - object_unref(new); - qdev_realize(DEVICE(new), NULL, &error_fatal); -} - static void sclp_realize(DeviceState *dev, Error **errp) { MachineState *machine = MACHINE(qdev_get_machine()); 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..a32d6a91f5 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; @@ -117,7 +117,7 @@ static void quiesce_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *k = SCLP_EVENT_CLASS(klass); - dc->reset = quiesce_reset; + device_class_set_legacy_reset(dc, quiesce_reset); dc->vmsd = &vmstate_sclpquiesce; set_bit(DEVICE_CATEGORY_MISC, dc->categories); /* diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index e2202dae2d..9588b90f2b 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -13,7 +13,7 @@ #include "qemu/module.h" #include "sysemu/runstate.h" #include "hw/s390x/tod.h" -#include "hw/s390x/pv.h" +#include "target/s390x/kvm/pv.h" #include "kvm/kvm_s390x.h" static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp) diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events index 8b9213eab9..4e74bf4484 100644 --- a/hw/s390x/trace-events +++ b/hw/s390x/trace-events @@ -19,3 +19,24 @@ 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" + +# s390-skeys.c +s390_skeys_get_nonzero(int rc) "SKEY: Call to get_skeys unexpectedly returned %d" +s390_skeys_set_nonzero(int rc) "SKEY: Call to set_skeys unexpectedly returned %d" diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index 8e0e58b77d..2364432c6e 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -48,6 +48,7 @@ static Property virtio_ccw_blk_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index 484e617659..a4a3f65c7e 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -51,6 +51,7 @@ static Property virtio_ccw_net_properties[] = { VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, VIRTIO_CCW_MAX_REV), + DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), DEFINE_PROP_END_OF_LIST(), }; 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 e33e5207ab..96747318d2 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; @@ -768,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; @@ -917,14 +913,15 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) } } -static void virtio_ccw_reset(DeviceState *d) +static void virtio_ccw_reset_hold(Object *obj, ResetType type) { - VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(obj); VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); virtio_ccw_reset_virtio(dev); - if (vdc->parent_reset) { - vdc->parent_reset(d); + + if (vdc->parent_phases.hold) { + vdc->parent_phases.hold(obj, type); } } @@ -1237,11 +1234,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->unrealize = virtio_ccw_busdev_unrealize; - device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, virtio_ccw_reset_hold, NULL, + &vdc->parent_phases); } static const TypeInfo virtio_ccw_device_info = { diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index fac186c8f6..c7a830a194 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -57,7 +57,7 @@ struct VirtIOCCWDeviceClass { CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); void (*unrealize)(VirtioCcwDevice *dev); - void (*parent_reset)(DeviceState *dev); + ResettablePhases parent_phases; }; /* Performance improves when virtqueue kick processing is decoupled from the 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..fe4e045a6f 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); } @@ -419,7 +440,7 @@ static void esp_pci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_STORAGE_SCSI; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->desc = "AMD Am53c974 PCscsi-PCI SCSI adapter"; - dc->reset = esp_pci_hard_reset; + device_class_set_legacy_reset(dc, esp_pci_hard_reset); dc->vmsd = &vmstate_esp_pci_scsi; } diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index e52188d022..ac841dc32e 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,40 +124,85 @@ 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) { - const uint8_t *buf; - uint32_t n; + fifo8_push_all(&s->fifo, buf, len); + esp_update_drq(s); +} - if (maxlen == 0) { - return 0; +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); } - buf = fifo8_pop_buf(fifo, maxlen, &n); - if (dest) { - memcpy(dest, buf, n); - } + esp_update_drq(s); + return val; +} - return n; +static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t *dest, int maxlen) +{ + uint32_t len = fifo8_pop_buf(&s->fifo, dest, maxlen); + + esp_update_drq(s); + return len; } static uint32_t esp_get_tc(ESPState *s) @@ -148,9 +218,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 +244,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 +252,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 +267,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 +305,32 @@ static void do_command_phase(ESPState *s) if (!cmdlen || !s->current_dev) { return; } - esp_fifo_pop_buf(&s->cmdfifo, buf, cmdlen); + fifo8_pop_buf(&s->cmdfifo, buf, cmdlen); current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, s->lun); + 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 +340,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 +351,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); + fifo8_drop(&s->cmdfifo, len); s->cmdfifo_cdb_offset = 0; } } @@ -344,434 +363,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 command 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_bufptr(&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 command 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)) { + /* 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)) { + /* 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 command 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 +756,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 +947,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 +956,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 +995,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 +1068,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 +1092,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,7 +1100,7 @@ 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); } @@ -951,31 +1116,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: @@ -985,7 +1219,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 @@ -995,7 +1230,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 */ @@ -1030,108 +1264,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); - 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(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; @@ -1180,6 +1320,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( @@ -1215,39 +1363,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), @@ -1268,18 +1389,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, @@ -1328,7 +1447,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, @@ -1349,9 +1468,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; } @@ -1395,7 +1512,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; } } @@ -1411,7 +1528,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; @@ -1447,7 +1564,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() @@ -1459,19 +1576,11 @@ static void sysbus_esp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = sysbus_esp_realize; - dc->reset = sysbus_esp_hard_reset; + device_class_set_legacy_reset(dc, sysbus_esp_hard_reset); dc->vmsd = &vmstate_sysbus_esp_scsi; 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); @@ -1497,19 +1606,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 42532c4744..1f728416e2 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 500 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. */ @@ -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) { @@ -1127,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); @@ -1136,6 +1158,11 @@ static void lsi_execute_script(LSIState *s) 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; @@ -1143,8 +1170,8 @@ again: /* * 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 force an unexpected device disconnect. This - * is apparently sufficient to beat the drivers into submission. + * 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 @@ -1152,13 +1179,9 @@ again: * which should be enough for all valid use cases). */ if (++insn_processed > LSI_MAX_INSN || reentrancy_level > 8) { - 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(); + s->waiting = LSI_WAIT_SCRIPTS; + lsi_scripts_timer_start(s); + reentrancy_level--; return; } insn = read_dword(s, s->dsp); @@ -1321,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; @@ -2196,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; } @@ -2205,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), @@ -2293,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); @@ -2312,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); @@ -2329,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) @@ -2342,7 +2386,7 @@ static void lsi_class_init(ObjectClass *klass, void *data) k->device_id = PCI_DEVICE_ID_LSI_53C895A; k->class_id = PCI_CLASS_STORAGE_SCSI; k->subsystem_id = 0x1000; - dc->reset = lsi_scsi_reset; + device_class_set_legacy_reset(dc, lsi_scsi_reset); dc->vmsd = &vmstate_lsi_scsi; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 9cbbb16121..b33229d71a 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 */ @@ -1780,7 +1781,7 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) uint8_t cdb[16]; int len; struct SCSIDevice *sdev = NULL; - int target_id, lun_id, cdb_len; + int target_id, lun_id; lba_count = le32_to_cpu(cmd->frame->io.header.data_len); lba_start_lo = le32_to_cpu(cmd->frame->io.lba_lo); @@ -1789,7 +1790,6 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) target_id = cmd->frame->header.target_id; lun_id = cmd->frame->header.lun_id; - cdb_len = cmd->frame->header.cdb_len; if (target_id < MFI_MAX_LD && lun_id == 0) { sdev = scsi_device_find(&s->bus, 0, target_id, lun_id); @@ -1804,15 +1804,6 @@ static int megasas_handle_io(MegasasState *s, MegasasCmd *cmd, int frame_cmd) return MFI_STAT_DEVICE_NOT_FOUND; } - if (cdb_len > 16) { - trace_megasas_scsi_invalid_cdb_len( - mfi_frame_desc(frame_cmd), 1, target_id, lun_id, cdb_len); - megasas_write_sense(cmd, SENSE_CODE(INVALID_OPCODE)); - cmd->frame->header.scsi_status = CHECK_CONDITION; - s->event_count++; - return MFI_STAT_SCSI_DONE_WITH_ERROR; - } - cmd->iov_size = lba_count * sdev->blocksize; if (megasas_map_sgl(s, cmd, &cmd->frame->io.sgl)) { megasas_write_sense(cmd, SENSE_CODE(TARGET_FAILURE)); @@ -1823,7 +1814,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, cdb_len, cmd); + lun_id, cdb, sizeof(cdb), cmd); if (!cmd->req) { trace_megasas_scsi_req_alloc_failed( mfi_frame_desc(frame_cmd), target_id, lun_id); @@ -2298,7 +2289,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 +2307,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 +2347,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 +2416,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; } @@ -2553,7 +2547,7 @@ static void megasas_class_init(ObjectClass *oc, void *data) e->product_name = info->product_name; e->product_version = info->product_version; device_class_set_props(dc, info->props); - dc->reset = megasas_scsi_reset; + device_class_set_legacy_reset(dc, megasas_scsi_reset); dc->vmsd = info->vmsd; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->desc = info->desc; 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 c485da792c..361b75e633 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; @@ -1322,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); } @@ -1365,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), @@ -1430,7 +1431,7 @@ static void mptsas1068_class_init(ObjectClass *oc, void *data) pc->subsystem_id = 0x8000; pc->class_id = PCI_CLASS_STORAGE_SCSI; device_class_set_props(dc, mptsas_properties); - dc->reset = mptsas_reset; + device_class_set_legacy_reset(dc, mptsas_reset); dc->vmsd = &vmstate_mptsas; dc->desc = "LSI SAS 1068"; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); 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 ceceafb2cd..53eff5dd3d 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); @@ -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,16 +376,15 @@ 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; + Error *local_err = NULL; if (blk_is_sg(blk)) { driver = "scsi-generic"; @@ -332,11 +401,18 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, object_property_add_child(OBJECT(bus), name, OBJECT(dev)); g_free(name); - qdev_prop_set_uint32(dev, "scsi-id", unit); - if (bootindex >= 0) { - object_property_set_int(OBJECT(dev), "bootindex", bootindex, - &error_abort); + s = SCSI_DEVICE(dev); + s->conf = *conf; + + check_boot_index(conf->bootindex, &local_err); + if (local_err) { + object_unparent(OBJECT(dev)); + error_propagate(errp, local_err); + return NULL; } + add_boot_device_path(conf->bootindex, dev, NULL); + + qdev_prop_set_uint32(dev, "scsi-id", unit); if (object_property_find(OBJECT(dev), "removable")) { qdev_prop_set_bit(dev, "removable", removable); } @@ -347,19 +423,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 +436,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 +451,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 +484,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 +575,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 +590,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,6 +795,11 @@ 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; } @@ -794,43 +896,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) @@ -1513,13 +1594,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); @@ -1654,20 +1728,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); @@ -1694,31 +1816,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; } @@ -1783,7 +1907,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), @@ -1795,7 +1919,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), @@ -1813,7 +1937,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 e493c28814..7f13b0588f 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -32,6 +32,7 @@ #include "migration/vmstate.h" #include "hw/scsi/emulation.h" #include "scsi/constants.h" +#include "sysemu/arch_init.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/block/block.h" @@ -58,10 +59,20 @@ #define TYPE_SCSI_DISK_BASE "scsi-disk-base" +#define MAX_SERIAL_LEN 36 +#define MAX_SERIAL_LEN_FOR_DEVID 20 + OBJECT_DECLARE_TYPE(SCSIDiskState, SCSIDiskClass, SCSI_DISK_BASE) struct SCSIDiskClass { SCSIDeviceClass parent_class; + /* + * Callbacks receive ret == 0 for success. Errors are represented either as + * negative errno values, or as positive SAM status codes. + * + * Beware: For errors returned in host_status, the function may directly + * complete the request and never call the callback. + */ DMAIOFunc *dma_readv; DMAIOFunc *dma_writev; bool (*need_fua_emulation)(SCSICommand *cmd); @@ -101,6 +112,7 @@ struct SCSIDiskState { char *vendor; char *product; char *device_id; + char *loadparm; /* only for s390x */ bool tray_open; bool tray_locked; /* @@ -111,6 +123,7 @@ struct SCSIDiskState { * 0xffff - reserved */ uint16_t rotation_rate; + bool migrate_emulated_scsi_request; }; static void scsi_free_request(SCSIRequest *req) @@ -159,6 +172,15 @@ static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) } } +static void scsi_disk_emulate_save_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + if (s->migrate_emulated_scsi_request) { + scsi_disk_save_request(f, req); + } +} + static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) { SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); @@ -182,6 +204,15 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) qemu_iovec_init_external(&r->qiov, &r->iov, 1); } +static void scsi_disk_emulate_load_request(QEMUFile *f, SCSIRequest *req) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + if (s->migrate_emulated_scsi_request) { + scsi_disk_load_request(f, req); + } +} + /* * scsi_handle_rw_error has two return values. False means that the error * must be ignored, true means that the error has been processed and the @@ -195,7 +226,7 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); SCSISense sense = SENSE_CODE(NO_SENSE); - int error = 0; + int error; bool req_has_sense = false; BlockErrorAction action; int status; @@ -206,11 +237,35 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) } else { /* A passthrough command has completed with nonzero status. */ status = ret; - if (status == CHECK_CONDITION) { + switch (status) { + case CHECK_CONDITION: req_has_sense = true; error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); - } else { + break; + case RESERVATION_CONFLICT: + /* + * Don't apply the error policy, always report to the guest. + * + * This is a passthrough code path, so it's not a backend error, but + * a response to an invalid guest request. + * + * Windows Failover Cluster validation intentionally sends invalid + * requests to verify that reservations work as intended. It is + * crucial that it sees the resulting errors. + * + * Treating a reservation conflict as a guest-side error is obvious + * when a pr-manager is in use. Without one, the situation is less + * clear, but there might be nothing that can be fixed on the host + * (like in the above example), and we don't want to be stuck in a + * loop where resuming the VM and retrying the request immediately + * stops it again. So always reporting is still the safer option in + * this case, too. + */ + error = 0; + break; + default: error = EINVAL; + break; } } @@ -220,8 +275,9 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int ret, bool acct_failed) * are usually retried immediately, so do not post them to QMP and * do not account them as failed I/O. */ - if (req_has_sense && - scsi_sense_buf_is_guest_recoverable(r->req.sense, sizeof(r->req.sense))) { + if (!error || (req_has_sense && + scsi_sense_buf_is_guest_recoverable(r->req.sense, + sizeof(r->req.sense)))) { action = BLOCK_ERROR_ACTION_REPORT; acct_failed = false; } else { @@ -261,7 +317,7 @@ static bool scsi_disk_req_check_error(SCSIDiskReq *r, int ret, bool acct_failed) return true; } - if (ret < 0) { + if (ret != 0) { return scsi_handle_rw_error(r, ret, acct_failed); } @@ -273,9 +329,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; } @@ -284,7 +344,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); } @@ -335,7 +394,7 @@ static void scsi_write_do_fua(SCSIDiskReq *r) static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) { assert(r->req.aiocb == NULL); - if (scsi_disk_req_check_error(r, ret, false)) { + if (scsi_disk_req_check_error(r, ret, ret > 0)) { goto done; } @@ -352,6 +411,7 @@ done: scsi_req_unref(&r->req); } +/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_dma_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -360,22 +420,26 @@ 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)); + /* ret > 0 is accounted for in scsi_disk_req_check_error() */ if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { + } else if (ret == 0) { 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)) { + if (scsi_disk_req_check_error(r, ret, ret > 0)) { goto done; } @@ -388,6 +452,7 @@ done: scsi_req_unref(&r->req); } +/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_read_complete(void *opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -396,15 +461,14 @@ 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)); + /* ret > 0 is accounted for in scsi_disk_req_check_error() */ if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { + } else if (ret == 0) { block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct); 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. */ @@ -449,14 +513,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. */ @@ -502,10 +564,15 @@ 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)) { + if (scsi_disk_req_check_error(r, ret, ret > 0)) { goto done; } @@ -525,6 +592,7 @@ done: scsi_req_unref(&r->req); } +/* May not be called in all error cases, don't rely on cleanup here */ static void scsi_write_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; @@ -533,14 +601,13 @@ 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)); + /* ret > 0 is accounted for in scsi_disk_req_check_error() */ if (ret < 0) { block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct); - } else { + } else if (ret == 0) { 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) @@ -643,8 +710,8 @@ static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) } l = strlen(s->serial); - if (l > 36) { - l = 36; + if (l > MAX_SERIAL_LEN) { + l = MAX_SERIAL_LEN; } trace_scsi_disk_emulate_vpd_page_80(req->cmd.xfer); @@ -1624,9 +1691,10 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf) * 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 + * 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 & ~0xff00) && bs != s->qdev.blocksize) { + if (bs && !(bs & ~0xfe00) && bs != s->qdev.blocksize) { s->qdev.blocksize = bs; trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize); } @@ -1740,7 +1808,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); @@ -1748,7 +1815,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) @@ -1818,7 +1884,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; } @@ -1839,7 +1905,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; } @@ -1849,7 +1914,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) @@ -1951,6 +2015,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(); } @@ -2336,6 +2404,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--; @@ -2348,6 +2417,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; @@ -2402,16 +2485,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) @@ -2477,9 +2563,20 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) if (!s->vendor) { s->vendor = g_strdup("QEMU"); } + if (s->serial && strlen(s->serial) > MAX_SERIAL_LEN) { + error_setg(errp, "The serial number can't be longer than %d characters", + MAX_SERIAL_LEN); + return; + } if (!s->device_id) { if (s->serial) { - s->device_id = g_strdup_printf("%.20s", s->serial); + if (strlen(s->serial) > MAX_SERIAL_LEN_FOR_DEVID) { + error_setg(errp, "The serial number can't be longer than %d " + "characters when it is also used as the default for " + "device_id", MAX_SERIAL_LEN_FOR_DEVID); + return; + } + s->device_id = g_strdup(s->serial); } else { const char *str = blk_name(s->qdev.conf.blk); if (str && *str) { @@ -2516,15 +2613,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; @@ -2533,16 +2628,11 @@ 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; @@ -2558,8 +2648,6 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) blocksize = dev->conf.physical_block_size; } - ctx = blk_get_aio_context(dev->conf.blk); - aio_context_acquire(ctx); s->qdev.blocksize = blocksize; s->qdev.type = TYPE_ROM; s->features |= 1 << SCSI_DISK_F_REMOVABLE; @@ -2567,7 +2655,6 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) s->product = g_strdup("QEMU CD-ROM"); } scsi_realize(&s->qdev, errp); - aio_context_release(ctx); } @@ -2578,6 +2665,8 @@ static const SCSIReqOps scsi_disk_emulate_reqops = { .read_data = scsi_disk_emulate_read_data, .write_data = scsi_disk_emulate_write_data, .get_buf = scsi_get_buf, + .load_request = scsi_disk_emulate_load_request, + .save_request = scsi_disk_emulate_save_request, }; static const SCSIReqOps scsi_disk_dma_reqops = { @@ -2634,19 +2723,12 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { static void scsi_disk_new_request_dump(uint32_t lun, uint32_t tag, uint8_t *buf) { - int i; int len = scsi_cdb_length(buf); - char *line_buffer, *p; + g_autoptr(GString) str = NULL; assert(len > 0 && len <= 16); - line_buffer = g_malloc(len * 5 + 1); - - for (i = 0, p = line_buffer; i < len; i++) { - p += sprintf(p, " 0x%02x", buf[i]); - } - trace_scsi_disk_new_request(lun, tag, line_buffer); - - g_free(line_buffer); + str = qemu_hexdump_line(NULL, buf, len, 1, 0); + trace_scsi_disk_new_request(lun, tag, str->str); } static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, @@ -2698,7 +2780,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; @@ -2713,9 +2794,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) { @@ -2723,18 +2801,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. @@ -2754,9 +2832,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 { @@ -2776,10 +2851,10 @@ 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) { + /* FIXME This skips calling req->cb() and any cleanup in it */ if (io_hdr->host_status != SCSI_HOST_OK) { scsi_req_complete_failed(&r->req, io_hdr->host_status); scsi_req_unref(&r->req); @@ -2791,19 +2866,6 @@ static void scsi_block_sgio_complete(void *opaque, int ret) } else { ret = io_hdr->status; } - - 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; - } } req->cb(req->cb_opaque, ret); @@ -3075,13 +3137,50 @@ BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); } +static char *scsi_property_get_loadparm(Object *obj, Error **errp) +{ + return g_strdup(SCSI_DISK_BASE(obj)->loadparm); +} + +static void scsi_property_set_loadparm(Object *obj, const char *value, + Error **errp) +{ + void *lp_str; + + if (object_property_get_int(obj, "bootindex", NULL) < 0) { + error_setg(errp, "'loadparm' is only valid for boot devices"); + return; + } + + lp_str = g_malloc0(strlen(value) + 1); + if (!qdev_prop_sanitize_s390x_loadparm(lp_str, value, errp)) { + g_free(lp_str); + return; + } + SCSI_DISK_BASE(obj)->loadparm = lp_str; +} + +static void scsi_property_add_specifics(DeviceClass *dc) +{ + ObjectClass *oc = OBJECT_CLASS(dc); + + /* The loadparm property is only supported on s390x */ + if (arch_type & QEMU_ARCH_S390X) { + object_class_property_add_str(oc, "loadparm", + scsi_property_get_loadparm, + scsi_property_set_loadparm); + object_class_property_set_description(oc, "loadparm", + "load parameter (s390x only)"); + } +} + static void scsi_disk_base_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass); dc->fw_name = "disk"; - dc->reset = scsi_disk_reset; + device_class_set_legacy_reset(dc, scsi_disk_reset); sdc->dma_readv = scsi_dma_readv; sdc->dma_writev = scsi_dma_writev; sdc->need_fua_emulation = scsi_is_cmd_fua; @@ -3104,7 +3203,8 @@ static const TypeInfo scsi_disk_base_info = { DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ DEFINE_PROP_STRING("product", SCSIDiskState, product), \ - DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id) + DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id), \ + DEFINE_PROP_BOOL("migrate-emulated-scsi-request", SCSIDiskState, migrate_emulated_scsi_request, true) static Property scsi_hd_properties[] = { @@ -3134,7 +3234,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), @@ -3157,6 +3257,8 @@ static void scsi_hd_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI disk"; device_class_set_props(dc, scsi_hd_properties); dc->vmsd = &vmstate_scsi_disk_state; + + scsi_property_add_specifics(dc); } static const TypeInfo scsi_hd_info = { @@ -3197,6 +3299,8 @@ static void scsi_cd_class_initfn(ObjectClass *klass, void *data) dc->desc = "virtual SCSI CD-ROM"; device_class_set_props(dc, scsi_cd_properties); dc->vmsd = &vmstate_scsi_disk_state; + + scsi_property_add_specifics(dc); } static const TypeInfo scsi_cd_info = { diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index d513870181..76f04a5ee8 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, @@ -276,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; @@ -319,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. */ @@ -355,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. */ @@ -393,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 && @@ -407,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. @@ -765,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); } @@ -811,7 +797,7 @@ static void scsi_generic_class_initfn(ObjectClass *klass, void *data) sc->parse_cdb = scsi_generic_parse_cdb; dc->fw_name = "disk"; dc->desc = "pass through generic scsi device (/dev/sg*)"; - dc->reset = scsi_generic_reset; + device_class_set_legacy_reset(dc, scsi_generic_reset); device_class_set_props(dc, scsi_generic_properties); dc->vmsd = &vmstate_scsi_device; } diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 5bbbef64ef..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), @@ -1259,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 ab238293f0..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" diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index 18ea5dcfa1..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, 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); @@ -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 6a0fd0dfb1..22d16dc26b 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" @@ -39,6 +38,8 @@ static const int kernel_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; @@ -76,6 +77,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) { @@ -89,8 +91,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; } @@ -157,15 +160,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 = 0; + + /* 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; @@ -209,7 +266,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; } } @@ -231,6 +288,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; @@ -242,10 +306,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) { @@ -261,8 +324,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. */ @@ -298,6 +360,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 b7a71a802c..55e4be5b34 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" @@ -37,76 +36,238 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_EVENT_IDX, VIRTIO_SCSI_F_HOTPLUG, VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, 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 (vhost_dev_is_started(&vsc->dev) == 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) { + goto done; + } + s->connected = false; + + vhost_user_scsi_stop(s); + + vhost_dev_cleanup(&vsc->dev); + +done: + /* 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); + 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; @@ -116,18 +277,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--); - 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 +307,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 +322,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 +363,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 +401,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 20bb91766e..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,22 +149,18 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) memory_region_transaction_commit(); - /* - * These fields are visible to the IOThread so we rely on implicit barriers - * in aio_context_acquire() on the write side and aio_notify_accept() on - * the read side. - */ s->dataplane_starting = false; s->dataplane_started = true; + smp_wmb(); /* paired with aio_notify_accept() */ - 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); + 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); + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx); + } } - aio_context_release(s->ctx); return 0; fail_host_notifiers: @@ -176,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)); @@ -197,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 6f6e2e32ba..6637cfeaf5 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,115 @@ 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(); + } + +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 +433,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 +489,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++; - device_cold_reset(&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 +534,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) { - device_cold_reset(&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 +641,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) @@ -654,7 +761,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; @@ -670,7 +777,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; @@ -708,7 +815,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; } @@ -719,7 +826,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); } @@ -745,7 +852,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); @@ -772,9 +879,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, @@ -830,22 +935,39 @@ static void virtio_scsi_reset(VirtIODevice *vdev) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); assert(!s->dataplane_started); - s->resetting++; + + virtio_scsi_reset_tmf_bh(s); + + qatomic_inc(&s->resetting); bus_cold_reset(BUS(&s->bus)); - s->resetting--; + 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; @@ -871,27 +993,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); } } @@ -903,9 +1026,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) @@ -915,10 +1036,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); } } @@ -935,29 +1062,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); + 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)); - virtio_scsi_release(s); } } @@ -967,26 +1095,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); - scsi_bus_set_ua(&s->bus, SENSE_CODE(REPORTED_LUNS_CHANGED)); - 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); + } } } @@ -1004,6 +1188,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, @@ -1052,6 +1238,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, @@ -1089,8 +1278,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[] = { @@ -1117,7 +1309,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 fa76696855..57761b5594 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -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 } @@ -1332,7 +1333,7 @@ static void pvscsi_class_init(ObjectClass *klass, void *data) k->subsystem_id = 0x1000; device_class_set_parent_realize(dc, pvscsi_realize, &pvs_k->parent_dc_realize); - dc->reset = pvscsi_reset; + device_class_set_legacy_reset(dc, pvscsi_reset); dc->vmsd = &vmstate_pvscsi; device_class_set_props(dc, pvscsi_properties); set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index 92a0f42708..bcfb4c1322 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -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, @@ -459,6 +461,7 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, { AwSdHostState *s = AW_SDHOST(opaque); AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); + bool out_of_bounds = false; uint32_t res = 0; switch (offset) { @@ -577,13 +580,24 @@ static uint64_t allwinner_sdhost_read(void *opaque, hwaddr offset, case REG_SD_FIFO: /* Read/Write FIFO */ res = allwinner_sdhost_fifo_read(s); break; + case REG_SD_SAMP_DL: /* Sample Delay */ + if (sc->can_calibrate) { + res = s->sample_delay; + } else { + 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; } @@ -602,6 +616,7 @@ static void allwinner_sdhost_write(void *opaque, hwaddr offset, { AwSdHostState *s = AW_SDHOST(opaque); AwSdHostClass *sc = AW_SDHOST_GET_CLASS(s); + bool out_of_bounds = false; trace_allwinner_sdhost_write(offset, value, size); @@ -725,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; } } @@ -747,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), @@ -777,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() } }; @@ -815,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; @@ -855,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) @@ -868,7 +900,7 @@ static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = allwinner_sdhost_reset; + device_class_set_legacy_reset(dc, allwinner_sdhost_reset); dc->vmsd = &vmstate_allwinner_sdhost; dc->realize = allwinner_sdhost_realize; device_class_set_props(dc, allwinner_sdhost_properties); @@ -879,6 +911,7 @@ 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) @@ -886,6 +919,25 @@ 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 = { @@ -910,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, @@ -922,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..98d5460905 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -24,8 +24,10 @@ #define ASPEED_SDHCI_DEBOUNCE_RESET 0x00000005 #define ASPEED_SDHCI_BUS 0x08 #define ASPEED_SDHCI_SDIO_140 0x10 +#define ASPEED_SDHCI_SDIO_144 0x14 #define ASPEED_SDHCI_SDIO_148 0x18 #define ASPEED_SDHCI_SDIO_240 0x20 +#define ASPEED_SDHCI_SDIO_244 0x24 #define ASPEED_SDHCI_SDIO_248 0x28 #define ASPEED_SDHCI_WP_POL 0xec #define ASPEED_SDHCI_CARD_DET 0xf0 @@ -35,21 +37,27 @@ static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size) { - uint32_t val = 0; + uint64_t val = 0; AspeedSDHCIState *sdhci = opaque; switch (addr) { case ASPEED_SDHCI_SDIO_140: - val = (uint32_t)sdhci->slots[0].capareg; + val = extract64(sdhci->slots[0].capareg, 0, 32); + break; + case ASPEED_SDHCI_SDIO_144: + val = extract64(sdhci->slots[0].capareg, 32, 32); break; case ASPEED_SDHCI_SDIO_148: - val = (uint32_t)sdhci->slots[0].maxcurr; + val = extract64(sdhci->slots[0].maxcurr, 0, 32); break; case ASPEED_SDHCI_SDIO_240: - val = (uint32_t)sdhci->slots[1].capareg; + val = extract64(sdhci->slots[1].capareg, 0, 32); + break; + case ASPEED_SDHCI_SDIO_244: + val = extract64(sdhci->slots[1].capareg, 32, 32); break; case ASPEED_SDHCI_SDIO_248: - val = (uint32_t)sdhci->slots[1].maxcurr; + val = extract64(sdhci->slots[1].maxcurr, 0, 32); break; default: if (addr < ASPEED_SDHCI_REG_SIZE) { @@ -61,9 +69,9 @@ static uint64_t aspeed_sdhci_read(void *opaque, hwaddr addr, unsigned int size) } } - trace_aspeed_sdhci_read(addr, size, (uint64_t) val); + trace_aspeed_sdhci_read(addr, size, val); - return (uint64_t)val; + return val; } static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, @@ -79,16 +87,26 @@ static void aspeed_sdhci_write(void *opaque, hwaddr addr, uint64_t val, sdhci->regs[TO_REG(addr)] = (uint32_t)val & ~ASPEED_SDHCI_INFO_RESET; break; case ASPEED_SDHCI_SDIO_140: - sdhci->slots[0].capareg = (uint64_t)(uint32_t)val; + sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg, 0, 32, val); + break; + case ASPEED_SDHCI_SDIO_144: + sdhci->slots[0].capareg = deposit64(sdhci->slots[0].capareg, 32, 32, val); break; case ASPEED_SDHCI_SDIO_148: - sdhci->slots[0].maxcurr = (uint64_t)(uint32_t)val; + sdhci->slots[0].maxcurr = deposit64(sdhci->slots[0].maxcurr, + 0, 32, val); break; case ASPEED_SDHCI_SDIO_240: - sdhci->slots[1].capareg = (uint64_t)(uint32_t)val; + sdhci->slots[1].capareg = deposit64(sdhci->slots[1].capareg, + 0, 32, val); + break; + case ASPEED_SDHCI_SDIO_244: + sdhci->slots[1].capareg = deposit64(sdhci->slots[1].capareg, + 32, 32, val); break; case ASPEED_SDHCI_SDIO_248: - sdhci->slots[1].maxcurr = (uint64_t)(uint32_t)val; + sdhci->slots[1].maxcurr = deposit64(sdhci->slots[0].maxcurr, + 0, 32, val); break; default: if (addr < ASPEED_SDHCI_REG_SIZE) { @@ -177,7 +195,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(), }, @@ -193,21 +211,18 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) DeviceClass *dc = DEVICE_CLASS(classp); dc->realize = aspeed_sdhci_realize; - dc->reset = aspeed_sdhci_reset; + device_class_set_legacy_reset(dc, aspeed_sdhci_reset); dc->vmsd = &vmstate_aspeed_sdhci; 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..4e411ff798 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), @@ -432,28 +432,23 @@ static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = bcm2835_sdhost_reset; + device_class_set_legacy_reset(dc, bcm2835_sdhost_reset); 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..ad9daa20ed 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(), }, @@ -171,21 +171,18 @@ static void cadence_sdhci_class_init(ObjectClass *classp, void *data) dc->desc = "Cadence SD/SDIO/eMMC Host Controller (SD4HC)"; dc->realize = cadence_sdhci_realize; - dc->reset = cadence_sdhci_reset; + device_class_set_legacy_reset(dc, cadence_sdhci_reset); 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..4b30218b52 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -24,6 +24,7 @@ #include "hw/sd/sd.h" #include "qemu/module.h" #include "qapi/error.h" +#include "sdmmc-internal.h" #include "trace.h" static inline const char *sdbus_name(SDBus *sdbus) @@ -39,7 +40,7 @@ static SDState *get_card(SDBus *sdbus) if (!kid) { return NULL; } - return SD_CARD(kid->child); + return SDMMC_COMMON(kid->child); } uint8_t sdbus_get_dat_lines(SDBus *sdbus) @@ -48,7 +49,7 @@ uint8_t sdbus_get_dat_lines(SDBus *sdbus) uint8_t dat_lines = 0b1111; /* 4 bit bus width */ if (slave) { - SDCardClass *sc = SD_CARD_GET_CLASS(slave); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(slave); if (sc->get_dat_lines) { dat_lines = sc->get_dat_lines(slave); @@ -65,7 +66,7 @@ bool sdbus_get_cmd_line(SDBus *sdbus) bool cmd_line = true; if (slave) { - SDCardClass *sc = SD_CARD_GET_CLASS(slave); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(slave); if (sc->get_cmd_line) { cmd_line = sc->get_cmd_line(slave); @@ -82,7 +83,7 @@ void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts) trace_sdbus_set_voltage(sdbus_name(sdbus), millivolts); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); assert(sc->set_voltage); sc->set_voltage(card, millivolts); @@ -95,7 +96,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->do_command(card, req, response); } @@ -109,7 +110,7 @@ void sdbus_write_byte(SDBus *sdbus, uint8_t value) trace_sdbus_write(sdbus_name(sdbus), value); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); sc->write_byte(card, value); } @@ -121,7 +122,7 @@ void sdbus_write_data(SDBus *sdbus, const void *buf, size_t length) const uint8_t *data = buf; if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); for (size_t i = 0; i < length; i++) { trace_sdbus_write(sdbus_name(sdbus), data[i]); @@ -136,7 +137,7 @@ uint8_t sdbus_read_byte(SDBus *sdbus) uint8_t value = 0; if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); value = sc->read_byte(card); } @@ -151,7 +152,7 @@ void sdbus_read_data(SDBus *sdbus, void *buf, size_t length) uint8_t *data = buf; if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); for (size_t i = 0; i < length; i++) { data[i] = sc->read_byte(card); @@ -165,7 +166,7 @@ bool sdbus_receive_ready(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->receive_ready(card); } @@ -178,7 +179,7 @@ bool sdbus_data_ready(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->data_ready(card); } @@ -191,7 +192,7 @@ bool sdbus_get_inserted(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->get_inserted(card); } @@ -204,7 +205,7 @@ bool sdbus_get_readonly(SDBus *sdbus) SDState *card = get_card(sdbus); if (card) { - SDCardClass *sc = SD_CARD_GET_CLASS(card); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); return sc->get_readonly(card); } @@ -250,7 +251,7 @@ void sdbus_reparent_card(SDBus *from, SDBus *to) return; } - sc = SD_CARD_GET_CLASS(card); + sc = SDMMC_COMMON_GET_CLASS(card); readonly = sc->get_readonly(card); sdbus_set_inserted(from, false); @@ -259,16 +260,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..b43d45bc56 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -1,13 +1,12 @@ -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')) +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_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..99028c1a2c 100644 --- a/hw/sd/npcm7xx_sdhci.c +++ b/hw/sd/npcm7xx_sdhci.c @@ -16,6 +16,7 @@ #include "qemu/osdep.h" +#include "hw/sd/sdhci.h" #include "hw/sd/npcm7xx_sdhci.h" #include "migration/vmstate.h" #include "sdhci-internal.h" @@ -142,7 +143,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(), }, @@ -154,7 +155,7 @@ static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) dc->desc = "NPCM7xx SD/eMMC Host Controller"; dc->realize = npcm7xx_sdhci_realize; - dc->reset = npcm7xx_sdhci_reset; + device_class_set_legacy_reset(dc, npcm7xx_sdhci_reset); dc->vmsd = &vmstate_npcm7xx_sdhci; } @@ -162,21 +163,18 @@ static void npcm7xx_sdhci_instance_init(Object *obj) { NPCM7xxSDHCIState *s = NPCM7XX_SDHCI(obj); - object_initialize_child(OBJECT(s), "generic-sdhci", &s->sdhci, + object_initialize_child(OBJECT(s), TYPE_SYSBUS_SDHCI, &s->sdhci, 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..1d4e30e6b7 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -103,6 +103,7 @@ static void omap_mmc_fifolevel_update(struct omap_mmc_s *host) } } +/* These must match the encoding of the MMC_CMD Response field */ typedef enum { sd_nore = 0, /* no response */ sd_r1, /* normal response command */ @@ -112,8 +113,17 @@ typedef enum { sd_r1b = -1, } sd_rsp_type_t; +/* These must match the encoding of the MMC_CMD Type field */ +typedef enum { + SD_TYPE_BC = 0, /* broadcast -- no response */ + SD_TYPE_BCR = 1, /* broadcast with response */ + SD_TYPE_AC = 2, /* addressed -- no data transfer */ + SD_TYPE_ADTC = 3, /* addressed with data transfer */ +} MMCCmdType; + static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, - sd_cmd_type_t type, int busy, sd_rsp_type_t resptype, int init) + MMCCmdType type, int busy, + sd_rsp_type_t resptype, int init) { uint32_t rspstatus, mask; int rsplen, timeout; @@ -128,7 +138,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, if (resptype == sd_r1 && busy) resptype = sd_r1b; - if (type == sd_adtc) { + if (type == SD_TYPE_ADTC) { host->fifo_start = 0; host->fifo_len = 0; host->transfer = 1; @@ -321,11 +331,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 +427,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); @@ -434,10 +443,10 @@ static void omap_mmc_write(void *opaque, hwaddr offset, for (i = 0; i < 8; i ++) s->rsp[i] = 0x0000; omap_mmc_command(s, value & 63, (value >> 15) & 1, - (sd_cmd_type_t) ((value >> 12) & 3), - (value >> 11) & 1, - (sd_rsp_type_t) ((value >> 8) & 7), - (value >> 7) & 1); + (MMCCmdType)((value >> 12) & 3), + (value >> 11) & 1, + (sd_rsp_type_t) ((value >> 8) & 7), + (value >> 7) & 1); omap_mmc_update(s); break; @@ -574,24 +583,6 @@ static const MemoryRegionOps omap_mmc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void omap_mmc_cover_cb(void *opaque, int line, int level) -{ - struct omap_mmc_s *host = (struct omap_mmc_s *) opaque; - - if (!host->cdet_state && level) { - host->status |= 0x0002; - omap_mmc_interrupts_update(host); - if (host->cdet_wakeup) { - /* TODO: Assert wake-up */ - } - } - - if (host->cdet_state != level) { - qemu_set_irq(host->coverswitch, level); - host->cdet_state = level; - } -} - struct omap_mmc_s *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, BlockBackend *blk, @@ -618,48 +609,3 @@ struct omap_mmc_s *omap_mmc_init(hwaddr base, return s; } - -struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta, - BlockBackend *blk, qemu_irq irq, qemu_irq dma[], - omap_clk fclk, omap_clk iclk) -{ - struct omap_mmc_s *s = g_new0(struct omap_mmc_s, 1); - - s->irq = irq; - s->dma = dma; - s->clk = fclk; - s->lines = 4; - s->rev = 2; - - memory_region_init_io(&s->iomem, NULL, &omap_mmc_ops, s, "omap.mmc", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - /* Instantiate the storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } - - s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0); - sd_set_cb(s->card, NULL, s->cdet); - - omap_mmc_reset(s); - - return s; -} - -void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover) -{ - if (s->cdet) { - sd_set_cb(s->card, ro, s->cdet); - s->coverswitch = cover; - qemu_set_irq(cover, s->cdet_state); - } else - sd_set_cb(s->card, ro, cover); -} - -void omap_mmc_enable(struct omap_mmc_s *s, int enable) -{ - sd_enable(s->card, enable); -} diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 5e554bd467..51b10cadca 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), @@ -514,19 +514,11 @@ static void pl181_class_init(ObjectClass *klass, void *data) DeviceClass *k = DEVICE_CLASS(klass); k->vmsd = &vmstate_pl181; - k->reset = pl181_reset; + device_class_set_legacy_reset(k, pl181_reset); /* Reason: output IRQs should be wired up */ 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 deleted file mode 100644 index 124fbf8bbd..0000000000 --- a/hw/sd/pxa2xx_mmci.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Intel XScale PXA255/270 MultiMediaCard/SD/SDIO Controller emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * 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 "qapi/error.h" -#include "hw/irq.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "hw/arm/pxa.h" -#include "hw/sd/sd.h" -#include "hw/qdev-properties.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "trace.h" -#include "qom/object.h" - -#define TYPE_PXA2XX_MMCI_BUS "pxa2xx-mmci-bus" -/* This is reusing the SDBus typedef from SD_BUS */ -DECLARE_INSTANCE_CHECKER(SDBus, PXA2XX_MMCI_BUS, - TYPE_PXA2XX_MMCI_BUS) - -struct PXA2xxMMCIState { - SysBusDevice parent_obj; - - MemoryRegion iomem; - qemu_irq irq; - qemu_irq rx_dma; - qemu_irq tx_dma; - qemu_irq inserted; - qemu_irq readonly; - - BlockBackend *blk; - SDBus sdbus; - - uint32_t status; - uint32_t clkrt; - uint32_t spi; - uint32_t cmdat; - uint32_t resp_tout; - uint32_t read_tout; - int32_t blklen; - int32_t numblk; - uint32_t intmask; - uint32_t intreq; - int32_t cmd; - uint32_t arg; - - int32_t active; - int32_t bytesleft; - uint8_t tx_fifo[64]; - uint32_t tx_start; - uint32_t tx_len; - uint8_t rx_fifo[32]; - uint32_t rx_start; - uint32_t rx_len; - uint16_t resp_fifo[9]; - uint32_t resp_len; - - int32_t cmdreq; -}; - -static bool pxa2xx_mmci_vmstate_validate(void *opaque, int version_id) -{ - PXA2xxMMCIState *s = opaque; - - return s->tx_start < ARRAY_SIZE(s->tx_fifo) - && s->rx_start < ARRAY_SIZE(s->rx_fifo) - && s->tx_len <= ARRAY_SIZE(s->tx_fifo) - && s->rx_len <= ARRAY_SIZE(s->rx_fifo) - && s->resp_len <= ARRAY_SIZE(s->resp_fifo); -} - - -static const VMStateDescription vmstate_pxa2xx_mmci = { - .name = "pxa2xx-mmci", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32(status, PXA2xxMMCIState), - VMSTATE_UINT32(clkrt, PXA2xxMMCIState), - VMSTATE_UINT32(spi, PXA2xxMMCIState), - VMSTATE_UINT32(cmdat, PXA2xxMMCIState), - VMSTATE_UINT32(resp_tout, PXA2xxMMCIState), - VMSTATE_UINT32(read_tout, PXA2xxMMCIState), - VMSTATE_INT32(blklen, PXA2xxMMCIState), - VMSTATE_INT32(numblk, PXA2xxMMCIState), - VMSTATE_UINT32(intmask, PXA2xxMMCIState), - VMSTATE_UINT32(intreq, PXA2xxMMCIState), - VMSTATE_INT32(cmd, PXA2xxMMCIState), - VMSTATE_UINT32(arg, PXA2xxMMCIState), - VMSTATE_INT32(cmdreq, PXA2xxMMCIState), - VMSTATE_INT32(active, PXA2xxMMCIState), - VMSTATE_INT32(bytesleft, PXA2xxMMCIState), - VMSTATE_UINT32(tx_start, PXA2xxMMCIState), - VMSTATE_UINT32(tx_len, PXA2xxMMCIState), - VMSTATE_UINT32(rx_start, PXA2xxMMCIState), - VMSTATE_UINT32(rx_len, PXA2xxMMCIState), - VMSTATE_UINT32(resp_len, PXA2xxMMCIState), - VMSTATE_VALIDATE("fifo size incorrect", pxa2xx_mmci_vmstate_validate), - VMSTATE_UINT8_ARRAY(tx_fifo, PXA2xxMMCIState, 64), - VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxMMCIState, 32), - VMSTATE_UINT16_ARRAY(resp_fifo, PXA2xxMMCIState, 9), - VMSTATE_END_OF_LIST() - } -}; - -#define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */ -#define MMC_STAT 0x04 /* MMC Status register */ -#define MMC_CLKRT 0x08 /* MMC Clock Rate register */ -#define MMC_SPI 0x0c /* MMC SPI Mode register */ -#define MMC_CMDAT 0x10 /* MMC Command/Data register */ -#define MMC_RESTO 0x14 /* MMC Response Time-Out register */ -#define MMC_RDTO 0x18 /* MMC Read Time-Out register */ -#define MMC_BLKLEN 0x1c /* MMC Block Length register */ -#define MMC_NUMBLK 0x20 /* MMC Number of Blocks register */ -#define MMC_PRTBUF 0x24 /* MMC Buffer Partly Full register */ -#define MMC_I_MASK 0x28 /* MMC Interrupt Mask register */ -#define MMC_I_REG 0x2c /* MMC Interrupt Request register */ -#define MMC_CMD 0x30 /* MMC Command register */ -#define MMC_ARGH 0x34 /* MMC Argument High register */ -#define MMC_ARGL 0x38 /* MMC Argument Low register */ -#define MMC_RES 0x3c /* MMC Response FIFO */ -#define MMC_RXFIFO 0x40 /* MMC Receive FIFO */ -#define MMC_TXFIFO 0x44 /* MMC Transmit FIFO */ -#define MMC_RDWAIT 0x48 /* MMC RD_WAIT register */ -#define MMC_BLKS_REM 0x4c /* MMC Blocks Remaining register */ - -/* Bitfield masks */ -#define STRPCL_STOP_CLK (1 << 0) -#define STRPCL_STRT_CLK (1 << 1) -#define STAT_TOUT_RES (1 << 1) -#define STAT_CLK_EN (1 << 8) -#define STAT_DATA_DONE (1 << 11) -#define STAT_PRG_DONE (1 << 12) -#define STAT_END_CMDRES (1 << 13) -#define SPI_SPI_MODE (1 << 0) -#define CMDAT_RES_TYPE (3 << 0) -#define CMDAT_DATA_EN (1 << 2) -#define CMDAT_WR_RD (1 << 3) -#define CMDAT_DMA_EN (1 << 7) -#define CMDAT_STOP_TRAN (1 << 10) -#define INT_DATA_DONE (1 << 0) -#define INT_PRG_DONE (1 << 1) -#define INT_END_CMD (1 << 2) -#define INT_STOP_CMD (1 << 3) -#define INT_CLK_OFF (1 << 4) -#define INT_RXFIFO_REQ (1 << 5) -#define INT_TXFIFO_REQ (1 << 6) -#define INT_TINT (1 << 7) -#define INT_DAT_ERR (1 << 8) -#define INT_RES_ERR (1 << 9) -#define INT_RD_STALLED (1 << 10) -#define INT_SDIO_INT (1 << 11) -#define INT_SDIO_SACK (1 << 12) -#define PRTBUF_PRT_BUF (1 << 0) - -/* Route internal interrupt lines to the global IC and DMA */ -static void pxa2xx_mmci_int_update(PXA2xxMMCIState *s) -{ - uint32_t mask = s->intmask; - if (s->cmdat & CMDAT_DMA_EN) { - mask |= INT_RXFIFO_REQ | INT_TXFIFO_REQ; - - qemu_set_irq(s->rx_dma, !!(s->intreq & INT_RXFIFO_REQ)); - qemu_set_irq(s->tx_dma, !!(s->intreq & INT_TXFIFO_REQ)); - } - - qemu_set_irq(s->irq, !!(s->intreq & ~mask)); -} - -static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s) -{ - if (!s->active) - return; - - if (s->cmdat & CMDAT_WR_RD) { - while (s->bytesleft && s->tx_len) { - sdbus_write_byte(&s->sdbus, s->tx_fifo[s->tx_start++]); - s->tx_start &= 0x1f; - s->tx_len --; - s->bytesleft --; - } - if (s->bytesleft) - s->intreq |= INT_TXFIFO_REQ; - } else - while (s->bytesleft && s->rx_len < 32) { - s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] = - sdbus_read_byte(&s->sdbus); - s->bytesleft --; - s->intreq |= INT_RXFIFO_REQ; - } - - if (!s->bytesleft) { - s->active = 0; - s->intreq |= INT_DATA_DONE; - s->status |= STAT_DATA_DONE; - - if (s->cmdat & CMDAT_WR_RD) { - s->intreq |= INT_PRG_DONE; - s->status |= STAT_PRG_DONE; - } - } - - pxa2xx_mmci_int_update(s); -} - -static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s) -{ - int rsplen, i; - SDRequest request; - uint8_t response[16]; - - s->active = 1; - s->rx_len = 0; - s->tx_len = 0; - s->cmdreq = 0; - - request.cmd = s->cmd; - request.arg = s->arg; - request.crc = 0; /* FIXME */ - - rsplen = sdbus_do_command(&s->sdbus, &request, response); - s->intreq |= INT_END_CMD; - - memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); - switch (s->cmdat & CMDAT_RES_TYPE) { -#define PXAMMCI_RESP(wd, value0, value1) \ - s->resp_fifo[(wd) + 0] |= (value0); \ - s->resp_fifo[(wd) + 1] |= (value1) << 8; - case 0: /* No response */ - goto complete; - - case 1: /* R1, R4, R5 or R6 */ - if (rsplen < 4) - goto timeout; - goto complete; - - case 2: /* R2 */ - if (rsplen < 16) - goto timeout; - goto complete; - - case 3: /* R3 */ - if (rsplen < 4) - goto timeout; - goto complete; - - complete: - for (i = 0; rsplen > 0; i ++, rsplen -= 2) { - PXAMMCI_RESP(i, response[i * 2], response[i * 2 + 1]); - } - s->status |= STAT_END_CMDRES; - - if (!(s->cmdat & CMDAT_DATA_EN)) - s->active = 0; - else - s->bytesleft = s->numblk * s->blklen; - - s->resp_len = 0; - break; - - timeout: - s->active = 0; - s->status |= STAT_TOUT_RES; - break; - } - - pxa2xx_mmci_fifo_update(s); -} - -static uint64_t pxa2xx_mmci_read(void *opaque, hwaddr offset, unsigned size) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - uint32_t ret = 0; - - switch (offset) { - case MMC_STRPCL: - break; - case MMC_STAT: - ret = s->status; - break; - case MMC_CLKRT: - ret = s->clkrt; - break; - case MMC_SPI: - ret = s->spi; - break; - case MMC_CMDAT: - ret = s->cmdat; - break; - case MMC_RESTO: - ret = s->resp_tout; - break; - case MMC_RDTO: - ret = s->read_tout; - break; - case MMC_BLKLEN: - ret = s->blklen; - break; - case MMC_NUMBLK: - ret = s->numblk; - break; - case MMC_PRTBUF: - break; - case MMC_I_MASK: - ret = s->intmask; - break; - case MMC_I_REG: - ret = s->intreq; - break; - case MMC_CMD: - ret = s->cmd | 0x40; - break; - case MMC_ARGH: - ret = s->arg >> 16; - break; - case MMC_ARGL: - ret = s->arg & 0xffff; - break; - case MMC_RES: - ret = (s->resp_len < 9) ? s->resp_fifo[s->resp_len++] : 0; - break; - case MMC_RXFIFO: - while (size-- && s->rx_len) { - ret |= s->rx_fifo[s->rx_start++] << (size << 3); - s->rx_start &= 0x1f; - s->rx_len --; - } - s->intreq &= ~INT_RXFIFO_REQ; - pxa2xx_mmci_fifo_update(s); - break; - case MMC_RDWAIT: - break; - case MMC_BLKS_REM: - ret = s->numblk; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: incorrect register 0x%02" HWADDR_PRIx "\n", - __func__, offset); - } - trace_pxa2xx_mmci_read(size, offset, ret); - - return ret; -} - -static void pxa2xx_mmci_write(void *opaque, - hwaddr offset, uint64_t value, unsigned size) -{ - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - - trace_pxa2xx_mmci_write(size, offset, value); - switch (offset) { - case MMC_STRPCL: - if (value & STRPCL_STRT_CLK) { - s->status |= STAT_CLK_EN; - s->intreq &= ~INT_CLK_OFF; - - if (s->cmdreq && !(s->cmdat & CMDAT_STOP_TRAN)) { - s->status &= STAT_CLK_EN; - pxa2xx_mmci_wakequeues(s); - } - } - - if (value & STRPCL_STOP_CLK) { - s->status &= ~STAT_CLK_EN; - s->intreq |= INT_CLK_OFF; - s->active = 0; - } - - pxa2xx_mmci_int_update(s); - break; - - case MMC_CLKRT: - s->clkrt = value & 7; - break; - - case MMC_SPI: - s->spi = value & 0xf; - if (value & SPI_SPI_MODE) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: attempted to use card in SPI mode\n", __func__); - } - break; - - case MMC_CMDAT: - s->cmdat = value & 0x3dff; - s->active = 0; - s->cmdreq = 1; - if (!(value & CMDAT_STOP_TRAN)) { - s->status &= STAT_CLK_EN; - - if (s->status & STAT_CLK_EN) - pxa2xx_mmci_wakequeues(s); - } - - pxa2xx_mmci_int_update(s); - break; - - case MMC_RESTO: - s->resp_tout = value & 0x7f; - break; - - case MMC_RDTO: - s->read_tout = value & 0xffff; - break; - - case MMC_BLKLEN: - s->blklen = value & 0xfff; - break; - - case MMC_NUMBLK: - s->numblk = value & 0xffff; - break; - - case MMC_PRTBUF: - if (value & PRTBUF_PRT_BUF) { - s->tx_start ^= 32; - s->tx_len = 0; - } - pxa2xx_mmci_fifo_update(s); - break; - - case MMC_I_MASK: - s->intmask = value & 0x1fff; - pxa2xx_mmci_int_update(s); - break; - - case MMC_CMD: - s->cmd = value & 0x3f; - break; - - case MMC_ARGH: - s->arg &= 0x0000ffff; - s->arg |= value << 16; - break; - - case MMC_ARGL: - s->arg &= 0xffff0000; - s->arg |= value & 0x0000ffff; - break; - - case MMC_TXFIFO: - while (size-- && s->tx_len < 0x20) - s->tx_fifo[(s->tx_start + (s->tx_len ++)) & 0x1f] = - (value >> (size << 3)) & 0xff; - s->intreq &= ~INT_TXFIFO_REQ; - pxa2xx_mmci_fifo_update(s); - break; - - case MMC_RDWAIT: - case MMC_BLKS_REM: - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: incorrect reg 0x%02" HWADDR_PRIx " " - "(value 0x%08" PRIx64 ")\n", __func__, offset, value); - } -} - -static const MemoryRegionOps pxa2xx_mmci_ops = { - .read = pxa2xx_mmci_read, - .write = pxa2xx_mmci_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, - hwaddr base, - 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); - 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); -} - -static void pxa2xx_mmci_set_inserted(DeviceState *dev, bool inserted) -{ - PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - - qemu_set_irq(s->inserted, inserted); -} - -static void pxa2xx_mmci_set_readonly(DeviceState *dev, bool readonly) -{ - PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - - qemu_set_irq(s->readonly, readonly); -} - -void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, - qemu_irq coverswitch) -{ - DeviceState *dev = DEVICE(s); - - s->readonly = readonly; - s->inserted = coverswitch; - - pxa2xx_mmci_set_inserted(dev, sdbus_get_inserted(&s->sdbus)); - pxa2xx_mmci_set_readonly(dev, sdbus_get_readonly(&s->sdbus)); -} - -static void pxa2xx_mmci_reset(DeviceState *d) -{ - PXA2xxMMCIState *s = PXA2XX_MMCI(d); - - s->status = 0; - s->clkrt = 0; - s->spi = 0; - s->cmdat = 0; - s->resp_tout = 0; - s->read_tout = 0; - s->blklen = 0; - s->numblk = 0; - s->intmask = 0; - s->intreq = 0; - s->cmd = 0; - s->arg = 0; - s->active = 0; - s->bytesleft = 0; - s->tx_start = 0; - s->tx_len = 0; - s->rx_start = 0; - s->rx_len = 0; - s->resp_len = 0; - s->cmdreq = 0; - memset(s->tx_fifo, 0, sizeof(s->tx_fifo)); - memset(s->rx_fifo, 0, sizeof(s->rx_fifo)); - memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); -} - -static void pxa2xx_mmci_instance_init(Object *obj) -{ - PXA2xxMMCIState *s = PXA2XX_MMCI(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - DeviceState *dev = DEVICE(obj); - - memory_region_init_io(&s->iomem, obj, &pxa2xx_mmci_ops, s, - "pxa2xx-mmci", 0x00100000); - sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_out_named(dev, &s->rx_dma, "rx-dma", 1); - qdev_init_gpio_out_named(dev, &s->tx_dma, "tx-dma", 1); - - qbus_init(&s->sdbus, sizeof(s->sdbus), - TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus"); -} - -static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_pxa2xx_mmci; - dc->reset = pxa2xx_mmci_reset; -} - -static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data) -{ - SDBusClass *sbc = SD_BUS_CLASS(klass); - - sbc->set_inserted = pxa2xx_mmci_set_inserted; - 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_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) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index da5bdd134a..f9bd03f3fd 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2,6 +2,8 @@ * SD Memory Card emulation as defined in the "SD Memory Card Physical * layer specification, Version 2.00." * + * eMMC emulation defined in "JEDEC Standard No. 84-A43" + * * Copyright (c) 2006 Andrzej Zaborowski * Copyright (c) 2007 CodeSourcery * Copyright (c) 2018 Philippe Mathieu-Daudé @@ -46,6 +48,7 @@ #include "qemu/error-report.h" #include "qemu/timer.h" #include "qemu/log.h" +#include "qemu/guest-random.h" #include "qemu/module.h" #include "sdmmc-internal.h" #include "trace.h" @@ -68,6 +71,14 @@ typedef enum { sd_illegal = -2, } sd_rsp_type_t; +typedef enum { + sd_spi, + sd_bc, /* broadcast -- no response */ + sd_bcr, /* broadcast with response */ + sd_ac, /* addressed -- no data transfer */ + sd_adtc, /* addressed with data transfer */ +} sd_cmd_type_t; + enum SDCardModes { sd_inactive, sd_card_identification_mode, @@ -75,18 +86,37 @@ enum SDCardModes { }; enum SDCardStates { - sd_inactive_state = -1, - sd_idle_state = 0, - sd_ready_state, - sd_identification_state, - sd_standby_state, - sd_transfer_state, - sd_sendingdata_state, - sd_receivingdata_state, - sd_programming_state, - sd_disconnect_state, + sd_waitirq_state = -2, /* emmc */ + sd_inactive_state = -1, + + sd_idle_state = 0, + sd_ready_state = 1, + sd_identification_state = 2, + sd_standby_state = 3, + sd_transfer_state = 4, + sd_sendingdata_state = 5, + sd_receivingdata_state = 6, + sd_programming_state = 7, + sd_disconnect_state = 8, + sd_bus_test_state = 9, /* emmc */ + sd_sleep_state = 10, /* emmc */ + sd_io_state = 15 /* sd */ }; +#define SDMMC_CMD_MAX 64 + +typedef sd_rsp_type_t (*sd_cmd_handler)(SDState *sd, SDRequest req); + +typedef struct SDProto { + const char *name; + struct { + const unsigned class; + const sd_cmd_type_t type; + const char *name; + sd_cmd_handler handler; + } cmd[SDMMC_CMD_MAX], acmd[SDMMC_CMD_MAX]; +} SDProto; + struct SDState { DeviceState parent_obj; @@ -102,12 +132,22 @@ struct SDState { uint16_t rca; uint32_t card_status; uint8_t sd_status[64]; + union { + uint8_t ext_csd[512]; + struct { + uint8_t ext_csd_rw[192]; /* Modes segment */ + uint8_t ext_csd_ro[320]; /* Properties segment */ + }; + }; /* Static properties */ uint8_t spec_version; + uint64_t boot_part_size; BlockBackend *blk; - bool spi; + uint8_t boot_config; + + const SDProto *proto; /* Runtime changeables */ @@ -126,18 +166,20 @@ struct SDState { uint32_t pwd_len; uint8_t function_group[6]; uint8_t current_cmd; + const char *last_cmd_name; /* True if we will handle the next command as an ACMD. Note that this does * *not* track the APP_CMD status bit! */ bool expecting_acmd; uint32_t blk_written; + uint64_t data_start; uint32_t data_offset; + size_t data_size; uint8_t data[512]; 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 +187,43 @@ struct SDState { static void sd_realize(DeviceState *dev, Error **errp); +static const SDProto sd_proto_spi; +static const SDProto sd_proto_emmc; + +static bool sd_is_spi(SDState *sd) +{ + return sd->proto == &sd_proto_spi; +} + +static bool sd_is_emmc(SDState *sd) +{ + return sd->proto == &sd_proto_emmc; +} + +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_mode_name(enum SDCardModes mode) +{ + static const char *mode_name[] = { + [sd_inactive] = "inactive", + [sd_card_identification_mode] = "identification", + [sd_data_transfer_mode] = "transfer", + }; + assert(mode < ARRAY_SIZE(mode_name)); + return mode_name[mode]; +} + static const char *sd_state_name(enum SDCardStates state) { static const char *state_name[] = { @@ -154,13 +233,19 @@ static const char *sd_state_name(enum SDCardStates state) [sd_standby_state] = "standby", [sd_transfer_state] = "transfer", [sd_sendingdata_state] = "sendingdata", + [sd_bus_test_state] = "bus-test", [sd_receivingdata_state] = "receivingdata", [sd_programming_state] = "programming", [sd_disconnect_state] = "disconnect", + [sd_sleep_state] = "sleep", + [sd_io_state] = "i/o" }; if (state == sd_inactive_state) { return "inactive"; } + if (state == sd_waitirq_state) { + return "wait-irq"; + } assert(state < ARRAY_SIZE(state_name)); return state_name[state]; } @@ -186,6 +271,32 @@ static const char *sd_response_name(sd_rsp_type_t rsp) return response_name[rsp]; } +static const char *sd_cmd_name(SDState *sd, uint8_t cmd) +{ + static const char *cmd_abbrev[SDMMC_CMD_MAX] = { + [18] = "READ_MULTIPLE_BLOCK", + [25] = "WRITE_MULTIPLE_BLOCK", + }; + const SDProto *sdp = sd->proto; + + if (sdp->cmd[cmd].handler) { + assert(!cmd_abbrev[cmd]); + return sdp->cmd[cmd].name; + } + return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; +} + +static const char *sd_acmd_name(SDState *sd, uint8_t cmd) +{ + const SDProto *sdp = sd->proto; + + if (sdp->acmd[cmd].handler) { + return sdp->acmd[cmd].name; + } + + return "UNKNOWN_ACMD"; +} + static uint8_t sd_get_dat_lines(SDState *sd) { return sd->enable ? sd->dat_lines : 0; @@ -234,27 +345,6 @@ static void sd_set_mode(SDState *sd) } } -static const sd_cmd_type_t sd_cmd_type[SDMMC_CMD_MAX] = { - sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac, - sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac, - /* 16 */ - sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, - sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none, - /* 32 */ - sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, - sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none, - /* 48 */ - sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, - sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, -}; - -static const int sd_cmd_class[SDMMC_CMD_MAX] = { - 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0, - 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, - 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7, - 7, 7, 10, 7, 9, 9, 9, 8, 8, 10, 8, 8, 8, 8, 8, 8, -}; - static uint8_t sd_crc7(const void *message, size_t width) { int i, bit; @@ -271,6 +361,8 @@ static uint8_t sd_crc7(const void *message, size_t width) return shift_reg; } +/* Operation Conditions register */ + #define OCR_POWER_DELAY_NS 500000 /* 0.5ms */ FIELD(OCR, VDD_VOLTAGE_WINDOW, 0, 24) @@ -309,7 +401,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. @@ -320,6 +412,8 @@ static void sd_set_ocr(SDState *sd) } } +/* SD Configuration register */ + static void sd_set_scr(SDState *sd) { sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */ @@ -342,45 +436,136 @@ 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 +/* Card IDentification register */ + +#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[10] = 0xad; - sd->cid[11] = 0xbe; - sd->cid[12] = 0xef; - sd->cid[13] = 0x00 | /* Manufacture date (MDT) */ + sd->cid[8] = PRV; /* Fake product revision (PRV) */ + stl_be_p(&sd->cid[9], 0xdeadbeef); /* Fake serial number (PSN) */ + 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)) +static void emmc_set_cid(SDState *sd) +{ + sd->cid[0] = MID; /* Fake card manufacturer ID (MID) */ + sd->cid[1] = 0b01; /* CBX: soldered BGA */ + sd->cid[2] = OID[0]; /* OEM/Application ID (OID) */ + 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] = PNM[4]; + sd->cid[9] = PRV; /* Fake product revision (PRV) */ + stl_be_p(&sd->cid[10], 0xdeadbeef); /* Fake serial number (PSN) */ + sd->cid[14] = (MDT_MON << 4) | (MDT_YR - 1997); /* Manufacture date (MDT) */ + sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1; +} + +/* Card-Specific Data register */ + +#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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe, }; +static void emmc_set_ext_csd(SDState *sd, uint64_t size) +{ + uint32_t sectcount = size >> HWBLOCK_SHIFT; + + memset(sd->ext_csd, 0, sizeof(sd->ext_csd)); /* FIXME only RW at reset */ + + /* Properties segment (RO) */ + sd->ext_csd[EXT_CSD_S_CMD_SET] = 0b1; /* supported command sets */ + sd->ext_csd[EXT_CSD_BOOT_INFO] = 0x0; /* Boot information */ + /* Boot partition size. 128KB unit */ + sd->ext_csd[EXT_CSD_BOOT_MULT] = sd->boot_part_size / (128 * KiB); + sd->ext_csd[EXT_CSD_ACC_SIZE] = 0x1; /* Access size */ + sd->ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] = 0x01; /* HC Erase unit size */ + sd->ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] = 0x01; /* HC erase timeout */ + sd->ext_csd[EXT_CSD_REL_WR_SEC_C] = 0x1; /* Reliable write sector count */ + sd->ext_csd[EXT_CSD_HC_WP_GRP_SIZE] = 0x01; /* HC write protect group size */ + sd->ext_csd[EXT_CSD_S_C_VCC] = 0x01; /* Sleep current VCC */ + sd->ext_csd[EXT_CSD_S_C_VCCQ] = 0x01; /* Sleep current VCCQ */ + sd->ext_csd[EXT_CSD_S_A_TIMEOUT] = 0x01; /* Sleep/Awake timeout */ + stl_le_p(&sd->ext_csd[EXT_CSD_SEC_CNT], sectcount); /* Sector count */ + sd->ext_csd[210] = 0x46; /* Min write perf for 8bit@52Mhz */ + sd->ext_csd[209] = 0x46; /* Min read perf for 8bit@52Mhz */ + sd->ext_csd[208] = 0x46; /* Min write perf for 4bit@52Mhz */ + sd->ext_csd[207] = 0x46; /* Min read perf for 4bit@52Mhz */ + sd->ext_csd[206] = 0x46; /* Min write perf for 4bit@26Mhz */ + sd->ext_csd[205] = 0x46; /* Min read perf for 4bit@26Mhz */ + sd->ext_csd[EXT_CSD_CARD_TYPE] = 0b11; + sd->ext_csd[EXT_CSD_STRUCTURE] = 2; + sd->ext_csd[EXT_CSD_REV] = 3; + + /* Mode segment (RW) */ + sd->ext_csd[EXT_CSD_PART_CONFIG] = sd->boot_config; +} + +static void emmc_set_csd(SDState *sd, uint64_t size) +{ + int hwblock_shift = HWBLOCK_SHIFT; + uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; + uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; + + sd->csd[0] = (3 << 6) | (4 << 2); /* Spec v4.3 with EXT_CSD */ + sd->csd[1] = (1 << 3) | 6; /* Asynchronous data access time: 1ms */ + sd->csd[2] = 0x00; + sd->csd[3] = (1 << 3) | 3;; /* Maximum bus clock frequency: 100MHz */ + sd->csd[4] = 0x0f; + if (size <= 2 * GiB) { + /* use 1k blocks */ + uint32_t csize1k = (size >> (CMULT_SHIFT + 10)) - 1; + sd->csd[5] = 0x5a; + sd->csd[6] = 0x80 | ((csize1k >> 10) & 0xf); + sd->csd[7] = (csize1k >> 2) & 0xff; + } else { /* >= 2GB : size stored in ext CSD, block addressing */ + sd->csd[5] = 0x59; + sd->csd[6] = 0x8f; + sd->csd[7] = 0xff; + sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1); + } + sd->csd[8] = 0xff; + sd->csd[9] = 0xfc | /* Max. write current */ + ((CMULT_SHIFT - 2) >> 1); + sd->csd[10] = 0x40 | /* Erase sector size */ + (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1); + sd->csd[11] = 0x00 | /* Write protect group size */ + ((sectsize << 7) & 0x80) | wpsize; + sd->csd[12] = 0x90 | /* Write speed factor */ + (hwblock_shift >> 2); + sd->csd[13] = 0x20 | /* Max. write data block length */ + ((hwblock_shift << 6) & 0xc0); + sd->csd[14] = 0x00; + sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; + emmc_set_ext_csd(sd, size); +} + static void sd_set_csd(SDState *sd, uint64_t size) { int hwblock_shift = HWBLOCK_SHIFT; @@ -395,31 +580,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; @@ -429,9 +614,7 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[4] = 0x5b; sd->csd[5] = 0x59; sd->csd[6] = 0x00; - sd->csd[7] = (size >> 16) & 0xff; - sd->csd[8] = (size >> 8) & 0xff; - sd->csd[9] = (size & 0xff); + st24_be_p(&sd->csd[7], size); sd->csd[10] = 0x7f; sd->csd[11] = 0x80; sd->csd[12] = 0x0a; @@ -441,14 +624,37 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; } -static void sd_set_rca(SDState *sd) +/* Relative Card Address register */ + +static void sd_set_rca(SDState *sd, uint16_t value) { - sd->rca += 0x4567; + trace_sdcard_set_rca(value); + sd->rca = value; } +static uint16_t sd_req_get_rca(SDState *s, SDRequest req) +{ + switch (s->proto->cmd[req.cmd].type) { + case sd_ac: + case sd_adtc: + return req.arg >> 16; + case sd_spi: + default: + g_assert_not_reached(); + } +} + +static bool sd_req_rca_same(SDState *s, SDRequest req) +{ + return sd_req_get_rca(s, req) == s->rca; +} + +/* Card Status register */ + FIELD(CSR, AKE_SEQ_ERROR, 3, 1) FIELD(CSR, APP_CMD, 5, 1) FIELD(CSR, FX_EVENT, 6, 1) +FIELD(CSR, SWITCH_ERROR, 7, 1) FIELD(CSR, READY_FOR_DATA, 8, 1) FIELD(CSR, CURRENT_STATE, 9, 4) FIELD(CSR, ERASE_RESET, 13, 1) @@ -499,7 +705,7 @@ FIELD(CSR, OUT_OF_RANGE, 31, 1) static void sd_set_cardstatus(SDState *sd) { - sd->card_status = 0x00000100; + sd->card_status = READY_FOR_DATA; } static void sd_set_sdstatus(SDState *sd) @@ -507,13 +713,28 @@ static void sd_set_sdstatus(SDState *sd) memset(sd->sd_status, 0, 64); } +static const uint8_t sd_tuning_block_pattern4[64] = { + /* + * See: Physical Layer Simplified Specification Version 3.01, + * Table 4-2. + */ + 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde +}; + static int sd_req_crc_validate(SDRequest *req) { uint8_t buffer[5]; 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) @@ -546,6 +767,54 @@ static void sd_response_r7_make(SDState *sd, uint8_t *response) stl_be_p(response, sd->vhs); } +static uint32_t sd_blk_len(SDState *sd) +{ + if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + return 1 << HWBLOCK_SHIFT; + } + return sd->blk_len; +} + +/* + * This requires a disk image that has two boot partitions inserted at the + * beginning of it. The size of the boot partitions is the "boot-size" + * property. + */ +static uint32_t sd_bootpart_offset(SDState *sd) +{ + unsigned partition_access; + + if (!sd->boot_part_size || !sd_is_emmc(sd)) { + return 0; + } + + partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG] + & EXT_CSD_PART_CONFIG_ACC_MASK; + switch (partition_access) { + case EXT_CSD_PART_CONFIG_ACC_DEFAULT: + return sd->boot_part_size * 2; + case EXT_CSD_PART_CONFIG_ACC_BOOT0: + return 0; + case EXT_CSD_PART_CONFIG_ACC_BOOT0 + 1: + return sd->boot_part_size * 1; + default: + g_assert_not_reached(); + } +} + +static uint64_t sd_req_get_address(SDState *sd, SDRequest req) +{ + uint64_t addr; + + if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + addr = (uint64_t) req.arg << HWBLOCK_SHIFT; + } else { + addr = req.arg; + } + trace_sdcard_req_addr(req.arg, addr); + return addr; +} + static inline uint64_t sd_addr_to_wpnum(uint64_t addr) { return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); @@ -553,7 +822,8 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr) static void sd_reset(DeviceState *dev) { - SDState *sd = SD_CARD(dev); + SDState *sd = SDMMC_COMMON(dev); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(sd); uint64_t size; uint64_t sect; @@ -563,17 +833,22 @@ static void sd_reset(DeviceState *dev) } else { sect = 0; } - size = sect << 9; + size = sect << HWBLOCK_SHIFT; + if (sd_is_emmc(sd)) { + size -= sd->boot_part_size * 2; + } sect = sd_addr_to_wpnum(size) + 1; sd->state = sd_idle_state; - sd->rca = 0x0000; + + /* card registers */ + sd->rca = sd_is_emmc(sd) ? 0x0001 : 0x0000; sd->size = size; sd_set_ocr(sd); sd_set_scr(sd); - sd_set_cid(sd); - sd_set_csd(sd, size); + sc->set_cid(sd); + sc->set_csd(sd, size); sd_set_cardstatus(sd); sd_set_sdstatus(sd); @@ -648,13 +923,31 @@ 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() }, }; +static bool vmstate_needed_for_emmc(void *opaque) +{ + SDState *sd = opaque; + + return sd_is_emmc(sd); +} + +static const VMStateDescription emmc_extcsd_vmstate = { + .name = "sd-card/ext_csd_modes-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_needed_for_emmc, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(ext_csd_rw, SDState, 192), + VMSTATE_END_OF_LIST() + }, +}; + static int sd_vmstate_pre_load(void *opaque) { SDState *sd = opaque; @@ -673,7 +966,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,8 +993,9 @@ static const VMStateDescription sd_vmstate = { VMSTATE_BOOL(enable, SDState), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &sd_ocr_vmstate, + &emmc_extcsd_vmstate, NULL }, }; @@ -714,13 +1008,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,6 +1045,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); + addr += sd_bootpart_offset(sd); 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,16 +1054,12 @@ 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); + addr += sd_bootpart_offset(sd); if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } } -#define BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len) -#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len) -#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len) -#define APP_WRITE_BLOCK(a, len) - static void sd_erase(SDState *sd) { uint64_t erase_start = sd->erase_start; @@ -790,8 +1080,8 @@ static void sd_erase(SDState *sd) if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { /* High capacity memory card: erase units are 512 byte blocks */ - erase_start *= 512; - erase_end *= 512; + erase_start <<= HWBLOCK_SHIFT; + erase_end <<= HWBLOCK_SHIFT; sdsc = false; } @@ -818,7 +1108,7 @@ static void sd_erase(SDState *sd) continue; } } - BLK_WRITE_BLOCK(erase_addr, erase_len); + sd_blk_write(sd, erase_addr, erase_len); } } @@ -846,24 +1136,65 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr) return ret; } +enum ExtCsdAccessMode { + EXT_CSD_ACCESS_MODE_COMMAND_SET = 0, + EXT_CSD_ACCESS_MODE_SET_BITS = 1, + EXT_CSD_ACCESS_MODE_CLEAR_BITS = 2, + EXT_CSD_ACCESS_MODE_WRITE_BYTE = 3 +}; + +static void emmc_function_switch(SDState *sd, uint32_t arg) +{ + uint8_t access = extract32(arg, 24, 2); + uint8_t index = extract32(arg, 16, 8); + uint8_t value = extract32(arg, 8, 8); + uint8_t b = sd->ext_csd[index]; + + trace_sdcard_switch(access, index, value, extract32(arg, 0, 2)); + + if (index >= 192) { + qemu_log_mask(LOG_GUEST_ERROR, "MMC switching illegal offset\n"); + sd->card_status |= R_CSR_SWITCH_ERROR_MASK; + return; + } + + switch (access) { + case EXT_CSD_ACCESS_MODE_COMMAND_SET: + qemu_log_mask(LOG_UNIMP, "MMC Command set switching not supported\n"); + return; + case EXT_CSD_ACCESS_MODE_SET_BITS: + b |= value; + break; + case EXT_CSD_ACCESS_MODE_CLEAR_BITS: + b &= ~value; + break; + case EXT_CSD_ACCESS_MODE_WRITE_BYTE: + b = value; + break; + } + + trace_sdcard_ext_csd_update(index, sd->ext_csd[index], b); + sd->ext_csd[index] = b; +} + 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,284 +1297,790 @@ 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->name, req.cmd, sd_state_name(sd->state), + sd_version_str(sd->spec_version)); + + return sd_illegal; +} + +static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong mode: %s (spec %s)\n", + sd->proto->name, req.cmd, sd_mode_name(sd->mode), + 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->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->name, req.cmd); + + return sd_illegal; +} + +static sd_rsp_type_t sd_cmd_optional(SDState *sd, SDRequest req) +{ + qemu_log_mask(LOG_UNIMP, "%s: Optional CMD%i not implemented\n", + sd->proto->name, req.cmd); + + return sd_illegal; +} + +/* Configure fields for following sd_generic_write_byte() calls */ +static sd_rsp_type_t sd_cmd_to_receivingdata(SDState *sd, SDRequest req, + uint64_t start, size_t size) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->state = sd_receivingdata_state; + sd->data_start = start; + sd->data_offset = 0; + /* sd->data[] used as receive buffer */ + sd->data_size = size ?: sizeof(sd->data); + return sd_r1; +} + +/* Configure fields for following sd_generic_read_byte() calls */ +static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, + uint64_t start, + const void *data, size_t size) +{ + if (sd->state != sd_transfer_state) { + sd_invalid_state_for_cmd(sd, req); + } + + sd->state = sd_sendingdata_state; + sd->data_start = start; + sd->data_offset = 0; + if (data) { + assert(size > 0 && size <= sizeof(sd->data)); + memcpy(sd->data, data, size); + } + if (size) { + sd->data_size = size; + } + return sd_r1; +} + +/* CMD0 */ +static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) +{ + if (sd->state == sd_sleep_state) { + switch (req.arg) { + case 0x00000000: + case 0xf0f0f0f0: + break; + default: + return sd_r0; + } + } + if (sd->state != sd_inactive_state) { + sd->state = sd_idle_state; + sd_reset(DEVICE(sd)); + } + + return sd_is_spi(sd) ? sd_r1 : sd_r0; +} + +/* CMD1 */ +static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req) +{ + sd->state = sd_transfer_state; + + return sd_r1; +} + +/* CMD2 */ +static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_ready_state: + sd->state = sd_identification_state; + return sd_r2_i; + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +/* CMD3 */ +static sd_rsp_type_t sd_cmd_SEND_RELATIVE_ADDR(SDState *sd, SDRequest req) +{ + uint16_t random_rca; + + switch (sd->state) { + case sd_identification_state: + case sd_standby_state: + sd->state = sd_standby_state; + qemu_guest_getrandom_nofail(&random_rca, sizeof(random_rca)); + sd_set_rca(sd, random_rca); + return sd_r6; + + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +static sd_rsp_type_t emmc_cmd_SET_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, req.arg >> 16); + return sd_r1; + + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +/* CMD5 */ +static sd_rsp_type_t emmc_cmd_sleep_awake(SDState *sd, SDRequest req) +{ + bool do_sleep = extract32(req.arg, 15, 1); + + switch (sd->state) { + case sd_sleep_state: + if (!do_sleep) { + /* Awake */ + sd->state = sd_standby_state; + } + return sd_r1b; + + case sd_standby_state: + if (do_sleep) { + sd->state = sd_sleep_state; + } + return sd_r1b; + + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +/* CMD6 */ +static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) +{ + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); + } + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd_function_switch(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, 0, NULL, 64); +} + +static sd_rsp_type_t emmc_cmd_SWITCH(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_transfer_state: + sd->state = sd_programming_state; + emmc_function_switch(sd, req.arg); + sd->state = sd_transfer_state; + return sd_r1b; + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +/* CMD7 */ +static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req) +{ + bool same_rca = sd_req_rca_same(sd, req); + + switch (sd->state) { + case sd_standby_state: + if (!same_rca) { + return sd_r0; + } + sd->state = sd_transfer_state; + return sd_r1b; + + case sd_transfer_state: + case sd_sendingdata_state: + if (same_rca) { + break; + } + sd->state = sd_standby_state; + return sd_r1b; + + case sd_disconnect_state: + if (!same_rca) { + return sd_r0; + } + sd->state = sd_programming_state; + return sd_r1b; + + case sd_programming_state: + if (same_rca) { + break; + } + sd->state = sd_disconnect_state; + return sd_r1b; + + default: + break; + } + return sd_invalid_state_for_cmd(sd, req); +} + +/* CMD8 */ +static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) +{ + if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { + return sd_cmd_illegal(sd, req); + } + if (sd->state != sd_idle_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->vhs = 0; + + /* No response if not exactly one VHS bit is set. */ + if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { + return sd_is_spi(sd) ? sd_r7 : sd_r0; + } + + /* Accept. */ + sd->vhs = req.arg; + return sd_r7; +} + +/* CMD8 */ +static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->ext_csd, sizeof(sd->ext_csd)); +} + +/* CMD9 */ +static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->csd, 16); +} + +static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + return sd_req_rca_same(sd, req) ? sd_r2_s : sd_r0; +} + +/* CMD10 */ +static sd_rsp_type_t spi_cmd_SEND_CID(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), + sd->cid, 16); +} + +static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req) +{ + if (sd->state != sd_standby_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + return sd_req_rca_same(sd, req) ? sd_r2_i : sd_r0; +} + +/* CMD12 */ +static sd_rsp_type_t sd_cmd_STOP_TRANSMISSION(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_sendingdata_state: + sd->state = sd_transfer_state; + return sd_r1b; + case sd_receivingdata_state: + sd->state = sd_programming_state; + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return sd_r1; + default: + return sd_invalid_state_for_cmd(sd, req); + } +} + +/* CMD13 */ +static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) +{ + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); + } + + switch (sd->state) { + case sd_standby_state: + case sd_transfer_state: + case sd_sendingdata_state: + case sd_receivingdata_state: + case sd_programming_state: + case sd_disconnect_state: + break; + default: + return sd_invalid_state_for_cmd(sd, req); + } + + if (sd_is_spi(sd)) { + return sd_r2_s; + } + + return sd_req_rca_same(sd, req) ? sd_r1 : sd_r0; +} + +/* CMD15 */ +static sd_rsp_type_t sd_cmd_GO_INACTIVE_STATE(SDState *sd, SDRequest req) +{ + if (sd->mode != sd_data_transfer_mode) { + return sd_invalid_mode_for_cmd(sd, req); + } + switch (sd->state) { + case sd_standby_state: + case sd_transfer_state: + case sd_sendingdata_state: + case sd_receivingdata_state: + case sd_programming_state: + case sd_disconnect_state: + break; + default: + return sd_invalid_state_for_cmd(sd, req); + } + if (sd_req_rca_same(sd, req)) { + sd->state = sd_inactive_state; + } + + return sd_r0; +} + +/* CMD16 */ +static sd_rsp_type_t sd_cmd_SET_BLOCKLEN(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + if (req.arg > (1 << HWBLOCK_SHIFT)) { + sd->card_status |= BLOCK_LEN_ERROR; + } else { + trace_sdcard_set_blocklen(req.arg); + sd->blk_len = req.arg; + } + + return sd_r1; +} + +/* CMD17 */ +static sd_rsp_type_t sd_cmd_READ_SINGLE_BLOCK(SDState *sd, SDRequest req) +{ + uint64_t addr; + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "READ_SINGLE_BLOCK", addr, sd->blk_len)) { + return sd_r1; + } + + sd_blk_read(sd, addr, sd->blk_len); + return sd_cmd_to_sendingdata(sd, req, addr, NULL, sd->blk_len); +} + +/* CMD19 */ +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); + } + + return sd_cmd_to_sendingdata(sd, req, 0, + sd_tuning_block_pattern4, + sizeof(sd_tuning_block_pattern4)); +} + +/* CMD23 */ +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; + if (sd_is_emmc(sd)) { + sd->multi_blk_cnt &= 0xffff; + } + trace_sdcard_set_block_count(sd->multi_blk_cnt); + + return sd_r1; +} + +/* CMD24 */ +static sd_rsp_type_t sd_cmd_WRITE_SINGLE_BLOCK(SDState *sd, SDRequest req) +{ + uint64_t addr; + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "WRITE_SINGLE_BLOCK", addr, sd->blk_len)) { + return sd_r1; + } + + if (sd->size <= SDSC_MAX_CAPACITY) { + if (sd_wp_addr(sd, addr)) { + sd->card_status |= WP_VIOLATION; + } + } + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + } + + sd->blk_written = 0; + return sd_cmd_to_receivingdata(sd, req, addr, sd->blk_len); +} + +/* CMD26 */ +static sd_rsp_type_t emmc_cmd_PROGRAM_CID(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->cid)); +} + +/* CMD27 */ +static sd_rsp_type_t sd_cmd_PROGRAM_CSD(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, sizeof(sd->csd)); +} + +static sd_rsp_type_t sd_cmd_SET_CLR_WRITE_PROT(SDState *sd, SDRequest req, + bool is_write) +{ + uint64_t addr; + + if (sd->size > SDSC_MAX_CAPACITY) { + return sd_illegal; + } + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, is_write ? "SET_WRITE_PROT" : "CLR_WRITE_PROT", + addr, 1)) { + return sd_r1b; + } + + sd->state = sd_programming_state; + if (is_write) { + set_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); + } else { + clear_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); + } + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return sd_r1; +} + +/* CMD28 */ +static sd_rsp_type_t sd_cmd_SET_WRITE_PROT(SDState *sd, SDRequest req) +{ + return sd_cmd_SET_CLR_WRITE_PROT(sd, req, true); +} + +/* CMD29 */ +static sd_rsp_type_t sd_cmd_CLR_WRITE_PROT(SDState *sd, SDRequest req) +{ + return sd_cmd_SET_CLR_WRITE_PROT(sd, req, false); +} + +/* CMD30 */ +static sd_rsp_type_t sd_cmd_SEND_WRITE_PROT(SDState *sd, SDRequest req) +{ + uint64_t addr; + uint32_t data; + + if (sd->size > SDSC_MAX_CAPACITY) { + return sd_illegal; + } + + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + addr = sd_req_get_address(sd, req); + if (!address_in_range(sd, "SEND_WRITE_PROT", addr, sd->blk_len)) { + return sd_r1; + } + + data = sd_wpbits(sd, req.arg); + return sd_cmd_to_sendingdata(sd, req, addr, &data, sizeof(data)); +} + +/* CMD32 */ +static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_START(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->erase_start = req.arg; + return sd_r1; +} + +/* CMD33 */ +static sd_rsp_type_t sd_cmd_ERASE_WR_BLK_END(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + sd->erase_end = req.arg; + return sd_r1; +} + +/* CMD38 */ +static sd_rsp_type_t sd_cmd_ERASE(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + if (sd->csd[14] & 0x30) { + sd->card_status |= WP_VIOLATION; + return sd_r1b; + } + + sd->state = sd_programming_state; + sd_erase(sd); + /* Bzzzzzzztt .... Operation complete. */ + sd->state = sd_transfer_state; + return sd_r1b; +} + +/* CMD42 */ +static sd_rsp_type_t sd_cmd_LOCK_UNLOCK(SDState *sd, SDRequest req) +{ + return sd_cmd_to_receivingdata(sd, req, 0, 0); +} + +/* CMD55 */ +static sd_rsp_type_t sd_cmd_APP_CMD(SDState *sd, SDRequest req) +{ + switch (sd->state) { + case sd_ready_state: + case sd_identification_state: + case sd_inactive_state: + case sd_sleep_state: + return sd_invalid_state_for_cmd(sd, req); + case sd_idle_state: + if (!sd_is_spi(sd) && sd_req_get_rca(sd, req) != 0x0000) { + qemu_log_mask(LOG_GUEST_ERROR, + "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd); + } + /* fall-through */ + default: + break; + } + if (!sd_is_spi(sd) && !sd_req_rca_same(sd, req)) { + return sd_r0; + } + sd->expecting_acmd = true; + sd->card_status |= APP_CMD; + + return sd_r1; +} + +/* CMD56 */ +static sd_rsp_type_t sd_cmd_GEN_CMD(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + /* Vendor specific command: our model is RAZ/WI */ + if (req.arg & 1) { + memset(sd->data, 0, sizeof(sd->data)); + return sd_cmd_to_sendingdata(sd, req, 0, NULL, 0); + } else { + return sd_cmd_to_receivingdata(sd, req, 0, 0); + } +} + +/* CMD58 */ +static sd_rsp_type_t spi_cmd_READ_OCR(SDState *sd, SDRequest req) +{ + return sd_r3; +} + +/* CMD59 */ +static sd_rsp_type_t spi_cmd_CRC_ON_OFF(SDState *sd, SDRequest req) +{ + return sd_r1; +} + +/* ACMD6 */ +static sd_rsp_type_t sd_acmd_SET_BUS_WIDTH(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + sd->sd_status[0] &= 0x3f; + sd->sd_status[0] |= (req.arg & 0x03) << 6; + return sd_r1; +} + +/* ACMD13 */ +static sd_rsp_type_t sd_acmd_SD_STATUS(SDState *sd, SDRequest req) +{ + return sd_cmd_to_sendingdata(sd, req, 0, + sd->sd_status, sizeof(sd->sd_status)); +} + +/* ACMD22 */ +static sd_rsp_type_t sd_acmd_SEND_NUM_WR_BLOCKS(SDState *sd, SDRequest req) +{ + return sd_cmd_to_sendingdata(sd, req, 0, + &sd->blk_written, sizeof(sd->blk_written)); +} + +/* ACMD23 */ +static sd_rsp_type_t sd_acmd_SET_WR_BLK_ERASE_COUNT(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + return sd_r1; +} + +/* ACMD41 */ +static sd_rsp_type_t sd_cmd_SEND_OP_COND(SDState *sd, SDRequest req) +{ + if (sd->state != sd_idle_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + /* + * If it's the first ACMD41 since reset, we need to decide + * whether to power up. If this is not an enquiry ACMD41, + * we immediately report power on and proceed below to the + * ready state, but if it is, we set a timer to model a + * delay for power up. This works around a bug in EDK2 + * UEFI, which sends an initial enquiry ACMD41, but + * assumes that the card is in ready state as soon as it + * sees the power up bit set. + */ + if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) { + if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { + timer_del(sd->ocr_power_timer); + sd_ocr_powerup(sd); + } else { + trace_sdcard_inquiry_cmd41(); + if (!timer_pending(sd->ocr_power_timer)) { + timer_mod_ns(sd->ocr_power_timer, + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + OCR_POWER_DELAY_NS)); + } + } + } + + if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) { + /* + * We accept any voltage. 10000 V is nothing. + * + * Once we're powered up, we advance straight to ready state + * unless it's an enquiry ACMD41 (bits 23:0 == 0). + */ + sd->state = sd_ready_state; + } + + return sd_r3; +} + +/* ACMD42 */ +static sd_rsp_type_t sd_acmd_SET_CLR_CARD_DETECT(SDState *sd, SDRequest req) +{ + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } + + /* Bringing in the 50KOhm pull-up resistor... Done. */ + return sd_r1; +} + +/* ACMD51 */ +static sd_rsp_type_t sd_acmd_SEND_SCR(SDState *sd, SDRequest req) +{ + return sd_cmd_to_sendingdata(sd, req, 0, sd->scr, sizeof(sd->scr)); +} + static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) { - uint32_t rca = 0x0000; - uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; + uint64_t addr; + sd->last_cmd_name = sd_cmd_name(sd, req.cmd); /* CMD55 precedes an ACMD, so we are not interested in tracing it. * 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, - sd_cmd_name(req.cmd), req.cmd, + trace_sdcard_normal_command(sd->proto->name, + sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); } /* Not interpreting this as an app command */ sd->card_status &= ~APP_CMD; - if (sd_cmd_type[req.cmd] == sd_ac - || sd_cmd_type[req.cmd] == sd_adtc) { - rca = req.arg >> 16; - } - /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25 * if not, its effects are cancelled */ if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) { sd->multi_blk_cnt = 0; } - if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + if (sd->proto->cmd[req.cmd].class == 6 && FIELD_EX32(sd->ocr, OCR, + CARD_CAPACITY)) { /* Only Standard Capacity cards support class 6 commands */ return sd_illegal; } + if (sd->proto->cmd[req.cmd].handler) { + return sd->proto->cmd[req.cmd].handler(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; - switch (sd->state) { - case sd_standby_state: - break; - - default: - break; - } - break; - - case 5: /* CMD5: reserved for SDIO cards */ - return sd_illegal; - - case 6: /* CMD6: SWITCH_FUNCTION */ - switch (sd->mode) { - case sd_data_transfer_mode: - sd_function_switch(sd, req.arg); - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 7: /* CMD7: SELECT/DESELECT_CARD */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_transfer_state; - return sd_r1b; - - case sd_transfer_state: - case sd_sendingdata_state: - if (sd->rca == rca) - break; - - sd->state = sd_standby_state; - return sd_r1b; - - case sd_disconnect_state: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_programming_state; - return sd_r1b; - - case sd_programming_state: - if (sd->rca == rca) - break; - - sd->state = sd_disconnect_state; - return sd_r1b; - - default: - break; - } - break; - - case 8: /* CMD8: SEND_IF_COND */ - if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { - break; - } - if (sd->state != sd_idle_state) { - break; - } - sd->vhs = 0; - - /* 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; - } - - /* Accept. */ - sd->vhs = req.arg; - return sd_r7; - - case 9: /* CMD9: SEND_CSD */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - return sd_r2_s; - - case sd_transfer_state: - if (!sd->spi) - break; - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->csd, 16); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 10: /* CMD10: SEND_CID */ - switch (sd->state) { - case sd_standby_state: - if (sd->rca != rca) - return sd_r0; - - return sd_r2_i; - - case sd_transfer_state: - if (!sd->spi) - break; - sd->state = sd_sendingdata_state; - memcpy(sd->data, sd->cid, 16); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 12: /* CMD12: STOP_TRANSMISSION */ - switch (sd->state) { - case sd_sendingdata_state: - sd->state = sd_transfer_state; - return sd_r1b; - - case sd_receivingdata_state: - sd->state = sd_programming_state; - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 13: /* CMD13: SEND_STATUS */ - switch (sd->mode) { - case sd_data_transfer_mode: - if (!sd->spi && sd->rca != rca) { - return sd_r0; - } - - return sd_r1; - - default: - break; - } - break; - - case 15: /* CMD15: GO_INACTIVE_STATE */ - if (sd->spi) - goto bad_cmd; - switch (sd->mode) { - case sd_data_transfer_mode: - if (sd->rca != rca) - return sd_r0; - - sd->state = sd_inactive_state; - return sd_r0; - - default: - break; - } - break; - - /* Block read commands (Classs 2) */ - case 16: /* CMD16: SET_BLOCKLEN */ - switch (sd->state) { - case sd_transfer_state: - if (req.arg > (1 << HWBLOCK_SHIFT)) { - sd->card_status |= BLOCK_LEN_ERROR; - } else { - trace_sdcard_set_blocklen(req.arg); - sd->blk_len = req.arg; - } - - return sd_r1; - - default: - break; - } - break; - - case 17: /* CMD17: READ_SINGLE_BLOCK */ - case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + /* Block read commands (Class 2) */ + case 18: /* CMD18: READ_MULTIPLE_BLOCK */ + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: @@ -1261,34 +2098,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 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ + addr = sd_req_get_address(sd, req); switch (sd->state) { case sd_transfer_state: @@ -1316,355 +2128,27 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) } break; - case 26: /* CMD26: PROGRAM_CID */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 27: /* CMD27: PROGRAM_CSD */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - /* Write protection (Class 6) */ - case 28: /* CMD28: SET_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } - - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "SET_WRITE_PROT", addr, 1)) { - return sd_r1b; - } - - sd->state = sd_programming_state; - set_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 29: /* CMD29: CLR_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } - - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "CLR_WRITE_PROT", addr, 1)) { - return sd_r1b; - } - - sd->state = sd_programming_state; - clear_bit(sd_addr_to_wpnum(addr), sd->wp_group_bmap); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - case 30: /* CMD30: SEND_WRITE_PROT */ - if (sd->size > SDSC_MAX_CAPACITY) { - return sd_illegal; - } - - switch (sd->state) { - case sd_transfer_state: - if (!address_in_range(sd, "SEND_WRITE_PROT", - req.arg, sd->blk_len)) { - return sd_r1; - } - - sd->state = sd_sendingdata_state; - *(uint32_t *) sd->data = sd_wpbits(sd, req.arg); - sd->data_start = addr; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - /* Erase commands (Class 5) */ - case 32: /* CMD32: ERASE_WR_BLK_START */ - switch (sd->state) { - case sd_transfer_state: - sd->erase_start = req.arg; - return sd_r1; - - default: - break; - } - break; - - case 33: /* CMD33: ERASE_WR_BLK_END */ - switch (sd->state) { - case sd_transfer_state: - sd->erase_end = req.arg; - return sd_r1; - - default: - break; - } - break; - - case 38: /* CMD38: ERASE */ - switch (sd->state) { - case sd_transfer_state: - if (sd->csd[14] & 0x30) { - sd->card_status |= WP_VIOLATION; - return sd_r1b; - } - - sd->state = sd_programming_state; - sd_erase(sd); - /* Bzzzzzzztt .... Operation complete. */ - sd->state = sd_transfer_state; - return sd_r1b; - - default: - break; - } - break; - - /* Lock card commands (Class 7) */ - case 42: /* CMD42: LOCK_UNLOCK */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_receivingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - 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 */ - switch (sd->state) { - case sd_ready_state: - case sd_identification_state: - case sd_inactive_state: - return sd_illegal; - case sd_idle_state: - if (rca) { - qemu_log_mask(LOG_GUEST_ERROR, - "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd); - } - default: - break; - } - if (!sd->spi) { - if (sd->rca != rca) { - return sd_r0; - } - } - sd->expecting_acmd = true; - sd->card_status |= APP_CMD; - return sd_r1; - - case 56: /* CMD56: GEN_CMD */ - switch (sd->state) { - case sd_transfer_state: - sd->data_offset = 0; - if (req.arg & 1) - sd->state = sd_sendingdata_state; - else - sd->state = sd_receivingdata_state; - return sd_r1; - - default: - break; - } - 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), + sd->last_cmd_name = sd_acmd_name(sd, req.cmd); + trace_sdcard_app_command(sd->proto->name, sd->last_cmd_name, req.cmd, req.arg, sd_state_name(sd->state)); sd->card_status |= APP_CMD; + + if (sd->proto->acmd[req.cmd].handler) { + return sd->proto->acmd[req.cmd].handler(sd, req); + } + switch (req.cmd) { - case 6: /* ACMD6: SET_BUS_WIDTH */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } - switch (sd->state) { - case sd_transfer_state: - sd->sd_status[0] &= 0x3f; - sd->sd_status[0] |= (req.arg & 0x03) << 6; - return sd_r1; - - default: - break; - } - break; - - case 13: /* ACMD13: SD_STATUS */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ - switch (sd->state) { - case sd_transfer_state: - *(uint32_t *) sd->data = sd->blk_written; - - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - - case 23: /* ACMD23: SET_WR_BLK_ERASE_COUNT */ - switch (sd->state) { - case sd_transfer_state: - return sd_r1; - - default: - break; - } - break; - - case 41: /* ACMD41: SD_APP_OP_COND */ - if (sd->spi) { - /* SEND_OP_CMD */ - sd->state = sd_transfer_state; - return sd_r1; - } - if (sd->state != sd_idle_state) { - break; - } - /* If it's the first ACMD41 since reset, we need to decide - * whether to power up. If this is not an enquiry ACMD41, - * we immediately report power on and proceed below to the - * ready state, but if it is, we set a timer to model a - * delay for power up. This works around a bug in EDK2 - * UEFI, which sends an initial enquiry ACMD41, but - * assumes that the card is in ready state as soon as it - * sees the power up bit set. */ - if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) { - if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { - timer_del(sd->ocr_power_timer); - sd_ocr_powerup(sd); - } else { - trace_sdcard_inquiry_cmd41(); - if (!timer_pending(sd->ocr_power_timer)) { - timer_mod_ns(sd->ocr_power_timer, - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + OCR_POWER_DELAY_NS)); - } - } - } - - if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) { - /* We accept any voltage. 10000 V is nothing. - * - * Once we're powered up, we advance straight to ready state - * unless it's an enquiry ACMD41 (bits 23:0 == 0). - */ - sd->state = sd_ready_state; - } - - return sd_r3; - - case 42: /* ACMD42: SET_CLR_CARD_DETECT */ - switch (sd->state) { - case sd_transfer_state: - /* Bringing in the 50KOhm pull-up resistor... Done. */ - return sd_r1; - - default: - break; - } - break; - - case 51: /* ACMD51: SEND_SCR */ - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = 0; - sd->data_offset = 0; - return sd_r1; - - default: - break; - } - break; - case 18: /* Reserved for SD security applications */ case 25: case 26: @@ -1680,20 +2164,16 @@ 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); return sd_illegal; } -static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd) +static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) { + unsigned cmd_class; + /* Valid commands in locked state: * basic class (0) * lock card class (7) @@ -1706,9 +2186,14 @@ static int cmd_valid_while_locked(SDState *sd, const uint8_t cmd) return cmd == 41 || cmd == 42; } if (cmd == 16 || cmd == 55) { - return 1; + return true; } - return sd_cmd_class[cmd] == 0 || sd_cmd_class[cmd] == 7; + if (!sd->proto->cmd[cmd].handler) { + return false; + } + cmd_class = sd->proto->cmd[cmd].class; + + return cmd_class == 0 || cmd_class == 7; } int sd_do_command(SDState *sd, SDRequest *req, @@ -1721,6 +2206,11 @@ int sd_do_command(SDState *sd, SDRequest *req, return 0; } + if (sd->state == sd_inactive_state) { + rtype = sd_illegal; + goto send_response; + } + if (sd_req_crc_validate(req)) { sd->card_status |= COM_CRC_ERROR; rtype = sd_illegal; @@ -1733,6 +2223,12 @@ int sd_do_command(SDState *sd, SDRequest *req, req->cmd &= 0x3f; } + if (sd->state == sd_sleep_state && req->cmd) { + qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is sleeping\n"); + rtype = sd_r0; + goto send_response; + } + if (sd->card_status & CARD_IS_LOCKED) { if (!cmd_valid_while_locked(sd, req->cmd)) { sd->card_status |= ILLEGAL_COMMAND; @@ -1759,9 +2255,8 @@ int sd_do_command(SDState *sd, SDRequest *req, /* Valid command, we can update the 'state before command' bits. * (Do this now so they appear in r1 responses.) */ - sd->current_cmd = req->cmd; - sd->card_status &= ~CURRENT_STATE; - sd->card_status |= (last_state << 9); + sd->card_status = FIELD_DP32(sd->card_status, CSR, + CURRENT_STATE, last_state); } send_response: @@ -1798,6 +2293,13 @@ send_response: break; case sd_r0: + /* + * Invalid state transition, reset implementation + * fields to avoid OOB abuse. + */ + sd->data_start = 0; + sd->data_offset = 0; + /* fall-through */ case sd_illegal: rsplen = 0; break; @@ -1817,9 +2319,36 @@ send_response: qemu_hexdump(stderr, "Response", response, rsplen); #endif + sd->current_cmd = rtype == sd_illegal ? 0 : req->cmd; + return rsplen; } +/* Return true if buffer is consumed. Configured by sd_cmd_to_receivingdata() */ +static bool sd_generic_write_byte(SDState *sd, uint8_t value) +{ + sd->data[sd->data_offset] = value; + + if (++sd->data_offset >= sd->data_size) { + sd->state = sd_transfer_state; + return true; + } + return false; +} + +/* Return true when buffer is consumed. Configured by sd_cmd_to_sendingdata() */ +static bool sd_generic_read_byte(SDState *sd, uint8_t *value) +{ + *value = sd->data[sd->data_offset]; + + if (++sd->data_offset >= sd->data_size) { + sd->state = sd_transfer_state; + return true; + } + + return false; +} + void sd_write_byte(SDState *sd, uint8_t value) { int i; @@ -1836,16 +2365,15 @@ 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, - sd_acmd_name(sd->current_cmd), - sd->current_cmd, value); + trace_sdcard_write_data(sd->proto->name, + sd->last_cmd_name, + sd->current_cmd, sd->data_offset, value); switch (sd->current_cmd) { - case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { + case 24: /* CMD24: WRITE_SINGLE_BLOCK */ + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd_blk_write(sd, sd->data_start, sd->data_offset); sd->blk_written ++; sd->csd[14] |= 0x40; /* Bzzzzzzztt .... Operation complete. */ @@ -1853,7 +2381,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", @@ -1871,7 +2399,7 @@ void sd_write_byte(SDState *sd, uint8_t value) if (sd->data_offset >= sd->blk_len) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; - BLK_WRITE_BLOCK(sd->data_start, sd->data_offset); + sd_blk_write(sd, sd->data_start, sd->data_offset); sd->blk_written++; sd->data_start += sd->blk_len; sd->data_offset = 0; @@ -1890,9 +2418,8 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 26: /* CMD26: PROGRAM_CID */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sizeof(sd->cid)) { + case 26: /* CMD26: PROGRAM_CID */ + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; for (i = 0; i < sizeof(sd->cid); i ++) @@ -1909,9 +2436,8 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 27: /* CMD27: PROGRAM_CSD */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sizeof(sd->csd)) { + case 27: /* CMD27: PROGRAM_CSD */ + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; for (i = 0; i < sizeof(sd->csd); i ++) @@ -1933,9 +2459,8 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - case 42: /* CMD42: LOCK_UNLOCK */ - sd->data[sd->data_offset ++] = value; - if (sd->data_offset >= sd->blk_len) { + case 42: /* CMD42: LOCK_UNLOCK */ + if (sd_generic_write_byte(sd, value)) { /* TODO: Check CRC before committing */ sd->state = sd_programming_state; sd_lock_command(sd); @@ -1944,96 +2469,62 @@ void sd_write_byte(SDState *sd, uint8_t value) } break; - 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); - sd->state = sd_transfer_state; - } + case 56: /* CMD56: GEN_CMD */ + sd_generic_write_byte(sd, value); break; default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__); - break; + g_assert_not_reached(); } } -#define SD_TUNING_BLOCK_SIZE 64 - -static const uint8_t sd_tuning_block_pattern[SD_TUNING_BLOCK_SIZE] = { - /* See: Physical Layer Simplified Specification Version 3.01, Table 4-2 */ - 0xff, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0xc3, 0xcc, - 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, - 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, - 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, - 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, - 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, - 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, - 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, -}; - uint8_t sd_read_byte(SDState *sd) { /* TODO: Append CRCs */ + const uint8_t dummy_byte = 0x00; uint8_t ret; uint32_t io_len; if (!sd->blk || !blk_is_inserted(sd->blk) || !sd->enable) - return 0x00; + return dummy_byte; if (sd->state != sd_sendingdata_state) { qemu_log_mask(LOG_GUEST_ERROR, "%s: not in Sending-Data state\n", __func__); - return 0x00; + return dummy_byte; } - if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) - return 0x00; + if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) { + return dummy_byte; + } - io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; + io_len = sd_blk_len(sd); - trace_sdcard_read_data(sd->proto_name, - sd_acmd_name(sd->current_cmd), - sd->current_cmd, io_len); + trace_sdcard_read_data(sd->proto->name, + sd->last_cmd_name, sd->current_cmd, + sd->data_offset, sd->data_size, io_len); switch (sd->current_cmd) { - case 6: /* CMD6: SWITCH_FUNCTION */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 64) - sd->state = sd_transfer_state; + case 6: /* CMD6: SWITCH_FUNCTION */ + case 8: /* CMD8: SEND_EXT_CSD */ + case 9: /* CMD9: SEND_CSD */ + case 10: /* CMD10: SEND_CID */ + case 13: /* ACMD13: SD_STATUS */ + case 17: /* CMD17: READ_SINGLE_BLOCK */ + case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + case 22: /* ACMD22: SEND_NUM_WR_BLOCKS */ + case 30: /* CMD30: SEND_WRITE_PROT */ + case 51: /* ACMD51: SEND_SCR */ + case 56: /* CMD56: GEN_CMD */ + sd_generic_read_byte(sd, &ret); break; - 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 */ - 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 */ - if (sd->data_offset == 0) - BLK_READ_BLOCK(sd->data_start, io_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= io_len) - 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)) { - return 0x00; + return dummy_byte; } - BLK_READ_BLOCK(sd->data_start, io_len); + sd_blk_read(sd, sd->data_start, io_len); } ret = sd->data[sd->data_offset ++]; @@ -2051,46 +2542,10 @@ uint8_t sd_read_byte(SDState *sd) } break; - case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ - if (sd->data_offset >= SD_TUNING_BLOCK_SIZE - 1) { - sd->state = sd_transfer_state; - } - ret = sd_tuning_block_pattern[sd->data_offset++]; - break; - - 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 */ - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= 4) - sd->state = sd_transfer_state; - break; - - 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 */ - if (sd->data_offset == 0) - APP_READ_BLOCK(sd->data_start, sd->blk_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= sd->blk_len) - sd->state = sd_transfer_state; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown command\n", __func__); - return 0x00; + qemu_log_mask(LOG_GUEST_ERROR, "%s: DAT read illegal for command %s\n", + __func__, sd->last_cmd_name); + return dummy_byte; } return ret; @@ -2111,28 +2566,181 @@ void sd_enable(SDState *sd, bool enable) sd->enable = enable; } +static const SDProto sd_proto_spi = { + .name = "SPI", + .cmd = { + [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, + [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, + [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, + [9] = {0, sd_spi, "SEND_CSD", spi_cmd_SEND_CSD}, + [10] = {0, sd_spi, "SEND_CID", spi_cmd_SEND_CID}, + [12] = {0, sd_spi, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, + [13] = {0, sd_spi, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [16] = {2, sd_spi, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, + [17] = {2, sd_spi, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, + [24] = {4, sd_spi, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [27] = {4, sd_spi, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, + [28] = {6, sd_spi, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, + [29] = {6, sd_spi, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, + [30] = {6, sd_spi, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [32] = {5, sd_spi, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, + [33] = {5, sd_spi, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, + [34] = {10, sd_spi, "READ_SEC_CMD", sd_cmd_optional}, + [35] = {10, sd_spi, "WRITE_SEC_CMD", sd_cmd_optional}, + [36] = {10, sd_spi, "SEND_PSI", sd_cmd_optional}, + [37] = {10, sd_spi, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, + [38] = {5, sd_spi, "ERASE", sd_cmd_ERASE}, + [42] = {7, sd_spi, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, + [50] = {10, sd_spi, "DIRECT_SECURE_READ", sd_cmd_optional}, + [52] = {9, sd_spi, "IO_RW_DIRECT", sd_cmd_optional}, + [53] = {9, sd_spi, "IO_RW_EXTENDED", sd_cmd_optional}, + [55] = {8, sd_spi, "APP_CMD", sd_cmd_APP_CMD}, + [56] = {8, sd_spi, "GEN_CMD", sd_cmd_GEN_CMD}, + [57] = {10, sd_spi, "DIRECT_SECURE_WRITE", sd_cmd_optional}, + [58] = {0, sd_spi, "READ_OCR", spi_cmd_READ_OCR}, + [59] = {0, sd_spi, "CRC_ON_OFF", spi_cmd_CRC_ON_OFF}, + }, + .acmd = { + [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS}, + [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, + [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, + [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [42] = {8, sd_spi, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, + [51] = {8, sd_spi, "SEND_SCR", sd_acmd_SEND_SCR}, + }, +}; + +static const SDProto sd_proto_sd = { + .name = "SD", + .cmd = { + [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [3] = {0, sd_bcr, "SEND_RELATIVE_ADDR", sd_cmd_SEND_RELATIVE_ADDR}, + [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, + [5] = {9, sd_bc, "IO_SEND_OP_COND", sd_cmd_optional}, + [6] = {10, sd_adtc, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, + [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, + [8] = {0, sd_bcr, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, + [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, + [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, + [11] = {0, sd_ac, "VOLTAGE_SWITCH", sd_cmd_optional}, + [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, + [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, + [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, + [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, + [19] = {2, sd_adtc, "SEND_TUNING_BLOCK", sd_cmd_SEND_TUNING_BLOCK}, + [20] = {2, sd_ac, "SPEED_CLASS_CONTROL", sd_cmd_optional}, + [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, + [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, + [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, + [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [32] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, + [33] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, + [34] = {10, sd_adtc, "READ_SEC_CMD", sd_cmd_optional}, + [35] = {10, sd_adtc, "WRITE_SEC_CMD", sd_cmd_optional}, + [36] = {10, sd_adtc, "SEND_PSI", sd_cmd_optional}, + [37] = {10, sd_ac, "CONTROL_ASSD_SYSTEM", sd_cmd_optional}, + [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE}, + [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, + [43] = {1, sd_ac, "Q_MANAGEMENT", sd_cmd_optional}, + [44] = {1, sd_ac, "Q_TASK_INFO_A", sd_cmd_optional}, + [45] = {1, sd_ac, "Q_TASK_INFO_B", sd_cmd_optional}, + [46] = {1, sd_adtc, "Q_RD_TASK", sd_cmd_optional}, + [47] = {1, sd_adtc, "Q_WR_TASK", sd_cmd_optional}, + [48] = {1, sd_adtc, "READ_EXTR_SINGLE", sd_cmd_optional}, + [49] = {1, sd_adtc, "WRITE_EXTR_SINGLE", sd_cmd_optional}, + [50] = {10, sd_adtc, "DIRECT_SECURE_READ", sd_cmd_optional}, + [52] = {9, sd_bc, "IO_RW_DIRECT", sd_cmd_optional}, + [53] = {9, sd_bc, "IO_RW_EXTENDED", sd_cmd_optional}, + [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD}, + [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD}, + [57] = {10, sd_adtc, "DIRECT_SECURE_WRITE", sd_cmd_optional}, + [58] = {11, sd_adtc, "READ_EXTR_MULTI", sd_cmd_optional}, + [59] = {11, sd_adtc, "WRITE_EXTR_MULTI", sd_cmd_optional}, + }, + .acmd = { + [6] = {8, sd_ac, "SET_BUS_WIDTH", sd_acmd_SET_BUS_WIDTH}, + [13] = {8, sd_adtc, "SD_STATUS", sd_acmd_SD_STATUS}, + [22] = {8, sd_adtc, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, + [23] = {8, sd_ac, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, + [41] = {8, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, + [42] = {8, sd_ac, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, + [51] = {8, sd_adtc, "SEND_SCR", sd_acmd_SEND_SCR}, + }, +}; + +static const SDProto sd_proto_emmc = { + /* Only v4.3 is supported */ + .name = "eMMC", + .cmd = { + [0] = {0, sd_bc, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, + [1] = {0, sd_bcr, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, + [2] = {0, sd_bcr, "ALL_SEND_CID", sd_cmd_ALL_SEND_CID}, + [3] = {0, sd_ac, "SET_RELATIVE_ADDR", emmc_cmd_SET_RELATIVE_ADDR}, + [4] = {0, sd_bc, "SEND_DSR", sd_cmd_unimplemented}, + [5] = {0, sd_ac, "SLEEP/AWAKE", emmc_cmd_sleep_awake}, + [6] = {10, sd_adtc, "SWITCH", emmc_cmd_SWITCH}, + [7] = {0, sd_ac, "(DE)SELECT_CARD", sd_cmd_DE_SELECT_CARD}, + [8] = {0, sd_adtc, "SEND_EXT_CSD", emmc_cmd_SEND_EXT_CSD}, + [9] = {0, sd_ac, "SEND_CSD", sd_cmd_SEND_CSD}, + [10] = {0, sd_ac, "SEND_CID", sd_cmd_SEND_CID}, + [11] = {1, sd_adtc, "READ_DAT_UNTIL_STOP", sd_cmd_unimplemented}, + [12] = {0, sd_ac, "STOP_TRANSMISSION", sd_cmd_STOP_TRANSMISSION}, + [13] = {0, sd_ac, "SEND_STATUS", sd_cmd_SEND_STATUS}, + [14] = {0, sd_adtc, "BUSTEST_R", sd_cmd_unimplemented}, + [15] = {0, sd_ac, "GO_INACTIVE_STATE", sd_cmd_GO_INACTIVE_STATE}, + [16] = {2, sd_ac, "SET_BLOCKLEN", sd_cmd_SET_BLOCKLEN}, + [17] = {2, sd_adtc, "READ_SINGLE_BLOCK", sd_cmd_READ_SINGLE_BLOCK}, + [19] = {0, sd_adtc, "BUSTEST_W", sd_cmd_unimplemented}, + [20] = {3, sd_adtc, "WRITE_DAT_UNTIL_STOP", sd_cmd_unimplemented}, + [23] = {2, sd_ac, "SET_BLOCK_COUNT", sd_cmd_SET_BLOCK_COUNT}, + [24] = {4, sd_adtc, "WRITE_SINGLE_BLOCK", sd_cmd_WRITE_SINGLE_BLOCK}, + [26] = {4, sd_adtc, "PROGRAM_CID", emmc_cmd_PROGRAM_CID}, + [27] = {4, sd_adtc, "PROGRAM_CSD", sd_cmd_PROGRAM_CSD}, + [28] = {6, sd_ac, "SET_WRITE_PROT", sd_cmd_SET_WRITE_PROT}, + [29] = {6, sd_ac, "CLR_WRITE_PROT", sd_cmd_CLR_WRITE_PROT}, + [30] = {6, sd_adtc, "SEND_WRITE_PROT", sd_cmd_SEND_WRITE_PROT}, + [31] = {6, sd_adtc, "SEND_WRITE_PROT_TYPE", sd_cmd_unimplemented}, + [35] = {5, sd_ac, "ERASE_WR_BLK_START", sd_cmd_ERASE_WR_BLK_START}, + [36] = {5, sd_ac, "ERASE_WR_BLK_END", sd_cmd_ERASE_WR_BLK_END}, + [38] = {5, sd_ac, "ERASE", sd_cmd_ERASE}, + [39] = {9, sd_ac, "FAST_IO", sd_cmd_unimplemented}, + [40] = {9, sd_bcr, "GO_IRQ_STATE", sd_cmd_unimplemented}, + [42] = {7, sd_adtc, "LOCK_UNLOCK", sd_cmd_LOCK_UNLOCK}, + [49] = {0, sd_adtc, "SET_TIME", sd_cmd_unimplemented}, + [55] = {8, sd_ac, "APP_CMD", sd_cmd_APP_CMD}, + [56] = {8, sd_adtc, "GEN_CMD", sd_cmd_GEN_CMD}, + }, +}; + static void sd_instance_init(Object *obj) { - SDState *sd = SD_CARD(obj); + SDState *sd = SDMMC_COMMON(obj); + SDCardClass *sc = SDMMC_COMMON_GET_CLASS(sd); + sd->proto = sc->proto; + sd->last_cmd_name = "UNSET"; sd->enable = true; sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd); } static void sd_instance_finalize(Object *obj) { - SDState *sd = SD_CARD(obj); + SDState *sd = SDMMC_COMMON(obj); timer_free(sd->ocr_power_timer); } static void sd_realize(DeviceState *dev, Error **errp) { - SDState *sd = SD_CARD(dev); + SDState *sd = SDMMC_COMMON(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: @@ -2181,27 +2789,40 @@ static void sd_realize(DeviceState *dev, Error **errp) } } -static Property sd_properties[] = { - DEFINE_PROP_UINT8("spec_version", SDState, - spec_version, SD_PHY_SPECv2_00_VERS), +static void emmc_realize(DeviceState *dev, Error **errp) +{ + SDState *sd = SDMMC_COMMON(dev); + + sd->spec_version = SD_PHY_SPECv3_01_VERS; /* Actually v4.3 */ + + sd_realize(dev, errp); +} + +static Property sdmmc_common_properties[] = { DEFINE_PROP_DRIVE("drive", SDState, blk), - /* 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. */ - DEFINE_PROP_BOOL("spi", SDState, spi, false), DEFINE_PROP_END_OF_LIST() }; -static void sd_class_init(ObjectClass *klass, void *data) +static Property sd_properties[] = { + DEFINE_PROP_UINT8("spec_version", SDState, + spec_version, SD_PHY_SPECv3_01_VERS), + DEFINE_PROP_END_OF_LIST() +}; + +static Property emmc_properties[] = { + DEFINE_PROP_UINT64("boot-partition-size", SDState, boot_part_size, 0), + DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), + DEFINE_PROP_END_OF_LIST() +}; + +static void sdmmc_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SDCardClass *sc = SD_CARD_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); - dc->realize = sd_realize; - device_class_set_props(dc, sd_properties); + device_class_set_props(dc, sdmmc_common_properties); dc->vmsd = &sd_vmstate; - dc->reset = sd_reset; + device_class_set_legacy_reset(dc, sd_reset); dc->bus_type = TYPE_SD_BUS; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); @@ -2218,19 +2839,77 @@ static void sd_class_init(ObjectClass *klass, void *data) sc->get_readonly = sd_get_readonly; } -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, -}; - -static void sd_register_types(void) +static void sd_class_init(ObjectClass *klass, void *data) { - type_register_static(&sd_info); + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); + + dc->realize = sd_realize; + device_class_set_props(dc, sd_properties); + + sc->set_cid = sd_set_cid; + sc->set_csd = sd_set_csd; + sc->proto = &sd_proto_sd; } -type_init(sd_register_types) +/* + * 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 = SDMMC_COMMON_CLASS(klass); + + dc->desc = "SD SPI"; + sc->proto = &sd_proto_spi; +} + +static void emmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SDMMC_COMMON_CLASS(klass); + + dc->desc = "eMMC"; + dc->realize = emmc_realize; + device_class_set_props(dc, emmc_properties); + /* Reason: Soldered on board */ + dc->user_creatable = false; + + sc->proto = &sd_proto_emmc; + + sc->set_cid = emmc_set_cid; + sc->set_csd = emmc_set_csd; +} + +static const TypeInfo sd_types[] = { + { + .name = TYPE_SDMMC_COMMON, + .parent = TYPE_DEVICE, + .abstract = true, + .instance_size = sizeof(SDState), + .class_size = sizeof(SDCardClass), + .class_init = sdmmc_common_class_init, + .instance_init = sd_instance_init, + .instance_finalize = sd_instance_finalize, + }, + { + .name = TYPE_SD_CARD, + .parent = TYPE_SDMMC_COMMON, + .class_init = sd_class_init, + }, + { + .name = TYPE_SD_CARD_SPI, + .parent = TYPE_SD_CARD, + .class_init = sd_spi_class_init, + }, + { + .name = TYPE_EMMC, + .parent = TYPE_SDMMC_COMMON, + .class_init = emmc_class_init, + }, +}; + +DEFINE_TYPES(sd_types) diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index 964570f8e8..5f3765f12d 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -308,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 306070c872..37875c02c3 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -37,7 +37,6 @@ #include "migration/vmstate.h" #include "sdhci-internal.h" #include "qemu/log.h" -#include "qemu/module.h" #include "trace.h" #include "qom/object.h" @@ -234,7 +233,7 @@ static void sdhci_raise_insertion_irq(void *opaque) if (s->norintsts & SDHC_NIS_REMOVE) { timer_mod(s->insert_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY); } else { s->prnsts = 0x1ff0000; if (s->norintstsen & SDHC_NISEN_INSERT) { @@ -252,7 +251,7 @@ static void sdhci_set_inserted(DeviceState *dev, bool level) if ((s->norintsts & SDHC_NIS_REMOVE) && level) { /* Give target some time to notice card ejection */ timer_mod(s->insert_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + SDHC_INSERTION_DELAY); } else { if (level) { s->prnsts = 0x1ff0000; @@ -290,9 +289,11 @@ static void sdhci_reset(SDHCIState *s) timer_del(s->insert_timer); timer_del(s->transfer_timer); - /* Set all registers to 0. Capabilities/Version registers are not cleared + /* + * Set all registers to 0. Capabilities/Version registers are not cleared * and assumed to always preserve their value, given to them during - * initialization */ + * initialization + */ memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad); /* Reset other state based on current card insertion/readonly status */ @@ -306,7 +307,8 @@ static void sdhci_reset(SDHCIState *s) static void sdhci_poweron_reset(DeviceState *dev) { - /* QOM (ie power-on) reset. This is identical to reset + /* + * QOM (ie power-on) reset. This is identical to reset * commanded via device register apart from handling of the * 'pending insert on powerup' quirk. */ @@ -321,6 +323,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 +375,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 +411,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) @@ -444,8 +448,10 @@ static void sdhci_read_block_from_card(SDHCIState *s) s->prnsts &= ~SDHC_DAT_LINE_ACTIVE; } - /* If stop at block gap request was set and it's not the last block of - * data - generate Block Event interrupt */ + /* + * If stop at block gap request was set and it's not the last block of + * data - generate Block Event interrupt + */ if (s->stopped_state == sdhc_gap_read && (s->trnmod & SDHC_TRNS_MULTI) && s->blkcnt != 1) { s->prnsts &= ~SDHC_DAT_LINE_ACTIVE; @@ -471,6 +477,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 */ @@ -546,8 +553,10 @@ static void sdhci_write_block_to_card(SDHCIState *s) sdhci_update_irq(s); } -/* Write @size bytes of @value data to host controller @s Buffer Data Port - * register */ +/* + * Write @size bytes of @value data to host controller @s Buffer Data Port + * register + */ static void sdhci_write_dataport(SDHCIState *s, uint32_t value, unsigned size) { unsigned i; @@ -559,6 +568,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; @@ -591,9 +601,11 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) return; } - /* XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for + /* + * XXX: Some sd/mmc drivers (for example, u-boot-slp) do not account for * possible stop at page boundary if initial address is not page aligned, - * allow them to work properly */ + * allow them to work properly + */ if ((s->sdmasysad % boundary_chk) == 0) { page_aligned = true; } @@ -699,7 +711,8 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) dma_memory_read(s->dma_as, entry_addr, &adma2, sizeof(adma2), MEMTXATTRS_UNSPECIFIED); adma2 = le64_to_cpu(adma2); - /* The spec does not specify endianness of descriptor table. + /* + * The spec does not specify endianness of descriptor table. * We currently assume that it is LE. */ dscr->addr = (hwaddr)extract64(adma2, 32, 32) & ~0x3ull; @@ -743,7 +756,7 @@ static void sdhci_do_adma(SDHCIState *s) const uint16_t block_size = s->blksize & BLOCK_SIZE_MASK; const MemTxAttrs attrs = { .memory = true }; ADMADescr dscr = {}; - MemTxResult res; + MemTxResult res = MEMTX_ERROR; int i; if (s->trnmod & SDHC_TRNS_BLK_CNT_EN && !s->blkcnt) { @@ -842,6 +855,7 @@ static void sdhci_do_adma(SDHCIState *s) } } if (res != MEMTX_OK) { + s->data_count = 0; if (s->errintstsen & SDHC_EISEN_ADMAERR) { trace_sdhci_error("Set ADMA error flag"); s->errintsts |= SDHC_EIS_ADMAERR; @@ -973,14 +987,17 @@ static bool sdhci_can_issue_command(SDHCIState *s) return true; } -/* The Buffer Data Port register must be accessed in sequential and - * continuous manner */ +/* + * The Buffer Data Port register must be accessed in sequential and + * continuous manner + */ static inline bool sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num) { if ((s->data_count & 0x3) != byte_num) { - trace_sdhci_error("Non-sequential access to Buffer Data Port register" - "is prohibited\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "SDHCI: Non-sequential access to Buffer Data Port" + " register is prohibited\n"); return false; } return true; @@ -1154,7 +1171,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 +1186,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 */ @@ -1196,11 +1218,19 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) MASKED_WRITE(s->argument, mask, value); break; case SDHC_TRNMOD: - /* DMA can be enabled only if it is supported as indicated by - * capabilities register */ + /* + * DMA can be enabled only if it is supported as indicated by + * capabilities register + */ 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); @@ -1263,8 +1293,10 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) } else { s->norintsts &= ~SDHC_NIS_ERR; } - /* Quirk for Raspberry Pi: pending card insert interrupt - * appears when first enabled after power on */ + /* + * Quirk for Raspberry Pi: pending card insert interrupt + * appears when first enabled after power on + */ if ((s->norintstsen & SDHC_NISEN_INSERT) && s->pending_insert_state) { assert(s->pending_insert_quirk); s->norintsts |= SDHC_NIS_INSERT; @@ -1329,7 +1361,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 +1372,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(); @@ -1365,10 +1412,12 @@ void sdhci_initfn(SDHCIState *s) { qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SDHCI_BUS, DEVICE(s), "sd-bus"); - 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->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 +1433,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); @@ -1397,11 +1463,13 @@ void sdhci_common_realize(SDHCIState *s, Error **errp) void sdhci_common_unrealize(SDHCIState *s) { - /* This function is expected to be called only once for each class: + /* + * This function is expected to be called only once for each class: * - SysBus: via DeviceClass->unrealize(), * - PCI: via PCIDeviceClass->exit(). * However to avoid double-free and/or use-after-free we still nullify - * this variable (better safe than sorry!). */ + * this variable (better safe than sorry!). + */ g_free(s->fifo_buffer); s->fifo_buffer = NULL; } @@ -1418,7 +1486,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 +1496,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 +1527,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 }, @@ -1471,7 +1539,7 @@ void sdhci_common_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); dc->vmsd = &sdhci_vmstate; - dc->reset = sdhci_poweron_reset; + device_class_set_legacy_reset(dc, sdhci_poweron_reset); } /* --- qdev SysBus --- */ @@ -1549,15 +1617,6 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) sdhci_common_class_init(klass, data); } -static const TypeInfo sdhci_sysbus_info = { - .name = TYPE_SYSBUS_SDHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SDHCIState), - .instance_init = sdhci_sysbus_init, - .instance_finalize = sdhci_sysbus_finalize, - .class_init = sdhci_sysbus_class_init, -}; - /* --- qdev bus master --- */ static void sdhci_bus_class_init(ObjectClass *klass, void *data) @@ -1568,13 +1627,6 @@ static void sdhci_bus_class_init(ObjectClass *klass, void *data) sbc->set_readonly = sdhci_set_readonly; } -static const TypeInfo sdhci_bus_info = { - .name = TYPE_SDHCI_BUS, - .parent = TYPE_SD_BUS, - .instance_size = sizeof(SDBus), - .class_init = sdhci_bus_class_init, -}; - /* --- qdev i.MX eSDHC --- */ #define USDHC_MIX_CTRL 0x48 @@ -1779,7 +1831,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. * */ @@ -1833,12 +1885,6 @@ static void imx_usdhc_init(Object *obj) s->quirks = SDHCI_QUIRK_NO_BUSY_IRQ; } -static const TypeInfo imx_usdhc_info = { - .name = TYPE_IMX_USDHC, - .parent = TYPE_SYSBUS_SDHCI, - .instance_init = imx_usdhc_init, -}; - /* --- qdev Samsung s3c --- */ #define S3C_SDHCI_CONTROL2 0x80 @@ -1897,18 +1943,31 @@ static void sdhci_s3c_init(Object *obj) s->io_ops = &sdhci_s3c_mmio_ops; } -static const TypeInfo sdhci_s3c_info = { - .name = TYPE_S3C_SDHCI , - .parent = TYPE_SYSBUS_SDHCI, - .instance_init = sdhci_s3c_init, +static const TypeInfo sdhci_types[] = { + { + .name = TYPE_SDHCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = sdhci_bus_class_init, + }, + { + .name = TYPE_SYSBUS_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SDHCIState), + .instance_init = sdhci_sysbus_init, + .instance_finalize = sdhci_sysbus_finalize, + .class_init = sdhci_sysbus_class_init, + }, + { + .name = TYPE_IMX_USDHC, + .parent = TYPE_SYSBUS_SDHCI, + .instance_init = imx_usdhc_init, + }, + { + .name = TYPE_S3C_SDHCI, + .parent = TYPE_SYSBUS_SDHCI, + .instance_init = sdhci_s3c_init, + }, }; -static void sdhci_register_types(void) -{ - type_register_static(&sdhci_sysbus_info); - type_register_static(&sdhci_bus_info); - type_register_static(&imx_usdhc_info); - type_register_static(&sdhci_s3c_info); -} - -type_init(sdhci_register_types) +DEFINE_TYPES(sdhci_types) diff --git a/hw/sd/sdmmc-internal.c b/hw/sd/sdmmc-internal.c deleted file mode 100644 index 2053def3f1..0000000000 --- a/hw/sd/sdmmc-internal.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SD/MMC cards common helpers - * - * 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 "sdmmc-internal.h" - -const char *sd_cmd_name(uint8_t cmd) -{ - static const char *cmd_abbrev[SDMMC_CMD_MAX] = { - [0] = "GO_IDLE_STATE", - [2] = "ALL_SEND_CID", [3] = "SEND_RELATIVE_ADDR", - [4] = "SET_DSR", [5] = "IO_SEND_OP_COND", - [6] = "SWITCH_FUNC", [7] = "SELECT/DESELECT_CARD", - [8] = "SEND_IF_COND", [9] = "SEND_CSD", - [10] = "SEND_CID", [11] = "VOLTAGE_SWITCH", - [12] = "STOP_TRANSMISSION", [13] = "SEND_STATUS", - [15] = "GO_INACTIVE_STATE", - [16] = "SET_BLOCKLEN", [17] = "READ_SINGLE_BLOCK", - [18] = "READ_MULTIPLE_BLOCK", [19] = "SEND_TUNING_BLOCK", - [20] = "SPEED_CLASS_CONTROL", [21] = "DPS_spec", - [23] = "SET_BLOCK_COUNT", - [24] = "WRITE_BLOCK", [25] = "WRITE_MULTIPLE_BLOCK", - [26] = "MANUF_RSVD", [27] = "PROGRAM_CSD", - [28] = "SET_WRITE_PROT", [29] = "CLR_WRITE_PROT", - [30] = "SEND_WRITE_PROT", - [32] = "ERASE_WR_BLK_START", [33] = "ERASE_WR_BLK_END", - [34] = "SW_FUNC_RSVD", [35] = "SW_FUNC_RSVD", - [36] = "SW_FUNC_RSVD", [37] = "SW_FUNC_RSVD", - [38] = "ERASE", - [40] = "DPS_spec", - [42] = "LOCK_UNLOCK", [43] = "Q_MANAGEMENT", - [44] = "Q_TASK_INFO_A", [45] = "Q_TASK_INFO_B", - [46] = "Q_RD_TASK", [47] = "Q_WR_TASK", - [48] = "READ_EXTR_SINGLE", [49] = "WRITE_EXTR_SINGLE", - [50] = "SW_FUNC_RSVD", - [52] = "IO_RW_DIRECT", [53] = "IO_RW_EXTENDED", - [54] = "SDIO_RSVD", [55] = "APP_CMD", - [56] = "GEN_CMD", [57] = "SW_FUNC_RSVD", - [58] = "READ_EXTR_MULTI", [59] = "WRITE_EXTR_MULTI", - [60] = "MANUF_RSVD", [61] = "MANUF_RSVD", - [62] = "MANUF_RSVD", [63] = "MANUF_RSVD", - }; - return cmd_abbrev[cmd] ? cmd_abbrev[cmd] : "UNKNOWN_CMD"; -} - -const char *sd_acmd_name(uint8_t cmd) -{ - static const char *acmd_abbrev[SDMMC_CMD_MAX] = { - [6] = "SET_BUS_WIDTH", - [13] = "SD_STATUS", - [14] = "DPS_spec", [15] = "DPS_spec", - [16] = "DPS_spec", - [18] = "SECU_spec", - [22] = "SEND_NUM_WR_BLOCKS", [23] = "SET_WR_BLK_ERASE_COUNT", - [41] = "SD_SEND_OP_COND", - [42] = "SET_CLR_CARD_DETECT", - [51] = "SEND_SCR", - [52] = "SECU_spec", [53] = "SECU_spec", - [54] = "SECU_spec", - [56] = "SECU_spec", [57] = "SECU_spec", - [58] = "SECU_spec", [59] = "SECU_spec", - }; - - return acmd_abbrev[cmd] ? acmd_abbrev[cmd] : "UNKNOWN_ACMD"; -} diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index d8bf17d204..91eb5b6b2f 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -11,30 +11,115 @@ #ifndef SDMMC_INTERNAL_H #define SDMMC_INTERNAL_H -#define SDMMC_CMD_MAX 64 +#define TYPE_SDMMC_COMMON "sdmmc-common" +DECLARE_OBJ_CHECKERS(SDState, SDCardClass, SDMMC_COMMON, TYPE_SDMMC_COMMON) -/** - * sd_cmd_name: - * @cmd: A SD "normal" command, up to SDMMC_CMD_MAX. +/* + * EXT_CSD Modes segment * - * Returns a human-readable name describing the command. - * The return value is always a static string which does not need - * to be freed after use. - * - * Returns: The command name of @cmd or "UNKNOWN_CMD". + * Define the configuration the Device is working in. + * These modes can be changed by the host by means of the SWITCH command. */ -const char *sd_cmd_name(uint8_t cmd); +#define EXT_CSD_CMDQ_MODE_EN 15 /* R/W */ +#define EXT_CSD_FLUSH_CACHE 32 /* W */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W */ +#define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ +#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */ +#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */ +#define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */ +#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ +#define EXT_CSD_CLASS_6_CTRL 59 +#define EXT_CSD_INI_TIMEOUT_EMU 60 +#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ +#define EXT_CSD_USE_NATIVE_SECTOR 62 +#define EXT_CSD_NATIVE_SECTOR_SIZE 63 +#define EXT_CSD_VENDOR_SPECIFIC_FIELD 64 /* 64 bytes */ +#define EXT_CSD_PROGRAM_CID_CSD_DDR_SUPPORT 130 +#define EXT_CSD_PERIODIC_WAKEUP 131 +#define EXT_CSD_TCASE_SUPPORT 132 +#define EXT_CSD_SEC_BAD_BLK_MGMNT 134 +#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ +#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */ +#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ +#define EXT_CSD_MAX_ENH_SIZE_MULT 157 /* RO, 3 bytes */ +#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ +#define EXT_CSD_HPI_MGMT 161 /* R/W */ +#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ +#define EXT_CSD_BKOPS_EN 163 /* R/W */ +#define EXT_CSD_BKOPS_START 164 /* W */ +#define EXT_CSD_SANITIZE_START 165 /* W */ +#define EXT_CSD_WR_REL_PARAM 166 /* RO */ +#define EXT_CSD_WR_REL_SET 167 +#define EXT_CSD_RPMB_MULT 168 /* RO */ +#define EXT_CSD_FW_CONFIG 169 /* R/W */ +#define EXT_CSD_USER_WP 171 +#define EXT_CSD_BOOT_WP 173 /* R/W */ +#define EXT_CSD_BOOT_WP_STATUS 174 +#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */ +#define EXT_CSD_BOOT_BUS_CONDITIONS 177 +#define EXT_CSD_BOOT_CONFIG_PROT 178 +#define EXT_CSD_PART_CONFIG 179 /* R/W */ +#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_STROBE_SUPPORT 184 /* RO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_CMD_SET_REV 189 +#define EXT_CSD_CMD_SET 191 +/* + * EXT_CSD Properties segment + * + * Define the Device capabilities, cannot be modified by the host. + */ +#define EXT_CSD_REV 192 +#define EXT_CSD_STRUCTURE 194 +#define EXT_CSD_CARD_TYPE 196 +#define EXT_CSD_DRIVER_STRENGTH 197 +#define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 +#define EXT_CSD_PART_SWITCH_TIME 199 +#define EXT_CSD_PWR_CL_52_195 200 +#define EXT_CSD_PWR_CL_26_195 201 +#define EXT_CSD_PWR_CL_52_360 202 +#define EXT_CSD_PWR_CL_26_360 203 +#define EXT_CSD_SEC_CNT 212 /* 4 bytes */ +#define EXT_CSD_S_A_TIMEOUT 217 +#define EXT_CSD_S_C_VCCQ 219 +#define EXT_CSD_S_C_VCC 220 +#define EXT_CSD_REL_WR_SEC_C 222 +#define EXT_CSD_HC_WP_GRP_SIZE 221 +#define EXT_CSD_ERASE_TIMEOUT_MULT 223 +#define EXT_CSD_HC_ERASE_GRP_SIZE 224 +#define EXT_CSD_ACC_SIZE 225 +#define EXT_CSD_BOOT_MULT 226 +#define EXT_CSD_BOOT_INFO 228 +#define EXT_CSD_SEC_FEATURE_SUPPORT 231 +#define EXT_CSD_TRIM_MULT 232 +#define EXT_CSD_INI_TIMEOUT_PA 241 +#define EXT_CSD_BKOPS_STATUS 246 +#define EXT_CSD_POWER_OFF_LONG_TIME 247 +#define EXT_CSD_GENERIC_CMD6_TIME 248 +#define EXT_CSD_CACHE_SIZE 249 /* 4 bytes */ +#define EXT_CSD_EXT_SUPPORT 494 +#define EXT_CSD_LARGE_UNIT_SIZE_M1 495 +#define EXT_CSD_CONTEXT_CAPABILITIES 496 +#define EXT_CSD_TAG_RES_SIZE 497 +#define EXT_CSD_TAG_UNIT_SIZE 498 +#define EXT_CSD_DATA_TAG_SUPPORT 499 +#define EXT_CSD_MAX_PACKED_WRITES 500 +#define EXT_CSD_MAX_PACKED_READS 501 +#define EXT_CSD_BKOPS_SUPPORT 502 +#define EXT_CSD_HPI_FEATURES 503 +#define EXT_CSD_S_CMD_SET 504 -/** - * sd_acmd_name: - * @cmd: A SD "Application-Specific" command, up to SDMMC_CMD_MAX. - * - * Returns a human-readable name describing the application command. - * The return value is always a static string which does not need - * to be freed after use. - * - * Returns: The application command name of @cmd or "UNKNOWN_ACMD". - */ -const char *sd_acmd_name(uint8_t cmd); +#define EXT_CSD_WR_REL_PARAM_EN (1 << 2) +#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR (1 << 4) + +#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) +#define EXT_CSD_PART_CONFIG_ACC_DEFAULT (0x0) +#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) + +#define EXT_CSD_PART_CONFIG_EN_MASK (0x7 << 3) +#define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3) +#define EXT_CSD_PART_CONFIG_EN_USER (0x7 << 3) #endif diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 167c03b780..15940515ab 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), @@ -398,21 +398,18 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data) k->transfer = ssi_sd_transfer; k->cs_polarity = SSI_CS_LOW; dc->vmsd = &vmstate_ssi_sd; - dc->reset = ssi_sd_reset; + device_class_set_legacy_reset(dc, ssi_sd_reset); /* Reason: GPIO chip-select line should be wired up */ 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/sd/trace-events b/hw/sd/trace-events index 94a00557b2..db0644256d 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -43,21 +43,22 @@ sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" sdcard_reset(void) "" -sdcard_set_blocklen(uint16_t length) "0x%03x" +sdcard_set_rca(uint16_t value) "new RCA: 0x%04x" +sdcard_set_blocklen(uint16_t length) "block len 0x%03x" +sdcard_set_block_count(uint32_t cnt) "block cnt 0x%"PRIx32 sdcard_inserted(bool readonly) "read_only: %u" sdcard_ejected(void) "" sdcard_erase(uint32_t first, uint32_t last) "addr first 0x%" PRIx32" last 0x%" PRIx32 sdcard_lock(void) "" sdcard_unlock(void) "" +sdcard_req_addr(uint32_t req_arg, uint64_t addr) "req 0x%" PRIx32 " addr 0x%" PRIx64 sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" -sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t value) "%s %20s/ CMD%02d value 0x%02x" -sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t length) "%s %20s/ CMD%02d len %" PRIu32 +sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint8_t value) "%s %20s/ CMD%02d ofs %"PRIu32" value 0x%02x" +sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t offset, uint64_t size, uint32_t blklen) "%s %20s/ CMD%02d ofs %"PRIu32" size %"PRIu64" blklen %" PRIu32 sdcard_set_voltage(uint16_t millivolts) "%u mV" - -# pxa2xx_mmci.c -pxa2xx_mmci_read(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x" -pxa2xx_mmci_write(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x" +sdcard_ext_csd_update(unsigned index, uint8_t oval, uint8_t nval) "index %u: 0x%02x -> 0x%02x" +sdcard_switch(unsigned access, unsigned index, unsigned value, unsigned set) "SWITCH acc:%u idx:%u val:%u set:%u" # pl181.c pl181_command_send(uint8_t cmd, uint32_t arg) "sending CMD%02d arg 0x%08" PRIx32 diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index e03bd09b50..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 diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c new file mode 100644 index 0000000000..25b87a7296 --- /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, ResetType type) +{ + 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..3fc1e5d0ad 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" @@ -186,7 +185,7 @@ static uint32_t adm1272_direct_to_watts(uint16_t value) return pmbus_direct_mode2data(c, value); } -static void adm1272_exit_reset(Object *obj) +static void adm1272_exit_reset(Object *obj, ResetType type) { ADM1272State *s = ADM1272(obj); PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -387,7 +386,7 @@ static int adm1272_write_data(PMBusDevice *pmdev, const uint8_t *buf, break; case ADM1272_MFR_POWER_CYCLE: - adm1272_exit_reset((Object *)s); + device_cold_reset(DEVICE(s)); break; case ADM1272_HYSTERESIS_LOW: @@ -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..6966a53248 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), @@ -206,7 +205,7 @@ static void dps310_class_init(ObjectClass *klass, void *data) k->event = dps310_event; k->recv = dps310_rx; k->send = dps310_tx; - dc->reset = dps310_reset; + device_class_set_legacy_reset(dc, dps310_reset); dc->vmsd = &vmstate_dps310; } diff --git a/hw/sensor/emc141x.c b/hw/sensor/emc141x.c index 7ce8f4e979..aeccd2a3c9 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), @@ -270,7 +270,7 @@ static void emc141x_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - dc->reset = emc141x_reset; + device_class_set_legacy_reset(dc, emc141x_reset); k->event = emc141x_event; k->recv = emc141x_rx; k->send = emc141x_tx; diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index eb344dd5a9..304a66ea8b 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -63,7 +63,7 @@ static void isl_pmbus_vr_set(Object *obj, Visitor *v, const char *name, pmbus_check_limits(pmdev); } -static void isl_pmbus_vr_exit_reset(Object *obj) +static void isl_pmbus_vr_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); @@ -101,12 +101,12 @@ static void isl_pmbus_vr_exit_reset(Object *obj) } } -/* The raa228000 uses different direct mode coefficents from most isl devices */ -static void raa228000_exit_reset(Object *obj) +/* The raa228000 uses different direct mode coefficients from most isl devices */ +static void raa228000_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); - isl_pmbus_vr_exit_reset(obj); + isl_pmbus_vr_exit_reset(obj, type); pmdev->pages[0].read_iout = 0; pmdev->pages[0].read_pout = 0; @@ -119,13 +119,13 @@ static void raa228000_exit_reset(Object *obj) pmdev->pages[0].read_temperature_3 = 0; } -static void isl69259_exit_reset(Object *obj) +static void isl69259_exit_reset(Object *obj, ResetType type) { 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); + isl_pmbus_vr_exit_reset(obj, type); s->ic_device_id_len = sizeof(ic_device_id); memcpy(s->ic_device_id, ic_device_id, sizeof(ic_device_id)); diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index bb8d48b2fd..04471539b5 100644 --- a/hw/sensor/lsm303dlhc_mag.c +++ b/hw/sensor/lsm303dlhc_mag.c @@ -442,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), @@ -535,7 +535,7 @@ static void lsm303dlhc_mag_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - dc->reset = lsm303dlhc_mag_reset; + device_class_set_legacy_reset(dc, lsm303dlhc_mag_reset); dc->vmsd = &vmstate_lsm303dlhc_mag; k->event = lsm303dlhc_mag_event; k->recv = lsm303dlhc_mag_recv; diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c index 8b95e32481..3577a7c218 100644 --- a/hw/sensor/max31785.c +++ b/hw/sensor/max31785.c @@ -444,7 +444,7 @@ static int max31785_write_data(PMBusDevice *pmdev, const uint8_t *buf, return 0; } -static void max31785_exit_reset(Object *obj) +static void max31785_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); MAX31785State *s = MAX31785(obj); @@ -487,7 +487,7 @@ static const VMStateDescription vmstate_max31785 = { .name = TYPE_MAX31785, .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]){ + .fields = (const VMStateField[]){ VMSTATE_PMBUS_DEVICE(parent, MAX31785State), VMSTATE_UINT16_ARRAY(mfr_mode, MAX31785State, MAX31785_TOTAL_NUM_PAGES), diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c index a91d8bd487..93b53f3db2 100644 --- a/hw/sensor/max34451.c +++ b/hw/sensor/max34451.c @@ -608,7 +608,7 @@ static inline void *memset_word(void *s, uint16_t c, size_t n) return s; } -static void max34451_exit_reset(Object *obj) +static void max34451_exit_reset(Object *obj, ResetType type) { PMBusDevice *pmdev = PMBUS_DEVICE(obj); MAX34451State *s = MAX34451(obj); @@ -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 9e9be602c3..420fdc3359 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -1,9 +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')) -softmmu_ss.add(when: 'CONFIG_MAX31785', if_true: files('max31785.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..ef2824f3e1 100644 --- a/hw/sensor/tmp105.c +++ b/hw/sensor/tmp105.c @@ -26,22 +26,28 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" +#include "hw/registerfields.h" +#include "trace.h" + +FIELD(CONFIG, SHUTDOWN_MODE, 0, 1) +FIELD(CONFIG, THERMOSTAT_MODE, 1, 1) +FIELD(CONFIG, POLARITY, 2, 1) +FIELD(CONFIG, FAULT_QUEUE, 3, 2) +FIELD(CONFIG, CONVERTER_RESOLUTION, 5, 2) +FIELD(CONFIG, ONE_SHOT, 7, 1) static void tmp105_interrupt_update(TMP105State *s) { - qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */ + qemu_set_irq(s->pin, s->alarm ^ FIELD_EX8(~s->config, CONFIG, POLARITY)); } -static void tmp105_alarm_update(TMP105State *s) +static void tmp105_alarm_update(TMP105State *s, bool one_shot) { - if ((s->config >> 0) & 1) { /* SD */ - if ((s->config >> 7) & 1) /* OS */ - s->config &= ~(1 << 7); /* OS */ - else - return; + if (FIELD_EX8(s->config, CONFIG, SHUTDOWN_MODE) && !one_shot) { + return; } - if (s->config >> 1 & 1) { + if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) { /* * TM == 1 : Interrupt mode. We signal Alert when the * temperature rises above T_high, and expect the guest to clear @@ -89,7 +95,8 @@ static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name, visit_type_int(v, name, &value, errp); } -/* Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8 +/* + * Units are 0.001 centigrades relative to 0 C. s->temperature is 8.8 * fixed point, so units are 1/256 centigrades. A simple ratio will do. */ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name, @@ -109,7 +116,7 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name, s->temperature = (int16_t) (temp * 256 / 1000); - tmp105_alarm_update(s); + tmp105_alarm_update(s, false); } static const int tmp105_faultq[4] = { 1, 2, 4, 6 }; @@ -118,54 +125,60 @@ static void tmp105_read(TMP105State *s) { s->len = 0; - if ((s->config >> 1) & 1) { /* TM */ + if (FIELD_EX8(s->config, CONFIG, THERMOSTAT_MODE)) { s->alarm = 0; tmp105_interrupt_update(s); } switch (s->pointer & 3) { case TMP105_REG_TEMPERATURE: - s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8); - s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) & - (0xf0 << ((~s->config >> 5) & 3)); /* R */ + s->buf[s->len++] = (((uint16_t) s->temperature) >> 8); + s->buf[s->len++] = (((uint16_t) s->temperature) >> 0) & + (0xf0 << (FIELD_EX8(~s->config, CONFIG, CONVERTER_RESOLUTION))); break; case TMP105_REG_CONFIG: - s->buf[s->len ++] = s->config; + s->buf[s->len++] = s->config; break; case TMP105_REG_T_LOW: - s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8; - s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0; + s->buf[s->len++] = ((uint16_t) s->limit[0]) >> 8; + s->buf[s->len++] = ((uint16_t) s->limit[0]) >> 0; break; case TMP105_REG_T_HIGH: - s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8; - s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0; + s->buf[s->len++] = ((uint16_t) s->limit[1]) >> 8; + s->buf[s->len++] = ((uint16_t) s->limit[1]) >> 0; break; } + + trace_tmp105_read(s->i2c.address, s->pointer); } static void tmp105_write(TMP105State *s) { + trace_tmp105_write(s->i2c.address, s->pointer); + switch (s->pointer & 3) { case TMP105_REG_TEMPERATURE: break; case TMP105_REG_CONFIG: - if (s->buf[0] & ~s->config & (1 << 0)) /* SD */ - printf("%s: TMP105 shutdown\n", __func__); - s->config = s->buf[0]; - s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ - tmp105_alarm_update(s); + if (FIELD_EX8(s->buf[0] & ~s->config, CONFIG, SHUTDOWN_MODE)) { + trace_tmp105_write_shutdown(s->i2c.address); + } + s->config = FIELD_DP8(s->buf[0], CONFIG, ONE_SHOT, 0); + s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)]; + tmp105_alarm_update(s, FIELD_EX8(s->buf[0], CONFIG, ONE_SHOT)); break; case TMP105_REG_T_LOW: case TMP105_REG_T_HIGH: - if (s->len >= 3) + if (s->len >= 3) { s->limit[s->pointer & 1] = (int16_t) - ((((uint16_t) s->buf[0]) << 8) | s->buf[1]); - tmp105_alarm_update(s); + ((((uint16_t) s->buf[0]) << 8) | (s->buf[1] & 0xf0)); + } + tmp105_alarm_update(s, false); break; } } @@ -175,7 +188,7 @@ static uint8_t tmp105_rx(I2CSlave *i2c) TMP105State *s = TMP105(i2c); if (s->len < 2) { - return s->buf[s->len ++]; + return s->buf[s->len++]; } else { return 0xff; } @@ -215,7 +228,7 @@ static int tmp105_post_load(void *opaque, int version_id) { TMP105State *s = opaque; - s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ + s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)]; tmp105_interrupt_update(s); return 0; @@ -238,7 +251,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 +262,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 +273,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 } @@ -273,7 +286,7 @@ static void tmp105_reset(I2CSlave *i2c) s->temperature = 0; s->pointer = 0; s->config = 0; - s->faults = tmp105_faultq[(s->config >> 3) & 3]; + s->faults = tmp105_faultq[FIELD_EX8(s->config, CONFIG, FAULT_QUEUE)]; s->alarm = 0; s->detect_falling = false; 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/sensor/trace-events b/hw/sensor/trace-events new file mode 100644 index 0000000000..a3fe54fa6d --- /dev/null +++ b/hw/sensor/trace-events @@ -0,0 +1,6 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# tmp105.c +tmp105_read(uint8_t dev, uint8_t addr) "device: 0x%02x, addr: 0x%02x" +tmp105_write(uint8_t dev, uint8_t addr) "device: 0x%02x, addr 0x%02x" +tmp105_write_shutdown(uint8_t dev) "device: 0x%02x" diff --git a/hw/sensor/trace.h b/hw/sensor/trace.h new file mode 100644 index 0000000000..e4721560b0 --- /dev/null +++ b/hw/sensor/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_sensor.h" diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig index ab733a3f76..1660d292d5 100644 --- a/hw/sh4/Kconfig +++ b/hw/sh4/Kconfig @@ -1,22 +1,18 @@ config R2D bool + default y + depends on SH4 imply PCI_DEVICES imply TEST_DEVICES imply RTL8139_PCI select I82378 if TEST_DEVICES select IDE_MMIO select PFLASH_CFI02 - select USB_OHCI_PCI select PCI select SM501 select SH7750 select SH_PCI -config SHIX - bool - select SH7750 - select TC58128 - config SH7750 bool select SH_INTC diff --git a/hw/sh4/meson.build b/hw/sh4/meson.build index 424d5674de..7d27839fee 100644 --- a/hw/sh4/meson.build +++ b/hw/sh4/meson.build @@ -1,9 +1,8 @@ sh4_ss = ss.source_set() -sh4_ss.add(files( +sh4_ss.add(when: 'CONFIG_SH7750', if_true: files( 'sh7750.c', 'sh7750_regnames.c', )) sh4_ss.add(when: 'CONFIG_R2D', if_true: files('r2d.c')) -sh4_ss.add(when: 'CONFIG_SHIX', if_true: files('shix.c')) hw_arch += {'sh4': sh4_ss} diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 39fc4f19d9..7eecd79fcc 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); @@ -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..8041b3b651 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -38,8 +38,6 @@ #include "exec/exec-all.h" #include "trace.h" -#define NB_DEVICES 4 - typedef struct SH7750State { MemoryRegion iomem; MemoryRegion iomem_1f0; @@ -75,7 +73,6 @@ typedef struct SH7750State { uint16_t periph_portdira; /* Direction seen from the peripherals */ uint16_t periph_pdtrb; /* Imposed by the peripherals */ uint16_t periph_portdirb; /* Direction seen from the peripherals */ - sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ /* Cache */ uint32_t ccr; @@ -92,19 +89,6 @@ static inline int has_bcr3_and_bcr4(SH7750State *s) * I/O ports */ -int sh7750_register_io_device(SH7750State *s, sh7750_io_device *device) -{ - int i; - - for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] == NULL) { - s->devices[i] = device; - return 0; - } - } - return -1; -} - static uint16_t portdir(uint32_t v) { #define EVENPORTMASK(n) ((v & (1 << ((n) << 1))) >> (n)) @@ -142,63 +126,26 @@ static uint16_t portb_lines(SH7750State *s) (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ } -static void gen_port_interrupts(SH7750State *s) -{ - /* XXXXX interrupts not generated */ -} - static void porta_changed(SH7750State *s, uint16_t prev) { - uint16_t currenta, changes; - int i, r = 0; + uint16_t currenta; currenta = porta_lines(s); if (currenta == prev) { return; } trace_sh7750_porta(prev, currenta, s->pdtra, s->pctra); - changes = currenta ^ prev; - - for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) { - r |= s->devices[i]->port_change_cb(currenta, portb_lines(s), - &s->periph_pdtra, - &s->periph_portdira, - &s->periph_pdtrb, - &s->periph_portdirb); - } - } - - if (r) { - gen_port_interrupts(s); - } } static void portb_changed(SH7750State *s, uint16_t prev) { - uint16_t currentb, changes; - int i, r = 0; + uint16_t currentb; currentb = portb_lines(s); if (currentb == prev) { return; } trace_sh7750_portb(prev, currentb, s->pdtrb, s->pctrb); - changes = currentb ^ prev; - - for (i = 0; i < NB_DEVICES; i++) { - if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) { - r |= s->devices[i]->port_change_cb(portb_lines(s), currentb, - &s->periph_pdtra, - &s->periph_portdira, - &s->periph_pdtrb, - &s->periph_portdirb); - } - } - - if (r) { - gen_port_interrupts(s); - } } /* @@ -207,13 +154,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 deleted file mode 100644 index aa812512f0..0000000000 --- a/hw/sh4/shix.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SHIX 2.0 board description - * - * Copyright (c) 2005 Samuel Tardieu - * - * 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. - */ -/* - * Shix 2.0 board by Alexis Polti, described at - * https://web.archive.org/web/20070917001736/perso.enst.fr/~polti/realisations/shix20 - * - * More information in target/sh4/README.sh4 - */ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "cpu.h" -#include "hw/sh4/sh.h" -#include "sysemu/qtest.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "qemu/error-report.h" - -#define BIOS_FILENAME "shix_bios.bin" -#define BIOS_ADDRESS 0xA0000000 - -static void shix_init(MachineState *machine) -{ - int ret; - SuperHCPU *cpu; - struct SH7750State *s; - MemoryRegion *sysmem = get_system_memory(); - MemoryRegion *rom = g_new(MemoryRegion, 1); - MemoryRegion *sdram = g_new(MemoryRegion, 2); - const char *bios_name = machine->firmware ?: BIOS_FILENAME; - - cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); - - /* Allocate memory space */ - memory_region_init_rom(rom, NULL, "shix.rom", 0x4000, &error_fatal); - memory_region_add_subregion(sysmem, 0x00000000, rom); - memory_region_init_ram(&sdram[0], NULL, "shix.sdram1", 0x01000000, - &error_fatal); - memory_region_add_subregion(sysmem, 0x08000000, &sdram[0]); - memory_region_init_ram(&sdram[1], NULL, "shix.sdram2", 0x01000000, - &error_fatal); - memory_region_add_subregion(sysmem, 0x0c000000, &sdram[1]); - - /* Load BIOS in 0 (and access it through P2, 0xA0000000) */ - ret = load_image_targphys(bios_name, 0, 0x4000); - if (ret < 0 && !qtest_enabled()) { - error_report("Could not load SHIX bios '%s'", bios_name); - exit(1); - } - - /* Register peripherals */ - s = sh7750_init(cpu, sysmem); - /* XXXXX Check success */ - tc58128_init(s, "shix_linux_nand.bin", NULL); -} - -static void shix_machine_init(MachineClass *mc) -{ - mc->desc = "shix card"; - mc->init = shix_init; - mc->is_default = true; - mc->default_cpu_type = TYPE_SH7750R_CPU; -} - -DEFINE_MACHINE("shix", shix_machine_init) diff --git a/hw/sh4/trace-events b/hw/sh4/trace-events index 4b61cd56c8..6bfd7eebc4 100644 --- a/hw/sh4/trace-events +++ b/hw/sh4/trace-events @@ -1,3 +1,3 @@ # sh7750.c -sh7750_porta(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "porta changed from 0x%04x to 0x%04x\npdtra=0x%04x, pctra=0x%08x" -sh7750_portb(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "portb changed from 0x%04x to 0x%04x\npdtrb=0x%04x, pctrb=0x%08x" +sh7750_porta(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "porta changed from 0x%04x to 0x%04x (pdtra=0x%04x, pctra=0x%08x)" +sh7750_portb(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "portb changed from 0x%04x to 0x%04x (pdtrb=0x%04x, pctrb=0x%08x)" 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 66a020999b..a394514264 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,33 @@ #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 +71,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,6 +80,7 @@ static struct { .max_speed = DEFAULT_CPU_SPEED, .current_speed = DEFAULT_CPU_SPEED, .processor_id = 0, + .processor_family = 0x01, /* Other */ }; struct type8_instance { @@ -118,6 +90,16 @@ struct type8_instance { }; 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; @@ -336,6 +318,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, @@ -345,6 +331,11 @@ static const QemuOptDesc qemu_smbios_type4_opts[] = { }; static const QemuOptDesc qemu_smbios_type8_opts[] = { + { + .name = "type", + .type = QEMU_OPT_NUMBER, + .help = "SMBIOS element type", + }, { .name = "internal_reference", .type = QEMU_OPT_STRING, @@ -365,9 +356,68 @@ static const QemuOptDesc qemu_smbios_type8_opts[] = { .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, @@ -378,6 +428,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[] = { @@ -457,126 +508,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) { @@ -590,6 +548,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 @@ -603,25 +562,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; @@ -640,29 +599,27 @@ static void smbios_build_type_0_table(void) static void smbios_encode_uuid(struct smbios_uuid *uuid, QemuUUID *in) { memcpy(uuid, in, 16); - if (smbios_uuid_encoded) { - uuid->time_low = bswap32(uuid->time_low); - uuid->time_mid = bswap16(uuid->time_mid); - uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); - } + uuid->time_low = bswap32(uuid->time_low); + uuid->time_mid = bswap16(uuid->time_mid); + uuid->time_hi_and_version = bswap16(uuid->time_hi_and_version); } 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; } @@ -708,12 +665,16 @@ 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; - if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { + if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) { tbl_len = SMBIOS_TYPE_4_LEN_V30; } @@ -723,7 +684,7 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance) 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); @@ -746,17 +707,26 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance) SMBIOS_TABLE_SET_STR(4, asset_tag_number_str, type4.asset); SMBIOS_TABLE_SET_STR(4, part_number_str, type4.part); - t->core_count = (ms->smp.cores > 255) ? 0xFF : ms->smp.cores; + 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 = (ms->smp.threads > 255) ? 0xFF : ms->smp.threads; + 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(ms->smp.cores); - t->thread_count2 = cpu_to_le16(ms->smp.threads); + 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; @@ -783,6 +753,65 @@ static void smbios_build_type_8_table(void) } } +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]; @@ -977,32 +1006,21 @@ 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) { 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); @@ -1015,9 +1033,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); @@ -1066,7 +1084,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, @@ -1074,78 +1093,88 @@ void smbios_get_tables(MachineState *ms, Error **errp) { unsigned i, dimm_cnt, offset; + MachineClass *mc = MACHINE_GET_CLASS(ms); + 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_build_type_8_table(); + smbios_build_type_9_table(errp); + smbios_build_type_11_table(); - smbios_smp_sockets = DIV_ROUND_UP(ms->smp.cpus, - ms->smp.cores * ms->smp.threads); - assert(smbios_smp_sockets >= 1); +#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? mc->smbios_memory_device_size \ + : ((current_machine->ram_size - 1) % mc->smbios_memory_device_size) + 1) - for (i = 0; i < smbios_smp_sockets; i++) { - smbios_build_type_4_table(ms, i); - } + dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, + mc->smbios_memory_device_size) / + mc->smbios_memory_device_size; - smbios_build_type_8_table(); - smbios_build_type_11_table(); + /* + * 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; -#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) + smbios_build_type_16_table(dimm_cnt); - dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ; - - /* - * The offset determines if we need to keep additional space betweeen - * table 17 and table 19 header handle numbers so that they do - * not overlap. For example, for a VM with larger than 8 TB guest - * memory and DIMM like chunks of 16 GiB, the default space between - * the two tables (T19_BASE - T17_BASE = 512) is not enough. - */ - offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \ - dimm_cnt - (T19_BASE - T17_BASE) : 0; - - smbios_build_type_16_table(dimm_cnt); - - for (i = 0; i < dimm_cnt; i++) { - 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); @@ -1154,6 +1183,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) @@ -1239,13 +1319,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; @@ -1262,9 +1339,9 @@ 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); @@ -1272,47 +1349,30 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) } if (header->type <= SMBIOS_MAX_TYPE) { - if (test_bit(header->type, have_fields_bitmap)) { + 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, have_binfile_bitmap); + set_bit(header->type, smbios_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; } @@ -1326,41 +1386,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) { @@ -1397,6 +1458,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"); @@ -1418,14 +1482,36 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) if (!qemu_opts_validate(opts, qemu_smbios_type8_opts, errp)) { return; } - struct type8_instance *t; - t = g_new0(struct type8_instance, 1); - save_opt(&t->internal_reference, opts, "internal_reference"); - save_opt(&t->external_reference, opts, "external_reference"); - t->connector_type = qemu_opt_get_number(opts, "connector_type", 0); - t->port_type = qemu_opt_get_number(opts, "port_type", 0); - QTAILQ_INSERT_TAIL(&type8, t, next); + 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; @@ -1447,27 +1533,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_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/Kconfig b/hw/sparc/Kconfig index 79d58beb7a..3cc165dbfb 100644 --- a/hw/sparc/Kconfig +++ b/hw/sparc/Kconfig @@ -1,5 +1,7 @@ config SUN4M bool + default y + depends on SPARC && !SPARC64 imply TCX imply CG3 select CS4231 @@ -18,6 +20,8 @@ config SUN4M config LEON3 bool + default y + depends on SPARC && !SPARC64 select GRLIB config GRLIB 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..6f765e97e4 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() @@ -377,7 +377,7 @@ static void iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = iommu_reset; + device_class_set_legacy_reset(dc, iommu_reset); dc->vmsd = &vmstate_iommu; device_class_set_props(dc, iommu_properties); } diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig index 7e557ad17b..f764c8a219 100644 --- a/hw/sparc64/Kconfig +++ b/hw/sparc64/Kconfig @@ -1,5 +1,7 @@ config SUN4U bool + default y + depends on SPARC64 imply PCI_DEVICES imply SUNHME imply TEST_DEVICES @@ -8,6 +10,7 @@ config SUN4U select ISA_BUS select FDC_ISA select SERIAL_ISA + select SERIAL_MM select PCI_SABRE select IDE_CMD646 select PCKBD @@ -16,6 +19,8 @@ config SUN4U config NIAGARA bool + default y + depends on SPARC64 select EMPTY_SLOT select SUN4V_RTC select UNIMP diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index ccad2c43a3..67ec403e1d 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -23,10 +23,11 @@ */ #include "qemu/osdep.h" +#include "block/block_int-common.h" #include "qemu/units.h" #include "cpu.h" #include "hw/boards.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "hw/misc/unimp.h" #include "hw/loader.h" #include "hw/sparc/sparc64.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 387181ff77..541c7f74fa 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -28,14 +28,15 @@ #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/serial-isa.h" +#include "hw/char/serial-mm.h" +#include "hw/char/parallel-isa.h" #include "hw/rtc/m48t59.h" #include "migration/vmstate.h" #include "hw/input/i8042.h" @@ -84,7 +85,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; @@ -287,7 +289,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); @@ -303,7 +305,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; @@ -315,9 +316,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 */ @@ -360,11 +361,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); } @@ -454,12 +460,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; } @@ -553,6 +556,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; @@ -607,11 +611,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); @@ -641,30 +645,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 */ @@ -802,6 +794,12 @@ static void sun4v_init(MachineState *machine) sun4uv_init(get_system_memory(), machine, &hwdefs[1]); } +static GlobalProperty hw_compat_sparc64[] = { + { "virtio-pci", "disable-legacy", "on", .optional = true }, + { "virtio-device", "iommu_platform", "on" }, +}; +static const size_t hw_compat_sparc64_len = G_N_ELEMENTS(hw_compat_sparc64); + static void sun4u_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -816,7 +814,10 @@ 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; + compat_props_add(mc->compat_props, hw_compat_sparc64, hw_compat_sparc64_len); } static const TypeInfo sun4u_type = { @@ -840,6 +841,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/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c index 1c1dca712e..eba811af0c 100644 --- a/hw/sparc64/sun4u_iommu.c +++ b/hw/sparc64/sun4u_iommu.c @@ -309,7 +309,7 @@ static void iommu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = iommu_reset; + device_class_set_legacy_reset(dc, iommu_reset); } static const TypeInfo iommu_info = { diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 7d90a02181..1bd56463c1 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -20,3 +20,15 @@ config XILINX_SPIPS config STM32F2XX_SPI bool select SSI + +config BCM2835_SPI + bool + select SSI + +config PNV_SPI + bool + select SSI + +config ALLWINNER_A10_SPI + bool + select SSI diff --git a/hw/ssi/allwinner-a10-spi.c b/hw/ssi/allwinner-a10-spi.c new file mode 100644 index 0000000000..3eb50b44ac --- /dev/null +++ b/hw/ssi/allwinner-a10-spi.c @@ -0,0 +1,561 @@ +/* + * Allwinner SPI Bus Serial Interface Emulation + * + * Copyright (C) 2024 Strahinja Jankovic + * + * 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: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/ssi/allwinner-a10-spi.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "trace.h" + +/* Allwinner SPI memory map */ +#define SPI_RXDATA_REG 0x00 /* receive data register */ +#define SPI_TXDATA_REG 0x04 /* transmit data register */ +#define SPI_CTL_REG 0x08 /* control register */ +#define SPI_INTCTL_REG 0x0c /* interrupt control register */ +#define SPI_INT_STA_REG 0x10 /* interrupt status register */ +#define SPI_DMACTL_REG 0x14 /* DMA control register */ +#define SPI_WAIT_REG 0x18 /* wait clock counter register */ +#define SPI_CCTL_REG 0x1c /* clock rate control register */ +#define SPI_BC_REG 0x20 /* burst control register */ +#define SPI_TC_REG 0x24 /* transmit counter register */ +#define SPI_FIFO_STA_REG 0x28 /* FIFO status register */ + +/* Data register */ +#define SPI_DATA_RESET 0 + +/* Control register */ +#define SPI_CTL_SDC (1 << 19) +#define SPI_CTL_TP_EN (1 << 18) +#define SPI_CTL_SS_LEVEL (1 << 17) +#define SPI_CTL_SS_CTRL (1 << 16) +#define SPI_CTL_DHB (1 << 15) +#define SPI_CTL_DDB (1 << 14) +#define SPI_CTL_SS (3 << 12) +#define SPI_CTL_SS_SHIFT 12 +#define SPI_CTL_RPSM (1 << 11) +#define SPI_CTL_XCH (1 << 10) +#define SPI_CTL_RF_RST (1 << 9) +#define SPI_CTL_TF_RST (1 << 8) +#define SPI_CTL_SSCTL (1 << 7) +#define SPI_CTL_LMTF (1 << 6) +#define SPI_CTL_DMAMC (1 << 5) +#define SPI_CTL_SSPOL (1 << 4) +#define SPI_CTL_POL (1 << 3) +#define SPI_CTL_PHA (1 << 2) +#define SPI_CTL_MODE (1 << 1) +#define SPI_CTL_EN (1 << 0) +#define SPI_CTL_MASK 0xFFFFFu +#define SPI_CTL_RESET 0x0002001Cu + +/* Interrupt control register */ +#define SPI_INTCTL_SS_INT_EN (1 << 17) +#define SPI_INTCTL_TX_INT_EN (1 << 16) +#define SPI_INTCTL_TF_UR_INT_EN (1 << 14) +#define SPI_INTCTL_TF_OF_INT_EN (1 << 13) +#define SPI_INTCTL_TF_E34_INT_EN (1 << 12) +#define SPI_INTCTL_TF_E14_INT_EN (1 << 11) +#define SPI_INTCTL_TF_FL_INT_EN (1 << 10) +#define SPI_INTCTL_TF_HALF_EMP_INT_EN (1 << 9) +#define SPI_INTCTL_TF_EMP_INT_EN (1 << 8) +#define SPI_INTCTL_RF_UR_INT_EN (1 << 6) +#define SPI_INTCTL_RF_OF_INT_EN (1 << 5) +#define SPI_INTCTL_RF_E34_INT_EN (1 << 4) +#define SPI_INTCTL_RF_E14_INT_EN (1 << 3) +#define SPI_INTCTL_RF_FU_INT_EN (1 << 2) +#define SPI_INTCTL_RF_HALF_FU_INT_EN (1 << 1) +#define SPI_INTCTL_RF_RDY_INT_EN (1 << 0) +#define SPI_INTCTL_MASK 0x37F7Fu +#define SPI_INTCTL_RESET 0 + +/* Interrupt status register */ +#define SPI_INT_STA_INT_CBF (1 << 31) +#define SPI_INT_STA_SSI (1 << 17) +#define SPI_INT_STA_TC (1 << 16) +#define SPI_INT_STA_TU (1 << 14) +#define SPI_INT_STA_TO (1 << 13) +#define SPI_INT_STA_TE34 (1 << 12) +#define SPI_INT_STA_TE14 (1 << 11) +#define SPI_INT_STA_TF (1 << 10) +#define SPI_INT_STA_THE (1 << 9) +#define SPI_INT_STA_TE (1 << 8) +#define SPI_INT_STA_RU (1 << 6) +#define SPI_INT_STA_RO (1 << 5) +#define SPI_INT_STA_RF34 (1 << 4) +#define SPI_INT_STA_RF14 (1 << 3) +#define SPI_INT_STA_RF (1 << 2) +#define SPI_INT_STA_RHF (1 << 1) +#define SPI_INT_STA_RR (1 << 0) +#define SPI_INT_STA_MASK 0x80037F7Fu +#define SPI_INT_STA_RESET 0x00001B00u + +/* DMA control register - not implemented */ +#define SPI_DMACTL_RESET 0 + +/* Wait clock register */ +#define SPI_WAIT_REG_WCC_MASK 0xFFFFu +#define SPI_WAIT_RESET 0 + +/* Clock control register - not implemented */ +#define SPI_CCTL_RESET 2 + +/* Burst count register */ +#define SPI_BC_BC_MASK 0xFFFFFFu +#define SPI_BC_RESET 0 + +/* Transmi counter register */ +#define SPI_TC_WTC_MASK 0xFFFFFFu +#define SPI_TC_RESET 0 + +/* FIFO status register */ +#define SPI_FIFO_STA_CNT_MASK 0x7F +#define SPI_FIFO_STA_TF_CNT_SHIFT 16 +#define SPI_FIFO_STA_RF_CNT_SHIFT 0 +#define SPI_FIFO_STA_RESET 0 + +#define REG_INDEX(offset) (offset / sizeof(uint32_t)) + + +static const char *allwinner_a10_spi_get_regname(unsigned offset) +{ + switch (offset) { + case SPI_RXDATA_REG: + return "RXDATA"; + case SPI_TXDATA_REG: + return "TXDATA"; + case SPI_CTL_REG: + return "CTL"; + case SPI_INTCTL_REG: + return "INTCTL"; + case SPI_INT_STA_REG: + return "INT_STA"; + case SPI_DMACTL_REG: + return "DMACTL"; + case SPI_WAIT_REG: + return "WAIT"; + case SPI_CCTL_REG: + return "CCTL"; + case SPI_BC_REG: + return "BC"; + case SPI_TC_REG: + return "TC"; + case SPI_FIFO_STA_REG: + return "FIFO_STA"; + default: + return "[?]"; + } +} + +static bool allwinner_a10_spi_is_enabled(AWA10SPIState *s) +{ + return s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_EN; +} + +static void allwinner_a10_spi_txfifo_reset(AWA10SPIState *s) +{ + fifo8_reset(&s->tx_fifo); + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= (SPI_INT_STA_TE | SPI_INT_STA_TE14 | + SPI_INT_STA_THE | SPI_INT_STA_TE34); + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~(SPI_INT_STA_TU | SPI_INT_STA_TO); +} + +static void allwinner_a10_spi_rxfifo_reset(AWA10SPIState *s) +{ + fifo8_reset(&s->rx_fifo); + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= + ~(SPI_INT_STA_RU | SPI_INT_STA_RO | SPI_INT_STA_RF | SPI_INT_STA_RR | + SPI_INT_STA_RHF | SPI_INT_STA_RF14 | SPI_INT_STA_RF34); +} + +static uint8_t allwinner_a10_spi_selected_channel(AWA10SPIState *s) +{ + return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SS) >> SPI_CTL_SS_SHIFT; +} + +static void allwinner_a10_spi_reset_hold(Object *obj, ResetType type) +{ + AWA10SPIState *s = AW_A10_SPI(obj); + + s->regs[REG_INDEX(SPI_RXDATA_REG)] = SPI_DATA_RESET; + s->regs[REG_INDEX(SPI_TXDATA_REG)] = SPI_DATA_RESET; + s->regs[REG_INDEX(SPI_CTL_REG)] = SPI_CTL_RESET; + s->regs[REG_INDEX(SPI_INTCTL_REG)] = SPI_INTCTL_RESET; + s->regs[REG_INDEX(SPI_INT_STA_REG)] = SPI_INT_STA_RESET; + s->regs[REG_INDEX(SPI_DMACTL_REG)] = SPI_DMACTL_RESET; + s->regs[REG_INDEX(SPI_WAIT_REG)] = SPI_WAIT_RESET; + s->regs[REG_INDEX(SPI_CCTL_REG)] = SPI_CCTL_RESET; + s->regs[REG_INDEX(SPI_BC_REG)] = SPI_BC_RESET; + s->regs[REG_INDEX(SPI_TC_REG)] = SPI_TC_RESET; + s->regs[REG_INDEX(SPI_FIFO_STA_REG)] = SPI_FIFO_STA_RESET; + + allwinner_a10_spi_txfifo_reset(s); + allwinner_a10_spi_rxfifo_reset(s); +} + +static void allwinner_a10_spi_update_irq(AWA10SPIState *s) +{ + bool level; + + if (fifo8_is_empty(&s->rx_fifo)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RR; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RR; + } + + if (fifo8_num_used(&s->rx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 2)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF14; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF14; + } + + if (fifo8_num_used(&s->rx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 1)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RHF; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RHF; + } + + if (fifo8_num_free(&s->rx_fifo) <= (AW_A10_SPI_FIFO_SIZE >> 2)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF34; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF34; + } + + if (fifo8_is_full(&s->rx_fifo)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF; + } + + if (fifo8_is_empty(&s->tx_fifo)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE; + } + + if (fifo8_num_free(&s->tx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 2)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE14; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE14; + } + + if (fifo8_num_free(&s->tx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 1)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_THE; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_THE; + } + + if (fifo8_num_used(&s->tx_fifo) <= (AW_A10_SPI_FIFO_SIZE >> 2)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE34; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE34; + } + + if (fifo8_is_full(&s->rx_fifo)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TF; + } else { + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TF; + } + + level = (s->regs[REG_INDEX(SPI_INT_STA_REG)] & + s->regs[REG_INDEX(SPI_INTCTL_REG)]) != 0; + + qemu_set_irq(s->irq, level); + + trace_allwinner_a10_spi_update_irq(level); +} + +static void allwinner_a10_spi_flush_txfifo(AWA10SPIState *s) +{ + uint32_t burst_count = s->regs[REG_INDEX(SPI_BC_REG)]; + uint32_t tx_burst = s->regs[REG_INDEX(SPI_TC_REG)]; + trace_allwinner_a10_spi_burst_length(tx_burst); + + trace_allwinner_a10_spi_flush_txfifo_begin(fifo8_num_used(&s->tx_fifo), + fifo8_num_used(&s->rx_fifo)); + + while (!fifo8_is_empty(&s->tx_fifo)) { + uint8_t tx = fifo8_pop(&s->tx_fifo); + uint8_t rx = 0; + bool fill_rx = true; + + trace_allwinner_a10_spi_tx(tx); + + /* Write one byte at a time */ + rx = ssi_transfer(s->bus, tx); + + trace_allwinner_a10_spi_rx(rx); + + /* Check DHB here to determine if RX bytes should be stored */ + if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_DHB) { + /* Store rx bytes only after WTC transfers */ + if (tx_burst > 0u) { + fill_rx = false; + tx_burst--; + } + } + + if (fill_rx) { + if (fifo8_is_full(&s->rx_fifo)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF; + } else { + fifo8_push(&s->rx_fifo, rx); + } + } + + allwinner_a10_spi_update_irq(s); + + burst_count--; + + if (burst_count == 0) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TC; + s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_XCH; + break; + } + } + + if (fifo8_is_empty(&s->tx_fifo)) { + s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TC; + s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_XCH; + } + + trace_allwinner_a10_spi_flush_txfifo_end(fifo8_num_used(&s->tx_fifo), + fifo8_num_used(&s->rx_fifo)); +} + +static uint64_t allwinner_a10_spi_read(void *opaque, hwaddr offset, + unsigned size) +{ + uint32_t value = 0; + AWA10SPIState *s = opaque; + uint32_t index = offset >> 2; + + if (offset > SPI_FIFO_STA_REG) { + qemu_log_mask(LOG_GUEST_ERROR, + "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n", + TYPE_AW_A10_SPI, __func__, offset); + return 0; + } + + value = s->regs[index]; + + if (allwinner_a10_spi_is_enabled(s)) { + switch (offset) { + case SPI_RXDATA_REG: + if (fifo8_is_empty(&s->rx_fifo)) { + /* value is undefined */ + value = 0xdeadbeef; + } else { + /* read from the RX FIFO */ + value = fifo8_pop(&s->rx_fifo); + } + break; + case SPI_TXDATA_REG: + qemu_log_mask(LOG_GUEST_ERROR, + "[%s]%s: Trying to read from TX FIFO\n", + TYPE_AW_A10_SPI, __func__); + + /* Reading from TXDATA gives 0 */ + break; + case SPI_FIFO_STA_REG: + /* Read current tx/rx fifo data count */ + value = fifo8_num_used(&s->tx_fifo) << SPI_FIFO_STA_TF_CNT_SHIFT | + fifo8_num_used(&s->rx_fifo) << SPI_FIFO_STA_RF_CNT_SHIFT; + break; + case SPI_CTL_REG: + case SPI_INTCTL_REG: + case SPI_INT_STA_REG: + case SPI_DMACTL_REG: + case SPI_WAIT_REG: + case SPI_CCTL_REG: + case SPI_BC_REG: + case SPI_TC_REG: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad offset 0x%x\n", __func__, + (uint32_t)offset); + break; + } + + allwinner_a10_spi_update_irq(s); + } + trace_allwinner_a10_spi_read(allwinner_a10_spi_get_regname(offset), value); + + return value; +} + +static bool allwinner_a10_spi_update_cs_level(AWA10SPIState *s, int cs_line_nr) +{ + if (cs_line_nr == allwinner_a10_spi_selected_channel(s)) { + return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SS_LEVEL) != 0; + } else { + return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SSPOL) != 0; + } +} + +static void allwinner_a10_spi_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + AWA10SPIState *s = opaque; + uint32_t index = offset >> 2; + int i = 0; + + if (offset > SPI_FIFO_STA_REG) { + qemu_log_mask(LOG_GUEST_ERROR, + "[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n", + TYPE_AW_A10_SPI, __func__, offset); + return; + } + + trace_allwinner_a10_spi_write(allwinner_a10_spi_get_regname(offset), + (uint32_t)value); + + if (!allwinner_a10_spi_is_enabled(s)) { + /* Block is disabled */ + if (offset != SPI_CTL_REG) { + /* Ignore access */ + return; + } + } + + switch (offset) { + case SPI_RXDATA_REG: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n", + TYPE_AW_A10_SPI, __func__); + break; + case SPI_TXDATA_REG: + if (fifo8_is_full(&s->tx_fifo)) { + /* Ignore writes if queue is full */ + break; + } + + fifo8_push(&s->tx_fifo, (uint8_t)value); + + break; + case SPI_INT_STA_REG: + /* Handle W1C bits - everything except SPI_INT_STA_INT_CBF. */ + value &= ~SPI_INT_STA_INT_CBF; + s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~(value & SPI_INT_STA_MASK); + break; + case SPI_CTL_REG: + s->regs[REG_INDEX(SPI_CTL_REG)] = value; + + for (i = 0; i < AW_A10_SPI_CS_LINES_NR; i++) { + qemu_set_irq( + s->cs_lines[i], + allwinner_a10_spi_update_cs_level(s, i)); + } + + if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_XCH) { + /* Request to start emitting */ + allwinner_a10_spi_flush_txfifo(s); + } + if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_TF_RST) { + allwinner_a10_spi_txfifo_reset(s); + s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_TF_RST; + } + if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_RF_RST) { + allwinner_a10_spi_rxfifo_reset(s); + s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_RF_RST; + } + break; + case SPI_INTCTL_REG: + case SPI_DMACTL_REG: + case SPI_WAIT_REG: + case SPI_CCTL_REG: + case SPI_BC_REG: + case SPI_TC_REG: + case SPI_FIFO_STA_REG: + s->regs[index] = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad offset 0x%x\n", __func__, + (uint32_t)offset); + break; + } + + allwinner_a10_spi_update_irq(s); +} + +static const MemoryRegionOps allwinner_a10_spi_ops = { + .read = allwinner_a10_spi_read, + .write = allwinner_a10_spi_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription allwinner_a10_spi_vmstate = { + .name = TYPE_AW_A10_SPI, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_FIFO8(tx_fifo, AWA10SPIState), + VMSTATE_FIFO8(rx_fifo, AWA10SPIState), + VMSTATE_UINT32_ARRAY(regs, AWA10SPIState, AW_A10_SPI_REGS_NUM), + VMSTATE_END_OF_LIST() + } +}; + +static void allwinner_a10_spi_realize(DeviceState *dev, Error **errp) +{ + AWA10SPIState *s = AW_A10_SPI(dev); + int i = 0; + + memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_spi_ops, s, + TYPE_AW_A10_SPI, AW_A10_SPI_IOSIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); + + s->bus = ssi_create_bus(dev, "spi"); + for (i = 0; i < AW_A10_SPI_CS_LINES_NR; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]); + } + fifo8_create(&s->tx_fifo, AW_A10_SPI_FIFO_SIZE); + fifo8_create(&s->rx_fifo, AW_A10_SPI_FIFO_SIZE); +} + +static void allwinner_a10_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = allwinner_a10_spi_reset_hold; + dc->vmsd = &allwinner_a10_spi_vmstate; + dc->realize = allwinner_a10_spi_realize; + dc->desc = "Allwinner A10 SPI Controller"; +} + +static const TypeInfo allwinner_a10_spi_type_info = { + .name = TYPE_AW_A10_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AWA10SPIState), + .class_init = allwinner_a10_spi_class_init, +}; + +static void allwinner_a10_spi_register_types(void) +{ + type_register_static(&allwinner_a10_spi_type_info); +} + +type_init(allwinner_a10_spi_register_types) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 22df4be528..033cbbb59b 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" @@ -131,6 +132,9 @@ #define FMC_WDT2_CTRL_BOOT_SOURCE BIT(4) /* O: primary 1: alternate */ #define FMC_WDT2_CTRL_EN BIT(0) +/* DMA DRAM Side Address High Part (AST2700) */ +#define R_DMA_DRAM_ADDR_HIGH (0x7c / 4) + /* DMA Control/Status Register */ #define R_DMA_CTRL (0x80 / 4) #define DMA_CTRL_REQUEST (1 << 31) @@ -177,13 +181,18 @@ * DMA flash addresses should be 4 bytes aligned and the valid address * range is 0x20000000 - 0x2FFFFFFF. * - * DMA length is from 4 bytes to 32MB + * DMA length is from 4 bytes to 32MB (AST2500) * 0: 4 bytes - * 0x7FFFFF: 32M bytes + * 0x1FFFFFC: 32M bytes + * + * DMA length is from 1 byte to 32MB (AST2600, AST10x0 and AST2700) + * 0: 1 byte + * 0x1FFFFFF: 32M bytes */ #define DMA_DRAM_ADDR(asc, val) ((val) & (asc)->dma_dram_mask) +#define DMA_DRAM_ADDR_HIGH(val) ((val) & 0xf) #define DMA_FLASH_ADDR(asc, val) ((val) & (asc)->dma_flash_mask) -#define DMA_LENGTH(val) ((val) & 0x01FFFFFC) +#define DMA_LENGTH(val) ((val) & 0x01FFFFFF) /* Flash opcodes. */ #define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */ @@ -202,6 +211,7 @@ static const AspeedSegments aspeed_2500_spi2_segments[]; #define ASPEED_SMC_FEATURE_DMA 0x1 #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 #define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4 +#define ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH 0x08 static inline bool aspeed_smc_has_dma(const AspeedSMCClass *asc) { @@ -213,6 +223,11 @@ static inline bool aspeed_smc_has_wdt_control(const AspeedSMCClass *asc) return !!(asc->features & ASPEED_SMC_FEATURE_WDT_CONTROL); } +static inline bool aspeed_smc_has_dma64(const AspeedSMCClass *asc) +{ + return !!(asc->features & ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH); +} + #define aspeed_smc_error(fmt, ...) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__) @@ -402,7 +417,7 @@ static void aspeed_smc_flash_do_select(AspeedSMCFlash *fl, bool unselect) AspeedSMCState *s = fl->controller; trace_aspeed_smc_flash_select(fl->cs, unselect ? "un" : ""); - + s->unselect = unselect; qemu_set_irq(s->cs_lines[fl->cs], unselect); } @@ -662,22 +677,35 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { static void aspeed_smc_flash_update_ctrl(AspeedSMCFlash *fl, uint32_t value) { AspeedSMCState *s = fl->controller; - bool unselect; + bool unselect = false; + uint32_t old_mode; + uint32_t new_mode; - /* User mode selects the CS, other modes unselect */ - unselect = (value & CTRL_CMD_MODE_MASK) != CTRL_USERMODE; + old_mode = s->regs[s->r_ctrl0 + fl->cs] & CTRL_CMD_MODE_MASK; + new_mode = value & CTRL_CMD_MODE_MASK; - /* A change of CTRL_CE_STOP_ACTIVE from 0 to 1, unselects the CS */ - if (!(s->regs[s->r_ctrl0 + fl->cs] & CTRL_CE_STOP_ACTIVE) && - value & CTRL_CE_STOP_ACTIVE) { - unselect = true; + if (old_mode == CTRL_USERMODE) { + if (new_mode != CTRL_USERMODE) { + unselect = true; + } + + /* A change of CTRL_CE_STOP_ACTIVE from 0 to 1, unselects the CS */ + if (!(s->regs[s->r_ctrl0 + fl->cs] & CTRL_CE_STOP_ACTIVE) && + value & CTRL_CE_STOP_ACTIVE) { + unselect = true; + } + } else { + if (new_mode != CTRL_USERMODE) { + unselect = true; + } } s->regs[s->r_ctrl0 + fl->cs] = value; - s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START; - - aspeed_smc_flash_do_select(fl, unselect); + if (unselect != s->unselect) { + s->snoop_index = unselect ? SNOOP_OFF : SNOOP_START; + aspeed_smc_flash_do_select(fl, unselect); + } } static void aspeed_smc_reset(DeviceState *d) @@ -692,12 +720,30 @@ 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; qemu_set_irq(s->cs_lines[i], true); } + s->unselect = true; + /* setup the default segment register values and regions for all */ for (i = 0; i < asc->cs_num_max; ++i) { aspeed_smc_flash_set_segment_region(s, i, @@ -726,6 +772,8 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) (aspeed_smc_has_dma(asc) && addr == R_DMA_CTRL) || (aspeed_smc_has_dma(asc) && addr == R_DMA_FLASH_ADDR) || (aspeed_smc_has_dma(asc) && addr == R_DMA_DRAM_ADDR) || + (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) && + addr == R_DMA_DRAM_ADDR_HIGH) || (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN) || (aspeed_smc_has_dma(asc) && addr == R_DMA_CHECKSUM) || (addr >= R_SEG_ADDR0 && @@ -756,8 +804,7 @@ static uint8_t aspeed_smc_hclk_divisor(uint8_t hclk_mask) } } - aspeed_smc_error("invalid HCLK mask %x", hclk_mask); - return 0; + g_assert_not_reached(); } /* @@ -826,6 +873,19 @@ static bool aspeed_smc_inject_read_failure(AspeedSMCState *s) } } +static uint64_t aspeed_smc_dma_dram_addr(AspeedSMCState *s) +{ + return s->regs[R_DMA_DRAM_ADDR] | + ((uint64_t) s->regs[R_DMA_DRAM_ADDR_HIGH] << 32); +} + +static uint32_t aspeed_smc_dma_len(AspeedSMCState *s) +{ + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + return QEMU_ALIGN_UP(s->regs[R_DMA_LEN] + asc->dma_start_length, 4); +} + /* * Accumulate the result of the reads to provide a checksum that will * be used to validate the read timing settings. @@ -833,6 +893,7 @@ static bool aspeed_smc_inject_read_failure(AspeedSMCState *s) static void aspeed_smc_dma_checksum(AspeedSMCState *s) { MemTxResult result; + uint32_t dma_len; uint32_t data; if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { @@ -844,7 +905,9 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) aspeed_smc_dma_calibration(s); } - while (s->regs[R_DMA_LEN]) { + dma_len = aspeed_smc_dma_len(s); + + while (dma_len) { data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR], MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { @@ -860,7 +923,8 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) */ s->regs[R_DMA_CHECKSUM] += data; s->regs[R_DMA_FLASH_ADDR] += 4; - s->regs[R_DMA_LEN] -= 4; + dma_len -= 4; + s->regs[R_DMA_LEN] = dma_len; } if (s->inject_failure && aspeed_smc_inject_read_failure(s)) { @@ -871,21 +935,34 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s) static void aspeed_smc_dma_rw(AspeedSMCState *s) { + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + uint64_t dma_dram_offset; + uint64_t dma_dram_addr; MemTxResult result; + uint32_t dma_len; uint32_t data; + dma_len = aspeed_smc_dma_len(s); + dma_dram_addr = aspeed_smc_dma_dram_addr(s); + + if (aspeed_smc_has_dma64(asc)) { + dma_dram_offset = dma_dram_addr - s->dram_base; + } else { + dma_dram_offset = dma_dram_addr; + } + trace_aspeed_smc_dma_rw(s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE ? "write" : "read", s->regs[R_DMA_FLASH_ADDR], - s->regs[R_DMA_DRAM_ADDR], - s->regs[R_DMA_LEN]); - while (s->regs[R_DMA_LEN]) { + dma_dram_offset, + dma_len); + while (dma_len) { if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) { - data = address_space_ldl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], + data = address_space_ldl_le(&s->dram_as, dma_dram_offset, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - aspeed_smc_error("DRAM read failed @%08x", - s->regs[R_DMA_DRAM_ADDR]); + aspeed_smc_error("DRAM read failed @%" PRIx64, + dma_dram_offset); return; } @@ -905,11 +982,11 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) return; } - address_space_stl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR], + address_space_stl_le(&s->dram_as, dma_dram_offset, data, MEMTXATTRS_UNSPECIFIED, &result); if (result != MEMTX_OK) { - aspeed_smc_error("DRAM write failed @%08x", - s->regs[R_DMA_DRAM_ADDR]); + aspeed_smc_error("DRAM write failed @%" PRIx64, + dma_dram_offset); return; } } @@ -918,9 +995,14 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s) * When the DMA is on-going, the DMA registers are updated * with the current working addresses and length. */ + dma_dram_offset += 4; + dma_dram_addr += 4; + + s->regs[R_DMA_DRAM_ADDR_HIGH] = dma_dram_addr >> 32; + s->regs[R_DMA_DRAM_ADDR] = dma_dram_addr & 0xffffffff; s->regs[R_DMA_FLASH_ADDR] += 4; - s->regs[R_DMA_DRAM_ADDR] += 4; - s->regs[R_DMA_LEN] -= 4; + dma_len -= 4; + s->regs[R_DMA_LEN] = dma_len; s->regs[R_DMA_CHECKSUM] += data; } } @@ -1071,6 +1153,9 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, } else if (aspeed_smc_has_dma(asc) && addr == R_DMA_LEN && aspeed_smc_dma_granted(s)) { s->regs[addr] = DMA_LENGTH(value); + } else if (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) && + addr == R_DMA_DRAM_ADDR_HIGH) { + s->regs[addr] = DMA_DRAM_ADDR_HIGH(value); } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -1134,10 +1219,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, @@ -1194,18 +1276,20 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_smc = { .name = "aspeed.smc", - .version_id = 2, + .version_id = 3, .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), + VMSTATE_BOOL_V(unselect, AspeedSMCState, 3), VMSTATE_END_OF_LIST() } }; static Property aspeed_smc_properties[] = { DEFINE_PROP_BOOL("inject-failure", AspeedSMCState, inject_failure, false), + DEFINE_PROP_UINT64("dram-base", AspeedSMCState, dram_base, 0), DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr, TYPE_MEMORY_REGION, MemoryRegion *), DEFINE_PROP_END_OF_LIST(), @@ -1216,7 +1300,7 @@ static void aspeed_smc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_smc_realize; - dc->reset = aspeed_smc_reset; + device_class_set_legacy_reset(dc, aspeed_smc_reset); device_class_set_props(dc, aspeed_smc_properties); dc->vmsd = &vmstate_aspeed_smc; } @@ -1247,7 +1331,7 @@ static void aspeed_smc_flash_realize(DeviceState *dev, Error **errp) * 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, + memory_region_init_io(&s->mmio, OBJECT(s), s->asc->reg_ops, s, name, s->asc->segments[s->cs].size); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); } @@ -1322,6 +1406,7 @@ static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2400_smc_info = { @@ -1367,10 +1452,12 @@ static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x1FFFFFFC; + asc->dma_start_length = 4; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2400_fmc_info = { @@ -1410,6 +1497,7 @@ static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; asc->addr_width = aspeed_2400_spi1_addr_width; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2400_spi1_info = { @@ -1434,7 +1522,7 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); - dc->desc = "Aspeed 2600 FMC Controller"; + dc->desc = "Aspeed 2500 FMC Controller"; asc->r_conf = R_CONF; asc->r_ce_ctrl = R_CE_CTRL; asc->r_ctrl0 = R_CTRL0; @@ -1450,10 +1538,12 @@ static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 4; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2500_fmc_info = { @@ -1472,7 +1562,7 @@ static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); - dc->desc = "Aspeed 2600 SPI1 Controller"; + dc->desc = "Aspeed 2500 SPI1 Controller"; asc->r_conf = R_CONF; asc->r_ce_ctrl = R_CE_CTRL; asc->r_ctrl0 = R_CTRL0; @@ -1489,6 +1579,7 @@ static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2500_spi1_info = { @@ -1507,7 +1598,7 @@ static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); - dc->desc = "Aspeed 2600 SPI2 Controller"; + dc->desc = "Aspeed 2500 SPI2 Controller"; asc->r_conf = R_CONF; asc->r_ce_ctrl = R_CE_CTRL; asc->r_ctrl0 = R_CTRL0; @@ -1524,6 +1615,7 @@ static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) asc->segment_to_reg = aspeed_smc_segment_to_reg; asc->reg_to_segment = aspeed_smc_reg_to_segment; asc->dma_ctrl = aspeed_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2500_spi2_info = { @@ -1606,10 +1698,12 @@ static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) ASPEED_SMC_FEATURE_WDT_CONTROL; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2600_fmc_info = { @@ -1644,10 +1738,12 @@ static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) ASPEED_SMC_FEATURE_DMA_GRANT; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2600_spi1_info = { @@ -1683,10 +1779,12 @@ static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) ASPEED_SMC_FEATURE_DMA_GRANT; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x3FFFFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_2600_spi2_info = { @@ -1764,10 +1862,12 @@ static void aspeed_1030_fmc_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_1030_smc_segment_to_reg; asc->reg_to_segment = aspeed_1030_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_1030_fmc_info = { @@ -1801,10 +1901,12 @@ static void aspeed_1030_spi1_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_1030_spi1_info = { @@ -1837,10 +1939,12 @@ static void aspeed_1030_spi2_class_init(ObjectClass *klass, void *data) asc->features = ASPEED_SMC_FEATURE_DMA; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; + asc->dma_start_length = 1; asc->nregs = ASPEED_SMC_R_MAX; asc->segment_to_reg = aspeed_2600_smc_segment_to_reg; asc->reg_to_segment = aspeed_2600_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_smc_flash_ops; } static const TypeInfo aspeed_1030_spi2_info = { @@ -1849,6 +1953,234 @@ static const TypeInfo aspeed_1030_spi2_info = { .class_init = aspeed_1030_spi2_class_init, }; +/* + * The FMC Segment Registers of the AST2700 have a 64KB unit. + * Only bits [31:16] are used for decoding. + */ +#define AST2700_SEG_ADDR_MASK 0xffff0000 + +static uint32_t aspeed_2700_smc_segment_to_reg(const AspeedSMCState *s, + const AspeedSegments *seg) +{ + uint32_t reg = 0; + + /* Disabled segments have a nil register */ + if (!seg->size) { + return 0; + } + + reg |= (seg->addr & AST2700_SEG_ADDR_MASK) >> 16; /* start offset */ + reg |= (seg->addr + seg->size - 1) & AST2700_SEG_ADDR_MASK; /* end offset */ + return reg; +} + +static void aspeed_2700_smc_reg_to_segment(const AspeedSMCState *s, + uint32_t reg, AspeedSegments *seg) +{ + uint32_t start_offset = (reg << 16) & AST2700_SEG_ADDR_MASK; + uint32_t end_offset = reg & AST2700_SEG_ADDR_MASK; + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + + if (reg) { + seg->addr = asc->flash_window_base + start_offset; + seg->size = end_offset + (64 * KiB) - start_offset; + } else { + seg->addr = asc->flash_window_base; + seg->size = 0; + } +} + +static const uint32_t aspeed_2700_fmc_resets[ASPEED_SMC_R_MAX] = { + [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | + CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1), + [R_CE_CTRL] = 0x0000aa00, + [R_CTRL0] = 0x406b0641, + [R_CTRL1] = 0x00000400, + [R_CTRL2] = 0x00000400, + [R_CTRL3] = 0x00000400, + [R_SEG_ADDR0] = 0x08000000, + [R_SEG_ADDR1] = 0x10000800, + [R_SEG_ADDR2] = 0x00000000, + [R_SEG_ADDR3] = 0x00000000, + [R_DUMMY_DATA] = 0x00010000, + [R_DMA_DRAM_ADDR_HIGH] = 0x00000000, + [R_TIMINGS] = 0x007b0000, +}; + +static const MemoryRegionOps aspeed_2700_smc_flash_ops = { + .read = aspeed_smc_flash_read, + .write = aspeed_smc_flash_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static const AspeedSegments aspeed_2700_fmc_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 128 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ + { 256 * MiB, 128 * MiB }, /* default is disabled but needed for -kernel */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_fmc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 FMC Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 3; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 3; + asc->segments = aspeed_2700_fmc_segments; + asc->segment_addr_mask = 0xffffffff; + asc->resets = aspeed_2700_fmc_resets; + asc->flash_window_base = 0x100000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x2FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_fmc_info = { + .name = "aspeed.fmc-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_fmc_class_init, +}; + +static const AspeedSegments aspeed_2700_spi0_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 128 * MiB, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_spi0_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 SPI0 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_2700_spi0_segments; + asc->segment_addr_mask = 0xffffffff; + asc->flash_window_base = 0x180000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x2FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_spi0_info = { + .name = "aspeed.spi0-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_spi0_class_init, +}; + +static const AspeedSegments aspeed_2700_spi1_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_spi1_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 SPI1 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_2700_spi1_segments; + asc->segment_addr_mask = 0xffffffff; + asc->flash_window_base = 0x200000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x2FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_spi1_info = { + .name = "aspeed.spi1-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_spi1_class_init, +}; + +static const AspeedSegments aspeed_2700_spi2_segments[] = { + { 0x0, 128 * MiB }, /* start address is readonly */ + { 0x0, 0 }, /* disabled */ +}; + +static void aspeed_2700_spi2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); + + dc->desc = "Aspeed 2700 SPI2 Controller"; + asc->r_conf = R_CONF; + asc->r_ce_ctrl = R_CE_CTRL; + asc->r_ctrl0 = R_CTRL0; + asc->r_timings = R_TIMINGS; + asc->nregs_timings = 2; + asc->conf_enable_w0 = CONF_ENABLE_W0; + asc->cs_num_max = 2; + asc->segments = aspeed_2700_spi2_segments; + asc->segment_addr_mask = 0xffffffff; + asc->flash_window_base = 0x280000000; + asc->flash_window_size = 1 * GiB; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + asc->dma_flash_mask = 0x0FFFFFFC; + asc->dma_dram_mask = 0xFFFFFFFC; + asc->dma_start_length = 1; + asc->nregs = ASPEED_SMC_R_MAX; + asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; + asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; + asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->reg_ops = &aspeed_2700_smc_flash_ops; +} + +static const TypeInfo aspeed_2700_spi2_info = { + .name = "aspeed.spi2-ast2700", + .parent = TYPE_ASPEED_SMC, + .class_init = aspeed_2700_spi2_class_init, +}; + static void aspeed_smc_register_types(void) { type_register_static(&aspeed_smc_flash_info); @@ -1865,6 +2197,10 @@ static void aspeed_smc_register_types(void) type_register_static(&aspeed_1030_fmc_info); type_register_static(&aspeed_1030_spi1_info); type_register_static(&aspeed_1030_spi2_info); + type_register_static(&aspeed_2700_fmc_info); + type_register_static(&aspeed_2700_spi0_info); + type_register_static(&aspeed_2700_spi1_info); + type_register_static(&aspeed_2700_spi2_info); } type_init(aspeed_smc_register_types) diff --git a/hw/ssi/bcm2835_spi.c b/hw/ssi/bcm2835_spi.c new file mode 100644 index 0000000000..ebd8809f7c --- /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); + + device_class_set_legacy_reset(dc, 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 57df462e3c..9e07432f7c 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" @@ -204,9 +205,10 @@ static void ibex_spi_host_irq(IbexSPIHostState *s) 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 (FIELD_EX32(intr_test_reg, INTR_STATE, SPI_EVENT)) { @@ -228,8 +230,9 @@ static void ibex_spi_host_irq(IbexSPIHostState *s) 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) @@ -567,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), @@ -625,7 +628,7 @@ static void ibex_spi_host_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ibex_spi_host_realize; - dc->reset = ibex_spi_host_reset; + device_class_set_legacy_reset(dc, ibex_spi_host_reset); dc->vmsd = &vmstate_ibex; device_class_set_props(dc, ibex_spi_properties); } diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index 189423bb3a..2e317879b4 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -53,7 +53,7 @@ static const char *imx_spi_reg_name(uint32_t reg) case ECSPI_MSGDATA: return "ECSPI_MSGDATA"; default: - sprintf(unknown, "%u ?", reg); + snprintf(unknown, sizeof(unknown), "%u ?", reg); return unknown; } } @@ -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), @@ -481,7 +481,7 @@ static void imx_spi_class_init(ObjectClass *klass, void *data) dc->realize = imx_spi_realize; dc->vmsd = &vmstate_imx_spi; - dc->reset = imx_spi_reset; + device_class_set_legacy_reset(dc, imx_spi_reset); dc->desc = "i.MX SPI Controller"; } diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 702aa5e4df..6afb1ea200 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -1,13 +1,15 @@ -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_ALLWINNER_A10_SPI', if_true: files('allwinner-a10-spi.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_IBEX', if_true: files('ibex_spi_host.c')) +system_ss.add(when: 'CONFIG_BCM2835_SPI', if_true: files('bcm2835_spi.c')) +system_ss.add(when: 'CONFIG_PNV_SPI', if_true: files('pnv_spi.c')) diff --git a/hw/ssi/mss-spi.c b/hw/ssi/mss-spi.c index b2432c5a13..340adcdd3f 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), @@ -403,7 +403,7 @@ static void mss_spi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = mss_spi_realize; - dc->reset = mss_spi_reset; + device_class_set_legacy_reset(dc, mss_spi_reset); dc->vmsd = &vmstate_mss_spi; } diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 4eedb2927e..119c38c415 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -483,7 +483,7 @@ static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type) s->regs[NPCM7XX_FIU_CFG] = 0x0000000b; } -static void npcm7xx_fiu_hold_reset(Object *obj) +static void npcm7xx_fiu_hold_reset(Object *obj, ResetType type) { NPCM7xxFIUState *s = NPCM7XX_FIU(obj); int i; @@ -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 deleted file mode 100644 index 7c7e689707..0000000000 --- a/hw/ssi/omap_spi.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * TI OMAP processor's Multichannel SPI emulation. - * - * Copyright (C) 2007-2009 Nokia Corporation - * - * Original code for OMAP2 by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/arm/omap.h" - -/* Multichannel SPI */ -struct omap_mcspi_s { - MemoryRegion iomem; - qemu_irq irq; - int chnum; - - uint32_t sysconfig; - uint32_t systest; - uint32_t irqst; - uint32_t irqen; - uint32_t wken; - uint32_t control; - - struct omap_mcspi_ch_s { - qemu_irq txdrq; - qemu_irq rxdrq; - uint32_t (*txrx)(void *opaque, uint32_t, int); - void *opaque; - - uint32_t tx; - uint32_t rx; - - uint32_t config; - uint32_t status; - uint32_t control; - } ch[4]; -}; - -static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) -{ - qemu_set_irq(s->irq, s->irqst & s->irqen); -} - -static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) -{ - qemu_set_irq(ch->txdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 14)) && /* DMAW */ - (ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1); /* TRM */ - qemu_set_irq(ch->rxdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 15)) && /* DMAW */ - (ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2); /* TRM */ -} - -static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) -{ - struct omap_mcspi_ch_s *ch = s->ch + chnum; - - if (!(ch->control & 1)) /* EN */ - return; - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - goto intr_update; - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - goto intr_update; - - if (!(s->control & 1) || /* SINGLE */ - (ch->config & (1 << 20))) { /* FORCE */ - if (ch->txrx) - ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ - 1 + (0x1f & (ch->config >> 7))); - } - - ch->tx = 0; - ch->status |= 1 << 2; /* EOT */ - ch->status |= 1 << 1; /* TXS */ - if (((ch->config >> 12) & 3) != 2) /* TRM */ - ch->status |= 1 << 0; /* RXS */ - -intr_update: - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ - omap_mcspi_interrupt_update(s); - omap_mcspi_dmarequest_update(ch); -} - -void omap_mcspi_reset(struct omap_mcspi_s *s) -{ - int ch; - - s->sysconfig = 0; - s->systest = 0; - s->irqst = 0; - s->irqen = 0; - s->wken = 0; - s->control = 4; - - for (ch = 0; ch < 4; ch ++) { - s->ch[ch].config = 0x060000; - s->ch[ch].status = 2; /* TXS */ - s->ch[ch].control = 0; - - omap_mcspi_dmarequest_update(s->ch + ch); - } - - omap_mcspi_interrupt_update(s); -} - -static uint64_t omap_mcspi_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - uint32_t ret; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - return 0x91; - - case 0x10: /* MCSPI_SYSCONFIG */ - return s->sysconfig; - - case 0x14: /* MCSPI_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x18: /* MCSPI_IRQSTATUS */ - return s->irqst; - - case 0x1c: /* MCSPI_IRQENABLE */ - return s->irqen; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - return s->wken; - - case 0x24: /* MCSPI_SYST */ - return s->systest; - - case 0x28: /* MCSPI_MODULCTRL */ - return s->control; - - case 0x68: ch ++; - /* fall through */ - case 0x54: ch ++; - /* fall through */ - case 0x40: ch ++; - /* fall through */ - case 0x2c: /* MCSPI_CHCONF */ - return s->ch[ch].config; - - case 0x6c: ch ++; - /* fall through */ - case 0x58: ch ++; - /* fall through */ - case 0x44: ch ++; - /* fall through */ - case 0x30: /* MCSPI_CHSTAT */ - return s->ch[ch].status; - - case 0x70: ch ++; - /* fall through */ - case 0x5c: ch ++; - /* fall through */ - case 0x48: ch ++; - /* fall through */ - case 0x34: /* MCSPI_CHCTRL */ - return s->ch[ch].control; - - case 0x74: ch ++; - /* fall through */ - case 0x60: ch ++; - /* fall through */ - case 0x4c: ch ++; - /* fall through */ - case 0x38: /* MCSPI_TX */ - return s->ch[ch].tx; - - case 0x78: ch ++; - /* fall through */ - case 0x64: ch ++; - /* fall through */ - case 0x50: ch ++; - /* fall through */ - case 0x3c: /* MCSPI_RX */ - s->ch[ch].status &= ~(1 << 0); /* RXS */ - ret = s->ch[ch].rx; - omap_mcspi_transfer_run(s, ch); - return ret; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mcspi_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - - if (size != 4) { - omap_badwidth_write32(opaque, addr, value); - return; - } - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - case 0x14: /* MCSPI_SYSSTATUS */ - case 0x30: /* MCSPI_CHSTAT0 */ - case 0x3c: /* MCSPI_RX0 */ - case 0x44: /* MCSPI_CHSTAT1 */ - case 0x50: /* MCSPI_RX1 */ - case 0x58: /* MCSPI_CHSTAT2 */ - case 0x64: /* MCSPI_RX2 */ - case 0x6c: /* MCSPI_CHSTAT3 */ - case 0x78: /* MCSPI_RX3 */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* MCSPI_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_mcspi_reset(s); - s->sysconfig = value & 0x31d; - break; - - case 0x18: /* MCSPI_IRQSTATUS */ - if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { - s->irqst &= ~value; - omap_mcspi_interrupt_update(s); - } - break; - - case 0x1c: /* MCSPI_IRQENABLE */ - s->irqen = value & 0x1777f; - omap_mcspi_interrupt_update(s); - break; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - s->wken = value & 1; - break; - - case 0x24: /* MCSPI_SYST */ - if (s->control & (1 << 3)) /* SYSTEM_TEST */ - if (value & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->systest = value & 0xfff; - break; - - case 0x28: /* MCSPI_MODULCTRL */ - if (value & (1 << 3)) /* SYSTEM_TEST */ - if (s->systest & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->control = value & 0xf; - break; - - case 0x68: ch ++; - /* fall through */ - case 0x54: ch ++; - /* fall through */ - case 0x40: ch ++; - /* fall through */ - case 0x2c: /* MCSPI_CHCONF */ - if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ - omap_mcspi_dmarequest_update(s->ch + ch); - if (((value >> 12) & 3) == 3) { /* TRM */ - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid TRM value (3)\n", - __func__); - } - if (((value >> 7) & 0x1f) < 3) { /* WL */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid WL value (%" PRIx64 ")\n", - __func__, (value >> 7) & 0x1f); - } - s->ch[ch].config = value & 0x7fffff; - break; - - case 0x70: ch ++; - /* fall through */ - case 0x5c: ch ++; - /* fall through */ - case 0x48: ch ++; - /* fall through */ - case 0x34: /* MCSPI_CHCTRL */ - if (value & ~s->ch[ch].control & 1) { /* EN */ - s->ch[ch].control |= 1; - omap_mcspi_transfer_run(s, ch); - } else - s->ch[ch].control = value & 1; - break; - - case 0x74: ch ++; - /* fall through */ - case 0x60: ch ++; - /* fall through */ - case 0x4c: ch ++; - /* fall through */ - case 0x38: /* MCSPI_TX */ - s->ch[ch].tx = value; - s->ch[ch].status &= ~(1 << 1); /* TXS */ - omap_mcspi_transfer_run(s, ch); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static const MemoryRegionOps omap_mcspi_ops = { - .read = omap_mcspi_read, - .write = omap_mcspi_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, - qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) -{ - struct omap_mcspi_s *s = g_new0(struct omap_mcspi_s, 1); - struct omap_mcspi_ch_s *ch = s->ch; - - s->irq = irq; - s->chnum = chnum; - while (chnum --) { - ch->txdrq = *drq ++; - ch->rxdrq = *drq ++; - ch ++; - } - omap_mcspi_reset(s); - - memory_region_init_io(&s->iomem, NULL, &omap_mcspi_ops, s, "omap.mcspi", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} - -void omap_mcspi_attach(struct omap_mcspi_s *s, - uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, - int chipselect) -{ - if (chipselect < 0 || chipselect >= s->chnum) - hw_error("%s: Bad chipselect %i\n", __func__, chipselect); - - s->ch[chipselect].txrx = txrx; - s->ch[chipselect].opaque = opaque; -} diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index 8954ffebb1..53c9c225ad 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), @@ -296,7 +296,7 @@ static void pl022_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = pl022_reset; + device_class_set_legacy_reset(dc, pl022_reset); dc->vmsd = &vmstate_pl022; dc->realize = pl022_realize; } diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c new file mode 100644 index 0000000000..c21b2ebb3c --- /dev/null +++ b/hw/ssi/pnv_spi.c @@ -0,0 +1,1270 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, 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_xscom.h" +#include "hw/ssi/pnv_spi.h" +#include "hw/ssi/pnv_spi_regs.h" +#include "hw/ssi/ssi.h" +#include +#include "hw/irq.h" +#include "trace.h" + +#define PNV_SPI_OPCODE_LO_NIBBLE(x) (x & 0x0F) +#define PNV_SPI_MASKED_OPCODE(x) (x & 0xF0) + +/* + * Macro from include/hw/ppc/fdt.h + * fdt.h cannot be included here as it contain ppc target specific dependency. + */ +#define _FDT(exp) \ + do { \ + int _ret = (exp); \ + if (_ret < 0) { \ + qemu_log_mask(LOG_GUEST_ERROR, \ + "error creating device tree: %s: %s", \ + #exp, fdt_strerror(_ret)); \ + exit(1); \ + } \ + } while (0) + +/* PnvXferBuffer */ +typedef struct PnvXferBuffer { + + uint32_t len; + uint8_t *data; + +} PnvXferBuffer; + +/* pnv_spi_xfer_buffer_methods */ +static PnvXferBuffer *pnv_spi_xfer_buffer_new(void) +{ + PnvXferBuffer *payload = g_malloc0(sizeof(*payload)); + + return payload; +} + +static void pnv_spi_xfer_buffer_free(PnvXferBuffer *payload) +{ + g_free(payload->data); + g_free(payload); +} + +static uint8_t *pnv_spi_xfer_buffer_write_ptr(PnvXferBuffer *payload, + uint32_t offset, uint32_t length) +{ + if (payload->len < (offset + length)) { + payload->len = offset + length; + payload->data = g_realloc(payload->data, payload->len); + } + return &payload->data[offset]; +} + +static bool does_rdr_match(PnvSpi *s) +{ + /* + * According to spec, the mask bits that are 0 are compared and the + * bits that are 1 are ignored. + */ + uint16_t rdr_match_mask = GETFIELD(SPI_MM_RDR_MATCH_MASK, + s->regs[SPI_MM_REG]); + uint16_t rdr_match_val = GETFIELD(SPI_MM_RDR_MATCH_VAL, + s->regs[SPI_MM_REG]); + + if ((~rdr_match_mask & rdr_match_val) == ((~rdr_match_mask) & + GETFIELD(PPC_BITMASK(48, 63), s->regs[SPI_RCV_DATA_REG]))) { + return true; + } + return false; +} + +static uint8_t get_from_offset(PnvSpi *s, uint8_t offset) +{ + uint8_t byte; + + /* + * Offset is an index between 0 and PNV_SPI_REG_SIZE - 1 + * Check the offset before using it. + */ + if (offset < PNV_SPI_REG_SIZE) { + byte = (s->regs[SPI_XMIT_DATA_REG] >> (56 - offset * 8)) & 0xFF; + } else { + /* + * Log an error and return a 0xFF since we have to assign something + * to byte before returning. + */ + qemu_log_mask(LOG_GUEST_ERROR, "Invalid offset = %d used to get byte " + "from TDR\n", offset); + byte = 0xff; + } + return byte; +} + +static uint8_t read_from_frame(PnvSpi *s, uint8_t *read_buf, uint8_t nr_bytes, + uint8_t ecc_count, uint8_t shift_in_count) +{ + uint8_t byte; + int count = 0; + + while (count < nr_bytes) { + shift_in_count++; + if ((ecc_count != 0) && + (shift_in_count == (PNV_SPI_REG_SIZE + ecc_count))) { + shift_in_count = 0; + } else { + byte = read_buf[count]; + trace_pnv_spi_shift_rx(byte, count); + s->regs[SPI_RCV_DATA_REG] = (s->regs[SPI_RCV_DATA_REG] << 8) | byte; + } + count++; + } /* end of while */ + return shift_in_count; +} + +static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload) +{ + uint8_t ecc_count; + uint8_t shift_in_count; + + /* + * Processing here must handle: + * - Which bytes in the payload we should move to the RDR + * - Explicit mode counter configuration settings + * - RDR full and RDR overrun status + */ + + /* + * First check that the response payload is the exact same + * number of bytes as the request payload was + */ + if (rsp_payload->len != (s->N1_bytes + s->N2_bytes)) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in " + "bytes, expected %d, got %d\n", + (s->N1_bytes + s->N2_bytes), rsp_payload->len); + } else { + uint8_t ecc_control; + trace_pnv_spi_rx_received(rsp_payload->len); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * Adding an ECC count let's us know when we have found a payload byte + * that was shifted in but cannot be loaded into RDR. Bits 29-30 of + * clock_config_reset_control register equal to either 0b00 or 0b10 + * indicate that we are taking in data with ECC and either applying + * the ECC or discarding it. + */ + ecc_count = 0; + ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + ecc_count = 1; + } + /* + * Use the N1_rx and N2_rx counts to control shifting data from the + * payload into the RDR. Keep an overall count of the number of bytes + * shifted into RDR so we can discard every 9th byte when ECC is + * enabled. + */ + shift_in_count = 0; + /* Handle the N1 portion of the frame first */ + if (s->N1_rx != 0) { + trace_pnv_spi_rx_read_N1frame(); + shift_in_count = read_from_frame(s, &rsp_payload->data[0], + s->N1_bytes, ecc_count, shift_in_count); + } + /* Handle the N2 portion of the frame */ + if (s->N2_rx != 0) { + trace_pnv_spi_rx_read_N2frame(); + shift_in_count = read_from_frame(s, + &rsp_payload->data[s->N1_bytes], s->N2_bytes, + ecc_count, shift_in_count); + } + if ((s->N1_rx + s->N2_rx) > 0) { + /* + * Data was received so handle RDR status. + * It is easier to handle RDR_full and RDR_overrun status here + * since the RDR register's shift_byte_in method is called + * multiple times in a row. Controlling RDR status is done here + * instead of in the RDR scoped methods for that reason. + */ + if (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1) { + /* + * Data was shifted into the RDR before having been read + * causing previous data to have been overrun. + */ + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, 1); + } else { + /* + * Set status to indicate that the received data register is + * full. This flag is only cleared once the RDR is unloaded. + */ + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 1); + } + } + } /* end of else */ +} /* end of spi_response() */ + +static void transfer(PnvSpi *s, PnvXferBuffer *payload) +{ + uint32_t tx; + uint32_t rx; + PnvXferBuffer *rsp_payload = NULL; + + rsp_payload = pnv_spi_xfer_buffer_new(); + if (!rsp_payload) { + return; + } + for (int offset = 0; offset < payload->len; offset += s->transfer_len) { + tx = 0; + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload->len) { + tx <<= 8; + } else { + tx = (tx << 8) | payload->data[offset + i]; + } + } + rx = ssi_transfer(s->ssi_bus, tx); + for (int i = 0; i < s->transfer_len; i++) { + if ((offset + i) >= payload->len) { + break; + } + *(pnv_spi_xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) = + (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF; + } + } + spi_response(s, s->N1_bits, rsp_payload); + pnv_spi_xfer_buffer_free(rsp_payload); +} + +static inline uint8_t get_seq_index(PnvSpi *s) +{ + return GETFIELD(SPI_STS_SEQ_INDEX, s->status); +} + +static inline void next_sequencer_fsm(PnvSpi *s) +{ + uint8_t seq_index = get_seq_index(s); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, (seq_index + 1)); + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT); +} + +/* + * Calculate the N1 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N1 opcode + * and doesn't test it. + * The counters returned are: + * N1 bits: Number of bits in the payload data that are significant + * to the responder. + * N1_bytes: Total count of payload bytes for the N1 (portion of the) frame. + * N1_tx: Total number of bytes taken from TDR for N1 + * N1_rx: Total number of bytes taken from the payload for N1 + */ +static void calculate_N1(PnvSpi *s, uint8_t opcode) +{ + /* + * Shift_N1 opcode form: 0x3M + * Implicit mode: + * If M != 0 the shift count is M bytes and M is the number of tx bytes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count control + * register fields. Note that we only check for forced Implicit mode when + * M != 0 since the mode doesn't make sense when M = 0. + * Explicit mode: + * If M == 0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) { + /* Explicit mode */ + s->N1_bits = GETFIELD(SPI_CTR_CFG_N1, s->regs[SPI_CTR_CFG_REG]); + s->N1_bytes = (s->N1_bits + 7) / 8; + s->N1_tx = 0; + s->N1_rx = 0; + /* If tx count control for N1 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_tx = s->N1_bytes; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_rx = s->N1_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + s->N1_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->N1_bits = s->N1_bytes * 8; + /* + * Assume that we are going to transmit the count + * (pure Implicit only) + */ + s->N1_tx = s->N1_bytes; + s->N1_rx = 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate transmit then reset the tx count to 0 + */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, + s->regs[SPI_CTR_CFG_REG]) == 0) { + s->N1_tx = 0; + } + /* If rx count control for N1 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, + s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N1_rx = s->N1_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the known size + * of the shift register, 64 bits or 72 bits if ECC is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM + * error bit. + */ + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, + s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + if (s->N1_bytes > (PNV_SPI_REG_SIZE + 1)) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when " + "ECC enabled, bytes = 0x%x, bits = 0x%x\n", + s->N1_bytes, s->N1_bits); + s->N1_bytes = PNV_SPI_REG_SIZE + 1; + s->N1_bits = s->N1_bytes * 8; + } + } else if (s->N1_bytes > PNV_SPI_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, " + "bytes = 0x%x, bits = 0x%x\n", + s->N1_bytes, s->N1_bits); + s->N1_bytes = PNV_SPI_REG_SIZE; + s->N1_bits = s->N1_bytes * 8; + } +} /* end of calculate_N1 */ + +/* + * Shift_N1 operation handler method + */ +static bool operation_shiftn1(PnvSpi *s, uint8_t opcode, + PnvXferBuffer **payload, bool send_n1_alone) +{ + uint8_t n1_count; + bool stop = false; + + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload == NULL) { + *payload = pnv_spi_xfer_buffer_new(); + } + /* + * Use a combination of N1 counters to build the N1 portion of the + * transmit payload. + * We only care about transmit at this time since the request payload + * only represents data going out on the controller output line. + * Leave mode specific considerations in the calculate function since + * all we really care about are counters that tell use exactly how + * many bytes are in the payload and how many of those bytes to + * include from the TDR into the payload. + */ + calculate_N1(s, opcode); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * Zero out the N2 counters here in case there is no N2 operation following + * the N1 operation in the sequencer. This keeps leftover N2 information + * from interfering with spi_response logic. + */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + /* + * N1_bytes is the overall size of the N1 portion of the frame regardless of + * whether N1 is used for tx, rx or both. Loop over the size to build a + * payload that is N1_bytes long. + * N1_tx is the count of bytes to take from the TDR and "shift" into the + * frame which means append those bytes to the payload for the N1 portion + * of the frame. + * If N1_tx is 0 or if the count exceeds the size of the TDR append 0xFF to + * the frame until the overall N1 count is reached. + */ + n1_count = 0; + while (n1_count < s->N1_bytes) { + /* + * Assuming that if N1_tx is not equal to 0 then it is the same as + * N1_bytes. + */ + if ((s->N1_tx != 0) && (n1_count < PNV_SPI_REG_SIZE)) { + + if (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1) { + /* + * Note that we are only appending to the payload IF the TDR + * is full otherwise we don't touch the payload because we are + * going to NOT send the payload and instead tell the sequencer + * that called us to stop and wait for a TDR write so we have + * data to load into the payload. + */ + uint8_t n1_byte = 0x00; + n1_byte = get_from_offset(s, n1_count); + trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = + n1_byte; + } else { + /* + * We hit a shift_n1 opcode TX but the TDR is empty, tell the + * sequencer to stop and break this loop. + */ + trace_pnv_spi_sequencer_stop_requested("Shift N1" + "set for transmit but TDR is empty"); + stop = true; + break; + } + } else { + /* + * Cases here: + * - we are receiving during the N1 frame segment and the RDR + * is full so we need to stop until the RDR is read + * - we are transmitting and we don't care about RDR status + * since we won't be loading RDR during the frame segment. + * - we are receiving and the RDR is empty so we allow the operation + * to proceed. + */ + if ((s->N1_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL, + s->status) == 1)) { + trace_pnv_spi_sequencer_stop_requested("shift N1" + "set for receive but RDR is full"); + stop = true; + break; + } else { + trace_pnv_spi_tx_append_FF("n1_byte"); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = 0xff; + } + } + n1_count++; + } /* end of while */ + /* + * If we are not stopping due to an empty TDR and we are doing an N1 TX + * and the TDR is full we need to clear the TDR_full status. + * Do this here instead of up in the loop above so we don't log the message + * in every loop iteration. + * Ignore the send_n1_alone flag, all that does is defer the TX until the N2 + * operation, which was found immediately after the current opcode. The TDR + * was unloaded and will be shifted so we have to clear the TDR_full status. + */ + if (!stop && (s->N1_tx != 0) && + (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); + } + /* + * There are other reasons why the shifter would stop, such as a TDR empty + * or RDR full condition with N1 set to receive. If we haven't stopped due + * to either one of those conditions then check if the send_n1_alone flag is + * equal to False, indicating the next opcode is an N2 operation, AND if + * the N2 counter reload switch (bit 0 of the N2 count control field) is + * set. This condition requires a pacing write to "kick" off the N2 + * shift which includes the N1 shift as well when send_n1_alone is False. + */ + if (!stop && !send_n1_alone && + (GETFIELD(SPI_CTR_CFG_N2_CTRL_B0, s->regs[SPI_CTR_CFG_REG]) == 1)) { + trace_pnv_spi_sequencer_stop_requested("N2 counter reload " + "active, stop N1 shift, TDR_underrun set to 1"); + stop = true; + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 1); + } + /* + * If send_n1_alone is set AND we have a full TDR then this is the first and + * last payload to send and we don't have an N2 frame segment to add to the + * payload. + */ + if (send_n1_alone && !stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + trace_pnv_spi_tx_request("Shifting N1 frame", (*payload)->len); + transfer(s, *payload); + /* The N1 frame shift is complete so reset the N1 counters */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + pnv_spi_xfer_buffer_free(*payload); + *payload = NULL; + } + return stop; +} /* end of operation_shiftn1() */ + +/* + * Calculate the N2 counters based on passed in opcode and + * internal register values. + * The method assumes that the opcode is a Shift_N2 opcode + * and doesn't test it. + * The counters returned are: + * N2 bits: Number of bits in the payload data that are significant + * to the responder. + * N2_bytes: Total count of payload bytes for the N2 frame. + * N2_tx: Total number of bytes taken from TDR for N2 + * N2_rx: Total number of bytes taken from the payload for N2 + */ +static void calculate_N2(PnvSpi *s, uint8_t opcode) +{ + /* + * Shift_N2 opcode form: 0x4M + * Implicit mode: + * If M!=0 the shift count is M bytes and M is the number of rx bytes. + * Forced Implicit mode: + * M is the shift count but tx and rx is determined by the count control + * register fields. Note that we only check for Forced Implicit mode when + * M != 0 since the mode doesn't make sense when M = 0. + * Explicit mode: + * If M==0 then shift count is number of bits defined in the + * Counter Configuration Register's shift_count_N1 field. + */ + if (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) { + /* Explicit mode */ + s->N2_bits = GETFIELD(SPI_CTR_CFG_N2, s->regs[SPI_CTR_CFG_REG]); + s->N2_bytes = (s->N2_bits + 7) / 8; + s->N2_tx = 0; + s->N2_rx = 0; + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_tx = s->N2_bytes; + } + /* If rx count control for N2 is set, load the rx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_rx = s->N2_bytes; + } + } else { + /* Implicit mode/Forced Implicit mode, use M field from opcode */ + s->N2_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + s->N2_bits = s->N2_bytes * 8; + /* Assume that we are going to receive the count */ + s->N2_rx = s->N2_bytes; + s->N2_tx = 0; + /* Let Forced Implicit mode have an effect on the counts */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) { + /* + * If Forced Implicit mode and count control doesn't + * indicate a receive then reset the rx count to 0 + */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, + s->regs[SPI_CTR_CFG_REG]) == 0) { + s->N2_rx = 0; + } + /* If tx count control for N2 is set, load the tx value */ + if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, + s->regs[SPI_CTR_CFG_REG]) == 1) { + s->N2_tx = s->N2_bytes; + } + } + } + /* + * Enforce an upper limit on the size of N1 that is equal to the + * known size of the shift register, 64 bits or 72 bits if ECC + * is enabled. + * If the size exceeds 72 bits it is a user error so log an error, + * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM + * error bit. + */ + uint8_t ecc_control = GETFIELD(SPI_CLK_CFG_ECC_CTRL, + s->regs[SPI_CLK_CFG_REG]); + if (ecc_control == 0 || ecc_control == 2) { + if (s->N2_bytes > (PNV_SPI_REG_SIZE + 1)) { + /* Unsupported N2 shift size when ECC enabled */ + s->N2_bytes = PNV_SPI_REG_SIZE + 1; + s->N2_bits = s->N2_bytes * 8; + } + } else if (s->N2_bytes > PNV_SPI_REG_SIZE) { + /* Unsupported N2 shift size */ + s->N2_bytes = PNV_SPI_REG_SIZE; + s->N2_bits = s->N2_bytes * 8; + } +} /* end of calculate_N2 */ + +/* + * Shift_N2 operation handler method + */ + +static bool operation_shiftn2(PnvSpi *s, uint8_t opcode, + PnvXferBuffer **payload) +{ + uint8_t n2_count; + bool stop = false; + + /* + * If there isn't a current payload left over from a stopped sequence + * create a new one. + */ + if (*payload == NULL) { + *payload = pnv_spi_xfer_buffer_new(); + } + /* + * Use a combination of N2 counters to build the N2 portion of the + * transmit payload. + */ + calculate_N2(s, opcode); + trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx, + s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx); + /* + * The only difference between this code and the code for shift N1 is + * that this code has to account for the possible presence of N1 transmit + * bytes already taken from the TDR. + * If there are bytes to be transmitted for the N2 portion of the frame + * and there are still bytes in TDR that have not been copied into the + * TX data of the payload, this code will handle transmitting those + * remaining bytes. + * If for some reason the transmit count(s) add up to more than the size + * of the TDR we will just append 0xFF to the transmit payload data until + * the payload is N1 + N2 bytes long. + */ + n2_count = 0; + while (n2_count < s->N2_bytes) { + /* + * If the RDR is full and we need to RX just bail out, letting the + * code continue will end up building the payload twice in the same + * buffer since RDR full causes a sequence stop and restart. + */ + if ((s->N2_rx != 0) && + (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) { + trace_pnv_spi_sequencer_stop_requested("shift N2 set" + "for receive but RDR is full"); + stop = true; + break; + } + if ((s->N2_tx != 0) && ((s->N1_tx + n2_count) < + PNV_SPI_REG_SIZE)) { + /* Always append data for the N2 segment if it is set for TX */ + uint8_t n2_byte = 0x00; + n2_byte = get_from_offset(s, (s->N1_tx + n2_count)); + trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count)); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = n2_byte; + } else { + /* + * Regardless of whether or not N2 is set for TX or RX, we need + * the number of bytes in the payload to match the overall length + * of the operation. + */ + trace_pnv_spi_tx_append_FF("n2_byte"); + *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) + = 0xff; + } + n2_count++; + } /* end of while */ + if (!stop) { + /* We have a TX and a full TDR or an RX and an empty RDR */ + trace_pnv_spi_tx_request("Shifting N2 frame", (*payload)->len); + transfer(s, *payload); + /* + * If we are doing an N2 TX and the TDR is full we need to clear the + * TDR_full status. Do this here instead of up in the loop above so we + * don't log the message in every loop iteration. + */ + if ((s->N2_tx != 0) && + (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) { + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0); + } + /* + * The N2 frame shift is complete so reset the N2 counters. + * Reset the N1 counters also in case the frame was a combination of + * N1 and N2 segments. + */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + s->N1_bits = 0; + s->N1_bytes = 0; + s->N1_tx = 0; + s->N1_rx = 0; + pnv_spi_xfer_buffer_free(*payload); + *payload = NULL; + } + return stop; +} /* end of operation_shiftn2()*/ + +static void operation_sequencer(PnvSpi *s) +{ + /* + * Loop through each sequencer operation ID and perform the requested + * operations. + * Flag for indicating if we should send the N1 frame or wait to combine + * it with a preceding N2 frame. + */ + bool send_n1_alone = true; + bool stop = false; /* Flag to stop the sequencer */ + uint8_t opcode = 0; + uint8_t masked_opcode = 0; + + /* + * PnvXferBuffer for containing the payload of the SPI frame. + * This is a static because there are cases where a sequence has to stop + * and wait for the target application to unload the RDR. If this occurs + * during a sequence where N1 is not sent alone and instead combined with + * N2 since the N1 tx length + the N2 tx length is less than the size of + * the TDR. + */ + static PnvXferBuffer *payload; + + if (payload == NULL) { + payload = pnv_spi_xfer_buffer_new(); + } + /* + * Clear the sequencer FSM error bit - general_SPI_status[3] + * before starting a sequence. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 0); + /* + * If the FSM is idle set the sequencer index to 0 + * (new/restarted sequence) + */ + if (GETFIELD(SPI_STS_SEQ_FSM, s->status) == SEQ_STATE_IDLE) { + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + } + /* + * There are only 8 possible operation IDs to iterate through though + * some operations may cause more than one frame to be sequenced. + */ + while (get_seq_index(s) < NUM_SEQ_OPS) { + opcode = s->seq_op[get_seq_index(s)]; + /* Set sequencer state to decode */ + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_DECODE); + /* + * Only the upper nibble of the operation ID is needed to know what + * kind of operation is requested. + */ + masked_opcode = PNV_SPI_MASKED_OPCODE(opcode); + switch (masked_opcode) { + /* + * Increment the operation index in each case instead of just + * once at the end in case an operation like the branch + * operation needs to change the index. + */ + case SEQ_OP_STOP: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* A stop operation in any position stops the sequencer */ + trace_pnv_spi_sequencer_op("STOP", get_seq_index(s)); + + stop = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); + break; + + case SEQ_OP_SELECT_SLAVE: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SELECT_SLAVE", get_seq_index(s)); + /* + * This device currently only supports a single responder + * connection at position 0. De-selecting a responder is fine + * and expected at the end of a sequence but selecting any + * responder other than 0 should cause an error. + */ + s->responder_select = PNV_SPI_OPCODE_LO_NIBBLE(opcode); + if (s->responder_select == 0) { + trace_pnv_spi_shifter_done(); + qemu_set_irq(s->cs_line[0], 1); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + (get_seq_index(s) + 1)); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_DONE); + } else if (s->responder_select != 1) { + qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 " + "not supported, select = 0x%x\n", + s->responder_select); + trace_pnv_spi_sequencer_stop_requested("invalid " + "responder select"); + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + stop = true; + } else { + /* + * Only allow an FSM_START state when a responder is + * selected + */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_START); + trace_pnv_spi_shifter_stating(); + qemu_set_irq(s->cs_line[0], 0); + /* + * A Shift_N2 operation is only valid after a Shift_N1 + * according to the spec. The spec doesn't say if that means + * immediately after or just after at any point. We will track + * the occurrence of a Shift_N1 to enforce this requirement in + * the most generic way possible by assuming that the rule + * applies once a valid responder select has occurred. + */ + s->shift_n1_done = false; + next_sequencer_fsm(s); + } + break; + + case SEQ_OP_SHIFT_N1: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SHIFT_N1", get_seq_index(s)); + /* + * Only allow a shift_n1 when the state is not IDLE or DONE. + * In either of those two cases the sequencer is not in a proper + * state to perform shift operations because the sequencer has: + * - processed a responder deselect (DONE) + * - processed a stop opcode (IDLE) + * - encountered an error (IDLE) + */ + if ((GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_IDLE) || + (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_DONE)) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N1 not allowed in " + "shifter state = 0x%llx", GETFIELD( + SPI_STS_SHIFTER_FSM, s->status)); + /* + * Set sequencer FSM error bit 3 (general_SPI_status[3]) + * in status reg. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); + trace_pnv_spi_sequencer_stop_requested("invalid shifter state"); + stop = true; + } else { + /* + * Look for the special case where there is a shift_n1 set for + * transmit and it is followed by a shift_n2 set for transmit + * AND the combined transmit length of the two operations is + * less than or equal to the size of the TDR register. In this + * case we want to use both this current shift_n1 opcode and the + * following shift_n2 opcode to assemble the frame for + * transmission to the responder without requiring a refill of + * the TDR between the two operations. + */ + if (PNV_SPI_MASKED_OPCODE(s->seq_op[get_seq_index(s) + 1]) + == SEQ_OP_SHIFT_N2) { + send_n1_alone = false; + } + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N1); + stop = operation_shiftn1(s, opcode, &payload, send_n1_alone); + if (stop) { + /* + * The operation code says to stop, this can occur if: + * (1) RDR is full and the N1 shift is set for receive + * (2) TDR was empty at the time of the N1 shift so we need + * to wait for data. + * (3) Neither 1 nor 2 are occurring and we aren't sending + * N1 alone and N2 counter reload is set (bit 0 of the N2 + * counter reload field). In this case TDR_underrun will + * will be set and the Payload has been loaded so it is + * ok to advance the sequencer. + */ + if (GETFIELD(SPI_STS_TDR_UNDERRUN, s->status)) { + s->shift_n1_done = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N2); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + (get_seq_index(s) + 1)); + } else { + /* + * This is case (1) or (2) so the sequencer needs to + * wait and NOT go to the next sequence yet. + */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_WAIT); + } + } else { + /* Ok to move on to the next index */ + s->shift_n1_done = true; + next_sequencer_fsm(s); + } + } + break; + + case SEQ_OP_SHIFT_N2: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("SHIFT_N2", get_seq_index(s)); + if (!s->shift_n1_done) { + qemu_log_mask(LOG_GUEST_ERROR, "Shift_N2 is not allowed if a " + "Shift_N1 is not done, shifter state = 0x%llx", + GETFIELD(SPI_STS_SHIFTER_FSM, s->status)); + /* + * In case the sequencer actually stops if an N2 shift is + * requested before any N1 shift is done. Set sequencer FSM + * error bit 3 (general_SPI_status[3]) in status reg. + */ + s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1); + trace_pnv_spi_sequencer_stop_requested("shift_n2 " + "w/no shift_n1 done"); + stop = true; + } else { + /* Ok to do a Shift_N2 */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_SHIFT_N2); + stop = operation_shiftn2(s, opcode, &payload); + /* + * If the operation code says to stop set the shifter state to + * wait and stop + */ + if (stop) { + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, + FSM_WAIT); + } else { + /* Ok to move on to the next index */ + next_sequencer_fsm(s); + } + } + break; + + case SEQ_OP_BRANCH_IFNEQ_RDR: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_RDR", get_seq_index(s)); + /* + * The memory mapping register RDR match value is compared against + * the 16 rightmost bytes of the RDR (potentially with masking). + * Since this comparison is performed against the contents of the + * RDR then a receive must have previously occurred otherwise + * there is no data to compare and the operation cannot be + * completed and will stop the sequencer until RDR full is set to + * 1. + */ + if (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1) { + bool rdr_matched = false; + rdr_matched = does_rdr_match(s); + if (rdr_matched) { + trace_pnv_spi_RDR_match("success"); + /* A match occurred, increment the sequencer index. */ + next_sequencer_fsm(s); + } else { + trace_pnv_spi_RDR_match("failed"); + /* + * Branch the sequencer to the index coded into the op + * code. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + } + /* + * Regardless of where the branch ended up we want the + * sequencer to continue shifting so we have to clear + * RDR_full. + */ + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + } else { + trace_pnv_spi_sequencer_stop_requested("RDR not" + "full for 0x6x opcode"); + stop = true; + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT); + } + break; + + case SEQ_OP_TRANSFER_TDR: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\n"); + next_sequencer_fsm(s); + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_1: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_1", get_seq_index(s)); + /* + * The spec says the loop should execute count compare + 1 times. + * However we learned from engineering that we really only loop + * count_compare times, count compare = 0 makes this op code a + * no-op + */ + if (s->loop_counter_1 != + GETFIELD(SPI_CTR_CFG_CMP1, s->regs[SPI_CTR_CFG_REG])) { + /* + * Next index is the lower nibble of the branch operation ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, + PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + s->loop_counter_1++; + } else { + /* Continue to next index if loop counter is reached */ + next_sequencer_fsm(s); + } + break; + + case SEQ_OP_BRANCH_IFNEQ_INC_2: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", get_seq_index(s)); + uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2, + s->regs[SPI_CTR_CFG_REG]); + /* + * The spec says the loop should execute count compare + 1 times. + * However we learned from engineering that we really only loop + * count_compare times, count compare = 0 makes this op code a + * no-op + */ + if (s->loop_counter_2 != condition2) { + /* + * Next index is the lower nibble of the branch operation ID, + * mask off all but the first three bits so we don't try to + * access beyond the sequencer_operation_reg boundary. + */ + s->status = SETFIELD(SPI_STS_SEQ_INDEX, + s->status, PNV_SPI_OPCODE_LO_NIBBLE(opcode)); + s->loop_counter_2++; + } else { + /* Continue to next index if loop counter is reached */ + next_sequencer_fsm(s); + } + break; + + default: + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE); + /* Ignore unsupported operations. */ + next_sequencer_fsm(s); + break; + } /* end of switch */ + /* + * If we used all 8 opcodes without seeing a 00 - STOP in the sequence + * we need to go ahead and end things as if there was a STOP at the + * end. + */ + if (get_seq_index(s) == NUM_SEQ_OPS) { + /* All 8 opcodes completed, sequencer idling */ + s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE); + s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0); + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE); + break; + } + /* Break the loop if a stop was requested */ + if (stop) { + break; + } + } /* end of while */ + return; +} /* end of operation_sequencer() */ + +/* + * The SPIC engine and its internal sequencer can be interrupted and reset by + * a hardware signal, the sbe_spicst_hard_reset bits from Pervasive + * Miscellaneous Register of sbe_register_bo device. + * Reset immediately aborts any SPI transaction in progress and returns the + * sequencer and state machines to idle state. + * The configuration register values are not changed. The status register is + * not reset. The engine registers are not reset. + * The SPIC engine reset does not have any affect on the attached devices. + * Reset handling of any attached devices is beyond the scope of the engine. + */ +static void do_reset(DeviceState *dev) +{ + PnvSpi *s = PNV_SPI(dev); + DeviceState *ssi_dev; + + trace_pnv_spi_reset(); + + /* Connect cs irq */ + ssi_dev = ssi_get_cs(s->ssi_bus, 0); + if (ssi_dev) { + qemu_irq cs_line = qdev_get_gpio_in_named(ssi_dev, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(s), "cs", 0, cs_line); + } + + /* Reset all N1 and N2 counters, and other constants */ + s->N2_bits = 0; + s->N2_bytes = 0; + s->N2_tx = 0; + s->N2_rx = 0; + s->N1_bits = 0; + s->N1_bytes = 0; + s->N1_tx = 0; + s->N1_rx = 0; + s->loop_counter_1 = 0; + s->loop_counter_2 = 0; + /* Disconnected from responder */ + qemu_set_irq(s->cs_line[0], 1); +} + +static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvSpi *s = PNV_SPI(opaque); + uint32_t reg = addr >> 3; + uint64_t val = ~0ull; + + switch (reg) { + case ERROR_REG: + case SPI_CTR_CFG_REG: + case CONFIG_REG1: + case SPI_CLK_CFG_REG: + case SPI_MM_REG: + case SPI_XMIT_DATA_REG: + val = s->regs[reg]; + break; + case SPI_RCV_DATA_REG: + val = s->regs[reg]; + trace_pnv_spi_read_RDR(val); + s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0); + if (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_WAIT) { + trace_pnv_spi_start_sequencer(); + operation_sequencer(s); + } + break; + case SPI_SEQ_OP_REG: + val = 0; + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { + val = (val << 8) | s->seq_op[i]; + } + break; + case SPI_STS_REG: + val = s->status; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " + "read at 0x%" PRIx32 "\n", reg); + } + + trace_pnv_spi_read(addr, val); + return val; +} + +static void pnv_spi_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvSpi *s = PNV_SPI(opaque); + uint32_t reg = addr >> 3; + + trace_pnv_spi_write(addr, val); + + switch (reg) { + case ERROR_REG: + case SPI_CTR_CFG_REG: + case CONFIG_REG1: + case SPI_MM_REG: + case SPI_RCV_DATA_REG: + s->regs[reg] = val; + break; + case SPI_CLK_CFG_REG: + /* + * To reset the SPI controller write the sequence 0x5 0xA to + * reset_control field + */ + if ((GETFIELD(SPI_CLK_CFG_RST_CTRL, s->regs[SPI_CLK_CFG_REG]) == 0x5) + && (GETFIELD(SPI_CLK_CFG_RST_CTRL, val) == 0xA)) { + /* SPI controller reset sequence completed, resetting */ + s->regs[reg] = SPI_CLK_CFG_HARD_RST; + } else { + s->regs[reg] = val; + } + break; + case SPI_XMIT_DATA_REG: + /* + * Writing to the transmit data register causes the transmit data + * register full status bit in the status register to be set. Writing + * when the transmit data register full status bit is already set + * causes a "Resource Not Available" condition. This is not possible + * in the model since writes to this register are not asynchronous to + * the operation sequence like it would be in hardware. + */ + s->regs[reg] = val; + trace_pnv_spi_write_TDR(val); + s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1); + s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0); + trace_pnv_spi_start_sequencer(); + operation_sequencer(s); + break; + case SPI_SEQ_OP_REG: + for (int i = 0; i < PNV_SPI_REG_SIZE; i++) { + s->seq_op[i] = (val >> (56 - i * 8)) & 0xFF; + } + break; + case SPI_STS_REG: + /* other fields are ignore_write */ + s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, + GETFIELD(SPI_STS_RDR, val)); + s->status = SETFIELD(SPI_STS_TDR_OVERRUN, s->status, + GETFIELD(SPI_STS_TDR, val)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " + "write at 0x%" PRIx32 "\n", reg); + } + return; +} + +static const MemoryRegionOps pnv_spi_xscom_ops = { + .read = pnv_spi_xscom_read, + .write = pnv_spi_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 Property pnv_spi_properties[] = { + DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0), + DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_spi_realize(DeviceState *dev, Error **errp) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name = g_strdup_printf(TYPE_PNV_SPI_BUS ".%d", + s->spic_num); + s->ssi_bus = ssi_create_bus(dev, name); + s->cs_line = g_new0(qemu_irq, 1); + qdev_init_gpio_out_named(DEVICE(s), s->cs_line, "cs", 1); + + /* spi scoms */ + pnv_xscom_region_init(&s->xscom_spic_regs, OBJECT(s), &pnv_spi_xscom_ops, + s, "xscom-spi", PNV10_XSCOM_PIB_SPIC_SIZE); +} + +static int pnv_spi_dt_xscom(PnvXScomInterface *dev, void *fdt, + int offset) +{ + PnvSpi *s = PNV_SPI(dev); + g_autofree char *name; + int s_offset; + const char compat[] = "ibm,power10-spi"; + uint32_t spic_pcba = PNV10_XSCOM_PIB_SPIC_BASE + + s->spic_num * PNV10_XSCOM_PIB_SPIC_SIZE; + uint32_t reg[] = { + cpu_to_be32(spic_pcba), + cpu_to_be32(PNV10_XSCOM_PIB_SPIC_SIZE) + }; + name = g_strdup_printf("pnv_spi@%x", spic_pcba); + s_offset = fdt_add_subnode(fdt, offset, name); + _FDT(s_offset); + + _FDT(fdt_setprop(fdt, s_offset, "reg", reg, sizeof(reg))); + _FDT(fdt_setprop(fdt, s_offset, "compatible", compat, sizeof(compat))); + _FDT((fdt_setprop_cell(fdt, s_offset, "spic_num#", s->spic_num))); + return 0; +} + +static void pnv_spi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xscomc->dt_xscom = pnv_spi_dt_xscom; + + dc->desc = "PowerNV SPI"; + dc->realize = pnv_spi_realize; + device_class_set_legacy_reset(dc, do_reset); + device_class_set_props(dc, pnv_spi_properties); +} + +static const TypeInfo pnv_spi_info = { + .name = TYPE_PNV_SPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvSpi), + .class_init = pnv_spi_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_spi_register_types(void) +{ + type_register_static(&pnv_spi_info); +} + +type_init(pnv_spi_register_types); diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c index 03540cf5ca..08a107792b 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; @@ -338,7 +338,7 @@ static void sifive_spi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, sifive_spi_properties); - dc->reset = sifive_spi_reset; + device_class_set_legacy_reset(dc, sifive_spi_reset); dc->realize = sifive_spi_realize; } diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index d54a109bee..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) @@ -71,6 +108,11 @@ static void ssi_peripheral_realize(DeviceState *dev, Error **errp) 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 = { @@ -129,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..ea9b74a409 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), @@ -206,7 +206,7 @@ static void stm32f2xx_spi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f2xx_spi_reset; + device_class_set_legacy_reset(dc, stm32f2xx_spi_reset); dc->vmsd = &vmstate_stm32f2xx_spi; } diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index c707d4aaba..2f36cf96b8 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -6,7 +6,7 @@ aspeed_smc_do_snoop(int cs, int index, int dummies, int data) "CS%d index:0x%x d aspeed_smc_flash_write(int cs, uint64_t addr, uint32_t size, uint64_t data, int mode) "CS%d @0x%" PRIx64 " size %u: 0x%" PRIx64" mode:%d" aspeed_smc_read(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x" -aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x" +aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint64_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%" PRIx64 " size:0x%08x" aspeed_smc_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect" @@ -21,9 +21,45 @@ 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" ibex_spi_host_transfer(uint32_t tx_data, uint32_t rx_data) "tx_data: 0x%" PRIx32 " rx_data: @0x%" PRIx32 ibex_spi_host_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64 ibex_spi_host_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size %u:" + +#pnv_spi.c +pnv_spi_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_spi_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64 +pnv_spi_read_RDR(uint64_t val) "data extracted = 0x%" PRIx64 +pnv_spi_write_TDR(uint64_t val) "being written, data written = 0x%" PRIx64 +pnv_spi_start_sequencer(void) "" +pnv_spi_reset(void) "spic engine sequencer configuration and spi communication" +pnv_spi_sequencer_op(const char* op, uint8_t index) "%s at index = 0x%x" +pnv_spi_shifter_stating(void) "pull CS line low" +pnv_spi_shifter_done(void) "pull the CS line high" +pnv_spi_log_Ncounts(uint8_t N1_bits, uint8_t N1_bytes, uint8_t N1_tx, uint8_t N1_rx, uint8_t N2_bits, uint8_t N2_bytes, uint8_t N2_tx, uint8_t N2_rx) "N1_bits = %d, N1_bytes = %d, N1_tx = %d, N1_rx = %d, N2_bits = %d, N2_bytes = %d, N2_tx = %d, N2_rx = %d" +pnv_spi_tx_append(const char* frame, uint8_t byte, uint8_t tdr_index) "%s = 0x%2.2x to payload from TDR at index %d" +pnv_spi_tx_append_FF(const char* frame) "%s to Payload" +pnv_spi_tx_request(const char* frame, uint32_t payload_len) "%s, payload len = %d" +pnv_spi_rx_received(uint32_t payload_len) "payload len = %d" +pnv_spi_rx_read_N1frame(void) "" +pnv_spi_rx_read_N2frame(void) "" +pnv_spi_shift_rx(uint8_t byte, uint32_t index) "byte = 0x%2.2x into RDR from payload index %d" +pnv_spi_sequencer_stop_requested(const char* reason) "due to %s" +pnv_spi_RDR_match(const char* result) "%s" + +# allwinner_a10_spi.c +allwinner_a10_spi_update_irq(uint32_t level) "IRQ level is %d" +allwinner_a10_spi_flush_txfifo_begin(uint32_t tx, uint32_t rx) "Begin: TX Fifo Size = %d, RX Fifo Size = %d" +allwinner_a10_spi_flush_txfifo_end(uint32_t tx, uint32_t rx) "End: TX Fifo Size = %d, RX Fifo Size = %d" +allwinner_a10_spi_burst_length(uint32_t len) "Burst length = %d" +allwinner_a10_spi_tx(uint8_t byte) "write 0x%02x" +allwinner_a10_spi_rx(uint8_t byte) "read 0x%02x" +allwinner_a10_spi_read(const char* regname, uint32_t value) "reg[%s] => 0x%08x" +allwinner_a10_spi_write(const char* regname, uint32_t value) "reg[%s] <= 0x%08x" diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index b2819a7ff0..7f1e1808c5 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), @@ -370,7 +371,7 @@ static void xilinx_spi_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = xilinx_spi_realize; - dc->reset = xlx_spi_reset; + device_class_set_legacy_reset(dc, xlx_spi_reset); device_class_set_props(dc, xilinx_spi_properties); dc->vmsd = &vmstate_xilinx_spi; } diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 1e9dba2039..aeb462c3ce 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; @@ -620,7 +620,9 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) } else if (s->snoop_state == SNOOP_STRIPING || s->snoop_state == SNOOP_NONE) { for (i = 0; i < num_effective_busses(s); ++i) { - tx_rx[i] = fifo8_pop(&s->tx_fifo); + if (!fifo8_is_empty(&s->tx_fifo)) { + tx_rx[i] = fifo8_pop(&s->tx_fifo); + } } stripe8(tx_rx, num_effective_busses(s), false); } else if (s->snoop_state >= SNOOP_ADDR) { @@ -768,7 +770,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 +889,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 +918,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 +973,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 +1303,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 +1371,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 +1397,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 +1409,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 +1439,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; } @@ -1445,11 +1450,12 @@ static void xilinx_spips_class_init(ObjectClass *klass, void *data) XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); dc->realize = xilinx_spips_realize; - dc->reset = xilinx_spips_reset; + device_class_set_legacy_reset(dc, xilinx_spips_reset); device_class_set_props(dc, xilinx_spips_properties); 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; } @@ -1460,10 +1466,11 @@ static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); dc->realize = xlnx_zynqmp_qspips_realize; - dc->reset = xlnx_zynqmp_qspips_reset; + device_class_set_legacy_reset(dc, xlnx_zynqmp_qspips_reset); 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..ecc1903b8e 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), @@ -1830,7 +1836,7 @@ static void xlnx_versal_ospi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xlnx_versal_ospi_reset; + device_class_set_legacy_reset(dc, xlnx_versal_ospi_reset); dc->realize = xlnx_versal_ospi_realize; dc->vmsd = &vmstate_xlnx_versal_ospi; device_class_set_props(dc, xlnx_versal_ospi_properties); diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 010be7ed1f..c96fd5d97a 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -17,14 +17,13 @@ config I8254 bool depends on ISA_BUS -config ALTERA_TIMER - bool - select PTIMER - config ALLWINNER_A10_PIT bool select PTIMER +config PXA2XX_TIMER + bool + config SIFIVE_PWM bool diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 5e959b6d09..8091ec18c7 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -32,6 +32,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "hw/core/cpu.h" +#include "sysemu/qtest.h" #ifndef A9_GTIMER_ERR_DEBUG #define A9_GTIMER_ERR_DEBUG 0 @@ -48,6 +49,10 @@ static inline int a9_gtimer_get_current_cpu(A9GTimerState *s) { + if (qtest_enabled()) { + return 0; + } + if (current_cpu->cpu_index >= s->num_cpu) { hw_error("a9gtimer: num-cpu %d but this cpu is %d!\n", s->num_cpu, current_cpu->cpu_index); @@ -328,7 +333,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 +347,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 +357,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 +367,7 @@ static const VMStateDescription vmstate_a9_gtimer = { A9GTimerPerCPU), VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_a9_gtimer_control, NULL } @@ -379,7 +384,7 @@ static void a9_gtimer_class_init(ObjectClass *klass, void *data) dc->realize = a9_gtimer_realize; dc->vmsd = &vmstate_a9_gtimer; - dc->reset = a9_gtimer_reset; + device_class_set_legacy_reset(dc, a9_gtimer_reset); device_class_set_props(dc, a9_gtimer_properties); } diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index 971f78462a..d488e9782b 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), @@ -293,7 +293,7 @@ static void a10_pit_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = a10_pit_reset; + device_class_set_legacy_reset(dc, a10_pit_reset); device_class_set_props(dc, a10_pit_properties); dc->desc = "allwinner a10 timer"; dc->vmsd = &vmstate_a10_pit; diff --git a/hw/timer/altera_timer.c b/hw/timer/altera_timer.c deleted file mode 100644 index 0f1f54206a..0000000000 --- a/hw/timer/altera_timer.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * QEMU model of the Altera timer. - * - * Copyright (c) 2012 Chris Wulff - * - * 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/module.h" -#include "qapi/error.h" - -#include "hw/sysbus.h" -#include "hw/irq.h" -#include "hw/ptimer.h" -#include "hw/qdev-properties.h" -#include "qom/object.h" - -#define R_STATUS 0 -#define R_CONTROL 1 -#define R_PERIODL 2 -#define R_PERIODH 3 -#define R_SNAPL 4 -#define R_SNAPH 5 -#define R_MAX 6 - -#define STATUS_TO 0x0001 -#define STATUS_RUN 0x0002 - -#define CONTROL_ITO 0x0001 -#define CONTROL_CONT 0x0002 -#define CONTROL_START 0x0004 -#define CONTROL_STOP 0x0008 - -#define TYPE_ALTERA_TIMER "ALTR.timer" -OBJECT_DECLARE_SIMPLE_TYPE(AlteraTimer, ALTERA_TIMER) - -struct AlteraTimer { - SysBusDevice busdev; - MemoryRegion mmio; - qemu_irq irq; - uint32_t freq_hz; - ptimer_state *ptimer; - uint32_t regs[R_MAX]; -}; - -static int timer_irq_state(AlteraTimer *t) -{ - bool irq = (t->regs[R_STATUS] & STATUS_TO) && - (t->regs[R_CONTROL] & CONTROL_ITO); - return irq; -} - -static uint64_t timer_read(void *opaque, hwaddr addr, - unsigned int size) -{ - AlteraTimer *t = opaque; - uint64_t r = 0; - - addr >>= 2; - - switch (addr) { - case R_CONTROL: - r = t->regs[R_CONTROL] & (CONTROL_ITO | CONTROL_CONT); - break; - - default: - if (addr < ARRAY_SIZE(t->regs)) { - r = t->regs[addr]; - } - break; - } - - return r; -} - -static void timer_write(void *opaque, hwaddr addr, - uint64_t value, unsigned int size) -{ - AlteraTimer *t = opaque; - uint64_t tvalue; - uint32_t count = 0; - int irqState = timer_irq_state(t); - - addr >>= 2; - - switch (addr) { - case R_STATUS: - /* The timeout bit is cleared by writing the status register. */ - t->regs[R_STATUS] &= ~STATUS_TO; - break; - - case R_CONTROL: - ptimer_transaction_begin(t->ptimer); - t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT); - if ((value & CONTROL_START) && - !(t->regs[R_STATUS] & STATUS_RUN)) { - ptimer_run(t->ptimer, 1); - t->regs[R_STATUS] |= STATUS_RUN; - } - if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) { - ptimer_stop(t->ptimer); - t->regs[R_STATUS] &= ~STATUS_RUN; - } - ptimer_transaction_commit(t->ptimer); - break; - - case R_PERIODL: - case R_PERIODH: - ptimer_transaction_begin(t->ptimer); - t->regs[addr] = value & 0xFFFF; - if (t->regs[R_STATUS] & STATUS_RUN) { - ptimer_stop(t->ptimer); - t->regs[R_STATUS] &= ~STATUS_RUN; - } - tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; - ptimer_set_limit(t->ptimer, tvalue + 1, 1); - ptimer_transaction_commit(t->ptimer); - break; - - case R_SNAPL: - case R_SNAPH: - count = ptimer_get_count(t->ptimer); - t->regs[R_SNAPL] = count & 0xFFFF; - t->regs[R_SNAPH] = count >> 16; - break; - - default: - break; - } - - if (irqState != timer_irq_state(t)) { - qemu_set_irq(t->irq, timer_irq_state(t)); - } -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 4 - } -}; - -static void timer_hit(void *opaque) -{ - AlteraTimer *t = opaque; - const uint64_t tvalue = (t->regs[R_PERIODH] << 16) | t->regs[R_PERIODL]; - - t->regs[R_STATUS] |= STATUS_TO; - - ptimer_set_limit(t->ptimer, tvalue + 1, 1); - - if (!(t->regs[R_CONTROL] & CONTROL_CONT)) { - t->regs[R_STATUS] &= ~STATUS_RUN; - ptimer_set_count(t->ptimer, tvalue); - } else { - ptimer_run(t->ptimer, 1); - } - - qemu_set_irq(t->irq, timer_irq_state(t)); -} - -static void altera_timer_realize(DeviceState *dev, Error **errp) -{ - AlteraTimer *t = ALTERA_TIMER(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - if (t->freq_hz == 0) { - error_setg(errp, "\"clock-frequency\" property must be provided."); - return; - } - - t->ptimer = ptimer_init(timer_hit, t, PTIMER_POLICY_LEGACY); - ptimer_transaction_begin(t->ptimer); - ptimer_set_freq(t->ptimer, t->freq_hz); - ptimer_transaction_commit(t->ptimer); - - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, - TYPE_ALTERA_TIMER, R_MAX * sizeof(uint32_t)); - sysbus_init_mmio(sbd, &t->mmio); -} - -static void altera_timer_init(Object *obj) -{ - AlteraTimer *t = ALTERA_TIMER(obj); - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - - sysbus_init_irq(sbd, &t->irq); -} - -static void altera_timer_reset(DeviceState *dev) -{ - AlteraTimer *t = ALTERA_TIMER(dev); - - ptimer_transaction_begin(t->ptimer); - ptimer_stop(t->ptimer); - ptimer_set_limit(t->ptimer, 0xffffffff, 1); - ptimer_transaction_commit(t->ptimer); - memset(t->regs, 0, sizeof(t->regs)); -} - -static Property altera_timer_properties[] = { - DEFINE_PROP_UINT32("clock-frequency", AlteraTimer, freq_hz, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void altera_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = altera_timer_realize; - device_class_set_props(dc, altera_timer_properties); - dc->reset = altera_timer_reset; -} - -static const TypeInfo altera_timer_info = { - .name = TYPE_ALTERA_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(AlteraTimer), - .instance_init = altera_timer_init, - .class_init = altera_timer_class_init, -}; - -static void altera_timer_register(void) -{ - type_register_static(&altera_timer_info); -} - -type_init(altera_timer_register) diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index cdfca3000b..defa30b46d 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() @@ -311,7 +311,7 @@ static void arm_mptimer_class_init(ObjectClass *klass, void *data) dc->realize = arm_mptimer_realize; dc->vmsd = &vmstate_arm_mptimer; - dc->reset = arm_mptimer_reset; + device_class_set_legacy_reset(dc, arm_mptimer_reset); device_class_set_props(dc, arm_mptimer_properties); } 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..a07febd1d1 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), @@ -290,7 +290,7 @@ static void systick_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_systick; - dc->reset = systick_reset; + device_class_set_legacy_reset(dc, systick_reset); dc->realize = systick_realize; } diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 9c20b3d6ad..149f7cc5a6 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); @@ -276,7 +276,8 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, old_reload = t->reload; t->reload = calculate_min_ticks(t, value); - /* If the reload value was not previously set, or zero, and + /* + * If the reload value was not previously set, or zero, and * the current value is valid, try to start the timer if it is * enabled. */ @@ -312,7 +313,8 @@ static void aspeed_timer_set_value(AspeedTimerCtrlState *s, int timer, int reg, } } -/* Control register operations are broken out into helpers that can be +/* + * Control register operations are broken out into helpers that can be * explicitly called on aspeed_timer_reset(), but also from * aspeed_timer_ctrl_op(). */ @@ -396,7 +398,8 @@ static void aspeed_timer_set_ctrl(AspeedTimerCtrlState *s, uint32_t reg) AspeedTimer *t; const uint8_t enable_mask = BIT(op_enable); - /* Handle a dependency between the 'enable' and remaining three + /* + * Handle a dependency between the 'enable' and remaining three * configuration bits - i.e. if more than one bit in the control set has * changed, including the 'enable' bit, then we want either disable the * timer and perform configuration, or perform configuration and then @@ -577,12 +580,11 @@ static void aspeed_2600_timer_write(AspeedTimerCtrlState *s, hwaddr offset, switch (offset) { case 0x34: - s->irq_sts &= tv; + s->irq_sts &= ~tv; break; case 0x3C: aspeed_timer_set_ctrl(s, s->ctrl & ~tv); break; - case 0x38: default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", @@ -623,7 +625,8 @@ static void aspeed_timer_reset(DeviceState *dev) for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { AspeedTimer *t = &s->timers[i]; - /* Explicitly call helpers to avoid any conditional behaviour through + /* + * Explicitly call helpers to avoid any conditional behaviour through * aspeed_timer_set_ctrl(). */ aspeed_timer_ctrl_enable(t, false); @@ -645,7 +648,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 +662,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), @@ -682,7 +685,7 @@ static void timer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_timer_realize; - dc->reset = aspeed_timer_reset; + device_class_set_legacy_reset(dc, aspeed_timer_reset); dc->desc = "ASPEED Timer"; dc->vmsd = &vmstate_aspeed_timer_state; device_class_set_props(dc, aspeed_timer_properties); diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c index c48555da52..421920054f 100644 --- a/hw/timer/avr_timer16.c +++ b/hw/timer/avr_timer16.c @@ -600,7 +600,7 @@ static void avr_timer16_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = avr_timer16_reset; + device_class_set_legacy_reset(dc, avr_timer16_reset); dc->realize = avr_timer16_realize; device_class_set_props(dc, avr_timer16_properties); } diff --git a/hw/timer/bcm2835_systmr.c b/hw/timer/bcm2835_systmr.c index 67669a57ff..2f0fee3342 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), @@ -159,7 +159,7 @@ static void bcm2835_systmr_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = bcm2835_systmr_realize; - dc->reset = bcm2835_systmr_reset; + device_class_set_legacy_reset(dc, bcm2835_systmr_reset); dc->vmsd = &bcm2835_systmr_vmstate; } 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..2ecd8dfe3c 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, @@ -540,7 +540,7 @@ static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_dualtimer_realize; dc->vmsd = &cmsdk_apb_dualtimer_vmstate; - dc->reset = cmsdk_apb_dualtimer_reset; + device_class_set_legacy_reset(dc, cmsdk_apb_dualtimer_reset); } static const TypeInfo cmsdk_apb_dualtimer_info = { diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 68aa1a7636..16d0b2170e 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), @@ -267,7 +267,7 @@ static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_timer_realize; dc->vmsd = &cmsdk_apb_timer_vmstate; - dc->reset = cmsdk_apb_timer_reset; + device_class_set_legacy_reset(dc, cmsdk_apb_timer_reset); } static const TypeInfo cmsdk_apb_timer_info = { diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index d5186f4454..00c32978d2 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); } } @@ -165,7 +165,7 @@ static void digic_timer_class_init(ObjectClass *klass, void *class_data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = digic_timer_reset; + device_class_set_legacy_reset(dc, digic_timer_reset); dc->vmsd = &vmstate_digic_timer; } diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c deleted file mode 100644 index ecc2831baf..0000000000 --- a/hw/timer/etraxfs_timer.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * QEMU ETRAX Timers - * - * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. - * - * 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 "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qemu/timer.h" -#include "hw/irq.h" -#include "hw/ptimer.h" -#include "qom/object.h" - -#define D(x) - -#define RW_TMR0_DIV 0x00 -#define R_TMR0_DATA 0x04 -#define RW_TMR0_CTRL 0x08 -#define RW_TMR1_DIV 0x10 -#define R_TMR1_DATA 0x14 -#define RW_TMR1_CTRL 0x18 -#define R_TIME 0x38 -#define RW_WD_CTRL 0x40 -#define R_WD_STAT 0x44 -#define RW_INTR_MASK 0x48 -#define RW_ACK_INTR 0x4c -#define R_INTR 0x50 -#define R_MASKED_INTR 0x54 - -#define TYPE_ETRAX_FS_TIMER "etraxfs-timer" -typedef struct ETRAXTimerState ETRAXTimerState; -DECLARE_INSTANCE_CHECKER(ETRAXTimerState, ETRAX_TIMER, - TYPE_ETRAX_FS_TIMER) - -struct ETRAXTimerState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - qemu_irq irq; - qemu_irq nmi; - - ptimer_state *ptimer_t0; - ptimer_state *ptimer_t1; - ptimer_state *ptimer_wd; - - uint32_t wd_hits; - - /* Control registers. */ - uint32_t rw_tmr0_div; - uint32_t r_tmr0_data; - uint32_t rw_tmr0_ctrl; - - uint32_t rw_tmr1_div; - uint32_t r_tmr1_data; - uint32_t rw_tmr1_ctrl; - - uint32_t rw_wd_ctrl; - - uint32_t rw_intr_mask; - uint32_t rw_ack_intr; - uint32_t r_intr; - uint32_t r_masked_intr; -}; - -static const VMStateDescription vmstate_etraxfs = { - .name = "etraxfs", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_PTIMER(ptimer_t0, ETRAXTimerState), - VMSTATE_PTIMER(ptimer_t1, ETRAXTimerState), - VMSTATE_PTIMER(ptimer_wd, ETRAXTimerState), - - VMSTATE_UINT32(wd_hits, ETRAXTimerState), - - VMSTATE_UINT32(rw_tmr0_div, ETRAXTimerState), - VMSTATE_UINT32(r_tmr0_data, ETRAXTimerState), - VMSTATE_UINT32(rw_tmr0_ctrl, ETRAXTimerState), - - VMSTATE_UINT32(rw_tmr1_div, ETRAXTimerState), - VMSTATE_UINT32(r_tmr1_data, ETRAXTimerState), - VMSTATE_UINT32(rw_tmr1_ctrl, ETRAXTimerState), - - VMSTATE_UINT32(rw_wd_ctrl, ETRAXTimerState), - - VMSTATE_UINT32(rw_intr_mask, ETRAXTimerState), - VMSTATE_UINT32(rw_ack_intr, ETRAXTimerState), - VMSTATE_UINT32(r_intr, ETRAXTimerState), - VMSTATE_UINT32(r_masked_intr, ETRAXTimerState), - - VMSTATE_END_OF_LIST() - } -}; - -static uint64_t -timer_read(void *opaque, hwaddr addr, unsigned int size) -{ - ETRAXTimerState *t = opaque; - uint32_t r = 0; - - switch (addr) { - case R_TMR0_DATA: - r = ptimer_get_count(t->ptimer_t0); - break; - case R_TMR1_DATA: - r = ptimer_get_count(t->ptimer_t1); - break; - case R_TIME: - r = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 10; - break; - case RW_INTR_MASK: - r = t->rw_intr_mask; - break; - case R_MASKED_INTR: - r = t->r_intr & t->rw_intr_mask; - break; - default: - D(printf ("%s %x\n", __func__, addr)); - break; - } - return r; -} - -static void update_ctrl(ETRAXTimerState *t, int tnum) -{ - unsigned int op; - unsigned int freq; - unsigned int freq_hz; - unsigned int div; - uint32_t ctrl; - - ptimer_state *timer; - - if (tnum == 0) { - ctrl = t->rw_tmr0_ctrl; - div = t->rw_tmr0_div; - timer = t->ptimer_t0; - } else { - ctrl = t->rw_tmr1_ctrl; - div = t->rw_tmr1_div; - timer = t->ptimer_t1; - } - - - op = ctrl & 3; - freq = ctrl >> 2; - freq_hz = 32000000; - - switch (freq) - { - case 0: - case 1: - D(printf ("extern or disabled timer clock?\n")); - break; - case 4: freq_hz = 29493000; break; - case 5: freq_hz = 32000000; break; - case 6: freq_hz = 32768000; break; - case 7: freq_hz = 100000000; break; - default: - abort(); - break; - } - - D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); - ptimer_transaction_begin(timer); - ptimer_set_freq(timer, freq_hz); - ptimer_set_limit(timer, div, 0); - - switch (op) - { - case 0: - /* Load. */ - ptimer_set_limit(timer, div, 1); - break; - case 1: - /* Hold. */ - ptimer_stop(timer); - break; - case 2: - /* Run. */ - ptimer_run(timer, 0); - break; - default: - abort(); - break; - } - ptimer_transaction_commit(timer); -} - -static void timer_update_irq(ETRAXTimerState *t) -{ - t->r_intr &= ~(t->rw_ack_intr); - t->r_masked_intr = t->r_intr & t->rw_intr_mask; - - D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr)); - qemu_set_irq(t->irq, !!t->r_masked_intr); -} - -static void timer0_hit(void *opaque) -{ - ETRAXTimerState *t = opaque; - t->r_intr |= 1; - timer_update_irq(t); -} - -static void timer1_hit(void *opaque) -{ - ETRAXTimerState *t = opaque; - t->r_intr |= 2; - timer_update_irq(t); -} - -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 - a bit friendlier to compensate for our slower execution. */ - ptimer_set_count(t->ptimer_wd, 10); - ptimer_run(t->ptimer_wd, 1); - qemu_irq_raise(t->nmi); - } - else - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - - t->wd_hits++; -} - -static inline void timer_watchdog_update(ETRAXTimerState *t, uint32_t value) -{ - unsigned int wd_en = t->rw_wd_ctrl & (1 << 8); - unsigned int wd_key = t->rw_wd_ctrl >> 9; - unsigned int wd_cnt = t->rw_wd_ctrl & 511; - unsigned int new_key = value >> 9 & ((1 << 7) - 1); - unsigned int new_cmd = (value >> 8) & 1; - - /* If the watchdog is enabled, they written key must match the - complement of the previous. */ - wd_key = ~wd_key & ((1 << 7) - 1); - - if (wd_en && wd_key != new_key) - return; - - D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", - wd_en, new_key, wd_key, new_cmd, wd_cnt)); - - if (t->wd_hits) - qemu_irq_lower(t->nmi); - - t->wd_hits = 0; - - ptimer_transaction_begin(t->ptimer_wd); - ptimer_set_freq(t->ptimer_wd, 760); - if (wd_cnt == 0) - wd_cnt = 256; - ptimer_set_count(t->ptimer_wd, wd_cnt); - if (new_cmd) - ptimer_run(t->ptimer_wd, 1); - else - ptimer_stop(t->ptimer_wd); - - t->rw_wd_ctrl = value; - ptimer_transaction_commit(t->ptimer_wd); -} - -static void -timer_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - ETRAXTimerState *t = opaque; - uint32_t value = val64; - - switch (addr) - { - case RW_TMR0_DIV: - t->rw_tmr0_div = value; - break; - case RW_TMR0_CTRL: - D(printf ("RW_TMR0_CTRL=%x\n", value)); - t->rw_tmr0_ctrl = value; - update_ctrl(t, 0); - break; - case RW_TMR1_DIV: - t->rw_tmr1_div = value; - break; - case RW_TMR1_CTRL: - D(printf ("RW_TMR1_CTRL=%x\n", value)); - t->rw_tmr1_ctrl = value; - update_ctrl(t, 1); - break; - case RW_INTR_MASK: - D(printf ("RW_INTR_MASK=%x\n", value)); - t->rw_intr_mask = value; - timer_update_irq(t); - break; - case RW_WD_CTRL: - timer_watchdog_update(t, value); - break; - case RW_ACK_INTR: - t->rw_ack_intr = value; - timer_update_irq(t); - t->rw_ack_intr = 0; - break; - default: - printf ("%s " TARGET_FMT_plx " %x\n", - __func__, addr, value); - break; - } -} - -static const MemoryRegionOps timer_ops = { - .read = timer_read, - .write = timer_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void etraxfs_timer_reset_enter(Object *obj, ResetType type) -{ - ETRAXTimerState *t = ETRAX_TIMER(obj); - - ptimer_transaction_begin(t->ptimer_t0); - ptimer_stop(t->ptimer_t0); - ptimer_transaction_commit(t->ptimer_t0); - ptimer_transaction_begin(t->ptimer_t1); - ptimer_stop(t->ptimer_t1); - ptimer_transaction_commit(t->ptimer_t1); - ptimer_transaction_begin(t->ptimer_wd); - ptimer_stop(t->ptimer_wd); - ptimer_transaction_commit(t->ptimer_wd); - t->rw_wd_ctrl = 0; - t->r_intr = 0; - t->rw_intr_mask = 0; -} - -static void etraxfs_timer_reset_hold(Object *obj) -{ - ETRAXTimerState *t = ETRAX_TIMER(obj); - - qemu_irq_lower(t->irq); -} - -static void etraxfs_timer_realize(DeviceState *dev, Error **errp) -{ - ETRAXTimerState *t = ETRAX_TIMER(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - t->ptimer_t0 = ptimer_init(timer0_hit, t, PTIMER_POLICY_LEGACY); - t->ptimer_t1 = ptimer_init(timer1_hit, t, PTIMER_POLICY_LEGACY); - t->ptimer_wd = ptimer_init(watchdog_hit, t, PTIMER_POLICY_LEGACY); - - sysbus_init_irq(sbd, &t->irq); - sysbus_init_irq(sbd, &t->nmi); - - memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, - "etraxfs-timer", 0x5c); - sysbus_init_mmio(sbd, &t->mmio); -} - -static void etraxfs_timer_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ResettableClass *rc = RESETTABLE_CLASS(klass); - - dc->realize = etraxfs_timer_realize; - dc->vmsd = &vmstate_etraxfs; - rc->phases.enter = etraxfs_timer_reset_enter; - rc->phases.hold = etraxfs_timer_reset_hold; -} - -static const TypeInfo etraxfs_timer_info = { - .name = TYPE_ETRAX_FS_TIMER, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(ETRAXTimerState), - .class_init = etraxfs_timer_class_init, -}; - -static void etraxfs_timer_register_types(void) -{ - type_register_static(&etraxfs_timer_info); -} - -type_init(etraxfs_timer_register_types) diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index e175a9f5b9..5c6e139b20 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; } @@ -812,7 +815,7 @@ static uint32_t exynos4210_ltick_cnt_get_cnto(struct tick_timer *s) /* Both are counting */ icnto = remain / s->tcntb; if (icnto) { - tcnto = remain % (icnto * s->tcntb); + tcnto = remain % ((uint64_t)icnto * s->tcntb); } else { tcnto = remain % s->tcntb; } @@ -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; @@ -1547,7 +1550,7 @@ static void exynos4210_mct_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_mct_reset; + device_class_set_legacy_reset(dc, exynos4210_mct_reset); dc->vmsd = &vmstate_exynos4210_mct_state; } diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index 02924a9e5b..703d1d2b4a 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; @@ -424,7 +424,7 @@ static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = exynos4210_pwm_reset; + device_class_set_legacy_reset(dc, exynos4210_pwm_reset); dc->vmsd = &vmstate_exynos4210_pwm_state; } diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 5c4923c1e0..6ef08f25fd 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" @@ -413,7 +415,7 @@ static void grlib_gptimer_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = grlib_gptimer_realize; - dc->reset = grlib_gptimer_reset; + device_class_set_legacy_reset(dc, grlib_gptimer_reset); device_class_set_props(dc, grlib_gptimer_properties); } diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 5f88ffdef8..5399f1b2a3 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 @@ -60,10 +54,12 @@ typedef struct HPETTimer { /* timers */ uint64_t cmp; /* comparator */ uint64_t fsb; /* FSB route */ /* Hidden register state */ + uint64_t cmp64; /* comparator (extended to counter width) */ uint64_t period; /* Last value written to comparator */ uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit * mode. Next pop will be actual timer expiration. */ + uint64_t last; /* last value armed, to avoid timer storms */ } HPETTimer; struct HPETState { @@ -121,11 +117,6 @@ static uint32_t timer_enabled(HPETTimer *t) } static uint32_t hpet_time_after(uint64_t a, uint64_t b) -{ - return ((int32_t)(b - a) < 0); -} - -static uint32_t hpet_time_after64(uint64_t a, uint64_t b) { return ((int64_t)(b - a) < 0); } @@ -162,29 +153,34 @@ static uint64_t hpet_get_ticks(HPETState *s) return ns_to_ticks(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->hpet_offset); } -/* - * calculate diff between comparator value and current ticks - */ -static inline uint64_t hpet_calculate_diff(HPETTimer *t, uint64_t current) +static uint64_t hpet_get_ns(HPETState *s, uint64_t tick) { + return ticks_to_ns(tick) - s->hpet_offset; +} +/* + * calculate next value of the general counter that matches the + * target (either entirely, or the low 32-bit only depending on + * the timer mode). + */ +static uint64_t hpet_calculate_cmp64(HPETTimer *t, uint64_t cur_tick, uint64_t target) +{ if (t->config & HPET_TN_32BIT) { - uint32_t diff, cmp; - - cmp = (uint32_t)t->cmp; - diff = cmp - (uint32_t)current; - diff = (int32_t)diff > 0 ? diff : (uint32_t)1; - return (uint64_t)diff; + uint64_t result = deposit64(cur_tick, 0, 32, target); + if (result < cur_tick) { + result += 0x100000000ULL; + } + return result; } else { - uint64_t diff, cmp; - - cmp = t->cmp; - diff = cmp - current; - diff = (int64_t)diff > 0 ? diff : (uint64_t)1; - return diff; + return target; } } +static uint64_t hpet_next_wrap(uint64_t cur_tick) +{ + return (cur_tick | 0xffffffffU) + 1; +} + static void update_irq(struct HPETTimer *timer, int set) { uint64_t mask; @@ -202,21 +198,31 @@ static void update_irq(struct HPETTimer *timer, int set) } s = timer->state; mask = 1 << timer->tn; - if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { + + if (set && (timer->config & HPET_TN_TYPE_LEVEL)) { + /* + * If HPET_TN_ENABLE bit is 0, "the timer will still operate and + * generate appropriate status bits, but will not cause an interrupt" + */ + s->isr |= mask; + } else { s->isr &= ~mask; + } + + if (set && timer_enabled(timer) && hpet_enabled(s)) { + if (timer_fsb_route(timer)) { + address_space_stl_le(&address_space_memory, timer->fsb >> 32, + timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED, + NULL); + } else if (timer->config & HPET_TN_TYPE_LEVEL) { + qemu_irq_raise(s->irqs[route]); + } else { + qemu_irq_pulse(s->irqs[route]); + } + } else { if (!timer_fsb_route(timer)) { qemu_irq_lower(s->irqs[route]); } - } else if (timer_fsb_route(timer)) { - address_space_stl_le(&address_space_memory, timer->fsb >> 32, - timer->fsb & 0xffffffff, MEMTXATTRS_UNSPECIFIED, - NULL); - } else if (timer->config & HPET_TN_TYPE_LEVEL) { - s->isr |= mask; - qemu_irq_raise(s->irqs[route]); - } else { - s->isr &= ~mask; - qemu_irq_pulse(s->irqs[route]); } } @@ -256,7 +262,13 @@ static bool hpet_validate_num_timers(void *opaque, int version_id) static int hpet_post_load(void *opaque, int version_id) { HPETState *s = opaque; + int i; + for (i = 0; i < s->num_timers; i++) { + HPETTimer *t = &s->timer[i]; + t->cmp64 = hpet_calculate_cmp64(t, s->hpet_counter, t->cmp); + t->last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - NANOSECONDS_PER_SECOND; + } /* Recalculate the offset between the main counter and guest time */ if (!s->hpet_offset_saved) { s->hpet_offset = ticks_to_ns(s->hpet_counter) @@ -295,7 +307,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 +318,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 +328,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 +347,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,21 +357,24 @@ 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) +static void hpet_arm(HPETTimer *t, uint64_t tick) { - 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); + uint64_t ns = hpet_get_ns(t->state, tick); + + /* Clamp period to reasonable min value (1 us) */ + if (timer_is_periodic(t) && ns - t->last < 1000) { + ns = t->last + 1000; } + + t->last = ns; + timer_mod(t->qemu_timer, ns); } /* @@ -368,128 +383,105 @@ static void hpet_arm(HPETTimer *t, uint64_t ticks) static void hpet_timer(void *opaque) { HPETTimer *t = opaque; - uint64_t diff; - uint64_t period = t->period; uint64_t cur_tick = hpet_get_ticks(t->state); if (timer_is_periodic(t) && period != 0) { + while (hpet_time_after(cur_tick, t->cmp64)) { + t->cmp64 += period; + } if (t->config & HPET_TN_32BIT) { - while (hpet_time_after(cur_tick, t->cmp)) { - t->cmp = (uint32_t)(t->cmp + t->period); - } + t->cmp = (uint32_t)t->cmp64; } else { - while (hpet_time_after64(cur_tick, t->cmp)) { - t->cmp += period; - } - } - diff = hpet_calculate_diff(t, cur_tick); - 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); - hpet_arm(t, diff); - t->wrap_flag = 0; + t->cmp = t->cmp64; } + hpet_arm(t, t->cmp64); + } else if (t->wrap_flag) { + t->wrap_flag = 0; + hpet_arm(t, t->cmp64); } update_irq(t, 1); } static void hpet_set_timer(HPETTimer *t) { - uint64_t diff; - uint32_t wrap_diff; /* how many ticks until we wrap? */ uint64_t cur_tick = hpet_get_ticks(t->state); - /* whenever new timer is being set up, make sure wrap_flag is 0 */ t->wrap_flag = 0; - diff = hpet_calculate_diff(t, cur_tick); + t->cmp64 = hpet_calculate_cmp64(t, cur_tick, t->cmp); + if (t->config & HPET_TN_32BIT) { - /* hpet spec says in one-shot 32-bit mode, generate an interrupt when - * counter wraps in addition to an interrupt with comparator match. - */ - if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { - wrap_diff = 0xffffffff - (uint32_t)cur_tick; - if (wrap_diff < (uint32_t)diff) { - diff = wrap_diff; + /* hpet spec says in one-shot 32-bit mode, generate an interrupt when + * counter wraps in addition to an interrupt with comparator match. + */ + if (!timer_is_periodic(t) && t->cmp64 > hpet_next_wrap(cur_tick)) { t->wrap_flag = 1; + hpet_arm(t, hpet_next_wrap(cur_tick)); + return; } } - hpet_arm(t, diff); + hpet_arm(t, t->cmp64); } static void hpet_del_timer(HPETTimer *t) { + HPETState *s = t->state; timer_del(t->qemu_timer); - update_irq(t, 0); + + if (s->isr & (1 << t->tn)) { + /* For level-triggered interrupt, this leaves ISR set but lowers irq. */ + update_irq(t, 1); + } } static uint64_t hpet_ram_read(void *opaque, hwaddr addr, unsigned size) { HPETState *s = opaque; - uint64_t cur_tick, index; + int shift = (addr & 4) * 8; + uint64_t cur_tick; + + trace_hpet_ram_read(addr); - DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); - index = addr; /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { + if (addr >= 0x100 && addr <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; 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; } - switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - return timer->config; - case HPET_TN_CFG + 4: // Interrupt capabilities - return timer->config >> 32; + switch (addr & 0x18) { + case HPET_TN_CFG: // including interrupt capabilities + return timer->config >> shift; case HPET_TN_CMP: // comparator register - return timer->cmp; - case HPET_TN_CMP + 4: - return timer->cmp >> 32; + return timer->cmp >> shift; case HPET_TN_ROUTE: - return timer->fsb; - case HPET_TN_ROUTE + 4: - return timer->fsb >> 32; + return timer->fsb >> shift; default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); + trace_hpet_ram_read_invalid(); break; } } else { - switch (index) { - case HPET_ID: - return s->capability; - case HPET_PERIOD: - return s->capability >> 32; + switch (addr & ~4) { + case HPET_ID: // including HPET_PERIOD + return s->capability >> shift; case HPET_CFG: - return s->config; - case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl\n"); - return 0; + return s->config >> shift; case HPET_COUNTER: if (hpet_enabled(s)) { cur_tick = hpet_get_ticks(s); } else { cur_tick = s->hpet_counter; } - DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick); - return cur_tick; - case HPET_COUNTER + 4: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); - return cur_tick >> 32; + trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); + return cur_tick >> shift; case HPET_STATUS: - return s->isr; + return s->isr >> shift; default: - DPRINTF("qemu: invalid hpet_ram_readl\n"); + trace_hpet_ram_read_invalid(); break; } } @@ -501,114 +493,97 @@ static void hpet_ram_write(void *opaque, hwaddr addr, { int i; HPETState *s = opaque; - uint64_t old_val, new_val, val, index; + int shift = (addr & 4) * 8; + int len = MIN(size * 8, 64 - shift); + uint64_t old_val, new_val, cleared; - DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = 0x%" PRIx64 "\n", - addr, value); - index = addr; - old_val = hpet_ram_read(opaque, addr, 4); - new_val = value; + trace_hpet_ram_write(addr, value); /*address range of all TN regs*/ - if (index >= 0x100 && index <= 0x3ff) { + if (addr >= 0x100 && addr <= 0x3ff) { 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) { + switch (addr & 0x18) { case HPET_TN_CFG: - DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); - if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { + trace_hpet_ram_write_tn_cfg(addr & 4); + old_val = timer->config; + new_val = deposit64(old_val, shift, len, value); + new_val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + if (deactivating_bit(old_val, new_val, HPET_TN_TYPE_LEVEL)) { + /* + * Do this before changing timer->config; otherwise, if + * HPET_TN_FSB is set, update_irq will not lower the qemu_irq. + */ update_irq(timer, 0); } - val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | val; + timer->config = new_val; + if (activating_bit(old_val, new_val, HPET_TN_ENABLE) + && (s->isr & (1 << timer_id))) { + update_irq(timer, 1); + } if (new_val & HPET_TN_32BIT) { timer->cmp = (uint32_t)timer->cmp; timer->period = (uint32_t)timer->period; } - if (activating_bit(old_val, new_val, HPET_TN_ENABLE) && - hpet_enabled(s)) { + if (hpet_enabled(s)) { hpet_set_timer(timer); - } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { - hpet_del_timer(timer); } break; - case HPET_TN_CFG + 4: // Interrupt capabilities - DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); - break; case HPET_TN_CMP: // comparator register - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP\n"); if (timer->config & HPET_TN_32BIT) { - new_val = (uint32_t)new_val; + /* High 32-bits are zero, leave them untouched. */ + if (shift) { + trace_hpet_ram_write_invalid_tn_cmp(); + break; + } + len = 64; + value = (uint32_t) value; } + trace_hpet_ram_write_tn_cmp(addr & 4); if (!timer_is_periodic(timer) || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val; + timer->cmp = deposit64(timer->cmp, shift, len, value); } if (timer_is_periodic(timer)) { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = - (timer->period & 0xffffffff00000000ULL) | new_val; + timer->period = deposit64(timer->period, shift, len, value); } timer->config &= ~HPET_TN_SETVAL; if (hpet_enabled(s)) { hpet_set_timer(timer); } break; - case HPET_TN_CMP + 4: // comparator register high order - DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); - if (!timer_is_periodic(timer) - || (timer->config & HPET_TN_SETVAL)) { - timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; - } else { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = - (timer->period & 0xffffffffULL) | new_val << 32; - } - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled(s)) { - hpet_set_timer(timer); - } - break; case HPET_TN_ROUTE: - timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val; - break; - case HPET_TN_ROUTE + 4: - timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff); + timer->fsb = deposit64(timer->fsb, shift, len, value); break; default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); + trace_hpet_ram_write_invalid(); break; } return; } else { - switch (index) { + switch (addr & ~4) { case HPET_ID: return; case HPET_CFG: - val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = (s->config & 0xffffffff00000000ULL) | val; + old_val = s->config; + new_val = deposit64(old_val, shift, len, value); + new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + s->config = new_val; if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Enable main counter and interrupt generation. */ s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); for (i = 0; i < s->num_timers; i++) { - if ((&s->timer[i])->cmp != ~0ULL) { - hpet_set_timer(&s->timer[i]); + if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) { + update_irq(&s->timer[i], 1); } + hpet_set_timer(&s->timer[i]); } } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Halt main counter and disable interrupt generation. */ @@ -629,37 +604,23 @@ static void hpet_ram_write(void *opaque, hwaddr addr, qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); } break; - case HPET_CFG + 4: - DPRINTF("qemu: invalid HPET_CFG+4 write\n"); - break; case HPET_STATUS: - val = new_val & s->isr; + new_val = value << shift; + cleared = new_val & s->isr; for (i = 0; i < s->num_timers; i++) { - if (val & (1 << i)) { + if (cleared & (1 << i)) { update_irq(&s->timer[i], 0); } } 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); - break; - case HPET_COUNTER + 4: - if (hpet_enabled(s)) { - DPRINTF("qemu: Writing counter while HPET enabled!\n"); - } - 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); + s->hpet_counter = deposit64(s->hpet_counter, shift, len, value); break; default: - DPRINTF("qemu: invalid hpet_ram_writel\n"); + trace_hpet_ram_write_invalid(); break; } } @@ -670,7 +631,11 @@ static const MemoryRegionOps hpet_ram_ops = { .write = hpet_ram_write, .valid = { .min_access_size = 4, - .max_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, }, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -793,7 +758,7 @@ static void hpet_device_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = hpet_realize; - dc->reset = hpet_reset; + device_class_set_legacy_reset(dc, hpet_reset); dc->vmsd = &vmstate_hpet; device_class_set_props(dc, hpet_device_properties); } diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index c8388ea432..058fc61ce9 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); @@ -365,8 +360,7 @@ static void pit_class_initfn(ObjectClass *klass, void *data) k->set_channel_gate = pit_set_channel_gate; 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); + device_class_set_legacy_reset(dc, pit_reset); } 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 d8b8e4e1f6..2bdcff532d 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -252,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), @@ -291,7 +291,7 @@ static void ibex_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = ibex_timer_reset; + device_class_set_legacy_reset(dc, ibex_timer_reset); dc->vmsd = &vmstate_ibex_timer; dc->realize = ibex_timer_realize; device_class_set_props(dc, ibex_timer_properties); diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index ec0fa440d7..f40ab16697 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,144 +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); - } - /* - * Commit the change to s->timer_reload, so it can propagate. Otherwise - * the timer interrupt may not fire properly. The commit must happen - * before calling imx_epit_reload_compare_timer(), which reads - * s->timer_reload internally again. - */ - ptimer_transaction_commit(s->timer_reload); - imx_epit_reload_compare_timer(s); - ptimer_transaction_commit(s->timer_cmp); + 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); } @@ -325,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() @@ -352,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; + device_class_set_legacy_reset(dc, 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..2663a9d9ef 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -18,19 +18,12 @@ #include "migration/vmstate.h" #include "qemu/module.h" #include "qemu/log.h" +#include "trace.h" #ifndef DEBUG_IMX_GPT #define DEBUG_IMX_GPT 0 #endif -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX_GPT) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX_GPT, \ - __func__, ##args); \ - } \ - } while (0) - static const char *imx_gpt_reg_name(uint32_t reg) { switch (reg) { @@ -63,7 +56,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 +108,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*/ @@ -134,7 +138,7 @@ static void imx_gpt_set_freq(IMXGPTState *s) s->freq = imx_ccm_get_clock_frequency(s->ccm, s->clocks[clksrc]) / (1 + s->pr); - DPRINTF("Setting clksrc %d to frequency %d\n", clksrc, s->freq); + trace_imx_gpt_set_freq(clksrc, s->freq); if (s->freq) { ptimer_set_freq(s->timer, s->freq); @@ -306,7 +310,7 @@ static uint64_t imx_gpt_read(void *opaque, hwaddr offset, unsigned size) break; } - DPRINTF("(%s) = 0x%08x\n", imx_gpt_reg_name(offset >> 2), reg_value); + trace_imx_gpt_read(imx_gpt_reg_name(offset >> 2), reg_value); return reg_value; } @@ -373,8 +377,7 @@ static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, IMXGPTState *s = IMX_GPT(opaque); uint32_t oldreg; - DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(offset >> 2), - (uint32_t)value); + trace_imx_gpt_write(imx_gpt_reg_name(offset >> 2), (uint32_t)value); switch (offset >> 2) { case 0: @@ -474,7 +477,7 @@ static void imx_gpt_timeout(void *opaque) { IMXGPTState *s = IMX_GPT(opaque); - DPRINTF("\n"); + trace_imx_gpt_timeout(); s->sr |= s->next_int; s->next_int = 0; @@ -513,7 +516,7 @@ static void imx_gpt_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = imx_gpt_realize; - dc->reset = imx_gpt_reset; + device_class_set_legacy_reset(dc, imx_gpt_reset); dc->vmsd = &vmstate_imx_timer_gpt; dc->desc = "i.MX general timer"; } @@ -539,6 +542,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 +576,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 +593,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..f5f9eed2d0 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -1,40 +1,36 @@ -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_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_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_PXA2XX_TIMER', 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..c55ba02235 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); } @@ -589,7 +592,7 @@ static void npcm7xx_watchdog_timer_expired(void *opaque) } } -static void npcm7xx_timer_hold_reset(Object *obj) +static void npcm7xx_timer_hold_reset(Object *obj, ResetType type) { NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); int i; @@ -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 50c6772383..35b0e62d5b 100644 --- a/hw/timer/nrf51_timer.c +++ b/hw/timer/nrf51_timer.c @@ -361,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), @@ -388,7 +388,7 @@ static void nrf51_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = nrf51_timer_reset; + device_class_set_legacy_reset(dc, nrf51_timer_reset); dc->vmsd = &vmstate_nrf51_timer; device_class_set_props(dc, nrf51_timer_properties); } diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c deleted file mode 100644 index c407190138..0000000000 --- a/hw/timer/omap_gptimer.c +++ /dev/null @@ -1,514 +0,0 @@ -/* - * TI OMAP2 general purpose timers emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "hw/irq.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" - -/* GP timers */ -struct omap_gp_timer_s { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq wkup; - qemu_irq in; - qemu_irq out; - omap_clk clk; - QEMUTimer *timer; - QEMUTimer *match; - struct omap_target_agent_s *ta; - - int in_val; - int out_val; - int64_t time; - int64_t rate; - int64_t ticks_per_sec; - - int16_t config; - int status; - int it_ena; - int wu_ena; - int enable; - int inout; - int capt2; - int pt; - enum { - gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both - } trigger; - enum { - gpt_capture_none, gpt_capture_rising, - gpt_capture_falling, gpt_capture_both - } capture; - int scpwm; - int ce; - int pre; - int ptv; - int ar; - int st; - int posted; - uint32_t val; - uint32_t load_val; - uint32_t capture_val[2]; - uint32_t match_val; - int capt_num; - - uint16_t writeh; /* LSB */ - uint16_t readh; /* MSB */ -}; - -#define GPT_TCAR_IT (1 << 2) -#define GPT_OVF_IT (1 << 1) -#define GPT_MAT_IT (1 << 0) - -static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) -{ - if (timer->it_ena & it) { - if (!timer->status) - qemu_irq_raise(timer->irq); - - timer->status |= it; - /* Or are the status bits set even when masked? - * i.e. is masking applied before or after the status register? */ - } - - if (timer->wu_ena & it) - qemu_irq_pulse(timer->wkup); -} - -static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) -{ - if (!timer->inout && timer->out_val != level) { - timer->out_val = level; - qemu_set_irq(timer->out, level); - } -} - -static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) -{ - uint64_t distance; - - if (timer->st && timer->rate) { - distance = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->time; - distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); - - if (distance >= 0xffffffff - timer->val) - return 0xffffffff; - else - return timer->val + distance; - } else - return timer->val; -} - -static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) -{ - if (timer->st) { - timer->val = omap_gp_timer_read(timer); - timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } -} - -static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) -{ - int64_t expires, matches; - - if (timer->st && timer->rate) { - expires = muldiv64(0x100000000ll - timer->val, - timer->ticks_per_sec, timer->rate); - timer_mod(timer->timer, timer->time + expires); - - if (timer->ce && timer->match_val >= timer->val) { - matches = muldiv64(timer->ticks_per_sec, - timer->match_val - timer->val, timer->rate); - timer_mod(timer->match, timer->time + matches); - } else - timer_del(timer->match); - } else { - timer_del(timer->timer); - timer_del(timer->match); - omap_gp_timer_out(timer, timer->scpwm); - } -} - -static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) -{ - if (timer->pt) - /* TODO in overflow-and-match mode if the first event to - * occur is the match, don't toggle. */ - omap_gp_timer_out(timer, !timer->out_val); - else - /* TODO inverted pulse on timer->out_val == 1? */ - qemu_irq_pulse(timer->out); -} - -static void omap_gp_timer_tick(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (!timer->ar) { - timer->st = 0; - timer->val = 0; - } else { - timer->val = timer->load_val; - timer->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - - if (timer->trigger == gpt_trigger_overflow || - timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_OVF_IT); - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_match(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_MAT_IT); -} - -static void omap_gp_timer_input(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - int trigger; - - switch (s->capture) { - default: - case gpt_capture_none: - trigger = 0; - break; - case gpt_capture_rising: - trigger = !s->in_val && on; - break; - case gpt_capture_falling: - trigger = s->in_val && !on; - break; - case gpt_capture_both: - trigger = (s->in_val == !on); - break; - } - s->in_val = on; - - if (s->inout && trigger && s->capt_num < 2) { - s->capture_val[s->capt_num] = omap_gp_timer_read(s); - - if (s->capt2 == s->capt_num ++) - omap_gp_timer_intr(s, GPT_TCAR_IT); - } -} - -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; - - omap_gp_timer_sync(timer); - timer->rate = on ? omap_clk_getrate(timer->clk) : 0; - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) -{ - omap_clk_adduser(timer->clk, - qemu_allocate_irq(omap_gp_timer_clk_update, timer, 0)); - timer->rate = omap_clk_getrate(timer->clk); -} - -void omap_gp_timer_reset(struct omap_gp_timer_s *s) -{ - s->config = 0x000; - s->status = 0; - s->it_ena = 0; - s->wu_ena = 0; - s->inout = 0; - s->capt2 = 0; - s->capt_num = 0; - s->pt = 0; - s->trigger = gpt_trigger_none; - s->capture = gpt_capture_none; - s->scpwm = 0; - s->ce = 0; - s->pre = 0; - s->ptv = 0; - s->ar = 0; - s->st = 0; - s->posted = 1; - s->val = 0x00000000; - s->load_val = 0x00000000; - s->capture_val[0] = 0x00000000; - s->capture_val[1] = 0x00000000; - s->match_val = 0x00000000; - omap_gp_timer_update(s); -} - -static uint32_t omap_gp_timer_readw(void *opaque, hwaddr addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - return 0x21; - - case 0x10: /* TIOCP_CFG */ - return s->config; - - case 0x14: /* TISTAT */ - /* ??? When's this bit reset? */ - return 1; /* RESETDONE */ - - case 0x18: /* TISR */ - return s->status; - - case 0x1c: /* TIER */ - return s->it_ena; - - case 0x20: /* TWER */ - return s->wu_ena; - - case 0x24: /* TCLR */ - return (s->inout << 14) | - (s->capt2 << 13) | - (s->pt << 12) | - (s->trigger << 10) | - (s->capture << 8) | - (s->scpwm << 7) | - (s->ce << 6) | - (s->pre << 5) | - (s->ptv << 2) | - (s->ar << 1) | - (s->st << 0); - - case 0x28: /* TCRR */ - return omap_gp_timer_read(s); - - case 0x2c: /* TLDR */ - return s->load_val; - - case 0x30: /* TTGR */ - return 0xffffffff; - - case 0x34: /* TWPS */ - return 0x00000000; /* No posted writes pending. */ - - case 0x38: /* TMAR */ - return s->match_val; - - case 0x3c: /* TCAR1 */ - return s->capture_val[0]; - - case 0x40: /* TSICR */ - return s->posted << 2; - - case 0x44: /* TCAR2 */ - return s->capture_val[1]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_gp_timer_readh(void *opaque, hwaddr addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_gp_timer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -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; - - switch (addr) { - case 0x00: /* TIDR */ - case 0x14: /* TISTAT */ - case 0x34: /* TWPS */ - case 0x3c: /* TCAR1 */ - case 0x44: /* TCAR2 */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* TIOCP_CFG */ - s->config = value & 0x33d; - if (((value >> 3) & 3) == 3) /* IDLEMODE */ - fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", - __func__); - if (value & 2) /* SOFTRESET */ - omap_gp_timer_reset(s); - break; - - case 0x18: /* TISR */ - if (value & GPT_TCAR_IT) - s->capt_num = 0; - if (s->status && !(s->status &= ~value)) - qemu_irq_lower(s->irq); - break; - - case 0x1c: /* TIER */ - s->it_ena = value & 7; - break; - - case 0x20: /* TWER */ - s->wu_ena = value & 7; - break; - - case 0x24: /* TCLR */ - omap_gp_timer_sync(s); - s->inout = (value >> 14) & 1; - s->capt2 = (value >> 13) & 1; - s->pt = (value >> 12) & 1; - s->trigger = (value >> 10) & 3; - if (s->capture == gpt_capture_none && - ((value >> 8) & 3) != gpt_capture_none) - s->capt_num = 0; - s->capture = (value >> 8) & 3; - s->scpwm = (value >> 7) & 1; - s->ce = (value >> 6) & 1; - s->pre = (value >> 5) & 1; - s->ptv = (value >> 2) & 7; - s->ar = (value >> 1) & 1; - s->st = (value >> 0) & 1; - if (s->inout && s->trigger != gpt_trigger_none) - fprintf(stderr, "%s: GP timer pin must be an output " - "for this trigger mode\n", __func__); - if (!s->inout && s->capture != gpt_capture_none) - fprintf(stderr, "%s: GP timer pin must be an input " - "for this capture mode\n", __func__); - if (s->trigger == gpt_trigger_none) - omap_gp_timer_out(s, s->scpwm); - /* TODO: make sure this doesn't overflow 32-bits */ - s->ticks_per_sec = NANOSECONDS_PER_SECOND << (s->pre ? s->ptv + 1 : 0); - omap_gp_timer_update(s); - break; - - case 0x28: /* TCRR */ - s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->val = value; - omap_gp_timer_update(s); - break; - - case 0x2c: /* TLDR */ - s->load_val = value; - break; - - case 0x30: /* TTGR */ - s->time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - s->val = s->load_val; - omap_gp_timer_update(s); - break; - - case 0x38: /* TMAR */ - omap_gp_timer_sync(s); - s->match_val = value; - omap_gp_timer_update(s); - break; - - case 0x40: /* TSICR */ - s->posted = (value >> 2) & 1; - if (value & 2) /* How much exactly are we supposed to reset? */ - omap_gp_timer_reset(s); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -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; - - if (addr & 2) - omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); - else - s->writeh = (uint16_t) value; -} - -static uint64_t omap_gp_timer_readfn(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return omap_badwidth_read32(opaque, addr); - case 2: - return omap_gp_timer_readh(opaque, addr); - case 4: - return omap_gp_timer_readw(opaque, addr); - default: - g_assert_not_reached(); - } -} - -static void omap_gp_timer_writefn(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - switch (size) { - case 1: - omap_badwidth_write32(opaque, addr, value); - break; - case 2: - omap_gp_timer_writeh(opaque, addr, value); - break; - case 4: - omap_gp_timer_write(opaque, addr, value); - break; - default: - g_assert_not_reached(); - } -} - -static const MemoryRegionOps omap_gp_timer_ops = { - .read = omap_gp_timer_readfn, - .write = omap_gp_timer_writefn, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk) -{ - struct omap_gp_timer_s *s = g_new0(struct omap_gp_timer_s, 1); - - s->ta = ta; - s->irq = irq; - s->clk = fclk; - s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_tick, s); - s->match = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_match, s); - s->in = qemu_allocate_irq(omap_gp_timer_input, s, 0); - omap_gp_timer_reset(s); - omap_gp_timer_clk_setup(s); - - memory_region_init_io(&s->iomem, NULL, &omap_gp_timer_ops, s, "omap.gptimer", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} diff --git a/hw/timer/omap_synctimer.c b/hw/timer/omap_synctimer.c deleted file mode 100644 index 72b997939b..0000000000 --- a/hw/timer/omap_synctimer.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * TI OMAP2 32kHz sync timer emulation. - * - * Copyright (C) 2007-2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) any later version of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "hw/arm/omap.h" -struct omap_synctimer_s { - MemoryRegion iomem; - uint32_t val; - uint16_t readh; -}; - -/* 32-kHz Sync Timer of the OMAP2 */ -static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 0x8000, - NANOSECONDS_PER_SECOND); -} - -void omap_synctimer_reset(struct omap_synctimer_s *s) -{ - s->val = omap_synctimer_read(s); -} - -static uint32_t omap_synctimer_readw(void *opaque, hwaddr addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - - switch (addr) { - case 0x00: /* 32KSYNCNT_REV */ - return 0x21; - - case 0x10: /* CR */ - return omap_synctimer_read(s) - s->val; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_synctimer_readh(void *opaque, hwaddr addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_synctimer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static uint64_t omap_synctimer_readfn(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return omap_badwidth_read32(opaque, addr); - case 2: - return omap_synctimer_readh(opaque, addr); - case 4: - return omap_synctimer_readw(opaque, addr); - default: - g_assert_not_reached(); - } -} - -static void omap_synctimer_writefn(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - OMAP_BAD_REG(addr); -} - -static const MemoryRegionOps omap_synctimer_ops = { - .read = omap_synctimer_readfn, - .write = omap_synctimer_writefn, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk) -{ - struct omap_synctimer_s *s = g_malloc0(sizeof(*s)); - - omap_synctimer_reset(s); - memory_region_init_io(&s->iomem, NULL, &omap_synctimer_ops, s, "omap.synctimer", - omap_l4_region_size(ta, 0)); - omap_l4_attach(ta, 0, &s->iomem); - - return s; -} diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 2ae5ae3212..3234bbb1f4 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -12,12 +12,12 @@ #include "hw/qdev-properties.h" #include "qemu/timer.h" #include "sysemu/runstate.h" -#include "hw/arm/pxa.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" #include "qom/object.h" +#include "sysemu/watchdog.h" #define OSMR0 0x00 #define OSMR1 0x04 @@ -54,7 +54,6 @@ #define OSNR 0x20 #define PXA25X_FREQ 3686400 /* 3.6864 MHz */ -#define PXA27X_FREQ 3250000 /* 3.25 MHz */ static int pxa2xx_timer4_freq[8] = { [0] = 0, @@ -417,7 +416,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 +500,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 +510,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 +532,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), @@ -572,28 +571,6 @@ static const TypeInfo pxa25x_timer_dev_info = { .class_init = pxa25x_timer_dev_class_init, }; -static Property pxa27x_timer_dev_properties[] = { - DEFINE_PROP_UINT32("freq", PXA2xxTimerInfo, freq, PXA27X_FREQ), - DEFINE_PROP_BIT("tm4", PXA2xxTimerInfo, flags, - PXA2XX_TIMER_HAVE_TM4, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void pxa27x_timer_dev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->desc = "PXA27x timer"; - device_class_set_props(dc, pxa27x_timer_dev_properties); -} - -static const TypeInfo pxa27x_timer_dev_info = { - .name = "pxa27x-timer", - .parent = TYPE_PXA2XX_TIMER, - .instance_size = sizeof(PXA2xxTimerInfo), - .class_init = pxa27x_timer_dev_class_init, -}; - static void pxa2xx_timer_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -615,7 +592,6 @@ static void pxa2xx_timer_register_types(void) { type_register_static(&pxa2xx_timer_type_info); type_register_static(&pxa25x_timer_dev_info); - type_register_static(&pxa27x_timer_dev_info); } type_init(pxa2xx_timer_register_types) diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c index 69eabc678a..cd59b08c87 100644 --- a/hw/timer/renesas_cmt.c +++ b/hw/timer/renesas_cmt.c @@ -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), @@ -263,7 +263,7 @@ static void rcmt_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_rcmt; - dc->reset = rcmt_reset; + device_class_set_legacy_reset(dc, rcmt_reset); device_class_set_props(dc, rcmt_properties); } diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c index c15f654738..a93e075fcd 100644 --- a/hw/timer/renesas_tmr.c +++ b/hw/timer/renesas_tmr.c @@ -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; @@ -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), @@ -473,7 +473,7 @@ static void rtmr_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_rtmr; - dc->reset = rtmr_reset; + device_class_set_legacy_reset(dc, rtmr_reset); device_class_set_props(dc, rtmr_properties); } diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c index c664480ccf..4602fc1a61 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), @@ -446,7 +446,7 @@ static void sifive_pwm_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = sifive_pwm_reset; + device_class_set_legacy_reset(dc, sifive_pwm_reset); device_class_set_props(dc, sifive_pwm_properties); dc->vmsd = &vmstate_sifive_pwm; dc->realize = sifive_pwm_realize; diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 8c4f6eb06b..12cb3bac97 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() @@ -429,7 +429,7 @@ static void slavio_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = slavio_timer_reset; + device_class_set_legacy_reset(dc, slavio_timer_reset); dc->vmsd = &vmstate_slavio_timer; device_class_set_props(dc, slavio_timer_properties); } diff --git a/hw/timer/sse-counter.c b/hw/timer/sse-counter.c index 16c0e8ad15..f17064abe3 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() } @@ -454,7 +454,7 @@ static void sse_counter_class_init(ObjectClass *klass, void *data) dc->realize = sse_counter_realize; dc->vmsd = &sse_counter_vmstate; - dc->reset = sse_counter_reset; + device_class_set_legacy_reset(dc, sse_counter_reset); } static const TypeInfo sse_counter_info = { diff --git a/hw/timer/sse-timer.c b/hw/timer/sse-timer.c index e92e83747d..115b0138c8 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), @@ -451,7 +451,7 @@ static void sse_timer_class_init(ObjectClass *klass, void *data) dc->realize = sse_timer_realize; dc->vmsd = &sse_timer_vmstate; - dc->reset = sse_timer_reset; + device_class_set_legacy_reset(dc, sse_timer_reset); device_class_set_props(dc, sse_timer_properties); } 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..16b47887a5 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), @@ -325,7 +325,7 @@ static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = stm32f2xx_timer_reset; + device_class_set_legacy_reset(dc, stm32f2xx_timer_reset); device_class_set_props(dc, stm32f2xx_timer_properties); dc->vmsd = &vmstate_stm32f2xx_timer; dc->realize = stm32f2xx_timer_realize; diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 3eccef8385..5cfc369fba 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" @@ -49,6 +49,12 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" +# imx_gpt.c +imx_gpt_set_freq(uint32_t clksrc, uint32_t freq) "Setting clksrc %u to %u Hz" +imx_gpt_read(const char *name, uint64_t value) "%s -> 0x%08" PRIx64 +imx_gpt_write(const char *name, uint64_t value) "%s <- 0x%08" PRIx64 +imx_gpt_timeout(void) "" + # npcm7xx_timer.c npcm7xx_timer_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 npcm7xx_timer_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64 @@ -99,3 +105,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(uint8_t reg_off) "hpet_ram_writel HPET_TN_CFG + %" PRIu8 +hpet_ram_write_tn_cmp(uint8_t reg_off) "hpet_ram_writel HPET_TN_CMP + %" PRIu8 +hpet_ram_write_invalid_tn_cmp(void) "invalid HPET_TN_CMP + 4 write" +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 ea930da545..5cd5a2533b 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -220,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(), } 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..5f7a0dfc61 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -206,7 +206,6 @@ static int tpm_spapr_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data) break; default: g_assert_not_reached(); - break; } trace_tpm_spapr_do_crq_get_version(be32_to_cpu(local_crq.data)); spapr_tpm_send_crq(dev, &local_crq); @@ -353,7 +352,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..c5548b0a45 --- /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; + device_class_set_legacy_reset(dc, 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 91e3792248..21109edcaa 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" @@ -53,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), @@ -177,7 +177,7 @@ static void tpm_tis_isa_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_tpm_tis_isa; tc->model = TPM_MODEL_TPM_TIS; dc->realize = tpm_tis_isa_realizefn; - dc->reset = tpm_tis_isa_reset; + device_class_set_legacy_reset(dc, tpm_tis_isa_reset); tc->request_completed = tpm_tis_isa_request_completed; tc->get_version = tpm_tis_isa_get_tpm_version; set_bit(DEVICE_CATEGORY_MISC, dc->categories); diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 45e63efd63..967f264634 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(), }; @@ -136,7 +135,7 @@ static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) tc->model = TPM_MODEL_TPM_TIS; dc->realize = tpm_tis_sysbus_realizefn; dc->user_creatable = true; - dc->reset = tpm_tis_sysbus_reset; + device_class_set_legacy_reset(dc, tpm_tis_sysbus_reset); tc->request_completed = tpm_tis_sysbus_request_completed; tc->get_version = tpm_tis_sysbus_get_tpm_version; set_bit(DEVICE_CATEGORY_MISC, dc->categories); 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/Kconfig b/hw/tricore/Kconfig index 33c1e852c3..6c04f64949 100644 --- a/hw/tricore/Kconfig +++ b/hw/tricore/Kconfig @@ -1,8 +1,12 @@ config TRICORE_TESTBOARD + default y + depends on TRICORE bool config TRIBOARD bool + default y + depends on TRICORE select TC27X_SOC config TC27X_SOC 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..ae95c49565 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); } @@ -63,7 +67,7 @@ static void tricore_testdevice_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, tricore_testdevice_properties); - dc->reset = tricore_testdevice_reset; + device_class_set_legacy_reset(dc, tricore_testdevice_reset); } static const TypeInfo tricore_testdevice_info = { 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..531dcfc686 --- /dev/null +++ b/hw/ufs/trace-events @@ -0,0 +1,50 @@ +# 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"" +ufs_mcq_complete_req(uint8_t qid) "sqid %"PRIu8"" +ufs_mcq_create_sq(uint8_t sqid, uint8_t cqid, uint64_t addr, uint16_t size) "mcq create sq sqid %"PRIu8", cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" +ufs_mcq_create_cq(uint8_t cqid, uint64_t addr, uint16_t size) "mcq create cq cqid %"PRIu8", addr 0x%"PRIx64", size %"PRIu16"" + +# 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_read_sq(uint8_t sqid, uint64_t addr) "failed to read sq entry. sqid %"PRIu8", hwaddr %"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_dma_write_cq(uint8_t cqid, uint64_t addr) "failed to write cq entry. cqid %"PRIu8", hwaddr %"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"" +ufs_err_mcq_db_wr_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_db_wr_invalid_db(uint8_t qid, uint32_t db) "invalid mcq doorbell sqid %"PRIu8", db %"PRIu32"" +ufs_err_mcq_create_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_create_sq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_sq_already_exists(uint8_t qid) "mcq sqid %"PRIu8 "already exists" +ufs_err_mcq_delete_sq_invalid_sqid(uint8_t qid) "invalid mcq sqid %"PRIu8"" +ufs_err_mcq_delete_sq_not_exists(uint8_t qid) "mcq sqid %"PRIu8 "not exists" +ufs_err_mcq_create_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_create_cq_already_exists(uint8_t qid) "mcq cqid %"PRIu8 "already exists" +ufs_err_mcq_delete_cq_invalid_cqid(uint8_t qid) "invalid mcq cqid %"PRIu8"" +ufs_err_mcq_delete_cq_not_exists(uint8_t qid) "mcq cqid %"PRIu8 "not exists" +ufs_err_mcq_delete_cq_sq_not_deleted(uint8_t sqid, uint8_t cqid) "mcq sq %"PRIu8" still has cq %"PRIu8"" 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..79f786ed4e --- /dev/null +++ b/hw/ufs/ufs.c @@ -0,0 +1,1834 @@ +/* + * 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/, 4.0 + * + * 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 4.0 */ +#define UFS_SPEC_VER 0x0400 +#define UFS_MAX_NUTRS 32 +#define UFS_MAX_NUTMRS 8 +#define UFS_MCQ_QCFGPTR 2 + +static void ufs_exec_req(UfsRequest *req); +static void ufs_clear_req(UfsRequest *req); + +static inline uint64_t ufs_mcq_reg_addr(UfsHc *u, int qid) +{ + /* Submission Queue MCQ Registers offset (400h) */ + return (UFS_MCQ_QCFGPTR * 0x200) + qid * 0x40; +} + +static inline uint64_t ufs_mcq_op_reg_addr(UfsHc *u, int qid) +{ + /* MCQ Operation & Runtime Registers offset (1000h) */ + return UFS_MCQ_OPR_START + qid * 48; +} + +static inline uint64_t ufs_reg_size(UfsHc *u) +{ + /* Total UFS HCI Register size in bytes */ + return ufs_mcq_op_reg_addr(u, 0) + sizeof(u->mcq_op_reg); +} + +static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr, unsigned size) +{ + uint64_t mcq_reg_addr; + + if (!u->params.mcq) { + return false; + } + + mcq_reg_addr = ufs_mcq_reg_addr(u, 0); + return (addr >= mcq_reg_addr && + addr + size <= mcq_reg_addr + sizeof(u->mcq_reg)); +} + +static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr, unsigned size) +{ + uint64_t mcq_op_reg_addr; + + if (!u->params.mcq) { + return false; + } + + mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0); + return (addr >= mcq_op_reg_addr && + addr + size <= mcq_op_reg_addr + sizeof(u->mcq_op_reg)); +} + +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; + + if (copy_size > sizeof(req->req_upiu)) { + copy_size = sizeof(req->req_upiu); + } + + 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; + + /* + * In case of MCQ, UTRD has already been read from a SQ, so skip it. + */ + if (!ufs_mcq_req(req)) { + 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; + } + + if (copy_size > sizeof(req->rsp_upiu)) { + copy_size = sizeof(req->rsp_upiu); + } + + 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_mcq_init_req(UfsHc *u, UfsRequest *req, UfsSq *sq) +{ + memset(req, 0, sizeof(*req)); + + req->hc = u; + req->state = UFS_REQUEST_IDLE; + req->slot = UFS_INVALID_SLOT; + req->sq = sq; +} + +static void ufs_mcq_process_sq(void *opaque) +{ + UfsSq *sq = opaque; + UfsHc *u = sq->u; + UfsSqEntry sqe; + UfsRequest *req; + hwaddr addr; + uint16_t head = ufs_mcq_sq_head(u, sq->sqid); + int err; + + while (!(ufs_mcq_sq_empty(u, sq->sqid) || QTAILQ_EMPTY(&sq->req_list))) { + addr = sq->addr + head; + err = ufs_addr_read(sq->u, addr, (void *)&sqe, sizeof(sqe)); + if (err) { + trace_ufs_err_dma_read_sq(sq->sqid, addr); + return; + } + + head = (head + sizeof(sqe)) % (sq->size * sizeof(sqe)); + ufs_mcq_update_sq_head(u, sq->sqid, head); + + req = QTAILQ_FIRST(&sq->req_list); + QTAILQ_REMOVE(&sq->req_list, req, entry); + + ufs_mcq_init_req(sq->u, req, sq); + memcpy(&req->utrd, &sqe, sizeof(req->utrd)); + + req->state = UFS_REQUEST_RUNNING; + ufs_exec_req(req); + } +} + +static void ufs_mcq_process_cq(void *opaque) +{ + UfsCq *cq = opaque; + UfsHc *u = cq->u; + UfsRequest *req, *next; + MemTxResult ret; + uint32_t tail = ufs_mcq_cq_tail(u, cq->cqid); + + QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next) + { + ufs_dma_write_rsp_upiu(req); + + req->cqe.utp_addr = + ((uint64_t)req->utrd.command_desc_base_addr_hi << 32ULL) | + req->utrd.command_desc_base_addr_lo; + req->cqe.utp_addr |= req->sq->sqid; + req->cqe.resp_len = req->utrd.response_upiu_length; + req->cqe.resp_off = req->utrd.response_upiu_offset; + req->cqe.prdt_len = req->utrd.prd_table_length; + req->cqe.prdt_off = req->utrd.prd_table_offset; + req->cqe.status = req->utrd.header.dword_2 & 0xf; + req->cqe.error = 0; + + ret = ufs_addr_write(u, cq->addr + tail, &req->cqe, sizeof(req->cqe)); + if (ret) { + trace_ufs_err_dma_write_cq(cq->cqid, cq->addr + tail); + } + QTAILQ_REMOVE(&cq->req_list, req, entry); + + tail = (tail + sizeof(req->cqe)) % (cq->size * sizeof(req->cqe)); + ufs_mcq_update_cq_tail(u, cq->cqid, tail); + + ufs_clear_req(req); + QTAILQ_INSERT_TAIL(&req->sq->req_list, req, entry); + } + + if (!ufs_mcq_cq_empty(u, cq->cqid)) { + u->mcq_op_reg[cq->cqid].cq_int.is = + FIELD_DP32(u->mcq_op_reg[cq->cqid].cq_int.is, CQIS, TEPS, 1); + + u->reg.is = FIELD_DP32(u->reg.is, IS, CQES, 1); + ufs_irq_check(u); + } +} + +static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr) +{ + UfsMcqReg *reg = &u->mcq_reg[qid]; + UfsSq *sq; + uint8_t cqid = FIELD_EX32(attr, SQATTR, CQID); + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_create_sq_invalid_sqid(qid); + return false; + } + + if (u->sq[qid]) { + trace_ufs_err_mcq_create_sq_already_exists(qid); + return false; + } + + if (!u->cq[cqid]) { + trace_ufs_err_mcq_create_sq_invalid_cqid(qid); + return false; + } + + sq = g_malloc0(sizeof(*sq)); + sq->u = u; + sq->sqid = qid; + sq->cq = u->cq[cqid]; + sq->addr = ((uint64_t)reg->squba << 32) | reg->sqlba; + sq->size = ((FIELD_EX32(attr, SQATTR, SIZE) + 1) << 2) / sizeof(UfsSqEntry); + + sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq, + &DEVICE(u)->mem_reentrancy_guard); + sq->req = g_new0(UfsRequest, sq->size); + QTAILQ_INIT(&sq->req_list); + for (int i = 0; i < sq->size; i++) { + ufs_mcq_init_req(u, &sq->req[i], sq); + QTAILQ_INSERT_TAIL(&sq->req_list, &sq->req[i], entry); + } + + u->sq[qid] = sq; + + trace_ufs_mcq_create_sq(sq->sqid, sq->cq->cqid, sq->addr, sq->size); + return true; +} + +static bool ufs_mcq_delete_sq(UfsHc *u, uint8_t qid) +{ + UfsSq *sq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_delete_sq_invalid_sqid(qid); + return false; + } + + if (!u->sq[qid]) { + trace_ufs_err_mcq_delete_sq_not_exists(qid); + return false; + } + + sq = u->sq[qid]; + + qemu_bh_delete(sq->bh); + g_free(sq->req); + g_free(sq); + u->sq[qid] = NULL; + return true; +} + +static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr) +{ + UfsMcqReg *reg = &u->mcq_reg[qid]; + UfsCq *cq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_create_cq_invalid_cqid(qid); + return false; + } + + if (u->cq[qid]) { + trace_ufs_err_mcq_create_cq_already_exists(qid); + return false; + } + + cq = g_malloc0(sizeof(*cq)); + cq->u = u; + cq->cqid = qid; + cq->addr = ((uint64_t)reg->cquba << 32) | reg->cqlba; + cq->size = ((FIELD_EX32(attr, CQATTR, SIZE) + 1) << 2) / sizeof(UfsCqEntry); + + cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq, + &DEVICE(u)->mem_reentrancy_guard); + QTAILQ_INIT(&cq->req_list); + + u->cq[qid] = cq; + + trace_ufs_mcq_create_cq(cq->cqid, cq->addr, cq->size); + return true; +} + +static bool ufs_mcq_delete_cq(UfsHc *u, uint8_t qid) +{ + UfsCq *cq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_delete_cq_invalid_cqid(qid); + return false; + } + + if (!u->cq[qid]) { + trace_ufs_err_mcq_delete_cq_not_exists(qid); + return false; + } + + for (int i = 0; i < ARRAY_SIZE(u->sq); i++) { + if (u->sq[i] && u->sq[i]->cq->cqid == qid) { + trace_ufs_err_mcq_delete_cq_sq_not_deleted(i, qid); + return false; + } + } + + cq = u->cq[qid]; + + qemu_bh_delete(cq->bh); + g_free(cq); + u->cq[qid] = NULL; + return true; +} + +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_CONFIG: + u->reg.config = data; + break; + case A_MCQCONFIG: + u->reg.mcqconfig = 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 void ufs_write_mcq_reg(UfsHc *u, hwaddr offset, uint32_t data, + unsigned size) +{ + int qid = offset / sizeof(UfsMcqReg); + UfsMcqReg *reg = &u->mcq_reg[qid]; + + switch (offset % sizeof(UfsMcqReg)) { + case A_SQATTR: + if (!FIELD_EX32(reg->sqattr, SQATTR, SQEN) && + FIELD_EX32(data, SQATTR, SQEN)) { + if (!ufs_mcq_create_sq(u, qid, data)) { + break; + } + } else if (FIELD_EX32(reg->sqattr, SQATTR, SQEN) && + !FIELD_EX32(data, SQATTR, SQEN)) { + if (!ufs_mcq_delete_sq(u, qid)) { + break; + } + } + reg->sqattr = data; + break; + case A_SQLBA: + reg->sqlba = data; + break; + case A_SQUBA: + reg->squba = data; + break; + case A_SQCFG: + reg->sqcfg = data; + break; + case A_CQATTR: + if (!FIELD_EX32(reg->cqattr, CQATTR, CQEN) && + FIELD_EX32(data, CQATTR, CQEN)) { + if (!ufs_mcq_create_cq(u, qid, data)) { + break; + } + } else if (FIELD_EX32(reg->cqattr, CQATTR, CQEN) && + !FIELD_EX32(data, CQATTR, CQEN)) { + if (!ufs_mcq_delete_cq(u, qid)) { + break; + } + } + reg->cqattr = data; + break; + case A_CQLBA: + reg->cqlba = data; + break; + case A_CQUBA: + reg->cquba = data; + break; + case A_CQCFG: + reg->cqcfg = data; + break; + case A_SQDAO: + case A_SQISAO: + case A_CQDAO: + case A_CQISAO: + trace_ufs_err_unsupport_register_offset(offset); + break; + default: + trace_ufs_err_invalid_register_offset(offset); + break; + } +} + +static void ufs_mcq_process_db(UfsHc *u, uint8_t qid, uint32_t db) +{ + UfsSq *sq; + + if (qid >= u->params.mcq_maxq) { + trace_ufs_err_mcq_db_wr_invalid_sqid(qid); + return; + } + + sq = u->sq[qid]; + if (sq->size * sizeof(UfsSqEntry) <= db) { + trace_ufs_err_mcq_db_wr_invalid_db(qid, db); + return; + } + + ufs_mcq_update_sq_tail(u, sq->sqid, db); + qemu_bh_schedule(sq->bh); +} + +static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data, + unsigned size) +{ + int qid = offset / sizeof(UfsMcqOpReg); + UfsMcqOpReg *opr = &u->mcq_op_reg[qid]; + + switch (offset % sizeof(UfsMcqOpReg)) { + case offsetof(UfsMcqOpReg, sq.tp): + if (opr->sq.tp != data) { + ufs_mcq_process_db(u, qid, data); + } + opr->sq.tp = data; + break; + case offsetof(UfsMcqOpReg, cq.hp): + opr->cq.hp = data; + ufs_mcq_update_cq_head(u, qid, data); + break; + case offsetof(UfsMcqOpReg, cq_int.is): + opr->cq_int.is &= ~data; + 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; + uint32_t *ptr; + uint64_t value; + uint64_t offset; + + if (addr + size <= sizeof(u->reg)) { + offset = addr; + ptr = (uint32_t *)&u->reg; + } else if (ufs_is_mcq_reg(u, addr, size)) { + offset = addr - ufs_mcq_reg_addr(u, 0); + ptr = (uint32_t *)&u->mcq_reg; + } else if (ufs_is_mcq_op_reg(u, addr, size)) { + offset = addr - ufs_mcq_op_reg_addr(u, 0); + ptr = (uint32_t *)&u->mcq_op_reg; + } else { + trace_ufs_err_invalid_register_offset(addr); + return 0; + } + + value = ptr[offset >> 2]; + 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; + + trace_ufs_mmio_write(addr, data, size); + + if (addr + size <= sizeof(u->reg)) { + ufs_write_reg(u, addr, data, size); + } else if (ufs_is_mcq_reg(u, addr, size)) { + ufs_write_mcq_reg(u, addr - ufs_mcq_reg_addr(u, 0), data, size); + } else if (ufs_is_mcq_op_reg(u, addr, size)) { + ufs_write_mcq_op_reg(u, addr - ufs_mcq_op_reg_addr(u, 0), data, size); + } else { + trace_ufs_err_invalid_register_offset(addr); + } +} + +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); +} + +void ufs_build_query_response(UfsRequest *req) +{ + 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; +} + +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 QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value) +{ + switch (idn) { + case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL: + if (value > UFS_QUERY_ATTR_ACTIVE_ICC_MAXVALUE) { + return UFS_QUERY_RESULT_INVALID_VALUE; + } + 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; + } + return UFS_QUERY_RESULT_SUCCESS; +} + +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); + ret = UFS_QUERY_RESULT_SUCCESS; + } else { + value = req->req_upiu.qr.value; + ret = ufs_write_attr_value(u, idn, value); + } + req->rsp_upiu.qr.value = cpu_to_be32(value); + return ret; +} + +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; + uint8_t selector = req->req_upiu.qr.selector; + uint16_t length = be16_to_cpu(req->req_upiu.qr.length); + InterconnectDescriptor desc; + if (selector != 0) { + return UFS_QUERY_RESULT_INVALID_SELECTOR; + } + 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.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); + ufs_build_query_response(req); + + 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); + } + + req->state = UFS_REQUEST_COMPLETE; + + if (ufs_mcq_req(req)) { + trace_ufs_mcq_complete_req(req->sq->sqid); + QTAILQ_INSERT_TAIL(&req->sq->cq->req_list, req, entry); + qemu_bh_schedule(req->sq->cq->bh); + } else { + trace_ufs_complete_req(req->slot); + 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; + } + + if (u->params.mcq_maxq >= UFS_MAX_MCQ_QNUM) { + error_setg(errp, "mcq-maxq must be less than %d", UFS_MAX_MCQ_QNUM); + 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); + + if (u->params.mcq) { + memset(u->sq, 0, sizeof(u->sq)); + memset(u->cq, 0, sizeof(u->cq)); + } +} + +static void ufs_init_hc(UfsHc *u) +{ + uint32_t cap = 0; + uint32_t mcqconfig = 0; + uint32_t mcqcap = 0; + + u->reg_size = pow2ceil(ufs_reg_size(u)); + + memset(&u->reg, 0, sizeof(u->reg)); + memset(&u->mcq_reg, 0, sizeof(u->mcq_reg)); + memset(&u->mcq_op_reg, 0, sizeof(u->mcq_op_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); + cap = FIELD_DP32(cap, CAP, LSDBS, 1); + cap = FIELD_DP32(cap, CAP, MCQS, u->params.mcq); + u->reg.cap = cap; + + if (u->params.mcq) { + mcqconfig = FIELD_DP32(mcqconfig, MCQCONFIG, MAC, 0x1f); + u->reg.mcqconfig = mcqconfig; + + mcqcap = FIELD_DP32(mcqcap, MCQCAP, MAXQ, u->params.mcq_maxq - 1); + mcqcap = FIELD_DP32(mcqcap, MCQCAP, RRP, 1); + mcqcap = FIELD_DP32(mcqcap, MCQCAP, QCFGPTR, UFS_MCQ_QCFGPTR); + u->reg.mcqcap = mcqcap; + + for (int i = 0; i < ARRAY_SIZE(u->mcq_reg); i++) { + uint64_t addr = ufs_mcq_op_reg_addr(u, i); + u->mcq_reg[i].sqdao = addr; + u->mcq_reg[i].sqisao = addr + sizeof(UfsMcqSqReg); + addr += sizeof(UfsMcqSqReg); + u->mcq_reg[i].cqdao = addr + sizeof(UfsMcqSqIntReg); + addr += sizeof(UfsMcqSqIntReg); + u->mcq_reg[i].cqisao = addr + sizeof(UfsMcqCqReg); + } + } + 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); + + for (int i = 0; i < ARRAY_SIZE(u->sq); i++) { + if (u->sq[i]) { + ufs_mcq_delete_sq(u, i); + } + } + for (int i = 0; i < ARRAY_SIZE(u->cq); i++) { + if (u->cq[i]) { + ufs_mcq_delete_cq(u, i); + } + } +} + +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_BOOL("mcq", UfsHc, params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2), + 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..4bcc41f53a --- /dev/null +++ b/hw/ufs/ufs.h @@ -0,0 +1,234 @@ +/* + * 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_MAX_MCQ_QNUM 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; + +#define UFS_INVALID_SLOT (-1) +typedef struct UfsRequest { + struct UfsHc *hc; + UfsRequestState state; + int slot; /* -1 when it's a MCQ request */ + + UtpTransferReqDesc utrd; + UtpUpiuReq req_upiu; + UtpUpiuRsp rsp_upiu; + + /* for scsi command */ + QEMUSGList *sg; + uint32_t data_len; + + /* for MCQ */ + struct UfsSq *sq; + struct UfsCqEntry cqe; + QTAILQ_ENTRY(UfsRequest) entry; +} UfsRequest; + +static inline bool ufs_mcq_req(UfsRequest *req) +{ + return req->sq != NULL; +} + +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 */ + bool mcq; /* Multiple Command Queue support */ + uint8_t mcq_qcfgptr; /* MCQ Queue Configuration Pointer in MCQCAP */ + uint8_t mcq_maxq; /* MCQ Maximum number of Queues */ +} UfsParams; + +/* + * MCQ Properties + */ +typedef struct UfsSq { + struct UfsHc *u; + uint8_t sqid; + struct UfsCq *cq; + uint64_t addr; + uint16_t size; /* A number of entries (qdepth) */ + + QEMUBH *bh; /* Bottom half to process requests in async */ + UfsRequest *req; + QTAILQ_HEAD(, UfsRequest) req_list; /* Free request list */ +} UfsSq; + +typedef struct UfsCq { + struct UfsHc *u; + uint8_t cqid; + uint64_t addr; + uint16_t size; /* A number of entries (qdepth) */ + + QEMUBH *bh; + QTAILQ_HEAD(, UfsRequest) req_list; +} UfsCq; + +typedef struct UfsHc { + PCIDevice parent_obj; + UfsBus bus; + MemoryRegion iomem; + UfsReg reg; + UfsMcqReg mcq_reg[UFS_MAX_MCQ_QNUM]; + UfsMcqOpReg mcq_op_reg[UFS_MAX_MCQ_QNUM]; + 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; + + /* MCQ properties */ + UfsSq *sq[UFS_MAX_MCQ_QNUM]; + UfsCq *cq[UFS_MAX_MCQ_QNUM]; +} UfsHc; + +static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].sq.tp; +} + +static inline void ufs_mcq_update_sq_tail(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].sq.tp = db; +} + +static inline uint32_t ufs_mcq_sq_head(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].sq.hp; +} + +static inline void ufs_mcq_update_sq_head(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].sq.hp = db; +} + +static inline bool ufs_mcq_sq_empty(UfsHc *u, uint32_t qid) +{ + return ufs_mcq_sq_tail(u, qid) == ufs_mcq_sq_head(u, qid); +} + +static inline uint32_t ufs_mcq_cq_tail(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].cq.tp; +} + +static inline void ufs_mcq_update_cq_tail(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].cq.tp = db; +} + +static inline uint32_t ufs_mcq_cq_head(UfsHc *u, uint32_t qid) +{ + return u->mcq_op_reg[qid].cq.hp; +} + +static inline void ufs_mcq_update_cq_head(UfsHc *u, uint32_t qid, uint32_t db) +{ + u->mcq_op_reg[qid].cq.hp = db; +} + +static inline bool ufs_mcq_cq_empty(UfsHc *u, uint32_t qid) +{ + return ufs_mcq_cq_tail(u, qid) == ufs_mcq_cq_head(u, qid); +} + +#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_build_query_response(UfsRequest *req); +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 ce4f433976..5fbecd2f43 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 @@ -49,17 +53,19 @@ config USB_XHCI_SYSBUS bool select USB_XHCI -config USB_MUSB - bool - select USB - config USB_DWC2 bool select USB -config TUSB6010 +config USB_HUB bool - select USB_MUSB + default y + depends on USB + +config USB_HID + bool + default y + depends on USB config USB_TABLET_WACOM bool @@ -136,5 +142,4 @@ config USB_DWC3 config XLNX_USB_SUBSYS bool - default y if XLNX_VERSAL select USB_DWC3 diff --git a/hw/usb/bus-stub.c b/hw/usb/bus-stub.c new file mode 100644 index 0000000000..fcabe8429e --- /dev/null +++ b/hw/usb/bus-stub.c @@ -0,0 +1,28 @@ +/* + * QEMU USB device emulation stubs + * + * Copyright (C) 2021 Philippe Mathieu-Daudé + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#include "hw/usb.h" + +USBDevice *usbdevice_create(const char *driver) +{ + error_report("Support for USB devices not built-in"); + + return NULL; +} + +HumanReadableText *qmp_x_query_usb(Error **errp) +{ + error_setg(errp, "Support for USB devices not built-in"); + return NULL; +} 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 index bbc5da07b5..b306eeb20e 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -4,7 +4,7 @@ * Copyright (c) 2021-2022 Canokeys.org * Written by Hongren (Zenithal) Zheng * - * This code is licensed under the Apache-2.0. + * This code is licensed under the GPL v2 or later. */ #include "qemu/osdep.h" diff --git a/hw/usb/canokey.h b/hw/usb/canokey.h index 24cf304203..e528889d33 100644 --- a/hw/usb/canokey.h +++ b/hw/usb/canokey.h @@ -4,7 +4,7 @@ * Copyright (c) 2021-2022 Canokeys.org * Written by Hongren (Zenithal) Zheng * - * This code is licensed under the Apache-2.0. + * This code is licensed under the GPL v2 or later. */ #ifndef CANOKEY_H diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index ee41a81801..3ee9c73b87 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -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 4aebdf9ed5..18f6d598dd 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -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); @@ -479,6 +479,7 @@ static void usb_hub_handle_control(USBDevice *dev, USBPacket *p, usb_hub_port_clear(port, PORT_STAT_SUSPEND); port->wPortChange = 0; } + break; default: goto fail; } @@ -630,7 +631,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() @@ -649,7 +650,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() }, @@ -659,13 +660,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 1cac1cd435..554b397e88 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -886,7 +886,7 @@ static MTPData *usb_mtp_get_storage_info(MTPState *s, MTPControl *c) rc = statvfs(s->root, &buf); if (rc == 0) { usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_blocks); - usb_mtp_add_u64(d, (uint64_t)buf.f_bavail * buf.f_blocks); + usb_mtp_add_u64(d, (uint64_t)buf.f_frsize * buf.f_bavail); usb_mtp_add_u32(d, buf.f_ffree); } else { usb_mtp_add_u64(d, 0xffffffff); @@ -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 5fff487ee5..d00d68b21d 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -475,14 +475,6 @@ struct rndis_packet_msg_type { le32 Reserved; }; -struct rndis_config_parameter { - le32 ParameterNameOffset; - le32 ParameterNameLength; - le32 ParameterType; - le32 ParameterValueOffset; - le32 ParameterValueLength; -}; - /* implementation specific */ enum rndis_state { @@ -1386,7 +1378,8 @@ static void usb_net_realize(USBDevice *dev, Error **errp) 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 e3bcffb3e0..341e505bd0 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -403,7 +403,7 @@ 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; @@ -465,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) { @@ -526,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) { @@ -572,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 5192b062d6..1804cb6799 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -791,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; @@ -937,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); @@ -946,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/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index 8755e9cbb0..b4f0652c7d 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1128,7 +1128,10 @@ static uint64_t dwc2_hsotg_read(void *ptr, hwaddr addr, unsigned size) val = dwc2_pcgreg_read(ptr, addr, (addr - HSOTG_REG(0xe00)) >> 2, size); break; default: - g_assert_not_reached(); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, addr); + val = 0; + break; } return val; @@ -1160,7 +1163,9 @@ static void dwc2_hsotg_write(void *ptr, hwaddr addr, uint64_t val, dwc2_pcgreg_write(ptr, addr, (addr - HSOTG_REG(0xe00)) >> 2, val, size); break; default: - g_assert_not_reached(); + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", + __func__, addr); + break; } } @@ -1305,7 +1310,7 @@ static void dwc2_reset_enter(Object *obj, ResetType type) } } -static void dwc2_reset_hold(Object *obj) +static void dwc2_reset_hold(Object *obj, ResetType type) { DWC2Class *c = DWC2_USB_GET_CLASS(obj); DWC2State *s = DWC2_USB(obj); @@ -1313,13 +1318,13 @@ static void dwc2_reset_hold(Object *obj) trace_usb_dwc2_reset_hold(); if (c->parent_phases.hold) { - c->parent_phases.hold(obj); + c->parent_phases.hold(obj, type); } dwc2_update_irq(s); } -static void dwc2_reset_exit(Object *obj) +static void dwc2_reset_exit(Object *obj, ResetType type) { DWC2Class *c = DWC2_USB_GET_CLASS(obj); DWC2State *s = DWC2_USB(obj); @@ -1327,7 +1332,7 @@ static void dwc2_reset_exit(Object *obj) trace_usb_dwc2_reset_exit(); if (c->parent_phases.exit) { - c->parent_phases.exit(obj); + c->parent_phases.exit(obj, type); } s->hprt0 = HPRT0_PWR; @@ -1364,7 +1369,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 +1396,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 +1416,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..e7d8c7924b 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), @@ -666,7 +666,7 @@ static void usb_dwc3_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = usb_dwc3_reset; + device_class_set_legacy_reset(dc, usb_dwc3_reset); dc->realize = usb_dwc3_realize; dc->vmsd = &vmstate_usb_dwc3; device_class_set_props(dc, usb_dwc3_properties); diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 4c37c8e227..c94fc9f6c5 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() @@ -162,7 +162,7 @@ static void ehci_class_init(ObjectClass *klass, void *data) k->config_write = usb_ehci_pci_write_config; dc->vmsd = &vmstate_ehci_pci; device_class_set_props(dc, ehci_pci_properties); - dc->reset = usb_ehci_pci_reset; + device_class_set_legacy_reset(dc, usb_ehci_pci_reset); } static const TypeInfo ehci_pci_type_info = { diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index a12e218848..eb7df93ac2 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -19,13 +19,12 @@ #include "hw/qdev-properties.h" #include "hw/usb/hcd-ehci.h" #include "migration/vmstate.h" -#include "qemu/module.h" 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,26 +87,15 @@ 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; device_class_set_props(dc, ehci_sysbus_properties); - dc->reset = usb_ehci_sysbus_reset; + device_class_set_legacy_reset(dc, usb_ehci_sysbus_reset); set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_type_info = { - .name = TYPE_SYS_BUS_EHCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(EHCISysBusState), - .instance_init = ehci_sysbus_init, - .instance_finalize = ehci_sysbus_finalize, - .abstract = true, - .class_init = ehci_sysbus_class_init, - .class_size = sizeof(SysBusEHCIClass), -}; - static void ehci_platform_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -118,12 +106,6 @@ static void ehci_platform_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_platform_type_info = { - .name = TYPE_PLATFORM_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_platform_class_init, -}; - static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -134,12 +116,6 @@ static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_exynos4210_type_info = { - .name = TYPE_EXYNOS4210_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_exynos4210_class_init, -}; - static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -150,12 +126,6 @@ static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_aw_h3_type_info = { - .name = TYPE_AW_H3_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_aw_h3_class_init, -}; - static void ehci_npcm7xx_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -168,12 +138,6 @@ static void ehci_npcm7xx_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_npcm7xx_type_info = { - .name = TYPE_NPCM7XX_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_npcm7xx_class_init, -}; - static void ehci_tegra2_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -184,12 +148,6 @@ static void ehci_tegra2_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_tegra2_type_info = { - .name = TYPE_TEGRA2_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_tegra2_class_init, -}; - static void ehci_ppc4xx_init(Object *o) { EHCISysBusState *s = SYS_BUS_EHCI(o); @@ -207,13 +165,6 @@ static void ehci_ppc4xx_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_ppc4xx_type_info = { - .name = TYPE_PPC4xx_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .class_init = ehci_ppc4xx_class_init, - .instance_init = ehci_ppc4xx_init, -}; - /* * Faraday FUSBH200 USB 2.0 EHCI */ @@ -282,24 +233,55 @@ static void fusbh200_ehci_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static const TypeInfo ehci_fusbh200_type_info = { - .name = TYPE_FUSBH200_EHCI, - .parent = TYPE_SYS_BUS_EHCI, - .instance_size = sizeof(FUSBH200EHCIState), - .instance_init = fusbh200_ehci_init, - .class_init = fusbh200_ehci_class_init, +static const TypeInfo ehci_sysbus_types[] = { + { + .name = TYPE_SYS_BUS_EHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(EHCISysBusState), + .instance_init = ehci_sysbus_init, + .instance_finalize = ehci_sysbus_finalize, + .abstract = true, + .class_init = ehci_sysbus_class_init, + .class_size = sizeof(SysBusEHCIClass), + }, + { + .name = TYPE_PLATFORM_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_platform_class_init, + }, + { + .name = TYPE_EXYNOS4210_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_exynos4210_class_init, + }, + { + .name = TYPE_AW_H3_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_aw_h3_class_init, + }, + { + .name = TYPE_NPCM7XX_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_npcm7xx_class_init, + }, + { + .name = TYPE_TEGRA2_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_tegra2_class_init, + }, + { + .name = TYPE_PPC4xx_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_ppc4xx_class_init, + .instance_init = ehci_ppc4xx_init, + }, + { + .name = TYPE_FUSBH200_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .instance_size = sizeof(FUSBH200EHCIState), + .instance_init = fusbh200_ehci_init, + .class_init = fusbh200_ehci_class_init, + }, }; -static void ehci_sysbus_register_types(void) -{ - type_register_static(&ehci_type_info); - type_register_static(&ehci_platform_type_info); - type_register_static(&ehci_exynos4210_type_info); - type_register_static(&ehci_aw_h3_type_info); - type_register_static(&ehci_npcm7xx_type_info); - type_register_static(&ehci_tegra2_type_info); - type_register_static(&ehci_ppc4xx_type_info); - type_register_static(&ehci_fusbh200_type_info); -} - -type_init(ehci_sysbus_register_types) +DEFINE_TYPES(ehci_sysbus_types) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d4da8dcb8d..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) { @@ -2426,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; @@ -2451,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) { @@ -2473,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), @@ -2508,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) { @@ -2533,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 deleted file mode 100644 index 6dca373cb1..0000000000 --- a/hw/usb/hcd-musb.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* - * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics, - * USB2.0 OTG compliant core used in various chips. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Only host-mode and non-DMA accesses are currently supported. - */ -#include "qemu/osdep.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/usb/hcd-musb.h" -#include "hw/irq.h" -#include "hw/hw.h" - -/* Common USB registers */ -#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 */ - -/* Per-EP registers in indexed mode */ -#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit */ - -/* EP FIFOs */ -#define MUSB_HDRC_FIFO 0x20 - -/* Additional Control Registers */ -#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 */ - -/* Some more registers */ -#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 - -/* 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 */ - -/* Per-EP BUSCTL registers */ -#define MUSB_HDRC_BUSCTL 0x80 - -/* Per-EP registers in flat mode */ -#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 */ - -/* "Bus control" registers */ -#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 - -/* - * 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 - -/* 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 */ - -/* 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 - -/* 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 - -/* CSR0 */ -#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 - -/* 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 - -/* 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 */ - -/* 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 - -/* 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 - -/* 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 - -/* 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 - -/* 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 - -/* 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 - -/* HUBADDR */ -#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 MUSB_DEBUG */ - -#ifdef MUSB_DEBUG -#define TRACE(fmt, ...) fprintf(stderr, "%s@%d: " fmt "\n", __func__, \ - __LINE__, ##__VA_ARGS__) -#else -#define TRACE(...) -#endif - - -static void musb_attach(USBPort *port); -static void musb_detach(USBPort *port); -static void musb_child_detach(USBPort *port, USBDevice *child); -static void musb_schedule_cb(USBPort *port, USBPacket *p); -static void musb_async_cancel_device(MUSBState *s, USBDevice *dev); - -static USBPortOps musb_port_ops = { - .attach = musb_attach, - .detach = musb_detach, - .child_detach = musb_child_detach, - .complete = musb_schedule_cb, -}; - -static USBBusOps musb_bus_ops = { -}; - -typedef struct MUSBPacket MUSBPacket; -typedef struct MUSBEndPoint MUSBEndPoint; - -struct MUSBPacket { - USBPacket p; - MUSBEndPoint *ep; - int dir; -}; - -struct MUSBEndPoint { - uint16_t faddr[2]; - uint8_t haddr[2]; - uint8_t hport[2]; - uint16_t csr[2]; - uint16_t maxp[2]; - uint16_t rxcount; - uint8_t type[2]; - uint8_t interval[2]; - uint8_t config; - uint8_t fifosize; - int timeout[2]; /* Always in microframes */ - - uint8_t *buf[2]; - int fifolen[2]; - int fifostart[2]; - int fifoaddr[2]; - MUSBPacket packey[2]; - int status[2]; - int ext_size[2]; - - /* For callbacks' use */ - int epnum; - int interrupt[2]; - MUSBState *musb; - USBCallback *delayed_cb[2]; - QEMUTimer *intv_timer[2]; -}; - -struct MUSBState { - qemu_irq irqs[musb_irq_max]; - USBBus bus; - USBPort port; - - int idx; - uint8_t devctl; - uint8_t power; - uint8_t faddr; - - uint8_t intr; - uint8_t mask; - uint16_t tx_intr; - uint16_t tx_mask; - uint16_t rx_intr; - uint16_t rx_mask; - - int setup_len; - int session; - - uint8_t buf[0x8000]; - - /* Duplicating the world since 2008!... probably we should have 32 - * logical, single endpoints instead. */ - MUSBEndPoint ep[16]; -}; - -void musb_reset(MUSBState *s) -{ - int i; - - s->faddr = 0x00; - s->devctl = 0; - s->power = MGC_M_POWER_HSENAB; - s->tx_intr = 0x0000; - s->rx_intr = 0x0000; - s->tx_mask = 0xffff; - s->rx_mask = 0xffff; - s->intr = 0x00; - s->mask = 0x06; - s->idx = 0; - - s->setup_len = 0; - s->session = 0; - memset(s->buf, 0, sizeof(s->buf)); - - /* TODO: _DW */ - s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO; - for (i = 0; i < 16; i ++) { - s->ep[i].fifosize = 64; - s->ep[i].maxp[0] = 0x40; - s->ep[i].maxp[1] = 0x40; - s->ep[i].musb = s; - s->ep[i].epnum = i; - usb_packet_init(&s->ep[i].packey[0].p); - usb_packet_init(&s->ep[i].packey[1].p); - } -} - -struct MUSBState *musb_init(DeviceState *parent_device, int gpio_base) -{ - MUSBState *s = g_malloc0(sizeof(*s)); - int i; - - for (i = 0; i < musb_irq_max; i++) { - s->irqs[i] = qdev_get_gpio_in(parent_device, gpio_base + i); - } - - musb_reset(s); - - usb_bus_new(&s->bus, sizeof(s->bus), &musb_bus_ops, parent_device); - usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - - return s; -} - -static void musb_vbus_set(MUSBState *s, int level) -{ - if (level) - s->devctl |= 3 << MGC_S_DEVCTL_VBUS; - else - s->devctl &= ~MGC_M_DEVCTL_VBUS; - - qemu_set_irq(s->irqs[musb_set_vbus], level); -} - -static void musb_intr_set(MUSBState *s, int line, int level) -{ - if (!level) { - s->intr &= ~(1 << line); - qemu_irq_lower(s->irqs[line]); - } else if (s->mask & (1 << line)) { - s->intr |= 1 << line; - qemu_irq_raise(s->irqs[line]); - } -} - -static void musb_tx_intr_set(MUSBState *s, int line, int level) -{ - if (!level) { - s->tx_intr &= ~(1 << line); - if (!s->tx_intr) - qemu_irq_lower(s->irqs[musb_irq_tx]); - } else if (s->tx_mask & (1 << line)) { - s->tx_intr |= 1 << line; - qemu_irq_raise(s->irqs[musb_irq_tx]); - } -} - -static void musb_rx_intr_set(MUSBState *s, int line, int level) -{ - if (line) { - if (!level) { - s->rx_intr &= ~(1 << line); - if (!s->rx_intr) - qemu_irq_lower(s->irqs[musb_irq_rx]); - } else if (s->rx_mask & (1 << line)) { - s->rx_intr |= 1 << line; - qemu_irq_raise(s->irqs[musb_irq_rx]); - } - } else - musb_tx_intr_set(s, line, level); -} - -uint32_t musb_core_intr_get(MUSBState *s) -{ - return (s->rx_intr << 15) | s->tx_intr; -} - -void musb_core_intr_clear(MUSBState *s, uint32_t mask) -{ - if (s->rx_intr) { - s->rx_intr &= mask >> 15; - if (!s->rx_intr) - qemu_irq_lower(s->irqs[musb_irq_rx]); - } - - if (s->tx_intr) { - s->tx_intr &= mask & 0xffff; - if (!s->tx_intr) - qemu_irq_lower(s->irqs[musb_irq_tx]); - } -} - -void musb_set_size(MUSBState *s, int epnum, int size, int is_tx) -{ - s->ep[epnum].ext_size[!is_tx] = size; - s->ep[epnum].fifostart[0] = 0; - s->ep[epnum].fifostart[1] = 0; - s->ep[epnum].fifolen[0] = 0; - s->ep[epnum].fifolen[1] = 0; -} - -static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess) -{ - int detect_prev = prev_dev && prev_sess; - int detect = !!s->port.dev && s->session; - - if (detect && !detect_prev) { - /* Let's skip the ID pin sense and VBUS sense formalities and - * and signal a successful SRP directly. This should work at least - * for the Linux driver stack. */ - musb_intr_set(s, musb_irq_connect, 1); - - if (s->port.dev->speed == USB_SPEED_LOW) { - s->devctl &= ~MGC_M_DEVCTL_FSDEV; - s->devctl |= MGC_M_DEVCTL_LSDEV; - } else { - s->devctl |= MGC_M_DEVCTL_FSDEV; - s->devctl &= ~MGC_M_DEVCTL_LSDEV; - } - - /* A-mode? */ - s->devctl &= ~MGC_M_DEVCTL_BDEVICE; - - /* Host-mode bit? */ - s->devctl |= MGC_M_DEVCTL_HM; -#if 1 - musb_vbus_set(s, 1); -#endif - } else if (!detect && detect_prev) { -#if 1 - musb_vbus_set(s, 0); -#endif - } -} - -/* Attach or detach a device on our only port. */ -static void musb_attach(USBPort *port) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_intr_set(s, musb_irq_vbus_request, 1); - musb_session_update(s, 0, s->session); -} - -static void musb_detach(USBPort *port) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_async_cancel_device(s, port->dev); - - musb_intr_set(s, musb_irq_disconnect, 1); - musb_session_update(s, 1, s->session); -} - -static void musb_child_detach(USBPort *port, USBDevice *child) -{ - MUSBState *s = (MUSBState *) port->opaque; - - musb_async_cancel_device(s, child); -} - -static void musb_cb_tick0(void *opaque) -{ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - - ep->delayed_cb[0](&ep->packey[0].p, opaque); -} - -static void musb_cb_tick1(void *opaque) -{ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - - ep->delayed_cb[1](&ep->packey[1].p, opaque); -} - -#define musb_cb_tick (dir ? musb_cb_tick1 : musb_cb_tick0) - -static void musb_schedule_cb(USBPort *port, USBPacket *packey) -{ - MUSBPacket *p = container_of(packey, MUSBPacket, p); - MUSBEndPoint *ep = p->ep; - int dir = p->dir; - int timeout = 0; - - if (ep->status[dir] == USB_RET_NAK) - timeout = ep->timeout[dir]; - else if (ep->interrupt[dir]) - timeout = 8; - else { - musb_cb_tick(ep); - return; - } - - if (!ep->intv_timer[dir]) - ep->intv_timer[dir] = timer_new_ns(QEMU_CLOCK_VIRTUAL, musb_cb_tick, ep); - - timer_mod(ep->intv_timer[dir], qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(timeout, NANOSECONDS_PER_SECOND, 8000)); -} - -static int musb_timeout(int ttype, int speed, int val) -{ -#if 1 - return val << 3; -#endif - - switch (ttype) { - case USB_ENDPOINT_XFER_CONTROL: - if (val < 2) - return 0; - else if (speed == USB_SPEED_HIGH) - return 1 << (val - 1); - else - return 8 << (val - 1); - - case USB_ENDPOINT_XFER_INT: - if (speed == USB_SPEED_HIGH) - if (val < 2) - return 0; - else - return 1 << (val - 1); - else - return val << 3; - - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_ISOC: - if (val < 2) - return 0; - else if (speed == USB_SPEED_HIGH) - return 1 << (val - 1); - else - return 8 << (val - 1); - /* TODO: what with low-speed Bulk and Isochronous? */ - } - - hw_error("bad interval\n"); -} - -static void musb_packet(MUSBState *s, MUSBEndPoint *ep, - int epnum, int pid, int len, USBCallback cb, int dir) -{ - USBDevice *dev; - USBEndpoint *uep; - int idx = epnum && dir; - int id; - int ttype; - - /* ep->type[0,1] contains: - * in bits 7:6 the speed (0 - invalid, 1 - high, 2 - full, 3 - slow) - * in bits 5:4 the transfer type (BULK / INT) - * in bits 3:0 the EP num - */ - ttype = epnum ? (ep->type[idx] >> 4) & 3 : 0; - - ep->timeout[dir] = musb_timeout(ttype, - ep->type[idx] >> 6, ep->interval[idx]); - ep->interrupt[dir] = ttype == USB_ENDPOINT_XFER_INT; - ep->delayed_cb[dir] = cb; - - /* A wild guess on the FADDR semantics... */ - dev = usb_find_device(&s->port, ep->faddr[idx]); - if (dev == NULL) { - return; - } - uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - id = pid | (dev->addr << 16) | (uep->nr << 8); - usb_packet_setup(&ep->packey[dir].p, pid, uep, 0, id, false, true); - usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); - ep->packey[dir].ep = ep; - ep->packey[dir].dir = dir; - - usb_handle_packet(dev, &ep->packey[dir].p); - - if (ep->packey[dir].p.status == USB_RET_ASYNC) { - usb_device_flush_ep_queue(dev, uep); - ep->status[dir] = len; - return; - } - - if (ep->packey[dir].p.status == USB_RET_SUCCESS) { - ep->status[dir] = ep->packey[dir].p.actual_length; - } else { - ep->status[dir] = ep->packey[dir].p.status; - } - musb_schedule_cb(&s->port, &ep->packey[dir].p); -} - -static void musb_tx_packet_complete(USBPacket *packey, void *opaque) -{ - /* Unfortunately we can't use packey->devep because that's the remote - * endpoint number and may be different than our local. */ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - int epnum = ep->epnum; - MUSBState *s = ep->musb; - - ep->fifostart[0] = 0; - ep->fifolen[0] = 0; -#ifdef CLEAR_NAK - if (ep->status[0] != USB_RET_NAK) { -#endif - if (epnum) - ep->csr[0] &= ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); - else - ep->csr[0] &= ~MGC_M_CSR0_TXPKTRDY; -#ifdef CLEAR_NAK - } -#endif - - /* Clear all of the error bits first */ - if (epnum) - ep->csr[0] &= ~(MGC_M_TXCSR_H_ERROR | MGC_M_TXCSR_H_RXSTALL | - MGC_M_TXCSR_H_NAKTIMEOUT); - else - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - if (ep->status[0] == USB_RET_STALL) { - /* Command not supported by target! */ - ep->status[0] = 0; - - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_RXSTALL; - else - ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; - } - - if (ep->status[0] == USB_RET_NAK) { - ep->status[0] = 0; - - /* NAK timeouts are only generated in Bulk transfers and - * Data-errors in Isochronous. */ - if (ep->interrupt[0]) { - return; - } - - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_NAKTIMEOUT; - else - ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; - } - - if (ep->status[0] < 0) { - if (ep->status[0] == USB_RET_BABBLE) - musb_intr_set(s, musb_irq_rst_babble, 1); - - /* Pretend we've tried three times already and failed (in - * case of USB_TOKEN_SETUP). */ - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_H_ERROR; - else - ep->csr[0] |= MGC_M_CSR0_H_ERROR; - - musb_tx_intr_set(s, epnum, 1); - return; - } - /* TODO: check len for over/underruns of an OUT packet? */ - -#ifdef SETUPLEN_HACK - if (!epnum && ep->packey[0].pid == USB_TOKEN_SETUP) - s->setup_len = ep->packey[0].data[6]; -#endif - - /* In DMA mode: if no error, assert DMA request for this EP, - * and skip the interrupt. */ - musb_tx_intr_set(s, epnum, 1); -} - -static void musb_rx_packet_complete(USBPacket *packey, void *opaque) -{ - /* Unfortunately we can't use packey->devep because that's the remote - * endpoint number and may be different than our local. */ - MUSBEndPoint *ep = (MUSBEndPoint *) opaque; - int epnum = ep->epnum; - MUSBState *s = ep->musb; - - ep->fifostart[1] = 0; - ep->fifolen[1] = 0; - -#ifdef CLEAR_NAK - if (ep->status[1] != USB_RET_NAK) { -#endif - ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; - if (!epnum) - ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; -#ifdef CLEAR_NAK - } -#endif - - /* Clear all of the imaginable error bits first */ - ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | - MGC_M_RXCSR_DATAERROR); - if (!epnum) - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - if (ep->status[1] == USB_RET_STALL) { - ep->status[1] = 0; - - ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_RXSTALL; - } - - if (ep->status[1] == USB_RET_NAK) { - ep->status[1] = 0; - - /* NAK timeouts are only generated in Bulk transfers and - * Data-errors in Isochronous. */ - if (ep->interrupt[1]) { - musb_packet(s, ep, epnum, USB_TOKEN_IN, - packey->iov.size, musb_rx_packet_complete, 1); - return; - } - - ep->csr[1] |= MGC_M_RXCSR_DATAERROR; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_NAKTIMEOUT; - } - - if (ep->status[1] < 0) { - if (ep->status[1] == USB_RET_BABBLE) { - musb_intr_set(s, musb_irq_rst_babble, 1); - return; - } - - /* Pretend we've tried three times already and failed (in - * case of a control transfer). */ - ep->csr[1] |= MGC_M_RXCSR_H_ERROR; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_H_ERROR; - - musb_rx_intr_set(s, epnum, 1); - return; - } - /* TODO: check len for over/underruns of an OUT packet? */ - /* TODO: perhaps make use of e->ext_size[1] here. */ - - if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) { - ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - - ep->rxcount = ep->status[1]; /* XXX: MIN(packey->len, ep->maxp[1]); */ - /* In DMA mode: assert DMA request for this EP */ - } - - /* Only if DMA has not been asserted */ - musb_rx_intr_set(s, epnum, 1); -} - -static void musb_async_cancel_device(MUSBState *s, USBDevice *dev) -{ - int ep, dir; - - for (ep = 0; ep < 16; ep++) { - for (dir = 0; dir < 2; dir++) { - if (!usb_packet_is_inflight(&s->ep[ep].packey[dir].p) || - s->ep[ep].packey[dir].p.ep->dev != dev) { - continue; - } - usb_cancel_packet(&s->ep[ep].packey[dir].p); - /* status updates needed here? */ - } - } -} - -static void musb_tx_rdy(MUSBState *s, int epnum) -{ - MUSBEndPoint *ep = s->ep + epnum; - int pid; - int total, valid = 0; - TRACE("start %d, len %d", ep->fifostart[0], ep->fifolen[0] ); - ep->fifostart[0] += ep->fifolen[0]; - ep->fifolen[0] = 0; - - /* XXX: how's the total size of the packet retrieved exactly in - * the generic case? */ - total = ep->maxp[0] & 0x3ff; - - if (ep->ext_size[0]) { - total = ep->ext_size[0]; - ep->ext_size[0] = 0; - valid = 1; - } - - /* If the packet is not fully ready yet, wait for a next segment. */ - if (epnum && (ep->fifostart[0]) < total) - return; - - if (!valid) - total = ep->fifostart[0]; - - pid = USB_TOKEN_OUT; - if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) { - pid = USB_TOKEN_SETUP; - if (total != 8) { - TRACE("illegal SETUPPKT length of %i bytes", total); - } - /* Controller should retry SETUP packets three times on errors - * but it doesn't make sense for us to do that. */ - } - - musb_packet(s, ep, epnum, pid, total, musb_tx_packet_complete, 0); -} - -static void musb_rx_req(MUSBState *s, int epnum) -{ - MUSBEndPoint *ep = s->ep + epnum; - int total; - - /* If we already have a packet, which didn't fit into the - * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ - if (ep->packey[1].p.pid == USB_TOKEN_IN && ep->status[1] >= 0 && - (ep->fifostart[1]) + ep->rxcount < - ep->packey[1].p.iov.size) { - TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); - ep->fifostart[1] += ep->rxcount; - ep->fifolen[1] = 0; - - ep->rxcount = MIN(ep->packey[0].p.iov.size - (ep->fifostart[1]), - ep->maxp[1]); - - ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; - if (!epnum) - ep->csr[0] &= ~MGC_M_CSR0_H_REQPKT; - - /* Clear all of the error bits first */ - ep->csr[1] &= ~(MGC_M_RXCSR_H_ERROR | MGC_M_RXCSR_H_RXSTALL | - MGC_M_RXCSR_DATAERROR); - if (!epnum) - ep->csr[0] &= ~(MGC_M_CSR0_H_ERROR | MGC_M_CSR0_H_RXSTALL | - MGC_M_CSR0_H_NAKTIMEOUT | MGC_M_CSR0_H_NO_PING); - - ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY; - if (!epnum) - ep->csr[0] |= MGC_M_CSR0_RXPKTRDY; - musb_rx_intr_set(s, epnum, 1); - return; - } - - /* The driver sets maxp[1] to 64 or less because it knows the hardware - * FIFO is this deep. Bigger packets get split in - * usb_generic_handle_packet but we can also do the splitting locally - * for performance. It turns out we can also have a bigger FIFO and - * ignore the limit set in ep->maxp[1]. The Linux MUSB driver deals - * OK with single packets of even 32KB and we avoid splitting, however - * usb_msd.c sometimes sends a packet bigger than what Linux expects - * (e.g. 8192 bytes instead of 4096) and we get an OVERRUN. Splitting - * hides this overrun from Linux. Up to 4096 everything is fine - * though. Currently this is disabled. - * - * XXX: mind ep->fifosize. */ - total = MIN(ep->maxp[1] & 0x3ff, sizeof(s->buf)); - -#ifdef SETUPLEN_HACK - /* Why should *we* do that instead of Linux? */ - if (!epnum) { - if (ep->packey[0].p.devaddr == 2) { - total = MIN(s->setup_len, 8); - } else { - total = MIN(s->setup_len, 64); - } - s->setup_len -= total; - } -#endif - - musb_packet(s, ep, epnum, USB_TOKEN_IN, total, musb_rx_packet_complete, 1); -} - -static uint8_t musb_read_fifo(MUSBEndPoint *ep) -{ - uint8_t value; - if (ep->fifolen[1] >= 64) { - /* We have a FIFO underrun */ - TRACE("EP%d FIFO is now empty, stop reading", ep->epnum); - return 0x00000000; - } - /* In DMA mode clear RXPKTRDY and set REQPKT automatically - * (if AUTOREQ is set) */ - - ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL; - value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++]; - TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] ); - return value; -} - -static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value) -{ - TRACE("EP%d = %02x", ep->epnum, value); - if (ep->fifolen[0] >= 64) { - /* We have a FIFO overrun */ - TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum); - return; - } - - ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value; - ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY; -} - -static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir) -{ - if (ep->intv_timer[dir]) - timer_del(ep->intv_timer[dir]); -} - -/* Bus control */ -static uint8_t musb_busctl_readb(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - /* For USB2.0 HS hubs only */ - case MUSB_HDRC_TXHUBADDR: - return s->ep[ep].haddr[0]; - case MUSB_HDRC_TXHUBPORT: - return s->ep[ep].hport[0]; - case MUSB_HDRC_RXHUBADDR: - return s->ep[ep].haddr[1]; - case MUSB_HDRC_RXHUBPORT: - return s->ep[ep].hport[1]; - - default: - TRACE("unknown register 0x%02x", addr); - return 0x00; - }; -} - -static void musb_busctl_writeb(void *opaque, int ep, int addr, uint8_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - s->ep[ep].faddr[0] = value; - break; - case MUSB_HDRC_RXFUNCADDR: - s->ep[ep].faddr[1] = value; - break; - case MUSB_HDRC_TXHUBADDR: - s->ep[ep].haddr[0] = value; - break; - case MUSB_HDRC_TXHUBPORT: - s->ep[ep].hport[0] = value; - break; - case MUSB_HDRC_RXHUBADDR: - s->ep[ep].haddr[1] = value; - break; - case MUSB_HDRC_RXHUBPORT: - s->ep[ep].hport[1] = value; - break; - - default: - TRACE("unknown register 0x%02x", addr); - break; - }; -} - -static uint16_t musb_busctl_readh(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - return s->ep[ep].faddr[0]; - case MUSB_HDRC_RXFUNCADDR: - return s->ep[ep].faddr[1]; - - default: - return musb_busctl_readb(s, ep, addr) | - (musb_busctl_readb(s, ep, addr | 1) << 8); - }; -} - -static void musb_busctl_writeh(void *opaque, int ep, int addr, uint16_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXFUNCADDR: - s->ep[ep].faddr[0] = value; - break; - case MUSB_HDRC_RXFUNCADDR: - s->ep[ep].faddr[1] = value; - break; - - default: - musb_busctl_writeb(s, ep, addr, value & 0xff); - musb_busctl_writeb(s, ep, addr | 1, value >> 8); - }; -} - -/* Endpoint control */ -static uint8_t musb_ep_readb(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXTYPE: - return s->ep[ep].type[0]; - case MUSB_HDRC_TXINTERVAL: - return s->ep[ep].interval[0]; - case MUSB_HDRC_RXTYPE: - return s->ep[ep].type[1]; - case MUSB_HDRC_RXINTERVAL: - return s->ep[ep].interval[1]; - case (MUSB_HDRC_FIFOSIZE & ~1): - return 0x00; - case MUSB_HDRC_FIFOSIZE: - return ep ? s->ep[ep].fifosize : s->ep[ep].config; - case MUSB_HDRC_RXCOUNT: - return s->ep[ep].rxcount; - - default: - TRACE("unknown register 0x%02x", addr); - return 0x00; - }; -} - -static void musb_ep_writeb(void *opaque, int ep, int addr, uint8_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXTYPE: - s->ep[ep].type[0] = value; - break; - case MUSB_HDRC_TXINTERVAL: - s->ep[ep].interval[0] = value; - musb_ep_frame_cancel(&s->ep[ep], 0); - break; - case MUSB_HDRC_RXTYPE: - s->ep[ep].type[1] = value; - break; - case MUSB_HDRC_RXINTERVAL: - s->ep[ep].interval[1] = value; - musb_ep_frame_cancel(&s->ep[ep], 1); - break; - case (MUSB_HDRC_FIFOSIZE & ~1): - break; - case MUSB_HDRC_FIFOSIZE: - TRACE("somebody messes with fifosize (now %i bytes)", value); - s->ep[ep].fifosize = value; - break; - default: - TRACE("unknown register 0x%02x", addr); - break; - }; -} - -static uint16_t musb_ep_readh(void *opaque, int ep, int addr) -{ - MUSBState *s = (MUSBState *) opaque; - uint16_t ret; - - switch (addr) { - case MUSB_HDRC_TXMAXP: - return s->ep[ep].maxp[0]; - case MUSB_HDRC_TXCSR: - return s->ep[ep].csr[0]; - case MUSB_HDRC_RXMAXP: - return s->ep[ep].maxp[1]; - case MUSB_HDRC_RXCSR: - ret = s->ep[ep].csr[1]; - - /* TODO: This and other bits probably depend on - * ep->csr[1] & MGC_M_RXCSR_AUTOCLEAR. */ - if (s->ep[ep].csr[1] & MGC_M_RXCSR_AUTOCLEAR) - s->ep[ep].csr[1] &= ~MGC_M_RXCSR_RXPKTRDY; - - return ret; - case MUSB_HDRC_RXCOUNT: - return s->ep[ep].rxcount; - - default: - return musb_ep_readb(s, ep, addr) | - (musb_ep_readb(s, ep, addr | 1) << 8); - }; -} - -static void musb_ep_writeh(void *opaque, int ep, int addr, uint16_t value) -{ - MUSBState *s = (MUSBState *) opaque; - - switch (addr) { - case MUSB_HDRC_TXMAXP: - s->ep[ep].maxp[0] = value; - break; - case MUSB_HDRC_TXCSR: - if (ep) { - s->ep[ep].csr[0] &= value & 0xa6; - s->ep[ep].csr[0] |= value & 0xff59; - } else { - s->ep[ep].csr[0] &= value & 0x85; - s->ep[ep].csr[0] |= value & 0xf7a; - } - - musb_ep_frame_cancel(&s->ep[ep], 0); - - if ((ep && (value & MGC_M_TXCSR_FLUSHFIFO)) || - (!ep && (value & MGC_M_CSR0_FLUSHFIFO))) { - s->ep[ep].fifolen[0] = 0; - s->ep[ep].fifostart[0] = 0; - if (ep) - s->ep[ep].csr[0] &= - ~(MGC_M_TXCSR_FIFONOTEMPTY | MGC_M_TXCSR_TXPKTRDY); - else - s->ep[ep].csr[0] &= - ~(MGC_M_CSR0_TXPKTRDY | MGC_M_CSR0_RXPKTRDY); - } - if ( - (ep && -#ifdef CLEAR_NAK - (value & MGC_M_TXCSR_TXPKTRDY) && - !(value & MGC_M_TXCSR_H_NAKTIMEOUT)) || -#else - (value & MGC_M_TXCSR_TXPKTRDY)) || -#endif - (!ep && -#ifdef CLEAR_NAK - (value & MGC_M_CSR0_TXPKTRDY) && - !(value & MGC_M_CSR0_H_NAKTIMEOUT))) -#else - (value & MGC_M_CSR0_TXPKTRDY))) -#endif - musb_tx_rdy(s, ep); - if (!ep && - (value & MGC_M_CSR0_H_REQPKT) && -#ifdef CLEAR_NAK - !(value & (MGC_M_CSR0_H_NAKTIMEOUT | - MGC_M_CSR0_RXPKTRDY))) -#else - !(value & MGC_M_CSR0_RXPKTRDY)) -#endif - musb_rx_req(s, ep); - break; - - case MUSB_HDRC_RXMAXP: - s->ep[ep].maxp[1] = value; - break; - case MUSB_HDRC_RXCSR: - /* (DMA mode only) */ - if ( - (value & MGC_M_RXCSR_H_AUTOREQ) && - !(value & MGC_M_RXCSR_RXPKTRDY) && - (s->ep[ep].csr[1] & MGC_M_RXCSR_RXPKTRDY)) - value |= MGC_M_RXCSR_H_REQPKT; - - s->ep[ep].csr[1] &= 0x102 | (value & 0x4d); - s->ep[ep].csr[1] |= value & 0xfeb0; - - musb_ep_frame_cancel(&s->ep[ep], 1); - - if (value & MGC_M_RXCSR_FLUSHFIFO) { - s->ep[ep].fifolen[1] = 0; - s->ep[ep].fifostart[1] = 0; - s->ep[ep].csr[1] &= ~(MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY); - /* If double buffering and we have two packets ready, flush - * only the first one and set up the fifo at the second packet. */ - } -#ifdef CLEAR_NAK - if ((value & MGC_M_RXCSR_H_REQPKT) && !(value & MGC_M_RXCSR_DATAERROR)) -#else - if (value & MGC_M_RXCSR_H_REQPKT) -#endif - musb_rx_req(s, ep); - break; - case MUSB_HDRC_RXCOUNT: - s->ep[ep].rxcount = value; - break; - - default: - musb_ep_writeb(s, ep, addr, value & 0xff); - musb_ep_writeb(s, ep, addr | 1, value >> 8); - }; -} - -/* Generic control */ -static uint32_t musb_readb(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep, i; - uint8_t ret; - - switch (addr) { - case MUSB_HDRC_FADDR: - return s->faddr; - case MUSB_HDRC_POWER: - return s->power; - case MUSB_HDRC_INTRUSB: - ret = s->intr; - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRUSBE: - return s->mask; - case MUSB_HDRC_INDEX: - return s->idx; - case MUSB_HDRC_TESTMODE: - return 0x00; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - return musb_ep_readb(s, s->idx, addr & 0xf); - - case MUSB_HDRC_DEVCTL: - return s->devctl; - - case MUSB_HDRC_TXFIFOSZ: - case MUSB_HDRC_RXFIFOSZ: - case MUSB_HDRC_VCTRL: - /* TODO */ - return 0x00; - - case MUSB_HDRC_HWVERS: - return (1 << 10) | 400; - - case (MUSB_HDRC_VCTRL | 1): - case (MUSB_HDRC_HWVERS | 1): - case (MUSB_HDRC_DEVCTL | 1): - return 0x00; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - return musb_busctl_readb(s, ep, addr & 0x7); - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - return musb_ep_readb(s, ep, addr & 0xf); - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return musb_read_fifo(s->ep + ep); - - default: - TRACE("unknown register 0x%02x", (int) addr); - return 0x00; - }; -} - -static void musb_writeb(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FADDR: - s->faddr = value & 0x7f; - break; - case MUSB_HDRC_POWER: - s->power = (value & 0xef) | (s->power & 0x10); - /* MGC_M_POWER_RESET is also read-only in Peripheral Mode */ - if ((value & MGC_M_POWER_RESET) && s->port.dev) { - usb_device_reset(s->port.dev); - /* 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 */ - /* Restart frame counting. */ - } - if (value & MGC_M_POWER_SUSPENDM) { - /* When all transfers finish, suspend and if MGC_M_POWER_ENSUSPEND - * is set, also go into low power mode. Frame counting stops. */ - /* XXX: Cleared when the interrupt register is read */ - } - if (value & MGC_M_POWER_RESUME) { - /* Wait 20ms and signal resuming on the bus. Frame counting - * restarts. */ - } - break; - case MUSB_HDRC_INTRUSB: - break; - case MUSB_HDRC_INTRUSBE: - s->mask = value & 0xff; - break; - case MUSB_HDRC_INDEX: - s->idx = value & 0xf; - break; - case MUSB_HDRC_TESTMODE: - break; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - musb_ep_writeb(s, s->idx, addr & 0xf, value); - break; - - case MUSB_HDRC_DEVCTL: - s->session = !!(value & MGC_M_DEVCTL_SESSION); - musb_session_update(s, - !!s->port.dev, - !!(s->devctl & MGC_M_DEVCTL_SESSION)); - - /* It seems this is the only R/W bit in this register? */ - s->devctl &= ~MGC_M_DEVCTL_SESSION; - s->devctl |= value & MGC_M_DEVCTL_SESSION; - break; - - case MUSB_HDRC_TXFIFOSZ: - case MUSB_HDRC_RXFIFOSZ: - case MUSB_HDRC_VCTRL: - /* TODO */ - break; - - case (MUSB_HDRC_VCTRL | 1): - case (MUSB_HDRC_DEVCTL | 1): - break; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - musb_busctl_writeb(s, ep, addr & 0x7, value); - break; - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - musb_ep_writeb(s, ep, addr & 0xf, value); - break; - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - break; - - default: - TRACE("unknown register 0x%02x", (int) addr); - break; - }; -} - -static uint32_t musb_readh(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep, i; - uint16_t ret; - - switch (addr) { - case MUSB_HDRC_INTRTX: - ret = s->tx_intr; - /* Auto clear */ - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_tx_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRRX: - ret = s->rx_intr; - /* Auto clear */ - for (i = 0; i < sizeof(ret) * 8; i ++) - if (ret & (1 << i)) - musb_rx_intr_set(s, i, 0); - return ret; - case MUSB_HDRC_INTRTXE: - return s->tx_mask; - case MUSB_HDRC_INTRRXE: - return s->rx_mask; - - case MUSB_HDRC_FRAME: - /* TODO */ - return 0x0000; - case MUSB_HDRC_TXFIFOADDR: - return s->ep[s->idx].fifoaddr[0]; - case MUSB_HDRC_RXFIFOADDR: - return s->ep[s->idx].fifoaddr[1]; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - return musb_ep_readh(s, s->idx, addr & 0xf); - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - return musb_busctl_readh(s, ep, addr & 0x7); - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - return musb_ep_readh(s, ep, addr & 0xf); - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8); - - default: - return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8); - }; -} - -static void musb_writeh(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_INTRTXE: - s->tx_mask = value; - /* XXX: the masks seem to apply on the raising edge like with - * edge-triggered interrupts, thus no need to update. I may be - * wrong though. */ - break; - case MUSB_HDRC_INTRRXE: - s->rx_mask = value; - break; - - case MUSB_HDRC_FRAME: - /* TODO */ - break; - case MUSB_HDRC_TXFIFOADDR: - s->ep[s->idx].fifoaddr[0] = value; - s->ep[s->idx].buf[0] = - s->buf + ((value << 3) & 0x7ff ); - break; - case MUSB_HDRC_RXFIFOADDR: - s->ep[s->idx].fifoaddr[1] = value; - s->ep[s->idx].buf[1] = - s->buf + ((value << 3) & 0x7ff); - break; - - case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): - musb_ep_writeh(s, s->idx, addr & 0xf, value); - break; - - case MUSB_HDRC_BUSCTL ... (MUSB_HDRC_BUSCTL + 0x7f): - ep = (addr >> 3) & 0xf; - musb_busctl_writeh(s, ep, addr & 0x7, value); - break; - - case MUSB_HDRC_EP ... (MUSB_HDRC_EP + 0xff): - ep = (addr >> 4) & 0xf; - musb_ep_writeh(s, ep, addr & 0xf, value); - break; - - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - musb_write_fifo(s->ep + ep, (value >> 8) & 0xff); - break; - - default: - musb_writeb(s, addr, value & 0xff); - musb_writeb(s, addr | 1, value >> 8); - }; -} - -static uint32_t musb_readw(void *opaque, hwaddr addr) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - return ( musb_read_fifo(s->ep + ep) | - musb_read_fifo(s->ep + ep) << 8 | - musb_read_fifo(s->ep + ep) << 16 | - musb_read_fifo(s->ep + ep) << 24 ); - default: - TRACE("unknown register 0x%02x", (int) addr); - return 0x00000000; - }; -} - -static void musb_writew(void *opaque, hwaddr addr, uint32_t value) -{ - MUSBState *s = (MUSBState *) opaque; - int ep; - - switch (addr) { - case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - musb_write_fifo(s->ep + ep, value & 0xff); - musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff); - musb_write_fifo(s->ep + ep, (value >> 16) & 0xff); - musb_write_fifo(s->ep + ep, (value >> 24) & 0xff); - break; - default: - TRACE("unknown register 0x%02x", (int) addr); - break; - }; -} - -MUSBReadFunc * const musb_read[] = { - musb_readb, - musb_readh, - musb_readw, -}; - -MUSBWriteFunc * const musb_write[] = { - musb_writeb, - musb_writeh, - musb_writew, -}; diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 5e12e2f81e..a0d772feca 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() @@ -152,7 +152,7 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, ohci_pci_properties); dc->hotpluggable = false; dc->vmsd = &vmstate_ohci; - dc->reset = usb_ohci_reset_pci; + device_class_set_legacy_reset(dc, usb_ohci_reset_pci); } static const TypeInfo ohci_pci_info = { diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c new file mode 100644 index 0000000000..313e1e71bb --- /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); + device_class_set_legacy_reset(dc, 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 8306824961..0c2cde7188 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; @@ -584,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, @@ -601,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)) { @@ -615,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; @@ -655,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; @@ -801,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); @@ -845,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; @@ -869,7 +892,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) return 1; } - /* See if this TD has already been submitted to the device. */ + /* 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(); @@ -885,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); @@ -904,6 +927,11 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) case OHCI_TD_DIR_SETUP: str = "setup"; pid = USB_TOKEN_SETUP; + if (OHCI_BM(ed->flags, ED_EN) > 0) { /* setup only allowed to ep 0 */ + trace_usb_ohci_td_bad_pid(str, ed->flags, td.flags); + ohci_die(ohci); + return 1; + } break; default: trace_usb_ohci_td_bad_direction(dir); @@ -913,8 +941,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) { len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff); } else { - if (td.cbp > td.be) { - trace_usb_ohci_iso_td_bad_cc_overrun(td.cbp, td.be); + if (td.cbp - 1 > td.be) { /* rely on td.cbp != 0 */ + trace_usb_ohci_td_bad_buf(td.cbp, td.be); ohci_die(ohci); return 1; } @@ -956,11 +984,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; } @@ -1004,7 +1033,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; } @@ -1020,8 +1049,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(); @@ -1050,8 +1080,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; @@ -1063,10 +1095,12 @@ 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; - if (OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR) + } + if (OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR) { ohci->done_count = 0; + } exit_no_retire: if (ohci_put_td(ohci, addr, &td)) { ohci_die(ohci); @@ -1075,7 +1109,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; @@ -1085,9 +1119,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); @@ -1099,7 +1133,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); @@ -1116,15 +1150,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)) { @@ -1155,7 +1190,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)) { @@ -1196,7 +1231,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); } @@ -1218,19 +1253,21 @@ static void ohci_frame_boundary(void *opaque) 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); @@ -1240,18 +1277,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); @@ -1265,39 +1301,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; @@ -1314,10 +1318,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); } } @@ -1332,9 +1334,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: @@ -1360,21 +1362,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); @@ -1390,33 +1390,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 */ @@ -1429,12 +1477,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)) { @@ -1445,20 +1493,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, @@ -1475,6 +1523,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 */ @@ -1579,6 +1629,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; @@ -1599,10 +1653,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); @@ -1615,8 +1672,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 */ @@ -1694,8 +1752,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 */ @@ -1796,11 +1855,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. @@ -1833,7 +1888,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; @@ -1865,7 +1920,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, @@ -1898,7 +1953,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. */ @@ -1910,36 +1965,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() }, @@ -1957,7 +1987,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() }, @@ -1967,7 +1997,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), @@ -2004,41 +2034,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..3d0339af7b 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, @@ -1248,7 +1247,7 @@ static void uhci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; dc->vmsd = &vmstate_uhci; - dc->reset = uhci_reset; + device_class_set_legacy_reset(dc, uhci_reset); set_bit(DEVICE_CATEGORY_USB, dc->categories); } @@ -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..0c063b3697 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -27,22 +27,20 @@ #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), DEFINE_PROP_ON_OFF_AUTO("msix", XHCIPciState, msix, ON_OFF_AUTO_AUTO), - DEFINE_PROP_BIT("superspeed-ports-first", XHCINecState, flags, - XHCI_FLAG_SS_FIRST, true), - DEFINE_PROP_BIT("force-pcie-endcap", XHCINecState, flags, - XHCI_FLAG_FORCE_PCIE_ENDCAP, false), DEFINE_PROP_UINT32("intrs", XHCINecState, intrs, XHCI_MAXINTRS), DEFINE_PROP_UINT32("slots", XHCINecState, slots, XHCI_MAXSLOTS), DEFINE_PROP_END_OF_LIST(), @@ -51,7 +49,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 643d4643e4..a039f5778a 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -148,8 +148,7 @@ static void usb_xhci_pci_realize(struct PCIDevice *dev, Error **errp) PCI_BASE_ADDRESS_MEM_TYPE_64, &s->xhci.mem); - if (pci_bus_is_express(pci_get_bus(dev)) || - xhci_get_flag(&s->xhci, XHCI_FLAG_FORCE_PCIE_ENDCAP)) { + if (pci_bus_is_express(pci_get_bus(dev))) { ret = pcie_endpoint_cap_init(dev, 0xa0); assert(ret > 0); } @@ -178,7 +177,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), @@ -203,7 +202,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xhci_pci_reset; + device_class_set_legacy_reset(dc, xhci_pci_reset); dc->vmsd = &vmstate_xhci_pci; set_bit(DEVICE_CATEGORY_USB, dc->categories); k->realize = usb_xhci_pci_realize; @@ -243,7 +242,6 @@ static void qemu_xhci_instance_init(Object *obj) s->msix = ON_OFF_AUTO_AUTO; xhci->numintrs = XHCI_MAXINTRS; xhci->numslots = XHCI_MAXSLOTS; - xhci_set_flag(xhci, XHCI_FLAG_SS_FIRST); } static const TypeInfo qemu_xhci_info = { 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 faf57b4797..59cf7fd4ab 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -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() } @@ -101,7 +101,7 @@ static void xhci_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = xhci_sysbus_reset; + device_class_set_legacy_reset(dc, xhci_sysbus_reset); dc->realize = xhci_sysbus_realize; dc->vmsd = &vmstate_xhci_sysbus; device_class_set_props(dc, xhci_sysbus_props); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index b89b618ec2..d85adaca0d 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -217,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 { @@ -541,18 +541,10 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index + xhci->numports_3; - } else { - index = uport->index; - } + index = uport->index + xhci->numports_3; break; case USB_SPEED_SUPER: - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - index = uport->index; - } else { - index = uport->index + xhci->numports_2; - } + index = uport->index; break; default: return NULL; @@ -1894,7 +1886,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); @@ -2434,7 +2426,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"); @@ -2442,11 +2433,10 @@ 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% */ - if (dma_memory_write(xhci->as, ctx, bw_ctx, sizeof(bw_ctx), - MEMTXATTRS_UNSPECIFIED) != MEMTX_OK) { + /* 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; @@ -2781,11 +2771,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x20425355; /* "USB " */ break; case 0x28: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_2<<8) | (xhci->numports_3+1); - } else { - ret = (xhci->numports_2<<8) | 1; - } + ret = (xhci->numports_2 << 8) | (xhci->numports_3 + 1); break; case 0x2c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -2797,11 +2783,7 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size) ret = 0x20425355; /* "USB " */ break; case 0x38: /* Supported Protocol:08 */ - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - ret = (xhci->numports_3<<8) | 1; - } else { - ret = (xhci->numports_3<<8) | (xhci->numports_2+1); - } + ret = (xhci->numports_3 << 8) | 1; break; case 0x3c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -3351,13 +3333,8 @@ static void usb_xhci_init(XHCIState *xhci) for (i = 0; i < usbports; i++) { speedmask = 0; if (i < xhci->numports_2) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i + xhci->numports_3]; - port->portnr = i + 1 + xhci->numports_3; - } else { - port = &xhci->ports[i]; - port->portnr = i + 1; - } + port = &xhci->ports[i + xhci->numports_3]; + port->portnr = i + 1 + xhci->numports_3; port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_LOW | @@ -3368,13 +3345,8 @@ static void usb_xhci_init(XHCIState *xhci) speedmask |= port->speedmask; } if (i < xhci->numports_3) { - if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) { - port = &xhci->ports[i]; - port->portnr = i + 1; - } else { - port = &xhci->ports[i + xhci->numports_2]; - port->portnr = i + 1 + xhci->numports_2; - } + port = &xhci->ports[i]; + port->portnr = i + 1; port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_SUPER; assert(i < XHCI_MAXPORTS); @@ -3524,7 +3496,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() @@ -3534,7 +3506,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() } @@ -3543,7 +3515,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() @@ -3553,7 +3525,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), @@ -3573,7 +3545,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), @@ -3606,7 +3578,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, @@ -3649,7 +3621,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->realize = usb_xhci_realize; dc->unrealize = usb_xhci_unrealize; - dc->reset = xhci_reset; + device_class_set_legacy_reset(dc, xhci_reset); device_class_set_props(dc, xhci_properties); dc->user_creatable = false; } diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index 98f598382a..fe16d7ad05 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -36,9 +36,7 @@ typedef struct XHCIStreamContext XHCIStreamContext; typedef struct XHCIEPContext XHCIEPContext; enum xhci_flags { - XHCI_FLAG_SS_FIRST = 1, - XHCI_FLAG_FORCE_PCIE_ENDCAP, - XHCI_FLAG_ENABLE_STREAMS, + XHCI_FLAG_ENABLE_STREAMS = 1, }; typedef enum TRBType { diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 176868d345..691bc881fb 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); } @@ -1211,9 +1212,8 @@ static void usb_host_realize(USBDevice *udev, Error **errp) if (s->hostdevice) { int fd; s->needs_autoscan = false; - fd = qemu_open_old(s->hostdevice, O_RDWR); + fd = qemu_open(s->hostdevice, O_RDWR, errp); if (fd < 0) { - error_setg_errno(errp, errno, "failed to open %s", s->hostdevice); return; } rc = usb_host_open(s, NULL, fd); @@ -1739,7 +1739,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 +1752,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() } diff --git a/hw/usb/host.h b/hw/usb/host.h deleted file mode 100644 index 048ff3b482..0000000000 --- a/hw/usb/host.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Linux host USB redirector - * - * Copyright (c) 2005 Fabrice Bellard - * - * Copyright (c) 2008 Max Krasnyansky - * Support for host device auto connect & disconnect - * Major rewrite to support fully async operation - * - * Copyright 2008 TJ - * Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition - * to the legacy /proc/bus/usb USB device discovery and handling - * - * 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_USB_HOST_H -#define QEMU_USB_HOST_H - -struct USBAutoFilter { - uint32_t bus_num; - uint32_t addr; - char *port; - uint32_t vendor_id; - uint32_t product_id; -}; - -#endif /* QEMU_USB_HOST_H */ diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c index 5d7a549e34..f519250567 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; } } @@ -203,7 +218,7 @@ static void imx_usbphy_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = imx_usbphy_reset; + device_class_set_legacy_reset(dc, imx_usbphy_reset); dc->vmsd = &vmstate_imx_usbphy; dc->desc = "i.MX USB PHY Module"; dc->realize = imx_usbphy_realize; diff --git a/hw/usb/meson.build b/hw/usb/meson.build index 793df42e21..1b4d1507e4 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', @@ -9,45 +9,46 @@ softmmu_ss.add(when: 'CONFIG_USB', if_true: files( 'desc-msos.c', 'libhw.c', 'pcap.c', -)) +), if_false: files('bus-stub.c')) # 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_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_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_HUB', if_true: files('dev-hub.c')) +system_ss.add(when: 'CONFIG_USB_HID', 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,15 +58,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() - softmmu_ss.add(when: 'CONFIG_USB_CANOKEY', if_true: [canokey, files('canokey.c')]) + system_ss.add(when: 'CONFIG_USB_CANOKEY', if_true: [canokey, files('canokey.c')]) endif # usb redirect @@ -84,6 +87,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.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 b65269892c..dd04f14add 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -15,7 +15,7 @@ usb_ohci_exit(const char *s) "%s" # hcd-ohci.c usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at 0x%x" -usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count 0x%.8x relative %d" +usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x, flags 0x%.8x bp 0x%.8x next 0x%.8x be 0x%.8x, frame_number 0x%.8x starting_frame 0x%.8x, frame_count 0x%.8x relative %d" usb_ohci_iso_td_head_offset(uint32_t o0, uint32_t o1, uint32_t o2, uint32_t o3, uint32_t o4, uint32_t o5, uint32_t o6, uint32_t o7) "0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x" usb_ohci_iso_td_relative_frame_number_neg(int rel) "ISO_TD R=%d < 0" usb_ohci_iso_td_relative_frame_number_big(int rel, int count) "ISO_TD R=%d > FC=%d" @@ -23,11 +23,13 @@ usb_ohci_iso_td_bad_direction(int dir) "Bad direction %d" usb_ohci_iso_td_bad_bp_be(uint32_t bp, uint32_t be) "ISO_TD bp 0x%.8x be 0x%.8x" usb_ohci_iso_td_bad_cc_not_accessed(uint32_t start, uint32_t next) "ISO_TD cc != not accessed 0x%.8x 0x%.8x" usb_ohci_iso_td_bad_cc_overrun(uint32_t start, uint32_t next) "ISO_TD start_offset=0x%.8x > next_offset=0x%.8x" -usb_ohci_iso_td_so(uint32_t so, uint32_t eo, uint32_t s, uint32_t e, const char *str, ssize_t len, int ret) "0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d" +usb_ohci_iso_td_so(uint32_t so, uint32_t eo, uint32_t s, uint32_t e, const char *str, ssize_t len, int ret) "0x%.8x eo 0x%.8x sa 0x%.8x ea 0x%.8x dir %s len %zu ret %d" usb_ohci_iso_td_data_overrun(int ret, ssize_t len) "DataOverrun %d > %zu" usb_ohci_iso_td_data_underrun(int ret) "DataUnderrun %d" usb_ohci_iso_td_nak(int ret) "got NAK/STALL %d" usb_ohci_iso_td_bad_response(int ret) "Bad device response %d" +usb_ohci_td_bad_pid(const char *s, uint32_t edf, uint32_t tdf) "Bad pid %s: ed.flags 0x%x td.flags 0x%x" +usb_ohci_td_bad_buf(uint32_t cbp, uint32_t be) "Bad cbp = 0x%x > be = 0x%x" usb_ohci_port_attach(int index) "port #%d" usb_ohci_port_detach(int index) "port #%d" usb_ohci_port_wakeup(int index) "port #%d" @@ -54,11 +56,15 @@ usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s" usb_ohci_td_too_many_pending(int ep) "ep=%d" usb_ohci_td_packet_status(int status) "status=%d" usb_ohci_ed_read_error(uint32_t addr) "ED read error at 0x%x" -usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x" +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 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 +250,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" diff --git a/hw/usb/tusb6010.c b/hw/usb/tusb6010.c deleted file mode 100644 index 1dd4071e68..0000000000 --- a/hw/usb/tusb6010.c +++ /dev/null @@ -1,850 +0,0 @@ -/* - * Texas Instruments TUSB6010 emulation. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/module.h" -#include "qemu/timer.h" -#include "hw/usb.h" -#include "hw/usb/hcd-musb.h" -#include "hw/arm/omap.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/sysbus.h" -#include "qom/object.h" - -#define TYPE_TUSB6010 "tusb6010" -OBJECT_DECLARE_SIMPLE_TYPE(TUSBState, TUSB6010) - -struct TUSBState { - SysBusDevice parent_obj; - - MemoryRegion iomem[2]; - qemu_irq irq; - MUSBState *musb; - QEMUTimer *otg_timer; - QEMUTimer *pwr_timer; - - int power; - uint32_t scratch; - uint16_t test_reset; - uint32_t prcm_config; - uint32_t prcm_mngmt; - uint16_t otg_status; - uint32_t dev_config; - int host_mode; - uint32_t intr; - uint32_t intr_ok; - uint32_t mask; - uint32_t usbip_intr; - uint32_t usbip_mask; - uint32_t gpio_intr; - uint32_t gpio_mask; - uint32_t gpio_config; - uint32_t dma_intr; - uint32_t dma_mask; - uint32_t dma_map; - uint32_t dma_config; - uint32_t ep0_config; - uint32_t rx_config[15]; - uint32_t tx_config[15]; - uint32_t wkup_mask; - uint32_t pullup[2]; - uint32_t control_config; - uint32_t otg_timer_val; -}; - -#define TUSB_DEVCLOCK 60000000 /* 60 MHz */ - -#define TUSB_VLYNQ_CTRL 0x004 - -/* Mentor Graphics OTG core registers. */ -#define TUSB_BASE_OFFSET 0x400 - -/* FIFO registers, 32-bit. */ -#define TUSB_FIFO_BASE 0x600 - -/* Device System & Control registers, 32-bit. */ -#define TUSB_SYS_REG_BASE 0x800 - -#define TUSB_DEV_CONF (TUSB_SYS_REG_BASE + 0x000) -#define TUSB_DEV_CONF_USB_HOST_MODE (1 << 16) -#define TUSB_DEV_CONF_PROD_TEST_MODE (1 << 15) -#define TUSB_DEV_CONF_SOFT_ID (1 << 1) -#define TUSB_DEV_CONF_ID_SEL (1 << 0) - -#define TUSB_PHY_OTG_CTRL_ENABLE (TUSB_SYS_REG_BASE + 0x004) -#define TUSB_PHY_OTG_CTRL (TUSB_SYS_REG_BASE + 0x008) -#define TUSB_PHY_OTG_CTRL_WRPROTECT (0xa5 << 24) -#define TUSB_PHY_OTG_CTRL_O_ID_PULLUP (1 << 23) -#define TUSB_PHY_OTG_CTRL_O_VBUS_DET_EN (1 << 19) -#define TUSB_PHY_OTG_CTRL_O_SESS_END_EN (1 << 18) -#define TUSB_PHY_OTG_CTRL_TESTM2 (1 << 17) -#define TUSB_PHY_OTG_CTRL_TESTM1 (1 << 16) -#define TUSB_PHY_OTG_CTRL_TESTM0 (1 << 15) -#define TUSB_PHY_OTG_CTRL_TX_DATA2 (1 << 14) -#define TUSB_PHY_OTG_CTRL_TX_GZ2 (1 << 13) -#define TUSB_PHY_OTG_CTRL_TX_ENABLE2 (1 << 12) -#define TUSB_PHY_OTG_CTRL_DM_PULLDOWN (1 << 11) -#define TUSB_PHY_OTG_CTRL_DP_PULLDOWN (1 << 10) -#define TUSB_PHY_OTG_CTRL_OSC_EN (1 << 9) -#define TUSB_PHY_OTG_CTRL_PHYREF_CLK(v) (((v) & 3) << 7) -#define TUSB_PHY_OTG_CTRL_PD (1 << 6) -#define TUSB_PHY_OTG_CTRL_PLL_ON (1 << 5) -#define TUSB_PHY_OTG_CTRL_EXT_RPU (1 << 4) -#define TUSB_PHY_OTG_CTRL_PWR_GOOD (1 << 3) -#define TUSB_PHY_OTG_CTRL_RESET (1 << 2) -#define TUSB_PHY_OTG_CTRL_SUSPENDM (1 << 1) -#define TUSB_PHY_OTG_CTRL_CLK_MODE (1 << 0) - -/* OTG status register */ -#define TUSB_DEV_OTG_STAT (TUSB_SYS_REG_BASE + 0x00c) -#define TUSB_DEV_OTG_STAT_PWR_CLK_GOOD (1 << 8) -#define TUSB_DEV_OTG_STAT_SESS_END (1 << 7) -#define TUSB_DEV_OTG_STAT_SESS_VALID (1 << 6) -#define TUSB_DEV_OTG_STAT_VBUS_VALID (1 << 5) -#define TUSB_DEV_OTG_STAT_VBUS_SENSE (1 << 4) -#define TUSB_DEV_OTG_STAT_ID_STATUS (1 << 3) -#define TUSB_DEV_OTG_STAT_HOST_DISCON (1 << 2) -#define TUSB_DEV_OTG_STAT_LINE_STATE (3 << 0) -#define TUSB_DEV_OTG_STAT_DP_ENABLE (1 << 1) -#define TUSB_DEV_OTG_STAT_DM_ENABLE (1 << 0) - -#define TUSB_DEV_OTG_TIMER (TUSB_SYS_REG_BASE + 0x010) -#define TUSB_DEV_OTG_TIMER_ENABLE (1 << 31) -#define TUSB_DEV_OTG_TIMER_VAL(v) ((v) & 0x07ffffff) -#define TUSB_PRCM_REV (TUSB_SYS_REG_BASE + 0x014) - -/* PRCM configuration register */ -#define TUSB_PRCM_CONF (TUSB_SYS_REG_BASE + 0x018) -#define TUSB_PRCM_CONF_SFW_CPEN (1 << 24) -#define TUSB_PRCM_CONF_SYS_CLKSEL(v) (((v) & 3) << 16) - -/* PRCM management register */ -#define TUSB_PRCM_MNGMT (TUSB_SYS_REG_BASE + 0x01c) -#define TUSB_PRCM_MNGMT_SRP_FIX_TMR(v) (((v) & 0xf) << 25) -#define TUSB_PRCM_MNGMT_SRP_FIX_EN (1 << 24) -#define TUSB_PRCM_MNGMT_VBUS_VAL_TMR(v) (((v) & 0xf) << 20) -#define TUSB_PRCM_MNGMT_VBUS_VAL_FLT_EN (1 << 19) -#define TUSB_PRCM_MNGMT_DFT_CLK_DIS (1 << 18) -#define TUSB_PRCM_MNGMT_VLYNQ_CLK_DIS (1 << 17) -#define TUSB_PRCM_MNGMT_OTG_SESS_END_EN (1 << 10) -#define TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN (1 << 9) -#define TUSB_PRCM_MNGMT_OTG_ID_PULLUP (1 << 8) -#define TUSB_PRCM_MNGMT_15_SW_EN (1 << 4) -#define TUSB_PRCM_MNGMT_33_SW_EN (1 << 3) -#define TUSB_PRCM_MNGMT_5V_CPEN (1 << 2) -#define TUSB_PRCM_MNGMT_PM_IDLE (1 << 1) -#define TUSB_PRCM_MNGMT_DEV_IDLE (1 << 0) - -/* Wake-up source clear and mask registers */ -#define TUSB_PRCM_WAKEUP_SOURCE (TUSB_SYS_REG_BASE + 0x020) -#define TUSB_PRCM_WAKEUP_CLEAR (TUSB_SYS_REG_BASE + 0x028) -#define TUSB_PRCM_WAKEUP_MASK (TUSB_SYS_REG_BASE + 0x02c) -#define TUSB_PRCM_WAKEUP_RESERVED_BITS (0xffffe << 13) -#define TUSB_PRCM_WGPIO_7 (1 << 12) -#define TUSB_PRCM_WGPIO_6 (1 << 11) -#define TUSB_PRCM_WGPIO_5 (1 << 10) -#define TUSB_PRCM_WGPIO_4 (1 << 9) -#define TUSB_PRCM_WGPIO_3 (1 << 8) -#define TUSB_PRCM_WGPIO_2 (1 << 7) -#define TUSB_PRCM_WGPIO_1 (1 << 6) -#define TUSB_PRCM_WGPIO_0 (1 << 5) -#define TUSB_PRCM_WHOSTDISCON (1 << 4) /* Host disconnect */ -#define TUSB_PRCM_WBUS (1 << 3) /* USB bus resume */ -#define TUSB_PRCM_WNORCS (1 << 2) /* NOR chip select */ -#define TUSB_PRCM_WVBUS (1 << 1) /* OTG PHY VBUS */ -#define TUSB_PRCM_WID (1 << 0) /* OTG PHY ID detect */ - -#define TUSB_PULLUP_1_CTRL (TUSB_SYS_REG_BASE + 0x030) -#define TUSB_PULLUP_2_CTRL (TUSB_SYS_REG_BASE + 0x034) -#define TUSB_INT_CTRL_REV (TUSB_SYS_REG_BASE + 0x038) -#define TUSB_INT_CTRL_CONF (TUSB_SYS_REG_BASE + 0x03c) -#define TUSB_USBIP_INT_SRC (TUSB_SYS_REG_BASE + 0x040) -#define TUSB_USBIP_INT_SET (TUSB_SYS_REG_BASE + 0x044) -#define TUSB_USBIP_INT_CLEAR (TUSB_SYS_REG_BASE + 0x048) -#define TUSB_USBIP_INT_MASK (TUSB_SYS_REG_BASE + 0x04c) -#define TUSB_DMA_INT_SRC (TUSB_SYS_REG_BASE + 0x050) -#define TUSB_DMA_INT_SET (TUSB_SYS_REG_BASE + 0x054) -#define TUSB_DMA_INT_CLEAR (TUSB_SYS_REG_BASE + 0x058) -#define TUSB_DMA_INT_MASK (TUSB_SYS_REG_BASE + 0x05c) -#define TUSB_GPIO_INT_SRC (TUSB_SYS_REG_BASE + 0x060) -#define TUSB_GPIO_INT_SET (TUSB_SYS_REG_BASE + 0x064) -#define TUSB_GPIO_INT_CLEAR (TUSB_SYS_REG_BASE + 0x068) -#define TUSB_GPIO_INT_MASK (TUSB_SYS_REG_BASE + 0x06c) - -/* NOR flash interrupt source registers */ -#define TUSB_INT_SRC (TUSB_SYS_REG_BASE + 0x070) -#define TUSB_INT_SRC_SET (TUSB_SYS_REG_BASE + 0x074) -#define TUSB_INT_SRC_CLEAR (TUSB_SYS_REG_BASE + 0x078) -#define TUSB_INT_MASK (TUSB_SYS_REG_BASE + 0x07c) -#define TUSB_INT_SRC_TXRX_DMA_DONE (1 << 24) -#define TUSB_INT_SRC_USB_IP_CORE (1 << 17) -#define TUSB_INT_SRC_OTG_TIMEOUT (1 << 16) -#define TUSB_INT_SRC_VBUS_SENSE_CHNG (1 << 15) -#define TUSB_INT_SRC_ID_STATUS_CHNG (1 << 14) -#define TUSB_INT_SRC_DEV_WAKEUP (1 << 13) -#define TUSB_INT_SRC_DEV_READY (1 << 12) -#define TUSB_INT_SRC_USB_IP_TX (1 << 9) -#define TUSB_INT_SRC_USB_IP_RX (1 << 8) -#define TUSB_INT_SRC_USB_IP_VBUS_ERR (1 << 7) -#define TUSB_INT_SRC_USB_IP_VBUS_REQ (1 << 6) -#define TUSB_INT_SRC_USB_IP_DISCON (1 << 5) -#define TUSB_INT_SRC_USB_IP_CONN (1 << 4) -#define TUSB_INT_SRC_USB_IP_SOF (1 << 3) -#define TUSB_INT_SRC_USB_IP_RST_BABBLE (1 << 2) -#define TUSB_INT_SRC_USB_IP_RESUME (1 << 1) -#define TUSB_INT_SRC_USB_IP_SUSPEND (1 << 0) - -#define TUSB_GPIO_REV (TUSB_SYS_REG_BASE + 0x080) -#define TUSB_GPIO_CONF (TUSB_SYS_REG_BASE + 0x084) -#define TUSB_DMA_CTRL_REV (TUSB_SYS_REG_BASE + 0x100) -#define TUSB_DMA_REQ_CONF (TUSB_SYS_REG_BASE + 0x104) -#define TUSB_EP0_CONF (TUSB_SYS_REG_BASE + 0x108) -#define TUSB_EP_IN_SIZE (TUSB_SYS_REG_BASE + 0x10c) -#define TUSB_DMA_EP_MAP (TUSB_SYS_REG_BASE + 0x148) -#define TUSB_EP_OUT_SIZE (TUSB_SYS_REG_BASE + 0x14c) -#define TUSB_EP_MAX_PACKET_SIZE_OFFSET (TUSB_SYS_REG_BASE + 0x188) -#define TUSB_SCRATCH_PAD (TUSB_SYS_REG_BASE + 0x1c4) -#define TUSB_WAIT_COUNT (TUSB_SYS_REG_BASE + 0x1c8) -#define TUSB_PROD_TEST_RESET (TUSB_SYS_REG_BASE + 0x1d8) - -#define TUSB_DIDR1_LO (TUSB_SYS_REG_BASE + 0x1f8) -#define TUSB_DIDR1_HI (TUSB_SYS_REG_BASE + 0x1fc) - -/* Device System & Control register bitfields */ -#define TUSB_INT_CTRL_CONF_INT_RLCYC(v) (((v) & 0x7) << 18) -#define TUSB_INT_CTRL_CONF_INT_POLARITY (1 << 17) -#define TUSB_INT_CTRL_CONF_INT_MODE (1 << 16) -#define TUSB_GPIO_CONF_DMAREQ(v) (((v) & 0x3f) << 24) -#define TUSB_DMA_REQ_CONF_BURST_SIZE(v) (((v) & 3) << 26) -#define TUSB_DMA_REQ_CONF_DMA_RQ_EN(v) (((v) & 0x3f) << 20) -#define TUSB_DMA_REQ_CONF_DMA_RQ_ASR(v) (((v) & 0xf) << 16) -#define TUSB_EP0_CONFIG_SW_EN (1 << 8) -#define TUSB_EP0_CONFIG_DIR_TX (1 << 7) -#define TUSB_EP0_CONFIG_XFR_SIZE(v) ((v) & 0x7f) -#define TUSB_EP_CONFIG_SW_EN (1 << 31) -#define TUSB_EP_CONFIG_XFR_SIZE(v) ((v) & 0x7fffffff) -#define TUSB_PROD_TEST_RESET_VAL 0xa596 - -static void tusb_intr_update(TUSBState *s) -{ - if (s->control_config & TUSB_INT_CTRL_CONF_INT_POLARITY) - qemu_set_irq(s->irq, s->intr & ~s->mask & s->intr_ok); - else - qemu_set_irq(s->irq, (!(s->intr & ~s->mask)) & s->intr_ok); -} - -static void tusb_usbip_intr_update(TUSBState *s) -{ - /* TX interrupt in the MUSB */ - if (s->usbip_intr & 0x0000ffff & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_TX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_TX; - - /* RX interrupt in the MUSB */ - if (s->usbip_intr & 0xffff0000 & ~s->usbip_mask) - s->intr |= TUSB_INT_SRC_USB_IP_RX; - else - s->intr &= ~TUSB_INT_SRC_USB_IP_RX; - - /* XXX: What about TUSB_INT_SRC_USB_IP_CORE? */ - - tusb_intr_update(s); -} - -static void tusb_dma_intr_update(TUSBState *s) -{ - if (s->dma_intr & ~s->dma_mask) - s->intr |= TUSB_INT_SRC_TXRX_DMA_DONE; - else - s->intr &= ~TUSB_INT_SRC_TXRX_DMA_DONE; - - tusb_intr_update(s); -} - -static void tusb_gpio_intr_update(TUSBState *s) -{ - /* TODO: How is this signalled? */ -} - -static uint32_t tusb_async_readb(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[0](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[0](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __func__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readh(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[1](s->musb, addr & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[1](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - } - - printf("%s: unknown register at %03x\n", - __func__, (int) (addr & 0xfff)); - return 0; -} - -static uint32_t tusb_async_readw(void *opaque, hwaddr addr) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - uint32_t ret; - - switch (offset) { - case TUSB_DEV_CONF: - return s->dev_config; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - return musb_read[2](s->musb, offset & 0x1ff); - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - return musb_read[2](s->musb, 0x20 + ((addr >> 3) & 0x3c)); - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return 0x00; /* TODO */ - - case TUSB_DEV_OTG_STAT: - ret = s->otg_status; -#if 0 - if (!(s->prcm_mngmt & TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN)) - ret &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; -#endif - return ret; - case TUSB_DEV_OTG_TIMER: - return s->otg_timer_val; - - case TUSB_PRCM_REV: - return 0x20; - case TUSB_PRCM_CONF: - return s->prcm_config; - case TUSB_PRCM_MNGMT: - return s->prcm_mngmt; - case TUSB_PRCM_WAKEUP_SOURCE: - case TUSB_PRCM_WAKEUP_CLEAR: /* TODO: What does this one return? */ - return 0x00000000; - case TUSB_PRCM_WAKEUP_MASK: - return s->wkup_mask; - - case TUSB_PULLUP_1_CTRL: - return s->pullup[0]; - case TUSB_PULLUP_2_CTRL: - return s->pullup[1]; - - case TUSB_INT_CTRL_REV: - return 0x20; - case TUSB_INT_CTRL_CONF: - return s->control_config; - - case TUSB_USBIP_INT_SRC: - case TUSB_USBIP_INT_SET: /* TODO: What do these two return? */ - case TUSB_USBIP_INT_CLEAR: - return s->usbip_intr; - case TUSB_USBIP_INT_MASK: - return s->usbip_mask; - - case TUSB_DMA_INT_SRC: - case TUSB_DMA_INT_SET: /* TODO: What do these two return? */ - case TUSB_DMA_INT_CLEAR: - return s->dma_intr; - case TUSB_DMA_INT_MASK: - return s->dma_mask; - - case TUSB_GPIO_INT_SRC: /* TODO: What do these two return? */ - case TUSB_GPIO_INT_SET: - case TUSB_GPIO_INT_CLEAR: - return s->gpio_intr; - case TUSB_GPIO_INT_MASK: - return s->gpio_mask; - - case TUSB_INT_SRC: - case TUSB_INT_SRC_SET: /* TODO: What do these two return? */ - case TUSB_INT_SRC_CLEAR: - return s->intr; - case TUSB_INT_MASK: - return s->mask; - - case TUSB_GPIO_REV: - return 0x30; - case TUSB_GPIO_CONF: - return s->gpio_config; - - case TUSB_DMA_CTRL_REV: - return 0x30; - case TUSB_DMA_REQ_CONF: - return s->dma_config; - case TUSB_EP0_CONF: - return s->ep0_config; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - return s->tx_config[epnum]; - case TUSB_DMA_EP_MAP: - return s->dma_map; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - return s->rx_config[epnum]; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return 0x00000000; /* TODO */ - case TUSB_WAIT_COUNT: - return 0x00; /* TODO */ - - case TUSB_SCRATCH_PAD: - return s->scratch; - - case TUSB_PROD_TEST_RESET: - return s->test_reset; - - /* DIE IDs */ - case TUSB_DIDR1_LO: - return 0xa9453c59; - case TUSB_DIDR1_HI: - return 0x54059adf; - } - - printf("%s: unknown register at %03x\n", __func__, offset); - return 0; -} - -static void tusb_async_writeb(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[0](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __func__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writeh(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - - switch (addr & 0xfff) { - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[1](s->musb, addr & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - default: - printf("%s: unknown register at %03x\n", - __func__, (int) (addr & 0xfff)); - return; - } -} - -static void tusb_async_writew(void *opaque, hwaddr addr, - uint32_t value) -{ - TUSBState *s = (TUSBState *) opaque; - int offset = addr & 0xfff; - int epnum; - - switch (offset) { - case TUSB_VLYNQ_CTRL: - break; - - case TUSB_BASE_OFFSET ... (TUSB_BASE_OFFSET | 0x1ff): - musb_write[2](s->musb, offset & 0x1ff, value); - break; - - case TUSB_FIFO_BASE ... (TUSB_FIFO_BASE | 0x1ff): - musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value); - break; - - case TUSB_DEV_CONF: - s->dev_config = value; - s->host_mode = (value & TUSB_DEV_CONF_USB_HOST_MODE); - if (value & TUSB_DEV_CONF_PROD_TEST_MODE) - hw_error("%s: Product Test mode not allowed\n", __func__); - break; - - case TUSB_PHY_OTG_CTRL_ENABLE: - case TUSB_PHY_OTG_CTRL: - return; /* TODO */ - case TUSB_DEV_OTG_TIMER: - s->otg_timer_val = value; - if (value & TUSB_DEV_OTG_TIMER_ENABLE) - timer_mod(s->otg_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(TUSB_DEV_OTG_TIMER_VAL(value), - NANOSECONDS_PER_SECOND, TUSB_DEVCLOCK)); - else - timer_del(s->otg_timer); - break; - - case TUSB_PRCM_CONF: - s->prcm_config = value; - break; - case TUSB_PRCM_MNGMT: - s->prcm_mngmt = value; - break; - case TUSB_PRCM_WAKEUP_CLEAR: - break; - case TUSB_PRCM_WAKEUP_MASK: - s->wkup_mask = value; - break; - - case TUSB_PULLUP_1_CTRL: - s->pullup[0] = value; - break; - case TUSB_PULLUP_2_CTRL: - s->pullup[1] = value; - break; - case TUSB_INT_CTRL_CONF: - s->control_config = value; - tusb_intr_update(s); - break; - - case TUSB_USBIP_INT_SET: - s->usbip_intr |= value; - tusb_usbip_intr_update(s); - break; - case TUSB_USBIP_INT_CLEAR: - s->usbip_intr &= ~value; - tusb_usbip_intr_update(s); - musb_core_intr_clear(s->musb, ~value); - break; - case TUSB_USBIP_INT_MASK: - s->usbip_mask = value; - tusb_usbip_intr_update(s); - break; - - case TUSB_DMA_INT_SET: - s->dma_intr |= value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_CLEAR: - s->dma_intr &= ~value; - tusb_dma_intr_update(s); - break; - case TUSB_DMA_INT_MASK: - s->dma_mask = value; - tusb_dma_intr_update(s); - break; - - case TUSB_GPIO_INT_SET: - s->gpio_intr |= value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_CLEAR: - s->gpio_intr &= ~value; - tusb_gpio_intr_update(s); - break; - case TUSB_GPIO_INT_MASK: - s->gpio_mask = value; - tusb_gpio_intr_update(s); - break; - - case TUSB_INT_SRC_SET: - s->intr |= value; - tusb_intr_update(s); - break; - case TUSB_INT_SRC_CLEAR: - s->intr &= ~value; - tusb_intr_update(s); - break; - case TUSB_INT_MASK: - s->mask = value; - tusb_intr_update(s); - break; - - case TUSB_GPIO_CONF: - s->gpio_config = value; - break; - case TUSB_DMA_REQ_CONF: - s->dma_config = value; - break; - case TUSB_EP0_CONF: - s->ep0_config = value & 0x1ff; - musb_set_size(s->musb, 0, TUSB_EP0_CONFIG_XFR_SIZE(value), - value & TUSB_EP0_CONFIG_DIR_TX); - break; - case TUSB_EP_IN_SIZE ... (TUSB_EP_IN_SIZE + 0x3b): - epnum = (offset - TUSB_EP_IN_SIZE) >> 2; - s->tx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 1); - break; - case TUSB_DMA_EP_MAP: - s->dma_map = value; - break; - case TUSB_EP_OUT_SIZE ... (TUSB_EP_OUT_SIZE + 0x3b): - epnum = (offset - TUSB_EP_OUT_SIZE) >> 2; - s->rx_config[epnum] = value; - musb_set_size(s->musb, epnum + 1, TUSB_EP_CONFIG_XFR_SIZE(value), 0); - break; - case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... - (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - return; /* TODO */ - case TUSB_WAIT_COUNT: - return; /* TODO */ - - case TUSB_SCRATCH_PAD: - s->scratch = value; - break; - - case TUSB_PROD_TEST_RESET: - s->test_reset = value; - break; - - default: - printf("%s: unknown register at %03x\n", __func__, offset); - return; - } -} - -static uint64_t tusb_async_readfn(void *opaque, hwaddr addr, unsigned size) -{ - switch (size) { - case 1: - return tusb_async_readb(opaque, addr); - case 2: - return tusb_async_readh(opaque, addr); - case 4: - return tusb_async_readw(opaque, addr); - default: - g_assert_not_reached(); - } -} - -static void tusb_async_writefn(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - switch (size) { - case 1: - tusb_async_writeb(opaque, addr, value); - break; - case 2: - tusb_async_writeh(opaque, addr, value); - break; - case 4: - tusb_async_writew(opaque, addr, value); - break; - default: - g_assert_not_reached(); - } -} - -static const MemoryRegionOps tusb_async_ops = { - .read = tusb_async_readfn, - .write = tusb_async_writefn, - .valid.min_access_size = 1, - .valid.max_access_size = 4, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void tusb_otg_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - s->otg_timer_val = 0; - s->intr |= TUSB_INT_SRC_OTG_TIMEOUT; - tusb_intr_update(s); -} - -static void tusb_power_tick(void *opaque) -{ - TUSBState *s = (TUSBState *) opaque; - - if (s->power) { - s->intr_ok = ~0; - tusb_intr_update(s); - } -} - -static void tusb_musb_core_intr(void *opaque, int source, int level) -{ - TUSBState *s = (TUSBState *) opaque; - uint16_t otg_status = s->otg_status; - - switch (source) { - case musb_set_vbus: - if (level) - otg_status |= TUSB_DEV_OTG_STAT_VBUS_VALID; - else - otg_status &= ~TUSB_DEV_OTG_STAT_VBUS_VALID; - - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_VBUS_DET_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_VBUS_DET_EN set? */ - if (s->otg_status != otg_status) { - s->otg_status = otg_status; - s->intr |= TUSB_INT_SRC_VBUS_SENSE_CHNG; - tusb_intr_update(s); - } - break; - - case musb_set_session: - /* XXX: only if TUSB_PHY_OTG_CTRL_OTG_SESS_END_EN set? */ - /* XXX: only if TUSB_PRCM_MNGMT_OTG_SESS_END_EN set? */ - if (level) { - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_END; - } else { - s->otg_status &= ~TUSB_DEV_OTG_STAT_SESS_VALID; - s->otg_status |= TUSB_DEV_OTG_STAT_SESS_END; - } - - /* XXX: some IRQ or anything? */ - break; - - case musb_irq_tx: - case musb_irq_rx: - s->usbip_intr = musb_core_intr_get(s->musb); - /* Fall through. */ - default: - if (level) - s->intr |= 1 << source; - else - s->intr &= ~(1 << source); - tusb_intr_update(s); - break; - } -} - -static void tusb6010_power(TUSBState *s, int on) -{ - if (!on) { - s->power = 0; - } else if (!s->power && on) { - s->power = 1; - /* Pull the interrupt down after TUSB6010 comes up. */ - s->intr_ok = 0; - tusb_intr_update(s); - timer_mod(s->pwr_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / 2); - } -} - -static void tusb6010_irq(void *opaque, int source, int level) -{ - if (source) { - tusb_musb_core_intr(opaque, source - 1, level); - } else { - tusb6010_power(opaque, level); - } -} - -static void tusb6010_reset(DeviceState *dev) -{ - TUSBState *s = TUSB6010(dev); - int i; - - s->test_reset = TUSB_PROD_TEST_RESET_VAL; - s->host_mode = 0; - s->dev_config = 0; - s->otg_status = 0; /* !TUSB_DEV_OTG_STAT_ID_STATUS means host mode */ - s->power = 0; - s->mask = 0xffffffff; - s->intr = 0x00000000; - s->otg_timer_val = 0; - s->scratch = 0; - s->prcm_config = 0; - s->prcm_mngmt = 0; - s->intr_ok = 0; - s->usbip_intr = 0; - s->usbip_mask = 0; - s->gpio_intr = 0; - s->gpio_mask = 0; - s->gpio_config = 0; - s->dma_intr = 0; - s->dma_mask = 0; - s->dma_map = 0; - s->dma_config = 0; - s->ep0_config = 0; - s->wkup_mask = 0; - s->pullup[0] = s->pullup[1] = 0; - s->control_config = 0; - for (i = 0; i < 15; i++) { - s->rx_config[i] = s->tx_config[i] = 0; - } - musb_reset(s->musb); -} - -static void tusb6010_realize(DeviceState *dev, Error **errp) -{ - TUSBState *s = TUSB6010(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - - s->otg_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_otg_tick, s); - s->pwr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, tusb_power_tick, s); - memory_region_init_io(&s->iomem[1], OBJECT(s), &tusb_async_ops, s, - "tusb-async", UINT32_MAX); - sysbus_init_mmio(sbd, &s->iomem[0]); - sysbus_init_mmio(sbd, &s->iomem[1]); - sysbus_init_irq(sbd, &s->irq); - qdev_init_gpio_in(dev, tusb6010_irq, musb_irq_max + 1); - s->musb = musb_init(dev, 1); -} - -static void tusb6010_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = tusb6010_realize; - dc->reset = tusb6010_reset; -} - -static const TypeInfo tusb6010_info = { - .name = TYPE_TUSB6010, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TUSBState), - .class_init = tusb6010_class_init, -}; - -static void tusb6010_register_types(void) -{ - type_register_static(&tusb6010_info); -} - -type_init(tusb6010_register_types) diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index fc93429c9c..c4a783d128 100644 --- a/hw/usb/u2f-passthru.c +++ b/hw/usb/u2f-passthru.c @@ -482,10 +482,8 @@ static void u2f_passthru_realize(U2FKeyState *base, Error **errp) return; #endif } else { - fd = qemu_open_old(key->hidraw, O_RDWR); + fd = qemu_open(key->hidraw, O_RDWR, errp); if (fd < 0) { - error_setg(errp, "%s: Failed to open %s", TYPE_U2F_PASSTHRU, - key->hidraw); return; } @@ -512,7 +510,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 a408a82927..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,7 +64,7 @@ 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) diff --git a/hw/usb/vt82c686-uhci-pci.c b/hw/usb/vt82c686-uhci-pci.c index 46a901f56f..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) diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 0f7369e7ed..13901625c0 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) @@ -1071,7 +1083,7 @@ static void usbback_event(struct XenLegacyDevice *xendev) qemu_bh_schedule(usbif->bh); } -struct XenDevOps xen_usb_ops = { +static const struct XenDevOps xen_usb_ops = { .size = sizeof(struct usbback_info), .flags = DEVOPS_FLAG_NEED_GNTDEV, .init = usbback_init, @@ -1083,15 +1095,9 @@ struct XenDevOps xen_usb_ops = { .event = usbback_event, }; -#else /* USBIF_SHORT_NOT_OK */ - -static int usbback_not_supported(void) +static void xen_usb_register_backend(void) { - return -EINVAL; + xen_be_register("qusb", &xen_usb_ops); } - -struct XenDevOps xen_usb_ops = { - .backend_register = usbback_not_supported, -}; - +xen_backend_init(xen_usb_register_backend); #endif diff --git a/hw/usb/xlnx-versal-usb2-ctrl-regs.c b/hw/usb/xlnx-versal-usb2-ctrl-regs.c index 1c094aa1a6..66c793a602 100644 --- a/hw/usb/xlnx-versal-usb2-ctrl-regs.c +++ b/hw/usb/xlnx-versal-usb2-ctrl-regs.c @@ -153,7 +153,7 @@ static void usb2_ctrl_regs_reset_init(Object *obj, ResetType type) } } -static void usb2_ctrl_regs_reset_hold(Object *obj) +static void usb2_ctrl_regs_reset_hold(Object *obj, ResetType type) { VersalUsb2CtrlRegs *s = XILINX_VERSAL_USB2_CTRL_REGS(obj); @@ -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..4013e7b436 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,156 @@ 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 bool 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; + g_autofree struct vfio_irq_info *irq_info = NULL; + 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 false; } - 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 false; } - 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"); + return false; + } + + if (event_notifier_init(notifier, 0)) { + error_setg_errno(errp, errno, + "vfio: Unable to init event notifier for irq (%d)", + irq); + return false; + } + + 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); + } + + return true; +} + +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) { - int ret; - char *mdevid; - VFIOGroup *vfio_group; - APDevice *apdev = AP_DEVICE(dev); - VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + ERRP_GUARD(); + 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)) { 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; + if (!vfio_attach_device(vbasedev->name, vbasedev, + &address_space_memory, errp)) { + goto error; + } - /* - * 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); - if (ret) { - goto out_get_dev_err; + if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err)) { + /* + * Report this error, but do not make it a failing condition. + * Lack of this IRQ in the host does not prevent normal operation. + */ + warn_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,18 +217,46 @@ 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); + + /* AP device is mdev type device */ + vbasedev->mdev = 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); dc->realize = vfio_ap_realize; dc->unrealize = vfio_ap_unrealize; dc->hotpluggable = true; - dc->reset = vfio_ap_reset; + device_class_set_legacy_reset(dc, vfio_ap_reset); dc->bus_type = TYPE_AP_BUS; } @@ -175,6 +264,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..c1cd7736cd 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; @@ -385,12 +379,12 @@ read_err: css_inject_io_interrupt(sch); } -static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, +static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, unsigned int irq, Error **errp) { VFIODevice *vdev = &vcdev->vdev; - struct vfio_irq_info *irq_info; + g_autofree struct vfio_irq_info *irq_info = NULL; size_t argsz; int fd; EventNotifier *notifier; @@ -411,13 +405,13 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, break; default: error_setg(errp, "vfio: Unsupported device irq(%d)", irq); - return; + return false; } if (vdev->num_irqs < irq + 1) { error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)", irq, vdev->num_irqs); - return; + return false; } argsz = sizeof(*irq_info); @@ -427,27 +421,26 @@ static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, 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; + return false; } 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; + return false; } fd = event_notifier_get_fd(notifier); qemu_set_fd_handler(fd, fd_read, NULL, vcdev); - if (vfio_set_irq_signaling(vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { + if (!vfio_set_irq_signaling(vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vcdev); event_notifier_cleanup(notifier); } -out_free_info: - g_free(irq_info); + return true; } static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, @@ -471,8 +464,8 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, return; } - if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vcdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } @@ -481,7 +474,7 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, event_notifier_cleanup(notifier); } -static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) +static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) { VFIODevice *vdev = &vcdev->vdev; struct vfio_region_info *info; @@ -490,7 +483,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) /* Sanity check device */ if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) { error_setg(errp, "vfio: Um, this isn't a vfio-ccw device"); - return; + return false; } /* @@ -500,13 +493,13 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { error_setg(errp, "vfio: too few regions (%u), expected at least %u", vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); - return; + return false; } ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info); if (ret) { error_setg_errno(errp, -ret, "vfio: Error getting config info"); - return; + return false; } vcdev->io_region_size = info->size; @@ -560,7 +553,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) g_free(info); } - return; + return true; out_err: g_free(vcdev->crw_region); @@ -568,7 +561,7 @@ out_err: g_free(vcdev->async_cmd_region); g_free(vcdev->io_region); g_free(info); - return; + return false; } static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) @@ -579,131 +572,51 @@ 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; /* Call the class init function for subchannel. */ if (cdc->realize) { - cdc->realize(cdev, vcdev->vdev.sysfsdev, &err); - if (err) { - goto out_err_propagate; + if (!cdc->realize(cdev, vcdev->vdev.sysfsdev, errp)) { + return; } } - group = vfio_ccw_get_group(cdev, &err); - if (!group) { - goto out_group_err; + if (!vfio_device_get_name(vbasedev, errp)) { + goto out_unrealize; } - vfio_ccw_get_device(group, vcdev, &err); - if (err) { - goto out_device_err; + if (!vfio_attach_device(cdev->mdevid, vbasedev, + &address_space_memory, errp)) { + goto out_attach_dev_err; } - vfio_ccw_get_region(vcdev, &err); - if (err) { + if (!vfio_ccw_get_region(vcdev, errp)) { goto out_region_err; } - vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err); - if (err) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, errp)) { goto out_io_notifier_err; } if (vcdev->crw_region) { - vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); - if (err) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, + errp)) { goto out_irq_notifier_err; } } - vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &err); - if (err) { + if (!vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_REQ_IRQ_INDEX, &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); + warn_report_err(err); } return; @@ -715,31 +628,27 @@ 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); +out_unrealize: if (cdc->unrealize) { cdc->unrealize(cdev); } -out_err_propagate: - error_propagate(errp, err); } 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 +658,11 @@ 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_CCW_LOADPARM("loadparm", CcwDevice, loadparm), DEFINE_PROP_END_OF_LIST(), }; @@ -757,18 +671,48 @@ 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; + + /* CCW device is mdev type device */ + vbasedev->mdev = true; + + /* + * 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); dc->realize = vfio_ccw_realize; dc->unrealize = vfio_ccw_unrealize; - dc->reset = vfio_ccw_reset; + device_class_set_legacy_reset(dc, vfio_ccw_reset); cdc->handle_request = vfio_ccw_handle_request; cdc->handle_halt = vfio_ccw_handle_halt; @@ -780,6 +724,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 130e5d1dc7..dcef44fe55 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,187 @@ 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 ret) +{ + if (migration_is_running()) { + migration_file_set_error(ret, NULL); + } +} + +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->device_dirty_page_tracking == ON_OFF_AUTO_OFF) { + return false; + } + 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) @@ -576,12 +256,13 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) /* Called with rcu_read_lock held. */ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only) + ram_addr_t *ram_addr, bool *read_only, + Error **errp) { bool ret, mr_has_discard_manager; ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only, - &mr_has_discard_manager); + &mr_has_discard_manager, errp); if (ret && mr_has_discard_manager) { /* * Malicious VMs might trigger discarding of IOMMU-mapped memory. The @@ -607,10 +288,11 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, 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; + Error *local_err = NULL; trace_vfio_iommu_map_notify(iotlb->perm == IOMMU_NONE ? "UNMAP" : "MAP", iova, iova + iotlb->addr_mask); @@ -618,6 +300,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; } @@ -626,7 +309,8 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { bool read_only; - if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only)) { + if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, &local_err)) { + error_report_err(local_err); goto out; } /* @@ -636,22 +320,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: @@ -663,14 +349,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)); } } @@ -680,6 +367,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; @@ -698,8 +386,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); @@ -709,7 +397,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); @@ -722,7 +410,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); @@ -730,14 +418,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 @@ -752,7 +440,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 @@ -761,7 +449,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, @@ -773,23 +461,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) { @@ -822,24 +510,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 & @@ -854,15 +533,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), @@ -872,77 +590,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; } @@ -953,7 +602,7 @@ static void vfio_listener_region_add(MemoryListener *listener, IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); int iommu_idx; - trace_vfio_listener_region_add_iommu(iova, end); + trace_vfio_listener_region_add_iommu(section->mr->name, iova, end); /* * FIXME: For VFIO iommu types which have KVM acceleration to * avoid bouncing all map/unmaps through qemu this way, this @@ -964,7 +613,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()); @@ -976,21 +625,13 @@ static void vfio_listener_region_add(MemoryListener *listener, int128_get64(llend), iommu_idx); - ret = memory_region_iommu_set_page_size_mask(giommu->iommu_mr, - container->pgsizes, - &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; @@ -1004,7 +645,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; } @@ -1017,7 +658,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( @@ -1029,12 +670,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); @@ -1047,7 +689,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; } /* @@ -1055,9 +697,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 { @@ -1072,39 +714,22 @@ 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) { + trace_vfio_listener_region_del_iommu(section->mr->name); + 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, @@ -1124,15 +749,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)); @@ -1140,21 +760,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; } @@ -1163,119 +773,427 @@ 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_set_dirty_page_tracking(VFIOContainer *container, bool start) +static void vfio_dirty_tracking_update_range(VFIODirtyRanges *range, + hwaddr iova, hwaddr end, + bool update_pci) { - int ret; - struct vfio_iommu_type1_dirty_bitmap dirty = { - .argsz = sizeof(dirty), - }; + hwaddr *min, *max; - if (start) { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_START; + /* + * 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 (update_pci && iova >= UINT32_MAX) { + min = &range->minpci64; + max = &range->maxpci64; } else { - dirty.flags = VFIO_IOMMU_DIRTY_PAGES_FLAG_STOP; + 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; } - ret = ioctl(container->fd, VFIO_IOMMU_DIRTY_PAGES, &dirty); - if (ret) { - error_report("Failed to set dirty tracking flag 0x%x errno: %d", - dirty.flags, errno); + trace_vfio_device_dirty_tracking_update(iova, end, *min, *max); +} + +static void vfio_dirty_tracking_update(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIODirtyRangesListener *dirty = + container_of(listener, VFIODirtyRangesListener, listener); + hwaddr iova, end; + + if (!vfio_listener_valid_section(section, "tracking_update") || + !vfio_get_section_iova_range(dirty->bcontainer, section, + &iova, &end, NULL)) { + return; + } + + vfio_dirty_tracking_update_range(&dirty->ranges, iova, end, + vfio_section_is_vfio_pci(section, dirty->bcontainer)); +} + +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_listener_log_global_start(MemoryListener *listener) +static struct vfio_device_feature * +vfio_device_feature_dma_logging_start_create(VFIOContainerBase *bcontainer, + VFIODirtyRanges *tracking) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); + 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; - vfio_set_dirty_page_tracking(container, true); + 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; + } + + 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 bool vfio_devices_dma_logging_start(VFIOContainerBase *bcontainer, + Error **errp) +{ + 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) { + error_setg_errno(errp, errno, "Failed to prepare DMA logging"); + return false; + } + + 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_setg_errno(errp, errno, "%s: Failed to start DMA logging", + vbasedev->name); + goto out; + } + vbasedev->dirty_tracking = true; + } + +out: + if (ret) { + vfio_devices_dma_logging_stop(bcontainer); + } + + vfio_device_feature_dma_logging_start_destroy(feature); + + return ret == 0; +} + +static bool vfio_listener_log_global_start(MemoryListener *listener, + Error **errp) +{ + ERRP_GUARD(); + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + bool ret; + + if (vfio_devices_all_device_dirty_tracking(bcontainer)) { + ret = vfio_devices_dma_logging_start(bcontainer, errp); + } else { + ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp) == 0; + } + + if (!ret) { + error_prepend(errp, "vfio: Could not start dirty page tracking - "); + } + return ret; } static void vfio_listener_log_global_stop(MemoryListener *listener) { - VFIOContainer *container = container_of(listener, VFIOContainer, listener); + VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, + listener); + Error *local_err = NULL; + 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, + &local_err); + } + + if (ret) { + error_prepend(&local_err, + "vfio: Could not stop dirty page tracking - "); + error_report_err(local_err); + 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, Error **errp) +{ + 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_setg_errno(errp, -ret, + "%s: Failed to get DMA logging report, iova: " + "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx, + vbasedev->name, iova, size); - 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, Error **errp) +{ + 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; + error_setg_errno(errp, -ret, + "Failed to allocate dirty tracking bitmap"); + 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, + errp); + } else { + ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); + } - 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; } @@ -1290,32 +1208,43 @@ 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; + Error *local_err = NULL; + 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, - 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); - } + if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, &local_err)) { + error_report_err(local_err); + goto out_unlock; } + + ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, + translated_addr, &local_err); + if (ret) { + error_prepend(&local_err, + "vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") failed - ", bcontainer, iova, + iotlb->addr_mask + 1); + error_report_err(local_err); + } + +out_unlock: rcu_read_unlock(); + +out: + if (ret) { + vfio_set_migration_error(ret); + } } static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, @@ -1326,21 +1255,29 @@ static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, const ram_addr_t ram_addr = memory_region_get_ram_addr(section->mr) + section->offset_within_region; VFIORamDiscardListener *vrdl = opaque; + Error *local_err = NULL; + int ret; /* * Sync the whole mapped region (spanning multiple individual mappings) * in one go. */ - return vfio_get_dirty_bitmap(vrdl->container, iova, size, ram_addr); + ret = vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr, + &local_err); + if (ret) { + error_report_err(local_err); + } + return ret; } -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) { @@ -1361,65 +1298,91 @@ static int vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainer *container, &vrdl); } -static int vfio_sync_dirty_bitmap(VFIOContainer *container, - MemoryRegionSection *section) +static int vfio_sync_iommu_dirty_bitmap(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIOGuestIOMMU *giommu; + bool found = false; + Int128 llend; + vfio_giommu_dirty_notifier gdn; + int idx; + + QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { + if (MEMORY_REGION(giommu->iommu_mr) == section->mr && + giommu->n.start == section->offset_within_region) { + found = true; + break; + } + } + + if (!found) { + return 0; + } + + gdn.giommu = giommu; + idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr, + MEMTXATTRS_UNSPECIFIED); + + llend = int128_add(int128_make64(section->offset_within_region), + section->size); + llend = int128_sub(llend, int128_one()); + + iommu_notifier_init(&gdn.n, vfio_iommu_map_dirty_notify, IOMMU_NOTIFIER_MAP, + section->offset_within_region, int128_get64(llend), + idx); + memory_region_iommu_replay(giommu->iommu_mr, &gdn.n); + + return 0; +} + +static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, Error **errp) { ram_addr_t ram_addr; if (memory_region_is_iommu(section->mr)) { - VFIOGuestIOMMU *giommu; - - QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) { - if (MEMORY_REGION(giommu->iommu_mr) == section->mr && - giommu->n.start == section->offset_within_region) { - Int128 llend; - vfio_giommu_dirty_notifier gdn = { .giommu = giommu }; - int idx = memory_region_iommu_attrs_to_index(giommu->iommu_mr, - MEMTXATTRS_UNSPECIFIED); - - llend = int128_add(int128_make64(section->offset_within_region), - section->size); - llend = int128_sub(llend, int128_one()); - - iommu_notifier_init(&gdn.n, - vfio_iommu_map_dirty_notify, - IOMMU_NOTIFIER_MAP, - section->offset_within_region, - int128_get64(llend), - idx); - memory_region_iommu_replay(giommu->iommu_mr, &gdn.n); - break; - } - } - return 0; + return vfio_sync_iommu_dirty_bitmap(bcontainer, section); } else if (memory_region_has_ram_discard_manager(section->mr)) { - return vfio_sync_ram_discard_listener_dirty_bitmap(container, section); + int ret; + + ret = vfio_sync_ram_discard_listener_dirty_bitmap(bcontainer, section); + if (ret) { + error_setg(errp, + "Failed to sync dirty bitmap with RAM discard listener"); + } + return ret; } 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); + int128_get64(section->size), ram_addr, errp); } 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; + Error *local_err = NULL; - 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, &local_err); + if (ret) { + error_report_err(local_err); + 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, @@ -1428,338 +1391,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) { @@ -1768,41 +1427,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; @@ -1817,758 +1481,93 @@ 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; - - ret = vfio_get_iommu_info(container, &info); - if (ret) { - error_setg_errno(errp, -ret, "Failed to get VFIO IOMMU info"); - goto enable_discards_exit; - } - - if (info->flags & VFIO_IOMMU_INFO_PGSIZES) { - container->pgsizes = info->iova_pgsizes; - } else { - container->pgsizes = qemu_real_host_page_size(); - } - - if (!vfio_get_info_dma_avail(info, &container->dma_max_mappings)) { - container->dma_max_mappings = 65535; - } - vfio_get_iommu_info_migration(container, info); - g_free(info); - - /* - * FIXME: We should parse VFIO_IOMMU_TYPE1_INFO_CAP_IOVA_RANGE - * information to get the actual window extent rather than assume - * a 64-bit IOVA address space. - */ - vfio_host_win_add(container, 0, (hwaddr)-1, container->pgsizes); - - 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) +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer) { - struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; - int ret, 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; - } - - 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; + QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + bcontainer->space = space; } -void vfio_put_base_device(VFIODevice *vbasedev) +struct vfio_device_info *vfio_get_device_info(int fd) { - if (!vbasedev->group) { - return; - } - QLIST_REMOVE(vbasedev, next); - vbasedev->group = NULL; - trace_vfio_put_base_device(vbasedev->fd); - close(vbasedev->fd); -} + struct vfio_device_info *info; + uint32_t argsz = sizeof(*info); -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 = 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) +bool 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)); + HostIOMMUDevice *hiod = NULL; - 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); -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); + if (!vbasedev->mdev) { + hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); + vbasedev->hiod = hiod; } - 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)) { + if (!ops->attach_device(name, vbasedev, as, errp)) { + object_unref(hiod); + vbasedev->hiod = NULL; return false; } return true; } -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) +void vfio_detach_device(VFIODevice *vbasedev) { - 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; + if (!vbasedev->bcontainer) { + return; } - - 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); + object_unref(vbasedev->hiod); + VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c new file mode 100644 index 0000000000..6f86c37d97 --- /dev/null +++ b/hw/vfio/container-base.c @@ -0,0 +1,142 @@ +/* + * 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) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->dma_map); + return vioc->dma_map(bcontainer, iova, size, vaddr, readonly); +} + +int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->dma_unmap); + return vioc->dma_unmap(bcontainer, iova, size, iotlb); +} + +bool vfio_container_add_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + if (!vioc->add_window) { + return true; + } + + return vioc->add_window(bcontainer, section, errp); +} + +void vfio_container_del_section_window(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + if (!vioc->del_window) { + return; + } + + return vioc->del_window(bcontainer, section); +} + +int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, + bool start, Error **errp) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + if (!bcontainer->dirty_pages_supported) { + return 0; + } + + g_assert(vioc->set_dirty_page_tracking); + return vioc->set_dirty_page_tracking(bcontainer, start, errp); +} + +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->query_dirty_bitmap); + return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size, + errp); +} + +static gpointer copy_iova_range(gconstpointer src, gpointer data) +{ + Range *source = (Range *)src; + Range *dest = g_new(Range, 1); + + range_set_bounds(dest, range_lob(source), range_upb(source)); + return dest; +} + +GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer) +{ + assert(bcontainer); + return g_list_copy_deep(bcontainer->iova_ranges, copy_iova_range, NULL); +} + +static void vfio_container_instance_finalize(Object *obj) +{ + VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); + VFIOGuestIOMMU *giommu, *tmp; + + QLIST_SAFE_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 void vfio_container_instance_init(Object *obj) +{ + VFIOContainerBase *bcontainer = VFIO_IOMMU(obj); + + 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); +} + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU, + .parent = TYPE_OBJECT, + .instance_init = vfio_container_instance_init, + .instance_finalize = vfio_container_instance_finalize, + .instance_size = sizeof(VFIOContainerBase), + .class_size = sizeof(VFIOIOMMUClass), + .abstract = true, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/container.c b/hw/vfio/container.c new file mode 100644 index 0000000000..9ccdb639ac --- /dev/null +++ b/hw/vfio/container.c @@ -0,0 +1,1214 @@ +/* + * 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 "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; + Error *local_err = NULL; + + 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, &local_err); + if (ret) { + error_report_err(local_err); + 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, Error **errp) +{ + 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_setg_errno(errp, errno, "Failed to set dirty tracking flag 0x%x", + dirty.flags); + } + + return ret; +} + +static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +{ + 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_setg_errno(errp, errno, + "Failed to get dirty bitmap for iova: 0x%"PRIx64 + " size: 0x%"PRIx64, (uint64_t)range->iova, + (uint64_t)range->size); + } + + 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(int container_fd, + 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 char *vfio_get_iommu_class_name(int iommu_type) +{ + switch (iommu_type) { + case VFIO_TYPE1v2_IOMMU: + case VFIO_TYPE1_IOMMU: + return TYPE_VFIO_IOMMU_LEGACY; + break; + case VFIO_SPAPR_TCE_v2_IOMMU: + case VFIO_SPAPR_TCE_IOMMU: + return TYPE_VFIO_IOMMU_SPAPR; + break; + default: + g_assert_not_reached(); + }; +} + +static bool vfio_set_iommu(int container_fd, int group_fd, + int *iommu_type, Error **errp) +{ + if (ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container_fd)) { + error_setg_errno(errp, errno, "Failed to set group container"); + return false; + } + + 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 false; + } + + return true; +} + +static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, + Error **errp) +{ + int iommu_type; + const char *vioc_name; + VFIOContainer *container; + + iommu_type = vfio_get_iommu_type(fd, errp); + if (iommu_type < 0) { + return NULL; + } + + if (!vfio_set_iommu(fd, group->fd, &iommu_type, errp)) { + return NULL; + } + + vioc_name = vfio_get_iommu_class_name(iommu_type); + + container = VFIO_IOMMU_LEGACY(object_new(vioc_name)); + container->fd = fd; + container->iommu_type = iommu_type; + return container; +} + +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 bool 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 false; + } + + 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 true; +} + +static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, + Error **errp) +{ + VFIOContainer *container; + VFIOContainerBase *bcontainer; + int ret, fd; + VFIOAddressSpace *space; + VFIOIOMMUClass *vioc; + + 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 false; + } + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + vfio_kvm_device_add_group(group); + return true; + } + } + + fd = qemu_open("/dev/vfio/vfio", O_RDWR, errp); + if (fd < 0) { + 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); + goto close_fd_exit; + } + + container = vfio_create_container(fd, group, errp); + if (!container) { + goto close_fd_exit; + } + bcontainer = &container->bcontainer; + + if (!vfio_cpr_register_container(bcontainer, errp)) { + 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; + } + + vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + assert(vioc->setup); + + if (!vioc->setup(bcontainer, errp)) { + goto enable_discards_exit; + } + + vfio_kvm_device_add_group(group); + + vfio_address_space_insert(space, bcontainer); + + 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) { + error_propagate_prepend(errp, bcontainer->error, + "memory listener initialization failed: "); + goto listener_release_exit; + } + + bcontainer->initialized = true; + + return true; +listener_release_exit: + QLIST_REMOVE(group, container_next); + vfio_kvm_device_del_group(group); + memory_listener_unregister(&bcontainer->listener); + if (vioc->release) { + vioc->release(bcontainer); + } + +enable_discards_exit: + vfio_ram_block_discard_disable(container, false); + +unregister_container_exit: + vfio_cpr_unregister_container(bcontainer); + +free_container_exit: + object_unref(container); + +close_fd_exit: + close(fd); + +put_space_exit: + vfio_put_address_space(space); + + return false; +} + +static void vfio_disconnect_container(VFIOGroup *group) +{ + VFIOContainer *container = group->container; + VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(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 (vioc->release) { + vioc->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; + + trace_vfio_disconnect_container(container->fd); + vfio_cpr_unregister_container(bcontainer); + close(container->fd); + object_unref(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(path, O_RDWR, errp); + if (group->fd < 0) { + 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 bool 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 false; + } + + info = vfio_get_device_info(fd); + if (!info) { + error_setg_errno(errp, errno, "error getting device info"); + close(fd); + return false; + } + + /* + * 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 false; + } + + 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 true; +} + +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 bool 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; + + if (groupid < 0) { + return false; + } + + trace_vfio_attach_device(vbasedev->name, groupid); + + if (!vfio_device_hiod_realize(vbasedev, errp)) { + return false; + } + + group = vfio_get_group(groupid, as, errp); + if (!group) { + return false; + } + + 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 false; + } + } + if (!vfio_get_device(group, name, vbasedev, errp)) { + vfio_put_group(group); + return false; + } + + 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 true; +} + +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->hiod_typename = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO; + + 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 bool hiod_legacy_vfio_realize(HostIOMMUDevice *hiod, void *opaque, + Error **errp) +{ + VFIODevice *vdev = opaque; + + hiod->name = g_strdup(vdev->name); + hiod->agent = opaque; + + return true; +} + +static int hiod_legacy_vfio_get_cap(HostIOMMUDevice *hiod, int cap, + Error **errp) +{ + switch (cap) { + case HOST_IOMMU_DEVICE_CAP_AW_BITS: + return vfio_device_get_aw_bits(hiod->agent); + default: + error_setg(errp, "%s: unsupported capability %x", hiod->name, cap); + return -EINVAL; + } +} + +static GList * +hiod_legacy_vfio_get_iova_ranges(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_iova_ranges(vdev->bcontainer); +} + +static uint64_t +hiod_legacy_vfio_get_page_size_mask(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_page_size_mask(vdev->bcontainer); +} + +static void vfio_iommu_legacy_instance_init(Object *obj) +{ + VFIOContainer *container = VFIO_IOMMU_LEGACY(obj); + + QLIST_INIT(&container->group_list); +} + +static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) +{ + HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); + + hioc->realize = hiod_legacy_vfio_realize; + hioc->get_cap = hiod_legacy_vfio_get_cap; + hioc->get_iova_ranges = hiod_legacy_vfio_get_iova_ranges; + hioc->get_page_size_mask = hiod_legacy_vfio_get_page_size_mask; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_LEGACY, + .parent = TYPE_VFIO_IOMMU, + .instance_init = vfio_iommu_legacy_instance_init, + .instance_size = sizeof(VFIOContainer), + .class_init = vfio_iommu_legacy_class_init, + }, { + .name = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, + .parent = TYPE_HOST_IOMMU_DEVICE, + .class_init = hiod_legacy_vfio_class_init, + } +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c new file mode 100644 index 0000000000..87e51fcee1 --- /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; +} + +bool 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 true; +} + +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 78f4d82c1c..ea87830fe0 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" @@ -123,7 +124,7 @@ static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, } } -static void vfio_display_edid_init(VFIOPCIDevice *vdev) +static bool vfio_display_edid_init(VFIOPCIDevice *vdev, Error **errp) { VFIODisplay *dpy = vdev->dpy; int fd = vdev->vbasedev.fd; @@ -134,7 +135,8 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) VFIO_REGION_SUBTYPE_GFX_EDID, &dpy->edid_info); if (ret) { - return; + /* Failed to get GFX edid info, allow to go through without edid. */ + return true; } trace_vfio_display_edid_available(); @@ -166,13 +168,16 @@ static void vfio_display_edid_init(VFIOPCIDevice *vdev) vfio_display_edid_link_up, vdev); vfio_display_edid_update(vdev, true, 0, 0); - return; + return true; err: + error_setg(errp, "vfio: failed to read GFX edid field"); trace_vfio_display_edid_write_error(); + g_free(dpy->edid_info); g_free(dpy->edid_regs); + dpy->edid_info = NULL; dpy->edid_regs = NULL; - return; + return false; } static void vfio_display_edid_exit(VFIODisplay *dpy) @@ -181,6 +186,7 @@ static void vfio_display_edid_exit(VFIODisplay *dpy) return; } + g_free(dpy->edid_info); g_free(dpy->edid_regs); g_free(dpy->edid_blob); timer_free(dpy->edid_link_timer); @@ -240,12 +246,11 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, dmabuf = g_new0(VFIODMABuf, 1); dmabuf->dmabuf_id = plane.dmabuf_id; - dmabuf->buf.width = plane.width; - dmabuf->buf.height = plane.height; - dmabuf->buf.stride = plane.stride; - dmabuf->buf.fourcc = plane.drm_format; - dmabuf->buf.modifier = plane.drm_format_mod; - dmabuf->buf.fd = fd; + dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height, + plane.stride, 0, 0, plane.width, + plane.height, plane.drm_format, + plane.drm_format_mod, fd, false, false); + if (plane_type == DRM_PLANE_TYPE_CURSOR) { vfio_display_update_cursor(dmabuf, &plane); } @@ -257,8 +262,10 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) { QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); - dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); - close(dmabuf->buf.fd); + + qemu_dmabuf_close(dmabuf->buf); + dpy_gl_release_dmabuf(dpy->con, dmabuf->buf); + g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); g_free(dmabuf); } @@ -283,6 +290,7 @@ static void vfio_display_dmabuf_update(void *opaque) VFIOPCIDevice *vdev = opaque; VFIODisplay *dpy = vdev->dpy; VFIODMABuf *primary, *cursor; + uint32_t width, height; bool free_bufs = false, new_cursor = false; primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); @@ -293,11 +301,13 @@ static void vfio_display_dmabuf_update(void *opaque) return; } + width = qemu_dmabuf_get_width(primary->buf); + height = qemu_dmabuf_get_height(primary->buf); + if (dpy->dmabuf.primary != primary) { dpy->dmabuf.primary = primary; - qemu_console_resize(dpy->con, - primary->buf.width, primary->buf.height); - dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); + qemu_console_resize(dpy->con, width, height); + dpy_gl_scanout_dmabuf(dpy->con, primary->buf); free_bufs = true; } @@ -311,7 +321,7 @@ static void vfio_display_dmabuf_update(void *opaque) if (cursor && (new_cursor || cursor->hot_updates)) { bool have_hot = (cursor->hot_x != 0xffffffff && cursor->hot_y != 0xffffffff); - dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, + dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot, cursor->hot_x, cursor->hot_y); cursor->hot_updates = 0; } else if (!cursor && new_cursor) { @@ -325,7 +335,7 @@ static void vfio_display_dmabuf_update(void *opaque) cursor->pos_updates = 0; } - dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); + dpy_gl_update(dpy->con, 0, 0, width, height); if (free_bufs) { vfio_display_free_dmabufs(vdev); @@ -343,11 +353,11 @@ static const GraphicHwOps vfio_display_dmabuf_ops = { .ui_info = vfio_display_edid_ui_info, }; -static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) { if (!display_opengl) { error_setg(errp, "vfio-display-dmabuf: opengl not available"); - return -1; + return false; } vdev->dpy = g_new0(VFIODisplay, 1); @@ -356,9 +366,11 @@ static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) vdev); if (vdev->enable_ramfb) { vdev->dpy->ramfb = ramfb_setup(errp); + if (!vdev->dpy->ramfb) { + return false; + } } - vfio_display_edid_init(vdev); - return 0; + return vfio_display_edid_init(vdev, errp); } static void vfio_display_dmabuf_exit(VFIODisplay *dpy) @@ -475,7 +487,7 @@ static const GraphicHwOps vfio_display_region_ops = { .gfx_update = vfio_display_region_update, }; -static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) { vdev->dpy = g_new0(VFIODisplay, 1); vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, @@ -483,8 +495,11 @@ static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) vdev); if (vdev->enable_ramfb) { vdev->dpy->ramfb = ramfb_setup(errp); + if (!vdev->dpy->ramfb) { + return false; + } } - return 0; + return true; } static void vfio_display_region_exit(VFIODisplay *dpy) @@ -499,7 +514,7 @@ static void vfio_display_region_exit(VFIODisplay *dpy) /* ---------------------------------------------------------------------- */ -int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) +bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) { struct vfio_device_gfx_plane_info probe; int ret; @@ -522,11 +537,11 @@ int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) if (vdev->display == ON_OFF_AUTO_AUTO) { /* not an error in automatic mode */ - return 0; + return true; } error_setg(errp, "vfio: device doesn't support any (known) display method"); - return -1; + return false; } void vfio_display_finalize(VFIOPCIDevice *vdev) @@ -541,3 +556,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..913796f437 --- /dev/null +++ b/hw/vfio/helpers.c @@ -0,0 +1,730 @@ +/* + * 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 "qemu/units.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; + } +} + +bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp) +{ + ERRP_GUARD(); + g_autofree struct vfio_irq_set *irq_set = NULL; + int argsz; + 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)) { + return true; + } + + error_setg_errno(errp, errno, "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 false; +} + +/* + * 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) +{ + g_autofree struct vfio_region_info *info = NULL; + 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; + } + } + } + + 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, ret, 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++) { + size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB); + void *map_base, *map_align; + + /* + * Align the mmap for more efficient mapping in the kernel. Ideally + * we'd know the PMD and PUD mapping sizes to use as discrete alignment + * intervals, but we don't. As of Linux v6.12, the largest PUD size + * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only set + * on x86_64). Align by power-of-two size, capped at 1GiB. + * + * NB. qemu_memalign() and friends actually allocate memory, whereas + * the region size here can exceed host memory, therefore we manually + * create an oversized anonymous mapping and clean it up for alignment. + */ + map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map_base == MAP_FAILED) { + ret = -errno; + goto no_mmap; + } + + map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align); + munmap(map_base, map_align - map_base); + munmap(map_align + region->mmaps[i].size, + align - (map_align - map_base)); + + region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot, + MAP_SHARED | MAP_FIXED, + region->vbasedev->fd, + region->fd_offset + + region->mmaps[i].offset); + if (region->mmaps[i].mmap == MAP_FAILED) { + ret = -errno; + goto no_mmap; + } + + 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; + +no_mmap: + 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; +} + +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) +{ + g_autofree 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; + } + } + + return ret; +} + +bool 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 false; + } + /* 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 false; + } + /* + * 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 true; +} + +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; +} + +int vfio_device_get_aw_bits(VFIODevice *vdev) +{ + /* + * iova_ranges is a sorted list. For old kernels that support + * VFIO but not support query of iova ranges, iova_ranges is NULL, + * in this case HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX(64) is returned. + */ + GList *l = g_list_last(vdev->bcontainer->iova_ranges); + + if (l) { + Range *range = l->data; + return range_get_last_bit(range) + 1; + } + + return HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX; +} + +bool vfio_device_is_mdev(VFIODevice *vbasedev) +{ + g_autofree char *subsys = NULL; + g_autofree char *tmp = NULL; + + if (!vbasedev->sysfsdev) { + return false; + } + + tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); + subsys = realpath(tmp, NULL); + return subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); +} + +bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) +{ + HostIOMMUDevice *hiod = vbasedev->hiod; + + if (!hiod) { + return true; + } + + return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); +} diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index afe3fe7efc..4047f4f071 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" @@ -87,19 +88,33 @@ static int igd_gen(VFIOPCIDevice *vdev) case 0x2200: case 0x5900: return 8; + /* CoffeeLake */ + case 0x3e00: + return 9; + /* ElkhartLake */ + case 0x4500: + return 11; + /* TigerLake */ + case 0x9A00: + return 12; } - return 8; /* Assume newer is compatible */ + /* + * Unfortunately, Intel changes it's specification quite often. This makes + * it impossible to use a suitable default value for unknown devices. + */ + return -1; } typedef struct VFIOIGDQuirk { struct VFIOPCIDevice *vdev; uint32_t index; - uint32_t bdsm; + uint64_t bdsm; } VFIOIGDQuirk; #define IGD_GMCH 0x50 /* Graphics Control Register */ #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ +#define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ /* @@ -308,9 +323,13 @@ static void vfio_igd_quirk_data_write(void *opaque, hwaddr addr, */ if ((igd->index % 4 == 1) && igd->index < vfio_igd_gtt_max(vdev)) { if (gen < 8 || (igd->index % 8 == 1)) { - uint32_t base; + uint64_t base; - base = pci_get_long(vdev->pdev.config + IGD_BDSM); + if (gen < 11) { + base = pci_get_long(vdev->pdev.config + IGD_BDSM); + } else { + base = pci_get_quad(vdev->pdev.config + IGD_BDSM_GEN11); + } if (!base) { hw_error("vfio-igd: Guest attempted to program IGD GTT before " "BIOS reserved stolen memory. Unsupported BIOS?"); @@ -364,10 +383,134 @@ static const MemoryRegionOps vfio_igd_index_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +#define IGD_BDSM_MMIO_OFFSET 0x1080C0 + +static uint64_t vfio_igd_quirk_bdsm_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIOPCIDevice *vdev = opaque; + uint64_t offset; + + offset = IGD_BDSM_GEN11 + addr; + + switch (size) { + case 1: + return pci_get_byte(vdev->pdev.config + offset); + case 2: + return pci_get_word(vdev->pdev.config + offset); + case 4: + return pci_get_long(vdev->pdev.config + offset); + case 8: + return pci_get_quad(vdev->pdev.config + offset); + default: + hw_error("igd: unsupported read size, %u bytes", size); + break; + } + + return 0; +} + +static void vfio_igd_quirk_bdsm_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIOPCIDevice *vdev = opaque; + uint64_t offset; + + offset = IGD_BDSM_GEN11 + addr; + + switch (size) { + case 1: + pci_set_byte(vdev->pdev.config + offset, data); + break; + case 2: + pci_set_word(vdev->pdev.config + offset, data); + break; + case 4: + pci_set_long(vdev->pdev.config + offset, data); + break; + case 8: + pci_set_quad(vdev->pdev.config + offset, data); + break; + default: + hw_error("igd: unsupported read size, %u bytes", size); + break; + } +} + +static const MemoryRegionOps vfio_igd_bdsm_quirk = { + .read = vfio_igd_quirk_bdsm_read, + .write = vfio_igd_quirk_bdsm_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) +{ + VFIOQuirk *quirk; + int gen; + + /* + * This must be an Intel VGA device at address 00:02.0 for us to even + * consider enabling legacy mode. Some driver have dependencies on the PCI + * bus address. + */ + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || nr != 0 || + &vdev->pdev != pci_find_device(pci_device_root_bus(&vdev->pdev), + 0, PCI_DEVFN(0x2, 0))) { + return; + } + + /* + * Only on IGD devices of gen 11 and above, the BDSM register is mirrored + * into MMIO space and read from MMIO space by the Windows driver. + */ + gen = igd_gen(vdev); + if (gen < 11) { + return; + } + + quirk = vfio_quirk_alloc(1); + quirk->data = vdev; + + memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_bdsm_quirk, + vdev, "vfio-igd-bdsm-quirk", 8); + memory_region_add_subregion_overlap(vdev->bars[0].region.mem, + IGD_BDSM_MMIO_OFFSET, &quirk->mem[0], + 1); + + QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next); +} + +static int igd_get_stolen_mb(int gen, uint32_t gmch) +{ + int gms; + + if (gen < 8) { + gms = (gmch >> 3) & 0x1f; + } else { + gms = (gmch >> 8) & 0xff; + } + + if (gen < 9) { + if (gms > 0x10) { + error_report("Unsupported IGD GMS value 0x%x", gms); + return 0; + } + return gms * 32; + } else { + if (gms < 0xf0) + return gms * 32; + else + return (gms - 0xf0) * 4 + 4; + } +} + void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) { - struct vfio_region_info *rom = NULL, *opregion = NULL, - *host = NULL, *lpc = NULL; + g_autofree struct vfio_region_info *rom = NULL; + g_autofree struct vfio_region_info *opregion = NULL; + g_autofree struct vfio_region_info *host = NULL; + g_autofree struct vfio_region_info *lpc = NULL; VFIOQuirk *quirk; VFIOIGDQuirk *igd; PCIDevice *lpc_bridge; @@ -409,7 +552,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * devices maintain compatibility with generation 8. */ gen = igd_gen(vdev); - if (gen != 6 && gen != 8) { + if (gen == -1) { error_report("IGD device %s is unsupported in legacy mode, " "try SandyBridge or newer", vdev->vbasedev.name); return; @@ -425,7 +568,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if ((ret || !rom->size) && !vdev->pdev.romfile) { error_report("IGD device %s has no ROM, legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* @@ -436,7 +579,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) error_report("IGD device %s hotplugged, ROM disabled, " "legacy mode disabled", vdev->vbasedev.name); vdev->rom_read_failed = true; - goto out; + return; } /* @@ -449,7 +592,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s does not support OpRegion access," "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } ret = vfio_get_dev_region_info(&vdev->vbasedev, @@ -458,7 +601,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s does not support host bridge access," "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } ret = vfio_get_dev_region_info(&vdev->vbasedev, @@ -467,7 +610,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s does not support LPC bridge access," "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); @@ -477,11 +620,11 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * try to enable it. Probably shouldn't be using legacy mode without VGA, * but also no point in us enabling VGA if disabled in hardware. */ - if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) { + if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); error_report("IGD device %s failed to enable VGA access, " "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* Create our LPC/ISA bridge */ @@ -489,7 +632,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s failed to create LPC bridge, " "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* Stuff some host values into the VM PCI host bridge */ @@ -497,15 +640,14 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) if (ret) { error_report("IGD device %s failed to modify host bridge, " "legacy mode disabled", vdev->vbasedev.name); - goto out; + return; } /* Setup OpRegion access */ - ret = vfio_pci_igd_opregion_init(vdev, opregion, &err); - if (ret) { + if (!vfio_pci_igd_opregion_init(vdev, opregion, &err)) { error_append_hint(&err, "IGD legacy mode disabled\n"); error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - goto out; + return; } /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ @@ -513,7 +655,13 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) igd = quirk->data = g_malloc0(sizeof(*igd)); igd->vdev = vdev; igd->index = ~0; - igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); + if (gen < 11) { + igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); + } else { + igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM_GEN11, 4); + igd->bdsm |= + (uint64_t)vfio_pci_read_config(&vdev->pdev, IGD_BDSM_GEN11 + 4, 4) << 32; + } igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, @@ -534,23 +682,7 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) ggms_mb = 1 << ggms_mb; } - /* - * Assume we have no GMS memory, but allow it to be overridden by device - * option (experimental). The spec doesn't actually allow zero GMS when - * when IVD (IGD VGA Disable) is clear, but the claim is that it's unused, - * so let's not waste VM memory for it. - */ - gmch &= ~((gen < 8 ? 0x1f : 0xff) << (gen < 8 ? 3 : 8)); - - if (vdev->igd_gms) { - if (vdev->igd_gms <= 0x10) { - gms_mb = vdev->igd_gms * 32; - gmch |= vdev->igd_gms << (gen < 8 ? 3 : 8); - } else { - error_report("Unsupported IGD GMS value 0x%x", vdev->igd_gms); - vdev->igd_gms = 0; - } - } + gms_mb = igd_get_stolen_mb(gen, gmch); /* * Request reserved memory for stolen memory via fw_cfg. VM firmware @@ -571,9 +703,15 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); /* BDSM is read-write, emulated. The BIOS needs to be able to write it */ - pci_set_long(vdev->pdev.config + IGD_BDSM, 0); - pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); - pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); + if (gen < 11) { + pci_set_long(vdev->pdev.config + IGD_BDSM, 0); + pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); + pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); + } else { + pci_set_quad(vdev->pdev.config + IGD_BDSM_GEN11, 0); + pci_set_quad(vdev->pdev.wmask + IGD_BDSM_GEN11, ~0); + pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0); + } /* * This IOBAR gives us access to GTTADR, which allows us to write to @@ -607,10 +745,4 @@ void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } trace_vfio_pci_igd_bdsm_enabled(vdev->vbasedev.name, ggms_mb + gms_mb); - -out: - g_free(rom); - g_free(opregion); - g_free(host); - g_free(lpc); } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c new file mode 100644 index 0000000000..e7bece4ea1 --- /dev/null +++ b/hw/vfio/iommufd.c @@ -0,0 +1,871 @@ +/* + * 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" +#include "exec/ram_addr.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 bool 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 bool iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) +{ + IOMMUFDBackend *iommufd = vbasedev->iommufd; + struct vfio_device_bind_iommufd bind = { + .argsz = sizeof(bind), + .flags = 0, + }; + + if (!iommufd_backend_connect(iommufd, errp)) { + return false; + } + + /* + * 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. + */ + if (!iommufd_cdev_kvm_device_add(vbasedev, errp)) { + goto err_kvm_device_add; + } + + /* Bind device to iommufd */ + bind.iommufd = iommufd->fd; + if (ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind)) { + 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 true; +err_bind: + iommufd_cdev_kvm_device_del(vbasedev); +err_kvm_device_add: + iommufd_backend_disconnect(iommufd); + return false; +} + +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 bool iommufd_hwpt_dirty_tracking(VFIOIOASHwpt *hwpt) +{ + return hwpt && hwpt->hwpt_flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING; +} + +static int iommufd_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, + bool start, Error **errp) +{ + const VFIOIOMMUFDContainer *container = + container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + VFIOIOASHwpt *hwpt; + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + if (!iommufd_hwpt_dirty_tracking(hwpt)) { + continue; + } + + if (!iommufd_backend_set_dirty_tracking(container->be, + hwpt->hwpt_id, start, errp)) { + goto err; + } + } + + return 0; + +err: + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + if (!iommufd_hwpt_dirty_tracking(hwpt)) { + continue; + } + iommufd_backend_set_dirty_tracking(container->be, + hwpt->hwpt_id, !start, NULL); + } + return -EINVAL; +} + +static int iommufd_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, Error **errp) +{ + VFIOIOMMUFDContainer *container = container_of(bcontainer, + VFIOIOMMUFDContainer, + bcontainer); + unsigned long page_size = qemu_real_host_page_size(); + VFIOIOASHwpt *hwpt; + + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + if (!iommufd_hwpt_dirty_tracking(hwpt)) { + continue; + } + + if (!iommufd_backend_get_dirty_bitmap(container->be, hwpt->hwpt_id, + iova, size, page_size, + (uint64_t *)vbmap->bitmap, + errp)) { + return -EINVAL; + } + } + + return 0; +} + +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 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 */ + if (ioctl(vbasedev->fd, VFIO_DEVICE_ATTACH_IOMMUFD_PT, &attach_data)) { + error_setg_errno(errp, errno, + "[iommufd=%d] error attach %s (%d) to id=%d", + iommufd, vbasedev->name, vbasedev->fd, id); + return -errno; + } + + trace_iommufd_cdev_attach_ioas_hwpt(iommufd, vbasedev->name, + vbasedev->fd, id); + return 0; +} + +static bool iommufd_cdev_detach_ioas_hwpt(VFIODevice *vbasedev, Error **errp) +{ + int iommufd = vbasedev->iommufd->fd; + struct vfio_device_detach_iommufd_pt detach_data = { + .argsz = sizeof(detach_data), + .flags = 0, + }; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_DETACH_IOMMUFD_PT, &detach_data)) { + error_setg_errno(errp, errno, "detach %s failed", vbasedev->name); + return false; + } + + trace_iommufd_cdev_detach_ioas_hwpt(iommufd, vbasedev->name); + return true; +} + +static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container, + Error **errp) +{ + ERRP_GUARD(); + IOMMUFDBackend *iommufd = vbasedev->iommufd; + uint32_t flags = 0; + VFIOIOASHwpt *hwpt; + uint32_t hwpt_id; + int ret; + + /* Try to find a domain */ + QLIST_FOREACH(hwpt, &container->hwpt_list, next) { + ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); + if (ret) { + /* -EINVAL means the domain is incompatible with the device. */ + if (ret == -EINVAL) { + /* + * It is an expected failure and it just means we will try + * another domain, or create one if no existing compatible + * domain is found. Hence why the error is discarded below. + */ + error_free(*errp); + *errp = NULL; + continue; + } + + return false; + } else { + vbasedev->hwpt = hwpt; + QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); + vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); + return true; + } + } + + /* + * This is quite early and VFIO Migration state isn't yet fully + * initialized, thus rely only on IOMMU hardware capabilities as to + * whether IOMMU dirty tracking is going to be requested. Later + * vfio_migration_realize() may decide to use VF dirty tracking + * instead. + */ + if (vbasedev->hiod->caps.hw_caps & IOMMU_HW_CAP_DIRTY_TRACKING) { + flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING; + } + + if (!iommufd_backend_alloc_hwpt(iommufd, vbasedev->devid, + container->ioas_id, flags, + IOMMU_HWPT_DATA_NONE, 0, NULL, + &hwpt_id, errp)) { + return false; + } + + hwpt = g_malloc0(sizeof(*hwpt)); + hwpt->hwpt_id = hwpt_id; + hwpt->hwpt_flags = flags; + QLIST_INIT(&hwpt->device_list); + + ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); + if (ret) { + iommufd_backend_free_id(container->be, hwpt->hwpt_id); + g_free(hwpt); + return false; + } + + vbasedev->hwpt = hwpt; + vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); + QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); + QLIST_INSERT_HEAD(&container->hwpt_list, hwpt, next); + container->bcontainer.dirty_pages_supported |= + vbasedev->iommu_dirty_tracking; + if (container->bcontainer.dirty_pages_supported && + !vbasedev->iommu_dirty_tracking) { + warn_report("IOMMU instance for device %s doesn't support dirty tracking", + vbasedev->name); + } + return true; +} + +static void iommufd_cdev_autodomains_put(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container) +{ + VFIOIOASHwpt *hwpt = vbasedev->hwpt; + + QLIST_REMOVE(vbasedev, hwpt_next); + vbasedev->hwpt = NULL; + + if (QLIST_EMPTY(&hwpt->device_list)) { + QLIST_REMOVE(hwpt, next); + iommufd_backend_free_id(container->be, hwpt->hwpt_id); + g_free(hwpt); + } +} + +static bool iommufd_cdev_attach_container(VFIODevice *vbasedev, + VFIOIOMMUFDContainer *container, + Error **errp) +{ + /* mdevs aren't physical devices and will fail with auto domains */ + if (!vbasedev->mdev) { + return iommufd_cdev_autodomains_get(vbasedev, container, 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); + } + + if (vbasedev->hwpt) { + iommufd_cdev_autodomains_put(vbasedev, container); + } + +} + +static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + + if (!QLIST_EMPTY(&bcontainer->device_list)) { + return; + } + memory_listener_unregister(&bcontainer->listener); + iommufd_backend_free_id(container->be, container->ioas_id); + object_unref(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 bool iommufd_cdev_get_info_iova_range(VFIOIOMMUFDContainer *container, + uint32_t ioas_id, Error **errp) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + g_autofree struct iommu_ioas_iova_ranges *info = NULL; + struct iommu_iova_range *iova_ranges; + int sz, fd = container->be->fd; + + info = g_malloc0(sizeof(*info)); + info->size = sizeof(*info); + info->ioas_id = ioas_id; + + if (ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info) && 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); + + if (ioctl(fd, IOMMU_IOAS_IOVA_RANGES, info)) { + 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; + + return true; + +error: + error_setg_errno(errp, errno, "Cannot get IOVA ranges"); + return false; +} + +static bool 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 false; + } + vbasedev->fd = devfd; + } else { + devfd = vbasedev->fd; + } + + if (!iommufd_cdev_connect_and_bind(vbasedev, errp)) { + goto err_connect_bind; + } + + space = vfio_get_address_space(as); + + /* + * The HostIOMMUDevice data from legacy backend is static and doesn't need + * any information from the (type1-iommu) backend to be initialized. In + * contrast however, the IOMMUFD HostIOMMUDevice data requires the iommufd + * FD to be connected and having a devid to be able to successfully call + * iommufd_backend_get_device_info(). + */ + if (!vfio_device_hiod_realize(vbasedev, errp)) { + goto err_alloc_ioas; + } + + /* try to attach to an existing container in this space */ + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + if (VFIO_IOMMU_GET_CLASS(bcontainer) != 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 */ + if (!iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp)) { + goto err_alloc_ioas; + } + + trace_iommufd_cdev_alloc_ioas(vbasedev->iommufd->fd, ioas_id); + + container = VFIO_IOMMU_IOMMUFD(object_new(TYPE_VFIO_IOMMU_IOMMUFD)); + container->be = vbasedev->iommufd; + container->ioas_id = ioas_id; + QLIST_INIT(&container->hwpt_list); + + bcontainer = &container->bcontainer; + vfio_address_space_insert(space, bcontainer); + + if (!iommufd_cdev_attach_container(vbasedev, container, errp)) { + goto err_attach_container; + } + + ret = iommufd_cdev_ram_block_discard_disable(true); + if (ret) { + goto err_discard_disable; + } + + if (!iommufd_cdev_get_info_iova_range(container, ioas_id, &err)) { + 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) { + 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; + } + + if (!vfio_cpr_register_container(bcontainer, errp)) { + 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 true; + +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 false; +} + +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 (VFIO_IOMMU_GET_CLASS(vbasedev_iter->bcontainer) != 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->hiod_typename = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO; + + 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; + vioc->set_dirty_page_tracking = iommufd_set_dirty_page_tracking; + vioc->query_dirty_bitmap = iommufd_query_dirty_bitmap; +}; + +static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, + Error **errp) +{ + VFIODevice *vdev = opaque; + HostIOMMUDeviceCaps *caps = &hiod->caps; + enum iommu_hw_info_type type; + union { + struct iommu_hw_info_vtd vtd; + } data; + uint64_t hw_caps; + + hiod->agent = opaque; + + if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, + &type, &data, sizeof(data), + &hw_caps, errp)) { + return false; + } + + hiod->name = g_strdup(vdev->name); + caps->type = type; + caps->hw_caps = hw_caps; + + return true; +} + +static GList * +hiod_iommufd_vfio_get_iova_ranges(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_iova_ranges(vdev->bcontainer); +} + +static uint64_t +hiod_iommufd_vfio_get_page_size_mask(HostIOMMUDevice *hiod) +{ + VFIODevice *vdev = hiod->agent; + + g_assert(vdev); + return vfio_container_get_page_size_mask(vdev->bcontainer); +} + + +static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) +{ + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); + + hiodc->realize = hiod_iommufd_vfio_realize; + hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges; + hiodc->get_page_size_mask = hiod_iommufd_vfio_get_page_size_mask; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_IOMMUFD, + .parent = TYPE_VFIO_IOMMU, + .instance_size = sizeof(VFIOIOMMUFDContainer), + .class_init = vfio_iommu_iommufd_class_init, + }, { + .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, + .parent = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, + .class_init = hiod_iommufd_vfio_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 c74453e0b5..01aa11013e 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -10,18 +10,21 @@ #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 "qapi/qapi-events-vfio.h" #include "exec/ramlist.h" #include "exec/ram_addr.h" #include "pci.h" @@ -43,328 +46,238 @@ #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) +static QapiVfioMigrationState +mig_state_to_qapi_state(enum vfio_device_mig_state state) { - 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; + switch (state) { + case VFIO_DEVICE_STATE_STOP: + return QAPI_VFIO_MIGRATION_STATE_STOP; + case VFIO_DEVICE_STATE_RUNNING: + return QAPI_VFIO_MIGRATION_STATE_RUNNING; + case VFIO_DEVICE_STATE_STOP_COPY: + return QAPI_VFIO_MIGRATION_STATE_STOP_COPY; + case VFIO_DEVICE_STATE_RESUMING: + return QAPI_VFIO_MIGRATION_STATE_RESUMING; + case VFIO_DEVICE_STATE_RUNNING_P2P: + return QAPI_VFIO_MIGRATION_STATE_RUNNING_P2P; + case VFIO_DEVICE_STATE_PRE_COPY: + return QAPI_VFIO_MIGRATION_STATE_PRE_COPY; + case VFIO_DEVICE_STATE_PRE_COPY_P2P: + return QAPI_VFIO_MIGRATION_STATE_PRE_COPY_P2P; + default: + g_assert_not_reached(); } - 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 void vfio_migration_send_event(VFIODevice *vbasedev) { 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; - int ret; + DeviceState *dev = vbasedev->dev; + g_autofree char *qom_path = NULL; + Object *obj; - ret = vfio_mig_read(vbasedev, &device_state, sizeof(device_state), - dev_state_off); - if (ret < 0) { - return ret; + if (!vbasedev->migration_events) { + return; } - device_state = (device_state & mask) | value; + g_assert(vbasedev->ops->vfio_get_object); + obj = vbasedev->ops->vfio_get_object(vbasedev); + g_assert(obj); + qom_path = object_get_canonical_path(obj); - 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; + qapi_event_send_vfio_migration( + dev->id, qom_path, mig_state_to_qapi_state(migration->device_state)); } -static void *get_data_section_size(VFIORegion *region, uint64_t data_offset, - uint64_t data_size, uint64_t *size) +static void vfio_migration_set_device_state(VFIODevice *vbasedev, + enum vfio_device_mig_state state) { - void *ptr = NULL; - uint64_t limit = 0; - int i; + VFIOMigration *migration = vbasedev->migration; - if (!region->mmaps) { - if (size) { - *size = MIN(data_size, region->size - data_offset); - } - return ptr; + trace_vfio_migration_set_device_state(vbasedev->name, + mig_state_to_str(state)); + + migration->device_state = state; + vfio_migration_send_event(vbasedev); +} + +static int vfio_migration_set_state(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state, + enum vfio_device_mig_state recover_state, + Error **errp) +{ + VFIOMigration *migration = vbasedev->migration; + 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; + g_autofree char *error_prefix = + g_strdup_printf("%s: Failed setting device state to %s.", + vbasedev->name, mig_state_to_str(new_state)); + + trace_vfio_migration_set_state(vbasedev->name, mig_state_to_str(new_state), + mig_state_to_str(recover_state)); + + if (new_state == migration->device_state) { + return 0; } - for (i = 0; i < region->nr_mmaps; i++) { - VFIOMmap *map = region->mmaps + i; + 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 ((data_offset >= map->offset) && - (data_offset < map->offset + map->size)) { + if (recover_state == VFIO_DEVICE_STATE_ERROR) { + error_setg_errno(errp, errno, + "%s Recover state is ERROR. Resetting device", + error_prefix); - /* 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)) { + goto reset_device; + } + + error_setg_errno(errp, errno, + "%s Setting device in recover state %s", + error_prefix, mig_state_to_str(recover_state)); + + mig_state->device_state = recover_state; + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + ret = -errno; /* - * 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. + * If setting the device in recover state fails, report + * the error here and propagate the first error. */ - limit = map->offset; + error_report( + "%s: Failed setting device in recover state, err: %s. Resetting device", + vbasedev->name, strerror(errno)); + + goto reset_device; } + + vfio_migration_set_device_state(vbasedev, recover_state); + + return ret; } - if (!ptr && size) { - *size = limit ? MIN(data_size, limit - data_offset) : data_size; + vfio_migration_set_device_state(vbasedev, new_state); + if (mig_state->data_fd != -1) { + if (migration->data_fd != -1) { + /* + * This can happen if the device is asynchronously reset and + * terminates a data transfer. + */ + error_setg(errp, "%s: data_fd out of sync", vbasedev->name); + close(mig_state->data_fd); + + return -EBADF; + } + + migration->data_fd = mig_state->data_fd; } - return ptr; + + return 0; + +reset_device: + if (ioctl(vbasedev->fd, VFIO_DEVICE_RESET)) { + hw_error("%s: Failed resetting device, err: %s", vbasedev->name, + strerror(errno)); + } + + vfio_migration_set_device_state(vbasedev, 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, + Error **errp) { - 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, errp); } 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) +static int vfio_save_device_config_state(QEMUFile *f, void *opaque, + Error **errp) { VFIODevice *vbasedev = opaque; + int ret; qemu_put_be64(f, VFIO_MIG_FLAG_DEV_CONFIG_STATE); if (vbasedev->ops && vbasedev->ops->vfio_save_config) { - vbasedev->ops->vfio_save_config(vbasedev, f); + ret = vbasedev->ops->vfio_save_config(vbasedev, f, errp); + if (ret) { + return ret; + } } qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); trace_vfio_save_device_config_state(vbasedev->name); - return qemu_file_get_error(f); + ret = qemu_file_get_error(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to save state"); + } + return ret; } static int vfio_load_device_config_state(QEMUFile *f, void *opaque) @@ -398,225 +311,386 @@ 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) { + if (!migration->event_precopy_empty_hit) { + trace_vfio_save_block_precopy_empty_hit(migration->vbasedev->name); + migration->event_precopy_empty_hit = true; + } + return 0; + } + + return -errno; + } + if (data_size == 0) { + return 0; + } + + /* Non-empty read: re-arm the trace event */ + migration->event_precopy_empty_hit = false; + + 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_setup(QEMUFile *f, void *opaque) +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, Error **errp) { VFIODevice *vbasedev = opaque; VFIOMigration *migration = vbasedev->migration; + uint64_t stop_copy_size = VFIO_MIG_DEFAULT_DATA_BUFFER_SIZE; int ret; - trace_vfio_save_setup(vbasedev->name); - 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_setg(errp, "%s: Failed to allocate migration data buffer", + vbasedev->name); + return -ENOMEM; + } + + migration->event_save_iterate_started = false; + migration->event_precopy_empty_hit = false; + + if (vfio_precopy_supported(vbasedev)) { + switch (migration->device_state) { + case VFIO_DEVICE_STATE_RUNNING: + ret = vfio_migration_set_state(vbasedev, VFIO_DEVICE_STATE_PRE_COPY, + VFIO_DEVICE_STATE_RUNNING, errp); + 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: + error_setg(errp, "%s: Invalid device state %d", vbasedev->name, + migration->device_state); + 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; + if (ret < 0) { + error_setg_errno(errp, -ret, "%s: save setup failed", vbasedev->name); } - return 0; + return ret; } static void vfio_save_cleanup(void *opaque) { VFIODevice *vbasedev = opaque; + VFIOMigration *migration = vbasedev->migration; + Error *local_err = NULL; + int ret; + /* + * 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) { + ret = vfio_migration_set_state_or_reset(vbasedev, + VFIO_DEVICE_STATE_STOP, + &local_err); + if (ret) { + error_report_err(local_err); + } + } + + 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); + } + + 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; - } + if (!migration->event_save_iterate_started) { + trace_vfio_save_iterate_start(vbasedev->name); + migration->event_save_iterate_started = true; } - ret = vfio_save_buffer(f, vbasedev, &data_size); - if (ret) { - error_report("%s: vfio_save_buffer failed %s", vbasedev->name, - strerror(errno)); - return ret; + data_size = vfio_save_block(f, migration); + if (data_size < 0) { + return data_size; } - qemu_put_be64(f, VFIO_MIG_FLAG_END_OF_STATE); + vfio_update_estimated_pending_data(migration, data_size); - ret = qemu_file_get_error(f); - if (ret) { - return ret; + 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); } - /* - * 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; + trace_vfio_save_iterate(vbasedev->name, migration->precopy_init_size, + migration->precopy_dirty_size); + + 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; + Error *local_err = NULL; - ret = vfio_migration_set_state(vbasedev, ~VFIO_DEVICE_STATE_V1_RUNNING, - VFIO_DEVICE_STATE_V1_SAVING); + trace_vfio_save_complete_precopy_start(vbasedev->name); + + /* 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, &local_err); if (ret) { - error_report("%s: Failed to set state STOP and SAVING", - vbasedev->name); + error_report_err(local_err); return ret; } - ret = vfio_update_pending(vbasedev); - 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; } static void vfio_save_state(QEMUFile *f, void *opaque) { VFIODevice *vbasedev = opaque; + Error *local_err = NULL; int ret; - ret = vfio_save_device_config_state(f, opaque); + ret = vfio_save_device_config_state(f, opaque, &local_err); if (ret) { - error_report("%s: Failed to save device config space", - vbasedev->name); - qemu_file_set_error(f, ret); + error_prepend(&local_err, + "vfio: Failed to save device config space of %s - ", + vbasedev->name); + qemu_file_set_error_obj(f, ret, local_err); } } -static int vfio_load_setup(QEMUFile *f, void *opaque) +static int vfio_load_setup(QEMUFile *f, void *opaque, Error **errp) { 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, errp); } static int vfio_load_cleanup(void *opaque) @@ -625,6 +699,7 @@ static int vfio_load_cleanup(void *opaque) vfio_migration_cleanup(vbasedev); trace_vfio_load_cleanup(vbasedev->name); + return 0; } @@ -668,6 +743,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 +775,163 @@ 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; + Error *local_err = NULL; 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, &local_err); 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, local_err); } - 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; + Error *local_err = NULL; + 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, &local_err); + if (ret) { + /* + * Migration should be aborted in this case, but vm_state_notify() + * currently does not support reporting failures. + */ + migration_file_set_error(ret, local_err); + } + + 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; + Error *local_err = NULL; 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 (e->type == MIG_EVENT_PRECOPY_FAILED) { + /* + * MigrationNotifyFunc may not return an error code and an Error + * object for MIG_EVENT_PRECOPY_FAILED. Hence, report the error + * locally and ignore the errp argument. + */ + ret = vfio_migration_set_state_or_reset(vbasedev, + VFIO_DEVICE_STATE_RUNNING, + &local_err); if (ret) { - error_report("%s: Failed to set state RUNNING", vbasedev->name); + error_report_err(local_err); } } + 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,27 +942,24 @@ static int vfio_migration_init(VFIODevice *vbasedev, return -EINVAL; } - vbasedev->migration = g_new0(VFIOMigration, 1); - vbasedev->migration->device_state = VFIO_DEVICE_STATE_V1_RUNNING; - vbasedev->migration->vm_running = runstate_is_running(); - - 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) { @@ -838,16 +972,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); } /* ---------------------------------------------------------------------- */ @@ -857,60 +1014,84 @@ 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 || + vbasedev->device_dirty_page_tracking == ON_OFF_AUTO_OFF) && + !vbasedev->iommu_dirty_tracking) { + if (vbasedev->enable_migration == ON_OFF_AUTO_AUTO) { + error_setg(&err, + "%s: VFIO device doesn't support device and " + "IOMMU dirty tracking", vbasedev->name); + goto add_blocker; + } + + warn_report("%s: VFIO device doesn't support device and " + "IOMMU 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..d37f722cce 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1169,8 +1169,8 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) * the table and to write the base address of that memory to the ASLS register * of the IGD device. */ -int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, - struct vfio_region_info *info, Error **errp) +bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info, Error **errp) { int ret; @@ -1181,7 +1181,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, error_setg(errp, "failed to read IGD OpRegion"); g_free(vdev->igd_opregion); vdev->igd_opregion = NULL; - return -EINVAL; + return false; } /* @@ -1206,7 +1206,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); - return 0; + return true; } /* @@ -1259,6 +1259,7 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr) vfio_probe_nvidia_bar0_quirk(vdev, nr); vfio_probe_rtl8168_bar2_quirk(vdev, nr); #ifdef CONFIG_VFIO_IGD + vfio_probe_igd_bar0_quirk(vdev, nr); vfio_probe_igd_bar4_quirk(vdev, nr); #endif } @@ -1490,6 +1491,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,30 +1531,73 @@ const PropertyInfo qdev_prop_nv_gpudirect_clique = { .set = set_nv_gpudirect_clique_id, }; -static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) +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 bool 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; + return true; } if (!vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID)) { error_setg(errp, "NVIDIA GPUDirect Clique ID: invalid device vendor"); - return -EINVAL; + return false; } if (pci_get_byte(pdev->config + PCI_CLASS_DEVICE + 1) != PCI_BASE_CLASS_DISPLAY) { error_setg(errp, "NVIDIA GPUDirect Clique ID: unsupported PCI class"); - return -EINVAL; + return false; + } + + /* + * 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 false; + } + + 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 false; } ret = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, 8, errp); if (ret < 0) { error_prepend(errp, "Failed to add NVIDIA GPUDirect cap: "); - return ret; + return false; } memset(vdev->emulated_config_bits + pos, 0xFF, 8); @@ -1562,122 +1609,7 @@ static int vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) pci_set_byte(pdev->config + pos++, vdev->nv_gpudirect_clique << 3); pci_set_byte(pdev->config + pos, 0); - 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; + return true; } /* @@ -1698,8 +1630,9 @@ free_exit: */ #define VMD_SHADOW_CAP_VER 1 #define VMD_SHADOW_CAP_LEN 24 -static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) { + ERRP_GUARD(); uint8_t membar_phys[16]; int ret, pos = 0xE8; @@ -1707,7 +1640,7 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x467F) || vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x4C3D) || vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, 0x9A0B))) { - return 0; + return true; } ret = pread(vdev->vbasedev.fd, membar_phys, 16, @@ -1715,14 +1648,14 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) if (ret != 16) { error_report("VMD %s cannot read MEMBARs (%d)", vdev->vbasedev.name, ret); - return -EFAULT; + return false; } ret = pci_add_capability(&vdev->pdev, PCI_CAP_ID_VNDR, pos, VMD_SHADOW_CAP_LEN, errp); if (ret < 0) { error_prepend(errp, "Failed to add VMD MEMBAR Shadow cap: "); - return ret; + return false; } memset(vdev->emulated_config_bits + pos, 0xFF, VMD_SHADOW_CAP_LEN); @@ -1732,22 +1665,18 @@ static int vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) pci_set_long(vdev->pdev.config + pos, 0x53484457); /* SHDW */ memcpy(vdev->pdev.config + pos + 4, membar_phys, 16); - return 0; + return true; } -int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp) +bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp) { - int ret; - - ret = vfio_add_nv_gpudirect_cap(vdev, errp); - if (ret) { - return ret; + if (!vfio_add_nv_gpudirect_cap(vdev, errp)) { + return false; } - ret = vfio_add_vmd_shadow_cap(vdev, errp); - if (ret) { - return ret; + if (!vfio_add_vmd_shadow_cap(vdev, errp)) { + return false; } - return 0; + return true; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 92a45de4c3..14bcc725c3 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" @@ -114,7 +116,7 @@ static void vfio_intx_eoi(VFIODevice *vbasedev) vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } -static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) { #ifdef CONFIG_KVM int irq_fd = event_notifier_get_fd(&vdev->intx.interrupt); @@ -122,7 +124,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || vdev->intx.route.mode != PCI_INTX_ENABLED || !kvm_resamplefds_enabled()) { - return; + return true; } /* Get to a known interrupt state */ @@ -145,10 +147,10 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) goto fail_irqfd; } - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_UNMASK, - event_notifier_get_fd(&vdev->intx.unmask), - errp)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_UNMASK, + event_notifier_get_fd(&vdev->intx.unmask), + errp)) { goto fail_vfio; } @@ -159,7 +161,7 @@ static void vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) trace_vfio_intx_enable_kvm(vdev->vbasedev.name); - return; + return true; fail_vfio: kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, @@ -169,6 +171,9 @@ fail_irqfd: fail: qemu_set_fd_handler(irq_fd, vfio_intx_interrupt, NULL, vdev); vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + return false; +#else + return true; #endif } @@ -224,8 +229,7 @@ static void vfio_intx_update(VFIOPCIDevice *vdev, PCIINTxRoute *route) return; } - vfio_intx_enable_kvm(vdev, &err); - if (err) { + if (!vfio_intx_enable_kvm(vdev, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } @@ -257,7 +261,7 @@ static void vfio_irqchip_change(Notifier *notify, void *data) vfio_intx_update(vdev, &vdev->intx.route); } -static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); Error *err = NULL; @@ -266,7 +270,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) if (!pin) { - return 0; + return true; } vfio_disable_interrupts(vdev); @@ -288,27 +292,26 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) ret = event_notifier_init(&vdev->intx.interrupt, 0); if (ret) { error_setg_errno(errp, -ret, "event_notifier_init failed"); - return ret; + return false; } fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, vfio_intx_interrupt, NULL, vdev); - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->intx.interrupt); - return -errno; + return false; } - vfio_intx_enable_kvm(vdev, &err); - if (err) { + if (!vfio_intx_enable_kvm(vdev, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } vdev->interrupt = VFIO_INT_INTx; trace_vfio_intx_enable(vdev->vbasedev.name); - return 0; + return true; } static void vfio_intx_disable(VFIOPCIDevice *vdev) @@ -369,12 +372,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 +517,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 +560,43 @@ 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); + } } } @@ -578,8 +636,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) int32_t fd = event_notifier_get_fd(&vector->interrupt); Error *err = NULL; - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + 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 +667,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 +691,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 +704,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); @@ -769,13 +829,14 @@ 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); - if (err) { + if (!vfio_intx_enable(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } @@ -818,7 +879,7 @@ static void vfio_update_msi(VFIOPCIDevice *vdev) static void vfio_pci_load_rom(VFIOPCIDevice *vdev) { - struct vfio_region_info *reg_info; + g_autofree struct vfio_region_info *reg_info = NULL; uint64_t size; off_t off = 0; ssize_t bytes; @@ -836,8 +897,6 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) vdev->rom_size = size = reg_info->size; vdev->rom_offset = reg_info->offset; - g_free(reg_info); - if (!vdev->rom_size) { vdev->rom_read_failed = true; error_report("vfio-pci: Cannot read device rom at " @@ -1278,7 +1337,7 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) } } -static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) +static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { uint16_t ctrl; bool msi_64bit, msi_maskbit; @@ -1288,7 +1347,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) if (pread(vdev->vbasedev.fd, &ctrl, sizeof(ctrl), vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { error_setg_errno(errp, errno, "failed reading MSI PCI_CAP_FLAGS"); - return -errno; + return false; } ctrl = le16_to_cpu(ctrl); @@ -1301,14 +1360,14 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err); if (ret < 0) { if (ret == -ENOTSUP) { - return 0; + return true; } error_propagate_prepend(errp, err, "msi_init failed: "); - return ret; + return false; } vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); - return 0; + return true; } static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) @@ -1388,13 +1447,13 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) } } -static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) { int target_bar = -1; size_t msix_sz; - if (!vdev->msix || vdev->msix_relo == OFF_AUTOPCIBAR_OFF) { - return; + if (!vdev->msix || vdev->msix_relo == OFF_AUTO_PCIBAR_OFF) { + return true; } /* The actual minimum size of MSI-X structures */ @@ -1405,7 +1464,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) /* PCI BARs must be a power of 2 */ msix_sz = pow2ceil(msix_sz); - if (vdev->msix_relo == OFF_AUTOPCIBAR_AUTO) { + if (vdev->msix_relo == OFF_AUTO_PCIBAR_AUTO) { /* * TODO: Lookup table for known devices. * @@ -1417,17 +1476,17 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) if (target_bar < 0) { error_setg(errp, "No automatic MSI-X relocation available for " "device %04x:%04x", vdev->vendor_id, vdev->device_id); - return; + return false; } } else { - target_bar = (int)(vdev->msix_relo - OFF_AUTOPCIBAR_BAR0); + target_bar = (int)(vdev->msix_relo - OFF_AUTO_PCIBAR_BAR0); } /* I/O port BARs cannot host MSI-X structures */ if (vdev->bars[target_bar].ioport) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "I/O port BAR", target_bar); - return; + return false; } /* Cannot use a BAR in the "shadow" of a 64-bit BAR */ @@ -1435,7 +1494,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) target_bar > 0 && vdev->bars[target_bar - 1].mem64) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "consumed by 64-bit BAR %d", target_bar, target_bar - 1); - return; + return false; } /* 2GB max size for 32-bit BARs, cannot double if already > 1G */ @@ -1443,7 +1502,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) !vdev->bars[target_bar].mem64) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "no space to extend 32-bit BAR", target_bar); - return; + return false; } /* @@ -1478,6 +1537,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) trace_vfio_msix_relo(vdev->vbasedev.name, vdev->msix->table_bar, vdev->msix->table_offset); + return true; } /* @@ -1488,35 +1548,37 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) * need to first look for where the MSI-X table lives. So we * unfortunately split MSI-X setup across two functions. */ -static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) +static bool 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); if (!pos) { - return; + return true; } if (pread(fd, &ctrl, sizeof(ctrl), vdev->config_offset + pos + PCI_MSIX_FLAGS) != sizeof(ctrl)) { error_setg_errno(errp, errno, "failed to read PCI MSIX FLAGS"); - return; + return false; } if (pread(fd, &table, sizeof(table), vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) { error_setg_errno(errp, errno, "failed to read PCI MSIX TABLE"); - return; + return false; } if (pread(fd, &pba, sizeof(pba), vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) { error_setg_errno(errp, errno, "failed to read PCI MSIX PBA"); - return; + return false; } ctrl = le16_to_cpu(ctrl); @@ -1530,6 +1592,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 false; + } + + 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 @@ -1553,24 +1624,25 @@ static void vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) } else if (vfio_pci_is(vdev, PCI_VENDOR_ID_BAIDU, PCI_DEVICE_ID_KUNLUN_VF)) { msix->pba_offset = 0xb400; - } else if (vdev->msix_relo == OFF_AUTOPCIBAR_OFF) { + } else if (vdev->msix_relo == OFF_AUTO_PCIBAR_OFF) { error_setg(errp, "hardware reports invalid configuration, " "MSIX PBA outside of specified BAR"); g_free(msix); - return; + return false; } } 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); - vfio_pci_relocate_msix(vdev, errp); + return vfio_pci_relocate_msix(vdev, errp); } -static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) +static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { int ret; Error *err = NULL; @@ -1586,11 +1658,11 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) if (ret < 0) { if (ret == -ENOTSUP) { warn_report_err(err); - return 0; + return true; } error_propagate(errp, err); - return ret; + return false; } /* @@ -1624,7 +1696,7 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false); } - return 0; + return true; } static void vfio_teardown_msi(VFIOPCIDevice *vdev) @@ -1752,9 +1824,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,8 +1900,83 @@ static void vfio_add_emulated_long(VFIOPCIDevice *vdev, int pos, vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); } -static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, - Error **errp) +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 bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, + Error **errp) { uint16_t flags; uint8_t type; @@ -1841,7 +1990,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, error_setg(errp, "assignment of PCIe type 0x%x " "devices is not currently supported", type); - return -EINVAL; + return false; } if (!pci_bus_is_express(pci_get_bus(&vdev->pdev))) { @@ -1874,7 +2023,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, } if (pci_bus_is_express(bus)) { - return 0; + return true; } } else if (pci_bus_is_root(pci_get_bus(&vdev->pdev))) { @@ -1912,7 +2061,7 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, * Legacy endpoints don't belong on the root complex. Windows * seems to be happier with devices if we skip the capability. */ - return 0; + return true; } } else { @@ -1929,6 +2078,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); } /* @@ -1946,12 +2097,12 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size, errp); if (pos < 0) { - return pos; + return false; } vdev->pdev.exp.exp_cap = pos; - return pos; + return true; } static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) @@ -1984,11 +2135,34 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) } } -static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) +static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, + uint8_t size, Error **errp) { PCIDevice *pdev = &vdev->pdev; + + pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp); + if (pos < 0) { + return false; + } + + /* + * Exempt config space check for Vendor Specific Information during + * restore/load. + * Config space check is still enforced for 3 byte VSC header. + */ + if (vdev->skip_vsc_check && size > 3) { + memset(pdev->cmask + pos + 3, 0, size - 3); + } + + return true; +} + +static bool 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; + bool ret; cap_id = pdev->config[pos]; next = pdev->config[pos + PCI_CAP_LIST_NEXT]; @@ -2009,9 +2183,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) * will be changed as we unwind the stack. */ if (next) { - ret = vfio_add_std_cap(vdev, next, errp); - if (ret) { - return ret; + if (!vfio_add_std_cap(vdev, next, errp)) { + return false; } } else { /* Begin the rebuild, use QEMU emulated list bits */ @@ -2019,9 +2192,8 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) vdev->emulated_config_bits[PCI_CAPABILITY_LIST] = 0xff; vdev->emulated_config_bits[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - ret = vfio_add_virt_caps(vdev, errp); - if (ret) { - return ret; + if (!vfio_add_virt_caps(vdev, errp)) { + return false; } } @@ -2045,22 +2217,72 @@ static int vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) case PCI_CAP_ID_PM: vfio_check_pm_reset(vdev, pos); vdev->pm_cap = pos; - ret = pci_add_capability(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; break; case PCI_CAP_ID_AF: vfio_check_af_flr(vdev, pos); - ret = pci_add_capability(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; + break; + case PCI_CAP_ID_VNDR: + ret = vfio_add_vendor_specific_cap(vdev, pos, size, errp); break; default: - ret = pci_add_capability(pdev, cap_id, pos, size, errp); + ret = pci_add_capability(pdev, cap_id, pos, size, errp) >= 0; break; } - if (ret < 0) { + if (!ret) { error_prepend(errp, "failed to add PCI capability 0x%x[0x%x]@0x%x: ", cap_id, size, pos); - return ret; + } + + return ret; +} + +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; @@ -2139,9 +2361,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); } @@ -2157,26 +2383,24 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) return; } -static int vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; - int ret; if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || !pdev->config[PCI_CAPABILITY_LIST]) { - return 0; /* Nothing to add */ + return true; /* Nothing to add */ } - ret = vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp); - if (ret) { - return ret; + if (!vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST], errp)) { + return false; } vfio_add_ext_cap(vdev); - return 0; + return true; } -static void vfio_pci_pre_reset(VFIOPCIDevice *vdev) +void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; uint16_t cmd; @@ -2213,13 +2437,12 @@ 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; - vfio_intx_enable(vdev, &err); - if (err) { + if (!vfio_intx_enable(vdev, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } @@ -2237,7 +2460,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 +2470,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 +2484,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 *vioc = VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer); - 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 vioc->pci_hot_reset(vbasedev, single); } /* @@ -2477,22 +2564,54 @@ static bool vfio_msix_present(void *opaque, int version_id) return msix_present(pdev); } -const VMStateDescription vmstate_vfio_pci_config = { - .name = "VFIOPCIDevice", +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, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), - VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, vfio_msix_present), + .needed = vfio_display_migration_needed, + .fields = (const VMStateField[]){ + VMSTATE_STRUCT_POINTER(dpy, VFIOPCIDevice, vfio_display_vmstate, + VFIODisplay), VMSTATE_END_OF_LIST() } }; -static void vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f) +static const VMStateDescription vmstate_vfio_pci_config = { + .name = "VFIOPCIDevice", + .version_id = 1, + .minimum_version_id = 1, + .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 + } +}; + +static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - vmstate_save_state(f, &vmstate_vfio_pci_config, vdev, NULL); + return vmstate_save_state_with_err(f, &vmstate_vfio_pci_config, vdev, NULL, + errp); } static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) @@ -2544,10 +2663,10 @@ static VFIODeviceOps vfio_pci_ops = { .vfio_load_config = vfio_pci_load_config, }; -int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) +bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; - struct vfio_region_info *reg_info; + g_autofree struct vfio_region_info *reg_info = NULL; int ret; ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); @@ -2555,7 +2674,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) error_setg_errno(errp, -ret, "failed getting region info for VGA region index %d", VFIO_PCI_VGA_REGION_INDEX); - return ret; + return false; } if (!(reg_info->flags & VFIO_REGION_INFO_FLAG_READ) || @@ -2564,8 +2683,7 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) error_setg(errp, "unexpected VGA info, flags 0x%lx, size 0x%lx", (unsigned long)reg_info->flags, (unsigned long)reg_info->size); - g_free(reg_info); - return -EINVAL; + return false; } vdev->vga = g_new0(VFIOVGA, 1); @@ -2573,8 +2691,6 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) vdev->vga->fd_offset = reg_info->offset; vdev->vga->fd = vdev->vbasedev.fd; - g_free(reg_info); - vdev->vga->region[QEMU_PCI_VGA_MEM].offset = QEMU_PCI_VGA_MEM_BASE; vdev->vga->region[QEMU_PCI_VGA_MEM].nr = QEMU_PCI_VGA_MEM; QLIST_INIT(&vdev->vga->region[QEMU_PCI_VGA_MEM].quirks); @@ -2609,31 +2725,31 @@ int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); - return 0; + return true; } -static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) +static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; - struct vfio_region_info *reg_info; + g_autofree struct vfio_region_info *reg_info = NULL; struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info) }; int i, ret = -1; /* Sanity check device */ if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PCI)) { error_setg(errp, "this isn't a PCI device"); - return; + return false; } if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { error_setg(errp, "unexpected number of io regions %u", vbasedev->num_regions); - return; + return false; } if (vbasedev->num_irqs < VFIO_PCI_MSIX_IRQ_INDEX + 1) { error_setg(errp, "unexpected number of irqs %u", vbasedev->num_irqs); - return; + return false; } for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) { @@ -2645,7 +2761,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) if (ret) { error_setg_errno(errp, -ret, "failed to get region %d info", i); - return; + return false; } QLIST_INIT(&vdev->bars[i].quirks); @@ -2655,7 +2771,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) VFIO_PCI_CONFIG_REGION_INDEX, ®_info); if (ret) { error_setg_errno(errp, -ret, "failed to get config info"); - return; + return false; } trace_vfio_populate_device_config(vdev->vbasedev.name, @@ -2669,14 +2785,11 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) } vdev->config_offset = reg_info->offset; - g_free(reg_info); - if (vdev->features & VFIO_FEATURE_ENABLE_VGA) { - ret = vfio_populate_vga(vdev, errp); - if (ret) { + if (!vfio_populate_vga(vdev, errp)) { error_append_hint(errp, "device does not support " "requested feature x-vga\n"); - return; + return false; } } @@ -2693,14 +2806,16 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) "Could not enable error recovery for the device", vbasedev->name); } + + return true; } -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) @@ -2749,8 +2864,8 @@ static void vfio_register_err_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->err_notifier); qemu_set_fd_handler(fd, vfio_err_notifier_handler, NULL, vdev); - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->err_notifier); @@ -2766,8 +2881,8 @@ static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev) return; } - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier), @@ -2814,8 +2929,8 @@ static void vfio_register_req_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->req_notifier); qemu_set_fd_handler(fd, vfio_req_notifier_handler, NULL, vdev); - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->req_notifier); @@ -2832,8 +2947,8 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) return; } - if (vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier), @@ -2845,23 +2960,21 @@ 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; - Error *err = NULL; - ssize_t len; - struct stat st; - int groupid; int i, ret; - bool is_mdev; + char uuid[UUID_STR_LEN]; + g_autofree char *name = NULL; - 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,80 +2984,39 @@ 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)) { 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 * stays in sync with the active working set of the guest driver. Prevent * the x-balloon-allowed option unless this is minimally an mdev device. */ - tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); - subsys = realpath(tmp, NULL); - g_free(tmp); - is_mdev = subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); - free(subsys); + vbasedev->mdev = vfio_device_is_mdev(vbasedev); - trace_vfio_mdev(vbasedev->name, is_mdev); + trace_vfio_mdev(vbasedev->name, vbasedev->mdev); - if (vbasedev->ram_block_discard_allowed && !is_mdev) { + if (vbasedev->ram_block_discard_allowed && !vbasedev->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 (ret) { - vfio_put_group(group); + 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); + } + + if (!vfio_attach_device(name, vbasedev, + pci_device_iommu_address_space(pdev), errp)) { goto error; } - vfio_populate_device(vdev, &err); - if (err) { - error_propagate(errp, err); + if (!vfio_populate_device(vdev, errp)) { goto error; } @@ -3037,19 +3109,22 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bars_prepare(vdev); - vfio_msix_early_setup(vdev, &err); - if (err) { - error_propagate(errp, err); + if (!vfio_msix_early_setup(vdev, errp)) { goto error; } vfio_bars_register(vdev); - ret = vfio_add_capabilities(vdev, errp); - if (ret) { + if (!vbasedev->mdev && + !pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { + error_prepend(errp, "Failed to set iommu_device: "); goto out_teardown; } + if (!vfio_add_capabilities(vdev, errp)) { + goto out_unset_idev; + } + if (vdev->vga) { vfio_vga_quirk_setup(vdev); } @@ -3060,13 +3135,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (!vdev->igd_opregion && vdev->features & VFIO_FEATURE_ENABLE_IGD_OPREGION) { - struct vfio_region_info *opregion; + g_autofree struct vfio_region_info *opregion = NULL; if (vdev->pdev.qdev.hotplugged) { error_setg(errp, "cannot support IGD OpRegion feature on hotplugged " "device"); - goto out_teardown; + goto out_unset_idev; } ret = vfio_get_dev_region_info(vbasedev, @@ -3075,13 +3150,11 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (ret) { error_setg_errno(errp, -ret, "does not support requested IGD OpRegion feature"); - goto out_teardown; + goto out_unset_idev; } - ret = vfio_pci_igd_opregion_init(vdev, opregion, errp); - g_free(opregion); - if (ret) { - goto out_teardown; + if (!vfio_pci_igd_opregion_init(vdev, opregion, errp)) { + goto out_unset_idev; } } @@ -3103,15 +3176,13 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_intx_routing_notifier); vdev->irqchip_change_notifier.notify = vfio_irqchip_change; kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); - ret = vfio_intx_enable(vdev, errp); - if (ret) { + if (!vfio_intx_enable(vdev, errp)) { goto out_deregister; } } if (vdev->display != ON_OFF_AUTO_OFF) { - ret = vfio_display_probe(vdev, errp); - if (ret) { + if (!vfio_display_probe(vdev, errp)) { goto out_deregister; } } @@ -3130,24 +3201,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,10 +3228,20 @@ 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); 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_unset_idev: + if (!vbasedev->mdev) { + pci_device_unset_iommu_device(pdev); + } out_teardown: vfio_teardown_msi(vdev); vfio_bars_exit(vdev); @@ -3172,7 +3252,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); @@ -3185,13 +3264,13 @@ 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) { VFIOPCIDevice *vdev = VFIO_PCI(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); @@ -3204,8 +3283,12 @@ 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(vbasedev); + if (!vbasedev->mdev) { + pci_device_unset_iommu_device(pdev); + } } static void vfio_pci_reset(DeviceState *dev) @@ -3251,6 +3334,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, @@ -3260,6 +3344,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 @@ -3269,10 +3356,14 @@ 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, ON_OFF_AUTO_ON), + DEFINE_PROP_ON_OFF_AUTO("x-device-dirty-page-tracking", VFIOPCIDevice, + vbasedev.device_dirty_page_tracking, + ON_OFF_AUTO_ON), DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, display, ON_OFF_AUTO_OFF), DEFINE_PROP_UINT32("xres", VFIOPCIDevice, display_xres, 0), @@ -3285,8 +3376,10 @@ 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("migration-events", VFIOPCIDevice, + vbasedev.migration_events, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice, vbasedev.ram_block_discard_allowed, false), @@ -3310,22 +3403,32 @@ static Property vfio_pci_dev_properties[] = { nv_gpudirect_clique, 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), - */ + OFF_AUTO_PCIBAR_OFF), +#ifdef CONFIG_IOMMUFD + DEFINE_PROP_LINK("iommufd", VFIOPCIDevice, vbasedev.iommufd, + TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), +#endif + DEFINE_PROP_BOOL("skip-vsc-check", VFIOPCIDevice, skip_vsc_check, true), 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); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); - dc->reset = vfio_pci_reset; + device_class_set_legacy_reset(dc, 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; @@ -3350,6 +3453,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..5ad090a229 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,10 @@ 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; + bool skip_vsc_check; VFIODisplay *dpy; Notifier irqchip_change_notifier; }; @@ -207,23 +212,30 @@ void vfio_bar_quirk_setup(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); -int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); void vfio_quirk_reset(VFIOPCIDevice *vdev); VFIOQuirk *vfio_quirk_alloc(int nr_mem); +void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr); void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; -int vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); +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_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); +bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp); + +bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, + struct vfio_region_info *info, + Error **errp); void vfio_display_reset(VFIOPCIDevice *vdev); -int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); +bool 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..a85c199c76 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" @@ -113,18 +115,17 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp, VFIODevice *vbasedev = &intp->vdev->vbasedev; int32_t fd = event_notifier_get_fd(intp->interrupt); Error *err = NULL; - int ret; qemu_set_fd_handler(fd, (IOHandler *)handler, NULL, intp); - ret = vfio_set_irq_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err); - if (ret) { + if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); qemu_set_fd_handler(fd, NULL, NULL, NULL); + return -EINVAL; } - return ret; + return 0; } /* @@ -353,15 +354,14 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp) int32_t fd = event_notifier_get_fd(intp->unmask); VFIODevice *vbasedev = &intp->vdev->vbasedev; Error *err = NULL; - int ret; qemu_set_fd_handler(fd, NULL, NULL, NULL); - ret = vfio_set_irq_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_UNMASK, fd, &err); - if (ret) { + if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0, + VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); + return -EINVAL; } - return ret; + return 0; } /** @@ -441,7 +441,7 @@ static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev) * @errp: error object * */ -static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) +static bool vfio_populate_device(VFIODevice *vbasedev, Error **errp) { VFIOINTp *intp, *tmp; int i, ret = -1; @@ -450,7 +450,7 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) { error_setg(errp, "this isn't a platform device"); - return ret; + return false; } vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions); @@ -487,12 +487,11 @@ static int vfio_populate_device(VFIODevice *vbasedev, Error **errp) irq.flags); intp = vfio_init_intp(vbasedev, irq, errp); if (!intp) { - ret = -1; goto irq_err; } } } - return 0; + return true; irq_err: timer_del(vdev->mmap_timer); QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) { @@ -507,7 +506,7 @@ reg_error: g_free(vdev->regions[i]); } g_free(vdev->regions); - return ret; + return false; } /* specialized functions for VFIO Platform devices */ @@ -527,80 +526,37 @@ static VFIODeviceOps vfio_platform_ops = { * fd retrieval, resource query. * Precondition: the device name must be initialized */ -static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp) +static bool 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; + return false; } vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s", vbasedev->name); } - if (stat(vbasedev->sysfsdev, &st) < 0) { - error_setg_errno(errp, errno, - "failed to get the sysfs host device file status"); - return -errno; + if (!vfio_device_get_name(vbasedev, errp)) { + return false; } - 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; + if (!vfio_attach_device(vbasedev->name, vbasedev, + &address_space_memory, errp)) { + return false; } - 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; + if (vfio_populate_device(vbasedev, errp)) { + return true; } - 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); - if (ret) { - vfio_put_group(group); - return ret; - } - - ret = vfio_populate_device(vbasedev, errp); - if (ret) { - vfio_put_group(group); - } - - return ret; + vfio_detach_device(vbasedev); + return false; } /** @@ -613,14 +569,11 @@ 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; + int i; qemu_mutex_init(&vdev->intp_mutex); @@ -628,9 +581,8 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) vbasedev->sysfsdev : vbasedev->name, vdev->compat); - ret = vfio_base_device_init(vbasedev, errp); - if (ret) { - goto out; + if (!vfio_base_device_init(vbasedev, errp)) { + goto init_err; } if (!vdev->compat) { @@ -662,11 +614,9 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) } sysbus_init_mmio(sbdev, vdev->regions[i]->mem); } -out: - if (!ret) { - return; - } + return; +init_err: if (vdev->vbasedev.name) { error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); } else { @@ -686,9 +636,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 +666,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 +681,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..018bd20481 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,14 @@ #include "qapi/error.h" #include "trace.h" +typedef struct VFIOSpaprContainer { + VFIOContainer container; + MemoryListener prereg_listener; + QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; +} VFIOSpaprContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOSpaprContainer, VFIO_IOMMU_SPAPR); + static bool vfio_prereg_listener_skipped_section(MemoryRegionSection *section) { if (memory_region_is_iommu(section->mr)) { @@ -39,8 +52,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 +98,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 +112,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 +151,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 +251,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 +325,234 @@ int vfio_spapr_create_window(VFIOContainer *container, return 0; } -int vfio_spapr_remove_window(VFIOContainer *container, - hwaddr offset_within_address_space) +static bool +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 false; + } + return true; } - trace_vfio_spapr_remove_window(offset_within_address_space); + if (container->iommu_type != VFIO_SPAPR_TCE_v2_IOMMU) { + return true; + } - 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 false; + } + } + + ret = vfio_spapr_create_window(container, section, &pgsize); + if (ret) { + error_setg_errno(errp, -ret, "Failed to create SPAPR window"); + return false; + } + + 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 false; + } + trace_vfio_spapr_group_attach(param.groupfd, param.tablefd); + } + } + } +#endif + return true; } + +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 bool 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 false; + } + } else { + scontainer->prereg_listener = vfio_prereg_listener; + + memory_listener_register(&scontainer->prereg_listener, + &address_space_memory); + if (bcontainer->error) { + 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"); + 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 true; + +listener_unregister_exit: + if (v2) { + memory_listener_unregister(&scontainer->prereg_listener); + } + return false; +} + +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, + .instance_size = sizeof(VFIOSpaprContainer), + .class_init = vfio_iommu_spapr_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 73dffe9e00..cab1cf1de0 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -19,7 +19,7 @@ vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) vfio_msix_relo(const char *name, int bar, uint64_t offset) " (%s) BAR %d offset 0x%"PRIx64"" vfio_msi_enable(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" vfio_msi_disable(const char *name) " (%s)" -vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s ROM:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_pci_load_rom(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' ROM: size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_rom_read(const char *name, uint64_t addr, int size, uint64_t data) " (%s, 0x%"PRIx64", 0x%x) = 0x%"PRIx64 vfio_pci_size_rom(const char *name, int size) "%s ROM size 0x%x" vfio_vga_write(uint64_t addr, uint64_t data, int size) " (0x%"PRIx64", 0x%"PRIx64", %d)" @@ -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, uint64_t offset, int entries, bool noresize) "%s PCI MSI-X CAP @0x%x, BAR %d, offset 0x%"PRIx64", 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" @@ -35,9 +35,10 @@ vfio_pci_hot_reset(const char *name, const char *type) " (%s) %s" vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent devices:" vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" 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_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' config: 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,16 @@ 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_iommu(const char* name, uint64_t start, uint64_t end) "region_add [iommu] %s 0x%"PRIx64" - 0x%"PRIx64 +vfio_listener_region_del_iommu(const char *name) "region_del [iommu] %s" 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 +116,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 +148,35 @@ 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 %"PRIu64" ret %d" +vfio_migration_realize(const char *name) " (%s)" +vfio_migration_set_device_state(const char *name, const char *state) " (%s) state %s" +vfio_migration_set_state(const char *name, const char *new_state, const char *recover_state) " (%s) new state %s, recover 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_block_precopy_empty_hit(const char *name) " (%s)" +vfio_save_cleanup(const char *name) " (%s)" +vfio_save_complete_precopy(const char *name, int ret) " (%s) ret %d" +vfio_save_complete_precopy_start(const char *name) " (%s)" +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 %"PRIu64" precopy dirty size %"PRIu64 +vfio_save_iterate_start(const char *name) " (%s)" +vfio_save_setup(const char *name, uint64_t data_buffer_size) " (%s) data buffer size %"PRIu64 +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 %"PRIu64" postcopy %"PRIu64" precopy initial size %"PRIu64" precopy dirty size %"PRIu64 +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 %"PRIu64" postcopy %"PRIu64" stopcopy size %"PRIu64" precopy initial size %"PRIu64" precopy dirty size %"PRIu64 +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 cbfd8c7173..70c77e183d 100644 --- a/hw/virtio/Kconfig +++ b/hw/virtio/Kconfig @@ -6,6 +6,10 @@ config VIRTIO_RNG default y depends on VIRTIO +config VIRTIO_NSM + bool + depends on LIBCBOR && VIRTIO + config VIRTIO_IOMMU bool default y @@ -16,6 +20,7 @@ config VIRTIO_PCI default y if PCI_DEVICES depends on PCI select VIRTIO + select VIRTIO_MD_SUPPORTED config VIRTIO_MMIO bool @@ -35,6 +40,17 @@ config VIRTIO_CRYPTO default y depends on VIRTIO +# not all virtio transports support memory devices; if none does, +# no need to include the code +config VIRTIO_MD_SUPPORTED + bool + +config VIRTIO_MD + bool + depends on VIRTIO_MD_SUPPORTED + select MEM_DEVICE + +# selected by the board if it has the required support code config VIRTIO_PMEM_SUPPORTED bool @@ -42,9 +58,11 @@ config VIRTIO_PMEM bool default y depends on VIRTIO + depends on VIRTIO_MD_SUPPORTED depends on VIRTIO_PMEM_SUPPORTED - select MEM_DEVICE + select VIRTIO_MD +# selected by the board if it has the required support code config VIRTIO_MEM_SUPPORTED bool @@ -53,17 +71,24 @@ config VIRTIO_MEM default y depends on VIRTIO depends on LINUX + depends on VIRTIO_MD_SUPPORTED 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 @@ -85,3 +110,18 @@ 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 && ARM diff --git a/hw/virtio/cbor-helpers.c b/hw/virtio/cbor-helpers.c new file mode 100644 index 0000000000..49f55df399 --- /dev/null +++ b/hw/virtio/cbor-helpers.c @@ -0,0 +1,321 @@ +/* + * QEMU CBOR helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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 "hw/virtio/cbor-helpers.h" + +bool qemu_cbor_map_add(cbor_item_t *map, cbor_item_t *key, cbor_item_t *value) +{ + bool success = false; + struct cbor_pair pair = (struct cbor_pair) { + .key = cbor_move(key), + .value = cbor_move(value) + }; + + success = cbor_map_add(map, pair); + if (!success) { + cbor_incref(pair.key); + cbor_incref(pair.value); + } + + return success; +} + +bool qemu_cbor_array_push(cbor_item_t *array, cbor_item_t *value) +{ + bool success = false; + + success = cbor_array_push(array, cbor_move(value)); + if (!success) { + cbor_incref(value); + } + + return success; +} + +bool qemu_cbor_add_bool_to_map(cbor_item_t *map, const char *key, bool value) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_build_bool(value); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_uint8_to_map(cbor_item_t *map, const char *key, + uint8_t value) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_build_uint8(value); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_map_to_map(cbor_item_t *map, const char *key, + size_t nested_map_size, + cbor_item_t **nested_map) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_new_definite_map(nested_map_size); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + *nested_map = value_cbor; + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_bytestring_to_map(cbor_item_t *map, const char *key, + uint8_t *arr, size_t len) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_build_bytestring(arr, len); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_null_to_map(cbor_item_t *map, const char *key) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_new_null(); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_string_to_map(cbor_item_t *map, const char *key, + const char *value) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_build_string(value); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_uint8_array_to_map(cbor_item_t *map, const char *key, + uint8_t *arr, size_t len) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_new_definite_array(len); + if (!value_cbor) { + goto cleanup; + } + + for (int i = 0; i < len; ++i) { + cbor_item_t *tmp = cbor_build_uint8(arr[i]); + if (!tmp) { + goto cleanup; + } + if (!qemu_cbor_array_push(value_cbor, tmp)) { + cbor_decref(&tmp); + goto cleanup; + } + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_uint8_key_bytestring_to_map(cbor_item_t *map, uint8_t key, + uint8_t *buf, size_t len) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_uint8(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_build_bytestring(buf, len); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +bool qemu_cbor_add_uint64_to_map(cbor_item_t *map, const char *key, + uint64_t value) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + + key_cbor = cbor_build_string(key); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_build_uint64(value); + if (!value_cbor) { + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index dfed1e7af5..a5f9f7999d 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -1,44 +1,68 @@ -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')) -virtio_ss.add(when: 'CONFIG_VHOST_USER_GPIO', if_true: files('vhost-user-gpio.c')) -virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_GPIO'], if_true: files('vhost-user-gpio-pci.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_NSM', if_true: [files('virtio-nsm.c', 'cbor-helpers.c'), libcbor]) +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')) @@ -47,6 +71,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto-pc virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng-pci.c')) +virtio_pci_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('virtio-nsm-pci.c', 'cbor-helpers.c'), libcbor]) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_9P', if_true: files('virtio-9p-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_SCSI', if_true: files('virtio-scsi-pci.c')) @@ -56,12 +81,17 @@ 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_VIRTIO', if_false: files('virtio-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-stub.c')) -softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('virtio-stub.c')) +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(when: 'CONFIG_VIRTIO_MD', if_false: files('virtio-md-stubs.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 14fc5b9bb2..04e36ae047 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -30,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_dump_config(void *dev, const char *line) "dev: %p %s" +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, unsigned ofs, const char *line) "dev: %p 0x%04x: %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" @@ -69,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" @@ -106,10 +112,11 @@ 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" +virtio_iommu_detach_endpoint_from_domain(uint32_t domain_id, uint32_t ep_id) "domain=%d endpoint=%d" virtio_iommu_map(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 " phys_start=0x%"PRIx64" flags=%d" virtio_iommu_unmap(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 virtio_iommu_unmap_done(uint32_t domain_id, uint64_t virt_start, uint64_t virt_end) "domain=%d virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 @@ -125,10 +132,12 @@ virtio_iommu_fill_resv_property(uint32_t devid, uint8_t subtype, uint64_t start, virtio_iommu_notify_map(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start, uint32_t flags) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64" flags=%d" virtio_iommu_notify_unmap(const char *name, uint64_t virt_start, uint64_t virt_end) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64 virtio_iommu_remap(const char *name, uint64_t virt_start, uint64_t virt_end, uint64_t phys_start) "mr=%s virt_start=0x%"PRIx64" virt_end=0x%"PRIx64" phys_start=0x%"PRIx64 -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_update_page_size_mask(const char *name, uint64_t old, uint64_t new) "host iommu device=%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 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 8e581575c9..833804dd40 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -158,6 +158,30 @@ static int vhost_kernel_set_vring_busyloop_timeout(struct vhost_dev *dev, 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) { @@ -197,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); @@ -318,11 +337,14 @@ const VhostOps kernel_ops = { .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-shadow-virtqueue.c b/hw/virtio/vhost-shadow-virtqueue.c index d422418f2d..37aca8b431 100644 --- a/hw/virtio/vhost-shadow-virtqueue.c +++ b/hw/virtio/vhost-shadow-virtqueue.c @@ -66,7 +66,7 @@ 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->num_free; } @@ -111,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, @@ -414,6 +414,7 @@ static uint16_t vhost_svq_last_desc_of_chain(const VhostShadowVirtqueue *svq, return i; } +G_GNUC_WARN_UNUSED_RESULT static VirtQueueElement *vhost_svq_get_buf(VhostShadowVirtqueue *svq, uint32_t *len) { @@ -514,29 +515,38 @@ static void vhost_svq_flush(VhostShadowVirtqueue *svq, } /** - * Poll the SVQ for one device used buffer. + * 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. * - * Return the length written by the device. + * @svq: The svq + * @num: The number of elements that need to be used */ -size_t vhost_svq_poll(VhostShadowVirtqueue *svq) +size_t vhost_svq_poll(VhostShadowVirtqueue *svq, size_t num) { - int64_t start_us = g_get_monotonic_time(); - uint32_t len = 0; + size_t len = 0; - do { - if (vhost_svq_more_used(svq)) { - break; - } + while (num--) { + g_autofree VirtQueueElement *elem = NULL; + int64_t start_us = g_get_monotonic_time(); + uint32_t r = 0; - if (unlikely(g_get_monotonic_time() - start_us > 10e6)) { - return 0; - } - } while (true); + do { + if (vhost_svq_more_used(svq)) { + break; + } + + if (unlikely(g_get_monotonic_time() - start_us > 10e6)) { + return len; + } + } while (true); + + elem = vhost_svq_get_buf(svq, &r); + len += r; + } - vhost_svq_get_buf(svq, &len); return len; } @@ -644,29 +654,32 @@ 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)); svq->num_free = svq->vring.num; - 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->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->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++) { @@ -694,66 +707,42 @@ void vhost_svq_stop(VhostShadowVirtqueue *svq) g_autofree VirtQueueElement *elem = NULL; 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->desc_state); - qemu_vfree(svq->vring.desc); - qemu_vfree(svq->vring.used); + 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 * @ops: SVQ owner callbacks * @ops_opaque: ops opaque pointer - * - * Returns the new virtqueue or NULL. - * - * In case of error, reason is reported through error_report. */ -VhostShadowVirtqueue *vhost_svq_new(VhostIOVATree *iova_tree, - const VhostShadowVirtqueueOps *ops, +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; svq->ops = ops; svq->ops_opaque = ops_opaque; - return g_steal_pointer(&svq); - -err_init_hdev_call: - event_notifier_cleanup(&svq->hdev_kick); - -err_init_hdev_kick: - return NULL; + return svq; } /** @@ -765,8 +754,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 328a7fc075..19c842a15b 100644 --- a/hw/virtio/vhost-shadow-virtqueue.h +++ b/hw/virtio/vhost-shadow-virtqueue.h @@ -114,12 +114,13 @@ typedef struct 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 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); @@ -129,11 +130,10 @@ 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, - const VhostShadowVirtqueueOps *ops, +VhostShadowVirtqueue *vhost_svq_new(const VhostShadowVirtqueueOps *ops, void *ops_opaque); void vhost_svq_free(gpointer vq); 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..2bc3423326 --- /dev/null +++ b/hw/virtio/vhost-user-base.c @@ -0,0 +1,374 @@ +/* + * 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); + struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs; + + if (!vub->connected) { + goto done; + } + vub->connected = false; + + vub_stop(vdev); + vhost_dev_cleanup(&vub->vhost_dev); + g_free(vhost_vqs); + +done: + /* 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); + 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 d97b179e6f..32ee7f496d 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -33,7 +33,8 @@ static const int user_feature_bits[] = { VIRTIO_F_RING_PACKED, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_RING_RESET, - + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; @@ -159,6 +160,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); } @@ -166,6 +176,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); } @@ -255,6 +274,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. */ @@ -270,8 +290,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) @@ -280,9 +299,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.c b/hw/virtio/vhost-user-gpio.c index 97145376eb..9f37c25415 100644 --- a/hw/virtio/vhost-user-gpio.c +++ b/hw/virtio/vhost-user-gpio.c @@ -11,372 +11,25 @@ #include "hw/qdev-properties.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/vhost-user-gpio.h" -#include "qemu/error-report.h" #include "standard-headers/linux/virtio_ids.h" -#include "trace.h" +#include "standard-headers/linux/virtio_gpio.h" -#define REALIZE_CONNECTION_RETRIES 3 -#define VHOST_NVQS 2 - -/* Features required from VirtIO */ -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_GPIO_F_IRQ, - VIRTIO_F_RING_RESET, - VHOST_INVALID_FEATURE_BIT +static Property vgpio_properties[] = { + DEFINE_PROP_CHR("chardev", VHostUserBase, chardev), + DEFINE_PROP_END_OF_LIST(), }; -static void vu_gpio_get_config(VirtIODevice *vdev, uint8_t *config) +static void vgpio_realize(DeviceState *dev, Error **errp) { - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); + VHostUserBase *vub = VHOST_USER_BASE(dev); + VHostUserBaseClass *vubc = VHOST_USER_BASE_GET_CLASS(dev); - memcpy(config, &gpio->config, sizeof(gpio->config)); -} + /* Fixed for GPIO */ + vub->virtio_id = VIRTIO_ID_GPIO; + vub->num_vqs = 2; + vub->config_size = sizeof(struct virtio_gpio_config); -static int vu_gpio_config_notifier(struct vhost_dev *dev) -{ - VHostUserGPIO *gpio = VHOST_USER_GPIO(dev->vdev); - - memcpy(dev->vdev->config, &gpio->config, sizeof(gpio->config)); - virtio_notify_config(dev->vdev); - - return 0; -} - -const VhostDevConfigOps gpio_ops = { - .vhost_dev_config_notifier = vu_gpio_config_notifier, -}; - -static int vu_gpio_start(VirtIODevice *vdev) -{ - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - struct vhost_dev *vhost_dev = &gpio->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; - } - - /* - * Before we start up we need to ensure we have the final feature - * set needed for the vhost configuration. The backend may also - * apply backend_features when the feature set is sent. - */ - vhost_ack_features(&gpio->vhost_dev, feature_bits, vdev->guest_features); - - ret = vhost_dev_start(&gpio->vhost_dev, vdev, false); - if (ret < 0) { - error_report("Error starting vhost-user-gpio: %d", ret); - goto err_guest_notifiers; - } - gpio->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 < gpio->vhost_dev.nvqs; i++) { - vhost_virtqueue_mask(&gpio->vhost_dev, vdev, i, false); - } - - /* - * As we must have VHOST_USER_F_PROTOCOL_FEATURES (because - * VHOST_USER_GET_CONFIG requires it) we need to explicitly enable - * the vrings. - */ - g_assert(vhost_dev->vhost_ops && - vhost_dev->vhost_ops->vhost_set_vring_enable); - ret = vhost_dev->vhost_ops->vhost_set_vring_enable(vhost_dev, true); - if (ret == 0) { - return 0; - } - - error_report("Failed to start vrings for vhost-user-gpio: %d", ret); - -err_guest_notifiers: - k->set_guest_notifiers(qbus->parent, gpio->vhost_dev.nvqs, false); -err_host_notifiers: - vhost_dev_disable_notifiers(&gpio->vhost_dev, vdev); - - return ret; -} - -static void vu_gpio_stop(VirtIODevice *vdev) -{ - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - struct vhost_dev *vhost_dev = &gpio->vhost_dev; - int ret; - - if (!gpio->started_vu) { - return; - } - gpio->started_vu = false; - - if (!k->set_guest_notifiers) { - return; - } - - vhost_dev_stop(vhost_dev, vdev, false); - - 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_gpio_set_status(VirtIODevice *vdev, uint8_t status) -{ - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - bool should_start = virtio_device_should_start(vdev, status); - - trace_virtio_gpio_set_status(status); - - if (!gpio->connected) { - return; - } - - if (vhost_dev_is_started(&gpio->vhost_dev) == should_start) { - return; - } - - if (should_start) { - if (vu_gpio_start(vdev)) { - qemu_chr_fe_disconnect(&gpio->chardev); - } - } else { - vu_gpio_stop(vdev); - } -} - -static uint64_t vu_gpio_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - - return vhost_get_features(&gpio->vhost_dev, feature_bits, features); -} - -static void vu_gpio_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_gpio_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) -{ - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - - vhost_virtqueue_mask(&gpio->vhost_dev, vdev, idx, mask); -} - -static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserGPIO *gpio) -{ - virtio_delete_queue(gpio->command_vq); - virtio_delete_queue(gpio->interrupt_vq); - g_free(gpio->vhost_vqs); - virtio_cleanup(vdev); - vhost_user_cleanup(&gpio->vhost_user); -} - -static int vu_gpio_connect(DeviceState *dev, Error **errp) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - struct vhost_dev *vhost_dev = &gpio->vhost_dev; - int ret; - - if (gpio->connected) { - return 0; - } - gpio->connected = true; - - vhost_dev_set_config_notifier(vhost_dev, &gpio_ops); - gpio->vhost_user.supports_config = true; - - gpio->vhost_dev.nvqs = VHOST_NVQS; - gpio->vhost_dev.vqs = gpio->vhost_vqs; - - ret = vhost_dev_init(vhost_dev, &gpio->vhost_user, - VHOST_BACKEND_TYPE_USER, 0, errp); - if (ret < 0) { - return ret; - } - - /* restore vhost state */ - if (virtio_device_started(vdev, vdev->status)) { - vu_gpio_start(vdev); - } - - return 0; -} - -static void vu_gpio_event(void *opaque, QEMUChrEvent event); - -static void vu_gpio_disconnect(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - - if (!gpio->connected) { - return; - } - gpio->connected = false; - - vu_gpio_stop(vdev); - vhost_dev_cleanup(&gpio->vhost_dev); - - /* Re-instate the event handler for new connections */ - qemu_chr_fe_set_handlers(&gpio->chardev, - NULL, NULL, vu_gpio_event, - NULL, dev, NULL, true); -} - -static void vu_gpio_event(void *opaque, QEMUChrEvent event) -{ - DeviceState *dev = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserGPIO *gpio = VHOST_USER_GPIO(vdev); - Error *local_err = NULL; - - switch (event) { - case CHR_EVENT_OPENED: - if (vu_gpio_connect(dev, &local_err) < 0) { - qemu_chr_fe_disconnect(&gpio->chardev); - return; - } - break; - case CHR_EVENT_CLOSED: - /* defer close until later to avoid circular close */ - vhost_user_async_close(dev, &gpio->chardev, &gpio->vhost_dev, - vu_gpio_disconnect); - break; - case CHR_EVENT_BREAK: - case CHR_EVENT_MUX_IN: - case CHR_EVENT_MUX_OUT: - /* Ignore */ - break; - } -} - -static int vu_gpio_realize_connect(VHostUserGPIO *gpio, Error **errp) -{ - VirtIODevice *vdev = &gpio->parent_obj; - DeviceState *dev = &vdev->parent_obj; - struct vhost_dev *vhost_dev = &gpio->vhost_dev; - int ret; - - ret = qemu_chr_fe_wait_connected(&gpio->chardev, errp); - if (ret < 0) { - return ret; - } - - /* - * vu_gpio_connect() may have already connected (via the event - * callback) in which case it will just report success. - */ - ret = vu_gpio_connect(dev, errp); - if (ret < 0) { - qemu_chr_fe_disconnect(&gpio->chardev); - return ret; - } - g_assert(gpio->connected); - - ret = vhost_dev_get_config(vhost_dev, (uint8_t *)&gpio->config, - sizeof(gpio->config), errp); - - if (ret < 0) { - error_report("vhost-user-gpio: get config failed"); - - qemu_chr_fe_disconnect(&gpio->chardev); - vhost_dev_cleanup(vhost_dev); - return ret; - } - - return 0; -} - -static void vu_gpio_device_realize(DeviceState *dev, Error **errp) -{ - ERRP_GUARD(); - - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserGPIO *gpio = VHOST_USER_GPIO(dev); - int retries, ret; - - if (!gpio->chardev.chr) { - error_setg(errp, "vhost-user-gpio: chardev is mandatory"); - return; - } - - if (!vhost_user_init(&gpio->vhost_user, &gpio->chardev, errp)) { - return; - } - - virtio_init(vdev, VIRTIO_ID_GPIO, sizeof(gpio->config)); - - gpio->command_vq = virtio_add_queue(vdev, 256, vu_gpio_handle_output); - gpio->interrupt_vq = virtio_add_queue(vdev, 256, vu_gpio_handle_output); - gpio->vhost_vqs = g_new0(struct vhost_virtqueue, VHOST_NVQS); - - gpio->connected = false; - - qemu_chr_fe_set_handlers(&gpio->chardev, NULL, NULL, vu_gpio_event, NULL, - dev, NULL, true); - - retries = REALIZE_CONNECTION_RETRIES; - g_assert(!*errp); - do { - if (*errp) { - error_prepend(errp, "Reconnecting after error: "); - error_report_err(*errp); - *errp = NULL; - } - ret = vu_gpio_realize_connect(gpio, errp); - } while (ret < 0 && retries--); - - if (ret < 0) { - do_vhost_user_cleanup(vdev, gpio); - } - - return; -} - -static void vu_gpio_device_unrealize(DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VHostUserGPIO *gpio = VHOST_USER_GPIO(dev); - - vu_gpio_set_status(vdev, 0); - qemu_chr_fe_set_handlers(&gpio->chardev, NULL, NULL, NULL, NULL, NULL, NULL, - false); - vhost_dev_cleanup(&gpio->vhost_dev); - do_vhost_user_cleanup(vdev, gpio); + vubc->parent_realize(dev, errp); } static const VMStateDescription vu_gpio_vmstate = { @@ -384,30 +37,21 @@ static const VMStateDescription vu_gpio_vmstate = { .unmigratable = 1, }; -static Property vu_gpio_properties[] = { - DEFINE_PROP_CHR("chardev", VHostUserGPIO, chardev), - DEFINE_PROP_END_OF_LIST(), -}; - static void vu_gpio_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_gpio_properties); 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); - vdc->realize = vu_gpio_device_realize; - vdc->unrealize = vu_gpio_device_unrealize; - vdc->get_features = vu_gpio_get_features; - vdc->get_config = vu_gpio_get_config; - vdc->set_status = vu_gpio_set_status; - vdc->guest_notifier_mask = vu_gpio_guest_notifier_mask; } static const TypeInfo vu_gpio_info = { .name = TYPE_VHOST_USER_GPIO, - .parent = TYPE_VIRTIO_DEVICE, + .parent = TYPE_VHOST_USER_BASE, .instance_size = sizeof(VHostUserGPIO), .class_init = vu_gpio_class_init, }; diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index 60eaf0d95b..a464f5e039 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -14,237 +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, - VIRTIO_F_RING_RESET, - 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, true); - 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, true); - - 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 = virtio_device_should_start(vdev, status); - - if (vhost_dev_is_started(&i2c->vhost_dev) == 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); -} - -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 (vhost_dev_is_started(&i2c->vhost_dev)) { - 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) { - g_free(i2c->vhost_dev.vqs); - 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); - struct vhost_virtqueue *vhost_vqs = i2c->vhost_dev.vqs; - - /* This will stop vhost backend if appropriate. */ - vu_i2c_set_status(vdev, 0); - vhost_dev_cleanup(&i2c->vhost_dev); - g_free(vhost_vqs); - do_vhost_user_cleanup(vdev, i2c); + vubc->parent_realize(dev, errp); } static const VMStateDescription vu_i2c_vmstate = { @@ -252,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 efc54cd3fb..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,281 +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 const int feature_bits[] = { - VIRTIO_F_RING_RESET, - VHOST_INVALID_FEATURE_BIT -}; - -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, true); - 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, true); - - 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 = virtio_device_should_start(vdev, status); - - if (vhost_dev_is_started(&rng->vhost_dev) == 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) -{ - VHostUserRNG *rng = VHOST_USER_RNG(vdev); - - return vhost_get_features(&rng->vhost_dev, feature_bits, - 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 (vhost_dev_is_started(&rng->vhost_dev)) { - 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: - g_free(rng->vhost_dev.vqs); - 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); - struct vhost_virtqueue *vhost_vqs = rng->vhost_dev.vqs; - - vu_rng_set_status(vdev, 0); - - vhost_dev_cleanup(&rng->vhost_dev); - g_free(vhost_vqs); - 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 9431b9792c..da3b0e0229 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -21,6 +21,8 @@ static const int user_feature_bits[] = { VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, VHOST_INVALID_FEATURE_BIT }; diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index d92b026e1c..f170f0b25b 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,10 +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" @@ -40,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 @@ -65,27 +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_STATUS = 16, - VHOST_USER_PROTOCOL_F_MAX -}; - #define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) typedef enum VhostUserRequest { @@ -110,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, @@ -130,16 +101,22 @@ typedef enum VhostUserRequest { 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; @@ -173,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)); @@ -200,6 +188,10 @@ typedef struct VhostUserInflight { uint16_t queue_size; } VhostUserInflight; +typedef struct VhostUserShared { + unsigned char uuid[16]; +} VhostUserShared; + typedef struct { VhostUserRequest request; @@ -210,6 +202,12 @@ typedef struct { 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) @@ -224,6 +222,8 @@ typedef union { VhostUserCryptoSession session; VhostUserVringArea area; VhostUserInflight inflight; + VhostUserShared object; + VhostUserTransferDeviceState transfer_state; } VhostUserPayload; typedef struct VhostUserMsg { @@ -243,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]; @@ -272,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; @@ -365,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: @@ -373,6 +368,10 @@ 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: + case VHOST_USER_SET_LOG_BASE: return true; default: return false; @@ -388,11 +387,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; } @@ -441,6 +446,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; } @@ -476,6 +486,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; } @@ -1066,9 +1077,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, @@ -1077,20 +1174,27 @@ 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) { - assert(n && n->unmap_addr); - munmap(n->unmap_addr, qemu_real_host_page_size()); - n->unmap_addr = NULL; + if (n->unmap_addr) { + munmap(n->unmap_addr, qemu_real_host_page_size()); + n->unmap_addr = NULL; + } + if (n->destroy) { + memory_region_transaction_begin(); + object_unparent(OBJECT(&n->mr)); + memory_region_transaction_commit(); + g_free(n); + } } /* @@ -1098,23 +1202,34 @@ static void vhost_user_host_notifier_free(VhostUserHostNotifier *n) * under rcu. */ static void vhost_user_host_notifier_remove(VhostUserHostNotifier *n, - VirtIODevice *vdev) + VirtIODevice *vdev, bool destroy) { + /* + * if destroy == false and n->addr == NULL, we have nothing to do. + * so, just return. + */ + if (!n || (!destroy && !n->addr)) { + return; + } + if (n->addr) { if (vdev) { + memory_region_transaction_begin(); virtio_queue_set_host_notifier_mr(vdev, n->idx, &n->mr, false); + memory_region_transaction_commit(); } assert(!n->unmap_addr); n->unmap_addr = n->addr; n->addr = NULL; - call_rcu(n, vhost_user_host_notifier_free, rcu); } + n->destroy = destroy; + call_rcu(n, vhost_user_host_notifier_free, rcu); } static int vhost_user_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { - 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) @@ -1132,7 +1247,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 @@ -1168,9 +1297,7 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, struct vhost_user *u = dev->opaque; VhostUserHostNotifier *n = fetch_notifier(u->user, ring->index); - if (n) { - vhost_user_host_notifier_remove(n, dev->vdev); - } + vhost_user_host_notifier_remove(n, dev->vdev, false); ret = vhost_user_write(dev, &msg, NULL, 0); if (ret < 0) { @@ -1211,7 +1338,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; @@ -1238,75 +1365,9 @@ static int vhost_user_set_vring_err(struct vhost_dev *dev, return vhost_set_vring_file(dev, VHOST_USER_SET_VRING_ERR, file); } -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_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); -} - 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, @@ -1314,29 +1375,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, @@ -1348,26 +1393,8 @@ static int vhost_user_set_u64(struct vhost_dev *dev, int request, uint64_t u64, .payload.u64 = u64, .hdr.size = sizeof(msg.payload.u64), }; - 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) { - return enforce_reply(dev, &msg); - } - - return 0; + return vhost_user_write_sync(dev, &msg, wait_for_reply); } static int vhost_user_set_status(struct vhost_dev *dev, uint8_t status) @@ -1475,17 +1502,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; @@ -1522,7 +1554,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) { @@ -1546,7 +1578,7 @@ static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, * new mapped address. */ n = fetch_or_create_notifier(user, queue_idx); - vhost_user_host_notifier_remove(n, vdev); + vhost_user_host_notifier_remove(n, vdev, false); if (area->u64 & VHOST_USER_VRING_NOFD_MASK) { return 0; @@ -1584,16 +1616,170 @@ 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, +/* + * Handle VHOST_USER_BACKEND_SHARED_OBJECT_REMOVE backend requests. + * + * Return: 0 on success, 1 on error. + */ +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 1; + } + break; + } + default: + /* Not allowed to remove non-owned entries */ + return 1; + } + + 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; @@ -1631,16 +1817,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; @@ -1651,21 +1848,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; } @@ -1674,7 +1860,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: @@ -1686,10 +1872,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; @@ -1700,7 +1886,7 @@ 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; } @@ -1715,10 +1901,10 @@ static int vhost_setup_slave_channel(struct vhost_dev *dev) error_report_err(local_err); return -ECONNREFUSED; } - u->slave_ioc = ioc; - u->slave_src = qio_channel_add_watch_source(u->slave_ioc, + u->backend_ioc = ioc; + u->backend_src = qio_channel_add_watch_source(u->backend_ioc, G_IO_IN | G_IO_HUP, - slave_read, dev, NULL, NULL); + backend_read, dev, NULL, NULL); if (reply_supported) { msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK; @@ -1736,7 +1922,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; @@ -1936,7 +2122,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, @@ -1948,20 +2134,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 */ @@ -2058,11 +2244,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; } @@ -2098,7 +2284,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; @@ -2128,8 +2314,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; @@ -2188,19 +2374,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; @@ -2225,7 +2398,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); } @@ -2359,7 +2532,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, @@ -2373,16 +2546,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", @@ -2446,14 +2656,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, @@ -2552,15 +2757,7 @@ static int vhost_user_set_inflight_fd(struct vhost_dev *dev, static void vhost_user_state_destroy(gpointer data) { VhostUserHostNotifier *n = (VhostUserHostNotifier *) data; - if (n) { - vhost_user_host_notifier_remove(n, NULL); - object_unparent(OBJECT(&n->mr)); - /* - * We can't free until vhost_user_host_notifier_remove has - * done it's thing so schedule the free with RCU. - */ - g_free_rcu(n, rcu); - } + vhost_user_host_notifier_remove(n, NULL, true); } bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp) @@ -2581,9 +2778,7 @@ void vhost_user_cleanup(VhostUserState *user) if (!user->chr) { return; } - memory_region_transaction_begin(); user->notifiers = (GPtrArray *) g_ptr_array_free(user->notifiers, true); - memory_region_transaction_commit(); user->chr = NULL; } @@ -2598,16 +2793,8 @@ typedef struct { 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); - } + data->cb(data->dev); g_free(data); } @@ -2674,15 +2861,163 @@ static int vhost_user_dev_start(struct vhost_dev *dev, bool started) VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); } else { - return vhost_user_set_status(dev, 0); + 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, @@ -2701,7 +3036,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, @@ -2709,8 +3043,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 03c78d25d8..8b7bff97d3 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -14,6 +14,7 @@ #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" @@ -23,7 +24,6 @@ #include "migration/blocker.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" -#include "cpu.h" #include "trace.h" #include "qapi/error.h" @@ -31,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; } @@ -60,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; } -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)", @@ -98,18 +119,24 @@ int vhost_vdpa_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, return ret; } -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,81 +148,200 @@ int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, hwaddr size) 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; + Error *local_err = NULL; + + 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, + &local_err)) { + error_report_err(local_err); + 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) { DMAMap mem_region = {}; - struct vhost_vdpa *v = container_of(listener, struct vhost_vdpa, listener); + 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; } @@ -208,18 +354,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) { + if (s->shadow_data) { int r; 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(v->iova_tree, &mem_region); + 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; @@ -228,9 +374,9 @@ 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_map; @@ -239,8 +385,8 @@ static void vhost_vdpa_listener_region_add(MemoryListener *listener, return; fail_map: - if (v->shadow_vqs_enabled) { - vhost_iova_tree_remove(v->iova_tree, mem_region); + if (s->shadow_data) { + vhost_iova_tree_remove(s->iova_tree, mem_region); } fail: @@ -257,26 +403,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; @@ -284,7 +438,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 + @@ -294,18 +448,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, *result); + 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); @@ -326,7 +499,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); @@ -345,6 +518,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; @@ -365,17 +542,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; } /* @@ -392,6 +563,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) { @@ -402,54 +578,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; + VhostShadowVirtqueue *svq; - svq = vhost_svq_new(v->iova_tree, v->shadow_vq_ops, - v->shadow_vq_ops_opaque); - 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. @@ -460,30 +635,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, @@ -510,7 +665,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; @@ -547,9 +702,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) @@ -562,17 +726,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) @@ -580,10 +748,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)); } @@ -596,12 +760,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; } @@ -675,9 +842,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)) { @@ -694,6 +865,7 @@ static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) } dev->backend_cap = features; + v->shared->backend_cap = features; return 0; } @@ -713,7 +885,8 @@ static int vhost_vdpa_reset_device(struct vhost_dev *dev) uint8_t status = 0; 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; } @@ -725,30 +898,60 @@ 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) { - int b, len; - char line[QEMU_HEXDUMP_LINE_LEN]; + g_autoptr(GString) str = g_string_sized_new(4 * 16); + size_t b, len; - for (b = 0; b < config_len; b += 16) { - len = config_len - b; - qemu_hexdump_line(line, b, config, len, false); - trace_vhost_vdpa_dump_config(dev, line); + for (b = 0; b < config_len; b += len) { + len = MIN(config_len - b, 16); + + g_string_truncate(str, 0); + qemu_hexdump_line(str, config + b, len, 1, 4); + trace_vhost_vdpa_dump_config(dev, b, str->str); } } @@ -798,7 +1001,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); } @@ -849,11 +1055,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; @@ -861,8 +1079,18 @@ 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; } @@ -874,7 +1102,8 @@ static void vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, hwaddr addr) const DMAMap needle = { .translated_addr = addr, }; - const DMAMap *result = vhost_iova_tree_find_iova(v->iova_tree, &needle); + const DMAMap *result = vhost_iova_tree_find_iova(v->shared->iova_tree, + &needle); hwaddr size; int r; @@ -884,13 +1113,14 @@ static void vhost_vdpa_svq_unmap_ring(struct vhost_vdpa *v, hwaddr addr) } size = ROUND_UP(result->size, qemu_real_host_page_size()); - r = vhost_vdpa_dma_unmap(v, result->iova, size); + 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->iova_tree, *result); + vhost_iova_tree_remove(v->shared->iova_tree, *result); } static void vhost_vdpa_svq_unmap_rings(struct vhost_dev *dev, @@ -918,18 +1148,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; @@ -948,6 +1179,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; @@ -956,7 +1188,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) { @@ -1014,7 +1245,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; } @@ -1030,7 +1261,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; @@ -1067,7 +1298,7 @@ static void vhost_vdpa_svqs_stop(struct vhost_dev *dev) { struct vhost_vdpa *v = dev->opaque; - if (!v->shadow_vqs) { + if (!v->shadow_vqs_enabled) { return; } @@ -1076,9 +1307,35 @@ static void vhost_vdpa_svqs_stop(struct vhost_dev *dev) 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; + } + } + + vhost_vdpa_reset_device(dev); +} + static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) { struct vhost_vdpa *v = dev->opaque; @@ -1091,27 +1348,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 { + 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, @@ -1154,18 +1426,7 @@ static int vhost_vdpa_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { struct vhost_vdpa *v = dev->opaque; - VirtQueue *vq = virtio_get_queue(dev->vdev, ring->index); - /* - * vhost-vdpa devices does not support in-flight requests. Set all of them - * as available. - * - * TODO: This is ok for networking, but other kinds of devices might - * have problems with these retransmissions. - */ - while (virtqueue_rewind(vq, 1)) { - continue; - } if (v->shadow_vqs_enabled) { /* * Device vring base was set at device start. SVQ base is handled by @@ -1185,11 +1446,20 @@ static int vhost_vdpa_get_vring_base(struct vhost_dev *dev, if (v->shadow_vqs_enabled) { 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; } @@ -1212,25 +1482,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); } @@ -1285,11 +1562,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, @@ -1297,4 +1574,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 d21c72b401..fd88df2560 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -11,7 +11,7 @@ #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" @@ -22,6 +22,7 @@ const int feature_bits[] = { VIRTIO_VSOCK_F_SEQPACKET, VIRTIO_F_RING_RESET, + VIRTIO_F_RING_PACKED, VHOST_INVALID_FEATURE_BIT }; @@ -127,6 +128,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); } @@ -135,6 +145,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); } diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index aa16d584ee..3d4a5a97f4 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -111,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() }, @@ -121,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 f38997b3f6..c40f48ac4d 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -23,7 +23,7 @@ #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" @@ -43,23 +43,48 @@ do { } while (0) #endif -static struct vhost_log *vhost_log; -static struct vhost_log *vhost_log_shm; +static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX]; +static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX]; +static QLIST_HEAD(, vhost_dev) vhost_log_devs[VHOST_BACKEND_TYPE_MAX]; +/* 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, @@ -67,12 +92,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) { @@ -107,7 +132,7 @@ static void vhost_dev_sync_region(struct vhost_dev *dev, } } -static bool vhost_dev_has_iommu(struct vhost_dev *dev) +bool vhost_dev_has_iommu(struct vhost_dev *dev) { VirtIODevice *vdev = dev->vdev; @@ -125,6 +150,47 @@ static bool vhost_dev_has_iommu(struct vhost_dev *dev) } } +static inline bool vhost_dev_should_log(struct vhost_dev *dev) +{ + assert(dev->vhost_ops); + assert(dev->vhost_ops->backend_type > VHOST_BACKEND_TYPE_NONE); + assert(dev->vhost_ops->backend_type < VHOST_BACKEND_TYPE_MAX); + + return dev == QLIST_FIRST(&vhost_log_devs[dev->vhost_ops->backend_type]); +} + +static inline void vhost_dev_elect_mem_logger(struct vhost_dev *hdev, bool add) +{ + VhostBackendType backend_type; + + assert(hdev->vhost_ops); + + backend_type = hdev->vhost_ops->backend_type; + assert(backend_type > VHOST_BACKEND_TYPE_NONE); + assert(backend_type < VHOST_BACKEND_TYPE_MAX); + + if (add && !QLIST_IS_INSERTED(hdev, logdev_entry)) { + if (QLIST_EMPTY(&vhost_log_devs[backend_type])) { + QLIST_INSERT_HEAD(&vhost_log_devs[backend_type], + hdev, logdev_entry); + } else { + /* + * The first vhost_device in the list is selected as the shared + * logger to scan memory sections. Put new entry next to the head + * to avoid inadvertent change to the underlying logger device. + * This is done in order to get better cache locality and to avoid + * performance churn on the hot path for log scanning. Even when + * new devices come and go quickly, it wouldn't end up changing + * the active leading logger device at all. + */ + QLIST_INSERT_AFTER(QLIST_FIRST(&vhost_log_devs[backend_type]), + hdev, logdev_entry); + } + } else if (!add && QLIST_IS_INSERTED(hdev, logdev_entry)) { + QLIST_REMOVE(hdev, logdev_entry); + } +} + static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, MemoryRegionSection *section, hwaddr first, @@ -142,12 +208,14 @@ static int vhost_sync_dirty_bitmap(struct vhost_dev *dev, start_addr = MAX(first, start_addr); end_addr = MIN(last, end_addr); - for (i = 0; i < dev->mem->nregions; ++i) { - struct vhost_memory_region *reg = dev->mem->regions + i; - vhost_dev_sync_region(dev, section, start_addr, end_addr, - reg->guest_phys_addr, - range_get_last(reg->guest_phys_addr, - reg->memory_size)); + if (vhost_dev_should_log(dev)) { + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + vhost_dev_sync_region(dev, section, start_addr, end_addr, + reg->guest_phys_addr, + range_get_last(reg->guest_phys_addr, + reg->memory_size)); + } } for (i = 0; i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; @@ -263,6 +331,10 @@ static int vhost_set_backend_type(struct vhost_dev *dev, r = -1; } + if (r == 0) { + assert(dev->vhost_ops->backend_type == backend_type); + } + return r; } @@ -295,16 +367,22 @@ static struct vhost_log *vhost_log_alloc(uint64_t size, bool share) return log; } -static struct vhost_log *vhost_log_get(uint64_t size, bool share) +static struct vhost_log *vhost_log_get(VhostBackendType backend_type, + uint64_t size, bool share) { - struct vhost_log *log = share ? vhost_log_shm : vhost_log; + struct vhost_log *log; + + assert(backend_type > VHOST_BACKEND_TYPE_NONE); + assert(backend_type < VHOST_BACKEND_TYPE_MAX); + + log = share ? vhost_log_shm[backend_type] : vhost_log[backend_type]; if (!log || log->size != size) { log = vhost_log_alloc(size, share); if (share) { - vhost_log_shm = log; + vhost_log_shm[backend_type] = log; } else { - vhost_log = log; + vhost_log[backend_type] = log; } } else { ++log->refcnt; @@ -316,11 +394,20 @@ static struct vhost_log *vhost_log_get(uint64_t size, bool share) static void vhost_log_put(struct vhost_dev *dev, bool sync) { struct vhost_log *log = dev->log; + VhostBackendType backend_type; if (!log) { return; } + assert(dev->vhost_ops); + backend_type = dev->vhost_ops->backend_type; + + if (backend_type == VHOST_BACKEND_TYPE_NONE || + backend_type >= VHOST_BACKEND_TYPE_MAX) { + return; + } + --log->refcnt; if (log->refcnt == 0) { /* Sync only the range covered by the old log */ @@ -328,18 +415,19 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) vhost_log_sync_range(dev, 0, dev->log_size * VHOST_LOG_CHUNK - 1); } - if (vhost_log == log) { + if (vhost_log[backend_type] == log) { g_free(log->log); - vhost_log = NULL; - } else if (vhost_log_shm == log) { + vhost_log[backend_type] = NULL; + } else if (vhost_log_shm[backend_type] == log) { qemu_memfd_free(log->log, log->size * sizeof(*(log->log)), log->fd); - vhost_log_shm = NULL; + vhost_log_shm[backend_type] = NULL; } g_free(log); } + vhost_dev_elect_mem_logger(dev, false); dev->log = NULL; dev->log_size = 0; } @@ -352,7 +440,8 @@ static bool vhost_dev_log_is_shared(struct vhost_dev *dev) static inline void vhost_dev_log_resize(struct vhost_dev *dev, uint64_t size) { - struct vhost_log *log = vhost_log_get(size, vhost_dev_log_is_shared(dev)); + struct vhost_log *log = vhost_log_get(dev->vhost_ops->backend_type, + size, vhost_dev_log_is_shared(dev)); uint64_t log_base = (uintptr_t)log->log; int r; @@ -475,8 +564,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) { @@ -503,8 +591,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; } @@ -550,7 +646,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; @@ -569,7 +665,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; @@ -669,7 +772,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 @@ -702,11 +805,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 = @@ -781,7 +880,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; @@ -796,7 +894,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); @@ -804,16 +904,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? */ } @@ -841,6 +933,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) @@ -930,6 +1043,15 @@ static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) goto err_vq; } } + + /* + * At log start we select our vhost_device logger that will scan the + * memory sections and skip for the others. This is possible because + * the log is shared amongst all vhost devices for a given type of + * backend. + */ + vhost_dev_elect_mem_logger(dev, enable_log); + return 0; err_vq: for (; i >= 0; --i) { @@ -996,7 +1118,7 @@ check_dev_state: return r; } -static void vhost_log_global_start(MemoryListener *listener) +static bool vhost_log_global_start(MemoryListener *listener, Error **errp) { int r; @@ -1004,6 +1126,7 @@ static void vhost_log_global_start(MemoryListener *listener) if (r < 0) { abort(); } + return true; } static void vhost_log_global_stop(MemoryListener *listener) @@ -1291,18 +1414,6 @@ 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) { @@ -1399,6 +1510,7 @@ 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; @@ -1425,6 +1537,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) { @@ -1457,9 +1582,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) { @@ -1479,9 +1602,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; } } @@ -1496,9 +1618,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; } @@ -1531,10 +1671,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) { @@ -1545,13 +1682,47 @@ void vhost_dev_cleanup(struct vhost_dev *hdev) memset(hdev, 0, sizeof(struct vhost_dev)); } +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. @@ -1559,32 +1730,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. @@ -1594,19 +1762,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. @@ -1640,7 +1796,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); + } } } @@ -1713,62 +1930,6 @@ void vhost_dev_free_inflight(struct vhost_inflight *inflight) } } -static int vhost_dev_resize_inflight(struct vhost_inflight *inflight, - uint64_t new_size) -{ - Error *err = NULL; - int fd = -1; - void *addr = qemu_memfd_alloc("vhost-inflight", new_size, - F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL, - &fd, &err); - - if (err) { - error_report_err(err); - return -ENOMEM; - } - - vhost_dev_free_inflight(inflight); - inflight->offset = 0; - inflight->addr = addr; - inflight->fd = fd; - inflight->size = new_size; - - return 0; -} - -void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f) -{ - if (inflight->addr) { - qemu_put_be64(f, inflight->size); - qemu_put_be16(f, inflight->queue_size); - qemu_put_buffer(f, inflight->addr, inflight->size); - } else { - qemu_put_be64(f, 0); - } -} - -int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f) -{ - uint64_t size; - - size = qemu_get_be64(f); - if (!size) { - return 0; - } - - if (inflight->size != size) { - int ret = vhost_dev_resize_inflight(inflight, size); - if (ret < 0) { - return ret; - } - } - inflight->queue_size = qemu_get_be16(f); - - qemu_get_buffer(f, inflight->addr, size); - - return 0; -} - int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev) { int r; @@ -1842,7 +2003,13 @@ static int vhost_dev_set_vring_enable(struct vhost_dev *hdev, int enable) return hdev->vhost_ops->vhost_set_vring_enable(hdev, enable); } -/* Host notifiers must be enabled at this point. */ +/* + * 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; @@ -1880,11 +2047,23 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) } } + 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; hdev->log_size = vhost_get_log_size(hdev); - hdev->log = vhost_log_get(hdev->log_size, + hdev->log = vhost_log_get(hdev->vhost_ops->backend_type, + hdev->log_size, vhost_dev_log_is_shared(hdev)); log_base = (uintptr_t)hdev->log->log; r = hdev->vhost_ops->vhost_set_log_base(hdev, @@ -1894,6 +2073,7 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) VHOST_OPS_DEBUG(r, "vhost_set_log_base failed"); goto fail_log; } + vhost_dev_elect_mem_logger(hdev, true); } if (vrings) { r = vhost_dev_set_vring_enable(hdev, true); @@ -1915,10 +2095,22 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) * vhost-kernel code requires for this.*/ for (i = 0; i < hdev->nvqs; ++i) { struct vhost_virtqueue *vq = hdev->vqs + i; - vhost_device_iotlb_miss(hdev, vq->used_phys, true); + r = vhost_device_iotlb_miss(hdev, vq->used_phys, true); + if (r) { + goto fail_iotlb; + } } } + vhost_start_config_intr(hdev); return 0; +fail_iotlb: + if (vhost_dev_has_iommu(hdev) && + hdev->vhost_ops->vhost_set_iotlb_callback) { + hdev->vhost_ops->vhost_set_iotlb_callback(hdev, false); + } + if (hdev->vhost_ops->vhost_dev_start) { + hdev->vhost_ops->vhost_dev_start(hdev, false); + } fail_start: if (vrings) { vhost_dev_set_vring_enable(hdev, false); @@ -1950,6 +2142,11 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) /* 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); @@ -1965,6 +2162,9 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) 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) { @@ -1972,6 +2172,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) } memory_listener_unregister(&hdev->iommu_listener); } + vhost_stop_config_intr(hdev); vhost_log_put(hdev, true); hdev->started = false; vdev->vhost_started = false; @@ -1987,3 +2188,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..85becef03c --- /dev/null +++ b/hw/virtio/virtio-acpi.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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..afd2ad6dd6 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" @@ -168,19 +167,33 @@ static void balloon_deflate_page(VirtIOBalloon *balloon, } } +/* + * All stats upto VIRTIO_BALLOON_S_NR /must/ have a + * non-NULL name declared here, since these are used + * as keys for populating the QDict with stats + */ static const char *balloon_stat_names[] = { [VIRTIO_BALLOON_S_SWAP_IN] = "stat-swap-in", [VIRTIO_BALLOON_S_SWAP_OUT] = "stat-swap-out", [VIRTIO_BALLOON_S_MAJFLT] = "stat-major-faults", [VIRTIO_BALLOON_S_MINFLT] = "stat-minor-faults", [VIRTIO_BALLOON_S_MEMFREE] = "stat-free-memory", + [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory", [VIRTIO_BALLOON_S_CACHES] = "stat-disk-caches", [VIRTIO_BALLOON_S_HTLB_PGALLOC] = "stat-htlb-pgalloc", [VIRTIO_BALLOON_S_HTLB_PGFAIL] = "stat-htlb-pgfail", - [VIRTIO_BALLOON_S_NR] = NULL + + [VIRTIO_BALLOON_S_OOM_KILL] = "stat-oom-kills", + [VIRTIO_BALLOON_S_ALLOC_STALL] = "stat-alloc-stalls", + [VIRTIO_BALLOON_S_ASYNC_SCAN] = "stat-async-scans", + [VIRTIO_BALLOON_S_DIRECT_SCAN] = "stat-direct-scans", + [VIRTIO_BALLOON_S_ASYNC_RECLAIM] = "stat-async-reclaims", + + [VIRTIO_BALLOON_S_DIRECT_RECLAIM] = "stat-direct-reclaims", }; +G_STATIC_ASSERT(G_N_ELEMENTS(balloon_stat_names) == VIRTIO_BALLOON_S_NR); /* * reset_stats - Mark all items in the stats array as unset @@ -241,36 +254,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 +645,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 +743,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 +830,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 +842,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 +853,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 +899,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 +1009,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-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 a6dbdd32da..c3ffd8328d 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -21,7 +21,6 @@ #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" @@ -206,6 +205,7 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, int queue_index; uint32_t algo, keytype, keylen; + sreq->info.op_code = opcode; algo = ldl_le_p(&sess_req->para.algo); keytype = ldl_le_p(&sess_req->para.keytype); keylen = ldl_le_p(&sess_req->para.keylen); @@ -225,7 +225,6 @@ virtio_crypto_create_asym_session(VirtIOCrypto *vcrypto, 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; @@ -462,7 +461,7 @@ 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->flags = QCRYPTODEV_BACKEND_ALGO_TYPE__MAX; memset(&req->op_info, 0x00, sizeof(req->op_info)); } @@ -472,7 +471,7 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req) return; } - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { + if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { size_t max_len; CryptoDevBackendSymOpInfo *op_info = req->op_info.u.sym_op_info; @@ -487,7 +486,7 @@ static void virtio_crypto_free_request(VirtIOCryptoReq *req) memset(op_info, 0, sizeof(*op_info) + max_len); g_free(op_info); } - } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) { + } else if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { CryptoDevBackendAsymOpInfo *op_info = req->op_info.u.asym_op_info; if (op_info) { g_free(op_info->src); @@ -572,10 +571,10 @@ static void virtio_crypto_req_complete(void *opaque, int ret) VirtIODevice *vdev = VIRTIO_DEVICE(vcrypto); uint8_t status = -ret; - if (req->flags == CRYPTODEV_BACKEND_ALG_SYM) { + if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_SYM) { virtio_crypto_sym_input_data_helper(vdev, req, status, req->op_info.u.sym_op_info); - } else if (req->flags == CRYPTODEV_BACKEND_ALG_ASYM) { + } else if (req->flags == QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM) { virtio_crypto_akcipher_input_data_helper(vdev, req, status, req->op_info.u.asym_op_info); } @@ -635,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"); @@ -651,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; @@ -873,11 +877,14 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) opcode = ldl_le_p(&req.header.opcode); 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 = CRYPTODEV_BACKEND_ALG_SYM; + op_info->algtype = request->flags = QCRYPTODEV_BACKEND_ALGO_TYPE_SYM; ret = virtio_crypto_handle_sym_req(vcrypto, &req.u.sym_req, op_info, out_iov, out_num); @@ -887,7 +894,7 @@ virtio_crypto_handle_request(VirtIOCryptoReq *request) case VIRTIO_CRYPTO_AKCIPHER_DECRYPT: case VIRTIO_CRYPTO_AKCIPHER_SIGN: case VIRTIO_CRYPTO_AKCIPHER_VERIFY: - op_info->algtype = request->flags = CRYPTODEV_BACKEND_ALG_ASYM; + op_info->algtype = request->flags = QCRYPTODEV_BACKEND_ALGO_TYPE_ASYM; ret = virtio_crypto_handle_asym_req(vcrypto, &req.u.akcipher_req, op_info, out_iov, out_num); @@ -900,9 +907,7 @@ check_result: virtio_crypto_req_complete(request, -VIRTIO_CRYPTO_NOTSUPP); } else { ret = cryptodev_backend_crypto_operation(vcrypto->cryptodev, - request, queue_index, - virtio_crypto_req_complete, - request); + op_info); if (ret < 0) { virtio_crypto_req_complete(request, ret); } @@ -999,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_TYPE_CIPHER)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_CIPHER); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_HASH)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_HASH); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_MAC)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_MAC); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_AEAD)) { + vservices |= (1 << VIRTIO_CRYPTO_SERVICE_AEAD); + } + if (qservices & (1 << QCRYPTODEV_BACKEND_SERVICE_TYPE_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 = @@ -1052,7 +1080,8 @@ 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; } @@ -1093,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() }, @@ -1184,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); } @@ -1194,15 +1232,36 @@ 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); } static struct vhost_dev *virtio_crypto_get_vhost(VirtIODevice *vdev) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); - CryptoDevBackend *b = vcrypto->cryptodev; - CryptoDevBackendClient *cc = b->conf.peers.ccs[0]; - CryptoDevBackendVhost *vhost_crypto = cryptodev_get_vhost(cc, b, 0); + CryptoDevBackend *b; + CryptoDevBackendClient *cc; + CryptoDevBackendVhost *vhost_crypto; + + b = vcrypto->cryptodev; + if (!b) { + return NULL; + } + + cc = b->conf.peers.ccs[0]; + vhost_crypto = cryptodev_get_vhost(cc, b, 0); + if (!vhost_crypto) { + return NULL; + } + return &vhost_crypto->dev; } @@ -1231,7 +1290,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 7ef2f9dcdb..cbdfe4c591 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -37,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(), }; @@ -54,9 +54,9 @@ 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; @@ -95,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, .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 62e07ec2e4..59ef4fb217 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" @@ -64,6 +69,11 @@ typedef struct VirtIOIOMMUMapping { uint32_t flags; } VirtIOIOMMUMapping; +struct hiod_key { + PCIBus *bus; + uint8_t devfn; +}; + static inline uint16_t virtio_iommu_get_bdf(IOMMUDevice *dev) { return PCI_BUILD_BDF(pci_bus_num(dev->bus), dev->devfn); @@ -298,6 +308,7 @@ static void virtio_iommu_detach_endpoint_from_domain(VirtIOIOMMUEndpoint *ep) if (!ep->domain) { return; } + trace_virtio_iommu_detach_endpoint_from_domain(domain->id, ep->id); g_tree_foreach(domain->mappings, virtio_iommu_notify_unmap_cb, ep->iommu_mr); QLIST_REMOVE(ep, next); @@ -377,6 +388,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) { @@ -407,6 +431,7 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, 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 @@ -443,6 +468,247 @@ static AddressSpace *virtio_iommu_find_add_as(PCIBus *bus, void *opaque, return &sdev->as; } +static gboolean hiod_equal(gconstpointer v1, gconstpointer v2) +{ + const struct hiod_key *key1 = v1; + const struct hiod_key *key2 = v2; + + return (key1->bus == key2->bus) && (key1->devfn == key2->devfn); +} + +static guint hiod_hash(gconstpointer v) +{ + const struct hiod_key *key = v; + guint value = (guint)(uintptr_t)key->bus; + + return (guint)(value << 8 | key->devfn); +} + +static void hiod_destroy(gpointer v) +{ + object_unref(v); +} + +static HostIOMMUDevice * +get_host_iommu_device(VirtIOIOMMU *viommu, PCIBus *bus, int devfn) { + struct hiod_key key = { + .bus = bus, + .devfn = devfn, + }; + + return g_hash_table_lookup(viommu->host_iommu_devices, &key); +} + +/** + * 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; +} + +static int virtio_iommu_set_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, + int devfn, GList *iova_ranges, + Error **errp) +{ + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + IOMMUDevice *sdev; + int ret = -EINVAL; + + if (!sbus) { + error_setg(errp, "%s: no IOMMUPciBus found!", __func__); + return ret; + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + error_setg(errp, "%s: no IOMMUDevice found!", __func__); + return ret; + } + + if (sdev->host_resv_ranges) { + error_setg(errp, "%s virtio-iommu does not support aliased BDF", + __func__); + return ret; + } + + range_inverse_array(iova_ranges, + &sdev->host_resv_ranges, + 0, UINT64_MAX); + rebuild_resv_regions(sdev); + + return 0; +} + +static void virtio_iommu_unset_host_iova_ranges(VirtIOIOMMU *s, PCIBus *bus, + int devfn) +{ + IOMMUPciBus *sbus = g_hash_table_lookup(s->as_by_busptr, bus); + IOMMUDevice *sdev; + + if (!sbus) { + return; + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + return; + } + + g_list_free_full(g_steal_pointer(&sdev->host_resv_ranges), g_free); + g_list_free_full(sdev->resv_regions, g_free); + sdev->host_resv_ranges = NULL; + sdev->resv_regions = NULL; + add_prop_resv_regions(sdev); +} + + +static bool check_page_size_mask(VirtIOIOMMU *viommu, uint64_t new_mask, + Error **errp) +{ + uint64_t cur_mask = viommu->config.page_size_mask; + + if ((cur_mask & new_mask) == 0) { + error_setg(errp, "virtio-iommu reports a page size mask 0x%"PRIx64 + " incompatible with currently supported mask 0x%"PRIx64, + new_mask, cur_mask); + return false; + } + /* + * 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. + */ + if (viommu->granule_frozen) { + int cur_granule = ctz64(cur_mask); + + if (!(BIT_ULL(cur_granule) & new_mask)) { + error_setg(errp, + "virtio-iommu does not support frozen granule 0x%llx", + BIT_ULL(cur_granule)); + return false; + } + } + return true; +} + +static bool virtio_iommu_set_iommu_device(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *hiod, Error **errp) +{ + ERRP_GUARD(); + VirtIOIOMMU *viommu = opaque; + HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_GET_CLASS(hiod); + struct hiod_key *new_key; + GList *host_iova_ranges = NULL; + + assert(hiod); + + if (get_host_iommu_device(viommu, bus, devfn)) { + error_setg(errp, "Host IOMMU device already exists"); + return false; + } + + if (hiodc->get_iova_ranges) { + int ret; + host_iova_ranges = hiodc->get_iova_ranges(hiod); + if (!host_iova_ranges) { + return true; /* some old kernels may not support that capability */ + } + ret = virtio_iommu_set_host_iova_ranges(viommu, hiod->aliased_bus, + hiod->aliased_devfn, + host_iova_ranges, errp); + if (ret) { + goto error; + } + } + if (hiodc->get_page_size_mask) { + uint64_t new_mask = hiodc->get_page_size_mask(hiod); + + if (check_page_size_mask(viommu, new_mask, errp)) { + /* + * 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. + */ + trace_virtio_iommu_update_page_size_mask(hiod->name, + viommu->config.page_size_mask, + new_mask); + if (!viommu->granule_frozen) { + viommu->config.page_size_mask &= new_mask; + } + } else { + error_prepend(errp, "%s: ", hiod->name); + goto error; + } + } + + new_key = g_malloc(sizeof(*new_key)); + new_key->bus = bus; + new_key->devfn = devfn; + + object_ref(hiod); + g_hash_table_insert(viommu->host_iommu_devices, new_key, hiod); + g_list_free_full(host_iova_ranges, g_free); + + return true; +error: + g_list_free_full(host_iova_ranges, g_free); + return false; +} + +static void +virtio_iommu_unset_iommu_device(PCIBus *bus, void *opaque, int devfn) +{ + VirtIOIOMMU *viommu = opaque; + HostIOMMUDevice *hiod; + struct hiod_key key = { + .bus = bus, + .devfn = devfn, + }; + + hiod = g_hash_table_lookup(viommu->host_iommu_devices, &key); + if (!hiod) { + return; + } + virtio_iommu_unset_host_iova_ranges(viommu, hiod->aliased_bus, + hiod->aliased_devfn); + + g_hash_table_remove(viommu->host_iommu_devices, &key); +} + +static const PCIIOMMUOps virtio_iommu_ops = { + .get_address_space = virtio_iommu_find_add_as, + .set_iommu_device = virtio_iommu_set_iommu_device, + .unset_iommu_device = virtio_iommu_unset_iommu_device, +}; + static int virtio_iommu_attach(VirtIOIOMMU *s, struct virtio_iommu_req_attach *req) { @@ -521,6 +787,7 @@ static int virtio_iommu_detach(VirtIOIOMMU *s, if (QLIST_EMPTY(&domain->endpoint_list)) { g_tree_remove(s->domains, GUINT_TO_POINTER(domain->id)); } + g_tree_remove(s->endpoints, GUINT_TO_POINTER(ep_id)); return VIRTIO_IOMMU_S_OK; } @@ -623,29 +890,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); @@ -665,14 +933,18 @@ 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; } @@ -727,13 +999,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; @@ -751,6 +1025,9 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) iov = elem->out_sg; sz = iov_to_buf(iov, iov_cnt, 0, &head, sizeof(head)); if (unlikely(sz != sizeof(head))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read %zu bytes from command head" + "but expected %zu\n", __func__, sz, sizeof(head)); tail.status = VIRTIO_IOMMU_S_DEVERR; goto out; } @@ -775,8 +1052,7 @@ 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; } @@ -788,6 +1064,25 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) out: sz = iov_from_buf(elem->in_sg, elem->in_num, 0, buf ? buf : &tail, output_size); + if (unlikely(sz != output_size)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: wrote %zu bytes to command response" + "but response size is %zu\n", + __func__, sz, output_size); + tail.status = VIRTIO_IOMMU_S_DEVERR; + /* + * We checked that sizeof(tail) can fit to elem->in_sg at the + * beginning of the loop + */ + output_size = sizeof(tail); + g_free(buf); + buf = NULL; + sz = iov_from_buf(elem->in_sg, + elem->in_num, + 0, + &tail, + output_size); + } assert(sz == output_size); virtqueue_push(vq, elem, sz); @@ -852,17 +1147,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, }; @@ -890,10 +1187,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; @@ -1083,61 +1380,14 @@ static int virtio_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu_mr, return 0; } -/* - * The default mask (TARGET_PAGE_MASK) is the smallest supported guest granule, - * for example 0xfffffffffffff000. When an assigned device has page size - * restrictions due to the hardware IOMMU configuration, apply this restriction - * to the mask. - */ -static int virtio_iommu_set_page_size_mask(IOMMUMemoryRegion *mr, - uint64_t new_mask, - Error **errp) -{ - IOMMUDevice *sdev = container_of(mr, IOMMUDevice, iommu_mr); - VirtIOIOMMU *s = sdev->viommu; - uint64_t cur_mask = s->config.page_size_mask; - - trace_virtio_iommu_set_page_size_mask(mr->parent_obj.name, cur_mask, - 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); - return -1; - } - - /* - * After the machine is finalized, 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. - */ - if (phase_check(PHASE_MACHINE_READY)) { - int new_granule = ctz64(new_mask); - 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); - 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; - } - - s->config.page_size_mask &= new_mask; - return 0; -} - 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 @@ -1147,6 +1397,16 @@ static void virtio_iommu_system_reset(void *opaque) } +static void virtio_iommu_freeze_granule(Notifier *notifier, void *data) +{ + VirtIOIOMMU *s = container_of(notifier, VirtIOIOMMU, machine_done); + int granule; + + 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) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -1154,8 +1414,6 @@ 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); @@ -1165,8 +1423,32 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) * in vfio realize */ s->config.bypass = s->boot_bypass; - s->config.page_size_mask = TARGET_PAGE_MASK; - s->config.input_range.end = UINT64_MAX; + 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; @@ -1184,12 +1466,18 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) s->as_by_busptr = g_hash_table_new_full(NULL, NULL, NULL, g_free); + s->host_iommu_devices = g_hash_table_new_full(hiod_hash, hiod_equal, + g_free, hiod_destroy); + 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); } @@ -1199,6 +1487,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) { @@ -1247,7 +1536,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() \ @@ -1259,7 +1548,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() \ @@ -1284,7 +1573,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() } @@ -1295,7 +1584,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, @@ -1347,7 +1636,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), @@ -1360,15 +1649,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(), }; @@ -1399,7 +1692,6 @@ static void virtio_iommu_memory_region_class_init(ObjectClass *klass, imrc->translate = virtio_iommu_translate; 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; } 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..9ec5067662 --- /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, &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-md-stubs.c b/hw/virtio/virtio-md-stubs.c new file mode 100644 index 0000000000..ce5bba0c9d --- /dev/null +++ b/hw/virtio/virtio-md-stubs.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/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index 5c5c1e3ae3..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,17 +108,57 @@ 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); @@ -111,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) @@ -125,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 @@ -137,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 56db586c89..80ada89551 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 @@ -79,6 +88,7 @@ static uint32_t virtio_mem_default_thp_size(void) static uint32_t thp_size; #define HPAGE_PMD_SIZE_PATH "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" +#define HPAGE_PATH "/sys/kernel/mm/transparent_hugepage/" static uint32_t virtio_mem_thp_size(void) { gchar *content = NULL; @@ -89,6 +99,12 @@ static uint32_t virtio_mem_thp_size(void) return thp_size; } + /* No THP -> no restrictions. */ + if (!g_file_test(HPAGE_PATH, G_FILE_TEST_EXISTS)) { + thp_size = VIRTIO_MEM_MIN_BLOCK_SIZE; + return thp_size; + } + /* * Try to probe the actual THP size, fallback to (sane but eventually * incorrect) default sizes. @@ -133,7 +149,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 */ @@ -172,13 +188,13 @@ static bool virtio_mem_is_busy(void) * after plugging them) until we're running on the destination (as we didn't * migrate these blocks when they were unplugged). */ - return migration_in_incoming_postcopy() || !migration_is_idle(); + return migration_in_incoming_postcopy() || migration_is_running(); } -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 +218,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); @@ -245,7 +285,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); @@ -277,7 +317,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 +353,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 +369,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); @@ -346,7 +386,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, 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 +413,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 +497,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 +599,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; - qemu_prealloc_mem(fd, area, size, 1, NULL, &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 +672,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 +738,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 +792,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); @@ -724,6 +890,9 @@ static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features, if (vmem->unplugged_inaccessible == ON_OFF_AUTO_ON) { virtio_add_feature(&features, VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE); } + if (qemu_wakeup_suspend_enabled()) { + virtio_add_feature(&features, VIRTIO_MEM_F_PERSISTENT_SUSPEND); + } return features; } @@ -736,16 +905,47 @@ static int virtio_mem_validate_features(VirtIODevice *vdev) return 0; } -static void virtio_mem_system_reset(void *opaque) +static void virtio_mem_prepare_mr(VirtIOMEM *vmem) { - VirtIOMEM *vmem = VIRTIO_MEM(opaque); + const uint64_t region_size = memory_region_size(&vmem->memdev->mr); - /* - * During usual resets, we will unplug all memory and shrink the usable - * region size. This is, however, not possible in all scenarios. Then, - * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL). - */ - virtio_mem_unplug_all(vmem); + 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) @@ -772,6 +972,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 +1019,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 +1068,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,9 +1096,32 @@ 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)); - qemu_register_reset(virtio_mem_system_reset, vmem); + if (vmem->early_migration) { + vmstate_register_any(VMSTATE_IF(vmem), + &vmstate_virtio_mem_device_early, vmem); + } + qemu_register_resettable(OBJECT(vmem)); /* * Set ourselves as RamDiscardManager before the plug handler maps the @@ -892,7 +1141,11 @@ static void virtio_mem_device_unrealize(DeviceState *dev) * found via an address space anymore. Unset ourselves. */ memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); - qemu_unregister_reset(virtio_mem_system_reset, vmem); + qemu_unregister_resettable(OBJECT(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 +1154,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 +1169,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 +1205,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 +1239,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 +1339,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 +1366,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 +1375,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 +1432,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 +1455,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 +1564,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 +1626,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 +1633,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 +1665,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 +1688,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 +1722,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,12 +1817,62 @@ 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 some" + " of its memory is still plugged"); + return; + } + if (vmem->requested_size) { + error_setg(errp, "virtio-mem device cannot get unplugged while" + " '" VIRTIO_MEM_REQUESTED_SIZE_PROP "' != '0'"); + return; + } +} + +static ResettableState *virtio_mem_get_reset_state(Object *obj) +{ + VirtIOMEM *vmem = VIRTIO_MEM(obj); + return &vmem->reset_state; +} + +static void virtio_mem_system_reset_hold(Object *obj, ResetType type) +{ + VirtIOMEM *vmem = VIRTIO_MEM(obj); + + /* + * When waking up from standby/suspend-to-ram, do not unplug any memory. + */ + if (type == RESET_TYPE_WAKEUP) { + return; + } + + /* + * During usual resets, we will unplug all memory and shrink the usable + * region size. This is, however, not possible in all scenarios. Then, + * the guest has to deal with this manually (VIRTIO_MEM_REQ_UNPLUG_ALL). + */ + virtio_mem_unplug_all(vmem); +} + static void virtio_mem_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); VirtIOMEMClass *vmc = VIRTIO_MEM_CLASS(klass); RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_props(dc, virtio_mem_properties); dc->vmsd = &vmstate_virtio_mem; @@ -1354,8 +1887,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; @@ -1363,6 +1899,9 @@ static void virtio_mem_class_init(ObjectClass *klass, void *data) rdmc->replay_discarded = virtio_mem_rdm_replay_discarded; rdmc->register_listener = virtio_mem_rdm_register_listener; rdmc->unregister_listener = virtio_mem_rdm_unregister_listener; + + rc->get_state = virtio_mem_get_reset_state; + rc->phases.hold = virtio_mem_system_reset_hold; } static const TypeInfo virtio_mem_info = { @@ -1370,6 +1909,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 d240efef97..e3366fe54c 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -248,6 +248,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint16_t vq_idx; trace_virtio_mmio_write_offset(offset, value); @@ -354,6 +355,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; @@ -406,8 +408,14 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, } break; case VIRTIO_MMIO_QUEUE_NOTIFY: - if (value < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, value); + vq_idx = value; + if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + VirtQueue *vq = virtio_get_queue(vdev, vq_idx); + + virtio_queue_set_shadow_avail_idx(vq, (value >> 16) & 0xFFFF); + } + virtio_queue_notify(vdev, vq_idx); } break; case VIRTIO_MMIO_INTERRUPT_ACK: @@ -564,7 +572,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), @@ -578,7 +586,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, @@ -591,10 +599,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 } @@ -670,7 +678,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) { @@ -692,6 +723,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; @@ -733,10 +768,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; @@ -759,7 +790,7 @@ static void virtio_mmio_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = virtio_mmio_realizefn; - dc->reset = virtio_mmio_reset; + device_class_set_legacy_reset(dc, virtio_mmio_reset); set_bit(DEVICE_CATEGORY_MISC, dc->categories); device_class_set_props(dc, virtio_mmio_properties); } @@ -802,10 +833,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-nsm-pci.c b/hw/virtio/virtio-nsm-pci.c new file mode 100644 index 0000000000..dca797315a --- /dev/null +++ b/hw/virtio/virtio-nsm-pci.c @@ -0,0 +1,73 @@ +/* + * AWS Nitro Secure Module (NSM) device + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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-pci.h" +#include "hw/virtio/virtio-nsm.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "qom/object.h" + +typedef struct VirtIONsmPCI VirtIONsmPCI; + +#define TYPE_VIRTIO_NSM_PCI "virtio-nsm-pci-base" +DECLARE_INSTANCE_CHECKER(VirtIONsmPCI, VIRTIO_NSM_PCI, + TYPE_VIRTIO_NSM_PCI) + +struct VirtIONsmPCI { + VirtIOPCIProxy parent_obj; + VirtIONSM vdev; +}; + +static void virtio_nsm_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIONsmPCI *vnsm = VIRTIO_NSM_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&vnsm->vdev); + + virtio_pci_force_virtio_1(vpci_dev); + + if (!qdev_realize(vdev, BUS(&vpci_dev->bus), errp)) { + return; + } +} + +static void virtio_nsm_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + + k->realize = virtio_nsm_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static void virtio_nsm_initfn(Object *obj) +{ + VirtIONsmPCI *dev = VIRTIO_NSM_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_NSM); +} + +static const VirtioPCIDeviceTypeInfo virtio_nsm_pci_info = { + .base_name = TYPE_VIRTIO_NSM_PCI, + .generic_name = "virtio-nsm-pci", + .instance_size = sizeof(VirtIONsmPCI), + .instance_init = virtio_nsm_initfn, + .class_init = virtio_nsm_pci_class_init, +}; + +static void virtio_nsm_pci_register(void) +{ + virtio_pci_types_register(&virtio_nsm_pci_info); +} + +type_init(virtio_nsm_pci_register) diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c new file mode 100644 index 0000000000..a3db8eef3e --- /dev/null +++ b/hw/virtio/virtio-nsm.c @@ -0,0 +1,1732 @@ +/* + * AWS Nitro Secure Module (NSM) device + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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/guest-random.h" +#include "qapi/error.h" + +#include "crypto/hash.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-nsm.h" +#include "hw/virtio/cbor-helpers.h" +#include "standard-headers/linux/virtio_ids.h" + +#define NSM_REQUEST_MAX_SIZE 0x1000 +#define NSM_RESPONSE_BUF_SIZE 0x3000 +#define NSM_RND_BUF_SIZE 256 + +enum NSMResponseTypes { + NSM_SUCCESS = 0, + NSM_INVALID_ARGUMENT = 1, + NSM_INVALID_INDEX = 2, + NSM_READONLY_INDEX = 3, + NSM_INVALID_OPERATION = 4, + NSM_BUFFER_TOO_SMALL = 5, + NSM_INPUT_TOO_LARGE = 6, + NSM_INTERNAL_ERROR = 7, +}; + +static const char *error_string(enum NSMResponseTypes type) +{ + const char *str; + switch (type) { + case NSM_INVALID_ARGUMENT: + str = "InvalidArgument"; + break; + case NSM_INVALID_INDEX: + str = "InvalidIndex"; + break; + case NSM_READONLY_INDEX: + str = "ReadOnlyIndex"; + break; + case NSM_INVALID_OPERATION: + str = "InvalidOperation"; + break; + case NSM_BUFFER_TOO_SMALL: + str = "BufferTooSmall"; + break; + case NSM_INPUT_TOO_LARGE: + str = "InputTooLarge"; + break; + default: + str = "InternalError"; + break; + } + + return str; +} + +/* + * Error response structure: + * + * { + * Map(1) { + * key = String("Error"), + * value = String(error_name) + * } + * } + * + * where error_name can be one of the following: + * InvalidArgument + * InvalidIndex + * InvalidResponse + * ReadOnlyIndex + * InvalidOperation + * BufferTooSmall + * InputTooLarge + * InternalError + */ + +static bool error_response(struct iovec *response, enum NSMResponseTypes error, + Error **errp) +{ + cbor_item_t *root; + size_t len; + bool r = false; + + root = cbor_new_definite_map(1); + if (!root) { + goto err; + } + + if (!qemu_cbor_add_string_to_map(root, "Error", error_string(error))) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + error_setg(errp, "Response buffer is small for %s response", + error_string(error)); + goto out; + } + response->iov_len = len; + r = true; + + out: + if (root) { + cbor_decref(&root); + } + return r; + + err: + error_setg(errp, "Failed to initialize %s response", error_string(error)); + goto out; +} + +/* + * GetRandom response structure: + * + * { + * Map(1) { + * key = String("GetRandom"), + * value = Map(1) { + * key = String("random"), + * value = Byte_String() + * } + * } + * } + */ +static bool handle_get_random(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root, *nested_map; + size_t len; + uint8_t rnd[NSM_RND_BUF_SIZE]; + bool r = false; + + root = cbor_new_definite_map(1); + if (!root) { + goto err; + } + + if (!qemu_cbor_add_map_to_map(root, "GetRandom", 1, &nested_map)) { + goto err; + } + + qemu_guest_getrandom_nofail(rnd, NSM_RND_BUF_SIZE); + + if (!qemu_cbor_add_bytestring_to_map(nested_map, "random", rnd, + NSM_RND_BUF_SIZE)) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) { + r = true; + } + goto out; + } + + response->iov_len = len; + r = true; + + out: + if (root) { + cbor_decref(&root); + } + return r; + + err: + error_setg(errp, "Failed to initialize GetRandom response"); + goto out; +} + +/* + * DescribeNSM response structure: + * + * { + * Map(1) { + * key = String("DescribeNSM"), + * value = Map(7) { + * key = String("digest"), + * value = String("SHA384"), + * key = String("max_pcrs"), + * value = Uint8(32), + * key = String("module_id"), + * value = String("i-1234-enc5678"), + * key = String("locked_pcrs"), + * value = Array(), + * key = String("version_major"), + * value = Uint8(1), + * key = String("version_minor"), + * value = Uint8(0), + * key = String("version_patch"), + * value = Uint8(0) + * } + * } + * } + */ +static bool handle_describe_nsm(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root, *nested_map; + uint16_t locked_pcrs_cnt = 0; + uint8_t locked_pcrs_ind[NSM_MAX_PCRS]; + size_t len; + bool r = false; + + root = cbor_new_definite_map(1); + if (!root) { + goto err; + } + + if (!qemu_cbor_add_map_to_map(root, "DescribeNSM", 7, &nested_map)) { + goto err; + } + + if (!qemu_cbor_add_string_to_map(nested_map, "digest", vnsm->digest)) { + goto err; + } + + if (!qemu_cbor_add_uint8_to_map(nested_map, "max_pcrs", vnsm->max_pcrs)) { + goto err; + } + + if (!qemu_cbor_add_string_to_map(nested_map, "module_id", + vnsm->module_id)) { + goto err; + } + + for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) { + if (vnsm->pcrs[i].locked) { + locked_pcrs_ind[locked_pcrs_cnt++] = i; + } + } + if (!qemu_cbor_add_uint8_array_to_map(nested_map, "locked_pcrs", + locked_pcrs_ind, locked_pcrs_cnt)) { + goto err; + } + + if (!qemu_cbor_add_uint8_to_map(nested_map, "version_major", + vnsm->version_major)) { + goto err; + } + + if (!qemu_cbor_add_uint8_to_map(nested_map, "version_minor", + vnsm->version_minor)) { + goto err; + } + + if (!qemu_cbor_add_uint8_to_map(nested_map, "version_patch", + vnsm->version_patch)) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) { + r = true; + } + goto out; + } + + response->iov_len = len; + r = true; + + out: + if (root) { + cbor_decref(&root); + } + return r; + + err: + error_setg(errp, "Failed to initialize DescribeNSM response"); + goto out; +} + +/* + * DescribePCR request structure: + * + * { + * Map(1) { + * key = String("DescribePCR"), + * value = Map(1) { + * key = String("index"), + * value = Uint8(pcr) + * } + * } + * } + */ +typedef struct NSMDescribePCRReq { + uint8_t index; +} NSMDescribePCRReq; + +static enum NSMResponseTypes get_nsm_describe_pcr_req( + uint8_t *req, size_t len, + NSMDescribePCRReq *nsm_req) +{ + size_t size; + uint8_t *str; + struct cbor_pair *pair; + cbor_item_t *item = NULL; + struct cbor_load_result result; + enum NSMResponseTypes r = NSM_INVALID_OPERATION; + + item = cbor_load(req, len, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + goto cleanup; + } + + pair = cbor_map_handle(item); + if (!cbor_isa_map(pair->value)) { + goto cleanup; + } + size = cbor_map_size(pair->value); + if (size < 1) { + goto cleanup; + } + + pair = cbor_map_handle(pair->value); + for (int i = 0; i < size; ++i) { + if (!cbor_isa_string(pair[i].key)) { + continue; + } + + str = cbor_string_handle(pair[i].key); + if (str && cbor_string_length(pair[i].key) == 5 && + memcmp(str, "index", 5) == 0) { + if (!cbor_isa_uint(pair[i].value) || + cbor_int_get_width(pair[i].value) != CBOR_INT_8) { + break; + } + + nsm_req->index = cbor_get_uint8(pair[i].value); + r = NSM_SUCCESS; + break; + } + } + + cleanup: + if (item) { + cbor_decref(&item); + } + return r; +} + +/* + * DescribePCR response structure: + * + * { + * Map(1) { + * key = String("DescribePCR"), + * value = Map(2) { + * key = String("data"), + * value = Byte_String(), + * key = String("lock"), + * value = Bool() + * } + * } + * } + */ +static bool handle_describe_pcr(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root = NULL; + cbor_item_t *nested_map; + size_t len; + NSMDescribePCRReq nsm_req; + enum NSMResponseTypes type; + struct PCRInfo *pcr; + bool r = false; + + type = get_nsm_describe_pcr_req(request->iov_base, request->iov_len, + &nsm_req); + if (type != NSM_SUCCESS) { + if (error_response(response, type, errp)) { + r = true; + } + goto out; + } + if (nsm_req.index >= vnsm->max_pcrs) { + if (error_response(response, NSM_INVALID_INDEX, errp)) { + r = true; + } + goto out; + } + pcr = &(vnsm->pcrs[nsm_req.index]); + + root = cbor_new_definite_map(1); + if (!root) { + goto err; + } + + if (!qemu_cbor_add_map_to_map(root, "DescribePCR", 2, &nested_map)) { + goto err; + } + + if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data, + QCRYPTO_HASH_DIGEST_LEN_SHA384)) { + goto err; + } + + if (!qemu_cbor_add_bool_to_map(nested_map, "lock", pcr->locked)) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) { + r = true; + } + goto out; + } + + response->iov_len = len; + r = true; + + out: + if (root) { + cbor_decref(&root); + } + return r; + + err: + error_setg(errp, "Failed to initialize DescribePCR response"); + goto out; +} + +/* + * ExtendPCR request structure: + * + * { + * Map(1) { + * key = String("ExtendPCR"), + * value = Map(2) { + * key = String("index"), + * value = Uint8(pcr), + * key = String("data"), + * value = Byte_String(data), + * } + * } + * } + */ +typedef struct NSMExtendPCRReq { + uint8_t index; + uint16_t data_len; + uint8_t data[NSM_REQUEST_MAX_SIZE]; +} NSMExtendPCRReq; + +static enum NSMResponseTypes get_nsm_extend_pcr_req(uint8_t *req, size_t len, + NSMExtendPCRReq *nsm_req) +{ + cbor_item_t *item = NULL; + size_t size ; + uint8_t *str; + bool index_found = false; + bool data_found = false; + struct cbor_pair *pair; + struct cbor_load_result result; + enum NSMResponseTypes r = NSM_INVALID_OPERATION; + + item = cbor_load(req, len, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + goto cleanup; + } + + pair = cbor_map_handle(item); + if (!cbor_isa_map(pair->value)) { + goto cleanup; + } + size = cbor_map_size(pair->value); + if (size < 2) { + goto cleanup; + } + + pair = cbor_map_handle(pair->value); + for (int i = 0; i < size; ++i) { + if (!cbor_isa_string(pair[i].key)) { + continue; + } + str = cbor_string_handle(pair[i].key); + if (!str) { + continue; + } + + if (cbor_string_length(pair[i].key) == 5 && + memcmp(str, "index", 5) == 0) { + if (!cbor_isa_uint(pair[i].value) || + cbor_int_get_width(pair[i].value) != CBOR_INT_8) { + goto cleanup; + } + nsm_req->index = cbor_get_uint8(pair[i].value); + index_found = true; + continue; + } + + if (cbor_string_length(pair[i].key) == 4 && + memcmp(str, "data", 4) == 0) { + if (!cbor_isa_bytestring(pair[i].value)) { + goto cleanup; + } + str = cbor_bytestring_handle(pair[i].value); + if (!str) { + goto cleanup; + } + nsm_req->data_len = cbor_bytestring_length(pair[i].value); + /* + * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as + * we already check for the max request size before processing + * any request. So it's safe to copy. + */ + memcpy(nsm_req->data, str, nsm_req->data_len); + data_found = true; + continue; + } + } + + if (index_found && data_found) { + r = NSM_SUCCESS; + } + + cleanup: + if (item) { + cbor_decref(&item); + } + return r; +} + +/* + * ExtendPCR response structure: + * + * { + * Map(1) { + * key = String("ExtendPCR"), + * value = Map(1) { + * key = String("data"), + * value = Byte_String() + * } + * } + * } + */ +static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root = NULL; + cbor_item_t *nested_map; + size_t len; + struct PCRInfo *pcr; + enum NSMResponseTypes type; + bool r = false; + g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq)); + + type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len, + nsm_req); + if (type != NSM_SUCCESS) { + if (error_response(response, type, errp)) { + r = true; + } + goto out; + } + if (nsm_req->index >= vnsm->max_pcrs) { + if (error_response(response, NSM_INVALID_INDEX, errp)) { + r = true; + } + goto out; + } + + pcr = &(vnsm->pcrs[nsm_req->index]); + + if (pcr->locked) { + if (error_response(response, NSM_READONLY_INDEX, errp)) { + r = true; + } + goto out; + } + + if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data, + nsm_req->data_len)) { + if (error_response(response, NSM_INTERNAL_ERROR, errp)) { + r = true; + } + goto out; + } + + root = cbor_new_definite_map(1); + if (!root) { + goto err; + } + + if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) { + goto err; + } + + if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data, + QCRYPTO_HASH_DIGEST_LEN_SHA384)) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) { + r = true; + } + goto out; + } + + response->iov_len = len; + r = true; + + out: + if (root) { + cbor_decref(&root); + } + return r; + + err: + error_setg(errp, "Failed to initialize DescribePCR response"); + goto out; +} + +/* + * LockPCR request structure: + * + * { + * Map(1) { + * key = String("LockPCR"), + * value = Map(1) { + * key = String("index"), + * value = Uint8(pcr) + * } + * } + * } + */ +typedef struct NSMLockPCRReq { + uint8_t index; +} NSMLockPCRReq; + +static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len, + NSMLockPCRReq *nsm_req) +{ + cbor_item_t *item = NULL; + size_t size; + uint8_t *str; + struct cbor_pair *pair; + struct cbor_load_result result; + enum NSMResponseTypes r = NSM_INVALID_OPERATION; + + item = cbor_load(req, len, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + goto cleanup; + } + + pair = cbor_map_handle(item); + if (!cbor_isa_map(pair->value)) { + goto cleanup; + } + size = cbor_map_size(pair->value); + if (size < 1) { + goto cleanup; + } + + pair = cbor_map_handle(pair->value); + for (int i = 0; i < size; ++i) { + if (!cbor_isa_string(pair[i].key)) { + continue; + } + str = cbor_string_handle(pair[i].key); + if (str && cbor_string_length(pair[i].key) == 5 && + memcmp(str, "index", 5) == 0) { + if (!cbor_isa_uint(pair[i].value) || + cbor_int_get_width(pair[i].value) != CBOR_INT_8) { + break; + } + + nsm_req->index = cbor_get_uint8(pair[i].value); + r = NSM_SUCCESS; + break; + } + } + + cleanup: + if (item) { + cbor_decref(&item); + } + return r; +} + +/* + * LockPCR success response structure: + * { + * String("LockPCR") + * } + */ +static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root = NULL; + size_t len; + NSMLockPCRReq nsm_req; + enum NSMResponseTypes type; + struct PCRInfo *pcr; + bool r = false; + + type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req); + if (type != NSM_SUCCESS) { + if (error_response(response, type, errp)) { + r = true; + } + goto cleanup; + } + if (nsm_req.index >= vnsm->max_pcrs) { + if (error_response(response, NSM_INVALID_INDEX, errp)) { + r = true; + } + goto cleanup; + } + + pcr = &(vnsm->pcrs[nsm_req.index]); + + if (pcr->locked) { + if (error_response(response, NSM_READONLY_INDEX, errp)) { + r = true; + } + goto cleanup; + } + + pcr->locked = true; + + root = cbor_build_string("LockPCR"); + if (!root) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) { + r = true; + } + goto cleanup; + } + + response->iov_len = len; + r = true; + goto cleanup; + + err: + error_setg(errp, "Failed to initialize LockPCR response"); + + cleanup: + if (root) { + cbor_decref(&root); + } + return r; +} + +/* + * LockPCRs request structure: + * + * { + * Map(1) { + * key = String("LockPCRs"), + * value = Map(1) { + * key = String("range"), + * value = Uint8(pcr) + * } + * } + * } + */ +typedef struct NSMLockPCRsReq { + uint16_t range; +} NSMLockPCRsReq; + +static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len, + NSMLockPCRsReq *nsm_req) +{ + cbor_item_t *item = NULL; + size_t size; + uint8_t *str; + struct cbor_pair *pair; + struct cbor_load_result result; + enum NSMResponseTypes r = NSM_INVALID_OPERATION; + + item = cbor_load(req, len, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + goto cleanup; + } + + pair = cbor_map_handle(item); + if (!cbor_isa_map(pair->value)) { + goto cleanup; + } + size = cbor_map_size(pair->value); + if (size < 1) { + goto cleanup; + } + + pair = cbor_map_handle(pair->value); + for (int i = 0; i < size; ++i) { + if (!cbor_isa_string(pair[i].key)) { + continue; + } + str = cbor_string_handle(pair[i].key); + if (str && cbor_string_length(pair[i].key) == 5 && + memcmp(str, "range", 5) == 0) { + if (!cbor_isa_uint(pair[i].value) || + cbor_int_get_width(pair[i].value) != CBOR_INT_8) { + break; + } + + nsm_req->range = cbor_get_uint8(pair[i].value); + r = NSM_SUCCESS; + break; + } + } + + cleanup: + if (item) { + cbor_decref(&item); + } + return r; +} + +/* + * LockPCRs success response structure: + * { + * String("LockPCRs") + * } + */ +static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root = NULL; + size_t len; + NSMLockPCRsReq nsm_req; + enum NSMResponseTypes type; + bool r = false; + + type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req); + if (type != NSM_SUCCESS) { + if (error_response(response, type, errp)) { + r = true; + } + goto cleanup; + } + if (nsm_req.range > vnsm->max_pcrs) { + if (error_response(response, NSM_INVALID_INDEX, errp)) { + r = true; + } + goto cleanup; + } + + for (int i = 0; i < nsm_req.range; ++i) { + vnsm->pcrs[i].locked = true; + } + + root = cbor_build_string("LockPCRs"); + if (!root) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) { + r = true; + } + goto cleanup; + } + + response->iov_len = len; + r = true; + goto cleanup; + + err: + error_setg(errp, "Failed to initialize response"); + + cleanup: + if (root) { + cbor_decref(&root); + } + return r; +} + +/* + * Attestation request structure: + * + * Map(1) { + * key = String("Attestation"), + * value = Map(3) { + * key = String("user_data"), + * value = Byte_String() || null, // Optional + * key = String("nonce"), + * value = Byte_String() || null, // Optional + * key = String("public_key"), + * value = Byte_String() || null, // Optional + * } + * } + * } + */ + +struct AttestationProperty { + bool is_null; /* True if property is not present in map or is null */ + uint16_t len; + uint8_t buf[NSM_REQUEST_MAX_SIZE]; +}; + +typedef struct NSMAttestationReq { + struct AttestationProperty public_key; + struct AttestationProperty user_data; + struct AttestationProperty nonce; +} NSMAttestationReq; + +static bool fill_attestation_property(struct AttestationProperty *prop, + cbor_item_t *value) +{ + uint8_t *str; + bool ret = false; + + if (cbor_is_null(value)) { + prop->is_null = true; + ret = true; + goto out; + } else if (cbor_isa_bytestring(value)) { + str = cbor_bytestring_handle(value); + if (!str) { + goto out; + } + prop->len = cbor_bytestring_length(value); + } else if (cbor_isa_string(value)) { + str = cbor_string_handle(value); + if (!str) { + goto out; + } + prop->len = cbor_string_length(value); + } else { + goto out; + } + + /* + * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we + * already check for the max request size before processing + * any request. So it's safe to copy. + */ + memcpy(prop->buf, str, prop->len); + prop->is_null = false; + ret = true; + + out: + return ret; +} + +static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len, + NSMAttestationReq *nsm_req) +{ + cbor_item_t *item = NULL; + size_t size; + uint8_t *str; + struct cbor_pair *pair; + struct cbor_load_result result; + enum NSMResponseTypes r = NSM_INVALID_OPERATION; + + nsm_req->public_key.is_null = true; + nsm_req->user_data.is_null = true; + nsm_req->nonce.is_null = true; + + item = cbor_load(req, len, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + goto cleanup; + } + + pair = cbor_map_handle(item); + if (!cbor_isa_map(pair->value)) { + goto cleanup; + } + size = cbor_map_size(pair->value); + if (size == 0) { + r = NSM_SUCCESS; + goto cleanup; + } + + pair = cbor_map_handle(pair->value); + for (int i = 0; i < size; ++i) { + if (!cbor_isa_string(pair[i].key)) { + continue; + } + + str = cbor_string_handle(pair[i].key); + if (!str) { + continue; + } + + if (cbor_string_length(pair[i].key) == 10 && + memcmp(str, "public_key", 10) == 0) { + if (!fill_attestation_property(&(nsm_req->public_key), + pair[i].value)) { + goto cleanup; + } + continue; + } + + if (cbor_string_length(pair[i].key) == 9 && + memcmp(str, "user_data", 9) == 0) { + if (!fill_attestation_property(&(nsm_req->user_data), + pair[i].value)) { + goto cleanup; + } + continue; + } + + if (cbor_string_length(pair[i].key) == 5 && + memcmp(str, "nonce", 5) == 0) { + if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) { + goto cleanup; + } + continue; + } + } + + r = NSM_SUCCESS; + + cleanup: + if (item) { + cbor_decref(&item); + } + return r; +} + +static bool add_protected_header_to_cose(cbor_item_t *cose) +{ + cbor_item_t *map = NULL; + cbor_item_t *key = NULL; + cbor_item_t *value = NULL; + cbor_item_t *bs = NULL; + size_t len; + bool r = false; + size_t buf_len = 4096; + g_autofree uint8_t *buf = g_malloc(buf_len); + + map = cbor_new_definite_map(1); + if (!map) { + goto cleanup; + } + key = cbor_build_uint8(1); + if (!key) { + goto cleanup; + } + value = cbor_new_int8(); + if (!value) { + goto cleanup; + } + cbor_mark_negint(value); + /* we don't actually sign the data, so we use -1 as the 'alg' value */ + cbor_set_uint8(value, 0); + + if (!qemu_cbor_map_add(map, key, value)) { + goto cleanup; + } + + len = cbor_serialize(map, buf, buf_len); + if (len == 0) { + goto cleanup_map; + } + + bs = cbor_build_bytestring(buf, len); + if (!bs) { + goto cleanup_map; + } + if (!qemu_cbor_array_push(cose, bs)) { + cbor_decref(&bs); + goto cleanup_map; + } + r = true; + goto cleanup_map; + + cleanup: + if (key) { + cbor_decref(&key); + } + if (value) { + cbor_decref(&value); + } + + cleanup_map: + if (map) { + cbor_decref(&map); + } + return r; +} + +static bool add_unprotected_header_to_cose(cbor_item_t *cose) +{ + cbor_item_t *map = cbor_new_definite_map(0); + if (!map) { + goto cleanup; + } + if (!qemu_cbor_array_push(cose, map)) { + goto cleanup; + } + + return true; + + cleanup: + if (map) { + cbor_decref(&map); + } + return false; +} + +static bool add_ca_bundle_to_payload(cbor_item_t *map) +{ + cbor_item_t *key_cbor = NULL; + cbor_item_t *value_cbor = NULL; + cbor_item_t *bs = NULL; + uint8_t zero[64] = {0}; + + key_cbor = cbor_build_string("cabundle"); + if (!key_cbor) { + goto cleanup; + } + value_cbor = cbor_new_definite_array(1); + if (!value_cbor) { + goto cleanup; + } + bs = cbor_build_bytestring(zero, 64); + if (!bs) { + goto cleanup; + } + if (!qemu_cbor_array_push(value_cbor, bs)) { + cbor_decref(&bs); + goto cleanup; + } + if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) { + goto cleanup; + } + + return true; + + cleanup: + if (key_cbor) { + cbor_decref(&key_cbor); + } + if (value_cbor) { + cbor_decref(&value_cbor); + } + return false; +} + +static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm, + NSMAttestationReq *req) +{ + cbor_item_t *root = NULL; + cbor_item_t *nested_map; + cbor_item_t *bs = NULL; + size_t locked_cnt; + uint8_t ind[NSM_MAX_PCRS]; + size_t payload_map_size = 9; + size_t len; + struct PCRInfo *pcr; + uint8_t zero[64] = {0}; + bool r = false; + size_t buf_len = 16384; + g_autofree uint8_t *buf = g_malloc(buf_len); + + root = cbor_new_definite_map(payload_map_size); + if (!root) { + goto cleanup; + } + if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) { + goto cleanup; + } + if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) { + goto cleanup; + } + if (!qemu_cbor_add_uint64_to_map(root, "timestamp", + (uint64_t) time(NULL) * 1000)) { + goto cleanup; + } + + locked_cnt = 0; + for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) { + if (vnsm->pcrs[i].locked) { + ind[locked_cnt++] = i; + } + } + if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) { + goto cleanup; + } + for (uint8_t i = 0; i < locked_cnt; ++i) { + pcr = &(vnsm->pcrs[ind[i]]); + if (!qemu_cbor_add_uint8_key_bytestring_to_map( + nested_map, ind[i], + pcr->data, + QCRYPTO_HASH_DIGEST_LEN_SHA384)) { + goto cleanup; + } + } + if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) { + goto cleanup; + } + if (!add_ca_bundle_to_payload(root)) { + goto cleanup; + } + + if (req->public_key.is_null) { + if (!qemu_cbor_add_null_to_map(root, "public_key")) { + goto cleanup; + } + } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key", + req->public_key.buf, + req->public_key.len)) { + goto cleanup; + } + + if (req->user_data.is_null) { + if (!qemu_cbor_add_null_to_map(root, "user_data")) { + goto cleanup; + } + } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data", + req->user_data.buf, + req->user_data.len)) { + goto cleanup; + } + + if (req->nonce.is_null) { + if (!qemu_cbor_add_null_to_map(root, "nonce")) { + goto cleanup; + } + } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce", + req->nonce.buf, + req->nonce.len)) { + goto cleanup; + } + + len = cbor_serialize(root, buf, buf_len); + if (len == 0) { + goto cleanup; + } + + bs = cbor_build_bytestring(buf, len); + if (!bs) { + goto cleanup; + } + if (!qemu_cbor_array_push(cose, bs)) { + cbor_decref(&bs); + goto cleanup; + } + + r = true; + + cleanup: + if (root) { + cbor_decref(&root); + } + return r; +} + +static bool add_signature_to_cose(cbor_item_t *cose) +{ + cbor_item_t *bs = NULL; + uint8_t zero[64] = {0}; + + /* we don't actually sign the data, so we just put 64 zero bytes */ + bs = cbor_build_bytestring(zero, 64); + if (!bs) { + goto cleanup; + } + + if (!qemu_cbor_array_push(cose, bs)) { + goto cleanup; + } + + return true; + + cleanup: + if (bs) { + cbor_decref(&bs); + } + return false; +} + +/* + * Attestation response structure: + * + * { + * Map(1) { + * key = String("Attestation"), + * value = Map(1) { + * key = String("document"), + * value = Byte_String() + * } + * } + * } + * + * The document is a serialized COSE sign1 blob of the structure: + * { + * Array(4) { + * [0] { ByteString() }, // serialized protected header + * [1] { Map(0) }, // 0 length map + * [2] { ByteString() }, // serialized payload + * [3] { ByteString() }, // signature + * } + * } + * + * where [0] protected header is a serialized CBOR blob of the structure: + * { + * Map(1) { + * key = Uint8(1) // alg + * value = NegativeInt8() // Signing algorithm + * } + * } + * + * [2] payload is serialized CBOR blob of the structure: + * { + * Map(9) { + * [0] { key = String("module_id"), value = String(module_id) }, + * [1] { key = String("digest"), value = String("SHA384") }, + * [2] { + * key = String("timestamp"), + * value = Uint64(unix epoch of when document was created) + * }, + * [3] { + * key = String("pcrs"), + * value = Map(locked_pcr_cnt) { + * key = Uint8(pcr_index), + * value = ByteString(pcr_data) + * }, + * }, + * [4] { + * key = String("certificate"), + * value = ByteString(Signing certificate) + * }, + * [5] { key = String("cabundle"), value = Array(N) { ByteString()... } }, + * [6] { key = String("public_key"), value = ByteString() || null }, + * [7] { key = String("user_data"), value = ByteString() || null}, + * [8] { key = String("nonce"), value = ByteString() || null}, + * } + * } + */ +static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp) +{ + cbor_item_t *root = NULL; + cbor_item_t *cose = NULL; + cbor_item_t *nested_map; + size_t len; + enum NSMResponseTypes type; + bool r = false; + size_t buf_len = 16384; + g_autofree uint8_t *buf = g_malloc(buf_len); + g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq)); + + nsm_req->public_key.is_null = true; + nsm_req->user_data.is_null = true; + nsm_req->nonce.is_null = true; + + type = get_nsm_attestation_req(request->iov_base, request->iov_len, + nsm_req); + if (type != NSM_SUCCESS) { + if (error_response(response, type, errp)) { + r = true; + } + goto out; + } + + cose = cbor_new_definite_array(4); + if (!cose) { + goto err; + } + if (!add_protected_header_to_cose(cose)) { + goto err; + } + if (!add_unprotected_header_to_cose(cose)) { + goto err; + } + if (!add_payload_to_cose(cose, vnsm, nsm_req)) { + goto err; + } + if (!add_signature_to_cose(cose)) { + goto err; + } + + len = cbor_serialize(cose, buf, buf_len); + if (len == 0) { + goto err; + } + + root = cbor_new_definite_map(1); + if (!root) { + goto err; + } + if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) { + goto err; + } + if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) { + goto err; + } + + len = cbor_serialize(root, response->iov_base, response->iov_len); + if (len == 0) { + if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) { + r = true; + } + goto out; + } + + response->iov_len = len; + r = true; + + out: + if (root) { + cbor_decref(&root); + } + if (cose) { + cbor_decref(&cose); + } + return r; + + err: + error_setg(errp, "Failed to initialize Attestation response"); + goto out; +} + +enum CBOR_ROOT_TYPE { + CBOR_ROOT_TYPE_STRING = 0, + CBOR_ROOT_TYPE_MAP = 1, +}; + +struct nsm_cmd { + char name[16]; + /* + * There are 2 types of request + * 1) String(); "GetRandom", "DescribeNSM" + * 2) Map(1) { key: String(), value: ... } + */ + enum CBOR_ROOT_TYPE root_type; + bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request, + struct iovec *response, Error **errp); +}; + +const struct nsm_cmd nsm_cmds[] = { + { "GetRandom", CBOR_ROOT_TYPE_STRING, handle_get_random }, + { "DescribeNSM", CBOR_ROOT_TYPE_STRING, handle_describe_nsm }, + { "DescribePCR", CBOR_ROOT_TYPE_MAP, handle_describe_pcr }, + { "ExtendPCR", CBOR_ROOT_TYPE_MAP, handle_extend_pcr }, + { "LockPCR", CBOR_ROOT_TYPE_MAP, handle_lock_pcr }, + { "LockPCRs", CBOR_ROOT_TYPE_MAP, handle_lock_pcrs }, + { "Attestation", CBOR_ROOT_TYPE_MAP, handle_attestation }, +}; + +static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len) +{ + size_t size; + uint8_t *req; + enum CBOR_ROOT_TYPE root_type; + struct cbor_load_result result; + cbor_item_t *item = cbor_load(buf, len, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + goto cleanup; + } + + if (cbor_isa_string(item)) { + size = cbor_string_length(item); + req = cbor_string_handle(item); + root_type = CBOR_ROOT_TYPE_STRING; + } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) { + struct cbor_pair *handle = cbor_map_handle(item); + if (cbor_isa_string(handle->key)) { + size = cbor_string_length(handle->key); + req = cbor_string_handle(handle->key); + root_type = CBOR_ROOT_TYPE_MAP; + } else { + goto cleanup; + } + } else { + goto cleanup; + } + + if (size == 0 || req == NULL) { + goto cleanup; + } + + for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) { + if (nsm_cmds[i].root_type == root_type && + strlen(nsm_cmds[i].name) == size && + memcmp(nsm_cmds[i].name, req, size) == 0) { + cbor_decref(&item); + return &nsm_cmds[i]; + } + } + + cleanup: + if (item) { + cbor_decref(&item); + } + return NULL; +} + +static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req, + struct iovec *resp, Error **errp) +{ + const struct nsm_cmd *cmd; + + if (req->iov_len > NSM_REQUEST_MAX_SIZE) { + if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) { + return true; + } + error_setg(errp, "Failed to initialize InputTooLarge response"); + return false; + } + + cmd = get_nsm_request_cmd(req->iov_base, req->iov_len); + + if (cmd == NULL) { + if (error_response(resp, NSM_INVALID_OPERATION, errp)) { + return true; + } + error_setg(errp, "Failed to initialize InvalidOperation response"); + return false; + } + + return cmd->response_fn(vnsm, req, resp, errp); +} + +static void handle_input(VirtIODevice *vdev, VirtQueue *vq) +{ + g_autofree VirtQueueElement *out_elem = NULL; + g_autofree VirtQueueElement *in_elem = NULL; + VirtIONSM *vnsm = VIRTIO_NSM(vdev); + Error *err = NULL; + size_t sz; + struct iovec req = {.iov_base = NULL, .iov_len = 0}; + struct iovec res = {.iov_base = NULL, .iov_len = 0}; + + out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!out_elem) { + /* nothing in virtqueue */ + return; + } + + sz = iov_size(out_elem->out_sg, out_elem->out_num); + if (sz == 0) { + virtio_error(vdev, "Expected non-zero sized request buffer in " + "virtqueue"); + goto cleanup; + } + + in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!in_elem) { + virtio_error(vdev, "Expected response buffer after request buffer " + "in virtqueue"); + goto cleanup; + } + if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) { + virtio_error(vdev, "Expected response buffer of length 0x3000"); + goto cleanup; + } + + req.iov_base = g_malloc(sz); + req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0, + req.iov_base, sz); + if (req.iov_len != sz) { + virtio_error(vdev, "Failed to copy request buffer"); + goto cleanup; + } + + res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE); + res.iov_len = NSM_RESPONSE_BUF_SIZE; + + if (!get_nsm_request_response(vnsm, &req, &res, &err)) { + error_report_err(err); + virtio_error(vdev, "Failed to get NSM request response"); + goto cleanup; + } + + sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base, + res.iov_len); + if (sz != res.iov_len) { + virtio_error(vdev, "Failed to copy response buffer"); + goto cleanup; + } + + g_free(req.iov_base); + g_free(res.iov_base); + virtqueue_push(vq, out_elem, 0); + virtqueue_push(vq, in_elem, in_elem->in_sg->iov_len); + virtio_notify(vdev, vq); + return; + + cleanup: + g_free(req.iov_base); + g_free(res.iov_base); + if (out_elem) { + virtqueue_detach_element(vq, out_elem, 0); + } + if (in_elem) { + virtqueue_detach_element(vq, in_elem, 0); + } + return; +} + +static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) +{ + return f; +} + +static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len) +{ + Error *err = NULL; + struct PCRInfo *pcr = &(vnsm->pcrs[ind]); + size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + uint8_t *ptr = result; + struct iovec iov[2] = { + { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 }, + { .iov_base = data, .iov_len = len }, + }; + + if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len, + &err) < 0) { + return false; + } + + memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384); + return true; +} + +static void lock_pcr(VirtIONSM *vnsm, int ind) +{ + vnsm->pcrs[ind].locked = true; +} + +static void virtio_nsm_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIONSM *vnsm = VIRTIO_NSM(dev); + + vnsm->max_pcrs = NSM_MAX_PCRS; + vnsm->digest = (char *) "SHA384"; + if (vnsm->module_id == NULL) { + vnsm->module_id = (char *) "i-234-enc5678"; + } + vnsm->version_major = 1; + vnsm->version_minor = 0; + vnsm->version_patch = 0; + vnsm->extend_pcr = extend_pcr; + vnsm->lock_pcr = lock_pcr; + + virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0); + + vnsm->vq = virtio_add_queue(vdev, 2, handle_input); +} + +static void virtio_nsm_device_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + + virtio_del_queue(vdev, 0); + virtio_cleanup(vdev); +} + +static const VMStateDescription vmstate_pcr_info_entry = { + .name = "pcr_info_entry", + .minimum_version_id = 1, + .version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(locked, struct PCRInfo), + VMSTATE_UINT8_ARRAY(data, struct PCRInfo, + QCRYPTO_HASH_DIGEST_LEN_SHA384), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_virtio_nsm_device = { + .name = "virtio-nsm-device", + .minimum_version_id = 1, + .version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1, + vmstate_pcr_info_entry, struct PCRInfo), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_virtio_nsm = { + .name = "virtio-nsm", + .minimum_version_id = 1, + .version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static Property virtio_nsm_properties[] = { + DEFINE_PROP_STRING("module-id", VirtIONSM, module_id), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_nsm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + device_class_set_props(dc, virtio_nsm_properties); + dc->vmsd = &vmstate_virtio_nsm; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + vdc->realize = virtio_nsm_device_realize; + vdc->unrealize = virtio_nsm_device_unrealize; + vdc->get_features = get_features; + vdc->vmsd = &vmstate_virtio_nsm_device; +} + +static const TypeInfo virtio_nsm_info = { + .name = TYPE_VIRTIO_NSM, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIONSM), + .class_init = virtio_nsm_class_init, +}; + +static void virtio_register_types(void) +{ + type_register_static(&virtio_nsm_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index a1c9dfa7bb..5a394821da 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" @@ -96,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), @@ -119,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), @@ -134,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 } @@ -224,6 +225,90 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) 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); @@ -247,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; @@ -258,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); @@ -276,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); @@ -310,7 +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; + uint16_t vector, vq_idx; hwaddr pa; switch (addr) { @@ -334,8 +408,14 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->queue_sel = val; break; case VIRTIO_PCI_QUEUE_NOTIFY: - if (val < VIRTIO_QUEUE_MAX) { - virtio_queue_notify(vdev, val); + vq_idx = val; + if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) { + if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) { + VirtQueue *vq = virtio_get_queue(vdev, vq_idx); + + virtio_queue_set_shadow_avail_idx(vq, val >> 16); + } + virtio_queue_notify(vdev, vq_idx); } break; case VIRTIO_PCI_STATUS: @@ -535,8 +615,12 @@ static MemoryRegion *virtio_address_space_lookup(VirtIOPCIProxy *proxy, reg = &proxy->regs[i]; if (*off >= reg->offset && *off + len <= reg->offset + reg->size) { - *off -= reg->offset; - return ®->mr; + MemoryRegionSection mrs = memory_region_find(®->mr, + *off - reg->offset, len); + assert(mrs.mr); + *off = mrs.offset_within_region; + memory_region_unref(mrs.mr); + return mrs.mr; } } @@ -631,6 +715,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) { @@ -644,6 +760,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); @@ -659,15 +779,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); } } } @@ -683,15 +803,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); } } @@ -699,7 +819,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]; @@ -728,112 +847,149 @@ 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 (!proxy->vector_irqfd && vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) + return -1; + + 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) { + return ret; + } + /* + * 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); + return ret; + } + } + + return 0; +} +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; @@ -860,14 +1016,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); @@ -878,7 +1035,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); } } @@ -888,6 +1045,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) { @@ -896,7 +1054,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; } @@ -904,15 +1063,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); @@ -925,18 +1095,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, @@ -949,19 +1126,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); @@ -970,6 +1145,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, @@ -978,17 +1181,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); } @@ -1031,10 +1242,13 @@ static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) 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; } @@ -1050,20 +1264,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) { @@ -1076,15 +1300,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; } @@ -1191,6 +1423,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) { @@ -1319,7 +1601,8 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, } 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)) { @@ -1346,6 +1629,7 @@ 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: vector = virtio_queue_vector(vdev, vdev->queue_sel); @@ -1358,7 +1642,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr, } 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) { @@ -1677,7 +1961,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 @@ -1729,6 +2013,9 @@ 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, @@ -1770,6 +2057,8 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) if (modern_pio) { memory_region_init(&proxy->io_bar, OBJECT(proxy), "virtio-pci-io", 0x4); + address_space_init(&proxy->modern_cfg_io_as, &proxy->io_bar, + "virtio-pci-cfg-io-as"); pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar_idx, PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar); @@ -1848,10 +2137,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; @@ -1897,6 +2182,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) /* PCI BAR regions must be powers of 2 */ pow2ceil(proxy->notify.offset + proxy->notify.size)); + address_space_init(&proxy->modern_cfg_mem_as, &proxy->modern_bar, + "virtio-pci-cfg-mem-as"); + if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) { proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } @@ -1946,6 +2234,11 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pcie_cap_lnkctl_init(pci_dev); } + if (proxy->flags & VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET) { + pci_set_word(pci_dev->config + pos + PCI_PM_CTRL, + PCI_PM_CTRL_NO_SOFT_RESET); + } + if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) { /* Init Power Management Control Register */ pci_set_word(pci_dev->wmask + pos + PCI_PM_CTRL, @@ -1981,12 +2274,17 @@ static void virtio_pci_exit(PCIDevice *pci_dev) VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); bool pcie_port = pci_bus_is_express(pci_get_bus(pci_dev)) && !pci_bus_is_root(pci_get_bus(pci_dev)); + bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; msix_uninit_exclusive_bar(pci_dev); if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port && pci_is_express(pci_dev)) { pcie_aer_exit(pci_dev); } + address_space_destroy(&proxy->modern_cfg_mem_as); + if (modern_pio) { + address_space_destroy(&proxy->modern_cfg_io_as); + } } static void virtio_pci_reset(DeviceState *qdev) @@ -2008,17 +2306,46 @@ static void virtio_pci_reset(DeviceState *qdev) } } -static void virtio_pci_bus_reset(DeviceState *qdev) +static bool virtio_pci_no_soft_reset(PCIDevice *dev) { - PCIDevice *dev = PCI_DEVICE(qdev); + uint16_t pmcsr; + + if (!pci_is_express(dev) || !dev->exp.pm_cap) { + return false; + } + + pmcsr = pci_get_word(dev->config + dev->exp.pm_cap + PCI_PM_CTRL); + + /* + * When No_Soft_Reset bit is set and the device + * is in D3hot state, don't reset device + */ + return (pmcsr & PCI_PM_CTRL_NO_SOFT_RESET) && + (pmcsr & PCI_PM_CTRL_STATE_MASK) == 3; +} + +static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) +{ + PCIDevice *dev = PCI_DEVICE(obj); + DeviceState *qdev = DEVICE(obj); + + if (virtio_pci_no_soft_reset(dev)) { + return; + } virtio_pci_reset(qdev); if (pci_is_express(dev)) { + VirtIOPCIProxy *proxy = VIRTIO_PCI(dev); + pcie_cap_deverr_reset(dev); pcie_cap_lnkctl_reset(dev); - pci_set_word(dev->config + dev->exp.pm_cap + PCI_PM_CTRL, 0); + if (proxy->flags & VIRTIO_PCI_FLAG_INIT_PM) { + pci_word_test_and_clear_mask( + dev->config + dev->exp.pm_cap + PCI_PM_CTRL, + PCI_PM_CTRL_STATE_MASK); + } } } @@ -2045,6 +2372,8 @@ static Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_INIT_LNKCTL_BIT, true), DEFINE_PROP_BIT("x-pcie-pm-init", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_INIT_PM_BIT, true), + DEFINE_PROP_BIT("x-pcie-pm-no-soft-reset", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET_BIT, false), DEFINE_PROP_BIT("x-pcie-flr-init", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_INIT_FLR_BIT, true), DEFINE_PROP_BIT("aer", VirtIOPCIProxy, flags, @@ -2066,11 +2395,20 @@ static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) vpciklass->parent_dc_realize(qdev, errp); } +static int virtio_pci_sync_config(DeviceState *dev, Error **errp) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(dev); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + + return qdev_sync_config(DEVICE(vdev), errp); +} + 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; @@ -2080,7 +2418,8 @@ 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_bus_reset; + rc->phases.hold = virtio_pci_bus_reset_hold; + dc->sync_config = virtio_pci_sync_config; } static const TypeInfo virtio_pci_info = { @@ -2138,6 +2477,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 7d9f4ec189..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); } @@ -111,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..cccc6fe761 --- /dev/null +++ b/hw/virtio/virtio-qmp.c @@ -0,0 +1,840 @@ +/* + * 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"), + FEATURE_ENTRY(VIRTIO_MEM_F_PERSISTENT_SUSPEND, \ + "VIRTIO_MEM_F_PERSISTENT_SUSPND: Plugged memory will remain " + "plugged when suspending+resuming"), + { -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.c b/hw/virtio/virtio-rng.c index 7e12fc03bf..7cf31da071 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -184,8 +184,9 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) /* Workaround: Property parsing does not enforce unsigned integers, * So this is a hack to reject such numbers. */ - if (vrng->conf.max_bytes > INT64_MAX) { - error_setg(errp, "'max-bytes' parameter must be non-negative, " + if (vrng->conf.max_bytes == 0 || + vrng->conf.max_bytes > INT64_MAX) { + error_setg(errp, "'max-bytes' parameter must be positive, " "and less than 2^63"); return; } @@ -242,7 +243,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.c b/hw/virtio/virtio.c index 384c8f0f08..f12c4aa81e 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -13,19 +13,18 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" #include "qapi/qapi-commands-virtio.h" -#include "qapi/qapi-commands-qom.h" -#include "qapi/qapi-visit-virtio.h" -#include "qapi/qmp/qjson.h" -#include "cpu.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 "exec/tswap.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" @@ -33,6 +32,8 @@ #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" @@ -45,422 +46,12 @@ #include "standard-headers/linux/virtio_iommu.h" #include "standard-headers/linux/virtio_mem.h" #include "standard-headers/linux/virtio_vsock.h" -#include CONFIG_DEVICES - -/* QAPI list of realized VirtIODevices */ -static QTAILQ_HEAD(, VirtIODevice) virtio_list; /* * Maximum size of virtio device config space */ #define VHOST_USER_MAX_CONFIG_SIZE 256 -#define FEATURE_ENTRY(name, desc) (qmp_virtio_feature_map_t) \ - { .virtio_bit = name, .feature_desc = desc } - -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, - VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS = 14, - VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS = 15, - VHOST_USER_PROTOCOL_F_MAX -}; - -/* Virtio transport features mapping */ -static 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"), - /* 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 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_SLAVE_REQ, \ - "VHOST_USER_PROTOCOL_F_SLAVE_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_SLAVE_SEND_FD, \ - "VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD: Slave 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"), - { -1, "" } -}; - -/* virtio device configuration statuses */ -static 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 */ -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"), -#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, "" } -}; - -/* virtio-serial features mapping */ -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, "" } -}; - -/* virtio-gpu features mapping */ -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, "" } -}; - -/* virtio-input features mapping */ -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, "" } -}; - -/* virtio-net features mapping */ -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_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, "" } -}; - -/* virtio-scsi features mapping */ -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 suppoted"), - 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, "" } -}; - -/* virtio/vhost-user-fs features mapping */ -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, "" } -}; - -/* virtio/vhost-user-i2c features mapping */ -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, "" } -}; - -/* virtio/vhost-vsock features mapping */ -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, "" } -}; - -/* virtio-balloon features mapping */ -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, "" } -}; - -/* virtio-crypto features mapping */ -qmp_virtio_feature_map_t virtio_crypto_feature_map[] = { - FEATURE_ENTRY(VHOST_F_LOG_ALL, \ - "VHOST_F_LOG_ALL: Logging write descriptors supported"), - { -1, "" } -}; - -/* virtio-iommu features mapping */ -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, "" } -}; - -/* virtio-mem features mapping */ -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, "" } -}; - -/* virtio-rng features mapping */ -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, "" } -}; - /* * The alignment to use between consumer and producer parts of vring. * x86 pagesize again. This is the default, used by transports like PCI @@ -635,7 +226,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; @@ -732,7 +323,6 @@ static void vring_packed_event_read(VirtIODevice *vdev, /* Make sure flags is seen before off_wrap */ smp_rmb(); e->off_wrap = virtio_lduw_phys_cached(vdev, cache, off_off); - virtio_tswap16s(vdev, &e->flags); } static void vring_packed_off_wrap_write(VirtIODevice *vdev, @@ -1154,6 +744,60 @@ int virtio_queue_empty(VirtQueue *vq) } } +static bool virtio_queue_split_poll(VirtQueue *vq, unsigned shadow_idx) +{ + if (unlikely(!vq->vring.avail)) { + return false; + } + + return (uint16_t)shadow_idx != vring_avail_idx(vq); +} + +static bool virtio_queue_packed_poll(VirtQueue *vq, unsigned shadow_idx) +{ + VRingPackedDesc desc; + VRingMemoryRegionCaches *caches; + + if (unlikely(!vq->vring.desc)) { + return false; + } + + caches = vring_get_region_caches(vq); + if (!caches) { + return false; + } + + vring_packed_desc_read(vq->vdev, &desc, &caches->desc, + shadow_idx, true); + + return is_desc_avail(desc.flags, vq->shadow_avail_wrap_counter); +} + +static bool virtio_queue_poll(VirtQueue *vq, unsigned shadow_idx) +{ + if (virtio_device_disabled(vq->vdev)) { + return false; + } + + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + return virtio_queue_packed_poll(vq, shadow_idx); + } else { + return virtio_queue_split_poll(vq, shadow_idx); + } +} + +bool virtio_queue_enable_notification_and_check(VirtQueue *vq, + int opaque) +{ + virtio_queue_set_notification(vq, 1); + + if (opaque >= 0) { + return virtio_queue_poll(vq, (unsigned)opaque); + } else { + return false; + } +} + static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { @@ -1282,6 +926,46 @@ static void virtqueue_packed_fill(VirtQueue *vq, const VirtQueueElement *elem, vq->used_elems[idx].ndescs = elem->ndescs; } +static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len) +{ + unsigned int i, steps, max_steps; + + i = vq->used_idx % vq->vring.num; + steps = 0; + /* + * We shouldn't need to increase 'i' by more than the distance + * between used_idx and last_avail_idx. + */ + max_steps = (vq->last_avail_idx - vq->used_idx) % vq->vring.num; + + /* Search for element in vq->used_elems */ + while (steps <= max_steps) { + /* Found element, set length and mark as filled */ + if (vq->used_elems[i].index == elem->index) { + vq->used_elems[i].len = len; + vq->used_elems[i].in_order_filled = true; + break; + } + + i += vq->used_elems[i].ndescs; + steps += vq->used_elems[i].ndescs; + + if (i >= vq->vring.num) { + i -= vq->vring.num; + } + } + + /* + * We should be able to find a matching VirtQueueElement in + * used_elems. If we don't, this is an error. + */ + if (steps >= max_steps) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: %s cannot fill buffer id %u\n", + __func__, vq->vdev->name, elem->index); + } +} + static void virtqueue_packed_fill_desc(VirtQueue *vq, const VirtQueueElement *elem, unsigned int idx, @@ -1332,7 +1016,9 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, return; } - if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_IN_ORDER)) { + virtqueue_ordered_fill(vq, elem, len); + } else if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { virtqueue_packed_fill(vq, elem, len, idx); } else { virtqueue_split_fill(vq, elem, len, idx); @@ -1367,12 +1053,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; @@ -1383,6 +1077,73 @@ static void virtqueue_packed_flush(VirtQueue *vq, unsigned int count) } } +static void virtqueue_ordered_flush(VirtQueue *vq) +{ + unsigned int i = vq->used_idx % vq->vring.num; + unsigned int ndescs = 0; + uint16_t old = vq->used_idx; + uint16_t new; + bool packed; + VRingUsedElem uelem; + + packed = virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED); + + if (packed) { + if (unlikely(!vq->vring.desc)) { + return; + } + } else if (unlikely(!vq->vring.used)) { + return; + } + + /* First expected in-order element isn't ready, nothing to do */ + if (!vq->used_elems[i].in_order_filled) { + return; + } + + /* Search for filled elements in-order */ + while (vq->used_elems[i].in_order_filled) { + /* + * First entry for packed VQs is written last so the guest + * doesn't see invalid descriptors. + */ + if (packed && i != vq->used_idx) { + virtqueue_packed_fill_desc(vq, &vq->used_elems[i], ndescs, false); + } else if (!packed) { + uelem.id = vq->used_elems[i].index; + uelem.len = vq->used_elems[i].len; + vring_used_write(vq, &uelem, i); + } + + vq->used_elems[i].in_order_filled = false; + ndescs += vq->used_elems[i].ndescs; + i += vq->used_elems[i].ndescs; + if (i >= vq->vring.num) { + i -= vq->vring.num; + } + } + + if (packed) { + virtqueue_packed_fill_desc(vq, &vq->used_elems[vq->used_idx], 0, true); + vq->used_idx += ndescs; + if (vq->used_idx >= vq->vring.num) { + vq->used_idx -= vq->vring.num; + vq->used_wrap_counter ^= 1; + vq->signalled_used_valid = false; + } + } else { + /* Make sure buffer is written before we update index. */ + smp_wmb(); + new = old + ndescs; + vring_used_idx_set(vq, new); + if (unlikely((int16_t)(new - vq->signalled_used) < + (uint16_t)(new - old))) { + vq->signalled_used_valid = false; + } + } + vq->inuse -= ndescs; +} + void virtqueue_flush(VirtQueue *vq, unsigned int count) { if (virtio_device_disabled(vq->vdev)) { @@ -1390,7 +1151,9 @@ void virtqueue_flush(VirtQueue *vq, unsigned int count) return; } - if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_IN_ORDER)) { + virtqueue_ordered_flush(vq); + } else if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { virtqueue_packed_flush(vq, count); } else { virtqueue_split_flush(vq, count); @@ -1408,7 +1171,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) { @@ -1416,8 +1184,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(); } @@ -1448,9 +1223,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)) { @@ -1458,16 +1234,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; } @@ -1480,10 +1252,12 @@ static void virtqueue_split_get_avail_bytes(VirtQueue *vq, VirtIODevice *vdev = vq->vdev; 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; @@ -1545,7 +1319,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) { @@ -1616,12 +1390,14 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, VirtIODevice *vdev = vq->vdev; 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; @@ -1720,9 +1496,9 @@ err: goto done; } -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes) +int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, unsigned max_in_bytes, + unsigned max_out_bytes) { uint16_t desc_size; VRingMemoryRegionCaches *caches; @@ -1755,7 +1531,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, caches); } - return; + return (int)vq->shadow_avail_idx; err: if (in_bytes) { *in_bytes = 0; @@ -1763,6 +1539,8 @@ err: if (out_bytes) { *out_bytes = 0; } + + return -1; } int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, @@ -1894,9 +1672,9 @@ static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_nu static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) { - unsigned int i, head, max; + unsigned int i, head, max, idx; VRingMemoryRegionCaches *caches; - MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; + MemoryRegionCache indirect_desc_cache; MemoryRegionCache *desc_cache; int64_t len; VirtIODevice *vdev = vq->vdev; @@ -1907,6 +1685,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; @@ -1996,7 +1776,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) { @@ -2016,6 +1796,13 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) elem->in_sg[i] = iov[out_num + i]; } + if (virtio_vdev_has_feature(vdev, VIRTIO_F_IN_ORDER)) { + idx = (vq->last_avail_idx - 1) % vq->vring.num; + vq->used_elems[idx].index = elem->index; + vq->used_elems[idx].len = elem->len; + vq->used_elems[idx].ndescs = elem->ndescs; + } + vq->inuse++; trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num); @@ -2033,7 +1820,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; @@ -2045,6 +1832,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; @@ -2129,6 +1918,11 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) &indirect_desc_cache); } while (rc == VIRTQUEUE_READ_DESC_MORE); + if (desc_cache != &indirect_desc_cache) { + /* Buffer ID is included in the last descriptor in the list. */ + id = desc.id; + } + /* Now copy what we have collected and mapped */ elem = virtqueue_alloc_element(sz, out_num, in_num); for (i = 0; i < out_num; i++) { @@ -2142,6 +1936,13 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) elem->index = id; elem->ndescs = (desc_cache == &indirect_desc_cache) ? 1 : elem_entries; + + if (virtio_vdev_has_feature(vdev, VIRTIO_F_IN_ORDER)) { + vq->used_elems[vq->last_avail_idx].index = elem->index; + vq->used_elems[vq->last_avail_idx].len = elem->len; + vq->used_elems[vq->last_avail_idx].ndescs = elem->ndescs; + } + vq->last_avail_idx += elem->ndescs; vq->inuse += elem->ndescs; @@ -2505,7 +2306,7 @@ void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index) * being converted to LOG_GUEST_ERROR. * if (!virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { - error_report("queue_enable is only suppported in devices of virtio " + error_report("queue_enable is only supported in devices of virtio " "1.0 or later."); } */ @@ -2530,6 +2331,14 @@ void virtio_reset(void *opaque) vdev->device_endian = virtio_default_endian(); } + if (k->get_vhost) { + struct vhost_dev *hdev = k->get_vhost(vdev); + /* Only reset when vhost back-end is connected */ + if (hdev && hdev->vhost_ops) { + vhost_reset_device(hdev); + } + } + if (k->reset) { k->reset(vdev); } @@ -2550,195 +2359,6 @@ void virtio_reset(void *opaque) } } -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); - } -} - void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr) { if (!vdev->vq[n].vring.num) { @@ -2833,6 +2453,24 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) } } +void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t shadow_avail_idx) +{ + if (!vq->vring.desc) { + return; + } + + /* + * 16-bit data for packed VQs include 1-bit wrap counter and + * 15-bit shadow_avail_idx. + */ + if (virtio_vdev_has_feature(vq->vdev, VIRTIO_F_RING_PACKED)) { + vq->shadow_avail_wrap_counter = (shadow_avail_idx >> 15) & 0x1; + vq->shadow_avail_idx = shadow_avail_idx & 0x7FFF; + } else { + vq->shadow_avail_idx = shadow_avail_idx; + } +} + static void virtio_queue_notify_vq(VirtQueue *vq) { if (vq->vring.desc && vq->handle_output) { @@ -3024,6 +2662,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() { @@ -3050,7 +2698,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) @@ -3162,7 +2810,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() @@ -3173,7 +2821,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), @@ -3188,7 +2836,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() @@ -3200,7 +2848,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() @@ -3211,7 +2859,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() } @@ -3222,7 +2870,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() @@ -3265,7 +2913,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, @@ -3284,7 +2932,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() } @@ -3295,7 +2943,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() } @@ -3306,7 +2954,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() } @@ -3317,7 +2965,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() } @@ -3328,7 +2976,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() } @@ -3338,10 +2986,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, @@ -3423,8 +3071,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)); @@ -3451,6 +3100,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; @@ -3487,6 +3169,20 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } +static void virtio_device_check_notification_compatibility(VirtIODevice *vdev, + Error **errp) +{ + VirtioBusState *bus = VIRTIO_BUS(qdev_get_parent_bus(DEVICE(vdev))); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); + DeviceState *proxy = DEVICE(BUS(bus)->parent); + + if (virtio_host_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA) && + k->ioeventfd_enabled(proxy)) { + error_setg(errp, + "notification_data=on without ioeventfd=off is not supported"); + } +} + size_t virtio_get_config_size(const VirtIOConfigSizeParams *params, uint64_t host_features) { @@ -3504,7 +3200,8 @@ size_t virtio_get_config_size(const VirtIOConfigSizeParams *params, 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; @@ -3558,6 +3255,13 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) config_len--; } + if (vdc->pre_load_queues) { + ret = vdc->pre_load_queues(vdev); + if (ret) { + return ret; + } + } + num = qemu_get_be32(f); if (num > VIRTIO_QUEUE_MAX) { @@ -3621,14 +3325,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); @@ -3919,7 +3623,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); } @@ -3972,7 +3676,7 @@ static void virtio_queue_packed_update_used_idx(VirtIODevice *vdev, int n) return; } -static void virtio_split_packed_update_used_idx(VirtIODevice *vdev, int n) +static void virtio_queue_split_update_used_idx(VirtIODevice *vdev, int n) { RCU_READ_LOCK_GUARD(); if (vdev->vq[n].vring.desc) { @@ -3985,7 +3689,7 @@ void virtio_queue_update_used_idx(VirtIODevice *vdev, int n) if (virtio_vdev_has_feature(vdev, VIRTIO_F_RING_PACKED)) { return virtio_queue_packed_update_used_idx(vdev, n); } else { - return virtio_split_packed_update_used_idx(vdev, n); + return virtio_queue_split_update_used_idx(vdev, n); } } @@ -4011,7 +3715,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) { @@ -4028,6 +3739,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; @@ -4065,13 +3793,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); } /* @@ -4082,17 +3828,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) @@ -4108,6 +3875,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; @@ -4178,6 +3950,14 @@ static void virtio_device_realize(DeviceState *dev, Error **errp) } } + /* Devices should not use both ioeventfd and notification data feature */ + virtio_device_check_notification_compatibility(vdev, &err); + if (err != NULL) { + error_propagate(errp, err); + vdc->unrealize(dev); + return; + } + virtio_bus_device_plugged(vdev, &err); if (err != NULL) { error_propagate(errp, err); @@ -4188,7 +3968,6 @@ static void virtio_device_realize(DeviceState *dev, Error **errp) vdev->listener.commit = virtio_memory_listener_commit; vdev->listener.name = "virtio"; memory_listener_register(&vdev->listener, vdev->dma_as); - QTAILQ_INSERT_TAIL(&virtio_list, vdev, next); } static void virtio_device_unrealize(DeviceState *dev) @@ -4203,7 +3982,6 @@ static void virtio_device_unrealize(DeviceState *dev) vdc->unrealize(dev); } - QTAILQ_REMOVE(&virtio_list, vdev, next); g_free(vdev->bus_name); vdev->bus_name = NULL; } @@ -4377,8 +4155,6 @@ static void virtio_device_class_init(ObjectClass *klass, void *data) vdc->stop_ioeventfd = virtio_device_stop_ioeventfd_impl; vdc->legacy_features |= VIRTIO_LEGACY_FEATURES; - - QTAILQ_INIT(&virtio_list); } bool virtio_device_ioeventfd_enabled(VirtIODevice *vdev) @@ -4389,389 +4165,6 @@ bool virtio_device_ioeventfd_enabled(VirtIODevice *vdev) return virtio_bus_ioeventfd_enabled(vbus); } -VirtioInfoList *qmp_x_query_virtio(Error **errp) -{ - VirtioInfoList *list = NULL; - VirtioInfoList *node; - VirtIODevice *vdev; - - QTAILQ_FOREACH(vdev, &virtio_list, next) { - DeviceState *dev = DEVICE(vdev); - Error *err = NULL; - QObject *obj = qmp_qom_get(dev->canonical_path, "realized", &err); - - if (err == NULL) { - GString *is_realized = qobject_to_json_pretty(obj, true); - /* virtio device is NOT realized, remove it from list */ - if (!strncmp(is_realized->str, "false", 4)) { - QTAILQ_REMOVE(&virtio_list, vdev, next); - } else { - node = g_new0(VirtioInfoList, 1); - node->value = g_new(VirtioInfo, 1); - node->value->path = g_strdup(dev->canonical_path); - node->value->name = g_strdup(vdev->name); - QAPI_LIST_PREPEND(list, node->value); - } - g_string_free(is_realized, true); - } - qobject_unref(obj); - } - - return list; -} - -static VirtIODevice *virtio_device_find(const char *path) -{ - VirtIODevice *vdev; - - QTAILQ_FOREACH(vdev, &virtio_list, next) { - DeviceState *dev = DEVICE(vdev); - - if (strcmp(dev->canonical_path, path) != 0) { - continue; - } - - Error *err = NULL; - QObject *obj = qmp_qom_get(dev->canonical_path, "realized", &err); - if (err == NULL) { - GString *is_realized = qobject_to_json_pretty(obj, true); - /* virtio device is NOT realized, remove it from list */ - if (!strncmp(is_realized->str, "false", 4)) { - g_string_free(is_realized, true); - qobject_unref(obj); - QTAILQ_REMOVE(&virtio_list, vdev, next); - return NULL; - } - g_string_free(is_realized, true); - } else { - /* virtio device doesn't exist in QOM tree */ - QTAILQ_REMOVE(&virtio_list, vdev, next); - qobject_unref(obj); - return NULL; - } - /* device exists in QOM tree & is realized */ - qobject_unref(obj); - return vdev; - } - return NULL; -} - -#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; \ - }) - -static 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; -} - -static 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; -} - -static 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 - /* 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: - case VIRTIO_ID_GPIO: - 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; -} - -VirtioStatus *qmp_x_query_virtio_status(const char *path, Error **errp) -{ - VirtIODevice *vdev; - VirtioStatus *status; - - vdev = virtio_device_find(path); - if (vdev == NULL) { - error_setg(errp, "Path %s is not a 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; - status->has_vhost_dev = vdev->vhost_started; - - 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 = virtio_device_find(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; -} - VirtQueueStatus *qmp_x_query_virtio_queue_status(const char *path, uint16_t queue, Error **errp) @@ -4779,7 +4172,7 @@ VirtQueueStatus *qmp_x_query_virtio_queue_status(const char *path, VirtIODevice *vdev; VirtQueueStatus *status; - vdev = virtio_device_find(path); + vdev = qmp_find_virtio_device(path); if (vdev == NULL) { error_setg(errp, "Path %s is not a VirtIODevice", path); return NULL; @@ -4872,7 +4265,7 @@ VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, VirtQueue *vq; VirtioQueueElement *element = NULL; - vdev = virtio_device_find(path); + vdev = qmp_find_virtio_device(path); if (vdev == NULL) { error_setg(errp, "Path %s is not a VirtIO device", path); return NULL; @@ -4890,13 +4283,15 @@ VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, } else { unsigned int head, i, max; VRingMemoryRegionCaches *caches; - MemoryRegionCache indirect_desc_cache = MEMORY_REGION_CACHE_INVALID; + 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; @@ -4961,8 +4356,7 @@ VirtioQueueElement *qmp_x_query_virtio_queue_element(const char *path, list = node; ndescs++; - 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); element->descs = list; done: @@ -4988,3 +4382,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..ed5ff4257c 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -12,8 +12,8 @@ /* * This is a model of the "APB watchdog" which is part of the Cortex-M * System Design Kit (CMSDK) and documented in the Cortex-M System - * Design Kit Technical Reference Manual (ARM DDI0479C): - * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * Design Kit Technical Reference Manual (ARM DDI0479): + * https://developer.arm.com/documentation/ddi0479/ * * We also support the variant of this device found in the TI * Stellaris/Luminary boards and documented in: @@ -196,16 +196,13 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, switch (offset) { case A_WDOGLOAD: - /* - * Reset the load value and the current count, and make sure - * we're counting. - */ + /* Reset the load value and the current count. */ ptimer_transaction_begin(s->timer); ptimer_set_limit(s->timer, value, 1); - ptimer_run(s->timer, 0); ptimer_transaction_commit(s->timer); break; - case A_WDOGCONTROL: + case A_WDOGCONTROL: { + uint32_t prev_control = s->control; if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) { /* * The Luminary version of this device ignores writes to @@ -215,8 +212,25 @@ static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, break; } s->control = value & R_WDOGCONTROL_VALID_MASK; + if (R_WDOGCONTROL_INTEN_MASK & (s->control ^ prev_control)) { + ptimer_transaction_begin(s->timer); + if (R_WDOGCONTROL_INTEN_MASK & s->control) { + /* + * Set HIGH to enable the counter and the interrupt. Reloads + * the counter from the value in WDOGLOAD when the interrupt + * is enabled, after previously being disabled. + */ + ptimer_set_count(s->timer, ptimer_get_limit(s->timer)); + ptimer_run(s->timer, 0); + } else { + /* Or LOW to disable the counter and interrupt. */ + ptimer_stop(s->timer); + } + ptimer_transaction_commit(s->timer); + } cmsdk_apb_watchdog_update(s); break; + } case A_WDOGINTCLR: s->intstatus = 0; ptimer_transaction_begin(s->timer); @@ -305,8 +319,14 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev) s->resetstatus = 0; /* Set the limit and the count */ ptimer_transaction_begin(s->timer); + /* + * We need to stop the ptimer before setting its limit reset value. If the + * order is the opposite when the code executes the stop after setting a new + * limit it may want to recalculate the count based on the current time (if + * the timer was currently running) and it won't get the proper reset value. + */ + ptimer_stop(s->timer); ptimer_set_limit(s->timer, 0xffffffff, 1); - ptimer_run(s->timer, 0); ptimer_transaction_commit(s->timer); } @@ -361,7 +381,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), @@ -380,7 +400,7 @@ static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data) dc->realize = cmsdk_apb_watchdog_realize; dc->vmsd = &cmsdk_apb_watchdog_vmstate; - dc->reset = cmsdk_apb_watchdog_reset; + device_class_set_legacy_reset(dc, cmsdk_apb_watchdog_reset); } static const TypeInfo cmsdk_apb_watchdog_info = { diff --git a/hw/watchdog/meson.build b/hw/watchdog/meson.build index 8974b5cf4c..15370565bd 100644 --- a/hw/watchdog/meson.build +++ b/hw/watchdog/meson.build @@ -1,9 +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 7aa57a8c51..80f9b36e79 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "sysemu/reset.h" #include "sysemu/watchdog.h" +#include "hw/qdev-properties.h" #include "hw/watchdog/sbsa_gwdt.h" #include "qemu/timer.h" #include "migration/vmstate.h" @@ -28,7 +29,7 @@ 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), @@ -109,7 +110,7 @@ static void sbsa_gwdt_update_timer(SBSA_GWDTState *s, WdtRefreshType rtype) timeout = s->woru; timeout <<= 32; timeout |= s->worl; - timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, SBSA_TIMER_FREQ); + timeout = muldiv64(timeout, NANOSECONDS_PER_SECOND, s->freq); timeout += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if ((rtype == EXPLICIT_REFRESH) || ((rtype == TIMEOUT_REFRESH) && @@ -261,16 +262,28 @@ static void wdt_sbsa_gwdt_realize(DeviceState *dev, Error **errp) dev); } +static Property wdt_sbsa_gwdt_props[] = { + /* + * Timer frequency in Hz. This must match the frequency used by + * the CPU's generic timer. Default 62.5Hz matches QEMU's legacy + * CPU timer frequency default. + */ + DEFINE_PROP_UINT64("clock-frequency", struct SBSA_GWDTState, freq, + 62500000), + DEFINE_PROP_END_OF_LIST(), +}; + static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = wdt_sbsa_gwdt_realize; - dc->reset = wdt_sbsa_gwdt_reset; + device_class_set_legacy_reset(dc, wdt_sbsa_gwdt_reset); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_sbsa_gwdt; dc->desc = "SBSA-compliant generic watchdog device"; + device_class_set_props(dc, wdt_sbsa_gwdt_props); } static const TypeInfo wdt_sbsa_gwdt_info = { diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c index 55ff1f03c1..2bb1d3c532 100644 --- a/hw/watchdog/spapr_watchdog.c +++ b/hw/watchdog/spapr_watchdog.c @@ -226,7 +226,7 @@ static const VMStateDescription vmstate_wdt = { .version_id = 1, .minimum_version_id = 1, .needed = watchdog_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_TIMER(timer, SpaprWatchdog), VMSTATE_UINT8(action, SpaprWatchdog), VMSTATE_UINT8(leave_others, SpaprWatchdog), diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 89ccbcfdfd..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" @@ -10,9 +17,19 @@ cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 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 6c082a3263..d0ce3c4ac5 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -30,6 +30,7 @@ #include "sysemu/watchdog.h" #include "hw/nmi.h" #include "qemu/help_option.h" +#include "trace.h" static WatchdogAction watchdog_action = WATCHDOG_ACTION_RESET; @@ -43,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); @@ -82,11 +85,12 @@ void watchdog_perform_action(void) break; default: - assert(0); + g_assert_not_reached(); } } 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 d753693a2e..39c3f362a8 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); @@ -206,7 +218,7 @@ 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() @@ -260,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); @@ -271,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); } @@ -287,7 +300,7 @@ static void aspeed_wdt_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED Watchdog Controller"; dc->realize = aspeed_wdt_realize; - dc->reset = aspeed_wdt_reset; + device_class_set_legacy_reset(dc, aspeed_wdt_reset); set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_aspeed_wdt; device_class_set_props(dc, aspeed_wdt_properties); @@ -309,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; @@ -346,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; @@ -369,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; @@ -392,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; @@ -409,12 +422,36 @@ static const TypeInfo aspeed_1030_wdt_info = { .class_init = aspeed_1030_wdt_class_init, }; +static void aspeed_2700_wdt_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); + + dc->desc = "ASPEED 2700 Watchdog Controller"; + 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; + awc->wdt_reload = aspeed_wdt_reload_1mhz; + awc->sanitize_ctrl = aspeed_2600_sanitize_ctrl; + awc->default_status = 0x014FB180; + awc->default_reload_value = 0x014FB180; +} + +static const TypeInfo aspeed_2700_wdt_info = { + .name = TYPE_ASPEED_2700_WDT, + .parent = TYPE_ASPEED_WDT, + .instance_size = sizeof(AspeedWDTState), + .class_init = aspeed_2700_wdt_class_init, +}; + static void wdt_aspeed_register_types(void) { type_register_static(&aspeed_wdt_info); type_register_static(&aspeed_2400_wdt_info); type_register_static(&aspeed_2500_wdt_info); type_register_static(&aspeed_2600_wdt_info); + type_register_static(&aspeed_2700_wdt_info); type_register_static(&aspeed_1030_wdt_info); } diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c index 76d89fbf78..040d20fde7 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -23,7 +23,7 @@ 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() @@ -115,7 +115,7 @@ static void wdt_diag288_class_init(ObjectClass *klass, void *data) dc->realize = wdt_diag288_realize; dc->unrealize = wdt_diag288_unrealize; - dc->reset = wdt_diag288_reset; + device_class_set_legacy_reset(dc, wdt_diag288_reset); dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->vmsd = &vmstate_diag288; diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 5693ec6a09..9427abfb49 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), @@ -469,7 +469,7 @@ static void i6300esb_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_ESB_9; k->class_id = PCI_CLASS_SYSTEM_OTHER; - dc->reset = i6300esb_reset; + device_class_set_legacy_reset(dc, i6300esb_reset); dc->vmsd = &vmstate_i6300esb; set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->desc = "Intel 6300ESB"; diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index b116c3a3aa..17c82897bf 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() } @@ -133,7 +133,7 @@ static void wdt_ib700_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = wdt_ib700_realize; - dc->reset = wdt_ib700_reset; + device_class_set_legacy_reset(dc, wdt_ib700_reset); dc->vmsd = &vmstate_ib700; set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); dc->desc = "iBASE 700"; diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index e776a2fbd4..8162d58afa 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,11 +33,12 @@ 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 */ if (s->wcr & IMX2_WDT_WCR_WDE) { - s->wrsr = IMX2_WDT_WRSR_TOUT; watchdog_perform_action(); } } @@ -67,20 +71,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 +150,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 +233,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), @@ -278,7 +293,7 @@ static void imx2_wdt_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, imx2_wdt_properties); dc->realize = imx2_wdt_realize; - dc->reset = imx2_wdt_reset; + device_class_set_legacy_reset(dc, imx2_wdt_reset); dc->vmsd = &vmstate_imx2_wdt; dc->desc = "i.MX2 watchdog timer"; set_bit(DEVICE_CATEGORY_WATCHDOG, dc->categories); diff --git a/hw/xbox/lpc47m157.c b/hw/xbox/lpc47m157.c index b88e0b01d5..aca0dd8ff6 100644 --- a/hw/xbox/lpc47m157.c +++ b/hw/xbox/lpc47m157.c @@ -2,7 +2,7 @@ * QEMU SMSC LPC47M157 (Super I/O) * * Copyright (c) 2013 espes - * Copyright (c) 2018-2021 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -127,7 +127,6 @@ static void update_devices(ISALPC47M157State *isa) memory_region_init_io(&ss->io, OBJECT(s), &serial_io_ops, ss, "serial", 8); isa_register_ioport(NULL, &ss->io, iobase); - serial_set_frequency(ss, 115200); serial->iobase = iobase; serial->irq = irq; serial->active = true; diff --git a/hw/xbox/mcpx/adpcm.h b/hw/xbox/mcpx/adpcm.h index feaa12d10b..f6dd8f6037 100644 --- a/hw/xbox/mcpx/adpcm.h +++ b/hw/xbox/mcpx/adpcm.h @@ -75,14 +75,14 @@ static int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inb -1, -1, -1, -1, 2, 4, 6, 8 }; - int ch, samples = 1, chunks; + int samples = 1, chunks; int32_t pcmdata[2]; int8_t index[2]; if (inbufsize < (uint32_t) channels * 4) return 0; - for (ch = 0; ch < channels; ch++) { + for (int ch = 0; ch < channels; ch++) { *outbuf++ = pcmdata[ch] = (int16_t) (inbuf [0] | (inbuf [1] << 8)); index[ch] = inbuf [2]; @@ -97,11 +97,10 @@ static int adpcm_decode_block (int16_t *outbuf, const uint8_t *inbuf, size_t inb samples += chunks * 8; while (chunks--) { - int ch, i; - for (ch = 0; ch < channels; ++ch) { + for (int ch = 0; ch < channels; ++ch) { - for (i = 0; i < 4; ++i) { + for (int i = 0; i < 4; ++i) { int step = step_table [index [ch]], delta = step >> 3; if (*inbuf & 1) delta += (step >> 2); diff --git a/hw/xbox/mcpx/apu.c b/hw/xbox/mcpx/apu.c index e1301bc07c..1b2b918323 100644 --- a/hw/xbox/mcpx/apu.c +++ b/hw/xbox/mcpx/apu.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2018-2019 Jannik Vogel - * Copyright (c) 2019-2021 Matt Borgerson + * Copyright (c) 2019-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,8 +25,10 @@ #include #include "hw/hw.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "cpu.h" #include "migration/vmstate.h" +#include "qemu/main-loop.h" #include "sysemu/runstate.h" #include "audio/audio.h" #include "qemu/fifo8.h" @@ -86,7 +88,10 @@ typedef struct MCPXAPUVoiceFilter { } MCPXAPUVoiceFilter; typedef struct MCPXAPUState { - PCIDevice dev; + /*< private >*/ + PCIDevice parent_obj; + /*< public >*/ + bool exiting; bool set_irq; @@ -227,7 +232,6 @@ static void mcpx_apu_vm_state_change(void *opaque, bool running, RunState state) static int mcpx_apu_post_save(void *opaque); static int mcpx_apu_pre_load(void *opaque); static int mcpx_apu_post_load(void *opaque, int version_id); -static void qdev_mcpx_apu_reset(DeviceState *dev); static void mcpx_apu_register(void); static void *mcpx_apu_frame_thread(void *arg); @@ -364,12 +368,12 @@ static void update_irq(MCPXAPUState *d) qatomic_or(&d->regs[NV_PAPU_ISTS], NV_PAPU_ISTS_GINTSTS); // fprintf(stderr, "mcpx irq raise ien=%08x ists=%08x\n", // d->regs[NV_PAPU_IEN], d->regs[NV_PAPU_ISTS]); - pci_irq_assert(&d->dev); + pci_irq_assert(PCI_DEVICE(d)); } else { qatomic_and(&d->regs[NV_PAPU_ISTS], ~NV_PAPU_ISTS_GINTSTS); // fprintf(stderr, "mcpx irq lower ien=%08x ists=%08x\n", // d->regs[NV_PAPU_IEN], d->regs[NV_PAPU_ISTS]); - pci_irq_deassert(&d->dev); + pci_irq_deassert(PCI_DEVICE(d)); } } @@ -488,7 +492,7 @@ static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument) case NV1BA0_PIO_SET_ANTECEDENT_VOICE: d->regs[NV_PAPU_FEAV] = argument; break; - case NV1BA0_PIO_VOICE_ON: + case NV1BA0_PIO_VOICE_ON: { selected_handle = argument & NV1BA0_PIO_VOICE_ON_HANDLE; DPRINTF("VOICE %d ON\n", selected_handle); @@ -579,6 +583,7 @@ static void fe_method(MCPXAPUState *d, uint32_t method, uint32_t argument) } break; + } case NV1BA0_PIO_VOICE_RELEASE: { selected_handle = argument & NV1BA0_PIO_VOICE_ON_HANDLE; @@ -2231,7 +2236,7 @@ static void se_frame(MCPXAPUState *d) } qemu_spin_lock(&d->vp.out_buf_lock); - int num_bytes_free = fifo8_num_free(&d->vp.out_buf); + num_bytes_free = fifo8_num_free(&d->vp.out_buf); assert(num_bytes_free >= sizeof(d->apu_fifo_output)); fifo8_push_all(&d->vp.out_buf, (uint8_t *)d->apu_fifo_output, sizeof(d->apu_fifo_output)); @@ -2281,10 +2286,8 @@ static void mcpx_vp_out_cb(void *opaque, uint8_t *stream, int free_b) while (to_copy > 0) { uint32_t chunk_len = 0; qemu_spin_lock(&s->vp.out_buf_lock); - const uint8_t *samples = - fifo8_pop_buf(&s->vp.out_buf, to_copy, &chunk_len); + chunk_len = fifo8_pop_buf(&s->vp.out_buf, stream, to_copy); assert(chunk_len <= to_copy); - memcpy(stream, samples, chunk_len); qemu_spin_unlock(&s->vp.out_buf_lock); stream += chunk_len; to_copy -= chunk_len; @@ -2314,7 +2317,7 @@ static void mcpx_apu_realize(PCIDevice *dev, Error **errp) "mcpx-apu-ep", 0x10000); memory_region_add_subregion(&d->mmio, 0x50000, &d->ep.mmio); - pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } static void mcpx_apu_exitfn(PCIDevice *dev) @@ -2383,9 +2386,9 @@ static int mcpx_apu_post_load(void *opaque, int version_id) return 0; } -static void qdev_mcpx_apu_reset(DeviceState *dev) +static void mcpx_apu_reset_hold(Object *obj, ResetType type) { - MCPXAPUState *d = MCPX_APU_DEVICE(dev); + MCPXAPUState *d = MCPX_APU_DEVICE(obj); mcpx_apu_reset(d); } @@ -2482,7 +2485,7 @@ static const VMStateDescription vmstate_mcpx_apu = { .pre_load = mcpx_apu_pre_load, .post_load = mcpx_apu_post_load, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, MCPXAPUState), + VMSTATE_PCI_DEVICE(parent_obj, MCPXAPUState), VMSTATE_STRUCT_POINTER(gp.dsp, MCPXAPUState, vmstate_vp_dsp_state, DSPState), VMSTATE_UINT32_ARRAY(gp.regs, MCPXAPUState, 0x10000), @@ -2506,6 +2509,7 @@ static const VMStateDescription vmstate_mcpx_apu = { static void mcpx_apu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_NVIDIA; @@ -2515,8 +2519,9 @@ static void mcpx_apu_class_init(ObjectClass *klass, void *data) k->realize = mcpx_apu_realize; k->exit = mcpx_apu_exitfn; + rc->phases.hold = mcpx_apu_reset_hold; + dc->desc = "MCPX Audio Processing Unit"; - dc->reset = qdev_mcpx_apu_reset; dc->vmsd = &vmstate_mcpx_apu; } @@ -2554,9 +2559,9 @@ static void *mcpx_apu_frame_thread(void *arg) if (d->set_irq) { qemu_mutex_unlock(&d->lock); - qemu_mutex_lock_iothread(); + bql_lock(); update_irq(d); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->lock); d->set_irq = false; } diff --git a/hw/xbox/nv2a/debug.h b/hw/xbox/nv2a/debug.h index a843df3259..3873f94239 100644 --- a/hw/xbox/nv2a/debug.h +++ b/hw/xbox/nv2a/debug.h @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2023 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,8 +30,11 @@ } \ } while (0) -// #define DEBUG_NV2A -#ifdef DEBUG_NV2A +#ifndef DEBUG_NV2A +# define DEBUG_NV2A 0 +#endif + +#if DEBUG_NV2A # define NV2A_DPRINTF(format, ...) printf("nv2a: " format, ## __VA_ARGS__) #else # define NV2A_DPRINTF(format, ...) do { } while (0) @@ -42,9 +45,11 @@ * varying degree, but should otherwise not crash the system. Enable this * macro for debugging. */ -// #define DEBUG_NV2A_FEATURES 1 +#ifndef DEBUG_NV2A_FEATURES +# define DEBUG_NV2A_FEATURES 0 +#endif -#ifdef DEBUG_NV2A_FEATURES +#if DEBUG_NV2A_FEATURES /* Feature which has not yet been confirmed */ #define NV2A_UNCONFIRMED(format, ...) do { \ diff --git a/hw/xbox/nv2a/nv2a.c b/hw/xbox/nv2a/nv2a.c index fd1bcbaba6..0124a0d451 100644 --- a/hw/xbox/nv2a/nv2a.c +++ b/hw/xbox/nv2a/nv2a.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2021 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +20,7 @@ */ #include "hw/xbox/nv2a/nv2a_int.h" +#include "qemu/main-loop.h" void nv2a_update_irq(NV2AState *d) { @@ -149,27 +150,16 @@ static int nv2a_get_bpp(VGACommonState *s) return bpp; } -static void nv2a_get_offsets(VGACommonState *s, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare) +static void nv2a_get_params(VGACommonState *s, VGADisplayParams *params) { NV2AState *d = container_of(s, NV2AState, vga); - uint32_t start_addr, line_offset, line_compare; - - line_offset = s->cr[0x13] - | ((s->cr[0x19] & 0xe0) << 3) - | ((s->cr[0x25] & 0x20) << 6); - line_offset <<= 3; - *pline_offset = line_offset; - - start_addr = d->pcrtc.start / 4; - *pstart_addr = start_addr; - - line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | - ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); - *pline_compare = line_compare; + params->line_offset = (s->cr[0x13] | ((s->cr[0x19] & 0xe0) << 3) | + ((s->cr[0x25] & 0x20) << 6)) + << 3; + params->start_addr = d->pcrtc.start / 4; + params->line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | + ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); } const uint8_t *nv2a_get_dac_palette(void) @@ -235,7 +225,7 @@ static void nv2a_init_vga(NV2AState *d) vga_common_init(vga, OBJECT(d), &error_fatal); vga->get_bpp = nv2a_get_bpp; - vga->get_offsets = nv2a_get_offsets; + vga->get_params = nv2a_get_params; // vga->overlay_draw_line = nv2a_overlay_draw_line; d->hw_ops = *vga->hw_ops; @@ -255,9 +245,9 @@ static void nv2a_lock_fifo(NV2AState *d) { qemu_mutex_lock(&d->pfifo.lock); qemu_cond_broadcast(&d->pfifo.fifo_cond); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_cond_wait(&d->pfifo.fifo_idle_cond, &d->pfifo.lock); - qemu_mutex_lock_iothread(); + bql_lock(); qemu_mutex_lock(&d->pgraph.lock); } @@ -278,9 +268,9 @@ static void nv2a_reset(NV2AState *d) qemu_event_reset(&d->pgraph.flush_complete); qatomic_set(&d->pgraph.flush_pending, true); nv2a_unlock_fifo(d); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_event_wait(&d->pgraph.flush_complete); - qemu_mutex_lock_iothread(); + bql_lock(); nv2a_lock_fifo(d); if (!halted) { qatomic_set(&d->pfifo.halt, false); @@ -360,10 +350,10 @@ static void nv2a_exitfn(PCIDevice *dev) pgraph_destroy(&d->pgraph); } -static void qdev_nv2a_reset(DeviceState *dev) +static void nv2a_reset_hold(Object *obj, ResetType type) { - NV2AState *d = NV2A_DEVICE(dev); - nv2a_reset(d); + NV2AState *s = NV2A_DEVICE(obj); + nv2a_reset(s); } // Note: This is handled as a VM state change and not as a `pre_save` callback @@ -377,9 +367,9 @@ static void nv2a_vm_state_change(void *opaque, bool running, RunState state) qatomic_set(&d->pfifo.halt, true); pgraph_pre_savevm_trigger(d); nv2a_unlock_fifo(d); - qemu_mutex_unlock_iothread(); + bql_unlock(); pgraph_pre_savevm_wait(d); - qemu_mutex_lock_iothread(); + bql_lock(); nv2a_lock_fifo(d); } else if (state == RUN_STATE_RESTORE_VM) { nv2a_lock_fifo(d); @@ -393,9 +383,9 @@ static void nv2a_vm_state_change(void *opaque, bool running, RunState state) nv2a_lock_fifo(d); pgraph_pre_shutdown_trigger(d); nv2a_unlock_fifo(d); - qemu_mutex_unlock_iothread(); + bql_unlock(); pgraph_pre_shutdown_wait(d); - qemu_mutex_lock_iothread(); + bql_lock(); } } @@ -558,6 +548,7 @@ static const VMStateDescription vmstate_nv2a = { static void nv2a_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_NVIDIA; @@ -572,9 +563,10 @@ static void nv2a_class_init(ObjectClass *klass, void *data) k->realize = nv2a_realize; k->exit = nv2a_exitfn; + rc->phases.hold = nv2a_reset_hold; + dc->desc = "GeForce NV2A Integrated Graphics"; dc->vmsd = &vmstate_nv2a; - dc->reset = qdev_nv2a_reset; } static const TypeInfo nv2a_info = { diff --git a/hw/xbox/nv2a/nv2a_int.h b/hw/xbox/nv2a/nv2a_int.h index 9b0189ebc8..e007c287b2 100644 --- a/hw/xbox/nv2a/nv2a_int.h +++ b/hw/xbox/nv2a/nv2a_int.h @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2021 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -35,6 +35,7 @@ #include "sysemu/runstate.h" #include "ui/console.h" #include "hw/display/vga_int.h" +#include "hw/pci/pci_device.h" #include "hw/hw.h" #include "hw/display/vga.h" diff --git a/hw/xbox/nv2a/pfifo.c b/hw/xbox/nv2a/pfifo.c index 0e55826ad3..eab8b897dd 100644 --- a/hw/xbox/nv2a/pfifo.c +++ b/hw/xbox/nv2a/pfifo.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2021 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -207,12 +207,12 @@ static ssize_t pfifo_run_puller(NV2AState *d, uint32_t method_entry, /* methods that take objects. * TODO: Check this range is correct for the nv2a */ if (method >= 0x180 && method < 0x200) { - //qemu_mutex_lock_iothread(); + //bql_lock(); RAMHTEntry entry = ramht_lookup(d, parameter); assert(entry.valid); // assert(entry.channel_id == state->channel_id); parameter = entry.instance; - //qemu_mutex_unlock_iothread(); + //bql_unlock(); } enum FIFOEngine engine = GET_MASK(*engine_reg, 3 << (4*subchannel)); diff --git a/hw/xbox/nv2a/pgraph/gl/debug.c b/hw/xbox/nv2a/pgraph/gl/debug.c index 8e7f49e47c..968941dc7e 100644 --- a/hw/xbox/nv2a/pgraph/gl/debug.c +++ b/hw/xbox/nv2a/pgraph/gl/debug.c @@ -21,7 +21,7 @@ #include "renderer.h" #include "debug.h" -#ifdef DEBUG_NV2A_GL +#if DEBUG_NV2A_GL #include #include diff --git a/hw/xbox/nv2a/pgraph/gl/debug.h b/hw/xbox/nv2a/pgraph/gl/debug.h index c242e1f384..1220244da8 100644 --- a/hw/xbox/nv2a/pgraph/gl/debug.h +++ b/hw/xbox/nv2a/pgraph/gl/debug.h @@ -21,8 +21,11 @@ #ifndef HW_XBOX_NV2A_PGRAPH_GL_DEBUG_H #define HW_XBOX_NV2A_PGRAPH_GL_DEBUG_H -// #define DEBUG_NV2A_GL -#ifdef DEBUG_NV2A_GL +#ifndef DEBUG_NV2A_GL +# define DEBUG_NV2A_GL 0 +#endif + +#if DEBUG_NV2A_GL #include #include "gloffscreen.h" diff --git a/hw/xbox/nv2a/pgraph/gl/display.c b/hw/xbox/nv2a/pgraph/gl/display.c index a978c54ab0..6d52a5c3b3 100644 --- a/hw/xbox/nv2a/pgraph/gl/display.c +++ b/hw/xbox/nv2a/pgraph/gl/display.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2024 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,6 +19,8 @@ * License along with this library; if not, see . */ +#include "qemu/osdep.h" +#include "hw/display/vga_int.h" #include "hw/xbox/nv2a/nv2a_int.h" #include "hw/xbox/nv2a/pgraph/util.h" #include "renderer.h" @@ -284,10 +286,10 @@ static void render_display(NV2AState *d, SurfaceBinding *surface) PGRAPHGLState *r = pg->gl_renderer_state; unsigned int width, height; - uint32_t pline_offset, pstart_addr, pline_compare; + VGADisplayParams vga_display_params; d->vga.get_resolution(&d->vga, (int*)&width, (int*)&height); - d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare); - int line_offset = pline_offset ? surface->pitch / pline_offset : 1; + d->vga.get_params(&d->vga, &vga_display_params); + int line_offset = vga_display_params.line_offset ? surface->pitch / vga_display_params.line_offset : 1; /* Adjust viewport height for interlaced mode, used only in 1080i */ if (d->vga.cr[NV_PRMCIO_INTERLACE_MODE] != NV_PRMCIO_INTERLACE_MODE_DISABLED) { @@ -376,9 +378,10 @@ static void gl_fence(void) void pgraph_gl_sync(NV2AState *d) { - uint32_t pline_offset, pstart_addr, pline_compare; - d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare); - SurfaceBinding *surface = pgraph_gl_surface_get_within(d, d->pcrtc.start + pline_offset); + VGADisplayParams vga_display_params; + d->vga.get_params(&d->vga, &vga_display_params); + + SurfaceBinding *surface = pgraph_gl_surface_get_within(d, d->pcrtc.start + vga_display_params.line_offset); if (surface == NULL) { qemu_event_set(&d->pgraph.sync_complete); return; @@ -411,9 +414,12 @@ int pgraph_gl_get_framebuffer_surface(NV2AState *d) qemu_mutex_lock(&d->pfifo.lock); // FIXME: Possible race condition with pgraph, consider lock - uint32_t pline_offset, pstart_addr, pline_compare; - d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare); - SurfaceBinding *surface = pgraph_gl_surface_get_within(d, d->pcrtc.start + pline_offset); + + VGADisplayParams vga_display_params; + d->vga.get_params(&d->vga, &vga_display_params); + + SurfaceBinding *surface = pgraph_gl_surface_get_within( + d, d->pcrtc.start + vga_display_params.line_offset); if (surface == NULL || !surface->color) { qemu_mutex_unlock(&d->pfifo.lock); return 0; diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.c b/hw/xbox/nv2a/pgraph/gl/renderer.c index f91a192b25..36b8029439 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.c +++ b/hw/xbox/nv2a/pgraph/gl/renderer.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2024 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -43,7 +43,7 @@ static void pgraph_gl_init(NV2AState *d, Error **errp) /* fire up opengl */ glo_set_current(g_nv2a_context_render); -#ifdef DEBUG_NV2A_GL +#if DEBUG_NV2A_GL gl_debug_initialize(); #endif diff --git a/hw/xbox/nv2a/pgraph/gl/renderer.h b/hw/xbox/nv2a/pgraph/gl/renderer.h index 918cbaa588..5c765361d6 100644 --- a/hw/xbox/nv2a/pgraph/gl/renderer.h +++ b/hw/xbox/nv2a/pgraph/gl/renderer.h @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2024 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -83,6 +83,16 @@ typedef struct TextureBinding { } TextureBinding; typedef struct ShaderBinding { + LruNode node; + bool initialized; + + bool cached; + void *program; + size_t program_size; + GLenum program_format; + ShaderState state; + QemuThread *save_thread; + GLuint gl_program; GLenum gl_primitive_mode; @@ -117,17 +127,6 @@ typedef struct ShaderBinding { GLint material_alpha_loc; } ShaderBinding; -typedef struct ShaderLruNode { - LruNode node; - bool cached; - void *program; - size_t program_size; - GLenum program_format; - ShaderState state; - ShaderBinding *binding; - QemuThread *save_thread; -} ShaderLruNode; - typedef struct VertexKey { size_t count; size_t stride; @@ -196,7 +195,7 @@ typedef struct PGRAPHGLState { TextureLruNode *texture_cache_entries; Lru shader_cache; - ShaderLruNode *shader_cache_entries; + ShaderBinding *shader_cache_entries; ShaderBinding *shader_binding; QemuMutex shader_cache_lock; QemuThread shader_disk_thread; @@ -279,8 +278,8 @@ SurfaceBinding *pgraph_gl_surface_get_within(NV2AState *d, hwaddr addr); void pgraph_gl_surface_invalidate(NV2AState *d, SurfaceBinding *e); void pgraph_gl_unbind_surface(NV2AState *d, bool color); void pgraph_gl_upload_surface_data(NV2AState *d, SurfaceBinding *surface, bool force); -void pgraph_gl_shader_cache_to_disk(ShaderLruNode *snode); -bool pgraph_gl_shader_load_from_memory(ShaderLruNode *snode); +void pgraph_gl_shader_cache_to_disk(ShaderBinding *snode); +bool pgraph_gl_shader_load_from_memory(ShaderBinding *snode); void pgraph_gl_shader_write_cache_reload_list(PGRAPHState *pg); void pgraph_gl_set_surface_scale_factor(NV2AState *d, unsigned int scale); unsigned int pgraph_gl_get_surface_scale_factor(NV2AState *d); diff --git a/hw/xbox/nv2a/pgraph/gl/shaders.c b/hw/xbox/nv2a/pgraph/gl/shaders.c index 5da89e10e3..f47105980c 100644 --- a/hw/xbox/nv2a/pgraph/gl/shaders.c +++ b/hw/xbox/nv2a/pgraph/gl/shaders.c @@ -3,7 +3,7 @@ * * Copyright (c) 2015 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2020-2024 Matt Borgerson + * Copyright (c) 2020-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -34,8 +34,6 @@ #include "debug.h" #include "renderer.h" -static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool binding_changed, bool vertex_program, bool fixed_function); - static GLenum get_gl_primitive_mode(enum ShaderPolygonMode polygon_mode, enum ShaderPrimitiveMode primitive_mode) { if (polygon_mode == POLY_MODE_POINT) { @@ -102,13 +100,12 @@ static GLuint create_gl_shader(GLenum gl_shader_type, return shader; } -static void update_shader_constant_locations(ShaderBinding *binding, const ShaderState *state) +static void update_shader_constant_locations(ShaderBinding *binding) { - int i, j; char tmp[64]; /* set texture samplers */ - for (i = 0; i < NV2A_MAX_TEXTURES; i++) { + for (int i = 0; i < NV2A_MAX_TEXTURES; i++) { char samplerName[16]; snprintf(samplerName, sizeof(samplerName), "texSamp%d", i); GLint texSampLoc = glGetUniformLocation(binding->gl_program, samplerName); @@ -129,14 +126,14 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade } /* lookup fragment shader uniforms */ - for (i = 0; i < 9; i++) { - for (j = 0; j < 2; j++) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 2; j++) { snprintf(tmp, sizeof(tmp), "c%d_%d", j, i); binding->psh_constant_loc[i][j] = glGetUniformLocation(binding->gl_program, tmp); } } binding->alpha_ref_loc = glGetUniformLocation(binding->gl_program, "alphaRef"); - for (i = 1; i < NV2A_MAX_TEXTURES; i++) { + for (int i = 1; i < NV2A_MAX_TEXTURES; i++) { snprintf(tmp, sizeof(tmp), "bumpMat%d", i); binding->bump_mat_loc[i] = glGetUniformLocation(binding->gl_program, tmp); snprintf(tmp, sizeof(tmp), "bumpScale%d", i); @@ -151,7 +148,7 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade } /* lookup vertex shader uniforms */ - for(i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) { + for (int i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) { snprintf(tmp, sizeof(tmp), "c[%d]", i); binding->vsh_constant_loc[i] = glGetUniformLocation(binding->gl_program, tmp); } @@ -161,19 +158,19 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade binding->fog_param_loc = glGetUniformLocation(binding->gl_program, "fogParam"); binding->inv_viewport_loc = glGetUniformLocation(binding->gl_program, "invViewport"); - for (i = 0; i < NV2A_LTCTXA_COUNT; i++) { + for (int i = 0; i < NV2A_LTCTXA_COUNT; i++) { snprintf(tmp, sizeof(tmp), "ltctxa[%d]", i); binding->ltctxa_loc[i] = glGetUniformLocation(binding->gl_program, tmp); } - for (i = 0; i < NV2A_LTCTXB_COUNT; i++) { + for (int i = 0; i < NV2A_LTCTXB_COUNT; i++) { snprintf(tmp, sizeof(tmp), "ltctxb[%d]", i); binding->ltctxb_loc[i] = glGetUniformLocation(binding->gl_program, tmp); } - for (i = 0; i < NV2A_LTC1_COUNT; i++) { + for (int i = 0; i < NV2A_LTC1_COUNT; i++) { snprintf(tmp, sizeof(tmp), "ltc1[%d]", i); binding->ltc1_loc[i] = glGetUniformLocation(binding->gl_program, tmp); } - for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + for (int i = 0; i < NV2A_MAX_LIGHTS; i++) { snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i); binding->light_infinite_half_vector_loc[i] = glGetUniformLocation(binding->gl_program, tmp); @@ -187,12 +184,12 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade binding->light_local_attenuation_loc[i] = glGetUniformLocation(binding->gl_program, tmp); } - for (i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { snprintf(tmp, sizeof(tmp), "clipRegion[%d]", i); binding->clip_region_loc[i] = glGetUniformLocation(binding->gl_program, tmp); } - if (state->fixed_function) { + if (binding->state.fixed_function) { binding->material_alpha_loc = glGetUniformLocation(binding->gl_program, "material_alpha"); } else { @@ -200,7 +197,7 @@ static void update_shader_constant_locations(ShaderBinding *binding, const Shade } } -static ShaderBinding *generate_shaders(const ShaderState *state) +static void generate_shaders(ShaderBinding *binding) { char *previous_numeric_locale = setlocale(LC_NUMERIC, NULL); if (previous_numeric_locale) { @@ -211,6 +208,8 @@ static ShaderBinding *generate_shaders(const ShaderState *state) setlocale(LC_NUMERIC, "C"); GLuint program = glCreateProgram(); + ShaderState *state = &binding->state; + /* Create an optional geometry shader and find primitive type */ GLenum gl_primitive_mode = get_gl_primitive_mode(state->polygon_front_mode, state->primitive_mode); @@ -262,18 +261,15 @@ static ShaderBinding *generate_shaders(const ShaderState *state) glUseProgram(program); - ShaderBinding* ret = g_malloc0(sizeof(ShaderBinding)); - ret->gl_program = program; - ret->gl_primitive_mode = gl_primitive_mode; - - update_shader_constant_locations(ret, state); + binding->initialized = true; + binding->gl_program = program; + binding->gl_primitive_mode = gl_primitive_mode; + update_shader_constant_locations(binding); if (previous_numeric_locale) { setlocale(LC_NUMERIC, previous_numeric_locale); g_free(previous_numeric_locale); } - - return ret; } static const char *shader_gl_vendor = NULL; @@ -329,19 +325,22 @@ void pgraph_gl_shader_write_cache_reload_list(PGRAPHState *pg) qemu_event_set(&r->shader_cache_writeback_complete); } -bool pgraph_gl_shader_load_from_memory(ShaderLruNode *snode) +bool pgraph_gl_shader_load_from_memory(ShaderBinding *binding) { assert(glGetError() == GL_NO_ERROR); - if (!snode->program) { + if (!binding->program) { return false; } GLuint gl_program = glCreateProgram(); - glProgramBinary(gl_program, snode->program_format, snode->program, snode->program_size); + glProgramBinary(gl_program, binding->program_format, binding->program, + binding->program_size); GLint gl_error = glGetError(); if (gl_error != GL_NO_ERROR) { - NV2A_DPRINTF("failed to load shader binary from disk: GL error code %d\n", gl_error); + NV2A_DPRINTF( + "failed to load shader binary from disk: GL error code %d\n", + gl_error); glDeleteProgram(gl_program); return false; } @@ -359,16 +358,15 @@ bool pgraph_gl_shader_load_from_memory(ShaderLruNode *snode) glUseProgram(gl_program); - ShaderBinding* binding = g_malloc0(sizeof(ShaderBinding)); binding->gl_program = gl_program; - binding->gl_primitive_mode = get_gl_primitive_mode(snode->state.polygon_front_mode, - snode->state.primitive_mode); - snode->binding = binding; + binding->gl_primitive_mode = get_gl_primitive_mode( + binding->state.polygon_front_mode, binding->state.primitive_mode); + binding->initialized = true; - g_free(snode->program); - snode->program = NULL; + g_free(binding->program); + binding->program = NULL; - update_shader_constant_locations(binding, &snode->state); + update_shader_constant_locations(binding); return true; } @@ -461,18 +459,18 @@ static void shader_load_from_disk(PGRAPHState *pg, uint64_t hash) qemu_mutex_lock(&r->shader_cache_lock); LruNode *node = lru_lookup(&r->shader_cache, hash, &state); - ShaderLruNode *snode = container_of(node, ShaderLruNode, node); + ShaderBinding *binding = container_of(node, ShaderBinding, node); /* If we happened to regenerate this shader already, then we may as well use the new one */ - if (snode->binding) { + if (binding->initialized) { qemu_mutex_unlock(&r->shader_cache_lock); return; } - snode->program_format = program_binary_format; - snode->program_size = shader_size; - snode->program = program_buffer; - snode->cached = true; + binding->program_format = program_binary_format; + binding->program_size = shader_size; + binding->program = program_buffer; + binding->cached = true; qemu_mutex_unlock(&r->shader_cache_lock); return; @@ -510,43 +508,38 @@ static void *shader_reload_lru_from_disk(void *arg) static void shader_cache_entry_init(Lru *lru, LruNode *node, void *state) { - ShaderLruNode *snode = container_of(node, ShaderLruNode, node); - memcpy(&snode->state, state, sizeof(ShaderState)); - snode->cached = false; - snode->binding = NULL; - snode->program = NULL; - snode->save_thread = NULL; + ShaderBinding *binding = container_of(node, ShaderBinding, node); + memcpy(&binding->state, state, sizeof(ShaderState)); + binding->initialized = false; + binding->cached = false; + binding->program = NULL; + binding->save_thread = NULL; } static void shader_cache_entry_post_evict(Lru *lru, LruNode *node) { - ShaderLruNode *snode = container_of(node, ShaderLruNode, node); + ShaderBinding *binding = container_of(node, ShaderBinding, node); - if (snode->save_thread) { - qemu_thread_join(snode->save_thread); - g_free(snode->save_thread); + if (binding->save_thread) { + qemu_thread_join(binding->save_thread); + g_free(binding->save_thread); } - if (snode->binding) { - glDeleteProgram(snode->binding->gl_program); - g_free(snode->binding); + glDeleteProgram(binding->gl_program); + if (binding->program) { + g_free(binding->program); } - if (snode->program) { - g_free(snode->program); - } - - snode->cached = false; - snode->save_thread = NULL; - snode->binding = NULL; - snode->program = NULL; - memset(&snode->state, 0, sizeof(ShaderState)); + binding->cached = false; + binding->save_thread = NULL; + binding->program = NULL; + memset(&binding->state, 0, sizeof(ShaderState)); } static bool shader_cache_entry_compare(Lru *lru, LruNode *node, void *key) { - ShaderLruNode *snode = container_of(node, ShaderLruNode, node); - return memcmp(&snode->state, key, sizeof(ShaderState)); + ShaderBinding *binding = container_of(node, ShaderBinding, node); + return memcmp(&binding->state, key, sizeof(ShaderState)); } void pgraph_gl_init_shaders(PGRAPHState *pg) @@ -565,7 +558,7 @@ void pgraph_gl_init_shaders(PGRAPHState *pg) /* FIXME: Make this configurable */ const size_t shader_cache_size = 50*1024; lru_init(&r->shader_cache); - r->shader_cache_entries = malloc(shader_cache_size * sizeof(ShaderLruNode)); + r->shader_cache_entries = malloc(shader_cache_size * sizeof(ShaderBinding)); assert(r->shader_cache_entries != NULL); for (int i = 0; i < shader_cache_size; i++) { lru_add_free(&r->shader_cache, &r->shader_cache_entries[i].node); @@ -593,10 +586,10 @@ void pgraph_gl_finalize_shaders(PGRAPHState *pg) static void *shader_write_to_disk(void *arg) { - ShaderLruNode *snode = (ShaderLruNode*) arg; + ShaderBinding *binding = (ShaderBinding*) arg; - char *shader_bin = shader_get_bin_directory(snode->node.hash); - char *shader_path = shader_get_binary_path(shader_bin, snode->node.hash); + char *shader_bin = shader_get_bin_directory(binding->node.hash); + char *shader_path = shader_get_binary_path(shader_bin, binding->node.hash); static uint64_t gl_vendor_len; if (gl_vendor_len == 0) { @@ -632,19 +625,19 @@ static void *shader_write_to_disk(void *arg) WRITE_OR_ERR(&gl_vendor_len, sizeof(gl_vendor_len)); WRITE_OR_ERR(shader_gl_vendor, gl_vendor_len); - WRITE_OR_ERR(&snode->program_format, sizeof(snode->program_format)); - WRITE_OR_ERR(&snode->state, sizeof(snode->state)); + WRITE_OR_ERR(&binding->program_format, sizeof(binding->program_format)); + WRITE_OR_ERR(&binding->state, sizeof(binding->state)); - WRITE_OR_ERR(&snode->program_size, sizeof(snode->program_size)); - WRITE_OR_ERR(snode->program, snode->program_size); + WRITE_OR_ERR(&binding->program_size, sizeof(binding->program_size)); + WRITE_OR_ERR(binding->program, binding->program_size); #undef WRITE_OR_ERR fclose(shader_file); g_free(shader_path); - g_free(snode->program); - snode->program = NULL; + g_free(binding->program); + binding->program = NULL; return NULL; @@ -652,23 +645,23 @@ error: fprintf(stderr, "nv2a: Failed to write shader binary file to %s\n", shader_path); qemu_unlink(shader_path); g_free(shader_path); - g_free(snode->program); - snode->program = NULL; + g_free(binding->program); + binding->program = NULL; return NULL; } -void pgraph_gl_shader_cache_to_disk(ShaderLruNode *snode) +void pgraph_gl_shader_cache_to_disk(ShaderBinding *binding) { - if (!snode->binding || snode->cached) { + if (binding->cached) { return; } GLint program_size; - glGetProgramiv(snode->binding->gl_program, GL_PROGRAM_BINARY_LENGTH, &program_size); + glGetProgramiv(binding->gl_program, GL_PROGRAM_BINARY_LENGTH, &program_size); - if (snode->program) { - g_free(snode->program); - snode->program = NULL; + if (binding->program) { + g_free(binding->program); + binding->program = NULL; } /* program_size might be zero on some systems, if no binary formats are supported */ @@ -676,27 +669,23 @@ void pgraph_gl_shader_cache_to_disk(ShaderLruNode *snode) return; } - snode->program = g_malloc(program_size); + binding->program = g_malloc(program_size); GLsizei program_size_copied; - glGetProgramBinary(snode->binding->gl_program, program_size, &program_size_copied, - &snode->program_format, snode->program); + glGetProgramBinary(binding->gl_program, program_size, &program_size_copied, + &binding->program_format, binding->program); assert(glGetError() == GL_NO_ERROR); - snode->program_size = program_size_copied; - snode->cached = true; + binding->program_size = program_size_copied; + binding->cached = true; char name[24]; - snprintf(name, sizeof(name), "scache-%llx", (unsigned long long) snode->node.hash); - snode->save_thread = g_malloc0(sizeof(QemuThread)); - qemu_thread_create(snode->save_thread, name, shader_write_to_disk, snode, QEMU_THREAD_JOINABLE); + snprintf(name, sizeof(name), "scache-%llx", (unsigned long long) binding->node.hash); + binding->save_thread = g_malloc0(sizeof(QemuThread)); + qemu_thread_create(binding->save_thread, name, shader_write_to_disk, binding, QEMU_THREAD_JOINABLE); } static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, - bool binding_changed, - - // FIXME: Remove these... We already know it from binding.state - bool vertex_program, - bool fixed_function) + bool binding_changed) { PGRAPHGLState *r = pg->gl_renderer_state; int i, j; @@ -797,7 +786,7 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, assert(0); } - if (fixed_function) { + if (binding->state.fixed_function) { /* update lighting constants */ struct { uint32_t* v; @@ -1016,10 +1005,7 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg) goto update_constants; } - pg->program_data_dirty = false; - - ShaderBinding* old_binding = r->shader_binding; - + ShaderBinding *old_binding = r->shader_binding; ShaderState state = pgraph_get_shader_state(pg); assert(!state.vulkan); @@ -1027,22 +1013,24 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg) state.vertex_program ? "yes" : "no", state.fixed_function ? "yes" : "no"); - uint64_t shader_state_hash = fast_hash((uint8_t*) &state, sizeof(ShaderState)); qemu_mutex_lock(&r->shader_cache_lock); - LruNode *node = lru_lookup(&r->shader_cache, shader_state_hash, &state); - ShaderLruNode *snode = container_of(node, ShaderLruNode, node); - if (snode->binding || pgraph_gl_shader_load_from_memory(snode)) { - r->shader_binding = snode->binding; - } else { - r->shader_binding = generate_shaders(&state); - nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN); - /* cache it */ - snode->binding = r->shader_binding; + uint64_t shader_state_hash = + fast_hash((uint8_t *)&state, sizeof(ShaderState)); + + LruNode *node = lru_lookup(&r->shader_cache, shader_state_hash, &state); + ShaderBinding *binding = container_of(node, ShaderBinding, node); + + if (!binding->initialized && !pgraph_gl_shader_load_from_memory(binding)) { + nv2a_profile_inc_counter(NV2A_PROF_SHADER_GEN); + generate_shaders(binding); if (g_config.perf.cache_shaders) { - pgraph_gl_shader_cache_to_disk(snode); + pgraph_gl_shader_cache_to_disk(binding); } } + assert(binding->initialized); + r->shader_binding = binding; + pg->program_data_dirty = false; qemu_mutex_unlock(&r->shader_cache_lock); @@ -1055,8 +1043,9 @@ void pgraph_gl_bind_shaders(PGRAPHState *pg) NV2A_GL_DGROUP_END(); update_constants: - shader_update_constants(pg, r->shader_binding, binding_changed, - state.vertex_program, state.fixed_function); + assert(r->shader_binding); + assert(r->shader_binding->initialized); + shader_update_constants(pg, r->shader_binding, binding_changed); } GLuint pgraph_gl_compile_shader(const char *vs_src, const char *fs_src) diff --git a/hw/xbox/nv2a/pgraph/gl/surface.c b/hw/xbox/nv2a/pgraph/gl/surface.c index 802a3febc0..cd6dd145ca 100644 --- a/hw/xbox/nv2a/pgraph/gl/surface.c +++ b/hw/xbox/nv2a/pgraph/gl/surface.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2024 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -482,12 +482,12 @@ static SurfaceBinding *surface_put(NV2AState *d, hwaddr addr, if (tcg_enabled()) { qemu_mutex_unlock(&d->pgraph.lock); - qemu_mutex_lock_iothread(); + bql_lock(); mem_access_callback_insert(qemu_get_cpu(0), d->vram, surface_out->vram_addr, surface_out->size, &surface_out->access_cb, &surface_access_callback, surface_out); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->pgraph.lock); } @@ -545,9 +545,9 @@ void pgraph_gl_surface_invalidate(NV2AState *d, SurfaceBinding *surface) if (tcg_enabled()) { qemu_mutex_unlock(&d->pgraph.lock); - qemu_mutex_lock_iothread(); + bql_lock(); mem_access_callback_remove_by_ref(qemu_get_cpu(0), surface->access_cb); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->pgraph.lock); } diff --git a/hw/xbox/nv2a/pgraph/gl/texture.c b/hw/xbox/nv2a/pgraph/gl/texture.c index b951b7e1e0..1f2d599eda 100644 --- a/hw/xbox/nv2a/pgraph/gl/texture.c +++ b/hw/xbox/nv2a/pgraph/gl/texture.c @@ -666,8 +666,6 @@ static TextureBinding* generate_texture(const TextureShape s, s.width, s.height, s.depth); if (gl_target == GL_TEXTURE_CUBE_MAP) { - - ColorFormatInfo f = kelvin_color_format_gl_map[s.color_format]; unsigned int block_size; if (f.gl_internal_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { block_size = 8; diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index fcb594d500..9775db2a8a 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -250,9 +250,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C0: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C0 || ps->cur_stage == 8) { - MString *reg = mstring_from_fmt("c0_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg)); - return reg; + MString *reg_name = mstring_from_fmt("c0_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg_name)); + return reg_name; } else { // Same c0 add_const_ref(ps, "c0_0"); return mstring_from_str("c0_0"); @@ -260,9 +260,9 @@ static MString* get_var(struct PixelShader *ps, int reg, bool is_dest) break; case PS_REGISTER_C1: if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C1 || ps->cur_stage == 8) { - MString *reg = mstring_from_fmt("c1_%d", ps->cur_stage); - add_const_ref(ps, mstring_get_str(reg)); - return reg; + MString *reg_name = mstring_from_fmt("c1_%d", ps->cur_stage); + add_const_ref(ps, mstring_get_str(reg_name)); + return reg_name; } else { // Same c1 add_const_ref(ps, "c1_0"); return mstring_from_str("c1_0"); @@ -728,8 +728,6 @@ static void apply_convolution_filter(const struct PixelShader *ps, MString *vars static MString* psh_convert(struct PixelShader *ps) { - int i; - const char *u = ps->state.vulkan ? "" : "uniform "; // FIXME: Remove MString *preflight = mstring_new(); @@ -895,7 +893,7 @@ static MString* psh_convert(struct PixelShader *ps) ps->code = mstring_new(); - for (i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { const char *sampler_type = get_sampler_type(ps->tex_modes[i], &ps->state, i); @@ -969,8 +967,7 @@ static MString* psh_convert(struct PixelShader *ps) i, ps->input_tex[i], ps->input_tex[i]); } - mstring_append_fmt(vars, "dsdt%d = bumpMat%d * dsdt%d;\n", - i, i, i, i); + mstring_append_fmt(vars, "dsdt%d = bumpMat%d * dsdt%d;\n", i, i, i); if (ps->state.dim_tex[i] == 2) { mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, %s(pT%d.xy + dsdt%d));\n", @@ -997,7 +994,7 @@ static MString* psh_convert(struct PixelShader *ps) } mstring_append_fmt(vars, "dsdtl%d.st = bumpMat%d * dsdtl%d.st;\n", - i, i, i, i); + i, i, i); if (ps->state.dim_tex[i] == 2) { mstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, %s(pT%d.xy + dsdtl%d.st));\n", @@ -1159,7 +1156,7 @@ static MString* psh_convert(struct PixelShader *ps) } } - for (i = 0; i < ps->num_stages; i++) { + for (int i = 0; i < ps->num_stages; i++) { ps->cur_stage = i; mstring_append_fmt(ps->code, "// Stage %d\n", i); MString* color = add_stage_code(ps, ps->stage[i].rgb_input, ps->stage[i].rgb_output, "rgb", false); @@ -1198,7 +1195,7 @@ static MString* psh_convert(struct PixelShader *ps) } } - for (i = 0; i < ps->num_var_refs; i++) { + for (int i = 0; i < ps->num_var_refs; i++) { mstring_append_fmt(vars, "vec4 %s = vec4(0);\n", ps->var_refs[i]); if (strcmp(ps->var_refs[i], "r0") == 0) { if (ps->tex_modes[0] != PS_TEXTUREMODES_NONE) { diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index 7bebed71e8..650d95854c 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -368,7 +368,7 @@ static MString* decode_opcode_input(const uint32_t *shader_token, if (vsh_get_field(shader_token, neg_field) > 0) { - mstring_append_chr(ret_str, '-'); + mstring_append_fmt(ret_str, "-"); } /* PARAM_R uses the supplied reg_num, but the other two need to be @@ -446,10 +446,9 @@ static MString* decode_opcode(const uint32_t *shader_token, bool write_fog_register = false; if (vsh_get_field(shader_token, FLD_OUT_ORB) == OUTPUT_C) { assert(!"TODO: Emulate writeable const registers"); - mstring_append(ret, "c"); - mstring_append_int(ret, - convert_c_register( - vsh_get_field(shader_token, FLD_OUT_ADDRESS))); + mstring_append_fmt(ret, "c%d", + convert_c_register(vsh_get_field( + shader_token, FLD_OUT_ADDRESS))); } else { int out_reg = vsh_get_field(shader_token, FLD_OUT_ADDRESS) & 0xF; mstring_append(ret,out_reg_name[out_reg]); diff --git a/hw/xbox/nv2a/pgraph/pgraph.c b/hw/xbox/nv2a/pgraph/pgraph.c index 534daa3c0f..9db475796e 100644 --- a/hw/xbox/nv2a/pgraph/pgraph.c +++ b/hw/xbox/nv2a/pgraph/pgraph.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2024 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -136,7 +136,7 @@ void pgraph_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size) NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4; if (val & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) { -#ifdef DEBUG_NV2A +#if DEBUG_NV2A unsigned pgraph_channel_id = PG_GET_MASK(NV_PGRAPH_CTX_USER, NV_PGRAPH_CTX_USER_CHID); #endif @@ -197,10 +197,10 @@ void pgraph_context_switch(NV2AState *d, unsigned int channel_id) pg->waiting_for_context_switch = true; qemu_mutex_unlock(&pg->lock); - qemu_mutex_lock_iothread(); + bql_lock(); pg->pending_interrupts |= NV_PGRAPH_INTR_CONTEXT_SWITCH; nv2a_update_irq(d); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&pg->lock); } } @@ -381,13 +381,13 @@ void nv2a_set_surface_scale_factor(unsigned int scale) { NV2AState *d = g_nv2a; - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->pgraph.renderer_lock); if (d->pgraph.renderer->ops.set_surface_scale_factor) { d->pgraph.renderer->ops.set_surface_scale_factor(d, scale); } qemu_mutex_unlock(&d->pgraph.renderer_lock); - qemu_mutex_lock_iothread(); + bql_lock(); } unsigned int nv2a_get_surface_scale_factor(void) @@ -395,13 +395,13 @@ unsigned int nv2a_get_surface_scale_factor(void) NV2AState *d = g_nv2a; int s = 1; - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->pgraph.renderer_lock); if (d->pgraph.renderer->ops.get_surface_scale_factor) { s = d->pgraph.renderer->ops.get_surface_scale_factor(d); } qemu_mutex_unlock(&d->pgraph.renderer_lock); - qemu_mutex_lock_iothread(); + bql_lock(); return s; } @@ -846,9 +846,9 @@ DEF_METHOD(NV097, NO_OPERATION) pg->waiting_for_nop = true; qemu_mutex_unlock(&pg->lock); - qemu_mutex_lock_iothread(); + bql_lock(); nv2a_update_irq(d); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&pg->lock); } @@ -2649,7 +2649,7 @@ DEF_METHOD(NV097, BACK_END_WRITE_SEMAPHORE_RELEASE) d->pgraph.renderer->ops.surface_update(d, false, true, true); //qemu_mutex_unlock(&d->pgraph.lock); - //qemu_mutex_lock_iothread(); + //bql_lock(); uint32_t semaphore_offset = pgraph_reg_r(pg, NV_PGRAPH_SEMAPHOREOFFSET); @@ -2662,7 +2662,7 @@ DEF_METHOD(NV097, BACK_END_WRITE_SEMAPHORE_RELEASE) stl_le_p((uint32_t*)semaphore_data, parameter); //qemu_mutex_lock(&d->pgraph.lock); - //qemu_mutex_unlock_iothread(); + //bql_unlock(); } DEF_METHOD(NV097, SET_ZMIN_MAX_CONTROL) diff --git a/hw/xbox/nv2a/pgraph/texture.c b/hw/xbox/nv2a/pgraph/texture.c index e5350ea8d4..30bfa87990 100644 --- a/hw/xbox/nv2a/pgraph/texture.c +++ b/hw/xbox/nv2a/pgraph/texture.c @@ -3,7 +3,7 @@ * * Copyright (c) 2012 espes * Copyright (c) 2015 Jannik Vogel - * Copyright (c) 2018-2024 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -238,7 +238,7 @@ TextureShape pgraph_get_texture_shape(PGRAPHState *pg, int texture_idx) unsigned int rect_height = GET_MASK(pgraph_reg_r(pg, NV_PGRAPH_TEXIMAGERECT0 + i*4), NV_PGRAPH_TEXIMAGERECT0_HEIGHT); -#ifdef DEBUG_NV2A +#if DEBUG_NV2A unsigned int lod_bias = GET_MASK(filter, NV_PGRAPH_TEXFILTER0_MIPMAP_LOD_BIAS); #endif diff --git a/hw/xbox/nv2a/pgraph/vk/debug.h b/hw/xbox/nv2a/pgraph/vk/debug.h index 62cd63e592..d281fc9796 100644 --- a/hw/xbox/nv2a/pgraph/vk/debug.h +++ b/hw/xbox/nv2a/pgraph/vk/debug.h @@ -24,13 +24,12 @@ extern int nv2a_vk_dgroup_indent; -#define NV2A_VK_XDPRINTF(x, fmt, ...) \ - do { \ - if (x) { \ - for (int i = 0; i < nv2a_vk_dgroup_indent; i++) \ - fprintf(stderr, " "); \ - fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ - } \ +#define NV2A_VK_XDPRINTF(x, fmt, ...) \ + do { \ + if (x) { \ + fprintf(stderr, "%*s" fmt "\n", nv2a_vk_dgroup_indent, "", \ + ##__VA_ARGS__); \ + } \ } while (0) #define NV2A_VK_DPRINTF(fmt, ...) NV2A_VK_XDPRINTF(DEBUG_VK, fmt, ##__VA_ARGS__) diff --git a/hw/xbox/nv2a/pgraph/vk/display.c b/hw/xbox/nv2a/pgraph/vk/display.c index 4298214898..e1993d4186 100644 --- a/hw/xbox/nv2a/pgraph/vk/display.c +++ b/hw/xbox/nv2a/pgraph/vk/display.c @@ -1,7 +1,7 @@ /* * Geforce NV2A PGRAPH Vulkan Renderer * - * Copyright (c) 2024 Matt Borgerson + * Copyright (c) 2024-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -870,9 +870,11 @@ static void update_uniforms(PGRAPHState *pg, SurfaceBinding *surface) int display_size_loc = uniform_index(l, "display_size"); // FIXME: Cache uniform2f(l, display_size_loc, r->display.width, r->display.height); - uint32_t pline_offset, pstart_addr, pline_compare; - d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare); - int line_offset = pline_offset ? surface->pitch / pline_offset : 1; + VGADisplayParams vga_display_params; + d->vga.get_params(&d->vga, &vga_display_params); + int line_offset = vga_display_params.line_offset ? + surface->pitch / vga_display_params.line_offset : + 1; int line_offset_loc = uniform_index(l, "line_offset"); uniform1f(l, line_offset_loc, line_offset); @@ -1065,11 +1067,11 @@ void pgraph_vk_render_display(PGRAPHState *pg) NV2AState *d = container_of(pg, NV2AState, pgraph); PGRAPHVkState *r = pg->vk_renderer_state; - uint32_t pline_offset, pstart_addr, pline_compare; - d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare); + VGADisplayParams vga_display_params; + d->vga.get_params(&d->vga, &vga_display_params); - SurfaceBinding *surface = - pgraph_vk_surface_get_within(d, d->pcrtc.start + pline_offset); + SurfaceBinding *surface = pgraph_vk_surface_get_within( + d, d->pcrtc.start + vga_display_params.line_offset); if (surface == NULL || !surface->color) { return; } diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.c b/hw/xbox/nv2a/pgraph/vk/renderer.c index 272b5f6ae5..3dbc724b95 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.c +++ b/hw/xbox/nv2a/pgraph/vk/renderer.c @@ -1,7 +1,7 @@ /* * Geforce NV2A PGRAPH Vulkan Renderer * - * Copyright (c) 2024 Matt Borgerson + * Copyright (c) 2024-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -173,10 +173,12 @@ static int pgraph_vk_get_framebuffer_surface(NV2AState *d) PGRAPHVkState *r = pg->vk_renderer_state; qemu_mutex_lock(&d->pfifo.lock); - // FIXME: Possible race condition with pgraph, consider lock - uint32_t pline_offset, pstart_addr, pline_compare; - d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare); - SurfaceBinding *surface = pgraph_vk_surface_get_within(d, d->pcrtc.start + pline_offset); + + VGADisplayParams vga_display_params; + d->vga.get_params(&d->vga, &vga_display_params); + + SurfaceBinding *surface = pgraph_vk_surface_get_within( + d, d->pcrtc.start + vga_display_params.line_offset); if (surface == NULL || !surface->color) { qemu_mutex_unlock(&d->pfifo.lock); return 0; diff --git a/hw/xbox/nv2a/pgraph/vk/shaders.c b/hw/xbox/nv2a/pgraph/vk/shaders.c index f831dece46..1f4ad765de 100644 --- a/hw/xbox/nv2a/pgraph/vk/shaders.c +++ b/hw/xbox/nv2a/pgraph/vk/shaders.c @@ -238,12 +238,11 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg) static void update_shader_constant_locations(ShaderBinding *binding) { - int i, j; char tmp[64]; /* lookup fragment shader uniforms */ - for (i = 0; i < 9; i++) { - for (j = 0; j < 2; j++) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 2; j++) { snprintf(tmp, sizeof(tmp), "c%d_%d", j, i); binding->psh_constant_loc[i][j] = uniform_index(&binding->fragment->uniforms, tmp); @@ -253,7 +252,7 @@ static void update_shader_constant_locations(ShaderBinding *binding) uniform_index(&binding->fragment->uniforms, "alphaRef"); binding->fog_color_loc = uniform_index(&binding->fragment->uniforms, "fogColor"); - for (i = 1; i < NV2A_MAX_TEXTURES; i++) { + for (int i = 1; i < NV2A_MAX_TEXTURES; i++) { snprintf(tmp, sizeof(tmp), "bumpMat%d", i); binding->bump_mat_loc[i] = uniform_index(&binding->fragment->uniforms, tmp); @@ -286,7 +285,7 @@ static void update_shader_constant_locations(ShaderBinding *binding) binding->ltctxb_loc = uniform_index(&binding->vertex->uniforms, "ltctxb"); binding->ltc1_loc = uniform_index(&binding->vertex->uniforms, "ltc1"); - for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + for (int i = 0; i < NV2A_MAX_LIGHTS; i++) { snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i); binding->light_infinite_half_vector_loc[i] = uniform_index(&binding->vertex->uniforms, tmp); @@ -455,10 +454,9 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, bool fixed_function) { ShaderState *state = &binding->state; - int i, j; /* update combiner constants */ - for (i = 0; i < 9; i++) { + for (int i = 0; i < 9; i++) { uint32_t constant[2]; if (i == 8) { /* final combiner */ @@ -469,7 +467,7 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, constant[1] = pgraph_reg_r(pg, NV_PGRAPH_COMBINEFACTOR1 + i * 4); } - for (j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) { GLint loc = binding->psh_constant_loc[i][j]; if (loc != -1) { float value[4]; @@ -488,7 +486,7 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, /* For each texture stage */ - for (i = 0; i < NV2A_MAX_TEXTURES; i++) { + for (int i = 0; i < NV2A_MAX_TEXTURES; i++) { int loc; /* Bump luminance only during stages 1 - 3 */ @@ -576,13 +574,13 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, { &pg->ltc1[0][0], binding->ltc1_loc, NV2A_LTC1_COUNT }, }; - for (i = 0; i < ARRAY_SIZE(lighting_arrays); i++) { + for (int i = 0; i < ARRAY_SIZE(lighting_arrays); i++) { uniform1iv( &binding->vertex->uniforms, lighting_arrays[i].locs, lighting_arrays[i].len * 4, (void *)lighting_arrays[i].v); } - for (i = 0; i < NV2A_MAX_LIGHTS; i++) { + for (int i = 0; i < NV2A_MAX_LIGHTS; i++) { int loc = binding->light_infinite_half_vector_loc[i]; if (loc != -1) { uniform1fv(&binding->vertex->uniforms, loc, 3, @@ -657,7 +655,7 @@ static void shader_update_constants(PGRAPHState *pg, ShaderBinding *binding, uint32_t clip_regions[8][4]; - for (i = 0; i < 8; i++) { + for (int i = 0; i < 8; i++) { uint32_t x = pgraph_reg_r(pg, NV_PGRAPH_WINDOWCLIPX0 + i * 4); unsigned int x_min = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMIN); unsigned int x_max = GET_MASK(x, NV_PGRAPH_WINDOWCLIPX0_XMAX) + 1; diff --git a/hw/xbox/nv2a/pgraph/vk/surface.c b/hw/xbox/nv2a/pgraph/vk/surface.c index f7f68bb0d4..2ce4f84695 100644 --- a/hw/xbox/nv2a/pgraph/vk/surface.c +++ b/hw/xbox/nv2a/pgraph/vk/surface.c @@ -1,7 +1,7 @@ /* * Geforce NV2A PGRAPH Vulkan Renderer * - * Copyright (c) 2024 Matt Borgerson + * Copyright (c) 2024-2025 Matt Borgerson * * Based on GL implementation: * @@ -284,19 +284,20 @@ static void download_surface_to_buffer(NV2AState *d, SurfaceBinding *surface, BUFFER_STAGING_DST; VkBuffer copy_buffer = r->storage_buffers[copy_buffer_idx].buffer; - VkBufferMemoryBarrier pre_copy_dst_barrier = { - .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, - .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT, - .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = copy_buffer, - .size = VK_WHOLE_SIZE - }; - vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1, - &pre_copy_dst_barrier, 0, NULL); - + { + VkBufferMemoryBarrier pre_copy_dst_barrier = { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = copy_buffer, + .size = VK_WHOLE_SIZE + }; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1, + &pre_copy_dst_barrier, 0, NULL); + } vkCmdCopyImageToBuffer(cmd, surface_image_loc, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, copy_buffer, num_copy_regions, copy_regions); @@ -546,12 +547,12 @@ static void register_cpu_access_callback(NV2AState *d, SurfaceBinding *surface) { if (tcg_enabled()) { qemu_mutex_unlock(&d->pgraph.lock); - qemu_mutex_lock_iothread(); + bql_lock(); mem_access_callback_insert(qemu_get_cpu(0), d->vram, surface->vram_addr, surface->size, &surface->access_cb, &surface_access_callback, surface); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->pgraph.lock); } } @@ -561,9 +562,9 @@ static void unregister_cpu_access_callback(NV2AState *d, { if (tcg_enabled()) { qemu_mutex_unlock(&d->pgraph.lock); - qemu_mutex_lock_iothread(); + bql_lock(); mem_access_callback_remove_by_ref(qemu_get_cpu(0), surface->access_cb); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_lock(&d->pgraph.lock); } } @@ -1179,10 +1180,6 @@ void pgraph_vk_upload_surface_data(NV2AState *d, SurfaceBinding *surface, !use_compute_to_convert_depth_stencil_format; if (upscale) { - unsigned int scaled_width = surface->width, - scaled_height = surface->height; - pgraph_apply_scaling_factor(pg, &scaled_width, &scaled_height); - VkImageBlit blitRegion = { .srcSubresource.aspectMask = surface->host_fmt.aspect, .srcSubresource.mipLevel = 0, diff --git a/hw/xbox/nvnet.c b/hw/xbox/nvnet.c index 2440b2cd93..fef42f0360 100644 --- a/hw/xbox/nvnet.c +++ b/hw/xbox/nvnet.c @@ -2,7 +2,7 @@ * QEMU nForce Ethernet Controller implementation * * Copyright (c) 2013 espes - * Copyright (c) 2015-2021 Matt Borgerson + * Copyright (c) 2015-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,8 +21,8 @@ #include "qemu/osdep.h" #include "trace.h" #include "hw/hw.h" -#include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "net/net.h" #include "qemu/bswap.h" @@ -98,7 +98,7 @@ static void nvnet_uninit(PCIDevice *dev); static void nvnet_class_init(ObjectClass *klass, void *data); static void nvnet_cleanup(NetClientState *nc); static void nvnet_reset(void *opaque); -static void qdev_nvnet_reset(DeviceState *dev); +static void nvnet_reset_hold(Object *obj, ResetType type); static void nvnet_register(void); /* MMIO / IO / Phy / Device Register Access */ @@ -290,11 +290,9 @@ out: */ static uint64_t nvnet_mmio_read(void *opaque, hwaddr addr, unsigned int size) { - NvNetState *s; + NvNetState *s = NVNET_DEVICE(opaque); uint64_t retval; - s = NVNET_DEVICE(opaque); - switch (addr) { case NvRegMIIData: assert(size == 4); @@ -415,7 +413,7 @@ static void nvnet_send_packet(NvNetState *s, const uint8_t *buf, int size) static bool nvnet_can_receive(NetClientState *nc) { NVNET_DPRINTF("nvnet_can_receive called\n"); - return 1; + return true; } static ssize_t nvnet_receive(NetClientState *nc, @@ -520,16 +518,15 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s, const uint8_t *buf, size_t size) { PCIDevice *d = PCI_DEVICE(s); - struct RingDesc desc; - int i; bool did_receive = false; nvnet_set_reg(s, NvRegTxRxControl, nvnet_get_reg(s, NvRegTxRxControl, 4) & ~NVREG_TXRXCTL_IDLE, 4); - for (i = 0; i < s->rx_ring_size; i++) { + for (int i = 0; i < s->rx_ring_size; i++) { /* Read current ring descriptor */ + struct RingDesc desc; s->rx_ring_index %= s->rx_ring_size; dma_addr_t rx_ring_addr = nvnet_get_reg(s, NvRegRxRingPhysAddr, 4); rx_ring_addr += s->rx_ring_index * sizeof(desc); @@ -586,17 +583,15 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s, static ssize_t nvnet_dma_packet_from_guest(NvNetState *s) { PCIDevice *d = PCI_DEVICE(s); - struct RingDesc desc; - bool is_last_packet; bool packet_sent = false; - int i; nvnet_set_reg(s, NvRegTxRxControl, nvnet_get_reg(s, NvRegTxRxControl, 4) & ~NVREG_TXRXCTL_IDLE, 4); - for (i = 0; i < s->tx_ring_size; i++) { + for (int i = 0; i < s->tx_ring_size; i++) { /* Read ring descriptor */ + struct RingDesc desc; s->tx_ring_index %= s->tx_ring_size; dma_addr_t tx_ring_addr = nvnet_get_reg(s, NvRegTxRingPhysAddr, 4); tx_ring_addr += s->tx_ring_index * sizeof(desc); @@ -621,7 +616,7 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s) s->tx_dma_buf_offset += desc.length + 1; /* Update descriptor */ - is_last_packet = desc.flags & NV_TX_LASTPACKET; + bool is_last_packet = desc.flags & NV_TX_LASTPACKET; if (is_last_packet) { NVNET_DPRINTF("Sending packet...\n"); nvnet_send_packet(s, s->tx_dma_buf, s->tx_dma_buf_offset); @@ -741,7 +736,7 @@ static void nvnet_realize(PCIDevice *pci_dev, Error **errp) qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_nvnet_info, &s->conf, - object_get_typename(OBJECT(s)), dev->id, s); + object_get_typename(OBJECT(s)), dev->id, &dev->mem_reentrancy_guard, s); assert(s->nic); } @@ -781,9 +776,9 @@ static void nvnet_reset(void *opaque) memset(&s->rx_dma_buf, 0, sizeof(s->rx_dma_buf)); } -static void qdev_nvnet_reset(DeviceState *dev) +static void nvnet_reset_hold(Object *obj, ResetType type) { - NvNetState *s = NVNET_DEVICE(dev); + NvNetState *s = NVNET_DEVICE(obj); nvnet_reset(s); } @@ -955,6 +950,7 @@ static const VMStateDescription vmstate_nvnet = { static void nvnet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->vendor_id = PCI_VENDOR_ID_NVIDIA; @@ -964,9 +960,10 @@ static void nvnet_class_init(ObjectClass *klass, void *data) k->realize = nvnet_realize; k->exit = nvnet_uninit; + rc->phases.hold = nvnet_reset_hold; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "nForce Ethernet Controller"; - dc->reset = qdev_nvnet_reset; dc->vmsd = &vmstate_nvnet; device_class_set_props(dc, nvnet_properties); } diff --git a/hw/xbox/smbus_xbox_smc.c b/hw/xbox/smbus_xbox_smc.c index c7cf02e3e5..0695589bbd 100644 --- a/hw/xbox/smbus_xbox_smc.c +++ b/hw/xbox/smbus_xbox_smc.c @@ -2,7 +2,7 @@ * QEMU SMBus Xbox System Management Controller * * Copyright (c) 2011 espes - * Copyright (c) 2020-2021 Matt Borgerson + * Copyright (c) 2020-2025 Matt Borgerson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,6 +37,7 @@ #include "smbus.h" #include "sysemu/runstate.h" #include "hw/qdev-properties.h" +#include "block/block_int-io.h" #define TYPE_XBOX_SMC "smbus-xbox-smc" #define XBOX_SMC(obj) OBJECT_CHECK(SMBusSMCDevice, (obj), TYPE_XBOX_SMC) diff --git a/hw/xbox/xbox.c b/hw/xbox/xbox.c index 7a496118ab..72af26cb4f 100644 --- a/hw/xbox/xbox.c +++ b/hw/xbox/xbox.c @@ -2,7 +2,7 @@ * QEMU Xbox System Emulator * * Copyright (c) 2012 espes - * Copyright (c) 2018-2021 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,6 +24,7 @@ #include "hw/hw.h" #include "hw/loader.h" #include "hw/i386/pc.h" +#include "hw/i386/kvm/clock.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" @@ -33,7 +34,6 @@ #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "kvm/kvm_i386.h" -#include "hw/kvm/clock.h" #include "hw/dma/i8257.h" #include "hw/sysbus.h" @@ -217,8 +217,6 @@ void xbox_init_common(MachineState *machine, MemoryRegion *system_memory = get_system_memory(); // MemoryRegion *system_io = get_system_io(); - int i; - PCIBus *pci_bus; ISABus *isa_bus; @@ -229,7 +227,7 @@ void xbox_init_common(MachineState *machine, // DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; // BusState *idebus[MAX_IDE_BUS]; - ISADevice *rtc_state; + MC146818RtcState *rtc_state; ISADevice *pit = NULL; int pit_isa_irq = 0; qemu_irq pit_alt_irq = NULL; @@ -244,7 +242,7 @@ void xbox_init_common(MachineState *machine, x86_cpus_init(x86ms, pcmc->default_cpu_version); - if (kvm_enabled() && pcmc->kvmclock_enabled) { + if (kvm_enabled()) { kvmclock_create(pcmc->kvmclock_create_always); } @@ -267,9 +265,9 @@ void xbox_init_common(MachineState *machine, &smbus, &agp_bus); - pcms->bus = pci_bus; + pcms->pcibus = pci_bus; - isa_bus_irqs(isa_bus, x86ms->gsi); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); pc_i8259_create(isa_bus, gsi_state->i8259_irq); @@ -279,6 +277,7 @@ void xbox_init_common(MachineState *machine, /* init basic PC hardware */ rtc_state = mc146818_rtc_init(isa_bus, 2000, NULL); + x86ms->rtc = ISA_DEVICE(rtc_state); if (kvm_pit_in_kernel()) { pit = kvm_pit_init(isa_bus, 0x40); @@ -286,27 +285,17 @@ void xbox_init_common(MachineState *machine, pit = i8254_pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq); } - i8257_dma_init(isa_bus, 0); + i8257_dma_init(OBJECT(machine), isa_bus, 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); PCIDevice *dev = pci_create_simple(pci_bus, PCI_DEVFN(9, 0), "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"); - // xbox bios wants this bit pattern set to mark the data as valid - uint8_t bits = 0x55; - for (i = 0x10; i < 0x70; i++) { - rtc_set_memory(rtc_state, i, bits); - bits = ~bits; - } - bits = 0x55; - for (i = 0x80; i < 0x100; i++) { - rtc_set_memory(rtc_state, i, bits); - bits = ~bits; - } - /* smbus devices */ smbus_xbox_smc_init(smbus, 0x10); @@ -335,13 +324,8 @@ void xbox_init_common(MachineState *machine, /* Ethernet! */ PCIDevice *nvnet = pci_new(PCI_DEVFN(4, 0), "nvnet"); - - for (i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; - qemu_check_nic_model(nd, "nvnet"); - qdev_set_nic_properties(&nvnet->qdev, nd); - pci_realize_and_unref(nvnet, pci_bus, &error_fatal); - } + qemu_configure_nic_device(DEVICE(nvnet), true, "nvnet"); + pci_realize_and_unref(nvnet, pci_bus, &error_fatal); /* APU! */ mcpx_apu_init(pci_bus, PCI_DEVFN(5, 0), ram_memory); @@ -375,6 +359,7 @@ static void xbox_machine_options(MachineClass *m) m->no_sdcard = 1, m->default_cpu_type = X86_CPU_TYPE_NAME("pentium3"); m->is_default = true; + m->default_nic = "nvnet"; pcmc->pci_enabled = true; pcmc->has_acpi_build = false; @@ -382,7 +367,6 @@ static void xbox_machine_options(MachineClass *m) pcmc->gigabyte_align = false; pcmc->smbios_legacy_mode = true; pcmc->has_reserved_memory = false; - pcmc->default_nic_model = "nvnet"; } static char *machine_get_bootrom(Object *obj, Error **errp) diff --git a/hw/xbox/xbox_pci.c b/hw/xbox/xbox_pci.c index cd9d7d8048..2280f107a0 100644 --- a/hw/xbox/xbox_pci.c +++ b/hw/xbox/xbox_pci.c @@ -2,7 +2,7 @@ * QEMU Xbox PCI buses implementation * * Copyright (c) 2012 espes - * Copyright (c) 2018-2021 Matt Borgerson + * Copyright (c) 2018-2025 Matt Borgerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,7 +30,6 @@ #include "hw/xen/xen.h" #include "hw/pci-host/pam.h" #include "sysemu/sysemu.h" -#include "hw/i386/ioapic.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "hw/loader.h" @@ -184,7 +183,7 @@ void xbox_pci_init(qemu_irq *pic, sysbus_realize_and_unref(SYS_BUS_DEVICE(host), &error_fatal); bridge = pci_create_simple_multifunction(host_bus, PCI_DEVFN(0, 0), - true, "xbox-pci"); + "xbox-pci"); bridge_state = XBOX_PCI_DEVICE(bridge); bridge_state->ram_memory = ram_memory; bridge_state->pci_address_space = pci_memory; @@ -204,13 +203,14 @@ void xbox_pci_init(qemu_irq *pic, /* lpc bridge */ PCIDevice *lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(1, 0), - true, "xbox-lpc"); + "xbox-lpc"); XBOX_LPCState *lpc_state = XBOX_LPC_DEVICE(lpc); lpc_state->pic = pic; lpc_state->rom_memory = rom_memory; - pci_bus_irqs(host_bus, xbox_lpc_set_irq, xbox_lpc_map_irq, lpc_state, + pci_bus_irqs(host_bus, xbox_lpc_set_irq, lpc_state, XBOX_NUM_INT_IRQS + XBOX_NUM_PIRQS); + pci_bus_map_irqs(host_bus, xbox_lpc_map_irq); qemu_irq *acpi_irq = qemu_allocate_irqs(xbox_lpc_set_acpi_irq, lpc_state, 2); @@ -220,7 +220,7 @@ void xbox_pci_init(qemu_irq *pic, /* smbus */ PCIDevice *smbus = pci_create_simple_multifunction(host_bus, PCI_DEVFN(1, 1), - true, "xbox-smbus"); + "xbox-smbus"); XBOX_SMBState *smbus_state = XBOX_SMBUS_DEVICE(smbus); amd756_smbus_init(&smbus->qdev, &smbus_state->smb, acpi_irq[1]); @@ -346,6 +346,12 @@ static void xbox_lpc_reset(DeviceState *dev) xbox_lpc_enable_mcpx_rom(PCI_DEVICE(dev), true); } +static void xbox_lpc_reset_hold(Object *obj, ResetType type) +{ + XBOX_LPCState *s = XBOX_LPC_DEVICE(obj); + xbox_lpc_reset(DEVICE(s)); +} + static void xbox_lpc_config_write(PCIDevice *dev, uint32_t addr, uint32_t val, int len) { @@ -428,6 +434,7 @@ static const VMStateDescription vmstate_xbox_lpc = { static void xbox_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass); @@ -439,9 +446,10 @@ static void xbox_lpc_class_init(ObjectClass *klass, void *data) k->revision = 178; k->class_id = PCI_CLASS_BRIDGE_ISA; + rc->phases.hold = xbox_lpc_reset_hold; + dc->desc = "nForce LPC Bridge"; dc->user_creatable = false; - dc->reset = xbox_lpc_reset; dc->vmsd = &vmstate_xbox_lpc; adevc->send_event = xbox_send_gpe; } @@ -473,14 +481,13 @@ static void xbox_agp_class_init(ObjectClass *klass, void *data) k->realize = xbox_agp_realize; k->exit = pci_bridge_exitfn; k->config_write = pci_bridge_write_config; - k->is_bridge = 1; k->vendor_id = PCI_VENDOR_ID_NVIDIA; k->device_id = PCI_DEVICE_ID_NVIDIA_NFORCE_AGP; k->revision = 161; dc->desc = "nForce AGP to PCI Bridge"; dc->vmsd = &vmstate_pci_device; - dc->reset = pci_bridge_reset; + device_class_set_legacy_reset(dc, pci_bridge_reset); } static const TypeInfo xbox_agp_info = { @@ -542,18 +549,17 @@ static const TypeInfo xbox_pci_info = { static void xbox_pcihost_realize(DeviceState *dev, Error **errp) { PCIHostState *s = PCI_HOST_BRIDGE(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&s->conf_mem, OBJECT(dev), &pci_host_conf_le_ops, s, "pci-conf-idx", 4); - sysbus_add_io(sbd, CONFIG_ADDR, &s->conf_mem); + memory_region_add_subregion(get_system_io(), CONFIG_ADDR, &s->conf_mem); sysbus_init_ioports(&s->busdev, CONFIG_ADDR, 4); memory_region_init_io(&s->data_mem, OBJECT(dev), &pci_host_data_le_ops, s, "pci-conf-data", 4); - sysbus_add_io(sbd, CONFIG_DATA, &s->data_mem); + memory_region_add_subregion(get_system_io(), CONFIG_DATA, &s->data_mem); sysbus_init_ioports(&s->busdev, CONFIG_DATA, 4); } 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 ae0ace3046..4a486e3673 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,16 @@ 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', + 'xen-pvh-common.c', +)) if have_xen_pci_passthrough xen_specific_ss.add(files( 'xen-host-pci-device.c', diff --git a/hw/xen/trace-events b/hw/xen/trace-events index 3da3fd8348..a07fe41c6d 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,45 @@ 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-pvh-common.c +xen_create_virtio_mmio_devices(int i, int irq, uint64_t base) "Created virtio-mmio device %d: irq %d base 0x%"PRIx64 +xen_enable_tpm(uint64_t addr) "Connected tpmdev at address 0x%"PRIx64 + +# 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..95b207ac8b 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -13,12 +13,14 @@ #include "hw/sysbus.h" #include "hw/xen/xen.h" #include "hw/xen/xen-backend.h" +#include "hw/xen/xen-legacy-backend.h" /* xen_be_init() */ #include "hw/xen/xen-bus.h" #include "hw/xen/xen-bus-helper.h" #include "monitor/monitor.h" #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 +64,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 +82,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 +97,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 +113,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 +126,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 +165,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 +192,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 +211,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 +238,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 +281,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 +300,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 +308,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,12 +324,15 @@ 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; } + /* Initialize legacy backend core & drivers */ + xen_be_init(); + if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ "domid", NULL, "%u", &domid) == 1) { xenbus->backend_id = domid; @@ -476,19 +340,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 +424,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 +494,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 +548,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 +589,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 +669,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 +717,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 +746,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 +759,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 +782,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 +791,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 +802,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 +837,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 +857,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 +876,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 +905,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 +922,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 +943,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 +987,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 +1037,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 +1059,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 +1077,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 +1145,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-host-pci-device.c b/hw/xen/xen-host-pci-device.c index 8c6e9a1716..eaf32f2710 100644 --- a/hw/xen/xen-host-pci-device.c +++ b/hw/xen/xen-host-pci-device.c @@ -9,6 +9,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/cutils.h" +#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus-helper.h" #include "xen-host-pci-device.h" #define XEN_HOST_PCI_MAX_EXT_CAP \ @@ -33,13 +35,73 @@ #define IORESOURCE_PREFETCH 0x00001000 /* No side effects */ #define IORESOURCE_MEM_64 0x00100000 +/* + * Non-passthrough (dom0) accesses are local PCI devices and use the given BDF + * Passthough (stubdom) accesses are through PV frontend PCI device. Those + * either have a BDF identical to the backend's BDF (xen-backend.passthrough=1) + * or a local virtual BDF (xen-backend.passthrough=0) + * + * We are always given the backend's BDF and need to lookup the appropriate + * local BDF for sysfs access. + */ +static void xen_host_pci_fill_local_addr(XenHostPCIDevice *d, Error **errp) +{ + unsigned int num_devs, len, i; + unsigned int domain, bus, dev, func; + char *be_path = NULL; + char path[16]; + + be_path = qemu_xen_xs_read(xenstore, 0, "device/pci/0/backend", &len); + if (!be_path) { + error_setg(errp, "Failed to read device/pci/0/backend"); + goto out; + } + + if (xs_node_scanf(xenstore, 0, be_path, "num_devs", NULL, + "%d", &num_devs) != 1) { + error_setg(errp, "Failed to read or parse %s/num_devs", be_path); + goto out; + } + + for (i = 0; i < num_devs; i++) { + snprintf(path, sizeof(path), "dev-%d", i); + if (xs_node_scanf(xenstore, 0, be_path, path, NULL, + "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) { + error_setg(errp, "Failed to read or parse %s/%s", be_path, path); + goto out; + } + if (domain != d->domain || + bus != d->bus || + dev != d->dev || + func != d->func) + continue; + snprintf(path, sizeof(path), "vdev-%d", i); + if (xs_node_scanf(xenstore, 0, be_path, path, NULL, + "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) { + error_setg(errp, "Failed to read or parse %s/%s", be_path, path); + goto out; + } + d->local_domain = domain; + d->local_bus = bus; + d->local_dev = dev; + d->local_func = func; + goto out; + } + error_setg(errp, "Failed to find PCI device %x:%x:%x.%x in xenstore", + d->domain, d->bus, d->dev, d->func); + +out: + free(be_path); +} + static void xen_host_pci_sysfs_path(const XenHostPCIDevice *d, const char *name, char *buf, ssize_t size) { int rc; rc = snprintf(buf, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", - d->domain, d->bus, d->dev, d->func, name); + d->local_domain, d->local_bus, d->local_dev, d->local_func, + name); assert(rc >= 0 && rc < size); } @@ -342,6 +404,18 @@ void xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, d->dev = dev; d->func = func; + if (xen_is_stubdomain) { + xen_host_pci_fill_local_addr(d, errp); + if (*errp) { + goto error; + } + } else { + d->local_domain = d->domain; + d->local_bus = d->bus; + d->local_dev = d->dev; + d->local_func = d->func; + } + xen_host_pci_config_open(d, errp); if (*errp) { goto error; diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h index 4d8d34ecb0..270dcb27f7 100644 --- a/hw/xen/xen-host-pci-device.h +++ b/hw/xen/xen-host-pci-device.h @@ -23,6 +23,12 @@ typedef struct XenHostPCIDevice { uint8_t dev; uint8_t func; + /* different from the above in case of stubdomain */ + uint16_t local_domain; + uint8_t local_bus; + uint8_t local_dev; + uint8_t local_func; + uint16_t vendor_id; uint16_t device_id; uint32_t class_code; diff --git a/hw/xen/xen-hvm-common.c b/hw/xen/xen-hvm-common.c new file mode 100644 index 0000000000..7ffbbfea23 --- /dev/null +++ b/hw/xen/xen-hvm-common.c @@ -0,0 +1,925 @@ +#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, xen_grants; + +/* Check for any kind of xen memory, foreign mappings or grants. */ +bool xen_mr_is_memory(MemoryRegion *mr) +{ + return mr == &xen_memory || mr == &xen_grants; +} + +/* Check specifically for grants. */ +bool xen_mr_is_grants(MemoryRegion *mr) +{ + return mr == &xen_grants; +} + +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 (xen_mr_is_memory(mr)) { + 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 (xen_mr_is_memory(section->mr)) { + 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 unsigned int handle_buffered_iopage(XenIOState *state) +{ + buffered_iopage_t *buf_page = state->buffered_io_page; + buf_ioreq_t *buf_req = NULL; + unsigned int handled = 0; + 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; + + do { + 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 += qw + 1; + } while (handled < IOREQ_BUFFER_SLOT_NUM); + + return handled; +} + +static void handle_buffered_io(void *opaque) +{ + unsigned int handled; + XenIOState *state = opaque; + + handled = handle_buffered_iopage(state); + if (handled >= IOREQ_BUFFER_SLOT_NUM) { + /* We handled a full page of ioreqs. Schedule a timer to continue + * processing while giving other stuff a chance to run. + */ + timer_mod(state->buffered_io_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } else if (handled == 0) { + timer_del(state->buffered_io_timer); + qemu_xen_evtchn_unmask(state->xce_handle, state->bufioreq_local_port); + } else { + timer_mod(state->buffered_io_timer, + BUFFER_IO_MAX_DELAY + qemu_clock_get_ms(QEMU_CLOCK_REALTIME)); + } +} + +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; + unsigned long num_frames = 1; + unsigned long frame = 1; + 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); + + if (state->has_bufioreq) { + frame = 0; + num_frames = 2; + } + state->fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, + XENMEM_resource_ioreq_server, + state->ioservid, + frame, num_frames, + &addr, + PROT_READ | PROT_WRITE, 0); + if (state->fres != NULL) { + trace_xen_map_resource_ioreq(state->ioservid, addr); + state->shared_page = addr; + if (state->has_bufioreq) { + 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; + } + + /* + * If we fail to map the shared page with xenforeignmemory_map_resource() + * or if we're using buffered ioreqs, we need xen_get_ioreq_server_info() + * to provide the the addresses to map the shared page and/or to get the + * event-channel port for buffered ioreqs. + */ + if (state->shared_page == NULL || state->has_bufioreq) { + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + (state->shared_page == NULL) ? + &ioreq_pfn : NULL, + (state->has_bufioreq && + 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->has_bufioreq && 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->has_bufioreq && state->buffered_io_page == NULL)) { + return -1; + } + + if (state->has_bufioreq) { + 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; + } + + if (state->has_bufioreq) { + 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, + uint8_t handle_bufioreq, + 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; + } + + state->has_bufioreq = handle_bufioreq != HVM_IOREQSRV_BUFIOREQ_OFF; + rc = xen_create_ioreq_server(xen_domid, handle_bufioreq, &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(); + + 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..e8e1ee4f7d 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,132 +137,22 @@ 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); - - if (rc) { - xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", - strerror(errno)); - } - - 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; -} - /* * get xen backend device, allocate a new one if it doesn't exist. */ static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, int dev, - struct XenDevOps *ops) + const struct XenDevOps *ops) { struct XenLegacyDevice *xendev; @@ -294,13 +183,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 +256,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 +288,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 +308,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 +374,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 +432,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 +499,67 @@ void xen_be_check_state(struct XenLegacyDevice *xendev) /* ------------------------------------------------------------- */ -static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) +struct xenstore_be { + const char *type; + int dom; + const 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, const 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 +574,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,17 +585,30 @@ static void xen_set_dynamic_sysbus(void) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_XENSYSDEV); } -int xen_be_register(const char *type, struct XenDevOps *ops) +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(); +} + +int xen_be_register(const char *type, const struct XenDevOps *ops) { char path[50]; - int rc; - - if (ops->backend_register) { - rc = ops->backend_register(); - if (rc) { - return rc; - } - } snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid, type); @@ -744,33 +617,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/xen/xen-mapcache.c b/hw/xen/xen-mapcache.c new file mode 100644 index 0000000000..18ba7b1d8f --- /dev/null +++ b/hw/xen/xen-mapcache.c @@ -0,0 +1,772 @@ +/* + * Copyright (C) 2011 Citrix Ltd. + * + * 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/units.h" +#include "qemu/error-report.h" + +#include + +#include "hw/xen/xen-hvm-common.h" +#include "hw/xen/xen_native.h" +#include "qemu/bitmap.h" + +#include "sysemu/runstate.h" +#include "sysemu/xen-mapcache.h" +#include "trace.h" + +#include +#include + +#if HOST_LONG_BITS == 32 +# define MCACHE_MAX_SIZE (1UL<<31) /* 2GB Cap */ +#else +# define MCACHE_MAX_SIZE (1UL<<35) /* 32GB Cap */ +#endif + +/* This is the size of the virtual address space reserve to QEMU that will not + * be use by MapCache. + * From empirical tests I observed that qemu use 75MB more than the + * max_mcache_size. + */ +#define NON_MCACHE_MEMORY_SIZE (80 * MiB) + +typedef struct MapCacheEntry { + hwaddr paddr_index; + uint8_t *vaddr_base; + unsigned long *valid_mapping; + uint32_t lock; +#define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0) +#define XEN_MAPCACHE_ENTRY_GRANT (1 << 1) + uint8_t flags; + hwaddr size; + struct MapCacheEntry *next; +} MapCacheEntry; + +typedef struct MapCacheRev { + uint8_t *vaddr_req; + hwaddr paddr_index; + hwaddr size; + QTAILQ_ENTRY(MapCacheRev) next; + bool dma; +} MapCacheRev; + +typedef struct MapCache { + MapCacheEntry *entry; + unsigned long nr_buckets; + QTAILQ_HEAD(, MapCacheRev) locked_entries; + + /* For most cases (>99.9%), the page address is the same. */ + MapCacheEntry *last_entry; + unsigned long max_mcache_size; + unsigned int bucket_shift; + unsigned long bucket_size; + + phys_offset_to_gaddr_t phys_offset_to_gaddr; + QemuMutex lock; + void *opaque; +} MapCache; + +static MapCache *mapcache; +static MapCache *mapcache_grants; +static xengnttab_handle *xen_region_gnttabdev; + +static inline void mapcache_lock(MapCache *mc) +{ + qemu_mutex_lock(&mc->lock); +} + +static inline void mapcache_unlock(MapCache *mc) +{ + qemu_mutex_unlock(&mc->lock); +} + +static inline int test_bits(int nr, int size, const unsigned long *addr) +{ + unsigned long res = find_next_zero_bit(addr, size + nr, nr); + if (res >= nr + size) + return 1; + else + return 0; +} + +static MapCache *xen_map_cache_init_single(phys_offset_to_gaddr_t f, + void *opaque, + unsigned int bucket_shift, + unsigned long max_size) +{ + unsigned long size; + MapCache *mc; + + assert(bucket_shift >= XC_PAGE_SHIFT); + + mc = g_new0(MapCache, 1); + + mc->phys_offset_to_gaddr = f; + mc->opaque = opaque; + qemu_mutex_init(&mc->lock); + + QTAILQ_INIT(&mc->locked_entries); + + mc->bucket_shift = bucket_shift; + mc->bucket_size = 1UL << bucket_shift; + mc->max_mcache_size = max_size; + + mc->nr_buckets = + (((mc->max_mcache_size >> XC_PAGE_SHIFT) + + (1UL << (bucket_shift - XC_PAGE_SHIFT)) - 1) >> + (bucket_shift - XC_PAGE_SHIFT)); + + size = mc->nr_buckets * sizeof(MapCacheEntry); + size = (size + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1); + trace_xen_map_cache_init(mc->nr_buckets, size); + mc->entry = g_malloc0(size); + return mc; +} + +void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque) +{ + struct rlimit rlimit_as; + unsigned long max_mcache_size; + unsigned int bucket_shift; + + xen_region_gnttabdev = xengnttab_open(NULL, 0); + if (xen_region_gnttabdev == NULL) { + error_report("mapcache: Failed to open gnttab device"); + exit(EXIT_FAILURE); + } + + if (HOST_LONG_BITS == 32) { + bucket_shift = 16; + } else { + bucket_shift = 20; + } + + if (geteuid() == 0) { + rlimit_as.rlim_cur = RLIM_INFINITY; + rlimit_as.rlim_max = RLIM_INFINITY; + max_mcache_size = MCACHE_MAX_SIZE; + } else { + getrlimit(RLIMIT_AS, &rlimit_as); + rlimit_as.rlim_cur = rlimit_as.rlim_max; + + if (rlimit_as.rlim_max != RLIM_INFINITY) { + warn_report("QEMU's maximum size of virtual" + " memory is not infinity"); + } + if (rlimit_as.rlim_max < MCACHE_MAX_SIZE + NON_MCACHE_MEMORY_SIZE) { + max_mcache_size = rlimit_as.rlim_max - NON_MCACHE_MEMORY_SIZE; + } else { + max_mcache_size = MCACHE_MAX_SIZE; + } + } + + mapcache = xen_map_cache_init_single(f, opaque, + bucket_shift, + max_mcache_size); + + /* + * Grant mappings must use XC_PAGE_SIZE granularity since we can't + * map anything beyond the number of pages granted to us. + */ + mapcache_grants = xen_map_cache_init_single(f, opaque, + XC_PAGE_SHIFT, + max_mcache_size); + + setrlimit(RLIMIT_AS, &rlimit_as); +} + +static void xen_remap_bucket(MapCache *mc, + MapCacheEntry *entry, + void *vaddr, + hwaddr size, + hwaddr address_index, + bool dummy, + bool grant, + bool is_write, + ram_addr_t ram_offset) +{ + uint8_t *vaddr_base; + g_autofree uint32_t *refs = NULL; + g_autofree xen_pfn_t *pfns = NULL; + g_autofree int *err; + unsigned int i; + hwaddr nb_pfn = size >> XC_PAGE_SHIFT; + + trace_xen_remap_bucket(address_index); + + if (grant) { + refs = g_new0(uint32_t, nb_pfn); + } else { + pfns = g_new0(xen_pfn_t, nb_pfn); + } + err = g_new0(int, nb_pfn); + + if (entry->vaddr_base != NULL) { + if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { + ram_block_notify_remove(entry->vaddr_base, entry->size, + entry->size); + } + + /* + * If an entry is being replaced by another mapping and we're using + * MAP_FIXED flag for it - there is possibility of a race for vaddr + * address with another thread doing an mmap call itself + * (see man 2 mmap). To avoid that we skip explicit unmapping here + * and allow the kernel to destroy the previous mappings by replacing + * them in mmap call later. + * + * Non-identical replacements are not allowed therefore. + */ + assert(!vaddr || (entry->vaddr_base == vaddr && entry->size == size)); + + if (!vaddr && munmap(entry->vaddr_base, entry->size) != 0) { + perror("unmap fails"); + exit(-1); + } + } + g_free(entry->valid_mapping); + entry->valid_mapping = NULL; + + if (grant) { + hwaddr grant_base = address_index - (ram_offset >> XC_PAGE_SHIFT); + + for (i = 0; i < nb_pfn; i++) { + refs[i] = grant_base + i; + } + } else { + for (i = 0; i < nb_pfn; i++) { + pfns[i] = (address_index << (mc->bucket_shift - XC_PAGE_SHIFT)) + i; + } + } + + entry->flags &= ~XEN_MAPCACHE_ENTRY_GRANT; + + if (!dummy) { + if (grant) { + int prot = PROT_READ; + + if (is_write) { + prot |= PROT_WRITE; + } + + entry->flags |= XEN_MAPCACHE_ENTRY_GRANT; + assert(vaddr == NULL); + vaddr_base = xengnttab_map_domain_grant_refs(xen_region_gnttabdev, + nb_pfn, + xen_domid, refs, + prot); + } else { + /* + * If the caller has requested the mapping at a specific address use + * MAP_FIXED to make sure it's honored. + * + * We don't yet support upgrading mappings from RO to RW, to handle + * models using ordinary address_space_rw(), foreign mappings ignore + * is_write and are always mapped RW. + */ + vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, + PROT_READ | PROT_WRITE, + vaddr ? MAP_FIXED : 0, + nb_pfn, pfns, err); + } + if (vaddr_base == NULL) { + perror(grant ? "xengnttab_map_domain_grant_refs" + : "xenforeignmemory_map2"); + exit(-1); + } + } else { + /* + * We create dummy mappings where we are unable to create a foreign + * mapping immediately due to certain circumstances (i.e. on resume now) + */ + vaddr_base = mmap(vaddr, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED | (vaddr ? MAP_FIXED : 0), + -1, 0); + if (vaddr_base == MAP_FAILED) { + perror("mmap"); + exit(-1); + } + } + + if (!(entry->flags & XEN_MAPCACHE_ENTRY_DUMMY)) { + ram_block_notify_add(vaddr_base, size, size); + } + + entry->vaddr_base = vaddr_base; + entry->paddr_index = address_index; + entry->size = size; + entry->valid_mapping = g_new0(unsigned long, + BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); + + if (dummy) { + entry->flags |= XEN_MAPCACHE_ENTRY_DUMMY; + } else { + entry->flags &= ~(XEN_MAPCACHE_ENTRY_DUMMY); + } + + bitmap_zero(entry->valid_mapping, nb_pfn); + for (i = 0; i < nb_pfn; i++) { + if (!err[i]) { + bitmap_set(entry->valid_mapping, i, 1); + } + } +} + +static uint8_t *xen_map_cache_unlocked(MapCache *mc, + hwaddr phys_addr, hwaddr size, + ram_addr_t ram_offset, + uint8_t lock, bool dma, + bool grant, bool is_write) +{ + MapCacheEntry *entry, *pentry = NULL, + *free_entry = NULL, *free_pentry = NULL; + hwaddr address_index; + hwaddr address_offset; + hwaddr cache_size = size; + hwaddr test_bit_size; + bool translated G_GNUC_UNUSED = false; + bool dummy = false; + +tryagain: + address_index = phys_addr >> mc->bucket_shift; + address_offset = phys_addr & (mc->bucket_size - 1); + + trace_xen_map_cache(phys_addr); + + /* test_bit_size is always a multiple of XC_PAGE_SIZE */ + if (size) { + test_bit_size = size + (phys_addr & (XC_PAGE_SIZE - 1)); + + if (test_bit_size % XC_PAGE_SIZE) { + test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); + } + } else { + test_bit_size = XC_PAGE_SIZE; + } + + if (mc->last_entry != NULL && + mc->last_entry->paddr_index == address_index && + !lock && !size && + test_bits(address_offset >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, + mc->last_entry->valid_mapping)) { + trace_xen_map_cache_return( + mc->last_entry->vaddr_base + address_offset + ); + return mc->last_entry->vaddr_base + address_offset; + } + + /* size is always a multiple of mc->bucket_size */ + if (size) { + cache_size = size + address_offset; + if (cache_size % mc->bucket_size) { + cache_size += mc->bucket_size - (cache_size % mc->bucket_size); + } + } else { + cache_size = mc->bucket_size; + } + + entry = &mc->entry[address_index % mc->nr_buckets]; + + while (entry && (lock || entry->lock) && entry->vaddr_base && + (entry->paddr_index != address_index || entry->size != cache_size || + !test_bits(address_offset >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, + entry->valid_mapping))) { + if (!free_entry && !entry->lock) { + free_entry = entry; + free_pentry = pentry; + } + pentry = entry; + entry = entry->next; + } + if (!entry && free_entry) { + entry = free_entry; + pentry = free_pentry; + } + if (!entry) { + entry = g_new0(MapCacheEntry, 1); + pentry->next = entry; + xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy, + grant, is_write, ram_offset); + } else if (!entry->lock) { + if (!entry->vaddr_base || entry->paddr_index != address_index || + entry->size != cache_size || + !test_bits(address_offset >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, + entry->valid_mapping)) { + xen_remap_bucket(mc, entry, NULL, cache_size, address_index, dummy, + grant, is_write, ram_offset); + } + } + + if(!test_bits(address_offset >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, + entry->valid_mapping)) { + mc->last_entry = NULL; +#ifdef XEN_COMPAT_PHYSMAP + if (!translated && mc->phys_offset_to_gaddr) { + phys_addr = mc->phys_offset_to_gaddr(phys_addr, size); + translated = true; + goto tryagain; + } +#endif + if (!dummy && runstate_check(RUN_STATE_INMIGRATE)) { + dummy = true; + goto tryagain; + } + trace_xen_map_cache_return(NULL); + return NULL; + } + + mc->last_entry = entry; + if (lock) { + MapCacheRev *reventry = g_new0(MapCacheRev, 1); + entry->lock++; + if (entry->lock == 0) { + error_report("mapcache entry lock overflow: "HWADDR_FMT_plx" -> %p", + entry->paddr_index, entry->vaddr_base); + abort(); + } + reventry->dma = dma; + reventry->vaddr_req = mc->last_entry->vaddr_base + address_offset; + reventry->paddr_index = mc->last_entry->paddr_index; + reventry->size = entry->size; + QTAILQ_INSERT_HEAD(&mc->locked_entries, reventry, next); + } + + trace_xen_map_cache_return( + mc->last_entry->vaddr_base + address_offset + ); + return mc->last_entry->vaddr_base + address_offset; +} + +uint8_t *xen_map_cache(MemoryRegion *mr, + hwaddr phys_addr, hwaddr size, + ram_addr_t ram_addr_offset, + uint8_t lock, bool dma, + bool is_write) +{ + bool grant = xen_mr_is_grants(mr); + MapCache *mc = grant ? mapcache_grants : mapcache; + uint8_t *p; + + if (grant && !lock) { + /* + * Grants are only supported via address_space_map(). Anything + * else is considered a user/guest error. + * + * QEMU generally doesn't expect these mappings to ever fail, so + * if this happens we report an error message and abort(). + */ + error_report("Tried to access a grant reference without mapping it."); + abort(); + } + + mapcache_lock(mc); + p = xen_map_cache_unlocked(mc, phys_addr, size, ram_addr_offset, + lock, dma, grant, is_write); + mapcache_unlock(mc); + return p; +} + +static ram_addr_t xen_ram_addr_from_mapcache_single(MapCache *mc, void *ptr) +{ + MapCacheEntry *entry = NULL; + MapCacheRev *reventry; + hwaddr paddr_index; + hwaddr size; + ram_addr_t raddr; + int found = 0; + + mapcache_lock(mc); + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { + if (reventry->vaddr_req == ptr) { + paddr_index = reventry->paddr_index; + size = reventry->size; + found = 1; + break; + } + } + if (!found) { + trace_xen_ram_addr_from_mapcache_not_found(ptr); + mapcache_unlock(mc); + return RAM_ADDR_INVALID; + } + + entry = &mc->entry[paddr_index % mc->nr_buckets]; + while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { + entry = entry->next; + } + if (!entry) { + trace_xen_ram_addr_from_mapcache_not_in_cache(ptr); + raddr = RAM_ADDR_INVALID; + } else { + raddr = (reventry->paddr_index << mc->bucket_shift) + + ((unsigned long) ptr - (unsigned long) entry->vaddr_base); + } + mapcache_unlock(mc); + return raddr; +} + +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + ram_addr_t addr; + + addr = xen_ram_addr_from_mapcache_single(mapcache, ptr); + if (addr == RAM_ADDR_INVALID) { + addr = xen_ram_addr_from_mapcache_single(mapcache_grants, ptr); + } + + return addr; +} + +static void xen_invalidate_map_cache_entry_unlocked(MapCache *mc, + uint8_t *buffer) +{ + MapCacheEntry *entry = NULL, *pentry = NULL; + MapCacheRev *reventry; + hwaddr paddr_index; + hwaddr size; + int found = 0; + int rc; + + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { + if (reventry->vaddr_req == buffer) { + paddr_index = reventry->paddr_index; + size = reventry->size; + found = 1; + break; + } + } + if (!found) { + trace_xen_invalidate_map_cache_entry_unlocked_not_found(buffer); + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { + trace_xen_invalidate_map_cache_entry_unlocked_found( + reventry->paddr_index, + reventry->vaddr_req + ); + } + return; + } + QTAILQ_REMOVE(&mc->locked_entries, reventry, next); + g_free(reventry); + + if (mc->last_entry != NULL && + mc->last_entry->paddr_index == paddr_index) { + mc->last_entry = NULL; + } + + entry = &mc->entry[paddr_index % mc->nr_buckets]; + while (entry && (entry->paddr_index != paddr_index || entry->size != size)) { + pentry = entry; + entry = entry->next; + } + if (!entry) { + trace_xen_invalidate_map_cache_entry_unlocked_miss(buffer); + return; + } + entry->lock--; + if (entry->lock > 0) { + return; + } + + ram_block_notify_remove(entry->vaddr_base, entry->size, entry->size); + if (entry->flags & XEN_MAPCACHE_ENTRY_GRANT) { + rc = xengnttab_unmap(xen_region_gnttabdev, entry->vaddr_base, + entry->size >> mc->bucket_shift); + } else { + rc = munmap(entry->vaddr_base, entry->size); + } + + if (rc) { + perror("unmap fails"); + exit(-1); + } + + g_free(entry->valid_mapping); + if (pentry) { + pentry->next = entry->next; + g_free(entry); + } else { + /* + * Invalidate mapping but keep entry->next pointing to the rest + * of the list. + * + * Note that lock is already zero here, otherwise we don't unmap. + */ + entry->paddr_index = 0; + entry->vaddr_base = NULL; + entry->valid_mapping = NULL; + entry->flags = 0; + entry->size = 0; + } +} + +typedef struct XenMapCacheData { + Coroutine *co; + uint8_t *buffer; +} XenMapCacheData; + +static void xen_invalidate_map_cache_entry_single(MapCache *mc, uint8_t *buffer) +{ + mapcache_lock(mc); + xen_invalidate_map_cache_entry_unlocked(mc, buffer); + mapcache_unlock(mc); +} + +static void xen_invalidate_map_cache_entry_all(uint8_t *buffer) +{ + xen_invalidate_map_cache_entry_single(mapcache, buffer); + xen_invalidate_map_cache_entry_single(mapcache_grants, buffer); +} + +static void xen_invalidate_map_cache_entry_bh(void *opaque) +{ + XenMapCacheData *data = opaque; + + xen_invalidate_map_cache_entry_all(data->buffer); + 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 { + xen_invalidate_map_cache_entry_all(buffer); + } +} + +static void xen_invalidate_map_cache_single(MapCache *mc) +{ + unsigned long i; + MapCacheRev *reventry; + + mapcache_lock(mc); + + QTAILQ_FOREACH(reventry, &mc->locked_entries, next) { + if (!reventry->dma) { + continue; + } + trace_xen_invalidate_map_cache(reventry->paddr_index, + reventry->vaddr_req); + } + + for (i = 0; i < mc->nr_buckets; i++) { + MapCacheEntry *entry = &mc->entry[i]; + + if (entry->vaddr_base == NULL) { + continue; + } + if (entry->lock > 0) { + continue; + } + + if (munmap(entry->vaddr_base, entry->size) != 0) { + perror("unmap fails"); + exit(-1); + } + + entry->paddr_index = 0; + entry->vaddr_base = NULL; + entry->size = 0; + g_free(entry->valid_mapping); + entry->valid_mapping = NULL; + } + + mc->last_entry = NULL; + + mapcache_unlock(mc); +} + +void xen_invalidate_map_cache(void) +{ + /* Flush pending AIO before destroying the mapcache */ + bdrv_drain_all(); + + xen_invalidate_map_cache_single(mapcache); + xen_invalidate_map_cache_single(mapcache_grants); +} + +static uint8_t *xen_replace_cache_entry_unlocked(MapCache *mc, + hwaddr old_phys_addr, + hwaddr new_phys_addr, + hwaddr size) +{ + MapCacheEntry *entry; + hwaddr address_index, address_offset; + hwaddr test_bit_size, cache_size = size; + + address_index = old_phys_addr >> mc->bucket_shift; + address_offset = old_phys_addr & (mc->bucket_size - 1); + + assert(size); + /* test_bit_size is always a multiple of XC_PAGE_SIZE */ + test_bit_size = size + (old_phys_addr & (XC_PAGE_SIZE - 1)); + if (test_bit_size % XC_PAGE_SIZE) { + test_bit_size += XC_PAGE_SIZE - (test_bit_size % XC_PAGE_SIZE); + } + cache_size = size + address_offset; + if (cache_size % mc->bucket_size) { + cache_size += mc->bucket_size - (cache_size % mc->bucket_size); + } + + entry = &mc->entry[address_index % mc->nr_buckets]; + while (entry && !(entry->paddr_index == address_index && + entry->size == cache_size)) { + entry = entry->next; + } + if (!entry) { + trace_xen_replace_cache_entry_unlocked(old_phys_addr); + return NULL; + } + + assert((entry->flags & XEN_MAPCACHE_ENTRY_GRANT) == 0); + + address_index = new_phys_addr >> mc->bucket_shift; + address_offset = new_phys_addr & (mc->bucket_size - 1); + + trace_xen_replace_cache_entry_dummy(old_phys_addr, new_phys_addr); + + xen_remap_bucket(mc, entry, entry->vaddr_base, + cache_size, address_index, false, + false, false, old_phys_addr); + if (!test_bits(address_offset >> XC_PAGE_SHIFT, + test_bit_size >> XC_PAGE_SHIFT, + entry->valid_mapping)) { + trace_xen_replace_cache_entry_unlocked_could_not_update_entry( + old_phys_addr + ); + return NULL; + } + + return entry->vaddr_base + address_offset; +} + +uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, + hwaddr new_phys_addr, + hwaddr size) +{ + uint8_t *p; + + mapcache_lock(mapcache); + p = xen_replace_cache_entry_unlocked(mapcache, old_phys_addr, + new_phys_addr, size); + mapcache_unlock(mapcache); + return p; +} 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-pvh-common.c b/hw/xen/xen-pvh-common.c new file mode 100644 index 0000000000..218ac851cf --- /dev/null +++ b/hw/xen/xen-pvh-common.c @@ -0,0 +1,400 @@ +/* + * QEMU Xen PVH machine - common code. + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "sysemu/tpm.h" +#include "sysemu/tpm_backend.h" +#include "hw/xen/xen-pvh-common.h" +#include "trace.h" + +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, +}; + +static void xen_pvh_init_ram(XenPVHMachineState *s, + MemoryRegion *sysmem) +{ + MachineState *ms = MACHINE(s); + ram_addr_t block_len, ram_size[2]; + + if (ms->ram_size <= s->cfg.ram_low.size) { + ram_size[0] = ms->ram_size; + ram_size[1] = 0; + block_len = s->cfg.ram_low.base + ram_size[0]; + } else { + ram_size[0] = s->cfg.ram_low.size; + ram_size[1] = ms->ram_size - s->cfg.ram_low.size; + block_len = s->cfg.ram_high.base + ram_size[1]; + } + + memory_region_init_ram(&xen_memory, NULL, "xen.ram", block_len, + &error_fatal); + + memory_region_init_alias(&s->ram.low, NULL, "xen.ram.lo", &xen_memory, + s->cfg.ram_low.base, ram_size[0]); + memory_region_add_subregion(sysmem, s->cfg.ram_low.base, &s->ram.low); + if (ram_size[1] > 0) { + memory_region_init_alias(&s->ram.high, NULL, "xen.ram.hi", &xen_memory, + s->cfg.ram_high.base, ram_size[1]); + memory_region_add_subregion(sysmem, s->cfg.ram_high.base, &s->ram.high); + } + + /* Setup support for grants. */ + memory_region_init_ram(&xen_grants, NULL, "xen.grants", block_len, + &error_fatal); + memory_region_add_subregion(sysmem, XEN_GRANT_ADDR_OFF, &xen_grants); +} + +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(XenPVHMachineState *s) +{ + int i; + + /* + * We create the transports in reverse order. Since qbus_realize() + * prepends (not appends) new child buses, the decrementing loop below will + * create a list of virtio-mmio buses with increasing base addresses. + * + * When a -device option is processed from the command line, + * qbus_find_recursive() picks the next free virtio-mmio bus in forwards + * order. + * + * This is what the Xen tools expect. + */ + for (i = s->cfg.virtio_mmio_num - 1; i >= 0; i--) { + hwaddr base = s->cfg.virtio_mmio.base + i * s->cfg.virtio_mmio.size; + qemu_irq irq = qemu_allocate_irq(xen_set_irq, NULL, + s->cfg.virtio_mmio_irq_base + i); + + sysbus_create_simple("virtio-mmio", base, irq); + + trace_xen_create_virtio_mmio_devices(i, + s->cfg.virtio_mmio_irq_base + i, + base); + } +} + +#ifdef CONFIG_TPM +static void xen_enable_tpm(XenPVHMachineState *s) +{ + 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, s->cfg.tpm.base); + + trace_xen_enable_tpm(s->cfg.tpm.base); +} +#endif + +/* + * We use the GPEX PCIe controller with its internal INTX PCI interrupt + * swizzling. This swizzling is emulated in QEMU and routes all INTX + * interrupts from endpoints down to only 4 INTX interrupts. + * See include/hw/pci/pci.h : pci_swizzle() + */ +static inline void xenpvh_gpex_init(XenPVHMachineState *s, + XenPVHMachineClass *xpc, + MemoryRegion *sysmem) +{ + MemoryRegion *ecam_reg; + MemoryRegion *mmio_reg; + DeviceState *dev; + int i; + + object_initialize_child(OBJECT(s), "gpex", &s->pci.gpex, + TYPE_GPEX_HOST); + dev = DEVICE(&s->pci.gpex); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(sysmem, s->cfg.pci_ecam.base, ecam_reg); + + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + + if (s->cfg.pci_mmio.size) { + memory_region_init_alias(&s->pci.mmio_alias, OBJECT(dev), "pcie-mmio", + mmio_reg, + s->cfg.pci_mmio.base, s->cfg.pci_mmio.size); + memory_region_add_subregion(sysmem, s->cfg.pci_mmio.base, + &s->pci.mmio_alias); + } + + if (s->cfg.pci_mmio_high.size) { + memory_region_init_alias(&s->pci.mmio_high_alias, OBJECT(dev), + "pcie-mmio-high", + mmio_reg, s->cfg.pci_mmio_high.base, s->cfg.pci_mmio_high.size); + memory_region_add_subregion(sysmem, s->cfg.pci_mmio_high.base, + &s->pci.mmio_high_alias); + } + + /* + * PVH implementations with PCI enabled must provide set_pci_intx_irq() + * and optionally an implementation of set_pci_link_route(). + */ + assert(xpc->set_pci_intx_irq); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + qemu_irq irq = qemu_allocate_irq(xpc->set_pci_intx_irq, s, i); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); + gpex_set_irq_num(GPEX_HOST(dev), i, s->cfg.pci_intx_irq_base + i); + if (xpc->set_pci_link_route) { + xpc->set_pci_link_route(i, s->cfg.pci_intx_irq_base + i); + } + } +} + +static void xen_pvh_init(MachineState *ms) +{ + XenPVHMachineState *s = XEN_PVH_MACHINE(ms); + XenPVHMachineClass *xpc = XEN_PVH_MACHINE_GET_CLASS(s); + MemoryRegion *sysmem = get_system_memory(); + + if (ms->ram_size == 0) { + warn_report("%s: ram size not specified. QEMU machine started" + " without IOREQ (no emulated devices including virtio)", + MACHINE_CLASS(object_get_class(OBJECT(ms)))->desc); + return; + } + + xen_pvh_init_ram(s, sysmem); + xen_register_ioreq(&s->ioreq, ms->smp.max_cpus, + xpc->handle_bufioreq, + &xen_memory_listener); + + if (s->cfg.virtio_mmio_num) { + xen_create_virtio_mmio_devices(s); + } + +#ifdef CONFIG_TPM + if (xpc->has_tpm) { + if (s->cfg.tpm.base) { + xen_enable_tpm(s); + } else { + warn_report("tpm-base-addr is not set. TPM will not be enabled"); + } + } +#endif + + /* Non-zero pci-ecam-size enables PCI. */ + if (s->cfg.pci_ecam.size) { + if (s->cfg.pci_ecam.size != 256 * MiB) { + error_report("pci-ecam-size only supports values 0 or 0x10000000"); + exit(EXIT_FAILURE); + } + if (!s->cfg.pci_intx_irq_base) { + error_report("PCI enabled but pci-intx-irq-base not set"); + exit(EXIT_FAILURE); + } + + xenpvh_gpex_init(s, xpc, sysmem); + } + + /* Call the implementation specific init. */ + if (xpc->init) { + xpc->init(ms); + } +} + +#define XEN_PVH_PROP_MEMMAP_SETTER(n, f) \ +static void xen_pvh_set_ ## n ## _ ## f(Object *obj, Visitor *v, \ + const char *name, void *opaque, \ + Error **errp) \ +{ \ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \ + uint64_t value; \ + \ + if (!visit_type_size(v, name, &value, errp)) { \ + return; \ + } \ + xp->cfg.n.f = value; \ +} + +#define XEN_PVH_PROP_MEMMAP_GETTER(n, f) \ +static void xen_pvh_get_ ## n ## _ ## f(Object *obj, Visitor *v, \ + const char *name, void *opaque, \ + Error **errp) \ +{ \ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); \ + uint64_t value = xp->cfg.n.f; \ + \ + visit_type_uint64(v, name, &value, errp); \ +} + +#define XEN_PVH_PROP_MEMMAP_BASE(n) \ + XEN_PVH_PROP_MEMMAP_SETTER(n, base) \ + XEN_PVH_PROP_MEMMAP_GETTER(n, base) \ + +#define XEN_PVH_PROP_MEMMAP_SIZE(n) \ + XEN_PVH_PROP_MEMMAP_SETTER(n, size) \ + XEN_PVH_PROP_MEMMAP_GETTER(n, size) + +#define XEN_PVH_PROP_MEMMAP(n) \ + XEN_PVH_PROP_MEMMAP_BASE(n) \ + XEN_PVH_PROP_MEMMAP_SIZE(n) + +XEN_PVH_PROP_MEMMAP(ram_low) +XEN_PVH_PROP_MEMMAP(ram_high) +/* TPM only has a base-addr option. */ +XEN_PVH_PROP_MEMMAP_BASE(tpm) +XEN_PVH_PROP_MEMMAP(virtio_mmio) +XEN_PVH_PROP_MEMMAP(pci_ecam) +XEN_PVH_PROP_MEMMAP(pci_mmio) +XEN_PVH_PROP_MEMMAP(pci_mmio_high) + +static void xen_pvh_set_pci_intx_irq_base(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + xp->cfg.pci_intx_irq_base = value; +} + +static void xen_pvh_get_pci_intx_irq_base(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + XenPVHMachineState *xp = XEN_PVH_MACHINE(obj); + uint32_t value = xp->cfg.pci_intx_irq_base; + + visit_type_uint32(v, name, &value, errp); +} + +void xen_pvh_class_setup_common_props(XenPVHMachineClass *xpc) +{ + ObjectClass *oc = OBJECT_CLASS(xpc); + MachineClass *mc = MACHINE_CLASS(xpc); + +#define OC_MEMMAP_PROP_BASE(c, prop_name, name) \ +do { \ + object_class_property_add(c, prop_name "-base", "uint64_t", \ + xen_pvh_get_ ## name ## _base, \ + xen_pvh_set_ ## name ## _base, NULL, NULL); \ + object_class_property_set_description(oc, prop_name "-base", \ + "Set base address for " prop_name); \ +} while (0) + +#define OC_MEMMAP_PROP_SIZE(c, prop_name, name) \ +do { \ + object_class_property_add(c, prop_name "-size", "uint64_t", \ + xen_pvh_get_ ## name ## _size, \ + xen_pvh_set_ ## name ## _size, NULL, NULL); \ + object_class_property_set_description(oc, prop_name "-size", \ + "Set memory range size for " prop_name); \ +} while (0) + +#define OC_MEMMAP_PROP(c, prop_name, name) \ +do { \ + OC_MEMMAP_PROP_BASE(c, prop_name, name); \ + OC_MEMMAP_PROP_SIZE(c, prop_name, name); \ +} while (0) + + /* + * We provide memmap properties to allow Xen to move things to other + * addresses for example when users need to accomodate the memory-map + * for 1:1 mapped devices/memory. + */ + OC_MEMMAP_PROP(oc, "ram-low", ram_low); + OC_MEMMAP_PROP(oc, "ram-high", ram_high); + + if (xpc->has_virtio_mmio) { + OC_MEMMAP_PROP(oc, "virtio-mmio", virtio_mmio); + } + + if (xpc->has_pci) { + OC_MEMMAP_PROP(oc, "pci-ecam", pci_ecam); + OC_MEMMAP_PROP(oc, "pci-mmio", pci_mmio); + OC_MEMMAP_PROP(oc, "pci-mmio-high", pci_mmio_high); + + object_class_property_add(oc, "pci-intx-irq-base", "uint32_t", + xen_pvh_get_pci_intx_irq_base, + xen_pvh_set_pci_intx_irq_base, + NULL, NULL); + object_class_property_set_description(oc, "pci-intx-irq-base", + "Set PCI INTX interrupt base line."); + } + +#ifdef CONFIG_TPM + if (xpc->has_tpm) { + object_class_property_add(oc, "tpm-base-addr", "uint64_t", + xen_pvh_get_tpm_base, + xen_pvh_set_tpm_base, + 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 void xen_pvh_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = xen_pvh_init; + + mc->desc = "Xen PVH machine"; + mc->max_cpus = 1; + mc->default_machine_opts = "accel=xen"; + /* Set to zero to make sure that the real ram size is passed. */ + mc->default_ram_size = 0; +} + +static const TypeInfo xen_pvh_info = { + .name = TYPE_XEN_PVH_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(XenPVHMachineState), + .class_size = sizeof(XenPVHMachineClass), + .class_init = xen_pvh_class_init, +}; + +static void xen_pvh_register_types(void) +{ + type_register_static(&xen_pvh_info); +} + +type_init(xen_pvh_register_types); diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index 46ee4a7f02..45ae134b84 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]; @@ -119,11 +66,3 @@ int xen_config_dev_vkbd(int vdev) xen_config_dev_dirs("vkbd", "vkbd", vdev, fe, be, sizeof(fe)); return xen_config_dev_all(fe, be); } - -int xen_config_dev_console(int vdev) -{ - char fe[256], be[256]; - - xen_config_dev_dirs("console", "console", vdev, fe, be, sizeof(fe)); - return xen_config_dev_all(fe, be); -} diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 5dd706efbf..3635d1b39f 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -57,12 +57,12 @@ #include #include "hw/pci/pci.h" -#include "hw/pci/pci_bus.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; @@ -435,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; } @@ -444,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); } @@ -692,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. */ @@ -711,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, @@ -760,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 */ @@ -951,7 +951,7 @@ void xen_igd_reserve_slot(PCIBus *pci_bus) } XEN_PT_LOG(0, "Reserving PCI slot 2 for IGD\n"); - pci_bus->slot_reserved_mask |= XEN_PCI_IGD_SLOT_MASK; + pci_bus_set_slot_reserved_mask(pci_bus, XEN_PCI_IGD_SLOT_MASK); } static void xen_igd_clear_slot(DeviceState *qdev, Error **errp) @@ -971,7 +971,7 @@ static void xen_igd_clear_slot(DeviceState *qdev, Error **errp) return; } - if (!(pci_bus->slot_reserved_mask & XEN_PCI_IGD_SLOT_MASK)) { + if (!(pci_bus_get_slot_reserved_mask(pci_bus) & XEN_PCI_IGD_SLOT_MASK)) { xpdc->pci_qdev_realize(qdev, errp); return; } @@ -982,7 +982,7 @@ static void xen_igd_clear_slot(DeviceState *qdev, Error **errp) 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->slot_reserved_mask &= ~XEN_PCI_IGD_SLOT_MASK; + 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); diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 292bdf7499..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) @@ -53,12 +59,6 @@ typedef struct XenPTDeviceClass { XenPTQdevRealize pci_qdev_realize; } XenPTDeviceClass; -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); - /* function type for config reg */ typedef int (*xen_pt_conf_reg_init) (XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset, @@ -341,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 cde898b744..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, @@ -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, 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_load_rom.c b/hw/xen/xen_pt_load_rom.c index 03422a8a71..6bc64acd33 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -53,7 +53,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, } fseek(fp, 0, SEEK_SET); - if (dev->romsize != -1) { + if (dev->romsize != UINT_MAX) { if (st.st_size > dev->romsize) { error_report("ROM BAR \"%s\" (%ld bytes) is too large for ROM size %u", rom_file, (long) st.st_size, dev->romsize); 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 5c108446a8..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) 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..24395f42cb 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -32,14 +32,9 @@ 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_bus_init(); switch (xen_mode) { case XEN_ATTACH: @@ -55,10 +50,6 @@ 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) { xen_config_dev_vfb(0, "vnc"); @@ -66,23 +57,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 */ atexit(xen_config_cleanup); } diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig index 0740657ea5..1f0492d89c 100644 --- a/hw/xtensa/Kconfig +++ b/hw/xtensa/Kconfig @@ -1,14 +1,21 @@ config XTENSA_SIM + default y + depends on XTENSA bool config XTENSA_VIRT bool + default y + depends on XTENSA select XTENSA_SIM select PCI_EXPRESS_GENERIC_BRIDGE select PCI_DEVICES config XTENSA_XTFPGA bool + default y + depends on XTENSA && FDT + select DEVICE_TREE select OPENCORES_ETH select PFLASH_CFI01 - select SERIAL + select SERIAL_MM diff --git a/hw/xtensa/bootparam.h b/hw/xtensa/bootparam.h index ade7891ec5..f57ff850bc 100644 --- a/hw/xtensa/bootparam.h +++ b/hw/xtensa/bootparam.h @@ -1,6 +1,8 @@ #ifndef HW_XTENSA_BOOTPARAM_H #define HW_XTENSA_BOOTPARAM_H +#include "exec/cpu-common.h" + #define BP_TAG_COMMAND_LINE 0x1001 /* command line (0-terminated string)*/ #define BP_TAG_INITRD 0x1002 /* ramdisk addr and size (bp_meminfo) */ #define BP_TAG_MEMORY 0x1003 /* memory addr and size (bp_meminfo) */ 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..398e6256e1 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -35,7 +35,7 @@ #include "hw/qdev-properties.h" #include "elf.h" #include "exec/memory.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #include "net/net.h" #include "hw/sysbus.h" #include "hw/block/flash.h" @@ -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. */ @@ -362,7 +356,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_tagptr = put_tag(cur_tagptr, BP_TAG_COMMAND_LINE, strlen(kernel_cmdline) + 1, kernel_cmdline); } -#ifdef CONFIG_FDT if (dtb_filename) { int fdt_size; void *fdt = load_device_tree(dtb_filename, &fdt_size); @@ -379,14 +372,6 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB); g_free(fdt); } -#else - if (dtb_filename) { - error_report("could not load DTB '%s': " - "FDT support is not configured in QEMU", - dtb_filename); - exit(EXIT_FAILURE); - } -#endif if (initrd_filename) { BpMemInfo initrd_location = { 0 }; int initrd_size = load_ramdisk(initrd_filename, cur_lowmem, @@ -412,7 +397,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 { @@ -429,8 +415,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) } } if (entry_point != env->pc) { - uint8_t boot[] = { -#if TARGET_BIG_ENDIAN + uint8_t boot_be[] = { 0x60, 0x00, 0x08, /* j 1f */ 0x00, /* .literal_position */ 0x00, 0x00, 0x00, 0x00, /* .literal entry_pc */ @@ -439,7 +424,8 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) 0x10, 0xff, 0xfe, /* l32r a0, entry_pc */ 0x12, 0xff, 0xfe, /* l32r a2, entry_a2 */ 0x0a, 0x00, 0x00, /* jx a0 */ -#else + }; + uint8_t boot_le[] = { 0x06, 0x02, 0x00, /* j 1f */ 0x00, /* .literal_position */ 0x00, 0x00, 0x00, 0x00, /* .literal entry_pc */ @@ -448,14 +434,16 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) 0x01, 0xfe, 0xff, /* l32r a0, entry_pc */ 0x21, 0xfe, 0xff, /* l32r a2, entry_a2 */ 0xa0, 0x00, 0x00, /* jx a0 */ -#endif }; + const size_t boot_sz = TARGET_BIG_ENDIAN ? sizeof(boot_be) + : sizeof(boot_le); + uint8_t *boot = TARGET_BIG_ENDIAN ? boot_be : boot_le; uint32_t entry_pc = tswap32(entry_point); uint32_t entry_a2 = tswap32(tagptr); memcpy(boot + 4, &entry_pc, sizeof(entry_pc)); memcpy(boot + 8, &entry_a2, sizeof(entry_a2)); - cpu_physical_memory_write(env->pc, boot, sizeof(boot)); + cpu_physical_memory_write(env->pc, boot, boot_sz); } } else { if (flash) { diff --git a/include/block/accounting.h b/include/block/accounting.h index b9caad60d5..a59e39f49d 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -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, }; diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index dd9a7f6461..cf5e8bde1c 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -63,9 +63,6 @@ extern AioWait global_aio_wait; * @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 - * @unlock: whether to unlock and then lock again @ctx. This apples - * only when waiting for another AioContext from the main loop. - * Otherwise it's ignored. * * Wait while a condition is true. Use this to implement synchronous * operations that require event loop activity. @@ -78,14 +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_INTERNAL(ctx, cond, unlock) ({ \ +#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(); \ + smp_mb__after_rmw(); \ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ while ((cond)) { \ aio_poll(ctx_, true); \ @@ -95,13 +92,7 @@ extern AioWait global_aio_wait; assert(qemu_get_current_aio_context() == \ qemu_get_aio_context()); \ while ((cond)) { \ - if (unlock && ctx_) { \ - aio_context_release(ctx_); \ - } \ aio_poll(qemu_get_aio_context(), true); \ - if (unlock && ctx_) { \ - aio_context_acquire(ctx_); \ - } \ waited_ = true; \ } \ } \ @@ -109,10 +100,11 @@ extern AioWait global_aio_wait; waited_; }) #define AIO_WAIT_WHILE(ctx, cond) \ - AIO_WAIT_WHILE_INTERNAL(ctx, cond, true) + 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, false) + AIO_WAIT_WHILE_INTERNAL(ctx, cond) /** * aio_wait_kick: @@ -131,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); @@ -151,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..43883a8a33 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -17,18 +17,21 @@ #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/lockcnt.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 +54,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 +129,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 +210,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 +225,6 @@ struct AioContext { */ QEMUTimerListGroup tlg; - int external_disable_cnt; - /* Number of AioHandlers without .io_poll() */ int poll_disable_cnt; @@ -279,23 +279,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 +306,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 +319,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 +451,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 +462,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 +477,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 +510,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 +610,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 +622,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: @@ -693,6 +630,9 @@ void aio_co_schedule(AioContext *ctx, struct Coroutine *co); * * Move the currently running coroutine to new_ctx. If the coroutine is already * running in new_ctx, do nothing. + * + * Note that this function cannot reschedule from iohandler_ctx to + * qemu_aio_context. */ void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx); @@ -708,7 +648,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 +657,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. @@ -725,6 +665,9 @@ void aio_co_enter(AioContext *ctx, struct Coroutine *co); * If called from an IOThread this will be the IOThread's AioContext. If * called from the main thread or with the "big QEMU lock" taken it * will be the main loop AioContext. + * + * Note that the return value is never the main loop's iohandler_ctx and the + * return value is the main loop AioContext instead. */ AioContext *qemu_get_current_aio_context(void); @@ -768,8 +711,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..c81d637617 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); @@ -42,8 +40,6 @@ void aio_task_pool_free(AioTaskPool *); /* error code of failed task or 0 if all is OK */ int aio_task_pool_status(AioTaskPool *pool); -bool aio_task_pool_empty(AioTaskPool *pool); - /* User provides filled @task, however task->pool will be set automatically */ void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task); diff --git a/include/block/block-common.h b/include/block/block-common.h index 72528c0ba9..0b5f0d5a27 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; @@ -154,8 +243,10 @@ typedef enum { read-write fails */ #define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ +#define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */ + #ifdef XBOX -#define BDRV_O_RO_WRITE_SHARE 0x80000 /* allow the file to open RO alongside an existing RW handle */ +#define BDRV_O_RO_WRITE_SHARE 0x100000 /* allow the file to open RO alongside an existing RW handle */ #endif #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) @@ -175,6 +266,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) @@ -200,6 +297,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 @@ -235,6 +334,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; diff --git a/include/block/block-copy.h b/include/block/block-copy.h index ba0b425d78..dd5cc82f3b 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 */ @@ -25,7 +25,10 @@ typedef struct BlockCopyState BlockCopyState; typedef struct BlockCopyCallState BlockCopyCallState; BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, + BlockDriverState *copy_bitmap_bs, const BdrvDirtyBitmap *bitmap, + bool discard_source, + uint64_t min_cluster_size, Error **errp); /* Function should be called prior any actual copy request */ @@ -36,8 +39,9 @@ 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, uint64_t timeout_ns, diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index c7bd4a2088..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,37 +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); +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 *bdrv_open_blockdev_ref(BlockdevRef *ref, 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, @@ -103,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 @@ -128,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); @@ -168,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, @@ -184,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); @@ -206,32 +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); - -/** - * Unlocks the AioContext of @bs if it's not the current AioContext. - */ -void coroutine_fn bdrv_co_unlock(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); -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); /** * diff --git a/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h index ba0593c440..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); diff --git a/include/block/block-io.h b/include/block/block-io.h index b099d7db45..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,101 +41,182 @@ * to catch when they are accidentally called by the wrong API. */ -int generated_co_wrapper 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 generated_co_wrapper bdrv_pread(BdrvChild *child, int64_t offset, - int64_t bytes, void *buf, - BdrvRequestFlags flags); -int generated_co_wrapper bdrv_pwrite(BdrvChild *child, int64_t offset, - int64_t bytes, const void *buf, - BdrvRequestFlags flags); -int generated_co_wrapper bdrv_pwrite_sync(BdrvChild *child, int64_t offset, - int64_t bytes, const void *buf, - BdrvRequestFlags flags); -int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, - int64_t bytes, const void *buf, - BdrvRequestFlags flags); + +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 coroutine_fn 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 coroutine_fn 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); @@ -155,7 +238,18 @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); void bdrv_enable_copy_on_read(BlockDriverState *bs); void bdrv_disable_copy_on_read(BlockDriverState *bs); -void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); +void coroutine_fn 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 { \ @@ -190,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); /** * @@ -232,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. @@ -281,47 +356,54 @@ void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter); void bdrv_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 @@ -329,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: @@ -338,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); @@ -348,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 31ae91e56e..ebb4e56a50 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 @@ -141,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 @@ -162,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. @@ -210,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); @@ -235,42 +235,39 @@ 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); 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. @@ -293,15 +290,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 @@ -310,27 +307,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, @@ -340,13 +340,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 @@ -363,12 +364,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. @@ -376,12 +393,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 @@ -398,12 +417,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). @@ -411,7 +430,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() @@ -421,7 +441,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 @@ -435,11 +455,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 @@ -450,9 +470,10 @@ struct BlockDriver { * * Returns: true on success, false on failure */ - bool (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size, - Error **errp); - void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host, size_t size); + 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 @@ -469,25 +490,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); /** @@ -505,16 +528,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); /** @@ -532,12 +555,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 @@ -545,10 +568,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, @@ -558,14 +583,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, @@ -576,14 +597,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 @@ -610,7 +627,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); @@ -630,43 +648,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 @@ -681,81 +704,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 coroutine_fn (*bdrv_co_can_store_new_dirty_bitmap)( + + 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 (*bdrv_co_remove_persistent_dirty_bitmap)( + + 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; @@ -840,6 +868,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; @@ -893,11 +949,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. @@ -905,7 +982,9 @@ 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 (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, GHashTable *visited, Transaction *tran, @@ -928,31 +1007,6 @@ struct BdrvChildClass { const char *(*get_name)(BdrvChild *child); AioContext *(*get_parent_aio_context)(BdrvChild *child); - - /* - * If this pair of functions is implemented, the parent doesn't issue new - * requests after returning from .drained_begin() until .drained_end() is - * called. - * - * These functions must not change the graph (and therefore also must not - * call aio_poll(), which could change the graph indirectly). - * - * If drained_end() schedules background operations, it must atomically - * increment *drained_end_counter for each such operation and atomically - * decrement it once the operation has settled. - * - * Note that this can be nested. If drained_begin() was called twice, new - * I/O is allowed only after drained_end() was called twice, too. - */ - void (*drained_begin)(BdrvChild *child); - void (*drained_end)(BdrvChild *child, int *drained_end_counter); - - /* - * Returns whether the parent has pending requests for the child. This - * callback is polled after .drained_begin() has been called until all - * activity on the child has stopped. - */ - bool (*drained_poll)(BdrvChild *child); }; extern const BdrvChildClass child_of_bds; @@ -982,16 +1036,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; }; /* @@ -1124,11 +1178,11 @@ struct BlockDriverState { * See also comment in include/block/block.h, to learn how backing and file * are connected with BdrvChildRole. */ - QLIST_HEAD(, BdrvChild) children; - BdrvChild *backing; - BdrvChild *file; + QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) children; + BdrvChild * GRAPH_RDLOCK_PTR backing; + BdrvChild * GRAPH_RDLOCK_PTR file; - QLIST_HEAD(, BdrvChild) parents; + QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) parents; QDict *options; QDict *explicit_options; @@ -1137,8 +1191,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 @@ -1175,23 +1227,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? */ @@ -1206,6 +1251,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 { @@ -1244,7 +1292,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. */ diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index b49f4eb35b..eb2d92a226 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: @@ -184,7 +193,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap, BitmapSyncMode bitmap_mode, - bool compress, + bool compress, bool discard_source, const char *filter_node_name, BackupPerf *perf, BlockdevOnError on_source_error, @@ -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 4b0b3e17ef..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,42 +35,44 @@ * 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, +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, +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); } @@ -95,7 +99,7 @@ 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); @@ -109,45 +113,53 @@ void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, 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 03032b2eca..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 */ @@ -54,7 +54,7 @@ typedef struct BlockJob { /** * Speed that was set with @block_job_set_speed. - * Always modified and read under QEMU global mutex (GLOBAL_STATE_CODE). + * Always modified and read under the BQL (GLOBAL_STATE_CODE). */ int64_t speed; @@ -66,7 +66,7 @@ typedef struct BlockJob { /** * Block other operations when block job is running. - * Always modified and read under QEMU global mutex (GLOBAL_STATE_CODE). + * Always modified and read under the BQL (GLOBAL_STATE_CODE). */ Error *blocker; @@ -89,7 +89,7 @@ typedef struct BlockJob { /** * BlockDriverStates that are involved in this block job. - * Always modified and read under QEMU global mutex (GLOBAL_STATE_CODE). + * Always modified and read under the BQL (GLOBAL_STATE_CODE). */ GSList *nodes; } BlockJob; @@ -138,8 +138,9 @@ BlockJob *block_job_get_locked(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: @@ -172,6 +173,17 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); */ bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp); +/** + * 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. 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..dc8d949184 --- /dev/null +++ b/include/block/graph-lock.h @@ -0,0 +1,285 @@ +/* + * 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_ACQUIRE_SHARED(graph_lock) coroutine_fn +graph_lockable_auto_lock(GraphLockable *x) +{ + bdrv_graph_co_rdlock(); + return x; +} + +static inline void TSA_RELEASE_SHARED(graph_lock) coroutine_fn +graph_lockable_auto_unlock(GraphLockable **x) +{ + bdrv_graph_co_rdunlock(); +} + +#define GRAPH_AUTO_UNLOCK __attribute__((cleanup(graph_lockable_auto_unlock))) + +/* + * @var is only used to break the loop after the first iteration. + * @unlock_var can't be unlocked and then set to NULL because TSA wants the lock + * to be held at the start of every iteration of the loop. + */ +#define WITH_GRAPH_RDLOCK_GUARD_(var) \ + for (GraphLockable *unlock_var GRAPH_AUTO_UNLOCK = \ + graph_lockable_auto_lock(GML_OBJ_()), \ + *var = unlock_var; \ + var; \ + var = NULL) + +#define WITH_GRAPH_RDLOCK_GUARD() \ + WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) + +#define GRAPH_RDLOCK_GUARD(x) \ + GraphLockable * GRAPH_AUTO_UNLOCK \ + 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 4ede3b2bd0..d4f8b21aec 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,42 @@ #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; +/* + * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must + * succeed at NBD_OPT_GO before being forcefully dropped as too slow. + */ +#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 + +/* + * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at + * once; must be large enough to allow a MULTI_CONN-aware client like + * nbdcopy to create its typical number of 8-16 sockets. + */ +#define NBD_DEFAULT_MAX_CONNECTIONS 100 + /* 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 +67,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 +104,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 +209,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 +247,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 +265,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 +275,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 +308,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 +363,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 +374,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 +391,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,23 +404,24 @@ 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); NBDExport *nbd_export_find(const char *name); void nbd_client_new(QIOChannelSocket *sioc, + uint32_t handshake_max_secs, QCryptoTLSCreds *tlscreds, const char *tlsauthz, - void (*close_fn)(NBDClient *, bool)); + void (*close_fn)(NBDClient *, bool), + void *owner); +void *nbd_client_owner(NBDClient *client); void nbd_client_get(NBDClient *client); void nbd_client_put(NBDClient *client); @@ -406,10 +487,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, diff --git a/include/block/nvme.h b/include/block/nvme.h index 8027b7126b..f4d108841b 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, @@ -595,7 +615,9 @@ 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, @@ -611,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, @@ -704,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; @@ -773,6 +799,8 @@ typedef struct QEMU_PACKED NvmeDsmRange { enum { NVME_COPY_FORMAT_0 = 0x0, NVME_COPY_FORMAT_1 = 0x1, + NVME_COPY_FORMAT_2 = 0x2, + NVME_COPY_FORMAT_3 = 0x3, }; typedef struct QEMU_PACKED NvmeCopyCmd { @@ -794,25 +822,30 @@ typedef struct QEMU_PACKED NvmeCopyCmd { uint16_t appmask; } NvmeCopyCmd; -typedef struct QEMU_PACKED NvmeCopySourceRangeFormat0 { - uint8_t rsvd0[8]; +typedef struct QEMU_PACKED NvmeCopySourceRangeFormat0_2 { + uint32_t sparams; + uint8_t rsvd4[4]; uint64_t slba; uint16_t nlb; - uint8_t rsvd18[6]; + uint8_t rsvd18[4]; + uint16_t sopt; uint32_t reftag; uint16_t apptag; uint16_t appmask; -} NvmeCopySourceRangeFormat0; +} NvmeCopySourceRangeFormat0_2; -typedef struct QEMU_PACKED NvmeCopySourceRangeFormat1 { - uint8_t rsvd0[8]; +typedef struct QEMU_PACKED NvmeCopySourceRangeFormat1_3 { + uint32_t sparams; + uint8_t rsvd4[4]; uint64_t slba; uint16_t nlb; - uint8_t rsvd18[8]; + uint8_t rsvd18[4]; + uint16_t sopt; + uint8_t rsvd24[2]; uint8_t sr[10]; uint16_t apptag; uint16_t appmask; -} NvmeCopySourceRangeFormat1; +} NvmeCopySourceRangeFormat1_3; enum NvmeAsyncEventRequest { NVME_AER_TYPE_ERROR = 0, @@ -875,6 +908,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, @@ -909,6 +944,8 @@ enum NvmeStatusCodes { NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, NVME_CMD_SIZE_LIMIT = 0x0183, + NVME_CMD_INCOMP_NS_OR_FMT = 0x0185, + NVME_CMD_OVERLAP_IO_RANGE = 0x0187, NVME_INVALID_ZONE_OP = 0x01b6, NVME_NOZRWA = 0x01b7, NVME_ZONE_BOUNDARY_ERROR = 0x01b8, @@ -1005,11 +1042,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 { @@ -1035,15 +1077,18 @@ enum NvmeIdCns { NVME_ID_CNS_CS_NS = 0x05, NVME_ID_CNS_CS_CTRL = 0x06, NVME_ID_CNS_CS_NS_ACTIVE_LIST = 0x07, + NVME_ID_CNS_CS_IND_NS = 0x08, NVME_ID_CNS_NS_PRESENT_LIST = 0x10, 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_ENDURANCE_GROUP_LIST = 0x19, NVME_ID_CNS_CS_NS_PRESENT_LIST = 0x1a, NVME_ID_CNS_CS_NS_PRESENT = 0x1b, NVME_ID_CNS_IO_COMMAND_SET = 0x1c, + NVME_ID_CNS_CS_IND_NS_ALLOCATED = 0x1f, }; typedef struct QEMU_PACKED NvmeIdCtrl { @@ -1091,7 +1136,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; @@ -1134,15 +1182,19 @@ enum NvmeIdCtrlOaes { }; enum NvmeIdCtrlCtratt { + NVME_CTRATT_ENDGRPS = 1 << 4, NVME_CTRATT_ELBAS = 1 << 15, + NVME_CTRATT_MEM = 1 << 16, + 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_DBBUF = 1 << 8, + 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 { @@ -1155,11 +1207,15 @@ enum NvmeIdCtrlOncs { NVME_ONCS_TIMESTAMP = 1 << 6, NVME_ONCS_VERIFY = 1 << 7, NVME_ONCS_COPY = 1 << 8, + NVME_ONCS_NVMCSA = 1 << 9, + NVME_ONCS_NVMAFC = 1 << 10, }; enum NvmeIdCtrlOcfs { NVME_OCFS_COPY_FORMAT_0 = 1 << NVME_COPY_FORMAT_0, NVME_OCFS_COPY_FORMAT_1 = 1 << NVME_COPY_FORMAT_1, + NVME_OCFS_COPY_FORMAT_2 = 1 << NVME_COPY_FORMAT_2, + NVME_OCFS_COPY_FORMAT_3 = 1 << NVME_COPY_FORMAT_3, }; enum NvmeIdctrlVwc { @@ -1227,10 +1283,13 @@ 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) +#define NVME_ID_CTRL_CTRATT_MEM(ctratt) (ctratt & NVME_CTRATT_MEM) + enum NvmeFeatureIds { NVME_ARBITRATION = 0x1, NVME_POWER_MANAGEMENT = 0x2, @@ -1246,6 +1305,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, }; @@ -1290,7 +1351,9 @@ typedef struct NvmeHostBehaviorSupport { uint8_t acre; uint8_t etdas; uint8_t lbafee; - uint8_t rsvd3[509]; + uint8_t rsvd3; + uint16_t cdfe; + uint8_t rsvd6[506]; } NvmeHostBehaviorSupport; typedef struct QEMU_PACKED NvmeLBAF { @@ -1338,7 +1401,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]; @@ -1352,9 +1418,28 @@ typedef struct QEMU_PACKED NvmeIdNsNvm { uint8_t pic; uint8_t rsvd9[3]; uint32_t elbaf[NVME_MAX_NLBAF]; - uint8_t rsvd268[3828]; + uint32_t npdgl; + uint32_t nprg; + uint32_t npra; + uint32_t nors; + uint32_t npdal; + uint8_t rsvd288[3808]; } NvmeIdNsNvm; +typedef struct QEMU_PACKED NvmeIdNsInd { + uint8_t nsfeat; + uint8_t nmic; + uint8_t rescap; + uint8_t fpi; + uint32_t anagrpid; + uint8_t nsattr; + uint8_t rsvd9; + uint16_t nvmsetid; + uint16_t endgrpid; + uint8_t nstat; + uint8_t rsvd15[4081]; +} NvmeIdNsInd; + typedef struct QEMU_PACKED NvmeIdNsDescr { uint8_t nidt; uint8_t nidl; @@ -1375,8 +1460,10 @@ enum NvmeNsIdentifierType { NVME_NIDT_CSI = 0x04, }; -enum NvmeIdNsNmic { - NVME_NMIC_NS_SHARED = 1 << 0, +enum NvmeIdNsIndependent { + NVME_ID_NS_IND_NMIC_SHRNS = 1 << 0, + NVME_ID_NS_IND_NMIC_DISNS = 1 << 1, + NVME_ID_NS_IND_NSTAT_NRDY = 1 << 0, }; enum NvmeCsi { @@ -1454,6 +1541,16 @@ enum NvmeIdNsMc { NVME_ID_NS_MC_SEPARATE = 1 << 1, }; +enum NvmeIdNsNsfeat { + NVME_ID_NS_NSFEAT_THINP = 1 << 0, + NVME_ID_NS_NSFEAT_NSABPNS = 1 << 1, + NVME_ID_NS_NSFEAT_DAE = 1 << 2, + NVME_ID_NS_NSFEAT_UIDREUSE = 1 << 3, + NVME_ID_NS_NSFEAT_OPTPERF_ALL = 3 << 4, + NVME_ID_NS_NSFEAT_MAM = 1 << 6, + NVME_ID_NS_NSFEAT_OPTRPERF = 1 << 7, +}; + #define NVME_ID_NS_DPS_TYPE(dps) (dps & NVME_ID_NS_DPS_TYPE_MASK) enum NvmePIFormat { @@ -1617,6 +1714,169 @@ typedef enum NvmeVirtualResourceType { 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); @@ -1624,8 +1884,8 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeZonedResult) != 8); QEMU_BUILD_BUG_ON(sizeof(NvmeCqe) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeDsmRange) != 16); - QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat0) != 32); - QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat1) != 40); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat0_2) != 32); + QEMU_BUILD_BUG_ON(sizeof(NvmeCopySourceRangeFormat1_3) != 40); QEMU_BUILD_BUG_ON(sizeof(NvmeCmd) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeDeleteQ) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeCreateCq) != 64); @@ -1646,6 +1906,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeLBAF) != 4); QEMU_BUILD_BUG_ON(sizeof(NvmeLBAFE) != 16); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNs) != 4096); + QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsInd) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsNvm) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdNsZoned) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeSglDescriptor) != 16); @@ -1655,5 +1916,7 @@ static inline void _nvme_check_size(void) 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..626706827f 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,25 @@ 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); + +bool laio_has_fdsync(int); 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..57f5ea3500 --- /dev/null +++ b/include/block/ufs.h @@ -0,0 +1,1221 @@ +/* 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 mcqcap; + 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; + uint32_t rsvd9[127]; + uint32_t config; + uint32_t rsvd10[3]; + uint32_t rsvd11[28]; + uint32_t mcqconfig; + uint32_t esilba; + uint32_t esiuba; +} 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) + FIELD(CAP, LSDBS, 29, 1) + FIELD(CAP, MCQS, 30, 1) +REG32(MCQCAP, offsetof(UfsReg, mcqcap)) + FIELD(MCQCAP, MAXQ, 0, 8) + FIELD(MCQCAP, SP, 8, 1) + FIELD(MCQCAP, RRP, 9, 1) + FIELD(MCQCAP, EIS, 10, 1) + FIELD(MCQCAP, QCFGPTR, 16, 8) + FIELD(MCQCAP, MIAG, 24, 8) +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) + FIELD(IS, CQES, 20, 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) + FIELD(IE, CQEE, 20, 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)) +REG32(CONFIG, offsetof(UfsReg, config)) + FIELD(CONFIG, QT, 0, 1) +REG32(MCQCONFIG, offsetof(UfsReg, mcqconfig)) + FIELD(MCQCONFIG, MAC, 8, 8) + +#define UFS_INTR_MASK \ + ((1 << R_IS_CQES_SHIFT) | \ + (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 UfsMcqReg { + uint32_t sqattr; + uint32_t sqlba; + uint32_t squba; + uint32_t sqdao; + uint32_t sqisao; + uint32_t sqcfg; + uint32_t rsvd0[2]; + uint32_t cqattr; + uint32_t cqlba; + uint32_t cquba; + uint32_t cqdao; + uint32_t cqisao; + uint32_t cqcfg; + uint32_t rsvd1[2]; +} UfsMcqReg; + +REG32(SQATTR, offsetof(UfsMcqReg, sqattr)) + FIELD(SQATTR, SIZE, 0, 16) + FIELD(SQATTR, CQID, 16, 8) + FIELD(SQATTR, SQPL, 28, 3) + FIELD(SQATTR, SQEN, 31, 1) +REG32(SQLBA, offsetof(UfsMcqReg, sqlba)) +REG32(SQUBA, offsetof(UfsMcqReg, squba)) +REG32(SQDAO, offsetof(UfsMcqReg, sqdao)) +REG32(SQISAO, offsetof(UfsMcqReg, sqisao)) +REG32(SQCFG, offsetof(UfsMcqReg, sqcfg)) +REG32(CQATTR, offsetof(UfsMcqReg, cqattr)) + FIELD(CQATTR, SIZE, 0, 16) + FIELD(CQATTR, CQEN, 31, 1) +REG32(CQLBA, offsetof(UfsMcqReg, cqlba)) +REG32(CQUBA, offsetof(UfsMcqReg, cquba)) +REG32(CQDAO, offsetof(UfsMcqReg, cqdao)) +REG32(CQISAO, offsetof(UfsMcqReg, cqisao)) +REG32(CQCFG, offsetof(UfsMcqReg, cqcfg)) + +typedef struct QEMU_PACKED UfsMcqSqReg { + uint32_t hp; + uint32_t tp; + uint32_t rtc; + uint32_t cti; + uint32_t rts; +} UfsMcqSqReg; + +typedef struct QEMU_PACKED UfsMcqCqReg { + uint32_t hp; + uint32_t tp; +} UfsMcqCqReg; + +typedef struct QEMU_PACKED UfsMcqSqIntReg { + uint32_t is; + uint32_t ie; +} UfsMcqSqIntReg; + +typedef struct QEMU_PACKED UfsMcqCqIntReg { + uint32_t is; + uint32_t ie; + uint32_t iacr; +} UfsMcqCqIntReg; + +REG32(CQIS, offsetof(UfsMcqCqIntReg, is)) + FIELD(CQIS, TEPS, 0, 1) + +/* + * Provide MCQ Operation & Runtime Registers as a contiguous addressed + * registers for the simplicity. + * DAO(Doorbell Address Offset) and ISAO(Interrupt Status Register Address + * Offset) registers should be properly configured with the following + * structure. + */ +#define UFS_MCQ_OPR_START 0x1000 +typedef struct QEMU_PACKED UfsMcqOpReg { + UfsMcqSqReg sq; + UfsMcqSqIntReg sq_int; + UfsMcqCqReg cq; + UfsMcqCqIntReg cq_int; +} UfsMcqOpReg; + +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 + +/* + * The range of valid value of Active ICC attritbute + * is from 0x00 to 0x0F. + */ +#define UFS_QUERY_ATTR_ACTIVE_ICC_MAXVALUE 0x0F + +/* + * 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; + +/* + * MCQ Completion Queue Entry + */ +typedef UtpTransferReqDesc UfsSqEntry; +typedef struct QEMU_PACKED UfsCqEntry { + uint64_t utp_addr; + uint16_t resp_len; + uint16_t resp_off; + uint16_t prdt_len; + uint16_t prdt_off; + uint8_t status; + uint8_t error; + uint16_t rsvd1; + uint32_t rsvd2[3]; +} UfsCqEntry; + +static inline void _ufs_check_size(void) +{ + QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x38C); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqReg) != 64); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqSqReg) != 20); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqCqReg) != 8); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqSqIntReg) != 8); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqCqIntReg) != 12); + QEMU_BUILD_BUG_ON(sizeof(UfsMcqOpReg) != 48); + 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); + QEMU_BUILD_BUG_ON(sizeof(UfsSqEntry) != 32); + QEMU_BUILD_BUG_ON(sizeof(UfsCqEntry) != 32); +} +#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..8ef05b3dd0 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; @@ -16,8 +20,8 @@ struct CharBackend { IOReadHandler *chr_read; BackendChangeHandler *chr_be_change; void *opaque; - int tag; - int fe_open; + unsigned int tag; + 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); /** @@ -209,6 +228,7 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, * is thread-safe. * * Returns: the number of bytes consumed (0 if no associated Chardev) + * or -1 on error. */ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); @@ -223,6 +243,7 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); * attempted to be written. This function is thread-safe. * * Returns: the number of bytes consumed (0 if no associated Chardev) + * or -1 on error. */ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); @@ -234,6 +255,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); * Read data to a buffer from the back end. * * Returns: the number of bytes read (0 if no associated Chardev) + * or -1 on error. */ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len); diff --git a/include/chardev/char-socket.h b/include/chardev/char-socket.h index 0708ca6fa9..d6d13ad37f 100644 --- a/include/chardev/char-socket.h +++ b/include/chardev/char-socket.h @@ -74,7 +74,7 @@ struct SocketChardev { bool is_websock; GSource *reconnect_timer; - int64_t reconnect_time; + int64_t reconnect_time_ms; bool connect_err_reported; QIOTask *connect_task; diff --git a/include/chardev/char.h b/include/chardev/char.h index 44cd82e405..01df55f9e8 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -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/afsplit.h b/include/crypto/afsplit.h index 4894d64330..06f28fe67c 100644 --- a/include/crypto/afsplit.h +++ b/include/crypto/afsplit.h @@ -46,7 +46,7 @@ * * splitkey = g_new0(uint8_t, nkey * stripes); * - * if (qcrypto_afsplit_encode(QCRYPTO_HASH_ALG_SHA256, + * if (qcrypto_afsplit_encode(QCRYPTO_HASH_ALGO_SHA256, * nkey, stripes, * masterkey, splitkey, errp) < 0) { * g_free(splitkey); @@ -71,7 +71,7 @@ * * masterkey = g_new0(uint8_t, nkey); * - * if (qcrypto_afsplit_decode(QCRYPTO_HASH_ALG_SHA256, + * if (qcrypto_afsplit_decode(QCRYPTO_HASH_ALGO_SHA256, * nkey, stripes, * splitkey, masterkey, errp) < 0) { * g_free(splitkey); @@ -102,7 +102,7 @@ * * Returns: 0 on success, -1 on error; */ -int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, +int qcrypto_afsplit_encode(QCryptoHashAlgo hash, size_t blocklen, uint32_t stripes, const uint8_t *in, @@ -124,7 +124,7 @@ int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash, * * Returns: 0 on success, -1 on error; */ -int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash, +int qcrypto_afsplit_decode(QCryptoHashAlgo hash, size_t blocklen, uint32_t stripes, const uint8_t *in, diff --git a/include/crypto/akcipher.h b/include/crypto/akcipher.h index 214e58ca47..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. diff --git a/include/crypto/block.h b/include/crypto/block.h index 4f63a37872..b013d27f00 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -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; /** @@ -75,7 +76,6 @@ typedef enum { * @readfunc: callback for reading data from the volume * @opaque: data to pass to @readfunc * @flags: bitmask of QCryptoBlockOpenFlags values - * @n_threads: allow concurrent I/O from up to @n_threads threads * @errp: pointer to a NULL-initialized error object * * Create a new block encryption object for an existing @@ -95,6 +95,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 @@ -108,9 +112,12 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, - size_t n_threads, Error **errp); +typedef enum { + QCRYPTO_BLOCK_CREATE_DETACHED = (1 << 0), +} QCryptoBlockCreateFlags; + /** * qcrypto_block_create: * @options: the encryption options @@ -118,6 +125,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 +137,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 +155,7 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, QCryptoBlockInitFunc initfunc, QCryptoBlockWriteFunc writefunc, void *opaque, + unsigned int flags, Error **errp); /** @@ -273,7 +287,7 @@ QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block); * * Returns: the hash algorithm */ -QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block); +QCryptoHashAlgo qcrypto_block_get_kdf_hash(QCryptoBlock *block); /** * qcrypto_block_get_payload_offset: diff --git a/include/crypto/cipher.h b/include/crypto/cipher.h index 083e12a7d9..92939310ef 100644 --- a/include/crypto/cipher.h +++ b/include/crypto/cipher.h @@ -26,7 +26,7 @@ typedef struct QCryptoCipher QCryptoCipher; typedef struct QCryptoCipherDriver QCryptoCipherDriver; -/* See also "QCryptoCipherAlgorithm" and "QCryptoCipherMode" +/* See also "QCryptoCipherAlgo" and "QCryptoCipherMode" * enums defined in qapi/crypto.json */ /** @@ -50,12 +50,12 @@ typedef struct QCryptoCipherDriver QCryptoCipherDriver; * size_t keylen = 16; * uint8_t iv = ....; * - * if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALG_AES_128)) { + * if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128)) { * error_report(errp, "Feature requires AES cipher support"); * return -1; * } * - * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALGO_AES_128, * QCRYPTO_CIPHER_MODE_CBC, * key, keylen, * errp); @@ -78,7 +78,7 @@ typedef struct QCryptoCipherDriver QCryptoCipherDriver; */ struct QCryptoCipher { - QCryptoCipherAlgorithm alg; + QCryptoCipherAlgo alg; QCryptoCipherMode mode; const QCryptoCipherDriver *driver; }; @@ -93,7 +93,7 @@ struct QCryptoCipher { * * Returns: true if the algorithm is supported, false otherwise */ -bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, +bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, QCryptoCipherMode mode); /** @@ -106,7 +106,7 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg, * * Returns: the block size in bytes */ -size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg); +size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgo alg); /** @@ -117,7 +117,7 @@ size_t qcrypto_cipher_get_block_len(QCryptoCipherAlgorithm alg); * * Returns: the key size in bytes */ -size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg); +size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgo alg); /** @@ -130,7 +130,7 @@ size_t qcrypto_cipher_get_key_len(QCryptoCipherAlgorithm alg); * * Returns: the IV size in bytes, or 0 if no IV is permitted */ -size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, +size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgo alg, QCryptoCipherMode mode); @@ -156,7 +156,7 @@ size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg, * * Returns: a new cipher object, or NULL on error */ -QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, +QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, size_t nkey, 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/hash.h b/include/crypto/hash.h index 54d87aa2a1..1868d4a0f7 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -23,7 +24,23 @@ #include "qapi/qapi-types-crypto.h" -/* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ +#define QCRYPTO_HASH_DIGEST_LEN_MD5 16 +#define QCRYPTO_HASH_DIGEST_LEN_SHA1 20 +#define QCRYPTO_HASH_DIGEST_LEN_SHA224 28 +#define QCRYPTO_HASH_DIGEST_LEN_SHA256 32 +#define QCRYPTO_HASH_DIGEST_LEN_SHA384 48 +#define QCRYPTO_HASH_DIGEST_LEN_SHA512 64 +#define QCRYPTO_HASH_DIGEST_LEN_RIPEMD160 20 +#define QCRYPTO_HASH_DIGEST_LEN_SM3 32 + +/* See also "QCryptoHashAlgo" defined in qapi/crypto.json */ + +typedef struct QCryptoHash QCryptoHash; +struct QCryptoHash { + QCryptoHashAlgo alg; + void *opaque; + void *driver; +}; /** * qcrypto_hash_supports: @@ -34,7 +51,7 @@ * * Returns: true if the algorithm is supported, false otherwise */ -gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); +gboolean qcrypto_hash_supports(QCryptoHashAlgo alg); /** @@ -45,7 +62,7 @@ gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); * * Returns: the digest length in bytes */ -size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg); +size_t qcrypto_hash_digest_len(QCryptoHashAlgo alg); /** * qcrypto_hash_bytesv: @@ -57,15 +74,22 @@ size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg); * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory regions - * present in @iov. The @result pointer will be - * filled with raw bytes representing the computed - * hash, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * present in @iov. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: 0 on success, -1 on error */ -int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, +int qcrypto_hash_bytesv(QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, uint8_t **result, @@ -82,15 +106,22 @@ int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, * @errp: pointer to a NULL-initialized error object * * Computes the hash across all the memory region - * @buf of length @len. The @result pointer will be - * filled with raw bytes representing the computed - * hash, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * @buf of length @len. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: 0 on success, -1 on error */ -int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, +int qcrypto_hash_bytes(QCryptoHashAlgo alg, const char *buf, size_t len, uint8_t **result, @@ -114,12 +145,132 @@ int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, * * Returns: 0 on success, -1 on error */ -int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, +int qcrypto_hash_digestv(QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, char **digest, Error **errp); +/** + * qcrypto_hash_updatev: + * @hash: hash object from qcrypto_hash_new + * @iov: the array of memory regions to hash + * @niov: the length of @iov + * @errp: pointer to a NULL-initialized error object + * + * Updates the given hash object with all the memory regions + * present in @iov. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_hash_updatev(QCryptoHash *hash, + const struct iovec *iov, + size_t niov, + Error **errp); +/** + * qcrypto_hash_update: + * @hash: hash object from qcrypto_hash_new + * @buf: the memory region to hash + * @len: the length of @buf + * @errp: pointer to a NULL-initialized error object + * + * Updates the given hash object with the data from + * the given buffer. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_hash_update(QCryptoHash *hash, + const char *buf, + size_t len, + Error **errp); + +/** + * qcrypto_hash_finalize_digest: + * @hash: the hash object to finalize + * @digest: pointer to hold output hash + * @errp: pointer to a NULL-initialized error object + * + * Computes the hash from the given hash object. Hash object + * is expected to have its data updated from the qcrypto_hash_update function. + * The @digest pointer will be filled with the printable hex digest of the + * computed hash, which will be terminated by '\0'. The memory pointer + * in @digest must be released with a call to g_free() when + * no longer required. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_hash_finalize_digest(QCryptoHash *hash, + char **digest, + Error **errp); + +/** + * qcrypto_hash_finalize_base64: + * @hash_ctx: hash object to finalize + * @base64: pointer to store the hash result in + * @errp: pointer to a NULL-initialized error object + * + * Computes the hash from the given hash object. Hash object + * is expected to have it's data updated from the qcrypto_hash_update function. + * The @base64 pointer will be filled with the base64 encoding of the computed + * hash, which will be terminated by '\0'. The memory pointer in @base64 + * must be released with a call to g_free() when no longer required. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_hash_finalize_base64(QCryptoHash *hash, + char **base64, + Error **errp); + +/** + * qcrypto_hash_finalize_bytes: + * @hash_ctx: hash object to finalize + * @result: pointer to store the hash result in + * @result_len: Pointer to store the length of the result in + * @errp: pointer to a NULL-initialized error object + * + * Computes the hash from the given hash object. Hash object + * is expected to have it's data updated from the qcrypto_hash_update function. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_hash_finalize_bytes(QCryptoHash *hash, + uint8_t **result, + size_t *result_len, + Error **errp); + +/** + * qcrypto_hash_new: + * @alg: the hash algorithm + * @errp: pointer to a NULL-initialized error object + * + * Creates a new hashing context for the chosen algorithm for + * usage with qcrypto_hash_update. + * + * Returns: New hash object with the given algorithm, or NULL on error. + */ +QCryptoHash *qcrypto_hash_new(QCryptoHashAlgo alg, Error **errp); + +/** + * qcrypto_hash_free: + * @hash: hash object to free + * + * Frees a hashing context for the chosen algorithm. + */ +void qcrypto_hash_free(QCryptoHash *hash); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHash, qcrypto_hash_free) + /** * qcrypto_hash_digest: * @alg: the hash algorithm @@ -137,7 +288,7 @@ int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, * * Returns: 0 on success, -1 on error */ -int qcrypto_hash_digest(QCryptoHashAlgorithm alg, +int qcrypto_hash_digest(QCryptoHashAlgo alg, const char *buf, size_t len, char **digest, @@ -160,7 +311,7 @@ int qcrypto_hash_digest(QCryptoHashAlgorithm alg, * * Returns: 0 on success, -1 on error */ -int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, +int qcrypto_hash_base64v(QCryptoHashAlgo alg, const struct iovec *iov, size_t niov, char **base64, @@ -183,7 +334,7 @@ int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, * * Returns: 0 on success, -1 on error */ -int qcrypto_hash_base64(QCryptoHashAlgorithm alg, +int qcrypto_hash_base64(QCryptoHashAlgo alg, const char *buf, size_t len, char **base64, diff --git a/include/crypto/hmac.h b/include/crypto/hmac.h index ad4d778416..da8a1e3ceb 100644 --- a/include/crypto/hmac.h +++ b/include/crypto/hmac.h @@ -16,7 +16,7 @@ typedef struct QCryptoHmac QCryptoHmac; struct QCryptoHmac { - QCryptoHashAlgorithm alg; + QCryptoHashAlgo alg; void *opaque; void *driver; }; @@ -31,7 +31,7 @@ struct QCryptoHmac { * Returns: * true if the algorithm is supported, false otherwise */ -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg); +bool qcrypto_hmac_supports(QCryptoHashAlgo alg); /** * qcrypto_hmac_new: @@ -52,7 +52,7 @@ bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg); * Returns: * a new hmac object, or NULL on error */ -QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgorithm alg, +QCryptoHmac *qcrypto_hmac_new(QCryptoHashAlgo alg, const uint8_t *key, size_t nkey, Error **errp); @@ -77,11 +77,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHmac, qcrypto_hmac_free) * @errp: pointer to a NULL-initialized error object * * Computes the hmac across all the memory regions - * present in @iov. The @result pointer will be - * filled with raw bytes representing the computed - * hmac, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * present in @iov. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: * 0 on success, -1 on error @@ -103,11 +110,18 @@ int qcrypto_hmac_bytesv(QCryptoHmac *hmac, * @errp: pointer to a NULL-initialized error object * * Computes the hmac across all the memory region - * @buf of length @len. The @result pointer will be - * filled with raw bytes representing the computed - * hmac, which will have length @resultlen. The - * memory pointer in @result must be released - * with a call to g_free() when no longer required. + * @buf of length @len. + * + * If @result_len is set to a non-zero value by the caller, then + * @result must hold a pointer that is @result_len in size, and + * @result_len match the size of the hash output. The digest will + * be written into @result. + * + * If @result_len is set to zero, then this function will allocate + * a buffer to hold the hash output digest, storing a pointer to + * the buffer in @result, and setting @result_len to its size. + * The memory referenced in @result must be released with a call + * to g_free() when no longer required by the caller. * * Returns: * 0 on success, -1 on error diff --git a/include/crypto/ivgen.h b/include/crypto/ivgen.h index e41521519c..bfa5d28103 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; @@ -44,22 +44,22 @@ * * g_assert((ndata % 512) == 0); * - * QCryptoIVGen *ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_ESSIV, - * QCRYPTO_CIPHER_ALG_AES_128, - * QCRYPTO_HASH_ALG_SHA256, + * QCryptoIVGen *ivgen = qcrypto_ivgen_new(QCRYPTO_IV_GEN_ALGO_ESSIV, + * QCRYPTO_CIPHER_ALGO_AES_128, + * QCRYPTO_HASH_ALGO_SHA256, * key, nkey, errp); * if (!ivgen) { * return -1; * } * - * QCryptoCipher *cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * QCryptoCipher *cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALGO_AES_128, * QCRYPTO_CIPHER_MODE_CBC, * key, nkey, errp); * if (!cipher) { * goto error; * } * - * niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, + * niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALGO_AES_128, * QCRYPTO_CIPHER_MODE_CBC); * iv = g_new0(uint8_t, niv); * @@ -97,7 +97,7 @@ typedef struct QCryptoIVGen QCryptoIVGen; -/* See also QCryptoIVGenAlgorithm enum in qapi/crypto.json */ +/* See also QCryptoIVGenAlgo enum in qapi/crypto.json */ /** @@ -113,19 +113,19 @@ typedef struct QCryptoIVGen QCryptoIVGen; * are required or not depends on the choice of @alg * requested. * - * - QCRYPTO_IVGEN_ALG_PLAIN + * - QCRYPTO_IV_GEN_ALGO_PLAIN * * The IVs are generated by the 32-bit truncated sector * number. This should never be used for block devices * that are larger than 2^32 sectors in size. * All the other parameters are unused. * - * - QCRYPTO_IVGEN_ALG_PLAIN64 + * - QCRYPTO_IV_GEN_ALGO_PLAIN64 * * The IVs are generated by the 64-bit sector number. * All the other parameters are unused. * - * - QCRYPTO_IVGEN_ALG_ESSIV: + * - QCRYPTO_IV_GEN_ALGO_ESSIV: * * The IVs are generated by encrypting the 64-bit sector * number with a hash of an encryption key. The @cipheralg, @@ -133,9 +133,9 @@ typedef struct QCryptoIVGen QCryptoIVGen; * * Returns: a new IV generator, or NULL on error */ -QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, - QCryptoCipherAlgorithm cipheralg, - QCryptoHashAlgorithm hash, +QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgo alg, + QCryptoCipherAlgo cipheralg, + QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, Error **errp); @@ -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. * @@ -167,7 +167,7 @@ int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen, * * Returns: the IV generator algorithm */ -QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen); +QCryptoIVGenAlgo qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen); /** @@ -179,7 +179,7 @@ QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen); * * Returns: the cipher algorithm */ -QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen); +QCryptoCipherAlgo qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen); /** @@ -191,7 +191,7 @@ QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen); * * Returns: the hash algorithm */ -QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen); +QCryptoHashAlgo qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen); /** diff --git a/include/crypto/pbkdf.h b/include/crypto/pbkdf.h index 2c31a44a27..cf59fce610 100644 --- a/include/crypto/pbkdf.h +++ b/include/crypto/pbkdf.h @@ -38,7 +38,7 @@ * .... * * char *password = "a-typical-awful-user-password"; - * size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALG_AES_128); + * size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALGO_AES_128); * uint8_t *salt = g_new0(uint8_t, nkey); * uint8_t *key = g_new0(uint8_t, nkey); * int iterations; @@ -50,7 +50,7 @@ * return -1; * } * - * iterations = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + * iterations = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALGO_SHA256, * (const uint8_t *)password, * strlen(password), * salt, nkey, errp); @@ -60,7 +60,7 @@ * return -1; * } * - * if (qcrypto_pbkdf2(QCRYPTO_HASH_ALG_SHA256, + * if (qcrypto_pbkdf2(QCRYPTO_HASH_ALGO_SHA256, * (const uint8_t *)password, strlen(password), * salt, nkey, iterations, key, nkey, errp) < 0) { * g_free(key); @@ -70,7 +70,7 @@ * * g_free(salt); * - * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, + * cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALGO_AES_128, * QCRYPTO_CIPHER_MODE_ECB, * key, nkey, errp); * g_free(key); @@ -92,7 +92,7 @@ * * Returns true if supported, false otherwise */ -bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash); +bool qcrypto_pbkdf2_supports(QCryptoHashAlgo hash); /** @@ -119,7 +119,7 @@ bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash); * * Returns: 0 on success, -1 on error */ -int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, +int qcrypto_pbkdf2(QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, const uint8_t *salt, size_t nsalt, uint64_t iterations, @@ -147,7 +147,7 @@ int qcrypto_pbkdf2(QCryptoHashAlgorithm hash, * * Returns: number of iterations in 1 second, -1 on error */ -uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash, +uint64_t qcrypto_pbkdf2_count_iters(QCryptoHashAlgo hash, const uint8_t *key, size_t nkey, const uint8_t *salt, size_t nsalt, size_t nout, 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..f694a5c3c5 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -107,6 +107,7 @@ typedef struct QCryptoTLSSession QCryptoTLSSession; +#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 /** * qcrypto_tls_session_new: @@ -177,12 +178,18 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, Error **errp); +/* + * These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O + * would block, but on other errors, must fill 'errp' + */ typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, size_t len, - void *opaque); + void *opaque, + Error **errp); typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, size_t len, - void *opaque); + void *opaque, + Error **errp); /** * qcrypto_tls_session_set_callbacks: @@ -212,6 +219,7 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, * @sess: the TLS session object * @buf: the plain text to send * @len: the length of @buf + * @errp: pointer to hold returned error object * * Encrypt @len bytes of the data in @buf and send * it to the remote peer using the callback previously @@ -221,32 +229,56 @@ void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, * qcrypto_tls_session_get_handshake_status() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * - * Returns: the number of bytes sent, or -1 on error + * Returns: the number of bytes sent, + * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the write would block, + * or -1 on error. */ ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, const char *buf, - size_t len); + size_t len, + Error **errp); /** * qcrypto_tls_session_read: * @sess: the TLS session object * @buf: to fill with plain text received * @len: the length of @buf + * @gracefulTermination: treat premature termination as graceful EOF + * @errp: pointer to hold returned error object * * Receive up to @len bytes of data from the remote peer * using the callback previously registered with * qcrypto_tls_session_set_callbacks(), decrypt it and * store it in @buf. * + * If @gracefulTermination is true, then a premature termination + * of the TLS session will be treated as indicating EOF, as + * opposed to an error. + * * It is an error to call this before * qcrypto_tls_session_get_handshake_status() returns * QCRYPTO_TLS_HANDSHAKE_COMPLETE * - * Returns: the number of bytes received, or -1 on error + * Returns: the number of bytes received, + * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block, + * or -1 on error. */ ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, char *buf, - size_t len); + size_t len, + bool gracefulTermination, + Error **errp); + +/** + * 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: diff --git a/include/crypto/x509-utils.h b/include/crypto/x509-utils.h new file mode 100644 index 0000000000..1e99661a71 --- /dev/null +++ b/include/crypto/x509-utils.h @@ -0,0 +1,22 @@ +/* + * X.509 certificate related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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 QCRYPTO_X509_UTILS_H +#define QCRYPTO_X509_UTILS_H + +#include "crypto/hash.h" + +int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, + QCryptoHashAlgo hash, + uint8_t *result, + size_t *resultlen, + Error **errp); + +#endif diff --git a/include/disas/capstone.h b/include/disas/capstone.h index e29068dd97..c43033f7f6 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -3,6 +3,8 @@ #ifdef CONFIG_CAPSTONE +#define CAPSTONE_AARCH64_COMPAT_HEADER +#define CAPSTONE_SYSTEMZ_COMPAT_HEADER #include #else diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h index 64247ecb11..3b50ecfb54 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 */ @@ -236,19 +232,11 @@ enum bfd_architecture #define bfd_mach_avrxmega5 105 #define bfd_mach_avrxmega6 106 #define bfd_mach_avrxmega7 107 - bfd_arch_cris, /* Axis CRIS */ -#define bfd_mach_cris_v0_v10 255 -#define bfd_mach_cris_v32 32 -#define bfd_mach_cris_v10_v32 1032 bfd_arch_microblaze, /* Xilinx MicroBlaze. */ bfd_arch_moxie, /* The Moxie core. */ bfd_arch_ia64, /* HP/Intel ia64 */ #define bfd_mach_ia64_elf64 64 #define bfd_mach_ia64_elf32 32 - bfd_arch_nios2, /* Nios II */ -#define bfd_mach_nios2 0 -#define bfd_mach_nios2r1 1 -#define bfd_mach_nios2r2 2 bfd_arch_rx, /* Renesas RX */ #define bfd_mach_rx 0x75 #define bfd_mach_rx_v2 0x76 @@ -273,14 +261,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, @@ -323,8 +311,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 @@ -334,7 +322,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 @@ -388,20 +376,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; @@ -448,11 +444,8 @@ int print_insn_w65 (bfd_vma, disassemble_info*); int print_insn_d10v (bfd_vma, disassemble_info*); int print_insn_v850 (bfd_vma, disassemble_info*); int print_insn_tic30 (bfd_vma, disassemble_info*); -int print_insn_crisv32 (bfd_vma, disassemble_info*); -int print_insn_crisv10 (bfd_vma, disassemble_info*); int print_insn_microblaze (bfd_vma, disassemble_info*); int print_insn_ia64 (bfd_vma, disassemble_info*); -int print_insn_nios2(bfd_vma, disassemble_info*); int print_insn_xtensa (bfd_vma, disassemble_info*); int print_insn_riscv32 (bfd_vma, disassemble_info*); int print_insn_riscv64 (bfd_vma, disassemble_info*); @@ -506,8 +499,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..c702b1effc 100644 --- a/include/disas/disas.h +++ b/include/disas/disas.h @@ -1,34 +1,28 @@ #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); +#ifdef CONFIG_TCG +void disas(FILE *out, const void *code, size_t size); +void target_disas(FILE *out, CPUState *cpu, const DisasContextBase *db); +#endif -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); +#ifdef CONFIG_PLUGIN +char *plugin_disas(CPUState *cpu, const DisasContextBase *db, + uint64_t addr, size_t size); +#endif /* 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 8bf1e72720..e7259ec366 100644 --- a/include/elf.h +++ b/include/elf.h @@ -11,25 +11,25 @@ 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) @@ -41,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 */ @@ -129,162 +129,162 @@ 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_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 @@ -297,61 +297,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; @@ -362,72 +362,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. */ @@ -448,48 +448,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. */ @@ -596,25 +596,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 @@ -647,29 +675,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 @@ -693,7 +721,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 @@ -708,78 +736,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 @@ -806,46 +834,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) */ @@ -975,385 +1003,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 @@ -1421,47 +1449,47 @@ typedef struct { #define EF_RISCV_TSO 0x0010 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 @@ -1469,30 +1497,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; @@ -1504,107 +1532,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. */ @@ -1619,56 +1647,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_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 */ +#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. */ @@ -1701,16 +1730,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; @@ -1735,13 +1764,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 @@ -1751,13 +1780,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/abi_ptr.h b/include/exec/abi_ptr.h new file mode 100644 index 0000000000..2aedcceb0c --- /dev/null +++ b/include/exec/abi_ptr.h @@ -0,0 +1,33 @@ +/* + * QEMU abi_ptr type definitions + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef EXEC_ABI_PTR_H +#define EXEC_ABI_PTR_H + +#include "cpu-param.h" + +#if defined(CONFIG_USER_ONLY) +/* + * sparc32plus has 64bit long but 32bit space address + * this can make bad result with g2h() and h2g() + */ +#if TARGET_VIRT_ADDR_SPACE_BITS <= 32 +typedef uint32_t abi_ptr; +#define TARGET_ABI_FMT_ptr "%x" +#else +typedef uint64_t abi_ptr; +#define TARGET_ABI_FMT_ptr "%"PRIx64 +#endif + +#else /* !CONFIG_USER_ONLY */ + +#include "exec/target_long.h" + +typedef target_ulong abi_ptr; +#define TARGET_ABI_FMT_ptr TARGET_FMT_lx + +#endif /* !CONFIG_USER_ONLY */ + +#endif 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/breakpoint.h b/include/exec/breakpoint.h new file mode 100644 index 0000000000..95f0482e6d --- /dev/null +++ b/include/exec/breakpoint.h @@ -0,0 +1,30 @@ +/* + * QEMU breakpoint & watchpoint definitions + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef EXEC_BREAKPOINT_H +#define EXEC_BREAKPOINT_H + +#include "qemu/queue.h" +#include "exec/vaddr.h" +#include "exec/memattrs.h" + +typedef struct CPUBreakpoint { + vaddr pc; + int flags; /* BP_* */ + QTAILQ_ENTRY(CPUBreakpoint) entry; +} CPUBreakpoint; + +typedef struct CPUWatchpoint { + vaddr vaddr; + vaddr len; + vaddr hitaddr; + MemTxAttrs hitattrs; + int flags; /* BP_* */ + QTAILQ_ENTRY(CPUWatchpoint) entry; +} CPUWatchpoint; + +#endif diff --git a/include/exec/confidential-guest-support.h b/include/exec/confidential-guest-support.h index ba2dd4b5df..02dc4e518f 100644 --- a/include/exec/confidential-guest-support.h +++ b/include/exec/confidential-guest-support.h @@ -23,11 +23,19 @@ #include "qom/object.h" #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" -OBJECT_DECLARE_SIMPLE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) +OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, + ConfidentialGuestSupportClass, + CONFIDENTIAL_GUEST_SUPPORT) + struct ConfidentialGuestSupport { Object parent; + /* + * True if the machine should use guest_memfd for RAM. + */ + bool require_guest_memfd; + /* * ready: flag set by CGS initialization code once it's ready to * start executing instructions in a potentially-secure @@ -55,8 +63,37 @@ struct ConfidentialGuestSupport { typedef struct ConfidentialGuestSupportClass { ObjectClass parent; + + int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); + int (*kvm_reset)(ConfidentialGuestSupport *cgs, Error **errp); } ConfidentialGuestSupportClass; +static inline int confidential_guest_kvm_init(ConfidentialGuestSupport *cgs, + Error **errp) +{ + ConfidentialGuestSupportClass *klass; + + klass = CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs); + if (klass->kvm_init) { + return klass->kvm_init(cgs, errp); + } + + return 0; +} + +static inline int confidential_guest_kvm_reset(ConfidentialGuestSupport *cgs, + Error **errp) +{ + ConfidentialGuestSupportClass *klass; + + klass = CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs); + if (klass->kvm_reset) { + return klass->kvm_reset(cgs, errp); + } + + return 0; +} + #endif /* !CONFIG_USER_ONLY */ #endif /* QEMU_CONFIDENTIAL_GUEST_SUPPORT_H */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 2eb1176538..45e6676938 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -19,18 +19,11 @@ #ifndef CPU_ALL_H #define CPU_ALL_H +#include "exec/page-protection.h" #include "exec/cpu-common.h" #include "exec/memory.h" -#include "qemu/thread.h" +#include "exec/tswap.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: * @@ -44,79 +37,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)) -#define bswaptls(s) bswap32s(s) -#else -#define tswapl(s) tswap64(s) -#define tswapls(s) tswap64s((uint64_t *)(s)) -#define bswaptls(s) bswap64s(s) -#endif - /* Target-endianness CPU memory access functions. These fit into the * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. */ @@ -145,13 +65,16 @@ static inline void tswap64s(uint64_t *s) /* MMU memory access macros */ #if defined(CONFIG_USER_ONLY) -#include "exec/user/abitypes.h" +#include "user/abitypes.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. +/* + * 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 uintptr_t guest_base; -extern bool have_guest_base; extern unsigned long reserved_va; /* @@ -171,7 +94,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 @@ -217,57 +140,24 @@ static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val #ifdef TARGET_PAGE_BITS_VARY # include "exec/page-vary.h" extern const TargetPageBits target_page; -#ifdef CONFIG_DEBUG_TCG -#define TARGET_PAGE_BITS ({ assert(target_page.decided); target_page.bits; }) -#define TARGET_PAGE_MASK ({ assert(target_page.decided); \ - (target_long)target_page.mask; }) +# ifdef CONFIG_DEBUG_TCG +# define TARGET_PAGE_BITS ({ assert(target_page.decided); \ + target_page.bits; }) +# define TARGET_PAGE_MASK ({ assert(target_page.decided); \ + (target_long)target_page.mask; }) +# else +# define TARGET_PAGE_BITS target_page.bits +# define TARGET_PAGE_MASK ((target_long)target_page.mask) +# endif +# define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) #else -#define TARGET_PAGE_BITS target_page.bits -#define TARGET_PAGE_MASK ((target_long)target_page.mask) -#endif -#define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) -#else -#define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS -#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) -#define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS) +# define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS +# define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) +# define TARGET_PAGE_MASK ((target_long)-1 << TARGET_PAGE_BITS) #endif #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); @@ -276,9 +166,60 @@ 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); -void page_reset_target_data(target_ulong start, target_ulong end); -int page_check_range(target_ulong start, target_ulong len, int flags); + +/** + * page_set_flags: + * @start: first byte of range + * @last: last byte of range + * @flags: flags to set + * Context: holding mmap lock + * + * 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); + +void page_reset_target_data(target_ulong start, target_ulong last); + +/** + * page_check_range + * @start: first byte of range + * @len: length of range + * @flags: flags required for each page + * + * 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. + */ +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) @@ -357,9 +298,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 /* @@ -370,6 +315,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)) @@ -378,19 +326,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 @@ -399,7 +362,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)); } @@ -410,90 +373,16 @@ 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/translate-all.c */ -void dump_exec_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. */ +#include "cpu.h" +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 6feaa40ca7..638dc806a5 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -1,44 +1,41 @@ +/* + * CPU interfaces that are target independent. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1+ + */ #ifndef CPU_COMMON_H #define CPU_COMMON_H -/* 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" +#include "exec/page-protection.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); unsigned int cpu_list_generation_id_get(void); -void tcg_flush_softmmu_tlb(CPUState *cs); -void tcg_flush_jmp_cache(CPUState *cs); +int cpu_get_free_index(void); void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); @@ -75,6 +72,21 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length); 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); @@ -92,6 +104,7 @@ 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); @@ -118,6 +131,14 @@ size_t qemu_ram_pagesize_largest(void); */ void cpu_address_space_init(CPUState *cpu, int asidx, const char *prefix, MemoryRegion *mr); +/** + * cpu_address_space_destroy: + * @cpu: CPU for which address space needs to be destroyed + * @asidx: integer index of this address space + * + * Note that with KVM only one address space is supported. + */ +void cpu_address_space_destroy(CPUState *cpu, int asidx); void cpu_physical_memory_rw(hwaddr addr, void *buf, hwaddr len, bool is_write); @@ -131,14 +152,11 @@ 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); void cpu_physical_memory_unmap(void *buffer, hwaddr len, bool is_write, hwaddr access_len); -void cpu_register_map_client(QEMUBH *bh); -void cpu_unregister_map_client(QEMUBH *bh); bool cpu_physical_memory_is_io(hwaddr phys_addr); @@ -155,6 +173,8 @@ typedef int (RAMBlockIterFunc)(RAMBlock *rb, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, + size_t length); #endif @@ -163,8 +183,101 @@ 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 + +bool tcg_cflags_has(CPUState *cpu, uint32_t flags); +void tcg_cflags_set(CPUState *cpu, uint32_t flags); + +/* current cflags for hashing/comparison */ +uint32_t curr_cflags(CPUState *cpu); + +/** + * 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); + +/* 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_const(env) + * @env: The architecture environment + * + * Return the CPUState associated with the environment. + */ +static inline const CPUState *env_cpu_const(const 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 (CPUState *)env_cpu_const(env); +} + +#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 21309cf567..0dbef3010c 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -19,7 +19,7 @@ #ifndef CPU_DEFS_H #define CPU_DEFS_H -#ifndef NEED_CPU_H +#ifndef COMPILING_PER_TARGET #error cpu.h included from common code #endif @@ -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 @@ -54,40 +51,10 @@ # error TARGET_PAGE_BITS must be defined in cpu-param.h # endif #endif -#ifndef TARGET_TB_PCREL -# define TARGET_TB_PCREL 0 -#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 @@ -111,161 +78,6 @@ typedef uint64_t target_ulong; # endif # endif -/* Minimalized TLB entry for use by TCG fast path. */ -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 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; - - /* - * 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. - */ -#ifdef TARGET_PAGE_ENTRY_EXTRA - TARGET_PAGE_ENTRY_EXTRA -#endif -} 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. - */ - 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]; - CPUTLBEntryFull vfulltlb[CPU_VTLB_SIZE]; - CPUTLBEntryFull *fulltlb; -} 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 a32129cea8..b8b5752604 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -1,5 +1,5 @@ /* - * Software MMU support + * Software MMU support (per-target) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -62,21 +62,18 @@ #ifndef CPU_LDST_H #define CPU_LDST_H +#ifndef CONFIG_TCG +#error Can only include this header with TCG +#endif + #include "exec/memopidx.h" +#include "exec/abi_ptr.h" +#include "exec/mmu-access-type.h" #include "qemu/int128.h" -#include "cpu.h" #if defined(CONFIG_USER_ONLY) -/* sparc32plus has 64bit long but 32bit space address - * this can make bad result with g2h() and h2g() - */ -#if TARGET_VIRT_ADDR_SPACE_BITS <= 32 -typedef uint32_t abi_ptr; -#define TARGET_ABI_FMT_ptr "%x" -#else -typedef uint64_t abi_ptr; -#define TARGET_ABI_FMT_ptr "%"PRIx64 -#endif + +#include "user/guest-base.h" #ifndef TARGET_TAGGED_ADDRESSES static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) @@ -120,10 +117,8 @@ static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) assert(h2g_valid(x)); \ h2g_nocheck(x); \ }) -#else -typedef target_ulong abi_ptr; -#define TARGET_ABI_FMT_ptr TARGET_FMT_lx -#endif + +#endif /* CONFIG_USER_ONLY */ uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr); @@ -207,59 +202,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,78 +288,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; - -static inline void set_helper_retaddr(uintptr_t ra) -{ - helper_retaddr = ra; - /* - * Ensure that this write is visible to the SIGSEGV handler that - * may be invoked due to a subsequent invalid memory operation. - */ - signal_barrier(); -} - -static inline void clear_helper_retaddr(void) -{ - /* - * Ensure that previous memory operations have succeeded before - * removing the data visible to the signal handler. - */ - signal_barrier(); - helper_retaddr = 0; -} - -#else - -/* Needed for TCG_OVERSIZED_GUEST */ -#include "tcg/tcg.h" - -static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry) -{ -#if TCG_OVERSIZED_GUEST - return entry->addr_write; -#else - return qatomic_read(&entry->addr_write); -#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) -{ - uintptr_t size_mask = env_tlb(env)->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) -{ - return &env_tlb(env)->f[mmu_idx].table[tlb_index(env, mmu_idx, addr)]; -} - -#endif /* defined(CONFIG_USER_ONLY) */ - #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data # define cpu_ldsw_data cpu_ldsw_be_data @@ -390,9 +308,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 +317,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 +330,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,27 +339,23 @@ 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); uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr); void cpu_ld_code(CPUArchState *env, abi_ptr addr, size_t len, uint8_t *out); -static inline int cpu_ldsb_code(CPUArchState *env, abi_ptr addr) -{ - return (int8_t)cpu_ldub_code(env, addr); -} - -static inline int cpu_ldsw_code(CPUArchState *env, abi_ptr addr) -{ - return (int16_t)cpu_lduw_code(env, addr); -} - /** * tlb_vaddr_to_host: * @env: CPUArchState @@ -475,4 +380,38 @@ void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, MMUAccessType access_type, int mmu_idx); #endif +/* + * For user-only, helpers that use guest to host address translation + * must protect the actual host memory access by recording 'retaddr' + * for the signal handler. This is required for a race condition in + * which another thread unmaps the page between a probe and the + * actual access. + */ +#ifdef CONFIG_USER_ONLY +extern __thread uintptr_t helper_retaddr; + +static inline void set_helper_retaddr(uintptr_t ra) +{ + helper_retaddr = ra; + /* + * Ensure that this write is visible to the SIGSEGV handler that + * may be invoked due to a subsequent invalid memory operation. + */ + signal_barrier(); +} + +static inline void clear_helper_retaddr(void) +{ + /* + * Ensure that previous memory operations have succeeded before + * removing the data visible to the signal handler. + */ + signal_barrier(); + helper_retaddr = 0; +} +#else +#define set_helper_retaddr(ra) do { } while (0) +#define clear_helper_retaddr() do { } while (0) +#endif + #endif /* CPU_LDST_H */ diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 19b16e58f8..ef18642a32 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -22,10 +22,14 @@ #include "exec/cpu-common.h" +#ifdef CONFIG_TCG + #if !defined(CONFIG_USER_ONLY) /* 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 /* CONFIG_TCG */ + #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 1071c25458..36b224688e 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -21,53 +21,13 @@ #define EXEC_ALL_H #include "cpu.h" -#ifdef CONFIG_TCG +#if defined(CONFIG_USER_ONLY) +#include "exec/abi_ptr.h" #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 - -/** - * 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(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/mmu-access-type.h" +#include "exec/translation-block.h" +#include "qemu/clang-tsa.h" /** * cpu_loop_exit_requested: @@ -83,7 +43,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) @@ -106,28 +66,19 @@ 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); -/** - * tlb_flush_page_all_cpus: - * @cpu: src CPU of the flush - * @addr: virtual address of page to be flushed - * - * 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(CPUState *cpu, vaddr addr); /** * tlb_flush_page_all_cpus_synced: * @cpu: src CPU of the flush * @addr: virtual address of page to be flushed * - * Flush one page from the TLB of the specified CPU, for all MMU - * indexes like tlb_flush_page_all_cpus except the source vCPUs work - * is scheduled as safe work meaning all flushes will be complete once - * the source vCPUs safe work is complete. This will depend on when - * the guests translation ends the TB. + * Flush one page from the TLB of all CPUs, for all + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ -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 @@ -138,19 +89,14 @@ void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr); * use one of the other functions for efficiency. */ void tlb_flush(CPUState *cpu); -/** - * tlb_flush_all_cpus: - * @cpu: src CPU of the flush - */ -void tlb_flush_all_cpus(CPUState *src_cpu); /** * tlb_flush_all_cpus_synced: * @cpu: src CPU of the flush * - * Like tlb_flush_all_cpus except this except the source vCPUs work is - * scheduled as safe work meaning all flushes will be complete once - * the source vCPUs safe work is complete. This will depend on when - * the guests translation ends the TB. + * Flush the entire TLB for all CPUs, for all MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_all_cpus_synced(CPUState *src_cpu); /** @@ -162,32 +108,21 @@ 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: - * @cpu: Originating CPU of the flush - * @addr: virtual address of page to be flushed - * @idxmap: bitmap of MMU indexes to flush - * - * 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, - uint16_t idxmap); /** * tlb_flush_page_by_mmuidx_all_cpus_synced: * @cpu: Originating CPU of the flush * @addr: virtual address of page to be flushed * @idxmap: bitmap of MMU indexes to flush * - * Flush one page from the TLB of all CPUs, for the specified MMU - * indexes like tlb_flush_page_by_mmuidx_all_cpus except the source - * vCPUs work is scheduled as safe work meaning all flushes will be - * complete once the source vCPUs safe work is complete. This will - * depend on when the guests translation ends the TB. + * Flush one page from the TLB of all CPUs, for the specified + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ -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: @@ -199,25 +134,16 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, target_ulong addr, * MMU indexes. */ void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap); -/** - * tlb_flush_by_mmuidx_all_cpus: - * @cpu: Originating CPU of the flush - * @idxmap: bitmap of MMU indexes to flush - * - * Flush all entries from all TLBs of all CPUs, for the specified - * MMU indexes. - */ -void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap); /** * tlb_flush_by_mmuidx_all_cpus_synced: * @cpu: Originating CPU of the flush * @idxmap: bitmap of MMU indexes to flush * - * Flush all entries from all TLBs of all CPUs, for the specified - * MMU indexes like tlb_flush_by_mmuidx_all_cpus except except the source - * vCPUs work is scheduled as safe work meaning all flushes will be - * complete once the source vCPUs safe work is complete. This will - * depend on when the guests translation ends the TB. + * Flush all entries from the TLB of all CPUs, for the specified + * MMU indexes. + * + * When this function returns, no CPUs will subsequently perform + * translations using the flushed TLBs. */ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap); @@ -230,14 +156,12 @@ 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, - 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 @@ -250,17 +174,14 @@ 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, - 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); @@ -268,7 +189,7 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, * tlb_set_page_full: * @cpu: CPU context * @mmu_idx: mmu index of the tlb to modify - * @vaddr: virtual address of the entry to add + * @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 @@ -283,13 +204,13 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *cpu, * 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, target_ulong vaddr, +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) @@ -297,7 +218,7 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, target_ulong vaddr, * @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 @@ -308,18 +229,18 @@ void tlb_set_page_full(CPUState *cpu, int mmu_idx, target_ulong vaddr, * 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) { @@ -327,88 +248,62 @@ 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_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) { } -static inline void tlb_flush_all_cpus(CPUState *src_cpu) -{ -} 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) { } 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, - uint16_t idxmap) -{ -} static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, - target_ulong addr, + vaddr addr, uint16_t idxmap) { } -static inline void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap) -{ -} - static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } 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, - 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, - 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) { } #endif + +#if defined(CONFIG_TCG) + /** * probe_access: * @env: CPUArchState @@ -426,16 +321,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); @@ -445,6 +340,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 @@ -459,11 +355,12 @@ 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); #ifndef CONFIG_USER_ONLY + /** * probe_access_full: * Like probe_access_flags, except also return into @pfull. @@ -471,194 +368,95 @@ int probe_access_flags(CPUArchState *env, target_ulong addr, * 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. + * + * This function will not fault if @nonfault is set, but will + * return TLB_INVALID_MASK if the page is not mapped, or is not + * accessible with @access_type. + * + * This function will return TLB_MMIO in order to force the access + * to be handled out-of-line if plugins wish to instrument the access. */ -int probe_access_full(CPUArchState *env, target_ulong addr, +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); -#endif -#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ - -/* 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. +/** + * probe_access_full_mmu: + * Like probe_access_full, except: + * + * This function is intended to be used for page table accesses by + * the target mmu itself. Since such page walking happens while + * handling another potential mmu fault, this function never raises + * exceptions (akin to @nonfault true for probe_access_full). + * Likewise this function does not trigger plugin instrumentation. */ -struct tb_tc { - const void *ptr; /* pointer to the translated code */ - size_t size; -}; +int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + void **phost, CPUTLBEntryFull **pfull); -struct TranslationBlock { -#if !TARGET_TB_PCREL - /* - * 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 TARGET_TB_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. - */ - target_ulong pc; -#endif - - /* - * 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). - */ - target_ulong 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_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; - uint64_t ihash; - - 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 read to avoid ifdefs for TARGET_TB_PCREL. */ -static inline target_ulong tb_pc(const TranslationBlock *tb) -{ -#if TARGET_TB_PCREL - qemu_build_not_reached(); -#else - return tb->pc; -#endif -} - -/* 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 /* !CONFIG_USER_ONLY */ +#endif /* CONFIG_TCG */ static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb) { +#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); -#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); -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); +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); -TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, +TranslationBlock *tb_htable_lookup(CPUState *cpu, uint64_t pc, + uint64_t cs_base, uint32_t flags, uint32_t cflags); -TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, target_ulong pc, - target_ulong cs_base, uint32_t flags, +TranslationBlock *inv_tb_htable_lookup(CPUState *cpu, uint64_t pc, + uint64_t cs_base, uint32_t flags, uint32_t cflags); /* GETPC is the true target of the return instruction that we'll execute. */ @@ -708,7 +506,7 @@ struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, * * Note: this function can trigger an exception. */ -tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr, +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, void **hostp); /** @@ -723,16 +521,25 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong 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 get_page_addr_code_hostp(env, addr, NULL); } #if defined(CONFIG_USER_ONLY) -void mmap_lock(void); -void mmap_unlock(void); +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) +{ + 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. @@ -787,9 +594,10 @@ 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) {} +#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_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 f667014888..d73f424f56 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -1,220 +1,50 @@ #ifndef GDBSTUB_H #define GDBSTUB_H -#define DEFAULT_GDBSTUB_PORT "1234" +typedef struct GDBFeature { + const char *xmlname; + const char *xml; + const char *name; + const char * const *regs; + int num_regs; +} GDBFeature; -/* GDB breakpoint/watchpoint types */ -#define GDB_BREAKPOINT_SW 0 -#define GDB_BREAKPOINT_HW 1 -#define GDB_WATCHPOINT_WRITE 2 -#define GDB_WATCHPOINT_READ 3 -#define GDB_WATCHPOINT_ACCESS 4 +typedef struct GDBFeatureBuilder { + GDBFeature *feature; + GPtrArray *xml; + GPtrArray *regs; + int base_reg; +} GDBFeatureBuilder; -/* 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; - -#ifdef NEED_CPU_H -#include "cpu.h" - -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, ...); -/** - * 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; -} + const GDBFeature *feature, int g_pos); /** - * 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. + * gdb_unregister_coprocessor_all() - unregisters supplemental set of registers + * @cpu - the CPU associated with registers */ -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 */ +void gdb_unregister_coprocessor_all(CPUState *cpu); /** * gdbserver_start: start the gdb server @@ -227,26 +57,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.py */ +extern const GDBFeature gdb_static_features[]; -/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ -extern const char *const xml_builtin[][2]; - -#endif +#endif /* GDBSTUB_H */ 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..834590dc4e --- /dev/null +++ b/include/exec/helper-gen-common.h @@ -0,0 +1,14 @@ +/* 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 + +#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..dabe138e20 --- /dev/null +++ b/include/exec/helper-gen.h.inc @@ -0,0 +1,110 @@ +/* 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.inc" + +#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).func, \ + &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).func, \ + &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).func, \ + &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).func, \ + &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).func, \ + &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).func, \ + &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).func, \ + &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).func, \ + &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 deleted file mode 100644 index e242fed46e..0000000000 --- a/include/exec/helper-head.h +++ /dev/null @@ -1,138 +0,0 @@ -/* 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. - */ - -#ifndef EXEC_HELPER_HEAD_H -#define EXEC_HELPER_HEAD_H - -#define HELPER(name) glue(helper_, name) - -/* Some types that make sense in C, but not for TCG. */ -#define dh_alias_i32 i32 -#define dh_alias_s32 i32 -#define dh_alias_int i32 -#define dh_alias_i64 i64 -#define dh_alias_s64 i64 -#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_void void -#define dh_alias_noreturn noreturn -#define dh_alias(t) glue(dh_alias_, t) - -#define dh_ctype_i32 uint32_t -#define dh_ctype_s32 int32_t -#define dh_ctype_int int -#define dh_ctype_i64 uint64_t -#define dh_ctype_s64 int64_t -#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_void void -#define dh_ctype_noreturn G_NORETURN void -#define dh_ctype(t) dh_ctype_##t - -#ifdef NEED_CPU_H -# ifdef TARGET_LONG_BITS -# if TARGET_LONG_BITS == 32 -# define dh_alias_tl i32 -# define dh_typecode_tl dh_typecode_i32 -# else -# define dh_alias_tl i64 -# define dh_typecode_tl dh_typecode_i64 -# endif -# endif -# define dh_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 - recursive expansion rules. */ -#define dh_retvar_decl0_void void -#define dh_retvar_decl0_noreturn void -#define dh_retvar_decl0_i32 TCGv_i32 retval -#define dh_retvar_decl0_i64 TCGv_i64 retval -#define dh_retvar_decl0_ptr TCGv_ptr retval -#define dh_retvar_decl0(t) glue(dh_retvar_decl0_, dh_alias(t)) - -#define dh_retvar_decl_void -#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_ptr TCGv_ptr retval, -#define dh_retvar_decl(t) glue(dh_retvar_decl_, dh_alias(t)) - -#define dh_retvar_void NULL -#define dh_retvar_noreturn NULL -#define dh_retvar_i32 tcgv_i32_temp(retval) -#define dh_retvar_i64 tcgv_i64_temp(retval) -#define dh_retvar_ptr tcgv_ptr_temp(retval) -#define dh_retvar(t) glue(dh_retvar_, dh_alias(t)) - -#define dh_typecode_void 0 -#define dh_typecode_noreturn 0 -#define dh_typecode_i32 2 -#define dh_typecode_s32 3 -#define dh_typecode_i64 4 -#define dh_typecode_s64 5 -#define dh_typecode_ptr 6 -#define dh_typecode_int dh_typecode_s32 -#define dh_typecode_f16 dh_typecode_i32 -#define dh_typecode_f32 dh_typecode_i32 -#define dh_typecode_f64 dh_typecode_i64 -#define dh_typecode_cptr dh_typecode_ptr -#define dh_typecode(t) dh_typecode_##t - -#define dh_callflag_i32 0 -#define dh_callflag_i64 0 -#define dh_callflag_ptr 0 -#define dh_callflag_void 0 -#define dh_callflag_noreturn TCG_CALL_NO_RETURN -#define dh_callflag(t) glue(dh_callflag_, dh_alias(t)) - -#define dh_typemask(t, n) (dh_typecode(t) << (n * 3)) - -#define dh_arg(t, n) \ - glue(glue(tcgv_, dh_alias(t)), _temp)(glue(arg, n)) - -#define dh_arg_decl(t, n) glue(TCGv_, dh_alias(t)) glue(arg, n) - -#define DEF_HELPER_0(name, ret) \ - DEF_HELPER_FLAGS_0(name, 0, ret) -#define DEF_HELPER_1(name, ret, t1) \ - DEF_HELPER_FLAGS_1(name, 0, ret, t1) -#define DEF_HELPER_2(name, ret, t1, t2) \ - DEF_HELPER_FLAGS_2(name, 0, ret, t1, t2) -#define DEF_HELPER_3(name, ret, t1, t2, t3) \ - DEF_HELPER_FLAGS_3(name, 0, ret, t1, t2, t3) -#define DEF_HELPER_4(name, ret, t1, t2, t3, t4) \ - DEF_HELPER_FLAGS_4(name, 0, ret, t1, t2, t3, t4) -#define DEF_HELPER_5(name, ret, t1, t2, t3, t4, t5) \ - DEF_HELPER_FLAGS_5(name, 0, ret, t1, t2, t3, t4, t5) -#define DEF_HELPER_6(name, ret, t1, t2, t3, t4, t5, t6) \ - DEF_HELPER_FLAGS_6(name, 0, ret, t1, t2, t3, t4, t5, t6) -#define DEF_HELPER_7(name, ret, t1, t2, t3, t4, t5, t6, t7) \ - DEF_HELPER_FLAGS_7(name, 0, ret, t1, t2, t3, t4, t5, t6, t7) - -/* MAX_OPC_PARAM_IARGS must be set to n if last entry is DEF_HELPER_FLAGS_n. */ - -#endif /* EXEC_HELPER_HEAD_H */ diff --git a/include/exec/helper-head.h.inc b/include/exec/helper-head.h.inc new file mode 100644 index 0000000000..5ef467a79d --- /dev/null +++ b/include/exec/helper-head.h.inc @@ -0,0 +1,135 @@ +/* + * 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. */ +#define dh_alias_i32 i32 +#define dh_alias_s32 i32 +#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) + +#define dh_ctype_i32 uint32_t +#define dh_ctype_s32 int32_t +#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 + +#ifdef COMPILING_PER_TARGET +# ifdef TARGET_LONG_BITS +# if TARGET_LONG_BITS == 32 +# define dh_alias_tl i32 +# define dh_typecode_tl dh_typecode_i32 +# else +# define dh_alias_tl i64 +# define dh_typecode_tl dh_typecode_i64 +# endif +# endif +# define dh_ctype_tl target_ulong +#endif /* COMPILING_PER_TARGET */ + +/* We can't use glue() here because it falls foul of C preprocessor + recursive expansion rules. */ +#define dh_retvar_decl0_void void +#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)) + +#define dh_retvar_decl_void +#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)) + +#define dh_retvar_void NULL +#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)) + +#define dh_typecode_void 0 +#define dh_typecode_noreturn 0 +#define dh_typecode_i32 2 +#define dh_typecode_s32 3 +#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 +#define dh_callflag(t) glue(dh_callflag_, dh_alias(t)) + +#define dh_typemask(t, n) (dh_typecode(t) << (n * 3)) + +#define dh_arg(t, n) \ + glue(glue(tcgv_, dh_alias(t)), _temp)(glue(arg, n)) + +#define dh_arg_decl(t, n) glue(TCGv_, dh_alias(t)) glue(arg, n) + +#define DEF_HELPER_0(name, ret) \ + DEF_HELPER_FLAGS_0(name, 0, ret) +#define DEF_HELPER_1(name, ret, t1) \ + DEF_HELPER_FLAGS_1(name, 0, ret, t1) +#define DEF_HELPER_2(name, ret, t1, t2) \ + DEF_HELPER_FLAGS_2(name, 0, ret, t1, t2) +#define DEF_HELPER_3(name, ret, t1, t2, t3) \ + DEF_HELPER_FLAGS_3(name, 0, ret, t1, t2, t3) +#define DEF_HELPER_4(name, ret, t1, t2, t3, t4) \ + DEF_HELPER_FLAGS_4(name, 0, ret, t1, t2, t3, t4) +#define DEF_HELPER_5(name, ret, t1, t2, t3, t4, t5) \ + DEF_HELPER_FLAGS_5(name, 0, ret, t1, t2, t3, t4, t5) +#define DEF_HELPER_6(name, ret, t1, t2, t3, t4, t5, t6) \ + DEF_HELPER_FLAGS_6(name, 0, ret, t1, t2, t3, t4, t5, t6) +#define DEF_HELPER_7(name, ret, t1, t2, t3, t4, t5, t6, t7) \ + DEF_HELPER_FLAGS_7(name, 0, ret, t1, t2, t3, t4, t5, t6, t7) + +/* MAX_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..c551736d49 --- /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.inc" + +/* + * 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..16782ef46c --- /dev/null +++ b/include/exec/helper-proto-common.h @@ -0,0 +1,16 @@ +/* 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 + +#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..f8e57e43ce --- /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.inc" + +/* + * 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..e27c18f3dc 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,11 @@ 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. + * PID (PCI PASID) support: Limited to 8 bits process identifier. */ - unsigned int target_tlb_bit0 : 1; - unsigned int target_tlb_bit1 : 1; - unsigned int target_tlb_bit2 : 1; + unsigned int pid:8; } 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..acdb40a9b3 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -35,7 +35,7 @@ typedef enum MemOp { MO_LE = 0, MO_BE = MO_BSWAP, #endif -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #if TARGET_BIG_ENDIAN MO_TE = MO_BE, #else @@ -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, @@ -107,7 +135,7 @@ typedef enum MemOp { MO_BESL = MO_BE | MO_SL, MO_BESQ = MO_BE | MO_SQ, -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET MO_TEUW = MO_TE | MO_UW, MO_TEUL = MO_TE | MO_UL, MO_TEUQ = MO_TE | MO_UQ, @@ -133,13 +161,54 @@ static inline MemOp size_memop(unsigned size) /* Power of 2 up to 8. */ assert((size & (size - 1)) == 0 && size >= 1 && size <= 8); #endif - return ctz32(size); + return (MemOp)ctz32(size); } -/* Big endianness from MemOp. */ -static inline bool memop_big_endian(MemOp op) +/** + * memop_alignment_bits: + * @memop: MemOp value + * + * Extract the alignment size from the memop. + */ +static inline unsigned memop_alignment_bits(MemOp memop) { - return (op & MO_BSWAP) == MO_BE; + unsigned a = memop & MO_AMASK; + + if (a == MO_UNALN) { + /* No alignment required. */ + a = 0; + } else if (a == MO_ALIGN) { + /* A natural alignment requirement. */ + a = memop & MO_SIZE; + } else { + /* A specific alignment requirement. */ + a = a >> MO_ASHIFT; + } + return a; +} + +/* + * memop_atomicity_bits: + * @memop: MemOp value + * + * Extract the atomicity size from the memop. + */ +static inline unsigned memop_atomicity_bits(MemOp memop) +{ + unsigned size = memop & MO_SIZE; + + switch (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; + } + return size; } #endif 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 ddb9b9e10b..42d83f24ef 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, @@ -79,8 +80,7 @@ extern unsigned int global_dirty_tracking; typedef struct MemoryRegionOps MemoryRegionOps; struct ReservedRegion { - hwaddr low; - hwaddr high; + Range range; unsigned type; }; @@ -95,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; @@ -104,6 +105,7 @@ struct MemoryRegionSection { hwaddr offset_within_address_space; bool readonly; bool nonvolatile; + bool unmergeable; }; typedef struct IOMMUTLBEntry IOMMUTLBEntry; @@ -129,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, @@ -206,6 +234,18 @@ 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) + +/* RAM can be private that has kvm guest memfd backend */ +#define RAM_GUEST_MEMFD (1 << 12) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -464,32 +504,6 @@ struct IOMMUMemoryRegionClass { * @iommu: the IOMMUMemoryRegion */ int (*num_indexes)(IOMMUMemoryRegion *iommu); - - /** - * @iommu_set_page_size_mask: - * - * Restrict the page size mask that can be supported with a given IOMMU - * memory region. Used for example to propagate host physical IOMMU page - * size mask limitations to the virtual IOMMU. - * - * Optional method: if this method is not provided, then the default global - * page mask is used. - * - * @iommu: the IOMMUMemoryRegion - * - * @page_size_mask: a bitmask of supported page sizes. At least one bit, - * representing the smallest page size, must be set. Additional set bits - * represent supported block sizes. For example a host physical IOMMU that - * uses page tables with a page size of 4kB, and supports 2MB and 4GB - * blocks, will set mask 0x40201000. A granule of 4kB with indiscriminate - * block sizes is specified with mask 0xfffffffffffff000. - * - * Returns 0 on success, or a negative error. In case of failure, the error - * object must be created. - */ - int (*iommu_set_page_size_mask)(IOMMUMemoryRegion *iommu, - uint64_t page_size_mask, - Error **errp); }; typedef struct RamDiscardListener RamDiscardListener; @@ -564,8 +578,9 @@ typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque); * 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 @@ -713,9 +728,22 @@ void ram_discard_manager_register_listener(RamDiscardManager *rdm, void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl); +/** + * memory_get_xlat_addr: Extract addresses from a TLB entry + * + * @iotlb: pointer to an #IOMMUTLBEntry + * @vaddr: virtual address + * @ram_addr: RAM address + * @read_only: indicates if writes are allowed + * @mr_has_discard_manager: indicates memory is controlled by a + * RamDiscardManager + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. + */ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, ram_addr_t *ram_addr, bool *read_only, - bool *mr_has_discard_manager); + bool *mr_has_discard_manager, Error **errp); typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; @@ -737,10 +765,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; @@ -765,6 +796,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 { @@ -777,6 +811,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 * @@ -861,7 +899,7 @@ struct MemoryListener { * the current transaction. */ void (*log_start)(MemoryListener *listener, MemoryRegionSection *section, - int old, int new); + int old_val, int new_val); /** * @log_stop: @@ -880,7 +918,7 @@ struct MemoryListener { * the current transaction. */ void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section, - int old, int new); + int old_val, int new_val); /** * @log_sync: @@ -903,8 +941,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: @@ -927,8 +968,11 @@ struct MemoryListener { * active at that time. * * @listener: The #MemoryListener. + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ - void (*log_global_start)(MemoryListener *listener); + bool (*log_global_start)(MemoryListener *listener, Error **errp); /** * @log_global_stop: @@ -1035,6 +1079,13 @@ struct MemoryListener { QTAILQ_ENTRY(MemoryListener) link_as; }; +typedef struct AddressSpaceMapClient { + QEMUBH *bh; + QLIST_ENTRY(AddressSpaceMapClient) link; +} AddressSpaceMapClient; + +#define DEFAULT_MAX_BOUNCE_BUFFER_SIZE (4096) + /** * struct AddressSpace: describes a mapping of addresses to #MemoryRegion objects */ @@ -1048,9 +1099,25 @@ 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; + + /* + * Maximum DMA bounce buffer size used for indirect memory map requests. + * This limits the total size of bounce buffer allocations made for + * DMA requests to indirect memory regions within this AddressSpace. DMA + * requests that exceed the limit (e.g. due to overly large requested size + * or concurrent DMA requests having claimed too much buffer space) will be + * rejected and left to the caller to handle. + */ + size_t max_bounce_buffer_size; + /* Total size of bounce buffers currently allocated, atomically accessed */ + size_t bounce_buffer_size; + /* List of callbacks to invoke when buffers free up */ + QemuMutex map_client_list_lock; + QLIST_HEAD(, AddressSpaceMapClient) map_client_list; }; typedef struct AddressSpaceDispatch AddressSpaceDispatch; @@ -1222,8 +1289,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, @@ -1239,13 +1308,16 @@ void memory_region_init_ram_nomigrate(MemoryRegion *mr, * @name: Region name, becomes part of RAMBlock name used in migration stream * must be unique within any device * @size: size of the region. - * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_NORESERVE. + * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_NORESERVE, + * RAM_GUEST_MEMFD. * @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_flags_nomigrate(MemoryRegion *mr, +bool memory_region_init_ram_flags_nomigrate(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, @@ -1272,8 +1344,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, @@ -1296,22 +1370,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, RAM_GUEST_MEMFD * @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); /** @@ -1323,15 +1400,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, RAM_GUEST_MEMFD * @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, @@ -1426,8 +1506,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, @@ -1449,8 +1531,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, @@ -1508,13 +1592,21 @@ 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, Error **errp); +bool memory_region_init_ram_guest_memfd(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + Error **errp); + /** * memory_region_init_rom: Initialize a ROM memory region. * @@ -1535,8 +1627,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, @@ -1566,8 +1660,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, @@ -1634,6 +1730,16 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) */ bool memory_region_is_protected(MemoryRegion *mr); +/** + * memory_region_has_guest_memfd: check whether a memory region has guest_memfd + * associated + * + * Returns %true if a memory region's ram_block has valid guest_memfd assigned. + * + * @mr: the memory region being queried + */ +bool memory_region_has_guest_memfd(MemoryRegion *mr); + /** * memory_region_get_iommu: check whether a memory region is an iommu * @@ -1694,7 +1800,7 @@ uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr); */ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, int iommu_idx, - IOMMUTLBEvent event); + const IOMMUTLBEvent event); /** * memory_region_notify_iommu_one: notify a change in an IOMMU translation @@ -1709,7 +1815,17 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, * range. */ void memory_region_notify_iommu_one(IOMMUNotifier *notifier, - IOMMUTLBEvent *event); + const 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 @@ -1745,7 +1861,7 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); * memory_region_unregister_iommu_notifier: unregister a notifier for * changes to IOMMU translation entries. * - * @mr: the memory region which was observed and for which notity_stopped() + * @mr: the memory region which was observed and for which notify_stopped() * needs to be called * @n: the notifier to be removed. */ @@ -1786,18 +1902,6 @@ int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, */ int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); -/** - * memory_region_iommu_set_page_size_mask: set the supported page - * sizes for a given IOMMU memory region - * - * @iommu_mr: IOMMU memory region - * @page_size_mask: supported page size mask - * @errp: pointer to Error*, to store an error if it happens. - */ -int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, - uint64_t page_size_mask, - Error **errp); - /** * memory_region_name: get a memory region's name * @@ -1872,7 +1976,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. * @@ -1889,7 +1993,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. * @@ -2315,6 +2419,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 @@ -2406,8 +2529,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 @@ -2455,8 +2580,11 @@ void memory_listener_unregister(MemoryListener *listener); * memory_global_dirty_log_start: begin dirty logging for all regions * * @flags: purpose of starting dirty log, migration or dirty rate + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: true on success, else false setting @errp with error. */ -void memory_global_dirty_log_start(unsigned int flags); +bool memory_global_dirty_log_start(unsigned int flags, Error **errp); /** * memory_global_dirty_log_stop: end dirty logging for all regions @@ -2467,6 +2595,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. @@ -2622,7 +2754,7 @@ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, #include "exec/memory_ldst_phys.h.inc" struct MemoryRegionCache { - void *ptr; + uint8_t *ptr; hwaddr xlat; hwaddr len; FlatView *fv; @@ -2630,9 +2762,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 * @@ -2721,6 +2850,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 * @@ -2798,8 +2942,8 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, hwaddr len, * May return %NULL and set *@plen to zero(0), if resources needed to perform * the mapping are exhausted. * Use only for reads OR writes - not for read-modify-write operations. - * Use cpu_register_map_client() to know when retrying the map operation is - * likely to succeed. + * Use address_space_register_map_client() to know when retrying the map + * operation is likely to succeed. * * @as: #AddressSpace to be accessed * @addr: address within that address space @@ -2824,6 +2968,28 @@ void *address_space_map(AddressSpace *as, hwaddr addr, void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, bool is_write, hwaddr access_len); +/* + * address_space_register_map_client: Register a callback to invoke when + * resources for address_space_map() are available again. + * + * address_space_map may fail when there are not enough resources available, + * such as when bounce buffer memory would exceed the limit. The callback can + * be used to retry the address_space_map operation. Note that the callback + * gets automatically removed after firing. + * + * @as: #AddressSpace to be accessed + * @bh: callback to invoke when address_space_map() retry is appropriate + */ +void address_space_register_map_client(AddressSpace *as, QEMUBH *bh); + +/* + * address_space_unregister_map_client: Unregister a callback that has + * previously been registered and not fired yet. + * + * @as: #AddressSpace to be accessed + * @bh: callback to unregister + */ +void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh); /* Internal functions, part of the implementation of address_space_read. */ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, @@ -2959,7 +3125,7 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, MemTxResult address_space_set(AddressSpace *as, hwaddr addr, uint8_t c, hwaddr len, MemTxAttrs attrs); -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET /* enum device_endian to MemOp. */ static inline MemOp devend_memop(enum device_endian end) { @@ -2977,7 +3143,7 @@ static inline MemOp devend_memop(enum device_endian end) return (end == non_host_endianness) ? MO_BSWAP : 0; #endif } -#endif +#endif /* COMPILING_PER_TARGET */ /* * Inhibit technologies that require discarding of pages in RAM blocks, e.g., @@ -3018,7 +3184,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/mmu-access-type.h b/include/exec/mmu-access-type.h new file mode 100644 index 0000000000..28bbb05b94 --- /dev/null +++ b/include/exec/mmu-access-type.h @@ -0,0 +1,18 @@ +/* + * QEMU MMU Access type definitions + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef EXEC_MMU_ACCESS_TYPE_H +#define EXEC_MMU_ACCESS_TYPE_H + +typedef enum MMUAccessType { + MMU_DATA_LOAD = 0, + MMU_DATA_STORE = 1, + MMU_INST_FETCH = 2 +#define MMU_ACCESS_COUNT 3 +} MMUAccessType; + +#endif diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h new file mode 100644 index 0000000000..c43231af8b --- /dev/null +++ b/include/exec/page-protection.h @@ -0,0 +1,41 @@ +/* + * QEMU page protection definitions. + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1+ + */ +#ifndef EXEC_PAGE_PROT_COMMON_H +#define EXEC_PAGE_PROT_COMMON_H + +/* same as PROT_xxx */ +#define PAGE_READ 0x0001 +#define PAGE_WRITE 0x0002 +#define PAGE_EXEC 0x0004 +#define PAGE_RWX (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 + +#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 5004728c61..cbb2ca2131 100644 --- a/include/exec/plugin-gen.h +++ b/include/exec/plugin-gen.h @@ -12,45 +12,23 @@ #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 struct DisasContextBase *db, - bool supress); -void plugin_gen_tb_end(CPUState *cpu); +bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db); +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); -} #else /* !CONFIG_PLUGIN */ -static inline bool -plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db, bool sup) +static inline +bool plugin_gen_tb_start(CPUState *cpu, const struct DisasContextBase *db) { return false; } @@ -62,18 +40,12 @@ 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) -{ } - #endif /* CONFIG_PLUGIN */ #endif /* QEMU_PLUGIN_GEN_H */ diff --git a/include/exec/poison.h b/include/exec/poison.h index f0959bc84e..f4283f693a 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -11,7 +11,6 @@ #pragma GCC poison TARGET_AARCH64 #pragma GCC poison TARGET_ALPHA #pragma GCC poison TARGET_ARM -#pragma GCC poison TARGET_CRIS #pragma GCC poison TARGET_HEXAGON #pragma GCC poison TARGET_HPPA #pragma GCC poison TARGET_LOONGARCH64 @@ -22,7 +21,6 @@ #pragma GCC poison TARGET_ABI_MIPSO32 #pragma GCC poison TARGET_MIPS64 #pragma GCC poison TARGET_ABI_MIPSN64 -#pragma GCC poison TARGET_NIOS2 #pragma GCC poison TARGET_OPENRISC #pragma GCC poison TARGET_PPC #pragma GCC poison TARGET_PPC64 @@ -35,7 +33,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 @@ -66,7 +63,6 @@ #pragma GCC poison CPU_INTERRUPT_TGT_INT_2 #pragma GCC poison CONFIG_ALPHA_DIS -#pragma GCC poison CONFIG_CRIS_DIS #pragma GCC poison CONFIG_HPPA_DIS #pragma GCC poison CONFIG_I386_DIS #pragma GCC poison CONFIG_HEXAGON_DIS @@ -74,8 +70,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 #pragma GCC poison CONFIG_S390_DIS @@ -83,11 +77,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 ae0499ec89..07da8e47bc 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -25,6 +25,8 @@ #include "sysemu/tcg.h" #include "exec/ramlist.h" #include "exec/ramblock.h" +#include "exec/exec-all.h" +#include "qemu/rcu.h" extern uint64_t total_dirty_pages; @@ -108,9 +110,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, RAM_GUEST_MEMFD * @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: @@ -119,10 +122,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); @@ -351,14 +354,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); @@ -386,6 +398,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); qatomic_or(&blocks[DIRTY_MEMORY_NV2A][idx][offset], temp); qatomic_or(&blocks[DIRTY_MEMORY_NV2A_TEX][idx][offset], temp); @@ -396,10 +409,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); @@ -428,9 +443,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); @@ -443,9 +460,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); @@ -509,6 +536,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 adc03df59c..0babd105c0 100644 --- a/include/exec/ramblock.h +++ b/include/exec/ramblock.h @@ -34,16 +34,31 @@ 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; + int guest_memfd; size_t page_size; /* dirty bitmap used during migration */ unsigned long *bmap; - /* bitmap of already received pages in postcopy */ + + /* + * 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. Only used on destination side. */ unsigned long *receivedmap; /* diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index 5c6e5c5b4c..92fe7bdc23 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -52,6 +52,7 @@ typedef struct RAMList { /* RCU-enabled, writes protected by the ramlist lock. */ QLIST_HEAD(, RAMBlock) blocks; DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM]; + unsigned int num_dirty_blocks; uint32_t version; QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; } RAMList; 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/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 3e9cb91565..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(tb_page_addr_t addr); 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..d1155dcb6f --- /dev/null +++ b/include/exec/translation-block.h @@ -0,0 +1,156 @@ +/* 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_BP_PAGE 0x00040000 /* Breakpoint present in code page */ +#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; + uint64_t ihash; + + 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 af2ff95cd5..d8dcb77b5f 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -18,13 +18,8 @@ * 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/vaddr.h" /** * gen_intermediate_code @@ -37,8 +32,8 @@ * 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, - target_ulong pc, void *host_pc); +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc); /** * DisasJumpType: @@ -76,20 +71,37 @@ typedef enum DisasJumpType { * @is_jmp: What instruction to disassemble next. * @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. + * @plugin_enabled: TCG plugin enabled in this TB. + * @fake_insn: True if translator_fake_ldb used. + * @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 { +struct DisasContextBase { TranslationBlock *tb; - target_ulong pc_first; - target_ulong pc_next; + vaddr pc_first; + vaddr pc_next; DisasJumpType is_jmp; int num_insns; int max_insns; - bool singlestep_enabled; + bool plugin_enabled; + bool fake_insn; + struct TCGOp *insn_start; void *host_addr[2]; -} DisasContextBase; + + /* + * Record insn data that we cannot read directly from host memory. + * There are only two reasons we cannot use host memory: + * (1) We are executing from I/O, + * (2) We are executing a synthetic instruction (s390x EX). + * In both cases we need record exactly one instruction, + * and thus the maximum amount of data we record is limited. + */ + int record_start; + int record_len; + uint8_t record[32]; +}; /** * TranslatorOps: @@ -121,7 +133,7 @@ typedef struct TranslatorOps { void (*insn_start)(DisasContextBase *db, CPUState *cpu); void (*translate_insn)(DisasContextBase *db, CPUState *cpu); void (*tb_stop)(DisasContextBase *db, CPUState *cpu); - void (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f); + bool (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f); } TranslatorOps; /** @@ -146,11 +158,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(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc, - const TranslatorOps *ops, DisasContextBase *db); - -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 @@ -160,7 +170,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 @@ -173,14 +193,14 @@ bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest); * the relevant information at translation time. */ -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); +uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc); static inline uint16_t translator_lduw_swap(CPUArchState *env, DisasContextBase *db, - abi_ptr pc, bool do_swap) + vaddr pc, bool do_swap) { uint16_t ret = translator_lduw(env, db, pc); if (do_swap) { @@ -191,7 +211,7 @@ translator_lduw_swap(CPUArchState *env, DisasContextBase *db, static inline uint32_t translator_ldl_swap(CPUArchState *env, DisasContextBase *db, - abi_ptr pc, bool do_swap) + vaddr pc, bool do_swap) { uint32_t ret = translator_ldl(env, db, pc); if (do_swap) { @@ -202,7 +222,7 @@ translator_ldl_swap(CPUArchState *env, DisasContextBase *db, static inline uint64_t translator_ldq_swap(CPUArchState *env, DisasContextBase *db, - abi_ptr pc, bool do_swap) + vaddr pc, bool do_swap) { uint64_t ret = translator_ldq(env, db, pc); if (do_swap) { @@ -212,30 +232,51 @@ translator_ldq_swap(CPUArchState *env, DisasContextBase *db, } /** - * translator_fake_ldb - fake instruction load - * @insn8: byte of instruction - * @pc: program counter of instruction + * translator_fake_ld - fake instruction load + * @db: Disassembly context + * @data: bytes of instruction + * @len: number of bytes * * 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_ld(DisasContextBase *db, const void *data, size_t len); -static inline void translator_fake_ldb(uint8_t insn8, abi_ptr pc) -{ - plugin_insn_append(pc, &insn8, sizeof(insn8)); -} +/** + * translator_st + * @db: disassembly context + * @dest: address to copy into + * @addr: virtual address within TB + * @len: length + * + * Copy @len bytes from @addr into @dest. + * All bytes must have been read during translation. + * Return true on success or false on failure. + */ +bool translator_st(const DisasContextBase *db, void *dest, + vaddr addr, size_t len); +/** + * translator_st_len + * @db: disassembly context + * + * Return the number of bytes available to copy from the + * current translation block with translator_st. + */ +size_t translator_st_len(const DisasContextBase *db); +#ifdef COMPILING_PER_TARGET /* * 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, target_ulong addr) +static inline bool is_same_page(const DisasContextBase *db, vaddr addr) { return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0; } +#endif #endif /* EXEC__TRANSLATOR_H */ diff --git a/include/exec/tswap.h b/include/exec/tswap.h new file mode 100644 index 0000000000..ecd4faef01 --- /dev/null +++ b/include/exec/tswap.h @@ -0,0 +1,82 @@ +/* + * 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 "qemu/bswap.h" + +/** + * target_words_bigendian: + * Returns true if the (default) endianness of the target is big endian, + * false otherwise. Note that in target-specific code, you can use + * TARGET_BIG_ENDIAN directly instead. On the other hand, common + * code should normally never need to know about the endianness of the + * target, so please do *not* use this function unless you know very well + * what you are doing! + */ +bool target_words_bigendian(void); + +/* + * If we're in target-specific code, we can hard-code the swapping + * condition, otherwise we have to do (slower) run-time checks. + */ +#ifdef COMPILING_PER_TARGET +#define target_needs_bswap() (HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN) +#else +#define target_needs_bswap() (HOST_BIG_ENDIAN != target_words_bigendian()) +#endif /* COMPILING_PER_TARGET */ + +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/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-helpers.h b/include/fpu/softfloat-helpers.h index 94cbe073ec..453188de70 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -75,6 +75,12 @@ static inline void set_floatx80_rounding_precision(FloatX80RoundPrec val, status->floatx80_rounding_precision = val; } +static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, + float_status *status) +{ + status->float_2nan_prop_rule = rule; +} + static inline void set_flush_to_zero(bool val, float_status *status) { status->flush_to_zero = val; @@ -126,6 +132,11 @@ get_floatx80_rounding_precision(float_status *status) return status->floatx80_rounding_precision; } +static inline Float2NaNPropRule get_float_2nan_prop_rule(float_status *status) +{ + return status->float_2nan_prop_rule; +} + static inline bool get_flush_to_zero(float_status *status) { return status->flush_to_zero; diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 1e37d04b39..1a635b1653 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -183,6 +183,43 @@ typedef enum __attribute__((__packed__)) { floatx80_precision_s, } FloatX80RoundPrec; +/* + * 2-input NaN propagation rule. Individual architectures have + * different rules for which input NaN is propagated to the output + * when there is more than one NaN on the input. + * + * If default_nan_mode is enabled then it is valid not to set a + * NaN propagation rule, because the softfloat code guarantees + * not to try to pick a NaN to propagate in default NaN mode. + * When not in default-NaN mode, it is an error for the target + * not to set the rule in float_status, and we will assert if + * we need to handle an input NaN and no rule was selected. + */ +typedef enum __attribute__((__packed__)) { + /* No propagation rule specified */ + float_2nan_prop_none = 0, + /* Prefer SNaN over QNaN, then operand A over B */ + float_2nan_prop_s_ab, + /* Prefer SNaN over QNaN, then operand B over A */ + float_2nan_prop_s_ba, + /* Prefer A over B regardless of SNaN vs QNaN */ + float_2nan_prop_ab, + /* Prefer B over A regardless of SNaN vs QNaN */ + float_2nan_prop_ba, + /* + * This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + float_2nan_prop_x87, +} Float2NaNPropRule; + /* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The @@ -194,6 +231,7 @@ typedef struct float_status { uint16_t float_exception_flags; FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; + Float2NaNPropRule float_2nan_prop_rule; bool tininess_before_rounding; /* should denormalised results go to zero and set the inexact flag? */ bool flush_to_zero; 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/commands.h b/include/gdbstub/commands.h new file mode 100644 index 0000000000..40f0514fe9 --- /dev/null +++ b/include/gdbstub/commands.h @@ -0,0 +1,108 @@ +#ifndef GDBSTUB_COMMANDS_H +#define GDBSTUB + +typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); + +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 gdb_get_cmd_param(p, i) (&g_array_index(p, GdbCmdVariant, i)) + +/** + * typedef GdbCmdParseEntry - gdb command parser + * + * This structure keeps the information necessary to match a gdb command, + * parse it (extract its parameters), and select the correct handler for it. + * + * @cmd: The command to be matched + * @cmd_startswith: If true, @cmd is compared using startswith + * @schema: Each schema for the command parameter entry consists of 2 chars, + * the first char represents the parameter type handling the second char + * represents the delimiter for the next parameter. + * + * Currently supported schema types: + * 'l' -> unsigned long (stored in .val_ul) + * 'L' -> unsigned long long (stored in .val_ull) + * 's' -> string (stored in .data) + * 'o' -> single char (stored in .opcode) + * 't' -> thread id (stored in .thread_id) + * '?' -> skip according to delimiter + * + * Currently supported delimiters: + * '?' -> Stop at any delimiter (",;:=\0") + * '0' -> Stop at "\0" + * '.' -> Skip 1 char unless reached "\0" + * Any other value is treated as the delimiter value itself + * + * @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. + * + * @need_cpu_context: Pass current CPU context to command handler via user_ctx. + */ +typedef struct GdbCmdParseEntry { + GdbCmdHandler handler; + const char *cmd; + bool cmd_startswith; + const char *schema; + bool allow_stop_reply; + bool need_cpu_context; +} GdbCmdParseEntry; + +/** + * gdb_put_packet() - put string into gdb server's buffer so it is sent + * to the client + */ +int gdb_put_packet(const char *buf); + +/** + * gdb_extend_query_table() - Extend query table. + * @table: GPtrArray of GdbCmdParseEntry entries. + * + * The caller should free @table afterwards + */ +void gdb_extend_query_table(GPtrArray *table); + +/** + * gdb_extend_set_table() - Extend set table. + * @table: GPtrArray of GdbCmdParseEntry entries. + * + * The caller should free @table afterwards + */ +void gdb_extend_set_table(GPtrArray *table); + +/** + * gdb_extend_qsupported_features() - Extend the qSupported features string. + * @qsupported_features: The additional qSupported feature(s) string. The string + * should start with a semicolon and, if there are more than one feature, the + * features should be separate by a semicolon. + * + * The caller should free @qsupported_features afterwards if + * dynamically allocated. + */ +void gdb_extend_qsupported_features(char *qsupported_features); + +/** + * Convert a hex string to bytes. Conversion is done per byte, so 2 hex digits + * are converted to 1 byte. Invalid hex digits are treated as 0 digits. + */ +void gdb_hextomem(GByteArray *mem, const char *buf, int len); + +#endif /* GDBSTUB_COMMANDS_H */ diff --git a/include/gdbstub/enums.h b/include/gdbstub/enums.h new file mode 100644 index 0000000000..c4d54a1d08 --- /dev/null +++ b/include/gdbstub/enums.h @@ -0,0 +1,21 @@ +/* + * gdbstub enums + * + * Copyright (c) 2024 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GDBSTUB_ENUMS_H +#define GDBSTUB_ENUMS_H + +#define DEFAULT_GDBSTUB_PORT "1234" + +/* GDB breakpoint/watchpoint types */ +#define GDB_BREAKPOINT_SW 0 +#define GDB_BREAKPOINT_HW 1 +#define GDB_WATCHPOINT_WRITE 2 +#define GDB_WATCHPOINT_READ 3 +#define GDB_WATCHPOINT_ACCESS 4 + +#endif /* GDBSTUB_ENUMS_H */ diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h new file mode 100644 index 0000000000..6f7cc48adc --- /dev/null +++ b/include/gdbstub/helpers.h @@ -0,0 +1,107 @@ +/* + * 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_ + +#ifndef COMPILING_PER_TARGET +#error "gdbstub helpers should only be included by target specific code" +#endif + +#include "exec/tswap.h" +#include "cpu-param.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) +#define ldtul_le_p(addr) ldq_le_p(addr) +#define ldtul_be_p(addr) ldq_be_p(addr) +#else +#define gdb_get_regl(buf, val) gdb_get_reg32(buf, val) +#define ldtul_p(addr) ldl_p(addr) +#define ldtul_le_p(addr) ldl_le_p(addr) +#define ldtul_be_p(addr) ldl_be_p(addr) +#endif + +#endif /* _GDBSTUB_HELPERS_H_ */ diff --git a/include/gdbstub/syscalls.h b/include/gdbstub/syscalls.h new file mode 100644 index 0000000000..d63228e96b --- /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-or-later + */ + +#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..654986d483 --- /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-or-later + */ + +#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/glib-compat.h b/include/glib-compat.h index 43a562974d..86be439ba0 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -19,12 +19,12 @@ /* Ask for warnings for anything that was marked deprecated in * the defined version, or before. It is a candidate for rewrite. */ -#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56 +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66 /* Ask for warnings if code tries to use function that did not * exist in the defined version. These risk breaking builds */ -#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56 +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -105,29 +105,6 @@ static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size) } #define g_memdup2(m, s) g_memdup2_qemu(m, s) -#if defined(G_OS_UNIX) -/* - * Note: The fallback implementation is not MT-safe, and it returns a copy of - * the libc passwd (must be g_free() after use) but not the content. Because of - * these important differences the caller must be aware of, it's not #define for - * GLib API substitution. - */ -static inline struct passwd * -g_unix_get_passwd_entry_qemu(const gchar *user_name, GError **error) -{ -#if GLIB_CHECK_VERSION(2, 64, 0) - return g_unix_get_passwd_entry(user_name, error); -#else - struct passwd *p = getpwnam(user_name); - if (!p) { - g_set_error_literal(error, G_UNIX_ERROR, 0, g_strerror(errno)); - return NULL; - } - return (struct passwd *)g_memdup(p, sizeof(*p)); -#endif -} -#endif /* G_OS_UNIX */ - static inline bool qemu_g_test_slow(void) { 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 edbf35bf98..143529ec01 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -70,7 +70,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 (\ @@ -88,7 +88,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 index 436da069d6..11748a8866 100644 --- a/include/hw/acpi/acpi_aml_interface.h +++ b/include/hw/acpi/acpi_aml_interface.h @@ -3,6 +3,7 @@ #include "qom/object.h" #include "hw/acpi/aml-build.h" +#include "hw/qdev-core.h" #define TYPE_ACPI_DEV_AML_IF "acpi-dev-aml-interface" typedef struct AcpiDevAmlIfClass AcpiDevAmlIfClass; @@ -46,4 +47,6 @@ static inline void call_dev_aml_func(DeviceState *dev, Aml *scope) } } +void qbus_build_aml(BusState *bus, Aml *scope); + #endif diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index 2301f86513..99673e95c9 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 */ @@ -55,8 +54,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/aml-build.h b/include/hw/acpi/aml-build.h index d1fb08514b..4fd5da49e7 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"); @@ -486,6 +486,13 @@ Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set, uint32_t io_offset, void build_srat_memory(GArray *table_data, uint64_t base, uint64_t len, int node, MemoryAffinityFlags flags); +void build_srat_pci_generic_initiator(GArray *table_data, uint32_t node, + uint16_t segment, uint8_t bus, + uint8_t devfn); + +void build_srat_acpi_generic_port(GArray *table_data, uint32_t node, + const char *hid, uint32_t uid); + void build_slit(GArray *table_data, BIOSLinker *linker, MachineState *ms, const char *oem_id, const char *oem_table_id); @@ -497,4 +504,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..32654dc274 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -12,13 +12,17 @@ #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" +#define ACPI_CPU_HOTPLUG_REG_LEN 12 + typedef struct AcpiCpuStatus { - struct CPUState *cpu; + CPUState *cpu; uint64_t arch_id; bool is_inserting; bool is_removing; @@ -55,10 +59,14 @@ 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 base_addr, const char *res_root, - const char *event_handler_method); + const char *event_handler_method, + AmlRegionSpace rs); void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list); diff --git a/include/hw/acpi/cxl.h b/include/hw/acpi/cxl.h index acf4418886..8f22c71530 100644 --- a/include/hw/acpi/cxl.h +++ b/include/hw/acpi/cxl.h @@ -25,5 +25,6 @@ void cxl_build_cedt(GArray *table_offsets, GArray *table_data, BIOSLinker *linker, const char *oem_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..d2dac87b4a 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -62,6 +62,7 @@ #include "hw/sysbus.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/ghes.h" +#include "hw/acpi/cpu.h" #include "qom/object.h" #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -69,8 +70,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 @@ -82,12 +81,16 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) /* ACPI_GED_REG_RESET value for reset*/ #define ACPI_GED_RESET_VALUE 0x42 -/* ACPI_GED_REG_SLEEP_CTL.SLP_TYP value for S5 (aka poweroff) */ -#define ACPI_GED_SLP_TYP_S5 0x05 +/* [ACPI 5.0 Chapter 4.8.3.7] Sleep Control and Status Register */ +#define ACPI_GED_SLP_TYP_POS 0x2 /* SLP_TYPx Bit Offset */ +#define ACPI_GED_SLP_TYP_MASK 0x07 /* SLP_TYPx 3-bit mask */ +#define ACPI_GED_SLP_TYP_S5 0x05 /* System _S5 State (Soft Off) */ +#define ACPI_GED_SLP_EN 0x20 /* SLP_EN write-only bit */ #define GED_DEVICE "GED" #define AML_GED_EVT_REG "EREG" #define AML_GED_EVT_SEL "ESEL" +#define AML_GED_EVT_CPU_SCAN_METHOD "\\_SB.GED.CSCN" /* * Platforms need to specify the GED event bitmap @@ -97,6 +100,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define ACPI_GED_MEM_HOTPLUG_EVT 0x1 #define ACPI_GED_PWR_DOWN_EVT 0x2 #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 +#define ACPI_GED_CPU_HOTPLUG_EVT 0x8 typedef struct GEDState { MemoryRegion evt; @@ -108,6 +112,8 @@ struct AcpiGedState { SysBusDevice parent_obj; MemHotplugState memhp_state; MemoryRegion container_memhp; + CPUHotplugState cpuhp_state; + MemoryRegion container_cpuhp; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 7ca92843c6..245fe08dc2 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 @@ -46,6 +46,7 @@ typedef struct ICH9LPCPMRegs { uint32_t smi_en; uint32_t smi_en_wmask; uint32_t smi_sts; + uint32_t smi_sts_wmask; qemu_irq irq; /* SCI */ @@ -64,17 +65,20 @@ 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; + + bool swsmi_timer_enabled; + bool periodic_timer_enabled; + QEMUTimer *swsmi_timer; + QEMUTimer *periodic_timer; } 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 +93,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/ich9_tco.h b/include/hw/acpi/ich9_tco.h new file mode 100644 index 0000000000..2562a7cf39 --- /dev/null +++ b/include/hw/acpi/ich9_tco.h @@ -0,0 +1,83 @@ +/* + * QEMU ICH9 TCO emulation (total cost of ownership) + * + * Copyright (c) 2015 Paulo Alcantara + * + * 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_ACPI_TCO_H +#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 + +/* TCO I/O register offsets */ +enum { + TCO_RLD = 0x00, + TCO_DAT_IN = 0x02, + TCO_DAT_OUT = 0x03, + TCO1_STS = 0x04, + TCO2_STS = 0x06, + TCO1_CNT = 0x08, + TCO2_CNT = 0x0a, + TCO_MESSAGE1 = 0x0c, + TCO_MESSAGE2 = 0x0d, + TCO_WDCNT = 0x0e, + SW_IRQ_GEN = 0x10, + TCO_TMR = 0x12, +}; + +/* TCO I/O register control/status bits */ +enum { + SW_TCO_SMI = 1 << 1, + TCO_INT_STS = 1 << 2, + TCO_LOCK = 1 << 12, + TCO_TMR_HLT = 1 << 11, + TCO_TIMEOUT = 1 << 3, + TCO_SECOND_TO_STS = 1 << 1, + TCO_BOOT_STS = 1 << 2, +}; + +/* TCO I/O registers mask bits */ +enum { + TCO_RLD_MASK = 0x3ff, + TCO1_STS_MASK = 0xe870, + TCO2_STS_MASK = 0xfff8, + TCO1_CNT_MASK = 0xfeff, + TCO_TMR_MASK = 0x3ff, +}; + +typedef struct TCOIORegs { + struct { + uint16_t rld; + uint8_t din; + uint8_t dout; + uint16_t sts1; + uint16_t sts2; + uint16_t cnt1; + uint16_t cnt2; + uint8_t msg1; + uint8_t msg2; + uint8_t wdcnt; + uint16_t tmr; + } tco; + uint8_t sw_irq_gen; + + QEMUTimer *tco_timer; + int64_t expire_time; + uint8_t timeouts_no; + + MemoryRegion io; +} TCOIORegs; + +/* tco.c */ +void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent); + +extern const VMStateDescription vmstate_tco_io_sts; + +#endif /* HW_ACPI_TCO_H */ diff --git a/include/hw/acpi/ich9_timer.h b/include/hw/acpi/ich9_timer.h new file mode 100644 index 0000000000..5112df4385 --- /dev/null +++ b/include/hw/acpi/ich9_timer.h @@ -0,0 +1,23 @@ +/* + * QEMU ICH9 Timer emulation + * + * Copyright (c) 2024 Dominic Prinz + * + * 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_ACPI_ICH9_TIMER_H +#define HW_ACPI_ICH9_TIMER_H + +#include "hw/acpi/ich9.h" + +void ich9_pm_update_swsmi_timer(ICH9LPCPMRegs *pm, bool enable); + +void ich9_pm_swsmi_timer_init(ICH9LPCPMRegs *pm); + +void ich9_pm_update_periodic_timer(ICH9LPCPMRegs *pm, bool enable); + +void ich9_pm_periodic_timer_init(ICH9LPCPMRegs *pm); + +#endif 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..6359d574fd 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,10 @@ 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); + +void build_srat_generic_affinity_structures(GArray *table_data); + #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 index 32686a75c5..eb1c122d80 100644 --- a/include/hw/acpi/piix4.h +++ b/include/hw/acpi/piix4.h @@ -22,7 +22,7 @@ #ifndef HW_ACPI_PIIX4_H #define HW_ACPI_PIIX4_H -#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" #include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/acpi/memory_hotplug.h" @@ -57,8 +57,6 @@ struct PIIX4PMState { 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; diff --git a/include/hw/acpi/tco.h b/include/hw/acpi/tco.h deleted file mode 100644 index a1e0da8213..0000000000 --- a/include/hw/acpi/tco.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * QEMU ICH9 TCO emulation - * - * Copyright (c) 2015 Paulo Alcantara - * - * 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_ACPI_TCO_H -#define HW_ACPI_TCO_H - -#include "exec/memory.h" - -/* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */ -#define TCO_TICK_NSEC 600000000LL - -/* TCO I/O register offsets */ -enum { - TCO_RLD = 0x00, - TCO_DAT_IN = 0x02, - TCO_DAT_OUT = 0x03, - TCO1_STS = 0x04, - TCO2_STS = 0x06, - TCO1_CNT = 0x08, - TCO2_CNT = 0x0a, - TCO_MESSAGE1 = 0x0c, - TCO_MESSAGE2 = 0x0d, - TCO_WDCNT = 0x0e, - SW_IRQ_GEN = 0x10, - TCO_TMR = 0x12, -}; - -/* TCO I/O register control/status bits */ -enum { - SW_TCO_SMI = 1 << 1, - TCO_INT_STS = 1 << 2, - TCO_LOCK = 1 << 12, - TCO_TMR_HLT = 1 << 11, - TCO_TIMEOUT = 1 << 3, - TCO_SECOND_TO_STS = 1 << 1, - TCO_BOOT_STS = 1 << 2, -}; - -/* TCO I/O registers mask bits */ -enum { - TCO_RLD_MASK = 0x3ff, - TCO1_STS_MASK = 0xe870, - TCO2_STS_MASK = 0xfff8, - TCO1_CNT_MASK = 0xfeff, - TCO_TMR_MASK = 0x3ff, -}; - -typedef struct TCOIORegs { - struct { - uint16_t rld; - uint8_t din; - uint8_t dout; - uint16_t sts1; - uint16_t sts2; - uint16_t cnt1; - uint16_t cnt2; - uint8_t msg1; - uint8_t msg2; - uint8_t wdcnt; - uint16_t tmr; - } tco; - uint8_t sw_irq_gen; - - QEMUTimer *tco_timer; - int64_t expire_time; - uint8_t timeouts_no; - - MemoryRegion io; -} TCOIORegs; - -/* tco.c */ -void acpi_pm_tco_init(TCOIORegs *tr, MemoryRegion *parent); - -extern const VMStateDescription vmstate_tco_io_sts; - -#endif /* HW_ACPI_TCO_H */ 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/aspeed_adc.h b/include/hw/adc/aspeed_adc.h index ff1d06ea91..f502f197ac 100644 --- a/include/hw/adc/aspeed_adc.h +++ b/include/hw/adc/aspeed_adc.h @@ -18,6 +18,7 @@ #define TYPE_ASPEED_2500_ADC TYPE_ASPEED_ADC "-ast2500" #define TYPE_ASPEED_2600_ADC TYPE_ASPEED_ADC "-ast2600" #define TYPE_ASPEED_1030_ADC TYPE_ASPEED_ADC "-ast1030" +#define TYPE_ASPEED_2700_ADC TYPE_ASPEED_ADC "-ast2700" OBJECT_DECLARE_TYPE(AspeedADCState, AspeedADCClass, ASPEED_ADC) #define TYPE_ASPEED_ADC_ENGINE "aspeed.adc.engine" diff --git a/include/hw/adc/max111x.h b/include/hw/adc/max111x.h deleted file mode 100644 index beff59c815..0000000000 --- a/include/hw/adc/max111x.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Maxim MAX1110/1111 ADC chip emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPLv2. - * - * 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_MISC_MAX111X_H -#define HW_MISC_MAX111X_H - -#include "hw/ssi/ssi.h" -#include "qom/object.h" - -/* - * This is a model of the Maxim MAX1110/1111 ADC chip, which for QEMU - * is an SSI slave device. It has either 4 (max1110) or 8 (max1111) - * 8-bit ADC channels. - * - * QEMU interface: - * + GPIO inputs 0..3 (for max1110) or 0..7 (for max1111): set the value - * of each ADC input, as an unsigned 8-bit value - * + GPIO output 0: interrupt line - * + Properties "input0" to "input3" (max1110) or "input0" to "input7" - * (max1111): initial reset values for ADC inputs. - * - * Known bugs: - * + the interrupt line is not correctly implemented, and will never - * be lowered once it has been asserted. - */ -struct MAX111xState { - SSIPeripheral parent_obj; - - qemu_irq interrupt; - /* Values of inputs at system reset (settable by QOM property) */ - uint8_t reset_input[8]; - - uint8_t tb1, rb2, rb3; - int cycle; - - uint8_t input[8]; - int inputs, com; -}; - -#define TYPE_MAX_111X "max111x" - -OBJECT_DECLARE_SIMPLE_TYPE(MAX111xState, MAX_111X) - -#define TYPE_MAX_1110 "max1110" -#define TYPE_MAX_1111 "max1111" - -#endif 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..e5815b0d12 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -1,17 +1,20 @@ #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/ssi/allwinner-a10-spi.h" +#include "hw/watchdog/allwinner-wdt.h" +#include "sysemu/block-backend.h" #include "target/arm/cpu.h" #include "qom/object.h" @@ -30,15 +33,40 @@ struct AwA10State { /*< public >*/ ARMCPU cpu; + AwA10ClockCtlState ccm; + AwA10DramControllerState dramc; AwA10PITState timer; AwA10PICState intc; AwEmacState emac; AllwinnerAHCIState sata; AwSdHostState mmc0; + AWI2CState i2c0; + AWA10SPIState spi0; 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 8389200b2d..689f52dae8 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -15,6 +15,7 @@ #include "hw/cpu/a15mpcore.h" #include "hw/arm/armv7m.h" #include "hw/intc/aspeed_vic.h" +#include "hw/intc/aspeed_intc.h" #include "hw/misc/aspeed_scu.h" #include "hw/adc/aspeed_adc.h" #include "hw/misc/aspeed_sdmc.h" @@ -26,6 +27,7 @@ #include "hw/ssi/aspeed_smc.h" #include "hw/misc/aspeed_hace.h" #include "hw/misc/aspeed_sbc.h" +#include "hw/misc/aspeed_sli.h" #include "hw/watchdog/wdt_aspeed.h" #include "hw/net/ftgmac100.h" #include "target/arm/cpu.h" @@ -36,33 +38,34 @@ #include "hw/misc/aspeed_lpc.h" #include "hw/misc/unimp.h" #include "hw/misc/aspeed_peci.h" -#include "hw/char/serial.h" +#include "hw/fsi/aspeed_apb2opb.h" +#include "hw/char/serial-mm.h" +#include "hw/intc/arm_gicv3.h" #define ASPEED_SPIS_NUM 2 #define ASPEED_EHCIS_NUM 2 -#define ASPEED_WDTS_NUM 4 -#define ASPEED_CPUS_NUM 2 +#define ASPEED_WDTS_NUM 8 +#define ASPEED_CPUS_NUM 4 #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; + AddressSpace dram_as; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; AspeedI2CState i2c; AspeedI3CState i3c; AspeedSCUState scu; + AspeedSCUState scuio; AspeedHACEState hace; AspeedXDMAState xdma; AspeedADCState adc; @@ -70,6 +73,9 @@ struct AspeedSoCState { AspeedSMCState spi[ASPEED_SPIS_NUM]; EHCISysBusState ehci[ASPEED_EHCIS_NUM]; AspeedSBCState sbc; + AspeedSLIState sli; + AspeedSLIState sliio; + MemoryRegion secsram; UnimplementedDeviceState sbc_unimplemented; AspeedSDMCState sdmc; AspeedWDTState wdt[ASPEED_WDTS_NUM]; @@ -87,32 +93,86 @@ struct AspeedSoCState { 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 Aspeed27x0SoCState { + AspeedSoCState parent; + + ARMCPU cpu[ASPEED_CPUS_NUM]; + AspeedINTCState intc; + GICv3State gic; + MemoryRegion dram_empty; +}; + +#define TYPE_ASPEED27X0_SOC "aspeed27x0-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SoCState, ASPEED27X0_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); + bool (*boot_from_emmc)(AspeedSoCState *s); }; +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, @@ -128,15 +188,18 @@ enum { ASPEED_DEV_UART13, ASPEED_DEV_VUART, ASPEED_DEV_FMC, + ASPEED_DEV_SPI0, ASPEED_DEV_SPI1, ASPEED_DEV_SPI2, ASPEED_DEV_EHCI1, ASPEED_DEV_EHCI2, ASPEED_DEV_VIC, + ASPEED_DEV_INTC, ASPEED_DEV_SDMC, ASPEED_DEV_SCU, ASPEED_DEV_ADC, ASPEED_DEV_SBC, + ASPEED_DEV_SECSRAM, ASPEED_DEV_EMMC_BC, ASPEED_DEV_VIDEO, ASPEED_DEV_SRAM, @@ -174,6 +237,18 @@ 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, + ASPEED_DEV_SCUIO, + ASPEED_DEV_SLI, + ASPEED_DEV_SLIIO, + ASPEED_GIC_DIST, + ASPEED_GIC_REDIST, }; qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); @@ -187,4 +262,19 @@ void aspeed_mmio_map_unimplemented(AspeedSoCState *s, SysBusDevice *dev, 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 c9d25d493e..1eeaeec9e0 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -31,13 +31,19 @@ #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/nvram/bcm2835_otp.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 >*/ @@ -56,19 +62,17 @@ struct BCM2835PeripheralState { BCM2835AuxState aux; BCM2835FBState fb; BCM2835DMAState dma; - qemu_or_irq orgated_dma_irq; + 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]; - UnimplementedDeviceState otp; + BCM2835SPIState spi[1]; + BCM2835I2CState i2c[3]; + OrIRQState orgated_i2c_irq; + BCM2835OTPState otp; UnimplementedDeviceState dbus; UnimplementedDeviceState ave0; UnimplementedDeviceState v3d; @@ -78,4 +82,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/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..420ed1d573 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -21,13 +21,11 @@ #define HW_ARM_OMAP_H #include "exec/memory.h" -#include "hw/input/tsc2xxx.h" #include "target/arm/cpu-qom.h" #include "qemu/log.h" #include "qom/object.h" # define OMAP_EMIFS_BASE 0x00000000 -# define OMAP2_Q0_BASE 0x00000000 # define OMAP_CS0_BASE 0x00000000 # define OMAP_CS1_BASE 0x04000000 # define OMAP_CS2_BASE 0x08000000 @@ -35,20 +33,12 @@ # define OMAP_EMIFF_BASE 0x10000000 # define OMAP_IMIF_BASE 0x20000000 # define OMAP_LOCALBUS_BASE 0x30000000 -# define OMAP2_Q1_BASE 0x40000000 -# define OMAP2_L4_BASE 0x48000000 -# define OMAP2_SRAM_BASE 0x40200000 -# define OMAP2_L3_BASE 0x68000000 -# define OMAP2_Q2_BASE 0x80000000 -# define OMAP2_Q3_BASE 0xc0000000 # define OMAP_MPUI_BASE 0xe1000000 # define OMAP730_SRAM_SIZE 0x00032000 # define OMAP15XX_SRAM_SIZE 0x00030000 # define OMAP16XX_SRAM_SIZE 0x00004000 # define OMAP1611_SRAM_SIZE 0x0003e800 -# define OMAP242X_SRAM_SIZE 0x000a0000 -# define OMAP243X_SRAM_SIZE 0x00010000 # define OMAP_CS0_SIZE 0x04000000 # define OMAP_CS1_SIZE 0x04000000 # define OMAP_CS2_SIZE 0x04000000 @@ -69,10 +59,9 @@ int64_t omap_clk_getrate(omap_clk clk); void omap_clk_reparent(omap_clk clk, omap_clk parent); /* omap_intc.c */ -#define TYPE_OMAP_INTC "common-omap-intc" -typedef struct omap_intr_handler_s omap_intr_handler; -DECLARE_INSTANCE_CHECKER(omap_intr_handler, OMAP_INTC, - TYPE_OMAP_INTC) +#define TYPE_OMAP_INTC "omap-intc" +typedef struct OMAPIntcState OMAPIntcState; +DECLARE_INSTANCE_CHECKER(OMAPIntcState, OMAP_INTC, TYPE_OMAP_INTC) /* @@ -89,8 +78,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,75 +92,12 @@ 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, - TYPE_OMAP2_GPIO) - -typedef struct omap_gpif_s omap_gpif; -typedef struct omap2_gpif_s omap2_gpif; - /* TODO: clock framework (see above) */ -void omap_gpio_set_clk(omap_gpif *gpio, omap_clk clk); - -void omap2_gpio_set_iclk(omap2_gpif *gpio, omap_clk clk); -void omap2_gpio_set_fclk(omap2_gpif *gpio, uint8_t i, omap_clk clk); - -/* OMAP2 l4 Interconnect */ -struct omap_l4_s; -struct omap_l4_region_s { - hwaddr offset; - size_t size; - int access; -}; -struct omap_l4_agent_info_s { - int ta; - int region; - int regions; - int ta_region; -}; -struct omap_target_agent_s { - MemoryRegion iomem; - struct omap_l4_s *bus; - int regions; - const struct omap_l4_region_s *start; - hwaddr base; - uint32_t component; - uint32_t control; - uint32_t status; -}; -struct omap_l4_s *omap_l4_init(MemoryRegion *address_space, - hwaddr base, int ta_num); - -struct omap_target_agent_s; -struct omap_target_agent_s *omap_l4ta_get( - struct omap_l4_s *bus, - const struct omap_l4_region_s *regions, - const struct omap_l4_agent_info_s *agents, - int cs); -hwaddr omap_l4_attach(struct omap_target_agent_s *ta, - int region, MemoryRegion *mr); -hwaddr omap_l4_region_base(struct omap_target_agent_s *ta, - int region); -hwaddr omap_l4_region_size(struct omap_target_agent_s *ta, - int region); - -/* OMAP2 SDRAM controller */ -struct omap_sdrc_s; -struct omap_sdrc_s *omap_sdrc_init(MemoryRegion *sysmem, - hwaddr base); -void omap_sdrc_reset(struct omap_sdrc_s *s); - -/* OMAP2 general purpose memory controller */ -struct omap_gpmc_s; -struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, - hwaddr base, - qemu_irq irq, qemu_irq drq); -void omap_gpmc_reset(struct omap_gpmc_s *s); -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem); -void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand); +void omap_gpio_set_clk(Omap1GpioState *gpio, omap_clk clk); /* * Common IRQ numbers for level 1 interrupt handler @@ -400,93 +326,11 @@ void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand); # define OMAP_INT_730_DMA_CH15 62 # define OMAP_INT_730_NAND 63 -/* - * OMAP-24xx common IRQ numbers - */ -# define OMAP_INT_24XX_STI 4 -# define OMAP_INT_24XX_SYS_NIRQ 7 -# define OMAP_INT_24XX_L3_IRQ 10 -# define OMAP_INT_24XX_PRCM_MPU_IRQ 11 -# define OMAP_INT_24XX_SDMA_IRQ0 12 -# define OMAP_INT_24XX_SDMA_IRQ1 13 -# define OMAP_INT_24XX_SDMA_IRQ2 14 -# define OMAP_INT_24XX_SDMA_IRQ3 15 -# define OMAP_INT_243X_MCBSP2_IRQ 16 -# define OMAP_INT_243X_MCBSP3_IRQ 17 -# define OMAP_INT_243X_MCBSP4_IRQ 18 -# define OMAP_INT_243X_MCBSP5_IRQ 19 -# define OMAP_INT_24XX_GPMC_IRQ 20 -# define OMAP_INT_24XX_GUFFAW_IRQ 21 -# define OMAP_INT_24XX_IVA_IRQ 22 -# define OMAP_INT_24XX_EAC_IRQ 23 -# define OMAP_INT_24XX_CAM_IRQ 24 -# define OMAP_INT_24XX_DSS_IRQ 25 -# define OMAP_INT_24XX_MAIL_U0_MPU 26 -# define OMAP_INT_24XX_DSP_UMA 27 -# define OMAP_INT_24XX_DSP_MMU 28 -# define OMAP_INT_24XX_GPIO_BANK1 29 -# define OMAP_INT_24XX_GPIO_BANK2 30 -# define OMAP_INT_24XX_GPIO_BANK3 31 -# define OMAP_INT_24XX_GPIO_BANK4 32 -# define OMAP_INT_243X_GPIO_BANK5 33 -# define OMAP_INT_24XX_MAIL_U3_MPU 34 -# define OMAP_INT_24XX_WDT3 35 -# define OMAP_INT_24XX_WDT4 36 -# define OMAP_INT_24XX_GPTIMER1 37 -# define OMAP_INT_24XX_GPTIMER2 38 -# define OMAP_INT_24XX_GPTIMER3 39 -# define OMAP_INT_24XX_GPTIMER4 40 -# define OMAP_INT_24XX_GPTIMER5 41 -# define OMAP_INT_24XX_GPTIMER6 42 -# define OMAP_INT_24XX_GPTIMER7 43 -# define OMAP_INT_24XX_GPTIMER8 44 -# define OMAP_INT_24XX_GPTIMER9 45 -# define OMAP_INT_24XX_GPTIMER10 46 -# define OMAP_INT_24XX_GPTIMER11 47 -# define OMAP_INT_24XX_GPTIMER12 48 -# define OMAP_INT_24XX_PKA_IRQ 50 -# define OMAP_INT_24XX_SHA1MD5_IRQ 51 -# define OMAP_INT_24XX_RNG_IRQ 52 -# define OMAP_INT_24XX_MG_IRQ 53 -# define OMAP_INT_24XX_I2C1_IRQ 56 -# define OMAP_INT_24XX_I2C2_IRQ 57 -# define OMAP_INT_24XX_MCBSP1_IRQ_TX 59 -# define OMAP_INT_24XX_MCBSP1_IRQ_RX 60 -# define OMAP_INT_24XX_MCBSP2_IRQ_TX 62 -# define OMAP_INT_24XX_MCBSP2_IRQ_RX 63 -# define OMAP_INT_243X_MCBSP1_IRQ 64 -# define OMAP_INT_24XX_MCSPI1_IRQ 65 -# define OMAP_INT_24XX_MCSPI2_IRQ 66 -# define OMAP_INT_24XX_SSI1_IRQ0 67 -# define OMAP_INT_24XX_SSI1_IRQ1 68 -# define OMAP_INT_24XX_SSI2_IRQ0 69 -# define OMAP_INT_24XX_SSI2_IRQ1 70 -# define OMAP_INT_24XX_SSI_GDD_IRQ 71 -# define OMAP_INT_24XX_UART1_IRQ 72 -# define OMAP_INT_24XX_UART2_IRQ 73 -# define OMAP_INT_24XX_UART3_IRQ 74 -# define OMAP_INT_24XX_USB_IRQ_GEN 75 -# define OMAP_INT_24XX_USB_IRQ_NISO 76 -# define OMAP_INT_24XX_USB_IRQ_ISO 77 -# define OMAP_INT_24XX_USB_IRQ_HGEN 78 -# define OMAP_INT_24XX_USB_IRQ_HSOF 79 -# define OMAP_INT_24XX_USB_IRQ_OTG 80 -# define OMAP_INT_24XX_VLYNQ_IRQ 81 -# define OMAP_INT_24XX_MMC_IRQ 83 -# define OMAP_INT_24XX_MS_IRQ 84 -# define OMAP_INT_24XX_FAC_IRQ 85 -# define OMAP_INT_24XX_MCSPI3_IRQ 91 -# define OMAP_INT_243X_HS_USB_MC 92 -# define OMAP_INT_243X_HS_USB_DMA 93 -# define OMAP_INT_243X_CARKIT 94 -# define OMAP_INT_34XX_GPTIMER12 95 - /* omap_dma.c */ enum omap_dma_model { omap_dma_3_0, omap_dma_3_1, omap_dma_3_2, - omap_dma_4, }; struct soc_dma_s; @@ -634,99 +478,12 @@ struct omap_dma_lcd_channel_s { # define OMAP_DMA_MMC2_RX 55 # define OMAP_DMA_CRYPTO_DES_OUT 56 -/* - * DMA request numbers for the OMAP2 - */ -# define OMAP24XX_DMA_NO_DEVICE 0 -# define OMAP24XX_DMA_XTI_DMA 1 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_EXT_DMAREQ0 2 -# define OMAP24XX_DMA_EXT_DMAREQ1 3 -# define OMAP24XX_DMA_GPMC 4 -# define OMAP24XX_DMA_GFX 5 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_DSS 6 -# define OMAP24XX_DMA_VLYNQ_TX 7 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_CWT 8 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_AES_TX 9 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_AES_RX 10 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_DES_TX 11 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_DES_RX 12 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_SHA1MD5_RX 13 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_EXT_DMAREQ2 14 -# define OMAP24XX_DMA_EXT_DMAREQ3 15 -# define OMAP24XX_DMA_EXT_DMAREQ4 16 -# define OMAP24XX_DMA_EAC_AC_RD 17 -# define OMAP24XX_DMA_EAC_AC_WR 18 -# define OMAP24XX_DMA_EAC_MD_UL_RD 19 -# define OMAP24XX_DMA_EAC_MD_UL_WR 20 -# define OMAP24XX_DMA_EAC_MD_DL_RD 21 -# define OMAP24XX_DMA_EAC_MD_DL_WR 22 -# define OMAP24XX_DMA_EAC_BT_UL_RD 23 -# define OMAP24XX_DMA_EAC_BT_UL_WR 24 -# define OMAP24XX_DMA_EAC_BT_DL_RD 25 -# define OMAP24XX_DMA_EAC_BT_DL_WR 26 -# define OMAP24XX_DMA_I2C1_TX 27 -# define OMAP24XX_DMA_I2C1_RX 28 -# define OMAP24XX_DMA_I2C2_TX 29 -# define OMAP24XX_DMA_I2C2_RX 30 -# define OMAP24XX_DMA_MCBSP1_TX 31 -# define OMAP24XX_DMA_MCBSP1_RX 32 -# define OMAP24XX_DMA_MCBSP2_TX 33 -# define OMAP24XX_DMA_MCBSP2_RX 34 -# define OMAP24XX_DMA_SPI1_TX0 35 -# define OMAP24XX_DMA_SPI1_RX0 36 -# define OMAP24XX_DMA_SPI1_TX1 37 -# define OMAP24XX_DMA_SPI1_RX1 38 -# define OMAP24XX_DMA_SPI1_TX2 39 -# define OMAP24XX_DMA_SPI1_RX2 40 -# define OMAP24XX_DMA_SPI1_TX3 41 -# define OMAP24XX_DMA_SPI1_RX3 42 -# define OMAP24XX_DMA_SPI2_TX0 43 -# define OMAP24XX_DMA_SPI2_RX0 44 -# define OMAP24XX_DMA_SPI2_TX1 45 -# define OMAP24XX_DMA_SPI2_RX1 46 - -# define OMAP24XX_DMA_UART1_TX 49 -# define OMAP24XX_DMA_UART1_RX 50 -# define OMAP24XX_DMA_UART2_TX 51 -# define OMAP24XX_DMA_UART2_RX 52 -# define OMAP24XX_DMA_UART3_TX 53 -# define OMAP24XX_DMA_UART3_RX 54 -# define OMAP24XX_DMA_USB_W2FC_TX0 55 -# define OMAP24XX_DMA_USB_W2FC_RX0 56 -# define OMAP24XX_DMA_USB_W2FC_TX1 57 -# define OMAP24XX_DMA_USB_W2FC_RX1 58 -# define OMAP24XX_DMA_USB_W2FC_TX2 59 -# define OMAP24XX_DMA_USB_W2FC_RX2 60 -# define OMAP24XX_DMA_MMC1_TX 61 -# define OMAP24XX_DMA_MMC1_RX 62 -# define OMAP24XX_DMA_MS 63 /* Not in OMAP2420 */ -# define OMAP24XX_DMA_EXT_DMAREQ5 64 - -/* omap[123].c */ -/* OMAP2 gp timer */ -struct omap_gp_timer_s; -struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk); -void omap_gp_timer_reset(struct omap_gp_timer_s *s); - -/* OMAP2 sysctimer */ -struct omap_synctimer_s; -struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk); -void omap_synctimer_reset(struct omap_synctimer_s *s); - struct omap_uart_s; struct omap_uart_s *omap_uart_init(hwaddr base, qemu_irq irq, omap_clk fclk, omap_clk iclk, qemu_irq txdma, qemu_irq rxdma, const char *label, Chardev *chr); -struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, - struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - 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); @@ -734,17 +491,6 @@ void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler); void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down); struct omap_uwire_s; -void omap_uwire_attach(struct omap_uwire_s *s, - uWireSlave *slave, int chipselect); - -/* OMAP2 spi */ -struct omap_mcspi_s; -struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, - qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk); -void omap_mcspi_attach(struct omap_mcspi_s *s, - uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, - int chipselect); -void omap_mcspi_reset(struct omap_mcspi_s *s); struct I2SCodec { void *opaque; @@ -773,9 +519,6 @@ struct I2SCodec { struct omap_mcbsp_s; void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave); -void omap_tap_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu); - /* omap_lcdc.c */ struct omap_lcd_panel_s; void omap_lcdc_reset(struct omap_lcd_panel_s *s); @@ -785,35 +528,13 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, struct omap_dma_lcd_channel_s *dma, omap_clk clk); -/* omap_dss.c */ -struct rfbi_chip_s { - void *opaque; - void (*write)(void *opaque, int dc, uint16_t value); - void (*block)(void *opaque, int dc, void *buf, size_t len, int pitch); - uint16_t (*read)(void *opaque, int dc); -}; -struct omap_dss_s; -void omap_dss_reset(struct omap_dss_s *s); -struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, - MemoryRegion *sysmem, - hwaddr l3_base, - qemu_irq irq, qemu_irq drq, - omap_clk fck1, omap_clk fck2, omap_clk ck54m, - omap_clk ick1, omap_clk ick2); -void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip); - /* omap_mmc.c */ struct omap_mmc_s; struct omap_mmc_s *omap_mmc_init(hwaddr base, MemoryRegion *sysmem, BlockBackend *blk, qemu_irq irq, qemu_irq dma[], omap_clk clk); -struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta, - BlockBackend *blk, qemu_irq irq, qemu_irq dma[], - omap_clk fclk, omap_clk iclk); void omap_mmc_reset(struct omap_mmc_s *s); -void omap_mmc_handlers(struct omap_mmc_s *s, qemu_irq ro, qemu_irq cover); -void omap_mmc_enable(struct omap_mmc_s *s, int enable); /* omap_i2c.c */ I2CBus *omap_i2c_bus(DeviceState *omap_i2c); @@ -822,24 +543,11 @@ I2CBus *omap_i2c_bus(DeviceState *omap_i2c); # define cpu_is_omap1510(cpu) (cpu->mpu_model == omap1510) # define cpu_is_omap1610(cpu) (cpu->mpu_model == omap1610) # define cpu_is_omap1710(cpu) (cpu->mpu_model == omap1710) -# define cpu_is_omap2410(cpu) (cpu->mpu_model == omap2410) -# define cpu_is_omap2420(cpu) (cpu->mpu_model == omap2420) -# define cpu_is_omap2430(cpu) (cpu->mpu_model == omap2430) -# define cpu_is_omap3430(cpu) (cpu->mpu_model == omap3430) -# define cpu_is_omap3630(cpu) (cpu->mpu_model == omap3630) # define cpu_is_omap15xx(cpu) \ (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu)) # define cpu_is_omap16xx(cpu) \ (cpu_is_omap1610(cpu) || cpu_is_omap1710(cpu)) -# define cpu_is_omap24xx(cpu) \ - (cpu_is_omap2410(cpu) || cpu_is_omap2420(cpu) || cpu_is_omap2430(cpu)) - -# define cpu_class_omap1(cpu) \ - (cpu_is_omap15xx(cpu) || cpu_is_omap16xx(cpu)) -# define cpu_class_omap2(cpu) cpu_is_omap24xx(cpu) -# define cpu_class_omap3(cpu) \ - (cpu_is_omap3430(cpu) || cpu_is_omap3630(cpu)) struct omap_mpu_state_s { enum omap_mpu_model { @@ -847,13 +555,6 @@ struct omap_mpu_state_s { omap1510, omap1610, omap1710, - omap2410, - omap2420, - omap2422, - omap2423, - omap2430, - omap3430, - omap3630, } mpu_model; ARMCPU *cpu; @@ -963,33 +664,12 @@ struct omap_mpu_state_s { uint16_t dsp_idlect2; uint16_t dsp_rstct2; } clkm; - - /* OMAP2-only peripherals */ - struct omap_l4_s *l4; - - struct omap_gp_timer_s *gptimer[12]; - struct omap_synctimer_s *synctimer; - - struct omap_prcm_s *prcm; - struct omap_sdrc_s *sdrc; - struct omap_gpmc_s *gpmc; - struct omap_sysctl_s *sysc; - - struct omap_mcspi_s *mcspi[2]; - - struct omap_dss_s *dss; - - struct omap_eac_s *eac; }; /* omap1.c */ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *sdram, const char *core); -/* omap2.c */ -struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sdram, - const char *core); - uint32_t omap_badwidth_read8(void *opaque, hwaddr addr); void omap_badwidth_write8(void *opaque, hwaddr addr, uint32_t value); @@ -1010,34 +690,6 @@ void omap_mpu_wakeup(void *opaque, int irq, int req); HWADDR_PRIx "\n", \ __func__, paddr) -/* OMAP-specific Linux bootloader tags for the ATAG_BOARD area - (Board-specifc tags are not here) */ -#define OMAP_TAG_CLOCK 0x4f01 -#define OMAP_TAG_MMC 0x4f02 -#define OMAP_TAG_SERIAL_CONSOLE 0x4f03 -#define OMAP_TAG_USB 0x4f04 -#define OMAP_TAG_LCD 0x4f05 -#define OMAP_TAG_GPIO_SWITCH 0x4f06 -#define OMAP_TAG_UART 0x4f07 -#define OMAP_TAG_FBMEM 0x4f08 -#define OMAP_TAG_STI_CONSOLE 0x4f09 -#define OMAP_TAG_CAMERA_SENSOR 0x4f0a -#define OMAP_TAG_PARTITION 0x4f0b -#define OMAP_TAG_TEA5761 0x4f10 -#define OMAP_TAG_TMP105 0x4f11 -#define OMAP_TAG_BOOT_REASON 0x4f80 -#define OMAP_TAG_FLASH_PART_STR 0x4f81 -#define OMAP_TAG_VERSION_STR 0x4f82 - -enum { - OMAP_GPIOSW_TYPE_COVER = 0 << 4, - OMAP_GPIOSW_TYPE_CONNECTION = 1 << 4, - OMAP_GPIOSW_TYPE_ACTIVITY = 2 << 4, -}; - -#define OMAP_GPIOSW_INVERTED 0x0001 -#define OMAP_GPIOSW_OUTPUT 0x0002 - # define OMAP_MPUI_REG_MASK 0x000007ff #endif diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h deleted file mode 100644 index 1095504b86..0000000000 --- a/include/hw/arm/pxa.h +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Intel XScale PXA255/270 processor support. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GNU GPL v2. - */ - -#ifndef PXA_H -#define PXA_H - -#include "exec/memory.h" -#include "target/arm/cpu-qom.h" -#include "hw/pcmcia.h" -#include "qom/object.h" - -/* Interrupt numbers */ -# define PXA2XX_PIC_SSP3 0 -# define PXA2XX_PIC_USBH2 2 -# define PXA2XX_PIC_USBH1 3 -# define PXA2XX_PIC_KEYPAD 4 -# define PXA2XX_PIC_PWRI2C 6 -# define PXA25X_PIC_HWUART 7 -# define PXA27X_PIC_OST_4_11 7 -# define PXA2XX_PIC_GPIO_0 8 -# define PXA2XX_PIC_GPIO_1 9 -# define PXA2XX_PIC_GPIO_X 10 -# define PXA2XX_PIC_I2S 13 -# define PXA26X_PIC_ASSP 15 -# define PXA25X_PIC_NSSP 16 -# define PXA27X_PIC_SSP2 16 -# define PXA2XX_PIC_LCD 17 -# define PXA2XX_PIC_I2C 18 -# define PXA2XX_PIC_ICP 19 -# define PXA2XX_PIC_STUART 20 -# define PXA2XX_PIC_BTUART 21 -# define PXA2XX_PIC_FFUART 22 -# define PXA2XX_PIC_MMC 23 -# define PXA2XX_PIC_SSP 24 -# define PXA2XX_PIC_DMA 25 -# define PXA2XX_PIC_OST_0 26 -# define PXA2XX_PIC_RTC1HZ 30 -# define PXA2XX_PIC_RTCALARM 31 - -/* DMA requests */ -# define PXA2XX_RX_RQ_I2S 2 -# define PXA2XX_TX_RQ_I2S 3 -# define PXA2XX_RX_RQ_BTUART 4 -# define PXA2XX_TX_RQ_BTUART 5 -# define PXA2XX_RX_RQ_FFUART 6 -# define PXA2XX_TX_RQ_FFUART 7 -# define PXA2XX_RX_RQ_SSP1 13 -# define PXA2XX_TX_RQ_SSP1 14 -# define PXA2XX_RX_RQ_SSP2 15 -# define PXA2XX_TX_RQ_SSP2 16 -# define PXA2XX_RX_RQ_ICP 17 -# define PXA2XX_TX_RQ_ICP 18 -# define PXA2XX_RX_RQ_STUART 19 -# define PXA2XX_TX_RQ_STUART 20 -# define PXA2XX_RX_RQ_MMCI 21 -# define PXA2XX_TX_RQ_MMCI 22 -# define PXA2XX_USB_RQ(x) ((x) + 24) -# define PXA2XX_RX_RQ_SSP3 66 -# define PXA2XX_TX_RQ_SSP3 67 - -# define PXA2XX_SDRAM_BASE 0xa0000000 -# define PXA2XX_INTERNAL_BASE 0x5c000000 -# define PXA2XX_INTERNAL_SIZE 0x40000 - -/* pxa2xx_pic.c */ -DeviceState *pxa2xx_pic_init(hwaddr base, ARMCPU *cpu); - -/* pxa2xx_gpio.c */ -DeviceState *pxa2xx_gpio_init(hwaddr base, - ARMCPU *cpu, DeviceState *pic, int lines); -void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); - -/* pxa2xx_dma.c */ -DeviceState *pxa255_dma_init(hwaddr base, qemu_irq irq); -DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq); - -/* pxa2xx_lcd.c */ -typedef struct PXA2xxLCDState PXA2xxLCDState; -PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irq); -void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler); - -/* pxa2xx_mmci.c */ -#define TYPE_PXA2XX_MMCI "pxa2xx-mmci" -OBJECT_DECLARE_SIMPLE_TYPE(PXA2xxMMCIState, PXA2XX_MMCI) - -PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma); -void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, - qemu_irq coverswitch); - -/* pxa2xx_pcmcia.c */ -#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); - -/* pxa2xx_keypad.c */ -struct keymap { - int8_t column; - int8_t row; -}; -typedef struct PXA2xxKeyPadState PXA2xxKeyPadState; -PXA2xxKeyPadState *pxa27x_keypad_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq); -void pxa27x_register_keypad(PXA2xxKeyPadState *kp, - const struct keymap *map, int size); - -/* pxa2xx.c */ -typedef struct PXA2xxI2CState PXA2xxI2CState; -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) - -typedef struct { - ARMCPU *cpu; - DeviceState *pic; - qemu_irq reset; - MemoryRegion sdram; - MemoryRegion internal; - MemoryRegion cm_iomem; - MemoryRegion mm_iomem; - MemoryRegion pm_iomem; - DeviceState *dma; - DeviceState *gpio; - PXA2xxLCDState *lcd; - SSIBus **ssp; - PXA2xxI2CState *i2c[2]; - PXA2xxMMCIState *mmc; - PXA2xxPCMCIAState *pcmcia[2]; - PXA2xxI2SState *i2s; - PXA2xxFIrState *fir; - PXA2xxKeyPadState *kp; - - /* Power management */ - hwaddr pm_base; - uint32_t pm_regs[0x40]; - - /* Clock management */ - hwaddr cm_base; - uint32_t cm_regs[4]; - uint32_t clkcfg; - - /* Memory management */ - hwaddr mm_base; - uint32_t mm_regs[0x1a]; - - /* Performance monitoring */ - uint32_t pmnc; -} PXA2xxState; - -struct PXA2xxI2SState { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq rx_dma; - qemu_irq tx_dma; - void (*data_req)(void *, int, int); - - uint32_t control[2]; - uint32_t status; - uint32_t mask; - uint32_t clk; - - int enable; - int rx_len; - int tx_len; - void (*codec_out)(void *, uint32_t); - uint32_t (*codec_in)(void *); - void *opaque; - - int fifo_len; - uint32_t fifo[16]; -}; - -# 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); - -#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..60b8e5b451 --- /dev/null +++ b/include/hw/arm/raspberrypi-fw-defs.h @@ -0,0 +1,175 @@ +/* + * 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_GET_PRIVATE_KEY = 0x00030081, + 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_PRIVATE_KEY = 0x00038081, + 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..d1a4a64551 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -23,11 +23,22 @@ #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) + +#define CACHED_ENTRY_TO_ADDR(ent, addr) ((ent)->entry.translated_addr + \ + ((addr) & (ent)->entry.addr_mask)) /* * Page table walk error types @@ -41,9 +52,18 @@ typedef enum { SMMU_PTW_ERR_PERMISSION, /* Permission fault */ } SMMUPTWEventType; +/* SMMU Stage */ +typedef enum { + SMMU_STAGE_1 = 1, + SMMU_STAGE_2, + SMMU_NESTED, +} SMMUStage; + typedef struct SMMUPTWEventInfo { + SMMUStage stage; SMMUPTWEventType type; dma_addr_t addr; /* fetched address that induced an abort, if any */ + bool is_ipa_descriptor; /* src for fault in nested translation. */ } SMMUPTWEventInfo; typedef struct SMMUTransTableInfo { @@ -58,27 +78,45 @@ typedef struct SMMUTLBEntry { IOMMUTLBEntry entry; uint8_t level; uint8_t granule; + IOMMUAccessFlags parent_perm; } 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) */ + int 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 { - int stage; /* translation stage */ - bool aa64; /* arch64 or aarch32 translation table */ + /* Shared fields between stage-1 and stage-2. */ + SMMUStage stage; /* translation stage */ 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; + int 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 { @@ -99,7 +137,8 @@ typedef struct SMMUPciBus { typedef struct SMMUIOTLBKey { uint64_t iova; - uint16_t asid; + int asid; + int vmid; uint8_t tg; uint8_t level; } SMMUIOTLBKey; @@ -146,8 +185,16 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev) * smmu_ptw - Perform the page table walk for a given iova / access flags * pair, according to @cfg translation config */ -int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, - SMMUTLBEntry *tlbe, SMMUPTWEventInfo *info); +int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, + IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, + SMMUPTWEventInfo *info); + +/* + * smmu_translate - Look for a translation in TLB, if not, do a PTW. + * Returns NULL on PTW error or incase of TLB permission errors. + */ +SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, + IOMMUAccessFlags flag, SMMUPTWEventInfo *info); /** * select_tt - compute which translation table shall be used according to @@ -155,25 +202,25 @@ int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, */ SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); -/* Return the iommu mr associated to @sid, or NULL if none */ -IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); +/* Return the SMMUDevice associated to @sid, or NULL if none */ +SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid); #define SMMU_IOTLB_MAX_SIZE 256 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(int asid, int 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_asid_vmid(SMMUState *s, int asid, int vmid); +void smmu_iotlb_inv_vmid(SMMUState *s, int vmid); +void smmu_iotlb_inv_vmid_s1(SMMUState *s, int 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); - +void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, 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..2eeada64de 100644 --- a/include/hw/arm/stm32f405_soc.h +++ b/include/hw/arm/stm32f405_soc.h @@ -25,6 +25,7 @@ #ifndef HW_ARM_STM32F405_SOC_H #define HW_ARM_STM32F405_SOC_H +#include "hw/misc/stm32_rcc.h" #include "hw/misc/stm32f4xx_syscfg.h" #include "hw/timer/stm32f2xx_timer.h" #include "hw/char/stm32f2xx_usart.h" @@ -46,25 +47,25 @@ 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; + STM32RccState rcc; STM32F4xxSyscfgState syscfg; 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..c243fb0e7f --- /dev/null +++ b/include/hw/arm/stm32l4x5_soc.h @@ -0,0 +1,74 @@ +/* + * 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 "hw/char/stm32l4x5_usart.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 + +#define STM_NUM_USARTS 3 +#define STM_NUM_UARTS 2 + +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]; + Stm32l4x5UsartBaseState usart[STM_NUM_USARTS]; + Stm32l4x5UsartBaseState uart[STM_NUM_UARTS]; + Stm32l4x5UsartBaseState lpuart; + + 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 6ec479ca2b..aca4f8061b 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,20 +44,12 @@ #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 +/* GPIO pins */ +#define GPIO_PIN_POWER_BUTTON 3 + enum { VIRT_FLASH, VIRT_MEM, @@ -69,7 +62,7 @@ enum { VIRT_GIC_ITS, VIRT_GIC_REDIST, VIRT_SMMU, - VIRT_UART, + VIRT_UART0, VIRT_MMIO, VIRT_RTC, VIRT_FW_CFG, @@ -79,7 +72,7 @@ enum { VIRT_PCIE_ECAM, VIRT_PLATFORM_BUS, VIRT_GPIO, - VIRT_SECURE_UART, + VIRT_UART1, VIRT_SECURE_MEM, VIRT_SECURE_GPIO, VIRT_PCDIMM_ACPI, @@ -109,14 +102,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 +123,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 +133,8 @@ 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; + bool no_nested_smmu; }; struct VirtMachineState { @@ -144,6 +145,7 @@ struct VirtMachineState { PFlashCFI01 *flash[2]; bool secure; bool highmem; + bool highmem_compact; bool highmem_ecam; bool highmem_mmio; bool highmem_redists; @@ -153,6 +155,7 @@ struct VirtMachineState { bool ras; bool mte; bool dtb_randomness; + bool second_ns_uart_present; OnOffAuto acpi; VirtGICType gic_version; VirtIOMMUType iommu; @@ -176,6 +179,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..05ed641b6b 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 >*/ @@ -71,8 +78,11 @@ struct Versal { struct { PL011State uart[XLNX_VERSAL_NR_UARTS]; CadenceGEMState gem[XLNX_VERSAL_NR_GEMS]; + OrIRQState gem_irq_orgate[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 +95,7 @@ struct Versal { } rpu; struct { - qemu_or_irq irq_orgate; + OrIRQState irq_orgate; XlnxXramCtrl ctrl[XLNX_VERSAL_NR_XRAM]; } xram; @@ -103,17 +113,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 +149,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 +158,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 +183,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 +253,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 20bdf894aa..c137ac59e8 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" @@ -117,6 +116,7 @@ struct XlnxZynqMPState { MemoryRegion mr_unimp[XLNX_ZYNQMP_NUM_UNIMP_AREAS]; CadenceGEMState gem[XLNX_ZYNQMP_NUM_GEMS]; + OrIRQState gem_irq_orgate[XLNX_ZYNQMP_NUM_GEMS]; CadenceUARTState uart[XLNX_ZYNQMP_NUM_UARTS]; XlnxZynqMPCANState can[XLNX_ZYNQMP_NUM_CAN]; SysbusAHCIState sata; @@ -130,7 +130,7 @@ 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]; 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 f22c64dfab..c64327a13b 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -88,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 35248c0837..c367c5efea 100644 --- a/include/hw/block/fdc.h +++ b/include/hw/block/fdc.h @@ -14,6 +14,9 @@ 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..5fd67f5bb7 100644 --- a/include/hw/block/flash.h +++ b/include/hw/block/flash.h @@ -53,27 +53,19 @@ 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); +/* m25p80.c */ -/* ecc.c */ -typedef struct { - uint8_t cp; /* Column parity */ - uint16_t lp[2]; /* Line parity */ - uint16_t count; -} ECCState; +#define TYPE_M25P80 "m25p80-generic" -uint8_t ecc_digest(ECCState *s, uint8_t sample); -void ecc_reset(ECCState *s); -extern const VMStateDescription vmstate_ecc_state; +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 90f1dd3aeb..36fbb9b59d 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -6,11 +6,11 @@ #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" #include "hw/core/cpu.h" +#include "hw/resettable.h" #define TYPE_MACHINE_SUFFIX "-machine" @@ -25,17 +25,36 @@ 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); bool machine_dump_guest_core(MachineState *machine); bool machine_mem_merge(MachineState *machine); +bool machine_require_guest_memfd(MachineState *machine); HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine); void machine_set_cpu_numa_node(MachineState *machine, const CpuInstanceProperties *props, Error **errp); void machine_parse_smp_config(MachineState *ms, const SMPConfiguration *config, Error **errp); +bool machine_parse_smp_cache(MachineState *ms, + const SmpCachePropertiesList *caches, + Error **errp); +unsigned int machine_topo_get_cores_per_socket(const MachineState *ms); +unsigned int machine_topo_get_threads_per_socket(const MachineState *ms); +CpuTopologyLevel machine_get_cache_topo_level(const MachineState *ms, + CacheLevelAndType cache); +void machine_set_cache_topo_level(MachineState *ms, CacheLevelAndType cache, + CpuTopologyLevel level); +bool machine_check_smp_cache(const MachineState *ms, Error **errp); +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 +130,7 @@ typedef struct CPUArchId { uint64_t arch_id; int64_t vcpus_count; CpuInstanceProperties props; - Object *cpu; + CPUState *cpu; const char *type; } CPUArchId; @@ -130,11 +149,23 @@ 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 + * @modules_supported - whether modules are supported by the machine + * @cache_supported - whether cache (l1d, l1i, l2 and l3) configuration 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; + bool modules_supported; + bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX]; } SMPCompatProps; /** @@ -153,7 +184,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 @@ -196,6 +227,10 @@ typedef struct { * Return the type of KVM corresponding to the kvm-type string option or * computed based on other criteria such as the host kernel capabilities. * kvm-type may be NULL if it is not needed. + * @hvf_get_physical_address_range: + * Returns the physical address range in bits to use for the HVF virtual + * machine based on the current boards memory map. This may be NULL if it + * is not needed. * @numa_mem_supported: * true if '--numa node.mem' option is supported and false otherwise * @hotplug_allowed: @@ -206,10 +241,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: @@ -218,6 +253,9 @@ typedef struct { * purposes only. * Applies only to default memory backend, i.e., explicit memory backend * wasn't used. + * @smbios_memory_device_size: + * Default size of memory device, + * SMBIOS 3.1.0 "7.18 Memory Device (Type 17)" */ struct MachineClass { /*< private >*/ @@ -231,9 +269,10 @@ struct MachineClass { const char *deprecation_reason; void (*init)(MachineState *state); - void (*reset)(MachineState *state, ShutdownCause reason); + void (*reset)(MachineState *state, ResetType type); void (*wakeup)(MachineState *state); int (*kvm_type)(MachineState *machine, const char *arg); + int (*hvf_get_physical_address_range)(MachineState *machine); BlockInterfaceType block_default_type; int units_per_default_bus; @@ -251,6 +290,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,7 +302,7 @@ 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; @@ -271,6 +311,7 @@ struct MachineClass { bool nvdimm_supported; bool numa_mem_supported; bool auto_enable_numa; + bool cpu_cluster_has_numa_boundary; SMPCompatProps smp_props; const char *default_ram_id; @@ -283,39 +324,68 @@ struct MachineClass { const CPUArchIdList *(*possible_cpu_arch_ids)(MachineState *machine); int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx); ram_addr_t (*fixup_ram_size)(ram_addr_t size); + uint64_t smbios_memory_device_size; + bool (*create_default_memdev)(MachineState *ms, const char *path, + Error **errp); }; /** * 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 + * @modules: the number of modules in one cluster * @cores: the number of cores in one cluster * @threads: the number of threads in one core * @max_cpus: the maximum number of logical processors on the machine */ typedef struct CpuTopology { unsigned int cpus; + unsigned int drawers; + unsigned int books; unsigned int sockets; unsigned int dies; unsigned int clusters; + unsigned int modules; unsigned int cores; unsigned int threads; unsigned int max_cpus; } CpuTopology; +typedef struct SmpCache { + SmpCacheProperties props[CACHE_LEVEL_AND_TYPE__MAX]; +} SmpCache; + /** * MachineState: */ @@ -347,6 +417,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; @@ -358,10 +436,309 @@ struct MachineState { AccelState *accelerator; CPUArchIdList *possible_cpus; CpuTopology smp; + SmpCache smp_cache; struct NVDIMMState *nvdimms_state; struct NumaState *numa_state; }; +/* + * The macros which follow are intended to facilitate the + * definition of versioned machine types, using a somewhat + * similar pattern across targets. + * + * For example, a macro that can be used to define versioned + * 'virt' machine types would look like: + * + * #define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ + * static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ + * ObjectClass *oc, \ + * void *data) \ + * { \ + * MachineClass *mc = MACHINE_CLASS(oc); \ + * MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ + * mc->desc = "QEMU " MACHINE_VER_STR(__VA_ARGS__) " Virtual Machine"; \ + * MACHINE_VER_DEPRECATION(__VA_ARGS__); \ + * if (latest) { \ + * mc->alias = "virt"; \ + * } \ + * } \ + * static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = { \ + * .name = MACHINE_VER_TYPE_NAME("virt", __VA_ARGS__), \ + * .parent = TYPE_VIRT_MACHINE, \ + * .class_init = MACHINE_VER_SYM(class_init, virt, __VA_ARGS__), \ + * }; \ + * static void MACHINE_VER_SYM(register, virt, __VA_ARGS__)(void) \ + * { \ + * MACHINE_VER_DELETION(__VA_ARGS__); \ + * type_register_static(&MACHINE_VER_SYM(info, virt, __VA_ARGS__)); \ + * } \ + * type_init(MACHINE_VER_SYM(register, virt, __VA_ARGS__)); + * + * Following this, one (or more) helpers can be added for + * whichever scenarios need to be catered for with a machine: + * + * // Normal 2 digit, marked as latest e.g. 'virt-9.0' + * #define DEFINE_VIRT_MACHINE_LATEST(major, minor) \ + * DEFINE_VIRT_MACHINE_IMPL(true, major, minor) + * + * // Normal 2 digit e.g. 'virt-9.0' + * #define DEFINE_VIRT_MACHINE(major, minor) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor) + * + * // Bugfix 3 digit e.g. 'virt-9.0.1' + * #define DEFINE_VIRT_MACHINE_BUGFIX(major, minor, micro) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, micro) + * + * // Tagged 2 digit e.g. 'virt-9.0-extra' + * #define DEFINE_VIRT_MACHINE_TAGGED(major, minor, tag) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, _, tag) + * + * // Tagged bugfix 2 digit e.g. 'virt-9.0.1-extra' + * #define DEFINE_VIRT_MACHINE_TAGGED(major, minor, micro, tag) \ + * DEFINE_VIRT_MACHINE_IMPL(false, major, minor, micro, _, tag) + */ + +/* + * Helper for dispatching different macros based on how + * many __VA_ARGS__ are passed. Supports 1 to 5 variadic + * arguments, with the called target able to be prefixed + * with 0 or more fixed arguments too. To be called thus: + * + * _MACHINE_VER_PICK(__VA_ARGS, + * MACRO_MATCHING_5_ARGS, + * MACRO_MATCHING_4_ARGS, + * MACRO_MATCHING_3_ARGS, + * MACRO_MATCHING_2_ARGS, + * MACRO_MATCHING_1_ARG) (FIXED-ARG-1, + * ..., + * FIXED-ARG-N, + * __VA_ARGS__) + */ +#define _MACHINE_VER_PICK(x1, x2, x3, x4, x5, x6, ...) x6 + +/* + * Construct a human targeted machine version string. + * + * Can be invoked with various signatures + * + * MACHINE_VER_STR(sym, prefix, major, minor) + * MACHINE_VER_STR(sym, prefix, major, minor, micro) + * MACHINE_VER_STR(sym, prefix, major, minor, _, tag) + * MACHINE_VER_STR(sym, prefix, major, minor, micro, _, tag) + * + * Respectively emitting symbols with the format + * + * "{major}.{minor}" + * "{major}.{minor}-{tag}" + * "{major}.{minor}.{micro}" + * "{major}.{minor}.{micro}-{tag}" + */ +#define _MACHINE_VER_STR2(major, minor) \ + #major "." #minor + +#define _MACHINE_VER_STR3(major, minor, micro) \ + #major "." #minor "." #micro + +#define _MACHINE_VER_STR4(major, minor, _unused_, tag) \ + #major "." #minor "-" #tag + +#define _MACHINE_VER_STR5(major, minor, micro, _unused_, tag) \ + #major "." #minor "." #micro "-" #tag + +#define MACHINE_VER_STR(...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_STR5, \ + _MACHINE_VER_STR4, \ + _MACHINE_VER_STR3, \ + _MACHINE_VER_STR2) (__VA_ARGS__) + + +/* + * Construct a QAPI type name for a versioned machine + * type + * + * Can be invoked with various signatures + * + * MACHINE_VER_TYPE_NAME(prefix, major, minor) + * MACHINE_VER_TYPE_NAME(prefix, major, minor, micro) + * MACHINE_VER_TYPE_NAME(prefix, major, minor, _, tag) + * MACHINE_VER_TYPE_NAME(prefix, major, minor, micro, _, tag) + * + * Respectively emitting symbols with the format + * + * "{prefix}-{major}.{minor}" + * "{prefix}-{major}.{minor}.{micro}" + * "{prefix}-{major}.{minor}-{tag}" + * "{prefix}-{major}.{minor}.{micro}-{tag}" + */ +#define _MACHINE_VER_TYPE_NAME2(prefix, major, minor) \ + prefix "-" #major "." #minor TYPE_MACHINE_SUFFIX + +#define _MACHINE_VER_TYPE_NAME3(prefix, major, minor, micro) \ + prefix "-" #major "." #minor "." #micro TYPE_MACHINE_SUFFIX + +#define _MACHINE_VER_TYPE_NAME4(prefix, major, minor, _unused_, tag) \ + prefix "-" #major "." #minor "-" #tag TYPE_MACHINE_SUFFIX + +#define _MACHINE_VER_TYPE_NAME5(prefix, major, minor, micro, _unused_, tag) \ + prefix "-" #major "." #minor "." #micro "-" #tag TYPE_MACHINE_SUFFIX + +#define MACHINE_VER_TYPE_NAME(prefix, ...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_TYPE_NAME5, \ + _MACHINE_VER_TYPE_NAME4, \ + _MACHINE_VER_TYPE_NAME3, \ + _MACHINE_VER_TYPE_NAME2) (prefix, __VA_ARGS__) + +/* + * Construct a name for a versioned machine type that is + * suitable for use as a C symbol (function/variable/etc). + * + * Can be invoked with various signatures + * + * MACHINE_VER_SYM(sym, prefix, major, minor) + * MACHINE_VER_SYM(sym, prefix, major, minor, micro) + * MACHINE_VER_SYM(sym, prefix, major, minor, _, tag) + * MACHINE_VER_SYM(sym, prefix, major, minor, micro, _, tag) + * + * Respectively emitting symbols with the format + * + * {prefix}_machine_{major}_{minor}_{sym} + * {prefix}_machine_{major}_{minor}_{micro}_{sym} + * {prefix}_machine_{major}_{minor}_{tag}_{sym} + * {prefix}_machine_{major}_{minor}_{micro}_{tag}_{sym} + */ +#define _MACHINE_VER_SYM2(sym, prefix, major, minor) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## sym + +#define _MACHINE_VER_SYM3(sym, prefix, major, minor, micro) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## micro ## _ ## sym + +#define _MACHINE_VER_SYM4(sym, prefix, major, minor, _unused_, tag) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## tag ## _ ## sym + +#define _MACHINE_VER_SYM5(sym, prefix, major, minor, micro, _unused_, tag) \ + prefix ## _machine_ ## major ## _ ## minor ## _ ## micro ## _ ## tag ## _ ## sym + +#define MACHINE_VER_SYM(sym, prefix, ...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_SYM5, \ + _MACHINE_VER_SYM4, \ + _MACHINE_VER_SYM3, \ + _MACHINE_VER_SYM2) (sym, prefix, __VA_ARGS__) + + +/* + * How many years/major releases for each phase + * of the life cycle. Assumes use of versioning + * scheme where major is bumped each year + */ +#define MACHINE_VER_DELETION_MAJOR 6 +#define MACHINE_VER_DEPRECATION_MAJOR 3 + +/* + * Expands to a static string containing a deprecation + * message for a versioned machine type + */ +#define MACHINE_VER_DEPRECATION_MSG \ + "machines more than " stringify(MACHINE_VER_DEPRECATION_MAJOR) \ + " years old are subject to deletion after " \ + stringify(MACHINE_VER_DELETION_MAJOR) " years" + +#define _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) \ + (((QEMU_VERSION_MAJOR - major) > cutoff) || \ + (((QEMU_VERSION_MAJOR - major) == cutoff) && \ + (QEMU_VERSION_MINOR - minor) >= 0)) + +#define _MACHINE_VER_IS_EXPIRED2(cutoff, major, minor) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) +#define _MACHINE_VER_IS_EXPIRED3(cutoff, major, minor, micro) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) +#define _MACHINE_VER_IS_EXPIRED4(cutoff, major, minor, _unused, tag) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) +#define _MACHINE_VER_IS_EXPIRED5(cutoff, major, minor, micro, _unused, tag) \ + _MACHINE_VER_IS_EXPIRED_IMPL(cutoff, major, minor) + +#define _MACHINE_IS_EXPIRED(cutoff, ...) \ + _MACHINE_VER_PICK(__VA_ARGS__, \ + _MACHINE_VER_IS_EXPIRED5, \ + _MACHINE_VER_IS_EXPIRED4, \ + _MACHINE_VER_IS_EXPIRED3, \ + _MACHINE_VER_IS_EXPIRED2) (cutoff, __VA_ARGS__) + +/* + * Evaluates true when a machine type with (major, minor) + * or (major, minor, micro) version should be considered + * deprecated based on the current versioned machine type + * lifecycle rules + */ +#define MACHINE_VER_IS_DEPRECATED(...) \ + _MACHINE_IS_EXPIRED(MACHINE_VER_DEPRECATION_MAJOR, __VA_ARGS__) + +/* + * Evaluates true when a machine type with (major, minor) + * or (major, minor, micro) version should be considered + * for deletion based on the current versioned machine type + * lifecycle rules + */ +#define MACHINE_VER_SHOULD_DELETE(...) \ + _MACHINE_IS_EXPIRED(MACHINE_VER_DELETION_MAJOR, __VA_ARGS__) + +/* + * Sets the deprecation reason for a versioned machine based + * on its age + * + * This must be unconditionally used in the _class_init + * function for all machine types which support versioning. + * + * Initially it will effectively be a no-op, but after a + * suitable period of time has passed, it will set the + * 'deprecation_reason' field on the machine, to warn users + * about forthcoming removal. + */ +#define MACHINE_VER_DEPRECATION(...) \ + do { \ + if (MACHINE_VER_IS_DEPRECATED(__VA_ARGS__)) { \ + mc->deprecation_reason = MACHINE_VER_DEPRECATION_MSG; \ + } \ + } while (0) + +/* + * Prevents registration of a versioned machined based on + * its age + * + * This must be unconditionally used in the register + * method for all machine types which support versioning. + * + * Inijtially it will effectively be a no-op, but after a + * suitable period of time has passed, it will cause + * execution of the method to return, avoiding registration + * of the machine + * + * The new deprecation and deletion policy for versioned + * machine types was introduced in QEMU 9.1.0. + * + * Under the new policy a number of old machine types (any + * prior to 2.12) would be liable for immediate deletion + * which would be a violation of our historical deprecation + * and removal policy + * + * Thus deletions are temporarily gated on existance of + * the env variable "QEMU_DELETE_MACHINES" / QEMU version + * number >= 10.1.0. This gate can be deleted in the 10.1.0 + * dev cycle + */ +#define MACHINE_VER_DELETION(...) \ + do { \ + if (MACHINE_VER_SHOULD_DELETE(__VA_ARGS__)) { \ + if (getenv("QEMU_DELETE_MACHINES") || \ + QEMU_VERSION_MAJOR > 10 || (QEMU_VERSION_MAJOR == 10 && \ + QEMU_VERSION_MINOR >= 1)) { \ + return; \ + } \ + } \ + } while (0) + #define DEFINE_MACHINE(namestr, machine_initfn) \ static void machine_initfn##_class_init(ObjectClass *oc, void *data) \ { \ @@ -379,6 +756,24 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_9_1[]; +extern const size_t hw_compat_9_1_len; + +extern GlobalProperty hw_compat_9_0[]; +extern const size_t hw_compat_9_0_len; + +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; @@ -445,13 +840,4 @@ extern const size_t hw_compat_2_5_len; extern GlobalProperty hw_compat_2_4[]; extern const size_t hw_compat_2_4_len; -extern GlobalProperty hw_compat_2_3[]; -extern const size_t hw_compat_2_3_len; - -extern GlobalProperty hw_compat_2_2[]; -extern const size_t hw_compat_2_2_len; - -extern GlobalProperty hw_compat_2_1[]; -extern const size_t hw_compat_2_1_len; - #endif 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..8c4c6a7730 100644 --- a/include/hw/char/escc.h +++ b/include/hw/char/escc.h @@ -45,6 +45,10 @@ typedef struct ESCCChannelState { ESCCChnType type; uint8_t rx, tx; QemuInputHandlerState *hs; + char *sunkbd_layout; + int sunmouse_dx; + int sunmouse_dy; + int sunmouse_buttons; } 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/include/hw/char/grlib_uart.h b/include/hw/char/grlib_uart.h new file mode 100644 index 0000000000..7496f8fd5e --- /dev/null +++ b/include/hw/char/grlib_uart.h @@ -0,0 +1,32 @@ +/* + * QEMU GRLIB UART + * + * 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 + * 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 GRLIB_UART_H +#define GRLIB_UART_H + +#define TYPE_GRLIB_APB_UART "grlib-apbuart" + +#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/mchp_pfsoc_mmuart.h b/include/hw/char/mchp_pfsoc_mmuart.h index b0e14ca355..a7b8b1b08b 100644 --- a/include/hw/char/mchp_pfsoc_mmuart.h +++ b/include/hw/char/mchp_pfsoc_mmuart.h @@ -29,7 +29,7 @@ #define HW_MCHP_PFSOC_MMUART_H #include "hw/sysbus.h" -#include "hw/char/serial.h" +#include "hw/char/serial-mm.h" #define MCHP_PFSOC_MMUART_REG_COUNT 13 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..4fcaf3d7d3 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,11 +25,13 @@ 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; MemoryRegion iomem; - uint32_t readbuff; uint32_t flags; uint32_t lcr; uint32_t rsr; @@ -39,7 +39,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 +54,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-isa.h b/include/hw/char/serial-isa.h new file mode 100644 index 0000000000..8517afa128 --- /dev/null +++ b/include/hw/char/serial-isa.h @@ -0,0 +1,38 @@ +/* + * QEMU ISA 16550A UART emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix 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. + */ + +#ifndef HW_SERIAL_ISA_H +#define HW_SERIAL_ISA_H + +#include "hw/isa/isa.h" + +#define MAX_ISA_SERIAL_PORTS 4 + +#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/serial-mm.h b/include/hw/char/serial-mm.h new file mode 100644 index 0000000000..62a8489d69 --- /dev/null +++ b/include/hw/char/serial-mm.h @@ -0,0 +1,52 @@ +/* + * QEMU 16550A UART emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix 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. + */ + +#ifndef HW_SERIAL_MM_H +#define HW_SERIAL_MM_H + +#include "hw/char/serial.h" +#include "exec/memory.h" +#include "chardev/char.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_SERIAL_MM "serial-mm" +OBJECT_DECLARE_SIMPLE_TYPE(SerialMM, SERIAL_MM) + +struct SerialMM { + SysBusDevice parent; + + SerialState serial; + + uint8_t regshift; + uint8_t endianness; +}; + +SerialMM *serial_mm_init(MemoryRegion *address_space, + hwaddr base, int regshift, + qemu_irq irq, int baudbase, + Chardev *chr, enum device_endian end); + +#endif diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index 8ba7eca3d6..942b372df6 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -29,8 +29,6 @@ #include "chardev/char-fe.h" #include "exec/memory.h" #include "qemu/fifo8.h" -#include "chardev/char.h" -#include "hw/sysbus.h" #include "qom/object.h" #define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ @@ -81,36 +79,10 @@ struct SerialState { }; typedef struct SerialState SerialState; -struct SerialMM { - SysBusDevice parent; - - SerialState serial; - - uint8_t regshift; - uint8_t endianness; -}; - extern const VMStateDescription vmstate_serial; extern const MemoryRegionOps serial_io_ops; -void serial_set_frequency(SerialState *s, uint32_t frequency); - #define TYPE_SERIAL "serial" OBJECT_DECLARE_SIMPLE_TYPE(SerialState, SERIAL) -#define TYPE_SERIAL_MM "serial-mm" -OBJECT_DECLARE_SIMPLE_TYPE(SerialMM, SERIAL_MM) - -SerialMM *serial_mm_init(MemoryRegion *address_space, - hwaddr base, int regshift, - qemu_irq irq, int baudbase, - Chardev *chr, enum device_endian end); - -/* serial-isa.c */ - -#define MAX_ISA_SERIAL_PORTS 4 - -#define TYPE_ISA_SERIAL "isa-serial" -void serial_hds_isa_init(ISABus *bus, int from, int to); - #endif diff --git a/include/hw/char/sifive_uart.h b/include/hw/char/sifive_uart.h index 7f6c79f8bd..0846cf6218 100644 --- a/include/hw/char/sifive_uart.h +++ b/include/hw/char/sifive_uart.h @@ -24,6 +24,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "qom/object.h" +#include "qemu/fifo8.h" enum { SIFIVE_UART_TXFIFO = 0, @@ -48,9 +49,13 @@ enum { SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */ }; +#define SIFIVE_UART_TXFIFO_FULL 0x80000000 + #define SIFIVE_UART_GET_TXCNT(txctrl) ((txctrl >> 16) & 0x7) #define SIFIVE_UART_GET_RXCNT(rxctrl) ((rxctrl >> 16) & 0x7) + #define SIFIVE_UART_RX_FIFO_SIZE 8 +#define SIFIVE_UART_TX_FIFO_SIZE 8 #define TYPE_SIFIVE_UART "riscv.sifive.uart" OBJECT_DECLARE_SIMPLE_TYPE(SiFiveUARTState, SIFIVE_UART) @@ -63,13 +68,20 @@ struct SiFiveUARTState { qemu_irq irq; MemoryRegion mmio; CharBackend chr; - uint8_t rx_fifo[SIFIVE_UART_RX_FIFO_SIZE]; - uint8_t rx_fifo_len; + + uint32_t txfifo; uint32_t ie; uint32_t ip; uint32_t txctrl; uint32_t rxctrl; uint32_t div; + + uint8_t rx_fifo[SIFIVE_UART_RX_FIFO_SIZE]; + uint8_t rx_fifo_len; + + Fifo8 tx_fifo; + + QEMUTimer *fifo_trigger_handle; }; SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, 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/stm32l4x5_usart.h b/include/hw/char/stm32l4x5_usart.h new file mode 100644 index 0000000000..dd3866682a --- /dev/null +++ b/include/hw/char/stm32l4x5_usart.h @@ -0,0 +1,67 @@ +/* + * STM32L4X5 USART (Universal Synchronous Asynchronous Receiver Transmitter) + * + * 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 STM32L4X5 USART is heavily inspired by the stm32f2xx_usart + * by Alistair Francis. + * The reference used is the STMicroElectronics RM0351 Reference manual + * for STM32L4x5 and STM32L4x6 advanced Arm ® -based 32-bit MCUs. + */ + +#ifndef HW_STM32L4X5_USART_H +#define HW_STM32L4X5_USART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qom/object.h" + +#define TYPE_STM32L4X5_USART_BASE "stm32l4x5-usart-base" +#define TYPE_STM32L4X5_USART "stm32l4x5-usart" +#define TYPE_STM32L4X5_UART "stm32l4x5-uart" +#define TYPE_STM32L4X5_LPUART "stm32l4x5-lpuart" +OBJECT_DECLARE_TYPE(Stm32l4x5UsartBaseState, Stm32l4x5UsartBaseClass, + STM32L4X5_USART_BASE) + +typedef enum { + STM32L4x5_USART, + STM32L4x5_UART, + STM32L4x5_LPUART, +} Stm32l4x5UsartType; + +struct Stm32l4x5UsartBaseState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t cr1; + uint32_t cr2; + uint32_t cr3; + uint32_t brr; + uint32_t gtpr; + uint32_t rtor; + /* rqr is write-only */ + uint32_t isr; + /* icr is a clear register */ + uint32_t rdr; + uint32_t tdr; + + Clock *clk; + CharBackend chr; + qemu_irq irq; + guint watch_tag; +}; + +struct Stm32l4x5UsartBaseClass { + SysBusDeviceClass parent_class; + + Stm32l4x5UsartType type; +}; + +#endif /* HW_STM32L4X5_USART_H */ 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 4e6bd279fa..1b2247f6df 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -22,15 +22,19 @@ #include "hw/qdev-core.h" #include "disas/dis-asm.h" -#include "exec/cpu-common.h" +#include "exec/breakpoint.h" #include "exec/hwaddr.h" +#include "exec/vaddr.h" #include "exec/memattrs.h" +#include "exec/mmu-access-type.h" +#include "exec/tlb-common.h" +#include "qapi/qapi-types-machine.h" #include "qapi/qapi-types-run-state.h" #include "qemu/bitmap.h" #include "qemu/rcu_queue.h" #include "qemu/queue.h" +#include "qemu/lockcnt.h" #include "qemu/thread.h" -#include "qemu/plugin.h" #include "qom/object.h" typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, @@ -80,16 +84,13 @@ DECLARE_CLASS_CHECKERS(CPUClass, CPU, typedef struct ArchCPU CpuInstanceType; \ OBJECT_DECLARE_TYPE(ArchCPU, CpuClassType, CPU_MODULE_OBJ_NAME); -typedef enum MMUAccessType { - MMU_DATA_LOAD = 0, - MMU_DATA_STORE = 1, - MMU_INST_FETCH = 2 -} MMUAccessType; - typedef struct CPUWatchpoint CPUWatchpoint; -/* see tcg-cpu-ops.h */ -struct TCGCPUOps; +/* see physmem.c */ +struct CPUAddressSpace; + +/* see accel/tcg/tb-jmp-cache.h */ +struct CPUJumpCache; /* see accel-cpu.h */ struct AccelCPUClass; @@ -100,12 +101,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 @@ -122,15 +128,13 @@ struct SysemuCPUOps; * @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. @@ -148,9 +152,11 @@ 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); @@ -159,8 +165,7 @@ struct CPUClass { 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); @@ -171,7 +176,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 @@ -187,6 +192,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. + */ +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; +}; + +/* + * 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 @@ -207,12 +346,6 @@ typedef union IcountDecr { } u16; } IcountDecr; -typedef struct CPUBreakpoint { - vaddr pc; - int flags; /* BP_* */ - QTAILQ_ENTRY(CPUBreakpoint) entry; -} CPUBreakpoint; - #ifdef XBOX typedef void (*MemAccessCallbackFunc)(void *opaque, MemoryRegion *mr, hwaddr addr, hwaddr len, bool write); @@ -226,32 +359,31 @@ typedef struct MemAccessCallback { } MemAccessCallback; #endif -struct CPUWatchpoint { - vaddr vaddr; - vaddr len; - vaddr hitaddr; - MemTxAttrs hitattrs; - int flags; /* BP_* */ - 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. +/** + * CPUNegativeOffsetState: Elements of CPUState most efficiently accessed + * from CPUArchState, via small negative offsets. + * @can_do_io: True if memory-mapped IO is allowed. + * @plugin_mem_cbs: active plugin memory callbacks + * @plugin_mem_value_low: 64 lower bits of latest accessed mem value. + * @plugin_mem_value_high: 64 higher bits of latest accessed mem value. */ -typedef struct SavedIOTLB { - MemoryRegionSection *section; - hwaddr mr_offset; -} SavedIOTLB; +typedef struct CPUNegativeOffsetState { + CPUTLB tlb; +#ifdef CONFIG_PLUGIN + /* + * The callback pointer are accessed via TCG (see gen_empty_mem_helper). + */ + GArray *plugin_mem_cbs; + uint64_t plugin_mem_value_low; + uint64_t plugin_mem_value_high; #endif + IcountDecr icount_decr; + bool can_do_io; +} CPUNegativeOffsetState; struct KVMState; struct kvm_run; -struct hax_vcpu_state; -struct hvf_vcpu_state; - /* work queue */ /* The union type allows passing of 64 bit target pointers on 32 bit @@ -275,23 +407,29 @@ 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: + * struct CPUState - common state of one CPU core or thread. + * * @cpu_index: CPU index (informative). * @cluster_index: Identifies which cluster this CPU is in. * For boards which don't define clusters or for "loose" CPUs not assigned * 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. + * @thread: Host thread details, only live once @created is #true + * @sem: WIN32 only semaphore used only for qtest + * @thread_id: native thread id of vCPU, only live once @created is #true * @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. * @created: Indicates whether the CPU thread has been successfully created. + * @halt_cond: condition variable sleeping threads can wait on. * @interrupt_request: Indicates a pending interrupt request. * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. @@ -300,29 +438,22 @@ 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. * @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_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. @@ -331,7 +462,15 @@ struct qemu_work_item; * @kvm_fetch_index: Keeps the index that we last fetched from the per-vCPU * dirty ring structure. * - * State of one CPU core or thread. + * @neg_align: The CPUState is the common part of a concrete ArchCPU + * which is allocated when an individual CPU instance is created. As + * such care is taken is ensure there is no gap between between + * CPUState and CPUArchState within ArchCPU. + * + * @neg: The architectural register state ("cpu_env") immediately follows + * CPUState in ArchCPU and is passed to TCG code. The @neg structure holds + * some common TCG CPU variables which are accessed with a negative offset + * from cpu_env. */ struct CPUState { /*< private >*/ @@ -345,7 +484,6 @@ struct CPUState { struct QemuThread *thread; #ifdef _WIN32 - HANDLE hThread; QemuSemaphore sem; #endif int thread_id; @@ -362,7 +500,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; @@ -375,17 +513,15 @@ struct CPUState { QemuMutex work_mutex; QSIMPLEQ_HEAD(, qemu_work_item) work_list; - CPUAddressSpace *cpu_ases; + struct CPUAddressSpace *cpu_ases; + int cpu_ases_count; int num_ases; AddressSpace *as; MemoryRegion *memory; - CPUArchState *env_ptr; - IcountDecr *icount_decr_ptr; + struct CPUJumpCache *tb_jmp_cache; - CPUJumpCache *tb_jmp_cache; - - struct GDBRegisterState *gdb_regs; + GArray *gdb_regs; int gdb_num_regs; int gdb_num_g_regs; QTAILQ_ENTRY(CPUState) node; @@ -412,17 +548,14 @@ struct CPUState { struct kvm_dirty_gfn *kvm_dirty_gfns; uint32_t kvm_fetch_index; uint64_t dirty_pages; + int kvm_vcpu_stats_fd; + bool vcpu_dirty; - /* 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 - GArray *plugin_mem_cbs; - /* saved iotlb data from io_writex */ - SavedIOTLB saved_iotlb; + CPUPluginState *plugin_state; #endif /* TODO Move common fields from CPUArchState here. */ @@ -430,11 +563,9 @@ 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 */ - bool vcpu_dirty; + AccelCPUState *accel; /* Used to keep track of an outstanding cpu throttle thread for migration * autoconverge @@ -452,22 +583,34 @@ struct CPUState { /* 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; @@ -493,8 +636,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) @@ -555,11 +700,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, }; /** @@ -643,12 +790,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. @@ -770,7 +931,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; } /** @@ -844,6 +1005,14 @@ void cpu_reset_interrupt(CPUState *cpu, int mask); */ void cpu_exit(CPUState *cpu); +/** + * cpu_pause: + * @cpu: The CPU to pause. + * + * Pauses CPU, i.e. puts CPU into stopped state. + */ +void cpu_pause(CPUState *cpu); + /** * cpu_resume: * @cpu: The CPU to resume. @@ -860,6 +1029,12 @@ void cpu_resume(CPUState *cpu); */ void cpu_remove_sync(CPUState *cpu); +/** + * free_queued_cpu_work() - free all items on CPU work queue + * @cpu: The CPU which work queue to free. + */ +void free_queued_cpu_work(CPUState *cpu); + /** * process_queued_cpu_work() - process all items on CPU work queue * @cpu: The CPU which work queue to process. @@ -933,9 +1108,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); @@ -958,7 +1134,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) { @@ -979,17 +1155,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); @@ -998,32 +1163,6 @@ int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, 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); - #ifdef XBOX /** * Access callbacks to facilitate lazy syncronization, specifically when @@ -1045,6 +1184,7 @@ void mem_check_access_callback_ramaddr(CPUState *cpu, void mem_check_access_callback_vaddr(CPUState *cpu, vaddr addr, vaddr len, int flags, void *iotlbentry); #endif + #endif /** @@ -1063,25 +1203,15 @@ 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: - * Returns true if the (default) endianness of the target is big endian, - * false otherwise. Note that in target-specific code, you can use - * TARGET_BIG_ENDIAN directly instead. On the other hand, common - * code should normally never need to know about the endianness of the - * target, so please do *not* use this function unless you know very well - * what you are doing! - */ -bool target_words_bigendian(void); +const char *target_name(void); -void page_size_init(void); +#ifdef COMPILING_PER_TARGET -#ifdef NEED_CPU_H - -#ifdef CONFIG_SOFTMMU +#ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_cpu_common; @@ -1092,9 +1222,9 @@ extern const VMStateDescription vmstate_cpu_common; .flags = VMS_STRUCT, \ .offset = 0, \ } -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #define UNASSIGNED_CPU_INDEX -1 #define UNASSIGNED_CLUSTER_INDEX -1 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 ee169b872c..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. diff --git a/include/hw/core/tcg-cpu-ops.h b/include/hw/core/tcg-cpu-ops.h index 20e3c0ffbb..663efb9133 100644 --- a/include/hw/core/tcg-cpu-ops.h +++ b/include/hw/core/tcg-cpu-ops.h @@ -10,11 +10,16 @@ #ifndef TCG_CPU_OPS_H #define TCG_CPU_OPS_H -#include "hw/core/cpu.h" +#include "exec/breakpoint.h" +#include "exec/hwaddr.h" +#include "exec/memattrs.h" +#include "exec/memop.h" +#include "exec/mmu-access-type.h" +#include "exec/vaddr.h" struct TCGCPUOps { /** - * @initialize: Initalize TCG state + * @initialize: Initialize TCG state * * Called when the first CPU is realized. */ @@ -49,8 +54,7 @@ struct TCGCPUOps { /** @debug_excp_handler: Callback for handling debug exceptions */ 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. * @@ -58,15 +62,101 @@ 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. + * + * The target CPU should do any special processing here that it needs + * to do when the CPU is in the halted state. + * + * Return true to indicate that the CPU should now leave halt, false + * if it should remain in the halted state. (This should generally + * be the same value that cpu_has_work() would return.) + * + * This method must be provided. If the target does not need to + * do anything special for halt, the same function used for its + * CPUClass::has_work method can be used here, as they have the + * same function signature. + */ + bool (*cpu_exec_halt)(CPUState *cpu); + /** + * @tlb_fill_align: Handle a softmmu tlb miss + * @cpu: cpu context + * @out: output page properties + * @addr: virtual address + * @access_type: read, write or execute + * @mmu_idx: mmu context + * @memop: memory operation for the access + * @size: memory access size, or 0 for whole page + * @probe: test only, no fault + * @ra: host return address for exception unwind + * + * If the access is valid, fill in @out and return true. + * Otherwise if probe is true, return false. + * Otherwise raise an exception and do not return. + * + * The alignment check for the access is deferred to this hook, + * so that the target can determine the priority of any alignment + * fault with respect to other potential faults from paging. + * Zero may be passed for @memop to skip any alignment check + * for non-memory-access operations such as probing. + */ + bool (*tlb_fill_align)(CPUState *cpu, CPUTLBEntryFull *out, vaddr addr, + MMUAccessType access_type, int mmu_idx, + MemOp memop, int size, bool probe, uintptr_t ra); /** * @tlb_fill: Handle a softmmu tlb miss * @@ -121,58 +211,55 @@ 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 */ -#endif /* NEED_CPU_H */ - + bool (*need_replay_interrupt)(int interrupt_request); +#endif /* !CONFIG_USER_ONLY */ }; +#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 deleted file mode 100644 index 8b01ed67d3..0000000000 --- a/include/hw/cris/etraxfs.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * QEMU ETRAX System Emulator - * - * Copyright (c) 2008 Edgar E. Iglesias, Axis Communications AB. - * - * 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_ETRAXFS_H -#define HW_ETRAXFS_H - -#include "net/net.h" -#include "hw/cris/etraxfs_dma.h" -#include "hw/qdev-properties.h" -#include "hw/sysbus.h" - -DeviceState *etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, - struct etraxfs_dma_client *dma_out, - struct etraxfs_dma_client *dma_in); - -static inline DeviceState *etraxfs_ser_create(hwaddr addr, - qemu_irq irq, - Chardev *chr) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qdev_new("etraxfs-serial"); - 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; -} - -#endif diff --git a/include/hw/cris/etraxfs_dma.h b/include/hw/cris/etraxfs_dma.h deleted file mode 100644 index 095d76b956..0000000000 --- a/include/hw/cris/etraxfs_dma.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef HW_ETRAXFS_DMA_H -#define HW_ETRAXFS_DMA_H - -#include "exec/hwaddr.h" - -struct dma_context_metadata { - /* data descriptor md */ - uint16_t metadata; -}; - -struct etraxfs_dma_client -{ - /* DMA controller. */ - int channel; - void *ctrl; - - /* client. */ - struct { - int (*push)(void *opaque, unsigned char *buf, - int len, bool eop); - void (*pull)(void *opaque); - void (*metadata_push)(void *opaque, - const struct dma_context_metadata *md); - void *opaque; - } client; -}; - -void *etraxfs_dmac_init(hwaddr base, int nr_channels); -void etraxfs_dmac_connect(void *opaque, int channel, qemu_irq *line, - int input); -void etraxfs_dmac_connect_client(void *opaque, int c, - struct etraxfs_dma_client *cl); -int etraxfs_dmac_input(struct etraxfs_dma_client *client, - void *buf, int len, int eop); - -#endif diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index 38e0e271d5..75e47b6864 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -13,21 +13,23 @@ #include "qapi/qapi-types-machine.h" #include "qapi/qapi-visit-machine.h" -#include "hw/pci/pci_bridge.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; @@ -48,6 +50,7 @@ struct CXLHost { PCIHostState parent_obj; CXLComponentState cxl_cstate; + bool passthrough; }; #define TYPE_PXB_CXL_HOST "pxb-cxl-host" @@ -58,4 +61,10 @@ OBJECT_DECLARE_SIMPLE_TYPE(CXLHost, PXB_CXL_HOST) 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 index e9eda00142..17a09066dc 100644 --- a/include/hw/cxl/cxl_cdat.h +++ b/include/hw/cxl/cxl_cdat.h @@ -11,21 +11,22 @@ #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.0, Aug. 2022 + * Compute Express Link (CXL) Specification, Rev. 3.1, Aug. 2023 */ -/* Table Access DOE - CXL r3.0 8.1.11 */ +/* 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.0 8.1.11.1 */ +/* 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.0 8.1.11.1 Table 8-13 */ +/* Read Entry Request - CXL r3.1 8.1.11.1 Table 8-13 */ #define CXL_DOE_TAB_REQ 0 typedef struct CDATReq { DOEHeader header; @@ -34,7 +35,7 @@ typedef struct CDATReq { uint16_t entry_handle; } QEMU_PACKED CDATReq; -/* Read Entry Response - CXL r3.0 8.1.11.1 Table 8-14 */ +/* Read Entry Response - CXL r3.1 8.1.11.1 Table 8-14 */ #define CXL_DOE_TAB_RSP 0 typedef struct CDATRsp { DOEHeader header; @@ -81,7 +82,8 @@ typedef struct CDATDsmas { uint16_t reserved; uint64_t DPA_base; uint64_t DPA_length; -} QEMU_PACKED CDATDsmas; +} CDATDsmas; +QEMU_BUILD_BUG_ON(sizeof(CDATDsmas) != 24); /* Device Scoped Latency and Bandwidth Information Structure - CDAT Table 5 */ typedef struct CDATDslbis { @@ -94,7 +96,8 @@ typedef struct CDATDslbis { uint64_t entry_base_unit; uint16_t entry[3]; uint16_t reserved2; -} QEMU_PACKED CDATDslbis; +} CDATDslbis; +QEMU_BUILD_BUG_ON(sizeof(CDATDslbis) != 24); /* Device Scoped Memory Side Cache Information Structure - CDAT Table 6 */ typedef struct CDATDsmscis { @@ -121,7 +124,8 @@ typedef struct CDATDsemts { uint16_t reserved; uint64_t DPA_offset; uint64_t DPA_length; -} QEMU_PACKED CDATDsemts; +} CDATDsemts; +QEMU_BUILD_BUG_ON(sizeof(CDATDsemts) != 24); /* Switch Scoped Latency and Bandwidth Information Structure - CDAT Table 9 */ typedef struct CDATSslbisHeader { @@ -129,7 +133,8 @@ typedef struct CDATSslbisHeader { uint8_t data_type; uint8_t reserved[3]; uint64_t entry_base_unit; -} QEMU_PACKED CDATSslbisHeader; +} CDATSslbisHeader; +QEMU_BUILD_BUG_ON(sizeof(CDATSslbisHeader) != 16); #define CDAT_PORT_ID_USP 0x100 /* Switch Scoped Latency and Bandwidth Entry - CDAT Table 10 */ @@ -138,12 +143,13 @@ typedef struct CDATSslbe { uint16_t port_y_id; uint16_t latency_bandwidth; uint16_t reserved; -} QEMU_PACKED CDATSslbe; +} CDATSslbe; +QEMU_BUILD_BUG_ON(sizeof(CDATSslbe) != 8); typedef struct CDATSslbis { CDATSslbisHeader sslbis_header; CDATSslbe sslbe[]; -} QEMU_PACKED CDATSslbis; +} CDATSslbis; typedef struct CDATEntry { void *base; diff --git a/include/hw/cxl/cxl_component.h b/include/hw/cxl/cxl_component.h index 34075cfb72..945ee6ffd0 100644 --- a/include/hw/cxl/cxl_component.h +++ b/include/hw/cxl/cxl_component.h @@ -10,14 +10,13 @@ #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" @@ -26,18 +25,21 @@ enum reg_type { 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) @@ -60,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) @@ -107,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) @@ -118,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 { @@ -150,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 @@ -200,30 +261,19 @@ 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 1ULL << (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); +bool 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); diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 449b0edfe9..561b375dc8 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,128 @@ (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 { + struct cxl_cmd cxl_cmd_set[256][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; + + /* firmware update */ + struct { + uint8_t active_slot; + uint8_t staged_slot; + bool slot[4]; + uint8_t curr_action; + uint8_t curr_slot; + /* handle partial transfers */ + bool transferring; + size_t prev_offset; + size_t prev_len; + time_t last_partxfer; + } fw; + + 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 +226,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 static_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 +312,24 @@ 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); +void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256], + 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 +337,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 +346,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,22 +398,210 @@ 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 cxl_dev_media_disabled(CXLDeviceState *cxl_dstate) +{ + uint64_t dev_status_reg = cxl_dstate->mbox_reg_state64[R_CXL_MEM_DEV_STS]; + return FIELD_EX64(dev_status_reg, CXL_MEM_DEV_STS, MEDIA_STATUS) == 0x3; +} +static inline bool scan_media_running(CXLCCI *cci) +{ + return !!cci->bg.runtime && cci->bg.opcode == 0x4304; +} + +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 + +/* CXL memory device patrol scrub control attributes */ +typedef struct CXLMemPatrolScrubReadAttrs { + uint8_t scrub_cycle_cap; + uint16_t scrub_cycle; + uint8_t scrub_flags; +} QEMU_PACKED CXLMemPatrolScrubReadAttrs; + +typedef struct CXLMemPatrolScrubWriteAttrs { + uint8_t scrub_cycle_hr; + uint8_t scrub_flags; +} QEMU_PACKED CXLMemPatrolScrubWriteAttrs; + +#define CXL_MEMDEV_PS_GET_FEATURE_VERSION 0x01 +#define CXL_MEMDEV_PS_SET_FEATURE_VERSION 0x01 +#define CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT BIT(0) +#define CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT BIT(1) +#define CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT 12 +#define CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT 1 +#define CXL_MEMDEV_PS_ENABLE_DEFAULT 0 + +/* CXL memory device DDR5 ECS control attributes */ +#define CXL_ECS_GET_FEATURE_VERSION 0x01 +#define CXL_ECS_SET_FEATURE_VERSION 0x01 +#define CXL_ECS_LOG_ENTRY_TYPE_DEFAULT 0x01 +#define CXL_ECS_REALTIME_REPORT_CAP_DEFAULT 1 +#define CXL_ECS_THRESHOLD_COUNT_DEFAULT 3 /* 3: 256, 4: 1024, 5: 4096 */ +#define CXL_ECS_MODE_DEFAULT 0 +#define CXL_ECS_NUM_MEDIA_FRUS 3 /* Default */ + +typedef struct CXLMemECSFRUReadAttrs { + uint8_t ecs_cap; + uint16_t ecs_config; + uint8_t ecs_flags; +} QEMU_PACKED CXLMemECSFRUReadAttrs; + +typedef struct CXLMemECSReadAttrs { + uint8_t ecs_log_cap; + CXLMemECSFRUReadAttrs fru_attrs[CXL_ECS_NUM_MEDIA_FRUS]; +} QEMU_PACKED CXLMemECSReadAttrs; + +typedef struct CXLMemECSFRUWriteAttrs { + uint16_t ecs_config; +} QEMU_PACKED CXLMemECSFRUWriteAttrs; + +typedef struct CXLMemECSWriteAttrs { + uint8_t ecs_log_cap; + CXLMemECSFRUWriteAttrs fru_attrs[CXL_ECS_NUM_MEDIA_FRUS]; +} QEMU_PACKED CXLMemECSWriteAttrs; + +#define DCD_MAX_NUM_REGION 8 + +typedef struct CXLDCExtentRaw { + uint64_t start_dpa; + uint64_t len; + uint8_t tag[0x10]; + uint16_t shared_seq; + uint8_t rsvd[0x6]; +} QEMU_PACKED CXLDCExtentRaw; + +typedef struct CXLDCExtent { + uint64_t start_dpa; + uint64_t len; + uint8_t tag[0x10]; + uint16_t shared_seq; + uint8_t rsvd[0x6]; + + QTAILQ_ENTRY(CXLDCExtent) node; +} CXLDCExtent; +typedef QTAILQ_HEAD(, CXLDCExtent) CXLDCExtentList; + +typedef struct CXLDCExtentGroup { + CXLDCExtentList list; + QTAILQ_ENTRY(CXLDCExtentGroup) node; +} CXLDCExtentGroup; +typedef QTAILQ_HEAD(, CXLDCExtentGroup) CXLDCExtentGroupList; + +typedef struct CXLDCRegion { + uint64_t base; /* aligned to 256*MiB */ + uint64_t decode_len; /* aligned to 256*MiB */ + uint64_t len; + uint64_t block_size; + uint32_t dsmadhandle; + uint8_t flags; + unsigned long *blk_bitmap; +} CXLDCRegion; + +typedef struct CXLSetFeatureInfo { + QemuUUID uuid; + uint8_t data_transfer_flag; + bool data_saved_across_reset; + uint16_t data_offset; + size_t data_size; +} CXLSetFeatureInfo; + 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; + + /* PCIe link characteristics */ + PCIExpLinkSpeed speed; + PCIExpLinkWidth width; /* 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; + /* Poison Injection - backup */ + CXLPoisonList poison_list_bkp; + CXLPoisonList scan_media_results; + bool scan_media_hasrun; + + CXLSetFeatureInfo set_feat_info; + + /* Patrol scrub control attributes */ + CXLMemPatrolScrubReadAttrs patrol_scrub_attrs; + CXLMemPatrolScrubWriteAttrs patrol_scrub_wr_attrs; + /* ECS control attributes */ + CXLMemECSReadAttrs ecs_attrs; + CXLMemECSWriteAttrs ecs_wr_attrs; + + struct dynamic_capacity { + HostMemoryBackend *host_dc; + AddressSpace host_dc_as; + /* + * total_capacity is equivalent to the dynamic capability + * memory region size. + */ + uint64_t total_capacity; /* 256M aligned */ + CXLDCExtentList extents; + CXLDCExtentGroupList extents_pending; + uint32_t total_extent_count; + uint32_t ext_list_gen_seq; + + uint8_t num_regions; /* 0-8 regions */ + CXLDCRegion regions[DCD_MAX_NUM_REGION]; + } dc; }; #define TYPE_CXL_TYPE3 "cxl-type3" @@ -262,11 +618,66 @@ 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_discard_all_event_records(CXLDeviceState *cxlds); + +void cxl_event_irq_assert(CXLType3Dev *ct3d); + +void cxl_set_poison_list_overflowed(CXLType3Dev *ct3d); +void cxl_clear_poison_list_overflowed(CXLType3Dev *ct3d); + +CXLDCRegion *cxl_find_dc_region(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); + +void cxl_remove_extent_from_extent_list(CXLDCExtentList *list, + CXLDCExtent *extent); +void cxl_insert_extent_to_extent_list(CXLDCExtentList *list, uint64_t dpa, + uint64_t len, uint8_t *tag, + uint16_t shared_seq); +bool test_any_bits_set(const unsigned long *addr, unsigned long nr, + unsigned long size); +bool cxl_extents_contains_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len); +CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, + uint64_t dpa, + uint64_t len, + uint8_t *tag, + uint16_t shared_seq); +void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, + CXLDCExtentGroup *group); +void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); +void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len); +void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len); +bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, + uint64_t len); #endif diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h new file mode 100644 index 0000000000..38cadaa0f3 --- /dev/null +++ b/include/hw/cxl/cxl_events.h @@ -0,0 +1,187 @@ +/* + * 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; + +/* + * CXL r3.1 section Table 8-50: Dynamic Capacity Event Record + * All fields little endian. + */ +typedef struct CXLEventDynamicCapacity { + CXLEventRecordHdr hdr; + uint8_t type; + uint8_t validity_flags; + uint16_t host_id; + uint8_t updated_region_id; + uint8_t flags; + uint8_t reserved2[2]; + uint8_t dynamic_capacity_extent[0x28]; /* defined in cxl_device.h */ + uint8_t reserved[0x18]; + uint32_t extents_avail; + uint32_t tags_avail; +} QEMU_PACKED CXLEventDynamicCapacity; + +#endif /* CXL_EVENTS_H */ diff --git a/include/hw/cxl/cxl_host.h b/include/hw/cxl/cxl_host.h index a1b662ce40..c9bc9c7c50 100644 --- a/include/hw/cxl/cxl_host.h +++ b/include/hw/cxl/cxl_host.h @@ -7,7 +7,6 @@ * COPYING file in the top-level directory. */ -#include "qemu/osdep.h" #include "hw/cxl/cxl.h" #include "hw/boards.h" diff --git a/include/hw/cxl/cxl_mailbox.h b/include/hw/cxl/cxl_mailbox.h new file mode 100644 index 0000000000..beb048052e --- /dev/null +++ b/include/hw/cxl/cxl_mailbox.h @@ -0,0 +1,18 @@ +/* + * QEMU CXL Mailbox + * + * This work is licensed under the terms of the GNU GPL, version 2. See the + * COPYING file in the top-level directory. + */ + +#ifndef CXL_MAILBOX_H +#define CXL_MAILBOX_H + +#define CXL_MBOX_IMMEDIATE_CONFIG_CHANGE (1 << 1) +#define CXL_MBOX_IMMEDIATE_DATA_CHANGE (1 << 2) +#define CXL_MBOX_IMMEDIATE_POLICY_CHANGE (1 << 3) +#define CXL_MBOX_IMMEDIATE_LOG_CHANGE (1 << 4) +#define CXL_MBOX_SECURITY_STATE_CHANGE (1 << 5) +#define CXL_MBOX_BACKGROUND_OPERATION (1 << 6) + +#endif diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h index 3cb79eca1e..d0855ed78b 100644 --- a/include/hw/cxl/cxl_pci.h +++ b/include/hw/cxl/cxl_pci.h @@ -10,19 +10,14 @@ #ifndef CXL_PCI_H #define CXL_PCI_H -#include "qemu/compiler.h" -#include "hw/pci/pci.h" -#include "hw/pci/pcie.h" -#include "hw/cxl/cxl_cdat.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 @@ -33,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 @@ -59,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; @@ -86,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; @@ -104,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; @@ -120,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; @@ -128,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/blizzard.h b/include/hw/display/blizzard.h deleted file mode 100644 index 5b33018835..0000000000 --- a/include/hw/display/blizzard.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * 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_DISPLAY_BLIZZARD_H -#define HW_DISPLAY_BLIZZARD_H - - -void *s1d13745_init(qemu_irq gpio_int); -void s1d13745_write(void *opaque, int dc, uint16_t value); -void s1d13745_write_block(void *opaque, int dc, - void *buf, size_t len, int pitch); -uint16_t s1d13745_read(void *opaque, int dc); - -#endif diff --git a/include/hw/display/dm163.h b/include/hw/display/dm163.h new file mode 100644 index 0000000000..4377f77bb7 --- /dev/null +++ b/include/hw/display/dm163.h @@ -0,0 +1,59 @@ +/* + * QEMU DM163 8x3-channel constant current led driver + * driving columns of associated 8x8 RGB matrix. + * + * Copyright (C) 2024 Samuel Tardieu + * Copyright (C) 2024 Arnaud Minier + * Copyright (C) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_DISPLAY_DM163_H +#define HW_DISPLAY_DM163_H + +#include "qom/object.h" +#include "hw/qdev-core.h" + +#define TYPE_DM163 "dm163" +OBJECT_DECLARE_SIMPLE_TYPE(DM163State, DM163); + +#define RGB_MATRIX_NUM_ROWS 8 +#define RGB_MATRIX_NUM_COLS 8 +#define DM163_NUM_LEDS (RGB_MATRIX_NUM_COLS * 3) +/* The last row is filled with 0 (turned off row) */ +#define COLOR_BUFFER_SIZE (RGB_MATRIX_NUM_ROWS + 1) + +typedef struct DM163State { + DeviceState parent_obj; + + /* DM163 driver */ + uint64_t bank0_shift_register[3]; + uint64_t bank1_shift_register[3]; + uint16_t latched_outputs[DM163_NUM_LEDS]; + uint16_t outputs[DM163_NUM_LEDS]; + qemu_irq sout; + + uint8_t sin; + uint8_t dck; + uint8_t rst_b; + uint8_t lat_b; + uint8_t selbk; + uint8_t en_b; + + /* IM120417002 colors shield */ + uint8_t activated_rows; + + /* 8x8 RGB matrix */ + QemuConsole *console; + uint8_t redraw; + /* Rows currently being displayed on the matrix. */ + /* The last row is filled with 0 (turned off row) */ + uint32_t buffer[COLOR_BUFFER_SIZE][RGB_MATRIX_NUM_COLS]; + uint8_t last_buffer_idx; + uint8_t buffer_idx_of_row[RGB_MATRIX_NUM_ROWS]; + /* Used to simulate retinal persistence of rows */ + uint8_t row_persistence_delay[RGB_MATRIX_NUM_ROWS]; +} DM163State; + +#endif /* HW_DISPLAY_DM163_H */ 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/display/tc6393xb.h b/include/hw/display/tc6393xb.h deleted file mode 100644 index f9263bf98a..0000000000 --- a/include/hw/display/tc6393xb.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Toshiba TC6393XB I/O Controller. - * Found in Sharp Zaurus SL-6000 (tosa) or some - * Toshiba e-Series PDAs. - * - * Copyright (c) 2007 Hervé Poussineau - * - * 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_DISPLAY_TC6393XB_H -#define HW_DISPLAY_TC6393XB_H - -typedef struct TC6393xbState TC6393xbState; - -TC6393xbState *tc6393xb_init(struct MemoryRegion *sysmem, - uint32_t base, qemu_irq irq); -qemu_irq tc6393xb_l3v_get(TC6393xbState *s); - -#endif 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 deleted file mode 100644 index fbe0b1e956..0000000000 --- a/include/hw/elf_ops.h +++ /dev/null @@ -1,624 +0,0 @@ -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 */ -} - -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 */ -} - -static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) -{ - bswap32s(&shdr->sh_name); - bswap32s(&shdr->sh_type); - bswapSZs(&shdr->sh_flags); - bswapSZs(&shdr->sh_addr); - bswapSZs(&shdr->sh_offset); - bswapSZs(&shdr->sh_size); - bswap32s(&shdr->sh_link); - bswap32s(&shdr->sh_info); - bswapSZs(&shdr->sh_addralign); - bswapSZs(&shdr->sh_entsize); -} - -static void glue(bswap_sym, SZ)(struct elf_sym *sym) -{ - bswap32s(&sym->st_name); - bswapSZs(&sym->st_value); - bswapSZs(&sym->st_size); - bswap16s(&sym->st_shndx); -} - -static void glue(bswap_rela, SZ)(struct elf_rela *rela) -{ - bswapSZs(&rela->r_offset); - bswapSZs(&rela->r_info); - bswapSZs((elf_word *)&rela->r_addend); -} - -static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, - int n, int type) -{ - int i; - for(i=0;ist_value) { - result = -1; - } else if (addr >= sym->st_value + sym->st_size) { - result = 1; - } - return result; -} - -static const char *glue(lookup_symbol, SZ)(struct syminfo *s, - hwaddr orig_addr) -{ - struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); - struct elf_sym *sym; - - sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), - glue(symfind, SZ)); - if (sym != NULL) { - return s->disas_strtab + sym->st_name; - } - - return ""; -} - -static int glue(symcmp, SZ)(const void *s0, const void *s1) -{ - struct elf_sym *sym0 = (struct elf_sym *)s0; - struct elf_sym *sym1 = (struct elf_sym *)s1; - return (sym0->st_value < sym1->st_value) - ? -1 - : ((sym0->st_value > sym1->st_value) ? 1 : 0); -} - -static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, - int clear_lsb, symbol_fn_t sym_cb) -{ - struct elf_shdr *symtab, *strtab; - g_autofree struct elf_shdr *shdr_table = NULL; - g_autofree struct elf_sym *syms = NULL; - g_autofree char *str = NULL; - struct syminfo *s; - int nsyms, i; - - shdr_table = load_at(fd, ehdr->e_shoff, - sizeof(struct elf_shdr) * ehdr->e_shnum); - if (!shdr_table) { - return; - } - - if (must_swab) { - for (i = 0; i < ehdr->e_shnum; i++) { - glue(bswap_shdr, SZ)(shdr_table + i); - } - } - - symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); - if (!symtab) { - return; - } - syms = load_at(fd, symtab->sh_offset, symtab->sh_size); - if (!syms) { - return; - } - - nsyms = symtab->sh_size / sizeof(struct elf_sym); - - /* String table */ - if (symtab->sh_link >= ehdr->e_shnum) { - return; - } - strtab = &shdr_table[symtab->sh_link]; - - str = load_at(fd, strtab->sh_offset, strtab->sh_size); - if (!str) { - return; - } - - i = 0; - while (i < nsyms) { - if (must_swab) { - glue(bswap_sym, SZ)(&syms[i]); - } - if (sym_cb) { - sym_cb(str + syms[i].st_name, syms[i].st_info, - syms[i].st_value, syms[i].st_size); - } - /* We are only interested in function symbols. - Throw everything else away. */ - if (syms[i].st_shndx == SHN_UNDEF || - syms[i].st_shndx >= SHN_LORESERVE || - ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { - nsyms--; - if (i < nsyms) { - syms[i] = syms[nsyms]; - } - continue; - } - if (clear_lsb) { - /* The bottom address bit marks a Thumb or MIPS16 symbol. */ - syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; - } - i++; - } - - /* check we have symbols left */ - if (nsyms == 0) { - return; - } - - syms = g_realloc(syms, nsyms * sizeof(*syms)); - qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); - for (i = 0; i < nsyms - 1; i++) { - if (syms[i].st_size == 0) { - syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; - } - } - - /* Commit */ - s = g_malloc0(sizeof(*s)); - s->lookup_symbol = glue(lookup_symbol, SZ); - glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); - s->disas_num_syms = nsyms; - s->disas_strtab = g_steal_pointer(&str); - s->next = syminfos; - syminfos = s; -} - -static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint8_t *data, - struct elf_phdr *ph, int elf_machine) -{ - struct elf_shdr *reltab, *shdr_table = NULL; - struct elf_rela *rels = NULL; - int nrels, i, ret = -1; - elf_word wordval; - void *addr; - - shdr_table = load_at(fd, ehdr->e_shoff, - sizeof(struct elf_shdr) * ehdr->e_shnum); - if (!shdr_table) { - return -1; - } - if (must_swab) { - for (i = 0; i < ehdr->e_shnum; i++) { - glue(bswap_shdr, SZ)(&shdr_table[i]); - } - } - - reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); - if (!reltab) { - goto fail; - } - rels = load_at(fd, reltab->sh_offset, reltab->sh_size); - if (!rels) { - goto fail; - } - nrels = reltab->sh_size / sizeof(struct elf_rela); - - for (i = 0; i < nrels; i++) { - if (must_swab) { - glue(bswap_rela, SZ)(&rels[i]); - } - if (rels[i].r_offset < ph->p_vaddr || - rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { - continue; - } - addr = &data[rels[i].r_offset - ph->p_vaddr]; - switch (elf_machine) { - case EM_S390: - switch (rels[i].r_info) { - case R_390_RELATIVE: - wordval = *(elf_word *)addr; - if (must_swab) { - bswapSZs(&wordval); - } - wordval = translate_fn(translate_opaque, wordval); - if (must_swab) { - bswapSZs(&wordval); - } - *(elf_word *)addr = wordval; - break; - default: - fprintf(stderr, "Unsupported relocation type %i!\n", - (int)rels[i].r_info); - } - } - } - - ret = 0; -fail: - g_free(rels); - g_free(shdr_table); - return ret; -} - -/* - * Given 'nhdr', a pointer to a range of ELF Notes, search through them - * for a note matching type 'elf_note_type' and return a pointer to - * the matching ELF note. - */ -static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, - elf_word note_size, - elf_word phdr_align, - elf_word elf_note_type) -{ - elf_word nhdr_size = sizeof(struct elf_note); - elf_word elf_note_entry_offset = 0; - elf_word note_type; - elf_word nhdr_namesz; - elf_word nhdr_descsz; - - if (nhdr == NULL) { - return NULL; - } - - note_type = nhdr->n_type; - while (note_type != elf_note_type) { - nhdr_namesz = nhdr->n_namesz; - nhdr_descsz = nhdr->n_descsz; - - elf_note_entry_offset = nhdr_size + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + - QEMU_ALIGN_UP(nhdr_descsz, phdr_align); - - /* - * If the offset calculated in this iteration exceeds the - * supplied size, we are done and no matching note was found. - */ - if (elf_note_entry_offset > note_size) { - return NULL; - } - - /* skip to the next ELF Note entry */ - nhdr = (void *)nhdr + elf_note_entry_offset; - note_type = nhdr->n_type; - } - - return nhdr; -} - -static ssize_t glue(load_elf, SZ)(const char *name, int fd, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, - int must_swab, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int elf_machine, - int clear_lsb, int data_swab, - AddressSpace *as, bool load_rom, - symbol_fn_t sym_cb) -{ - struct elfhdr ehdr; - struct elf_phdr *phdr = NULL, *ph; - int size, i; - ssize_t total_size; - elf_word mem_size, file_size, data_offset; - uint64_t addr, low = (uint64_t)-1, high = 0; - GMappedFile *mapped_file = NULL; - uint8_t *data = NULL; - ssize_t ret = ELF_LOAD_FAILED; - - if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) - goto fail; - if (must_swab) { - glue(bswap_ehdr, SZ)(&ehdr); - } - - if (elf_machine <= EM_NONE) { - /* The caller didn't specify an ARCH, we can figure it out */ - elf_machine = ehdr.e_machine; - } - - switch (elf_machine) { - case EM_PPC64: - if (ehdr.e_machine != EM_PPC64) { - if (ehdr.e_machine != EM_PPC) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - break; - case EM_X86_64: - if (ehdr.e_machine != EM_X86_64) { - if (ehdr.e_machine != EM_386) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - break; - case EM_MICROBLAZE: - if (ehdr.e_machine != EM_MICROBLAZE) { - if (ehdr.e_machine != EM_MICROBLAZE_OLD) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - break; - case EM_MIPS: - case EM_NANOMIPS: - if ((ehdr.e_machine != EM_MIPS) && - (ehdr.e_machine != EM_NANOMIPS)) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - break; - default: - if (elf_machine != ehdr.e_machine) { - ret = ELF_LOAD_WRONG_ARCH; - goto fail; - } - } - - if (pflags) { - *pflags = (elf_word)ehdr.e_flags; - } - if (pentry) - *pentry = (uint64_t)(elf_sword)ehdr.e_entry; - - glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); - - size = ehdr.e_phnum * sizeof(phdr[0]); - if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { - goto fail; - } - phdr = g_malloc0(size); - if (!phdr) - goto fail; - if (read(fd, phdr, size) != size) - goto fail; - if (must_swab) { - for(i = 0; i < ehdr.e_phnum; i++) { - ph = &phdr[i]; - glue(bswap_phdr, SZ)(ph); - } - } - - /* - * Since we want to be able to modify the mapped buffer, we set the - * 'writable' parameter to 'true'. Modifications to the buffer are not - * written back to the file. - */ - mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); - if (!mapped_file) { - goto fail; - } - - total_size = 0; - for(i = 0; i < ehdr.e_phnum; i++) { - ph = &phdr[i]; - if (ph->p_type == PT_LOAD) { - mem_size = ph->p_memsz; /* Size of the ROM */ - file_size = ph->p_filesz; /* Size of the allocated data */ - data_offset = ph->p_offset; /* Offset where the data is located */ - - if (file_size > 0) { - if (g_mapped_file_get_length(mapped_file) < - file_size + data_offset) { - goto fail; - } - - data = (uint8_t *)g_mapped_file_get_contents(mapped_file); - data += data_offset; - } - - /* The ELF spec is somewhat vague about the purpose of the - * physical address field. One common use in the embedded world - * is that physical address field specifies the load address - * and the virtual address field specifies the execution address. - * Segments are packed into ROM or flash, and the relocation - * and zero-initialization of data is done at runtime. This - * means that the memsz header represents the runtime size of the - * segment, but the filesz represents the loadtime size. If - * we try to honour the memsz value for an ELF file like this - * we will end up with overlapping segments (which the - * loader.c code will later reject). - * We support ELF files using this scheme by by checking whether - * paddr + memsz for this segment would overlap with any other - * segment. If so, then we assume it's using this scheme and - * truncate the loaded segment to the filesz size. - * If the segment considered as being memsz size doesn't overlap - * then we use memsz for the segment length, to handle ELF files - * which assume that the loader will do the zero-initialization. - */ - if (mem_size > file_size) { - /* If this segment's zero-init portion overlaps another - * segment's data or zero-init portion, then truncate this one. - * Invalid ELF files where the segments overlap even when - * only file_size bytes are loaded will be rejected by - * the ROM overlap check in loader.c, so we don't try to - * explicitly detect those here. - */ - int j; - elf_word zero_start = ph->p_paddr + file_size; - elf_word zero_end = ph->p_paddr + mem_size; - - for (j = 0; j < ehdr.e_phnum; j++) { - struct elf_phdr *jph = &phdr[j]; - - if (i != j && jph->p_type == PT_LOAD) { - elf_word other_start = jph->p_paddr; - elf_word other_end = jph->p_paddr + jph->p_memsz; - - if (!(other_start >= zero_end || - zero_start >= other_end)) { - mem_size = file_size; - break; - } - } - } - } - - if (mem_size > SSIZE_MAX - total_size) { - ret = ELF_LOAD_TOO_BIG; - goto fail; - } - - /* address_offset is hack for kernel images that are - linked at the wrong physical address. */ - if (translate_fn) { - addr = translate_fn(translate_opaque, ph->p_paddr); - glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, - translate_opaque, data, ph, elf_machine); - } else { - addr = ph->p_paddr; - } - - if (data_swab) { - int j; - for (j = 0; j < file_size; j += (1 << data_swab)) { - uint8_t *dp = data + j; - switch (data_swab) { - case (1): - *(uint16_t *)dp = bswap16(*(uint16_t *)dp); - break; - case (2): - *(uint32_t *)dp = bswap32(*(uint32_t *)dp); - break; - case (3): - *(uint64_t *)dp = bswap64(*(uint64_t *)dp); - break; - default: - g_assert_not_reached(); - } - } - } - - /* the entry pointer in the ELF header is a virtual - * address, if the text segments paddr and vaddr differ - * we need to adjust the entry */ - if (pentry && !translate_fn && - ph->p_vaddr != ph->p_paddr && - ehdr.e_entry >= ph->p_vaddr && - ehdr.e_entry < ph->p_vaddr + ph->p_filesz && - ph->p_flags & PF_X) { - *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; - } - - /* Some ELF files really do have segments of zero size; - * just ignore them rather than trying to create empty - * ROM blobs, because the zero-length blob can falsely - * trigger the overlapping-ROM-blobs check. - */ - if (mem_size != 0) { - if (load_rom) { - g_autofree char *label = - g_strdup_printf("%s ELF program header segment %d", - name, i); - - /* - * rom_add_elf_program() takes its own reference to - * 'mapped_file'. - */ - rom_add_elf_program(label, mapped_file, data, file_size, - mem_size, addr, as); - } else { - MemTxResult res; - - res = address_space_write(as ? as : &address_space_memory, - addr, MEMTXATTRS_UNSPECIFIED, - data, file_size); - if (res != MEMTX_OK) { - goto fail; - } - /* - * We need to zero'ify the space that is not copied - * from file - */ - if (file_size < mem_size) { - res = address_space_set(as ? as : &address_space_memory, - addr + file_size, 0, - mem_size - file_size, - MEMTXATTRS_UNSPECIFIED); - if (res != MEMTX_OK) { - goto fail; - } - } - } - } - - total_size += mem_size; - if (addr < low) - low = addr; - if ((addr + mem_size) > high) - high = addr + mem_size; - - data = NULL; - - } else if (ph->p_type == PT_NOTE && elf_note_fn) { - struct elf_note *nhdr = NULL; - - file_size = ph->p_filesz; /* Size of the range of ELF notes */ - data_offset = ph->p_offset; /* Offset where the notes are located */ - - if (file_size > 0) { - if (g_mapped_file_get_length(mapped_file) < - file_size + data_offset) { - goto fail; - } - - data = (uint8_t *)g_mapped_file_get_contents(mapped_file); - data += data_offset; - } - - /* - * Search the ELF notes to find one with a type matching the - * value passed in via 'translate_opaque' - */ - nhdr = (struct elf_note *)data; - assert(translate_opaque != NULL); - nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, - *(uint64_t *)translate_opaque); - if (nhdr != NULL) { - elf_note_fn((void *)nhdr, (void *)&ph->p_align, SZ == 64); - } - data = NULL; - } - } - - if (lowaddr) - *lowaddr = (uint64_t)(elf_sword)low; - if (highaddr) - *highaddr = (uint64_t)(elf_sword)high; - ret = total_size; - fail: - if (mapped_file) { - g_mapped_file_unref(mapped_file); - } - g_free(phdr); - return ret; -} diff --git a/include/hw/elf_ops.h.inc b/include/hw/elf_ops.h.inc new file mode 100644 index 0000000000..9c35d1b9da --- /dev/null +++ b/include/hw/elf_ops.h.inc @@ -0,0 +1,627 @@ +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 */ +} + +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 */ +} + +static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswapSZs(&shdr->sh_flags); + bswapSZs(&shdr->sh_addr); + bswapSZs(&shdr->sh_offset); + bswapSZs(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswapSZs(&shdr->sh_addralign); + bswapSZs(&shdr->sh_entsize); +} + +static void glue(bswap_sym, SZ)(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswapSZs(&sym->st_value); + bswapSZs(&sym->st_size); + bswap16s(&sym->st_shndx); +} + +static void glue(bswap_rela, SZ)(struct elf_rela *rela) +{ + bswapSZs(&rela->r_offset); + bswapSZs(&rela->r_info); + bswapSZs((elf_word *)&rela->r_addend); +} + +static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, + int n, int type) +{ + int i; + for(i=0;ist_value) { + result = -1; + } else if (addr >= sym->st_value + sym->st_size) { + result = 1; + } + return result; +} + +static const char *glue(lookup_symbol, SZ)(struct syminfo *s, + hwaddr orig_addr) +{ + struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); + struct elf_sym *sym; + + sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), + glue(symfind, SZ)); + if (sym != NULL) { + return s->disas_strtab + sym->st_name; + } + + return ""; +} + +static int glue(symcmp, SZ)(const void *s0, const void *s1) +{ + struct elf_sym *sym0 = (struct elf_sym *)s0; + struct elf_sym *sym1 = (struct elf_sym *)s1; + return (sym0->st_value < sym1->st_value) + ? -1 + : ((sym0->st_value > sym1->st_value) ? 1 : 0); +} + +static void glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + int clear_lsb, symbol_fn_t sym_cb) +{ + struct elf_shdr *symtab, *strtab; + g_autofree struct elf_shdr *shdr_table = NULL; + g_autofree struct elf_sym *syms = NULL; + g_autofree char *str = NULL; + struct syminfo *s; + int nsyms, i; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) { + return; + } + + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(shdr_table + i); + } + } + + symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); + if (!symtab) { + return; + } + syms = load_at(fd, symtab->sh_offset, symtab->sh_size); + if (!syms) { + return; + } + + nsyms = symtab->sh_size / sizeof(struct elf_sym); + + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) { + return; + } + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) { + return; + } + + i = 0; + while (i < nsyms) { + if (must_swab) { + glue(bswap_sym, SZ)(&syms[i]); + } + if (sym_cb) { + sym_cb(str + syms[i].st_name, syms[i].st_info, + syms[i].st_value, syms[i].st_size); + } + /* We are only interested in function symbols. + Throw everything else away. */ + if (syms[i].st_shndx == SHN_UNDEF || + syms[i].st_shndx >= SHN_LORESERVE || + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { + nsyms--; + if (i < nsyms) { + syms[i] = syms[nsyms]; + } + continue; + } + if (clear_lsb) { + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ + syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; + } + i++; + } + + /* check we have symbols left */ + if (nsyms == 0) { + return; + } + + syms = g_realloc(syms, nsyms * sizeof(*syms)); + qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); + for (i = 0; i < nsyms - 1; i++) { + if (syms[i].st_size == 0) { + syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; + } + } + + /* Commit */ + s = g_malloc0(sizeof(*s)); + s->lookup_symbol = glue(lookup_symbol, SZ); + glue(s->disas_symtab.elf, SZ) = g_steal_pointer(&syms); + s->disas_num_syms = nsyms; + s->disas_strtab = g_steal_pointer(&str); + s->next = syminfos; + syminfos = s; +} + +static int glue(elf_reloc, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint8_t *data, + struct elf_phdr *ph, int elf_machine) +{ + struct elf_shdr *reltab, *shdr_table = NULL; + struct elf_rela *rels = NULL; + int nrels, i, ret = -1; + elf_word wordval; + void *addr; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) { + return -1; + } + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(&shdr_table[i]); + } + } + + reltab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_RELA); + if (!reltab) { + goto fail; + } + rels = load_at(fd, reltab->sh_offset, reltab->sh_size); + if (!rels) { + goto fail; + } + nrels = reltab->sh_size / sizeof(struct elf_rela); + + for (i = 0; i < nrels; i++) { + if (must_swab) { + glue(bswap_rela, SZ)(&rels[i]); + } + if (rels[i].r_offset < ph->p_vaddr || + rels[i].r_offset >= ph->p_vaddr + ph->p_filesz) { + continue; + } + addr = &data[rels[i].r_offset - ph->p_vaddr]; + switch (elf_machine) { + case EM_S390: + switch (rels[i].r_info) { + case R_390_RELATIVE: + wordval = *(elf_word *)addr; + if (must_swab) { + bswapSZs(&wordval); + } + wordval = translate_fn(translate_opaque, wordval); + if (must_swab) { + bswapSZs(&wordval); + } + *(elf_word *)addr = wordval; + break; + default: + fprintf(stderr, "Unsupported relocation type %i!\n", + (int)rels[i].r_info); + } + } + } + + ret = 0; +fail: + g_free(rels); + g_free(shdr_table); + return ret; +} + +/* + * Given 'nhdr', a pointer to a range of ELF Notes, search through them + * for a note matching type 'elf_note_type' and return a pointer to + * the matching ELF note. + */ +static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, + elf_word note_size, + elf_word phdr_align, + elf_word elf_note_type) +{ + elf_word nhdr_size = sizeof(struct elf_note); + elf_word elf_note_entry_offset = 0; + elf_word note_type; + elf_word nhdr_namesz; + elf_word nhdr_descsz; + + if (nhdr == NULL) { + return NULL; + } + + note_type = nhdr->n_type; + while (note_type != elf_note_type) { + nhdr_namesz = nhdr->n_namesz; + nhdr_descsz = nhdr->n_descsz; + + elf_note_entry_offset = nhdr_size + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + + QEMU_ALIGN_UP(nhdr_descsz, phdr_align); + + /* + * If the offset calculated in this iteration exceeds the + * supplied size, we are done and no matching note was found. + */ + if (elf_note_entry_offset > note_size) { + return NULL; + } + + /* skip to the next ELF Note entry */ + nhdr = (void *)nhdr + elf_note_entry_offset; + note_type = nhdr->n_type; + } + + return nhdr; +} + +static ssize_t glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*elf_note_fn)(void *, void *, bool), + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + uint32_t *pflags, int elf_machine, + int clear_lsb, int data_swab, + AddressSpace *as, bool load_rom, + symbol_fn_t sym_cb) +{ + struct elfhdr ehdr; + struct elf_phdr *phdr = NULL, *ph; + int size, i; + ssize_t total_size; + elf_word mem_size, file_size, data_offset; + uint64_t addr, low = (uint64_t)-1, high = 0; + GMappedFile *mapped_file = NULL; + uint8_t *data = NULL; + ssize_t ret = ELF_LOAD_FAILED; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail; + if (must_swab) { + glue(bswap_ehdr, SZ)(&ehdr); + } + + if (elf_machine <= EM_NONE) { + /* The caller didn't specify an ARCH, we can figure it out */ + elf_machine = ehdr.e_machine; + } + + switch (elf_machine) { + case EM_PPC64: + if (ehdr.e_machine != EM_PPC64) { + if (ehdr.e_machine != EM_PPC) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + break; + case EM_X86_64: + if (ehdr.e_machine != EM_X86_64) { + if (ehdr.e_machine != EM_386) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + break; + case EM_MICROBLAZE: + if (ehdr.e_machine != EM_MICROBLAZE) { + if (ehdr.e_machine != EM_MICROBLAZE_OLD) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + break; + case EM_MIPS: + case EM_NANOMIPS: + if ((ehdr.e_machine != EM_MIPS) && + (ehdr.e_machine != EM_NANOMIPS)) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + break; + default: + if (elf_machine != ehdr.e_machine) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + } + + if (pflags) { + *pflags = ehdr.e_flags; + } + if (pentry) { + *pentry = ehdr.e_entry; + } + + glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); + + size = ehdr.e_phnum * sizeof(phdr[0]); + if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) { + goto fail; + } + phdr = g_malloc0(size); + if (!phdr) + goto fail; + if (read(fd, phdr, size) != size) + goto fail; + if (must_swab) { + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + glue(bswap_phdr, SZ)(ph); + } + } + + /* + * Since we want to be able to modify the mapped buffer, we set the + * 'writable' parameter to 'true'. Modifications to the buffer are not + * written back to the file. + */ + mapped_file = g_mapped_file_new_from_fd(fd, true, NULL); + if (!mapped_file) { + goto fail; + } + + total_size = 0; + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + if (ph->p_type == PT_LOAD) { + mem_size = ph->p_memsz; /* Size of the ROM */ + file_size = ph->p_filesz; /* Size of the allocated data */ + data_offset = ph->p_offset; /* Offset where the data is located */ + + if (file_size > 0) { + if (g_mapped_file_get_length(mapped_file) < + file_size + data_offset) { + goto fail; + } + + data = (uint8_t *)g_mapped_file_get_contents(mapped_file); + data += data_offset; + } + + /* The ELF spec is somewhat vague about the purpose of the + * physical address field. One common use in the embedded world + * is that physical address field specifies the load address + * and the virtual address field specifies the execution address. + * Segments are packed into ROM or flash, and the relocation + * and zero-initialization of data is done at runtime. This + * means that the memsz header represents the runtime size of the + * segment, but the filesz represents the loadtime size. If + * we try to honour the memsz value for an ELF file like this + * we will end up with overlapping segments (which the + * loader.c code will later reject). + * We support ELF files using this scheme by by checking whether + * paddr + memsz for this segment would overlap with any other + * segment. If so, then we assume it's using this scheme and + * truncate the loaded segment to the filesz size. + * If the segment considered as being memsz size doesn't overlap + * then we use memsz for the segment length, to handle ELF files + * which assume that the loader will do the zero-initialization. + */ + if (mem_size > file_size) { + /* If this segment's zero-init portion overlaps another + * segment's data or zero-init portion, then truncate this one. + * Invalid ELF files where the segments overlap even when + * only file_size bytes are loaded will be rejected by + * the ROM overlap check in loader.c, so we don't try to + * explicitly detect those here. + */ + int j; + elf_word zero_start = ph->p_paddr + file_size; + elf_word zero_end = ph->p_paddr + mem_size; + + for (j = 0; j < ehdr.e_phnum; j++) { + struct elf_phdr *jph = &phdr[j]; + + if (i != j && jph->p_type == PT_LOAD) { + elf_word other_start = jph->p_paddr; + elf_word other_end = jph->p_paddr + jph->p_memsz; + + if (!(other_start >= zero_end || + zero_start >= other_end)) { + mem_size = file_size; + break; + } + } + } + } + + if (mem_size > SSIZE_MAX - total_size) { + ret = ELF_LOAD_TOO_BIG; + goto fail; + } + + /* address_offset is hack for kernel images that are + linked at the wrong physical address. */ + if (translate_fn) { + addr = translate_fn(translate_opaque, ph->p_paddr); + glue(elf_reloc, SZ)(&ehdr, fd, must_swab, translate_fn, + translate_opaque, data, ph, elf_machine); + } else { + addr = ph->p_paddr; + } + + if (data_swab) { + elf_word j; + for (j = 0; j < file_size; j += (1 << data_swab)) { + uint8_t *dp = data + j; + switch (data_swab) { + case (1): + *(uint16_t *)dp = bswap16(*(uint16_t *)dp); + break; + case (2): + *(uint32_t *)dp = bswap32(*(uint32_t *)dp); + break; + case (3): + *(uint64_t *)dp = bswap64(*(uint64_t *)dp); + break; + default: + g_assert_not_reached(); + } + } + } + + /* the entry pointer in the ELF header is a virtual + * address, if the text segments paddr and vaddr differ + * we need to adjust the entry */ + if (pentry && !translate_fn && + ph->p_vaddr != ph->p_paddr && + ehdr.e_entry >= ph->p_vaddr && + ehdr.e_entry < ph->p_vaddr + ph->p_filesz && + ph->p_flags & PF_X) { + *pentry = ehdr.e_entry - ph->p_vaddr + ph->p_paddr; + } + + /* Some ELF files really do have segments of zero size; + * just ignore them rather than trying to create empty + * ROM blobs, because the zero-length blob can falsely + * trigger the overlapping-ROM-blobs check. + */ + if (mem_size != 0) { + if (load_rom) { + g_autofree char *label = + g_strdup_printf("%s ELF program header segment %d", + name, i); + + /* + * rom_add_elf_program() takes its own reference to + * 'mapped_file'. + */ + rom_add_elf_program(label, mapped_file, data, file_size, + mem_size, addr, as); + } else { + MemTxResult res; + + res = address_space_write(as ? as : &address_space_memory, + addr, MEMTXATTRS_UNSPECIFIED, + data, file_size); + if (res != MEMTX_OK) { + goto fail; + } + /* + * We need to zero'ify the space that is not copied + * from file + */ + if (file_size < mem_size) { + res = address_space_set(as ? as : &address_space_memory, + addr + file_size, 0, + mem_size - file_size, + MEMTXATTRS_UNSPECIFIED); + if (res != MEMTX_OK) { + goto fail; + } + } + } + } + + total_size += mem_size; + if (addr < low) + low = addr; + if ((addr + mem_size) > high) + high = addr + mem_size; + + data = NULL; + + } else if (ph->p_type == PT_NOTE && elf_note_fn) { + struct elf_note *nhdr = NULL; + + file_size = ph->p_filesz; /* Size of the range of ELF notes */ + data_offset = ph->p_offset; /* Offset where the notes are located */ + + if (file_size > 0) { + if (g_mapped_file_get_length(mapped_file) < + file_size + data_offset) { + goto fail; + } + + data = (uint8_t *)g_mapped_file_get_contents(mapped_file); + data += data_offset; + } + + /* + * Search the ELF notes to find one with a type matching the + * value passed in via 'translate_opaque' + */ + nhdr = (struct elf_note *)data; + assert(translate_opaque != NULL); + nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, + *(uint64_t *)translate_opaque); + if (nhdr != NULL) { + elf_note_fn((void *)nhdr, (void *)&ph->p_align, SZ == 64); + } + data = NULL; + } + } + + if (lowaddr) { + *lowaddr = low; + } + if (highaddr) { + *highaddr = high; + } + ret = total_size; + fail: + if (mapped_file) { + g_mapped_file_unref(mapped_file); + } + g_free(phdr); + return ret; +} diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h index 7f3259a630..f066ab7262 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,26 @@ * */ +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)) @@ -211,6 +230,23 @@ struct smbios_type_8 { 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 */ struct smbios_type_11 { struct smbios_structure_header header; @@ -290,13 +326,16 @@ 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); +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/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h index 904eecf62c..e1e6c54333 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -75,6 +75,9 @@ struct AspeedGPIOClass { uint32_t nr_gpio_pins; uint32_t nr_gpio_sets; const AspeedGPIOReg *reg_table; + unsigned reg_table_count; + uint64_t mem_size; + const MemoryRegionOps *reg_ops; }; struct AspeedGPIOState { @@ -87,7 +90,7 @@ struct AspeedGPIOState { qemu_irq irq; qemu_irq gpios[ASPEED_GPIO_MAX_NR_SETS][ASPEED_GPIOS_PER_SET]; -/* Parallel GPIO Registers */ + /* Parallel GPIO Registers */ uint32_t debounce_regs[ASPEED_GPIO_NR_DEBOUNCE_REGS]; struct GPIOSets { uint32_t data_value; /* Reflects pin values */ 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 adc904d6c1..2c4c81bd20 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -31,12 +31,21 @@ #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" +#define TYPE_ASPEED_2700_I2C TYPE_ASPEED_I2C "-ast2700" OBJECT_DECLARE_TYPE(AspeedI2CState, AspeedI2CClass, ASPEED_I2C) #define ASPEED_I2C_NR_BUSSES 16 -#define ASPEED_I2C_MAX_POOL_SIZE 0x800 +#define ASPEED_I2C_SHARE_POOL_SIZE 0x800 +#define ASPEED_I2C_BUS_POOL_SIZE 0x20 #define ASPEED_I2C_OLD_NUM_REG 11 -#define ASPEED_I2C_NEW_NUM_REG 22 +#define ASPEED_I2C_NEW_NUM_REG 28 + +#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 @@ -132,10 +141,11 @@ REG32(I2CD_CMD, 0x14) /* I2CD Command/Status */ 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, 5) + SHARED_FIELD(RX_COUNT, 24, 6) SHARED_FIELD(RX_SIZE, 16, 5) - SHARED_FIELD(TX_COUNT, 9, 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) @@ -217,6 +227,15 @@ REG32(I2CS_DMA_LEN_STS, 0x4c) FIELD(I2CS_DMA_LEN_STS, TX_LEN, 0, 13) REG32(I2CC_DMA_ADDR, 0x50) REG32(I2CC_DMA_LEN, 0x54) +/* DMA 64bits */ +REG32(I2CM_DMA_TX_ADDR_HI, 0x60) + FIELD(I2CM_DMA_TX_ADDR_HI, ADDR_HI, 0, 7) +REG32(I2CM_DMA_RX_ADDR_HI, 0x64) + FIELD(I2CM_DMA_RX_ADDR_HI, ADDR_HI, 0, 7) +REG32(I2CS_DMA_TX_ADDR_HI, 0x68) + FIELD(I2CS_DMA_TX_ADDR_HI, ADDR_HI, 0, 7) +REG32(I2CS_DMA_RX_ADDR_HI, 0x6c) + FIELD(I2CS_DMA_RX_ADDR_HI, ADDR_HI, 0, 7) struct AspeedI2CState; @@ -231,12 +250,15 @@ struct AspeedI2CBus { I2CSlave *slave; MemoryRegion mr; + MemoryRegion mr_pool; I2CBus *bus; uint8_t id; qemu_irq irq; uint32_t regs[ASPEED_I2C_NEW_NUM_REG]; + uint8_t pool[ASPEED_I2C_BUS_POOL_SIZE]; + uint64_t dma_dram_offset; }; struct AspeedI2CState { @@ -249,7 +271,7 @@ struct AspeedI2CState { uint32_t ctrl_global; uint32_t new_clk_divider; MemoryRegion pool_iomem; - uint8_t pool[ASPEED_I2C_MAX_POOL_SIZE]; + uint8_t share_pool[ASPEED_I2C_SHARE_POOL_SIZE]; AspeedI2CBus busses[ASPEED_I2C_NR_BUSSES]; MemoryRegion *dram_mr; @@ -267,15 +289,19 @@ struct AspeedI2CClass { uint8_t num_busses; uint8_t reg_size; + uint32_t reg_gap_size; uint8_t gap; qemu_irq (*bus_get_irq)(AspeedI2CBus *); uint64_t pool_size; hwaddr pool_base; + uint32_t pool_gap_size; uint8_t *(*bus_pool_base)(AspeedI2CBus *); bool check_sram; bool has_dma; - + bool has_share_pool; + uint64_t mem_size; + bool has_dma64; }; static inline bool aspeed_i2c_is_new_mode(AspeedI2CState *s) @@ -355,14 +381,6 @@ static inline uint32_t aspeed_i2c_bus_dma_len_offset(AspeedI2CBus *bus) 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), 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 9b9581d230..2a3abacd1b 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -141,6 +141,8 @@ int i2c_start_send(I2CBus *bus, uint8_t address); */ 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); 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 93f5d57c9d..f195c11384 100644 --- a/include/hw/i2c/pmbus_device.h +++ b/include/hw/i2c/pmbus_device.h @@ -243,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) @@ -258,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) @@ -444,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 * @@ -501,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 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 46d973e629..d372cd396b 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -109,7 +109,43 @@ struct VTDAddressSpace { QLIST_ENTRY(VTDAddressSpace) next; /* Superset of notifier flags that this address space has */ IOMMUNotifierFlag notifier_flags; - IOVATree *iova_tree; /* Traces mapped IOVA ranges */ + /* + * @iova_tree traces mapped IOVA ranges. + * + * The tree is not needed if no MAP notifier is registered with current + * VTD address space, because all guest invalidate commands can be + * directly passed to the IOMMU UNMAP notifiers without any further + * reshuffling. + * + * The tree OTOH is required for MAP typed iommu notifiers for a few + * reasons. + * + * Firstly, there's no way to identify whether an PSI (Page Selective + * Invalidations) or DSI (Domain Selective Invalidations) event is an + * MAP or UNMAP event within the message itself. Without having prior + * knowledge of existing state vIOMMU doesn't know whether it should + * notify MAP or UNMAP for a PSI message it received when caching mode + * is enabled (for MAP notifiers). + * + * Secondly, PSI messages received from guest driver can be enlarged in + * range, covers but not limited to what the guest driver wanted to + * invalidate. When the range to invalidates gets bigger than the + * limit of a PSI message, it can even become a DSI which will + * invalidate the whole domain. If the vIOMMU directly notifies the + * registered device with the unmodified range, it may confuse the + * registered drivers (e.g. vfio-pci) on either: + * + * (1) Trying to map the same region more than once (for + * VFIO_IOMMU_MAP_DMA, -EEXIST will trigger), or, + * + * (2) Trying to UNMAP a range that is still partially mapped. + * + * That accuracy is not required for UNMAP-only notifiers, but it is a + * must-to-have for notifiers registered with MAP events, because the + * vIOMMU needs to make sure the shadow page table is always in sync + * with the guest IOMMU pgtables for a device. + */ + IOVATree *iova_tree; }; struct VTDIOTLBEntry { @@ -142,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 */ @@ -254,6 +292,8 @@ struct IntelIOMMUState { /* list of registered notifiers */ QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; + GHashTable *vtd_host_iommu_dev; /* HostIOMMUDevice */ + /* interrupt remapping */ bool intr_enabled; /* Whether guest enabled IR */ dma_addr_t intr_root; /* Interrupt remapping table pointer */ @@ -266,6 +306,9 @@ struct IntelIOMMUState { bool dma_translation; /* Whether DMA translation supported */ bool pasid; /* Whether to support PASID */ + /* Transient Mapping, Reserved(0) since VTD spec revision 3.2 */ + bool stale_tm; + /* * Protects IOMMU states in general. Currently it protects the * per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace. diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h index fad97a891d..b9ac34a3ef 100644 --- a/include/hw/i386/microvm.h +++ b/include/hw/i386/microvm.h @@ -78,6 +78,8 @@ struct MicrovmMachineClass { X86MachineClass parent; HotplugHandler *(*orig_hotplug_handler)(MachineState *machine, DeviceState *dev); + void (*x86_load_linux)(X86MachineState *x86ms, FWCfgState *fw_cfg, + int acpi_data_size, bool pvh_enabled); }; struct MicrovmMachineState { diff --git a/include/hw/i386/nitro_enclave.h b/include/hw/i386/nitro_enclave.h new file mode 100644 index 0000000000..b65875033c --- /dev/null +++ b/include/hw/i386/nitro_enclave.h @@ -0,0 +1,62 @@ +/* + * AWS nitro-enclave machine + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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_I386_NITRO_ENCLAVE_H +#define HW_I386_NITRO_ENCLAVE_H + +#include "crypto/hash.h" +#include "hw/i386/microvm.h" +#include "qom/object.h" +#include "hw/virtio/virtio-nsm.h" + +/* Machine type options */ +#define NITRO_ENCLAVE_VSOCK_CHARDEV_ID "vsock" +#define NITRO_ENCLAVE_ID "id" +#define NITRO_ENCLAVE_PARENT_ROLE "parent-role" +#define NITRO_ENCLAVE_PARENT_ID "parent-id" + +struct NitroEnclaveMachineClass { + MicrovmMachineClass parent; + + void (*parent_init)(MachineState *state); + void (*parent_reset)(MachineState *machine, ResetType type); +}; + +struct NitroEnclaveMachineState { + MicrovmMachineState parent; + + /* Machine type options */ + char *vsock; + /* Enclave identifier */ + char *id; + /* Parent instance IAM role ARN */ + char *parent_role; + /* Parent instance identifier */ + char *parent_id; + + /* Machine state */ + VirtIONSM *vnsm; + + /* kernel + ramdisks + cmdline sha384 hash */ + uint8_t image_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* kernel + boot ramdisk + cmdline sha384 hash */ + uint8_t bootstrap_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* application ramdisk(s) hash */ + uint8_t app_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + /* certificate fingerprint hash */ + uint8_t fingerprint_sha384[QCRYPTO_HASH_DIGEST_LEN_SHA384]; + bool signature_found; +}; + +#define TYPE_NITRO_ENCLAVE_MACHINE MACHINE_TYPE_NAME("nitro-enclave") +OBJECT_DECLARE_TYPE(NitroEnclaveMachineState, NitroEnclaveMachineClass, + NITRO_ENCLAVE_MACHINE) + +#endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 0c76e82626..14ee06287d 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -9,14 +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: @@ -33,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; @@ -50,6 +50,7 @@ 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 */ @@ -61,7 +62,6 @@ typedef struct 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" @@ -74,11 +74,6 @@ typedef struct PCMachineState { * * Compat fields: * - * @enforce_aligned_dimm: check that DIMM's address/size is aligned by - * backend's alignment value if provided - * @acpi_data_size: Size of the chunk of memory at the top of RAM - * for the BIOS ACPI tables and other BIOS - * datastructures. * @gigabyte_align: Make sure that guest addresses aligned at * 1Gbyte boundaries get mapped to host * addresses aligned at 1Gbyte boundaries. This @@ -93,8 +88,7 @@ struct PCMachineClass { /* Device configuration: */ bool pci_enabled; - bool kvmclock_enabled; - const char *default_nic_model; + const char *default_south_bridge; /* Compat options: */ @@ -103,22 +97,19 @@ struct PCMachineClass { /* ACPI compat: */ bool has_acpi_build; - bool rsdp_in_ram; - int legacy_acpi_table_size; - unsigned acpi_data_size; 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; + bool isa_bios_alias; /* generate legacy CPU hotplug AML */ bool legacy_cpu_hotplug; @@ -128,6 +119,12 @@ struct PCMachineClass { /* create kvmclock device even when KVM PV features are not exposed */ bool kvmclock_create_always; + + /* + * 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" @@ -138,12 +135,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,27 +149,52 @@ void pc_guest_info_init(PCMachineState *pcms); #define PCI_HOST_PROP_PCI_HOLE64_SIZE "pci-hole64-size" #define PCI_HOST_BELOW_4G_MEM_SIZE "below-4g-mem-size" #define PCI_HOST_ABOVE_4G_MEM_SIZE "above-4g-mem-size" +#define PCI_HOST_PROP_SMM_RANGES "smm-ranges" +typedef enum { + SEV_DESC_TYPE_UNDEF, + /* The section contains the region that must be validated by the VMM. */ + SEV_DESC_TYPE_SNP_SEC_MEM, + /* The section contains the SNP secrets page */ + SEV_DESC_TYPE_SNP_SECRETS, + /* The section contains address that can be used as a CPUID page */ + SEV_DESC_TYPE_CPUID, + /* The section contains the region for kernel hashes for measured direct boot */ + SEV_DESC_TYPE_SNP_KERNEL_HASHES = 0x10, -void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, +} ovmf_sev_metadata_desc_type; + +typedef struct __attribute__((__packed__)) OvmfSevMetadataDesc { + uint32_t base; + uint32_t len; + ovmf_sev_metadata_desc_type type; +} OvmfSevMetadataDesc; + +typedef struct __attribute__((__packed__)) OvmfSevMetadata { + uint8_t signature[4]; + uint32_t len; + uint32_t version; + uint32_t num_desc; + OvmfSevMetadataDesc descs[]; +} OvmfSevMetadata; + +OvmfSevMetadata *pc_system_get_ovmf_sev_metadata_ptr(void); + +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_cmos_init(PCMachineState *pcms, - BusState *ide0, BusState *ide1, - ISADevice *s); void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus); void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs); @@ -189,14 +212,27 @@ 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_9_1[]; +extern const size_t pc_compat_9_1_len; + +extern GlobalProperty pc_compat_9_0[]; +extern const size_t pc_compat_9_0_len; + +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; @@ -266,27 +302,6 @@ extern const size_t pc_compat_2_4_len; extern GlobalProperty pc_compat_2_3[]; extern const size_t pc_compat_2_3_len; -extern GlobalProperty pc_compat_2_2[]; -extern const size_t pc_compat_2_2_len; - -extern GlobalProperty pc_compat_2_1[]; -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; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ { \ @@ -305,4 +320,32 @@ extern const size_t pc_compat_1_4_len; } \ type_init(pc_machine_init_##suffix) +#define DEFINE_PC_VER_MACHINE(namesym, namestr, initfn, ...) \ + static void MACHINE_VER_SYM(init, namesym, __VA_ARGS__)( \ + MachineState *machine) \ + { \ + initfn(machine); \ + } \ + static void MACHINE_VER_SYM(class_init, namesym, __VA_ARGS__)( \ + ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + MACHINE_VER_SYM(options, namesym, __VA_ARGS__)(mc); \ + mc->init = MACHINE_VER_SYM(init, namesym, __VA_ARGS__); \ + MACHINE_VER_DEPRECATION(__VA_ARGS__); \ + } \ + static const TypeInfo MACHINE_VER_SYM(info, namesym, __VA_ARGS__) = \ + { \ + .name = MACHINE_VER_TYPE_NAME(namestr, __VA_ARGS__), \ + .parent = TYPE_PC_MACHINE, \ + .class_init = MACHINE_VER_SYM(class_init, namesym, __VA_ARGS__), \ + }; \ + static void MACHINE_VER_SYM(register, namesym, __VA_ARGS__)(void) \ + { \ + MACHINE_VER_DELETION(__VA_ARGS__); \ + type_register(&MACHINE_VER_SYM(info, namesym, __VA_ARGS__)); \ + } \ + type_init(MACHINE_VER_SYM(register, namesym, __VA_ARGS__)); + #endif diff --git a/include/hw/i386/sgx-epc.h b/include/hw/i386/sgx-epc.h index 581fac389a..41d55da479 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" @@ -57,6 +58,7 @@ typedef struct SGXEPCState { int nr_sections; } SGXEPCState; +bool check_sgx_support(void); bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); void sgx_epc_build_srat(GArray *table_data); diff --git a/include/hw/i386/topology.h b/include/hw/i386/topology.h index 81573f6cfd..b2c8bf2de1 100644 --- a/include/hw/i386/topology.h +++ b/include/hw/i386/topology.h @@ -24,42 +24,47 @@ #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; * CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width(). */ - +#include "qapi/qapi-types-machine-common.h" #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; typedef struct X86CPUTopoIDs { unsigned pkg_id; unsigned die_id; + unsigned module_id; unsigned core_id; unsigned smt_id; } X86CPUTopoIDs; typedef struct X86CPUTopoInfo { unsigned dies_per_pkg; - unsigned cores_per_die; + unsigned modules_per_die; + unsigned cores_per_module; unsigned threads_per_core; } X86CPUTopoInfo; -/* Return the bit width needed for 'count' IDs - */ +#define CPU_TOPOLOGY_LEVEL_INVALID CPU_TOPOLOGY_LEVEL__MAX + +/* Return the bit width needed for 'count' IDs */ static unsigned apicid_bitwidth_for_count(unsigned count) { g_assert(count >= 1); @@ -67,18 +72,22 @@ 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); + return apicid_bitwidth_for_count(topo_info->cores_per_module); +} + +/* Bit width of the Module_ID field */ +static inline unsigned apicid_module_width(X86CPUTopoInfo *topo_info) +{ + return apicid_bitwidth_for_count(topo_info->modules_per_die); } /* Bit width of the Die_ID field */ @@ -87,27 +96,32 @@ 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); } -/* Bit offset of the Die_ID field */ -static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) +/* Bit offset of the Module_ID field */ +static inline unsigned apicid_module_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 Die_ID field */ +static inline unsigned apicid_die_offset(X86CPUTopoInfo *topo_info) +{ + return apicid_module_offset(topo_info) + apicid_module_width(topo_info); +} + +/* 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. */ @@ -116,11 +130,13 @@ static inline apic_id_t x86_apicid_from_topo_ids(X86CPUTopoInfo *topo_info, { return (topo_ids->pkg_id << apicid_pkg_offset(topo_info)) | (topo_ids->die_id << apicid_die_offset(topo_info)) | + (topo_ids->module_id << apicid_module_offset(topo_info)) | (topo_ids->core_id << apicid_core_offset(topo_info)) | topo_ids->smt_id; } -/* Calculate thread/core/package IDs for a specific topology, +/* + * 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, @@ -128,16 +144,22 @@ static inline void x86_topo_ids_from_idx(X86CPUTopoInfo *topo_info, X86CPUTopoIDs *topo_ids) { unsigned nr_dies = topo_info->dies_per_pkg; - unsigned nr_cores = topo_info->cores_per_die; + unsigned nr_modules = topo_info->modules_per_die; + unsigned nr_cores = topo_info->cores_per_module; unsigned nr_threads = topo_info->threads_per_core; - topo_ids->pkg_id = cpu_index / (nr_dies * nr_cores * nr_threads); - topo_ids->die_id = cpu_index / (nr_cores * nr_threads) % nr_dies; + topo_ids->pkg_id = cpu_index / (nr_dies * nr_modules * + nr_cores * nr_threads); + topo_ids->die_id = cpu_index / (nr_modules * nr_cores * + nr_threads) % nr_dies; + topo_ids->module_id = cpu_index / (nr_cores * nr_threads) % + nr_modules; topo_ids->core_id = cpu_index / nr_threads % nr_cores; topo_ids->smt_id = cpu_index % nr_threads; } -/* Calculate thread/core/package IDs for a specific topology, +/* + * 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, @@ -149,13 +171,17 @@ static inline void x86_topo_ids_from_apicid(apic_id_t apicid, topo_ids->core_id = (apicid >> apicid_core_offset(topo_info)) & ~(0xFFFFFFFFUL << apicid_core_width(topo_info)); + topo_ids->module_id = + (apicid >> apicid_module_offset(topo_info)) & + ~(0xFFFFFFFFUL << apicid_module_width(topo_info)); topo_ids->die_id = (apicid >> apicid_die_offset(topo_info)) & ~(0xFFFFFFFFUL << apicid_die_width(topo_info)); topo_ids->pkg_id = apicid >> apicid_pkg_offset(topo_info); } -/* Make APIC ID for the CPU 'cpu_index' +/* + * Make APIC ID for the CPU 'cpu_index' * * 'cpu_index' is a sequential, contiguous ID for the CPU. */ @@ -167,4 +193,13 @@ static inline apic_id_t x86_apicid_from_cpu_idx(X86CPUTopoInfo *topo_info, return x86_apicid_from_topo_ids(topo_info, &topo_ids); } +/* + * Check whether there's extended topology level (module or die)? + */ +static inline bool x86_has_extended_topo(unsigned long *topo_bitmap) +{ + return test_bit(CPU_TOPOLOGY_LEVEL_MODULE, topo_bitmap) || + test_bit(CPU_TOPOLOGY_LEVEL_DIE, topo_bitmap); +} + #endif /* HW_I386_TOPOLOGY_H */ 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 df82c5fd42..d43cb3908e 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -18,13 +18,12 @@ #define HW_I386_X86_H #include "exec/hwaddr.h" -#include "qemu/notify.h" +#include "exec/memory.h" -#include "hw/i386/topology.h" #include "hw/boards.h" -#include "hw/nmi.h" +#include "hw/i386/topology.h" +#include "hw/intc/ioapic.h" #include "hw/isa/isa.h" -#include "hw/i386/ioapic.h" #include "qom/object.h" struct X86MachineClass { @@ -37,6 +36,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 { @@ -53,6 +54,18 @@ struct X86MachineState { GMappedFile *initrd_mapped_file; HotplugHandler *acpi_dev; + /* + * Map the whole BIOS just underneath the 4 GiB address boundary. Only used + * in the ROM (-bios) case. + */ + MemoryRegion bios; + + /* + * Map the upper 128 KiB of the BIOS just underneath the 1 MiB address + * boundary. + */ + MemoryRegion isa_bios; + /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; @@ -60,7 +73,6 @@ struct X86MachineState { 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; @@ -99,17 +111,10 @@ struct X86MachineState { 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, +uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index); -void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp); void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); -CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, - unsigned cpu_index); -int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx); -const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms); -CPUArchId *x86_find_cpu_slot(MachineState *ms, uint32_t id, int *idx); void x86_rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count); void x86_cpu_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); @@ -120,7 +125,9 @@ void x86_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, void x86_cpu_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); -void x86_bios_rom_init(MachineState *ms, const char *default_firmware, +void x86_isa_bios_init(MemoryRegion *isa_bios, MemoryRegion *isa_memory, + MemoryRegion *bios, bool read_only); +void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, MemoryRegion *rom_memory, bool isapc_ram_fw); void x86_load_linux(X86MachineState *x86ms, @@ -133,7 +140,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 { @@ -144,10 +150,10 @@ 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 */ -void x86_firmware_configure(void *ptr, int size); +void x86_firmware_configure(hwaddr gpa, void *ptr, int size); #endif 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..8d217895ba --- /dev/null +++ b/include/hw/ide/ide-dev.h @@ -0,0 +1,187 @@ +/* + * 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; + bool locked; + 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/internal.h b/include/hw/ide/internal.h deleted file mode 100644 index 9e9336a96c..0000000000 --- a/include/hw/ide/internal.h +++ /dev/null @@ -1,661 +0,0 @@ -#ifndef HW_IDE_INTERNAL_H -#define HW_IDE_INTERNAL_H - -/* - * 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 - */ - -#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" - -/* 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 -/* ATA1,3: Defined as '1'. - * ATA2: Reserved. - * ATA3-7: obsolete. */ -#define ATA_DEV_ALWAYS_ON 0xA0 -#define ATA_DEV_LBA 0x40 -#define ATA_DEV_LBA_MSB 0x0F /* LBA 24:27 */ -#define ATA_DEV_HS 0x0F /* HS 3:0 */ - - -/* 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 - -/* 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 */ - -/* Bits of HD_NSECTOR */ -#define CD 0x01 -#define IO 0x02 -#define REL 0x04 -#define TAG_MASK 0xf8 - -/* Bits of Device Control register */ -#define IDE_CTRL_HOB 0x80 -#define IDE_CTRL_RESET 0x04 -#define IDE_CTRL_DISABLE_IRQ 0x02 - -/* ACS-2 T13/2015-D Table B.2 Command codes */ -#define WIN_NOP 0x00 -/* reserved 0x01..0x02 */ -#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 -/* 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_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_READDMA_QUEUED_EXT 0x26 /* 48-Bit, obsolete since ACS2 */ -#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */ -/* reserved 0x28 */ -#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_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_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 */ -/* 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_ONCE 0x41 /* 28-Bit - w/o retries, obsolete since ATA5 */ -#define WIN_VERIFY_EXT 0x42 /* 48-Bit */ -/* reserved 0x43..0x44 */ -/* WRITE UNCORRECTABLE EXT 0x45 */ -/* reserved 0x46 */ -/* READ LOG DMA EXT 0x47 */ -/* reserved 0x48..0x4F */ -/* obsolete since ATA4 0x50 */ -/* CONFIGURE STREAM 0x51 */ -/* reserved 0x52..0x56 */ -/* WRITE LOG DMA EXT 0x57 */ -/* reserved 0x58..0x5A */ -/* TRUSTED NON DATA 0x5B */ -/* TRUSTED RECEIVE 0x5C */ -/* TRUSTED RECEIVE DMA 0x5D */ -/* TRUSTED SEND 0x5E */ -/* TRUSTED SEND DMA 0x5F */ -/* READ FPDMA QUEUED 0x60 */ -/* WRITE FPDMA QUEUED 0x61 */ -/* reserved 0x62->0x6F */ -#define WIN_SEEK 0x70 /* obsolete since ATA7 */ -/* reserved 0x71-0x7F */ -/* vendor specific 0x80-0x86 */ -#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */ -/* vendor specific 0x88-0x8F */ -#define WIN_DIAGNOSE 0x90 -#define WIN_SPECIFY 0x91 /* set drive geometry translation, obsolete since ATA6 */ -#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 */ -#define WIN_STANDBY2 0x96 /* retired in ATA4 */ -#define WIN_SETIDLE2 0x97 /* retired in ATA4 */ -#define WIN_CHECKPOWERMODE2 0x98 /* retired in ATA4 */ -#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_QUEUED_SERVICE 0xA2 /* obsolete since ACS2 */ -/* reserved 0xA3..0xAF */ -#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 -/* reserved 0xBC..0xBF */ -#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_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers, obsolete since ACS2 */ -#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_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 */ -/* WRITE MULTIPLE FUA EXT 0xCE */ -/* reserved 0xCF..0xDO */ -/* CHECK MEDIA CARD TYPE 0xD1 */ -/* reserved for media card pass through 0xD2..0xD4 */ -/* reserved 0xD5..0xD9 */ -#define WIN_GETMEDIASTATUS 0xDA /* obsolete since ATA8 */ -/* 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 */ -/* READ BUFFER DMA 0xE9 */ -#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */ -/* WRITE BUFFER DMA 0xEB */ -#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 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 CFA_WEAR_LEVEL 0xF5 /* microdrives implement as NOP; not specified in T13! */ -#define WIN_SECURITY_DISABLE 0xF6 -/* vendor specific 0xF7 */ -#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 */ -#define MAX_MULT_SECTORS 16 - -#define IDE_DMA_BUF_SECTORS 256 - -/* feature values for Data Set Management */ -#define DSM_TRIM 0x01 - -#if (IDE_DMA_BUF_SECTORS < MAX_MULT_SECTORS) -#error "IDE_DMA_BUF_SECTORS must be bigger or equal to MAX_MULT_SECTORS" -#endif - -/* ATAPI defines */ - -#define ATAPI_PACKET_SIZE 12 - -/* 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_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_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 -/* 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 -/* This seems to be a SCSI specific CD-ROM opcode - * to play data at track/index */ -#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 ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */ -#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */ -#define ATAPI_INT_REASON_REL 0x04 -#define ATAPI_INT_REASON_TAG 0xf8 - -/* same constants as bochs */ -#define ASC_NO_SEEK_COMPLETE 0x02 -#define ASC_ILLEGAL_OPCODE 0x20 -#define ASC_LOGICAL_BLOCK_OOR 0x21 -#define ASC_INV_FIELD_IN_CMD_PACKET 0x24 -#define ASC_MEDIUM_MAY_HAVE_CHANGED 0x28 -#define ASC_INCOMPATIBLE_FORMAT 0x30 -#define ASC_MEDIUM_NOT_PRESENT 0x3a -#define ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39 -#define ASC_DATA_PHASE_ERROR 0x4b -#define ASC_MEDIA_REMOVAL_PREVENTED 0x53 - -#define CFA_NO_ERROR 0x00 -#define CFA_MISC_ERROR 0x09 -#define CFA_INVALID_COMMAND 0x20 -#define CFA_INVALID_ADDRESS 0x21 -#define CFA_ADDRESS_OVERFLOW 0x2f - -#define SMART_READ_DATA 0xd0 -#define SMART_READ_THRESH 0xd1 -#define SMART_ATTR_AUTOSAVE 0xd2 -#define SMART_SAVE_ATTR 0xd3 -#define SMART_EXECUTE_OFFLINE 0xd4 -#define SMART_READ_LOG 0xd5 -#define SMART_WRITE_LOG 0xd6 -#define SMART_ENABLE 0xd8 -#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]; - -#define ide_cmd_is_read(s) \ - ((s)->dma_cmd == IDE_DMA_READ) - -typedef struct IDEBufferedRequest { - QLIST_ENTRY(IDEBufferedRequest) list; - QEMUIOVector qiov; - QEMUIOVector *original_qiov; - BlockCompletionFunc *original_cb; - void *original_opaque; - bool orphaned; -} IDEBufferedRequest; - -/* 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]; - 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 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 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; - bool locked; - 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 -#define IDE_RETRY_PIO 0x10 -#define IDE_RETRY_ATAPI 0x20 /* reused IDE_RETRY_READ bit */ -#define IDE_RETRY_READ 0x20 -#define IDE_RETRY_FLUSH 0x40 -#define IDE_RETRY_TRIM 0x80 -#define IDE_RETRY_HBA 0x100 - -#define IS_IDE_RETRY_DMA(_status) \ - ((_status) & IDE_RETRY_DMA) - -#define IS_IDE_RETRY_PIO(_status) \ - ((_status) & IDE_RETRY_PIO) - -/* - * The method of the IDE_RETRY_ATAPI determination is to use a previously - * impossible bit combination as a new status value. - */ -#define IS_IDE_RETRY_ATAPI(_status) \ - (((_status) & IDE_RETRY_MASK) == IDE_RETRY_ATAPI) - -static inline uint8_t ide_dma_cmd_to_retry(uint8_t dma_cmd) -{ - switch (dma_cmd) { - case IDE_DMA_READ: - return IDE_RETRY_DMA | IDE_RETRY_READ; - case IDE_DMA_WRITE: - return IDE_RETRY_DMA; - case IDE_DMA_TRIM: - return IDE_RETRY_DMA | IDE_RETRY_TRIM; - case IDE_DMA_ATAPI: - return IDE_RETRY_ATAPI; - default: - break; - } - return 0; -} - -static inline IDEState *idebus_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; - -#define VMSTATE_IDE_BUS(_field, _state) \ - VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_bus, IDEBus) - -#define VMSTATE_IDE_BUS_ARRAY(_field, _state, _num) \ - VMSTATE_STRUCT_ARRAY(_field, _state, _num, 1, vmstate_ide_bus, IDEBus) - -extern const VMStateDescription vmstate_ide_drive; - -#define VMSTATE_IDE_DRIVES(_field, _state) \ - VMSTATE_STRUCT_ARRAY(_field, _state, 2, 3, vmstate_ide_drive, IDEState) - -#define VMSTATE_IDE_DRIVE(_field, _state) \ - VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) - -void ide_bus_reset(IDEBus *bus); -int64_t ide_get_sector(IDEState *s); -void ide_set_sector(IDEState *s, int64_t sector_num); - -void ide_start_dma(IDEState *s, BlockCompletionFunc *cb); -void dma_buf_commit(IDEState *s, uint32_t tx_bytes); -void ide_dma_error(IDEState *s); -void ide_abort_command(IDEState *s); - -void ide_atapi_cmd_ok(IDEState *s); -void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc); -void ide_atapi_dma_restart(IDEState *s); -void ide_atapi_io_error(IDEState *s, int ret); - -void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val); -uint32_t ide_ioport_read(void *opaque, uint32_t addr1); -uint32_t ide_status_read(void *opaque, uint32_t addr); -void ide_ctrl_write(void *opaque, uint32_t addr, uint32_t val); -void ide_data_writew(void *opaque, uint32_t addr, uint32_t val); -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); -void ide_exit(IDEState *s); -int ide_init_ioport(IDEBus *bus, ISADevice *isa, int iobase, int iobase2); -void ide_register_restart_cb(IDEBus *bus); - -void ide_exec_cmd(IDEBus *bus, uint32_t val); - -void ide_transfer_start(IDEState *s, uint8_t *buf, int size, - EndTransferFunc *end_transfer_func); -bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, - EndTransferFunc *end_transfer_func); -void ide_transfer_stop(IDEState *s); -void ide_set_inactive(IDEState *s, bool more); -BlockAIOCB *ide_issue_trim( - int64_t offset, QEMUIOVector *qiov, - BlockCompletionFunc *cb, void *cb_opaque, void *opaque); -BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, - QEMUIOVector *iov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque); -void ide_cancel_dma_sync(IDEState *s); - -/* hw/ide/atapi.c */ -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/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/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 9fb3f8d787..e90f008b66 100644 --- a/include/hw/input/i8042.h +++ b/include/hw/input/i8042.h @@ -89,7 +89,6 @@ struct MMIOKBDState { 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/lm832x.h b/include/hw/input/lm832x.h deleted file mode 100644 index e0e5d5ef20..0000000000 --- a/include/hw/input/lm832x.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * National Semiconductor LM8322/8323 GPIO keyboard & PWM chips. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef HW_INPUT_LM832X_H -#define HW_INPUT_LM832X_H - -#define TYPE_LM8323 "lm8323" - -void lm832x_key_event(DeviceState *dev, int key, int state); - -#endif diff --git a/include/hw/input/pl050.h b/include/hw/input/pl050.h index 89ec4fafc9..4cb8985f31 100644 --- a/include/hw/input/pl050.h +++ b/include/hw/input/pl050.h @@ -10,7 +10,6 @@ #ifndef HW_PL050_H #define HW_PL050_H -#include "qemu/osdep.h" #include "hw/sysbus.h" #include "migration/vmstate.h" #include "hw/input/ps2.h" diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index ff777582cd..cd61a634c3 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -36,7 +36,7 @@ struct PS2DeviceClass { SysBusDeviceClass parent_class; - DeviceReset parent_reset; + ResettablePhases parent_phases; }; /* 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 deleted file mode 100644 index 5b76ebc177..0000000000 --- a/include/hw/input/tsc2xxx.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * TI touchscreen controller - * - * Copyright (c) 2006 Andrzej Zaborowski - * Copyright (C) 2008 Nokia 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. - */ - -#ifndef HW_INPUT_TSC2XXX_H -#define HW_INPUT_TSC2XXX_H - -typedef struct MouseTransformInfo { - /* Touchscreen resolution */ - int x; - int y; - /* Calibration values as used/generated by tslib */ - int a[7]; -} MouseTransformInfo; - -typedef struct uWireSlave { - uint16_t (*receive)(void *opaque); - void (*send)(void *opaque, uint16_t data); - void *opaque; -} uWireSlave; - -/* tsc210x.c */ -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_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); - -#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_gic_common.h b/include/hw/intc/arm_gic_common.h index 7080375008..97fea4102d 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -71,6 +71,8 @@ struct GICState { qemu_irq parent_fiq[GIC_NCPU]; qemu_irq parent_virq[GIC_NCPU]; qemu_irq parent_vfiq[GIC_NCPU]; + qemu_irq parent_nmi[GIC_NCPU]; + qemu_irq parent_vnmi[GIC_NCPU]; qemu_irq maintenance_irq[GIC_NCPU]; /* GICD_CTLR; for a GIC with the security extensions the NS banked version diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index ab5182a28a..a3d6a0e507 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -51,13 +51,13 @@ /* Maximum number of list registers (architectural limit) */ #define GICV3_LR_MAX 16 -/* For some distributor fields we want to model the array of 32-bit +/* + * For some distributor fields we want to model the array of 32-bit * register values which hold various bitmaps corresponding to enabled, - * pending, etc bits. These macros and functions facilitate that; the - * APIs are generally modelled on the generic bitmap.h functions - * (which are unsuitable here because they use 'unsigned long' as the - * underlying storage type, which is very awkward when you need to - * access the data as 32-bit values.) + * pending, etc bits. We use the set_bit32() etc family of functions + * from bitops.h for this. For a few cases we need to implement some + * extra operations. + * * Each bitmap contains a bit for each interrupt. Although there is * space for the PPIs and SGIs, those bits (the first 32) are never * used as that state lives in the redistributor. The unused bits are @@ -65,39 +65,13 @@ * avoids bugs where we forget to subtract GIC_INTERNAL from an * interrupt number. */ -#define GICV3_BMP_SIZE DIV_ROUND_UP(GICV3_MAXIRQ, 32) - -#define GIC_DECLARE_BITMAP(name) \ - uint32_t name[GICV3_BMP_SIZE] - -#define GIC_BIT_MASK(nr) (1U << ((nr) % 32)) -#define GIC_BIT_WORD(nr) ((nr) / 32) - -static inline void gic_bmp_set_bit(int nr, uint32_t *addr) -{ - uint32_t mask = GIC_BIT_MASK(nr); - uint32_t *p = addr + GIC_BIT_WORD(nr); - - *p |= mask; -} - -static inline void gic_bmp_clear_bit(int nr, uint32_t *addr) -{ - uint32_t mask = GIC_BIT_MASK(nr); - uint32_t *p = addr + GIC_BIT_WORD(nr); - - *p &= ~mask; -} - -static inline int gic_bmp_test_bit(int nr, const uint32_t *addr) -{ - return 1U & (addr[GIC_BIT_WORD(nr)] >> (nr & 31)); -} +#define GIC_DECLARE_BITMAP(name) DECLARE_BITMAP32(name, GICV3_MAXIRQ) +#define GICV3_BMP_SIZE BITS_TO_U32S(GICV3_MAXIRQ) static inline void gic_bmp_replace_bit(int nr, uint32_t *addr, int val) { - uint32_t mask = GIC_BIT_MASK(nr); - uint32_t *p = addr + GIC_BIT_WORD(nr); + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); *p &= ~mask; *p |= (val & 1U) << (nr % 32); @@ -106,7 +80,7 @@ static inline void gic_bmp_replace_bit(int nr, uint32_t *addr, int val) /* Return a pointer to the 32-bit word containing the specified bit. */ static inline uint32_t *gic_bmp_ptr32(uint32_t *addr, int nr) { - return addr + GIC_BIT_WORD(nr); + return addr + BIT32_WORD(nr); } typedef struct GICv3State GICv3State; @@ -146,6 +120,7 @@ typedef struct { int irq; uint8_t prio; int grp; + bool nmi; } PendingIrq; struct GICv3CPUState { @@ -155,6 +130,8 @@ struct GICv3CPUState { qemu_irq parent_fiq; qemu_irq parent_virq; qemu_irq parent_vfiq; + qemu_irq parent_nmi; + qemu_irq parent_vnmi; /* Redistributor */ uint32_t level; /* Current IRQ level */ @@ -170,6 +147,7 @@ struct GICv3CPUState { uint32_t gicr_ienabler0; uint32_t gicr_ipendr0; uint32_t gicr_iactiver0; + uint32_t gicr_inmir0; uint32_t edge_trigger; /* ICFGR0 and ICFGR1 even bits */ uint32_t gicr_igrpmodr0; uint32_t gicr_nsacr; @@ -221,6 +199,13 @@ struct GICv3CPUState { /* This is temporary working state, to avoid a malloc in gicv3_update() */ bool seenbetter; + + /* + * Whether the CPU interface has NMI support (FEAT_GICv3_NMI). The + * CPU interface may support NMIs even when the GIC proper (what the + * spec calls the IRI; the redistributors and distributor) does not. + */ + bool nmi_support; }; /* @@ -247,6 +232,7 @@ struct GICv3State { uint32_t num_irq; uint32_t revision; bool lpi_enable; + bool nmi_support; bool security_extn; bool force_8bit_prio; bool irq_reset_nonsecure; @@ -272,6 +258,7 @@ struct GICv3State { GIC_DECLARE_BITMAP(active); /* GICD_ISACTIVER */ GIC_DECLARE_BITMAP(level); /* Current level */ GIC_DECLARE_BITMAP(edge_trigger); /* GICD_ICFGR even bits */ + GIC_DECLARE_BITMAP(nmi); /* GICD_INMIR */ uint8_t gicd_ipriority[GICV3_MAXIRQ]; uint64_t gicd_irouter[GICV3_MAXIRQ]; /* Cached information: pointer to the cpu i/f for the CPUs specified @@ -288,15 +275,15 @@ struct GICv3State { #define GICV3_BITMAP_ACCESSORS(BMP) \ static inline void gicv3_gicd_##BMP##_set(GICv3State *s, int irq) \ { \ - gic_bmp_set_bit(irq, s->BMP); \ + set_bit32(irq, s->BMP); \ } \ static inline int gicv3_gicd_##BMP##_test(GICv3State *s, int irq) \ { \ - return gic_bmp_test_bit(irq, s->BMP); \ + return test_bit32(irq, s->BMP); \ } \ static inline void gicv3_gicd_##BMP##_clear(GICv3State *s, int irq) \ { \ - gic_bmp_clear_bit(irq, s->BMP); \ + clear_bit32(irq, s->BMP); \ } \ static inline void gicv3_gicd_##BMP##_replace(GICv3State *s, \ int irq, int value) \ @@ -311,6 +298,7 @@ GICV3_BITMAP_ACCESSORS(pending) GICV3_BITMAP_ACCESSORS(active) GICV3_BITMAP_ACCESSORS(level) GICV3_BITMAP_ACCESSORS(edge_trigger) +GICV3_BITMAP_ACCESSORS(nmi) #define TYPE_ARM_GICV3_COMMON "arm-gicv3-common" typedef struct ARMGICv3CommonClass ARMGICv3CommonClass; @@ -329,4 +317,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/aspeed_intc.h b/include/hw/intc/aspeed_intc.h new file mode 100644 index 0000000000..18cb43476c --- /dev/null +++ b/include/hw/intc/aspeed_intc.h @@ -0,0 +1,44 @@ +/* + * ASPEED INTC Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ASPEED_INTC_H +#define ASPEED_INTC_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "hw/or-irq.h" + +#define TYPE_ASPEED_INTC "aspeed.intc" +#define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" +OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) + +#define ASPEED_INTC_NR_REGS (0x2000 >> 2) +#define ASPEED_INTC_NR_INTS 9 + +struct AspeedINTCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + uint32_t regs[ASPEED_INTC_NR_REGS]; + OrIRQState orgates[ASPEED_INTC_NR_INTS]; + qemu_irq output_pins[ASPEED_INTC_NR_INTS]; + + uint32_t enable[ASPEED_INTC_NR_INTS]; + uint32_t mask[ASPEED_INTC_NR_INTS]; + uint32_t pending[ASPEED_INTC_NR_INTS]; +}; + +struct AspeedINTCClass { + SysBusDeviceClass parent_class; + + uint32_t num_lines; + uint32_t num_ints; +}; + +#endif /* ASPEED_INTC_H */ 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/intc/grlib_irqmp.h b/include/hw/intc/grlib_irqmp.h new file mode 100644 index 0000000000..a76acbf940 --- /dev/null +++ b/include/hw/intc/grlib_irqmp.h @@ -0,0 +1,41 @@ +/* + * QEMU GRLIB Components + * + * 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 + * 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 GRLIB_IRQMP_H +#define GRLIB_IRQMP_H + +#include "hw/sysbus.h" + +/* Emulation of GrLib device is base on the GRLIB IP Core User's Manual: + * http://www.gaisler.com/products/grlib/grip.pdf + */ + +/* IRQMP */ +#define TYPE_GRLIB_IRQMP "grlib-irqmp" + +void grlib_irqmp_ack(DeviceState *dev, unsigned int cpu, int intno); + +#endif /* GRLIB_IRQMP_H */ diff --git a/include/hw/intc/i8259.h b/include/hw/intc/i8259.h index e2b1e8c59a..1f2420231f 100644 --- a/include/hw/intc/i8259.h +++ b/include/hw/intc/i8259.h @@ -3,10 +3,20 @@ /* i8259.c */ -extern DeviceState *isa_pic; -qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq); +typedef struct PICCommonState PICCommonState; + +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/intc/intc.h b/include/hw/intc/intc.h index 7018f608ca..e40194b8e3 100644 --- a/include/hw/intc/intc.h +++ b/include/hw/intc/intc.h @@ -22,7 +22,7 @@ struct InterruptStatsProviderClass { */ bool (*get_statistics)(InterruptStatsProvider *obj, uint64_t **irq_counts, unsigned int *nb_irqs); - void (*print_info)(InterruptStatsProvider *obj, Monitor *mon); + void (*print_info)(InterruptStatsProvider *obj, GString *buf); }; #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..626a37dfa1 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 @@ -37,26 +39,51 @@ #define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) +#define EXTIOI_SIZE 0x800 + +#define EXTIOI_VIRT_BASE (0x40000000) +#define EXTIOI_VIRT_SIZE (0x1000) +#define EXTIOI_VIRT_FEATURES (0x0) +#define EXTIOI_HAS_VIRT_EXTENSION (0) +#define EXTIOI_HAS_ENABLE_OPTION (1) +#define EXTIOI_HAS_INT_ENCODE (2) +#define EXTIOI_HAS_CPU_ENCODE (3) +#define EXTIOI_VIRT_HAS_FEATURES (BIT(EXTIOI_HAS_VIRT_EXTENSION) \ + | BIT(EXTIOI_HAS_ENABLE_OPTION) \ + | BIT(EXTIOI_HAS_CPU_ENCODE)) +#define EXTIOI_VIRT_CONFIG (0x4) +#define EXTIOI_ENABLE (1) +#define EXTIOI_ENABLE_INT_ENCODE (2) +#define EXTIOI_ENABLE_CPU_ENCODE (3) +#define EXTIOI_VIRT_COREMAP_START (0x40) +#define EXTIOI_VIRT_COREMAP_END (0x240) + +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; + uint32_t features; + uint32_t status; /* 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; + MemoryRegion virt_extend; }; #endif /* LOONGARCH_EXTIOI_H */ diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 0ee48fca55..276b3040a3 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -1,53 +1,25 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * LoongArch ipi interrupt header files + * LoongArch IPI interrupt header files * - * Copyright (C) 2021 Loongson Technology Corporation Limited + * Copyright (C) 2024 Loongson Technology Corporation Limited */ #ifndef HW_LOONGARCH_IPI_H #define HW_LOONGARCH_IPI_H -#include "hw/sysbus.h" +#include "qom/object.h" +#include "hw/intc/loongson_ipi_common.h" -/* Mainy used by iocsr read and write */ -#define SMP_IPI_MAILBOX 0x1000ULL -#define CORE_STATUS_OFF 0x0 -#define CORE_EN_OFF 0x4 -#define CORE_SET_OFF 0x8 -#define CORE_CLEAR_OFF 0xc -#define CORE_BUF_20 0x20 -#define CORE_BUF_28 0x28 -#define CORE_BUF_30 0x30 -#define CORE_BUF_38 0x38 -#define IOCSR_IPI_SEND 0x40 -#define IOCSR_MAIL_SEND 0x48 -#define IOCSR_ANY_SEND 0x158 +#define TYPE_LOONGARCH_IPI "loongarch_ipi" +OBJECT_DECLARE_TYPE(LoongarchIPIState, LoongarchIPIClass, LOONGARCH_IPI) -#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) +struct LoongarchIPIState { + LoongsonIPICommonState parent_obj; +}; -#define MAX_IPI_CORE_NUM 4 -#define MAX_IPI_MBX_NUM 4 - -#define TYPE_LOONGARCH_IPI "loongarch_ipi" -OBJECT_DECLARE_SIMPLE_TYPE(LoongArchIPI, LOONGARCH_IPI) - -typedef struct IPICore { - uint32_t status; - uint32_t en; - uint32_t set; - uint32_t clear; - /* 64bit buf divide into 2 32bit buf */ - uint32_t buf[MAX_IPI_MBX_NUM * 2]; - qemu_irq irq; -} IPICore; - -struct LoongArchIPI { - SysBusDevice parent_obj; - MemoryRegion ipi_iocsr_mem[MAX_IPI_CORE_NUM]; - MemoryRegion ipi64_iocsr_mem[MAX_IPI_CORE_NUM]; +struct LoongarchIPIClass { + LoongsonIPICommonClass parent_class; }; #endif diff --git a/include/hw/intc/loongarch_pch_msi.h b/include/hw/intc/loongarch_pch_msi.h index 6d67560dea..b8586fb3b6 100644 --- a/include/hw/intc/loongarch_pch_msi.h +++ b/include/hw/intc/loongarch_pch_msi.h @@ -5,18 +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/loongson_ipi.h b/include/hw/intc/loongson_ipi.h new file mode 100644 index 0000000000..4e517cc8dc --- /dev/null +++ b/include/hw/intc/loongson_ipi.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson ipi interrupt header files + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGSON_IPI_H +#define HW_LOONGSON_IPI_H + +#include "qom/object.h" +#include "hw/intc/loongson_ipi_common.h" +#include "hw/sysbus.h" + +#define TYPE_LOONGSON_IPI "loongson_ipi" +OBJECT_DECLARE_TYPE(LoongsonIPIState, LoongsonIPIClass, LOONGSON_IPI) + +struct LoongsonIPIClass { + LoongsonIPICommonClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; +}; + +struct LoongsonIPIState { + LoongsonIPICommonState parent_obj; + + MemoryRegion *ipi_mmio_mem; +}; + +#endif diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h new file mode 100644 index 0000000000..df9d9c5168 --- /dev/null +++ b/include/hw/intc/loongson_ipi_common.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Loongson ipi interrupt header files + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGSON_IPI_COMMON_H +#define HW_LOONGSON_IPI_COMMON_H + +#include "qom/object.h" +#include "hw/sysbus.h" +#include "exec/memattrs.h" + +#define IPI_MBX_NUM 4 + +#define TYPE_LOONGSON_IPI_COMMON "loongson_ipi_common" +OBJECT_DECLARE_TYPE(LoongsonIPICommonState, + LoongsonIPICommonClass, LOONGSON_IPI_COMMON) + +typedef struct IPICore { + LoongsonIPICommonState *ipi; + uint32_t status; + uint32_t en; + uint32_t set; + uint32_t clear; + /* 64bit buf divide into 2 32-bit buf */ + uint32_t buf[IPI_MBX_NUM * 2]; + qemu_irq irq; +} IPICore; + +struct LoongsonIPICommonState { + SysBusDevice parent_obj; + + MemoryRegion ipi_iocsr_mem; + MemoryRegion ipi64_iocsr_mem; + uint32_t num_cpu; + IPICore *cpu; +}; + +struct LoongsonIPICommonClass { + SysBusDeviceClass parent_class; + + DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; + AddressSpace *(*get_iocsr_as)(CPUState *cpu); + CPUState *(*cpu_by_arch_id)(int64_t id); +}; + +MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs); +MemTxResult loongson_ipi_core_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs); + +/* Mainy used by iocsr read and write */ +#define SMP_IPI_MAILBOX 0x1000ULL + +#define CORE_STATUS_OFF 0x0 +#define CORE_EN_OFF 0x4 +#define CORE_SET_OFF 0x8 +#define CORE_CLEAR_OFF 0xc +#define CORE_BUF_20 0x20 +#define CORE_BUF_28 0x28 +#define CORE_BUF_30 0x30 +#define CORE_BUF_38 0x38 +#define IOCSR_IPI_SEND 0x40 +#define IOCSR_MAIL_SEND 0x48 +#define IOCSR_ANY_SEND 0x158 + +#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) + +#endif 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 deleted file mode 100644 index ac507b9d74..0000000000 --- a/include/hw/intc/nios2_vic.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Vectored Interrupt Controller for nios2 processor - * - * Copyright (c) 2022 Neuroblade - * - * Interface: - * QOM property "cpu": link to the Nios2 CPU (must be set) - * Unnamed GPIO inputs 0..NIOS2_VIC_MAX_IRQ-1: input IRQ lines - * IRQ should be connected to nios2 IRQ0. - * - * Reference: "Embedded Peripherals IP User Guide - * for Intel® Quartus® Prime Design Suite: 21.4" - * Chapter 38 "Vectored Interrupt Controller Core" - * See: https://www.intel.com/content/www/us/en/docs/programmable/683130/21-4/vectored-interrupt-controller-core.html - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef HW_INTC_NIOS2_VIC_H -#define HW_INTC_NIOS2_VIC_H - -#define TYPE_NIOS2_VIC "nios2-vic" -OBJECT_DECLARE_SIMPLE_TYPE(Nios2VIC, NIOS2_VIC) - -#define NIOS2_VIC_MAX_IRQ 32 - -struct Nios2VIC { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - qemu_irq output_int; - - /* properties */ - CPUState *cpu; - MemoryRegion csr; - - uint32_t int_config[NIOS2_VIC_MAX_IRQ]; - uint32_t vic_config; - uint32_t int_raw_status; - uint32_t int_enable; - uint32_t sw_int; - uint32_t vic_status; - uint32_t vec_tbl_base; - uint32_t vec_tbl_addr; -}; - -#endif /* HW_INTC_NIOS2_VIC_H */ 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/irq.h b/include/hw/irq.h index 645b73d251..c861c1debd 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -1,9 +1,20 @@ #ifndef QEMU_IRQ_H #define QEMU_IRQ_H +#include "qom/object.h" + /* Generic IRQ/GPIO pin infrastructure. */ #define TYPE_IRQ "irq" +OBJECT_DECLARE_SIMPLE_TYPE(IRQState, IRQ) + +struct IRQState { + Object parent_obj; + + qemu_irq_handler handler; + void *opaque; + int n; +}; void qemu_set_irq(qemu_irq irq, int level); @@ -23,6 +34,13 @@ static inline void qemu_irq_pulse(qemu_irq irq) qemu_set_irq(irq, 0); } +/* + * Init a single IRQ. The irq is assigned with a handler, an opaque data + * and the interrupt number. + */ +void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, + int n); + /* Returns an array of N IRQs. Each IRQ is assigned the argument handler and * opaque data. */ 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 1e21dc9694..b196dec800 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -11,7 +11,7 @@ #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) @@ -48,10 +48,6 @@ struct IsaDmaClass { void *opaque); }; -struct ISADeviceClass { - DeviceClass parent_class; -}; - struct ISABus { /*< private >*/ BusState parent_obj; @@ -59,7 +55,7 @@ struct ISABus { MemoryRegion *address_space; MemoryRegion *address_space_io; - qemu_irq *irqs; + qemu_irq *irqs_in; IsaDma *dma[2]; }; @@ -73,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. @@ -130,9 +135,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))); -} - #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 eaa07881c5..da1722daf2 100644 --- a/include/hw/isa/vt82c686.h +++ b/include/hw/isa/vt82c686.h @@ -1,7 +1,8 @@ #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_USB_UHCI "vt82c686b-usb-uhci" @@ -10,6 +11,29 @@ #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/kvm/clock.h b/include/hw/kvm/clock.h deleted file mode 100644 index 7994071c4f..0000000000 --- a/include/hw/kvm/clock.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * QEMU KVM support, paravirtual clock device - * - * Copyright (C) 2011 Siemens AG - * - * Authors: - * Jan Kiszka - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - */ - -#ifndef HW_KVM_CLOCK_H -#define HW_KVM_CLOCK_H - -#ifdef CONFIG_KVM - -void kvmclock_create(bool create_always); - -#else /* CONFIG_KVM */ - -static inline void kvmclock_create(bool create_always) -{ -} - -#endif /* !CONFIG_KVM */ - -#endif diff --git a/include/hw/loader.h b/include/hw/loader.h index 70248e0da7..7f6d06b956 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -77,14 +77,31 @@ ssize_t load_image_targphys(const char *filename, hwaddr, 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 + * load_image_gzipped_buffer() will read. It prevents * g_malloc() in those functions from allocating a huge amount of memory. */ #define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) 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 @@ -251,12 +268,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; - ssize_t rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, - bool option_rom, MemoryRegion *mr, AddressSpace *as); + 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, @@ -322,7 +336,6 @@ void *rom_ptr(hwaddr addr, size_t size); * rom_ptr(). */ void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size); -void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ rom_add_file(_f, NULL, _a, _i, false, NULL, NULL) diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h new file mode 100644 index 0000000000..b3b870df1f --- /dev/null +++ b/include/hw/loongarch/boot.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Definitions for LoongArch boot. + * + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef HW_LOONGARCH_BOOT_H +#define HW_LOONGARCH_BOOT_H + +/* UEFI 2.10 */ +#define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249 +#define EFI_2_100_SYSTEM_TABLE_REVISION ((2<<16) | (100)) +#define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION +#define EFI_SYSTEM_TABLE_REVISION EFI_2_100_SYSTEM_TABLE_REVISION + +#define FW_VERSION 0x1 +#define FW_PATCHLEVEL 0x0 + +typedef struct { + uint8_t b[16]; +} efi_guid_t QEMU_ALIGNED(8); + +#define EFI_GUID(a, b, c, d...) (efi_guid_t){ { \ + (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, d } } + +#define LINUX_EFI_BOOT_MEMMAP_GUID \ + EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, \ + 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) + +#define LINUX_EFI_INITRD_MEDIA_GUID \ + EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, \ + 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) + +#define DEVICE_TREE_GUID \ + EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, 0x83, 0x0b, \ + 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0) + +struct efi_config_table { + efi_guid_t guid; + uint64_t *ptr; + const char name[16]; +}; + +typedef struct { + uint64_t signature; + uint32_t revision; + uint32_t headersize; + uint32_t crc32; + uint32_t reserved; +} efi_table_hdr_t; + +struct efi_configuration_table { + efi_guid_t guid; + void *table; +}; + +struct efi_system_table { + efi_table_hdr_t hdr; + uint64_t fw_vendor; /* physical addr of CHAR16 vendor string */ + uint32_t fw_revision; + uint64_t con_in_handle; + uint64_t *con_in; + uint64_t con_out_handle; + uint64_t *con_out; + uint64_t stderr_handle; + uint64_t stderr_placeholder; + uint64_t *runtime; + uint64_t *boottime; + uint64_t nr_tables; + struct efi_configuration_table *tables; +}; + +typedef struct { + uint32_t type; + uint32_t pad; + uint64_t phys_addr; + uint64_t virt_addr; + uint64_t num_pages; + uint64_t attribute; +} efi_memory_desc_t; + +struct efi_boot_memmap { + uint64_t map_size; + uint64_t desc_size; + uint32_t desc_ver; + uint64_t map_key; + uint64_t buff_size; + efi_memory_desc_t map[32]; +}; + +struct efi_initrd { + uint64_t base; + uint64_t size; +}; + +struct loongarch_boot_info { + uint64_t ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; + uint64_t a0, a1, a2; +}; + +extern struct memmap_entry *memmap_table; +extern unsigned memmap_entries; + +struct memmap_entry { + uint64_t address; + uint64_t length; + uint32_t type; + uint32_t reserved; +}; + +void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info); + +#endif /* HW_LOONGARCH_BOOT_H */ diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 45c383f5a7..9ba47793ef 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -8,50 +8,61 @@ #ifndef HW_LOONGARCH_H #define HW_LOONGARCH_H -#include "target/loongarch/cpu.h" #include "hw/boards.h" #include "qemu/queue.h" -#include "hw/intc/loongarch_ipi.h" +#include "hw/block/flash.h" +#include "hw/loongarch/boot.h" -#define LOONGARCH_MAX_VCPUS 4 +#define LOONGARCH_MAX_CPUS 256 -#define VIRT_ISA_IO_BASE 0x18000000UL -#define VIRT_ISA_IO_SIZE 0x0004000 #define VIRT_FWCFG_BASE 0x1e020000UL #define VIRT_BIOS_BASE 0x1c000000UL -#define VIRT_BIOS_SIZE (4 * MiB) +#define VIRT_BIOS_SIZE (16 * MiB) +#define VIRT_FLASH_SECTOR_SIZE (256 * 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 0x90000000 +#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 { +#define COMMAND_LINE_SIZE 512 + +#define FDT_BASE 0x100000 + +struct LoongArchVirtMachineState { /*< 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; + OnOffAuto veiointc; 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; + struct loongarch_boot_info bootinfo; }; -#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); +#define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") +OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) +void loongarch_acpi_setup(LoongArchVirtMachineState *lvms); #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..c0a58087cc 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,10 +143,33 @@ 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); + Error **errp); void memory_device_plug(MemoryDeviceState *md, MachineState *ms); void memory_device_unplug(MemoryDeviceState *md, MachineState *ms); uint64_t memory_device_get_region_size(const MemoryDeviceState *md, diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index acf887c83d..d3b763453a 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -77,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/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 322bebe555..fe0f3ea963 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -66,8 +66,7 @@ struct PCDIMMDeviceClass { void (*unrealize)(PCDIMMDevice *dimm); }; -void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, - const uint64_t *legacy_align, Error **errp); +void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp); void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine); void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine); #endif 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 fffb0b7da8..c32f6c2835 100644 --- a/include/hw/mips/bootloader.h +++ b/include/hw/mips/bootloader.h @@ -11,16 +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, +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/cps.h b/include/hw/mips/cps.h index 04d636246a..05ef9f76b7 100644 --- a/include/hw/mips/cps.h +++ b/include/hw/mips/cps.h @@ -38,6 +38,7 @@ struct MIPSCPSState { uint32_t num_vp; uint32_t num_irq; char *cpu_type; + bool cpu_is_bigendian; MemoryRegion container; MIPSGCRState gcr; 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_hace.h b/include/hw/misc/aspeed_hace.h index ecb1b67de8..4af9919195 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -1,6 +1,7 @@ /* * ASPEED Hash and Crypto Engine * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (C) 2021 IBM Corp. * * SPDX-License-Identifier: GPL-2.0-or-later @@ -10,6 +11,7 @@ #define ASPEED_HACE_H #include "hw/sysbus.h" +#include "crypto/hash.h" #define TYPE_ASPEED_HACE "aspeed.hace" #define TYPE_ASPEED_AST2400_HACE TYPE_ASPEED_HACE "-ast2400" @@ -35,6 +37,8 @@ struct AspeedHACEState { MemoryRegion *dram_mr; AddressSpace dram_as; + + QCryptoHash *hash_ctx; }; 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_scu.h b/include/hw/misc/aspeed_scu.h index 5c7c04eedf..356be95e45 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -19,10 +19,13 @@ OBJECT_DECLARE_TYPE(AspeedSCUState, AspeedSCUClass, ASPEED_SCU) #define TYPE_ASPEED_2400_SCU TYPE_ASPEED_SCU "-ast2400" #define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500" #define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600" +#define TYPE_ASPEED_2700_SCU TYPE_ASPEED_SCU "-ast2700" +#define TYPE_ASPEED_2700_SCUIO TYPE_ASPEED_SCU "io" "-ast2700" #define TYPE_ASPEED_1030_SCU TYPE_ASPEED_SCU "-ast1030" #define ASPEED_SCU_NR_REGS (0x1A8 >> 2) #define ASPEED_AST2600_SCU_NR_REGS (0xE20 >> 2) +#define ASPEED_AST2700_SCU_NR_REGS (0xE20 >> 2) struct AspeedSCUState { /*< private >*/ @@ -31,7 +34,7 @@ struct AspeedSCUState { /*< public >*/ MemoryRegion iomem; - uint32_t regs[ASPEED_AST2600_SCU_NR_REGS]; + uint32_t regs[ASPEED_AST2700_SCU_NR_REGS]; uint32_t silicon_rev; uint32_t hw_strap1; uint32_t hw_strap2; @@ -48,10 +51,13 @@ struct AspeedSCUState { #define AST2600_A3_SILICON_REV 0x05030303U #define AST1030_A0_SILICON_REV 0x80000000U #define AST1030_A1_SILICON_REV 0x80010000U +#define AST2700_A0_SILICON_REV 0x06000103U +#define AST2720_A0_SILICON_REV 0x06000203U +#define AST2750_A0_SILICON_REV 0x06000003U #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 { @@ -87,7 +93,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); * 1. 2012/12/29 Ryan Chen Create */ -/* SCU08 Clock Selection Register +/* + * SCU08 Clock Selection Register * * 31 Enable Video Engine clock dynamic slow down * 30:28 Video Engine clock slow down setting @@ -109,7 +116,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); */ #define SCU_CLK_GET_PCLK_DIV(x) (((x) >> 23) & 0x7) -/* SCU24 H-PLL Parameter Register (for Aspeed AST2400 SOC) +/* + * SCU24 H-PLL Parameter Register (for Aspeed AST2400 SOC) * * 18 H-PLL parameter selection * 0: Select H-PLL by strapping resistors @@ -127,7 +135,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); #define SCU_AST2400_H_PLL_BYPASS_EN (0x1 << 17) #define SCU_AST2400_H_PLL_OFF (0x1 << 16) -/* SCU24 H-PLL Parameter Register (for Aspeed AST2500 SOC) +/* + * SCU24 H-PLL Parameter Register (for Aspeed AST2500 SOC) * * 21 Enable H-PLL reset * 20 Enable H-PLL bypass mode @@ -144,7 +153,8 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); #define SCU_H_PLL_BYPASS_EN (0x1 << 20) #define SCU_H_PLL_OFF (0x1 << 19) -/* SCU70 Hardware Strapping Register definition (for Aspeed AST2400 SOC) +/* + * SCU70 Hardware Strapping Register definition (for Aspeed AST2400 SOC) * * 31:29 Software defined strapping registers * 28:27 DRAM size setting (for VGA driver use) @@ -339,6 +349,10 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); #define SCU_AST2600_H_PLL_BYPASS_EN (0x1 << 24) #define SCU_AST2600_H_PLL_OFF (0x1 << 23) +/* STRAP1 SCU500 */ +#define SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC (0x1 << 2) +#define SCU_AST2600_HW_STRAP_BOOT_SRC_SPI (0x0 << 2) + /* * SCU310 Clock Selection Register Set 4 (for Aspeed AST1030 SOC) * @@ -361,4 +375,31 @@ uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s); */ #define SCU_AST1030_CLK_GET_PCLK_DIV(x) (((x) >> 8) & 0xf) +/* + * SCU280 Clock Selection 1 Register (for Aspeed AST2700 SCUIO) + * + * 31:29 MHCLK_DIV + * 28 Reserved + * 27:25 RGMIICLK_DIV + * 24 Reserved + * 23:21 RMIICLK_DIV + * 20:18 PCLK_DIV + * 17:14 SDCLK_DIV + * 13 SDCLK_SEL + * 12 UART13CLK_SEL + * 11 UART12CLK_SEL + * 10 UART11CLK_SEL + * 9 UART10CLK_SEL + * 8 UART9CLK_SEL + * 7 UART8CLK_SEL + * 6 UART7CLK_SEL + * 5 UART6CLK_SEL + * 4 UARTDBCLK_SEL + * 3 UART4CLK_SEL + * 2 UART3CLK_SEL + * 1 UART2CLK_SEL + * 0 UART1CLK_SEL + */ +#define SCUIO_AST2700_CLK_GET_PCLK_DIV(x) (((x) >> 18) & 0x7) + #endif /* ASPEED_SCU_H */ diff --git a/include/hw/misc/aspeed_sdmc.h b/include/hw/misc/aspeed_sdmc.h index ec2d59a14f..61c979583a 100644 --- a/include/hw/misc/aspeed_sdmc.h +++ b/include/hw/misc/aspeed_sdmc.h @@ -17,6 +17,7 @@ OBJECT_DECLARE_TYPE(AspeedSDMCState, AspeedSDMCClass, ASPEED_SDMC) #define TYPE_ASPEED_2400_SDMC TYPE_ASPEED_SDMC "-ast2400" #define TYPE_ASPEED_2500_SDMC TYPE_ASPEED_SDMC "-ast2500" #define TYPE_ASPEED_2600_SDMC TYPE_ASPEED_SDMC "-ast2600" +#define TYPE_ASPEED_2700_SDMC TYPE_ASPEED_SDMC "-ast2700" /* * SDMC has 174 documented registers. In addition the u-boot device tree @@ -29,7 +30,7 @@ OBJECT_DECLARE_TYPE(AspeedSDMCState, AspeedSDMCClass, ASPEED_SDMC) * time, and the other is in the DDR-PHY IP which is used during DDR-PHY * training. */ -#define ASPEED_SDMC_NR_REGS (0x500 >> 2) +#define ASPEED_SDMC_NR_REGS (0x1000 >> 2) struct AspeedSDMCState { /*< private >*/ @@ -41,6 +42,7 @@ struct AspeedSDMCState { uint32_t regs[ASPEED_SDMC_NR_REGS]; uint64_t ram_size; uint64_t max_ram_size; + bool unlocked; }; @@ -51,6 +53,7 @@ struct AspeedSDMCClass { const uint64_t *valid_ram_sizes; uint32_t (*compute_conf)(AspeedSDMCState *s, uint32_t data); void (*write)(AspeedSDMCState *s, uint32_t reg, uint32_t data); + bool is_bus64bit; }; #endif /* ASPEED_SDMC_H */ diff --git a/include/hw/misc/aspeed_sli.h b/include/hw/misc/aspeed_sli.h new file mode 100644 index 0000000000..23f346ab93 --- /dev/null +++ b/include/hw/misc/aspeed_sli.h @@ -0,0 +1,27 @@ +/* + * ASPEED SLI Controller + * + * Copyright (C) 2024 ASPEED Technology Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ASPEED_SLI_H +#define ASPEED_SLI_H + +#include "hw/sysbus.h" + +#define TYPE_ASPEED_SLI "aspeed.sli" +#define TYPE_ASPEED_2700_SLI TYPE_ASPEED_SLI "-ast2700" +#define TYPE_ASPEED_2700_SLIIO TYPE_ASPEED_SLI "io" "-ast2700" +OBJECT_DECLARE_SIMPLE_TYPE(AspeedSLIState, ASPEED_SLI) + +#define ASPEED_SLI_NR_REGS (0x500 >> 2) + +struct AspeedSLIState { + SysBusDevice parent; + MemoryRegion iomem; + + uint32_t regs[ASPEED_SLI_NR_REGS]; +}; + +#endif /* ASPEED_SLI_H */ 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..2f93fd0c75 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -11,6 +11,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/display/bcm2835_fb.h" +#include "hw/nvram/bcm2835_otp.h" #include "qom/object.h" #define TYPE_BCM2835_PROPERTY "bcm2835-property" @@ -26,10 +27,12 @@ struct BCM2835PropertyState { MemoryRegion iomem; qemu_irq mbox_irq; BCM2835FBState *fbdev; + BCM2835OTPState *otp; MACAddr macaddr; uint32_t board_rev; uint32_t addr; + char *command_line; bool pending; }; diff --git a/include/hw/misc/cbus.h b/include/hw/misc/cbus.h deleted file mode 100644 index 5334984020..0000000000 --- a/include/hw/misc/cbus.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * CBUS three-pin bus and the Retu / Betty / Tahvo / Vilma / Avilma / - * Hinku / Vinku / Ahne / Pihi chips used in various Nokia platforms. - * Based on reverse-engineering of a linux driver. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * 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_MISC_CBUS_H -#define HW_MISC_CBUS_H - - -typedef struct { - qemu_irq clk; - qemu_irq dat; - qemu_irq sel; -} CBus; - -CBus *cbus_init(qemu_irq dat_out); -void cbus_attach(CBus *bus, void *slave_opaque); - -void *retu_init(qemu_irq irq, int vilma); -void *tahvo_init(qemu_irq irq, int betty); - -void retu_key_event(void *retu, int state); - -#endif 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 95d30a1745..16aa95b876 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -27,8 +27,8 @@ #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" @@ -80,8 +80,6 @@ struct MACIOIDEState { uint32_t channel; qemu_irq real_ide_irq; qemu_irq real_dma_irq; - qemu_irq ide_irq; - qemu_irq dma_irq; MemoryRegion mem; IDEBus bus; @@ -92,6 +90,11 @@ struct MACIOIDEState { uint32_t irq_reg; }; +#define MACIO_IDE_PMAC_NIRQS 2 + +#define MACIO_IDE_PMAC_DMA_IRQ 0 +#define MACIO_IDE_PMAC_IDE_IRQ 1 + void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); void macio_ide_register_dma(MACIOIDEState *ide); 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..920871a598 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 @@ -155,9 +154,9 @@ struct MOS6522State { OBJECT_DECLARE_TYPE(MOS6522State, MOS6522DeviceClass, MOS6522) struct MOS6522DeviceClass { - DeviceClass parent_class; + SysBusDeviceClass 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 e520566ab0..9a71a5ad0d 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -15,8 +15,15 @@ #ifndef HW_MISC_PVPANIC_H #define HW_MISC_PVPANIC_H +#include "exec/memory.h" #include "qom/object.h" +#include "standard-headers/misc/pvpanic.h" + +#define PVPANIC_EVENTS (PVPANIC_PANICKED | \ + PVPANIC_CRASH_LOADED | \ + PVPANIC_SHUTDOWN) + #define TYPE_PVPANIC_ISA_DEVICE "pvpanic" #define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci" 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/stm32_rcc.h b/include/hw/misc/stm32_rcc.h new file mode 100644 index 0000000000..ffbdf202ea --- /dev/null +++ b/include/hw/misc/stm32_rcc.h @@ -0,0 +1,91 @@ +/* + * STM32 RCC (only reset and enable registers are implemented) + * + * Copyright (c) 2024 Román Cárdenas + * + * 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_STM32_RCC_H +#define HW_STM32_RCC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define STM32_RCC_CR 0x00 +#define STM32_RCC_PLL_CFGR 0x04 +#define STM32_RCC_CFGR 0x08 +#define STM32_RCC_CIR 0x0C +#define STM32_RCC_AHB1_RSTR 0x10 +#define STM32_RCC_AHB2_RSTR 0x14 +#define STM32_RCC_AHB3_RSTR 0x18 + +#define STM32_RCC_APB1_RSTR 0x20 +#define STM32_RCC_APB2_RSTR 0x24 + +#define STM32_RCC_AHB1_ENR 0x30 +#define STM32_RCC_AHB2_ENR 0x34 +#define STM32_RCC_AHB3_ENR 0x38 + +#define STM32_RCC_APB1_ENR 0x40 +#define STM32_RCC_APB2_ENR 0x44 + +#define STM32_RCC_AHB1_LPENR 0x50 +#define STM32_RCC_AHB2_LPENR 0x54 +#define STM32_RCC_AHB3_LPENR 0x58 + +#define STM32_RCC_APB1_LPENR 0x60 +#define STM32_RCC_APB2_LPENR 0x64 + +#define STM32_RCC_BDCR 0x70 +#define STM32_RCC_CSR 0x74 + +#define STM32_RCC_SSCGR 0x80 +#define STM32_RCC_PLLI2SCFGR 0x84 +#define STM32_RCC_PLLSAI_CFGR 0x88 +#define STM32_RCC_DCKCFGR 0x8C +#define STM32_RCC_CKGATENR 0x90 +#define STM32_RCC_DCKCFGR2 0x94 + +#define STM32_RCC_NREGS ((STM32_RCC_DCKCFGR2 >> 2) + 1) +#define STM32_RCC_PERIPHERAL_SIZE 0x400 +#define STM32_RCC_NIRQS (32 * 5) /* 32 bits per reg, 5 en/rst regs */ + +#define STM32_RCC_GPIO_IRQ_OFFSET 0 + +#define TYPE_STM32_RCC "stm32.rcc" + +typedef struct STM32RccState STM32RccState; + +DECLARE_INSTANCE_CHECKER(STM32RccState, STM32_RCC, TYPE_STM32_RCC) + +#define NUM_GPIO_EVENT_IN_LINES 16 + +struct STM32RccState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t regs[STM32_RCC_NREGS]; + + qemu_irq enable_irq[STM32_RCC_NIRQS]; + qemu_irq reset_irq[STM32_RCC_NIRQS]; +}; + +#endif /* HW_STM32_RCC_H */ diff --git a/include/hw/misc/stm32l4x5_exti.h b/include/hw/misc/stm32l4x5_exti.h new file mode 100644 index 0000000000..62f79362f2 --- /dev/null +++ b/include/hw/misc/stm32l4x5_exti.h @@ -0,0 +1,53 @@ +/* + * 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_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]; + + /* used for edge detection */ + uint32_t irq_levels[EXTI_NUM_REGISTER]; + qemu_irq irq[EXTI_NUM_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..c450df2b9e --- /dev/null +++ b/include/hw/misc/stm32l4x5_syscfg.h @@ -0,0 +1,54 @@ +/* + * 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]; + Clock *clk; +}; + +#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..5010401517 --- /dev/null +++ b/include/hw/misc/xlnx-cfi-if.h @@ -0,0 +1,58 @@ +/* + * 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 "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..d96f8f9eff --- /dev/null +++ b/include/hw/misc/xlnx-versal-trng.h @@ -0,0 +1,59 @@ +/* + * 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]; + + RegisterInfoArray *reg_array; + 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/ftgmac100.h b/include/hw/net/ftgmac100.h index 765d1538a4..24ccdf0260 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -14,6 +14,11 @@ #define TYPE_FTGMAC100 "ftgmac100" OBJECT_DECLARE_SIMPLE_TYPE(FTGMAC100State, FTGMAC100) +#define FTGMAC100_MEM_SIZE 0x1000 +#define FTGMAC100_REG_MEM_SIZE 0x100 +#define FTGMAC100_REG_HIGH_MEM_SIZE 0x100 +#define FTGMAC100_REG_HIGH_OFFSET 0x100 + #include "hw/sysbus.h" #include "net/net.h" @@ -30,7 +35,9 @@ struct FTGMAC100State { NICState *nic; NICConf conf; qemu_irq irq; + MemoryRegion iomem_container; MemoryRegion iomem; + MemoryRegion iomem_high; uint8_t frame[FTGMAC100_MAX_FRAME_SIZE]; @@ -38,10 +45,6 @@ struct FTGMAC100State { uint32_t isr; uint32_t ier; uint32_t rx_enabled; - uint32_t rx_ring; - uint32_t rx_descriptor; - uint32_t tx_ring; - uint32_t tx_descriptor; uint32_t math[2]; uint32_t rbsr; uint32_t itc; @@ -54,7 +57,10 @@ struct FTGMAC100State { uint32_t phycr; uint32_t phydata; uint32_t fcr; - + uint64_t rx_ring; + uint64_t rx_descriptor; + uint64_t tx_ring; + uint64_t tx_descriptor; uint32_t phy_status; uint32_t phy_control; @@ -65,6 +71,7 @@ struct FTGMAC100State { bool aspeed; uint32_t txdes0_edotr; uint32_t rxdes0_edorr; + bool dma64; }; #define TYPE_ASPEED_MII "aspeed-mmi" 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/bcm2835_otp.h b/include/hw/nvram/bcm2835_otp.h new file mode 100644 index 0000000000..1df33700bd --- /dev/null +++ b/include/hw/nvram/bcm2835_otp.h @@ -0,0 +1,68 @@ +/* + * BCM2835 One-Time Programmable (OTP) Memory + * + * Copyright (c) 2024 Rayhan Faizel + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BCM2835_OTP_H +#define BCM2835_OTP_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_BCM2835_OTP "bcm2835-otp" +OBJECT_DECLARE_SIMPLE_TYPE(BCM2835OTPState, BCM2835_OTP) + +#define BCM2835_OTP_ROW_COUNT 66 + +/* https://elinux.org/BCM2835_registers#OTP */ +#define BCM2835_OTP_BOOTMODE_REG 0x00 +#define BCM2835_OTP_CONFIG_REG 0x04 +#define BCM2835_OTP_CTRL_LO_REG 0x08 +#define BCM2835_OTP_CTRL_HI_REG 0x0c +#define BCM2835_OTP_STATUS_REG 0x10 +#define BCM2835_OTP_BITSEL_REG 0x14 +#define BCM2835_OTP_DATA_REG 0x18 +#define BCM2835_OTP_ADDR_REG 0x1c +#define BCM2835_OTP_WRITE_DATA_READ_REG 0x20 +#define BCM2835_OTP_INIT_STATUS_REG 0x24 + + +/* -- Row 32: Undocumented -- */ + +#define BCM2835_OTP_ROW_32 32 + +/* Lock OTP Programming (Customer OTP and private key) */ +#define BCM2835_OTP_ROW_32_LOCK BIT(6) + +/* -- Row 36-43: Customer OTP -- */ + +#define BCM2835_OTP_CUSTOMER_OTP 36 +#define BCM2835_OTP_CUSTOMER_OTP_LEN 8 + +/* Magic numbers to lock programming of customer OTP and private key */ +#define BCM2835_OTP_LOCK_NUM1 0xffffffff +#define BCM2835_OTP_LOCK_NUM2 0xaffe0000 + +/* -- Row 56-63: Device-specific private key -- */ + +#define BCM2835_OTP_PRIVATE_KEY 56 +#define BCM2835_OTP_PRIVATE_KEY_LEN 8 + + +struct BCM2835OTPState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + uint32_t otp_rows[BCM2835_OTP_ROW_COUNT]; +}; + + +uint32_t bcm2835_otp_get_row(BCM2835OTPState *s, unsigned int row); +void bcm2835_otp_set_row(BCM2835OTPState *s, unsigned int row, uint32_t value); + +#endif 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 c1f81a5f13..fa42677619 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -59,6 +59,8 @@ typedef struct fw_cfg_dma_access FWCfgDmaAccess; typedef void (*FWCfgCallback)(void *opaque); typedef void (*FWCfgWriteCallback)(void *opaque, off_t start, size_t len); +typedef struct FWCfgEntry FWCfgEntry; + struct FWCfgState { /*< private >*/ SysBusDevice parent_obj; @@ -319,7 +321,6 @@ void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s); FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, AddressSpace *dma_as); -FWCfgState *fw_cfg_init_io(uint32_t iobase); FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, uint32_t data_width, diff --git a/include/hw/nvram/fw_cfg_acpi.h b/include/hw/nvram/fw_cfg_acpi.h new file mode 100644 index 0000000000..dfd2a44ef0 --- /dev/null +++ b/include/hw/nvram/fw_cfg_acpi.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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 index b780aca470..0c4dfaeff6 100644 --- a/include/hw/nvram/mac_nvram.h +++ b/include/hw/nvram/mac_nvram.h @@ -44,6 +44,7 @@ struct MacIONVRAMState { MemoryRegion mem; uint8_t *data; + BlockBackend *blk; }; void pmac_format_nvram_partition(MacIONVRAMState *nvr, int len); 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..bce8e89d90 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 { @@ -47,6 +47,7 @@ struct XlnxBBRam { bool bbram8_wo; bool blk_ro; + RegisterInfoArray *reg_array; uint32_t regs[RMAX_XLNX_BBRAM]; RegisterInfo regs_info[RMAX_XLNX_BBRAM]; }; 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..afa4f4f996 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); @@ -44,6 +44,7 @@ struct XlnxVersalEFuseCtrl { void *extra_pg0_lock_spec; /* Opaque property */ uint32_t extra_pg0_lock_n16; + RegisterInfoArray *reg_array; uint32_t regs[XLNX_VERSAL_EFUSE_CTRL_R_MAX]; RegisterInfo regs_info[XLNX_VERSAL_EFUSE_CTRL_R_MAX]; }; diff --git a/include/hw/nvram/xlnx-zynqmp-efuse.h b/include/hw/nvram/xlnx-zynqmp-efuse.h index 6b051ec4f1..7fb12df3fb 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 { @@ -37,6 +37,7 @@ struct XlnxZynqMPEFuse { qemu_irq irq; XlnxEFuse *efuse; + RegisterInfoArray *reg_array; uint32_t regs[XLNX_ZYNQMP_EFUSE_R_MAX]; RegisterInfo regs_info[XLNX_ZYNQMP_EFUSE_R_MAX]; }; 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..f208397ffe --- /dev/null +++ b/include/hw/pci-bridge/cxl_upstream_port.h @@ -0,0 +1,23 @@ + +#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; + + PCIExpLinkSpeed speed; + PCIExpLinkWidth width; + + DOECap doe_cdat; + uint64_t sn; +} CXLUpstreamPort; + +#endif /* CXL_SUP_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..c484e377a8 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" @@ -34,8 +31,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIEHost, DESIGNWARE_PCIE_HOST) #define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root" OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIERoot, DESIGNWARE_PCIE_ROOT) -struct DesignwarePCIERoot; - typedef struct DesignwarePCIEViewport { DesignwarePCIERoot *root; 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/i440fx.h b/include/hw/pci-host/i440fx.h index d02bf1ed6b..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 *pci_type, - DeviceState *dev, - 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 df7fa55a30..79d4ea8501 100644 --- a/include/hw/pci-host/ls7a.h +++ b/include/hw/pci-host/ls7a.h @@ -8,8 +8,6 @@ #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" @@ -26,26 +24,30 @@ #define VIRT_PCH_REG_BASE 0x10000000UL #define VIRT_IOAPIC_REG_BASE (VIRT_PCH_REG_BASE) #define VIRT_PCH_MSI_ADDR_LOW 0x2FF00000UL +#define VIRT_PCH_REG_SIZE 0x400 +#define VIRT_PCH_MSI_SIZE 0x8 /* - * 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 VIRT_PCH_PIC_IRQ_NUM 32 +#define VIRT_GSI_BASE 64 #define VIRT_DEVICE_IRQS 16 -#define VIRT_PCI_IRQS 48 -#define VIRT_UART_IRQ (PCH_PIC_IRQ_OFFSET + 2) +#define VIRT_UART_COUNT 4 +#define VIRT_UART_IRQ (VIRT_GSI_BASE + 2) #define VIRT_UART_BASE 0x1fe001e0 -#define VIRT_UART_SIZE 0X100 -#define VIRT_RTC_IRQ (PCH_PIC_IRQ_OFFSET + 3) +#define VIRT_UART_SIZE 0x100 +#define VIRT_RTC_IRQ (VIRT_GSI_BASE + 6) #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 (PCH_PIC_IRQ_OFFSET + 4) +#define VIRT_SCI_IRQ (VIRT_GSI_BASE + 7) #define VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000 #define VIRT_PLATFORM_BUS_SIZE 0x2000000 #define VIRT_PLATFORM_BUS_NUM_IRQS 2 -#define VIRT_PLATFORM_BUS_IRQ 69 +#define VIRT_PLATFORM_BUS_IRQ (VIRT_GSI_BASE + 8) #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 4854f6d2f6..24ca3dddaa 100644 --- a/include/hw/pci-host/pnv_phb3.h +++ b/include/hw/pci-host/pnv_phb3.h @@ -10,14 +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 @@ -43,7 +40,7 @@ void pnv_phb3_msi_update_config(Phb3MsiState *msis, uint32_t base, void pnv_phb3_msi_send(Phb3MsiState *msis, uint64_t addr, uint16_t data, int32_t dev_pe); void pnv_phb3_msi_ffi(Phb3MsiState *msis, uint64_t val); -void pnv_phb3_msi_pic_print_info(Phb3MsiState *msis, Monitor *mon); +void pnv_phb3_msi_pic_print_info(Phb3MsiState *msis, GString *buf); /* diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 50d4faa001..8abee78e4d 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -10,16 +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 PnvPHB PnvPHB; -typedef struct PnvChip PnvChip; /* * We have one such address space wrapper per possible device under @@ -119,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,8 +155,9 @@ struct PnvPHB4 { QLIST_HEAD(, PnvPhb4DMASpace) dma_spaces; }; -void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon); +void pnv_phb4_pic_print_info(PnvPHB4 *phb, GString *buf); 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; @@ -175,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]; @@ -189,6 +186,8 @@ struct PnvPhb4PecState { /* PHBs */ uint32_t num_phbs; +#define MAX_PHBS_PER_PEC 3 + PnvPHB *phbs[MAX_PHBS_PER_PEC]; PnvChip *chip; }; @@ -218,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..ddafc3f2e3 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" @@ -50,11 +50,11 @@ struct MCHPCIState { MemoryRegion tseg_blackhole, tseg_window; MemoryRegion smbase_blackhole, smbase_window; bool has_smram_at_smbase; + bool has_smm_ranges; Range pci_hole; 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 +74,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" @@ -186,8 +181,6 @@ struct Q35PCIHost { #define MCH_PCIE_DEV 1 #define MCH_PCIE_FUNC 0 -uint64_t mch_mcfg_base(void); - /* * Arbitrary but unique BNF number for IOAPIC device. * 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..0db87f1281 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -47,15 +47,12 @@ typedef struct SpaprPciLsi { uint32_t irq; } SpaprPciLsi; -typedef struct SpaprPhbPciNvGpuConfig SpaprPhbPciNvGpuConfig; - struct SpaprPhbState { PCIHostState parent_obj; uint32_t index; uint64_t buid; char *dtbusname; - bool dr_enabled; MemoryRegion memspace, iospace; hwaddr mem_win_addr, mem_win_size, mem64_win_addr, mem64_win_size; @@ -86,13 +83,6 @@ struct SpaprPhbState { bool pcie_ecs; /* Allow access to PCIe extended config space? */ /* Fields for migration compatibility hacks */ - bool pre_2_8_migration; - 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 +102,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 +125,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 +151,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 58aa576215..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); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 6ccaaf5154..c0717e3121 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -3,6 +3,7 @@ #include "exec/memory.h" #include "sysemu/dma.h" +#include "sysemu/host_iommu_device.h" /* PCI includes legacy ISA access. */ #include "hw/isa/isa.h" @@ -15,7 +16,7 @@ extern bool pci_available; #define PCI_BUS_NUM(x) (((x) >> 8) & 0xff) #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07) -#define PCI_BUILD_BDF(bus, devfn) ((bus << 8) | (devfn)) +#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 @@ -114,6 +115,8 @@ 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_RISCV_IOMMU 0x0014 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 @@ -166,7 +169,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 @@ -208,25 +210,14 @@ 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 QEMU_PCIE_EXT_TAG_BITNR 13 + QEMU_PCIE_EXT_TAG = (1 << QEMU_PCIE_EXT_TAG_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, @@ -236,32 +227,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); @@ -270,126 +235,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; - - /* 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; -}; - void pci_register_bar(PCIDevice *pci_dev, int region_num, uint8_t attr, MemoryRegion *memory); void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, @@ -434,22 +279,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) { @@ -459,8 +306,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); @@ -472,10 +318,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) @@ -521,10 +366,77 @@ 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); + /** + * @set_iommu_device: attach a HostIOMMUDevice to a vIOMMU + * + * Optional callback, if not implemented in vIOMMU, then vIOMMU can't + * retrieve host information from the associated HostIOMMUDevice. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @dev: the #HostIOMMUDevice to attach. + * + * @errp: pass an Error out only when return false + * + * Returns: true if HostIOMMUDevice is attached or else false with errp set. + */ + bool (*set_iommu_device)(PCIBus *bus, void *opaque, int devfn, + HostIOMMUDevice *dev, Error **errp); + /** + * @unset_iommu_device: detach a HostIOMMUDevice from a vIOMMU + * + * Optional callback. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + */ + void (*unset_iommu_device)(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); +bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, + Error **errp); +void pci_device_unset_iommu_device(PCIDevice *dev); + +/** + * 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); @@ -735,13 +647,11 @@ pci_set_quad_by_mask(uint8_t *config, uint64_t mask, uint64_t reg) pci_set_quad(config, (~mask & val) | (mask & rval)); } -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); @@ -750,11 +660,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); @@ -765,199 +670,6 @@ static inline void pci_irq_deassert(PCIDevice *pci_dev) pci_set_irq(pci_dev, 0); } -/* - * FIXME: PCI does not work this way. - * All the callers to this method should be fixed. - */ -static inline void pci_irq_pulse(PCIDevice *pci_dev) -{ - pci_irq_assert(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..b0f5204d80 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 >*/ @@ -71,11 +72,16 @@ struct PCIBridge { */ MemoryRegion address_space_mem; MemoryRegion address_space_io; + AddressSpace as_mem; + AddressSpace as_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 +89,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 +97,28 @@ 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_CXL_BUS "pxb-cxl-bus" +#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 +155,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 5653175957..2261312546 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -33,7 +33,7 @@ enum PCIBusFlags { 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..8eaf0d58bb --- /dev/null +++ b/include/hw/pci/pci_device.h @@ -0,0 +1,364 @@ +#ifndef QEMU_PCI_DEVICE_H +#define QEMU_PCI_DEVICE_H + +#include "hw/pci/pci.h" +#include "hw/pci/pcie.h" +#include "hw/pci/pcie_doe.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; + + /* SPDM */ + uint16_t spdm_port; + + /* DOE */ + DOECap doe_spdm; + + /* ID of standby device in net_failover pair */ + char *failover_pair_id; + uint32_t acpi_index; + + /* + * Indirect DMA region bounce buffer size as configured for the device. This + * is a configuration parameter that is reflected into bus_master_as when + * realizing the device. + */ + uint32_t max_bounce_buffer_size; +}; + +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 e78f2ef6e1..e068966a9d 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 @@ -169,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 @@ -179,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 diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 698d3de851..b8d59732bc 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -26,15 +26,9 @@ #include "hw/pci/pcie_aer.h" #include "hw/pci/pcie_sriov.h" #include "hw/hotplug.h" -#include "hw/pci/pcie_doe.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 struct PCIEPort PCIEPort; +typedef struct PCIESlot PCIESlot; typedef enum { /* these bits must match the bits in Slot Control/Status registers. @@ -102,6 +96,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); @@ -143,9 +138,11 @@ 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); +void pcie_cap_fill_link_ep_usp(PCIDevice *dev, PCIExpLinkWidth width, + PCIExpLinkSpeed speed); void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); diff --git a/include/hw/pci/pcie_aer.h b/include/hw/pci/pcie_aer.h index 65e71d98fe..4d8c0e0507 100644 --- a/include/hw/pci/pcie_aer.h +++ b/include/hw/pci/pcie_aer.h @@ -25,8 +25,23 @@ /* definitions which PCIExpressDevice uses */ +/* error */ +typedef struct PCIEAERErr { + uint32_t status; /* error status bits */ + uint16_t source_id; /* bdf */ + +#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ +#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ +#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ +#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ + uint16_t flags; + + uint32_t header[4]; /* TLP header */ + uint32_t prefix[4]; /* TLP header prefix */ +} PCIEAERErr; + /* AER log */ -struct PCIEAERLog { +typedef struct PCIEAERLog { /* This structure is saved/loaded. So explicitly size them instead of unsigned int */ @@ -40,7 +55,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 @@ -48,11 +63,11 @@ struct PCIEAERLog { /* Error log. log_max-sized array */ PCIEAERErr *log; -}; +} PCIEAERLog; /* aer error message: error signaling message has only error severity and source id. See 2.2.8.3 error signaling messages */ -struct PCIEAERMsg { +typedef struct PCIEAERMsg { /* * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE} @@ -60,7 +75,7 @@ struct PCIEAERMsg { uint32_t severity; uint16_t source_id; /* bdf */ -}; +} PCIEAERMsg; static inline bool pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) @@ -69,21 +84,6 @@ pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN; } -/* error */ -struct PCIEAERErr { - uint32_t status; /* error status bits */ - uint16_t source_id; /* bdf */ - -#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ -#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ -#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ -#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ - uint16_t flags; - - uint32_t header[4]; /* TLP header */ - uint32_t prefix[4]; /* TLP header prefix */ -}; - extern const VMStateDescription vmstate_pcie_aer_log; int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, @@ -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 index ba4d8b03bd..9e1275db8a 100644 --- a/include/hw/pci/pcie_doe.h +++ b/include/hw/pci/pcie_doe.h @@ -11,7 +11,6 @@ #define PCIE_DOE_H #include "qemu/range.h" -#include "qemu/typedefs.h" #include "hw/register.h" /* @@ -47,6 +46,8 @@ REG32(PCI_DOE_CAP_STATUS, 0) /* PCI-SIG defined Data Object Types - r6.0 Table 6-32 */ #define PCI_SIG_DOE_DISCOVERY 0x00 +#define PCI_SIG_DOE_CMA 0x01 +#define PCI_SIG_DOE_SECURED_CMA 0x02 #define PCI_DOE_DW_SIZE_MAX (1 << 18) #define PCI_DOE_PROTOCOL_NUM_MAX 256 @@ -107,6 +108,9 @@ struct DOECap { /* Protocols and its callback response */ DOEProtocol *protocols; uint16_t protocol_num; + + /* Used for spdm-socket */ + int spdm_socket; }; void pcie_doe_init(PCIDevice *pdev, DOECap *doe_cap, uint16_t offset, diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 7b8193061a..7cd7af8cfa 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,13 +65,13 @@ 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; }; void pcie_chassis_create(uint8_t chassis_number); -PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot); int pcie_chassis_add_slot(struct PCIESlot *slot); void pcie_chassis_del_slot(PCIESlot *s); @@ -80,7 +83,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 963dc2e170..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 | \ diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index 80f5c84e75..450cbef6c2 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -13,17 +13,19 @@ #ifndef QEMU_PCIE_SRIOV_H #define QEMU_PCIE_SRIOV_H -struct PCIESriovPF { +#include "hw/pci/pci.h" + +typedef struct PCIESriovPF { uint16_t num_vfs; /* Number of virtual functions created */ uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ const char *vfname; /* Reference to the device type used for the VFs */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ -}; +} PCIESriovPF; -struct PCIESriovVF { +typedef struct PCIESriovVF { PCIDevice *pf; /* Pointer back to owner physical function */ uint16_t vf_number; /* Logical VF number of this function */ -}; +} PCIESriovVF; void pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, const char *vfname, uint16_t vf_dev_id, @@ -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 deleted file mode 100644 index e3ba44e0bf..0000000000 --- a/include/hw/pcmcia.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef HW_PCMCIA_H -#define HW_PCMCIA_H - -/* PCMCIA/Cardbus */ - -#include "hw/qdev-core.h" -#include "qom/object.h" - -typedef struct PCMCIASocket { - qemu_irq irq; - bool attached; -} PCMCIASocket; - -#define TYPE_PCMCIA_CARD "pcmcia-card" -OBJECT_DECLARE_TYPE(PCMCIACardState, PCMCIACardClass, PCMCIA_CARD) - -struct PCMCIACardState { - /*< private >*/ - DeviceState parent_obj; - /*< public >*/ - - PCMCIASocket *slot; -}; - -struct PCMCIACardClass { - /*< private >*/ - DeviceClass parent_class; - /*< public >*/ - - int (*attach)(PCMCIACardState *state); - int (*detach)(PCMCIACardState *state); - - const uint8_t *cis; - int cis_len; - - /* Only valid if attached */ - uint8_t (*attr_read)(PCMCIACardState *card, uint32_t address); - void (*attr_write)(PCMCIACardState *card, uint32_t address, uint8_t value); - uint16_t (*common_read)(PCMCIACardState *card, uint32_t address); - void (*common_write)(PCMCIACardState *card, - uint32_t address, uint16_t value); - uint16_t (*io_read)(PCMCIACardState *card, uint32_t address); - 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 - -/* dscm1xxxx.c */ -PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv); - -#endif 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/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 4a3f644516..c774f6bf84 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -44,10 +44,6 @@ struct DBDMA_io { DBDMA_end dma_end; /* DMA is in progress, don't start another one */ bool processing; - /* DMA request */ - void *dma_mem; - dma_addr_t dma_len; - DMADirection dir; }; /* 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/pef.h b/include/hw/ppc/pef.h deleted file mode 100644 index 707dbe524c..0000000000 --- a/include/hw/ppc/pef.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * PEF (Protected Execution Facility) for POWER support - * - * 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. - * - */ - -#ifndef HW_PPC_PEF_H -#define HW_PPC_PEF_H - -int pef_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); -int pef_kvm_reset(ConfidentialGuestSupport *cgs, Error **errp); - -#endif /* HW_PPC_PEF_H */ diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 9ef7e2d0dc..fcb6699150 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -20,158 +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_sbe.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 "hw/pci-host/pnv_phb.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 - /* - * 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" 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; - PnvSBE sbe; - 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; - PnvSBE sbe; - 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 @@ -188,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) @@ -196,8 +57,11 @@ 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); +typedef struct PnvPHB PnvPHB; + #define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv") typedef struct PnvMachineClass PnvMachineClass; typedef struct PnvMachineState PnvMachineState; @@ -212,8 +76,12 @@ struct PnvMachineClass { /*< public >*/ const char *compat; int compat_size; + int max_smt_threads; + bool has_lpar_per_thread; + bool quirk_tb_big_core; void (*dt_power_mgt)(PnvMachineState *pnv, void *fdt); + void (*i2c_init)(PnvMachineState *pnv); }; struct PnvMachineState { @@ -235,14 +103,19 @@ struct PnvMachineState { PnvPnor *pnor; hwaddr fw_load_addr; + + bool big_core; + bool lpar_per_core; }; PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id); -Object *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb, Error **errp); +PnvChip *pnv_chip_add_phb(PnvChip *chip, PnvPHB *phb); #define PNV_FDT_ADDR 0x01000000 #define PNV_TIMEBASE_FREQ 512000000ULL +void pnv_cpu_do_nmi_resume(CPUState *cs); + /* * BMC helpers */ diff --git a/include/hw/ppc/pnv_adu.h b/include/hw/ppc/pnv_adu.h new file mode 100644 index 0000000000..f9dbd8c8b3 --- /dev/null +++ b/include/hw/ppc/pnv_adu.h @@ -0,0 +1,32 @@ +/* + * QEMU PowerPC PowerNV Emulation of some ADU behaviour + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_ADU_H +#define PPC_PNV_ADU_H + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_lpc.h" +#include "hw/qdev-core.h" + +#define TYPE_PNV_ADU "pnv-adu" + +OBJECT_DECLARE_TYPE(PnvADU, PnvADUClass, PNV_ADU) + +struct PnvADU { + DeviceState xd; + + /* LPCMC (LPC Master Controller) access engine */ + PnvLpcController *lpc; + uint64_t lpc_base_reg; + uint64_t lpc_cmd_reg; + uint64_t lpc_data_reg; + + MemoryRegion xscom_regs; +}; + +#endif /* PPC_PNV_ADU_H */ diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h new file mode 100644 index 0000000000..24ce37a9c8 --- /dev/null +++ b/include/hw/ppc/pnv_chip.h @@ -0,0 +1,173 @@ +#ifndef PPC_PNV_CHIP_H +#define PPC_PNV_CHIP_H + +#include "hw/pci-host/pnv_phb4.h" +#include "hw/ppc/pnv_adu.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/ssi/pnv_spi.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; + + bool big_core; + bool lpar_per_core; + 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 >*/ + PnvADU adu; + 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 >*/ + PnvADU adu; + PnvXive2 xive; + Pnv9Psi psi; + PnvLpcController lpc; + PnvChipTOD chiptod; + PnvOCC occ; + PnvSBE sbe; + PnvHomer homer; + PnvN1Chiplet n1_chiplet; +#define PNV10_CHIP_MAX_PIB_SPIC 6 + PnvSpi pib_spic[PNV10_CHIP_MAX_PIB_SPIC]; + + 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) +#define PNV10_PIR2THREAD(pir) (((pir) & 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; + + /* Get PIR and TIR values for a CPU thread identified by core/thread id */ + void (*get_pir_tir)(PnvChip *chip, uint32_t core_id, uint32_t thread_id, + uint32_t *pir, uint32_t *tir); + 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, GString *buf); + ISABus *(*isa_create)(PnvChip *chip, Error **errp); + void (*dt_populate)(PnvChip *chip, void *fdt); + void (*pic_print_info)(PnvChip *chip, GString *buf); + 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..d8afb4f95f 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -22,22 +22,49 @@ #include "hw/cpu/core.h" #include "target/ppc/cpu.h" +#include "hw/ppc/pnv.h" #include "qom/object.h" +/* Per-core ChipTOD / TimeBase state */ +typedef struct PnvCoreTODState { + /* + * POWER10 DD2.0 - big core TFMR drives the state machine on the even + * small core. Skiboot has a workaround that targets the even small core + * for CHIPTOD_TO_TB ops. + */ + bool big_core_quirk; + + 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; +} PnvCoreTODState; + #define TYPE_PNV_CORE "powernv-cpu-core" OBJECT_DECLARE_TYPE(PnvCore, PnvCoreClass, PNV_CORE) -typedef struct PnvChip PnvChip; - struct PnvCore { /*< private >*/ CPUCore parent_obj; /*< public >*/ PowerPCCPU **threads; + bool big_core; + bool lpar_per_core; uint32_t pir; + uint32_t hwid; uint64_t hrmor; + + target_ulong scratch[8]; /* SPRC/SPRD indirect SCRATCH registers */ + PnvCoreTODState tod_state; + PnvChip *chip; MemoryRegion xscom_regs; @@ -47,12 +74,14 @@ struct PnvCoreClass { DeviceClass parent_class; const MemoryRegionOps *xscom_ops; + uint64_t xscom_size; }; #define PNV_CORE_TYPE_SUFFIX "-" TYPE_PNV_CORE #define PNV_CORE_TYPE_NAME(cpu_model) cpu_model PNV_CORE_TYPE_SUFFIX typedef struct PnvCPUState { + PnvCore *pnv_core; Object *intc; } PnvCPUState; @@ -61,13 +90,31 @@ 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; + bool special_wakeup_done; + bool special_wakeup[4]; + 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..174add4c53 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -20,7 +20,10 @@ #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" +#include "hw/isa/isa.h" /* For ISA_NUM_IRQS */ #define TYPE_PNV_LPC "pnv-lpc" typedef struct PnvLpcClass PnvLpcClass; @@ -71,6 +74,9 @@ struct PnvLpcController { uint32_t opb_irq_pol; uint32_t opb_irq_input; + /* LPC device IRQ state */ + uint32_t lpc_hc_irq_inputs; + /* LPC HC registers */ uint32_t lpc_hc_fw_seg_idsel; uint32_t lpc_hc_fw_rd_acc_size; @@ -82,8 +88,19 @@ struct PnvLpcController { /* XSCOM registers */ MemoryRegion xscom_regs; + /* + * In P8, ISA irqs are combined with internal sources to drive the + * LPCHC interrupt output. P9 ISA irqs raise one of 4 lines that + * drive PSI SERIRQ irqs, routing according to OPB routing registers. + */ + bool psi_has_serirq; + /* PSI to generate interrupts */ - qemu_irq psi_irq; + qemu_irq psi_irq_lpchc; + + /* P9 serirq lines and irq routing table */ + qemu_irq psi_irq_serirq[4]; + int irq_to_serirq_route[ISA_NUM_IRQS]; }; struct PnvLpcClass { @@ -92,13 +109,13 @@ struct PnvLpcClass { DeviceRealize parent_realize; }; -/* - * Old compilers error on typdef forward declarations. Keep them happy. - */ -struct PnvChip; +bool pnv_lpc_opb_read(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz); +bool pnv_lpc_opb_write(PnvLpcController *lpc, uint32_t addr, + uint8_t *data, int sz); 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..cf7f95a6b1 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, @@ -110,6 +110,6 @@ typedef enum PnvPsiIrq { #define PSIHB9_IRQ_PSU 13 #define PSIHB9_NUM_IRQS 14 -void pnv_psi_pic_print_info(Pnv9Psi *psi, Monitor *mon); +void pnv_psi_pic_print_info(Pnv9Psi *psi, GString *buf); #endif /* PPC_PNV_PSI_H */ diff --git a/include/hw/ppc/pnv_sbe.h b/include/hw/ppc/pnv_sbe.h index f54a3ae9ba..b6b378ad14 100644 --- a/include/hw/ppc/pnv_sbe.h +++ b/include/hw/ppc/pnv_sbe.h @@ -20,7 +20,8 @@ #ifndef PPC_PNV_SBE_H #define PPC_PNV_SBE_H -#include "qom/object.h" +#include "exec/memory.h" +#include "hw/qdev-core.h" #define TYPE_PNV_SBE "pnv-sbe" OBJECT_DECLARE_TYPE(PnvSBE, PnvSBEClass, PNV_SBE) diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h index b5d91505e5..5b4cb4167b 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; @@ -94,7 +93,7 @@ struct PnvXiveClass { DeviceRealize parent_realize; }; -void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon); +void pnv_xive_pic_print_info(PnvXive *xive, GString *buf); /* * XIVE2 interrupt controller (POWER10) @@ -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; @@ -164,6 +163,6 @@ typedef struct PnvXive2Class { DeviceRealize parent_realize; } PnvXive2Class; -void pnv_xive2_pic_print_info(PnvXive2 *xive, Monitor *mon); +void pnv_xive2_pic_print_info(PnvXive2 *xive, GString *buf); #endif /* PPC_PNV_XIVE_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index c6e9ef8dd2..648388a599 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -20,9 +20,10 @@ #ifndef PPC_PNV_XSCOM_H #define PPC_PNV_XSCOM_H -#include "qom/object.h" +#include "exec/memory.h" typedef struct PnvXScomInterface PnvXScomInterface; +typedef struct PnvChip PnvChip; #define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" #define PNV_XSCOM_INTERFACE(obj) \ @@ -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 @@ -78,6 +82,9 @@ struct PnvXScomInterfaceClass { #define PNV_XSCOM_PBCQ_SPCI_BASE 0x9013c00 #define PNV_XSCOM_PBCQ_SPCI_SIZE 0x5 +#define PNV9_XSCOM_ADU_BASE 0x0090000 +#define PNV9_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 9) */ @@ -89,6 +96,12 @@ 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 @@ -118,6 +131,9 @@ struct PnvXScomInterfaceClass { #define PNV9_XSCOM_PEC_PCI_STK1 0x140 #define PNV9_XSCOM_PEC_PCI_STK2 0x180 +#define PNV10_XSCOM_ADU_BASE 0x0090000 +#define PNV10_XSCOM_ADU_SIZE 0x55 + /* * Layout of the XSCOM PCB addresses (POWER 10) */ @@ -126,17 +142,34 @@ 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 @@ -152,13 +185,25 @@ struct PnvXScomInterfaceClass { #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); +#define PNV10_XSCOM_PIB_SPIC_BASE 0xc0000 +#define PNV10_XSCOM_PIB_SPIC_SIZE 0x20 + +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..8a14d623f8 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); @@ -114,6 +116,13 @@ enum { #define PPC_SERIAL_MM_BAUDBASE 399193 +#ifndef CONFIG_USER_ONLY +void booke206_set_tlb(ppcmas_tlb_t *tlb, target_ulong va, hwaddr pa, + hwaddr len); +void booke_set_tlb(ppcemb_tlb_t *tlb, target_ulong va, hwaddr pa, + target_ulong size); +#endif + /* ppc_booke.c */ void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags); #endif diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index f8c86e09ec..1bd9b8821b 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -29,8 +29,6 @@ #include "exec/memory.h" #include "hw/sysbus.h" -#define TYPE_PPC4xx_PCI_HOST_BRIDGE "ppc4xx-pcihost" - /* * Generic DCR device */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 04a95669ab..af4aa1cb0f 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 @@ -138,11 +141,8 @@ struct SpaprMachineClass { MachineClass parent_class; /*< public >*/ - bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */ bool dr_phb_enabled; /* enable dynamic-reconfig/hotplug of PHBs */ bool update_dt_enabled; /* enable KVMPPC_H_UPDATE_DT */ - bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */ - bool pre_2_10_has_unused_icps; bool legacy_irq_allocation; uint32_t nr_xirqs; bool broken_host_serial_model; /* present real host info to the guest */ @@ -157,8 +157,7 @@ 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; @@ -194,13 +193,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; @@ -213,7 +213,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; @@ -272,7 +272,6 @@ 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]; @@ -364,6 +363,9 @@ struct SpaprMachineState { #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 @@ -583,8 +585,16 @@ struct SpaprMachineState { #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_WATCHDOG +#define MAX_HCALL_OPCODE H_GUEST_DELETE /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -618,66 +628,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; @@ -692,15 +642,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); @@ -1044,10 +996,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_cap_ail_mode_3; extern const VMStateDescription vmstate_spapr_wdt; static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) @@ -1071,6 +1025,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); @@ -1087,5 +1042,10 @@ 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..68f7083483 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -28,7 +28,6 @@ struct SpaprCpuCore { /*< public >*/ PowerPCCPU **threads; int node_id; - bool pre_3_0_migration; /* older machine don't know about SpaprCpuState */ }; struct SpaprCpuCoreClass { @@ -41,6 +40,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 +52,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..cb9a85f657 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) @@ -61,7 +73,7 @@ struct SpaprInterruptControllerClass { /* These methods should only be called on the active intc */ void (*set_irq)(SpaprInterruptController *intc, int irq, int val); - void (*print_info)(SpaprInterruptController *intc, Monitor *mon); + void (*print_info)(SpaprInterruptController *intc, GString *buf); void (*dt)(SpaprInterruptController *intc, uint32_t nr_servers, void *fdt, uint32_t phandle); int (*post_load)(SpaprInterruptController *intc, int version_id); @@ -73,7 +85,7 @@ int spapr_irq_cpu_intc_create(struct SpaprMachineState *spapr, PowerPCCPU *cpu, Error **errp); void spapr_irq_cpu_intc_reset(struct SpaprMachineState *spapr, PowerPCCPU *cpu); void spapr_irq_cpu_intc_destroy(struct SpaprMachineState *spapr, PowerPCCPU *cpu); -void spapr_irq_print_info(struct SpaprMachineState *spapr, Monitor *mon); +void spapr_irq_print_info(struct SpaprMachineState *spapr, GString *buf); void spapr_irq_dt(struct SpaprMachineState *spapr, uint32_t nr_servers, void *fdt, uint32_t phandle); diff --git a/include/hw/ppc/spapr_nested.h b/include/hw/ppc/spapr_nested.h new file mode 100644 index 0000000000..e420220484 --- /dev/null +++ b/include/hw/ppc/spapr_nested.h @@ -0,0 +1,528 @@ +#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 +#define GSB_VCPU_SPR_DPDES 0x1053 + /* RESERVED 0x1054 - 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_CAPABILITIES_P11_MODE 0x1000000000000000 +#define H_GUEST_CAP_VALID_MASK (H_GUEST_CAPABILITIES_P11_MODE | \ + 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 H_GUEST_CAP_P11_MODE_BMAP 3 +#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..e94d53405f 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); @@ -171,8 +171,8 @@ static inline bool ics_irq_free(ICSState *ics, uint32_t srcno) } void ics_set_irq_type(ICSState *ics, int srcno, bool lsi); -void icp_pic_print_info(ICPState *icp, Monitor *mon); -void ics_pic_print_info(ICSState *ics, Monitor *mon); +void icp_pic_print_info(ICPState *icp, GString *buf); +void ics_pic_print_info(ICSState *ics, GString *buf); void ics_resend(ICSState *ics); void icp_resend(ICPState *ss); diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index f7eea4ca81..ebee982528 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; @@ -217,7 +218,7 @@ static inline bool xive_source_esb_has_2page(XiveSource *xsrc) xsrc->esb_shift == XIVE_ESB_4K_2PAGE; } -static inline size_t xive_source_esb_len(XiveSource *xsrc) +static inline uint64_t xive_source_esb_len(XiveSource *xsrc) { return (1ull << xsrc->esb_shift) * xsrc->nr_irqs; } @@ -313,7 +314,7 @@ static inline bool xive_source_is_asserted(XiveSource *xsrc, uint32_t srcno) } void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, - Monitor *mon); + GString *buf); static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno) { @@ -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, @@ -522,12 +528,12 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); -void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon); +void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf); Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp); void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); void xive_tctx_ipb_update(XiveTCTX *tctx, uint8_t ring, uint8_t ipb); -void xive_tctx_reset_os_signal(XiveTCTX *tctx); +void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); /* * KVM XIVE device helpers diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index e9e3ea135e..5bccf41159 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) @@ -51,6 +53,12 @@ typedef struct Xive2RouterClass { Xive2Nvp *nvp); int (*write_nvp)(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, Xive2Nvp *nvp, uint8_t word_number); + int (*get_nvgc)(Xive2Router *xrtr, bool crowd, + uint8_t nvgc_blk, uint32_t nvgc_idx, + Xive2Nvgc *nvgc); + int (*write_nvgc)(Xive2Router *xrtr, bool crowd, + uint8_t nvgc_blk, uint32_t nvgc_idx, + Xive2Nvgc *nvgc); uint8_t (*get_block_id)(Xive2Router *xrtr); uint32_t (*get_config)(Xive2Router *xrtr); } Xive2RouterClass; @@ -65,6 +73,12 @@ int xive2_router_get_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, Xive2Nvp *nvp); int xive2_router_write_nvp(Xive2Router *xrtr, uint8_t nvp_blk, uint32_t nvp_idx, Xive2Nvp *nvp, uint8_t word_number); +int xive2_router_get_nvgc(Xive2Router *xrtr, bool crowd, + uint8_t nvgc_blk, uint32_t nvgc_idx, + Xive2Nvgc *nvgc); +int xive2_router_write_nvgc(Xive2Router *xrtr, bool crowd, + uint8_t nvgc_blk, uint32_t nvgc_idx, + Xive2Nvgc *nvgc); uint32_t xive2_router_get_config(Xive2Router *xrtr); void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); @@ -105,5 +119,11 @@ void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); +void xive2_tm_pull_os_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); #endif /* PPC_XIVE2_H */ diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 14605bd458..1d00c8df64 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) * @@ -17,16 +19,18 @@ * mode (P10), the CAM line is slightly different as the VP space was * increased. */ -#define TM2_QW0W2_VU PPC_BIT32(0) +#define TM2_W2_VALID PPC_BIT32(0) +#define TM2_W2_HW PPC_BIT32(1) +#define TM2_QW0W2_VU TM2_W2_VALID #define TM2_QW0W2_LOGIC_SERV PPC_BITMASK32(4, 31) -#define TM2_QW1W2_VO PPC_BIT32(0) -#define TM2_QW1W2_HO PPC_BIT32(1) +#define TM2_QW1W2_VO TM2_W2_VALID +#define TM2_QW1W2_HO TM2_W2_HW #define TM2_QW1W2_OS_CAM PPC_BITMASK32(4, 31) -#define TM2_QW2W2_VP PPC_BIT32(0) -#define TM2_QW2W2_HP PPC_BIT32(1) +#define TM2_QW2W2_VP TM2_W2_VALID +#define TM2_QW2W2_HP TM2_W2_HW #define TM2_QW2W2_POOL_CAM PPC_BITMASK32(4, 31) -#define TM2_QW3W2_VT PPC_BIT32(0) -#define TM2_QW3W2_HT PPC_BIT32(1) +#define TM2_QW3W2_VT TM2_W2_VALID +#define TM2_QW3W2_HT TM2_W2_HW #define TM2_QW3W2_LP PPC_BIT32(6) #define TM2_QW3W2_LE PPC_BIT32(7) @@ -46,7 +50,7 @@ typedef struct Xive2Eas { #define xive2_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS2_VALID) #define xive2_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS2_MASKED) -void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, Monitor *mon); +void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf); /* * Event Notifification Descriptor (END) @@ -95,6 +99,7 @@ typedef struct Xive2End { uint32_t w6; #define END2_W6_FORMAT_BIT PPC_BIT32(0) #define END2_W6_IGNORE PPC_BIT32(1) +#define END2_W6_CROWD PPC_BIT32(2) #define END2_W6_VP_BLOCK PPC_BITMASK32(4, 7) #define END2_W6_VP_OFFSET PPC_BITMASK32(8, 31) #define END2_W6_VP_OFFSET_GEN1 PPC_BITMASK32(13, 31) @@ -109,6 +114,8 @@ typedef struct Xive2End { #define xive2_end_is_notify(end) \ (be32_to_cpu((end)->w0) & END2_W0_UCOND_NOTIFY) #define xive2_end_is_backlog(end) (be32_to_cpu((end)->w0) & END2_W0_BACKLOG) +#define xive2_end_is_precluded_escalation(end) \ + (be32_to_cpu((end)->w0) & END2_W0_PRECL_ESC_CTL) #define xive2_end_is_escalate(end) \ (be32_to_cpu((end)->w0) & END2_W0_ESCALATE_CTL) #define xive2_end_is_uncond_escalation(end) \ @@ -121,6 +128,10 @@ typedef struct Xive2End { (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE1) #define xive2_end_is_firmware2(end) \ (be32_to_cpu((end)->w0) & END2_W0_FIRMWARE2) +#define xive2_end_is_ignore(end) \ + (be32_to_cpu((end)->w6) & END2_W6_IGNORE) +#define xive2_end_is_crowd(end) \ + (be32_to_cpu((end)->w6) & END2_W6_CROWD) static inline uint64_t xive2_end_qaddr(Xive2End *end) { @@ -128,11 +139,11 @@ static inline uint64_t xive2_end_qaddr(Xive2End *end) (be32_to_cpu(end->w3) & END2_W3_EQ_ADDR_LO); } -void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, Monitor *mon); +void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf); void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, - Monitor *mon); + GString *buf); void xive2_end_eas_pic_print_info(Xive2End *end, uint32_t end_idx, - Monitor *mon); + GString *buf); /* * Notification Virtual Processor (NVP) @@ -142,6 +153,7 @@ typedef struct Xive2Nvp { #define NVP2_W0_VALID PPC_BIT32(0) #define NVP2_W0_HW PPC_BIT32(7) #define NVP2_W0_ESC_END PPC_BIT32(25) /* 'N' bit 0:ESB 1:END */ +#define NVP2_W0_PGOFIRST PPC_BITMASK32(26, 31) uint32_t w1; #define NVP2_W1_CO PPC_BIT32(13) #define NVP2_W1_CO_PRIV PPC_BITMASK32(14, 15) @@ -162,7 +174,9 @@ typedef struct Xive2Nvp { #define NVP2_W5_VP_END_BLOCK PPC_BITMASK32(4, 7) #define NVP2_W5_VP_END_INDEX PPC_BITMASK32(8, 31) uint32_t w6; +#define NVP2_W6_REPORTING_LINE PPC_BITMASK32(4, 31) uint32_t w7; +#define NVP2_W7_REPORTING_LINE PPC_BITMASK32(0, 23) } Xive2Nvp; #define xive2_nvp_is_valid(nvp) (be32_to_cpu((nvp)->w0) & NVP2_W0_VALID) @@ -192,12 +206,15 @@ static inline uint32_t xive2_nvp_blk(uint32_t cam_line) return (cam_line >> XIVE2_NVP_SHIFT) & 0xf; } +void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf); + /* * Notification Virtual Group or Crowd (NVG/NVC) */ typedef struct Xive2Nvgc { uint32_t w0; #define NVGC2_W0_VALID PPC_BIT32(0) +#define NVGC2_W0_PGONEXT PPC_BITMASK32(26, 31) uint32_t w1; uint32_t w2; uint32_t w3; @@ -207,4 +224,9 @@ typedef struct Xive2Nvgc { uint32_t w7; } Xive2Nvgc; +#define xive2_nvgc_is_valid(nvgc) (be32_to_cpu((nvgc)->w0) & NVGC2_W0_VALID) + +void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, + GString *buf); + #endif /* PPC_XIVE2_REGS_H */ diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h index b7fde2354e..326327fc79 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 */ @@ -61,8 +77,11 @@ #define TM_LSMFB 0x3 /* - + + + */ #define TM_ACK_CNT 0x4 /* - + - - */ #define TM_INC 0x5 /* - + - + */ +#define TM_LGS 0x5 /* + + + + */ /* Rename P10 */ #define TM_AGE 0x6 /* - + - + */ +#define TM_T 0x6 /* - + - + */ /* Rename P10 */ #define TM_PIPR 0x7 /* - + - + */ +#define TM_OGEN 0xF /* - + - - */ /* P10 only */ #define TM_WORD0 0x0 #define TM_WORD1 0x4 @@ -82,6 +101,7 @@ #define TM_QW3W2_LP PPC_BIT32(6) #define TM_QW3W2_LE PPC_BIT32(7) #define TM_QW3W2_T PPC_BIT32(31) +#define TM_QW3B8_VT PPC_BIT8(0) /* * In addition to normal loads to "peek" and writes (only when invalid) @@ -98,23 +118,32 @@ * Then we have all these "special" CI ops at these offset that trigger * all sorts of side effects: */ -#define TM_SPC_ACK_EBB 0x800 /* Load8 ack EBB to reg*/ -#define TM_SPC_ACK_OS_REG 0x810 /* Load16 ack OS irq to reg */ +#define TM_SPC_ACK_EBB 0x800 /* Load8 ack EBB to reg */ +#define TM_SPC_ACK_OS_REG 0x810 /* Load16 ack OS irq to reg */ #define TM_SPC_PUSH_USR_CTX 0x808 /* Store32 Push/Validate user context */ -#define TM_SPC_PULL_USR_CTX 0x808 /* Load32 Pull/Invalidate user - * context */ -#define TM_SPC_SET_OS_PENDING 0x812 /* Store8 Set OS irq pending bit */ -#define TM_SPC_PULL_OS_CTX 0x818 /* Load32/Load64 Pull/Invalidate OS - * context to reg */ -#define TM_SPC_PULL_POOL_CTX 0x828 /* Load32/Load64 Pull/Invalidate Pool - * context to reg*/ -#define TM_SPC_ACK_HV_REG 0x830 /* Load16 ack HV irq to reg */ -#define TM_SPC_PULL_USR_CTX_OL 0xc08 /* Store8 Pull/Inval usr ctx to odd - * line */ -#define TM_SPC_ACK_OS_EL 0xc10 /* Store8 ack OS irq to even line */ -#define TM_SPC_ACK_HV_POOL_EL 0xc20 /* Store8 ack HV evt pool to even - * line */ -#define TM_SPC_ACK_HV_EL 0xc30 /* Store8 ack HV irq to even line */ +#define TM_SPC_PULL_USR_CTX 0x808 /* Load32 Pull/Invalidate user */ + /* context */ +#define TM_SPC_SET_OS_PENDING 0x812 /* Store8 Set OS irq pending bit */ +#define TM_SPC_PULL_OS_CTX_G2 0x810 /* Load32/Load64 Pull/Invalidate OS */ + /* context to reg */ +#define TM_SPC_PULL_OS_CTX 0x818 /* Load32/Load64 Pull/Invalidate OS */ + /* context to reg */ +#define TM_SPC_PULL_POOL_CTX_G2 0x820 /* Load32/Load64 Pull/Invalidate Pool */ + /* context to reg */ +#define TM_SPC_PULL_POOL_CTX 0x828 /* Load32/Load64 Pull/Invalidate Pool */ + /* context to reg */ +#define TM_SPC_ACK_HV_REG 0x830 /* Load16 ack HV irq to reg */ +#define TM_SPC_PULL_PHYS_CTX_G2 0x830 /* Load32 Pull phys ctx to reg */ +#define TM_SPC_PULL_PHYS_CTX 0x838 /* Load8 Pull phys ctx to reg */ +#define TM_SPC_PULL_USR_CTX_OL 0xc08 /* Store8 Pull/Inval usr ctx to odd */ + /* line */ +#define TM_SPC_ACK_OS_EL 0xc10 /* Store8 ack OS irq to even line */ +#define TM_SPC_PULL_OS_CTX_OL 0xc18 /* Pull/Invalidate OS context to */ + /* odd Thread reporting line */ +#define TM_SPC_ACK_HV_POOL_EL 0xc20 /* Store8 ack HV evt pool to even */ + /* line */ +#define TM_SPC_ACK_HV_EL 0xc30 /* Store8 ack HV irq to even line */ +#define TM_SPC_PULL_PHYS_CTX_OL 0xc38 /* Pull phys ctx to odd cache line */ /* XXX more... */ /* NSR fields for the various QW ack types */ @@ -151,7 +180,7 @@ typedef struct XiveEAS { #define xive_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS_VALID) #define xive_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS_MASKED) -void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon); +void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, GString *buf); static inline uint64_t xive_get_field64(uint64_t mask, uint64_t word) { @@ -245,9 +274,9 @@ static inline uint64_t xive_end_qaddr(XiveEND *end) be32_to_cpu(end->w3); } -void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); -void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon); -void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); +void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf); +void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, GString *buf); +void xive_end_eas_pic_print_info(XiveEND *end, uint32_t end_idx, GString *buf); /* Notification Virtual Target (NVT) */ typedef struct XiveNVT { diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 785dd5a56e..5be9844412 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, }; @@ -35,83 +95,52 @@ typedef void (*DeviceUnrealize)(DeviceState *dev); typedef void (*DeviceReset)(DeviceState *dev); typedef void (*BusRealize)(BusState *bus, Error **errp); typedef void (*BusUnrealize)(BusState *bus); +typedef int (*DeviceSyncConfig)(DeviceState *dev, Error **errp); /** - * 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. * @unrealize: Callback function invoked when the #DeviceState:realized * property is changed to %false. + * @sync_config: Callback function invoked when QMP command device-sync-config + * is called. Should synchronize device configuration from host to guest part + * and notify the guest about the change. * @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_; + const 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 +148,36 @@ 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. + /** + * @legacy_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; + DeviceReset legacy_reset; DeviceRealize realize; DeviceUnrealize unrealize; + DeviceSyncConfig sync_config; - /* 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,40 +202,104 @@ 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; }; +typedef struct DeviceListener DeviceListener; struct DeviceListener { void (*realize)(DeviceListener *listener, DeviceState *dev); void (*unrealize)(DeviceListener *listener, DeviceState *dev); @@ -231,8 +335,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) @@ -259,13 +361,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; @@ -274,18 +387,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. @@ -319,7 +438,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); @@ -329,9 +449,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 @@ -343,11 +478,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); @@ -374,6 +509,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); @@ -402,19 +539,20 @@ 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); +int qdev_sync_config(DeviceState *dev, Error **errp); void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void qdev_machine_creation_done(void); @@ -441,15 +579,15 @@ 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 - * @reason: Returns one of the reasons why the device is blocked, - * if any + * @errp: The reasons why the device is blocked, if any * - * Returns: true if device is blocked from unplug, false otherwise + * Returns: true (also setting @errp) if device is blocked from unplug, + * false otherwise */ bool qdev_unplug_blocked(DeviceState *dev, Error **errp); /** - * GpioPolarity: Polarity of a GPIO line + * typedef GpioPolarity - Polarity of a GPIO line * * GPIO lines use either positive (active-high) logic, * or negative (active-low) logic. @@ -481,6 +619,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); @@ -490,14 +630,17 @@ qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); * @name: Name of the input GPIO array * @n: Number of the GPIO line in that array (which must be in range) * - * Returns the qemu_irq corresponding to a named input GPIO line - * (which the device has set up with qdev_init_gpio_in_named()). + * Returns the qemu_irq corresponding to a single input GPIO line + * in a named array of input GPIO lines on a device (which the device + * has set up with qdev_init_gpio_in_named()). * The @name string must correspond to an input GPIO array which exists on * the device, and the index @n of the GPIO line must be valid (i.e. * be at least 0 and less than the total number of input GPIOs in that * 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); @@ -505,7 +648,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 @@ -537,15 +680,15 @@ void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); * GPIO lines * @dev: Device whose GPIO to connect * @name: Name of the output GPIO array - * @n: Number of the anonymous output GPIO line (which must be in range) + * @n: Number of the output GPIO line within that array (which must be in range) * @input_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 - * output GPIO line, the qemu_irq's callback is invoked. + * This function connects a single GPIO output in a named array of output + * GPIO lines on a device up to an arbitrary qemu_irq, so that when the + * device asserts that output GPIO line, the qemu_irq's callback is invoked. * The @name string must correspond to an output GPIO array which exists on * the device, and the index @n of the GPIO line must be valid (i.e. - * be at least 0 and less than the total number of input GPIOs in that + * be at least 0 and less than the total number of output GPIOs in that * array); this function will assert() if passed an invalid name or index. * * Outbound GPIO lines can be connected to any qemu_irq, but the common @@ -576,6 +719,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); @@ -586,14 +731,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); @@ -655,7 +803,7 @@ void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); * @dev: Device to create output GPIOs for * @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines * @name: Name to give this array of GPIO lines - * @n: Number of GPIO lines to create + * @n: Number of GPIO lines to create in this array * * Like qdev_init_gpio_out(), but creates an array of GPIO output lines * with a name. Code using the device can then connect these GPIO lines @@ -665,9 +813,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 @@ -680,8 +826,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). @@ -715,7 +864,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. ***/ @@ -744,40 +893,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. @@ -785,14 +911,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); @@ -803,27 +933,58 @@ 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); +void device_class_set_props(DeviceClass *dc, const Property *props); /** - * device_class_set_parent_reset: - * TODO: remove the function when DeviceClass's reset method - * is not used anymore. + * 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_reset(DeviceClass *dc, - DeviceReset dev_reset, - DeviceReset *parent_reset); void device_class_set_parent_realize(DeviceClass *dc, DeviceRealize dev_realize, DeviceRealize *parent_realize); + +/** + * device_class_set_legacy_reset(): set the DeviceClass::reset method + * @dc: The device class + * @dev_reset: the reset function + * + * This function sets the DeviceClass::reset method. This is widely + * used in existing code, but new code should prefer to use the + * Resettable API as documented in docs/devel/reset.rst. + * In addition, devices which need to chain to their parent class's + * reset methods or which need to be subclassed must use Resettable. + */ +void device_class_set_legacy_reset(DeviceClass *dc, + DeviceReset dev_reset); + +/** + * 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); @@ -835,6 +996,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); @@ -847,7 +1022,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; } /** @@ -877,14 +1063,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); @@ -901,6 +1088,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 @@ -916,7 +1108,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..7ec37f6316 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -3,10 +3,16 @@ #include "hw/qdev-properties.h" +bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str, + Error **errp); + 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 +28,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 +49,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 +89,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, \ + S390CpuEntitlement) + +#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..26ebd23068 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -37,7 +37,7 @@ struct PropertyInfo { int (*print)(Object *obj, Property *prop, char *dest, size_t len); void (*set_default_value)(ObjectProperty *op, const Property *prop); ObjectProperty *(*create)(ObjectClass *oc, const char *name, - Property *prop); + const Property *prop); ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; ObjectPropertyRelease *release; @@ -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); @@ -225,15 +223,15 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj, * On error, store error in @errp. Static properties access data in a struct. * The type of the QOM property is derived from prop->info. */ -void qdev_property_add_static(DeviceState *dev, Property *prop); +void qdev_property_add_static(DeviceState *dev, const Property *prop); /** * qdev_alias_all_properties: Create aliases on source for all target properties * @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/rdma/rdma.h b/include/hw/rdma/rdma.h deleted file mode 100644 index 80b2e531c4..0000000000 --- a/include/hw/rdma/rdma.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * RDMA device interface - * - * Copyright (C) 2019 Oracle - * Copyright (C) 2019 Red Hat Inc - * - * Authors: - * Yuval Shaia - * - * 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 RDMA_H -#define RDMA_H - -#include "qom/object.h" - -#define INTERFACE_RDMA_PROVIDER "rdma" - -typedef struct RdmaProviderClass RdmaProviderClass; -DECLARE_CLASS_CHECKERS(RdmaProviderClass, RDMA_PROVIDER, - INTERFACE_RDMA_PROVIDER) -#define RDMA_PROVIDER(obj) \ - INTERFACE_CHECK(RdmaProvider, (obj), \ - INTERFACE_RDMA_PROVIDER) - -typedef struct RdmaProvider RdmaProvider; - -struct RdmaProviderClass { - InterfaceClass parent; - - void (*format_statistics)(RdmaProvider *obj, GString *buf); -}; - -#endif diff --git a/include/hw/remote/iohub.h b/include/hw/remote/iohub.h index 0bf98e0d78..09ee6c77b6 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" @@ -37,6 +37,5 @@ void remote_iohub_set_irq(void *opaque, int pirq, int level); void process_set_irqfd_msg(PCIDevice *pci_dev, MPQemuMsg *msg); void remote_iohub_init(RemoteIOHubState *iohub); -void remote_iohub_finalize(RemoteIOHubState *iohub); #endif 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/resettable.h b/include/hw/resettable.h index bdcd1276b6..fd862f1e9f 100644 --- a/include/hw/resettable.h +++ b/include/hw/resettable.h @@ -29,12 +29,17 @@ typedef struct ResettableState ResettableState; * Types of reset. * * + Cold: reset resulting from a power cycle of the object. + * + Wakeup: reset resulting from a wake-up from a suspended state. * * TODO: Support has to be added to handle more types. In particular, * ResettableState structure needs to be expanded. */ typedef enum ResetType { RESET_TYPE_COLD, + RESET_TYPE_SNAPSHOT_LOAD, + RESET_TYPE_WAKEUP, + RESET_TYPE_S390_CPU_INITIAL, + RESET_TYPE_S390_CPU_NORMAL, } ResetType; /* @@ -87,14 +92,6 @@ typedef enum ResetType { * @get_state: Mandatory method which must return a pointer to a * ResettableState. * - * @get_transitional_function: transitional method to handle Resettable objects - * not yet fully moved to this interface. It will be removed as soon as it is - * not needed anymore. This method is optional and may return a pointer to a - * function to be used instead of the phases. If the method exists and returns - * a non-NULL function pointer then that function is executed as a replacement - * of the 'hold' phase method taking the object as argument. The two other phase - * methods are not executed. - * * @child_foreach: Executes a given callback on every Resettable child. Child * in this context means a child in the qbus tree, so the children of a qbus * are the devices on it, and the children of a device are all the buses it @@ -103,11 +100,9 @@ typedef enum ResetType { * the callback. */ typedef void (*ResettableEnterPhase)(Object *obj, ResetType type); -typedef void (*ResettableHoldPhase)(Object *obj); -typedef void (*ResettableExitPhase)(Object *obj); +typedef void (*ResettableHoldPhase)(Object *obj, ResetType type); +typedef void (*ResettableExitPhase)(Object *obj, ResetType type); typedef ResettableState * (*ResettableGetState)(Object *obj); -typedef void (*ResettableTrFunction)(Object *obj); -typedef ResettableTrFunction (*ResettableGetTrFunction)(Object *obj); typedef void (*ResettableChildCallback)(Object *, void *opaque, ResetType type); typedef void (*ResettableChildForeach)(Object *obj, @@ -127,9 +122,6 @@ struct ResettableClass { /* State access method */ ResettableGetState get_state; - /* Transitional method for legacy reset compatibility */ - ResettableGetTrFunction get_transitional_function; - /* Hierarchy handling method */ ResettableChildForeach child_foreach; }; diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h index 93e5f8760d..f778b560de 100644 --- a/include/hw/riscv/boot.h +++ b/include/hw/riscv/boot.h @@ -35,24 +35,30 @@ target_ulong riscv_calc_kernel_start_addr(RISCVHartArrayState *harts, target_ulong firmware_end_addr); target_ulong riscv_find_and_load_firmware(MachineState *machine, const char *default_machine_firmware, - hwaddr firmware_load_addr, + 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, + 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 riscv_rom_copy_firmware_info(MachineState *machine, hwaddr rom_base, +void riscv_rom_copy_firmware_info(MachineState *machine, + RISCVHartArrayState *harts, + hwaddr rom_base, hwaddr rom_size, uint32_t reset_vec_size, uint64_t kernel_entry); diff --git a/include/hw/riscv/boot_opensbi.h b/include/hw/riscv/boot_opensbi.h index c19cad4818..18664a174b 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 @@ -56,4 +58,33 @@ struct fw_dynamic_info { target_long boot_hart; }; +/** Representation dynamic info passed by previous booting stage */ +struct fw_dynamic_info32 { + /** Info magic */ + int32_t magic; + /** Info version */ + int32_t version; + /** Next booting stage address */ + int32_t next_addr; + /** Next booting stage mode */ + int32_t next_mode; + /** Options for OpenSBI library */ + int32_t options; + /** + * Preferred boot HART id + * + * It is possible that the previous booting stage uses same link + * address as the FW_DYNAMIC firmware. In this case, the relocation + * lottery mechanism can potentially overwrite the previous booting + * stage while other HARTs are still running in the previous booting + * stage leading to boot-time crash. To avoid this boot-time crash, + * the previous booting stage can specify last HART that will jump + * to the FW_DYNAMIC firmware as the preferred boot HART. + * + * To avoid specifying a preferred boot HART, the previous booting + * stage can set it to -1UL which will force the FW_DYNAMIC firmware + * to use the relocation lottery mechanism. + */ + int32_t boot_hart; +}; #endif diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h new file mode 100644 index 0000000000..80769a1400 --- /dev/null +++ b/include/hw/riscv/iommu.h @@ -0,0 +1,36 @@ +/* + * QEMU emulation of an RISC-V IOMMU + * + * Copyright (C) 2022-2023 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 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_RISCV_IOMMU_H +#define HW_RISCV_IOMMU_H + +#include "qemu/osdep.h" +#include "qom/object.h" + +#define TYPE_RISCV_IOMMU "riscv-iommu" +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUState, RISCV_IOMMU) +typedef struct RISCVIOMMUState RISCVIOMMUState; + +#define TYPE_RISCV_IOMMU_MEMORY_REGION "riscv-iommu-mr" +typedef struct RISCVIOMMUSpace RISCVIOMMUSpace; + +#define TYPE_RISCV_IOMMU_PCI "riscv-iommu-pci" +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI) +typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; + +#endif diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index a757b240e0..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 >*/ @@ -121,6 +124,8 @@ enum { MICROCHIP_PFSOC_USB, MICROCHIP_PFSOC_QSPI_XIP, MICROCHIP_PFSOC_IOSCB, + MICROCHIP_PFSOC_FABRIC_FIC0, + MICROCHIP_PFSOC_FABRIC_FIC1, MICROCHIP_PFSOC_FABRIC_FIC3, MICROCHIP_PFSOC_DRAM_LO, MICROCHIP_PFSOC_DRAM_LO_ALIAS, @@ -145,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 6665cd5794..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" @@ -53,9 +54,12 @@ struct LowRISCIbexSoCState { 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; @@ -81,7 +85,7 @@ enum { 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, @@ -94,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 { @@ -108,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 = 127, - IBEX_SPI_HOST0_ERR_IRQ = 151, - IBEX_SPI_HOST0_SPI_EVENT_IRQ = 152, - IBEX_SPI_HOST1_ERR_IRQ = 153, - IBEX_SPI_HOST1_SPI_EVENT_IRQ = 154, + 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 d738745925..31180a680e 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -22,6 +22,7 @@ #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" @@ -35,6 +36,7 @@ typedef struct SiFiveESoCState { /*< public >*/ RISCVHartArrayState cpus; DeviceState *plic; + SiFiveEAONState aon; SIFIVEGPIOState gpio; MemoryRegion xip_mem; MemoryRegion mask_rom; @@ -76,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 be4ab8fe7f..c0dc41ff9a 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 @@ -114,6 +118,7 @@ enum { #define FDT_PLIC_ADDR_CELLS 0 #define FDT_PLIC_INT_CELLS 1 #define FDT_APLIC_INT_CELLS 2 +#define FDT_APLIC_ADDR_CELLS 0 #define FDT_IMSIC_INT_CELLS 0 #define FDT_MAX_INT_CELLS 2 #define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \ @@ -123,4 +128,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/mc146818rtc.h b/include/hw/rtc/mc146818rtc.h index 6342ae6920..aa38d3fe87 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; @@ -46,14 +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 -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 rtc_reset_reinjection(MC146818RtcState *rtc); #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/adapter.h b/include/hw/s390x/adapter.h index 7f1703508c..d4fadc4f7f 100644 --- a/include/hw/s390x/adapter.h +++ b/include/hw/s390x/adapter.h @@ -12,12 +12,12 @@ #ifndef S390X_ADAPTER_H #define S390X_ADAPTER_H -struct AdapterInfo { +typedef struct AdapterInfo { uint64_t ind_addr; uint64_t summary_addr; uint64_t ind_offset; uint32_t summary_offset; uint32_t adapter_id; -}; +} AdapterInfo; #endif diff --git a/include/hw/s390x/cpu-topology.h b/include/hw/s390x/cpu-topology.h new file mode 100644 index 0000000000..9283c948e3 --- /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; + S390CpuPolarization 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 */ + g_assert_not_reached(); +} +#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..8289e45837 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); @@ -333,4 +333,10 @@ static inline int ccw_dstream_read_buf(CcwDataStream *cds, void *buff, int len) #define ccw_dstream_read(cds, v) ccw_dstream_read_buf((cds), &(v), sizeof(v)) #define ccw_dstream_write(cds, v) ccw_dstream_write_buf((cds), &(v), sizeof(v)) +/** + * true if (vmstate based) migration of the channel subsystem + * is enabled, false if it is disabled. + */ +extern bool css_migration_enabled; + #endif diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 3ffd575d8f..ff874e792d 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -203,6 +203,6 @@ struct SCLPEventFacilityClass { bool (*event_pending)(SCLPEventFacility *ef); }; -BusState *sclp_get_event_facility_bus(void); +BusState *sclp_get_event_facility_bus(SCLPEventFacility *ef); #endif diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h new file mode 100644 index 0000000000..6824391111 --- /dev/null +++ b/include/hw/s390x/ipl/qipl.h @@ -0,0 +1,127 @@ +/* + * S/390 boot structures + * + * Copyright 2024 IBM Corp. + * Author(s): Jared Rossi + * + * 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 S390X_QIPL_H +#define S390X_QIPL_H + +/* Boot Menu flags */ +#define QIPL_FLAG_BM_OPTS_CMD 0x80 +#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 + +#define QIPL_ADDRESS 0xcc +#define LOADPARM_LEN 8 +#define NO_LOADPARM "\0\0\0\0\0\0\0\0" + +/* + * The QEMU IPL Parameters will be stored at absolute address + * 204 (0xcc) which means it is 32-bit word aligned but not + * double-word aligned. Placement of 64-bit data fields in this + * area must account for their alignment needs. + * The total size of the struct must never exceed 28 bytes. + */ +struct QemuIplParameters { + uint8_t qipl_flags; + uint8_t index; + uint8_t reserved1[2]; + uint64_t reserved2; + uint32_t boot_menu_timeout; + uint8_t reserved3[2]; + uint16_t chain_len; + uint64_t next_iplb; +} QEMU_PACKED; +typedef struct QemuIplParameters QemuIplParameters; + +struct IPLBlockPVComp { + uint64_t tweak_pref; + uint64_t addr; + uint64_t size; +} QEMU_PACKED; +typedef struct IPLBlockPVComp IPLBlockPVComp; + +struct IPLBlockPV { + uint8_t reserved18[87]; /* 0x18 */ + uint8_t version; /* 0x6f */ + uint32_t reserved70; /* 0x70 */ + uint32_t num_comp; /* 0x74 */ + uint64_t pv_header_addr; /* 0x78 */ + uint64_t pv_header_len; /* 0x80 */ + struct IPLBlockPVComp components[0]; +} QEMU_PACKED; +typedef struct IPLBlockPV IPLBlockPV; + +struct IplBlockCcw { + uint8_t reserved0[85]; + uint8_t ssid; + uint16_t devno; + uint8_t vm_flags; + uint8_t reserved3[3]; + uint32_t vm_parm_len; + uint8_t nss_name[8]; + uint8_t vm_parm[64]; + uint8_t reserved4[8]; +} QEMU_PACKED; +typedef struct IplBlockCcw IplBlockCcw; + +struct IplBlockFcp { + uint8_t reserved1[305 - 1]; + uint8_t opt; + uint8_t reserved2[3]; + uint16_t reserved3; + uint16_t devno; + uint8_t reserved4[4]; + uint64_t wwpn; + uint64_t lun; + uint32_t bootprog; + uint8_t reserved5[12]; + uint64_t br_lba; + uint32_t scp_data_len; + uint8_t reserved6[260]; + uint8_t scp_data[0]; +} QEMU_PACKED; +typedef struct IplBlockFcp IplBlockFcp; + +struct IplBlockQemuScsi { + uint32_t lun; + uint16_t target; + uint16_t channel; + uint8_t reserved0[77]; + uint8_t ssid; + uint16_t devno; +} QEMU_PACKED; +typedef struct IplBlockQemuScsi IplBlockQemuScsi; + +union IplParameterBlock { + struct { + uint32_t len; + uint8_t reserved0[3]; + uint8_t version; + uint32_t blk0_len; + uint8_t pbt; + uint8_t flags; + uint16_t reserved01; + uint8_t loadparm[LOADPARM_LEN]; + union { + IplBlockCcw ccw; + IplBlockFcp fcp; + IPLBlockPV pv; + IplBlockQemuScsi scsi; + }; + } QEMU_PACKED; + struct { + uint8_t reserved1[110]; + uint16_t devno; + uint8_t reserved2[88]; + uint8_t reserved_ext[4096 - 200]; + } QEMU_PACKED; +} QEMU_PACKED; +typedef union IplParameterBlock IplParameterBlock; + +#endif diff --git a/include/hw/s390x/s390-ccw.h b/include/hw/s390x/s390-ccw.h index 2c807ee3a1..2e0a709981 100644 --- a/include/hw/s390x/s390-ccw.h +++ b/include/hw/s390x/s390-ccw.h @@ -31,7 +31,7 @@ struct S390CCWDevice { struct S390CCWDeviceClass { CCWDeviceClass parent_class; - void (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp); + bool (*realize)(S390CCWDevice *dev, char *sysfsdev, Error **errp); void (*unrealize)(S390CCWDevice *dev); IOInstEnding (*handle_request) (SubchDev *sch); int (*handle_halt) (SubchDev *sch); diff --git a/include/hw/s390x/s390-pci-bus.h b/include/hw/s390x/s390-pci-bus.h index 0605fcea24..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; }; @@ -343,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; @@ -351,10 +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; }; @@ -394,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-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 9bba21a916..996864a34e 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -13,6 +13,7 @@ #include "hw/boards.h" #include "qom/object.h" +#include "hw/s390x/sclp.h" #define TYPE_S390_CCW_MACHINE "s390-ccw-machine" @@ -28,8 +29,16 @@ struct S390CcwMachineState { bool dea_key_wrap; bool pv; uint8_t loadparm[8]; + + SCLPDevice *sclp; }; +#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; @@ -37,7 +46,6 @@ struct S390CcwMachineClass { /*< public >*/ bool ri_allowed; bool cpu_model_allowed; - bool css_migration_enabled; bool hpage_1m_allowed; int max_threads; }; @@ -49,10 +57,4 @@ bool cpu_model_allowed(void); /* 1M huge page mappings allowed by the machine */ bool hpage_1m_allowed(void); -/** - * Returns true if (vmstate based) migration of the channel subsystem - * is enabled, false if it is disabled. - */ -bool css_migration_enabled(void); - #endif diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 3907a13d07..4d66c5e42e 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -35,9 +35,6 @@ typedef struct AdapterRoutes { extern const VMStateDescription vmstate_adapter_routes; -#define VMSTATE_ADAPTER_ROUTES(_f, _s) \ - VMSTATE_STRUCT(_f, _s, 1, vmstate_adapter_routes, AdapterRoutes) - #define TYPE_S390_FLIC_COMMON "s390-flic" OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass, S390_FLIC_COMMON) @@ -47,6 +44,7 @@ struct S390FLICState { /* to limit AdapterRoutes.num_routes for compat */ uint32_t adapter_routes_max_batch; bool ais_supported; + bool migration_enabled; }; @@ -118,6 +116,7 @@ struct QEMUS390FLICState { uint8_t simm; uint8_t nimm; QLIST_HEAD(, QEMUS390FlicIO) io[8]; + bool migrate_all_state; }; uint32_t qemu_s390_flic_dequeue_service(QEMUS390FLICState *flic); diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h index d3ade40a5a..d32f6180e0 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 */ @@ -221,12 +221,9 @@ static inline int sccb_data_len(SCCB *sccb) return be16_to_cpu(sccb->h.length) - sizeof(sccb->h); } - -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/storage-attributes.h b/include/hw/s390x/storage-attributes.h index 5239eb538c..8921a04d51 100644 --- a/include/hw/s390x/storage-attributes.h +++ b/include/hw/s390x/storage-attributes.h @@ -39,7 +39,7 @@ struct S390StAttribClass { int (*set_stattr)(S390StAttribState *sa, uint64_t start_gfn, uint32_t count, uint8_t *values); void (*synchronize)(S390StAttribState *sa); - int (*set_migrationmode)(S390StAttribState *sa, bool value); + int (*set_migrationmode)(S390StAttribState *sa, bool value, Error **errp); int (*get_active)(S390StAttribState *sa); long long (*get_dirtycount)(S390StAttribState *sa); }; diff --git a/include/hw/s390x/storage-keys.h b/include/hw/s390x/storage-keys.h index aa2ec2aae5..976ffb2039 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -111,6 +111,16 @@ struct QEMUS390SKeysState { }; void s390_skeys_init(void); +/** + * @s390_skeys_get: See S390SKeysClass::get_skeys() + */ +int s390_skeys_get(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys); +/** + * @s390_skeys_set: See S390SKeysClass::set_skeys() + */ +int s390_skeys_set(S390SKeysState *ks, uint64_t start_gfn, + uint64_t count, uint8_t *keys); S390SKeysState *s390_get_skeys_device(void); 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 6ea4b64fe7..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; @@ -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); @@ -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,10 +199,7 @@ 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); @@ -213,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); diff --git a/include/hw/sd/allwinner-sdhost.h b/include/hw/sd/allwinner-sdhost.h index 30c1e60404..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 */ /** @} */ @@ -132,6 +139,8 @@ struct AwSdHostClass { 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..f2458f37b3 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -75,14 +75,6 @@ typedef enum { UHS_III = 3, /* currently not supported */ } sd_uhs_mode_t; -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_cmd_type_t; - typedef struct { uint8_t cmd; uint32_t arg; @@ -93,6 +85,12 @@ 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) + +#define TYPE_EMMC "emmc" +DECLARE_INSTANCE_CHECKER(SDState, EMMC, TYPE_EMMC) + struct SDCardClass { /*< private >*/ DeviceClass parent_class; @@ -124,6 +122,10 @@ struct SDCardClass { void (*enable)(SDState *sd, bool enable); bool (*get_inserted)(SDState *sd); bool (*get_readonly)(SDState *sd); + void (*set_cid)(SDState *sd); + void (*set_csd)(SDState *sd, uint64_t size); + + 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/sh4/sh.h b/include/hw/sh4/sh.h index ec716cdd45..c82feef8d0 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -38,29 +38,10 @@ struct SH7750State; struct SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem); -typedef struct { - /* The callback will be triggered if any of the designated lines change */ - uint16_t portamask_trigger; - uint16_t portbmask_trigger; - /* Return 0 if no action was taken */ - int (*port_change_cb) (uint16_t porta, uint16_t portb, - uint16_t *periph_pdtra, - uint16_t *periph_portdira, - uint16_t *periph_pdtrb, - uint16_t *periph_portdirb); -} sh7750_io_device; - -int sh7750_register_io_device(struct SH7750State *s, - sh7750_io_device *device); - -/* sh_serial.c */ #define TYPE_SH_SERIAL "sh-serial" #define SH_SERIAL_FEAT_SCIF (1 << 0) /* sh7750.c */ qemu_irq sh7750_irl(struct SH7750State *s); -/* tc58128.c */ -int tc58128_init(struct SH7750State *s, const char *zone1, const char *zone2); - #endif 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..6c60017024 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 @@ -205,8 +196,12 @@ struct ICH9LPCState { #define ICH9_PMIO_GPE0_LEN 16 #define ICH9_PMIO_SMI_EN 0x30 #define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) +#define ICH9_PMIO_SMI_EN_SWSMI_EN (1 << 6) #define ICH9_PMIO_SMI_EN_TCO_EN (1 << 13) +#define ICH9_PMIO_SMI_EN_PERIODIC_EN (1 << 14) #define ICH9_PMIO_SMI_STS 0x34 +#define ICH9_PMIO_SMI_STS_SWSMI_STS (1 << 6) +#define ICH9_PMIO_SMI_STS_PERIODIC_STS (1 << 14) #define ICH9_PMIO_TCO_RLD 0x60 #define ICH9_PMIO_TCO_LEN 32 @@ -253,4 +248,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 2693778b23..86709ba2e4 100644 --- a/include/hw/southbridge/piix.h +++ b/include/hw/southbridge/piix.h @@ -12,8 +12,11 @@ #ifndef HW_SOUTHBRIDGE_PIIX_H #define HW_SOUTHBRIDGE_PIIX_H -#include "hw/pci/pci.h" -#include "qom/object.h" +#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 @@ -27,7 +30,6 @@ */ #define PIIX_RCR_IOPORT 0xcf9 -#define PIIX_NUM_PIC_IRQS 16 /* i8259 * 2 */ #define PIIX_NUM_PIRQS 4ULL /* PIRQ[A-D] */ struct PIIXState { @@ -39,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; -}; -typedef struct PIIXState PIIX3State; -#define TYPE_PIIX3_PCI_DEVICE "pci-piix3" -DECLARE_INSTANCE_CHECKER(PIIX3State, PIIX3_PCI_DEVICE, - TYPE_PIIX3_PCI_DEVICE) + bool has_acpi; + bool has_pic; + bool has_pit; + bool has_usb; + bool smm_enabled; +}; + +#define TYPE_PIIX_PCI_DEVICE "pci-piix" +OBJECT_DECLARE_SIMPLE_TYPE(PIIXState, PIIX_PCI_DEVICE) #define TYPE_PIIX3_DEVICE "PIIX3" -#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen" #define TYPE_PIIX4_PCI_DEVICE "piix4-isa" #endif diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h deleted file mode 100644 index ef1946c7f8..0000000000 --- a/include/hw/sparc/grlib.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * QEMU GRLIB Components - * - * Copyright (c) 2010-2019 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 - * 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 GRLIB_H -#define GRLIB_H - -#include "hw/sysbus.h" - -/* Emulation of GrLib device is base on the GRLIB IP Core User's Manual: - * http://www.gaisler.com/products/grlib/grip.pdf - */ - -/* IRQMP */ -#define TYPE_GRLIB_IRQMP "grlib-irqmp" - -void grlib_irqmp_ack(DeviceState *dev, int intno); - -/* GPTimer */ -#define TYPE_GRLIB_GPTIMER "grlib-gptimer" - -/* APB UART */ -#define TYPE_GRLIB_APB_UART "grlib-apbuart" - -#endif /* GRLIB_H */ diff --git a/include/hw/ssi/allwinner-a10-spi.h b/include/hw/ssi/allwinner-a10-spi.h new file mode 100644 index 0000000000..da46e29a27 --- /dev/null +++ b/include/hw/ssi/allwinner-a10-spi.h @@ -0,0 +1,57 @@ +/* + * Allwinner SPI Bus Serial Interface registers definition + * + * Copyright (C) 2024 Strahinja Jankovic. + * + * 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: GPL-2.0-or-later + */ + +#ifndef ALLWINNER_A10_SPI_H +#define ALLWINNER_A10_SPI_H + +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" +#include "qemu/fifo8.h" +#include "qom/object.h" + +/** Size of register I/O address space used by SPI device */ +#define AW_A10_SPI_IOSIZE (0x1000) + +/** Total number of known registers */ +#define AW_A10_SPI_REGS_NUM (AW_A10_SPI_IOSIZE / sizeof(uint32_t)) +#define AW_A10_SPI_FIFO_SIZE (64) +#define AW_A10_SPI_CS_LINES_NR (4) + +#define TYPE_AW_A10_SPI "allwinner.spi" +OBJECT_DECLARE_SIMPLE_TYPE(AWA10SPIState, AW_A10_SPI) + +struct AWA10SPIState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + SSIBus *bus; + qemu_irq irq; + qemu_irq cs_lines[AW_A10_SPI_CS_LINES_NR]; + + uint32_t regs[AW_A10_SPI_REGS_NUM]; + + Fifo8 rx_fifo; + Fifo8 tx_fifo; +}; + +#endif /* ALLWINNER_A10_SPI_H */ diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 8e1dda556b..25b95e7406 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -76,11 +76,13 @@ struct AspeedSMCState { AddressSpace flash_as; MemoryRegion *dram_mr; AddressSpace dram_as; + uint64_t dram_base; AspeedSMCFlash flashes[ASPEED_SMC_CS_MAX]; uint8_t snoop_index; uint8_t snoop_dummies; + bool unselect; }; typedef struct AspeedSegments { @@ -106,6 +108,7 @@ struct AspeedSMCClass { uint32_t features; hwaddr dma_flash_mask; hwaddr dma_dram_mask; + uint32_t dma_start_length; uint32_t nregs; uint32_t (*segment_to_reg)(const AspeedSMCState *s, const AspeedSegments *seg); @@ -113,6 +116,7 @@ struct AspeedSMCClass { AspeedSegments *seg); void (*dma_ctrl)(AspeedSMCState *s, uint32_t value); int (*addr_width)(const AspeedSMCState *s); + const MemoryRegionOps *reg_ops; }; #endif /* ASPEED_SMC_H */ 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 1f6d077766..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" 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/pnv_spi.h b/include/hw/ssi/pnv_spi.h new file mode 100644 index 0000000000..8815f67d45 --- /dev/null +++ b/include/hw/ssi/pnv_spi.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This model Supports a connection to a single SPI responder. + * Introduced for P10 to provide access to SPI seeproms, TPM, flash device + * and an ADC controller. + * + * All SPI function control is mapped into the SPI register space to enable + * full control by firmware. + * + * SPI Controller has sequencer and shift engine. The SPI shift engine + * performs serialization and de-serialization according to the control by + * the sequencer and according to the setup defined in the configuration + * registers and the SPI sequencer implements the main control logic. + */ + +#ifndef PPC_PNV_SPI_H +#define PPC_PNV_SPI_H + +#include "hw/ssi/ssi.h" +#include "hw/sysbus.h" + +#define TYPE_PNV_SPI "pnv-spi" +OBJECT_DECLARE_SIMPLE_TYPE(PnvSpi, PNV_SPI) + +#define PNV_SPI_REG_SIZE 8 +#define PNV_SPI_REGS 7 + +#define TYPE_PNV_SPI_BUS "pnv-spi-bus" +typedef struct PnvSpi { + SysBusDevice parent_obj; + + SSIBus *ssi_bus; + qemu_irq *cs_line; + MemoryRegion xscom_spic_regs; + /* SPI object number */ + uint32_t spic_num; + uint8_t transfer_len; + uint8_t responder_select; + /* To verify if shift_n1 happens prior to shift_n2 */ + bool shift_n1_done; + /* Loop counter for branch operation opcode Ex/Fx */ + uint8_t loop_counter_1; + uint8_t loop_counter_2; + /* N1/N2_bits specifies the size of the N1/N2 segment of a frame in bits.*/ + uint8_t N1_bits; + uint8_t N2_bits; + /* Number of bytes in a payload for the N1/N2 frame segment.*/ + uint8_t N1_bytes; + uint8_t N2_bytes; + /* Number of N1/N2 bytes marked for transmit */ + uint8_t N1_tx; + uint8_t N2_tx; + /* Number of N1/N2 bytes marked for receive */ + uint8_t N1_rx; + uint8_t N2_rx; + + /* SPI registers */ + uint64_t regs[PNV_SPI_REGS]; + uint8_t seq_op[PNV_SPI_REG_SIZE]; + uint64_t status; +} PnvSpi; +#endif /* PPC_PNV_SPI_H */ diff --git a/include/hw/ssi/pnv_spi_regs.h b/include/hw/ssi/pnv_spi_regs.h new file mode 100644 index 0000000000..596e2c1911 --- /dev/null +++ b/include/hw/ssi/pnv_spi_regs.h @@ -0,0 +1,133 @@ +/* + * QEMU PowerPC SPI model + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PNV_SPI_CONTROLLER_REGS_H +#define PNV_SPI_CONTROLLER_REGS_H + +/* + * Macros from target/ppc/cpu.h + * These macros are copied from ppc target specific file target/ppc/cpu.h + * as target/ppc/cpu.h cannot be included here. + */ +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) | PPC_BIT8(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))) + +/* Error Register */ +#define ERROR_REG 0x00 + +/* counter_config_reg */ +#define SPI_CTR_CFG_REG 0x01 +#define SPI_CTR_CFG_N1 PPC_BITMASK(0, 7) +#define SPI_CTR_CFG_N2 PPC_BITMASK(8, 15) +#define SPI_CTR_CFG_CMP1 PPC_BITMASK(24, 31) +#define SPI_CTR_CFG_CMP2 PPC_BITMASK(32, 39) +#define SPI_CTR_CFG_N1_CTRL_B1 PPC_BIT(49) +#define SPI_CTR_CFG_N1_CTRL_B2 PPC_BIT(50) +#define SPI_CTR_CFG_N1_CTRL_B3 PPC_BIT(51) +#define SPI_CTR_CFG_N2_CTRL_B0 PPC_BIT(52) +#define SPI_CTR_CFG_N2_CTRL_B1 PPC_BIT(53) +#define SPI_CTR_CFG_N2_CTRL_B2 PPC_BIT(54) +#define SPI_CTR_CFG_N2_CTRL_B3 PPC_BIT(55) + +/* config_reg */ +#define CONFIG_REG1 0x02 + +/* clock_config_reset_control_ecc_enable_reg */ +#define SPI_CLK_CFG_REG 0x03 +#define SPI_CLK_CFG_HARD_RST 0x0084000000000000; +#define SPI_CLK_CFG_RST_CTRL PPC_BITMASK(24, 27) +#define SPI_CLK_CFG_ECC_EN PPC_BIT(28) +#define SPI_CLK_CFG_ECC_CTRL PPC_BITMASK(29, 30) + +/* memory_mapping_reg */ +#define SPI_MM_REG 0x04 +#define SPI_MM_RDR_MATCH_VAL PPC_BITMASK(32, 47) +#define SPI_MM_RDR_MATCH_MASK PPC_BITMASK(48, 63) + +/* transmit_data_reg */ +#define SPI_XMIT_DATA_REG 0x05 + +/* receive_data_reg */ +#define SPI_RCV_DATA_REG 0x06 + +/* sequencer_operation_reg */ +#define SPI_SEQ_OP_REG 0x07 + +/* status_reg */ +#define SPI_STS_REG 0x08 +#define SPI_STS_RDR_FULL PPC_BIT(0) +#define SPI_STS_RDR_OVERRUN PPC_BIT(1) +#define SPI_STS_RDR_UNDERRUN PPC_BIT(2) +#define SPI_STS_TDR_FULL PPC_BIT(4) +#define SPI_STS_TDR_OVERRUN PPC_BIT(5) +#define SPI_STS_TDR_UNDERRUN PPC_BIT(6) +#define SPI_STS_SEQ_FSM PPC_BITMASK(8, 15) +#define SPI_STS_SHIFTER_FSM PPC_BITMASK(16, 27) +#define SPI_STS_SEQ_INDEX PPC_BITMASK(28, 31) +#define SPI_STS_GEN_STATUS_B3 PPC_BIT(35) +#define SPI_STS_RDR PPC_BITMASK(1, 3) +#define SPI_STS_TDR PPC_BITMASK(5, 7) + +/* + * Shifter states + * + * These are the same values defined for the Shifter FSM field of the + * status register. It's a 12 bit field so we will represent it as three + * nibbles in the constants. + * + * These are shifter_fsm values + * + * Status reg bits 16-27 -> field bits 0-11 + * bits 0,1,2,5 unused/reserved + * bit 4 crc shift in (unused) + * bit 8 crc shift out (unused) + */ + +#define FSM_DONE 0x100 /* bit 3 */ +#define FSM_SHIFT_N2 0x020 /* bit 6 */ +#define FSM_WAIT 0x010 /* bit 7 */ +#define FSM_SHIFT_N1 0x004 /* bit 9 */ +#define FSM_START 0x002 /* bit 10 */ +#define FSM_IDLE 0x001 /* bit 11 */ + +/* + * Sequencer states + * + * These are sequencer_fsm values + * + * Status reg bits 8-15 -> field bits 0-7 + * bits 0-3 unused/reserved + * + */ +#define SEQ_STATE_INDEX_INCREMENT 0x08 /* bit 4 */ +#define SEQ_STATE_EXECUTE 0x04 /* bit 5 */ +#define SEQ_STATE_DECODE 0x02 /* bit 6 */ +#define SEQ_STATE_IDLE 0x01 /* bit 7 */ + +/* + * These are the supported sequencer operations. + * Only the upper nibble is significant because for many operations + * the lower nibble is a variable specific to the operation. + */ +#define SEQ_OP_STOP 0x00 +#define SEQ_OP_SELECT_SLAVE 0x10 +#define SEQ_OP_SHIFT_N1 0x30 +#define SEQ_OP_SHIFT_N2 0x40 +#define SEQ_OP_BRANCH_IFNEQ_RDR 0x60 +#define SEQ_OP_TRANSFER_TDR 0xC0 +#define SEQ_OP_BRANCH_IFNEQ_INC_1 0xE0 +#define SEQ_OP_BRANCH_IFNEQ_INC_2 0xF0 +#define NUM_SEQ_OPS 8 + +#endif 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 6950f86810..3cdcbd5390 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -64,6 +64,9 @@ struct SSIPeripheral { /* Chip select state */ bool cs; + + /* Chip select index */ + uint8_t cs_index; }; extern const VMStateDescription vmstate_ssi_peripheral; @@ -109,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..c9b1e0e90e 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -82,10 +82,6 @@ qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n); 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/hw/timer/grlib_gptimer.h b/include/hw/timer/grlib_gptimer.h new file mode 100644 index 0000000000..e56f1b8bf3 --- /dev/null +++ b/include/hw/timer/grlib_gptimer.h @@ -0,0 +1,32 @@ +/* + * QEMU GRLIB GPTimer + * + * 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 + * 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 GRLIB_GPTIMER_H +#define GRLIB_GPTIMER_H + +#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 49b7bc4334..a1aa203792 100644 --- a/include/hw/timer/i8254.h +++ b/include/hw/timer/i8254.h @@ -64,7 +64,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/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 32c23a5ca2..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 */ @@ -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); @@ -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 0bf3f2aa17..b8b4266543 100644 --- a/include/hw/usb/dwc2-regs.h +++ b/include/hw/usb/dwc2-regs.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause) */ /* * Imported from the Linux kernel file drivers/usb/dwc2/hw.h, commit * a89bae709b3492b478480a2c9734e7e9393b279c ("usb: dwc2: Move @@ -838,7 +838,7 @@ struct dwc2_dma_desc { uint32_t status; uint32_t buf; -} __packed; +} QEMU_PACKED; /* Host Mode DMA descriptor status quadlet */ 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 deleted file mode 100644 index f30a26f7f4..0000000000 --- a/include/hw/usb/hcd-musb.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics, - * USB2.0 OTG compliant core used in various chips. - * - * Only host-mode and non-DMA accesses are currently supported. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef HW_USB_HCD_MUSB_H -#define HW_USB_HCD_MUSB_H - -enum musb_irq_source_e { - musb_irq_suspend = 0, - musb_irq_resume, - musb_irq_rst_babble, - musb_irq_sof, - musb_irq_connect, - musb_irq_disconnect, - musb_irq_vbus_request, - musb_irq_vbus_error, - musb_irq_rx, - musb_irq_tx, - musb_set_vbus, - musb_set_session, - /* Add new interrupts here */ - musb_irq_max /* total number of interrupts defined */ -}; - -/* TODO convert hcd-musb to QOM/qdev and remove MUSBReadFunc/MUSBWriteFunc */ -typedef void MUSBWriteFunc(void *opaque, hwaddr addr, uint32_t value); -typedef uint32_t MUSBReadFunc(void *opaque, hwaddr addr); -extern MUSBReadFunc * const musb_read[]; -extern MUSBWriteFunc * const musb_write[]; - -typedef struct MUSBState MUSBState; - -MUSBState *musb_init(DeviceState *parent_device, int gpio_base); -void musb_reset(MUSBState *s); -uint32_t musb_core_intr_get(MUSBState *s); -void musb_core_intr_clear(MUSBState *s, uint32_t mask); -void musb_set_size(MUSBState *s, int epnum, int size, int is_tx); - -#endif 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..e0ce6ec3a9 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -30,6 +30,9 @@ #include #endif #include "sysemu/sysemu.h" +#include "hw/vfio/vfio-container-base.h" +#include "sysemu/host_iommu_device.h" +#include "sysemu/iommufd.h" #define VFIO_MSG_PREFIX "vfio %s: " @@ -61,58 +64,30 @@ 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; -} VFIOMigration; + 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; -typedef struct VFIOAddressSpace { - AddressSpace *as; - QLIST_HEAD(, VFIOContainer) containers; - QLIST_ENTRY(VFIOAddressSpace) list; -} VFIOAddressSpace; + bool event_save_iterate_started; + bool event_precopy_empty_hit; +} VFIOMigration; 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; +OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); typedef struct VFIOHostDMAWindow { hwaddr min_iova; @@ -121,21 +96,44 @@ typedef struct VFIOHostDMAWindow { QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; } VFIOHostDMAWindow; +typedef struct IOMMUFDBackend IOMMUFDBackend; + +typedef struct VFIOIOASHwpt { + uint32_t hwpt_id; + uint32_t hwpt_flags; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOIOASHwpt) next; +} VFIOIOASHwpt; + +typedef struct VFIOIOMMUFDContainer { + VFIOContainerBase bcontainer; + IOMMUFDBackend *be; + uint32_t ioas_id; + QLIST_HEAD(, VFIOIOASHwpt) hwpt_list; +} VFIOIOMMUFDContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); + 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; int fd; int type; + bool mdev; bool reset_works; bool needs_reset; bool no_mmap; bool ram_block_discard_allowed; - bool enable_migration; + OnOffAuto enable_migration; + bool migration_events; VFIODeviceOps *ops; unsigned int num_irqs; unsigned int num_regions; @@ -143,6 +141,15 @@ typedef struct VFIODevice { VFIOMigration *migration; Error *migration_blocker; OnOffAuto pre_copy_dirty_page_tracking; + OnOffAuto device_dirty_page_tracking; + bool dirty_pages_supported; + bool dirty_tracking; + bool iommu_dirty_tracking; + HostIOMMUDevice *hiod; + int devid; + IOMMUFDBackend *iommufd; + VFIOIOASHwpt *hwpt; + QLIST_ENTRY(VFIODevice) hwpt_next; } VFIODevice; struct VFIODeviceOps { @@ -150,7 +157,30 @@ struct VFIODeviceOps { int (*vfio_hot_reset_multi)(VFIODevice *vdev); void (*vfio_eoi)(VFIODevice *vdev); Object *(*vfio_get_object)(VFIODevice *vdev); - void (*vfio_save_config)(VFIODevice *vdev, QEMUFile *f); + + /** + * @vfio_save_config + * + * Save device config state + * + * @vdev: #VFIODevice for which to save the config + * @f: #QEMUFile where to send the data + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*vfio_save_config)(VFIODevice *vdev, QEMUFile *f, Error **errp); + + /** + * @vfio_load_config + * + * Load device config state + * + * @vdev: #VFIODevice for which to load the config + * @f: #QEMUFile where to get the data + * + * Returns zero to indicate success and negative for error + */ int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f); }; @@ -164,8 +194,12 @@ typedef struct VFIOGroup { bool ram_block_discard_allowed; } VFIOGroup; +#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ + TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" + typedef struct VFIODMABuf { - QemuDmaBuf buf; + QemuDmaBuf *buf; uint32_t pos_x, pos_y, pos_updates; uint32_t hot_x, hot_y, hot_updates; int dmabuf_id; @@ -190,12 +224,16 @@ 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); +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer); + 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); -int vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp); +bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp); void vfio_region_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); uint64_t vfio_region_read(void *opaque, @@ -208,17 +246,35 @@ 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); +bool vfio_device_is_mdev(VFIODevice *vbasedev); +bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp); +bool 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); + +bool 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 +288,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, Error **errp); +int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr, Error **errp); +/* Returns 0 on success, or a negative errno. */ +bool 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); +int vfio_device_get_aw_bits(VFIODevice *vdev); #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..62a8b60d87 --- /dev/null +++ b/include/hw/vfio/vfio-container-base.h @@ -0,0 +1,165 @@ +/* + * 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 { + Object parent; + 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); +bool 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, Error **errp); +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); + +GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); + +static inline uint64_t +vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) +{ + assert(bcontainer); + return bcontainer->pgsizes; +} + +#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" + +OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) + +struct VFIOIOMMUClass { + ObjectClass parent_class; + + /* Properties */ + const char *hiod_typename; + + /* basic feature */ + bool (*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); + bool (*attach_device)(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp); + void (*detach_device)(VFIODevice *vbasedev); + + /* migration feature */ + + /** + * @set_dirty_page_tracking + * + * Start or stop dirty pages tracking on VFIO container + * + * @bcontainer: #VFIOContainerBase on which to de/activate dirty + * page tracking + * @start: indicates whether to start or stop dirty pages tracking + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer, + bool start, Error **errp); + /** + * @query_dirty_bitmap + * + * Get bitmap of dirty pages from container + * + * @bcontainer: #VFIOContainerBase from which to get dirty pages + * @vbmap: #VFIOBitmap internal bitmap structure + * @iova: iova base address + * @size: size of iova range + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); + /* PCI specific */ + int (*pci_hot_reset)(VFIODevice *vbasedev, bool single); + + /* SPAPR specific */ + bool (*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/cbor-helpers.h b/include/hw/virtio/cbor-helpers.h new file mode 100644 index 0000000000..f25fd481ad --- /dev/null +++ b/include/hw/virtio/cbor-helpers.h @@ -0,0 +1,45 @@ +/* + * QEMU CBOR helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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_CBOR_HELPERS_H +#define QEMU_VIRTIO_CBOR_HELPERS_H + +#include + +bool qemu_cbor_map_add(cbor_item_t *map, cbor_item_t *key, cbor_item_t *value); + +bool qemu_cbor_array_push(cbor_item_t *array, cbor_item_t *value); + +bool qemu_cbor_add_bool_to_map(cbor_item_t *map, const char *key, bool value); + +bool qemu_cbor_add_uint8_to_map(cbor_item_t *map, const char *key, + uint8_t value); + +bool qemu_cbor_add_map_to_map(cbor_item_t *map, const char *key, + size_t nested_map_size, + cbor_item_t **nested_map); + +bool qemu_cbor_add_bytestring_to_map(cbor_item_t *map, const char *key, + uint8_t *arr, size_t len); + +bool qemu_cbor_add_null_to_map(cbor_item_t *map, const char *key); + +bool qemu_cbor_add_string_to_map(cbor_item_t *map, const char *key, + const char *value); + +bool qemu_cbor_add_uint8_array_to_map(cbor_item_t *map, const char *key, + uint8_t *arr, size_t len); + +bool qemu_cbor_add_uint8_key_bytestring_to_map(cbor_item_t *map, uint8_t key, + uint8_t *buf, size_t len); + +bool qemu_cbor_add_uint64_to_map(cbor_item_t *map, const char *key, + uint64_t value); +#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 eab46d7f0b..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; @@ -73,6 +87,14 @@ 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, @@ -86,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); @@ -108,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, @@ -128,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; @@ -149,6 +182,10 @@ typedef struct VhostOps { 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; @@ -158,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; @@ -167,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, @@ -189,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-gpio.h b/include/hw/virtio/vhost-user-gpio.h index a9d3f9b049..5814a8400a 100644 --- a/include/hw/virtio/vhost-user-gpio.h +++ b/include/hw/virtio/vhost-user-gpio.h @@ -12,34 +12,13 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" -#include "standard-headers/linux/virtio_gpio.h" -#include "chardev/char-fe.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 { - /*< private >*/ - VirtIODevice parent_obj; - CharBackend chardev; - struct virtio_gpio_config config; - struct vhost_virtqueue *vhost_vqs; - struct vhost_dev vhost_dev; - VhostUserState vhost_user; - VirtQueue *command_vq; - VirtQueue *interrupt_vq; - /** - * There are at least two steps of initialization of the - * vhost-user device. The first is a "connect" step and - * second is a "start" step. Make a separation between - * those initialization phases by using two fields. - * - * @connected: see vu_gpio_connect()/vu_gpio_disconnect() - * @started_vu: see vu_gpio_start()/vu_gpio_stop() - */ - bool connected; - bool started_vu; - /*< public >*/ + 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 191216a74f..9a3f238b43 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 @@ -30,6 +54,7 @@ typedef struct VhostUserHostNotifier { void *addr; void *unmap_addr; int idx; + bool destroy; } VhostUserHostNotifier; /** diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 1111d85643..0a9575b469 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -19,31 +19,77 @@ #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_dma_map(struct vhost_vdpa *v, hwaddr iova, hwaddr size, - void *vaddr, bool readonly); -int vhost_vdpa_dma_unmap(struct vhost_vdpa *v, hwaddr iova, hwaddr size); +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 67a6807fac..461c168c37 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -8,6 +8,8 @@ #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 { @@ -33,6 +35,7 @@ struct vhost_virtqueue { unsigned used_size; EventNotifier masked_notifier; EventNotifier error_notifier; + EventNotifier masked_config_notifier; struct vhost_dev *dev; }; @@ -41,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; @@ -88,13 +92,32 @@ 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; - /** @acked_features: final set of negotiated features */ uint64_t acked_features; - /** @backend_features: backend specific feature bits */ uint64_t backend_features; - /** @protocol_features: final negotiated protocol 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? */ @@ -106,6 +129,7 @@ struct vhost_dev { void *opaque; struct vhost_log *log; QLIST_ENTRY(vhost_dev) entry; + QLIST_ENTRY(vhost_dev) logdev_entry; QLIST_HEAD(, vhost_iommu) iommu_list; IOMMUNotifier n; const VhostDevConfigOps *config_ops; @@ -147,6 +171,10 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, */ void vhost_dev_cleanup(struct vhost_dev *hdev); +void vhost_dev_disable_notifiers_nvqs(struct vhost_dev *hdev, + VirtIODevice *vdev, + unsigned int nvqs); + /** * vhost_dev_enable_notifiers() - enable event notifiers * @hdev: common vhost_dev structure @@ -168,6 +196,8 @@ 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 @@ -292,11 +322,13 @@ uint64_t vhost_get_features(struct vhost_dev *hdev, const int *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, @@ -306,11 +338,133 @@ void vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, 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); -int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f); int vhost_dev_prepare_inflight(struct vhost_dev *hdev, VirtIODevice *vdev); 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..cdfbd943ae --- /dev/null +++ b/include/hw/virtio/virtio-acpi.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * 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.h b/include/hw/virtio/virtio-blk.h index 7f589b4146..5c14110c4b 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -21,6 +21,7 @@ #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) @@ -37,6 +38,7 @@ struct VirtIOBlkConf { BlockConf conf; IOThread *iothread; + IOThreadVirtQueueMappingList *iothread_vq_mapping_list; char *serial; uint32_t request_merging; uint16_t num_queues; @@ -48,21 +50,27 @@ 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; @@ -93,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-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 2e28507efe..8c977beebd 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,7 @@ struct virtio_gpu_simple_resource { unsigned int iov_cnt; uint32_t scanout_bitmask; pixman_image_t *image; + qemu_pixman_shareable share_handle; uint64_t hostmem; uint64_t blob_size; @@ -75,6 +79,7 @@ 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 { @@ -90,6 +95,9 @@ 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, + VIRTIO_GPU_FLAG_VENUS_ENABLED, }; #define virtio_gpu_virgl_enabled(_cfg) \ @@ -102,12 +110,21 @@ 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) +#define virtio_gpu_venus_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_VENUS_ENABLED)) 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 { @@ -131,6 +148,8 @@ struct VirtIOGPUBase { int renderer_blocked; int enable; + MemoryRegion hostmem; + struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS]; int enabled_output_bitmask; @@ -151,7 +170,7 @@ struct VirtIOGPUBaseClass { DEFINE_PROP_UINT32("yres", _state, _conf.yres, 800) typedef struct VGPUDMABuf { - QemuDmaBuf buf; + QemuDmaBuf *buf; uint32_t scanout_id; QTAILQ_ENTRY(VGPUDMABuf) next; } VGPUDMABuf; @@ -159,6 +178,7 @@ typedef struct VGPUDMABuf { struct VirtIOGPU { VirtIOGPUBase parent_obj; + uint8_t scanout_vmstate_version; uint64_t conf_max_hostmem; VirtQueue *ctrl_vq; @@ -166,6 +186,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; @@ -174,8 +197,6 @@ struct VirtIOGPU { uint64_t hostmem; bool processing_cmdq; - QEMUTimer *fence_poll; - QEMUTimer *print_stats; uint32_t inflight; struct { @@ -189,6 +210,8 @@ struct VirtIOGPU { QTAILQ_HEAD(, VGPUDMABuf) bufs; VGPUDMABuf *primary[VIRTIO_GPU_MAX_SCANOUTS]; } dmabuf; + + GArray *capset_ids; }; struct VirtIOGPUClass { @@ -199,13 +222,28 @@ 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); }; +/* VirtIOGPUGL renderer states */ +typedef enum { + RS_START, /* starting state */ + RS_INIT_FAILED, /* failed initialisation */ + RS_INITED, /* initialised and working */ + RS_RESET, /* inited and reset pending, moves to start after reset */ +} RenderState; + struct VirtIOGPUGL { struct VirtIOGPU parent_obj; - bool renderer_inited; - bool renderer_reset; + RenderState renderer_state; + + QEMUTimer *fence_poll; + QEMUTimer *print_stats; + + QEMUBH *cmdq_resume_bh; }; struct VhostUserGPU { @@ -214,18 +252,39 @@ struct VhostUserGPU { VhostUserBackend *vhost; int vhost_gpu_fd; /* closed by the chardev */ CharBackend vhost_chr; - QemuDmaBuf dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; + QemuDmaBuf *dmabuf[VIRTIO_GPU_MAX_SCANOUTS]; 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) @@ -235,11 +294,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, @@ -258,6 +323,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); @@ -266,6 +333,21 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id); +/** + * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data + * fb: the frame-buffer descriptor to fill out + * ss: the scanout blob data + * blob_size: size of scanout blob data + * + * This will check we have enough space for the frame taking into + * account that stride. + * + * Returns true on success, otherwise logs guest error and returns false + */ +bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, + struct virtio_gpu_set_scanout_blob *ss, + uint64_t blob_size); + /* virtio-gpu-udmabuf.c */ bool virtio_gpu_have_udmabuf(void); void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res); @@ -276,6 +358,13 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, struct virtio_gpu_framebuffer *fb, struct virtio_gpu_rect *r); +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); +void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id); + /* virtio-gpu-3d.c */ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd); @@ -283,6 +372,6 @@ void virtio_gpu_virgl_fence_poll(VirtIOGPU *g); void virtio_gpu_virgl_reset_scanout(VirtIOGPU *g); void virtio_gpu_virgl_reset(VirtIOGPU *g); int virtio_gpu_virgl_init(VirtIOGPU *g); -int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g); +GArray *virtio_gpu_virgl_get_capsets(VirtIOGPU *g); #endif 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 2ad5ee320b..7db4210b16 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -24,6 +24,8 @@ #include "hw/virtio/virtio.h" #include "hw/pci/pci.h" #include "qom/object.h" +#include "qapi/qapi-types-virtio.h" +#include "sysemu/host_iommu_device.h" #define TYPE_VIRTIO_IOMMU "virtio-iommu-device" #define TYPE_VIRTIO_IOMMU_PCI "virtio-iommu-pci" @@ -39,6 +41,8 @@ typedef struct IOMMUDevice { 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; } IOMMUDevice; typedef struct IOMMUPciBus { @@ -53,14 +57,19 @@ struct VirtIOIOMMU { struct virtio_iommu_config config; uint64_t features; GHashTable *as_by_busptr; + GHashTable *host_iommu_devices; 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; 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..a1af144c28 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -14,6 +14,7 @@ #define HW_VIRTIO_MEM_H #include "standard-headers/linux/virtio_mem.h" +#include "hw/resettable.h" #include "hw/virtio/virtio.h" #include "qapi/qapi-types-misc.h" #include "sysemu/hostmem.h" @@ -31,7 +32,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 +46,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,11 +98,27 @@ 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; /* listeners to notify on plug/unplug activity. */ QLIST_HEAD(, RamDiscardListener) rdl_list; + + /* State of the resettable container */ + ResettableState reset_state; }; struct VirtIOMEMClass { @@ -88,8 +128,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 ef234ffe7e..b9ea9e824e 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -38,6 +38,12 @@ OBJECT_DECLARE_SIMPLE_TYPE(VirtIONet, VIRTIO_NET) /* 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; @@ -96,7 +102,7 @@ typedef struct VirtioNetRscStat { /* Rsc unit general info used to checking if can coalescing */ typedef struct VirtioNetRscUnit { void *ip; /* ip header */ - uint16_t *ip_plen; /* data len pointer in ip header field */ + void *ip_plen; /* pointer to unaligned uint16_t data len in ip header */ struct tcp_header *tcp; /* tcp header */ uint16_t tcp_hdrlen; /* tcp header len */ uint16_t payload; /* pure payload without virtio/eth/ip/tcp */ @@ -109,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; @@ -215,10 +221,12 @@ 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, @@ -227,5 +235,6 @@ size_t virtio_net_handle_ctrl_iov(VirtIODevice *vdev, 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-nsm.h b/include/hw/virtio/virtio-nsm.h new file mode 100644 index 0000000000..57ddbbbf3f --- /dev/null +++ b/include/hw/virtio/virtio-nsm.h @@ -0,0 +1,49 @@ +/* + * AWS Nitro Secure Module (NSM) device + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * 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_NSM_H +#define QEMU_VIRTIO_NSM_H + +#include "crypto/hash.h" +#include "hw/virtio/virtio.h" +#include "qom/object.h" + +#define NSM_MAX_PCRS 32 + +#define TYPE_VIRTIO_NSM "virtio-nsm-device" +OBJECT_DECLARE_SIMPLE_TYPE(VirtIONSM, VIRTIO_NSM) +#define VIRTIO_NSM_GET_PARENT_CLASS(obj) \ + OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_NSM) + +struct PCRInfo { + bool locked; + uint8_t data[QCRYPTO_HASH_DIGEST_LEN_SHA384]; +}; + +struct VirtIONSM { + VirtIODevice parent_obj; + + /* Only one vq - guest puts request and response buffers on it */ + VirtQueue *vq; + + /* NSM State */ + uint16_t max_pcrs; + struct PCRInfo pcrs[NSM_MAX_PCRS]; + char *digest; + char *module_id; + uint8_t version_major; + uint8_t version_minor; + uint8_t version_patch; + + bool (*extend_pcr)(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len); + void (*lock_pcr)(VirtIONSM *vnsm, int ind); +}; + +#endif diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 938799e8f6..971c5fabd4 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -43,6 +43,7 @@ enum { VIRTIO_PCI_FLAG_INIT_FLR_BIT, VIRTIO_PCI_FLAG_AER_BIT, VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED_BIT, + VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET_BIT, }; /* Need to activate work-arounds for buggy guests at vmstate load. */ @@ -79,6 +80,10 @@ enum { /* Init Power Management */ #define VIRTIO_PCI_FLAG_INIT_PM (1 << VIRTIO_PCI_FLAG_INIT_PM_BIT) +/* Init The No_Soft_Reset bit of Power Management */ +#define VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET \ + (1 << VIRTIO_PCI_FLAG_PM_NO_SOFT_RESET_BIT) + /* Init Function Level Reset capability */ #define VIRTIO_PCI_FLAG_INIT_FLR (1 << VIRTIO_PCI_FLAG_INIT_FLR_BIT) @@ -142,6 +147,9 @@ struct VirtIOPCIProxy { }; MemoryRegion modern_bar; MemoryRegion io_bar; + /* address space for VirtIOPCIRegions */ + AddressSpace modern_cfg_mem_as; + AddressSpace modern_cfg_io_as; uint32_t legacy_io_bar_idx; uint32_t msix_bar_idx; uint32_t modern_io_bar_idx; @@ -151,6 +159,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; @@ -184,6 +194,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. */ @@ -241,6 +254,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; @@ -256,5 +270,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 acfd4df125..6386910280 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -22,7 +22,7 @@ #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 @@ -30,7 +30,7 @@ * vhost-user to advertise VHOST_USER_F_PROTOCOL_FEATURES between QEMU * and a vhost-user backend. */ -#define VIRTIO_F_BAD_FEATURE 30 +#define VIRTIO_F_BAD_FEATURE 30 #define VIRTIO_LEGACY_FEATURES ((0x1ULL << VIRTIO_F_BAD_FEATURE) | \ (0x1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | \ @@ -69,6 +69,8 @@ typedef struct VirtQueueElement unsigned int ndescs; unsigned int out_num; unsigned int in_num; + /* Element has been processed (VIRTIO_F_IN_ORDER) */ + bool in_order_filled; hwaddr *in_addr; hwaddr *out_addr; struct iovec *in_sg; @@ -79,6 +81,9 @@ 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) @@ -93,6 +98,12 @@ enum virtio_device_endian { VIRTIO_DEVICE_ENDIAN_BIG, }; +/** + * struct VirtIODevice - common VirtIO structure + * @name: name of the device + * @status: VirtIO Device Status field + * + */ struct VirtIODevice { DeviceState parent_obj; @@ -100,9 +111,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; @@ -131,10 +153,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 { @@ -178,6 +210,8 @@ struct VirtioDeviceClass { void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); int (*start_ioeventfd)(VirtIODevice *vdev); void (*stop_ioeventfd)(VirtIODevice *vdev); + /* Called before loading queues. Useful to add queues before loading. */ + int (*pre_load_queues)(VirtIODevice *vdev); /* Saving and loading of a device; trying to deprecate save/load * use vmsd for new devices. */ @@ -191,12 +225,20 @@ struct VirtioDeviceClass { int (*post_load)(VirtIODevice *vdev); const VMStateDescription *vmsd; bool (*primary_unplug_pending)(void *opaque); + /* May be called even when vdev->vhost_started is false */ 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); @@ -234,9 +276,13 @@ void qemu_put_virtqueue_element(VirtIODevice *vdev, QEMUFile *f, VirtQueueElement *elem); int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, unsigned int out_bytes); -void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, - unsigned int *out_bytes, - unsigned max_in_bytes, unsigned max_out_bytes); +/** + * Return <0 on error or an opaque >=0 to pass to + * virtio_queue_enable_notification_and_check on success. + */ +int virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes, unsigned max_in_bytes, + unsigned max_out_bytes); void virtio_notify_irqfd(VirtIODevice *vdev, VirtQueue *vq); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); @@ -254,6 +300,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); @@ -263,6 +316,17 @@ int virtio_queue_ready(VirtQueue *vq); int virtio_queue_empty(VirtQueue *vq); +/** + * Enable notification and check whether guest has added some + * buffers since last call to virtqueue_get_avail_bytes. + * + * @opaque: value returned from virtqueue_get_avail_bytes + */ +bool virtio_queue_enable_notification_and_check(VirtQueue *vq, + int opaque); + +void virtio_queue_set_shadow_avail_idx(VirtQueue *vq, uint16_t idx); + /* Host binding interface. */ uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr); @@ -289,6 +353,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); @@ -324,7 +389,9 @@ typedef struct VirtIORNGConf VirtIORNGConf; DEFINE_PROP_BIT64("packed", _state, _field, \ VIRTIO_F_RING_PACKED, false), \ DEFINE_PROP_BIT64("queue_reset", _state, _field, \ - VIRTIO_F_RING_RESET, true) + VIRTIO_F_RING_RESET, true), \ + DEFINE_PROP_BIT64("in_order", _state, _field, \ + VIRTIO_F_IN_ORDER, false) hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); bool virtio_queue_enabled_legacy(VirtIODevice *vdev, int n); @@ -357,6 +424,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) { @@ -376,7 +446,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); @@ -422,9 +492,9 @@ static inline bool virtio_device_started(VirtIODevice *vdev, uint8_t status) * @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. + * This is similar to virtio_device_started() but ignores vdev->started + * and also encapsulates a check on the VM status which would prevent a + * device from starting anyway. */ static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status) { @@ -432,7 +502,7 @@ static inline bool virtio_device_should_start(VirtIODevice *vdev, uint8_t status return false; } - return virtio_device_started(vdev, status); + return status & VIRTIO_CONFIG_S_DRIVER_OK; } static inline void virtio_set_started(VirtIODevice *vdev, bool started) @@ -461,4 +531,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/sbsa_gwdt.h b/include/hw/watchdog/sbsa_gwdt.h index 70b137de30..4bdc6c6fdb 100644 --- a/include/hw/watchdog/sbsa_gwdt.h +++ b/include/hw/watchdog/sbsa_gwdt.h @@ -55,8 +55,6 @@ #define SBSA_GWDT_RMMIO_SIZE 0x1000 #define SBSA_GWDT_CMMIO_SIZE 0x1000 -#define SBSA_TIMER_FREQ 62500000 /* Hz */ - typedef struct SBSA_GWDTState { /* */ SysBusDevice parent_obj; @@ -67,6 +65,7 @@ typedef struct SBSA_GWDTState { qemu_irq irq; QEMUTimer *timer; + uint64_t freq; uint32_t id; uint32_t wcs; diff --git a/include/hw/watchdog/wdt_aspeed.h b/include/hw/watchdog/wdt_aspeed.h index dfa5dfa424..830b0a7936 100644 --- a/include/hw/watchdog/wdt_aspeed.h +++ b/include/hw/watchdog/wdt_aspeed.h @@ -19,9 +19,10 @@ OBJECT_DECLARE_TYPE(AspeedWDTState, AspeedWDTClass, ASPEED_WDT) #define TYPE_ASPEED_2400_WDT TYPE_ASPEED_WDT "-ast2400" #define TYPE_ASPEED_2500_WDT TYPE_ASPEED_WDT "-ast2500" #define TYPE_ASPEED_2600_WDT TYPE_ASPEED_WDT "-ast2600" +#define TYPE_ASPEED_2700_WDT TYPE_ASPEED_WDT "-ast2700" #define TYPE_ASPEED_1030_WDT TYPE_ASPEED_WDT "-ast1030" -#define ASPEED_WDT_REGS_MAX (0x20 / 4) +#define ASPEED_WDT_REGS_MAX (0x80 / 4) struct AspeedWDTState { /*< private >*/ @@ -40,7 +41,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..0f586c4384 --- /dev/null +++ b/include/hw/xen/xen-hvm-common.h @@ -0,0 +1,104 @@ +#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 MemoryRegion xen_grants; +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 + +#define XEN_GRANT_ADDR_OFF (1ULL << 63) + +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; + + bool has_bufioreq; + + 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, + uint8_t handle_bufioreq, + 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..e198b120c5 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,9 +39,8 @@ 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); -int xen_be_register(const char *type, struct XenDevOps *ops); +void xen_be_init(void); +int xen_be_register(const char *type, const struct XenDevOps *ops); int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenLegacyDevice *xendev); void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, @@ -52,22 +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; - -int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, - bool to_domain, XenGrantCopySegment segs[], - unsigned int nr_segs); + uint32_t *refs, unsigned int nr_refs); static inline void *xen_be_map_grant_ref(struct XenLegacyDevice *xendev, uint32_t ref, int prot) @@ -76,30 +57,14 @@ 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 */ -extern struct XenDevOps xen_console_ops; /* xen_console.c */ -extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ -extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ -extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ -#ifdef CONFIG_VIRTFS -extern struct XenDevOps xen_9pfs_ops; /* xen-9p-backend.c */ -#endif -extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */ -#ifdef CONFIG_USB_LIBUSB -extern struct XenDevOps xen_usb_ops; /* xen-usb.c */ -#endif - /* 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); #endif /* HW_XEN_LEGACY_BACKEND_H */ diff --git a/include/hw/xen/xen-pvh-common.h b/include/hw/xen/xen-pvh-common.h new file mode 100644 index 0000000000..5cdd23c2f4 --- /dev/null +++ b/include/hw/xen/xen-pvh-common.h @@ -0,0 +1,91 @@ +/* + * QEMU Xen PVH machine - common code. + * + * Copyright (c) 2024 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef XEN_PVH_COMMON_H__ +#define XEN_PVH_COMMON_H__ + +#include +#include "hw/sysbus.h" +#include "hw/hw.h" +#include "hw/xen/xen-hvm-common.h" +#include "hw/pci-host/gpex.h" + +#define TYPE_XEN_PVH_MACHINE MACHINE_TYPE_NAME("xen-pvh-base") +OBJECT_DECLARE_TYPE(XenPVHMachineState, XenPVHMachineClass, + XEN_PVH_MACHINE) + +struct XenPVHMachineClass { + MachineClass parent; + + /* PVH implementation specific init. */ + void (*init)(MachineState *state); + + /* + * set_pci_intx_irq - Deliver INTX irqs to the guest. + * + * @opaque: pointer to XenPVHMachineState. + * @irq: IRQ after swizzling, between 0-3. + * @level: IRQ level. + */ + void (*set_pci_intx_irq)(void *opaque, int irq, int level); + + /* + * set_pci_link_route: - optional implementation call to setup + * routing between INTX IRQ (0 - 3) and GSI's. + * + * @line: line the INTx line (0 => A .. 3 => B) + * @irq: GSI + */ + int (*set_pci_link_route)(uint8_t line, uint8_t irq); + + /* Allow implementations to optionally enable buffered ioreqs. */ + uint8_t handle_bufioreq; + + /* + * Each implementation can optionally enable features that it + * supports and are known to work. + */ + bool has_pci; + bool has_tpm; + bool has_virtio_mmio; +}; + +struct XenPVHMachineState { + /*< private >*/ + MachineState parent; + + XenIOState ioreq; + + struct { + MemoryRegion low; + MemoryRegion high; + } ram; + + struct { + GPEXHost gpex; + MemoryRegion mmio_alias; + MemoryRegion mmio_high_alias; + } pci; + + struct { + MemMapEntry ram_low, ram_high; + MemMapEntry tpm; + + /* Virtio-mmio */ + MemMapEntry virtio_mmio; + uint32_t virtio_mmio_num; + uint32_t virtio_mmio_irq_base; + + /* PCI */ + MemMapEntry pci_ecam, pci_mmio, pci_mmio_high; + uint32_t pci_intx_irq_base; + } cfg; +}; + +void xen_pvh_class_setup_common_props(XenPVHMachineClass *xpc); +#endif diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index afdf9c436a..ecb89ecfc1 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -1,35 +1,51 @@ -#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; extern enum xen_mode xen_mode; extern bool xen_domid_restrict; +extern bool xen_is_stubdomain; int xen_pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num); int xen_set_pci_link_route(uint8_t link, uint8_t irq); -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_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_common.h b/include/hw/xen/xen_common.h deleted file mode 100644 index 77ce17d8a4..0000000000 --- a/include/hw/xen/xen_common.h +++ /dev/null @@ -1,690 +0,0 @@ -#ifndef QEMU_HW_XEN_COMMON_H -#define QEMU_HW_XEN_COMMON_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 -#include -#include "hw/xen/interface/io/xenbus.h" - -#include "hw/xen/xen.h" -#include "hw/pci/pci.h" -#include "hw/xen/trace.h" - -extern xc_interface *xen_xc; - -/* - * We don't support Xen prior to 4.2.0. - */ - -/* 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 - -typedef xc_interface xendevicemodel_handle; - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ - -#undef XC_WANT_COMPAT_DEVICEMODEL_API -#include - -#endif - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 - -static inline int xendevicemodel_relocate_memory( - xendevicemodel_handle *dmod, domid_t domid, uint32_t size, uint64_t src_gfn, - uint64_t dst_gfn) -{ - uint32_t i; - int rc; - - for (i = 0; i < size; i++) { - unsigned long idx = src_gfn + i; - xen_pfn_t gpfn = dst_gfn + i; - - rc = xc_domain_add_to_physmap(xen_xc, domid, XENMAPSPACE_gmfn, idx, - gpfn); - if (rc) { - return rc; - } - } - - return 0; -} - -static inline int xendevicemodel_pin_memory_cacheattr( - xendevicemodel_handle *dmod, domid_t domid, uint64_t start, uint64_t end, - uint32_t type) -{ - return xc_domain_pin_memory_cacheattr(xen_xc, domid, start, end, type); -} - -typedef void xenforeignmemory_resource_handle; - -#define XENMEM_resource_ioreq_server 0 - -#define XENMEM_resource_ioreq_server_frame_bufioreq 0 -#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) - -static inline xenforeignmemory_resource_handle *xenforeignmemory_map_resource( - xenforeignmemory_handle *fmem, domid_t domid, unsigned int type, - unsigned int id, unsigned long frame, unsigned long nr_frames, - void **paddr, int prot, int flags) -{ - errno = EOPNOTSUPP; - return NULL; -} - -static inline int xenforeignmemory_unmap_resource( - xenforeignmemory_handle *fmem, xenforeignmemory_resource_handle *fres) -{ - return 0; -} - -#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 */ - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 - -#define XEN_COMPAT_PHYSMAP -static inline void *xenforeignmemory_map2(xenforeignmemory_handle *h, - uint32_t dom, void *addr, - int prot, int flags, size_t pages, - const xen_pfn_t arr[/*pages*/], - int err[/*pages*/]) -{ - assert(addr == NULL && flags == 0); - return xenforeignmemory_map(h, dom, prot, pages, arr, err); -} - -static inline int xentoolcore_restrict_all(domid_t domid) -{ - errno = ENOTTY; - return -1; -} - -static inline int xendevicemodel_shutdown(xendevicemodel_handle *dmod, - domid_t domid, unsigned int reason) -{ - errno = ENOTTY; - return -1; -} - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 41000 */ - -#include - -#endif - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 - -static inline xendevicemodel_handle *xendevicemodel_open( - struct xentoollog_logger *logger, unsigned int open_flags) -{ - 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) -{ - return xc_hvm_create_ioreq_server(dmod, domid, handle_bufioreq, - id); -} - -static inline int xendevicemodel_get_ioreq_server_info( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, - xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, - evtchn_port_t *bufioreq_port) -{ - return xc_hvm_get_ioreq_server_info(dmod, domid, id, ioreq_pfn, - bufioreq_pfn, bufioreq_port); -} - -static inline int xendevicemodel_map_io_range_to_ioreq_server( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int is_mmio, - uint64_t start, uint64_t end) -{ - return xc_hvm_map_io_range_to_ioreq_server(dmod, domid, id, is_mmio, - start, end); -} - -static inline int xendevicemodel_unmap_io_range_from_ioreq_server( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int is_mmio, - uint64_t start, uint64_t end) -{ - return xc_hvm_unmap_io_range_from_ioreq_server(dmod, domid, id, is_mmio, - start, end); -} - -static inline int xendevicemodel_map_pcidev_to_ioreq_server( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, - uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) -{ - return xc_hvm_map_pcidev_to_ioreq_server(dmod, domid, id, segment, - bus, device, function); -} - -static inline int xendevicemodel_unmap_pcidev_from_ioreq_server( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, - uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) -{ - return xc_hvm_unmap_pcidev_from_ioreq_server(dmod, domid, id, segment, - bus, device, function); -} - -static inline int xendevicemodel_destroy_ioreq_server( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id) -{ - return xc_hvm_destroy_ioreq_server(dmod, domid, id); -} - -static inline int xendevicemodel_set_ioreq_server_state( - xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int enabled) -{ - 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) -{ - return xc_hvm_set_pci_intx_level(dmod, domid, segment, bus, device, - intx, level); -} - -static inline int xendevicemodel_set_isa_irq_level( - xendevicemodel_handle *dmod, domid_t domid, uint8_t irq, - unsigned int level) -{ - return xc_hvm_set_isa_irq_level(dmod, domid, irq, level); -} - -static inline int xendevicemodel_set_pci_link_route( - xendevicemodel_handle *dmod, domid_t domid, uint8_t link, uint8_t irq) -{ - return xc_hvm_set_pci_link_route(dmod, domid, link, irq); -} - -static inline int xendevicemodel_inject_msi( - xendevicemodel_handle *dmod, domid_t domid, uint64_t msi_addr, - uint32_t msi_data) -{ - return xc_hvm_inject_msi(dmod, domid, msi_addr, msi_data); -} - -static inline int xendevicemodel_track_dirty_vram( - xendevicemodel_handle *dmod, domid_t domid, uint64_t first_pfn, - uint32_t nr, unsigned long *dirty_bitmap) -{ - return xc_hvm_track_dirty_vram(dmod, domid, first_pfn, nr, - dirty_bitmap); -} - -static inline int xendevicemodel_modified_memory( - xendevicemodel_handle *dmod, domid_t domid, uint64_t first_pfn, - uint32_t nr) -{ - return xc_hvm_modified_memory(dmod, domid, first_pfn, nr); -} - -static inline int xendevicemodel_set_mem_type( - xendevicemodel_handle *dmod, domid_t domid, hvmmem_type_t mem_type, - uint64_t first_pfn, uint32_t nr) -{ - return xc_hvm_set_mem_type(dmod, domid, mem_type, first_pfn, nr); -} - -#endif - -extern xendevicemodel_handle *xen_dmod; - -static inline int xen_set_mem_type(domid_t domid, hvmmem_type_t type, - uint64_t first_pfn, uint32_t nr) -{ - return xendevicemodel_set_mem_type(xen_dmod, domid, type, first_pfn, - nr); -} - -static inline int xen_set_pci_intx_level(domid_t domid, uint16_t segment, - uint8_t bus, uint8_t device, - uint8_t intx, unsigned int level) -{ - return xendevicemodel_set_pci_intx_level(xen_dmod, domid, segment, bus, - device, intx, level); -} - -static inline int xen_inject_msi(domid_t domid, uint64_t msi_addr, - uint32_t msi_data) -{ - return xendevicemodel_inject_msi(xen_dmod, domid, msi_addr, msi_data); -} - -static inline int xen_set_isa_irq_level(domid_t domid, uint8_t irq, - unsigned int level) -{ - return xendevicemodel_set_isa_irq_level(xen_dmod, domid, irq, level); -} - -static inline int xen_track_dirty_vram(domid_t domid, uint64_t first_pfn, - uint32_t nr, unsigned long *bitmap) -{ - return xendevicemodel_track_dirty_vram(xen_dmod, domid, first_pfn, nr, - bitmap); -} - -static inline int xen_modified_memory(domid_t domid, uint64_t first_pfn, - uint32_t nr) -{ - return xendevicemodel_modified_memory(xen_dmod, domid, first_pfn, nr); -} - -static inline int xen_restrict(domid_t domid) -{ - int rc; - rc = xentoolcore_restrict_all(domid); - trace_xen_domid_restrict(rc ? errno : 0); - return rc; -} - -void destroy_hvm_domain(bool reboot); - -/* shutdown/destroy current domain because of an error */ -void xen_shutdown_fatal_error(const char *fmt, ...) G_GNUC_PRINTF(1, 2); - -#ifdef HVM_PARAM_VMPORT_REGS_PFN -static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, - xen_pfn_t *vmport_regs_pfn) -{ - int rc; - uint64_t value; - rc = xc_hvm_param_get(xc, dom, HVM_PARAM_VMPORT_REGS_PFN, &value); - if (rc >= 0) { - *vmport_regs_pfn = (xen_pfn_t) value; - } - return rc; -} -#else -static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, - xen_pfn_t *vmport_regs_pfn) -{ - return -ENOSYS; -} -#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, - evtchn_port_t - *bufioreq_evtchn) -{ - unsigned long param; - int rc; - - rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_IOREQ_PFN, ¶m); - if (rc < 0) { - fprintf(stderr, "failed to get HVM_PARAM_IOREQ_PFN\n"); - return -1; - } - - *ioreq_pfn = param; - - rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_BUFIOREQ_PFN, ¶m); - if (rc < 0) { - fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_PFN\n"); - return -1; - } - - *bufioreq_pfn = param; - - rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_BUFIOREQ_EVTCHN, - ¶m); - if (rc < 0) { - fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_EVTCHN\n"); - return -1; - } - - *bufioreq_evtchn = param; - - 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, - ioservid_t ioservid, - MemoryRegionSection *section) -{ - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - hwaddr end_addr = start_addr + size - 1; - - if (use_default_ioreq_server) { - return; - } - - trace_xen_map_mmio_range(ioservid, start_addr, end_addr); - xendevicemodel_map_io_range_to_ioreq_server(xen_dmod, dom, ioservid, 1, - start_addr, end_addr); -} - -static inline void xen_unmap_memory_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - hwaddr end_addr = start_addr + size - 1; - - if (use_default_ioreq_server) { - return; - } - - trace_xen_unmap_mmio_range(ioservid, start_addr, end_addr); - xendevicemodel_unmap_io_range_from_ioreq_server(xen_dmod, dom, ioservid, - 1, start_addr, end_addr); -} - -static inline void xen_map_io_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - hwaddr end_addr = start_addr + size - 1; - - if (use_default_ioreq_server) { - return; - } - - trace_xen_map_portio_range(ioservid, start_addr, end_addr); - xendevicemodel_map_io_range_to_ioreq_server(xen_dmod, dom, ioservid, 0, - start_addr, end_addr); -} - -static inline void xen_unmap_io_section(domid_t dom, - ioservid_t ioservid, - MemoryRegionSection *section) -{ - hwaddr start_addr = section->offset_within_address_space; - ram_addr_t size = int128_get64(section->size); - hwaddr end_addr = start_addr + size - 1; - - if (use_default_ioreq_server) { - return; - } - - trace_xen_unmap_portio_range(ioservid, start_addr, end_addr); - xendevicemodel_unmap_io_range_from_ioreq_server(xen_dmod, dom, ioservid, - 0, start_addr, end_addr); -} - -static inline void xen_map_pcidev(domid_t dom, - ioservid_t ioservid, - PCIDevice *pci_dev) -{ - if (use_default_ioreq_server) { - return; - } - - trace_xen_map_pcidev(ioservid, pci_dev_bus_num(pci_dev), - PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); - xendevicemodel_map_pcidev_to_ioreq_server(xen_dmod, dom, ioservid, 0, - pci_dev_bus_num(pci_dev), - PCI_SLOT(pci_dev->devfn), - PCI_FUNC(pci_dev->devfn)); -} - -static inline void xen_unmap_pcidev(domid_t dom, - ioservid_t ioservid, - PCIDevice *pci_dev) -{ - if (use_default_ioreq_server) { - return; - } - - trace_xen_unmap_pcidev(ioservid, pci_dev_bus_num(pci_dev), - PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); - xendevicemodel_unmap_pcidev_from_ioreq_server(xen_dmod, dom, ioservid, 0, - pci_dev_bus_num(pci_dev), - PCI_SLOT(pci_dev->devfn), - PCI_FUNC(pci_dev->devfn)); -} - -static inline void xen_create_ioreq_server(domid_t dom, - ioservid_t *ioservid) -{ - int rc = xendevicemodel_create_ioreq_server(xen_dmod, dom, - HVM_IOREQSRV_BUFIOREQ_ATOMIC, - ioservid); - - if (rc == 0) { - trace_xen_ioreq_server_create(*ioservid); - return; - } - - *ioservid = 0; - use_default_ioreq_server = true; - trace_xen_default_ioreq_server(); -} - -static inline void xen_destroy_ioreq_server(domid_t dom, - ioservid_t ioservid) -{ - if (use_default_ioreq_server) { - return; - } - - trace_xen_ioreq_server_destroy(ioservid); - xendevicemodel_destroy_ioreq_server(xen_dmod, dom, 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) -{ - if (use_default_ioreq_server) { - return xen_get_default_ioreq_server_info(dom, ioreq_pfn, - bufioreq_pfn, - bufioreq_evtchn); - } - - return xendevicemodel_get_ioreq_server_info(xen_dmod, dom, ioservid, - ioreq_pfn, bufioreq_pfn, - bufioreq_evtchn); -} - -static inline int xen_set_ioreq_server_state(domid_t dom, - ioservid_t ioservid, - bool enable) -{ - if (use_default_ioreq_server) { - return 0; - } - - trace_xen_ioreq_server_state(ioservid, enable); - return xendevicemodel_set_ioreq_server_state(xen_dmod, dom, ioservid, - 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) -{ - return -ENOSYS; -} -#endif - -#endif /* QEMU_HW_XEN_COMMON_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_native.h b/include/hw/xen/xen_native.h new file mode 100644 index 0000000000..5caf91a616 --- /dev/null +++ b/include/hw/xen/xen_native.h @@ -0,0 +1,551 @@ +#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 + * 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 +#include + +#include "hw/xen/xen.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.7.1. + */ + +#include + +extern xenforeignmemory_handle *xen_fmem; + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 + +typedef xc_interface xendevicemodel_handle; + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ + +#undef XC_WANT_COMPAT_DEVICEMODEL_API +#include + +#endif + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 + +static inline int xendevicemodel_relocate_memory( + xendevicemodel_handle *dmod, domid_t domid, uint32_t size, uint64_t src_gfn, + uint64_t dst_gfn) +{ + uint32_t i; + int rc; + + for (i = 0; i < size; i++) { + unsigned long idx = src_gfn + i; + xen_pfn_t gpfn = dst_gfn + i; + + rc = xc_domain_add_to_physmap(xen_xc, domid, XENMAPSPACE_gmfn, idx, + gpfn); + if (rc) { + return rc; + } + } + + return 0; +} + +static inline int xendevicemodel_pin_memory_cacheattr( + xendevicemodel_handle *dmod, domid_t domid, uint64_t start, uint64_t end, + uint32_t type) +{ + return xc_domain_pin_memory_cacheattr(xen_xc, domid, start, end, type); +} + +typedef void xenforeignmemory_resource_handle; + +#define XENMEM_resource_ioreq_server 0 + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + +static inline xenforeignmemory_resource_handle *xenforeignmemory_map_resource( + xenforeignmemory_handle *fmem, domid_t domid, unsigned int type, + unsigned int id, unsigned long frame, unsigned long nr_frames, + void **paddr, int prot, int flags) +{ + errno = EOPNOTSUPP; + return NULL; +} + +static inline int xenforeignmemory_unmap_resource( + xenforeignmemory_handle *fmem, xenforeignmemory_resource_handle *fres) +{ + return 0; +} + +#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 */ + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 + +#define XEN_COMPAT_PHYSMAP +static inline void *xenforeignmemory_map2(xenforeignmemory_handle *h, + uint32_t dom, void *addr, + int prot, int flags, size_t pages, + const xen_pfn_t arr[/*pages*/], + int err[/*pages*/]) +{ + assert(addr == NULL && flags == 0); + return xenforeignmemory_map(h, dom, prot, pages, arr, err); +} + +static inline int xentoolcore_restrict_all(domid_t domid) +{ + errno = ENOTTY; + return -1; +} + +static inline int xendevicemodel_shutdown(xendevicemodel_handle *dmod, + domid_t domid, unsigned int reason) +{ + errno = ENOTTY; + return -1; +} + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 41000 */ + +#include + +#endif + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 + +static inline xendevicemodel_handle *xendevicemodel_open( + struct xentoollog_logger *logger, unsigned int open_flags) +{ + return xen_xc; +} + +static inline int xendevicemodel_create_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, int handle_bufioreq, + ioservid_t *id) +{ + return xc_hvm_create_ioreq_server(dmod, domid, handle_bufioreq, + id); +} + +static inline int xendevicemodel_get_ioreq_server_info( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, + xen_pfn_t *ioreq_pfn, xen_pfn_t *bufioreq_pfn, + evtchn_port_t *bufioreq_port) +{ + return xc_hvm_get_ioreq_server_info(dmod, domid, id, ioreq_pfn, + bufioreq_pfn, bufioreq_port); +} + +static inline int xendevicemodel_map_io_range_to_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int is_mmio, + uint64_t start, uint64_t end) +{ + return xc_hvm_map_io_range_to_ioreq_server(dmod, domid, id, is_mmio, + start, end); +} + +static inline int xendevicemodel_unmap_io_range_from_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int is_mmio, + uint64_t start, uint64_t end) +{ + return xc_hvm_unmap_io_range_from_ioreq_server(dmod, domid, id, is_mmio, + start, end); +} + +static inline int xendevicemodel_map_pcidev_to_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, + uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) +{ + return xc_hvm_map_pcidev_to_ioreq_server(dmod, domid, id, segment, + bus, device, function); +} + +static inline int xendevicemodel_unmap_pcidev_from_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, + uint16_t segment, uint8_t bus, uint8_t device, uint8_t function) +{ + return xc_hvm_unmap_pcidev_from_ioreq_server(dmod, domid, id, segment, + bus, device, function); +} + +static inline int xendevicemodel_destroy_ioreq_server( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id) +{ + return xc_hvm_destroy_ioreq_server(dmod, domid, id); +} + +static inline int xendevicemodel_set_ioreq_server_state( + xendevicemodel_handle *dmod, domid_t domid, ioservid_t id, int enabled) +{ + return xc_hvm_set_ioreq_server_state(dmod, domid, id, enabled); +} + +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) +{ + return xc_hvm_set_pci_intx_level(dmod, domid, segment, bus, device, + intx, level); +} + +static inline int xendevicemodel_set_isa_irq_level( + xendevicemodel_handle *dmod, domid_t domid, uint8_t irq, + unsigned int level) +{ + return xc_hvm_set_isa_irq_level(dmod, domid, irq, level); +} + +static inline int xendevicemodel_set_pci_link_route( + xendevicemodel_handle *dmod, domid_t domid, uint8_t link, uint8_t irq) +{ + return xc_hvm_set_pci_link_route(dmod, domid, link, irq); +} + +static inline int xendevicemodel_inject_msi( + xendevicemodel_handle *dmod, domid_t domid, uint64_t msi_addr, + uint32_t msi_data) +{ + return xc_hvm_inject_msi(dmod, domid, msi_addr, msi_data); +} + +static inline int xendevicemodel_track_dirty_vram( + xendevicemodel_handle *dmod, domid_t domid, uint64_t first_pfn, + uint32_t nr, unsigned long *dirty_bitmap) +{ + return xc_hvm_track_dirty_vram(dmod, domid, first_pfn, nr, + dirty_bitmap); +} + +static inline int xendevicemodel_modified_memory( + xendevicemodel_handle *dmod, domid_t domid, uint64_t first_pfn, + uint32_t nr) +{ + return xc_hvm_modified_memory(dmod, domid, first_pfn, nr); +} + +static inline int xendevicemodel_set_mem_type( + xendevicemodel_handle *dmod, domid_t domid, hvmmem_type_t mem_type, + uint64_t first_pfn, uint32_t nr) +{ + return xc_hvm_set_mem_type(dmod, domid, mem_type, first_pfn, nr); +} + +#endif + +extern xendevicemodel_handle *xen_dmod; + +static inline int xen_set_mem_type(domid_t domid, hvmmem_type_t type, + uint64_t first_pfn, uint32_t nr) +{ + return xendevicemodel_set_mem_type(xen_dmod, domid, type, first_pfn, + nr); +} + +static inline int xen_set_pci_intx_level(domid_t domid, uint16_t segment, + uint8_t bus, uint8_t device, + uint8_t intx, unsigned int level) +{ + return xendevicemodel_set_pci_intx_level(xen_dmod, domid, segment, bus, + device, intx, level); +} + +static inline int xen_inject_msi(domid_t domid, uint64_t msi_addr, + uint32_t msi_data) +{ + return xendevicemodel_inject_msi(xen_dmod, domid, msi_addr, msi_data); +} + +static inline int xen_set_isa_irq_level(domid_t domid, uint8_t irq, + unsigned int level) +{ + return xendevicemodel_set_isa_irq_level(xen_dmod, domid, irq, level); +} + +static inline int xen_track_dirty_vram(domid_t domid, uint64_t first_pfn, + uint32_t nr, unsigned long *bitmap) +{ + return xendevicemodel_track_dirty_vram(xen_dmod, domid, first_pfn, nr, + bitmap); +} + +static inline int xen_modified_memory(domid_t domid, uint64_t first_pfn, + uint32_t nr) +{ + return xendevicemodel_modified_memory(xen_dmod, domid, first_pfn, nr); +} + +static inline int xen_restrict(domid_t domid) +{ + int rc; + rc = xentoolcore_restrict_all(domid); + trace_xen_domid_restrict(rc ? errno : 0); + return rc; +} + +void destroy_hvm_domain(bool reboot); + +/* shutdown/destroy current domain because of an error */ +void xen_shutdown_fatal_error(const char *fmt, ...) G_GNUC_PRINTF(1, 2); + +#ifdef HVM_PARAM_VMPORT_REGS_PFN +static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, + xen_pfn_t *vmport_regs_pfn) +{ + int rc; + uint64_t value; + rc = xc_hvm_param_get(xc, dom, HVM_PARAM_VMPORT_REGS_PFN, &value); + if (rc >= 0) { + *vmport_regs_pfn = (xen_pfn_t) value; + } + return rc; +} +#else +static inline int xen_get_vmport_regs_pfn(xc_interface *xc, domid_t dom, + xen_pfn_t *vmport_regs_pfn) +{ + return -ENOSYS; +} +#endif + +static inline int xen_get_default_ioreq_server_info(domid_t dom, + xen_pfn_t *ioreq_pfn, + xen_pfn_t *bufioreq_pfn, + evtchn_port_t + *bufioreq_evtchn) +{ + unsigned long param; + int rc; + + rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_IOREQ_PFN, ¶m); + if (rc < 0) { + fprintf(stderr, "failed to get HVM_PARAM_IOREQ_PFN\n"); + return -1; + } + + *ioreq_pfn = param; + + rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_BUFIOREQ_PFN, ¶m); + if (rc < 0) { + fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_PFN\n"); + return -1; + } + + *bufioreq_pfn = param; + + rc = xc_get_hvm_param(xen_xc, dom, HVM_PARAM_BUFIOREQ_EVTCHN, + ¶m); + if (rc < 0) { + fprintf(stderr, "failed to get HVM_PARAM_BUFIOREQ_EVTCHN\n"); + return -1; + } + + *bufioreq_evtchn = param; + + return 0; +} + +static bool use_default_ioreq_server; + +static inline void xen_map_memory_section(domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + if (use_default_ioreq_server) { + return; + } + + trace_xen_map_mmio_range(ioservid, start_addr, end_addr); + xendevicemodel_map_io_range_to_ioreq_server(xen_dmod, dom, ioservid, 1, + start_addr, end_addr); +} + +static inline void xen_unmap_memory_section(domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + if (use_default_ioreq_server) { + return; + } + + trace_xen_unmap_mmio_range(ioservid, start_addr, end_addr); + xendevicemodel_unmap_io_range_from_ioreq_server(xen_dmod, dom, ioservid, + 1, start_addr, end_addr); +} + +static inline void xen_map_io_section(domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + if (use_default_ioreq_server) { + return; + } + + trace_xen_map_portio_range(ioservid, start_addr, end_addr); + xendevicemodel_map_io_range_to_ioreq_server(xen_dmod, dom, ioservid, 0, + start_addr, end_addr); +} + +static inline void xen_unmap_io_section(domid_t dom, + ioservid_t ioservid, + MemoryRegionSection *section) +{ + hwaddr start_addr = section->offset_within_address_space; + ram_addr_t size = int128_get64(section->size); + hwaddr end_addr = start_addr + size - 1; + + if (use_default_ioreq_server) { + return; + } + + trace_xen_unmap_portio_range(ioservid, start_addr, end_addr); + xendevicemodel_unmap_io_range_from_ioreq_server(xen_dmod, dom, ioservid, + 0, start_addr, end_addr); +} + +static inline void xen_map_pcidev(domid_t dom, + ioservid_t ioservid, + PCIDevice *pci_dev) +{ + if (use_default_ioreq_server) { + return; + } + + trace_xen_map_pcidev(ioservid, pci_dev_bus_num(pci_dev), + PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); + xendevicemodel_map_pcidev_to_ioreq_server(xen_dmod, dom, ioservid, 0, + pci_dev_bus_num(pci_dev), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); +} + +static inline void xen_unmap_pcidev(domid_t dom, + ioservid_t ioservid, + PCIDevice *pci_dev) +{ + if (use_default_ioreq_server) { + return; + } + + trace_xen_unmap_pcidev(ioservid, pci_dev_bus_num(pci_dev), + PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); + xendevicemodel_unmap_pcidev_from_ioreq_server(xen_dmod, dom, ioservid, 0, + pci_dev_bus_num(pci_dev), + PCI_SLOT(pci_dev->devfn), + PCI_FUNC(pci_dev->devfn)); +} + +static inline int xen_create_ioreq_server(domid_t dom, + int handle_bufioreq, + ioservid_t *ioservid) +{ + int rc = xendevicemodel_create_ioreq_server(xen_dmod, dom, + handle_bufioreq, + ioservid); + + if (rc == 0) { + trace_xen_ioreq_server_create(*ioservid); + 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, + ioservid_t ioservid) +{ + if (use_default_ioreq_server) { + return; + } + + trace_xen_ioreq_server_destroy(ioservid); + xendevicemodel_destroy_ioreq_server(xen_dmod, dom, 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) +{ + if (use_default_ioreq_server) { + return xen_get_default_ioreq_server_info(dom, ioreq_pfn, + bufioreq_pfn, + bufioreq_evtchn); + } + + return xendevicemodel_get_ioreq_server_info(xen_dmod, dom, ioservid, + ioreq_pfn, bufioreq_pfn, + bufioreq_evtchn); +} + +static inline int xen_set_ioreq_server_state(domid_t dom, + ioservid_t ioservid, + bool enable) +{ + if (use_default_ioreq_server) { + return 0; + } + + trace_xen_ioreq_server_state(ioservid, enable); + return xendevicemodel_set_ioreq_server_state(xen_dmod, dom, ioservid, + enable); +} + +#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 -1; +} +#endif + +#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..0c98444047 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 @@ -27,7 +29,6 @@ struct XenDevOps { const char *node); void (*frontend_changed)(struct XenLegacyDevice *xendev, const char *node); - int (*backend_register)(void); }; struct XenLegacyDevice { @@ -38,6 +39,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; @@ -50,7 +52,7 @@ struct XenLegacyDevice { xenevtchn_handle *evtchndev; xengnttab_handle *gnttabdev; - struct XenDevOps *ops; + const struct XenDevOps *ops; QTAILQ_ENTRY(XenLegacyDevice) next; }; @@ -63,7 +65,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-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-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..bdf0bca92a 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,12 +153,16 @@ 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, Error **errp); + int (*io_peerpid)(QIOChannel *ioc, + unsigned int *pid, + Error **errp); }; /* General I/O handling functions */ @@ -188,6 +209,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 +246,7 @@ ssize_t qio_channel_readv_full(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp); @@ -295,10 +318,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 +345,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 +370,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 +454,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 +474,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 +493,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 +515,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 +542,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 +807,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 +826,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 +854,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 +864,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 +899,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 +925,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 +959,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: @@ -891,4 +984,22 @@ int qio_channel_writev_full_all(QIOChannel *ioc, int qio_channel_flush(QIOChannel *ioc, Error **errp); +/** + * qio_channel_get_peercred: + * @ioc: the channel object + * @pid: pointer to pid + * @errp: pointer to a NULL-initialized error object + * + * Returns the pid of the peer process connected to this socket. + * + * The use of this function is possible only for connected + * AF_UNIX stream sockets and for AF_UNIX stream and datagram + * socket pairs on Linux. + * Return -1 on error with pid -1 for the non-Linux OS. + * + */ +int qio_channel_get_peerpid(QIOChannel *ioc, + unsigned int *pid, + Error **errp); + #endif /* QIO_CHANNEL_H */ 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..43222ef5ae 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. + */ +void 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..804eb23c06 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); @@ -38,17 +39,11 @@ void precopy_add_notifier(NotifierWithReturn *n); void precopy_remove_notifier(NotifierWithReturn *n); 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 */ -#ifdef CONFIG_LIVE_BLOCK_MIGRATION -void blk_mig_init(void); -#else -static inline void blk_mig_init(void) {} -#endif - AnnounceParameters *migrate_announce_params(void); /* migration/savevm.c */ @@ -57,22 +52,58 @@ void dump_vmstate_json_to_file(FILE *out_fp); /* migration/migration.c */ 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_is_running(void); +bool migration_thread_is_self(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); +void migration_file_set_error(int ret, Error *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); -/* migration/block-dirty-bitmap.c */ -void dirty_bitmap_mig_init(void); - #endif 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..f60e797894 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -16,66 +16,289 @@ #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() + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*save_setup)(QEMUFile *f, void *opaque, Error **errp); + + /** + * @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; - int (*load_setup)(QEMUFile *f, void *opaque); + /** + * @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() + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns zero to indicate success and negative for error + */ + int (*load_setup)(QEMUFile *f, void *opaque, Error **errp); + + /** + * @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..f313f2f408 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; @@ -369,16 +388,6 @@ extern const VMStateInfo vmstate_info_qlist; .offset = vmstate_offset_varray(_state, _field, _type), \ } -#define VMSTATE_ARRAY_TEST(_field, _state, _num, _test, _info, _type) {\ - .name = (stringify(_field)), \ - .field_exists = (_test), \ - .num = (_num), \ - .info = &(_info), \ - .size = sizeof(_type), \ - .flags = VMS_ARRAY, \ - .offset = vmstate_offset_array(_state, _field, _type, _num),\ -} - #define VMSTATE_SUB_ARRAY(_field, _state, _start, _num, _version, _info, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -705,8 +714,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 +725,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 +751,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 +761,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 +1178,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 +1203,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 +1220,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..b679aaebbf 100644 --- a/include/monitor/hmp-target.h +++ b/include/monitor/hmp-target.h @@ -25,11 +25,10 @@ #ifndef MONITOR_HMP_TARGET_H #define MONITOR_HMP_TARGET_H +typedef struct MonitorDef MonitorDef; + +#ifdef COMPILING_PER_TARGET #include "cpu.h" - -#define MD_TLONG 0 -#define MD_I32 1 - struct MonitorDef { const char *name; int offset; @@ -37,6 +36,10 @@ struct MonitorDef { int val); int type; }; +#endif + +#define MD_TLONG 0 +#define MD_I32 1 const MonitorDef *target_monitor_defs(void); int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval); @@ -51,5 +54,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 dfbc0c9a2f..ae116d9804 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -15,10 +15,11 @@ #define HMP_H #include "qemu/readline.h" -#include "qemu/coroutine.h" #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); @@ -34,9 +35,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vnc(Monitor *mon, const QDict *qdict); void hmp_info_spice(Monitor *mon, const QDict *qdict); void hmp_info_balloon(Monitor *mon, const QDict *qdict); -void hmp_info_irq(Monitor *mon, const QDict *qdict); void hmp_info_pic(Monitor *mon, const QDict *qdict); -void hmp_info_rdma(Monitor *mon, const QDict *qdict); void hmp_info_pci(Monitor *mon, const QDict *qdict); void hmp_info_tpm(Monitor *mon, const QDict *qdict); void hmp_info_iothreads(Monitor *mon, const QDict *qdict); @@ -55,6 +54,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); @@ -73,6 +73,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); @@ -81,6 +89,9 @@ 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 coroutine_fn hmp_screendump(Monitor *mon, const QDict *qdict); void hmp_chardev_add(Monitor *mon, const QDict *qdict); @@ -90,7 +101,6 @@ void hmp_chardev_send_break(Monitor *mon, const QDict *qdict); void hmp_object_add(Monitor *mon, const QDict *qdict); void hmp_object_del(Monitor *mon, const QDict *qdict); void hmp_info_memdev(Monitor *mon, const QDict *qdict); -void hmp_info_numa(Monitor *mon, const QDict *qdict); void hmp_info_memory_devices(Monitor *mon, const QDict *qdict); void hmp_qom_list(Monitor *mon, const QDict *qdict); void hmp_qom_get(Monitor *mon, const QDict *qdict); @@ -101,6 +111,8 @@ 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); @@ -127,7 +139,6 @@ void hmp_rocker_ports(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); void hmp_info_dump(Monitor *mon, const QDict *qdict); -void hmp_info_ramblock(Monitor *mon, const QDict *qdict); void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict); void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict); void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict); @@ -143,5 +154,29 @@ 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); +void hmp_dumpdtb(Monitor *mon, const QDict *qdict); #endif diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 737e750670..c3740ec616 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -35,10 +35,14 @@ 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); @@ -46,11 +50,9 @@ 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); -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags); + const char *opaque, Error **errp); +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp); void monitor_fdset_dup_fd_remove(int dup_fd); -int64_t monitor_fdset_dup_fd_find(int dup_fd); void monitor_register_hmp(const char *name, bool info, void (*cmd)(Monitor *mon, const QDict *qdict)); 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/announce.h b/include/net/announce.h index 3d90c83c23..72e7e501f7 100644 --- a/include/net/announce.h +++ b/include/net/announce.h @@ -12,12 +12,12 @@ #include "qapi/qapi-types-net.h" #include "qemu/timer.h" -struct AnnounceTimer { +typedef struct AnnounceTimer { QEMUTimer *tm; AnnounceParameters params; QEMUClockType type; int round; -}; +} AnnounceTimer; /* Returns: update the timer to the next time point */ int64_t qemu_announce_timer_step(AnnounceTimer *timer); diff --git a/include/net/checksum.h b/include/net/checksum.h index 7dec37e56c..188e4cca0b 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -30,7 +30,7 @@ uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq); uint16_t net_checksum_finish(uint32_t sum); uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, uint8_t *addrs, uint8_t *buf); -void net_checksum_calculate(uint8_t *data, int length, int csum_flag); +void net_checksum_calculate(void *data, int length, int csum_flag); static inline uint32_t net_checksum_add(int len, uint8_t *buf) diff --git a/include/net/eth.h b/include/net/eth.h index 6e699b0d7a..14c34f530f 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 */ @@ -54,7 +56,7 @@ struct ip_header { uint8_t ip_p; /* protocol */ uint16_t ip_sum; /* checksum */ uint32_t ip_src, ip_dst; /* source and destination address */ -}; +} QEMU_PACKED; typedef struct tcp_header { uint16_t th_sport; /* source port */ @@ -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 dc20b31e9f..cdd5b109b0 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -54,10 +54,11 @@ 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 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,7 +72,6 @@ typedef struct NetClientInfo { NetClientDriver type; size_t size; NetReceive *receive; - NetReceive *receive_raw; NetReceiveIOV *receive_iov; NetCanReceive *can_receive; NetStart *start; @@ -82,9 +82,9 @@ typedef struct NetClientInfo { QueryRxFilter *query_rx_filter; NetPoll *poll; HasUfo *has_ufo; + HasUso *has_uso; HasVnetHdr *has_vnet_hdr; HasVnetHdrLen *has_vnet_hdr_len; - UsingVnetHdr *using_vnet_hdr; SetOffload *set_offload; SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; @@ -115,9 +115,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; @@ -151,6 +154,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); @@ -168,9 +172,6 @@ ssize_t qemu_sendv_packet_async(NetClientState *nc, const struct iovec *iov, int iovcnt, NetPacketSent *sent_cb); ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size); ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size); -ssize_t qemu_receive_packet_iov(NetClientState *nc, - const struct iovec *iov, - int iovcnt); ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size); ssize_t qemu_send_packet_async(NetClientState *nc, const uint8_t *buf, int size, NetPacketSent *sent_cb); @@ -181,27 +182,99 @@ 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); -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 @@ -217,14 +290,11 @@ struct NICInfo { int nvectors; }; -extern int nb_nics; -extern NICInfo nd_table[MAX_NICS]; -extern const char *host_net_devices[]; - /* from net.c */ -bool netdev_is_modern(const char *optarg); -void netdev_parse_modern(const char *optarg); -void 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); void net_init_clients(void); void net_check_clients(void); @@ -234,7 +304,6 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict); void netdev_add(QemuOpts *opts, Error **errp); int net_hub_id_for_client(NetClientState *nc, int *id); -NetClientState *net_hub_port_find(int hub_id); #define DEFAULT_NETWORK_SCRIPT CONFIG_SYSCONFDIR "/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT CONFIG_SYSCONFDIR "/qemu-ifdown" diff --git a/include/net/queue.h b/include/net/queue.h index 9f2f289d77..2e686b1b61 100644 --- a/include/net/queue.h +++ b/include/net/queue.h @@ -59,10 +59,6 @@ ssize_t qemu_net_queue_receive(NetQueue *queue, const uint8_t *data, size_t size); -ssize_t qemu_net_queue_receive_iov(NetQueue *queue, - const struct iovec *iov, - int iovcnt); - ssize_t qemu_net_queue_send(NetQueue *queue, NetClientState *sender, unsigned flags, 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 40b9a40074..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); @@ -52,4 +51,6 @@ 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/clone-visitor.h b/include/qapi/clone-visitor.h index adf9a788e2..ebc182b034 100644 --- a/include/qapi/clone-visitor.h +++ b/include/qapi/clone-visitor.h @@ -11,6 +11,7 @@ #ifndef QAPI_CLONE_VISITOR_H #define QAPI_CLONE_VISITOR_H +#include "qapi/error.h" #include "qapi/visitor.h" /* @@ -20,11 +21,8 @@ */ typedef struct QapiCloneVisitor QapiCloneVisitor; -void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *, - void **, Error **)); -void qapi_clone_members(void *dst, const void *src, size_t sz, - bool (*visit_type_members)(Visitor *, void *, - Error **)); +Visitor *qapi_clone_visitor_new(void); +Visitor *qapi_clone_members_visitor_new(void); /* * Deep-clone QAPI object @src of the given @type, and return the result. @@ -32,10 +30,18 @@ void qapi_clone_members(void *dst, const void *src, size_t sz, * Not usable on QAPI scalars (integers, strings, enums), nor on a * QAPI object that references the 'any' type. Safe when @src is NULL. */ -#define QAPI_CLONE(type, src) \ - ((type *)qapi_clone(src, \ - (bool (*)(Visitor *, const char *, void **, \ - Error **))visit_type_ ## type)) +#define QAPI_CLONE(type, src) \ + ({ \ + Visitor *v_; \ + type *dst_ = (type *) (src); /* Cast away const */ \ + \ + if (dst_) { \ + v_ = qapi_clone_visitor_new(); \ + visit_type_ ## type(v_, NULL, &dst_, &error_abort); \ + visit_free(v_); \ + } \ + dst_; \ + }) /* * Copy deep clones of @type members from @src to @dst. @@ -43,9 +49,14 @@ void qapi_clone_members(void *dst, const void *src, size_t sz, * Not usable on QAPI scalars (integers, strings, enums), nor on a * QAPI object that references the 'any' type. */ -#define QAPI_CLONE_MEMBERS(type, dst, src) \ - qapi_clone_members(dst, src, sizeof(type), \ - (bool (*)(Visitor *, void *, \ - Error **))visit_type_ ## type ## _members) +#define QAPI_CLONE_MEMBERS(type, dst, src) \ + ({ \ + Visitor *v_; \ + \ + v_ = qapi_clone_members_visitor_new(); \ + *(type *)(dst) = *(src); \ + visit_type_ ## type ## _members(v_, (type *)(dst), &error_abort); \ + visit_free(v_); \ + }) #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 87ca83b155..d1db6f18cd 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -17,52 +17,10 @@ * add new ones! */ -#define QERR_BUS_NO_HOTPLUG \ - "Bus '%s' does not support hotplugging" - -#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'" - -#define QERR_INVALID_PARAMETER_TYPE \ - "Invalid parameter type for '%s', expected: %s" - #define QERR_INVALID_PARAMETER_VALUE \ "Parameter '%s' expects %s" -#define QERR_IO_ERROR \ - "An IO error has occurred" - -#define QERR_MIGRATION_ACTIVE \ - "There's a migration process in progress" - #define QERR_MISSING_PARAMETER \ "Parameter '%s' is missing" -#define QERR_PROPERTY_VALUE_BAD \ - "Property '%s.%s' doesn't take value '%s'" - -#define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ - "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" - -#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" - #endif /* QERROR_H */ diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index 9003b71fd3..256d782688 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_IDENTIFIER(_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..b8254247b8 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_strip_qual(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/accel.h b/include/qemu/accel.h index ce4747634a..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,8 @@ 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); @@ -90,11 +92,17 @@ 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: diff --git a/include/qemu/async-teardown.h b/include/qemu/async-teardown.h index 092e7a37e7..b281da005b 100644 --- a/include/qemu/async-teardown.h +++ b/include/qemu/async-teardown.h @@ -13,8 +13,6 @@ #ifndef QEMU_ASYNC_TEARDOWN_H #define QEMU_ASYNC_TEARDOWN_H -#include "config-host.h" - #ifdef CONFIG_LINUX void init_async_teardown(void); #endif diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 874134fd19..7a3f2e6576 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -20,48 +20,6 @@ /* Compiler barrier */ #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) -/* The variable that receives the old value of an atomically-accessed - * variable must be non-qualified, because atomic builtins return values - * through a pointer-type argument as in __atomic_load(&var, &old, MODEL). - * - * This macro has to handle types smaller than int manually, because of - * implicit promotion. int and larger types, as well as pointers, can be - * converted to a non-qualified type just by applying a binary operator. - */ -#define typeof_strip_qual(expr) \ - typeof( \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), bool) || \ - __builtin_types_compatible_p(typeof(expr), const bool) || \ - __builtin_types_compatible_p(typeof(expr), volatile bool) || \ - __builtin_types_compatible_p(typeof(expr), const volatile bool), \ - (bool)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), signed char) || \ - __builtin_types_compatible_p(typeof(expr), const signed char) || \ - __builtin_types_compatible_p(typeof(expr), volatile signed char) || \ - __builtin_types_compatible_p(typeof(expr), const volatile signed char), \ - (signed char)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), unsigned char) || \ - __builtin_types_compatible_p(typeof(expr), const unsigned char) || \ - __builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \ - __builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \ - (unsigned char)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), signed short) || \ - __builtin_types_compatible_p(typeof(expr), const signed short) || \ - __builtin_types_compatible_p(typeof(expr), volatile signed short) || \ - __builtin_types_compatible_p(typeof(expr), const volatile signed short), \ - (signed short)1, \ - __builtin_choose_expr( \ - __builtin_types_compatible_p(typeof(expr), unsigned short) || \ - __builtin_types_compatible_p(typeof(expr), const unsigned short) || \ - __builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \ - __builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \ - (unsigned short)1, \ - (expr)+0)))))) - #ifndef __ATOMIC_RELAXED #error "Expecting C11 atomic ops" #endif @@ -157,13 +115,20 @@ smp_read_barrier_depends(); #endif -#define qatomic_rcu_read(ptr) \ - ({ \ +/* + * 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; \ + typeof_strip_qual(*ptr) _val; \ + qatomic_rcu_read__nocheck(ptr, &_val); \ + _val; \ }) +#define qatomic_rcu_read(ptr) \ + qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val)) #define qatomic_rcu_set(ptr, i) do { \ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ @@ -195,7 +160,7 @@ 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, \ @@ -245,23 +210,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 a915fdc0a1..0044333cb5 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))) @@ -69,8 +69,16 @@ #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] +/* + * This is for use with the bit32 versions of set_bit() etc; + * we don't currently support the full range of bitmap operations + * on bitmaps backed by an array of uint32_t. + */ +#define DECLARE_BITMAP32(name, bits) \ + uint32_t name[BITS_TO_U32S(bits)] + #define small_nbits(nbits) \ - ((unsigned long)(nbits) <= BITS_PER_LONG) + ((nbits) <= BITS_PER_LONG) int slow_bitmap_empty(const unsigned long *bitmap, long bits); int slow_bitmap_full(const unsigned long *bitmap, long bits); @@ -92,17 +100,14 @@ long slow_bitmap_count_one(const unsigned long *bitmap, long nbits); static inline unsigned long *bitmap_try_new(long nbits) { - long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); - return (unsigned long *)g_try_malloc0(len); + long nelem = BITS_TO_LONGS(nbits); + return g_try_new0(unsigned long, nelem); } static inline unsigned long *bitmap_new(long nbits) { - unsigned long *ptr = bitmap_try_new(nbits); - if (ptr == NULL) { - abort(); - } - return ptr; + long nelem = BITS_TO_LONGS(nbits); + return g_new0(unsigned long, nelem); } static inline void bitmap_zero(unsigned long *dst, long nbits) @@ -265,10 +270,10 @@ unsigned long bitmap_find_next_zero_area(unsigned long *map, static inline unsigned long *bitmap_zero_extend(unsigned long *old, long old_nbits, long new_nbits) { - long new_len = BITS_TO_LONGS(new_nbits) * sizeof(unsigned long); - unsigned long *new_ = (unsigned long *)g_realloc(old, new_len); - bitmap_clear(new_, old_nbits, new_nbits - old_nbits); - return new_; + long new_nelem = BITS_TO_LONGS(new_nbits); + unsigned long *ptr = g_renew(unsigned long, old, new_nelem); + bitmap_clear(ptr, old_nbits, new_nbits - old_nbits); + return ptr; } void bitmap_to_le(unsigned long *dst, const unsigned long *src, diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index 03213ce952..c7b838a628 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -18,16 +18,47 @@ #define BITS_PER_BYTE CHAR_BIT #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define BITS_TO_U32S(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(uint32_t)) #define BIT(nr) (1UL << (nr)) #define BIT_ULL(nr) (1ULL << (nr)) -#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) -#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) -#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #define MAKE_64BIT_MASK(shift, length) \ (((~0ULL) >> (64 - (length))) << (shift)) +/** + * DOC: Functions operating on arrays of bits + * + * We provide a set of functions which work on arbitrary-length arrays of + * bits. These come in several flavours which vary in what the type of the + * underlying storage for the bits is: + * + * - Bits stored in an array of 'unsigned long': set_bit(), clear_bit(), etc + * - Bits stored in an array of 'uint32_t': set_bit32(), clear_bit32(), etc + * + * Because the 'unsigned long' type has a size which varies between + * host systems, the versions using 'uint32_t' are often preferable. + * This is particularly the case in a device model where there may + * be some guest-visible register view of the bit array. + * + * We do not currently implement uint32_t versions of find_last_bit(), + * find_next_bit(), find_next_zero_bit(), find_first_bit() or + * find_first_zero_bit(), because we haven't yet needed them. If you + * need them you should implement them similarly to the 'unsigned long' + * versions. + * + * You can declare a bitmap to be used with these functions via the + * DECLARE_BITMAP and DECLARE_BITMAP32 macros in bitmap.h. + */ + +/** + * DOC: 'unsigned long' bit array APIs + */ + +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) + /** * set_bit - Set a bit in memory * @nr: the bit to set @@ -67,6 +98,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 @@ -211,6 +255,141 @@ static inline unsigned long find_first_zero_bit(const unsigned long *addr, return find_next_zero_bit(addr, size, 0); } +/** + * DOC: 'uint32_t' bit array APIs + */ + +#define BIT32_MASK(nr) (1UL << ((nr) % 32)) +#define BIT32_WORD(nr) ((nr) / 32) + +/** + * set_bit32 - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + */ +static inline void set_bit32(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + + *p |= mask; +} + +/** + * set_bit32_atomic - Set a bit in memory atomically + * @nr: the bit to set + * @addr: the address to start counting from + */ +static inline void set_bit32_atomic(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + + qatomic_or(p, mask); +} + +/** + * clear_bit32 - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + */ +static inline void clear_bit32(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + + *p &= ~mask; +} + +/** + * clear_bit32_atomic - Clears a bit in memory atomically + * @nr: Bit to clear + * @addr: Address to start counting from + */ +static inline void clear_bit32_atomic(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + + return qatomic_and(p, ~mask); +} + +/** + * change_bit32 - Toggle a bit in memory + * @nr: Bit to change + * @addr: Address to start counting from + */ +static inline void change_bit32(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + + *p ^= mask; +} + +/** + * test_and_set_bit32 - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + */ +static inline int test_and_set_bit32(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + uint32_t old = *p; + + *p = old | mask; + return (old & mask) != 0; +} + +/** + * test_and_clear_bit32 - Clear a bit and return its old value + * @nr: Bit to clear + * @addr: Address to count from + */ +static inline int test_and_clear_bit32(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + uint32_t old = *p; + + *p = old & ~mask; + return (old & mask) != 0; +} + +/** + * test_and_change_bit32 - Change a bit and return its old value + * @nr: Bit to change + * @addr: Address to count from + */ +static inline int test_and_change_bit32(long nr, uint32_t *addr) +{ + uint32_t mask = BIT32_MASK(nr); + uint32_t *p = addr + BIT32_WORD(nr); + uint32_t old = *p; + + *p = old ^ mask; + return (old & mask) != 0; +} + +/** + * test_bit32 - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + */ +static inline int test_bit32(long nr, const uint32_t *addr) +{ + return 1U & (addr[BIT32_WORD(nr)] >> (nr & 31)); +} + +/** + * DOC: Miscellaneous bit operations on single values + * + * These functions are a collection of useful operations + * (rotations, bit extract, bit deposit, etc) on single + * integer values. + */ + /** * rol8 - rotate an 8-bit value left * @word: value to rotate @@ -218,7 +397,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 +407,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 +417,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 +427,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 +437,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 +447,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 +457,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 +467,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..b915835bea 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -1,97 +1,56 @@ #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 be_bswap24(v) (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 be_bswap24(v) bswap24(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 /** @@ -181,12 +140,23 @@ CPU_CONVERT(le, 16, uint16_t) CPU_CONVERT(le, 32, uint32_t) CPU_CONVERT(le, 64, uint64_t) +#undef CPU_CONVERT + /* - * 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 +166,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 +194,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 +267,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 +321,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)); @@ -379,6 +361,11 @@ static inline void stw_be_p(void *ptr, uint16_t v) stw_he_p(ptr, be_bswap(v, 16)); } +static inline void st24_be_p(void *ptr, uint32_t v) +{ + st24_he_p(ptr, be_bswap24(v)); +} + static inline void stl_be_p(void *ptr, uint32_t v) { stl_he_p(ptr, be_bswap(v, 32)); @@ -448,8 +435,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/co-shared-resource.h b/include/qemu/co-shared-resource.h index 78ca5850f8..41be1a8131 100644 --- a/include/qemu/co-shared-resource.h +++ b/include/qemu/co-shared-resource.h @@ -44,13 +44,6 @@ SharedResource *shres_create(uint64_t total); */ void shres_destroy(SharedResource *s); -/* - * Try to allocate an amount of @n. Return true on success, and false - * if there is too little left of the collective resource to fulfill - * the request. - */ -bool co_try_get_from_shres(SharedResource *s, uint64_t n); - /* * Allocate an amount of @n, and, if necessary, yield until * that becomes possible. diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 7375ea09a6..c611ce4667 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -28,10 +28,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_IDENTIFIER(stem) glue(stem, __COUNTER__) + #ifndef likely #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) @@ -196,4 +199,77 @@ #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) + +#ifndef __cplusplus +/* + * Useful in macros that need to declare temporary variables. For example, + * the variable that receives the old value of an atomically-accessed + * variable must be non-qualified, because atomic builtins return values + * through a pointer-type argument as in __atomic_load(&var, &old, MODEL). + * + * This macro has to handle types smaller than int manually, because of + * implicit promotion. int and larger types, as well as pointers, can be + * converted to a non-qualified type just by applying a binary operator. + */ +#define typeof_strip_qual(expr) \ + typeof( \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), bool) || \ + __builtin_types_compatible_p(typeof(expr), const bool) || \ + __builtin_types_compatible_p(typeof(expr), volatile bool) || \ + __builtin_types_compatible_p(typeof(expr), const volatile bool), \ + (bool)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), signed char) || \ + __builtin_types_compatible_p(typeof(expr), const signed char) || \ + __builtin_types_compatible_p(typeof(expr), volatile signed char) || \ + __builtin_types_compatible_p(typeof(expr), const volatile signed char), \ + (signed char)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), unsigned char) || \ + __builtin_types_compatible_p(typeof(expr), const unsigned char) || \ + __builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \ + __builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \ + (unsigned char)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), signed short) || \ + __builtin_types_compatible_p(typeof(expr), const signed short) || \ + __builtin_types_compatible_p(typeof(expr), volatile signed short) || \ + __builtin_types_compatible_p(typeof(expr), const volatile signed short), \ + (signed short)1, \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(expr), unsigned short) || \ + __builtin_types_compatible_p(typeof(expr), const unsigned short) || \ + __builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \ + __builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \ + (unsigned short)1, \ + (expr)+0)))))) +#endif + #endif /* COMPILER_H */ diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h index 321e7c7c03..51b310fa3b 100644 --- a/include/qemu/config-file.h +++ b/include/qemu/config-file.h @@ -8,6 +8,9 @@ QemuOptsList *qemu_find_opts(const char *group); QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); QemuOpts *qemu_find_opts_singleton(const char *group); +extern QemuOptsList *vm_config_groups[]; +extern QemuOptsList *drive_config_groups[]; + void qemu_add_opts(QemuOptsList *list); void qemu_add_drive_opts(QemuOptsList *list); int qemu_global_option(const char *str); @@ -22,7 +25,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 89650a2d7f..ff3084538b 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 *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); /** * 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. */ @@ -183,6 +84,8 @@ static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex) mutex->holder == qemu_coroutine_self()); } +#include "qemu/lockable.h" + /** * CoQueues are a mechanism to queue coroutines in order to continue executing * them later. They are similar to condition variables, but they need help @@ -380,8 +283,6 @@ void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size); */ void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size); -#include "qemu/lockable.h" - /** * Sends a (part of) iovec down a socket, yielding when the socket is full, or * Receives data into a (part of) iovec from a socket, 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..ce28e29720 100644 --- a/include/qemu/crc-ccitt.h +++ b/include/qemu/crc-ccitt.h @@ -8,7 +8,7 @@ * * From Linux kernel v5.10 include/linux/crc-ccitt.h * - * SPDX-License-Identifier: GPL-2.0 + * SPDX-License-Identifier: GPL-2.0-only */ #ifndef 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 92c436d8c7..34a9b9b220 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -163,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); @@ -188,9 +187,39 @@ char *freq_to_str(uint64_t freq_hz); /* used to print char* safely */ #define STR_OR_NULL(str) ((str) ? (str) : "null") -bool buffer_is_zero(const void *buf, size_t len); +/* + * Check if a buffer is all zeroes. + */ + +bool buffer_is_zero_ool(const void *vbuf, size_t len); +bool buffer_is_zero_ge256(const void *vbuf, size_t len); bool test_buffer_is_zero_next_accel(void); +static inline bool buffer_is_zero_sample3(const char *buf, size_t len) +{ + /* + * For any reasonably sized buffer, these three samples come from + * three different cachelines. In qemu-img usage, we find that + * each byte eliminates more than half of all buffer testing. + * It is therefore critical to performance that the byte tests + * short-circuit, so that we do not pull in additional cache lines. + * Do not "optimize" this to !(a | b | c). + */ + return !buf[0] && !buf[len - 1] && !buf[len / 2]; +} + +#ifdef __OPTIMIZE__ +static inline bool buffer_is_zero(const void *buf, size_t len) +{ + return (__builtin_constant_p(len) && len >= 256 + ? buffer_is_zero_sample3(buf, len) && + buffer_is_zero_ge256(buf, len) + : buffer_is_zero_ool(buf, len)); +} +#else +#define buffer_is_zero buffer_is_zero_ool +#endif + /* * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) * Input is limited to 14-bit numbers @@ -212,13 +241,10 @@ int uleb128_decode_small(const uint8_t *in, uint32_t *n); int qemu_pstrcmp0(const char **str1, const char **str2); /* Find program directory, and save it for later usage with - * qemu_get_exec_dir(). + * get_relocated_path(). * Try OS specific API first, if not working, parse from argv0. */ void qemu_init_exec_dir(const char *argv0); -/* Get the saved exec dir. */ -const char *qemu_get_exec_dir(void); - /** * get_relocated_path: * @dir: the directory (typically a `CONFIG_*DIR` variable) to be relocated. @@ -253,13 +279,21 @@ static inline const char *yes_no(bool b) */ int parse_debug_env(const char *name, int max, int initial); -/* - * Hexdump a line of a byte buffer into a hexadecimal/ASCII buffer +/** + * qemu_hexdump_line: + * @str: GString into which to append + * @buf: buffer to dump + * @len: number of bytes to dump + * @unit_len: add a space between every @unit_len bytes + * @block_len: add an extra space between every @block_len bytes + * + * Append @len bytes of @buf as hexadecimal into @str. + * Add spaces between every @unit_len and @block_len bytes. + * If @str is NULL, allocate a new string and return it; + * otherwise return @str. */ -#define QEMU_HEXDUMP_LINE_BYTES 16 /* Number of bytes to dump */ -#define QEMU_HEXDUMP_LINE_LEN 75 /* Number of characters in line */ -void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, - unsigned int len, bool ascii); +GString *qemu_hexdump_line(GString *str, const void *buf, size_t len, + size_t unit_len, size_t block_len); /* * Hexdump a buffer to a file. An optional string prefix is added to every line 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..b2883f6659 100644 --- a/include/qemu/envlist.h +++ b/include/qemu/envlist.h @@ -1,22 +1,12 @@ #ifndef ENVLIST_H #define ENVLIST_H -#ifdef __cplusplus -extern "C" { -#endif - typedef struct envlist envlist_t; envlist_t *envlist_create(void); void envlist_free(envlist_t *); int envlist_setenv(envlist_t *, const char *); int envlist_unsetenv(envlist_t *, const char *); -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..4f768d4ee3 100644 --- a/include/qemu/fifo8.h +++ b/include/qemu/fifo8.h @@ -15,10 +15,9 @@ typedef struct { * @fifo: struct Fifo8 to initialise with new FIFO * @capacity: capacity of the newly created FIFO * - * Create a FIFO of the specified size. Clients should call fifo8_destroy() + * Create a FIFO of the specified capacity. Clients should call fifo8_destroy() * when finished using the fifo. The FIFO is initially empty. */ - void fifo8_create(Fifo8 *fifo, uint32_t capacity); /** @@ -26,9 +25,8 @@ void fifo8_create(Fifo8 *fifo, uint32_t capacity); * @fifo: FIFO to cleanup * * Cleanup a FIFO created with fifo8_create(). Frees memory created for FIFO - *storage. The FIFO is no longer usable after this has been called. + * storage. The FIFO is no longer usable after this has been called. */ - void fifo8_destroy(Fifo8 *fifo); /** @@ -39,20 +37,18 @@ void fifo8_destroy(Fifo8 *fifo); * Push a data byte to the FIFO. Behaviour is undefined if the FIFO is full. * Clients are responsible for checking for fullness using fifo8_is_full(). */ - 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 * fifo8_num_free(). */ - void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); /** @@ -64,34 +60,110 @@ void fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num); * * Returns: The popped data byte. */ - uint8_t fifo8_pop(Fifo8 *fifo); +/** + * fifo8_peek: + * @fifo: fifo to peek from + * + * Peek the data byte at the current head of the FIFO. Clients are responsible + * for checking for emptyness using fifo8_is_empty(). + * + * Returns: The peeked data byte. + */ +uint8_t fifo8_peek(Fifo8 *fifo); + /** * fifo8_pop_buf: * @fifo: FIFO to pop from - * @max: maximum number of bytes to pop - * @num: actual number of returned bytes + * @dest: the buffer to write the data into (can be NULL) + * @destlen: size of @dest and maximum number of bytes to pop * - * Pop a number of elements from the FIFO up to a maximum of max. The buffer + * Pop a number of elements from the FIFO up to a maximum of @destlen. + * The popped data is copied into the @dest buffer. + * Care is taken when the data wraps around in the ring buffer. + * + * Returns: number of bytes popped. + */ +uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen); + +/** + * fifo8_peek_buf: + * @fifo: FIFO to read from + * @dest: the buffer to write the data into (can be NULL) + * @destlen: size of @dest and maximum number of bytes to peek + * + * Peek a number of elements from the FIFO up to a maximum of @destlen. + * The peeked data is copied into the @dest buffer. + * Care is taken when the data wraps around in the ring buffer. + * + * Returns: number of bytes peeked. + */ +uint32_t fifo8_peek_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen); + +/** + * fifo8_pop_bufptr: + * @fifo: FIFO to pop from + * @max: maximum number of bytes to pop + * @numptr: pointer filled with number of bytes returned (can be NULL) + * + * New code should prefer to use fifo8_pop_buf() instead of fifo8_pop_bufptr(). + * + * 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 - * the FIFO backing store and data is invalidated once any of the fifo8_* APIs - * are called on the FIFO. + * the internal FIFO backing store and data (without checking for overflow!) + * and is invalidated once any of the fifo8_* APIs are called on the FIFO. * * 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 *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_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); + +/** + * fifo8_peek_bufptr: 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_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr); + +/** + * fifo8_drop: + * @fifo: FIFO to drop bytes + * @len: number of bytes to drop + * + * Drop (consume) bytes from a FIFO. + */ +void fifo8_drop(Fifo8 *fifo, uint32_t len); /** * fifo8_reset: @@ -99,7 +171,6 @@ const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num); * * Reset a FIFO. All data is discarded and the FIFO is emptied. */ - void fifo8_reset(Fifo8 *fifo); /** @@ -110,7 +181,6 @@ void fifo8_reset(Fifo8 *fifo); * * Returns: True if the fifo is empty, false otherwise. */ - bool fifo8_is_empty(Fifo8 *fifo); /** @@ -121,7 +191,6 @@ bool fifo8_is_empty(Fifo8 *fifo); * * Returns: True if the fifo is full, false otherwise. */ - bool fifo8_is_full(Fifo8 *fifo); /** @@ -132,7 +201,6 @@ bool fifo8_is_full(Fifo8 *fifo); * * Returns: Number of free bytes. */ - uint32_t fifo8_num_free(Fifo8 *fifo); /** @@ -143,7 +211,6 @@ uint32_t fifo8_num_free(Fifo8 *fifo); * * Returns: Number of used bytes. */ - uint32_t fifo8_num_used(Fifo8 *fifo); extern const VMStateDescription vmstate_fifo8; 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 af4e4ab746..8136e33674 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -330,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 b3434ec0bc..4d28fa22cf 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -30,7 +30,6 @@ #ifndef HOST_UTILS_H #define HOST_UTILS_H -#include "qemu/compiler.h" #include "qemu/bswap.h" #include "qemu/int128.h" @@ -57,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) { @@ -84,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; @@ -100,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 @@ -154,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 @@ -241,6 +313,15 @@ static inline int ctpop8(uint8_t val) return __builtin_popcount(val); } +/* + * parity8 - return the parity (1 = odd) of an 8-bit value. + * @val: The value to search + */ +static inline int parity8(uint8_t val) +{ + return __builtin_parity(val); +} + /** * ctpop16 - count the population of one bits in a 16-bit value. * @val: The value to search diff --git a/include/qemu/int128.h b/include/qemu/int128.h index d2b76ca6ac..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) { @@ -219,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 @@ -460,8 +467,7 @@ 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) { @@ -472,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..44f9db5cee 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -1,6 +1,7 @@ /* * Helpers for using (partial) iovecs. * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (C) 2010 Red Hat, Inc. * * Author(s): @@ -75,6 +76,32 @@ iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, size_t offset, int fillc, size_t bytes); +/* + * Send/recv data from/to iovec buffers directly, with the provided + * socket flags. + * + * `offset' bytes in the beginning of iovec buffer are skipped and + * next `bytes' bytes are used, which must be within data of iovec. + * + * r = iov_send_recv_with_flags(sockfd, sockflags, iov, iovcnt, + * offset, bytes, true); + * + * is logically equivalent to + * + * char *buf = malloc(bytes); + * iov_to_buf(iov, iovcnt, offset, buf, bytes); + * r = send(sockfd, buf, bytes, sockflags); + * free(buf); + * + * For iov_send_recv_with_flags() _whole_ area being sent or received + * should be within the iovec, not only beginning of it. + */ +ssize_t iov_send_recv_with_flags(int sockfd, int sockflags, + const struct iovec *iov, + unsigned iov_cnt, size_t offset, + size_t bytes, + bool do_send); + /* * Send/recv data from/to iovec buffers directly * @@ -222,13 +249,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 8528e5c98f..44a45931d5 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 @@ -111,31 +111,6 @@ const DMAMap *iova_tree_find(const IOVATree *tree, const DMAMap *map); */ const DMAMap *iova_tree_find_iova(const IOVATree *tree, const DMAMap *map); -/** - * iova_tree_find_address: - * - * @tree: the iova tree to search from - * @iova: the iova address to find - * - * Similar to iova_tree_find(), but it tries to find mapping with - * range iova=iova & size=0. - * - * Return: same as iova_tree_find(). - */ -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 - * - * Iterate over the iova tree. - * - * Return: 1 if found any overlap, 0 if not, <0 if error. - */ -void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator); - /** * iova_tree_alloc_map: * diff --git a/include/qemu/job.h b/include/qemu/job.h index e502787dd8..2b873f2576 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -67,8 +67,6 @@ typedef struct Job { /** * The completion function that will be called when the job completes. - * Called with AioContext lock held, since many callback implementations - * use bdrv_* functions that require to hold the lock. */ BlockCompletionFunc *cb; @@ -264,9 +262,6 @@ struct JobDriver { * * This callback will not be invoked if the job has already failed. * If it fails, abort and then clean will be called. - * - * Called with AioContext lock held, since many callbacs implementations - * use bdrv_* functions that require to hold the lock. */ int (*prepare)(Job *job); @@ -277,9 +272,6 @@ struct JobDriver { * * All jobs will complete with a call to either .commit() or .abort() but * never both. - * - * Called with AioContext lock held, since many callback implementations - * use bdrv_* functions that require to hold the lock. */ void (*commit)(Job *job); @@ -290,9 +282,6 @@ struct JobDriver { * * All jobs will complete with a call to either .commit() or .abort() but * never both. - * - * Called with AioContext lock held, since many callback implementations - * use bdrv_* functions that require to hold the lock. */ void (*abort)(Job *job); @@ -301,9 +290,6 @@ struct JobDriver { * .commit() or .abort(). Regardless of which callback is invoked after * completion, .clean() will always be called, even if the job does not * belong to a transaction group. - * - * Called with AioContext lock held, since many callbacs implementations - * use bdrv_* functions that require to hold the lock. */ void (*clean)(Job *job); @@ -318,17 +304,12 @@ struct JobDriver { * READY). * (If the callback is NULL, the job is assumed to terminate * without I/O.) - * - * Called with AioContext lock held, since many callback implementations - * use bdrv_* functions that require to hold the lock. */ bool (*cancel)(Job *job, bool force); /** * Called when the job is freed. - * Called with AioContext lock held, since many callback implementations - * use bdrv_* functions that require to hold the lock. */ void (*free)(Job *job); }; @@ -424,7 +405,6 @@ void job_ref_locked(Job *job); * 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. * - * Takes AioContext lock internally to invoke a job->driver callback. * Called with job lock held. */ void job_unref_locked(Job *job); @@ -503,7 +483,7 @@ void job_enter(Job *job); * * 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. diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h index 86db7cb04c..66713bd429 100644 --- a/include/qemu/lockable.h +++ b/include/qemu/lockable.h @@ -13,16 +13,16 @@ #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 *); -struct QemuLockable { +typedef struct QemuLockable { void *object; QemuLockUnlockFunc *lock; QemuLockUnlockFunc *unlock; -}; +} QemuLockable; static inline __attribute__((__always_inline__)) QemuLockable * qemu_make_lockable(void *x, QemuLockable *lockable) @@ -43,15 +43,30 @@ qemu_null_lockable(void *x) return NULL; } +#define QML_FUNC_(name) \ + static inline void qemu_lockable_ ## name ## _lock(void *x) \ + { \ + qemu_ ## name ## _lock(x); \ + } \ + static inline void qemu_lockable_ ## name ## _unlock(void *x) \ + { \ + qemu_ ## name ## _unlock(x); \ + } + +QML_FUNC_(mutex) +QML_FUNC_(rec_mutex) +QML_FUNC_(co_mutex) +QML_FUNC_(spin) + /* * 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 QML_OBJ_(x, name) (&(QemuLockable) { \ - .object = (x), \ - .lock = (QemuLockUnlockFunc *) qemu_ ## name ## _lock, \ - .unlock = (QemuLockUnlockFunc *) qemu_ ## name ## _unlock \ +#define QML_OBJ_(x, name) (&(QemuLockable) { \ + .object = (x), \ + .lock = qemu_lockable_ ## name ## _lock, \ + .unlock = qemu_lockable_ ## name ## _unlock \ }) /** diff --git a/include/qemu/lockcnt.h b/include/qemu/lockcnt.h new file mode 100644 index 0000000000..f4b62a3f70 --- /dev/null +++ b/include/qemu/lockcnt.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QemuLockCnt implementation + * + * Copyright Red Hat, Inc. 2017 + * + * Author: + * Paolo Bonzini + * + */ + +#ifndef QEMU_LOCKCNT_H +#define QEMU_LOCKCNT_H + +#include "qemu/thread.h" + +typedef struct QemuLockCnt QemuLockCnt; + +struct QemuLockCnt { +#ifndef CONFIG_LINUX + QemuMutex mutex; +#endif + unsigned count; +}; + +/** + * qemu_lockcnt_init: initialize a QemuLockcnt + * @lockcnt: the lockcnt to initialize + * + * Initialize lockcnt's counter to zero and prepare its mutex + * for usage. + */ +void qemu_lockcnt_init(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_destroy: destroy a QemuLockcnt + * @lockcnt: the lockcnt to destruct + * + * Destroy lockcnt's mutex. + */ +void qemu_lockcnt_destroy(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_inc: increment a QemuLockCnt's counter + * @lockcnt: the lockcnt to operate on + * + * If the lockcnt's count is zero, wait for critical sections + * to finish and increment lockcnt's count to 1. If the count + * is not zero, just increment it. + * + * Because this function can wait on the mutex, it must not be + * called while the lockcnt's mutex is held by the current thread. + * For the same reason, qemu_lockcnt_inc can also contribute to + * AB-BA deadlocks. This is a sample deadlock scenario:: + * + * thread 1 thread 2 + * ------------------------------------------------------- + * qemu_lockcnt_lock(&lc1); + * qemu_lockcnt_lock(&lc2); + * qemu_lockcnt_inc(&lc2); + * qemu_lockcnt_inc(&lc1); + */ +void qemu_lockcnt_inc(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_dec: decrement a QemuLockCnt's counter + * @lockcnt: the lockcnt to operate on + */ +void qemu_lockcnt_dec(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_dec_and_lock: decrement a QemuLockCnt's counter and + * possibly lock it. + * @lockcnt: the lockcnt to operate on + * + * Decrement lockcnt's count. If the new count is zero, lock + * the mutex and return true. Otherwise, return false. + */ +bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_dec_if_lock: possibly decrement a QemuLockCnt's counter and + * lock it. + * @lockcnt: the lockcnt to operate on + * + * If the count is 1, decrement the count to zero, lock + * the mutex and return true. Otherwise, return false. + */ +bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_lock: lock a QemuLockCnt's mutex. + * @lockcnt: the lockcnt to operate on + * + * Remember that concurrent visits are not blocked unless the count is + * also zero. You can use qemu_lockcnt_count to check for this inside a + * critical section. + */ +void qemu_lockcnt_lock(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_unlock: release a QemuLockCnt's mutex. + * @lockcnt: the lockcnt to operate on. + */ +void qemu_lockcnt_unlock(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_inc_and_unlock: combined unlock/increment on a QemuLockCnt. + * @lockcnt: the lockcnt to operate on. + * + * This is the same as + * + * qemu_lockcnt_unlock(lockcnt); + * qemu_lockcnt_inc(lockcnt); + * + * but more efficient. + */ +void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt); + +/** + * qemu_lockcnt_count: query a LockCnt's count. + * @lockcnt: the lockcnt to query. + * + * Note that the count can change at any time. Still, while the + * lockcnt is locked, one can usefully check whether the count + * is non-zero. + */ +unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt); + +#endif diff --git a/include/qemu/log.h b/include/qemu/log.h index c5643d8dd5..e10e24cd4f 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -35,6 +35,8 @@ 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) +#define LOG_TB_OP_PLUGIN (1 << 22) /* Lock/unlock output. */ diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 19f40a7222..d6877f73a1 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -248,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 @@ -312,57 +312,85 @@ bool qemu_in_main_thread(void); } 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 2825e231a7..8344daaa03 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -1,8 +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); +QemuFsType qemu_fd_getfs(int fd); /** * qemu_ram_mmap: mmap anonymous memory, the specified file or device. diff --git a/include/qemu/mstring.h b/include/qemu/mstring.h index 567fd4cdf3..c3e32c181c 100644 --- a/include/qemu/mstring.h +++ b/include/qemu/mstring.h @@ -9,9 +9,9 @@ typedef struct { gchar *string; } MString; -void mstring_append_fmt(MString *mstring, const char *fmt, ...); -MString *mstring_from_fmt(const char *fmt, ...); -void mstring_append_va(MString *mstring, const char *fmt, va_list va); +void mstring_append_fmt(MString *mstring, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); +MString *mstring_from_fmt(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void mstring_append_va(MString *mstring, const char *fmt, va_list va) __attribute__ ((format (printf, 2, 0))); static inline void mstring_ref(MString *mstr) @@ -37,18 +37,6 @@ void mstring_append(MString *mstr, const char *str) mstr->string = n; } -static inline -void mstring_append_chr(MString *mstr, char chr) -{ - mstring_append_fmt(mstr, "%c", chr); -} - -static inline -void mstring_append_int(MString *mstr, int val) -{ - mstring_append_fmt(mstr, "%" PRId64, val); -} - static inline MString *mstring_new(void) { 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/option.h b/include/qemu/option.h index b349828782..01e673ae03 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -54,6 +54,8 @@ enum QemuOptType { QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */ }; +typedef struct QemuOpt QemuOpt; + typedef struct QemuOptDesc { const char *name; enum QemuOptType type; diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index fd712ee883..6df97bae74 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -27,8 +27,12 @@ #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 +#ifdef COMPILING_PER_TARGET #include CONFIG_TARGET #else #include "exec/poison.h" @@ -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. */ -G_NORETURN extern +G_NORETURN void QEMU_ERROR("code path is reachable") qemu_build_not_reached_always(void); #if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) && !defined(XEMU_DEBUG_BUILD) @@ -220,6 +297,10 @@ void QEMU_ERROR("code path is reachable") #error building with G_DISABLE_ASSERT is not supported #endif +#ifndef OFF_MAX +#define OFF_MAX (sizeof (off_t) == 8 ? INT64_MAX : INT32_MAX) +#endif + #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif @@ -229,9 +310,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 @@ -251,7 +329,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 @@ -307,19 +391,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_IDENTIFIER(_a), MAKE_IDENTIFIER(_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_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) #ifdef __COVERITY__ # define MIN_CONST(a, b) ((a) < (b) ? (a) : (b)) @@ -340,14 +433,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_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) /* * Round number down to multiple. Safe when m is not a power of 2 (see @@ -415,15 +512,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__) @@ -435,7 +534,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) @@ -459,6 +558,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 @@ -527,6 +634,8 @@ static inline int qemu_access(const char *pathname, int mode) } #endif +bool qemu_has_direct_io(void); + #if defined(__HAIKU__) && defined(__i386__) #define FMT_pid "%ld" #elif defined(WIN64) @@ -602,15 +711,33 @@ typedef struct ThreadContext ThreadContext; * @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. */ -void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, - ThreadContext *tc, Error **errp); +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: @@ -622,20 +749,6 @@ void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, */ 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. */ @@ -666,6 +779,17 @@ static inline void qemu_reset_optind(void) int qemu_fdatasync(int fd); +/** + * qemu_close_all_open_fd: + * + * Close all open file descriptors except the ones supplied in the @skip array + * + * @skip: ordered array of distinct file descriptors that should not be closed + * if any, or NULL. + * @nskip: number of entries in the @skip array or 0 if @skip is NULL. + */ +void qemu_close_all_open_fd(const int *skip, unsigned int nskip); + /** * Sync changes made to the memory mapped file back to the backing * storage. For POSIX compliant systems this will fallback @@ -695,8 +819,7 @@ size_t qemu_get_host_physmem(void); * Toggle write/execute on the pages marked MAP_JIT * for the current thread. */ -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 +#ifdef __APPLE__ static inline void qemu_thread_jit_execute(void) { pthread_jit_write_protect_np(true); 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 a772e14193..9726a9ebf3 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -12,23 +12,10 @@ #include "qemu/error-report.h" #include "qemu/queue.h" #include "qemu/option.h" +#include "qemu/plugin-event.h" +#include "qemu/bitmap.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 +51,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 { @@ -80,15 +67,33 @@ union qemu_plugin_cb_sig { }; enum plugin_dyn_cb_type { - PLUGIN_CB_INSN, - PLUGIN_CB_MEM, - PLUGIN_N_CB_TYPES, + PLUGIN_CB_REGULAR, + PLUGIN_CB_COND, + PLUGIN_CB_MEM_REGULAR, + PLUGIN_CB_INLINE_ADD_U64, + PLUGIN_CB_INLINE_STORE_U64, }; -enum plugin_dyn_cb_subtype { - PLUGIN_CB_REGULAR, - PLUGIN_CB_INLINE, - PLUGIN_N_CB_SUBTYPES, +struct qemu_plugin_regular_cb { + union qemu_plugin_cb_sig f; + TCGHelperInfo *info; + void *userp; + enum qemu_plugin_mem_rw rw; +}; + +struct qemu_plugin_inline_cb { + qemu_plugin_u64 entry; + uint64_t imm; + enum qemu_plugin_mem_rw rw; +}; + +struct qemu_plugin_conditional_cb { + union qemu_plugin_cb_sig f; + TCGHelperInfo *info; + void *userp; + qemu_plugin_u64 entry; + enum qemu_plugin_cond cond; + uint64_t imm; }; /* @@ -97,100 +102,58 @@ enum plugin_dyn_cb_subtype { * instance of a callback to be called upon the execution of a particular TB. */ struct qemu_plugin_dyn_cb { - union qemu_plugin_cb_sig f; - void *userp; - enum plugin_dyn_cb_subtype type; - /* @rw applies to mem callbacks only (both regular and inline) */ - enum qemu_plugin_mem_rw rw; - /* fields specific to each dyn_cb type go here */ + enum plugin_dyn_cb_type type; union { - struct { - enum qemu_plugin_op op; - uint64_t imm; - } inline_insn; + struct qemu_plugin_regular_cb regular; + struct qemu_plugin_conditional_cb cond; + struct qemu_plugin_inline_cb inline_insn; }; }; /* Internal context for instrumenting an instruction */ struct qemu_plugin_insn { - GByteArray *data; uint64_t vaddr; - void *haddr; - GArray *cbs[PLUGIN_N_CB_TYPES][PLUGIN_N_CB_SUBTYPES]; + GArray *insn_cbs; + GArray *mem_cbs; + uint8_t len; bool calls_helpers; + + /* if set, the instruction calls helpers that might access guest memory */ bool mem_helper; - bool mem_only; }; -/* - * qemu_plugin_insn allocate and cleanup functions. We don't expect to - * cleanup many of these structures. They are reused for each fresh - * translation. - */ - -static inline void qemu_plugin_insn_cleanup_fn(gpointer data) -{ - struct qemu_plugin_insn *insn = (struct qemu_plugin_insn *) data; - g_byte_array_free(insn->data, true); -} - -static inline struct qemu_plugin_insn *qemu_plugin_insn_alloc(void) -{ - int i, j; - struct qemu_plugin_insn *insn = g_new0(struct qemu_plugin_insn, 1); - insn->data = g_byte_array_sized_new(4); - - for (i = 0; i < PLUGIN_N_CB_TYPES; i++) { - for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) { - insn->cbs[i][j] = g_array_new(false, false, - sizeof(struct qemu_plugin_dyn_cb)); - } - } - return insn; -} +/* A scoreboard is an array of values, indexed by vcpu_index */ +struct qemu_plugin_scoreboard { + GArray *data; + QLIST_ENTRY(qemu_plugin_scoreboard) entry; +}; /* Internal context for this TranslationBlock */ struct qemu_plugin_tb { GPtrArray *insns; size_t n; - uint64_t vaddr; - uint64_t vaddr2; - void *haddr1; - void *haddr2; - bool mem_only; - GArray *cbs[PLUGIN_N_CB_SUBTYPES]; + + /* if set, the TB calls helpers that might access guest memory */ + bool mem_helper; + + GArray *cbs; }; /** - * qemu_plugin_tb_insn_get(): get next plugin record for translation. - * @tb: the internal tb context - * @pc: address of instruction + * struct CPUPluginState - per-CPU state for plugins + * @event_mask: plugin event bitmap. Modified only via async work. */ -static inline -struct qemu_plugin_insn *qemu_plugin_tb_insn_get(struct qemu_plugin_tb *tb, - uint64_t pc) -{ - struct qemu_plugin_insn *insn; - int i, j; +struct CPUPluginState { + DECLARE_BITMAP(event_mask, QEMU_PLUGIN_EV_MAX); +}; - if (unlikely(tb->n == tb->insns->len)) { - struct qemu_plugin_insn *new_insn = qemu_plugin_insn_alloc(); - g_ptr_array_add(tb->insns, new_insn); - } - insn = g_ptr_array_index(tb->insns, tb->n++); - g_byte_array_set_size(insn->data, 0); - insn->calls_helpers = false; - insn->mem_helper = false; - insn->vaddr = pc; - - for (i = 0; i < PLUGIN_N_CB_TYPES; i++) { - for (j = 0; j < PLUGIN_N_CB_SUBTYPES; j++) { - g_array_set_size(insn->cbs[i][j], 0); - } - } - - return insn; -} +/** + * qemu_plugin_create_vcpu_state: allocate plugin state + * + * The returned data must be released with g_free() + * when no longer required. + */ +CPUPluginState *qemu_plugin_create_vcpu_state(void); void qemu_plugin_vcpu_init_hook(CPUState *cpu); void qemu_plugin_vcpu_exit_hook(CPUState *cpu); @@ -204,6 +167,8 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret); void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, + uint64_t value_low, + uint64_t value_high, MemOpIdx oi, enum qemu_plugin_mem_rw rw); void qemu_plugin_flush_cb(void); @@ -212,7 +177,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->neg.plugin_mem_cbs = NULL; +} /** * qemu_plugin_user_exit(): clean-up callbacks before calling exit callbacks @@ -246,7 +214,7 @@ void qemu_plugin_user_postfork(bool is_child); 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"); @@ -285,6 +253,8 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) { } static inline void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, + uint64_t value_low, + uint64_t value_high, MemOpIdx oi, enum qemu_plugin_mem_rw rw) { } diff --git a/include/qemu/pmem.h b/include/qemu/pmem.h index d2d7ad085c..e12a67ba2c 100644 --- a/include/qemu/pmem.h +++ b/include/qemu/pmem.h @@ -22,7 +22,6 @@ pmem_memcpy_persist(void *pmemdest, const void *src, size_t len) /* If 'pmem' option is 'on', we should always have libpmem support, or qemu will report a error and exit, never come here. */ g_assert_not_reached(); - return NULL; } static inline void 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-options.h b/include/qemu/qemu-options.h deleted file mode 100644 index 4a62c83c45..0000000000 --- a/include/qemu/qemu-options.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * qemu-options.h - * - * Defines needed for command line argument processing. - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2010 Jes Sorensen - * - * 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_OPTIONS_H -#define QEMU_OPTIONS_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" -}; - -#endif diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index d0e9d03adf..0fba36ae02 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,25 @@ 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. + * + * version 3: + * - modified arguments and return value of qemu_plugin_insn_data to copy + * the data into a user-provided buffer instead of returning a pointer + * to the data. + * + * version 4: + * - added qemu_plugin_read_memory_vaddr */ extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; -#define QEMU_PLUGIN_VERSION 1 +#define QEMU_PLUGIN_VERSION 4 /** * struct qemu_info_t - system information for plugins @@ -147,6 +165,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 +179,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 +191,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 +204,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 +215,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 +226,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 +234,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 +255,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, @@ -233,6 +270,52 @@ enum qemu_plugin_mem_rw { QEMU_PLUGIN_MEM_RW, }; +enum qemu_plugin_mem_value_type { + QEMU_PLUGIN_MEM_VALUE_U8, + QEMU_PLUGIN_MEM_VALUE_U16, + QEMU_PLUGIN_MEM_VALUE_U32, + QEMU_PLUGIN_MEM_VALUE_U64, + QEMU_PLUGIN_MEM_VALUE_U128, +}; + +/* typedef qemu_plugin_mem_value - value accessed during a load/store */ +typedef struct { + enum qemu_plugin_mem_value_type type; + union { + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + struct { + uint64_t low; + uint64_t high; + } u128; + } data; +} qemu_plugin_mem_value; + +/** + * enum qemu_plugin_cond - condition to enable callback + * + * @QEMU_PLUGIN_COND_NEVER: false + * @QEMU_PLUGIN_COND_ALWAYS: true + * @QEMU_PLUGIN_COND_EQ: is equal? + * @QEMU_PLUGIN_COND_NE: is not equal? + * @QEMU_PLUGIN_COND_LT: is less than? + * @QEMU_PLUGIN_COND_LE: is less than or equal? + * @QEMU_PLUGIN_COND_GT: is greater than? + * @QEMU_PLUGIN_COND_GE: is greater than or equal? + */ +enum qemu_plugin_cond { + QEMU_PLUGIN_COND_NEVER, + QEMU_PLUGIN_COND_ALWAYS, + QEMU_PLUGIN_COND_EQ, + QEMU_PLUGIN_COND_NE, + QEMU_PLUGIN_COND_LT, + QEMU_PLUGIN_COND_LE, + QEMU_PLUGIN_COND_GT, + QEMU_PLUGIN_COND_GE, +}; + /** * typedef qemu_plugin_vcpu_tb_trans_cb_t - translation callback * @id: unique plugin id @@ -253,6 +336,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,40 +349,65 @@ 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, void *userdata); +/** + * qemu_plugin_register_vcpu_tb_exec_cond_cb() - register conditional callback + * @tb: the opaque qemu_plugin_tb handle for the translation + * @cb: callback function + * @cond: condition to enable callback + * @entry: first operand for condition + * @imm: second operand for condition + * @flags: does the plugin read or write the CPU's registers? + * @userdata: any plugin data to pass to the @cb? + * + * The @cb function is called when a translated unit executes if + * entry @cond imm is true. + * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and + * this function is equivalent to qemu_plugin_register_vcpu_tb_exec_cb. + * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and + * callback is never installed. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_tb_exec_cond_cb(struct qemu_plugin_tb *tb, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *userdata); + /** * enum qemu_plugin_op - describes an inline op * * @QEMU_PLUGIN_INLINE_ADD_U64: add an immediate value uint64_t - * - * Note: currently only a single inline op is supported. + * @QEMU_PLUGIN_INLINE_STORE_U64: store an immediate value uint64_t */ enum qemu_plugin_op { QEMU_PLUGIN_INLINE_ADD_U64, + QEMU_PLUGIN_INLINE_STORE_U64, }; /** - * 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 +418,54 @@ 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_cond_cb() - conditional insn execution cb + * @insn: the opaque qemu_plugin_insn handle for an instruction + * @cb: callback function + * @flags: does the plugin read or write the CPU's registers? + * @cond: condition to enable callback + * @entry: first operand for condition + * @imm: second operand for condition + * @userdata: any plugin data to pass to the @cb? + * + * The @cb function is called when an instruction executes if + * entry @cond imm is true. + * If condition is QEMU_PLUGIN_COND_ALWAYS, condition is never interpreted and + * this function is equivalent to qemu_plugin_register_vcpu_insn_exec_cb. + * If condition QEMU_PLUGIN_COND_NEVER, condition is never interpreted and + * callback is never installed. + */ +QEMU_PLUGIN_API +void qemu_plugin_register_vcpu_insn_exec_cond_cb( + struct qemu_plugin_insn *insn, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *userdata); + +/** + * 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 +473,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 +482,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,20 +496,21 @@ 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); /** - * qemu_plugin_insn_data() - return ptr to instruction data + * qemu_plugin_insn_data() - copy instruction data * @insn: opaque instruction handle from qemu_plugin_tb_get_insn() + * @dest: destination into which data is copied + * @len: length of dest * - * Note: data is only valid for duration of callback. See - * qemu_plugin_insn_size() to calculate size of stream. - * - * Returns: pointer to a stream of bytes containing the value of this - * instructions opcode. + * Returns the number of bytes copied, minimum of @len and insn size. */ -const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn); +QEMU_PLUGIN_API +size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, + void *dest, size_t len); /** * qemu_plugin_insn_size() - return size of instruction @@ -376,6 +518,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 +527,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 +536,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 +555,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 +563,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 +571,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,8 +579,18 @@ 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); +/** + * qemu_plugin_mem_get_mem_value() - return last value loaded/stored + * @info: opaque memory transaction handle + * + * Returns: memory value + */ +QEMU_PLUGIN_API +qemu_plugin_mem_value qemu_plugin_mem_get_value(qemu_plugin_meminfo_t info); + /** * qemu_plugin_get_hwaddr() - return handle for memory operation * @info: opaque memory info structure @@ -446,6 +604,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 +621,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 +633,100 @@ 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); +/** + * qemu_plugin_request_time_control() - request the ability to control time + * + * This grants the plugin the ability to control system time. Only one + * plugin can control time so if multiple plugins request the ability + * all but the first will fail. + * + * Returns an opaque handle or NULL if fails + */ +QEMU_PLUGIN_API +const void *qemu_plugin_request_time_control(void); +/** + * qemu_plugin_update_ns() - update system emulation time + * @handle: opaque handle returned by qemu_plugin_request_time_control() + * @time: time in nanoseconds + * + * This allows an appropriately authorised plugin (i.e. holding the + * time control handle) to move system time forward to @time. For + * user-mode emulation the time is not changed by this as all reported + * time comes from the host kernel. + * + * Start time is 0. + */ +QEMU_PLUGIN_API +void qemu_plugin_update_ns(const void *handle, int64_t time); typedef void (*qemu_plugin_vcpu_syscall_cb_t)(qemu_plugin_id_t id, unsigned int vcpu_index, @@ -505,6 +734,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 +742,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 +755,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 +765,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 +777,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 +798,19 @@ 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 */ +QEMU_PLUGIN_API +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 +824,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 +835,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 +844,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 +853,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 +862,144 @@ 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_memory_vaddr() - read from memory using a virtual address + * + * @addr: A virtual address to read from + * @data: A byte array to store data into + * @len: The number of bytes to read, starting from @addr + * + * @len bytes of data is read starting at @addr and stored into @data. If @data + * is not large enough to hold @len bytes, it will be expanded to the necessary + * size, reallocating if necessary. @len must be greater than 0. + * + * This function does not ensure writes are flushed prior to reading, so + * callers should take care when calling this function in plugin callbacks to + * avoid attempting to read data which may not yet be written and should use + * the memory callback API instead. + * + * Returns true on success and false on failure. + */ +QEMU_PLUGIN_API +bool qemu_plugin_read_memory_vaddr(uint64_t addr, + GByteArray *data, size_t len); + +/** + * 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..d446ad885d 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -20,6 +20,8 @@ #ifndef QEMU_RANGE_H #define QEMU_RANGE_H +#include "qemu/bitops.h" + /* * Operations on 64 bit address ranges. * Notes: @@ -208,8 +210,8 @@ static inline int range_covers_byte(uint64_t offset, uint64_t len, /* Check whether 2 given ranges overlap. * Undefined if ranges that wrap around 0. */ -static inline int ranges_overlap(uint64_t first1, uint64_t len1, - uint64_t first2, uint64_t len2) +static inline bool ranges_overlap(uint64_t first1, uint64_t len1, + uint64_t first2, uint64_t len2) { uint64_t last1 = range_get_last(first1, len1); uint64_t last2 = range_get_last(first2, len2); @@ -217,6 +219,29 @@ static inline int ranges_overlap(uint64_t first1, uint64_t len1, return !(last2 < first1 || last1 < first2); } +/* Get highest non-zero bit position of a range */ +static inline int range_get_last_bit(Range *range) +{ + if (range_is_empty(range)) { + return -1; + } + return 63 - clz64(range->upb); +} + +/* + * 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 2b0698a7c9..c562690d89 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -15,7 +15,6 @@ int inet_aton(const char *cp, struct in_addr *ia); bool fd_is_socket(int fd); int qemu_socket(int domain, int type, int protocol); -#ifndef WIN32 /** * qemu_socketpair: * @domain: specifies a communication domain, such as PF_UNIX @@ -30,7 +29,6 @@ int qemu_socket(int domain, int type, int protocol); * Return 0 on success. */ int qemu_socketpair(int domain, int type, int protocol, int sv[2]); -#endif int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); /* @@ -63,7 +61,6 @@ int socket_set_fast_reuse(int fd); int inet_ai_family_from_address(InetSocketAddress *addr, Error **errp); int inet_parse(InetSocketAddress *addr, const char *str, Error **errp); -int inet_connect(const char *str, Error **errp); int inet_connect_saddr(InetSocketAddress *saddr, Error **errp); NetworkAddressFamily inet_netfamily(int family); @@ -119,21 +116,6 @@ socket_sockaddr_to_address(struct sockaddr_storage *sa, */ SocketAddress *socket_local_address(int fd, Error **errp); -/** - * socket_remote_address: - * @fd: the socket file handle - * @errp: pointer to uninitialized error object - * - * Get the string representation of the remote socket - * address. A pointer to the allocated address information - * struct will be returned, which the caller is required to - * release with a call qapi_free_SocketAddress() when no - * longer required. - * - * Returns: the socket address struct, or NULL on error - */ -SocketAddress *socket_remote_address(int fd, Error **errp); - /** * socket_address_flatten: * @addr: the socket address to flatten 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.h b/include/qemu/thread.h index 7c6703bce3..7eba27a704 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); @@ -237,11 +241,10 @@ static inline void qemu_spin_init(QemuSpin *spin) #endif } -/* const parameter because the only purpose here is the TSAN annotation */ -static inline void qemu_spin_destroy(const QemuSpin *spin) +static inline void qemu_spin_destroy(QemuSpin *spin) { #ifdef CONFIG_TSAN - __tsan_mutex_destroy((void *)spin, __tsan_mutex_not_static); + __tsan_mutex_destroy(spin, __tsan_mutex_not_static); #endif } @@ -290,115 +293,4 @@ static inline void qemu_spin_unlock(QemuSpin *spin) #endif } -struct QemuLockCnt { -#ifndef CONFIG_LINUX - QemuMutex mutex; -#endif - unsigned count; -}; - -/** - * qemu_lockcnt_init: initialize a QemuLockcnt - * @lockcnt: the lockcnt to initialize - * - * Initialize lockcnt's counter to zero and prepare its mutex - * for usage. - */ -void qemu_lockcnt_init(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_destroy: destroy a QemuLockcnt - * @lockcnt: the lockcnt to destruct - * - * Destroy lockcnt's mutex. - */ -void qemu_lockcnt_destroy(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_inc: increment a QemuLockCnt's counter - * @lockcnt: the lockcnt to operate on - * - * If the lockcnt's count is zero, wait for critical sections - * to finish and increment lockcnt's count to 1. If the count - * is not zero, just increment it. - * - * Because this function can wait on the mutex, it must not be - * called while the lockcnt's mutex is held by the current thread. - * For the same reason, qemu_lockcnt_inc can also contribute to - * AB-BA deadlocks. This is a sample deadlock scenario: - * - * thread 1 thread 2 - * ------------------------------------------------------- - * qemu_lockcnt_lock(&lc1); - * qemu_lockcnt_lock(&lc2); - * qemu_lockcnt_inc(&lc2); - * qemu_lockcnt_inc(&lc1); - */ -void qemu_lockcnt_inc(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_dec: decrement a QemuLockCnt's counter - * @lockcnt: the lockcnt to operate on - */ -void qemu_lockcnt_dec(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_dec_and_lock: decrement a QemuLockCnt's counter and - * possibly lock it. - * @lockcnt: the lockcnt to operate on - * - * Decrement lockcnt's count. If the new count is zero, lock - * the mutex and return true. Otherwise, return false. - */ -bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_dec_if_lock: possibly decrement a QemuLockCnt's counter and - * lock it. - * @lockcnt: the lockcnt to operate on - * - * If the count is 1, decrement the count to zero, lock - * the mutex and return true. Otherwise, return false. - */ -bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_lock: lock a QemuLockCnt's mutex. - * @lockcnt: the lockcnt to operate on - * - * Remember that concurrent visits are not blocked unless the count is - * also zero. You can use qemu_lockcnt_count to check for this inside a - * critical section. - */ -void qemu_lockcnt_lock(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_unlock: release a QemuLockCnt's mutex. - * @lockcnt: the lockcnt to operate on. - */ -void qemu_lockcnt_unlock(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_inc_and_unlock: combined unlock/increment on a QemuLockCnt. - * @lockcnt: the lockcnt to operate on. - * - * This is the same as - * - * qemu_lockcnt_unlock(lockcnt); - * qemu_lockcnt_inc(lockcnt); - * - * but more efficient. - */ -void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt); - -/** - * qemu_lockcnt_count: query a LockCnt's count. - * @lockcnt: the lockcnt to query. - * - * Note that the count can change at any time. Still, while the - * lockcnt is locked, one can usefully check whether the count - * is non-zero. - */ -unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt); - #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/timed-average.h b/include/qemu/timed-average.h index 08245e7a10..dfd8d653fa 100644 --- a/include/qemu/timed-average.h +++ b/include/qemu/timed-average.h @@ -8,10 +8,12 @@ * Benoît Canet * Alberto Garcia * + * SPDX-License-Identifier: GPL-2.0-or-later + * * 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) version 3 or any later version. + * (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 diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 046a9701a2..4cec8f4d14 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -190,16 +190,6 @@ bool qemu_clock_use_for_deadline(QEMUClockType type); */ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask); -/** - * qemu_clock_get_main_loop_timerlist: - * @type: the clock type - * - * Return the default timer list associated with a clock. - * - * Returns: the default timer list - */ -QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type); - /** * qemu_clock_nofify: * @type: the clock type @@ -245,6 +235,21 @@ bool qemu_clock_run_timers(QEMUClockType type); */ bool qemu_clock_run_all_timers(void); +/** + * qemu_clock_advance_virtual_time(): advance the virtual time tick + * @target_ns: target time in nanoseconds + * + * This function is used where the control of the flow of time has + * been delegated to outside the clock subsystem (be it qtest, icount + * or some other external source). You can ask the clock system to + * return @early at the first expired timer. + * + * Time can only move forward, attempts to reverse time would lead to + * an error. + * + * Returns: new virtual time. + */ +int64_t qemu_clock_advance_virtual_time(int64_t target_ns); /* * QEMUTimerList @@ -311,17 +316,6 @@ bool timerlist_expired(QEMUTimerList *timer_list); */ int64_t timerlist_deadline_ns(QEMUTimerList *timer_list); -/** - * timerlist_get_clock: - * @timer_list: the timer list to operate on - * - * Determine the clock type associated with a timer list. - * - * Returns: the clock type associated with the - * timer list. - */ -QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list); - /** * timerlist_run_timers: * @timer_list: the timer list to use @@ -979,6 +973,37 @@ 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; +} + +#elif defined(__loongarch64) +static inline int64_t cpu_get_host_ticks(void) +{ + uint64_t val; + + asm volatile("rdtime.d %0, $zero" : "=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 +1014,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 688408e048..3d84efcac4 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -21,11 +21,11 @@ * Incomplete struct types * Please keep this list in case-insensitive alphabetical order. */ -typedef struct AdapterInfo AdapterInfo; +typedef struct AccelCPUState AccelCPUState; +typedef struct AccelState AccelState; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; typedef struct Aml Aml; -typedef struct AnnounceTimer AnnounceTimer; typedef struct ArchCPU ArchCPU; typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; @@ -36,25 +36,20 @@ typedef struct BusClass BusClass; 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 CPUJumpCache CPUJumpCache; +typedef struct CPUPluginState CPUPluginState; typedef struct CPUState CPUState; typedef struct CPUTLBEntryFull CPUTLBEntryFull; -typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; +typedef struct DisasContextBase DisasContextBase; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DriveInfo DriveInfo; +typedef struct DumpState DumpState; typedef struct Error Error; typedef struct EventNotifier EventNotifier; typedef struct FlatView FlatView; -typedef struct FWCfgEntry FWCfgEntry; -typedef struct FWCfgIoState FWCfgIoState; -typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; typedef struct HostMemoryBackend HostMemoryBackend; typedef struct I2CBus I2CBus; @@ -75,40 +70,30 @@ typedef struct MemoryRegionSection MemoryRegionSection; typedef struct MigrationIncomingState MigrationIncomingState; typedef struct MigrationState MigrationState; typedef struct Monitor Monitor; -typedef struct MonitorDef MonitorDef; typedef struct MSIMessage MSIMessage; typedef struct NetClientState NetClientState; typedef struct NetFilterState NetFilterState; typedef struct NICInfo NICInfo; -typedef struct NodeInfo NodeInfo; -typedef struct NumaNodeMem NumaNodeMem; typedef struct Object Object; typedef struct ObjectClass ObjectClass; typedef struct PCIBridge PCIBridge; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; -typedef struct PCIEAERErr PCIEAERErr; -typedef struct PCIEAERLog PCIEAERLog; -typedef struct PCIEAERMsg PCIEAERMsg; -typedef struct PCIESriovPF PCIESriovPF; -typedef struct PCIESriovVF PCIESriovVF; typedef struct PCIEPort PCIEPort; typedef struct PCIESlot PCIESlot; typedef struct PCIExpressDevice PCIExpressDevice; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; typedef struct PCIHostState PCIHostState; -typedef struct PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; typedef struct PropertyInfo PropertyInfo; 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; -typedef struct QemuOpt QemuOpt; typedef struct QemuOpts QemuOpts; typedef struct QemuOptsList QemuOptsList; typedef struct QEMUSGList QEMUSGList; @@ -123,17 +108,16 @@ 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 TaskState TaskState; typedef struct TranslationBlock TranslationBlock; typedef struct VirtIODevice VirtIODevice; typedef struct Visitor Visitor; typedef struct VMChangeStateEntry VMChangeStateEntry; typedef struct VMStateDescription VMStateDescription; -typedef struct DumpState DumpState; -typedef struct GraphicHwOps GraphicHwOps; -typedef struct QEMUCursor QEMUCursor; /* * Pointer types @@ -146,8 +130,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 deleted file mode 100644 index d201c61260..0000000000 --- a/include/qemu/uri.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Summary: library of generic URI related routines - * Description: library of generic URI related routines - * Implements RFC 2396 - * - * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. - * - * 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 - * DANIEL VEILLARD 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. - * - * Except as contained in this notice, the name of Daniel Veillard shall not - * be used in advertising or otherwise to promote the sale, use or other - * dealings in this Software without prior written authorization from him. - * - * Author: Daniel Veillard - ** - * Copyright (C) 2007 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Authors: - * Richard W.M. Jones - * - * Utility functions to help parse and assemble query strings. - */ - -#ifndef QEMU_URI_H -#define QEMU_URI_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * URI: - * - * A parsed URI reference. This is a struct containing the various fields - * 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) */ -} 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 */ -} QueryParam; - -/* Set of parameters. */ -typedef struct QueryParams { - 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); - -#ifdef __cplusplus -} -#endif -#endif /* QEMU_URI_H */ diff --git a/include/qemu/userfaultfd.h b/include/qemu/userfaultfd.h index 6b74f92792..a1979308d7 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); @@ -30,6 +39,7 @@ int uffd_copy_page(int uffd_fd, void *dst_addr, void *src_addr, int uffd_zero_page(int uffd_fd, void *addr, uint64_t length, bool dont_wake); 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 92aed2ac3e..8cb6b03e0f 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -263,6 +263,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 @@ -289,32 +334,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: @@ -373,6 +396,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. @@ -1098,6 +1160,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 @@ -1504,8 +1574,8 @@ char *object_get_canonical_path(const Object *obj); /** * object_resolve_path: * @path: the path to resolve - * @ambiguous: returns true if the path resolution failed because of an - * ambiguous match + * @ambiguous: (out) (optional): location to store whether the lookup failed + * because it was ambiguous, or %NULL. Set to %false on success. * * There are two types of supported paths--absolute paths and partial paths. * @@ -1522,7 +1592,7 @@ char *object_get_canonical_path(const Object *obj); * only one match is found. If more than one match is found, a flag is * returned to indicate that the match was ambiguous. * - * Returns: The matched object or NULL on path lookup failure. + * Returns: The matched object or %NULL on path lookup failure. */ Object *object_resolve_path(const char *path, bool *ambiguous); @@ -1530,10 +1600,10 @@ Object *object_resolve_path(const char *path, bool *ambiguous); * object_resolve_path_type: * @path: the path to resolve * @typename: the type to look for. - * @ambiguous: returns true if the path resolution failed because of an - * ambiguous match + * @ambiguous: (out) (optional): location to store whether the lookup failed + * because it was ambiguous, or %NULL. Set to %false on success. * - * This is similar to object_resolve_path. However, when looking for a + * This is similar to object_resolve_path(). However, when looking for a * partial path only matches that implement the given type are considered. * This restricts the search and avoids spuriously flagging matches as * ambiguous. @@ -1547,6 +1617,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 @@ -1954,14 +2037,6 @@ int object_child_foreach_recursive(Object *obj, */ Object *container_get(Object *root, const char *path); -/** - * object_type_get_instance_size: - * @typename: Name of the Type whose instance_size is required - * - * Returns the instance_size of the given @typename. - */ -size_t object_type_get_instance_size(const char *typename); - /** * object_property_help: * @name: the name of the property 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 6a8bad556a..9b98451912 100644 --- a/include/scsi/constants.h +++ b/include/scsi/constants.h @@ -231,6 +231,7 @@ #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 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/include/semihosting/semihost.h b/include/semihosting/semihost.h index efd2efa25a..97d2a2ba99 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -66,7 +66,7 @@ const char *semihosting_get_cmdline(void); void semihosting_arg_fallback(const char *file, const char *cmd); /* for vl.c hooks */ void qemu_semihosting_enable(void); -int qemu_semihosting_config_options(const char *opt); +int qemu_semihosting_config_options(const char *optstr); void qemu_semihosting_chardev_init(void); void qemu_semihosting_console_init(Chardev *); #endif /* CONFIG_USER_ONLY */ diff --git a/include/semihosting/softmmu-uaccess.h b/include/semihosting/softmmu-uaccess.h deleted file mode 100644 index 4f08dfc098..0000000000 --- a/include/semihosting/softmmu-uaccess.h +++ /dev/null @@ -1,59 +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 SEMIHOSTING_SOFTMMU_UACCESS_H -#define SEMIHOSTING_SOFTMMU_UACCESS_H - -#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 *softmmu_lock_user(CPUArchState *env, target_ulong addr, - target_ulong len, bool copy); -#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy) - -char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr); -#define lock_user_string(p) softmmu_lock_user_string(env, p) - -void softmmu_unlock_user(CPUArchState *env, void *p, - target_ulong addr, target_ulong len); -#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len) - -ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr); -#define target_strlen(p) softmmu_strlen_user(env, p) - -#endif /* SEMIHOSTING_SOFTMMU_UACCESS_H */ diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h index 3a5ec229eb..b5937c619a 100644 --- a/include/semihosting/syscalls.h +++ b/include/semihosting/syscalls.h @@ -9,6 +9,8 @@ #ifndef SEMIHOSTING_SYSCALLS_H #define SEMIHOSTING_SYSCALLS_H +#include "gdbstub/syscalls.h" + /* * Argument loading from the guest is performed by the caller; * results are returned via the 'complete' callback. diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h new file mode 100644 index 0000000000..c2fa5a655d --- /dev/null +++ b/include/semihosting/uaccess.h @@ -0,0 +1,66 @@ +/* + * 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 "exec/cpu-common.h" +#include "exec/cpu-defs.h" +#include "exec/tswap.h" +#include "exec/page-protection.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-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h index 0b06d2bff1..b582a105c0 100644 --- a/include/standard-headers/asm-x86/bootparam.h +++ b/include/standard-headers/asm-x86/bootparam.h @@ -2,21 +2,7 @@ #ifndef _ASM_X86_BOOTPARAM_H #define _ASM_X86_BOOTPARAM_H -/* setup_data/setup_indirect types */ -#define SETUP_NONE 0 -#define SETUP_E820_EXT 1 -#define SETUP_DTB 2 -#define SETUP_PCI 3 -#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) -#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) +#include "standard-headers/asm-x86/setup_data.h" /* ram_size flags */ #define RAMDISK_IMAGE_START_MASK 0x07FF @@ -38,6 +24,7 @@ #define XLF_EFI_KEXEC (1<<4) #define XLF_5LEVEL (1<<5) #define XLF_5LEVEL_ENABLED (1<<6) +#define XLF_MEM_ENCRYPTION (1<<7) #endif /* _ASM_X86_BOOTPARAM_H */ diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index f0235e58a1..9a011d20f0 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -92,7 +92,7 @@ struct kvm_clock_pairing { #define KVM_ASYNC_PF_DELIVERY_AS_INT (1 << 3) /* MSR_KVM_ASYNC_PF_INT */ -#define KVM_ASYNC_PF_VEC_MASK GENMASK(7, 0) +#define KVM_ASYNC_PF_VEC_MASK __GENMASK(7, 0) /* MSR_KVM_MIGRATION_CONTROL */ #define KVM_MIGRATION_READY (1 << 0) @@ -142,7 +142,6 @@ struct kvm_vcpu_pv_apf_data { uint32_t token; uint8_t pad[56]; - uint32_t enabled; }; #define KVM_PV_EOI_BIT 0 diff --git a/include/standard-headers/asm-x86/setup_data.h b/include/standard-headers/asm-x86/setup_data.h new file mode 100644 index 0000000000..09355f54c5 --- /dev/null +++ b/include/standard-headers/asm-x86/setup_data.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_X86_SETUP_DATA_H +#define _ASM_X86_SETUP_DATA_H + +/* setup_data/setup_indirect types */ +#define SETUP_NONE 0 +#define SETUP_E820_EXT 1 +#define SETUP_DTB 2 +#define SETUP_PCI 3 +#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) +#define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) + +#ifndef __ASSEMBLY__ + +#include "standard-headers/linux/types.h" + +/* extensible setup data list node */ +struct setup_data { + uint64_t next; + uint32_t type; + uint32_t len; + uint8_t data[]; +}; + +/* extensible setup indirect data node */ +struct setup_indirect { + uint32_t type; + uint32_t reserved; /* Reserved, must be set to zero. */ + uint64_t len; + uint64_t addr; +}; + +/* + * The E820 memory region entry of the boot protocol ABI: + */ +struct boot_e820_entry { + uint64_t addr; + uint64_t size; + uint32_t type; +} QEMU_PACKED; + +/* + * The boot loader is passing platform information via this Jailhouse-specific + * setup data structure. + */ +struct jailhouse_setup_data { + struct { + uint16_t version; + uint16_t compatible_version; + } QEMU_PACKED hdr; + struct { + uint16_t pm_timer_address; + uint16_t num_cpus; + uint64_t pci_mmconfig_base; + uint32_t tsc_khz; + uint32_t apic_khz; + uint8_t standard_ioapic; + uint8_t cpu_ids[255]; + } QEMU_PACKED v1; + struct { + uint32_t flags; + } QEMU_PACKED v2; +} QEMU_PACKED; + +/* + * IMA buffer setup data information from the previous kernel during kexec + */ +struct ima_setup_data { + uint64_t addr; + uint64_t size; +} QEMU_PACKED; + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_X86_SETUP_DATA_H */ diff --git a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h deleted file mode 100644 index a5a1c8234e..0000000000 --- a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h +++ /dev/null @@ -1,685 +0,0 @@ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * 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. - * - * 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 HOLDER 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 __PVRDMA_DEV_API_H__ -#define __PVRDMA_DEV_API_H__ - -#include "standard-headers/linux/types.h" - -#include "pvrdma_verbs.h" - -/* - * PVRDMA version macros. Some new features require updates to PVRDMA_VERSION. - * These macros allow us to check for different features if necessary. - */ - -#define PVRDMA_ROCEV1_VERSION 17 -#define PVRDMA_ROCEV2_VERSION 18 -#define PVRDMA_PPN64_VERSION 19 -#define PVRDMA_QPHANDLE_VERSION 20 -#define PVRDMA_VERSION PVRDMA_QPHANDLE_VERSION - -#define PVRDMA_BOARD_ID 1 -#define PVRDMA_REV_ID 1 - -/* - * Masks and accessors for page directory, which is a two-level lookup: - * page directory -> page table -> page. Only one directory for now, but we - * could expand that easily. 9 bits for tables, 9 bits for pages, gives one - * gigabyte for memory regions and so forth. - */ - -#define PVRDMA_PDIR_SHIFT 18 -#define PVRDMA_PTABLE_SHIFT 9 -#define PVRDMA_PAGE_DIR_DIR(x) (((x) >> PVRDMA_PDIR_SHIFT) & 0x1) -#define PVRDMA_PAGE_DIR_TABLE(x) (((x) >> PVRDMA_PTABLE_SHIFT) & 0x1ff) -#define PVRDMA_PAGE_DIR_PAGE(x) ((x) & 0x1ff) -#define PVRDMA_PAGE_DIR_MAX_PAGES (1 * 512 * 512) -#define PVRDMA_MAX_FAST_REG_PAGES 128 - -/* - * Max MSI-X vectors. - */ - -#define PVRDMA_MAX_INTERRUPTS 3 - -/* Register offsets within PCI resource on BAR1. */ -#define PVRDMA_REG_VERSION 0x00 /* R: Version of device. */ -#define PVRDMA_REG_DSRLOW 0x04 /* W: Device shared region low PA. */ -#define PVRDMA_REG_DSRHIGH 0x08 /* W: Device shared region high PA. */ -#define PVRDMA_REG_CTL 0x0c /* W: PVRDMA_DEVICE_CTL */ -#define PVRDMA_REG_REQUEST 0x10 /* W: Indicate device request. */ -#define PVRDMA_REG_ERR 0x14 /* R: Device error. */ -#define PVRDMA_REG_ICR 0x18 /* R: Interrupt cause. */ -#define PVRDMA_REG_IMR 0x1c /* R/W: Interrupt mask. */ -#define PVRDMA_REG_MACL 0x20 /* R/W: MAC address low. */ -#define PVRDMA_REG_MACH 0x24 /* R/W: MAC address high. */ - -/* Object flags. */ -#define PVRDMA_CQ_FLAG_ARMED_SOL BIT(0) /* Armed for solicited-only. */ -#define PVRDMA_CQ_FLAG_ARMED BIT(1) /* Armed. */ -#define PVRDMA_MR_FLAG_DMA BIT(0) /* DMA region. */ -#define PVRDMA_MR_FLAG_FRMR BIT(1) /* Fast reg memory region. */ - -/* - * Atomic operation capability (masked versions are extended atomic - * operations. - */ - -#define PVRDMA_ATOMIC_OP_COMP_SWAP BIT(0) /* Compare and swap. */ -#define PVRDMA_ATOMIC_OP_FETCH_ADD BIT(1) /* Fetch and add. */ -#define PVRDMA_ATOMIC_OP_MASK_COMP_SWAP BIT(2) /* Masked compare and swap. */ -#define PVRDMA_ATOMIC_OP_MASK_FETCH_ADD BIT(3) /* Masked fetch and add. */ - -/* - * Base Memory Management Extension flags to support Fast Reg Memory Regions - * and Fast Reg Work Requests. Each flag represents a verb operation and we - * must support all of them to qualify for the BMME device cap. - */ - -#define PVRDMA_BMME_FLAG_LOCAL_INV BIT(0) /* Local Invalidate. */ -#define PVRDMA_BMME_FLAG_REMOTE_INV BIT(1) /* Remote Invalidate. */ -#define PVRDMA_BMME_FLAG_FAST_REG_WR BIT(2) /* Fast Reg Work Request. */ - -/* - * GID types. The interpretation of the gid_types bit field in the device - * capabilities will depend on the device mode. For now, the device only - * supports RoCE as mode, so only the different GID types for RoCE are - * defined. - */ - -#define PVRDMA_GID_TYPE_FLAG_ROCE_V1 BIT(0) -#define PVRDMA_GID_TYPE_FLAG_ROCE_V2 BIT(1) - -/* - * Version checks. This checks whether each version supports specific - * capabilities from the device. - */ - -#define PVRDMA_IS_VERSION17(_dev) \ - (_dev->dsr_version == PVRDMA_ROCEV1_VERSION && \ - _dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V1) - -#define PVRDMA_IS_VERSION18(_dev) \ - (_dev->dsr_version >= PVRDMA_ROCEV2_VERSION && \ - (_dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V1 || \ - _dev->dsr->caps.gid_types == PVRDMA_GID_TYPE_FLAG_ROCE_V2)) \ - -#define PVRDMA_SUPPORTED(_dev) \ - ((_dev->dsr->caps.mode == PVRDMA_DEVICE_MODE_ROCE) && \ - (PVRDMA_IS_VERSION17(_dev) || PVRDMA_IS_VERSION18(_dev))) - -/* - * Get capability values based on device version. - */ - -#define PVRDMA_GET_CAP(_dev, _old_val, _val) \ - ((PVRDMA_IS_VERSION18(_dev)) ? _val : _old_val) - -enum pvrdma_pci_resource { - PVRDMA_PCI_RESOURCE_MSIX, /* BAR0: MSI-X, MMIO. */ - PVRDMA_PCI_RESOURCE_REG, /* BAR1: Registers, MMIO. */ - PVRDMA_PCI_RESOURCE_UAR, /* BAR2: UAR pages, MMIO, 64-bit. */ - PVRDMA_PCI_RESOURCE_LAST, /* Last. */ -}; - -enum pvrdma_device_ctl { - PVRDMA_DEVICE_CTL_ACTIVATE, /* Activate device. */ - PVRDMA_DEVICE_CTL_UNQUIESCE, /* Unquiesce device. */ - PVRDMA_DEVICE_CTL_RESET, /* Reset device. */ -}; - -enum pvrdma_intr_vector { - PVRDMA_INTR_VECTOR_RESPONSE, /* Command response. */ - PVRDMA_INTR_VECTOR_ASYNC, /* Async events. */ - PVRDMA_INTR_VECTOR_CQ, /* CQ notification. */ - /* Additional CQ notification vectors. */ -}; - -enum pvrdma_intr_cause { - PVRDMA_INTR_CAUSE_RESPONSE = (1 << PVRDMA_INTR_VECTOR_RESPONSE), - PVRDMA_INTR_CAUSE_ASYNC = (1 << PVRDMA_INTR_VECTOR_ASYNC), - PVRDMA_INTR_CAUSE_CQ = (1 << PVRDMA_INTR_VECTOR_CQ), -}; - -enum pvrdma_gos_bits { - PVRDMA_GOS_BITS_UNK, /* Unknown. */ - PVRDMA_GOS_BITS_32, /* 32-bit. */ - PVRDMA_GOS_BITS_64, /* 64-bit. */ -}; - -enum pvrdma_gos_type { - PVRDMA_GOS_TYPE_UNK, /* Unknown. */ - PVRDMA_GOS_TYPE_LINUX, /* Linux. */ -}; - -enum pvrdma_device_mode { - PVRDMA_DEVICE_MODE_ROCE, /* RoCE. */ - PVRDMA_DEVICE_MODE_IWARP, /* iWarp. */ - PVRDMA_DEVICE_MODE_IB, /* InfiniBand. */ -}; - -struct pvrdma_gos_info { - uint32_t gos_bits:2; /* W: PVRDMA_GOS_BITS_ */ - uint32_t gos_type:4; /* W: PVRDMA_GOS_TYPE_ */ - uint32_t gos_ver:16; /* W: Guest OS version. */ - uint32_t gos_misc:10; /* W: Other. */ - uint32_t pad; /* Pad to 8-byte alignment. */ -}; - -struct pvrdma_device_caps { - uint64_t fw_ver; /* R: Query device. */ - uint64_t node_guid; - uint64_t sys_image_guid; - uint64_t max_mr_size; - uint64_t page_size_cap; - uint64_t atomic_arg_sizes; /* EX verbs. */ - uint32_t ex_comp_mask; /* EX verbs. */ - uint32_t device_cap_flags2; /* EX verbs. */ - uint32_t max_fa_bit_boundary; /* EX verbs. */ - uint32_t log_max_atomic_inline_arg; /* EX verbs. */ - uint32_t vendor_id; - uint32_t vendor_part_id; - uint32_t hw_ver; - uint32_t max_qp; - uint32_t max_qp_wr; - uint32_t device_cap_flags; - uint32_t max_sge; - uint32_t max_sge_rd; - uint32_t max_cq; - uint32_t max_cqe; - uint32_t max_mr; - uint32_t max_pd; - uint32_t max_qp_rd_atom; - uint32_t max_ee_rd_atom; - uint32_t max_res_rd_atom; - uint32_t max_qp_init_rd_atom; - uint32_t max_ee_init_rd_atom; - uint32_t max_ee; - uint32_t max_rdd; - uint32_t max_mw; - uint32_t max_raw_ipv6_qp; - uint32_t max_raw_ethy_qp; - uint32_t max_mcast_grp; - uint32_t max_mcast_qp_attach; - uint32_t max_total_mcast_qp_attach; - uint32_t max_ah; - uint32_t max_fmr; - uint32_t max_map_per_fmr; - uint32_t max_srq; - uint32_t max_srq_wr; - uint32_t max_srq_sge; - uint32_t max_uar; - uint32_t gid_tbl_len; - uint16_t max_pkeys; - uint8_t local_ca_ack_delay; - uint8_t phys_port_cnt; - uint8_t mode; /* PVRDMA_DEVICE_MODE_ */ - uint8_t atomic_ops; /* PVRDMA_ATOMIC_OP_* bits */ - uint8_t bmme_flags; /* FRWR Mem Mgmt Extensions */ - uint8_t gid_types; /* PVRDMA_GID_TYPE_FLAG_ */ - uint32_t max_fast_reg_page_list_len; -}; - -struct pvrdma_ring_page_info { - uint32_t num_pages; /* Num pages incl. header. */ - uint32_t reserved; /* Reserved. */ - uint64_t pdir_dma; /* Page directory PA. */ -}; - -#pragma pack(push, 1) - -struct pvrdma_device_shared_region { - uint32_t driver_version; /* W: Driver version. */ - uint32_t pad; /* Pad to 8-byte align. */ - struct pvrdma_gos_info gos_info; /* W: Guest OS information. */ - uint64_t cmd_slot_dma; /* W: Command slot address. */ - uint64_t resp_slot_dma; /* W: Response slot address. */ - struct pvrdma_ring_page_info async_ring_pages; - /* W: Async ring page info. */ - struct pvrdma_ring_page_info cq_ring_pages; - /* W: CQ ring page info. */ - union { - uint32_t uar_pfn; /* W: UAR pageframe. */ - uint64_t uar_pfn64; /* W: 64-bit UAR page frame. */ - }; - struct pvrdma_device_caps caps; /* R: Device capabilities. */ -}; - -#pragma pack(pop) - -/* Event types. Currently a 1:1 mapping with enum ib_event. */ -enum pvrdma_eqe_type { - PVRDMA_EVENT_CQ_ERR, - PVRDMA_EVENT_QP_FATAL, - PVRDMA_EVENT_QP_REQ_ERR, - PVRDMA_EVENT_QP_ACCESS_ERR, - PVRDMA_EVENT_COMM_EST, - PVRDMA_EVENT_SQ_DRAINED, - PVRDMA_EVENT_PATH_MIG, - PVRDMA_EVENT_PATH_MIG_ERR, - PVRDMA_EVENT_DEVICE_FATAL, - PVRDMA_EVENT_PORT_ACTIVE, - PVRDMA_EVENT_PORT_ERR, - PVRDMA_EVENT_LID_CHANGE, - PVRDMA_EVENT_PKEY_CHANGE, - PVRDMA_EVENT_SM_CHANGE, - PVRDMA_EVENT_SRQ_ERR, - PVRDMA_EVENT_SRQ_LIMIT_REACHED, - PVRDMA_EVENT_QP_LAST_WQE_REACHED, - PVRDMA_EVENT_CLIENT_REREGISTER, - PVRDMA_EVENT_GID_CHANGE, -}; - -/* Event queue element. */ -struct pvrdma_eqe { - uint32_t type; /* Event type. */ - uint32_t info; /* Handle, other. */ -}; - -/* CQ notification queue element. */ -struct pvrdma_cqne { - uint32_t info; /* Handle */ -}; - -enum { - PVRDMA_CMD_FIRST, - PVRDMA_CMD_QUERY_PORT = PVRDMA_CMD_FIRST, - PVRDMA_CMD_QUERY_PKEY, - PVRDMA_CMD_CREATE_PD, - PVRDMA_CMD_DESTROY_PD, - PVRDMA_CMD_CREATE_MR, - PVRDMA_CMD_DESTROY_MR, - PVRDMA_CMD_CREATE_CQ, - PVRDMA_CMD_RESIZE_CQ, - PVRDMA_CMD_DESTROY_CQ, - PVRDMA_CMD_CREATE_QP, - PVRDMA_CMD_MODIFY_QP, - PVRDMA_CMD_QUERY_QP, - PVRDMA_CMD_DESTROY_QP, - PVRDMA_CMD_CREATE_UC, - PVRDMA_CMD_DESTROY_UC, - PVRDMA_CMD_CREATE_BIND, - PVRDMA_CMD_DESTROY_BIND, - PVRDMA_CMD_CREATE_SRQ, - PVRDMA_CMD_MODIFY_SRQ, - PVRDMA_CMD_QUERY_SRQ, - PVRDMA_CMD_DESTROY_SRQ, - PVRDMA_CMD_MAX, -}; - -enum { - PVRDMA_CMD_FIRST_RESP = (1 << 31), - PVRDMA_CMD_QUERY_PORT_RESP = PVRDMA_CMD_FIRST_RESP, - PVRDMA_CMD_QUERY_PKEY_RESP, - PVRDMA_CMD_CREATE_PD_RESP, - PVRDMA_CMD_DESTROY_PD_RESP_NOOP, - PVRDMA_CMD_CREATE_MR_RESP, - PVRDMA_CMD_DESTROY_MR_RESP_NOOP, - PVRDMA_CMD_CREATE_CQ_RESP, - PVRDMA_CMD_RESIZE_CQ_RESP, - PVRDMA_CMD_DESTROY_CQ_RESP_NOOP, - PVRDMA_CMD_CREATE_QP_RESP, - PVRDMA_CMD_MODIFY_QP_RESP, - PVRDMA_CMD_QUERY_QP_RESP, - PVRDMA_CMD_DESTROY_QP_RESP, - PVRDMA_CMD_CREATE_UC_RESP, - PVRDMA_CMD_DESTROY_UC_RESP_NOOP, - PVRDMA_CMD_CREATE_BIND_RESP_NOOP, - PVRDMA_CMD_DESTROY_BIND_RESP_NOOP, - PVRDMA_CMD_CREATE_SRQ_RESP, - PVRDMA_CMD_MODIFY_SRQ_RESP, - PVRDMA_CMD_QUERY_SRQ_RESP, - PVRDMA_CMD_DESTROY_SRQ_RESP, - PVRDMA_CMD_MAX_RESP, -}; - -struct pvrdma_cmd_hdr { - uint64_t response; /* Key for response lookup. */ - uint32_t cmd; /* PVRDMA_CMD_ */ - uint32_t reserved; /* Reserved. */ -}; - -struct pvrdma_cmd_resp_hdr { - uint64_t response; /* From cmd hdr. */ - uint32_t ack; /* PVRDMA_CMD_XXX_RESP */ - uint8_t err; /* Error. */ - uint8_t reserved[3]; /* Reserved. */ -}; - -struct pvrdma_cmd_query_port { - struct pvrdma_cmd_hdr hdr; - uint8_t port_num; - uint8_t reserved[7]; -}; - -struct pvrdma_cmd_query_port_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_port_attr attrs; -}; - -struct pvrdma_cmd_query_pkey { - struct pvrdma_cmd_hdr hdr; - uint8_t port_num; - uint8_t index; - uint8_t reserved[6]; -}; - -struct pvrdma_cmd_query_pkey_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint16_t pkey; - uint8_t reserved[6]; -}; - -struct pvrdma_cmd_create_uc { - struct pvrdma_cmd_hdr hdr; - union { - uint32_t pfn; /* UAR page frame number */ - uint64_t pfn64; /* 64-bit UAR page frame number */ - }; -}; - -struct pvrdma_cmd_create_uc_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t ctx_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_uc { - struct pvrdma_cmd_hdr hdr; - uint32_t ctx_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_pd { - struct pvrdma_cmd_hdr hdr; - uint32_t ctx_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_pd_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t pd_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_pd { - struct pvrdma_cmd_hdr hdr; - uint32_t pd_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_mr { - struct pvrdma_cmd_hdr hdr; - uint64_t start; - uint64_t length; - uint64_t pdir_dma; - uint32_t pd_handle; - uint32_t access_flags; - uint32_t flags; - uint32_t nchunks; -}; - -struct pvrdma_cmd_create_mr_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t mr_handle; - uint32_t lkey; - uint32_t rkey; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_mr { - struct pvrdma_cmd_hdr hdr; - uint32_t mr_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_cq { - struct pvrdma_cmd_hdr hdr; - uint64_t pdir_dma; - uint32_t ctx_handle; - uint32_t cqe; - uint32_t nchunks; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_cq_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t cq_handle; - uint32_t cqe; -}; - -struct pvrdma_cmd_resize_cq { - struct pvrdma_cmd_hdr hdr; - uint32_t cq_handle; - uint32_t cqe; -}; - -struct pvrdma_cmd_resize_cq_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t cqe; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_cq { - struct pvrdma_cmd_hdr hdr; - uint32_t cq_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_srq { - struct pvrdma_cmd_hdr hdr; - uint64_t pdir_dma; - uint32_t pd_handle; - uint32_t nchunks; - struct pvrdma_srq_attr attrs; - uint8_t srq_type; - uint8_t reserved[7]; -}; - -struct pvrdma_cmd_create_srq_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t srqn; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_modify_srq { - struct pvrdma_cmd_hdr hdr; - uint32_t srq_handle; - uint32_t attr_mask; - struct pvrdma_srq_attr attrs; -}; - -struct pvrdma_cmd_query_srq { - struct pvrdma_cmd_hdr hdr; - uint32_t srq_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_query_srq_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_srq_attr attrs; -}; - -struct pvrdma_cmd_destroy_srq { - struct pvrdma_cmd_hdr hdr; - uint32_t srq_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_qp { - struct pvrdma_cmd_hdr hdr; - uint64_t pdir_dma; - uint32_t pd_handle; - uint32_t send_cq_handle; - uint32_t recv_cq_handle; - uint32_t srq_handle; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; - uint32_t lkey; - uint32_t access_flags; - uint16_t total_chunks; - uint16_t send_chunks; - uint16_t max_atomic_arg; - uint8_t sq_sig_all; - uint8_t qp_type; - uint8_t is_srq; - uint8_t reserved[3]; -}; - -struct pvrdma_cmd_create_qp_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t qpn; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; -}; - -struct pvrdma_cmd_create_qp_resp_v2 { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t qpn; - uint32_t qp_handle; - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; -}; - -struct pvrdma_cmd_modify_qp { - struct pvrdma_cmd_hdr hdr; - uint32_t qp_handle; - uint32_t attr_mask; - struct pvrdma_qp_attr attrs; -}; - -struct pvrdma_cmd_query_qp { - struct pvrdma_cmd_hdr hdr; - uint32_t qp_handle; - uint32_t attr_mask; -}; - -struct pvrdma_cmd_query_qp_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_qp_attr attrs; -}; - -struct pvrdma_cmd_destroy_qp { - struct pvrdma_cmd_hdr hdr; - uint32_t qp_handle; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_destroy_qp_resp { - struct pvrdma_cmd_resp_hdr hdr; - uint32_t events_reported; - uint8_t reserved[4]; -}; - -struct pvrdma_cmd_create_bind { - struct pvrdma_cmd_hdr hdr; - uint32_t mtu; - uint32_t vlan; - uint32_t index; - uint8_t new_gid[16]; - uint8_t gid_type; - uint8_t reserved[3]; -}; - -struct pvrdma_cmd_destroy_bind { - struct pvrdma_cmd_hdr hdr; - uint32_t index; - uint8_t dest_gid[16]; - uint8_t reserved[4]; -}; - -union pvrdma_cmd_req { - struct pvrdma_cmd_hdr hdr; - struct pvrdma_cmd_query_port query_port; - struct pvrdma_cmd_query_pkey query_pkey; - struct pvrdma_cmd_create_uc create_uc; - struct pvrdma_cmd_destroy_uc destroy_uc; - struct pvrdma_cmd_create_pd create_pd; - struct pvrdma_cmd_destroy_pd destroy_pd; - struct pvrdma_cmd_create_mr create_mr; - struct pvrdma_cmd_destroy_mr destroy_mr; - struct pvrdma_cmd_create_cq create_cq; - struct pvrdma_cmd_resize_cq resize_cq; - struct pvrdma_cmd_destroy_cq destroy_cq; - struct pvrdma_cmd_create_qp create_qp; - struct pvrdma_cmd_modify_qp modify_qp; - struct pvrdma_cmd_query_qp query_qp; - struct pvrdma_cmd_destroy_qp destroy_qp; - struct pvrdma_cmd_create_bind create_bind; - struct pvrdma_cmd_destroy_bind destroy_bind; - struct pvrdma_cmd_create_srq create_srq; - struct pvrdma_cmd_modify_srq modify_srq; - struct pvrdma_cmd_query_srq query_srq; - struct pvrdma_cmd_destroy_srq destroy_srq; -}; - -union pvrdma_cmd_resp { - struct pvrdma_cmd_resp_hdr hdr; - struct pvrdma_cmd_query_port_resp query_port_resp; - struct pvrdma_cmd_query_pkey_resp query_pkey_resp; - struct pvrdma_cmd_create_uc_resp create_uc_resp; - struct pvrdma_cmd_create_pd_resp create_pd_resp; - struct pvrdma_cmd_create_mr_resp create_mr_resp; - struct pvrdma_cmd_create_cq_resp create_cq_resp; - struct pvrdma_cmd_resize_cq_resp resize_cq_resp; - struct pvrdma_cmd_create_qp_resp create_qp_resp; - struct pvrdma_cmd_create_qp_resp_v2 create_qp_resp_v2; - struct pvrdma_cmd_query_qp_resp query_qp_resp; - struct pvrdma_cmd_destroy_qp_resp destroy_qp_resp; - struct pvrdma_cmd_create_srq_resp create_srq_resp; - struct pvrdma_cmd_query_srq_resp query_srq_resp; -}; - -#endif /* __PVRDMA_DEV_API_H__ */ diff --git a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h b/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h deleted file mode 100644 index 94d41b202c..0000000000 --- a/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * 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. - * - * 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 HOLDER 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 __PVRDMA_VERBS_H__ -#define __PVRDMA_VERBS_H__ - -#include "standard-headers/linux/types.h" - -union pvrdma_gid { - uint8_t raw[16]; - struct { - uint64_t subnet_prefix; - uint64_t interface_id; - } global; -}; - -enum pvrdma_link_layer { - PVRDMA_LINK_LAYER_UNSPECIFIED, - PVRDMA_LINK_LAYER_INFINIBAND, - PVRDMA_LINK_LAYER_ETHERNET, -}; - -enum pvrdma_mtu { - PVRDMA_MTU_256 = 1, - PVRDMA_MTU_512 = 2, - PVRDMA_MTU_1024 = 3, - PVRDMA_MTU_2048 = 4, - PVRDMA_MTU_4096 = 5, -}; - -enum pvrdma_port_state { - PVRDMA_PORT_NOP = 0, - PVRDMA_PORT_DOWN = 1, - PVRDMA_PORT_INIT = 2, - PVRDMA_PORT_ARMED = 3, - PVRDMA_PORT_ACTIVE = 4, - PVRDMA_PORT_ACTIVE_DEFER = 5, -}; - -enum pvrdma_port_cap_flags { - PVRDMA_PORT_SM = 1 << 1, - PVRDMA_PORT_NOTICE_SUP = 1 << 2, - PVRDMA_PORT_TRAP_SUP = 1 << 3, - PVRDMA_PORT_OPT_IPD_SUP = 1 << 4, - PVRDMA_PORT_AUTO_MIGR_SUP = 1 << 5, - PVRDMA_PORT_SL_MAP_SUP = 1 << 6, - PVRDMA_PORT_MKEY_NVRAM = 1 << 7, - PVRDMA_PORT_PKEY_NVRAM = 1 << 8, - PVRDMA_PORT_LED_INFO_SUP = 1 << 9, - PVRDMA_PORT_SM_DISABLED = 1 << 10, - PVRDMA_PORT_SYS_IMAGE_GUID_SUP = 1 << 11, - PVRDMA_PORT_PKEY_SW_EXT_PORT_TRAP_SUP = 1 << 12, - PVRDMA_PORT_EXTENDED_SPEEDS_SUP = 1 << 14, - PVRDMA_PORT_CM_SUP = 1 << 16, - PVRDMA_PORT_SNMP_TUNNEL_SUP = 1 << 17, - PVRDMA_PORT_REINIT_SUP = 1 << 18, - PVRDMA_PORT_DEVICE_MGMT_SUP = 1 << 19, - PVRDMA_PORT_VENDOR_CLASS_SUP = 1 << 20, - PVRDMA_PORT_DR_NOTICE_SUP = 1 << 21, - PVRDMA_PORT_CAP_MASK_NOTICE_SUP = 1 << 22, - PVRDMA_PORT_BOOT_MGMT_SUP = 1 << 23, - PVRDMA_PORT_LINK_LATENCY_SUP = 1 << 24, - PVRDMA_PORT_CLIENT_REG_SUP = 1 << 25, - PVRDMA_PORT_IP_BASED_GIDS = 1 << 26, - PVRDMA_PORT_CAP_FLAGS_MAX = PVRDMA_PORT_IP_BASED_GIDS, -}; - -enum pvrdma_port_width { - PVRDMA_WIDTH_1X = 1, - PVRDMA_WIDTH_4X = 2, - PVRDMA_WIDTH_8X = 4, - PVRDMA_WIDTH_12X = 8, -}; - -enum pvrdma_port_speed { - PVRDMA_SPEED_SDR = 1, - PVRDMA_SPEED_DDR = 2, - PVRDMA_SPEED_QDR = 4, - PVRDMA_SPEED_FDR10 = 8, - PVRDMA_SPEED_FDR = 16, - PVRDMA_SPEED_EDR = 32, -}; - -struct pvrdma_port_attr { - enum pvrdma_port_state state; - enum pvrdma_mtu max_mtu; - enum pvrdma_mtu active_mtu; - uint32_t gid_tbl_len; - uint32_t port_cap_flags; - uint32_t max_msg_sz; - uint32_t bad_pkey_cntr; - uint32_t qkey_viol_cntr; - uint16_t pkey_tbl_len; - uint16_t lid; - uint16_t sm_lid; - uint8_t lmc; - uint8_t max_vl_num; - uint8_t sm_sl; - uint8_t subnet_timeout; - uint8_t init_type_reply; - uint8_t active_width; - uint8_t active_speed; - uint8_t phys_state; - uint8_t reserved[2]; -}; - -struct pvrdma_global_route { - union pvrdma_gid dgid; - uint32_t flow_label; - uint8_t sgid_index; - uint8_t hop_limit; - uint8_t traffic_class; - uint8_t reserved; -}; - -struct pvrdma_grh { - uint32_t version_tclass_flow; - uint16_t paylen; - uint8_t next_hdr; - uint8_t hop_limit; - union pvrdma_gid sgid; - union pvrdma_gid dgid; -}; - -enum pvrdma_ah_flags { - PVRDMA_AH_GRH = 1, -}; - -enum pvrdma_rate { - PVRDMA_RATE_PORT_CURRENT = 0, - PVRDMA_RATE_2_5_GBPS = 2, - PVRDMA_RATE_5_GBPS = 5, - PVRDMA_RATE_10_GBPS = 3, - PVRDMA_RATE_20_GBPS = 6, - PVRDMA_RATE_30_GBPS = 4, - PVRDMA_RATE_40_GBPS = 7, - PVRDMA_RATE_60_GBPS = 8, - PVRDMA_RATE_80_GBPS = 9, - PVRDMA_RATE_120_GBPS = 10, - PVRDMA_RATE_14_GBPS = 11, - PVRDMA_RATE_56_GBPS = 12, - PVRDMA_RATE_112_GBPS = 13, - PVRDMA_RATE_168_GBPS = 14, - PVRDMA_RATE_25_GBPS = 15, - PVRDMA_RATE_100_GBPS = 16, - PVRDMA_RATE_200_GBPS = 17, - PVRDMA_RATE_300_GBPS = 18, -}; - -struct pvrdma_ah_attr { - struct pvrdma_global_route grh; - uint16_t dlid; - uint16_t vlan_id; - uint8_t sl; - uint8_t src_path_bits; - uint8_t static_rate; - uint8_t ah_flags; - uint8_t port_num; - uint8_t dmac[6]; - uint8_t reserved; -}; - -enum pvrdma_cq_notify_flags { - PVRDMA_CQ_SOLICITED = 1 << 0, - PVRDMA_CQ_NEXT_COMP = 1 << 1, - PVRDMA_CQ_SOLICITED_MASK = PVRDMA_CQ_SOLICITED | - PVRDMA_CQ_NEXT_COMP, - PVRDMA_CQ_REPORT_MISSED_EVENTS = 1 << 2, -}; - -struct pvrdma_qp_cap { - uint32_t max_send_wr; - uint32_t max_recv_wr; - uint32_t max_send_sge; - uint32_t max_recv_sge; - uint32_t max_inline_data; - uint32_t reserved; -}; - -enum pvrdma_sig_type { - PVRDMA_SIGNAL_ALL_WR, - PVRDMA_SIGNAL_REQ_WR, -}; - -enum pvrdma_qp_type { - PVRDMA_QPT_SMI, - PVRDMA_QPT_GSI, - PVRDMA_QPT_RC, - PVRDMA_QPT_UC, - PVRDMA_QPT_UD, - PVRDMA_QPT_RAW_IPV6, - PVRDMA_QPT_RAW_ETHERTYPE, - PVRDMA_QPT_RAW_PACKET = 8, - PVRDMA_QPT_XRC_INI = 9, - PVRDMA_QPT_XRC_TGT, - PVRDMA_QPT_MAX, -}; - -enum pvrdma_qp_create_flags { - PVRDMA_QP_CREATE_IPOPVRDMA_UD_LSO = 1 << 0, - PVRDMA_QP_CREATE_BLOCK_MULTICAST_LOOPBACK = 1 << 1, -}; - -enum pvrdma_qp_attr_mask { - PVRDMA_QP_STATE = 1 << 0, - PVRDMA_QP_CUR_STATE = 1 << 1, - PVRDMA_QP_EN_SQD_ASYNC_NOTIFY = 1 << 2, - PVRDMA_QP_ACCESS_FLAGS = 1 << 3, - PVRDMA_QP_PKEY_INDEX = 1 << 4, - PVRDMA_QP_PORT = 1 << 5, - PVRDMA_QP_QKEY = 1 << 6, - PVRDMA_QP_AV = 1 << 7, - PVRDMA_QP_PATH_MTU = 1 << 8, - PVRDMA_QP_TIMEOUT = 1 << 9, - PVRDMA_QP_RETRY_CNT = 1 << 10, - PVRDMA_QP_RNR_RETRY = 1 << 11, - PVRDMA_QP_RQ_PSN = 1 << 12, - PVRDMA_QP_MAX_QP_RD_ATOMIC = 1 << 13, - PVRDMA_QP_ALT_PATH = 1 << 14, - PVRDMA_QP_MIN_RNR_TIMER = 1 << 15, - PVRDMA_QP_SQ_PSN = 1 << 16, - PVRDMA_QP_MAX_DEST_RD_ATOMIC = 1 << 17, - PVRDMA_QP_PATH_MIG_STATE = 1 << 18, - PVRDMA_QP_CAP = 1 << 19, - PVRDMA_QP_DEST_QPN = 1 << 20, - PVRDMA_QP_ATTR_MASK_MAX = PVRDMA_QP_DEST_QPN, -}; - -enum pvrdma_qp_state { - PVRDMA_QPS_RESET, - PVRDMA_QPS_INIT, - PVRDMA_QPS_RTR, - PVRDMA_QPS_RTS, - PVRDMA_QPS_SQD, - PVRDMA_QPS_SQE, - PVRDMA_QPS_ERR, -}; - -enum pvrdma_mig_state { - PVRDMA_MIG_MIGRATED, - PVRDMA_MIG_REARM, - PVRDMA_MIG_ARMED, -}; - -enum pvrdma_mw_type { - PVRDMA_MW_TYPE_1 = 1, - PVRDMA_MW_TYPE_2 = 2, -}; - -struct pvrdma_srq_attr { - uint32_t max_wr; - uint32_t max_sge; - uint32_t srq_limit; - uint32_t reserved; -}; - -struct pvrdma_qp_attr { - enum pvrdma_qp_state qp_state; - enum pvrdma_qp_state cur_qp_state; - enum pvrdma_mtu path_mtu; - enum pvrdma_mig_state path_mig_state; - uint32_t qkey; - uint32_t rq_psn; - uint32_t sq_psn; - uint32_t dest_qp_num; - uint32_t qp_access_flags; - uint16_t pkey_index; - uint16_t alt_pkey_index; - uint8_t en_sqd_async_notify; - uint8_t sq_draining; - uint8_t max_rd_atomic; - uint8_t max_dest_rd_atomic; - uint8_t min_rnr_timer; - uint8_t port_num; - uint8_t timeout; - uint8_t retry_cnt; - uint8_t rnr_retry; - uint8_t alt_port_num; - uint8_t alt_timeout; - uint8_t reserved[5]; - struct pvrdma_qp_cap cap; - struct pvrdma_ah_attr ah_attr; - struct pvrdma_ah_attr alt_ah_attr; -}; - -enum pvrdma_send_flags { - PVRDMA_SEND_FENCE = 1 << 0, - PVRDMA_SEND_SIGNALED = 1 << 1, - PVRDMA_SEND_SOLICITED = 1 << 2, - PVRDMA_SEND_INLINE = 1 << 3, - PVRDMA_SEND_IP_CSUM = 1 << 4, - PVRDMA_SEND_FLAGS_MAX = PVRDMA_SEND_IP_CSUM, -}; - -enum pvrdma_access_flags { - PVRDMA_ACCESS_LOCAL_WRITE = 1 << 0, - PVRDMA_ACCESS_REMOTE_WRITE = 1 << 1, - PVRDMA_ACCESS_REMOTE_READ = 1 << 2, - PVRDMA_ACCESS_REMOTE_ATOMIC = 1 << 3, - PVRDMA_ACCESS_MW_BIND = 1 << 4, - PVRDMA_ZERO_BASED = 1 << 5, - PVRDMA_ACCESS_ON_DEMAND = 1 << 6, - PVRDMA_ACCESS_FLAGS_MAX = PVRDMA_ACCESS_ON_DEMAND, -}; - -#endif /* __PVRDMA_VERBS_H__ */ diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index 48b620cbef..d4a2231306 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. @@ -618,6 +658,74 @@ extern "C" { */ #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) + +/* + * Intel Color Control Surfaces (CCS) for graphics ver. 20 unified compression + * on integrated graphics + * + * 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 compressible GEM objects. + */ +#define I915_FORMAT_MOD_4_TILED_LNL_CCS fourcc_mod_code(INTEL, 16) + +/* + * Intel Color Control Surfaces (CCS) for graphics ver. 20 unified compression + * on discrete graphics + * + * 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 compressible GEM objects. The GEM object must be stored in + * contiguous memory with a size aligned to 64KB + */ +#define I915_FORMAT_MOD_4_TILED_BMG_CCS fourcc_mod_code(INTEL, 17) + /* * Tiled, NV12MT, grouped in 64 (pixels) x 32 (lines) -sized macroblocks * @@ -717,6 +825,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 */ /* @@ -990,7 +1127,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. @@ -1306,7 +1443,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 @@ -1363,6 +1500,7 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) #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 +#define AMD_FMT_MOD_TILE_VER_GFX12 5 /* * 64K_S is the same for GFX9/GFX10/GFX10_RBPLUS and hence has GFX9 as canonical @@ -1373,6 +1511,8 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) /* * 64K_D for non-32 bpp is the same for GFX9/GFX10/GFX10_RBPLUS and hence has * GFX9 as canonical version. + * + * 64K_D_2D on GFX12 is identical to 64K_D on GFX11. */ #define AMD_FMT_MOD_TILE_GFX9_64K_D 10 #define AMD_FMT_MOD_TILE_GFX9_64K_S_X 25 @@ -1380,6 +1520,21 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) #define AMD_FMT_MOD_TILE_GFX9_64K_R_X 27 #define AMD_FMT_MOD_TILE_GFX11_256K_R_X 31 +/* Gfx12 swizzle modes: + * 0 - LINEAR + * 1 - 256B_2D - 2D block dimensions + * 2 - 4KB_2D + * 3 - 64KB_2D + * 4 - 256KB_2D + * 5 - 4KB_3D - 3D block dimensions + * 6 - 64KB_3D + * 7 - 256KB_3D + */ +#define AMD_FMT_MOD_TILE_GFX12_256B_2D 1 +#define AMD_FMT_MOD_TILE_GFX12_4K_2D 2 +#define AMD_FMT_MOD_TILE_GFX12_64K_2D 3 +#define AMD_FMT_MOD_TILE_GFX12_256K_2D 4 + #define AMD_FMT_MOD_DCC_BLOCK_64B 0 #define AMD_FMT_MOD_DCC_BLOCK_128B 1 #define AMD_FMT_MOD_DCC_BLOCK_256B 2 diff --git a/include/standard-headers/linux/const.h b/include/standard-headers/linux/const.h index 5e48987251..2122610de7 100644 --- a/include/standard-headers/linux/const.h +++ b/include/standard-headers/linux/const.h @@ -28,7 +28,24 @@ #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) +#if !defined(__ASSEMBLY__) +/* + * Missing __asm__ support + * + * __BIT128() would not work in the __asm__ code, as it shifts an + * 'unsigned __init128' data type as direct representation of + * 128 bit constants is not supported in the gcc compiler, as + * they get silently truncated. + * + * TODO: Please revisit this implementation when gcc compiler + * starts representing 128 bit constants directly like long + * and unsigned long etc. Subsequently drop the comment for + * GENMASK_U128() which would then start supporting __asm__ code. + */ +#define _BIT128(x) ((unsigned __int128)(1) << (x)) +#endif + +#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 4537da20cc..b05e84825b 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; @@ -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,340 @@ enum ethtool_module_power_mode { ETHTOOL_MODULE_POWER_MODE_HIGH, }; +/** + * enum ethtool_c33_pse_ext_state - groups of PSE extended states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION: Group of error_condition states + * @ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID: Group of mr_mps_valid states + * @ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE: Group of mr_pse_enable states + * @ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED: Group of option_detect_ted + * states + * @ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM: Group of option_vport_lim states + * @ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED: Group of ovld_detected states + * @ETHTOOL_C33_PSE_EXT_STATE_PD_DLL_POWER_TYPE: Group of pd_dll_power_type + * states + * @ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE: Group of power_not_available + * states + * @ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED: Group of short_detected states + */ +enum ethtool_c33_pse_ext_state { + ETHTOOL_C33_PSE_EXT_STATE_ERROR_CONDITION = 1, + ETHTOOL_C33_PSE_EXT_STATE_MR_MPS_VALID, + ETHTOOL_C33_PSE_EXT_STATE_MR_PSE_ENABLE, + ETHTOOL_C33_PSE_EXT_STATE_OPTION_DETECT_TED, + ETHTOOL_C33_PSE_EXT_STATE_OPTION_VPORT_LIM, + ETHTOOL_C33_PSE_EXT_STATE_OVLD_DETECTED, + ETHTOOL_C33_PSE_EXT_STATE_PD_DLL_POWER_TYPE, + ETHTOOL_C33_PSE_EXT_STATE_POWER_NOT_AVAILABLE, + ETHTOOL_C33_PSE_EXT_STATE_SHORT_DETECTED, +}; + +/** + * enum ethtool_c33_pse_ext_substate_mr_mps_valid - mr_mps_valid states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_DETECTED_UNDERLOAD: Underload + * state + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_CONNECTION_OPEN: Port is not + * connected + * + * The PSE monitors either the DC or AC Maintain Power Signature + * (MPS, see 33.2.9.1). This variable indicates the presence or absence of + * a valid MPS. + */ +enum ethtool_c33_pse_ext_substate_mr_mps_valid { + ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_DETECTED_UNDERLOAD = 1, + ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_MPS_VALID_CONNECTION_OPEN, +}; + +/** + * enum ethtool_c33_pse_ext_substate_error_condition - error_condition states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_NON_EXISTING_PORT: Non-existing + * port number + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNDEFINED_PORT: Undefined port + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT: Internal + * hardware fault + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_COMM_ERROR_AFTER_FORCE_ON: + * Communication error after force on + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS: Unknown + * port status + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_TURN_OFF: Host + * crash turn off + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_FORCE_SHUTDOWN: + * Host crash force shutdown + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_CONFIG_CHANGE: Configuration + * change + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP: Over + * temperature detected + * + * error_condition is a variable indicating the status of + * implementation-specific fault conditions or optionally other system faults + * that prevent the PSE from meeting the specifications in Table 33–11 and that + * require the PSE not to source power. These error conditions are different + * from those monitored by the state diagrams in Figure 33–10. + */ +enum ethtool_c33_pse_ext_substate_error_condition { + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_NON_EXISTING_PORT = 1, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNDEFINED_PORT, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_INTERNAL_HW_FAULT, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_COMM_ERROR_AFTER_FORCE_ON, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_UNKNOWN_PORT_STATUS, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_TURN_OFF, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_HOST_CRASH_FORCE_SHUTDOWN, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_CONFIG_CHANGE, + ETHTOOL_C33_PSE_EXT_SUBSTATE_ERROR_CONDITION_DETECTED_OVER_TEMP, +}; + +/** + * enum ethtool_c33_pse_ext_substate_mr_pse_enable - mr_pse_enable states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_PSE_ENABLE_DISABLE_PIN_ACTIVE: Disable + * pin active + * + * mr_pse_enable is control variable that selects PSE operation and test + * functions. + */ +enum ethtool_c33_pse_ext_substate_mr_pse_enable { + ETHTOOL_C33_PSE_EXT_SUBSTATE_MR_PSE_ENABLE_DISABLE_PIN_ACTIVE = 1, +}; + +/** + * enum ethtool_c33_pse_ext_substate_option_detect_ted - option_detect_ted + * states functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_DET_IN_PROCESS: Detection + * in process + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_CONNECTION_CHECK_ERROR: + * Connection check error + * + * option_detect_ted is a variable indicating if detection can be performed + * by the PSE during the ted_timer interval. + */ +enum ethtool_c33_pse_ext_substate_option_detect_ted { + ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_DET_IN_PROCESS = 1, + ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_DETECT_TED_CONNECTION_CHECK_ERROR, +}; + +/** + * enum ethtool_c33_pse_ext_substate_option_vport_lim - option_vport_lim states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_HIGH_VOLTAGE: Main supply + * voltage is high + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_LOW_VOLTAGE: Main supply + * voltage is low + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_VOLTAGE_INJECTION: Voltage + * injection into the port + * + * option_vport_lim is an optional variable indicates if VPSE is out of the + * operating range during normal operating state. + */ +enum ethtool_c33_pse_ext_substate_option_vport_lim { + ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_HIGH_VOLTAGE = 1, + ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_LOW_VOLTAGE, + ETHTOOL_C33_PSE_EXT_SUBSTATE_OPTION_VPORT_LIM_VOLTAGE_INJECTION, +}; + +/** + * enum ethtool_c33_pse_ext_substate_ovld_detected - ovld_detected states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_OVLD_DETECTED_OVERLOAD: Overload state + * + * ovld_detected is a variable indicating if the PSE output current has been + * in an overload condition (see 33.2.7.6) for at least TCUT of a one-second + * sliding time. + */ +enum ethtool_c33_pse_ext_substate_ovld_detected { + ETHTOOL_C33_PSE_EXT_SUBSTATE_OVLD_DETECTED_OVERLOAD = 1, +}; + +/** + * enum ethtool_c33_pse_ext_substate_power_not_available - power_not_available + * states functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_BUDGET_EXCEEDED: Power + * budget exceeded for the controller + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PORT_PW_LIMIT_EXCEEDS_CONTROLLER_BUDGET: + * Configured port power limit exceeded controller power budget + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PD_REQUEST_EXCEEDS_PORT_LIMIT: + * Power request from PD exceeds port limit + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_HW_PW_LIMIT: Power + * denied due to Hardware power limit + * + * power_not_available is a variable that is asserted in an + * implementation-dependent manner when the PSE is no longer capable of + * sourcing sufficient power to support the attached PD. Sufficient power + * is defined by classification; see 33.2.6. + */ +enum ethtool_c33_pse_ext_substate_power_not_available { + ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_BUDGET_EXCEEDED = 1, + ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PORT_PW_LIMIT_EXCEEDS_CONTROLLER_BUDGET, + ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_PD_REQUEST_EXCEEDS_PORT_LIMIT, + ETHTOOL_C33_PSE_EXT_SUBSTATE_POWER_NOT_AVAILABLE_HW_PW_LIMIT, +}; + +/** + * enum ethtool_c33_pse_ext_substate_short_detected - short_detected states + * functions. IEEE 802.3-2022 33.2.4.4 Variables + * + * @ETHTOOL_C33_PSE_EXT_SUBSTATE_SHORT_DETECTED_SHORT_CONDITION: Short + * condition was detected + * + * short_detected is a variable indicating if the PSE output current has been + * in a short circuit condition for TLIM within a sliding window (see 33.2.7.7). + */ +enum ethtool_c33_pse_ext_substate_short_detected { + ETHTOOL_C33_PSE_EXT_SUBSTATE_SHORT_DETECTED_SHORT_CONDITION = 1, +}; + +/** + * enum ethtool_pse_types - Types of PSE controller. + * @ETHTOOL_PSE_UNKNOWN: Type of PSE controller is unknown + * @ETHTOOL_PSE_PODL: PSE controller which support PoDL + * @ETHTOOL_PSE_C33: PSE controller which support Clause 33 (PoE) + */ +enum ethtool_pse_types { + ETHTOOL_PSE_UNKNOWN = 1 << 0, + ETHTOOL_PSE_PODL = 1 << 1, + ETHTOOL_PSE_C33 = 1 << 2, +}; + +/** + * enum ethtool_c33_pse_admin_state - operational state of the PoDL PSE + * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState + * @ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN: state of PSE functions is unknown + * @ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED: PSE functions are disabled + * @ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED: PSE functions are enabled + */ +enum ethtool_c33_pse_admin_state { + ETHTOOL_C33_PSE_ADMIN_STATE_UNKNOWN = 1, + ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED, + ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED, +}; + +/** + * enum ethtool_c33_pse_pw_d_status - power detection status of the PSE. + * IEEE 802.3-2022 30.9.1.1.3 aPoDLPSEPowerDetectionStatus: + * @ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN: PSE status is unknown + * @ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED: The enumeration "disabled" + * indicates that the PSE State diagram is in the state DISABLED. + * @ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING: The enumeration "searching" + * indicates the PSE State diagram is in a state other than those + * listed. + * @ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING: The enumeration + * "deliveringPower" indicates that the PSE State diagram is in the + * state POWER_ON. + * @ETHTOOL_C33_PSE_PW_D_STATUS_TEST: The enumeration "test" indicates that + * the PSE State diagram is in the state TEST_MODE. + * @ETHTOOL_C33_PSE_PW_D_STATUS_FAULT: The enumeration "fault" indicates that + * the PSE State diagram is in the state TEST_ERROR. + * @ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT: The enumeration "otherFault" + * indicates that the PSE State diagram is in the state IDLE due to + * the variable error_condition = true. + */ +enum ethtool_c33_pse_pw_d_status { + ETHTOOL_C33_PSE_PW_D_STATUS_UNKNOWN = 1, + ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED, + ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING, + ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING, + ETHTOOL_C33_PSE_PW_D_STATUS_TEST, + ETHTOOL_C33_PSE_PW_D_STATUS_FAULT, + ETHTOOL_C33_PSE_PW_D_STATUS_OTHERFAULT, +}; + +/** + * 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, +}; + +/** + * enum ethtool_module_fw_flash_status - plug-in module firmware flashing status + * @ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED: The firmware flashing process has + * started. + * @ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS: The firmware flashing process + * is in progress. + * @ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED: The firmware flashing process was + * completed successfully. + * @ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR: The firmware flashing process was + * stopped due to an error. + */ +enum ethtool_module_fw_flash_status { + ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED = 1, + ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS, + ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED, + ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR, +}; + /** * struct ethtool_gstrings - string set for data tagging * @cmd: Command number = %ETHTOOL_GSTRINGS @@ -1140,7 +1490,7 @@ struct ethtool_rxnfc { uint32_t rule_cnt; uint32_t rss_context; }; - uint32_t rule_locs[0]; + uint32_t rule_locs[]; }; @@ -1180,6 +1530,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,7 +1551,8 @@ 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[]; }; @@ -1692,6 +2045,17 @@ enum ethtool_link_mode_bit_indices { 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, + ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT = 102, + /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS }; @@ -1803,6 +2167,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 @@ -1840,6 +2205,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 @@ -1881,6 +2260,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) */ @@ -1900,6 +2288,53 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define IPV4_FLOW 0x10 /* hash only */ #define IPV6_FLOW 0x11 /* hash only */ #define ETHER_FLOW 0x12 /* spec only (ether_spec) */ + +/* Used for GTP-U IPv4 and IPv6. + * The format of GTP packets only includes + * elements such as TEID and GTP version. + * It is primarily intended for data communication of the UE. + */ +#define GTPU_V4_FLOW 0x13 /* hash only */ +#define GTPU_V6_FLOW 0x14 /* hash only */ + +/* Use for GTP-C IPv4 and v6. + * The format of these GTP packets does not include TEID. + * Primarily expected to be used for communication + * to create sessions for UE data communication, + * commonly referred to as CSR (Create Session Request). + */ +#define GTPC_V4_FLOW 0x15 /* hash only */ +#define GTPC_V6_FLOW 0x16 /* hash only */ + +/* Use for GTP-C IPv4 and v6. + * Unlike GTPC_V4_FLOW, the format of these GTP packets includes TEID. + * After session creation, it becomes this packet. + * This is mainly used for requests to realize UE handover. + */ +#define GTPC_TEID_V4_FLOW 0x17 /* hash only */ +#define GTPC_TEID_V6_FLOW 0x18 /* hash only */ + +/* Use for GTP-U and extended headers for the PSC (PDU Session Container). + * The format of these GTP packets includes TEID and QFI. + * In 5G communication using UPF (User Plane Function), + * data communication with this extended header is performed. + */ +#define GTPU_EH_V4_FLOW 0x19 /* hash only */ +#define GTPU_EH_V6_FLOW 0x1a /* hash only */ + +/* Use for GTP-U IPv4 and v6 PSC (PDU Session Container) extended headers. + * This differs from GTPU_EH_V(4|6)_FLOW in that it is distinguished by + * UL/DL included in the PSC. + * There are differences in the data included based on Downlink/Uplink, + * and can be used to distinguish packets. + * The functions described so far are useful when you want to + * handle communication from the mobile network in UPF, PGW, etc. + */ +#define GTPU_UL_V4_FLOW 0x1b /* hash only */ +#define GTPU_UL_V6_FLOW 0x1c /* hash only */ +#define GTPU_DL_V4_FLOW 0x1d /* hash only */ +#define GTPU_DL_V6_FLOW 0x1e /* hash only */ + /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 #define FLOW_MAC_EXT 0x40000000 @@ -1914,6 +2349,7 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define RXH_IP_DST (1 << 5) #define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ #define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_GTP_TEID (1 << 8) /* teid in case of GTP */ #define RXH_DISCARD (1 << 31) #define RX_CLS_FLOW_DISC 0xffffffffffffffffULL @@ -2017,24 +2453,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 @@ -2070,6 +2494,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; @@ -2085,7 +2524,7 @@ 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[]; /* layout of link_mode_masks fields: @@ -2094,4 +2533,20 @@ struct ethtool_link_settings { * uint32_t map_lp_advertising[link_mode_masks_nwords]; */ }; + +/** + * enum phy_upstream - Represents the upstream component a given PHY device + * is connected to, as in what is on the other end of the MII bus. Most PHYs + * will be attached to an Ethernet MAC controller, but in some cases, there's + * an intermediate PHY used as a media-converter, which will driver another + * MII interface as its output. + * @PHY_UPSTREAM_MAC: Upstream component is a MAC (a switch port, + * or ethernet controller) + * @PHY_UPSTREAM_PHY: Upstream component is a PHY (likely a media converter) + */ +enum phy_upstream { + PHY_UPSTREAM_MAC, + PHY_UPSTREAM_PHY, +}; + #endif /* _LINUX_ETHTOOL_H */ diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h index bda06258be..889e12ad15 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -194,6 +194,32 @@ * - 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 + * + * 7.40 + * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag + * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag + * - add FUSE_NO_EXPORT_SUPPORT init flag + * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag + * + * 7.41 + * - add FUSE_ALLOW_IDMAP */ #ifndef _LINUX_FUSE_H @@ -225,7 +251,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 36 +#define FUSE_KERNEL_MINOR_VERSION 41 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -252,6 +278,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 +357,8 @@ 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 + * FOPEN_PASSTHROUGH: passthrough read/write io for this open file */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) @@ -304,6 +366,8 @@ 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) +#define FOPEN_PASSTHROUGH (1 << 7) /** * INIT request/reply flags @@ -349,6 +413,14 @@ 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. + * FUSE_NO_EXPORT_SUPPORT: explicitly disable export support + * FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit + * of the request ID indicates resend requests + * FUSE_ALLOW_IDMAP: allow creation of idmapped mounts */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -385,6 +457,16 @@ 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) +#define FUSE_PASSTHROUGH (1ULL << 37) +#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38) +#define FUSE_HAS_RESEND (1ULL << 39) + +/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ +#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP +#define FUSE_ALLOW_IDMAP (1ULL << 40) /** * CUSE INIT request/reply flags @@ -484,6 +566,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 +632,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, @@ -549,6 +650,7 @@ enum fuse_notify_code { FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_CODE_MAX, }; @@ -597,6 +699,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 { @@ -659,7 +777,7 @@ struct fuse_create_in { struct fuse_open_out { uint64_t fh; uint32_t open_flags; - uint32_t padding; + int32_t backing_id; }; struct fuse_release_in { @@ -775,7 +893,8 @@ struct fuse_init_out { uint16_t max_pages; uint16_t map_alignment; uint32_t flags2; - uint32_t unused[7]; + uint32_t max_stack_depth; + uint32_t unused[6]; }; #define CUSE_INIT_INFO_MAX 4096 @@ -858,6 +977,29 @@ struct fuse_fallocate_in { uint32_t padding; }; +/** + * FUSE request unique ID flag + * + * Indicates whether this is a resend request. The receiver should handle this + * request accordingly. + */ +#define FUSE_UNIQUE_RESEND (1ULL << 63) + +/** + * This value will be set by the kernel to + * (struct fuse_in_header).{uid,gid} fields in + * case when: + * - fuse daemon enabled FUSE_ALLOW_IDMAP + * - idmapping information is not available and uid/gid + * can not be mapped in accordance with an idmapping. + * + * Note: an idmapping information always available + * for inode creation operations like: + * FUSE_MKNOD, FUSE_SYMLINK, FUSE_MKDIR, FUSE_TMPFILE, + * FUSE_CREATE and FUSE_RENAME2 (with RENAME_WHITEOUT). + */ +#define FUSE_INVALID_UIDGID ((uint32_t)(-1)) + struct fuse_in_header { uint32_t len; uint32_t opcode; @@ -866,7 +1008,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 +1054,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 { @@ -946,9 +1089,18 @@ struct fuse_notify_retrieve_in { uint64_t dummy4; }; +struct fuse_backing_map { + int32_t fd; + uint32_t flags; + uint64_t padding; +}; + /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) +#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \ + struct fuse_backing_map) +#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t) struct fuse_lseek_in { uint64_t fh; @@ -1027,4 +1179,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..50b2b7497e 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -602,6 +602,7 @@ #define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ #define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ +#define KEY_REFRESH_RATE_TOGGLE 0x232 /* Display refresh rate toggle */ #define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ #define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ @@ -614,6 +615,11 @@ #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_ACCESSIBILITY 0x24e /* Toggles the system bound accessibility UI/command (HUTRR116) */ +#define KEY_DO_NOT_DISTURB 0x24f /* Toggles the system-wide "Do Not Disturb" control (HUTRR94)*/ #define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ #define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ @@ -862,6 +868,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/kvm_para.h b/include/standard-headers/linux/kvm_para.h new file mode 100644 index 0000000000..015c166302 --- /dev/null +++ b/include/standard-headers/linux/kvm_para.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_KVM_PARA_H +#define __LINUX_KVM_PARA_H + +/* + * This header file provides a method for making a hypercall to the host + * Architectures should define: + * - kvm_hypercall0, kvm_hypercall1... + * - kvm_arch_para_features + * - kvm_para_available + */ + +/* Return values for hypercalls */ +#define KVM_ENOSYS 1000 +#define KVM_EFAULT EFAULT +#define KVM_EINVAL EINVAL +#define KVM_E2BIG E2BIG +#define KVM_EPERM EPERM +#define KVM_EOPNOTSUPP 95 + +#define KVM_HC_VAPIC_POLL_IRQ 1 +#define KVM_HC_MMU_OP 2 +#define KVM_HC_FEATURES 3 +#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 +#define KVM_HC_KICK_CPU 5 +#define KVM_HC_MIPS_GET_CLOCK_FREQ 6 +#define KVM_HC_MIPS_EXIT_VM 7 +#define KVM_HC_MIPS_CONSOLE_OUTPUT 8 +#define KVM_HC_CLOCK_PAIRING 9 +#define KVM_HC_SEND_IPI 10 +#define KVM_HC_SCHED_YIELD 11 +#define KVM_HC_MAP_GPA_RANGE 12 + +/* + * hypercalls use architecture specific + */ + +#endif /* __LINUX_KVM_PARA_H */ diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 57b8e2ffb1..12323b3334 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 */ @@ -633,10 +634,13 @@ #define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */ #define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */ #define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */ -#define PCI_EXP_RTCTL_CRSSVE 0x0010 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCTL_RRS_SVE 0x0010 /* Config RRS Software Visibility Enable */ +#define PCI_EXP_RTCTL_CRSSVE PCI_EXP_RTCTL_RRS_SVE /* compatibility */ #define PCI_EXP_RTCAP 0x1e /* Root Capabilities */ -#define PCI_EXP_RTCAP_CRSVIS 0x0001 /* CRS Software Visibility capability */ +#define PCI_EXP_RTCAP_RRS_SV 0x0001 /* Config RRS Software Visibility */ +#define PCI_EXP_RTCAP_CRSVIS PCI_EXP_RTCAP_RRS_SV /* compatibility */ #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 */ /* @@ -693,6 +697,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 */ @@ -737,6 +742,8 @@ #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_NPEM 0x29 /* Native PCIe Enclosure Management */ +#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 @@ -928,12 +935,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 */ @@ -973,6 +981,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 */ @@ -1040,9 +1050,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 */ @@ -1058,6 +1075,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 */ @@ -1085,6 +1103,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 */ @@ -1104,6 +1124,40 @@ #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0 #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4 +/* Native PCIe Enclosure Management */ +#define PCI_NPEM_CAP 0x04 /* NPEM capability register */ +#define PCI_NPEM_CAP_CAPABLE 0x00000001 /* NPEM Capable */ + +#define PCI_NPEM_CTRL 0x08 /* NPEM control register */ +#define PCI_NPEM_CTRL_ENABLE 0x00000001 /* NPEM Enable */ + +/* + * Native PCIe Enclosure Management indication bits and Reset command bit + * are corresponding for capability and control registers. + */ +#define PCI_NPEM_CMD_RESET 0x00000002 /* Reset Command */ +#define PCI_NPEM_IND_OK 0x00000004 /* OK */ +#define PCI_NPEM_IND_LOCATE 0x00000008 /* Locate */ +#define PCI_NPEM_IND_FAIL 0x00000010 /* Fail */ +#define PCI_NPEM_IND_REBUILD 0x00000020 /* Rebuild */ +#define PCI_NPEM_IND_PFA 0x00000040 /* Predicted Failure Analysis */ +#define PCI_NPEM_IND_HOTSPARE 0x00000080 /* Hot Spare */ +#define PCI_NPEM_IND_ICA 0x00000100 /* In Critical Array */ +#define PCI_NPEM_IND_IFA 0x00000200 /* In Failed Array */ +#define PCI_NPEM_IND_IDT 0x00000400 /* Device Type */ +#define PCI_NPEM_IND_DISABLED 0x00000800 /* Disabled */ +#define PCI_NPEM_IND_SPEC_0 0x01000000 +#define PCI_NPEM_IND_SPEC_1 0x02000000 +#define PCI_NPEM_IND_SPEC_2 0x04000000 +#define PCI_NPEM_IND_SPEC_3 0x08000000 +#define PCI_NPEM_IND_SPEC_4 0x10000000 +#define PCI_NPEM_IND_SPEC_5 0x20000000 +#define PCI_NPEM_IND_SPEC_6 0x40000000 +#define PCI_NPEM_IND_SPEC_7 0x80000000 + +#define PCI_NPEM_STATUS 0x0c /* NPEM status register */ +#define PCI_NPEM_STATUS_CC 0x00000001 /* Command Completed */ + /* Data Object Exchange */ #define PCI_DOE_CAP 0x04 /* DOE Capabilities Register */ #define PCI_DOE_CAP_INT_SUP 0x00000001 /* Interrupt Support */ @@ -1119,6 +1173,7 @@ #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 @@ -1126,8 +1181,14 @@ #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_REQ_3_VER 0x0000ff00 #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 +/* Compute Express Link (CXL r3.1, sec 8.1.5) */ +#define PCI_DVSEC_CXL_PORT 3 +#define PCI_DVSEC_CXL_PORT_CTL 0x0c +#define PCI_DVSEC_CXL_PORT_CTL_UNMASK_SBR 0x00000001 + #endif /* LINUX_PCI_REGS_H */ diff --git a/include/standard-headers/linux/pvpanic.h b/include/standard-headers/linux/pvpanic.h deleted file mode 100644 index 54b7485390..0000000000 --- a/include/standard-headers/linux/pvpanic.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ - -#ifndef __PVPANIC_H__ -#define __PVPANIC_H__ - -#define PVPANIC_PANICKED (1 << 0) -#define PVPANIC_CRASH_LOADED (1 << 1) - -#endif /* __PVPANIC_H__ */ diff --git a/include/standard-headers/linux/vhost_types.h b/include/standard-headers/linux/vhost_types.h index c41a73fe36..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; @@ -163,5 +179,18 @@ struct vhost_vdpa_iova_range { #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_balloon.h b/include/standard-headers/linux/virtio_balloon.h index f343bfefd8..3121cd2e0e 100644 --- a/include/standard-headers/linux/virtio_balloon.h +++ b/include/standard-headers/linux/virtio_balloon.h @@ -71,7 +71,13 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */ #define VIRTIO_BALLOON_S_HTLB_PGALLOC 8 /* Hugetlb page allocations */ #define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ -#define VIRTIO_BALLOON_S_NR 10 +#define VIRTIO_BALLOON_S_OOM_KILL 10 /* OOM killer invocations */ +#define VIRTIO_BALLOON_S_ALLOC_STALL 11 /* Stall count of memory allocatoin */ +#define VIRTIO_BALLOON_S_ASYNC_SCAN 12 /* Amount of memory scanned asynchronously */ +#define VIRTIO_BALLOON_S_DIRECT_SCAN 13 /* Amount of memory scanned directly */ +#define VIRTIO_BALLOON_S_ASYNC_RECLAIM 14 /* Amount of memory reclaimed asynchronously */ +#define VIRTIO_BALLOON_S_DIRECT_RECLAIM 15 /* Amount of memory reclaimed directly */ +#define VIRTIO_BALLOON_S_NR 16 #define VIRTIO_BALLOON_S_NAMES_WITH_PREFIX(VIRTIO_BALLOON_S_NAMES_prefix) { \ VIRTIO_BALLOON_S_NAMES_prefix "swap-in", \ @@ -83,7 +89,13 @@ struct virtio_balloon_config { VIRTIO_BALLOON_S_NAMES_prefix "available-memory", \ VIRTIO_BALLOON_S_NAMES_prefix "disk-caches", \ VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-allocations", \ - VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures" \ + VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures", \ + VIRTIO_BALLOON_S_NAMES_prefix "oom-kills", \ + VIRTIO_BALLOON_S_NAMES_prefix "alloc-stalls", \ + VIRTIO_BALLOON_S_NAMES_prefix "async-scans", \ + VIRTIO_BALLOON_S_NAMES_prefix "direct-scans", \ + VIRTIO_BALLOON_S_NAMES_prefix "async-reclaims", \ + VIRTIO_BALLOON_S_NAMES_prefix "direct-reclaims" \ } #define VIRTIO_BALLOON_S_NAMES VIRTIO_BALLOON_S_NAMES_WITH_PREFIX("") 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..6f0dee7e32 100644 --- a/include/standard-headers/linux/virtio_bt.h +++ b/include/standard-headers/linux/virtio_bt.h @@ -9,10 +9,10 @@ #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, - VIRTIO_BT_CONFIG_TYPE_AMP = 1, }; enum virtio_bt_config_vendor { @@ -28,4 +28,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 965ee6ae23..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 41 +#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 @@ -97,8 +97,25 @@ */ #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_gpu.h b/include/standard-headers/linux/virtio_gpu.h index 2da48d3d4c..6459fdb9fb 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -309,6 +309,9 @@ struct virtio_gpu_cmd_submit { #define VIRTIO_GPU_CAPSET_VIRGL 1 #define VIRTIO_GPU_CAPSET_VIRGL2 2 +/* 3 is reserved for gfxstream */ +#define VIRTIO_GPU_CAPSET_VENUS 4 +#define VIRTIO_GPU_CAPSET_DRM 6 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ struct virtio_gpu_get_capset_info { diff --git a/include/standard-headers/linux/virtio_mem.h b/include/standard-headers/linux/virtio_mem.h index 18c74c527c..6bfa41bd8b 100644 --- a/include/standard-headers/linux/virtio_mem.h +++ b/include/standard-headers/linux/virtio_mem.h @@ -90,6 +90,8 @@ #define VIRTIO_MEM_F_ACPI_PXM 0 /* unplugged memory must not be accessed */ #define VIRTIO_MEM_F_UNPLUGGED_INACCESSIBLE 1 +/* plugged memory will remain plugged when suspending+resuming */ +#define VIRTIO_MEM_F_PERSISTENT_SUSPEND 2 /* --- virtio-mem: guest -> host requests --- */ diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index 42c68caf71..fc594fe5fc 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -56,8 +56,14 @@ #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_DEVICE_STATS 50 /* Device can provide device-level statistics. */ +#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 +136,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 */ @@ -386,5 +393,160 @@ struct virtio_net_ctrl_coal_rx { }; #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; +}; + +/* + * Device Statistics + */ +#define VIRTIO_NET_CTRL_STATS 8 +#define VIRTIO_NET_CTRL_STATS_QUERY 0 +#define VIRTIO_NET_CTRL_STATS_GET 1 + +struct virtio_net_stats_capabilities { + +#define VIRTIO_NET_STATS_TYPE_CVQ (1ULL << 32) + +#define VIRTIO_NET_STATS_TYPE_RX_BASIC (1ULL << 0) +#define VIRTIO_NET_STATS_TYPE_RX_CSUM (1ULL << 1) +#define VIRTIO_NET_STATS_TYPE_RX_GSO (1ULL << 2) +#define VIRTIO_NET_STATS_TYPE_RX_SPEED (1ULL << 3) + +#define VIRTIO_NET_STATS_TYPE_TX_BASIC (1ULL << 16) +#define VIRTIO_NET_STATS_TYPE_TX_CSUM (1ULL << 17) +#define VIRTIO_NET_STATS_TYPE_TX_GSO (1ULL << 18) +#define VIRTIO_NET_STATS_TYPE_TX_SPEED (1ULL << 19) + + uint64_t supported_stats_types[1]; +}; + +struct virtio_net_ctrl_queue_stats { + struct { + uint16_t vq_index; + uint16_t reserved[3]; + uint64_t types_bitmap[1]; + } stats[1]; +}; + +struct virtio_net_stats_reply_hdr { +#define VIRTIO_NET_STATS_TYPE_REPLY_CVQ 32 + +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_BASIC 0 +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_CSUM 1 +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_GSO 2 +#define VIRTIO_NET_STATS_TYPE_REPLY_RX_SPEED 3 + +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_BASIC 16 +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_CSUM 17 +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_GSO 18 +#define VIRTIO_NET_STATS_TYPE_REPLY_TX_SPEED 19 + uint8_t type; + uint8_t reserved; + uint16_t vq_index; + uint16_t reserved1; + uint16_t size; +}; + +struct virtio_net_stats_cvq { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t command_num; + uint64_t ok_num; +}; + +struct virtio_net_stats_rx_basic { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t rx_notifications; + + uint64_t rx_packets; + uint64_t rx_bytes; + + uint64_t rx_interrupts; + + uint64_t rx_drops; + uint64_t rx_drop_overruns; +}; + +struct virtio_net_stats_tx_basic { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t tx_notifications; + + uint64_t tx_packets; + uint64_t tx_bytes; + + uint64_t tx_interrupts; + + uint64_t tx_drops; + uint64_t tx_drop_malformed; +}; + +struct virtio_net_stats_rx_csum { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t rx_csum_valid; + uint64_t rx_needs_csum; + uint64_t rx_csum_none; + uint64_t rx_csum_bad; +}; + +struct virtio_net_stats_tx_csum { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t tx_csum_none; + uint64_t tx_needs_csum; +}; + +struct virtio_net_stats_rx_gso { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t rx_gso_packets; + uint64_t rx_gso_bytes; + uint64_t rx_gso_packets_coalesced; + uint64_t rx_gso_bytes_coalesced; +}; + +struct virtio_net_stats_tx_gso { + struct virtio_net_stats_reply_hdr hdr; + + uint64_t tx_gso_packets; + uint64_t tx_gso_bytes; + uint64_t tx_gso_segments; + uint64_t tx_gso_segments_bytes; + uint64_t tx_gso_packets_noseg; + uint64_t tx_gso_bytes_noseg; +}; + +struct virtio_net_stats_rx_speed { + struct virtio_net_stats_reply_hdr hdr; + + /* rx_{packets,bytes}_allowance_exceeded are too long. So rename to + * short name. + */ + uint64_t rx_ratelimit_packets; + uint64_t rx_ratelimit_bytes; +}; + +struct virtio_net_stats_tx_speed { + struct virtio_net_stats_reply_hdr hdr; + + /* tx_{packets,bytes}_allowance_exceeded are too long. So rename to + * short name. + */ + uint64_t tx_ratelimit_packets; + uint64_t tx_ratelimit_bytes; +}; #endif /* _LINUX_VIRTIO_NET_H */ diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h index be912cfc95..4010216103 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; @@ -204,7 +218,72 @@ struct virtio_pci_cfg_cap { #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 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 virtio_admin_cmd_status { + uint16_t status; + uint16_t status_qualifier; + /* Unused, reserved for future extensions. */ + uint8_t reserved2[4]; +}; + +struct 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 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 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_snd.h b/include/standard-headers/linux/virtio_snd.h index 1af96b9fc6..860f12e0a4 100644 --- a/include/standard-headers/linux/virtio_snd.h +++ b/include/standard-headers/linux/virtio_snd.h @@ -7,6 +7,14 @@ #include "standard-headers/linux/virtio_types.h" +/******************************************************************************* + * FEATURE BITS + */ +enum { + /* device supports control elements */ + VIRTIO_SND_F_CTLS = 0 +}; + /******************************************************************************* * CONFIGURATION SPACE */ @@ -17,6 +25,8 @@ struct virtio_snd_config { uint32_t streams; /* # of available channel maps */ uint32_t chmaps; + /* # of available control elements */ + uint32_t controls; }; enum { @@ -55,6 +65,15 @@ enum { /* channel map control request types */ VIRTIO_SND_R_CHMAP_INFO = 0x0200, + /* control element request types */ + VIRTIO_SND_R_CTL_INFO = 0x0300, + VIRTIO_SND_R_CTL_ENUM_ITEMS, + VIRTIO_SND_R_CTL_READ, + VIRTIO_SND_R_CTL_WRITE, + VIRTIO_SND_R_CTL_TLV_READ, + VIRTIO_SND_R_CTL_TLV_WRITE, + VIRTIO_SND_R_CTL_TLV_COMMAND, + /* jack event types */ VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, VIRTIO_SND_EVT_JACK_DISCONNECTED, @@ -63,6 +82,9 @@ enum { VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, VIRTIO_SND_EVT_PCM_XRUN, + /* control element event types */ + VIRTIO_SND_EVT_CTL_NOTIFY = 0x1200, + /* common status codes */ VIRTIO_SND_S_OK = 0x8000, VIRTIO_SND_S_BAD_MSG, @@ -331,4 +353,136 @@ struct virtio_snd_chmap_info { uint8_t positions[VIRTIO_SND_CHMAP_MAX_SIZE]; }; +/******************************************************************************* + * CONTROL ELEMENTS MESSAGES + */ +struct virtio_snd_ctl_hdr { + /* VIRTIO_SND_R_CTL_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + uint32_t control_id; +}; + +/* supported roles for control elements */ +enum { + VIRTIO_SND_CTL_ROLE_UNDEFINED = 0, + VIRTIO_SND_CTL_ROLE_VOLUME, + VIRTIO_SND_CTL_ROLE_MUTE, + VIRTIO_SND_CTL_ROLE_GAIN +}; + +/* supported value types for control elements */ +enum { + VIRTIO_SND_CTL_TYPE_BOOLEAN = 0, + VIRTIO_SND_CTL_TYPE_INTEGER, + VIRTIO_SND_CTL_TYPE_INTEGER64, + VIRTIO_SND_CTL_TYPE_ENUMERATED, + VIRTIO_SND_CTL_TYPE_BYTES, + VIRTIO_SND_CTL_TYPE_IEC958 +}; + +/* supported access rights for control elements */ +enum { + VIRTIO_SND_CTL_ACCESS_READ = 0, + VIRTIO_SND_CTL_ACCESS_WRITE, + VIRTIO_SND_CTL_ACCESS_VOLATILE, + VIRTIO_SND_CTL_ACCESS_INACTIVE, + VIRTIO_SND_CTL_ACCESS_TLV_READ, + VIRTIO_SND_CTL_ACCESS_TLV_WRITE, + VIRTIO_SND_CTL_ACCESS_TLV_COMMAND +}; + +struct virtio_snd_ctl_info { + /* common header */ + struct virtio_snd_info hdr; + /* element role (VIRTIO_SND_CTL_ROLE_XXX) */ + uint32_t role; + /* element value type (VIRTIO_SND_CTL_TYPE_XXX) */ + uint32_t type; + /* element access right bit map (1 << VIRTIO_SND_CTL_ACCESS_XXX) */ + uint32_t access; + /* # of members in the element value */ + uint32_t count; + /* index for an element with a non-unique name */ + uint32_t index; + /* name identifier string for the element */ + uint8_t name[44]; + /* additional information about the element's value */ + union { + /* VIRTIO_SND_CTL_TYPE_INTEGER */ + struct { + /* minimum supported value */ + uint32_t min; + /* maximum supported value */ + uint32_t max; + /* fixed step size for value (0 = variable size) */ + uint32_t step; + } integer; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 */ + struct { + /* minimum supported value */ + uint64_t min; + /* maximum supported value */ + uint64_t max; + /* fixed step size for value (0 = variable size) */ + uint64_t step; + } integer64; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED */ + struct { + /* # of options supported for value */ + uint32_t items; + } enumerated; + } value; +}; + +struct virtio_snd_ctl_enum_item { + /* option name */ + uint8_t item[64]; +}; + +struct virtio_snd_ctl_iec958 { + /* AES/IEC958 channel status bits */ + uint8_t status[24]; + /* AES/IEC958 subcode bits */ + uint8_t subcode[147]; + /* nothing */ + uint8_t pad; + /* AES/IEC958 subframe bits */ + uint8_t dig_subframe[4]; +}; + +struct virtio_snd_ctl_value { + union { + /* VIRTIO_SND_CTL_TYPE_BOOLEAN|INTEGER value */ + uint32_t integer[128]; + /* VIRTIO_SND_CTL_TYPE_INTEGER64 value */ + uint64_t integer64[64]; + /* VIRTIO_SND_CTL_TYPE_ENUMERATED value (option indexes) */ + uint32_t enumerated[128]; + /* VIRTIO_SND_CTL_TYPE_BYTES value */ + uint8_t bytes[512]; + /* VIRTIO_SND_CTL_TYPE_IEC958 value */ + struct virtio_snd_ctl_iec958 iec958; + } value; +}; + +/* supported event reason types */ +enum { + /* element's value has changed */ + VIRTIO_SND_CTL_EVT_MASK_VALUE = 0, + /* element's information has changed */ + VIRTIO_SND_CTL_EVT_MASK_INFO, + /* element's metadata has changed */ + VIRTIO_SND_CTL_EVT_MASK_TLV +}; + +struct virtio_snd_ctl_event { + /* VIRTIO_SND_EVT_CTL_NOTIFY */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::controls - 1 */ + uint16_t control_id; + /* event reason bit map (1 << VIRTIO_SND_CTL_EVT_MASK_XXX) */ + uint16_t mask; +}; + #endif /* VIRTIO_SND_IF_H */ diff --git a/include/standard-headers/misc/pvpanic.h b/include/standard-headers/misc/pvpanic.h new file mode 100644 index 0000000000..b115094431 --- /dev/null +++ b/include/standard-headers/misc/pvpanic.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#ifndef __PVPANIC_H__ +#define __PVPANIC_H__ + +#include "standard-headers/linux/const.h" + +#define PVPANIC_PANICKED _BITUL(0) +#define PVPANIC_CRASH_LOADED _BITUL(1) +#define PVPANIC_SHUTDOWN _BITUL(2) + +#endif /* __PVPANIC_H__ */ diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h deleted file mode 100644 index c30182a7ae..0000000000 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ /dev/null @@ -1,310 +0,0 @@ -/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ -/* - * Copyright (c) 2012-2016 VMware, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of EITHER the GNU General Public License - * version 2 as published by the Free Software Foundation or the BSD - * 2-Clause License. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License version 2 for more details at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html. - * - * You should have received a copy of the GNU General Public License - * along with this program available in the file COPYING in the main - * directory of this source tree. - * - * The BSD 2-Clause License - * - * 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. - * - * 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 HOLDER 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 __VMW_PVRDMA_ABI_H__ -#define __VMW_PVRDMA_ABI_H__ - -#include "standard-headers/linux/types.h" - -#define PVRDMA_UVERBS_ABI_VERSION 3 /* ABI Version. */ -#define PVRDMA_UAR_HANDLE_MASK 0x00FFFFFF /* Bottom 24 bits. */ -#define PVRDMA_UAR_QP_OFFSET 0 /* QP doorbell. */ -#define PVRDMA_UAR_QP_SEND (1 << 30) /* Send bit. */ -#define PVRDMA_UAR_QP_RECV (1 << 31) /* Recv bit. */ -#define PVRDMA_UAR_CQ_OFFSET 4 /* CQ doorbell. */ -#define PVRDMA_UAR_CQ_ARM_SOL (1 << 29) /* Arm solicited bit. */ -#define PVRDMA_UAR_CQ_ARM (1 << 30) /* Arm bit. */ -#define PVRDMA_UAR_CQ_POLL (1 << 31) /* Poll bit. */ -#define PVRDMA_UAR_SRQ_OFFSET 8 /* SRQ doorbell. */ -#define PVRDMA_UAR_SRQ_RECV (1 << 30) /* Recv bit. */ - -enum pvrdma_wr_opcode { - PVRDMA_WR_RDMA_WRITE, - PVRDMA_WR_RDMA_WRITE_WITH_IMM, - PVRDMA_WR_SEND, - PVRDMA_WR_SEND_WITH_IMM, - PVRDMA_WR_RDMA_READ, - PVRDMA_WR_ATOMIC_CMP_AND_SWP, - PVRDMA_WR_ATOMIC_FETCH_AND_ADD, - PVRDMA_WR_LSO, - PVRDMA_WR_SEND_WITH_INV, - PVRDMA_WR_RDMA_READ_WITH_INV, - PVRDMA_WR_LOCAL_INV, - PVRDMA_WR_FAST_REG_MR, - PVRDMA_WR_MASKED_ATOMIC_CMP_AND_SWP, - PVRDMA_WR_MASKED_ATOMIC_FETCH_AND_ADD, - PVRDMA_WR_BIND_MW, - PVRDMA_WR_REG_SIG_MR, - PVRDMA_WR_ERROR, -}; - -enum pvrdma_wc_status { - PVRDMA_WC_SUCCESS, - PVRDMA_WC_LOC_LEN_ERR, - PVRDMA_WC_LOC_QP_OP_ERR, - PVRDMA_WC_LOC_EEC_OP_ERR, - PVRDMA_WC_LOC_PROT_ERR, - PVRDMA_WC_WR_FLUSH_ERR, - PVRDMA_WC_MW_BIND_ERR, - PVRDMA_WC_BAD_RESP_ERR, - PVRDMA_WC_LOC_ACCESS_ERR, - PVRDMA_WC_REM_INV_REQ_ERR, - PVRDMA_WC_REM_ACCESS_ERR, - PVRDMA_WC_REM_OP_ERR, - PVRDMA_WC_RETRY_EXC_ERR, - PVRDMA_WC_RNR_RETRY_EXC_ERR, - PVRDMA_WC_LOC_RDD_VIOL_ERR, - PVRDMA_WC_REM_INV_RD_REQ_ERR, - PVRDMA_WC_REM_ABORT_ERR, - PVRDMA_WC_INV_EECN_ERR, - PVRDMA_WC_INV_EEC_STATE_ERR, - PVRDMA_WC_FATAL_ERR, - PVRDMA_WC_RESP_TIMEOUT_ERR, - PVRDMA_WC_GENERAL_ERR, -}; - -enum pvrdma_wc_opcode { - PVRDMA_WC_SEND, - PVRDMA_WC_RDMA_WRITE, - PVRDMA_WC_RDMA_READ, - PVRDMA_WC_COMP_SWAP, - PVRDMA_WC_FETCH_ADD, - PVRDMA_WC_BIND_MW, - PVRDMA_WC_LSO, - PVRDMA_WC_LOCAL_INV, - PVRDMA_WC_FAST_REG_MR, - PVRDMA_WC_MASKED_COMP_SWAP, - PVRDMA_WC_MASKED_FETCH_ADD, - PVRDMA_WC_RECV = 1 << 7, - PVRDMA_WC_RECV_RDMA_WITH_IMM, -}; - -enum pvrdma_wc_flags { - PVRDMA_WC_GRH = 1 << 0, - PVRDMA_WC_WITH_IMM = 1 << 1, - PVRDMA_WC_WITH_INVALIDATE = 1 << 2, - PVRDMA_WC_IP_CSUM_OK = 1 << 3, - PVRDMA_WC_WITH_SMAC = 1 << 4, - PVRDMA_WC_WITH_VLAN = 1 << 5, - PVRDMA_WC_WITH_NETWORK_HDR_TYPE = 1 << 6, - PVRDMA_WC_FLAGS_MAX = PVRDMA_WC_WITH_NETWORK_HDR_TYPE, -}; - -enum pvrdma_network_type { - PVRDMA_NETWORK_IB, - PVRDMA_NETWORK_ROCE_V1 = PVRDMA_NETWORK_IB, - PVRDMA_NETWORK_IPV4, - PVRDMA_NETWORK_IPV6 -}; - -struct pvrdma_alloc_ucontext_resp { - uint32_t qp_tab_size; - uint32_t reserved; -}; - -struct pvrdma_alloc_pd_resp { - uint32_t pdn; - uint32_t reserved; -}; - -struct pvrdma_create_cq { - uint64_t __attribute__((aligned(8))) buf_addr; - uint32_t buf_size; - uint32_t reserved; -}; - -struct pvrdma_create_cq_resp { - uint32_t cqn; - uint32_t reserved; -}; - -struct pvrdma_resize_cq { - uint64_t __attribute__((aligned(8))) buf_addr; - uint32_t buf_size; - uint32_t reserved; -}; - -struct pvrdma_create_srq { - uint64_t __attribute__((aligned(8))) buf_addr; - uint32_t buf_size; - uint32_t reserved; -}; - -struct pvrdma_create_srq_resp { - uint32_t srqn; - uint32_t reserved; -}; - -struct pvrdma_create_qp { - uint64_t __attribute__((aligned(8))) rbuf_addr; - uint64_t __attribute__((aligned(8))) sbuf_addr; - uint32_t rbuf_size; - uint32_t sbuf_size; - uint64_t __attribute__((aligned(8))) qp_addr; -}; - -struct pvrdma_create_qp_resp { - uint32_t qpn; - uint32_t qp_handle; -}; - -/* PVRDMA masked atomic compare and swap */ -struct pvrdma_ex_cmp_swap { - uint64_t __attribute__((aligned(8))) swap_val; - uint64_t __attribute__((aligned(8))) compare_val; - uint64_t __attribute__((aligned(8))) swap_mask; - uint64_t __attribute__((aligned(8))) compare_mask; -}; - -/* PVRDMA masked atomic fetch and add */ -struct pvrdma_ex_fetch_add { - uint64_t __attribute__((aligned(8))) add_val; - uint64_t __attribute__((aligned(8))) field_boundary; -}; - -/* PVRDMA address vector. */ -struct pvrdma_av { - uint32_t port_pd; - uint32_t sl_tclass_flowlabel; - uint8_t dgid[16]; - uint8_t src_path_bits; - uint8_t gid_index; - uint8_t stat_rate; - uint8_t hop_limit; - uint8_t dmac[6]; - uint8_t reserved[6]; -}; - -/* PVRDMA scatter/gather entry */ -struct pvrdma_sge { - uint64_t __attribute__((aligned(8))) addr; - uint32_t length; - uint32_t lkey; -}; - -/* PVRDMA receive queue work request */ -struct pvrdma_rq_wqe_hdr { - uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ - uint32_t num_sge; /* size of s/g array */ - uint32_t total_len; /* reserved */ -}; -/* Use pvrdma_sge (ib_sge) for receive queue s/g array elements. */ - -/* PVRDMA send queue work request */ -struct pvrdma_sq_wqe_hdr { - uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ - uint32_t num_sge; /* size of s/g array */ - uint32_t total_len; /* reserved */ - uint32_t opcode; /* operation type */ - uint32_t send_flags; /* wr flags */ - union { - uint32_t imm_data; - uint32_t invalidate_rkey; - } ex; - uint32_t reserved; - union { - struct { - uint64_t __attribute__((aligned(8))) remote_addr; - uint32_t rkey; - uint8_t reserved[4]; - } rdma; - struct { - uint64_t __attribute__((aligned(8))) remote_addr; - uint64_t __attribute__((aligned(8))) compare_add; - uint64_t __attribute__((aligned(8))) swap; - uint32_t rkey; - uint32_t reserved; - } atomic; - struct { - uint64_t __attribute__((aligned(8))) remote_addr; - uint32_t log_arg_sz; - uint32_t rkey; - union { - struct pvrdma_ex_cmp_swap cmp_swap; - struct pvrdma_ex_fetch_add fetch_add; - } wr_data; - } masked_atomics; - struct { - uint64_t __attribute__((aligned(8))) iova_start; - uint64_t __attribute__((aligned(8))) pl_pdir_dma; - uint32_t page_shift; - uint32_t page_list_len; - uint32_t length; - uint32_t access_flags; - uint32_t rkey; - uint32_t reserved; - } fast_reg; - struct { - uint32_t remote_qpn; - uint32_t remote_qkey; - struct pvrdma_av av; - } ud; - } wr; -}; -/* Use pvrdma_sge (ib_sge) for send queue s/g array elements. */ - -/* Completion queue element. */ -struct pvrdma_cqe { - uint64_t __attribute__((aligned(8))) wr_id; - uint64_t __attribute__((aligned(8))) qp; - uint32_t opcode; - uint32_t status; - uint32_t byte_len; - uint32_t imm_data; - uint32_t src_qp; - uint32_t wc_flags; - uint32_t vendor_err; - uint16_t pkey_index; - uint16_t slid; - uint8_t sl; - uint8_t dlid_path_bits; - uint8_t port_num; - uint8_t smac[6]; - uint8_t network_hdr_type; - uint8_t reserved2[6]; /* Pad to next power of 2 (64). */ -}; - -#endif /* __VMW_PVRDMA_ABI_H__ */ 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 8cc7996def..a088672230 100644 --- a/include/sysemu/accel-ops.h +++ b/include/sysemu/accel-ops.h @@ -10,7 +10,7 @@ #ifndef ACCEL_OPS_H #define ACCEL_OPS_H -#include "exec/hwaddr.h" +#include "exec/cpu-common.h" #include "qom/object.h" #define ACCEL_OPS_SUFFIX "-ops" @@ -20,7 +20,12 @@ typedef struct AccelOpsClass AccelOpsClass; DECLARE_CLASS_CHECKERS(AccelOpsClass, ACCEL_OPS, TYPE_ACCEL_OPS) -/* cpus.c operations interface */ +/** + * struct AccelOpsClass - accelerator interfaces + * + * This structure is used to abstract accelerator differences from the + * core CPU code. Not all have to be implemented. + */ struct AccelOpsClass { /*< private >*/ ObjectClass parent_class; @@ -30,6 +35,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); @@ -43,13 +49,25 @@ struct AccelOpsClass { void (*handle_interrupt)(CPUState *cpu, int mask); + /** + * @get_virtual_clock: fetch virtual clock + * @set_virtual_clock: set virtual clock + * + * These allow the timer subsystem to defer to the accelerator to + * fetch time. The set function is needed if the accelerator wants + * to track the changes to time as the timer is warped through + * various timer events. + */ int64_t (*get_virtual_clock)(void); + void (*set_virtual_clock)(int64_t time); + int64_t (*get_elapsed_ticks)(void); /* gdbstub hooks */ bool (*supports_guest_debug)(void); - int (*insert_breakpoint)(CPUState *cpu, int type, hwaddr addr, hwaddr len); - int (*remove_breakpoint)(CPUState *cpu, int type, hwaddr addr, hwaddr len); + 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); }; diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 8850cb1a14..5b1c1026f3 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -6,7 +6,6 @@ enum { QEMU_ARCH_ALL = -1, QEMU_ARCH_ALPHA = (1 << 0), QEMU_ARCH_ARM = (1 << 1), - QEMU_ARCH_CRIS = (1 << 2), QEMU_ARCH_I386 = (1 << 3), QEMU_ARCH_M68K = (1 << 4), QEMU_ARCH_MICROBLAZE = (1 << 6), @@ -18,7 +17,6 @@ enum { QEMU_ARCH_XTENSA = (1 << 12), QEMU_ARCH_OPENRISC = (1 << 13), QEMU_ARCH_TRICORE = (1 << 16), - QEMU_ARCH_NIOS2 = (1 << 17), QEMU_ARCH_HPPA = (1 << 18), QEMU_ARCH_RISCV = (1 << 19), QEMU_ARCH_RX = (1 << 20), 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 6858e39cb6..9cc9b008ec 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); @@ -38,20 +54,18 @@ bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); void monitor_remove_blk(BlockBackend *blk); BlockBackendPublic *blk_get_public(BlockBackend *blk); -BlockBackend *blk_by_public(BlockBackendPublic *public); void blk_remove_bs(BlockBackend *blk); int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); -bool bdrv_has_blk(BlockDriverState *bs); -bool bdrv_is_root_node(BlockDriverState *bs); -int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, - Error **errp); +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); BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); -void blk_iostatus_disable(BlockBackend *blk); void blk_iostatus_reset(BlockBackend *blk); int blk_attach_dev(BlockBackend *blk, DeviceState *dev); void blk_detach_dev(BlockBackend *blk, DeviceState *dev); @@ -60,11 +74,10 @@ BlockBackend *blk_by_dev(void *dev); BlockBackend *blk_by_qdev_id(const char *id, Error **errp); void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque); -void blk_activate(BlockBackend *blk, Error **errp); - int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); void blk_aio_cancel(BlockAIOCB *acb); int blk_commit_all(void); +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, @@ -74,9 +87,6 @@ bool blk_is_sg(BlockBackend *blk); void blk_set_enable_write_cache(BlockBackend *blk, bool wce); int blk_get_flags(BlockBackend *blk); bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); -void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason); -void blk_op_block_all(BlockBackend *blk, Error *reason); -void blk_op_unblock_all(BlockBackend *blk, Error *reason); int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, Error **errp); void blk_add_aio_context_notifier(BlockBackend *blk, @@ -88,7 +98,6 @@ void blk_remove_aio_context_notifier(BlockBackend *blk, void (*detach_aio_context)(void *), void *opaque); void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); -void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); BlockBackendRootState *blk_get_root_state(BlockBackend *blk); void blk_update_root_state(BlockBackend *blk); bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk); diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h index 50f5aa2e07..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); @@ -73,8 +100,6 @@ void blk_iostatus_set_err(BlockBackend *blk, int error); int blk_get_max_iov(BlockBackend *blk); int blk_get_max_hw_iov(BlockBackend *blk); -void blk_io_plug(BlockBackend *blk); -void blk_io_unplug(BlockBackend *blk); AioContext *blk_get_aio_context(BlockBackend *blk); BlockAcctStats *blk_get_stats(BlockBackend *blk); void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, @@ -92,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 @@ -101,77 +135,94 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, * the "I/O or GS" API. */ -int generated_co_wrapper blk_pread(BlockBackend *blk, int64_t offset, - int64_t bytes, void *buf, - 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 generated_co_wrapper blk_preadv(BlockBackend *blk, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - 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 generated_co_wrapper blk_preadv_part(BlockBackend *blk, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags); +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 generated_co_wrapper blk_pwrite(BlockBackend *blk, int64_t offset, - int64_t bytes, const void *buf, - 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 generated_co_wrapper blk_pwritev(BlockBackend *blk, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - 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); -int generated_co_wrapper blk_pwritev_part(BlockBackend *blk, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - size_t qiov_offset, - BdrvRequestFlags flags); +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); -int generated_co_wrapper blk_pwrite_compressed(BlockBackend *blk, - int64_t offset, int64_t bytes, - const void *buf); +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); -int generated_co_wrapper blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, - int64_t bytes, - BdrvRequestFlags 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); -int generated_co_wrapper blk_pdiscard(BlockBackend *blk, int64_t offset, - int64_t bytes); +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 generated_co_wrapper blk_flush(BlockBackend *blk); +int co_wrapper_mixed blk_flush(BlockBackend *blk); int coroutine_fn blk_co_flush(BlockBackend *blk); -int generated_co_wrapper 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 generated_co_wrapper 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); diff --git a/include/sysemu/cpu-throttle.h b/include/sysemu/cpu-throttle.h index d65bdef6d0..420702b8d3 100644 --- a/include/sysemu/cpu-throttle.h +++ b/include/sysemu/cpu-throttle.h @@ -65,4 +65,18 @@ bool cpu_throttle_active(void); */ int cpu_throttle_get_percentage(void); +/** + * cpu_throttle_dirty_sync_timer_tick: + * + * Dirty sync timer hook. + */ +void cpu_throttle_dirty_sync_timer_tick(void *opaque); + +/** + * cpu_throttle_dirty_sync_timer: + * + * Start or stop the dirty sync timer. + */ +void cpu_throttle_dirty_sync_timer(bool enable); + #endif /* SYSEMU_CPU_THROTTLE_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..7bfa960fbd 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); @@ -84,8 +96,9 @@ int64_t cpu_get_clock(void); void qemu_timer_notify_cb(void *opaque, QEMUClockType type); -/* get the VIRTUAL clock and VM elapsed ticks via the cpus accel interface */ +/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */ int64_t cpus_get_virtual_clock(void); +void cpus_set_virtual_clock(int64_t new_time); int64_t cpus_get_elapsed_ticks(void); #endif /* SYSEMU_CPU_TIMERS_H */ diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index 1bace3379b..b4a566cfe7 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -1,7 +1,6 @@ #ifndef QEMU_CPUS_H #define QEMU_CPUS_H -#include "qemu/timer.h" #include "sysemu/accel-ops.h" /* register accel-specific operations */ @@ -51,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 cf9b3f07fe..b20822df0d 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,12 +50,6 @@ typedef struct CryptoDevBackendPeers CryptoDevBackendPeers; typedef struct CryptoDevBackendClient CryptoDevBackendClient; -enum CryptoDevBackendAlgType { - CRYPTODEV_BACKEND_ALG_SYM, - CRYPTODEV_BACKEND_ALG_ASYM, - CRYPTODEV_BACKEND_ALG__MAX, -}; - /** * CryptoDevBackendSymSessionInfo: * @@ -179,17 +175,22 @@ typedef struct CryptoDevBackendAsymOpInfo { uint8_t *dst; } CryptoDevBackendAsymOpInfo; +typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret); + typedef struct CryptoDevBackendOpInfo { - enum CryptoDevBackendAlgType algtype; + QCryptodevBackendAlgoType 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; -typedef void (*CryptoDevCompletionFunc) (void *opaque, int ret); struct CryptoDevBackendClass { ObjectClass parent_class; @@ -209,24 +210,11 @@ struct CryptoDevBackendClass { void *opaque); int (*do_op)(CryptoDevBackend *backend, - CryptoDevBackendOpInfo *op_info, - uint32_t queue_index, - CryptoDevCompletionFunc cb, - void *opaque); + 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_LKCF = 3, - 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; @@ -260,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; @@ -267,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 @@ -283,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 @@ -301,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( @@ -354,29 +392,22 @@ int cryptodev_backend_close_session( /** * cryptodev_backend_crypto_operation: * @backend: the cryptodev backend object - * @opaque1: pointer to a VirtIOCryptoReq object - * @queue_index: queue index of cryptodev backend client - * @errp: pointer to a NULL-initialized error object - * @cb: callbacks when operation is completed - * @opaque2: parameter passed to cb + * @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: 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 *opaque1, - uint32_t queue_index, - CryptoDevCompletionFunc cb, - void *opaque2); + 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 */ @@ -396,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 ca5339beae..eb601522f8 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -126,17 +126,14 @@ 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: diff --git a/include/sysemu/dirtylimit.h b/include/sysemu/dirtylimit.h index 8d2c1f3a6b..d11ebbbbdb 100644 --- a/include/sysemu/dirtylimit.h +++ b/include/sysemu/dirtylimit.h @@ -34,4 +34,6 @@ void dirtylimit_set_vcpu(int cpu_index, 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 index 4d3b9a4902..20813f303f 100644 --- a/include/sysemu/dirtyrate.h +++ b/include/sysemu/dirtyrate.h @@ -13,6 +13,8 @@ #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 */ diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index a1ac5bc1b5..5a49a30628 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -152,7 +152,7 @@ static inline MemTxResult dma_memory_read(AddressSpace *as, dma_addr_t addr, } /** - * address_space_write: Write to address space from DMA controller. + * dma_memory_write: Write to address space from DMA controller. * * Return a MemTxResult indicating whether the operation succeeded * or failed (eg unassigned memory, device rejected the transaction, @@ -189,7 +189,7 @@ MemTxResult dma_memory_set(AddressSpace *as, dma_addr_t addr, uint8_t c, dma_addr_t len, MemTxAttrs attrs); /** - * address_space_map: Map a physical memory region into a host virtual address. + * dma_memory_map: Map a physical memory region into a host virtual address. * * May map a subset of the requested range, given by and returned in @plen. * May return %NULL and set *@plen to zero(0), if resources needed to perform @@ -216,16 +216,15 @@ static inline void *dma_memory_map(AddressSpace *as, } /** - * address_space_unmap: Unmaps a memory region previously mapped - * by dma_memory_map() + * dma_memory_unmap: Unmaps a memory region previously mapped by dma_memory_map() * * Will also mark the memory as dirty if @dir == %DMA_DIRECTION_FROM_DEVICE. * @access_len gives the amount of memory that was actually read or written * by the caller. * * @as: #AddressSpace used - * @buffer: host pointer as returned by address_space_map() - * @len: buffer length as returned by address_space_map() + * @buffer: host pointer as returned by dma_memory_map() + * @len: buffer length as returned by dma_memory_map() * @dir: indicates the transfer direction * @access_len: amount of data actually transferred */ diff --git a/include/sysemu/dump-arch.h b/include/sysemu/dump-arch.h index 59bbc9be38..743916e46c 100644 --- a/include/sysemu/dump-arch.h +++ b/include/sysemu/dump-arch.h @@ -24,6 +24,7 @@ typedef struct ArchDumpInfo { 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 4ffed0b659..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 */ @@ -156,6 +157,7 @@ typedef struct DumpState { MemoryMappingList list; bool resume; bool detached; + bool kdump_raw; hwaddr memory_offset; int fd; 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/host_iommu_device.h b/include/sysemu/host_iommu_device.h new file mode 100644 index 0000000000..809cced4ba --- /dev/null +++ b/include/sysemu/host_iommu_device.h @@ -0,0 +1,110 @@ +/* + * Host IOMMU device abstract declaration + * + * Copyright (C) 2024 Intel Corporation. + * + * Authors: Zhenzhong Duan + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef HOST_IOMMU_DEVICE_H +#define HOST_IOMMU_DEVICE_H + +#include "qom/object.h" +#include "qapi/error.h" + +/** + * struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities. + * + * @type: host platform IOMMU type. + * + * @hw_caps: host platform IOMMU capabilities (e.g. on IOMMUFD this represents + * the @out_capabilities value returned from IOMMU_GET_HW_INFO ioctl) + */ +typedef struct HostIOMMUDeviceCaps { + uint32_t type; + uint64_t hw_caps; +} HostIOMMUDeviceCaps; + +#define TYPE_HOST_IOMMU_DEVICE "host-iommu-device" +OBJECT_DECLARE_TYPE(HostIOMMUDevice, HostIOMMUDeviceClass, HOST_IOMMU_DEVICE) + +struct HostIOMMUDevice { + Object parent_obj; + + char *name; + void *agent; /* pointer to agent device, ie. VFIO or VDPA device */ + PCIBus *aliased_bus; + int aliased_devfn; + HostIOMMUDeviceCaps caps; +}; + +/** + * struct HostIOMMUDeviceClass - The base class for all host IOMMU devices. + * + * Different types of host devices (e.g., VFIO or VDPA device) or devices + * with different backend (e.g., VFIO legacy container or IOMMUFD backend) + * will have different implementations of the HostIOMMUDeviceClass. + */ +struct HostIOMMUDeviceClass { + ObjectClass parent_class; + + /** + * @realize: initialize host IOMMU device instance further. + * + * Mandatory callback. + * + * @hiod: pointer to a host IOMMU device instance. + * + * @opaque: pointer to agent device of this host IOMMU device, + * e.g., VFIO base device or VDPA device. + * + * @errp: pass an Error out when realize fails. + * + * Returns: true on success, false on failure. + */ + bool (*realize)(HostIOMMUDevice *hiod, void *opaque, Error **errp); + /** + * @get_cap: check if a host IOMMU device capability is supported. + * + * Optional callback, if not implemented, hint not supporting query + * of @cap. + * + * @hiod: pointer to a host IOMMU device instance. + * + * @cap: capability to check. + * + * @errp: pass an Error out when fails to query capability. + * + * Returns: <0 on failure, 0 if a @cap is unsupported, or else + * 1 or some positive value for some special @cap, + * i.e., HOST_IOMMU_DEVICE_CAP_AW_BITS. + */ + int (*get_cap)(HostIOMMUDevice *hiod, int cap, Error **errp); + /** + * @get_iova_ranges: Return the list of usable iova_ranges along with + * @hiod Host IOMMU device + * + * @hiod: handle to the host IOMMU device + */ + GList* (*get_iova_ranges)(HostIOMMUDevice *hiod); + /** + * + * @get_page_size_mask: Return the page size mask supported along this + * @hiod Host IOMMU device + * + * @hiod: handle to the host IOMMU device + */ + uint64_t (*get_page_size_mask)(HostIOMMUDevice *hiod); +}; + +/* + * Host IOMMU device capability list. + */ +#define HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE 0 +#define HOST_IOMMU_DEVICE_CAP_AW_BITS 1 + +#define HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX 64 +#endif diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 39326f1d4f..67f45abe39 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -39,6 +39,8 @@ OBJECT_DECLARE_TYPE(HostMemoryBackend, HostMemoryBackendClass, */ #define TYPE_MEMORY_BACKEND_FILE "memory-backend-file" +#define TYPE_MEMORY_BACKEND_MEMFD "memory-backend-memfd" + /** * HostMemoryBackendClass: @@ -47,7 +49,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 +76,7 @@ struct HostMemoryBackend { uint64_t size; bool merge, dump, use_canonical_path; bool prealloc, is_mapped, share, reserve; + bool guest_memfd, aligned; uint32_t prealloc_threads; ThreadContext *prealloc_context; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index bb70082e45..730f927f03 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -16,19 +16,17 @@ #include "qemu/accel.h" #include "qom/object.h" -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET +#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 */ +#endif /* COMPILING_PER_TARGET */ #define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf") @@ -36,4 +34,38 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) +#ifdef COMPILING_PER_TARGET +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 /* COMPILING_PER_TARGET */ + #endif diff --git a/include/sysemu/hvf_int.h b/include/sysemu/hvf_int.h index 6545f7cd61..42ae18433f 100644 --- a/include/sysemu/hvf_int.h +++ b/include/sysemu/hvf_int.h @@ -13,8 +13,10 @@ #ifdef __aarch64__ #include +typedef hv_vcpu_t hvf_vcpuid; #else #include +typedef hv_vcpuid_t hvf_vcpuid; #endif /* hvf_slot flags */ @@ -45,18 +47,25 @@ 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 { - uint64_t fd; +struct AccelCPUState { + hvf_vcpuid fd; void *exit; bool vtimer_masked; sigset_t unblock_ipi_mask; + bool guest_debug_enabled; + bool dirty; }; -void assert_hvf_ok(hv_return_t ret); +void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, + const char *exp); +#define assert_hvf_ok(EX) assert_hvf_ok_impl((EX), __FILE__, __LINE__, #EX) +const char *hvf_return_string(hv_return_t ret); int hvf_arch_init(void); +hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range); int hvf_arch_init_vcpu(CPUState *cpu); void hvf_arch_vcpu_destroy(CPUState *cpu); int hvf_vcpu_exec(CPUState *); 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..4c4886c778 --- /dev/null +++ b/include/sysemu/iommufd.h @@ -0,0 +1,66 @@ +/* + * iommufd container backend declaration + * + * Copyright (C) 2024 Intel Corporation. + * Copyright Red Hat, Inc. 2024 + * + * Authors: Yi Liu + * Eric Auger + * Zhenzhong Duan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SYSEMU_IOMMUFD_H +#define SYSEMU_IOMMUFD_H + +#include "qom/object.h" +#include "exec/hwaddr.h" +#include "exec/cpu-common.h" +#include "sysemu/host_iommu_device.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 >*/ +}; + +bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp); +void iommufd_backend_disconnect(IOMMUFDBackend *be); + +bool 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); +bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, + uint32_t *type, void *data, uint32_t len, + uint64_t *caps, Error **errp); +bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id, + uint32_t pt_id, uint32_t flags, + uint32_t data_type, uint32_t data_len, + void *data_ptr, uint32_t *out_hwpt, + Error **errp); +bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be, uint32_t hwpt_id, + bool start, Error **errp); +bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id, + uint64_t iova, ram_addr_t size, + uint64_t page_size, uint64_t *data, + Error **errp); + +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" +#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 e9a97eda8c..c3a60b2890 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -11,16 +11,16 @@ * */ +/* 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" -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET # ifdef CONFIG_KVM # include # define CONFIG_KVM_IS_POSSIBLE @@ -36,15 +36,11 @@ 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; #define kvm_enabled() (kvm_allowed) @@ -88,23 +84,16 @@ extern bool kvm_msi_use_devid; */ #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: @@ -147,19 +136,6 @@ extern bool kvm_msi_use_devid; */ #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 @@ -174,21 +150,17 @@ extern bool kvm_msi_use_devid; #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) #endif /* CONFIG_KVM_IS_POSSIBLE */ struct kvm_run; -struct kvm_lapic_state; struct kvm_irq_routing_entry; typedef struct KVMCapabilityInfo { @@ -216,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 @@ -242,7 +210,7 @@ bool kvm_arm_supports_user_irq(void); int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #include "cpu.h" void kvm_flush_coalesced_mmio_buffer(void); @@ -256,7 +224,7 @@ void kvm_flush_coalesced_mmio_buffer(void); * calling down to kvm_arch_update_guest_debug after the generic * fields have been set. */ -#ifdef KVM_CAP_SET_GUEST_DEBUG +#ifdef TARGET_KVM_HAVE_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) @@ -267,11 +235,11 @@ static inline int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_t /* internal API */ -int kvm_ioctl(KVMState *s, int type, ...); +int kvm_ioctl(KVMState *s, unsigned long type, ...); -int kvm_vm_ioctl(KVMState *s, int type, ...); +int kvm_vm_ioctl(KVMState *s, unsigned long type, ...); -int kvm_vcpu_ioctl(CPUState *cpu, int type, ...); +int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...); /** * kvm_device_ioctl - call an ioctl on a kvm device @@ -280,7 +248,7 @@ int kvm_vcpu_ioctl(CPUState *cpu, int type, ...); * * Returns: -errno on error, nonnegative on success */ -int kvm_device_ioctl(int fd, int type, ...); +int kvm_device_ioctl(int fd, unsigned long type, ...); /** * kvm_vm_check_attr - check for existence of a specific vm attribute @@ -345,6 +313,39 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); */ bool kvm_device_supported(int vmfd, uint64_t type); +/** + * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. + * + * @returns: 0 when success, errno (<0) when failed. + */ +int kvm_create_vcpu(CPUState *cpu); + +/** + * kvm_park_vcpu - Park QEMU KVM vCPU context + * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. + * + * @returns: none + */ +void kvm_park_vcpu(CPUState *cpu); + +/** + * kvm_unpark_vcpu - unpark QEMU KVM vCPU context + * @s: KVM State + * @vcpu_id: Architecture vCPU ID of the parked vCPU + * + * @returns: KVM fd + */ +int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); + +/** + * kvm_create_and_park_vcpu - Create and park a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be created and parked. + * + * @returns: 0 when success, errno (<0) when failed. + */ +int kvm_create_and_park_vcpu(CPUState *cpu); + /* Arch specific hooks */ extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; @@ -358,7 +359,7 @@ int kvm_arch_handle_exit(CPUState *cpu, struct kvm_run *run); int kvm_arch_process_async_events(CPUState *cpu); -int kvm_arch_get_registers(CPUState *cpu); +int kvm_arch_get_registers(CPUState *cpu, Error **errp); /* state subset only touched by the VCPU itself during runtime */ #define KVM_PUT_RUNTIME_STATE 1 @@ -367,7 +368,9 @@ int kvm_arch_get_registers(CPUState *cpu); /* full state set, modified during initialization or on vmload */ #define KVM_PUT_FULL_STATE 3 -int kvm_arch_put_registers(CPUState *cpu, int level); +int kvm_arch_put_registers(CPUState *cpu, int level, Error **errp); + +int kvm_arch_get_default_type(MachineState *ms); int kvm_arch_init(MachineState *ms, KVMState *s); @@ -405,20 +408,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); @@ -426,10 +427,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); @@ -464,19 +463,12 @@ 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 */ +#endif /* COMPILING_PER_TARGET */ void kvm_cpu_synchronize_state(CPUState *cpu); @@ -511,10 +503,11 @@ static inline void kvm_irqchip_commit_route_changes(KVMRouteChange *c) } } +int kvm_irqchip_get_virq(KVMState *s); void kvm_irqchip_release_virq(KVMState *s, int virq); -int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter); -int kvm_irqchip_add_hv_sint_route(KVMState *s, uint32_t vcpu, uint32_t sint); +void kvm_add_routing_entry(KVMState *s, + struct kvm_irq_routing_entry *entry); int kvm_irqchip_add_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq); @@ -525,7 +518,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); @@ -563,23 +555,27 @@ 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); -/** - * kvm_cpu_check_are_resettable - return whether CPUs can be reset - * - * Returns: true: CPUs are resettable - * false: CPUs are not resettable - */ -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); + +void kvm_mark_guest_state_protected(void); + +/** + * kvm_hwpoisoned_mem - indicate if there is any hwpoisoned page + * reported for the VM. + */ +bool kvm_hwpoisoned_mem(void); + +int kvm_create_guest_memfd(uint64_t size, uint64_t flags, Error **errp); + +int kvm_set_memory_attributes_private(hwaddr start, uint64_t size); +int kvm_set_memory_attributes_shared(hwaddr start, uint64_t size); + +int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private); + #endif diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 3b4adcdc10..a1e72763da 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -12,7 +12,11 @@ #include "exec/memory.h" #include "qapi/qapi-types-common.h" #include "qemu/accel.h" +#include "qemu/queue.h" #include "sysemu/kvm.h" +#include "hw/boards.h" +#include "hw/i386/topology.h" +#include "io/channel-socket.h" typedef struct KVMSlot { @@ -29,16 +33,55 @@ typedef struct KVMSlot int as_id; /* Cache of the offset in ram address space */ ram_addr_t ram_start_offset; + int guest_memfd; + hwaddr guest_memfd_offset; } KVMSlot; +typedef struct KVMMemoryUpdate { + QSIMPLEQ_ENTRY(KVMMemoryUpdate) next; + MemoryRegionSection section; +} KVMMemoryUpdate; + typedef struct KVMMemoryListener { MemoryListener listener; KVMSlot *slots; + unsigned int nr_slots_used; + unsigned int nr_slots_allocated; int as_id; + QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_add; + QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_del; } KVMMemoryListener; #define KVM_MSI_HASHTAB_SIZE 256 +typedef struct KVMHostTopoInfo { + /* Number of package on the Host */ + unsigned int maxpkgs; + /* Number of cpus on the Host */ + unsigned int maxcpus; + /* Number of cpus on each different package */ + unsigned int *pkg_cpu_count; + /* Each package can have different maxticks */ + unsigned int *maxticks; +} KVMHostTopoInfo; + +struct KVMMsrEnergy { + pid_t pid; + bool enable; + char *socket_path; + QIOChannelSocket *sioc; + QemuThread msr_thr; + unsigned int guest_vcpus; + unsigned int guest_vsockets; + X86CPUTopoInfo guest_topo_info; + KVMHostTopoInfo host_topo; + const CPUArchIdList *guest_cpu_list; + uint64_t *msr_value; + uint64_t msr_unit; + uint64_t msr_limit; + uint64_t msr_info; +}; + enum KVMDirtyRingReaperState { KVM_DIRTY_RING_REAPER_NONE = 0, /* The reaper is sleeping */ @@ -60,8 +103,8 @@ struct KVMDirtyRingReaper { struct KVMState { AccelState parent_obj; - - int nr_slots; + /* Max number of KVM slots supported */ + int nr_slots_max; int fd; int vmfd; int coalesced_mmio; @@ -69,24 +112,30 @@ struct KVMState 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 +#ifdef TARGET_KVM_HAVE_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; + bool guest_state_protected; 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; + /* + * Older POSIX says that ioctl numbers are signed int, but in + * practice they are not. (Newer POSIX doesn't specify ioctl + * at all.) Linux, glibc and *BSD all treat ioctl numbers as + * unsigned, and real-world ioctl values like KVM_GET_XSAVE have + * bit 31 set, which means that passing them via an 'int' will + * result in sign-extension when they get converted back to the + * 'unsigned long' which the ioctl() prototype uses. Luckily Linux + * always treats the argument as an unsigned 32-bit int, so any + * possible sign-extension is deliberately ignored, but for + * consistency we keep to the same type that glibc is using. + */ + unsigned long irq_set_ioctl; unsigned int sigmask_len; GHashTable *gsimap; #ifdef KVM_CAP_IRQ_ROUTING @@ -94,7 +143,6 @@ struct KVMState 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; @@ -107,9 +155,17 @@ struct KVMState } *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; + struct KVMMsrEnergy msr_energy; 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, 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..0467614147 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -36,20 +36,21 @@ enum { #define UINT16_BITS 16 -struct NodeInfo { +typedef struct NodeInfo { uint64_t node_mem; 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]; -}; +} NodeInfo; -struct NumaNodeMem { +typedef struct NumaNodeMem { uint64_t node_mem; uint64_t node_plugged_mem; -}; +} NumaNodeMem; struct HMAT_LB_Data { uint8_t initiator; diff --git a/include/sysemu/nvmm.h b/include/sysemu/nvmm.h index 833670fccb..6971ddb3a5 100644 --- a/include/sysemu/nvmm.h +++ b/include/sysemu/nvmm.h @@ -7,10 +7,12 @@ * 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 -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_NVMM @@ -22,6 +24,6 @@ int nvmm_enabled(void); #endif /* CONFIG_NVMM */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #endif /* 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 5b38c7bd04..b82a5d3ad9 100644 --- a/include/sysemu/os-win32.h +++ b/include/sysemu/os-win32.h @@ -29,6 +29,7 @@ #include #include #include +#include "qemu/typedefs.h" #ifdef HAVE_AFUNIX_H #include @@ -51,14 +52,34 @@ typedef struct sockaddr_un { 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 @@ -80,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); @@ -108,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) @@ -144,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, @@ -179,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, @@ -221,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..c161d75165 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); @@ -29,7 +34,6 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** void qtest_server_set_send_handler(void (*send)(void *, const char *), void *opaque); 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 7ec0882b50..cba74fa9bc 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,22 +73,6 @@ 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); - /* Processing clocks and other time sources */ /*! Save the specified clock */ @@ -131,25 +82,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. */ @@ -171,8 +117,6 @@ void replay_async_events(void); /* Asynchronous events queue */ -/*! Disables storing events in the queue */ -void replay_disable_events(void); /*! Enables storing events in the queue */ void replay_enable_events(void); /*! Returns true when saving events is enabled */ diff --git a/include/sysemu/reset.h b/include/sysemu/reset.h index 609e4d50c2..0e297c0e02 100644 --- a/include/sysemu/reset.h +++ b/include/sysemu/reset.h @@ -1,13 +1,127 @@ +/* + * 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 "hw/resettable.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(ShutdownCause reason); + +/** + * qemu_devices_reset: Perform a complete system reset + * @reason: type of 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(ResetType type); #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..11c7ff3ffb 100644 --- a/include/sysemu/runstate.h +++ b/include/sysemu/runstate.h @@ -6,9 +6,10 @@ 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); +void runstate_replay_enable(void); typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); @@ -16,9 +17,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 +41,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 +58,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 +89,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); @@ -75,6 +105,7 @@ void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(ShutdownCause reason); void qemu_system_guest_panicked(GuestPanicInformation *info); void qemu_system_guest_crashloaded(GuestPanicInformation *info); +void qemu_system_guest_pvshutdown(void); bool qemu_system_dump_in_progress(void); #endif diff --git a/include/sysemu/spdm-socket.h b/include/sysemu/spdm-socket.h new file mode 100644 index 0000000000..5d8bd9aa4e --- /dev/null +++ b/include/sysemu/spdm-socket.h @@ -0,0 +1,74 @@ +/* + * QEMU SPDM socket support + * + * 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 SPDM_REQUESTER_H +#define SPDM_REQUESTER_H + +/** + * spdm_socket_connect: connect to an external SPDM socket + * @port: port to connect to + * @errp: error object handle + * + * This will connect to an external SPDM socket server. On error + * it will return -1 and errp will be set. On success this function + * will return the socket number. + */ +int spdm_socket_connect(uint16_t port, Error **errp); + +/** + * spdm_socket_rsp: send and receive a message to a SPDM server + * @socket: socket returned from spdm_socket_connect() + * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro + * @req: request buffer + * @req_len: request buffer length + * @rsp: response buffer + * @rsp_len: response buffer length + * + * Send platform data to a SPDM server on socket and then receive + * a response. + */ +uint32_t spdm_socket_rsp(const int socket, uint32_t transport_type, + void *req, uint32_t req_len, + void *rsp, uint32_t rsp_len); + +/** + * spdm_socket_close: send a shutdown command to the server + * @socket: socket returned from spdm_socket_connect() + * @transport_type: SPDM_SOCKET_TRANSPORT_TYPE_* macro + * + * This will issue a shutdown command to the server. + */ +void spdm_socket_close(const int socket, uint32_t transport_type); + +#define SPDM_SOCKET_COMMAND_NORMAL 0x0001 +#define SPDM_SOCKET_COMMAND_OOB_ENCAP_KEY_UPDATE 0x8001 +#define SPDM_SOCKET_COMMAND_CONTINUE 0xFFFD +#define SPDM_SOCKET_COMMAND_SHUTDOWN 0xFFFE +#define SPDM_SOCKET_COMMAND_UNKOWN 0xFFFF +#define SPDM_SOCKET_COMMAND_TEST 0xDEAD + +#define SPDM_SOCKET_TRANSPORT_TYPE_MCTP 0x01 +#define SPDM_SOCKET_TRANSPORT_TYPE_PCI_DOE 0x02 + +#define SPDM_SOCKET_MAX_MESSAGE_BUFFER_SIZE 0x1200 + +#endif diff --git a/include/monitor/stats.h b/include/sysemu/stats.h similarity index 99% rename from include/monitor/stats.h rename to include/sysemu/stats.h index fcf0983154..42c236c795 100644 --- a/include/monitor/stats.h +++ b/include/sysemu/stats.h @@ -34,7 +34,7 @@ void add_stats_schema(StatsSchemaList **, StatsProvider, StatsTarget, StatsSchemaValueList *); /* - * True if a string matches the filter passed to the stats_fn callabck, + * 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 diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 6a7a31e64d..7ec419ce13 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -41,8 +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; extern bool enable_mlock; @@ -61,9 +59,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 */ @@ -75,8 +70,6 @@ Chardev *serial_hd(int i); extern Chardev *parallel_hds[MAX_PARALLEL_PORTS]; -void hmp_info_usb(Monitor *mon, const QDict *qdict); - void add_boot_device_path(int32_t bootindex, DeviceState *dev, const char *suffix); char *get_boot_devices_list(size_t *size); @@ -104,7 +97,7 @@ bool defaults_enabled(void); void qemu_init(int argc, char **argv); int qemu_main_loop(void); -void qemu_cleanup(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/whpx.h b/include/sysemu/whpx.h index 2889fa2278..00ff409b68 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -10,10 +10,12 @@ * */ +/* header to be included in non-WHPX-specific code */ + #ifndef QEMU_WHPX_H #define QEMU_WHPX_H -#ifdef NEED_CPU_H +#ifdef COMPILING_PER_TARGET #ifdef CONFIG_WHPX @@ -27,6 +29,6 @@ bool whpx_apic_in_platform(void); #endif /* CONFIG_WHPX */ -#endif /* NEED_CPU_H */ +#endif /* COMPILING_PER_TARGET */ #endif /* QEMU_WHPX_H */ diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index c8e7c2f6cf..b5e3ea1bc0 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -10,15 +10,18 @@ #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); -uint8_t *xen_map_cache(hwaddr phys_addr, hwaddr size, - uint8_t lock, bool dma); +uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, + ram_addr_t ram_addr_offset, + uint8_t lock, bool dma, + bool is_write); ram_addr_t xen_ram_addr_from_mapcache(void *ptr); void xen_invalidate_map_cache_entry(uint8_t *buffer); void xen_invalidate_map_cache(void); @@ -32,10 +35,13 @@ static inline void xen_map_cache_init(phys_offset_to_gaddr_t f, { } -static inline uint8_t *xen_map_cache(hwaddr phys_addr, +static inline uint8_t *xen_map_cache(MemoryRegion *mr, + hwaddr phys_addr, hwaddr size, + ram_addr_t ram_addr_offset, uint8_t lock, - bool dma) + bool dma, + bool is_write) { abort(); } diff --git a/include/sysemu/xen.h b/include/sysemu/xen.h index 0ca25697e4..d70eacfbe2 100644 --- a/include/sysemu/xen.h +++ b/include/sysemu/xen.h @@ -5,18 +5,24 @@ * 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 +#ifdef COMPILING_PER_TARGET # ifdef CONFIG_XEN # define CONFIG_XEN_IS_POSSIBLE # endif #else # define CONFIG_XEN_IS_POSSIBLE -#endif +#endif /* COMPILING_PER_TARGET */ #ifdef CONFIG_XEN_IS_POSSIBLE @@ -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,8 +46,9 @@ 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 */ +bool xen_mr_is_memory(MemoryRegion *mr); +bool xen_mr_is_grants(MemoryRegion *mr); #endif 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..909fe73afa --- /dev/null +++ b/include/tcg/helper-info.h @@ -0,0 +1,67 @@ +/* + * 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 +#include "tcg-target-reg-bits.h" + +#define MAX_CALL_IARGS 7 + +/* + * 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..e0662b2a12 --- /dev/null +++ b/include/tcg/startup.h @@ -0,0 +1,62 @@ +/* + * 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 + +#ifdef XBOX +void tcg_register_init_ctx(void); +#endif + +/** + * 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..e8e9657986 --- /dev/null +++ b/include/tcg/tcg-op-common.h @@ -0,0 +1,608 @@ +/* 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_f32 tcg_temp_new_f32(void); +TCGv_f64 tcg_temp_new_f64(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_f32 tcg_global_mem_new_f32(TCGv_ptr reg, intptr_t offset, const char *name); +TCGv_f64 tcg_global_mem_new_f64(TCGv_ptr reg, intptr_t offset, 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(unsigned from); +void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo); + +/* 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 + +/* FP */ +void tcg_gen_flcr(TCGv_i32 arg); +void tcg_gen_st80f_f32(TCGv_f32 arg, TCGv_ptr dst); +void tcg_gen_st80f_f64(TCGv_f64 arg, TCGv_ptr dst); +void tcg_gen_ld80f_f32(TCGv_f32 ret, TCGv_ptr src); +void tcg_gen_ld80f_f64(TCGv_f64 ret, TCGv_ptr src); +void tcg_gen_abs_f32(TCGv_f32 ret, TCGv_f32 src); +void tcg_gen_abs_f64(TCGv_f64 ret, TCGv_f64 src); +void tcg_gen_add_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2); +void tcg_gen_add_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2); +void tcg_gen_chs_f32(TCGv_f32 ret, TCGv_f32 src); +void tcg_gen_chs_f64(TCGv_f64 ret, TCGv_f64 src); +void tcg_gen_com_f32(TCGv_i64 ret, TCGv_f32 arg1, TCGv_f32 arg2); +void tcg_gen_com_f64(TCGv_i64 ret, TCGv_f64 arg1, TCGv_f64 arg2); +void tcg_gen_cos_f32(TCGv_f32 ret, TCGv_f32 arg); +void tcg_gen_cos_f64(TCGv_f64 ret, TCGv_f64 arg); +void tcg_gen_cvt32f_f64(TCGv_f64 ret, TCGv_f32 arg); +void tcg_gen_cvt32f_i32(TCGv_i32 ret, TCGv_f32 arg); +void tcg_gen_cvt32f_i64(TCGv_i64 ret, TCGv_f32 arg); +void tcg_gen_cvt32i_f32(TCGv_f32 ret, TCGv_i32 arg); +void tcg_gen_cvt32i_f64(TCGv_f64 ret, TCGv_i32 arg); +void tcg_gen_cvt64f_f32(TCGv_f32 ret, TCGv_f64 arg); +void tcg_gen_cvt64f_i32(TCGv_i32 ret, TCGv_f64 src); +void tcg_gen_cvt64f_i64(TCGv_i64 ret, TCGv_f64 src); +void tcg_gen_cvt64i_f32(TCGv_f32 ret, TCGv_i64 arg); +void tcg_gen_cvt64i_f64(TCGv_f64 ret, TCGv_i64 arg); +void tcg_gen_div_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2); +void tcg_gen_div_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2); +void tcg_gen_mov32f_i32(TCGv_i32 ret, TCGv_f32 src); +void tcg_gen_mov32i_f32(TCGv_f32 ret, TCGv_i32 arg); +void tcg_gen_mov64f_i64(TCGv_i64 ret, TCGv_f64 src); +void tcg_gen_mov64i_f64(TCGv_f64 ret, TCGv_i64 arg); +void tcg_gen_mov_f32(TCGv_f32 ret, TCGv_f32 src); +void tcg_gen_mov_f64(TCGv_f64 ret, TCGv_f64 src); +void tcg_gen_mul_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2); +void tcg_gen_mul_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2); +void tcg_gen_sin_f32(TCGv_f32 ret, TCGv_f32 arg); +void tcg_gen_sin_f64(TCGv_f64 ret, TCGv_f64 arg); +void tcg_gen_sqrt_f32(TCGv_f32 ret, TCGv_f32 arg); +void tcg_gen_sqrt_f64(TCGv_f64 ret, TCGv_f64 arg); +void tcg_gen_sub_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2); +void tcg_gen_sub_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2); + +#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..65553f5f97 --- /dev/null +++ b/include/tcg/tcg-op-gvec-common.h @@ -0,0 +1,434 @@ +/* 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; + /* Write aofs as a 2nd dest operand. */ + bool write_aofs; +} 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 4c1d7beb35..a02850583b 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -1,1238 +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_flcr(TCGv_i32 arg) -{ - tcg_gen_op1_i32(INDEX_op_flcr, arg); -} - -static inline void tcg_gen_st80f_f32(TCGv_f32 arg, TCGv_ptr dst) -{ - tcg_gen_op2(INDEX_op_st80f_f32, tcgv_f32_arg(arg), tcgv_ptr_arg(dst)); -} - -static inline void tcg_gen_st80f_f64(TCGv_f64 arg, TCGv_ptr dst) -{ - tcg_gen_op2(INDEX_op_st80f_f64, tcgv_f64_arg(arg), tcgv_ptr_arg(dst)); -} - -static inline void tcg_gen_ld80f_f32(TCGv_f32 ret, TCGv_ptr src) -{ - tcg_gen_op2(INDEX_op_ld80f_f32, tcgv_f32_arg(ret), tcgv_ptr_arg(src)); -} - -static inline void tcg_gen_ld80f_f64(TCGv_f64 ret, TCGv_ptr src) -{ - tcg_gen_op2(INDEX_op_ld80f_f64, tcgv_f64_arg(ret), tcgv_ptr_arg(src)); -} - -static inline void tcg_gen_abs_f32(TCGv_f32 ret, TCGv_f32 src) -{ - tcg_gen_op2(INDEX_op_abs_f32, tcgv_f32_arg(ret), tcgv_f32_arg(src)); -} - -static inline void tcg_gen_abs_f64(TCGv_f64 ret, TCGv_f64 src) -{ - tcg_gen_op2(INDEX_op_abs_f64, tcgv_f64_arg(ret), tcgv_f64_arg(src)); -} - -static inline void tcg_gen_add_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) -{ - tcg_gen_op3(INDEX_op_add_f32, - tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); -} - -static inline void tcg_gen_add_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) -{ - tcg_gen_op3(INDEX_op_add_f64, - tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); -} - -static inline void tcg_gen_chs_f32(TCGv_f32 ret, TCGv_f32 src) -{ - tcg_gen_op2(INDEX_op_chs_f32, tcgv_f32_arg(ret), tcgv_f32_arg(src)); -} - -static inline void tcg_gen_chs_f64(TCGv_f64 ret, TCGv_f64 src) -{ - tcg_gen_op2(INDEX_op_chs_f64, tcgv_f64_arg(ret), tcgv_f64_arg(src)); -} - -static inline void tcg_gen_com_f32(TCGv_i64 ret, TCGv_f32 arg1, TCGv_f32 arg2) -{ - tcg_gen_op3(INDEX_op_com_f32, - tcgv_i64_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); -} - -static inline void tcg_gen_com_f64(TCGv_i64 ret, TCGv_f64 arg1, TCGv_f64 arg2) -{ - tcg_gen_op3(INDEX_op_com_f64, - tcgv_i64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); -} - -static inline void tcg_gen_cos_f32(TCGv_f32 ret, TCGv_f32 arg) -{ - tcg_gen_op2(INDEX_op_cos_f32, tcgv_f32_arg(ret), tcgv_f32_arg(arg)); -} - -static inline void tcg_gen_cos_f64(TCGv_f64 ret, TCGv_f64 arg) -{ - tcg_gen_op2(INDEX_op_cos_f64, tcgv_f64_arg(ret), tcgv_f64_arg(arg)); -} - -static inline void tcg_gen_cvt32f_f64(TCGv_f64 ret, TCGv_f32 arg) -{ - tcg_gen_op2(INDEX_op_cvt32f_f64, tcgv_f64_arg(ret), tcgv_f32_arg(arg)); -} - -static inline void tcg_gen_cvt32f_i32(TCGv_i32 ret, TCGv_f32 arg) -{ - tcg_gen_op2(INDEX_op_cvt32f_i32, tcgv_i32_arg(ret), tcgv_f32_arg(arg)); -} - -static inline void tcg_gen_cvt32f_i64(TCGv_i64 ret, TCGv_f32 arg) -{ - tcg_gen_op2(INDEX_op_cvt32f_i64, tcgv_i64_arg(ret), tcgv_f32_arg(arg)); -} - -static inline void tcg_gen_cvt32i_f32(TCGv_f32 ret, TCGv_i32 arg) -{ - tcg_gen_op2(INDEX_op_cvt32i_f32, tcgv_f32_arg(ret), tcgv_i32_arg(arg)); -} - -static inline void tcg_gen_cvt32i_f64(TCGv_f64 ret, TCGv_i32 arg) -{ - tcg_gen_op2(INDEX_op_cvt32i_f64, tcgv_f64_arg(ret), tcgv_i32_arg(arg)); -} - -static inline void tcg_gen_cvt64f_f32(TCGv_f32 ret, TCGv_f64 arg) -{ - tcg_gen_op2(INDEX_op_cvt64f_f32, tcgv_f32_arg(ret), tcgv_f64_arg(arg)); -} - -static inline void tcg_gen_cvt64f_i32(TCGv_i32 ret, TCGv_f64 src) -{ - tcg_gen_op2(INDEX_op_cvt64f_i32, tcgv_i32_arg(ret), tcgv_f64_arg(src)); -} - -static inline void tcg_gen_cvt64f_i64(TCGv_i64 ret, TCGv_f64 src) -{ - tcg_gen_op2(INDEX_op_cvt64f_i64, tcgv_i64_arg(ret), tcgv_f64_arg(src)); -} - -static inline void tcg_gen_cvt64i_f32(TCGv_f32 ret, TCGv_i64 arg) -{ - tcg_gen_op2(INDEX_op_cvt64i_f32, tcgv_f32_arg(ret), tcgv_i64_arg(arg)); -} - -static inline void tcg_gen_cvt64i_f64(TCGv_f64 ret, TCGv_i64 arg) -{ - tcg_gen_op2(INDEX_op_cvt64i_f64, tcgv_f64_arg(ret), tcgv_i64_arg(arg)); -} - -static inline void tcg_gen_div_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) -{ - tcg_gen_op3(INDEX_op_div_f32, - tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); -} - -static inline void tcg_gen_div_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) -{ - tcg_gen_op3(INDEX_op_div_f64, - tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); -} - -static inline void tcg_gen_mov32f_i32(TCGv_i32 ret, TCGv_f32 src) -{ - tcg_gen_op2(INDEX_op_mov32f_i32, tcgv_i32_arg(ret), tcgv_f32_arg(src)); -} - -static inline void tcg_gen_mov32i_f32(TCGv_f32 ret, TCGv_i32 arg) -{ - tcg_gen_op2(INDEX_op_mov32i_f32, tcgv_f32_arg(ret), tcgv_i32_arg(arg)); -} - -static inline void tcg_gen_mov64f_i64(TCGv_i64 ret, TCGv_f64 src) -{ - tcg_gen_op2(INDEX_op_mov64f_i64, tcgv_i64_arg(ret), tcgv_f64_arg(src)); -} - -static inline void tcg_gen_mov64i_f64(TCGv_f64 ret, TCGv_i64 arg) -{ - tcg_gen_op2(INDEX_op_mov64i_f64, tcgv_f64_arg(ret), tcgv_i64_arg(arg)); -} - -static inline void tcg_gen_mov_f32(TCGv_f32 ret, TCGv_f32 src) -{ - tcg_gen_op2(INDEX_op_mov_f32, tcgv_f32_arg(ret), tcgv_f32_arg(src)); -} - -static inline void tcg_gen_mov_f64(TCGv_f64 ret, TCGv_f64 src) -{ - tcg_gen_op2(INDEX_op_mov_f64, tcgv_f64_arg(ret), tcgv_f64_arg(src)); -} - -static inline void tcg_gen_mul_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) -{ - tcg_gen_op3(INDEX_op_mul_f32, - tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); -} - -static inline void tcg_gen_mul_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) -{ - tcg_gen_op3(INDEX_op_mul_f64, - tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); -} - -static inline void tcg_gen_sin_f32(TCGv_f32 ret, TCGv_f32 arg) -{ - tcg_gen_op2(INDEX_op_sin_f32, tcgv_f32_arg(ret), tcgv_f32_arg(arg)); -} - -static inline void tcg_gen_sin_f64(TCGv_f64 ret, TCGv_f64 arg) -{ - tcg_gen_op2(INDEX_op_sin_f64, tcgv_f64_arg(ret), tcgv_f64_arg(arg)); -} - -static inline void tcg_gen_sqrt_f32(TCGv_f32 ret, TCGv_f32 arg) -{ - tcg_gen_op2(INDEX_op_sqrt_f32, tcgv_f32_arg(ret), tcgv_f32_arg(arg)); -} - -static inline void tcg_gen_sqrt_f64(TCGv_f64 ret, TCGv_f64 arg) -{ - tcg_gen_op2(INDEX_op_sqrt_f64, tcgv_f64_arg(ret), tcgv_f64_arg(arg)); -} - -static inline void tcg_gen_sub_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) -{ - tcg_gen_op3(INDEX_op_sub_f32, - tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); -} - -static inline void tcg_gen_sub_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) -{ - tcg_gen_op3(INDEX_op_sub_f64, - tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); -} - -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 @@ -1272,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 @@ -1291,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 @@ -1319,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 @@ -1352,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 @@ -1390,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 @@ -1409,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 @@ -1435,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 @@ -1468,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 dad9f84e26..1e15d4c3ea 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,32 +188,58 @@ 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) -DEF(plugin_cb_start, 0, 0, 3, TCG_OPF_NOT_PRESENT | TCG_OPF_BB_END) -DEF(plugin_cb_end, 0, 0, 0, TCG_OPF_NOT_PRESENT) +DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT | TCG_OPF_BB_END) +DEF(plugin_mem_cb, 0, 1, 1, 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 floating point support. */ DEF(flcr, 0, 1, 0, IMPL(TCG_TARGET_HAS_fpu)) @@ -326,7 +354,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..5b0fcf6d87 --- /dev/null +++ b/include/tcg/tcg-temp-internal.h @@ -0,0 +1,53 @@ +/* + * 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); +void tcg_temp_free_f32(TCGv_f32 arg); +void tcg_temp_free_f64(TCGv_f64 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); + +/* Forget all freed EBB temps, so that new allocations produce new temps. */ +static inline void tcg_temp_ebb_reset_freed(TCGContext *s) +{ + memset(s->free_temps, 0, sizeof(s->free_temps)); +} + +#endif /* TCG_TEMP_FREE_H */ diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 79aa496059..2136e3ad92 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -25,47 +25,23 @@ #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 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 +56,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 +66,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 +80,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 +93,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 +132,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) @@ -197,6 +155,7 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_minmax_vec 0 #define TCG_TARGET_HAS_bitsel_vec 0 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 0 #else #define TCG_TARGET_MAYBE_vec 1 #endif @@ -214,12 +173,6 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_fpu 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" @@ -245,14 +198,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; @@ -261,16 +206,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; }; @@ -293,6 +245,7 @@ typedef struct TCGPool { typedef enum TCGType { TCG_TYPE_I32, TCG_TYPE_I64, + TCG_TYPE_I128, TCG_TYPE_F32, TCG_TYPE_F64, @@ -301,7 +254,8 @@ typedef enum TCGType { 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 @@ -316,40 +270,28 @@ 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; /** - * get_alignment_bits - * @memop: MemOp value + * tcg_type_size + * @t: type * - * Extract the alignment size from the memop. + * Return the size of the type in bytes. */ -static inline unsigned get_alignment_bits(MemOp memop) +static inline int tcg_type_size(TCGType t) { - unsigned a = memop & MO_AMASK; - - if (a == MO_UNALN) { - /* No alignment required. */ - a = 0; - } else if (a == MO_ALIGN) { - /* A natural alignment requirement. */ - a = memop & MO_SIZE; - } else { - /* A specific alignment requirement. */ - a = a >> MO_ASHIFT; + if (t == TCG_TYPE_F32) { + return 4; + } else if (t == TCG_TYPE_F64) { + return 8; } -#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; + + unsigned i = t; + if (i >= TCG_TYPE_V64) { + tcg_debug_assert(i < TCG_TYPE_COUNT); + i -= TCG_TYPE_V64 - 1; + } + return 4 << i; } typedef tcg_target_ulong TCGArg; @@ -360,15 +302,16 @@ 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_f32 : 32 bit floating point type - * TCGv_f64 : 64 bit floating point type - * 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_f32 : 32 bit floating point type + * TCGv_f64 : 64 bit floating point 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. @@ -388,18 +331,12 @@ typedef tcg_target_ulong TCGArg; typedef struct TCGv_i32_d *TCGv_i32; typedef struct TCGv_i64_d *TCGv_i64; -typedef struct TCGv_ptr_d *TCGv_ptr; -typedef struct TCGv_vec_d *TCGv_vec; +typedef struct TCGv_i128_d *TCGv_i128; typedef struct TCGv_f32_d *TCGv_f32; typedef struct TCGv_f64_d *TCGv_f64; +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 @@ -419,9 +356,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. @@ -442,13 +376,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, @@ -467,6 +403,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; @@ -486,35 +423,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 @@ -525,26 +462,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; @@ -554,28 +475,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 @@ -614,24 +532,37 @@ struct TCGContext { * space for instructions (for variable-instruction-length ISAs). */ struct qemu_plugin_tb *plugin_tb; + const struct DisasContextBase *plugin_db; /* descriptor of the instruction being translated */ struct qemu_plugin_insn *plugin_insn; #endif + /* For host-specific values. */ +#ifdef __riscv + MemOp riscv_cur_vsew; + TCGType riscv_cur_type; +#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; @@ -644,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); @@ -666,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; @@ -683,28 +613,32 @@ 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. */ +#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) +{ + 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) { - 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 (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_ptr_temp(TCGv_ptr v) -{ - return tcgv_i32_temp((TCGv_i32)v); -} - -static inline TCGTemp *tcgv_vec_temp(TCGv_vec v) +static inline TCGTemp *tcgv_i128_temp(TCGv_i128 v) { return tcgv_i32_temp((TCGv_i32)v); } @@ -719,6 +653,16 @@ static inline TCGTemp *tcgv_f64_temp(TCGv_f64 v) return tcgv_i32_temp((TCGv_i32)v); } +static inline TCGTemp *tcgv_ptr_temp(TCGv_ptr v) +{ + return tcgv_i32_temp((TCGv_i32)v); +} + +static inline TCGTemp *tcgv_vec_temp(TCGv_vec v) +{ + return tcgv_i32_temp((TCGv_i32)v); +} + static inline TCGArg tcgv_i32_arg(TCGv_i32 v) { return temp_arg(tcgv_i32_temp(v)); @@ -729,14 +673,9 @@ static inline TCGArg tcgv_i64_arg(TCGv_i64 v) return temp_arg(tcgv_i64_temp(v)); } -static inline TCGArg tcgv_ptr_arg(TCGv_ptr v) +static inline TCGArg tcgv_i128_arg(TCGv_i128 v) { - return temp_arg(tcgv_ptr_temp(v)); -} - -static inline TCGArg tcgv_vec_arg(TCGv_vec v) -{ - return temp_arg(tcgv_vec_temp(v)); + return temp_arg(tcgv_i128_temp(v)); } static inline TCGArg tcgv_f32_arg(TCGv_f32 v) @@ -749,6 +688,16 @@ static inline TCGArg tcgv_f64_arg(TCGv_f64 v) return temp_arg(tcgv_f64_temp(v)); } +static inline TCGArg tcgv_ptr_arg(TCGv_ptr v) +{ + return temp_arg(tcgv_ptr_temp(v)); +} + +static inline TCGArg tcgv_vec_arg(TCGv_vec v) +{ + return temp_arg(tcgv_vec_temp(v)); +} + static inline TCGv_i32 temp_tcgv_i32(TCGTemp *t) { (void)temp_idx(t); /* trigger embedded assert */ @@ -760,14 +709,9 @@ static inline TCGv_i64 temp_tcgv_i64(TCGTemp *t) return (TCGv_i64)temp_tcgv_i32(t); } -static inline TCGv_ptr temp_tcgv_ptr(TCGTemp *t) +static inline TCGv_i128 temp_tcgv_i128(TCGTemp *t) { - return (TCGv_ptr)temp_tcgv_i32(t); -} - -static inline TCGv_vec temp_tcgv_vec(TCGTemp *t) -{ - return (TCGv_vec)temp_tcgv_i32(t); + return (TCGv_i128)temp_tcgv_i32(t); } static inline TCGv_f32 temp_tcgv_f32(TCGTemp *t) @@ -780,17 +724,15 @@ static inline TCGv_f64 temp_tcgv_f64(TCGTemp *t) return (TCGv_f64)temp_tcgv_i32(t); } -#if TCG_TARGET_REG_BITS == 32 -static inline TCGv_i32 TCGV_LOW(TCGv_i64 t) +static inline TCGv_ptr temp_tcgv_ptr(TCGTemp *t) { - return temp_tcgv_i32(tcgv_i64_temp(t)); + return (TCGv_ptr)temp_tcgv_i32(t); } -static inline TCGv_i32 TCGV_HIGH(TCGv_i64 t) +static inline TCGv_vec temp_tcgv_vec(TCGTemp *t) { - return temp_tcgv_i32(tcgv_i64_temp(t) + 1); + return (TCGv_vec)temp_tcgv_i32(t); } -#endif static inline TCGArg tcg_get_insn_param(TCGOp *op, int arg) { @@ -802,24 +744,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. */ @@ -878,174 +820,23 @@ static inline void *tcg_malloc(int size) } } -#ifdef XBOX -void tcg_register_init_ctx(void); -#endif - -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, target_ulong pc_start); +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 void tcg_temp_free_f32(TCGv_f32 arg) -{ - tcg_temp_free_internal(tcgv_f32_temp(arg)); -} - -static inline void tcg_temp_free_f64(TCGv_f64 arg) -{ - tcg_temp_free_internal(tcgv_f64_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); -} - -static inline TCGv_f32 tcg_global_mem_new_f32(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_F32, reg, offset, name); - return temp_tcgv_f32(t); -} - -static inline TCGv_f32 tcg_temp_new_f32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_F32, false); - return temp_tcgv_f32(t); -} - -static inline TCGv_f32 tcg_temp_local_new_f32(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_F32, true); - return temp_tcgv_f32(t); -} - -static inline TCGv_f64 tcg_global_mem_new_f64(TCGv_ptr reg, intptr_t offset, - const char *name) -{ - TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_F64, reg, offset, name); - return temp_tcgv_f64(t); -} - -static inline TCGv_f64 tcg_temp_new_f64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_F64, false); - return temp_tcgv_f64(t); -} - -static inline TCGv_f64 tcg_temp_local_new_f64(void) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_F64, true); - return temp_tcgv_f64(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; @@ -1068,7 +859,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, @@ -1091,20 +882,31 @@ 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(void *func, TCGHelperInfo *, TCGTemp *ret); +void tcg_gen_call1(void *func, TCGHelperInfo *, TCGTemp *ret, TCGTemp *); +void tcg_gen_call2(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *); +void tcg_gen_call3(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call4(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call5(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *); +void tcg_gen_call6(void *func, TCGHelperInfo *, TCGTemp *ret, + TCGTemp *, TCGTemp *, TCGTemp *, TCGTemp *, + TCGTemp *, TCGTemp *); +void tcg_gen_call7(void *func, 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: @@ -1118,46 +920,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); /** @@ -1318,7 +1080,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) \ @@ -1330,24 +1092,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 @@ -1360,6 +1104,7 @@ static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n) } bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned); +void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs); void gen_bb_epilogue(void); /* translate.c */ 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 5a88700c7f..ffae0960df 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -4,13 +4,31 @@ #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" +#include "ui/dmabuf.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 */ @@ -52,8 +70,6 @@ typedef struct QEMUPutMouseEntry QEMUPutMouseEntry; typedef struct QEMUPutKbdEntry QEMUPutKbdEntry; typedef struct QEMUPutLEDEntry QEMUPutLEDEntry; -QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, - void *opaque); QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute, const char *name); @@ -65,7 +81,7 @@ 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) */ @@ -91,24 +107,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; @@ -118,19 +140,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; @@ -145,15 +157,15 @@ typedef struct 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); @@ -161,7 +173,6 @@ int cursor_get_mono_bpl(QEMUCursor *c); void cursor_set_mono(QEMUCursor *c, uint32_t foreground, uint32_t background, uint8_t *image, int transparent, uint8_t *mask); -void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *mask); void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask); typedef void *QEMUGLContext; @@ -172,25 +183,6 @@ struct QEMUGLParams { int minor_ver; }; -typedef struct QemuDmaBuf { - int fd; - uint32_t width; - uint32_t height; - uint32_t stride; - uint32_t fourcc; - uint64_t modifier; - uint32_t texture; - uint32_t x; - uint32_t y; - uint32_t scanout_width; - uint32_t scanout_height; - bool y0_top; - void *sync; - int fence_fd; - bool allow_fences; - bool draw_submitted; -} QemuDmaBuf; - enum display_scanout { SCANOUT_NONE, SCANOUT_SURFACE, @@ -238,7 +230,7 @@ typedef struct DisplayChangeListenerOps { /* optional */ void (*dpy_mouse_set)(DisplayChangeListener *dcl, - int x, int y, int on); + int x, int y, bool on); /* optional */ void (*dpy_cursor_define)(DisplayChangeListener *dcl, QEMUCursor *cursor); @@ -252,7 +244,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 */ @@ -309,33 +302,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); @@ -346,9 +319,8 @@ void dpy_gfx_replace_surface(QemuConsole *con, void dpy_text_cursor(QemuConsole *con, int x, int y); void dpy_text_update(QemuConsole *con, int x, int y, int w, int h); 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 *con, int x, int y, bool on); void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor); -bool dpy_cursor_define_supported(QemuConsole *con); bool dpy_gfx_check_format(QemuConsole *con, pixman_format_code_t format); @@ -356,7 +328,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, @@ -375,43 +348,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) @@ -455,16 +391,16 @@ 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); bool qemu_console_is_gl_blocked(QemuConsole *con); -bool qemu_console_is_multihead(DeviceState *dev); char *qemu_console_get_label(QemuConsole *con); int qemu_console_get_index(QemuConsole *con); uint32_t qemu_console_get_head(QemuConsole *con); @@ -475,9 +411,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 @@ -503,12 +440,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/dmabuf.h b/include/ui/dmabuf.h new file mode 100644 index 0000000000..dc74ba895a --- /dev/null +++ b/include/ui/dmabuf.h @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QemuDmaBuf struct and helpers used for accessing its data + * + * 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 DMABUF_H +#define DMABUF_H + +typedef struct QemuDmaBuf QemuDmaBuf; + +QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, + uint32_t stride, uint32_t x, + uint32_t y, uint32_t backing_width, + uint32_t backing_height, uint32_t fourcc, + uint64_t modifier, int dmabuf_fd, + bool allow_fences, bool y0_top); +void qemu_dmabuf_free(QemuDmaBuf *dmabuf); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuDmaBuf, qemu_dmabuf_free); + +int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf); +int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf); +void qemu_dmabuf_close(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf); +uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_x(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_y(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_backing_width(QemuDmaBuf *dmabuf); +uint32_t qemu_dmabuf_get_backing_height(QemuDmaBuf *dmabuf); +bool qemu_dmabuf_get_y0_top(QemuDmaBuf *dmabuf); +void *qemu_dmabuf_get_sync(QemuDmaBuf *dmabuf); +int32_t qemu_dmabuf_get_fence_fd(QemuDmaBuf *dmabuf); +bool qemu_dmabuf_get_allow_fences(QemuDmaBuf *dmabuf); +bool qemu_dmabuf_get_draw_submitted(QemuDmaBuf *dmabuf); +void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture); +void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd); +void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync); +void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted); +void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd); + +#endif 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 ae0f53740d..aa3d637029 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -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..193bc046d1 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -6,11 +6,13 @@ #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 + +#include "qapi/error.h" /* * pixman image formats are defined to be native endian, @@ -32,6 +34,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 +49,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 +76,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 +95,31 @@ 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); + +#ifdef WIN32 +typedef HANDLE qemu_pixman_shareable; +#define SHAREABLE_NONE (NULL) +#define SHAREABLE_TO_PTR(handle) (handle) +#define PTR_TO_SHAREABLE(ptr) (ptr) +#else +typedef int qemu_pixman_shareable; +#define SHAREABLE_NONE (-1) +#define SHAREABLE_TO_PTR(handle) GINT_TO_POINTER(handle) +#define PTR_TO_SHAREABLE(ptr) GPOINTER_TO_INT(ptr) +#endif + +bool qemu_pixman_image_new_shareable( + pixman_image_t **image, + qemu_pixman_shareable *handle, + const char *name, + pixman_format_code_t format, + int width, + int height, + int rowstride_bytes, + Error **errp); 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..dbe6e3d973 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -42,6 +42,7 @@ struct sdl2_console { int updates; int idle_counter; int ignore_hotkeys; + bool gui_keysym; SDL_GLContext winctx; QKbdState *kbd; #ifdef CONFIG_OPENGL @@ -60,6 +61,7 @@ void sdl2_poll_events(struct sdl2_console *scon); void sdl2_process_key(struct sdl2_console *scon, SDL_KeyboardEvent *ev); +void sdl2_release_modifiers(struct sdl2_console *scon); void sdl2_2d_update(DisplayChangeListener *dcl, int x, int y, int w, int h); @@ -90,7 +92,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..f16f7be8be --- /dev/null +++ b/include/ui/surface.h @@ -0,0 +1,93 @@ +/* + * 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 + qemu_pixman_shareable share_handle; + uint32_t share_handle_offset; +} 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); + +void qemu_displaysurface_set_share_handle(DisplaySurface *surface, + qemu_pixman_shareable handle, + uint32_t offset); + +DisplaySurface *qemu_create_displaysurface(int width, int height); +void qemu_free_displaysurface(DisplaySurface *surface); + +static inline int surface_is_allocated(DisplaySurface *surface) +{ + return surface->flags & QEMU_ALLOCATED_FLAG; +} + +static inline int surface_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/exec/user/abitypes.h b/include/user/abitypes.h similarity index 82% rename from include/exec/user/abitypes.h rename to include/user/abitypes.h index 743b8bb9ea..7528124b62 100644 --- a/include/exec/user/abitypes.h +++ b/include/user/abitypes.h @@ -1,7 +1,13 @@ -#ifndef EXEC_USER_ABITYPES_H -#define EXEC_USER_ABITYPES_H +#ifndef USER_ABITYPES_H +#define USER_ABITYPES_H -#include "cpu.h" +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +#include "exec/cpu-defs.h" +#include "exec/tswap.h" +#include "user/tswap-target.h" #ifdef TARGET_ABI32 #define TARGET_ABI_BITS 32 @@ -15,7 +21,10 @@ #define ABI_LLONG_ALIGNMENT 2 #endif -#if (defined(TARGET_I386) && !defined(TARGET_X86_64)) || defined(TARGET_SH4) +#if (defined(TARGET_I386) && !defined(TARGET_X86_64)) \ + || defined(TARGET_SH4) \ + || defined(TARGET_OPENRISC) \ + || defined(TARGET_MICROBLAZE) #define ABI_LLONG_ALIGNMENT 4 #endif diff --git a/include/user/guest-base.h b/include/user/guest-base.h new file mode 100644 index 0000000000..055c1d14fe --- /dev/null +++ b/include/user/guest-base.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Declaration of guest_base. + * Copyright (c) 2003 Fabrice Bellard + */ + +#ifndef USER_GUEST_BASE_H +#define USER_GUEST_BASE_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif + +extern uintptr_t guest_base; + +extern bool have_guest_base; + +#endif diff --git a/include/user/safe-syscall.h b/include/user/safe-syscall.h index ddceef12e2..aa075f4d5c 100644 --- a/include/user/safe-syscall.h +++ b/include/user/safe-syscall.h @@ -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..9bd7ca19c8 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 "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/include/exec/user/thunk.h b/include/user/thunk.h similarity index 86% rename from include/exec/user/thunk.h rename to include/user/thunk.h index 300a840d58..2a2104b568 100644 --- a/include/exec/user/thunk.h +++ b/include/user/thunk.h @@ -17,11 +17,15 @@ * License along with this library; if not, see . */ -#ifndef THUNK_H -#define THUNK_H +#ifndef USER_THUNK_H +#define USER_THUNK_H + +#ifndef CONFIG_USER_ONLY +#error Cannot include this header from system emulation +#endif #include "cpu.h" -#include "exec/user/abitypes.h" +#include "user/abitypes.h" /* types enums definitions */ @@ -111,8 +115,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 +196,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/user/tswap-target.h b/include/user/tswap-target.h new file mode 100644 index 0000000000..4719330dbb --- /dev/null +++ b/include/user/tswap-target.h @@ -0,0 +1,22 @@ +/* + * target-specific swap() definitions + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef USER_TSWAP_H +#define USER_TSWAP_H + +#include "exec/cpu-defs.h" +#include "exec/tswap.h" + +#if TARGET_LONG_SIZE == 4 +#define tswapl(s) tswap32(s) +#define bswaptls(s) bswap32s(s) +#else +#define tswapl(s) tswap64(s) +#define bswaptls(s) bswap64s(s) +#endif + +#endif 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 74516252ba..6d5f64e146 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -20,6 +20,7 @@ #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" @@ -203,6 +204,7 @@ static ssize_t qio_channel_command_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc); @@ -330,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..2ea8d08360 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, @@ -51,14 +68,20 @@ qio_channel_file_new_path(const char *path, ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); - ioc->fd = qemu_open_old(path, flags, mode); + if (flags & O_CREAT) { + ioc->fd = qemu_create(path, flags & ~O_CREAT, mode, errp); + } else { + ioc->fd = qemu_open(path, flags, errp); + } if (ioc->fd < 0) { object_unref(OBJECT(ioc)); - error_setg_errno(errp, errno, - "Unable to open %s", 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 +109,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 +160,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 +267,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 +297,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 index 75e3781507..ef99586348 100644 --- a/io/channel-null.c +++ b/io/channel-null.c @@ -60,6 +60,7 @@ qio_channel_null_readv(QIOChannel *ioc, size_t niov, int **fds G_GNUC_UNUSED, size_t *nfds G_GNUC_UNUSED, + int flags, Error **errp) { QIOChannelNull *nioc = QIO_CHANNEL_NULL(ioc); @@ -127,8 +128,9 @@ qio_channel_null_close(QIOChannel *ioc, static void qio_channel_null_set_aio_fd_handler(QIOChannel *ioc G_GNUC_UNUSED, - AioContext *ctx 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) { diff --git a/io/channel-socket.c b/io/channel-socket.c index b76dca9cc1..608bcf066e 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) { @@ -624,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; @@ -636,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) { @@ -764,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; @@ -818,6 +841,33 @@ qio_channel_socket_set_cork(QIOChannel *ioc, socket_set_cork(sioc->fd, v); } +static int +qio_channel_socket_get_peerpid(QIOChannel *ioc, + unsigned int *pid, + Error **errp) +{ +#ifdef CONFIG_LINUX + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + Error *err = NULL; + socklen_t len = sizeof(struct ucred); + + struct ucred cred; + if (getsockopt(sioc->fd, + SOL_SOCKET, SO_PEERCRED, + &cred, &len) == -1) { + error_setg_errno(&err, errno, "Unable to get peer credentials"); + error_propagate(errp, err); + *pid = -1; + return -1; + } + *pid = (unsigned int)cred.pid; + return 0; +#else + error_setg(errp, "Unsupported feature"); + *pid = -1; + return -1; +#endif +} static int qio_channel_socket_close(QIOChannel *ioc, @@ -829,13 +879,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); @@ -876,14 +926,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, @@ -912,6 +965,7 @@ static void qio_channel_socket_class_init(ObjectClass *klass, #ifdef QEMU_MSG_ZEROCOPY ioc_klass->io_flush = qio_channel_socket_flush; #endif + ioc_klass->io_peerpid = qio_channel_socket_get_peerpid; } static const TypeInfo qio_channel_socket_info = { diff --git a/io/channel-tls.c b/io/channel-tls.c index 4ce08ccc28..aab630e5ae 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -28,17 +28,16 @@ static ssize_t qio_channel_tls_write_handler(const char *buf, size_t len, - void *opaque) + void *opaque, + Error **errp) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); ssize_t ret; - ret = qio_channel_write(tioc->master, buf, len, NULL); + ret = qio_channel_write(tioc->master, buf, len, errp); if (ret == QIO_CHANNEL_ERR_BLOCK) { - errno = EAGAIN; - return -1; + return QCRYPTO_TLS_SESSION_ERR_BLOCK; } else if (ret < 0) { - errno = EIO; return -1; } return ret; @@ -46,17 +45,16 @@ static ssize_t qio_channel_tls_write_handler(const char *buf, static ssize_t qio_channel_tls_read_handler(char *buf, size_t len, - void *opaque) + void *opaque, + Error **errp) { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); ssize_t ret; - ret = qio_channel_read(tioc->master, buf, len, NULL); + ret = qio_channel_read(tioc->master, buf, len, errp); if (ret == QIO_CHANNEL_ERR_BLOCK) { - errno = EAGAIN; - return -1; + return QCRYPTO_TLS_SESSION_ERR_BLOCK; } else if (ret < 0) { - errno = EIO; return -1; } return ret; @@ -69,37 +67,40 @@ qio_channel_tls_new_server(QIOChannel *master, const char *aclname, Error **errp) { - QIOChannelTLS *ioc; + QIOChannelTLS *tioc; + QIOChannel *ioc; - ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); + tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); + ioc = QIO_CHANNEL(tioc); - ioc->master = master; + tioc->master = master; + ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { - qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SHUTDOWN); + qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } object_ref(OBJECT(master)); - ioc->session = qcrypto_tls_session_new( + tioc->session = qcrypto_tls_session_new( creds, NULL, aclname, QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, errp); - if (!ioc->session) { + if (!tioc->session) { goto error; } qcrypto_tls_session_set_callbacks( - ioc->session, + tioc->session, qio_channel_tls_write_handler, qio_channel_tls_read_handler, - ioc); + tioc); - trace_qio_channel_tls_new_server(ioc, master, creds, aclname); - return ioc; + trace_qio_channel_tls_new_server(tioc, master, creds, aclname); + return tioc; error: - object_unref(OBJECT(ioc)); + object_unref(OBJECT(tioc)); return NULL; } @@ -116,6 +117,7 @@ qio_channel_tls_new_client(QIOChannel *master, ioc = QIO_CHANNEL(tioc); tioc->master = master; + ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } @@ -198,12 +200,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); } } @@ -218,6 +221,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); @@ -263,6 +267,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); @@ -270,24 +275,19 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, ssize_t got = 0; for (i = 0 ; i < niov ; i++) { - ssize_t ret = qcrypto_tls_session_read(tioc->session, - iov[i].iov_base, - iov[i].iov_len); - if (ret < 0) { - if (errno == EAGAIN) { - if (got) { - return got; - } else { - return QIO_CHANNEL_ERR_BLOCK; - } - } else if (errno == ECONNABORTED && - (qatomic_load_acquire(&tioc->shutdown) & - QIO_CHANNEL_SHUTDOWN_READ)) { - return 0; + ssize_t ret = qcrypto_tls_session_read( + tioc->session, + iov[i].iov_base, + iov[i].iov_len, + qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, + errp); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + if (got) { + return got; + } else { + return QIO_CHANNEL_ERR_BLOCK; } - - error_setg_errno(errp, errno, - "Cannot read from TLS channel"); + } else if (ret < 0) { return -1; } got += ret; @@ -314,18 +314,15 @@ static ssize_t qio_channel_tls_writev(QIOChannel *ioc, for (i = 0 ; i < niov ; i++) { ssize_t ret = qcrypto_tls_session_write(tioc->session, iov[i].iov_base, - iov[i].iov_len); - if (ret <= 0) { - if (errno == EAGAIN) { - if (done) { - return done; - } else { - return QIO_CHANNEL_ERR_BLOCK; - } + iov[i].iov_len, + errp); + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { + if (done) { + return done; + } else { + return QIO_CHANNEL_ERR_BLOCK; } - - error_setg_errno(errp, errno, - "Cannot write to TLS channel"); + } else if (ret < 0) { return -1; } done += ret; @@ -377,26 +374,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 ad7c568a84..64b486e378 100644 --- a/io/channel-watch.c +++ b/io/channel-watch.c @@ -275,15 +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; - WSAEventSelect(socket, ioc->event, - FD_READ | FD_ACCEPT | FD_CLOSE | - FD_CONNECT | FD_WRITE | FD_OOB); + 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)); @@ -293,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 fb4932ade7..55192b770a 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -351,7 +351,7 @@ static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc, QIO_CHANNEL_WEBSOCK_GUID_LEN + 1); /* hash and encode it */ - if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1, + if (qcrypto_hash_base64(QCRYPTO_HASH_ALGO_SHA1, combined_key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + QIO_CHANNEL_WEBSOCK_GUID_LEN, @@ -883,6 +883,7 @@ qio_channel_websock_new_server(QIOChannel *master) ioc = QIO_CHANNEL(wioc); wioc->master = master; + ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); } @@ -1081,6 +1082,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..e3f17c24a0 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) @@ -473,6 +548,19 @@ void qio_channel_set_cork(QIOChannel *ioc, } } +int qio_channel_get_peerpid(QIOChannel *ioc, + unsigned int *pid, + Error **errp) +{ + QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); + + if (!klass->io_peerpid) { + error_setg(errp, "Channel does not support peer pid"); + return -1; + } + klass->io_peerpid(ioc, pid, errp); + return 0; +} off_t qio_channel_io_seek(QIOChannel *ioc, off_t offset, @@ -506,7 +594,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 +609,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 +621,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 +776,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 283b9b2bdb..1164812f91 100644 --- a/io/meson.build +++ b/io/meson.build @@ -13,4 +13,4 @@ io_ss.add(files( 'dns-resolver.c', 'net-listener.c', 'task.c', -), gnutls) +)) 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 3cc5cf1efd..d4c0f84a9a 100644 --- a/io/trace-events +++ b/io/trace-events @@ -43,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 d498fc89c0..9e26fa899f 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -156,8 +156,7 @@ static JobInfo *job_query_single_locked(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, }; diff --git a/job.c b/job.c index 72d57f0934..660ce22c56 100644 --- a/job.c +++ b/job.c @@ -80,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 */ @@ -463,12 +464,8 @@ void job_unref_locked(Job *job) assert(!job->txn); if (job->driver->free) { - AioContext *aio_context = job->aio_context; job_unlock(); - /* FIXME: aiocontext lock is required because cb calls blk_unref */ - aio_context_acquire(aio_context); job->driver->free(job); - aio_context_release(aio_context); job_lock(); } @@ -839,12 +836,10 @@ static void job_clean(Job *job) /* * Called with job_mutex held, but releases it temporarily. - * Takes AioContext lock internally to invoke a job->driver callback. */ static int job_finalize_single_locked(Job *job) { int job_ret; - AioContext *ctx = job->aio_context; assert(job_is_completed_locked(job)); @@ -853,7 +848,6 @@ static int job_finalize_single_locked(Job *job) job_ret = job->ret; job_unlock(); - aio_context_acquire(ctx); if (!job_ret) { job_commit(job); @@ -866,7 +860,6 @@ static int job_finalize_single_locked(Job *job) job->cb(job->opaque, job_ret); } - aio_context_release(ctx); job_lock(); /* Emit events only if we actually started */ @@ -885,17 +878,13 @@ static int job_finalize_single_locked(Job *job) /* * Called with job_mutex held, but releases it temporarily. - * Takes AioContext lock internally to invoke a job->driver callback. */ static void job_cancel_async_locked(Job *job, bool force) { - AioContext *ctx = job->aio_context; GLOBAL_STATE_CODE(); if (job->driver->cancel) { job_unlock(); - aio_context_acquire(ctx); force = job->driver->cancel(job, force); - aio_context_release(ctx); job_lock(); } else { /* No .cancel() means the job will behave as if force-cancelled */ @@ -930,7 +919,6 @@ static void job_cancel_async_locked(Job *job, bool force) /* * Called with job_mutex held, but releases it temporarily. - * Takes AioContext lock internally to invoke a job->driver callback. */ static void job_completed_txn_abort_locked(Job *job) { @@ -978,15 +966,12 @@ static void job_completed_txn_abort_locked(Job *job) static int job_prepare_locked(Job *job) { int ret; - AioContext *ctx = job->aio_context; GLOBAL_STATE_CODE(); if (job->ret == 0 && job->driver->prepare) { job_unlock(); - aio_context_acquire(ctx); ret = job->driver->prepare(job); - aio_context_release(ctx); job_lock(); job->ret = ret; job_update_rc_locked(job); diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 4bf2d7246e..2af9931ae9 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -37,12 +37,11 @@ #include #include -#define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_READONLY_MEM #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,11 +74,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 GENMASK(KVM_ARM_DEVICE_TYPE_SHIFT + 15, \ - 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 GENMASK(KVM_ARM_DEVICE_ID_SHIFT + 15, \ - 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 @@ -108,6 +107,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; @@ -160,6 +160,11 @@ struct kvm_sync_regs { __u64 device_irq_level; }; +/* Bits for run->s.regs.device_irq_level */ +#define KVM_ARM_DEV_EL1_VTIMER (1 << 0) +#define KVM_ARM_DEV_EL1_PTIMER (1 << 1) +#define KVM_ARM_DEV_PMU (1 << 2) + /* * PMU filter structure. Describe a range of events with a particular * action. To be used with KVM_ARM_VCPU_PMU_V3_FILTER. @@ -196,6 +201,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 @@ -361,6 +375,10 @@ enum { 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 @@ -400,6 +418,8 @@ enum { #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 @@ -456,6 +476,56 @@ enum { /* 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-arm64/mman.h b/linux-headers/asm-arm64/mman.h index d0dbfe9587..7b500a3a7b 100644 --- a/linux-headers/asm-arm64/mman.h +++ b/linux-headers/asm-arm64/mman.h @@ -7,4 +7,13 @@ #define PROT_BTI 0x10 /* BTI guarded page */ #define PROT_MTE 0x20 /* Normal Tagged mapping */ +/* Override any generic PKEY permission defines */ +#define PKEY_DISABLE_EXECUTE 0x4 +#define PKEY_DISABLE_READ 0x8 +#undef PKEY_ACCESS_MASK +#define PKEY_ACCESS_MASK (PKEY_DISABLE_ACCESS |\ + PKEY_DISABLE_WRITE |\ + PKEY_DISABLE_READ |\ + PKEY_DISABLE_EXECUTE) + #endif /* ! _UAPI__ASM_MMAN_H */ diff --git a/linux-headers/asm-arm64/sve_context.h b/linux-headers/asm-arm64/sve_context.h index 1d0e3e1d09..d1b1ec8cb1 100644 --- a/linux-headers/asm-arm64/sve_context.h +++ b/linux-headers/asm-arm64/sve_context.h @@ -13,6 +13,17 @@ #define __SVE_VQ_BYTES 16 /* number of bytes per quadword */ +/* + * Yes, __SVE_VQ_MAX is 512 QUADWORDS. + * + * To help ensure forward portability, this is much larger than the + * current maximum value defined by the SVE architecture. While arrays + * or static allocations can be sized based on this value, watch out! + * It will waste a surprisingly large amount of memory. + * + * Dynamic sizing based on the actual runtime vector length is likely to + * be preferable for most purposes. + */ #define __SVE_VQ_MIN 1 #define __SVE_VQ_MAX 512 diff --git a/linux-headers/asm-arm64/unistd.h b/linux-headers/asm-arm64/unistd.h index ce2ee8f1e3..df36f23876 100644 --- a/linux-headers/asm-arm64/unistd.h +++ b/linux-headers/asm-arm64/unistd.h @@ -1,25 +1,2 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Copyright (C) 2012 ARM Ltd. - * - * 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 . - */ - -#define __ARCH_WANT_RENAMEAT -#define __ARCH_WANT_NEW_STAT -#define __ARCH_WANT_SET_GET_RLIMIT -#define __ARCH_WANT_TIME32_SYSCALLS -#define __ARCH_WANT_SYS_CLONE3 -#define __ARCH_WANT_MEMFD_SECRET - -#include +#include diff --git a/linux-headers/asm-arm64/unistd_64.h b/linux-headers/asm-arm64/unistd_64.h new file mode 100644 index 0000000000..99a1d70459 --- /dev/null +++ b/linux-headers/asm-arm64/unistd_64.h @@ -0,0 +1,324 @@ +#ifndef _ASM_UNISTD_64_H +#define _ASM_UNISTD_64_H + +#define __NR_io_setup 0 +#define __NR_io_destroy 1 +#define __NR_io_submit 2 +#define __NR_io_cancel 3 +#define __NR_io_getevents 4 +#define __NR_setxattr 5 +#define __NR_lsetxattr 6 +#define __NR_fsetxattr 7 +#define __NR_getxattr 8 +#define __NR_lgetxattr 9 +#define __NR_fgetxattr 10 +#define __NR_listxattr 11 +#define __NR_llistxattr 12 +#define __NR_flistxattr 13 +#define __NR_removexattr 14 +#define __NR_lremovexattr 15 +#define __NR_fremovexattr 16 +#define __NR_getcwd 17 +#define __NR_lookup_dcookie 18 +#define __NR_eventfd2 19 +#define __NR_epoll_create1 20 +#define __NR_epoll_ctl 21 +#define __NR_epoll_pwait 22 +#define __NR_dup 23 +#define __NR_dup3 24 +#define __NR_fcntl 25 +#define __NR_inotify_init1 26 +#define __NR_inotify_add_watch 27 +#define __NR_inotify_rm_watch 28 +#define __NR_ioctl 29 +#define __NR_ioprio_set 30 +#define __NR_ioprio_get 31 +#define __NR_flock 32 +#define __NR_mknodat 33 +#define __NR_mkdirat 34 +#define __NR_unlinkat 35 +#define __NR_symlinkat 36 +#define __NR_linkat 37 +#define __NR_renameat 38 +#define __NR_umount2 39 +#define __NR_mount 40 +#define __NR_pivot_root 41 +#define __NR_nfsservctl 42 +#define __NR_statfs 43 +#define __NR_fstatfs 44 +#define __NR_truncate 45 +#define __NR_ftruncate 46 +#define __NR_fallocate 47 +#define __NR_faccessat 48 +#define __NR_chdir 49 +#define __NR_fchdir 50 +#define __NR_chroot 51 +#define __NR_fchmod 52 +#define __NR_fchmodat 53 +#define __NR_fchownat 54 +#define __NR_fchown 55 +#define __NR_openat 56 +#define __NR_close 57 +#define __NR_vhangup 58 +#define __NR_pipe2 59 +#define __NR_quotactl 60 +#define __NR_getdents64 61 +#define __NR_lseek 62 +#define __NR_read 63 +#define __NR_write 64 +#define __NR_readv 65 +#define __NR_writev 66 +#define __NR_pread64 67 +#define __NR_pwrite64 68 +#define __NR_preadv 69 +#define __NR_pwritev 70 +#define __NR_sendfile 71 +#define __NR_pselect6 72 +#define __NR_ppoll 73 +#define __NR_signalfd4 74 +#define __NR_vmsplice 75 +#define __NR_splice 76 +#define __NR_tee 77 +#define __NR_readlinkat 78 +#define __NR_newfstatat 79 +#define __NR_fstat 80 +#define __NR_sync 81 +#define __NR_fsync 82 +#define __NR_fdatasync 83 +#define __NR_sync_file_range 84 +#define __NR_timerfd_create 85 +#define __NR_timerfd_settime 86 +#define __NR_timerfd_gettime 87 +#define __NR_utimensat 88 +#define __NR_acct 89 +#define __NR_capget 90 +#define __NR_capset 91 +#define __NR_personality 92 +#define __NR_exit 93 +#define __NR_exit_group 94 +#define __NR_waitid 95 +#define __NR_set_tid_address 96 +#define __NR_unshare 97 +#define __NR_futex 98 +#define __NR_set_robust_list 99 +#define __NR_get_robust_list 100 +#define __NR_nanosleep 101 +#define __NR_getitimer 102 +#define __NR_setitimer 103 +#define __NR_kexec_load 104 +#define __NR_init_module 105 +#define __NR_delete_module 106 +#define __NR_timer_create 107 +#define __NR_timer_gettime 108 +#define __NR_timer_getoverrun 109 +#define __NR_timer_settime 110 +#define __NR_timer_delete 111 +#define __NR_clock_settime 112 +#define __NR_clock_gettime 113 +#define __NR_clock_getres 114 +#define __NR_clock_nanosleep 115 +#define __NR_syslog 116 +#define __NR_ptrace 117 +#define __NR_sched_setparam 118 +#define __NR_sched_setscheduler 119 +#define __NR_sched_getscheduler 120 +#define __NR_sched_getparam 121 +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#define __NR_sched_yield 124 +#define __NR_sched_get_priority_max 125 +#define __NR_sched_get_priority_min 126 +#define __NR_sched_rr_get_interval 127 +#define __NR_restart_syscall 128 +#define __NR_kill 129 +#define __NR_tkill 130 +#define __NR_tgkill 131 +#define __NR_sigaltstack 132 +#define __NR_rt_sigsuspend 133 +#define __NR_rt_sigaction 134 +#define __NR_rt_sigprocmask 135 +#define __NR_rt_sigpending 136 +#define __NR_rt_sigtimedwait 137 +#define __NR_rt_sigqueueinfo 138 +#define __NR_rt_sigreturn 139 +#define __NR_setpriority 140 +#define __NR_getpriority 141 +#define __NR_reboot 142 +#define __NR_setregid 143 +#define __NR_setgid 144 +#define __NR_setreuid 145 +#define __NR_setuid 146 +#define __NR_setresuid 147 +#define __NR_getresuid 148 +#define __NR_setresgid 149 +#define __NR_getresgid 150 +#define __NR_setfsuid 151 +#define __NR_setfsgid 152 +#define __NR_times 153 +#define __NR_setpgid 154 +#define __NR_getpgid 155 +#define __NR_getsid 156 +#define __NR_setsid 157 +#define __NR_getgroups 158 +#define __NR_setgroups 159 +#define __NR_uname 160 +#define __NR_sethostname 161 +#define __NR_setdomainname 162 +#define __NR_getrlimit 163 +#define __NR_setrlimit 164 +#define __NR_getrusage 165 +#define __NR_umask 166 +#define __NR_prctl 167 +#define __NR_getcpu 168 +#define __NR_gettimeofday 169 +#define __NR_settimeofday 170 +#define __NR_adjtimex 171 +#define __NR_getpid 172 +#define __NR_getppid 173 +#define __NR_getuid 174 +#define __NR_geteuid 175 +#define __NR_getgid 176 +#define __NR_getegid 177 +#define __NR_gettid 178 +#define __NR_sysinfo 179 +#define __NR_mq_open 180 +#define __NR_mq_unlink 181 +#define __NR_mq_timedsend 182 +#define __NR_mq_timedreceive 183 +#define __NR_mq_notify 184 +#define __NR_mq_getsetattr 185 +#define __NR_msgget 186 +#define __NR_msgctl 187 +#define __NR_msgrcv 188 +#define __NR_msgsnd 189 +#define __NR_semget 190 +#define __NR_semctl 191 +#define __NR_semtimedop 192 +#define __NR_semop 193 +#define __NR_shmget 194 +#define __NR_shmctl 195 +#define __NR_shmat 196 +#define __NR_shmdt 197 +#define __NR_socket 198 +#define __NR_socketpair 199 +#define __NR_bind 200 +#define __NR_listen 201 +#define __NR_accept 202 +#define __NR_connect 203 +#define __NR_getsockname 204 +#define __NR_getpeername 205 +#define __NR_sendto 206 +#define __NR_recvfrom 207 +#define __NR_setsockopt 208 +#define __NR_getsockopt 209 +#define __NR_shutdown 210 +#define __NR_sendmsg 211 +#define __NR_recvmsg 212 +#define __NR_readahead 213 +#define __NR_brk 214 +#define __NR_munmap 215 +#define __NR_mremap 216 +#define __NR_add_key 217 +#define __NR_request_key 218 +#define __NR_keyctl 219 +#define __NR_clone 220 +#define __NR_execve 221 +#define __NR_mmap 222 +#define __NR_fadvise64 223 +#define __NR_swapon 224 +#define __NR_swapoff 225 +#define __NR_mprotect 226 +#define __NR_msync 227 +#define __NR_mlock 228 +#define __NR_munlock 229 +#define __NR_mlockall 230 +#define __NR_munlockall 231 +#define __NR_mincore 232 +#define __NR_madvise 233 +#define __NR_remap_file_pages 234 +#define __NR_mbind 235 +#define __NR_get_mempolicy 236 +#define __NR_set_mempolicy 237 +#define __NR_migrate_pages 238 +#define __NR_move_pages 239 +#define __NR_rt_tgsigqueueinfo 240 +#define __NR_perf_event_open 241 +#define __NR_accept4 242 +#define __NR_recvmmsg 243 +#define __NR_wait4 260 +#define __NR_prlimit64 261 +#define __NR_fanotify_init 262 +#define __NR_fanotify_mark 263 +#define __NR_name_to_handle_at 264 +#define __NR_open_by_handle_at 265 +#define __NR_clock_adjtime 266 +#define __NR_syncfs 267 +#define __NR_setns 268 +#define __NR_sendmmsg 269 +#define __NR_process_vm_readv 270 +#define __NR_process_vm_writev 271 +#define __NR_kcmp 272 +#define __NR_finit_module 273 +#define __NR_sched_setattr 274 +#define __NR_sched_getattr 275 +#define __NR_renameat2 276 +#define __NR_seccomp 277 +#define __NR_getrandom 278 +#define __NR_memfd_create 279 +#define __NR_bpf 280 +#define __NR_execveat 281 +#define __NR_userfaultfd 282 +#define __NR_membarrier 283 +#define __NR_mlock2 284 +#define __NR_copy_file_range 285 +#define __NR_preadv2 286 +#define __NR_pwritev2 287 +#define __NR_pkey_mprotect 288 +#define __NR_pkey_alloc 289 +#define __NR_pkey_free 290 +#define __NR_statx 291 +#define __NR_io_pgetevents 292 +#define __NR_rseq 293 +#define __NR_kexec_file_load 294 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 +#define __NR_close_range 436 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 +#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 +#define __NR_mseal 462 + + +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h index 0aac245b6b..1fb4f0c9f2 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,12 @@ * 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 + +#ifndef __BITS_PER_LONG_LONG +#define __BITS_PER_LONG_LONG 64 +#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 45fa180cc5..5bf6148cac 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 __SC_COMP(__NR_ptrace, sys_ptrace, compat_sys_ptrace) - -/* kernel/sched/core.c */ #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,15 +698,19 @@ __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 defined(__SYSCALL_COMPAT) || __BITS_PER_LONG == 32 #define __NR_clock_gettime64 403 __SYSCALL(__NR_clock_gettime64, sys_clock_gettime) @@ -805,7 +737,7 @@ __SC_COMP(__NR_pselect6_time64, sys_pselect6, compat_sys_pselect6_time64) #define __NR_ppoll_time64 414 __SC_COMP(__NR_ppoll_time64, sys_ppoll, compat_sys_ppoll_time64) #define __NR_io_pgetevents_time64 416 -__SYSCALL(__NR_io_pgetevents_time64, sys_io_pgetevents) +__SC_COMP(__NR_io_pgetevents_time64, sys_io_pgetevents, compat_sys_io_pgetevents_time64) #define __NR_recvmmsg_time64 417 __SC_COMP(__NR_recvmmsg_time64, sys_recvmmsg, compat_sys_recvmmsg_time64) #define __NR_mq_timedsend_time64 418 @@ -844,13 +776,10 @@ __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 +794,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 +805,44 @@ __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) + +#define __NR_mseal 462 +__SYSCALL(__NR_mseal, sys_mseal) #undef __NR_syscalls -#define __NR_syscalls 451 +#define __NR_syscalls 463 /* * 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..70d89070bf --- /dev/null +++ b/linux-headers/asm-loongarch/kvm.h @@ -0,0 +1,135 @@ +/* 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_COALESCED_MMIO_PAGE_OFFSET 1 +#define KVM_DIRTY_LOG_PAGE_OFFSET 64 + +#define KVM_GUESTDBG_USE_SW_BP 0x00010000 + +/* + * 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_LBT (KVM_REG_LOONGARCH | 0x50000ULL) +#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) +/* Debugging: Special instruction for software breakpoint */ +#define KVM_REG_LOONGARCH_DEBUG_INST (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3) + +/* LBT registers */ +#define KVM_REG_LOONGARCH_LBT_SCR0 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 1) +#define KVM_REG_LOONGARCH_LBT_SCR1 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 2) +#define KVM_REG_LOONGARCH_LBT_SCR2 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 3) +#define KVM_REG_LOONGARCH_LBT_SCR3 (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 4) +#define KVM_REG_LOONGARCH_LBT_EFLAGS (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 5) +#define KVM_REG_LOONGARCH_LBT_FTOP (KVM_REG_LOONGARCH_LBT | KVM_REG_SIZE_U64 | 6) + +#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) + +/* Device Control API on vm fd */ +#define KVM_LOONGARCH_VM_FEAT_CTRL 0 +#define KVM_LOONGARCH_VM_FEAT_LSX 0 +#define KVM_LOONGARCH_VM_FEAT_LASX 1 +#define KVM_LOONGARCH_VM_FEAT_X86BT 2 +#define KVM_LOONGARCH_VM_FEAT_ARMBT 3 +#define KVM_LOONGARCH_VM_FEAT_MIPSBT 4 +#define KVM_LOONGARCH_VM_FEAT_PMU 5 +#define KVM_LOONGARCH_VM_FEAT_PV_IPI 6 +#define KVM_LOONGARCH_VM_FEAT_PV_STEALTIME 7 + +/* Device Control API on vcpu fd */ +#define KVM_LOONGARCH_VCPU_CPUCFG 0 +#define KVM_LOONGARCH_VCPU_PVTIME_CTRL 1 +#define KVM_LOONGARCH_VCPU_PVTIME_GPA 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/kvm_para.h b/linux-headers/asm-loongarch/kvm_para.h new file mode 100644 index 0000000000..4ba4ad8db1 --- /dev/null +++ b/linux-headers/asm-loongarch/kvm_para.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_KVM_PARA_H +#define _ASM_KVM_PARA_H + +#include + +/* + * CPUCFG index area: 0x40000000 -- 0x400000ff + * SW emulation for KVM hypervirsor + */ +#define CPUCFG_KVM_BASE 0x40000000 +#define CPUCFG_KVM_SIZE 0x100 +#define CPUCFG_KVM_SIG (CPUCFG_KVM_BASE + 0) +#define KVM_SIGNATURE "KVM\0" +#define CPUCFG_KVM_FEATURE (CPUCFG_KVM_BASE + 4) +#define KVM_FEATURE_IPI 1 +#define KVM_FEATURE_STEAL_TIME 2 +/* BIT 24 - 31 are features configurable by user space vmm */ +#define KVM_FEATURE_VIRT_EXTIOI 24 + +#endif /* _ASM_KVM_PARA_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..1f01980f9c --- /dev/null +++ b/linux-headers/asm-loongarch/unistd.h @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ + +#include diff --git a/linux-headers/asm-loongarch/unistd_64.h b/linux-headers/asm-loongarch/unistd_64.h new file mode 100644 index 0000000000..887ea50cca --- /dev/null +++ b/linux-headers/asm-loongarch/unistd_64.h @@ -0,0 +1,320 @@ +#ifndef _ASM_UNISTD_64_H +#define _ASM_UNISTD_64_H + +#define __NR_io_setup 0 +#define __NR_io_destroy 1 +#define __NR_io_submit 2 +#define __NR_io_cancel 3 +#define __NR_io_getevents 4 +#define __NR_setxattr 5 +#define __NR_lsetxattr 6 +#define __NR_fsetxattr 7 +#define __NR_getxattr 8 +#define __NR_lgetxattr 9 +#define __NR_fgetxattr 10 +#define __NR_listxattr 11 +#define __NR_llistxattr 12 +#define __NR_flistxattr 13 +#define __NR_removexattr 14 +#define __NR_lremovexattr 15 +#define __NR_fremovexattr 16 +#define __NR_getcwd 17 +#define __NR_lookup_dcookie 18 +#define __NR_eventfd2 19 +#define __NR_epoll_create1 20 +#define __NR_epoll_ctl 21 +#define __NR_epoll_pwait 22 +#define __NR_dup 23 +#define __NR_dup3 24 +#define __NR_fcntl 25 +#define __NR_inotify_init1 26 +#define __NR_inotify_add_watch 27 +#define __NR_inotify_rm_watch 28 +#define __NR_ioctl 29 +#define __NR_ioprio_set 30 +#define __NR_ioprio_get 31 +#define __NR_flock 32 +#define __NR_mknodat 33 +#define __NR_mkdirat 34 +#define __NR_unlinkat 35 +#define __NR_symlinkat 36 +#define __NR_linkat 37 +#define __NR_umount2 39 +#define __NR_mount 40 +#define __NR_pivot_root 41 +#define __NR_nfsservctl 42 +#define __NR_statfs 43 +#define __NR_fstatfs 44 +#define __NR_truncate 45 +#define __NR_ftruncate 46 +#define __NR_fallocate 47 +#define __NR_faccessat 48 +#define __NR_chdir 49 +#define __NR_fchdir 50 +#define __NR_chroot 51 +#define __NR_fchmod 52 +#define __NR_fchmodat 53 +#define __NR_fchownat 54 +#define __NR_fchown 55 +#define __NR_openat 56 +#define __NR_close 57 +#define __NR_vhangup 58 +#define __NR_pipe2 59 +#define __NR_quotactl 60 +#define __NR_getdents64 61 +#define __NR_lseek 62 +#define __NR_read 63 +#define __NR_write 64 +#define __NR_readv 65 +#define __NR_writev 66 +#define __NR_pread64 67 +#define __NR_pwrite64 68 +#define __NR_preadv 69 +#define __NR_pwritev 70 +#define __NR_sendfile 71 +#define __NR_pselect6 72 +#define __NR_ppoll 73 +#define __NR_signalfd4 74 +#define __NR_vmsplice 75 +#define __NR_splice 76 +#define __NR_tee 77 +#define __NR_readlinkat 78 +#define __NR_newfstatat 79 +#define __NR_fstat 80 +#define __NR_sync 81 +#define __NR_fsync 82 +#define __NR_fdatasync 83 +#define __NR_sync_file_range 84 +#define __NR_timerfd_create 85 +#define __NR_timerfd_settime 86 +#define __NR_timerfd_gettime 87 +#define __NR_utimensat 88 +#define __NR_acct 89 +#define __NR_capget 90 +#define __NR_capset 91 +#define __NR_personality 92 +#define __NR_exit 93 +#define __NR_exit_group 94 +#define __NR_waitid 95 +#define __NR_set_tid_address 96 +#define __NR_unshare 97 +#define __NR_futex 98 +#define __NR_set_robust_list 99 +#define __NR_get_robust_list 100 +#define __NR_nanosleep 101 +#define __NR_getitimer 102 +#define __NR_setitimer 103 +#define __NR_kexec_load 104 +#define __NR_init_module 105 +#define __NR_delete_module 106 +#define __NR_timer_create 107 +#define __NR_timer_gettime 108 +#define __NR_timer_getoverrun 109 +#define __NR_timer_settime 110 +#define __NR_timer_delete 111 +#define __NR_clock_settime 112 +#define __NR_clock_gettime 113 +#define __NR_clock_getres 114 +#define __NR_clock_nanosleep 115 +#define __NR_syslog 116 +#define __NR_ptrace 117 +#define __NR_sched_setparam 118 +#define __NR_sched_setscheduler 119 +#define __NR_sched_getscheduler 120 +#define __NR_sched_getparam 121 +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#define __NR_sched_yield 124 +#define __NR_sched_get_priority_max 125 +#define __NR_sched_get_priority_min 126 +#define __NR_sched_rr_get_interval 127 +#define __NR_restart_syscall 128 +#define __NR_kill 129 +#define __NR_tkill 130 +#define __NR_tgkill 131 +#define __NR_sigaltstack 132 +#define __NR_rt_sigsuspend 133 +#define __NR_rt_sigaction 134 +#define __NR_rt_sigprocmask 135 +#define __NR_rt_sigpending 136 +#define __NR_rt_sigtimedwait 137 +#define __NR_rt_sigqueueinfo 138 +#define __NR_rt_sigreturn 139 +#define __NR_setpriority 140 +#define __NR_getpriority 141 +#define __NR_reboot 142 +#define __NR_setregid 143 +#define __NR_setgid 144 +#define __NR_setreuid 145 +#define __NR_setuid 146 +#define __NR_setresuid 147 +#define __NR_getresuid 148 +#define __NR_setresgid 149 +#define __NR_getresgid 150 +#define __NR_setfsuid 151 +#define __NR_setfsgid 152 +#define __NR_times 153 +#define __NR_setpgid 154 +#define __NR_getpgid 155 +#define __NR_getsid 156 +#define __NR_setsid 157 +#define __NR_getgroups 158 +#define __NR_setgroups 159 +#define __NR_uname 160 +#define __NR_sethostname 161 +#define __NR_setdomainname 162 +#define __NR_getrusage 165 +#define __NR_umask 166 +#define __NR_prctl 167 +#define __NR_getcpu 168 +#define __NR_gettimeofday 169 +#define __NR_settimeofday 170 +#define __NR_adjtimex 171 +#define __NR_getpid 172 +#define __NR_getppid 173 +#define __NR_getuid 174 +#define __NR_geteuid 175 +#define __NR_getgid 176 +#define __NR_getegid 177 +#define __NR_gettid 178 +#define __NR_sysinfo 179 +#define __NR_mq_open 180 +#define __NR_mq_unlink 181 +#define __NR_mq_timedsend 182 +#define __NR_mq_timedreceive 183 +#define __NR_mq_notify 184 +#define __NR_mq_getsetattr 185 +#define __NR_msgget 186 +#define __NR_msgctl 187 +#define __NR_msgrcv 188 +#define __NR_msgsnd 189 +#define __NR_semget 190 +#define __NR_semctl 191 +#define __NR_semtimedop 192 +#define __NR_semop 193 +#define __NR_shmget 194 +#define __NR_shmctl 195 +#define __NR_shmat 196 +#define __NR_shmdt 197 +#define __NR_socket 198 +#define __NR_socketpair 199 +#define __NR_bind 200 +#define __NR_listen 201 +#define __NR_accept 202 +#define __NR_connect 203 +#define __NR_getsockname 204 +#define __NR_getpeername 205 +#define __NR_sendto 206 +#define __NR_recvfrom 207 +#define __NR_setsockopt 208 +#define __NR_getsockopt 209 +#define __NR_shutdown 210 +#define __NR_sendmsg 211 +#define __NR_recvmsg 212 +#define __NR_readahead 213 +#define __NR_brk 214 +#define __NR_munmap 215 +#define __NR_mremap 216 +#define __NR_add_key 217 +#define __NR_request_key 218 +#define __NR_keyctl 219 +#define __NR_clone 220 +#define __NR_execve 221 +#define __NR_mmap 222 +#define __NR_fadvise64 223 +#define __NR_swapon 224 +#define __NR_swapoff 225 +#define __NR_mprotect 226 +#define __NR_msync 227 +#define __NR_mlock 228 +#define __NR_munlock 229 +#define __NR_mlockall 230 +#define __NR_munlockall 231 +#define __NR_mincore 232 +#define __NR_madvise 233 +#define __NR_remap_file_pages 234 +#define __NR_mbind 235 +#define __NR_get_mempolicy 236 +#define __NR_set_mempolicy 237 +#define __NR_migrate_pages 238 +#define __NR_move_pages 239 +#define __NR_rt_tgsigqueueinfo 240 +#define __NR_perf_event_open 241 +#define __NR_accept4 242 +#define __NR_recvmmsg 243 +#define __NR_wait4 260 +#define __NR_prlimit64 261 +#define __NR_fanotify_init 262 +#define __NR_fanotify_mark 263 +#define __NR_name_to_handle_at 264 +#define __NR_open_by_handle_at 265 +#define __NR_clock_adjtime 266 +#define __NR_syncfs 267 +#define __NR_setns 268 +#define __NR_sendmmsg 269 +#define __NR_process_vm_readv 270 +#define __NR_process_vm_writev 271 +#define __NR_kcmp 272 +#define __NR_finit_module 273 +#define __NR_sched_setattr 274 +#define __NR_sched_getattr 275 +#define __NR_renameat2 276 +#define __NR_seccomp 277 +#define __NR_getrandom 278 +#define __NR_memfd_create 279 +#define __NR_bpf 280 +#define __NR_execveat 281 +#define __NR_userfaultfd 282 +#define __NR_membarrier 283 +#define __NR_mlock2 284 +#define __NR_copy_file_range 285 +#define __NR_preadv2 286 +#define __NR_pwritev2 287 +#define __NR_pkey_mprotect 288 +#define __NR_pkey_alloc 289 +#define __NR_pkey_free 290 +#define __NR_statx 291 +#define __NR_io_pgetevents 292 +#define __NR_rseq 293 +#define __NR_kexec_file_load 294 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 +#define __NR_close_range 436 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 +#define __NR_landlock_create_ruleset 444 +#define __NR_landlock_add_rule 445 +#define __NR_landlock_restrict_self 446 +#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 +#define __NR_mseal 462 + + +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index edcf717c43..9673dc9cb3 100644 --- a/linux-headers/asm-mips/kvm.h +++ b/linux-headers/asm-mips/kvm.h @@ -20,8 +20,6 @@ * Some parts derived from the x86 version of this file. */ -#define __KVM_HAVE_READONLY_MEM - #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 /* 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..fc93b3be30 100644 --- a/linux-headers/asm-mips/unistd_n32.h +++ b/linux-headers/asm-mips/unistd_n32.h @@ -379,5 +379,17 @@ #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) +#define __NR_mseal (__NR_Linux + 462) #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..e72a3eb2c9 100644 --- a/linux-headers/asm-mips/unistd_n64.h +++ b/linux-headers/asm-mips/unistd_n64.h @@ -355,5 +355,17 @@ #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) +#define __NR_mseal (__NR_Linux + 462) #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..b86eb0786c 100644 --- a/linux-headers/asm-mips/unistd_o32.h +++ b/linux-headers/asm-mips/unistd_o32.h @@ -425,5 +425,17 @@ #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) +#define __NR_mseal (__NR_Linux + 462) #endif /* _ASM_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 9f18fa090f..eaeda00178 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -28,7 +28,6 @@ #define __KVM_HAVE_PPC_SMT #define __KVM_HAVE_IRQCHIP #define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_GUEST_DEBUG /* Not always available, but if it is, this is the correct offset. */ #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -646,6 +645,9 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_SIER3 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc3) #define KVM_REG_PPC_DAWR1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc4) #define KVM_REG_PPC_DAWRX1 (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc5) +#define KVM_REG_PPC_DEXCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc6) +#define KVM_REG_PPC_HASHKEYR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc7) +#define KVM_REG_PPC_HASHPKEYR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc8) /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs @@ -733,4 +735,48 @@ struct kvm_ppc_xive_eq { #define KVM_XIVE_TIMA_PAGE_OFFSET 0 #define KVM_XIVE_ESB_PAGE_OFFSET 4 +/* for KVM_PPC_GET_PVINFO */ + +#define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) + +struct kvm_ppc_pvinfo { + /* out */ + __u32 flags; + __u32 hcall[4]; + __u8 pad[108]; +}; + +/* for KVM_PPC_GET_SMMU_INFO */ +#define KVM_PPC_PAGE_SIZES_MAX_SZ 8 + +struct kvm_ppc_one_page_size { + __u32 page_shift; /* Page shift (or 0) */ + __u32 pte_enc; /* Encoding in the HPTE (>>12) */ +}; + +struct kvm_ppc_one_seg_page_size { + __u32 page_shift; /* Base page shift of segment (or 0) */ + __u32 slb_enc; /* SLB encoding for BookS */ + struct kvm_ppc_one_page_size enc[KVM_PPC_PAGE_SIZES_MAX_SZ]; +}; + +#define KVM_PPC_PAGE_SIZES_REAL 0x00000001 +#define KVM_PPC_1T_SEGMENTS 0x00000002 +#define KVM_PPC_NO_HASH 0x00000004 + +struct kvm_ppc_smmu_info { + __u64 flags; + __u32 slb_size; + __u16 data_keys; /* # storage keys supported for data */ + __u16 instr_keys; /* # storage keys supported for instructions */ + struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ]; +}; + +/* for KVM_PPC_RESIZE_HPT_{PREPARE,COMMIT} */ +struct kvm_ppc_resize_hpt { + __u64 flags; + __u32 shift; + __u32 pad; +}; + #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h index 585c7fefbc..28627b6546 100644 --- a/linux-headers/asm-powerpc/unistd_32.h +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -432,6 +432,18 @@ #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 +#define __NR_mseal 462 #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..1fc42a8300 100644 --- a/linux-headers/asm-powerpc/unistd_64.h +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -404,6 +404,18 @@ #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 +#define __NR_mseal 462 #endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-riscv/kvm.h b/linux-headers/asm-riscv/kvm.h index 7351417afd..e97db32964 100644 --- a/linux-headers/asm-riscv/kvm.h +++ b/linux-headers/asm-riscv/kvm.h @@ -12,9 +12,10 @@ #ifndef __ASSEMBLY__ #include +#include #include -#define __KVM_HAVE_READONLY_MEM +#define __KVM_HAVE_IRQ_LINE #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -48,6 +49,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 +67,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 +79,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 */ @@ -98,9 +122,87 @@ enum KVM_RISCV_ISA_EXT_ID { 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_ZTSO, + KVM_RISCV_ISA_EXT_ZACAS, + KVM_RISCV_ISA_EXT_SSCOFPMF, + KVM_RISCV_ISA_EXT_ZIMOP, + KVM_RISCV_ISA_EXT_ZCA, + KVM_RISCV_ISA_EXT_ZCB, + KVM_RISCV_ISA_EXT_ZCD, + KVM_RISCV_ISA_EXT_ZCF, + KVM_RISCV_ISA_EXT_ZCMOP, + KVM_RISCV_ISA_EXT_ZAWRS, 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 @@ -111,6 +213,8 @@ enum KVM_RISCV_ISA_EXT_ID { /* 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) @@ -124,8 +228,15 @@ enum KVM_RISCV_ISA_EXT_ID { /* 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) @@ -144,6 +255,111 @@ enum KVM_RISCV_ISA_EXT_ID { /* 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 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 73d7cdd2ec..81896bbbf7 100644 --- a/linux-headers/asm-riscv/unistd.h +++ b/linux-headers/asm-riscv/unistd.h @@ -14,32 +14,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include -#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 - -/* - * Allows the instruction cache to be flushed from userspace. Despite RISC-V - * having a direct 'fence.i' instruction available to userspace (which we - * can't trap!), that's not actually viable when running on Linux because the - * kernel might schedule a process on another hart. There is no way for - * userspace to handle this without invoking the kernel (as it doesn't know the - * thread->hart mappings), so we've defined a RISC-V specific system call to - * flush the instruction cache. - * - * __NR_riscv_flush_icache is defined to flush the instruction cache over an - * address range, with the flush applying to either all threads or just the - * caller. We don't currently do anything with the address range, that's just - * in there for forwards compatibility. - */ -#ifndef __NR_riscv_flush_icache -#define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15) +#if __BITS_PER_LONG == 64 +#include +#else +#include #endif -__SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache) diff --git a/linux-headers/asm-riscv/unistd_32.h b/linux-headers/asm-riscv/unistd_32.h new file mode 100644 index 0000000000..9625743dfd --- /dev/null +++ b/linux-headers/asm-riscv/unistd_32.h @@ -0,0 +1,315 @@ +#ifndef _ASM_UNISTD_32_H +#define _ASM_UNISTD_32_H + +#define __NR_io_setup 0 +#define __NR_io_destroy 1 +#define __NR_io_submit 2 +#define __NR_io_cancel 3 +#define __NR_setxattr 5 +#define __NR_lsetxattr 6 +#define __NR_fsetxattr 7 +#define __NR_getxattr 8 +#define __NR_lgetxattr 9 +#define __NR_fgetxattr 10 +#define __NR_listxattr 11 +#define __NR_llistxattr 12 +#define __NR_flistxattr 13 +#define __NR_removexattr 14 +#define __NR_lremovexattr 15 +#define __NR_fremovexattr 16 +#define __NR_getcwd 17 +#define __NR_lookup_dcookie 18 +#define __NR_eventfd2 19 +#define __NR_epoll_create1 20 +#define __NR_epoll_ctl 21 +#define __NR_epoll_pwait 22 +#define __NR_dup 23 +#define __NR_dup3 24 +#define __NR_fcntl64 25 +#define __NR_inotify_init1 26 +#define __NR_inotify_add_watch 27 +#define __NR_inotify_rm_watch 28 +#define __NR_ioctl 29 +#define __NR_ioprio_set 30 +#define __NR_ioprio_get 31 +#define __NR_flock 32 +#define __NR_mknodat 33 +#define __NR_mkdirat 34 +#define __NR_unlinkat 35 +#define __NR_symlinkat 36 +#define __NR_linkat 37 +#define __NR_umount2 39 +#define __NR_mount 40 +#define __NR_pivot_root 41 +#define __NR_nfsservctl 42 +#define __NR_statfs64 43 +#define __NR_fstatfs64 44 +#define __NR_truncate64 45 +#define __NR_ftruncate64 46 +#define __NR_fallocate 47 +#define __NR_faccessat 48 +#define __NR_chdir 49 +#define __NR_fchdir 50 +#define __NR_chroot 51 +#define __NR_fchmod 52 +#define __NR_fchmodat 53 +#define __NR_fchownat 54 +#define __NR_fchown 55 +#define __NR_openat 56 +#define __NR_close 57 +#define __NR_vhangup 58 +#define __NR_pipe2 59 +#define __NR_quotactl 60 +#define __NR_getdents64 61 +#define __NR_llseek 62 +#define __NR_read 63 +#define __NR_write 64 +#define __NR_readv 65 +#define __NR_writev 66 +#define __NR_pread64 67 +#define __NR_pwrite64 68 +#define __NR_preadv 69 +#define __NR_pwritev 70 +#define __NR_sendfile64 71 +#define __NR_signalfd4 74 +#define __NR_vmsplice 75 +#define __NR_splice 76 +#define __NR_tee 77 +#define __NR_readlinkat 78 +#define __NR_sync 81 +#define __NR_fsync 82 +#define __NR_fdatasync 83 +#define __NR_sync_file_range 84 +#define __NR_timerfd_create 85 +#define __NR_acct 89 +#define __NR_capget 90 +#define __NR_capset 91 +#define __NR_personality 92 +#define __NR_exit 93 +#define __NR_exit_group 94 +#define __NR_waitid 95 +#define __NR_set_tid_address 96 +#define __NR_unshare 97 +#define __NR_set_robust_list 99 +#define __NR_get_robust_list 100 +#define __NR_getitimer 102 +#define __NR_setitimer 103 +#define __NR_kexec_load 104 +#define __NR_init_module 105 +#define __NR_delete_module 106 +#define __NR_timer_create 107 +#define __NR_timer_getoverrun 109 +#define __NR_timer_delete 111 +#define __NR_syslog 116 +#define __NR_ptrace 117 +#define __NR_sched_setparam 118 +#define __NR_sched_setscheduler 119 +#define __NR_sched_getscheduler 120 +#define __NR_sched_getparam 121 +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#define __NR_sched_yield 124 +#define __NR_sched_get_priority_max 125 +#define __NR_sched_get_priority_min 126 +#define __NR_restart_syscall 128 +#define __NR_kill 129 +#define __NR_tkill 130 +#define __NR_tgkill 131 +#define __NR_sigaltstack 132 +#define __NR_rt_sigsuspend 133 +#define __NR_rt_sigaction 134 +#define __NR_rt_sigprocmask 135 +#define __NR_rt_sigpending 136 +#define __NR_rt_sigqueueinfo 138 +#define __NR_rt_sigreturn 139 +#define __NR_setpriority 140 +#define __NR_getpriority 141 +#define __NR_reboot 142 +#define __NR_setregid 143 +#define __NR_setgid 144 +#define __NR_setreuid 145 +#define __NR_setuid 146 +#define __NR_setresuid 147 +#define __NR_getresuid 148 +#define __NR_setresgid 149 +#define __NR_getresgid 150 +#define __NR_setfsuid 151 +#define __NR_setfsgid 152 +#define __NR_times 153 +#define __NR_setpgid 154 +#define __NR_getpgid 155 +#define __NR_getsid 156 +#define __NR_setsid 157 +#define __NR_getgroups 158 +#define __NR_setgroups 159 +#define __NR_uname 160 +#define __NR_sethostname 161 +#define __NR_setdomainname 162 +#define __NR_getrusage 165 +#define __NR_umask 166 +#define __NR_prctl 167 +#define __NR_getcpu 168 +#define __NR_getpid 172 +#define __NR_getppid 173 +#define __NR_getuid 174 +#define __NR_geteuid 175 +#define __NR_getgid 176 +#define __NR_getegid 177 +#define __NR_gettid 178 +#define __NR_sysinfo 179 +#define __NR_mq_open 180 +#define __NR_mq_unlink 181 +#define __NR_mq_notify 184 +#define __NR_mq_getsetattr 185 +#define __NR_msgget 186 +#define __NR_msgctl 187 +#define __NR_msgrcv 188 +#define __NR_msgsnd 189 +#define __NR_semget 190 +#define __NR_semctl 191 +#define __NR_semop 193 +#define __NR_shmget 194 +#define __NR_shmctl 195 +#define __NR_shmat 196 +#define __NR_shmdt 197 +#define __NR_socket 198 +#define __NR_socketpair 199 +#define __NR_bind 200 +#define __NR_listen 201 +#define __NR_accept 202 +#define __NR_connect 203 +#define __NR_getsockname 204 +#define __NR_getpeername 205 +#define __NR_sendto 206 +#define __NR_recvfrom 207 +#define __NR_setsockopt 208 +#define __NR_getsockopt 209 +#define __NR_shutdown 210 +#define __NR_sendmsg 211 +#define __NR_recvmsg 212 +#define __NR_readahead 213 +#define __NR_brk 214 +#define __NR_munmap 215 +#define __NR_mremap 216 +#define __NR_add_key 217 +#define __NR_request_key 218 +#define __NR_keyctl 219 +#define __NR_clone 220 +#define __NR_execve 221 +#define __NR_mmap2 222 +#define __NR_fadvise64_64 223 +#define __NR_swapon 224 +#define __NR_swapoff 225 +#define __NR_mprotect 226 +#define __NR_msync 227 +#define __NR_mlock 228 +#define __NR_munlock 229 +#define __NR_mlockall 230 +#define __NR_munlockall 231 +#define __NR_mincore 232 +#define __NR_madvise 233 +#define __NR_remap_file_pages 234 +#define __NR_mbind 235 +#define __NR_get_mempolicy 236 +#define __NR_set_mempolicy 237 +#define __NR_migrate_pages 238 +#define __NR_move_pages 239 +#define __NR_rt_tgsigqueueinfo 240 +#define __NR_perf_event_open 241 +#define __NR_accept4 242 +#define __NR_riscv_hwprobe 258 +#define __NR_riscv_flush_icache 259 +#define __NR_prlimit64 261 +#define __NR_fanotify_init 262 +#define __NR_fanotify_mark 263 +#define __NR_name_to_handle_at 264 +#define __NR_open_by_handle_at 265 +#define __NR_syncfs 267 +#define __NR_setns 268 +#define __NR_sendmmsg 269 +#define __NR_process_vm_readv 270 +#define __NR_process_vm_writev 271 +#define __NR_kcmp 272 +#define __NR_finit_module 273 +#define __NR_sched_setattr 274 +#define __NR_sched_getattr 275 +#define __NR_renameat2 276 +#define __NR_seccomp 277 +#define __NR_getrandom 278 +#define __NR_memfd_create 279 +#define __NR_bpf 280 +#define __NR_execveat 281 +#define __NR_userfaultfd 282 +#define __NR_membarrier 283 +#define __NR_mlock2 284 +#define __NR_copy_file_range 285 +#define __NR_preadv2 286 +#define __NR_pwritev2 287 +#define __NR_pkey_mprotect 288 +#define __NR_pkey_alloc 289 +#define __NR_pkey_free 290 +#define __NR_statx 291 +#define __NR_rseq 293 +#define __NR_kexec_file_load 294 +#define __NR_clock_gettime64 403 +#define __NR_clock_settime64 404 +#define __NR_clock_adjtime64 405 +#define __NR_clock_getres_time64 406 +#define __NR_clock_nanosleep_time64 407 +#define __NR_timer_gettime64 408 +#define __NR_timer_settime64 409 +#define __NR_timerfd_gettime64 410 +#define __NR_timerfd_settime64 411 +#define __NR_utimensat_time64 412 +#define __NR_pselect6_time64 413 +#define __NR_ppoll_time64 414 +#define __NR_io_pgetevents_time64 416 +#define __NR_recvmmsg_time64 417 +#define __NR_mq_timedsend_time64 418 +#define __NR_mq_timedreceive_time64 419 +#define __NR_semtimedop_time64 420 +#define __NR_rt_sigtimedwait_time64 421 +#define __NR_futex_time64 422 +#define __NR_sched_rr_get_interval_time64 423 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 +#define __NR_close_range 436 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 +#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 +#define __NR_mseal 462 + + +#endif /* _ASM_UNISTD_32_H */ diff --git a/linux-headers/asm-riscv/unistd_64.h b/linux-headers/asm-riscv/unistd_64.h new file mode 100644 index 0000000000..95bca8ae81 --- /dev/null +++ b/linux-headers/asm-riscv/unistd_64.h @@ -0,0 +1,325 @@ +#ifndef _ASM_UNISTD_64_H +#define _ASM_UNISTD_64_H + +#define __NR_io_setup 0 +#define __NR_io_destroy 1 +#define __NR_io_submit 2 +#define __NR_io_cancel 3 +#define __NR_io_getevents 4 +#define __NR_setxattr 5 +#define __NR_lsetxattr 6 +#define __NR_fsetxattr 7 +#define __NR_getxattr 8 +#define __NR_lgetxattr 9 +#define __NR_fgetxattr 10 +#define __NR_listxattr 11 +#define __NR_llistxattr 12 +#define __NR_flistxattr 13 +#define __NR_removexattr 14 +#define __NR_lremovexattr 15 +#define __NR_fremovexattr 16 +#define __NR_getcwd 17 +#define __NR_lookup_dcookie 18 +#define __NR_eventfd2 19 +#define __NR_epoll_create1 20 +#define __NR_epoll_ctl 21 +#define __NR_epoll_pwait 22 +#define __NR_dup 23 +#define __NR_dup3 24 +#define __NR_fcntl 25 +#define __NR_inotify_init1 26 +#define __NR_inotify_add_watch 27 +#define __NR_inotify_rm_watch 28 +#define __NR_ioctl 29 +#define __NR_ioprio_set 30 +#define __NR_ioprio_get 31 +#define __NR_flock 32 +#define __NR_mknodat 33 +#define __NR_mkdirat 34 +#define __NR_unlinkat 35 +#define __NR_symlinkat 36 +#define __NR_linkat 37 +#define __NR_umount2 39 +#define __NR_mount 40 +#define __NR_pivot_root 41 +#define __NR_nfsservctl 42 +#define __NR_statfs 43 +#define __NR_fstatfs 44 +#define __NR_truncate 45 +#define __NR_ftruncate 46 +#define __NR_fallocate 47 +#define __NR_faccessat 48 +#define __NR_chdir 49 +#define __NR_fchdir 50 +#define __NR_chroot 51 +#define __NR_fchmod 52 +#define __NR_fchmodat 53 +#define __NR_fchownat 54 +#define __NR_fchown 55 +#define __NR_openat 56 +#define __NR_close 57 +#define __NR_vhangup 58 +#define __NR_pipe2 59 +#define __NR_quotactl 60 +#define __NR_getdents64 61 +#define __NR_lseek 62 +#define __NR_read 63 +#define __NR_write 64 +#define __NR_readv 65 +#define __NR_writev 66 +#define __NR_pread64 67 +#define __NR_pwrite64 68 +#define __NR_preadv 69 +#define __NR_pwritev 70 +#define __NR_sendfile 71 +#define __NR_pselect6 72 +#define __NR_ppoll 73 +#define __NR_signalfd4 74 +#define __NR_vmsplice 75 +#define __NR_splice 76 +#define __NR_tee 77 +#define __NR_readlinkat 78 +#define __NR_newfstatat 79 +#define __NR_fstat 80 +#define __NR_sync 81 +#define __NR_fsync 82 +#define __NR_fdatasync 83 +#define __NR_sync_file_range 84 +#define __NR_timerfd_create 85 +#define __NR_timerfd_settime 86 +#define __NR_timerfd_gettime 87 +#define __NR_utimensat 88 +#define __NR_acct 89 +#define __NR_capget 90 +#define __NR_capset 91 +#define __NR_personality 92 +#define __NR_exit 93 +#define __NR_exit_group 94 +#define __NR_waitid 95 +#define __NR_set_tid_address 96 +#define __NR_unshare 97 +#define __NR_futex 98 +#define __NR_set_robust_list 99 +#define __NR_get_robust_list 100 +#define __NR_nanosleep 101 +#define __NR_getitimer 102 +#define __NR_setitimer 103 +#define __NR_kexec_load 104 +#define __NR_init_module 105 +#define __NR_delete_module 106 +#define __NR_timer_create 107 +#define __NR_timer_gettime 108 +#define __NR_timer_getoverrun 109 +#define __NR_timer_settime 110 +#define __NR_timer_delete 111 +#define __NR_clock_settime 112 +#define __NR_clock_gettime 113 +#define __NR_clock_getres 114 +#define __NR_clock_nanosleep 115 +#define __NR_syslog 116 +#define __NR_ptrace 117 +#define __NR_sched_setparam 118 +#define __NR_sched_setscheduler 119 +#define __NR_sched_getscheduler 120 +#define __NR_sched_getparam 121 +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#define __NR_sched_yield 124 +#define __NR_sched_get_priority_max 125 +#define __NR_sched_get_priority_min 126 +#define __NR_sched_rr_get_interval 127 +#define __NR_restart_syscall 128 +#define __NR_kill 129 +#define __NR_tkill 130 +#define __NR_tgkill 131 +#define __NR_sigaltstack 132 +#define __NR_rt_sigsuspend 133 +#define __NR_rt_sigaction 134 +#define __NR_rt_sigprocmask 135 +#define __NR_rt_sigpending 136 +#define __NR_rt_sigtimedwait 137 +#define __NR_rt_sigqueueinfo 138 +#define __NR_rt_sigreturn 139 +#define __NR_setpriority 140 +#define __NR_getpriority 141 +#define __NR_reboot 142 +#define __NR_setregid 143 +#define __NR_setgid 144 +#define __NR_setreuid 145 +#define __NR_setuid 146 +#define __NR_setresuid 147 +#define __NR_getresuid 148 +#define __NR_setresgid 149 +#define __NR_getresgid 150 +#define __NR_setfsuid 151 +#define __NR_setfsgid 152 +#define __NR_times 153 +#define __NR_setpgid 154 +#define __NR_getpgid 155 +#define __NR_getsid 156 +#define __NR_setsid 157 +#define __NR_getgroups 158 +#define __NR_setgroups 159 +#define __NR_uname 160 +#define __NR_sethostname 161 +#define __NR_setdomainname 162 +#define __NR_getrlimit 163 +#define __NR_setrlimit 164 +#define __NR_getrusage 165 +#define __NR_umask 166 +#define __NR_prctl 167 +#define __NR_getcpu 168 +#define __NR_gettimeofday 169 +#define __NR_settimeofday 170 +#define __NR_adjtimex 171 +#define __NR_getpid 172 +#define __NR_getppid 173 +#define __NR_getuid 174 +#define __NR_geteuid 175 +#define __NR_getgid 176 +#define __NR_getegid 177 +#define __NR_gettid 178 +#define __NR_sysinfo 179 +#define __NR_mq_open 180 +#define __NR_mq_unlink 181 +#define __NR_mq_timedsend 182 +#define __NR_mq_timedreceive 183 +#define __NR_mq_notify 184 +#define __NR_mq_getsetattr 185 +#define __NR_msgget 186 +#define __NR_msgctl 187 +#define __NR_msgrcv 188 +#define __NR_msgsnd 189 +#define __NR_semget 190 +#define __NR_semctl 191 +#define __NR_semtimedop 192 +#define __NR_semop 193 +#define __NR_shmget 194 +#define __NR_shmctl 195 +#define __NR_shmat 196 +#define __NR_shmdt 197 +#define __NR_socket 198 +#define __NR_socketpair 199 +#define __NR_bind 200 +#define __NR_listen 201 +#define __NR_accept 202 +#define __NR_connect 203 +#define __NR_getsockname 204 +#define __NR_getpeername 205 +#define __NR_sendto 206 +#define __NR_recvfrom 207 +#define __NR_setsockopt 208 +#define __NR_getsockopt 209 +#define __NR_shutdown 210 +#define __NR_sendmsg 211 +#define __NR_recvmsg 212 +#define __NR_readahead 213 +#define __NR_brk 214 +#define __NR_munmap 215 +#define __NR_mremap 216 +#define __NR_add_key 217 +#define __NR_request_key 218 +#define __NR_keyctl 219 +#define __NR_clone 220 +#define __NR_execve 221 +#define __NR_mmap 222 +#define __NR_fadvise64 223 +#define __NR_swapon 224 +#define __NR_swapoff 225 +#define __NR_mprotect 226 +#define __NR_msync 227 +#define __NR_mlock 228 +#define __NR_munlock 229 +#define __NR_mlockall 230 +#define __NR_munlockall 231 +#define __NR_mincore 232 +#define __NR_madvise 233 +#define __NR_remap_file_pages 234 +#define __NR_mbind 235 +#define __NR_get_mempolicy 236 +#define __NR_set_mempolicy 237 +#define __NR_migrate_pages 238 +#define __NR_move_pages 239 +#define __NR_rt_tgsigqueueinfo 240 +#define __NR_perf_event_open 241 +#define __NR_accept4 242 +#define __NR_recvmmsg 243 +#define __NR_riscv_hwprobe 258 +#define __NR_riscv_flush_icache 259 +#define __NR_wait4 260 +#define __NR_prlimit64 261 +#define __NR_fanotify_init 262 +#define __NR_fanotify_mark 263 +#define __NR_name_to_handle_at 264 +#define __NR_open_by_handle_at 265 +#define __NR_clock_adjtime 266 +#define __NR_syncfs 267 +#define __NR_setns 268 +#define __NR_sendmmsg 269 +#define __NR_process_vm_readv 270 +#define __NR_process_vm_writev 271 +#define __NR_kcmp 272 +#define __NR_finit_module 273 +#define __NR_sched_setattr 274 +#define __NR_sched_getattr 275 +#define __NR_renameat2 276 +#define __NR_seccomp 277 +#define __NR_getrandom 278 +#define __NR_memfd_create 279 +#define __NR_bpf 280 +#define __NR_execveat 281 +#define __NR_userfaultfd 282 +#define __NR_membarrier 283 +#define __NR_mlock2 284 +#define __NR_copy_file_range 285 +#define __NR_preadv2 286 +#define __NR_pwritev2 287 +#define __NR_pkey_mprotect 288 +#define __NR_pkey_alloc 289 +#define __NR_pkey_free 290 +#define __NR_statx 291 +#define __NR_io_pgetevents 292 +#define __NR_rseq 293 +#define __NR_kexec_file_load 294 +#define __NR_pidfd_send_signal 424 +#define __NR_io_uring_setup 425 +#define __NR_io_uring_enter 426 +#define __NR_io_uring_register 427 +#define __NR_open_tree 428 +#define __NR_move_mount 429 +#define __NR_fsopen 430 +#define __NR_fsconfig 431 +#define __NR_fsmount 432 +#define __NR_fspick 433 +#define __NR_pidfd_open 434 +#define __NR_clone3 435 +#define __NR_close_range 436 +#define __NR_openat2 437 +#define __NR_pidfd_getfd 438 +#define __NR_faccessat2 439 +#define __NR_process_madvise 440 +#define __NR_epoll_pwait2 441 +#define __NR_mount_setattr 442 +#define __NR_quotactl_fd 443 +#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 +#define __NR_mseal 462 + + +#endif /* _ASM_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index e2afd95420..684c4e1205 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -12,7 +12,320 @@ #include #define __KVM_S390 -#define __KVM_HAVE_GUEST_DEBUG + +struct kvm_s390_skeys { + __u64 start_gfn; + __u64 count; + __u64 skeydata_addr; + __u32 flags; + __u32 reserved[9]; +}; + +#define KVM_S390_CMMA_PEEK (1 << 0) + +/** + * kvm_s390_cmma_log - Used for CMMA migration. + * + * Used both for input and output. + * + * @start_gfn: Guest page number to start from. + * @count: Size of the result buffer. + * @flags: Control operation mode via KVM_S390_CMMA_* flags + * @remaining: Used with KVM_S390_GET_CMMA_BITS. Indicates how many dirty + * pages are still remaining. + * @mask: Used with KVM_S390_SET_CMMA_BITS. Bitmap of bits to actually set + * in the PGSTE. + * @values: Pointer to the values buffer. + * + * Used in KVM_S390_{G,S}ET_CMMA_BITS ioctls. + */ +struct kvm_s390_cmma_log { + __u64 start_gfn; + __u32 count; + __u32 flags; + union { + __u64 remaining; + __u64 mask; + }; + __u64 values; +}; + +#define KVM_S390_RESET_POR 1 +#define KVM_S390_RESET_CLEAR 2 +#define KVM_S390_RESET_SUBSYSTEM 4 +#define KVM_S390_RESET_CPU_INIT 8 +#define KVM_S390_RESET_IPL 16 + +/* for KVM_S390_MEM_OP */ +struct kvm_s390_mem_op { + /* in */ + __u64 gaddr; /* the guest address */ + __u64 flags; /* flags */ + __u32 size; /* amount of bytes */ + __u32 op; /* type of operation */ + __u64 buf; /* buffer in userspace */ + union { + 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 */ + }; +}; +/* types for kvm_s390_mem_op->op */ +#define KVM_S390_MEMOP_LOGICAL_READ 0 +#define KVM_S390_MEMOP_LOGICAL_WRITE 1 +#define KVM_S390_MEMOP_SIDA_READ 2 +#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) + +struct kvm_s390_psw { + __u64 mask; + __u64 addr; +}; + +/* valid values for type in kvm_s390_interrupt */ +#define KVM_S390_SIGP_STOP 0xfffe0000u +#define KVM_S390_PROGRAM_INT 0xfffe0001u +#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u +#define KVM_S390_RESTART 0xfffe0003u +#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u +#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u +#define KVM_S390_MCHK 0xfffe1000u +#define KVM_S390_INT_CLOCK_COMP 0xffff1004u +#define KVM_S390_INT_CPU_TIMER 0xffff1005u +#define KVM_S390_INT_VIRTIO 0xffff2603u +#define KVM_S390_INT_SERVICE 0xffff2401u +#define KVM_S390_INT_EMERGENCY 0xffff1201u +#define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u +/* Anything below 0xfffe0000u is taken by INT_IO */ +#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \ + (((schid)) | \ + ((ssid) << 16) | \ + ((cssid) << 18) | \ + ((ai) << 26)) +#define KVM_S390_INT_IO_MIN 0x00000000u +#define KVM_S390_INT_IO_MAX 0xfffdffffu +#define KVM_S390_INT_IO_AI_MASK 0x04000000u + + +struct kvm_s390_interrupt { + __u32 type; + __u32 parm; + __u64 parm64; +}; + +struct kvm_s390_io_info { + __u16 subchannel_id; + __u16 subchannel_nr; + __u32 io_int_parm; + __u32 io_int_word; +}; + +struct kvm_s390_ext_info { + __u32 ext_params; + __u32 pad; + __u64 ext_params2; +}; + +struct kvm_s390_pgm_info { + __u64 trans_exc_code; + __u64 mon_code; + __u64 per_address; + __u32 data_exc_code; + __u16 code; + __u16 mon_class_nr; + __u8 per_code; + __u8 per_atmid; + __u8 exc_access_id; + __u8 per_access_id; + __u8 op_access_id; +#define KVM_S390_PGM_FLAGS_ILC_VALID 0x01 +#define KVM_S390_PGM_FLAGS_ILC_0 0x02 +#define KVM_S390_PGM_FLAGS_ILC_1 0x04 +#define KVM_S390_PGM_FLAGS_ILC_MASK 0x06 +#define KVM_S390_PGM_FLAGS_NO_REWIND 0x08 + __u8 flags; + __u8 pad[2]; +}; + +struct kvm_s390_prefix_info { + __u32 address; +}; + +struct kvm_s390_extcall_info { + __u16 code; +}; + +struct kvm_s390_emerg_info { + __u16 code; +}; + +#define KVM_S390_STOP_FLAG_STORE_STATUS 0x01 +struct kvm_s390_stop_info { + __u32 flags; +}; + +struct kvm_s390_mchk_info { + __u64 cr14; + __u64 mcic; + __u64 failing_storage_address; + __u32 ext_damage_code; + __u32 pad; + __u8 fixed_logout[16]; +}; + +struct kvm_s390_irq { + __u64 type; + union { + struct kvm_s390_io_info io; + struct kvm_s390_ext_info ext; + struct kvm_s390_pgm_info pgm; + struct kvm_s390_emerg_info emerg; + struct kvm_s390_extcall_info extcall; + struct kvm_s390_prefix_info prefix; + struct kvm_s390_stop_info stop; + struct kvm_s390_mchk_info mchk; + char reserved[64]; + } u; +}; + +struct kvm_s390_irq_state { + __u64 buf; + __u32 flags; /* will stay unused for compatibility reasons */ + __u32 len; + __u32 reserved[4]; /* will stay unused for compatibility reasons */ +}; + +struct kvm_s390_ucas_mapping { + __u64 user_addr; + __u64 vcpu_addr; + __u64 length; +}; + +struct kvm_s390_pv_sec_parm { + __u64 origin; + __u64 length; +}; + +struct kvm_s390_pv_unp { + __u64 addr; + __u64 size; + __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, + KVM_PV_SET_SEC_PARMS, + KVM_PV_UNPACK, + 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 { + __u32 cmd; /* Command to be executed */ + __u16 rc; /* Ultravisor return code */ + __u16 rrc; /* Ultravisor return reason code */ + __u64 data; /* Data or address */ + __u32 flags; /* flags for future extensions. Must be 0 for now */ + __u32 reserved[3]; +}; + +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) /* Device control API: s390-specific devices */ #define KVM_DEV_FLIC_GET_ALL_IRQS 1 @@ -159,6 +472,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..7706c21b87 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -419,8 +419,21 @@ #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 +#define __NR_mseal 462 #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..62082d592d 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -367,8 +367,21 @@ #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 +#define __NR_mseal 462 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 46de10a809..4711ef2c3d 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -7,8 +7,11 @@ * */ +#include +#include #include #include +#include #define KVM_PIO_PAGE_OFFSET 1 #define KVM_COALESCED_MMIO_PAGE_OFFSET 2 @@ -39,7 +42,6 @@ #define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_MSI #define __KVM_HAVE_USER_NMI -#define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_MSIX #define __KVM_HAVE_MCE #define __KVM_HAVE_PIT_STATE2 @@ -48,19 +50,10 @@ #define __KVM_HAVE_DEBUGREGS #define __KVM_HAVE_XSAVE #define __KVM_HAVE_XCRS -#define __KVM_HAVE_READONLY_MEM /* 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 */ @@ -113,6 +106,7 @@ struct kvm_ioapic_state { #define KVM_RUN_X86_SMM (1 << 0) #define KVM_RUN_X86_BUS_LOCK (1 << 1) +#define KVM_RUN_X86_GUEST_MODE (1 << 2) /* for KVM_GET_REGS and KVM_SET_REGS */ struct kvm_regs { @@ -214,6 +208,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 +220,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]; }; @@ -440,6 +437,7 @@ struct kvm_sync_regs { #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_X86_QUIRK_SLOT_ZAP_ALL (1 << 7) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 @@ -459,8 +457,13 @@ struct kvm_sync_regs { #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 -/* attributes for system fd (group 0) */ -#define KVM_X86_XCOMP_GUEST_SUPP 0 +/* vendor-independent attributes for system fd (group 0) */ +#define KVM_X86_GRP_SYSTEM 0 +# define KVM_X86_XCOMP_GUEST_SUPP 0 + +/* vendor-specific groups and attributes for system fd */ +#define KVM_X86_GRP_SEV 1 +# define KVM_X86_SEV_VMSA_FEATURES 0 struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; @@ -510,8 +513,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; }; @@ -528,8 +531,396 @@ struct kvm_pmu_event_filter { #define KVM_PMU_EVENT_ALLOW 0 #define KVM_PMU_EVENT_DENY 1 +#define KVM_PMU_EVENT_FLAG_MASKED_EVENTS _BITUL(0) +#define KVM_PMU_EVENT_FLAGS_VALID_MASK (KVM_PMU_EVENT_FLAG_MASKED_EVENTS) + +/* for KVM_CAP_MCE */ +struct kvm_x86_mce { + __u64 status; + __u64 addr; + __u64 misc; + __u64 mcg_status; + __u8 bank; + __u8 pad1[7]; + __u64 pad2[3]; +}; + +/* for KVM_CAP_XEN_HVM */ +#define KVM_XEN_HVM_CONFIG_HYPERCALL_MSR (1 << 0) +#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1) +#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) +#define KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA (1 << 8) + +struct kvm_xen_hvm_config { + __u32 flags; + __u32 msr; + __u64 blob_addr_32; + __u64 blob_addr_64; + __u8 blob_size_32; + __u8 blob_size_64; + __u8 pad2[30]; +}; + +struct kvm_xen_hvm_attr { + __u16 type; + __u16 pad[3]; + union { + __u8 long_mode; + __u8 vector; + __u8 runstate_update_flag; + union { + __u64 gfn; +#define KVM_XEN_INVALID_GFN ((__u64)-1) + __u64 hva; + } 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 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA */ +#define KVM_XEN_ATTR_TYPE_SHARED_INFO_HVA 0x6 + +struct kvm_xen_vcpu_attr { + __u16 type; + __u16 pad[3]; + union { + __u64 gpa; +#define KVM_XEN_INVALID_GPA ((__u64)-1) + __u64 hva; + __u64 pad[8]; + struct { + __u64 state; + __u64 state_entry_time; + __u64 time_running; + __u64 time_runnable; + __u64 time_blocked; + __u64 time_offline; + } runstate; + __u32 vcpu_id; + struct { + __u32 port; + __u32 priority; + __u64 expires_ns; + } timer; + __u8 vector; + } u; +}; + +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO 0x0 +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO 0x1 +#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR 0x2 +#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 +/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO_HVA */ +#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO_HVA 0x9 + +/* Secure Encrypted Virtualization command */ +enum sev_cmd_id { + /* Guest initialization commands */ + KVM_SEV_INIT = 0, + KVM_SEV_ES_INIT, + /* Guest launch commands */ + KVM_SEV_LAUNCH_START, + KVM_SEV_LAUNCH_UPDATE_DATA, + KVM_SEV_LAUNCH_UPDATE_VMSA, + KVM_SEV_LAUNCH_SECRET, + KVM_SEV_LAUNCH_MEASURE, + KVM_SEV_LAUNCH_FINISH, + /* Guest migration commands (outgoing) */ + KVM_SEV_SEND_START, + KVM_SEV_SEND_UPDATE_DATA, + KVM_SEV_SEND_UPDATE_VMSA, + KVM_SEV_SEND_FINISH, + /* Guest migration commands (incoming) */ + KVM_SEV_RECEIVE_START, + KVM_SEV_RECEIVE_UPDATE_DATA, + KVM_SEV_RECEIVE_UPDATE_VMSA, + KVM_SEV_RECEIVE_FINISH, + /* Guest status and debug commands */ + KVM_SEV_GUEST_STATUS, + KVM_SEV_DBG_DECRYPT, + KVM_SEV_DBG_ENCRYPT, + /* Guest certificates commands */ + KVM_SEV_CERT_EXPORT, + /* Attestation report */ + KVM_SEV_GET_ATTESTATION_REPORT, + /* Guest Migration Extension */ + KVM_SEV_SEND_CANCEL, + + /* Second time is the charm; improved versions of the above ioctls. */ + KVM_SEV_INIT2, + + /* SNP-specific commands */ + KVM_SEV_SNP_LAUNCH_START = 100, + KVM_SEV_SNP_LAUNCH_UPDATE, + KVM_SEV_SNP_LAUNCH_FINISH, + + KVM_SEV_NR_MAX, +}; + +struct kvm_sev_cmd { + __u32 id; + __u32 pad0; + __u64 data; + __u32 error; + __u32 sev_fd; +}; + +struct kvm_sev_init { + __u64 vmsa_features; + __u32 flags; + __u16 ghcb_version; + __u16 pad1; + __u32 pad2[8]; +}; + +struct kvm_sev_launch_start { + __u32 handle; + __u32 policy; + __u64 dh_uaddr; + __u32 dh_len; + __u32 pad0; + __u64 session_uaddr; + __u32 session_len; + __u32 pad1; +}; + +struct kvm_sev_launch_update_data { + __u64 uaddr; + __u32 len; + __u32 pad0; +}; + + +struct kvm_sev_launch_secret { + __u64 hdr_uaddr; + __u32 hdr_len; + __u32 pad0; + __u64 guest_uaddr; + __u32 guest_len; + __u32 pad1; + __u64 trans_uaddr; + __u32 trans_len; + __u32 pad2; +}; + +struct kvm_sev_launch_measure { + __u64 uaddr; + __u32 len; + __u32 pad0; +}; + +struct kvm_sev_guest_status { + __u32 handle; + __u32 policy; + __u32 state; +}; + +struct kvm_sev_dbg { + __u64 src_uaddr; + __u64 dst_uaddr; + __u32 len; + __u32 pad0; +}; + +struct kvm_sev_attestation_report { + __u8 mnonce[16]; + __u64 uaddr; + __u32 len; + __u32 pad0; +}; + +struct kvm_sev_send_start { + __u32 policy; + __u32 pad0; + __u64 pdh_cert_uaddr; + __u32 pdh_cert_len; + __u32 pad1; + __u64 plat_certs_uaddr; + __u32 plat_certs_len; + __u32 pad2; + __u64 amd_certs_uaddr; + __u32 amd_certs_len; + __u32 pad3; + __u64 session_uaddr; + __u32 session_len; + __u32 pad4; +}; + +struct kvm_sev_send_update_data { + __u64 hdr_uaddr; + __u32 hdr_len; + __u32 pad0; + __u64 guest_uaddr; + __u32 guest_len; + __u32 pad1; + __u64 trans_uaddr; + __u32 trans_len; + __u32 pad2; +}; + +struct kvm_sev_receive_start { + __u32 handle; + __u32 policy; + __u64 pdh_uaddr; + __u32 pdh_len; + __u32 pad0; + __u64 session_uaddr; + __u32 session_len; + __u32 pad1; +}; + +struct kvm_sev_receive_update_data { + __u64 hdr_uaddr; + __u32 hdr_len; + __u32 pad0; + __u64 guest_uaddr; + __u32 guest_len; + __u32 pad1; + __u64 trans_uaddr; + __u32 trans_len; + __u32 pad2; +}; + +struct kvm_sev_snp_launch_start { + __u64 policy; + __u8 gosvw[16]; + __u16 flags; + __u8 pad0[6]; + __u64 pad1[4]; +}; + +/* Kept in sync with firmware values for simplicity. */ +#define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 +#define KVM_SEV_SNP_PAGE_TYPE_ZERO 0x3 +#define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED 0x4 +#define KVM_SEV_SNP_PAGE_TYPE_SECRETS 0x5 +#define KVM_SEV_SNP_PAGE_TYPE_CPUID 0x6 + +struct kvm_sev_snp_launch_update { + __u64 gfn_start; + __u64 uaddr; + __u64 len; + __u8 type; + __u8 pad0; + __u16 flags; + __u32 pad1; + __u64 pad2[4]; +}; + +#define KVM_SEV_SNP_ID_BLOCK_SIZE 96 +#define KVM_SEV_SNP_ID_AUTH_SIZE 4096 +#define KVM_SEV_SNP_FINISH_DATA_SIZE 32 + +struct kvm_sev_snp_launch_finish { + __u64 id_block_uaddr; + __u64 id_auth_uaddr; + __u8 id_block_en; + __u8 auth_key_en; + __u8 vcek_disabled; + __u8 host_data[KVM_SEV_SNP_FINISH_DATA_SIZE]; + __u8 pad0[3]; + __u16 flags; + __u64 pad1[4]; +}; + +#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0) +#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1) + +struct kvm_hyperv_eventfd { + __u32 conn_id; + __s32 fd; + __u32 flags; + __u32 padding[3]; +}; + +#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff +#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) + +/* + * 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 (_BITULL(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 _BITULL(0) + +#define KVM_X86_DEFAULT_VM 0 +#define KVM_X86_SW_PROTECTED_VM 1 +#define KVM_X86_SEV_VM 2 +#define KVM_X86_SEV_ES_VM 3 +#define KVM_X86_SNP_VM 4 + #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/asm-x86/kvm_para.h b/linux-headers/asm-x86/kvm_para.h new file mode 100644 index 0000000000..1d3e0e0b07 --- /dev/null +++ b/linux-headers/asm-x86/kvm_para.h @@ -0,0 +1 @@ +#include "standard-headers/asm-x86/kvm_para.h" diff --git a/linux-headers/asm-x86/mman.h b/linux-headers/asm-x86/mman.h index 775dbd3aff..46cdc941f9 100644 --- a/linux-headers/asm-x86/mman.h +++ b/linux-headers/asm-x86/mman.h @@ -3,14 +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 -#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..fb7b8b169b 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -441,6 +441,18 @@ #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 +#define __NR_mseal 462 #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..24c979be54 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -336,6 +336,7 @@ #define __NR_statx 332 #define __NR_io_pgetevents 333 #define __NR_rseq 334 +#define __NR_uretprobe 335 #define __NR_pidfd_send_signal 424 #define __NR_io_uring_setup 425 #define __NR_io_uring_enter 426 @@ -363,6 +364,18 @@ #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 +#define __NR_mseal 462 #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..c23dd21a2d 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -289,6 +289,7 @@ #define __NR_statx (__X32_SYSCALL_BIT + 332) #define __NR_io_pgetevents (__X32_SYSCALL_BIT + 333) #define __NR_rseq (__X32_SYSCALL_BIT + 334) +#define __NR_uretprobe (__X32_SYSCALL_BIT + 335) #define __NR_pidfd_send_signal (__X32_SYSCALL_BIT + 424) #define __NR_io_uring_setup (__X32_SYSCALL_BIT + 425) #define __NR_io_uring_enter (__X32_SYSCALL_BIT + 426) @@ -316,6 +317,18 @@ #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_map_shadow_stack (__X32_SYSCALL_BIT + 453) +#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_mseal (__X32_SYSCALL_BIT + 462) #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/bits.h b/linux-headers/linux/bits.h new file mode 100644 index 0000000000..c0d00c0a98 --- /dev/null +++ b/linux-headers/linux/bits.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* bits.h: Macros for dealing with bitmasks. */ + +#ifndef _LINUX_BITS_H +#define _LINUX_BITS_H + +#define __GENMASK(h, l) \ + (((~_UL(0)) - (_UL(1) << (l)) + 1) & \ + (~_UL(0) >> (__BITS_PER_LONG - 1 - (h)))) + +#define __GENMASK_ULL(h, l) \ + (((~_ULL(0)) - (_ULL(1) << (l)) + 1) & \ + (~_ULL(0) >> (__BITS_PER_LONG_LONG - 1 - (h)))) + +#define __GENMASK_U128(h, l) \ + ((_BIT128((h)) << 1) - (_BIT128(l))) + +#endif /* _LINUX_BITS_H */ diff --git a/linux-headers/linux/const.h b/linux-headers/linux/const.h new file mode 100644 index 0000000000..2122610de7 --- /dev/null +++ b/linux-headers/linux/const.h @@ -0,0 +1,53 @@ +/* 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)) + +#if !defined(__ASSEMBLY__) +/* + * Missing __asm__ support + * + * __BIT128() would not work in the __asm__ code, as it shifts an + * 'unsigned __init128' data type as direct representation of + * 128 bit constants is not supported in the gcc compiler, as + * they get silently truncated. + * + * TODO: Please revisit this implementation when gcc compiler + * starts representing 128 bit constants directly like long + * and unsigned long etc. Subsequently drop the comment for + * GENMASK_U128() which would then start supporting __asm__ code. + */ +#define _BIT128(x) ((unsigned __int128)(1) << (x)) +#endif + +#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..782baf477f --- /dev/null +++ b/linux-headers/linux/iommufd.h @@ -0,0 +1,800 @@ +/* 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 = 0x81, + IOMMUFD_CMD_IOAS_ALLOW_IOVAS = 0x82, + IOMMUFD_CMD_IOAS_COPY = 0x83, + IOMMUFD_CMD_IOAS_IOVA_RANGES = 0x84, + IOMMUFD_CMD_IOAS_MAP = 0x85, + IOMMUFD_CMD_IOAS_UNMAP = 0x86, + IOMMUFD_CMD_OPTION = 0x87, + IOMMUFD_CMD_VFIO_IOAS = 0x88, + IOMMUFD_CMD_HWPT_ALLOC = 0x89, + IOMMUFD_CMD_GET_HW_INFO = 0x8a, + IOMMUFD_CMD_HWPT_SET_DIRTY_TRACKING = 0x8b, + IOMMUFD_CMD_HWPT_GET_DIRTY_BITMAP = 0x8c, + IOMMUFD_CMD_HWPT_INVALIDATE = 0x8d, + IOMMUFD_CMD_FAULT_QUEUE_ALLOC = 0x8e, +}; + +/** + * 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 + * @IOMMU_HWPT_FAULT_ID_VALID: The fault_id field of hwpt allocation data is + * valid. + */ +enum iommufd_hwpt_alloc_flags { + IOMMU_HWPT_ALLOC_NEST_PARENT = 1 << 0, + IOMMU_HWPT_ALLOC_DIRTY_TRACKING = 1 << 1, + IOMMU_HWPT_FAULT_ID_VALID = 1 << 2, +}; + +/** + * 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 = 0, + IOMMU_HWPT_DATA_VTD_S1 = 1, +}; + +/** + * 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 + * @fault_id: The ID of IOMMUFD_FAULT object. Valid only if flags field of + * IOMMU_HWPT_FAULT_ID_VALID is set. + * @__reserved2: Padding to 64-bit alignment. Must be 0. + * + * 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; + __u32 fault_id; + __u32 __reserved2; +}; +#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 = 0, + IOMMU_HW_INFO_TYPE_INTEL_VTD = 1, +}; + +/** + * 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 = 0, +}; + +/** + * 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) + +/** + * enum iommu_hwpt_pgfault_flags - flags for struct iommu_hwpt_pgfault + * @IOMMU_PGFAULT_FLAGS_PASID_VALID: The pasid field of the fault data is + * valid. + * @IOMMU_PGFAULT_FLAGS_LAST_PAGE: It's the last fault of a fault group. + */ +enum iommu_hwpt_pgfault_flags { + IOMMU_PGFAULT_FLAGS_PASID_VALID = (1 << 0), + IOMMU_PGFAULT_FLAGS_LAST_PAGE = (1 << 1), +}; + +/** + * enum iommu_hwpt_pgfault_perm - perm bits for struct iommu_hwpt_pgfault + * @IOMMU_PGFAULT_PERM_READ: request for read permission + * @IOMMU_PGFAULT_PERM_WRITE: request for write permission + * @IOMMU_PGFAULT_PERM_EXEC: (PCIE 10.4.1) request with a PASID that has the + * Execute Requested bit set in PASID TLP Prefix. + * @IOMMU_PGFAULT_PERM_PRIV: (PCIE 10.4.1) request with a PASID that has the + * Privileged Mode Requested bit set in PASID TLP + * Prefix. + */ +enum iommu_hwpt_pgfault_perm { + IOMMU_PGFAULT_PERM_READ = (1 << 0), + IOMMU_PGFAULT_PERM_WRITE = (1 << 1), + IOMMU_PGFAULT_PERM_EXEC = (1 << 2), + IOMMU_PGFAULT_PERM_PRIV = (1 << 3), +}; + +/** + * struct iommu_hwpt_pgfault - iommu page fault data + * @flags: Combination of enum iommu_hwpt_pgfault_flags + * @dev_id: id of the originated device + * @pasid: Process Address Space ID + * @grpid: Page Request Group Index + * @perm: Combination of enum iommu_hwpt_pgfault_perm + * @addr: Fault address + * @length: a hint of how much data the requestor is expecting to fetch. For + * example, if the PRI initiator knows it is going to do a 10MB + * transfer, it could fill in 10MB and the OS could pre-fault in + * 10MB of IOVA. It's default to 0 if there's no such hint. + * @cookie: kernel-managed cookie identifying a group of fault messages. The + * cookie number encoded in the last page fault of the group should + * be echoed back in the response message. + */ +struct iommu_hwpt_pgfault { + __u32 flags; + __u32 dev_id; + __u32 pasid; + __u32 grpid; + __u32 perm; + __u64 addr; + __u32 length; + __u32 cookie; +}; + +/** + * enum iommufd_page_response_code - Return status of fault handlers + * @IOMMUFD_PAGE_RESP_SUCCESS: Fault has been handled and the page tables + * populated, retry the access. This is the + * "Success" defined in PCI 10.4.2.1. + * @IOMMUFD_PAGE_RESP_INVALID: Could not handle this fault, don't retry the + * access. This is the "Invalid Request" in PCI + * 10.4.2.1. + */ +enum iommufd_page_response_code { + IOMMUFD_PAGE_RESP_SUCCESS = 0, + IOMMUFD_PAGE_RESP_INVALID = 1, +}; + +/** + * struct iommu_hwpt_page_response - IOMMU page fault response + * @cookie: The kernel-managed cookie reported in the fault message. + * @code: One of response code in enum iommufd_page_response_code. + */ +struct iommu_hwpt_page_response { + __u32 cookie; + __u32 code; +}; + +/** + * struct iommu_fault_alloc - ioctl(IOMMU_FAULT_QUEUE_ALLOC) + * @size: sizeof(struct iommu_fault_alloc) + * @flags: Must be 0 + * @out_fault_id: The ID of the new FAULT + * @out_fault_fd: The fd of the new FAULT + * + * Explicitly allocate a fault handling object. + */ +struct iommu_fault_alloc { + __u32 size; + __u32 flags; + __u32 out_fault_id; + __u32 out_fault_fd; +}; +#define IOMMU_FAULT_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_FAULT_QUEUE_ALLOC) +#endif diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index ebdafa576d..49dd1b30ce 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -16,83 +16,10 @@ #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 */ -}; +/* + * Backwards-compatible definitions. + */ +#define __KVM_HAVE_GUEST_DEBUG /* for KVM_SET_USER_MEMORY_REGION */ struct kvm_userspace_memory_region { @@ -103,13 +30,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 { @@ -149,43 +90,6 @@ struct kvm_pit_config { #define KVM_PIT_SPEAKER_DUMMY 1 -struct kvm_s390_skeys { - __u64 start_gfn; - __u64 count; - __u64 skeydata_addr; - __u32 flags; - __u32 reserved[9]; -}; - -#define KVM_S390_CMMA_PEEK (1 << 0) - -/** - * kvm_s390_cmma_log - Used for CMMA migration. - * - * Used both for input and output. - * - * @start_gfn: Guest page number to start from. - * @count: Size of the result buffer. - * @flags: Control operation mode via KVM_S390_CMMA_* flags - * @remaining: Used with KVM_S390_GET_CMMA_BITS. Indicates how many dirty - * pages are still remaining. - * @mask: Used with KVM_S390_SET_CMMA_BITS. Bitmap of bits to actually set - * in the PGSTE. - * @values: Pointer to the values buffer. - * - * Used in KVM_S390_{G,S}ET_CMMA_BITS ioctls. - */ -struct kvm_s390_cmma_log { - __u64 start_gfn; - __u32 count; - __u32 flags; - union { - __u64 remaining; - __u64 mask; - }; - __u64 values; -}; - struct kvm_hyperv_exit { #define KVM_EXIT_HYPERV_SYNIC 1 #define KVM_EXIT_HYPERV_HCALL 2 @@ -272,6 +176,8 @@ struct kvm_xen_exit { #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. */ @@ -286,11 +192,20 @@ struct kvm_xen_exit { /* Flags that describe what fields in emulation_failure hold valid data. */ #define KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES (1ULL << 0) +/* + * struct kvm_run can be modified by userspace at any time, so KVM must be + * careful to avoid TOCTOU bugs. In order to protect KVM, HINT_UNSAFE_IN_KVM() + * renames fields in struct kvm_run from to __unsafe when + * compiled into the kernel, ensuring that any use within KVM is obvious and + * gets extra scrutiny. + */ +#define HINT_UNSAFE_IN_KVM(_symbol) _symbol + /* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */ struct kvm_run { /* in */ __u8 request_interrupt_window; - __u8 immediate_exit; + __u8 HINT_UNSAFE_IN_KVM(immediate_exit); __u8 padding1[6]; /* out */ @@ -344,13 +259,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 { @@ -365,11 +290,6 @@ struct kvm_run { __u32 ipb; } s390_sieic; /* KVM_EXIT_S390_RESET */ -#define KVM_S390_RESET_POR 1 -#define KVM_S390_RESET_CLEAR 2 -#define KVM_S390_RESET_SUBSYSTEM 4 -#define KVM_S390_RESET_CPU_INIT 8 -#define KVM_S390_RESET_IPL 16 __u64 s390_reset_flags; /* KVM_EXIT_S390_UCONTROL */ struct { @@ -483,6 +403,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 */ @@ -508,6 +431,13 @@ struct kvm_run { #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]; }; @@ -574,35 +504,6 @@ struct kvm_translation { __u8 pad[5]; }; -/* for KVM_S390_MEM_OP */ -struct kvm_s390_mem_op { - /* in */ - __u64 gaddr; /* the guest address */ - __u64 flags; /* flags */ - __u32 size; /* amount of bytes */ - __u32 op; /* type of operation */ - __u64 buf; /* buffer in userspace */ - union { - struct { - __u8 ar; /* the access register number */ - __u8 key; /* access key, ignored if flag unset */ - }; - __u32 sida_offset; /* offset into the sida */ - __u8 reserved[32]; /* ignored */ - }; -}; -/* types for kvm_s390_mem_op->op */ -#define KVM_S390_MEMOP_LOGICAL_READ 0 -#define KVM_S390_MEMOP_LOGICAL_WRITE 1 -#define KVM_S390_MEMOP_SIDA_READ 2 -#define KVM_S390_MEMOP_SIDA_WRITE 3 -#define KVM_S390_MEMOP_ABSOLUTE_READ 4 -#define KVM_S390_MEMOP_ABSOLUTE_WRITE 5 -/* 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) - /* for KVM_INTERRUPT */ struct kvm_interrupt { /* in */ @@ -667,124 +568,6 @@ struct kvm_mp_state { __u32 mp_state; }; -struct kvm_s390_psw { - __u64 mask; - __u64 addr; -}; - -/* valid values for type in kvm_s390_interrupt */ -#define KVM_S390_SIGP_STOP 0xfffe0000u -#define KVM_S390_PROGRAM_INT 0xfffe0001u -#define KVM_S390_SIGP_SET_PREFIX 0xfffe0002u -#define KVM_S390_RESTART 0xfffe0003u -#define KVM_S390_INT_PFAULT_INIT 0xfffe0004u -#define KVM_S390_INT_PFAULT_DONE 0xfffe0005u -#define KVM_S390_MCHK 0xfffe1000u -#define KVM_S390_INT_CLOCK_COMP 0xffff1004u -#define KVM_S390_INT_CPU_TIMER 0xffff1005u -#define KVM_S390_INT_VIRTIO 0xffff2603u -#define KVM_S390_INT_SERVICE 0xffff2401u -#define KVM_S390_INT_EMERGENCY 0xffff1201u -#define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u -/* Anything below 0xfffe0000u is taken by INT_IO */ -#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \ - (((schid)) | \ - ((ssid) << 16) | \ - ((cssid) << 18) | \ - ((ai) << 26)) -#define KVM_S390_INT_IO_MIN 0x00000000u -#define KVM_S390_INT_IO_MAX 0xfffdffffu -#define KVM_S390_INT_IO_AI_MASK 0x04000000u - - -struct kvm_s390_interrupt { - __u32 type; - __u32 parm; - __u64 parm64; -}; - -struct kvm_s390_io_info { - __u16 subchannel_id; - __u16 subchannel_nr; - __u32 io_int_parm; - __u32 io_int_word; -}; - -struct kvm_s390_ext_info { - __u32 ext_params; - __u32 pad; - __u64 ext_params2; -}; - -struct kvm_s390_pgm_info { - __u64 trans_exc_code; - __u64 mon_code; - __u64 per_address; - __u32 data_exc_code; - __u16 code; - __u16 mon_class_nr; - __u8 per_code; - __u8 per_atmid; - __u8 exc_access_id; - __u8 per_access_id; - __u8 op_access_id; -#define KVM_S390_PGM_FLAGS_ILC_VALID 0x01 -#define KVM_S390_PGM_FLAGS_ILC_0 0x02 -#define KVM_S390_PGM_FLAGS_ILC_1 0x04 -#define KVM_S390_PGM_FLAGS_ILC_MASK 0x06 -#define KVM_S390_PGM_FLAGS_NO_REWIND 0x08 - __u8 flags; - __u8 pad[2]; -}; - -struct kvm_s390_prefix_info { - __u32 address; -}; - -struct kvm_s390_extcall_info { - __u16 code; -}; - -struct kvm_s390_emerg_info { - __u16 code; -}; - -#define KVM_S390_STOP_FLAG_STORE_STATUS 0x01 -struct kvm_s390_stop_info { - __u32 flags; -}; - -struct kvm_s390_mchk_info { - __u64 cr14; - __u64 mcic; - __u64 failing_storage_address; - __u32 ext_damage_code; - __u32 pad; - __u8 fixed_logout[16]; -}; - -struct kvm_s390_irq { - __u64 type; - union { - struct kvm_s390_io_info io; - struct kvm_s390_ext_info ext; - struct kvm_s390_pgm_info pgm; - struct kvm_s390_emerg_info emerg; - struct kvm_s390_extcall_info extcall; - struct kvm_s390_prefix_info prefix; - struct kvm_s390_stop_info stop; - struct kvm_s390_mchk_info mchk; - char reserved[64]; - } u; -}; - -struct kvm_s390_irq_state { - __u64 buf; - __u32 flags; /* will stay unused for compatibility reasons */ - __u32 len; - __u32 reserved[4]; /* will stay unused for compatibility reasons */ -}; - /* for KVM_SET_GUEST_DEBUG */ #define KVM_GUESTDBG_ENABLE 0x00000001 @@ -840,50 +623,6 @@ struct kvm_enable_cap { __u8 pad[64]; }; -/* for KVM_PPC_GET_PVINFO */ - -#define KVM_PPC_PVINFO_FLAGS_EV_IDLE (1<<0) - -struct kvm_ppc_pvinfo { - /* out */ - __u32 flags; - __u32 hcall[4]; - __u8 pad[108]; -}; - -/* for KVM_PPC_GET_SMMU_INFO */ -#define KVM_PPC_PAGE_SIZES_MAX_SZ 8 - -struct kvm_ppc_one_page_size { - __u32 page_shift; /* Page shift (or 0) */ - __u32 pte_enc; /* Encoding in the HPTE (>>12) */ -}; - -struct kvm_ppc_one_seg_page_size { - __u32 page_shift; /* Base page shift of segment (or 0) */ - __u32 slb_enc; /* SLB encoding for BookS */ - struct kvm_ppc_one_page_size enc[KVM_PPC_PAGE_SIZES_MAX_SZ]; -}; - -#define KVM_PPC_PAGE_SIZES_REAL 0x00000001 -#define KVM_PPC_1T_SEGMENTS 0x00000002 -#define KVM_PPC_NO_HASH 0x00000004 - -struct kvm_ppc_smmu_info { - __u64 flags; - __u32 slb_size; - __u16 data_keys; /* # storage keys supported for data */ - __u16 instr_keys; /* # storage keys supported for instructions */ - struct kvm_ppc_one_seg_page_size sps[KVM_PPC_PAGE_SIZES_MAX_SZ]; -}; - -/* for KVM_PPC_RESIZE_HPT_{PREPARE,COMMIT} */ -struct kvm_ppc_resize_hpt { - __u64 flags; - __u32 shift; - __u32 pad; -}; - #define KVMIO 0xAE /* machine type bits, to be used as argument to KVM_CREATE_VM */ @@ -927,9 +666,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) @@ -956,9 +692,7 @@ struct kvm_ppc_resize_hpt { /* Bug in KVM_SET_USER_MEMORY_REGION fixed: */ #define KVM_CAP_DESTROY_MEMORY_REGION_WORKS 21 #define KVM_CAP_USER_NMI 22 -#ifdef __KVM_HAVE_GUEST_DEBUG #define KVM_CAP_SET_GUEST_DEBUG 23 -#endif #ifdef __KVM_HAVE_PIT #define KVM_CAP_REINJECT_CONTROL 24 #endif @@ -1175,8 +909,22 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_VM_DISABLE_NX_HUGE_PAGES 220 #define KVM_CAP_S390_ZPCI_OP 221 #define KVM_CAP_S390_CPU_TOPOLOGY 222 - -#ifdef KVM_CAP_IRQ_ROUTING +#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 +#define KVM_CAP_PRE_FAULT_MEMORY 236 +#define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 +#define KVM_CAP_X86_GUEST_MODE 238 struct kvm_irq_routing_irqchip { __u32 irqchip; @@ -1242,40 +990,6 @@ struct kvm_irq_routing { struct kvm_irq_routing_entry entries[]; }; -#endif - -#ifdef KVM_CAP_MCE -/* x86 MCE */ -struct kvm_x86_mce { - __u64 status; - __u64 addr; - __u64 misc; - __u64 mcg_status; - __u8 bank; - __u8 pad1[7]; - __u64 pad2[3]; -}; -#endif - -#ifdef KVM_CAP_XEN_HVM -#define KVM_XEN_HVM_CONFIG_HYPERCALL_MSR (1 << 0) -#define KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL (1 << 1) -#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) - -struct kvm_xen_hvm_config { - __u32 flags; - __u32 msr; - __u64 blob_addr_32; - __u64 blob_addr_64; - __u8 blob_size_32; - __u8 blob_size_64; - __u8 pad2[30]; -}; -#endif - #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0) /* * Available with KVM_CAP_IRQFD_RESAMPLE @@ -1344,6 +1058,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 @@ -1400,9 +1115,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 { @@ -1426,6 +1148,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, }; @@ -1434,31 +1158,22 @@ 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 { - __u64 user_addr; - __u64 vcpu_addr; - __u64 length; -}; #define KVM_S390_UCAS_MAP _IOW(KVMIO, 0x50, struct kvm_s390_ucas_mapping) #define KVM_S390_UCAS_UNMAP _IOW(KVMIO, 0x51, struct kvm_s390_ucas_mapping) #define KVM_S390_VCPU_FAULT _IOW(KVMIO, 0x52, unsigned long) @@ -1476,20 +1191,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) @@ -1506,9 +1209,6 @@ struct kvm_s390_ucas_mapping { * 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 */ @@ -1529,9 +1229,9 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_SPAPR_RESIZE_HPT */ #define KVM_PPC_RESIZE_HPT_PREPARE _IOR(KVMIO, 0xad, struct kvm_ppc_resize_hpt) #define KVM_PPC_RESIZE_HPT_COMMIT _IOR(KVMIO, 0xae, struct kvm_ppc_resize_hpt) -/* Available with KVM_CAP_PPC_RADIX_MMU or KVM_CAP_PPC_HASH_MMU_V3 */ +/* Available with KVM_CAP_PPC_MMU_RADIX or KVM_CAP_PPC_MMU_HASH_V3 */ #define KVM_PPC_CONFIGURE_V3_MMU _IOW(KVMIO, 0xaf, struct kvm_ppc_mmuv3_cfg) -/* Available with KVM_CAP_PPC_RADIX_MMU */ +/* Available with KVM_CAP_PPC_MMU_RADIX */ #define KVM_PPC_GET_RMMU_INFO _IOW(KVMIO, 0xb0, struct kvm_ppc_rmmu_info) /* Available with KVM_CAP_PPC_GET_CPU_CHAR */ #define KVM_PPC_GET_CPU_CHAR _IOR(KVMIO, 0xb1, struct kvm_ppc_cpu_char) @@ -1539,6 +1239,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) @@ -1558,8 +1261,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) @@ -1601,7 +1302,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) @@ -1667,87 +1368,6 @@ struct kvm_enc_region { #define KVM_S390_NORMAL_RESET _IO(KVMIO, 0xc3) #define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4) -struct kvm_s390_pv_sec_parm { - __u64 origin; - __u64 length; -}; - -struct kvm_s390_pv_unp { - __u64 addr; - __u64 size; - __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, - KVM_PV_SET_SEC_PARMS, - KVM_PV_UNPACK, - KVM_PV_VERIFY, - KVM_PV_PREP_RESET, - KVM_PV_UNSHARE_ALL, - KVM_PV_INFO, - KVM_PV_DUMP, -}; - -struct kvm_pv_cmd { - __u32 cmd; /* Command to be executed */ - __u16 rc; /* Ultravisor return code */ - __u16 rrc; /* Ultravisor return reason code */ - __u64 data; /* Data or address */ - __u32 flags; /* flags for future extensions. Must be 0 for now */ - __u32 reserved[3]; -}; - /* Available with KVM_CAP_S390_PROTECTED */ #define KVM_S390_PV_COMMAND _IOWR(KVMIO, 0xc5, struct kvm_pv_cmd) @@ -1761,53 +1381,6 @@ struct kvm_pv_cmd { #define KVM_XEN_HVM_GET_ATTR _IOWR(KVMIO, 0xc8, struct kvm_xen_hvm_attr) #define KVM_XEN_HVM_SET_ATTR _IOW(KVMIO, 0xc9, struct kvm_xen_hvm_attr) -struct kvm_xen_hvm_attr { - __u16 type; - __u16 pad[3]; - union { - __u8 long_mode; - __u8 vector; - struct { - __u64 gfn; - } 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 - /* 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) @@ -1818,241 +1391,6 @@ struct kvm_xen_hvm_attr { #define KVM_GET_SREGS2 _IOR(KVMIO, 0xcc, struct kvm_sregs2) #define KVM_SET_SREGS2 _IOW(KVMIO, 0xcd, struct kvm_sregs2) -struct kvm_xen_vcpu_attr { - __u16 type; - __u16 pad[3]; - union { - __u64 gpa; - __u64 pad[8]; - struct { - __u64 state; - __u64 state_entry_time; - __u64 time_running; - __u64 time_runnable; - __u64 time_blocked; - __u64 time_offline; - } runstate; - __u32 vcpu_id; - struct { - __u32 port; - __u32 priority; - __u64 expires_ns; - } timer; - __u8 vector; - } u; -}; - -/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */ -#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO 0x0 -#define KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO 0x1 -#define KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR 0x2 -#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 { - /* Guest initialization commands */ - KVM_SEV_INIT = 0, - KVM_SEV_ES_INIT, - /* Guest launch commands */ - KVM_SEV_LAUNCH_START, - KVM_SEV_LAUNCH_UPDATE_DATA, - KVM_SEV_LAUNCH_UPDATE_VMSA, - KVM_SEV_LAUNCH_SECRET, - KVM_SEV_LAUNCH_MEASURE, - KVM_SEV_LAUNCH_FINISH, - /* Guest migration commands (outgoing) */ - KVM_SEV_SEND_START, - KVM_SEV_SEND_UPDATE_DATA, - KVM_SEV_SEND_UPDATE_VMSA, - KVM_SEV_SEND_FINISH, - /* Guest migration commands (incoming) */ - KVM_SEV_RECEIVE_START, - KVM_SEV_RECEIVE_UPDATE_DATA, - KVM_SEV_RECEIVE_UPDATE_VMSA, - KVM_SEV_RECEIVE_FINISH, - /* Guest status and debug commands */ - KVM_SEV_GUEST_STATUS, - KVM_SEV_DBG_DECRYPT, - KVM_SEV_DBG_ENCRYPT, - /* Guest certificates commands */ - KVM_SEV_CERT_EXPORT, - /* Attestation report */ - KVM_SEV_GET_ATTESTATION_REPORT, - /* Guest Migration Extension */ - KVM_SEV_SEND_CANCEL, - - KVM_SEV_NR_MAX, -}; - -struct kvm_sev_cmd { - __u32 id; - __u64 data; - __u32 error; - __u32 sev_fd; -}; - -struct kvm_sev_launch_start { - __u32 handle; - __u32 policy; - __u64 dh_uaddr; - __u32 dh_len; - __u64 session_uaddr; - __u32 session_len; -}; - -struct kvm_sev_launch_update_data { - __u64 uaddr; - __u32 len; -}; - - -struct kvm_sev_launch_secret { - __u64 hdr_uaddr; - __u32 hdr_len; - __u64 guest_uaddr; - __u32 guest_len; - __u64 trans_uaddr; - __u32 trans_len; -}; - -struct kvm_sev_launch_measure { - __u64 uaddr; - __u32 len; -}; - -struct kvm_sev_guest_status { - __u32 handle; - __u32 policy; - __u32 state; -}; - -struct kvm_sev_dbg { - __u64 src_uaddr; - __u64 dst_uaddr; - __u32 len; -}; - -struct kvm_sev_attestation_report { - __u8 mnonce[16]; - __u64 uaddr; - __u32 len; -}; - -struct kvm_sev_send_start { - __u32 policy; - __u64 pdh_cert_uaddr; - __u32 pdh_cert_len; - __u64 plat_certs_uaddr; - __u32 plat_certs_len; - __u64 amd_certs_uaddr; - __u32 amd_certs_len; - __u64 session_uaddr; - __u32 session_len; -}; - -struct kvm_sev_send_update_data { - __u64 hdr_uaddr; - __u32 hdr_len; - __u64 guest_uaddr; - __u32 guest_len; - __u64 trans_uaddr; - __u32 trans_len; -}; - -struct kvm_sev_receive_start { - __u32 handle; - __u32 policy; - __u64 pdh_uaddr; - __u32 pdh_len; - __u64 session_uaddr; - __u32 session_len; -}; - -struct kvm_sev_receive_update_data { - __u64 hdr_uaddr; - __u32 hdr_len; - __u64 guest_uaddr; - __u32 guest_len; - __u64 trans_uaddr; - __u32 trans_len; -}; - -#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) -#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) -#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) - -struct kvm_assigned_pci_dev { - __u32 assigned_dev_id; - __u32 busnr; - __u32 devfn; - __u32 flags; - __u32 segnr; - union { - __u32 reserved[11]; - }; -}; - -#define KVM_DEV_IRQ_HOST_INTX (1 << 0) -#define KVM_DEV_IRQ_HOST_MSI (1 << 1) -#define KVM_DEV_IRQ_HOST_MSIX (1 << 2) - -#define KVM_DEV_IRQ_GUEST_INTX (1 << 8) -#define KVM_DEV_IRQ_GUEST_MSI (1 << 9) -#define KVM_DEV_IRQ_GUEST_MSIX (1 << 10) - -#define KVM_DEV_IRQ_HOST_MASK 0x00ff -#define KVM_DEV_IRQ_GUEST_MASK 0xff00 - -struct kvm_assigned_irq { - __u32 assigned_dev_id; - __u32 host_irq; /* ignored (legacy field) */ - __u32 guest_irq; - __u32 flags; - union { - __u32 reserved[12]; - }; -}; - -struct kvm_assigned_msix_nr { - __u32 assigned_dev_id; - __u16 entry_nr; - __u16 padding; -}; - -#define KVM_MAX_MSIX_PER_DEV 256 -struct kvm_assigned_msix_entry { - __u32 assigned_dev_id; - __u32 gsi; - __u16 entry; /* The index of entry in the MSI-X table */ - __u16 padding[3]; -}; - -#define KVM_X2APIC_API_USE_32BIT_IDS (1ULL << 0) -#define KVM_X2APIC_API_DISABLE_BROADCAST_QUIRK (1ULL << 1) - -/* Available with KVM_CAP_ARM_USER_IRQ */ - -/* Bits for run->s.regs.device_irq_level */ -#define KVM_ARM_DEV_EL1_VTIMER (1 << 0) -#define KVM_ARM_DEV_EL1_PTIMER (1 << 1) -#define KVM_ARM_DEV_PMU (1 << 2) - -struct kvm_hyperv_eventfd { - __u32 conn_id; - __s32 fd; - __u32 flags; - __u32 padding[3]; -}; - -#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff -#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) - #define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (1 << 0) #define KVM_DIRTY_LOG_INITIALLY_SET (1 << 1) @@ -2198,31 +1536,33 @@ struct kvm_stats_desc { /* 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; +/* 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; }; -/* types for kvm_s390_zpci_op->op */ -#define KVM_S390_ZPCIOP_REG_AEN 0 -#define KVM_S390_ZPCIOP_DEREG_AEN 1 +#define KVM_MEMORY_ATTRIBUTE_PRIVATE (1ULL << 3) -/* flags for kvm_s390_zpci_op->u.reg_aen.flags */ -#define KVM_S390_ZPCIOP_REGAEN_HOST (1 << 0) +#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]; +}; + +#define KVM_PRE_FAULT_MEMORY _IOWR(KVMIO, 0xd5, struct kvm_pre_fault_memory) + +struct kvm_pre_fault_memory { + __u64 gpa; + __u64 size; + __u64 flags; + __u64 padding[5]; +}; #endif /* __LINUX_KVM_H */ diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h new file mode 100644 index 0000000000..6a1e672259 --- /dev/null +++ b/linux-headers/linux/kvm_para.h @@ -0,0 +1,2 @@ +#include "standard-headers/linux/kvm_para.h" +#include 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..2b83059586 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 @@ -16,6 +17,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ #define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ +#define MAP_DROPPABLE 0x08 /* Zero memory under memory pressure. */ /* * Huge page size encoding when MAP_HUGETLB is specified, and a huge page @@ -41,4 +43,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..17bf191573 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -28,6 +28,10 @@ enum { SEV_PEK_CERT_IMPORT, SEV_GET_ID, /* This command is deprecated, use SEV_GET_ID2 */ SEV_GET_ID2, + SNP_PLATFORM_STATUS, + SNP_COMMIT, + SNP_SET_CONFIG, + SNP_VLEK_LOAD, SEV_MAX, }; @@ -36,10 +40,18 @@ 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, SEV_RET_INAVLID_CONFIG, + SEV_RET_INVALID_CONFIG = SEV_RET_INAVLID_CONFIG, SEV_RET_INVALID_LEN, SEV_RET_ALREADY_OWNED, SEV_RET_INVALID_CERTIFICATE, @@ -61,6 +73,13 @@ typedef enum { SEV_RET_INVALID_PARAM, SEV_RET_RESOURCE_LIMIT, SEV_RET_SECURE_DATA_INVALID, + SEV_RET_INVALID_KEY = 0x27, + SEV_RET_INVALID_PAGE_SIZE, + SEV_RET_INVALID_PAGE_STATE, + SEV_RET_INVALID_MDATA_ENTRY, + SEV_RET_INVALID_PAGE_OWNER, + SEV_RET_INVALID_PAGE_AEAD_OFLOW, + SEV_RET_RMP_INIT_REQUIRED, SEV_RET_MAX, } sev_ret_code; @@ -147,6 +166,82 @@ struct sev_user_data_get_id2 { __u32 length; /* In/Out */ } __attribute__((packed)); +/** + * struct sev_user_data_snp_status - SNP status + * + * @api_major: API major version + * @api_minor: API minor version + * @state: current platform state + * @is_rmp_initialized: whether RMP is initialized or not + * @rsvd: reserved + * @build_id: firmware build id for the API version + * @mask_chip_id: whether chip id is present in attestation reports or not + * @mask_chip_key: whether attestation reports are signed or not + * @vlek_en: VLEK (Version Loaded Endorsement Key) hashstick is loaded + * @rsvd1: reserved + * @guest_count: the number of guest currently managed by the firmware + * @current_tcb_version: current TCB version + * @reported_tcb_version: reported TCB version + */ +struct sev_user_data_snp_status { + __u8 api_major; /* Out */ + __u8 api_minor; /* Out */ + __u8 state; /* Out */ + __u8 is_rmp_initialized:1; /* Out */ + __u8 rsvd:7; + __u32 build_id; /* Out */ + __u32 mask_chip_id:1; /* Out */ + __u32 mask_chip_key:1; /* Out */ + __u32 vlek_en:1; /* Out */ + __u32 rsvd1:29; + __u32 guest_count; /* Out */ + __u64 current_tcb_version; /* Out */ + __u64 reported_tcb_version; /* Out */ +} __attribute__((packed)); + +/** + * struct sev_user_data_snp_config - system wide configuration value for SNP. + * + * @reported_tcb: the TCB version to report in the guest attestation report. + * @mask_chip_id: whether chip id is present in attestation reports or not + * @mask_chip_key: whether attestation reports are signed or not + * @rsvd: reserved + * @rsvd1: reserved + */ +struct sev_user_data_snp_config { + __u64 reported_tcb ; /* In */ + __u32 mask_chip_id:1; /* In */ + __u32 mask_chip_key:1; /* In */ + __u32 rsvd:30; /* In */ + __u8 rsvd1[52]; +} __attribute__((packed)); + +/** + * struct sev_data_snp_vlek_load - SNP_VLEK_LOAD structure + * + * @len: length of the command buffer read by the PSP + * @vlek_wrapped_version: version of wrapped VLEK hashstick (Must be 0h) + * @rsvd: reserved + * @vlek_wrapped_address: address of a wrapped VLEK hashstick + * (struct sev_user_data_snp_wrapped_vlek_hashstick) + */ +struct sev_user_data_snp_vlek_load { + __u32 len; /* In */ + __u8 vlek_wrapped_version; /* In */ + __u8 rsvd[3]; /* In */ + __u64 vlek_wrapped_address; /* In */ +} __attribute__((packed)); + +/** + * struct sev_user_data_snp_vlek_wrapped_vlek_hashstick - Wrapped VLEK data + * + * @data: Opaque data provided by AMD KDS (as described in SEV-SNP Firmware ABI + * 1.54, SNP_VLEK_LOAD) + */ +struct sev_user_data_snp_wrapped_vlek_hashstick { + __u8 data[432]; /* In */ +} __attribute__((packed)); + /** * struct sev_issue_cmd - SEV ioctl parameters * diff --git a/linux-headers/linux/stddef.h b/linux-headers/linux/stddef.h new file mode 100644 index 0000000000..96aa341942 --- /dev/null +++ b/linux-headers/linux/stddef.h @@ -0,0 +1,66 @@ +/* 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 + +#ifndef __counted_by_le +#define __counted_by_le(m) +#endif + +#ifndef __counted_by_be +#define __counted_by_be(m) +#endif + +#endif /* _LINUX_STDDEF_H */ diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h index a3a377cd44..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 @@ -34,7 +38,11 @@ UFFD_FEATURE_MINOR_HUGETLBFS | \ UFFD_FEATURE_MINOR_SHMEM | \ UFFD_FEATURE_EXACT_ADDRESS | \ - UFFD_FEATURE_WP_HUGETLBFS_SHMEM) + 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 | \ @@ -43,13 +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_WRITEPROTECT | \ (__u64)1 << _UFFDIO_CONTINUE | \ - (__u64)1 << _UFFDIO_WRITEPROTECT) + (__u64)1 << _UFFDIO_POISON) /* * Valid ioctl command number range with this API is from 0x00 to @@ -64,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 */ @@ -82,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 { @@ -199,6 +216,20 @@ struct uffdio_api { * * 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) @@ -213,6 +244,10 @@ struct uffdio_api { #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; @@ -293,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; /* @@ -302,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/vfio.h b/linux-headers/linux/vfio.h index ede44b5572..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 - _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,9 +864,10 @@ 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) @@ -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/vhost.h b/linux-headers/linux/vhost.h index f9f115a7c7..b95dd84eef 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. */ @@ -148,12 +179,6 @@ /* 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) @@ -180,4 +205,34 @@ */ #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) + + +/* 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 queue size of a specific virtqueue. + * userspace set the vring index in vhost_vring_state.index + * kernel set the queue size in vhost_vring_state.num + */ +#define VHOST_VDPA_GET_VRING_SIZE _IOWR(VHOST_VIRTIO, 0x82, \ + 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..c33a679c0f --- /dev/null +++ b/linux-user/aarch64/Makefile.vdso @@ -0,0 +1,16 @@ +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,-z,max-page-size=4096 -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 9875d609a9..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,15 +90,8 @@ void cpu_loop(CPUARMState *env) switch (trapnr) { case EXCP_SWI: - /* - * On syscall, PSTATE.ZA is preserved, along with the ZA matrix. - * PSTATE.SM is cleared, per SMSTOP, which does ResetSVEState. - */ - if (FIELD_EX64(env->svcr, SVCR, SM)) { - env->svcr = FIELD_DP64(env->svcr, SVCR, SM, 0); - arm_rebuild_hflags(env); - arm_reset_sve_state(env); - } + /* 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], @@ -195,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..f25a67a21e --- /dev/null +++ b/linux-user/aarch64/meson.build @@ -0,0 +1,19 @@ +# 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]) + +linux_user_ss.add(when: 'TARGET_AARCH64', if_true: [files('mte_user_helper.c')]) + +syscall_nr_generators += { + 'aarch64': generator(sh, + arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], + output: '@BASENAME@_nr.h') +} diff --git a/linux-user/aarch64/mte_user_helper.c b/linux-user/aarch64/mte_user_helper.c new file mode 100644 index 0000000000..a5b1c8503b --- /dev/null +++ b/linux-user/aarch64/mte_user_helper.c @@ -0,0 +1,35 @@ +/* + * ARM MemTag convenience functions. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "mte_user_helper.h" + +void arm_set_mte_tcf0(CPUArchState *env, abi_long value) +{ + /* + * Write PR_MTE_TCF to SCTLR_EL1[TCF0]. + * + * 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. + */ + unsigned tcf = 0; + if (value & PR_MTE_TCF_SYNC) { + tcf = 1; + } else if (value & PR_MTE_TCF_ASYNC) { + tcf = 2; + } + env->cp15.sctlr_el[1] = deposit64(env->cp15.sctlr_el[1], 38, 2, tcf); +} diff --git a/linux-user/aarch64/mte_user_helper.h b/linux-user/aarch64/mte_user_helper.h new file mode 100644 index 0000000000..8685e5175a --- /dev/null +++ b/linux-user/aarch64/mte_user_helper.h @@ -0,0 +1,32 @@ +/* + * ARM MemTag convenience functions. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef AARCH64_MTE_USER_HELPER_H +#define AARCH64_MTE USER_HELPER_H + +#ifndef PR_MTE_TCF_SHIFT +# define PR_MTE_TCF_SHIFT 1 +# define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT) +# define PR_MTE_TAG_SHIFT 3 +# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) +#endif + +/** + * arm_set_mte_tcf0 - Set TCF0 field in SCTLR_EL1 register + * @env: The CPU environment + * @value: The value to be set for the Tag Check Fault in EL0 field. + * + * Only SYNC and ASYNC modes can be selected. If ASYMM mode is given, the SYNC + * mode is selected instead. So, there is no way to set the ASYMM mode. + */ +void arm_set_mte_tcf0(CPUArchState *env, abi_long value); + +#endif /* AARCH64_MTE_USER_HELPER_H */ diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 6a2c6e06d2..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; @@ -665,20 +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. - * When clearing SM, ResetSVEState, per SMSTOP. - */ - if (FIELD_EX64(env->svcr, SVCR, SM)) { - arm_reset_sve_state(env); - } - if (env->svcr) { - env->svcr = 0; - arm_rebuild_hflags(env); - } + /* 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/syscall_64.tbl b/linux-user/aarch64/syscall_64.tbl new file mode 100644 index 0000000000..845e24eb37 --- /dev/null +++ b/linux-user/aarch64/syscall_64.tbl @@ -0,0 +1,405 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# This file contains the system call numbers for all of the +# more recently added architectures. +# +# As a basic principle, no duplication of functionality +# should be added, e.g. we don't use lseek when llseek +# is present. New architectures should use this file +# and implement the less feature-full calls in user space. +# +0 common io_setup sys_io_setup compat_sys_io_setup +1 common io_destroy sys_io_destroy +2 common io_submit sys_io_submit compat_sys_io_submit +3 common io_cancel sys_io_cancel +4 time32 io_getevents sys_io_getevents_time32 +4 64 io_getevents sys_io_getevents +5 common setxattr sys_setxattr +6 common lsetxattr sys_lsetxattr +7 common fsetxattr sys_fsetxattr +8 common getxattr sys_getxattr +9 common lgetxattr sys_lgetxattr +10 common fgetxattr sys_fgetxattr +11 common listxattr sys_listxattr +12 common llistxattr sys_llistxattr +13 common flistxattr sys_flistxattr +14 common removexattr sys_removexattr +15 common lremovexattr sys_lremovexattr +16 common fremovexattr sys_fremovexattr +17 common getcwd sys_getcwd +18 common lookup_dcookie sys_ni_syscall +19 common eventfd2 sys_eventfd2 +20 common epoll_create1 sys_epoll_create1 +21 common epoll_ctl sys_epoll_ctl +22 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +23 common dup sys_dup +24 common dup3 sys_dup3 +25 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +25 64 fcntl sys_fcntl +26 common inotify_init1 sys_inotify_init1 +27 common inotify_add_watch sys_inotify_add_watch +28 common inotify_rm_watch sys_inotify_rm_watch +29 common ioctl sys_ioctl compat_sys_ioctl +30 common ioprio_set sys_ioprio_set +31 common ioprio_get sys_ioprio_get +32 common flock sys_flock +33 common mknodat sys_mknodat +34 common mkdirat sys_mkdirat +35 common unlinkat sys_unlinkat +36 common symlinkat sys_symlinkat +37 common linkat sys_linkat +# renameat is superseded with flags by renameat2 +38 renameat renameat sys_renameat +39 common umount2 sys_umount +40 common mount sys_mount +41 common pivot_root sys_pivot_root +42 common nfsservctl sys_ni_syscall +43 32 statfs64 sys_statfs64 compat_sys_statfs64 +43 64 statfs sys_statfs +44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +44 64 fstatfs sys_fstatfs +45 32 truncate64 sys_truncate64 compat_sys_truncate64 +45 64 truncate sys_truncate +46 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +46 64 ftruncate sys_ftruncate +47 common fallocate sys_fallocate compat_sys_fallocate +48 common faccessat sys_faccessat +49 common chdir sys_chdir +50 common fchdir sys_fchdir +51 common chroot sys_chroot +52 common fchmod sys_fchmod +53 common fchmodat sys_fchmodat +54 common fchownat sys_fchownat +55 common fchown sys_fchown +56 common openat sys_openat +57 common close sys_close +58 common vhangup sys_vhangup +59 common pipe2 sys_pipe2 +60 common quotactl sys_quotactl +61 common getdents64 sys_getdents64 +62 32 llseek sys_llseek +62 64 lseek sys_lseek +63 common read sys_read +64 common write sys_write +65 common readv sys_readv sys_readv +66 common writev sys_writev sys_writev +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 common preadv sys_preadv compat_sys_preadv +70 common pwritev sys_pwritev compat_sys_pwritev +71 32 sendfile64 sys_sendfile64 +71 64 sendfile sys_sendfile64 +72 time32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +72 64 pselect6 sys_pselect6 +73 time32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +73 64 ppoll sys_ppoll +74 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +75 common vmsplice sys_vmsplice +76 common splice sys_splice +77 common tee sys_tee +78 common readlinkat sys_readlinkat +79 stat64 fstatat64 sys_fstatat64 +79 64 newfstatat sys_newfstatat +80 stat64 fstat64 sys_fstat64 +80 64 fstat sys_newfstat +81 common sync sys_sync +82 common fsync sys_fsync +83 common fdatasync sys_fdatasync +84 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +85 common timerfd_create sys_timerfd_create +86 time32 timerfd_settime sys_timerfd_settime32 +86 64 timerfd_settime sys_timerfd_settime +87 time32 timerfd_gettime sys_timerfd_gettime32 +87 64 timerfd_gettime sys_timerfd_gettime +88 time32 utimensat sys_utimensat_time32 +88 64 utimensat sys_utimensat +89 common acct sys_acct +90 common capget sys_capget +91 common capset sys_capset +92 common personality sys_personality +93 common exit sys_exit +94 common exit_group sys_exit_group +95 common waitid sys_waitid compat_sys_waitid +96 common set_tid_address sys_set_tid_address +97 common unshare sys_unshare +98 time32 futex sys_futex_time32 +98 64 futex sys_futex +99 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +100 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +101 time32 nanosleep sys_nanosleep_time32 +101 64 nanosleep sys_nanosleep +102 common getitimer sys_getitimer compat_sys_getitimer +103 common setitimer sys_setitimer compat_sys_setitimer +104 common kexec_load sys_kexec_load compat_sys_kexec_load +105 common init_module sys_init_module +106 common delete_module sys_delete_module +107 common timer_create sys_timer_create compat_sys_timer_create +108 time32 timer_gettime sys_timer_gettime32 +108 64 timer_gettime sys_timer_gettime +109 common timer_getoverrun sys_timer_getoverrun +110 time32 timer_settime sys_timer_settime32 +110 64 timer_settime sys_timer_settime +111 common timer_delete sys_timer_delete +112 time32 clock_settime sys_clock_settime32 +112 64 clock_settime sys_clock_settime +113 time32 clock_gettime sys_clock_gettime32 +113 64 clock_gettime sys_clock_gettime +114 time32 clock_getres sys_clock_getres_time32 +114 64 clock_getres sys_clock_getres +115 time32 clock_nanosleep sys_clock_nanosleep_time32 +115 64 clock_nanosleep sys_clock_nanosleep +116 common syslog sys_syslog +117 common ptrace sys_ptrace compat_sys_ptrace +118 common sched_setparam sys_sched_setparam +119 common sched_setscheduler sys_sched_setscheduler +120 common sched_getscheduler sys_sched_getscheduler +121 common sched_getparam sys_sched_getparam +122 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +123 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +124 common sched_yield sys_sched_yield +125 common sched_get_priority_max sys_sched_get_priority_max +126 common sched_get_priority_min sys_sched_get_priority_min +127 time32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +127 64 sched_rr_get_interval sys_sched_rr_get_interval +128 common restart_syscall sys_restart_syscall +129 common kill sys_kill +130 common tkill sys_tkill +131 common tgkill sys_tgkill +132 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +133 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +134 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +135 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +136 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +137 time32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +137 64 rt_sigtimedwait sys_rt_sigtimedwait +138 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +139 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +140 common setpriority sys_setpriority +141 common getpriority sys_getpriority +142 common reboot sys_reboot +143 common setregid sys_setregid +144 common setgid sys_setgid +145 common setreuid sys_setreuid +146 common setuid sys_setuid +147 common setresuid sys_setresuid +148 common getresuid sys_getresuid +149 common setresgid sys_setresgid +150 common getresgid sys_getresgid +151 common setfsuid sys_setfsuid +152 common setfsgid sys_setfsgid +153 common times sys_times compat_sys_times +154 common setpgid sys_setpgid +155 common getpgid sys_getpgid +156 common getsid sys_getsid +157 common setsid sys_setsid +158 common getgroups sys_getgroups +159 common setgroups sys_setgroups +160 common uname sys_newuname +161 common sethostname sys_sethostname +162 common setdomainname sys_setdomainname +# getrlimit and setrlimit are superseded with prlimit64 +163 rlimit getrlimit sys_getrlimit compat_sys_getrlimit +164 rlimit setrlimit sys_setrlimit compat_sys_setrlimit +165 common getrusage sys_getrusage compat_sys_getrusage +166 common umask sys_umask +167 common prctl sys_prctl +168 common getcpu sys_getcpu +169 time32 gettimeofday sys_gettimeofday compat_sys_gettimeofday +169 64 gettimeofday sys_gettimeofday +170 time32 settimeofday sys_settimeofday compat_sys_settimeofday +170 64 settimeofday sys_settimeofday +171 time32 adjtimex sys_adjtimex_time32 +171 64 adjtimex sys_adjtimex +172 common getpid sys_getpid +173 common getppid sys_getppid +174 common getuid sys_getuid +175 common geteuid sys_geteuid +176 common getgid sys_getgid +177 common getegid sys_getegid +178 common gettid sys_gettid +179 common sysinfo sys_sysinfo compat_sys_sysinfo +180 common mq_open sys_mq_open compat_sys_mq_open +181 common mq_unlink sys_mq_unlink +182 time32 mq_timedsend sys_mq_timedsend_time32 +182 64 mq_timedsend sys_mq_timedsend +183 time32 mq_timedreceive sys_mq_timedreceive_time32 +183 64 mq_timedreceive sys_mq_timedreceive +184 common mq_notify sys_mq_notify compat_sys_mq_notify +185 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +186 common msgget sys_msgget +187 common msgctl sys_msgctl compat_sys_msgctl +188 common msgrcv sys_msgrcv compat_sys_msgrcv +189 common msgsnd sys_msgsnd compat_sys_msgsnd +190 common semget sys_semget +191 common semctl sys_semctl compat_sys_semctl +192 time32 semtimedop sys_semtimedop_time32 +192 64 semtimedop sys_semtimedop +193 common semop sys_semop +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +196 common shmat sys_shmat compat_sys_shmat +197 common shmdt sys_shmdt +198 common socket sys_socket +199 common socketpair sys_socketpair +200 common bind sys_bind +201 common listen sys_listen +202 common accept sys_accept +203 common connect sys_connect +204 common getsockname sys_getsockname +205 common getpeername sys_getpeername +206 common sendto sys_sendto +207 common recvfrom sys_recvfrom compat_sys_recvfrom +208 common setsockopt sys_setsockopt sys_setsockopt +209 common getsockopt sys_getsockopt sys_getsockopt +210 common shutdown sys_shutdown +211 common sendmsg sys_sendmsg compat_sys_sendmsg +212 common recvmsg sys_recvmsg compat_sys_recvmsg +213 common readahead sys_readahead compat_sys_readahead +214 common brk sys_brk +215 common munmap sys_munmap +216 common mremap sys_mremap +217 common add_key sys_add_key +218 common request_key sys_request_key +219 common keyctl sys_keyctl compat_sys_keyctl +220 common clone sys_clone +221 common execve sys_execve compat_sys_execve +222 32 mmap2 sys_mmap2 +222 64 mmap sys_mmap +223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +223 64 fadvise64 sys_fadvise64_64 +224 common swapon sys_swapon +225 common swapoff sys_swapoff +226 common mprotect sys_mprotect +227 common msync sys_msync +228 common mlock sys_mlock +229 common munlock sys_munlock +230 common mlockall sys_mlockall +231 common munlockall sys_munlockall +232 common mincore sys_mincore +233 common madvise sys_madvise +234 common remap_file_pages sys_remap_file_pages +235 common mbind sys_mbind +236 common get_mempolicy sys_get_mempolicy +237 common set_mempolicy sys_set_mempolicy +238 common migrate_pages sys_migrate_pages +239 common move_pages sys_move_pages +240 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +241 common perf_event_open sys_perf_event_open +242 common accept4 sys_accept4 +243 time32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +243 64 recvmmsg sys_recvmmsg +# Architectures may provide up to 16 syscalls of their own between 244 and 259 +244 arc cacheflush sys_cacheflush +245 arc arc_settls sys_arc_settls +246 arc arc_gettls sys_arc_gettls +247 arc sysfs sys_sysfs +248 arc arc_usr_cmpxchg sys_arc_usr_cmpxchg + +244 csky set_thread_area sys_set_thread_area +245 csky cacheflush sys_cacheflush + +244 nios2 cacheflush sys_cacheflush + +244 or1k or1k_atomic sys_or1k_atomic + +258 riscv riscv_hwprobe sys_riscv_hwprobe +259 riscv riscv_flush_icache sys_riscv_flush_icache + +260 time32 wait4 sys_wait4 compat_sys_wait4 +260 64 wait4 sys_wait4 +261 common prlimit64 sys_prlimit64 +262 common fanotify_init sys_fanotify_init +263 common fanotify_mark sys_fanotify_mark +264 common name_to_handle_at sys_name_to_handle_at +265 common open_by_handle_at sys_open_by_handle_at +266 time32 clock_adjtime sys_clock_adjtime32 +266 64 clock_adjtime sys_clock_adjtime +267 common syncfs sys_syncfs +268 common setns sys_setns +269 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +270 common process_vm_readv sys_process_vm_readv +271 common process_vm_writev sys_process_vm_writev +272 common kcmp sys_kcmp +273 common finit_module sys_finit_module +274 common sched_setattr sys_sched_setattr +275 common sched_getattr sys_sched_getattr +276 common renameat2 sys_renameat2 +277 common seccomp sys_seccomp +278 common getrandom sys_getrandom +279 common memfd_create sys_memfd_create +280 common bpf sys_bpf +281 common execveat sys_execveat compat_sys_execveat +282 common userfaultfd sys_userfaultfd +283 common membarrier sys_membarrier +284 common mlock2 sys_mlock2 +285 common copy_file_range sys_copy_file_range +286 common preadv2 sys_preadv2 compat_sys_preadv2 +287 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +288 common pkey_mprotect sys_pkey_mprotect +289 common pkey_alloc sys_pkey_alloc +290 common pkey_free sys_pkey_free +291 common statx sys_statx +292 time32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +292 64 io_pgetevents sys_io_pgetevents +293 common rseq sys_rseq +294 common kexec_file_load sys_kexec_file_load +# 295 through 402 are unassigned to sync up with generic numbers don't use +403 32 clock_gettime64 sys_clock_gettime +404 32 clock_settime64 sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime +409 32 timer_settime64 sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +447 memfd_secret memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/aarch64/syscall_nr.h b/linux-user/aarch64/syscall_nr.h index 12ef002d60..760302cb3e 100644 --- a/linux-user/aarch64/syscall_nr.h +++ b/linux-user/aarch64/syscall_nr.h @@ -1,313 +1 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_AARCH64_SYSCALL_NR_H -#define LINUX_USER_AARCH64_SYSCALL_NR_H - -#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_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs 43 -#define TARGET_NR_fstatfs 44 -#define TARGET_NR_truncate 45 -#define TARGET_NR_ftruncate 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_lseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_newfstatat 79 -#define TARGET_NR_fstat 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap 222 -#define TARGET_NR_fadvise64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_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_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_AARCH64_SYSCALL_NR_H */ +#include "syscall_64_nr.h" diff --git a/linux-user/aarch64/syscallhdr.sh b/linux-user/aarch64/syscallhdr.sh new file mode 100644 index 0000000000..dd6b586b1b --- /dev/null +++ b/linux-user/aarch64/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_AARCH64_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry compat; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/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 index e7ba6070fe..69ec5d5739 100644 --- a/linux-user/aarch64/target_mman.h +++ b/linux-user/aarch64/target_mman.h @@ -1 +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 907c314146..ed75b9e4b5 100644 --- a/linux-user/aarch64/target_prctl.h +++ b/linux-user/aarch64/target_prctl.h @@ -6,6 +6,9 @@ #ifndef AARCH64_TARGET_PRCTL_H #define AARCH64_TARGET_PRCTL_H +#include "target/arm/cpu-features.h" +#include "mte_user_helper.h" + static abi_long do_prctl_sve_get_vl(CPUArchState *env) { ARMCPU *cpu = env_archcpu(env); @@ -171,21 +174,7 @@ 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. - */ - env->cp15.sctlr_el[1] = - deposit64(env->cp15.sctlr_el[1], 38, 2, arg2 >> PR_MTE_TCF_SHIFT); + arm_set_mte_tcf0(env, arg2); /* * 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..d43c3b19cd 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..aaedc9d85e 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/syscall.tbl b/linux-user/alpha/syscall.tbl index 3000a2e8ee..54ee7aaa24 100644 --- a/linux-user/alpha/syscall.tbl +++ b/linux-user/alpha/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for alpha # @@ -125,8 +125,8 @@ 116 common osf_gettimeofday sys_osf_gettimeofday 117 common osf_getrusage sys_osf_getrusage 118 common getsockopt sys_getsockopt -120 common readv sys_osf_readv -121 common writev sys_osf_writev +120 common readv sys_readv +121 common writev sys_writev 122 common osf_settimeofday sys_osf_settimeofday 123 common fchown sys_fchown 124 common fchmod sys_fchmod @@ -230,7 +230,7 @@ 259 common osf_swapctl sys_ni_syscall 260 common osf_memcntl sys_ni_syscall 261 common osf_fdatasync sys_ni_syscall -300 common bdflush sys_bdflush +300 common bdflush sys_ni_syscall 301 common sethae sys_sethae 302 common mount sys_mount 303 common old_adjtimex sys_old_adjtimex @@ -334,7 +334,7 @@ 401 common io_submit sys_io_submit 402 common io_cancel sys_io_cancel 405 common exit_group sys_exit_group -406 common lookup_dcookie sys_lookup_dcookie +406 common lookup_dcookie sys_ni_syscall 407 common epoll_create sys_epoll_create 408 common epoll_ctl sys_epoll_ctl 409 common epoll_wait sys_epoll_wait @@ -474,7 +474,7 @@ 542 common fsmount sys_fsmount 543 common fspick sys_fspick 544 common pidfd_open sys_pidfd_open -# 545 reserved for clone3 +545 common clone3 alpha_clone3 546 common close_range sys_close_range 547 common openat2 sys_openat2 548 common pidfd_getfd sys_pidfd_getfd @@ -482,7 +482,23 @@ 550 common process_madvise sys_process_madvise 551 common epoll_pwait2 sys_epoll_pwait2 552 common mount_setattr sys_mount_setattr -# 553 reserved for quotactl_path +553 common quotactl_fd sys_quotactl_fd 554 common landlock_create_ruleset sys_landlock_create_ruleset 555 common landlock_add_rule sys_landlock_add_rule 556 common landlock_restrict_self sys_landlock_restrict_self +# 557 reserved for memfd_secret +558 common process_mrelease sys_process_mrelease +559 common futex_waitv sys_futex_waitv +560 common set_mempolicy_home_node sys_ni_syscall +561 common cachestat sys_cachestat +562 common fchmodat2 sys_fchmodat2 +563 common map_shadow_stack sys_map_shadow_stack +564 common futex_wake sys_futex_wake +565 common futex_wait sys_futex_wait +566 common futex_requeue sys_futex_requeue +567 common statmount sys_statmount +568 common listmount sys_listmount +569 common lsm_get_self_attr sys_lsm_get_self_attr +570 common lsm_set_self_attr sys_lsm_set_self_attr +571 common lsm_list_modules sys_lsm_list_modules +572 common mseal sys_mseal diff --git a/linux-user/alpha/syscallhdr.sh b/linux-user/alpha/syscallhdr.sh index 55cafe6abf..6da0c957e2 100644 --- a/linux-user/alpha/syscallhdr.sh +++ b/linux-user/alpha/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" 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 index cd6e3d70a6..8edfe2b88c 100644 --- a/linux-user/alpha/target_mman.h +++ b/linux-user/alpha/target_mman.h @@ -1,8 +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..ede489e236 --- /dev/null +++ b/linux-user/arm/Makefile.vdso @@ -0,0 +1,20 @@ +include $(BUILD_DIR)/tests/tcg/arm-linux-user/config-target.mak + +SUBDIR = $(SRC_PATH)/linux-user/arm +VPATH += $(SUBDIR) + +all: $(SUBDIR)/vdso-be8.so $(SUBDIR)/vdso-be32.so $(SUBDIR)/vdso-le.so + +# Adding -use-blx disables unneeded interworking without actually using blx. +LDFLAGS = -nostdlib -shared -Wl,-use-blx -Wl,-z,max-page-size=4096 \ + -Wl,-h,linux-vdso.so.1 -Wl,--build-id=sha1 \ + -Wl,--hash-style=both -Wl,-T,$(SUBDIR)/vdso.ld + +$(SUBDIR)/vdso-be8.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mbig-endian -mbe8 $< + +$(SUBDIR)/vdso-be32.so: vdso.S vdso.ld vdso-asmoffset.h + $(CC) -o $@ $(LDFLAGS) -mbig-endian -mbe32 $< + +$(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 c0790f3246..ec665862d9 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -24,6 +24,7 @@ #include "cpu_loop-common.h" #include "signal-common.h" #include "semihosting/common-semi.h" +#include "exec/page-protection.h" #include "target/arm/syndrome.h" #define get_user_code_u32(x, gaddr, env) \ @@ -117,8 +118,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 +176,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 +264,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 +362,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 +388,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; } } @@ -509,7 +515,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..348ffb810d 100644 --- a/linux-user/arm/meson.build +++ b/linux-user/arm/meson.build @@ -5,3 +5,22 @@ 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_be8_inc = gen_vdso.process('vdso-be8.so', + extra_args: ['-s', 'sigreturn_codes', + '-p', 'vdso_be8']) + +vdso_be32_inc = gen_vdso.process('vdso-be32.so', + extra_args: ['-s', 'sigreturn_codes', + '-p', 'vdso_be32']) + +vdso_le_inc = gen_vdso.process('vdso-le.so', + extra_args: ['-s', 'sigreturn_codes']) + +linux_user_ss.add(when: 'TARGET_ARM', if_true: [ + vdso_be8_inc, vdso_be32_inc, vdso_le_inc +]) diff --git a/linux-user/arm/nwfpe/fpa11.c b/linux-user/arm/nwfpe/fpa11.c index 9a93610d24..8356beb52c 100644 --- a/linux-user/arm/nwfpe/fpa11.c +++ b/linux-user/arm/nwfpe/fpa11.c @@ -51,6 +51,24 @@ void resetFPA11(void) #ifdef MAINTAIN_FPCR fpa11->fpcr = MASK_RESET; #endif + + /* + * Real FPA11 hardware does not handle NaNs, but always takes an + * exception for them to be software-emulated (ARM7500FE datasheet + * section 10.4). There is no documented architectural requirement + * for NaN propagation rules and it will depend on how the OS + * level software emulation opted to do it. We here use prop_s_ab + * which matches the later VFP hardware choice and how QEMU's + * fpa11 emulation has worked in the past. The real Linux kernel + * does something slightly different: arch/arm/nwfpe/softfloat-specialize + * propagateFloat64NaN() has the curious behaviour that it prefers + * the QNaN over the SNaN, but if both are QNaN it picks A and + * if both are SNaN it picks B. In theory we could add this as + * a NaN propagation rule, but in practice FPA11 emulation is so + * close to totally dead that it's not worth trying to match it at + * this late date. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &fpa11->fp_status); } void SetRoundingMode(const unsigned int opcode) 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/syscall.tbl b/linux-user/arm/syscall.tbl index 28e03b5fec..23c98203c4 100644 --- a/linux-user/arm/syscall.tbl +++ b/linux-user/arm/syscall.tbl @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note # # Linux system call numbers and entry vectors # @@ -147,7 +148,7 @@ 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir -134 common bdflush sys_bdflush +134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality # 137 was sys_afs_syscall @@ -263,10 +264,10 @@ 246 common io_submit sys_io_submit 247 common io_cancel sys_io_cancel 248 common exit_group sys_exit_group -249 common lookup_dcookie sys_lookup_dcookie +249 common lookup_dcookie sys_ni_syscall 250 common epoll_create sys_epoll_create 251 common epoll_ctl sys_epoll_ctl sys_oabi_epoll_ctl -252 common epoll_wait sys_epoll_wait sys_oabi_epoll_wait +252 common epoll_wait sys_epoll_wait 253 common remap_file_pages sys_remap_file_pages # 254 for set_thread_area # 255 for get_thread_area @@ -456,7 +457,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/arm/syscallhdr.sh b/linux-user/arm/syscallhdr.sh index 4c952b2cfb..692fd6a76e 100644 --- a/linux-user/arm/syscallhdr.sh +++ b/linux-user/arm/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index 89ba274cfc..f6383a7cd1 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -30,7 +30,7 @@ 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. 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 index e7ba6070fe..51005da869 100644 --- a/linux-user/arm/target_mman.h +++ b/linux-user/arm/target_mman.h @@ -1 +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-be32.so b/linux-user/arm/vdso-be32.so new file mode 100755 index 0000000000..b896d3d545 Binary files /dev/null and b/linux-user/arm/vdso-be32.so differ diff --git a/linux-user/arm/vdso-be8.so b/linux-user/arm/vdso-be8.so new file mode 100755 index 0000000000..784b7bdb2a Binary files /dev/null and b/linux-user/arm/vdso-be8.so differ diff --git a/linux-user/arm/vdso-le.so b/linux-user/arm/vdso-le.so new file mode 100755 index 0000000000..38d3d51047 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/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c deleted file mode 100644 index 01e6ff16fc..0000000000 --- a/linux-user/cris/cpu_loop.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * qemu user cpu loop - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu.h" -#include "user-internals.h" -#include "cpu_loop-common.h" -#include "signal-common.h" - -void cpu_loop(CPUCRISState *env) -{ - CPUState *cs = env_cpu(env); - int trapnr, ret; - - while (1) { - 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 EXCP_BREAK: - ret = do_syscall(env, - env->regs[9], - env->regs[10], - env->regs[11], - env->regs[12], - env->regs[13], - env->pregs[7], - env->pregs[11], - 0, 0); - if (ret == -QEMU_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -QEMU_ESIGRETURN) { - env->regs[10] = ret; - } - break; - case EXCP_DEBUG: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) -{ - CPUState *cpu = env_cpu(env); - TaskState *ts = cpu->opaque; - struct image_info *info = ts->info; - - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = info->start_stack; - env->regs[15] = regs->acr; - env->pc = regs->erp; -} diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c deleted file mode 100644 index 4f532b2903..0000000000 --- a/linux-user/cris/signal.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Emulation of Linux signals - * - * Copyright (c) 2003 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu.h" -#include "user-internals.h" -#include "signal-common.h" -#include "linux-user/trace.h" - -struct target_sigcontext { - struct target_pt_regs regs; /* needs to be first */ - uint32_t oldmask; - uint32_t usp; /* usp before stacking this gunk on it */ -}; - -/* Signal frames. */ -struct target_signal_frame { - struct target_sigcontext sc; - uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint16_t retcode[4]; /* Trampoline code. */ -}; - -struct rt_signal_frame { - siginfo_t *pinfo; - void *puc; - siginfo_t info; - ucontext_t uc; - uint16_t retcode[4]; /* Trampoline code. */ -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) -{ - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->usp); - __put_user(env->regs[15], &sc->regs.acr); - __put_user(env->pregs[PR_MOF], &sc->regs.mof); - __put_user(env->pregs[PR_SRP], &sc->regs.srp); - __put_user(env->pc, &sc->regs.erp); -} - -static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) -{ - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->usp); - __get_user(env->regs[15], &sc->regs.acr); - __get_user(env->pregs[PR_MOF], &sc->regs.mof); - __get_user(env->pregs[PR_SRP], &sc->regs.srp); - __get_user(env->pc, &sc->regs.erp); -} - -static abi_ulong get_sigframe(CPUCRISState *env, int framesize) -{ - abi_ulong sp; - /* Align the stack downwards to 4. */ - sp = (env->regs[R_SP] & ~3); - return sp - framesize; -} - -static void setup_sigreturn(uint16_t *retcode) -{ - /* This is movu.w __NR_sigreturn, r9; break 13; */ - __put_user(0x9c5f, retcode + 0); - __put_user(TARGET_NR_sigreturn, retcode + 1); - __put_user(0xe93d, retcode + 2); -} - -void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUCRISState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(env, sizeof *frame); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto badframe; - - /* - * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't - * use this trampoline anymore but it sets it up for GDB. - */ - setup_sigreturn(frame->retcode); - - /* Save the mask. */ - __put_user(set->sig[0], &frame->sc.oldmask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_sigcontext(&frame->sc, env); - - /* Move the stack and setup the arguments for the handler. */ - env->regs[R_SP] = frame_addr; - env->regs[10] = sig; - env->pc = (unsigned long) ka->_sa_handler; - /* Link SRP so the guest returns through the trampoline. */ - env->pregs[PR_SRP] = default_sigreturn; - - unlock_user_struct(frame, frame_addr, 1); - return; -badframe: - force_sigsegv(sig); -} - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUCRISState *env) -{ - qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUCRISState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - target_sigset_t target_set; - sigset_t set; - int i; - - frame_addr = env->regs[R_SP]; - trace_user_do_sigreturn(env, frame_addr); - /* Make sure the guest isn't playing games. */ - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { - goto badframe; - } - - /* Restore blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(&frame->sc, env); - unlock_user_struct(frame, frame_addr, 0); - return -QEMU_ESIGRETURN; -badframe: - force_sig(TARGET_SIGSEGV); - return -QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUCRISState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -void setup_sigtramp(abi_ulong sigtramp_page) -{ - uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0); - assert(tramp != NULL); - - default_sigreturn = sigtramp_page; - setup_sigreturn(tramp); - - unlock_user(tramp, sigtramp_page, 6); -} diff --git a/linux-user/cris/sockbits.h b/linux-user/cris/sockbits.h deleted file mode 100644 index 0e4c8f012d..0000000000 --- a/linux-user/cris/sockbits.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/sockbits.h" diff --git a/linux-user/cris/syscall_nr.h b/linux-user/cris/syscall_nr.h deleted file mode 100644 index 4b6cf65c42..0000000000 --- a/linux-user/cris/syscall_nr.h +++ /dev/null @@ -1,367 +0,0 @@ -/* - * This file contains the system call numbers, and stub macros for libc. - */ - -#ifndef LINUX_USER_CRIS_SYSCALL_NR_H -#define LINUX_USER_CRIS_SYSCALL_NR_H - -#define TARGET_NR_restart_syscall 0 -#define TARGET_NR_exit 1 -#define TARGET_NR_fork 2 -#define TARGET_NR_read 3 -#define TARGET_NR_write 4 -#define TARGET_NR_open 5 -#define TARGET_NR_close 6 -#define TARGET_NR_waitpid 7 -#define TARGET_NR_creat 8 -#define TARGET_NR_link 9 -#define TARGET_NR_unlink 10 -#define TARGET_NR_execve 11 -#define TARGET_NR_chdir 12 -#define TARGET_NR_time 13 -#define TARGET_NR_mknod 14 -#define TARGET_NR_chmod 15 -#define TARGET_NR_lchown 16 -#define TARGET_NR_break 17 -#define TARGET_NR_oldstat 18 -#define TARGET_NR_lseek 19 -#define TARGET_NR_getpid 20 -#define TARGET_NR_mount 21 -#define TARGET_NR_umount 22 -#define TARGET_NR_setuid 23 -#define TARGET_NR_getuid 24 -#define TARGET_NR_stime 25 -#define TARGET_NR_ptrace 26 -#define TARGET_NR_alarm 27 -#define TARGET_NR_oldfstat 28 -#define TARGET_NR_pause 29 -#define TARGET_NR_utime 30 -#define TARGET_NR_stty 31 -#define TARGET_NR_gtty 32 -#define TARGET_NR_access 33 -#define TARGET_NR_nice 34 -#define TARGET_NR_ftime 35 -#define TARGET_NR_sync 36 -#define TARGET_NR_kill 37 -#define TARGET_NR_rename 38 -#define TARGET_NR_mkdir 39 -#define TARGET_NR_rmdir 40 -#define TARGET_NR_dup 41 -#define TARGET_NR_pipe 42 -#define TARGET_NR_times 43 -#define TARGET_NR_prof 44 -#define TARGET_NR_brk 45 -#define TARGET_NR_setgid 46 -#define TARGET_NR_getgid 47 -#define TARGET_NR_signal 48 -#define TARGET_NR_geteuid 49 -#define TARGET_NR_getegid 50 -#define TARGET_NR_acct 51 -#define TARGET_NR_umount2 52 -#define TARGET_NR_lock 53 -#define TARGET_NR_ioctl 54 -#define TARGET_NR_fcntl 55 -#define TARGET_NR_mpx 56 -#define TARGET_NR_setpgid 57 -#define TARGET_NR_ulimit 58 -#define TARGET_NR_oldolduname 59 -#define TARGET_NR_umask 60 -#define TARGET_NR_chroot 61 -#define TARGET_NR_ustat 62 -#define TARGET_NR_dup2 63 -#define TARGET_NR_getppid 64 -#define TARGET_NR_getpgrp 65 -#define TARGET_NR_setsid 66 -#define TARGET_NR_sigaction 67 -#define TARGET_NR_sgetmask 68 -#define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid 70 -#define TARGET_NR_setregid 71 -#define TARGET_NR_sigsuspend 72 -#define TARGET_NR_sigpending 73 -#define TARGET_NR_sethostname 74 -#define TARGET_NR_setrlimit 75 -#define TARGET_NR_getrlimit 76 -#define TARGET_NR_getrusage 77 -#define TARGET_NR_gettimeofday 78 -#define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups 80 -#define TARGET_NR_setgroups 81 -#define TARGET_NR_select 82 -#define TARGET_NR_symlink 83 -#define TARGET_NR_oldlstat 84 -#define TARGET_NR_readlink 85 -#define TARGET_NR_uselib 86 -#define TARGET_NR_swapon 87 -#define TARGET_NR_reboot 88 -#define TARGET_NR_readdir 89 -#define TARGET_NR_mmap 90 -#define TARGET_NR_munmap 91 -#define TARGET_NR_truncate 92 -#define TARGET_NR_ftruncate 93 -#define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown 95 -#define TARGET_NR_getpriority 96 -#define TARGET_NR_setpriority 97 -#define TARGET_NR_profil 98 -#define TARGET_NR_statfs 99 -#define TARGET_NR_fstatfs 100 -#define TARGET_NR_ioperm 101 -#define TARGET_NR_socketcall 102 -#define TARGET_NR_syslog 103 -#define TARGET_NR_setitimer 104 -#define TARGET_NR_getitimer 105 -#define TARGET_NR_stat 106 -#define TARGET_NR_lstat 107 -#define TARGET_NR_fstat 108 -#define TARGET_NR_olduname 109 -#define TARGET_NR_iopl 110 -#define TARGET_NR_vhangup 111 -#define TARGET_NR_idle 112 -#define TARGET_NR_vm86 113 -#define TARGET_NR_wait4 114 -#define TARGET_NR_swapoff 115 -#define TARGET_NR_sysinfo 116 -#define TARGET_NR_ipc 117 -#define TARGET_NR_fsync 118 -#define TARGET_NR_sigreturn 119 -#define TARGET_NR_clone 120 -#define TARGET_NR_setdomainname 121 -#define TARGET_NR_uname 122 -#define TARGET_NR_modify_ldt 123 -#define TARGET_NR_adjtimex 124 -#define TARGET_NR_mprotect 125 -#define TARGET_NR_sigprocmask 126 -#define TARGET_NR_create_module 127 -#define TARGET_NR_init_module 128 -#define TARGET_NR_delete_module 129 -#define TARGET_NR_get_kernel_syms 130 -#define TARGET_NR_quotactl 131 -#define TARGET_NR_getpgid 132 -#define TARGET_NR_fchdir 133 -#define TARGET_NR_bdflush 134 -#define TARGET_NR_sysfs 135 -#define TARGET_NR_personality 136 -#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid 138 -#define TARGET_NR_setfsgid 139 -#define TARGET_NR__llseek 140 -#define TARGET_NR_getdents 141 -#define TARGET_NR__newselect 142 -#define TARGET_NR_flock 143 -#define TARGET_NR_msync 144 -#define TARGET_NR_readv 145 -#define TARGET_NR_writev 146 -#define TARGET_NR_getsid 147 -#define TARGET_NR_fdatasync 148 -#define TARGET_NR__sysctl 149 -#define TARGET_NR_mlock 150 -#define TARGET_NR_munlock 151 -#define TARGET_NR_mlockall 152 -#define TARGET_NR_munlockall 153 -#define TARGET_NR_sched_setparam 154 -#define TARGET_NR_sched_getparam 155 -#define TARGET_NR_sched_setscheduler 156 -#define TARGET_NR_sched_getscheduler 157 -#define TARGET_NR_sched_yield 158 -#define TARGET_NR_sched_get_priority_max 159 -#define TARGET_NR_sched_get_priority_min 160 -#define TARGET_NR_sched_rr_get_interval 161 -#define TARGET_NR_nanosleep 162 -#define TARGET_NR_mremap 163 -#define TARGET_NR_setresuid 164 -#define TARGET_NR_getresuid 165 - -#define TARGET_NR_query_module 167 -#define TARGET_NR_poll 168 -#define TARGET_NR_nfsservctl 169 -#define TARGET_NR_setresgid 170 -#define TARGET_NR_getresgid 171 -#define TARGET_NR_prctl 172 -#define TARGET_NR_rt_sigreturn 173 -#define TARGET_NR_rt_sigaction 174 -#define TARGET_NR_rt_sigprocmask 175 -#define TARGET_NR_rt_sigpending 176 -#define TARGET_NR_rt_sigtimedwait 177 -#define TARGET_NR_rt_sigqueueinfo 178 -#define TARGET_NR_rt_sigsuspend 179 -#define TARGET_NR_pread64 180 -#define TARGET_NR_pwrite64 181 -#define TARGET_NR_chown 182 -#define TARGET_NR_getcwd 183 -#define TARGET_NR_capget 184 -#define TARGET_NR_capset 185 -#define TARGET_NR_sigaltstack 186 -#define TARGET_NR_sendfile 187 -#define TARGET_NR_getpmsg 188 /* some people actually want streams */ -#define TARGET_NR_putpmsg 189 /* some people actually want streams */ -#define TARGET_NR_vfork 190 -#define TARGET_NR_ugetrlimit 191 /* SuS compliant getrlimit */ -#define TARGET_NR_mmap2 192 -#define TARGET_NR_truncate64 193 -#define TARGET_NR_ftruncate64 194 -#define TARGET_NR_stat64 195 -#define TARGET_NR_lstat64 196 -#define TARGET_NR_fstat64 197 -#define TARGET_NR_lchown32 198 -#define TARGET_NR_getuid32 199 -#define TARGET_NR_getgid32 200 -#define TARGET_NR_geteuid32 201 -#define TARGET_NR_getegid32 202 -#define TARGET_NR_setreuid32 203 -#define TARGET_NR_setregid32 204 -#define TARGET_NR_getgroups32 205 -#define TARGET_NR_setgroups32 206 -#define TARGET_NR_fchown32 207 -#define TARGET_NR_setresuid32 208 -#define TARGET_NR_getresuid32 209 -#define TARGET_NR_setresgid32 210 -#define TARGET_NR_getresgid32 211 -#define TARGET_NR_chown32 212 -#define TARGET_NR_setuid32 213 -#define TARGET_NR_setgid32 214 -#define TARGET_NR_setfsuid32 215 -#define TARGET_NR_setfsgid32 216 -#define TARGET_NR_pivot_root 217 -#define TARGET_NR_mincore 218 -#define TARGET_NR_madvise 219 -#define TARGET_NR_getdents64 220 -#define TARGET_NR_fcntl64 221 -/* 223 is unused */ -#define TARGET_NR_gettid 224 -#define TARGET_NR_readahead 225 -#define TARGET_NR_setxattr 226 -#define TARGET_NR_lsetxattr 227 -#define TARGET_NR_fsetxattr 228 -#define TARGET_NR_getxattr 229 -#define TARGET_NR_lgetxattr 230 -#define TARGET_NR_fgetxattr 231 -#define TARGET_NR_listxattr 232 -#define TARGET_NR_llistxattr 233 -#define TARGET_NR_flistxattr 234 -#define TARGET_NR_removexattr 235 -#define TARGET_NR_lremovexattr 236 -#define TARGET_NR_fremovexattr 237 -#define TARGET_NR_tkill 238 -#define TARGET_NR_sendfile64 239 -#define TARGET_NR_futex 240 -#define TARGET_NR_sched_setaffinity 241 -#define TARGET_NR_sched_getaffinity 242 -#define TARGET_NR_set_thread_area 243 -#define TARGET_NR_get_thread_area 244 -#define TARGET_NR_io_setup 245 -#define TARGET_NR_io_destroy 246 -#define TARGET_NR_io_getevents 247 -#define TARGET_NR_io_submit 248 -#define TARGET_NR_io_cancel 249 -#define TARGET_NR_fadvise64 250 -#define TARGET_NR_exit_group 252 -#define TARGET_NR_lookup_dcookie 253 -#define TARGET_NR_epoll_create 254 -#define TARGET_NR_epoll_ctl 255 -#define TARGET_NR_epoll_wait 256 -#define TARGET_NR_remap_file_pages 257 -#define TARGET_NR_set_tid_address 258 -#define TARGET_NR_timer_create 259 -#define TARGET_NR_timer_settime (TARGET_NR_timer_create+1) -#define TARGET_NR_timer_gettime (TARGET_NR_timer_create+2) -#define TARGET_NR_timer_getoverrun (TARGET_NR_timer_create+3) -#define TARGET_NR_timer_delete (TARGET_NR_timer_create+4) -#define TARGET_NR_clock_settime (TARGET_NR_timer_create+5) -#define TARGET_NR_clock_gettime (TARGET_NR_timer_create+6) -#define TARGET_NR_clock_getres (TARGET_NR_timer_create+7) -#define TARGET_NR_clock_nanosleep (TARGET_NR_timer_create+8) -#define TARGET_NR_statfs64 268 -#define TARGET_NR_fstatfs64 269 -#define TARGET_NR_tgkill 270 -#define TARGET_NR_utimes 271 -#define TARGET_NR_fadvise64_64 272 -#define TARGET_NR_vserver 273 -#define TARGET_NR_mbind 274 -#define TARGET_NR_get_mempolicy 275 -#define TARGET_NR_set_mempolicy 276 -#define TARGET_NR_mq_open 277 -#define TARGET_NR_mq_unlink (TARGET_NR_mq_open+1) -#define TARGET_NR_mq_timedsend (TARGET_NR_mq_open+2) -#define TARGET_NR_mq_timedreceive (TARGET_NR_mq_open+3) -#define TARGET_NR_mq_notify (TARGET_NR_mq_open+4) -#define TARGET_NR_mq_getsetattr (TARGET_NR_mq_open+5) -#define TARGET_NR_kexec_load 283 -#define TARGET_NR_waitid 284 -/* #define TARGET_NR_sys_setaltroot 285 */ -#define TARGET_NR_add_key 286 -#define TARGET_NR_request_key 287 -#define TARGET_NR_keyctl 288 -#define TARGET_NR_ioprio_set 289 -#define TARGET_NR_ioprio_get 290 -#define TARGET_NR_inotify_init 291 -#define TARGET_NR_inotify_add_watch 292 -#define TARGET_NR_inotify_rm_watch 293 -#define TARGET_NR_migrate_pages 294 -#define TARGET_NR_openat 295 -#define TARGET_NR_mkdirat 296 -#define TARGET_NR_mknodat 297 -#define TARGET_NR_fchownat 298 -#define TARGET_NR_futimesat 299 -#define TARGET_NR_fstatat64 300 -#define TARGET_NR_unlinkat 301 -#define TARGET_NR_renameat 302 -#define TARGET_NR_linkat 303 -#define TARGET_NR_symlinkat 304 -#define TARGET_NR_readlinkat 305 -#define TARGET_NR_fchmodat 306 -#define TARGET_NR_faccessat 307 -#define TARGET_NR_pselect6 308 -#define TARGET_NR_ppoll 309 -#define TARGET_NR_unshare 310 -#define TARGET_NR_set_robust_list 311 -#define TARGET_NR_get_robust_list 312 -#define TARGET_NR_splice 313 -#define TARGET_NR_sync_file_range 314 -#define TARGET_NR_tee 315 -#define TARGET_NR_vmsplice 316 -#define TARGET_NR_move_pages 317 -#define TARGET_NR_getcpu 318 -#define TARGET_NR_epoll_pwait 319 -#define TARGET_NR_utimensat 320 -#define TARGET_NR_signalfd 321 -#define TARGET_NR_timerfd_create 322 -#define TARGET_NR_eventfd 323 -#define TARGET_NR_fallocate 324 -#define TARGET_NR_timerfd_settime 325 -#define TARGET_NR_timerfd_gettime 326 -#define TARGET_NR_signalfd4 327 -#define TARGET_NR_eventfd2 328 -#define TARGET_NR_epoll_create1 329 -#define TARGET_NR_dup3 330 -#define TARGET_NR_pipe2 331 -#define TARGET_NR_inotify_init1 332 -#define TARGET_NR_preadv 333 -#define TARGET_NR_pwritev 334 -#define TARGET_NR_setns 335 -#define TARGET_NR_name_to_handle_at 336 -#define TARGET_NR_open_by_handle_at 337 -#define TARGET_NR_rt_tgsigqueueinfo 338 -#define TARGET_NR_perf_event_open 339 -#define TARGET_NR_recvmmsg 340 -#define TARGET_NR_accept4 341 -#define TARGET_NR_fanotify_init 342 -#define TARGET_NR_fanotify_mark 343 -#define TARGET_NR_prlimit64 344 -#define TARGET_NR_clock_adjtime 345 -#define TARGET_NR_syncfs 346 -#define TARGET_NR_sendmmsg 347 -#define TARGET_NR_process_vm_readv 348 -#define TARGET_NR_process_vm_writev 349 -#define TARGET_NR_kcmp 350 -#define TARGET_NR_finit_module 351 -#define TARGET_NR_sched_setattr 352 -#define TARGET_NR_sched_getattr 353 -#define TARGET_NR_renameat2 354 -#define TARGET_NR_seccomp 355 -#define TARGET_NR_getrandom 356 -#define TARGET_NR_memfd_create 357 -#define TARGET_NR_bpf 358 -#define TARGET_NR_execveat 359 - -#endif diff --git a/linux-user/cris/target_cpu.h b/linux-user/cris/target_cpu.h deleted file mode 100644 index 7f6cade7b6..0000000000 --- a/linux-user/cris/target_cpu.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * CRIS specific CPU ABI and functions for linux-user - * - * Copyright (c) 2007 AXIS Communications AB - * Written by Edgar E. Iglesias - * - * 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 - * 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 CRIS_TARGET_CPU_H -#define CRIS_TARGET_CPU_H - -static inline void cpu_clone_regs_child(CPUCRISState *env, target_ulong newsp, - unsigned flags) -{ - if (newsp) { - env->regs[14] = newsp; - } - env->regs[10] = 0; -} - -static inline void cpu_clone_regs_parent(CPUCRISState *env, unsigned flags) -{ -} - -static inline void cpu_set_tls(CPUCRISState *env, target_ulong newtls) -{ - env->pregs[PR_PID] = (env->pregs[PR_PID] & 0xff) | newtls; -} - -static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) -{ - return state->regs[14]; -} -#endif diff --git a/linux-user/cris/target_elf.h b/linux-user/cris/target_elf.h deleted file mode 100644 index 99eb4ec704..0000000000 --- a/linux-user/cris/target_elf.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef CRIS_TARGET_ELF_H -#define CRIS_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} -#endif diff --git a/linux-user/cris/target_errno_defs.h b/linux-user/cris/target_errno_defs.h deleted file mode 100644 index 1cf43b17a5..0000000000 --- a/linux-user/cris/target_errno_defs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CRIS_TARGET_ERRNO_DEFS_H -#define CRIS_TARGET_ERRNO_DEFS_H - -/* Target uses generic errno */ -#include "../generic/target_errno_defs.h" - -#endif diff --git a/linux-user/cris/target_fcntl.h b/linux-user/cris/target_fcntl.h deleted file mode 100644 index df0aceea34..0000000000 --- a/linux-user/cris/target_fcntl.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * 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, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef CRIS_TARGET_FCNTL_H -#define CRIS_TARGET_FCNTL_H -#include "../generic/fcntl.h" -#endif diff --git a/linux-user/cris/target_mman.h b/linux-user/cris/target_mman.h deleted file mode 100644 index e7ba6070fe..0000000000 --- a/linux-user/cris/target_mman.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_mman.h" diff --git a/linux-user/cris/target_prctl.h b/linux-user/cris/target_prctl.h deleted file mode 100644 index eb53b31ad5..0000000000 --- a/linux-user/cris/target_prctl.h +++ /dev/null @@ -1 +0,0 @@ -/* No special prctl support required. */ diff --git a/linux-user/cris/target_resource.h b/linux-user/cris/target_resource.h deleted file mode 100644 index 227259594c..0000000000 --- a/linux-user/cris/target_resource.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_resource.h" diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h deleted file mode 100644 index ab0653fcdc..0000000000 --- a/linux-user/cris/target_signal.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CRIS_TARGET_SIGNAL_H -#define CRIS_TARGET_SIGNAL_H - -#include "../generic/signal.h" - -#define TARGET_ARCH_HAS_SETUP_FRAME -#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 1 - -#endif /* CRIS_TARGET_SIGNAL_H */ diff --git a/linux-user/cris/target_structs.h b/linux-user/cris/target_structs.h deleted file mode 100644 index 3a06f373c3..0000000000 --- a/linux-user/cris/target_structs.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_structs.h" diff --git a/linux-user/cris/target_syscall.h b/linux-user/cris/target_syscall.h deleted file mode 100644 index 0b5ebf1f02..0000000000 --- a/linux-user/cris/target_syscall.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef CRIS_TARGET_SYSCALL_H -#define CRIS_TARGET_SYSCALL_H - -#define UNAME_MACHINE "cris" -#define UNAME_MINIMUM_RELEASE "2.6.32" - -/* pt_regs not only specifies the format in the user-struct during - * ptrace but is also the frame format used in the kernel prologue/epilogues - * themselves - */ - -struct target_pt_regs { - unsigned long orig_r10; - /* pushed by movem r13, [sp] in SAVE_ALL. */ - unsigned long r0; - unsigned long r1; - unsigned long r2; - unsigned long r3; - unsigned long r4; - unsigned long r5; - unsigned long r6; - unsigned long r7; - unsigned long r8; - unsigned long r9; - unsigned long r10; - unsigned long r11; - unsigned long r12; - unsigned long r13; - unsigned long acr; - unsigned long srs; - unsigned long mof; - unsigned long spc; - unsigned long ccs; - unsigned long srp; - unsigned long erp; /* This is actually the debugged process's PC */ - /* For debugging purposes; saved only when needed. */ - unsigned long exs; - unsigned long eda; -}; - -#define TARGET_CLONE_BACKWARDS2 -#define TARGET_MCL_CURRENT 1 -#define TARGET_MCL_FUTURE 2 -#define TARGET_MCL_ONFAULT 4 - -#endif diff --git a/linux-user/cris/termbits.h b/linux-user/cris/termbits.h deleted file mode 100644 index 0c8d8fc051..0000000000 --- a/linux-user/cris/termbits.h +++ /dev/null @@ -1,225 +0,0 @@ -/* from asm/termbits.h */ - -#ifndef LINUX_USER_CRIS_TERMBITS_H -#define LINUX_USER_CRIS_TERMBITS_H - -#define TARGET_NCCS 19 - -typedef unsigned char target_cc_t; /* cc_t */ -typedef unsigned int target_speed_t; /* speed_t */ -typedef unsigned int target_tcflag_t; /* tcflag_t */ - -struct target_termios { - target_tcflag_t c_iflag; /* input mode flags */ - target_tcflag_t c_oflag; /* output mode flags */ - target_tcflag_t c_cflag; /* control mode flags */ - target_tcflag_t c_lflag; /* local mode flags */ - target_cc_t c_line; /* line discipline */ - target_cc_t c_cc[TARGET_NCCS]; /* control characters */ -}; - -/* 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 - -/* 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 - -/* 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_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_CBAUDEX 0010000 -#define TARGET_B57600 0010001 -#define TARGET_B115200 0010002 -#define TARGET_B230400 0010003 -#define TARGET_B460800 0010004 -#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ -#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_EXTPROC 0200000 - -/* c_cc character offsets */ -#define TARGET_VINTR 0 -#define TARGET_VQUIT 1 -#define TARGET_VERASE 2 -#define TARGET_VKILL 3 -#define TARGET_VEOF 4 -#define TARGET_VTIME 5 -#define TARGET_VMIN 6 -#define TARGET_VSWTC 7 -#define TARGET_VSTART 8 -#define TARGET_VSTOP 9 -#define TARGET_VSUSP 10 -#define TARGET_VEOL 11 -#define TARGET_VREPRINT 12 -#define TARGET_VDISCARD 13 -#define TARGET_VWERASE 14 -#define TARGET_VLNEXT 15 -#define TARGET_VEOL2 16 - -/* ioctls */ - -#define TARGET_TCGETS 0x5401 -#define TARGET_TCSETS 0x5402 -#define TARGET_TCSETSW 0x5403 -#define TARGET_TCSETSF 0x5404 -#define TARGET_TCGETA 0x5405 -#define TARGET_TCSETA 0x5406 -#define TARGET_TCSETAW 0x5407 -#define TARGET_TCSETAF 0x5408 -#define TARGET_TCSBRK 0x5409 -#define TARGET_TCXONC 0x540A -#define TARGET_TCFLSH 0x540B - -#define TARGET_TIOCEXCL 0x540C -#define TARGET_TIOCNXCL 0x540D -#define TARGET_TIOCSCTTY 0x540E -#define TARGET_TIOCGPGRP 0x540F -#define TARGET_TIOCSPGRP 0x5410 -#define TARGET_TIOCOUTQ 0x5411 -#define TARGET_TIOCSTI 0x5412 -#define TARGET_TIOCGWINSZ 0x5413 -#define TARGET_TIOCSWINSZ 0x5414 -#define TARGET_TIOCMGET 0x5415 -#define TARGET_TIOCMBIS 0x5416 -#define TARGET_TIOCMBIC 0x5417 -#define TARGET_TIOCMSET 0x5418 -#define TARGET_TIOCGSOFTCAR 0x5419 -#define TARGET_TIOCSSOFTCAR 0x541A -#define TARGET_FIONREAD 0x541B -#define TARGET_TIOCINQ TARGET_FIONREAD -#define TARGET_TIOCLINUX 0x541C -#define TARGET_TIOCCONS 0x541D -#define TARGET_TIOCGSERIAL 0x541E -#define TARGET_TIOCSSERIAL 0x541F -#define TARGET_TIOCPKT 0x5420 -#define TARGET_FIONBIO 0x5421 -#define TARGET_TIOCNOTTY 0x5422 -#define TARGET_TIOCSETD 0x5423 -#define TARGET_TIOCGETD 0x5424 -#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ -#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */ -#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ -#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ -#define TARGET_TIOCGSID 0x5429 /* Return the session ID of FD */ -#define TARGET_TIOCGPTN TARGET_IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ -#define TARGET_TIOCSPTLCK TARGET_IOW('T',0x31, int) /* Lock/unlock Pty */ -#define TARGET_TIOCGPTPEER TARGET_IO('T', 0x41) /* Safely open the slave */ - -#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */ -#define TARGET_FIOCLEX 0x5451 -#define TARGET_FIOASYNC 0x5452 -#define TARGET_TIOCSERCONFIG 0x5453 -#define TARGET_TIOCSERGWILD 0x5454 -#define TARGET_TIOCSERSWILD 0x5455 -#define TARGET_TIOCGLCKTRMIOS 0x5456 -#define TARGET_TIOCSLCKTRMIOS 0x5457 -#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ -#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ -#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ -#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ - -#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ -#define TARGET_TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ -#define TARGET_TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ -#define TARGET_TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ - -/* Used for packet mode */ -#define TARGET_TIOCPKT_DATA 0 -#define TARGET_TIOCPKT_FLUSHREAD 1 -#define TARGET_TIOCPKT_FLUSHWRITE 2 -#define TARGET_TIOCPKT_STOP 4 -#define TARGET_TIOCPKT_START 8 -#define TARGET_TIOCPKT_NOSTOP 16 -#define TARGET_TIOCPKT_DOSTOP 32 - -#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ - -#endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 20894b633f..471a384b22 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2,10 +2,14 @@ #include "qemu/osdep.h" #include +#include #include #include #include "qemu.h" +#include "user/tswap-target.h" +#include "exec/page-protection.h" +#include "user/guest-base.h" #include "user-internals.h" #include "signal-common.h" #include "loader.h" @@ -17,8 +21,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 +41,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 */ @@ -140,8 +164,6 @@ 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 @@ -181,7 +203,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en (*regs)[12] = tswapreg(env->regs[R_EDX]); (*regs)[13] = tswapreg(env->regs[R_ESI]); (*regs)[14] = tswapreg(env->regs[R_EDI]); - (*regs)[15] = tswapreg(env->regs[R_EAX]); /* XXX */ + (*regs)[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); (*regs)[16] = tswapreg(env->eip); (*regs)[17] = tswapreg(env->segs[R_CS].selector & 0xffff); (*regs)[18] = tswapreg(env->eflags); @@ -206,20 +228,18 @@ static bool init_guest_commpage(void) * has specified -R reserved_va, which would trigger an assert(). */ if (reserved_va != 0 && - TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE >= reserved_va) { + 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_SIZE, + 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. */ @@ -286,27 +306,40 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en (*regs)[8] = tswapreg(env->segs[R_ES].selector & 0xffff); (*regs)[9] = tswapreg(env->segs[R_FS].selector & 0xffff); (*regs)[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[11] = tswapreg(env->regs[R_EAX]); /* XXX */ + (*regs)[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); (*regs)[12] = tswapreg(env->eip); (*regs)[13] = tswapreg(env->segs[R_CS].selector & 0xffff); (*regs)[14] = tswapreg(env->eflags); (*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 @@ -405,6 +438,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 { @@ -413,6 +452,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 */ @@ -421,10 +462,26 @@ enum { static bool init_guest_commpage(void) { - abi_ptr commpage = HI_COMMPAGE & -qemu_host_page_size; - void *want = g2h_untagged(commpage); - 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"); @@ -437,12 +494,12 @@ 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 + qemu_host_page_size, + page_set_flags(commpage, commpage | (host_page_size - 1), PAGE_READ | PAGE_EXEC | PAGE_VALID); return true; } @@ -450,7 +507,7 @@ static bool init_guest_commpage(void) #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; @@ -488,23 +545,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 @@ -512,7 +632,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" @@ -539,9 +659,25 @@ static const char *get_elf_platform(void) #undef END } +#if TARGET_BIG_ENDIAN +#include "elf.h" +#include "vdso-be8.c.inc" +#include "vdso-be32.c.inc" + +static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) +{ + return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 + && (elf_flags & EF_ARM_BE8) + ? &vdso_be8_image_info + : &vdso_be32_image_info); +} +#define vdso_image_info vdso_image_info +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #else /* 64 bit ARM definitions */ -#define ELF_START_MMAP 0x80000000 #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 @@ -644,6 +780,20 @@ enum { 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() @@ -652,7 +802,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; @@ -674,12 +824,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); @@ -690,10 +842,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); @@ -713,6 +865,7 @@ 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 | @@ -721,36 +874,159 @@ static uint32_t get_elf_hwcap2(void) 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 +#if TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-be.c.inc" +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #endif /* not TARGET_AARCH64 */ + #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 -#define elf_check_arch(x) ( (x) == EM_SPARCV9 || (x) == EM_SPARC32PLUS ) +#ifndef TARGET_SPARC64 +# define ELF_CLASS ELFCLASS32 +# define ELF_ARCH EM_SPARC +#elif defined(TARGET_ABI32) +# define ELF_CLASS ELFCLASS32 +# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else -#define elf_check_arch(x) ( (x) == EM_SPARC32PLUS || (x) == EM_SPARC ) +# define ELF_CLASS ELFCLASS64 +# define ELF_ARCH EM_SPARCV9 #endif -#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 -#define ELF_ARCH EM_SPARC -#endif /* TARGET_SPARC64 */ +#include "elf.h" + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + /* There are not many sparc32 hwcap bits -- we have all of them. */ + uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | + HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; + +#ifdef TARGET_SPARC64 + CPUSPARCState *env = cpu_env(thread_cpu); + uint32_t features = env->def.features; + + r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; + /* 32x32 multiply and divide are efficient. */ + r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; + /* We don't have an internal feature bit for this. */ + r |= HWCAP_SPARC_POPC; + r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; + r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; + r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; + r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; + r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; + r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; +#endif + + return r; +} static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) @@ -767,7 +1043,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) @@ -959,27 +1234,33 @@ 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 + #endif #ifdef TARGET_LOONGARCH64 -#define ELF_START_MMAP 0x80000000 - #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) { @@ -1055,6 +1336,14 @@ static uint32_t get_elf_hwcap(void) 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; } @@ -1064,8 +1353,6 @@ static uint32_t get_elf_hwcap(void) #ifdef TARGET_MIPS -#define ELF_START_MMAP 0x80000000 - #ifdef TARGET_MIPS64 #define ELF_CLASS ELFCLASS64 #else @@ -1223,8 +1510,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 @@ -1263,107 +1548,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #endif /* TARGET_MICROBLAZE */ -#ifdef TARGET_NIOS2 - -#define ELF_START_MMAP 0x80000000 - -#define elf_check_arch(x) ((x) == EM_ALTERA_NIOS2) - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_ALTERA_NIOS2 - -static void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->ea = infop->entry; - regs->sp = infop->start_stack; -} - -#define LO_COMMPAGE TARGET_PAGE_SIZE - -static bool init_guest_commpage(void) -{ - static const uint8_t kuser_page[4 + 2 * 64] = { - /* __kuser_helper_version */ - [0x00] = 0x02, 0x00, 0x00, 0x00, - - /* __kuser_cmpxchg */ - [0x04] = 0x3a, 0x6c, 0x3b, 0x00, /* trap 16 */ - 0x3a, 0x28, 0x00, 0xf8, /* ret */ - - /* __kuser_sigtramp */ - [0x44] = 0xc4, 0x22, 0x80, 0x00, /* movi r2, __NR_rt_sigreturn */ - 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); - - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - - memcpy(addr, kuser_page, sizeof(kuser_page)); - - if (mprotect(addr, qemu_host_page_size, PROT_READ)) { - perror("Protecting guest commpage"); - exit(EXIT_FAILURE); - } - - page_set_flags(LO_COMMPAGE, LO_COMMPAGE + TARGET_PAGE_SIZE, - PAGE_READ | PAGE_EXEC | PAGE_VALID); - return true; -} - -#define ELF_EXEC_PAGESIZE 4096 - -#define USE_ELF_CORE_DUMP -#define ELF_NREG 49 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUNios2State *env) -{ - int i; - - (*regs)[0] = -1; - for (i = 1; i < 8; i++) /* r0-r7 */ - (*regs)[i] = tswapreg(env->regs[i + 7]); - - for (i = 8; i < 16; i++) /* r8-r15 */ - (*regs)[i] = tswapreg(env->regs[i - 8]); - - for (i = 16; i < 24; i++) /* r16-r23 */ - (*regs)[i] = tswapreg(env->regs[i + 7]); - (*regs)[24] = -1; /* R_ET */ - (*regs)[25] = -1; /* R_BT */ - (*regs)[26] = tswapreg(env->regs[R_GP]); - (*regs)[27] = tswapreg(env->regs[R_SP]); - (*regs)[28] = tswapreg(env->regs[R_FP]); - (*regs)[29] = tswapreg(env->regs[R_EA]); - (*regs)[30] = -1; /* R_SSTATUS */ - (*regs)[31] = tswapreg(env->regs[R_RA]); - - (*regs)[32] = tswapreg(env->pc); - - (*regs)[33] = -1; /* R_STATUS */ - (*regs)[34] = tswapreg(env->regs[CR_ESTATUS]); - - for (i = 35; i < 49; i++) /* ... */ - (*regs)[i] = -1; -} - -#endif /* TARGET_NIOS2 */ - #ifdef TARGET_OPENRISC -#define ELF_START_MMAP 0x08000000 - #define ELF_ARCH EM_OPENRISC #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB @@ -1400,8 +1586,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 @@ -1480,27 +1664,8 @@ static uint32_t get_elf_hwcap(void) #endif -#ifdef TARGET_CRIS - -#define ELF_START_MMAP 0x80000000 - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_CRIS - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->erp = infop->entry; -} - -#define ELF_EXEC_PAGESIZE 8192 - -#endif - #ifdef TARGET_M68K -#define ELF_START_MMAP 0x80000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K @@ -1550,8 +1715,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 @@ -1569,8 +1732,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 @@ -1582,7 +1743,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. @@ -1600,14 +1761,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; } @@ -1644,17 +1838,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() @@ -1664,7 +1861,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 @@ -1683,18 +1881,19 @@ 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) { - regs->iaoq[0] = infop->entry; - regs->iaoq[1] = infop->entry + 4; + regs->iaoq[0] = infop->entry | PRIV_USER; + regs->iaoq[1] = regs->iaoq[0] + 4; regs->gr[23] = 0; regs->gr[24] = infop->argv; regs->gr[25] = infop->argc; @@ -1707,16 +1906,20 @@ static inline void init_thread(struct target_pt_regs *regs, static bool init_guest_commpage(void) { - void *want = g2h_untagged(LO_COMMPAGE); - void *addr = mmap(want, qemu_host_page_size, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + /* If reserved_va, then we have already mapped 0 page on the host. */ + if (!reserved_va) { + void *want, *addr; - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; + 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; + } } /* @@ -1726,7 +1929,7 @@ static bool init_guest_commpage(void) * 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_SIZE, + page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, PAGE_EXEC | PAGE_VALID); return true; } @@ -1735,8 +1938,6 @@ static bool init_guest_commpage(void) #ifdef TARGET_XTENSA -#define ELF_START_MMAP 0x20000000 - #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA @@ -1747,6 +1948,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. */ @@ -1793,8 +2003,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 @@ -1904,15 +2112,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) @@ -2001,7 +2200,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. */ @@ -2164,53 +2364,76 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm, } } -/* 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_RWX)) { + /* + * 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) @@ -2247,7 +2470,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; @@ -2335,10 +2559,15 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } size = (DLINFO_ITEMS + 1) * 2; - if (k_base_platform) + if (k_base_platform) { size += 2; - if (k_platform) + } + if (k_platform) { size += 2; + } + if (vdso_info) { + size += 2; + } #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif @@ -2390,13 +2619,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); @@ -2420,6 +2643,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, 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 @@ -2461,6 +2687,157 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #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) { error_report("%s: requires virtual address space that is in use " @@ -2469,19 +2846,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 = sizeof(uintptr_t) == 4 ? MiB : GiB; + 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) { @@ -2491,304 +3018,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 != -1) { - 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)); @@ -2856,10 +3103,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 { @@ -2887,22 +3133,16 @@ 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; } /* - * The contents of a valid PT_GNU_PROPERTY is a sequence - * of uint32_t -- swap them all now. + * The contents of a valid PT_GNU_PROPERTY is a sequence of uint32_t. + * Swap most of them now, beyond the header and namesz. */ #ifdef BSWAP_NEEDED - for (int i = 0; i < n / 4; i++) { + for (int i = 4; i < n / 4; i++) { bswap32s(note.data + i); } #endif @@ -2912,15 +3152,15 @@ static bool parse_elf_properties(int image_fd, * immediately follows nhdr and is thus at the 4th word. Further, all * of the inputs to the kernel's round_up are multiples of 4. */ - if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 || - note.nhdr.n_namesz != NOTE_NAME_SZ || + if (tswap32(note.nhdr.n_type) != NT_GNU_PROPERTY_TYPE_0 || + tswap32(note.nhdr.n_namesz) != NOTE_NAME_SZ || note.data[3] != GNU0_MAGIC) { error_setg(errp, "Invalid note in PT_GNU_PROPERTY"); return false; } off = sizeof(note.nhdr) + NOTE_NAME_SZ; - datasz = note.nhdr.n_descsz + off; + datasz = tswap32(note.nhdr.n_descsz) + off; if (datasz > n) { error_setg(errp, "Invalid note size in PT_GNU_PROPERTY"); return false; @@ -2940,29 +3180,35 @@ 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; - abi_ulong load_addr, load_bias, loaddr, hiaddr, error; - int i, retval, prot_exec; + g_autofree struct elf_phdr *phdr = NULL; + abi_ulong load_addr, load_bias, loaddr, hiaddr, error, align; + size_t reserve_size, align_size; + 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; @@ -2973,15 +3219,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); @@ -2995,21 +3237,21 @@ static void load_elf_image(const char *image_name, int image_fd, * amount of memory to handle that. Locate the interpreter, if any. */ loaddr = -1, hiaddr = 0; - info->alignment = 0; + align = 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; } ++info->nsegs; - info->alignment |= eppnt->p_align; + align |= eppnt->p_align; } else if (eppnt->p_type == PT_INTERP && pinterp_name) { g_autofree char *interp_name = NULL; @@ -3018,17 +3260,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"); @@ -3036,7 +3271,7 @@ 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) { @@ -3044,28 +3279,11 @@ static void load_elf_image(const char *image_name, int image_fd, } } - 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; + align = pow2ceil(align); + + if (pinterp_name != NULL) { if (ehdr->e_type == ET_EXEC) { /* * Make sure that the low address does not conflict with @@ -3078,30 +3296,71 @@ static void load_elf_image(const char *image_name, int image_fd, * 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. + */ + 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, + reserve_size = (size_t)hiaddr - loaddr + 1; + align_size = reserve_size; + + if (ehdr->e_type != ET_EXEC && align > qemu_real_host_page_size()) { + align_size += align - 1; + } + + load_addr = target_mmap(load_addr, align_size, 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; } + + if (align_size != reserve_size) { + abi_ulong align_addr = ROUND_UP(load_addr, align); + abi_ulong align_end = align_addr + reserve_size; + abi_ulong load_end = load_addr + align_size; + + if (align_addr != load_addr) { + target_munmap(load_addr, align_addr - load_addr); + } + if (align_end != load_end) { + target_munmap(align_end, load_end - align_end); + } + load_addr = align_addr; + } + load_bias = load_addr - loaddr; if (elf_is_fdpic(ehdr)) { @@ -3132,7 +3391,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; @@ -3158,7 +3418,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) { @@ -3172,8 +3432,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; @@ -3183,30 +3443,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. */ @@ -3226,26 +3474,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 @@ -3258,21 +3494,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; @@ -3284,6 +3515,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; @@ -3301,18 +3534,67 @@ 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); +} + +#ifndef vdso_image_info +#ifdef VDSO_HEADER +#include VDSO_HEADER +#define vdso_image_info(flags) &vdso_image_info +#else +#define vdso_image_info(flags) NULL +#endif /* VDSO_HEADER */ +#endif /* vdso_image_info */ + +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) { @@ -3321,7 +3603,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; @@ -3340,7 +3622,7 @@ static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr) return ""; } -/* FIXME: This should use elf_ops.h */ +/* FIXME: This should use elf_ops.h.inc */ static int symcmp(const void *s0, const void *s1) { struct elf_sym *sym0 = (struct elf_sym *)s0; @@ -3351,19 +3633,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; } @@ -3381,31 +3664,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. */ @@ -3430,10 +3715,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; @@ -3442,20 +3729,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); } @@ -3497,8 +3787,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; @@ -3507,15 +3803,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 */ @@ -3554,6 +3842,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. */ @@ -3565,8 +3866,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; @@ -3574,10 +3876,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(info->elf_flags); + 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); @@ -3589,8 +3895,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. @@ -3607,21 +3914,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_" @@ -3661,18 +3959,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 */ @@ -3712,77 +3998,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) { @@ -3824,146 +4039,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; @@ -3981,95 +4117,78 @@ 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, 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 = get_task_state(cpu)->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); } /* @@ -4093,27 +4212,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 @@ -4135,135 +4236,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); } /* @@ -4311,148 +4353,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 CPUState *cpu = env_cpu_const(env); + 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, 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 146aaaafaa..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); diff --git a/linux-user/flat.h b/linux-user/flat.h index ed518e2013..e374b73e26 100644 --- a/linux-user/flat.h +++ b/linux-user/flat.h @@ -12,11 +12,8 @@ #define FLAT_VERSION 0x00000004L -#ifdef CONFIG_BINFMT_SHARED_FLAT -#define MAX_SHARED_LIBS (4) -#else +/* QEMU doesn't support bflt shared libraries */ #define MAX_SHARED_LIBS (1) -#endif /* * To make everything easier to port and manage cross platform diff --git a/linux-user/flatload.c b/linux-user/flatload.c index e99570ca18..0e4be5bf44 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -29,8 +29,6 @@ * JAN/99 -- coded full program relocation (gerg@snapgear.com) */ -/* ??? ZFLAT and shared library support is currently disabled. */ - /****************************************************************************/ #include "qemu/osdep.h" @@ -64,10 +62,6 @@ struct lib_info { short loaded; /* Has this library been loaded? */ }; -#ifdef CONFIG_BINFMT_SHARED_FLAT -static int load_flat_shared_library(int id, struct lib_info *p); -#endif - struct linux_binprm; /****************************************************************************/ @@ -108,153 +102,6 @@ static int target_pread(int fd, abi_ulong ptr, abi_ulong len, unlock_user(buf, ptr, len); return ret; } -/****************************************************************************/ - -#ifdef CONFIG_BINFMT_ZFLAT - -#include - -#define LBUFSIZE 4000 - -/* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ - -static int decompress_exec( - struct linux_binprm *bprm, - unsigned long offset, - char *dst, - long len, - int fd) -{ - unsigned char *buf; - z_stream strm; - loff_t fpos; - int ret, retval; - - DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n",(int)offset, (int)dst, (int)len); - - memset(&strm, 0, sizeof(strm)); - strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL); - if (strm.workspace == NULL) { - DBG_FLT("binfmt_flat: no memory for decompress workspace\n"); - return -ENOMEM; - } - buf = kmalloc(LBUFSIZE, GFP_KERNEL); - if (buf == NULL) { - DBG_FLT("binfmt_flat: no memory for read buffer\n"); - retval = -ENOMEM; - goto out_free; - } - - /* Read in first chunk of data and parse gzip header. */ - fpos = offset; - ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); - - strm.next_in = buf; - strm.avail_in = ret; - strm.total_in = 0; - - retval = -ENOEXEC; - - /* Check minimum size -- gzip header */ - if (ret < 10) { - DBG_FLT("binfmt_flat: file too small?\n"); - goto out_free_buf; - } - - /* Check gzip magic number */ - if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) { - DBG_FLT("binfmt_flat: unknown compression magic?\n"); - goto out_free_buf; - } - - /* Check gzip method */ - if (buf[2] != 8) { - DBG_FLT("binfmt_flat: unknown compression method?\n"); - goto out_free_buf; - } - /* Check gzip flags */ - if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) || - (buf[3] & RESERVED)) { - DBG_FLT("binfmt_flat: unknown flags?\n"); - goto out_free_buf; - } - - ret = 10; - if (buf[3] & EXTRA_FIELD) { - ret += 2 + buf[10] + (buf[11] << 8); - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n"); - goto out_free_buf; - } - } - if (buf[3] & ORIG_NAME) { - for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) - ; - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n"); - goto out_free_buf; - } - } - if (buf[3] & COMMENT) { - for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) - ; - if (unlikely(LBUFSIZE == ret)) { - DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n"); - goto out_free_buf; - } - } - - strm.next_in += ret; - strm.avail_in -= ret; - - strm.next_out = dst; - strm.avail_out = len; - strm.total_out = 0; - - if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) { - DBG_FLT("binfmt_flat: zlib init failed?\n"); - goto out_free_buf; - } - - while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) { - ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); - if (ret <= 0) - break; - if (is_error(ret)) { - break; - } - len -= ret; - - strm.next_in = buf; - strm.avail_in = ret; - strm.total_in = 0; - } - - if (ret < 0) { - DBG_FLT("binfmt_flat: decompression failed (%d), %s\n", - ret, strm.msg); - goto out_zlib; - } - - retval = 0; -out_zlib: - zlib_inflateEnd(&strm); -out_free_buf: - kfree(buf); -out_free: - kfree(strm.workspace); -out: - return retval; -} - -#endif /* CONFIG_BINFMT_ZFLAT */ /****************************************************************************/ @@ -268,40 +115,7 @@ calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp) abi_ulong text_len; abi_ulong start_code; -#ifdef CONFIG_BINFMT_SHARED_FLAT -#error needs checking - if (r == 0) - id = curid; /* Relocs of 0 are always self referring */ - else { - id = (r >> 24) & 0xff; /* Find ID for this reloc */ - r &= 0x00ffffff; /* Trim ID off here */ - } - if (id >= MAX_SHARED_LIBS) { - fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n", - (unsigned) r, id); - goto failed; - } - if (curid != id) { - if (internalp) { - fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not " - "in same module (%d != %d)\n", - (unsigned) r, curid, id); - goto failed; - } else if (!p[id].loaded && is_error(load_flat_shared_library(id, p))) { - fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id); - goto failed; - } - /* Check versioning information (i.e. time stamps) */ - if (p[id].build_date && p[curid].build_date - && p[curid].build_date < p[id].build_date) { - fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n", - id, curid); - goto failed; - } - } -#else id = 0; -#endif start_brk = p[id].start_brk; start_data = p[id].start_data; @@ -425,12 +239,10 @@ static int load_flat_file(struct linux_binprm * bprm, if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags)) flags = FLAT_FLAG_RAM; -#ifndef CONFIG_BINFMT_ZFLAT if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) { - fprintf(stderr, "Support for ZFLAT executables is not enabled\n"); + fprintf(stderr, "ZFLAT executables are not supported\n"); return -ENOEXEC; } -#endif /* * calculate the extra space we need to map in @@ -448,7 +260,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 +275,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; @@ -483,17 +295,9 @@ static int load_flat_file(struct linux_binprm * bprm, (int)(data_len + bss_len + stack_len), (int)datapos); fpos = ntohl(hdr->data_start); -#ifdef CONFIG_BINFMT_ZFLAT - if (flags & FLAT_FLAG_GZDATA) { - result = decompress_exec(bprm, fpos, (char *) datapos, - data_len + (relocs * sizeof(abi_ulong))) - } else -#endif - { - result = target_pread(bprm->fd, datapos, - data_len + (relocs * sizeof(abi_ulong)), - fpos); - } + result = target_pread(bprm->src.fd, datapos, + data_len + (relocs * sizeof(abi_ulong)), + fpos); if (result < 0) { fprintf(stderr, "Unable to read data+bss\n"); return result; @@ -515,38 +319,12 @@ static int load_flat_file(struct linux_binprm * bprm, datapos = realdatastart + indx_len; reloc = (textpos + ntohl(hdr->reloc_start) + indx_len); -#ifdef CONFIG_BINFMT_ZFLAT -#error code needs checking - /* - * load it all in and treat it like a RAM load from now on - */ - if (flags & FLAT_FLAG_GZIP) { - result = decompress_exec(bprm, sizeof (struct flat_hdr), - (((char *) textpos) + sizeof (struct flat_hdr)), - (text_len + data_len + (relocs * sizeof(unsigned long)) - - sizeof (struct flat_hdr)), - 0); - memmove((void *) datapos, (void *) realdatastart, - data_len + (relocs * sizeof(unsigned long))); - } else if (flags & FLAT_FLAG_GZDATA) { - fpos = 0; - result = bprm->file->f_op->read(bprm->file, - (char *) textpos, text_len, &fpos); - if (!is_error(result)) { - result = decompress_exec(bprm, text_len, (char *) datapos, - data_len + (relocs * sizeof(unsigned long)), 0); - } - } - else -#endif - { - result = target_pread(bprm->fd, textpos, - text_len, 0); - if (result >= 0) { - result = target_pread(bprm->fd, datapos, - data_len + (relocs * sizeof(abi_ulong)), - ntohl(hdr->data_start)); - } + result = target_pread(bprm->src.fd, textpos, + text_len, 0); + if (result >= 0) { + result = target_pread(bprm->src.fd, datapos, + data_len + (relocs * sizeof(abi_ulong)), + ntohl(hdr->data_start)); } if (result < 0) { fprintf(stderr, "Unable to read code+data+bss\n"); @@ -678,44 +456,6 @@ static int load_flat_file(struct linux_binprm * bprm, /****************************************************************************/ -#ifdef CONFIG_BINFMT_SHARED_FLAT - -/* - * Load a shared library into memory. The library gets its own data - * segment (including bss) but not argv/argc/environ. - */ - -static int load_flat_shared_library(int id, struct lib_info *libs) -{ - struct linux_binprm bprm; - int res; - char buf[16]; - - /* Create the file name */ - sprintf(buf, "/lib/lib%d.so", id); - - /* Open the file up */ - bprm.filename = buf; - bprm.file = open_exec(bprm.filename); - res = PTR_ERR(bprm.file); - if (IS_ERR(bprm.file)) - return res; - - res = prepare_binprm(&bprm); - - if (!is_error(res)) { - res = load_flat_file(&bprm, libs, id, NULL); - } - if (bprm.file) { - allow_write_access(bprm.file); - fput(bprm.file); - bprm.file = NULL; - } - return(res); -} - -#endif /* CONFIG_BINFMT_SHARED_FLAT */ - int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) { struct lib_info libinfo[MAX_SHARED_LIBS]; @@ -747,7 +487,10 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) stack_len += (bprm->envc + 1) * 4; /* the envp array */ + mmap_lock(); res = load_flat_file(bprm, libinfo, 0, &stack_len); + mmap_unlock(); + if (is_error(res)) { return res; } @@ -755,15 +498,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; @@ -793,25 +536,12 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) */ start_addr = libinfo[0].entry; -#ifdef CONFIG_BINFMT_SHARED_FLAT -#error here - for (i = MAX_SHARED_LIBS-1; i>0; i--) { - if (libinfo[i].loaded) { - /* Push previous first to call address */ - --sp; - if (put_user_ual(start_addr, sp)) - return -EFAULT; - start_addr = libinfo[i].entry; - } - } -#endif - /* 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->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..b47019e136 --- /dev/null +++ b/linux-user/gen-vdso-elfn.c.inc @@ -0,0 +1,350 @@ +/* + * 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) *target_sym = buf + shdr[sym_idx].sh_offset; + unsigned sym_n = shdr[sym_idx].sh_size / sizeof(*target_sym); + const char *str = buf + shdr[str_idx].sh_offset; + + for (unsigned i = 0; i < sym_n; ++i) { + const char *name; + ElfN(Sym) sym; + + memcpy(&sym, &target_sym[i], sizeof(sym)); + if (need_bswap) { + elfN(bswap_sym)(&sym); + } + name = str + sym.st_name; + + if (sigreturn_sym && strcmp(sigreturn_sym, name) == 0) { + sigreturn_addr = sym.st_value; + } + if (rt_sigreturn_sym && strcmp(rt_sigreturn_sym, name) == 0) { + rt_sigreturn_addr = sym.st_value; + } + } +} + +static void elfN(bswap_ps_hdrs)(ElfN(Ehdr) *ehdr) +{ + ElfN(Phdr) *phdr = (void *)ehdr + ehdr->e_phoff; + ElfN(Shdr) *shdr = (void *)ehdr + ehdr->e_shoff; + ElfN(Half) i; + + for (i = 0; i < ehdr->e_phnum; ++i) { + elfN(bswap_phdr)(&phdr[i]); + } + + for (i = 0; i < ehdr->e_shnum; ++i) { + elfN(bswap_shdr)(&shdr[i]); + } +} + +static void elfN(process)(FILE *outf, void *buf, long len, 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)(buf); + elfN(bswap_ps_hdrs)(buf); + } + + phnum = ehdr->e_phnum; + phdr = buf + ehdr->e_phoff; + shnum = ehdr->e_shnum; + shdr = buf + ehdr->e_shoff; + 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++; + } + /* + * Extend the program header to cover the entire VDSO, so that + * load_elf_vdso() loads everything, including section headers. + * + * Require that there is no .bss, since it would break this + * approach. + */ + if (phdr[i].p_filesz != phdr[i].p_memsz) { + fprintf(stderr, "LOAD segment's filesz and memsz differ\n"); + errors++; + } + if (phdr[i].p_filesz > len) { + fprintf(stderr, "LOAD segment is larger than the whole VDSO\n"); + errors++; + } + phdr[i].p_filesz = len; + phdr[i].p_memsz = len; + first_segsz = len; + 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 section headers. */ + for (unsigned i = 0; i < shnum; ++i) { + output_reloc(outf, buf, &shdr[i].sh_addr); + } + + /* Relocate the DYNAMIC entries. */ + if (dynamic_addr) { + ElfN(Dyn) *target_dyn = buf + dynamic_ofs; + __typeof(((ElfN(Dyn) *)target_dyn)->d_tag) tag; + + do { + ElfN(Dyn) dyn; + + memcpy(&dyn, target_dyn, sizeof(dyn)); + 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, &target_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; + } + target_dyn++; + } while (tag != DT_NULL); + if (errors) { + exit(EXIT_FAILURE); + } + } + + /* Relocate the dynamic symbol table. */ + if (dynsym_idx) { + ElfN(Sym) *target_sym = buf + shdr[dynsym_idx].sh_offset; + unsigned sym_n = shdr[dynsym_idx].sh_size / sizeof(*target_sym); + + for (unsigned i = 0; i < sym_n; ++i) { + output_reloc(outf, buf, &target_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); + } + + if (need_bswap) { + elfN(bswap_ps_hdrs)(buf); + elfN(bswap_ehdr)(buf); + } +} diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c new file mode 100644 index 0000000000..721f38d5a3 --- /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); + + /* + * 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, + "/* Automatically generated by linux-user/gen-vdso.c. */\n" + "\n" + "static const unsigned %s_relocs[] = {\n", prefix); + + switch (buf[EI_CLASS]) { + case ELFCLASS32: + elf32_process(outf, buf, total_len, need_bswap); + break; + case ELFCLASS64: + elf64_process(outf, buf, total_len, 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. */ + + /* + * Write out the vdso image now, after we made local changes. + */ + fprintf(outf, + "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"); + + 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 index 1436a3c543..ec76a91b46 100644 --- a/linux-user/generic/target_mman.h +++ b/linux-user/generic/target_mman.h @@ -1,6 +1,64 @@ #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 @@ -89,4 +147,17 @@ #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/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index b84e25bf71..d41159e52a 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -60,9 +60,16 @@ void cpu_loop(CPUHexagonState *env) env->gpr[0] = ret; } break; + case HEX_EXCP_PC_NOT_ALIGNED: + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, + env->gpr[HEX_REG_R31]); + break; 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/meson.build b/linux-user/hexagon/meson.build new file mode 100644 index 0000000000..d203c3ec92 --- /dev/null +++ b/linux-user/hexagon/meson.build @@ -0,0 +1,6 @@ + +syscall_nr_generators += { + 'hexagon': generator(sh, + arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], + output: '@BASENAME@_nr.h') +} 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/syscall.tbl b/linux-user/hexagon/syscall.tbl new file mode 100644 index 0000000000..845e24eb37 --- /dev/null +++ b/linux-user/hexagon/syscall.tbl @@ -0,0 +1,405 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# This file contains the system call numbers for all of the +# more recently added architectures. +# +# As a basic principle, no duplication of functionality +# should be added, e.g. we don't use lseek when llseek +# is present. New architectures should use this file +# and implement the less feature-full calls in user space. +# +0 common io_setup sys_io_setup compat_sys_io_setup +1 common io_destroy sys_io_destroy +2 common io_submit sys_io_submit compat_sys_io_submit +3 common io_cancel sys_io_cancel +4 time32 io_getevents sys_io_getevents_time32 +4 64 io_getevents sys_io_getevents +5 common setxattr sys_setxattr +6 common lsetxattr sys_lsetxattr +7 common fsetxattr sys_fsetxattr +8 common getxattr sys_getxattr +9 common lgetxattr sys_lgetxattr +10 common fgetxattr sys_fgetxattr +11 common listxattr sys_listxattr +12 common llistxattr sys_llistxattr +13 common flistxattr sys_flistxattr +14 common removexattr sys_removexattr +15 common lremovexattr sys_lremovexattr +16 common fremovexattr sys_fremovexattr +17 common getcwd sys_getcwd +18 common lookup_dcookie sys_ni_syscall +19 common eventfd2 sys_eventfd2 +20 common epoll_create1 sys_epoll_create1 +21 common epoll_ctl sys_epoll_ctl +22 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +23 common dup sys_dup +24 common dup3 sys_dup3 +25 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +25 64 fcntl sys_fcntl +26 common inotify_init1 sys_inotify_init1 +27 common inotify_add_watch sys_inotify_add_watch +28 common inotify_rm_watch sys_inotify_rm_watch +29 common ioctl sys_ioctl compat_sys_ioctl +30 common ioprio_set sys_ioprio_set +31 common ioprio_get sys_ioprio_get +32 common flock sys_flock +33 common mknodat sys_mknodat +34 common mkdirat sys_mkdirat +35 common unlinkat sys_unlinkat +36 common symlinkat sys_symlinkat +37 common linkat sys_linkat +# renameat is superseded with flags by renameat2 +38 renameat renameat sys_renameat +39 common umount2 sys_umount +40 common mount sys_mount +41 common pivot_root sys_pivot_root +42 common nfsservctl sys_ni_syscall +43 32 statfs64 sys_statfs64 compat_sys_statfs64 +43 64 statfs sys_statfs +44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +44 64 fstatfs sys_fstatfs +45 32 truncate64 sys_truncate64 compat_sys_truncate64 +45 64 truncate sys_truncate +46 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +46 64 ftruncate sys_ftruncate +47 common fallocate sys_fallocate compat_sys_fallocate +48 common faccessat sys_faccessat +49 common chdir sys_chdir +50 common fchdir sys_fchdir +51 common chroot sys_chroot +52 common fchmod sys_fchmod +53 common fchmodat sys_fchmodat +54 common fchownat sys_fchownat +55 common fchown sys_fchown +56 common openat sys_openat +57 common close sys_close +58 common vhangup sys_vhangup +59 common pipe2 sys_pipe2 +60 common quotactl sys_quotactl +61 common getdents64 sys_getdents64 +62 32 llseek sys_llseek +62 64 lseek sys_lseek +63 common read sys_read +64 common write sys_write +65 common readv sys_readv sys_readv +66 common writev sys_writev sys_writev +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 common preadv sys_preadv compat_sys_preadv +70 common pwritev sys_pwritev compat_sys_pwritev +71 32 sendfile64 sys_sendfile64 +71 64 sendfile sys_sendfile64 +72 time32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +72 64 pselect6 sys_pselect6 +73 time32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +73 64 ppoll sys_ppoll +74 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +75 common vmsplice sys_vmsplice +76 common splice sys_splice +77 common tee sys_tee +78 common readlinkat sys_readlinkat +79 stat64 fstatat64 sys_fstatat64 +79 64 newfstatat sys_newfstatat +80 stat64 fstat64 sys_fstat64 +80 64 fstat sys_newfstat +81 common sync sys_sync +82 common fsync sys_fsync +83 common fdatasync sys_fdatasync +84 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +85 common timerfd_create sys_timerfd_create +86 time32 timerfd_settime sys_timerfd_settime32 +86 64 timerfd_settime sys_timerfd_settime +87 time32 timerfd_gettime sys_timerfd_gettime32 +87 64 timerfd_gettime sys_timerfd_gettime +88 time32 utimensat sys_utimensat_time32 +88 64 utimensat sys_utimensat +89 common acct sys_acct +90 common capget sys_capget +91 common capset sys_capset +92 common personality sys_personality +93 common exit sys_exit +94 common exit_group sys_exit_group +95 common waitid sys_waitid compat_sys_waitid +96 common set_tid_address sys_set_tid_address +97 common unshare sys_unshare +98 time32 futex sys_futex_time32 +98 64 futex sys_futex +99 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +100 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +101 time32 nanosleep sys_nanosleep_time32 +101 64 nanosleep sys_nanosleep +102 common getitimer sys_getitimer compat_sys_getitimer +103 common setitimer sys_setitimer compat_sys_setitimer +104 common kexec_load sys_kexec_load compat_sys_kexec_load +105 common init_module sys_init_module +106 common delete_module sys_delete_module +107 common timer_create sys_timer_create compat_sys_timer_create +108 time32 timer_gettime sys_timer_gettime32 +108 64 timer_gettime sys_timer_gettime +109 common timer_getoverrun sys_timer_getoverrun +110 time32 timer_settime sys_timer_settime32 +110 64 timer_settime sys_timer_settime +111 common timer_delete sys_timer_delete +112 time32 clock_settime sys_clock_settime32 +112 64 clock_settime sys_clock_settime +113 time32 clock_gettime sys_clock_gettime32 +113 64 clock_gettime sys_clock_gettime +114 time32 clock_getres sys_clock_getres_time32 +114 64 clock_getres sys_clock_getres +115 time32 clock_nanosleep sys_clock_nanosleep_time32 +115 64 clock_nanosleep sys_clock_nanosleep +116 common syslog sys_syslog +117 common ptrace sys_ptrace compat_sys_ptrace +118 common sched_setparam sys_sched_setparam +119 common sched_setscheduler sys_sched_setscheduler +120 common sched_getscheduler sys_sched_getscheduler +121 common sched_getparam sys_sched_getparam +122 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +123 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +124 common sched_yield sys_sched_yield +125 common sched_get_priority_max sys_sched_get_priority_max +126 common sched_get_priority_min sys_sched_get_priority_min +127 time32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +127 64 sched_rr_get_interval sys_sched_rr_get_interval +128 common restart_syscall sys_restart_syscall +129 common kill sys_kill +130 common tkill sys_tkill +131 common tgkill sys_tgkill +132 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +133 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +134 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +135 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +136 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +137 time32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +137 64 rt_sigtimedwait sys_rt_sigtimedwait +138 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +139 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +140 common setpriority sys_setpriority +141 common getpriority sys_getpriority +142 common reboot sys_reboot +143 common setregid sys_setregid +144 common setgid sys_setgid +145 common setreuid sys_setreuid +146 common setuid sys_setuid +147 common setresuid sys_setresuid +148 common getresuid sys_getresuid +149 common setresgid sys_setresgid +150 common getresgid sys_getresgid +151 common setfsuid sys_setfsuid +152 common setfsgid sys_setfsgid +153 common times sys_times compat_sys_times +154 common setpgid sys_setpgid +155 common getpgid sys_getpgid +156 common getsid sys_getsid +157 common setsid sys_setsid +158 common getgroups sys_getgroups +159 common setgroups sys_setgroups +160 common uname sys_newuname +161 common sethostname sys_sethostname +162 common setdomainname sys_setdomainname +# getrlimit and setrlimit are superseded with prlimit64 +163 rlimit getrlimit sys_getrlimit compat_sys_getrlimit +164 rlimit setrlimit sys_setrlimit compat_sys_setrlimit +165 common getrusage sys_getrusage compat_sys_getrusage +166 common umask sys_umask +167 common prctl sys_prctl +168 common getcpu sys_getcpu +169 time32 gettimeofday sys_gettimeofday compat_sys_gettimeofday +169 64 gettimeofday sys_gettimeofday +170 time32 settimeofday sys_settimeofday compat_sys_settimeofday +170 64 settimeofday sys_settimeofday +171 time32 adjtimex sys_adjtimex_time32 +171 64 adjtimex sys_adjtimex +172 common getpid sys_getpid +173 common getppid sys_getppid +174 common getuid sys_getuid +175 common geteuid sys_geteuid +176 common getgid sys_getgid +177 common getegid sys_getegid +178 common gettid sys_gettid +179 common sysinfo sys_sysinfo compat_sys_sysinfo +180 common mq_open sys_mq_open compat_sys_mq_open +181 common mq_unlink sys_mq_unlink +182 time32 mq_timedsend sys_mq_timedsend_time32 +182 64 mq_timedsend sys_mq_timedsend +183 time32 mq_timedreceive sys_mq_timedreceive_time32 +183 64 mq_timedreceive sys_mq_timedreceive +184 common mq_notify sys_mq_notify compat_sys_mq_notify +185 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +186 common msgget sys_msgget +187 common msgctl sys_msgctl compat_sys_msgctl +188 common msgrcv sys_msgrcv compat_sys_msgrcv +189 common msgsnd sys_msgsnd compat_sys_msgsnd +190 common semget sys_semget +191 common semctl sys_semctl compat_sys_semctl +192 time32 semtimedop sys_semtimedop_time32 +192 64 semtimedop sys_semtimedop +193 common semop sys_semop +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +196 common shmat sys_shmat compat_sys_shmat +197 common shmdt sys_shmdt +198 common socket sys_socket +199 common socketpair sys_socketpair +200 common bind sys_bind +201 common listen sys_listen +202 common accept sys_accept +203 common connect sys_connect +204 common getsockname sys_getsockname +205 common getpeername sys_getpeername +206 common sendto sys_sendto +207 common recvfrom sys_recvfrom compat_sys_recvfrom +208 common setsockopt sys_setsockopt sys_setsockopt +209 common getsockopt sys_getsockopt sys_getsockopt +210 common shutdown sys_shutdown +211 common sendmsg sys_sendmsg compat_sys_sendmsg +212 common recvmsg sys_recvmsg compat_sys_recvmsg +213 common readahead sys_readahead compat_sys_readahead +214 common brk sys_brk +215 common munmap sys_munmap +216 common mremap sys_mremap +217 common add_key sys_add_key +218 common request_key sys_request_key +219 common keyctl sys_keyctl compat_sys_keyctl +220 common clone sys_clone +221 common execve sys_execve compat_sys_execve +222 32 mmap2 sys_mmap2 +222 64 mmap sys_mmap +223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +223 64 fadvise64 sys_fadvise64_64 +224 common swapon sys_swapon +225 common swapoff sys_swapoff +226 common mprotect sys_mprotect +227 common msync sys_msync +228 common mlock sys_mlock +229 common munlock sys_munlock +230 common mlockall sys_mlockall +231 common munlockall sys_munlockall +232 common mincore sys_mincore +233 common madvise sys_madvise +234 common remap_file_pages sys_remap_file_pages +235 common mbind sys_mbind +236 common get_mempolicy sys_get_mempolicy +237 common set_mempolicy sys_set_mempolicy +238 common migrate_pages sys_migrate_pages +239 common move_pages sys_move_pages +240 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +241 common perf_event_open sys_perf_event_open +242 common accept4 sys_accept4 +243 time32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +243 64 recvmmsg sys_recvmmsg +# Architectures may provide up to 16 syscalls of their own between 244 and 259 +244 arc cacheflush sys_cacheflush +245 arc arc_settls sys_arc_settls +246 arc arc_gettls sys_arc_gettls +247 arc sysfs sys_sysfs +248 arc arc_usr_cmpxchg sys_arc_usr_cmpxchg + +244 csky set_thread_area sys_set_thread_area +245 csky cacheflush sys_cacheflush + +244 nios2 cacheflush sys_cacheflush + +244 or1k or1k_atomic sys_or1k_atomic + +258 riscv riscv_hwprobe sys_riscv_hwprobe +259 riscv riscv_flush_icache sys_riscv_flush_icache + +260 time32 wait4 sys_wait4 compat_sys_wait4 +260 64 wait4 sys_wait4 +261 common prlimit64 sys_prlimit64 +262 common fanotify_init sys_fanotify_init +263 common fanotify_mark sys_fanotify_mark +264 common name_to_handle_at sys_name_to_handle_at +265 common open_by_handle_at sys_open_by_handle_at +266 time32 clock_adjtime sys_clock_adjtime32 +266 64 clock_adjtime sys_clock_adjtime +267 common syncfs sys_syncfs +268 common setns sys_setns +269 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +270 common process_vm_readv sys_process_vm_readv +271 common process_vm_writev sys_process_vm_writev +272 common kcmp sys_kcmp +273 common finit_module sys_finit_module +274 common sched_setattr sys_sched_setattr +275 common sched_getattr sys_sched_getattr +276 common renameat2 sys_renameat2 +277 common seccomp sys_seccomp +278 common getrandom sys_getrandom +279 common memfd_create sys_memfd_create +280 common bpf sys_bpf +281 common execveat sys_execveat compat_sys_execveat +282 common userfaultfd sys_userfaultfd +283 common membarrier sys_membarrier +284 common mlock2 sys_mlock2 +285 common copy_file_range sys_copy_file_range +286 common preadv2 sys_preadv2 compat_sys_preadv2 +287 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +288 common pkey_mprotect sys_pkey_mprotect +289 common pkey_alloc sys_pkey_alloc +290 common pkey_free sys_pkey_free +291 common statx sys_statx +292 time32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +292 64 io_pgetevents sys_io_pgetevents +293 common rseq sys_rseq +294 common kexec_file_load sys_kexec_file_load +# 295 through 402 are unassigned to sync up with generic numbers don't use +403 32 clock_gettime64 sys_clock_gettime +404 32 clock_settime64 sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime +409 32 timer_settime64 sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +447 memfd_secret memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/hexagon/syscall_nr.h b/linux-user/hexagon/syscall_nr.h deleted file mode 100644 index b047dbbf6d..0000000000 --- a/linux-user/hexagon/syscall_nr.h +++ /dev/null @@ -1,332 +0,0 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_HEXAGON_SYSCALL_NR_H -#define LINUX_USER_HEXAGON_SYSCALL_NR_H - -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl64 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs64 43 -#define TARGET_NR_fstatfs64 44 -#define TARGET_NR_truncate64 45 -#define TARGET_NR_ftruncate64 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_llseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile64 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 -#define TARGET_NR_fstat64 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_io_pgetevents 292 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_clock_gettime64 403 -#define TARGET_NR_clock_settime64 404 -#define TARGET_NR_clock_adjtime64 405 -#define TARGET_NR_clock_getres_time64 406 -#define TARGET_NR_clock_nanosleep_time64 407 -#define TARGET_NR_timer_gettime64 408 -#define TARGET_NR_timer_settime64 409 -#define TARGET_NR_timerfd_gettime64 410 -#define TARGET_NR_timerfd_settime64 411 -#define TARGET_NR_utimensat_time64 412 -#define TARGET_NR_pselect6_time64 413 -#define TARGET_NR_ppoll_time64 414 -#define TARGET_NR_io_pgetevents_time64 416 -#define TARGET_NR_recvmmsg_time64 417 -#define TARGET_NR_mq_timedsend_time64 418 -#define TARGET_NR_mq_timedreceive_time64 419 -#define TARGET_NR_semtimedop_time64 420 -#define TARGET_NR_rt_sigtimedwait_time64 421 -#define TARGET_NR_futex_time64 422 -#define TARGET_NR_sched_rr_get_interval_time64 423 -#define TARGET_NR_pidfd_send_signal 424 -#define TARGET_NR_io_uring_setup 425 -#define TARGET_NR_io_uring_enter 426 -#define TARGET_NR_io_uring_register 427 -#define TARGET_NR_open_tree 428 -#define TARGET_NR_move_mount 429 -#define TARGET_NR_fsopen 430 -#define TARGET_NR_fsconfig 431 -#define TARGET_NR_fsmount 432 -#define TARGET_NR_fspick 433 -#define TARGET_NR_pidfd_open 434 -#define TARGET_NR_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_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_HEXAGON_SYSCALL_NR_H */ diff --git a/linux-user/hexagon/syscallhdr.sh b/linux-user/hexagon/syscallhdr.sh new file mode 100644 index 0000000000..ed605c038e --- /dev/null +++ b/linux-user/hexagon/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_HEXAGON_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/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 index e7ba6070fe..e6b5e2ca36 100644 --- a/linux-user/hexagon/target_mman.h +++ b/linux-user/hexagon/target_mman.h @@ -1 +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 8ab1335106..23b38ff9b2 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -99,6 +99,8 @@ static abi_ulong hppa_lws(CPUHPPAState *env) #endif } break; + default: + g_assert_not_reached(); } break; } @@ -129,8 +131,8 @@ void cpu_loop(CPUHPPAState *env) default: env->gr[28] = ret; /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; break; case -QEMU_ERESTARTSYS: case -QEMU_ESIGRETURN: @@ -140,27 +142,24 @@ void cpu_loop(CPUHPPAState *env) case EXCP_SYSCALL_LWS: env->gr[21] = hppa_lws(env); /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; break; case EXCP_IMP: force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, env->iaoq_f); break; case EXCP_ILL: - EXCP_DUMP(env, "qemu: EXCP_ILL exception %#x\n", trapnr); force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); break; case EXCP_PRIV_OPR: /* check for glibc ABORT_INSTRUCTION "iitlbp %r0,(%sr0, %r0)" */ - EXCP_DUMP(env, "qemu: EXCP_PRIV_OPR exception %#x\n", trapnr); if (env->cr[CR_IIR] == 0x04000000) { - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->iaoq_f); } else { - force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); + force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->iaoq_f); } break; case EXCP_PRIV_REG: - EXCP_DUMP(env, "qemu: EXCP_PRIV_REG exception %#x\n", trapnr); force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVREG, env->iaoq_f); break; case EXCP_OVERFLOW: @@ -173,8 +172,7 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); break; case EXCP_BREAK: - EXCP_DUMP(env, "qemu: EXCP_BREAK exception %#x\n", trapnr); - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f & ~3); + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); break; case EXCP_DEBUG: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); 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 f253a15864..f6f094c960 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; @@ -47,6 +48,19 @@ struct target_rt_sigframe { /* 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 i; @@ -72,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]); @@ -87,20 +101,12 @@ static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) cpu_hppa_loaded_fr0(env); __get_user(env->iaoq_f, &sc->sc_iaoq[0]); + env->iaoq_f |= PRIV_USER; __get_user(env->iaoq_b, &sc->sc_iaoq[1]); + env->iaoq_b |= PRIV_USER; __get_user(env->cr[CR_SAR], &sc->sc_sar); } -#if TARGET_ABI_BITS == 32 -#define SIGFRAME 64 -#define FUNCTIONCALLFRAME 48 -#else -#define SIGFRAME 128 -#define FUNCTIONCALLFRAME 96 -#endif -#define PARISC_RT_SIGFRAME_SIZE32 \ - ((sizeof(struct target_rt_sigframe) + FUNCTIONCALLFRAME + SIGFRAME) & -SIGFRAME) - void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUArchState *env) @@ -108,7 +114,7 @@ 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)) { @@ -123,7 +129,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; @@ -146,19 +152,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->iaoq_f = haddr | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; + env->psw_n = 0; return; give_sigsegv: diff --git a/linux-user/hppa/syscall.tbl b/linux-user/hppa/syscall.tbl index aabc37f8ca..647f08e67a 100644 --- a/linux-user/hppa/syscall.tbl +++ b/linux-user/hppa/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for parisc # @@ -108,7 +108,7 @@ 95 common fchown sys_fchown 96 common getpriority sys_getpriority 97 common setpriority sys_setpriority -98 common recv sys_recv +98 common recv sys_recv compat_sys_recv 99 common statfs sys_statfs compat_sys_statfs 100 common fstatfs sys_fstatfs compat_sys_fstatfs 101 common stat64 sys_stat64 @@ -131,11 +131,11 @@ 116 common sysinfo sys_sysinfo compat_sys_sysinfo 117 common shutdown sys_shutdown 118 common fsync sys_fsync -119 common madvise sys_madvise +119 common madvise parisc_madvise 120 common clone sys_clone_wrapper 121 common setdomainname sys_setdomainname 122 common sendfile sys_sendfile compat_sys_sendfile -123 common recvfrom sys_recvfrom +123 common recvfrom sys_recvfrom compat_sys_recvfrom 124 32 adjtimex sys_adjtimex_time32 124 64 adjtimex sys_adjtimex 125 common mprotect sys_mprotect @@ -147,7 +147,7 @@ 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir -134 common bdflush sys_bdflush +134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 32 personality parisc_personality 136 64 personality sys_personality @@ -245,7 +245,7 @@ # 220 was alloc_hugepages # 221 was free_hugepages 222 common exit_group sys_exit_group -223 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +223 common lookup_dcookie sys_ni_syscall 224 common epoll_create sys_epoll_create 225 common epoll_ctl sys_epoll_ctl 226 common epoll_wait sys_epoll_wait @@ -292,9 +292,9 @@ 258 32 clock_nanosleep sys_clock_nanosleep_time32 258 64 clock_nanosleep sys_clock_nanosleep 259 common tgkill sys_tgkill -260 common mbind sys_mbind compat_sys_mbind -261 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy -262 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +260 common mbind sys_mbind +261 common get_mempolicy sys_get_mempolicy +262 common set_mempolicy sys_set_mempolicy # 263 was vserver 264 common add_key sys_add_key 265 common request_key sys_request_key @@ -331,7 +331,7 @@ 292 64 sync_file_range sys_sync_file_range 293 common tee sys_tee 294 common vmsplice sys_vmsplice -295 common move_pages sys_move_pages compat_sys_move_pages +295 common move_pages sys_move_pages 296 common getcpu sys_getcpu 297 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 298 common statfs64 sys_statfs64 compat_sys_statfs64 @@ -364,7 +364,7 @@ 320 common accept4 sys_accept4 321 common prlimit64 sys_prlimit64 322 common fanotify_init sys_fanotify_init -323 common fanotify_mark sys_fanotify_mark sys32_fanotify_mark +323 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 324 32 clock_adjtime sys_clock_adjtime32 324 64 clock_adjtime sys_clock_adjtime 325 common name_to_handle_at sys_name_to_handle_at @@ -400,6 +400,7 @@ 353 common pkey_free sys_pkey_free 354 common rseq sys_rseq 355 common kexec_file_load sys_kexec_file_load sys_kexec_file_load +356 common cacheflush sys_cacheflush # up to 402 is unassigned and reserved for arch specific syscalls 403 32 clock_gettime64 sys_clock_gettime sys_clock_gettime 404 32 clock_settime64 sys_clock_settime sys_clock_settime @@ -413,7 +414,7 @@ 412 32 utimensat_time64 sys_utimensat sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 -416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive @@ -440,7 +441,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/hppa/syscallhdr.sh b/linux-user/hppa/syscallhdr.sh index ac91a95762..bf1c1d4f30 100644 --- a/linux-user/hppa/syscallhdr.sh +++ b/linux-user/hppa/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h index aacf3e9e02..4b84422a90 100644 --- a/linux-user/hppa/target_cpu.h +++ b/linux-user/hppa/target_cpu.h @@ -28,8 +28,8 @@ static inline void cpu_clone_regs_child(CPUHPPAState *env, target_ulong newsp, /* Indicate child in return value. */ env->gr[28] = 0; /* Return from the syscall. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; + env->iaoq_f = env->gr[31] | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; } static inline void cpu_clone_regs_parent(CPUHPPAState *env, unsigned flags) 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 index 66dd9f7941..ccda46e842 100644 --- a/linux-user/hppa/target_mman.h +++ b/linux-user/hppa/target_mman.h @@ -1,6 +1,16 @@ #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 @@ -10,6 +20,16 @@ #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/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 865413c08f..7a35215278 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 @@ -166,6 +172,7 @@ static void emulate_vsyscall(CPUX86State *env) /* * Perform the syscall. None of the vsyscalls should need restarting. */ + get_task_state(env_cpu(env))->orig_ax = syscall; ret = do_syscall(env, syscall, env->regs[R_EDI], env->regs[R_ESI], env->regs[R_EDX], env->regs[10], env->regs[8], env->regs[9], 0, 0); @@ -211,7 +218,11 @@ void cpu_loop(CPUX86State *env) switch(trapnr) { case 0x80: +#ifndef TARGET_X86_64 + case EXCP_SYSCALL: +#endif /* linux syscall from int $0x80 */ + get_task_state(cs)->orig_ax = env->regs[R_EAX]; ret = do_syscall(env, env->regs[R_EAX], env->regs[R_EBX], @@ -227,9 +238,10 @@ 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. */ + get_task_state(cs)->orig_ax = env->regs[R_EAX]; ret = do_syscall(env, env->regs[R_EAX], env->regs[R_EDI], @@ -245,8 +257,6 @@ void cpu_loop(CPUX86State *env) env->regs[R_EAX] = ret; } break; -#endif -#ifdef TARGET_X86_64 case EXCP_VSYSCALL: emulate_vsyscall(env); break; @@ -314,23 +324,38 @@ void cpu_loop(CPUX86State *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 */ @@ -369,27 +394,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 60fa07d6f9..0f11dba831 100644 --- a/linux-user/i386/signal.c +++ b/linux-user/i386/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "user/tswap-target.h" /* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ @@ -33,15 +34,22 @@ struct target_fpreg { uint16_t exponent; }; -struct target_fpxreg { - uint16_t significand[4]; - uint16_t exponent; - uint16_t padding[3]; -}; +/* Legacy x87 fpu state format for FSAVE/FRESTOR. */ +struct target_fregs_state { + uint32_t cwd; + uint32_t swd; + uint32_t twd; + uint32_t fip; + uint32_t fcs; + uint32_t foo; + uint32_t fos; + struct target_fpreg st[8]; -struct target_xmmreg { - uint32_t element[4]; + /* Software status information [not touched by FSAVE]. */ + uint16_t status; + uint16_t magic; /* 0xffff: FPU data only, 0x0000: FXSR FPU data */ }; +QEMU_BUILD_BUG_ON(sizeof(struct target_fregs_state) != 32 + 80); struct target_fpx_sw_bytes { uint32_t magic1; @@ -52,55 +60,11 @@ struct target_fpx_sw_bytes { }; 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; - uint32_t sw; - uint32_t tag; - uint32_t ipoff; - uint32_t cssel; - uint32_t dataoff; - uint32_t datasel; - struct target_fpreg st[8]; - uint16_t status; - uint16_t magic; /* 0xffff = regular FPU data only */ - struct target_fpstate_fxsave fxsave; + struct target_fregs_state fpstate; + X86LegacyXSaveArea fxstate; }; -/* - * 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_fxsave -# define TARGET_FPSTATE_FXSAVE_OFFSET 0 -#endif - struct target_sigcontext_32 { uint16_t gs, __gsh; uint16_t fs, __fsh; @@ -183,24 +147,16 @@ struct sigframe { int sig; struct target_sigcontext sc; /* - * 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 + * 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; + struct target_fpstate_32 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); + /* fp state follows here */ }; -#define TARGET_SIGFRAME_FXSAVE_OFFSET ( \ - offsetof(struct sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) struct rt_sigframe { abi_ulong pretcode; @@ -210,74 +166,194 @@ struct rt_sigframe { struct target_siginfo info; struct target_ucontext uc; char retcode[8]; - struct target_fpstate fpstate QEMU_ALIGNED(8); + /* fp state follows here */ }; -#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 struct rt_sigframe { abi_ulong pretcode; struct target_ucontext uc; struct target_siginfo info; - struct target_fpstate fpstate QEMU_ALIGNED(16); + /* fp state follows here */ }; -#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \ - offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET) #endif +typedef enum { +#ifndef TARGET_X86_64 + FPSTATE_FSAVE, +#endif + FPSTATE_FXSAVE, + FPSTATE_XSAVE +} FPStateKind; + +static FPStateKind get_fpstate_kind(CPUX86State *env) +{ + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { + return FPSTATE_XSAVE; + } +#ifdef TARGET_X86_64 + return FPSTATE_FXSAVE; +#else + if (env->features[FEAT_1_EDX] & CPUID_FXSR) { + return FPSTATE_FXSAVE; + } + return FPSTATE_FSAVE; +#endif +} + +static unsigned get_fpstate_size(CPUX86State *env, FPStateKind fpkind) +{ + /* + * Kernel: + * fpu__alloc_mathframe + * xstate_sigframe_size(current->thread.fpu.fpstate); + * size = fpstate->user_size + * use_xsave() ? size + FP_XSTATE_MAGIC2_SIZE : size + * where fpstate->user_size is computed at init in + * fpu__init_system_xstate_size_legacy and + * fpu__init_system_xstate. + * + * Here we have no place to pre-compute, so inline it all. + */ + switch (fpkind) { + case FPSTATE_XSAVE: + return (xsave_area_size(env->xcr0, false) + + TARGET_FP_XSTATE_MAGIC2_SIZE); + case FPSTATE_FXSAVE: + return sizeof(X86LegacyXSaveArea); +#ifndef TARGET_X86_64 + case FPSTATE_FSAVE: + return sizeof(struct target_fregs_state); +#endif + } + g_assert_not_reached(); +} + +static abi_ptr get_sigframe(struct target_sigaction *ka, CPUX86State *env, + unsigned frame_size, FPStateKind fpkind, + abi_ptr *fpstate, abi_ptr *fxstate, abi_ptr *fpend) +{ + abi_ptr sp; + unsigned math_size; + + /* Default to using normal stack */ + sp = get_sp_from_cpustate(env); +#ifdef TARGET_X86_64 + sp -= 128; /* this is the redzone */ +#endif + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + sp = target_sigsp(sp, ka); + } else { +#ifndef TARGET_X86_64 + /* This is the legacy signal stack switching. */ + if ((env->segs[R_SS].selector & 0xffff) != __USER_DS + && !(ka->sa_flags & TARGET_SA_RESTORER) + && ka->sa_restorer) { + sp = ka->sa_restorer; + } +#endif + } + + math_size = get_fpstate_size(env, fpkind); + sp = ROUND_DOWN(sp - math_size, 64); + *fpend = sp + math_size; + *fxstate = sp; +#ifndef TARGET_X86_64 + if (fpkind != FPSTATE_FSAVE) { + sp -= sizeof(struct target_fregs_state); + } +#endif + *fpstate = sp; + + sp -= frame_size; + /* + * Align the stack pointer according to the ABI, i.e. so that on + * function entry ((sp + sizeof(return_addr)) & 15) == 0. + */ + sp += sizeof(target_ulong); + sp = ROUND_DOWN(sp, 16); + sp -= sizeof(target_ulong); + + return sp; +} + /* * Set up a signal frame. */ -static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave, - abi_ulong fxsave_addr) +static void fxsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxstate) { - if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { - /* fxsave_addr must be 16 byte aligned for fxsave */ - assert(!(fxsave_addr & 0xf)); + struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; - 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]); - } + cpu_x86_fxsave(env, fxstate, sizeof(*fxstate)); + __put_user(0, &sw->magic1); } -static void setup_sigcontext(struct target_sigcontext *sc, - struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, - abi_ulong fpstate_addr) +static void xsave_sigcontext(CPUX86State *env, + X86LegacyXSaveArea *fxstate, + abi_ptr fpstate_addr, + abi_ptr xstate_addr, + abi_ptr fpend_addr) +{ + struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; + /* + * 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 = fpend_addr - fpstate_addr; + /* Recover xstate_size by removing magic2. */ + uint32_t xstate_size = (fpend_addr - xstate_addr + - TARGET_FP_XSTATE_MAGIC2_SIZE); + /* magic2 goes just after xstate. */ + uint32_t *magic2 = (void *)fxstate + xstate_size; + + /* xstate_addr must be 64 byte aligned for xsave */ + assert(!(xstate_addr & 0x3f)); + + /* Zero the header, XSAVE *adds* features to an existing save state. */ + memset(fxstate + 1, 0, sizeof(X86XSaveHeader)); + cpu_x86_xsave(env, fxstate, fpend_addr - xstate_addr, env->xcr0); + + __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1); + __put_user(extended_size, &sw->extended_size); + __put_user(env->xcr0, &sw->xfeatures); + __put_user(xstate_size, &sw->xstate_size); + __put_user(TARGET_FP_XSTATE_MAGIC2, magic2); +} + +static void setup_sigcontext(CPUX86State *env, + struct target_sigcontext *sc, + abi_ulong mask, FPStateKind fpkind, + struct target_fregs_state *fpstate, + abi_ptr fpstate_addr, + X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr, + abi_ptr fpend_addr) { CPUState *cs = env_cpu(env); + #ifndef TARGET_X86_64 uint16_t magic; /* already locked in setup_frame() */ - __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs); - __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs); - __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es); - __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds); + __put_user(env->segs[R_GS].selector, (uint32_t *)&sc->gs); + __put_user(env->segs[R_FS].selector, (uint32_t *)&sc->fs); + __put_user(env->segs[R_ES].selector, (uint32_t *)&sc->es); + __put_user(env->segs[R_DS].selector, (uint32_t *)&sc->ds); __put_user(env->regs[R_EDI], &sc->edi); __put_user(env->regs[R_ESI], &sc->esi); __put_user(env->regs[R_EBP], &sc->ebp); @@ -289,20 +365,14 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user(cs->exception_index, &sc->trapno); __put_user(env->error_code, &sc->err); __put_user(env->eip, &sc->eip); - __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs); + __put_user(env->segs[R_CS].selector, (uint32_t *)&sc->cs); __put_user(env->eflags, &sc->eflags); __put_user(env->regs[R_ESP], &sc->esp_at_signal); - __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); + __put_user(env->segs[R_SS].selector, (uint32_t *)&sc->ss); - cpu_x86_fsave(env, fpstate_addr, 1); - fpstate->status = fpstate->sw; - if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) { - magic = 0xffff; - } else { - xsave_sigcontext(env, &fpstate->fxsave, - fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET); - magic = 0; - } + cpu_x86_fsave(env, fpstate, sizeof(*fpstate)); + fpstate->status = fpstate->swd; + magic = (fpkind == FPSTATE_FSAVE ? 0 : 0xffff); __put_user(magic, &fpstate->magic); #else __put_user(env->regs[R_EDI], &sc->rdi); @@ -332,57 +402,25 @@ static void setup_sigcontext(struct target_sigcontext *sc, __put_user((uint16_t)0, &sc->gs); __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); + switch (fpkind) { + case FPSTATE_XSAVE: + xsave_sigcontext(env, fxstate, fpstate_addr, fxstate_addr, fpend_addr); + break; + case FPSTATE_FXSAVE: + fxsave_sigcontext(env, fxstate); + break; + default: + break; + } + __put_user(fpstate_addr, &sc->fpstate); /* non-iBCS2 extensions.. */ __put_user(mask, &sc->oldmask); __put_user(env->cr[2], &sc->cr2); } -/* - * Determine which stack to use.. - */ - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t fxsave_offset) -{ - unsigned long esp; - - /* Default to using normal stack */ - esp = get_sp_from_cpustate(env); -#ifdef TARGET_X86_64 - esp -= 128; /* this is the redzone */ -#endif - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa_flags & TARGET_SA_ONSTACK) { - esp = target_sigsp(esp, ka); - } else { -#ifndef TARGET_X86_64 - /* This is the legacy signal stack switching. */ - if ((env->segs[R_SS].selector & 0xffff) != __USER_DS && - !(ka->sa_flags & TARGET_SA_RESTORER) && - ka->sa_restorer) { - esp = (unsigned long) ka->sa_restorer; - } -#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 static void install_sigtramp(void *tramp) { @@ -404,22 +442,36 @@ static void install_rt_sigtramp(void *tramp) void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUX86State *env) { - abi_ulong frame_addr; + abi_ptr frame_addr, fpstate_addr, fxstate_addr, fpend_addr; struct sigframe *frame; - int i; + struct target_fregs_state *fpstate; + X86LegacyXSaveArea *fxstate; + unsigned total_size; + FPStateKind fpkind; - frame_addr = get_sigframe(ka, env, TARGET_SIGFRAME_FXSAVE_OFFSET); + fpkind = get_fpstate_kind(env); + frame_addr = get_sigframe(ka, env, sizeof(struct sigframe), fpkind, + &fpstate_addr, &fxstate_addr, &fpend_addr); trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; + total_size = fpend_addr - frame_addr; + frame = lock_user(VERIFY_WRITE, frame_addr, total_size, 0); + if (!frame) { + force_sigsegv(sig); + return; + } - __put_user(sig, &frame->sig); + fxstate = (void *)frame + (fxstate_addr - frame_addr); +#ifdef TARGET_X86_64 + fpstate = NULL; +#else + fpstate = (void *)frame + (fpstate_addr - frame_addr); +#endif - setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], - frame_addr + offsetof(struct sigframe, fpstate)); + setup_sigcontext(env, &frame->sc, set->sig[0], fpkind, + fpstate, fpstate_addr, fxstate, fxstate_addr, fpend_addr); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { + for (int i = 1; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->extramask[i - 1]); } @@ -432,23 +484,24 @@ void setup_frame(int sig, struct target_sigaction *ka, install_sigtramp(frame->retcode); __put_user(default_sigreturn, &frame->pretcode); } + unlock_user(frame, frame_addr, total_size); /* Set up registers for signal handler */ env->regs[R_ESP] = frame_addr; env->eip = ka->_sa_handler; + /* Store argument for both -mregparm=3 and standard. */ + env->regs[R_EAX] = sig; + __put_user(sig, &frame->sig); + /* The kernel clears EDX and ECX even though there is only one arg. */ + env->regs[R_EDX] = 0; + env->regs[R_ECX] = 0; + cpu_x86_load_seg(env, R_DS, __USER_DS); cpu_x86_load_seg(env, R_ES, __USER_DS); cpu_x86_load_seg(env, R_SS, __USER_DS); cpu_x86_load_seg(env, R_CS, __USER_CS); env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - - return; - -give_sigsegv: - force_sigsegv(sig); } #endif @@ -457,48 +510,51 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUX86State *env) { - abi_ulong frame_addr; -#ifndef TARGET_X86_64 - abi_ulong addr; -#endif + abi_ptr frame_addr, fpstate_addr, fxstate_addr, fpend_addr; struct rt_sigframe *frame; - int i; + X86LegacyXSaveArea *fxstate; + struct target_fregs_state *fpstate; + unsigned total_size; + FPStateKind fpkind; - frame_addr = get_sigframe(ka, env, TARGET_RT_SIGFRAME_FXSAVE_OFFSET); + fpkind = get_fpstate_kind(env); + frame_addr = get_sigframe(ka, env, sizeof(struct rt_sigframe), fpkind, + &fpstate_addr, &fxstate_addr, &fpend_addr); trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + total_size = fpend_addr - frame_addr; + frame = lock_user(VERIFY_WRITE, frame_addr, total_size, 0); + if (!frame) { goto give_sigsegv; + } - /* These fields are only in rt_sigframe on 32 bit */ -#ifndef TARGET_X86_64 - __put_user(sig, &frame->sig); - addr = frame_addr + offsetof(struct rt_sigframe, info); - __put_user(addr, &frame->pinfo); - addr = frame_addr + offsetof(struct rt_sigframe, uc); - __put_user(addr, &frame->puc); -#endif if (ka->sa_flags & TARGET_SA_SIGINFO) { - tswap_siginfo(&frame->info, info); + frame->info = *info; } /* Create the ucontext. */ - 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(fpkind == FPSTATE_XSAVE, &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++) { + fxstate = (void *)frame + (fxstate_addr - frame_addr); +#ifdef TARGET_X86_64 + fpstate = NULL; +#else + fpstate = (void *)frame + (fpstate_addr - frame_addr); +#endif + + setup_sigcontext(env, &frame->uc.tuc_mcontext, set->sig[0], fpkind, + fpstate, fpstate_addr, fxstate, fxstate_addr, fpend_addr); + + for (int i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ + /* + * Set up to return from userspace. If provided, use a stub + * already in userspace. + */ if (ka->sa_flags & TARGET_SA_RESTORER) { __put_user(ka->sa_restorer, &frame->pretcode); } else { @@ -517,63 +573,148 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->eip = ka->_sa_handler; #ifndef TARGET_X86_64 + /* Store arguments for both -mregparm=3 and standard. */ env->regs[R_EAX] = sig; + __put_user(sig, &frame->sig); env->regs[R_EDX] = frame_addr + offsetof(struct rt_sigframe, info); + __put_user(env->regs[R_EDX], &frame->pinfo); env->regs[R_ECX] = frame_addr + offsetof(struct rt_sigframe, uc); + __put_user(env->regs[R_ECX], &frame->puc); #else env->regs[R_EAX] = 0; env->regs[R_EDI] = sig; env->regs[R_ESI] = frame_addr + offsetof(struct rt_sigframe, info); env->regs[R_EDX] = frame_addr + offsetof(struct rt_sigframe, uc); #endif + unlock_user(frame, frame_addr, total_size); cpu_x86_load_seg(env, R_DS, __USER_DS); cpu_x86_load_seg(env, R_ES, __USER_DS); cpu_x86_load_seg(env, R_CS, __USER_CS); cpu_x86_load_seg(env, R_SS, __USER_DS); env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - return; 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; +/* + * Restore a signal frame. + */ - /* 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; - } +static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind, + X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr) +{ + struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved; + uint32_t magic1, magic2; + uint32_t extended_size, xstate_size, min_size, max_size; + uint64_t xfeatures; + void *xstate; + bool ok; + + switch (fpkind) { + case FPSTATE_XSAVE: + magic1 = tswap32(sw->magic1); + extended_size = tswap32(sw->extended_size); + xstate_size = tswap32(sw->xstate_size); + min_size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); + max_size = xsave_area_size(env->xcr0, false); + + /* Check for the first magic field and other error scenarios. */ + if (magic1 != TARGET_FP_XSTATE_MAGIC1 || + xstate_size < min_size || + xstate_size > max_size || + xstate_size > extended_size) { + break; } - /* fall through to fxrstor */ + + /* + * Restore the features indicated in the frame, masked by + * those currently enabled. Re-check the frame size. + * ??? It is not clear where the kernel does this, but it + * is not in check_xstate_in_sigframe, and so (probably) + * does not fall back to fxrstor. + */ + xfeatures = tswap64(sw->xfeatures) & env->xcr0; + min_size = xsave_area_size(xfeatures, false); + if (xstate_size < min_size) { + return false; + } + + /* Re-lock the entire xstate area, with the extensions and magic. */ + xstate = lock_user(VERIFY_READ, fxstate_addr, + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE, 1); + if (!xstate) { + return false; + } + + /* + * Check for the presence of second magic word at the end of memory + * layout. This detects the case where the user just copied the legacy + * fpstate layout with out copying the extended state information + * in the memory layout. + */ + magic2 = tswap32(*(uint32_t *)(xstate + xstate_size)); + if (magic2 != TARGET_FP_XSTATE_MAGIC2) { + unlock_user(xstate, fxstate_addr, 0); + break; + } + + ok = cpu_x86_xrstor(env, xstate, xstate_size, xfeatures); + unlock_user(xstate, fxstate_addr, 0); + return ok; + + default: + break; } - cpu_x86_fxrstor(env, fxsave_addr); - return 0; + cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate)); + return true; } -static int -restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) +#ifndef TARGET_X86_64 +static bool frstor_sigcontext(CPUX86State *env, FPStateKind fpkind, + struct target_fregs_state *fpstate, + abi_ptr fpstate_addr, + X86LegacyXSaveArea *fxstate, + abi_ptr fxstate_addr) { - int err = 1; - abi_ulong fpstate_addr; - unsigned int tmpflags; + switch (fpkind) { + case FPSTATE_XSAVE: + if (!xrstor_sigcontext(env, fpkind, fxstate, fxstate_addr)) { + return false; + } + break; + case FPSTATE_FXSAVE: + cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate)); + break; + case FPSTATE_FSAVE: + break; + default: + g_assert_not_reached(); + } + + /* + * Copy the legacy state because the FP portion of the FX frame has + * to be ignored for histerical raisins. The kernel folds the two + * states together and then performs a single load; here we perform + * the merge within ENV by loading XSTATE/FXSTATE first, then + * overriding with the FSTATE afterward. + */ + cpu_x86_frstor(env, fpstate, sizeof(*fpstate)); + return true; +} +#endif + +static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) +{ + abi_ptr fpstate_addr; + unsigned tmpflags, math_size; + FPStateKind fpkind; + void *fpstate; + bool ok; #ifndef TARGET_X86_64 cpu_x86_load_seg(env, R_GS, tswap16(sc->gs)); @@ -613,37 +754,39 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) env->eip = tswapl(sc->rip); #endif - cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); - cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3); + cpu_x86_load_seg(env, R_CS, lduw_le_p(&sc->cs) | 3); + cpu_x86_load_seg(env, R_SS, lduw_le_p(&sc->ss) | 3); tmpflags = tswapl(sc->eflags); env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); - // regs->orig_eax = -1; /* disable syscall checks */ fpstate_addr = tswapl(sc->fpstate); - if (fpstate_addr != 0) { - struct target_fpstate *fpstate; - if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr, - sizeof(struct target_fpstate))) { - return err; - } -#ifndef TARGET_X86_64 - 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 - err = xrstor_sigcontext(env, fpstate, fpstate_addr); -#endif - unlock_user_struct(fpstate, fpstate_addr, 0); - } else { - err = 0; + if (fpstate_addr == 0) { + return true; } - return err; + fpkind = get_fpstate_kind(env); + math_size = get_fpstate_size(env, fpkind); +#ifndef TARGET_X86_64 + if (fpkind != FPSTATE_FSAVE) { + math_size += sizeof(struct target_fregs_state); + } +#endif + fpstate = lock_user(VERIFY_READ, fpstate_addr, math_size, 1); + if (!fpstate) { + return false; + } + +#ifdef TARGET_X86_64 + ok = xrstor_sigcontext(env, fpkind, fpstate, fpstate_addr); +#else + ok = frstor_sigcontext(env, fpkind, fpstate, fpstate_addr, + fpstate + sizeof(struct target_fregs_state), + fpstate_addr + sizeof(struct target_fregs_state)); +#endif + + unlock_user(fpstate, fpstate_addr, 0); + return ok; } /* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ @@ -654,29 +797,27 @@ long do_sigreturn(CPUX86State *env) abi_ulong frame_addr = env->regs[R_ESP] - 8; target_sigset_t target_set; sigset_t set; - int i; trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - /* set blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + force_sig(TARGET_SIGSEGV); + return -QEMU_ESIGRETURN; } + /* Set blocked signals. */ + __get_user(target_set.sig[0], &frame->sc.oldmask); + for (int i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } target_to_host_sigset_internal(&set, &target_set); set_sigmask(&set); - /* restore registers */ - if (restore_sigcontext(env, &frame->sc)) - goto badframe; - unlock_user_struct(frame, frame_addr, 0); - return -QEMU_ESIGRETURN; + /* Restore registers */ + if (!restore_sigcontext(env, &frame->sc)) { + force_sig(TARGET_SIGSEGV); + } -badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); return -QEMU_ESIGRETURN; } #endif @@ -694,7 +835,7 @@ long do_rt_sigreturn(CPUX86State *env) target_to_host_sigset(&set, &frame->uc.tuc_sigmask); set_sigmask(&set); - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + if (!restore_sigcontext(env, &frame->uc.tuc_mcontext)) { goto badframe; } diff --git a/linux-user/i386/syscall_32.tbl b/linux-user/i386/syscall_32.tbl index 4bbc267fb3..534c74b14f 100644 --- a/linux-user/i386/syscall_32.tbl +++ b/linux-user/i386/syscall_32.tbl @@ -1,8 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note # # 32-bit system call numbers and entry vectors # # The format is: -# +# [ [noreturn]] # # The __ia32_sys and __ia32_compat_sys stubs are created on-the-fly for # sys_*() system calls and compat_sys_*() compat system calls if @@ -12,7 +13,7 @@ # The abi is always "i386" for this file. # 0 i386 restart_syscall sys_restart_syscall -1 i386 exit sys_exit +1 i386 exit sys_exit - noreturn 2 i386 fork sys_fork 3 i386 read sys_read 4 i386 write sys_write @@ -145,7 +146,7 @@ 131 i386 quotactl sys_quotactl 132 i386 getpgid sys_getpgid 133 i386 fchdir sys_fchdir -134 i386 bdflush sys_bdflush +134 i386 bdflush sys_ni_syscall 135 i386 sysfs sys_sysfs 136 i386 personality sys_personality 137 i386 afs_syscall @@ -263,8 +264,8 @@ 249 i386 io_cancel sys_io_cancel 250 i386 fadvise64 sys_ia32_fadvise64 # 251 is available for reuse (was briefly sys_set_zone_reclaim) -252 i386 exit_group sys_exit_group -253 i386 lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +252 i386 exit_group sys_exit_group - noreturn +253 i386 lookup_dcookie 254 i386 epoll_create sys_epoll_create 255 i386 epoll_ctl sys_epoll_ctl 256 i386 epoll_wait sys_epoll_wait @@ -286,7 +287,7 @@ 272 i386 fadvise64_64 sys_ia32_fadvise64_64 273 i386 vserver 274 i386 mbind sys_mbind -275 i386 get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy +275 i386 get_mempolicy sys_get_mempolicy 276 i386 set_mempolicy sys_set_mempolicy 277 i386 mq_open sys_mq_open compat_sys_mq_open 278 i386 mq_unlink sys_mq_unlink @@ -328,7 +329,7 @@ 314 i386 sync_file_range sys_ia32_sync_file_range 315 i386 tee sys_tee 316 i386 vmsplice sys_vmsplice -317 i386 move_pages sys_move_pages compat_sys_move_pages +317 i386 move_pages sys_move_pages 318 i386 getcpu sys_getcpu 319 i386 epoll_pwait sys_epoll_pwait 320 i386 utimensat sys_utimensat_time32 @@ -420,7 +421,7 @@ 412 i386 utimensat_time64 sys_utimensat 413 i386 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 i386 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 -416 i386 io_pgetevents_time64 sys_io_pgetevents +416 i386 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 i386 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 i386 mq_timedsend_time64 sys_mq_timedsend 419 i386 mq_timedreceive_time64 sys_mq_timedreceive @@ -447,7 +448,23 @@ 440 i386 process_madvise sys_process_madvise 441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 i386 mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 i386 quotactl_fd sys_quotactl_fd 444 i386 landlock_create_ruleset sys_landlock_create_ruleset 445 i386 landlock_add_rule sys_landlock_add_rule 446 i386 landlock_restrict_self sys_landlock_restrict_self +447 i386 memfd_secret sys_memfd_secret +448 i386 process_mrelease sys_process_mrelease +449 i386 futex_waitv sys_futex_waitv +450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node +451 i386 cachestat sys_cachestat +452 i386 fchmodat2 sys_fchmodat2 +453 i386 map_shadow_stack sys_map_shadow_stack +454 i386 futex_wake sys_futex_wake +455 i386 futex_wait sys_futex_wait +456 i386 futex_requeue sys_futex_requeue +457 i386 statmount sys_statmount +458 i386 listmount sys_listmount +459 i386 lsm_get_self_attr sys_lsm_get_self_attr +460 i386 lsm_set_self_attr sys_lsm_set_self_attr +461 i386 lsm_list_modules sys_lsm_list_modules +462 i386 mseal sys_mseal diff --git a/linux-user/i386/syscallhdr.sh b/linux-user/i386/syscallhdr.sh index b2eca96db7..938a793d2a 100644 --- a/linux-user/i386/syscallhdr.sh +++ b/linux-user/i386/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/i386/target_mman.h b/linux-user/i386/target_mman.h index e7ba6070fe..e3b8e1eaa6 100644 --- a/linux-user/i386/target_mman.h +++ b/linux-user/i386/target_mman.h @@ -1 +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 e6d3ec26dc..0000000000 --- a/linux-user/include/host/s390/host-signal.h +++ /dev/null @@ -1,138 +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 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/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 071f7ca253..3b41128fd7 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -102,6 +102,7 @@ IOCTL(BLKRAGET, IOC_R, MK_PTR(TYPE_LONG)) IOCTL(BLKSSZGET, IOC_R, MK_PTR(TYPE_INT)) IOCTL(BLKBSZGET, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(BLKBSZSET, IOC_W, MK_PTR(TYPE_INT)) IOCTL_SPECIAL(BLKPG, IOC_W, do_ioctl_blkpg, MK_PTR(MK_STRUCT(STRUCT_blkpg_ioctl_arg))) @@ -134,6 +135,15 @@ IOCTL(FICLONE, IOC_W, TYPE_INT) IOCTL(FICLONERANGE, IOC_W, MK_PTR(MK_STRUCT(STRUCT_file_clone_range))) #endif +#ifdef FIFREEZE + IOCTL(FIFREEZE, IOC_W | IOC_R, TYPE_INT) +#endif +#ifdef FITHAW + IOCTL(FITHAW, IOC_W | IOC_R, TYPE_INT) +#endif +#ifdef FITRIM + IOCTL(FITRIM, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_fstrim_range))) +#endif IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG)) #ifdef CONFIG_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..1d760b1e47 --- /dev/null +++ b/linux-user/loongarch64/Makefile.vdso @@ -0,0 +1,12 @@ +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,-z,max-page-size=4096 \ + -Wl,-T,$(SUBDIR)/vdso.ld $< diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index 894fdd111a..73d7b6796a 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -72,6 +72,19 @@ void cpu_loop(CPULoongArchState *env) 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; diff --git a/linux-user/loongarch64/meson.build b/linux-user/loongarch64/meson.build new file mode 100644 index 0000000000..64cb537bf9 --- /dev/null +++ b/linux-user/loongarch64/meson.build @@ -0,0 +1,11 @@ +vdso_inc = gen_vdso.process('vdso.so', + extra_args: ['-r', '__vdso_rt_sigreturn']) + +linux_user_ss.add(when: 'TARGET_LOONGARCH64', if_true: vdso_inc) + + +syscall_nr_generators += { + 'loongarch64': generator(sh, + arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], + output: '@BASENAME@_nr.h') +} diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index 7c7afb652e..1a322f9697 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -10,35 +10,62 @@ #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 { - uint64_t sc_pc; - uint64_t sc_regs[32]; - uint32_t sc_flags; - uint64_t sc_extcontext[0] QEMU_ALIGNED(16); + 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 { - uint64_t regs[32]; - uint64_t fcc; - uint32_t fcsr; + 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 { - uint32_t magic; - uint32_t size; - uint64_t padding; + 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; @@ -53,6 +80,11 @@ struct target_rt_sigframe { 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 @@ -65,9 +97,11 @@ struct ctx_layout { }; struct extctx_layout { - unsigned int size; + unsigned long size; unsigned int flags; struct ctx_layout fpu; + struct ctx_layout lsx; + struct ctx_layout lasx; struct ctx_layout end; }; @@ -89,7 +123,8 @@ static abi_ptr extframe_alloc(struct extctx_layout *extctx, return sp; } -static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) +static abi_ptr setup_extcontext(CPULoongArchState *env, + struct extctx_layout *extctx, abi_ptr sp) { memset(extctx, 0, sizeof(struct extctx_layout)); @@ -98,8 +133,17 @@ static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) /* For qemu, there is no lazy fp context switch, so fp always present. */ extctx->flags = SC_USED_FP; - sp = extframe_alloc(extctx, &extctx->fpu, - sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp); + + 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; } @@ -109,7 +153,6 @@ static void setup_sigframe(CPULoongArchState *env, struct extctx_layout *extctx) { struct target_sctx_info *info; - struct target_fpu_context *fpu_ctx; int i; __put_user(extctx->flags, &sc->sc_flags); @@ -120,25 +163,63 @@ static void setup_sigframe(CPULoongArchState *env, } /* - * Set fpu context + * Set extension context */ - 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], &fpu_ctx->regs[i]); + 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); } - __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(extctx->end.size, &info->size); + __put_user(0, &info->size); } static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) @@ -146,7 +227,7 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) memset(extctx, 0, sizeof(*extctx)); while (1) { - uint32_t magic, size; + abi_uint magic, size; if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) { return false; @@ -168,6 +249,24 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr 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; } @@ -181,19 +280,45 @@ static void restore_sigframe(CPULoongArchState *env, 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->fpu.haddr) { - struct target_fpu_context *fpu_ctx = - extctx->fpu.haddr + sizeof(struct target_sctx_info); - uint64_t fcc; + 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], &fpu_ctx->regs[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); @@ -213,7 +338,7 @@ static abi_ptr get_sigframe(struct target_sigaction *ka, sp = target_sigsp(get_sp_from_cpustate(env), ka); sp = ROUND_DOWN(sp, 16); - sp = setup_extcontext(extctx, sp); + sp = setup_extcontext(env, extctx, sp); sp -= sizeof(struct target_rt_sigframe); assert(QEMU_IS_ALIGNED(sp, 16)); @@ -239,10 +364,19 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, force_sigsegv(sig); return; } - extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); - extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); - tswap_siginfo(&frame->rs_info, info); + 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); @@ -283,7 +417,12 @@ long do_rt_sigreturn(CPULoongArchState *env) if (!frame) { goto badframe; } - if (extctx.fpu.gaddr) { + + 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); } diff --git a/linux-user/loongarch64/syscall.tbl b/linux-user/loongarch64/syscall.tbl new file mode 100644 index 0000000000..845e24eb37 --- /dev/null +++ b/linux-user/loongarch64/syscall.tbl @@ -0,0 +1,405 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# This file contains the system call numbers for all of the +# more recently added architectures. +# +# As a basic principle, no duplication of functionality +# should be added, e.g. we don't use lseek when llseek +# is present. New architectures should use this file +# and implement the less feature-full calls in user space. +# +0 common io_setup sys_io_setup compat_sys_io_setup +1 common io_destroy sys_io_destroy +2 common io_submit sys_io_submit compat_sys_io_submit +3 common io_cancel sys_io_cancel +4 time32 io_getevents sys_io_getevents_time32 +4 64 io_getevents sys_io_getevents +5 common setxattr sys_setxattr +6 common lsetxattr sys_lsetxattr +7 common fsetxattr sys_fsetxattr +8 common getxattr sys_getxattr +9 common lgetxattr sys_lgetxattr +10 common fgetxattr sys_fgetxattr +11 common listxattr sys_listxattr +12 common llistxattr sys_llistxattr +13 common flistxattr sys_flistxattr +14 common removexattr sys_removexattr +15 common lremovexattr sys_lremovexattr +16 common fremovexattr sys_fremovexattr +17 common getcwd sys_getcwd +18 common lookup_dcookie sys_ni_syscall +19 common eventfd2 sys_eventfd2 +20 common epoll_create1 sys_epoll_create1 +21 common epoll_ctl sys_epoll_ctl +22 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +23 common dup sys_dup +24 common dup3 sys_dup3 +25 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +25 64 fcntl sys_fcntl +26 common inotify_init1 sys_inotify_init1 +27 common inotify_add_watch sys_inotify_add_watch +28 common inotify_rm_watch sys_inotify_rm_watch +29 common ioctl sys_ioctl compat_sys_ioctl +30 common ioprio_set sys_ioprio_set +31 common ioprio_get sys_ioprio_get +32 common flock sys_flock +33 common mknodat sys_mknodat +34 common mkdirat sys_mkdirat +35 common unlinkat sys_unlinkat +36 common symlinkat sys_symlinkat +37 common linkat sys_linkat +# renameat is superseded with flags by renameat2 +38 renameat renameat sys_renameat +39 common umount2 sys_umount +40 common mount sys_mount +41 common pivot_root sys_pivot_root +42 common nfsservctl sys_ni_syscall +43 32 statfs64 sys_statfs64 compat_sys_statfs64 +43 64 statfs sys_statfs +44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +44 64 fstatfs sys_fstatfs +45 32 truncate64 sys_truncate64 compat_sys_truncate64 +45 64 truncate sys_truncate +46 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +46 64 ftruncate sys_ftruncate +47 common fallocate sys_fallocate compat_sys_fallocate +48 common faccessat sys_faccessat +49 common chdir sys_chdir +50 common fchdir sys_fchdir +51 common chroot sys_chroot +52 common fchmod sys_fchmod +53 common fchmodat sys_fchmodat +54 common fchownat sys_fchownat +55 common fchown sys_fchown +56 common openat sys_openat +57 common close sys_close +58 common vhangup sys_vhangup +59 common pipe2 sys_pipe2 +60 common quotactl sys_quotactl +61 common getdents64 sys_getdents64 +62 32 llseek sys_llseek +62 64 lseek sys_lseek +63 common read sys_read +64 common write sys_write +65 common readv sys_readv sys_readv +66 common writev sys_writev sys_writev +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 common preadv sys_preadv compat_sys_preadv +70 common pwritev sys_pwritev compat_sys_pwritev +71 32 sendfile64 sys_sendfile64 +71 64 sendfile sys_sendfile64 +72 time32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +72 64 pselect6 sys_pselect6 +73 time32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +73 64 ppoll sys_ppoll +74 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +75 common vmsplice sys_vmsplice +76 common splice sys_splice +77 common tee sys_tee +78 common readlinkat sys_readlinkat +79 stat64 fstatat64 sys_fstatat64 +79 64 newfstatat sys_newfstatat +80 stat64 fstat64 sys_fstat64 +80 64 fstat sys_newfstat +81 common sync sys_sync +82 common fsync sys_fsync +83 common fdatasync sys_fdatasync +84 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +85 common timerfd_create sys_timerfd_create +86 time32 timerfd_settime sys_timerfd_settime32 +86 64 timerfd_settime sys_timerfd_settime +87 time32 timerfd_gettime sys_timerfd_gettime32 +87 64 timerfd_gettime sys_timerfd_gettime +88 time32 utimensat sys_utimensat_time32 +88 64 utimensat sys_utimensat +89 common acct sys_acct +90 common capget sys_capget +91 common capset sys_capset +92 common personality sys_personality +93 common exit sys_exit +94 common exit_group sys_exit_group +95 common waitid sys_waitid compat_sys_waitid +96 common set_tid_address sys_set_tid_address +97 common unshare sys_unshare +98 time32 futex sys_futex_time32 +98 64 futex sys_futex +99 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +100 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +101 time32 nanosleep sys_nanosleep_time32 +101 64 nanosleep sys_nanosleep +102 common getitimer sys_getitimer compat_sys_getitimer +103 common setitimer sys_setitimer compat_sys_setitimer +104 common kexec_load sys_kexec_load compat_sys_kexec_load +105 common init_module sys_init_module +106 common delete_module sys_delete_module +107 common timer_create sys_timer_create compat_sys_timer_create +108 time32 timer_gettime sys_timer_gettime32 +108 64 timer_gettime sys_timer_gettime +109 common timer_getoverrun sys_timer_getoverrun +110 time32 timer_settime sys_timer_settime32 +110 64 timer_settime sys_timer_settime +111 common timer_delete sys_timer_delete +112 time32 clock_settime sys_clock_settime32 +112 64 clock_settime sys_clock_settime +113 time32 clock_gettime sys_clock_gettime32 +113 64 clock_gettime sys_clock_gettime +114 time32 clock_getres sys_clock_getres_time32 +114 64 clock_getres sys_clock_getres +115 time32 clock_nanosleep sys_clock_nanosleep_time32 +115 64 clock_nanosleep sys_clock_nanosleep +116 common syslog sys_syslog +117 common ptrace sys_ptrace compat_sys_ptrace +118 common sched_setparam sys_sched_setparam +119 common sched_setscheduler sys_sched_setscheduler +120 common sched_getscheduler sys_sched_getscheduler +121 common sched_getparam sys_sched_getparam +122 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +123 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +124 common sched_yield sys_sched_yield +125 common sched_get_priority_max sys_sched_get_priority_max +126 common sched_get_priority_min sys_sched_get_priority_min +127 time32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +127 64 sched_rr_get_interval sys_sched_rr_get_interval +128 common restart_syscall sys_restart_syscall +129 common kill sys_kill +130 common tkill sys_tkill +131 common tgkill sys_tgkill +132 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +133 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +134 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +135 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +136 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +137 time32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +137 64 rt_sigtimedwait sys_rt_sigtimedwait +138 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +139 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +140 common setpriority sys_setpriority +141 common getpriority sys_getpriority +142 common reboot sys_reboot +143 common setregid sys_setregid +144 common setgid sys_setgid +145 common setreuid sys_setreuid +146 common setuid sys_setuid +147 common setresuid sys_setresuid +148 common getresuid sys_getresuid +149 common setresgid sys_setresgid +150 common getresgid sys_getresgid +151 common setfsuid sys_setfsuid +152 common setfsgid sys_setfsgid +153 common times sys_times compat_sys_times +154 common setpgid sys_setpgid +155 common getpgid sys_getpgid +156 common getsid sys_getsid +157 common setsid sys_setsid +158 common getgroups sys_getgroups +159 common setgroups sys_setgroups +160 common uname sys_newuname +161 common sethostname sys_sethostname +162 common setdomainname sys_setdomainname +# getrlimit and setrlimit are superseded with prlimit64 +163 rlimit getrlimit sys_getrlimit compat_sys_getrlimit +164 rlimit setrlimit sys_setrlimit compat_sys_setrlimit +165 common getrusage sys_getrusage compat_sys_getrusage +166 common umask sys_umask +167 common prctl sys_prctl +168 common getcpu sys_getcpu +169 time32 gettimeofday sys_gettimeofday compat_sys_gettimeofday +169 64 gettimeofday sys_gettimeofday +170 time32 settimeofday sys_settimeofday compat_sys_settimeofday +170 64 settimeofday sys_settimeofday +171 time32 adjtimex sys_adjtimex_time32 +171 64 adjtimex sys_adjtimex +172 common getpid sys_getpid +173 common getppid sys_getppid +174 common getuid sys_getuid +175 common geteuid sys_geteuid +176 common getgid sys_getgid +177 common getegid sys_getegid +178 common gettid sys_gettid +179 common sysinfo sys_sysinfo compat_sys_sysinfo +180 common mq_open sys_mq_open compat_sys_mq_open +181 common mq_unlink sys_mq_unlink +182 time32 mq_timedsend sys_mq_timedsend_time32 +182 64 mq_timedsend sys_mq_timedsend +183 time32 mq_timedreceive sys_mq_timedreceive_time32 +183 64 mq_timedreceive sys_mq_timedreceive +184 common mq_notify sys_mq_notify compat_sys_mq_notify +185 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +186 common msgget sys_msgget +187 common msgctl sys_msgctl compat_sys_msgctl +188 common msgrcv sys_msgrcv compat_sys_msgrcv +189 common msgsnd sys_msgsnd compat_sys_msgsnd +190 common semget sys_semget +191 common semctl sys_semctl compat_sys_semctl +192 time32 semtimedop sys_semtimedop_time32 +192 64 semtimedop sys_semtimedop +193 common semop sys_semop +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +196 common shmat sys_shmat compat_sys_shmat +197 common shmdt sys_shmdt +198 common socket sys_socket +199 common socketpair sys_socketpair +200 common bind sys_bind +201 common listen sys_listen +202 common accept sys_accept +203 common connect sys_connect +204 common getsockname sys_getsockname +205 common getpeername sys_getpeername +206 common sendto sys_sendto +207 common recvfrom sys_recvfrom compat_sys_recvfrom +208 common setsockopt sys_setsockopt sys_setsockopt +209 common getsockopt sys_getsockopt sys_getsockopt +210 common shutdown sys_shutdown +211 common sendmsg sys_sendmsg compat_sys_sendmsg +212 common recvmsg sys_recvmsg compat_sys_recvmsg +213 common readahead sys_readahead compat_sys_readahead +214 common brk sys_brk +215 common munmap sys_munmap +216 common mremap sys_mremap +217 common add_key sys_add_key +218 common request_key sys_request_key +219 common keyctl sys_keyctl compat_sys_keyctl +220 common clone sys_clone +221 common execve sys_execve compat_sys_execve +222 32 mmap2 sys_mmap2 +222 64 mmap sys_mmap +223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +223 64 fadvise64 sys_fadvise64_64 +224 common swapon sys_swapon +225 common swapoff sys_swapoff +226 common mprotect sys_mprotect +227 common msync sys_msync +228 common mlock sys_mlock +229 common munlock sys_munlock +230 common mlockall sys_mlockall +231 common munlockall sys_munlockall +232 common mincore sys_mincore +233 common madvise sys_madvise +234 common remap_file_pages sys_remap_file_pages +235 common mbind sys_mbind +236 common get_mempolicy sys_get_mempolicy +237 common set_mempolicy sys_set_mempolicy +238 common migrate_pages sys_migrate_pages +239 common move_pages sys_move_pages +240 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +241 common perf_event_open sys_perf_event_open +242 common accept4 sys_accept4 +243 time32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +243 64 recvmmsg sys_recvmmsg +# Architectures may provide up to 16 syscalls of their own between 244 and 259 +244 arc cacheflush sys_cacheflush +245 arc arc_settls sys_arc_settls +246 arc arc_gettls sys_arc_gettls +247 arc sysfs sys_sysfs +248 arc arc_usr_cmpxchg sys_arc_usr_cmpxchg + +244 csky set_thread_area sys_set_thread_area +245 csky cacheflush sys_cacheflush + +244 nios2 cacheflush sys_cacheflush + +244 or1k or1k_atomic sys_or1k_atomic + +258 riscv riscv_hwprobe sys_riscv_hwprobe +259 riscv riscv_flush_icache sys_riscv_flush_icache + +260 time32 wait4 sys_wait4 compat_sys_wait4 +260 64 wait4 sys_wait4 +261 common prlimit64 sys_prlimit64 +262 common fanotify_init sys_fanotify_init +263 common fanotify_mark sys_fanotify_mark +264 common name_to_handle_at sys_name_to_handle_at +265 common open_by_handle_at sys_open_by_handle_at +266 time32 clock_adjtime sys_clock_adjtime32 +266 64 clock_adjtime sys_clock_adjtime +267 common syncfs sys_syncfs +268 common setns sys_setns +269 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +270 common process_vm_readv sys_process_vm_readv +271 common process_vm_writev sys_process_vm_writev +272 common kcmp sys_kcmp +273 common finit_module sys_finit_module +274 common sched_setattr sys_sched_setattr +275 common sched_getattr sys_sched_getattr +276 common renameat2 sys_renameat2 +277 common seccomp sys_seccomp +278 common getrandom sys_getrandom +279 common memfd_create sys_memfd_create +280 common bpf sys_bpf +281 common execveat sys_execveat compat_sys_execveat +282 common userfaultfd sys_userfaultfd +283 common membarrier sys_membarrier +284 common mlock2 sys_mlock2 +285 common copy_file_range sys_copy_file_range +286 common preadv2 sys_preadv2 compat_sys_preadv2 +287 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +288 common pkey_mprotect sys_pkey_mprotect +289 common pkey_alloc sys_pkey_alloc +290 common pkey_free sys_pkey_free +291 common statx sys_statx +292 time32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +292 64 io_pgetevents sys_io_pgetevents +293 common rseq sys_rseq +294 common kexec_file_load sys_kexec_file_load +# 295 through 402 are unassigned to sync up with generic numbers don't use +403 32 clock_gettime64 sys_clock_gettime +404 32 clock_settime64 sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime +409 32 timer_settime64 sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +447 memfd_secret memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/loongarch64/syscall_nr.h b/linux-user/loongarch64/syscall_nr.h deleted file mode 100644 index be00915adf..0000000000 --- a/linux-user/loongarch64/syscall_nr.h +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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/syscallhdr.sh b/linux-user/loongarch64/syscallhdr.sh new file mode 100644 index 0000000000..3d8a993b42 --- /dev/null +++ b/linux-user/loongarch64/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_LOONGARCH64_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/loongarch64/target_mman.h b/linux-user/loongarch64/target_mman.h index e7ba6070fe..8c2a3d5596 100644 --- a/linux-user/loongarch64/target_mman.h +++ b/linux-user/loongarch64/target_mman.h @@ -1 +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_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_syscall.h b/linux-user/loongarch64/target_syscall.h index 8b5de52124..39f229bb9c 100644 --- a/linux-user/loongarch64/target_syscall.h +++ b/linux-user/loongarch64/target_syscall.h @@ -38,11 +38,4 @@ struct target_pt_regs { #define TARGET_MCL_FUTURE 2 #define TARGET_MCL_ONFAULT 4 -#define TARGET_FORCE_SHMLBA - -static inline abi_ulong target_shmlba(CPULoongArchState *env) -{ - return 64 * KiB; -} - #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..7c2de6c50e 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 caead1cb74..f79b8e4ab0 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -95,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/syscall.tbl b/linux-user/m68k/syscall.tbl index 79c2d24c89..b6094f8933 100644 --- a/linux-user/m68k/syscall.tbl +++ b/linux-user/m68k/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for m68k # @@ -141,7 +141,7 @@ 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir -134 common bdflush sys_bdflush +134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality # 137 was afs_syscall @@ -255,7 +255,7 @@ 245 common io_cancel sys_io_cancel 246 common fadvise64 sys_fadvise64 247 common exit_group sys_exit_group -248 common lookup_dcookie sys_lookup_dcookie +248 common lookup_dcookie sys_ni_syscall 249 common epoll_create sys_epoll_create 250 common epoll_ctl sys_epoll_ctl 251 common epoll_wait sys_epoll_wait @@ -442,7 +442,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/m68k/syscallhdr.sh b/linux-user/m68k/syscallhdr.sh index eeb4d01d34..39b11dd05e 100644 --- a/linux-user/m68k/syscallhdr.sh +++ b/linux-user/m68k/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" 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 index e7ba6070fe..20cfe750c5 100644 --- a/linux-user/m68k/target_mman.h +++ b/linux-user/m68k/target_mman.h @@ -1 +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 a17fed045b..b09af8d436 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -38,9 +38,11 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/plugin.h" +#include "user/guest-base.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 +55,8 @@ #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" @@ -64,8 +68,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; @@ -106,11 +111,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 @@ -143,10 +146,13 @@ void fork_start(void) 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) { @@ -155,18 +161,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; @@ -226,7 +235,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 */ @@ -234,6 +243,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 @@ -321,11 +338,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); } } @@ -348,10 +365,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); } } @@ -366,7 +380,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': @@ -380,10 +396,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); } @@ -392,11 +408,20 @@ 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 const char *rtsig_map = CONFIG_QEMU_RTSIG_MAP; + +static void handle_arg_rtsig_map(const char *arg) { - singlestep = 1; + rtsig_map = arg; +} + +static void handle_arg_one_insn_per_tb(const char *arg) +{ + opt_one_insn_per_tb = true; } static void handle_arg_strace(const char *arg) @@ -423,6 +448,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 @@ -466,6 +501,9 @@ static const struct qemu_argument arg_table[] = { "address", "set guest_base address to 'address'"}, {"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va, "size", "reserve 'size' bytes for guest virtual address space"}, + {"t", "QEMU_RTSIG_MAP", true, handle_arg_rtsig_map, + "tsig hsig n[,...]", + "map target rt signals [tsig,tsig+n) to [hsig,hsig+n]"}, {"d", "QEMU_LOG", true, handle_arg_log, "item[,...]", "enable logging of specified items " "(use '-d help' for a list of items)"}, @@ -474,9 +512,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, @@ -493,6 +532,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} }; @@ -653,6 +696,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; @@ -663,8 +707,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); } @@ -713,8 +765,9 @@ int main(int argc, char **argv, char **envp) /* * Manage binfmt-misc open-binary flag */ + errno = 0; execfd = qemu_getauxval(AT_EXECFD); - if (execfd == 0) { + if (errno != 0) { execfd = open(exec_path, O_RDONLY); if (execfd < 0) { printf("Error while loading %s: %s\n", exec_path, strerror(errno)); @@ -722,6 +775,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 */ @@ -741,38 +799,98 @@ 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; /* - * Reserving too much vm space via mmap can run into problems - * with rlimits, oom due to page table creation, etc. We will - * still try it, if directed by the command-line option, but - * not by default. + * Reserving too much vm space via mmap can run into problems with rlimits, + * oom due to page table creation, etc. We will still try it, if directed + * by the command-line option, but not by default. Unless we're running a + * target address space of 32 or fewer bits on a host with 64 bits. */ 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" +#pragma GCC diagnostic ignored "-Wtautological-compare" + + /* + * 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) { @@ -800,7 +918,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); } @@ -813,7 +931,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); @@ -823,11 +941,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 @@ -872,8 +986,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", @@ -900,12 +1012,12 @@ int main(int argc, char **argv, char **envp) target_set_brk(info->brk); syscall_init(); - signal_init(); + signal_init(rtsig_map); /* 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); @@ -915,7 +1027,7 @@ 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 diff --git a/linux-user/meson.build b/linux-user/meson.build index de4320af05..f75b4fe0e3 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -22,23 +22,33 @@ 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('hexagon') subdir('hppa') subdir('i386') +subdir('loongarch64') subdir('m68k') subdir('microblaze') subdir('mips64') subdir('mips') +subdir('openrisc') 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/syscall.tbl b/linux-user/microblaze/syscall.tbl index b11395a20c..e3b643870e 100644 --- a/linux-user/microblaze/syscall.tbl +++ b/linux-user/microblaze/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for microblaze # @@ -141,7 +141,7 @@ 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir -134 common bdflush sys_bdflush +134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality 137 common afs_syscall sys_ni_syscall @@ -260,7 +260,7 @@ 250 common fadvise64 sys_fadvise64 # 251 is available for reuse (was briefly sys_set_zone_reclaim) 252 common exit_group sys_exit_group -253 common lookup_dcookie sys_lookup_dcookie +253 common lookup_dcookie sys_ni_syscall 254 common epoll_create sys_epoll_create 255 common epoll_ctl sys_epoll_ctl 256 common epoll_wait sys_epoll_wait @@ -448,7 +448,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/microblaze/syscallhdr.sh b/linux-user/microblaze/syscallhdr.sh index f55dce8a62..b42b669154 100644 --- a/linux-user/microblaze/syscallhdr.sh +++ b/linux-user/microblaze/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" 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 index e7ba6070fe..6b3dd54f89 100644 --- a/linux-user/microblaze/target_mman.h +++ b/linux-user/microblaze/target_mman.h @@ -1 +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 8735e58bad..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; 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/syscall-args-o32.c.inc b/linux-user/mips/syscall-args-o32.c.inc index a6a2c5c566..780c0a8a49 100644 --- a/linux-user/mips/syscall-args-o32.c.inc +++ b/linux-user/mips/syscall-args-o32.c.inc @@ -441,3 +441,23 @@ [ 440] = 5, /* process_madvise */ [ 441] = 6, /* epoll_pwait2 */ [ 442] = 5, /* mount_setattr */ + [ 443] = 4, /* quotactl_fd */ + [ 444] = 3, /* landlock_create_ruleset */ + [ 445] = 4, /* landlock_add_rule */ + [ 446] = 2, /* landlock_restrict_self */ + [ 447] = 1, /* memfd_secret */ + [ 448] = 2, /* process_mrelease */ + [ 449] = 5, /* futex_waitv */ + [ 450] = 4, /* set_mempolicy_home_node */ + [ 451] = 4, /* cachestat */ + [ 452] = 4, /* fchmodat2 */ + [ 453] = 3, /* map_shadow_stack */ + [ 454] = 4, /* futex_wake */ + [ 455] = 6, /* futex_wait */ + [ 456] = 4, /* futex_requeue */ + [ 457] = 4, /* statmount */ + [ 458] = 4, /* listmount */ + [ 459] = 4, /* lsm_get_self_attr */ + [ 460] = 4, /* lsm_set_self_attr */ + [ 461] = 3, /* lsm_list_modules */ + [ 462] = 3, /* mseal */ diff --git a/linux-user/mips/syscall_o32.tbl b/linux-user/mips/syscall_o32.tbl index d560c467a8..360055c626 100644 --- a/linux-user/mips/syscall_o32.tbl +++ b/linux-user/mips/syscall_o32.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for mips # @@ -27,7 +27,7 @@ 17 o32 break sys_ni_syscall # 18 was sys_stat 18 o32 unused18 sys_ni_syscall -19 o32 lseek sys_lseek +19 o32 lseek sys_lseek compat_sys_lseek 20 o32 getpid sys_getpid 21 o32 mount sys_mount 22 o32 umount sys_oldumount @@ -145,7 +145,7 @@ 131 o32 quotactl sys_quotactl 132 o32 getpgid sys_getpgid 133 o32 fchdir sys_fchdir -134 o32 bdflush sys_bdflush +134 o32 bdflush sys_ni_syscall 135 o32 sysfs sys_sysfs 136 o32 personality sys_personality sys_32_personality 137 o32 afs_syscall sys_ni_syscall @@ -258,7 +258,7 @@ 244 o32 io_submit sys_io_submit compat_sys_io_submit 245 o32 io_cancel sys_io_cancel 246 o32 exit_group sys_exit_group -247 o32 lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +247 o32 lookup_dcookie sys_ni_syscall 248 o32 epoll_create sys_epoll_create 249 o32 epoll_ctl sys_epoll_ctl 250 o32 epoll_wait sys_epoll_wait @@ -279,9 +279,9 @@ 265 o32 clock_nanosleep sys_clock_nanosleep_time32 266 o32 tgkill sys_tgkill 267 o32 utimes sys_utimes_time32 -268 o32 mbind sys_mbind compat_sys_mbind -269 o32 get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy -270 o32 set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +268 o32 mbind sys_mbind +269 o32 get_mempolicy sys_get_mempolicy +270 o32 set_mempolicy sys_set_mempolicy 271 o32 mq_open sys_mq_open compat_sys_mq_open 272 o32 mq_unlink sys_mq_unlink 273 o32 mq_timedsend sys_mq_timedsend_time32 @@ -298,7 +298,7 @@ 284 o32 inotify_init sys_inotify_init 285 o32 inotify_add_watch sys_inotify_add_watch 286 o32 inotify_rm_watch sys_inotify_rm_watch -287 o32 migrate_pages sys_migrate_pages compat_sys_migrate_pages +287 o32 migrate_pages sys_migrate_pages 288 o32 openat sys_openat compat_sys_openat 289 o32 mkdirat sys_mkdirat 290 o32 mknodat sys_mknodat @@ -319,7 +319,7 @@ 305 o32 sync_file_range sys_sync_file_range sys32_sync_file_range 306 o32 tee sys_tee 307 o32 vmsplice sys_vmsplice -308 o32 move_pages sys_move_pages compat_sys_move_pages +308 o32 move_pages sys_move_pages 309 o32 set_robust_list sys_set_robust_list compat_sys_set_robust_list 310 o32 get_robust_list sys_get_robust_list compat_sys_get_robust_list 311 o32 kexec_load sys_kexec_load compat_sys_kexec_load @@ -403,7 +403,7 @@ 412 o32 utimensat_time64 sys_utimensat sys_utimensat 413 o32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 o32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 -416 o32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +416 o32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 o32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 o32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 o32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive @@ -430,7 +430,23 @@ 440 o32 process_madvise sys_process_madvise 441 o32 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 o32 mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 o32 quotactl_fd sys_quotactl_fd 444 o32 landlock_create_ruleset sys_landlock_create_ruleset 445 o32 landlock_add_rule sys_landlock_add_rule 446 o32 landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 o32 process_mrelease sys_process_mrelease +449 o32 futex_waitv sys_futex_waitv +450 o32 set_mempolicy_home_node sys_set_mempolicy_home_node +451 o32 cachestat sys_cachestat +452 o32 fchmodat2 sys_fchmodat2 +453 o32 map_shadow_stack sys_map_shadow_stack +454 o32 futex_wake sys_futex_wake +455 o32 futex_wait sys_futex_wait +456 o32 futex_requeue sys_futex_requeue +457 o32 statmount sys_statmount +458 o32 listmount sys_listmount +459 o32 lsm_get_self_attr sys_lsm_get_self_attr +460 o32 lsm_set_self_attr sys_lsm_set_self_attr +461 o32 lsm_list_modules sys_lsm_list_modules +462 o32 mseal sys_mseal diff --git a/linux-user/mips/syscallhdr.sh b/linux-user/mips/syscallhdr.sh index 761e3e47dd..cd7043ef5a 100644 --- a/linux-user/mips/syscallhdr.sh +++ b/linux-user/mips/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index a98c9bd6ad..71a32315a8 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -12,8 +12,8 @@ static inline const char *cpu_get_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } - if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { - return "R5900"; + if (eflags & EF_MIPS_NAN2008) { + return "P5600"; } return "24Kf"; } diff --git a/linux-user/mips/target_mman.h b/linux-user/mips/target_mman.h index e7ba6070fe..b84fe1e8a8 100644 --- a/linux-user/mips/target_mman.h +++ b/linux-user/mips/target_mman.h @@ -1 +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/syscall_n32.tbl b/linux-user/mips64/syscall_n32.tbl index 9220909526..793eca6635 100644 --- a/linux-user/mips64/syscall_n32.tbl +++ b/linux-user/mips64/syscall_n32.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for mips # @@ -214,7 +214,7 @@ 203 n32 io_submit compat_sys_io_submit 204 n32 io_cancel sys_io_cancel 205 n32 exit_group sys_exit_group -206 n32 lookup_dcookie sys_lookup_dcookie +206 n32 lookup_dcookie sys_ni_syscall 207 n32 epoll_create sys_epoll_create 208 n32 epoll_ctl sys_epoll_ctl 209 n32 epoll_wait sys_epoll_wait @@ -239,9 +239,9 @@ 228 n32 clock_nanosleep sys_clock_nanosleep_time32 229 n32 tgkill sys_tgkill 230 n32 utimes sys_utimes_time32 -231 n32 mbind compat_sys_mbind -232 n32 get_mempolicy compat_sys_get_mempolicy -233 n32 set_mempolicy compat_sys_set_mempolicy +231 n32 mbind sys_mbind +232 n32 get_mempolicy sys_get_mempolicy +233 n32 set_mempolicy sys_set_mempolicy 234 n32 mq_open compat_sys_mq_open 235 n32 mq_unlink sys_mq_unlink 236 n32 mq_timedsend sys_mq_timedsend_time32 @@ -258,7 +258,7 @@ 247 n32 inotify_init sys_inotify_init 248 n32 inotify_add_watch sys_inotify_add_watch 249 n32 inotify_rm_watch sys_inotify_rm_watch -250 n32 migrate_pages compat_sys_migrate_pages +250 n32 migrate_pages sys_migrate_pages 251 n32 openat sys_openat 252 n32 mkdirat sys_mkdirat 253 n32 mknodat sys_mknodat @@ -279,7 +279,7 @@ 268 n32 sync_file_range sys_sync_file_range 269 n32 tee sys_tee 270 n32 vmsplice sys_vmsplice -271 n32 move_pages compat_sys_move_pages +271 n32 move_pages sys_move_pages 272 n32 set_robust_list compat_sys_set_robust_list 273 n32 get_robust_list compat_sys_get_robust_list 274 n32 kexec_load compat_sys_kexec_load @@ -354,7 +354,7 @@ 412 n32 utimensat_time64 sys_utimensat 413 n32 pselect6_time64 compat_sys_pselect6_time64 414 n32 ppoll_time64 compat_sys_ppoll_time64 -416 n32 io_pgetevents_time64 sys_io_pgetevents +416 n32 io_pgetevents_time64 compat_sys_io_pgetevents_time64 417 n32 recvmmsg_time64 compat_sys_recvmmsg_time64 418 n32 mq_timedsend_time64 sys_mq_timedsend 419 n32 mq_timedreceive_time64 sys_mq_timedreceive @@ -381,7 +381,23 @@ 440 n32 process_madvise sys_process_madvise 441 n32 epoll_pwait2 compat_sys_epoll_pwait2 442 n32 mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 n32 quotactl_fd sys_quotactl_fd 444 n32 landlock_create_ruleset sys_landlock_create_ruleset 445 n32 landlock_add_rule sys_landlock_add_rule 446 n32 landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 n32 process_mrelease sys_process_mrelease +449 n32 futex_waitv sys_futex_waitv +450 n32 set_mempolicy_home_node sys_set_mempolicy_home_node +451 n32 cachestat sys_cachestat +452 n32 fchmodat2 sys_fchmodat2 +453 n32 map_shadow_stack sys_map_shadow_stack +454 n32 futex_wake sys_futex_wake +455 n32 futex_wait sys_futex_wait +456 n32 futex_requeue sys_futex_requeue +457 n32 statmount sys_statmount +458 n32 listmount sys_listmount +459 n32 lsm_get_self_attr sys_lsm_get_self_attr +460 n32 lsm_set_self_attr sys_lsm_set_self_attr +461 n32 lsm_list_modules sys_lsm_list_modules +462 n32 mseal sys_mseal diff --git a/linux-user/mips64/syscall_n64.tbl b/linux-user/mips64/syscall_n64.tbl index 9cd1c34f31..ebff531acc 100644 --- a/linux-user/mips64/syscall_n64.tbl +++ b/linux-user/mips64/syscall_n64.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for mips # @@ -214,7 +214,7 @@ 203 n64 io_submit sys_io_submit 204 n64 io_cancel sys_io_cancel 205 n64 exit_group sys_exit_group -206 n64 lookup_dcookie sys_lookup_dcookie +206 n64 lookup_dcookie sys_ni_syscall 207 n64 epoll_create sys_epoll_create 208 n64 epoll_ctl sys_epoll_ctl 209 n64 epoll_wait sys_epoll_wait @@ -357,7 +357,23 @@ 440 n64 process_madvise sys_process_madvise 441 n64 epoll_pwait2 sys_epoll_pwait2 442 n64 mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 n64 quotactl_fd sys_quotactl_fd 444 n64 landlock_create_ruleset sys_landlock_create_ruleset 445 n64 landlock_add_rule sys_landlock_add_rule 446 n64 landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 n64 process_mrelease sys_process_mrelease +449 n64 futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 n64 cachestat sys_cachestat +452 n64 fchmodat2 sys_fchmodat2 +453 n64 map_shadow_stack sys_map_shadow_stack +454 n64 futex_wake sys_futex_wake +455 n64 futex_wait sys_futex_wait +456 n64 futex_requeue sys_futex_requeue +457 n64 statmount sys_statmount +458 n64 listmount sys_listmount +459 n64 lsm_get_self_attr sys_lsm_get_self_attr +460 n64 lsm_set_self_attr sys_lsm_set_self_attr +461 n64 lsm_list_modules sys_lsm_list_modules +462 n64 mseal sys_mseal diff --git a/linux-user/mips64/syscallhdr.sh b/linux-user/mips64/syscallhdr.sh index ed5a45165a..a4339b2041 100644 --- a/linux-user/mips64/syscallhdr.sh +++ b/linux-user/mips64/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 5f2f2df29f..502af9d278 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -9,11 +9,27 @@ #define MIPS64_TARGET_ELF_H static inline const char *cpu_get_model(uint32_t eflags) { - if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_64R6) { - return "I6400"; + switch (eflags & EF_MIPS_MACH) { + case EF_MIPS_MACH_OCTEON: + case EF_MIPS_MACH_OCTEON2: + case EF_MIPS_MACH_OCTEON3: + return "Octeon68XX"; + case EF_MIPS_MACH_LS2E: + return "Loongson-2E"; + case EF_MIPS_MACH_LS2F: + return "Loongson-2F"; + case EF_MIPS_MACH_LS3A: + return "Loongson-3A1000"; + default: + break; } - if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { - return "R5900"; + switch (eflags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_64R6: + return "I6400"; + case EF_MIPS_ARCH_64R2: + return "MIPS64R2-generic"; + default: + break; } return "5KEf"; } diff --git a/linux-user/mips64/target_mman.h b/linux-user/mips64/target_mman.h index e7ba6070fe..7bdc47d902 100644 --- a/linux-user/mips64/target_mman.h +++ b/linux-user/mips64/target_mman.h @@ -1 +1 @@ -#include "../generic/target_mman.h" +#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 10f5079331..e4bf5d5f39 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -17,12 +17,19 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include #include "trace.h" #include "exec/log.h" +#include "exec/page-protection.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; @@ -36,6 +43,7 @@ void mmap_lock(void) void mmap_unlock(void) { + assert(mmap_lock_count > 0); if (--mmap_lock_count == 0) { pthread_mutex_unlock(&mmap_mutex); } @@ -56,10 +64,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); + } + } } /* @@ -68,23 +115,10 @@ 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); + int page_flags = (prot & PAGE_RWX) | PAGE_VALID; #ifdef TARGET_AARCH64 { @@ -113,206 +147,282 @@ static int validate_prot_to_pageflags(int *host_prot, int prot) 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); + page_set_flags(start, last, page_flags); ret = 0; -error: + 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 -#ifdef TARGET_HPPA -# define TASK_UNMAPPED_BASE 0xfa000000 -#else -# define TASK_UNMAPPED_BASE 0x40000000 -#endif -#endif -abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; +/* + * Perform a pread on behalf of target_mmap. We can reach EOF, we can be + * interrupted by signals, and in general there's no good error return path. + * If @zero, zero the rest of the block at EOF. + * Return true on success. + */ +static bool mmap_pread(int fd, void *p, size_t len, off_t offset, bool zero) +{ + while (1) { + ssize_t r = pread(fd, p, len, offset); -unsigned long last_brk; + if (likely(r == len)) { + /* Complete */ + return true; + } + if (r == 0) { + /* EOF */ + if (zero) { + memset(p, 0, len); + } + return true; + } + if (r > 0) { + /* Short read */ + p += r; + len -= r; + offset += r; + } else if (errno != EINTR) { + /* Error */ + return false; + } + } +} -/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk - of guest address space. */ +/* + * 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; + + 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; + } + + 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 (!mmap_pread(fd, g2h_untagged(start), last - start + 1, + offset, true)) { + 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; } /* @@ -323,21 +433,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); @@ -356,15 +466,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)) { @@ -372,7 +484,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; @@ -381,14 +493,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: @@ -401,8 +517,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); } @@ -417,8 +535,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); @@ -428,230 +548,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, - passthrough_start = -1, passthrough_end = -1; - 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); - passthrough_start = start; - passthrough_end = start + len; - } 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; - passthrough_start = real_start; - passthrough_end = real_end; - } - } - the_end1: if (flags & MAP_ANONYMOUS) { page_flags |= PAGE_ANON; } page_flags |= PAGE_RESET; - if (passthrough_start == passthrough_end) { - page_set_flags(start, start + len, page_flags); + if (passthrough_start > passthrough_last) { + page_set_flags(start, last, page_flags); } else { if (start < passthrough_start) { - page_set_flags(start, passthrough_start, page_flags); + page_set_flags(start, passthrough_start - 1, page_flags); } - page_set_flags(passthrough_start, passthrough_end, + page_set_flags(passthrough_start, passthrough_last, page_flags | PAGE_PASSTHROUGH); - if (passthrough_end < start + len) { - page_set_flags(passthrough_end, start + len, page_flags); + if (passthrough_last < last) { + page_set_flags(passthrough_last + 1, last, page_flags); } } - the_end: + shm_region_rm_complete(start, last); trace_target_mmap_complete(start); if (qemu_loglevel_mask(CPU_LOG_PAGE)) { FILE *f = qemu_log_trylock(); @@ -661,111 +582,519 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, qemu_log_unlock(f); } } - mmap_unlock(); return start; -fail: - mmap_unlock(); - return -1; } -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 = NULL; + abi_ulong last; + + if (start || (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + want_p = g2h_untagged(start); + } + + 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 = NULL; + off_t fileend_adj = 0; + int flags = mmap_flags; + abi_ulong last, pass_last; + + if (start || (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + want_p = g2h_untagged(start); + } + + 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 = NULL; + off_t host_offset = offset & -host_page_size; + abi_ulong last, real_start, real_last; + bool misaligned_offset = false; + size_t host_len; + + if (start || (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) { + want_p = g2h_untagged(start); + } + + 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) { + if (!mmap_pread(fd, p, host_len, offset + real_start - start, false)) { + 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 (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, 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); + 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; } @@ -792,9 +1121,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; @@ -809,20 +1140,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); @@ -835,7 +1166,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 { @@ -849,78 +1181,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); } mmap_unlock(); return new_addr; } -static bool can_passthrough_madv_dontneed(abi_ulong start, abi_ulong end) -{ - ulong addr; - - if ((start | end) & ~qemu_host_page_mask) { - return false; - } - - for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - if (!(page_get_flags(addr) & PAGE_PASSTHROUGH)) { - return false; - } - } - - return true; -} - abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice) { - abi_ulong len, end; + abi_ulong len; int ret = 0; if (start & ~TARGET_PAGE_MASK) { return -TARGET_EINVAL; } - len = TARGET_PAGE_ALIGN(len_in); - - if (len_in && !len) { - return -TARGET_EINVAL; - } - - end = start + len; - if (end < start) { - return -TARGET_EINVAL; - } - - if (end == start) { + if (len_in == 0) { return 0; } - - if (!guest_range_valid_untagged(start, len)) { + 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 */ + } + /* - * A straight passthrough may not be safe because qemu sometimes turns - * private file-backed mappings into anonymous mappings. + * Most advice values are hints, so ignoring and returning success is ok. * - * This is a hint, 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. * - * This breaks MADV_DONTNEED, completely implementing which is quite - * complicated. However, there is one low-hanging fruit: mappings that are - * known to have the same semantics in the host and the guest. In this case - * passthrough is safe, so do it. + * 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(); - if (advice == TARGET_MADV_DONTNEED && - can_passthrough_madv_dontneed(start, end)) { - ret = get_errno(madvise(g2h_untagged(start), len, MADV_DONTNEED)); - if (ret == 0) { - page_reset_target_data(start, start + len); + 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 (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, 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 deleted file mode 100644 index da77ede76b..0000000000 --- a/linux-user/nios2/cpu_loop.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * qemu user cpu loop - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu.h" -#include "user-internals.h" -#include "cpu_loop-common.h" -#include "signal-common.h" - -void cpu_loop(CPUNios2State *env) -{ - CPUState *cs = env_cpu(env); - int trapnr, ret; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - - switch (trapnr) { - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - - case EXCP_DIV: - /* Match kernel's handle_diverror_c(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); - break; - - case EXCP_UNALIGN: - case EXCP_UNALIGND: - force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, - env->ctrl[CR_BADADDR]); - break; - - case EXCP_ILLEGAL: - case EXCP_UNIMPL: - /* Match kernel's handle_illegal_c(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); - break; - case EXCP_SUPERI: - /* Match kernel's handle_supervisor_instr(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGILL, TARGET_ILL_PRVOPC, env->pc); - break; - - case EXCP_TRAP: - switch (env->error_code) { - case 0: - qemu_log_mask(CPU_LOG_INT, "\nSyscall\n"); - - ret = do_syscall(env, env->regs[2], - env->regs[4], env->regs[5], env->regs[6], - env->regs[7], env->regs[8], env->regs[9], - 0, 0); - - if (ret == -QEMU_ESIGRETURN) { - /* rt_sigreturn has set all state. */ - break; - } - if (ret == -QEMU_ERESTARTSYS) { - env->pc -= 4; - break; - } - /* - * See the code after translate_rc_and_ret: all negative - * values are errors (aided by userspace restricted to 2G), - * errno is returned positive in r2, and error indication - * is a boolean in r7. - */ - env->regs[2] = abs(ret); - env->regs[7] = ret < 0; - break; - - case 1: - qemu_log_mask(CPU_LOG_INT, "\nTrap 1\n"); - force_sig_fault(TARGET_SIGUSR1, 0, env->pc); - break; - case 2: - qemu_log_mask(CPU_LOG_INT, "\nTrap 2\n"); - force_sig_fault(TARGET_SIGUSR2, 0, env->pc); - break; - case 31: - qemu_log_mask(CPU_LOG_INT, "\nTrap 31\n"); - /* Match kernel's breakpoint_c(). */ - env->pc -= 4; - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); - break; - default: - qemu_log_mask(CPU_LOG_INT, "\nTrap %d\n", env->error_code); - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLTRP, env->pc); - break; - - case 16: /* QEMU specific, for __kuser_cmpxchg */ - { - abi_ptr g = env->regs[4]; - uint32_t *h, n, o; - - if (g & 0x3) { - force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, g); - break; - } - ret = page_get_flags(g); - if (!(ret & PAGE_VALID)) { - force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_MAPERR, g); - break; - } - if (!(ret & PAGE_READ) || !(ret & PAGE_WRITE)) { - force_sig_fault(TARGET_SIGSEGV, TARGET_SEGV_ACCERR, g); - break; - } - h = g2h(cs, g); - o = env->regs[5]; - n = env->regs[6]; - env->regs[2] = qatomic_cmpxchg(h, o, n) - o; - } - break; - } - break; - - case EXCP_DEBUG: - force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); - break; - default: - EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", - trapnr); - abort(); - } - - process_pending_signals(env); - } -} - -void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) -{ - env->regs[R_SP] = regs->sp; - env->pc = regs->ea; -} diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c deleted file mode 100644 index 32b3dc99c6..0000000000 --- a/linux-user/nios2/signal.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Emulation of Linux signals - * - * Copyright (c) 2003 Fabrice Bellard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ -#include "qemu/osdep.h" -#include "qemu.h" -#include "user-internals.h" -#include "signal-common.h" -#include "linux-user/trace.h" - -#define MCONTEXT_VERSION 2 - -struct target_sigcontext { - int version; - unsigned long gregs[32]; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -static void rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) -{ - unsigned long *gregs = uc->tuc_mcontext.gregs; - - __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); - __put_user(env->regs[1], &gregs[0]); - __put_user(env->regs[2], &gregs[1]); - __put_user(env->regs[3], &gregs[2]); - __put_user(env->regs[4], &gregs[3]); - __put_user(env->regs[5], &gregs[4]); - __put_user(env->regs[6], &gregs[5]); - __put_user(env->regs[7], &gregs[6]); - __put_user(env->regs[8], &gregs[7]); - __put_user(env->regs[9], &gregs[8]); - __put_user(env->regs[10], &gregs[9]); - __put_user(env->regs[11], &gregs[10]); - __put_user(env->regs[12], &gregs[11]); - __put_user(env->regs[13], &gregs[12]); - __put_user(env->regs[14], &gregs[13]); - __put_user(env->regs[15], &gregs[14]); - __put_user(env->regs[16], &gregs[15]); - __put_user(env->regs[17], &gregs[16]); - __put_user(env->regs[18], &gregs[17]); - __put_user(env->regs[19], &gregs[18]); - __put_user(env->regs[20], &gregs[19]); - __put_user(env->regs[21], &gregs[20]); - __put_user(env->regs[22], &gregs[21]); - __put_user(env->regs[23], &gregs[22]); - __put_user(env->regs[R_RA], &gregs[23]); - __put_user(env->regs[R_FP], &gregs[24]); - __put_user(env->regs[R_GP], &gregs[25]); - __put_user(env->pc, &gregs[27]); - __put_user(env->regs[R_SP], &gregs[28]); -} - -static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc) -{ - int temp; - unsigned long *gregs = uc->tuc_mcontext.gregs; - - /* Always make any pending restarted system calls return -EINTR */ - /* current->restart_block.fn = do_no_restart_syscall; */ - - __get_user(temp, &uc->tuc_mcontext.version); - if (temp != MCONTEXT_VERSION) { - return 1; - } - - /* restore passed registers */ - __get_user(env->regs[1], &gregs[0]); - __get_user(env->regs[2], &gregs[1]); - __get_user(env->regs[3], &gregs[2]); - __get_user(env->regs[4], &gregs[3]); - __get_user(env->regs[5], &gregs[4]); - __get_user(env->regs[6], &gregs[5]); - __get_user(env->regs[7], &gregs[6]); - __get_user(env->regs[8], &gregs[7]); - __get_user(env->regs[9], &gregs[8]); - __get_user(env->regs[10], &gregs[9]); - __get_user(env->regs[11], &gregs[10]); - __get_user(env->regs[12], &gregs[11]); - __get_user(env->regs[13], &gregs[12]); - __get_user(env->regs[14], &gregs[13]); - __get_user(env->regs[15], &gregs[14]); - __get_user(env->regs[16], &gregs[15]); - __get_user(env->regs[17], &gregs[16]); - __get_user(env->regs[18], &gregs[17]); - __get_user(env->regs[19], &gregs[18]); - __get_user(env->regs[20], &gregs[19]); - __get_user(env->regs[21], &gregs[20]); - __get_user(env->regs[22], &gregs[21]); - __get_user(env->regs[23], &gregs[22]); - /* gregs[23] is handled below */ - /* Verify, should this be settable */ - __get_user(env->regs[R_FP], &gregs[24]); - /* Verify, should this be settable */ - __get_user(env->regs[R_GP], &gregs[25]); - /* Not really necessary no user settable bits */ - __get_user(temp, &gregs[26]); - __get_user(env->pc, &gregs[27]); - - __get_user(env->regs[R_RA], &gregs[23]); - __get_user(env->regs[R_SP], &gregs[28]); - - target_restore_altstack(&uc->tuc_stack, env); - return 0; -} - -static abi_ptr get_sigframe(struct target_sigaction *ka, CPUNios2State *env, - size_t frame_size) -{ - unsigned long usp; - - /* This is the X/Open sanctioned signal stack switching. */ - usp = target_sigsp(get_sp_from_cpustate(env), ka); - - /* Verify, is it 32 or 64 bit aligned */ - return (usp - frame_size) & -8; -} - -void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, - CPUNios2State *env) -{ - struct target_rt_sigframe *frame; - abi_ptr frame_addr; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - force_sigsegv(sig); - return; - } - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - target_save_altstack(&frame->uc.tuc_stack, env); - rt_setup_ucontext(&frame->uc, env); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace; jump to fixed address sigreturn - trampoline on kuser page. */ - env->regs[R_RA] = (unsigned long) (0x1044); - - /* Set up registers for signal handler */ - env->regs[R_SP] = frame_addr; - env->regs[4] = sig; - env->regs[5] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->regs[6] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->pc = ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); -} - -long do_rt_sigreturn(CPUNios2State *env) -{ - /* Verify, can we follow the stack back */ - abi_ulong frame_addr = env->regs[R_SP]; - struct target_rt_sigframe *frame; - sigset_t set; - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - if (rt_restore_ucontext(env, &frame->uc)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -QEMU_ESIGRETURN; -} diff --git a/linux-user/nios2/sockbits.h b/linux-user/nios2/sockbits.h deleted file mode 100644 index 0e4c8f012d..0000000000 --- a/linux-user/nios2/sockbits.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/sockbits.h" diff --git a/linux-user/nios2/syscall_nr.h b/linux-user/nios2/syscall_nr.h deleted file mode 100644 index 11a37b32e8..0000000000 --- a/linux-user/nios2/syscall_nr.h +++ /dev/null @@ -1,333 +0,0 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_NIOS2_SYSCALL_NR_H -#define LINUX_USER_NIOS2_SYSCALL_NR_H - -#define TARGET_NR_cacheflush (TARGET_NR_arch_specific_syscall) -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl64 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs64 43 -#define TARGET_NR_fstatfs64 44 -#define TARGET_NR_truncate64 45 -#define TARGET_NR_ftruncate64 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_llseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile64 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 -#define TARGET_NR_fstat64 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_io_pgetevents 292 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_clock_gettime64 403 -#define TARGET_NR_clock_settime64 404 -#define TARGET_NR_clock_adjtime64 405 -#define TARGET_NR_clock_getres_time64 406 -#define TARGET_NR_clock_nanosleep_time64 407 -#define TARGET_NR_timer_gettime64 408 -#define TARGET_NR_timer_settime64 409 -#define TARGET_NR_timerfd_gettime64 410 -#define TARGET_NR_timerfd_settime64 411 -#define TARGET_NR_utimensat_time64 412 -#define TARGET_NR_pselect6_time64 413 -#define TARGET_NR_ppoll_time64 414 -#define TARGET_NR_io_pgetevents_time64 416 -#define TARGET_NR_recvmmsg_time64 417 -#define TARGET_NR_mq_timedsend_time64 418 -#define TARGET_NR_mq_timedreceive_time64 419 -#define TARGET_NR_semtimedop_time64 420 -#define TARGET_NR_rt_sigtimedwait_time64 421 -#define TARGET_NR_futex_time64 422 -#define TARGET_NR_sched_rr_get_interval_time64 423 -#define TARGET_NR_pidfd_send_signal 424 -#define TARGET_NR_io_uring_setup 425 -#define TARGET_NR_io_uring_enter 426 -#define TARGET_NR_io_uring_register 427 -#define TARGET_NR_open_tree 428 -#define TARGET_NR_move_mount 429 -#define TARGET_NR_fsopen 430 -#define TARGET_NR_fsconfig 431 -#define TARGET_NR_fsmount 432 -#define TARGET_NR_fspick 433 -#define TARGET_NR_pidfd_open 434 -#define TARGET_NR_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_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_NIOS2_SYSCALL_NR_H */ diff --git a/linux-user/nios2/target_cpu.h b/linux-user/nios2/target_cpu.h deleted file mode 100644 index 830b4c0741..0000000000 --- a/linux-user/nios2/target_cpu.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Nios2 specific CPU ABI and functions for linux-user - * - * Copyright (c) 2016 Marek Vasut - * - * 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 NIOS2_TARGET_CPU_H -#define NIOS2_TARGET_CPU_H - -static inline void cpu_clone_regs_child(CPUNios2State *env, target_ulong newsp, - unsigned flags) -{ - if (newsp) { - env->regs[R_SP] = newsp; - } - env->regs[R_RET0] = 0; - env->regs[7] = 0; -} - -static inline void cpu_clone_regs_parent(CPUNios2State *env, unsigned flags) -{ -} - -static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls) -{ - /* - * Linux kernel 3.10 does not pay any attention to CLONE_SETTLS - * in copy_thread(), so QEMU need not do so either. - */ -} - -static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) -{ - return state->regs[R_SP]; -} -#endif diff --git a/linux-user/nios2/target_elf.h b/linux-user/nios2/target_elf.h deleted file mode 100644 index 801e20afaf..0000000000 --- a/linux-user/nios2/target_elf.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef NIOS2_TARGET_ELF_H -#define NIOS2_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} -#endif diff --git a/linux-user/nios2/target_errno_defs.h b/linux-user/nios2/target_errno_defs.h deleted file mode 100644 index 28120013e2..0000000000 --- a/linux-user/nios2/target_errno_defs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NIOS2_TARGET_ERRNO_DEFS_H -#define NIOS2_TARGET_ERRNO_DEFS_H - -/* Target uses generic errno */ -#include "../generic/target_errno_defs.h" - -#endif diff --git a/linux-user/nios2/target_fcntl.h b/linux-user/nios2/target_fcntl.h deleted file mode 100644 index 714583215d..0000000000 --- a/linux-user/nios2/target_fcntl.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * 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, or (at your option) any - * later version. See the COPYING file in the top-level directory. - */ - -#ifndef NIOS2_TARGET_FCNTL_H -#define NIOS2_TARGET_FCNTL_H -#include "../generic/fcntl.h" -#endif diff --git a/linux-user/nios2/target_mman.h b/linux-user/nios2/target_mman.h deleted file mode 100644 index e7ba6070fe..0000000000 --- a/linux-user/nios2/target_mman.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_mman.h" diff --git a/linux-user/nios2/target_prctl.h b/linux-user/nios2/target_prctl.h deleted file mode 100644 index eb53b31ad5..0000000000 --- a/linux-user/nios2/target_prctl.h +++ /dev/null @@ -1 +0,0 @@ -/* No special prctl support required. */ diff --git a/linux-user/nios2/target_resource.h b/linux-user/nios2/target_resource.h deleted file mode 100644 index 227259594c..0000000000 --- a/linux-user/nios2/target_resource.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_resource.h" diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h deleted file mode 100644 index 46ca5948ce..0000000000 --- a/linux-user/nios2/target_signal.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef NIOS2_TARGET_SIGNAL_H -#define NIOS2_TARGET_SIGNAL_H - -#include "../generic/signal.h" - -/* Nios2 uses a fixed address on the kuser page for sigreturn. */ -#define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 - -#endif /* NIOS2_TARGET_SIGNAL_H */ diff --git a/linux-user/nios2/target_structs.h b/linux-user/nios2/target_structs.h deleted file mode 100644 index 3a06f373c3..0000000000 --- a/linux-user/nios2/target_structs.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/target_structs.h" diff --git a/linux-user/nios2/target_syscall.h b/linux-user/nios2/target_syscall.h deleted file mode 100644 index 561b28d281..0000000000 --- a/linux-user/nios2/target_syscall.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef NIOS2_TARGET_SYSCALL_H -#define NIOS2_TARGET_SYSCALL_H - -#define UNAME_MACHINE "nios2" -#define UNAME_MINIMUM_RELEASE "3.19.0" - -struct target_pt_regs { - unsigned long r8; /* r8-r15 Caller-saved GP registers */ - unsigned long r9; - unsigned long r10; - unsigned long r11; - unsigned long r12; - unsigned long r13; - unsigned long r14; - unsigned long r15; - unsigned long r1; /* Assembler temporary */ - unsigned long r2; /* Retval LS 32bits */ - unsigned long r3; /* Retval MS 32bits */ - unsigned long r4; /* r4-r7 Register arguments */ - unsigned long r5; - unsigned long r6; - unsigned long r7; - unsigned long orig_r2; /* Copy of r2 ?? */ - unsigned long ra; /* Return address */ - unsigned long fp; /* Frame pointer */ - unsigned long sp; /* Stack pointer */ - unsigned long gp; /* Global pointer */ - unsigned long estatus; - unsigned long ea; /* Exception return address (pc) */ - unsigned long orig_r7; -}; - -#define TARGET_MCL_CURRENT 1 -#define TARGET_MCL_FUTURE 2 -#define TARGET_MCL_ONFAULT 4 - -#endif /* NIOS2_TARGET_SYSCALL_H */ diff --git a/linux-user/nios2/termbits.h b/linux-user/nios2/termbits.h deleted file mode 100644 index b1d4f4fedb..0000000000 --- a/linux-user/nios2/termbits.h +++ /dev/null @@ -1 +0,0 @@ -#include "../generic/termbits.h" diff --git a/linux-user/openrisc/meson.build b/linux-user/openrisc/meson.build new file mode 100644 index 0000000000..273e7a0c38 --- /dev/null +++ b/linux-user/openrisc/meson.build @@ -0,0 +1,5 @@ +syscall_nr_generators += { + 'openrisc': generator(sh, + arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], + output: '@BASENAME@_nr.h') +} 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/syscall.tbl b/linux-user/openrisc/syscall.tbl new file mode 100644 index 0000000000..845e24eb37 --- /dev/null +++ b/linux-user/openrisc/syscall.tbl @@ -0,0 +1,405 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# This file contains the system call numbers for all of the +# more recently added architectures. +# +# As a basic principle, no duplication of functionality +# should be added, e.g. we don't use lseek when llseek +# is present. New architectures should use this file +# and implement the less feature-full calls in user space. +# +0 common io_setup sys_io_setup compat_sys_io_setup +1 common io_destroy sys_io_destroy +2 common io_submit sys_io_submit compat_sys_io_submit +3 common io_cancel sys_io_cancel +4 time32 io_getevents sys_io_getevents_time32 +4 64 io_getevents sys_io_getevents +5 common setxattr sys_setxattr +6 common lsetxattr sys_lsetxattr +7 common fsetxattr sys_fsetxattr +8 common getxattr sys_getxattr +9 common lgetxattr sys_lgetxattr +10 common fgetxattr sys_fgetxattr +11 common listxattr sys_listxattr +12 common llistxattr sys_llistxattr +13 common flistxattr sys_flistxattr +14 common removexattr sys_removexattr +15 common lremovexattr sys_lremovexattr +16 common fremovexattr sys_fremovexattr +17 common getcwd sys_getcwd +18 common lookup_dcookie sys_ni_syscall +19 common eventfd2 sys_eventfd2 +20 common epoll_create1 sys_epoll_create1 +21 common epoll_ctl sys_epoll_ctl +22 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +23 common dup sys_dup +24 common dup3 sys_dup3 +25 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +25 64 fcntl sys_fcntl +26 common inotify_init1 sys_inotify_init1 +27 common inotify_add_watch sys_inotify_add_watch +28 common inotify_rm_watch sys_inotify_rm_watch +29 common ioctl sys_ioctl compat_sys_ioctl +30 common ioprio_set sys_ioprio_set +31 common ioprio_get sys_ioprio_get +32 common flock sys_flock +33 common mknodat sys_mknodat +34 common mkdirat sys_mkdirat +35 common unlinkat sys_unlinkat +36 common symlinkat sys_symlinkat +37 common linkat sys_linkat +# renameat is superseded with flags by renameat2 +38 renameat renameat sys_renameat +39 common umount2 sys_umount +40 common mount sys_mount +41 common pivot_root sys_pivot_root +42 common nfsservctl sys_ni_syscall +43 32 statfs64 sys_statfs64 compat_sys_statfs64 +43 64 statfs sys_statfs +44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +44 64 fstatfs sys_fstatfs +45 32 truncate64 sys_truncate64 compat_sys_truncate64 +45 64 truncate sys_truncate +46 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +46 64 ftruncate sys_ftruncate +47 common fallocate sys_fallocate compat_sys_fallocate +48 common faccessat sys_faccessat +49 common chdir sys_chdir +50 common fchdir sys_fchdir +51 common chroot sys_chroot +52 common fchmod sys_fchmod +53 common fchmodat sys_fchmodat +54 common fchownat sys_fchownat +55 common fchown sys_fchown +56 common openat sys_openat +57 common close sys_close +58 common vhangup sys_vhangup +59 common pipe2 sys_pipe2 +60 common quotactl sys_quotactl +61 common getdents64 sys_getdents64 +62 32 llseek sys_llseek +62 64 lseek sys_lseek +63 common read sys_read +64 common write sys_write +65 common readv sys_readv sys_readv +66 common writev sys_writev sys_writev +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 common preadv sys_preadv compat_sys_preadv +70 common pwritev sys_pwritev compat_sys_pwritev +71 32 sendfile64 sys_sendfile64 +71 64 sendfile sys_sendfile64 +72 time32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +72 64 pselect6 sys_pselect6 +73 time32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +73 64 ppoll sys_ppoll +74 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +75 common vmsplice sys_vmsplice +76 common splice sys_splice +77 common tee sys_tee +78 common readlinkat sys_readlinkat +79 stat64 fstatat64 sys_fstatat64 +79 64 newfstatat sys_newfstatat +80 stat64 fstat64 sys_fstat64 +80 64 fstat sys_newfstat +81 common sync sys_sync +82 common fsync sys_fsync +83 common fdatasync sys_fdatasync +84 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +85 common timerfd_create sys_timerfd_create +86 time32 timerfd_settime sys_timerfd_settime32 +86 64 timerfd_settime sys_timerfd_settime +87 time32 timerfd_gettime sys_timerfd_gettime32 +87 64 timerfd_gettime sys_timerfd_gettime +88 time32 utimensat sys_utimensat_time32 +88 64 utimensat sys_utimensat +89 common acct sys_acct +90 common capget sys_capget +91 common capset sys_capset +92 common personality sys_personality +93 common exit sys_exit +94 common exit_group sys_exit_group +95 common waitid sys_waitid compat_sys_waitid +96 common set_tid_address sys_set_tid_address +97 common unshare sys_unshare +98 time32 futex sys_futex_time32 +98 64 futex sys_futex +99 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +100 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +101 time32 nanosleep sys_nanosleep_time32 +101 64 nanosleep sys_nanosleep +102 common getitimer sys_getitimer compat_sys_getitimer +103 common setitimer sys_setitimer compat_sys_setitimer +104 common kexec_load sys_kexec_load compat_sys_kexec_load +105 common init_module sys_init_module +106 common delete_module sys_delete_module +107 common timer_create sys_timer_create compat_sys_timer_create +108 time32 timer_gettime sys_timer_gettime32 +108 64 timer_gettime sys_timer_gettime +109 common timer_getoverrun sys_timer_getoverrun +110 time32 timer_settime sys_timer_settime32 +110 64 timer_settime sys_timer_settime +111 common timer_delete sys_timer_delete +112 time32 clock_settime sys_clock_settime32 +112 64 clock_settime sys_clock_settime +113 time32 clock_gettime sys_clock_gettime32 +113 64 clock_gettime sys_clock_gettime +114 time32 clock_getres sys_clock_getres_time32 +114 64 clock_getres sys_clock_getres +115 time32 clock_nanosleep sys_clock_nanosleep_time32 +115 64 clock_nanosleep sys_clock_nanosleep +116 common syslog sys_syslog +117 common ptrace sys_ptrace compat_sys_ptrace +118 common sched_setparam sys_sched_setparam +119 common sched_setscheduler sys_sched_setscheduler +120 common sched_getscheduler sys_sched_getscheduler +121 common sched_getparam sys_sched_getparam +122 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +123 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +124 common sched_yield sys_sched_yield +125 common sched_get_priority_max sys_sched_get_priority_max +126 common sched_get_priority_min sys_sched_get_priority_min +127 time32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +127 64 sched_rr_get_interval sys_sched_rr_get_interval +128 common restart_syscall sys_restart_syscall +129 common kill sys_kill +130 common tkill sys_tkill +131 common tgkill sys_tgkill +132 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +133 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +134 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +135 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +136 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +137 time32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +137 64 rt_sigtimedwait sys_rt_sigtimedwait +138 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +139 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +140 common setpriority sys_setpriority +141 common getpriority sys_getpriority +142 common reboot sys_reboot +143 common setregid sys_setregid +144 common setgid sys_setgid +145 common setreuid sys_setreuid +146 common setuid sys_setuid +147 common setresuid sys_setresuid +148 common getresuid sys_getresuid +149 common setresgid sys_setresgid +150 common getresgid sys_getresgid +151 common setfsuid sys_setfsuid +152 common setfsgid sys_setfsgid +153 common times sys_times compat_sys_times +154 common setpgid sys_setpgid +155 common getpgid sys_getpgid +156 common getsid sys_getsid +157 common setsid sys_setsid +158 common getgroups sys_getgroups +159 common setgroups sys_setgroups +160 common uname sys_newuname +161 common sethostname sys_sethostname +162 common setdomainname sys_setdomainname +# getrlimit and setrlimit are superseded with prlimit64 +163 rlimit getrlimit sys_getrlimit compat_sys_getrlimit +164 rlimit setrlimit sys_setrlimit compat_sys_setrlimit +165 common getrusage sys_getrusage compat_sys_getrusage +166 common umask sys_umask +167 common prctl sys_prctl +168 common getcpu sys_getcpu +169 time32 gettimeofday sys_gettimeofday compat_sys_gettimeofday +169 64 gettimeofday sys_gettimeofday +170 time32 settimeofday sys_settimeofday compat_sys_settimeofday +170 64 settimeofday sys_settimeofday +171 time32 adjtimex sys_adjtimex_time32 +171 64 adjtimex sys_adjtimex +172 common getpid sys_getpid +173 common getppid sys_getppid +174 common getuid sys_getuid +175 common geteuid sys_geteuid +176 common getgid sys_getgid +177 common getegid sys_getegid +178 common gettid sys_gettid +179 common sysinfo sys_sysinfo compat_sys_sysinfo +180 common mq_open sys_mq_open compat_sys_mq_open +181 common mq_unlink sys_mq_unlink +182 time32 mq_timedsend sys_mq_timedsend_time32 +182 64 mq_timedsend sys_mq_timedsend +183 time32 mq_timedreceive sys_mq_timedreceive_time32 +183 64 mq_timedreceive sys_mq_timedreceive +184 common mq_notify sys_mq_notify compat_sys_mq_notify +185 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +186 common msgget sys_msgget +187 common msgctl sys_msgctl compat_sys_msgctl +188 common msgrcv sys_msgrcv compat_sys_msgrcv +189 common msgsnd sys_msgsnd compat_sys_msgsnd +190 common semget sys_semget +191 common semctl sys_semctl compat_sys_semctl +192 time32 semtimedop sys_semtimedop_time32 +192 64 semtimedop sys_semtimedop +193 common semop sys_semop +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +196 common shmat sys_shmat compat_sys_shmat +197 common shmdt sys_shmdt +198 common socket sys_socket +199 common socketpair sys_socketpair +200 common bind sys_bind +201 common listen sys_listen +202 common accept sys_accept +203 common connect sys_connect +204 common getsockname sys_getsockname +205 common getpeername sys_getpeername +206 common sendto sys_sendto +207 common recvfrom sys_recvfrom compat_sys_recvfrom +208 common setsockopt sys_setsockopt sys_setsockopt +209 common getsockopt sys_getsockopt sys_getsockopt +210 common shutdown sys_shutdown +211 common sendmsg sys_sendmsg compat_sys_sendmsg +212 common recvmsg sys_recvmsg compat_sys_recvmsg +213 common readahead sys_readahead compat_sys_readahead +214 common brk sys_brk +215 common munmap sys_munmap +216 common mremap sys_mremap +217 common add_key sys_add_key +218 common request_key sys_request_key +219 common keyctl sys_keyctl compat_sys_keyctl +220 common clone sys_clone +221 common execve sys_execve compat_sys_execve +222 32 mmap2 sys_mmap2 +222 64 mmap sys_mmap +223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +223 64 fadvise64 sys_fadvise64_64 +224 common swapon sys_swapon +225 common swapoff sys_swapoff +226 common mprotect sys_mprotect +227 common msync sys_msync +228 common mlock sys_mlock +229 common munlock sys_munlock +230 common mlockall sys_mlockall +231 common munlockall sys_munlockall +232 common mincore sys_mincore +233 common madvise sys_madvise +234 common remap_file_pages sys_remap_file_pages +235 common mbind sys_mbind +236 common get_mempolicy sys_get_mempolicy +237 common set_mempolicy sys_set_mempolicy +238 common migrate_pages sys_migrate_pages +239 common move_pages sys_move_pages +240 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +241 common perf_event_open sys_perf_event_open +242 common accept4 sys_accept4 +243 time32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +243 64 recvmmsg sys_recvmmsg +# Architectures may provide up to 16 syscalls of their own between 244 and 259 +244 arc cacheflush sys_cacheflush +245 arc arc_settls sys_arc_settls +246 arc arc_gettls sys_arc_gettls +247 arc sysfs sys_sysfs +248 arc arc_usr_cmpxchg sys_arc_usr_cmpxchg + +244 csky set_thread_area sys_set_thread_area +245 csky cacheflush sys_cacheflush + +244 nios2 cacheflush sys_cacheflush + +244 or1k or1k_atomic sys_or1k_atomic + +258 riscv riscv_hwprobe sys_riscv_hwprobe +259 riscv riscv_flush_icache sys_riscv_flush_icache + +260 time32 wait4 sys_wait4 compat_sys_wait4 +260 64 wait4 sys_wait4 +261 common prlimit64 sys_prlimit64 +262 common fanotify_init sys_fanotify_init +263 common fanotify_mark sys_fanotify_mark +264 common name_to_handle_at sys_name_to_handle_at +265 common open_by_handle_at sys_open_by_handle_at +266 time32 clock_adjtime sys_clock_adjtime32 +266 64 clock_adjtime sys_clock_adjtime +267 common syncfs sys_syncfs +268 common setns sys_setns +269 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +270 common process_vm_readv sys_process_vm_readv +271 common process_vm_writev sys_process_vm_writev +272 common kcmp sys_kcmp +273 common finit_module sys_finit_module +274 common sched_setattr sys_sched_setattr +275 common sched_getattr sys_sched_getattr +276 common renameat2 sys_renameat2 +277 common seccomp sys_seccomp +278 common getrandom sys_getrandom +279 common memfd_create sys_memfd_create +280 common bpf sys_bpf +281 common execveat sys_execveat compat_sys_execveat +282 common userfaultfd sys_userfaultfd +283 common membarrier sys_membarrier +284 common mlock2 sys_mlock2 +285 common copy_file_range sys_copy_file_range +286 common preadv2 sys_preadv2 compat_sys_preadv2 +287 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +288 common pkey_mprotect sys_pkey_mprotect +289 common pkey_alloc sys_pkey_alloc +290 common pkey_free sys_pkey_free +291 common statx sys_statx +292 time32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +292 64 io_pgetevents sys_io_pgetevents +293 common rseq sys_rseq +294 common kexec_file_load sys_kexec_file_load +# 295 through 402 are unassigned to sync up with generic numbers don't use +403 32 clock_gettime64 sys_clock_gettime +404 32 clock_settime64 sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime +409 32 timer_settime64 sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +447 memfd_secret memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/openrisc/syscall_nr.h b/linux-user/openrisc/syscall_nr.h deleted file mode 100644 index f7faddb54c..0000000000 --- a/linux-user/openrisc/syscall_nr.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_OPENRISC_SYSCALL_NR_H -#define LINUX_USER_OPENRISC_SYSCALL_NR_H - -#define TARGET_NR_io_setup 0 -#define TARGET_NR_or1k_atomic TARGET_NR_arch_specific_syscall -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl64 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_renameat 38 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs64 43 -#define TARGET_NR_fstatfs64 44 -#define TARGET_NR_truncate64 45 -#define TARGET_NR_ftruncate64 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_llseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile64 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 -#define TARGET_NR_fstat64 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_io_pgetevents 292 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_clock_gettime64 403 -#define TARGET_NR_clock_settime64 404 -#define TARGET_NR_clock_adjtime64 405 -#define TARGET_NR_clock_getres_time64 406 -#define TARGET_NR_clock_nanosleep_time64 407 -#define TARGET_NR_timer_gettime64 408 -#define TARGET_NR_timer_settime64 409 -#define TARGET_NR_timerfd_gettime64 410 -#define TARGET_NR_timerfd_settime64 411 -#define TARGET_NR_utimensat_time64 412 -#define TARGET_NR_pselect6_time64 413 -#define TARGET_NR_ppoll_time64 414 -#define TARGET_NR_io_pgetevents_time64 416 -#define TARGET_NR_recvmmsg_time64 417 -#define TARGET_NR_mq_timedsend_time64 418 -#define TARGET_NR_mq_timedreceive_time64 419 -#define TARGET_NR_semtimedop_time64 420 -#define TARGET_NR_rt_sigtimedwait_time64 421 -#define TARGET_NR_futex_time64 422 -#define TARGET_NR_sched_rr_get_interval_time64 423 -#define TARGET_NR_pidfd_send_signal 424 -#define TARGET_NR_io_uring_setup 425 -#define TARGET_NR_io_uring_enter 426 -#define TARGET_NR_io_uring_register 427 -#define TARGET_NR_open_tree 428 -#define TARGET_NR_move_mount 429 -#define TARGET_NR_fsopen 430 -#define TARGET_NR_fsconfig 431 -#define TARGET_NR_fsmount 432 -#define TARGET_NR_fspick 433 -#define TARGET_NR_pidfd_open 434 -#define TARGET_NR_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_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_OPENRISC_SYSCALL_NR_H */ diff --git a/linux-user/openrisc/syscallhdr.sh b/linux-user/openrisc/syscallhdr.sh new file mode 100644 index 0000000000..047e9f77c7 --- /dev/null +++ b/linux-user/openrisc/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_OPENRISC_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/openrisc/target_mman.h b/linux-user/openrisc/target_mman.h index e7ba6070fe..243c1d5f26 100644 --- a/linux-user/openrisc/target_mman.h +++ b/linux-user/openrisc/target_mman.h @@ -1 +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..e2b8facbb5 --- /dev/null +++ b/linux-user/ppc/Makefile.vdso @@ -0,0 +1,22 @@ +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 -Wl,-z,max-page-size=4096 +LDFLAGS64 = -nostdlib -shared -Wl,-T,$(SUBDIR)/vdso-64.ld \ + -Wl,-h,linux-vdso64.so.1 -Wl,--hash-style=both \ + -Wl,--build-id=sha1 -Wl,-z,max-page-size=4096 + +$(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..24e5a02a78 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -21,14 +21,8 @@ #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 "user/tswap-target.h" +#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 +67,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 +165,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 +173,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 +193,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 +205,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 +259,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 +349,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 +487,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 +503,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]); } @@ -617,7 +628,7 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) return 1; - target_to_host_sigset_internal(&blocked, &set); + target_to_host_sigset(&blocked, &set); set_sigmask(&blocked); restore_user_regs(env, mcp, sig); @@ -663,7 +674,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/syscall.tbl b/linux-user/ppc/syscall.tbl index 8f052ff405..4b428a43cc 100644 --- a/linux-user/ppc/syscall.tbl +++ b/linux-user/ppc/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for powerpc # @@ -110,7 +110,7 @@ 79 common settimeofday sys_settimeofday compat_sys_settimeofday 80 common getgroups sys_getgroups 81 common setgroups sys_setgroups -82 32 select ppc_select sys_ni_syscall +82 32 select sys_old_select compat_sys_old_select 82 64 select sys_ni_syscall 82 spu select sys_ni_syscall 83 common symlink sys_symlink @@ -176,11 +176,11 @@ 131 nospu quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir -134 common bdflush sys_bdflush +134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs -136 32 personality sys_personality ppc64_personality -136 64 personality ppc64_personality -136 spu personality ppc64_personality +136 32 personality sys_personality compat_sys_ppc64_personality +136 64 personality sys_ppc64_personality +136 spu personality sys_ppc64_personality 137 common afs_syscall sys_ni_syscall 138 common setfsuid sys_setfsuid 139 common setfsgid sys_setfsgid @@ -228,8 +228,12 @@ 176 64 rt_sigtimedwait sys_rt_sigtimedwait 177 nospu rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 178 nospu rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend -179 common pread64 sys_pread64 compat_sys_pread64 -180 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +179 32 pread64 sys_ppc_pread64 compat_sys_ppc_pread64 +179 64 pread64 sys_pread64 +179 spu pread64 sys_pread64 +180 32 pwrite64 sys_ppc_pwrite64 compat_sys_ppc_pwrite64 +180 64 pwrite64 sys_pwrite64 +180 spu pwrite64 sys_pwrite64 181 common chown sys_chown 182 common getcwd sys_getcwd 183 common capget sys_capget @@ -242,10 +246,12 @@ 188 common putpmsg sys_ni_syscall 189 nospu vfork sys_vfork 190 common ugetrlimit sys_getrlimit compat_sys_getrlimit -191 common readahead sys_readahead compat_sys_readahead +191 32 readahead sys_ppc_readahead compat_sys_ppc_readahead +191 64 readahead sys_readahead +191 spu readahead sys_readahead 192 32 mmap2 sys_mmap2 compat_sys_mmap2 -193 32 truncate64 sys_truncate64 compat_sys_truncate64 -194 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +193 32 truncate64 sys_ppc_truncate64 compat_sys_ppc_truncate64 +194 32 ftruncate64 sys_ppc_ftruncate64 compat_sys_ppc_ftruncate64 195 32 stat64 sys_stat64 196 32 lstat64 sys_lstat64 197 32 fstat64 sys_fstat64 @@ -288,9 +294,11 @@ 230 common io_submit sys_io_submit compat_sys_io_submit 231 common io_cancel sys_io_cancel 232 nospu set_tid_address sys_set_tid_address -233 common fadvise64 sys_fadvise64 ppc32_fadvise64 +233 32 fadvise64 sys_ppc32_fadvise64 compat_sys_ppc32_fadvise64 +233 64 fadvise64 sys_fadvise64 +233 spu fadvise64 sys_fadvise64 234 nospu exit_group sys_exit_group -235 nospu lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +235 nospu lookup_dcookie sys_ni_syscall 236 common epoll_create sys_epoll_create 237 common epoll_ctl sys_epoll_ctl 238 common epoll_wait sys_epoll_wait @@ -323,17 +331,17 @@ 251 spu utimes sys_utimes 252 common statfs64 sys_statfs64 compat_sys_statfs64 253 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 -254 32 fadvise64_64 ppc_fadvise64_64 +254 32 fadvise64_64 sys_ppc_fadvise64_64 254 spu fadvise64_64 sys_ni_syscall 255 common rtas sys_rtas 256 32 sys_debug_setcontext sys_debug_setcontext sys_ni_syscall 256 64 sys_debug_setcontext sys_ni_syscall 256 spu sys_debug_setcontext sys_ni_syscall # 257 reserved for vserver -258 nospu migrate_pages sys_migrate_pages compat_sys_migrate_pages -259 nospu mbind sys_mbind compat_sys_mbind -260 nospu get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy -261 nospu set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +258 nospu migrate_pages sys_migrate_pages +259 nospu mbind sys_mbind +260 nospu get_mempolicy sys_get_mempolicy +261 nospu set_mempolicy sys_set_mempolicy 262 nospu mq_open sys_mq_open compat_sys_mq_open 263 nospu mq_unlink sys_mq_unlink 264 32 mq_timedsend sys_mq_timedsend_time32 @@ -381,7 +389,7 @@ 298 common faccessat sys_faccessat 299 common get_robust_list sys_get_robust_list compat_sys_get_robust_list 300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list -301 common move_pages sys_move_pages compat_sys_move_pages +301 common move_pages sys_move_pages 302 common getcpu sys_getcpu 303 nospu epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 304 32 utimensat sys_utimensat_time32 @@ -390,8 +398,11 @@ 305 common signalfd sys_signalfd compat_sys_signalfd 306 common timerfd_create sys_timerfd_create 307 common eventfd sys_eventfd -308 common sync_file_range2 sys_sync_file_range2 compat_sys_sync_file_range2 -309 nospu fallocate sys_fallocate compat_sys_fallocate +308 32 sync_file_range2 sys_ppc_sync_file_range2 compat_sys_ppc_sync_file_range2 +308 64 sync_file_range2 sys_sync_file_range2 +308 spu sync_file_range2 sys_sync_file_range2 +309 32 fallocate sys_ppc_fallocate compat_sys_fallocate +309 64 fallocate sys_fallocate 310 nospu subpage_prot sys_subpage_prot 311 32 timerfd_settime sys_timerfd_settime32 311 64 timerfd_settime sys_timerfd_settime @@ -495,7 +506,7 @@ 412 32 utimensat_time64 sys_utimensat sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 -416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive @@ -522,7 +533,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 nospu set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_ni_syscall +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/ppc/syscallhdr.sh b/linux-user/ppc/syscallhdr.sh index 6c44e0eaad..6e8b93d673 100644 --- a/linux-user/ppc/syscallhdr.sh +++ b/linux-user/ppc/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/ppc/target_mman.h b/linux-user/ppc/target_mman.h index e7ba6070fe..646d1ccae7 100644 --- a/linux-user/ppc/target_mman.h +++ b/linux-user/ppc/target_mman.h @@ -1 +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..0dc55e0ddd 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..ac1ab2582e 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..424abb4290 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 e2e93fbd1d..67bc81b149 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -4,12 +4,11 @@ #include "cpu.h" #include "exec/cpu_ldst.h" -#undef DEBUG_REMAP - -#include "exec/user/abitypes.h" +#include "user/abitypes.h" #include "syscall_defs.h" #include "target_syscall.h" +#include "accel/tcg/vcpu-state.h" /* * This is the size of the host kernel's sigset_t, needed where we make @@ -29,12 +28,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; @@ -47,7 +44,6 @@ struct image_info { abi_ulong file_string; uint32_t elf_flags; int personality; - abi_ulong alignment; bool exec_stack; /* Generic semihosting knows about these pointers. */ @@ -99,7 +95,7 @@ struct emulated_sigtable { target_siginfo_t info; }; -typedef struct TaskState { +struct TaskState { pid_t ts_tid; /* tid (or pid) of this task */ #ifdef TARGET_ARM # ifdef TARGET_ABI32 @@ -116,6 +112,10 @@ typedef struct TaskState { struct target_vm86plus_struct vm86plus; uint32_t v86flags; uint32_t v86mask; +#endif +#if defined(TARGET_I386) + /* Last syscall number. */ + target_ulong orig_ax; #endif abi_ulong child_tidptr; #ifdef TARGET_M68K @@ -162,12 +162,16 @@ typedef struct TaskState { /* Start time of task after system boot in clock ticks */ uint64_t start_boottime; -} TaskState; +}; 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) @@ -178,7 +182,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, @@ -312,6 +316,15 @@ static inline bool access_ok(CPUState *cpu, int type, int copy_from_user(void *hptr, abi_ulong gaddr, ssize_t len); int copy_to_user(abi_ulong gaddr, void *hptr, ssize_t len); +/* + * copy_struct_from_user() copies a target struct to a host struct, in + * a way that guarantees backwards-compatibility for struct syscall + * arguments. + * + * Similar to kernels uaccess.h:copy_struct_from_user() + */ +int copy_struct_from_user(void *dst, size_t ksize, abi_ptr src, size_t usize); + /* Functions for accessing guest memory. The tget and tput functions read/write single values, byteswapping as necessary. The lock_user function gets a pointer to a contiguous area of guest memory, but does not perform @@ -325,7 +338,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy); /* Unlock an area of guest memory. The first LEN bytes must be flushed back to guest memory. host_ptr = NULL is explicitly allowed and does nothing. */ -#ifndef DEBUG_REMAP +#ifndef CONFIG_DEBUG_REMAP static inline void unlock_user(void *host_ptr, abi_ulong guest_addr, ssize_t len) { 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 bffca7db12..0af533e186 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -47,7 +47,7 @@ void cpu_loop(CPURISCVState *env) break; case RISCV_EXCP_U_ECALL: env->pc += 4; - if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) { + if (env->gpr[xA7] == TARGET_NR_riscv_flush_icache) { /* riscv_flush_icache_syscall is a no-op in QEMU as self-modifying code is automatically detected */ ret = 0; @@ -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..b2e7df0f4f --- /dev/null +++ b/linux-user/riscv/meson.build @@ -0,0 +1,13 @@ +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) + +syscall_nr_generators += { + 'riscv': generator(sh, + arguments: [ meson.current_source_dir() / 'syscallhdr.sh', '@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@' ], + output: '@BASENAME@_nr.h') +} diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index eaa168199a..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) { @@ -117,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/syscall.tbl b/linux-user/riscv/syscall.tbl new file mode 100644 index 0000000000..845e24eb37 --- /dev/null +++ b/linux-user/riscv/syscall.tbl @@ -0,0 +1,405 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# +# This file contains the system call numbers for all of the +# more recently added architectures. +# +# As a basic principle, no duplication of functionality +# should be added, e.g. we don't use lseek when llseek +# is present. New architectures should use this file +# and implement the less feature-full calls in user space. +# +0 common io_setup sys_io_setup compat_sys_io_setup +1 common io_destroy sys_io_destroy +2 common io_submit sys_io_submit compat_sys_io_submit +3 common io_cancel sys_io_cancel +4 time32 io_getevents sys_io_getevents_time32 +4 64 io_getevents sys_io_getevents +5 common setxattr sys_setxattr +6 common lsetxattr sys_lsetxattr +7 common fsetxattr sys_fsetxattr +8 common getxattr sys_getxattr +9 common lgetxattr sys_lgetxattr +10 common fgetxattr sys_fgetxattr +11 common listxattr sys_listxattr +12 common llistxattr sys_llistxattr +13 common flistxattr sys_flistxattr +14 common removexattr sys_removexattr +15 common lremovexattr sys_lremovexattr +16 common fremovexattr sys_fremovexattr +17 common getcwd sys_getcwd +18 common lookup_dcookie sys_ni_syscall +19 common eventfd2 sys_eventfd2 +20 common epoll_create1 sys_epoll_create1 +21 common epoll_ctl sys_epoll_ctl +22 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait +23 common dup sys_dup +24 common dup3 sys_dup3 +25 32 fcntl64 sys_fcntl64 compat_sys_fcntl64 +25 64 fcntl sys_fcntl +26 common inotify_init1 sys_inotify_init1 +27 common inotify_add_watch sys_inotify_add_watch +28 common inotify_rm_watch sys_inotify_rm_watch +29 common ioctl sys_ioctl compat_sys_ioctl +30 common ioprio_set sys_ioprio_set +31 common ioprio_get sys_ioprio_get +32 common flock sys_flock +33 common mknodat sys_mknodat +34 common mkdirat sys_mkdirat +35 common unlinkat sys_unlinkat +36 common symlinkat sys_symlinkat +37 common linkat sys_linkat +# renameat is superseded with flags by renameat2 +38 renameat renameat sys_renameat +39 common umount2 sys_umount +40 common mount sys_mount +41 common pivot_root sys_pivot_root +42 common nfsservctl sys_ni_syscall +43 32 statfs64 sys_statfs64 compat_sys_statfs64 +43 64 statfs sys_statfs +44 32 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 +44 64 fstatfs sys_fstatfs +45 32 truncate64 sys_truncate64 compat_sys_truncate64 +45 64 truncate sys_truncate +46 32 ftruncate64 sys_ftruncate64 compat_sys_ftruncate64 +46 64 ftruncate sys_ftruncate +47 common fallocate sys_fallocate compat_sys_fallocate +48 common faccessat sys_faccessat +49 common chdir sys_chdir +50 common fchdir sys_fchdir +51 common chroot sys_chroot +52 common fchmod sys_fchmod +53 common fchmodat sys_fchmodat +54 common fchownat sys_fchownat +55 common fchown sys_fchown +56 common openat sys_openat +57 common close sys_close +58 common vhangup sys_vhangup +59 common pipe2 sys_pipe2 +60 common quotactl sys_quotactl +61 common getdents64 sys_getdents64 +62 32 llseek sys_llseek +62 64 lseek sys_lseek +63 common read sys_read +64 common write sys_write +65 common readv sys_readv sys_readv +66 common writev sys_writev sys_writev +67 common pread64 sys_pread64 compat_sys_pread64 +68 common pwrite64 sys_pwrite64 compat_sys_pwrite64 +69 common preadv sys_preadv compat_sys_preadv +70 common pwritev sys_pwritev compat_sys_pwritev +71 32 sendfile64 sys_sendfile64 +71 64 sendfile sys_sendfile64 +72 time32 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32 +72 64 pselect6 sys_pselect6 +73 time32 ppoll sys_ppoll_time32 compat_sys_ppoll_time32 +73 64 ppoll sys_ppoll +74 common signalfd4 sys_signalfd4 compat_sys_signalfd4 +75 common vmsplice sys_vmsplice +76 common splice sys_splice +77 common tee sys_tee +78 common readlinkat sys_readlinkat +79 stat64 fstatat64 sys_fstatat64 +79 64 newfstatat sys_newfstatat +80 stat64 fstat64 sys_fstat64 +80 64 fstat sys_newfstat +81 common sync sys_sync +82 common fsync sys_fsync +83 common fdatasync sys_fdatasync +84 common sync_file_range sys_sync_file_range compat_sys_sync_file_range +85 common timerfd_create sys_timerfd_create +86 time32 timerfd_settime sys_timerfd_settime32 +86 64 timerfd_settime sys_timerfd_settime +87 time32 timerfd_gettime sys_timerfd_gettime32 +87 64 timerfd_gettime sys_timerfd_gettime +88 time32 utimensat sys_utimensat_time32 +88 64 utimensat sys_utimensat +89 common acct sys_acct +90 common capget sys_capget +91 common capset sys_capset +92 common personality sys_personality +93 common exit sys_exit +94 common exit_group sys_exit_group +95 common waitid sys_waitid compat_sys_waitid +96 common set_tid_address sys_set_tid_address +97 common unshare sys_unshare +98 time32 futex sys_futex_time32 +98 64 futex sys_futex +99 common set_robust_list sys_set_robust_list compat_sys_set_robust_list +100 common get_robust_list sys_get_robust_list compat_sys_get_robust_list +101 time32 nanosleep sys_nanosleep_time32 +101 64 nanosleep sys_nanosleep +102 common getitimer sys_getitimer compat_sys_getitimer +103 common setitimer sys_setitimer compat_sys_setitimer +104 common kexec_load sys_kexec_load compat_sys_kexec_load +105 common init_module sys_init_module +106 common delete_module sys_delete_module +107 common timer_create sys_timer_create compat_sys_timer_create +108 time32 timer_gettime sys_timer_gettime32 +108 64 timer_gettime sys_timer_gettime +109 common timer_getoverrun sys_timer_getoverrun +110 time32 timer_settime sys_timer_settime32 +110 64 timer_settime sys_timer_settime +111 common timer_delete sys_timer_delete +112 time32 clock_settime sys_clock_settime32 +112 64 clock_settime sys_clock_settime +113 time32 clock_gettime sys_clock_gettime32 +113 64 clock_gettime sys_clock_gettime +114 time32 clock_getres sys_clock_getres_time32 +114 64 clock_getres sys_clock_getres +115 time32 clock_nanosleep sys_clock_nanosleep_time32 +115 64 clock_nanosleep sys_clock_nanosleep +116 common syslog sys_syslog +117 common ptrace sys_ptrace compat_sys_ptrace +118 common sched_setparam sys_sched_setparam +119 common sched_setscheduler sys_sched_setscheduler +120 common sched_getscheduler sys_sched_getscheduler +121 common sched_getparam sys_sched_getparam +122 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity +123 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity +124 common sched_yield sys_sched_yield +125 common sched_get_priority_max sys_sched_get_priority_max +126 common sched_get_priority_min sys_sched_get_priority_min +127 time32 sched_rr_get_interval sys_sched_rr_get_interval_time32 +127 64 sched_rr_get_interval sys_sched_rr_get_interval +128 common restart_syscall sys_restart_syscall +129 common kill sys_kill +130 common tkill sys_tkill +131 common tgkill sys_tgkill +132 common sigaltstack sys_sigaltstack compat_sys_sigaltstack +133 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend +134 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction +135 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask +136 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending +137 time32 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32 +137 64 rt_sigtimedwait sys_rt_sigtimedwait +138 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo +139 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn +140 common setpriority sys_setpriority +141 common getpriority sys_getpriority +142 common reboot sys_reboot +143 common setregid sys_setregid +144 common setgid sys_setgid +145 common setreuid sys_setreuid +146 common setuid sys_setuid +147 common setresuid sys_setresuid +148 common getresuid sys_getresuid +149 common setresgid sys_setresgid +150 common getresgid sys_getresgid +151 common setfsuid sys_setfsuid +152 common setfsgid sys_setfsgid +153 common times sys_times compat_sys_times +154 common setpgid sys_setpgid +155 common getpgid sys_getpgid +156 common getsid sys_getsid +157 common setsid sys_setsid +158 common getgroups sys_getgroups +159 common setgroups sys_setgroups +160 common uname sys_newuname +161 common sethostname sys_sethostname +162 common setdomainname sys_setdomainname +# getrlimit and setrlimit are superseded with prlimit64 +163 rlimit getrlimit sys_getrlimit compat_sys_getrlimit +164 rlimit setrlimit sys_setrlimit compat_sys_setrlimit +165 common getrusage sys_getrusage compat_sys_getrusage +166 common umask sys_umask +167 common prctl sys_prctl +168 common getcpu sys_getcpu +169 time32 gettimeofday sys_gettimeofday compat_sys_gettimeofday +169 64 gettimeofday sys_gettimeofday +170 time32 settimeofday sys_settimeofday compat_sys_settimeofday +170 64 settimeofday sys_settimeofday +171 time32 adjtimex sys_adjtimex_time32 +171 64 adjtimex sys_adjtimex +172 common getpid sys_getpid +173 common getppid sys_getppid +174 common getuid sys_getuid +175 common geteuid sys_geteuid +176 common getgid sys_getgid +177 common getegid sys_getegid +178 common gettid sys_gettid +179 common sysinfo sys_sysinfo compat_sys_sysinfo +180 common mq_open sys_mq_open compat_sys_mq_open +181 common mq_unlink sys_mq_unlink +182 time32 mq_timedsend sys_mq_timedsend_time32 +182 64 mq_timedsend sys_mq_timedsend +183 time32 mq_timedreceive sys_mq_timedreceive_time32 +183 64 mq_timedreceive sys_mq_timedreceive +184 common mq_notify sys_mq_notify compat_sys_mq_notify +185 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr +186 common msgget sys_msgget +187 common msgctl sys_msgctl compat_sys_msgctl +188 common msgrcv sys_msgrcv compat_sys_msgrcv +189 common msgsnd sys_msgsnd compat_sys_msgsnd +190 common semget sys_semget +191 common semctl sys_semctl compat_sys_semctl +192 time32 semtimedop sys_semtimedop_time32 +192 64 semtimedop sys_semtimedop +193 common semop sys_semop +194 common shmget sys_shmget +195 common shmctl sys_shmctl compat_sys_shmctl +196 common shmat sys_shmat compat_sys_shmat +197 common shmdt sys_shmdt +198 common socket sys_socket +199 common socketpair sys_socketpair +200 common bind sys_bind +201 common listen sys_listen +202 common accept sys_accept +203 common connect sys_connect +204 common getsockname sys_getsockname +205 common getpeername sys_getpeername +206 common sendto sys_sendto +207 common recvfrom sys_recvfrom compat_sys_recvfrom +208 common setsockopt sys_setsockopt sys_setsockopt +209 common getsockopt sys_getsockopt sys_getsockopt +210 common shutdown sys_shutdown +211 common sendmsg sys_sendmsg compat_sys_sendmsg +212 common recvmsg sys_recvmsg compat_sys_recvmsg +213 common readahead sys_readahead compat_sys_readahead +214 common brk sys_brk +215 common munmap sys_munmap +216 common mremap sys_mremap +217 common add_key sys_add_key +218 common request_key sys_request_key +219 common keyctl sys_keyctl compat_sys_keyctl +220 common clone sys_clone +221 common execve sys_execve compat_sys_execve +222 32 mmap2 sys_mmap2 +222 64 mmap sys_mmap +223 32 fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 +223 64 fadvise64 sys_fadvise64_64 +224 common swapon sys_swapon +225 common swapoff sys_swapoff +226 common mprotect sys_mprotect +227 common msync sys_msync +228 common mlock sys_mlock +229 common munlock sys_munlock +230 common mlockall sys_mlockall +231 common munlockall sys_munlockall +232 common mincore sys_mincore +233 common madvise sys_madvise +234 common remap_file_pages sys_remap_file_pages +235 common mbind sys_mbind +236 common get_mempolicy sys_get_mempolicy +237 common set_mempolicy sys_set_mempolicy +238 common migrate_pages sys_migrate_pages +239 common move_pages sys_move_pages +240 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo +241 common perf_event_open sys_perf_event_open +242 common accept4 sys_accept4 +243 time32 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32 +243 64 recvmmsg sys_recvmmsg +# Architectures may provide up to 16 syscalls of their own between 244 and 259 +244 arc cacheflush sys_cacheflush +245 arc arc_settls sys_arc_settls +246 arc arc_gettls sys_arc_gettls +247 arc sysfs sys_sysfs +248 arc arc_usr_cmpxchg sys_arc_usr_cmpxchg + +244 csky set_thread_area sys_set_thread_area +245 csky cacheflush sys_cacheflush + +244 nios2 cacheflush sys_cacheflush + +244 or1k or1k_atomic sys_or1k_atomic + +258 riscv riscv_hwprobe sys_riscv_hwprobe +259 riscv riscv_flush_icache sys_riscv_flush_icache + +260 time32 wait4 sys_wait4 compat_sys_wait4 +260 64 wait4 sys_wait4 +261 common prlimit64 sys_prlimit64 +262 common fanotify_init sys_fanotify_init +263 common fanotify_mark sys_fanotify_mark +264 common name_to_handle_at sys_name_to_handle_at +265 common open_by_handle_at sys_open_by_handle_at +266 time32 clock_adjtime sys_clock_adjtime32 +266 64 clock_adjtime sys_clock_adjtime +267 common syncfs sys_syncfs +268 common setns sys_setns +269 common sendmmsg sys_sendmmsg compat_sys_sendmmsg +270 common process_vm_readv sys_process_vm_readv +271 common process_vm_writev sys_process_vm_writev +272 common kcmp sys_kcmp +273 common finit_module sys_finit_module +274 common sched_setattr sys_sched_setattr +275 common sched_getattr sys_sched_getattr +276 common renameat2 sys_renameat2 +277 common seccomp sys_seccomp +278 common getrandom sys_getrandom +279 common memfd_create sys_memfd_create +280 common bpf sys_bpf +281 common execveat sys_execveat compat_sys_execveat +282 common userfaultfd sys_userfaultfd +283 common membarrier sys_membarrier +284 common mlock2 sys_mlock2 +285 common copy_file_range sys_copy_file_range +286 common preadv2 sys_preadv2 compat_sys_preadv2 +287 common pwritev2 sys_pwritev2 compat_sys_pwritev2 +288 common pkey_mprotect sys_pkey_mprotect +289 common pkey_alloc sys_pkey_alloc +290 common pkey_free sys_pkey_free +291 common statx sys_statx +292 time32 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents +292 64 io_pgetevents sys_io_pgetevents +293 common rseq sys_rseq +294 common kexec_file_load sys_kexec_file_load +# 295 through 402 are unassigned to sync up with generic numbers don't use +403 32 clock_gettime64 sys_clock_gettime +404 32 clock_settime64 sys_clock_settime +405 32 clock_adjtime64 sys_clock_adjtime +406 32 clock_getres_time64 sys_clock_getres +407 32 clock_nanosleep_time64 sys_clock_nanosleep +408 32 timer_gettime64 sys_timer_gettime +409 32 timer_settime64 sys_timer_settime +410 32 timerfd_gettime64 sys_timerfd_gettime +411 32 timerfd_settime64 sys_timerfd_settime +412 32 utimensat_time64 sys_utimensat +413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 +414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 +417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 +418 32 mq_timedsend_time64 sys_mq_timedsend +419 32 mq_timedreceive_time64 sys_mq_timedreceive +420 32 semtimedop_time64 sys_semtimedop +421 32 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64 +422 32 futex_time64 sys_futex +423 32 sched_rr_get_interval_time64 sys_sched_rr_get_interval +424 common pidfd_send_signal sys_pidfd_send_signal +425 common io_uring_setup sys_io_uring_setup +426 common io_uring_enter sys_io_uring_enter +427 common io_uring_register sys_io_uring_register +428 common open_tree sys_open_tree +429 common move_mount sys_move_mount +430 common fsopen sys_fsopen +431 common fsconfig sys_fsconfig +432 common fsmount sys_fsmount +433 common fspick sys_fspick +434 common pidfd_open sys_pidfd_open +435 common clone3 sys_clone3 +436 common close_range sys_close_range +437 common openat2 sys_openat2 +438 common pidfd_getfd sys_pidfd_getfd +439 common faccessat2 sys_faccessat2 +440 common process_madvise sys_process_madvise +441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 +442 common mount_setattr sys_mount_setattr +443 common quotactl_fd sys_quotactl_fd +444 common landlock_create_ruleset sys_landlock_create_ruleset +445 common landlock_add_rule sys_landlock_add_rule +446 common landlock_restrict_self sys_landlock_restrict_self +447 memfd_secret memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/riscv/syscall32_nr.h b/linux-user/riscv/syscall32_nr.h deleted file mode 100644 index 1327d7dffa..0000000000 --- a/linux-user/riscv/syscall32_nr.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_RISCV_SYSCALL32_NR_H -#define LINUX_USER_RISCV_SYSCALL32_NR_H - -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl64 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs64 43 -#define TARGET_NR_fstatfs64 44 -#define TARGET_NR_truncate64 45 -#define TARGET_NR_ftruncate64 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_llseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile64 71 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_fstatat64 79 -#define TARGET_NR_fstat64 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap2 222 -#define TARGET_NR_fadvise64_64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15) -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_clock_gettime64 403 -#define TARGET_NR_clock_settime64 404 -#define TARGET_NR_clock_adjtime64 405 -#define TARGET_NR_clock_getres_time64 406 -#define TARGET_NR_clock_nanosleep_time64 407 -#define TARGET_NR_timer_gettime64 408 -#define TARGET_NR_timer_settime64 409 -#define TARGET_NR_timerfd_gettime64 410 -#define TARGET_NR_timerfd_settime64 411 -#define TARGET_NR_utimensat_time64 412 -#define TARGET_NR_pselect6_time64 413 -#define TARGET_NR_ppoll_time64 414 -#define TARGET_NR_io_pgetevents_time64 416 -#define TARGET_NR_recvmmsg_time64 417 -#define TARGET_NR_mq_timedsend_time64 418 -#define TARGET_NR_mq_timedreceive_time64 419 -#define TARGET_NR_semtimedop_time64 420 -#define TARGET_NR_rt_sigtimedwait_time64 421 -#define TARGET_NR_futex_time64 422 -#define TARGET_NR_sched_rr_get_interval_time64 423 -#define TARGET_NR_pidfd_send_signal 424 -#define TARGET_NR_io_uring_setup 425 -#define TARGET_NR_io_uring_enter 426 -#define TARGET_NR_io_uring_register 427 -#define TARGET_NR_open_tree 428 -#define TARGET_NR_move_mount 429 -#define TARGET_NR_fsopen 430 -#define TARGET_NR_fsconfig 431 -#define TARGET_NR_fsmount 432 -#define TARGET_NR_fspick 433 -#define TARGET_NR_pidfd_open 434 -#define TARGET_NR_clone3 435 -#define TARGET_NR_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_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_RISCV_SYSCALL32_NR_H */ diff --git a/linux-user/riscv/syscall64_nr.h b/linux-user/riscv/syscall64_nr.h deleted file mode 100644 index 6659751933..0000000000 --- a/linux-user/riscv/syscall64_nr.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * This file contains the system call numbers. - * Do not modify. - * This file is generated by scripts/gensyscalls.sh - */ -#ifndef LINUX_USER_RISCV_SYSCALL64_NR_H -#define LINUX_USER_RISCV_SYSCALL64_NR_H - -#define TARGET_NR_io_setup 0 -#define TARGET_NR_io_destroy 1 -#define TARGET_NR_io_submit 2 -#define TARGET_NR_io_cancel 3 -#define TARGET_NR_io_getevents 4 -#define TARGET_NR_setxattr 5 -#define TARGET_NR_lsetxattr 6 -#define TARGET_NR_fsetxattr 7 -#define TARGET_NR_getxattr 8 -#define TARGET_NR_lgetxattr 9 -#define TARGET_NR_fgetxattr 10 -#define TARGET_NR_listxattr 11 -#define TARGET_NR_llistxattr 12 -#define TARGET_NR_flistxattr 13 -#define TARGET_NR_removexattr 14 -#define TARGET_NR_lremovexattr 15 -#define TARGET_NR_fremovexattr 16 -#define TARGET_NR_getcwd 17 -#define TARGET_NR_lookup_dcookie 18 -#define TARGET_NR_eventfd2 19 -#define TARGET_NR_epoll_create1 20 -#define TARGET_NR_epoll_ctl 21 -#define TARGET_NR_epoll_pwait 22 -#define TARGET_NR_dup 23 -#define TARGET_NR_dup3 24 -#define TARGET_NR_fcntl 25 -#define TARGET_NR_inotify_init1 26 -#define TARGET_NR_inotify_add_watch 27 -#define TARGET_NR_inotify_rm_watch 28 -#define TARGET_NR_ioctl 29 -#define TARGET_NR_ioprio_set 30 -#define TARGET_NR_ioprio_get 31 -#define TARGET_NR_flock 32 -#define TARGET_NR_mknodat 33 -#define TARGET_NR_mkdirat 34 -#define TARGET_NR_unlinkat 35 -#define TARGET_NR_symlinkat 36 -#define TARGET_NR_linkat 37 -#define TARGET_NR_umount2 39 -#define TARGET_NR_mount 40 -#define TARGET_NR_pivot_root 41 -#define TARGET_NR_nfsservctl 42 -#define TARGET_NR_statfs 43 -#define TARGET_NR_fstatfs 44 -#define TARGET_NR_truncate 45 -#define TARGET_NR_ftruncate 46 -#define TARGET_NR_fallocate 47 -#define TARGET_NR_faccessat 48 -#define TARGET_NR_chdir 49 -#define TARGET_NR_fchdir 50 -#define TARGET_NR_chroot 51 -#define TARGET_NR_fchmod 52 -#define TARGET_NR_fchmodat 53 -#define TARGET_NR_fchownat 54 -#define TARGET_NR_fchown 55 -#define TARGET_NR_openat 56 -#define TARGET_NR_close 57 -#define TARGET_NR_vhangup 58 -#define TARGET_NR_pipe2 59 -#define TARGET_NR_quotactl 60 -#define TARGET_NR_getdents64 61 -#define TARGET_NR_lseek 62 -#define TARGET_NR_read 63 -#define TARGET_NR_write 64 -#define TARGET_NR_readv 65 -#define TARGET_NR_writev 66 -#define TARGET_NR_pread64 67 -#define TARGET_NR_pwrite64 68 -#define TARGET_NR_preadv 69 -#define TARGET_NR_pwritev 70 -#define TARGET_NR_sendfile 71 -#define TARGET_NR_pselect6 72 -#define TARGET_NR_ppoll 73 -#define TARGET_NR_signalfd4 74 -#define TARGET_NR_vmsplice 75 -#define TARGET_NR_splice 76 -#define TARGET_NR_tee 77 -#define TARGET_NR_readlinkat 78 -#define TARGET_NR_newfstatat 79 -#define TARGET_NR_fstat 80 -#define TARGET_NR_sync 81 -#define TARGET_NR_fsync 82 -#define TARGET_NR_fdatasync 83 -#define TARGET_NR_sync_file_range 84 -#define TARGET_NR_timerfd_create 85 -#define TARGET_NR_timerfd_settime 86 -#define TARGET_NR_timerfd_gettime 87 -#define TARGET_NR_utimensat 88 -#define TARGET_NR_acct 89 -#define TARGET_NR_capget 90 -#define TARGET_NR_capset 91 -#define TARGET_NR_personality 92 -#define TARGET_NR_exit 93 -#define TARGET_NR_exit_group 94 -#define TARGET_NR_waitid 95 -#define TARGET_NR_set_tid_address 96 -#define TARGET_NR_unshare 97 -#define TARGET_NR_futex 98 -#define TARGET_NR_set_robust_list 99 -#define TARGET_NR_get_robust_list 100 -#define TARGET_NR_nanosleep 101 -#define TARGET_NR_getitimer 102 -#define TARGET_NR_setitimer 103 -#define TARGET_NR_kexec_load 104 -#define TARGET_NR_init_module 105 -#define TARGET_NR_delete_module 106 -#define TARGET_NR_timer_create 107 -#define TARGET_NR_timer_gettime 108 -#define TARGET_NR_timer_getoverrun 109 -#define TARGET_NR_timer_settime 110 -#define TARGET_NR_timer_delete 111 -#define TARGET_NR_clock_settime 112 -#define TARGET_NR_clock_gettime 113 -#define TARGET_NR_clock_getres 114 -#define TARGET_NR_clock_nanosleep 115 -#define TARGET_NR_syslog 116 -#define TARGET_NR_ptrace 117 -#define TARGET_NR_sched_setparam 118 -#define TARGET_NR_sched_setscheduler 119 -#define TARGET_NR_sched_getscheduler 120 -#define TARGET_NR_sched_getparam 121 -#define TARGET_NR_sched_setaffinity 122 -#define TARGET_NR_sched_getaffinity 123 -#define TARGET_NR_sched_yield 124 -#define TARGET_NR_sched_get_priority_max 125 -#define TARGET_NR_sched_get_priority_min 126 -#define TARGET_NR_sched_rr_get_interval 127 -#define TARGET_NR_restart_syscall 128 -#define TARGET_NR_kill 129 -#define TARGET_NR_tkill 130 -#define TARGET_NR_tgkill 131 -#define TARGET_NR_sigaltstack 132 -#define TARGET_NR_rt_sigsuspend 133 -#define TARGET_NR_rt_sigaction 134 -#define TARGET_NR_rt_sigprocmask 135 -#define TARGET_NR_rt_sigpending 136 -#define TARGET_NR_rt_sigtimedwait 137 -#define TARGET_NR_rt_sigqueueinfo 138 -#define TARGET_NR_rt_sigreturn 139 -#define TARGET_NR_setpriority 140 -#define TARGET_NR_getpriority 141 -#define TARGET_NR_reboot 142 -#define TARGET_NR_setregid 143 -#define TARGET_NR_setgid 144 -#define TARGET_NR_setreuid 145 -#define TARGET_NR_setuid 146 -#define TARGET_NR_setresuid 147 -#define TARGET_NR_getresuid 148 -#define TARGET_NR_setresgid 149 -#define TARGET_NR_getresgid 150 -#define TARGET_NR_setfsuid 151 -#define TARGET_NR_setfsgid 152 -#define TARGET_NR_times 153 -#define TARGET_NR_setpgid 154 -#define TARGET_NR_getpgid 155 -#define TARGET_NR_getsid 156 -#define TARGET_NR_setsid 157 -#define TARGET_NR_getgroups 158 -#define TARGET_NR_setgroups 159 -#define TARGET_NR_uname 160 -#define TARGET_NR_sethostname 161 -#define TARGET_NR_setdomainname 162 -#define TARGET_NR_getrlimit 163 -#define TARGET_NR_setrlimit 164 -#define TARGET_NR_getrusage 165 -#define TARGET_NR_umask 166 -#define TARGET_NR_prctl 167 -#define TARGET_NR_getcpu 168 -#define TARGET_NR_gettimeofday 169 -#define TARGET_NR_settimeofday 170 -#define TARGET_NR_adjtimex 171 -#define TARGET_NR_getpid 172 -#define TARGET_NR_getppid 173 -#define TARGET_NR_getuid 174 -#define TARGET_NR_geteuid 175 -#define TARGET_NR_getgid 176 -#define TARGET_NR_getegid 177 -#define TARGET_NR_gettid 178 -#define TARGET_NR_sysinfo 179 -#define TARGET_NR_mq_open 180 -#define TARGET_NR_mq_unlink 181 -#define TARGET_NR_mq_timedsend 182 -#define TARGET_NR_mq_timedreceive 183 -#define TARGET_NR_mq_notify 184 -#define TARGET_NR_mq_getsetattr 185 -#define TARGET_NR_msgget 186 -#define TARGET_NR_msgctl 187 -#define TARGET_NR_msgrcv 188 -#define TARGET_NR_msgsnd 189 -#define TARGET_NR_semget 190 -#define TARGET_NR_semctl 191 -#define TARGET_NR_semtimedop 192 -#define TARGET_NR_semop 193 -#define TARGET_NR_shmget 194 -#define TARGET_NR_shmctl 195 -#define TARGET_NR_shmat 196 -#define TARGET_NR_shmdt 197 -#define TARGET_NR_socket 198 -#define TARGET_NR_socketpair 199 -#define TARGET_NR_bind 200 -#define TARGET_NR_listen 201 -#define TARGET_NR_accept 202 -#define TARGET_NR_connect 203 -#define TARGET_NR_getsockname 204 -#define TARGET_NR_getpeername 205 -#define TARGET_NR_sendto 206 -#define TARGET_NR_recvfrom 207 -#define TARGET_NR_setsockopt 208 -#define TARGET_NR_getsockopt 209 -#define TARGET_NR_shutdown 210 -#define TARGET_NR_sendmsg 211 -#define TARGET_NR_recvmsg 212 -#define TARGET_NR_readahead 213 -#define TARGET_NR_brk 214 -#define TARGET_NR_munmap 215 -#define TARGET_NR_mremap 216 -#define TARGET_NR_add_key 217 -#define TARGET_NR_request_key 218 -#define TARGET_NR_keyctl 219 -#define TARGET_NR_clone 220 -#define TARGET_NR_execve 221 -#define TARGET_NR_mmap 222 -#define TARGET_NR_fadvise64 223 -#define TARGET_NR_swapon 224 -#define TARGET_NR_swapoff 225 -#define TARGET_NR_mprotect 226 -#define TARGET_NR_msync 227 -#define TARGET_NR_mlock 228 -#define TARGET_NR_munlock 229 -#define TARGET_NR_mlockall 230 -#define TARGET_NR_munlockall 231 -#define TARGET_NR_mincore 232 -#define TARGET_NR_madvise 233 -#define TARGET_NR_remap_file_pages 234 -#define TARGET_NR_mbind 235 -#define TARGET_NR_get_mempolicy 236 -#define TARGET_NR_set_mempolicy 237 -#define TARGET_NR_migrate_pages 238 -#define TARGET_NR_move_pages 239 -#define TARGET_NR_rt_tgsigqueueinfo 240 -#define TARGET_NR_perf_event_open 241 -#define TARGET_NR_accept4 242 -#define TARGET_NR_recvmmsg 243 -#define TARGET_NR_arch_specific_syscall 244 -#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15) -#define TARGET_NR_wait4 260 -#define TARGET_NR_prlimit64 261 -#define TARGET_NR_fanotify_init 262 -#define TARGET_NR_fanotify_mark 263 -#define TARGET_NR_name_to_handle_at 264 -#define TARGET_NR_open_by_handle_at 265 -#define TARGET_NR_clock_adjtime 266 -#define TARGET_NR_syncfs 267 -#define TARGET_NR_setns 268 -#define TARGET_NR_sendmmsg 269 -#define TARGET_NR_process_vm_readv 270 -#define TARGET_NR_process_vm_writev 271 -#define TARGET_NR_kcmp 272 -#define TARGET_NR_finit_module 273 -#define TARGET_NR_sched_setattr 274 -#define TARGET_NR_sched_getattr 275 -#define TARGET_NR_renameat2 276 -#define TARGET_NR_seccomp 277 -#define TARGET_NR_getrandom 278 -#define TARGET_NR_memfd_create 279 -#define TARGET_NR_bpf 280 -#define TARGET_NR_execveat 281 -#define TARGET_NR_userfaultfd 282 -#define TARGET_NR_membarrier 283 -#define TARGET_NR_mlock2 284 -#define TARGET_NR_copy_file_range 285 -#define TARGET_NR_preadv2 286 -#define TARGET_NR_pwritev2 287 -#define TARGET_NR_pkey_mprotect 288 -#define TARGET_NR_pkey_alloc 289 -#define TARGET_NR_pkey_free 290 -#define TARGET_NR_statx 291 -#define TARGET_NR_io_pgetevents 292 -#define TARGET_NR_rseq 293 -#define TARGET_NR_kexec_file_load 294 -#define TARGET_NR_pidfd_send_signal 424 -#define TARGET_NR_io_uring_setup 425 -#define TARGET_NR_io_uring_enter 426 -#define TARGET_NR_io_uring_register 427 -#define TARGET_NR_open_tree 428 -#define TARGET_NR_move_mount 429 -#define TARGET_NR_fsopen 430 -#define TARGET_NR_fsconfig 431 -#define TARGET_NR_fsmount 432 -#define TARGET_NR_fspick 433 -#define TARGET_NR_pidfd_open 434 -#define TARGET_NR_clone3 435 -#define TARGET_NR_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_landlock_create_ruleset 444 -#define TARGET_NR_landlock_add_rule 445 -#define TARGET_NR_landlock_restrict_self 446 -#define TARGET_NR_syscalls 447 - -#endif /* LINUX_USER_RISCV_SYSCALL64_NR_H */ diff --git a/linux-user/riscv/syscall_nr.h b/linux-user/riscv/syscall_nr.h deleted file mode 100644 index 0a5a2f2fb1..0000000000 --- a/linux-user/riscv/syscall_nr.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Syscall numbers from asm-generic, common for most - * of recently-added arches including RISC-V. - */ - -#ifndef LINUX_USER_RISCV_SYSCALL_NR_H -#define LINUX_USER_RISCV_SYSCALL_NR_H - -#ifdef TARGET_RISCV32 -# include "syscall32_nr.h" -#else -# include "syscall64_nr.h" -#endif - -#endif diff --git a/linux-user/riscv/syscallhdr.sh b/linux-user/riscv/syscallhdr.sh new file mode 100644 index 0000000000..4069dc59b6 --- /dev/null +++ b/linux-user/riscv/syscallhdr.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in="$1" +out="$2" +my_abis=`echo "($3)" | tr ',' '|'` +prefix="$4" +offset="$5" + +fileguard=LINUX_USER_X86_64_`basename "$out" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'` +grep -E "^[0-9A-Fa-fXx]+[[:space:]]+${my_abis}" "$in" | sort -n | ( + echo "#ifndef ${fileguard}" + echo "#define ${fileguard} 1" + echo "" + + while read nr abi name entry compat ; do + if [ -z "$offset" ]; then + echo "#define TARGET_NR_${prefix}${name} $nr" + else + echo "#define TARGET_NR_${prefix}${name} ($offset + $nr)" + fi + done + + echo "" + echo "#endif /* ${fileguard} */" +) > "$out" diff --git a/linux-user/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 index e7ba6070fe..3049bcc67d 100644 --- a/linux-user/riscv/target_mman.h +++ b/linux-user/riscv/target_mman.h @@ -1 +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/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 f72165576f..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. @@ -268,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; diff --git a/linux-user/s390x/syscall.tbl b/linux-user/s390x/syscall.tbl index 0690263df1..8e0d1f1c2a 100644 --- a/linux-user/s390x/syscall.tbl +++ b/linux-user/s390x/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # System call table for s390 # @@ -100,7 +100,7 @@ 106 common stat sys_newstat compat_sys_newstat 107 common lstat sys_newlstat compat_sys_newlstat 108 common fstat sys_newfstat compat_sys_newfstat -110 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +110 common lookup_dcookie - - 111 common vhangup sys_vhangup sys_vhangup 112 common idle - - 114 common wait4 sys_wait4 compat_sys_wait4 @@ -122,7 +122,7 @@ 131 common quotactl sys_quotactl sys_quotactl 132 common getpgid sys_getpgid sys_getpgid 133 common fchdir sys_fchdir sys_fchdir -134 common bdflush sys_bdflush sys_bdflush +134 common bdflush sys_ni_syscall sys_ni_syscall 135 common sysfs sys_sysfs sys_sysfs 136 common personality sys_s390_personality sys_s390_personality 137 common afs_syscall - - @@ -274,9 +274,9 @@ 265 common statfs64 sys_statfs64 compat_sys_statfs64 266 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 267 common remap_file_pages sys_remap_file_pages sys_remap_file_pages -268 common mbind sys_mbind compat_sys_mbind -269 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy -270 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +268 common mbind sys_mbind sys_mbind +269 common get_mempolicy sys_get_mempolicy sys_get_mempolicy +270 common set_mempolicy sys_set_mempolicy sys_set_mempolicy 271 common mq_open sys_mq_open compat_sys_mq_open 272 common mq_unlink sys_mq_unlink sys_mq_unlink 273 common mq_timedsend sys_mq_timedsend sys_mq_timedsend_time32 @@ -293,7 +293,7 @@ 284 common inotify_init sys_inotify_init sys_inotify_init 285 common inotify_add_watch sys_inotify_add_watch sys_inotify_add_watch 286 common inotify_rm_watch sys_inotify_rm_watch sys_inotify_rm_watch -287 common migrate_pages sys_migrate_pages compat_sys_migrate_pages +287 common migrate_pages sys_migrate_pages sys_migrate_pages 288 common openat sys_openat compat_sys_openat 289 common mkdirat sys_mkdirat sys_mkdirat 290 common mknodat sys_mknodat sys_mknodat @@ -317,7 +317,7 @@ 307 common sync_file_range sys_sync_file_range compat_sys_s390_sync_file_range 308 common tee sys_tee sys_tee 309 common vmsplice sys_vmsplice sys_vmsplice -310 common move_pages sys_move_pages compat_sys_move_pages +310 common move_pages sys_move_pages sys_move_pages 311 common getcpu sys_getcpu sys_getcpu 312 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 313 common utimes sys_utimes sys_utimes_time32 @@ -418,7 +418,7 @@ 412 32 utimensat_time64 - sys_utimensat 413 32 pselect6_time64 - compat_sys_pselect6_time64 414 32 ppoll_time64 - compat_sys_ppoll_time64 -416 32 io_pgetevents_time64 - sys_io_pgetevents +416 32 io_pgetevents_time64 - compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 - compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 - sys_mq_timedsend 419 32 mq_timedreceive_time64 - sys_mq_timedreceive @@ -445,7 +445,23 @@ 440 common process_madvise sys_process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self sys_landlock_restrict_self +447 common memfd_secret sys_memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue sys_futex_requeue +457 common statmount sys_statmount sys_statmount +458 common listmount sys_listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal sys_mseal diff --git a/linux-user/s390x/syscallhdr.sh b/linux-user/s390x/syscallhdr.sh index 85a99c48de..ac22d422b0 100755 --- a/linux-user/s390x/syscallhdr.sh +++ b/linux-user/s390x/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/s390x/target_mman.h b/linux-user/s390x/target_mman.h index e7ba6070fe..c82435e381 100644 --- a/linux-user/s390x/target_mman.h +++ b/linux-user/s390x/target_mman.h @@ -1 +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/sh4/signal.c b/linux-user/sh4/signal.c index c4ba962708..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; } } @@ -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); diff --git a/linux-user/sh4/syscall.tbl b/linux-user/sh4/syscall.tbl index 0b91499ebd..cf4ec0493d 100644 --- a/linux-user/sh4/syscall.tbl +++ b/linux-user/sh4/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for sh # @@ -141,7 +141,7 @@ 131 common quotactl sys_quotactl 132 common getpgid sys_getpgid 133 common fchdir sys_fchdir -134 common bdflush sys_bdflush +134 common bdflush sys_ni_syscall 135 common sysfs sys_sysfs 136 common personality sys_personality # 137 was afs_syscall @@ -260,7 +260,7 @@ 250 common fadvise64 sys_fadvise64 # 251 is unused 252 common exit_group sys_exit_group -253 common lookup_dcookie sys_lookup_dcookie +253 common lookup_dcookie sys_ni_syscall 254 common epoll_create sys_epoll_create 255 common epoll_ctl sys_epoll_ctl 256 common epoll_wait sys_epoll_wait @@ -321,7 +321,7 @@ 311 common set_robust_list sys_set_robust_list 312 common get_robust_list sys_get_robust_list 313 common splice sys_splice -314 common sync_file_range sys_sync_file_range +314 common sync_file_range sys_sh_sync_file_range6 315 common tee sys_tee 316 common vmsplice sys_vmsplice 317 common move_pages sys_move_pages @@ -395,6 +395,7 @@ 385 common pkey_alloc sys_pkey_alloc 386 common pkey_free sys_pkey_free 387 common rseq sys_rseq +388 common sync_file_range2 sys_sync_file_range2 # room for arch specific syscalls 393 common semget sys_semget 394 common semctl sys_semctl @@ -445,7 +446,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/sh4/syscallhdr.sh b/linux-user/sh4/syscallhdr.sh index 080790556a..cb3a5de711 100644 --- a/linux-user/sh4/syscallhdr.sh +++ b/linux-user/sh4/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" 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 index e7ba6070fe..dd9016081e 100644 --- a/linux-user/sh4/target_mman.h +++ b/linux-user/sh4/target_mman.h @@ -1 +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 3e2dc604c2..8584d9ecc2 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); @@ -58,7 +56,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUArchState *env); void process_pending_signals(CPUArchState *cpu_env); -void signal_init(void); +void signal_init(const char *rtsig_map); void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info); void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info); @@ -113,7 +111,7 @@ 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; } } diff --git a/linux-user/signal.c b/linux-user/signal.c index 61c6fa3fcf..9b6d772882 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -18,7 +18,9 @@ */ #include "qemu/osdep.h" #include "qemu/bitops.h" -#include "exec/gdbstub.h" +#include "qemu/cutils.h" +#include "gdbstub/user.h" +#include "exec/page-protection.h" #include "hw/core/tcg-cpu-ops.h" #include @@ -32,6 +34,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 +49,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. */ @@ -56,7 +61,6 @@ static uint8_t host_to_target_signal_table[_NSIG] = { #define MAKE_SIG_ENTRY(sig) [sig] = TARGET_##sig, MAKE_SIGNAL_LIST #undef MAKE_SIG_ENTRY - /* next signals stay the same */ }; static uint8_t target_to_host_signal_table[TARGET_NSIG + 1]; @@ -64,18 +68,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]; } @@ -167,7 +177,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 @@ -189,7 +199,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; @@ -232,7 +242,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; } @@ -241,7 +251,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); @@ -249,7 +259,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); @@ -260,7 +270,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; @@ -270,7 +280,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); @@ -279,7 +289,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; @@ -404,8 +414,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); @@ -487,26 +497,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) { @@ -524,62 +514,140 @@ static int core_dump_signal(int sig) } } -static void signal_table_init(void) +static void signal_table_init(const char *rtsig_map) { - 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. - * Attempts for configure "missing" signals via sigaction will be - * silently ignored. - */ - for (host_sig = SIGRTMIN; host_sig <= SIGRTMAX; host_sig++) { - target_sig = host_sig - SIGRTMIN + TARGET_SIGRTMIN; - if (target_sig <= TARGET_NSIG) { - host_to_target_signal_table[host_sig] = target_sig; - } - } + if (rtsig_map) { + /* + * Map host RT signals to target RT signals according to the + * user-provided specification. + */ + const char *s = rtsig_map; - /* generate signal conversion tables */ - for (target_sig = 1; target_sig <= TARGET_NSIG; target_sig++) { - target_to_host_signal_table[target_sig] = _NSIG; /* poison */ - } - for (host_sig = 1; host_sig < _NSIG; host_sig++) { - if (host_to_target_signal_table[host_sig] == 0) { - host_to_target_signal_table[host_sig] = host_sig; - } - target_sig = host_to_target_signal_table[host_sig]; - if (target_sig <= TARGET_NSIG) { - target_to_host_signal_table[target_sig] = host_sig; - } - } + while (true) { + int i; - 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++; + if (qemu_strtoi(s, &s, 10, &tsig) || *s++ != ' ') { + fprintf(stderr, "Malformed target signal in QEMU_RTSIG_MAP\n"); + exit(EXIT_FAILURE); + } + if (qemu_strtoi(s, &s, 10, &hsig) || *s++ != ' ') { + fprintf(stderr, "Malformed host signal in QEMU_RTSIG_MAP\n"); + exit(EXIT_FAILURE); + } + if (qemu_strtoi(s, &s, 10, &count) || (*s && *s != ',')) { + fprintf(stderr, "Malformed signal count in QEMU_RTSIG_MAP\n"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < count; i++, tsig++, hsig++) { + if (tsig < TARGET_SIGRTMIN || tsig > TARGET_NSIG) { + fprintf(stderr, "%d is not a target rt signal\n", tsig); + exit(EXIT_FAILURE); + } + if (hsig < SIGRTMIN || hsig > SIGRTMAX) { + fprintf(stderr, "%d is not a host rt signal\n", hsig); + exit(EXIT_FAILURE); + } + if (host_to_target_signal_table[hsig]) { + fprintf(stderr, "%d already maps %d\n", + hsig, host_to_target_signal_table[hsig]); + exit(EXIT_FAILURE); + } + host_to_target_signal_table[hsig] = tsig; + } + + if (*s) { + s++; + } else { + break; } } - trace_signal_table_init(count); + } else { + /* + * Default host-to-target RT signal mapping. + * + * Signals are supported starting from TARGET_SIGRTMIN and going up + * 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. + * + * Reserve one signal for internal usage (see below). + */ + + hsig = SIGRTMIN + 1; + for (tsig = TARGET_SIGRTMIN; + hsig <= SIGRTMAX && tsig <= TARGET_NSIG; + hsig++, tsig++) { + host_to_target_signal_table[hsig] = tsig; + } } + + /* + * 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. + */ + + host_to_target_signal_table[SIGABRT] = 0; + for (hsig = SIGRTMIN; hsig <= SIGRTMAX; hsig++) { + if (!host_to_target_signal_table[hsig]) { + host_to_target_signal_table[hsig] = TARGET_SIGABRT; + break; + } + } + if (hsig > SIGRTMAX) { + fprintf(stderr, "No rt signals left for SIGABRT mapping\n"); + exit(EXIT_FAILURE); + } + + /* Invert the mapping that has already been assigned. */ + for (hsig = 1; hsig < _NSIG; hsig++) { + tsig = host_to_target_signal_table[hsig]; + if (tsig) { + if (target_to_host_signal_table[tsig]) { + fprintf(stderr, "%d is already mapped to %d\n", + tsig, target_to_host_signal_table[tsig]); + exit(EXIT_FAILURE); + } + target_to_host_signal_table[tsig] = hsig; + } + } + + 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; + } + } + 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++; + } + } + + trace_signal_table_init(count); } -void signal_init(void) +void signal_init(const char *rtsig_map) { - 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(); + signal_table_init(rtsig_map); /* Set the signal mask from the host mask. */ sigprocmask(0, 0, &ts->signal_mask); @@ -587,27 +655,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; } } @@ -618,7 +695,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; @@ -626,7 +702,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); } /* @@ -636,14 +712,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 @@ -666,7 +741,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); @@ -682,7 +757,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); @@ -695,15 +770,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); @@ -724,28 +829,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 @@ -754,7 +839,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); @@ -779,72 +864,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 */ @@ -885,6 +1093,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); @@ -940,7 +1149,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; @@ -1000,22 +1208,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); } @@ -1029,15 +1242,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; @@ -1047,7 +1272,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) { @@ -1058,12 +1283,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; @@ -1113,7 +1338,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; @@ -1173,7 +1398,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..8181b8b92c 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 @@ -528,11 +546,6 @@ long do_rt_sigreturn(CPUSPARCState *env) typedef abi_ulong target_mc_greg_t; typedef target_mc_greg_t target_mc_gregset_t[SPARC_MC_NGREG]; -struct target_mc_fq { - abi_ulong mcfq_addr; - uint32_t mcfq_insn; -}; - /* * Note the manual 16-alignment; the kernel gets this because it * includes a "long double qregs[16]" in the mcpu_fregs union, @@ -575,7 +588,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 +659,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 +688,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 +788,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/syscall.tbl b/linux-user/sparc/syscall.tbl index e34cc30ef2..3bc83783b7 100644 --- a/linux-user/sparc/syscall.tbl +++ b/linux-user/sparc/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for sparc # @@ -117,7 +117,7 @@ 90 common dup2 sys_dup2 91 32 setfsuid32 sys_setfsuid 92 common fcntl sys_fcntl compat_sys_fcntl -93 common select sys_select +93 common select sys_select compat_sys_select 94 32 setfsgid32 sys_setfsgid 95 common fsync sys_fsync 96 common setpriority sys_setpriority @@ -155,7 +155,7 @@ 123 32 fchown sys_fchown16 123 64 fchown sys_fchown 124 common fchmod sys_fchmod -125 common recvfrom sys_recvfrom +125 common recvfrom sys_recvfrom compat_sys_recvfrom 126 32 setreuid sys_setreuid16 126 64 setreuid sys_setreuid 127 32 setregid sys_setregid16 @@ -247,9 +247,9 @@ 204 32 readdir sys_old_readdir compat_sys_old_readdir 204 64 readdir sys_nis_syscall 205 common readahead sys_readahead compat_sys_readahead -206 common socketcall sys_socketcall sys32_socketcall +206 common socketcall sys_socketcall compat_sys_socketcall 207 common syslog sys_syslog -208 common lookup_dcookie sys_lookup_dcookie compat_sys_lookup_dcookie +208 common lookup_dcookie sys_ni_syscall 209 common fadvise64 sys_fadvise64 compat_sys_fadvise64 210 common fadvise64_64 sys_fadvise64_64 compat_sys_fadvise64_64 211 common tgkill sys_tgkill @@ -270,7 +270,7 @@ 222 common delete_module sys_delete_module 223 common get_kernel_syms sys_ni_syscall 224 common getpgid sys_getpgid -225 common bdflush sys_bdflush +225 common bdflush sys_ni_syscall 226 common sysfs sys_sysfs 227 common afs_syscall sys_nis_syscall 228 common setfsuid sys_setfsuid16 @@ -365,12 +365,12 @@ 299 common unshare sys_unshare 300 common set_robust_list sys_set_robust_list compat_sys_set_robust_list 301 common get_robust_list sys_get_robust_list compat_sys_get_robust_list -302 common migrate_pages sys_migrate_pages compat_sys_migrate_pages -303 common mbind sys_mbind compat_sys_mbind -304 common get_mempolicy sys_get_mempolicy compat_sys_get_mempolicy -305 common set_mempolicy sys_set_mempolicy compat_sys_set_mempolicy +302 common migrate_pages sys_migrate_pages +303 common mbind sys_mbind +304 common get_mempolicy sys_get_mempolicy +305 common set_mempolicy sys_set_mempolicy 306 common kexec_load sys_kexec_load compat_sys_kexec_load -307 common move_pages sys_move_pages compat_sys_move_pages +307 common move_pages sys_move_pages 308 common getcpu sys_getcpu 309 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait 310 32 utimensat sys_utimensat_time32 @@ -461,7 +461,7 @@ 412 32 utimensat_time64 sys_utimensat sys_utimensat 413 32 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64 414 32 ppoll_time64 sys_ppoll compat_sys_ppoll_time64 -416 32 io_pgetevents_time64 sys_io_pgetevents sys_io_pgetevents +416 32 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64 417 32 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64 418 32 mq_timedsend_time64 sys_mq_timedsend sys_mq_timedsend 419 32 mq_timedreceive_time64 sys_mq_timedreceive sys_mq_timedreceive @@ -488,7 +488,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/sparc/syscallhdr.sh b/linux-user/sparc/syscallhdr.sh index 34a99dc832..938a02bb48 100644 --- a/linux-user/sparc/syscallhdr.sh +++ b/linux-user/sparc/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" 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 index e7ba6070fe..696ca73fe4 100644 --- a/linux-user/sparc/target_mman.h +++ b/linux-user/sparc/target_mman.h @@ -1 +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 9ae5a812cd..3b744ccd4a 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -13,6 +13,9 @@ #include #include #include +#ifdef HAVE_OPENAT2_H +#include +#endif #include #include "qemu.h" #include "user-internals.h" @@ -46,15 +49,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 { @@ -81,6 +90,7 @@ 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); @@ -150,20 +160,21 @@ static const char * const target_signal_name[] = { #undef MAKE_SIG_ENTRY }; +static void +print_signal_1(abi_ulong arg) +{ + if (arg < ARRAY_SIZE(target_signal_name)) { + qemu_log("%s", target_signal_name[arg]); + } else { + qemu_log(TARGET_ABI_FMT_lu, arg); + } +} + static void print_signal(abi_ulong arg, int last) { - const char *signal_name = NULL; - - if (arg < ARRAY_SIZE(target_signal_name)) { - signal_name = target_signal_name[arg]; - } - - if (signal_name == NULL) { - print_raw_param("%ld", arg, last); - return; - } - qemu_log("%s%s", signal_name, get_comma(last)); + print_signal_1(arg); + qemu_log("%s", get_comma(last)); } static void print_si_code(int arg) @@ -360,14 +371,13 @@ 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) && un->sun_path[i]; i++) { qemu_log("%c", un->sun_path[i]); } - qemu_log("\"}"); + qemu_log("\"},"); break; } case AF_INET: { @@ -377,7 +387,7 @@ print_sockaddr(abi_ulong addr, abi_long addrlen, int last) ntohs(in->sin_port)); qemu_log("sin_addr=inet_addr(\"%d.%d.%d.%d\")", c[0], c[1], c[2], c[3]); - qemu_log("}"); + qemu_log("},"); break; } case AF_PACKET: { @@ -408,12 +418,12 @@ print_sockaddr(abi_ulong addr, abi_long addrlen, int last) } qemu_log(",sll_addr=%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); - qemu_log("}"); + qemu_log("},"); break; } case AF_NETLINK: { struct target_sockaddr_nl *nl = (struct target_sockaddr_nl *)sa; - qemu_log("{nl_family=AF_NETLINK,nl_pid=%u,nl_groups=%u}", + qemu_log("{nl_family=AF_NETLINK,nl_pid=%u,nl_groups=%u},", tswap32(nl->nl_pid), tswap32(nl->nl_groups)); break; } @@ -423,14 +433,14 @@ print_sockaddr(abi_ulong addr, abi_long addrlen, int last) qemu_log("%02x, ", sa->sa_data[i]); } qemu_log("%02x}", sa->sa_data[i]); - qemu_log("}"); + qemu_log("},"); break; } unlock_user(sa, addr, 0); } else { - print_raw_param("0x"TARGET_ABI_FMT_lx, addr, 0); + print_pointer(addr, 0); } - qemu_log(", "TARGET_ABI_FMT_ld"%s", addrlen, get_comma(last)); + qemu_log(TARGET_ABI_FMT_ld"%s", addrlen, get_comma(last)); } static void @@ -506,21 +516,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; @@ -603,7 +661,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, @@ -614,38 +671,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 @@ -656,10 +700,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(" @@ -673,6 +719,51 @@ print_ipc(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#ifdef TARGET_NR_rt_sigprocmask +static void print_target_sigset_t_1(target_sigset_t *set, int last) +{ + bool first = true; + int i, sig = 1; + + qemu_log("["); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + abi_ulong bits = 0; + int j; + + __get_user(bits, &set->sig[i]); + for (j = 0; j < sizeof(bits) * 8; j++) { + if (bits & ((abi_ulong)1 << j)) { + if (first) { + first = false; + } else { + qemu_log(" "); + } + print_signal_1(sig); + } + sig++; + } + } + qemu_log("]%s", get_comma(last)); +} + +static void print_target_sigset_t(abi_ulong addr, abi_ulong size, int last) +{ + if (addr && size == sizeof(target_sigset_t)) { + target_sigset_t *set; + + set = lock_user(VERIFY_READ, addr, sizeof(target_sigset_t), 1); + if (set) { + print_target_sigset_t_1(set, last); + unlock_user(set, addr, 0); + } else { + print_pointer(addr, last); + } + } else { + print_pointer(addr, last); + } +} +#endif + /* * Variants for the return value output function */ @@ -945,15 +1036,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 @@ -963,14 +1054,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), @@ -981,19 +1072,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 */ @@ -1019,7 +1112,19 @@ UNUSED static struct flags open_flags[] = { FLAG_END, }; -UNUSED static struct flags mount_flags[] = { +UNUSED static const struct flags openat2_resolve_flags[] = { +#ifdef HAVE_OPENAT2_H + FLAG_GENERIC(RESOLVE_NO_XDEV), + FLAG_GENERIC(RESOLVE_NO_MAGICLINKS), + FLAG_GENERIC(RESOLVE_NO_SYMLINKS), + FLAG_GENERIC(RESOLVE_BENEATH), + FLAG_GENERIC(RESOLVE_IN_ROOT), + FLAG_GENERIC(RESOLVE_CACHED), +#endif + FLAG_END, +}; + +UNUSED static const struct flags mount_flags[] = { #ifdef MS_BIND FLAG_GENERIC(MS_BIND), #endif @@ -1044,7 +1149,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 @@ -1057,8 +1162,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), @@ -1068,35 +1173,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), @@ -1136,7 +1245,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), @@ -1156,7 +1275,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 @@ -1167,18 +1286,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), @@ -1226,7 +1345,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 @@ -1246,7 +1365,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), @@ -1265,7 +1384,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), @@ -1349,7 +1468,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), @@ -1360,7 +1479,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), @@ -1380,7 +1499,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 @@ -1388,6 +1508,7 @@ UNUSED static struct flags mlockall_flags[] = { #endif FLAG_END, }; +#endif /* IDs of the various system clocks */ #define TARGET_CLOCK_REALTIME 0 @@ -1445,14 +1566,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++; } @@ -1599,6 +1716,13 @@ print_buf(abi_long addr, abi_long len, int last) } } +static void +print_buf_len(abi_long addr, abi_long len, int last) +{ + print_buf(addr, len, 0); + print_raw_param(TARGET_ABI_FMT_ld, len, last); +} + /* * Prints out raw parameter using given format. Caller needs * to do byte swapping if needed. @@ -1612,6 +1736,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) { @@ -1688,10 +1825,8 @@ print_timespec64(abi_ulong ts_addr, int last) print_pointer(ts_addr, last); return; } - qemu_log("{tv_sec = %lld" - ",tv_nsec = %lld}%s", - (long long)tswap64(ts->tv_sec), (long long)tswap64(ts->tv_nsec), - get_comma(last)); + 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)); @@ -1969,6 +2104,58 @@ print_execv(CPUArchState *cpu_env, const struct syscallname *name, } #endif +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, @@ -2623,8 +2810,7 @@ static void do_print_sendrecv(const char *name, abi_long arg1) qemu_log("%s(", name); print_sockfd(sockfd, 0); - print_buf(msg, len, 0); - print_raw_param(TARGET_ABI_FMT_ld, len, 0); + print_buf_len(msg, len, 0); print_flags(msg_flags, flags, 1); qemu_log(")"); } @@ -2642,8 +2828,7 @@ static void do_print_msgaddr(const char *name, abi_long arg1) qemu_log("%s(", name); print_sockfd(sockfd, 0); - print_buf(msg, len, 0); - print_raw_param(TARGET_ABI_FMT_ld, len, 0); + print_buf_len(msg, len, 0); print_flags(msg_flags, flags, 0); print_sockaddr(addr, addrlen, 0); qemu_log(")"); @@ -3003,6 +3188,38 @@ print_bind(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#ifdef TARGET_NR_recvfrom +static void +print_recvfrom(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_sockfd(arg0, 0); + print_pointer(arg1, 0); /* output */ + print_raw_param(TARGET_ABI_FMT_ld, arg2, 0); + print_flags(msg_flags, arg3, 0); + print_pointer(arg4, 0); /* output */ + print_pointer(arg5, 1); /* in/out */ + print_syscall_epilogue(name); +} +#endif + +#ifdef TARGET_NR_sendto +static void +print_sendto(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_sockfd(arg0, 0); + print_buf_len(arg1, arg2, 0); + print_flags(msg_flags, arg3, 0); + print_sockaddr(arg4, arg5, 1); + print_syscall_epilogue(name); +} +#endif + #if defined(TARGET_NR_stat) || defined(TARGET_NR_stat64) || \ defined(TARGET_NR_lstat) || defined(TARGET_NR_lstat64) static void @@ -3141,10 +3358,29 @@ print_rt_sigprocmask(CPUArchState *cpu_env, const struct syscallname *name, case TARGET_SIG_SETMASK: how = "SIG_SETMASK"; break; } qemu_log("%s,", how); - print_pointer(arg1, 0); - print_pointer(arg2, 1); + print_target_sigset_t(arg1, arg3, 0); + print_pointer(arg2, 0); + print_raw_param("%u", arg3, 1); print_syscall_epilogue(name); } + +static void +print_rt_sigprocmask_ret(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 (arg2) { + qemu_log(" (oldset="); + print_target_sigset_t(arg2, arg3, 1); + qemu_log(")"); + } + } + + qemu_log("\n"); +} #endif #ifdef TARGET_NR_rt_sigqueueinfo @@ -3363,6 +3599,38 @@ print_openat(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#ifdef TARGET_NR_openat2 +static void +print_openat2(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) +{ + struct open_how_ver0 how; + + print_syscall_prologue(name); + print_at_dirfd(arg0, 0); + print_string(arg1, 0); + + if ((abi_ulong)arg3 >= sizeof(struct target_open_how_ver0) && + copy_struct_from_user(&how, sizeof(how), arg2, arg3) == 0) { + how.flags = tswap64(how.flags); + how.mode = tswap64(how.mode); + how.resolve = tswap64(how.resolve); + qemu_log("{"); + print_open_flags(how.flags, 0); + if (how.flags & TARGET_O_CREAT) { + print_file_mode(how.mode, 0); + } + print_flags(openat2_resolve_flags, how.resolve, 1); + qemu_log("},"); + } else { + print_pointer(arg2, 0); + } + print_raw_param(TARGET_ABI_FMT_lu, arg3, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_pidfd_send_signal static void print_pidfd_send_signal(CPUArchState *cpu_env, const struct syscallname *name, @@ -3666,10 +3934,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); @@ -3679,7 +3961,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, +#ifdef TARGET_ARCH_WANT_SYS_OLD_MMAP + 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 @@ -3771,6 +4080,94 @@ print_futex(CPUArchState *cpu_env, const struct syscallname *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, @@ -3811,6 +4208,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, @@ -3900,6 +4316,63 @@ print_ioctl(CPUArchState *cpu_env, const struct syscallname *name, } #endif +#if defined(TARGET_NR_wait4) || defined(TARGET_NR_waitpid) +static void print_wstatus(int wstatus) +{ + if (WIFSIGNALED(wstatus)) { + qemu_log("{WIFSIGNALED(s) && WTERMSIG(s) == "); + print_signal(WTERMSIG(wstatus), 1); + if (WCOREDUMP(wstatus)) { + qemu_log(" && WCOREDUMP(s)"); + } + qemu_log("}"); + } else if (WIFEXITED(wstatus)) { + qemu_log("{WIFEXITED(s) && WEXITSTATUS(s) == %d}", + WEXITSTATUS(wstatus)); + } else { + print_number(wstatus, 1); + } +} + +static void print_ret_wstatus(abi_long ret, abi_long wstatus_addr) +{ + int wstatus; + + if (!print_syscall_err(ret) + && wstatus_addr + && get_user_s32(wstatus, wstatus_addr)) { + qemu_log(TARGET_ABI_FMT_ld " (wstatus=", ret); + print_wstatus(wstatus); + qemu_log(")"); + } + qemu_log("\n"); +} +#endif + +#ifdef TARGET_NR_wait4 +static void +print_syscall_ret_wait4(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) +{ + print_ret_wstatus(ret, arg1); +} +#endif + +#ifdef TARGET_NR_waitpid +static void +print_syscall_ret_waitpid(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) +{ + print_ret_wstatus(ret, arg1); +} +#endif + /* * An array of all of the syscalls we know about */ @@ -3928,7 +4401,7 @@ print_syscall(CPUArchState *cpu_env, int num, if (!f) { return; } - fprintf(f, "%d ", getpid()); + fprintf(f, "%d ", get_task_state(env_cpu(cpu_env))->ts_tid); for (i = 0; i < nsyscalls; i++) { if (scnames[i].nr == num) { diff --git a/linux-user/strace.list b/linux-user/strace.list index 3a898e2532..fdf94ef32a 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -86,6 +86,9 @@ { TARGET_NR_clock_getres, "clock_getres" , NULL, print_clock_getres, print_syscall_ret_clock_getres }, #endif +#ifdef TARGET_NR_clock_getres_time64 +{ TARGET_NR_clock_getres_time64, "clock_getres_time64" , NULL, NULL, NULL }, +#endif #ifdef TARGET_NR_clock_gettime { TARGET_NR_clock_gettime, "clock_gettime" , NULL, print_clock_gettime, print_syscall_ret_clock_gettime }, @@ -164,7 +167,7 @@ { TARGET_NR_execve, "execve" , NULL, print_execve, NULL }, #endif #ifdef TARGET_NR_execveat -{ TARGET_NR_execveat, "execveat" , NULL, NULL, NULL }, +{ TARGET_NR_execveat, "execveat" , NULL, print_execveat, NULL }, #endif #ifdef TARGET_NR_exec_with_loader { TARGET_NR_exec_with_loader, "exec_with_loader" , NULL, NULL, NULL }, @@ -275,6 +278,9 @@ #ifdef TARGET_NR_futex { TARGET_NR_futex, "futex" , NULL, print_futex, NULL }, #endif +#ifdef TARGET_NR_futex_time64 +{ TARGET_NR_futex_time64, "futex_time64" , NULL, NULL, NULL }, +#endif #ifdef TARGET_NR_futimesat { TARGET_NR_futimesat, "futimesat" , NULL, print_futimesat, NULL }, #endif @@ -315,10 +321,10 @@ { TARGET_NR_getgid32, "getgid32" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_getgroups -{ TARGET_NR_getgroups, "getgroups" , NULL, NULL, NULL }, +{ TARGET_NR_getgroups, "getgroups" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_getgroups32 -{ TARGET_NR_getgroups32, "getgroups32" , NULL, NULL, NULL }, +{ TARGET_NR_getgroups32, "getgroups32" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_gethostname { TARGET_NR_gethostname, "gethostname" , NULL, NULL, NULL }, @@ -337,7 +343,7 @@ { TARGET_NR_getpagesize, "getpagesize" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_getpeername -{ TARGET_NR_getpeername, "getpeername" , NULL, NULL, NULL }, +{ TARGET_NR_getpeername, "getpeername" , "%s(%d,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_getpgid { TARGET_NR_getpgid, "getpgid" , "%s(%u)", NULL, NULL }, @@ -361,19 +367,19 @@ { TARGET_NR_getrandom, "getrandom", "%s(%p,%u,%u)", NULL, NULL }, #endif #ifdef TARGET_NR_getresgid -{ TARGET_NR_getresgid, "getresgid" , NULL, NULL, NULL }, +{ TARGET_NR_getresgid, "getresgid" , "%s(%p,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_getresgid32 { TARGET_NR_getresgid32, "getresgid32" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_getresuid -{ TARGET_NR_getresuid, "getresuid" , NULL, NULL, NULL }, +{ TARGET_NR_getresuid, "getresuid" , "%s(%p,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_getresuid32 { TARGET_NR_getresuid32, "getresuid32" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_getrlimit -{ TARGET_NR_getrlimit, "getrlimit" , NULL, NULL, NULL }, +{ TARGET_NR_getrlimit, "getrlimit" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_get_robust_list { TARGET_NR_get_robust_list, "get_robust_list" , NULL, NULL, NULL }, @@ -385,10 +391,10 @@ { TARGET_NR_getsid, "getsid" , "%s(%d)", NULL, NULL }, #endif #ifdef TARGET_NR_getsockname -{ TARGET_NR_getsockname, "getsockname" , NULL, NULL, NULL }, +{ TARGET_NR_getsockname, "getsockname" , "%s(%d,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_getsockopt -{ TARGET_NR_getsockopt, "getsockopt" , NULL, NULL, NULL }, +{ TARGET_NR_getsockopt, "getsockopt" , "%s(%d,%d,%d,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_get_thread_area #if defined(TARGET_I386) && defined(TARGET_ABI32) @@ -650,7 +656,7 @@ { TARGET_NR_msgsnd, "msgsnd" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_msync -{ TARGET_NR_msync, "msync" , NULL, NULL, NULL }, +{ TARGET_NR_msync, "msync" , "%s(%p,%u,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_multiplexer { TARGET_NR_multiplexer, "multiplexer" , NULL, NULL, NULL }, @@ -709,6 +715,9 @@ #ifdef TARGET_NR_openat { TARGET_NR_openat, "openat" , NULL, print_openat, NULL }, #endif +#ifdef TARGET_NR_openat2 +{ TARGET_NR_openat2, "openat2" , NULL, print_openat2, NULL }, +#endif #ifdef TARGET_NR_osf_adjtime { TARGET_NR_osf_adjtime, "osf_adjtime" , NULL, NULL, NULL }, #endif @@ -1043,7 +1052,8 @@ { TARGET_NR_perfctr, "perfctr" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_personality -{ TARGET_NR_personality, "personality" , NULL, NULL, NULL }, +{ TARGET_NR_personality, "personality" , "%s(0x"TARGET_ABI_FMT_lx")", NULL, + print_syscall_ret_addr }, #endif #ifdef TARGET_NR_pipe { TARGET_NR_pipe, "pipe" , NULL, NULL, NULL }, @@ -1052,22 +1062,23 @@ { TARGET_NR_pivot_root, "pivot_root" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_poll -{ TARGET_NR_poll, "poll" , NULL, NULL, NULL }, +{ TARGET_NR_poll, "poll" , "%s(%p,%u,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_ppoll -{ TARGET_NR_ppoll, "ppoll" , NULL, NULL, NULL }, +{ TARGET_NR_ppoll, "ppoll" , "%s(%p,%u,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_prctl { TARGET_NR_prctl, "prctl" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_pread64 -{ TARGET_NR_pread64, "pread64" , NULL, NULL, NULL }, +{ TARGET_NR_pread64, "pread64" , NULL, print_pread64, NULL }, #endif #ifdef TARGET_NR_preadv { TARGET_NR_preadv, "preadv" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_prlimit64 -{ TARGET_NR_prlimit64, "prlimit64" , NULL, NULL, NULL }, +{ TARGET_NR_prlimit64, "prlimit64" , NULL, print_prlimit64, + print_syscall_ret_prlimit64 }, #endif #ifdef TARGET_NR_process_vm_readv { TARGET_NR_process_vm_readv, "process_vm_readv" , NULL, NULL, NULL }, @@ -1091,7 +1102,7 @@ { TARGET_NR_putpmsg, "putpmsg" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_pwrite64 -{ TARGET_NR_pwrite64, "pwrite64" , NULL, NULL, NULL }, +{ TARGET_NR_pwrite64, "pwrite64" , NULL, print_pread64, NULL }, #endif #ifdef TARGET_NR_pwritev { TARGET_NR_pwritev, "pwritev" , NULL, NULL, NULL }, @@ -1124,10 +1135,10 @@ { TARGET_NR_reboot, "reboot" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_recv -{ TARGET_NR_recv, "recv" , NULL, NULL, NULL }, +{ TARGET_NR_recv, "recv" , "%s(%d,%p,%u,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_recvfrom -{ TARGET_NR_recvfrom, "recvfrom" , NULL, NULL, NULL }, +{ TARGET_NR_recvfrom, "recvfrom" , NULL, print_recvfrom, NULL }, #endif #ifdef TARGET_NR_recvmmsg { TARGET_NR_recvmmsg, "recvmmsg" , NULL, NULL, NULL }, @@ -1178,13 +1189,14 @@ { TARGET_NR_rt_sigpending, "rt_sigpending" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_rt_sigprocmask -{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, print_rt_sigprocmask, NULL }, +{ TARGET_NR_rt_sigprocmask, "rt_sigprocmask" , NULL, print_rt_sigprocmask, + print_rt_sigprocmask_ret }, #endif #ifdef TARGET_NR_rt_sigqueueinfo { TARGET_NR_rt_sigqueueinfo, "rt_sigqueueinfo" , NULL, print_rt_sigqueueinfo, NULL }, #endif #ifdef TARGET_NR_rt_sigreturn -{ TARGET_NR_rt_sigreturn, "rt_sigreturn" , NULL, NULL, NULL }, +{ TARGET_NR_rt_sigreturn, "rt_sigreturn" , "%s(%p)", NULL, NULL }, #endif #ifdef TARGET_NR_rt_sigsuspend { TARGET_NR_rt_sigsuspend, "rt_sigsuspend" , NULL, NULL, NULL }, @@ -1196,16 +1208,19 @@ { TARGET_NR_rt_tgsigqueueinfo, "rt_tgsigqueueinfo" , NULL, print_rt_tgsigqueueinfo, NULL }, #endif #ifdef TARGET_NR_sched_getaffinity -{ TARGET_NR_sched_getaffinity, "sched_getaffinity" , NULL, NULL, NULL }, +{ TARGET_NR_sched_getaffinity, "sched_getaffinity" , "%s(%d,%u,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_sched_get_affinity { TARGET_NR_sched_get_affinity, "sched_get_affinity" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_sched_getattr -{ TARGET_NR_sched_getattr, "sched_getattr" , NULL, NULL, NULL }, +{ TARGET_NR_sched_getattr, "sched_getattr" , "%s(%d,%p,%u,%u)", NULL, NULL }, +#endif +#ifdef TARGET_NR_sched_setattr +{ TARGET_NR_sched_setattr, "sched_setattr" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_sched_getparam -{ TARGET_NR_sched_getparam, "sched_getparam" , NULL, NULL, NULL }, +{ TARGET_NR_sched_getparam, "sched_getparam" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_sched_get_priority_max { TARGET_NR_sched_get_priority_max, "sched_get_priority_max" , NULL, NULL, NULL }, @@ -1220,7 +1235,7 @@ { TARGET_NR_sched_rr_get_interval, "sched_rr_get_interval" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_sched_setaffinity -{ TARGET_NR_sched_setaffinity, "sched_setaffinity" , NULL, NULL, NULL }, +{ TARGET_NR_sched_setaffinity, "sched_setaffinity" , "%s(%d,%u,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_sched_setatt { TARGET_NR_sched_setatt, "sched_setatt" , NULL, NULL, NULL }, @@ -1274,7 +1289,7 @@ { TARGET_NR_sendmsg, "sendmsg" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_sendto -{ TARGET_NR_sendto, "sendto" , NULL, NULL, NULL }, +{ TARGET_NR_sendto, "sendto" , NULL, print_sendto, NULL }, #endif #ifdef TARGET_NR_setdomainname { TARGET_NR_setdomainname, "setdomainname" , NULL, NULL, NULL }, @@ -1298,10 +1313,10 @@ { TARGET_NR_setgid32, "setgid32" , "%s(%u)", NULL, NULL }, #endif #ifdef TARGET_NR_setgroups -{ TARGET_NR_setgroups, "setgroups" , NULL, NULL, NULL }, +{ TARGET_NR_setgroups, "setgroups" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_setgroups32 -{ TARGET_NR_setgroups32, "setgroups32" , NULL, NULL, NULL }, +{ TARGET_NR_setgroups32, "setgroups32" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_sethae { TARGET_NR_sethae, "sethae" , NULL, NULL, NULL }, @@ -1353,23 +1368,23 @@ { TARGET_NR_setreuid32, "setreuid32" , "%s(%u,%u)", NULL, NULL }, #endif #ifdef TARGET_NR_setrlimit -{ TARGET_NR_setrlimit, "setrlimit" , NULL, NULL, NULL }, +{ TARGET_NR_setrlimit, "setrlimit" , "%s(%d,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_set_robust_list -{ TARGET_NR_set_robust_list, "set_robust_list" , NULL, NULL, NULL }, +{ TARGET_NR_set_robust_list, "set_robust_list" , "%s(%p,%u)", NULL, NULL }, #endif #ifdef TARGET_NR_setsid { TARGET_NR_setsid, "setsid" , "%s()", NULL, NULL }, #endif #ifdef TARGET_NR_setsockopt -{ TARGET_NR_setsockopt, "setsockopt" , NULL, NULL, NULL }, +{ TARGET_NR_setsockopt, "setsockopt" , "%s(%d,%d,%d,%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_set_thread_area { TARGET_NR_set_thread_area, "set_thread_area", "%s(0x"TARGET_ABI_FMT_lx")", NULL, NULL }, #endif #ifdef TARGET_NR_set_tid_address -{ TARGET_NR_set_tid_address, "set_tid_address" , NULL, NULL, NULL }, +{ TARGET_NR_set_tid_address, "set_tid_address" , "%s(%p)", NULL, NULL }, #endif #ifdef TARGET_NR_settimeofday { TARGET_NR_settimeofday, "settimeofday" , NULL, print_settimeofday, NULL }, @@ -1387,7 +1402,7 @@ { TARGET_NR_sgetmask, "sgetmask" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_shmat -{ TARGET_NR_shmat, "shmat" , NULL, NULL, print_syscall_ret_addr }, +{ TARGET_NR_shmat, "shmat" , NULL, print_shmat, print_syscall_ret_addr }, #endif #ifdef TARGET_NR_shmctl { TARGET_NR_shmctl, "shmctl" , NULL, NULL, NULL }, @@ -1498,7 +1513,7 @@ { TARGET_NR_sysfs, "sysfs" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_sysinfo -{ TARGET_NR_sysinfo, "sysinfo" , NULL, NULL, NULL }, +{ TARGET_NR_sysinfo, "sysinfo" , "%s(%p)", NULL, NULL }, #endif #ifdef TARGET_NR_sys_kexec_load { TARGET_NR_sys_kexec_load, "sys_kexec_load" , NULL, NULL, NULL }, @@ -1648,13 +1663,15 @@ { TARGET_NR_vserver, "vserver" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_wait4 -{ TARGET_NR_wait4, "wait4" , NULL, NULL, NULL }, +{ TARGET_NR_wait4, "wait4" , "%s(%d,%p,%d,%p)", NULL, + print_syscall_ret_wait4 }, #endif #ifdef TARGET_NR_waitid { TARGET_NR_waitid, "waitid" , "%s(%#x,%d,%p,%#x)", NULL, NULL }, #endif #ifdef TARGET_NR_waitpid -{ TARGET_NR_waitpid, "waitpid" , "%s(%d,%p,%#x)", NULL, NULL }, +{ TARGET_NR_waitpid, "waitpid", "%s(%d,%p,%#x)", NULL, + print_syscall_ret_waitpid }, #endif #ifdef TARGET_NR_write { TARGET_NR_write, "write" , "%s(%d,%#x,%d)", NULL, NULL }, @@ -1672,7 +1689,7 @@ { TARGET_NR_sync_file_range2, "sync_file_range2", NULL, NULL, NULL }, #endif #ifdef TARGET_NR_pipe2 -{ TARGET_NR_pipe2, "pipe2", NULL, NULL, NULL }, +{ TARGET_NR_pipe2, "pipe2", "%s(%p,%d)", NULL, NULL }, #endif #ifdef TARGET_NR_pidfd_open { TARGET_NR_pidfd_open, "pidfd_open", "%s(%d,%u)", NULL, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index cedf22c5b5..1ce4c79784 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -22,6 +22,10 @@ #include "qemu/path.h" #include "qemu/memfd.h" #include "qemu/queue.h" +#include "qemu/plugin.h" +#include "tcg/startup.h" +#include "target_mman.h" +#include "exec/page-protection.h" #include #include #include @@ -50,7 +54,6 @@ #include #include #include -//#include #include #include #include @@ -95,50 +98,7 @@ #include #include #include - -#ifdef HAVE_SYS_MOUNT_FSCONFIG -/* - * glibc >= 2.36 linux/mount.h conflicts with sys/mount.h, - * which in turn prevents use of linux/fs.h. So we have to - * define the constants ourselves for now. - */ -#define FS_IOC_GETFLAGS _IOR('f', 1, long) -#define FS_IOC_SETFLAGS _IOW('f', 2, long) -#define FS_IOC_GETVERSION _IOR('v', 1, long) -#define FS_IOC_SETVERSION _IOW('v', 2, long) -#define FS_IOC_FIEMAP _IOWR('f', 11, struct fiemap) -#define FS_IOC32_GETFLAGS _IOR('f', 1, int) -#define FS_IOC32_SETFLAGS _IOW('f', 2, int) -#define FS_IOC32_GETVERSION _IOR('v', 1, int) -#define FS_IOC32_SETVERSION _IOW('v', 2, int) - -#define BLKGETSIZE64 _IOR(0x12,114,size_t) -#define BLKDISCARD _IO(0x12,119) -#define BLKIOMIN _IO(0x12,120) -#define BLKIOOPT _IO(0x12,121) -#define BLKALIGNOFF _IO(0x12,122) -#define BLKPBSZGET _IO(0x12,123) -#define BLKDISCARDZEROES _IO(0x12,124) -#define BLKSECDISCARD _IO(0x12,125) -#define BLKROTATIONAL _IO(0x12,126) -#define BLKZEROOUT _IO(0x12,127) - -#define FIBMAP _IO(0x00,1) -#define FIGETBSZ _IO(0x00,2) - -struct file_clone_range { - __s64 src_fd; - __u64 src_offset; - __u64 src_length; - __u64 dest_offset; -}; - -#define FICLONE _IOW(0x94, 9, int) -#define FICLONERANGE _IOW(0x94, 13, struct file_clone_range) - -#else #include -#endif #include #if defined(CONFIG_FIEMAP) #include @@ -182,7 +142,6 @@ struct file_clone_range { #include "special-errno.h" #include "qapi/error.h" #include "fd-trans.h" -#include "tcg/tcg.h" #include "cpu_loop-common.h" #ifndef CLONE_IO @@ -211,9 +170,13 @@ struct file_clone_range { #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 */ @@ -346,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, @@ -492,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) @@ -639,6 +601,33 @@ static int check_zeroed_user(abi_long addr, size_t ksize, size_t usize) return 1; } +/* + * Copies a target struct to a host struct, in a way that guarantees + * backwards-compatibility for struct syscall arguments. + * + * Similar to kernels uaccess.h:copy_struct_from_user() + */ +int copy_struct_from_user(void *dst, size_t ksize, abi_ptr src, size_t usize) +{ + size_t size = MIN(ksize, usize); + size_t rest = MAX(ksize, usize) - size; + + /* Deal with trailing bytes. */ + if (usize < ksize) { + memset(dst + size, 0, rest); + } else if (usize > ksize) { + int ret = check_zeroed_user(src, ksize, usize); + if (ret <= 0) { + return ret ?: -TARGET_E2BIG; + } + } + /* Copy the interoperable parts of the struct. */ + if (copy_from_user(dst, src, size)) { + return -TARGET_EFAULT; + } + return 0; +} + #define safe_syscall0(type, name) \ static type safe_##name(void) \ { \ @@ -690,6 +679,10 @@ safe_syscall3(ssize_t, read, int, fd, void *, buff, size_t, count) safe_syscall3(ssize_t, write, int, fd, const void *, buff, size_t, count) safe_syscall4(int, openat, int, dirfd, const char *, pathname, \ int, flags, mode_t, mode) + +safe_syscall4(int, openat2, int, dirfd, const char *, pathname, \ + const struct open_how_ver0 *, how, size_t, size) + #if defined(TARGET_NR_wait4) || defined(TARGET_NR_waitpid) safe_syscall4(pid_t, wait4, pid_t, pid, int *, status, int, options, \ struct rusage *, rusage) @@ -697,6 +690,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, \ @@ -794,10 +789,8 @@ safe_syscall6(ssize_t, copy_file_range, int, infd, loff_t *, pinoff, * the libc function. */ #define safe_ioctl(...) safe_syscall(__NR_ioctl, __VA_ARGS__) -/* Similarly for fcntl. Note that callers must always: - * pass the F_GETLK64 etc constants rather than the unsuffixed F_GETLK - * use the flock64 struct rather than unsuffixed flock - * This will then work and use a 64-bit offset for both 32-bit and 64-bit hosts. +/* Similarly for fcntl. Since we always build with LFS enabled, + * we should be using the 64-bit structures automatically. */ #ifdef __NR_fcntl64 #define safe_fcntl(...) safe_syscall(__NR_fcntl64, __VA_ARGS__) @@ -836,88 +829,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) @@ -1686,24 +1644,6 @@ static abi_long do_pipe(CPUArchState *cpu_env, abi_ulong pipedes, return get_errno(ret); } -static inline abi_long target_to_host_ip_mreq(struct ip_mreqn *mreqn, - abi_ulong target_addr, - socklen_t len) -{ - struct target_ip_mreqn *target_smreqn; - - target_smreqn = lock_user(VERIFY_READ, target_addr, len, 1); - if (!target_smreqn) - return -TARGET_EFAULT; - mreqn->imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; - mreqn->imr_address.s_addr = target_smreqn->imr_address.s_addr; - if (len == sizeof(struct target_ip_mreqn)) - mreqn->imr_ifindex = tswapal(target_smreqn->imr_ifindex); - unlock_user(target_smreqn, target_addr, 0); - - return 0; -} - static inline abi_long target_to_host_sockaddr(int fd, struct sockaddr *addr, abi_ulong target_addr, socklen_t len) @@ -1876,6 +1816,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); @@ -2130,8 +2078,6 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, { abi_long ret; int val; - struct ip_mreqn *ip_mreq; - struct ip_mreq_source *ip_mreq_source; switch(level) { case SOL_TCP: @@ -2174,19 +2120,40 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, break; case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: + { + struct ip_mreqn ip_mreq; + struct target_ip_mreqn *target_smreqn; + + QEMU_BUILD_BUG_ON(sizeof(struct ip_mreq) != + sizeof(struct target_ip_mreq)); + if (optlen < sizeof (struct target_ip_mreq) || - optlen > sizeof (struct target_ip_mreqn)) + optlen > sizeof (struct target_ip_mreqn)) { return -TARGET_EINVAL; + } - ip_mreq = (struct ip_mreqn *) alloca(optlen); - target_to_host_ip_mreq(ip_mreq, optval_addr, optlen); - ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq, optlen)); + target_smreqn = lock_user(VERIFY_READ, optval_addr, optlen, 1); + if (!target_smreqn) { + return -TARGET_EFAULT; + } + ip_mreq.imr_multiaddr.s_addr = target_smreqn->imr_multiaddr.s_addr; + ip_mreq.imr_address.s_addr = target_smreqn->imr_address.s_addr; + if (optlen == sizeof(struct target_ip_mreqn)) { + ip_mreq.imr_ifindex = tswapal(target_smreqn->imr_ifindex); + optlen = sizeof(struct ip_mreqn); + } + unlock_user(target_smreqn, optval_addr, 0); + + ret = get_errno(setsockopt(sockfd, level, optname, &ip_mreq, optlen)); break; - + } case IP_BLOCK_SOURCE: case IP_UNBLOCK_SOURCE: case IP_ADD_SOURCE_MEMBERSHIP: case IP_DROP_SOURCE_MEMBERSHIP: + { + struct ip_mreq_source *ip_mreq_source; + if (optlen != sizeof (struct target_ip_mreq_source)) return -TARGET_EINVAL; @@ -2197,7 +2164,7 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq_source, optlen)); unlock_user (ip_mreq_source, optval_addr, 0); break; - + } default: goto unimplemented; } @@ -2340,18 +2307,13 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, switch (optname) { case ALG_SET_KEY: { - char *alg_key = g_malloc(optlen); - + char *alg_key = lock_user(VERIFY_READ, optval_addr, optlen, 1); if (!alg_key) { - return -TARGET_ENOMEM; - } - if (copy_from_user(alg_key, optval_addr, optlen)) { - g_free(alg_key); return -TARGET_EFAULT; } ret = get_errno(setsockopt(sockfd, level, optname, alg_key, optlen)); - g_free(alg_key); + unlock_user(alg_key, optval_addr, optlen); break; } case ALG_SET_AEAD_AUTHSIZE: @@ -2368,12 +2330,10 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case TARGET_SOL_SOCKET: switch (optname) { case TARGET_SO_RCVTIMEO: + case TARGET_SO_SNDTIMEO: { struct timeval tv; - optname = SO_RCVTIMEO; - -set_timeout: if (optlen != sizeof(struct target_timeval)) { return -TARGET_EINVAL; } @@ -2382,13 +2342,12 @@ set_timeout: return -TARGET_EFAULT; } - ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, + optname == TARGET_SO_RCVTIMEO ? + SO_RCVTIMEO : SO_SNDTIMEO, &tv, sizeof(tv))); return ret; } - case TARGET_SO_SNDTIMEO: - optname = SO_SNDTIMEO; - goto set_timeout; case TARGET_SO_ATTACH_FILTER: { struct target_sock_fprog *tfprog; @@ -2805,8 +2764,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; @@ -2978,7 +2942,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]); } @@ -3327,7 +3291,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; @@ -3379,7 +3346,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; } @@ -3457,7 +3426,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)); @@ -3768,14 +3747,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 @@ -4525,134 +4496,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. */ @@ -4739,7 +4582,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)) @@ -4752,7 +4595,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: @@ -5229,8 +5072,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++) { @@ -5238,7 +5081,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; @@ -5278,7 +5121,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) { @@ -5290,7 +5133,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; @@ -5306,8 +5149,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++) { @@ -5318,7 +5161,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; @@ -5346,8 +5189,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; @@ -5358,7 +5201,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; @@ -5476,7 +5319,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( @@ -5764,7 +5607,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; @@ -5776,7 +5619,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]) { @@ -5834,7 +5677,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; @@ -5856,7 +5699,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[] = { @@ -5884,7 +5726,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[] = { @@ -5919,7 +5760,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[] = { @@ -5939,7 +5779,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) @@ -6019,9 +5858,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 }, @@ -6039,9 +5884,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 @@ -6390,15 +6309,6 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) # define PR_GET_TAGGED_ADDR_CTRL 56 # define PR_TAGGED_ADDR_ENABLE (1UL << 0) #endif -#ifndef PR_MTE_TCF_SHIFT -# define PR_MTE_TCF_SHIFT 1 -# define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT) -# define PR_MTE_TAG_SHIFT 3 -# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) -#endif #ifndef PR_SET_IO_FLUSHER # define PR_SET_IO_FLUSHER 57 # define PR_GET_IO_FLUSHER 58 @@ -6553,16 +6463,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 = get_task_state(env_cpu(env)); + return put_user_ual(ts->child_tidptr, arg2); + } case PR_GET_FPEXC: case PR_SET_FPEXC: @@ -6618,7 +6540,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) @@ -6660,7 +6582,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; @@ -6680,8 +6602,8 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, * generate code for parallel execution and flush old translations. * Do this now so that the copy gets CF_PARALLEL too. */ - if (!(cpu->tcg_cflags & CF_PARALLEL)) { - cpu->tcg_cflags |= CF_PARALLEL; + if (!tcg_cflags_has(cpu, CF_PARALLEL)) { + tcg_cflags_set(cpu, CF_PARALLEL); tb_flush(cpu); } @@ -6752,6 +6674,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; } @@ -6761,7 +6694,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 @@ -6772,15 +6705,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; } @@ -6802,13 +6750,13 @@ static int target_to_host_fcntl_cmd(int cmd) ret = cmd; break; case TARGET_F_GETLK: - ret = F_GETLK64; + ret = F_GETLK; break; case TARGET_F_SETLK: - ret = F_SETLK64; + ret = F_SETLK; break; case TARGET_F_SETLKW: - ret = F_SETLKW64; + ret = F_SETLKW; break; case TARGET_F_GETOWN: ret = F_GETOWN; @@ -6824,13 +6772,13 @@ static int target_to_host_fcntl_cmd(int cmd) break; #if TARGET_ABI_BITS == 32 case TARGET_F_GETLK64: - ret = F_GETLK64; + ret = F_GETLK; break; case TARGET_F_SETLK64: - ret = F_SETLK64; + ret = F_SETLK; break; case TARGET_F_SETLKW64: - ret = F_SETLKW64; + ret = F_SETLKW; break; #endif case TARGET_F_SETLEASE: @@ -6884,8 +6832,8 @@ static int target_to_host_fcntl_cmd(int cmd) * them to 5, 6 and 7 before making the syscall(). Since we make the * syscall directly, adjust to what is supported by the kernel. */ - if (ret >= F_GETLK64 && ret <= F_SETLKW64) { - ret -= F_GETLK64 - 5; + if (ret >= F_GETLK && ret <= F_SETLKW) { + ret -= F_GETLK - 5; } #endif @@ -6918,7 +6866,7 @@ static int host_to_target_flock(int type) return type; } -static inline abi_long copy_from_user_flock(struct flock64 *fl, +static inline abi_long copy_from_user_flock(struct flock *fl, abi_ulong target_flock_addr) { struct target_flock *target_fl; @@ -6943,7 +6891,7 @@ static inline abi_long copy_from_user_flock(struct flock64 *fl, } static inline abi_long copy_to_user_flock(abi_ulong target_flock_addr, - const struct flock64 *fl) + const struct flock *fl) { struct target_flock *target_fl; short l_type; @@ -6962,8 +6910,8 @@ static inline abi_long copy_to_user_flock(abi_ulong target_flock_addr, return 0; } -typedef abi_long from_flock64_fn(struct flock64 *fl, abi_ulong target_addr); -typedef abi_long to_flock64_fn(abi_ulong target_addr, const struct flock64 *fl); +typedef abi_long from_flock64_fn(struct flock *fl, abi_ulong target_addr); +typedef abi_long to_flock64_fn(abi_ulong target_addr, const struct flock *fl); #if defined(TARGET_ARM) && TARGET_ABI_BITS == 32 struct target_oabi_flock64 { @@ -6974,7 +6922,7 @@ struct target_oabi_flock64 { abi_int l_pid; } QEMU_PACKED; -static inline abi_long copy_from_user_oabi_flock64(struct flock64 *fl, +static inline abi_long copy_from_user_oabi_flock64(struct flock *fl, abi_ulong target_flock_addr) { struct target_oabi_flock64 *target_fl; @@ -6999,7 +6947,7 @@ static inline abi_long copy_from_user_oabi_flock64(struct flock64 *fl, } static inline abi_long copy_to_user_oabi_flock64(abi_ulong target_flock_addr, - const struct flock64 *fl) + const struct flock *fl) { struct target_oabi_flock64 *target_fl; short l_type; @@ -7019,7 +6967,7 @@ static inline abi_long copy_to_user_oabi_flock64(abi_ulong target_flock_addr, } #endif -static inline abi_long copy_from_user_flock64(struct flock64 *fl, +static inline abi_long copy_from_user_flock64(struct flock *fl, abi_ulong target_flock_addr) { struct target_flock64 *target_fl; @@ -7044,7 +6992,7 @@ static inline abi_long copy_from_user_flock64(struct flock64 *fl, } static inline abi_long copy_to_user_flock64(abi_ulong target_flock_addr, - const struct flock64 *fl) + const struct flock *fl) { struct target_flock64 *target_fl; short l_type; @@ -7065,7 +7013,7 @@ static inline abi_long copy_to_user_flock64(abi_ulong target_flock_addr, static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) { - struct flock64 fl64; + struct flock fl; #ifdef F_GETOWN_EX struct f_owner_ex fox; struct target_f_owner_ex *target_fox; @@ -7078,51 +7026,55 @@ static abi_long do_fcntl(int fd, int cmd, abi_ulong arg) switch(cmd) { case TARGET_F_GETLK: - ret = copy_from_user_flock(&fl64, arg); + ret = copy_from_user_flock(&fl, arg); if (ret) { return ret; } - ret = get_errno(safe_fcntl(fd, host_cmd, &fl64)); + ret = get_errno(safe_fcntl(fd, host_cmd, &fl)); if (ret == 0) { - ret = copy_to_user_flock(arg, &fl64); + ret = copy_to_user_flock(arg, &fl); } break; case TARGET_F_SETLK: case TARGET_F_SETLKW: - ret = copy_from_user_flock(&fl64, arg); + ret = copy_from_user_flock(&fl, arg); if (ret) { return ret; } - ret = get_errno(safe_fcntl(fd, host_cmd, &fl64)); + ret = get_errno(safe_fcntl(fd, host_cmd, &fl)); break; case TARGET_F_GETLK64: case TARGET_F_OFD_GETLK: - ret = copy_from_user_flock64(&fl64, arg); + ret = copy_from_user_flock64(&fl, arg); if (ret) { return ret; } - ret = get_errno(safe_fcntl(fd, host_cmd, &fl64)); + ret = get_errno(safe_fcntl(fd, host_cmd, &fl)); if (ret == 0) { - ret = copy_to_user_flock64(arg, &fl64); + ret = copy_to_user_flock64(arg, &fl); } break; case TARGET_F_SETLK64: case TARGET_F_SETLKW64: case TARGET_F_OFD_SETLK: case TARGET_F_OFD_SETLKW: - ret = copy_from_user_flock64(&fl64, arg); + ret = copy_from_user_flock64(&fl, arg); if (ret) { return ret; } - ret = get_errno(safe_fcntl(fd, host_cmd, &fl64)); + ret = get_errno(safe_fcntl(fd, host_cmd, &fl)); break; case TARGET_F_GETFL: 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; @@ -7276,11 +7228,29 @@ static inline int tswapid(int id) #else #define __NR_sys_setresgid __NR_setresgid #endif +#ifdef __NR_setgroups32 +#define __NR_sys_setgroups __NR_setgroups32 +#else +#define __NR_sys_setgroups __NR_setgroups +#endif +#ifdef __NR_sys_setreuid32 +#define __NR_sys_setreuid __NR_setreuid32 +#else +#define __NR_sys_setreuid __NR_setreuid +#endif +#ifdef __NR_sys_setregid32 +#define __NR_sys_setregid __NR_setregid32 +#else +#define __NR_sys_setregid __NR_setregid +#endif _syscall1(int, sys_setuid, uid_t, uid) _syscall1(int, sys_setgid, gid_t, gid) _syscall3(int, sys_setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) _syscall3(int, sys_setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) +_syscall2(int, sys_setgroups, int, size, gid_t *, grouplist) +_syscall2(int, sys_setreuid, uid_t, ruid, uid_t, euid); +_syscall2(int, sys_setregid, gid_t, rgid, gid_t, egid); void syscall_init(void) { @@ -7337,7 +7307,7 @@ static inline abi_long target_truncate64(CPUArchState *cpu_env, const char *arg1 arg2 = arg3; arg3 = arg4; } - return get_errno(truncate64(arg1, target_offset64(arg2, arg3))); + return get_errno(truncate(arg1, target_offset64(arg2, arg3))); } #endif @@ -7351,7 +7321,7 @@ static inline abi_long target_ftruncate64(CPUArchState *cpu_env, abi_long arg1, arg2 = arg3; arg3 = arg4; } - return get_errno(ftruncate64(arg1, target_offset64(arg2, arg3))); + return get_errno(ftruncate(arg1, target_offset64(arg2, arg3))); } #endif @@ -7634,6 +7604,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)) @@ -8011,7 +7989,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++) { @@ -8025,76 +8003,195 @@ 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; - - 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; - } +/* + * Subroutine to output one line of /proc/self/maps, + * or one region of /proc/self/smaps. + */ #ifdef TARGET_HPPA - if (h2g(max) == ts->info->stack_limit) { +# define test_stack(S, E, L) (E == L) #else - if (h2g(min) == ts->info->stack_limit) { +# define test_stack(S, E, L) (S == L) #endif - 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"); - } - } +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 } - free_self_maps(map_info); + /* 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; + } -#ifdef TARGET_VSYSCALL_PAGE + 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 /* - * We only support execution from the vsyscall page. - * This is as if CONFIG_LEGACY_VSYSCALL_XONLY=y from v5.3. + * 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. */ - 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]"); + 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 = get_task_state(env_cpu(env)), + .fd = fd, + .smaps = smaps + }; + + mmap_lock(); + d.host_maps = read_self_maps(); + 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); + } + mmap_unlock(); 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; @@ -8107,9 +8204,22 @@ 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()); + } else if (i == 19) { + /* num_threads */ + int cpus = 0; + WITH_RCU_READ_LOCK_GUARD() { + CPUState *cpu_iter; + CPU_FOREACH(cpu_iter) { + cpus++; + } + } + g_string_printf(buf, "%d ", cpus); } else if (i == 21) { /* starttime */ g_string_printf(buf, "%" PRIu64 " ", ts->start_boottime); @@ -8132,7 +8242,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; @@ -8210,8 +8320,11 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code) } } +#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; @@ -8263,36 +8376,12 @@ 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) +static int maybe_do_fake_open(CPUArchState *cpu_env, int dirfd, + const char *fname, int flags, mode_t mode, + int openat2_resolve, bool safe) { + g_autofree char *proc_name = NULL; + const char *pathname; struct fake_open { const char *filename; int (*fill)(CPUArchState *cpu_env, int fd); @@ -8301,23 +8390,42 @@ 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")) { - return safe_openat(dirfd, exec_path, flags, mode); + /* Honor openat2 resolve flags */ + if ((openat2_resolve & RESOLVE_NO_MAGICLINKS) || + (openat2_resolve & RESOLVE_NO_SYMLINKS)) { + errno = ELOOP; + return -1; + } + 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++) { @@ -8359,7 +8467,211 @@ static int do_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, int return fd; } - return safe_openat(dirfd, path(pathname), flags, mode); + return -2; +} + +int do_guest_openat(CPUArchState *cpu_env, int dirfd, const char *pathname, + int flags, mode_t mode, bool safe) +{ + int fd = maybe_do_fake_open(cpu_env, dirfd, pathname, flags, mode, 0, safe); + if (fd > -2) { + return fd; + } + + if (safe) { + return safe_openat(dirfd, path(pathname), flags, mode); + } else { + return openat(dirfd, path(pathname), flags, mode); + } +} + + +static int do_openat2(CPUArchState *cpu_env, abi_long dirfd, + abi_ptr guest_pathname, abi_ptr guest_open_how, + abi_ulong guest_size) +{ + struct open_how_ver0 how = {0}; + char *pathname; + int ret; + + if (guest_size < sizeof(struct target_open_how_ver0)) { + return -TARGET_EINVAL; + } + ret = copy_struct_from_user(&how, sizeof(how), guest_open_how, guest_size); + if (ret) { + if (ret == -TARGET_E2BIG) { + qemu_log_mask(LOG_UNIMP, + "Unimplemented openat2 open_how size: " + TARGET_ABI_FMT_lu "\n", guest_size); + } + return ret; + } + pathname = lock_user_string(guest_pathname); + if (!pathname) { + return -TARGET_EFAULT; + } + + how.flags = target_to_host_bitmask(tswap64(how.flags), fcntl_flags_tbl); + how.mode = tswap64(how.mode); + how.resolve = tswap64(how.resolve); + int fd = maybe_do_fake_open(cpu_env, dirfd, pathname, how.flags, how.mode, + how.resolve, true); + if (fd > -2) { + ret = get_errno(fd); + } else { + ret = get_errno(safe_openat2(dirfd, pathname, &how, + sizeof(struct open_how_ver0))); + } + + fd_trans_unregister(ret); + unlock_user(pathname, guest_pathname, 0); + return ret; +} + +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 @@ -8457,7 +8769,7 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count) void *tdirp; int hlen, hoff, toff; int hreclen, treclen; - off64_t prev_diroff = 0; + off_t prev_diroff = 0; hdirp = g_try_malloc(count); if (!hdirp) { @@ -8510,7 +8822,7 @@ static int do_getdents(abi_long dirfd, abi_long arg2, abi_long count) * Return what we have, resetting the file pointer to the * location of the first record not returned. */ - lseek64(dirfd, prev_diroff, SEEK_SET); + lseek(dirfd, prev_diroff, SEEK_SET); break; } @@ -8544,7 +8856,7 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) void *tdirp; int hlen, hoff, toff; int hreclen, treclen; - off64_t prev_diroff = 0; + off_t prev_diroff = 0; hdirp = g_try_malloc(count); if (!hdirp) { @@ -8586,7 +8898,7 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count) * Return what we have, resetting the file pointer to the * location of the first record not returned. */ - lseek64(dirfd, prev_diroff, SEEK_SET); + lseek(dirfd, prev_diroff, SEEK_SET); break; } @@ -8603,10 +8915,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 (1ULL << 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. @@ -8645,9 +9218,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 @@ -8657,11 +9236,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(); @@ -8710,9 +9284,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; @@ -8720,12 +9294,15 @@ 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; + case TARGET_NR_openat2: + ret = do_openat2(cpu_env, arg1, arg2, arg3, arg4); + return ret; #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) case TARGET_NR_name_to_handle_at: ret = do_name_to_handle_at(arg1, arg2, arg3, arg4, arg5); @@ -8802,14 +9379,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; @@ -8871,104 +9458,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. - */ - if (is_proc_myself(p, "exe")) { - ret = get_errno(safe_execve(exec_path, argp, envp)); - } else { - 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; @@ -9093,6 +9586,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: { @@ -9111,7 +9658,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 @@ -9677,7 +10224,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; @@ -9985,27 +10532,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); } @@ -10023,18 +10550,13 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, /* 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), arg4); - /* We cannot NUL terminate the string. */ - memcpy(p2, real, ret); - } + /* + * 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)); } @@ -10066,10 +10588,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return ret; #ifdef TARGET_NR_mmap case TARGET_NR_mmap: -#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || \ - (defined(TARGET_ARM) && defined(TARGET_ABI32)) || \ - defined(TARGET_M68K) || defined(TARGET_CRIS) || defined(TARGET_MICROBLAZE) \ - || defined(TARGET_S390X) +#ifdef TARGET_ARCH_WANT_SYS_OLD_MMAP { abi_ulong *v; abi_ulong v1, v2, v3, v4, v5, v6; @@ -10082,28 +10601,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); @@ -10111,7 +10622,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 @@ -10131,7 +10642,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: @@ -10613,11 +11125,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)); @@ -10700,16 +11212,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; @@ -10907,14 +11417,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)) { @@ -11119,7 +11629,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return -TARGET_EFAULT; } } - ret = get_errno(pread64(arg1, p, arg3, target_offset64(arg4, arg5))); + ret = get_errno(pread(arg1, p, arg3, target_offset64(arg4, arg5))); unlock_user(p, arg2, ret); return ret; case TARGET_NR_pwrite64: @@ -11136,7 +11646,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return -TARGET_EFAULT; } } - ret = get_errno(pwrite64(arg1, p, arg3, target_offset64(arg4, arg5))); + ret = get_errno(pwrite(arg1, p, arg3, target_offset64(arg4, arg5))); unlock_user(p, arg2, 0); return ret; #endif @@ -11431,17 +11941,17 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return get_errno(high2lowgid(getegid())); #endif case TARGET_NR_setreuid: - return get_errno(setreuid(low2highuid(arg1), low2highuid(arg2))); + return get_errno(sys_setreuid(low2highuid(arg1), low2highuid(arg2))); case TARGET_NR_setregid: - return get_errno(setregid(low2highgid(arg1), low2highgid(arg2))); + return get_errno(sys_setregid(low2highgid(arg1), low2highgid(arg2))); case TARGET_NR_getgroups: - { + { /* the same code as for TARGET_NR_getgroups32 */ int gidsetsize = arg1; target_id *target_grouplist; g_autofree gid_t *grouplist = NULL; int i; - if (gidsetsize > NGROUPS_MAX) { + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { return -TARGET_EINVAL; } if (gidsetsize > 0) { @@ -11466,7 +11976,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return ret; } case TARGET_NR_setgroups: - { + { /* the same code as for TARGET_NR_setgroups32 */ int gidsetsize = arg1; target_id *target_grouplist; g_autofree gid_t *grouplist = NULL; @@ -11491,7 +12001,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id)); } - return get_errno(setgroups(gidsetsize, grouplist)); + return get_errno(sys_setgroups(gidsetsize, grouplist)); } case TARGET_NR_fchown: return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); @@ -11763,21 +12273,21 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setreuid32 case TARGET_NR_setreuid32: - return get_errno(setreuid(arg1, arg2)); + return get_errno(sys_setreuid(arg1, arg2)); #endif #ifdef TARGET_NR_setregid32 case TARGET_NR_setregid32: - return get_errno(setregid(arg1, arg2)); + return get_errno(sys_setregid(arg1, arg2)); #endif #ifdef TARGET_NR_getgroups32 case TARGET_NR_getgroups32: - { + { /* the same code as for TARGET_NR_getgroups */ int gidsetsize = arg1; uint32_t *target_grouplist; g_autofree gid_t *grouplist = NULL; int i; - if (gidsetsize > NGROUPS_MAX) { + if (gidsetsize > NGROUPS_MAX || gidsetsize < 0) { return -TARGET_EINVAL; } if (gidsetsize > 0) { @@ -11803,7 +12313,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_setgroups32 case TARGET_NR_setgroups32: - { + { /* the same code as for TARGET_NR_setgroups */ int gidsetsize = arg1; uint32_t *target_grouplist; g_autofree gid_t *grouplist = NULL; @@ -11827,7 +12337,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, } unlock_user(target_grouplist, arg2, 0); } - return get_errno(setgroups(gidsetsize, grouplist)); + return get_errno(sys_setgroups(gidsetsize, grouplist)); } #endif #ifdef TARGET_NR_fchown32 @@ -11897,7 +12407,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; } @@ -11996,7 +12506,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_fcntl64: { int cmd; - struct flock64 fl; + struct flock fl; from_flock64_fn *copyfrom = copy_from_user_flock64; to_flock64_fn *copyto = copy_to_user_flock64; @@ -12069,7 +12579,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) { @@ -12106,7 +12616,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) { @@ -12151,7 +12661,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) { @@ -12196,7 +12706,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) { @@ -12231,19 +12741,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #if defined(TARGET_MIPS) cpu_env->active_tc.CP0_UserLocal = arg1; return 0; -#elif defined(TARGET_CRIS) - if (arg1 & 0xff) - ret = -TARGET_EINVAL; - else { - cpu_env->pregs[PR_PID] = arg1; - ret = 0; - } - return ret; #elif defined(TARGET_I386) && defined(TARGET_ABI32) 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; } @@ -12257,7 +12759,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 @@ -12382,7 +12884,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_set_tid_address) case TARGET_NR_set_tid_address: { - TaskState *ts = cpu->opaque; + 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()); @@ -13336,6 +13838,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 77864de57f..faad9147c9 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,42 @@ * 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) \ - || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) - /* 16 bit uid wrappers emulation */ +#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) +/* 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) \ +#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SH4) \ + || defined(TARGET_M68K) \ + || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ + || defined(TARGET_RISCV) \ || defined(TARGET_XTENSA) || defined(TARGET_LOONGARCH64) -#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 +115,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 +174,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 +215,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 +360,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 +389,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 +501,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 +575,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 +676,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 +687,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 +750,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 +851,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 +908,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 +940,13 @@ 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) +#define TARGET_FITRIM TARGET_IOWR('X', 121, struct fstrim_range) + /* * 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,144 +1214,49 @@ 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) || defined(TARGET_XTENSA) -#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 */ -#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)) \ - || defined(TARGET_CRIS) +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) \ + || (defined(TARGET_ARM) && defined(TARGET_ABI32)) #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 @@ -1374,239 +1264,239 @@ 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_PPC) #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 @@ -1614,78 +1504,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 @@ -1693,37 +1583,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) @@ -1731,94 +1621,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]; }; /* @@ -1829,107 +1719,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 @@ -1937,72 +1827,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; @@ -2020,15 +1910,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; @@ -2036,17 +1926,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; @@ -2063,17 +1953,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. */ @@ -2084,8 +1974,8 @@ struct target_stat64 { abi_ulong __unused5; }; -#elif defined(TARGET_OPENRISC) || defined(TARGET_NIOS2) \ - || defined(TARGET_RISCV) || defined(TARGET_HEXAGON) +#elif defined(TARGET_OPENRISC) \ + || defined(TARGET_RISCV) || defined(TARGET_HEXAGON) || defined(TARGET_LOONGARCH) /* These are the asm-generic versions of the stat and stat64 structures */ @@ -2093,15 +1983,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; @@ -2109,33 +1999,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 @@ -2175,184 +2065,179 @@ 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) || \ +#elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \ + defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \ defined(TARGET_RISCV) || defined(TARGET_LOONGARCH64)) && \ - !defined(TARGET_ABI32) + !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]; }; #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 @@ -2370,7 +2255,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 @@ -2382,20 +2267,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) @@ -2443,89 +2328,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]; }; @@ -2550,8 +2435,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; @@ -2561,31 +2446,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) @@ -2638,11 +2523,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.. */ }; @@ -2669,9 +2554,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; @@ -2681,7 +2566,7 @@ struct target_drm_version { }; struct target_drm_i915_getparam { - int param; + abi_int param; abi_ulong value; }; @@ -2732,26 +2617,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; @@ -2773,14 +2658,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 */ @@ -2809,40 +2694,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 */ @@ -2863,4 +2748,29 @@ struct target_sched_param { abi_int sched_priority; }; +/* from kernel's include/uapi/linux/openat2.h */ +struct open_how_ver0 { + uint64_t flags; + uint64_t mode; + uint64_t resolve; +}; +struct target_open_how_ver0 { + abi_ullong flags; + abi_ullong mode; + abi_ullong resolve; +}; +#ifndef RESOLVE_NO_MAGICLINKS +#define RESOLVE_NO_MAGICLINKS 0x02 +#endif +#ifndef RESOLVE_NO_SYMLINKS +#define RESOLVE_NO_SYMLINKS 0x04 +#endif + +#if (defined(TARGET_I386) && defined(TARGET_ABI32)) || \ + (defined(TARGET_ARM) && defined(TARGET_ABI32)) || \ + defined(TARGET_M68K) || defined(TARGET_MICROBLAZE) || \ + defined(TARGET_S390X) +#define TARGET_ARCH_WANT_SYS_OLD_MMAP +#endif + #endif diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index c3b43f8022..6dd7a80ce5 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -341,6 +341,11 @@ STRUCT(file_clone_range, TYPE_ULONGLONG, /* src_length */ TYPE_ULONGLONG) /* dest_offset */ +STRUCT(fstrim_range, + TYPE_ULONGLONG, /* start */ + TYPE_ULONGLONG, /* len */ + TYPE_ULONGLONG) /* minlen */ + STRUCT(fiemap_extent, TYPE_ULONGLONG, /* fe_logical */ TYPE_ULONGLONG, /* fe_physical */ diff --git a/linux-user/thunk.c b/linux-user/thunk.c index dac4bf11c6..3cd19e79c6 100644 --- a/linux-user/thunk.c +++ b/linux-user/thunk.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qemu.h" -#include "exec/user/thunk.h" +#include "user/thunk.h" //#define DEBUG @@ -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/uaccess.c b/linux-user/uaccess.c index 425cbf677f..27e841e651 100644 --- a/linux-user/uaccess.c +++ b/linux-user/uaccess.c @@ -14,7 +14,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy) return NULL; } host_addr = g2h_untagged(guest_addr); -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP if (copy) { host_addr = g_memdup(host_addr, len); } else { @@ -24,7 +24,7 @@ void *lock_user(int type, abi_ulong guest_addr, ssize_t len, bool copy) return host_addr; } -#ifdef DEBUG_REMAP +#ifdef CONFIG_DEBUG_REMAP void unlock_user(void *host_ptr, abi_ulong guest_addr, ssize_t len) { void *host_ptr_conv; diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 0280e76add..46ffc093f4 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -18,8 +18,9 @@ #ifndef LINUX_USER_USER_INTERNALS_H #define LINUX_USER_USER_INTERNALS_H -#include "exec/user/thunk.h" +#include "user/thunk.h" #include "exec/exec-all.h" +#include "exec/tb-flush.h" #include "qemu/log.h" extern char *exec_path; @@ -70,24 +71,24 @@ 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. */ @@ -101,7 +102,6 @@ int host_to_target_waitstatus(int status); /* vm86.c */ void save_v86_state(CPUX86State *env); void handle_vm86_trap(CPUX86State *env, int trapno); -void handle_vm86_fault(CPUX86State *env); int do_vm86(CPUX86State *env, long subfunction, abi_ulong v86_addr); #elif defined(TARGET_SPARC64) void sparc64_set_context(CPUSPARCState *env); @@ -135,7 +135,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 480ce1c114..b94bcdcf83 100644 --- a/linux-user/user-mmap.h +++ b/linux-user/user-mmap.h @@ -18,18 +18,48 @@ #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); abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice); -extern unsigned long last_brk; -extern abi_ulong mmap_next_start; abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong); void mmap_fork_start(void); void mmap_fork_end(int child); +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..5091d53fb8 100644 --- a/linux-user/vm86.c +++ b/linux-user/vm86.c @@ -47,34 +47,10 @@ static inline void vm_putw(CPUX86State *env, uint32_t segptr, cpu_stw_data(env, segptr + (reg16 & 0xffff), val); } -static inline void vm_putl(CPUX86State *env, uint32_t segptr, - unsigned int reg16, unsigned int val) -{ - cpu_stl_data(env, segptr + (reg16 & 0xffff), val); -} - -static inline unsigned int vm_getb(CPUX86State *env, - uint32_t segptr, unsigned int reg16) -{ - return cpu_ldub_data(env, segptr + (reg16 & 0xffff)); -} - -static inline unsigned int vm_getw(CPUX86State *env, - uint32_t segptr, unsigned int reg16) -{ - return cpu_lduw_data(env, segptr + (reg16 & 0xffff)); -} - -static inline unsigned int vm_getl(CPUX86State *env, - uint32_t segptr, unsigned int reg16) -{ - return cpu_ldl_data(env, segptr + (reg16 & 0xffff)); -} - 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)) @@ -131,23 +107,10 @@ static inline void return_to_32bit(CPUX86State *env, int retval) env->regs[R_EAX] = retval; } -static inline int set_IF(CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - - ts->v86flags |= VIF_MASK; - if (ts->v86flags & VIP_MASK) { - return_to_32bit(env, TARGET_VM86_STI); - return 1; - } - return 0; -} - 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; } @@ -162,38 +125,10 @@ static inline void clear_AC(CPUX86State *env) env->eflags &= ~AC_MASK; } -static inline int set_vflags_long(unsigned long eflags, CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - - set_flags(ts->v86flags, eflags, ts->v86mask); - set_flags(env->eflags, eflags, SAFE_MASK); - if (eflags & IF_MASK) - return set_IF(env); - else - clear_IF(env); - return 0; -} - -static inline int set_vflags_short(unsigned short flags, CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - - set_flags(ts->v86flags, flags, ts->v86mask & 0xffff); - set_flags(env->eflags, flags, SAFE_MASK); - if (flags & IF_MASK) - return set_IF(env); - else - clear_IF(env); - return 0; -} - 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 +145,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; @@ -255,146 +190,10 @@ void handle_vm86_trap(CPUX86State *env, int trapno) } } -#define CHECK_IF_IN_TRAP() \ - if ((ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) && \ - (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_TFpendig)) \ - newflags |= TF_MASK - -#define VM86_FAULT_RETURN \ - if ((ts->vm86plus.vm86plus.flags & TARGET_force_return_for_pic) && \ - (ts->v86flags & (IF_MASK | VIF_MASK))) \ - return_to_32bit(env, TARGET_VM86_PICRETURN); \ - return - -void handle_vm86_fault(CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - TaskState *ts = cs->opaque; - uint32_t csp, ssp; - unsigned int ip, sp, newflags, newip, newcs, opcode, intno; - int data32, pref_done; - - csp = env->segs[R_CS].selector << 4; - ip = env->eip & 0xffff; - - ssp = env->segs[R_SS].selector << 4; - sp = env->regs[R_ESP] & 0xffff; - - LOG_VM86("VM86 exception %04x:%08x\n", - env->segs[R_CS].selector, env->eip); - - data32 = 0; - pref_done = 0; - do { - opcode = vm_getb(env, csp, ip); - ADD16(ip, 1); - switch (opcode) { - case 0x66: /* 32-bit data */ data32=1; break; - case 0x67: /* 32-bit address */ break; - case 0x2e: /* CS */ break; - case 0x3e: /* DS */ break; - case 0x26: /* ES */ break; - case 0x36: /* SS */ break; - case 0x65: /* GS */ break; - case 0x64: /* FS */ break; - case 0xf2: /* repnz */ break; - case 0xf3: /* rep */ break; - default: pref_done = 1; - } - } while (!pref_done); - - /* VM86 mode */ - switch(opcode) { - case 0x9c: /* pushf */ - if (data32) { - vm_putl(env, ssp, sp - 4, get_vflags(env)); - ADD16(env->regs[R_ESP], -4); - } else { - vm_putw(env, ssp, sp - 2, get_vflags(env)); - ADD16(env->regs[R_ESP], -2); - } - env->eip = ip; - VM86_FAULT_RETURN; - - case 0x9d: /* popf */ - if (data32) { - newflags = vm_getl(env, ssp, sp); - ADD16(env->regs[R_ESP], 4); - } else { - newflags = vm_getw(env, ssp, sp); - ADD16(env->regs[R_ESP], 2); - } - env->eip = ip; - CHECK_IF_IN_TRAP(); - if (data32) { - if (set_vflags_long(newflags, env)) - return; - } else { - if (set_vflags_short(newflags, env)) - return; - } - VM86_FAULT_RETURN; - - case 0xcd: /* int */ - intno = vm_getb(env, csp, ip); - ADD16(ip, 1); - env->eip = ip; - if (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) { - if ( (ts->vm86plus.vm86plus.vm86dbg_intxxtab[intno >> 3] >> - (intno &7)) & 1) { - return_to_32bit(env, TARGET_VM86_INTx + (intno << 8)); - return; - } - } - do_int(env, intno); - break; - - case 0xcf: /* iret */ - if (data32) { - newip = vm_getl(env, ssp, sp) & 0xffff; - newcs = vm_getl(env, ssp, sp + 4) & 0xffff; - newflags = vm_getl(env, ssp, sp + 8); - ADD16(env->regs[R_ESP], 12); - } else { - newip = vm_getw(env, ssp, sp); - newcs = vm_getw(env, ssp, sp + 2); - newflags = vm_getw(env, ssp, sp + 4); - ADD16(env->regs[R_ESP], 6); - } - env->eip = newip; - cpu_x86_load_seg(env, R_CS, newcs); - CHECK_IF_IN_TRAP(); - if (data32) { - if (set_vflags_long(newflags, env)) - return; - } else { - if (set_vflags_short(newflags, env)) - return; - } - VM86_FAULT_RETURN; - - case 0xfa: /* cli */ - env->eip = ip; - clear_IF(env); - VM86_FAULT_RETURN; - - case 0xfb: /* sti */ - env->eip = ip; - if (set_IF(env)) - return; - VM86_FAULT_RETURN; - - default: - /* real VM86 GPF exception */ - return_to_32bit(env, TARGET_VM86_UNKNOWN); - break; - } -} - 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/syscall_64.tbl b/linux-user/x86_64/syscall_64.tbl index ce18119ea0..7093ee21c0 100644 --- a/linux-user/x86_64/syscall_64.tbl +++ b/linux-user/x86_64/syscall_64.tbl @@ -1,8 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note # # 64-bit system call numbers and entry vectors # # The format is: -# +# [ [noreturn]] # # The __x64_sys_*() stubs are created on-the-fly for sys_*() system calls # @@ -68,7 +69,7 @@ 57 common fork sys_fork 58 common vfork sys_vfork 59 64 execve sys_execve -60 common exit sys_exit +60 common exit sys_exit - noreturn 61 common wait4 sys_wait4 62 common kill sys_kill 63 common uname sys_newuname @@ -220,7 +221,7 @@ 209 64 io_submit sys_io_submit 210 common io_cancel sys_io_cancel 211 64 get_thread_area -212 common lookup_dcookie sys_lookup_dcookie +212 common lookup_dcookie 213 common epoll_create sys_epoll_create 214 64 epoll_ctl_old 215 64 epoll_wait_old @@ -239,7 +240,7 @@ 228 common clock_gettime sys_clock_gettime 229 common clock_getres sys_clock_getres 230 common clock_nanosleep sys_clock_nanosleep -231 common exit_group sys_exit_group +231 common exit_group sys_exit_group - noreturn 232 common epoll_wait sys_epoll_wait 233 common epoll_ctl sys_epoll_ctl 234 common tgkill sys_tgkill @@ -343,6 +344,7 @@ 332 common statx sys_statx 333 common io_pgetevents sys_io_pgetevents 334 common rseq sys_rseq +335 common uretprobe sys_uretprobe # don't use numbers 387 through 423, add new calls after the last # 'common' entry 424 common pidfd_send_signal sys_pidfd_send_signal @@ -364,10 +366,26 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +447 common memfd_secret sys_memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal # # Due to a historical design error, certain syscalls are numbered differently @@ -396,7 +414,7 @@ 530 x32 set_robust_list compat_sys_set_robust_list 531 x32 get_robust_list compat_sys_get_robust_list 532 x32 vmsplice sys_vmsplice -533 x32 move_pages compat_sys_move_pages +533 x32 move_pages sys_move_pages 534 x32 preadv compat_sys_preadv64 535 x32 pwritev compat_sys_pwritev64 536 x32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo diff --git a/linux-user/x86_64/syscallhdr.sh b/linux-user/x86_64/syscallhdr.sh index 182be52a74..988256b6c6 100644 --- a/linux-user/x86_64/syscallhdr.sh +++ b/linux-user/x86_64/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/x86_64/target_mman.h b/linux-user/x86_64/target_mman.h index e7ba6070fe..48fbf20b42 100644 --- a/linux-user/x86_64/target_mman.h +++ b/linux-user/x86_64/target_mman.h @@ -1 +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/syscall.tbl b/linux-user/xtensa/syscall.tbl index fd2f30227d..735a89b3bd 100644 --- a/linux-user/xtensa/syscall.tbl +++ b/linux-user/xtensa/syscall.tbl @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note +# SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note # # system call numbers and entry vectors for xtensa # @@ -223,7 +223,7 @@ # 205 was old nfsservctl 205 common nfsservctl sys_ni_syscall 206 common _sysctl sys_ni_syscall -207 common bdflush sys_bdflush +207 common bdflush sys_ni_syscall 208 common uname sys_newuname 209 common sysinfo sys_sysinfo 210 common init_module sys_init_module @@ -273,7 +273,7 @@ 252 common timer_getoverrun sys_timer_getoverrun # System 253 common reserved253 sys_ni_syscall -254 common lookup_dcookie sys_lookup_dcookie +254 common lookup_dcookie sys_ni_syscall 255 common available255 sys_ni_syscall 256 common add_key sys_add_key 257 common request_key sys_request_key @@ -413,7 +413,23 @@ 440 common process_madvise sys_process_madvise 441 common epoll_pwait2 sys_epoll_pwait2 442 common mount_setattr sys_mount_setattr -# 443 reserved for quotactl_path +443 common quotactl_fd sys_quotactl_fd 444 common landlock_create_ruleset sys_landlock_create_ruleset 445 common landlock_add_rule sys_landlock_add_rule 446 common landlock_restrict_self sys_landlock_restrict_self +# 447 reserved for memfd_secret +448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node +451 common cachestat sys_cachestat +452 common fchmodat2 sys_fchmodat2 +453 common map_shadow_stack sys_map_shadow_stack +454 common futex_wake sys_futex_wake +455 common futex_wait sys_futex_wait +456 common futex_requeue sys_futex_requeue +457 common statmount sys_statmount +458 common listmount sys_listmount +459 common lsm_get_self_attr sys_lsm_get_self_attr +460 common lsm_set_self_attr sys_lsm_set_self_attr +461 common lsm_list_modules sys_lsm_list_modules +462 common mseal sys_mseal diff --git a/linux-user/xtensa/syscallhdr.sh b/linux-user/xtensa/syscallhdr.sh index eef0644c94..dc787fbbfe 100644 --- a/linux-user/xtensa/syscallhdr.sh +++ b/linux-user/xtensa/syscallhdr.sh @@ -1,5 +1,5 @@ #!/bin/sh -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only in="$1" out="$2" diff --git a/linux-user/xtensa/target_mman.h b/linux-user/xtensa/target_mman.h index e7ba6070fe..8fa6337a97 100644 --- a/linux-user/xtensa/target_mman.h +++ b/linux-user/xtensa/target_mman.h @@ -1 +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 1e2e30bbcf..0000000000 --- a/meson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1e2e30bbcfea876e05ce5b863579dc7eb71b5fb3 diff --git a/meson.build b/meson.build index 53923ee72a..6d6aadd0d7 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ -project('qemu', ['c'], meson_version: '>=0.61.3', +project('qemu', ['c', 'cpp'], meson_version: '>=1.5.0', default_options: ['warning_level=1', 'c_std=gnu11', 'cpp_std=c++17', 'b_colorout=auto', - 'b_staticpic=false', 'stdsplit=false', 'optimization=3', 'b_pie=true'], + 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], version: files('QEMU_VERSION')) add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) @@ -9,19 +9,18 @@ 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') +rust = import('rust') 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 @@ -42,25 +41,19 @@ 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', +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() + +# type of binaries to build have_linux_user = false have_bsd_user = false have_system = false @@ -70,90 +63,62 @@ foreach target : target_dirs 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', 'freebsd'], - 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() cmake = import('cmake') cmake_macos_arch = host_machine.cpu() == 'aarch64' ? 'arm64' : host_machine.cpu() -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') +cxx = meson.get_compiler('cpp') +all_languages = ['c', 'cpp'] +if host_os == 'darwin' and \ + add_languages('objc', required: true, native: false) + all_languages += ['objc'] + objc = meson.get_compiler('objc') 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 = [] -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 +have_rust = add_languages('rust', native: false, + required: get_option('rust').disable_auto_if(not have_system)) +have_rust = have_rust and add_languages('rust', native: true, + required: get_option('rust').disable_auto_if(not have_system)) +if have_rust + rustc = meson.get_compiler('rust') + if rustc.version().version_compare('<1.63.0') + if get_option('rust').enabled() + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.63.0') + else + warning('rustc version ' + rustc.version() + ' is unsupported, disabling Rust compilation.') + message('Please upgrade to at least 1.63.0 to use Rust.') + have_rust = false + endif endif -endforeach +endif + +if have_rust + bindgen = find_program('bindgen', required: get_option('rust')) + if not bindgen.found() or bindgen.version().version_compare('<0.60.0') + if get_option('rust').enabled() + error('bindgen version ' + bindgen.version() + ' is unsupported. You can install a new version with "cargo install bindgen-cli"') + else + if bindgen.found() + warning('bindgen version ' + bindgen.version() + ' is unsupported, disabling Rust compilation.') + else + warning('bindgen not found, disabling Rust compilation.') + endif + message('To use Rust you can install a new version with "cargo install bindgen-cli"') + have_rust = false + endif + endif +endif + +if have_rust + rustfmt = find_program('rustfmt', required: false) +endif dtrace = not_found stap = not_found @@ -167,7 +132,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 @@ -177,134 +142,15 @@ else iasl = find_program(get_option('iasl'), required: true) endif -################## -# Compiler flags # -################## - -qemu_cflags = config_host['QEMU_CFLAGS'].split() -qemu_objcflags = config_host['QEMU_OBJCFLAGS'].split() -qemu_ldflags = config_host['QEMU_LDFLAGS'].split() - -if enable_static - qemu_ldflags += get_option('b_pie') ? '-static-pie' : '-static' -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 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', 'riscv64-softmmu', 'loongarch64-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 - -if get_option('gprof') - qemu_cflags += ['-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) - - if cc.compiles('int main () { return 0; }', - name: '-fsanitize-coverage-allowlist=/dev/null', - args: ['-fsanitize-coverage-allowlist=/dev/null', - '-fsanitize-coverage=trace-pc'] ) - add_global_arguments('-fsanitize-coverage-allowlist=instrumentation-filter', - native: false, language: ['c', 'cpp', 'objc']) - 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. - 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_objcflags, native: false, language: ['objc']) - -# Check that the C++ compiler exists and works with the C compiler. -link_language = 'c' -linker = cc -qemu_cxxflags = [] -if add_languages('cpp', required: false, native: false) - cxx = meson.get_compiler('cpp') - add_global_arguments(['-D__STDC_LIMIT_MACROS', '-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS'], - native: false, language: 'cpp') - foreach k: qemu_cflags - if k not in ['-Wstrict-prototypes', '-Wmissing-prototypes', '-Wnested-externs', - '-Wold-style-declaration', '-Wold-style-definition', '-Wredundant-decls'] - qemu_cxxflags += [k] - endif - endforeach - add_global_arguments(qemu_cxxflags, native: false, language: 'cpp') - - if cxx.links(files('scripts/main.c'), args: qemu_cflags) - link_language = 'cpp' - linker = cxx - else - message('C++ compiler does not work with C compiler') - message('Disabling C++-specific optional code') - endif -endif - -# Exclude --warn-common with TSan to suppress warnings from the TSan libraries. -if targetos != 'sunos' and not config_host.has_key('CONFIG_TSAN') - qemu_ldflags += linker.get_supported_link_arguments('-Wl,--warn-common') -endif - -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', - language: ['c', 'cpp', 'objc']) - -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 +endforeach ########################################### # xemu Version @@ -319,10 +165,9 @@ xemu_version = custom_target('xemu-version-macro.h', build_by_default: true, build_always_stale: true) - -########################################### -# Target-specific checks and dependencies # -########################################### +##################### +# Option validation # +##################### # Fuzzing if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \ @@ -337,7 +182,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(''' @@ -352,30 +197,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(targetos == 'linux', error_message: 'vfio-user server is supported only on Linux') \ + .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, @@ -388,10 +233,574 @@ 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 +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__ < 15 || (__clang_major__ == 15 && __clang_minor__ < 0) + # error You need at least XCode Clang v15.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 v15.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'] +# elif host_os == 'windows' +# if not compiler.compiles('struct x { int y; } __attribute__((gcc_struct));', +# args: '-Werror') +# error('Your compiler does not support __attribute__((gcc_struct)) - please use GCC instead of Clang') +# endif +endif + +# Choose instruction set (currently x86-only) + +qemu_isa_flags = [] + +# __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_isa_flags += ['-march=i486'] +endif + +# Pick x86-64 baseline version +if host_arch in ['i386', 'x86_64'] + if get_option('x86_version') == '0' and host_arch == 'x86_64' + error('x86_64-v1 required for x86-64 hosts') + endif + + # add flags for individual instruction set extensions + if get_option('x86_version') >= '1' + if host_arch == 'i386' + qemu_common_flags = ['-mfpmath=sse'] + qemu_common_flags + else + # present on basically all processors but technically not part of + # x86-64-v1, so only include -mneeded for x86-64 version 2 and above + qemu_isa_flags += ['-mcx16'] + endif + endif + if get_option('x86_version') >= '2' + qemu_isa_flags += ['-mpopcnt'] + qemu_isa_flags += cc.get_supported_arguments('-mneeded') + endif + if get_option('x86_version') >= '3' + qemu_isa_flags += ['-mmovbe', '-mabm', '-mbmi', '-mbmi2', '-mfma', '-mf16c'] + endif + + # add required vector instruction set (each level implies those below) + if get_option('x86_version') == '1' + qemu_isa_flags += ['-msse2'] + elif get_option('x86_version') == '2' + qemu_isa_flags += ['-msse4.2'] + elif get_option('x86_version') == '3' + qemu_isa_flags += ['-mavx2'] + elif get_option('x86_version') == '4' + qemu_isa_flags += ['-mavx512f', '-mavx512bw', '-mavx512cd', '-mavx512dq', '-mavx512vl'] + endif +endif + +qemu_common_flags = qemu_isa_flags + qemu_common_flags + +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('asan') + if cc.has_argument('-fsanitize=address') + qemu_cflags = ['-fsanitize=address'] + qemu_cflags + qemu_ldflags = ['-fsanitize=address'] + qemu_ldflags + else + error('Your compiler does not support -fsanitize=address') + endif +endif + +if get_option('ubsan') + # 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_ldflags += ['-fsanitize=undefined'] + + # Suppress undefined behaviour from function call to mismatched type. + # In addition, tcg prologue does not emit function type prefix + # required by function call sanitizer. + if cc.has_argument('-fno-sanitize=function') + qemu_cflags += ['-fno-sanitize=function'] + endif + else + error('Your compiler does not support -fsanitize=undefined') + 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('asan') or get_option('ubsan') + 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 + tsan_warn_suppress = [] + # gcc (>=11) will report constructions not supported by tsan: + # "error: ‘atomic_thread_fence’ is not supported with ‘-fsanitize=thread’" + # https://gcc.gnu.org/gcc-11/changes.html + # However, clang does not support this warning and this triggers an error. + if cc.has_argument('-Wno-tsan') + tsan_warn_suppress = ['-Wno-tsan'] + endif + qemu_cflags = ['-fsanitize=thread'] + tsan_warn_suppress + 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' + tsa_has_cleanup = cc.compiles(''' + struct __attribute__((capability("mutex"))) mutex {}; + void lock(struct mutex *m) __attribute__((acquire_capability(m))); + void unlock(struct mutex *m) __attribute__((release_capability(m))); + + void test(void) { + struct mutex __attribute__((cleanup(unlock))) m; + lock(&m); + } + ''', args: ['-Wthread-safety', '-Werror']) + if tsa_has_cleanup + warn_flags += ['-Wthread-safety'] + endif +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) -if targetos == 'windows' +if host_os == 'windows' threads = declare_dependency() meson.override_dependency('threads', threads) else @@ -405,13 +814,11 @@ 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') @@ -425,38 +832,43 @@ if targetos == 'windows' include_directories: include_directories('.'), depends: xemu_version) 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')) @@ -464,12 +876,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' @@ -479,9 +888,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') @@ -500,10 +907,9 @@ if get_option('tcg').allowed() 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() @@ -519,1026 +925,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 -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', 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 -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 - -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', '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', kwargs: static_kwargs) - 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'), - 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 - -slirp = not_found -if not get_option('slirp').auto() or have_system - slirp = dependency('slirp', required: get_option('slirp'), - method: 'pkg-config', kwargs: static_kwargs) - # 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'), - 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 -sndio = not_found -if not get_option('sndio').auto() or have_system - sndio = dependency('sndio', required: get_option('sndio'), - 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 -blkio = not_found -if not get_option('blkio').auto() or have_block - blkio = dependency('blkio', - method: 'pkg-config', - required: get_option('blkio'), - 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'] - curses = dependency(curses_dep_list, - required: false, - method: 'pkg-config', - kwargs: static_kwargs) - 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 - 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 - # FIXME: Use meson's 'gl' dep - if targetos == 'darwin' - opengl_libs=['-framework', 'OpenGL'] - elif targetos == 'windows' - opengl_libs=['-lopengl32', '-lgdi32'] - elif targetos == 'linux' - opengl_libs=['-lGL'] - else - error('Unknown GL platform') - endif - - opengl = declare_dependency(compile_args: config_host['EPOXY_CFLAGS'].split(), - link_args: config_host['EPOXY_LIBS'].split() + opengl_libs) -endif - -vulkan = not_found -libglslang = not_found - -if targetos == 'windows' - vulkan = declare_dependency(compile_args: ['-DVK_USE_PLATFORM_WIN32_KHR']) - libglslang = declare_dependency(link_args: [ - '-lglslang', - '-lMachineIndependent', - '-lGenericCodeGen', - '-lSPIRV', - '-lSPIRV-Tools', - '-lSPIRV-Tools-opt' - ]) -elif targetos == 'linux' - vulkan = dependency('vulkan') -endif - -if vulkan.found() - if not libglslang.found() - # FIXME: Get spirv-tools to enable opt. - glslang_opts = cmake.subproject_options() - glslang_opts.add_cmake_defines({'ENABLE_OPT': false}) - glslang_subpro = cmake.subproject('glslang', options: glslang_opts) - libglslang = declare_dependency(link_with: [ - glslang_subpro.target('glslang'), - glslang_subpro.target('MachineIndependent'), - glslang_subpro.target('GenericCodeGen'), - glslang_subpro.target('SPIRV'), - ], include_directories: glslang_subpro.include_directories('glslang') - ) - endif - - volk_opts = cmake.subproject_options() - volk_opts.add_cmake_defines({'VOLK_STATIC_DEFINES': 'VK_NO_PROTOTYPES'}) - if targetos == 'windows' - volk_opts.append_compile_args('c', '-DVK_USE_PLATFORM_WIN32_KHR=1') - endif - volk_subproj = cmake.subproject('volk', options: volk_opts) - volk = declare_dependency(compile_args: ['-DVK_NO_PROTOTYPES'], - include_directories: volk_subproj.include_directories('volk'), - link_with: volk_subproj.target('volk'), - dependencies: vulkan) - - spirv_reflect_opts = cmake.subproject_options() - spirv_reflect_opts.add_cmake_defines({'SPIRV_REFLECT_STATIC_LIB': 'ON'}) - spirv_reflect_subproj = cmake.subproject('SPIRV-Reflect', options: spirv_reflect_opts) - spirv_reflect = declare_dependency(include_directories: spirv_reflect_subproj.include_directories('spirv-reflect-static'), - link_with: spirv_reflect_subproj.target('spirv-reflect-static'), - dependencies: vulkan) -endif - -subdir('thirdparty') - - -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 opengl.found() and gbm.found() - -genconfig = declare_dependency(include_directories: 'genconfig') - -openssl = dependency('openssl', method: 'pkg-config', required: true, - kwargs: static_kwargs) - -if targetos == 'windows' - libpcap = declare_dependency(include_directories: 'winpcap-loader/include', - link_args: ['-lws2_32']) -else - libpcap = dependency('libpcap', method: 'pkg-config', required: true, - kwargs: static_kwargs) -endif - -libsamplerate = dependency('samplerate', method: 'pkg-config', required: true, - kwargs: static_kwargs) - -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 -have_gtk_clipboard = get_option('gtk_clipboard').enabled() - -# xemu: GTK is required in Linux builds for file selection interfaces. In the -# future, this may be relaxed in the future with alternative options. -require_gtk = targetos == 'linux' - -if require_gtk - gtk = dependency('gtk+-3.0', version: '>=3.22.0', - method: 'pkg-config', - required: require_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 - 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(), - kwargs: static_kwargs) -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', 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 @@ -1573,68 +998,1169 @@ 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. +# You should also check if any of the glib.version() checks +# below can also be removed. +glib_req_ver = '>=2.66.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 + +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') +# Check whether glib has the aligned_alloc family of functions. +# +glib_has_aligned_alloc = glib.version().version_compare('>=2.72.0') + +# 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('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', 'QuartzCore'], + 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 +qpl = not_found +if not get_option('qpl').auto() or have_system + qpl = dependency('qpl', version: '>=1.5.0', + required: get_option('qpl'), + method: 'pkg-config') +endif +uadk = not_found +if not get_option('uadk').auto() or have_system + libwd = dependency('libwd', version: '>=2.6', + required: get_option('uadk'), + method: 'pkg-config') + libwd_comp = dependency('libwd_comp', version: '>=2.6', + required: get_option('uadk'), + method: 'pkg-config') + if libwd.found() and libwd_comp.found() + uadk = declare_dependency(dependencies: [libwd, libwd_comp]) + endif +endif + +qatzip = not_found +if not get_option('qatzip').auto() or have_system + qatzip = dependency('qatzip', version: '>=1.1.2', + required: get_option('qatzip'), + 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 = ''' + #ifdef __APPLE__ + #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 + # FIXME: Use meson's 'gl' dep + if host_os == 'darwin' + opengl_libs=['-framework', 'OpenGL'] + elif host_os == 'windows' + opengl_libs=['-lopengl32', '-lgdi32'] + elif host_os == 'linux' + opengl_libs=['-lGL'] + else + error('Unknown GL platform') + endif + + opengl = declare_dependency(compile_args: config_host['EPOXY_CFLAGS'].split(), + link_args: config_host['EPOXY_LIBS'].split() + opengl_libs) +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() + +libcbor = not_found +if not get_option('libcbor').auto() or have_system + libcbor = dependency('libcbor', version: '>=0.7.0', + required: get_option('libcbor')) +endif + +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 +crypto_sm3 = 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', + 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 + crypto_sm3 = gcrypt + # SM3 ALG is available in libgcrypt >= 1.9 + if gcrypt.found() and not cc.links(''' + #include + int main(void) { + gcry_md_hd_t handler; + gcry_md_open(&handler, GCRY_MD_SM3, 0); + return 0; + }''', dependencies: gcrypt) + crypto_sm3 = 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 + crypto_sm3 = nettle + # SM3 ALG is available in nettle >= 3.8 + if nettle.found() and not cc.links(''' + #include + #include + int main(void) { + struct sm3_ctx ctx; + struct hmac_sm3_ctx hmac_ctx; + unsigned char data[64] = {0}; + unsigned char output[32]; + + // SM3 hash function test + sm3_init(&ctx); + sm3_update(&ctx, 64, data); + sm3_digest(&ctx, 32, data); + + // HMAC-SM3 test + hmac_sm3_set_key(&hmac_ctx, 32, data); + hmac_sm3_update(&hmac_ctx, 64, data); + hmac_sm3_digest(&hmac_ctx, 32, output); + + return 0; + }''', dependencies: nettle) + crypto_sm3 = 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() + +# xemu: GTK is required in Linux builds for file selection interfaces. In the +# future, this may be relaxed in the future with alternative options. +require_gtk = host_os == 'linux' + +if require_gtk + gtk = dependency('gtk+-3.0', version: '>=3.22.0', + method: 'pkg-config', + required: require_gtk or 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 + +fdt = not_found +fdt_opt = get_option('fdt') +if fdt_opt == 'enabled' and get_option('wrap_mode') == 'nodownload' + fdt_opt = 'system' +endif +if fdt_opt in ['enabled', 'system'] or (fdt_opt == 'auto' and have_system) + fdt = cc.find_library('fdt', required: fdt_opt == 'system') + if fdt.found() and cc.links(''' + #include + #include + int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', + dependencies: fdt) + fdt_opt = 'system' + elif fdt_opt != 'system' + fdt_opt = get_option('wrap_mode') == 'nodownload' ? 'disabled' : 'internal' + fdt = not_found + else + error('system libfdt is too old (1.5.1 or newer required)') + endif +endif +if fdt_opt == 'internal' + assert(not fdt.found()) + 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 + +rdma = not_found +if not get_option('rdma').auto() or have_system + rdma_libs = [cc.find_library('rdmacm', has_headers: ['rdma/rdma_cma.h'], + required: get_option('rdma')), + cc.find_library('ibverbs', required: get_option('rdma'))] + 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 have_system +if not get_option('canokey').auto() or have_system canokey = dependency('canokey-qemu', required: get_option('canokey'), - method: 'pkg-config', - kwargs: static_kwargs) + 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) + required: false, + 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 @@ -1642,8 +2168,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) @@ -1656,37 +2181,22 @@ 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') \ @@ -1698,8 +2208,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() @@ -1715,18 +2224,18 @@ if get_option('fuse_lseek').allowed() endif endif -have_libvduse = (targetos == 'linux') +have_libvduse = (host_os == 'linux') if get_option('libvduse').enabled() - if targetos != 'linux' + 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 targetos == 'linux') +have_vduse_blk_export = (have_libvduse and host_os == 'linux') if get_option('vduse_blk_export').enabled() - if targetos != 'linux' + if host_os != 'linux' error('vduse_blk_export requires linux') elif not have_libvduse error('vduse_blk_export requires libvduse support') @@ -1736,26 +2245,148 @@ elif get_option('vduse_blk_export').disabled() 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 + if libbpf.found() + libxdp = dependency('libxdp', required: get_option('af_xdp'), + version: '>=1.4.0', method: 'pkg-config') + else + if get_option('af_xdp').enabled() + error('libxdp requested, but libbpf is not available') + endif + endif +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 + +vulkan = not_found +libglslang = not_found + +if host_os == 'windows' + vulkan = declare_dependency(compile_args: ['-DVK_USE_PLATFORM_WIN32_KHR']) + libglslang = declare_dependency(link_args: [ + '-lglslang', + '-lMachineIndependent', + '-lGenericCodeGen', + '-lSPIRV', + '-lSPIRV-Tools', + '-lSPIRV-Tools-opt' + ]) +elif host_os == 'linux' + vulkan = dependency('vulkan') +endif + +if vulkan.found() + if not libglslang.found() + # FIXME: Get spirv-tools to enable opt. + glslang_opts = cmake.subproject_options() + glslang_opts.add_cmake_defines({'ENABLE_OPT': false}) + glslang_subpro = cmake.subproject('glslang', options: glslang_opts) + libglslang = declare_dependency(link_with: [ + glslang_subpro.target('glslang'), + glslang_subpro.target('MachineIndependent'), + glslang_subpro.target('GenericCodeGen'), + glslang_subpro.target('SPIRV'), + ], include_directories: glslang_subpro.include_directories('glslang') + ) + endif + + volk_opts = cmake.subproject_options() + volk_opts.add_cmake_defines({'VOLK_STATIC_DEFINES': 'VK_NO_PROTOTYPES'}) + if host_os == 'windows' + volk_opts.append_compile_args('c', '-DVK_USE_PLATFORM_WIN32_KHR=1') + endif + volk_subproj = cmake.subproject('volk', options: volk_opts) + volk = declare_dependency(compile_args: ['-DVK_NO_PROTOTYPES'], + include_directories: volk_subproj.include_directories('volk'), + link_with: volk_subproj.target('volk'), + dependencies: vulkan) + + spirv_reflect_opts = cmake.subproject_options() + spirv_reflect_opts.add_cmake_defines({'SPIRV_REFLECT_STATIC_LIB': 'ON'}) + spirv_reflect_subproj = cmake.subproject('SPIRV-Reflect', options: spirv_reflect_opts) + spirv_reflect = declare_dependency(include_directories: spirv_reflect_subproj.include_directories('spirv-reflect-static'), + link_with: spirv_reflect_subproj.target('spirv-reflect-static'), + dependencies: vulkan) +endif + +openssl = dependency('openssl', method: 'pkg-config', required: true) + +if host_os == 'windows' + libpcap = declare_dependency(include_directories: 'winpcap-loader/include', + link_args: ['-lws2_32']) +else + libpcap = dependency('libpcap', method: 'pkg-config', required: true) +endif + +libsamplerate = dependency('samplerate', method: 'pkg-config', required: true) + +imgui_proj = subproject('imgui', required: true, + default_options: [ + 'default_library=static', + 'dx9=disabled', + 'dx10=disabled', + 'dx11=disabled', + 'dx12=disabled', + 'metal=disabled', + 'opengl=enabled', + 'sdl2_renderer=disabled', + 'sdl3_renderer=disabled', + 'vulkan=disabled', + 'webgpu=disabled', + 'glfw=disabled', + 'sdl2=enabled', + 'sdl3=disabled', + 'osx=disabled', + 'win=disabled', + 'allegro5=disabled', + ]) +imgui = imgui_proj.get_variable('imgui_dep') + +implot_proj = subproject('implot', required: true, + default_options: ['default_library=static']) +implot = implot_proj.get_variable('implot_dep') + +genconfig_proj = subproject('genconfig', required: true) +genconfig = genconfig_proj.get_variable('cnode_dep') + +subdir('thirdparty') + ################# # config-host.h # ################# +config_host_data = configuration_data() + +config_host_data.set('CONFIG_HAVE_RUST', have_rust) audio_drivers_selected = [] if have_system audio_drivers_available = { @@ -1765,6 +2396,7 @@ if have_system 'jack': jack.found(), 'oss': oss.found(), 'pa': pulse.found(), + 'pipewire': pipewire.found(), 'sdl': sdl.found(), 'sndio': sndio.found(), } @@ -1775,7 +2407,7 @@ if have_system # Default to native drivers first, OSS second, SDL third audio_drivers_priority = \ [ 'pa', 'coreaudio', 'dsound', 'sndio', 'oss' ] + \ - (targetos == 'linux' ? [] : [ 'sdl' ]) + (host_os == 'linux' ? [] : [ 'sdl' ]) audio_drivers_default = [] foreach k: audio_drivers_priority if audio_drivers_available[k] @@ -1796,47 +2428,7 @@ 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')) have_renderdoc = false @@ -1861,21 +2453,30 @@ dbus_display = get_option('dbus_display') \ error_message: '-display dbus requires glib>=2.64') \ .require(gdbus_codegen.found(), error_message: gdbus_codegen_error.format('-display dbus')) \ - .require(opengl.found() and gbm.found(), - error_message: '-display dbus requires epoxy/egl and gbm') \ .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 +qga_fsfreeze = false +qga_fstrim = false +if host_os == 'linux' + if cc.has_header_symbol('linux/fs.h', 'FIFREEZE') + qga_fsfreeze = true + endif + if cc.has_header_symbol('linux/fs.h', 'FITRIM') + qga_fstrim = true + endif +elif host_os == 'freebsd' and cc.has_header_symbol('ufs/ffs/fs.h', 'UFSSUSPEND') + qga_fsfreeze = true +endif if get_option('block_drv_ro_whitelist') == '' config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '') @@ -1917,7 +2518,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'), '--', @@ -1926,18 +2527,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 @@ -1946,14 +2553,27 @@ 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_FREEBSD', host_os == 'freebsd') +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_FDT', fdt.found()) 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')) + config_host_data.set('CONFIG_BLKIO_WRITE_ZEROS_FUA', + blkio.version().version_compare('>=1.4.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()) @@ -1970,9 +2590,11 @@ 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()) @@ -1980,22 +2602,39 @@ 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_VULKAN', vulkan.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) @@ -2010,6 +2649,9 @@ 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('VIRGL_VERSION_MAJOR', virgl.version().split('.')[0]) +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()) @@ -2020,12 +2662,17 @@ 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_CRYPTO_SM3', crypto_sm3.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) config_host_data.set('CONFIG_STATX', has_statx) config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id) config_host_data.set('CONFIG_ZSTD', zstd.found()) +config_host_data.set('CONFIG_QPL', qpl.found()) +config_host_data.set('CONFIG_UADK', uadk.found()) +config_host_data.set('CONFIG_QATZIP', qatzip.found()) config_host_data.set('CONFIG_FUSE', fuse.found()) config_host_data.set('CONFIG_FUSE_LSEEK', fuse_lseek.found()) config_host_data.set('CONFIG_SPICE_PROTOCOL', spice_protocol.found()) @@ -2040,6 +2687,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'] @@ -2062,13 +2710,16 @@ 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_LIVE_BLOCK_MIGRATION', get_option('live_block_migration').allowed()) +config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg')) +config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap')) config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug')) config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed()) +config_host_data.set('CONFIG_FSFREEZE', qga_fsfreeze) +config_host_data.set('CONFIG_FSTRIM', qga_fstrim) # has_header config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h')) @@ -2076,11 +2727,12 @@ config_host_data.set('CONFIG_LINUX_MAGIC_H', cc.has_header('linux/magic.h')) config_host_data.set('CONFIG_VALGRIND_H', cc.has_header('valgrind/valgrind.h')) config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h')) config_host_data.set('HAVE_DRM_H', cc.has_header('libdrm/drm.h')) +config_host_data.set('HAVE_OPENAT2_H', cc.has_header('linux/openat2.h')) 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 targetos == 'windows' +if host_os == 'windows' config_host_data.set('HAVE_AFUNIX_H', cc.has_header('afunix.h')) endif @@ -2091,6 +2743,8 @@ 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 ')) @@ -2107,6 +2761,8 @@ 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_GLIB_WITH_ALIGNED_ALLOC', glib_has_aligned_alloc) 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 ')) @@ -2123,9 +2779,37 @@ if rdma.found() prefix: '#include ')) endif +have_asan_fiber = false +if get_option('asan') 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', @@ -2139,14 +2823,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', @@ -2157,8 +2833,6 @@ config_host_data.set('HAVE_OPTRESET', cc.has_header_symbol('getopt.h', 'optreset')) config_host_data.set('HAVE_IPPROTO_MPTCP', cc.has_header_symbol('netinet/in.h', 'IPPROTO_MPTCP')) -config_host_data.set('HAVE_SYS_MOUNT_FSCONFIG', - cc.has_header_symbol('sys/mount.h', 'FSCONFIG_SET_FLAG')) # has_member config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', @@ -2167,6 +2841,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', @@ -2222,10 +2899,16 @@ 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_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' - #include - #include - int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) + +# On Darwin posix_madvise() has the same return semantics as plain madvise(), +# i.e. errno is set and -1 is returned. That's not really how POSIX defines the +# function. On the flip side, it has madvise() which is preferred anyways. +if host_os != 'darwin' + config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' + #include + #include + int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) +endif config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(gnu_source_prefix + ''' #include @@ -2248,6 +2931,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 @@ -2344,25 +3039,29 @@ config_host_data.set('HAVE_BROKEN_SIZE_MAX', not cc.compiles(''' 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; - }''' + }''', args: qemu_isa_flags)) -# 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) { @@ -2371,28 +3070,46 @@ 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, args: qemu_isa_flags) 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, + args: qemu_isa_flags) + 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; + } + ''', args: qemu_isa_flags)) + endif endif endif @@ -2402,6 +3119,14 @@ config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' return getauxval(AT_HWCAP) == 0; }''')) +config_host_data.set('CONFIG_ELF_AUX_INFO', cc.links(gnu_source_prefix + ''' + #include + int main(void) { + unsigned long hwcap = 0; + elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap)); + return hwcap; + }''')) + config_host_data.set('CONFIG_USBFS', have_linux_user and cc.compiles(''' #include @@ -2416,7 +3141,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 @@ -2446,70 +3171,51 @@ have_cpuid_h = cc.links(''' }''') config_host_data.set('CONFIG_CPUID_H', have_cpuid_h) +# Don't bother to advertise asm/hwprobe.h for old versions that do +# not contain RISCV_HWPROBE_EXT_ZBA. +config_host_data.set('CONFIG_ASM_HWPROBE_H', + cc.has_header_symbol('asm/hwprobe.h', + 'RISCV_HWPROBE_EXT_ZBA')) + 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[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') \ +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(''' - #pragma GCC push_options - #pragma GCC target("avx512f") #include #include - static int bar(void *a) { - __m512i x = *(__m512i *)a; - return _mm512_test_epi64_mask(x, x); + 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[argc - 1]); } - '''), error_message: 'AVX512F not available').allowed()) + int main(int argc, char *argv[]) { return bar(argv[0]); } + '''), error_message: 'AVX512BW not available').allowed()) -have_pvrdma = get_option('pvrdma') \ - .require(rdma.found(), error_message: 'PVRDMA requires OpenFabrics libraries') \ - .require(cc.compiles(gnu_source_prefix + ''' - #include - int main(void) - { - char buf = 0; - void *addr = &buf; - addr = mremap(addr, 0, 1, MREMAP_MAYMOVE | MREMAP_FIXED); - - return 0; - }'''), error_message: 'PVRDMA requires mremap').allowed() - -if have_pvrdma - config_host_data.set('LEGACY_RDMA_REG_MR', not cc.links(''' - #include - int main(void) - { - struct ibv_mr *mr; - struct ibv_pd *pd = NULL; - size_t length = 10; - uint64_t iova = 0; - int access = 0; - void *addr = NULL; - - mr = ibv_reg_mr_iova(pd, addr, length, iova, access); - ibv_dereg_mr(mr); - return 0; - }''')) -endif +# 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); } + ''')) 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 @@ -2546,7 +3252,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 @@ -2555,15 +3261,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) { @@ -2573,14 +3273,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 = {} @@ -2589,7 +3310,6 @@ config_target_mak = {} disassemblers = { 'alpha' : ['CONFIG_ALPHA_DIS'], 'avr' : ['CONFIG_AVR_DIS'], - 'cris' : ['CONFIG_CRIS_DIS'], 'hexagon' : ['CONFIG_HEXAGON_DIS'], 'hppa' : ['CONFIG_HPPA_DIS'], 'i386' : ['CONFIG_I386_DIS'], @@ -2597,7 +3317,6 @@ disassemblers = { 'm68k' : ['CONFIG_M68K_DIS'], 'microblaze' : ['CONFIG_MICROBLAZE_DIS'], 'mips' : ['CONFIG_MIPS_DIS'], - 'nios2' : ['CONFIG_NIOS2_DIS'], 'or1k' : ['CONFIG_OPENRISC_DIS'], 'ppc' : ['CONFIG_PPC_DIS'], 'riscv' : ['CONFIG_RISCV_DIS'], @@ -2608,28 +3327,28 @@ disassemblers = { 'xtensa' : ['CONFIG_XTENSA_DIS'], 'loongarch' : ['CONFIG_LOONGARCH_DIS'], } -if link_language == 'cpp' - disassemblers += { - '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'] : []) + \ + (libcbor.found() ? ['CONFIG_LIBCBOR=y'] : []) + \ + (gnutls.found() ? ['CONFIG_GNUTLS=y'] : []) + \ (x11.found() ? ['CONFIG_X11=y'] : []) + \ + (fdt.found() ? ['CONFIG_FDT=y'] : []) + \ (have_vhost_user ? ['CONFIG_VHOST_USER=y'] : []) + \ (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'] : []) + \ - (have_pvrdma ? ['CONFIG_PVRDMA=y'] : []) + \ + (host_os == 'linux' ? ['CONFIG_LINUX=y'] : []) + \ (multiprocess_allowed ? ['CONFIG_MULTIPROCESS_ALLOWED=y'] : []) + \ - (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + (vfio_user_server_allowed ? ['CONFIG_VFIO_USER_SERVER_ALLOWED=y'] : []) + \ + (hv_balloon ? ['CONFIG_HV_BALLOON_POSSIBLE=y'] : []) + \ + (have_rust ? ['CONFIG_HAVE_RUST=y'] : []) ignored = [ 'TARGET_XML_FILES', 'TARGET_ABI_DIR', 'TARGET_ARCH' ] @@ -2639,7 +3358,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 @@ -2647,7 +3366,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 @@ -2655,47 +3374,52 @@ 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') config_target += { 'CONFIG_USER_ONLY': 'y', 'CONFIG_QEMU_INTERP_PREFIX': - get_option('interp_prefix').replace('%M', config_target['TARGET_NAME']) + get_option('interp_prefix').replace('%M', config_target['TARGET_NAME']), + 'CONFIG_QEMU_RTSIG_MAP': get_option('rtsig_map'), } endif - accel_kconfig = [] + target_kconfig = [] 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 config_target += { 'CONFIG_TCG_BUILTIN': 'y' } endif - accel_kconfig += [ sym + '=y' ] + target_kconfig += [ sym + '=y' ] endif endforeach - if accel_kconfig.length() == 0 + if target_kconfig.length() == 0 if default_targets continue endif error('No accelerator available for target @0@'.format(target)) endif - actual_target_dirs += target config_target += keyval.load('configs/targets' / target + '.mak') config_target += { 'TARGET_' + config_target['TARGET_ARCH'].to_upper(): 'y' } - if 'TARGET_NEED_FDT' in config_target - fdt_required += target + if 'TARGET_NEED_FDT' in config_target and not fdt.found() + if default_targets + warning('Disabling ' + target + ' due to missing libfdt') + else + fdt_required += target + endif + continue endif + actual_target_dirs += target + # Add default keys if 'TARGET_BASE_ARCH' not in config_target config_target += {'TARGET_BASE_ARCH': config_target['TARGET_ARCH']} @@ -2711,7 +3435,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 @@ -2742,6 +3465,9 @@ foreach target : target_dirs configuration: config_target_data)} if target.endswith('-softmmu') + target_kconfig += 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y' + target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN'] + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( @@ -2752,8 +3478,7 @@ foreach target : target_dirs command: [minikconf, get_option('default_devices') ? '--defconfig' : '--allnoconfig', config_devices_mak, '@DEPFILE@', '@INPUT@', - host_kconfig, accel_kconfig, - 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y']) + host_kconfig, target_kconfig]) config_devices_data = configuration_data() config_devices = keyval.load(config_devices_mak) @@ -2770,25 +3495,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] @@ -2801,99 +3507,31 @@ genh += custom_target('config-poison.h', command: [find_program('scripts/make-config-poison.sh'), target_configs_h]) -############## -# Submodules # -############## - -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 +if fdt_required.length() > 0 + error('fdt disabled but required by targets ' + ', '.join(fdt_required)) endif +############### +# Subprojects # +############### + libvfio_user_dep = not_found if have_system and vfio_user_server_allowed - have_internal = fs.exists(meson.current_source_dir() / 'subprojects/libvfio-user/meson.build') - - if not have_internal - error('libvfio-user source not found - please pull git submodule') - endif - - libvfio_user_proj = subproject('libvfio-user') - - libvfio_user_lib = libvfio_user_proj.get_variable('libvfio_user_dep') - - libvfio_user_dep = declare_dependency(dependencies: [libvfio_user_lib]) + 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') - 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 fdt.found() and cc.links(''' - #include - #include - int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', - dependencies: fdt) - 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 = 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) - 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)) +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 tomlplusplus_proj = subproject('tomlplusplus', default_options: ['default_library=static']) tomlplusplus = tomlplusplus_proj.get_variable('tomlplusplus_dep') @@ -2907,6 +3545,35 @@ xxhash = xxhash_proj.get_variable('xxhash_dep') genh += configure_file(output: 'config-host.h', configuration: config_host_data) +if have_rust + rustc_args = run_command( + find_program('scripts/rust/rustc_args.py'), + '--config-headers', meson.project_build_root() / 'config-host.h', + capture : true, + check: true).stdout().strip().split() + + # Prohibit code that is forbidden in Rust 2024 + rustc_args += ['-D', 'unsafe_op_in_unsafe_fn'] + + # Occasionally, we may need to silence warnings and clippy lints that + # were only introduced in newer Rust compiler versions. Do not croak + # in that case; a CI job with rust_strict_lints == true ensures that + # we do not have misspelled allow() attributes. + if not get_option('strict_rust_lints') + rustc_args += ['-A', 'unknown_lints'] + endif + + # Apart from procedural macros, our Rust executables will often link + # with C code, so include all the libraries that C code needs. This + # is safe; https://github.com/rust-lang/rust/pull/54675 says that + # passing -nodefaultlibs to the linker "was more ideological to + # start with than anything". + add_project_arguments(rustc_args + ['-C', 'default-linker-libraries'], + native: false, language: 'rust') + + add_project_arguments(rustc_args, native: true, language: 'rust') +endif + hxtool = find_program('scripts/hxtool') shaderinclude = find_program('scripts/shaderinclude.py') qapi_gen = find_program('scripts/qapi-gen.py') @@ -2918,12 +3585,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' ] @@ -2951,8 +3618,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' ) qemu_version_cmd = [find_program('scripts/qemu-version.sh'), @@ -2965,16 +3630,11 @@ qemu_version = custom_target('qemu-version.h', build_by_default: true, build_always_stale: true) -genconfig_cmd = [ - python, files('genconfig/gen_config.py'), - meson.current_source_dir() / 'config_spec.yml', 'xemu-config.h' -] - xemu_config = custom_target('xemu-config.h', output: 'xemu-config.h', input: [ files('config_spec.yml') ], - command: genconfig_cmd, - depend_files: files('config_spec.yml')) + command : [python, genconfig_proj.get_variable('gen_config_script_path'), '@INPUT@', '@OUTPUT@'], + ) genh += qemu_version genh += xemu_version @@ -2996,44 +3656,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 # ############### @@ -3058,6 +3684,7 @@ if have_block trace_events_subdirs += [ 'authz', 'block', + 'chardev', 'io', 'nbd', 'scsi', @@ -3069,7 +3696,6 @@ if have_system 'audio', 'backends', 'backends/tpm', - 'chardev', 'ebpf', 'hw/9pfs', 'hw/acpi', @@ -3078,14 +3704,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', @@ -3102,18 +3729,19 @@ if have_system 'hw/pci', 'hw/pci-host', 'hw/ppc', - 'hw/rdma', - 'hw/rdma/vmw', 'hw/rtc', + 'hw/riscv', 'hw/s390x', 'hw/scsi', 'hw/sd', + 'hw/sensor', 'hw/sh4', 'hw/sparc', 'hw/sparc64', 'hw/ssi', 'hw/timer', 'hw/tpm', + 'hw/ufs', 'hw/usb', 'hw/vfio', 'hw/virtio', @@ -3122,7 +3750,7 @@ if have_system 'hw/gpio', 'migration', 'net', - 'softmmu', + 'system', 'ui', 'hw/remote', 'hw/xbox/nv2a', @@ -3139,8 +3767,8 @@ if have_system or have_user 'target/hppa', 'target/i386', 'target/i386/kvm', + 'target/loongarch', 'target/mips/tcg', - 'target/nios2', 'target/ppc', 'target/riscv', 'target/s390x', @@ -3149,17 +3777,39 @@ 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 # +################### -libvduse = not_found -if have_libvduse - libvduse_proj = subproject('libvduse') - libvduse = libvduse_proj.get_variable('libvduse_dep') -endif +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() +rust_devices_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/. @@ -3172,36 +3822,43 @@ subdir('qom') subdir('authz') subdir('crypto') subdir('ui') -subdir('hw') subdir('gdbstub') subdir('data') subdir('winpcap-loader') +if have_system + subdir('hw') +else + subdir('hw/core') +endif 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') + modulecommon = declare_dependency(objects: libmodulecommon.extract_all_objects(recursive: false), 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') -qom = declare_dependency(link_whole: libqom) + build_by_default: false) +qom = declare_dependency(objects: libqom.extract_all_objects(recursive: false), + dependencies: qom_ss.dependencies()) 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 = declare_dependency(link_whole: event_loop_base, +event_loop_base = static_library('event-loop-base', + sources: event_loop_base + genh, + build_by_default: false) +event_loop_base = declare_dependency(objects: event_loop_base.extract_all_objects(recursive: false), 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, xxhash]) + dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, xxhash]) qemuutil = declare_dependency(link_with: libqemuutil, sources: genh + version_res, dependencies: [event_loop_base]) @@ -3240,25 +3897,26 @@ if have_block 'blockdev-nbd.c', 'iothread.c', 'job-qmp.c', - ), gnutls) + )) # 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 specific_ss.add(files('xemu-xbe.c', 'xemu-version.c')) -common_ss.add(files('cpus-common.c')) - common_ss.add(tomlplusplus) common_ss.add(genconfig) -subdir('softmmu') +common_ss.add(files('cpu-common.c')) +specific_ss.add(files('cpu-target.c')) -common_ss.add(capstone) -specific_ss.add(files('cpu.c', 'disas.c'), capstone) +subdir('system') # Work around a gcc bug/misfeature wherein constant propagation looks # through an alias: @@ -3278,7 +3936,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-target.c', 'page-vary-target.c')) subdir('backends') subdir('disas') @@ -3287,12 +3945,17 @@ subdir('monitor') subdir('net') subdir('replay') subdir('semihosting') +subdir('stats') subdir('tcg') subdir('fpu') subdir('accel') subdir('plugins') subdir('ebpf') +if 'CONFIG_TCG' in config_all_accel + subdir('contrib/plugins') +endif + common_user_inc = [] subdir('common-user') @@ -3310,31 +3973,39 @@ 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 = [] +emulator_modules = [] 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.add(modulecommon) + 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) + dependencies: module_ss.dependencies(), pic: true) if d == 'block' block_mods += sl else - softmmu_mods += sl + system_mods += sl endif + emulator_modules += shared_module(sl.name(), + name_prefix: '', + objects: sl.extract_all_objects(recursive: false), + dependencies: module_ss.dependencies(), + install: true, + install_dir: qemu_moddir) if module_ss.sources() != [] # FIXME: Should use sl.extract_all_objects(recursive: true) as # input. Sources can be used multiple times but objects are @@ -3351,7 +4022,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 @@ -3359,13 +4030,13 @@ endforeach foreach d, list : target_modules foreach m, module_ss : list - if enable_modules and targetos != 'windows' + if enable_modules + module_ss.add(modulecommon) 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', + c_args = ['-DCOMPILING_PER_TARGET', '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), '-DCONFIG_DEVICES="@0@-config-devices.h"'.format(target)] target_module_ss = module_ss.apply(config_target, strict: false) @@ -3373,11 +4044,17 @@ foreach d, list : target_modules module_name = d + '-' + m + '-' + config_target['TARGET_NAME'] sl = static_library(module_name, [genh, target_module_ss.sources()], - dependencies: [modulecommon, target_module_ss.dependencies()], + dependencies: target_module_ss.dependencies(), include_directories: target_inc, c_args: c_args, pic: true) - softmmu_mods += sl + system_mods += sl + emulator_modules += shared_module(sl.name(), + name_prefix: '', + objects: sl.extract_all_objects(recursive: false), + dependencies: target_module_ss.dependencies(), + install: true, + install_dir: qemu_moddir) # FIXME: Should use sl.extract_all_objects(recursive: true) too. modinfo_files += custom_target(module_name + '.modinfo', output: module_name + '.modinfo', @@ -3411,6 +4088,10 @@ if enable_modules hw_arch[arch].add(modinfo_dep) endif endforeach + + if emulator_modules.length() > 0 + alias_target('modules', emulator_modules) + endif endif nm = find_program('nm') @@ -3420,124 +4101,179 @@ 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', build_by_default: false) -authz = declare_dependency(link_whole: libauthz, - dependencies: qom) +authz = declare_dependency(objects: libauthz.extract_all_objects(recursive: false), + dependencies: [authz_ss.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', build_by_default: false) -crypto = declare_dependency(link_whole: libcrypto, - dependencies: [authz, qom]) +crypto = declare_dependency(objects: libcrypto.extract_all_objects(recursive: false), + dependencies: [crypto_ss.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, - name_suffix: 'fa', build_by_default: false) -io = declare_dependency(link_whole: libio, dependencies: [crypto, qom]) +io = declare_dependency(objects: libio.extract_all_objects(recursive: false), + dependencies: [io_ss.dependencies(), crypto, qom]) libmigration = static_library('migration', sources: migration_files + genh, - name_suffix: 'fa', build_by_default: false) -migration = declare_dependency(link_with: libmigration, - dependencies: [zlib, qom, io]) -softmmu_ss.add(migration) +migration = declare_dependency(objects: libmigration.extract_all_objects(recursive: false), + dependencies: [qom, io]) +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, - name_suffix: 'fa', build_by_default: false) -block = declare_dependency(link_whole: [libblock], - link_args: '@block.syms', - dependencies: [crypto, io]) +block = declare_dependency(objects: libblock.extract_all_objects(recursive: false), + dependencies: [block_ss.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', build_by_default: false) -blockdev = declare_dependency(link_whole: [libblockdev], - dependencies: [block, event_loop_base]) +blockdev = declare_dependency(objects: libblockdev.extract_all_objects(recursive: false), + dependencies: [blockdev_ss.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', build_by_default: false) -qmp = declare_dependency(link_whole: [libqmp]) +qmp = declare_dependency(objects: libqmp.extract_all_objects(recursive: false), + dependencies: qmp_ss.dependencies()) libchardev = static_library('chardev', chardev_ss.sources() + genh, - name_suffix: 'fa', dependencies: chardev_ss.dependencies(), build_by_default: false) -chardev = declare_dependency(link_whole: libchardev) +chardev = declare_dependency(objects: libchardev.extract_all_objects(recursive: false), + dependencies: chardev_ss.dependencies()) -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) -hwcore = declare_dependency(link_whole: libhwcore) +hwcore = declare_dependency(objects: libhwcore.extract_all_objects(recursive: false)) common_ss.add(hwcore) ########### # Targets # ########### -emulator_modules = [] -foreach m : block_mods + softmmu_mods - emulator_modules += shared_module(m.name(), - build_by_default: true, - name_prefix: '', - link_whole: m, - install: true, - install_dir: qemu_moddir) -endforeach -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(), - name_suffix: 'fa') + dependencies: common_ss.all_dependencies()) -feature_to_c = find_program('scripts/feature_to_c.sh') +if have_rust + # We would like to use --generate-cstr, but it is only available + # starting with bindgen 0.66.0. The oldest supported versions + # is 0.60.x (Debian 12 has 0.60.1) which introduces --allowlist-file. + bindgen_args = [ + '--disable-header-comment', + '--raw-line', '// @generated', + '--ctypes-prefix', 'std::os::raw', + '--generate-block', + '--impl-debug', + '--no-doc-comments', + '--with-derive-default', + '--no-layout-tests', + '--no-prepend-enum-name', + '--allowlist-file', meson.project_source_root() + '/include/.*', + '--allowlist-file', meson.project_source_root() + '/.*', + '--allowlist-file', meson.project_build_root() + '/.*' + ] + if not rustfmt.found() + if bindgen.version().version_compare('<0.65.0') + bindgen_args += ['--no-rustfmt-bindings'] + else + bindgen_args += ['--formatter', 'none'] + endif + endif + if bindgen.version().version_compare('<0.61.0') + # default in 0.61+ + bindgen_args += ['--size_t-is-usize'] + else + bindgen_args += ['--merge-extern-blocks'] + endif + c_enums = [ + 'DeviceCategory', + 'GpioPolarity', + 'MachineInitPhase', + 'MemoryDeviceInfoKind', + 'MigrationPolicy', + 'MigrationPriority', + 'QEMUChrEvent', + 'QEMUClockType', + 'device_endian', + 'module_init_type', + ] + foreach enum : c_enums + bindgen_args += ['--rustified-enum', enum] + endforeach + c_bitfields = [ + 'ClockEvent', + 'VMStateFlags', + ] + foreach enum : c_bitfields + bindgen_args += ['--bitfield-enum', enum] + endforeach -if targetos == 'darwin' + # TODO: Remove this comment when the clang/libclang mismatch issue is solved. + # + # Rust bindings generation with `bindgen` might fail in some cases where the + # detected `libclang` does not match the expected `clang` version/target. In + # this case you must pass the path to `clang` and `libclang` to your build + # command invocation using the environment variables CLANG_PATH and + # LIBCLANG_PATH + bindings_rs = rust.bindgen( + input: 'rust/wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.rs', + include_directories: include_directories('.', 'include'), + bindgen_version: ['>=0.60.0'], + args: bindgen_args, + ) + subdir('rust') +endif + + +feature_to_c = find_program('scripts/feature_to_c.py') +rust_root_crate = find_program('scripts/rust/rust_root_crate.sh') + +if host_os == 'darwin' entitlement = find_program('scripts/entitlement.sh') endif +traceable = [] emulators = {} foreach target : target_dirs config_target = config_target_mak[target] @@ -3545,26 +4281,27 @@ foreach target : target_dirs target_base_arch = config_target['TARGET_BASE_ARCH'] arch_srcs = [config_target_h[target]] arch_deps = [] - c_args = ['-DNEED_CPU_H', + c_args = ['-DCOMPILING_PER_TARGET', '-DCONFIG_TARGET="@0@-config-target.h"'.format(target), '-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') 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'] @@ -3582,7 +4319,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') @@ -3617,34 +4354,63 @@ foreach target : target_dirs target_common = common_ss.apply(config_target, strict: false) objects = common_all.extract_objects(target_common.sources()) - deps = target_common.dependencies() + arch_deps += target_common.dependencies() target_specific = specific_ss.apply(config_target, strict: false) arch_srcs += target_specific.sources() arch_deps += target_specific.dependencies() + if have_rust and target_type == 'system' + target_rust = rust_devices_ss.apply(config_target, strict: false) + crates = [] + foreach dep : target_rust.dependencies() + crates += dep.get_variable('crate') + endforeach + if crates.length() > 0 + rlib_rs = custom_target('rust_' + target.underscorify() + '.rs', + output: 'rust_' + target.underscorify() + '.rs', + command: [rust_root_crate, crates], + capture: true, + build_by_default: true, + build_always_stale: true) + rlib = static_library('rust_' + target.underscorify(), + rlib_rs, + dependencies: target_rust.dependencies(), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'c') + arch_deps += declare_dependency(link_whole: [rlib]) + endif + endif + + # allow using headers from the dependencies but do not include the sources, + # because this emulator only needs those in "objects". For external + # dependencies, the full dependency is included below in the executable. + lib_deps = [] + foreach dep : arch_deps + lib_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + lib = static_library('qemu-' + target, sources: arch_srcs + genh, - dependencies: arch_deps, + dependencies: lib_deps, objects: objects, include_directories: target_inc, c_args: c_args, - build_by_default: false, - name_suffix: 'fa') + build_by_default: false) if target.endswith('-softmmu') execs = [{ 'name': 'qemu-system-' + target_name, 'win_subsystem': 'console', - 'sources': files('softmmu/main.c'), - 'dependencies': [] + 'sources': files('system/main.c'), + 'dependencies': [sdl] }] - 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'), - 'dependencies': [] + 'sources': files('system/main.c'), + 'dependencies': [sdl] }] endif if get_option('fuzzing') @@ -3666,21 +4432,20 @@ foreach target : target_dirs endif foreach exe: execs exe_name = exe['name'] - if targetos == 'darwin' + if host_os == 'darwin' exe_name += '-unsigned' endif emulator = executable(exe_name, exe['sources'], install: true, c_args: c_args, - dependencies: arch_deps + deps + exe['dependencies'], + dependencies: arch_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 = [ @@ -3706,36 +4471,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') @@ -3750,15 +4502,25 @@ endif if have_tools qemu_img = executable('qemu-img', [files('qemu-img.c'), hxdep], + link_args: '@block.syms', link_depends: block_syms, dependencies: [authz, block, crypto, io, qom, qemuutil], install: true) qemu_io = executable('qemu-io', files('qemu-io.c'), + link_args: '@block.syms', link_depends: block_syms, dependencies: [block, qemuutil], install: true) qemu_nbd = executable('qemu-nbd', files('qemu-nbd.c'), - dependencies: [blockdev, qemuutil, gnutls, selinux], + link_args: '@block.syms', link_depends: block_syms, + dependencies: [blockdev, qemuutil, selinux], install: true) subdir('storage-daemon') - subdir('contrib/rdmacm-mux') + + foreach exe: [ 'qemu-img', 'qemu-io', 'qemu-nbd', 'qemu-storage-daemon'] + traceable += [{ + 'exe': exe, + 'probe-prefix': 'qemu.' + exe.substring(5).replace('-', '_') + }] + endforeach + subdir('contrib/elf2dmp') executable('qemu-edid', files('qemu-edid.c', 'hw/display/edid-generate.c'), @@ -3772,7 +4534,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, @@ -3782,6 +4544,13 @@ if have_tools dependencies: [authz, crypto, io, qom, qemuutil, libcap_ng, mpathpersist], install: true) + + if cpu in ['x86', 'x86_64'] + executable('qemu-vmsr-helper', files('tools/i386/qemu-vmsr-helper.c'), + dependencies: [authz, crypto, io, qom, qemuutil, + libcap_ng, mpathpersist], + install: true) + endif endif if have_ivshmem @@ -3790,6 +4559,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') @@ -3805,7 +4600,7 @@ if host_machine.system() == 'windows' '@OUTPUT@', get_option('prefix'), meson.current_source_dir(), - config_host['GLIB_BINDIR'], + glib_pc.get_variable('bindir'), host_machine.cpu(), '--', '-DDISPLAYVERSION=' + meson.project_version(), @@ -3829,11 +4624,17 @@ 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} -pathsep = targetos == 'windows' ? ';' : ':' +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')} @@ -3841,30 +4642,30 @@ 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.found() and have_system @@ -3879,8 +4680,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')} @@ -3893,6 +4694,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} @@ -3908,56 +4710,67 @@ 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'] : []))} +summary_info += {'Rust support': have_rust} +if have_rust + summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']} + summary_info += {'rustc': ' '.join(rustc.cmd_array())} + summary_info += {'rustc version': rustc.version()} + summary_info += {'bindgen': bindgen.full_path()} + summary_info += {'bindgen version': bindgen.version()} endif -if targetos == 'darwin' - summary_info += {'OBJCFLAGS': ' '.join(get_option('objc_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 -link_args = get_option(link_language + '_link_args') +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 +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 += {'avx512f optimization': config_host_data.get('CONFIG_AVX512F_OPT')} -summary_info += {'gprof enabled': get_option('gprof')} +summary_info += {'avx512bw optimization': config_host_data.get('CONFIG_AVX512BW_OPT')} 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 @@ -3980,25 +4793,28 @@ 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')} + if have_linux_user or have_bsd_user + summary_info += {'syscall buffer debugging support': get_option('debug_remap')} + endif endif summary_info += {'target list': ' '.join(target_dirs)} if have_system @@ -4010,21 +4826,22 @@ 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 += {'Live block migration': config_host_data.get('CONFIG_LIVE_BLOCK_MIGRATION')} + summary_info += {'VirtFS (9P) support': have_virtfs} summary_info += {'replication support': config_host_data.get('CONFIG_REPLICATION')} summary_info += {'bochs support': get_option('bochs').allowed()} summary_info += {'cloop support': get_option('cloop').allowed()} 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()} @@ -4045,66 +4862,89 @@ summary_info += {'nettle': nettle} if nettle.found() summary_info += {' XTS': xts != 'private'} endif +summary_info += {'SM4 ALG support': crypto_sm4} +summary_info += {'SM3 ALG support': crypto_sm3} 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} -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 += {'blkio support': blkio} -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 += {'OSS support': oss} - summary_info += {'sndio support': sndio} -elif targetos == 'darwin' - summary_info += {'CoreAudio support': coreaudio} -elif targetos == 'windows' - summary_info += {'DirectSound support': dsound} -endif -if targetos == 'linux' - summary_info += {'ALSA support': alsa} - summary_info += {'PulseAudio support': pulse} -endif -summary_info += {'JACK support': jack} -summary_info += {'brlapi support': brlapi} -summary_info += {'vde support': vde} -summary_info += {'netmap support': have_netmap} -summary_info += {'l2tpv3 support': have_l2tpv3} -summary_info += {'Linux AIO support': libaio} -summary_info += {'Linux io_uring support': linux_io_uring} -summary_info += {'ATTR/XATTR support': libattr} -summary_info += {'RDMA support': rdma} -summary_info += {'PVRDMA support': have_pvrdma} -summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt} -summary_info += {'libcap-ng support': libcap_ng} -summary_info += {'bpf support': libbpf} 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} + summary_info += {'sndio support': sndio} +elif host_os == 'darwin' + summary_info += {'CoreAudio support': coreaudio} +elif host_os == 'windows' + summary_info += {'DirectSound support': dsound} +endif +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(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} +summary_info += {'RDMA support': rdma} +summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt} +summary_info += {'libcap-ng support': libcap_ng} +summary_info += {'bpf support': libbpf} summary_info += {'rbd support': rbd} summary_info += {'smartcard support': cacard} summary_info += {'U2F support': u2f} @@ -4115,13 +4955,14 @@ summary_info += {'Vulkan support': vulkan} 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} @@ -4129,39 +4970,82 @@ summary_info += {'snappy support': snappy} summary_info += {'bzip2 support': libbzip2} summary_info += {'lzfse support': liblzfse} summary_info += {'zstd support': zstd} +summary_info += {'Query Processing Library support': qpl} +summary_info += {'UADK Library support': uadk} +summary_info += {'qatzip support': qatzip} summary_info += {'NUMA host support': numa} summary_info += {'capstone': capstone} summary_info += {'libpmem support': libpmem} summary_info += {'libdaxctl support': libdaxctl} +summary_info += {'libcbor support': libcbor} summary_info += {'libudev': libudev} # Dummy dependency, keep .found() summary_info += {'FUSE lseek': fuse_lseek.found()} summary_info += {'selinux': selinux} summary_info += {'vtune': have_vtune ? vtune_path : false} +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 +elif host_arch == 'mips' + message() + warning('DEPRECATED HOST CPU') + message() + message('Support for CPU host architecture ' + cpu + ' is going to be') + message('dropped as soon as the QEMU project stops supporting Debian 12') + message('("Bookworm"). Going forward, the QEMU project will not guarantee') + message('that QEMU will compile or work on this host CPU.') 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 84427cc95b..d9b2023c80 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,8 +12,6 @@ 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', 'sndio'], + 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)') @@ -29,12 +27,17 @@ option('block_drv_ro_whitelist', type : 'string', value : '', description: 'set block driver read-only whitelist (by default affects only QEMU, not tools like qemu-img)') option('interp_prefix', type : 'string', value : '/usr/gnemul/qemu-%M', description: 'where to find shared libraries etc., use %M for cpu name') +option('rtsig_map', type : 'string', value : 'NULL', + description: 'default value of QEMU_RTSIG_MAP') 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('vtune', type : 'string', value : '', description: 'Path to VTune directory for profiling') +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 @@ -46,6 +49,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, @@ -68,8 +73,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', @@ -82,8 +85,24 @@ 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('debug_remap', type: 'boolean', value: false, + description: 'syscall buffer debugging support') option('tcg_interpreter', type: 'boolean', value: false, description: 'TCG with bytecode interpreter (slow)') +option('safe_stack', type: 'boolean', value: false, + description: 'SafeStack Stack Smash Protection (requires clang/llvm and coroutine backend ucontext)') +option('asan', type: 'boolean', value: false, + description: 'enable address sanitizer') +option('ubsan', type: 'boolean', value: false, + description: 'enable undefined behaviour sanitizer') +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, @@ -92,6 +111,8 @@ option('multiprocess', type: 'feature', value: 'auto', description: 'Out of process device emulation support') option('renderdoc', type: 'feature', value: 'auto', description: 'RenderDoc frame capture 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', @@ -106,11 +127,15 @@ option('membarrier', type: 'feature', value: 'disabled', 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', @@ -133,6 +158,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', @@ -145,6 +174,8 @@ option('iconv', type : 'feature', value : 'auto', description: 'Font glyph conversion support') option('curses', type : 'feature', value : 'auto', description: 'curses UI') +option('libcbor', type : 'feature', value : 'auto', + description: 'libcbor support') option('gnutls', type : 'feature', value : 'auto', description: 'GNUTLS cryptography support') option('nettle', type : 'feature', value : 'auto', @@ -177,8 +208,6 @@ option('opengl', type : 'feature', value : 'enabled', description: 'OpenGL support') option('rdma', type : 'feature', value : 'auto', description: 'Enable RDMA-based migration') -option('pvrdma', type : 'feature', value : 'auto', - description: 'Enable PVRDMA support') # option('gtk', type : 'feature', value : 'auto', # description: 'GTK+ user interface') option('sdl', type : 'feature', value : 'enabled', @@ -205,6 +234,8 @@ 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: 'enabled', description: 'libslirp user mode network backend support') option('vde', type : 'feature', value : 'auto', @@ -213,6 +244,8 @@ 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', @@ -234,6 +267,12 @@ option('xkbcommon', type : 'feature', value : 'auto', description: 'xkbcommon support') option('zstd', type : 'feature', value : 'auto', description: 'zstd compression support') +option('qpl', type : 'feature', value : 'auto', + description: 'Query Processing Library support') +option('uadk', type : 'feature', value : 'auto', + description: 'UADK Library support') +option('qatzip', type: 'feature', value: 'auto', + description: 'QATzip compression support') option('fuse', type: 'feature', value: 'auto', description: 'FUSE block device export') option('fuse_lseek', type : 'feature', value : 'auto', @@ -255,6 +294,8 @@ 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') @@ -272,8 +313,6 @@ 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('libvduse', type: 'feature', value: 'auto', description: 'build VDUSE Library') option('vduse_blk_export', type: 'feature', value: 'auto', @@ -287,10 +326,10 @@ option('fdt', type: 'combo', value: 'auto', option('selinux', type: 'feature', value: 'auto', description: 'SELinux support in qemu-nbd') -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', @@ -301,6 +340,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', @@ -313,18 +358,31 @@ 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('renderdoc', type: 'feature', value: 'auto', - description: 'improved RenderDoc frame capture support') \ No newline at end of file +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') + +option('x86_version', type : 'combo', choices : ['0', '1', '2', '3', '4'], value: '1', + description: 'tweak required x86_64 architecture version beyond compiler default') + +option('rust', type: 'feature', value: 'disabled', + description: 'Rust support') +option('strict_rust_lints', type: 'boolean', value: false, + description: 'Enable stricter set of Rust warnings') diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 9aba7d9c22..a7d55048c2 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,15 +479,15 @@ 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) + const char *bs_name, GHashTable *alias_map, + Error **errp) { BdrvDirtyBitmap *bitmap; SaveBitmapState *dbms; GHashTable *bitmap_aliases; const char *node_alias, *bitmap_name, *bitmap_alias; - Error *local_err = NULL; /* When an alias map is given, @bs_name must be @bs's node name */ assert(!alias_map || !strcmp(bs_name, bdrv_get_node_name(bs))); @@ -502,8 +504,8 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, bitmap_name = bdrv_dirty_bitmap_name(bitmap); if (!bs_name || strcmp(bs_name, "") == 0) { - error_report("Bitmap '%s' in unnamed node can't be migrated", - bitmap_name); + error_setg(errp, "Bitmap '%s' in unnamed node can't be migrated", + bitmap_name); return -1; } @@ -523,9 +525,9 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, } if (node_alias[0] == '#') { - error_report("Bitmap '%s' in a node with auto-generated " - "name '%s' can't be migrated", - bitmap_name, node_alias); + error_setg(errp, "Bitmap '%s' in a node with auto-generated " + "name '%s' can't be migrated", + bitmap_name, node_alias); return -1; } @@ -536,8 +538,7 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, continue; } - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, &local_err)) { - error_report_err(local_err); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { return -1; } @@ -551,14 +552,14 @@ 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 { if (strlen(bitmap_name) > UINT8_MAX) { - error_report("Cannot migrate bitmap '%s' on node '%s': " - "Name is longer than %u bytes", - bitmap_name, bs_name, UINT8_MAX); + error_setg(errp, "Cannot migrate bitmap '%s' on node '%s': " + "Name is longer than %u bytes", + bitmap_name, bs_name, UINT8_MAX); return -1; } bitmap_alias = bitmap_name; @@ -596,18 +597,21 @@ static int add_bitmaps_to_list(DBMSaveState *s, BlockDriverState *bs, return 0; } -/* Called with iothread lock taken. */ -static int init_dirty_bitmap_migration(DBMSaveState *s) +/* Called with the BQL taken. */ +static int init_dirty_bitmap_migration(DBMSaveState *s, Error **errp) { 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); } @@ -638,7 +642,7 @@ static int init_dirty_bitmap_migration(DBMSaveState *s) } if (bs && bs->drv && !bs->drv->is_filter) { - if (add_bitmaps_to_list(s, bs, name, NULL)) { + if (add_bitmaps_to_list(s, bs, name, NULL, errp)) { goto fail; } g_hash_table_add(handled_by_blk, bs); @@ -651,7 +655,8 @@ static int init_dirty_bitmap_migration(DBMSaveState *s) continue; } - if (add_bitmaps_to_list(s, bs, bdrv_get_node_name(bs), alias_map)) { + if (add_bitmaps_to_list(s, bs, bdrv_get_node_name(bs), alias_map, + errp)) { goto fail; } } @@ -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 { @@ -1211,14 +1213,12 @@ fail: return ret; } -static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque) +static int dirty_bitmap_save_setup(QEMUFile *f, void *opaque, Error **errp) { DBMSaveState *s = &((DBMState *)opaque)->save; SaveBitmapState *dbms = NULL; - qemu_mutex_lock_iothread(); - if (init_dirty_bitmap_migration(s) < 0) { - qemu_mutex_unlock_iothread(); + if (init_dirty_bitmap_migration(s, errp) < 0) { 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 deleted file mode 100644 index 4347da1526..0000000000 --- a/migration/block.c +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * QEMU live block migration - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Liran Schour - * - * 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 "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" -#include "qemu/cutils.h" -#include "qemu/queue.h" -#include "block.h" -#include "migration/misc.h" -#include "migration.h" -#include "migration/register.h" -#include "qemu-file.h" -#include "migration/vmstate.h" -#include "sysemu/block-backend.h" -#include "trace.h" - -#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 -#define BLK_MIG_FLAG_EOS 0x02 -#define BLK_MIG_FLAG_PROGRESS 0x04 -#define BLK_MIG_FLAG_ZERO_BLOCK 0x08 - -#define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE) - -#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; - char *blk_name; - int shared_base; - int64_t total_sectors; - QSIMPLEQ_ENTRY(BlkMigDevState) entry; - Error *blocker; - - /* Only used by migration thread. Does not need a lock. */ - int bulk_completed; - int64_t cur_sector; - int64_t cur_dirty; - - /* Data in the aio_bitmap is protected by block migration lock. - * Allocation and free happen during setup and cleanup respectively. - */ - unsigned long *aio_bitmap; - - /* Protected by block migration lock. */ - int64_t completed_sectors; - - /* During migration this is protected by iothread lock / AioContext. - * Allocation and free happen during setup and cleanup respectively. - */ - BdrvDirtyBitmap *dirty_bitmap; -} BlkMigDevState; - -typedef struct BlkMigBlock { - /* Only used by migration thread. */ - uint8_t *buf; - BlkMigDevState *bmds; - int64_t sector; - int nr_sectors; - QEMUIOVector qiov; - BlockAIOCB *aiocb; - - /* Protected by block migration lock. */ - int ret; - QSIMPLEQ_ENTRY(BlkMigBlock) entry; -} BlkMigBlock; - -typedef struct BlkMigState { - QSIMPLEQ_HEAD(, BlkMigDevState) bmds_list; - int64_t total_sector_sum; - bool zero_blocks; - - /* Protected by lock. */ - QSIMPLEQ_HEAD(, BlkMigBlock) blk_list; - int submitted; - int read_done; - - /* Only used by migration thread. Does not need a lock. */ - int transferred; - int prev_progress; - int bulk_completed; - - /* Lock must be taken _inside_ the iothread lock and any AioContexts. */ - QemuMutex lock; -} BlkMigState; - -static BlkMigState block_mig_state; - -static void blk_mig_lock(void) -{ - qemu_mutex_lock(&block_mig_state.lock); -} - -static void blk_mig_unlock(void) -{ - qemu_mutex_unlock(&block_mig_state.lock); -} - -/* Must run outside of the iothread lock during the bulk phase, - * or the VM will stall. - */ - -static void blk_send(QEMUFile *f, BlkMigBlock * blk) -{ - int len; - uint64_t flags = BLK_MIG_FLAG_DEVICE_BLOCK; - - if (block_mig_state.zero_blocks && - buffer_is_zero(blk->buf, BLK_MIG_BLOCK_SIZE)) { - flags |= BLK_MIG_FLAG_ZERO_BLOCK; - } - - /* sector number and flags */ - qemu_put_be64(f, (blk->sector << BDRV_SECTOR_BITS) - | flags); - - /* device name */ - len = strlen(blk->bmds->blk_name); - qemu_put_byte(f, len); - qemu_put_buffer(f, (uint8_t *) blk->bmds->blk_name, len); - - /* if a block is zero we need to flush here since the network - * bandwidth is now a lot higher than the storage device bandwidth. - * thus if we queue zero blocks we slow down the migration */ - if (flags & BLK_MIG_FLAG_ZERO_BLOCK) { - qemu_fflush(f); - return; - } - - qemu_put_buffer(f, blk->buf, BLK_MIG_BLOCK_SIZE); -} - -int blk_mig_active(void) -{ - return !QSIMPLEQ_EMPTY(&block_mig_state.bmds_list); -} - -int blk_mig_bulk_active(void) -{ - return blk_mig_active() && !block_mig_state.bulk_completed; -} - -uint64_t blk_mig_bytes_transferred(void) -{ - BlkMigDevState *bmds; - uint64_t sum = 0; - - blk_mig_lock(); - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - sum += bmds->completed_sectors; - } - blk_mig_unlock(); - return sum << BDRV_SECTOR_BITS; -} - -uint64_t blk_mig_bytes_remaining(void) -{ - return blk_mig_bytes_total() - blk_mig_bytes_transferred(); -} - -uint64_t blk_mig_bytes_total(void) -{ - BlkMigDevState *bmds; - uint64_t sum = 0; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - sum += bmds->total_sectors; - } - return sum << BDRV_SECTOR_BITS; -} - - -/* Called with migration lock held. */ - -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)) { - return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] & - (1UL << (chunk % (sizeof(unsigned long) * 8)))); - } else { - return 0; - } -} - -/* Called with migration lock held. */ - -static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, - int nb_sectors, int set) -{ - int64_t start, end; - unsigned long val, idx, bit; - - start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; - end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; - - for (; start <= end; start++) { - idx = start / (sizeof(unsigned long) * 8); - bit = start % (sizeof(unsigned long) * 8); - val = bmds->aio_bitmap[idx]; - if (set) { - val |= 1UL << bit; - } else { - val &= ~(1UL << bit); - } - bmds->aio_bitmap[idx] = val; - } -} - -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 /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; - - bmds->aio_bitmap = g_malloc0(bitmap_size); -} - -/* Never hold migration lock when yielding to the main loop! */ - -static void blk_mig_read_cb(void *opaque, int ret) -{ - BlkMigBlock *blk = opaque; - - blk_mig_lock(); - blk->ret = ret; - - QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); - bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); - - block_mig_state.submitted--; - block_mig_state.read_done++; - assert(block_mig_state.submitted >= 0); - blk_mig_unlock(); -} - -/* Called with no lock taken. */ - -static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) -{ - int64_t total_sectors = bmds->total_sectors; - int64_t cur_sector = bmds->cur_sector; - BlockBackend *bb = bmds->blk; - BlkMigBlock *blk; - int nr_sectors; - int64_t count; - - if (bmds->shared_base) { - qemu_mutex_lock_iothread(); - aio_context_acquire(blk_get_aio_context(bb)); - /* Skip unallocated sectors; intentionally treats failure or - * partial sector as an allocated sector */ - while (cur_sector < total_sectors && - !bdrv_is_allocated(blk_bs(bb), cur_sector * BDRV_SECTOR_SIZE, - MAX_IS_ALLOCATED_SEARCH, &count)) { - if (count < BDRV_SECTOR_SIZE) { - break; - } - cur_sector += count >> BDRV_SECTOR_BITS; - } - aio_context_release(blk_get_aio_context(bb)); - qemu_mutex_unlock_iothread(); - } - - if (cur_sector >= total_sectors) { - bmds->cur_sector = bmds->completed_sectors = total_sectors; - return 1; - } - - bmds->completed_sectors = cur_sector; - - cur_sector &= ~((int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK - 1); - - /* we are going to transfer a full block even if it is not allocated */ - nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; - - if (total_sectors - cur_sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { - nr_sectors = total_sectors - cur_sector; - } - - blk = g_new(BlkMigBlock, 1); - blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE); - blk->bmds = bmds; - blk->sector = cur_sector; - blk->nr_sectors = nr_sectors; - - qemu_iovec_init_buf(&blk->qiov, blk->buf, nr_sectors * BDRV_SECTOR_SIZE); - - blk_mig_lock(); - 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. - */ - qemu_mutex_lock_iothread(); - aio_context_acquire(blk_get_aio_context(bmds->blk)); - 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(); - - bmds->cur_sector = cur_sector + nr_sectors; - return (bmds->cur_sector >= total_sectors); -} - -/* Called with iothread lock taken. */ - -static int set_dirty_tracking(void) -{ - BlkMigDevState *bmds; - int ret; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bmds->dirty_bitmap = bdrv_create_dirty_bitmap(blk_bs(bmds->blk), - BLK_MIG_BLOCK_SIZE, - NULL, NULL); - if (!bmds->dirty_bitmap) { - ret = -errno; - goto fail; - } - } - return 0; - -fail: - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (bmds->dirty_bitmap) { - bdrv_release_dirty_bitmap(bmds->dirty_bitmap); - } - } - return ret; -} - -/* Called with iothread lock 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); - } -} - -static int init_blk_migration(QEMUFile *f) -{ - BlockDriverState *bs; - BlkMigDevState *bmds; - int64_t sectors; - BdrvNextIterator it; - int i, num_bs = 0; - struct { - BlkMigDevState *bmds; - BlockDriverState *bs; - } *bmds_bs; - Error *local_err = NULL; - int ret; - - block_mig_state.submitted = 0; - block_mig_state.read_done = 0; - block_mig_state.transferred = 0; - block_mig_state.total_sector_sum = 0; - block_mig_state.prev_progress = -1; - block_mig_state.bulk_completed = 0; - block_mig_state.zero_blocks = migrate_zero_blocks(); - - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - num_bs++; - } - bmds_bs = g_malloc0(num_bs * sizeof(*bmds_bs)); - - for (i = 0, bs = bdrv_first(&it); bs; bs = bdrv_next(&it), i++) { - if (bdrv_is_read_only(bs)) { - continue; - } - - sectors = bdrv_nb_sectors(bs); - if (sectors <= 0) { - ret = sectors; - bdrv_next_cleanup(&it); - goto out; - } - - bmds = g_new0(BlkMigDevState, 1); - bmds->blk = blk_new(qemu_get_aio_context(), - BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); - bmds->blk_name = g_strdup(bdrv_get_device_name(bs)); - bmds->bulk_completed = 0; - bmds->total_sectors = sectors; - bmds->completed_sectors = 0; - bmds->shared_base = migrate_use_block_incremental(); - - assert(i < num_bs); - bmds_bs[i].bmds = bmds; - bmds_bs[i].bs = bs; - - block_mig_state.total_sector_sum += sectors; - - if (bmds->shared_base) { - trace_migration_block_init_shared(bdrv_get_device_name(bs)); - } else { - trace_migration_block_init_full(bdrv_get_device_name(bs)); - } - - QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); - } - - /* 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; - - if (bmds) { - ret = blk_insert_bs(bmds->blk, bs, &local_err); - if (ret < 0) { - error_report_err(local_err); - goto out; - } - - alloc_aio_bitmap(bmds); - error_setg(&bmds->blocker, "block device is in use by migration"); - bdrv_op_block_all(bs, bmds->blocker); - } - } - - ret = 0; -out: - g_free(bmds_bs); - return ret; -} - -/* Called with no lock taken. */ - -static int blk_mig_save_bulked_block(QEMUFile *f) -{ - int64_t completed_sector_sum = 0; - BlkMigDevState *bmds; - int progress; - int ret = 0; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (bmds->bulk_completed == 0) { - if (mig_save_device_bulk(f, bmds) == 1) { - /* completed bulk section for this device */ - bmds->bulk_completed = 1; - } - completed_sector_sum += bmds->completed_sectors; - ret = 1; - break; - } else { - completed_sector_sum += bmds->completed_sectors; - } - } - - if (block_mig_state.total_sector_sum != 0) { - progress = completed_sector_sum * 100 / - block_mig_state.total_sector_sum; - } else { - progress = 100; - } - if (progress != block_mig_state.prev_progress) { - block_mig_state.prev_progress = progress; - qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) - | BLK_MIG_FLAG_PROGRESS); - DPRINTF("Completed %d %%\r", progress); - } - - return ret; -} - -static void blk_mig_reset_dirty_cursor(void) -{ - BlkMigDevState *bmds; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - bmds->cur_dirty = 0; - } -} - -/* Called with iothread lock and AioContext taken. */ - -static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, - int is_async) -{ - BlkMigBlock *blk; - int64_t total_sectors = bmds->total_sectors; - int64_t sector; - int nr_sectors; - int ret = -EIO; - - for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { - blk_mig_lock(); - if (bmds_aio_inflight(bmds, sector)) { - blk_mig_unlock(); - blk_drain(bmds->blk); - } else { - blk_mig_unlock(); - } - bdrv_dirty_bitmap_lock(bmds->dirty_bitmap); - if (bdrv_dirty_bitmap_get_locked(bmds->dirty_bitmap, - sector * BDRV_SECTOR_SIZE)) { - if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { - nr_sectors = total_sectors - sector; - } else { - nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; - } - bdrv_reset_dirty_bitmap_locked(bmds->dirty_bitmap, - sector * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE); - bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); - - blk = g_new(BlkMigBlock, 1); - blk->buf = g_malloc(BLK_MIG_BLOCK_SIZE); - blk->bmds = bmds; - blk->sector = sector; - blk->nr_sectors = nr_sectors; - - if (is_async) { - qemu_iovec_init_buf(&blk->qiov, blk->buf, - nr_sectors * BDRV_SECTOR_SIZE); - - blk->aiocb = blk_aio_preadv(bmds->blk, - sector * BDRV_SECTOR_SIZE, - &blk->qiov, 0, blk_mig_read_cb, - blk); - - blk_mig_lock(); - block_mig_state.submitted++; - bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); - blk_mig_unlock(); - } else { - ret = blk_pread(bmds->blk, sector * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE, blk->buf, 0); - if (ret < 0) { - goto error; - } - blk_send(f, blk); - - g_free(blk->buf); - g_free(blk); - } - - sector += nr_sectors; - bmds->cur_dirty = sector; - break; - } - - bdrv_dirty_bitmap_unlock(bmds->dirty_bitmap); - sector += BDRV_SECTORS_PER_DIRTY_CHUNK; - bmds->cur_dirty = sector; - } - - return (bmds->cur_dirty >= bmds->total_sectors); - -error: - trace_migration_block_save_device_dirty(sector); - g_free(blk->buf); - g_free(blk); - return ret; -} - -/* Called with iothread lock taken. - * - * return value: - * 0: too much data for max_downtime - * 1: few enough data for max_downtime -*/ -static int blk_mig_save_dirty_block(QEMUFile *f, int is_async) -{ - BlkMigDevState *bmds; - 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; - } - } - - return ret; -} - -/* Called with no locks taken. */ - -static int flush_blks(QEMUFile *f) -{ - BlkMigBlock *blk; - int ret = 0; - - trace_migration_block_flush_blks("Enter", block_mig_state.submitted, - block_mig_state.read_done, - block_mig_state.transferred); - - blk_mig_lock(); - while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { - if (qemu_file_rate_limit(f)) { - break; - } - if (blk->ret < 0) { - ret = blk->ret; - break; - } - - QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); - blk_mig_unlock(); - blk_send(f, blk); - blk_mig_lock(); - - g_free(blk->buf); - g_free(blk); - - block_mig_state.read_done--; - block_mig_state.transferred++; - assert(block_mig_state.read_done >= 0); - } - blk_mig_unlock(); - - trace_migration_block_flush_blks("Exit", block_mig_state.submitted, - block_mig_state.read_done, - block_mig_state.transferred); - return ret; -} - -/* Called with iothread lock taken. */ - -static int64_t get_remaining_dirty(void) -{ - BlkMigDevState *bmds; - int64_t dirty = 0; - - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - aio_context_acquire(blk_get_aio_context(bmds->blk)); - dirty += bdrv_get_dirty_count(bmds->dirty_bitmap); - aio_context_release(blk_get_aio_context(bmds->blk)); - } - - return dirty; -} - - - -/* Called with iothread lock taken. */ -static void block_migration_cleanup_bmds(void) -{ - BlkMigDevState *bmds; - AioContext *ctx; - - 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); - 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. */ -static void block_migration_cleanup(void *opaque) -{ - BlkMigBlock *blk; - - bdrv_drain_all(); - - block_migration_cleanup_bmds(); - - blk_mig_lock(); - while ((blk = QSIMPLEQ_FIRST(&block_mig_state.blk_list)) != NULL) { - QSIMPLEQ_REMOVE_HEAD(&block_mig_state.blk_list, entry); - g_free(blk->buf); - g_free(blk); - } - blk_mig_unlock(); -} - -static int block_save_setup(QEMUFile *f, void *opaque) -{ - int ret; - - trace_migration_block_save("setup", block_mig_state.submitted, - block_mig_state.transferred); - - qemu_mutex_lock_iothread(); - 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; - } - - ret = flush_blks(f); - blk_mig_reset_dirty_cursor(); - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - - return ret; -} - -static int block_save_iterate(QEMUFile *f, void *opaque) -{ - int ret; - int64_t last_bytes = qemu_file_total_transferred(f); - int64_t delta_bytes; - - trace_migration_block_save("iterate", block_mig_state.submitted, - block_mig_state.transferred); - - ret = flush_blks(f); - if (ret) { - return ret; - } - - blk_mig_reset_dirty_cursor(); - - /* control the rate of transfer */ - blk_mig_lock(); - while (block_mig_state.read_done * BLK_MIG_BLOCK_SIZE < - qemu_file_get_rate_limit(f) && - block_mig_state.submitted < MAX_PARALLEL_IO && - (block_mig_state.submitted + block_mig_state.read_done) < - MAX_IO_BUFFERS) { - blk_mig_unlock(); - if (block_mig_state.bulk_completed == 0) { - /* first finish the bulk phase */ - if (blk_mig_save_bulked_block(f) == 0) { - /* finished saving bulk on all devices */ - block_mig_state.bulk_completed = 1; - } - ret = 0; - } else { - /* Always called with iothread lock taken for - * simplicity, block_save_complete also calls it. - */ - qemu_mutex_lock_iothread(); - ret = blk_mig_save_dirty_block(f, 1); - qemu_mutex_unlock_iothread(); - } - if (ret < 0) { - return ret; - } - blk_mig_lock(); - if (ret != 0) { - /* no more dirty blocks */ - break; - } - } - blk_mig_unlock(); - - ret = flush_blks(f); - if (ret) { - return ret; - } - - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - delta_bytes = qemu_file_total_transferred(f) - last_bytes; - if (delta_bytes > 0) { - return 1; - } else if (delta_bytes < 0) { - return -1; - } else { - return 0; - } -} - -/* Called with iothread lock taken. */ - -static int block_save_complete(QEMUFile *f, void *opaque) -{ - int ret; - - trace_migration_block_save("complete", block_mig_state.submitted, - block_mig_state.transferred); - - ret = flush_blks(f); - if (ret) { - return ret; - } - - blk_mig_reset_dirty_cursor(); - - /* we know for sure that save bulk is completed and - all async read completed */ - blk_mig_lock(); - assert(block_mig_state.submitted == 0); - blk_mig_unlock(); - - do { - ret = blk_mig_save_dirty_block(f, 0); - if (ret < 0) { - return ret; - } - } while (ret == 0); - - /* report completion */ - qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS); - - trace_migration_block_save_complete(); - - qemu_put_be64(f, BLK_MIG_FLAG_EOS); - - /* Make sure that our BlockBackends are gone, so that the block driver - * nodes can be inactivated. */ - block_migration_cleanup_bmds(); - - 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) -{ - /* Estimate pending number of bytes to send */ - uint64_t pending; - - qemu_mutex_lock_iothread(); - pending = get_remaining_dirty(); - qemu_mutex_unlock_iothread(); - - blk_mig_lock(); - pending += block_mig_state.submitted * BLK_MIG_BLOCK_SIZE + - block_mig_state.read_done * BLK_MIG_BLOCK_SIZE; - blk_mig_unlock(); - - /* Report at least one block pending during bulk phase */ - if (!pending && !block_mig_state.bulk_completed) { - pending = BLK_MIG_BLOCK_SIZE; - } - - trace_migration_block_save_pending(pending); - /* We don't do postcopy */ - *res_precopy_only += pending; -} - -static int block_load(QEMUFile *f, void *opaque, int version_id) -{ - static int banner_printed; - int len, flags; - char device_name[256]; - int64_t addr; - BlockBackend *blk, *blk_prev = NULL; - Error *local_err = NULL; - uint8_t *buf; - int64_t total_sectors = 0; - int nr_sectors; - int ret; - BlockDriverInfo bdi; - int cluster_size = BLK_MIG_BLOCK_SIZE; - - do { - addr = qemu_get_be64(f); - - flags = addr & (BDRV_SECTOR_SIZE - 1); - addr >>= BDRV_SECTOR_BITS; - - if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { - /* get device name */ - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)device_name, len); - device_name[len] = '\0'; - - blk = blk_by_name(device_name); - if (!blk) { - fprintf(stderr, "Error unknown block device %s\n", - device_name); - return -EINVAL; - } - - if (blk != blk_prev) { - blk_prev = blk; - total_sectors = blk_nb_sectors(blk); - if (total_sectors <= 0) { - error_report("Error getting length of block device %s", - device_name); - return -EINVAL; - } - - blk_activate(blk, &local_err); - if (local_err) { - error_report_err(local_err); - return -EINVAL; - } - - ret = bdrv_get_info(blk_bs(blk), &bdi); - if (ret == 0 && bdi.cluster_size > 0 && - bdi.cluster_size <= BLK_MIG_BLOCK_SIZE && - BLK_MIG_BLOCK_SIZE % bdi.cluster_size == 0) { - cluster_size = bdi.cluster_size; - } else { - cluster_size = BLK_MIG_BLOCK_SIZE; - } - } - - if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { - nr_sectors = total_sectors - addr; - } else { - nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; - } - - if (flags & BLK_MIG_FLAG_ZERO_BLOCK) { - ret = blk_pwrite_zeroes(blk, addr * BDRV_SECTOR_SIZE, - nr_sectors * BDRV_SECTOR_SIZE, - BDRV_REQ_MAY_UNMAP); - } else { - int i; - int64_t cur_addr; - uint8_t *cur_buf; - - buf = g_malloc(BLK_MIG_BLOCK_SIZE); - qemu_get_buffer(f, buf, BLK_MIG_BLOCK_SIZE); - for (i = 0; i < BLK_MIG_BLOCK_SIZE / cluster_size; i++) { - cur_addr = addr * BDRV_SECTOR_SIZE + i * cluster_size; - cur_buf = buf + i * cluster_size; - - if ((!block_mig_state.zero_blocks || - cluster_size < BLK_MIG_BLOCK_SIZE) && - buffer_is_zero(cur_buf, cluster_size)) { - ret = blk_pwrite_zeroes(blk, cur_addr, - cluster_size, - BDRV_REQ_MAY_UNMAP); - } else { - ret = blk_pwrite(blk, cur_addr, cluster_size, cur_buf, - 0); - } - if (ret < 0) { - break; - } - } - g_free(buf); - } - - if (ret < 0) { - return ret; - } - } else if (flags & BLK_MIG_FLAG_PROGRESS) { - if (!banner_printed) { - printf("Receiving block device images\n"); - banner_printed = 1; - } - printf("Completed %d %%%c", (int)addr, - (addr == 100) ? '\n' : '\r'); - fflush(stdout); - } else if (!(flags & BLK_MIG_FLAG_EOS)) { - fprintf(stderr, "Unknown block migration flags: 0x%x\n", flags); - return -EINVAL; - } - ret = qemu_file_get_error(f); - if (ret != 0) { - return ret; - } - } while (!(flags & BLK_MIG_FLAG_EOS)); - - return 0; -} - -static bool block_is_active(void *opaque) -{ - return migrate_use_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, - .load_state = block_load, - .save_cleanup = block_migration_cleanup, - .is_active = block_is_active, -}; - -void blk_mig_init(void) -{ - QSIMPLEQ_INIT(&block_mig_state.bmds_list); - QSIMPLEQ_INIT(&block_mig_state.blk_list); - qemu_mutex_init(&block_mig_state.lock); - - register_savevm_live("block", 0, 1, &savevm_block_handlers, - &block_mig_state); -} diff --git a/migration/block.h b/migration/block.h deleted file mode 100644 index 3178609dbd..0000000000 --- a/migration/block.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * QEMU live block migration - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Liran Schour - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef MIGRATION_BLOCK_H -#define MIGRATION_BLOCK_H - -#ifdef CONFIG_LIVE_BLOCK_MIGRATION -int blk_mig_active(void); -int blk_mig_bulk_active(void); -uint64_t blk_mig_bytes_transferred(void); -uint64_t blk_mig_bytes_remaining(void); -uint64_t blk_mig_bytes_total(void); - -#else -static inline int blk_mig_active(void) -{ - return false; -} - -static inline int blk_mig_bulk_active(void) -{ - return false; -} - -static inline uint64_t blk_mig_bytes_transferred(void) -{ - return 0; -} - -static inline uint64_t blk_mig_bytes_remaining(void) -{ - return 0; -} - -static inline uint64_t blk_mig_bytes_total(void) -{ - return 0; -} -#endif /* CONFIG_LIVE_BLOCK_MIGRATION */ - -void migrate_set_block_enabled(bool value, Error **errp); -#endif /* MIGRATION_BLOCK_H */ diff --git a/migration/channel-block.c b/migration/channel-block.c index f4ab53acdb..fff8d87094 100644 --- a/migration/channel-block.c +++ b/migration/channel-block.c @@ -53,6 +53,7 @@ qio_channel_block_readv(QIOChannel *ioc, size_t niov, int **fds, size_t *nfds, + int flags, Error **errp) { QIOChannelBlock *bioc = QIO_CHANNEL_BLOCK(ioc); @@ -157,8 +158,9 @@ qio_channel_block_close(QIOChannel *ioc, static void qio_channel_block_set_aio_fd_handler(QIOChannel *ioc, - AioContext *ctx, + AioContext *read_ctx, IOHandler *io_read, + AioContext *write_ctx, IOHandler *io_write, void *opaque) { diff --git a/migration/channel.c b/migration/channel.c index 1b0815039f..f9de064f3b 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -92,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-stubs.c b/migration/colo-stubs.c new file mode 100644 index 0000000000..e22ce65234 --- /dev/null +++ b/migration/colo-stubs.c @@ -0,0 +1,35 @@ +#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) +{ +} + +void coroutine_fn colo_incoming_co(void) +{ +} + +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/migration/colo.c b/migration/colo.c index 2b71722fd6..9590f281d0 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -18,7 +18,6 @@ #include "qemu-file.h" #include "savevm.h" #include "migration/colo.h" -#include "block.h" #include "io/channel-buffer.h" #include "trace.h" #include "qemu/error-report.h" @@ -26,17 +25,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; @@ -65,10 +62,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; @@ -130,17 +149,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; @@ -151,7 +166,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(), @@ -181,9 +196,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) @@ -217,7 +229,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) @@ -250,7 +261,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; @@ -272,7 +282,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) { @@ -309,9 +318,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"); } @@ -330,9 +337,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)); @@ -419,13 +424,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(), @@ -434,27 +439,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; } @@ -482,8 +483,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; } @@ -508,9 +508,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: @@ -522,7 +522,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) @@ -561,23 +561,19 @@ static void colo_process_checkpoint(MigrationState *s) 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) { @@ -645,28 +641,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, @@ -677,9 +661,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 */ @@ -697,10 +681,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"); @@ -739,23 +723,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; } @@ -764,25 +747,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) { @@ -836,7 +816,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; @@ -854,6 +834,15 @@ void *colo_process_incoming_thread(void *opaque) return NULL; } + /* Make sure all file formats throw away their mutable metadata */ + bql_lock(); + bdrv_activate_all(&local_err); + bql_unlock(); + if (local_err) { + error_report_err(local_err); + return NULL; + } + failover_init_state(); mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); @@ -875,18 +864,14 @@ void *colo_process_incoming_thread(void *opaque) 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, @@ -941,3 +926,28 @@ out: rcu_unregister_thread(); return NULL; } + +void coroutine_fn colo_incoming_co(void) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + QemuThread th; + + assert(bql_locked()); + assert(migration_incoming_colo_enabled()); + + qemu_thread_create(&th, MIGRATION_THREAD_DST_COLO, + 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(); +} diff --git a/migration/cpu-throttle.c b/migration/cpu-throttle.c new file mode 100644 index 0000000000..5179019e33 --- /dev/null +++ b/migration/cpu-throttle.c @@ -0,0 +1,199 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "hw/core/cpu.h" +#include "qemu/main-loop.h" +#include "sysemu/cpus.h" +#include "sysemu/cpu-throttle.h" +#include "migration.h" +#include "migration-stats.h" +#include "trace.h" + +/* vcpu throttling controls */ +static QEMUTimer *throttle_timer, *throttle_dirty_sync_timer; +static unsigned int throttle_percentage; +static bool throttle_dirty_sync_timer_active; +static uint64_t throttle_dirty_sync_count_prev; + +#define CPU_THROTTLE_PCT_MIN 1 +#define CPU_THROTTLE_PCT_MAX 99 +#define CPU_THROTTLE_TIMESLICE_NS 10000000 + +/* Making sure RAMBlock dirty bitmap is synchronized every five seconds */ +#define CPU_THROTTLE_DIRTY_SYNC_TIMESLICE_MS 5000 + +static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque) +{ + double pct; + double throttle_ratio; + int64_t sleeptime_ns, endtime_ns; + + if (!cpu_throttle_get_percentage()) { + return; + } + + pct = (double)cpu_throttle_get_percentage() / 100; + throttle_ratio = pct / (1 - pct); + /* Add 1ns to fix double's rounding error (like 0.9999999...) */ + sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1); + 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_bql(cpu->halt_cond, + sleeptime_ns / SCALE_MS); + } else { + bql_unlock(); + g_usleep(sleeptime_ns / SCALE_US); + bql_lock(); + } + sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + } + qatomic_set(&cpu->throttle_thread_scheduled, 0); +} + +static void cpu_throttle_timer_tick(void *opaque) +{ + CPUState *cpu; + double pct; + + /* Stop the timer if needed */ + if (!cpu_throttle_get_percentage()) { + return; + } + CPU_FOREACH(cpu) { + if (!qatomic_xchg(&cpu->throttle_thread_scheduled, 1)) { + async_run_on_cpu(cpu, cpu_throttle_thread, + RUN_ON_CPU_NULL); + } + } + + pct = (double)cpu_throttle_get_percentage() / 100; + timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + + CPU_THROTTLE_TIMESLICE_NS / (1 - pct)); +} + +void cpu_throttle_set(int new_throttle_pct) +{ + /* + * boolean to store whether throttle is already active or not, + * before modifying throttle_percentage + */ + bool throttle_active = cpu_throttle_active(); + + trace_cpu_throttle_set(new_throttle_pct); + + /* Ensure throttle percentage is within valid range */ + new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX); + new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN); + + qatomic_set(&throttle_percentage, new_throttle_pct); + + if (!throttle_active) { + cpu_throttle_timer_tick(NULL); + } +} + +void cpu_throttle_stop(void) +{ + qatomic_set(&throttle_percentage, 0); + cpu_throttle_dirty_sync_timer(false); +} + +bool cpu_throttle_active(void) +{ + return (cpu_throttle_get_percentage() != 0); +} + +int cpu_throttle_get_percentage(void) +{ + return qatomic_read(&throttle_percentage); +} + +void cpu_throttle_dirty_sync_timer_tick(void *opaque) +{ + uint64_t sync_cnt = stat64_get(&mig_stats.dirty_sync_count); + + /* + * The first iteration copies all memory anyhow and has no + * effect on guest performance, therefore omit it to avoid + * paying extra for the sync penalty. + */ + if (sync_cnt <= 1) { + goto end; + } + + if (sync_cnt == throttle_dirty_sync_count_prev) { + trace_cpu_throttle_dirty_sync(); + WITH_RCU_READ_LOCK_GUARD() { + migration_bitmap_sync_precopy(false); + } + } + +end: + throttle_dirty_sync_count_prev = stat64_get(&mig_stats.dirty_sync_count); + + timer_mod(throttle_dirty_sync_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + + CPU_THROTTLE_DIRTY_SYNC_TIMESLICE_MS); +} + +static bool cpu_throttle_dirty_sync_active(void) +{ + return qatomic_read(&throttle_dirty_sync_timer_active); +} + +void cpu_throttle_dirty_sync_timer(bool enable) +{ + assert(throttle_dirty_sync_timer); + + if (enable) { + if (!cpu_throttle_dirty_sync_active()) { + /* + * Always reset the dirty sync count cache, in case migration + * was cancelled once. + */ + throttle_dirty_sync_count_prev = 0; + timer_mod(throttle_dirty_sync_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + + CPU_THROTTLE_DIRTY_SYNC_TIMESLICE_MS); + qatomic_set(&throttle_dirty_sync_timer_active, 1); + } + } else { + if (cpu_throttle_dirty_sync_active()) { + timer_del(throttle_dirty_sync_timer); + qatomic_set(&throttle_dirty_sync_timer_active, 0); + } + } +} + +void cpu_throttle_init(void) +{ + throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + cpu_throttle_timer_tick, NULL); + throttle_dirty_sync_timer = + timer_new_ms(QEMU_CLOCK_VIRTUAL_RT, + cpu_throttle_dirty_sync_timer_tick, NULL); +} diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index d6f1e01a70..f7e86686fc 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -11,11 +11,11 @@ */ #include "qemu/osdep.h" -#include +#include "qemu/error-report.h" +#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 +28,8 @@ #include "sysemu/kvm.h" #include "sysemu/runstate.h" #include "exec/memory.h" +#include "qemu/xxhash.h" +#include "migration.h" /* * total_dirty_pages is procted by BQL and is used @@ -55,6 +57,8 @@ static int64_t dirty_stat_wait(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; @@ -73,24 +77,32 @@ static inline void record_dirtypages(DirtyPageRecord *dirty_pages, static int64_t do_calculate_dirtyrate(DirtyPageRecord dirty_pages, int64_t calc_time_ms) { - uint64_t memory_size_MB; uint64_t increased_dirty_pages = dirty_pages.end_pages - dirty_pages.start_pages; - memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20; - - return memory_size_MB * 1000 / calc_time_ms; + /* + * 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) { - qemu_mutex_lock_iothread(); + Error *local_err = NULL; + bool ret; + + bql_lock(); if (start) { - memory_global_dirty_log_start(flag); + ret = memory_global_dirty_log_start(flag, &local_err); + if (!ret) { + error_report_err(local_err); + } } else { memory_global_dirty_log_stop(flag); } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* @@ -100,18 +112,17 @@ void global_dirty_log_change(unsigned int flag, bool start) */ static void global_dirty_log_sync(unsigned int flag, bool one_shot) { - qemu_mutex_lock_iothread(); - memory_global_dirty_log_sync(); + bql_lock(); + memory_global_dirty_log_sync(false); if (one_shot) { memory_global_dirty_log_stop(flag); } - qemu_mutex_unlock_iothread(); + bql_unlock(); } static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) { CPUState *cpu; - DirtyPageRecord *records; int nvcpu = 0; CPU_FOREACH(cpu) { @@ -121,13 +132,10 @@ static DirtyPageRecord *vcpu_dirty_stat_alloc(VcpuStat *stat) stat->nvcpu = nvcpu; stat->rates = g_new0(DirtyRateVcpu, nvcpu); - records = g_new0(DirtyPageRecord, nvcpu); - - return records; + return g_new0(DirtyPageRecord, nvcpu); } -static void vcpu_dirty_stat_collect(VcpuStat *stat, - DirtyPageRecord *records, +static void vcpu_dirty_stat_collect(DirtyPageRecord *records, bool start) { CPUState *cpu; @@ -142,35 +150,35 @@ int64_t vcpu_calculate_dirtyrate(int64_t calc_time_ms, unsigned int flag, bool one_shot) { - DirtyPageRecord *records; + DirtyPageRecord *records = NULL; int64_t init_time_ms; int64_t duration; int64_t dirtyrate; int i = 0; - unsigned int gen_id; + unsigned int gen_id = 0; retry: init_time_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - cpu_list_lock(); - gen_id = cpu_list_generation_id_get(); - records = vcpu_dirty_stat_alloc(stat); - vcpu_dirty_stat_collect(stat, records, true); - cpu_list_unlock(); + 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); - cpu_list_lock(); - if (gen_id != cpu_list_generation_id_get()) { - g_free(records); - g_free(stat->rates); - cpu_list_unlock(); - goto retry; + 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); } - vcpu_dirty_stat_collect(stat, records, false); - cpu_list_unlock(); for (i = 0; i < stat->nvcpu; i++) { dirtyrate = do_calculate_dirtyrate(records[i], duration); @@ -186,10 +194,9 @@ retry: return duration; } -static bool is_sample_period_valid(int64_t sec) +static bool is_calc_time_valid(int64_t msec) { - if (sec < MIN_FETCH_DIRTYRATE_TIME_SEC || - sec > MAX_FETCH_DIRTYRATE_TIME_SEC) { + if ((msec < MIN_CALC_TIME_MS) || (msec > MAX_CALC_TIME_MS)) { return false; } @@ -213,7 +220,38 @@ 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: + g_assert_not_reached(); + } +} + +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; @@ -222,7 +260,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; @@ -256,12 +297,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) { @@ -293,8 +333,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) @@ -310,6 +350,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. @@ -317,13 +385,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) @@ -369,15 +437,18 @@ static void get_ramblock_dirty_info(RAMBlock *block, struct DirtyRateConfig *config) { uint64_t sample_pages_per_gigabytes = config->sample_pages_per_gigabytes; + gsize len; /* Right shift 30 bits to calc ramblock size in GB */ info->sample_pages_count = (qemu_ram_get_used_length(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)); + len = g_strlcpy(info->idstr, qemu_ram_get_idstr(block), + sizeof(info->idstr)); + g_assert(len < sizeof(info->idstr)); } static void free_ramblock_dirty_info(struct RamblockDirtyInfo *infos, int count) @@ -456,13 +527,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++; } } @@ -473,7 +544,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))) { @@ -487,14 +557,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, @@ -546,12 +614,14 @@ 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; + Error *local_err = NULL; - qemu_mutex_lock_iothread(); - memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE); + bql_lock(); + if (!memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE, &local_err)) { + error_report_err(local_err); + } /* * 1'round of log sync may return all 1 bits with @@ -559,7 +629,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. @@ -567,16 +637,14 @@ 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 = dirty_stat_wait(msec, start_time); - DirtyStat.calc_time = msec / 1000; + DirtyStat.calc_time_ms = dirty_stat_wait(config.calc_time_ms, start_time); /* * do two things. @@ -587,12 +655,12 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config) record_dirtypages_bitmap(&dirty_pages, false); - DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, msec); + DirtyStat.dirty_rate = do_calculate_dirtyrate(dirty_pages, + DirtyStat.calc_time_ms); } static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) { - int64_t duration; uint64_t dirtyrate = 0; uint64_t dirtyrate_sum = 0; int i = 0; @@ -600,15 +668,13 @@ static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config) /* start log sync */ global_dirty_log_change(GLOBAL_DIRTY_DIRTY_RATE, true); - DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; + DirtyStat.start_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; /* calculate vcpu dirtyrate */ - duration = vcpu_calculate_dirtyrate(config.sample_period_seconds * 1000, - &DirtyStat.dirty_ring, - GLOBAL_DIRTY_DIRTY_RATE, - true); - - DirtyStat.calc_time = duration / 1000; + 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++) { @@ -624,27 +690,25 @@ 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 = dirty_stat_wait(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(); @@ -690,6 +754,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, @@ -699,7 +765,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. @@ -709,10 +774,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; } @@ -720,8 +790,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; } @@ -759,7 +829,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; @@ -771,28 +841,34 @@ 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); + qemu_thread_create(&thread, MIGRATION_THREAD_DIRTY_RATE, + 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", @@ -843,8 +919,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 594a5c0bb6..869c060941 100644 --- a/migration/dirtyrate.h +++ b/migration/dirtyrate.h @@ -31,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 @@ -44,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 */ }; @@ -73,7 +75,7 @@ typedef struct SampleVMStat { 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..9bf9be6acb 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,11 +17,37 @@ #include "qemu/osdep.h" #include "channel.h" #include "fd.h" +#include "file.h" #include "migration.h" #include "monitor/monitor.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" #include "io/channel-util.h" #include "trace.h" +static bool fd_is_pipe(int fd) +{ + struct stat statbuf; + + if (fstat(fd, &statbuf) == -1) { + return false; + } + + return S_ISFIFO(statbuf.st_mode); +} + +static bool migration_fd_valid(int fd) +{ + if (fd_is_socket(fd)) { + return true; + } + + if (fd_is_pipe(fd)) { + return true; + } + + return false; +} void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error **errp) { @@ -31,6 +57,11 @@ void fd_start_outgoing_migration(MigrationState *s, const char *fdname, Error ** return; } + if (!migration_fd_valid(fd)) { + warn_report("fd: migration to a file is deprecated." + " Use file: instead."); + } + trace_migration_fd_outgoing(fd); ioc = qio_channel_new_fd(fd, errp); if (!ioc) { @@ -38,7 +69,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)); } @@ -60,6 +91,11 @@ void fd_start_incoming_migration(const char *fdname, Error **errp) return; } + if (!migration_fd_valid(fd)) { + warn_report("fd: migration to a file is deprecated." + " Use file: instead."); + } + trace_migration_fd_incoming(fd); ioc = qio_channel_new_fd(fd, errp); @@ -68,7 +104,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..7f11e26f5c --- /dev/null +++ b/migration/file.c @@ -0,0 +1,268 @@ +/* + * 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; +} + +static void file_enable_direct_io(int *flags) +{ +#ifdef O_DIRECT + *flags |= O_DIRECT; +#else + /* it should have been rejected when setting the parameter */ + g_assert_not_reached(); +#endif +} + +bool file_send_channel_create(gpointer opaque, Error **errp) +{ + QIOChannelFile *ioc; + int flags = O_WRONLY; + bool ret = true; + + if (migrate_direct_io()) { + /* + * Enable O_DIRECT for the secondary channels. These are used + * for sending ram pages and writes should be guaranteed to be + * aligned to at least page size. + */ + file_enable_direct_io(&flags); + } + + 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, 0600, errp); + if (!fioc) { + return; + } + + if (ftruncate(fioc->fd, offset)) { + error_setg_errno(errp, errno, + "failed to truncate migration file to offset %" PRIx64, + offset); + 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; +} + +static void file_create_incoming_channels(QIOChannel *ioc, char *filename, + Error **errp) +{ + int i, channels = 1; + g_autofree QIOChannel **iocs = NULL; + int flags = O_RDONLY; + + if (migrate_multifd()) { + channels += migrate_multifd_channels(); + if (migrate_direct_io()) { + file_enable_direct_io(&flags); + } + } + + iocs = g_new0(QIOChannel *, channels); + iocs[0] = ioc; + + for (i = 1; i < channels; i++) { + QIOChannelFile *fioc = qio_channel_file_new_path(filename, flags, 0, 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), filename, errp); +} + +int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, + int niov, MultiFDPages_t *pages, Error **errp) +{ + ssize_t ret = 0; + int i, slice_idx, slice_num; + uintptr_t base, next, offset; + size_t len; + RAMBlock *block = pages->block; + + 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..1a1115f7f1 --- /dev/null +++ b/migration/file.h @@ -0,0 +1,26 @@ +/* + * 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); +int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, + int niov, MultiFDPages_t *pages, 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 690487cf1a..d53cf3417a 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -1,5 +1,6 @@ # Files needed by unit tests migration_files = files( + 'migration-stats.c', 'page_cache.c', 'xbzrle.c', 'vmstate-types.c', @@ -7,31 +8,43 @@ migration_files = files( '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', 'channel-block.c', - 'colo-failover.c', - 'colo.c', + 'cpu-throttle.c', + 'dirtyrate.c', 'exec.c', 'fd.c', + 'file.c', 'global_state.c', + 'migration-hmp-cmds.c', 'migration.c', 'multifd.c', + 'multifd-nocomp.c', 'multifd-zlib.c', + 'multifd-zero-page.c', + 'options.c', 'postcopy-ram.c', 'savevm.c', 'socket.c', 'tls.c', -), gnutls) + 'threadinfo.c', +), gnutls, zlib) -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')) +else + system_ss.add(files('colo-stubs.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')) +system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) +system_ss.add(when: qpl, if_true: files('multifd-qpl.c')) +system_ss.add(when: uadk, if_true: files('multifd-uadk.c')) +system_ss.add(when: qatzip, if_true: files('multifd-qatzip.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..20d1a6e219 --- /dev/null +++ b/migration/migration-hmp-cmds.c @@ -0,0 +1,816 @@ +/* + * 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, "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, "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->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_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_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); + 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)); + + if (params->has_direct_io) { + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str( + MIGRATION_PARAMETER_DIRECT_IO), + params->direct_io ? "on" : "off"); + } + } + + 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, true, false, &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_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_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_QATZIP_LEVEL: + p->has_multifd_qatzip_level = true; + visit_type_uint8(v, param, &p->multifd_qatzip_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; + case MIGRATION_PARAMETER_DIRECT_IO: + p->has_direct_io = true; + visit_type_bool(v, param, &p->direct_io, &err); + break; + default: + g_assert_not_reached(); + } + + 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) { + timer_mod(status->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); + } else { + 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 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 (!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, 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 c19fb5cb3e..8c5bd0a75c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -20,6 +20,7 @@ #include "migration/blocker.h" #include "exec.h" #include "fd.h" +#include "file.h" #include "socket.h" #include "sysemu/runstate.h" #include "sysemu/sysemu.h" @@ -29,8 +30,10 @@ #include "migration/global_state.h" #include "migration/misc.h" #include "migration.h" +#include "migration-stats.h" #include "savevm.h" #include "qemu-file.h" +#include "channel.h" #include "migration/vmstate.h" #include "block/block.h" #include "qapi/error.h" @@ -42,7 +45,6 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qnull.h" #include "qemu/rcu.h" -#include "block.h" #include "postcopy-ram.h" #include "qemu/thread.h" #include "trace.h" @@ -51,73 +53,29 @@ #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) +#define INMIGRATE_DEFAULT_EXIT_ON_ERROR true -/* 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,43 +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, - MIGRATION_CAPABILITY_ZERO_COPY_SEND); - /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add dynamic creation of migration */ @@ -173,24 +99,111 @@ 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 migration_needs_extra_fds(void) +{ + /* + * When doing direct-io, multifd requires two different, + * non-duplicated file descriptors so we can use one of them for + * unaligned IO. + */ + return migrate_multifd() && migrate_direct_io(); +} + +static bool transport_supports_seeking(MigrationAddress *addr) +{ + if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + return true; + } + + return false; +} + +static bool transport_supports_extra_fds(MigrationAddress *addr) +{ + /* file: works because QEMU can open it multiple times */ + return addr->transport == MIGRATION_ADDRESS_TYPE_FILE; +} + +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; + } + + if (migration_needs_extra_fds() && + !transport_supports_extra_fds(addr)) { + error_setg(errp, + "Migration requires a transport that allows for extra fds (e.g. file)"); + return false; + } + + return true; } static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) @@ -200,6 +213,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. */ @@ -221,14 +251,62 @@ void migration_object_init(void) 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); + current_incoming->exit_on_error = INMIGRATE_DEFAULT_EXIT_ON_ERROR; + migration_object_check(current_migration, &error_fatal); - blk_mig_init(); ram_mig_init(); dirty_bitmap_mig_init(); + + /* Initialize cpu throttle timers */ + cpu_throttle_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) @@ -236,6 +314,9 @@ 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); } @@ -299,6 +380,13 @@ void migration_incoming_state_destroy(void) { struct MigrationIncomingState *mis = migration_incoming_get_current(); + multifd_recv_cleanup(); + /* + * RAM state cleanup needs to happen after multifd cleanup, because + * multifd threads can use some of its states (receivedmap). + */ + qemu_loadvm_state_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); @@ -333,23 +421,13 @@ void migration_incoming_state_destroy(void) yank_unregister_instance(MIGRATION_YANK_INSTANCE); } -static void migrate_generate_event(int new_state) +static void migrate_generate_event(MigrationStatus 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. @@ -375,12 +453,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. @@ -440,7 +513,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); } } @@ -470,6 +543,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 the " + "replication module is not built in"); + return -ENOTSUP; +#endif + + if (!migrate_colo()) { + error_report("ENABLE_COLO command come in migration stream, but x-colo " + "capability is not set"); + return -EINVAL; + } + if (ram_block_discard_disable(true)) { error_report("COLO: cannot disable RAM discard"); return -EBUSY; @@ -486,25 +571,151 @@ 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 bool +migration_incoming_state_setup(MigrationIncomingState *mis, Error **errp) +{ + MigrationStatus current = mis->state; + + if (current == MIGRATION_STATUS_POSTCOPY_PAUSED) { + /* + * Incoming postcopy migration will stay in PAUSED state even if + * reconnection happened. + */ + return true; + } + + if (current != MIGRATION_STATUS_NONE) { + error_setg(errp, "Illegal migration incoming state: %s", + MigrationStatus_str(current)); + return false; + } + + migrate_set_state(&mis->state, current, MIGRATION_STATUS_SETUP); + 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; + } + + if (!migration_incoming_state_setup(mis, errp)) { + return; + } + + 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_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); } @@ -515,6 +726,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. @@ -523,7 +736,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); @@ -540,18 +753,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 { @@ -563,6 +772,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 @@ -570,25 +780,30 @@ 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 coroutine_fn process_incoming_migration_co(void *opaque) { + MigrationState *s = migrate_get_current(); 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(); + 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); @@ -611,67 +826,48 @@ 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) { - error_report("load of migration failed: %s", strerror(-ret)); + error_setg(&local_err, "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 (migration_incoming_colo_enabled()) { + /* yield until COLO exit */ + colo_incoming_co(); + } + + 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); + migrate_set_error(s, local_err); + error_free(local_err); + + migration_incoming_state_destroy(); + + if (mis->exit_on_error) { + WITH_QEMU_LOCK_GUARD(&s->error_mutex) { + error_report_err(s->error); + s->error = NULL; + } + + exit(EXIT_FAILURE); } - 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) @@ -713,51 +909,90 @@ 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(); } -static bool migration_needs_multiple_sockets(void) +/* + * Returns true when we want to start a new incoming migration process, + * false otherwise. + */ +static bool migration_should_start_incoming(bool main_channel) { - return migrate_use_multifd() || migrate_postcopy_preempt(); + /* 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) */ - f = qemu_file_new_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. Some features need more than one channel, we wait. - */ - start_migration = !migration_needs_multiple_sockets(); + 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(migration_needs_multiple_sockets()); - if (migrate_use_multifd()) { - start_migration = multifd_recv_new_channel(ioc, &local_err); + if (migrate_multifd()) { + multifd_recv_new_channel(ioc, &local_err); } else { assert(migrate_postcopy_preempt()); f = qemu_file_new_input(ioc); - start_migration = postcopy_preempt_new_channel(mis, f); + postcopy_preempt_new_channel(mis, f); } if (local_err) { error_propagate(errp, local_err); @@ -765,7 +1000,7 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) } } - if (start_migration) { + if (migration_should_start_incoming(default_channel)) { /* If it's a recovery, we're done */ if (postcopy_try_recover()) { return; @@ -788,7 +1023,7 @@ bool migration_has_all_channels(void) return false; } - if (migrate_use_multifd()) { + if (migrate_multifd()) { return multifd_recv_all_channels_created(); } @@ -799,6 +1034,11 @@ bool migration_has_all_channels(void) return true; } +int migrate_send_rp_switchover_ack(MigrationIncomingState *mis) +{ + return migrate_send_rp_message(mis, MIG_RP_MSG_SWITCHOVER_ACK, 0, NULL); +} + /* * Send a 'SHUT' message on the return channel with the given value * to indicate that we've finished with the RP. Non-0 value indicates @@ -873,171 +1113,56 @@ 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) +bool migration_is_running(void) { - MigrationCapabilityStatusList *head = NULL, **tail = &head; - MigrationCapabilityStatus *caps; - MigrationState *s = migrate_get_current(); - int i; + MigrationState *s = current_migration; - 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; - 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) -{ - switch (state) { - case MIGRATION_STATUS_ACTIVE: - case MIGRATION_STATUS_POSTCOPY_ACTIVE: - case MIGRATION_STATUS_POSTCOPY_PAUSED: - case MIGRATION_STATUS_POSTCOPY_RECOVER: - case MIGRATION_STATUS_SETUP: - case MIGRATION_STATUS_PRE_SWITCHOVER: - case MIGRATION_STATUS_DEVICE: - case MIGRATION_STATUS_WAIT_UNPLUG: - case MIGRATION_STATUS_COLO: - return true; - - default: + if (!s) { return false; - } -} -bool migration_is_running(int state) -{ - switch (state) { + switch (s->state) { case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_WAIT_UNPLUG: case MIGRATION_STATUS_CANCELLING: + case MIGRATION_STATUS_COLO: return true; - default: return false; - } } +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; } @@ -1047,29 +1172,27 @@ 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; - /* 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->duplicate = stat64_get(&mig_stats.zero_pages); + 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->dirty_sync_count = + stat64_get(&mig_stats.dirty_sync_count); info->ram->dirty_sync_missed_zero_copy = - ram_counters.dirty_sync_missed_zero_copy; - info->ram->postcopy_requests = ram_counters.postcopy_requests; + 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; @@ -1080,18 +1203,6 @@ 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; - } - if (cpu_throttle_active()) { info->has_cpu_throttle_percentage = true; info->cpu_throttle_percentage = cpu_throttle_get_percentage(); @@ -1099,18 +1210,17 @@ 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); } -} -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(); - info->disk->total = blk_mig_bytes_total(); + 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(); } } @@ -1118,7 +1228,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; @@ -1152,12 +1262,12 @@ static void fill_source_migration_info(MigrationInfo *info) case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: /* TODO add some postcopy stats */ 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; @@ -1166,14 +1276,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; @@ -1183,181 +1289,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; - } - } - } - -#ifdef CONFIG_LINUX - if (cap_list[MIGRATION_CAPABILITY_ZERO_COPY_SEND] && - (!cap_list[MIGRATION_CAPABILITY_MULTIFD] || - cap_list[MIGRATION_CAPABILITY_COMPRESS] || - cap_list[MIGRATION_CAPABILITY_XBZRLE] || - migrate_multifd_compression() || - migrate_use_tls())) { - error_setg(errp, - "Zero copy only available for non-compressed non-TLS multifd migration"); - return false; - } -#else - if (cap_list[MIGRATION_CAPABILITY_ZERO_COPY_SEND]) { - error_setg(errp, - "Zero copy currently only available on Linux"); - return false; - } -#endif - - - /* 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; - } - - if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]) { - if (!cap_list[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 (cap_list[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, "Postcopy preempt not compatible with compress"); - return false; - } - } - - if (cap_list[MIGRATION_CAPABILITY_MULTIFD]) { - if (cap_list[MIGRATION_CAPABILITY_COMPRESS]) { - error_setg(errp, "Multifd is not compatible with compress"); - return false; - } - } - - return true; } static void fill_destination_migration_info(MigrationInfo *info) @@ -1371,8 +1307,6 @@ static void fill_destination_migration_info(MigrationInfo *info) } switch (mis->state) { - case MIGRATION_STATUS_NONE: - return; case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_CANCELLING: case MIGRATION_STATUS_CANCELLED: @@ -1388,8 +1322,19 @@ static void fill_destination_migration_info(MigrationInfo *info) info->has_status = true; fill_destination_postcopy_migration_info(info); break; + default: + return; } info->status = mis->state; + + if (!info->error_desc) { + MigrationState *s = migrate_get_current(); + QEMU_LOCK_GUARD(&s->error_mutex); + + if (s->error) { + info->error_desc = g_strdup(error_get_pretty(s->error)); + } + } } MigrationInfo *qmp_query_migrate(Error **errp) @@ -1402,439 +1347,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 (migrate_use_zero_copy_send() && - ((params->has_multifd_compression && params->multifd_compression) || - (params->has_tls_creds && 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; - } - 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; - } - 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(); @@ -1859,7 +1371,8 @@ void qmp_migrate_start_postcopy(Error **errp) /* shared migration helpers */ -void migrate_set_state(int *state, int old_state, int new_state) +void migrate_set_state(MigrationStatus *state, MigrationStatus old_state, + MigrationStatus new_state) { assert(new_state < MIGRATION_STATUS__MAX); if (qatomic_cmpxchg(state, old_state, new_state) == old_state) { @@ -1868,83 +1381,50 @@ 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; + QEMUFile *tmp = NULL; + + trace_migrate_fd_cleanup(); g_free(s->hostname); s->hostname = NULL; + json_writer_free(s->vmdesc); + s->vmdesc = NULL; qemu_savevm_state_cleanup(); - if (s->to_dst_file) { - QEMUFile *tmp; + close_return_path_on_source(s); - trace_migrate_fd_cleanup(); - qemu_mutex_unlock_iothread(); - if (s->migration_thread_running) { - qemu_thread_join(&s->thread); - s->migration_thread_running = false; - } - qemu_mutex_lock_iothread(); + if (s->migration_thread_running) { + bql_unlock(); + qemu_thread_join(&s->thread); + s->migration_thread_running = false; + bql_lock(); + } - multifd_save_cleanup(); - qemu_mutex_lock(&s->qemu_file_lock); + WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { + /* + * Close the file handle without the lock to make sure the critical + * section won't block for long. + */ tmp = s->to_dst_file; s->to_dst_file = NULL; - qemu_mutex_unlock(&s->qemu_file_lock); + } + + if (tmp) { /* - * Close the file handle without the lock to make sure the - * critical section won't block for long. + * We only need to shutdown multifd if tmp!=NULL, because if + * tmp==NULL, it means the main channel isn't established, while + * multifd is only setup after that (in migration_thread()). */ + multifd_send_shutdown(); migration_ioc_unregister_yank_from_file(tmp); qemu_fclose(tmp); } - if (s->postcopy_qemufile_src) { - migration_ioc_unregister_yank_from_file(s->postcopy_qemufile_src); - qemu_fclose(s->postcopy_qemufile_src); - s->postcopy_qemufile_src = NULL; - } - - assert(!migration_is_active(s)); + assert(!migration_is_active()); if (s->state == MIGRATION_STATUS_CANCELLING) { migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING, @@ -1955,36 +1435,35 @@ 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); 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) { QEMU_LOCK_GUARD(&s->error_mutex); + + trace_migrate_error(error_get_pretty(error)); + if (!s->error) { s->error = error_copy(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); @@ -1994,19 +1473,39 @@ 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)); + MigrationStatus current = s->state; + MigrationStatus next; + assert(s->to_dst_file == NULL); - migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, - MIGRATION_STATUS_FAILED); + + switch (current) { + case MIGRATION_STATUS_SETUP: + next = MIGRATION_STATUS_FAILED; + break; + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: + /* Never fail a postcopy migration; switch back to PAUSED instead */ + next = MIGRATION_STATUS_POSTCOPY_PAUSED; + break; + default: + /* + * This really shouldn't happen. Just be careful to not crash a VM + * just for this. Instead, dump something. + */ + error_report("%s: Illegal migration status (%s) detected", + __func__, MigrationStatus_str(current)); + return; + } + + migrate_set_state(&s->state, current, next); migrate_set_error(s, 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) { @@ -2018,7 +1517,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 */ @@ -2032,11 +1531,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; @@ -2050,24 +1551,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) @@ -2083,6 +1599,7 @@ bool migration_in_postcopy(void) switch (s->state) { case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: case MIGRATION_STATUS_POSTCOPY_RECOVER: return true; default: @@ -2090,9 +1607,15 @@ bool migration_in_postcopy(void) } } -bool migration_in_postcopy_after_devices(MigrationState *s) +bool migration_postcopy_is_alive(MigrationStatus 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) @@ -2102,119 +1625,203 @@ bool migration_in_incoming_postcopy(void) return ps >= POSTCOPY_INCOMING_DISCARD && ps < POSTCOPY_INCOMING_END; } -bool migration_in_bg_snapshot(void) +bool migration_incoming_postcopy_advised(void) { - MigrationState *s = migrate_get_current(); + PostcopyState ps = postcopy_state_get(); - return migrate_background_snapshot() && - migration_is_setup_or_active(s->state); + return ps >= POSTCOPY_INCOMING_ADVISE && ps < POSTCOPY_INCOMING_END; } -bool migration_is_idle(void) +bool migration_in_bg_snapshot(void) +{ + return migrate_background_snapshot() && migration_is_running(); +} + +bool migration_is_active(void) { MigrationState *s = current_migration; - if (!s) { - return true; - } - - switch (s->state) { - case MIGRATION_STATUS_NONE: - case MIGRATION_STATUS_CANCELLED: - case MIGRATION_STATUS_COMPLETED: - case MIGRATION_STATUS_FAILED: - return true; - case MIGRATION_STATUS_SETUP: - case MIGRATION_STATUS_CANCELLING: - case MIGRATION_STATUS_ACTIVE: - case MIGRATION_STATUS_POSTCOPY_ACTIVE: - case MIGRATION_STATUS_COLO: - case MIGRATION_STATUS_PRE_SWITCHOVER: - case MIGRATION_STATUS_DEVICE: - case MIGRATION_STATUS_WAIT_UNPLUG: - return false; - case MIGRATION_STATUS__MAX: - g_assert_not_reached(); - } - - return false; -} - -bool migration_is_active(MigrationState *s) -{ 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_running()) { + 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, + bool has_exit_on_error, bool exit_on_error, + Error **errp) { Error *local_err = NULL; static bool once = true; + MigrationIncomingState *mis = migration_incoming_get_current(); if (!once) { error_setg(errp, "The incoming migration has already been started"); @@ -2229,7 +1836,10 @@ void qmp_migrate_incoming(const char *uri, Error **errp) return; } - qemu_start_incoming_migration(uri, &local_err); + mis->exit_on_error = + has_exit_on_error ? exit_on_error : INMIGRATE_DEFAULT_EXIT_ON_ERROR; + + qemu_start_incoming_migration(uri, has_channels, channels, &local_err); if (local_err) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); @@ -2265,27 +1875,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"); @@ -2294,17 +1920,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; } @@ -2312,11 +1940,8 @@ bool migration_is_blocked(Error **errp) } /* Returns true if continue to migrate, or false if error detected */ -static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, - bool resume, Error **errp) +static bool migrate_prepare(MigrationState *s, bool resume, Error **errp) { - Error *local_err = NULL; - if (resume) { if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { error_setg(errp, "Cannot resume if there is no " @@ -2342,12 +1967,15 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return false; } + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP); + /* This is a resume, skip init status */ return true; } - if (migration_is_running(s->state)) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + if (migration_is_running()) { + error_setg(errp, "There's a migration process in progress"); return false; } @@ -2362,91 +1990,130 @@ 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 (blk || blk_inc) { - if (migrate_colo_enabled()) { - error_setg(errp, "No disk migration is required in COLO mode"); + if (migrate_mapped_ram()) { + if (migrate_tls()) { + error_setg(errp, "Cannot use TLS with mapped-ram"); return false; } - if (migrate_use_block() || migrate_use_block_incremental()) { - error_setg(errp, "Command options are incompatible with " - "current migration capabilities"); + + if (migrate_multifd_compression()) { + error_setg(errp, "Cannot use compression with mapped-ram"); return false; } - migrate_set_block_enabled(true, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return false; - } - s->must_remove_block_options = true; } - if (blk_inc) { - migrate_set_block_incremental(s, true); + 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; + } } - 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, - bool has_inc, bool inc, bool has_detach, bool detach, +void qmp_migrate(const char *uri, bool has_channels, + MigrationChannelList *channels, 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; - if (!migrate_prepare(s, has_blk && blk, has_inc && inc, - has_resume && resume, errp)) { + /* + * 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, 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; } if (local_err) { - if (!(has_resume && resume)) { + if (!resume_requested) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); } migrate_fd_error(s, local_err); @@ -2471,293 +2138,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(); - - assert(s->parameters.multifd_compression < MULTIFD_COMPRESSION__MAX); - 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->enabled_capabilities[MIGRATION_CAPABILITY_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]; -} - -bool migrate_postcopy_preempt(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT]; -} - -/* 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 { @@ -2771,6 +2171,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" }, }; @@ -2779,8 +2180,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(); @@ -2792,50 +2194,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. */ @@ -2843,15 +2232,18 @@ 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; + QEMUFile *file = NULL; WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) { /* @@ -2862,6 +2254,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); } @@ -2878,49 +2282,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_running()) { 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; } @@ -2930,8 +2331,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 @@ -2943,12 +2343,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: @@ -2963,74 +2367,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) { @@ -3039,12 +2443,7 @@ 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", + qemu_thread_create(&ms->rp_state.rp_thread, MIGRATION_THREAD_SRC_RETURN, source_return_path_thread, ms, QEMU_THREAD_JOINABLE); ms->rp_state.rp_thread_created = true; @@ -3053,46 +2452,64 @@ 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 (postcopy_preempt_wait_channel(ms)) { - migrate_set_state(&ms->state, ms->state, MIGRATION_STATUS_FAILED); - return -1; + 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()) { @@ -3101,24 +2518,27 @@ static int postcopy_start(MigrationState *ms) } 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; @@ -3144,12 +2564,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); @@ -3192,7 +2607,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; } @@ -3200,6 +2615,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); @@ -3208,12 +2624,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()) { /* @@ -3229,11 +2644,10 @@ 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; @@ -3254,13 +2668,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, @@ -3288,19 +2703,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. @@ -3309,71 +2801,24 @@ 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) { - 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) { - /* - * 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_enabled(); - qemu_file_set_rate_limit(s->to_dst_file, INT64_MAX); - ret = qemu_savevm_state_complete_precopy(s->to_dst_file, false, - s->block_inactive); - } - } - 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(); - - /* Shutdown the postcopy fast path thread */ - if (migrate_postcopy_preempt()) { - postcopy_preempt_shutdown_file(s); - } - - 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; - } + if (close_return_path_on_source(s)) { + goto fail; } if (qemu_file_get_error(s->to_dst_file)) { @@ -3381,38 +2826,27 @@ static void migration_completion(MigrationState *s) 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: - 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; - - 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(); + 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); } - migrate_set_state(&s->state, current_active_state, - MIGRATION_STATUS_FAILED); + migration_completion_failed(s, current_active_state); } /** @@ -3425,13 +2859,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. @@ -3450,8 +2877,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: @@ -3459,12 +2885,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, @@ -3479,7 +2899,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) { @@ -3505,6 +2927,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) @@ -3530,6 +2966,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 @@ -3549,18 +2992,6 @@ static MigThrError postcopy_pause(MigrationState *s) qemu_file_shutdown(file); qemu_fclose(file); - /* - * Do the same to postcopy fast path socket too if there is. No - * locking needed because no racer as long as we do this before setting - * status to paused. - */ - if (s->postcopy_qemufile_src) { - migration_ioc_unregister_yank_from_file(s->postcopy_qemufile_src); - qemu_file_shutdown(s->postcopy_qemufile_src); - qemu_fclose(s->postcopy_qemufile_src); - s->postcopy_qemufile_src = NULL; - } - migrate_set_state(&s->state, s->state, MIGRATION_STATUS_POSTCOPY_PAUSED); @@ -3571,27 +3002,13 @@ static MigThrError postcopy_pause(MigrationState *s) * We wait until things fixed up. Then someone will setup the * status back for us. */ - while (s->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + do { qemu_sem_wait(&s->postcopy_pause_sem); - } + } while (postcopy_is_paused(s->state)); if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { /* Woken up by a recover procedure. Give it a shot */ - if (postcopy_preempt_wait_channel(s)) { - /* - * Preempt enabled, and new channel create failed; loop - * back to wait for another recovery. - */ - continue; - } - - /* - * 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! */ @@ -3612,6 +3029,19 @@ static MigThrError postcopy_pause(MigrationState *s) } } +void migration_file_set_error(int ret, Error *err) +{ + MigrationState *s = current_migration; + + WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) { + if (s->to_dst_file) { + qemu_file_set_error_obj(s->to_dst_file, ret, err); + } else if (err) { + error_report_err(err); + } + } +} + static MigThrError migration_detect_error(MigrationState *s) { int ret; @@ -3662,32 +3092,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_file_total_transferred(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) @@ -3697,7 +3123,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(); } @@ -3706,17 +3132,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; @@ -3730,16 +3172,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 */ @@ -3755,65 +3215,74 @@ 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; } static void migration_iteration_finish(MigrationState *s) { - /* If we enabled cpu throttling for auto-converge, turn it off. */ - cpu_throttle_stop(); + bql_lock(); + + /* + * If we enabled cpu throttling for auto-converge, turn it off. + * Stopping CPU throttle should be serialized by BQL to avoid + * racing for the throttle_dirty_sync_timer. + */ + if (migrate_auto_converge()) { + cpu_throttle_stop(); + } - qemu_mutex_lock_iothread(); 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; @@ -3823,18 +3292,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: @@ -3847,8 +3321,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(); } /* @@ -3886,7 +3360,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; @@ -3958,16 +3432,27 @@ 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; + Error *local_err = NULL; + int ret; + + thread = migration_threads_add(MIGRATION_THREAD_SRC_MAIN, + 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 @@ -3990,22 +3475,41 @@ 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); } - qemu_savevm_state_setup(s->to_dst_file); + if (migrate_auto_converge()) { + /* Start RAMBlock dirty bitmap sync timer */ + cpu_throttle_dirty_sync_timer(true); + } + + bql_lock(); + ret = qemu_savevm_state_setup(s->to_dst_file, &local_err); + bql_unlock(); qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + /* + * Handle SETUP failures after waiting for virtio-net-failover + * devices to unplug. This to preserve migration state transitions. + */ + if (ret) { + migrate_set_error(s, local_err); + error_free(local_err); + migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); + goto out; + } + s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; 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; @@ -4034,10 +3538,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; } @@ -4045,11 +3551,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); } /** @@ -4074,11 +3577,12 @@ static void *bg_migration_thread(void *opaque) MigThrError thr_error; QEMUFile *fb; bool early_fail = true; + Error *local_err = NULL; + int ret; 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); /* @@ -4105,31 +3609,33 @@ 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); + ret = qemu_savevm_state_setup(s->to_dst_file, &local_err); + bql_unlock(); qemu_savevm_wait_unplug(s, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_ACTIVE); + /* + * Handle SETUP failures after waiting for virtio-net-failover + * devices to unplug. This to preserve migration state transitions. + */ + if (ret) { + migrate_set_error(s, local_err); + error_free(local_err); + migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); + goto fail_setup; + } + 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; } /* @@ -4158,12 +3664,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; @@ -4190,9 +3694,10 @@ fail: if (early_fail) { migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_FAILED); - qemu_mutex_unlock_iothread(); + bql_unlock(); } +fail_setup: bg_migration_iteration_finish(s); qemu_fclose(fb); @@ -4205,8 +3710,9 @@ fail: void migrate_fd_connect(MigrationState *s, Error *error_in) { Error *local_err = NULL; - int64_t rate_limit; - bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; + uint64_t rate_limit; + bool resume = (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP); + int ret; /* * If there's a previous error, free it and prepare for another one. @@ -4215,13 +3721,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) { @@ -4241,17 +3741,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); /* @@ -4259,179 +3760,63 @@ 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 */ - if (postcopy_preempt_setup(s, &local_err)) { - error_report_err(local_err); - migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, - MIGRATION_STATUS_FAILED); - migrate_fd_cleanup(s); - return; + /* + * 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, + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP, MIGRATION_STATUS_POSTCOPY_RECOVER); qemu_sem_post(&s->postcopy_pause_sem); 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; + } } + /* + * Take a refcount to make sure the migration object won't get freed by + * the main thread already in migration_shutdown(). + * + * The refcount will be released at the end of the thread function. + */ + object_ref(OBJECT(s)); + if (migrate_background_snapshot()) { - qemu_thread_create(&s->thread, "bg_snapshot", + qemu_thread_create(&s->thread, MIGRATION_THREAD_SNAPSHOT, bg_migration_thread, s, QEMU_THREAD_JOINABLE); } else { - qemu_thread_create(&s->thread, "live_migration", + qemu_thread_create(&s->thread, MIGRATION_THREAD_SRC_MAIN, 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), - 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_BOOL("x-postcopy-preempt-break-huge", MigrationState, - postcopy_preempt_break_huge, true), - 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), - - /* 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_END_OF_LIST(), -}; - static void migration_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -4450,8 +3835,8 @@ static void migration_instance_finalize(Object *obj) 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); } @@ -4459,7 +3844,6 @@ static void migration_instance_finalize(Object *obj) static void migration_instance_init(Object *obj) { MigrationState *ms = MIGRATION_OBJ(obj); - MigrationParameters *params = &ms->parameters; ms->state = MIGRATION_STATUS_NONE; ms->mbps = -1; @@ -4467,40 +3851,11 @@ 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_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_tls_creds = true; - params->has_tls_hostname = true; - params->has_tls_authz = 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); @@ -4513,27 +3868,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 cdad8aceaa..3857905c0e 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -17,13 +17,30 @@ #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 "qemu/coroutine.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" + +#define MIGRATION_THREAD_SNAPSHOT "mig/snapshot" +#define MIGRATION_THREAD_DIRTY_RATE "mig/dirtyrate" + +#define MIGRATION_THREAD_SRC_MAIN "mig/src/main" +#define MIGRATION_THREAD_SRC_MULTIFD "mig/src/send_%d" +#define MIGRATION_THREAD_SRC_RETURN "mig/src/return" +#define MIGRATION_THREAD_SRC_TLS "mig/src/tls" + +#define MIGRATION_THREAD_DST_COLO "mig/dst/colo" +#define MIGRATION_THREAD_DST_MULTIFD "mig/dst/recv_%d" +#define MIGRATION_THREAD_DST_FAULT "mig/dst/fault" +#define MIGRATION_THREAD_DST_LISTEN "mig/dst/listen" +#define MIGRATION_THREAD_DST_PREEMPT "mig/dst/preempt" struct PostcopyBlocktimeContext; @@ -64,6 +81,12 @@ 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; @@ -115,9 +138,20 @@ struct MigrationIncomingState { 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; - bool postcopy_prio_thread_created; + /* + * 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 @@ -140,14 +174,17 @@ struct MigrationIncomingState { /* PostCopyFD's for external userfaultfds & handlers of shared memory */ GArray *postcopy_remote_fds; - QEMUBH *bh; + MigrationStatus state; - int state; + /* + * 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; - bool have_colo_incoming_thread; - QemuThread colo_incoming_thread; /* The coroutine we should enter (back) after failover */ - Coroutine *migration_incoming_co; + Coroutine *colo_incoming_co; QemuSemaphore colo_incoming_sem; /* @@ -172,7 +209,10 @@ struct MigrationIncomingState { /* 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 @@ -186,6 +226,24 @@ 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; + + /* Do exit on incoming migration failure */ + bool exit_on_error; }; MigrationIncomingState *migration_incoming_get_current(void); @@ -213,8 +271,6 @@ 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 */ @@ -252,21 +308,20 @@ 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; - int state; + MigrationStatus state; /* State related to return path */ struct { /* 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. @@ -274,7 +329,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; @@ -286,19 +353,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; @@ -326,10 +393,6 @@ struct MigrationState { /* mutex to protect errp */ QemuMutex error_mutex; - /* Do we have to clean up -b/-i from old migrate parameters */ - /* This feature is deprecated and will be removed */ - bool must_remove_block_options; - /* * Global switch on whether we need to store the global state * during migration. @@ -340,25 +403,49 @@ struct MigrationState { bool send_configuration; /* Whether we send section footer during migration */ bool send_section_footer; - /* - * Whether we allow break sending huge pages when postcopy preempt is - * enabled. When disabled, we won't interrupt precopy within sending a - * host huge page, which is the old behavior of vanilla postcopy. - * NOTE: this parameter is ignored if postcopy preempt is not enabled. - */ - bool postcopy_preempt_break_huge; /* 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 - * older than 3.0, since only newer qemu sends streams that - * do not trigger spurious decompression errors. + * 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 decompress_error_check; + 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 @@ -373,76 +460,47 @@ 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 migrate_set_state(MigrationStatus *state, MigrationStatus old_state, + MigrationStatus 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(MigrationStatus 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); -bool migrate_postcopy_preempt(void); - /* Sending on the return path - generic and then for each message type */ void migrate_send_rp_shut(MigrationIncomingState *mis, uint32_t value); @@ -455,6 +513,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); @@ -463,7 +522,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 \ @@ -472,12 +532,28 @@ 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); + +void migration_bitmap_sync_precopy(bool last_stage); + +/* migration/block-dirty-bitmap.c */ +void dirty_bitmap_mig_init(void); #endif diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c new file mode 100644 index 0000000000..55191152f9 --- /dev/null +++ b/migration/multifd-nocomp.c @@ -0,0 +1,391 @@ +/* + * Multifd RAM migration without compression + * + * Copyright (c) 2019-2020 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "exec/ramblock.h" +#include "exec/target_page.h" +#include "file.h" +#include "multifd.h" +#include "options.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "trace.h" + +static MultiFDSendData *multifd_ram_send; + +size_t multifd_ram_payload_size(void) +{ + uint32_t n = multifd_ram_page_count(); + + /* + * We keep an array of page offsets at the end of MultiFDPages_t, + * add space for it in the allocation. + */ + return sizeof(MultiFDPages_t) + n * sizeof(ram_addr_t); +} + +void multifd_ram_save_setup(void) +{ + multifd_ram_send = multifd_send_data_alloc(); +} + +void multifd_ram_save_cleanup(void) +{ + g_free(multifd_ram_send); + multifd_ram_send = NULL; +} + +static void multifd_set_file_bitmap(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + + assert(pages->block); + + for (int i = 0; i < pages->normal_num; i++) { + ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], true); + } + + for (int i = pages->normal_num; i < pages->num; i++) { + ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], false); + } +} + +static int multifd_nocomp_send_setup(MultiFDSendParams *p, Error **errp) +{ + uint32_t page_count = multifd_ram_page_count(); + + if (migrate_zero_copy_send()) { + p->write_flags |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; + } + + if (!migrate_mapped_ram()) { + /* We need one extra place for the packet header */ + p->iov = g_new0(struct iovec, page_count + 1); + } else { + p->iov = g_new0(struct iovec, page_count); + } + + return 0; +} + +static void multifd_nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + g_free(p->iov); + p->iov = NULL; + return; +} + +static void multifd_send_prepare_iovs(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t page_size = multifd_ram_page_size(); + + 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 = page_size; + p->iovs_num++; + } + + p->next_packet_size = pages->normal_num * page_size; +} + +static int multifd_nocomp_send_prepare(MultiFDSendParams *p, Error **errp) +{ + bool use_zero_copy_send = migrate_zero_copy_send(); + int ret; + + multifd_send_zero_page_detect(p); + + if (migrate_mapped_ram()) { + multifd_send_prepare_iovs(p); + multifd_set_file_bitmap(p); + + return 0; + } + + 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; +} + +static int multifd_nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + p->iov = g_new0(struct iovec, multifd_ram_page_count()); + return 0; +} + +static void multifd_nocomp_recv_cleanup(MultiFDRecvParams *p) +{ + g_free(p->iov); + p->iov = NULL; +} + +static int multifd_nocomp_recv(MultiFDRecvParams *p, Error **errp) +{ + uint32_t flags; + + if (migrate_mapped_ram()) { + 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 = multifd_ram_page_size(); + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); + } + return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); +} + +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; +} + +void multifd_ram_fill_packet(MultiFDSendParams *p) +{ + MultiFDPacket_t *packet = p->packet; + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t zero_num = pages->num - pages->normal_num; + + packet->pages_alloc = cpu_to_be32(multifd_ram_page_count()); + packet->normal_pages = cpu_to_be32(pages->normal_num); + packet->zero_pages = cpu_to_be32(zero_num); + + if (pages->block) { + pstrcpy(packet->ramblock, sizeof(packet->ramblock), + pages->block->idstr); + } + + for (int i = 0; i < pages->num; i++) { + /* there are architectures where ram_addr_t is 32 bit */ + uint64_t temp = pages->offset[i]; + + packet->offset[i] = cpu_to_be64(temp); + } + + trace_multifd_send_ram_fill(p->id, pages->normal_num, + zero_num); +} + +int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + MultiFDPacket_t *packet = p->packet; + uint32_t page_count = multifd_ram_page_count(); + uint32_t page_size = multifd_ram_page_size(); + uint32_t pages_per_packet = be32_to_cpu(packet->pages_alloc); + int i; + + if (pages_per_packet > page_count) { + error_setg(errp, "multifd: received packet with %u pages, expected %u", + pages_per_packet, page_count); + return -1; + } + + p->normal_num = be32_to_cpu(packet->normal_pages); + if (p->normal_num > pages_per_packet) { + error_setg(errp, "multifd: received packet with %u non-zero pages, " + "which exceeds maximum expected pages %u", + p->normal_num, pages_per_packet); + return -1; + } + + p->zero_num = be32_to_cpu(packet->zero_pages); + if (p->zero_num > pages_per_packet - p->normal_num) { + error_setg(errp, + "multifd: received packet with %u zero pages, expected maximum %u", + p->zero_num, pages_per_packet - p->normal_num); + return -1; + } + + if (p->normal_num == 0 && p->zero_num == 0) { + return 0; + } + + /* make sure that ramblock is 0 terminated */ + packet->ramblock[255] = 0; + 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 = p->block->host; + for (i = 0; i < p->normal_num; i++) { + uint64_t offset = be64_to_cpu(packet->offset[i]); + + if (offset > (p->block->used_length - page_size)) { + error_setg(errp, "multifd: offset too long %" PRIu64 + " (max " RAM_ADDR_FMT ")", + 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 - 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; +} + +static inline bool multifd_queue_empty(MultiFDPages_t *pages) +{ + return pages->num == 0; +} + +static inline bool multifd_queue_full(MultiFDPages_t *pages) +{ + return pages->num == multifd_ram_page_count(); +} + +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_ram_send->u.ram; + + if (multifd_payload_empty(multifd_ram_send)) { + multifd_pages_reset(pages); + multifd_set_payload_type(multifd_ram_send, MULTIFD_PAYLOAD_RAM); + } + + /* If the queue is empty, we can already enqueue now */ + if (multifd_queue_empty(pages)) { + pages->block = block; + multifd_enqueue(pages, offset); + return true; + } + + /* + * 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(&multifd_ram_send)) { + return false; + } + goto retry; + } + + /* Not empty, and we still have space, do it! */ + multifd_enqueue(pages, offset); + return true; +} + +int multifd_ram_flush_and_sync(void) +{ + if (!migrate_multifd()) { + return 0; + } + + if (!multifd_payload_empty(multifd_ram_send)) { + if (!multifd_send(&multifd_ram_send)) { + error_report("%s: multifd_send fail", __func__); + return -1; + } + } + + return multifd_send_sync_main(); +} + +bool multifd_send_prepare_common(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + multifd_send_zero_page_detect(p); + + if (!pages->normal_num) { + p->next_packet_size = 0; + return false; + } + + multifd_send_prepare_header(p); + + return true; +} + +static const MultiFDMethods multifd_nocomp_ops = { + .send_setup = multifd_nocomp_send_setup, + .send_cleanup = multifd_nocomp_send_cleanup, + .send_prepare = multifd_nocomp_send_prepare, + .recv_setup = multifd_nocomp_recv_setup, + .recv_cleanup = multifd_nocomp_recv_cleanup, + .recv = multifd_nocomp_recv +}; + +static void multifd_nocomp_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_NONE, &multifd_nocomp_ops); +} + +migration_init(multifd_nocomp_register); diff --git a/migration/multifd-qatzip.c b/migration/multifd-qatzip.c new file mode 100644 index 0000000000..7b68397625 --- /dev/null +++ b/migration/multifd-qatzip.c @@ -0,0 +1,394 @@ +/* + * Multifd QATzip compression implementation + * + * Copyright (c) Bytedance + * + * Authors: + * Bryan Zhang + * Hao Xiang + * Yichen Wang + * + * 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 "qapi/error.h" +#include "qemu/error-report.h" +#include "qapi/qapi-types-migration.h" +#include "options.h" +#include "multifd.h" +#include + +typedef struct { + /* + * Unique session for use with QATzip API + */ + QzSession_T sess; + + /* + * For compression: Buffer for pages to compress + * For decompression: Buffer for data to decompress + */ + uint8_t *in_buf; + uint32_t in_len; + + /* + * For compression: Output buffer of compressed data + * For decompression: Output buffer of decompressed data + */ + uint8_t *out_buf; + uint32_t out_len; +} QatzipData; + +/** + * qatzip_send_setup: Set up QATzip session and private buffers. + * + * @param p Multifd channel params + * @param errp Pointer to error, which will be set in case of error + * @return 0 on success, -1 on error (and *errp will be set) + */ +static int qatzip_send_setup(MultiFDSendParams *p, Error **errp) +{ + QatzipData *q; + QzSessionParamsDeflate_T params; + const char *err_msg; + int ret; + + q = g_new0(QatzipData, 1); + p->compress_data = q; + /* We need one extra place for the packet header */ + p->iov = g_new0(struct iovec, 2); + + /* + * Initialize QAT device with software fallback by default. This allows + * QATzip to use CPU path when QAT hardware reaches maximum throughput. + */ + ret = qzInit(&q->sess, true); + if (ret != QZ_OK && ret != QZ_DUPLICATE) { + err_msg = "qzInit failed"; + goto err; + } + + ret = qzGetDefaultsDeflate(¶ms); + if (ret != QZ_OK) { + err_msg = "qzGetDefaultsDeflate failed"; + goto err; + } + + /* Make sure to use configured QATzip compression level. */ + params.common_params.comp_lvl = migrate_multifd_qatzip_level(); + ret = qzSetupSessionDeflate(&q->sess, ¶ms); + if (ret != QZ_OK && ret != QZ_DUPLICATE) { + err_msg = "qzSetupSessionDeflate failed"; + goto err; + } + + if (MULTIFD_PACKET_SIZE > UINT32_MAX) { + err_msg = "packet size too large for QAT"; + goto err; + } + + q->in_len = MULTIFD_PACKET_SIZE; + /* + * PINNED_MEM is an enum from qatzip headers, which means to use + * kzalloc_node() to allocate memory for QAT DMA purposes. When QAT device + * is not available or software fallback is used, the malloc flag needs to + * be set as COMMON_MEM. + */ + q->in_buf = qzMalloc(q->in_len, 0, PINNED_MEM); + if (!q->in_buf) { + q->in_buf = qzMalloc(q->in_len, 0, COMMON_MEM); + if (!q->in_buf) { + err_msg = "qzMalloc failed"; + goto err; + } + } + + q->out_len = qzMaxCompressedLength(MULTIFD_PACKET_SIZE, &q->sess); + q->out_buf = qzMalloc(q->out_len, 0, PINNED_MEM); + if (!q->out_buf) { + q->out_buf = qzMalloc(q->out_len, 0, COMMON_MEM); + if (!q->out_buf) { + err_msg = "qzMalloc failed"; + goto err; + } + } + + return 0; + +err: + error_setg(errp, "multifd %u: [sender] %s", p->id, err_msg); + return -1; +} + +/** + * qatzip_send_cleanup: Tear down QATzip session and release private buffers. + * + * @param p Multifd channel params + * @param errp Pointer to error, which will be set in case of error + * @return None + */ +static void qatzip_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + QatzipData *q = p->compress_data; + + if (q) { + if (q->in_buf) { + qzFree(q->in_buf); + } + if (q->out_buf) { + qzFree(q->out_buf); + } + (void)qzTeardownSession(&q->sess); + (void)qzClose(&q->sess); + g_free(q); + } + + g_free(p->iov); + p->iov = NULL; + p->compress_data = NULL; +} + +/** + * qatzip_send_prepare: Compress pages and update IO channel info. + * + * @param p Multifd channel params + * @param errp Pointer to error, which will be set in case of error + * @return 0 on success, -1 on error (and *errp will be set) + */ +static int qatzip_send_prepare(MultiFDSendParams *p, Error **errp) +{ + uint32_t page_size = multifd_ram_page_size(); + MultiFDPages_t *pages = &p->data->u.ram; + QatzipData *q = p->compress_data; + int ret; + unsigned int in_len, out_len; + + if (!multifd_send_prepare_common(p)) { + goto out; + } + + /* + * Unlike other multifd compression implementations, we use a non-streaming + * API and place all the data into one buffer, rather than sending each + * page to the compression API at a time. Based on initial benchmarks, the + * non-streaming API outperforms the streaming API. Plus, the logic in QEMU + * is friendly to using the non-streaming API anyway. If either of these + * statements becomes no longer true, we can revisit adding a streaming + * implementation. + */ + for (int i = 0; i < pages->normal_num; i++) { + memcpy(q->in_buf + (i * page_size), + pages->block->host + pages->offset[i], + page_size); + } + + in_len = pages->normal_num * page_size; + if (in_len > q->in_len) { + error_setg(errp, "multifd %u: unexpectedly large input", p->id); + return -1; + } + out_len = q->out_len; + + ret = qzCompress(&q->sess, q->in_buf, &in_len, q->out_buf, &out_len, 1); + if (ret != QZ_OK) { + error_setg(errp, "multifd %u: QATzip returned %d instead of QZ_OK", + p->id, ret); + return -1; + } + if (in_len != pages->normal_num * page_size) { + error_setg(errp, "multifd %u: QATzip failed to compress all input", + p->id); + return -1; + } + + p->iov[p->iovs_num].iov_base = q->out_buf; + p->iov[p->iovs_num].iov_len = out_len; + p->iovs_num++; + p->next_packet_size = out_len; + +out: + p->flags |= MULTIFD_FLAG_QATZIP; + multifd_send_fill_packet(p); + return 0; +} + +/** + * qatzip_recv_setup: Set up QATzip session and allocate private buffers. + * + * @param p Multifd channel params + * @param errp Pointer to error, which will be set in case of error + * @return 0 on success, -1 on error (and *errp will be set) + */ +static int qatzip_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + QatzipData *q; + QzSessionParamsDeflate_T params; + const char *err_msg; + int ret; + + q = g_new0(QatzipData, 1); + p->compress_data = q; + + /* + * Initialize QAT device with software fallback by default. This allows + * QATzip to use CPU path when QAT hardware reaches maximum throughput. + */ + ret = qzInit(&q->sess, true); + if (ret != QZ_OK && ret != QZ_DUPLICATE) { + err_msg = "qzInit failed"; + goto err; + } + + ret = qzGetDefaultsDeflate(¶ms); + if (ret != QZ_OK) { + err_msg = "qzGetDefaultsDeflate failed"; + goto err; + } + + ret = qzSetupSessionDeflate(&q->sess, ¶ms); + if (ret != QZ_OK && ret != QZ_DUPLICATE) { + err_msg = "qzSetupSessionDeflate failed"; + goto err; + } + + /* + * Reserve extra spaces for the incoming packets. Current implementation + * doesn't send uncompressed pages in case the compression gets too big. + */ + q->in_len = MULTIFD_PACKET_SIZE * 2; + /* + * PINNED_MEM is an enum from qatzip headers, which means to use + * kzalloc_node() to allocate memory for QAT DMA purposes. When QAT device + * is not available or software fallback is used, the malloc flag needs to + * be set as COMMON_MEM. + */ + q->in_buf = qzMalloc(q->in_len, 0, PINNED_MEM); + if (!q->in_buf) { + q->in_buf = qzMalloc(q->in_len, 0, COMMON_MEM); + if (!q->in_buf) { + err_msg = "qzMalloc failed"; + goto err; + } + } + + q->out_len = MULTIFD_PACKET_SIZE; + q->out_buf = qzMalloc(q->out_len, 0, PINNED_MEM); + if (!q->out_buf) { + q->out_buf = qzMalloc(q->out_len, 0, COMMON_MEM); + if (!q->out_buf) { + err_msg = "qzMalloc failed"; + goto err; + } + } + + return 0; + +err: + error_setg(errp, "multifd %u: [receiver] %s", p->id, err_msg); + return -1; +} + +/** + * qatzip_recv_cleanup: Tear down QATzip session and release private buffers. + * + * @param p Multifd channel params + * @return None + */ +static void qatzip_recv_cleanup(MultiFDRecvParams *p) +{ + QatzipData *q = p->compress_data; + + if (q) { + if (q->in_buf) { + qzFree(q->in_buf); + } + if (q->out_buf) { + qzFree(q->out_buf); + } + (void)qzTeardownSession(&q->sess); + (void)qzClose(&q->sess); + g_free(q); + } + p->compress_data = NULL; +} + + +/** + * qatzip_recv: Decompress pages and copy them to the appropriate + * locations. + * + * @param p Multifd channel params + * @param errp Pointer to error, which will be set in case of error + * @return 0 on success, -1 on error (and *errp will be set) + */ +static int qatzip_recv(MultiFDRecvParams *p, Error **errp) +{ + QatzipData *q = p->compress_data; + int ret; + unsigned int in_len, out_len; + uint32_t in_size = p->next_packet_size; + uint32_t page_size = multifd_ram_page_size(); + uint32_t expected_size = p->normal_num * page_size; + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + + if (in_size > q->in_len) { + error_setg(errp, "multifd %u: received unexpectedly large packet", + p->id); + return -1; + } + + if (flags != MULTIFD_FLAG_QATZIP) { + error_setg(errp, "multifd %u: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_QATZIP); + 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 *)q->in_buf, in_size, errp); + if (ret != 0) { + return ret; + } + + in_len = in_size; + out_len = q->out_len; + ret = qzDecompress(&q->sess, q->in_buf, &in_len, q->out_buf, &out_len); + if (ret != QZ_OK) { + error_setg(errp, "multifd %u: qzDecompress failed", p->id); + return -1; + } + if (out_len != expected_size) { + error_setg(errp, "multifd %u: packet size received %u size expected %u", + p->id, out_len, expected_size); + return -1; + } + + /* Copy each page to its appropriate location. */ + for (int i = 0; i < p->normal_num; i++) { + memcpy(p->host + p->normal[i], q->out_buf + page_size * i, page_size); + } + return 0; +} + +static MultiFDMethods multifd_qatzip_ops = { + .send_setup = qatzip_send_setup, + .send_cleanup = qatzip_send_cleanup, + .send_prepare = qatzip_send_prepare, + .recv_setup = qatzip_recv_setup, + .recv_cleanup = qatzip_recv_cleanup, + .recv = qatzip_recv +}; + +static void multifd_qatzip_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_QATZIP, &multifd_qatzip_ops); +} + +migration_init(multifd_qatzip_register); diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c new file mode 100644 index 0000000000..bbe466617f --- /dev/null +++ b/migration/multifd-qpl.c @@ -0,0 +1,711 @@ +/* + * Multifd qpl compression accelerator implementation + * + * Copyright (c) 2023 Intel Corporation + * + * Authors: + * Yuan Liu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "qapi/qapi-types-migration.h" +#include "exec/ramblock.h" +#include "multifd.h" +#include "qpl/qpl.h" + +/* Maximum number of retries to resubmit a job if IAA work queues are full */ +#define MAX_SUBMIT_RETRY_NUM (3) + +typedef struct { + /* the QPL hardware path job */ + qpl_job *job; + /* indicates if fallback to software path is required */ + bool fallback_sw_path; + /* output data from the software path */ + uint8_t *sw_output; + /* output data length from the software path */ + uint32_t sw_output_len; +} QplHwJob; + +typedef struct { + /* array of hardware jobs, the number of jobs equals the number pages */ + QplHwJob *hw_jobs; + /* the QPL software job for the slow path and software fallback */ + qpl_job *sw_job; + /* the number of pages that the QPL needs to process at one time */ + uint32_t page_num; + /* array of compressed page buffers */ + uint8_t *zbuf; + /* array of compressed page lengths */ + uint32_t *zlen; + /* the status of the hardware device */ + bool hw_avail; +} QplData; + +/** + * check_hw_avail: check if IAA hardware is available + * + * If the IAA hardware does not exist or is unavailable, + * the QPL hardware job initialization will fail. + * + * Returns true if IAA hardware is available, otherwise false. + * + * @job_size: indicates the hardware job size if hardware is available + */ +static bool check_hw_avail(uint32_t *job_size) +{ + qpl_path_t path = qpl_path_hardware; + uint32_t size = 0; + qpl_job *job; + + if (qpl_get_job_size(path, &size) != QPL_STS_OK) { + return false; + } + assert(size > 0); + job = g_malloc0(size); + if (qpl_init_job(path, job) != QPL_STS_OK) { + g_free(job); + return false; + } + g_free(job); + *job_size = size; + return true; +} + +/** + * multifd_qpl_free_sw_job: clean up software job + * + * Free the software job resources. + * + * @qpl: pointer to the QplData structure + */ +static void multifd_qpl_free_sw_job(QplData *qpl) +{ + assert(qpl); + if (qpl->sw_job) { + qpl_fini_job(qpl->sw_job); + g_free(qpl->sw_job); + qpl->sw_job = NULL; + } +} + +/** + * multifd_qpl_free_jobs: clean up hardware jobs + * + * Free all hardware job resources. + * + * @qpl: pointer to the QplData structure + */ +static void multifd_qpl_free_hw_job(QplData *qpl) +{ + assert(qpl); + if (qpl->hw_jobs) { + for (int i = 0; i < qpl->page_num; i++) { + qpl_fini_job(qpl->hw_jobs[i].job); + g_free(qpl->hw_jobs[i].job); + qpl->hw_jobs[i].job = NULL; + } + g_free(qpl->hw_jobs); + qpl->hw_jobs = NULL; + } +} + +/** + * multifd_qpl_init_sw_job: initialize a software job + * + * Use the QPL software path to initialize a job + * + * @qpl: pointer to the QplData structure + * @errp: pointer to an error + */ +static int multifd_qpl_init_sw_job(QplData *qpl, Error **errp) +{ + qpl_path_t path = qpl_path_software; + uint32_t size = 0; + qpl_job *job = NULL; + qpl_status status; + + status = qpl_get_job_size(path, &size); + if (status != QPL_STS_OK) { + error_setg(errp, "qpl_get_job_size failed with error %d", status); + return -1; + } + job = g_malloc0(size); + status = qpl_init_job(path, job); + if (status != QPL_STS_OK) { + error_setg(errp, "qpl_init_job failed with error %d", status); + g_free(job); + return -1; + } + qpl->sw_job = job; + return 0; +} + +/** + * multifd_qpl_init_jobs: initialize hardware jobs + * + * Use the QPL hardware path to initialize jobs + * + * @qpl: pointer to the QplData structure + * @size: the size of QPL hardware path job + * @errp: pointer to an error + */ +static void multifd_qpl_init_hw_job(QplData *qpl, uint32_t size, Error **errp) +{ + qpl_path_t path = qpl_path_hardware; + qpl_job *job = NULL; + qpl_status status; + + qpl->hw_jobs = g_new0(QplHwJob, qpl->page_num); + for (int i = 0; i < qpl->page_num; i++) { + job = g_malloc0(size); + status = qpl_init_job(path, job); + /* the job initialization should succeed after check_hw_avail */ + assert(status == QPL_STS_OK); + qpl->hw_jobs[i].job = job; + } +} + +/** + * multifd_qpl_init: initialize QplData structure + * + * Allocate and initialize a QplData structure + * + * Returns a QplData pointer on success or NULL on error + * + * @num: the number of pages + * @size: the page size + * @errp: pointer to an error + */ +static QplData *multifd_qpl_init(uint32_t num, uint32_t size, Error **errp) +{ + uint32_t job_size = 0; + QplData *qpl; + + qpl = g_new0(QplData, 1); + qpl->page_num = num; + if (multifd_qpl_init_sw_job(qpl, errp) != 0) { + g_free(qpl); + return NULL; + } + qpl->hw_avail = check_hw_avail(&job_size); + if (qpl->hw_avail) { + multifd_qpl_init_hw_job(qpl, job_size, errp); + } + qpl->zbuf = g_malloc0(size * num); + qpl->zlen = g_new0(uint32_t, num); + return qpl; +} + +/** + * multifd_qpl_deinit: clean up QplData structure + * + * Free jobs, buffers and the QplData structure + * + * @qpl: pointer to the QplData structure + */ +static void multifd_qpl_deinit(QplData *qpl) +{ + if (qpl) { + multifd_qpl_free_sw_job(qpl); + multifd_qpl_free_hw_job(qpl); + g_free(qpl->zbuf); + g_free(qpl->zlen); + g_free(qpl); + } +} + +static int multifd_qpl_send_setup(MultiFDSendParams *p, Error **errp) +{ + QplData *qpl; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); + + qpl = multifd_qpl_init(page_count, page_size, errp); + if (!qpl) { + return -1; + } + p->compress_data = qpl; + + /* + * the page will be compressed independently and sent using an IOV. The + * additional two IOVs are used to store packet header and compressed data + * length + */ + p->iov = g_new0(struct iovec, page_count + 2); + return 0; +} + +static void multifd_qpl_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + multifd_qpl_deinit(p->compress_data); + p->compress_data = NULL; + g_free(p->iov); + p->iov = NULL; +} + +/** + * multifd_qpl_prepare_job: prepare the job + * + * Set the QPL job parameters and properties. + * + * @job: pointer to the qpl_job structure + * @is_compression: indicates compression and decompression + * @input: pointer to the input data buffer + * @input_len: the length of the input data + * @output: pointer to the output data buffer + * @output_len: the length of the output data + */ +static void multifd_qpl_prepare_job(qpl_job *job, bool is_compression, + uint8_t *input, uint32_t input_len, + uint8_t *output, uint32_t output_len) +{ + job->op = is_compression ? qpl_op_compress : qpl_op_decompress; + job->next_in_ptr = input; + job->next_out_ptr = output; + job->available_in = input_len; + job->available_out = output_len; + job->flags = QPL_FLAG_FIRST | QPL_FLAG_LAST | QPL_FLAG_OMIT_VERIFY; + /* only supports compression level 1 */ + job->level = 1; +} + +/** + * multifd_qpl_prepare_comp_job: prepare the compression job + * + * Set the compression job parameters and properties. + * + * @job: pointer to the qpl_job structure + * @input: pointer to the input data buffer + * @output: pointer to the output data buffer + * @size: the page size + */ +static void multifd_qpl_prepare_comp_job(qpl_job *job, uint8_t *input, + uint8_t *output, uint32_t size) +{ + /* + * Set output length to less than the page size to force the job to + * fail in case it compresses to a larger size. We'll send that page + * without compression and skip the decompression operation on the + * destination. + */ + multifd_qpl_prepare_job(job, true, input, size, output, size - 1); +} + +/** + * multifd_qpl_prepare_decomp_job: prepare the decompression job + * + * Set the decompression job parameters and properties. + * + * @job: pointer to the qpl_job structure + * @input: pointer to the input data buffer + * @len: the length of the input data + * @output: pointer to the output data buffer + * @size: the page size + */ +static void multifd_qpl_prepare_decomp_job(qpl_job *job, uint8_t *input, + uint32_t len, uint8_t *output, + uint32_t size) +{ + multifd_qpl_prepare_job(job, false, input, len, output, size); +} + +/** + * multifd_qpl_fill_iov: fill in the IOV + * + * Fill in the QPL packet IOV + * + * @p: Params for the channel being used + * @data: pointer to the IOV data + * @len: The length of the IOV data + */ +static void multifd_qpl_fill_iov(MultiFDSendParams *p, uint8_t *data, + uint32_t len) +{ + p->iov[p->iovs_num].iov_base = data; + p->iov[p->iovs_num].iov_len = len; + p->iovs_num++; + p->next_packet_size += len; +} + +/** + * multifd_qpl_fill_packet: fill the compressed page into the QPL packet + * + * Fill the compressed page length and IOV into the QPL packet + * + * @idx: The index of the compressed length array + * @p: Params for the channel being used + * @data: pointer to the compressed page buffer + * @len: The length of the compressed page + */ +static void multifd_qpl_fill_packet(uint32_t idx, MultiFDSendParams *p, + uint8_t *data, uint32_t len) +{ + QplData *qpl = p->compress_data; + + qpl->zlen[idx] = cpu_to_be32(len); + multifd_qpl_fill_iov(p, data, len); +} + +/** + * multifd_qpl_submit_job: submit a job to the hardware + * + * Submit a QPL hardware job to the IAA device + * + * Returns true if the job is submitted successfully, otherwise false. + * + * @job: pointer to the qpl_job structure + */ +static bool multifd_qpl_submit_job(qpl_job *job) +{ + qpl_status status; + uint32_t num = 0; + +retry: + status = qpl_submit_job(job); + if (status == QPL_STS_QUEUES_ARE_BUSY_ERR) { + if (num < MAX_SUBMIT_RETRY_NUM) { + num++; + goto retry; + } + } + return (status == QPL_STS_OK); +} + +/** + * multifd_qpl_compress_pages_slow_path: compress pages using slow path + * + * Compress the pages using software. If compression fails, the uncompressed + * page will be sent. + * + * @p: Params for the channel being used + */ +static void multifd_qpl_compress_pages_slow_path(MultiFDSendParams *p) +{ + QplData *qpl = p->compress_data; + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t size = multifd_ram_page_size(); + qpl_job *job = qpl->sw_job; + uint8_t *zbuf = qpl->zbuf; + uint8_t *buf; + + for (int i = 0; i < pages->normal_num; i++) { + buf = pages->block->host + pages->offset[i]; + multifd_qpl_prepare_comp_job(job, buf, zbuf, size); + if (qpl_execute_job(job) == QPL_STS_OK) { + multifd_qpl_fill_packet(i, p, zbuf, job->total_out); + } else { + /* send the uncompressed page */ + multifd_qpl_fill_packet(i, p, buf, size); + } + zbuf += size; + } +} + +/** + * multifd_qpl_compress_pages: compress pages + * + * Submit the pages to the IAA hardware for compression. If hardware + * compression fails, it falls back to software compression. If software + * compression also fails, the uncompressed page is sent. + * + * @p: Params for the channel being used + */ +static void multifd_qpl_compress_pages(MultiFDSendParams *p) +{ + QplData *qpl = p->compress_data; + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t size = multifd_ram_page_size(); + QplHwJob *hw_job; + uint8_t *buf; + uint8_t *zbuf; + + for (int i = 0; i < pages->normal_num; i++) { + buf = pages->block->host + pages->offset[i]; + zbuf = qpl->zbuf + (size * i); + hw_job = &qpl->hw_jobs[i]; + multifd_qpl_prepare_comp_job(hw_job->job, buf, zbuf, size); + if (multifd_qpl_submit_job(hw_job->job)) { + hw_job->fallback_sw_path = false; + } else { + /* + * The IAA work queue is full, any immediate subsequent job + * submission is likely to fail, sending the page via the QPL + * software path at this point gives us a better chance of + * finding the queue open for the next pages. + */ + hw_job->fallback_sw_path = true; + multifd_qpl_prepare_comp_job(qpl->sw_job, buf, zbuf, size); + if (qpl_execute_job(qpl->sw_job) == QPL_STS_OK) { + hw_job->sw_output = zbuf; + hw_job->sw_output_len = qpl->sw_job->total_out; + } else { + hw_job->sw_output = buf; + hw_job->sw_output_len = size; + } + } + } + + for (int i = 0; i < pages->normal_num; i++) { + buf = pages->block->host + pages->offset[i]; + zbuf = qpl->zbuf + (size * i); + hw_job = &qpl->hw_jobs[i]; + if (hw_job->fallback_sw_path) { + multifd_qpl_fill_packet(i, p, hw_job->sw_output, + hw_job->sw_output_len); + continue; + } + if (qpl_wait_job(hw_job->job) == QPL_STS_OK) { + multifd_qpl_fill_packet(i, p, zbuf, hw_job->job->total_out); + } else { + /* send the uncompressed page */ + multifd_qpl_fill_packet(i, p, buf, size); + } + } +} + +static int multifd_qpl_send_prepare(MultiFDSendParams *p, Error **errp) +{ + QplData *qpl = p->compress_data; + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t len = 0; + + if (!multifd_send_prepare_common(p)) { + goto out; + } + + /* The first IOV is used to store the compressed page lengths */ + len = pages->normal_num * sizeof(uint32_t); + multifd_qpl_fill_iov(p, (uint8_t *) qpl->zlen, len); + if (qpl->hw_avail) { + multifd_qpl_compress_pages(p); + } else { + multifd_qpl_compress_pages_slow_path(p); + } + +out: + p->flags |= MULTIFD_FLAG_QPL; + multifd_send_fill_packet(p); + return 0; +} + +static int multifd_qpl_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + QplData *qpl; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); + + qpl = multifd_qpl_init(page_count, page_size, errp); + if (!qpl) { + return -1; + } + p->compress_data = qpl; + return 0; +} + +static void multifd_qpl_recv_cleanup(MultiFDRecvParams *p) +{ + multifd_qpl_deinit(p->compress_data); + p->compress_data = NULL; +} + +/** + * multifd_qpl_process_and_check_job: process and check a QPL job + * + * Process the job and check whether the job output length is the + * same as the specified length + * + * Returns true if the job execution succeeded and the output length + * is equal to the specified length, otherwise false. + * + * @job: pointer to the qpl_job structure + * @is_hardware: indicates whether the job is a hardware job + * @len: Specified output length + * @errp: pointer to an error + */ +static bool multifd_qpl_process_and_check_job(qpl_job *job, bool is_hardware, + uint32_t len, Error **errp) +{ + qpl_status status; + + status = (is_hardware ? qpl_wait_job(job) : qpl_execute_job(job)); + if (status != QPL_STS_OK) { + error_setg(errp, "qpl job failed with error %d", status); + return false; + } + if (job->total_out != len) { + error_setg(errp, "qpl decompressed len %u, expected len %u", + job->total_out, len); + return false; + } + return true; +} + +/** + * multifd_qpl_decompress_pages_slow_path: decompress pages using slow path + * + * Decompress the pages using software + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_decompress_pages_slow_path(MultiFDRecvParams *p, + Error **errp) +{ + QplData *qpl = p->compress_data; + uint32_t size = multifd_ram_page_size(); + qpl_job *job = qpl->sw_job; + uint8_t *zbuf = qpl->zbuf; + uint8_t *addr; + uint32_t len; + + for (int i = 0; i < p->normal_num; i++) { + len = qpl->zlen[i]; + addr = p->host + p->normal[i]; + /* the page is uncompressed, load it */ + if (len == size) { + memcpy(addr, zbuf, size); + zbuf += size; + continue; + } + multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size); + if (!multifd_qpl_process_and_check_job(job, false, size, errp)) { + return -1; + } + zbuf += len; + } + return 0; +} + +/** + * multifd_qpl_decompress_pages: decompress pages + * + * Decompress the pages using the IAA hardware. If hardware + * decompression fails, it falls back to software decompression. + * + * Returns 0 on success or -1 on error + * + * @p: Params for the channel being used + * @errp: pointer to an error + */ +static int multifd_qpl_decompress_pages(MultiFDRecvParams *p, Error **errp) +{ + QplData *qpl = p->compress_data; + uint32_t size = multifd_ram_page_size(); + uint8_t *zbuf = qpl->zbuf; + uint8_t *addr; + uint32_t len; + qpl_job *job; + + for (int i = 0; i < p->normal_num; i++) { + addr = p->host + p->normal[i]; + len = qpl->zlen[i]; + /* the page is uncompressed if received length equals the page size */ + if (len == size) { + memcpy(addr, zbuf, size); + zbuf += size; + continue; + } + + job = qpl->hw_jobs[i].job; + multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size); + if (multifd_qpl_submit_job(job)) { + qpl->hw_jobs[i].fallback_sw_path = false; + } else { + /* + * The IAA work queue is full, any immediate subsequent job + * submission is likely to fail, sending the page via the QPL + * software path at this point gives us a better chance of + * finding the queue open for the next pages. + */ + qpl->hw_jobs[i].fallback_sw_path = true; + job = qpl->sw_job; + multifd_qpl_prepare_decomp_job(job, zbuf, len, addr, size); + if (!multifd_qpl_process_and_check_job(job, false, size, errp)) { + return -1; + } + } + zbuf += len; + } + + for (int i = 0; i < p->normal_num; i++) { + /* ignore pages that have already been processed */ + if (qpl->zlen[i] == size || qpl->hw_jobs[i].fallback_sw_path) { + continue; + } + + job = qpl->hw_jobs[i].job; + if (!multifd_qpl_process_and_check_job(job, true, size, errp)) { + return -1; + } + } + return 0; +} +static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) +{ + QplData *qpl = p->compress_data; + uint32_t in_size = p->next_packet_size; + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + uint32_t len = 0; + uint32_t zbuf_len = 0; + int ret; + + if (flags != MULTIFD_FLAG_QPL) { + error_setg(errp, "multifd %u: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_QPL); + return -1; + } + multifd_recv_zero_page_process(p); + if (!p->normal_num) { + assert(in_size == 0); + return 0; + } + + /* read compressed page lengths */ + len = p->normal_num * sizeof(uint32_t); + assert(len < in_size); + ret = qio_channel_read_all(p->c, (void *) qpl->zlen, len, errp); + if (ret != 0) { + return ret; + } + for (int i = 0; i < p->normal_num; i++) { + qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]); + assert(qpl->zlen[i] <= multifd_ram_page_size()); + zbuf_len += qpl->zlen[i]; + } + + /* read compressed pages */ + assert(in_size == len + zbuf_len); + ret = qio_channel_read_all(p->c, (void *) qpl->zbuf, zbuf_len, errp); + if (ret != 0) { + return ret; + } + + if (qpl->hw_avail) { + return multifd_qpl_decompress_pages(p, errp); + } + return multifd_qpl_decompress_pages_slow_path(p, errp); +} + +static const MultiFDMethods multifd_qpl_ops = { + .send_setup = multifd_qpl_send_setup, + .send_cleanup = multifd_qpl_send_cleanup, + .send_prepare = multifd_qpl_send_prepare, + .recv_setup = multifd_qpl_recv_setup, + .recv_cleanup = multifd_qpl_recv_cleanup, + .recv = multifd_qpl_recv, +}; + +static void multifd_qpl_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_QPL, &multifd_qpl_ops); +} + +migration_init(multifd_qpl_register); diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c new file mode 100644 index 0000000000..6e6a290ae9 --- /dev/null +++ b/migration/multifd-uadk.c @@ -0,0 +1,323 @@ +/* + * Multifd UADK compression accelerator implementation + * + * Copyright (c) 2024 Huawei Technologies R & D (UK) Ltd + * + * Authors: + * Shameer Kolothum + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qapi/error.h" +#include "exec/ramblock.h" +#include "migration.h" +#include "multifd.h" +#include "options.h" +#include "qemu/error-report.h" +#include "uadk/wd_comp.h" +#include "uadk/wd_sched.h" + +struct wd_data { + handle_t handle; + uint8_t *buf; + uint32_t *buf_hdr; +}; + +static bool uadk_hw_init(void) +{ + char alg[] = "zlib"; + int ret; + + ret = wd_comp_init2(alg, SCHED_POLICY_RR, TASK_HW); + if (ret && ret != -WD_EEXIST) { + return false; + } else { + return true; + } +} + +static struct wd_data *multifd_uadk_init_sess(uint32_t count, + uint32_t page_size, + bool compress, Error **errp) +{ + struct wd_comp_sess_setup ss = {0}; + struct sched_params param = {0}; + uint32_t size = count * page_size; + struct wd_data *wd; + + wd = g_new0(struct wd_data, 1); + + if (uadk_hw_init()) { + ss.alg_type = WD_ZLIB; + if (compress) { + ss.op_type = WD_DIR_COMPRESS; + /* Add an additional page for handling output > input */ + size += page_size; + } else { + ss.op_type = WD_DIR_DECOMPRESS; + } + /* We use default level 1 compression and 4K window size */ + param.type = ss.op_type; + ss.sched_param = ¶m; + + wd->handle = wd_comp_alloc_sess(&ss); + if (!wd->handle) { + error_setg(errp, "multifd: failed wd_comp_alloc_sess"); + goto out; + } + } else { + /* For CI test use */ + warn_report_once("UADK hardware not available. Switch to no compression mode"); + } + + wd->buf = g_try_malloc(size); + if (!wd->buf) { + error_setg(errp, "multifd: out of mem for uadk buf"); + goto out_free_sess; + } + wd->buf_hdr = g_new0(uint32_t, count); + return wd; + +out_free_sess: + if (wd->handle) { + wd_comp_free_sess(wd->handle); + } +out: + wd_comp_uninit2(); + g_free(wd); + return NULL; +} + +static void multifd_uadk_uninit_sess(struct wd_data *wd) +{ + if (wd->handle) { + wd_comp_free_sess(wd->handle); + } + wd_comp_uninit2(); + g_free(wd->buf); + g_free(wd->buf_hdr); + g_free(wd); +} + +static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp) +{ + struct wd_data *wd; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); + + wd = multifd_uadk_init_sess(page_count, page_size, true, errp); + if (!wd) { + return -1; + } + + p->compress_data = wd; + assert(p->iov == NULL); + /* + * Each page will be compressed independently and sent using an IOV. The + * additional two IOVs are used to store packet header and compressed data + * length + */ + + p->iov = g_new0(struct iovec, page_count + 2); + return 0; +} + +static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + struct wd_data *wd = p->compress_data; + + multifd_uadk_uninit_sess(wd); + p->compress_data = NULL; + g_free(p->iov); + p->iov = NULL; +} + +static inline void prepare_next_iov(MultiFDSendParams *p, void *base, + uint32_t len) +{ + p->iov[p->iovs_num].iov_base = (uint8_t *)base; + p->iov[p->iovs_num].iov_len = len; + p->next_packet_size += len; + p->iovs_num++; +} + +static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) +{ + struct wd_data *uadk_data = p->compress_data; + uint32_t hdr_size; + uint32_t page_size = multifd_ram_page_size(); + uint8_t *buf = uadk_data->buf; + int ret = 0; + MultiFDPages_t *pages = &p->data->u.ram; + + if (!multifd_send_prepare_common(p)) { + goto out; + } + + hdr_size = pages->normal_num * sizeof(uint32_t); + /* prepare the header that stores the lengths of all compressed data */ + prepare_next_iov(p, uadk_data->buf_hdr, hdr_size); + + for (int i = 0; i < pages->normal_num; i++) { + struct wd_comp_req creq = { + .op_type = WD_DIR_COMPRESS, + .src = pages->block->host + pages->offset[i], + .src_len = page_size, + .dst = buf, + /* Set dst_len to double the src in case compressed out >= page_size */ + .dst_len = p->page_size * 2, + }; + + if (uadk_data->handle) { + ret = wd_do_comp_sync(uadk_data->handle, &creq); + if (ret || creq.status) { + error_setg(errp, "multifd %u: failed compression, ret %d status %d", + p->id, ret, creq.status); + return -1; + } + if (creq.dst_len < page_size) { + uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len); + prepare_next_iov(p, buf, creq.dst_len); + buf += creq.dst_len; + } + } + /* + * Send raw data if no UADK hardware or if compressed out >= page_size. + * We might be better off sending raw data if output is slightly less + * than page_size as well because at the receive end we can skip the + * decompression. But it is tricky to find the right number here. + */ + if (!uadk_data->handle || creq.dst_len >= page_size) { + uadk_data->buf_hdr[i] = cpu_to_be32(page_size); + prepare_next_iov(p, pages->block->host + pages->offset[i], + page_size); + buf += page_size; + } + } +out: + p->flags |= MULTIFD_FLAG_UADK; + multifd_send_fill_packet(p); + return 0; +} + +static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + struct wd_data *wd; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); + + wd = multifd_uadk_init_sess(page_count, page_size, false, errp); + if (!wd) { + return -1; + } + p->compress_data = wd; + return 0; +} + +static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p) +{ + struct wd_data *wd = p->compress_data; + + multifd_uadk_uninit_sess(wd); + p->compress_data = NULL; +} + +static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) +{ + struct wd_data *uadk_data = p->compress_data; + uint32_t in_size = p->next_packet_size; + uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + uint32_t hdr_len = p->normal_num * sizeof(uint32_t); + uint32_t data_len = 0; + uint32_t page_size = multifd_ram_page_size(); + uint8_t *buf = uadk_data->buf; + int ret = 0; + + if (flags != MULTIFD_FLAG_UADK) { + error_setg(errp, "multifd %u: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_ZLIB); + return -1; + } + + multifd_recv_zero_page_process(p); + if (!p->normal_num) { + assert(in_size == 0); + return 0; + } + + /* read compressed data lengths */ + assert(hdr_len < in_size); + ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr, + hdr_len, errp); + if (ret != 0) { + return ret; + } + + for (int i = 0; i < p->normal_num; i++) { + uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); + data_len += uadk_data->buf_hdr[i]; + assert(uadk_data->buf_hdr[i] <= page_size); + } + + /* read compressed data */ + assert(in_size == hdr_len + data_len); + ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp); + if (ret != 0) { + return ret; + } + + for (int i = 0; i < p->normal_num; i++) { + struct wd_comp_req creq = { + .op_type = WD_DIR_DECOMPRESS, + .src = buf, + .src_len = uadk_data->buf_hdr[i], + .dst = p->host + p->normal[i], + .dst_len = page_size, + }; + + if (uadk_data->buf_hdr[i] == page_size) { + memcpy(p->host + p->normal[i], buf, page_size); + buf += page_size; + continue; + } + + if (unlikely(!uadk_data->handle)) { + error_setg(errp, "multifd %u: UADK HW not available for decompression", + p->id); + return -1; + } + + ret = wd_do_comp_sync(uadk_data->handle, &creq); + if (ret || creq.status) { + error_setg(errp, "multifd %u: failed decompression, ret %d status %d", + p->id, ret, creq.status); + return -1; + } + if (creq.dst_len != page_size) { + error_setg(errp, "multifd %u: decompressed length error", p->id); + return -1; + } + buf += uadk_data->buf_hdr[i]; + } + + return 0; +} + +static const MultiFDMethods multifd_uadk_ops = { + .send_setup = multifd_uadk_send_setup, + .send_cleanup = multifd_uadk_send_cleanup, + .send_prepare = multifd_uadk_send_prepare, + .recv_setup = multifd_uadk_recv_setup, + .recv_cleanup = multifd_uadk_recv_cleanup, + .recv = multifd_uadk_recv, +}; + +static void multifd_uadk_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_UADK, &multifd_uadk_ops); +} +migration_init(multifd_uadk_register); diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c new file mode 100644 index 0000000000..f1e988a959 --- /dev/null +++ b/migration/multifd-zero-page.c @@ -0,0 +1,94 @@ +/* + * 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 "migration-stats.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->data->u.ram; + RAMBlock *rb = pages->block; + int i = 0; + int j = pages->num - 1; + + if (!multifd_zero_page_enabled()) { + pages->normal_num = pages->num; + goto out; + } + + /* + * 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, multifd_ram_page_size())) { + i++; + continue; + } + + swap_page_offset(pages->offset, i, j); + ram_release_page(rb->idstr, offset); + j--; + } + + pages->normal_num = i; + +out: + stat64_add(&mig_stats.normal_pages, pages->normal_num); + stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num); +} + +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 (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) { + memset(page, 0, multifd_ram_page_size()); + } else { + ramblock_recv_bitmap_set_offset(p->block, p->zero[i]); + } + } +} diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 18213a9513..8cf8a26bb4 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 { @@ -33,17 +34,7 @@ struct zlib_data { /* Multifd zlib compression */ -/** - * zlib_send_setup: setup send side - * - * Setup each channel with zlib compression. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_send_setup(MultiFDSendParams *p, Error **errp) +static int multifd_zlib_send_setup(MultiFDSendParams *p, Error **errp) { struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; @@ -56,7 +47,7 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) 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) { @@ -68,65 +59,58 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) err_msg = "out of memory for buf"; goto err_free_zbuff; } - p->data = z; + p->compress_data = z; + + /* Needs 2 IOVs, one for packet header and one for compressed data */ + p->iov = g_new0(struct iovec, 2); + return 0; err_free_zbuff: g_free(z->zbuff); err_deflate_end: - deflateEnd(&z->zs); + deflateEnd(zs); err_free_z: g_free(z); error_setg(errp, "multifd %u: %s", p->id, err_msg); return -1; } -/** - * zlib_send_cleanup: cleanup send side - * - * Close the channel and return memory. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) +static void multifd_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(z->buf); z->buf = NULL; - g_free(p->data); - p->data = NULL; + g_free(p->compress_data); + p->compress_data = NULL; + + g_free(p->iov); + p->iov = NULL; } -/** - * zlib_send_prepare: prepare date to be able to send - * - * Create a compressed buffer with all the pages that we are going to - * send. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) +static int multifd_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->data->u.ram; + struct zlib_data *z = p->compress_data; z_stream *zs = &z->zs; uint32_t out_size = 0; + uint32_t page_size = multifd_ram_page_size(); 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; } @@ -135,7 +119,7 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) * with compression. zlib does not guarantee that this is safe, * therefore copy the page before calling deflate(). */ - memcpy(z->buf, p->pages->block->host + p->normal[i], page_size); + memcpy(z->buf, pages->block->host + pages->offset[i], page_size); zs->avail_in = page_size; zs->next_in = z->buf; @@ -169,27 +153,19 @@ 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; } -/** - * zlib_recv_setup: setup receive side - * - * Create the compressed channel and buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) +static int multifd_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; @@ -210,43 +186,25 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) return 0; } -/** - * zlib_recv_cleanup: setup receive side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - */ -static void zlib_recv_cleanup(MultiFDRecvParams *p) +static void multifd_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 - * - * Read the compressed buffer, and uncompress it into the actual - * pages. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) +static int multifd_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 page_size = multifd_ram_page_size(); uint32_t expected_size = p->normal_num * page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; int ret; @@ -257,6 +215,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) { @@ -270,6 +236,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) int flush = Z_NO_FLUSH; unsigned long start = zs->total_out; + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); if (i == p->normal_num - 1) { flush = Z_SYNC_FLUSH; } @@ -306,16 +273,17 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) p->id, out_size, expected_size); return -1; } + return 0; } -static MultiFDMethods multifd_zlib_ops = { - .send_setup = zlib_send_setup, - .send_cleanup = zlib_send_cleanup, - .send_prepare = zlib_send_prepare, - .recv_setup = zlib_recv_setup, - .recv_cleanup = zlib_recv_cleanup, - .recv_pages = zlib_recv_pages +static const MultiFDMethods multifd_zlib_ops = { + .send_setup = multifd_zlib_send_setup, + .send_cleanup = multifd_zlib_send_cleanup, + .send_prepare = multifd_zlib_send_prepare, + .recv_setup = multifd_zlib_recv_setup, + .recv_cleanup = multifd_zlib_recv_cleanup, + .recv = multifd_zlib_recv }; static void multifd_zlib_register(void) diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index d788d309f2..abed140855 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 { @@ -36,22 +37,11 @@ struct zstd_data { /* Multifd zstd compression */ -/** - * zstd_send_setup: setup send side - * - * Setup each channel with zstd compression. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_send_setup(MultiFDSendParams *p, Error **errp) +static int multifd_zstd_send_setup(MultiFDSendParams *p, Error **errp) { struct zstd_data *z = g_new0(struct zstd_data, 1); int res; - p->data = z; z->zcs = ZSTD_createCStream(); if (!z->zcs) { g_free(z); @@ -67,7 +57,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) { @@ -76,59 +66,51 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) error_setg(errp, "multifd %u: out of memory for zbuff", p->id); return -1; } + p->compress_data = z; + + /* Needs 2 IOVs, one for packet header and one for compressed data */ + p->iov = g_new0(struct iovec, 2); return 0; } -/** - * zstd_send_cleanup: cleanup send side - * - * Close the channel and return memory. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) +static void multifd_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; + + g_free(p->iov); + p->iov = NULL; } -/** - * zstd_send_prepare: prepare date to be able to send - * - * Create a compressed buffer with all the pages that we are going to - * send. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) +static int multifd_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->data->u.ram; + 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 = pages->block->host + pages->offset[i]; + z->in.size = multifd_ram_page_size(); z->in.pos = 0; /* @@ -141,9 +123,9 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) */ do { ret = ZSTD_compressStream2(z->zcs, &z->out, &z->in, flush); - } while (ret > 0 && (z->in.size - z->in.pos > 0) - && (z->out.size - z->out.pos > 0)); - if (ret > 0 && (z->in.size - z->in.pos > 0)) { + } while (ret > 0 && (z->in.size > z->in.pos) + && (z->out.size > z->out.pos)); + if (ret > 0 && (z->in.size > z->in.pos)) { error_setg(errp, "multifd %u: compressStream buffer too small", p->id); return -1; @@ -158,27 +140,19 @@ 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; } -/** - * zstd_recv_setup: setup receive side - * - * Create the compressed channel and buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) +static int multifd_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); @@ -207,44 +181,26 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) return 0; } -/** - * zstd_recv_cleanup: setup receive side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - */ -static void zstd_recv_cleanup(MultiFDRecvParams *p) +static void multifd_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 - * - * Read the compressed buffer, and uncompress it into the actual - * pages. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) +static int multifd_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 page_size = multifd_ram_page_size(); uint32_t expected_size = p->normal_num * 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 +209,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) { @@ -264,6 +228,7 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) z->in.pos = 0; for (i = 0; i < p->normal_num; i++) { + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); z->out.dst = p->host + p->normal[i]; z->out.size = page_size; z->out.pos = 0; @@ -278,7 +243,7 @@ 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) + } while (ret > 0 && (z->in.size > z->in.pos) && (z->out.pos < page_size)); if (ret > 0 && (z->out.pos < page_size)) { error_setg(errp, "multifd %u: decompressStream buffer too small", @@ -300,13 +265,13 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) return 0; } -static MultiFDMethods multifd_zstd_ops = { - .send_setup = zstd_send_setup, - .send_cleanup = zstd_send_cleanup, - .send_prepare = zstd_send_prepare, - .recv_setup = zstd_recv_setup, - .recv_cleanup = zstd_recv_cleanup, - .recv_pages = zstd_recv_pages +static const MultiFDMethods multifd_zstd_ops = { + .send_setup = multifd_zstd_send_setup, + .send_cleanup = multifd_zstd_send_cleanup, + .send_prepare = multifd_zstd_send_prepare, + .recv_setup = multifd_zstd_recv_setup, + .recv_cleanup = multifd_zstd_recv_cleanup, + .recv = multifd_zstd_recv }; static void multifd_zstd_register(void) diff --git a/migration/multifd.c b/migration/multifd.c index 509bbbe3bf..498e71fd10 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,138 +47,100 @@ typedef struct { uint64_t unused2[4]; /* Reserved for future use */ } __attribute__((packed)) MultiFDInit_t; -/* Multifd without compression */ +struct { + MultiFDSendParams *params; + /* + * 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 */ + const MultiFDMethods *ops; +} *multifd_send_state; -/** - * 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) +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 */ + const MultiFDMethods *ops; +} *multifd_recv_state; + +MultiFDSendData *multifd_send_data_alloc(void) { - return 0; + size_t max_payload_size, size_minus_payload; + + /* + * MultiFDPages_t has a flexible array at the end, account for it + * when allocating MultiFDSendData. Use max() in case other types + * added to the union in the future are larger than + * (MultiFDPages_t + flex array). + */ + max_payload_size = MAX(multifd_ram_payload_size(), sizeof(MultiFDPayload)); + + /* + * Account for any holes the compiler might insert. We can't pack + * the structure because that misaligns the members and triggers + * Waddress-of-packed-member. + */ + size_minus_payload = sizeof(MultiFDSendData) - sizeof(MultiFDPayload); + + return g_malloc0(size_minus_payload + max_payload_size); } -/** - * nocomp_send_cleanup: cleanup send side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) +static bool multifd_use_packets(void) { - return; + return !migrate_mapped_ram(); } -/** - * nocomp_send_prepare: prepare date to be able to send - * - * For no compression we just have to calculate the size of the - * packet. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) +void multifd_send_channel_created(void) { - MultiFDPages_t *pages = p->pages; - size_t page_size = qemu_target_page_size(); - - 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++; - } - - p->next_packet_size = p->normal_num * page_size; - p->flags |= MULTIFD_FLAG_NOCOMP; - return 0; + qemu_sem_post(&multifd_send_state->channels_created); } -/** - * nocomp_recv_setup: setup receive side - * - * For no compression this function does nothing. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) +static const MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = {}; + +void multifd_register_ops(int method, const MultiFDMethods *ops) { - return 0; -} - -/** - * nocomp_recv_cleanup: setup receive side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - */ -static void nocomp_recv_cleanup(MultiFDRecvParams *p) -{ -} - -/** - * nocomp_recv_pages: read the data from the channel into actual pages - * - * For no compression we just need to read things into the correct place. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp) -{ - uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; - size_t page_size = qemu_target_page_size(); - - if (flags != MULTIFD_FLAG_NOCOMP) { - error_setg(errp, "multifd %u: flags received %x flags expected %x", - p->id, flags, MULTIFD_FLAG_NOCOMP); - return -1; - } - 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; - } - return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); -} - -static MultiFDMethods multifd_nocomp_ops = { - .send_setup = nocomp_send_setup, - .send_cleanup = nocomp_send_cleanup, - .send_prepare = nocomp_send_prepare, - .recv_setup = nocomp_recv_setup, - .recv_cleanup = nocomp_recv_cleanup, - .recv_pages = nocomp_recv_pages -}; - -static MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = { - [MULTIFD_COMPRESSION_NONE] = &multifd_nocomp_ops, -}; - -void multifd_register_ops(int method, MultiFDMethods *ops) -{ - assert(0 < method && method < MULTIFD_COMPRESSION__MAX); + assert(0 <= method && method < MULTIFD_COMPRESSION__MAX); + assert(!multifd_ops[method]); multifd_ops[method] = ops; } 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 +148,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,185 +193,125 @@ 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) -{ - MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); - - pages->allocated = size; - pages->offset = g_new0(ram_addr_t, size); - - return pages; -} - -static void multifd_pages_clear(MultiFDPages_t *pages) -{ - pages->num = 0; - 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; - int i; + uint64_t packet_num; + bool sync_packet = p->flags & MULTIFD_FLAG_SYNC; + + memset(packet, 0, p->packet_len); + + packet->magic = cpu_to_be32(MULTIFD_MAGIC); + packet->version = cpu_to_be32(MULTIFD_VERSION); 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->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); + + p->packets_sent++; + + if (!sync_packet) { + multifd_ram_fill_packet(p); } - for (i = 0; i < p->normal_num; i++) { - /* there are architectures where ram_addr_t is 32 bit */ - uint64_t temp = p->normal[i]; - - packet->offset[i] = cpu_to_be64(temp); - } + trace_multifd_send_fill(p->id, packet_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; + const MultiFDPacket_t *packet = p->packet; + uint32_t magic = be32_to_cpu(packet->magic); + uint32_t version = be32_to_cpu(packet->version); + int ret = 0; - packet->magic = be32_to_cpu(packet->magic); - if (packet->magic != MULTIFD_MAGIC) { - error_setg(errp, "multifd: received packet " - "magic %x and expected magic %x", - packet->magic, MULTIFD_MAGIC); + if (magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet magic %x, expected %x", + magic, MULTIFD_MAGIC); return -1; } - packet->version = be32_to_cpu(packet->version); - if (packet->version != MULTIFD_VERSION) { - error_setg(errp, "multifd: received packet " - "version %u and expected version %u", - packet->version, MULTIFD_VERSION); + if (version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet version %u, expected %u", + version, MULTIFD_VERSION); return -1; } p->flags = be32_to_cpu(packet->flags); - - packet->pages_alloc = be32_to_cpu(packet->pages_alloc); - /* - * If we received a packet that is 100 times bigger than expected - * just stop migration. It is a magic number. - */ - if (packet->pages_alloc > page_count) { - error_setg(errp, "multifd: received packet " - "with size %u and expected a size of %u", - packet->pages_alloc, 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", - p->normal_num, packet->pages_alloc) ; - return -1; - } - p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); + p->packets_recved++; - if (p->normal_num == 0) { - return 0; + if (!(p->flags & MULTIFD_FLAG_SYNC)) { + ret = multifd_ram_unfill_packet(p, errp); } - /* make sure that ramblock is 0 terminated */ - packet->ramblock[255] = 0; - block = qemu_ram_block_by_name(packet->ramblock); - if (!block) { - error_setg(errp, "multifd: unknown ram block %s", - packet->ramblock); - return -1; - } + trace_multifd_recv_unfill(p->id, p->packet_num, p->flags, + p->next_packet_size); - p->host = 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)) { - error_setg(errp, "multifd: offset too long %" PRIu64 - " (max " RAM_ADDR_FMT ")", - offset, block->used_length); - return -1; - } - p->normal[i] = offset; - } - - return 0; + return ret; } -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); +} /* - * How we use multifd_send_state->pages and channel->pages? - * - * We create a pages for each channel, and a main one. Each time that - * we need to send a batch of pages we interchange the ones between - * multifd_send_state and the channel that is sending it. There are - * two reasons for that: - * - to not have to do so many mallocs during migration - * - to make easier to know what to free at the end of migration - * - * This way we always know who is the owner of each "pages" struct, - * and we don't need any locking. It belongs to the migration thread - * or to the channel thread. Switching is safe because the migration - * thread is using the channel mutex when changing it, and the channel - * have to had finish with its own, otherwise pending_job can't be - * false. + * 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); +} -static int multifd_send_pages(QEMUFile *f) +/* + * multifd_send() works by exchanging the MultiFDSendData object + * provided by the caller with an unused MultiFDSendData object from + * the next channel that is found to be idle. + * + * The channel owns the data until it finishes transmitting and the + * caller owns the empty object until it fills it with data and calls + * this function again. No locking necessary. + * + * Switching is safe because both the migration thread and the channel + * thread have barriers in place to serialize access. + * + * Returns true if succeed, false otherwise. + */ +bool multifd_send(MultiFDSendData **send_data) { int i; static int next_channel; MultiFDSendParams *p = NULL; /* make happy gcc */ - MultiFDPages_t *pages = multifd_send_state->pages; - uint64_t transferred; + MultiFDSendData *tmp; - 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,72 +319,59 @@ 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++; - multifd_send_state->pages = p->pages; - p->pages = pages; - transferred = ((uint64_t) pages->num) * qemu_target_page_size() - + p->packet_len; - qemu_file_acct_rate_limit(f, transferred); - ram_counters.multifd_bytes += transferred; - ram_counters.transferred += transferred; - qemu_mutex_unlock(&p->mutex); + /* + * Make sure we read p->pending_job before all the rest. Pairs with + * qatomic_store_release() in multifd_send_thread(). + */ + smp_mb_acquire(); + + assert(multifd_payload_empty(p->data)); + + /* + * Swap the pointers. The channel gets the client data for + * transferring and the client gets back an unused data slot. + */ + tmp = *send_data; + *send_data = p->data; + p->data = tmp; + + /* + * Making sure p->data 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) +/* Multifd send side hit an error; remember it and prepare to quit */ +static void multifd_send_set_error(Error *err) { - MultiFDPages_t *pages = multifd_send_state->pages; - - if (!pages->block) { - pages->block = block; + /* + * 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 (pages->block == block) { - pages->offset[pages->num] = offset; - pages->num++; - - if (pages->num < pages->allocated) { - return 1; - } - } - - if (multifd_send_pages(f) < 0) { - return -1; - } - - if (pages->block != block) { - return multifd_queue_page(f, block, offset); - } - - return 1; -} - -static void multifd_send_terminate_threads(Error *err) -{ - int i; - - trace_multifd_send_terminate_threads(err != NULL); - if (err) { MigrationState *s = migrate_get_current(); migrate_set_error(s, err); @@ -489,81 +383,127 @@ 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); } } +} + +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_sem_destroy(&p->sem); + qemu_sem_destroy(&p->sem_sync); + g_free(p->name); + p->name = NULL; + g_free(p->data); + p->data = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = NULL; + multifd_send_state->ops->send_cleanup(p, errp); + assert(!p->iov); + + 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; + g_free(multifd_send_state); + multifd_send_state = NULL; +} + +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 (p->registered_yank) { - migration_ioc_unregister_yank(p->c); - } - socket_send_channel_destroy(p->c); - p->c = NULL; - qemu_mutex_destroy(&p->mutex); - qemu_sem_destroy(&p->sem); - qemu_sem_destroy(&p->sem_sync); - g_free(p->name); - p->name = NULL; - multifd_pages_clear(p->pages); - p->pages = NULL; - p->packet_len = 0; - g_free(p->packet); - p->packet = NULL; - 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) { + if (!multifd_send_cleanup_channel(p, &local_err)) { migrate_set_error(migrate_get_current(), local_err); error_free(local_err); } } - qemu_sem_destroy(&multifd_send_state->channels_ready); - g_free(multifd_send_state->params); - multifd_send_state->params = NULL; - multifd_pages_clear(multifd_send_state->pages); - multifd_send_state->pages = NULL; - g_free(multifd_send_state); - multifd_send_state = NULL; + + multifd_send_cleanup_state(); } static int multifd_zero_copy_flush(QIOChannel *c) @@ -577,72 +517,51 @@ static int multifd_zero_copy_flush(QIOChannel *c) return -1; } if (ret == 1) { - dirty_sync_missed_zero_copy(); + stat64_add(&mig_stats.dirty_sync_missed_zero_copy, 1); } return ret; } -int multifd_send_sync_main(QEMUFile *f) +int multifd_send_sync_main(void) { int i; bool flush_zero_copy; - if (!migrate_use_multifd()) { - return 0; - } - if (multifd_send_state->pages->num) { - if (multifd_send_pages(f) < 0) { - 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_acct_rate_limit(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); + } + 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; } } - for (i = 0; i < migrate_multifd_channels(); i++) { - MultiFDSendParams *p = &multifd_send_state->params[i]; - - trace_multifd_send_sync_main_wait(p->id); - qemu_sem_wait(&p->sem_sync); - } trace_multifd_send_sync_main(multifd_send_state->packet_num); return 0; @@ -651,337 +570,369 @@ 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; - - if (use_zero_copy_send) { - p->iovs_num = 0; - } else { - p->iovs_num = 1; - } - - for (int i = 0; i < p->pages->num; i++) { - p->normal[p->normal_num] = p->pages->offset[i]; - p->normal_num++; - } - - 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); + /* + * Read pending_job flag before p->data. Pairs with the + * qatomic_store_release() in multifd_send(). + */ + if (qatomic_load_acquire(&p->pending_job)) { 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); + p->iovs_num = 0; + assert(!multifd_payload_empty(p->data)); - trace_multifd_send(p->id, packet_num, p->normal_num, flags, - p->next_packet_size); + ret = multifd_send_state->ops->send_prepare(p, &local_err); + if (ret != 0) { + break; + } - if (use_zero_copy_send) { - /* Send header first, without zerocopy */ + if (migrate_mapped_ram()) { + ret = file_write_ramblock_iov(p->c, p->iov, p->iovs_num, + &p->data->u.ram, &local_err); + } else { + ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, + NULL, 0, p->write_flags, + &local_err); + } + + if (ret != 0) { + break; + } + + stat64_add(&mig_stats.multifd_bytes, + (uint64_t)p->next_packet_size + p->packet_len); + + p->next_packet_size = 0; + multifd_set_payload_type(p->data, MULTIFD_PAYLOAD_NONE); + + /* + * Making sure p->data is published before saying "we're + * free". Pairs with the smp_mb_acquire() in + * multifd_send(). + */ + 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); } - 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); 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, MIGRATION_THREAD_SRC_TLS, + 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_channel_requires_tls_upgrade(ioc)) { - 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; - uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + 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(); + int thread_count, ret = 0; + uint32_t page_count = multifd_ram_page_count(); + 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()]; for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; + Error *local_err = NULL; - 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); + p->data = multifd_send_data_alloc(); - if (migrate_use_zero_copy_send()) { - p->write_flags = QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; - } else { - p->write_flags = 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(MIGRATION_THREAD_SRC_MULTIFD, i); + p->write_flags = 0; - socket_send_channel_create(multifd_new_send_channel_async, p); + if (!multifd_new_send_channel_create(p, &local_err)) { + migrate_set_error(s, local_err); + ret = -1; + } + } + + /* + * 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); + } + + if (ret) { + goto err; } 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; + migrate_set_error(s, local_err); + goto err; } + assert(p->iov); } - return 0; + + return true; + +err: + migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, + MIGRATION_STATUS_FAILED); + return false; } -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) { @@ -989,6 +940,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); @@ -1002,8 +957,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 @@ -1013,73 +989,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->data); + p->data = NULL; + g_free(p->name); + p->name = NULL; + p->packet_len = 0; + g_free(p->packet); + p->packet = 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) { @@ -1097,53 +1119,88 @@ 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; + if (!(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->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); } } @@ -1151,33 +1208,37 @@ 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); 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(); + uint32_t page_count = multifd_ram_page_count(); + 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()]; @@ -1186,24 +1247,29 @@ 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->name = g_strdup_printf("multifdrecv_%d", i); - p->iov = g_new0(struct iovec, page_count); + + 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(MIGRATION_THREAD_DST_MULTIFD, i); p->normal = g_new0(ram_addr_t, page_count); + p->zero = g_new0(ram_addr_t, page_count); } 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; } } @@ -1214,7 +1280,7 @@ bool multifd_recv_all_channels_created(void) { int thread_count = migrate_multifd_channels(); - if (!migrate_use_multifd()) { + if (!migrate_multifd()) { return true; } @@ -1228,26 +1294,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) { @@ -1255,17 +1324,13 @@ 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(); } diff --git a/migration/multifd.h b/migration/multifd.h index 519f498643..50d58c0c9c 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -13,25 +13,38 @@ #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 "exec/target_page.h" +#include "ram.h" + +typedef struct MultiFDRecvData MultiFDRecvData; +typedef struct MultiFDSendData MultiFDSendData; + +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) -/* We reserve 3 bits for compression methods */ -#define MULTIFD_FLAG_COMPRESSION_MASK (7 << 1) +/* We reserve 5 bits for compression methods */ +#define MULTIFD_FLAG_COMPRESSION_MASK (0x1f << 1) /* we need to be compatible. Before compression value was 0 */ #define MULTIFD_FLAG_NOCOMP (0 << 1) #define MULTIFD_FLAG_ZLIB (1 << 1) #define MULTIFD_FLAG_ZSTD (2 << 1) +#define MULTIFD_FLAG_QPL (4 << 1) +#define MULTIFD_FLAG_UADK (8 << 1) +#define MULTIFD_FLAG_QATZIP (16 << 1) /* This value needs to be a multiple of qemu_target_page_size() */ #define MULTIFD_PACKET_SIZE (512 * 1024) @@ -47,23 +60,61 @@ 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 allocated pages */ - uint32_t allocated; - /* global number of generated multifd packets */ - uint64_t packet_num; - /* offset of each page */ - ram_addr_t *offset; + /* number of normal pages */ + uint32_t normal_num; RAMBlock *block; + /* offset of each page */ + ram_addr_t offset[]; } MultiFDPages_t; +struct MultiFDRecvData { + void *opaque; + size_t size; + /* for preadv */ + off_t file_offset; +}; + +typedef enum { + MULTIFD_PAYLOAD_NONE, + MULTIFD_PAYLOAD_RAM, +} MultiFDPayloadType; + +typedef union MultiFDPayload { + MultiFDPages_t ram; +} MultiFDPayload; + +struct MultiFDSendData { + MultiFDPayloadType type; + MultiFDPayload u; +}; + +static inline bool multifd_payload_empty(MultiFDSendData *data) +{ + return data->type == MULTIFD_PAYLOAD_NONE; +} + +static inline void multifd_set_payload_type(MultiFDSendData *data, + MultiFDPayloadType type) +{ + data->type = type; +} + typedef struct { /* Fields are only written at creating/deletion time */ /* No lock required for them, they are read only */ @@ -74,10 +125,11 @@ typedef struct { char *name; /* channel thread id */ QemuThread thread; + bool thread_created; + QemuThread tls_thread; + bool tls_thread_created; /* communication channel */ QIOChannel *c; - /* is the yank function registered */ - bool registered_yank; /* packet allocated len */ uint32_t packet_len; /* multifd flags for sending ram */ @@ -88,24 +140,20 @@ typedef struct { /* syncs main thread and channels */ QemuSemaphore sem_sync; - /* this mutex protects the following parameters */ - QemuMutex mutex; - /* is this channel thread running */ - bool running; - /* 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 has work to do */ - int pending_job; - /* 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. + /* + * 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. */ - MultiFDPages_t *pages; + bool pending_job; + bool pending_sync; + MultiFDSendData *data; /* thread local variables. No locking required */ @@ -114,19 +162,13 @@ typedef struct { /* size of the next packet that contains pages */ uint32_t next_packet_size; /* packets sent through this channel */ - uint64_t num_packets; - /* non zero pages sent through this channel */ - uint64_t total_normal_pages; + uint64_t packets_sent; /* 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 { @@ -139,6 +181,7 @@ typedef struct { char *name; /* channel thread id */ QemuThread thread; + bool thread_created; /* communication channel */ QIOChannel *c; /* packet allocated len */ @@ -146,17 +189,19 @@ typedef struct { /* 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; - /* is this channel thread running */ - bool running; /* should this thread finish */ bool quit; /* multifd flags for each packet */ uint32_t flags; /* global number of generated multifd packets */ uint64_t packet_num; + int pending_job; + MultiFDRecvData *data; /* thread local variables. No locking required */ @@ -164,38 +209,136 @@ typedef struct { 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; /* 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 { - /* Setup for sending side */ + /* + * The send_setup, send_cleanup, send_prepare are only called on + * the QEMU instance at the migration source. + */ + + /* + * Setup for sending side. Called once per channel during channel + * setup phase. + * + * Must allocate p->iov. If packets are in use (default), one + * extra iovec must be allocated for the packet header. Any memory + * allocated in this hook must be released at send_cleanup. + * + * p->write_flags may be used for passing flags to the QIOChannel. + * + * p->compression_data may be used by compression methods to store + * compression data. + */ int (*send_setup)(MultiFDSendParams *p, Error **errp); - /* Cleanup for sending side */ + + /* + * Cleanup for sending side. Called once per channel during + * channel cleanup phase. + */ void (*send_cleanup)(MultiFDSendParams *p, Error **errp); - /* Prepare the send packet */ + + /* + * Prepare the send packet. Called as a result of multifd_send() + * on the client side, with p pointing to the MultiFDSendParams of + * a channel that is currently idle. + * + * Must populate p->iov with the data to be sent, increment + * p->iovs_num to match the amount of iovecs used and set + * p->next_packet_size with the amount of data currently present + * in p->iov. + * + * Must indicate whether this is a compression packet by setting + * p->flags. + * + * As a last step, if packets are in use (default), must prepare + * the packet by calling multifd_send_fill_packet(). + */ int (*send_prepare)(MultiFDSendParams *p, Error **errp); - /* Setup for receiving side */ + + /* + * The recv_setup, recv_cleanup, recv are only called on the QEMU + * instance at the migration destination. + */ + + /* + * Setup for receiving side. Called once per channel during + * channel setup phase. May be empty. + * + * May allocate data structures for the receiving of data. May use + * p->iov. Compression methods may use p->compress_data. + */ int (*recv_setup)(MultiFDRecvParams *p, Error **errp); - /* Cleanup for receiving side */ + + /* + * Cleanup for receiving side. Called once per channel during + * channel cleanup phase. May be empty. + */ void (*recv_cleanup)(MultiFDRecvParams *p); - /* Read all pages */ - int (*recv_pages)(MultiFDRecvParams *p, Error **errp); + + /* + * Data receive method. Called as a result of multifd_recv() on + * the client side, with p pointing to the MultiFDRecvParams of a + * channel that is currently idle. Only called if there is data + * available to receive. + * + * Must validate p->flags according to what was set at + * send_prepare. + * + * Must read the data from the QIOChannel p->c. + */ + int (*recv)(MultiFDRecvParams *p, Error **errp); } MultiFDMethods; -void multifd_register_ops(int method, MultiFDMethods *ops); +void multifd_register_ops(int method, const 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); +bool multifd_send(MultiFDSendData **send_data); +MultiFDSendData *multifd_send_data_alloc(void); + +static inline uint32_t multifd_ram_page_size(void) +{ + return qemu_target_page_size(); +} + +static inline uint32_t multifd_ram_page_count(void) +{ + return MULTIFD_PACKET_SIZE / qemu_target_page_size(); +} + +void multifd_ram_save_setup(void); +void multifd_ram_save_cleanup(void); +int multifd_ram_flush_and_sync(void); +size_t multifd_ram_payload_size(void); +void multifd_ram_fill_packet(MultiFDSendParams *p); +int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); #endif - diff --git a/migration/options.c b/migration/options.c new file mode 100644 index 0000000000..ad8d6989a8 --- /dev/null +++ b/migration/options.c @@ -0,0 +1,1424 @@ +/* + * 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 + +/* 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 +/* + * 1: best speed, ... 9: best compress ratio + * There is some nuance here. Refer to QATzip documentation to understand + * the mapping of QATzip levels to standard deflate levels. + */ +#define DEFAULT_MIGRATE_MULTIFD_QATZIP_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("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-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-qatzip-level", MigrationState, + parameters.multifd_qatzip_level, + DEFAULT_MIGRATE_MULTIFD_QATZIP_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-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-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_colo(void) +{ + MigrationState *s = migrate_get_current(); + + return s->capabilities[MIGRATION_CAPABILITY_X_COLO]; +} + +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_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_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(); + + if (new_caps[MIGRATION_CAPABILITY_ZERO_BLOCKS]) { + warn_report("zero-blocks capability is deprecated"); + } + +#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_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; + } + + if (migrate_incoming_started()) { + error_setg(errp, + "Postcopy preempt must be set before incoming starts"); + return false; + } + } + + if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { + 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_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_POSTCOPY_RAM]) { + error_setg(errp, + "Mapped-ram migration is incompatible with postcopy"); + return false; + } + } + + 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++) { + 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, "There's a migration process in progress"); + 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; +} + +uint32_t migrate_checkpoint_delay(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.x_checkpoint_delay; +} + +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; +} + +bool migrate_direct_io(void) +{ + MigrationState *s = migrate_get_current(); + + /* + * O_DIRECT is only supported with mapped-ram and multifd. + * + * mapped-ram is needed because filesystems impose restrictions on + * O_DIRECT IO alignment (see MAPPED_RAM_FILE_OFFSET_ALIGNMENT). + * + * multifd is needed to keep the unaligned portion of the stream + * isolated to the main migration thread while multifd channels + * process the aligned data with O_DIRECT enabled. + */ + return s->parameters.direct_io && + s->capabilities[MIGRATION_CAPABILITY_MAPPED_RAM] && + s->capabilities[MIGRATION_CAPABILITY_MULTIFD]; +} + +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_qatzip_level(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.multifd_qatzip_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; +} + +/* parameters helpers */ + +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_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_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_qatzip_level = true; + params->multifd_qatzip_level = s->parameters.multifd_qatzip_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; + params->has_direct_io = true; + params->direct_io = s->parameters.direct_io; + + 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_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_multifd_channels = true; + params->has_multifd_compression = true; + params->has_multifd_zlib_level = true; + params->has_multifd_qatzip_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; + params->has_direct_io = 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_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_qatzip_level && + ((params->multifd_qatzip_level > 9) || + (params->multifd_qatzip_level < 1))) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_qatzip_level", + "a value between 1 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, + "Parameter 'vcpu_dirty_limit' must be greater than 1 MB/s"); + return false; + } + + if (params->has_direct_io && params->direct_io && !qemu_has_direct_io()) { + error_setg(errp, "No build-time support for direct-io"); + 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_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_multifd_channels) { + dest->multifd_channels = params->multifd_channels; + } + if (params->has_multifd_compression) { + dest->multifd_compression = params->multifd_compression; + } + if (params->has_multifd_qatzip_level) { + dest->multifd_qatzip_level = params->multifd_qatzip_level; + } + 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; + } + + if (params->has_direct_io) { + dest->direct_io = params->direct_io; + } +} + +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_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_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_qatzip_level) { + s->parameters.multifd_qatzip_level = params->multifd_qatzip_level; + } + 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; + } + + if (params->has_direct_io) { + s->parameters.direct_io = params->direct_io; + } +} + +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..79084eed0d --- /dev/null +++ b/migration/options.h @@ -0,0 +1,92 @@ +/* + * 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_colo(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_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); + +/* parameters */ + +const BitmapMigrationNodeAliasList *migrate_block_bitmap_mapping(void); +bool migrate_has_block_bitmap_mapping(void); + +uint32_t migrate_checkpoint_delay(void); +uint8_t migrate_cpu_throttle_increment(void); +uint8_t migrate_cpu_throttle_initial(void); +bool migrate_cpu_throttle_tailslow(void); +bool migrate_direct_io(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_qatzip_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 helpers */ + +bool migrate_params_check(MigrationParameters *params, Error **errp); +void migrate_params_init(MigrationParameters *params); +#endif diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index b9a37ef255..a535fd2e30 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" @@ -34,16 +33,18 @@ #include "hw/boards.h" #include "exec/ramblock.h" #include "socket.h" -#include "qemu-file.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 */ #define MAX_DISCARDS_PER_COMMAND 12 -struct PostcopyDiscardState { +typedef struct PostcopyDiscardState { const char *ramblock_name; uint16_t cur_entry; /* @@ -53,7 +54,7 @@ struct PostcopyDiscardState { uint64_t length_list[MAX_DISCARDS_PER_COMMAND]; unsigned int nsentwords; unsigned int nsentcmds; -}; +} PostcopyDiscardState; static NotifierWithReturnList postcopy_notifier_list; @@ -76,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); } /* @@ -101,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) @@ -226,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; } @@ -273,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)); @@ -284,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; @@ -296,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; } } @@ -318,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; } @@ -330,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; } } @@ -339,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; } @@ -359,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 */ @@ -368,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; + } } /* @@ -403,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; } @@ -415,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)); @@ -426,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; } @@ -571,9 +593,38 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) { trace_postcopy_ram_incoming_cleanup_entry(); - if (mis->postcopy_prio_thread_created) { + 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->postcopy_prio_thread_created = false; + mis->preempt_thread_status = PREEMPT_THREAD_NONE; } if (mis->have_fault_thread) { @@ -679,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); } @@ -695,18 +746,10 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, RAMBlock *rb) { size_t pagesize = qemu_ram_pagesize(rb); - struct uffdio_range range; - int ret; trace_postcopy_wake_shared(client_addr, qemu_ram_get_idstr(rb)); - range.start = ROUND_DOWN(client_addr, pagesize); - range.len = pagesize; - ret = ioctl(pcfd->fd, UFFDIO_WAKE, &range); - if (ret) { - error_report("%s: Failed to wake: %zx in %s (%s)", - __func__, (size_t)client_addr, qemu_ram_get_idstr(rb), - strerror(errno)); - } - return ret; + return uffd_wakeup(pcfd->fd, + (void *)(uintptr_t)ROUND_DOWN(client_addr, pagesize), + pagesize); } static int postcopy_request_page(MigrationIncomingState *mis, RAMBlock *rb, @@ -1159,8 +1202,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)); @@ -1171,7 +1216,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; } @@ -1184,7 +1230,8 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } - postcopy_thread_create(mis, &mis->fault_thread, "fault-default", + postcopy_thread_create(mis, &mis->fault_thread, + MIGRATION_THREAD_DST_FAULT, postcopy_ram_fault_thread, QEMU_THREAD_JOINABLE); mis->have_fault_thread = true; @@ -1204,9 +1251,10 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) * 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_thread_create(mis, &mis->postcopy_prio_thread, + MIGRATION_THREAD_DST_PREEMPT, postcopy_preempt_thread, QEMU_THREAD_JOINABLE); - mis->postcopy_prio_thread_created = true; + mis->preempt_thread_status = PREEMPT_THREAD_CREATED; } trace_postcopy_ram_enable_notify(); @@ -1221,18 +1269,10 @@ static int qemu_ufd_copy_ioctl(MigrationIncomingState *mis, void *host_addr, int ret; if (from_addr) { - struct uffdio_copy copy_struct; - copy_struct.dst = (uint64_t)(uintptr_t)host_addr; - copy_struct.src = (uint64_t)(uintptr_t)from_addr; - copy_struct.len = pagesize; - copy_struct.mode = 0; - ret = ioctl(userfault_fd, UFFDIO_COPY, ©_struct); + ret = uffd_copy_page(userfault_fd, host_addr, from_addr, pagesize, + false); } else { - struct uffdio_zeropage zero_struct; - zero_struct.range.start = (uint64_t)(uintptr_t)host_addr; - zero_struct.range.len = pagesize; - zero_struct.mode = 0; - ret = ioctl(userfault_fd, UFFDIO_ZEROPAGE, &zero_struct); + ret = uffd_zero_page(userfault_fd, host_addr, pagesize, false); } if (!ret) { qemu_mutex_lock(&mis->page_request_mutex); @@ -1244,8 +1284,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); @@ -1277,18 +1329,16 @@ int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from, RAMBlock *rb) { size_t pagesize = qemu_ram_pagesize(rb); + int e; /* copy also acks to the kernel waking the stalled thread up * TODO: We can inhibit that ack and only do it if it was requested * which would be slightly cheaper, but we'd have to be careful * of the order of updating our page state. */ - if (qemu_ufd_copy_ioctl(mis, host, from, pagesize, rb)) { - int e = errno; - error_report("%s: %s copy host: %p from: %p (size: %zd)", - __func__, strerror(e), host, from, pagesize); - - return -e; + e = qemu_ufd_copy_ioctl(mis, host, from, pagesize, rb); + if (e) { + return e; } trace_postcopy_place_page(host); @@ -1310,12 +1360,10 @@ int postcopy_place_page_zero(MigrationIncomingState *mis, void *host, * but it's not available for everything (e.g. hugetlbpages) */ if (qemu_ram_is_uf_zeroable(rb)) { - if (qemu_ufd_copy_ioctl(mis, host, NULL, pagesize, rb)) { - int e = errno; - error_report("%s: %s zero host: %p", - __func__, strerror(e), host); - - return -e; + int e; + e = qemu_ufd_copy_ioctl(mis, host, NULL, pagesize, rb); + if (e) { + return e; } return postcopy_notify_shared_wake(rb, qemu_ram_block_host_offset(rb, @@ -1331,7 +1379,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; @@ -1345,49 +1393,42 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis) int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) { - assert(0); - return -1; + g_assert_not_reached(); } int postcopy_ram_prepare_discard(MigrationIncomingState *mis) { - assert(0); - return -1; + g_assert_not_reached(); } int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, uint64_t client_addr, uint64_t rb_offset) { - assert(0); - return -1; + g_assert_not_reached(); } int postcopy_ram_incoming_setup(MigrationIncomingState *mis) { - assert(0); - return -1; + g_assert_not_reached(); } int postcopy_place_page(MigrationIncomingState *mis, void *host, void *from, RAMBlock *rb) { - assert(0); - return -1; + g_assert_not_reached(); } int postcopy_place_page_zero(MigrationIncomingState *mis, void *host, RAMBlock *rb) { - assert(0); - return -1; + g_assert_not_reached(); } int postcopy_wake_shared(struct PostCopyFD *pcfd, uint64_t client_addr, RAMBlock *rb) { - assert(0); - return -1; + g_assert_not_reached(); } #endif @@ -1498,7 +1539,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 */ @@ -1539,7 +1580,7 @@ void postcopy_unregister_shared_ufd(struct PostCopyFD *pcfd) } } -bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) +void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) { /* * The new loading channel has its own threads, so it needs to be @@ -1547,10 +1588,8 @@ bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file) */ qemu_file_set_blocking(file, true); mis->postcopy_qemufile_dst = file; + qemu_sem_post(&mis->postcopy_qemufile_dst_done); trace_postcopy_preempt_new_channel(); - - /* Start the migration immediately */ - return true; } /* @@ -1601,7 +1640,7 @@ postcopy_preempt_send_channel_new(QIOTask *task, gpointer opaque) } if (migrate_channel_requires_tls_upgrade(ioc)) { - tioc = migration_tls_client_create(s, ioc, s->hostname, &local_err); + tioc = migration_tls_client_create(ioc, s->hostname, &local_err); if (!tioc) { goto out; } @@ -1618,14 +1657,27 @@ out: postcopy_preempt_send_channel_done(s, ioc, local_err); } -/* Returns 0 if channel established, -1 for error. */ -int postcopy_preempt_wait_channel(MigrationState *s) +/* + * 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. @@ -1635,22 +1687,10 @@ int postcopy_preempt_wait_channel(MigrationState *s) return s->postcopy_qemufile_src ? 0 : -1; } -int postcopy_preempt_setup(MigrationState *s, Error **errp) +void postcopy_preempt_setup(MigrationState *s) { - if (!migrate_postcopy_preempt()) { - return 0; - } - - if (!migrate_multi_channels_is_allowed()) { - error_setg(errp, "Postcopy preempt is not supported as current " - "migration stream does not support multi-channels."); - return -1; - } - /* Kick an async task to connect */ socket_send_channel_create(postcopy_preempt_send_channel_new, s); - - return 0; } static void postcopy_pause_ram_fast_load(MigrationIncomingState *mis) @@ -1662,6 +1702,11 @@ static void postcopy_pause_ram_fast_load(MigrationIncomingState *mis) 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; @@ -1673,13 +1718,19 @@ void *postcopy_preempt_thread(void *opaque) 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 (1) { + 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) { + if (ret && preempt_thread_should_run(mis)) { postcopy_pause_ram_fast_load(mis); } else { /* We're done */ @@ -1694,3 +1745,9 @@ void *postcopy_preempt_thread(void *opaque) return NULL; } + +bool postcopy_is_paused(MigrationStatus status) +{ + return status == MIGRATION_STATUS_POSTCOPY_PAUSED || + status == MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP; +} diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index 6147bf7d1d..a6df1b2811 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -13,8 +13,11 @@ #ifndef QEMU_POSTCOPY_RAM_H #define QEMU_POSTCOPY_RAM_H +#include "qapi/qapi-types-migration.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 +130,6 @@ enum PostcopyNotifyReason { struct PostcopyNotifyData { enum PostcopyNotifyReason reason; - Error **errp; }; void postcopy_add_notifier(NotifierWithReturn *nn); @@ -190,8 +192,9 @@ enum PostcopyChannels { RAM_CHANNEL_MAX, }; -bool postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file); -int postcopy_preempt_setup(MigrationState *s, Error **errp); -int postcopy_preempt_wait_channel(MigrationState *s); +void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file); +void postcopy_preempt_setup(MigrationState *s); +int postcopy_preempt_establish_channel(MigrationState *s); +bool postcopy_is_paused(MigrationStatus status); #endif diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 2d5f74ffc2..b6d2f588bd 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -22,37 +22,25 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" -#include #include "qemu/madvise.h" #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 QEMUFileHooks *hooks; QIOChannel *ioc; bool is_writable; - /* - * Maximum amount of data in bytes to transfer during one - * rate limiting time window - */ - int64_t rate_limit_max; - /* - * Total amount of data in bytes queued for transfer - * during this rate limiting time window - */ - int64_t rate_limit_used; - - /* The sum of bytes transferred on the wire */ - int64_t total_transferred; - int buf_index; int buf_size; /* 0 when writing */ uint8_t buf[IO_BUF_SIZE]; @@ -63,8 +51,6 @@ struct QEMUFile { int last_error; Error *last_error_obj; - /* has the file has been shutdown */ - bool shutdown; }; /* @@ -76,9 +62,7 @@ struct QEMUFile { */ int qemu_file_shutdown(QEMUFile *f) { - int ret = 0; - - f->shutdown = true; + Error *err = NULL; /* * We must set qemufile error before the real shutdown(), otherwise @@ -108,23 +92,12 @@ int qemu_file_shutdown(QEMUFile *f) return -ENOSYS; } - if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL) < 0) { - ret = -EIO; + if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, &err) < 0) { + error_report_err(err); + return -EIO; } - return ret; -} - -bool qemu_file_mode_is_not_valid(const char *mode) -{ - 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 0; } static QEMUFile *qemu_file_new_impl(QIOChannel *ioc, bool is_writable) @@ -159,25 +132,29 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc) return qemu_file_new_impl(ioc, false); } -void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks) -{ - f->hooks = hooks; -} - /* * Get last error for stream f with optional Error* * * 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; } @@ -230,7 +207,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; } /* @@ -241,7 +218,7 @@ 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->is_writable; } @@ -281,6 +258,10 @@ 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 @@ -288,14 +269,14 @@ static void qemu_iovec_release_ram(QEMUFile *f) * 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) { 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) { Error *local_error = NULL; @@ -304,7 +285,8 @@ void qemu_fflush(QEMUFile *f) &local_error) < 0) { qemu_file_set_error_obj(f, -EIO, local_error); } else { - f->total_transferred += iov_size(f->iov, f->iovcnt); + uint64_t size = iov_size(f->iov, f->iovcnt); + stat64_add(&mig_stats.qemu_file_transferred, size); } qemu_iovec_release_ram(f); @@ -312,76 +294,7 @@ void qemu_fflush(QEMUFile *f) 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, 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, 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, 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, block_offset, - offset, size, bytes_sent); - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - f->rate_limit_used += size; - } - - if (ret != RAM_SAVE_CONTROL_DELAYED && - ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (bytes_sent && *bytes_sent > 0) { - qemu_file_credit_transfer(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; } /* @@ -392,7 +305,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; @@ -407,7 +320,7 @@ 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; } @@ -429,7 +342,6 @@ static ssize_t qemu_fill_buffer(QEMUFile *f) if (len > 0) { f->buf_size += len; - f->total_transferred += len; } else if (len == 0) { qemu_file_set_error_obj(f, -EIO, local_error); } else { @@ -439,11 +351,6 @@ static ssize_t qemu_fill_buffer(QEMUFile *f) return len; } -void qemu_file_credit_transfer(QEMUFile *f, size_t size) -{ - f->total_transferred += size; -} - /** Closes the file * * Returns negative error value if any error happened on previous operations or @@ -454,22 +361,12 @@ void qemu_file_credit_transfer(QEMUFile *f, size_t size) */ int qemu_fclose(QEMUFile *f) { - int ret, ret2; - qemu_fflush(f); - ret = qemu_file_get_error(f); - - ret2 = qio_channel_close(f->ioc, NULL); + 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); - - /* 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; - } error_free(f->last_error_obj); g_free(f); trace_qemu_file_fclose(); @@ -496,7 +393,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) { @@ -531,7 +428,6 @@ void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, size_t size, return; } - f->rate_limit_used += size; add_to_iovec(f, buf, size, may_free); } @@ -549,7 +445,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->rate_limit_used += l; add_buf_to_iovec(f, l); if (qemu_file_get_error(f)) { break; @@ -559,6 +454,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) { @@ -566,7 +562,6 @@ void qemu_put_byte(QEMUFile *f, int v) } f->buf[f->buf_index] = v; - f->rate_limit_used++; add_buf_to_iovec(f, 1); } @@ -585,7 +580,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; @@ -633,7 +628,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; @@ -674,7 +669,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; @@ -696,7 +691,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; @@ -713,7 +708,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; @@ -722,11 +717,13 @@ int qemu_get_byte(QEMUFile *f) return result; } -int64_t qemu_file_total_transferred_fast(QEMUFile *f) +uint64_t qemu_file_transferred(QEMUFile *f) { - int64_t ret = f->total_transferred; + 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; } @@ -734,46 +731,6 @@ int64_t qemu_file_total_transferred_fast(QEMUFile *f) return ret; } -int64_t qemu_file_total_transferred(QEMUFile *f) -{ - qemu_fflush(f); - return f->total_transferred; -} - -int qemu_file_rate_limit(QEMUFile *f) -{ - if (f->shutdown) { - return 1; - } - if (qemu_file_get_error(f)) { - return 1; - } - if (f->rate_limit_max > 0 && f->rate_limit_used > f->rate_limit_max) { - return 1; - } - return 0; -} - -int64_t qemu_file_get_rate_limit(QEMUFile *f) -{ - return f->rate_limit_max; -} - -void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit) -{ - f->rate_limit_max = limit; -} - -void qemu_file_reset_rate_limit(QEMUFile *f) -{ - f->rate_limit_used = 0; -} - -void qemu_file_acct_rate_limit(QEMUFile *f, int64_t len) -{ - f->rate_limit_used += len; -} - void qemu_put_be16(QEMUFile *f, unsigned int v) { qemu_put_byte(f, v >> 8); @@ -820,73 +777,6 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } -/* return the size after compression, or negative value on error */ -static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, - const uint8_t *source, size_t source_len) -{ - int err; - - err = deflateReset(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 = deflate(stream, Z_FINISH); - if (err != Z_STREAM_END) { - return -1; - } - - return stream->next_out - dest; -} - -/* Compress size bytes of data start at p and store the compressed - * data to the buffer of f. - * - * Since the file is dummy file with empty_ops, return -1 if f has no space to - * save the compressed data. - */ -ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, - const uint8_t *p, size_t size) -{ - ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); - - if (blen < compressBound(size)) { - return -1; - } - - blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), - blen, p, size); - if (blen < 0) { - return -1; - } - - qemu_put_be32(f, blen); - add_buf_to_iovec(f, blen); - return blen + sizeof(int32_t); -} - -/* Put the data in the buffer of f_src to the buffer of f_des, and - * then reset the buf_index of f_src to 0. - */ - -int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src) -{ - int len = 0; - - if (f_src->buf_index > 0) { - len = f_src->buf_index; - qemu_put_buffer(f_des, f_src->buf, f_src->buf_index); - f_src->buf_index = 0; - f_src->iovcnt = 0; - } - return len; -} - /* * Get a string whose length is determined by a single preceding byte * A preallocated 256 byte buffer must be passed in. @@ -894,7 +784,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); @@ -940,3 +830,37 @@ QIOChannel *qemu_file_get_ioc(QEMUFile *file) { 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 fa13d04d78..11c2120edd 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -29,73 +29,19 @@ #include "exec/cpu-common.h" #include "io/channel.h" -/* - * This function provides hooks around different - * stages of RAM migration. - * 'data' is call specific data associated with the 'flags' value - */ -typedef int (QEMURamHookFunc)(QEMUFile *f, 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, - ram_addr_t block_offset, - ram_addr_t offset, - size_t size, - uint64_t *bytes_sent); - -typedef struct QEMUFileHooks { - QEMURamHookFunc *before_ram_iterate; - QEMURamHookFunc *after_ram_iterate; - QEMURamHookFunc *hook_ram_load; - QEMURamSaveFunc *save_page; -} QEMUFileHooks; - QEMUFile *qemu_file_new_input(QIOChannel *ioc); QEMUFile *qemu_file_new_output(QIOChannel *ioc); -void qemu_file_set_hooks(QEMUFile *f, const QEMUFileHooks *hooks); int qemu_fclose(QEMUFile *f); /* - * qemu_file_total_transferred: + * qemu_file_transferred: * - * Report the total number of bytes transferred with - * this file. - * - * For writable files, any pending buffers will be - * flushed, so the reported value will be equal to - * the number of bytes transferred on the wire. - * - * For readable files, the reported value will be - * equal to the number of bytes transferred on the - * wire. - * - * Returns: the total bytes transferred - */ -int64_t qemu_file_total_transferred(QEMUFile *f); - -/* - * qemu_file_total_transferred_fast: - * - * As qemu_file_total_transferred except for writable - * files, where no flush is performed and the reported - * amount will include the size of any queued buffers, - * on top of the amount actually 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 */ -int64_t qemu_file_total_transferred_fast(QEMUFile *f); +uint64_t qemu_file_transferred(QEMUFile *f); /* * put_buffer without copying the buffer. @@ -103,69 +49,35 @@ int64_t qemu_file_total_transferred_fast(QEMUFile *f); */ 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); -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); +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); /* * 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); -/* - * qemu_file_credit_transfer: - * - * Report on a number of bytes that have been transferred - * out of band from the main file object I/O methods. This - * accounting information tracks the total migration traffic. - */ -void qemu_file_credit_transfer(QEMUFile *f, size_t size); -void qemu_file_reset_rate_limit(QEMUFile *f); -/* - * qemu_file_acct_rate_limit: - * - * Report on a number of bytes the have been transferred - * out of band from the main file object I/O methods, and - * need to be applied to the rate limiting calcuations - */ -void qemu_file_acct_rate_limit(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.c b/migration/ram.c index f25ebd9620..05ff9eb328 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -32,10 +32,10 @@ #include "qemu/bitmap.h" #include "qemu/madvise.h" #include "qemu/main-loop.h" -#include "io/channel-null.h" #include "xbzrle.h" #include "ram.h" #include "migration.h" +#include "migration-stats.h" #include "migration/register.h" #include "migration/misc.h" #include "qemu-file.h" @@ -45,18 +45,22 @@ #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" #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "migration/colo.h" -#include "block.h" #include "sysemu/cpu-throttle.h" #include "savevm.h" #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() */ @@ -67,24 +71,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. + * + * RAM_SAVE_FLAG_FULL was obsoleted in 2009. + * + * RAM_SAVE_FLAG_COMPRESS_PAGE (0x100) was removed in QEMU 9.1. */ - -#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */ +#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 */ -#define RAM_SAVE_FLAG_COMPRESS_PAGE 0x100 +/* 0x80 is reserved in rdma.h for RAM_SAVE_FLAG_HOOK */ +#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 { @@ -103,14 +150,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); } } @@ -162,10 +209,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 @@ -220,6 +273,10 @@ void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, nr); } +void ramblock_recv_bitmap_set_offset(RAMBlock *rb, uint64_t byte_offset) +{ + set_bit_atomic(byte_offset >> TARGET_PAGE_BITS, rb->receivedmap); +} #define RAMBLOCK_RECV_BITMAP_ENDING (0x0123456789abcdefULL) /* @@ -268,17 +325,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); @@ -296,30 +351,19 @@ struct RAMSrcPageRequest { QSIMPLEQ_ENTRY(RAMSrcPageRequest) next_req; }; -typedef struct { - /* - * Cached ramblock/offset values if preempted. They're only meaningful if - * preempted==true below. - */ - RAMBlock *ram_block; - unsigned long ram_page; - /* - * Whether a postcopy preemption just happened. Will be reset after - * precopy recovered to background migration. - */ - bool preempted; -} PostcopyPreemptState; - /* 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 */ @@ -339,17 +383,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; @@ -357,7 +394,12 @@ 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; @@ -365,13 +407,13 @@ struct RAMState { QemuMutex src_page_req_mutex; QSIMPLEQ_HEAD(, RAMSrcPageRequest) src_page_requests; - /* Postcopy preemption informations */ - PostcopyPreemptState postcopy_preempt_state; /* - * Current channel we're using on src VM. Only valid if postcopy-preempt - * is enabled. + * 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_channel; + unsigned int postcopy_bmap_sync_requested; }; typedef struct RAMState RAMState; @@ -379,11 +421,6 @@ static RAMState *ram_state; static NotifierWithReturnList precopy_notifier_list; -static void postcopy_preempt_reset(RAMState *rs) -{ - memset(&rs->postcopy_preempt_state, 0, sizeof(PostcopyPreemptState)); -} - /* Whether postcopy has queued requests? */ static bool postcopy_has_request(RAMState *rs) { @@ -409,9 +446,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) @@ -420,224 +456,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; } -void dirty_sync_missed_zero_copy(void) +struct MigrationOps { + int (*ram_save_target_page)(RAMState *rs, PageSearchStatus *pss); +}; +typedef struct MigrationOps MigrationOps; + +MigrationOps *migration_ops; + +static int ram_save_host_page_urgent(PageSearchStatus *pss); + +/* NOTE: page is the PFN not real ram_addr_t. */ +static void pss_init(PageSearchStatus *pss, RAMBlock *rb, ram_addr_t page) { - ram_counters.dirty_sync_missed_zero_copy++; + pss->block = rb; + pss->page = page; + pss->complete_round = false; } -/* 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; - /* - * [POSTCOPY-ONLY] Whether current page is explicitly requested by - * postcopy. When set, the request is "urgent" because the dest QEMU - * threads are waiting for us. - */ - bool postcopy_requested; - /* - * [POSTCOPY-ONLY] The target channel to use to send current page. - * - * Note: This may _not_ match with the value in postcopy_requested - * above. Let's imagine the case where the postcopy request is exactly - * the page that we're sending in progress during precopy. In this case - * we'll have postcopy_requested set to true but the target channel - * will be the precopy channel (so that we don't split brain on that - * specific page since the precopy channel already contains partial of - * that page data). - * - * Besides that specific use case, postcopy_target_channel should - * always be equal to postcopy_requested, because by default we send - * postcopy pages via postcopy preempt channel. - */ - bool postcopy_target_channel; -}; -typedef struct PageSearchStatus PageSearchStatus; - -CompressionStats compression_counters; - -struct CompressParam { - bool done; - bool quit; - bool zero_page; - 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; - -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; - -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 postcopy_preempt_restore(RAMState *rs, PageSearchStatus *pss, - bool postcopy_requested); - -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_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; + return pss1->host_page_sending && pss2->host_page_sending && + (pss1->host_page_start == pss2->host_page_start); } /** @@ -647,28 +501,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; } @@ -685,11 +540,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; @@ -719,13 +573,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. @@ -734,16 +587,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 @@ -756,24 +605,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 @@ -831,15 +682,14 @@ 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), - * the xbzrle encoded bytes don't count the 8 byte header with + * The xbzrle encoded bytes don't count the 8 byte header with * RAM_SAVE_FLAG_CONTINUE. */ xbzrle_counters.bytes += bytes_xbzrle - 8; @@ -849,26 +699,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, @@ -937,7 +799,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; } @@ -1083,24 +945,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) + + 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 - @@ -1117,79 +980,89 @@ 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; } +} - 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 ((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); + WITH_QEMU_LOCK_GUARD(&rs->bitmap_mutex) { + WITH_RCU_READ_LOCK_GUARD() { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ramblock_sync_dirty_bitmap(rs, block); + } + stat64_set(&mig_stats.dirty_bytes_last_sync, ram_bytes_remaining()); } - ram_counters.remaining = ram_bytes_remaining(); } - qemu_mutex_unlock(&rs->bitmap_mutex); memory_global_after_dirty_log_sync(); trace_migration_bitmap_sync_end(rs->num_dirty_pages_period); @@ -1207,16 +1080,18 @@ 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) +void migration_bitmap_sync_precopy(bool last_stage) { Error *local_err = NULL; + assert(ram_state); /* * The current notifier usage is just an optimization to migration, so we @@ -1227,14 +1102,14 @@ static void migration_bitmap_sync_precopy(RAMState *rs) local_err = NULL; } - migration_bitmap_sync(rs); + migration_bitmap_sync(ram_state, 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; @@ -1243,51 +1118,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; } /* @@ -1297,34 +1176,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; } @@ -1333,26 +1200,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; } @@ -1381,9 +1256,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 @@ -1394,7 +1269,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(); @@ -1402,164 +1277,45 @@ 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) -{ - RAMState *rs = ram_state; - uint8_t *p = block->host + offset; - int ret; - - if (save_zero_page_to_file(rs, f, block, offset)) { - return true; - } - - save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - - /* - * 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!"); - } - 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; -} +#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, mark it "not urgent", and use - * precopy channel to send it. - */ - pss->postcopy_requested = false; - pss->postcopy_target_channel = RAM_CHANNEL_PRECOPY; + /* 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)) { @@ -1567,34 +1323,35 @@ 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 memory migration starts over, we will meet a dirtied page - * which may still exists in compression threads's ring, so we - * should flush the compressed data to make sure the new page - * is not overwritten by the old one in the destination. - * - * 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); + if (migrate_multifd() && + (!migrate_multifd_flush_after_each_section() || + migrate_mapped_ram())) { + QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel; + int ret = multifd_ram_flush_and_sync(); + if (ret < 0) { + return ret; + } + + if (!migrate_mapped_ram()) { + qemu_put_be64(f, RAM_SAVE_FLAG_MULTIFD_FLUSH); + qemu_fflush(f); + } + } /* 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; } } @@ -1696,7 +1453,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); @@ -1856,6 +1613,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 * @@ -1891,8 +1681,7 @@ int ram_write_tracking_start(void) memory_region_ref(block->mr); /* Apply UFFD write protection to the block memory range */ - if (uffd_change_protection(rs->uffdio_fd, block->host, - block->max_length, true, false)) { + if (ram_block_uffd_protect(block, uffd_fd)) { goto fail; } @@ -1909,12 +1698,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; @@ -1940,9 +1723,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, @@ -1986,71 +1766,20 @@ bool ram_write_tracking_available(void) bool ram_write_tracking_compatible(void) { - assert(0); - return false; + g_assert_not_reached(); } int ram_write_tracking_start(void) { - assert(0); - return -1; + g_assert_not_reached(); } void ram_write_tracking_stop(void) { - assert(0); + g_assert_not_reached(); } #endif /* defined(__linux__) */ -/* - * Check whether two addr/offset of the ramblock falls onto the same host huge - * page. Returns true if so, false otherwise. - */ -static bool offset_on_same_huge_page(RAMBlock *rb, uint64_t addr1, - uint64_t addr2) -{ - size_t page_size = qemu_ram_pagesize(rb); - - addr1 = ROUND_DOWN(addr1, page_size); - addr2 = ROUND_DOWN(addr2, page_size); - - return addr1 == addr2; -} - -/* - * Whether a previous preempted precopy huge page contains current requested - * page? Returns true if so, false otherwise. - * - * This should really happen very rarely, because it means when we were sending - * during background migration for postcopy we're sending exactly the page that - * some vcpu got faulted on on dest node. When it happens, we probably don't - * need to do much but drop the request, because we know right after we restore - * the precopy stream it'll be serviced. It'll slightly affect the order of - * postcopy requests to be serviced (e.g. it'll be the same as we move current - * request to the end of the queue) but it shouldn't be a big deal. The most - * imporant thing is we can _never_ try to send a partial-sent huge page on the - * POSTCOPY channel again, otherwise that huge page will got "split brain" on - * two channels (PRECOPY, POSTCOPY). - */ -static bool postcopy_preempted_contains(RAMState *rs, RAMBlock *block, - ram_addr_t offset) -{ - PostcopyPreemptState *state = &rs->postcopy_preempt_state; - - /* No preemption at all? */ - if (!state->preempted) { - return false; - } - - /* Not even the same ramblock? */ - if (state->ram_block != block) { - return false; - } - - return offset_on_same_huge_page(block, offset, - state->ram_page << TARGET_PAGE_BITS); -} - /** * get_queued_page: unqueue a page from the postcopy requests * @@ -2065,7 +1794,7 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) { RAMBlock *block; ram_addr_t offset; - bool dirty; + bool dirty = false; do { block = unqueue_page(rs, &offset); @@ -2090,20 +1819,7 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) } while (block && !dirty); - if (block) { - /* See comment above postcopy_preempted_contains() */ - if (postcopy_preempted_contains(rs, block, offset)) { - trace_postcopy_preempt_hit(block->idstr, offset); - /* - * If what we preempted previously was exactly what we're - * requesting right now, restore the preempted precopy - * immediately, boosting its priority as it's requested by - * postcopy. - */ - postcopy_preempt_restore(rs, pss, true); - return true; - } - } else { + if (!block) { /* * Poll write faults too if background snapshot is enabled; that's * when we have vcpus got blocked by the write protected pages. @@ -2125,9 +1841,6 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) * really rare. */ pss->complete_round = false; - /* Mark it an urgent request, meanwhile using POSTCOPY channel */ - pss->postcopy_requested = true; - pss->postcopy_target_channel = RAM_CHANNEL_POSTCOPY; } return !!block; @@ -2167,12 +1880,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) { @@ -2184,7 +1898,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 { @@ -2192,19 +1906,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; @@ -2220,227 +1985,158 @@ 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) -{ - if (!save_page_use_compression(rs)) { - return false; - } - - /* - * When starting the process of a new block, the first page of - * the block should be sent out before other pages in the same - * block, and all the pages in last block should have been sent - * out, keeping this order is important, because the 'cont' flag - * is used to avoid resending the block name. - * - * 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); - return false; - } - - if (compress_page_with_multi_thread(rs, block, offset) > 0) { - return true; - } - - compression_counters.busy++; - return false; -} - /** - * 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_zero_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 in postcopy as one whole host page should be - * placed. Meanwhile postcopy requires atomic update of pages, so even - * if host page size == guest page size the dest guest during run may - * still see partially copied pages which is data corruption. - */ - if (migrate_use_multifd() && !migration_in_postcopy()) { - return ram_save_multifd_page(rs, block, offset); - } - return ram_save_page(rs, pss); } -static bool postcopy_needs_preempt(RAMState *rs, PageSearchStatus *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) { - MigrationState *ms = migrate_get_current(); - - /* Not enabled eager preempt? Then never do that. */ - if (!migrate_postcopy_preempt()) { - return false; - } - - /* If the user explicitly disabled breaking of huge page, skip */ - if (!ms->postcopy_preempt_break_huge) { - return false; - } - - /* If the ramblock we're sending is a small page? Never bother. */ - if (qemu_ram_pagesize(pss->block) == TARGET_PAGE_SIZE) { - return false; - } - - /* Not in postcopy at all? */ - if (!migration_in_postcopy()) { - return false; - } + RAMBlock *block = pss->block; + ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; /* - * If we're already handling a postcopy request, don't preempt as this page - * has got the same high priority. + * While using multifd live migration, we still need to handle zero + * page checking on the migration main thread. */ - if (pss->postcopy_requested) { - return false; - } - - /* If there's postcopy requests, then check it up! */ - return postcopy_has_request(rs); -} - -/* Returns true if we preempted precopy, false otherwise */ -static void postcopy_do_preempt(RAMState *rs, PageSearchStatus *pss) -{ - PostcopyPreemptState *p_state = &rs->postcopy_preempt_state; - - trace_postcopy_preempt_triggered(pss->block->idstr, pss->page); - - /* - * Time to preempt precopy. Cache current PSS into preempt state, so that - * after handling the postcopy pages we can recover to it. We need to do - * so because the dest VM will have partial of the precopy huge page kept - * over in its tmp huge page caches; better move on with it when we can. - */ - p_state->ram_block = pss->block; - p_state->ram_page = pss->page; - p_state->preempted = true; -} - -/* Whether we're preempted by a postcopy request during sending a huge page */ -static bool postcopy_preempt_triggered(RAMState *rs) -{ - return rs->postcopy_preempt_state.preempted; -} - -static void postcopy_preempt_restore(RAMState *rs, PageSearchStatus *pss, - bool postcopy_requested) -{ - PostcopyPreemptState *state = &rs->postcopy_preempt_state; - - assert(state->preempted); - - pss->block = state->ram_block; - pss->page = state->ram_page; - - /* Whether this is a postcopy request? */ - pss->postcopy_requested = postcopy_requested; - /* - * When restoring a preempted page, the old data resides in PRECOPY - * slow channel, even if postcopy_requested is set. So always use - * PRECOPY channel here. - */ - pss->postcopy_target_channel = RAM_CHANNEL_PRECOPY; - - trace_postcopy_preempt_restored(pss->block->idstr, pss->page); - - /* Reset preempt state, most importantly, set preempted==false */ - postcopy_preempt_reset(rs); -} - -static void postcopy_preempt_choose_channel(RAMState *rs, PageSearchStatus *pss) -{ - MigrationState *s = migrate_get_current(); - unsigned int channel = pss->postcopy_target_channel; - QEMUFile *next; - - if (channel != rs->postcopy_channel) { - if (channel == RAM_CHANNEL_PRECOPY) { - next = s->to_dst_file; - } else { - next = s->postcopy_qemufile_src; + if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) { + if (save_zero_page(rs, pss, offset)) { + return 1; } - /* Update and cache the current channel */ - rs->f = next; - rs->postcopy_channel = channel; - - /* - * If channel switched, reset last_sent_block since the old sent block - * may not be on the same channel. - */ - rs->last_sent_block = NULL; - - trace_postcopy_preempt_switch_channel(channel); } + 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); -/* We need to make sure rs->f always points to the default channel elsewhere */ -static void postcopy_preempt_reset_channel(RAMState *rs) -{ - if (migrate_postcopy_preempt() && migration_in_postcopy()) { - rs->postcopy_channel = RAM_CHANNEL_PRECOPY; - rs->f = migrate_get_current()->to_dst_file; - trace_postcopy_preempt_reset_channel(); + /* + * 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; } /** @@ -2451,9 +2147,14 @@ static void postcopy_preempt_reset_channel(RAMState *rs) * 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 @@ -2461,64 +2162,61 @@ static void postcopy_preempt_reset_channel(RAMState *rs) */ 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; } - if (migrate_postcopy_preempt() && migration_in_postcopy()) { - postcopy_preempt_choose_channel(rs, pss); - } + /* Update host page boundary information */ + pss_host_page_prepare(pss); do { - if (postcopy_needs_preempt(rs, pss)) { - postcopy_do_preempt(rs, pss); - break; - } + page_dirty = migration_bitmap_clear_dirty(rs, pss->block, pss->page); /* 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; - } - - pages += tmppages; + 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); - /* - * When with postcopy preempt mode, flush the data as soon as possible for - * postcopy requests, because we've already sent a whole huge page, so the - * dst node should already have enough resource to atomically filling in - * the current missing page. - * - * More importantly, when using separate postcopy channel, we must do - * explicit flush or it won't flush until the buffer is full. - */ - if (migrate_postcopy_preempt() && pss->postcopy_requested) { - qemu_fflush(rs->f); - } + 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); @@ -2539,12 +2237,11 @@ 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; } @@ -2560,74 +2257,59 @@ static int ram_find_and_save_block(RAMState *rs) rs->last_page = 0; } - pss.block = rs->last_seen_block; - pss.page = rs->last_page; - pss.complete_round = false; + pss_init(pss, rs->last_seen_block, rs->last_page); - do { - again = true; - found = get_queued_page(rs, &pss); - - if (!found) { - /* - * Recover previous precopy ramblock/offset if postcopy has - * preempted precopy. Otherwise find the next dirty bit. - */ - if (postcopy_preempt_triggered(rs)) { - postcopy_preempt_restore(rs, &pss, false); - found = true; - } else { - /* priority queue empty, so just search for something dirty */ - found = find_dirty_block(rs, &pss, &again); + while (true){ + if (!get_queued_page(rs, pss)) { + /* priority queue empty, so just search for something dirty */ + 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_file_credit_transfer(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) @@ -2668,14 +2350,27 @@ static void xbzrle_cleanup(void) XBZRLE_cache_unlock(); } +static void ram_bitmaps_destroy(void) +{ + RAMBlock *block; + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + g_free(block->clear_bmap); + block->clear_bmap = NULL; + g_free(block->bmap); + block->bmap = NULL; + g_free(block->file_bmap); + block->file_bmap = NULL; + } +} + static void ram_save_cleanup(void *opaque) { RAMState **rsp = opaque; - RAMBlock *block; /* 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) { @@ -2688,27 +2383,27 @@ static void ram_save_cleanup(void *opaque) } } - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - g_free(block->clear_bmap); - block->clear_bmap = NULL; - g_free(block->bmap); - block->bmap = NULL; - } + ram_bitmaps_destroy(); xbzrle_cleanup(); - compress_threads_save_cleanup(); + multifd_ram_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; - postcopy_preempt_reset(rs); - rs->postcopy_channel = RAM_CHANNEL_PRECOPY; + rs->xbzrle_started = false; } #define MAX_WAIT 50 /* ms, half buffered_file limit */ @@ -2894,11 +2589,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); @@ -2944,44 +2639,41 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) * For every allocation, we will try not to crash the VM if the * allocation failed. */ -static int xbzrle_init(void) +static bool xbzrle_init(Error **errp) { - Error *local_err = NULL; - - if (!migrate_use_xbzrle()) { - return 0; + if (!migrate_xbzrle()) { + return true; } XBZRLE_cache_lock(); XBZRLE.zero_target_page = g_try_malloc0(TARGET_PAGE_SIZE); if (!XBZRLE.zero_target_page) { - error_report("%s: Error allocating zero page", __func__); + error_setg(errp, "%s: Error allocating zero page", __func__); goto err_out; } XBZRLE.cache = cache_init(migrate_xbzrle_cache_size(), - TARGET_PAGE_SIZE, &local_err); + TARGET_PAGE_SIZE, errp); if (!XBZRLE.cache) { - error_report_err(local_err); goto free_zero_page; } XBZRLE.encoded_buf = g_try_malloc0(TARGET_PAGE_SIZE); if (!XBZRLE.encoded_buf) { - error_report("%s: Error allocating encoded_buf", __func__); + error_setg(errp, "%s: Error allocating encoded_buf", __func__); goto free_cache; } XBZRLE.current_buf = g_try_malloc(TARGET_PAGE_SIZE); if (!XBZRLE.current_buf) { - error_report("%s: Error allocating current_buf", __func__); + error_setg(errp, "%s: Error allocating current_buf", __func__); goto free_encoded_buf; } /* We are all good */ XBZRLE_cache_unlock(); - return 0; + return true; free_encoded_buf: g_free(XBZRLE.encoded_buf); @@ -2994,31 +2686,32 @@ free_zero_page: XBZRLE.zero_target_page = NULL; err_out: XBZRLE_cache_unlock(); - return -ENOMEM; + return false; } -static int ram_state_init(RAMState **rsp) +static bool ram_state_init(RAMState **rsp, Error **errp) { *rsp = g_try_new0(RAMState, 1); if (!*rsp) { - error_report("%s: Init ramstate fail", __func__); - return -1; + error_setg(errp, "%s: Init ramstate fail", __func__); + return false; } 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; + return true; } static void ram_list_init_bitmaps(void) @@ -3054,6 +2747,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)); } @@ -3073,42 +2769,53 @@ static void migration_bitmap_clear_discarded_pages(RAMState *rs) } } -static void ram_init_bitmaps(RAMState *rs) +static bool ram_init_bitmaps(RAMState *rs, Error **errp) { - /* For memory_global_dirty_log_start below. */ - qemu_mutex_lock_iothread(); + bool ret = true; + qemu_mutex_lock_ramlist(); WITH_RCU_READ_LOCK_GUARD() { ram_list_init_bitmaps(); /* 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); + ret = memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, errp); + if (!ret) { + goto out_unlock; + } + migration_bitmap_sync_precopy(false); } } +out_unlock: qemu_mutex_unlock_ramlist(); - qemu_mutex_unlock_iothread(); + + if (!ret) { + ram_bitmaps_destroy(); + return false; + } /* * After an eventual first bitmap sync, fixup the initial bitmap * containing all 1s to exclude any discarded pages from migration. */ migration_bitmap_clear_discarded_pages(rs); + return true; } -static int ram_init_all(RAMState **rsp) +static int ram_init_all(RAMState **rsp, Error **errp) { - if (ram_state_init(rsp)) { + if (!ram_state_init(rsp, errp)) { return -1; } - if (xbzrle_init()) { + if (!xbzrle_init(errp)) { ram_state_cleanup(rsp); return -1; } - ram_init_bitmaps(*rsp); + if (!ram_init_bitmaps(*rsp, errp)) { + return -1; + } return 0; } @@ -3135,7 +2842,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); } @@ -3151,10 +2858,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_running()) { return; } @@ -3194,6 +2900,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 @@ -3208,55 +2997,123 @@ void qemu_guest_free_page_hint(void *addr, size_t len) * * @f: QEMUFile where to send the data * @opaque: RAMState pointer + * @errp: pointer to Error*, to store an error if it happens. */ -static int ram_save_setup(QEMUFile *f, void *opaque) +static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) { RAMState **rsp = opaque; RAMBlock *block; - int ret; - - if (compress_threads_save_setup()) { - return -1; - } + int ret, max_hg_page_size; /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { - if (ram_init_all(rsp) != 0) { - compress_threads_save_cleanup(); + if (ram_init_all(rsp, errp) != 0) { 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 = multifd_send_sync_main(f); + ret = rdma_registration_start(f, RAM_CONTROL_SETUP); if (ret < 0) { + error_setg(errp, "%s: failed to start RDMA registration", __func__); + qemu_file_set_error(f, ret); return ret; } - qemu_put_be64(f, RAM_SAVE_FLAG_EOS); - qemu_fflush(f); + ret = rdma_registration_stop(f, RAM_CONTROL_SETUP); + if (ret < 0) { + error_setg(errp, "%s: failed to stop RDMA registration", __func__); + qemu_file_set_error(f, ret); + return ret; + } - return 0; + migration_ops = g_malloc0(sizeof(MigrationOps)); + + if (migrate_multifd()) { + multifd_ram_save_setup(); + 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_ram_flush_and_sync(); + bql_lock(); + if (ret < 0) { + error_setg(errp, "%s: multifd synchronization failed", __func__); + return ret; + } + + 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); + ret = qemu_fflush(f); + if (ret < 0) { + error_setg_errno(errp, -ret, "%s failed", __func__); + } + return ret; +} + +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); + } } /** @@ -3276,13 +3133,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) int64_t t0; int done = 0; - if (blk_mig_bulk_active()) { - /* Avoid transferring ram during bulk phase of block migration as - * the bulk phase will usually take a long time and transferring - * ram updates during that time is pointless. */ - goto out; - } - /* * We'll take this lock a little bit long, but it's okay for two reasons. * Firstly, the only possible other thread to take it is who calls @@ -3290,89 +3140,86 @@ 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; + + /* + * 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); - - postcopy_preempt_reset_channel(rs); /* * 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; + if (ret >= 0 && migration_is_running()) { + if (migrate_multifd() && migrate_multifd_flush_after_each_section() && + !migrate_mapped_ram()) { + ret = multifd_ram_flush_and_sync(); + 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; @@ -3386,7 +3233,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 @@ -3401,14 +3248,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(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; @@ -3418,58 +3270,78 @@ 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); + ret = rdma_registration_stop(f, RAM_CONTROL_FINISH); + if (ret < 0) { + qemu_file_set_error(f, ret); + return ret; + } } + ret = multifd_ram_flush_and_sync(); if (ret < 0) { return ret; } - postcopy_preempt_reset_channel(rs); + if (migrate_mapped_ram()) { + ram_save_file_bmap(f); - ret = multifd_send_sync_main(rs->f); - if (ret < 0) { - return ret; + 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; + } } 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(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; } } @@ -3545,7 +3417,7 @@ 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; } @@ -3579,6 +3451,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) { @@ -3596,15 +3480,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. @@ -3613,202 +3496,20 @@ 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); } } static void colo_init_ram_state(void) { - ram_state_init(&ram_state); + Error *local_err = NULL; + + if (!ram_state_init(&ram_state, &local_err)) { + error_report_err(local_err); + } } /* @@ -3849,8 +3550,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); @@ -3865,22 +3564,27 @@ int colo_init_ram_cache(void) void colo_incoming_start_dirty_log(void) { RAMBlock *block = NULL; + Error *local_err = 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); /* Discard this dirty bitmap record */ bitmap_zero(block->bmap, block->max_length >> TARGET_PAGE_BITS); } - memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION); + if (!memory_global_dirty_log_start(GLOBAL_DIRTY_MIGRATION, + &local_err)) { + error_report_err(local_err); + } } 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 */ @@ -3912,13 +3616,10 @@ void colo_release_ram_cache(void) * * @f: QEMUFile where to receive the data * @opaque: RAMState pointer + * @errp: pointer to Error*, to store an error if it happens. */ -static int ram_load_setup(QEMUFile *f, void *opaque) +static int ram_load_setup(QEMUFile *f, void *opaque, Error **errp) { - if (compress_threads_load_setup(f)) { - return -1; - } - xbzrle_load_setup(); ramblock_recv_map_init(); @@ -3934,7 +3635,6 @@ static int ram_load_cleanup(void *opaque) } xbzrle_load_cleanup(); - compress_threads_load_cleanup(); RAMBLOCK_FOREACH_NOT_IGNORED(rb) { g_free(rb->receivedmap); @@ -3985,7 +3685,6 @@ int ram_load_postcopy(QEMUFile *f, int channel) void *place_source = NULL; RAMBlock *block = NULL; uint8_t ch; - int len; addr = qemu_get_be64(f); @@ -4002,8 +3701,7 @@ int ram_load_postcopy(QEMUFile *f, int channel) addr &= TARGET_PAGE_MASK; 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)) { + if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE)) { block = ram_block_from_stream(mis, f, flags, channel); if (!block) { ret = -EINVAL; @@ -4066,16 +3764,18 @@ int ram_load_postcopy(QEMUFile *f, int channel) 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: @@ -4096,20 +3796,15 @@ int ram_load_postcopy(QEMUFile *f, int channel) TARGET_PAGE_SIZE); } break; - case RAM_SAVE_FLAG_COMPRESS_PAGE: - tmp_page->all_zero = false; - len = qemu_get_be32(f); - if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { - error_report("Invalid compressed data length: %d", len); - ret = -EINVAL; - break; - } - decompress_data_with_multi_threads(f, page_buffer, len); + 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" @@ -4118,11 +3813,6 @@ int ram_load_postcopy(QEMUFile *f, int channel) break; } - /* Got the whole host page, wait for decompress before placing. */ - if (place_needed) { - ret |= wait_for_decompress_done(); - } - /* Detect for any possible file errors */ if (!ret && qemu_file_get_error(f)) { ret = qemu_file_get_error(f); @@ -4143,12 +3833,6 @@ int ram_load_postcopy(QEMUFile *f, int channel) 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(); @@ -4166,7 +3850,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); @@ -4201,9 +3886,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 * @@ -4217,15 +4117,16 @@ void colo_flush_ram_cache(void) static int ram_load_precopy(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); - int flags = 0, ret = 0, invalid_flags = 0, len = 0, i = 0; - /* ADVISE is earlier, it shows the source has the postcopy capability on */ - bool postcopy_advised = postcopy_is_advised(); - if (!migrate_use_compression()) { - invalid_flags |= RAM_SAVE_FLAG_COMPRESS_PAGE; + int flags = 0, ret = 0, invalid_flags = 0, i = 0; + + 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; @@ -4241,20 +4142,24 @@ 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) { - if (flags & invalid_flags & RAM_SAVE_FLAG_COMPRESS_PAGE) { - error_report("Received an unexpected compressed page"); - } + error_report("Unexpected RAM flags: %d", flags & invalid_flags); ret = -EINVAL; break; } if (flags & (RAM_SAVE_FLAG_ZERO | RAM_SAVE_FLAG_PAGE | - RAM_SAVE_FLAG_COMPRESS_PAGE | RAM_SAVE_FLAG_XBZRLE)) { + RAM_SAVE_FLAG_XBZRLE)) { RAMBlock *block = ram_block_from_stream(mis, f, flags, RAM_CHANNEL_PRECOPY); @@ -4296,86 +4201,33 @@ 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: qemu_get_buffer(f, host, TARGET_PAGE_SIZE); break; - case RAM_SAVE_FLAG_COMPRESS_PAGE: - len = qemu_get_be32(f); - if (len < 0 || len > compressBound(TARGET_PAGE_SIZE)) { - error_report("Invalid compressed data length: %d", len); - ret = -EINVAL; - break; - } - decompress_data_with_multi_threads(f, host, len); - break; - case RAM_SAVE_FLAG_XBZRLE: if (load_xbzrle(f, addr, host) < 0) { error_report("Failed to decompress XBZRLE page at " @@ -4384,18 +4236,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); @@ -4405,7 +4269,6 @@ static int ram_load_precopy(QEMUFile *f) } } - ret |= wait_for_decompress_done(); return ret; } @@ -4431,6 +4294,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) * it will be necessary to reduce the granularity of this * critical section. */ + trace_ram_load_start(); WITH_RCU_READ_LOCK_GUARD() { if (postcopy_running) { /* @@ -4467,21 +4331,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(); @@ -4489,31 +4355,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; } /* @@ -4529,30 +4393,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; } /* @@ -4573,16 +4432,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) @@ -4612,7 +4473,8 @@ static SaveVMHandlers savevm_ram_handlers = { .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, @@ -4628,11 +4490,16 @@ 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 (!migration_is_idle()) { + if (migrate_ram_is_ignored(rb)) { + return; + } + + if (migration_is_running()) { /* * Precopy code on the source cannot deal with the size of RAM blocks * changing at random points in time - especially after sending the diff --git a/migration/ram.h b/migration/ram.h index c7af65ac74..0d1981f888 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -33,28 +33,26 @@ #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) \ if (!qemu_ram_is_migratable(block)) {} else +void ram_mig_init(void); int xbzrle_cache_resize(uint64_t new_size, Error **errp); uint64_t ram_bytes_remaining(void); 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); @@ -63,24 +61,31 @@ 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 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); void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); +void ramblock_recv_bitmap_set_offset(RAMBlock *rb, uint64_t byte_offset); 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); @@ -89,6 +94,4 @@ void ram_write_tracking_prepare(void); int ram_write_tracking_start(void); void ram_write_tracking_stop(void); -void dirty_sync_missed_zero_copy(void); - #endif diff --git a/migration/rdma.c b/migration/rdma.c index 94a55dd95b..855753c671 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -17,8 +17,10 @@ #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/error-report.h" @@ -35,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. */ @@ -82,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: @@ -130,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 @@ -265,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; @@ -272,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) { @@ -316,7 +293,6 @@ typedef struct RDMALocalBlocks { typedef struct RDMAContext { char *host; int port; - char *host_port; RDMAWorkRequestData wr_data[RDMA_WRID_MAX]; @@ -368,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. @@ -455,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; @@ -532,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) @@ -564,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; @@ -575,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, @@ -620,8 +605,6 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, block->nb_chunks); local->nb_blocks++; - - return 0; } /* @@ -635,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; } /* @@ -643,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; @@ -651,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; } @@ -707,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); } @@ -725,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--; } } @@ -745,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) { @@ -857,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) { @@ -890,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; } } @@ -915,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 @@ -937,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; } @@ -967,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: @@ -999,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); @@ -1045,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; } @@ -1073,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; } @@ -1120,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; @@ -1130,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; } @@ -1143,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; } @@ -1173,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; @@ -1194,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; @@ -1216,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; @@ -1242,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, @@ -1262,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; } /* @@ -1306,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; @@ -1322,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++; @@ -1357,18 +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]; -} - /* * Perform a non-optimized memory unregistration after every transfer * for demonstration purposes, only if pin-all is not requested. @@ -1382,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]; @@ -1431,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); @@ -1466,8 +1443,8 @@ static uint64_t qemu_rdma_make_wrid(uint64_t wr_id, uint64_t index, * (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; @@ -1481,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; } @@ -1509,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); @@ -1519,7 +1491,7 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, rdma->nb_sent--; } } 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; @@ -1537,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() @@ -1554,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; @@ -1573,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); } @@ -1597,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; } @@ -1638,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; @@ -1655,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; @@ -1664,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); } } @@ -1675,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; } @@ -1704,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); } } @@ -1725,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; } /* @@ -1734,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 = { @@ -1774,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 = { @@ -1807,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; } @@ -1817,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); @@ -1837,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; @@ -1890,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; } } @@ -1911,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; } /* @@ -1944,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); @@ -1975,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, @@ -1987,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); @@ -2009,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; @@ -2024,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 }; @@ -2081,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; } } @@ -2113,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; } @@ -2140,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 *) @@ -2168,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; } } @@ -2180,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; } } @@ -2214,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; @@ -2239,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; @@ -2247,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) { @@ -2265,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; @@ -2273,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]); @@ -2285,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; } /* @@ -2320,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; } @@ -2353,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; @@ -2361,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); @@ -2386,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) { @@ -2449,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 @@ -2466,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; } } @@ -2511,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; } @@ -2532,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, @@ -2570,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; } @@ -2587,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; } @@ -2610,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; } @@ -2630,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; } @@ -2666,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; } @@ -2709,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*/ @@ -2733,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; } @@ -2774,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; @@ -2812,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; @@ -2854,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; @@ -2881,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; @@ -2899,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) { @@ -2928,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; } } @@ -3064,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, @@ -3091,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); } } @@ -3174,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; } @@ -3210,15 +3199,15 @@ qio_channel_rdma_shutdown(QIOChannel *ioc, * * @size : Number of bytes to transfer * - * @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, - 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(qemu_file_get_ioc(f)); + Error *err = NULL; RDMAContext *rdma; int ret; @@ -3226,13 +3215,11 @@ static size_t qemu_rdma_save_page(QEMUFile *f, 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); @@ -3242,24 +3229,12 @@ static size_t qemu_rdma_save_page(QEMUFile *f, * 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); + ret = qemu_rdma_write(rdma, block_offset, offset, size, &err); if (ret < 0) { - error_report("rdma migration: write error! %d", ret); + error_report_err(err); 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; - } - /* * Drain the Completion Queue if possible, but do not block, * just poll. @@ -3269,9 +3244,10 @@ static size_t qemu_rdma_save_page(QEMUFile *f, */ 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; } @@ -3284,9 +3260,10 @@ static size_t qemu_rdma_save_page(QEMUFile *f, 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; } @@ -3298,8 +3275,27 @@ static size_t qemu_rdma_save_page(QEMUFile *f, } 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; } @@ -3308,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; } @@ -3341,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, @@ -3348,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; } @@ -3363,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; @@ -3382,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; } /* @@ -3415,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); @@ -3459,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; } @@ -3479,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; } @@ -3490,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) @@ -3513,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, @@ -3525,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; @@ -3535,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; } @@ -3571,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 @@ -3603,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; } } @@ -3622,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); @@ -3634,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, @@ -3647,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; @@ -3670,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) { @@ -3687,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)); @@ -3703,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); @@ -3715,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]); @@ -3754,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 */ @@ -3820,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; @@ -3830,73 +3827,57 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) return 0; } -static int rdma_load_hook(QEMUFile *f, uint64_t flags, void *data) +int rdma_registration_start(QEMUFile *f, uint64_t flags) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); - switch (flags) { - case RAM_CONTROL_BLOCK_REG: - return rdma_block_notification_handle(rioc, data); - - case RAM_CONTROL_HOOK: - return qemu_rdma_registration_handle(f, rioc); - - default: - /* Shouldn't be called with any other values */ - abort(); - } -} - -static int qemu_rdma_registration_start(QEMUFile *f, - uint64_t flags, void *data) -{ - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); - 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, - uint64_t flags, void *data) +int rdma_registration_stop(QEMUFile *f, uint64_t flags) { - QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(qemu_file_get_ioc(f)); + 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; @@ -3905,10 +3886,10 @@ static int qemu_rdma_registration_stop(QEMUFile *f, 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. @@ -3920,10 +3901,11 @@ static int qemu_rdma_registration_stop(QEMUFile *f, */ 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); @@ -3941,28 +3923,29 @@ static int qemu_rdma_registration_stop(QEMUFile *f, */ 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; @@ -3970,32 +3953,22 @@ static int qemu_rdma_registration_stop(QEMUFile *f, } } - 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); @@ -4040,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_file_new_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_file_new_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; } @@ -4068,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; } @@ -4086,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(); @@ -4114,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) { @@ -4194,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; } @@ -4213,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 e3a7fab2f4..9b85b664da 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -31,6 +31,7 @@ #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" @@ -42,16 +43,15 @@ #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" #include "qemu/error-report.h" #include "sysemu/cpus.h" #include "exec/memory.h" #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 +66,8 @@ #include "net/announce.h" #include "qemu/yank.h" #include "yank_functions.h" +#include "sysemu/qtest.h" +#include "options.h" #include "ui/xemu-snapshots.h" @@ -116,7 +118,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 @@ -236,12 +238,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; @@ -254,7 +259,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++; } } @@ -276,7 +281,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; } } @@ -326,7 +331,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), @@ -435,7 +440,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() } @@ -451,7 +456,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, @@ -468,8 +473,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) { /* @@ -496,7 +501,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() } @@ -509,12 +514,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, @@ -536,6 +541,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"); @@ -545,11 +553,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); } } @@ -587,10 +595,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, ""); @@ -599,7 +608,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; } @@ -711,6 +720,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) { @@ -805,6 +826,43 @@ 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++; + } +} + + int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, @@ -850,6 +908,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; @@ -881,11 +944,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_file_total_transferred_fast(f); + uint64_t old_offset = qemu_file_transferred(f); se->ops->save_state(f, se->opaque); - size = qemu_file_total_transferred_fast(f) - old_offset; + uint64_t size = qemu_file_transferred(f) - old_offset; if (vmdesc) { json_writer_int64(vmdesc, "size", size); @@ -899,17 +960,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) */ @@ -943,6 +993,45 @@ static void save_section_footer(QEMUFile *f, SaveStateEntry *se) } } +static int vmstate_save(QEMUFile *f, SaveStateEntry *se, JSONWriter *vmdesc, + Error **errp) +{ + int ret; + + 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, + errp); + if (ret) { + 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. @@ -998,10 +1087,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; } @@ -1139,13 +1232,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); } } @@ -1163,14 +1270,52 @@ 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; +} + +int qemu_savevm_state_setup(QEMUFile *f, Error **errp) +{ + ERRP_GUARD(); + MigrationState *ms = migrate_get_current(); + SaveStateEntry *se; + 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, errp); + if (ret) { + migrate_set_error(ms, *errp); + qemu_file_set_error(f, ret); + break; + } + continue; + } + if (!se->ops || !se->ops->save_setup) { continue; } @@ -1181,7 +1326,7 @@ void qemu_savevm_state_setup(QEMUFile *f) } save_section_header(f, se, QEMU_VM_SECTION_START); - ret = se->ops->save_setup(f, se->opaque); + ret = se->ops->save_setup(f, se->opaque, errp); save_section_footer(f, se); if (ret < 0) { qemu_file_set_error(f, ret); @@ -1189,9 +1334,12 @@ void qemu_savevm_state_setup(QEMUFile *f) } } - if (precopy_notify(PRECOPY_NOTIFY_SETUP, &local_err)) { - error_report_err(local_err); + if (ret) { + return ret; } + + /* TODO: Should we check that errp is set in case of failure ? */ + return precopy_notify(PRECOPY_NOTIFY_SETUP, errp); } int qemu_savevm_state_resume_prepare(MigrationState *s) @@ -1228,7 +1376,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) { @@ -1253,7 +1402,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); @@ -1269,16 +1418,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) @@ -1330,6 +1475,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; @@ -1346,6 +1492,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); @@ -1357,8 +1505,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; } @@ -1366,41 +1519,33 @@ 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; + Error *local_err = NULL; 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); + ret = vmstate_save(f, se, vmdesc, &local_err); if (ret) { + migrate_set_error(ms, local_err); + error_report_err(local_err); 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) { @@ -1408,8 +1553,10 @@ 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_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; } @@ -1429,6 +1576,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; } @@ -1465,28 +1618,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) { @@ -1494,9 +1642,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); } } @@ -1523,28 +1690,26 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) MigrationState *ms = migrate_get_current(); MigrationStatus status; - if (migration_is_running(ms->state)) { - error_setg(errp, QERR_MIGRATION_ACTIVE); + if (migration_is_running()) { + error_setg(errp, "There's a migration process in progress"); return -EINVAL; } - if (migrate_use_block()) { - error_setg(errp, "Block migration and snapshots are incompatible"); - return -EINVAL; + ret = migrate_init(ms, errp); + if (ret) { + return ret; } - - migrate_init(ms); - memset(&ram_counters, 0, sizeof(ram_counters)); - memset(&compression_counters, 0, sizeof(compression_counters)); ms->to_dst_file = f; #ifdef XBOX xemu_snapshots_save_extra_data(f); #endif - qemu_mutex_unlock_iothread(); + qemu_savevm_state_header(f); - qemu_savevm_state_setup(f); - qemu_mutex_lock_iothread(); + ret = qemu_savevm_state_setup(f, errp); + if (ret) { + goto cleanup; + } while (qemu_file_get_error(f) == 0) { if (qemu_savevm_state_iterate(f, false) > 0) { @@ -1557,10 +1722,11 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) qemu_savevm_state_complete_precopy(f, false, false); ret = qemu_file_get_error(f); } - qemu_savevm_state_cleanup(); if (ret != 0) { error_setg_errno(errp, -ret, "Error while writing VM state"); } +cleanup: + qemu_savevm_state_cleanup(); if (ret != 0) { status = MIGRATION_STATUS_FAILED; @@ -1585,6 +1751,8 @@ void qemu_savevm_live_state(QEMUFile *f) int qemu_save_device_state(QEMUFile *f) { + MigrationState *ms = migrate_get_current(); + Error *local_err = NULL; SaveStateEntry *se; if (!migration_in_colo_state()) { @@ -1599,21 +1767,12 @@ 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); + ret = vmstate_save(f, se, NULL, &local_err); if (ret) { + migrate_set_error(ms, local_err); + error_report_err(local_err); return ret; } - - save_section_footer(f, se); } qemu_put_byte(f, QEMU_VM_EOF); @@ -1683,7 +1842,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; } @@ -1903,7 +2063,6 @@ static void *postcopy_ram_listen_thread(void *opaque) * got a bad migration state). */ migration_incoming_state_destroy(); - qemu_loadvm_state_cleanup(); rcu_unregister_thread(); mis->have_listen_thread = false; @@ -1958,7 +2117,8 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) } mis->have_listen_thread = true; - postcopy_thread_create(mis, &mis->listen_thread, "postcopy/listen", + postcopy_thread_create(mis, &mis->listen_thread, + MIGRATION_THREAD_DST_LISTEN, postcopy_ram_listen_thread, QEMU_THREAD_DETACHED); trace_loadvm_postcopy_handle_listen("return"); @@ -1970,18 +2130,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. */ @@ -1992,7 +2152,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(); @@ -2004,9 +2164,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 */ @@ -2021,8 +2179,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 @@ -2123,7 +2280,11 @@ static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) qemu_sem_post(&mis->postcopy_pause_sem_fault); if (migrate_postcopy_preempt()) { - /* The channel should already be setup again; make sure of it */ + /* + * 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); @@ -2172,6 +2333,27 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) 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); qemu_fclose(packf); @@ -2283,6 +2465,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: @@ -2368,9 +2565,11 @@ 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, 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; @@ -2419,12 +2618,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; } @@ -2433,8 +2643,10 @@ 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, 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; @@ -2459,12 +2671,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; } @@ -2496,22 +2719,38 @@ static int qemu_loadvm_state_header(QEMUFile *f) if (migrate_get_current()->send_configuration) { if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) { error_report("Configuration section missing"); - qemu_loadvm_state_cleanup(); return -EINVAL; } ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0); if (ret) { - qemu_loadvm_state_cleanup(); return ret; } } return 0; } -static int qemu_loadvm_state_setup(QEMUFile *f) +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, Error **errp) +{ + ERRP_GUARD(); + SaveStateEntry *se; int ret; trace_loadvm_state_setup(); @@ -2525,10 +2764,11 @@ static int qemu_loadvm_state_setup(QEMUFile *f) } } - ret = se->ops->load_setup(f, se->opaque); + ret = se->ops->load_setup(f, se->opaque, errp); if (ret < 0) { + error_prepend(errp, "Load state of device %s failed: ", + se->idstr); qemu_file_set_error(f, ret); - error_report("Load state of device %s failed", se->idstr); return ret; } } @@ -2589,7 +2829,8 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) qemu_mutex_unlock(&mis->postcopy_prio_thread_mutex); } - migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + /* 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 */ @@ -2608,9 +2849,9 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) error_report("Detected IO failure for postcopy. " "Migration paused."); - while (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + do { qemu_sem_wait(&mis->postcopy_pause_sem_dst); - } + } while (postcopy_is_paused(mis->state)); trace_postcopy_pause_incoming_continued(); @@ -2635,14 +2876,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, 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, section_type); if (ret < 0) { goto out; } @@ -2714,10 +2955,15 @@ int qemu_loadvm_state(QEMUFile *f) return ret; } - if (qemu_loadvm_state_setup(f) != 0) { + if (qemu_loadvm_state_setup(f, &local_err) != 0) { + error_report_err(local_err); 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); @@ -2726,7 +2972,10 @@ int qemu_loadvm_state(QEMUFile *f) trace_qemu_loadvm_state_post_main(ret); if (mis->have_listen_thread) { - /* Listen thread still going, can't clean up yet */ + /* + * Postcopy listen thread still going, don't synchronize the + * cpus yet. + */ return ret; } @@ -2769,7 +3018,6 @@ int qemu_loadvm_state(QEMUFile *f) } } - qemu_loadvm_state_cleanup(); cpu_synchronize_all_post_init(); return ret; @@ -2791,6 +3039,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) { @@ -2798,10 +3064,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(); @@ -2844,21 +3109,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 */ @@ -2885,7 +3141,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_file_total_transferred(f); + vm_state_size = qemu_file_transferred(f); ret2 = qemu_fclose(f); if (ret < 0) { goto the_end; @@ -2895,14 +3151,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) { @@ -2913,15 +3161,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; } @@ -2953,7 +3195,7 @@ void qmp_xen_save_devices_state(const char *filename, bool has_live, bool live, object_unref(OBJECT(ioc)); ret = qemu_save_device_state(f); if (ret < 0 || qemu_fclose(f) < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "saving Xen device state failed"); } else { /* libxl calls the QMP command "stop" before calling * "xen-save-devices-state" and in case of migration failure, libxl @@ -3002,7 +3244,7 @@ void qmp_xen_load_devices_state(const char *filename, Error **errp) ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "loading Xen device state failed"); } migration_incoming_state_destroy(); } @@ -3014,7 +3256,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)) { @@ -3034,13 +3275,11 @@ 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) { + error_setg(errp, "Snapshot can not be found"); return false; } else if (sn.vm_state_size == 0) { error_setg(errp, "This is a disk-only snapshot. Revert to it " @@ -3076,10 +3315,8 @@ bool load_snapshot(const char *name, const char *vmstate, 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(); @@ -3095,6 +3332,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) { @@ -3163,16 +3408,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..9ec96a995c 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -31,7 +31,8 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_non_migratable_list(strList **reasons); -void qemu_savevm_state_setup(QEMUFile *f); +int qemu_savevm_state_prepare(Error **errp); +int qemu_savevm_state_setup(QEMUFile *f, Error **errp); bool qemu_savevm_state_guest_unplug_pending(void); int qemu_savevm_state_resume_prepare(MigrationState *s); void qemu_savevm_state_header(QEMUFile *f); @@ -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 e6fdf3c5e1..5ec65b8c03 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -27,6 +27,9 @@ #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,35 +42,6 @@ void socket_send_channel_create(QIOTaskFunc f, void *data) f, data, NULL, NULL); } -QIOChannel *socket_send_channel_create_sync(Error **errp) -{ - QIOChannelSocket *sioc = qio_channel_socket_new(); - - if (!outgoing_args.saddr) { - object_unref(OBJECT(sioc)); - error_setg(errp, "Initial sock address not set!"); - return NULL; - } - - if (qio_channel_socket_connect_sync(sioc, outgoing_args.saddr, errp) < 0) { - object_unref(OBJECT(sioc)); - return NULL; - } - - return QIO_CHANNEL(sioc); -} - -int socket_send_channel_destroy(QIOChannel *send) -{ - /* Remove channel */ - object_unref(OBJECT(send)); - if (outgoing_args.saddr) { - qapi_free_SocketAddress(outgoing_args.saddr); - outgoing_args.saddr = NULL; - } - return 0; -} - struct SocketConnectData { MigrationState *s; char *hostname; @@ -97,7 +71,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"); } @@ -107,19 +81,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); @@ -134,16 +108,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, @@ -171,9 +141,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(); @@ -182,7 +151,7 @@ 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; @@ -212,13 +181,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 dc54df4e6c..04ebbe95a1 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -19,13 +19,14 @@ #include "io/channel.h" #include "io/task.h" +#include "qemu/sockets.h" void socket_send_channel_create(QIOTaskFunc f, void *data); -QIOChannel *socket_send_channel_create_sync(Error **errp); -int socket_send_channel_destroy(QIOChannel *send); -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 73e8c9d3c2..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; } @@ -169,7 +158,7 @@ void migration_tls_channel_connect(MigrationState *s, bool migrate_channel_requires_tls_upgrade(QIOChannel *ioc) { - if (!migrate_use_tls()) { + if (!migrate_tls()) { return false; } diff --git a/migration/tls.h b/migration/tls.h index 98e23c9b0e..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); diff --git a/migration/trace-events b/migration/trace-events index 57003edcbd..bb0e0cc6dc 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" @@ -54,7 +58,7 @@ postcopy_page_req_sync(void *host_addr) "sync page req %p" vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d" vmstate_load_state(const char *name, int version_id) "%s v%d" vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d" -vmstate_load_state_field(const char *name, const char *field) "%s:%s" +vmstate_load_state_field(const char *name, const char *field, bool exists) "%s:%s exists=%d" vmstate_n_elems(const char *name, int n_elems) "%s: %d" vmstate_subsection_load(const char *parent) "%s" vmstate_subsection_load_bad(const char *parent, const char *sub, const char *sub2) "%s: %s/%s" @@ -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" @@ -91,6 +96,7 @@ 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(int channel, uint64_t addr, int flags) "chan=%d addr=0x%" PRIx64 " flags=0x%x" @@ -109,6 +115,7 @@ colo_flush_ram_cache_end(void) "" save_xbzrle_page_skipping(void) "" save_xbzrle_page_overflow(void) "" ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" +ram_load_start(void) "" 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" @@ -121,36 +128,37 @@ 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_unfill(uint8_t id, uint64_t packet_num, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " 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) "channel %u packets %" 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_fill(uint8_t id, uint64_t packet_num, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " flags 0x%x next packet size %u" +multifd_send_ram_fill(uint8_t id, uint32_t normal, uint32_t zero) "channel %u normal pages %u zero pages %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) "channel %u packets %" 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_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) "" @@ -160,7 +168,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) "" @@ -179,12 +187,16 @@ 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" migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p" @@ -199,12 +211,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)" @@ -213,27 +227,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" @@ -252,6 +252,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) "" @@ -303,6 +317,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" @@ -330,7 +348,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) "" @@ -341,8 +359,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" @@ -355,8 +373,13 @@ 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 migration_pagecache_insert(void) "Error allocating page" + +# cpu-throttle.c +cpu_throttle_set(int new_throttle_pct) "set guest CPU throttled by %d%%" +cpu_throttle_dirty_sync(void) "" diff --git a/migration/vmstate.c b/migration/vmstate.c index 924494bda3..fa002b24e8 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" @@ -21,10 +22,35 @@ #include "trace.h" static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, JSONWriter *vmdesc); + void *opaque, JSONWriter *vmdesc, + Error **errp); 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 +123,15 @@ 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)) { + bool exists = vmstate_field_exists(vmsd, field, opaque, version_id); + trace_vmstate_load_state_field(vmsd->name, field->name, exists); + if (exists) { void *first_elem = opaque + field->offset; int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); @@ -154,8 +178,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 +327,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 +340,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 +361,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 +373,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 +389,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_file_total_transferred_fast(f); + old_offset = qemu_file_transferred(f); if (field->flags & VMS_ARRAY_OF_POINTER) { assert(curr_elem); curr_elem = *(void **)curr_elem; @@ -376,22 +405,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_file_total_transferred_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 */ @@ -408,30 +436,34 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, } field++; } + assert(field->flags == VMS_END); if (vmdesc) { json_writer_end_array(vmdesc); } - ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc); + ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc, errp); if (vmsd->post_save) { int ps_ret = vmsd->post_save(opaque); - if (!ret) { + if (!ret && ps_ret) { ret = ps_ret; + error_setg(errp, "post-save failed: %s", vmsd->name); } } return ret; } 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; } @@ -449,7 +481,7 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, len = qemu_peek_byte(f, 1); if (len < strlen(vmsd->name) + 1) { - /* subsection name has be be "section_name/a" */ + /* subsection name has to be "section_name/a" */ trace_vmstate_subsection_load_bad(vmsd->name, "(short)", ""); return 0; } @@ -488,15 +520,16 @@ static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, } static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, JSONWriter *vmdesc) + void *opaque, JSONWriter *vmdesc, + Error **errp) { - 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; @@ -516,7 +549,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, qemu_put_byte(f, len); qemu_put_buffer(f, (uint8_t *)vmsdsub->name, len); qemu_put_be32(f, vmsdsub->version_id); - ret = vmstate_save_state(f, vmsdsub, opaque, vmdesc); + ret = vmstate_save_state_with_err(f, vmsdsub, opaque, vmdesc, errp); if (ret) { return ret; } 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..b5416b5b5d --- /dev/null +++ b/monitor/fds.c @@ -0,0 +1,515 @@ +/* + * 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; + 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_free(MonFdset *mon_fdset) +{ + QLIST_REMOVE(mon_fdset, next); + g_free(mon_fdset); +} + +static void monitor_fdset_free_if_empty(MonFdset *mon_fdset) +{ + /* + * Only remove an empty fdset. The fds are owned by the user and + * should have been removed with qmp_remove_fd(). The dup_fds are + * owned by QEMU and should have been removed with qemu_close(). + */ + if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { + monitor_fdset_free(mon_fdset); + } +} + +static void monitor_fdset_fd_free(MonFdsetFd *mon_fdset_fd) +{ + close(mon_fdset_fd->fd); + g_free(mon_fdset_fd->opaque); + QLIST_REMOVE(mon_fdset_fd, next); + g_free(mon_fdset_fd); +} + +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_free_if_empty(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, *mon_fdset_fd_next; + 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_SAFE(mon_fdset_fd, &mon_fdset->fds, next, + mon_fdset_fd_next) { + if (has_fd) { + if (mon_fdset_fd->fd != fd) { + continue; + } + monitor_fdset_fd_free(mon_fdset_fd); + break; + } else { + monitor_fdset_fd_free(mon_fdset_fd); + } + } + if (has_fd && !mon_fdset_fd) { + goto error; + } + monitor_fdset_free_if_empty(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->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, Error **errp) +{ +#ifdef _WIN32 + error_setg(errp, "Platform does not support fd passing (fdset)"); + 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; + int mask = O_ACCMODE; + +#ifdef O_DIRECT + mask |= O_DIRECT; +#endif + + 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) { + error_setg(errp, "Failed to read file status flags for fd=%d", + mon_fdset_fd->fd); + return -1; + } + + if ((flags & mask) == (mon_fd_flags & mask)) { + fd = mon_fdset_fd->fd; + break; + } + } + + if (fd == -1) { + errno = EACCES; + error_setg(errp, + "Failed to find file descriptor with matching flags=0x%x", + flags); + return -1; + } + + dup_fd = qemu_dup_flags(fd, flags); + if (dup_fd == -1) { + error_setg(errp, "Failed to dup() given file descriptor fd=%d", fd); + 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; + } + + error_setg(errp, "Failed to find fdset /dev/fdset/%" PRId64, fdset_id); + errno = ENOENT; + return -1; +#endif +} + +void monitor_fdset_dup_fd_remove(int dup_fd) +{ + 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) { + QLIST_REMOVE(mon_fdset_fd_dup, next); + g_free(mon_fdset_fd_dup); + monitor_fdset_free_if_empty(mon_fdset); + return; + } + } + } +} + +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 01b789a79e..f601d06ab8 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -14,55 +14,21 @@ */ #include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "exec/ioport.h" +#include "exec/gdbstub.h" +#include "gdbstub/enums.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-stats.h" -#include "qapi/qapi-commands-tpm.h" -#include "qapi/qapi-commands-ui.h" -#include "qapi/qapi-commands-virtio.h" -#include "qapi/qapi-visit-virtio.h" -#include "qapi/qapi-visit-net.h" -#include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" -#include "qapi/string-input-visitor.h" -#include "qapi/string-output-visitor.h" -#include "qom/object_interfaces.h" -#include "ui/console.h" #include "qemu/cutils.h" -#include "qemu/error-report.h" -#include "hw/core/cpu.h" -#include "hw/intc/intc.h" -#include "migration/snapshot.h" -#include "migration/misc.h" - -#ifdef CONFIG_SPICE -#include -#endif +#include "qemu/log.h" +#include "sysemu/sysemu.h" bool hmp_handle_error(Monitor *mon, Error *err) { @@ -74,28 +40,21 @@ bool hmp_handle_error(Monitor *mon, Error *err) } /* - * Produce a strList from a comma separated list. - * A NULL or empty input string return NULL. + * Split @str at comma. + * A null @str defaults to "". */ -static strList *strList_from_comma_list(const char *in) +strList *hmp_split_at_comma(const char *str) { + char **split = g_strsplit(str ?: "", ",", -1); strList *res = NULL; strList **tail = &res; + int i; - while (in && in[0]) { - char *comma = strchr(in, ','); - char *value; - - if (comma) { - value = g_strndup(in, comma - in); - in = comma + 1; /* skip the , */ - } else { - value = g_strdup(in); - in = NULL; - } - QAPI_LIST_APPEND(tail, value); + for (i = 0; split[i]; i++) { + QAPI_LIST_APPEND(tail, split[i]); } + g_free(split); return res; } @@ -104,7 +63,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); @@ -123,769 +82,6 @@ void hmp_info_version(Monitor *mon, const QDict *qdict) qapi_free_VersionInfo(info); } -void hmp_info_kvm(Monitor *mon, const QDict *qdict) -{ - KvmInfo *info; - - info = qmp_query_kvm(NULL); - monitor_printf(mon, "kvm support: "); - if (info->present) { - monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled"); - } else { - monitor_printf(mon, "not compiled\n"); - } - - qapi_free_KvmInfo(info); -} - -void hmp_info_status(Monitor *mon, const QDict *qdict) -{ - StatusInfo *info; - - info = qmp_query_status(NULL); - - monitor_printf(mon, "VM status: %s%s", - info->running ? "running" : "paused", - info->singlestep ? " (single step mode)" : ""); - - if (!info->running && info->status != RUN_STATE_PAUSED) { - monitor_printf(mon, " (%s)", RunState_str(info->status)); - } - - monitor_printf(mon, "\n"); - - qapi_free_StatusInfo(info); -} - -void hmp_info_uuid(Monitor *mon, const QDict *qdict) -{ - UuidInfo *info; - - info = qmp_query_uuid(NULL); - monitor_printf(mon, "%s\n", info->UUID); - qapi_free_UuidInfo(info); -} - -void hmp_info_chardev(Monitor *mon, const QDict *qdict) -{ - ChardevInfoList *char_info, *info; - - char_info = qmp_query_chardev(NULL); - for (info = char_info; info; info = info->next) { - monitor_printf(mon, "%s: filename=%s\n", info->value->label, - info->value->filename); - } - - qapi_free_ChardevInfoList(char_info); -} - -void hmp_info_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); -} - -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->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->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 = socket_uri(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_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->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; - InterruptStatsProviderClass *k; - Monitor *mon = opaque; - - if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) { - intc = INTERRUPT_STATS_PROVIDER(obj); - k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj); - if (k->print_info) { - k->print_info(intc, mon); - } else { - monitor_printf(mon, "Interrupt controller information not available for %s.\n", - object_get_typename(obj)); - } - } - - return 0; -} - -void hmp_info_pic(Monitor *mon, const QDict *qdict) -{ - object_child_foreach_recursive(object_get_root(), - 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); @@ -916,21 +112,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; @@ -951,74 +138,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; @@ -1027,433 +146,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; - 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"); @@ -1461,169 +153,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"); @@ -1632,6 +176,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) { @@ -1642,242 +187,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); @@ -1898,866 +207,230 @@ 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); - } - - qapi_free_MemoryInfo(info); - } - hmp_handle_error(mon, err); -} - -static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) -{ - const char *unit = NULL; - monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type), - value->has_unit || value->exponent ? ", " : ""); - - if (value->has_unit) { - if (value->unit == STATS_UNIT_SECONDS) { - unit = "s"; - } else if (value->unit == STATS_UNIT_BYTES) { - unit = "B"; + if (!strcmp(items, "none")) { + mask = 0; + } else { + mask = qemu_str_to_log_mask(items); + if (!mask) { + hmp_help_cmd(mon, "log"); + return; } } - if (unit && value->base == 10 && - value->exponent >= -18 && value->exponent <= 18 && - value->exponent % 3 == 0) { - monitor_puts(mon, si_prefix(value->exponent)); - } else if (unit && value->base == 2 && - value->exponent >= 0 && value->exponent <= 60 && - value->exponent % 10 == 0) { - - monitor_puts(mon, iec_binary_prefix(value->exponent)); - } else if (value->exponent) { - /* Use exponential notation and write the unit's English name */ - monitor_printf(mon, "* %d^%d%s", - value->base, value->exponent, - value->has_unit ? " " : ""); - unit = NULL; - } - - if (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"); - } + if (!qemu_set_log(mask, &err)) { + error_report_err(err); } } -/* Create the StatsFilter that is needed for an "info stats" invocation. */ -static StatsFilter *stats_filter(StatsTarget target, const char *names, - int cpu_index, StatsProvider provider) +void hmp_gdbserver(Monitor *mon, const QDict *qdict) { - StatsFilter *filter = g_malloc0(sizeof(*filter)); - StatsProvider provider_idx; - StatsRequestList *request_list = NULL; + const char *device = qdict_get_try_str(qdict, "device"); + if (!device) { + device = "tcp::" DEFAULT_GDBSTUB_PORT; + } - filter->target = target; - switch (target) { - case STATS_TARGET_VM: + 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 STATS_TARGET_VCPU: - { - strList *vcpu_list = NULL; - CPUState *cpu = qemu_get_cpu(cpu_index); - char *canonical_path = object_get_canonical_path(OBJECT(cpu)); - - QAPI_LIST_PREPEND(vcpu_list, canonical_path); - filter->u.vcpu.has_vcpus = true; - filter->u.vcpu.vcpus = vcpu_list; + case 'x': + monitor_printf(mon, "%#" HWADDR_PRIx, val); break; - } - default: - break; - } - - if (!names && provider == STATS_PROVIDER__MAX) { - return filter; - } - - /* - * "info stats" can only query either one or all the providers. Querying - * by name, but not by provider, requires the creation of one filter per - * provider. - */ - for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) { - if (provider == STATS_PROVIDER__MAX || provider == provider_idx) { - StatsRequest *request = g_new0(StatsRequest, 1); - request->provider = provider_idx; - if (names && !g_str_equal(names, "*")) { - request->has_names = true; - request->names = strList_from_comma_list(names); - } - QAPI_LIST_PREPEND(request_list, request); - } - } - - filter->has_providers = true; - filter->providers = request_list; - return filter; -} - -void hmp_info_stats(Monitor *mon, const QDict *qdict) -{ - const char *target_str = qdict_get_str(qdict, "target"); - const char *provider_str = qdict_get_try_str(qdict, "provider"); - const char *names = qdict_get_try_str(qdict, "names"); - - StatsProvider provider = STATS_PROVIDER__MAX; - StatsTarget target; - Error *err = NULL; - g_autoptr(StatsSchemaList) schema = NULL; - g_autoptr(StatsResultList) stats = NULL; - g_autoptr(StatsFilter) filter = NULL; - StatsResultList *entry; - - target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err); - if (err) { - monitor_printf(mon, "invalid stats target %s\n", target_str); - goto exit_no_print; - } - if (provider_str) { - provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err); - if (err) { - monitor_printf(mon, "invalid stats provider %s\n", provider_str); - goto exit_no_print; - } - } - - schema = qmp_query_stats_schemas(provider_str ? true : false, - provider, &err); - if (err) { - goto exit; - } - - switch (target) { - case STATS_TARGET_VM: - filter = stats_filter(target, names, -1, provider); - break; - case STATS_TARGET_VCPU: {} - int cpu_index = monitor_get_cpu_index(mon); - filter = stats_filter(target, names, cpu_index, provider); + case 'u': + monitor_printf(mon, "%" HWADDR_PRIu, val); break; default: - abort(); - } - - stats = qmp_query_stats(filter, &err); - if (err) { - goto exit; - } - for (entry = stats; entry; entry = entry->next) { - print_stats_results(mon, target, provider_str == NULL, entry->value, schema); - } - -exit: - if (err) { - monitor_printf(mon, "%s\n", error_get_pretty(err)); - } -exit_no_print: - error_free(err); -} - -static void hmp_virtio_dump_protocols(Monitor *mon, - VhostDeviceProtocols *pcol) -{ - strList *pcol_list = pcol->protocols; - while (pcol_list) { - monitor_printf(mon, "\t%s", pcol_list->value); - pcol_list = pcol_list->next; - if (pcol_list != NULL) { - monitor_printf(mon, ",\n"); - } + case 'd': + monitor_printf(mon, "%" HWADDR_PRId, val); + break; + case 'c': + monitor_printc(mon, val); + break; } monitor_printf(mon, "\n"); - if (pcol->has_unknown_protocols) { - monitor_printf(mon, " unknown-protocols(0x%016"PRIx64")\n", - pcol->unknown_protocols); +} + +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; } } -static void hmp_virtio_dump_status(Monitor *mon, - VirtioDeviceStatus *status) +void hmp_boot_set(Monitor *mon, const QDict *qdict) { - strList *status_list = status->statuses; - while (status_list) { - monitor_printf(mon, "\t%s", status_list->value); - status_list = status_list->next; - if (status_list != NULL) { - monitor_printf(mon, ",\n"); - } - } - monitor_printf(mon, "\n"); - if (status->has_unknown_statuses) { - monitor_printf(mon, " unknown-statuses(0x%016"PRIx32")\n", - status->unknown_statuses); + 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_virtio_dump_features(Monitor *mon, - VirtioDeviceFeatures *features) +void hmp_info_mtree(Monitor *mon, const QDict *qdict) { - 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"); - } - } + bool flatview = qdict_get_try_bool(qdict, "flatview", false); + bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); + bool owner = qdict_get_try_bool(qdict, "owner", false); + bool disabled = qdict_get_try_bool(qdict, "disabled", false); - monitor_printf(mon, "\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); - } + mtree_info(flatview, dispatch_tree, owner, disabled); } -void hmp_virtio_query(Monitor *mon, const QDict *qdict) +#if defined(CONFIG_FDT) +void hmp_dumpdtb(Monitor *mon, const QDict *qdict) { - Error *err = NULL; - VirtioInfoList *list = qmp_x_query_virtio(&err); - VirtioInfoList *node; + const char *filename = qdict_get_str(qdict, "filename"); + Error *local_err = NULL; - if (err != NULL) { - hmp_handle_error(mon, err); + qmp_dumpdtb(filename, &local_err); + + if (hmp_handle_error(mon, local_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->has_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->has_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); + monitor_printf(mon, "dtb dumped to %s", filename); } +#endif diff --git a/monitor/hmp-target.c b/monitor/hmp-target.c new file mode 100644 index 0000000000..1eb72ac1bf --- /dev/null +++ b/monitor/hmp-target.c @@ -0,0 +1,178 @@ +/* + * QEMU monitor, target-dependent part + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor-internal.h" +#include "monitor/qdev.h" +#include "net/slirp.h" +#include "sysemu/device_tree.h" +#include "monitor/hmp-target.h" +#include "monitor/hmp.h" +#include "block/block-hmp-cmds.h" +#include "qapi/qapi-commands-control.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/error.h" +#include "qemu/cutils.h" + +#if defined(TARGET_S390X) +#include "hw/s390x/storage-keys.h" +#include "hw/s390x/storage-attributes.h" +#endif + +/* Make devices configuration available for use in hmp-commands*.hx templates */ +#include CONFIG_DEVICES + +static HMPCommand hmp_info_cmds[]; + +/** + * Is @name in the '|' separated list of names @list? + */ +int hmp_compare_cmd(const char *name, const char *list) +{ + const char *p, *pstart; + int len; + len = strlen(name); + p = list; + for (;;) { + pstart = p; + p = qemu_strchrnul(p, '|'); + if ((p - pstart) == len && !memcmp(pstart, name, len)) { + return 1; + } + if (*p == '\0') { + break; + } + p++; + } + return 0; +} + +/* Please update hmp-commands.hx when adding or changing commands */ +static HMPCommand hmp_info_cmds[] = { +#include "hmp-commands-info.h" + { NULL, NULL, }, +}; + +/* hmp_cmds and hmp_info_cmds would be sorted at runtime */ +HMPCommand hmp_cmds[] = { +#include "hmp-commands.h" + { NULL, NULL, }, +}; + +/* + * Set @pval to the value in the register identified by @name. + * return 0 if OK, -1 if not found + */ +int get_monitor_def(Monitor *mon, int64_t *pval, const char *name) +{ + const MonitorDef *md = target_monitor_defs(); + CPUState *cs = mon_get_cpu(mon); + void *ptr; + uint64_t tmp = 0; + int ret; + + if (cs == NULL || md == NULL) { + return -1; + } + + for(; md->name != NULL; md++) { + if (hmp_compare_cmd(name, md->name)) { + if (md->get_value) { + *pval = md->get_value(mon, md, md->offset); + } else { + CPUArchState *env = mon_get_cpu_env(mon); + ptr = (uint8_t *)env + md->offset; + switch(md->type) { + case MD_I32: + *pval = *(int32_t *)ptr; + break; + case MD_TLONG: + *pval = *(target_long *)ptr; + break; + default: + *pval = 0; + break; + } + } + return 0; + } + } + + ret = target_get_monitor_def(cs, name, &tmp); + if (!ret) { + *pval = (target_long) tmp; + } + + return ret; +} + +static int +compare_mon_cmd(const void *a, const void *b) +{ + return strcmp(((const HMPCommand *)a)->name, + ((const HMPCommand *)b)->name); +} + +static void __attribute__((__constructor__)) sortcmdlist(void) +{ + qsort(hmp_cmds, ARRAY_SIZE(hmp_cmds) - 1, + sizeof(*hmp_cmds), + compare_mon_cmd); + qsort(hmp_info_cmds, ARRAY_SIZE(hmp_info_cmds) - 1, + sizeof(*hmp_info_cmds), + compare_mon_cmd); +} + +void monitor_register_hmp(const char *name, bool info, + void (*cmd)(Monitor *mon, const QDict *qdict)) +{ + HMPCommand *table = info ? hmp_info_cmds : hmp_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd = cmd; + return; + } + table++; + } + g_assert_not_reached(); +} + +void monitor_register_hmp_info_hrt(const char *name, + HumanReadableText *(*handler)(Error **errp)) +{ + HMPCommand *table = hmp_info_cmds; + + while (table->name != NULL) { + if (strcmp(table->name, name) == 0) { + g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL); + table->cmd_info_hrt = handler; + return; + } + table++; + } + g_assert_not_reached(); +} diff --git a/monitor/hmp.c b/monitor/hmp.c index 43fd69f984..460e8832f6 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; @@ -1169,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); @@ -1191,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; } @@ -1272,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; @@ -1337,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': @@ -1409,50 +1401,45 @@ 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; - mon_refcount++; + if (!mon->mux_out) { + /* Suspend-resume forces the prompt to be printed. */ + monitor_suspend(mon); + monitor_resume(mon); + } + qemu_mutex_unlock(&mon->mon_lock); break; case CHR_EVENT_CLOSED: - mon_refcount--; monitor_fdsets_cleanup(); break; diff --git a/monitor/meson.build b/monitor/meson.build index 6d00985ace..a71523a1ce 100644 --- a/monitor/meson.build +++ b/monitor/meson.build @@ -1,9 +1,12 @@ 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', + 'qemu-config-qmp.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 205487e2b9..0000000000 --- a/monitor/misc.c +++ /dev/null @@ -1,1994 +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 "sysemu/device_tree.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.h" -#include "qapi/qmp/qstring.h" -#include "qom/object_interfaces.h" -#include "trace/control.h" -#include "monitor/hmp-target.h" -#include "monitor/hmp.h" -#ifdef CONFIG_TRACE_SIMPLE -#include "trace/simple.h" -#endif -#include "exec/memory.h" -#include "exec/exec-all.h" -#include "qemu/option.h" -#include "qemu/thread.h" -#include "block/qapi.h" -#include "block/block-hmp-cmds.h" -#include "qapi/qapi-commands-char.h" -#include "qapi/qapi-commands-control.h" -#include "qapi/qapi-commands-migration.h" -#include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-commands-qom.h" -#include "qapi/qapi-commands-run-state.h" -#include "qapi/qapi-commands-trace.h" -#include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-init-commands.h" -#include "qapi/error.h" -#include "qapi/qmp-event.h" -#include "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); - int vcpu = qdict_get_try_int(qdict, "vcpu", -1); - CPUState *cs; - - if (all_cpus) { - CPU_FOREACH(cs) { - monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); - } - } else { - cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon); - - if (!cs) { - if (vcpu >= 0) { - monitor_printf(mon, "CPU#%d not available\n", vcpu); - } else { - monitor_printf(mon, "No CPU available\n"); - } - return; - } - - monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); - } -} - -static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) -{ - int64_t max = qdict_get_try_int(qdict, "max", 10); - bool mean = qdict_get_try_bool(qdict, "mean", false); - bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); - enum QSPSortBy sort_by; - - sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; - qsp_report(max, sort_by, coalesce); -} - -static void hmp_info_history(Monitor *mon, const QDict *qdict) -{ - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common); - int i; - const char *str; - - if (!hmp_mon->rs) { - return; - } - i = 0; - for(;;) { - str = readline_get_history(hmp_mon->rs, i); - if (!str) { - break; - } - monitor_printf(mon, "%d: '%s'\n", i, str); - i++; - } -} - -static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) -{ - const char *name = qdict_get_try_str(qdict, "name"); - bool has_vcpu = qdict_haskey(qdict, "vcpu"); - int vcpu = qdict_get_try_int(qdict, "vcpu", 0); - TraceEventInfoList *events; - TraceEventInfoList *elem; - Error *local_err = NULL; - - if (name == NULL) { - name = "*"; - } - if (vcpu < 0) { - monitor_printf(mon, "argument vcpu must be positive"); - return; - } - - events = qmp_trace_event_get_state(name, has_vcpu, vcpu, &local_err); - if (local_err) { - error_report_err(local_err); - return; - } - - for (elem = events; elem != NULL; elem = elem->next) { - monitor_printf(mon, "%s : state %u\n", - elem->value->name, - elem->value->state == TRACE_EVENT_STATE_ENABLED ? 1 : 0); - } - qapi_free_TraceEventInfoList(events); -} - -void qmp_client_migrate_info(const char *protocol, const char *hostname, - bool has_port, int64_t port, - bool has_tls_port, int64_t tls_port, - 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 a2cdbbf646..cb628f681d 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,11 +165,9 @@ 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; -extern int mon_refcount; extern HMPCommand hmp_cmds[]; @@ -184,9 +181,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..56786c0ccc 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, @@ -89,7 +71,6 @@ static GHashTable *monitor_qapi_event_state; static GHashTable *coroutine_mon; /* Maps Coroutine* to Monitor* */ MonitorList mon_list; -int mon_refcount; static bool monitor_destroyed; Monitor *monitor_cur(void) @@ -154,22 +135,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 +181,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 +201,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 +241,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. */ @@ -300,12 +308,14 @@ int error_printf_unless_qmp(const char *fmt, ...) static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { /* Limit guest-triggerable events to 1 per second */ [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS }, + [QAPI_EVENT_BLOCK_IO_ERROR] = { 1000 * SCALE_MS }, [QAPI_EVENT_WATCHDOG] = { 1000 * SCALE_MS }, [QAPI_EVENT_BALLOON_CHANGE] = { 1000 * SCALE_MS }, [QAPI_EVENT_QUORUM_REPORT_BAD] = { 1000 * SCALE_MS }, [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 }, }; /* @@ -484,7 +494,8 @@ static unsigned int qapi_event_throttle_hash(const void *key) hash += g_str_hash(qdict_get_str(evstate->data, "node-name")); } - if (evstate->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE) { + if (evstate->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE || + evstate->event == QAPI_EVENT_BLOCK_IO_ERROR) { hash += g_str_hash(qdict_get_str(evstate->data, "qom-path")); } @@ -510,7 +521,8 @@ static gboolean qapi_event_throttle_equal(const void *a, const void *b) qdict_get_str(evb->data, "node-name")); } - if (eva->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE) { + if (eva->event == QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE || + eva->event == QAPI_EVENT_BLOCK_IO_ERROR) { return !strcmp(qdict_get_str(eva->data, "qom-path"), qdict_get_str(evb->data, "qom-path")); } @@ -542,6 +554,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 +583,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 +593,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 +656,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 +664,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 +710,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 +722,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 +742,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 +753,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/qemu-config-qmp.c b/monitor/qemu-config-qmp.c new file mode 100644 index 0000000000..24477a0e44 --- /dev/null +++ b/monitor/qemu-config-qmp.c @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qlist.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "hw/boards.h" + +static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) +{ + CommandLineParameterInfoList *param_list = NULL; + CommandLineParameterInfo *info; + int i; + + for (i = 0; desc[i].name != NULL; i++) { + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(desc[i].name); + + switch (desc[i].type) { + case QEMU_OPT_STRING: + info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; + break; + case QEMU_OPT_BOOL: + info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; + break; + case QEMU_OPT_NUMBER: + info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; + break; + case QEMU_OPT_SIZE: + info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; + break; + } + + info->help = g_strdup(desc[i].help); + info->q_default = g_strdup(desc[i].def_value_str); + + QAPI_LIST_PREPEND(param_list, info); + } + + return param_list; +} + +/* remove repeated entry from the info list */ +static void cleanup_infolist(CommandLineParameterInfoList *head) +{ + CommandLineParameterInfoList *pre_entry, *cur, *del_entry; + + cur = head; + while (cur->next) { + pre_entry = head; + while (pre_entry != cur->next) { + if (!strcmp(pre_entry->value->name, cur->next->value->name)) { + del_entry = cur->next; + cur->next = cur->next->next; + del_entry->next = NULL; + qapi_free_CommandLineParameterInfoList(del_entry); + break; + } + pre_entry = pre_entry->next; + } + cur = cur->next; + } +} + +/* merge the description items of two parameter infolists */ +static void connect_infolist(CommandLineParameterInfoList *head, + CommandLineParameterInfoList *new) +{ + CommandLineParameterInfoList *cur; + + cur = head; + while (cur->next) { + cur = cur->next; + } + cur->next = new; +} + +/* access all the local QemuOptsLists for drive option */ +static CommandLineParameterInfoList *get_drive_infolist(void) +{ + CommandLineParameterInfoList *head = NULL, *cur; + int i; + + for (i = 0; drive_config_groups[i] != NULL; i++) { + if (!head) { + head = query_option_descs(drive_config_groups[i]->desc); + } else { + cur = query_option_descs(drive_config_groups[i]->desc); + connect_infolist(head, cur); + } + } + cleanup_infolist(head); + + return head; +} + +static CommandLineParameterInfo *objprop_to_cmdline_prop(ObjectProperty *prop) +{ + CommandLineParameterInfo *info; + + 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; + CommandLineOptionInfo *info; + int i; + + for (i = 0; vm_config_groups[i] != NULL; i++) { + 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)) { + info->parameters = get_drive_infolist(); + } else { + info->parameters = + query_option_descs(vm_config_groups[i]->desc); + } + QAPI_LIST_PREPEND(conf_list, info); + } + } + + if (!option || !strcmp(option, "machine")) { + info = g_malloc0(sizeof(*info)); + info->option = g_strdup("machine"); + info->parameters = query_all_machine_properties(); + QAPI_LIST_PREPEND(conf_list, info); + } + + if (conf_list == NULL) { + error_setg(errp, "invalid option name: %s", option); + } + + return conf_list; +} 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 81c8fdadf8..f84a0dc523 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -14,65 +14,29 @@ */ #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-stats.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" -#include "monitor/stats.h" 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; } @@ -98,16 +62,6 @@ void qmp_stop(Error **errp) } } -void qmp_system_reset(Error **errp) -{ - qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); -} - -void qmp_system_powerdown(Error **errp) -{ - qemu_system_powerdown_request(); -} - void qmp_cont(Error **errp) { BlockBackend *blk; @@ -161,441 +115,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; -} - -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); -} - -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) -{ - strList *targets = NULL; - strList *names = NULL; - ERRP_GUARD(); - - if (request) { - if (request->provider != entry->provider) { - return true; - } - if (request->has_names && !request->names) { - return true; - } - names = request->has_names ? request->names : NULL; + if (!qmp_add_client_char(fd, has_skipauth, skipauth, has_tls, tls, + protocol, errp)) { + close(fd); } - - switch (filter->target) { - case STATS_TARGET_VM: - break; - case STATS_TARGET_VCPU: - if (filter->u.vcpu.has_vcpus) { - if (!filter->u.vcpu.vcpus) { - /* No targets allowed? Return no statistics. */ - return true; - } - targets = filter->u.vcpu.vcpus; - } - break; - default: - abort(); - } - - entry->stats_cb(stats_results, filter->target, names, targets, errp); - if (*errp) { - qapi_free_StatsResultList(*stats_results); - *stats_results = NULL; - return false; - } - return true; } -StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp) +char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, + int64_t cpu_index, Error **errp) { - StatsResultList *stats_results = NULL; - StatsCallbacks *entry; - StatsRequestList *request; + char *output = NULL; + MonitorHMP hmp = {}; - 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; - } + 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; } } - return stats_results; -} + handle_hmp_command(&hmp, command_line); -StatsSchemaList *qmp_query_stats_schemas(bool has_provider, - StatsProvider provider, - Error **errp) -{ - StatsSchemaList *stats_results = NULL; - StatsCallbacks *entry; - ERRP_GUARD(); - - QTAILQ_FOREACH(entry, &stats_callbacks, next) { - if (!has_provider || provider == entry->provider) { - entry->schemas_cb(&stats_results, errp); - if (*errp) { - qapi_free_StatsSchemaList(stats_results); - return NULL; - } - } + WITH_QEMU_LOCK_GUARD(&hmp.common.mon_lock) { + output = g_strdup(hmp.common.outbuf->str); } - return stats_results; +out: + monitor_data_destroy(&hmp.common); + return output; } -void add_stats_entry(StatsResultList **stats_results, StatsProvider provider, - const char *qom_path, StatsList *stats_list) +static void __attribute__((__constructor__)) monitor_init_qmp_commands(void) { - StatsResult *entry = g_new0(StatsResult, 1); + /* + * Two command lists: + * - qmp_commands contains all QMP commands + * - qmp_cap_negotiation_commands contains just + * "qmp_capabilities", to enforce capability negotiation + */ - entry->provider = provider; - if (qom_path) { - entry->has_qom_path = true; - entry->qom_path = g_strdup(qom_path); - } - entry->stats = stats_list; + qmp_init_marshal(&qmp_commands); - 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; + 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..5e538f34c0 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) @@ -449,7 +466,6 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event) data = qmp_greeting(mon); qmp_send_response(mon, data); qobject_unref(data); - mon_refcount++; break; case CHR_EVENT_CLOSED: /* @@ -462,7 +478,6 @@ static void monitor_qmp_event(void *opaque, QEMUChrEvent event) json_message_parser_destroy(&mon->parser); json_message_parser_init(&mon->parser, handle_qmp_command, mon, NULL); - mon_refcount--; monitor_fdsets_cleanup(); break; case CHR_EVENT_BREAK: diff --git a/nbd/client-connection.c b/nbd/client-connection.c index 0c5f917efa..b11e266807 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. * @@ -29,6 +29,7 @@ #include "qapi/qapi-visit-sockets.h" #include "qapi/clone-visitor.h" +#include "qemu/coroutine.h" struct NBDClientConnection { /* Initialization constants, never change */ @@ -92,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 ?: "") @@ -145,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) { /* @@ -155,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 { @@ -196,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; @@ -410,7 +410,7 @@ nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, */ void nbd_co_establish_connection_cancel(NBDClientConnection *conn) { - Coroutine *wait_co; + Coroutine *wait_co = NULL; WITH_QEMU_LOCK_GUARD(&conn->mutex) { wait_co = g_steal_pointer(&conn->wait_co); diff --git a/nbd/client.c b/nbd/client.c index 30d5383cb1..c89c750467 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 @@ -596,13 +596,31 @@ static int nbd_request_simple_option(QIOChannel *ioc, int opt, bool strict, return 1; } +/* Callback to learn when QIO TLS upgrade is complete */ +struct NBDTLSClientHandshakeData { + bool complete; + Error *error; + GMainLoop *loop; +}; + +static void nbd_client_tls_handshake(QIOTask *task, void *opaque) +{ + struct NBDTLSClientHandshakeData *data = opaque; + + qio_task_propagate_error(task, &data->error); + data->complete = true; + if (data->loop) { + g_main_loop_quit(data->loop); + } +} + static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, const char *hostname, Error **errp) { int ret; QIOChannelTLS *tioc; - struct NBDTLSHandshakeData data = { 0 }; + struct NBDTLSClientHandshakeData data = { 0 }; ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, true, errp); if (ret <= 0) { @@ -619,18 +637,20 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, return NULL; } qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); - data.loop = g_main_loop_new(g_main_context_default(), FALSE); trace_nbd_receive_starttls_tls_handshake(); qio_channel_tls_handshake(tioc, - nbd_tls_handshake, + nbd_client_tls_handshake, &data, NULL, NULL); if (!data.complete) { + data.loop = g_main_loop_new(g_main_context_default(), FALSE); g_main_loop_run(data.loop); + assert(data.complete); + g_main_loop_unref(data.loop); } - g_main_loop_unref(data.loop); + if (data.error) { error_propagate(errp, data.error); object_unref(OBJECT(tioc)); @@ -650,19 +670,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 +895,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 +965,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 +973,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 +1037,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 +1049,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 +1072,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 +1095,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 +1111,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 +1121,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 +1172,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 +1205,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 +1221,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 +1230,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 +1248,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 +1376,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 +1420,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 +1522,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 +1545,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..589a748cfe 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -47,17 +47,6 @@ int nbd_drop(QIOChannel *ioc, size_t size, Error **errp) } -void nbd_tls_handshake(QIOTask *task, - void *opaque) -{ - struct NBDTLSHandshakeData *data = opaque; - - qio_task_propagate_error(task, &data->error); - data->complete = true; - g_main_loop_quit(data->loop); -} - - const char *nbd_opt_lookup(uint32_t opt) { switch (opt) { @@ -79,6 +68,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 +103,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 +165,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 +245,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..91895106a9 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 @@ -71,16 +72,6 @@ static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size, return qio_channel_write_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; } -struct NBDTLSHandshakeData { - GMainLoop *loop; - bool complete; - Error *error; -}; - - -void nbd_tls_handshake(QIOTask *task, - void *opaque); - int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); #endif diff --git a/nbd/server.c b/nbd/server.c index ada16089f3..f64e47270c 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,38 @@ 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); + void *owner; + + QemuMutex lock; NBDExport *exp; QCryptoTLSCreds *tlscreds; char *tlsauthz; + uint32_t handshake_max_secs; 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 @@ -189,8 +197,9 @@ static inline void set_be_option_rep(NBDOptionReply *rep, uint32_t option, /* Send a reply header, including length, but no payload. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, - uint32_t len, Error **errp) +static coroutine_fn int +nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, + uint32_t len, Error **errp) { NBDOptionReply rep; @@ -205,15 +214,15 @@ static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type, /* Send a reply header with default 0 length. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_rep(NBDClient *client, uint32_t type, Error **errp) { return nbd_negotiate_send_rep_len(client, type, 0, errp); } /* Send an error reply. * Return -errno on error, 0 on success. */ -static int G_GNUC_PRINTF(4, 0) +static coroutine_fn int G_GNUC_PRINTF(4, 0) nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type, Error **errp, const char *fmt, va_list va) { @@ -253,7 +262,7 @@ nbd_sanitize_name(const char *name) /* Send an error reply. * Return -errno on error, 0 on success. */ -static int G_GNUC_PRINTF(4, 5) +static coroutine_fn int G_GNUC_PRINTF(4, 5) nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, Error **errp, const char *fmt, ...) { @@ -269,7 +278,7 @@ nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type, /* Drop remainder of the current option, and send a reply with the * given error type and message. Return -errno on read or write * failure; or 0 if connection is still live. */ -static int G_GNUC_PRINTF(4, 0) +static coroutine_fn int G_GNUC_PRINTF(4, 0) nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, const char *fmt, va_list va) { @@ -282,7 +291,7 @@ nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp, return ret; } -static int G_GNUC_PRINTF(4, 5) +static coroutine_fn int G_GNUC_PRINTF(4, 5) nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, const char *fmt, ...) { @@ -296,7 +305,7 @@ nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp, return ret; } -static int G_GNUC_PRINTF(3, 4) +static coroutine_fn int G_GNUC_PRINTF(3, 4) nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...) { int ret; @@ -313,8 +322,9 @@ nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...) * If @check_nul, require that no NUL bytes appear in buffer. * 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_opt_read(NBDClient *client, void *buffer, size_t size, - bool check_nul, Error **errp) +static coroutine_fn int +nbd_opt_read(NBDClient *client, void *buffer, size_t size, + bool check_nul, Error **errp) { if (size > client->optlen) { return nbd_opt_invalid(client, errp, @@ -337,7 +347,8 @@ static int nbd_opt_read(NBDClient *client, void *buffer, size_t size, /* Drop size bytes from the unparsed payload of the current option. * 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_opt_skip(NBDClient *client, size_t size, Error **errp) +static coroutine_fn int +nbd_opt_skip(NBDClient *client, size_t size, Error **errp) { if (size > client->optlen) { return nbd_opt_invalid(client, errp, @@ -360,8 +371,9 @@ static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp) * 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_opt_read_name(NBDClient *client, char **name, uint32_t *length, - Error **errp) +static coroutine_fn int +nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length, + Error **errp) { int ret; uint32_t len; @@ -396,8 +408,8 @@ static int nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length, /* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, Error **errp) { ERRP_GUARD(); size_t name_len, desc_len; @@ -438,7 +450,8 @@ static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, /* Process the NBD_OPT_LIST command, with a potential series of replies. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_handle_list(NBDClient *client, Error **errp) { NBDExport *exp; assert(client->opt == NBD_OPT_LIST); @@ -453,17 +466,19 @@ 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 coroutine_fn 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; } } /* Send a reply to NBD_OPT_EXPORT_NAME. * Return -errno on error, 0 on success. */ -static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, - Error **errp) +static coroutine_fn int +nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, + Error **errp) { ERRP_GUARD(); g_autofree char *name = NULL; @@ -480,6 +495,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 +517,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 +538,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; } @@ -523,9 +545,9 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes, /* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes. * The buffer does NOT include the info type prefix. * Return -errno on error, 0 if ready to send more. */ -static int nbd_negotiate_send_info(NBDClient *client, - uint16_t info, uint32_t length, void *buf, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_info(NBDClient *client, uint16_t info, uint32_t length, + void *buf, Error **errp) { int rc; @@ -552,7 +574,8 @@ static int nbd_negotiate_send_info(NBDClient *client, * -errno transmission error occurred or @fatal was requested, errp is set * 0 error message successfully sent to client, errp is not set */ -static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp) +static coroutine_fn int +nbd_reject_length(NBDClient *client, bool fatal, Error **errp) { int ret; @@ -570,7 +593,8 @@ static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp) /* Handle NBD_OPT_INFO and NBD_OPT_GO. * Return -errno on error, 0 if ready for next option, and 1 to move * into transmission phase. */ -static int nbd_negotiate_handle_info(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_handle_info(NBDClient *client, Error **errp) { int rc; g_autofree char *name = NULL; @@ -635,6 +659,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 +712,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,21 +754,38 @@ 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; } +/* Callback to learn when QIO TLS upgrade is complete */ +struct NBDTLSServerHandshakeData { + bool complete; + Error *error; + Coroutine *co; +}; + +static void +nbd_server_tls_handshake(QIOTask *task, void *opaque) +{ + struct NBDTLSServerHandshakeData *data = opaque; + + qio_task_propagate_error(task, &data->error); + data->complete = true; + if (!qemu_coroutine_entered(data->co)) { + aio_co_wake(data->co); + } +} /* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the * new channel for all further (now-encrypted) communication. */ -static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, - Error **errp) +static coroutine_fn QIOChannel * +nbd_negotiate_handle_starttls(NBDClient *client, Error **errp) { QIOChannel *ioc; QIOChannelTLS *tioc; - struct NBDTLSHandshakeData data = { 0 }; + struct NBDTLSServerHandshakeData data = { 0 }; assert(client->opt == NBD_OPT_STARTTLS); @@ -758,17 +806,18 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls"); trace_nbd_negotiate_handle_starttls_handshake(); - data.loop = g_main_loop_new(g_main_context_default(), FALSE); + data.co = qemu_coroutine_self(); qio_channel_tls_handshake(tioc, - nbd_tls_handshake, + nbd_server_tls_handshake, &data, NULL, NULL); if (!data.complete) { - g_main_loop_run(data.loop); + qemu_coroutine_yield(); + assert(data.complete); } - g_main_loop_unref(data.loop); + if (data.error) { object_unref(OBJECT(tioc)); error_propagate(errp, data.error); @@ -784,10 +833,9 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, * * For NBD_OPT_LIST_META_CONTEXT @context_id is ignored, 0 is used instead. */ -static int nbd_negotiate_send_meta_context(NBDClient *client, - const char *context, - uint32_t context_id, - Error **errp) +static coroutine_fn int +nbd_negotiate_send_meta_context(NBDClient *client, const char *context, + uint32_t context_id, Error **errp) { NBDOptionReplyMetaContext opt; struct iovec iov[] = { @@ -812,8 +860,9 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, * Return true if @query matches @pattern, or if @query is empty when * the @client is performing _LIST_. */ -static bool nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, - const char *query) +static coroutine_fn bool +nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, + const char *query) { if (!*query) { trace_nbd_negotiate_meta_query_parse("empty"); @@ -830,7 +879,8 @@ static bool nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, /* * Return true and adjust @str in place if it begins with @prefix. */ -static bool nbd_strshift(const char **str, const char *prefix) +static coroutine_fn bool +nbd_strshift(const char **str, const char *prefix) { size_t len = strlen(prefix); @@ -846,8 +896,9 @@ 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, - const char *query) +static coroutine_fn bool +nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta, + const char *query) { if (!nbd_strshift(&query, "base:")) { return false; @@ -866,8 +917,9 @@ 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, - const char *query) +static coroutine_fn bool +nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta, + const char *query) { size_t i; @@ -931,8 +983,9 @@ 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) +static coroutine_fn int +nbd_negotiate_meta_query(NBDClient *client, + NBDMetaContexts *meta, Error **errp) { int ret; g_autofree char *query = NULL; @@ -971,19 +1024,21 @@ 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 coroutine_fn 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 +1048,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); @@ -1093,10 +1150,11 @@ static int nbd_negotiate_meta_queries(NBDClient *client, * Return: * -errno on error, errp is set * 0 on successful negotiation, errp is not set - * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, - * errp is not set + * 1 if client sent NBD_OPT_ABORT (i.e. on valid disconnect) or never + * wrote anything (i.e. port probe); errp is not set */ -static int nbd_negotiate_options(NBDClient *client, Error **errp) +static coroutine_fn int +nbd_negotiate_options(NBDClient *client, Error **errp) { uint32_t flags; bool fixedNewstyle = false; @@ -1117,13 +1175,20 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) ... Rest of request */ - if (nbd_read32(client->ioc, &flags, "flags", errp) < 0) { - return -EIO; + /* + * Intentionally ignore errors on this first read - we do not want + * to be noisy about a mere port probe, but only for clients that + * start talking the protocol and then quit abruptly. + */ + if (nbd_read32(client->ioc, &flags, "flags", NULL) < 0) { + return 1; } + 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 +1225,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 +1252,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 +1324,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: @@ -1307,8 +1388,8 @@ static int nbd_negotiate_options(NBDClient *client, Error **errp) * Return: * -errno on error, errp is set * 0 on successful negotiation, errp is not set - * 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect, - * errp is not set + * 1 if client sent NBD_OPT_ABORT (i.e. on valid disconnect) or never + * wrote anything (i.e. port probe); errp is not set */ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) { @@ -1331,6 +1412,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); @@ -1338,9 +1420,12 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) stq_be_p(buf + 8, NBD_OPTS_MAGIC); stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES); - if (nbd_write(client->ioc, buf, 18, errp) < 0) { - error_prepend(errp, "write failed: "); - return -EINVAL; + /* + * Be silent about failure to write our greeting: there is nothing + * wrong with a client testing if our port is alive. + */ + if (nbd_write(client->ioc, buf, 18, NULL) < 0) { + return 1; } ret = nbd_negotiate_options(client, errp); if (ret != 0) { @@ -1350,11 +1435,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 +1461,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 +1494,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 +1511,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 +1554,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 +1580,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 +1635,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 +1644,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 +1665,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 +1672,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 +1703,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 +1717,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 +1769,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 +1797,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 +1806,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 +1814,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 +1824,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 +1845,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 +1861,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 +1945,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); @@ -1820,7 +1982,7 @@ static void nbd_export_request_shutdown(BlockExport *blk_exp) blk_exp_ref(&exp->common); /* - * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a + * TODO: Should we expand QMP BlockExportRemoveMode enum to allow a * close mode that stops advertising the export to new clients but * still permits existing clients to run to completion? Because of * that possibility, nbd_export_close() can be called more than @@ -1848,15 +2010,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 +2049,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 +2070,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, pnum, - data + progress, 0); + 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 +2259,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 +2295,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 +2335,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 +2368,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 +2402,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 +2432,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 +2508,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 +2533,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 +2641,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 +2809,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 +2823,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 +2848,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, request->len, data, 0); + 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 +2899,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 +2933,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, request->len, data, - 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 +2950,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 +2961,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 +2969,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 +2997,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 +3010,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 +3028,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 +3041,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 +3051,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 +3076,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 +3106,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,72 +3155,131 @@ 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); } } +static void nbd_handshake_timer_cb(void *opaque) +{ + QIOChannel *ioc = opaque; + + trace_nbd_handshake_timer_cb(); + qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); +} + static coroutine_fn void nbd_co_client_start(void *opaque) { NBDClient *client = opaque; Error *local_err = NULL; + QEMUTimer *handshake_timer = NULL; qemu_co_mutex_init(&client->send_lock); + /* + * Create a timer to bound the time spent in negotiation. If the + * timer expires, it is likely nbd_negotiate will fail because the + * socket was shutdown. + */ + if (client->handshake_max_secs > 0) { + handshake_timer = aio_timer_new(qemu_get_aio_context(), + QEMU_CLOCK_REALTIME, + SCALE_NS, + nbd_handshake_timer_cb, + client->sioc); + timer_mod(handshake_timer, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + + client->handshake_max_secs * NANOSECONDS_PER_SECOND); + } + if (nbd_negotiate(client, &local_err)) { if (local_err) { error_report_err(local_err); } + timer_free(handshake_timer); client_close(client, false); return; } - nbd_client_receive_next_request(client); + timer_free(handshake_timer); + WITH_QEMU_LOCK_GUARD(&client->lock) { + nbd_client_receive_next_request(client); + } } /* - * Create a new client listener using the given channel @sioc. + * Create a new client listener using the given channel @sioc and @owner. * Begin servicing it in a coroutine. When the connection closes, call - * @close_fn with an indication of whether the client completed negotiation. + * @close_fn with an indication of whether the client completed negotiation + * within @handshake_max_secs seconds (0 for unbounded). */ void nbd_client_new(QIOChannelSocket *sioc, + uint32_t handshake_max_secs, QCryptoTLSCreds *tlscreds, const char *tlsauthz, - void (*close_fn)(NBDClient *, bool)) + void (*close_fn)(NBDClient *, bool), + void *owner) { NBDClient *client; Coroutine *co; client = g_new0(NBDClient, 1); + qemu_mutex_init(&client->lock); client->refcount = 1; client->tlscreds = tlscreds; if (tlscreds) { object_ref(OBJECT(client->tlscreds)); } client->tlsauthz = g_strdup(tlsauthz); + client->handshake_max_secs = handshake_max_secs; 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)); client->close_fn = close_fn; + client->owner = owner; co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } + +void * +nbd_client_owner(NBDClient *client) +{ + return client->owner; +} diff --git a/nbd/trace-events b/nbd/trace-events index b7032ca277..cbd0a4ab7e 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,19 +61,22 @@ 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" +nbd_handshake_timer_cb(void) "client took too long to negotiate" # 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/can_host.c b/net/can/can_host.c index a3c84028c6..b2fe553f91 100644 --- a/net/can/can_host.c +++ b/net/can/can_host.c @@ -34,12 +34,6 @@ #include "net/can_emu.h" #include "net/can_host.h" -struct CanBusState { - Object object; - - QTAILQ_HEAD(, CanBusClientState) clients; -}; - static void can_host_disconnect(CanHostState *ch) { CanHostClass *chc = CAN_HOST_GET_CLASS(ch); 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..537457d89d 100644 --- a/net/checksum.c +++ b/net/checksum.c @@ -57,7 +57,7 @@ uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto, return net_checksum_finish(sum); } -void net_checksum_calculate(uint8_t *data, int length, int csum_flag) +void net_checksum_calculate(void *data, int length, int csum_flag) { int mac_hdr_len, ip_len; struct ip_header *ip; @@ -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,12 +96,12 @@ 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; } - ip = (struct ip_header *)(data + mac_hdr_len); + ip = (struct ip_header *)((uint8_t *)data + mac_hdr_len); if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) { return; /* not IPv4 */ diff --git a/net/clients.h b/net/clients.h index e4095f3f0d..92602ea0a2 100644 --- a/net/clients.h +++ b/net/clients.h @@ -64,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 787c740f14..39f90c4065 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); } } @@ -413,8 +412,7 @@ static void colo_compare_tcp(CompareState *s, Connection *conn) * can ensure that the packet's payload is acknowledged by * primary and secondary. */ - uint32_t min_ack = conn->pack - conn->sack > 0 ? - conn->sack : conn->pack; + uint32_t min_ack = MIN(conn->pack, conn->sack); pri: if (g_queue_is_empty(&conn->primary_list)) { @@ -1135,22 +1133,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) @@ -1444,12 +1437,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-stubs.c b/net/colo-stubs.c new file mode 100644 index 0000000000..ec726665be --- /dev/null +++ b/net/colo-stubs.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/net/dgram.c b/net/dgram.c index 9f7bf38376..48f653bceb 100644 --- a/net/dgram.c +++ b/net/dgram.c @@ -230,7 +230,7 @@ static int net_dgram_mcast_create(struct sockaddr_in *mcastaddr, return fd; fail: if (fd >= 0) { - closesocket(fd); + close(fd); } return -1; } @@ -352,7 +352,7 @@ static int net_dgram_mcast_init(NetClientState *peer, if (convert_host_port(saddr, local->u.inet.host, local->u.inet.port, errp) < 0) { g_free(saddr); - closesocket(fd); + close(fd); return -1; } @@ -360,14 +360,14 @@ static int net_dgram_mcast_init(NetClientState *peer, if (saddr->sin_addr.s_addr == 0) { error_setg(errp, "can't setup multicast destination address"); g_free(saddr); - closesocket(fd); + close(fd); return -1; } /* clone dgram socket */ newfd = net_dgram_mcast_create(saddr, NULL, errp); if (newfd < 0) { g_free(saddr); - closesocket(fd); + close(fd); return -1; } /* clone newfd to fd, close newfd */ @@ -494,14 +494,14 @@ int net_init_dgram(const Netdev *netdev, const char *name, 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_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)); - closesocket(fd); + close(fd); return -1; } qemu_socket_set_nonblock(fd); @@ -548,7 +548,7 @@ int net_init_dgram(const Netdev *netdev, const char *name, if (ret < 0) { error_setg_errno(errp, errno, "can't bind unix=%s to socket", laddr_un.sun_path); - closesocket(fd); + close(fd); return -1; } qemu_socket_set_nonblock(fd); diff --git a/net/dump.c b/net/dump.c index 6a63b15359..956e34a123 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"); @@ -154,7 +155,7 @@ static ssize_t filter_dump_receive_iov(NetFilterState *nf, NetClientState *sndr, { NetFilterDumpState *nfds = FILTER_DUMP(nf); - dump_receive_iov(&nfds->ds, iov, iovcnt); + dump_receive_iov(&nfds->ds, iov, iovcnt, qemu_get_vnet_hdr_len(nf->netdev)); 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.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 67ca534856..496a3d3b4b 100644 --- a/net/hub.c +++ b/net/hub.c @@ -193,31 +193,6 @@ NetClientState *net_hub_add_port(int hub_id, const char *name, return &port->nc; } -/** - * Find a available port on a hub; otherwise create one new port - */ -NetClientState *net_hub_port_find(int hub_id) -{ - NetHub *hub; - NetHubPort *port; - NetClientState *nc; - - QLIST_FOREACH(hub, &hubs, next) { - if (hub->id == hub_id) { - QLIST_FOREACH(port, &hub->ports, next) { - nc = port->nc.peer; - if (!nc) { - return &(port->nc); - } - } - break; - } - } - - nc = net_hub_add_port(hub_id, NULL, NULL); - return nc; -} - /** * Print hub configuration */ @@ -274,7 +249,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); diff --git a/net/l2tpv3.c b/net/l2tpv3.c index 350041a0d6..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 { diff --git a/net/meson.build b/net/meson.build index a75a56092e..aea513e412 100644 --- a/net/meson.build +++ b/net/meson.build @@ -1,15 +1,13 @@ -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', @@ -18,35 +16,52 @@ softmmu_ss.add(files( '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')) +else + system_ss.add(files('colo-stubs.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, libbpf], 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 -softmmu_ss.add(when: 'CONFIG_POSIX', if_true: files(tap_posix)) -softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('tap-win32.c')) -softmmu_ss.add([libpcap, files('pcap.c')]) -softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files('capture_win_ifnames.c')) + +if host_os == 'windows' + system_ss.add(files('tap-win32.c')) + system_ss.add(files('capture_win_ifnames.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 + +system_ss.add([libpcap, files('pcap.c')]) + if have_vhost_net_vdpa - softmmu_ss.add(when: 'CONFIG_VIRTIO_NET', if_true: files('vhost-vdpa.c'), if_false: files('vhost-vdpa-stub.c')) - softmmu_ss.add(when: 'CONFIG_ALL', if_true: files('vhost-vdpa-stub.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( @@ -55,5 +70,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 108685f4f8..95fa0db813 100644 --- a/net/net.c +++ b/net/net.c @@ -56,6 +56,7 @@ #include "net/filter.h" #include "qapi/string-output-visitor.h" #include "qapi/qobject-input-visitor.h" +#include "standard-headers/linux/virtio_net.h" /* Net bridge is currently not supported for W32. */ #if !defined(_WIN32) @@ -63,7 +64,7 @@ #endif static VMChangeStateEntry *net_change_state_entry; -static QTAILQ_HEAD(, NetClientState) net_clients; +NetClientStateList net_clients; typedef struct NetdevQueueEntry { Netdev *nd; @@ -75,6 +76,11 @@ 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 */ @@ -319,6 +325,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; @@ -331,6 +338,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++) { @@ -495,6 +503,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) { @@ -513,23 +530,23 @@ bool qemu_has_vnet_hdr_len(NetClientState *nc, int len) return nc->info->has_vnet_hdr_len(nc, len); } -void qemu_using_vnet_hdr(NetClientState *nc, bool enable) -{ - if (!nc || !nc->info->using_vnet_hdr) { - return; - } - - nc->info->using_vnet_hdr(nc, 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) { + return 0; + } + + return nc->vnet_hdr_len; } void qemu_set_vnet_hdr_len(NetClientState *nc, int len) @@ -538,6 +555,10 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) return; } + assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || + len == sizeof(struct virtio_net_hdr) || + len == sizeof(struct virtio_net_hdr_v1_hash)); + nc->vnet_hdr_len = len; nc->info->set_vnet_hdr_len(nc, len); } @@ -733,16 +754,6 @@ ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size) return qemu_net_queue_receive(nc->incoming_queue, buf, size); } -ssize_t qemu_receive_packet_iov(NetClientState *nc, const struct iovec *iov, - int iovcnt) -{ - if (!qemu_can_receive_packet(nc)) { - return 0; - } - - return qemu_net_queue_receive_iov(nc->incoming_queue, iov, iovcnt); -} - ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size) { return qemu_send_packet_async_with_flags(nc, QEMU_NET_PACKET_FLAG_RAW, @@ -770,11 +781,7 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, offset = iov_to_buf(iov, iovcnt, 0, buf, offset); } - if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) { - ret = nc->info->receive_raw(nc, buffer, offset); - } else { - ret = nc->info->receive(nc, buffer, offset); - } + ret = nc->info->receive(nc, buffer, offset); g_free(buf); return ret; @@ -786,8 +793,11 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, int iovcnt, void *opaque) { + MemReentrancyGuard *owned_reentrancy_guard; NetClientState *nc = opaque; int ret; + struct virtio_net_hdr_v1_hash vnet_hdr = { }; + g_autofree struct iovec *iov_copy = NULL; if (nc->link_down) { @@ -798,12 +808,32 @@ static ssize_t qemu_deliver_packet_iov(NetClientState *sender, return 0; } - if (nc->info->receive_iov && !(flags & QEMU_NET_PACKET_FLAG_RAW)) { + 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 ((flags & QEMU_NET_PACKET_FLAG_RAW) && nc->vnet_hdr_len) { + iov_copy = g_new(struct iovec, iovcnt + 1); + iov_copy[0].iov_base = &vnet_hdr; + iov_copy[0].iov_len = nc->vnet_hdr_len; + memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov)); + iov = iov_copy; + } + + if (nc->info->receive_iov) { 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; } @@ -899,49 +929,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, @@ -964,7 +983,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); @@ -975,19 +994,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)"); @@ -1011,6 +1030,207 @@ 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; +} + +static bool is_nic_model_help_option(const char *model) +{ + if (model && is_help_option(model)) { + /* + * Trigger the help output by instantiating the hash table which + * will gather tha available models as they get registered. + */ + if (!nic_model_help) { + nic_model_help = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + } + return true; + } + return false; +} + +/* "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, @@ -1030,6 +1250,9 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( #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 @@ -1082,7 +1305,7 @@ static int net_client_init1(const Netdev *netdev, bool is_netdev, Error **errp) /* 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); } } @@ -1135,6 +1358,9 @@ void show_netdevs(void) #ifdef CONFIG_NETMAP "netmap", #endif +#ifdef CONFIG_AF_XDP + "af-xdp", +#endif #ifdef CONFIG_POSIX "vhost-user", #endif @@ -1161,7 +1387,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) { @@ -1181,8 +1407,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; } @@ -1296,8 +1522,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; @@ -1305,13 +1530,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; @@ -1328,51 +1553,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; @@ -1460,18 +1659,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); } @@ -1485,6 +1700,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) { @@ -1513,6 +1732,12 @@ void net_check_clients(void) static int net_init_client(void *dummy, QemuOpts *opts, Error **errp) { + const char *model = qemu_opt_get(opts, "model"); + + if (is_nic_model_help_option(model)) { + return 0; + } + return net_client_init(opts, false, errp); } @@ -1536,8 +1761,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(); @@ -1554,6 +1794,10 @@ 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 (is_nic_model_help_option(ni->model)) { + return 0; + } + /* Create an ID if the user did not specify one */ nd_id = g_strdup(qemu_opts_id(opts)); if (!nd_id) { @@ -1628,7 +1872,7 @@ void net_init_clients(void) * 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 *optarg) +bool netdev_is_modern(const char *optstr) { QemuOpts *opts; bool is_modern; @@ -1640,13 +1884,13 @@ bool netdev_is_modern(const char *optarg) .desc = { { } }, }; - if (optarg[0] == '{') { + if (optstr[0] == '{') { /* This is JSON, which means it's modern syntax */ return true; } opts = qemu_opts_create(&dummy_opts, NULL, false, &error_abort); - qemu_opts_do_parse(opts, optarg, dummy_opts.implied_opt_name, + 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"); @@ -1662,12 +1906,12 @@ bool netdev_is_modern(const char *optarg) * netdev_parse_modern() appends to @nd_queue, whereas net_client_parse() * appends to @qemu_netdev_opts. */ -void netdev_parse_modern(const char *optarg) +void netdev_parse_modern(const char *optstr) { Visitor *v; NetdevQueueEntry *nd; - v = qobject_input_visitor_new_str(optarg, "type", &error_fatal); + 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); @@ -1676,9 +1920,9 @@ void netdev_parse_modern(const char *optarg) QSIMPLEQ_INSERT_TAIL(&nd_queue, nd, entry); } -void net_client_parse(QemuOptsList *opts_list, const char *optarg) +void net_client_parse(QemuOptsList *opts_list, const char *optstr) { - if (!qemu_opts_parse_noisily(opts_list, optarg, true)) { + if (!qemu_opts_parse_noisily(opts_list, optstr, true)) { exit(1); } } diff --git a/net/netmap.c b/net/netmap.c index 9e0cec58d3..297510e190 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -351,10 +351,6 @@ static bool netmap_has_vnet_hdr(NetClientState *nc) return netmap_has_vnet_hdr_len(nc, sizeof(struct virtio_net_hdr)); } -static void netmap_using_vnet_hdr(NetClientState *nc, bool enable) -{ -} - static void netmap_set_vnet_hdr_len(NetClientState *nc, int len) { NetmapState *s = DO_UPCAST(NetmapState, nc, nc); @@ -371,7 +367,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); @@ -393,7 +389,6 @@ static NetClientInfo net_netmap_info = { .has_ufo = netmap_has_vnet_hdr, .has_vnet_hdr = netmap_has_vnet_hdr, .has_vnet_hdr_len = netmap_has_vnet_hdr_len, - .using_vnet_hdr = netmap_using_vnet_hdr, .set_offload = netmap_set_offload, .set_vnet_hdr_len = netmap_set_vnet_hdr_len, }; diff --git a/net/queue.c b/net/queue.c index c872d51df8..fb33856c18 100644 --- a/net/queue.c +++ b/net/queue.c @@ -193,17 +193,6 @@ ssize_t qemu_net_queue_receive(NetQueue *queue, return qemu_net_queue_deliver(queue, NULL, 0, data, size); } -ssize_t qemu_net_queue_receive_iov(NetQueue *queue, - const struct iovec *iov, - int iovcnt) -{ - if (queue->delivering) { - return 0; - } - - return qemu_net_queue_deliver_iov(queue, NULL, 0, iov, iovcnt); -} - ssize_t qemu_net_queue_send(NetQueue *queue, NetClientState *sender, unsigned flags, diff --git a/net/slirp.c b/net/slirp.c index a32a5f3661..b6810b5492 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) @@ -647,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); @@ -714,7 +727,12 @@ void *slirp_get_state_from_netdev(const char *id) void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) { - struct in_addr host_addr = { .s_addr = INADDR_ANY }; + struct sockaddr_in host_addr = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = INADDR_ANY, + }, + }; int host_port; char buf[256]; const char *src_str, *p; @@ -751,15 +769,21 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) { goto fail_syntax; } if (qemu_strtoi(p, NULL, 10, &host_port)) { goto fail_syntax; } + host_addr.sin_port = htons(host_port); - err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); +#if SLIRP_CHECK_VERSION(4, 5, 0) + err = slirp_remove_hostxfwd(s->slirp, (struct sockaddr *) &host_addr, + sizeof(host_addr), is_udp ? SLIRP_HOSTFWD_UDP : 0); +#else + err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr.sin_addr, host_port); +#endif monitor_printf(mon, "host forwarding rule for %s %s\n", src_str, err ? "not found" : "removed"); @@ -771,13 +795,24 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) { - struct in_addr host_addr = { .s_addr = INADDR_ANY }; - struct in_addr guest_addr = { .s_addr = 0 }; + struct sockaddr_in host_addr = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = INADDR_ANY, + }, + }; + struct sockaddr_in guest_addr = { + .sin_family = AF_INET, + .sin_addr = { + .s_addr = 0, + }, + }; + int err; int host_port, guest_port; const char *p; char buf[256]; int is_udp; - char *end; + const char *end; const char *fail_reason = "Unknown reason"; p = redir_str; @@ -798,7 +833,7 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) fail_reason = "Missing : separator"; goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) { fail_reason = "Bad host address"; goto fail_syntax; } @@ -807,29 +842,41 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) fail_reason = "Bad host port separator"; goto fail_syntax; } - host_port = strtol(buf, &end, 0); - if (*end != '\0' || host_port < 0 || host_port > 65535) { + err = qemu_strtoi(buf, &end, 0, &host_port); + if (err || host_port < 0 || host_port > 65535) { fail_reason = "Bad host port"; goto fail_syntax; } + host_addr.sin_port = htons(host_port); if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { fail_reason = "Missing guest address"; goto fail_syntax; } - if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) { + if (buf[0] != '\0' && !inet_aton(buf, &guest_addr.sin_addr)) { fail_reason = "Bad guest address"; goto fail_syntax; } - guest_port = strtol(p, &end, 0); - if (*end != '\0' || guest_port < 1 || guest_port > 65535) { + err = qemu_strtoi(p, &end, 0, &guest_port); + if (err || guest_port < 1 || guest_port > 65535) { fail_reason = "Bad guest port"; goto fail_syntax; } + guest_addr.sin_port = htons(guest_port); - if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, - guest_port) < 0) { +#if SLIRP_CHECK_VERSION(4, 5, 0) + err = slirp_add_hostxfwd(s->slirp, + (struct sockaddr *) &host_addr, sizeof(host_addr), + (struct sockaddr *) &guest_addr, sizeof(guest_addr), + is_udp ? SLIRP_HOSTFWD_UDP : 0); +#else + err = slirp_add_hostfwd(s->slirp, is_udp, + host_addr.sin_addr, host_port, + guest_addr.sin_addr, guest_port); +#endif + + if (err < 0) { error_setg(errp, "Could not set up host forwarding rule '%s'", redir_str); return -1; @@ -1162,8 +1209,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 e62137c839..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,7 +172,7 @@ 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); @@ -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; } } @@ -401,7 +399,7 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, return s; err: - closesocket(fd); + close(fd); return NULL; } @@ -448,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) @@ -528,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; } @@ -581,7 +569,7 @@ static int net_socket_connect_init(NetClientState *peer, break; } else { error_setg_errno(errp, errno, "can't connect socket"); - closesocket(fd); + close(fd); return -1; } } else { @@ -589,7 +577,7 @@ 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; } @@ -631,7 +619,7 @@ 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; } @@ -673,19 +661,19 @@ 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; } @@ -705,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; @@ -745,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; @@ -753,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, @@ -763,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 index 37ff727e0c..4de5613844 100644 --- a/net/stream.c +++ b/net/stream.c @@ -39,6 +39,8 @@ #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; @@ -49,11 +51,15 @@ typedef struct NetStreamState { guint ioc_write_tag; SocketReadState rs; unsigned int send_index; /* number of bytes sent*/ + uint32_t reconnect_ms; + 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, @@ -159,6 +165,7 @@ static gboolean net_stream_send(QIOChannel *ioc, 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); } @@ -167,9 +174,9 @@ static gboolean net_stream_send(QIOChannel *ioc, net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); s->nc.link_down = true; - qemu_set_info_str(&s->nc, "%s", ""); qapi_event_send_netdev_stream_disconnected(s->nc.name); + net_stream_arm_reconnect(s); return G_SOURCE_REMOVE; } @@ -187,6 +194,14 @@ static gboolean net_stream_send(QIOChannel *ioc, 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) { @@ -257,9 +272,11 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(s->listen_ioc); SocketAddress *addr; int ret; + Error *err = NULL; - if (listen_sioc->fd < 0) { - qemu_set_info_str(&s->nc, "connection error"); + if (qio_task_propagate_error(task, &err)) { + qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); + error_free(err); return; } @@ -277,6 +294,7 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) 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); @@ -294,6 +312,7 @@ static int net_stream_server_init(NetClientState *peer, 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, @@ -310,9 +329,11 @@ static void net_stream_client_connected(QIOTask *task, gpointer opaque) SocketAddress *addr; gchar *uri; int ret; + Error *err = NULL; - if (sioc->fd < 0) { - qemu_set_info_str(&s->nc, "connection error"); + if (qio_task_propagate_error(task, &err)) { + qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); + error_free(err); goto error; } @@ -346,12 +367,37 @@ static void net_stream_client_connected(QIOTask *task, gpointer opaque) 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_ms && s->timer_tag == 0) { + qemu_set_info_str(&s->nc, "connecting"); + s->timer_tag = g_timeout_add(s->reconnect_ms, net_stream_reconnect, s); + } } static int net_stream_client_init(NetClientState *peer, const char *model, const char *name, SocketAddress *addr, + uint32_t reconnect_ms, Error **errp) { NetStreamState *s; @@ -360,10 +406,15 @@ static int net_stream_client_init(NetClientState *peer, 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_ms = reconnect_ms; + if (reconnect_ms) { + s->addr = QAPI_CLONE(SocketAddress, addr); + } qio_channel_socket_connect_async(sioc, addr, net_stream_client_connected, s, NULL, NULL); @@ -380,7 +431,25 @@ int net_init_stream(const Netdev *netdev, const char *name, sock = &netdev->u.stream; if (!sock->has_server || !sock->server) { - return net_stream_client_init(peer, "stream", name, sock->addr, errp); + uint32_t reconnect_ms = 0; + + if (sock->has_reconnect && sock->has_reconnect_ms) { + error_setg(errp, "'reconnect' and 'reconnect-ms' are mutually " + "exclusive"); + return -1; + } else if (sock->has_reconnect_ms) { + reconnect_ms = sock->reconnect_ms; + } else if (sock->has_reconnect) { + reconnect_ms = sock->reconnect * 1000u; + } + + return net_stream_client_init(peer, "stream", name, sock->addr, + reconnect_ms, errp); + } + if (sock->has_reconnect || sock->has_reconnect_ms) { + error_setg(errp, "'reconnect' and 'reconnect-ms' options are " + "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..b4c84441ba 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,7 +212,7 @@ int tap_probe_has_ufo(int fd) return 0; } -int tap_probe_vnet_hdr_len(int fd, int len) +int tap_probe_has_uso(int fd) { return 0; } @@ -232,7 +232,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..1226d5fda2 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,23 +173,15 @@ int tap_probe_has_ufo(int fd) return 1; } -/* Verify that we can assign given length */ -int tap_probe_vnet_hdr_len(int fd, int len) +int tap_probe_has_uso(int fd) { - int orig; - if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) { + unsigned offload; + + offload = TUN_F_CSUM | TUN_F_USO4 | TUN_F_USO6; + + if (ioctl(fd, TUNSETOFFLOAD, offload) < 0) { return 0; } - if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { - return 0; - } - /* Restore original length: we can't handle failure. */ - if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) { - fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n", - strerror(errno)); - abort(); - return -errno; - } return 1; } @@ -237,7 +229,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 +248,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 bbbb62c2a7..9a58cecb7f 100644 --- a/net/tap-linux.h +++ b/net/tap-linux.h @@ -50,5 +50,7 @@ #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..51b7830bef 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,7 +216,7 @@ int tap_probe_has_ufo(int fd) return 0; } -int tap_probe_vnet_hdr_len(int fd, int len) +int tap_probe_has_uso(int fd) { return 0; } @@ -236,7 +236,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..38673434cb 100644 --- a/net/tap-stub.c +++ b/net/tap-stub.c @@ -47,7 +47,7 @@ int tap_probe_has_ufo(int fd) return 0; } -int tap_probe_vnet_hdr_len(int fd, int len) +int tap_probe_has_uso(int fd) { return 0; } @@ -67,7 +67,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 a49c28ba5d..671dee970f 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -214,7 +214,7 @@ static int is_tap_win32_dev(const char *guid) for (;;) { char enum_name[256]; - char unit_string[256]; + g_autofree char *unit_string = NULL; HKEY unit_key; char component_id_string[] = "ComponentId"; char component_id[256]; @@ -239,8 +239,7 @@ static int is_tap_win32_dev(const char *guid) return FALSE; } - snprintf (unit_string, sizeof(unit_string), "%s\\%s", - ADAPTER_KEY, enum_name); + unit_string = g_strdup_printf("%s\\%s", ADAPTER_KEY, enum_name); status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, @@ -315,7 +314,7 @@ static int get_device_guid( while (!stop) { char enum_name[256]; - char connection_string[256]; + g_autofree char *connection_string = NULL; HKEY connection_key; char name_data[256]; DWORD name_type; @@ -338,9 +337,7 @@ static int get_device_guid( return -1; } - snprintf(connection_string, - sizeof(connection_string), - "%s\\%s\\Connection", + connection_string = g_strdup_printf("%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name); status = RegOpenKeyEx( @@ -595,7 +592,7 @@ static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, static int tap_win32_open(tap_win32_overlapped_t **phandle, const char *preferred_name) { - char device_path[256]; + g_autofree char *device_path = NULL; char device_guid[0x100]; int rc; HANDLE handle; @@ -617,7 +614,7 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle, if (rc) return -1; - snprintf (device_path, sizeof(device_path), "%s%s%s", + device_path = g_strdup_printf("%s%s%s", USERMODEDEVICEDIR, device_guid, TAPSUFFIX); @@ -707,70 +704,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, @@ -807,7 +750,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 1210a0436d..3f90022c0b 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]; - struct virtio_net_hdr_mrg_rxbuf hdr = { }; + g_autofree struct iovec *iov_copy = NULL; + struct virtio_net_hdr 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)); @@ -133,39 +133,14 @@ static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov, return tap_write_packet(s, iovp, iovcnt); } -static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - struct iovec iov[2]; - int iovcnt = 0; - struct virtio_net_hdr_mrg_rxbuf hdr = { }; - - if (s->host_vnet_hdr_len) { - iov[iovcnt].iov_base = &hdr; - iov[iovcnt].iov_len = s->host_vnet_hdr_len; - iovcnt++; - } - - iov[iovcnt].iov_base = (char *)buf; - iov[iovcnt].iov_len = size; - iovcnt++; - - return tap_write_packet(s, iov, iovcnt); -} - static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); - struct iovec iov[1]; + struct iovec iov = { + .iov_base = (void *)buf, + .iov_len = size + }; - if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { - return tap_receive_raw(nc, buf, size); - } - - iov[0].iov_base = (char *)buf; - iov[0].iov_len = size; - - return tap_write_packet(s, iov, 1); + return tap_receive_iov(nc, &iov, 1); } #ifndef __sun__ @@ -219,7 +194,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 +214,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); @@ -250,11 +234,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc) static bool tap_has_vnet_hdr_len(NetClientState *nc, int len) { - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - - return !!tap_probe_vnet_hdr_len(s->fd, len); + return tap_has_vnet_hdr(nc); } static void tap_set_vnet_hdr_len(NetClientState *nc, int len) @@ -262,22 +242,10 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len) TAPState *s = DO_UPCAST(TAPState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || - len == sizeof(struct virtio_net_hdr) || - len == sizeof(struct virtio_net_hdr_v1_hash)); tap_fd_set_vnet_hdr_len(s->fd, len); s->host_vnet_hdr_len = len; -} - -static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - - assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - assert(!!s->host_vnet_hdr_len == using_vnet_hdr); - - s->using_vnet_hdr = using_vnet_hdr; + s->using_vnet_hdr = true; } static int tap_set_vnet_le(NetClientState *nc, bool is_le) @@ -295,14 +263,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) @@ -367,14 +335,13 @@ static NetClientInfo net_tap_info = { .type = NET_CLIENT_DRIVER_TAP, .size = sizeof(TAPState), .receive = tap_receive, - .receive_raw = tap_receive_raw, .receive_iov = tap_receive_iov, .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, - .using_vnet_hdr = tap_using_vnet_hdr, .set_offload = tap_set_offload, .set_vnet_hdr_len = tap_set_vnet_hdr_len, .set_vnet_le = tap_set_vnet_le, @@ -399,13 +366,14 @@ 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. */ - if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) { + if (vnet_hdr) { tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len); } tap_read_poll(s, true); @@ -417,6 +385,24 @@ static TAPState *net_tap_fd_init(NetClientState *peer, return s; } +static void close_all_fds_after_fork(int excluded_fd) +{ + const int skip_fd[] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, + excluded_fd}; + unsigned int nskip = ARRAY_SIZE(skip_fd); + + /* + * skip_fd must be an ordered array of distinct fds, exclude + * excluded_fd if already included in the [STDIN_FILENO - STDERR_FILENO] + * range + */ + if (excluded_fd <= STDERR_FILENO) { + nskip--; + } + + qemu_close_all_open_fd(skip_fd, nskip); +} + static void launch_script(const char *setup_script, const char *ifname, int fd, Error **errp) { @@ -432,13 +418,7 @@ static void launch_script(const char *setup_script, const char *ifname, return; } if (pid == 0) { - int open_max = sysconf(_SC_OPEN_MAX), i; - - for (i = 3; i < open_max; i++) { - if (i != fd) { - close(i); - } - } + close_all_fds_after_fork(fd); parg = args; *parg++ = (char *)setup_script; *parg++ = (char *)ifname; @@ -522,17 +502,11 @@ static int net_bridge_run_helper(const char *helper, const char *bridge, return -1; } if (pid == 0) { - int open_max = sysconf(_SC_OPEN_MAX), i; char *fd_buf = NULL; char *br_buf = NULL; char *helper_cmd = NULL; - for (i = 3; i < open_max; i++) { - if (i != sv[1]) { - close(i); - } - } - + close_all_fds_after_fork(sv[1]); fd_buf = g_strdup_printf("%s%d", "--fd=", sv[1]); if (strrchr(helper, ' ') || strrchr(helper, '\t')) { @@ -577,9 +551,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 +583,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) { @@ -650,7 +622,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; @@ -688,9 +660,9 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, goto failed; } - if (tap->has_fd || tap->has_fds) { + if (tap->fd || tap->fds) { qemu_set_info_str(&s->nc, "fd=%d", fd); - } else if (tap->has_helper) { + } else if (tap->helper) { qemu_set_info_str(&s->nc, "helper=%s", tap->helper); } else { qemu_set_info_str(&s->nc, "ifname=%s,script=%s,downscript=%s", ifname, @@ -718,11 +690,7 @@ 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); - } + error_propagate(errp, err); goto failed; } if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { @@ -733,13 +701,8 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } 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)); - } + error_setg_errno(errp, errno, + "tap: open vhost char device failed"); goto failed; } if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { @@ -752,11 +715,8 @@ 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); - } + error_setg(errp, + "vhost-net requested but could not be initialized"); goto failed; } } else if (vhostfdname) { @@ -812,21 +772,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="); @@ -859,14 +819,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="); @@ -877,7 +837,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 " @@ -916,7 +876,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); @@ -935,17 +895,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; @@ -972,7 +931,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; } @@ -985,7 +944,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'; @@ -998,7 +957,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..8857ff299d 100644 --- a/net/tap_int.h +++ b/net/tap_int.h @@ -35,9 +35,10 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen); 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 823a071bdc..cda960f42b 100644 --- a/net/trace-events +++ b/net/trace-events @@ -23,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/vhost-user.c b/net/vhost-user.c index 3a6b90da86..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); diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index e533f8a348..231b45246c 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -26,55 +26,75 @@ #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_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, - 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_MAC_ADDR, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_RX_EXTRA, VIRTIO_NET_F_CTRL_VLAN, - 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_F_RING_RESET, - 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_RSC_EXT, + 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 }; @@ -82,6 +102,7 @@ const int vdpa_feature_bits[] = { 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) | @@ -95,11 +116,21 @@ static const uint64_t vdpa_svq_device_features = 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_STANDBY) | + BIT_ULL(VIRTIO_NET_F_SPEED_DUPLEX); + +#define VHOST_VDPA_NET_CVQ_ASID 1 VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) { @@ -108,6 +139,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; @@ -159,7 +223,6 @@ err_init: static void vhost_vdpa_cleanup(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); - struct vhost_dev *dev = &s->vhost_net->dev; /* * If a peer NIC is attached, do not cleanup anything. @@ -169,20 +232,24 @@ static void vhost_vdpa_cleanup(NetClientState *nc) if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) { return; } - qemu_vfree(s->cvq_cmd_out_buffer); - qemu_vfree(s->status); - if (dev->vq_index + dev->nvqs == dev->vq_index_end) { - g_clear_pointer(&s->vhost_vdpa.iova_tree, vhost_iova_tree_delete); - } + 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) @@ -223,19 +290,197 @@ static ssize_t vhost_vdpa_receive(NetClientState *nc, const uint8_t *buf, 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_running()) { + 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) { + int ret = vhost_vdpa_set_vring_ready(v, i + v->dev->vq_index); + if (ret < 0) { + return ret; + } + } + 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->iova_tree; + VhostIOVATree *tree = v->shared->iova_tree; DMAMap needle = { /* * No need to specify size or to look for more translations since @@ -251,7 +496,8 @@ static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr) return; } - r = vhost_vdpa_dma_unmap(v, map->iova, map->size + 1); + 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); } @@ -259,22 +505,6 @@ static void vhost_vdpa_cvq_unmap_buf(struct vhost_vdpa *v, void *addr) vhost_iova_tree_remove(tree, *map); } -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()); -} - /** Map CVQ buffer. */ static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size, bool write) @@ -285,14 +515,14 @@ static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size, 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->iova_tree, &map); + 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, map.iova, vhost_vdpa_net_cvq_cmd_page_len(), buf, - !write); + 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; } @@ -300,22 +530,83 @@ static int vhost_vdpa_cvq_map_buf(struct vhost_vdpa *v, void *buf, size_t size, return 0; dma_map_err: - vhost_iova_tree_remove(v->iova_tree, map); + vhost_iova_tree_remove(v->shared->iova_tree, map); return r; } static int vhost_vdpa_net_cvq_start(NetClientState *nc) { - VhostVDPAState *s; + 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)) { @@ -341,118 +632,663 @@ static void vhost_vdpa_net_cvq_stop(NetClientState *nc) 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, size_t out_len, - size_t in_len) +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) { - /* Buffers for the device */ - const struct iovec out = { - .iov_base = s->cvq_cmd_out_buffer, - .iov_len = out_len, - }; - const struct iovec in = { - .iov_base = s->status, - .iov_len = sizeof(virtio_net_ctrl_ack), - }; VhostShadowVirtqueue *svq = g_ptr_array_index(s->vhost_vdpa.shadow_vqs, 0); int r; - r = vhost_svq_add(svq, &out, 1, &in, 1, NULL); + 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; } - /* - * We can poll here since we've had BQL from the time we sent the - * descriptor. Also, we need to take the answer before SVQ pulls by itself, - * when BQL is released - */ - return vhost_svq_poll(svq); + return r; } -static ssize_t vhost_vdpa_net_load_cmd(VhostVDPAState *s, uint8_t class, - uint8_t cmd, const void *data, - size_t data_size) +/* + * 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)); - - memcpy(s->cvq_cmd_out_buffer, &ctrl, sizeof(ctrl)); - memcpy(s->cvq_cmd_out_buffer + sizeof(ctrl), data, data_size); - - return vhost_vdpa_net_cvq_add(s, sizeof(ctrl) + data_size, - sizeof(virtio_net_ctrl_ack)); -} - -static int vhost_vdpa_net_load_mac(VhostVDPAState *s, const VirtIONet *n) -{ - uint64_t features = n->parent_obj.guest_features; - if (features & BIT_ULL(VIRTIO_NET_F_CTRL_MAC_ADDR)) { - ssize_t dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MAC, - VIRTIO_NET_CTRL_MAC_ADDR_SET, - n->mac, sizeof(n->mac)); - if (unlikely(dev_written < 0)) { - return dev_written; + 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; } - return *s->status != VIRTIO_NET_OK; + 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) + const VirtIONet *n, + struct iovec *out_cursor, + struct iovec *in_cursor) { struct virtio_net_ctrl_mq mq; - uint64_t features = n->parent_obj.guest_features; - ssize_t dev_written; + ssize_t r; - if (!(features & BIT_ULL(VIRTIO_NET_F_MQ))) { + 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); - dev_written = vhost_vdpa_net_load_cmd(s, VIRTIO_NET_CTRL_MQ, - VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &mq, - sizeof(mq)); - if (unlikely(dev_written < 0)) { - return dev_written; + 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; } - return *s->status != VIRTIO_NET_OK; + 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(NetClientState *nc) +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); - if (!v->shadow_vqs_enabled) { - return 0; - } - - n = VIRTIO_NET(v->dev->vdev); - r = vhost_vdpa_net_load_mac(s, n); + r = vhost_vdpa_set_vring_ready(v, v->dev->vq_index); if (unlikely(r < 0)) { return r; } - r = vhost_vdpa_net_load_mq(s, n); - if (unlikely(r)) { - return r; + + 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) { + r = vhost_vdpa_set_vring_ready(v, i); + if (unlikely(r < 0)) { + return r; + } } return 0; @@ -463,14 +1299,174 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { .size = sizeof(VhostVDPAState), .receive = vhost_vdpa_receive, .start = vhost_vdpa_net_cvq_start, - .load = vhost_vdpa_net_load, + .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. * @@ -483,24 +1479,67 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq, { 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 in = { + 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_len()); - dev_written = vhost_vdpa_net_cvq_add(s, out.iov_len, sizeof(status)); - if (unlikely(dev_written < 0)) { - goto out; + 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))) { @@ -513,7 +1552,7 @@ static int vhost_vdpa_net_handle_ctrl_avail(VhostShadowVirtqueue *svq, } status = VIRTIO_NET_ERR; - virtio_net_handle_ctrl_iov(svq->vdev, &in, 1, &out, 1); + virtio_net_handle_ctrl_iov(svq->vdev, &model_in, 1, &out, 1); if (status != VIRTIO_NET_OK) { error_report("Bad CVQ processing in model"); } @@ -525,7 +1564,16 @@ out: error_report("Bad device CVQ written length"); } vhost_svq_push_elem(svq, elem, MIN(in_len, sizeof(status))); - g_free(elem); + /* + * `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; } @@ -533,61 +1581,165 @@ 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, - bool svq, - VhostIOVATree *iova_tree) + 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 { + 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); } 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; - s->vhost_vdpa.iova_tree = iova_tree; - if (!is_datapath) { - s->cvq_cmd_out_buffer = qemu_memalign(qemu_real_host_page_size(), - vhost_vdpa_net_cvq_cmd_page_len()); - memset(s->cvq_cmd_out_buffer, 0, vhost_vdpa_net_cvq_cmd_page_len()); - s->status = qemu_memalign(qemu_real_host_page_size(), - vhost_vdpa_net_cvq_cmd_page_len()); - memset(s->status, 0, vhost_vdpa_net_cvq_cmd_page_len()); + 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_iova_range(int fd, - struct vhost_vdpa_iova_range *iova_range) -{ - int ret = ioctl(fd, VHOST_VDPA_GET_IOVA_RANGE, iova_range); - - return ret < 0 ? -errno : 0; -} - static int vhost_vdpa_get_features(int fd, uint64_t *features, Error **errp) { int ret = ioctl(fd, VHOST_GET_FEATURES, features); @@ -634,29 +1786,30 @@ static int vhost_vdpa_get_max_queue_pairs(int fd, uint64_t features, 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; - g_autoptr(VhostIOVATree) iova_tree = NULL; + struct vhost_vdpa_iova_range iova_range; NetClientState *nc; int queue_pairs, r, i = 0, has_cvq = 0; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); opts = &netdev->u.vhost_vdpa; - if (!opts->has_vhostdev && !opts->has_vhostfd) { + if (!opts->vhostdev && !opts->vhostfd) { error_setg(errp, "vhost-vdpa: neither vhostdev= nor vhostfd= was specified"); return -1; } - if (opts->has_vhostdev && opts->has_vhostfd) { + if (opts->vhostdev && opts->vhostfd) { error_setg(errp, "vhost-vdpa: vhostdev= and vhostfd= are mutually exclusive"); return -1; } - if (opts->has_vhostdev) { + if (opts->vhostdev) { vdpa_device_fd = qemu_open(opts->vhostdev, O_RDWR, errp); if (vdpa_device_fd == -1) { return -errno; @@ -682,45 +1835,44 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, return queue_pairs; } - if (opts->x_svq) { - struct vhost_vdpa_iova_range iova_range; + 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; + } - 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); - goto err_svq; - } - - vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range); - iova_tree = vhost_iova_tree_new(iova_range.first, iova_range.last); + 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, opts->x_svq, - iova_tree); + 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, - opts->x_svq, iova_tree); + opts->x_svq, iova_range, features, shared, + errp); if (!nc) goto err; } - /* iova_tree ownership belongs to last NetClientState */ - g_steal_pointer(&iova_tree); return 0; err: @@ -730,7 +1882,6 @@ err: } } -err_svq: qemu_close(vdpa_device_fd); return -1; diff --git a/net/vmnet-bridged.m b/net/vmnet-bridged.m index 46d2282863..a04a14fa11 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; @@ -88,15 +88,6 @@ static bool validate_options(const Netdev *netdev, Error **errp) return false; } -#if !defined(MAC_OS_VERSION_11_0) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 - if (options->has_isolated) { - error_setg(errp, - "vmnet-bridged.isolated feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#endif return true; } @@ -115,12 +106,10 @@ static xpc_object_t build_if_desc(const Netdev *netdev) vmnet_shared_interface_name_key, options->ifname); -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 xpc_dictionary_set_bool(if_desc, vmnet_enable_isolation_key, options->isolated); -#endif + return if_desc; } diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 2cb60b9ddd..30c4e53c13 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 @@ -46,11 +47,8 @@ const char *vmnet_status_map_str(vmnet_return_t status) return "buffers exhausted in kernel"; case VMNET_TOO_MANY_PACKETS: return "packet count exceeds limit"; -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 case VMNET_SHARING_SERVICE_BUSY: return "conflict, sharing service is in use"; -#endif default: return "unknown vmnet error"; } @@ -242,6 +240,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 +356,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 +373,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..49fb25c224 100644 --- a/net/vmnet-host.c +++ b/net/vmnet-host.c @@ -21,38 +21,20 @@ static bool validate_options(const Netdev *netdev, Error **errp) { const NetdevVmnetHostOptions *options = &(netdev->u.vmnet_host); - -#if defined(MAC_OS_VERSION_11_0) && \ - 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; } -#else - if (options->has_isolated) { - error_setg(errp, - "vmnet-host.isolated feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } - if (options->has_net_uuid) { - error_setg(errp, - "vmnet-host.net-uuid feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#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"); @@ -71,23 +53,19 @@ static xpc_object_t build_if_desc(const Netdev *netdev) vmnet_operation_mode_key, VMNET_HOST_MODE); -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 - xpc_dictionary_set_bool(if_desc, vmnet_enable_isolation_key, 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, net_uuid.data); } -#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..4726b07253 100644 --- a/net/vmnet-shared.c +++ b/net/vmnet-shared.c @@ -21,22 +21,12 @@ static bool validate_options(const Netdev *netdev, Error **errp) { const NetdevVmnetSharedOptions *options = &(netdev->u.vmnet_shared); -#if !defined(MAC_OS_VERSION_11_0) || \ - MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0 - if (options->has_isolated) { - error_setg(errp, - "vmnet-shared.isolated feature is " - "unavailable: outdated vmnet.framework API"); - return false; - } -#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 +48,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); @@ -76,14 +66,11 @@ static xpc_object_t build_if_desc(const Netdev *netdev) options->subnet_mask); } -#if defined(MAC_OS_VERSION_11_0) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_11_0 xpc_dictionary_set_bool( if_desc, vmnet_enable_isolation_key, options->isolated ); -#endif return if_desc; } 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 4858650c3e..43f9a43f3f 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" @@ -39,20 +37,8 @@ #ifdef CONFIG_LINUX #include -#include "qemu/async-teardown.h" #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) { @@ -100,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; @@ -108,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; @@ -126,43 +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; -#if defined(CONFIG_LINUX) - case QEMU_OPTION_asyncteardown: - init_async_teardown(); - break; -#endif - default: - return -1; - } - - return 0; -} - static void change_process_uid(void) { assert((user_uid == (uid_t)-1) || user_pwd == NULL); @@ -200,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) { @@ -215,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) { @@ -263,6 +257,31 @@ 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; + } + +#ifdef CONFIG_DARWIN + nofile.rlim_cur = OPEN_MAX < nofile.rlim_max ? OPEN_MAX : nofile.rlim_max; +#else + nofile.rlim_cur = nofile.rlim_max; +#endif + + 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; @@ -272,7 +291,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); } @@ -308,17 +327,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-target.c b/page-target.c new file mode 100644 index 0000000000..82211c8593 --- /dev/null +++ b/page-target.c @@ -0,0 +1,44 @@ +/* + * QEMU page values getters (target independent) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "qemu/osdep.h" +#include "exec/target_page.h" +#include "exec/cpu-defs.h" +#include "cpu.h" +#include "exec/cpu-all.h" + +size_t qemu_target_page_size(void) +{ + return TARGET_PAGE_SIZE; +} + +int qemu_target_page_mask(void) +{ + return TARGET_PAGE_MASK; +} + +int qemu_target_page_bits(void) +{ + return TARGET_PAGE_BITS; +} + +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); +} 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 b94f3fb081..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-20220719. + 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 211b2a4da2..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 6204a714cd..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 12d6a037be..7e2d062af6 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/descriptors/60-edk2-loongarch64.json b/pc-bios/descriptors/60-edk2-loongarch64.json new file mode 100644 index 0000000000..f174a1fc9b --- /dev/null +++ b/pc-bios/descriptors/60-edk2-loongarch64.json @@ -0,0 +1,31 @@ +{ + "description": "UEFI firmware for loongarch64", + "interface-types": [ + "uefi" + ], + "mapping": { + "device": "flash", + "executable": { + "filename": "@DATADIR@/edk2-loongarch64-code.fd", + "format": "raw" + }, + "nvram-template": { + "filename": "@DATADIR@/edk2-loongarch64-vars.fd", + "format": "raw" + } + }, + "targets": [ + { + "architecture": "loongarch64", + "machines": [ + "virt*" + ] + } + ], + "features": [ + + ], + "tags": [ + + ] +} diff --git a/pc-bios/descriptors/meson.build b/pc-bios/descriptors/meson.build index 66f85d01c4..afb5a959cc 100644 --- a/pc-bios/descriptors/meson.build +++ b/pc-bios/descriptors/meson.build @@ -5,7 +5,8 @@ if unpack_edk2_blobs and get_option('install_blobs') '60-edk2-aarch64.json', '60-edk2-arm.json', '60-edk2-i386.json', - '60-edk2-x86_64.json' + '60-edk2-x86_64.json', + '60-edk2-loongarch64.json' ] configure_file(input: files(f), output: f, diff --git a/pc-bios/edk2-aarch64-code.fd.bz2 b/pc-bios/edk2-aarch64-code.fd.bz2 index 0262f5bd8f..2ce728c9ed 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..9b98490a80 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..50c9869960 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..d58c16fb04 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-loongarch64-code.fd.bz2 b/pc-bios/edk2-loongarch64-code.fd.bz2 new file mode 100644 index 0000000000..ba12bc9a06 Binary files /dev/null and b/pc-bios/edk2-loongarch64-code.fd.bz2 differ diff --git a/pc-bios/edk2-loongarch64-vars.fd.bz2 b/pc-bios/edk2-loongarch64-vars.fd.bz2 new file mode 100644 index 0000000000..8a13571e28 Binary files /dev/null and b/pc-bios/edk2-loongarch64-vars.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..f4e243d996 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..cf043fcb62 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..c2b04f82c6 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..50f5b3694d 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 index 0fa3808f16..6832dfc06d 100644 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 100644 index 0000000000..16c08aa497 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 452395b962..0bd8ce0077 100644 --- a/pc-bios/keymaps/meson.build +++ b/pc-bios/keymaps/meson.build @@ -33,7 +33,7 @@ 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 @@ -47,7 +47,7 @@ if native_qemu_keymap.found() build_by_default: true, output: km, command: [native_qemu_keymap, '-f', '@OUTPUT@', args.split()], - install: true, + install: have_system, install_dir: qemu_datadir / 'keymaps') endforeach @@ -56,4 +56,6 @@ 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 388e0db6e4..4823dff189 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -4,11 +4,15 @@ if unpack_edk2_blobs 'edk2-aarch64-code.fd', 'edk2-arm-code.fd', 'edk2-arm-vars.fd', + 'edk2-riscv-code.fd', + 'edk2-riscv-vars.fd', 'edk2-i386-code.fd', 'edk2-i386-secure-code.fd', 'edk2-i386-vars.fd', 'edk2-x86_64-code.fd', 'edk2-x86_64-secure-code.fd', + 'edk2-loongarch64-code.fd', + 'edk2-loongarch64-vars.fd', ] foreach f : fds @@ -28,7 +32,6 @@ blobs = [ 'bios-256k.bin', 'bios-microvm.bin', 'qboot.rom', - 'sgabios.bin', 'vgabios.bin', 'vgabios-cirrus.bin', 'vgabios-stdvga.bin', @@ -58,10 +61,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', @@ -69,7 +68,6 @@ blobs = [ 'kvmvapic.bin', 'pvh.bin', 's390-ccw.img', - 's390-netboot.img', 'slof.bin', 'skiboot.lid', 'palcode-clipper', @@ -78,6 +76,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,6 +84,27 @@ blobs = [ 'vof-nvram.bin', ] +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 diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index d8203a5074..6f472d4b11 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..9679248260 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..0a13453efa 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 81bab1adc9..b2e740010b 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 5eb0a74326..018b4731a7 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 b1fff0ba6c..30d07026c7 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -36,7 +36,7 @@ config-cc.mak: Makefile $(call cc-option,-Wno-array-bounds)) 3> config-cc.mak -include config-cc.mak -override LDFLAGS = -nostdlib -Wl,-T,$(SRC_DIR)/flat.lds +override LDFLAGS = -nostdlib -Wl,--build-id=none,-T,$(SRC_DIR)/flat.lds pvh.img: pvh.o pvh_main.o 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/s390-ccw.img b/pc-bios/s390-ccw.img index 554fcbd1b7..0cbedf0fa6 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 10e8f5cb63..dc69dd484f 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -3,9 +3,12 @@ all: build-all @true include config-host.mak -CFLAGS = -O2 -g +CFLAGS = -O2 -g -I $(SRC_PATH)/../../include/hw/s390x/ipl +LDFLAGS ?= MAKEFLAGS += -rR +GIT_SUBMODULES = roms/SLOF + NULL := SPACE := $(NULL) # TARGET_PREFIX := $(patsubst %/,%:$(SPACE),$(TARGET_DIR)) @@ -30,15 +33,21 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d .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 +OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \ + virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o + +SLOF_DIR := $(SRC_PATH)/../../roms/SLOF + +LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include +LIBNET_INC := -I$(SLOF_DIR)/lib/libnet 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 +EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) +EXTRA_LDFLAGS += -Wl,-pie -nostdlib -z noexecstack -z text 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 \ @@ -53,26 +62,64 @@ config-cc.mak: Makefile $(call cc-option,-march=z900,-march=z10)) 3> config-cc.mak -include config-cc.mak -LDFLAGS += -Wl,-pie -nostdlib +# libc files: -build-all: s390-ccw.img s390-netboot.img +LIBC_CFLAGS = $(EXTRA_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) \ + -MMD -MP -MT $@ -MF $(@:%.o=%.d) -s390-ccw.elf: $(OBJECTS) - $(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),Linking) +CTYPE_OBJS = isdigit.o isxdigit.o toupper.o +%.o : $(SLOF_DIR)/lib/libc/ctype/%.c + $(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 $@ $<,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 $@ $<,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 $@ $<,Compiling) + +sbrk.o: $(SLOF_DIR)/slof/sbrk.c + $(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 $@ $^,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 = $(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 $@ $<,Compiling) + +libnet.a: $(LIBNETOBJS) + $(call quiet-command,$(AR) -rc $@ $^,Creating static library) + +# Main targets: + +build-all: s390-ccw.img + +s390-ccw.elf: $(OBJECTS) libnet.a libc.a + $(call quiet-command,$(CC) $(EXTRA_LDFLAGS) $(LDFLAGS) -o $@ $^,Linking) s390-ccw.img: s390-ccw.elf $(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)) +ALL_OBJS = $(sort $(OBJECTS) $(LIBCOBJS) $(LIBNETOBJS)) -include $(ALL_OBJS:%.o=%.d) clean: @@ -80,3 +127,12 @@ clean: 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 994e59c0b0..56f2f75640 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -8,7 +8,8 @@ * directory. */ -#include "libc.h" +#include +#include #include "s390-ccw.h" #include "s390-arch.h" #include "bootmap.h" @@ -21,7 +22,7 @@ #ifdef DEBUG_FALLBACK #define dputs(txt) \ - do { sclp_print("zipl: " txt); } while (0) + do { printf("zipl: " txt); } while (0) #else #define dputs(fmt, ...) \ do { } while (0) @@ -61,65 +62,119 @@ static void *s2_prev_blk = _s2; static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; -static inline void verify_boot_info(BootInfo *bip) +static inline int verify_boot_info(BootInfo *bip) { - IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL sig in BootInfo"); - IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version"); - IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL"); - IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD"); - IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch"); - IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size), - "Bad block size in zIPL section of the 1st record."); + if (!magic_match(bip->magic, ZIPL_MAGIC)) { + puts("No zIPL sig in BootInfo"); + return -EINVAL; + } + if (bip->version != BOOT_INFO_VERSION) { + puts("Wrong zIPL version"); + return -EINVAL; + } + if (bip->bp_type != BOOT_INFO_BP_TYPE_IPL) { + puts("DASD is not for IPL"); + return -ENODEV; + } + if (bip->dev_type != BOOT_INFO_DEV_TYPE_ECKD) { + puts("DASD is not ECKD"); + return -ENODEV; + } + if (bip->flags != BOOT_INFO_FLAGS_ARCH) { + puts("Not for this arch"); + return -EINVAL; + } + if (!block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size)) { + puts("Bad block size in zIPL section of 1st record"); + return -EINVAL; + } + + return 0; +} + +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 = c + ((h & 0xfff0) << 12); + const uint64_t head = h & 0x000f; + const block_number_t block = sectors * heads * cylinder + + sectors * head + + s - 1; /* block nr starts with zero */ + return block; } static block_number_t eckd_block_num(EckdCHS *chs) { - 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 block_number_t block = sectors * heads * cylinder - + sectors * head - + chs->sector - - 1; /* block nr starts with zero */ - return block; + return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector); } -static bool eckd_valid_address(BootMapPointer *p) +static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl) { - const uint64_t head = p->eckd.chs.head & 0x000f; + 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; memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); - read_block(blk, bprs, "BPRS read failed"); + if (virtio_read(blk, bprs)) { + puts("BPRS read failed"); + return ERROR_BLOCK_NR; + } 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; + return NULL_BLOCK_NR; } /* we need the updated blockno for the next indirect entry @@ -129,11 +184,31 @@ 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), - "bad chunk block size"); - IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr"); + /* List directed pointer does not store block size */ + if (!ldipl && !block_size_ok(bprs[j].xeckd.bptr.size)) { + puts("Bad chunk block size"); + return ERROR_BLOCK_NR; + } - 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, otherwise abort on error + */ + if (!ldipl) { + puts("Bad chunk ECKD address"); + return ERROR_BLOCK_NR; + } + 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 @@ -141,7 +216,10 @@ static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address) * I.e. the next ptr must point to the unused memory area */ memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs)); - read_block(block_nr, bprs, "BPRS continuation read failed"); + if (virtio_read(block_nr, bprs)) { + puts("BPRS continuation read failed"); + return ERROR_BLOCK_NR; + } more_data = true; break; } @@ -149,11 +227,13 @@ 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); - IPL_assert(rc == 0, "code chunk read failed"); + rc = virtio_read_many(block_nr, (void *)(*address), count + 1); + if (rc != 0) { + puts("Code chunk read failed"); + return ERROR_BLOCK_NR; + } - *address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size(); + *address += (count + 1) * virtio_get_block_size(); } } while (more_data); return block_nr; @@ -185,7 +265,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) /* Get Stage1b data */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(s1b_block_nr, s1b, "Cannot read stage1b boot loader"); + if (virtio_read(s1b_block_nr, s1b)) { + puts("Cannot read stage1b boot loader"); + return -EIO; + } memset(_s2, FREE_SPACE_FILLER, sizeof(_s2)); @@ -197,7 +280,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) break; } - read_block(cur_block_nr, s2_cur_blk, "Cannot read stage2 boot loader"); + if (virtio_read(cur_block_nr, s2_cur_blk)) { + puts("Cannot read stage2 boot loader"); + return -EIO; + } if (find_zipl_boot_menu_banner(&banner_offset)) { /* @@ -205,8 +291,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) * possibility of menu data spanning multiple blocks. */ if (prev_block_nr) { - read_block(prev_block_nr, s2_prev_blk, - "Cannot read stage2 boot loader"); + if (virtio_read(prev_block_nr, s2_prev_blk)) { + puts("Cannot read stage2 boot loader"); + return -EIO; + } } if (i + 1 < STAGE2_BLK_CNT_MAX) { @@ -214,8 +302,10 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) } if (next_block_nr && !is_null_block_number(next_block_nr)) { - read_block(next_block_nr, s2_next_blk, - "Cannot read stage2 boot loader"); + if (virtio_read(next_block_nr, s2_next_blk)) { + puts("Cannot read stage2 boot loader"); + return -EIO; + } } return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); @@ -224,11 +314,11 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) prev_block_nr = cur_block_nr; } - sclp_print("No zipl boot menu data found. Booting default entry."); + printf("No zipl boot menu data found. Booting default entry."); return 0; } -static void run_eckd_boot_script(block_number_t bmt_block_nr, +static int run_eckd_boot_script(block_number_t bmt_block_nr, block_number_t s1b_block_nr) { int i; @@ -237,23 +327,36 @@ 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); } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" - " maximum number of boot entries allowed"); + if (loadparm >= MAX_BOOT_ENTRIES) { + puts("loadparm value greater than max number of boot entries allowed"); + return -EINVAL; + } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); + if (virtio_read(bmt_block_nr, sec)) { + puts("Cannot read Boot Map Table"); + return -EIO; + } - block_nr = eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs); - IPL_assert(block_nr != -1, "Cannot find Boot Map Table Entry"); + block_nr = gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl); + if (block_nr == NULL_BLOCK_NR) { + puts("Cannot find Boot Map Table Entry"); + return -EIO; + } memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(block_nr, sec, "Cannot read Boot Map Script"); + if (virtio_read(block_nr, sec)) { + puts("Cannot read Boot Map Script"); + return -EIO; + } for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD || bms->entry[i].type == BOOT_SCRIPT_SIGNATURE; i++) { @@ -264,20 +367,31 @@ 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); - } while (block_nr != -1); + block_nr = load_eckd_segments(block_nr, ldipl, &address); + if (block_nr == ERROR_BLOCK_NR) { + return ldipl ? 0 : -EIO; + } + } while (block_nr != NULL_BLOCK_NR); } - IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC, - "Unknown script entry type"); - write_reset_psw(bms->entry[i].address.load_address); /* no return */ - jump_to_IPL_code(0); /* no return */ + if (ldipl && bms->entry[i].type != BOOT_SCRIPT_EXEC) { + /* Abort LD-IPL and retry as CCW-IPL */ + return 0; + } + + if (bms->entry[i].type != BOOT_SCRIPT_EXEC) { + puts("Unknown script entry type"); + return -EINVAL; + } + write_reset_psw(bms->entry[i].address.load_address); + jump_to_IPL_code(0); + return -1; } -static void ipl_eckd_cdl(void) +static int ipl_eckd_cdl(void) { XEckdMbr *mbr; EckdCdlIpl2 *ipl2 = (void *)sec; @@ -285,23 +399,26 @@ static void ipl_eckd_cdl(void) block_number_t bmt_block_nr, s1b_block_nr; /* we have just read the block #0 and recognized it as "IPL1" */ - sclp_print("CDL\n"); + puts("CDL"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(1, ipl2, "Cannot read IPL2 record at block 1"); + if (virtio_read(1, ipl2)) { + puts("Cannot read IPL2 record at block 1"); + return -EIO; + } mbr = &ipl2->mbr; if (!magic_match(mbr, ZIPL_MAGIC)) { - sclp_print("No zIPL section in IPL2 record.\n"); - return; + puts("No zIPL section in IPL2 record."); + return 0; } if (!block_size_ok(mbr->blockptr.xeckd.bptr.size)) { - sclp_print("Bad block size in zIPL section of IPL2 record.\n"); - return; + puts("Bad block size in zIPL section of IPL2 record."); + return 0; } if (mbr->dev_type != DEV_TYPE_ECKD) { - sclp_print("Non-ECKD device type in zIPL section of IPL2 record.\n"); - return; + puts("Non-ECKD device type in zIPL section of IPL2 record."); + return 0; } /* save pointer to Boot Map Table */ @@ -311,19 +428,21 @@ static void ipl_eckd_cdl(void) s1b_block_nr = eckd_block_num(&ipl2->stage1.seek[0].chs); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read Volume Label at block 2"); + if (virtio_read(2, vlbl)) { + puts("Cannot read Volume Label at block 2"); + return -EIO; + } if (!magic_match(vlbl->key, VOL1_MAGIC)) { - sclp_print("Invalid magic of volume label block.\n"); - return; + puts("Invalid magic of volume label block."); + return 0; } if (!magic_match(vlbl->f.key, VOL1_MAGIC)) { - sclp_print("Invalid magic of volser block.\n"); - return; + puts("Invalid magic of volser block."); + return 0; } print_volser(vlbl->f.volser); - run_eckd_boot_script(bmt_block_nr, s1b_block_nr); - /* no return */ + return run_eckd_boot_script(bmt_block_nr, s1b_block_nr); } static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) @@ -331,8 +450,8 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */ char msg[4] = { '?', '.', '\n', '\0' }; - sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL"); - sclp_print(" version "); + printf((mode == ECKD_CMS) ? "CMS" : "LDL"); + printf(" version "); switch (vlbl->LDL_version) { case LDL1_VERSION: msg[0] = '1'; @@ -345,11 +464,11 @@ static void print_eckd_ldl_msg(ECKD_IPL_mode_t mode) msg[1] = '?'; break; } - sclp_print(msg); + printf("%s", msg); print_volser(vlbl->volser); } -static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) +static int ipl_eckd_ldl(ECKD_IPL_mode_t mode) { block_number_t bmt_block_nr, s1b_block_nr; EckdLdlIpl1 *ipl1 = (void *)sec; @@ -361,12 +480,15 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* DO NOT read BootMap pointer (only one, xECKD) at block #2 */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, sec, "Cannot read block 0 to grab boot info."); + if (virtio_read(0, sec)) { + puts("Cannot read block 0 to grab boot info."); + return -EIO; + } if (mode == ECKD_LDL_UNLABELED) { if (!magic_match(ipl1->bip.magic, ZIPL_MAGIC)) { - return; /* not applicable layout */ + return 0; /* not applicable layout */ } - sclp_print("unlabeled LDL.\n"); + puts("unlabeled LDL."); } verify_boot_info(&ipl1->bip); @@ -376,8 +498,27 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* save pointer to Stage1b Data */ s1b_block_nr = eckd_block_num(&ipl1->stage1.seek[0].chs); - run_eckd_boot_script(bmt_block_nr, s1b_block_nr); - /* no return */ + return run_eckd_boot_script(bmt_block_nr, s1b_block_nr); +} + +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); + if (virtio_read(blockno, tmp_sec)) { + puts("Cannot read boot record"); + return ERROR_BLOCK_NR; + } + 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) @@ -396,49 +537,84 @@ static void print_eckd_msg(void) *p-- = ' '; } } - sclp_print(msg); + printf("%s", msg); } -static void ipl_eckd(void) +static int 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 */ + /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */ 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; + if (virtio_read(2, vlbl)) { + puts("Cannot read block 2"); + return -EIO; } - /* LDL/CMS? */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); - - if (magic_match(vlbl->magic, CMS1_MAGIC)) { - ipl_eckd_ldl(ECKD_CMS); /* no return */ - } - if (magic_match(vlbl->magic, LNX1_MAGIC)) { - ipl_eckd_ldl(ECKD_LDL); /* no return */ + /* + * 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); + switch (ldipl_bmt) { + case ERROR_BLOCK_NR: + return -EIO; + case NULL_BLOCK_NR: + break; /* Invalid BMT but the device may still boot with CCW-IPL */ + default: + puts("List-Directed"); + /* + * LD-IPL does not use the S1B bock, just make it NULL_BLOCK_NR. + * In some failure cases retry IPL before aborting. + */ + if (run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR)) { + return -EIO; + } + /* Non-fatal error, retry as CCW-IPL */ + printf("Retrying IPL "); + print_eckd_msg(); + } + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + if (virtio_read(2, vtoc)) { + puts("Cannot read block 2"); + return -EIO; + } } - ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */ + /* Not list-directed */ + if (magic_match(vtoc->magic, VOL1_MAGIC)) { + if (ipl_eckd_cdl()) { + return -1; + } + } + + if (magic_match(vtoc->magic, CMS1_MAGIC)) { + return ipl_eckd_ldl(ECKD_CMS); + } + if (magic_match(vtoc->magic, LNX1_MAGIC)) { + return ipl_eckd_ldl(ECKD_LDL); + } + + if (ipl_eckd_ldl(ECKD_LDL_UNLABELED)) { + return -1; + } /* * Ok, it is not a LDL by any means. * It still might be a CDL with zero record keys for IPL1 and IPL2 */ - ipl_eckd_cdl(); + return ipl_eckd_cdl(); } /*********************************************************************** * IPL a SCSI disk */ -static void zipl_load_segment(ComponentEntry *entry) +static int zipl_load_segment(ComponentEntry *entry) { const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr)); ScsiBlockPtr *bprs = (void *)sec; @@ -458,7 +634,10 @@ static void zipl_load_segment(ComponentEntry *entry) do { memset(bprs, FREE_SPACE_FILLER, bprs_size); fill_hex_val(blk_no, &blockno, sizeof(blockno)); - read_block(blockno, bprs, err_msg); + if (virtio_read(blockno, bprs)) { + puts(err_msg); + return -EIO; + } for (i = 0;; i++) { uint64_t *cur_desc = (void *)&bprs[i]; @@ -486,23 +665,37 @@ static void zipl_load_segment(ComponentEntry *entry) } address = virtio_load_direct(cur_desc[0], cur_desc[1], 0, (void *)address); - IPL_assert(address != -1, "zIPL load segment failed"); + if (!address) { + puts("zIPL load segment failed"); + return -EIO; + } } } while (blockno); + + return 0; } /* Run a zipl program */ -static void zipl_run(ScsiBlockPtr *pte) +static int zipl_run(ScsiBlockPtr *pte) { ComponentHeader *header; ComponentEntry *entry; uint8_t tmp_sec[MAX_SECTOR_SIZE]; - read_block(pte->blockno, tmp_sec, "Cannot read header"); + if (virtio_read(pte->blockno, tmp_sec)) { + puts("Cannot read header"); + return -EIO; + } header = (ComponentHeader *)tmp_sec; - IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic in header"); - IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type"); + if (!magic_match(tmp_sec, ZIPL_MAGIC)) { + puts("No zIPL magic in header"); + return -EINVAL; + } + if (header->type != ZIPL_COMP_HEADER_IPL) { + puts("Bad header type"); + return -EINVAL; + } dputs("start loading images\n"); @@ -517,22 +710,30 @@ static void zipl_run(ScsiBlockPtr *pte) continue; } - zipl_load_segment(entry); + if (zipl_load_segment(entry)) { + return -1; + } entry++; - IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE), - "Wrong entry value"); + if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) { + puts("Wrong entry value"); + return -EINVAL; + } } - IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry"); + if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) { + puts("No EXEC entry"); + return -EINVAL; + } /* should not return */ write_reset_psw(entry->compdat.load_psw); jump_to_IPL_code(0); + return -1; } -static void ipl_scsi(void) +static int ipl_scsi(void) { ScsiMbr *mbr = (void *)sec; int program_table_entries = 0; @@ -543,22 +744,34 @@ static void ipl_scsi(void) /* Grab the MBR */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0"); - - if (!magic_match(mbr->magic, ZIPL_MAGIC)) { - return; + if (virtio_read(0, mbr)) { + puts("Cannot read block 0"); + return -EIO; } - sclp_print("Using SCSI scheme.\n"); + if (!magic_match(mbr->magic, ZIPL_MAGIC)) { + return 0; + } + + puts("Using SCSI scheme."); debug_print_int("MBR Version", mbr->version_id); IPL_check(mbr->version_id == 1, "Unknown MBR layout version, assuming version 1"); debug_print_int("program table", mbr->pt.blockno); - IPL_assert(mbr->pt.blockno, "No Program Table"); + if (!mbr->pt.blockno) { + puts("No Program Table"); + return -EINVAL; + } /* Parse the program table */ - read_block(mbr->pt.blockno, sec, "Error reading Program Table"); - IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); + if (virtio_read(mbr->pt.blockno, sec)) { + puts("Error reading Program Table"); + return -EIO; + } + if (!magic_match(sec, ZIPL_MAGIC)) { + puts("No zIPL magic in Program Table"); + return -EINVAL; + } for (i = 0; i < MAX_BOOT_ENTRIES; i++) { if (prog_table->entry[i].scsi.blockno) { @@ -568,17 +781,22 @@ static void ipl_scsi(void) } debug_print_int("program table entries", program_table_entries); - IPL_assert(program_table_entries != 0, "Empty Program Table"); + if (program_table_entries == 0) { + puts("Empty Program Table"); + return -EINVAL; + } if (menu_is_enabled_enum()) { loadparm = menu_get_enum_boot_index(valid_entries); } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" - " maximum number of boot entries allowed"); + if (loadparm >= MAX_BOOT_ENTRIES) { + puts("loadparm value greater than max number of boot entries allowed"); + return -EINVAL; + } - zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ + return zipl_run(&prog_table->entry[loadparm].scsi); } /*********************************************************************** @@ -592,8 +810,10 @@ static bool is_iso_bc_entry_compatible(IsoBcSection *s) if (s->unused || !s->sector_count) { return false; } - read_iso_sector(bswap32(s->load_rba), magic_sec, - "Failed to read image sector 0"); + if (virtio_read(bswap32(s->load_rba), magic_sec)) { + puts("Failed to read image sector 0"); + return false; + } /* Checking bytes 8 - 32 for S390 Linux magic */ return !memcmp(magic_sec + 8, linux_s390_magic, 24); @@ -606,28 +826,35 @@ static uint32_t sec_offset[ISO9660_MAX_DIR_DEPTH]; /* Remained directory space in bytes */ static uint32_t dir_rem[ISO9660_MAX_DIR_DEPTH]; -static inline uint32_t iso_get_file_size(uint32_t load_rba) +static inline long iso_get_file_size(uint32_t load_rba) { IsoVolDesc *vd = (IsoVolDesc *)sec; IsoDirHdr *cur_record = &vd->vd.primary.rootdir; uint8_t *temp = sec + ISO_SECTOR_SIZE; int level = 0; - read_iso_sector(ISO_PRIMARY_VD_SECTOR, sec, - "Failed to read ISO primary descriptor"); + if (virtio_read(ISO_PRIMARY_VD_SECTOR, sec)) { + puts("Failed to read ISO primary descriptor"); + return -EIO; + } + sec_loc[0] = iso_733_to_u32(cur_record->ext_loc); dir_rem[0] = 0; sec_offset[0] = 0; while (level >= 0) { - IPL_assert(sec_offset[level] <= ISO_SECTOR_SIZE, - "Directory tree structure violation"); + if (sec_offset[level] > ISO_SECTOR_SIZE) { + puts("Directory tree structure violation"); + return -EIO; + } cur_record = (IsoDirHdr *)(temp + sec_offset[level]); if (sec_offset[level] == 0) { - read_iso_sector(sec_loc[level], temp, - "Failed to read ISO directory"); + if (virtio_read(sec_loc[level], temp)) { + puts("Failed to read ISO directory"); + return -EIO; + } if (dir_rem[level] == 0) { /* Skip self and parent records */ dir_rem[level] = iso_733_to_u32(cur_record->data_len) - @@ -658,7 +885,7 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba) if (cur_record->file_flags & 0x2) { /* Subdirectory */ if (level == ISO9660_MAX_DIR_DEPTH - 1) { - sclp_print("ISO-9660 directory depth limit exceeded\n"); + puts("ISO-9660 directory depth limit exceeded"); } else { level++; sec_loc[level] = iso_733_to_u32(cur_record->ext_loc); @@ -672,8 +899,10 @@ static inline uint32_t iso_get_file_size(uint32_t load_rba) if (dir_rem[level] == 0) { /* Nothing remaining */ level--; - read_iso_sector(sec_loc[level], temp, - "Failed to read ISO directory"); + if (virtio_read(sec_loc[level], temp)) { + puts("Failed to read ISO directory"); + return -EIO; + } } } @@ -688,19 +917,24 @@ static void load_iso_bc_entry(IsoBcSection *load) * is padded and ISO_SECTOR_SIZE bytes aligned */ uint32_t blks_to_load = bswap16(s.sector_count) >> ET_SECTOR_SHIFT; - uint32_t real_size = iso_get_file_size(bswap32(s.load_rba)); + long real_size = iso_get_file_size(bswap32(s.load_rba)); - if (real_size) { + if (real_size > 0) { /* Round up blocks to load */ blks_to_load = (real_size + ISO_SECTOR_SIZE - 1) / ISO_SECTOR_SIZE; - sclp_print("ISO boot image size verified\n"); + puts("ISO boot image size verified"); } else { - sclp_print("ISO boot image size could not be verified\n"); + puts("ISO boot image size could not be verified"); + if (real_size < 0) { + return; + } } - read_iso_boot_image(bswap32(s.load_rba), + if (read_iso_boot_image(bswap32(s.load_rba), (void *)((uint64_t)bswap16(s.load_segment)), - blks_to_load); + blks_to_load)) { + return; + } jump_to_low_kernel(); } @@ -723,17 +957,18 @@ static uint32_t find_iso_bc(void) return bswap32(et->bc_offset); } } - read_iso_sector(block_num++, sec, - "Failed to read ISO volume descriptor"); + if (virtio_read(block_num++, sec)) { + puts("Failed to read ISO volume descriptor"); + return 0; + } } return 0; } -static IsoBcSection *find_iso_bc_entry(void) +static IsoBcSection *find_iso_bc_entry(uint32_t offset) { IsoBcEntry *e = (IsoBcEntry *)sec; - uint32_t offset = find_iso_bc(); int i; unsigned int loadparm = get_loadparm_index(); @@ -741,11 +976,13 @@ static IsoBcSection *find_iso_bc_entry(void) return NULL; } - read_iso_sector(offset, sec, "Failed to read El Torito boot catalog"); + if (virtio_read(offset, sec)) { + puts("Failed to read El Torito boot catalog"); + return NULL; + } if (!is_iso_bc_valid(e)) { /* The validation entry is mandatory */ - panic("No valid boot catalog found!\n"); return NULL; } @@ -765,19 +1002,25 @@ static IsoBcSection *find_iso_bc_entry(void) } } - panic("No suitable boot entry found on ISO-9660 media!\n"); - return NULL; } -static void ipl_iso_el_torito(void) +static int ipl_iso_el_torito(void) { - IsoBcSection *s = find_iso_bc_entry(); + uint32_t offset = find_iso_bc(); + if (!offset) { + return 0; + } + + IsoBcSection *s = find_iso_bc_entry(offset); if (s) { - load_iso_bc_entry(s); - /* no return */ + load_iso_bc_entry(s); /* only return in error */ + return -1; } + + puts("No suitable boot entry found on ISO-9660 media!"); + return -EIO; } /** @@ -799,7 +1042,7 @@ static bool has_iso_signature(void) * Bus specific IPL sequences */ -static void zipl_load_vblk(void) +static int zipl_load_vblk(void) { int blksize = virtio_get_block_size(); @@ -807,26 +1050,30 @@ static void zipl_load_vblk(void) if (blksize != VIRTIO_ISO_BLOCK_SIZE) { virtio_assume_iso9660(); } - ipl_iso_el_torito(); + if (ipl_iso_el_torito()) { + return 0; + } } if (blksize != VIRTIO_DASD_DEFAULT_BLOCK_SIZE) { - sclp_print("Using guessed DASD geometry.\n"); + puts("Using guessed DASD geometry."); virtio_assume_eckd(); } - ipl_eckd(); + return ipl_eckd(); } -static void zipl_load_vscsi(void) +static int zipl_load_vscsi(void) { if (virtio_get_block_size() == VIRTIO_ISO_BLOCK_SIZE) { /* Is it an ISO image in non-CD drive? */ - ipl_iso_el_torito(); + if (ipl_iso_el_torito()) { + return 0; + } } - sclp_print("Using guessed DASD geometry.\n"); + puts("Using guessed DASD geometry."); virtio_assume_eckd(); - ipl_eckd(); + return ipl_eckd(); } /*********************************************************************** @@ -839,14 +1086,20 @@ void zipl_load(void) if (vdev->is_cdrom) { ipl_iso_el_torito(); - panic("\n! Cannot IPL this ISO image !\n"); + puts("Failed to IPL this ISO image!"); + return; } if (virtio_get_device_type() == VIRTIO_ID_NET) { - jump_to_IPL_code(vdev->netboot_start_addr); + netmain(); + puts("Failed to IPL from this network!"); + return; } - ipl_scsi(); + if (ipl_scsi()) { + puts("Failed to IPL from this SCSI device!"); + return; + } switch (virtio_get_device_type()) { case VIRTIO_ID_BLOCK: @@ -856,8 +1109,9 @@ void zipl_load(void) zipl_load_vscsi(); break; default: - panic("\n! Unknown IPL device type !\n"); + puts("Unknown IPL device type!"); + return; } - sclp_print("zIPL load failed.\n"); + puts("zIPL load failed!"); } diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 3946aa3f8d..95943441d3 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -16,6 +16,7 @@ typedef uint64_t block_number_t; #define NULL_BLOCK_NR 0xffffffffffffffffULL +#define ERROR_BLOCK_NR 0xfffffffffffffffeULL #define FREE_SPACE_FILLER '\xAA' @@ -45,9 +46,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 +72,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 +316,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; @@ -312,9 +337,7 @@ static inline void print_volser(const void *volser) ebcdic_to_ascii((char *)volser, ascii, 6); ascii[6] = '\0'; - sclp_print("VOLSER=["); - sclp_print(ascii); - sclp_print("]\n"); + printf("VOLSER=[%s]\n", ascii); } static inline bool unused_space(const void *p, size_t size) @@ -363,17 +386,14 @@ static inline uint32_t iso_733_to_u32(uint64_t x) #define ISO_PRIMARY_VD_SECTOR 16 -static inline void read_iso_sector(uint32_t block_offset, void *buf, - const char *errmsg) -{ - IPL_assert(virtio_read_many(block_offset, buf, 1) == 0, errmsg); -} - -static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr, +static inline int read_iso_boot_image(uint32_t block_offset, void *load_addr, uint32_t blks_to_load) { - IPL_assert(virtio_read_many(block_offset, load_addr, blks_to_load) == 0, - "Failed to read boot image!"); + if (virtio_read_many(block_offset, load_addr, blks_to_load)) { + puts("Failed to read boot image!"); + return -1; + } + return 0; } #define ISO9660_MAX_DIR_DEPTH 8 diff --git a/pc-bios/s390-ccw/cio.c b/pc-bios/s390-ccw/cio.c index 83ca27ab41..5d543da73f 100644 --- a/pc-bios/s390-ccw/cio.c +++ b/pc-bios/s390-ccw/cio.c @@ -11,7 +11,8 @@ * directory. */ -#include "libc.h" +#include +#include #include "s390-ccw.h" #include "s390-arch.h" #include "helper.h" @@ -58,7 +59,8 @@ uint16_t cu_type(SubChannelId schid) }; if (do_cio(schid, CU_TYPE_UNKNOWN, ptr2u32(&sense_id_ccw), CCW_FMT1)) { - panic("Failed to run SenseID CCw\n"); + puts("Failed to run SenseID CCW"); + return CU_TYPE_UNKNOWN; } return sense_data.cu_type; @@ -90,9 +92,9 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd) char msgline[512]; if (sd->config_info & 0x8000) { - sclp_print("Eckd Dasd Sense Data (fmt 24-bytes):\n"); + puts("Eckd Dasd Sense Data (fmt 24-bytes):"); } else { - sclp_print("Eckd Dasd Sense Data (fmt 32-bytes):\n"); + puts("Eckd Dasd Sense Data (fmt 32-bytes):"); } strcat(msgline, " Sense Condition Flags :"); @@ -158,22 +160,21 @@ static void print_eckd_dasd_sense_data(SenseDataEckdDasd *sd) if (sd->status[1] & SNS_STAT2_IMPRECISE_END) { strcat(msgline, " [Imprecise-End]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); - print_int(" Residual Count =", sd->res_count); - print_int(" Phys Drive ID =", sd->phys_drive_id); - print_int(" low cyl address =", sd->low_cyl_addr); - print_int(" head addr & hi cyl =", sd->head_high_cyl_addr); - print_int(" format/message =", sd->fmt_msg); - print_int(" fmt-dependent[0-7] =", sd->fmt_dependent_info[0]); - print_int(" fmt-dependent[8-15]=", sd->fmt_dependent_info[1]); - print_int(" prog action code =", sd->program_action_code); - print_int(" Configuration info =", sd->config_info); - print_int(" mcode / hi-cyl =", sd->mcode_hicyl); - print_int(" cyl & head addr [0]=", sd->cyl_head_addr[0]); - print_int(" cyl & head addr [1]=", sd->cyl_head_addr[1]); - print_int(" cyl & head addr [2]=", sd->cyl_head_addr[2]); + printf(" Residual Count = 0x%X\n", sd->res_count); + printf(" Phys Drive ID = 0x%X\n", sd->phys_drive_id); + printf(" low cyl address = 0x%X\n", sd->low_cyl_addr); + printf(" head addr & hi cyl = 0x%X\n", sd->head_high_cyl_addr); + printf(" format/message = 0x%X\n", sd->fmt_msg); + printf(" fmt-dependent[0-7] = 0x%llX\n", sd->fmt_dependent_info[0]); + printf(" fmt-dependent[8-15]= 0x%llX\n", sd->fmt_dependent_info[1]); + printf(" prog action code = 0x%X\n", sd->program_action_code); + printf(" Configuration info = 0x%X\n", sd->config_info); + printf(" mcode / hi-cyl = 0x%X\n", sd->mcode_hicyl); + printf(" cyl & head addr [0]= 0x%X\n", sd->cyl_head_addr[0]); + printf(" cyl & head addr [1]= 0x%X\n", sd->cyl_head_addr[1]); + printf(" cyl & head addr [2]= 0x%X\n", sd->cyl_head_addr[2]); } static void print_irb_err(Irb *irb) @@ -182,7 +183,7 @@ static void print_irb_err(Irb *irb) uint64_t prev_ccw = *(uint64_t *)u32toptr(irb->scsw.cpa - 8); char msgline[256]; - sclp_print("Interrupt Response Block Data:\n"); + puts("Interrupt Response Block Data:"); strcat(msgline, " Function Ctrl :"); if (irb->scsw.ctrl & SCSW_FCTL_START_FUNC) { @@ -194,8 +195,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) { strcat(msgline, " [Clear]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Activity Ctrl :"); @@ -220,8 +220,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.ctrl & SCSW_ACTL_SUSPENDED) { strcat(msgline, " [Suspended]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Status Ctrl :"); @@ -240,9 +239,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.ctrl & SCSW_SCTL_STATUS_PEND) { strcat(msgline, " [Status-Pending]"); } - - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Device Status :"); @@ -270,8 +267,7 @@ static void print_irb_err(Irb *irb) if (irb->scsw.dstat & SCSW_DSTAT_UEXCP) { strcat(msgline, " [Unit-Exception]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); msgline[0] = '\0'; strcat(msgline, " Channel Status :"); @@ -299,12 +295,11 @@ static void print_irb_err(Irb *irb) if (irb->scsw.cstat & SCSW_CSTAT_CHAINCHK) { strcat(msgline, " [Chaining-Check]"); } - strcat(msgline, "\n"); - sclp_print(msgline); + puts(msgline); - print_int(" cpa=", irb->scsw.cpa); - print_int(" prev_ccw=", prev_ccw); - print_int(" this_ccw=", this_ccw); + printf(" cpa= 0x%X\n", irb->scsw.cpa); + printf(" prev_ccw= 0x%llX\n", prev_ccw); + printf(" this_ccw= 0x%llX\n", this_ccw); } /* @@ -341,7 +336,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) return -1; } if (rc) { - print_int("ssch failed with cc=", rc); + printf("ssch failed with cc= 0x%x\n", rc); return rc; } @@ -350,7 +345,7 @@ static int __do_cio(SubChannelId schid, uint32_t ccw_addr, int fmt, Irb *irb) /* collect status */ rc = tsch(schid, irb); if (rc) { - print_int("tsch failed with cc=", rc); + printf("tsch failed with cc= 0x%X\n", rc); } return rc; @@ -406,12 +401,12 @@ int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt) continue; } - sclp_print("cio device error\n"); - print_int(" ssid ", schid.ssid); - print_int(" cssid ", schid.cssid); - print_int(" sch_no", schid.sch_no); - print_int(" ctrl-unit type", cutype); - sclp_print("\n"); + printf("cio device error\n"); + printf(" ssid 0x%X\n", schid.ssid); + printf(" cssid 0x%X\n", schid.cssid); + printf(" sch_no 0x%X\n", schid.sch_no); + printf(" ctrl-unit type 0x%X\n", cutype); + printf("\n"); print_irb_err(&irb); if (cutype == CU_TYPE_DASD_3990 || cutype == CU_TYPE_DASD_2107 || cutype == CU_TYPE_UNKNOWN) { diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h index 88a88adfd2..6a5e86ba01 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 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 ...*/ + 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 */ @@ -361,6 +361,8 @@ typedef struct CcwSearchIdData { uint8_t record; } __attribute__((packed)) CcwSearchIdData; +extern SubChannelId net_schid; + int enable_mss_facility(void); void enable_subchannel(SubChannelId schid); uint16_t cu_type(SubChannelId schid); diff --git a/pc-bios/s390-ccw/dasd-ipl.c b/pc-bios/s390-ccw/dasd-ipl.c index 254bb1a15e..babece95ea 100644 --- a/pc-bios/s390-ccw/dasd-ipl.c +++ b/pc-bios/s390-ccw/dasd-ipl.c @@ -8,7 +8,8 @@ * directory. */ -#include "libc.h" +#include +#include #include "s390-ccw.h" #include "s390-arch.h" #include "dasd-ipl.h" @@ -82,7 +83,7 @@ static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype, do { has_next = dynamic_cp_fixup(cpa, &next_cpa); - print_int("executing ccw chain at ", cpa); + printf("executing ccw chain at 0x%X\n", cpa); enable_prefixing(); rc = do_cio(schid, cutype, cpa, CCW_FMT0); disable_prefixing(); @@ -110,38 +111,29 @@ static void make_readipl(void) ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */ } -static void run_readipl(SubChannelId schid, uint16_t cutype) +static int run_readipl(SubChannelId schid, uint16_t cutype) { - if (do_cio(schid, cutype, 0x00, CCW_FMT0)) { - panic("dasd-ipl: Failed to run Read IPL channel program\n"); - } + return do_cio(schid, cutype, 0x00, CCW_FMT0); } /* * The architecture states that IPL1 data should consist of a psw followed by * format-0 READ and TIC CCWs. Let's sanity check. */ -static void check_ipl1(void) +static bool check_ipl1(void) { Ccw0 *ccwread = (Ccw0 *)0x08; Ccw0 *ccwtic = (Ccw0 *)0x10; - if (ccwread->cmd_code != CCW_CMD_DASD_READ || - ccwtic->cmd_code != CCW_CMD_TIC) { - panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n"); - } + return (ccwread->cmd_code == CCW_CMD_DASD_READ && + ccwtic->cmd_code == CCW_CMD_TIC); } -static void check_ipl2(uint32_t ipl2_addr) +static bool check_ipl2(uint32_t ipl2_addr) { Ccw0 *ccw = u32toptr(ipl2_addr); - if (ipl2_addr == 0x00) { - panic("IPL2 address invalid. Is this disk really bootable?\n"); - } - if (ccw->cmd_code == 0x00) { - panic("IPL2 ccw data invalid. Is this disk really bootable?\n"); - } + return (ipl2_addr != 0x00 && ccw->cmd_code != 0x00); } static uint32_t read_ipl2_addr(void) @@ -187,52 +179,67 @@ static void ipl1_fixup(void) ccwSearchTic->cda = ptr2u32(ccwSearchID); } -static void run_ipl1(SubChannelId schid, uint16_t cutype) +static int run_ipl1(SubChannelId schid, uint16_t cutype) { uint32_t startAddr = 0x08; - if (do_cio(schid, cutype, startAddr, CCW_FMT0)) { - panic("dasd-ipl: Failed to run IPL1 channel program\n"); - } + return do_cio(schid, cutype, startAddr, CCW_FMT0); } -static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr) +static int run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr) { - if (run_dynamic_ccw_program(schid, cutype, addr)) { - panic("dasd-ipl: Failed to run IPL2 channel program\n"); - } + return run_dynamic_ccw_program(schid, cutype, addr); } /* * Limitations in vfio-ccw support complicate the IPL process. Details can * be found in docs/devel/s390-dasd-ipl.rst */ -void dasd_ipl(SubChannelId schid, uint16_t cutype) +int dasd_ipl(SubChannelId schid, uint16_t cutype) { PSWLegacy *pswl = (PSWLegacy *) 0x00; uint32_t ipl2_addr; /* Construct Read IPL CCW and run it to read IPL1 from boot disk */ make_readipl(); - run_readipl(schid, cutype); + if (run_readipl(schid, cutype)) { + puts("Failed to run Read IPL channel program"); + return -EIO; + } + ipl2_addr = read_ipl2_addr(); - check_ipl1(); + + if (!check_ipl1()) { + puts("IPL1 invalid for DASD-IPL"); + return -EINVAL; + } /* * Fixup IPL1 channel program to account for vfio-ccw limitations, then run * it to read IPL2 channel program from boot disk. */ ipl1_fixup(); - run_ipl1(schid, cutype); - check_ipl2(ipl2_addr); + if (run_ipl1(schid, cutype)) { + puts("Failed to run IPL1 channel program"); + return -EIO; + } + + if (!check_ipl2(ipl2_addr)) { + puts("IPL2 invalid for DASD-IPL"); + return -EINVAL; + } /* * Run IPL2 channel program to read operating system code from boot disk */ - run_ipl2(schid, cutype, ipl2_addr); + if (run_ipl2(schid, cutype, ipl2_addr)) { + puts("Failed to run IPL2 channel program"); + return -EIO; + } /* Transfer control to the guest operating system */ pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */ pswl->addr |= PSW_MASK_BAMODE; /* ... */ jump_to_low_kernel(); + return -1; } diff --git a/pc-bios/s390-ccw/dasd-ipl.h b/pc-bios/s390-ccw/dasd-ipl.h index c394828906..eb1898c84a 100644 --- a/pc-bios/s390-ccw/dasd-ipl.h +++ b/pc-bios/s390-ccw/dasd-ipl.h @@ -11,6 +11,6 @@ #ifndef DASD_IPL_H #define DASD_IPL_H -void dasd_ipl(SubChannelId schid, uint16_t cutype); +int dasd_ipl(SubChannelId schid, uint16_t cutype); #endif /* DASD_IPL_H */ 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 cb6ac8a880..08f259ff31 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -12,88 +12,16 @@ #ifndef IPLB_H #define IPLB_H -#define LOADPARM_LEN 8 +#ifndef QEMU_PACKED +#define QEMU_PACKED __attribute__((packed)) +#endif -struct IplBlockCcw { - uint8_t reserved0[85]; - uint8_t ssid; - uint16_t devno; - uint8_t vm_flags; - uint8_t reserved3[3]; - uint32_t vm_parm_len; - uint8_t nss_name[8]; - uint8_t vm_parm[64]; - uint8_t reserved4[8]; -} __attribute__ ((packed)); -typedef struct IplBlockCcw IplBlockCcw; - -struct IplBlockFcp { - uint8_t reserved1[305 - 1]; - uint8_t opt; - uint8_t reserved2[3]; - uint16_t reserved3; - uint16_t devno; - uint8_t reserved4[4]; - uint64_t wwpn; - uint64_t lun; - uint32_t bootprog; - uint8_t reserved5[12]; - uint64_t br_lba; - uint32_t scp_data_len; - uint8_t reserved6[260]; - uint8_t scp_data[]; -} __attribute__ ((packed)); -typedef struct IplBlockFcp IplBlockFcp; - -struct IplBlockQemuScsi { - uint32_t lun; - uint16_t target; - uint16_t channel; - uint8_t reserved0[77]; - uint8_t ssid; - uint16_t devno; -} __attribute__ ((packed)); -typedef struct IplBlockQemuScsi IplBlockQemuScsi; - -struct IplParameterBlock { - uint32_t len; - uint8_t reserved0[3]; - uint8_t version; - uint32_t blk0_len; - uint8_t pbt; - uint8_t flags; - uint16_t reserved01; - uint8_t loadparm[LOADPARM_LEN]; - union { - IplBlockCcw ccw; - IplBlockFcp fcp; - IplBlockQemuScsi scsi; - }; -} __attribute__ ((packed)); -typedef struct IplParameterBlock IplParameterBlock; - -extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); - -#define QIPL_ADDRESS 0xcc - -/* Boot Menu flags */ -#define QIPL_FLAG_BM_OPTS_CMD 0x80 -#define QIPL_FLAG_BM_OPTS_ZIPL 0x40 - -/* - * This definition must be kept in sync with the definition - * in hw/s390x/ipl.h - */ -struct QemuIplParameters { - uint8_t qipl_flags; - uint8_t reserved1[3]; - uint64_t netboot_start_addr; - uint32_t boot_menu_timeout; - uint8_t reserved2[12]; -} __attribute__ ((packed)); -typedef struct QemuIplParameters QemuIplParameters; +#include +#include extern QemuIplParameters qipl; +extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); +extern bool have_iplb; #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 @@ -123,4 +51,26 @@ static inline bool set_iplb(IplParameterBlock *iplb) return manage_iplb(iplb, false); } +/* + * The IPL started on the device, but failed in some way. If the IPLB chain + * still has more devices left to try, use the next device in order. + */ +static inline bool load_next_iplb(void) +{ + IplParameterBlock *next_iplb; + + if (qipl.chain_len < 1) { + return false; + } + + qipl.index++; + next_iplb = (IplParameterBlock *) qipl.next_iplb; + memcpy(&iplb, next_iplb, sizeof(IplParameterBlock)); + + qipl.chain_len--; + qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock); + + return true; +} + #endif /* IPLB_H */ diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c index 78f5f46533..86321d0f46 100644 --- a/pc-bios/s390-ccw/jump2ipl.c +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -6,7 +6,8 @@ * directory. */ -#include "libc.h" +#include +#include #include "s390-ccw.h" #include "s390-arch.h" @@ -32,16 +33,22 @@ static void jump_to_IPL_addr(void) /* should not return */ } -void jump_to_IPL_code(uint64_t address) +int jump_to_IPL_code(uint64_t address) { /* store the subsystem information _after_ the bootmap was loaded */ write_subsystem_identification(); write_iplb_location(); - /* prevent unknown IPL types in the guest */ + /* + * The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The + * iplb.devno is set to the boot position of the target SCSI device. + */ if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { - iplb.pbt = S390_IPL_TYPE_CCW; - set_iplb(&iplb); + iplb.devno = qipl.index; + } + + if (have_iplb && !set_iplb(&iplb)) { + panic("Failed to set IPLB"); } /* @@ -57,7 +64,7 @@ void jump_to_IPL_code(uint64_t address) debug_print_int("set IPL addr to", address ?: *reset_psw & PSW_MASK_SHORT_ADDR); /* Ensure the guest output starts fresh */ - sclp_print("\n"); + printf("\n"); /* * HACK ALERT. @@ -67,7 +74,8 @@ void jump_to_IPL_code(uint64_t address) asm volatile("lghi %%r1,1\n\t" "diag %%r1,%%r1,0x308\n\t" : : : "1", "memory"); - panic("\n! IPL returns !\n"); + puts("IPL code jump failed"); + return -1; } void jump_to_low_kernel(void) diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c deleted file mode 100644 index 3187923950..0000000000 --- a/pc-bios/s390-ccw/libc.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * libc-style definitions and functions - * - * Copyright 2018 IBM Corp. - * Author(s): Collin L. Walling - * - * This code 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. - */ - -#include "libc.h" -#include "s390-ccw.h" - -/** - * atoui: - * @str: the string to be converted. - * - * Given a string @str, convert it to an integer. Leading spaces are - * ignored. Any other non-numerical value will terminate the conversion - * and return 0. This function only handles numbers between 0 and - * UINT64_MAX inclusive. - * - * Returns: an integer converted from the string @str, or the number 0 - * if an error occurred. - */ -uint64_t atoui(const char *str) -{ - int val = 0; - - if (!str || !str[0]) { - return 0; - } - - while (*str == ' ') { - str++; - } - - while (*str) { - if (!isdigit(*(unsigned char *)str)) { - break; - } - val = val * 10 + *str - '0'; - str++; - } - - return val; -} - -/** - * uitoa: - * @num: an integer (base 10) to be converted. - * @str: a pointer to a string to store the conversion. - * @len: the length of the passed string. - * - * Given an integer @num, convert it to a string. The string @str must be - * allocated beforehand. The resulting string will be null terminated and - * returned. This function only handles numbers between 0 and UINT64_MAX - * inclusive. - * - * Returns: the string @str of the converted integer @num - */ -char *uitoa(uint64_t num, char *str, size_t len) -{ - long num_idx = 1; /* account for NUL */ - uint64_t tmp = num; - - IPL_assert(str != NULL, "uitoa: no space allocated to store string"); - - /* Count indices of num */ - while ((tmp /= 10) != 0) { - num_idx++; - } - - /* Check if we have enough space for num and NUL */ - IPL_assert(len > num_idx, "uitoa: array too small for conversion"); - - str[num_idx--] = '\0'; - - /* Convert int to string */ - while (num_idx >= 0) { - str[num_idx--] = num % 10 + '0'; - num /= 10; - } - - return str; -} diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h deleted file mode 100644 index bcdc45732d..0000000000 --- a/pc-bios/s390-ccw/libc.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * libc-style definitions and functions - * - * Copyright (c) 2013 Alexander Graf - * - * This code 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. - */ - -#ifndef S390_CCW_LIBC_H -#define S390_CCW_LIBC_H - -typedef unsigned long size_t; -typedef int bool; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; - -static inline void *memset(void *s, int c, size_t n) -{ - size_t i; - unsigned char *p = s; - - for (i = 0; i < n; i++) { - p[i] = c; - } - - return s; -} - -static inline void *memcpy(void *s1, const void *s2, size_t n) -{ - uint8_t *dest = s1; - const uint8_t *src = s2; - size_t i; - - for (i = 0; i < n; i++) { - dest[i] = src[i]; - } - - return s1; -} - -static inline int memcmp(const void *s1, const void *s2, size_t n) -{ - size_t i; - const uint8_t *p1 = s1, *p2 = s2; - - for (i = 0; i < n; i++) { - if (p1[i] != p2[i]) { - return p1[i] > p2[i] ? 1 : -1; - } - } - - return 0; -} - -static inline size_t strlen(const char *str) -{ - size_t i; - for (i = 0; *str; i++) { - str++; - } - return i; -} - -static inline char *strcat(char *dest, const char *src) -{ - int i; - char *dest_end = dest + strlen(dest); - - for (i = 0; i <= strlen(src); i++) { - dest_end[i] = src[i]; - } - return dest; -} - -static inline int isdigit(int c) -{ - return (c >= '0') && (c <= '9'); -} - -uint64_t atoui(const char *str); -char *uitoa(uint64_t num, char *str, size_t len); - -#endif diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index a2def83e82..76bf743900 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -8,7 +8,9 @@ * directory. */ -#include "libc.h" +#include +#include +#include #include "helper.h" #include "s390-arch.h" #include "s390-ccw.h" @@ -17,12 +19,11 @@ #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; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); -static bool have_iplb; +bool have_iplb; static uint16_t cutype; LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ @@ -37,8 +38,13 @@ LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */ */ void write_subsystem_identification(void) { - lowcore->subchannel_id = blk_schid.sch_id; - lowcore->subchannel_nr = blk_schid.sch_no; + if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() == VIRTIO_ID_NET) { + lowcore->subchannel_id = net_schid.sch_id; + lowcore->subchannel_nr = net_schid.sch_no; + } else { + lowcore->subchannel_id = blk_schid.sch_id; + lowcore->subchannel_nr = blk_schid.sch_no; + } lowcore->io_int_parm = 0; } @@ -49,9 +55,15 @@ void write_iplb_location(void) } } +static void copy_qipl(void) +{ + QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; + memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); +} + unsigned int get_loadparm_index(void) { - return atoui(loadparm_str); + return atoi(loadparm_str); } static int is_dev_possibly_bootable(int dev_no, int sch_no) @@ -71,6 +83,9 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no) enable_subchannel(blk_schid); cutype = cu_type(blk_schid); + if (cutype == CU_TYPE_UNKNOWN) { + return -EIO; + } /* * Note: we always have to run virtio_is_supported() here to make @@ -143,6 +158,7 @@ static void menu_setup(void) /* If loadparm was set to any other value, then do not enable menu */ if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) { + menu_set_parms(qipl.qipl_flags & ~BOOT_MENU_FLAG_MASK, 0); return; } @@ -175,26 +191,34 @@ static void boot_setup(void) { char lpmsg[] = "LOADPARM=[________]\n"; - sclp_get_loadparm_ascii(loadparm_str); + if (have_iplb && memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) { + ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN); + } else { + sclp_get_loadparm_ascii(loadparm_str); + } + + if (have_iplb) { + menu_setup(); + } + memcpy(lpmsg + 10, loadparm_str, 8); - sclp_print(lpmsg); + puts(lpmsg); /* * Clear out any potential S390EP magic (see jump_to_low_kernel()), * so we don't taint our decision-making process during a reboot. */ memset((char *)S390EP, 0, 6); - - have_iplb = store_iplb(&iplb); } -static void find_boot_device(void) +static bool find_boot_device(void) { VDev *vdev = virtio_get_device(); - bool found; + bool found = false; switch (iplb.pbt) { case S390_IPL_TYPE_CCW: + vdev->scsi_device_selected = false; debug_print_int("device no. ", iplb.ccw.devno); blk_schid.ssid = iplb.ccw.ssid & 0x3; debug_print_int("ssid ", blk_schid.ssid); @@ -209,28 +233,21 @@ static void find_boot_device(void) found = find_subch(iplb.scsi.devno); break; default: - panic("List-directed IPL not supported yet!\n"); + puts("Unsupported IPLB"); } - IPL_assert(found, "Boot device not found\n"); + return found; } static int virtio_setup(void) { VDev *vdev = virtio_get_device(); - QemuIplParameters *early_qipl = (QemuIplParameters *)QIPL_ADDRESS; + vdev->is_cdrom = false; int ret; - memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); - - if (have_iplb) { - menu_setup(); - } - switch (vdev->senseid.cu_model) { case VIRTIO_ID_NET: - sclp_print("Network boot device detected\n"); - vdev->netboot_start_addr = qipl.netboot_start_addr; + puts("Network boot device detected"); return 0; case VIRTIO_ID_BLOCK: ret = virtio_blk_setup_device(blk_schid); @@ -239,11 +256,13 @@ static int virtio_setup(void) ret = virtio_scsi_setup_device(blk_schid); break; default: - panic("\n! No IPL device available !\n"); + puts("\n! No IPL device available !\n"); + return -1; } - if (!ret) { - IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); + if (!ret && !virtio_ipl_disk_is_valid()) { + puts("No valid IPL device detected"); + return -ENODEV; } return ret; @@ -254,16 +273,15 @@ static void ipl_boot_device(void) switch (cutype) { case CU_TYPE_DASD_3990: case CU_TYPE_DASD_2107: - dasd_ipl(blk_schid, cutype); /* no return */ + dasd_ipl(blk_schid, cutype); break; case CU_TYPE_VIRTIO: if (virtio_setup() == 0) { - zipl_load(); /* Only returns in case of errors */ + zipl_load(); } break; default: - print_int("Attempting to boot from unexpected device type", cutype); - panic("\nBoot failed.\n"); + printf("Attempting to boot from unexpected device type 0x%X\n", cutype); } } @@ -288,20 +306,28 @@ static void probe_boot_device(void) } } - sclp_print("Could not find a suitable boot device (none specified)\n"); + puts("Could not find a suitable boot device (none specified)"); } void main(void) { + copy_qipl(); sclp_setup(); css_setup(); - boot_setup(); - if (have_iplb) { - find_boot_device(); - ipl_boot_device(); - } else { + have_iplb = store_iplb(&iplb); + if (!have_iplb) { + boot_setup(); probe_boot_device(); } - panic("Failed to load OS from hard disk\n"); + while (have_iplb) { + boot_setup(); + if (have_iplb && find_boot_device()) { + ipl_boot_device(); + } + have_iplb = load_next_iplb(); + } + + panic("No suitable device for IPL. Halting..."); + } diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index d601952d3e..84062e94af 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -9,7 +9,10 @@ * directory. */ -#include "libc.h" +#include +#include +#include +#include #include "s390-ccw.h" #include "sclp.h" #include "s390-time.h" @@ -93,7 +96,7 @@ static int read_prompt(char *buf, size_t len) case KEYCODE_BACKSP: if (idx > 0) { buf[--idx] = 0; - sclp_print("\b \b"); + printf("\b \b"); } continue; case KEYCODE_ENTER: @@ -103,7 +106,7 @@ static int read_prompt(char *buf, size_t len) /* Echo input and add to buffer */ if (idx < len) { buf[idx++] = inp[0]; - sclp_print(inp); + printf("%s", inp); } } } @@ -140,22 +143,19 @@ static int get_index(void) } } - return atoui(buf); + return atoi(buf); } static void boot_menu_prompt(bool retry) { - char tmp[11]; - if (retry) { - sclp_print("\nError: undefined configuration" + printf("\nError: undefined configuration" "\nPlease choose:\n"); } else if (timeout > 0) { - sclp_print("Please choose (default will boot in "); - sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp))); - sclp_print(" seconds):\n"); + printf("Please choose (default will boot in %d seconds):\n", + (int)(timeout / 1000)); } else { - sclp_print("Please choose:\n"); + puts("Please choose:"); } } @@ -163,7 +163,6 @@ static int get_boot_index(bool *valid_entries) { int boot_index; bool retry = false; - char tmp[5]; do { boot_menu_prompt(retry); @@ -172,8 +171,7 @@ static int get_boot_index(bool *valid_entries) } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || !valid_entries[boot_index]); - sclp_print("\nBooting entry #"); - sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); + printf("\nBooting entry #%d", boot_index); return boot_index; } @@ -187,9 +185,9 @@ static int zipl_print_entry(const char *data, size_t len) buf[len] = '\n'; buf[len + 1] = '\0'; - sclp_print(buf); + printf("%s", buf); - return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); + return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf); } int menu_get_zipl_boot_index(const char *menu_data) @@ -209,7 +207,7 @@ int menu_get_zipl_boot_index(const char *menu_data) } /* Print banner */ - sclp_print("s390-ccw zIPL Boot Menu\n\n"); + puts("s390-ccw zIPL Boot Menu\n"); menu_data += strlen(menu_data) + 1; /* Print entries */ @@ -221,37 +219,34 @@ int menu_get_zipl_boot_index(const char *menu_data) valid_entries[entry] = true; if (entry == 0) { - sclp_print("\n"); + printf("\n"); } } - sclp_print("\n"); + printf("\n"); return get_boot_index(valid_entries); } int menu_get_enum_boot_index(bool *valid_entries) { - char tmp[3]; int i; - sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); + puts("s390-ccw Enumerated Boot Menu.\n"); for (i = 0; i < MAX_BOOT_ENTRIES; i++) { if (valid_entries[i]) { if (i < 10) { - sclp_print(" "); + printf(" "); } - sclp_print("["); - sclp_print(uitoa(i, tmp, sizeof(tmp))); - sclp_print("]"); + printf("[%d]", i); if (i == 0) { - sclp_print(" default\n"); + printf(" default\n"); } - sclp_print("\n"); + printf("\n"); } } - sclp_print("\n"); + printf("\n"); return get_boot_index(valid_entries); } diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak deleted file mode 100644 index 046aa35587..0000000000 --- a/pc-bios/s390-ccw/netboot.mak +++ /dev/null @@ -1,62 +0,0 @@ - -SLOF_DIR := $(SRC_PATH)/../../roms/SLOF - -NETOBJS := start.o sclp.o cio.o virtio.o virtio-net.o jump2ipl.o netmain.o - -LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include -LIBNET_INC := -I$(SLOF_DIR)/lib/libnet - -NETLDFLAGS := $(LDFLAGS) -Wl,-Ttext=0x7800000 - -$(NETOBJS): EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) - -s390-netboot.elf: $(NETOBJS) libnet.a libc.a - $(call quiet-command,$(CC) $(NETLDFLAGS) -o $@ $^,Linking) - -s390-netboot.img: s390-netboot.elf - $(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,Stripping $< into) - -# libc files: - -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 $@ $<,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 $@ $<,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 $@ $<,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 $@ $<,Compiling) - -sbrk.o: $(SLOF_DIR)/slof/sbrk.c - $(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 $@ $^,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 = $(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 $@ $<,Compiling) - -libnet.a: $(LIBNETOBJS) - $(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..e46e470db4 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -41,7 +41,6 @@ #define DEFAULT_TFTP_RETRIES 20 extern char _start[]; -void write_iplb_location(void) {} #define KERNEL_ADDR ((void *)0L) #define KERNEL_MAX_SIZE ((long)_start) @@ -50,11 +49,9 @@ 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]; -static SubChannelId net_schid = { .one = 1 }; +SubChannelId net_schid = { .one = 1 }; static uint8_t mac[6]; static uint64_t dest_timer; @@ -294,7 +291,7 @@ static int load_kernel_with_initrd(filename_ip_t *fn_ip, printf("Loading pxelinux.cfg entry '%s'\n", entry->label); if (!entry->kernel) { - printf("Kernel entry is missing!\n"); + puts("Kernel entry is missing!\n"); return -1; } @@ -439,15 +436,6 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) return rc; } -void write_subsystem_identification(void) -{ - SubChannelId *schid = (SubChannelId *) 184; - uint32_t *zeroes = (uint32_t *) 188; - - *schid = net_schid; - *zeroes = 0; -} - static bool find_net_dev(Schib *schib, int dev_no) { int i, r; @@ -476,7 +464,7 @@ static bool find_net_dev(Schib *schib, int dev_no) return false; } -static void virtio_setup(void) +static bool virtio_setup(void) { Schib schib; int ssid; @@ -490,7 +478,7 @@ static void virtio_setup(void) */ enable_mss_facility(); - if (store_iplb(&iplb)) { + if (have_iplb || store_iplb(&iplb)) { IPL_assert(iplb.pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected"); dev_no = iplb.ccw.devno; debug_print_int("device no. ", dev_no); @@ -507,22 +495,26 @@ static void virtio_setup(void) } } - IPL_assert(found, "No virtio net device found"); + return found; } -void main(void) +int netmain(void) { filename_ip_t fn_ip; int rc, fnlen; sclp_setup(); - sclp_print("Network boot starting...\n"); + puts("Network boot starting..."); - virtio_setup(); + if (!virtio_setup()) { + puts("No virtio net device found."); + return -1; + } rc = net_init(&fn_ip); if (rc) { - panic("Network initialization failed. Halting.\n"); + puts("Network initialization failed."); + return -1; } fnlen = strlen(fn_ip.filename); @@ -536,9 +528,10 @@ void main(void) net_release(&fn_ip); if (rc > 0) { - sclp_print("Network loading done, starting kernel...\n"); + puts("Network loading done, starting kernel..."); jump_to_low_kernel(); } - panic("Failed to load OS from network\n"); + puts("Failed to load OS from network."); + return -1; } diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index b88e0550ab..6cdce3e5e5 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -13,15 +13,15 @@ /* #define DEBUG */ +#include +#include +#include +#include + 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 @@ -30,10 +30,8 @@ typedef unsigned long long __u64; #define EIO 1 #define EBUSY 2 #define ENODEV 3 +#define EINVAL 4 -#ifndef NULL -#define NULL 0 -#endif #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif @@ -55,10 +53,12 @@ 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); +/* netmain.c */ +int netmain(void); + /* sclp.c */ void sclp_print(const char *string); void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask); @@ -67,18 +67,18 @@ 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); /* jump2ipl.c */ void write_reset_psw(uint64_t psw); -void jump_to_IPL_code(uint64_t address); +int jump_to_IPL_code(uint64_t address); void jump_to_low_kernel(void); /* menu.c */ @@ -93,7 +93,7 @@ bool menu_is_enabled_enum(void); __attribute__ ((__noreturn__)) static inline void panic(const char *string) { - sclp_print(string); + printf("ERROR: %s\n ", string); disabled_wait(); } @@ -115,20 +115,10 @@ static inline void fill_hex_val(char *out, void *ptr, unsigned size) } } -static inline void print_int(const char *desc, u64 addr) -{ - char out[] = ": 0xffffffffffffffff\n"; - - fill_hex_val(&out[4], &addr, sizeof(addr)); - - sclp_print(desc); - sclp_print(out); -} - static inline void debug_print_int(const char *desc, u64 addr) { #ifdef DEBUG - print_int(desc, addr); + printf("%s 0x%X\n", desc, addr); #endif } @@ -153,18 +143,14 @@ static inline void debug_print_addr(const char *desc, void *p) static inline void IPL_assert(bool term, const char *message) { if (!term) { - sclp_print("\n! "); - sclp_print(message); - panic(" !\n"); /* no return */ + panic(message); /* no return */ } } static inline void IPL_check(bool term, const char *message) { if (!term) { - sclp_print("\n! WARNING: "); - sclp_print(message); - sclp_print(" !\n"); + printf("WARNING: %s\n", message); } } diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index 7251f9af4d..4a07de018d 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -8,7 +8,7 @@ * directory. */ -#include "libc.h" +#include #include "s390-ccw.h" #include "sclp.h" @@ -101,11 +101,6 @@ long write(int fd, const void *str, size_t len) return len; } -void sclp_print(const char *str) -{ - write(1, str, strlen(str)); -} - void sclp_get_loadparm_ascii(char *loadparm) { diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index 6072906df4..b70213e412 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 empty? */ - 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,72 @@ 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 + .balign 8 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 + +.data + .balign 8 +bss_start_literal: + .quad __bss_start + +.bss + .balign 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 794f99b42c..7b2d1e20f4 100644 --- a/pc-bios/s390-ccw/virtio-blkdev.c +++ b/pc-bios/s390-ccw/virtio-blkdev.c @@ -8,7 +8,7 @@ * directory. */ -#include "libc.h" +#include #include "s390-ccw.h" #include "virtio.h" #include "virtio-scsi.h" @@ -16,7 +16,7 @@ #define VIRTIO_BLK_F_GEOMETRY (1 << 4) #define VIRTIO_BLK_F_BLK_SIZE (1 << 6) -static int virtio_blk_read_many(VDev *vdev, ulong sector, void *load_addr, +static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr, int sec_num) { VirtioBlkOuthdr out_hdr; @@ -49,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(); @@ -59,34 +59,34 @@ int virtio_read_many(ulong sector, void *load_addr, int sec_num) case VIRTIO_ID_SCSI: return virtio_scsi_read_many(vdev, sector, load_addr, sec_num); } - panic("\n! No readable IPL device !\n"); + 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; + return 0; } - sclp_print("."); + printf("."); status = virtio_read_many(sec, (void *)addr, sec_num); if (status) { - panic("I/O Error"); + return 0; } addr += sec_num * virtio_get_block_size(); 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); } @@ -230,7 +230,7 @@ int virtio_blk_setup_device(SubChannelId schid) vdev->schid = schid; virtio_setup_ccw(vdev); - sclp_print("Using virtio-blk.\n"); + puts("Using virtio-blk."); return 0; } diff --git a/pc-bios/s390-ccw/virtio-net.c b/pc-bios/s390-ccw/virtio-net.c index 2fcb0a58c5..578c89d0c5 100644 --- a/pc-bios/s390-ccw/virtio-net.c +++ b/pc-bios/s390-ccw/virtio-net.c @@ -51,11 +51,16 @@ int virtio_net_init(void *mac_addr) void *buf; int i; + rx_last_idx = 0; + vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT; virtio_setup_ccw(vdev); - IPL_assert(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT, - "virtio-net device does not support the MAC address feature"); + if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) { + puts("virtio-net device does not support the MAC address feature"); + return -1; + } + memcpy(mac_addr, vdev->config.net.mac, ETH_ALEN); for (i = 0; i < 64; i++) { diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index dcce696a33..71db75ce7b 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -9,7 +9,8 @@ * directory. */ -#include "libc.h" +#include +#include #include "s390-ccw.h" #include "virtio.h" #include "scsi.h" @@ -25,20 +26,22 @@ static uint8_t scsi_inquiry_std_response[256]; static ScsiInquiryEvpdPages scsi_inquiry_evpd_pages_response; static ScsiInquiryEvpdBl scsi_inquiry_evpd_bl_response; -static inline void vs_assert(bool term, const char **msgs) +static inline bool vs_assert(bool term, const char **msgs) { if (!term) { int i = 0; - sclp_print("\n! "); + printf("\n! "); while (msgs[i]) { - sclp_print(msgs[i++]); + printf("%s", msgs[i++]); } - panic(" !\n"); + puts(" !"); } + + return term; } -static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, +static bool virtio_scsi_verify_response(VirtioScsiCmdResp *resp, const char *title) { const char *mr[] = { @@ -55,8 +58,8 @@ static void virtio_scsi_verify_response(VirtioScsiCmdResp *resp, 0 }; - vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr); - vs_assert(resp->status == CDB_STATUS_GOOD, ms); + return vs_assert(resp->response == VIRTIO_SCSI_S_OK, mr) && + vs_assert(resp->status == CDB_STATUS_GOOD, ms); } static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, @@ -77,24 +80,31 @@ static void prepare_request(VDev *vdev, const void *cdb, int cdb_size, } } -static inline void vs_io_assert(bool term, const char *msg) +static inline bool vs_io_assert(bool term, const char *msg) { - if (!term) { - virtio_scsi_verify_response(&resp, msg); + if (!term && !virtio_scsi_verify_response(&resp, msg)) { + return false; } + + return true; } -static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, +static int vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, const void *cdb, int cdb_size, void *data, uint32_t data_size) { prepare_request(vdev, cdb, cdb_size, data, data_size); - vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title); + if (!vs_io_assert(virtio_run(vdev, VR_REQUEST, cmd) == 0, title)) { + puts(title); + return -EIO; + } + + return 0; } /* SCSI protocol implementation routines */ -static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, +static int scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, void *data, uint32_t data_size) { ScsiCdbInquiry cdb = { @@ -109,12 +119,13 @@ static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, { data, data_size, VRING_DESC_F_WRITE }, }; - vs_run("inquiry", inquiry, vdev, &cdb, sizeof(cdb), data, data_size); + int ret = vs_run("inquiry", inquiry, + vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } -static bool scsi_test_unit_ready(VDev *vdev) +static int scsi_test_unit_ready(VDev *vdev) { ScsiCdbTestUnitReady cdb = { .command = 0x00, @@ -130,7 +141,7 @@ static bool scsi_test_unit_ready(VDev *vdev) return virtio_scsi_response_ok(&resp); } -static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) +static int scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) { ScsiCdbReportLuns cdb = { .command = 0xa0, @@ -143,14 +154,14 @@ static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) { data, data_size, VRING_DESC_F_WRITE }, }; - vs_run("report luns", report_luns, + int ret = vs_run("report luns", report_luns, vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } -static bool scsi_read_10(VDev *vdev, - ulong sector, int sectors, void *data, +static int scsi_read_10(VDev *vdev, + unsigned long sector, int sectors, void *data, unsigned int data_size) { ScsiCdbRead10 cdb = { @@ -167,12 +178,13 @@ static bool scsi_read_10(VDev *vdev, debug_print_int("read_10 sector", sector); debug_print_int("read_10 sectors", sectors); - vs_run("read(10)", read_10, vdev, &cdb, sizeof(cdb), data, data_size); + int ret = vs_run("read(10)", read_10, + vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } -static bool scsi_read_capacity(VDev *vdev, +static int scsi_read_capacity(VDev *vdev, void *data, uint32_t data_size) { ScsiCdbReadCapacity16 cdb = { @@ -186,10 +198,10 @@ static bool scsi_read_capacity(VDev *vdev, { data, data_size, VRING_DESC_F_WRITE }, }; - vs_run("read capacity", read_capacity_16, + int ret = vs_run("read capacity", read_capacity_16, vdev, &cdb, sizeof(cdb), data, data_size); - return virtio_scsi_response_ok(&resp); + return ret ? ret : virtio_scsi_response_ok(&resp); } /* virtio-scsi routines */ @@ -206,7 +218,7 @@ static int virtio_scsi_locate_device(VDev *vdev) static uint8_t data[16 + 8 * 63]; ScsiLunReport *r = (void *) data; ScsiDevice *sdev = vdev->scsi_device; - int i, luns; + int i, ret, luns; /* QEMU has hardcoded channel #0 in many places. * If this hardcoded value is ever changed, we'll need to add code for @@ -232,15 +244,23 @@ static int virtio_scsi_locate_device(VDev *vdev) sdev->channel = channel; sdev->target = target; sdev->lun = 0; /* LUN has to be 0 for REPORT LUNS */ - if (!scsi_report_luns(vdev, data, sizeof(data))) { + ret = scsi_report_luns(vdev, data, sizeof(data)); + if (ret < 0) { + return ret; + } + + else if (ret == 0) { if (resp.response == VIRTIO_SCSI_S_BAD_TARGET) { continue; } - print_int("target", target); - virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs"); + printf("target 0x%X\n", target); + if (!virtio_scsi_verify_response(&resp, "SCSI cannot report LUNs")) { + return -EIO; + } } + if (r->lun_list_len == 0) { - print_int("no LUNs for target", target); + printf("no LUNs for target 0x%X\n", target); continue; } luns = r->lun_list_len / 8; @@ -264,12 +284,12 @@ static int virtio_scsi_locate_device(VDev *vdev) } } - sclp_print("Warning: Could not locate a usable virtio-scsi device\n"); + puts("Warning: Could not locate a usable virtio-scsi device"); return -ENODEV; } 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; @@ -282,7 +302,9 @@ int virtio_scsi_read_many(VDev *vdev, data_size = sector_count * virtio_get_block_size() * f; if (!scsi_read_10(vdev, sector * f, sector_count * f, load_addr, data_size)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); + if (!virtio_scsi_verify_response(&resp, "virtio-scsi:read_many")) { + return -1; + } } load_addr += data_size; sector += sector_count; @@ -351,11 +373,16 @@ static int virtio_scsi_setup(VDev *vdev) uint8_t code = resp.sense[0] & SCSI_SENSE_CODE_MASK; uint8_t sense_key = resp.sense[2] & SCSI_SENSE_KEY_MASK; - IPL_assert(resp.sense_len != 0, "virtio-scsi:setup: no SENSE data"); + if (resp.sense_len == 0) { + puts("virtio-scsi: setup: no SENSE data"); + return -EINVAL; + } - IPL_assert(retry_test_unit_ready && code == 0x70 && - sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION, - "virtio-scsi:setup: cannot retry"); + if (!retry_test_unit_ready || code != 0x70 || + sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) { + puts("virtio-scsi:setup: cannot retry"); + return -EIO; + } /* retry on CHECK_CONDITION/UNIT_ATTENTION as it * may not designate a real error, but it may be @@ -366,30 +393,40 @@ static int virtio_scsi_setup(VDev *vdev) continue; } - virtio_scsi_verify_response(&resp, "virtio-scsi:setup"); + if (!virtio_scsi_verify_response(&resp, "virtio-scsi:setup")) { + return -1; + } } /* read and cache SCSI INQUIRY response */ - if (!scsi_inquiry(vdev, + ret = scsi_inquiry(vdev, SCSI_INQUIRY_STANDARD, SCSI_INQUIRY_STANDARD_NONE, scsi_inquiry_std_response, - sizeof(scsi_inquiry_std_response))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); + sizeof(scsi_inquiry_std_response)); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:inquiry")) { + return -1; + } } if (virtio_scsi_inquiry_response_is_cdrom(scsi_inquiry_std_response)) { - sclp_print("SCSI CD-ROM detected.\n"); + puts("SCSI CD-ROM detected."); vdev->is_cdrom = true; vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; } - if (!scsi_inquiry(vdev, + ret = scsi_inquiry(vdev, SCSI_INQUIRY_EVPD, SCSI_INQUIRY_EVPD_SUPPORTED_PAGES, evpd, - sizeof(*evpd))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:supported_pages"); + sizeof(*evpd)); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:supported_pages")) { + return -1; + } } debug_print_int("EVPD length", evpd->page_length); @@ -401,12 +438,16 @@ static int virtio_scsi_setup(VDev *vdev) continue; } - if (!scsi_inquiry(vdev, + ret = scsi_inquiry(vdev, SCSI_INQUIRY_EVPD, SCSI_INQUIRY_EVPD_BLOCK_LIMITS, evpd_bl, - sizeof(*evpd_bl))) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:blocklimits"); + sizeof(*evpd_bl)); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:blocklimits")) { + return -1; + } } debug_print_int("max transfer", evpd_bl->max_transfer); @@ -422,8 +463,12 @@ static int virtio_scsi_setup(VDev *vdev) vdev->max_transfer = MIN_NON_ZERO(VIRTIO_SCSI_MAX_SECTORS, vdev->max_transfer); - if (!scsi_read_capacity(vdev, data, data_size)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); + ret = scsi_read_capacity(vdev, data, data_size); + if (ret < 1) { + if (ret != 0 || !virtio_scsi_verify_response(&resp, + "virtio-scsi:setup:read_capacity")) { + return -1; + } } scsi_parse_capacity_report(data, &vdev->scsi_last_block, (uint32_t *) &vdev->scsi_block_size); @@ -438,12 +483,17 @@ int virtio_scsi_setup_device(SubChannelId schid) 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"); + if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) { + puts("Config: sense size mismatch"); + return -EINVAL; + } - sclp_print("Using virtio-scsi.\n"); + if (vdev->config.scsi.cdb_size != VIRTIO_SCSI_CDB_SIZE) { + puts("Config: CDB size mismatch"); + return -EINVAL; + } + + puts("Using virtio-scsi."); return virtio_scsi_setup(vdev); } diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h index e6b6cd4815..c5612e16a2 100644 --- a/pc-bios/s390-ccw/virtio-scsi.h +++ b/pc-bios/s390-ccw/virtio-scsi.h @@ -68,7 +68,7 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r) } 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 f37510f312..8b5a370bb3 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -8,7 +8,7 @@ * directory. */ -#include "libc.h" +#include #include "s390-ccw.h" #include "cio.h" #include "virtio.h" @@ -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 { @@ -217,16 +217,19 @@ int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd) return 0; } -void virtio_setup_ccw(VDev *vdev) +int virtio_setup_ccw(VDev *vdev) { - int i, rc, cfg_size = 0; + int i, cfg_size = 0; uint8_t status; struct VirtioFeatureDesc { uint32_t features; uint8_t index; } __attribute__((packed)) feats; - IPL_assert(virtio_is_supported(vdev->schid), "PE"); + if (!virtio_is_supported(vdev->schid)) { + puts("Virtio unsupported for this device ID"); + return -ENODEV; + } /* device ID has been established now */ vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ @@ -235,8 +238,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"); + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { + puts("Could not write ACKNOWLEDGE status to host"); + return -EIO; + } switch (vdev->senseid.cu_model) { case VIRTIO_ID_NET: @@ -255,27 +260,37 @@ void virtio_setup_ccw(VDev *vdev) cfg_size = sizeof(vdev->config.scsi); break; default: - panic("Unsupported virtio device\n"); + puts("Unsupported virtio device"); + return -ENODEV; } 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"); + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { + puts("Could not write DRIVER status to host"); + return -EIO; + } /* Feature negotiation */ for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) { feats.features = 0; feats.index = i; - rc = run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false); - IPL_assert(rc == 0, "Could not get features bits"); + if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) { + puts("Could not get features bits"); + return -EIO; + } + vdev->guest_features[i] &= bswap32(feats.features); feats.features = bswap32(vdev->guest_features[i]); - rc = run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false); - IPL_assert(rc == 0, "Could not set features bits"); + if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) { + puts("Could not set features bits"); + return -EIO; + } } - rc = run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false); - IPL_assert(rc == 0, "Could not get virtio device configuration"); + if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) { + puts("Could not get virtio device configuration"); + return -EIO; + } for (i = 0; i < vdev->nr_vqs; i++) { VqInfo info = { @@ -289,19 +304,27 @@ void virtio_setup_ccw(VDev *vdev) .num = 0, }; - rc = run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), false); - IPL_assert(rc == 0, "Could not get virtio device VQ configuration"); + if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config), + false)) { + puts("Could not get virtio device VQ config"); + return -EIO; + } info.num = config.num; vring_init(&vdev->vrings[i], &info); vdev->vrings[i].schid = vdev->schid; - IPL_assert( - run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false) == 0, - "Cannot set VQ info"); + if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) { + puts("Cannot set VQ info"); + return -EIO; + } } 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"); + if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) { + puts("Could not write DRIVER_OK status to host"); + return -EIO; + } + + return 0; } bool virtio_is_supported(SubChannelId schid) diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index e657d381ec..9faf3986b1 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -190,14 +190,14 @@ 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(ulong sector, void *load_addr, int sec_num); +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); } @@ -253,7 +253,6 @@ struct VDev { uint8_t scsi_dev_heads; bool scsi_device_selected; ScsiDevice selected_scsi_device; - uint64_t netboot_start_addr; uint32_t max_transfer; uint32_t guest_features[2]; }; @@ -275,7 +274,7 @@ void vring_send_buf(VRing *vr, void *p, int len, int flags); int vr_poll(VRing *vr); int vring_wait_reply(void); int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd); -void virtio_setup_ccw(VDev *vdev); +int virtio_setup_ccw(VDev *vdev); int virtio_net_init(void *mac_addr); diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img deleted file mode 100644 index 682da24a05..0000000000 Binary files a/pc-bios/s390-netboot.img and /dev/null 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 ef9b81d628..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 39b2405148..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 b20d67ccf5..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 ebe53366e4..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 4b5573a857..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 d458ec7436..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 797e1036c9..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 3f8fe9de13..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 d5f263a9f7..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 d26af416ce..ee748f6599 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/plugins/api.c b/plugins/api.c index 2078b16edb..24ea64e2de 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,14 +36,20 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "qemu/plugin.h" #include "qemu/log.h" +#include "qemu/timer.h" #include "tcg/tcg.h" #include "exec/exec-all.h" -#include "exec/ram_addr.h" +#include "exec/gdbstub.h" +#include "exec/translator.h" #include "disas/disas.h" #include "plugin.h" #ifndef CONFIG_USER_ONLY +#include "qapi/error.h" +#include "migration/blocker.h" +#include "exec/ram_addr.h" #include "qemu/plugin-memory.h" #include "hw/boards.h" #else @@ -83,23 +90,48 @@ void qemu_plugin_register_vcpu_exit_cb(qemu_plugin_id_t id, plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_EXIT, cb); } +static bool tb_is_mem_only(void) +{ + return tb_cflags(tcg_ctx->gen_tb) & CF_MEMI_ONLY; +} + 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, void *udata) { - if (!tb->mem_only) { - plugin_register_dyn_cb__udata(&tb->cbs[PLUGIN_CB_REGULAR], - cb, flags, udata); + if (!tb_is_mem_only()) { + plugin_register_dyn_cb__udata(&tb->cbs, 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_cond_cb(struct qemu_plugin_tb *tb, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata) { - if (!tb->mem_only) { - plugin_register_inline_op(&tb->cbs[PLUGIN_CB_INLINE], 0, op, ptr, imm); + if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) { + return; + } + if (cond == QEMU_PLUGIN_COND_ALWAYS) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, cb, flags, udata); + return; + } + plugin_register_dyn_cond_cb__udata(&tb->cbs, cb, flags, + cond, entry, imm, udata); +} + +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_is_mem_only()) { + plugin_register_inline_op_on_entry(&tb->cbs, 0, op, entry, imm); } } @@ -108,19 +140,39 @@ void qemu_plugin_register_vcpu_insn_exec_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_cb_flags flags, void *udata) { - if (!insn->mem_only) { - plugin_register_dyn_cb__udata(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], - cb, flags, udata); + if (!tb_is_mem_only()) { + plugin_register_dyn_cb__udata(&insn->insn_cbs, 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_cond_cb( + struct qemu_plugin_insn *insn, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata) { - if (!insn->mem_only) { - plugin_register_inline_op(&insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], - 0, op, ptr, imm); + if (cond == QEMU_PLUGIN_COND_NEVER || tb_is_mem_only()) { + return; + } + if (cond == QEMU_PLUGIN_COND_ALWAYS) { + qemu_plugin_register_vcpu_insn_exec_cb(insn, cb, flags, udata); + return; + } + plugin_register_dyn_cond_cb__udata(&insn->insn_cbs, cb, flags, + cond, entry, imm, udata); +} + +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 (!tb_is_mem_only()) { + plugin_register_inline_op_on_entry(&insn->insn_cbs, 0, op, entry, imm); } } @@ -135,17 +187,17 @@ void qemu_plugin_register_vcpu_mem_cb(struct qemu_plugin_insn *insn, enum qemu_plugin_mem_rw rw, void *udata) { - plugin_register_vcpu_mem_cb(&insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], - cb, flags, rw, udata); + plugin_register_vcpu_mem_cb(&insn->mem_cbs, 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->mem_cbs, rw, op, entry, imm); } void qemu_plugin_register_vcpu_tb_trans_cb(qemu_plugin_id_t id, @@ -190,7 +242,8 @@ size_t qemu_plugin_tb_n_insns(const struct qemu_plugin_tb *tb) uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) { - return tb->vaddr; + const DisasContextBase *db = tcg_ctx->plugin_db; + return db->pc_first; } struct qemu_plugin_insn * @@ -201,7 +254,6 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) return NULL; } insn = g_ptr_array_index(tb->insns, idx); - insn->mem_only = tb->mem_only; return insn; } @@ -212,14 +264,18 @@ qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) * instruction being translated. */ -const void *qemu_plugin_insn_data(const struct qemu_plugin_insn *insn) +size_t qemu_plugin_insn_data(const struct qemu_plugin_insn *insn, + void *dest, size_t len) { - return insn->data->data; + const DisasContextBase *db = tcg_ctx->plugin_db; + + len = MIN(len, insn->len); + return translator_st(db, dest, insn->vaddr, len) ? len : 0; } size_t qemu_plugin_insn_size(const struct qemu_plugin_insn *insn) { - return insn->data->len; + return insn->len; } uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) @@ -229,13 +285,36 @@ uint64_t qemu_plugin_insn_vaddr(const struct qemu_plugin_insn *insn) void *qemu_plugin_insn_haddr(const struct qemu_plugin_insn *insn) { - return insn->haddr; + const DisasContextBase *db = tcg_ctx->plugin_db; + vaddr page0_last = db->pc_first | ~TARGET_PAGE_MASK; + + if (db->fake_insn) { + return NULL; + } + + /* + * ??? The return value is not intended for use of host memory, + * but as a proxy for address space and physical address. + * Thus we are only interested in the first byte and do not + * care about spanning pages. + */ + if (insn->vaddr <= page0_last) { + if (db->host_addr[0] == NULL) { + return NULL; + } + return db->host_addr[0] + insn->vaddr - db->pc_first; + } else { + if (db->host_addr[1] == NULL) { + return NULL; + } + return db->host_addr[1] + insn->vaddr - (page0_last + 1); + } } char *qemu_plugin_insn_disas(const struct qemu_plugin_insn *insn) { - CPUState *cpu = current_cpu; - return plugin_disas(cpu, insn->vaddr, insn->data->len); + return plugin_disas(tcg_ctx->cpu, tcg_ctx->plugin_db, + insn->vaddr, insn->len); } const char *qemu_plugin_insn_symbol(const struct qemu_plugin_insn *insn) @@ -272,6 +351,39 @@ bool qemu_plugin_mem_is_store(qemu_plugin_meminfo_t info) return get_plugin_meminfo_rw(info) & QEMU_PLUGIN_MEM_W; } +qemu_plugin_mem_value qemu_plugin_mem_get_value(qemu_plugin_meminfo_t info) +{ + uint64_t low = current_cpu->neg.plugin_mem_value_low; + qemu_plugin_mem_value value; + + switch (qemu_plugin_mem_size_shift(info)) { + case 0: + value.type = QEMU_PLUGIN_MEM_VALUE_U8; + value.data.u8 = (uint8_t)low; + break; + case 1: + value.type = QEMU_PLUGIN_MEM_VALUE_U16; + value.data.u16 = (uint16_t)low; + break; + case 2: + value.type = QEMU_PLUGIN_MEM_VALUE_U32; + value.data.u32 = (uint32_t)low; + break; + case 3: + value.type = QEMU_PLUGIN_MEM_VALUE_U64; + value.data.u64 = low; + break; + case 4: + value.type = QEMU_PLUGIN_MEM_VALUE_U128; + value.data.u128.low = low; + value.data.u128.high = current_cpu->neg.plugin_mem_value_high; + break; + default: + g_assert_not_reached(); + } + return value; +} + /* * Virtual Memory queries */ @@ -316,22 +428,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; @@ -341,13 +438,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"); @@ -357,34 +454,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(); } /* @@ -407,7 +479,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; @@ -417,7 +489,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; @@ -427,7 +499,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; @@ -437,8 +509,175 @@ 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 + 1); + 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); +} + +bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len) +{ + g_assert(current_cpu); + + if (len == 0) { + return false; + } + + g_byte_array_set_size(data, len); + + int result = cpu_memory_rw_debug(current_cpu, addr, data->data, + data->len, false); + + if (result < 0) { + return false; + } + + return true; +} + +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) - 1); +} + +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; +} + +/* + * Time control + */ +static bool has_control; +#ifdef CONFIG_SOFTMMU +static Error *migration_blocker; +#endif + +const void *qemu_plugin_request_time_control(void) +{ + if (!has_control) { + has_control = true; +#ifdef CONFIG_SOFTMMU + error_setg(&migration_blocker, + "TCG plugin time control does not support migration"); + migrate_add_blocker(&migration_blocker, NULL); +#endif + return &has_control; + } + return NULL; +} + +#ifdef CONFIG_SOFTMMU +static void advance_virtual_time__async(CPUState *cpu, run_on_cpu_data data) +{ + int64_t new_time = data.host_ulong; + qemu_clock_advance_virtual_time(new_time); +} +#endif + +void qemu_plugin_update_ns(const void *handle, int64_t new_time) +{ +#ifdef CONFIG_SOFTMMU + if (handle == &has_control) { + /* Need to execute out of cpu_exec, so bql can be locked. */ + async_run_on_cpu(current_cpu, + advance_virtual_time__async, + RUN_ON_CPU_HOST_ULONG(new_time)); + } +#endif +} diff --git a/plugins/core.c b/plugins/core.c index ccb770a485..bb105e8e68 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,7 +55,8 @@ 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); + bitmap_copy(cpu->plugin_state->event_mask, + &data.host_ulong, QEMU_PLUGIN_EV_MAX); tcg_flush_jmp_cache(cpu); } @@ -64,11 +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) { - async_run_on_cpu(cpu, plugin_cpu_update__async, mask); - } else { - plugin_cpu_update__async(cpu, mask); - } + async_run_on_cpu(cpu, plugin_cpu_update__async, mask); } void plugin_unregister_cb__locked(struct qemu_plugin_ctx *ctx, @@ -210,26 +207,89 @@ plugin_register_cb_udata(qemu_plugin_id_t id, enum qemu_plugin_event ev, do_plugin_register_cb(id, ev, func, udata); } -void qemu_plugin_vcpu_init_hook(CPUState *cpu) +CPUPluginState *qemu_plugin_create_vcpu_state(void) +{ + return g_new0(CPUPluginState, 1); +} + +static void plugin_grow_scoreboards__locked(CPUState *cpu) +{ + size_t scoreboard_size = plugin.scoreboard_alloc_size; + bool need_realloc = false; + + if (cpu->cpu_index < scoreboard_size) { + return; + } + + while (cpu->cpu_index >= scoreboard_size) { + scoreboard_size *= 2; + need_realloc = true; + } + + if (!need_realloc) { + return; + } + + if (QLIST_EMPTY(&plugin.scoreboards)) { + /* just update size for future scoreboards */ + plugin.scoreboard_alloc_size = scoreboard_size; + return; + } + + /* + * A scoreboard creation/deletion might be in progress. If a new vcpu is + * initialized at the same time, we are safe, as the new + * plugin.scoreboard_alloc_size was not yet written. + */ + qemu_rec_mutex_unlock(&plugin.lock); + + /* cpus must be stopped, as tb might still use an existing scoreboard. */ + start_exclusive(); + /* re-acquire lock */ + qemu_rec_mutex_lock(&plugin.lock); + /* in case another vcpu is created between unlock and exclusive section. */ + if (scoreboard_size > plugin.scoreboard_alloc_size) { + struct qemu_plugin_scoreboard *score; + QLIST_FOREACH(score, &plugin.scoreboards, entry) { + g_array_set_size(score->data, scoreboard_size); + } + plugin.scoreboard_alloc_size = scoreboard_size; + /* force all tb to be flushed, as scoreboard pointers were changed. */ + tb_flush(cpu); + } + end_exclusive(); +} + +static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused) { bool success; + assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); 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); } +void qemu_plugin_vcpu_init_hook(CPUState *cpu) +{ + /* Plugin initialization must wait until the cpu start executing code */ + async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL); +} + void qemu_plugin_vcpu_exit_hook(CPUState *cpu) { bool success; plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT); + assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); qemu_rec_mutex_lock(&plugin.lock); success = g_hash_table_remove(plugin.cpu_ht, &cpu->cpu_index); g_assert(success); @@ -270,7 +330,7 @@ static struct qemu_plugin_dyn_cb *plugin_get_dyn_cb(GArray **arr) GArray *cbs = *arr; if (!cbs) { - cbs = g_array_sized_new(false, false, + cbs = g_array_sized_new(false, true, sizeof(struct qemu_plugin_dyn_cb), 1); *arr = cbs; } @@ -279,19 +339,32 @@ 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) +static enum plugin_dyn_cb_type op_to_cb_type(enum qemu_plugin_op op) +{ + switch (op) { + case QEMU_PLUGIN_INLINE_ADD_U64: + return PLUGIN_CB_INLINE_ADD_U64; + case QEMU_PLUGIN_INLINE_STORE_U64: + return PLUGIN_CB_INLINE_STORE_U64; + default: + g_assert_not_reached(); + } +} + +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; + struct qemu_plugin_inline_cb inline_cb = { .rw = rw, + .entry = entry, + .imm = imm }; dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = ptr; - dyn_cb->type = PLUGIN_CB_INLINE; - dyn_cb->rw = rw; - dyn_cb->inline_insn.op = op; - dyn_cb->inline_insn.imm = imm; + dyn_cb->type = op_to_cb_type(op); + dyn_cb->inline_insn = inline_cb; } void plugin_register_dyn_cb__udata(GArray **arr, @@ -299,12 +372,57 @@ void plugin_register_dyn_cb__udata(GArray **arr, enum qemu_plugin_cb_flags flags, void *udata) { - struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); + static TCGHelperInfo info[3] = { + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + /* + * Match qemu_plugin_vcpu_udata_cb_t: + * void (*)(uint32_t, void *) + */ + [0 ... 2].typemask = (dh_typemask(void, 0) | + dh_typemask(i32, 1) | + dh_typemask(ptr, 2)) + }; + assert((unsigned)flags < ARRAY_SIZE(info)); - dyn_cb->userp = udata; - /* Note flags are discarded as unused. */ - dyn_cb->f.vcpu_udata = cb; + struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); + struct qemu_plugin_regular_cb regular_cb = { .f.vcpu_udata = cb, + .userp = udata, + .info = &info[flags] }; dyn_cb->type = PLUGIN_CB_REGULAR; + dyn_cb->regular = regular_cb; +} + +void plugin_register_dyn_cond_cb__udata(GArray **arr, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata) +{ + static TCGHelperInfo info[3] = { + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + /* + * Match qemu_plugin_vcpu_udata_cb_t: + * void (*)(uint32_t, void *) + */ + [0 ... 2].typemask = (dh_typemask(void, 0) | + dh_typemask(i32, 1) | + dh_typemask(ptr, 2)) + }; + assert((unsigned)flags < ARRAY_SIZE(info)); + + struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); + struct qemu_plugin_conditional_cb cond_cb = { .userp = udata, + .f.vcpu_udata = cb, + .cond = cond, + .entry = entry, + .imm = imm, + .info = &info[flags] }; + dyn_cb->type = PLUGIN_CB_COND; + dyn_cb->cond = cond_cb; } void plugin_register_vcpu_mem_cb(GArray **arr, @@ -313,14 +431,38 @@ void plugin_register_vcpu_mem_cb(GArray **arr, enum qemu_plugin_mem_rw rw, void *udata) { - struct qemu_plugin_dyn_cb *dyn_cb; + /* + * Expect that the underlying type for enum qemu_plugin_meminfo_t + * is either int32_t or uint32_t, aka int or unsigned int. + */ + QEMU_BUILD_BUG_ON( + !__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) && + !__builtin_types_compatible_p(qemu_plugin_meminfo_t, int32_t)); - dyn_cb = plugin_get_dyn_cb(arr); - dyn_cb->userp = udata; - /* Note flags are discarded as unused. */ - dyn_cb->type = PLUGIN_CB_REGULAR; - dyn_cb->rw = rw; - dyn_cb->f.generic = cb; + static TCGHelperInfo info[3] = { + [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, + [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + /* + * Match qemu_plugin_vcpu_mem_cb_t: + * void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *) + */ + [0 ... 2].typemask = + (dh_typemask(void, 0) | + dh_typemask(i32, 1) | + (__builtin_types_compatible_p(qemu_plugin_meminfo_t, uint32_t) + ? dh_typemask(i32, 2) : dh_typemask(s32, 2)) | + dh_typemask(i64, 3) | + dh_typemask(ptr, 4)) + }; + assert((unsigned)flags < ARRAY_SIZE(info)); + + struct qemu_plugin_dyn_cb *dyn_cb = plugin_get_dyn_cb(arr); + struct qemu_plugin_regular_cb regular_cb = { .userp = udata, + .rw = rw, + .f.vcpu_mem = cb, + .info = &info[flags] }; + dyn_cb->type = PLUGIN_CB_MEM_REGULAR; + dyn_cb->regular = regular_cb; } /* @@ -334,7 +476,7 @@ void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb) struct qemu_plugin_cb *cb, *next; enum qemu_plugin_event ev = QEMU_PLUGIN_EV_VCPU_TB_TRANS; - /* no plugin_mask check here; caller should have checked */ + /* no plugin_state->event_mask check here; caller should have checked */ QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans; @@ -357,7 +499,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 +521,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 +534,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,13 +579,22 @@ 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(enum plugin_dyn_cb_type type, + struct qemu_plugin_inline_cb *cb, + int cpu_index) { - uint64_t *val = cb->userp; + char *ptr = cb->entry.score->data->data; + size_t elem_size = g_array_get_element_size( + cb->entry.score->data); + size_t offset = cb->entry.offset; + uint64_t *val = (uint64_t *)(ptr + offset + cpu_index * elem_size); - switch (cb->inline_insn.op) { - case QEMU_PLUGIN_INLINE_ADD_U64: - *val += cb->inline_insn.imm; + switch (type) { + case PLUGIN_CB_INLINE_ADD_U64: + *val += cb->imm; + break; + case PLUGIN_CB_INLINE_STORE_U64: + *val = cb->imm; break; default: g_assert_not_reached(); @@ -446,28 +602,37 @@ void exec_inline_op(struct qemu_plugin_dyn_cb *cb) } void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, + uint64_t value_low, + uint64_t value_high, MemOpIdx oi, enum qemu_plugin_mem_rw rw) { - GArray *arr = cpu->plugin_mem_cbs; + GArray *arr = cpu->neg.plugin_mem_cbs; size_t i; if (arr == NULL) { return; } + + cpu->neg.plugin_mem_value_low = value_low; + cpu->neg.plugin_mem_value_high = value_high; + for (i = 0; i < arr->len; i++) { struct qemu_plugin_dyn_cb *cb = &g_array_index(arr, struct qemu_plugin_dyn_cb, i); - if (!(rw & cb->rw)) { - break; - } switch (cb->type) { - case PLUGIN_CB_REGULAR: - cb->f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), - vaddr, cb->userp); + case PLUGIN_CB_MEM_REGULAR: + if (rw & cb->regular.rw) { + cb->regular.f.vcpu_mem(cpu->cpu_index, + make_plugin_meminfo(oi, rw), + vaddr, cb->regular.userp); + } break; - case PLUGIN_CB_INLINE: - exec_inline_op(cb); + case PLUGIN_CB_INLINE_ADD_U64: + case PLUGIN_CB_INLINE_STORE_U64: + if (rw & cb->inline_insn.rw) { + exec_inline_op(cb->type, &cb->inline_insn, cpu->cpu_index); + } break; default: g_assert_not_reached(); @@ -500,26 +665,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 */ @@ -545,17 +717,6 @@ void qemu_plugin_user_postfork(bool is_child) } } - -/* - * 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. - */ -void qemu_plugin_disable_mem_helpers(CPUState *cpu) -{ - cpu->plugin_mem_cbs = NULL; -} - static bool plugin_dyn_cb_arr_cmp(const void *ap, const void *bp) { return ap == bp; @@ -571,8 +732,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..ebc01da9c6 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/config-file.h" +#include "qemu/help_option.h" #include "qapi/error.h" #include "qemu/lockable.h" #include "qemu/option.h" @@ -29,11 +30,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" @@ -99,7 +99,12 @@ static int plugin_add(void *opaque, const char *name, const char *value, bool is_on; char *fullarg; - if (strcmp(name, "file") == 0) { + if (is_help_option(value)) { + printf("Plugin options\n"); + printf(" file=\n"); + printf(" plugin specific arguments\n"); + exit(0); + } else if (strcmp(name, "file") == 0) { if (strcmp(value, "") == 0) { error_setg(errp, "requires a non-empty argument"); return 1; @@ -140,12 +145,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 +396,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 752377c66d..98542e926f 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,20 +1,48 @@ -plugin_ldflags = [] +if not get_option('plugins') + subdir_done() +endif + +qemu_plugin_symbols = configure_file( + input: files('../include/qemu/qemu-plugin.h'), + output: 'qemu-plugin.symbols', + capture: true, + command: [files('../scripts/qemu-plugin-symbols.py'), '@INPUT@']) + # Modules need more symbols than just those in plugins/qemu-plugins.symbols if not enable_modules - if targetos == 'darwin' + if host_os == 'darwin' configure_file( - input: files('qemu-plugins.symbols'), + input: qemu_plugin_symbols, output: 'qemu-plugins-ld64.symbols', capture: true, command: ['sed', '-ne', 's/^[[:space:]]*\\(qemu_.*\\);/_\\1/p', '@INPUT@']) - plugin_ldflags = ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols'] + emulator_link_args += ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols'] else - plugin_ldflags = ['-Xlinker', '--dynamic-list=' + (meson.project_source_root() / 'plugins/qemu-plugins.symbols')] + emulator_link_args += ['-Xlinker', '--dynamic-list=' + qemu_plugin_symbols.full_path()] endif endif -specific_ss.add(when: 'CONFIG_PLUGIN', if_true: [files( +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: qemu_plugin_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)]) +)) diff --git a/plugins/plugin.h b/plugins/plugin.h index 5eb2fdbc85..30e2299a54 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, @@ -88,6 +93,14 @@ plugin_register_dyn_cb__udata(GArray **arr, qemu_plugin_vcpu_udata_cb_t cb, enum qemu_plugin_cb_flags flags, void *udata); +void +plugin_register_dyn_cond_cb__udata(GArray **arr, + qemu_plugin_vcpu_udata_cb_t cb, + enum qemu_plugin_cb_flags flags, + enum qemu_plugin_cond cond, + qemu_plugin_u64 entry, + uint64_t imm, + void *udata); void plugin_register_vcpu_mem_cb(GArray **arr, void *cb, @@ -95,6 +108,14 @@ 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(enum plugin_dyn_cb_type type, + struct qemu_plugin_inline_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 deleted file mode 100644 index 71f6c90549..0000000000 --- a/plugins/qemu-plugins.symbols +++ /dev/null @@ -1,45 +0,0 @@ -{ - qemu_plugin_bool_parse; - qemu_plugin_end_code; - qemu_plugin_entry_code; - qemu_plugin_get_hwaddr; - qemu_plugin_hwaddr_device_name; - qemu_plugin_hwaddr_is_io; - qemu_plugin_hwaddr_phys_addr; - qemu_plugin_insn_data; - qemu_plugin_insn_disas; - qemu_plugin_insn_haddr; - qemu_plugin_insn_size; - qemu_plugin_insn_symbol; - qemu_plugin_insn_vaddr; - qemu_plugin_mem_is_big_endian; - 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_outs; - qemu_plugin_path_to_binary; - 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_mem_cb; - qemu_plugin_register_vcpu_mem_inline; - 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_trans_cb; - qemu_plugin_reset; - qemu_plugin_start_code; - qemu_plugin_tb_get_insn; - qemu_plugin_tb_n_insns; - qemu_plugin_tb_vaddr; - qemu_plugin_uninstall; - qemu_plugin_vcpu_for_each; -}; diff --git a/po/it.po b/po/it.po index c6d9517207..363b9bddf2 100644 --- a/po/it.po +++ b/po/it.po @@ -65,7 +65,7 @@ msgid "Detach Tab" msgstr "_Sposta in una nuova finestra" msgid "Show Menubar" -msgstr "" +msgstr "Mostra _barra dei menu" msgid "_Machine" msgstr "_Macchina virtuale" 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 b170708398..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 "" @@ -33,8 +34,8 @@ help: @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..ebb58d5b68 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']) @@ -332,19 +328,34 @@ class QEMUMachine: """Returns the list of arguments given to the QEMU binary.""" return self._args - def _pre_launch(self) -> None: - if self._console_set: - self._remove_files.append(self._console_address) + @property + def binary(self) -> str: + """Returns path to the QEMU binary""" + return self._binary + def _pre_launch(self) -> None: if self._qmp_set: + sock = None + 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 +371,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 +392,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 +403,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 +521,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 +543,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 +560,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 +581,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 +610,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 +636,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 +708,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 +906,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 +944,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 1951754455..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, @@ -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/qemu_ga_client.py b/python/qemu/utils/qemu_ga_client.py index 8c38a7ac9c..cf0fcf9a8b 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,18 +167,18 @@ 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)() # On error exception will raise except asyncio.TimeoutError: # On success command will timed out - return + pass 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..8ac5b0b2a0 --- /dev/null +++ b/python/scripts/mkvenv.py @@ -0,0 +1,886 @@ +""" +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 + 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 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: + # pylint 3.3 bug: + # pylint: disable=raising-non-exception, raise-missing-from + + # 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 _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 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_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 == "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..0405e910b4 --- /dev/null +++ b/python/scripts/vendor.py @@ -0,0 +1,74 @@ +#!/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.5.0": + "52b34f4903b882df52ad0d533146d4b992c018ea77399f825579737672ae7b20", + } + + 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..cf5af7e664 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -14,15 +14,16 @@ 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 + Programming Language :: Python :: 3.13 Typing :: Typed [options] -python_requires = >= 3.6 +python_requires = >= 3.8 packages = qemu.qmp qemu.machine @@ -32,16 +33,16 @@ 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 + pylint != 3.2.4; python_version<"3.9" tox >= 3.18.0 urwid >= 2.1.2 urwid-readline >= 0.13 @@ -71,12 +72,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 +96,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 +111,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 @@ -116,6 +142,7 @@ ignore_missing_imports = True disable=consider-using-f-string, consider-using-with, too-many-arguments, + too-many-positional-arguments, too-many-function-args, # mypy handles this with less false positives. too-many-instance-attributes, no-member, # mypy also handles this better. @@ -132,6 +159,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 +186,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, py313 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.5.0-py3-none-any.whl b/python/wheels/meson-1.5.0-py3-none-any.whl new file mode 100644 index 0000000000..c7edeb37ad Binary files /dev/null and b/python/wheels/meson-1.5.0-py3-none-any.whl differ diff --git a/python/wheels/pycotap-1.3.1-py3-none-any.whl b/python/wheels/pycotap-1.3.1-py3-none-any.whl new file mode 100644 index 0000000000..9c2c7d2593 Binary files /dev/null and b/python/wheels/pycotap-1.3.1-py3-none-any.whl differ diff --git a/pythondeps.toml b/pythondeps.toml new file mode 100644 index 0000000000..c03c9df81b --- /dev/null +++ b/pythondeps.toml @@ -0,0 +1,35 @@ +# 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 = ">=1.5.0", installed = "1.5.0", canary = "meson" } +pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } + +[docs] +# Please keep the installed versions in sync with docs/requirements.txt +sphinx = { accepted = ">=3.4.3", 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 = "(>=103.0, <104.0)", installed = "103.0", canary = "avocado" } +pycdlib = { accepted = ">=1.11.0" } diff --git a/qapi/acpi.json b/qapi/acpi.json index d148f6db9f..045dab6228 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 # @@ -109,15 +111,14 @@ # # Since: 2.1 # -# 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} -# ]} +# .. qmp-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} +# ]} ## { 'command': 'query-acpi-ospm-status', 'returns': ['ACPIOSTInfo'] } @@ -130,13 +131,12 @@ # # Since: 2.1 # -# Example: -# -# <- { "event": "ACPI_DEVICE_OST", -# "data": { "info": { "device": "d1", "slot": "0", -# "slot-type": "DIMM", "source": 1, "status": 0 } }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# .. qmp-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': 'ACPIOSTInfo' } } diff --git a/qapi/audio.json b/qapi/audio.json index 1e0a24bdfc..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 ## @@ -131,8 +131,8 @@ ## # @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 # @@ -168,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 ## @@ -185,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 ## @@ -239,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 ## @@ -260,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 ## @@ -283,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 ## @@ -324,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: # @@ -408,8 +450,19 @@ # Since: 4.0 ## { 'enum': 'AudiodevDriver', - 'data': [ 'none', 'alsa', 'coreaudio', 'dbus', 'dsound', 'jack', 'oss', 'pa', - 'sdl', 'sndio', 'spice', 'wav' ] } + 'data': [ 'none', + { 'name': 'alsa', 'if': 'CONFIG_AUDIO_ALSA' }, + { 'name': 'coreaudio', 'if': 'CONFIG_AUDIO_COREAUDIO' }, + { 'name': 'dbus', 'if': 'CONFIG_DBUS_DISPLAY' }, + { 'name': 'dsound', 'if': 'CONFIG_AUDIO_DSOUND' }, + { 'name': 'jack', 'if': 'CONFIG_AUDIO_JACK' }, + { 'name': 'oss', 'if': 'CONFIG_AUDIO_OSS' }, + { 'name': 'pa', 'if': 'CONFIG_AUDIO_PA' }, + { 'name': 'pipewire', 'if': 'CONFIG_AUDIO_PIPEWIRE' }, + { 'name': 'sdl', 'if': 'CONFIG_AUDIO_SDL' }, + { 'name': 'sndio', 'if': 'CONFIG_AUDIO_SNDIO' }, + { 'name': 'spice', 'if': 'CONFIG_SPICE' }, + 'wav' ] } ## # @Audiodev: @@ -420,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 ## @@ -432,14 +486,38 @@ 'discriminator': 'driver', 'data': { 'none': 'AudiodevGenericOptions', - 'alsa': 'AudiodevAlsaOptions', - 'coreaudio': 'AudiodevCoreaudioOptions', - 'dbus': 'AudiodevGenericOptions', - 'dsound': 'AudiodevDsoundOptions', - 'jack': 'AudiodevJackOptions', - 'oss': 'AudiodevOssOptions', - 'pa': 'AudiodevPaOptions', - 'sdl': 'AudiodevSdlOptions', - 'sndio': 'AudiodevSndioOptions', - 'spice': 'AudiodevGenericOptions', + 'alsa': { 'type': 'AudiodevAlsaOptions', + 'if': 'CONFIG_AUDIO_ALSA' }, + 'coreaudio': { 'type': 'AudiodevCoreaudioOptions', + 'if': 'CONFIG_AUDIO_COREAUDIO' }, + 'dbus': { 'type': 'AudiodevGenericOptions', + 'if': 'CONFIG_DBUS_DISPLAY' }, + 'dsound': { 'type': 'AudiodevDsoundOptions', + 'if': 'CONFIG_AUDIO_DSOUND' }, + 'jack': { 'type': 'AudiodevJackOptions', + 'if': 'CONFIG_AUDIO_JACK' }, + 'oss': { 'type': 'AudiodevOssOptions', + 'if': 'CONFIG_AUDIO_OSS' }, + 'pa': { 'type': 'AudiodevPaOptions', + 'if': 'CONFIG_AUDIO_PA' }, + '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 95ac4fa634..fd3bcc1c17 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,95 +759,94 @@ # # 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: +# .. qmp-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'], 'allow-preconfig': true } @@ -745,41 +856,53 @@ # # 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 ## @@ -787,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: @@ -800,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' } } ## @@ -905,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. # @@ -928,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 ## @@ -947,6 +1104,8 @@ # # Block driver specific statistics # +# @driver: block driver name +# # Since: 4.2 ## { 'union': 'BlockStatsSpecific', @@ -964,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 ## @@ -998,119 +1157,118 @@ # 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. # # Since: 0.14 # -# 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 -# } -# } -# ] -# } +# .. qmp-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 +# } +# } +# ] +# } ## { 'command': 'query-blockstats', 'data': { '*query-nodes': 'bool' }, @@ -1121,22 +1279,22 @@ # @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) # @@ -1157,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 ## @@ -1170,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 ## @@ -1196,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: # @@ -1212,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 # @@ -1236,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: @@ -1280,17 +1456,16 @@ # # @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": {} } +# .. qmp-example:: # +# -> { "execute": "block_resize", +# "arguments": { "device": "scratch", "size": 1073741824 } } +# <- { "return": {} } ## { 'command': 'block_resize', 'data': { '*device': 'str', @@ -1302,14 +1477,14 @@ ## # @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 ## @@ -1323,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', @@ -1347,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 ## @@ -1359,90 +1536,97 @@ ## # @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. +# +# @min-cluster-size: Minimum size of blocks used by copy-before-write +# and background copy operations. Has to be a power of 2. No +# effect if smaller than the maximum of the target's cluster size +# and 64 KiB. Default 0. (Since 9.2) # # Since: 6.0 ## { 'struct': 'BackupPerf', - 'data': { '*use-copy-range': 'bool', - '*max-workers': 'int', '*max-chunk': 'int64' } } + 'data': { '*use-copy-range': 'bool', '*max-workers': 'int', + '*max-chunk': 'int64', '*min-cluster-size': 'size' } } ## # @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) +# @discard-source: Discard blocks on source which have already been +# copied to the target. (Since 9.1) +# +# @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. +# .. 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. # # Since: 4.2 ## @@ -1455,21 +1639,22 @@ '*on-target-error': 'BlockdevOnError', '*auto-finalize': 'bool', '*auto-dismiss': 'bool', '*filter-node-name': 'str', + '*discard-source': 'bool', '*x-perf': { 'type': 'BackupPerf', 'features': [ 'unstable' ] } } } ## # @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 ## @@ -1495,22 +1680,19 @@ # # Takes a synchronous snapshot of a block device. # -# 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": {} } +# .. qmp-example:: # +# -> { "execute": "blockdev-snapshot-sync", +# "arguments": { "device": "ide-hd0", +# "snapshot-file": +# "/some/place/my-image", +# "format": "qcow2" } } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot-sync', 'data': 'BlockdevSnapshotSync', @@ -1522,36 +1704,33 @@ # Takes a snapshot of a block device. # # 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. -# -# For the arguments, see the documentation of BlockdevSnapshot. +# 'overlay'. Additionally, if 'node' is associated with a block +# device, the block device changes to using 'overlay' as its new +# active image. # # 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: +# .. qmp-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', @@ -1563,26 +1742,24 @@ # # 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 ## @@ -1594,14 +1771,14 @@ ## # @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 @@ -1609,94 +1786,95 @@ # 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 # # Since: 1.3 # -# Example: -# -# -> { "execute": "block-commit", -# "arguments": { "device": "virtio0", -# "top": "/tmp/snap1.qcow2" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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' }, @@ -1705,28 +1883,29 @@ ## # @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": {} } +# .. qmp-example:: # +# -> { "execute": "drive-backup", +# "arguments": { "device": "drive0", +# "sync": "full", +# "target": "backup.img" } } +# <- { "return": {} } ## { 'command': 'drive-backup', 'boxed': true, 'data': 'DriveBackup', 'features': ['deprecated'], @@ -1735,25 +1914,24 @@ ## # @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": {} } +# .. qmp-example:: # +# -> { "execute": "blockdev-backup", +# "arguments": { "device": "src-id", +# "sync": "full", +# "target": "tgt-id" } } +# <- { "return": {} } ## { 'command': 'blockdev-backup', 'boxed': true, 'data': 'BlockdevBackup', @@ -1764,62 +1942,61 @@ # # 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 # # Since: 2.0 # -# Example: +# .. qmp-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' ], @@ -1843,16 +2020,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 ## @@ -1864,25 +2041,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 ## @@ -1902,8 +2080,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 ## @@ -1928,6 +2106,7 @@ # Get the block graph. # # Features: +# # @unstable: This command is meant for debugging. # # Since: 4.0 @@ -1939,27 +2118,26 @@ ## # @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": {} } +# .. qmp-example:: # +# -> { "execute": "drive-mirror", +# "arguments": { "device": "ide-hd0", +# "target": "/some/place/my-image", +# "sync": "full", +# "format": "qcow2" } } +# <- { "return": {} } ## { 'command': 'drive-mirror', 'boxed': true, 'data': 'DriveMirror', @@ -1970,73 +2148,72 @@ # # 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 ## @@ -2070,16 +2247,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 ## @@ -2090,7 +2267,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 # @@ -2107,9 +2285,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 ## @@ -2120,20 +2298,20 @@ ## # @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 # # Since: 2.4 # -# Example: -# -# -> { "execute": "block-dirty-bitmap-add", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "block-dirty-bitmap-add", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-add', 'data': 'BlockDirtyBitmapAdd', @@ -2143,22 +2321,21 @@ # @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 +# - if @name is frozen by an operation, GenericError # # Since: 2.4 # -# Example: -# -# -> { "execute": "block-dirty-bitmap-remove", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "block-dirty-bitmap-remove", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-remove', 'data': 'BlockDirtyBitmap', @@ -2171,18 +2348,17 @@ # 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 # # Since: 2.4 # -# Example: -# -# -> { "execute": "block-dirty-bitmap-clear", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "block-dirty-bitmap-clear", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-clear', 'data': 'BlockDirtyBitmap', @@ -2193,18 +2369,17 @@ # # 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 # # Since: 4.0 # -# Example: -# -# -> { "execute": "block-dirty-bitmap-enable", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "block-dirty-bitmap-enable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-enable', 'data': 'BlockDirtyBitmap', @@ -2215,18 +2390,17 @@ # # 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 # # Since: 4.0 # -# Example: -# -# -> { "execute": "block-dirty-bitmap-disable", -# "arguments": { "node": "drive0", "name": "bitmap0" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "block-dirty-bitmap-disable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-disable', 'data': 'BlockDirtyBitmap', @@ -2236,30 +2410,30 @@ # @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": {} } +# .. qmp-example:: # +# -> { "execute": "block-dirty-bitmap-merge", +# "arguments": { "node": "drive0", "target": "bitmap0", +# "bitmaps": ["bitmap1"] } } +# <- { "return": {} } ## { 'command': 'block-dirty-bitmap-merge', 'data': 'BlockDirtyBitmapMerge', @@ -2283,12 +2457,15 @@ # 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 # # Since: 2.10 ## @@ -2302,75 +2479,70 @@ # # 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": {} } +# .. qmp-example:: # +# -> { "execute": "blockdev-mirror", +# "arguments": { "device": "ide-hd0", +# "target": "target0", +# "sync": "full" } } +# <- { "return": {} } ## { 'command': 'blockdev-mirror', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', @@ -2405,59 +2577,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 @@ -2477,35 +2643,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 @@ -2530,11 +2716,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 ## @@ -2584,104 +2770,109 @@ # # 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": {} } +# .. qmp-example:: # +# -> { "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' }, @@ -2696,15 +2887,16 @@ # # 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 ## @@ -2717,35 +2909,38 @@ # # 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 ## @@ -2757,20 +2952,21 @@ # # 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 ## @@ -2782,18 +2978,19 @@ # # 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 ## @@ -2803,26 +3000,28 @@ ## # @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 ## @@ -2832,19 +3031,18 @@ ## # @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' }, @@ -2854,27 +3052,63 @@ # @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' }, '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: # # Determines how to handle discard requests. # # @ignore: Ignore the request +# # @unmap: Forward as an unmap request # # Since: 2.9 @@ -2886,12 +3120,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 ## @@ -2904,7 +3142,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 @@ -2919,9 +3159,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 ## @@ -2935,20 +3175,33 @@ # 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 # +# Features: +# +# @deprecated: Member @gluster is deprecated because GlusterFS +# development ceased. +# # Since: 2.9 ## { 'enum': 'BlockdevDriver', 'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs', 'cloop', 'compress', 'copy-before-write', 'copy-on-read', 'dmg', - 'file', 'snapshot-access', 'ftp', 'ftps', 'gluster', + 'file', 'snapshot-access', 'ftp', 'ftps', + {'name': 'gluster', 'features': [ 'deprecated' ] }, {'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, {'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' }, 'http', 'https', @@ -2971,36 +3224,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 @@ -3024,11 +3284,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 ## @@ -3040,8 +3303,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 @@ -3058,13 +3322,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 @@ -3076,8 +3344,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 # @@ -3091,25 +3359,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 ## @@ -3124,11 +3395,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 # @@ -3140,14 +3411,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 ## @@ -3166,11 +3455,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 # @@ -3193,6 +3482,8 @@ ## # @BlockdevQcowEncryption: # +# @format: encryption format +# # Since: 2.10 ## { 'union': 'BlockdevQcowEncryption', @@ -3205,9 +3496,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 ## @@ -3228,6 +3518,8 @@ ## # @BlockdevQcow2Encryption: # +# @format: encryption format +# # Since: 2.10 ## { 'union': 'BlockdevQcow2Encryption', @@ -3239,11 +3531,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) # @@ -3258,51 +3550,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 ## @@ -3312,6 +3616,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', @@ -3325,7 +3630,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 @@ -3337,7 +3644,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 @@ -3349,6 +3658,7 @@ # @SshHostKeyHash: # # @type: The hash algorithm used for the hash +# # @hash: The expected hash value # # Since: 2.12 @@ -3360,6 +3670,8 @@ ## # @SshHostKeyCheck: # +# @mode: How to check the host key +# # Since: 2.12 ## { 'union': 'SshHostKeyCheck', @@ -3377,7 +3689,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 ## @@ -3393,13 +3705,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) # @@ -3442,7 +3755,7 @@ # # Since: 4.1 ## -{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE', +{ 'enum': 'BlkdebugIOType', 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush', 'block-status' ] } @@ -3453,23 +3766,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 # @@ -3492,10 +3802,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 ## @@ -3513,47 +3823,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 ## @@ -3577,13 +3885,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 ## @@ -3639,18 +3948,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 ## @@ -3672,8 +3981,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) # @@ -3704,7 +4012,8 @@ # # Driver specific block device options for the nvme-io_uring backend. # -# @path: path to the NVMe namespace's character device (e.g. /dev/ng0n1). +# @path: path to the NVMe namespace's character device (e.g. +# /dev/ng0n1). # # Since: 7.2 ## @@ -3715,10 +4024,11 @@ ## # @BlockdevOptionsVirtioBlkVfioPci: # -# Driver specific block device options for the virtio-blk-vfio-pci backend. +# 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). +# /sys/bus/pci/devices/0000:00:01.0). # # Since: 7.2 ## @@ -3729,7 +4039,8 @@ ## # @BlockdevOptionsVirtioBlkVhostUser: # -# Driver specific block device options for the virtio-blk-vhost-user backend. +# Driver specific block device options for the virtio-blk-vhost-user +# backend. # # @path: path to the vhost-user UNIX domain socket. # @@ -3742,14 +4053,22 @@ ## # @BlockdevOptionsVirtioBlkVhostVdpa: # -# Driver specific block device options for the virtio-blk-vhost-vdpa backend. +# 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' } ## @@ -3776,32 +4095,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 ## @@ -3827,16 +4145,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 ## @@ -3852,7 +4172,7 @@ ## { 'struct': 'RbdEncryptionCreateOptionsLUKSBase', 'base': 'RbdEncryptionOptionsLUKSBase', - 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm' } } + 'data': { '*cipher-alg': 'QCryptoCipherAlgo' } } ## # @RbdEncryptionOptionsLUKS: @@ -3872,6 +4192,15 @@ 'base': 'RbdEncryptionOptionsLUKSBase', 'data': { } } +## +# @RbdEncryptionOptionsLUKSAny: +# +# Since: 8.0 +## +{ 'struct': 'RbdEncryptionOptionsLUKSAny', + 'base': 'RbdEncryptionOptionsLUKSBase', + 'data': { } } + ## # @RbdEncryptionCreateOptionsLUKS: # @@ -3893,17 +4222,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', @@ -3917,31 +4257,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 ## @@ -3962,9 +4300,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 ## @@ -3978,9 +4318,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 ## @@ -4026,25 +4366,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 ## @@ -4061,25 +4398,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 ## @@ -4095,15 +4433,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 ## @@ -4115,18 +4453,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 ## @@ -4139,8 +4477,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 ## @@ -4151,11 +4489,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 ## @@ -4174,30 +4512,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 @@ -4217,6 +4556,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 @@ -4230,8 +4570,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 @@ -4246,11 +4587,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 ## @@ -4264,13 +4605,13 @@ # 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-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. +# @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 ## @@ -4280,71 +4621,82 @@ ## # @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) +# 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) +# @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) +# +# @min-cluster-size: Minimum size of blocks used by copy-before-write +# operations. Has to be a power of 2. No effect if smaller than +# the maximum of the target's cluster size and 64 KiB. Default 0. +# (Since 9.2) # # Since: 6.2 ## { 'struct': 'BlockdevOptionsCbw', 'base': 'BlockdevOptionsGenericFormat', 'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap', - '*on-cbw-error': 'OnCbwError', '*cbw-timeout': 'uint32' } } + '*on-cbw-error': 'OnCbwError', '*cbw-timeout': 'uint32', + '*min-cluster-size': 'size' } } ## # @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 ## @@ -4425,6 +4777,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 @@ -4439,9 +4792,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 @@ -4458,46 +4813,45 @@ # # Since: 2.9 # -# Example: +# .. qmp-example:: # -# 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": {} } +# +# .. qmp-example:: +# +# -> { "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, 'allow-preconfig': true } @@ -4506,18 +4860,19 @@ # @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: @@ -4533,7 +4888,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 @@ -4550,33 +4905,32 @@ ## # @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. # # Since: 2.9 # -# Example: +# .. qmp-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' }, 'allow-preconfig': true } @@ -4587,14 +4941,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 ## @@ -4611,11 +4968,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 ## @@ -4629,17 +4987,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' } } @@ -4649,6 +5012,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 @@ -4663,7 +5027,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 @@ -4679,9 +5045,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 @@ -4695,7 +5064,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 @@ -4709,6 +5080,7 @@ # Compression type used in qcow2 image file # # @zlib: zlib compression, see +# # @zstd: zstd compression, see # # Since: 5.1 @@ -4722,27 +5094,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 ## @@ -4768,11 +5154,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 @@ -4790,11 +5181,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 ## @@ -4813,14 +5207,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 ## @@ -4843,25 +5237,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 ## @@ -4882,6 +5287,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 @@ -4896,9 +5302,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 ## @@ -4911,6 +5319,7 @@ # @BlockdevVhdxSubformat: # # @dynamic: Growing image file +# # @fixed: Preallocated fixed-size image file # # Since: 2.12 @@ -4924,16 +5333,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 ## @@ -4949,6 +5363,7 @@ # @BlockdevVpcSubformat: # # @dynamic: Growing image file +# # @fixed: Preallocated fixed-size image file # # Since: 2.12 @@ -4962,11 +5377,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 ## @@ -5009,7 +5427,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. @@ -5038,10 +5456,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 ## @@ -5068,8 +5486,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. # @@ -5077,13 +5496,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 @@ -5115,40 +5534,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. +# .. note:: If action is "stop", a STOP event will eventually follow +# the BLOCK_IO_ERROR event. # -# Example: +# .. qmp-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 ## @@ -5165,46 +5584,50 @@ # # 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. +# @qom-path: path to the device object in the QOM tree (since 9.2) # -# @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) +# @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) # # @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 +# .. note:: If action is "stop", a STOP event will eventually follow +# the BLOCK_IO_ERROR event. +# +# .. note:: This event is rate-limited. # # 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 } } +# .. qmp-example:: # +# <- { "event": "BLOCK_IO_ERROR", +# "data": { "qom-path": "/machine/unattached/device[0]", +# "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', + 'data': { 'qom-path': 'str', 'device': 'str', '*node-name': 'str', 'operation': 'IoOperationType', 'action': 'BlockErrorAction', '*nospace': 'bool', 'reason': 'str' } } @@ -5216,31 +5639,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 } } +# .. qmp-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' : 'JobType', @@ -5257,26 +5679,25 @@ # # @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 # # Since: 1.1 # -# Example: -# -# <- { "event": "BLOCK_JOB_CANCELLED", -# "data": { "type": "stream", "device": "virtio-disk0", -# "len": 10737418240, "offset": 134217728, -# "speed": 0 }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } +# .. qmp-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' : 'JobType', @@ -5290,8 +5711,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 # @@ -5299,14 +5720,13 @@ # # Since: 1.3 # -# Example: -# -# <- { "event": "BLOCK_JOB_ERROR", -# "data": { "device": "ide0-hd1", -# "operation": "write", -# "action": "stop" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# .. qmp-example:: # +# <- { "event": "BLOCK_JOB_ERROR", +# "data": { "device": "ide0-hd1", +# "operation": "write", +# "action": "stop" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_ERROR', 'data': { 'device' : 'str', @@ -5320,28 +5740,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 } } +# .. qmp-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': { 'type' : 'JobType', @@ -5353,9 +5772,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 # @@ -5363,12 +5783,11 @@ # # Since: 2.12 # -# Example: -# -# <- { "event": "BLOCK_JOB_PENDING", -# "data": { "type": "mirror", "id": "backup_1" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# .. qmp-example:: # +# <- { "event": "BLOCK_JOB_PENDING", +# "data": { "type": "mirror", "id": "backup_1" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'BLOCK_JOB_PENDING', 'data': { 'type' : 'JobType', @@ -5380,13 +5799,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 ## @@ -5397,15 +5819,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. # @@ -5419,29 +5841,28 @@ ## # @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": {} } +# .. qmp-example:: # +# -> { "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' }, @@ -5450,13 +5871,13 @@ ## # @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. @@ -5466,41 +5887,43 @@ # @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: +# .. qmp-example:: +# :title: Add a new node to a quorum # -# 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": {} } +# -> { "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": {} } +# .. qmp-example:: +# :title: Delete a quorum's node # +# -> { "execute": "x-blockdev-change", +# "arguments": { "parent": "disk1", +# "child": "children.1" } } +# <- { "return": {} } ## { 'command': 'x-blockdev-change', 'data' : { 'parent': 'str', @@ -5512,8 +5935,8 @@ ## # @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. # @@ -5521,29 +5944,31 @@ # # @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: +# .. qmp-example:: +# :title: Move a node into an IOThread # -# 1. Move a node into an IOThread -# -> { "execute": "x-blockdev-set-iothread", -# "arguments": { "node-name": "disk1", -# "iothread": "iothread0" } } -# <- { "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": {} } +# .. qmp-example:: +# :title: 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', @@ -5579,16 +6004,15 @@ # # @sectors-count: failed read operation sector count # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.0 # -# Example: -# -# <- { "event": "QUORUM_FAILURE", -# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, -# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } +# .. qmp-example:: # +# <- { "event": "QUORUM_FAILURE", +# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 }, +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } ## { 'event': 'QUORUM_FAILURE', 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } } @@ -5600,10 +6024,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 # @@ -5611,26 +6035,25 @@ # # @sectors-count: failed read operation sector count # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.0 # -# Example: +# .. qmp-example:: +# :title: 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 } } +# .. qmp-example:: +# :title: 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', @@ -5639,15 +6062,11 @@ ## # @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. -# # Since: 1.7 ## { 'struct': 'BlockdevSnapshotInternal', @@ -5657,29 +6076,29 @@ # @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. +# 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 # -# 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 +# .. note:: Only some image formats such as qcow2 and rbd support +# internal snapshots. # # Since: 1.7 # -# Example: -# -# -> { "execute": "blockdev-snapshot-internal-sync", -# "arguments": { "device": "ide-hd0", -# "name": "snapshot0" } -# } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "blockdev-snapshot-internal-sync", +# "arguments": { "device": "ide-hd0", +# "name": "snapshot0" } +# } +# <- { "return": {} } ## { 'command': 'blockdev-snapshot-internal-sync', 'data': 'BlockdevSnapshotInternal', @@ -5688,47 +6107,59 @@ ## # @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 -# } -# } +# .. qmp-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 +# } +# } ## { 'command': 'blockdev-snapshot-delete-internal-sync', 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, 'returns': 'SnapshotInfo', 'allow-preconfig': true } + +## +# @DummyBlockCoreForceArrays: +# +# Not used by QMP; hack to let us use BlockGraphInfoList internally +# +# Since: 8.0 +## +{ 'struct': 'DummyBlockCoreForceArrays', + 'data': { 'unused-block-graph-info': ['BlockGraphInfo'] } } diff --git a/qapi/block-export.json b/qapi/block-export.json index 4627bbc4e6..ce33fe378d 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: 100) # # 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: 100). +# +# Errors: +# - if the server is already running # # Since: 1.3 ## @@ -71,14 +80,14 @@ ## # @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 ## @@ -92,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 ## @@ -114,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 ## @@ -138,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 ## @@ -152,23 +164,20 @@ # as a raw image. # # @mountpoint: Path on which to export the block device via FUSE. -# This must point to an existing regular file. +# 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 ## @@ -184,11 +193,16 @@ # 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. +# +# @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 ## @@ -206,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 ## @@ -226,13 +240,17 @@ # # 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 ## @@ -245,17 +263,19 @@ # # 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 ## @@ -268,16 +288,18 @@ # # @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 ## @@ -289,8 +311,8 @@ ## # @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 ## @@ -303,8 +325,11 @@ # 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 @@ -319,28 +344,33 @@ ## # @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 ## @@ -377,17 +407,19 @@ ## # @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 ## @@ -419,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 ## diff --git a/qapi/block.json b/qapi/block.json index 5fe068f903..e66666f5c6 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,22 +104,23 @@ # @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 +# .. note:: Ejecting a device with no media results in success. # # Since: 0.14 # -# Example: +# .. qmp-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: +# .. qmp-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: +# .. qmp-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,41 +220,41 @@ ## # @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 # # Since: 2.12 # -# Example: +# .. qmp-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 # @@ -265,22 +272,21 @@ # # Since: 2.12 # -# Example: +# .. qmp-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: +# .. qmp-example:: +# :title: 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": {} } +# .. qmp-example:: +# :title: 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 } } +# .. qmp-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': 'str', 'id': 'str', 'tray-open': 'bool' } } @@ -414,14 +421,13 @@ # # Since: 3.0 # -# Example: -# -# <- { "event": "PR_MANAGER_STATUS_CHANGED", -# "data": { "id": "pr-helper0", -# "connected": true -# }, -# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } +# .. qmp-example:: # +# <- { "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,64 +442,66 @@ # # 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. +# I/O limits can be disabled by setting all of them to 0. In this +# case the device will be removed from its group and the rest of its +# members will not be affected. The 'group' parameter is ignored. # -# Returns: - Nothing on success -# - If @device is not a valid block device, DeviceNotFound +# Errors: +# - If @device is not a valid block device, DeviceNotFound # # Since: 1.1 # -# Example: +# .. qmp-example:: # -# -> { "execute": "block_set_io_throttle", -# "arguments": { "id": "virtio-blk-pci0/virtio-backend", -# "bps": 0, -# "bps_rd": 0, -# "bps_wr": 0, -# "iops": 512, -# "iops_rd": 0, -# "iops_wr": 0, -# "bps_max": 0, -# "bps_rd_max": 0, -# "bps_wr_max": 0, -# "iops_max": 0, -# "iops_rd_max": 0, -# "iops_wr_max": 0, -# "bps_max_length": 0, -# "iops_size": 0 } } -# <- { "return": {} } +# -> { "execute": "block_set_io_throttle", +# "arguments": { "id": "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": {} } +# .. qmp-example:: +# +# -> { "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', @@ -504,74 +512,91 @@ # # 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): +# .. qmp-example:: +# :annotated: # -# -> { "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):: # -# 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": [10, 50, 100] } } +# <- { "return": {} } # -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries-write": [10, 50, 100] } } -# <- { "return": {} } +# .. qmp-example:: +# :annotated: # -# 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) +# Set new histogram only for write, other histograms will remain +# not changed (or not created):: # -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0", -# "boundaries": [10, 50, 100], -# "boundaries-write": [1000, 5000] } } -# <- { "return": {} } +# -> { "execute": "block-latency-histogram-set", +# "arguments": { "id": "drive0", +# "boundaries-write": [10, 50, 100] } } +# <- { "return": {} } # -# Example: -# remove all latency histograms: +# .. qmp-example:: +# :annotated: # -# -> { "execute": "block-latency-histogram-set", -# "arguments": { "id": "drive0" } } -# <- { "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": {} } +# +# .. qmp-example:: +# :annotated: +# +# 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-zap': ['uint64'], '*boundaries-flush': ['uint64'] }, 'allow-preconfig': true } diff --git a/qapi/char.json b/qapi/char.json index 923dc5056d..e045354350 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. +# .. note:: @filename is encoded using the QEMU command line character +# device encoding. See the QEMU man page for details. # # Since: 0.14 ## @@ -40,29 +40,28 @@ # # Since: 0.14 # -# 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 -# } -# ] -# } +# .. qmp-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 +# } +# ] +# } ## { 'command': 'query-chardev', 'returns': ['ChardevInfo'], 'allow-preconfig': true } @@ -87,26 +86,25 @@ # # Since: 2.0 # -# Example: -# -# -> { "execute": "query-chardev-backends" } -# <- { -# "return":[ -# { -# "name":"udp" -# }, -# { -# "name":"tcp" -# }, -# { -# "name":"unix" -# }, -# { -# "name":"spiceport" -# } -# ] -# } +# .. qmp-example:: # +# -> { "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": {} } +# .. qmp-example:: # +# -> { "execute": "ringbuf-write", +# "arguments": { "device": "foo", +# "data": "abcdefgh", +# "format": "utf8" } } +# <- { "return": {} } ## { 'command': 'ringbuf-write', 'data': { 'device': 'str', @@ -170,27 +165,25 @@ # # @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 # # Since: 1.4 # -# Example: -# -# -> { "execute": "ringbuf-read", -# "arguments": { "device": "foo", -# "size": 1000, -# "format": "utf8" } } -# <- { "return": "abcdefgh" } +# .. qmp-example:: # +# -> { "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,48 @@ # # 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. The use of this member is +# deprecated, use @reconnect-ms instead. (default: 0) (Since: 2.2) +# +# @reconnect-ms: For a client socket, if a socket is disconnected, +# then attempt a reconnect after the given number of milliseconds. +# Setting this to zero disables this function. This member is +# mutually exclusive with @reconnect. +# (default: 0) (Since: 9.2) +# +# Features: +# +# @deprecated: Member @reconnect is deprecated. Use @reconnect-ms +# instead. # # Since: 1.4 ## @@ -284,7 +299,8 @@ '*telnet': 'bool', '*tn3270': 'bool', '*websocket': 'bool', - '*reconnect': 'int' }, + '*reconnect': { 'type': 'int', 'features': [ 'deprecated' ] }, + '*reconnect-ms': 'int' }, 'base': 'ChardevCommon' } ## @@ -293,6 +309,7 @@ # Configuration info for datagram socket chardevs. # # @remote: remote address +# # @local: local address # # Since: 1.5 @@ -320,8 +337,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 +394,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 +433,7 @@ # Configuration info for qemu vdagent implementation. # # @mouse: enable/disable mouse, default is enabled. +# # @clipboard: enable/disable clipboard, default is disabled. # # Since: 6.1 @@ -419,32 +444,77 @@ 'base': 'ChardevCommon', 'if': 'CONFIG_SPICE_PROTOCOL' } +## +# @ChardevPty: +# +# Configuration info for pty implementation. +# +# @path: optional path to create a symbolic link that points to the +# allocated PTY +# +# Since: 9.2 +## +{ 'struct': 'ChardevPty', + 'data': { '*path': 'str' }, + 'base': 'ChardevCommon' } + ## # @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 +# @file: regular files +# +# @serial: serial host device +# +# @parallel: parallel host device +# +# @pipe: pipes (since 1.5) +# +# @socket: stream socket +# +# @udp: datagram socket (since 1.5) +# +# @pty: pseudo-terminal +# +# @null: provides no input, throws away output +# +# @mux: (since 1.5) +# +# @msmouse: emulated Microsoft serial mouse (since 1.5) +# +# @wctablet: emulated Wacom Penpartner serial tablet (since 2.9) +# +# @braille: Baum Braille device (since 1.5) +# +# @testdev: device for test-suite control (since 2.2) +# +# @stdio: standard I/O (since 1.5) +# +# @console: Windows console (since 1.5) +# +# @spicevmc: spice vm channel (since 1.5) +# +# @spiceport: Spice port channel (since 1.5) +# +# @qemu-vdagent: Spice vdagent (since 6.1) +# +# @dbus: D-Bus channel (since 7.0) +# +# @vc: virtual console (since 1.5) +# +# @ringbuf: memory ring buffer (since 1.6) +# +# @memory: synonym for @ringbuf (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 +523,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 +548,8 @@ ## # @ChardevHostdevWrapper: # +# @data: Configuration info for device and pipe chardevs +# # Since: 1.4 ## { 'struct': 'ChardevHostdevWrapper', @@ -485,6 +558,8 @@ ## # @ChardevSocketWrapper: # +# @data: Configuration info for (stream) socket chardevs +# # Since: 1.4 ## { 'struct': 'ChardevSocketWrapper', @@ -493,6 +568,8 @@ ## # @ChardevUdpWrapper: # +# @data: Configuration info for datagram socket chardevs +# # Since: 1.5 ## { 'struct': 'ChardevUdpWrapper', @@ -501,6 +578,8 @@ ## # @ChardevCommonWrapper: # +# @data: Configuration shared across all chardev backends +# # Since: 2.6 ## { 'struct': 'ChardevCommonWrapper', @@ -509,6 +588,8 @@ ## # @ChardevMuxWrapper: # +# @data: Configuration info for mux chardevs +# # Since: 1.5 ## { 'struct': 'ChardevMuxWrapper', @@ -517,6 +598,8 @@ ## # @ChardevStdioWrapper: # +# @data: Configuration info for stdio chardevs +# # Since: 1.5 ## { 'struct': 'ChardevStdioWrapper', @@ -525,6 +608,8 @@ ## # @ChardevSpiceChannelWrapper: # +# @data: Configuration info for spice vm channel chardevs +# # Since: 1.5 ## { 'struct': 'ChardevSpiceChannelWrapper', @@ -534,6 +619,8 @@ ## # @ChardevSpicePortWrapper: # +# @data: Configuration info for spice port chardevs +# # Since: 1.5 ## { 'struct': 'ChardevSpicePortWrapper', @@ -543,6 +630,8 @@ ## # @ChardevQemuVDAgentWrapper: # +# @data: Configuration info for qemu vdagent implementation +# # Since: 6.1 ## { 'struct': 'ChardevQemuVDAgentWrapper', @@ -552,6 +641,8 @@ ## # @ChardevDBusWrapper: # +# @data: Configuration info for DBus chardevs +# # Since: 7.0 ## { 'struct': 'ChardevDBusWrapper', @@ -561,6 +652,8 @@ ## # @ChardevVCWrapper: # +# @data: Configuration info for virtual console chardevs +# # Since: 1.5 ## { 'struct': 'ChardevVCWrapper', @@ -569,36 +662,55 @@ ## # @ChardevRingbufWrapper: # +# @data: Configuration info for ring buffer chardevs +# # Since: 1.5 ## { 'struct': 'ChardevRingbufWrapper', 'data': { 'data': 'ChardevRingbuf' } } + +## +# @ChardevPtyWrapper: +# +# @data: Configuration info for pty chardevs +# +# Since: 9.2 +## +{ 'struct': 'ChardevPtyWrapper', + 'data': { 'data': 'ChardevPty' } } + ## # @ChardevBackend: # # 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', - 'pty': 'ChardevCommonWrapper', + 'pty': 'ChardevPtyWrapper', 'null': 'ChardevCommonWrapper', '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 +721,6 @@ 'if': 'CONFIG_DBUS_DISPLAY' }, 'vc': 'ChardevVCWrapper', 'ringbuf': 'ChardevRingbufWrapper', - # next one is just for compatibility 'memory': 'ChardevRingbufWrapper' } } ## @@ -617,8 +728,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 +742,34 @@ # Add a character device backend # # @id: the chardev's ID, must be unique +# # @backend: backend type and parameters # # Returns: ChardevReturn. # # Since: 1.4 # -# Example: +# .. qmp-example:: # -# -> { "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": {} } +# .. qmp-example:: # -# -> { "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": {} } # +# .. qmp-example:: +# +# -> { "execute" : "chardev-add", +# "arguments" : { "id" : "baz", +# "backend" : { "type" : "pty", "data" : {} } } } +# <- { "return": { "pty" : "/dev/pty/42" } } ## { 'command': 'chardev-add', 'data': { 'id': 'str', @@ -667,35 +782,37 @@ # Change a character device backend # # @id: the chardev's ID, must exist +# # @backend: new backend type and parameters # # Returns: ChardevReturn. # # Since: 2.10 # -# Example: +# .. qmp-example:: # -# -> { "execute" : "chardev-change", -# "arguments" : { "id" : "baz", -# "backend" : { "type" : "pty", "data" : {} } } } -# <- { "return": { "pty" : "/dev/pty/42" } } +# -> { "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": {}} +# .. qmp-example:: # +# -> {"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 +826,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": {} } +# .. qmp-example:: # +# -> { "execute": "chardev-remove", "arguments": { "id" : "foo" } } +# <- { "return": {} } ## { 'command': 'chardev-remove', 'data': { 'id': 'str' } } @@ -729,15 +843,12 @@ # # @id: the chardev's ID, must exist # -# Returns: Nothing on success -# # Since: 2.10 # -# Example: -# -# -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "chardev-send-break", "arguments": { "id" : "foo" } } +# <- { "return": {} } ## { 'command': 'chardev-send-break', 'data': { 'id': 'str' } } @@ -751,16 +862,15 @@ # # @open: true if the guest has opened the virtio-serial port # -# Note: This event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 2.1 # -# Example: -# -# <- { "event": "VSERPORT_CHANGE", -# "data": { "id": "channel0", "open": true }, -# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } +# .. qmp-example:: # +# <- { "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..6ffc7a3789 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 ## @@ -189,7 +183,19 @@ ## # @GrabToggleKeys: # -# Keys to toggle input-linux between host and guest. +# Key combinations to toggle input-linux between host and guest. +# +# @ctrl-ctrl: left and right control key +# +# @alt-alt: left and right alt key +# +# @shift-shift: left and right shift key +# +# @meta-meta: left and right meta key +# +# @scrolllock: scroll lock key +# +# @ctrl-scrolllock: either control key and scroll lock key # # Since: 4.0 ## 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..336386f79e 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: +# .. qmp-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) +# .. note:: 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 +# :doc:`/interop/qmp-spec`) # -# The QMP client needs to explicitly enable QMP capabilities, otherwise -# all the QMP capabilities will be turned off by default. +# .. note:: 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":"" -# } -# } +# .. qmp-example:: # +# -> { "execute": "query-version" } +# <- { +# "return":{ +# "qemu":{ +# "major":0, +# "minor":11, +# "micro":5 +# }, +# "package":"" +# } +# } ## { 'command': 'query-version', 'returns': 'VersionInfo', 'allow-preconfig': true } @@ -136,22 +135,22 @@ # # Since: 0.14 # -# Example: +# .. qmp-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" +# }, +# ... +# ] +# } # +# 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: +# .. qmp-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..c9d967d782 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 # @@ -21,7 +20,6 @@ # Since: 2.5 ## { 'enum': 'QCryptoTLSCredsEndpoint', - 'prefix': 'QCRYPTO_TLS_CREDS_ENDPOINT', 'data': ['client', 'server']} ## @@ -29,61 +27,82 @@ # # 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 ## { 'enum': 'QCryptoSecretFormat', - 'prefix': 'QCRYPTO_SECRET_FORMAT', 'data': ['raw', 'base64']} ## -# @QCryptoHashAlgorithm: +# @QCryptoHashAlgo: # # 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) -# @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) +# @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) +# +# @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) +# @sm3: SM3. (since 9.2.0) # # Since: 2.6 ## -{ 'enum': 'QCryptoHashAlgorithm', - 'prefix': 'QCRYPTO_HASH_ALG', - 'data': ['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'ripemd160']} +{ 'enum': 'QCryptoHashAlgo', + 'data': ['md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', 'ripemd160', 'sm3']} ## -# @QCryptoCipherAlgorithm: +# @QCryptoCipherAlgo: # # 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', - 'prefix': 'QCRYPTO_CIPHER_ALG', +{ 'enum': 'QCryptoCipherAlgo', 'data': ['aes-128', 'aes-192', 'aes-256', '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,33 +110,36 @@ # 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 ## { 'enum': 'QCryptoCipherMode', - 'prefix': 'QCRYPTO_CIPHER_MODE', 'data': ['ecb', 'cbc', 'xts', 'ctr']} ## -# @QCryptoIVGenAlgorithm: +# @QCryptoIVGenAlgo: # -# 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 ## -{ 'enum': 'QCryptoIVGenAlgorithm', - 'prefix': 'QCRYPTO_IVGEN_ALG', +{ 'enum': 'QCryptoIVGenAlgo', 'data': ['plain', 'plain64', 'essiv']} ## @@ -125,21 +147,20 @@ # # 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 ## { 'enum': 'QCryptoBlockFormat', -# 'prefix': 'QCRYPTO_BLOCK_FORMAT', 'data': ['qcow', 'luks']} ## # @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 +175,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 +189,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,36 +202,40 @@ # # 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) # # Since: 2.6 ## { 'struct': 'QCryptoBlockCreateOptionsLUKS', 'base': 'QCryptoBlockOptionsLUKS', - 'data': { '*cipher-alg': 'QCryptoCipherAlgorithm', + 'data': { '*cipher-alg': 'QCryptoCipherAlgo', '*cipher-mode': 'QCryptoCipherMode', - '*ivgen-alg': 'QCryptoIVGenAlgorithm', - '*ivgen-hash-alg': 'QCryptoHashAlgorithm', - '*hash-alg': 'QCryptoHashAlgorithm', - '*iter-time': 'int'}} + '*ivgen-alg': 'QCryptoIVGenAlgo', + '*ivgen-hash-alg': 'QCryptoHashAlgo', + '*hash-alg': 'QCryptoHashAlgo', + '*iter-time': 'int' }} ## # @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 +248,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 +262,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 +275,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,23 +299,34 @@ # 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 ## { 'struct': 'QCryptoBlockInfoLUKS', - 'data': {'cipher-alg': 'QCryptoCipherAlgorithm', + 'data': {'cipher-alg': 'QCryptoCipherAlgo', 'cipher-mode': 'QCryptoCipherMode', - 'ivgen-alg': 'QCryptoIVGenAlgorithm', - '*ivgen-hash-alg': 'QCryptoHashAlgorithm', - 'hash-alg': 'QCryptoHashAlgorithm', + 'ivgen-alg': 'QCryptoIVGenAlgo', + '*ivgen-hash-alg': 'QCryptoHashAlgo', + 'hash-alg': 'QCryptoHashAlgo', + 'detached-header': 'bool', 'payload-offset': 'int', 'master-key-iters': 'int', 'uuid': 'str', @@ -312,7 +350,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 +362,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 +404,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,28 +420,22 @@ # # 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) +# @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. -# -# Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. +# @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. # # Since: 2.6 ## { 'struct': 'SecretCommonProperties', - 'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] }, - '*format': 'QCryptoSecretFormat', + 'data': { '*format': 'QCryptoSecretFormat', '*keyid': 'str', '*iv': 'str' } } @@ -435,7 +468,8 @@ ## { 'struct': 'SecretKeyringProperties', 'base': 'SecretCommonProperties', - 'data': { 'serial': 'int32' } } + 'data': { 'serial': 'int32' }, + 'if': 'CONFIG_SECRET_KEYRING' } ## # @TlsCredsProperties: @@ -443,16 +477,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,75 +502,49 @@ # # 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) -# -# Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. -# # Since: 2.5 ## { 'struct': 'TlsCredsAnonProperties', 'base': 'TlsCredsProperties', - 'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] } } } + 'data': { } } ## # @TlsCredsPskProperties: # # 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) -# -# @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. +# @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. # # Since: 3.0 ## { 'struct': 'TlsCredsPskProperties', 'base': 'TlsCredsProperties', - 'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] }, - '*username': 'str' } } + 'data': { '*username': 'str' } } ## # @TlsCredsX509Properties: # # 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) -# # @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. -# -# Features: -# @deprecated: Member @loaded is deprecated. Setting true doesn't make sense, -# and false is already the default. +# @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. # # Since: 2.5 ## { 'struct': 'TlsCredsX509Properties', 'base': 'TlsCredsProperties', - 'data': { '*loaded': { 'type': 'bool', 'features': ['deprecated'] }, - '*sanity-check': 'bool', + 'data': { '*sanity-check': 'bool', '*passwordid': 'str' } } ## -# @QCryptoAkCipherAlgorithm: +# @QCryptoAkCipherAlgo: # # The supported algorithms for asymmetric encryption ciphers # @@ -543,8 +552,7 @@ # # Since: 7.1 ## -{ 'enum': 'QCryptoAkCipherAlgorithm', - 'prefix': 'QCRYPTO_AKCIPHER_ALG', +{ 'enum': 'QCryptoAkCipherAlgo', 'data': ['rsa']} ## @@ -552,24 +560,27 @@ # # The type of asymmetric keys. # +# @public: public key +# +# @private: private key +# # Since: 7.1 ## { 'enum': 'QCryptoAkCipherKeyType', - 'prefix': 'QCRYPTO_AKCIPHER_KEY_TYPE', 'data': ['public', 'private']} ## -# @QCryptoRSAPaddingAlgorithm: +# @QCryptoRSAPaddingAlgo: # # The padding algorithm for RSA. # # @raw: no padding used +# # @pkcs1: pkcs1#v1.5 # # Since: 7.1 ## -{ 'enum': 'QCryptoRSAPaddingAlgorithm', - 'prefix': 'QCRYPTO_RSA_PADDING_ALG', +{ 'enum': 'QCryptoRSAPaddingAlgo', 'data': ['raw', 'pkcs1']} ## @@ -577,14 +588,15 @@ # # Specific parameters for RSA algorithm. # -# @hash-alg: QCryptoHashAlgorithm -# @padding-alg: QCryptoRSAPaddingAlgorithm +# @hash-alg: QCryptoHashAlgo +# +# @padding-alg: QCryptoRSAPaddingAlgo # # Since: 7.1 ## { 'struct': 'QCryptoAkCipherOptionsRSA', - 'data': { 'hash-alg':'QCryptoHashAlgorithm', - 'padding-alg': 'QCryptoRSAPaddingAlgorithm'}} + 'data': { 'hash-alg':'QCryptoHashAlgo', + 'padding-alg': 'QCryptoRSAPaddingAlgo'}} ## # @QCryptoAkCipherOptions: @@ -592,9 +604,11 @@ # The options that are available for all asymmetric key algorithms # when creating a new QCryptoAkCipher. # +# @alg: encryption cipher algorithm +# # Since: 7.1 ## { 'union': 'QCryptoAkCipherOptions', - 'base': { 'alg': 'QCryptoAkCipherAlgorithm' }, + 'base': { 'alg': 'QCryptoAkCipherAlgo' }, 'discriminator': 'alg', 'data': { 'rsa': 'QCryptoAkCipherOptionsRSA' }} diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json new file mode 100644 index 0000000000..04d0e21d20 --- /dev/null +++ b/qapi/cryptodev.json @@ -0,0 +1,103 @@ +# -*- 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 +## + +## +# @QCryptodevBackendAlgoType: +# +# The supported algorithm types of a crypto device. +# +# @sym: symmetric encryption +# +# @asym: asymmetric Encryption +# +# Since: 8.0 +## +{ 'enum': 'QCryptodevBackendAlgoType', + 'data': ['sym', 'asym']} + +## +# @QCryptodevBackendServiceType: +# +# The supported service types of a crypto device. +# +# @cipher: Symmetric Key Cipher service +# +# @hash: Hash service +# +# @mac: Message Authentication Codes service +# +# @aead: Authenticated Encryption with Associated Data service +# +# @akcipher: Asymmetric Key Cipher service +# +# Since: 8.0 +## +{ 'enum': 'QCryptodevBackendServiceType', + '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', + '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..9f65589bce --- /dev/null +++ b/qapi/cxl.json @@ -0,0 +1,555 @@ +# -*- 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. +# +# @retry-threshold: Retry threshold hit in the Local Retry State +# Machine, 68B Flits 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'}} + +## +# @CxlDynamicCapacityExtent: +# +# A single dynamic capacity extent. This is a contiguous allocation +# of memory by Device Physical Address within a single Dynamic +# Capacity Region on a CXL Type 3 Device. +# +# @offset: The offset (in bytes) to the start of the region where the +# extent belongs to. +# +# @len: The length of the extent in bytes. +# +# Since: 9.1 +## +{ 'struct': 'CxlDynamicCapacityExtent', + 'data': { + 'offset':'uint64', + 'len': 'uint64' + } +} + +## +# @CxlExtentSelectionPolicy: +# +# The policy to use for selecting which extents comprise the added +# capacity, as defined in Compute Express Link (CXL) Specification, +# Revision 3.1, Table 7-70. +# +# @free: Device is responsible for allocating the requested memory +# capacity and is free to do this using any combination of +# supported extents. +# +# @contiguous: Device is responsible for allocating the requested +# memory capacity but must do so as a single contiguous +# extent. +# +# @prescriptive: The precise set of extents to be allocated is +# specified by the command. Thus allocation is being managed +# by the issuer of the allocation command, not the device. +# +# @enable-shared-access: Capacity has already been allocated to a +# different host using free, contiguous or prescriptive policy +# with a known tag. This policy then instructs the device to make +# the capacity with the specified tag available to an additional +# host. Capacity is implicit as it matches that already +# associated with the tag. Note that the extent list (and hence +# Device Physical Addresses) used are per host, so a device may +# use different representations on each host. The ordering of the +# extents provided to each host is indicated to the host using per +# extent sequence numbers generated by the device. Has a similar +# meaning for temporal sharing, but in that case there may be only +# one host involved. +# +# Since: 9.1 +## +{ 'enum': 'CxlExtentSelectionPolicy', + 'data': ['free', + 'contiguous', + 'prescriptive', + 'enable-shared-access'] +} + +## +# @cxl-add-dynamic-capacity: +# +# Initiate adding dynamic capacity extents to a host. This simulates +# operations defined in Compute Express Link (CXL) Specification, +# Revision 3.1, Section 7.6.7.6.5. Note that, currently, establishing +# success or failure of the full Add Dynamic Capacity flow requires +# out of band communication with the OS of the CXL host. +# +# @path: path to the CXL Dynamic Capacity Device in the QOM tree. +# +# @host-id: The "Host ID" field as defined in Compute Express Link +# (CXL) Specification, Revision 3.1, Table 7-70. +# +# @selection-policy: The "Selection Policy" bits as defined in +# Compute Express Link (CXL) Specification, Revision 3.1, +# Table 7-70. It specifies the policy to use for selecting +# which extents comprise the added capacity. +# +# @region: The "Region Number" field as defined in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-70. Valid +# range is from 0-7. +# +# @tag: The "Tag" field as defined in Compute Express Link (CXL) +# Specification, Revision 3.1, Table 7-70. +# +# @extents: The "Extent List" field as defined in Compute Express Link +# (CXL) Specification, Revision 3.1, Table 7-70. +# +# Features: +# +# @unstable: For now this command is subject to change. +# +# Since : 9.1 +## +{ 'command': 'cxl-add-dynamic-capacity', + 'data': { 'path': 'str', + 'host-id': 'uint16', + 'selection-policy': 'CxlExtentSelectionPolicy', + 'region': 'uint8', + '*tag': 'str', + 'extents': [ 'CxlDynamicCapacityExtent' ] + }, + 'features': [ 'unstable' ] +} + +## +# @CxlExtentRemovalPolicy: +# +# The policy to use for selecting which extents comprise the released +# capacity, defined in the "Flags" field in Compute Express Link (CXL) +# Specification, Revision 3.1, Table 7-71. +# +# @tag-based: Extents are selected by the device based on tag, with +# no requirement for contiguous extents. +# +# @prescriptive: Extent list of capacity to release is included in +# the request payload. +# +# Since: 9.1 +## +{ 'enum': 'CxlExtentRemovalPolicy', + 'data': ['tag-based', + 'prescriptive'] +} + +## +# @cxl-release-dynamic-capacity: +# +# Initiate release of dynamic capacity extents from a host. This +# simulates operations defined in Compute Express Link (CXL) +# Specification, Revision 3.1, Section 7.6.7.6.6. Note that, +# currently, success or failure of the full Release Dynamic Capacity +# flow requires out of band communication with the OS of the CXL host. +# +# @path: path to the CXL Dynamic Capacity Device in the QOM tree. +# +# @host-id: The "Host ID" field as defined in Compute Express Link +# (CXL) Specification, Revision 3.1, Table 7-71. +# +# @removal-policy: Bit[3:0] of the "Flags" field as defined in +# Compute Express Link (CXL) Specification, Revision 3.1, +# Table 7-71. +# +# @forced-removal: Bit[4] of the "Flags" field in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-71. When set, +# the device does not wait for a Release Dynamic Capacity command +# from the host. Instead, the host immediately looses access to +# the released capacity. +# +# @sanitize-on-release: Bit[5] of the "Flags" field in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-71. When set, +# the device should sanitize all released capacity as a result of +# this request. This ensures that all user data and metadata is +# made permanently unavailable by whatever means is appropriate +# for the media type. Note that changing encryption keys is not +# sufficient. +# +# @region: The "Region Number" field as defined in Compute Express +# Link Specification, Revision 3.1, Table 7-71. Valid range +# is from 0-7. +# +# @tag: The "Tag" field as defined in Compute Express Link (CXL) +# Specification, Revision 3.1, Table 7-71. +# +# @extents: The "Extent List" field as defined in Compute Express +# Link (CXL) Specification, Revision 3.1, Table 7-71. +# +# Features: +# +# @unstable: For now this command is subject to change. +# +# Since : 9.1 +## +{ 'command': 'cxl-release-dynamic-capacity', + 'data': { 'path': 'str', + 'host-id': 'uint16', + 'removal-policy': 'CxlExtentRemovalPolicy', + '*forced-removal': 'bool', + '*sanitize-on-release': 'bool', + 'region': 'uint8', + '*tag': 'str', + 'extents': [ 'CxlDynamicCapacityExtent' ] + }, + 'features': [ 'unstable' ] +} diff --git a/qapi/dump.json b/qapi/dump.json index 6fc215dd47..d7826c0e32 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 +# .. note:: All boolean arguments default to false. # # Since: 1.2 # -# Example: -# -# -> { "execute": "dump-guest-memory", -# "arguments": { "paging": false, "protocol": "fd:dump" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "dump-guest-memory", +# "arguments": { "paging": false, "protocol": "fd:dump" } } +# <- { "return": {} } ## { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*detach': 'bool', @@ -137,12 +150,11 @@ # # Since: 2.6 # -# Example: -# -# -> { "execute": "query-dump" } -# <- { "return": { "status": "active", "completed": 1024000, -# "total": 2048000 } } +# .. qmp-example:: # +# -> { "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 } } +# .. qmp-example:: # +# <- { "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"] } } +# .. qmp-example:: # +# -> { "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..db19ae850f --- /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..01bb242947 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. +# .. 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. # # 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 ## @@ -131,6 +127,22 @@ # section 1, plus 'int' (split off 'number'), plus the obvious top # type 'value'. # +# @string: JSON string +# +# @number: JSON number +# +# @int: JSON number that is an integer +# +# @boolean: literal ``false`` or ``true`` +# +# @null: literal ``null`` +# +# @object: JSON object +# +# @array: JSON array +# +# @value: any JSON value +# # Since: 2.5 ## { 'enum': 'JSONType', @@ -142,13 +154,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 +182,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 +208,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 +237,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 +261,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 +273,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 +302,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 +324,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..cfc3beedd2 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..298e51f373 --- /dev/null +++ b/qapi/machine-common.json @@ -0,0 +1,112 @@ +# -*- 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. + +## +# = Common machine types +## + +## +# @S390CpuEntitlement: +# +# An enumeration of CPU entitlements that can be assumed by a virtual +# S390 CPU +# +# Since: 8.2 +## +{ 'enum': 'S390CpuEntitlement', + 'data': [ 'auto', 'low', 'medium', 'high' ] } + +## +# @CpuTopologyLevel: +# +# An enumeration of CPU topology levels. +# +# @thread: thread level, which would also be called SMT level or +# logical processor level. The @threads option in +# SMPConfiguration is used to configure the topology of this +# level. +# +# @core: core level. The @cores option in SMPConfiguration is used +# to configure the topology of this level. +# +# @module: module level. The @modules option in SMPConfiguration is +# used to configure the topology of this level. +# +# @cluster: cluster level. The @clusters option in SMPConfiguration +# is used to configure the topology of this level. +# +# @die: die level. The @dies option in SMPConfiguration is used to +# configure the topology of this level. +# +# @socket: socket level, which would also be called package level. +# The @sockets option in SMPConfiguration is used to configure +# the topology of this level. +# +# @book: book level. The @books option in SMPConfiguration is used +# to configure the topology of this level. +# +# @drawer: drawer level. The @drawers option in SMPConfiguration is +# used to configure the topology of this level. +# +# @default: default level. Some architectures will have default +# topology settings (e.g., cache topology), and this special +# level means following the architecture-specific settings. +# +# Since: 9.2 +## +{ 'enum': 'CpuTopologyLevel', + 'data': [ 'thread', 'core', 'module', 'cluster', 'die', + 'socket', 'book', 'drawer', 'default' ] } + +## +# @CacheLevelAndType: +# +# Caches a system may have. The enumeration value here is the +# combination of cache level and cache type. +# +# @l1d: L1 data cache. +# +# @l1i: L1 instruction cache. +# +# @l2: L2 (unified) cache. +# +# @l3: L3 (unified) cache +# +# Since: 9.2 +## +{ 'enum': 'CacheLevelAndType', + 'data': [ 'l1d', 'l1i', 'l2', 'l3' ] } + +## +# @SmpCacheProperties: +# +# Cache information for SMP system. +# +# @cache: Cache name, which is the combination of cache level +# and cache type. +# +# @topology: Cache topology level. It accepts the CPU topology +# enumeration as the parameter, i.e., CPUs in the same +# topology container share the same cache. +# +# Since: 9.2 +## +{ 'struct': 'SmpCacheProperties', + 'data': { + 'cache': 'CacheLevelAndType', + 'topology': 'CpuTopologyLevel' } } + +## +# @SmpCachePropertiesWrapper: +# +# List wrapper of SmpCacheProperties. +# +# @caches: the list of SmpCacheProperties. +# +# Since 9.2 +## +{ 'struct': 'SmpCachePropertiesWrapper', + 'data': { 'caches': ['SmpCacheProperties'] } } diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 2e267fa458..541f93eeb7 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -4,17 +4,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. +{ '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 -# that an architecture might require should be hidden behind the name. -# However, if required, architectures can expose relevant properties. +# 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 +32,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 +63,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 +104,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 +125,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 # -# Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. +# @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. # # Since: 2.8 ## @@ -160,38 +184,50 @@ ## # @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 # -# Note: this command isn't specific to s390x, but is only implemented -# on this architecture currently. +# @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. # # Since: 2.8 ## @@ -208,44 +244,66 @@ # # @model: the expanded CpuModelInfo. # +# @deprecated-props: a list of properties that are flagged as +# deprecated by the CPU vendor. The list depends on the +# CpuModelExpansionType: "static" properties are a subset of the +# enabled-properties for the expanded model; "full" properties are +# a set of properties that are deprecated across all models for +# the architecture. (since: 9.1). +# # Since: 2.8 ## { 'struct': 'CpuModelExpansionInfo', - 'data': { 'model': 'CpuModelInfo' }, + 'data': { 'model': 'CpuModelInfo', + 'deprecated-props' : { 'type': ['str'], + 'if': 'TARGET_S390X' } }, '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 +313,9 @@ 'returns': 'CpuModelExpansionInfo', 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } ## # @CpuDefinitionInfo: @@ -265,49 +325,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 +383,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 +401,123 @@ 'TARGET_I386', 'TARGET_S390X', 'TARGET_MIPS', - 'TARGET_LOONGARCH64' ] } } + 'TARGET_LOONGARCH64', + 'TARGET_RISCV' ] } } + +## +# @S390CpuPolarization: +# +# An enumeration of CPU polarization that can be assumed by a virtual +# S390 CPU +# +# Since: 8.2 +## +{ 'enum': 'S390CpuPolarization', + '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': 'S390CpuEntitlement', + '*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 +# +# .. qmp-example:: +# +# <- { "event": "CPU_POLARIZATION_CHANGE", +# "data": { "polarization": "horizontal" }, +# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } } +## +{ 'event': 'CPU_POLARIZATION_CHANGE', + 'data': { 'polarization': 'S390CpuPolarization' }, + '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': 'S390CpuPolarization' }, + '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 b9228a5e46..a6b8795b09 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -9,43 +9,46 @@ ## { '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". +# @loongarch64: since 7.1 +# +# .. note:: 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 ## { 'enum' : 'SysEmuTarget', - 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', + 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'hppa', 'i386', 'loongarch64', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', - 'mips64el', 'mipsel', 'nios2', 'or1k', 'ppc', + 'mips64el', 'mipsel', 'or1k', 'ppc', 'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4', 'sh4eb', 'sparc', 'sparc64', 'tricore', 'x86_64', 'xtensa', 'xtensaeb' ] } ## -# @CpuS390State: +# @S390CpuState: # -# 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 ## -{ 'enum': 'CpuS390State', - 'prefix': 'S390_CPU_STATE', +{ 'enum': 'S390CpuState', 'data': [ 'uninitialized', 'stopped', 'check-stop', 'operating', 'load' ] } ## @@ -55,9 +58,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': 'S390CpuState', + '*dedicated': 'bool', + '*entitlement': 'S390CpuEntitlement' } } ## # @CpuInfoFast: @@ -70,11 +80,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 ## @@ -96,37 +105,57 @@ # # Since: 2.12 # -# Example: +# .. qmp-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' ] } +## +# @CompatProperty: +# +# Property default values specific to a machine type, for use by +# scripts/compare-machine-types. +# +# @qom-type: name of the QOM type to which the default applies +# +# @property: name of its property to which the default applies +# +# @value: the default value (machine-specific default can overwrite +# the "default" default, to avoid this use -machine none) +# +# Since: 9.1 +## +{ 'struct': 'CompatProperty', + 'data': { 'qom-type': 'str', + 'property': 'str', + 'value': 'str' } } + ## # @MachineInfo: # @@ -139,21 +168,32 @@ # @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) +# +# @compat-props: The machine type's compatibility properties. Only +# present when query-machines argument @compat-props is true. +# (since 9.1) +# +# Features: +# +# @unstable: Member @compat-props is experimental. # # Since: 1.2 ## @@ -162,18 +202,53 @@ '*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', + '*compat-props': { 'type': ['CompatProperty'], + 'features': ['unstable'] } } } ## # @query-machines: # # Return a list of supported machines # +# @compat-props: if true, also return compatibility properties. +# (default: false) (since 9.1) +# +# Features: +# +# @unstable: Argument @compat-props is experimental. +# # Returns: a list of MachineInfo # # Since: 1.2 +# +# .. qmp-example:: +# +# -> { "execute": "query-machines", "arguments": { "compat-props": true } } +# <- { "return": [ +# { +# "hotpluggable-cpus": true, +# "name": "pc-q35-6.2", +# "compat-props": [ +# { +# "qom-type": "virtio-mem", +# "property": "unplugged-inaccessible", +# "value": "off" +# } +# ], +# "numa-mem-supported": false, +# "default-cpu-type": "qemu64-x86_64-cpu", +# "cpu-max": 288, +# "deprecated": false, +# "default-ram-id": "pc.ram" +# }, +# ... +# } ## -{ 'command': 'query-machines', 'returns': ['MachineInfo'] } +{ 'command': 'query-machines', + 'data': { '*compat-props': { 'type': 'bool', + 'features': [ 'unstable' ] } }, + 'returns': ['MachineInfo'] } ## # @CurrentMachineParams: @@ -181,7 +256,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 +306,8 @@ # # Since: 0.14 # -# Notes: If no UUID was specified for the guest, a null UUID is returned. +# .. note:: If no UUID was specified for the guest, the nil UUID (all +# zeroes) is returned. ## { 'struct': 'UuidInfo', 'data': {'UUID': 'str'} } @@ -244,11 +320,10 @@ # # Since: 0.14 # -# Example: -# -# -> { "execute": "query-uuid" } -# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } +# .. qmp-example:: # +# -> { "execute": "query-uuid" } +# <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } ## { 'command': 'query-uuid', 'returns': 'UuidInfo', 'allow-preconfig': true } @@ -279,11 +354,10 @@ # # Since: 0.14 # -# Example: -# -# -> { "execute": "system_reset" } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "system_reset" } +# <- { "return": {} } ## { 'command': 'system_reset' } @@ -294,68 +368,64 @@ # # 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. +# .. note:: 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. # -# Example: -# -# -> { "execute": "system_powerdown" } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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. # -# Note: prior to 4.0, this command does nothing in case the guest -# isn't suspended. -# -# Example: -# -# -> { "execute": "system_wakeup" } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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 +435,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": {} } +# .. qmp-example:: # +# -> { "execute": "inject-nmi" } +# <- { "return": {} } ## { 'command': 'inject-nmi' } @@ -404,11 +473,10 @@ # # Since: 0.14 # -# Example: -# -# -> { "execute": "query-kvm" } -# <- { "return": { "enabled": true, "present": true } } +# .. qmp-example:: # +# -> { "execute": "query-kvm" } +# <- { "return": { "enabled": true, "present": true } } ## { 'command': 'query-kvm', 'returns': 'KvmInfo' } @@ -433,7 +501,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 +520,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 +559,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 +576,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': { @@ -530,7 +601,7 @@ # # @cxl-fmw: List of CXLFixedMemoryWindowOptions # -# Since 7.1 +# Since: 7.1 ## { 'struct' : 'CXLFMWProperties', 'data': { 'cxl-fmw': ['CXLFixedMemoryWindowOptions'] } @@ -551,10 +622,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 # @@ -571,7 +643,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 ## @@ -581,8 +654,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. # @@ -617,11 +690,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) # @@ -644,28 +717,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 ## @@ -687,8 +759,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 # @@ -702,14 +774,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) # @@ -725,8 +797,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. # @@ -735,7 +807,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. # @@ -764,25 +836,26 @@ # @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 # -# Notes: Errors were not reliably returned until 1.1 +# .. caution:: Errors were not reliably returned until 1.1. # -# Example: -# -# -> { "execute": "memsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/virtual-mem-dump" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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'} } + 'data': { + 'val': 'uint64', + 'size': 'size', + 'filename': 'str', + '*cpu-index': 'int' } } ## # @pmemsave: @@ -795,23 +868,23 @@ # # @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 +# .. caution:: Errors were not reliably returned until 1.1. # -# Example: -# -# -> { "execute": "pmemsave", -# "arguments": { "val": 10, -# "size": 100, -# "filename": "/tmp/physical-mem-dump" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "pmemsave", +# "arguments": { "val": 10, +# "size": 100, +# "filename": "/tmp/physical-mem-dump" } } +# <- { "return": {} } ## { 'command': 'pmemsave', - 'data': {'val': 'int', 'size': 'int', 'filename': 'str'} } + 'data': { + 'val': 'uint64', + 'size': 'size', + 'filename': 'str' } } ## # @Memdev: @@ -830,11 +903,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 # @@ -863,62 +936,82 @@ # # Since: 2.1 # -# Example: +# .. qmp-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. +# Properties identifying a CPU. +# +# 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) +# +# @module-id: module number within the parent container the CPU +# belongs to (since 9.1) +# +# @core-id: core number within the parent container the CPU belongs to +# +# @thread-id: thread number within the core the CPU belongs to # # 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', + '*module-id': 'int', '*core-id': 'int', '*thread-id': 'int' } @@ -928,10 +1021,18 @@ # @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. +# +# @props: list of properties to pass for hotplugging a CPU with +# device_add +# +# @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. +# +# .. note:: Management should be prepared to pass through additional +# properties with device_add. # # Since: 2.7 ## @@ -952,49 +1053,56 @@ # # Since: 2.7 # -# Example: +# .. qmp-example:: +# :annotated: # -# 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-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]"} -# ]}' +# -> { "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: +# .. qmp-example:: +# :annotated: # -# -> { "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 pc machine type started with ``-smp 1,maxcpus=2``:: # -# For s390x-virtio-ccw machine type started with -smp 1,maxcpus=2 -cpu qemu -# (Since: 2.11): +# -> { "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": "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 } -# } -# ]} +# .. qmp-example:: +# :annotated: # +# 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 } @@ -1002,9 +1110,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 ## @@ -1018,31 +1125,33 @@ # # 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. +# .. note:: 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: +# .. qmp-example:: +# :annotated: # -# -> { "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'} } @@ -1051,8 +1160,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 ## @@ -1063,58 +1172,123 @@ # # 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 -# } -# } +# .. qmp-example:: # +# -> { "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. +# .. note:: This event is rate-limited. # # Since: 1.2 # -# Example: -# -# <- { "event": "BALLOON_CHANGE", -# "data": { "actual": 944766976 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +# .. qmp-example:: # +# <- { "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 +# +# .. qmp-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 +# +# .. qmp-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 ## @@ -1124,13 +1298,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: +# .. qmp-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 ## @@ -1155,7 +1329,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 ## @@ -1255,17 +1430,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', @@ -1274,6 +1485,8 @@ ## # @VirtioPMEMDeviceInfoWrapper: # +# @data: VirtioPMEM state information +# # Since: 2.1 ## { 'struct': 'VirtioPMEMDeviceInfoWrapper', @@ -1282,6 +1495,8 @@ ## # @VirtioMEMDeviceInfoWrapper: # +# @data: VirtioMEMDevice state information +# # Since: 2.1 ## { 'struct': 'VirtioMEMDeviceInfoWrapper', @@ -1290,18 +1505,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 ## @@ -1312,7 +1538,8 @@ 'nvdimm': 'PCDIMMDeviceInfoWrapper', 'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper', 'virtio-mem': 'VirtioMEMDeviceInfoWrapper', - 'sgx-epc': 'SgxEPCDeviceInfoWrapper' + 'sgx-epc': 'SgxEPCDeviceInfoWrapper', + 'hv-balloon': 'HvBalloonDeviceInfoWrapper' } } @@ -1353,30 +1580,29 @@ # # Since: 2.1 # -# 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" -# } ] } +# .. qmp-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" +# } ] } ## { '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 # @@ -1384,49 +1610,20 @@ # # @qom-path: path to the device object in the QOM tree (since 6.2) # -# Note: this event is rate-limited. +# .. note:: This event is rate-limited. # # Since: 5.1 # -# Example: -# -# <- { "event": "MEMORY_DEVICE_SIZE_CHANGE", -# "data": { "id": "vm0", "size": 1073741824, -# "qom-path": "/machine/unattached/device[2]" }, -# "timestamp": { "seconds": 1588168529, "microseconds": 201316 } } +# .. qmp-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': 'str', 'size': 'size', 'qom-path' : 'str'} } -## -# @MEM_UNPLUG_ERROR: -# -# Emitted when memory hot unplug error occurs. -# -# @device: device name -# -# @msg: Informative message -# -# Features: -# @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': 'str', 'msg': 'str' }, - 'features': ['deprecated'] } - ## # @BootConfiguration: # @@ -1438,13 +1635,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 ## @@ -1460,30 +1659,52 @@ ## # @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) +# +# @modules: number of modules per parent container (since 9.1) +# +# @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', + '*modules': 'int', '*cores': 'int', '*threads': 'int', '*maxcpus': 'int' } } @@ -1494,6 +1715,7 @@ # Query interrupt statistics # # Features: +# # @unstable: This command is meant for debugging. # # Returns: interrupt statistics @@ -1510,6 +1732,7 @@ # Query TCG compiler statistics # # Features: +# # @unstable: This command is meant for debugging. # # Returns: TCG compiler statistics @@ -1527,6 +1750,7 @@ # Query NUMA topology information # # Features: +# # @unstable: This command is meant for debugging. # # Returns: topology information @@ -1543,6 +1767,7 @@ # Query TCG opcode counters # # Features: +# # @unstable: This command is meant for debugging. # # Returns: TCG opcode counters @@ -1554,29 +1779,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 @@ -1587,28 +1796,13 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } -## -# @x-query-rdma: -# -# Query RDMA state -# -# Features: -# @unstable: This command is meant for debugging. -# -# Returns: RDMA state -# -# Since: 6.2 -## -{ 'command': 'x-query-rdma', - 'returns': 'HumanReadableText', - 'features': [ 'unstable' ] } - ## # @x-query-roms: # # Query information on the registered ROMS # # Features: +# # @unstable: This command is meant for debugging. # # Returns: registered ROMs @@ -1625,6 +1819,7 @@ # Query information on the USB devices # # Features: +# # @unstable: This command is meant for debugging. # # Returns: USB device information @@ -1642,10 +1837,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: @@ -1674,11 +1872,29 @@ # # Since: 7.2 # -# Example: -# {"execute": "dumpdtb"} -# "arguments": { "filename": "fdt.dtb" } } +# .. qmp-example:: # +# -> { "execute": "dumpdtb" } +# "arguments": { "filename": "fdt.dtb" } } +# <- { "return": {} } ## { 'command': 'dumpdtb', 'data': { 'filename': 'str' }, 'if': 'CONFIG_FDT' } + +## +# @x-query-interrupt-controllers: +# +# Query information on interrupt controller devices +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: Interrupt controller devices information +# +# Since: 9.1 +## +{ 'command': 'x-query-interrupt-controllers', + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ]} diff --git a/qapi/meson.build b/qapi/meson.build index fbdb442fdf..e7bc54e5d0 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -31,10 +31,13 @@ qapi_all_modules = [ 'compat', 'control', 'crypto', + 'cxl', 'dump', + 'ebpf', 'error', 'introspect', 'job', + 'machine-common', 'machine', 'machine-target', 'migration', @@ -49,6 +52,7 @@ qapi_all_modules = [ 'stats', 'trace', 'transaction', + 'vfio', 'virtio', 'yank', ] @@ -56,9 +60,9 @@ if have_system qapi_all_modules += [ 'acpi', 'audio', + 'cryptodev', 'qdev', 'pci', - 'rdma', 'rocker', 'tpm', ] @@ -139,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 88ecf86ac8..a605dc26db 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -16,61 +16,63 @@ # # @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) -# # @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) # -# @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) # 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', - 'dirty-sync-missed-zero-copy' : 'uint64' } } + 'duplicate': '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', + 'dirty-sync-missed-zero-copy': 'uint64' } } ## # @XBZRLECacheStats: @@ -105,7 +107,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 # @@ -134,32 +137,39 @@ # # @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-setup: setup phase for a postcopy recovery +# process, preparing for a recovery phase to start. (since 9.1) +# +# @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 ## { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', 'active', 'postcopy-active', 'postcopy-paused', + 'postcopy-recover-setup', 'postcopy-recover', 'completed', 'failed', 'colo', 'pre-switchover', 'device', 'wait-unplug' ] } ## @@ -167,7 +177,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 ## @@ -180,73 +191,76 @@ # 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) -# -# @disk: @MigrationStats containing detailed disk migration -# status, only returned if status is 'active' and it is a block -# migration +# @ram: @MigrationStats containing detailed migration status, only +# returned if status is 'active' or 'completed'(since 1.2) # # @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) +# @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) # # Since: 0.14 ## { 'struct': 'MigrationInfo', 'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats', - '*disk': 'MigrationStats', '*vfio': 'VfioStats', '*xbzrle-cache': 'XBZRLECacheStats', '*total-time': 'int', @@ -256,132 +270,108 @@ '*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'] } } + '*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. +# status. # # Returns: @MigrationInfo # # Since: 0.14 # -# Example: +# .. qmp-example:: +# :title: Before the first migration # -# 1. Before the first migration +# -> { "execute": "query-migrate" } +# <- { "return": {} } # -# -> { "execute": "query-migrate" } -# <- { "return": {} } +# .. qmp-example:: +# :title: 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: +# .. qmp-example:: +# :title: 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: +# .. qmp-example:: +# :title: Migration is being performed # -# -> { "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 +# } +# } +# } # +# .. qmp-example:: +# :title: 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' } @@ -390,109 +380,122 @@ # # 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 ) +# @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) -# -# @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) +# @release-ram: if enabled, qemu will free the migrated ram pages on +# the source during postcopy-ram migration. (since 2.9) # # @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) +# @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: +# # @unstable: Members @x-colo and @x-ignore-shared are experimental. +# @deprecated: Member @zero-blocks is deprecated as being part of +# block migration which was already removed. # # Since: 1.2 ## { 'enum': 'MigrationCapability', - 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', - 'compress', 'events', 'postcopy-ram', + 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', + { 'name': 'zero-blocks', 'features': [ 'deprecated' ] }, + 'events', 'postcopy-ram', { 'name': 'x-colo', 'features': [ 'unstable' ] }, 'release-ram', - 'block', 'return-path', 'pause-before-switchover', 'multifd', + 'return-path', 'pause-before-switchover', 'multifd', 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', { 'name': 'x-ignore-shared', 'features': [ 'unstable' ] }, 'validate-uuid', 'background-snapshot', - 'zero-copy-send', 'postcopy-preempt'] } + 'zero-copy-send', 'postcopy-preempt', 'switchover-ack', + 'dirty-limit', 'mapped-ram'] } ## # @MigrationCapabilityStatus: @@ -506,7 +509,7 @@ # Since: 1.2 ## { 'struct': 'MigrationCapabilityStatus', - 'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } } + 'data': { 'capability': 'MigrationCapability', 'state': 'bool' } } ## # @migrate-set-capabilities: @@ -517,11 +520,11 @@ # # Since: 1.2 # -# Example: -# -# -> { "execute": "migrate-set-capabilities" , "arguments": -# { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } +# .. qmp-example:: # +# -> { "execute": "migrate-set-capabilities" , "arguments": +# { "capabilities": [ { "capability": "xbzrle", "state": true } ] } } +# <- { "return": {} } ## { 'command': 'migrate-set-capabilities', 'data': { 'capabilities': ['MigrationCapabilityStatus'] } } @@ -531,24 +534,22 @@ # # 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"} -# ]} +# .. qmp-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": true, "capability": "events"}, +# {"state": false, "capability": "postcopy-ram"}, +# {"state": false, "capability": "x-colo"} +# ]} ## { 'command': 'query-migrate-capabilities', 'returns': ['MigrationCapabilityStatus']} @@ -558,20 +559,86 @@ # An enumeration of multifd compression methods. # # @none: no compression. +# # @zlib: use zlib compression method. +# # @zstd: use zstd compression method. # +# @qatzip: use qatzip compression method. (Since 9.2) +# +# @qpl: use qpl compression method. Query Processing Library(qpl) is +# based on the deflate compression algorithm and use the Intel +# In-Memory Analytics Accelerator(IAA) accelerated compression and +# decompression. (Since 9.1) +# +# @uadk: use UADK library compression method. (Since 9.1) +# # Since: 5.0 ## { 'enum': 'MultiFDCompression', + 'prefix': 'MULTIFD_COMPRESSION', 'data': [ 'none', 'zlib', - { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } + { 'name': 'zstd', 'if': 'CONFIG_ZSTD' }, + { 'name': 'qatzip', 'if': 'CONFIG_QATZIP'}, + { 'name': 'qpl', 'if': 'CONFIG_QPL' }, + { 'name': 'uadk', 'if': 'CONFIG_UADK' } ] } + +## +# @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 ## @@ -586,10 +653,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 ## @@ -608,8 +675,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. # @@ -627,336 +694,352 @@ # # 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. +# @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) # -# @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) -# -# @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) -# -# @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) # -# @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) +# @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) # # @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-qatzip-level: Set the compression level to be used in live +# migration. The level is an integer between 1 and 9, where 1 means +# the best compression speed, and 9 means the best compression +# ratio which will consume more CPU. Defaults to 1. (Since 9.2) # # @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) -# +# 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) +# +# @direct-io: Open migration files with O_DIRECT when possible. This +# only has effect if the @mapped-ram capability is enabled. +# (Since 9.1) # # Features: -# @unstable: Member @x-checkpoint-delay is experimental. +# +# @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', + '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', 'multifd-channels', 'xbzrle-cache-size', 'max-postcopy-bandwidth', 'max-cpu-throttle', 'multifd-compression', - 'multifd-zlib-level' ,'multifd-zstd-level', - 'block-bitmap-mapping' ] } + 'multifd-zlib-level', 'multifd-zstd-level', + 'multifd-qatzip-level', + 'block-bitmap-mapping', + { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, + 'vcpu-dirty-limit', + 'mode', + 'zero-page-detection', + 'direct-io'] } ## # @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-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) -# -# @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. -# 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) # -# @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) +# @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) # # @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-qatzip-level: Set the compression level to be used in live +# migration. The level is an integer between 1 and 9, where 1 means +# the best compression speed, and 9 means the best compression +# ratio which will consume more CPU. Defaults to 1. (Since 9.2) # # @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) +# 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) +# +# @direct-io: Open migration files with O_DIRECT when possible. This +# only has effect if the @mapped-ram capability is enabled. +# (Since 9.1) # # Features: -# @unstable: Member @x-checkpoint-delay is experimental. +# +# @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', '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -965,18 +1048,25 @@ '*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', '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', '*max-cpu-throttle': 'uint8', '*multifd-compression': 'MultiFDCompression', '*multifd-zlib-level': 'uint8', + '*multifd-qatzip-level': 'uint8', '*multifd-zstd-level': 'uint8', - '*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', + '*direct-io': 'bool' } } ## # @migrate-set-parameters: @@ -985,11 +1075,11 @@ # # Since: 2.4 # -# Example: -# -# -> { "execute": "migrate-set-parameters" , -# "arguments": { "compress-level": 1 } } +# .. qmp-example:: # +# -> { "execute": "migrate-set-parameters" , +# "arguments": { "multifd-channels": 5 } } +# <- { "return": {} } ## { 'command': 'migrate-set-parameters', 'boxed': true, 'data': 'MigrateSetParameters' } @@ -999,150 +1089,156 @@ # # 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) -# -# @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) # -# @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) +# @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) # # @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-qatzip-level: Set the compression level to be used in live +# migration. The level is an integer between 1 and 9, where 1 means +# the best compression speed, and 9 means the best compression +# ratio which will consume more CPU. Defaults to 1. (Since 9.2) # # @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) +# 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) +# +# @direct-io: Open migration files with O_DIRECT when possible. This +# only has effect if the @mapped-ram capability is enabled. +# (Since 9.1) # # Features: -# @unstable: Member @x-checkpoint-delay is experimental. +# +# @unstable: Members @x-checkpoint-delay and +# @x-vcpu-dirty-limit-period are experimental. # # Since: 2.4 ## @@ -1151,10 +1247,6 @@ '*announce-max': 'size', '*announce-rounds': 'size', '*announce-step': 'size', - '*compress-level': 'uint8', - '*compress-threads': 'uint8', - '*compress-wait-thread': 'bool', - '*decompress-threads': 'uint8', '*throttle-trigger-threshold': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', @@ -1163,18 +1255,25 @@ '*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', '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', '*max-postcopy-bandwidth': 'size', '*max-cpu-throttle': 'uint8', '*multifd-compression': 'MultiFDCompression', '*multifd-zlib-level': 'uint8', + '*multifd-qatzip-level': 'uint8', '*multifd-zstd-level': 'uint8', - '*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', + '*direct-io': 'bool' } } ## # @query-migrate-parameters: @@ -1185,66 +1284,34 @@ # # Since: 2.4 # -# 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 -# } -# } +# .. qmp-example:: # +# -> { "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": {} } +# .. qmp-example:: # +# -> { "execute": "migrate-start-postcopy" } +# <- { "return": {} } ## { 'command': 'migrate-start-postcopy' } @@ -1257,12 +1324,11 @@ # # Since: 2.4 # -# Example: -# -# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, -# "event": "MIGRATION", -# "data": {"status": "completed"} } +# .. qmp-example:: # +# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001}, +# "event": "MIGRATION", +# "data": {"status": "completed"} } ## { 'event': 'MIGRATION', 'data': {'status': 'MigrationStatus'}} @@ -1270,18 +1336,17 @@ ## # @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) # # Since: 2.6 # -# Example: -# -# { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, -# "event": "MIGRATION_PASS", "data": {"pass": 2} } +# .. qmp-example:: # +# <- { "timestamp": {"seconds": 1449669631, "microseconds": 239225}, +# "event": "MIGRATION_PASS", "data": {"pass": 2} } ## { 'event': 'MIGRATION_PASS', 'data': { 'pass': 'int' } } @@ -1293,7 +1358,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 # @@ -1341,7 +1407,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 ## @@ -1360,11 +1427,10 @@ # # Since: 3.1 # -# Example: -# -# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, -# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } +# .. qmp-example:: # +# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, +# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } ## { 'event': 'COLO_EXIT', 'data': {'mode': 'COLOMode', 'reason': 'COLOExitReason' } } @@ -1374,9 +1440,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. # @@ -1392,41 +1458,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": {} } +# .. qmp-example:: # +# -> { "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. +# .. note:: This command succeeds even if there is no migration +# process running. # # Since: 0.14 # -# Example: -# -# -> { "execute": "migrate_cancel" } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "migrate_cancel" } +# <- { "return": {} } ## { 'command': 'migrate_cancel' } @@ -1437,18 +1503,103 @@ # # @state: The state the migration is currently expected to be in # -# Returns: nothing on success -# # Since: 2.11 # -# Example: +# .. qmp-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: # @@ -1456,95 +1607,172 @@ # # @uri: the Uniform Resource Identifier of the destination VM # -# @blk: do block migration (full disk copy) +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. # -# @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) -# -# Returns: nothing on success +# @resume: resume one paused migration, default "off". (since 3.0) # # Since: 0.14 # -# Notes: +# .. admonition:: 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. # -# Example: +# 4. The uri argument should have the Uniform Resource Identifier +# of default destination VM. This connection will be bound to +# default network. # -# -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } -# <- { "return": {} } +# 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. +# +# .. qmp-example:: +# +# -> { "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' ], '*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. +# +# @exit-on-error: Exit on incoming migration failure. Default true. +# When set to false, the failure triggers a MIGRATION event, and +# error details could be retrieved with query-migrate. +# (since 9.1) # # Since: 2.3 # -# Notes: +# .. admonition:: 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 # -# Example: +# 4. For now, number of migration streams is restricted to one, +# i.e. number of items in 'channels' list is just 1. # -# -> { "execute": "migrate-incoming", -# "arguments": { "uri": "tcp::4446" } } -# <- { "return": {} } +# 5. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. # +# .. qmp-example:: +# +# -> { "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' ], + '*exit-on-error': 'bool' } } ## # @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": {} } +# .. qmp-example:: # +# -> { "execute": "xen-save-devices-state", +# "arguments": { "filename": "/tmp/save" } } +# <- { "return": {} } ## { 'command': 'xen-save-devices-state', 'data': {'filename': 'str', '*live':'bool' } } @@ -1556,37 +1784,33 @@ # # @enable: true to enable, false to disable. # -# Returns: nothing -# # Since: 1.3 # -# Example: -# -# -> { "execute": "xen-set-global-dirty-log", -# "arguments": { "enable": true } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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": {} } +# .. qmp-example:: # +# -> { "execute": "xen-load-devices-state", +# "arguments": { "filename": "/tmp/resume" } } +# <- { "return": {} } ## { 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} } @@ -1599,21 +1823,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. +# @failover: true to do failover, false to stop. Cannot be specified +# if 'enable' is true. Default value is false. # -# Returns: nothing. +# .. qmp-example:: # -# 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' } ## @@ -1623,8 +1845,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 ## @@ -1639,10 +1861,10 @@ # # Returns: A @ReplicationStatus object showing the status. # -# Example: +# .. qmp-example:: # -# -> { "execute": "query-xen-replication-status" } -# <- { "return": { "error": false } } +# -> { "execute": "query-xen-replication-status" } +# <- { "return": { "error": false } } # # Since: 2.9 ## @@ -1655,12 +1877,10 @@ # # Xen uses this command to notify replication to trigger a checkpoint. # -# Returns: nothing. +# .. qmp-example:: # -# Example: -# -# -> { "execute": "xen-colo-do-checkpoint" } -# <- { "return": {} } +# -> { "execute": "xen-colo-do-checkpoint" } +# <- { "return": {} } # # Since: 2.9 ## @@ -1672,12 +1892,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. # @@ -1685,7 +1905,8 @@ ## { 'struct': 'COLOStatus', 'data': { 'mode': 'COLOMode', 'last-mode': 'COLOMode', - 'reason': 'COLOExitReason' } } + 'reason': 'COLOExitReason' }, + 'if': 'CONFIG_REPLICATION' } ## # @query-colo-status: @@ -1694,15 +1915,16 @@ # # Returns: A @COLOStatus object showing the status. # -# Example: +# .. qmp-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: @@ -1711,13 +1933,11 @@ # # @uri: the URI to be used for the recovery of migration stream. # -# Returns: nothing. +# .. qmp-example:: # -# 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 ## @@ -1730,12 +1950,10 @@ # # Pause a migration. Currently it only supports postcopy. # -# Returns: nothing. +# .. qmp-example:: # -# Example: -# -# -> { "execute": "migrate-pause" } -# <- { "return": {} } +# -> { "execute": "migrate-pause" } +# <- { "return": {} } # # Since: 3.0 ## @@ -1745,20 +1963,19 @@ # @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 # # Since: 4.2 # -# Example: -# -# <- { "event": "UNPLUG_PRIMARY", -# "data": { "device-id": "hostdev0" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# .. qmp-example:: # +# <- { "event": "UNPLUG_PRIMARY", +# "data": { "device-id": "hostdev0" }, +# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } ## { 'event': 'UNPLUG_PRIMARY', 'data': { 'device-id': 'str' } } @@ -1780,13 +1997,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 ## @@ -1796,42 +2013,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 ## @@ -1840,6 +2073,7 @@ 'status': 'DirtyRateStatus', 'start-time': 'int64', 'calc-time': 'int64', + 'calc-time-unit': 'TimeUnit', 'sample-pages': 'uint64', 'mode': 'DirtyRateMeasureMode', '*vcpu-dirty-rate': [ 'DirtyRateVcpu' ] } } @@ -1847,36 +2081,103 @@ ## # @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: +# .. qmp-example:: # -# {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, -# 'sample-pages': 512} } +# -> {"execute": "calc-dirty-rate", "arguments": {"calc-time": 1, +# "sample-pages": 512} } +# <- { "return": {} } # +# .. qmp-example:: +# :annotated: +# +# 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 +# +# .. qmp-example:: +# :title: Measurement is in progress +# +# <- {"status": "measuring", "sample-pages": 512, +# "mode": "page-sampling", "start-time": 1693900454, "calc-time": 10, +# "calc-time-unit": "second"} +# +# .. qmp-example:: +# :title: 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: @@ -1886,12 +2187,11 @@ # @cpu-index: index of a virtual CPU. # # @limit-rate: upper limit of dirty page rate (MB/s) for a virtual -# CPU, 0 means unlimited. +# 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', @@ -1903,9 +2203,9 @@ # # 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. +# 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. # @@ -1913,11 +2213,12 @@ # # Since: 7.1 # -# Example: -# {"execute": "set-vcpu-dirty-limit"} -# "arguments": { "dirty-rate": 200, -# "cpu-index": 1 } } +# .. qmp-example:: # +# -> {"execute": "set-vcpu-dirty-limit"} +# "arguments": { "dirty-rate": 200, +# "cpu-index": 1 } } +# <- { "return": {} } ## { 'command': 'set-vcpu-dirty-limit', 'data': { '*cpu-index': 'int', @@ -1929,17 +2230,18 @@ # 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 +# 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 } } +# .. qmp-example:: # +# -> {"execute": "cancel-vcpu-dirty-limit"}, +# "arguments": { "cpu-index": 1 } } +# <- { "return": {} } ## { 'command': 'cancel-vcpu-dirty-limit', 'data': { '*cpu-index': 'int'} } @@ -1947,78 +2249,116 @@ ## # @query-vcpu-dirty-limit: # -# Returns information about virtual CPU dirty page rate limits, if any. +# Returns information about virtual CPU dirty page rate limits, if +# any. # # Since: 7.1 # -# Example: -# {"execute": "query-vcpu-dirty-limit"} +# .. qmp-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 +# +# Features: +# +# @deprecated: This command is deprecated with no replacement yet. +# +# Returns: @MigrationThreadInfo +# +# Since: 7.2 +## +{ 'command': 'query-migrationthreads', + 'returns': ['MigrationThreadInfo'], + 'features': ['deprecated'] } + ## # @snapshot-save: # # 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 +# .. qmp-example:: # -# Example: -# -# -> { "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"}]} +# -> { "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 ## @@ -2034,59 +2374,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. +# 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 +# .. qmp-example:: # -# Example: -# -# -> { "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"}]} +# -> { "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 ## @@ -2102,46 +2444,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. +# 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 +# .. qmp-example:: # -# Example: -# -# -> { "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"}]} +# -> { "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..8d70bd24d8 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": {} } +# .. qmp-example:: # +# -> { "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 ## @@ -47,6 +47,50 @@ 'send-update', 'receive-update' ], 'if': 'TARGET_I386' } +## +# @SevGuestType: +# +# An enumeration indicating the type of SEV guest being run. +# +# @sev: The guest is a legacy SEV or SEV-ES guest. +# +# @sev-snp: The guest is an SEV-SNP guest. +# +# Since: 6.2 +## +{ 'enum': 'SevGuestType', + 'data': [ 'sev', 'sev-snp' ], + 'if': 'TARGET_I386' } + +## +# @SevGuestInfo: +# +# Information specific to legacy SEV/SEV-ES guests. +# +# @policy: SEV policy value +# +# @handle: SEV firmware handle +# +# Since: 2.12 +## +{ 'struct': 'SevGuestInfo', + 'data': { 'policy': 'uint32', + 'handle': 'uint32' }, + 'if': 'TARGET_I386' } + +## +# @SevSnpGuestInfo: +# +# Information specific to SEV-SNP guests. +# +# @snp-policy: SEV-SNP policy value +# +# Since: 9.1 +## +{ 'struct': 'SevSnpGuestInfo', + 'data': { 'snp-policy': 'uint64' }, + 'if': 'TARGET_I386' } + ## # @SevInfo: # @@ -60,25 +104,25 @@ # # @build-id: SEV FW build id # -# @policy: SEV policy value -# # @state: SEV guest state # -# @handle: SEV firmware handle +# @sev-type: Type of SEV guest being run # # Since: 2.12 ## -{ 'struct': 'SevInfo', - 'data': { 'enabled': 'bool', - 'api-major': 'uint8', - 'api-minor' : 'uint8', - 'build-id' : 'uint8', - 'policy' : 'uint32', - 'state' : 'SevState', - 'handle' : 'uint32' - }, - 'if': 'TARGET_I386' -} +{ 'union': 'SevInfo', + 'base': { 'enabled': 'bool', + 'api-major': 'uint8', + 'api-minor' : 'uint8', + 'build-id' : 'uint8', + 'state' : 'SevState', + 'sev-type' : 'SevGuestType' }, + 'discriminator': 'sev-type', + 'data': { + 'sev': 'SevGuestInfo', + 'sev-snp': 'SevSnpGuestInfo' }, + 'if': 'TARGET_I386' } + ## # @query-sev: @@ -89,13 +133,12 @@ # # Since: 2.12 # -# Example: -# -# -> { "execute": "query-sev" } -# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, -# "build-id" : 0, "policy" : 0, "state" : "running", -# "handle" : 1 } } +# .. qmp-example:: # +# -> { "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' } @@ -121,11 +164,10 @@ # # Since: 2.12 # -# Example: -# -# -> { "execute": "query-sev-launch-measure" } -# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } +# .. qmp-example:: # +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } ## { 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo', 'if': 'TARGET_I386' } @@ -133,8 +175,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 +186,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,20 +202,19 @@ ## # @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. # # Since: 2.12 # -# Example: -# -# -> { "execute": "query-sev-capabilities" } -# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", -# "cpu0-id": "2lvmGwo+...61iEinw==", -# "cbitpos": 47, "reduced-phys-bits": 5}} +# .. qmp-example:: # +# -> { "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,18 +257,17 @@ # 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. # # Since: 6.1 # -# Example: -# -# -> { "execute" : "query-sev-attestation-report", -# "arguments": { "mnonce": "aaaaaaa" } } -# <- { "return" : { "data": "aaaaaaaabbbddddd"} } +# .. qmp-example:: # +# -> { "execute" : "query-sev-attestation-report", +# "arguments": { "mnonce": "aaaaaaa" } } +# <- { "return" : { "data": "aaaaaaaabbbddddd"} } ## { 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, @@ -241,16 +281,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": {} } +# .. qmp-example:: # +# -> { "execute": "dump-skeys", +# "arguments": { "filename": "/tmp/skeys" } } +# <- { "return": {} } ## { 'command': 'dump-skeys', 'data': { 'filename': 'str' }, @@ -260,18 +297,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,19 +321,18 @@ ## # @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. # # Since: 2.6 # -# Example: -# -# -> { "execute": "query-gic-capabilities" } -# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, -# { "version": 3, "emulated": false, "kernel": true } ] } +# .. qmp-example:: # +# -> { "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 +365,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 +374,6 @@ 'sgx1': 'bool', 'sgx2': 'bool', 'flc': 'bool', - 'section-size': { 'type': 'uint64', - 'features': [ 'deprecated' ] }, 'sections': ['SGXEPCSection']}, 'if': 'TARGET_I386' } @@ -358,14 +386,13 @@ # # Since: 6.2 # -# 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}]} } +# .. qmp-example:: # +# -> { "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' } @@ -378,13 +405,124 @@ # # Since: 6.2 # -# 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}]} } +# .. qmp-example:: # +# -> { "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 +# +# .. qmp-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 +# +# .. qmp-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 27ef5a2b20..559b66f201 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": {} } +# .. qmp-example:: # +# -> { "execute": "add_client", "arguments": { "protocol": "vnc", +# "fdname": "myclient" } } +# <- { "return": {} } ## { 'command': 'add_client', 'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool', @@ -60,11 +60,10 @@ # # Since: 0.14 # -# Example: -# -# -> { "execute": "query-name" } -# <- { "return": { "name": "qemu-name" } } +# .. qmp-example:: # +# -> { "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,29 +103,28 @@ # # 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 # # Since: 2.0 # -# Example: -# -# -> { "execute": "query-iothreads" } -# <- { "return": [ -# { -# "id":"iothread0", -# "thread-id":3134 -# }, -# { -# "id":"iothread1", -# "thread-id":3135 -# } -# ] -# } +# .. qmp-example:: # +# -> { "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. +# .. note:: This function will succeed even if the guest is already in +# the stopped state. In "inmigrate" state, it will ensure that the +# guest remains paused once migration finishes, as if the ``-S`` +# option was passed on the command line. # -# Example: +# In the "suspended" state, it will completely stop the VM and +# cause a transition to the "paused" state. (Since 9.0) # -# -> { "execute": "stop" } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "stop" } +# <- { "return": {} } ## { 'command': 'stop' } ## # @cont: # -# Resume guest VCPU execution. +# Resume guest VM execution. # # Since: 0.14 # -# Returns: If successful, nothing +# .. note:: 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": {} } +# .. qmp-example:: # +# -> { "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": {} } +# .. qmp-example:: # +# -> { "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. +# .. note:: 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. # -# 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" } +# .. qmp-example:: # +# -> { "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,48 @@ # # @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. +# .. note:: 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": {} } +# .. qmp-example:: # +# -> { "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 +# +# .. note:: 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. +# +# .. qmp-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 +300,12 @@ # # @fdname: file descriptor name # -# Returns: Nothing on success -# # Since: 0.14 # -# Example: -# -# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "closefd", "arguments": { "fdname": "fd1" } } +# <- { "return": {} } ## { 'command': 'closefd', 'data': {'fdname': 'str'} } @@ -298,8 +316,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 +332,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. +# .. note:: The list of fd sets is shared by all monitor connections. +# +# .. note:: 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 } } +# .. qmp-example:: # +# -> { "execute": "add-fd", "arguments": { "fdset-id": 1 } } +# <- { "return": { "fdset-id": 1, "fd": 3 } } ## { 'command': 'add-fd', 'data': { '*fdset-id': 'int', @@ -344,21 +365,20 @@ # # @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. +# .. note:: 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. +# .. note:: 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": {} } +# .. qmp-example:: # +# -> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } } +# <- { "return": {} } ## { 'command': 'remove-fd', 'data': {'fdset-id': 'int', '*fd': 'int'} } @@ -399,39 +419,38 @@ # # Since: 1.2 # -# Note: The list of fd sets is shared by all monitor connections. +# .. note:: The list of fd sets is shared by all monitor connections. # -# Example: +# .. qmp-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,61 @@ # # @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" -# } -# ] -# } +# .. qmp-example:: # +# -> { "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 } } +# .. qmp-example:: # +# <- { "event": "RTC_CHANGE", +# "data": { "offset": 78 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } ## { 'event': 'RTC_CHANGE', 'data': { 'offset': 'int', 'qom-path': 'str' } } @@ -560,10 +581,11 @@ # 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-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 +# @vfu-qom-path: path to the TYPE_VFIO_USER_SERVER object in the QOM +# tree # # @dev-id: ID of attached PCI device # @@ -571,15 +593,14 @@ # # 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 } } +# .. qmp-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', diff --git a/qapi/net.json b/qapi/net.json index 53acec579e..878706daa8 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -6,7 +6,6 @@ # = Net devices ## -{ 'include': 'common.json' } { 'include': 'sockets.json' } ## @@ -18,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. +# .. note:: 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": {} } +# .. qmp-example:: # +# -> { "execute": "set_link", +# "arguments": { "name": "e1000.0", "up": false } } +# <- { "return": {} } ## { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } @@ -45,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": {} } +# .. qmp-example:: # +# -> { "execute": "netdev_add", +# "arguments": { "type": "user", "id": "netdev1", +# "dnssearch": [ { "str": "example.org" } ] } } +# <- { "return": {} } ## { 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true, 'allow-preconfig': true } @@ -66,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": {} } +# .. qmp-example:: # +# -> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } +# <- { "return": {} } ## { 'command': 'netdev_del', 'data': {'id': 'str'}, 'allow-preconfig': true } @@ -105,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 # @@ -135,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 # @@ -205,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 # @@ -215,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 # @@ -224,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 ## @@ -303,9 +308,8 @@ # # @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 # @@ -313,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 ## @@ -382,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 ## @@ -396,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'). # @@ -412,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: # @@ -422,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 ## @@ -437,21 +500,21 @@ # # 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) +# @x-svq: Start device with (experimental) shadow virtqueue. (Since +# 7.1) (default: false) # # Features: +# # @unstable: Member @x-svq is experimental. # # Since: 5.1 @@ -472,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 ## @@ -515,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 ## @@ -564,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 ## @@ -582,18 +641,35 @@ # # Configuration info for stream socket netdev # -# @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) +# # @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) +# +# @reconnect-ms: For a client socket, if a socket is disconnected, then +# attempt a reconnect after the given number of milliseconds. Setting +# this to zero disables this function. This member is mutually +# exclusive with @reconnect. (default: 0) (Since: 9.2) +# # Only SocketAddress types 'unix', 'inet' and 'fd' are supported. # +# Features: +# +# @deprecated: Member @reconnect is deprecated. Use @reconnect-ms +# instead. +# # Since: 7.2 ## { 'struct': 'NetdevStreamOptions', 'data': { 'addr': 'SocketAddress', - '*server': 'bool' } } + '*server': 'bool', + '*reconnect': { 'type': 'int', 'features': [ 'deprecated' ] }, + '*reconnect-ms': 'int' } } ## # @NetdevDgramOptions: @@ -601,13 +677,14 @@ # 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. +# 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 @@ -649,19 +726,29 @@ # # 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 +# @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 +# +# Since: 2.7 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'stream', 'dgram', 'vde', 'bridge', 'hubport', 'netmap', 'vhost-user', 'vhost-vdpa', 'pcap', + { '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' }] } @@ -676,13 +763,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 -# 'stream' since 7.2 -# 'dgram' since 7.2 ## { 'union': 'Netdev', 'base': { 'id': 'str', 'type': 'NetClientDriver' }, @@ -699,6 +779,8 @@ 'bridge': 'NetdevBridgeOptions', 'hubport': 'NetdevHubPortOptions', 'netmap': 'NetdevNetmapOptions', + 'af-xdp': { 'type': 'NetdevAFXDPOptions', + 'if': 'CONFIG_AF_XDP' }, 'vhost-user': 'NetdevVhostUserOptions', 'vhost-vdpa': 'NetdevVhostVDPAOptions', 'pcap': 'NetdevPcapOptions', @@ -778,41 +860,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 -# } -# ] -# } +# .. qmp-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 +# } +# ] +# } ## { 'command': 'query-rx-filter', 'data': { '*name': 'str' }, @@ -821,8 +904,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 # @@ -830,13 +913,12 @@ # # Since: 1.6 # -# Example: -# -# <- { "event": "NIC_RX_FILTER_CHANGED", -# "data": { "name": "vnet0", -# "path": "/machine/peripheral/vnet0/virtio-backend" }, -# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } +# .. qmp-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': 'str', 'path': 'str' } } @@ -847,7 +929,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 # @@ -855,12 +937,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 ## @@ -876,16 +958,19 @@ ## # @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: +# TODO: This line is a hack to separate the example from the body # -# -> { "execute": "announce-self", -# "arguments": { -# "initial": 50, "max": 550, "rounds": 10, "step": 50, -# "interfaces": ["vn2", "vn3"], "id": "bob" } } -# <- { "return": {} } +# .. qmp-example:: +# +# -> { "execute": "announce-self", +# "arguments": { +# "initial": 50, "max": 550, "rounds": 10, "step": 50, +# "interfaces": ["vn2", "vn3"], "id": "bob" } } +# <- { "return": {} } # # Since: 4.0 ## @@ -895,20 +980,20 @@ ## # @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 # # Since: 4.2 # -# Example: -# -# <- { "event": "FAILOVER_NEGOTIATED", -# "data": { "device-id": "net1" }, -# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } +# .. qmp-example:: # +# <- { "event": "FAILOVER_NEGOTIATED", +# "data": { "device-id": "net1" }, +# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } } ## { 'event': 'FAILOVER_NEGOTIATED', 'data': {'device-id': 'str'} } @@ -919,25 +1004,25 @@ # Emitted when the netdev stream backend is connected # # @netdev-id: QEMU netdev id that is connected +# # @addr: The destination address # # Since: 7.2 # -# Example: +# .. qmp-example:: # -# <- { "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": { "port": "47666", "ipv6": true, +# "host": "::1", "type": "inet" } }, +# "timestamp": { "seconds": 1666269863, "microseconds": 311222 } } # -# or -# -# <- { "event": "NETDEV_STREAM_CONNECTED", -# "data": { "netdev-id": "netdev0", -# "addr": { "path": "/tmp/qemu0", "type": "unix" } }, -# "timestamp": { "seconds": 1666269706, "microseconds": 413651 } } +# .. qmp-example:: # +# <- { "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', @@ -952,12 +1037,11 @@ # # Since: 7.2 # -# Example: -# -# <- { 'event': 'NETDEV_STREAM_DISCONNECTED', -# 'data': {'netdev-id': 'netdev0'}, -# 'timestamp': {'seconds': 1663330937, 'microseconds': 526695} } +# .. qmp-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..3d1a28b419 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -184,7 +184,7 @@ opts_check_struct(Visitor *v, Error **errp) const QemuOpt *first; first = g_queue_peek_head(any); - error_setg(errp, QERR_INVALID_PARAMETER, first->name); + error_setg(errp, "Invalid parameter '%s'", first->name); return false; } return true; @@ -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..dc85a41d28 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -29,8 +29,11 @@ # # @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 +# +# @address: memory address # # @size: memory size # @@ -49,21 +52,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 ## @@ -92,7 +95,8 @@ # # Information about the Class of a PCI device # -# @desc: a string description of the device's class +# @desc: a string description of the device's class (not stable, and +# should only be treated as informational) # # @class: the class code of the device # @@ -145,9 +149,6 @@ # # @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. -# # Since: 0.14 ## { 'struct': 'PciDeviceInfo', @@ -174,143 +175,142 @@ # # 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: +# .. qmp-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" +# } +# ] +# } +# ] +# } +# ] +# } # +# 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..023a2ef7bc 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,52 @@ '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', + 'DisplayProtocol', + 'DriveBackupWrapper', + 'DummyBlockCoreForceArrays', + 'DummyForceArrays', + 'DummyVirtioForceArrays', + 'HotKeyMod', + 'ImageInfoSpecificKind', + 'InputAxis', + 'InputButton', + 'IscsiHeaderDigest', + 'IscsiTransport', + 'KeyValueKind', + 'MemoryDeviceInfoKind', + 'NetClientDriver', + 'ObjectType', + 'QKeyCode', + 'RbdAuthMode', + 'RbdImageEncryptionFormat', + 'S390CpuEntitlement', + 'S390CpuPolarization', + 'S390CpuState', + 'String', + 'StringWrapper', + 'SysEmuTarget', + 'ThrottleGroupProperties', + 'VncPrimaryAuth', + 'VncVencryptSubAuth', + 'X86CPURegister32', + 'XDbgBlockGraph', + 'YankInstanceType', + 'blockdev-reopen' ], # Externally visible types whose member names may use uppercase 'member-name-exceptions': [ # visible in: 'ACPISlotType', # query-acpi-ospm-status diff --git a/qapi/qapi-clone-visitor.c b/qapi/qapi-clone-visitor.c index c45c5caa3b..bbf953698f 100644 --- a/qapi/qapi-clone-visitor.c +++ b/qapi/qapi-clone-visitor.c @@ -149,7 +149,7 @@ static void qapi_clone_free(Visitor *v) g_free(v); } -static Visitor *qapi_clone_visitor_new(void) +Visitor *qapi_clone_visitor_new(void) { QapiCloneVisitor *v; @@ -174,31 +174,9 @@ static Visitor *qapi_clone_visitor_new(void) return &v->visitor; } -void *qapi_clone(const void *src, bool (*visit_type)(Visitor *, const char *, - void **, Error **)) +Visitor *qapi_clone_members_visitor_new(void) { - Visitor *v; - void *dst = (void *) src; /* Cast away const */ - - if (!src) { - return NULL; - } - - v = qapi_clone_visitor_new(); - visit_type(v, NULL, &dst, &error_abort); - visit_free(v); - return dst; -} - -void qapi_clone_members(void *dst, const void *src, size_t sz, - bool (*visit_type_members)(Visitor *, void *, - Error **)) -{ - Visitor *v; - - v = qapi_clone_visitor_new(); - memcpy(dst, src, sz); + Visitor *v = qapi_clone_visitor_new(); to_qcv(v)->depth++; - visit_type_members(v, dst, &error_abort); - visit_free(v); + return v; } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index f000b90744..b1581988e4 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,13 +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': 'rdma.json' } +{ 'include': 'ebpf.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } { 'include': 'ui.json' } @@ -84,6 +66,7 @@ { 'include': 'introspect.json' } { 'include': 'qom.json' } { 'include': 'qdev.json' } +{ 'include': 'machine-common.json' } { 'include': 'machine.json' } { 'include': 'machine-target.json' } { 'include': 'replay.json' } @@ -95,3 +78,6 @@ { 'include': 'pci.json' } { 'include': 'stats.json' } { 'include': 'virtio.json' } +{ 'include': 'vfio.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 2708fb4e99..25cbcf977b 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 # -# Notes: +# @json-cli: If present, the "-device" command line option supports +# JSON syntax with a structure identical to the arguments of this +# command. # -# Additional arguments depend on the type. +# @json-cli-hotplug: If present, the "-device" command line option +# supports JSON syntax without the reference counting leak that +# broke hot-unplug # -# 1. For detailed information about this command, please refer to the -# 'docs/qdev-device-use.txt' file. +# .. admonition:: Notes # -# 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 +# 1. Additional arguments depend on the type. # -# Example: +# 2. For detailed information about this command, please refer to +# the 'docs/qdev-device-use.txt' file. # -# -> { "execute": "device_add", -# "arguments": { "driver": "e1000", "id": "net1", -# "bus": "pci.0", -# "mac": "52:54:00:12:34:56" } } -# <- { "return": {} } +# 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. +# +# .. qmp-example:: +# +# -> { "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,42 @@ # # @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. +# .. note:: 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: +# .. qmp-example:: # -# -> { "execute": "device_del", -# "arguments": { "id": "net1" } } -# <- { "return": {} } +# -> { "execute": "device_del", +# "arguments": { "id": "net1" } } +# <- { "return": {} } # -# -> { "execute": "device_del", -# "arguments": { "id": "/machine/peripheral-anon/device[0]" } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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 # @@ -126,13 +132,12 @@ # # Since: 1.5 # -# Example: -# -# <- { "event": "DEVICE_DELETED", -# "data": { "device": "virtio-net-pci-0", -# "path": "/machine/peripheral/virtio-net-pci-0" }, -# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } +# .. qmp-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': 'str', 'path': 'str' } } @@ -140,7 +145,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 # @@ -148,13 +154,36 @@ # # Since: 6.2 # -# Example: -# -# <- { "event": "DEVICE_UNPLUG_GUEST_ERROR", -# "data": { "device": "core1", -# "path": "/machine/peripheral/core1" }, -# "timestamp": { "seconds": 1615570772, "microseconds": 202844 } } +# .. qmp-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': 'str', 'path': 'str' } } + +## +# @device-sync-config: +# +# Synchronize device configuration from host to guest part. First, +# copy the configuration from the host part (backend) to the guest +# part (frontend). Then notify guest software that device +# configuration changed. +# +# The command may be used to notify the guest about block device +# capcity change. Currently only vhost-user-blk device supports +# this. +# +# @id: the device's ID or QOM path +# +# Features: +# +# @unstable: The command is experimental. +# +# Since: 9.2 +## +{ 'command': 'device-sync-config', + 'features': [ 'unstable' ], + 'data': {'id': 'str'} } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 0990873ec8..176b549473 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,31 @@ 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_schedule(qemu_get_aio_context(), qemu_coroutine_self()); + qemu_coroutine_yield(); + } + 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_schedule(iohandler_get_aio_context(), + qemu_coroutine_self()); + qemu_coroutine_yield(); + } } else { /* * Actual context doesn't match the one the command needs. @@ -232,7 +254,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/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index 3e8aca6b15..f110a804b2 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -288,8 +288,8 @@ static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, return false; } if (qobject_type(qobj) != QTYPE_QDICT) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "object"); + error_setg(errp, "Invalid parameter type for '%s', expected: object", + full_name(qiv, name)); return false; } @@ -326,8 +326,8 @@ static bool qobject_input_start_list(Visitor *v, const char *name, return false; } if (qobject_type(qobj) != QTYPE_QLIST) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "array"); + error_setg(errp, "Invalid parameter type for '%s', expected: array", + full_name(qiv, name)); return false; } @@ -405,8 +405,8 @@ static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, } qnum = qobject_to(QNum, qobj); if (!qnum || !qnum_get_try_int(qnum, obj)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "integer"); + error_setg(errp, "Invalid parameter type for '%s', expected: integer", + full_name(qiv, name)); return false; } return true; @@ -494,8 +494,8 @@ static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, } qbool = qobject_to(QBool, qobj); if (!qbool) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "boolean"); + error_setg(errp, "Invalid parameter type for '%s', expected: boolean", + full_name(qiv, name)); return false; } @@ -534,8 +534,8 @@ static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, } qstr = qobject_to(QString, qobj); if (!qstr) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "string"); + error_setg(errp, "Invalid parameter type for '%s', expected: string", + full_name(qiv, name)); return false; } @@ -565,8 +565,8 @@ static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, } qnum = qobject_to(QNum, qobj); if (!qnum) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "number"); + error_setg(errp, "Invalid parameter type for '%s', expected: number", + full_name(qiv, name)); return false; } @@ -587,8 +587,8 @@ static bool qobject_input_type_number_keyval(Visitor *v, const char *name, if (qemu_strtod_finite(str, NULL, &val)) { /* TODO report -ERANGE more nicely */ - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "number"); + error_setg(errp, "Invalid parameter type for '%s', expected: number", + full_name(qiv, name)); return false; } @@ -623,8 +623,8 @@ static bool qobject_input_type_null(Visitor *v, const char *name, } if (qobject_type(qobj) != QTYPE_QNULL) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - full_name(qiv, name), "null"); + error_setg(errp, "Invalid parameter type for '%s', expected: null", + full_name(qiv, name)); return false; } *obj = qnull(); diff --git a/qapi/qom.json b/qapi/qom.json index 30e76653ad..28ce24cd8d 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" } ] } +# .. qmp-example:: # +# -> { "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: +# .. qmp-example:: +# :title: 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" } +# .. qmp-example:: +# :title: 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": {} } +# .. qmp-example:: # +# -> { "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,47 +215,50 @@ # # @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 ## { 'struct': 'CanHostSocketcanProperties', 'data': { 'if': 'str', - 'canbus': 'str' } } + 'canbus': 'str' }, + 'if': 'CONFIG_LINUX' } ## # @ColoCompareProperties: # # 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,30 +276,38 @@ ## # @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 ## { 'struct': 'CryptodevVhostUserProperties', 'base': 'CryptodevBackendProperties', - 'data': { 'chardev': 'str' } } + 'data': { 'chardev': 'str' }, + 'if': 'CONFIG_VHOST_CRYPTO' } ## # @DBusVMStateProperties: @@ -304,8 +316,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 +328,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 +349,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 +378,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 +395,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 +410,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 +428,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 +453,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 +467,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 +502,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 ## @@ -497,22 +516,23 @@ 'data': { 'evdev': 'str', '*grab_all': 'bool', '*repeat': 'bool', - '*grab-toggle': 'GrabToggleKeys' } } + '*grab-toggle': 'GrabToggleKeys' }, + 'if': 'CONFIG_LINUX' } ## # @EventLoopBaseProperties: # # 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 +546,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 +584,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,31 +596,32 @@ # # @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) # -# @prealloc-context: thread context to use for creation of preallocation threads -# (default: none) (since 7.2) +# @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) +# @share: if false, the memory is private to QEMU; if true, it is +# shared (default false for backends memory-backend-file and +# memory-backend-ram, true for backends memory-backend-epc, +# memory-backend-memfd, and memory-backend-shm) # # @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 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) +# @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 ## @@ -622,55 +643,77 @@ # # 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: # # Properties for memory-backend-memfd objects. # -# 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 ## @@ -678,15 +721,29 @@ 'base': 'MemoryBackendProperties', 'data': { '*hugetlb': 'bool', '*hugetlbsize': 'size', - '*seal': 'bool' } } + '*seal': 'bool' }, + 'if': 'CONFIG_LINUX' } + +## +# @MemoryBackendShmProperties: +# +# Properties for memory-backend-shm objects. +# +# This memory backend supports only shared memory, which is the +# default. +# +# Since: 9.1 +## +{ 'struct': 'MemoryBackendShmProperties', + 'base': 'MemoryBackendProperties', + 'data': { }, + 'if': 'CONFIG_POSIX' } ## # @MemoryBackendEpcProperties: # # Properties for memory-backend-epc objects. # -# The @share boolean option is true by default with epc -# # The @merge boolean option is false by default with epc # # The @dump boolean option is false by default with epc @@ -695,19 +752,22 @@ ## { 'struct': 'MemoryBackendEpcProperties', 'base': 'MemoryBackendProperties', - 'data': {} } + 'data': {}, + 'if': 'CONFIG_LINUX' } ## # @PrManagerHelperProperties: # # 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 ## { 'struct': 'PrManagerHelperProperties', - 'data': { 'path': 'str' } } + 'data': { 'path': 'str' }, + 'if': 'CONFIG_LINUX' } ## # @QtestProperties: @@ -731,7 +791,8 @@ # # @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 ## @@ -752,18 +813,90 @@ { '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' } } + +## +# @AcpiGenericPortProperties: +# +# Properties for acpi-generic-port objects. +# +# @pci-bus: QOM path of the PCI bus of the hostbridge associated with +# this SRAT Generic Port Affinity Structure. This is the same as +# the bus parameter for the root ports attached to this host +# bridge. The resulting SRAT Generic Port Affinity Structure will +# refer to the ACPI object in DSDT that represents the host bridge +# (e.g. ACPI0016 for CXL host bridges). See ACPI 6.5 Section +# 5.2.16.7 for more information. +# +# @node: Similar to a NUMA node ID, but instead of providing a +# reference point used for defining NUMA distances and access +# characteristics to memory or from an initiator (e.g. CPU), this +# node defines the boundary point between non-discoverable system +# buses which must be described by firmware, and a discoverable +# bus. NUMA distances and access characteristics are defined to +# and from that point. For system software to establish full +# initiator to target characteristics this information must be +# combined with information retrieved from the discoverable part +# of the path. An example would use CDAT (see UEFI.org) +# information read from devices and switches in conjunction with +# link characteristics read from PCIe Configuration space. +# To get the full path latency from CPU to CXL attached DRAM +# CXL device: Add the latency from CPU to Generic Port (from +# HMAT indexed via the the node ID in this SRAT structure) to +# that for CXL bus links, the latency across intermediate switches +# and from the EP port to the actual memory. Bandwidth is more +# complex as there may be interleaving across multiple devices +# and shared links in the path. +# +# Since: 9.2 +## +{ 'struct': 'AcpiGenericPortProperties', + 'data': { 'pci-bus': '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 ## @@ -775,8 +908,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 ## @@ -789,22 +922,45 @@ # # 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 ## { 'struct': 'RngRandomProperties', 'base': 'RngProperties', - 'data': { '*filename': 'str' } } + 'data': { '*filename': 'str' }, + 'if': 'CONFIG_POSIX' } + +## +# @SevCommonProperties: +# +# Properties common to objects that are derivatives of sev-common. +# +# @sev-device: SEV device to use (default: "/dev/sev") +# +# @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 +# +# @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) +# +# Since: 9.1 +## +{ 'struct': 'SevCommonProperties', + 'data': { '*sev-device': 'str', + '*cbitpos': 'uint32', + 'reduced-phys-bits': 'uint32', + '*kernel-hashes': 'bool' } } ## # @SevGuestProperties: # # Properties for sev-guest objects. # -# @sev-device: SEV device to use (default: "/dev/sev") -# # @dh-cert-file: guest owners DH certificate (encoded with base64) # # @session-file: guest owners session parameters (encoded with base64) @@ -813,41 +969,98 @@ # # @handle: SEV firmware handle (default: 0) # -# @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 -# -# @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) +# @legacy-vm-type: Use legacy KVM_SEV_INIT KVM interface for creating +# the VM. The newer KVM_SEV_INIT2 interface, from Linux >= 6.10, +# syncs additional vCPU state when initializing the VMSA +# structures, which will result in a different guest measurement. +# Set this to 'on' to force compatibility with older QEMU or kernel +# versions that rely on legacy KVM_SEV_INIT behavior. 'auto' will +# behave identically to 'on', but will automatically switch to +# using KVM_SEV_INIT2 if the user specifies any additional options +# that require it. If set to 'off', QEMU will require +# KVM_SEV_INIT2 unconditionally. +# (default: off) (since 9.1) # # Since: 2.12 ## { 'struct': 'SevGuestProperties', - 'data': { '*sev-device': 'str', - '*dh-cert-file': 'str', + 'base': 'SevCommonProperties', + 'data': { '*dh-cert-file': 'str', '*session-file': 'str', '*policy': 'uint32', '*handle': 'uint32', - '*cbitpos': 'uint32', - 'reduced-phys-bits': 'uint32', - '*kernel-hashes': 'bool' } } + '*legacy-vm-type': 'OnOffAuto' } } + +## +# @SevSnpGuestProperties: +# +# Properties for sev-snp-guest objects. Most of these are direct +# arguments for the KVM_SNP_* interfaces documented in the Linux +# kernel source under +# Documentation/arch/x86/amd-memory-encryption.rst, which are in turn +# closely coupled with the SNP_INIT/SNP_LAUNCH_* firmware commands +# documented in the SEV-SNP Firmware ABI Specification (Rev 0.9). +# +# More usage information is also available in the QEMU source tree +# under docs/amd-memory-encryption. +# +# @policy: the 'POLICY' parameter to the SNP_LAUNCH_START command, as +# defined in the SEV-SNP firmware ABI (default: 0x30000) +# +# @guest-visible-workarounds: 16-byte, base64-encoded blob to report +# hypervisor-defined workarounds, corresponding to the 'GOSVW' +# parameter of the SNP_LAUNCH_START command defined in the SEV-SNP +# firmware ABI (default: all-zero) +# +# @id-block: 96-byte, base64-encoded blob to provide the 'ID Block' +# structure for the SNP_LAUNCH_FINISH command defined in the +# SEV-SNP firmware ABI (default: all-zero) +# +# @id-auth: 4096-byte, base64-encoded blob to provide the 'ID +# Authentication Information Structure' for the SNP_LAUNCH_FINISH +# command defined in the SEV-SNP firmware ABI (default: all-zero) +# +# @author-key-enabled: true if 'id-auth' blob contains the 'AUTHOR_KEY' +# field defined SEV-SNP firmware ABI (default: false) +# +# @host-data: 32-byte, base64-encoded, user-defined blob to provide to +# the guest, as documented for the 'HOST_DATA' parameter of the +# SNP_LAUNCH_FINISH command in the SEV-SNP firmware ABI (default: +# all-zero) +# +# @vcek-disabled: Guests are by default allowed to choose between VLEK +# (Versioned Loaded Endorsement Key) or VCEK (Versioned Chip +# Endorsement Key) when requesting attestation reports from +# firmware. Set this to true to disable the use of VCEK. +# (default: false) (since: 9.1) +# +# Since: 9.1 +## +{ 'struct': 'SevSnpGuestProperties', + 'base': 'SevCommonProperties', + 'data': { + '*policy': 'uint64', + '*guest-visible-workarounds': 'str', + '*id-block': 'str', + '*id-auth': 'str', + '*author-key-enabled': 'bool', + '*host-data': 'str', + '*vcek-disabled': '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) +# @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) +# @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 ## @@ -860,12 +1073,16 @@ # @ObjectType: # # Features: -# @unstable: Member @x-remote-object is experimental. +# +# @unstable: Members @x-remote-object and @x-vfio-user-server are +# experimental. # # Since: 6.0 ## { 'enum': 'ObjectType', 'data': [ + 'acpi-generic-initiator', + 'acpi-generic-port', 'authz-list', 'authz-listfile', 'authz-pam', @@ -889,6 +1106,7 @@ 'input-barrier', { 'name': 'input-linux', 'if': 'CONFIG_LINUX' }, + 'iommufd', 'iothread', 'main-loop', { 'name': 'memory-backend-epc', @@ -897,6 +1115,8 @@ { 'name': 'memory-backend-memfd', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram', + { 'name': 'memory-backend-shm', + 'if': 'CONFIG_POSIX' }, 'pef-guest', { 'name': 'pr-manager-helper', 'if': 'CONFIG_LINUX' }, @@ -909,6 +1129,7 @@ { 'name': 'secret_keyring', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest', + 'sev-snp-guest', 'thread-context', 's390-pv-guest', 'throttle-group', @@ -936,6 +1157,8 @@ 'id': 'str' }, 'discriminator': 'qom-type', 'data': { + 'acpi-generic-initiator': 'AcpiGenericInitiatorProperties', + 'acpi-generic-port': 'AcpiGenericPortProperties', 'authz-list': 'AuthZListProperties', 'authz-listfile': 'AuthZListFileProperties', 'authz-pam': 'AuthZPAMProperties', @@ -958,6 +1181,7 @@ 'input-barrier': 'InputBarrierProperties', 'input-linux': { 'type': 'InputLinuxProperties', 'if': 'CONFIG_LINUX' }, + 'iommufd': 'IOMMUFDProperties', 'iothread': 'IothreadProperties', 'main-loop': 'MainLoopProperties', 'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties', @@ -966,6 +1190,8 @@ 'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties', 'if': 'CONFIG_LINUX' }, 'memory-backend-ram': 'MemoryBackendProperties', + 'memory-backend-shm': { 'type': 'MemoryBackendShmProperties', + 'if': 'CONFIG_POSIX' }, 'pr-manager-helper': { 'type': 'PrManagerHelperProperties', 'if': 'CONFIG_LINUX' }, 'qtest': 'QtestProperties', @@ -977,6 +1203,7 @@ 'secret_keyring': { 'type': 'SecretKeyringProperties', 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest': 'SevGuestProperties', + 'sev-snp-guest': 'SevSnpGuestProperties', 'thread-context': 'ThreadContextProperties', 'throttle-group': 'ThrottleGroupProperties', 'tls-creds-anon': 'TlsCredsAnonProperties', @@ -992,18 +1219,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": {} } +# .. qmp-example:: # +# -> { "execute": "object-add", +# "arguments": { "qom-type": "rng-random", "id": "rng1", +# "filename": "/dev/hwrng" } } +# <- { "return": {} } ## { 'command': 'object-add', 'data': 'ObjectOptions', 'boxed': true, 'allow-preconfig': true } @@ -1015,16 +1241,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": {} } +# .. qmp-example:: # +# -> { "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 deleted file mode 100644 index a1d2175a8b..0000000000 --- a/qapi/rdma.json +++ /dev/null @@ -1,39 +0,0 @@ -# -*- Mode: Python -*- -# vim: filetype=python -# - -## -# = RDMA device -## - -## -# @RDMA_GID_STATUS_CHANGED: -# -# Emitted when guest driver adds/deletes GID to/from device -# -# @netdev: RoCE Network Device name -# -# @gid-status: Add or delete indication -# -# @subnet-prefix: Subnet Prefix -# -# @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}} -# -## -{ 'event': 'RDMA_GID_STATUS_CHANGED', - 'data': { 'netdev' : 'str', - 'gid-status' : 'bool', - 'subnet-prefix' : 'uint64', - 'interface-id' : 'uint64' } } diff --git a/qapi/replay.json b/qapi/replay.json index 729470300d..35e0c4a692 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,19 +46,18 @@ ## # @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. # # Since: 5.2 # -# Example: -# -# -> { "execute": "query-replay" } -# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } +# .. qmp-example:: # +# -> { "execute": "query-replay" } +# <- { "return": { "mode": "play", "filename": "log.rr", "icount": 220414 } } ## { 'command': 'query-replay', 'returns': 'ReplayInfo' } @@ -67,36 +65,36 @@ ## # @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 # # Since: 5.2 # -# Example: -# -# -> { "execute": "replay-break", "arguments": { "icount": 220414 } } +# .. qmp-example:: # +# -> { "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" } +# .. qmp-example:: # +# -> { "execute": "replay-delete-break" } +# <- { "return": {} } ## { 'command': 'replay-delete-break' } @@ -104,18 +102,19 @@ # @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 # # Since: 5.2 # -# Example: +# .. qmp-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..51aa5b4930 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -26,15 +26,16 @@ # # Return rocker switch information. # +# @name: switch name +# # Returns: @Rocker information # # Since: 2.4 # -# Example: -# -# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } -# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} +# .. qmp-example:: # +# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } } +# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} ## { 'command': 'query-rocker', 'data': { 'name': 'str' }, @@ -43,7 +44,7 @@ ## # @RockerPortDuplex: # -# An eumeration of port duplex states. +# An enumeration of port duplex states. # # @half: half duplex # @@ -56,7 +57,7 @@ ## # @RockerPortAutoneg: # -# An eumeration of port autoneg states. +# An enumeration of port autoneg states. # # @off: autoneg is off # @@ -95,19 +96,20 @@ # # Return rocker switch port information. # +# @name: port name +# # Returns: a list of @RockerPort information # # Since: 2.4 # -# 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} -# ]} +# .. qmp-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} +# ]} ## { 'command': 'query-rocker-ports', 'data': { 'name': 'str' }, @@ -140,8 +142,8 @@ # # @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. +# .. note:: Optional members may or may not appear in the flow key +# depending if they're relevant to the flow key. # # Since: 2.4 ## @@ -170,8 +172,8 @@ # # @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. +# .. note:: Optional members may or may not appear in the flow mask +# depending if they're relevant to the flow mask. # # Since: 2.4 ## @@ -197,8 +199,8 @@ # # @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. +# .. note:: Optional members may or may not appear in the flow action +# depending if they're relevant to the flow action. # # Since: 2.4 ## @@ -235,26 +237,25 @@ # # @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 # # Since: 2.4 # -# 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...}, -# ]} +# .. qmp-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} +# }, +# {...}, +# ]} ## { 'command': 'query-rocker-of-dpa-flows', 'data': { 'name': 'str', '*tbl-id': 'uint32' }, @@ -291,8 +292,8 @@ # # @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. +# .. note:: Optional members may or may not appear in the group +# depending if they're relevant to the group type. # # Since: 2.4 ## @@ -311,31 +312,30 @@ # # @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 # # Since: 2.4 # -# 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} -# ]} +# .. qmp-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} +# ]} ## { 'command': 'query-rocker-of-dpa-groups', 'data': { 'name': 'str', '*type': 'uint8' }, diff --git a/qapi/run-state.json b/qapi/run-state.json index 419c188dd1..ce95cfa46b 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,21 +77,21 @@ # @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) -# +# 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 @@ -100,37 +102,32 @@ ## # @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" } } +# .. qmp-example:: # +# -> { "execute": "query-status" } +# <- { "return": { "running": true, +# "status": "running" } } ## { 'command': 'query-status', 'returns': 'StatusInfo', 'allow-preconfig': true } @@ -138,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 } } +# .. qmp-example:: # +# <- { "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 } } +# .. qmp-example:: # +# <- { "event": "POWERDOWN", +# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } ## { 'event': 'POWERDOWN' } @@ -183,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 } } +# .. qmp-example:: # +# <- { "event": "RESET", +# "data": { "guest": false, "reason": "guest-reset" }, +# "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } ## { 'event': 'RESET', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } @@ -207,11 +204,10 @@ # # Since: 0.12 # -# Example: -# -# <- { "event": "STOP", -# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } +# .. qmp-example:: # +# <- { "event": "STOP", +# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } ## { 'event': 'STOP' } @@ -222,60 +218,59 @@ # # Since: 0.12 # -# Example: -# -# <- { "event": "RESUME", -# "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } +# .. qmp-example:: # +# <- { "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 } } +# .. qmp-example:: # +# <- { "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 } } +# .. qmp-example:: # +# <- { "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 } } +# .. qmp-example:: # +# <- { "event": "WAKEUP", +# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } ## { 'event': 'WAKEUP' } @@ -286,19 +281,19 @@ # # @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. +# .. note:: This event is rate-limited. # # Since: 0.13 # -# Example: -# -# <- { "event": "WATCHDOG", -# "data": { "action": "reset" }, -# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } +# .. qmp-example:: # +# <- { "event": "WATCHDOG", +# "data": { "action": "reset" }, +# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } ## { 'event': 'WATCHDOG', 'data': { 'action': 'WatchdogAction' } } @@ -306,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 # @@ -322,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 ## @@ -338,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 ## @@ -366,10 +362,11 @@ # # @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) +# @exit-failure: Shutdown the VM and exit with nonzero status (since +# 7.1) # # Since: 6.0 ## @@ -379,17 +376,25 @@ ## # @watchdog-set-action: # -# Set watchdog action +# Set watchdog action. +# +# @action: @WatchdogAction action taken when watchdog timer expires. # # Since: 2.11 +# +# .. qmp-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. # @@ -397,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: +# .. qmp-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', @@ -430,12 +433,11 @@ # # Since: 1.5 # -# Example: -# -# <- { "event": "GUEST_PANICKED", -# "data": { "action": "pause" }, -# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } +# .. qmp-example:: # +# <- { "event": "GUEST_PANICKED", +# "data": { "action": "pause" }, +# "timestamp": { "seconds": 1648245231, "microseconds": 900001 } } ## { 'event': 'GUEST_PANICKED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } @@ -451,16 +453,29 @@ # # Since: 5.0 # -# Example: -# -# <- { "event": "GUEST_CRASHLOADED", -# "data": { "action": "run" }, -# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } +# .. qmp-example:: # +# <- { "event": "GUEST_CRASHLOADED", +# "data": { "action": "run" }, +# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } ## { 'event': 'GUEST_CRASHLOADED', 'data': { 'action': 'GuestPanicAction', '*info': 'GuestPanicInformation' } } +## +# @GUEST_PVSHUTDOWN: +# +# Emitted when guest submits a shutdown request via pvpanic interface +# +# Since: 9.1 +# +# .. qmp-example:: +# +# <- { "event": "GUEST_PVSHUTDOWN", +# "timestamp": { "seconds": 1648245259, "microseconds": 893771 } } +## +{ 'event': 'GUEST_PVSHUTDOWN' } + ## # @GuestPanicAction: # @@ -468,7 +483,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' ] } @@ -499,22 +518,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: @@ -525,13 +560,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 ## @@ -548,17 +583,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: @@ -567,21 +605,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 } } +# .. qmp-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': 'MemoryFailureRecipient', @@ -593,8 +630,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, # @@ -609,19 +646,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 ## @@ -637,10 +675,10 @@ # 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 ## @@ -653,14 +691,15 @@ # # 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. +# @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. +# @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' ] } \ No newline at end of file + 'data': [ 'run', 'internal-error', 'disable' ] } diff --git a/qapi/sockets.json b/qapi/sockets.json index bad74e34d3..6a95023315 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -5,8 +5,6 @@ # = Socket data types ## -{ 'include': 'common.json' } - ## # @NetworkAddressFamily: # @@ -31,6 +29,7 @@ # @InetSocketAddressBase: # # @host: host part of the address +# # @port: port part of the address ## { 'struct': 'InetSocketAddressBase', @@ -41,23 +40,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 +79,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 +102,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. +# .. note:: String types are used to allow for possible future +# hostname or service resolution support. # # Since: 2.8 ## @@ -110,9 +115,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 +145,8 @@ ## # @UnixSocketAddressWrapper: # +# @data: UNIX domain socket address +# # Since: 1.3 ## { 'struct': 'UnixSocketAddressWrapper', @@ -129,27 +155,30 @@ ## # @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 # -# Note: This type is deprecated in favor of SocketAddress. The -# difference between SocketAddressLegacy and SocketAddress is that the -# latter has fewer {} on the wire. +# @type: Transport type # # Since: 1.3 ## @@ -160,7 +189,10 @@ 'inet': 'InetSocketAddressWrapper', 'unix': 'UnixSocketAddressWrapper', 'vsock': 'VsockSocketAddressWrapper', - 'fd': 'StringWrapper' } } + 'fd': 'FdSocketAddressWrapper' } } +# Note: This type is deprecated in favor of SocketAddress. The +# difference between SocketAddressLegacy and SocketAddress is that the +# latter has fewer ``{}`` on the wire. ## # @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 index 57db5b1c74..8902ef94e0 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -18,11 +18,15 @@ # 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. +# for each power of two. # # Since: 7.1 ## @@ -36,8 +40,11 @@ # 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 @@ -50,33 +57,40 @@ # # Enumeration of statistics providers. # +# @kvm: since 7.1 +# +# @cryptodev: since 8.0 +# # Since: 7.1 ## { 'enum': 'StatsProvider', - 'data': [ 'kvm' ] } + '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. +# @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' ] } + 'data': [ 'vm', 'vcpu', 'cryptodev' ] } ## # @StatsRequest: # -# Indicates a set of statistics that should be returned by query-stats. +# 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 @@ -98,12 +112,15 @@ ## # @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: -# - which vCPUs to request statistics for -# - which providers to request statistics from -# - which named values to return within each provider +# 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 ## @@ -118,6 +135,9 @@ # @StatsValue: # # @scalar: single unsigned 64-bit integers. +# +# @boolean: single boolean value. +# # @list: list of unsigned 64-bit integers (used for histograms). # # Since: 7.1 @@ -131,6 +151,7 @@ # @Stats: # # @name: name of stat. +# # @value: stat value. # # Since: 7.1 @@ -145,7 +166,7 @@ # @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 +# if the object is exposed in the QOM tree # # @stats: list of statistics. # @@ -159,14 +180,14 @@ ## # @query-stats: # -# Return runtime-collected statistics for objects such as the -# VM or its vCPUs. +# 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). +# (e.g., for each vCPU). # # Since: 7.1 ## @@ -181,24 +202,25 @@ # 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. +# 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. +# @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``) +# @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 +# @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. +# @bucket-size: Present when @type is "linear-histogram", contains the +# width of each bucket of the histogram. # # Since: 7.1 ## @@ -217,7 +239,8 @@ # # @provider: provider for this set of statistics. # -# @target: the kind of object that can be queried through the provider. +# @target: the kind of object that can be queried through the +# provider. # # @stats: list of statistics. # @@ -233,16 +256,19 @@ # # Return the schema for all available runtime-collected statistics. # -# 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. +# @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 ## diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index 197139c1c0..3f1b9e9b41 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -353,8 +353,8 @@ static bool parse_type_number(Visitor *v, const char *name, double *obj, assert(siv->lm == LM_NONE); if (qemu_strtod_finite(siv->string, NULL, &val)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "number"); + error_setg(errp, "Invalid parameter type for '%s', expected: number", + name ? name : "null"); return false; } @@ -371,8 +371,8 @@ static bool parse_type_null(Visitor *v, const char *name, QNull **obj, *obj = NULL; if (siv->string[0]) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "null"); + error_setg(errp, "Invalid parameter type for '%s', expected: null", + name ? name : "null"); return false; } 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..a16a72edb9 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 @@ -29,11 +31,10 @@ # # Since: 1.5 # -# Example: -# -# -> { "execute": "query-tpm-models" } -# <- { "return": [ "tpm-tis", "tpm-crb", "tpm-spapr" ] } +# .. qmp-example:: # +# -> { "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 ## @@ -61,11 +62,10 @@ # # Since: 1.5 # -# Example: -# -# -> { "execute": "query-tpm-types" } -# <- { "return": [ "passthrough", "emulator" ] } +# .. qmp-example:: # +# -> { "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: +# .. qmp-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..eb5f63f513 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -32,16 +32,13 @@ # 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. +# @state: Tracing state. # # Since: 2.2 ## { 'struct': 'TraceEventInfo', - 'data': {'name': 'str', 'state': 'TraceEventState', 'vcpu': 'bool'} } + 'data': {'name': 'str', 'state': 'TraceEventState' } } ## # @trace-event-get-state: @@ -49,31 +46,19 @@ # Query the state of events. # # @name: Event name pattern (case-sensitive glob). -# @vcpu: The vCPU to query (any by default; since 2.7). # # 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 } ] } +# .. qmp-example:: # +# -> { "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' }, 'returns': ['TraceEventInfo'] } ## @@ -82,28 +67,18 @@ # 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. -# -# 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. # # Since: 2.2 # -# Example: -# -# -> { "execute": "trace-event-set-state", -# "arguments": { "name": "qemu_memalign", "enable": true } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "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'} } + 'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool' } } diff --git a/qapi/transaction.json b/qapi/transaction.json index 381a2df782..021e383496 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": {} } +# .. qmp-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": {} } ## { 'command': 'transaction', 'data': { 'actions': [ 'TransactionAction' ], diff --git a/qapi/ui.json b/qapi/ui.json index bf903996cb..4be92dcf81 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": {} } +# .. qmp-example:: # +# -> { "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. +# .. note:: 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": {} } +# .. qmp-example:: # +# -> { "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": {} } +# .. qmp-example:: # +# -> { "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,10 +271,8 @@ # # @server: Mouse cursor position is determined by the 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. +# @unknown: No information is available about mouse mode used by the +# spice server. # # Since: 1.1 ## @@ -287,10 +288,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 +301,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 # @@ -327,41 +329,40 @@ # # Since: 0.14 # -# 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 ... ] -# ] -# } -# } +# .. qmp-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 +# }, +# ... +# ] +# } +# } ## { 'command': 'query-spice', 'returns': 'SpiceInfo', 'if': 'CONFIG_SPICE' } @@ -377,15 +378,14 @@ # # Since: 0.14 # -# 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"} -# }} +# .. qmp-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"} +# }} ## { 'event': 'SPICE_CONNECTED', 'data': { 'server': 'SpiceBasicInfo', @@ -395,8 +395,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 # @@ -404,17 +404,16 @@ # # Since: 0.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} -# }} +# .. qmp-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} +# }} ## { 'event': 'SPICE_INITIALIZED', 'data': { 'server': 'SpiceServerInfo', @@ -432,15 +431,14 @@ # # Since: 0.14 # -# 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"} -# }} +# .. qmp-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"} +# }} ## { 'event': 'SPICE_DISCONNECTED', 'data': { 'server': 'SpiceBasicInfo', @@ -454,11 +452,10 @@ # # Since: 1.3 # -# Example: -# -# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, -# "event": "SPICE_MIGRATE_COMPLETED" } +# .. qmp-example:: # +# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172}, +# "event": "SPICE_MIGRATE_COMPLETED" } ## { 'event': 'SPICE_MIGRATE_COMPLETED', 'if': 'CONFIG_SPICE' } @@ -474,9 +471,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 +493,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 +509,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 +528,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 +606,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 +625,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. # @@ -654,26 +660,25 @@ # # Since: 0.14 # -# 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 -# } -# ] -# } -# } +# .. qmp-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 +# } +# ] +# } +# } ## { 'command': 'query-vnc', 'returns': 'VncInfo', 'if': 'CONFIG_VNC' } @@ -698,8 +703,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. +# .. note:: 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 +720,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 } } +# .. qmp-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': 'VncServerInfo', @@ -738,8 +743,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 # @@ -747,16 +752,15 @@ # # Since: 0.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 } } +# .. qmp-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': 'VncServerInfo', @@ -774,16 +778,15 @@ # # Since: 0.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 } } +# .. qmp-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': 'VncServerInfo', @@ -805,7 +808,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 ## @@ -822,25 +826,24 @@ # # Since: 0.14 # -# 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 -# } -# ] -# } +# .. qmp-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 +# } +# ] +# } ## { 'command': 'query-mice', 'returns': ['MouseInfo'] } @@ -852,50 +855,101 @@ # 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 ## { 'enum': 'QKeyCode', + 'prefix': 'Q_KEY_CODE', 'data': [ 'unmapped', 'shift', 'shift_r', 'alt', 'alt_r', 'ctrl', 'ctrl_r', 'menu', 'esc', '1', '2', '3', '4', '5', '6', '7', '8', @@ -918,7 +972,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 +985,8 @@ ## # @IntWrapper: # +# @data: a numeric key code +# # Since: 1.3 ## { 'struct': 'IntWrapper', @@ -939,6 +995,8 @@ ## # @QKeyCodeWrapper: # +# @data: An enumeration of key name +# # Since: 1.3 ## { 'struct': 'QKeyCodeWrapper', @@ -949,6 +1007,8 @@ # # Represents a keyboard key. # +# @type: key encoding +# # Since: 1.3 ## { 'union': 'KeyValue', @@ -963,27 +1023,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": {} } +# .. qmp-example:: # +# -> { "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 +1056,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 +1074,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 +1116,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 +1131,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 +1141,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 +1197,8 @@ ## # @InputBtnEventWrapper: # +# @data: Pointer button input event +# # Since: 2.0 ## { 'struct': 'InputBtnEventWrapper', @@ -1083,22 +1207,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 +1239,8 @@ 'data' : { 'key' : 'InputKeyEventWrapper', 'btn' : 'InputBtnEventWrapper', 'rel' : 'InputMoveEventWrapper', - 'abs' : 'InputMoveEventWrapper' } } + 'abs' : 'InputMoveEventWrapper', + 'mtt' : 'InputMultiTouchEventWrapper' } } ## # @input-send-event: @@ -1127,55 +1259,55 @@ # 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. +# .. 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. # -# Example: +# .. qmp-example:: +# :title: 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": {} } +# .. qmp-example:: +# :title: 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": {} } +# .. qmp-example:: +# :title: Move mouse pointer to absolute coordinates # +# -> { "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,19 +1320,20 @@ # 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 -# @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 +# 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 ## @@ -1215,8 +1348,8 @@ # # 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 ## @@ -1230,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. # @@ -1252,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 @@ -1281,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 ## @@ -1300,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' } } ## @@ -1318,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 ## @@ -1331,36 +1475,37 @@ # # 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) +# +# @xemu: The xemu user interface. # # Since: 2.12 ## @@ -1370,8 +1515,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' }, @@ -1386,9 +1530,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 @@ -1405,7 +1556,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' } } @@ -1466,16 +1617,13 @@ # # Reload display configuration. # -# Returns: Nothing on success. -# # Since: 6.0 # -# Example: -# -# -> { "execute": "display-reload", -# "arguments": { "type": "vnc", "tls-certs": true } } -# <- { "return": {} } +# .. qmp-example:: # +# -> { "execute": "display-reload", +# "arguments": { "type": "vnc", "tls-certs": true } } +# <- { "return": {} } ## { 'command': 'display-reload', 'data': 'DisplayReloadOptions', @@ -1498,9 +1646,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 ## @@ -1526,19 +1674,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": {} } +# .. qmp-example:: # +# -> { "execute": "display-update", +# "arguments": { "type": "vnc", "addresses": +# [ { "type": "inet", "host": "0.0.0.0", +# "port": "5901" } ] } } +# <- { "return": {} } ## { 'command': 'display-update', 'data': 'DisplayUpdateOptions', 'boxed' : true } + +## +# @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 +# +# .. qmp-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/vfio.json b/qapi/vfio.json new file mode 100644 index 0000000000..b53b7caecd --- /dev/null +++ b/qapi/vfio.json @@ -0,0 +1,66 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# + +## +# = VFIO devices +## + +## +# @QapiVfioMigrationState: +# +# An enumeration of the VFIO device migration states. +# +# @stop: The device is stopped. +# +# @running: The device is running. +# +# @stop-copy: The device is stopped and its internal state is +# available for reading. +# +# @resuming: The device is stopped and its internal state is available +# for writing. +# +# @running-p2p: The device is running in the P2P quiescent state. +# +# @pre-copy: The device is running, tracking its internal state and +# its internal state is available for reading. +# +# @pre-copy-p2p: The device is running in the P2P quiescent state, +# tracking its internal state and its internal state is available +# for reading. +# +# Since: 9.1 +## +{ 'enum': 'QapiVfioMigrationState', + 'data': [ 'stop', 'running', 'stop-copy', 'resuming', 'running-p2p', + 'pre-copy', 'pre-copy-p2p' ] } + +## +# @VFIO_MIGRATION: +# +# This event is emitted when a VFIO device migration state is changed. +# +# @device-id: The device's id, if it has one. +# +# @qom-path: The device's QOM path. +# +# @device-state: The new changed device migration state. +# +# Since: 9.1 +# +# .. qmp-example:: +# +# <- { "timestamp": { "seconds": 1713771323, "microseconds": 212268 }, +# "event": "VFIO_MIGRATION", +# "data": { +# "device-id": "vfio_dev1", +# "qom-path": "/machine/peripheral/vfio_dev1", +# "device-state": "stop" } } +## +{ 'event': 'VFIO_MIGRATION', + 'data': { + 'device-id': 'str', + 'qom-path': 'str', + 'device-state': 'QapiVfioMigrationState' + } } diff --git a/qapi/virtio.json b/qapi/virtio.json index 019d2d1987..2529c2d8b2 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -16,7 +16,6 @@ # @name: Name of the VirtIODevice # # Since: 7.2 -# ## { 'struct': 'VirtioInfo', 'data': { 'path': 'str', @@ -28,41 +27,40 @@ # 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" -# } -# ] -# } +# .. qmp-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' ] } @@ -70,7 +68,7 @@ ## # @VhostStatus: # -# Information about a vhost device. This information will only be +# 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 @@ -98,9 +96,7 @@ # @log-size: vhost_dev log_size # # Since: 7.2 -# ## - { 'struct': 'VhostStatus', 'data': { 'n-mem-sections': 'int', 'n-tmp-sections': 'int', @@ -119,8 +115,8 @@ # @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. +# Also includes the full status of the corresponding vhost device if +# the vhost device is active. # # @name: VirtIODevice name # @@ -136,8 +132,8 @@ # # @device-endian: VirtIODevice device_endian # -# @num-vqs: VirtIODevice virtqueue count. This is the number of active -# virtqueues being used by the VirtIODevice. +# @num-vqs: VirtIODevice virtqueue count. This is the number of +# active virtqueues being used by the VirtIODevice. # # @status: VirtIODevice configuration status (VirtioDeviceStatus) # @@ -163,14 +159,12 @@ # # @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. +# @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', @@ -202,240 +196,245 @@ # @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: +# .. qmp-example:: +# :annotated: # -# 1. Poll for the status of virtio-crypto (no vhost-crypto active) +# 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, +# -> { "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": [] -# }, -# "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" -# ], +# "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_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 -# } -# } +# }, +# "use-guest-notifier-mask": true, +# "vm-running": true, +# "queue-sel": 1, +# "disabled": false, +# "vhost-started": false, +# "use-started": true +# } +# } # +# .. qmp-example:: +# :annotated: +# +# 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', @@ -448,13 +447,13 @@ # device # # @statuses: List of decoded configuration statuses of the virtio -# device +# device # -# @unknown-statuses: Virtio device statuses bitmap that have not been decoded +# @unknown-statuses: Virtio device statuses bitmap that have not been +# decoded # # Since: 7.2 ## - { 'struct': 'VirtioDeviceStatus', 'data': { 'statuses': [ 'str' ], '*unknown-statuses': 'uint8' } } @@ -466,14 +465,13 @@ # Vhost User device # # @protocols: List of decoded vhost user protocol features of a vhost -# user device +# user device # # @unknown-protocols: Vhost user device protocol features bitmap that -# have not been decoded +# have not been decoded # # Since: 7.2 ## - { 'struct': 'VhostDeviceProtocols', 'data': { 'protocols': [ 'str' ], '*unknown-protocols': 'uint64' } } @@ -481,20 +479,19 @@ ## # @VirtioDeviceFeatures: # -# The common fields that apply to most Virtio devices. Some devices +# 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) +# unique features) # # @unknown-dev-features: Virtio device features bitmap that have not -# been decoded +# been decoded # # Since: 7.2 ## - { 'struct': 'VirtioDeviceFeatures', 'data': { 'transports': [ 'str' ], '*dev-features': [ 'str' ], @@ -525,7 +522,7 @@ # @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) +# vhost_get_vring_base (if vhost active) # # @shadow-avail-idx: VirtQueue shadow_avail_idx # @@ -536,9 +533,7 @@ # @signalled-used-valid: VirtQueue signalled_used_valid flag # # Since: 7.2 -# ## - { 'struct': 'VirtQueueStatus', 'data': { 'name': 'str', 'queue-index': 'uint16', @@ -565,70 +560,75 @@ # @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. +# .. note:: 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: +# .. qmp-example:: +# :annotated: # -# 1. Get VirtQueueStatus for virtio-vsock (vhost-vsock running) +# 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 -# } -# } +# -> { "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) +# .. qmp-example:: +# :annotated: # -# -> { "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 -# } -# } +# 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', @@ -654,22 +654,22 @@ # # @num: vhost_virtqueue num # -# @desc-phys: vhost_virtqueue desc_phys (descriptor area phys. addr.) +# @desc-phys: vhost_virtqueue desc_phys (descriptor area physical +# address) # # @desc-size: vhost_virtqueue desc_size # -# @avail-phys: vhost_virtqueue avail_phys (driver area phys. addr.) +# @avail-phys: vhost_virtqueue avail_phys (driver area physical +# address) # # @avail-size: vhost_virtqueue avail_size # -# @used-phys: vhost_virtqueue used_phys (device area phys. addr.) +# @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', @@ -695,62 +695,61 @@ # @queue: vhost_virtqueue index to examine # # Features: +# # @unstable: This command is meant for debugging. # # Returns: VirtVhostQueueStatus of the vhost_virtqueue # # Since: 7.2 # -# Examples: +# .. qmp-example:: +# :title: Get vhost_virtqueue status for vhost-crypto # -# 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 +# } +# } # -# -> { "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 -# } -# } +# .. qmp-example:: +# :title: 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', @@ -768,9 +767,7 @@ # @flags: List of descriptor flags # # Since: 7.2 -# ## - { 'struct': 'VirtioRingDesc', 'data': { 'addr': 'uint64', 'len': 'uint32', @@ -788,9 +785,7 @@ # @ring: VRingAvail ring[] entry at provided index # # Since: 7.2 -# ## - { 'struct': 'VirtioRingAvail', 'data': { 'flags': 'uint16', 'idx': 'uint16', @@ -806,9 +801,7 @@ # @idx: VRingUsed index # # Since: 7.2 -# ## - { 'struct': 'VirtioRingUsed', 'data': { 'flags': 'uint16', 'idx': 'uint16' } } @@ -830,9 +823,7 @@ # @used: VRingUsed info # # Since: 7.2 -# ## - { 'struct': 'VirtioQueueElement', 'data': { 'name': 'str', 'index': 'uint32', @@ -849,106 +840,155 @@ # # @queue: VirtQueue index to examine # -# @index: Index of the element in the queue -# (default: head of the queue) +# @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: +# .. qmp-example:: +# :title: Introspect on virtio-net's VirtQueue 0 at index 5 # -# 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 +# -> { "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 # } -# ], -# "avail": { -# "idx": 256, -# "flags": 0, -# "ring": 5 -# }, -# "used": { -# "idx": 13, -# "flags": 0 # } -# } -# } +# } # -# 2. Introspect on virtio-crypto's VirtQueue 1 at head +# .. qmp-example:: +# :title: 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 +# -> { "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 # } -# ], -# "avail": { -# "idx": 280, -# "flags": 0, -# "ring": 0 -# }, -# "used": { -# "idx": 280, -# "flags": 0 # } -# } -# } +# } # -# 3. Introspect on virtio-scsi's VirtQueue 2 at head +# .. qmp-example:: +# :title: 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 +# -> { "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 # } -# ], -# "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..30f46c97c9 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: +# .. qmp-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: +# .. qmp-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-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 a9b3a8103c..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" @@ -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; } @@ -954,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; @@ -1044,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 { @@ -1059,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; } @@ -1119,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) { @@ -1260,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; @@ -1545,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)); @@ -1693,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; @@ -1977,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; @@ -2025,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; @@ -2075,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 @@ -2089,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; } @@ -2731,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; } @@ -2803,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); @@ -2819,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); @@ -2835,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) { @@ -2846,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, "/"); } } @@ -2856,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; @@ -2882,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.", @@ -2899,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); @@ -2915,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; } } @@ -2932,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; } @@ -2943,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; @@ -3022,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; } @@ -3046,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. @@ -3059,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); } @@ -3086,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. @@ -3123,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, }; @@ -3141,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) { @@ -3418,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; @@ -3434,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 " @@ -3447,6 +3519,7 @@ static int img_snapshot(int argc, char **argv) ret = 1; } } + bdrv_graph_rdunlock_main_loop(); break; } @@ -3464,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; @@ -3488,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; @@ -3538,6 +3616,9 @@ static int img_rebase(int argc, char **argv) case 'U': force_share = true; break; + case 'c': + compress = true; + break; } } @@ -3588,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) { @@ -3598,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(), @@ -3631,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 = @@ -3695,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) { @@ -3736,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); @@ -3753,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) { @@ -3765,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, n, buf_old, 0); + 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, n, buf_new, 0); + 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; @@ -3810,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 { + assert(written + pnum <= IO_BUF_SIZE); ret = blk_pwrite(blk, offset + written, pnum, - buf_old + written, 0); + buf_old + written, write_flags); } if (ret < 0) { error_report("Error while writing to COW image: %s", @@ -3826,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); } @@ -4068,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) { @@ -4206,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; } @@ -4228,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; @@ -4237,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; @@ -4633,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); @@ -4817,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); diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index f66a575460..a4ea4b6e3b 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -354,7 +354,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; @@ -363,16 +364,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); } @@ -387,14 +396,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; @@ -403,19 +414,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); @@ -430,24 +435,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); } @@ -506,7 +510,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; @@ -542,7 +546,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]); @@ -555,7 +559,7 @@ 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; @@ -563,7 +567,7 @@ static int do_pread(BlockBackend *blk, char *buf, int64_t offset, return -ERANGE; } - ret = blk_pread(blk, offset, bytes, (uint8_t *)buf, 0); + ret = blk_pread(blk, offset, bytes, (uint8_t *)buf, flags); if (ret < 0) { return ret; } @@ -572,7 +576,7 @@ static int do_pread(BlockBackend *blk, char *buf, int64_t offset, } 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; @@ -588,54 +592,18 @@ static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset, return 1; } -typedef struct { - BlockBackend *blk; - int64_t offset; - int64_t bytes; - int64_t *total; - int flags; - int ret; - bool done; -} CoWriteZeroes; - -static void coroutine_fn co_pwrite_zeroes_entry(void *opaque) +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, @@ -690,11 +658,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); } @@ -704,7 +672,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; @@ -734,6 +702,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"); @@ -747,7 +716,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, }; @@ -765,8 +734,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; @@ -795,6 +765,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); @@ -859,15 +832,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); @@ -904,7 +882,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; } @@ -922,8 +900,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"); } @@ -934,7 +913,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, }; @@ -952,8 +931,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; @@ -968,6 +948,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; @@ -991,13 +974,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) { @@ -1032,8 +1016,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; } @@ -1050,15 +1034,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"); } @@ -1071,7 +1056,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, }; @@ -1081,7 +1066,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; @@ -1091,7 +1076,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; @@ -1121,6 +1106,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; @@ -1200,14 +1188,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); } } @@ -1215,7 +1210,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 { @@ -1241,7 +1236,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; } @@ -1258,10 +1253,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"); } @@ -1273,7 +1269,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, }; @@ -1282,7 +1278,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; @@ -1292,7 +1288,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; @@ -1303,6 +1299,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) { @@ -1328,7 +1327,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; } @@ -1353,8 +1353,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; } @@ -1370,6 +1370,7 @@ struct aio_ctx { bool zflag; BlockAcctCookie acct; int pattern; + BdrvRequestFlags flags; struct timespec t1; }; @@ -1399,7 +1400,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); @@ -1444,7 +1446,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); } @@ -1466,10 +1469,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"); } @@ -1480,7 +1484,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, }; @@ -1491,7 +1495,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; @@ -1512,6 +1516,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; @@ -1538,7 +1545,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); @@ -1548,7 +1556,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; } @@ -1569,11 +1578,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"); @@ -1587,7 +1597,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, }; @@ -1597,22 +1607,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); @@ -1648,7 +1660,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; @@ -1660,6 +1672,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; @@ -1678,12 +1696,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); @@ -1694,8 +1712,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; @@ -1728,6 +1746,270 @@ 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; + int64_t val; + unsigned int nr_zones; + + ++optind; + offset = cvtnum(argv[optind]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } + ++optind; + val = cvtnum(argv[optind]); + if (val < 0) { + print_cvtnum_err(val, argv[optind]); + return val; + } + if (val > UINT_MAX) { + printf("Number of zones must be less than 2^32\n"); + return -ERANGE; + } + nr_zones = val; + + 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]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } + ++optind; + len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } + 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]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } + ++optind; + len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } + 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]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } + ++optind; + len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } + 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]); + if (offset < 0) { + print_cvtnum_err(offset, argv[optind]); + return offset; + } + ++optind; + len = cvtnum(argv[optind]); + if (len < 0) { + print_cvtnum_err(len, argv[optind]); + return len; + } + 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", @@ -1817,6 +2099,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); } @@ -1841,8 +2126,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); } @@ -2520,6 +2806,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..6707067fea 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; @@ -201,6 +213,7 @@ int main(int argc, char *argv[]) ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); map = xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); + xkb_context_unref(ctx); if (!map) { /* libxkbcommon prints error */ exit(1); @@ -215,17 +228,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); + xkb_keymap_unref(map); /* add quirks */ fprintf(outfile, diff --git a/qemu-nbd.c b/qemu-nbd.c index 0cd5aa6f02..a186d2e119 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) { @@ -364,7 +390,9 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nb_fds++; nbd_update_server_watch(); - nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed); + /* TODO - expose handshake timeout as command line option */ + nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS, + tlscreds, tlsauthz, nbd_client_closed, NULL); } static void nbd_update_server_watch(void) @@ -510,7 +538,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"; @@ -563,7 +590,8 @@ int main(int argc, char **argv) pthread_t client_thread; const char *fmt = NULL; Error *local_err = NULL; - BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; + BlockdevDetectZeroesOptions detect_zeroes = + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; const char *export_name = NULL; /* defaults to "" later for server mode */ const char *export_description = NULL; @@ -573,13 +601,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 +741,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 +772,7 @@ int main(int argc, char **argv) } break; case 'v': - verbose = 1; + opts.verbose = true; break; case 'V': version(argv[0]); @@ -770,7 +804,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 +824,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 +854,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 +873,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 +903,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 +914,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 +942,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 +962,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 +1034,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 +1055,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 +1091,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 +1111,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 +1126,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 +1164,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 +1175,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 +1202,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 +1222,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 e52289479b..dacc9790a4 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" @@ -59,15 +61,15 @@ 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. ``vmport=on|off|auto`` Enables emulation of VMWare IO port, for vmmouse etc. auto says - to select the value based on accel. For accel=xen the default is - off otherwise the default is on. + to select the value based on accel and i8042. For accel=xen or + i8042=off the default is off otherwise the default is on. ``dump-guest-core=on|off`` Include guest memory in a core dump. The default is on. @@ -149,14 +151,14 @@ SRST 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. + 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=512k + -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, @@ -178,19 +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" + " 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", QEMU_ARCH_ALL) + " 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. @@ -210,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 @@ -237,6 +248,20 @@ 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. @@ -247,17 +272,27 @@ SRST 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][,modules=modules][,cores=cores]\n" + " [,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" + " modules= number of modules in one cluster\n" + " cores= number of cores in one module\n" " threads= number of threads in one core\n" "Note: Different machines may have different subsets of the CPU topology\n" " parameters supported, so the actual meaning of the supported parameters\n" @@ -273,7 +308,7 @@ DEF("smp", HAS_ARG, QEMU_OPTION_smp, " must be set as 1 in the purpose of correct parsing.\n", QEMU_ARCH_ALL) SRST -``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,clusters=clusters][,cores=cores][,threads=threads]`` +``-smp [[cpus=]n][,maxcpus=maxcpus][,drawers=drawers][,books=books][,sockets=sockets][,dies=dies][,clusters=clusters][,modules=modules][,cores=cores][,threads=threads]`` Simulate a SMP system with '\ ``n``\ ' CPUs initially present on the machine type board. On boards supporting CPU hotplug, the optional '\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be @@ -312,14 +347,14 @@ SRST -smp 8,sockets=2,cores=2,threads=2,maxcpus=8 The following sub-option defines a CPU topology hierarchy (2 sockets - totally on the machine, 2 dies per socket, 2 cores per die, 2 threads - per core) for PC machines which support sockets/dies/cores/threads. - Some members of the option can be omitted but their values will be - automatically computed: + totally on the machine, 2 dies per socket, 2 modules per die, 2 cores per + module, 2 threads per core) for PC machines which support sockets/dies + /modules/cores/threads. Some members of the option can be omitted but + their values will be automatically computed: :: - -smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16 + -smp 32,sockets=2,dies=2,modules=2,cores=2,threads=2,maxcpus=32 The following sub-option defines a CPU topology hierarchy (2 sockets totally on the machine, 2 clusters per socket, 2 cores per cluster, @@ -343,6 +378,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, @@ -402,15 +440,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 @@ -635,7 +680,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]`` @@ -695,31 +740,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 driver option is the same as with the corresponding ``-audiodev`` option below. - The guest hardware model can be set with ``model=modelname``. - - Use ``driver=help`` to list the available drivers, - and ``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: @@ -728,6 +765,17 @@ 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, @@ -776,6 +824,12 @@ 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" @@ -939,6 +993,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 @@ -1033,7 +1102,7 @@ SRST external entity that provides the IPMI services. A connection is made to an external BMC simulator. If you do this, - it is strongly recommended that you use the "reconnect=" chardev + it is strongly recommended that you use the "reconnect-ms=" chardev option to reconnect to the simulator if the connection is lost. Note that if this is not used carefully, it can be a security issue, as the interface has the ability to send resets, NMIs, and power off @@ -1105,6 +1174,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, @@ -1171,10 +1251,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`` @@ -1184,18 +1264,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, @@ -1393,6 +1477,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 @@ -1615,7 +1715,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: @@ -1666,27 +1766,18 @@ DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, " [[,throttling.bps-total-max=bm]|[[,throttling.bps-read-max=rm][,throttling.bps-write-max=wm]]]\n" " [[,throttling.iops-total-max=im]|[[,throttling.iops-read-max=irm][,throttling.iops-write-max=iwm]]]\n" " [[,throttling.iops-size=is]]\n" - "-fsdev proxy,id=id,socket=socket[,writeout=immediate][,readonly=on]\n" - "-fsdev proxy,id=id,sock_fd=sock_fd[,writeout=immediate][,readonly=on]\n" "-fsdev synth,id=id\n", QEMU_ARCH_ALL) SRST ``-fsdev local,id=id,path=path,security_model=security_model [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode] [,throttling.option=value[,throttling.option=value[,...]]]`` \ -``-fsdev proxy,id=id,socket=socket[,writeout=writeout][,readonly=on]`` - \ -``-fsdev proxy,id=id,sock_fd=sock_fd[,writeout=writeout][,readonly=on]`` - \ ``-fsdev synth,id=id[,readonly=on]`` Define a new file system device. Valid options are: ``local`` Accesses to the filesystem are done by QEMU. - ``proxy`` - Accesses to the filesystem are done by virtfs-proxy-helper(1). - ``synth`` Synthetic filesystem, only used by QTests. @@ -1711,8 +1802,6 @@ SRST security model is same as passthrough except the sever won't report failures if it fails to set file attributes like ownership. Security model is mandatory only for local fsdriver. - Other fsdrivers (like proxy) don't take security model as a - parameter. ``writeout=writeout`` This is an optional argument. The only supported value is @@ -1725,16 +1814,6 @@ SRST Enables exporting 9p share as a readonly mount for guests. By default read-write access is given. - ``socket=socket`` - Enables proxy filesystem driver to use passed socket file for - communicating with virtfs-proxy-helper(1). - - ``sock_fd=sock_fd`` - Enables proxy filesystem driver to use passed socket descriptor - for communicating with virtfs-proxy-helper(1). Usually a helper - like libvirt will create socketpair and pass one of the fds as - sock\_fd. - ``fmode=fmode`` Specifies the default mode for newly created files on the host. Works only with security models "mapped-xattr" and @@ -1787,18 +1866,12 @@ ERST DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, "-virtfs local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passthrough|none\n" " [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,multidevs=remap|forbid|warn]\n" - "-virtfs proxy,mount_tag=tag,socket=socket[,id=id][,writeout=immediate][,readonly=on]\n" - "-virtfs proxy,mount_tag=tag,sock_fd=sock_fd[,id=id][,writeout=immediate][,readonly=on]\n" "-virtfs synth,mount_tag=tag[,id=id][,readonly=on]\n", QEMU_ARCH_ALL) SRST ``-virtfs local,path=path,mount_tag=mount_tag ,security_model=security_model[,writeout=writeout][,readonly=on] [,fmode=fmode][,dmode=dmode][,multidevs=multidevs]`` \ -``-virtfs proxy,socket=socket,mount_tag=mount_tag [,writeout=writeout][,readonly=on]`` - \ -``-virtfs proxy,sock_fd=sock_fd,mount_tag=mount_tag [,writeout=writeout][,readonly=on]`` - \ ``-virtfs synth,mount_tag=mount_tag`` Define a new virtual filesystem device and expose it to the guest using a virtio-9p-device (a.k.a. 9pfs), which essentially means that a certain @@ -1815,9 +1888,6 @@ SRST ``local`` Accesses to the filesystem are done by QEMU. - ``proxy`` - Accesses to the filesystem are done by virtfs-proxy-helper(1). - ``synth`` Synthetic filesystem, only used by QTests. @@ -1842,8 +1912,6 @@ SRST security model is same as passthrough except the sever won't report failures if it fails to set file attributes like ownership. Security model is mandatory only for local fsdriver. - Other fsdrivers (like proxy) don't take security model as a - parameter. ``writeout=writeout`` This is an optional argument. The only supported value is @@ -1856,16 +1924,6 @@ SRST Enables exporting 9p share as a readonly mount for guests. By default read-write access is given. - ``socket=socket`` - Enables proxy filesystem driver to use passed socket file for - communicating with virtfs-proxy-helper(1). Usually a helper like - libvirt will create socketpair and pass one of the fds as - sock\_fd. - - ``sock_fd`` - Enables proxy filesystem driver to use passed 'sock\_fd' as the - socket descriptor for interfacing with virtfs-proxy-helper(1). - ``fmode=fmode`` Specifies the default mode for newly created files on the host. Works only with security models "mapped-xattr" and @@ -1909,8 +1967,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) @@ -1994,7 +2052,7 @@ DEF("display", HAS_ARG, QEMU_OPTION_display, #if defined(CONFIG_GTK) "-display gtk[,full-screen=on|off][,gl=on|off][,grab-on-hover=on|off]\n" " [,show-tabs=on|off][,show-cursor=on|off][,window-close=on|off]\n" - " [,show-menubar=on|off]\n" + " [,show-menubar=on|off][,zoom-to-fit=on|off]\n" #endif #if defined(CONFIG_VNC) "-display vnc=[,]\n" @@ -2004,6 +2062,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" @@ -2011,9 +2071,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" @@ -2089,6 +2146,9 @@ SRST ``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 @@ -2105,10 +2165,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 @@ -2152,7 +2228,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" @@ -2160,8 +2236,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 @@ -2178,13 +2254,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. @@ -2262,22 +2331,6 @@ SRST pick the first available. (Since 2.9) ERST -DEF("portrait", 0, QEMU_OPTION_portrait, - "-portrait rotate graphical output 90 deg left (only PXA LCD)\n", - QEMU_ARCH_ALL) -SRST -``-portrait`` - Rotate graphical output 90 deg left (only PXA LCD). -ERST - -DEF("rotate", HAS_ARG, QEMU_OPTION_rotate, - "-rotate rotate graphical output some deg left (only PXA LCD)\n", - QEMU_ARCH_ALL) -SRST -``-rotate deg`` - Rotate graphical output some deg left (only PXA LCD). -ERST - DEF("vga", HAS_ARG, QEMU_OPTION_vga, "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|virtio|none]\n" " select video card type\n", QEMU_ARCH_ALL) @@ -2349,8 +2402,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 @@ -2364,7 +2419,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. @@ -2401,6 +2456,10 @@ SRST host. It is possible to control the websocket listen address independently, using the syntax ``websocket``\ =host:port. + Websocket could be allowed over UNIX domain socket, using the syntax + ``websocket``\ =unix:path, where path is the location of a unix socket + to listen for connections on. + If no TLS credentials are provided, the websocket connection runs in unencrypted mode. If TLS credentials are provided, the websocket connection requires encrypted client connections. @@ -2537,7 +2596,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, @@ -2546,23 +2606,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, @@ -2598,7 +2642,7 @@ DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, " specify SMBIOS type 3 fields\n" "-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str]\n" " [,asset=str][,part=str][,max-speed=%d][,current-speed=%d]\n" - " [,processor-id=%d]\n" + " [,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" @@ -2609,7 +2653,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. @@ -2626,9 +2670,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 @@ -2786,9 +2833,9 @@ 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]\n" - "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off]\n" - "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor\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-ms=milliseconds]\n" + "-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect-ms=milliseconds]\n" + "-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect-ms=milliseconds]\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" @@ -2813,6 +2860,19 @@ 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" @@ -2858,6 +2918,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 @@ -2886,6 +2949,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 @@ -2893,7 +2959,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 @@ -2993,6 +3059,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" @@ -3223,7 +3291,196 @@ SRST -device e1000,netdev=n1,mac=52:54:00:12:34:56 \\ -netdev socket,id=n1,mcast=239.192.168.1:1102,localaddr=1.2.3.4 -``-netdev l2tpv3,id=id,src=srcaddr,dst=dstaddr[,srcport=srcport][,dstport=dstport],txsession=txsession[,rxsession=rxsession][,ipv6=on|off][,udp=on|off][,cookie64][,counter][,pincounter][,txcookie=txcookie][,rxcookie=rxcookie][,offset=offset]`` +``-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-ms=milliseconds]`` + Configure a network backend to connect to another QEMU virtual machine or a proxy using a TCP/IP socket. + + ``server=on|off`` + if ``on`` create a server socket + + ``addr.host=host,addr.port=port`` + socket address to listen on (server=on) or connect to (server=off) + + ``to=maxport`` + if present, this is range of possible addresses, with port between ``port`` and ``maxport``. + + ``numeric=on|off`` + if ``on`` ``host`` and ``port`` are guaranteed to be numeric, otherwise a name resolution should be attempted (default: ``off``) + + ``keep-alive=on|off`` + enable keep-alive when connecting to this socket. Not supported for passive sockets. + + ``mptcp=on|off`` + enable multipath TCP + + ``ipv4=on|off`` + whether to accept IPv4 addresses, default to try both IPv4 and IPv6 + + ``ipv6=on|off`` + whether to accept IPv6 addresses, default to try both IPv4 and IPv6 + + ``reconnect-ms=milliseconds`` + for a client socket, if a socket is disconnected, then attempt a reconnect after the given number of milliseconds. + Setting this to zero disables this function. (default: 0) + + Example (two guests connected using a TCP/IP socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev stream,id=net0,server=on,addr.type=inet,addr.host=localhost,addr.port=1234 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev stream,id=net0,server=off,addr.type=inet,addr.host=localhost,addr.port=1234,reconnect-ms=5000 + +``-netdev stream,id=str[,server=on|off],addr.type=unix,addr.path=path[,abstract=on|off][,tight=on|off][,reconnect-ms=milliseconds]`` + Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented unix domain socket. + + ``server=on|off`` + if ``on`` create a server socket + + ``addr.path=path`` + filesystem path to use + + ``abstract=on|off`` + if ``on``, this is a Linux abstract socket address. + + ``tight=on|off`` + if false, pad an abstract socket address with enough null bytes to make it fill struct sockaddr_un member sun_path. + + ``reconnect-ms=milliseconds`` + for a client socket, if a socket is disconnected, then attempt a reconnect after the given number of milliseconds. + Setting this to zero disables this function. (default: 0) + + Example (using passt as a replacement of -netdev user): + + .. parsed-literal:: + + # start passt server as a non privileged user + passt + UNIX domain socket bound at /tmp/passt_1.socket + # start QEMU to connect to passt + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0 \\ + -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket + + Example (two guests connected using a stream oriented unix domain socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + netdev stream,id=net0,server=on,addr.type=unix,addr.path=/tmp/qemu0 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev stream,id=net0,server=off,addr.type=unix,addr.path=/tmp/qemu0,reconnect-ms=5000 + +``-netdev stream,id=str[,server=on|off],addr.type=fd,addr.str=file-descriptor[,reconnect-ms=milliseconds]`` + Configure a network backend to connect to another QEMU virtual machine or a proxy using a stream oriented socket file descriptor. + + ``server=on|off`` + if ``on`` create a server socket + + ``addr.str=file-descriptor`` + file descriptor number to use as a socket + + ``reconnect-ms=milliseconds`` + for a client socket, if a socket is disconnected, then attempt a reconnect after the given number of milliseconds. + Setting this to zero disables this function. (default: 0) + +``-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=inet,local.host=addr]`` + Configure a network backend to connect to a multicast address. + + ``remote.host=maddr,remote.port=port`` + multicast address + + ``local.host=addr`` + specify the host address to send packets from + + Example: + + .. parsed-literal:: + + # launch one QEMU instance + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,remote.type=inet,remote.host=224.0.0.1,remote.port=1234 + # launch another QEMU instance on same "bus" + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev dgram,id=net0,remote.type=inet,remote.host=224.0.0.1,remote.port=1234 + # launch yet another QEMU instance on same "bus" + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:58 \\ + -netdev dgram,id=net0,remote.type=inet,remote.host=224.0.0.1,remote.port=1234 + +``-netdev dgram,id=str,remote.type=inet,remote.host=maddr,remote.port=port[,local.type=fd,local.str=file-descriptor]`` + Configure a network backend to connect to a multicast address using a UDP socket file descriptor. + + ``remote.host=maddr,remote.port=port`` + multicast address + + ``local.str=file-descriptor`` + File descriptor to use to send packets + +``-netdev dgram,id=str,local.type=inet,local.host=addr,local.port=port[,remote.type=inet,remote.host=addr,remote.port=port]`` + Configure a network backend to connect to another QEMU virtual + machine or a proxy using a datagram oriented unix domain socket. + + ``local.host=addr,local.port=port`` + IP address to use to send the packets from + + ``remote.host=addr,remote.port=port`` + Destination IP address + + Example (two guests connected using an UDP/IP socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,local.type=inet,local.host=localhost,local.port=1234,remote.type=inet,remote.host=localhost,remote.port=1235 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,local.type=inet,local.host=localhost,local.port=1235,remote.type=inet,remote.host=localhost,remote.port=1234 + +``-netdev dgram,id=str,local.type=unix,local.path=path[,remote.type=unix,remote.path=path]`` + Configure a network backend to connect to another QEMU virtual + machine or a proxy using a datagram oriented unix socket. + + ``local.path=path`` + filesystem path to use to bind the socket + + ``remote.path=path`` + filesystem path to use as a destination (see sendto(2)) + + Example (two guests connected using an UDP/UNIX socket): + + .. parsed-literal:: + + # first VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:56 \\ + -netdev dgram,id=net0,local.type=unix,local.path=/tmp/qemu0,remote.type=unix,remote.path=/tmp/qemu1 + # second VM + |qemu_system| linux.img \\ + -device virtio-net,netdev=net0,mac=52:54:00:12:34:57 \\ + -netdev dgram,id=net0,local.type=unix,local.path=/tmp/qemu1,remote.type=unix,remote.path=/tmp/qemu0 + +``-netdev dgram,id=str,local.type=fd,local.str=file-descriptor`` + Configure a network backend to connect to another QEMU virtual + machine or a proxy using a datagram oriented socket file descriptor. + + ``local.str=file-descriptor`` + File descriptor to use to send packets + +``-netdev l2tpv3,id=id,src=srcaddr,dst=dstaddr[,srcport=srcport][,dstport=dstport],txsession=txsession[,rxsession=rxsession][,ipv6=on|off][,udp=on|off][,cookie64=on|off][,counter=on|off][,pincounter=on|off][,txcookie=txcookie][,rxcookie=rxcookie][,offset=offset]`` Configure a L2TPv3 pseudowire host network backend. L2TPv3 (RFC3931) is a popular protocol to transport Ethernet (and other Layer 2) data frames between two systems. It is present in routers, firewalls and @@ -3238,7 +3495,7 @@ SRST ``dst=dstaddr`` destination address (mandatory) - ``udp`` + ``udp=on`` select udp encapsulation (default is ip). ``srcport=srcport`` @@ -3247,7 +3504,7 @@ SRST ``dstport=dstport`` destination udp port. - ``ipv6`` + ``ipv6=on`` force v6, otherwise defaults to v4. ``rxcookie=rxcookie``; \ ``txcookie=txcookie`` @@ -3255,7 +3512,7 @@ SRST Their function is mostly to prevent misconfiguration. By default they are 32 bit. - ``cookie64`` + ``cookie64=on`` Set cookie size to 64 bit instead of the default 32 ``counter=off`` @@ -3289,7 +3546,7 @@ SRST # launch QEMU instance - if your network has reorder or is very lossy add ,pincounter |qemu_system| linux.img -device e1000,netdev=n1 \\ - -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter + -netdev l2tpv3,id=n1,src=4.2.3.1,dst=1.2.3.4,udp=on,srcport=16384,dstport=16384,rxsession=0xffffffff,txsession=0xffffffff,counter=on ``-netdev vde,id=id[,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]`` Configure VDE backend to connect to PORT n of a vde switch running @@ -3307,6 +3564,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 @@ -3373,9 +3679,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev help\n" "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4=on|off][,ipv6=on|off][,nodelay=on|off]\n" - " [,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds][,mux=on|off]\n" + " [,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect-ms=milliseconds][,mux=on|off]\n" " [,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID] (tcp)\n" - "-chardev socket,id=id,path=path[,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds]\n" + "-chardev socket,id=id,path=path[,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect-ms=milliseconds]\n" " [,mux=on|off][,logfile=PATH][,logappend=on|off][,abstract=on|off][,tight=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4=on|off][,ipv6=on|off][,mux=on|off]\n" @@ -3384,13 +3690,13 @@ 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" "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #else - "-chardev pty,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev pty,id=id[,path=path][,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev stdio,id=id[,mux=on|off][,signal=on|off][,logfile=PATH][,logappend=on|off]\n" #endif #ifdef CONFIG_BRLAPI @@ -3399,11 +3705,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" @@ -3418,7 +3722,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. @@ -3488,7 +3792,7 @@ The available backends are: A void device. This device will not emit any data, and will drop any data it receives. The null backend does not take any options. -``-chardev socket,id=id[,TCP options or unix options][,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds][,tls-creds=id][,tls-authz=id]`` +``-chardev socket,id=id[,TCP options or unix options][,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect-ms=milliseconds][,tls-creds=id][,tls-authz=id]`` Create a two-way stream socket, which can be either a TCP or a unix socket. A unix socket will be created if ``path`` is specified. Behaviour is undefined if TCP options are specified for a unix @@ -3505,9 +3809,9 @@ The available backends are: ``websocket=on|off`` specifies that the socket uses WebSocket protocol for communication. - ``reconnect`` sets the timeout for reconnecting on non-server + ``reconnect-ms`` sets the timeout for reconnecting on non-server sockets when the remote end goes away. qemu will delay this many - seconds and then attempt to reconnect. Zero disables reconnecting, + milliseconds and then attempt to reconnect. Zero disables reconnecting, and is the default. ``tls-creds`` requests enablement of the TLS protocol for @@ -3589,13 +3893,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: @@ -3625,12 +3935,22 @@ The available backends are: ``path`` specifies the name of the serial device to open. -``-chardev pty,id=id`` - Create a new pseudo-terminal on the host and connect to it. ``pty`` - does not take any options. +``-chardev pty,id=id[,path=path]`` + Create a new pseudo-terminal on the host and connect to it. ``pty`` is not available on Windows hosts. + If ``path`` is specified, QEMU will create a symbolic link at + that location which points to the new PTY device. + + This avoids having to make QMP or HMP monitor queries to find out + what the new PTY device path is. + + Note that while QEMU will remove the symlink when it exits + gracefully, it will not do so in case of crashes or on certain + startup errors. It is recommended that the user checks and removes + the symlink after QEMU terminates to account for this. + ``-chardev stdio,id=id[,signal=on|off]`` Connect to standard input and standard output of the QEMU process. @@ -3642,15 +3962,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. @@ -3841,15 +4154,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, \ @@ -3932,9 +4253,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 @@ -3961,10 +4286,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: @@ -3982,14 +4308,32 @@ SRST vc:80Cx24C - ``pty`` - [Linux only] Pseudo TTY (a new PTY is automatically allocated) + ``pty[:path]`` + [Linux only] Pseudo TTY (a new PTY is automatically allocated). + + If ``path`` is specified, QEMU will create a symbolic link at + that location which points to the new PTY device. + + This avoids having to make QMP or HMP monitor queries to find + out what the new PTY device path is. + + Note that while QEMU will remove the symlink when it exits + gracefully, it will not do so in case of crashes or on certain + startup errors. It is recommended that the user checks and + removes the symlink after QEMU terminates to account for this. ``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`` @@ -4046,14 +4390,14 @@ SRST ``telnet options:`` localhost 5555 - ``tcp:[host]:port[,server=on|off][,wait=on|off][,nodelay=on|off][,reconnect=seconds]`` + ``tcp:[host]:port[,server=on|off][,wait=on|off][,nodelay=on|off][,reconnect-ms=milliseconds]`` The TCP Net Console has two modes of operation. It can send the serial I/O to a location or wait for a connection from a location. By default the TCP Net Console is sent to host at the port. If you use the ``server=on`` option QEMU will wait for a client socket application to connect to the port before continuing, unless the ``wait=on|off`` option was specified. The ``nodelay=on|off`` - option disables the Nagle buffering algorithm. The ``reconnect=on`` + option disables the Nagle buffering algorithm. The ``reconnect-ms`` option only applies if ``server=no`` is set, if the connection goes down it will attempt to reconnect at the given interval. If host is omitted, 0.0.0.0 is assumed. Only one TCP connection at a @@ -4083,7 +4427,7 @@ SRST The WebSocket protocol is used instead of raw tcp socket. The port acts as a WebSocket server. Client mode is not supported. - ``unix:path[,server=on|off][,wait=on|off][,reconnect=seconds]`` + ``unix:path[,server=on|off][,wait=on|off][,reconnect-ms=milliseconds]`` A unix domain socket is used instead of a tcp socket. The option works the same as if you had specified ``-serial tcp`` except the unix domain socket path is used for connections. @@ -4142,26 +4486,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, \ @@ -4184,13 +4544,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) @@ -4565,6 +4918,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" \ @@ -4581,7 +4935,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 @@ -4611,17 +4969,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" \ @@ -4631,7 +4978,8 @@ DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ SRST ``-runas user`` Immediately before starting guest execution, drop root privileges, - switching to the specified user. + switching to the specified user. This option is deprecated, use + ``-run-with user=...`` instead. ERST DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env, @@ -4656,13 +5004,14 @@ ERST DEF("semihosting", 0, QEMU_OPTION_semihosting, "-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | - QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV) + QEMU_ARCH_MIPS | 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, 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. @@ -4671,25 +5020,15 @@ DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config, "-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) +QEMU_ARCH_MIPS | QEMU_ARCH_RISCV) SRST ``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]`` - Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V + Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, 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 @@ -4804,24 +5143,42 @@ HXCOMM Internal use DEF("qtest", HAS_ARG, QEMU_OPTION_qtest, "", QEMU_ARCH_ALL) DEF("qtest-log", HAS_ARG, QEMU_OPTION_qtest_log, "", QEMU_ARCH_ALL) -#ifdef __linux__ -DEF("async-teardown", 0, QEMU_OPTION_asyncteardown, - "-async-teardown enable asynchronous teardown\n", +#ifdef CONFIG_POSIX +DEF("run-with", HAS_ARG, QEMU_OPTION_run_with, + "-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]\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" + " user=username switch to the specified user before starting the VM\n" + " user=uid:gid ditto, but use specified user-ID and group-ID instead\n", QEMU_ARCH_ALL) -#endif SRST -``-async-teardown`` - Enable 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. +``-run-with [async-teardown=on|off][,chroot=dir][user=username|uid:gid]`` + 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. + + ``user=username`` or ``user=uid:gid`` can be used to drop root privileges + before starting guest execution. QEMU will use the ``setuid`` and ``setgid`` + system calls to switch to the specified identity. Note that the + ``user=username`` syntax will also apply the full set of supplementary + groups for the user, whereas the ``user=uid:gid`` will use only the + ``gid`` group. ERST +#endif DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg [timestamp[=on|off]][,guest-name=[on|off]]\n" @@ -4864,6 +5221,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:) @@ -4881,7 +5258,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. @@ -4900,9 +5277,6 @@ SRST allows a co-operating external process to access the QEMU memory region. - The ``share`` is also required for pvrdma devices due to - limitations in the RDMA API provided by Linux. - Setting share=on might affect the ability to configure NUMA bindings for the memory backend under some circumstances, see Documentation/vm/numa\_memory\_policy.txt on the Linux kernel @@ -4951,6 +5325,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 @@ -4967,6 +5345,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 @@ -4999,6 +5391,34 @@ SRST The ``share`` boolean option is on by default with memfd. + ``-object memory-backend-shm,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 POSIX shared memory backend object, which allows + QEMU to share the memory with an external process (e.g. when + using vhost-user). + + ``memory-backend-shm`` is a more portable and less featureful version + of ``memory-backend-memfd``. It can then be used in any POSIX system, + especially when memfd is not supported. + + Please refer to ``memory-backend-file`` for a description of the + options. + + The ``share`` boolean option is on by default with shm. Setting it to + off will cause a failure during allocation because it is not supported + by this backend. + + ``-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 @@ -5244,7 +5664,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 @@ -5259,7 +5679,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 @@ -5270,7 +5690,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 @@ -5283,10 +5703,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 @@ -5427,7 +5847,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 @@ -5462,7 +5882,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 \\ ..... diff --git a/qemu.nsi b/qemu.nsi index 564d617d11..b186f223e1 100644 --- a/qemu.nsi +++ b/qemu.nsi @@ -7,7 +7,7 @@ ; 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) version 3 or any later version. +; (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 @@ -16,6 +16,8 @@ ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . +; +; SPDX-License-Identifier: GPL-2.0-or-later ; NSIS_WIN32_MAKENSIS diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 0c5175d957..465d688ecb 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -152,7 +152,7 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, #ifdef __FreeBSD__ /* * In the default state channel sends echo of every command to a - * client. The client programm doesn't expect this and raises an + * client. The client program doesn't expect this and raises an * error. Suppress echo by resetting ECHO terminal flag. */ struct termios tio; diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c index 15cade2d4c..94ff6fee6a 100644 --- a/qga/commands-bsd.c +++ b/qga/commands-bsd.c @@ -12,7 +12,6 @@ #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" @@ -21,7 +20,12 @@ #include #include #include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#else #include +#endif #include #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) @@ -144,30 +148,6 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp) } 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 diff --git a/qga/commands-common-ssh.c b/qga/commands-common-ssh.c new file mode 100644 index 0000000000..537869fb98 --- /dev/null +++ b/qga/commands-common-ssh.c @@ -0,0 +1,50 @@ +/* + * 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-ssh.h" + +GStrv read_authkeys(const char *path, Error **errp) +{ + g_autoptr(GError) err = NULL; + g_autofree char *contents = NULL; + + if (!g_file_get_contents(path, &contents, NULL, &err)) { + error_setg(errp, "failed to read '%s': %s", path, err->message); + return NULL; + } + + return g_strsplit(contents, "\n", -1); +} + +bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) +{ + size_t n = 0; + strList *k; + + for (k = keys; k != NULL; k = k->next) { + if (!check_openssh_pub_key(k->value, errp)) { + return false; + } + n++; + } + + if (nkeys) { + *nkeys = n; + } + return true; +} + +bool check_openssh_pub_key(const char *key, Error **errp) +{ + /* simple sanity-check, we may want more? */ + if (!key || key[0] == '#' || strchr(key, '\n')) { + error_setg(errp, "invalid OpenSSH public key: '%s'", key); + return false; + } + + return true; +} diff --git a/qga/commands-common-ssh.h b/qga/commands-common-ssh.h new file mode 100644 index 0000000000..14d955fa84 --- /dev/null +++ b/qga/commands-common-ssh.h @@ -0,0 +1,10 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qapi/qapi-builtin-types.h" + +GStrv read_authkeys(const char *path, Error **errp); +bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp); +bool check_openssh_pub_key(const char *key, Error **errp); diff --git a/qga/commands-common.h b/qga/commands-common.h index 8c1c56aac9..263e7c0525 100644 --- a/qga/commands-common.h +++ b/qga/commands-common.h @@ -15,19 +15,10 @@ #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) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 214e408fcd..cf077eb03d 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -13,10 +13,25 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qga-qapi-commands.h" +#include "qapi/error.h" #include "commands-common.h" #include "cutils.h" #include #include +#include +#include +#include "block/nvme.h" + +#ifdef CONFIG_LIBUDEV +#include +#endif + +#ifdef HAVE_GETIFADDRS +#include +#endif + +#include #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM) static int dev_major_minor(const char *devpath, @@ -284,3 +299,1921 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp) return i; } #endif /* CONFIG_FSFREEZE */ + +#if defined(CONFIG_FSFREEZE) + +static char *get_pci_driver(char const *syspath, int pathlen, Error **errp) +{ + char *path; + char *dpath; + char *driver = NULL; + char buf[PATH_MAX]; + ssize_t len; + + path = g_strndup(syspath, pathlen); + dpath = g_strdup_printf("%s/driver", path); + len = readlink(dpath, buf, sizeof(buf) - 1); + if (len != -1) { + buf[len] = 0; + driver = g_path_get_basename(buf); + } + g_free(dpath); + g_free(path); + return driver; +} + +static int compare_uint(const void *_a, const void *_b) +{ + unsigned int a = *(unsigned int *)_a; + unsigned int b = *(unsigned int *)_b; + + return a < b ? -1 : a > b ? 1 : 0; +} + +/* Walk the specified sysfs and build a sorted list of host or ata numbers */ +static int build_hosts(char const *syspath, char const *host, bool ata, + unsigned int *hosts, int hosts_max, Error **errp) +{ + char *path; + DIR *dir; + struct dirent *entry; + int i = 0; + + path = g_strndup(syspath, host - syspath); + dir = opendir(path); + if (!dir) { + error_setg_errno(errp, errno, "opendir(\"%s\")", path); + g_free(path); + return -1; + } + + while (i < hosts_max) { + entry = readdir(dir); + if (!entry) { + break; + } + if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) { + ++i; + } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) { + ++i; + } + } + + qsort(hosts, i, sizeof(hosts[0]), compare_uint); + + g_free(path); + closedir(dir); + return i; +} + +/* + * Store disk device info for devices on the PCI bus. + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_pci_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int pci[4], host, hosts[8], tgt[3]; + int i, nhosts = 0, pcilen; + GuestPCIAddress *pciaddr = disk->pci_controller; + bool has_ata = false, has_host = false, has_tgt = false; + char *p, *q, *driver = NULL; + bool ret = false; + + p = strstr(syspath, "/devices/pci"); + if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { + g_debug("only pci device is supported: sysfs path '%s'", syspath); + return false; + } + + p += 12 + pcilen; + while (true) { + driver = get_pci_driver(syspath, p - syspath, errp); + if (driver && (g_str_equal(driver, "ata_piix") || + g_str_equal(driver, "sym53c8xx") || + g_str_equal(driver, "virtio-pci") || + g_str_equal(driver, "ahci") || + g_str_equal(driver, "nvme") || + g_str_equal(driver, "xhci_hcd") || + g_str_equal(driver, "ehci-pci"))) { + break; + } + + g_free(driver); + if (sscanf(p, "/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { + p += pcilen; + continue; + } + + g_debug("unsupported driver or sysfs path '%s'", syspath); + return false; + } + + p = strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + tgt, tgt + 1, tgt + 2) == 3) { + has_tgt = true; + } + + p = strstr(syspath, "/ata"); + if (p) { + q = p + 4; + has_ata = true; + } else { + p = strstr(syspath, "/host"); + q = p + 5; + } + if (p && sscanf(q, "%u", &host) == 1) { + has_host = true; + nhosts = build_hosts(syspath, p, has_ata, hosts, + ARRAY_SIZE(hosts), errp); + if (nhosts < 0) { + goto cleanup; + } + } + + pciaddr->domain = pci[0]; + pciaddr->bus = pci[1]; + pciaddr->slot = pci[2]; + pciaddr->function = pci[3]; + + if (strcmp(driver, "ata_piix") == 0) { + /* a host per ide bus, target*:0::0 */ + if (!has_host || !has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + for (i = 0; i < nhosts; i++) { + if (host == hosts[i]) { + disk->bus_type = GUEST_DISK_BUS_TYPE_IDE; + disk->bus = i; + disk->unit = tgt[1]; + break; + } + } + if (i >= nhosts) { + g_debug("no host for '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + } else if (strcmp(driver, "sym53c8xx") == 0) { + /* scsi(LSI Logic): target*:0::0 */ + if (!has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->unit = tgt[1]; + } else if (strcmp(driver, "virtio-pci") == 0) { + if (has_tgt) { + /* virtio-scsi: target*:0:0: */ + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->unit = tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; + } + } else if (strcmp(driver, "ahci") == 0) { + /* ahci: 1 host per 1 unit */ + if (!has_host || !has_tgt) { + g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + for (i = 0; i < nhosts; i++) { + if (host == hosts[i]) { + disk->unit = i; + disk->bus_type = GUEST_DISK_BUS_TYPE_SATA; + break; + } + } + if (i >= nhosts) { + g_debug("no host for '%s' (driver '%s')", syspath, driver); + goto cleanup; + } + } else if (strcmp(driver, "nvme") == 0) { + disk->bus_type = GUEST_DISK_BUS_TYPE_NVME; + } else 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; + } + + ret = true; + +cleanup: + g_free(driver); + return ret; +} + +/* + * Store disk device info for non-PCI virtio devices (for example s390x + * channel I/O devices). Returns true if information has been stored, or + * false for failure. + */ +static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int tgt[3]; + char *p; + + if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { + g_debug("Unsupported virtio device '%s'", syspath); + return false; + } + + p = strstr(syspath, "/target"); + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", + &tgt[0], &tgt[1], &tgt[2]) == 3) { + /* virtio-scsi: target*:0:: */ + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; + disk->bus = tgt[0]; + disk->target = tgt[1]; + disk->unit = tgt[2]; + } else { + /* virtio-blk: 1 disk per 1 device */ + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; + } + + return true; +} + +/* + * Store disk device info for CCW devices (s390x channel I/O devices). + * Returns true if information has been stored, or false for failure. + */ +static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, + GuestDiskAddress *disk, + Error **errp) +{ + unsigned int cssid, ssid, subchno, devno; + char *p; + + p = strstr(syspath, "/devices/css"); + if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/", + &cssid, &ssid, &subchno, &devno) < 4) { + g_debug("could not parse ccw device sysfs path: %s", syspath); + return false; + } + + disk->ccw_address = g_new0(GuestCCWAddress, 1); + disk->ccw_address->cssid = cssid; + disk->ccw_address->ssid = ssid; + disk->ccw_address->subchno = subchno; + disk->ccw_address->devno = devno; + + if (strstr(p, "/virtio")) { + build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); + } + + return true; +} + +/* Store disk device info specified by @sysfs into @fs */ +static void build_guest_fsinfo_for_real_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + GuestDiskAddress *disk; + GuestPCIAddress *pciaddr; + bool has_hwinf; +#ifdef CONFIG_LIBUDEV + struct udev *udev = NULL; + struct udev_device *udevice = NULL; +#endif + + pciaddr = g_new0(GuestPCIAddress, 1); + pciaddr->domain = -1; /* -1 means field is invalid */ + pciaddr->bus = -1; + pciaddr->slot = -1; + pciaddr->function = -1; + + disk = g_new0(GuestDiskAddress, 1); + disk->pci_controller = pciaddr; + disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN; + +#ifdef CONFIG_LIBUDEV + udev = udev_new(); + udevice = udev_device_new_from_syspath(udev, syspath); + if (udev == NULL || udevice == NULL) { + g_debug("failed to query udev"); + } else { + const char *devnode, *serial; + devnode = udev_device_get_devnode(udevice); + if (devnode != NULL) { + disk->dev = g_strdup(devnode); + } + serial = udev_device_get_property_value(udevice, "ID_SERIAL"); + if (serial != NULL && *serial != 0) { + disk->serial = g_strdup(serial); + } + } + + udev_unref(udev); + udev_device_unref(udevice); +#endif + + if (strstr(syspath, "/devices/pci")) { + has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp); + } else if (strstr(syspath, "/devices/css")) { + has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp); + } else if (strstr(syspath, "/virtio")) { + has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); + } else { + g_debug("Unsupported device type for '%s'", syspath); + has_hwinf = false; + } + + if (has_hwinf || disk->dev || disk->serial) { + QAPI_LIST_PREPEND(fs->disk, disk); + } else { + qapi_free_GuestDiskAddress(disk); + } +} + +static void build_guest_fsinfo_for_device(char const *devpath, + GuestFilesystemInfo *fs, + Error **errp); + +/* Store a list of slave devices of virtual volume specified by @syspath into + * @fs */ +static void build_guest_fsinfo_for_virtual_device(char const *syspath, + GuestFilesystemInfo *fs, + Error **errp) +{ + Error *err = NULL; + DIR *dir; + char *dirpath; + struct dirent *entry; + + dirpath = g_strdup_printf("%s/slaves", syspath); + dir = opendir(dirpath); + if (!dir) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath); + } + g_free(dirpath); + return; + } + + for (;;) { + errno = 0; + entry = readdir(dir); + if (entry == NULL) { + if (errno) { + error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath); + } + break; + } + + if (entry->d_type == DT_LNK) { + char *path; + + g_debug(" slave device '%s'", entry->d_name); + path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name); + build_guest_fsinfo_for_device(path, fs, &err); + g_free(path); + + if (err) { + error_propagate(errp, err); + break; + } + } + } + + g_free(dirpath); + closedir(dir); +} + +static bool is_disk_virtual(const char *devpath, Error **errp) +{ + g_autofree char *syspath = realpath(devpath, NULL); + + if (!syspath) { + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); + return false; + } + return strstr(syspath, "/devices/virtual/block/") != NULL; +} + +/* Dispatch to functions for virtual/real device */ +static void build_guest_fsinfo_for_device(char const *devpath, + GuestFilesystemInfo *fs, + Error **errp) +{ + ERRP_GUARD(); + g_autofree char *syspath = NULL; + bool is_virtual = false; + + syspath = realpath(devpath, NULL); + if (!syspath) { + 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; + } + + if (!fs->name) { + fs->name = g_path_get_basename(syspath); + } + + g_debug(" parse sysfs path '%s'", syspath); + is_virtual = is_disk_virtual(syspath, errp); + if (*errp != NULL) { + return; + } + if (is_virtual) { + build_guest_fsinfo_for_virtual_device(syspath, fs, errp); + } else { + build_guest_fsinfo_for_real_device(syspath, fs, errp); + } +} + +#ifdef CONFIG_LIBUDEV + +/* + * Wrapper around build_guest_fsinfo_for_device() for getting just + * the disk address. + */ +static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp) +{ + g_autoptr(GuestFilesystemInfo) fs = NULL; + + fs = g_new0(GuestFilesystemInfo, 1); + build_guest_fsinfo_for_device(syspath, fs, errp); + if (fs->disk != NULL) { + return g_steal_pointer(&fs->disk->value); + } + return NULL; +} + +static char *get_alias_for_syspath(const char *syspath) +{ + struct udev *udev = NULL; + struct udev_device *udevice = NULL; + char *ret = NULL; + + udev = udev_new(); + if (udev == NULL) { + g_debug("failed to query udev"); + goto out; + } + udevice = udev_device_new_from_syspath(udev, syspath); + if (udevice == NULL) { + g_debug("failed to query udev for path: %s", syspath); + goto out; + } else { + const char *alias = udev_device_get_property_value( + udevice, "DM_NAME"); + /* + * NULL means there was an error and empty string means there is no + * alias. In case of no alias we return NULL instead of empty string. + */ + if (alias == NULL) { + g_debug("failed to query udev for device alias for: %s", + syspath); + } else if (*alias != 0) { + ret = g_strdup(alias); + } + } + +out: + udev_unref(udev); + udev_device_unref(udevice); + return ret; +} + +static char *get_device_for_syspath(const char *syspath) +{ + struct udev *udev = NULL; + struct udev_device *udevice = NULL; + char *ret = NULL; + + udev = udev_new(); + if (udev == NULL) { + g_debug("failed to query udev"); + goto out; + } + udevice = udev_device_new_from_syspath(udev, syspath); + if (udevice == NULL) { + g_debug("failed to query udev for path: %s", syspath); + goto out; + } else { + ret = g_strdup(udev_device_get_devnode(udevice)); + } + +out: + udev_unref(udev); + udev_device_unref(udevice); + return ret; +} + +static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk) +{ + g_autofree char *deps_dir = NULL; + const gchar *dep; + GDir *dp_deps = NULL; + + /* List dependent disks */ + deps_dir = g_strdup_printf("%s/slaves", disk_dir); + g_debug(" listing entries in: %s", deps_dir); + dp_deps = g_dir_open(deps_dir, 0, NULL); + if (dp_deps == NULL) { + g_debug("failed to list entries in %s", deps_dir); + return; + } + disk->has_dependencies = true; + while ((dep = g_dir_read_name(dp_deps)) != NULL) { + g_autofree char *dep_dir = NULL; + char *dev_name; + + /* Add dependent disks */ + dep_dir = g_strdup_printf("%s/%s", deps_dir, dep); + dev_name = get_device_for_syspath(dep_dir); + if (dev_name != NULL) { + g_debug(" adding dependent device: %s", dev_name); + QAPI_LIST_PREPEND(disk->dependencies, dev_name); + } + } + g_dir_close(dp_deps); +} + +/* + * Detect partitions subdirectory, name is "" or + * "p" + * + * @disk_name -- last component of /sys path (e.g. sda) + * @disk_dir -- sys path of the disk (e.g. /sys/block/sda) + * @disk_dev -- device node of the disk (e.g. /dev/sda) + */ +static GuestDiskInfoList *get_disk_partitions( + GuestDiskInfoList *list, + const char *disk_name, const char *disk_dir, + const char *disk_dev) +{ + GuestDiskInfoList *ret = list; + struct dirent *de_disk; + DIR *dp_disk = NULL; + size_t len = strlen(disk_name); + + dp_disk = opendir(disk_dir); + while ((de_disk = readdir(dp_disk)) != NULL) { + g_autofree char *partition_dir = NULL; + char *dev_name; + GuestDiskInfo *partition; + + if (!(de_disk->d_type & DT_DIR)) { + continue; + } + + if (!(strncmp(disk_name, de_disk->d_name, len) == 0 && + ((*(de_disk->d_name + len) == 'p' && + isdigit(*(de_disk->d_name + len + 1))) || + isdigit(*(de_disk->d_name + len))))) { + continue; + } + + partition_dir = g_strdup_printf("%s/%s", + disk_dir, de_disk->d_name); + dev_name = get_device_for_syspath(partition_dir); + if (dev_name == NULL) { + g_debug("Failed to get device name for syspath: %s", + disk_dir); + continue; + } + partition = g_new0(GuestDiskInfo, 1); + partition->name = dev_name; + partition->partition = true; + partition->has_dependencies = true; + /* Add parent disk as dependent for easier tracking of hierarchy */ + QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev)); + + QAPI_LIST_PREPEND(ret, partition); + } + closedir(dp_disk); + + return ret; +} + +static void get_nvme_smart(GuestDiskInfo *disk) +{ + int fd; + GuestNVMeSmart *smart; + NvmeSmartLog log = {0}; + struct nvme_admin_cmd cmd = { + .opcode = NVME_ADM_CMD_GET_LOG_PAGE, + .nsid = NVME_NSID_BROADCAST, + .addr = (uintptr_t)&log, + .data_len = sizeof(log), + .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ + | (((sizeof(log) >> 2) - 1) << 16) + }; + + fd = qga_open_cloexec(disk->name, O_RDONLY, 0); + if (fd == -1) { + g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno)); + return; + } + + if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { + g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno)); + close(fd); + return; + } + + disk->smart = g_new0(GuestDiskSmart, 1); + disk->smart->type = GUEST_DISK_BUS_TYPE_NVME; + + smart = &disk->smart->u.nvme; + smart->critical_warning = log.critical_warning; + smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */ + smart->available_spare = log.available_spare; + smart->available_spare_threshold = log.available_spare_threshold; + smart->percentage_used = log.percentage_used; + smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]); + smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]); + smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]); + smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]); + smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]); + smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]); + smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]); + smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]); + smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]); + smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]); + smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]); + smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]); + smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]); + smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]); + smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]); + smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]); + smart->media_errors_lo = le64_to_cpu(log.media_errors[0]); + smart->media_errors_hi = le64_to_cpu(log.media_errors[1]); + smart->number_of_error_log_entries_lo = + le64_to_cpu(log.number_of_error_log_entries[0]); + smart->number_of_error_log_entries_hi = + le64_to_cpu(log.number_of_error_log_entries[1]); + + close(fd); +} + +static void get_disk_smart(GuestDiskInfo *disk) +{ + if (disk->address + && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) { + get_nvme_smart(disk); + } +} + +GuestDiskInfoList *qmp_guest_get_disks(Error **errp) +{ + GuestDiskInfoList *ret = NULL; + GuestDiskInfo *disk; + DIR *dp = NULL; + struct dirent *de = NULL; + + g_debug("listing /sys/block directory"); + dp = opendir("/sys/block"); + if (dp == NULL) { + error_setg_errno(errp, errno, "Can't open directory \"/sys/block\""); + return NULL; + } + while ((de = readdir(dp)) != NULL) { + g_autofree char *disk_dir = NULL, *line = NULL, + *size_path = NULL; + char *dev_name; + Error *local_err = NULL; + if (de->d_type != DT_LNK) { + g_debug(" skipping entry: %s", de->d_name); + continue; + } + + /* Check size and skip zero-sized disks */ + g_debug(" checking disk size"); + size_path = g_strdup_printf("/sys/block/%s/size", de->d_name); + if (!g_file_get_contents(size_path, &line, NULL, NULL)) { + g_debug(" failed to read disk size"); + continue; + } + if (g_strcmp0(line, "0\n") == 0) { + g_debug(" skipping zero-sized disk"); + continue; + } + + g_debug(" adding %s", de->d_name); + disk_dir = g_strdup_printf("/sys/block/%s", de->d_name); + dev_name = get_device_for_syspath(disk_dir); + if (dev_name == NULL) { + g_debug("Failed to get device name for syspath: %s", + disk_dir); + continue; + } + disk = g_new0(GuestDiskInfo, 1); + disk->name = dev_name; + disk->partition = false; + disk->alias = get_alias_for_syspath(disk_dir); + QAPI_LIST_PREPEND(ret, disk); + + /* Get address for non-virtual devices */ + bool is_virtual = is_disk_virtual(disk_dir, &local_err); + if (local_err != NULL) { + g_debug(" failed to check disk path, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + /* Don't try to get the address */ + is_virtual = true; + } + if (!is_virtual) { + disk->address = get_disk_address(disk_dir, &local_err); + if (local_err != NULL) { + g_debug(" failed to get device info, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + } + } + + get_disk_deps(disk_dir, disk); + get_disk_smart(disk); + ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name); + } + + closedir(dp); + + return ret; +} + +#endif + +/* Return a list of the disk device(s)' info which @mount lies on */ +static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, + Error **errp) +{ + GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); + struct statvfs buf; + unsigned long used, nonroot_total, fr_size; + char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", + mount->devmajor, mount->devminor); + + fs->mountpoint = g_strdup(mount->dirname); + fs->type = g_strdup(mount->devtype); + build_guest_fsinfo_for_device(devpath, fs, errp); + + if (statvfs(fs->mountpoint, &buf) == 0) { + fr_size = buf.f_frsize; + used = buf.f_blocks - buf.f_bfree; + nonroot_total = used + buf.f_bavail; + fs->used_bytes = used * fr_size; + fs->total_bytes = nonroot_total * fr_size; + fs->total_bytes_privileged = buf.f_blocks * fr_size; + + fs->has_total_bytes = true; + fs->has_total_bytes_privileged = true; + fs->has_used_bytes = true; + } + + g_free(devpath); + + return fs; +} + +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) +{ + FsMountList mounts; + struct FsMount *mount; + GuestFilesystemInfoList *ret = NULL; + Error *local_err = NULL; + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, &local_err)) { + error_propagate(errp, local_err); + return NULL; + } + + QTAILQ_FOREACH(mount, &mounts, next) { + g_debug("Building guest fsinfo for '%s'", mount->dirname); + + QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err)); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_GuestFilesystemInfoList(ret); + ret = NULL; + break; + } + } + + free_fs_mount_list(&mounts); + return ret; +} +#endif /* CONFIG_FSFREEZE */ + +#if defined(CONFIG_FSTRIM) +/* + * Walk list of mounted file systems in the guest, and trim them. + */ +GuestFilesystemTrimResponse * +qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) +{ + GuestFilesystemTrimResponse *response; + GuestFilesystemTrimResult *result; + int ret = 0; + FsMountList mounts; + struct FsMount *mount; + int fd; + struct fstrim_range r; + + slog("guest-fstrim called"); + + QTAILQ_INIT(&mounts); + if (!build_fs_mount_list(&mounts, errp)) { + return NULL; + } + + response = g_malloc0(sizeof(*response)); + + QTAILQ_FOREACH(mount, &mounts, next) { + result = g_malloc0(sizeof(*result)); + result->path = g_strdup(mount->dirname); + + QAPI_LIST_PREPEND(response->paths, result); + + fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); + if (fd == -1) { + result->error = g_strdup_printf("failed to open: %s", + strerror(errno)); + continue; + } + + /* We try to cull filesystems we know won't work in advance, but other + * filesystems may not implement fstrim for less obvious reasons. + * These will report EOPNOTSUPP; while in some other cases ENOTTY + * will be reported (e.g. CD-ROMs). + * Any other error means an unexpected error. + */ + r.start = 0; + r.len = -1; + r.minlen = has_minimum ? minimum : 0; + ret = ioctl(fd, FITRIM, &r); + if (ret == -1) { + if (errno == ENOTTY || errno == EOPNOTSUPP) { + result->error = g_strdup("trim not supported"); + } else { + result->error = g_strdup_printf("failed to trim: %s", + strerror(errno)); + } + close(fd); + continue; + } + + result->has_minimum = true; + result->minimum = r.minlen; + result->has_trimmed = true; + result->trimmed = r.len; + close(fd); + } + + free_fs_mount_list(&mounts); + return response; +} +#endif /* CONFIG_FSTRIM */ + +#define LINUX_SYS_STATE_FILE "/sys/power/state" +#define SUSPEND_SUPPORTED 0 +#define SUSPEND_NOT_SUPPORTED 1 + +typedef enum { + SUSPEND_MODE_DISK = 0, + SUSPEND_MODE_RAM = 1, + SUSPEND_MODE_HYBRID = 2, +} SuspendMode; + +/* + * Executes a command in a child process using g_spawn_sync, + * returning an int >= 0 representing the exit status of the + * process. + * + * If the program wasn't found in path, returns -1. + * + * If a problem happened when creating the child process, + * returns -1 and errp is set. + */ +static int run_process_child(const char *command[], Error **errp) +{ + int exit_status, spawn_flag; + GError *g_err = NULL; + bool success; + + spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL; + + success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, + NULL, NULL, NULL, NULL, + &exit_status, &g_err); + + if (success) { + return WEXITSTATUS(exit_status); + } + + if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { + error_setg(errp, "failed to create child process, error '%s'", + g_err->message); + } + + g_error_free(g_err); + return -1; +} + +static bool systemd_supports_mode(SuspendMode mode, Error **errp) +{ + const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", + "systemd-hybrid-sleep"}; + const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, errp); + + /* + * systemctl status uses LSB return codes so we can expect + * status > 0 and be ok. To assert if the guest has support + * for the selected suspend mode, status should be < 4. 4 is + * the code for unknown service status, the return value when + * the service does not exist. A common value is status = 3 + * (program is not running). + */ + if (status > 0 && status < 4) { + return true; + } + + return false; +} + +static void systemd_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; + const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program 'systemctl %s' was not found", + systemctl_args[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "the helper program 'systemctl %s' returned an " + "unexpected exit status code (%d)", + systemctl_args[mode], status); + } +} + +static bool pmutils_supports_mode(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_args[3] = {"--hibernate", "--suspend", + "--suspend-hybrid"}; + const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == SUSPEND_SUPPORTED) { + return true; + } + + if ((status == -1) && !local_err) { + return false; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", "pm-is-supported", status); + } + + return false; +} + +static void pmutils_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", + "pm-suspend-hybrid"}; + const char *cmd[2] = {pmutils_binaries[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program '%s' was not found", + pmutils_binaries[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", pmutils_binaries[mode], status); + } +} + +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) +{ + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; + char buf[32]; /* hopefully big enough */ + int fd; + ssize_t ret; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return false; + } + + fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); + if (fd < 0) { + return false; + } + + ret = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (ret <= 0) { + return false; + } + buf[ret] = '\0'; + + if (strstr(buf, sysfile_str)) { + return true; + } + return false; +} + +static void linux_sys_state_suspend(SuspendMode mode, Error **errp) +{ + g_autoptr(GError) local_gerr = NULL; + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return; + } + + if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str, + -1, &local_gerr)) { + error_setg(errp, "suspend: cannot write to '%s': %s", + LINUX_SYS_STATE_FILE, local_gerr->message); + return; + } +} + +static void guest_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + bool mode_supported = false; + + if (systemd_supports_mode(mode, &local_err)) { + mode_supported = true; + systemd_suspend(mode, &local_err); + + if (!local_err) { + return; + } + } + + error_free(local_err); + local_err = NULL; + + if (pmutils_supports_mode(mode, &local_err)) { + mode_supported = true; + pmutils_suspend(mode, &local_err); + + if (!local_err) { + return; + } + } + + error_free(local_err); + local_err = NULL; + + if (linux_sys_state_supports_mode(mode, &local_err)) { + mode_supported = true; + linux_sys_state_suspend(mode, &local_err); + } + + if (!mode_supported) { + error_free(local_err); + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + } else { + error_propagate(errp, local_err); + } +} + +void qmp_guest_suspend_disk(Error **errp) +{ + guest_suspend(SUSPEND_MODE_DISK, errp); +} + +void qmp_guest_suspend_ram(Error **errp) +{ + guest_suspend(SUSPEND_MODE_RAM, errp); +} + +void qmp_guest_suspend_hybrid(Error **errp) +{ + guest_suspend(SUSPEND_MODE_HYBRID, errp); +} + +/* Transfer online/offline status between @vcpu and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@vcpu direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - W: vcpu->online + * - W: vcpu->can_offline + * + * In @vcpu-to-system direction, the following @vcpu fields are accessed: + * - R: vcpu->logical_id + * - R: vcpu->online + * + * Written members remain unmodified on error. + */ +static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, + char *dirpath, Error **errp) +{ + int fd; + int res; + int dirfd; + static const char fn[] = "online"; + + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + return; + } + + fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); + if (fd == -1) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); + } else if (sys2vcpu) { + vcpu->online = true; + vcpu->can_offline = false; + } else if (!vcpu->online) { + error_setg(errp, "logical processor #%" PRId64 " can't be " + "offlined", vcpu->logical_id); + } /* otherwise pretend successful re-onlining */ + } else { + unsigned char status; + + res = pread(fd, &status, 1, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); + } else if (res == 0) { + error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, + fn); + } else if (sys2vcpu) { + vcpu->online = (status != '0'); + vcpu->can_offline = true; + } else if (vcpu->online != (status != '0')) { + status = '0' + vcpu->online; + if (pwrite(fd, &status, 1, 0) == -1) { + error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, + fn); + } + } /* otherwise pretend successful re-(on|off)-lining */ + + res = close(fd); + g_assert(res == 0); + } + + res = close(dirfd); + g_assert(res == 0); +} + +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) +{ + GuestLogicalProcessorList *head, **tail; + const char *cpu_dir = "/sys/devices/system/cpu"; + const gchar *line; + g_autoptr(GDir) cpu_gdir = NULL; + Error *local_err = NULL; + + head = NULL; + tail = &head; + cpu_gdir = g_dir_open(cpu_dir, 0, NULL); + + if (cpu_gdir == NULL) { + error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir); + return NULL; + } + + while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) { + GuestLogicalProcessor *vcpu; + int64_t id; + if (sscanf(line, "cpu%" PRId64, &id)) { + g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/" + "cpu%" PRId64 "/", id); + vcpu = g_malloc0(sizeof *vcpu); + vcpu->logical_id = id; + vcpu->has_can_offline = true; /* lolspeak ftw */ + transfer_vcpu(vcpu, true, path, &local_err); + QAPI_LIST_APPEND(tail, vcpu); + } + } + + if (local_err == NULL) { + /* there's no guest with zero VCPUs */ + g_assert(head != NULL); + return head; + } + + qapi_free_GuestLogicalProcessorList(head); + error_propagate(errp, local_err); + return NULL; +} + +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) +{ + int64_t processed; + Error *local_err = NULL; + + processed = 0; + while (vcpus != NULL) { + char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", + vcpus->value->logical_id); + + transfer_vcpu(vcpus->value, false, path, &local_err); + g_free(path); + if (local_err != NULL) { + break; + } + ++processed; + vcpus = vcpus->next; + } + + if (local_err != NULL) { + if (processed == 0) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + } + } + + return processed; +} + + +static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, + int size, Error **errp) +{ + int fd; + int res; + + errno = 0; + fd = openat(dirfd, pathname, O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + res = pread(fd, buf, size, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); + } else if (res == 0) { + error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname); + } + close(fd); +} + +static void ga_write_sysfs_file(int dirfd, const char *pathname, + const char *buf, int size, Error **errp) +{ + int fd; + + errno = 0; + fd = openat(dirfd, pathname, O_WRONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + if (pwrite(fd, buf, size, 0) == -1) { + error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname); + } + + close(fd); +} + +/* Transfer online/offline status between @mem_blk and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@mem_blk direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - W: mem_blk->online + * - W: mem_blk->can_offline + * + * In @mem_blk-to-system direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - R: mem_blk->online + *- R: mem_blk->can_offline + * Written members remain unmodified on error. + */ +static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, + GuestMemoryBlockResponse *result, + Error **errp) +{ + char *dirpath; + int dirfd; + char *status; + Error *local_err = NULL; + + if (!sys2memblk) { + DIR *dp; + + if (!result) { + error_setg(errp, "Internal error, 'result' should not be NULL"); + return; + } + errno = 0; + dp = opendir("/sys/devices/system/memory/"); + /* if there is no 'memory' directory in sysfs, + * we think this VM does not support online/offline memory block, + * any other solution? + */ + if (!dp) { + if (errno == ENOENT) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + } + goto out1; + } + closedir(dp); + } + + dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/", + mem_blk->phys_index); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + if (sys2memblk) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + } else { + if (errno == ENOENT) { + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND; + } else { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + g_free(dirpath); + goto out1; + } + g_free(dirpath); + + status = g_malloc0(10); + ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); + if (local_err) { + /* treat with sysfs file that not exist in old kernel */ + if (errno == ENOENT) { + error_free(local_err); + if (sys2memblk) { + mem_blk->online = true; + mem_blk->can_offline = false; + } else if (!mem_blk->online) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + } + } else { + if (sys2memblk) { + error_propagate(errp, local_err); + } else { + error_free(local_err); + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + goto out2; + } + + if (sys2memblk) { + char removable = '0'; + + mem_blk->online = (strncmp(status, "online", 6) == 0); + + ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); + if (local_err) { + /* if no 'removable' file, it doesn't support offline mem blk */ + if (errno == ENOENT) { + error_free(local_err); + mem_blk->can_offline = false; + } else { + error_propagate(errp, local_err); + } + } else { + mem_blk->can_offline = (removable != '0'); + } + } else { + if (mem_blk->online != (strncmp(status, "online", 6) == 0)) { + const char *new_state = mem_blk->online ? "online" : "offline"; + + ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state), + &local_err); + if (local_err) { + error_free(local_err); + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + goto out2; + } + + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; + result->has_error_code = false; + } /* otherwise pretend successful re-(on|off)-lining */ + } + g_free(status); + close(dirfd); + return; + +out2: + g_free(status); + close(dirfd); +out1: + if (!sys2memblk) { + result->has_error_code = true; + result->error_code = errno; + } +} + +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + GuestMemoryBlockList *head, **tail; + Error *local_err = NULL; + struct dirent *de; + DIR *dp; + + head = NULL; + tail = &head; + + dp = opendir("/sys/devices/system/memory/"); + if (!dp) { + /* it's ok if this happens to be a system that doesn't expose + * memory blocks via sysfs, but otherwise we should report + * an error + */ + if (errno != ENOENT) { + error_setg_errno(errp, errno, "Can't open directory" + "\"/sys/devices/system/memory/\""); + } + return NULL; + } + + /* Note: the phys_index of memory block may be discontinuous, + * this is because a memblk is the unit of the Sparse Memory design, which + * allows discontinuous memory ranges (ex. NUMA), so here we should + * traverse the memory block directory. + */ + while ((de = readdir(dp)) != NULL) { + GuestMemoryBlock *mem_blk; + + if ((strncmp(de->d_name, "memory", 6) != 0) || + !(de->d_type & DT_DIR)) { + continue; + } + + mem_blk = g_malloc0(sizeof *mem_blk); + /* The d_name is "memoryXXX", phys_index is block id, same as XXX */ + mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10); + mem_blk->has_can_offline = true; /* lolspeak ftw */ + transfer_memory_block(mem_blk, true, NULL, &local_err); + if (local_err) { + break; + } + + QAPI_LIST_APPEND(tail, mem_blk); + } + + closedir(dp); + if (local_err == NULL) { + /* there's no guest with zero memory blocks */ + if (head == NULL) { + error_setg(errp, "guest reported zero memory blocks!"); + } + return head; + } + + qapi_free_GuestMemoryBlockList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + GuestMemoryBlockResponseList *head, **tail; + Error *local_err = NULL; + + head = NULL; + tail = &head; + + while (mem_blks != NULL) { + GuestMemoryBlockResponse *result; + GuestMemoryBlock *current_mem_blk = mem_blks->value; + + result = g_malloc0(sizeof(*result)); + result->phys_index = current_mem_blk->phys_index; + transfer_memory_block(current_mem_blk, false, result, &local_err); + if (local_err) { /* should never happen */ + goto err; + } + + QAPI_LIST_APPEND(tail, result); + mem_blks = mem_blks->next; + } + + return head; +err: + qapi_free_GuestMemoryBlockResponseList(head); + error_propagate(errp, local_err); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + Error *local_err = NULL; + char *dirpath; + int dirfd; + char *buf; + GuestMemoryBlockInfo *info; + + dirpath = g_strdup_printf("/sys/devices/system/memory/"); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + g_free(dirpath); + return NULL; + } + g_free(dirpath); + + buf = g_malloc0(20); + ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); + close(dirfd); + if (local_err) { + g_free(buf); + error_propagate(errp, local_err); + return NULL; + } + + info = g_new0(GuestMemoryBlockInfo, 1); + info->size = strtol(buf, NULL, 16); /* the unit is bytes */ + + g_free(buf); + + return info; +} + +#define MAX_NAME_LEN 128 +static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) +{ + GuestDiskStatsInfoList *head = NULL, **tail = &head; + const char *diskstats = "/proc/diskstats"; + FILE *fp; + size_t n; + char *line = NULL; + + fp = fopen(diskstats, "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", diskstats); + return NULL; + } + + while (getline(&line, &n, fp) != -1) { + g_autofree GuestDiskStatsInfo *diskstatinfo = NULL; + g_autofree GuestDiskStats *diskstat = NULL; + char dev_name[MAX_NAME_LEN]; + unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks; + unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios; + unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; + unsigned long dc_ios, dc_merges, dc_sec, fl_ios; + unsigned int major, minor; + int i; + + i = sscanf(line, "%u %u %s %lu %lu %lu" + "%lu %lu %lu %lu %u %u %u %u" + "%lu %lu %lu %u %lu %u", + &major, &minor, dev_name, + &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, + &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, + &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks, + &dc_ios, &dc_merges, &dc_sec, &dc_ticks, + &fl_ios, &fl_ticks); + + if (i < 7) { + continue; + } + + diskstatinfo = g_new0(GuestDiskStatsInfo, 1); + diskstatinfo->name = g_strdup(dev_name); + diskstatinfo->major = major; + diskstatinfo->minor = minor; + + diskstat = g_new0(GuestDiskStats, 1); + if (i == 7) { + diskstat->has_read_ios = true; + diskstat->read_ios = rd_ios; + diskstat->has_read_sectors = true; + diskstat->read_sectors = rd_merges_or_rd_sec; + diskstat->has_write_ios = true; + diskstat->write_ios = rd_sec_or_wr_ios; + diskstat->has_write_sectors = true; + diskstat->write_sectors = rd_ticks_or_wr_sec; + } + if (i >= 14) { + diskstat->has_read_ios = true; + diskstat->read_ios = rd_ios; + diskstat->has_read_sectors = true; + diskstat->read_sectors = rd_sec_or_wr_ios; + diskstat->has_read_merges = true; + diskstat->read_merges = rd_merges_or_rd_sec; + diskstat->has_read_ticks = true; + diskstat->read_ticks = rd_ticks_or_wr_sec; + diskstat->has_write_ios = true; + diskstat->write_ios = wr_ios; + diskstat->has_write_sectors = true; + diskstat->write_sectors = wr_sec; + diskstat->has_write_merges = true; + diskstat->write_merges = wr_merges; + diskstat->has_write_ticks = true; + diskstat->write_ticks = wr_ticks; + diskstat->has_ios_pgr = true; + diskstat->ios_pgr = ios_pgr; + diskstat->has_total_ticks = true; + diskstat->total_ticks = tot_ticks; + diskstat->has_weight_ticks = true; + diskstat->weight_ticks = rq_ticks; + } + if (i >= 18) { + diskstat->has_discard_ios = true; + diskstat->discard_ios = dc_ios; + diskstat->has_discard_merges = true; + diskstat->discard_merges = dc_merges; + diskstat->has_discard_sectors = true; + diskstat->discard_sectors = dc_sec; + diskstat->has_discard_ticks = true; + diskstat->discard_ticks = dc_ticks; + } + if (i >= 20) { + diskstat->has_flush_ios = true; + diskstat->flush_ios = fl_ios; + diskstat->has_flush_ticks = true; + diskstat->flush_ticks = fl_ticks; + } + + diskstatinfo->stats = g_steal_pointer(&diskstat); + QAPI_LIST_APPEND(tail, diskstatinfo); + diskstatinfo = NULL; + } + free(line); + fclose(fp); + return head; +} + +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; +} + +static char *hex_to_ip_address(const void *hex_value, int is_ipv6) +{ + if (is_ipv6) { + char addr[INET6_ADDRSTRLEN]; + struct in6_addr in6; + const char *hex_str = (const char *)hex_value; + int i; + + for (i = 0; i < 16; i++) { + if (sscanf(&hex_str[i * 2], "%02hhx", &in6.s6_addr[i]) != 1) { + return NULL; + } + } + inet_ntop(AF_INET6, &in6, addr, INET6_ADDRSTRLEN); + + return g_strdup(addr); + } else { + unsigned int hex_int = *(unsigned int *)hex_value; + unsigned int byte1 = (hex_int >> 24) & 0xFF; + unsigned int byte2 = (hex_int >> 16) & 0xFF; + unsigned int byte3 = (hex_int >> 8) & 0xFF; + unsigned int byte4 = hex_int & 0xFF; + + return g_strdup_printf("%u.%u.%u.%u", byte4, byte3, byte2, byte1); + } +} + +GuestNetworkRouteList *qmp_guest_network_get_route(Error **errp) +{ + GuestNetworkRouteList *head = NULL, **tail = &head; + const char *route_files[] = {"/proc/net/route", "/proc/net/ipv6_route"}; + FILE *fp; + size_t n = 0; + char *line = NULL; + int firstLine; + int is_ipv6; + int i; + char iface[IFNAMSIZ]; + + for (i = 0; i < 2; i++) { + firstLine = 1; + is_ipv6 = (i == 1); + fp = fopen(route_files[i], "r"); + if (fp == NULL) { + error_setg_errno(errp, errno, "open(\"%s\")", route_files[i]); + continue; + } + + while (getline(&line, &n, fp) != -1) { + if (firstLine && !is_ipv6) { + firstLine = 0; + continue; + } + g_autoptr(GuestNetworkRoute) route = g_new0(GuestNetworkRoute, 1); + + if (is_ipv6) { + char destination[33], source[33], next_hop[33]; + int des_prefixlen, src_prefixlen, metric, refcnt, use, flags; + if (sscanf(line, "%32s %x %32s %x %32s %x %x %x %x %s", + destination, &des_prefixlen, source, + &src_prefixlen, next_hop, &metric, &refcnt, + &use, &flags, iface) != 10) { + continue; + } + + route->destination = hex_to_ip_address(destination, 1); + if (route->destination == NULL) { + continue; + } + route->iface = g_strdup(iface); + route->source = hex_to_ip_address(source, 1); + route->nexthop = hex_to_ip_address(next_hop, 1); + route->desprefixlen = g_strdup_printf("%d", des_prefixlen); + route->srcprefixlen = g_strdup_printf("%d", src_prefixlen); + route->metric = metric; + route->has_flags = true; + route->flags = flags; + route->has_refcnt = true; + route->refcnt = refcnt; + route->has_use = true; + route->use = use; + route->version = 6; + } else { + unsigned int destination, gateway, mask, flags; + int refcnt, use, metric, mtu, window, irtt; + if (sscanf(line, "%s %X %X %x %d %d %d %X %d %d %d", + iface, &destination, &gateway, &flags, &refcnt, + &use, &metric, &mask, &mtu, &window, &irtt) != 11) { + continue; + } + + route->destination = hex_to_ip_address(&destination, 0); + if (route->destination == NULL) { + continue; + } + route->iface = g_strdup(iface); + route->gateway = hex_to_ip_address(&gateway, 0); + route->mask = hex_to_ip_address(&mask, 0); + route->metric = metric; + route->has_flags = true; + route->flags = flags; + route->has_refcnt = true; + route->refcnt = refcnt; + route->has_use = true; + route->use = use; + route->has_mtu = true; + route->mtu = mtu; + route->has_window = true; + route->window = window; + route->has_irtt = true; + route->irtt = irtt; + route->version = 4; + } + + QAPI_LIST_APPEND(tail, route); + route = NULL; + } + + fclose(fp); + } + + free(line); + return head; +} diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index f3a580b8cc..246171d323 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -9,6 +9,7 @@ #include #include +#include "commands-common-ssh.h" #include "qapi/error.h" #include "qga-qapi-commands.h" @@ -35,7 +36,7 @@ test_get_passwd_entry(const gchar *user_name, GError **error) return p; } -#define g_unix_get_passwd_entry_qemu(username, err) \ +#define g_unix_get_passwd_entry(username, err) \ test_get_passwd_entry(username, err) #endif @@ -45,7 +46,7 @@ get_passwd_entry(const char *username, Error **errp) g_autoptr(GError) err = NULL; struct passwd *p; - p = g_unix_get_passwd_entry_qemu(username, &err); + p = g_unix_get_passwd_entry(username, &err); if (p == NULL) { error_setg(errp, "failed to lookup user '%s': %s", username, err->message); @@ -80,37 +81,6 @@ mkdir_for_user(const char *path, const struct passwd *p, return true; } -static bool -check_openssh_pub_key(const char *key, Error **errp) -{ - /* simple sanity-check, we may want more? */ - if (!key || key[0] == '#' || strchr(key, '\n')) { - error_setg(errp, "invalid OpenSSH public key: '%s'", key); - return false; - } - - return true; -} - -static bool -check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) -{ - size_t n = 0; - strList *k; - - for (k = keys; k != NULL; k = k->next) { - if (!check_openssh_pub_key(k->value, errp)) { - return false; - } - n++; - } - - if (nkeys) { - *nkeys = n; - } - return true; -} - static bool write_authkeys(const char *path, const GStrv keys, const struct passwd *p, Error **errp) @@ -139,21 +109,6 @@ write_authkeys(const char *path, const GStrv keys, return true; } -static GStrv -read_authkeys(const char *path, Error **errp) -{ - g_autoptr(GError) err = NULL; - g_autofree char *contents = NULL; - - if (!g_file_get_contents(path, &contents, NULL, &err)) { - error_setg(errp, "failed to read '%s': %s", path, err->message); - return NULL; - } - - return g_strsplit(contents, "\n", -1); - -} - void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, bool has_reset, bool reset, @@ -288,7 +243,6 @@ qmp_guest_ssh_get_authorized_keys(const char *username, Error **errp) } #ifdef QGA_BUILD_UNIT_TEST -#if GLIB_CHECK_VERSION(2, 60, 0) static const strList test_key2 = { .value = (char *)"algo key2 comments" }; @@ -382,7 +336,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"); } @@ -484,11 +438,4 @@ int main(int argc, char *argv[]) return g_test_run(); } -#else -int main(int argc, char *argv[]) -{ - g_test_message("test skipped, needs glib >= 2.60"); - return 0; -} -#endif /* GLIB_2_60 */ #endif /* BUILD_UNIT_TEST */ diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 182eba4a38..636307bedf 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -18,65 +18,205 @@ #include #include "qga-qapi-commands.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/host-utils.h" #include "qemu/sockets.h" #include "qemu/base64.h" #include "qemu/cutils.h" #include "commands-common.h" -#include "block/nvme.h" #include "cutils.h" #ifdef HAVE_UTMPX #include #endif -#if defined(__linux__) -#include -#include -#include - -#ifdef CONFIG_LIBUDEV -#include -#endif -#endif - #ifdef HAVE_GETIFADDRS #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 -#include +#endif #ifdef CONFIG_SOLARIS #include #endif #endif -static void ga_wait_child(pid_t pid, int *status, Error **errp) +static bool ga_wait_child(pid_t pid, int *status, Error **errp) { pid_t rpid; *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)", pid); - return; + return false; } g_assert(rpid == pid); + return true; } -void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) +static ssize_t ga_pipe_read_str(int fd[2], char **str) +{ + ssize_t n, len = 0; + char buf[1024]; + + close(fd[1]); + fd[1] = -1; + while ((n = read(fd[0], buf, sizeof(buf))) != 0) { + if (n < 0) { + if (errno == EINTR) { + continue; + } else { + len = -errno; + break; + } + } + *str = g_realloc(*str, len + n + 1); + memcpy(*str + len, buf, n); + len += n; + (*str)[len] = '\0'; + } + close(fd[0]); + fd[0] = -1; + + return len; +} + +/* + * Helper to run command with input/output redirection, + * sending string to stdin and taking error message from + * stdout/err. + */ +static int ga_run_command(const char *argv[], const char *in_str, + const char *action, Error **errp) +{ + pid_t pid; + int status; + int retcode = -1; + int infd[2] = { -1, -1 }; + int outfd[2] = { -1, -1 }; + char *str = NULL; + ssize_t len = 0; + + if ((in_str && !g_unix_open_pipe(infd, FD_CLOEXEC, NULL)) || + !g_unix_open_pipe(outfd, FD_CLOEXEC, NULL)) { + error_setg(errp, "cannot create pipe FDs"); + goto out; + } + + pid = fork(); + if (pid == 0) { + char *cherr = NULL; + + setsid(); + + if (in_str) { + /* Redirect stdin to infd. */ + close(infd[1]); + dup2(infd[0], 0); + close(infd[0]); + } else { + reopen_fd_to_null(0); + } + + /* Redirect stdout/stderr to outfd. */ + close(outfd[0]); + dup2(outfd[1], 1); + dup2(outfd[1], 2); + close(outfd[1]); + + execvp(argv[0], (char *const *)argv); + + /* Write the cause of failed exec to pipe for the parent to read it. */ + cherr = g_strdup_printf("failed to exec '%s'", argv[0]); + perror(cherr); + g_free(cherr); + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + + if (in_str) { + close(infd[0]); + infd[0] = -1; + if (qemu_write_full(infd[1], in_str, strlen(in_str)) != + strlen(in_str)) { + error_setg_errno(errp, errno, "%s: cannot write to stdin pipe", + action); + goto out; + } + close(infd[1]); + infd[1] = -1; + } + + len = ga_pipe_read_str(outfd, &str); + if (len < 0) { + error_setg_errno(errp, -len, "%s: cannot read from stdout/stderr pipe", + action); + goto out; + } + + if (!ga_wait_child(pid, &status, errp)) { + goto out; + } + + if (!WIFEXITED(status)) { + if (len) { + error_setg(errp, "child process has terminated abnormally: %s", + str); + } else { + error_setg(errp, "child process has terminated abnormally"); + } + goto out; + } + + retcode = WEXITSTATUS(status); + + if (WEXITSTATUS(status)) { + if (len) { + error_setg(errp, "child process has failed to %s: %s", + action, str); + } else { + error_setg(errp, "child process has failed to %s: exit status %d", + action, WEXITSTATUS(status)); + } + goto out; + } + +out: + g_free(str); + + if (infd[0] != -1) { + close(infd[0]); + } + if (infd[1] != -1) { + close(infd[1]); + } + if (outfd[0] != -1) { + close(outfd[0]); + } + if (outfd[1] != -1) { + close(outfd[1]); + } + + return retcode; +} + +void qmp_guest_shutdown(const char *mode, Error **errp) { const char *shutdown_flag; Error *local_err = NULL; - pid_t pid; - int status; #ifdef CONFIG_SOLARIS const char *powerdown_flag = "-i5"; @@ -93,7 +233,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; @@ -105,67 +245,31 @@ void qmp_guest_shutdown(bool has_mode, const char *mode, Error **errp) return; } - pid = fork(); - if (pid == 0) { - /* child, start the shutdown */ - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - + const char *argv[] = {"/sbin/shutdown", #ifdef CONFIG_SOLARIS - execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y", - "hypervisor initiated shutdown", (char *)NULL); + shutdown_flag, "-g0", "-y", #elif defined(CONFIG_BSD) - execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", - "hypervisor initiated shutdown", (char *)NULL); + shutdown_flag, "+0", #else - execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0", - "hypervisor initiated shutdown", (char *)NULL); + "-h", shutdown_flag, "+0", #endif - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } + "hypervisor initiated shutdown", (char *) NULL}; - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, NULL, "shutdown", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - return; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to shutdown"); - return; - } - /* succeeded */ } void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) { int ret; - int status; - pid_t pid; Error *local_err = NULL; struct timeval tv; - static const char hwclock_path[] = "/sbin/hwclock"; - static int hwclock_available = -1; - - if (hwclock_available < 0) { - hwclock_available = (access(hwclock_path, X_OK) == 0); - } - - if (!hwclock_available) { - error_setg(errp, QERR_UNSUPPORTED); - return; - } + const char *argv[] = {"/sbin/hwclock", has_time ? "-w" : "-s", NULL}; /* If user has passed a time, validate and set it. */ if (has_time) { @@ -196,37 +300,12 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp) * just need to synchronize the hardware clock. However, if no time was * passed, user is requesting the opposite: set the system time from the * hardware clock (RTC). */ - pid = fork(); - if (pid == 0) { - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - /* Use '/sbin/hwclock -w' to set RTC from the system time, - * or '/sbin/hwclock -s' to set the system time from RTC. */ - execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL); - _exit(EXIT_FAILURE); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - return; - } - - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, NULL, "set hardware clock to system time", + &local_err); if (local_err) { error_propagate(errp, local_err); return; } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - return; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "hwclock failed to set hardware clock to system time"); - return; - } } typedef enum { @@ -404,14 +483,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); @@ -645,8 +724,6 @@ static const char *fsfreeze_hook_arg_string[] = { 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; @@ -655,42 +732,15 @@ static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp) if (!hook) { return; } - if (access(hook, X_OK) != 0) { - error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook); - return; - } + + const char *argv[] = {hook, arg_str, NULL}; 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); + ga_run_command(argv, NULL, "execute fsfreeze hook", &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; - } } /* @@ -780,1342 +830,6 @@ static void guest_fsfreeze_cleanup(void) } #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) -{ - char *path; - char *dpath; - char *driver = NULL; - char buf[PATH_MAX]; - ssize_t len; - - path = g_strndup(syspath, pathlen); - dpath = g_strdup_printf("%s/driver", path); - len = readlink(dpath, buf, sizeof(buf) - 1); - if (len != -1) { - buf[len] = 0; - driver = g_path_get_basename(buf); - } - g_free(dpath); - g_free(path); - return driver; -} - -static int compare_uint(const void *_a, const void *_b) -{ - unsigned int a = *(unsigned int *)_a; - unsigned int b = *(unsigned int *)_b; - - return a < b ? -1 : a > b ? 1 : 0; -} - -/* Walk the specified sysfs and build a sorted list of host or ata numbers */ -static int build_hosts(char const *syspath, char const *host, bool ata, - unsigned int *hosts, int hosts_max, Error **errp) -{ - char *path; - DIR *dir; - struct dirent *entry; - int i = 0; - - path = g_strndup(syspath, host - syspath); - dir = opendir(path); - if (!dir) { - error_setg_errno(errp, errno, "opendir(\"%s\")", path); - g_free(path); - return -1; - } - - while (i < hosts_max) { - entry = readdir(dir); - if (!entry) { - break; - } - if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) { - ++i; - } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) { - ++i; - } - } - - qsort(hosts, i, sizeof(hosts[0]), compare_uint); - - g_free(path); - closedir(dir); - return i; -} - -/* - * Store disk device info for devices on the PCI bus. - * Returns true if information has been stored, or false for failure. - */ -static bool build_guest_fsinfo_for_pci_dev(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int pci[4], host, hosts[8], tgt[3]; - int i, nhosts = 0, pcilen; - GuestPCIAddress *pciaddr = disk->pci_controller; - bool has_ata = false, has_host = false, has_tgt = false; - char *p, *q, *driver = NULL; - bool ret = false; - - p = strstr(syspath, "/devices/pci"); - if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", - pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { - g_debug("only pci device is supported: sysfs path '%s'", syspath); - return false; - } - - p += 12 + pcilen; - while (true) { - driver = get_pci_driver(syspath, p - syspath, errp); - if (driver && (g_str_equal(driver, "ata_piix") || - g_str_equal(driver, "sym53c8xx") || - g_str_equal(driver, "virtio-pci") || - g_str_equal(driver, "ahci") || - g_str_equal(driver, "nvme"))) { - break; - } - - g_free(driver); - if (sscanf(p, "/%x:%x:%x.%x%n", - pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { - p += pcilen; - continue; - } - - g_debug("unsupported driver or sysfs path '%s'", syspath); - return false; - } - - p = strstr(syspath, "/target"); - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", - tgt, tgt + 1, tgt + 2) == 3) { - has_tgt = true; - } - - p = strstr(syspath, "/ata"); - if (p) { - q = p + 4; - has_ata = true; - } else { - p = strstr(syspath, "/host"); - q = p + 5; - } - if (p && sscanf(q, "%u", &host) == 1) { - has_host = true; - nhosts = build_hosts(syspath, p, has_ata, hosts, - ARRAY_SIZE(hosts), errp); - if (nhosts < 0) { - goto cleanup; - } - } - - pciaddr->domain = pci[0]; - pciaddr->bus = pci[1]; - pciaddr->slot = pci[2]; - pciaddr->function = pci[3]; - - if (strcmp(driver, "ata_piix") == 0) { - /* a host per ide bus, target*:0::0 */ - if (!has_host || !has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - for (i = 0; i < nhosts; i++) { - if (host == hosts[i]) { - disk->bus_type = GUEST_DISK_BUS_TYPE_IDE; - disk->bus = i; - disk->unit = tgt[1]; - break; - } - } - if (i >= nhosts) { - g_debug("no host for '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - } else if (strcmp(driver, "sym53c8xx") == 0) { - /* scsi(LSI Logic): target*:0::0 */ - if (!has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; - disk->unit = tgt[1]; - } else if (strcmp(driver, "virtio-pci") == 0) { - if (has_tgt) { - /* virtio-scsi: target*:0:0: */ - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; - disk->unit = tgt[2]; - } else { - /* virtio-blk: 1 disk per 1 device */ - disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; - } - } else if (strcmp(driver, "ahci") == 0) { - /* ahci: 1 host per 1 unit */ - if (!has_host || !has_tgt) { - g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - for (i = 0; i < nhosts; i++) { - if (host == hosts[i]) { - disk->unit = i; - disk->bus_type = GUEST_DISK_BUS_TYPE_SATA; - break; - } - } - if (i >= nhosts) { - g_debug("no host for '%s' (driver '%s')", syspath, driver); - goto cleanup; - } - } else if (strcmp(driver, "nvme") == 0) { - disk->bus_type = GUEST_DISK_BUS_TYPE_NVME; - } else { - g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath); - goto cleanup; - } - - ret = true; - -cleanup: - g_free(driver); - return ret; -} - -/* - * Store disk device info for non-PCI virtio devices (for example s390x - * channel I/O devices). Returns true if information has been stored, or - * false for failure. - */ -static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int tgt[3]; - char *p; - - if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) { - g_debug("Unsupported virtio device '%s'", syspath); - return false; - } - - p = strstr(syspath, "/target"); - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u", - &tgt[0], &tgt[1], &tgt[2]) == 3) { - /* virtio-scsi: target*:0:: */ - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI; - disk->bus = tgt[0]; - disk->target = tgt[1]; - disk->unit = tgt[2]; - } else { - /* virtio-blk: 1 disk per 1 device */ - disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO; - } - - return true; -} - -/* - * Store disk device info for CCW devices (s390x channel I/O devices). - * Returns true if information has been stored, or false for failure. - */ -static bool build_guest_fsinfo_for_ccw_dev(char const *syspath, - GuestDiskAddress *disk, - Error **errp) -{ - unsigned int cssid, ssid, subchno, devno; - char *p; - - p = strstr(syspath, "/devices/css"); - if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/", - &cssid, &ssid, &subchno, &devno) < 4) { - g_debug("could not parse ccw device sysfs path: %s", 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; - disk->ccw_address->subchno = subchno; - disk->ccw_address->devno = devno; - - if (strstr(p, "/virtio")) { - build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); - } - - return true; -} - -/* Store disk device info specified by @sysfs into @fs */ -static void build_guest_fsinfo_for_real_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) -{ - GuestDiskAddress *disk; - GuestPCIAddress *pciaddr; - bool has_hwinf; -#ifdef CONFIG_LIBUDEV - struct udev *udev = NULL; - struct udev_device *udevice = NULL; -#endif - - pciaddr = g_new0(GuestPCIAddress, 1); - pciaddr->domain = -1; /* -1 means field is invalid */ - pciaddr->bus = -1; - pciaddr->slot = -1; - pciaddr->function = -1; - - disk = g_new0(GuestDiskAddress, 1); - disk->pci_controller = pciaddr; - disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN; - -#ifdef CONFIG_LIBUDEV - udev = udev_new(); - udevice = udev_device_new_from_syspath(udev, syspath); - if (udev == NULL || udevice == NULL) { - g_debug("failed to query udev"); - } else { - const char *devnode, *serial; - 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; - } - } - - udev_unref(udev); - udev_device_unref(udevice); -#endif - - if (strstr(syspath, "/devices/pci")) { - has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp); - } else if (strstr(syspath, "/devices/css")) { - has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp); - } else if (strstr(syspath, "/virtio")) { - has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp); - } else { - g_debug("Unsupported device type for '%s'", syspath); - has_hwinf = false; - } - - if (has_hwinf || disk->has_dev || disk->has_serial) { - QAPI_LIST_PREPEND(fs->disk, disk); - } else { - qapi_free_GuestDiskAddress(disk); - } -} - -static void build_guest_fsinfo_for_device(char const *devpath, - GuestFilesystemInfo *fs, - Error **errp); - -/* Store a list of slave devices of virtual volume specified by @syspath into - * @fs */ -static void build_guest_fsinfo_for_virtual_device(char const *syspath, - GuestFilesystemInfo *fs, - Error **errp) -{ - Error *err = NULL; - DIR *dir; - char *dirpath; - struct dirent *entry; - - dirpath = g_strdup_printf("%s/slaves", syspath); - dir = opendir(dirpath); - if (!dir) { - if (errno != ENOENT) { - error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath); - } - g_free(dirpath); - return; - } - - for (;;) { - errno = 0; - entry = readdir(dir); - if (entry == NULL) { - if (errno) { - error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath); - } - break; - } - - if (entry->d_type == DT_LNK) { - char *path; - - g_debug(" slave device '%s'", entry->d_name); - path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name); - build_guest_fsinfo_for_device(path, fs, &err); - g_free(path); - - if (err) { - error_propagate(errp, err); - break; - } - } - } - - g_free(dirpath); - closedir(dir); -} - -static bool is_disk_virtual(const char *devpath, Error **errp) -{ - g_autofree char *syspath = realpath(devpath, NULL); - - if (!syspath) { - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath); - return false; - } - return strstr(syspath, "/devices/virtual/block/") != NULL; -} - -/* Dispatch to functions for virtual/real device */ -static void build_guest_fsinfo_for_device(char const *devpath, - GuestFilesystemInfo *fs, - Error **errp) -{ - ERRP_GUARD(); - g_autofree char *syspath = NULL; - bool is_virtual = false; - - syspath = realpath(devpath, NULL); - if (!syspath) { - 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; - } - - if (!fs->name) { - fs->name = g_path_get_basename(syspath); - } - - g_debug(" parse sysfs path '%s'", syspath); - is_virtual = is_disk_virtual(syspath, errp); - if (*errp != NULL) { - return; - } - if (is_virtual) { - build_guest_fsinfo_for_virtual_device(syspath, fs, errp); - } else { - build_guest_fsinfo_for_real_device(syspath, fs, errp); - } -} - -#ifdef CONFIG_LIBUDEV - -/* - * Wrapper around build_guest_fsinfo_for_device() for getting just - * the disk address. - */ -static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp) -{ - g_autoptr(GuestFilesystemInfo) fs = NULL; - - fs = g_new0(GuestFilesystemInfo, 1); - build_guest_fsinfo_for_device(syspath, fs, errp); - if (fs->disk != NULL) { - return g_steal_pointer(&fs->disk->value); - } - return NULL; -} - -static char *get_alias_for_syspath(const char *syspath) -{ - struct udev *udev = NULL; - struct udev_device *udevice = NULL; - char *ret = NULL; - - udev = udev_new(); - if (udev == NULL) { - g_debug("failed to query udev"); - goto out; - } - udevice = udev_device_new_from_syspath(udev, syspath); - if (udevice == NULL) { - g_debug("failed to query udev for path: %s", syspath); - goto out; - } else { - const char *alias = udev_device_get_property_value( - udevice, "DM_NAME"); - /* - * NULL means there was an error and empty string means there is no - * alias. In case of no alias we return NULL instead of empty string. - */ - if (alias == NULL) { - g_debug("failed to query udev for device alias for: %s", - syspath); - } else if (*alias != 0) { - ret = g_strdup(alias); - } - } - -out: - udev_unref(udev); - udev_device_unref(udevice); - return ret; -} - -static char *get_device_for_syspath(const char *syspath) -{ - struct udev *udev = NULL; - struct udev_device *udevice = NULL; - char *ret = NULL; - - udev = udev_new(); - if (udev == NULL) { - g_debug("failed to query udev"); - goto out; - } - udevice = udev_device_new_from_syspath(udev, syspath); - if (udevice == NULL) { - g_debug("failed to query udev for path: %s", syspath); - goto out; - } else { - ret = g_strdup(udev_device_get_devnode(udevice)); - } - -out: - udev_unref(udev); - udev_device_unref(udevice); - return ret; -} - -static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk) -{ - g_autofree char *deps_dir = NULL; - const gchar *dep; - GDir *dp_deps = NULL; - - /* List dependent disks */ - deps_dir = g_strdup_printf("%s/slaves", disk_dir); - g_debug(" listing entries in: %s", deps_dir); - dp_deps = g_dir_open(deps_dir, 0, NULL); - if (dp_deps == NULL) { - g_debug("failed to list entries in %s", deps_dir); - return; - } - disk->has_dependencies = true; - while ((dep = g_dir_read_name(dp_deps)) != NULL) { - g_autofree char *dep_dir = NULL; - char *dev_name; - - /* Add dependent disks */ - dep_dir = g_strdup_printf("%s/%s", deps_dir, dep); - dev_name = get_device_for_syspath(dep_dir); - if (dev_name != NULL) { - g_debug(" adding dependent device: %s", dev_name); - QAPI_LIST_PREPEND(disk->dependencies, dev_name); - } - } - g_dir_close(dp_deps); -} - -/* - * Detect partitions subdirectory, name is "" or - * "p" - * - * @disk_name -- last component of /sys path (e.g. sda) - * @disk_dir -- sys path of the disk (e.g. /sys/block/sda) - * @disk_dev -- device node of the disk (e.g. /dev/sda) - */ -static GuestDiskInfoList *get_disk_partitions( - GuestDiskInfoList *list, - const char *disk_name, const char *disk_dir, - const char *disk_dev) -{ - GuestDiskInfoList *ret = list; - struct dirent *de_disk; - DIR *dp_disk = NULL; - size_t len = strlen(disk_name); - - dp_disk = opendir(disk_dir); - while ((de_disk = readdir(dp_disk)) != NULL) { - g_autofree char *partition_dir = NULL; - char *dev_name; - GuestDiskInfo *partition; - - if (!(de_disk->d_type & DT_DIR)) { - continue; - } - - if (!(strncmp(disk_name, de_disk->d_name, len) == 0 && - ((*(de_disk->d_name + len) == 'p' && - isdigit(*(de_disk->d_name + len + 1))) || - isdigit(*(de_disk->d_name + len))))) { - continue; - } - - partition_dir = g_strdup_printf("%s/%s", - disk_dir, de_disk->d_name); - dev_name = get_device_for_syspath(partition_dir); - if (dev_name == NULL) { - g_debug("Failed to get device name for syspath: %s", - disk_dir); - continue; - } - partition = g_new0(GuestDiskInfo, 1); - partition->name = dev_name; - partition->partition = true; - partition->has_dependencies = true; - /* Add parent disk as dependent for easier tracking of hierarchy */ - QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev)); - - QAPI_LIST_PREPEND(ret, partition); - } - closedir(dp_disk); - - return ret; -} - -static void get_nvme_smart(GuestDiskInfo *disk) -{ - int fd; - GuestNVMeSmart *smart; - NvmeSmartLog log = {0}; - struct nvme_admin_cmd cmd = { - .opcode = NVME_ADM_CMD_GET_LOG_PAGE, - .nsid = NVME_NSID_BROADCAST, - .addr = (uintptr_t)&log, - .data_len = sizeof(log), - .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */ - | (((sizeof(log) >> 2) - 1) << 16) - }; - - fd = qga_open_cloexec(disk->name, O_RDONLY, 0); - if (fd == -1) { - g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno)); - return; - } - - if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) { - g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno)); - close(fd); - return; - } - - disk->has_smart = true; - disk->smart = g_new0(GuestDiskSmart, 1); - disk->smart->type = GUEST_DISK_BUS_TYPE_NVME; - - smart = &disk->smart->u.nvme; - smart->critical_warning = log.critical_warning; - smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */ - smart->available_spare = log.available_spare; - smart->available_spare_threshold = log.available_spare_threshold; - smart->percentage_used = log.percentage_used; - smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]); - smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]); - smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]); - smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]); - smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]); - smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]); - smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]); - smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]); - smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]); - smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]); - smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]); - smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]); - smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]); - smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]); - smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]); - smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]); - smart->media_errors_lo = le64_to_cpu(log.media_errors[0]); - smart->media_errors_hi = le64_to_cpu(log.media_errors[1]); - smart->number_of_error_log_entries_lo = - le64_to_cpu(log.number_of_error_log_entries[0]); - smart->number_of_error_log_entries_hi = - le64_to_cpu(log.number_of_error_log_entries[1]); - - close(fd); -} - -static void get_disk_smart(GuestDiskInfo *disk) -{ - if (disk->has_address - && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) { - get_nvme_smart(disk); - } -} - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - GuestDiskInfoList *ret = NULL; - GuestDiskInfo *disk; - DIR *dp = NULL; - struct dirent *de = NULL; - - g_debug("listing /sys/block directory"); - dp = opendir("/sys/block"); - if (dp == NULL) { - error_setg_errno(errp, errno, "Can't open directory \"/sys/block\""); - return NULL; - } - while ((de = readdir(dp)) != NULL) { - g_autofree char *disk_dir = NULL, *line = NULL, - *size_path = NULL; - char *dev_name; - Error *local_err = NULL; - if (de->d_type != DT_LNK) { - g_debug(" skipping entry: %s", de->d_name); - continue; - } - - /* Check size and skip zero-sized disks */ - g_debug(" checking disk size"); - size_path = g_strdup_printf("/sys/block/%s/size", de->d_name); - if (!g_file_get_contents(size_path, &line, NULL, NULL)) { - g_debug(" failed to read disk size"); - continue; - } - if (g_strcmp0(line, "0\n") == 0) { - g_debug(" skipping zero-sized disk"); - continue; - } - - g_debug(" adding %s", de->d_name); - disk_dir = g_strdup_printf("/sys/block/%s", de->d_name); - dev_name = get_device_for_syspath(disk_dir); - if (dev_name == NULL) { - g_debug("Failed to get device name for syspath: %s", - disk_dir); - continue; - } - disk = g_new0(GuestDiskInfo, 1); - 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 */ - bool is_virtual = is_disk_virtual(disk_dir, &local_err); - if (local_err != NULL) { - g_debug(" failed to check disk path, ignoring error: %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - /* Don't try to get the address */ - is_virtual = true; - } - if (!is_virtual) { - disk->address = get_disk_address(disk_dir, &local_err); - if (local_err != NULL) { - g_debug(" failed to get device info, ignoring error: %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - } else if (disk->address != NULL) { - disk->has_address = true; - } - } - - get_disk_deps(disk_dir, disk); - get_disk_smart(disk); - ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name); - } - - closedir(dp); - - return ret; -} - -#else - -GuestDiskInfoList *qmp_guest_get_disks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif - -/* Return a list of the disk device(s)' info which @mount lies on */ -static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, - Error **errp) -{ - GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); - struct statvfs buf; - unsigned long used, nonroot_total, fr_size; - char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", - mount->devmajor, mount->devminor); - - fs->mountpoint = g_strdup(mount->dirname); - fs->type = g_strdup(mount->devtype); - build_guest_fsinfo_for_device(devpath, fs, errp); - - if (statvfs(fs->mountpoint, &buf) == 0) { - fr_size = buf.f_frsize; - used = buf.f_blocks - buf.f_bfree; - nonroot_total = used + buf.f_bavail; - fs->used_bytes = used * fr_size; - fs->total_bytes = nonroot_total * fr_size; - - fs->has_total_bytes = true; - fs->has_used_bytes = true; - } - - g_free(devpath); - - return fs; -} - -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) -{ - FsMountList mounts; - struct FsMount *mount; - GuestFilesystemInfoList *ret = NULL; - Error *local_err = NULL; - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, &local_err)) { - error_propagate(errp, local_err); - return NULL; - } - - QTAILQ_FOREACH(mount, &mounts, next) { - g_debug("Building guest fsinfo for '%s'", mount->dirname); - - QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err)); - if (local_err) { - error_propagate(errp, local_err); - qapi_free_GuestFilesystemInfoList(ret); - ret = NULL; - break; - } - } - - free_fs_mount_list(&mounts); - return ret; -} -#endif /* CONFIG_FSFREEZE */ - -#if defined(CONFIG_FSTRIM) -/* - * Walk list of mounted file systems in the guest, and trim them. - */ -GuestFilesystemTrimResponse * -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) -{ - GuestFilesystemTrimResponse *response; - GuestFilesystemTrimResult *result; - int ret = 0; - FsMountList mounts; - struct FsMount *mount; - int fd; - struct fstrim_range r; - - slog("guest-fstrim called"); - - QTAILQ_INIT(&mounts); - if (!build_fs_mount_list(&mounts, errp)) { - return NULL; - } - - response = g_malloc0(sizeof(*response)); - - QTAILQ_FOREACH(mount, &mounts, next) { - result = g_malloc0(sizeof(*result)); - result->path = g_strdup(mount->dirname); - - QAPI_LIST_PREPEND(response->paths, result); - - fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0); - if (fd == -1) { - result->error = g_strdup_printf("failed to open: %s", - strerror(errno)); - result->has_error = true; - continue; - } - - /* We try to cull filesystems we know won't work in advance, but other - * filesystems may not implement fstrim for less obvious reasons. - * These will report EOPNOTSUPP; while in some other cases ENOTTY - * will be reported (e.g. CD-ROMs). - * Any other error means an unexpected error. - */ - r.start = 0; - r.len = -1; - 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 { - result->error = g_strdup_printf("failed to trim: %s", - strerror(errno)); - } - close(fd); - continue; - } - - result->has_minimum = true; - result->minimum = r.minlen; - result->has_trimmed = true; - result->trimmed = r.len; - close(fd); - } - - free_fs_mount_list(&mounts); - return response; -} -#endif /* CONFIG_FSTRIM */ - - -#define LINUX_SYS_STATE_FILE "/sys/power/state" -#define SUSPEND_SUPPORTED 0 -#define SUSPEND_NOT_SUPPORTED 1 - -typedef enum { - SUSPEND_MODE_DISK = 0, - SUSPEND_MODE_RAM = 1, - SUSPEND_MODE_HYBRID = 2, -} SuspendMode; - -/* - * Executes a command in a child process using g_spawn_sync, - * returning an int >= 0 representing the exit status of the - * process. - * - * If the program wasn't found in path, returns -1. - * - * If a problem happened when creating the child process, - * returns -1 and errp is set. - */ -static int run_process_child(const char *command[], Error **errp) -{ - int exit_status, spawn_flag; - GError *g_err = NULL; - bool success; - - spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | - G_SPAWN_STDERR_TO_DEV_NULL; - - success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag, - NULL, NULL, NULL, NULL, - &exit_status, &g_err); - - if (success) { - return WEXITSTATUS(exit_status); - } - - if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { - error_setg(errp, "failed to create child process, error '%s'", - g_err->message); - } - - g_error_free(g_err); - return -1; -} - -static bool systemd_supports_mode(SuspendMode mode, Error **errp) -{ - const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", - "systemd-hybrid-sleep"}; - const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; - int status; - - status = run_process_child(cmd, errp); - - /* - * systemctl status uses LSB return codes so we can expect - * status > 0 and be ok. To assert if the guest has support - * for the selected suspend mode, status should be < 4. 4 is - * the code for unknown service status, the return value when - * the service does not exist. A common value is status = 3 - * (program is not running). - */ - if (status > 0 && status < 4) { - return true; - } - - return false; -} - -static void systemd_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; - const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; - int status; - - status = run_process_child(cmd, &local_err); - - if (status == 0) { - return; - } - - if ((status == -1) && !local_err) { - error_setg(errp, "the helper program 'systemctl %s' was not found", - systemctl_args[mode]); - return; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, "the helper program 'systemctl %s' returned an " - "unexpected exit status code (%d)", - systemctl_args[mode], status); - } -} - -static bool pmutils_supports_mode(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *pmutils_args[3] = {"--hibernate", "--suspend", - "--suspend-hybrid"}; - const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; - int status; - - status = run_process_child(cmd, &local_err); - - if (status == SUSPEND_SUPPORTED) { - return true; - } - - if ((status == -1) && !local_err) { - return false; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, - "the helper program '%s' returned an unexpected exit" - " status code (%d)", "pm-is-supported", status); - } - - return false; -} - -static void pmutils_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", - "pm-suspend-hybrid"}; - const char *cmd[2] = {pmutils_binaries[mode], NULL}; - int status; - - status = run_process_child(cmd, &local_err); - - if (status == 0) { - return; - } - - if ((status == -1) && !local_err) { - error_setg(errp, "the helper program '%s' was not found", - pmutils_binaries[mode]); - return; - } - - if (local_err) { - error_propagate(errp, local_err); - } else { - error_setg(errp, - "the helper program '%s' returned an unexpected exit" - " status code (%d)", pmutils_binaries[mode], status); - } -} - -static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) -{ - const char *sysfile_strs[3] = {"disk", "mem", NULL}; - const char *sysfile_str = sysfile_strs[mode]; - char buf[32]; /* hopefully big enough */ - int fd; - ssize_t ret; - - if (!sysfile_str) { - error_setg(errp, "unknown guest suspend mode"); - return false; - } - - fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); - if (fd < 0) { - return false; - } - - ret = read(fd, buf, sizeof(buf) - 1); - close(fd); - if (ret <= 0) { - return false; - } - buf[ret] = '\0'; - - if (strstr(buf, sysfile_str)) { - return true; - } - return false; -} - -static void linux_sys_state_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - const char *sysfile_strs[3] = {"disk", "mem", NULL}; - const char *sysfile_str = sysfile_strs[mode]; - pid_t pid; - int status; - - if (!sysfile_str) { - error_setg(errp, "unknown guest suspend mode"); - return; - } - - pid = fork(); - if (!pid) { - /* child */ - int fd; - - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); - if (fd < 0) { - _exit(EXIT_FAILURE); - } - - if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) { - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } 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 (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to suspend"); - } - -} - -static void guest_suspend(SuspendMode mode, Error **errp) -{ - Error *local_err = NULL; - bool mode_supported = false; - - if (systemd_supports_mode(mode, &local_err)) { - mode_supported = true; - systemd_suspend(mode, &local_err); - - if (!local_err) { - return; - } - } - - error_free(local_err); - local_err = NULL; - - if (pmutils_supports_mode(mode, &local_err)) { - mode_supported = true; - pmutils_suspend(mode, &local_err); - - if (!local_err) { - return; - } - } - - error_free(local_err); - local_err = NULL; - - if (linux_sys_state_supports_mode(mode, &local_err)) { - mode_supported = true; - linux_sys_state_suspend(mode, &local_err); - } - - if (!mode_supported) { - error_free(local_err); - error_setg(errp, - "the requested suspend mode is not supported by the guest"); - } else { - error_propagate(errp, local_err); - } -} - -void qmp_guest_suspend_disk(Error **errp) -{ - guest_suspend(SUSPEND_MODE_DISK, errp); -} - -void qmp_guest_suspend_ram(Error **errp) -{ - guest_suspend(SUSPEND_MODE_RAM, errp); -} - -void qmp_guest_suspend_hybrid(Error **errp) -{ - guest_suspend(SUSPEND_MODE_HYBRID, errp); -} - -/* Transfer online/offline status between @vcpu and the guest system. - * - * On input either @errp or *@errp must be NULL. - * - * In system-to-@vcpu direction, the following @vcpu fields are accessed: - * - R: vcpu->logical_id - * - W: vcpu->online - * - W: vcpu->can_offline - * - * In @vcpu-to-system direction, the following @vcpu fields are accessed: - * - R: vcpu->logical_id - * - R: vcpu->online - * - * Written members remain unmodified on error. - */ -static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, - char *dirpath, Error **errp) -{ - int fd; - int res; - int dirfd; - static const char fn[] = "online"; - - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd == -1) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - return; - } - - fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); - if (fd == -1) { - if (errno != ENOENT) { - error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); - } else if (sys2vcpu) { - vcpu->online = true; - vcpu->can_offline = false; - } else if (!vcpu->online) { - error_setg(errp, "logical processor #%" PRId64 " can't be " - "offlined", vcpu->logical_id); - } /* otherwise pretend successful re-onlining */ - } else { - unsigned char status; - - res = pread(fd, &status, 1, 0); - if (res == -1) { - error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); - } else if (res == 0) { - error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, - fn); - } else if (sys2vcpu) { - vcpu->online = (status != '0'); - vcpu->can_offline = true; - } else if (vcpu->online != (status != '0')) { - status = '0' + vcpu->online; - if (pwrite(fd, &status, 1, 0) == -1) { - error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, - fn); - } - } /* otherwise pretend successful re-(on|off)-lining */ - - res = close(fd); - g_assert(res == 0); - } - - res = close(dirfd); - g_assert(res == 0); -} - -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) -{ - GuestLogicalProcessorList *head, **tail; - const char *cpu_dir = "/sys/devices/system/cpu"; - const gchar *line; - g_autoptr(GDir) cpu_gdir = NULL; - Error *local_err = NULL; - - head = NULL; - tail = &head; - cpu_gdir = g_dir_open(cpu_dir, 0, NULL); - - if (cpu_gdir == NULL) { - error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir); - return NULL; - } - - while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) { - GuestLogicalProcessor *vcpu; - int64_t id; - if (sscanf(line, "cpu%" PRId64, &id)) { - g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/" - "cpu%" PRId64 "/", id); - vcpu = g_malloc0(sizeof *vcpu); - vcpu->logical_id = id; - vcpu->has_can_offline = true; /* lolspeak ftw */ - transfer_vcpu(vcpu, true, path, &local_err); - QAPI_LIST_APPEND(tail, vcpu); - } - } - - if (local_err == NULL) { - /* there's no guest with zero VCPUs */ - g_assert(head != NULL); - return head; - } - - qapi_free_GuestLogicalProcessorList(head); - error_propagate(errp, local_err); - return NULL; -} - -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - int64_t processed; - Error *local_err = NULL; - - processed = 0; - while (vcpus != NULL) { - char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", - vcpus->value->logical_id); - - transfer_vcpu(vcpus->value, false, path, &local_err); - g_free(path); - if (local_err != NULL) { - break; - } - ++processed; - vcpus = vcpus->next; - } - - if (local_err != NULL) { - if (processed == 0) { - error_propagate(errp, local_err); - } else { - error_free(local_err); - } - } - - return processed; -} -#endif /* __linux__ */ - #if defined(__linux__) || defined(__FreeBSD__) void qmp_guest_set_user_password(const char *username, const char *password, @@ -2123,14 +837,8 @@ void qmp_guest_set_user_password(const char *username, Error **errp) { Error *local_err = NULL; - char *passwd_path = NULL; - pid_t pid; - int status; - int datafd[2] = { -1, -1 }; - char *rawpasswddata = NULL; + g_autofree char *rawpasswddata = NULL; size_t rawpasswdlen; - char *chpasswddata = NULL; - size_t chpasswdlen; rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp); if (!rawpasswddata) { @@ -2141,665 +849,35 @@ void qmp_guest_set_user_password(const char *username, if (strchr(rawpasswddata, '\n')) { error_setg(errp, "forbidden characters in raw password"); - goto out; + return; } if (strchr(username, '\n') || strchr(username, ':')) { error_setg(errp, "forbidden characters in username"); - goto out; + return; } #ifdef __FreeBSD__ - chpasswddata = g_strdup(rawpasswddata); - passwd_path = g_find_program_in_path("pw"); + g_autofree char *chpasswddata = g_strdup(rawpasswddata); + const char *crypt_flag = crypted ? "-H" : "-h"; + const char *argv[] = {"pw", "usermod", "-n", username, + crypt_flag, "0", NULL}; #else - chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); - passwd_path = g_find_program_in_path("chpasswd"); + g_autofree char *chpasswddata = g_strdup_printf("%s:%s\n", username, + rawpasswddata); + const char *crypt_flag = crypted ? "-e" : NULL; + const char *argv[] = {"chpasswd", crypt_flag, NULL}; #endif - chpasswdlen = strlen(chpasswddata); - - if (!passwd_path) { - error_setg(errp, "cannot find 'passwd' program in PATH"); - goto out; - } - - if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) { - error_setg(errp, "cannot create pipe FDs"); - goto out; - } - - pid = fork(); - if (pid == 0) { - close(datafd[1]); - /* child */ - setsid(); - dup2(datafd[0], 0); - 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"); - goto out; - } - close(datafd[0]); - datafd[0] = -1; - - if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) { - error_setg_errno(errp, errno, "cannot write new account password"); - goto out; - } - close(datafd[1]); - datafd[1] = -1; - - ga_wait_child(pid, &status, &local_err); + ga_run_command(argv, chpasswddata, "set user password", &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; + return; } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - - if (WEXITSTATUS(status)) { - error_setg(errp, "child process has failed to set user password"); - goto out; - } - -out: - g_free(chpasswddata); - g_free(rawpasswddata); - g_free(passwd_path); - if (datafd[0] != -1) { - close(datafd[0]); - } - if (datafd[1] != -1) { - 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) -{ - int fd; - int res; - - errno = 0; - fd = openat(dirfd, pathname, O_RDONLY); - if (fd == -1) { - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); - return; - } - - res = pread(fd, buf, size, 0); - if (res == -1) { - error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); - } else if (res == 0) { - error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname); - } - close(fd); -} - -static void ga_write_sysfs_file(int dirfd, const char *pathname, - const char *buf, int size, Error **errp) -{ - int fd; - - errno = 0; - fd = openat(dirfd, pathname, O_WRONLY); - if (fd == -1) { - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); - return; - } - - if (pwrite(fd, buf, size, 0) == -1) { - error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname); - } - - close(fd); -} - -/* Transfer online/offline status between @mem_blk and the guest system. - * - * On input either @errp or *@errp must be NULL. - * - * In system-to-@mem_blk direction, the following @mem_blk fields are accessed: - * - R: mem_blk->phys_index - * - W: mem_blk->online - * - W: mem_blk->can_offline - * - * In @mem_blk-to-system direction, the following @mem_blk fields are accessed: - * - R: mem_blk->phys_index - * - R: mem_blk->online - *- R: mem_blk->can_offline - * Written members remain unmodified on error. - */ -static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, - GuestMemoryBlockResponse *result, - Error **errp) -{ - char *dirpath; - int dirfd; - char *status; - Error *local_err = NULL; - - if (!sys2memblk) { - DIR *dp; - - if (!result) { - error_setg(errp, "Internal error, 'result' should not be NULL"); - return; - } - errno = 0; - dp = opendir("/sys/devices/system/memory/"); - /* if there is no 'memory' directory in sysfs, - * we think this VM does not support online/offline memory block, - * any other solution? - */ - if (!dp) { - if (errno == ENOENT) { - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; - } - goto out1; - } - closedir(dp); - } - - dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/", - mem_blk->phys_index); - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd == -1) { - if (sys2memblk) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - } else { - if (errno == ENOENT) { - result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND; - } else { - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - } - } - g_free(dirpath); - goto out1; - } - g_free(dirpath); - - status = g_malloc0(10); - ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); - if (local_err) { - /* treat with sysfs file that not exist in old kernel */ - if (errno == ENOENT) { - error_free(local_err); - if (sys2memblk) { - mem_blk->online = true; - mem_blk->can_offline = false; - } else if (!mem_blk->online) { - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; - } - } else { - if (sys2memblk) { - error_propagate(errp, local_err); - } else { - error_free(local_err); - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - } - } - goto out2; - } - - if (sys2memblk) { - char removable = '0'; - - mem_blk->online = (strncmp(status, "online", 6) == 0); - - ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); - if (local_err) { - /* if no 'removable' file, it doesn't support offline mem blk */ - if (errno == ENOENT) { - error_free(local_err); - mem_blk->can_offline = false; - } else { - error_propagate(errp, local_err); - } - } else { - mem_blk->can_offline = (removable != '0'); - } - } else { - if (mem_blk->online != (strncmp(status, "online", 6) == 0)) { - const char *new_state = mem_blk->online ? "online" : "offline"; - - ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state), - &local_err); - if (local_err) { - error_free(local_err); - result->response = - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; - goto out2; - } - - result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; - result->has_error_code = false; - } /* otherwise pretend successful re-(on|off)-lining */ - } - g_free(status); - close(dirfd); - return; - -out2: - g_free(status); - close(dirfd); -out1: - if (!sys2memblk) { - result->has_error_code = true; - result->error_code = errno; - } -} - -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - GuestMemoryBlockList *head, **tail; - Error *local_err = NULL; - struct dirent *de; - DIR *dp; - - head = NULL; - tail = &head; - - dp = opendir("/sys/devices/system/memory/"); - if (!dp) { - /* it's ok if this happens to be a system that doesn't expose - * memory blocks via sysfs, but otherwise we should report - * an error - */ - if (errno != ENOENT) { - error_setg_errno(errp, errno, "Can't open directory" - "\"/sys/devices/system/memory/\""); - } - return NULL; - } - - /* Note: the phys_index of memory block may be discontinuous, - * this is because a memblk is the unit of the Sparse Memory design, which - * allows discontinuous memory ranges (ex. NUMA), so here we should - * traverse the memory block directory. - */ - while ((de = readdir(dp)) != NULL) { - GuestMemoryBlock *mem_blk; - - if ((strncmp(de->d_name, "memory", 6) != 0) || - !(de->d_type & DT_DIR)) { - continue; - } - - mem_blk = g_malloc0(sizeof *mem_blk); - /* The d_name is "memoryXXX", phys_index is block id, same as XXX */ - mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10); - mem_blk->has_can_offline = true; /* lolspeak ftw */ - transfer_memory_block(mem_blk, true, NULL, &local_err); - if (local_err) { - break; - } - - QAPI_LIST_APPEND(tail, mem_blk); - } - - closedir(dp); - if (local_err == NULL) { - /* there's no guest with zero memory blocks */ - if (head == NULL) { - error_setg(errp, "guest reported zero memory blocks!"); - } - return head; - } - - qapi_free_GuestMemoryBlockList(head); - error_propagate(errp, local_err); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - GuestMemoryBlockResponseList *head, **tail; - Error *local_err = NULL; - - head = NULL; - tail = &head; - - while (mem_blks != NULL) { - GuestMemoryBlockResponse *result; - GuestMemoryBlock *current_mem_blk = mem_blks->value; - - result = g_malloc0(sizeof(*result)); - result->phys_index = current_mem_blk->phys_index; - transfer_memory_block(current_mem_blk, false, result, &local_err); - if (local_err) { /* should never happen */ - goto err; - } - - QAPI_LIST_APPEND(tail, result); - mem_blks = mem_blks->next; - } - - return head; -err: - qapi_free_GuestMemoryBlockResponseList(head); - error_propagate(errp, local_err); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - Error *local_err = NULL; - char *dirpath; - int dirfd; - char *buf; - GuestMemoryBlockInfo *info; - - dirpath = g_strdup_printf("/sys/devices/system/memory/"); - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); - if (dirfd == -1) { - error_setg_errno(errp, errno, "open(\"%s\")", dirpath); - g_free(dirpath); - return NULL; - } - g_free(dirpath); - - buf = g_malloc0(20); - ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); - close(dirfd); - if (local_err) { - g_free(buf); - error_propagate(errp, local_err); - return NULL; - } - - info = g_new0(GuestMemoryBlockInfo, 1); - info->size = strtol(buf, NULL, 16); /* the unit is bytes */ - - g_free(buf); - - return info; -} - -#define MAX_NAME_LEN 128 -static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp) -{ -#ifdef CONFIG_LINUX - GuestDiskStatsInfoList *head = NULL, **tail = &head; - const char *diskstats = "/proc/diskstats"; - FILE *fp; - size_t n; - char *line = NULL; - - fp = fopen(diskstats, "r"); - if (fp == NULL) { - error_setg_errno(errp, errno, "open(\"%s\")", diskstats); - return NULL; - } - - while (getline(&line, &n, fp) != -1) { - g_autofree GuestDiskStatsInfo *diskstatinfo = NULL; - g_autofree GuestDiskStats *diskstat = NULL; - char dev_name[MAX_NAME_LEN]; - unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks; - unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios; - unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; - unsigned long dc_ios, dc_merges, dc_sec, fl_ios; - unsigned int major, minor; - int i; - - i = sscanf(line, "%u %u %s %lu %lu %lu" - "%lu %lu %lu %lu %u %u %u %u" - "%lu %lu %lu %u %lu %u", - &major, &minor, dev_name, - &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, - &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, - &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks, - &dc_ios, &dc_merges, &dc_sec, &dc_ticks, - &fl_ios, &fl_ticks); - - if (i < 7) { - continue; - } - - diskstatinfo = g_new0(GuestDiskStatsInfo, 1); - diskstatinfo->name = g_strdup(dev_name); - diskstatinfo->major = major; - diskstatinfo->minor = minor; - - diskstat = g_new0(GuestDiskStats, 1); - if (i == 7) { - diskstat->has_read_ios = true; - diskstat->read_ios = rd_ios; - diskstat->has_read_sectors = true; - diskstat->read_sectors = rd_merges_or_rd_sec; - diskstat->has_write_ios = true; - diskstat->write_ios = rd_sec_or_wr_ios; - diskstat->has_write_sectors = true; - diskstat->write_sectors = rd_ticks_or_wr_sec; - } - if (i >= 14) { - diskstat->has_read_ios = true; - diskstat->read_ios = rd_ios; - diskstat->has_read_sectors = true; - diskstat->read_sectors = rd_sec_or_wr_ios; - diskstat->has_read_merges = true; - diskstat->read_merges = rd_merges_or_rd_sec; - diskstat->has_read_ticks = true; - diskstat->read_ticks = rd_ticks_or_wr_sec; - diskstat->has_write_ios = true; - diskstat->write_ios = wr_ios; - diskstat->has_write_sectors = true; - diskstat->write_sectors = wr_sec; - diskstat->has_write_merges = true; - diskstat->write_merges = wr_merges; - diskstat->has_write_ticks = true; - diskstat->write_ticks = wr_ticks; - diskstat->has_ios_pgr = true; - diskstat->ios_pgr = ios_pgr; - diskstat->has_total_ticks = true; - diskstat->total_ticks = tot_ticks; - diskstat->has_weight_ticks = true; - diskstat->weight_ticks = rq_ticks; - } - if (i >= 18) { - diskstat->has_discard_ios = true; - diskstat->discard_ios = dc_ios; - diskstat->has_discard_merges = true; - diskstat->discard_merges = dc_merges; - diskstat->has_discard_sectors = true; - diskstat->discard_sectors = dc_sec; - diskstat->has_discard_ticks = true; - diskstat->discard_ticks = dc_ticks; - } - if (i >= 20) { - diskstat->has_flush_ios = true; - diskstat->flush_ios = fl_ios; - diskstat->has_flush_ticks = true; - diskstat->flush_ticks = fl_ticks; - } - - diskstatinfo->stats = g_steal_pointer(&diskstat); - QAPI_LIST_APPEND(tail, diskstatinfo); - diskstatinfo = NULL; - } - free(line); - fclose(fp); - return head; -#else - g_debug("disk stats reporting available only for Linux"); - return NULL; -#endif -} - -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) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - -void qmp_guest_suspend_ram(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - -void qmp_guest_suspend_hybrid(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return -1; -} - -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#endif - #ifdef HAVE_GETIFADDRS static GuestNetworkInterface * guest_find_interface(GuestNetworkInterfaceList *head, @@ -2881,7 +959,7 @@ static int guest_get_network_stats(const char *name, return -1; } -#ifndef __FreeBSD__ +#ifndef CONFIG_BSD /* * Fill "buf" with MAC address by ifaddrs. Pointer buf must point to a * buffer with ETHER_ADDR_LEN length at least. @@ -2930,7 +1008,7 @@ bool guest_get_hw_addr(struct ifaddrs *ifa, unsigned char *buf, close(sock); return true; } -#endif /* __FreeBSD__ */ +#endif /* CONFIG_BSD */ /* * Build information about guest interfaces @@ -2967,7 +1045,7 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) QAPI_LIST_APPEND(tail, info); } - if (!info->has_hardware_address) { + if (!info->hardware_address) { if (!guest_get_hw_addr(ifa, mac_addr, &obtained, errp)) { goto error; } @@ -2977,7 +1055,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; } } @@ -3037,14 +1114,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; } } } @@ -3058,131 +1133,8 @@ error: return NULL; } -#else - -GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - #endif /* HAVE_GETIFADDRS */ -#if !defined(CONFIG_FSFREEZE) - -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -int64_t qmp_guest_fsfreeze_freeze(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, - strList *mountpoints, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -int64_t qmp_guest_fsfreeze_thaw(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - -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 */ - -#if !defined(CONFIG_FSTRIM) -GuestFilesystemTrimResponse * -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} -#endif - -/* add unsupported commands to the list of blocked RPCs */ -GList *ga_command_init_blockedrpcs(GList *blockedrpcs) -{ -#if !defined(__linux__) - { - const char *list[] = { - "guest-suspend-disk", "guest-suspend-ram", - "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus", - "guest-get-memory-blocks", "guest-set-memory-blocks", - "guest-get-memory-block-size", "guest-get-memory-block-info", - NULL}; - char **p = (char **)list; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - } -#endif - -#if !defined(HAVE_GETIFADDRS) - blockedrpcs = g_list_append(blockedrpcs, - g_strdup("guest-network-get-interfaces")); -#endif - -#if !defined(CONFIG_FSFREEZE) - { - const char *list[] = { - "guest-get-fsinfo", "guest-fsfreeze-status", - "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list", - "guest-fsfreeze-thaw", "guest-get-fsinfo", - "guest-get-disks", NULL}; - char **p = (char **)list; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - } -#endif - -#if !defined(CONFIG_FSTRIM) - blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim")); -#endif - - blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices")); - - return blockedrpcs; -} - /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { @@ -3245,17 +1197,9 @@ GuestUserList *qmp_guest_get_users(Error **errp) return head; } -#else +#endif /* HAVE_UTMPX */ -GuestUserList *qmp_guest_get_users(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -#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) @@ -3351,11 +1295,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); } @@ -3375,7 +1316,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) @@ -3394,13 +1334,6 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp) return info; } -GuestDeviceInfoList *qmp_guest_get_devices(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return NULL; -} - #ifndef HOST_NAME_MAX # ifdef _POSIX_HOST_NAME_MAX # define HOST_NAME_MAX _POSIX_HOST_NAME_MAX diff --git a/qga/commands-win32.c b/qga/commands-win32.c index ec9f55b453..038beb8cfa 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); @@ -218,6 +217,9 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, w_path = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr); if (!w_path) { + error_setg(errp, "can't convert 'path' to UTF-16: %s", + gerr->message); + g_error_free(gerr); goto done; } @@ -245,10 +247,6 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, slog("guest-file-open, handle: % " PRId64, fd); done: - if (gerr) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); - g_error_free(gerr); - } g_free(w_path); return fd; } @@ -275,14 +273,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, - "no luid for requested privilege"); + error_setg(errp, "no luid for requested privilege"); goto out; } @@ -290,21 +286,18 @@ 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, - "unable to acquire requested privilege"); + error_setg(errp, "unable to acquire requested privilege"); goto out; } } else { - error_setg(&local_err, QERR_QGA_COMMAND_FAILED, - "failed to open privilege token"); + error_setg(errp, "failed to open privilege token"); } out: if (token) { CloseHandle(token); } - error_propagate(errp, local_err); } static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, @@ -312,19 +305,18 @@ static void execute_async(DWORD WINAPI (*func)(LPVOID), LPVOID opaque, { HANDLE thread = CreateThread(NULL, 0, func, opaque, 0, NULL); if (!thread) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "failed to dispatch asynchronous command"); + error_setg(errp, "failed to dispatch asynchronous command"); } } -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 +479,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 +496,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 +582,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 +603,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 +822,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 +860,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 +930,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 +945,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 +976,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 +1070,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 +1080,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp) address = NULL; } else { disk->address = address; - disk->has_address = true; } QAPI_LIST_PREPEND(ret, disk); @@ -1148,6 +1138,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); fs->has_total_bytes = false; + fs->has_total_bytes_privileged = false; fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); @@ -1212,7 +1203,7 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp) GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) { if (!vss_initialized()) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "fsfreeze not possible as VSS failed to initialize"); return 0; } @@ -1240,7 +1231,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, Error *local_err = NULL; if (!vss_initialized()) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "fsfreeze not possible as VSS failed to initialize"); return 0; } @@ -1275,7 +1266,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) int i; if (!vss_initialized()) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "fsfreeze not possible as VSS failed to initialize"); return 0; } @@ -1369,7 +1360,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 +1377,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 +1392,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; } @@ -1426,22 +1414,19 @@ static void check_suspend_mode(GuestSuspendMode mode, Error **errp) ZeroMemory(&sys_pwr_caps, sizeof(sys_pwr_caps)); if (!GetPwrCapabilities(&sys_pwr_caps)) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "failed to determine guest suspend capabilities"); + error_setg(errp, "failed to determine guest suspend capabilities"); return; } switch (mode) { case GUEST_SUSPEND_MODE_DISK: if (!sys_pwr_caps.SystemS4) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "suspend-to-disk not supported by OS"); + error_setg(errp, "suspend-to-disk not supported by OS"); } break; case GUEST_SUSPEND_MODE_RAM: if (!sys_pwr_caps.SystemS3) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "suspend-to-ram not supported by OS"); + error_setg(errp, "suspend-to-ram not supported by OS"); } break; default: @@ -1509,11 +1494,6 @@ out: } } -void qmp_guest_suspend_hybrid(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); -} - static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp) { IP_ADAPTER_ADDRESSES *adptr_addrs = NULL; @@ -1683,8 +1663,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 +1691,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; } } } @@ -1881,12 +1857,6 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) return NULL; } -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return -1; -} - static gchar * get_net_error_message(gint error) { @@ -1944,7 +1914,7 @@ void qmp_guest_set_user_password(const char *username, GError *gerr = NULL; if (crypted) { - error_setg(errp, QERR_UNSUPPORTED); + error_setg(errp, "'crypted' must be off on this host"); return; } @@ -1957,11 +1927,17 @@ void qmp_guest_set_user_password(const char *username, user = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr); if (!user) { + error_setg(errp, "can't convert 'username' to UTF-16: %s", + gerr->message); + g_error_free(gerr); goto done; } wpass = g_utf8_to_utf16(rawpasswddata, -1, NULL, NULL, &gerr); if (!wpass) { + error_setg(errp, "can't convert 'password' to UTF-16: %s", + gerr->message); + g_error_free(gerr); goto done; } @@ -1977,64 +1953,11 @@ void qmp_guest_set_user_password(const char *username, } done: - if (gerr) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); - g_error_free(gerr); - } g_free(user); g_free(wpass); g_free(rawpasswddata); } -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockResponseList * -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} - -/* add unsupported commands to the list of blocked RPCs */ -GList *ga_command_init_blockedrpcs(GList *blockedrpcs) -{ - const char *list_unsupported[] = { - "guest-suspend-hybrid", - "guest-set-vcpus", - "guest-get-memory-blocks", "guest-set-memory-blocks", - "guest-get-memory-block-size", "guest-get-memory-block-info", - NULL}; - char **p = (char **)list_unsupported; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - - if (!vss_init(true)) { - g_debug("vss_init failed, vss commands are going to be disabled"); - const char *list[] = { - "guest-get-fsinfo", "guest-fsfreeze-status", - "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL}; - p = (char **)list; - - while (*p) { - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++)); - } - } - - return blockedrpcs; -} - /* register init/cleanup routines for stateful command groups */ void ga_command_state_init(GAState *s, GACommandState *cs) { @@ -2113,7 +2036,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 +2055,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) @@ -2188,8 +2108,7 @@ static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) HMODULE module = GetModuleHandle("ntdll"); PVOID fun = GetProcAddress(module, "RtlGetVersion"); if (fun == NULL) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "Failed to get address of RtlGetVersion"); + error_setg(errp, "Failed to get address of RtlGetVersion"); return; } @@ -2198,16 +2117,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 +2187,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 +2252,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 +2388,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 +2411,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); @@ -2537,15 +2445,3 @@ char *qga_get_host_name(Error **errp) return g_utf16_to_utf8(tmp, size, NULL, NULL, 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; -} diff --git a/qga/commands-windows-ssh.c b/qga/commands-windows-ssh.c new file mode 100644 index 0000000000..df45c17b75 --- /dev/null +++ b/qga/commands-windows-ssh.c @@ -0,0 +1,712 @@ +/* + * QEMU Guest Agent win32-specific command implementations for SSH keys. + * The implementation is opinionated and expects the SSH implementation to + * be OpenSSH. + * + * Copyright Schweitzer Engineering Laboratories. 2024 + * + * Authors: + * Aidan Leuck + * + * 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 "commands-common-ssh.h" +#include "commands-windows-ssh.h" +#include "guest-agent-core.h" +#include "limits.h" +#include "lmaccess.h" +#include "lmapibuf.h" +#include "lmerr.h" +#include "qapi/error.h" + +#include "qga-qapi-commands.h" +#include "sddl.h" +#include "shlobj.h" +#include "userenv.h" + +#define AUTHORIZED_KEY_FILE "authorized_keys" +#define AUTHORIZED_KEY_FILE_ADMIN "administrators_authorized_keys" +#define LOCAL_SYSTEM_SID "S-1-5-18" +#define ADMIN_SID "S-1-5-32-544" + +/* + * Frees userInfo structure. This implements the g_auto cleanup + * for the structure. + */ +void free_userInfo(PWindowsUserInfo info) +{ + g_free(info->sshDirectory); + g_free(info->authorizedKeyFile); + LocalFree(info->SSID); + g_free(info->username); + g_free(info); +} + +/* + * Gets the admin SSH folder for OpenSSH. OpenSSH does not store + * the authorized_key file in the users home directory for security reasons and + * instead stores it at %PROGRAMDATA%/ssh. This function returns the path to + * that directory on the users machine + * + * parameters: + * errp -> error structure to set when an error occurs + * returns: The path to the ssh folder in %PROGRAMDATA% or NULL if an error + * occurred. + */ +static char *get_admin_ssh_folder(Error **errp) +{ + /* Allocate memory for the program data path */ + g_autofree char *programDataPath = NULL; + char *authkeys_path = NULL; + PWSTR pgDataW = NULL; + g_autoptr(GError) gerr = NULL; + + /* Get the KnownFolderPath on the machine. */ + HRESULT folderResult = + SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &pgDataW); + if (folderResult != S_OK) { + error_setg(errp, "Failed to retrieve ProgramData folder"); + return NULL; + } + + /* Convert from a wide string back to a standard character string. */ + programDataPath = g_utf16_to_utf8(pgDataW, -1, NULL, NULL, &gerr); + CoTaskMemFree(pgDataW); + if (!programDataPath) { + error_setg(errp, + "Failed converting ProgramData folder path to UTF-16 %s", + gerr->message); + return NULL; + } + + /* Build the path to the file. */ + authkeys_path = g_build_filename(programDataPath, "ssh", NULL); + return authkeys_path; +} + +/* + * Gets the path to the SSH folder for the specified user. If the user is an + * admin it returns the ssh folder located at %PROGRAMDATA%/ssh. If the user is + * not an admin it returns %USERPROFILE%/.ssh + * + * parameters: + * username -> Username to get the SSH folder for + * isAdmin -> Whether the user is an admin or not + * errp -> Error structure to set any errors that occur. + * returns: path to the ssh folder as a string. + */ +static char *get_ssh_folder(const char *username, const bool isAdmin, + Error **errp) +{ + DWORD maxSize = MAX_PATH; + g_autofree char *profilesDir = g_new0(char, maxSize); + + if (isAdmin) { + return get_admin_ssh_folder(errp); + } + + /* If not an Admin the SSH key is in the user directory. */ + /* Get the user profile directory on the machine. */ + BOOL ret = GetProfilesDirectory(profilesDir, &maxSize); + if (!ret) { + error_setg_win32(errp, GetLastError(), + "failed to retrieve profiles directory"); + return NULL; + } + + /* Builds the filename */ + return g_build_filename(profilesDir, username, ".ssh", NULL); +} + +/* + * Creates an entry for the user so they can access the ssh folder in their + * userprofile. + * + * parameters: + * userInfo -> Information about the current user + * pACL -> Pointer to an ACL structure + * errp -> Error structure to set any errors that occur + * returns -> 1 on success, 0 otherwise + */ +static bool create_acl_user(PWindowsUserInfo userInfo, PACL *pACL, Error **errp) +{ + const int aclSize = 1; + PACL newACL = NULL; + EXPLICIT_ACCESS eAccess[1]; + PSID userPSID = NULL; + + /* Get a pointer to the internal SID object in Windows */ + bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID", + userInfo->username); + goto error; + } + + /* Set the permissions for the user. */ + eAccess[0].grfAccessPermissions = GENERIC_ALL; + eAccess[0].grfAccessMode = SET_ACCESS; + eAccess[0].grfInheritance = NO_INHERITANCE; + eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER; + eAccess[0].Trustee.ptstrName = (LPTSTR)userPSID; + + /* Set the ACL entries */ + DWORD setResult; + + /* + * If we are given a pointer that is already initialized, then we can merge + * the existing entries instead of overwriting them. + */ + if (*pACL) { + setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &newACL); + } else { + setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &newACL); + } + + if (setResult != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set ACL entries for user %s %lu", + userInfo->username, setResult); + goto error; + } + + /* Free any old memory since we are going to overwrite the users pointer. */ + LocalFree(*pACL); + *pACL = newACL; + + LocalFree(userPSID); + return true; +error: + LocalFree(userPSID); + return false; +} + +/* + * Creates a base ACL for both normal users and admins to share + * pACL -> Pointer to an ACL structure + * errp -> Error structure to set any errors that occur + * returns: 1 on success, 0 otherwise + */ +static bool create_acl_base(PACL *pACL, Error **errp) +{ + PSID adminGroupPSID = NULL; + PSID systemPSID = NULL; + + const int aclSize = 2; + EXPLICIT_ACCESS eAccess[2]; + + /* Create an entry for the system user. */ + const char *systemSID = LOCAL_SYSTEM_SID; + bool converted = ConvertStringSidToSid(systemSID, &systemPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve system SID"); + goto error; + } + + /* set permissions for system user */ + eAccess[0].grfAccessPermissions = GENERIC_ALL; + eAccess[0].grfAccessMode = SET_ACCESS; + eAccess[0].grfInheritance = NO_INHERITANCE; + eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER; + eAccess[0].Trustee.ptstrName = (LPTSTR)systemPSID; + + /* Create an entry for the admin user. */ + const char *adminSID = ADMIN_SID; + converted = ConvertStringSidToSid(adminSID, &adminGroupPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID"); + goto error; + } + + /* Set permissions for admin group. */ + eAccess[1].grfAccessPermissions = GENERIC_ALL; + eAccess[1].grfAccessMode = SET_ACCESS; + eAccess[1].grfInheritance = NO_INHERITANCE; + eAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + eAccess[1].Trustee.ptstrName = (LPTSTR)adminGroupPSID; + + /* Put the entries in an ACL object. */ + PACL pNewACL = NULL; + DWORD setResult; + + /* + *If we are given a pointer that is already initialized, then we can merge + *the existing entries instead of overwriting them. + */ + if (*pACL) { + setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL); + } else { + setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL); + } + + if (setResult != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set base ACL entries for system user and " + "admin group %lu", + setResult); + goto error; + } + + LocalFree(adminGroupPSID); + LocalFree(systemPSID); + + /* Free any old memory since we are going to overwrite the users pointer. */ + LocalFree(*pACL); + + *pACL = pNewACL; + + return true; + +error: + LocalFree(adminGroupPSID); + LocalFree(systemPSID); + return false; +} + +/* + * Sets the access control on the authorized_keys file and any ssh folders that + * need to be created. For administrators the required permissions on the + * file/folders are that only administrators and the LocalSystem account can + * access the folders. For normal user accounts only the specified user, + * LocalSystem and Administrators can have access to the key. + * + * parameters: + * userInfo -> pointer to structure that contains information about the user + * PACL -> pointer to an access control structure that will be set upon + * successful completion of the function. + * errp -> error structure that will be set upon error. + * returns: 1 upon success 0 upon failure. + */ +static bool create_acl(PWindowsUserInfo userInfo, PACL *pACL, Error **errp) +{ + /* + * Creates a base ACL that both admins and users will share + * This adds the Administrators group and the SYSTEM group + */ + if (!create_acl_base(pACL, errp)) { + return false; + } + + /* + * If the user is not an admin give the user creating the key permission to + * access the file. + */ + if (!userInfo->isAdmin) { + if (!create_acl_user(userInfo, pACL, errp)) { + return false; + } + + return true; + } + + return true; +} +/* + * Create the SSH directory for the user and d sets appropriate permissions. + * In general the directory will be %PROGRAMDATA%/ssh if the user is an admin. + * %USERPOFILE%/.ssh if not an admin + * + * parameters: + * userInfo -> Contains information about the user + * errp -> Structure that will contain errors if the function fails. + * returns: zero upon failure, 1 upon success + */ +static bool create_ssh_directory(WindowsUserInfo *userInfo, Error **errp) +{ + PACL pNewACL = NULL; + g_autofree PSECURITY_DESCRIPTOR pSD = NULL; + + /* Gets the appropriate ACL for the user */ + if (!create_acl(userInfo, &pNewACL, errp)) { + goto error; + } + + /* Allocate memory for a security descriptor */ + pSD = g_malloc(SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { + error_setg_win32(errp, GetLastError(), + "Failed to initialize security descriptor"); + goto error; + } + + /* Associate the security descriptor with the ACL permissions. */ + if (!SetSecurityDescriptorDacl(pSD, TRUE, pNewACL, FALSE)) { + error_setg_win32(errp, GetLastError(), + "Failed to set security descriptor ACL"); + goto error; + } + + /* Set the security attributes on the folder */ + SECURITY_ATTRIBUTES sAttr; + sAttr.bInheritHandle = FALSE; + sAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + sAttr.lpSecurityDescriptor = pSD; + + /* Create the directory with the created permissions */ + BOOL created = CreateDirectory(userInfo->sshDirectory, &sAttr); + if (!created) { + error_setg_win32(errp, GetLastError(), "failed to create directory %s", + userInfo->sshDirectory); + goto error; + } + + /* Free memory */ + LocalFree(pNewACL); + return true; +error: + LocalFree(pNewACL); + return false; +} + +/* + * Sets permissions on the authorized_key_file that is created. + * + * parameters: userInfo -> Information about the user + * errp -> error structure that will contain errors upon failure + * returns: 1 upon success, zero upon failure. + */ +static bool set_file_permissions(PWindowsUserInfo userInfo, Error **errp) +{ + PACL pACL = NULL; + PSID userPSID = NULL; + + /* Creates the access control structure */ + if (!create_acl(userInfo, &pACL, errp)) { + goto error; + } + + /* Get the PSID structure for the user based off the string SID. */ + bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID", + userInfo->username); + goto error; + } + + /* Prevents permissions from being inherited and use the DACL provided. */ + const SE_OBJECT_TYPE securityBitFlags = + DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; + + /* Set the ACL on the file. */ + if (SetNamedSecurityInfo(userInfo->authorizedKeyFile, SE_FILE_OBJECT, + securityBitFlags, userPSID, NULL, pACL, + NULL) != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set file security for file %s", + userInfo->authorizedKeyFile); + goto error; + } + + LocalFree(pACL); + LocalFree(userPSID); + return true; + +error: + LocalFree(pACL); + LocalFree(userPSID); + + return false; +} + +/* + * Writes the specified keys to the authenticated keys file. + * parameters: + * userInfo: Information about the user we are writing the authkeys file to. + * authkeys: Array of keys to write to disk + * errp: Error structure that will contain any errors if they occur. + * returns: 1 if successful, 0 otherwise. + */ +static bool write_authkeys(WindowsUserInfo *userInfo, GStrv authkeys, + Error **errp) +{ + g_autofree char *contents = NULL; + g_autoptr(GError) err = NULL; + + contents = g_strjoinv("\n", authkeys); + + if (!g_file_set_contents(userInfo->authorizedKeyFile, contents, -1, &err)) { + error_setg(errp, "failed to write to '%s': %s", + userInfo->authorizedKeyFile, err->message); + return false; + } + + if (!set_file_permissions(userInfo, errp)) { + return false; + } + + return true; +} + +/* + * Retrieves information about a Windows user by their username + * + * parameters: + * userInfo -> Double pointer to a WindowsUserInfo structure. Upon success, it + * will be allocated with information about the user and need to be freed. + * username -> Name of the user to lookup. + * errp -> Contains any errors that occur. + * returns: 1 upon success, 0 upon failure. + */ +static bool get_user_info(PWindowsUserInfo *userInfo, const char *username, + Error **errp) +{ + DWORD infoLevel = 4; + LPUSER_INFO_4 uBuf = NULL; + g_autofree wchar_t *wideUserName = NULL; + g_autoptr(GError) gerr = NULL; + PSID psid = NULL; + + /* + * Converts a string to a Windows wide string since the GetNetUserInfo + * function requires it. + */ + wideUserName = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr); + if (!wideUserName) { + goto error; + } + + /* allocate data */ + PWindowsUserInfo uData = g_new0(WindowsUserInfo, 1); + + /* Set pointer so it can be cleaned up by the callee, even upon error. */ + *userInfo = uData; + + /* Find the information */ + NET_API_STATUS result = + NetUserGetInfo(NULL, wideUserName, infoLevel, (LPBYTE *)&uBuf); + if (result != NERR_Success) { + /* Give a friendlier error message if the user was not found. */ + if (result == NERR_UserNotFound) { + error_setg(errp, "User %s was not found", username); + goto error; + } + + error_setg(errp, + "Received unexpected error when asking for user info: Error " + "Code %lu", + result); + goto error; + } + + /* Get information from the buffer returned by NetUserGetInfo. */ + uData->username = g_strdup(username); + uData->isAdmin = uBuf->usri4_priv == USER_PRIV_ADMIN; + psid = uBuf->usri4_user_sid; + + char *sidStr = NULL; + + /* + * We store the string representation of the SID not SID structure in + * memory. Callees wanting to use the SID structure should call + * ConvertStringSidToSID. + */ + if (!ConvertSidToStringSid(psid, &sidStr)) { + error_setg_win32(errp, GetLastError(), + "failed to get SID string for user %s", username); + goto error; + } + + /* Store the SSID */ + uData->SSID = sidStr; + + /* Get the SSH folder for the user. */ + char *sshFolder = get_ssh_folder(username, uData->isAdmin, errp); + if (sshFolder == NULL) { + goto error; + } + + /* Get the authorized key file path */ + const char *authorizedKeyFile = + uData->isAdmin ? AUTHORIZED_KEY_FILE_ADMIN : AUTHORIZED_KEY_FILE; + char *authorizedKeyPath = + g_build_filename(sshFolder, authorizedKeyFile, NULL); + uData->sshDirectory = sshFolder; + uData->authorizedKeyFile = authorizedKeyPath; + + /* Free */ + NetApiBufferFree(uBuf); + return true; +error: + if (uBuf) { + NetApiBufferFree(uBuf); + } + + return false; +} + +/* + * Gets the list of authorized keys for a user. + * + * parameters: + * username -> Username to retrieve the keys for. + * errp -> Error structure that will display any errors through QMP. + * returns: List of keys associated with the user. + */ +GuestAuthorizedKeys *qmp_guest_ssh_get_authorized_keys(const char *username, + Error **errp) +{ + GuestAuthorizedKeys *keys = NULL; + g_auto(GStrv) authKeys = NULL; + g_autoptr(GuestAuthorizedKeys) ret = NULL; + g_auto(PWindowsUserInfo) userInfo = NULL; + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return NULL; + } + + /* Reads authkeys for the user */ + authKeys = read_authkeys(userInfo->authorizedKeyFile, errp); + if (authKeys == NULL) { + return NULL; + } + + /* Set the GuestAuthorizedKey struct with keys from the file */ + ret = g_new0(GuestAuthorizedKeys, 1); + for (int i = 0; authKeys[i] != NULL; i++) { + g_strstrip(authKeys[i]); + if (!authKeys[i][0] || authKeys[i][0] == '#') { + continue; + } + + QAPI_LIST_PREPEND(ret->keys, g_strdup(authKeys[i])); + } + + /* + * Steal the pointer because it is up for the callee to deallocate the + * memory. + */ + keys = g_steal_pointer(&ret); + return keys; +} + +/* + * Adds an ssh key for a user. + * + * parameters: + * username -> User to add the SSH key to + * strList -> Array of keys to add to the list + * has_reset -> Whether the keys have been reset + * reset -> Boolean to reset the keys (If this is set the existing list will be + * cleared) and the other key reset. errp -> Pointer to an error structure that + * will get returned over QMP if anything goes wrong. + */ +void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, + bool has_reset, bool reset, Error **errp) +{ + g_auto(PWindowsUserInfo) userInfo = NULL; + g_auto(GStrv) authkeys = NULL; + strList *k; + size_t nkeys, nauthkeys; + + /* Make sure the keys given are valid */ + if (!check_openssh_pub_keys(keys, &nkeys, errp)) { + return; + } + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return; + } + + /* Determine whether we should reset the keys */ + reset = has_reset && reset; + if (!reset) { + /* Read existing keys into memory */ + authkeys = read_authkeys(userInfo->authorizedKeyFile, NULL); + } + + /* Check that the SSH key directory exists for the user. */ + if (!g_file_test(userInfo->sshDirectory, G_FILE_TEST_IS_DIR)) { + BOOL success = create_ssh_directory(userInfo, errp); + if (!success) { + return; + } + } + + /* Reallocates the buffer to fit the new keys. */ + nauthkeys = authkeys ? g_strv_length(authkeys) : 0; + authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *)); + + /* zero out the memory for the reallocated buffer */ + memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *)); + + /* Adds the keys */ + for (k = keys; k != NULL; k = k->next) { + /* Check that the key doesn't already exist */ + if (g_strv_contains((const gchar *const *)authkeys, k->value)) { + continue; + } + + authkeys[nauthkeys++] = g_strdup(k->value); + } + + /* Write the authkeys to the file. */ + write_authkeys(userInfo, authkeys, errp); +} + +/* + * Removes an SSH key for a user + * + * parameters: + * username -> Username to remove the key from + * strList -> List of strings to remove + * errp -> Contains any errors that occur. + */ +void qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys, + Error **errp) +{ + g_auto(PWindowsUserInfo) userInfo = NULL; + g_autofree struct passwd *p = NULL; + g_autofree GStrv new_keys = NULL; /* do not own the strings */ + g_auto(GStrv) authkeys = NULL; + GStrv a; + size_t nkeys = 0; + + /* Validates the keys passed in by the user */ + if (!check_openssh_pub_keys(keys, NULL, errp)) { + return; + } + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return; + } + + /* Reads the authkeys for the user */ + authkeys = read_authkeys(userInfo->authorizedKeyFile, errp); + if (authkeys == NULL) { + return; + } + + /* Create a new buffer to hold the keys */ + new_keys = g_new0(char *, g_strv_length(authkeys) + 1); + for (a = authkeys; *a != NULL; a++) { + strList *k; + + /* Filters out keys that are equal to ones the user specified. */ + for (k = keys; k != NULL; k = k->next) { + if (g_str_equal(k->value, *a)) { + break; + } + } + + if (k != NULL) { + continue; + } + + new_keys[nkeys++] = *a; + } + + /* Write the new authkeys to the file. */ + write_authkeys(userInfo, new_keys, errp); +} diff --git a/qga/commands-windows-ssh.h b/qga/commands-windows-ssh.h new file mode 100644 index 0000000000..40ac67c4d9 --- /dev/null +++ b/qga/commands-windows-ssh.h @@ -0,0 +1,26 @@ +/* + * Header file for commands-windows-ssh.c + * + * Copyright Schweitzer Engineering Laboratories. 2024 + * + * Authors: + * Aidan Leuck + * + * 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 +typedef struct WindowsUserInfo { + char *sshDirectory; + char *authorizedKeyFile; + char *username; + char *SSID; + bool isAdmin; +} WindowsUserInfo; + +typedef WindowsUserInfo *PWindowsUserInfo; + +void free_userInfo(PWindowsUserInfo info); +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(PWindowsUserInfo, free_userInfo, NULL); diff --git a/qga/commands.c b/qga/commands.c index 6cf978322e..5a5fad31f8 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -15,7 +15,6 @@ #include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/base64.h" #include "qemu/cutils.h" #include "commands-common.h" @@ -154,7 +153,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; } @@ -205,18 +204,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); @@ -272,12 +269,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; @@ -381,11 +392,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; @@ -398,14 +421,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; @@ -417,15 +442,39 @@ 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); + error_setg(errp, "%s", gerr->message); g_error_free(gerr); goto done; } @@ -437,7 +486,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 @@ -536,8 +585,7 @@ GuestTimezone *qmp_guest_get_timezone(Error **errp) info = g_new0(GuestTimezone, 1); tz = g_time_zone_new_local(); if (tz == NULL) { - error_setg(errp, QERR_QGA_COMMAND_FAILED, - "Couldn't retrieve local timezone"); + error_setg(errp, "Couldn't retrieve local timezone"); goto error; } @@ -546,7 +594,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/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index 3442383627..df572adb4a 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -102,36 +102,51 @@ - - - + + + + + + + + + + + + + + + + - + diff --git a/qga/main.c b/qga/main.c index b3580508fa..50186760bf 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" @@ -40,11 +39,11 @@ #include "commands-common.h" #ifndef _WIN32 -#ifdef __FreeBSD__ +#ifdef CONFIG_BSD #define QGA_VIRTIO_PATH_DEFAULT "/dev/vtcon/org.qemu.guest_agent.0" -#else /* __FreeBSD__ */ +#else /* CONFIG_BSD */ #define QGA_VIRTIO_PATH_DEFAULT "/dev/virtio-ports/org.qemu.guest_agent.0" -#endif /* __FreeBSD__ */ +#endif /* CONFIG_BSD */ #define QGA_SERIAL_PATH_DEFAULT "/dev/ttyS0" #define QGA_STATE_RELATIVE_DIR "run" #else @@ -71,6 +70,28 @@ typedef struct GAPersistentState { typedef struct GAConfig GAConfig; +struct GAConfig { + char *channel_path; + char *method; + char *log_filepath; + char *pid_filepath; +#ifdef CONFIG_FSFREEZE + char *fsfreeze_hook; +#endif + char *state_dir; +#ifdef _WIN32 + const char *service; +#endif + 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; + bool retry_path; +}; + struct GAState { JSONMessageParser parser; GMainLoop *main_loop; @@ -83,10 +104,12 @@ struct GAState { #ifdef _WIN32 GAService service; HANDLE wakeup_event; + HANDLE event_log; #endif bool delimit_response; bool frozen; GList *blockedrpcs; + GList *allowedrpcs; char *state_filepath_isfrozen; struct { const char *log_filepath; @@ -225,12 +248,16 @@ static void usage(const char *cmd) #ifdef CONFIG_FSFREEZE g_autofree char *fsfreeze_hook = get_relocated_path(QGA_FSFREEZE_HOOK_DEFAULT); #endif + g_autofree char *conf_path = get_relocated_path(QGA_CONF_DEFAULT); printf( "Usage: %s [-m -p ] []\n" "QEMU Guest Agent " QEMU_FULL_VERSION "\n" QEMU_COPYRIGHT "\n" "\n" +" -c, --config=PATH configuration file path (default is\n" +" %s/qemu-ga.conf\n" +" unless overridden by the QGA_CONF environment variable)\n" " -m, --method transport method: one of unix-listen, virtio-serial,\n" " isa-serial, or vsock-listen (virtio-serial is the default)\n" " -p, --path device/socket path (the default for virtio-serial is:\n" @@ -260,7 +287,9 @@ QEMU_COPYRIGHT "\n" " -s, --service service commands: install, uninstall, vss-install, vss-uninstall\n" #endif " -b, --block-rpcs comma-separated list of RPCs to disable (no spaces,\n" -" use \"help\" to list available RPCs)\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" @@ -269,8 +298,8 @@ QEMU_COPYRIGHT "\n" " plug/unplug, etc.)\n" " -h, --help display this help and exit\n" "\n" -QEMU_HELP_BOTTOM "\n" - , cmd, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, +QEMU_HELP_BOTTOM "\n", + cmd, conf_path, QGA_VIRTIO_PATH_DEFAULT, QGA_SERIAL_PATH_DEFAULT, dfl_pathnames.pidfile, #ifdef CONFIG_FSFREEZE fsfreeze_hook, @@ -313,6 +342,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) { @@ -324,13 +385,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); @@ -361,38 +423,79 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2) return strcmp(str1, str2); } -/* disable commands that aren't safe for fsfreeze */ -static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque) +static bool ga_command_is_allowed(const QmpCommand *cmd, GAState *state) { - bool allowed = false; int i = 0; + GAConfig *config = state->config; const char *name = qmp_command_name(cmd); + /* Fallback policy is allow everything */ + bool allowed = true; - while (ga_freeze_allowlist[i] != NULL) { - if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + if (config->allowedrpcs) { + /* + * If an allow-list is given, this changes the fallback + * policy to deny everything + */ + allowed = false; + + if (g_list_find_custom(config->allowedrpcs, name, ga_strcmp) != NULL) { allowed = true; } - i++; } - if (!allowed) { - g_debug("disabling command: %s", name); - qmp_disable_command(&ga_commands, name, "the agent is in frozen state"); + + /* + * If both allowedrpcs and blockedrpcs are set, the blocked + * list will take priority + */ + if (config->blockedrpcs) { + if (g_list_find_custom(config->blockedrpcs, name, ga_strcmp) != NULL) { + allowed = false; + } } + + /* + * If frozen, this filtering must take priority over + * absolutely everything + */ + if (state->frozen) { + allowed = false; + + while (ga_freeze_allowlist[i] != NULL) { + if (strcmp(name, ga_freeze_allowlist[i]) == 0) { + allowed = true; + } + i++; + } + } + + return allowed; } -/* [re-]enable all commands, except those explicitly blocked by user */ -static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque) +static void ga_apply_command_filters_iter(const QmpCommand *cmd, void *opaque) { - GList *blockedrpcs = opaque; + GAState *state = opaque; + bool want = ga_command_is_allowed(cmd, state); + bool have = qmp_command_is_enabled(cmd); const char *name = qmp_command_name(cmd); - if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL && - !qmp_command_is_enabled(cmd)) { + if (want == have) { + return; + } + + if (have) { + g_debug("disabling command: %s", name); + qmp_disable_command(&ga_commands, name, "the command is not allowed"); + } else { g_debug("enabling command: %s", name); qmp_enable_command(&ga_commands, name); } } +static void ga_apply_command_filters(GAState *state) +{ + qmp_for_each_command(&ga_commands, ga_apply_command_filters_iter, state); +} + static bool ga_create_file(const char *path) { int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR); @@ -425,15 +528,14 @@ void ga_set_frozen(GAState *s) if (ga_is_frozen(s)) { return; } - /* disable all forbidden (for frozen state) commands */ - qmp_for_each_command(&ga_commands, ga_disable_not_allowed, NULL); g_warning("disabling logging due to filesystem freeze"); - ga_disable_logging(s); s->frozen = true; if (!ga_create_file(s->state_filepath_isfrozen)) { g_warning("unable to create %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } + ga_apply_command_filters(s); + ga_disable_logging(s); } void ga_unset_frozen(GAState *s) @@ -464,13 +566,13 @@ void ga_unset_frozen(GAState *s) s->deferred_options.pid_filepath = NULL; } - /* enable all disabled, non-blocked commands */ - qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s->blockedrpcs); + /* enable all disabled, non-blocked and allowed commands */ s->frozen = false; if (!ga_delete_file(s->state_filepath_isfrozen)) { g_warning("unable to delete %s, fsfreeze may not function properly", s->state_filepath_isfrozen); } + ga_apply_command_filters(s); } #ifdef CONFIG_FSFREEZE @@ -938,36 +1040,14 @@ static GList *split_list(const gchar *str, const gchar *delim) return list; } -struct GAConfig { - char *channel_path; - char *method; - char *log_filepath; - char *pid_filepath; -#ifdef CONFIG_FSFREEZE - char *fsfreeze_hook; -#endif - char *state_dir; -#ifdef _WIN32 - const char *service; -#endif - gchar *bliststr; /* blockedrpcs may point to this string */ - GList *blockedrpcs; - int daemonize; - GLogLevelFlags log_level; - int dumpconf; - bool retry_path; -}; - -static void config_load(GAConfig *config) +static void config_load(GAConfig *config, const char *confpath, bool required) { 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(); - if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) { + if (!g_key_file_load_from_file(keyfile, confpath, 0, &gerr)) { goto end; } if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) { @@ -1011,24 +1091,25 @@ static void config_load(GAConfig *config) 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)) { + if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL)) { config->bliststr = - g_key_file_get_string(keyfile, "general", blockrpcs_key, &gerr); + g_key_file_get_string(keyfile, "general", "block-rpcs", &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, ",")); + } end: g_key_file_free(keyfile); - if (gerr && - !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT)) { + if (gerr && (required || + !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT))) { g_critical("error loading configuration from path: %s, %s", - conf, gerr->message); + confpath, gerr->message); exit(EXIT_FAILURE); } g_clear_error(&gerr); @@ -1082,6 +1163,9 @@ static void config_dump(GAConfig *config) 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); if (error) { @@ -1097,11 +1181,12 @@ 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 = "hVvdc:m:p:l:f:F::b:a:s:t:Dr"; int opt_ind = 0, ch; const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, + { "config", 1, NULL, 'c' }, { "dump-conf", 0, NULL, 'D' }, { "logfile", 1, NULL, 'l' }, { "pidfile", 1, NULL, 'f' }, @@ -1113,7 +1198,7 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "path", 1, NULL, 'p' }, { "daemonize", 0, NULL, 'd' }, { "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 @@ -1121,6 +1206,26 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "retry-path", 0, NULL, 'r' }, { NULL, 0, NULL, 0 } }; + g_autofree char *confpath = g_strdup(g_getenv("QGA_CONF")) ?: + get_relocated_path(QGA_CONF_DEFAULT); + bool confrequired = false; + + while ((ch = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) { + switch (ch) { + case 'c': + g_free(confpath); + confpath = g_strdup(optarg); + confrequired = true; + break; + default: + break; + } + } + + config_load(config, confpath, confrequired); + + /* Reset for second pass */ + optind = 1; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -1175,6 +1280,15 @@ static void config_parse(GAConfig *config, int argc, char **argv) split_list(optarg, ",")); 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, ",")); + break; + } #ifdef _WIN32 case 's': config->service = optarg; @@ -1223,10 +1337,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->blockedrpcs, g_free); + g_list_free_full(config->allowedrpcs, g_free); g_free(config); } @@ -1236,7 +1352,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) { @@ -1286,6 +1402,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 @@ -1297,6 +1420,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) " '%s': %s", config->state_dir, strerror(errno)); return NULL; } + + if (!vss_init(true)) { + g_debug("vss_init failed, vss commands will not function"); + } #endif if (ga_is_frozen(s)) { @@ -1310,7 +1437,6 @@ 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_not_allowed, NULL); } else { if (config->daemonize) { become_daemon(config->pid_filepath); @@ -1334,16 +1460,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) return NULL; } - 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); - l = g_list_next(l); - } while (l); - } s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); @@ -1369,6 +1485,8 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) } #endif + ga_apply_command_filters(s); + ga_state = s; return s; } @@ -1377,6 +1495,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); @@ -1471,7 +1590,6 @@ int main(int argc, char **argv) qga_qmp_init_marshal(&ga_commands); init_dfl_pathnames(); - config_load(config); config_parse(config, argc, argv); if (config->pid_filepath == NULL) { diff --git a/qga/meson.build b/qga/meson.build index 3cfb9166e5..587ec4e5e8 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() @@ -66,45 +66,66 @@ qga_ss.add(files( 'guest-agent-command-state.c', 'main.c', 'cutils.c', + 'commands-common-ssh.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_LINUX', if_true: files( - 'commands-linux.c', -)) -qga_ss.add(when: 'CONFIG_BSD', if_true: files( - 'commands-bsd.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', + 'commands-windows-ssh.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'] + '-lsetupapi', '-lcfgmgr32', '-luserenv'] if have_qga_vss qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup'] subdir('vss-win32') 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'] @@ -123,6 +144,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), @@ -132,10 +161,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) @@ -153,13 +183,12 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) -# disable qga-ssh-test for now. glib's G_TEST_OPTION_ISOLATE_DIRS triggers +# disable qga-ssh-test with fuzzing: glib's G_TEST_OPTION_ISOLATE_DIRS triggers # 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 false - srcs = [files('commands-posix-ssh.c')] +if host_os != 'windows' and not get_option('fuzzing') + srcs = [files('commands-common-ssh.c', 'commands-posix-ssh.c')] i = 0 foreach output: qga_qapi_outputs if output.startswith('qga-qapi-types') or output.startswith('qga-qapi-visit') 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 796434ed34..0537bb7886 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1,15 +1,6 @@ # *-*- Mode: Python -*-* # vim: filetype=python -## -# = 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. -## - ## # = QEMU guest agent protocol commands and structs ## @@ -33,33 +24,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 +69,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 +113,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 +128,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 +159,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 +195,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 +219,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 +234,6 @@ # # @handle: filehandle returned by guest-file-open # -# Returns: Nothing on success. -# # Since: 0.15.0 ## { 'command': 'guest-file-close', @@ -259,7 +245,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 +259,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 +282,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 +300,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 +332,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 +347,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 +361,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 +370,7 @@ # # @whence: Symbolic or numeric code for interpreting offset # -# Returns: @GuestFileSeek on success. +# Returns: @GuestFileSeek # # Since: 0.15.0 ## @@ -396,8 +386,6 @@ # # @handle: filehandle returned by guest-file-open # -# Returns: Nothing on success. -# # Since: 0.15.0 ## { 'command': 'guest-file-flush', @@ -415,62 +403,70 @@ # Since: 0.15.0 ## { 'enum': 'GuestFsfreezeStatus', - 'data': [ 'thawed', 'frozen' ] } + 'data': [ 'thawed', 'frozen' ], + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @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 ## { 'command': 'guest-fsfreeze-status', - 'returns': 'GuestFsfreezeStatus' } + 'returns': 'GuestFsfreezeStatus', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @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 ## { 'command': 'guest-fsfreeze-freeze', - 'returns': 'int' } + 'returns': 'int', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @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 ## { 'command': 'guest-fsfreeze-freeze-list', 'data': { '*mountpoints': ['str'] }, - 'returns': 'int' } + 'returns': 'int', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @guest-fsfreeze-thaw: @@ -479,30 +475,34 @@ # # 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. +# .. note:: If the 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. # # Since: 0.15.0 ## { 'command': 'guest-fsfreeze-thaw', - 'returns': 'int' } + 'returns': 'int', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } } ## # @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 ## { 'struct': 'GuestFilesystemTrimResult', 'data': {'path': 'str', - '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} } + '*trimmed': 'int', '*minimum': 'int', '*error': 'str'}, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @GuestFilesystemTrimResponse: @@ -512,127 +512,134 @@ # Since: 2.4 ## { 'struct': 'GuestFilesystemTrimResponse', - 'data': {'paths': ['GuestFilesystemTrimResult']} } + 'data': {'paths': ['GuestFilesystemTrimResult']}, + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @guest-fstrim: # # 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 ## { 'command': 'guest-fstrim', 'data': { '*minimum': 'int' }, - 'returns': 'GuestFilesystemTrimResponse' } + 'returns': 'GuestFilesystemTrimResponse', + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } } ## # @guest-suspend-disk: # # 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 +# .. note:: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes. # # Since: 1.1 ## -{ 'command': 'guest-suspend-disk', 'success-response': false } +{ 'command': 'guest-suspend-disk', 'success-response': false, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-suspend-ram: # # 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 +# .. note:: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes. # # Since: 1.1 ## -{ 'command': 'guest-suspend-ram', 'success-response': false } +{ 'command': 'guest-suspend-ram', 'success-response': false, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-suspend-hybrid: # # 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 +# .. note:: It's strongly recommended to issue the guest-sync command +# before sending commands when the guest resumes. # # Since: 1.1 ## -{ 'command': 'guest-suspend-hybrid', 'success-response': false } +{ 'command': 'guest-suspend-hybrid', 'success-response': false, + 'if': 'CONFIG_LINUX' } ## # @GuestIpAddressType: @@ -646,7 +653,8 @@ # Since: 1.1 ## { 'enum': 'GuestIpAddressType', - 'data': [ 'ipv4', 'ipv6' ] } + 'data': [ 'ipv4', 'ipv6' ], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestIpAddress: @@ -662,7 +670,8 @@ { 'struct': 'GuestIpAddress', 'data': {'ip-address': 'str', 'ip-address-type': 'GuestIpAddressType', - 'prefix': 'int'} } + 'prefix': 'int'}, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestNetworkInterfaceStat: @@ -694,7 +703,8 @@ 'tx-packets': 'uint64', 'tx-errs': 'uint64', 'tx-dropped': 'uint64' - } } + }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestNetworkInterface: @@ -705,8 +715,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 ## @@ -714,20 +724,21 @@ 'data': {'name': 'str', '*hardware-address': 'str', '*ip-addresses': ['GuestIpAddress'], - '*statistics': 'GuestNetworkInterfaceStat' } } + '*statistics': 'GuestNetworkInterfaceStat' }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @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 ## { 'command': 'guest-network-get-interfaces', - 'returns': ['GuestNetworkInterface'] } + 'returns': ['GuestNetworkInterface'], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } } ## # @GuestLogicalProcessor: @@ -736,17 +747,18 @@ # # @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 ## { 'struct': 'GuestLogicalProcessor', 'data': {'logical-id': 'int', 'online': 'bool', - '*can-offline': 'bool'} } + '*can-offline': 'bool'}, + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @guest-get-vcpus: @@ -755,53 +767,57 @@ # # 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 ## { 'command': 'guest-get-vcpus', - 'returns': ['GuestLogicalProcessor'] } + 'returns': ['GuestLogicalProcessor'], + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } } ## # @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 ## { 'command': 'guest-set-vcpus', 'data': {'vcpus': ['GuestLogicalProcessor'] }, - 'returns': 'int' } + 'returns': 'int', + 'if': 'CONFIG_LINUX' } ## # @GuestDiskBusType: @@ -809,24 +825,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 @@ -834,29 +869,37 @@ { 'enum': 'GuestDiskBusType', 'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata', 'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi', - 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ] } + 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestPCIAddress: # # @domain: domain id +# # @bus: bus id +# # @slot: slot id +# # @function: function id # # Since: 2.2 ## { 'struct': 'GuestPCIAddress', 'data': {'domain': 'int', 'bus': 'int', - 'slot': 'int', 'function': 'int'} } + 'slot': 'int', 'function': 'int'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestCCWAddress: # # @cssid: channel subsystem image id +# # @ssid: subchannel set id +# # @subchno: subchannel number +# # @devno: device number # # Since: 6.0 @@ -865,18 +908,27 @@ 'data': {'cssid': 'int', 'ssid': 'int', 'subchno': 'int', - 'devno': 'int'} } + 'devno': 'int'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @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 @@ -886,13 +938,16 @@ 'bus-type': 'GuestDiskBusType', 'bus': 'int', 'target': 'int', 'unit': 'int', '*serial': 'str', '*dev': 'str', - '*ccw-address': 'GuestCCWAddress'} } + '*ccw-address': 'GuestCCWAddress'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @GuestNVMeSmart: # -# NVMe smart informations, based on NVMe specification, -# section +# NVMe smart information, based on NVMe specification, section +# +# +# TODO: document members briefly # # Since: 7.1 ## @@ -921,33 +976,40 @@ 'media-errors-lo': 'uint64', 'media-errors-hi': 'uint64', 'number-of-error-log-entries-lo': 'uint64', - 'number-of-error-log-entries-hi': 'uint64' } } + 'number-of-error-log-entries-hi': 'uint64' }, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @GuestDiskSmart: # # Disk type related smart information. # -# - @nvme: NVMe disk smart +# @type: disk bus type # # Since: 7.1 ## { 'union': 'GuestDiskSmart', 'base': { 'type': 'GuestDiskBusType' }, 'discriminator': 'type', - 'data': { 'nvme': 'GuestNVMeSmart' } } + 'data': { 'nvme': 'GuestNVMeSmart' }, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @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 @@ -955,97 +1017,112 @@ { 'struct': 'GuestDiskInfo', 'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'], '*address': 'GuestDiskAddress', '*alias': 'str', - '*smart': 'GuestDiskSmart'} } + '*smart': 'GuestDiskSmart'}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @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 ## { 'command': 'guest-get-disks', - 'returns': ['GuestDiskInfo'] } + 'returns': ['GuestDiskInfo'], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } } ## # @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 +# +# @total-bytes: filesystem capacity in bytes for unprivileged users (since 3.0) +# +# @total-bytes-privileged: filesystem capacity in bytes for privileged users +# (since 9.1) +# +# @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 ## { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', '*used-bytes': 'uint64', '*total-bytes': 'uint64', - 'disk': ['GuestDiskAddress']} } + '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']}, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @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 ## { 'command': 'guest-get-fsinfo', - 'returns': ['GuestFilesystemInfo'] } + 'returns': ['GuestFilesystemInfo'], + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } } ## # @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 ## { 'command': 'guest-set-user-password', - 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } } + 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' }, + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX', 'CONFIG_FREEBSD'] } } ## # @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 ## { 'struct': 'GuestMemoryBlock', 'data': {'phys-index': 'uint64', 'online': 'bool', - '*can-offline': 'bool'} } + '*can-offline': 'bool'}, + 'if': 'CONFIG_LINUX' } ## # @guest-get-memory-blocks: @@ -1054,32 +1131,39 @@ # # 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 ## { 'command': 'guest-get-memory-blocks', - 'returns': ['GuestMemoryBlock'] } + 'returns': ['GuestMemoryBlock'], + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockResponseType: # # 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 ## { 'enum': 'GuestMemoryBlockResponseType', 'data': ['success', 'not-found', 'operation-not-supported', - 'operation-failed'] } + 'operation-failed'], + 'if': 'CONFIG_LINUX' } ## # @GuestMemoryBlockResponse: @@ -1088,57 +1172,62 @@ # # @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 ## { 'struct': 'GuestMemoryBlockResponse', 'data': { 'phys-index': 'uint64', 'response': 'GuestMemoryBlockResponseType', - '*error-code': 'int' }} + '*error-code': 'int' }, + 'if': 'CONFIG_LINUX'} ## # @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 ## { 'command': 'guest-set-memory-blocks', 'data': {'mem-blks': ['GuestMemoryBlock'] }, - 'returns': ['GuestMemoryBlockResponse'] } + 'returns': ['GuestMemoryBlockResponse'], + 'if': 'CONFIG_LINUX' } ## # @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 ## { 'struct': 'GuestMemoryBlockInfo', - 'data': {'size': 'uint64'} } + 'data': {'size': 'uint64'}, + 'if': 'CONFIG_LINUX' } ## # @guest-get-memory-block-info: @@ -1150,23 +1239,32 @@ # Since: 2.3 ## { 'command': 'guest-get-memory-block-info', - 'returns': 'GuestMemoryBlockInfo' } + 'returns': 'GuestMemoryBlockInfo', + 'if': 'CONFIG_LINUX' } ## # @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 +1275,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 +1299,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 +1379,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,18 +1395,23 @@ # @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 ## { 'struct': 'GuestUser', - 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } } + 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' }, + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } } ## # @guest-get-users: +# # Retrieves a list of currently active users on the VM. # # Returns: A unique list of users. @@ -1272,15 +1419,17 @@ # Since: 2.10 ## { 'command': 'guest-get-users', - 'returns': ['GuestUser'] } + 'returns': ['GuestUser'], + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } } ## # @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 +1452,55 @@ # @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: +# .. note:: 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,40 +1525,50 @@ ## # @GuestDeviceType: +# +# @pci: PCI device ## { 'enum': 'GuestDeviceType', - 'data': [ 'pci' ] } + 'data': [ 'pci' ], + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceIdPCI: # # @vendor-id: vendor ID +# # @device-id: device ID # # Since: 5.2 ## { 'struct': 'GuestDeviceIdPCI', - 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' } } + 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' }, + 'if': 'CONFIG_WIN32' } ## # @GuestDeviceId: # # Id of the device -# - @pci: PCI ID, since: 5.2 +# +# @type: device type # # Since: 5.2 ## { 'union': 'GuestDeviceId', 'base': { 'type': 'GuestDeviceType' }, 'discriminator': 'type', - 'data': { 'pci': 'GuestDeviceIdPCI' } } + 'data': { 'pci': 'GuestDeviceIdPCI' }, + 'if': 'CONFIG_WIN32' } ## # @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 @@ -1410,7 +1579,8 @@ '*driver-date': 'int', '*driver-version': 'str', '*id': 'GuestDeviceId' - } } + }, + 'if': 'CONFIG_WIN32' } ## # @guest-get-devices: @@ -1422,7 +1592,8 @@ # Since: 5.2 ## { 'command': 'guest-get-devices', - 'returns': ['GuestDeviceInfo'] } + 'returns': ['GuestDeviceInfo'], + 'if': 'CONFIG_WIN32' } ## # @GuestAuthorizedKeys: @@ -1434,17 +1605,16 @@ { 'struct': 'GuestAuthorizedKeys', 'data': { 'keys': ['str'] - }, - 'if': 'CONFIG_POSIX' } - + } +} ## # @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 # @@ -1452,44 +1622,45 @@ ## { 'command': 'guest-ssh-get-authorized-keys', 'data': { 'username': 'str' }, - 'returns': 'GuestAuthorizedKeys', - 'if': 'CONFIG_POSIX' } + 'returns': 'GuestAuthorizedKeys' +} ## # @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 ## { 'command': 'guest-ssh-add-authorized-keys', - 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }, - 'if': 'CONFIG_POSIX' } + 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } +} ## # @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 ## { 'command': 'guest-ssh-remove-authorized-keys', - 'data': { 'username': 'str', 'keys': ['str'] }, - 'if': 'CONFIG_POSIX' } + 'data': { 'username': 'str', 'keys': ['str'] } +} ## # @GuestDiskStats: @@ -1526,7 +1697,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 ## @@ -1548,44 +1720,53 @@ '*ios-pgr': 'uint64', '*total-ticks': 'uint64', '*weight-ticks': 'uint64' - } } + }, + 'if': 'CONFIG_LINUX' } ## # @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', 'major': 'uint64', 'minor': 'uint64', - 'stats': 'GuestDiskStats' } } + 'stats': 'GuestDiskStats' }, + 'if': 'CONFIG_LINUX' } ## # @guest-get-diskstats: # # Retrieve information about disk stats. +# # Returns: List of disk stats of guest. # # Since: 7.1 ## { 'command': 'guest-get-diskstats', - 'returns': ['GuestDiskStatsInfo'] + 'returns': ['GuestDiskStatsInfo'], + 'if': 'CONFIG_LINUX' } ## # @GuestCpuStatsType: # -# An enumeration of OS type +# Guest operating systems supporting CPU statistics +# +# @linux: Linux # # Since: 7.1 ## { 'enum': 'GuestCpuStatsType', - 'data': [ 'linux' ] } + 'data': [ 'linux' ], + 'if': 'CONFIG_LINUX' } ## @@ -1611,8 +1792,8 @@ # # @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) +# @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) # @@ -1630,30 +1811,107 @@ '*steal': 'uint64', '*guest': 'uint64', '*guestnice': 'uint64' - } } + }, + 'if': 'CONFIG_LINUX' } ## # @GuestCpuStats: # # Get statistics of each CPU in millisecond. # -# - @linux: Linux style CPU statistics +# @type: guest operating system # # Since: 7.1 ## { 'union': 'GuestCpuStats', 'base': { 'type': 'GuestCpuStatsType' }, 'discriminator': 'type', - 'data': { 'linux': 'GuestLinuxCpuStats' } } + 'data': { 'linux': 'GuestLinuxCpuStats' }, + 'if': 'CONFIG_LINUX' } ## # @guest-get-cpustats: # # Retrieve information about CPU stats. +# # Returns: List of CPU stats of guest. # # Since: 7.1 ## { 'command': 'guest-get-cpustats', - 'returns': ['GuestCpuStats'] + 'returns': ['GuestCpuStats'], + 'if': 'CONFIG_LINUX' +} + +## +# @GuestNetworkRoute: +# +# Route information, currently, only linux supported. +# +# @iface: The destination network or host's egress network interface in the routing table +# +# @destination: The IP address of the target network or host, The final destination of the packet +# +# @metric: Route metric +# +# @gateway: The IP address of the next hop router +# +# @mask: Subnet Mask (IPv4 only) +# +# @irtt: Initial round-trip delay (not for windows, IPv4 only) +# +# @flags: Route flags (not for windows) +# +# @refcnt: The route's reference count (not for windows) +# +# @use: Route usage count (not for windows) +# +# @window: TCP window size, used for flow control (not for windows, IPv4 only) +# +# @mtu: Data link layer maximum packet size (not for windows) +# +# @desprefixlen: Destination prefix length (for IPv6) +# +# @source: Source IP address (for IPv6) +# +# @srcprefixlen: Source prefix length (for IPv6) +# +# @nexthop: Next hop IP address (for IPv6) +# +# @version: IP version (4 or 6) +# +# Since: 9.1 + +## +{ 'struct': 'GuestNetworkRoute', + 'data': {'iface': 'str', + 'destination': 'str', + 'metric': 'int', + '*gateway': 'str', + '*mask': 'str', + '*irtt': 'int', + '*flags': 'uint64', + '*refcnt': 'int', + '*use': 'int', + '*window': 'int', + '*mtu': 'int', + '*desprefixlen': 'str', + '*source': 'str', + '*srcprefixlen': 'str', + '*nexthop': 'str', + 'version': 'int' + }, + 'if': 'CONFIG_LINUX' } + +## +# @guest-network-get-route: +# +# Retrieve information about route of network. +# Returns: List of route info of guest. +# +# Since: 9.1 +## +{ 'command': 'guest-network-get-route', + 'returns': ['GuestNetworkRoute'], + 'if': 'CONFIG_LINUX' } diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index b8087e5baa..5cea5bcf74 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 @@ -38,7 +39,7 @@ const GUID CLSID_WbemLocator = { 0x4590f811, 0x1d3a, 0x11d0, const GUID IID_IWbemLocator = { 0xdc12a687, 0x737f, 0x11cf, {0x88, 0x4d, 0x00, 0xaa, 0x00, 0x4b, 0x2e, 0x24} }; -void errmsg(DWORD err, const char *text) +static void errmsg(DWORD err, const char *text) { /* * `text' contains function call statement when errmsg is called via chk(). @@ -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,55 @@ 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); 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); STDAPI COMRegister(void) { + qga_debug_begin; + HRESULT hr; COMInitializer initializer; COMPointer pUnknown; @@ -258,12 +281,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 +329,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 +345,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 +378,26 @@ out: COMUnregister(); } + qga_debug_end; return hr; } +STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int); +STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int) +{ + COMRegister(); +} + +STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int); +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 +418,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 +430,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 +443,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 +510,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 +536,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 +553,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,6 +563,8 @@ namespace _com_util /* Stop QGA VSS provider service using Winsvc API */ STDAPI StopService(void) { + qga_debug_begin; + HRESULT hr = S_OK; SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); SC_HANDLE service = NULL; @@ -542,5 +589,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..a102a23fbf 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 @@ -44,7 +45,7 @@ const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3, {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} }; -void LockModule(BOOL lock) +static void LockModule(BOOL lock) { if (lock) { InterlockedIncrement(&g_nComObjsInUse); @@ -526,12 +527,17 @@ STDAPI DllCanUnloadNow() return g_nComObjsInUse == 0 ? S_OK : S_FALSE; } +EXTERN_C +BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved); + 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..4401d55e3a 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; +} + +static 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; +} + +static bool is_valid_vss_backup_type(VSS_BACKUP_TYPE vssBT) +{ + return (vssBT > VSS_BT_UNDEFINED && vssBT < VSS_BT_OTHER); +} + +static 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/qobject/qlit.c b/qobject/qlit.c index be8332136c..a62865b642 100644 --- a/qobject/qlit.c +++ b/qobject/qlit.c @@ -118,7 +118,7 @@ QObject *qobject_from_qlit(const QLitObject *qlit) case QTYPE_QBOOL: return QOBJECT(qbool_from_bool(qlit->value.qbool)); default: - assert(0); + g_assert_not_reached(); } return NULL; diff --git a/qobject/qnum.c b/qobject/qnum.c index 2bbeaedc7b..dd8ea49565 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -85,8 +85,7 @@ bool qnum_get_try_int(const QNum *qn, int64_t *val) return false; } - assert(0); - return false; + g_assert_not_reached(); } /** @@ -123,8 +122,7 @@ bool qnum_get_try_uint(const QNum *qn, uint64_t *val) return false; } - assert(0); - return false; + g_assert_not_reached(); } /** @@ -156,8 +154,7 @@ double qnum_get_double(QNum *qn) return qn->u.dbl; } - assert(0); - return 0.0; + g_assert_not_reached(); } char *qnum_to_string(QNum *qn) @@ -172,8 +169,7 @@ char *qnum_to_string(QNum *qn) return g_strdup_printf("%.17g", qn->u.dbl); } - assert(0); - return NULL; + g_assert_not_reached(); } /** 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 e25f1e96db..9edc06d391 100644 --- a/qom/object.c +++ b/qom/object.c @@ -23,7 +23,6 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/forward-visitor.h" #include "qapi/qapi-builtin-visit.h" -#include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" #include "trace.h" @@ -31,6 +30,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 +137,38 @@ 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-_."); + + 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); @@ -166,7 +195,7 @@ void type_register_static_array(const TypeInfo *infos, int nr_infos) } } -static TypeImpl *type_get_by_name(const char *name) +static TypeImpl *type_get_by_name_noload(const char *name) { if (name == NULL) { return NULL; @@ -175,10 +204,32 @@ static TypeImpl *type_get_by_name(const char *name) return type_table_lookup(name); } +static TypeImpl *type_get_or_load_by_name(const char *name, Error **errp) +{ + TypeImpl *type = type_get_by_name_noload(name); + +#ifdef CONFIG_MODULES + if (!type) { + int rv = module_load_qom(name, errp); + if (rv > 0) { + type = type_get_by_name_noload(name); + } else { + error_prepend(errp, "could not load a module for type '%s'", name); + return NULL; + } + } +#endif + if (!type) { + error_setg(errp, "unknown type '%s'", name); + } + + return type; +} + static TypeImpl *type_get_parent(TypeImpl *type) { if (!type->parent_type && type->parent) { - type->parent_type = type_get_by_name(type->parent); + type->parent_type = type_get_by_name_noload(type->parent); if (!type->parent_type) { fprintf(stderr, "Type '%s' is missing its parent '%s'\n", type->name, type->parent); @@ -220,12 +271,17 @@ static size_t type_object_get_size(TypeImpl *ti) return 0; } -size_t object_type_get_instance_size(const char *typename) +static size_t type_object_get_align(TypeImpl *ti) { - TypeImpl *type = type_get_by_name(typename); + if (ti->instance_align) { + return ti->instance_align; + } - g_assert(type != NULL); - return type_object_get_size(type); + if (type_has_parent(ti)) { + return type_object_get_align(type_get_parent(ti)); + } + + return 0; } static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) @@ -293,6 +349,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. */ @@ -328,7 +385,7 @@ static void type_initialize(TypeImpl *ti) } for (i = 0; i < ti->num_interfaces; i++) { - TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); + TypeImpl *t = type_get_by_name_noload(ti->interfaces[i].typename); if (!t) { error_report("missing interface '%s' for object '%s'", ti->interfaces[i].typename, parent->name); @@ -522,23 +579,7 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type void object_initialize(void *data, size_t size, const char *typename) { - TypeImpl *type = type_get_by_name(typename); - -#ifdef CONFIG_MODULES - if (!type) { - 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) { - error_report("missing object type '%s'", typename); - abort(); - } + TypeImpl *type = type_get_or_load_by_name(typename, &error_fatal); object_initialize_with_type(data, size, type); } @@ -749,7 +790,7 @@ Object *object_new_with_class(ObjectClass *klass) Object *object_new(const char *typename) { - TypeImpl *ti = type_get_by_name(typename); + TypeImpl *ti = type_get_or_load_by_name(typename, &error_fatal); return object_new_with_type(ti); } @@ -922,7 +963,7 @@ ObjectClass *object_class_dynamic_cast(ObjectClass *class, return class; } - target_type = type_get_by_name(typename); + target_type = type_get_by_name_noload(typename); if (!target_type) { /* target class type unknown, so fail the cast */ return NULL; @@ -1020,7 +1061,7 @@ const char *object_class_get_name(ObjectClass *klass) ObjectClass *object_class_by_name(const char *typename) { - TypeImpl *type = type_get_by_name(typename); + TypeImpl *type = type_get_by_name_noload(typename); if (!type) { return NULL; @@ -1033,21 +1074,15 @@ ObjectClass *object_class_by_name(const char *typename) ObjectClass *module_object_class_by_name(const char *typename) { - ObjectClass *oc; + TypeImpl *type = type_get_or_load_by_name(typename, NULL); - oc = object_class_by_name(typename); -#ifdef CONFIG_MODULES - if (!oc) { - 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); - } + if (!type) { + return NULL; } -#endif - return oc; + + type_initialize(type); + + return type->class; } ObjectClass *object_class_get_parent(ObjectClass *class) @@ -1443,7 +1478,8 @@ char *object_property_get_str(Object *obj, const char *name, } qstring = qobject_to(QString, ret); if (!qstring) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string"); + error_setg(errp, "Invalid parameter type for '%s', expected: string", + name); retval = NULL; } else { retval = g_strdup(qstring_get_str(qstring)); @@ -1504,7 +1540,8 @@ bool object_property_get_bool(Object *obj, const char *name, } qbool = qobject_to(QBool, ret); if (!qbool) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "boolean"); + error_setg(errp, "Invalid parameter type for '%s', expected: boolean", + name); retval = false; } else { retval = qbool_get_bool(qbool); @@ -1537,7 +1574,8 @@ int64_t object_property_get_int(Object *obj, const char *name, qnum = qobject_to(QNum, ret); if (!qnum || !qnum_get_try_int(qnum, &retval)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "int"); + error_setg(errp, "Invalid parameter type for '%s', expected: int", + name); retval = -1; } @@ -1574,6 +1612,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))); @@ -1606,7 +1649,8 @@ uint64_t object_property_get_uint(Object *obj, const char *name, } qnum = qobject_to(QNum, ret); if (!qnum || !qnum_get_try_uint(qnum, &retval)) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "uint"); + error_setg(errp, "Invalid parameter type for '%s', expected: uint", + name); retval = 0; } @@ -1851,7 +1895,8 @@ static Object *object_resolve_link(Object *obj, const char *name, } else if (!target) { target = object_resolve_path(path, &ambiguous); if (target || ambiguous) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, target_type); + error_setg(errp, "Invalid parameter type for '%s', expected: %s", + name, target_type); } else { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found", path); @@ -2026,7 +2071,6 @@ const char *object_get_canonical_path_component(const Object *obj) /* obj had a parent but was not a child, should never happen */ g_assert_not_reached(); - return NULL; } char *object_get_canonical_path(const Object *obj) @@ -2132,7 +2176,7 @@ static Object *object_resolve_partial_path(Object *parent, } Object *object_resolve_path_type(const char *path, const char *typename, - bool *ambiguousp) + bool *ambiguous) { Object *obj; char **parts; @@ -2141,14 +2185,17 @@ Object *object_resolve_path_type(const char *path, const char *typename, assert(parts); if (parts[0] == NULL || strcmp(parts[0], "") != 0) { - bool ambiguous = false; + bool ambig = false; obj = object_resolve_partial_path(object_get_root(), parts, - typename, &ambiguous); - if (ambiguousp) { - *ambiguousp = ambiguous; + typename, &ambig); + if (ambiguous) { + *ambiguous = ambig; } } else { obj = object_resolve_abs_path(object_get_root(), parts + 1, typename); + if (ambiguous) { + *ambiguous = false; + } } g_strfreev(parts); @@ -2172,6 +2219,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 = false; + 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..1a6f29c053 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" @@ -90,7 +90,7 @@ Object *user_creatable_add_type(const char *type, const char *id, return NULL; } - klass = object_class_by_name(type); + klass = module_object_class_by_name(type); if (!klass) { error_setg(errp, "invalid object type: %s", type); return NULL; @@ -108,7 +108,7 @@ Object *user_creatable_add_type(const char *type, const char *id, } assert(qdict); - obj = object_new(type); + obj = object_new_with_class(klass); object_set_properties_from_qdict(obj, qdict, v, &local_err); if (local_err) { goto out; @@ -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..46e4562300 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) @@ -143,7 +141,7 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, return NULL; } - obj = object_new(typename); + obj = object_new_with_class(klass); object_property_iter_init(&iter, obj); while ((prop = object_property_iter_next(&iter))) { @@ -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); } @@ -190,7 +186,7 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, ObjectPropertyIterator iter; ObjectPropertyInfoList *prop_list = NULL; - klass = object_class_by_name(typename); + klass = module_object_class_by_name(typename); if (klass == NULL) { error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Class '%s' not found", typename); @@ -215,8 +211,8 @@ 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); + info->default_value = qobject_ref(prop->defval); 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 a31aded032..72b1f832dd 100644 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -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-events.c b/replay/replay-events.c index af0721cc1a..2e46eda6bf 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -92,15 +92,6 @@ void replay_flush_events(void) } } -void replay_disable_events(void) -{ - if (replay_mode != REPLAY_MODE_NONE) { - events_enabled = false; - /* Flush events queue before waiting of completion */ - replay_flush_events(); - } -} - /*! Adds specified async event to the queue */ void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque, 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 b6836354ac..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; @@ -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 9a0dc1cf44..895fa6b67a 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 */ @@ -275,6 +385,8 @@ static void replay_enable(const char *fname, int mode) replay_fetch_data_kind(); } + runstate_replay_enable(); + replay_init_events(); } @@ -376,8 +488,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 6b6c16b4b4..3a259df244 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 6b6c16b4b40763507cf1f518096f3c3883c5cf2d +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..4dfdca63a9 160000 --- a/roms/edk2 +++ b/roms/edk2 @@ -1 +1 @@ -Subproject commit b24306f15daa2ff8510b06702114724b33895d3c +Subproject commit 4dfdca63a93497203f197ec98ba20e2327e4afe4 diff --git a/roms/edk2-build.config b/roms/edk2-build.config new file mode 100644 index 0000000000..9e45361fb4 --- /dev/null +++ b/roms/edk2-build.config @@ -0,0 +1,146 @@ +[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 + +#################################################################################### +# LoongArch64 + +[build.loongarch64.qemu] +conf = OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc +arch = LOONGARCH64 +plat = LoongArchVirtQemu +dest = ../pc-bios +cpy1 = FV/QEMU_EFI.fd edk2-loongarch64-code.fd +pad1 = edk2-loongarch64-code.fd 16m +cpy2 = FV/QEMU_VARS.fd edk2-loongarch64-vars.fd +pad2 = edk2-loongarch64-vars.fd 16m 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..069f19f8bf --- /dev/null +++ b/roms/edk2-version @@ -0,0 +1,2 @@ +EDK2_STABLE = edk2-stable202408 +EDK2_DATE = 08/13/2024 diff --git a/roms/openbios b/roms/openbios index 0e0afae657..c3a19c1e54 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit 0e0afae6579c1efe9f0d85505b75ffe989554133 +Subproject commit c3a19c1e54977a53027d6232050e1e3e39a98a1b diff --git a/roms/opensbi b/roms/opensbi index 4489876e93..43cace6c36 160000 --- a/roms/opensbi +++ b/roms/opensbi @@ -1 +1 @@ -Subproject commit 4489876e933d8ba0d8bc6c64bae71e295d45faac +Subproject commit 43cace6c3671e5172d0df0a8963e552bb04b7b20 diff --git a/roms/seabios b/roms/seabios index 3208b098f5..a6ed6b701f 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit 3208b098f51a9ef96d0dfa71d5ec3a3eaec88f0a +Subproject commit a6ed6b701f0a57db0569ab98b0661c12a6ec3ff8 diff --git a/roms/seabios-hppa b/roms/seabios-hppa index 673d2595d4..a528f01d7a 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit 673d2595d4f773cc266cbf8dbaf2f475a6adb949 +Subproject commit a528f01d7abd511d3cc71b7acaab6e036ee524bd 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/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000000..1bf71b1f68 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,3 @@ +# Ignore any cargo development build artifacts; for qemu-wide builds, all build +# artifacts will go to the meson build directory. +target diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000000..c0c6069247 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,138 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arbitrary-int" +version = "1.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" + +[[package]] +name = "bilge" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" +dependencies = [ + "arbitrary-int", + "bilge-impl", +] + +[[package]] +name = "bilge-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" +dependencies = [ + "itertools", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "pl011" +version = "0.1.0" +dependencies = [ + "bilge", + "bilge-impl", + "qemu_api", + "qemu_api_macros", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qemu_api" +version = "0.1.0" +dependencies = [ + "qemu_api_macros", + "version_check", +] + +[[package]] +name = "qemu_api_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000000..0c94d5037d --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +resolver = "2" +members = [ + "qemu-api-macros", + "qemu-api", + "hw/char/pl011", +] diff --git a/rust/Kconfig b/rust/Kconfig new file mode 100644 index 0000000000..f9f5c39098 --- /dev/null +++ b/rust/Kconfig @@ -0,0 +1 @@ +source hw/Kconfig diff --git a/rust/hw/Kconfig b/rust/hw/Kconfig new file mode 100644 index 0000000000..4d934f30af --- /dev/null +++ b/rust/hw/Kconfig @@ -0,0 +1,2 @@ +# devices Kconfig +source char/Kconfig diff --git a/rust/hw/char/Kconfig b/rust/hw/char/Kconfig new file mode 100644 index 0000000000..5fe800c480 --- /dev/null +++ b/rust/hw/char/Kconfig @@ -0,0 +1,2 @@ +config X_PL011_RUST + bool diff --git a/rust/hw/char/meson.build b/rust/hw/char/meson.build new file mode 100644 index 0000000000..5716dc43ef --- /dev/null +++ b/rust/hw/char/meson.build @@ -0,0 +1 @@ +subdir('pl011') diff --git a/rust/hw/char/pl011/.gitignore b/rust/hw/char/pl011/.gitignore new file mode 100644 index 0000000000..71eaff2035 --- /dev/null +++ b/rust/hw/char/pl011/.gitignore @@ -0,0 +1,2 @@ +# Ignore generated bindings file overrides. +src/bindings.rs.inc diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml new file mode 100644 index 0000000000..a373906b9f --- /dev/null +++ b/rust/hw/char/pl011/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "pl011" +version = "0.1.0" +edition = "2021" +authors = ["Manos Pitsidianakis "] +license = "GPL-2.0-or-later" +readme = "README.md" +homepage = "https://www.qemu.org" +description = "pl011 device model for QEMU" +repository = "https://gitlab.com/epilys/rust-for-qemu" +resolver = "2" +publish = false +keywords = [] +categories = [] + +[lib] +crate-type = ["staticlib"] + +[dependencies] +bilge = { version = "0.2.0" } +bilge-impl = { version = "0.2.0" } +qemu_api = { path = "../../../qemu-api" } +qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/README.md b/rust/hw/char/pl011/README.md new file mode 100644 index 0000000000..cd7dea3163 --- /dev/null +++ b/rust/hw/char/pl011/README.md @@ -0,0 +1,31 @@ +# PL011 QEMU Device Model + +This library implements a device model for the PrimeCell® UART (PL011) +device in QEMU. + +## Build static lib + +Host build target must be explicitly specified: + +```sh +cargo build --target x86_64-unknown-linux-gnu +``` + +Replace host target triplet if necessary. + +## Generate Rust documentation + +To generate docs for this crate, including private items: + +```sh +cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu +``` + +To include direct dependencies like `bilge` (bitmaps for register types): + +```sh +cargo tree --depth 1 -e normal --prefix none \ + | cut -d' ' -f1 \ + | xargs printf -- '-p %s\n' \ + | xargs cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu +``` diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build new file mode 100644 index 0000000000..547cca5a96 --- /dev/null +++ b/rust/hw/char/pl011/meson.build @@ -0,0 +1,26 @@ +subproject('bilge-0.2-rs', required: true) +subproject('bilge-impl-0.2-rs', required: true) + +bilge_dep = dependency('bilge-0.2-rs') +bilge_impl_dep = dependency('bilge-impl-0.2-rs') + +_libpl011_rs = static_library( + 'pl011', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [ + bilge_dep, + bilge_impl_dep, + qemu_api, + qemu_api_macros, + ], +) + +rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( + link_whole: [_libpl011_rs], + # Putting proc macro crates in `dependencies` is necessary for Meson to find + # them when compiling the root per-target static rust lib. + dependencies: [bilge_impl_dep, qemu_api_macros], + variables: {'crate': 'pl011'}, +)]) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs new file mode 100644 index 0000000000..476cacc844 --- /dev/null +++ b/rust/hw/char/pl011/src/device.rs @@ -0,0 +1,669 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::ptr::{addr_of, addr_of_mut, NonNull}; +use std::{ + ffi::CStr, + os::raw::{c_int, c_uchar, c_uint, c_void}, +}; + +use qemu_api::{ + bindings::{self, *}, + c_str, + definitions::ObjectImpl, + device_class::TYPE_SYS_BUS_DEVICE, +}; + +use crate::{ + memory_ops::PL011_OPS, + registers::{self, Interrupt}, + RegisterOffset, +}; + +/// Integer Baud Rate Divider, `UARTIBRD` +const IBRD_MASK: u32 = 0xffff; + +/// Fractional Baud Rate Divider, `UARTFBRD` +const FBRD_MASK: u32 = 0x3f; + +const DATA_BREAK: u32 = 1 << 10; + +/// QEMU sourced constant. +pub const PL011_FIFO_DEPTH: usize = 16_usize; + +#[derive(Clone, Copy, Debug)] +enum DeviceId { + #[allow(dead_code)] + Arm = 0, + Luminary, +} + +impl std::ops::Index for DeviceId { + type Output = c_uchar; + + fn index(&self, idx: hwaddr) -> &Self::Output { + match self { + Self::Arm => &Self::PL011_ID_ARM[idx as usize], + Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize], + } + } +} + +impl DeviceId { + const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; + const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]; +} + +#[repr(C)] +#[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)] +/// PL011 Device Model in QEMU +pub struct PL011State { + pub parent_obj: SysBusDevice, + pub iomem: MemoryRegion, + #[doc(alias = "fr")] + pub flags: registers::Flags, + #[doc(alias = "lcr")] + pub line_control: registers::LineControl, + #[doc(alias = "rsr")] + pub receive_status_error_clear: registers::ReceiveStatusErrorClear, + #[doc(alias = "cr")] + pub control: registers::Control, + pub dmacr: u32, + pub int_enabled: u32, + pub int_level: u32, + pub read_fifo: [u32; PL011_FIFO_DEPTH], + pub ilpr: u32, + pub ibrd: u32, + pub fbrd: u32, + pub ifl: u32, + pub read_pos: usize, + pub read_count: usize, + pub read_trigger: usize, + #[doc(alias = "chr")] + pub char_backend: CharBackend, + /// QEMU interrupts + /// + /// ```text + /// * sysbus MMIO region 0: device registers + /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line) + /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line) + /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line) + /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line) + /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line) + /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line) + /// ``` + #[doc(alias = "irq")] + pub interrupts: [qemu_irq; 6usize], + #[doc(alias = "clk")] + pub clock: NonNull, + #[doc(alias = "migrate_clk")] + pub migrate_clock: bool, + /// The byte string that identifies the device. + device_id: DeviceId, +} + +impl ObjectImpl for PL011State { + type Class = PL011Class; + const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; + const TYPE_NAME: &'static CStr = crate::TYPE_PL011; + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE); + const ABSTRACT: bool = false; + const INSTANCE_INIT: Option = Some(pl011_init); + const INSTANCE_POST_INIT: Option = None; + const INSTANCE_FINALIZE: Option = None; +} + +#[repr(C)] +pub struct PL011Class { + _inner: [u8; 0], +} + +impl qemu_api::definitions::Class for PL011Class { + const CLASS_INIT: Option = + Some(crate::device_class::pl011_class_init); + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + > = None; +} + +impl PL011State { + /// Initializes a pre-allocated, unitialized instance of `PL011State`. + /// + /// # Safety + /// + /// `self` must point to a correctly sized and aligned location for the + /// `PL011State` type. It must not be called more than once on the same + /// location/instance. All its fields are expected to hold unitialized + /// values with the sole exception of `parent_obj`. + unsafe fn init(&mut self) { + const CLK_NAME: &CStr = c_str!("clk"); + + let dev = addr_of_mut!(*self).cast::(); + // SAFETY: + // + // self and self.iomem are guaranteed to be valid at this point since callers + // must make sure the `self` reference is valid. + unsafe { + memory_region_init_io( + addr_of_mut!(self.iomem), + addr_of_mut!(*self).cast::(), + &PL011_OPS, + addr_of_mut!(*self).cast::(), + Self::TYPE_INFO.name, + 0x1000, + ); + let sbd = addr_of_mut!(*self).cast::(); + sysbus_init_mmio(sbd, addr_of_mut!(self.iomem)); + for irq in self.interrupts.iter_mut() { + sysbus_init_irq(sbd, irq); + } + } + // SAFETY: + // + // self.clock is not initialized at this point; but since `NonNull<_>` is Copy, + // we can overwrite the undefined value without side effects. This is + // safe since all PL011State instances are created by QOM code which + // calls this function to initialize the fields; therefore no code is + // able to access an invalid self.clock value. + unsafe { + self.clock = NonNull::new(qdev_init_clock_in( + dev, + CLK_NAME.as_ptr(), + None, /* pl011_clock_update */ + addr_of_mut!(*self).cast::(), + ClockEvent::ClockUpdate.0, + )) + .unwrap(); + } + } + + pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow { + use RegisterOffset::*; + + std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) { + Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => { + u64::from(self.device_id[(offset - 0xfe0) >> 2]) + } + Err(_) => { + // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + 0 + } + Ok(DR) => { + self.flags.set_receive_fifo_full(false); + let c = self.read_fifo[self.read_pos]; + if self.read_count > 0 { + self.read_count -= 1; + self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); + } + if self.read_count == 0 { + self.flags.set_receive_fifo_empty(true); + } + if self.read_count + 1 == self.read_trigger { + self.int_level &= !registers::INT_RX; + } + // Update error bits. + self.receive_status_error_clear = c.to_be_bytes()[3].into(); + self.update(); + // Must call qemu_chr_fe_accept_input, so return Continue: + return std::ops::ControlFlow::Continue(c.into()); + } + Ok(RSR) => u8::from(self.receive_status_error_clear).into(), + Ok(FR) => u16::from(self.flags).into(), + Ok(FBRD) => self.fbrd.into(), + Ok(ILPR) => self.ilpr.into(), + Ok(IBRD) => self.ibrd.into(), + Ok(LCR_H) => u16::from(self.line_control).into(), + Ok(CR) => { + // We exercise our self-control. + u16::from(self.control).into() + } + Ok(FLS) => self.ifl.into(), + Ok(IMSC) => self.int_enabled.into(), + Ok(RIS) => self.int_level.into(), + Ok(MIS) => u64::from(self.int_level & self.int_enabled), + Ok(ICR) => { + // "The UARTICR Register is the interrupt clear register and is write-only" + // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR + 0 + } + Ok(DMACR) => self.dmacr.into(), + }) + } + + pub fn write(&mut self, offset: hwaddr, value: u64) { + // eprintln!("write offset {offset} value {value}"); + use RegisterOffset::*; + let value: u32 = value as u32; + match RegisterOffset::try_from(offset) { + Err(_bad_offset) => { + eprintln!("write bad offset {offset} value {value}"); + } + Ok(DR) => { + // ??? Check if transmitter is enabled. + let ch: u8 = value as u8; + // XXX this blocks entire thread. Rewrite to use + // qemu_chr_fe_write and background I/O callbacks + + // SAFETY: self.char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + unsafe { + qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1); + } + self.loopback_tx(value); + self.int_level |= registers::INT_TX; + self.update(); + } + Ok(RSR) => { + self.receive_status_error_clear = 0.into(); + } + Ok(FR) => { + // flag writes are ignored + } + Ok(ILPR) => { + self.ilpr = value; + } + Ok(IBRD) => { + self.ibrd = value; + } + Ok(FBRD) => { + self.fbrd = value; + } + Ok(LCR_H) => { + let value = value as u16; + let new_val: registers::LineControl = value.into(); + // Reset the FIFO state on FIFO enable or disable + if bool::from(self.line_control.fifos_enabled()) + ^ bool::from(new_val.fifos_enabled()) + { + self.reset_fifo(); + } + if self.line_control.send_break() ^ new_val.send_break() { + let mut break_enable: c_int = new_val.send_break().into(); + // SAFETY: self.char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + unsafe { + qemu_chr_fe_ioctl( + addr_of_mut!(self.char_backend), + CHR_IOCTL_SERIAL_SET_BREAK as i32, + addr_of_mut!(break_enable).cast::(), + ); + } + self.loopback_break(break_enable > 0); + } + self.line_control = new_val; + self.set_read_trigger(); + } + Ok(CR) => { + // ??? Need to implement the enable bit. + let value = value as u16; + self.control = value.into(); + self.loopback_mdmctrl(); + } + Ok(FLS) => { + self.ifl = value; + self.set_read_trigger(); + } + Ok(IMSC) => { + self.int_enabled = value; + self.update(); + } + Ok(RIS) => {} + Ok(MIS) => {} + Ok(ICR) => { + self.int_level &= !value; + self.update(); + } + Ok(DMACR) => { + self.dmacr = value; + if value & 3 > 0 { + // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); + eprintln!("pl011: DMA not implemented"); + } + } + } + } + + #[inline] + fn loopback_tx(&mut self, value: u32) { + if !self.loopback_enabled() { + 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. + self.put_fifo(value); + } + + fn loopback_mdmctrl(&mut self) { + if !self.loopback_enabled() { + 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. + */ + + self.flags.set_ring_indicator(self.control.out_2()); + self.flags.set_data_carrier_detect(self.control.out_1()); + self.flags.set_clear_to_send(self.control.request_to_send()); + self.flags + .set_data_set_ready(self.control.data_transmit_ready()); + + // Change interrupts based on updated FR + let mut il = self.int_level; + + il &= !Interrupt::MS; + + if self.flags.data_set_ready() { + il |= Interrupt::DSR as u32; + } + if self.flags.data_carrier_detect() { + il |= Interrupt::DCD as u32; + } + if self.flags.clear_to_send() { + il |= Interrupt::CTS as u32; + } + if self.flags.ring_indicator() { + il |= Interrupt::RI as u32; + } + self.int_level = il; + self.update(); + } + + fn loopback_break(&mut self, enable: bool) { + if enable { + self.loopback_tx(DATA_BREAK); + } + } + + fn set_read_trigger(&mut self) { + self.read_trigger = 1; + } + + pub fn realize(&mut self) { + // SAFETY: self.char_backend has the correct size and alignment for a + // CharBackend object, and its callbacks are of the correct types. + unsafe { + qemu_chr_fe_set_handlers( + addr_of_mut!(self.char_backend), + Some(pl011_can_receive), + Some(pl011_receive), + Some(pl011_event), + None, + addr_of_mut!(*self).cast::(), + core::ptr::null_mut(), + true, + ); + } + } + + pub fn reset(&mut self) { + self.line_control.reset(); + self.receive_status_error_clear.reset(); + self.dmacr = 0; + self.int_enabled = 0; + self.int_level = 0; + self.ilpr = 0; + self.ibrd = 0; + self.fbrd = 0; + self.read_trigger = 1; + self.ifl = 0x12; + self.control.reset(); + self.flags = 0.into(); + self.reset_fifo(); + } + + pub fn reset_fifo(&mut self) { + self.read_count = 0; + self.read_pos = 0; + + /* Reset FIFO flags */ + self.flags.reset(); + } + + pub fn can_receive(&self) -> bool { + // trace_pl011_can_receive(s->lcr, s->read_count, r); + self.read_count < self.fifo_depth() + } + + pub fn event(&mut self, event: QEMUChrEvent) { + if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.fifo_enabled() { + self.put_fifo(DATA_BREAK); + self.receive_status_error_clear.set_break_error(true); + } + } + + #[inline] + pub fn fifo_enabled(&self) -> bool { + matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO) + } + + #[inline] + pub fn loopback_enabled(&self) -> bool { + self.control.enable_loopback() + } + + #[inline] + pub fn fifo_depth(&self) -> usize { + // Note: FIFO depth is expected to be power-of-2 + if self.fifo_enabled() { + return PL011_FIFO_DEPTH; + } + 1 + } + + pub fn put_fifo(&mut self, value: c_uint) { + let depth = self.fifo_depth(); + assert!(depth > 0); + let slot = (self.read_pos + self.read_count) & (depth - 1); + self.read_fifo[slot] = value; + self.read_count += 1; + self.flags.set_receive_fifo_empty(false); + if self.read_count == depth { + self.flags.set_receive_fifo_full(true); + } + + if self.read_count == self.read_trigger { + self.int_level |= registers::INT_RX; + self.update(); + } + } + + pub fn update(&self) { + let flags = self.int_level & self.int_enabled; + for (irq, i) in self.interrupts.iter().zip(IRQMASK) { + // SAFETY: self.interrupts have been initialized in init(). + unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) }; + } + } + + pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> { + /* Sanity-check input state */ + if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() { + return Err(()); + } + + if !self.fifo_enabled() && self.read_count > 0 && self.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. + self.read_fifo[0] = self.read_fifo[self.read_pos]; + self.read_pos = 0; + } + + self.ibrd &= IBRD_MASK; + self.fbrd &= FBRD_MASK; + + Ok(()) + } +} + +/// Which bits in the interrupt status matter for each outbound IRQ line ? +pub const IRQMASK: [u32; 6] = [ + /* combined IRQ */ + Interrupt::E + | Interrupt::MS + | Interrupt::RT as u32 + | Interrupt::TX as u32 + | Interrupt::RX as u32, + Interrupt::RX as u32, + Interrupt::TX as u32, + Interrupt::RT as u32, + Interrupt::MS, + Interrupt::E, +]; + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int { + unsafe { + debug_assert!(!opaque.is_null()); + let state = NonNull::new_unchecked(opaque.cast::()); + state.as_ref().can_receive().into() + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +/// +/// The buffer and size arguments must also be valid. +pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + if state.as_ref().loopback_enabled() { + return; + } + if size > 0 { + debug_assert!(!buf.is_null()); + state.as_mut().put_fifo(c_uint::from(buf.read_volatile())) + } + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + state.as_mut().event(event) + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer for `chr`. +#[no_mangle] +pub unsafe extern "C" fn pl011_create( + addr: u64, + irq: qemu_irq, + chr: *mut Chardev, +) -> *mut DeviceState { + unsafe { + let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name); + let sysbus: *mut SysBusDevice = dev.cast::(); + + qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr); + sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error); + sysbus_mmio_map(sysbus, 0, addr); + sysbus_connect_irq(sysbus, 0, irq); + dev + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +pub unsafe extern "C" fn pl011_init(obj: *mut Object) { + unsafe { + debug_assert!(!obj.is_null()); + let mut state = NonNull::new_unchecked(obj.cast::()); + state.as_mut().init(); + } +} + +#[repr(C)] +#[derive(Debug, qemu_api_macros::Object)] +/// PL011 Luminary device model. +pub struct PL011Luminary { + parent_obj: PL011State, +} + +#[repr(C)] +pub struct PL011LuminaryClass { + _inner: [u8; 0], +} + +/// Initializes a pre-allocated, unitialized instance of `PL011Luminary`. +/// +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011Luminary`]. We also expect the device is +/// readable/writeable from one thread at any time. +pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) { + unsafe { + debug_assert!(!obj.is_null()); + let mut state = NonNull::new_unchecked(obj.cast::()); + let state = state.as_mut(); + state.parent_obj.device_id = DeviceId::Luminary; + } +} + +impl qemu_api::definitions::Class for PL011LuminaryClass { + const CLASS_INIT: Option = + None; + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + > = None; +} + +impl ObjectImpl for PL011Luminary { + type Class = PL011LuminaryClass; + const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; + const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY; + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011); + const ABSTRACT: bool = false; + const INSTANCE_INIT: Option = Some(pl011_luminary_init); + const INSTANCE_POST_INIT: Option = None; + const INSTANCE_FINALIZE: Option = None; +} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs new file mode 100644 index 0000000000..a707fde138 --- /dev/null +++ b/rust/hw/char/pl011/src/device_class.rs @@ -0,0 +1,128 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::ptr::NonNull; +use std::os::raw::{c_int, c_void}; + +use qemu_api::{ + bindings::*, c_str, vmstate_clock, vmstate_fields, vmstate_int32, vmstate_subsections, + vmstate_uint32, vmstate_uint32_array, vmstate_unused, zeroable::Zeroable, +}; + +use crate::device::{PL011State, PL011_FIFO_DEPTH}; + +extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { + unsafe { + debug_assert!(!opaque.is_null()); + let state = NonNull::new_unchecked(opaque.cast::()); + state.as_ref().migrate_clock + } +} + +/// Migration subsection for [`PL011State`] clock. +pub static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { + name: c_str!("pl011/clock").as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(pl011_clock_needed), + fields: vmstate_fields! { + vmstate_clock!(clock, PL011State), + }, + ..Zeroable::ZERO +}; + +extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { + unsafe { + debug_assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + let result = state.as_mut().post_load(version_id as u32); + if result.is_err() { + -1 + } else { + 0 + } + } +} + +pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { + name: c_str!("pl011").as_ptr(), + version_id: 2, + minimum_version_id: 2, + post_load: Some(pl011_post_load), + fields: vmstate_fields! { + vmstate_unused!(core::mem::size_of::()), + vmstate_uint32!(flags, PL011State), + vmstate_uint32!(line_control, PL011State), + vmstate_uint32!(receive_status_error_clear, PL011State), + vmstate_uint32!(control, PL011State), + vmstate_uint32!(dmacr, PL011State), + vmstate_uint32!(int_enabled, PL011State), + vmstate_uint32!(int_level, PL011State), + vmstate_uint32_array!(read_fifo, PL011State, PL011_FIFO_DEPTH), + vmstate_uint32!(ilpr, PL011State), + vmstate_uint32!(ibrd, PL011State), + vmstate_uint32!(fbrd, PL011State), + vmstate_uint32!(ifl, PL011State), + vmstate_int32!(read_pos, PL011State), + vmstate_int32!(read_count, PL011State), + vmstate_int32!(read_trigger, PL011State), + }, + subsections: vmstate_subsections! { + VMSTATE_PL011_CLOCK + }, + ..Zeroable::ZERO +}; + +qemu_api::declare_properties! { + PL011_PROPERTIES, + qemu_api::define_property!( + c_str!("chardev"), + PL011State, + char_backend, + unsafe { &qdev_prop_chr }, + CharBackend + ), + qemu_api::define_property!( + c_str!("migrate-clk"), + PL011State, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} + +qemu_api::device_class_init! { + pl011_class_init, + props => PL011_PROPERTIES, + realize_fn => Some(pl011_realize), + legacy_reset_fn => Some(pl011_reset), + vmsd => VMSTATE_PL011, +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +pub unsafe extern "C" fn pl011_realize(dev: *mut DeviceState, _errp: *mut *mut Error) { + unsafe { + assert!(!dev.is_null()); + let mut state = NonNull::new_unchecked(dev.cast::()); + state.as_mut().realize(); + } +} + +/// # Safety +/// +/// We expect the FFI user of this function to pass a valid pointer, that has +/// the same size as [`PL011State`]. We also expect the device is +/// readable/writeable from one thread at any time. +pub unsafe extern "C" fn pl011_reset(dev: *mut DeviceState) { + unsafe { + assert!(!dev.is_null()); + let mut state = NonNull::new_unchecked(dev.cast::()); + state.as_mut().reset(); + } +} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs new file mode 100644 index 0000000000..cd0a49acb9 --- /dev/null +++ b/rust/hw/char/pl011/src/lib.rs @@ -0,0 +1,590 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later +// +// PL011 QEMU Device Model +// +// This library implements a device model for the PrimeCell® UART (PL011) +// device in QEMU. +// +#![doc = include_str!("../README.md")] +//! # Library crate +//! +//! See [`PL011State`](crate::device::PL011State) for the device model type and +//! the [`registers`] module for register types. + +#![deny( + rustdoc::broken_intra_doc_links, + rustdoc::redundant_explicit_links, + clippy::correctness, + clippy::suspicious, + clippy::complexity, + clippy::perf, + clippy::cargo, + clippy::nursery, + clippy::style, + // restriction group + clippy::dbg_macro, + clippy::as_underscore, + clippy::assertions_on_result_states, + // pedantic group + clippy::doc_markdown, + clippy::borrow_as_ptr, + clippy::cast_lossless, + clippy::option_if_let_else, + clippy::missing_const_for_fn, + clippy::cognitive_complexity, + clippy::missing_safety_doc, + )] +#![allow(clippy::result_unit_err)] + +extern crate bilge; +extern crate bilge_impl; +extern crate qemu_api; + +use qemu_api::c_str; + +pub mod device; +pub mod device_class; +pub mod memory_ops; + +pub const TYPE_PL011: &::std::ffi::CStr = c_str!("pl011"); +pub const TYPE_PL011_LUMINARY: &::std::ffi::CStr = c_str!("pl011_luminary"); + +/// Offset of each register from the base memory address of the device. +/// +/// # Source +/// ARM DDI 0183G, Table 3-1 p.3-3 +#[doc(alias = "offset")] +#[allow(non_camel_case_types)] +#[repr(u64)] +#[derive(Debug)] +pub enum RegisterOffset { + /// Data Register + /// + /// A write to this register initiates the actual data transmission + #[doc(alias = "UARTDR")] + DR = 0x000, + /// Receive Status Register or Error Clear Register + #[doc(alias = "UARTRSR")] + #[doc(alias = "UARTECR")] + RSR = 0x004, + /// Flag Register + /// + /// A read of this register shows if transmission is complete + #[doc(alias = "UARTFR")] + FR = 0x018, + /// Fractional Baud Rate Register + /// + /// responsible for baud rate speed + #[doc(alias = "UARTFBRD")] + FBRD = 0x028, + /// `IrDA` Low-Power Counter Register + #[doc(alias = "UARTILPR")] + ILPR = 0x020, + /// Integer Baud Rate Register + /// + /// Responsible for baud rate speed + #[doc(alias = "UARTIBRD")] + IBRD = 0x024, + /// line control register (data frame format) + #[doc(alias = "UARTLCR_H")] + LCR_H = 0x02C, + /// Toggle UART, transmission or reception + #[doc(alias = "UARTCR")] + CR = 0x030, + /// Interrupt FIFO Level Select Register + #[doc(alias = "UARTIFLS")] + FLS = 0x034, + /// Interrupt Mask Set/Clear Register + #[doc(alias = "UARTIMSC")] + IMSC = 0x038, + /// Raw Interrupt Status Register + #[doc(alias = "UARTRIS")] + RIS = 0x03C, + /// Masked Interrupt Status Register + #[doc(alias = "UARTMIS")] + MIS = 0x040, + /// Interrupt Clear Register + #[doc(alias = "UARTICR")] + ICR = 0x044, + /// DMA control Register + #[doc(alias = "UARTDMACR")] + DMACR = 0x048, + ///// Reserved, offsets `0x04C` to `0x07C`. + //Reserved = 0x04C, +} + +impl core::convert::TryFrom for RegisterOffset { + type Error = u64; + + fn try_from(value: u64) -> Result { + macro_rules! case { + ($($discriminant:ident),*$(,)*) => { + /* check that matching on all macro arguments compiles, which means we are not + * missing any enum value; if the type definition ever changes this will stop + * compiling. + */ + const fn _assert_exhaustive(val: RegisterOffset) { + match val { + $(RegisterOffset::$discriminant => (),)* + } + } + + match value { + $(x if x == Self::$discriminant as u64 => Ok(Self::$discriminant),)* + _ => Err(value), + } + } + } + case! { DR, RSR, FR, FBRD, ILPR, IBRD, LCR_H, CR, FLS, IMSC, RIS, MIS, ICR, DMACR } + } +} + +pub mod registers { + //! Device registers exposed as typed structs which are backed by arbitrary + //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. + //! + //! All PL011 registers are essentially 32-bit wide, but are typed here as + //! bitmaps with only the necessary width. That is, if a struct bitmap + //! in this module is for example 16 bits long, it should be conceived + //! as a 32-bit register where the unmentioned higher bits are always + //! unused thus treated as zero when read or written. + use bilge::prelude::*; + + // TODO: FIFO Mode has different semantics + /// Data Register, `UARTDR` + /// + /// The `UARTDR` register is the data register. + /// + /// For words to be transmitted: + /// + /// - if the FIFOs are enabled, data written to this location is pushed onto + /// the transmit + /// FIFO + /// - if the FIFOs are not enabled, data is stored in the transmitter + /// holding register (the + /// bottom word of the transmit FIFO). + /// + /// The write operation initiates transmission from the UART. The data is + /// prefixed with a start bit, appended with the appropriate parity bit + /// (if parity is enabled), and a stop bit. The resultant word is then + /// transmitted. + /// + /// For received words: + /// + /// - if the FIFOs are enabled, the data byte and the 4-bit status (break, + /// frame, parity, + /// and overrun) is pushed onto the 12-bit wide receive FIFO + /// - if the FIFOs are not enabled, the data byte and status are stored in + /// the receiving + /// holding register (the bottom word of the receive FIFO). + /// + /// The received data byte is read by performing reads from the `UARTDR` + /// register along with the corresponding status information. The status + /// information can also be read by a read of the `UARTRSR/UARTECR` + /// register. + /// + /// # Note + /// + /// You must disable the UART before any of the control registers are + /// reprogrammed. When the UART is disabled in the middle of + /// transmission or reception, it completes the current character before + /// stopping. + /// + /// # Source + /// ARM DDI 0183G 3.3.1 Data Register, UARTDR + #[bitsize(16)] + #[derive(Clone, Copy, DebugBits, FromBits)] + #[doc(alias = "UARTDR")] + pub struct Data { + _reserved: u4, + pub data: u8, + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + } + + // TODO: FIFO Mode has different semantics + /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` + /// + /// The UARTRSR/UARTECR register is the receive status register/error clear + /// register. Receive status can also be read from the `UARTRSR` + /// register. If the status is read from this register, then the status + /// information for break, framing and parity corresponds to the + /// data character read from the [Data register](Data), `UARTDR` prior to + /// reading the UARTRSR register. The status information for overrun is + /// set immediately when an overrun condition occurs. + /// + /// + /// # Note + /// The received data character must be read first from the [Data + /// Register](Data), `UARTDR` before reading the error status associated + /// with that data character from the `UARTRSR` register. This read + /// sequence cannot be reversed, because the `UARTRSR` register is + /// updated only when a read occurs from the `UARTDR` register. However, + /// the status information can also be obtained by reading the `UARTDR` + /// register + /// + /// # Source + /// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, + /// UARTRSR/UARTECR + #[bitsize(8)] + #[derive(Clone, Copy, DebugBits, FromBits)] + pub struct ReceiveStatusErrorClear { + pub framing_error: bool, + pub parity_error: bool, + pub break_error: bool, + pub overrun_error: bool, + _reserved_unpredictable: u4, + } + + impl ReceiveStatusErrorClear { + pub fn reset(&mut self) { + // All the bits are cleared to 0 on reset. + *self = 0.into(); + } + } + + impl Default for ReceiveStatusErrorClear { + fn default() -> Self { + 0.into() + } + } + + #[bitsize(16)] + #[derive(Clone, Copy, DebugBits, FromBits)] + /// Flag Register, `UARTFR` + #[doc(alias = "UARTFR")] + pub struct Flags { + /// CTS Clear to send. This bit is the complement of the UART clear to + /// send, `nUARTCTS`, modem status input. That is, the bit is 1 + /// when `nUARTCTS` is LOW. + pub clear_to_send: bool, + /// DSR Data set ready. This bit is the complement of the UART data set + /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when + /// `nUARTDSR` is LOW. + pub data_set_ready: bool, + /// DCD Data carrier detect. This bit is the complement of the UART data + /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is + /// 1 when `nUARTDCD` is LOW. + pub data_carrier_detect: bool, + /// BUSY UART busy. If this bit is set to 1, the UART is busy + /// transmitting data. This bit remains set until the complete + /// byte, including all the stop bits, has been sent from the + /// shift register. This bit is set as soon as the transmit FIFO + /// becomes non-empty, regardless of whether the UART is enabled + /// or not. + pub busy: bool, + /// RXFE Receive FIFO empty. The meaning of this bit depends on the + /// state of the FEN bit in the UARTLCR_H register. If the FIFO + /// is disabled, this bit is set when the receive holding + /// register is empty. If the FIFO is enabled, the RXFE bit is + /// set when the receive FIFO is empty. + pub receive_fifo_empty: bool, + /// TXFF Transmit FIFO full. The meaning of this bit depends on the + /// state of the FEN bit in the UARTLCR_H register. If the FIFO + /// is disabled, this bit is set when the transmit holding + /// register is full. If the FIFO is enabled, the TXFF bit is + /// set when the transmit FIFO is full. + pub transmit_fifo_full: bool, + /// RXFF Receive FIFO full. The meaning of this bit depends on the state + /// of the FEN bit in the UARTLCR_H register. If the FIFO is + /// disabled, this bit is set when the receive holding register + /// is full. If the FIFO is enabled, the RXFF bit is set when + /// the receive FIFO is full. + pub receive_fifo_full: bool, + /// Transmit FIFO empty. The meaning of this bit depends on the state of + /// the FEN bit in the [Line Control register](LineControl), + /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the + /// transmit holding register is empty. If the FIFO is enabled, + /// the TXFE bit is set when the transmit FIFO is empty. This + /// bit does not indicate if there is data in the transmit shift + /// register. + pub transmit_fifo_empty: bool, + /// `RI`, is `true` when `nUARTRI` is `LOW`. + pub ring_indicator: bool, + _reserved_zero_no_modify: u7, + } + + impl Flags { + pub fn reset(&mut self) { + // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 + self.set_receive_fifo_full(false); + self.set_transmit_fifo_full(false); + self.set_busy(false); + self.set_receive_fifo_empty(true); + self.set_transmit_fifo_empty(true); + } + } + + impl Default for Flags { + fn default() -> Self { + let mut ret: Self = 0.into(); + ret.reset(); + ret + } + } + + #[bitsize(16)] + #[derive(Clone, Copy, DebugBits, FromBits)] + /// Line Control Register, `UARTLCR_H` + #[doc(alias = "UARTLCR_H")] + pub struct LineControl { + /// 15:8 - Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u8, + /// 7 SPS Stick parity select. + /// 0 = stick parity is disabled + /// 1 = either: + /// • if the EPS bit is 0 then the parity bit is transmitted and checked + /// as a 1 • if the EPS bit is 1 then the parity bit is + /// transmitted and checked as a 0. This bit has no effect when + /// the PEN bit disables parity checking and generation. See Table 3-11 + /// on page 3-14 for the parity truth table. + pub sticky_parity: bool, + /// WLEN Word length. These bits indicate the number of data bits + /// transmitted or received in a frame as follows: b11 = 8 bits + /// b10 = 7 bits + /// b01 = 6 bits + /// b00 = 5 bits. + pub word_length: WordLength, + /// FEN Enable FIFOs: + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers 1 = transmit and receive FIFO + /// buffers are enabled (FIFO mode). + pub fifos_enabled: Mode, + /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits + /// are transmitted at the end of the frame. The receive + /// logic does not check for two stop bits being received. + pub two_stops_bits: bool, + /// EPS Even parity select. Controls the type of parity the UART uses + /// during transmission and reception: + /// - 0 = odd parity. The UART generates or checks for an odd number of + /// 1s in the data and parity bits. + /// - 1 = even parity. The UART generates or checks for an even number + /// of 1s in the data and parity bits. + /// This bit has no effect when the `PEN` bit disables parity checking + /// and generation. See Table 3-11 on page 3-14 for the parity + /// truth table. + pub parity: Parity, + /// 1 PEN Parity enable: + /// + /// - 0 = parity is disabled and no parity bit added to the data frame + /// - 1 = parity checking and generation is enabled. + /// + /// See Table 3-11 on page 3-14 for the parity truth table. + pub parity_enabled: bool, + /// BRK Send break. + /// + /// If this bit is set to `1`, a low-level is continually output on the + /// `UARTTXD` output, after completing transmission of the + /// current character. For the proper execution of the break command, + /// the software must set this bit for at least two complete + /// frames. For normal use, this bit must be cleared to `0`. + pub send_break: bool, + } + + impl LineControl { + pub fn reset(&mut self) { + // All the bits are cleared to 0 when reset. + *self = 0.into(); + } + } + + impl Default for LineControl { + fn default() -> Self { + 0.into() + } + } + + #[bitsize(1)] + #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] + /// `EPS` "Even parity select", field of [Line Control + /// register](LineControl). + pub enum Parity { + /// - 0 = odd parity. The UART generates or checks for an odd number of + /// 1s in the data and parity bits. + Odd = 0, + /// - 1 = even parity. The UART generates or checks for an even number + /// of 1s in the data and parity bits. + Even = 1, + } + + #[bitsize(1)] + #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] + /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control + /// register](LineControl). + pub enum Mode { + /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become + /// 1-byte-deep holding registers + Character = 0, + /// 1 = transmit and receive FIFO buffers are enabled (FIFO mode). + FIFO = 1, + } + + impl From for bool { + fn from(val: Mode) -> Self { + matches!(val, Mode::FIFO) + } + } + + #[bitsize(2)] + #[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] + /// `WLEN` Word length, field of [Line Control register](LineControl). + /// + /// These bits indicate the number of data bits transmitted or received in a + /// frame as follows: + pub enum WordLength { + /// b11 = 8 bits + _8Bits = 0b11, + /// b10 = 7 bits + _7Bits = 0b10, + /// b01 = 6 bits + _6Bits = 0b01, + /// b00 = 5 bits. + _5Bits = 0b00, + } + + /// Control Register, `UARTCR` + /// + /// The `UARTCR` register is the control register. All the bits are cleared + /// to `0` on reset except for bits `9` and `8` that are set to `1`. + /// + /// # Source + /// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 + #[bitsize(16)] + #[doc(alias = "UARTCR")] + #[derive(Clone, Copy, DebugBits, FromBits)] + pub struct Control { + /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled + /// in the middle of transmission or reception, it completes the current + /// character before stopping. 1 = the UART is enabled. Data + /// transmission and reception occurs for either UART signals or SIR + /// signals depending on the setting of the SIREN bit. + pub enable_uart: bool, + /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT` + /// remains LOW (no light pulse generated), and signal transitions on + /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is + /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, + /// in the marking state. Signal transitions on UARTRXD or modem status + /// inputs have no effect. This bit has no effect if the UARTEN bit + /// disables the UART. + pub enable_sir: bool, + /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding + /// mode. If this bit is cleared to 0, low-level bits are transmitted as + /// an active high pulse with a width of 3/ 16th of the bit period. If + /// this bit is set to 1, low-level bits are transmitted with a pulse + /// width that is 3 times the period of the IrLPBaud16 input signal, + /// regardless of the selected bit rate. Setting this bit uses less + /// power, but might reduce transmission distances. + pub sir_lowpower_irda_mode: u1, + /// Reserved, do not modify, read as zero. + _reserved_zero_no_modify: u4, + /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is + /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR + /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed + /// through to the SIRIN path. The SIRTEST bit in the test register must + /// be set to 1 to override the normal half-duplex SIR operation. This + /// must be the requirement for accessing the test registers during + /// normal operation, and SIRTEST must be cleared to 0 when loopback + /// testing is finished. This feature reduces the amount of external + /// coupling required during system test. If this bit is set to 1, and + /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the + /// UARTRXD path. In either SIR mode or UART mode, when this bit is set, + /// the modem outputs are also fed through to the modem inputs. This bit + /// is cleared to 0 on reset, to disable loopback. + pub enable_loopback: bool, + /// `TXE` Transmit enable. If this bit is set to 1, the transmit section + /// of the UART is enabled. Data transmission occurs for either UART + /// signals, or SIR signals depending on the setting of the SIREN bit. + /// When the UART is disabled in the middle of transmission, it + /// completes the current character before stopping. + pub enable_transmit: bool, + /// `RXE` Receive enable. If this bit is set to 1, the receive section + /// of the UART is enabled. Data reception occurs for either UART + /// signals or SIR signals depending on the setting of the SIREN bit. + /// When the UART is disabled in the middle of reception, it completes + /// the current character before stopping. + pub enable_receive: bool, + /// `DTR` Data transmit ready. This bit is the complement of the UART + /// data transmit ready, `nUARTDTR`, modem status output. That is, when + /// the bit is programmed to a 1 then `nUARTDTR` is LOW. + pub data_transmit_ready: bool, + /// `RTS` Request to send. This bit is the complement of the UART + /// request to send, `nUARTRTS`, modem status output. That is, when the + /// bit is programmed to a 1 then `nUARTRTS` is LOW. + pub request_to_send: bool, + /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`) + /// modem status output. That is, when the bit is programmed to a 1 the + /// output is 0. For DTE this can be used as Data Carrier Detect (DCD). + pub out_1: bool, + /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`) + /// modem status output. That is, when the bit is programmed to a 1, the + /// output is 0. For DTE this can be used as Ring Indicator (RI). + pub out_2: bool, + /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1, + /// RTS hardware flow control is enabled. Data is only requested when + /// there is space in the receive FIFO for it to be received. + pub rts_hardware_flow_control_enable: bool, + /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1, + /// CTS hardware flow control is enabled. Data is only transmitted when + /// the `nUARTCTS` signal is asserted. + pub cts_hardware_flow_control_enable: bool, + } + + impl Control { + pub fn reset(&mut self) { + *self = 0.into(); + self.set_enable_receive(true); + self.set_enable_transmit(true); + } + } + + impl Default for Control { + fn default() -> Self { + let mut ret: Self = 0.into(); + ret.reset(); + ret + } + } + + /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC + pub const INT_OE: u32 = 1 << 10; + pub const INT_BE: u32 = 1 << 9; + pub const INT_PE: u32 = 1 << 8; + pub const INT_FE: u32 = 1 << 7; + pub const INT_RT: u32 = 1 << 6; + pub const INT_TX: u32 = 1 << 5; + pub const INT_RX: u32 = 1 << 4; + pub const INT_DSR: u32 = 1 << 3; + pub const INT_DCD: u32 = 1 << 2; + pub const INT_CTS: u32 = 1 << 1; + pub const INT_RI: u32 = 1 << 0; + pub const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; + pub const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + + #[repr(u32)] + pub enum Interrupt { + OE = 1 << 10, + BE = 1 << 9, + PE = 1 << 8, + FE = 1 << 7, + RT = 1 << 6, + TX = 1 << 5, + RX = 1 << 4, + DSR = 1 << 3, + DCD = 1 << 2, + CTS = 1 << 1, + RI = 1 << 0, + } + + impl Interrupt { + pub const E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; + pub const MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + } +} + +// TODO: You must disable the UART before any of the control registers are +// reprogrammed. When the UART is disabled in the middle of transmission or +// reception, it completes the current character before stopping diff --git a/rust/hw/char/pl011/src/memory_ops.rs b/rust/hw/char/pl011/src/memory_ops.rs new file mode 100644 index 0000000000..169d485a4d --- /dev/null +++ b/rust/hw/char/pl011/src/memory_ops.rs @@ -0,0 +1,49 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use core::ptr::NonNull; +use std::os::raw::{c_uint, c_void}; + +use qemu_api::{bindings::*, zeroable::Zeroable}; + +use crate::device::PL011State; + +pub static PL011_OPS: MemoryRegionOps = MemoryRegionOps { + read: Some(pl011_read), + write: Some(pl011_write), + read_with_attrs: None, + write_with_attrs: None, + endianness: device_endian::DEVICE_NATIVE_ENDIAN, + valid: Zeroable::ZERO, + impl_: MemoryRegionOps__bindgen_ty_2 { + min_access_size: 4, + max_access_size: 4, + ..Zeroable::ZERO + }, +}; + +unsafe extern "C" fn pl011_read(opaque: *mut c_void, addr: hwaddr, size: c_uint) -> u64 { + assert!(!opaque.is_null()); + let mut state = unsafe { NonNull::new_unchecked(opaque.cast::()) }; + let val = unsafe { state.as_mut().read(addr, size) }; + match val { + std::ops::ControlFlow::Break(val) => val, + std::ops::ControlFlow::Continue(val) => { + // SAFETY: self.char_backend is a valid CharBackend instance after it's been + // initialized in realize(). + let cb_ptr = unsafe { core::ptr::addr_of_mut!(state.as_mut().char_backend) }; + unsafe { qemu_chr_fe_accept_input(cb_ptr) }; + + val + } + } +} + +unsafe extern "C" fn pl011_write(opaque: *mut c_void, addr: hwaddr, data: u64, _size: c_uint) { + unsafe { + assert!(!opaque.is_null()); + let mut state = NonNull::new_unchecked(opaque.cast::()); + state.as_mut().write(addr, data) + } +} diff --git a/rust/hw/meson.build b/rust/hw/meson.build new file mode 100644 index 0000000000..860196645e --- /dev/null +++ b/rust/hw/meson.build @@ -0,0 +1 @@ +subdir('char') diff --git a/rust/meson.build b/rust/meson.build new file mode 100644 index 0000000000..def77389cd --- /dev/null +++ b/rust/meson.build @@ -0,0 +1,4 @@ +subdir('qemu-api-macros') +subdir('qemu-api') + +subdir('hw') diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-api-macros/Cargo.toml new file mode 100644 index 0000000000..a8f7377106 --- /dev/null +++ b/rust/qemu-api-macros/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "qemu_api_macros" +version = "0.1.0" +edition = "2021" +authors = ["Manos Pitsidianakis "] +license = "GPL-2.0-or-later" +readme = "README.md" +homepage = "https://www.qemu.org" +description = "Rust bindings for QEMU - Utility macros" +repository = "https://gitlab.com/qemu-project/qemu/" +resolver = "2" +publish = false +keywords = [] +categories = [] + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1" +quote = "1" +syn = { version = "2", features = ["extra-traits"] } diff --git a/rust/qemu-api-macros/README.md b/rust/qemu-api-macros/README.md new file mode 100644 index 0000000000..f60f54ac4b --- /dev/null +++ b/rust/qemu-api-macros/README.md @@ -0,0 +1 @@ +# `qemu-api-macros` - Utility macros for defining QEMU devices diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build new file mode 100644 index 0000000000..6f94a4bb3c --- /dev/null +++ b/rust/qemu-api-macros/meson.build @@ -0,0 +1,27 @@ +subproject('proc-macro2-1-rs', required: true) +subproject('quote-1-rs', required: true) +subproject('syn-2-rs', required: true) + +quote_dep = dependency('quote-1-rs', native: true) +syn_dep = dependency('syn-2-rs', native: true) +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) + +_qemu_api_macros_rs = rust.proc_macro( + 'qemu_api_macros', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: [ + '--cfg', 'use_fallback', + '--cfg', 'feature="syn-error"', + '--cfg', 'feature="proc-macro"', + ], + dependencies: [ + proc_macro2_dep, + quote_dep, + syn_dep, + ], +) + +qemu_api_macros = declare_dependency( + link_with: _qemu_api_macros_rs, +) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs new file mode 100644 index 0000000000..cf99ac04b8 --- /dev/null +++ b/rust/qemu-api-macros/src/lib.rs @@ -0,0 +1,94 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{quote, quote_spanned}; +use syn::{ + parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, + Fields, Ident, Type, Visibility, +}; + +struct CompileError(String, Span); + +impl From for proc_macro2::TokenStream { + fn from(err: CompileError) -> Self { + let CompileError(msg, span) = err; + quote_spanned! { span => compile_error!(#msg); } + } +} + +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), CompileError> { + let expected = parse_quote! { #[repr(C)] }; + + if input.attrs.iter().any(|attr| attr == &expected) { + Ok(()) + } else { + Err(CompileError( + format!("#[repr(C)] required for {}", msg), + input.ident.span(), + )) + } +} + +#[proc_macro_derive(Object)] +pub fn derive_object(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = input.ident; + + let expanded = quote! { + ::qemu_api::module_init! { + MODULE_INIT_QOM => unsafe { + ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::definitions::ObjectImpl>::TYPE_INFO); + } + } + }; + + TokenStream::from(expanded) +} + +fn get_fields(input: &DeriveInput) -> Result<&Punctuated, CompileError> { + if let Data::Struct(s) = &input.data { + if let Fields::Named(fs) = &s.fields { + Ok(&fs.named) + } else { + Err(CompileError( + "Cannot generate offsets for unnamed fields.".to_string(), + input.ident.span(), + )) + } + } else { + Err(CompileError( + "Cannot generate offsets for union or enum.".to_string(), + input.ident.span(), + )) + } +} + +#[rustfmt::skip::macros(quote)] +fn derive_offsets_or_error(input: DeriveInput) -> Result { + is_c_repr(&input, "#[derive(offsets)]")?; + + let name = &input.ident; + let fields = get_fields(&input)?; + let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect(); + let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect(); + let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect(); + + Ok(quote! { + ::qemu_api::with_offsets! { + struct #name { + #(#field_vis #field_names: #field_types,)* + } + } + }) +} + +#[proc_macro_derive(offsets)] +pub fn derive_offsets(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let expanded = derive_offsets_or_error(input).unwrap_or_else(Into::into); + + TokenStream::from(expanded) +} diff --git a/rust/qemu-api/.gitignore b/rust/qemu-api/.gitignore new file mode 100644 index 0000000000..b9e7e004c8 --- /dev/null +++ b/rust/qemu-api/.gitignore @@ -0,0 +1,2 @@ +# Ignore generated bindings file overrides. +src/bindings.rs diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml new file mode 100644 index 0000000000..cc716d75d4 --- /dev/null +++ b/rust/qemu-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "qemu_api" +version = "0.1.0" +edition = "2021" +authors = ["Manos Pitsidianakis "] +license = "GPL-2.0-or-later" +readme = "README.md" +homepage = "https://www.qemu.org" +description = "Rust bindings for QEMU" +repository = "https://gitlab.com/qemu-project/qemu/" +resolver = "2" +publish = false +keywords = [] +categories = [] + +[dependencies] +qemu_api_macros = { path = "../qemu-api-macros" } + +[build-dependencies] +version_check = "~0.9" + +[features] +default = [] +allocator = [] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', + 'cfg(has_offset_of)'] } diff --git a/rust/qemu-api/README.md b/rust/qemu-api/README.md new file mode 100644 index 0000000000..7588fa29ef --- /dev/null +++ b/rust/qemu-api/README.md @@ -0,0 +1,17 @@ +# QEMU bindings and API wrappers + +This library exports helper Rust types, Rust macros and C FFI bindings for internal QEMU APIs. + +The C bindings can be generated with `bindgen`, using this build target: + +```console +$ ninja bindings.rs +``` + +## Generate Rust documentation + +To generate docs for this crate, including private items: + +```sh +cargo doc --no-deps --document-private-items +``` diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs new file mode 100644 index 0000000000..20f8f718b9 --- /dev/null +++ b/rust/qemu-api/build.rs @@ -0,0 +1,23 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::path::Path; + +use version_check as rustc; + +fn main() { + if !Path::new("src/bindings.rs").exists() { + panic!( + "No generated C bindings found! Either build them manually with bindgen or with meson \ + (`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson." + ); + } + + // Check for available rustc features + if rustc::is_min_version("1.77.0").unwrap_or(false) { + println!("cargo:rustc-cfg=has_offset_of"); + } + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build new file mode 100644 index 0000000000..6f637af7b1 --- /dev/null +++ b/rust/qemu-api/meson.build @@ -0,0 +1,54 @@ +_qemu_api_cfg = ['--cfg', 'MESON'] +# _qemu_api_cfg += ['--cfg', 'feature="allocator"'] +if rustc.version().version_compare('>=1.77.0') + _qemu_api_cfg += ['--cfg', 'has_offset_of'] +endif + +_qemu_api_rs = static_library( + 'qemu_api', + structured_sources( + [ + 'src/lib.rs', + 'src/c_str.rs', + 'src/definitions.rs', + 'src/device_class.rs', + 'src/offset_of.rs', + 'src/vmstate.rs', + 'src/zeroable.rs', + ], + {'.' : bindings_rs}, + ), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: _qemu_api_cfg, +) + +rust.test('rust-qemu-api-tests', _qemu_api_rs, + suite: ['unit', 'rust']) + +qemu_api = declare_dependency( + link_with: _qemu_api_rs, + dependencies: qemu_api_macros, +) + +# Rust executables do not support objects, so add an intermediate step. +rust_qemu_api_objs = static_library( + 'rust_qemu_api_objs', + objects: [libqom.extract_all_objects(recursive: false), + libhwcore.extract_all_objects(recursive: false)]) + +test('rust-qemu-api-integration', + executable( + 'rust-qemu-api-integration', + 'tests/tests.rs', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: ['--test'], + install: false, + dependencies: [qemu_api, qemu_api_macros], + link_whole: [rust_qemu_api_objs, libqemuutil]), + args: [ + '--test', + '--format', 'pretty', + ], + protocol: 'rust', + suite: ['unit', 'rust']) diff --git a/rust/qemu-api/src/c_str.rs b/rust/qemu-api/src/c_str.rs new file mode 100644 index 0000000000..4cd96da0b4 --- /dev/null +++ b/rust/qemu-api/src/c_str.rs @@ -0,0 +1,53 @@ +// Copyright 2024 Red Hat, Inc. +// Author(s): Paolo Bonzini +// SPDX-License-Identifier: GPL-2.0-or-later + +#[macro_export] +/// Given a string constant _without_ embedded or trailing NULs, return +/// a `CStr`. +/// +/// Needed for compatibility with Rust <1.77. +macro_rules! c_str { + ($str:expr) => {{ + const STRING: &str = concat!($str, "\0"); + const BYTES: &[u8] = STRING.as_bytes(); + + // "for" is not allowed in const context... oh well, + // everybody loves some lisp. This could be turned into + // a procedural macro if this is a problem; alternatively + // Rust 1.72 makes CStr::from_bytes_with_nul a const function. + const fn f(b: &[u8], i: usize) { + if i == b.len() - 1 { + } else if b[i] == 0 { + panic!("c_str argument contains NUL") + } else { + f(b, i + 1) + } + } + f(BYTES, 0); + + // SAFETY: absence of NULs apart from the final byte was checked above + unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) } + }}; +} + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use crate::c_str; + + #[test] + fn test_cstr_macro() { + let good = c_str!("🦀"); + let good_bytes = b"\xf0\x9f\xa6\x80\0"; + assert_eq!(good.to_bytes_with_nul(), good_bytes); + } + + #[test] + fn test_cstr_macro_const() { + const GOOD: &CStr = c_str!("🦀"); + const GOOD_BYTES: &[u8] = b"\xf0\x9f\xa6\x80\0"; + assert_eq!(GOOD.to_bytes_with_nul(), GOOD_BYTES); + } +} diff --git a/rust/qemu-api/src/definitions.rs b/rust/qemu-api/src/definitions.rs new file mode 100644 index 0000000000..26597934bb --- /dev/null +++ b/rust/qemu-api/src/definitions.rs @@ -0,0 +1,91 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Definitions required by QEMU when registering a device. + +use std::{ffi::CStr, os::raw::c_void}; + +use crate::bindings::{Object, ObjectClass, TypeInfo}; + +/// Trait a type must implement to be registered with QEMU. +pub trait ObjectImpl { + type Class; + const TYPE_INFO: TypeInfo; + const TYPE_NAME: &'static CStr; + const PARENT_TYPE_NAME: Option<&'static CStr>; + const ABSTRACT: bool; + const INSTANCE_INIT: Option; + const INSTANCE_POST_INIT: Option; + const INSTANCE_FINALIZE: Option; +} + +pub trait Class { + const CLASS_INIT: Option; + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + >; +} + +#[macro_export] +macro_rules! module_init { + ($type:ident => $body:block) => { + const _: () = { + #[used] + #[cfg_attr( + not(any(target_vendor = "apple", target_os = "windows")), + link_section = ".init_array" + )] + #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")] + #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU")] + pub static LOAD_MODULE: extern "C" fn() = { + extern "C" fn init_fn() { + $body + } + + extern "C" fn ctor_fn() { + unsafe { + $crate::bindings::register_module_init( + Some(init_fn), + $crate::bindings::module_init_type::$type, + ); + } + } + + ctor_fn + }; + }; + }; + + // shortcut because it's quite common that $body needs unsafe {} + ($type:ident => unsafe $body:block) => { + $crate::module_init! { + $type => { unsafe { $body } } + } + }; +} + +#[macro_export] +macro_rules! type_info { + ($t:ty) => { + $crate::bindings::TypeInfo { + name: <$t as $crate::definitions::ObjectImpl>::TYPE_NAME.as_ptr(), + parent: if let Some(pname) = <$t as $crate::definitions::ObjectImpl>::PARENT_TYPE_NAME { + pname.as_ptr() + } else { + ::core::ptr::null_mut() + }, + instance_size: ::core::mem::size_of::<$t>(), + instance_align: ::core::mem::align_of::<$t>(), + instance_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_INIT, + instance_post_init: <$t as $crate::definitions::ObjectImpl>::INSTANCE_POST_INIT, + instance_finalize: <$t as $crate::definitions::ObjectImpl>::INSTANCE_FINALIZE, + abstract_: <$t as $crate::definitions::ObjectImpl>::ABSTRACT, + class_size: ::core::mem::size_of::<<$t as $crate::definitions::ObjectImpl>::Class>(), + class_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_INIT, + class_base_init: <<$t as $crate::definitions::ObjectImpl>::Class as $crate::definitions::Class>::CLASS_BASE_INIT, + class_data: ::core::ptr::null_mut(), + interfaces: ::core::ptr::null_mut(), + }; + } +} diff --git a/rust/qemu-api/src/device_class.rs b/rust/qemu-api/src/device_class.rs new file mode 100644 index 0000000000..0ba798d3e3 --- /dev/null +++ b/rust/qemu-api/src/device_class.rs @@ -0,0 +1,74 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::ffi::CStr; + +use crate::bindings; + +#[macro_export] +macro_rules! device_class_init { + ($func:ident, props => $props:ident, realize_fn => $realize_fn:expr, legacy_reset_fn => $legacy_reset_fn:expr, vmsd => $vmsd:ident$(,)*) => { + pub unsafe extern "C" fn $func( + klass: *mut $crate::bindings::ObjectClass, + _: *mut ::std::os::raw::c_void, + ) { + let mut dc = + ::core::ptr::NonNull::new(klass.cast::<$crate::bindings::DeviceClass>()).unwrap(); + unsafe { + dc.as_mut().realize = $realize_fn; + dc.as_mut().vmsd = &$vmsd; + $crate::bindings::device_class_set_legacy_reset(dc.as_mut(), $legacy_reset_fn); + $crate::bindings::device_class_set_props(dc.as_mut(), $props.as_ptr()); + } + } + }; +} + +#[macro_export] +macro_rules! define_property { + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr, default = $defval:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: $crate::offset_of!($state, $field) as isize, + set_default: true, + defval: $crate::bindings::Property__bindgen_ty_1 { u: $defval as u64 }, + ..$crate::zeroable::Zeroable::ZERO + } + }; + ($name:expr, $state:ty, $field:ident, $prop:expr, $type:expr$(,)*) => { + $crate::bindings::Property { + // use associated function syntax for type checking + name: ::std::ffi::CStr::as_ptr($name), + info: $prop, + offset: $crate::offset_of!($state, $field) as isize, + set_default: false, + ..$crate::zeroable::Zeroable::ZERO + } + }; +} + +#[macro_export] +macro_rules! declare_properties { + ($ident:ident, $($prop:expr),*$(,)*) => { + pub static $ident: [$crate::bindings::Property; { + let mut len = 1; + $({ + _ = stringify!($prop); + len += 1; + })* + len + }] = [ + $($prop),*, + $crate::zeroable::Zeroable::ZERO, + ]; + }; +} + +// workaround until we can use --generate-cstr in bindgen. +pub const TYPE_DEVICE: &CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_DEVICE) }; +pub const TYPE_SYS_BUS_DEVICE: &CStr = + unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs new file mode 100644 index 0000000000..aa8d16ec94 --- /dev/null +++ b/rust/qemu-api/src/lib.rs @@ -0,0 +1,175 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +#![cfg_attr(not(MESON), doc = include_str!("../README.md"))] + +#[allow( + dead_code, + improper_ctypes_definitions, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + clippy::missing_const_for_fn, + clippy::too_many_arguments, + clippy::approx_constant, + clippy::use_self, + clippy::useless_transmute, + clippy::missing_safety_doc, +)] +#[rustfmt::skip] +pub mod bindings; + +unsafe impl Send for bindings::Property {} +unsafe impl Sync for bindings::Property {} +unsafe impl Sync for bindings::TypeInfo {} +unsafe impl Sync for bindings::VMStateDescription {} +unsafe impl Sync for bindings::VMStateField {} +unsafe impl Sync for bindings::VMStateInfo {} + +pub mod c_str; +pub mod definitions; +pub mod device_class; +pub mod offset_of; +pub mod vmstate; +pub mod zeroable; + +use std::{ + alloc::{GlobalAlloc, Layout}, + os::raw::c_void, +}; + +#[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] +extern "C" { + fn g_aligned_alloc0( + n_blocks: bindings::gsize, + n_block_bytes: bindings::gsize, + alignment: bindings::gsize, + ) -> bindings::gpointer; + fn g_aligned_free(mem: bindings::gpointer); +} + +#[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] +extern "C" { + fn qemu_memalign(alignment: usize, size: usize) -> *mut c_void; + fn qemu_vfree(ptr: *mut c_void); +} + +extern "C" { + fn g_malloc0(n_bytes: bindings::gsize) -> bindings::gpointer; + fn g_free(mem: bindings::gpointer); +} + +/// An allocator that uses the same allocator as QEMU in C. +/// +/// It is enabled by default with the `allocator` feature. +/// +/// To set it up manually as a global allocator in your crate: +/// +/// ```ignore +/// use qemu_api::QemuAllocator; +/// +/// #[global_allocator] +/// static GLOBAL: QemuAllocator = QemuAllocator::new(); +/// ``` +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct QemuAllocator { + _unused: [u8; 0], +} + +#[cfg_attr(all(feature = "allocator", not(test)), global_allocator)] +pub static GLOBAL: QemuAllocator = QemuAllocator::new(); + +impl QemuAllocator { + // From the glibc documentation, on GNU systems, malloc guarantees 16-byte + // alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See + // https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html. + // This alignment guarantee also applies to Windows and Android. On Darwin + // and OpenBSD, the alignment is 16 bytes on both 64-bit and 32-bit systems. + #[cfg(all( + target_pointer_width = "32", + not(any(target_os = "macos", target_os = "openbsd")) + ))] + pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(8); + #[cfg(all( + target_pointer_width = "64", + not(any(target_os = "macos", target_os = "openbsd")) + ))] + pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(16); + #[cfg(all( + any(target_pointer_width = "32", target_pointer_width = "64"), + any(target_os = "macos", target_os = "openbsd") + ))] + pub const DEFAULT_ALIGNMENT_BYTES: Option = Some(16); + #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] + pub const DEFAULT_ALIGNMENT_BYTES: Option = None; + + pub const fn new() -> Self { + Self { _unused: [] } + } +} + +impl Default for QemuAllocator { + fn default() -> Self { + Self::new() + } +} + +// Sanity check. +const _: [(); 8] = [(); ::core::mem::size_of::<*mut c_void>()]; + +unsafe impl GlobalAlloc for QemuAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0)) + { + // SAFETY: g_malloc0() is safe to call. + unsafe { g_malloc0(layout.size().try_into().unwrap()).cast::() } + } else { + #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] + { + // SAFETY: g_aligned_alloc0() is safe to call. + unsafe { + g_aligned_alloc0( + layout.size().try_into().unwrap(), + 1, + layout.align().try_into().unwrap(), + ) + .cast::() + } + } + #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] + { + // SAFETY: qemu_memalign() is safe to call. + unsafe { qemu_memalign(layout.align(), layout.size()).cast::() } + } + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if matches!(Self::DEFAULT_ALIGNMENT_BYTES, Some(default) if default.checked_rem(layout.align()) == Some(0)) + { + // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid + // glib-allocated pointer, so `g_free`ing is safe. + unsafe { g_free(ptr.cast::<_>()) } + } else { + #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] + { + // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned + // glib-allocated pointer, so `g_aligned_free`ing is safe. + unsafe { g_aligned_free(ptr.cast::<_>()) } + } + #[cfg(not(HAVE_GLIB_WITH_ALIGNED_ALLOC))] + { + // SAFETY: `ptr` must have been allocated by Self::alloc thus a valid aligned + // glib-allocated pointer, so `qemu_vfree`ing is safe. + unsafe { qemu_vfree(ptr.cast::<_>()) } + } + } + } +} + +#[cfg(has_offset_of)] +pub use core::mem::offset_of; diff --git a/rust/qemu-api/src/offset_of.rs b/rust/qemu-api/src/offset_of.rs new file mode 100644 index 0000000000..075e98f986 --- /dev/null +++ b/rust/qemu-api/src/offset_of.rs @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: MIT + +/// This macro provides the same functionality as `core::mem::offset_of`, +/// except that only one level of field access is supported. The declaration +/// of the struct must be wrapped with `with_offsets! { }`. +/// +/// It is needed because `offset_of!` was only stabilized in Rust 1.77. +#[cfg(not(has_offset_of))] +#[macro_export] +macro_rules! offset_of { + ($Container:ty, $field:ident) => { + <$Container>::OFFSET_TO__.$field + }; +} + +/// A wrapper for struct declarations, that allows using `offset_of!` in +/// versions of Rust prior to 1.77 +#[macro_export] +macro_rules! with_offsets { + // This method to generate field offset constants comes from: + // + // https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df + // + // used under MIT license with permission of Yandros aka Daniel Henry-Mantilla + ( + $(#[$struct_meta:meta])* + $struct_vis:vis + struct $StructName:ident { + $( + $(#[$field_meta:meta])* + $field_vis:vis + $field_name:ident : $field_ty:ty + ),* + $(,)? + } + ) => ( + #[cfg(not(has_offset_of))] + const _: () = { + struct StructOffsetsHelper(std::marker::PhantomData); + const END_OF_PREV_FIELD: usize = 0; + + // populate StructOffsetsHelper with associated consts, + // one for each field + $crate::with_offsets! { + @struct $StructName + @names [ $($field_name)* ] + @tys [ $($field_ty ,)*] + } + + // now turn StructOffsetsHelper's consts into a single struct, + // applying field visibility. This provides better error messages + // than if offset_of! used StructOffsetsHelper:: directly. + pub + struct StructOffsets { + $( + $field_vis + $field_name: usize, + )* + } + impl $StructName { + pub + const OFFSET_TO__: StructOffsets = StructOffsets { + $( + $field_name: StructOffsetsHelper::<$StructName>::$field_name, + )* + }; + } + }; + ); + + ( + @struct $StructName:ident + @names [] + @tys [] + ) => (); + + ( + @struct $StructName:ident + @names [$field_name:ident $($other_names:tt)*] + @tys [$field_ty:ty , $($other_tys:tt)*] + ) => ( + #[allow(non_local_definitions)] + #[allow(clippy::modulo_one)] + impl StructOffsetsHelper<$StructName> { + #[allow(nonstandard_style)] + const $field_name: usize = { + const ALIGN: usize = std::mem::align_of::<$field_ty>(); + const TRAIL: usize = END_OF_PREV_FIELD % ALIGN; + END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL }) + }; + } + const _: () = { + const END_OF_PREV_FIELD: usize = + StructOffsetsHelper::<$StructName>::$field_name + + std::mem::size_of::<$field_ty>() + ; + $crate::with_offsets! { + @struct $StructName + @names [$($other_names)*] + @tys [$($other_tys)*] + } + }; + ); +} + +#[cfg(test)] +mod tests { + use crate::offset_of; + + #[repr(C)] + struct Foo { + a: u16, + b: u32, + c: u64, + d: u16, + } + + #[repr(C)] + struct Bar { + pub a: u16, + pub b: u64, + c: Foo, + d: u64, + } + + crate::with_offsets! { + #[repr(C)] + struct Bar { + pub a: u16, + pub b: u64, + c: Foo, + d: u64, + } + } + + #[repr(C)] + pub struct Baz { + b: u32, + a: u8, + } + crate::with_offsets! { + #[repr(C)] + pub struct Baz { + b: u32, + a: u8, + } + } + + #[test] + fn test_offset_of() { + const OFFSET_TO_C: usize = offset_of!(Bar, c); + + assert_eq!(offset_of!(Bar, a), 0); + assert_eq!(offset_of!(Bar, b), 8); + assert_eq!(OFFSET_TO_C, 16); + assert_eq!(offset_of!(Bar, d), 40); + + assert_eq!(offset_of!(Baz, b), 0); + assert_eq!(offset_of!(Baz, a), 4); + } +} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs new file mode 100644 index 0000000000..bedcf1e8f3 --- /dev/null +++ b/rust/qemu-api/src/vmstate.rs @@ -0,0 +1,360 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Helper macros to declare migration state for device models. +//! +//! Some macros are direct equivalents to the C macros declared in +//! `include/migration/vmstate.h` while +//! [`vmstate_subsections`](crate::vmstate_subsections) and +//! [`vmstate_fields`](crate::vmstate_fields) are meant to be used when +//! declaring a device model state struct. + +#[doc(alias = "VMSTATE_UNUSED_BUFFER")] +#[macro_export] +macro_rules! vmstate_unused_buffer { + ($field_exists_fn:expr, $version_id:expr, $size:expr) => {{ + $crate::bindings::VMStateField { + name: c_str!("unused").as_ptr(), + err_hint: ::core::ptr::null(), + offset: 0, + size: $size, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_info_unused_buffer) }, + flags: VMStateFlags::VMS_BUFFER, + vmsd: ::core::ptr::null(), + version_id: $version_id, + struct_version_id: 0, + field_exists: $field_exists_fn, + } + }}; +} + +#[doc(alias = "VMSTATE_UNUSED_V")] +#[macro_export] +macro_rules! vmstate_unused_v { + ($version_id:expr, $size:expr) => {{ + $crate::vmstate_unused_buffer!(None, $version_id, $size) + }}; +} + +#[doc(alias = "VMSTATE_UNUSED")] +#[macro_export] +macro_rules! vmstate_unused { + ($size:expr) => {{ + $crate::vmstate_unused_v!(0, $size) + }}; +} + +#[doc(alias = "VMSTATE_SINGLE_TEST")] +#[macro_export] +macro_rules! vmstate_single_test { + ($field_name:ident, $struct_name:ty, $field_exists_fn:expr, $version_id:expr, $info:expr, $size:expr) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + err_hint: ::core::ptr::null(), + offset: $crate::offset_of!($struct_name, $field_name), + size: $size, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: unsafe { $info }, + flags: VMStateFlags::VMS_SINGLE, + vmsd: ::core::ptr::null(), + version_id: $version_id, + struct_version_id: 0, + field_exists: $field_exists_fn, + } + }}; +} + +#[doc(alias = "VMSTATE_SINGLE")] +#[macro_export] +macro_rules! vmstate_single { + ($field_name:ident, $struct_name:ty, $version_id:expr, $info:expr, $size:expr) => {{ + $crate::vmstate_single_test!($field_name, $struct_name, None, $version_id, $info, $size) + }}; +} + +#[doc(alias = "VMSTATE_UINT32_V")] +#[macro_export] +macro_rules! vmstate_uint32_v { + ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ + $crate::vmstate_single!( + $field_name, + $struct_name, + $version_id, + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32), + ::core::mem::size_of::() + ) + }}; +} + +#[doc(alias = "VMSTATE_UINT32")] +#[macro_export] +macro_rules! vmstate_uint32 { + ($field_name:ident, $struct_name:ty) => {{ + $crate::vmstate_uint32_v!($field_name, $struct_name, 0) + }}; +} + +#[doc(alias = "VMSTATE_INT32_V")] +#[macro_export] +macro_rules! vmstate_int32_v { + ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ + $crate::vmstate_single!( + $field_name, + $struct_name, + $version_id, + ::core::ptr::addr_of!($crate::bindings::vmstate_info_int32), + ::core::mem::size_of::() + ) + }}; +} + +#[doc(alias = "VMSTATE_INT32")] +#[macro_export] +macro_rules! vmstate_int32 { + ($field_name:ident, $struct_name:ty) => {{ + $crate::vmstate_int32_v!($field_name, $struct_name, 0) + }}; +} + +#[doc(alias = "VMSTATE_ARRAY")] +#[macro_export] +macro_rules! vmstate_array { + ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr, $info:expr, $size:expr) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + err_hint: ::core::ptr::null(), + offset: $crate::offset_of!($struct_name, $field_name), + size: $size, + start: 0, + num: $length as _, + num_offset: 0, + size_offset: 0, + info: unsafe { $info }, + flags: VMStateFlags::VMS_ARRAY, + vmsd: ::core::ptr::null(), + version_id: $version_id, + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_UINT32_ARRAY_V")] +#[macro_export] +macro_rules! vmstate_uint32_array_v { + ($field_name:ident, $struct_name:ty, $length:expr, $version_id:expr) => {{ + $crate::vmstate_array!( + $field_name, + $struct_name, + $length, + $version_id, + ::core::ptr::addr_of!($crate::bindings::vmstate_info_uint32), + ::core::mem::size_of::() + ) + }}; +} + +#[doc(alias = "VMSTATE_UINT32_ARRAY")] +#[macro_export] +macro_rules! vmstate_uint32_array { + ($field_name:ident, $struct_name:ty, $length:expr) => {{ + $crate::vmstate_uint32_array_v!($field_name, $struct_name, $length, 0) + }}; +} + +#[doc(alias = "VMSTATE_STRUCT_POINTER_V")] +#[macro_export] +macro_rules! vmstate_struct_pointer_v { + ($field_name:ident, $struct_name:ty, $version_id:expr, $vmsd:expr, $type:ty) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + err_hint: ::core::ptr::null(), + offset: $crate::offset_of!($struct_name, $field_name), + size: ::core::mem::size_of::<*const $type>(), + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: ::core::ptr::null(), + flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), + vmsd: unsafe { $vmsd }, + version_id: $version_id, + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_OF_POINTER")] +#[macro_export] +macro_rules! vmstate_array_of_pointer { + ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $info:expr, $type:ty) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + version_id: $version_id, + num: $num as _, + info: unsafe { $info }, + size: ::core::mem::size_of::<*const $type>(), + flags: VMStateFlags(VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0), + offset: $crate::offset_of!($struct_name, $field_name), + err_hint: ::core::ptr::null(), + start: 0, + num_offset: 0, + size_offset: 0, + vmsd: ::core::ptr::null(), + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_OF_POINTER_TO_STRUCT")] +#[macro_export] +macro_rules! vmstate_array_of_pointer_to_struct { + ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr, $vmsd:expr, $type:ty) => {{ + $crate::bindings::VMStateField { + name: ::core::concat!(::core::stringify!($field_name), 0) + .as_bytes() + .as_ptr() as *const ::std::os::raw::c_char, + version_id: $version_id, + num: $num as _, + vmsd: unsafe { $vmsd }, + size: ::core::mem::size_of::<*const $type>(), + flags: VMStateFlags( + VMStateFlags::VMS_ARRAY.0 + | VMStateFlags::VMS_STRUCT.0 + | VMStateFlags::VMS_ARRAY_OF_POINTER.0, + ), + offset: $crate::offset_of!($struct_name, $field_name), + err_hint: ::core::ptr::null(), + start: 0, + num_offset: 0, + size_offset: 0, + vmsd: ::core::ptr::null(), + struct_version_id: 0, + field_exists: None, + } + }}; +} + +#[doc(alias = "VMSTATE_CLOCK_V")] +#[macro_export] +macro_rules! vmstate_clock_v { + ($field_name:ident, $struct_name:ty, $version_id:expr) => {{ + $crate::vmstate_struct_pointer_v!( + $field_name, + $struct_name, + $version_id, + ::core::ptr::addr_of!($crate::bindings::vmstate_clock), + $crate::bindings::Clock + ) + }}; +} + +#[doc(alias = "VMSTATE_CLOCK")] +#[macro_export] +macro_rules! vmstate_clock { + ($field_name:ident, $struct_name:ty) => {{ + $crate::vmstate_clock_v!($field_name, $struct_name, 0) + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_CLOCK_V")] +#[macro_export] +macro_rules! vmstate_array_clock_v { + ($field_name:ident, $struct_name:ty, $num:expr, $version_id:expr) => {{ + $crate::vmstate_array_of_pointer_to_struct!( + $field_name, + $struct_name, + $num, + $version_id, + ::core::ptr::addr_of!($crate::bindings::vmstate_clock), + $crate::bindings::Clock + ) + }}; +} + +#[doc(alias = "VMSTATE_ARRAY_CLOCK")] +#[macro_export] +macro_rules! vmstate_array_clock { + ($field_name:ident, $struct_name:ty, $num:expr) => {{ + $crate::vmstate_array_clock_v!($field_name, $struct_name, $name, 0) + }}; +} + +/// Helper macro to declare a list of +/// ([`VMStateField`](`crate::bindings::VMStateField`)) into a static and return +/// a pointer to the array of values it created. +#[macro_export] +macro_rules! vmstate_fields { + ($($field:expr),*$(,)*) => {{ + static _FIELDS: &[$crate::bindings::VMStateField] = &[ + $($field),*, + $crate::bindings::VMStateField { + name: ::core::ptr::null(), + err_hint: ::core::ptr::null(), + offset: 0, + size: 0, + start: 0, + num: 0, + num_offset: 0, + size_offset: 0, + info: ::core::ptr::null(), + flags: VMStateFlags::VMS_END, + vmsd: ::core::ptr::null(), + version_id: 0, + struct_version_id: 0, + field_exists: None, + } + ]; + _FIELDS.as_ptr() + }} +} + +/// A transparent wrapper type for the `subsections` field of +/// [`VMStateDescription`](crate::bindings::VMStateDescription). +/// +/// This is necessary to be able to declare subsection descriptions as statics, +/// because the only way to implement `Sync` for a foreign type (and `*const` +/// pointers are foreign types in Rust) is to create a wrapper struct and +/// `unsafe impl Sync` for it. +/// +/// This struct is used in the +/// [`vm_state_subsections`](crate::vmstate_subsections) macro implementation. +#[repr(transparent)] +pub struct VMStateSubsectionsWrapper(pub &'static [*const crate::bindings::VMStateDescription]); + +unsafe impl Sync for VMStateSubsectionsWrapper {} + +/// Helper macro to declare a list of subsections +/// ([`VMStateDescription`](`crate::bindings::VMStateDescription`)) into a +/// static and return a pointer to the array of pointers it created. +#[macro_export] +macro_rules! vmstate_subsections { + ($($subsection:expr),*$(,)*) => {{ + static _SUBSECTIONS: $crate::vmstate::VMStateSubsectionsWrapper = $crate::vmstate::VMStateSubsectionsWrapper(&[ + $({ + static _SUBSECTION: $crate::bindings::VMStateDescription = $subsection; + ::core::ptr::addr_of!(_SUBSECTION) + }),*, + ::core::ptr::null() + ]); + _SUBSECTIONS.0.as_ptr() + }} +} diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs new file mode 100644 index 0000000000..13cdb2ccba --- /dev/null +++ b/rust/qemu-api/src/zeroable.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::ptr; + +/// Encapsulates the requirement that +/// `MaybeUninit::::zeroed().assume_init()` does not cause undefined +/// behavior. This trait in principle could be implemented as just: +/// +/// ``` +/// const ZERO: Self = unsafe { +/// ::core::mem::MaybeUninit::<$crate::bindings::Property>::zeroed().assume_init() +/// }, +/// ``` +/// +/// The need for a manual implementation is only because `zeroed()` cannot +/// be used as a `const fn` prior to Rust 1.75.0. Once we can assume a new +/// enough version of the compiler, we could provide a `#[derive(Zeroable)]` +/// macro to check at compile-time that all struct fields are Zeroable, and +/// use the above blanket implementation of the `ZERO` constant. +/// +/// # Safety +/// +/// Because the implementation of `ZERO` is manual, it does not make +/// any assumption on the safety of `zeroed()`. However, other users of the +/// trait could use it that way. Do not add this trait to a type unless +/// all-zeroes is a valid value for the type. In particular, remember that +/// raw pointers can be zero, but references and `NonNull` cannot +pub unsafe trait Zeroable: Default { + const ZERO: Self; +} + +unsafe impl Zeroable for crate::bindings::Property__bindgen_ty_1 { + const ZERO: Self = Self { i: 0 }; +} + +unsafe impl Zeroable for crate::bindings::Property { + const ZERO: Self = Self { + name: ptr::null(), + info: ptr::null(), + offset: 0, + bitnr: 0, + bitmask: 0, + set_default: false, + defval: Zeroable::ZERO, + arrayoffset: 0, + arrayinfo: ptr::null(), + arrayfieldsize: 0, + link_type: ptr::null(), + }; +} + +unsafe impl Zeroable for crate::bindings::VMStateDescription { + const ZERO: Self = Self { + name: ptr::null(), + unmigratable: false, + early_setup: false, + version_id: 0, + minimum_version_id: 0, + priority: crate::bindings::MigrationPriority::MIG_PRI_DEFAULT, + pre_load: None, + post_load: None, + pre_save: None, + post_save: None, + needed: None, + dev_unplug_pending: None, + fields: ptr::null(), + subsections: ptr::null(), + }; +} + +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_1 { + const ZERO: Self = Self { + min_access_size: 0, + max_access_size: 0, + unaligned: false, + accepts: None, + }; +} + +unsafe impl Zeroable for crate::bindings::MemoryRegionOps__bindgen_ty_2 { + const ZERO: Self = Self { + min_access_size: 0, + max_access_size: 0, + unaligned: false, + }; +} diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs new file mode 100644 index 0000000000..43a4827de1 --- /dev/null +++ b/rust/qemu-api/tests/tests.rs @@ -0,0 +1,79 @@ +// Copyright 2024, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ffi::CStr, os::raw::c_void}; + +use qemu_api::{ + bindings::*, + c_str, declare_properties, define_property, + definitions::{Class, ObjectImpl}, + device_class, device_class_init, + zeroable::Zeroable, +}; + +#[test] +fn test_device_decl_macros() { + // Test that macros can compile. + pub static VMSTATE: VMStateDescription = VMStateDescription { + name: c_str!("name").as_ptr(), + unmigratable: true, + ..Zeroable::ZERO + }; + + #[derive(qemu_api_macros::offsets)] + #[repr(C)] + #[derive(qemu_api_macros::Object)] + pub struct DummyState { + pub _parent: DeviceState, + pub migrate_clock: bool, + } + + #[repr(C)] + pub struct DummyClass { + pub _parent: DeviceClass, + } + + declare_properties! { + DUMMY_PROPERTIES, + define_property!( + c_str!("migrate-clk"), + DummyState, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool + ), + } + + device_class_init! { + dummy_class_init, + props => DUMMY_PROPERTIES, + realize_fn => None, + legacy_reset_fn => None, + vmsd => VMSTATE, + } + + impl ObjectImpl for DummyState { + type Class = DummyClass; + const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self }; + const TYPE_NAME: &'static CStr = c_str!("dummy"); + const PARENT_TYPE_NAME: Option<&'static CStr> = Some(device_class::TYPE_DEVICE); + const ABSTRACT: bool = false; + const INSTANCE_INIT: Option = None; + const INSTANCE_POST_INIT: Option = None; + const INSTANCE_FINALIZE: Option = None; + } + + impl Class for DummyClass { + const CLASS_INIT: Option = + Some(dummy_class_init); + const CLASS_BASE_INIT: Option< + unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + > = None; + } + + unsafe { + module_call_init(module_init_type::MODULE_INIT_QOM); + object_unref(object_new(DummyState::TYPE_NAME.as_ptr()) as *mut _); + } +} diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml new file mode 100644 index 0000000000..ebecb99fe0 --- /dev/null +++ b/rust/rustfmt.toml @@ -0,0 +1,7 @@ +edition = "2021" +format_generated_files = false +format_code_in_doc_comments = true +format_strings = true +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +wrap_comments = true diff --git a/rust/wrapper.h b/rust/wrapper.h new file mode 100644 index 0000000000..285d0eb6ad --- /dev/null +++ b/rust/wrapper.h @@ -0,0 +1,64 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2024 Linaro Ltd. + * + * Authors: Manos Pitsidianakis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +/* + * This header file is meant to be used as input to the `bindgen` application + * in order to generate C FFI compatible Rust bindings. + */ + +#ifndef __CLANG_STDATOMIC_H +#define __CLANG_STDATOMIC_H +/* + * Fix potential missing stdatomic.h error in case bindgen does not insert the + * correct libclang header paths on its own. We do not use stdatomic.h symbols + * in QEMU code, so it's fine to declare dummy types instead. + */ +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst, +} memory_order; +#endif /* __CLANG_STDATOMIC_H */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu-io.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "exec/memory.h" +#include "chardev/char-fe.h" +#include "hw/clock.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/irq.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "chardev/char-serial.h" 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-inclusions b/scripts/analyze-inclusions index 45c821de32..b6280f25c8 100644 --- a/scripts/analyze-inclusions +++ b/scripts/analyze-inclusions @@ -92,7 +92,7 @@ echo trace/generated-tracers.h: analyze -include ../include/qemu/osdep.h trace/generated-tracers.h echo target/i386/cpu.h: -analyze -DNEED_CPU_H -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../target/i386/cpu.h +analyze -DCOMPILING_PER_TARGET -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../target/i386/cpu.h -echo hw/hw.h + NEED_CPU_H: -analyze -DNEED_CPU_H -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../include/hw/hw.h +echo hw/hw.h + COMPILING_PER_TARGET: +analyze -DCOMPILING_PER_TARGET -I../target/i386 -Ii386-softmmu -include ../include/qemu/osdep.h ../include/hw/hw.h 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 b319d5bf61..1afccbf141 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -26,16 +26,18 @@ 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 meson ui/keycodemapdb" -submodules="$submodules tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" - -# xemu extras -submodules="$submodules ui/thirdparty/imgui ui/thirdparty/implot genconfig" - -subprojects="glslang SPIRV-Reflect volk VulkanMemoryAllocator nv2a_vsh_cpu tomlplusplus cpp-httplib xxhash" - +subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 + berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs + syn-2-rs unicode-ident-1-rs" sub_deinit="" +# xemu only +subbprojects="keycodemapdb berkeley-softfloat-3" +subprojects="glslang SPIRV-Reflect volk VulkanMemoryAllocator + nv2a_vsh_cpu tomlplusplus cpp-httplib xxhash imgui implot genconfig" + function cleanup() { local status=$? rm -rf "$sub_tdir" @@ -55,39 +57,37 @@ function tree_ish() { echo "$retval" } +function subproject_dir() { + if test ! -f "subprojects/$1.wrap"; then + error "scripts/archive-source.sh should only process wrap subprojects" + fi + + # Print the directory key of the wrap file, defaulting to the + # subproject name. The wrap file is in ini format and should + # have a single section only. There should be only one section + # named "[wrap-*]", which helps keeping the script simple. + local dir + dir=$(sed -n \ + -e '/^\[wrap-[a-z][a-z]*\]$/,/^\[/{' \ + -e '/^directory *= */!b' \ + -e 's///p' \ + -e 'q' \ + -e '}' \ + "subprojects/$1.wrap") + + echo "${dir:-$1}" +} + git archive --format tar "$(tree_ish)" > "$tar_file" test $? -ne 0 && error "failed to archive qemu" for sp in $subprojects; do meson subprojects download $sp - sp_dir=$(grep -oP '^directory = \K.*' subprojects/${sp}.wrap || echo ${sp}) # test $? -ne 0 && error "failed to download subproject $sp" - tar --append --file "$tar_file" --exclude=.git subprojects/$sp_dir + tar --append --file "$tar_file" --exclude=.git subprojects/"$(subproject_dir $sp)" test $? -ne 0 && error "failed to append subproject $sp to $tar_file" done -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 rev-parse HEAD 2>/dev/null >HEAD) - (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" - tar --append --file "$tar_file" "$sm"/HEAD -done - git rev-parse HEAD 2>/dev/null | tr -d '\n' > XEMU_COMMIT git symbolic-ref --short HEAD > XEMU_BRANCH git describe --tags --match 'v*' | cut -c 2- | tr -d '\n' > XEMU_VERSION 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 6ecabfb2b5..06d07e6c22 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 = ( @@ -377,8 +435,8 @@ if ($chk_branch) { my @patches; my %git_commits = (); my $HASH; - open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--format=%H %s", $ARGV[0]) || - die "$P: git log --reverse --no-merges --format='%H %s' $ARGV[0] failed - $!\n"; + open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--no-mailmap", "--format=%H %s", $ARGV[0]) || + die "$P: git log --reverse --no-merges --no-mailmap --format='%H %s' $ARGV[0] failed - $!\n"; for my $line (<$HASH>) { $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; @@ -402,7 +460,7 @@ if ($chk_branch) { "-c", "diff.renamelimit=0", "-c", "diff.renames=True", "-c", "diff.algorithm=histogram", - "show", + "show", "--no-mailmap", "--patch-with-stat", $hash) || die "$P: git show $hash - $!\n"; while (<$FILE>) { @@ -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) = @_; @@ -1304,6 +1374,9 @@ sub process { my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch my $reported_maintainer_file = 0; + my $reported_mixing_imported_file = 0; + my $in_imported_file = 0; + my $in_no_imported_file = 0; my $non_utf8_charset = 0; our @report = (); @@ -1503,7 +1576,7 @@ sub process { $is_patch = 1; } - if ($line =~ /^(Author|From): .* via .*/) { + if ($line =~ /^(Author|From): .* via .*/) { ERROR("Author email address is mangled by the mailing list\n" . $herecurr); } @@ -1585,9 +1658,45 @@ 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 =~ /^-/); +# Check that updating imported files from Linux are not mixed with other changes + if ($realfile =~ /^(linux-headers|include\/standard-headers)\//) { + if (!$in_imported_file) { + WARN("added, moved or deleted file(s) " . + "imported from Linux, are you using " . + "scripts/update-linux-headers.sh?\n" . + $herecurr); + } + $in_imported_file = 1; + } else { + $in_no_imported_file = 1; + } + + if (!$reported_mixing_imported_file && + $in_imported_file && $in_no_imported_file) { + ERROR("headers imported from Linux should be self-" . + "contained in a patch with no other changes\n" . + $herecurr); + $reported_mixing_imported_file = 1; + } + # ignore files that are being periodically imported from Linux next if ($realfile =~ /^(linux-headers|include\/standard-headers)\//); @@ -1621,7 +1730,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; @@ -2137,7 +2246,7 @@ sub process { } } # Check operator spacing. - if (!($line=~/\#\s*include/)) { + if (!($line=~/\#\s*(include|import)/)) { my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| @@ -2865,6 +2974,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); @@ -2982,6 +3099,16 @@ 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); + } + if ($line =~ /\b(g_)?assert\(false\)/) { + ERROR("use g_assert_not_reached() instead of assert(false)\n" . + $herecurr); + } + if ($line =~ /\bstrerrorname_np\(/) { + ERROR("use strerror() instead of strerrorname_np()\n" . $herecurr); + } my $non_exit_glib_asserts = qr{g_assert_cmpstr| g_assert_cmpint| g_assert_cmpuint| diff --git a/scripts/ci/gitlab-ci-section b/scripts/ci/gitlab-ci-section new file mode 100644 index 0000000000..9bbe80420d --- /dev/null +++ b/scripts/ci/gitlab-ci-section @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Linaro Ltd +# SPDX-License-Identifier: GPL-2.0-or-later + +# gitlab-ci-section: This is a shell script fragment which defines +# functions section_start and section_end which will emit marker lines +# that GitLab will interpret as the beginning or end of a "collapsible +# section" in a CI job log. See +# https://docs.gitlab.com/ee/ci/yaml/script.html#expand-and-collapse-job-log-sections +# +# This is intended to be sourced in the before_script section of +# a CI config; the section_start and section_end functions will +# then be available for use in the before_script and script sections. + +# Section names are [-_.A-Za-z0-9] and the section_start pairs with +# a section_end with the same section name. +# The description can be any printable text without newlines; this is +# what will appear in the log. + +# Usage: +# section_start section_name "Description of the section" +section_start () { + printf "section_start:%s:%s\r\e[0K%s\n" "$(date +%s)" "$1" "$2" +} + +# Usage: +# section_end section_name +section_end () { + printf "section_end:%s:%s\r\e[0K\n" "$(date +%s)" "$1" +} 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 deleted file mode 100644 index 42b0471634..0000000000 --- a/scripts/ci/org.centos/stream/8/build-environment.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -- name: Installation of extra packages to build QEMU - hosts: all - tasks: - - name: Extra check for CentOS Stream 8 - lineinfile: - path: /etc/redhat-release - line: CentOS Stream release 8 - state: present - check_mode: yes - register: centos_stream_8 - - - name: Enable PowerTools repo on CentOS Stream 8 - ini_file: - path: /etc/yum.repos.d/CentOS-Stream-PowerTools.repo - section: powertools - 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: - - device-mapper-multipath-devel - - glusterfs-api-devel - - gnutls-devel - - libcap-ng-devel - - libcurl-devel - - libfdt-devel - - libiscsi-devel - - libpmem-devel - - librados-devel - - librbd-devel - - libseccomp-devel - - libssh-devel - - libxkbcommon-devel - - ninja-build - - numactl-devel - - python3-sphinx - - redhat-rpm-config - - snappy-devel - - spice-server-devel - - systemd-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 deleted file mode 100755 index a7f92aff90..0000000000 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ /dev/null @@ -1,203 +0,0 @@ -#!/bin/sh -e -# -# Configuration for QEMU based on CentOS Stream 8 x86_64 builds -# -# The "configure" command line is based on: -# -# https://git.centos.org/rpms/qemu-kvm/blob/c8s-stream-rhel/f/SPECS/qemu-kvm.spec -# -# But, because the SPEC file contains a number of conditionals and -# variable and expansions only available at RPM build time, this version -# was initially generated from an actual RPM build on an x86_64 platform. -# -# From that initial version, options that are required or are a -# consequence of non-upstream patches have been adapted. One example -# is "--without-default-devices" which is *not* present here, given -# that patches adding downstream specific devices are not available. -# -../configure \ ---prefix="/usr" \ ---libdir="/usr/lib64" \ ---datadir="/usr/share" \ ---sysconfdir="/etc" \ ---interp-prefix=/usr/qemu-%M \ ---localstatedir="/var" \ ---docdir="/usr/share/doc" \ ---libexecdir="/usr/libexec" \ ---extra-ldflags="-Wl,--build-id -Wl,-z,relro -Wl,-z,now" \ ---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-attr \ ---disable-auth-pam \ ---disable-avx2 \ ---disable-avx512f \ ---disable-bochs \ ---disable-bpf \ ---disable-brlapi \ ---disable-bsd-user \ ---disable-bzip2 \ ---disable-cap-ng \ ---disable-capstone \ ---disable-cfi \ ---disable-cfi-debug \ ---disable-cloop \ ---disable-cocoa \ ---disable-coroutine-pool \ ---disable-crypto-afalg \ ---disable-curl \ ---disable-curses \ ---disable-debug-info \ ---disable-debug-mutex \ ---disable-debug-tcg \ ---disable-dmg \ ---disable-docs \ ---disable-fuse \ ---disable-fuse-lseek \ ---disable-gcrypt \ ---disable-gio \ ---disable-glusterfs \ ---disable-gnutls \ ---disable-gtk \ ---disable-guest-agent \ ---disable-guest-agent-msi \ ---disable-hax \ ---disable-hvf \ ---disable-iconv \ ---disable-kvm \ ---disable-libdaxctl \ ---disable-libiscsi \ ---disable-libnfs \ ---disable-libpmem \ ---disable-libssh \ ---disable-libudev \ ---disable-libusb \ ---disable-linux-aio \ ---disable-linux-io-uring \ ---disable-linux-user \ ---disable-live-block-migration \ ---disable-lto \ ---disable-lzfse \ ---disable-lzo \ ---disable-malloc-trim \ ---disable-membarrier \ ---disable-modules \ ---disable-module-upgrades \ ---disable-mpath \ ---disable-multiprocess \ ---disable-netmap \ ---disable-nettle \ ---disable-numa \ ---disable-nvmm \ ---disable-opengl \ ---disable-parallels \ ---disable-pie \ ---disable-pvrdma \ ---disable-qcow1 \ ---disable-qed \ ---disable-qom-cast-debug \ ---disable-rbd \ ---disable-rdma \ ---disable-replication \ ---disable-rng-none \ ---disable-safe-stack \ ---disable-sanitizers \ ---disable-sdl \ ---disable-sdl-image \ ---disable-seccomp \ ---disable-slirp-smbd \ ---disable-smartcard \ ---disable-snappy \ ---disable-sparse \ ---disable-spice \ ---disable-strip \ ---disable-system \ ---disable-tcg \ ---disable-tools \ ---disable-tpm \ ---disable-u2f \ ---disable-usb-redir \ ---disable-user \ ---disable-vde \ ---disable-vdi \ ---disable-vhost-crypto \ ---disable-vhost-kernel \ ---disable-vhost-net \ ---disable-vhost-user \ ---disable-vhost-user-blk-server \ ---disable-vhost-vdpa \ ---disable-virglrenderer \ ---disable-virtfs \ ---disable-virtiofsd \ ---disable-vnc \ ---disable-vnc-jpeg \ ---disable-png \ ---disable-vnc-sasl \ ---disable-vte \ ---disable-vvfat \ ---disable-werror \ ---disable-whpx \ ---disable-xen \ ---disable-xen-pci-passthrough \ ---disable-xkbcommon \ ---disable-zstd \ ---enable-attr \ ---enable-avx2 \ ---enable-cap-ng \ ---enable-capstone \ ---enable-coroutine-pool \ ---enable-curl \ ---enable-debug-info \ ---enable-docs \ ---enable-fdt \ ---enable-gcrypt \ ---enable-glusterfs \ ---enable-gnutls \ ---enable-guest-agent \ ---enable-iconv \ ---enable-kvm \ ---enable-libiscsi \ ---enable-libpmem \ ---enable-libssh \ ---enable-libusb \ ---enable-libudev \ ---enable-linux-aio \ ---enable-lzo \ ---enable-malloc-trim \ ---enable-modules \ ---enable-mpath \ ---enable-numa \ ---enable-opengl \ ---enable-pie \ ---enable-rbd \ ---enable-rdma \ ---enable-seccomp \ ---enable-snappy \ ---enable-smartcard \ ---enable-spice \ ---enable-system \ ---enable-tcg \ ---enable-tools \ ---enable-tpm \ ---enable-trace-backend=dtrace \ ---enable-usb-redir \ ---enable-virtiofsd \ ---enable-vhost-kernel \ ---enable-vhost-net \ ---enable-vhost-user \ ---enable-vhost-user-blk-server \ ---enable-vhost-vdpa \ ---enable-vnc \ ---enable-png \ ---enable-vnc-sasl \ ---enable-werror \ ---enable-xkbcommon diff --git a/scripts/ci/org.centos/stream/8/x86_64/test-avocado b/scripts/ci/org.centos/stream/8/x86_64/test-avocado deleted file mode 100755 index 7aeecbcfb8..0000000000 --- a/scripts/ci/org.centos/stream/8/x86_64/test-avocado +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/sh -e -# -# Runs a previously vetted list of tests, either marked explicitly for -# 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 \ -# --filter-by-tags-include-empty-key -t accel:kvm,arch:x86_64 \ -# tests/avocado/ -# -# This is almost the complete list of avocado based tests available at -# the time this was compile, with the following exceptions: -# -# * 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 -# -# * Test is marked (unconditionally) to be skipped: -# - tests/avocado/virtio_check_params.py:VirtioMaxSegSettingsCheck.test_machine_types -# -make get-vm-images -./tests/venv/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 \ - tests/avocado/boot_linux_console.py:BootLinuxConsole.test_x86_64_pc \ - tests/avocado/cpu_queries.py:QueryCPUModelExpansion.test \ - tests/avocado/empty_cpu_model.py:EmptyCPUModel.test \ - tests/avocado/hotplug_cpu.py:HotPlugCPU.test \ - 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 \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_strict \ - tests/avocado/intel_iommu.py:IntelIOMMU.test_intel_iommu_strict_cm \ - tests/avocado/linux_initrd.py:LinuxInitrd.test_with_2gib_file_should_exit_error_msg_with_linux_v3_6 \ - tests/avocado/linux_initrd.py:LinuxInitrd.test_with_2gib_file_should_work_with_linux_v4_16 \ - tests/avocado/migration.py:Migration.test_migration_with_exec \ - tests/avocado/migration.py:Migration.test_migration_with_tcp_localhost \ - tests/avocado/migration.py:Migration.test_migration_with_unix \ - tests/avocado/pc_cpu_hotplug_props.py:OmittedCPUProps.test_no_die_id \ - tests/avocado/replay_kernel.py:ReplayKernelNormal.test_x86_64_pc \ - tests/avocado/reverse_debugging.py:ReverseDebugging_X86_64.test_x86_64_pc \ - tests/avocado/version.py:Version.test_qmp_human_info_version \ - tests/avocado/virtio_version.py:VirtioVersionCheck.test_conventional_devs \ - tests/avocado/virtio_version.py:VirtioVersionCheck.test_modern_only_devs \ - tests/avocado/vnc.py:Vnc.test_change_password \ - tests/avocado/vnc.py:Vnc.test_change_password_requires_a_password \ - tests/avocado/vnc.py:Vnc.test_no_vnc \ - tests/avocado/vnc.py:Vnc.test_no_vnc_change_password \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_4_1 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_set_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_unset_4_1 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v1_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v1_set_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v2_4_0 \ - tests/avocado/x86_cpu_model_versions.py:CascadelakeArchCapabilities.test_v2_unset_4_1 \ - tests/avocado/x86_cpu_model_versions.py:X86CPUModelAliases.test_4_0_alias_compatibility \ - tests/avocado/x86_cpu_model_versions.py:X86CPUModelAliases.test_4_1_alias \ - tests/avocado/x86_cpu_model_versions.py:X86CPUModelAliases.test_none_alias diff --git a/scripts/ci/org.centos/stream/README b/scripts/ci/org.centos/stream/README deleted file mode 100644 index e3eadfe3ea..0000000000 --- a/scripts/ci/org.centos/stream/README +++ /dev/null @@ -1,17 +0,0 @@ -This directory contains scripts for generating a build of QEMU that -closely matches the CentOS Stream[1] builds of the qemu-kvm package. - -To have the environment ready to configure, build QEMU and run tests, -please start with a CentOS Stream machine and: - - * apply the generic "build-environment.yml" playbook located at - scripts/ci/setup - - * apply the "build-environment.yml" in the directory following the - CentOS Stream version (such as "8"). - -This currently only covers CentOS Stream 8 environments and -packages[2]. - -[1] https://www.centos.org/centos-stream/ -[2] https://git.centos.org/rpms/qemu-kvm/commits/c8s-stream-rhel diff --git a/scripts/ci/setup/build-environment.yml b/scripts/ci/setup/build-environment.yml deleted file mode 100644 index b04c2b7cee..0000000000 --- a/scripts/ci/setup/build-environment.yml +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. -# -# This is an ansible playbook file. Run it to set up systems with the -# environment needed to build QEMU. ---- -- name: Installation of basic packages to build QEMU - hosts: all - tasks: - - name: Check for suitable ansible version - delegate_to: localhost - assert: - that: - - '((ansible_version.major == 2) and (ansible_version.minor >= 8)) or (ansible_version.major >= 3)' - msg: "Unsuitable ansible version, please use version 2.8.0 or later" - - - name: Add armhf foreign architecture to aarch64 hosts - command: dpkg --add-architecture armhf - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['architecture'] == 'aarch64' - - ansible_facts['distribution_version'] == '20.04' - - - name: Update apt cache / upgrade packages via apt - apt: - update_cache: yes - upgrade: yes - when: - - ansible_facts['distribution'] == 'Ubuntu' - - - name: Install basic packages to build QEMU on Ubuntu 20.04 - package: - name: - - ccache - - gcc - - gettext - - git - - glusterfs-common - - libaio-dev - - libattr1-dev - - libbrlapi-dev - - libbz2-dev - - libcacard-dev - - libcap-ng-dev - - libcurl4-gnutls-dev - - libdrm-dev - - libepoxy-dev - - libfdt-dev - - libgbm-dev - - libgtk-3-dev - - libibverbs-dev - - libiscsi-dev - - libjemalloc-dev - - libjpeg-turbo8-dev - - liblzo2-dev - - libncurses5-dev - - libncursesw5-dev - - libnfs-dev - - libnss3-dev - - libnuma-dev - - libpixman-1-dev - - librados-dev - - librbd-dev - - librdmacm-dev - - libsasl2-dev - - libsdl2-dev - - libseccomp-dev - - libsnappy-dev - - libspice-protocol-dev - - libssh-dev - - libusb-1.0-0-dev - - libusbredirhost-dev - - libvdeplug-dev - - libvte-2.91-dev - - libzstd-dev - - make - - python3-yaml - - python3-sphinx - - python3-sphinx-rtd-theme - - ninja-build - - sparse - - xfslibs-dev - state: present - when: - - ansible_facts['distribution'] == 'Ubuntu' - - - 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'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' - - - 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 - package: - name: - - binutils-arm-linux-gnueabihf - - gcc-arm-linux-gnueabihf - - libblkid-dev:armhf - - libc6-dev:armhf - - libffi-dev:armhf - - libglib2.0-dev:armhf - - libmount-dev:armhf - - libpcre2-dev:armhf - - libpixman-1-dev:armhf - - zlib1g-dev:armhf - when: - - ansible_facts['distribution'] == 'Ubuntu' - - ansible_facts['distribution_version'] == '20.04' - - ansible_facts['architecture'] == 'aarch64' - - - name: Install basic packages to build QEMU on EL8 - dnf: - # This list of packages start with tests/docker/dockerfiles/centos8.docker - # but only include files that are common to all distro variants and present - # in the standard repos (no add-ons) - name: - - bzip2 - - bzip2-devel - - dbus-daemon - - diffutils - - gcc - - gcc-c++ - - genisoimage - - gettext - - git - - glib2-devel - - libaio-devel - - libepoxy-devel - - libgcrypt-devel - - lzo-devel - - make - - mesa-libEGL-devel - - nettle-devel - - ninja-build - - nmap-ncat - - perl-Test-Harness - - pixman-devel - - python36 - - rdma-core-devel - - spice-glib-devel - - systemtap-sdt-devel - - tar - - zlib-devel - state: present - when: - - ansible_facts['distribution_file_variety'] == 'RedHat' - - ansible_facts['distribution_version'] == '8' - - - name: Install packages only available on x86 and aarch64 - dnf: - # Spice server not available in ppc64le - name: - - spice-server - state: present - when: - - ansible_facts['distribution_file_variety'] == 'RedHat' - - ansible_facts['distribution_version'] == '8' - - ansible_facts['architecture'] == 'aarch64' or ansible_facts['architecture'] == 'x86_64' diff --git a/scripts/ci/setup/gitlab-runner.yml b/scripts/ci/setup/gitlab-runner.yml index 33128be85d..57e7faebf1 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,64 @@ - debug: msg: gitlab-runner arch is {{ gitlab_runner_arch }} - - name: Download the matching gitlab-runner + # Debian/Ubuntu setup + - name: Get gitlab-runner repo setup script (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://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" + mode: 0755 + when: + - ansible_facts['distribution'] == 'Ubuntu' + - name: Run gitlab-runner repo setup script (DEB) + shell: "/root/script.deb.sh" + when: + - ansible_facts['distribution'] == 'Ubuntu' + + - name: Install gitlab-runner (DEB) + ansible.builtin.apt: + name: gitlab-runner + update_cache: yes + state: present + when: + - ansible_facts['distribution'] == 'Ubuntu' + + # RPM setup + - name: Get gitlab-runner repo setup script (RPM) + get_url: + dest: "/root/" + url: "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" + mode: 0755 + when: + - ansible_facts['distribution'] == 'CentOS' + + - name: Run gitlab-runner repo setup script (RPM) + shell: "/root/script.rpm.sh" + when: + - ansible_facts['distribution'] == 'CentOS' + + - name: Install gitlab-runner (RPM) + yum: + name: gitlab-runner + update_cache: yes + state: present + when: + - ansible_facts['distribution'] == 'CentOS' + + # Register Runners - 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 +115,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/ubuntu/build-environment.yml b/scripts/ci/setup/ubuntu/build-environment.yml new file mode 100644 index 0000000000..56b51609e3 --- /dev/null +++ b/scripts/ci/setup/ubuntu/build-environment.yml @@ -0,0 +1,67 @@ +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. +# +# This is an ansible playbook file. Run it to set up systems with the +# environment needed to build QEMU. +--- +- name: Installation of basic packages to build QEMU + hosts: all + tasks: + - name: Check for suitable ansible version + delegate_to: localhost + assert: + that: + - '((ansible_version.major == 2) and (ansible_version.minor >= 8)) or (ansible_version.major >= 3)' + msg: "Unsuitable ansible version, please use version 2.8.0 or later" + + - name: Add armhf foreign architecture to aarch64 hosts + command: dpkg --add-architecture armhf + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['architecture'] == 'aarch64' + + - name: Update apt cache / upgrade packages via apt + apt: + update_cache: yes + upgrade: yes + when: + - ansible_facts['distribution'] == 'Ubuntu' + + # the package lists are updated by "make lcitool-refresh" + - name: Include package lists based on OS and architecture + include_vars: + file: "ubuntu-2204-{{ ansible_facts['architecture'] }}.yaml" + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution_version'] == '22.04' + + - name: Install packages for QEMU on Ubuntu 22.04 + package: + name: "{{ packages }}" + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution_version'] == '22.04' + + - name: Install armhf cross-compile packages to build QEMU on AArch64 Ubuntu 22.04 + package: + name: + - binutils-arm-linux-gnueabihf + - gcc-arm-linux-gnueabihf + - libblkid-dev:armhf + - libc6-dev:armhf + - libffi-dev:armhf + - libglib2.0-dev:armhf + - libmount-dev:armhf + - libpcre2-dev:armhf + - libpixman-1-dev:armhf + - zlib1g-dev:armhf + when: + - ansible_facts['distribution'] == 'Ubuntu' + - ansible_facts['distribution_version'] == '22.04' + - ansible_facts['architecture'] == 'aarch64' + diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml new file mode 100644 index 0000000000..288156d1e4 --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-aarch64.yaml @@ -0,0 +1,129 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables --host-arch aarch64 ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +packages: + - bash + - bc + - bison + - bsdextrautils + - bzip2 + - ca-certificates + - ccache + - clang + - dbus + - debianutils + - diffutils + - exuberant-ctags + - findutils + - flex + - 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 + - libcbor-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 + - libgtk-vnc-2.0-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 + - 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 + - rustc + - sed + - socat + - sparse + - swtpm + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - xorriso + - zlib1g-dev + - zstd + diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml b/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml new file mode 100644 index 0000000000..0cc34cd10b --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-armhf-cross.yml @@ -0,0 +1,127 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables --cross-arch armv7l ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +packages: + - 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-tomli + - python3-venv + - python3-yaml + - rpm2cpio + - sed + - socat + - sparse + - swtpm + - tar + - tesseract-ocr + - tesseract-ocr-eng + - xorriso + - zstd + - 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 + - libjpeg-turbo8-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-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 + - zlib1g-dev:armhf + diff --git a/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml new file mode 100644 index 0000000000..d497139ef3 --- /dev/null +++ b/scripts/ci/setup/ubuntu/ubuntu-2204-s390x.yaml @@ -0,0 +1,127 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool variables --host-arch s390x ubuntu-2204 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +packages: + - bash + - bc + - bison + - bsdextrautils + - bzip2 + - ca-certificates + - ccache + - clang + - dbus + - debianutils + - diffutils + - exuberant-ctags + - findutils + - flex + - 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 + - libcbor-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 + - libgtk-vnc-2.0-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 + - 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 + - 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 + - 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 + - rustc + - sed + - socat + - sparse + - swtpm + - systemtap-sdt-dev + - tar + - tesseract-ocr + - tesseract-ocr-eng + - xorriso + - zlib1g-dev + - zstd + 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-includes b/scripts/clean-includes index d37bd4f692..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)' +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 - <reset = resetfn; ++ device_class_set_legacy_reset(dc, resetfn); +@@ +identifier dc, resetfn; +@@ + DeviceClass *dc; + ... +- dc->reset = &resetfn; ++ device_class_set_legacy_reset(dc, resetfn); diff --git a/scripts/coccinelle/reset-type.cocci b/scripts/coccinelle/reset-type.cocci new file mode 100644 index 0000000000..14abdd7bd0 --- /dev/null +++ b/scripts/coccinelle/reset-type.cocci @@ -0,0 +1,133 @@ +// Convert device code using three-phase reset to add a ResetType +// argument to implementations of ResettableHoldPhase and +// ResettableEnterPhase methods. +// +// Copyright Linaro Ltd 2024 +// SPDX-License-Identifier: GPL-2.0-or-later +// +// for dir in include hw target; do \ +// spatch --macro-file scripts/cocci-macro-file.h \ +// --sp-file scripts/coccinelle/reset-type.cocci \ +// --keep-comments --smpl-spacing --in-place --include-headers \ +// --dir $dir; done +// +// This coccinelle script aims to produce a complete change that needs +// no human interaction, so as well as the generic "update device +// implementations of the hold and exit phase methods" it includes +// the special-case transformations needed for the core code and for +// one device model that does something a bit nonstandard. Those +// special cases are at the end of the file. + +// Look for where we use a function as a ResettableHoldPhase method, +// either by directly assigning it to phases.hold or by calling +// resettable_class_set_parent_phases, and remember the function name. +@ holdfn_assigned @ +identifier enterfn, holdfn, exitfn; +identifier rc; +expression e; +@@ +ResettableClass *rc; +... +( + rc->phases.hold = holdfn; +| + resettable_class_set_parent_phases(rc, enterfn, holdfn, exitfn, e); +) + +// Look for the definition of the function we found in holdfn_assigned, +// and add the new argument. If the function calls a hold function +// itself (probably chaining to the parent class reset) then add the +// new argument there too. +@ holdfn_defined @ +identifier holdfn_assigned.holdfn; +typedef Object; +identifier obj; +expression parent; +@@ +-holdfn(Object *obj) ++holdfn(Object *obj, ResetType type) +{ + <... +- parent.hold(obj) ++ parent.hold(obj, type) + ...> +} + +// Similarly for ResettableExitPhase. +@ exitfn_assigned @ +identifier enterfn, holdfn, exitfn; +identifier rc; +expression e; +@@ +ResettableClass *rc; +... +( + rc->phases.exit = exitfn; +| + resettable_class_set_parent_phases(rc, enterfn, holdfn, exitfn, e); +) +@ exitfn_defined @ +identifier exitfn_assigned.exitfn; +typedef Object; +identifier obj; +expression parent; +@@ +-exitfn(Object *obj) ++exitfn(Object *obj, ResetType type) +{ + <... +- parent.exit(obj) ++ parent.exit(obj, type) + ...> +} + +// SPECIAL CASES ONLY BELOW HERE +// We use a python scripted constraint on the position of the match +// to ensure that they only match in a particular function. See +// https://public-inbox.org/git/alpine.DEB.2.21.1808240652370.2344@hadrien/ +// which recommends this as the way to do "match only in this function". + +// Special case: isl_pmbus_vr.c has some reset methods calling others directly +@ isl_pmbus_vr @ +identifier obj; +@@ +- isl_pmbus_vr_exit_reset(obj); ++ isl_pmbus_vr_exit_reset(obj, type); + +// Special case: device_phases_reset() needs to pass RESET_TYPE_COLD +@ device_phases_reset_hold @ +expression obj; +identifier rc; +identifier phase; +position p : script:python() { p[0].current_element == "device_phases_reset" }; +@@ +- rc->phases.phase(obj)@p ++ rc->phases.phase(obj, RESET_TYPE_COLD) + +// Special case: in resettable_phase_hold() and resettable_phase_exit() +// we need to pass through the ResetType argument to the method being called +@ resettable_phase_hold @ +expression obj; +identifier rc; +position p : script:python() { p[0].current_element == "resettable_phase_hold" }; +@@ +- rc->phases.hold(obj)@p ++ rc->phases.hold(obj, type) +@ resettable_phase_exit @ +expression obj; +identifier rc; +position p : script:python() { p[0].current_element == "resettable_phase_exit" }; +@@ +- rc->phases.exit(obj)@p ++ rc->phases.exit(obj, type) +// Special case: the typedefs for the methods need to declare the new argument +@ phase_typedef_hold @ +identifier obj; +@@ +- typedef void (*ResettableHoldPhase)(Object *obj); ++ typedef void (*ResettableHoldPhase)(Object *obj, ResetType type); +@ phase_typedef_exit @ +identifier obj; +@@ +- typedef void (*ResettableExitPhase)(Object *obj); ++ typedef void (*ResettableExitPhase)(Object *obj, ResetType type); diff --git a/scripts/coccinelle/return_directly.cocci b/scripts/coccinelle/return_directly.cocci index 4cf50e75ea..6cb1b3c99a 100644 --- a/scripts/coccinelle/return_directly.cocci +++ b/scripts/coccinelle/return_directly.cocci @@ -11,9 +11,8 @@ identifier F; - T VAR; ... when != VAR -- VAR = -+ return - E; +- VAR = (E); - return VAR; ++ return E; ... when != VAR } diff --git a/scripts/codeconverter/codeconverter/qom_macros.py b/scripts/codeconverter/codeconverter/qom_macros.py index 2d2f2055a3..2b0c8224a1 100644 --- a/scripts/codeconverter/codeconverter/qom_macros.py +++ b/scripts/codeconverter/codeconverter/qom_macros.py @@ -142,7 +142,7 @@ class FullStructTypedefMatch(TypedefMatch): return name def strip_typedef(self) -> 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/compare-machine-types.py b/scripts/compare-machine-types.py new file mode 100755 index 0000000000..2af3995eb8 --- /dev/null +++ b/scripts/compare-machine-types.py @@ -0,0 +1,486 @@ +#!/usr/bin/env python3 +# +# Script to compare machine type compatible properties (include/hw/boards.h). +# compat_props are applied to the driver during initialization to change +# default values, for instance, to maintain compatibility. +# This script constructs table with machines and values of their compat_props +# to compare and to find places for improvements or places with bugs. If +# during the comparison, some machine type doesn't have a property (it is in +# the comparison table because another machine type has it), then the +# appropriate method will be used to obtain the default value of this driver +# property via qmp command (e.g. query-cpu-model-expansion for x86_64-cpu). +# These methods are defined below in qemu_property_methods. +# +# Copyright (c) Yandex Technologies LLC, 2023 +# +# 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 +from os import path +from argparse import ArgumentParser, RawTextHelpFormatter, Namespace +import pandas as pd +from contextlib import ExitStack +from typing import Optional, List, Dict, Generator, Tuple, Union, Any, Set + +try: + qemu_dir = path.abspath(path.dirname(path.dirname(__file__))) + sys.path.append(path.join(qemu_dir, 'python')) + from qemu.machine import QEMUMachine +except ModuleNotFoundError as exc: + print(f"Module '{exc.name}' not found.") + print("Try export PYTHONPATH=top-qemu-dir/python or run from top-qemu-dir") + sys.exit(1) + + +default_qemu_args = '-enable-kvm -machine none' +default_qemu_binary = 'build/qemu-system-x86_64' + + +# Methods for gettig the right values of drivers properties +# +# Use these methods as a 'whitelist' and add entries only if necessary. It's +# important to be stable and predictable in analysis and tests. +# Be careful: +# * Class must be inherited from 'QEMUObject' and used in new_driver() +# * Class has to implement get_prop method in order to get values +# * Specialization always wins (with the given classes for 'device' and +# 'x86_64-cpu', method of 'x86_64-cpu' will be used for '486-x86_64-cpu') + +class Driver(): + def __init__(self, vm: QEMUMachine, name: str, abstract: bool) -> None: + self.vm = vm + self.name = name + self.abstract = abstract + self.parent: Optional[Driver] = None + self.property_getter: Optional[Driver] = None + + def get_prop(self, driver: str, prop: str) -> str: + if self.property_getter: + return self.property_getter.get_prop(driver, prop) + else: + return 'Unavailable method' + + def is_child_of(self, parent: 'Driver') -> bool: + """Checks whether self is (recursive) child of @parent""" + cur_parent = self.parent + while cur_parent: + if cur_parent is parent: + return True + cur_parent = cur_parent.parent + + return False + + def set_implementations(self, implementations: List['Driver']) -> None: + self.implementations = implementations + + +class QEMUObject(Driver): + def __init__(self, vm: QEMUMachine, name: str) -> None: + super().__init__(vm, name, True) + + def set_implementations(self, implementations: List[Driver]) -> None: + self.implementations = implementations + + # each implementation of the abstract driver has to use property getter + # of this abstract driver unless it has specialization. (e.g. having + # 'device' and 'x86_64-cpu', property getter of 'x86_64-cpu' will be + # used for '486-x86_64-cpu') + for impl in implementations: + if not impl.property_getter or\ + self.is_child_of(impl.property_getter): + impl.property_getter = self + + +class QEMUDevice(QEMUObject): + def __init__(self, vm: QEMUMachine) -> None: + super().__init__(vm, 'device') + self.cached: Dict[str, List[Dict[str, Any]]] = {} + + def get_prop(self, driver: str, prop_name: str) -> str: + if driver not in self.cached: + self.cached[driver] = self.vm.cmd('device-list-properties', + typename=driver) + for prop in self.cached[driver]: + if prop['name'] == prop_name: + return str(prop.get('default-value', 'No default value')) + + return 'Unknown property' + + +class QEMUx86CPU(QEMUObject): + def __init__(self, vm: QEMUMachine) -> None: + super().__init__(vm, 'x86_64-cpu') + self.cached: Dict[str, Dict[str, Any]] = {} + + def get_prop(self, driver: str, prop_name: str) -> str: + if not driver.endswith('-x86_64-cpu'): + return 'Wrong x86_64-cpu name' + + # crop last 11 chars '-x86_64-cpu' + name = driver[:-11] + if name not in self.cached: + self.cached[name] = self.vm.cmd( + 'query-cpu-model-expansion', type='full', + model={'name': name})['model']['props'] + return str(self.cached[name].get(prop_name, 'Unknown property')) + + +# Now it's stub, because all memory_backend types don't have default values +# but this behaviour can be changed +class QEMUMemoryBackend(QEMUObject): + def __init__(self, vm: QEMUMachine) -> None: + super().__init__(vm, 'memory-backend') + self.cached: Dict[str, List[Dict[str, Any]]] = {} + + def get_prop(self, driver: str, prop_name: str) -> str: + if driver not in self.cached: + self.cached[driver] = self.vm.cmd('qom-list-properties', + typename=driver) + for prop in self.cached[driver]: + if prop['name'] == prop_name: + return str(prop.get('default-value', 'No default value')) + + return 'Unknown property' + + +def new_driver(vm: QEMUMachine, name: str, is_abstr: bool) -> Driver: + if name == 'object': + return QEMUObject(vm, 'object') + elif name == 'device': + return QEMUDevice(vm) + elif name == 'x86_64-cpu': + return QEMUx86CPU(vm) + elif name == 'memory-backend': + return QEMUMemoryBackend(vm) + else: + return Driver(vm, name, is_abstr) +# End of methods definition + + +class VMPropertyGetter: + """It implements the relationship between drivers and how to get their + properties""" + def __init__(self, vm: QEMUMachine) -> None: + self.drivers: Dict[str, Driver] = {} + + qom_all_types = vm.cmd('qom-list-types', abstract=True) + self.drivers = {t['name']: new_driver(vm, t['name'], + t.get('abstract', False)) + for t in qom_all_types} + + for t in qom_all_types: + drv = self.drivers[t['name']] + if 'parent' in t: + drv.parent = self.drivers[t['parent']] + + for drv in self.drivers.values(): + imps = vm.cmd('qom-list-types', implements=drv.name) + # only implementations inherit property getter + drv.set_implementations([self.drivers[imp['name']] + for imp in imps]) + + def get_prop(self, driver: str, prop: str) -> str: + # wrong driver name or disabled in config driver + try: + drv = self.drivers[driver] + except KeyError: + return 'Unavailable driver' + + assert not drv.abstract + + return drv.get_prop(driver, prop) + + def get_implementations(self, driver: str) -> List[str]: + return [impl.name for impl in self.drivers[driver].implementations] + + +class Machine: + """A short QEMU machine type description. It contains only processed + compat_props (properties of abstract classes are applied to its + implementations) + """ + # raw_mt_dict - dict produced by `query-machines` + def __init__(self, raw_mt_dict: Dict[str, Any], + qemu_drivers: VMPropertyGetter) -> None: + self.name = raw_mt_dict['name'] + self.compat_props: Dict[str, Any] = {} + # properties are applied sequentially and can rewrite values like in + # QEMU. Also it has to resolve class relationships to apply appropriate + # values from abstract class to all implementations + for prop in raw_mt_dict['compat-props']: + driver = prop['qom-type'] + try: + # implementation adds only itself, abstract class adds + # lementation (abstract classes are uninterestiong) + impls = qemu_drivers.get_implementations(driver) + for impl in impls: + if impl not in self.compat_props: + self.compat_props[impl] = {} + self.compat_props[impl][prop['property']] = prop['value'] + except KeyError: + # QEMU doesn't know this driver thus it has to be saved + if driver not in self.compat_props: + self.compat_props[driver] = {} + self.compat_props[driver][prop['property']] = prop['value'] + + +class Configuration(): + """Class contains all necessary components to generate table and is used + to compare different binaries""" + def __init__(self, vm: QEMUMachine, + req_mt: List[str], all_mt: bool) -> None: + self._vm = vm + self._binary = vm.binary + self._qemu_args = args.qemu_args.split(' ') + + self._qemu_drivers = VMPropertyGetter(vm) + self.req_mt = get_req_mt(self._qemu_drivers, vm, req_mt, all_mt) + + def get_implementations(self, driver_name: str) -> List[str]: + return self._qemu_drivers.get_implementations(driver_name) + + def get_table(self, req_props: List[Tuple[str, str]]) -> pd.DataFrame: + table: List[pd.DataFrame] = [] + for mt in self.req_mt: + name = f'{self._binary}\n{mt.name}' + column = [] + for driver, prop in req_props: + try: + # values from QEMU machine type definitions + column.append(mt.compat_props[driver][prop]) + except KeyError: + # values from QEMU type definitions + column.append(self._qemu_drivers.get_prop(driver, prop)) + table.append(pd.DataFrame({name: column})) + + return pd.concat(table, axis=1) + + +script_desc = """Script to compare machine types (their compat_props). + +Examples: +* save info about all machines: ./scripts/compare-machine-types.py --all \ +--format csv --raw > table.csv +* compare machines: ./scripts/compare-machine-types.py --mt pc-q35-2.12 \ +pc-q35-3.0 +* compare binaries and machines: ./scripts/compare-machine-types.py \ +--mt pc-q35-6.2 pc-q35-7.0 --qemu-binary build/qemu-system-x86_64 \ +build/qemu-exp + ╒════════════╤══════════════════════════╤════════════════════════════\ +╤════════════════════════════╤══════════════════╤══════════════════╕ + │ Driver │ Property │ build/qemu-system-x86_64 \ +│ build/qemu-system-x86_64 │ build/qemu-exp │ build/qemu-exp │ + │ │ │ pc-q35-6.2 \ +│ pc-q35-7.0 │ pc-q35-6.2 │ pc-q35-7.0 │ + ╞════════════╪══════════════════════════╪════════════════════════════\ +╪════════════════════════════╪══════════════════╪══════════════════╡ + │ PIIX4_PM │ x-not-migrate-acpi-index │ True \ +│ False │ False │ False │ + ├────────────┼──────────────────────────┼────────────────────────────\ +┼────────────────────────────┼──────────────────┼──────────────────┤ + │ virtio-mem │ unplugged-inaccessible │ False \ +│ auto │ False │ auto │ + ╘════════════╧══════════════════════════╧════════════════════════════\ +╧════════════════════════════╧══════════════════╧══════════════════╛ + +If a property from QEMU machine defintion applies to an abstract class (e.g. \ +x86_64-cpu) this script will compare all implementations of this class. + +"Unavailable method" - means that this script doesn't know how to get \ +default values of the driver. To add method use the construction described \ +at the top of the script. +"Unavailable driver" - means that this script doesn't know this driver. \ +For instance, this can happen if you configure QEMU without this device or \ +if machine type definition has error. +"No default value" - means that the appropriate method can't get the default \ +value and most likely that this property doesn't have it. +"Unknown property" - means that the appropriate method can't find property \ +with this name.""" + + +def parse_args() -> Namespace: + parser = ArgumentParser(formatter_class=RawTextHelpFormatter, + description=script_desc) + parser.add_argument('--format', choices=['human-readable', 'json', 'csv'], + default='human-readable', + help='returns table in json format') + parser.add_argument('--raw', action='store_true', + help='prints ALL defined properties without value ' + 'transformation. By default, only rows ' + 'with different values will be printed and ' + 'values will be transformed(e.g. "on" -> True)') + parser.add_argument('--qemu-args', default=default_qemu_args, + help='command line to start qemu. ' + f'Default: "{default_qemu_args}"') + parser.add_argument('--qemu-binary', nargs="*", type=str, + default=[default_qemu_binary], + help='list of qemu binaries that will be compared. ' + f'Deafult: {default_qemu_binary}') + + mt_args_group = parser.add_mutually_exclusive_group() + mt_args_group.add_argument('--all', action='store_true', + help='prints all available machine types (list ' + 'of machine types will be ignored)') + mt_args_group.add_argument('--mt', nargs="*", type=str, + help='list of Machine Types ' + 'that will be compared') + + return parser.parse_args() + + +def mt_comp(mt: Machine) -> Tuple[str, int, int, int]: + """Function to compare and sort machine by names. + It returns socket_name, major version, minor version, revision""" + # none, microvm, x-remote and etc. + if '-' not in mt.name or '.' not in mt.name: + return mt.name, 0, 0, 0 + + socket, ver = mt.name.rsplit('-', 1) + ver_list = list(map(int, ver.split('.', 2))) + ver_list += [0] * (3 - len(ver_list)) + return socket, ver_list[0], ver_list[1], ver_list[2] + + +def get_mt_definitions(qemu_drivers: VMPropertyGetter, + vm: QEMUMachine) -> List[Machine]: + """Constructs list of machine definitions (primarily compat_props) via + info from QEMU""" + raw_mt_defs = vm.cmd('query-machines', compat_props=True) + mt_defs = [] + for raw_mt in raw_mt_defs: + mt_defs.append(Machine(raw_mt, qemu_drivers)) + + mt_defs.sort(key=mt_comp) + return mt_defs + + +def get_req_mt(qemu_drivers: VMPropertyGetter, vm: QEMUMachine, + req_mt: Optional[List[str]], all_mt: bool) -> List[Machine]: + """Returns list of requested by user machines""" + mt_defs = get_mt_definitions(qemu_drivers, vm) + if all_mt: + return mt_defs + + if req_mt is None: + print('Enter machine types for comparision') + exit(0) + + matched_mt = [] + for mt in mt_defs: + if mt.name in req_mt: + matched_mt.append(mt) + + return matched_mt + + +def get_affected_props(configs: List[Configuration]) -> Generator[Tuple[str, + str], + None, None]: + """Helps to go through all affected in machine definitions drivers + and properties""" + driver_props: Dict[str, Set[Any]] = {} + for config in configs: + for mt in config.req_mt: + compat_props = mt.compat_props + for driver, prop in compat_props.items(): + if driver not in driver_props: + driver_props[driver] = set() + driver_props[driver].update(prop.keys()) + + for driver, props in sorted(driver_props.items()): + for prop in sorted(props): + yield driver, prop + + +def transform_value(value: str) -> Union[str, bool]: + true_list = ['true', 'on'] + false_list = ['false', 'off'] + + out = value.lower() + + if out in true_list: + return True + + if out in false_list: + return False + + return value + + +def simplify_table(table: pd.DataFrame) -> pd.DataFrame: + """transforms values to make it easier to compare it and drops rows + with the same values for all columns""" + + table = table.map(transform_value) + + return table[~table.iloc[:, 3:].eq(table.iloc[:, 2], axis=0).all(axis=1)] + + +# constructs table in the format: +# +# Driver | Property | binary1 | binary1 | ... +# | | machine1 | machine2 | ... +# ------------------------------------------------------ ... +# driver1 | property1 | value1 | value2 | ... +# driver1 | property2 | value3 | value4 | ... +# driver2 | property3 | value5 | value6 | ... +# ... | ... | ... | ... | ... +# +def fill_prop_table(configs: List[Configuration], + is_raw: bool) -> pd.DataFrame: + req_props = list(get_affected_props(configs)) + if not req_props: + print('No drivers to compare. Check machine names') + exit(0) + + driver_col, prop_col = tuple(zip(*req_props)) + table = [pd.DataFrame({'Driver': driver_col}), + pd.DataFrame({'Property': prop_col})] + + table.extend([config.get_table(req_props) for config in configs]) + + df_table = pd.concat(table, axis=1) + + if is_raw: + return df_table + + return simplify_table(df_table) + + +def print_table(table: pd.DataFrame, table_format: str) -> None: + if table_format == 'json': + print(comp_table.to_json()) + elif table_format == 'csv': + print(comp_table.to_csv()) + else: + print(comp_table.to_markdown(index=False, stralign='center', + colalign=('center',), headers='keys', + tablefmt='fancy_grid', + disable_numparse=True)) + + +if __name__ == '__main__': + args = parse_args() + with ExitStack() as stack: + vms = [stack.enter_context(QEMUMachine(binary=binary, qmp_timer=15, + args=args.qemu_args.split(' '))) for binary in args.qemu_binary] + + configurations = [] + for vm in vms: + vm.launch() + configurations.append(Configuration(vm, args.mt, args.all)) + + comp_table = fill_prop_table(configurations, args.raw) + if not comp_table.empty: + print_table(comp_table, args.format) 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 0e6ab4936e..a58e7414c7 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -1,151 +1,157 @@ This is the list of currently configured Coverity components: alpha - ~ (/qemu)?((/include)?/hw/alpha/.*|/target/alpha/.*) + ~ .*/qemu((/include)?/hw/alpha/.*|/target/alpha/.*) arm - ~ (/qemu)?((/include)?/hw/arm/.*|(/include)?/hw/.*/(arm|allwinner-a10|bcm28|digic|exynos|imx|omap|stellaris|pxa2xx|versatile|zynq|cadence).*|/hw/net/xgmac.c|/hw/ssi/xilinx_spips.c|/target/arm/.*) + ~ .*/qemu((/include)?/hw/arm/.*|(/include)?/hw/.*/(arm|allwinner-a10|bcm28|digic|exynos|imx|omap|stellaris|pxa2xx|versatile|zynq|cadence).*|/hw/net/xgmac.c|/hw/ssi/xilinx_spips.c|/target/arm/.*) avr - ~ (/qemu)?((/include)?/hw/avr/.*|/target/avr/.*) + ~ .*/qemu((/include)?/hw/avr/.*|/target/avr/.*) -cris - ~ (/qemu)?((/include)?/hw/cris/.*|/target/cris/.*) +hexagon-gen (component should be ignored in analysis) + ~ .*/qemu(/target/hexagon/.*generated.*) hexagon - ~ (/qemu)?(/target/hexagon/.*) + ~ .*/qemu(/target/hexagon/.*) hppa - ~ (/qemu)?((/include)?/hw/hppa/.*|/target/hppa/.*) + ~ .*/qemu((/include)?/hw/hppa/.*|/target/hppa/.*) i386 - ~ (/qemu)?((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) - -m68k - ~ (/qemu)?((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*) - -microblaze - ~ (/qemu)?((/include)?/hw/microblaze/.*|/target/microblaze/.*) - -mips - ~ (/qemu)?((/include)?/hw/mips/.*|/target/mips/.*) - -nios2 - ~ (/qemu)?((/include)?/hw/nios2/.*|/target/nios2/.*) - -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/.*) - -rx - ~ (/qemu)?((/include)?/hw/rx/.*|/target/rx/.*) - -s390 - ~ (/qemu)?((/include)?/hw/s390x/.*|/target/s390x/.*|/hw/.*/s390_.*) - -sh4 - ~ (/qemu)?((/include)?/hw/sh4/.*|/target/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/.*) - -9pfs - ~ (/qemu)?(/hw/9pfs/.*|/fsdev/.*) - -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).*) - -char - ~ (/qemu)?(/qemu-char\.c|/include/sysemu/char\.h|(/include)?/hw/char/.*) - -capstone - ~ (/qemu)?(/capstone/.*) - -crypto - ~ (/qemu)?((/include)?/crypto/.*|/hw/.*/crypto.*) - -disas - ~ (/qemu)?((/include)?/disas.*) - -fpu - ~ (/qemu)?((/include)?(/fpu|/libdecnumber)/.*) - -io - ~ (/qemu)?((/include)?/io/.*) - -ipmi - ~ (/qemu)?((/include)?/hw/ipmi/.*) - -migration - ~ (/qemu)?((/include)?/migration/.*) - -monitor - ~ (/qemu)?(/qapi.*|/qobject/.*|/monitor\..*|/[hq]mp\..*) - -nbd - ~ (/qemu)?(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) - -net - ~ (/qemu)?((/include)?(/hw)?/(net|rdma)/.*) - -pci - ~ (/qemu)?(/hw/pci.*|/include/hw/pci.*) - -qemu-ga - ~ (/qemu)?(/qga/.*) - -scsi - ~ (/qemu)?(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) - -slirp (component should be ignored in analysis) - ~ (/qemu)?(/slirp/.*) - -tcg - ~ (/qemu)?(/accel/tcg/.*|/replay/.*|/(.*/)?softmmu.*) - -trace - ~ (/qemu)?(/.*trace.*\.[ch]) - -ui - ~ (/qemu)?((/include)?(/ui|/hw/display|/hw/input)/.*) - -usb - ~ (/qemu)?(/hw/usb/.*|/include/hw/usb/.*) - -user - ~ (/qemu)?(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/exec/user/.*) - -util - ~ (/qemu)?(/util/.*|/include/qemu/.*) - -xen - ~ (/qemu)?(.*/xen.*) - -virtiofsd - ~ (/qemu)?(/tools/virtiofsd/.*) - -(headers) - ~ (/qemu)?(/include/.*) - -testlibs - ~ (/qemu)?(/tests/qtest(/libqos/.*|/libqtest.*)) - -tests - ~ (/qemu)?(/tests/.*) + ~ .*/qemu((/include)?/hw/i386/.*|/target/i386/.*|/hw/intc/[^/]*apic[^/]*\.c) loongarch - ~ (/qemu)?((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*) + ~ .*/qemu((/include)?/hw/(loongarch/.*|.*/loongarch.*)|/target/loongarch/.*) + +m68k + ~ .*/qemu((/include)?/hw/m68k/.*|/target/m68k/.*|(/include)?/hw(/.*)?/mcf.*|(/include)?/hw/nubus/.*) + +microblaze + ~ .*/qemu((/include)?/hw/microblaze/.*|/target/microblaze/.*) + +mips + ~ .*/qemu((/include)?/hw/mips/.*|/target/mips/.*) + +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/.*|/hw/.*/(riscv_|ibex_|sifive_).*) + ~ .*/qemu((/include)?/hw/riscv/.*|/target/riscv/.*|/hw/.*/(riscv_|ibex_|sifive_).*) + +rx + ~ .*/qemu((/include)?/hw/rx/.*|/target/rx/.*) + +s390 + ~ .*/qemu((/include)?/hw/s390x/.*|/target/s390x/.*|/hw/.*/s390_.*) + +sh4 + ~ .*/qemu((/include)?/hw/sh4/.*|/target/sh4/.*) + +sparc + ~ .*/qemu((/include)?/hw/sparc(64)?.*|/target/sparc/.*|/hw/.*/grlib.*|/hw/display/cg3.c) + +tricore + ~ .*/qemu((/include)?/hw/tricore/.*|/target/tricore/.*) + +xtensa + ~ .*/qemu((/include)?/hw/xtensa/.*|/target/xtensa/.*) + +9pfs + ~ .*/qemu(/hw/9pfs/.*|/fsdev/.*) + +audio + ~ .*/qemu((/include)?/(audio|hw/audio)/.*) + +block + ~ .*/qemu(/block.*|(/include?)/(block|storage-daemon)/.*|(/include)?/hw/(block|ide|nvme)/.*|/qemu-(img|io).*|/util/(aio|async|thread-pool).*) + +char + ~ .*/qemu((/include)?/hw/char/.*) + +chardev + ~ .*/qemu((/include)?/chardev/.*) + +crypto + ~ .*/qemu((/include)?/crypto/.*|/hw/.*/.*crypto.*|(/include/sysemu|/backends)/cryptodev.*|/host/include/.*/host/crypto/.*) + +disas + ~ .*/qemu((/include)?/disas.*) + +fpu + ~ .*/qemu((/include)?(/fpu|/libdecnumber)/.*) + +io + ~ .*/qemu((/include)?/io/.*) + +ipmi + ~ .*/qemu((/include)?/hw/ipmi/.*) + +migration + ~ .*/qemu((/include)?/migration/.*) + +monitor + ~ .*/qemu((/include)?/(qapi|qobject|monitor)/.*|/job-qmp.c) + +nbd + ~ .*/qemu(/nbd/.*|/include/block/nbd.*|/qemu-nbd\.c) + +net + ~ .*/qemu((/include)?(/hw)?/(net|rdma)/.*) + +pci + ~ .*/qemu(/include)?/hw/(cxl/|pci).* + +qemu-ga + ~ .*/qemu(/qga/.*) + +scsi + ~ .*/qemu(/scsi/.*|/hw/scsi/.*|/include/hw/scsi/.*) + +trace + ~ .*/qemu(/.*trace.*\.[ch]) + +ui + ~ .*/qemu((/include)?(/ui|/hw/display|/hw/input)/.*) + +usb + ~ .*/qemu(/hw/usb/.*|/include/hw/usb/.*) + +user + ~ .*/qemu(/linux-user/.*|/bsd-user/.*|/user-exec\.c|/thunk\.c|/include/user/.*) + +util + ~ .*/qemu(/util/.*|/include/qemu/.*) + +vfio + ~ .*/qemu(/include)?/hw/vfio/.* + +virtio + ~ .*/qemu(/include)?/hw/virtio/.* + +xen + ~ .*/qemu(.*/xen.*) + +hvf + ~ .*/qemu(.*/hvf.*) + +kvm + ~ .*/qemu(.*/kvm.*) + +tcg + ~ .*/qemu(/accel/tcg|/replay|/tcg)/.* + +sysemu + ~ .*/qemu(/system/.*|/accel/.*) + +(headers) + ~ .*/qemu(/include/.*) + +testlibs + ~ .*/qemu(/tests/qtest(/libqos/.*|/libqtest.*|/libqmp.*)) + +tests + ~ .*/qemu(/tests/.*) 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 b74d887331..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() 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 c1f67c8f6a..0000000000 --- a/scripts/feature_to_c.sh +++ /dev/null @@ -1,69 +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 '#include "exec/gdbstub.h"' -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/gen-license.py b/scripts/gen-license.py index 656ad90431..8be7aeb3fa 100755 --- a/scripts/gen-license.py +++ b/scripts/gen-license.py @@ -192,13 +192,13 @@ Lib('slirp', 'https://gitlab.freedesktop.org/slirp', Lib('imgui', 'https://github.com/ocornut/imgui', mit, 'https://raw.githubusercontent.com/ocornut/imgui/master/LICENSE.txt', ships_static=all_platforms, - submodule=Submodule('ui/thirdparty/imgui') + submodule=Submodule('subprojects/imgui.wrap') ), Lib('implot', 'https://github.com/epezent/implot', mit, 'https://raw.githubusercontent.com/epezent/implot/master/LICENSE', ships_static=all_platforms, - submodule=Submodule('ui/thirdparty/implot') + submodule=Submodule('subprojects/implot.wrap') ), Lib('cpp-httplib', 'https://github.com/yhirose/cpp-httplib', diff --git a/scripts/gensyscalls.sh b/scripts/gensyscalls.sh deleted file mode 100755 index a2f7664b7b..0000000000 --- a/scripts/gensyscalls.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh -# -# Update syscall_nr.h files from linux headers asm-generic/unistd.h -# -# This code is licensed under the GPL version 2 or later. See -# the COPYING file in the top-level directory. -# - -linux="$1" -output="$2" - -TMP=$(mktemp -d) - -if [ "$linux" = "" ] ; then - echo "Needs path to linux source tree" 1>&2 - exit 1 -fi - -if [ "$output" = "" ] ; then - output="$PWD" -fi - -upper() -{ - echo "$1" | tr "[:lower:]" "[:upper:]" | tr "[:punct:]" "_" -} - -qemu_arch() -{ - case "$1" in - arm64) - echo "aarch64" - ;; - *) - echo "$1" - ;; - esac -} - -read_includes() -{ - arch=$1 - bits=$2 - - cpp -P -nostdinc -fdirectives-only \ - -D_UAPI_ASM_$(upper ${arch})_BITSPERLONG_H \ - -D__ASM_$(upper ${arch})_BITSPERLONG_H \ - -D__BITS_PER_LONG=${bits} \ - -I${linux}/arch/${arch}/include/uapi/ \ - -I${linux}/include/uapi \ - -I${TMP} \ - "${linux}/arch/${arch}/include/uapi/asm/unistd.h" -} - -filter_defines() -{ - grep -e "#define __NR_" -e "#define __NR3264" -} - -rename_defines() -{ - sed "s/ __NR_/ TARGET_NR_/g;s/(__NR_/(TARGET_NR_/g" -} - -evaluate_values() -{ - sed "s/#define TARGET_NR_/QEMU TARGET_NR_/" | \ - cpp -P -nostdinc | \ - sed "s/^QEMU /#define /" -} - -generate_syscall_nr() -{ - arch=$1 - bits=$2 - file="$3" - guard="$(upper LINUX_USER_$(qemu_arch $arch)_$(basename "$file"))" - - (echo "/*" - echo " * This file contains the system call numbers." - echo " * Do not modify." - echo " * This file is generated by scripts/gensyscalls.sh" - echo " */" - echo "#ifndef ${guard}" - echo "#define ${guard}" - echo - read_includes $arch $bits | filter_defines | rename_defines | \ - evaluate_values | sort -n -k 3 - echo - echo "#endif /* ${guard} */") > "$file" -} - -mkdir "$TMP/asm" -> "$TMP/asm/bitsperlong.h" - -generate_syscall_nr arm64 64 "$output/linux-user/aarch64/syscall_nr.h" -generate_syscall_nr nios2 32 "$output/linux-user/nios2/syscall_nr.h" -generate_syscall_nr openrisc 32 "$output/linux-user/openrisc/syscall_nr.h" - -generate_syscall_nr riscv 32 "$output/linux-user/riscv/syscall32_nr.h" -generate_syscall_nr riscv 64 "$output/linux-user/riscv/syscall64_nr.h" -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 7be41f5948..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 } -if test -n "$maybe_modules" && ! test -e ".git" -then - echo "$0: unexpectedly called with submodules but no git checkout exists" - exit 1 -fi +check_updated() { + local CURSTATUS OLDSTATUS + CURSTATUS=$($GIT submodule status $module) + OLDSTATUS=$(grep $module $substat) + test "$CURSTATUS" = "$OLDSTATUS" +} -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 +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/kernel-doc b/scripts/kernel-doc index 240923d509..fec83f53ed 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -1,5 +1,5 @@ #!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only use warnings; use strict; diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index ce27f5e635..508be19c75 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -24,6 +24,7 @@ MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490 MSR_IA32_VMX_VMFUNC = 0x491 MSR_IA32_VMX_PROCBASED_CTLS3 = 0x492 +MSR_IA32_VMX_EXIT_CTLS2 = 0x493 class msr(object): def __init__(self): @@ -115,6 +116,8 @@ controls = [ (50, 53): 'VMCS memory type', 54: 'INS/OUTS instruction information', 55: 'IA32_VMX_TRUE_*_CTLS support', + 56: 'Skip checks on event error code', + 58: 'VMX nested exception support', }, msr = MSR_IA32_VMX_BASIC, ), @@ -218,11 +221,21 @@ controls = [ 23: 'Clear IA32_BNDCFGS', 24: 'Conceal VM exits from PT', 25: 'Clear IA32_RTIT_CTL', + 31: 'Activate secondary VM-exit controls', }, cap_msr = MSR_IA32_VMX_EXIT_CTLS, true_cap_msr = MSR_IA32_VMX_TRUE_EXIT_CTLS, ), + Allowed1Control( + name = 'secondary VM-Exit controls', + bits = { + 0: 'Save IA32 FRED MSRs', + 1: 'Load IA32 FRED MSRs', + }, + cap_msr = MSR_IA32_VMX_EXIT_CTLS2, + ), + Control( name = 'VM-Entry controls', bits = { @@ -236,6 +249,7 @@ controls = [ 16: 'Load IA32_BNDCFGS', 17: 'Conceal VM entries from PT', 18: 'Load IA32_RTIT_CTL', + 23: 'Load IA32 FRED MSRs', }, cap_msr = MSR_IA32_VMX_ENTRY_CTLS, true_cap_msr = MSR_IA32_VMX_TRUE_ENTRY_CTLS, diff --git a/scripts/main.c b/scripts/main.c deleted file mode 100644 index b552c8e4ed..0000000000 --- a/scripts/main.c +++ /dev/null @@ -1 +0,0 @@ -int main(void) {} 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..8dc939124c 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -10,14 +10,31 @@ # 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 arbitrary-int-1-rs bilge-0.2-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs + syn-2-rs unicode-ident-1-rs" + 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 +45,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 3e2b478538..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: @@ -162,6 +184,7 @@ def cli_metavar(opt): 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("}") diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 54195eb5b3..cc2fc6755a 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: alsa/co' - printf "%s\n" ' reaudio/default/dsound/jack/oss/pa/sdl/sndio)' + 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,65 +12,94 @@ 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-asan enable address sanitizer' printf "%s\n" ' --enable-block-drv-whitelist-in-tools' printf "%s\n" ' use block whitelist also in tools instead of only' 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-remap syscall buffer 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-safe-stack SafeStack Stack Smash Protection (requires' + printf "%s\n" ' clang/llvm and coroutine backend ucontext)' + printf "%s\n" ' --enable-strict-rust-lints' + printf "%s\n" ' Enable stricter set of Rust warnings' 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" ' --enable-tsan enable thread sanitizer' + printf "%s\n" ' --enable-ubsan enable undefined behaviour 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 [lib/x86_64-linux-gnu]' + 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" ' --rtsig-map=VALUE default value of QEMU_RTSIG_MAP [NULL]' + 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" ' --vtune=VALUE Path to VTune directory for profiling' + 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" ' --x86-version=CHOICE tweak required x86_64 architecture version beyond' + printf "%s\n" ' compiler default [1] (choices: 0/1/2/3/4)' 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" ' avx512f AVX512F optimizations' + printf "%s\n" ' avx512bw AVX512BW optimizations' printf "%s\n" ' blkio libblkio block device driver' printf "%s\n" ' bochs bochs image format support' printf "%s\n" ' bpf eBPF support' @@ -79,6 +110,7 @@ meson_options_help() { 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' @@ -97,15 +129,18 @@ meson_options_help() { 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' printf "%s\n" ' keyring Linux keyring support' printf "%s\n" ' kvm KVM acceleration support' printf "%s\n" ' l2tpv3 l2tpv3 network backend support' + printf "%s\n" ' libcbor libcbor 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' @@ -114,12 +149,11 @@ meson_options_help() { 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' - printf "%s\n" ' block migration in the main migration stream' printf "%s\n" ' lzfse lzfse support for DMG images' 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' @@ -130,15 +164,21 @@ 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" ' qatzip QATzip compression support' printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)' + printf "%s\n" ' qpl Query Processing Library support' printf "%s\n" ' rbd Ceph block device driver' printf "%s\n" ' rdma Enable RDMA-based migration' - printf "%s\n" ' renderdoc improved RenderDoc frame capture support' + printf "%s\n" ' renderdoc RenderDoc frame capture support' printf "%s\n" ' replication replication support' + printf "%s\n" ' rust Rust 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' @@ -151,10 +191,12 @@ meson_options_help() { 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' printf "%s\n" ' u2f U2F emulation support' + printf "%s\n" ' uadk UADK Library support' printf "%s\n" ' usb-redir libusbredir support' printf "%s\n" ' vde vde network backend support' printf "%s\n" ' vdi vdi image format support' @@ -162,6 +204,7 @@ meson_options_help() { 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' @@ -171,13 +214,15 @@ 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" ' 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' @@ -187,8 +232,12 @@ 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-asan) printf "%s" -Dasan=true ;; + --disable-asan) printf "%s" -Dasan=false ;; --enable-attr) printf "%s" -Dattr=enabled ;; --disable-attr) printf "%s" -Dattr=disabled ;; --audio-drv-list=*) quote_sh "-Daudio_drv_list=$2" ;; @@ -196,12 +245,13 @@ _meson_option_parse() { --disable-auth-pam) printf "%s" -Dauth_pam=disabled ;; --enable-avx2) printf "%s" -Davx2=enabled ;; --disable-avx2) printf "%s" -Davx2=disabled ;; - --enable-avx512f) printf "%s" -Davx512f=enabled ;; - --disable-avx512f) printf "%s" -Davx512f=disabled ;; + --enable-avx512bw) printf "%s" -Davx512bw=enabled ;; + --disable-avx512bw) printf "%s" -Davx512bw=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" ;; @@ -230,8 +280,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 ;; @@ -243,10 +296,18 @@ _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-remap) printf "%s" -Ddebug_remap=true ;; + --disable-debug-remap) printf "%s" -Ddebug_remap=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" ;; @@ -273,16 +334,16 @@ _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-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" ;; @@ -300,12 +361,18 @@ _meson_option_parse() { --disable-kvm) printf "%s" -Dkvm=disabled ;; --enable-l2tpv3) printf "%s" -Dl2tpv3=enabled ;; --disable-l2tpv3) printf "%s" -Dl2tpv3=disabled ;; + --enable-libcbor) printf "%s" -Dlibcbor=enabled ;; + --disable-libcbor) printf "%s" -Dlibcbor=disabled ;; --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 ;; @@ -322,8 +389,6 @@ _meson_option_parse() { --disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;; --enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;; --disable-linux-io-uring) printf "%s" -Dlinux_io_uring=disabled ;; - --enable-live-block-migration) printf "%s" -Dlive_block_migration=enabled ;; - --disable-live-block-migration) printf "%s" -Dlive_block_migration=disabled ;; --localedir=*) quote_sh "-Dlocaledir=$2" ;; --localstatedir=*) quote_sh "-Dlocalstatedir=$2" ;; --enable-lzfse) printf "%s" -Dlzfse=enabled ;; @@ -338,6 +403,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 ;; @@ -358,32 +425,52 @@ _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 ;; - --enable-pvrdma) printf "%s" -Dpvrdma=enabled ;; - --disable-pvrdma) printf "%s" -Dpvrdma=disabled ;; + --prefix=*) quote_sh "-Dprefix=$2" ;; + --enable-qatzip) printf "%s" -Dqatzip=enabled ;; + --disable-qatzip) printf "%s" -Dqatzip=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=$(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 ;; --disable-qom-cast-debug) printf "%s" -Dqom_cast_debug=false ;; + --enable-qpl) printf "%s" -Dqpl=enabled ;; + --disable-qpl) printf "%s" -Dqpl=disabled ;; --enable-rbd) printf "%s" -Drbd=enabled ;; --disable-rbd) printf "%s" -Drbd=disabled ;; --enable-rdma) printf "%s" -Drdma=enabled ;; --disable-rdma) printf "%s" -Drdma=disabled ;; + --enable-relocatable) printf "%s" -Drelocatable=true ;; + --disable-relocatable) printf "%s" -Drelocatable=false ;; --enable-renderdoc) printf "%s" -Drenderdoc=enabled ;; --disable-renderdoc) printf "%s" -Drenderdoc=disabled ;; --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 ;; + --rtsig-map=*) quote_sh "-Drtsig_map=$2" ;; + --enable-rust) printf "%s" -Drust=enabled ;; + --disable-rust) printf "%s" -Drust=disabled ;; + --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-sdl) printf "%s" -Dsdl=enabled ;; --disable-sdl) printf "%s" -Dsdl=disabled ;; --enable-sdl-image) printf "%s" -Dsdl_image=enabled ;; @@ -398,17 +485,21 @@ _meson_option_parse() { --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-strict-rust-lints) printf "%s" -Dstrict_rust_lints=true ;; + --disable-strict-rust-lints) printf "%s" -Dstrict_rust_lints=false ;; --enable-strip) printf "%s" -Dstrip=true ;; --disable-strip) printf "%s" -Dstrip=false ;; --sysconfdir=*) quote_sh "-Dsysconfdir=$2" ;; @@ -423,8 +514,14 @@ _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-uadk) printf "%s" -Duadk=enabled ;; + --disable-uadk) printf "%s" -Duadk=disabled ;; + --enable-ubsan) printf "%s" -Dubsan=true ;; + --disable-ubsan) printf "%s" -Dubsan=false ;; --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;; --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;; --enable-vde) printf "%s" -Dvde=enabled ;; @@ -435,6 +532,8 @@ _meson_option_parse() { --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 ;; @@ -451,8 +550,8 @@ _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-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 ;; @@ -461,13 +560,18 @@ _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 ;; --vtune=*) quote_sh "-Dvtune=$2" ;; --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 ;; + --x86-version=*) quote_sh "-Dx86_version=$2" ;; --enable-xen) printf "%s" -Dxen=enabled ;; --disable-xen) printf "%s" -Dxen=disabled ;; --enable-xen-pci-passthrough) printf "%s" -Dxen_pci_passthrough=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/minikconf.py b/scripts/minikconf.py index bcd91015d3..6f7f43b291 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -112,7 +112,7 @@ class KconfigData: def set_value(self, val, clause): self.clauses_for_var.append(clause) if self.has_value() and self.value != val: - print("The following clauses were found for " + self.name) + print("The following clauses were found for " + self.name, file=sys.stderr) for i in self.clauses_for_var: print(" " + str(i), file=sys.stderr) raise KconfigDataError('contradiction between clauses when setting %s' % self) 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/oss-fuzz/build.sh b/scripts/oss-fuzz/build.sh index 3bda0d72c7..7398298173 100755 --- a/scripts/oss-fuzz/build.sh +++ b/scripts/oss-fuzz/build.sh @@ -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 @@ -92,6 +92,7 @@ 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 +export ASAN_OPTIONS=detect_leaks=0 targets=$(./qemu-fuzz-i386 | grep generic-fuzz | awk '$1 ~ /\*/ {print $2}') base_copy="$DEST_DIR/qemu-fuzz-i386-target-$(echo "$targets" | head -n 1)" 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..6bcadce150 --- /dev/null +++ b/scripts/probe-gdb-support.py @@ -0,0 +1,89 @@ +#!/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, CalledProcessError +import sys + +# Mappings from gdb arch to QEMU target +MAP = { + "alpha" : ["alpha"], + "aarch64" : ["aarch64", "aarch64_be"], + "armv7": ["arm"], + "armv8-a" : ["aarch64", "aarch64_be"], + "avr" : ["avr"], + # 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"], + "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): + try: + gdb_out = check_output([gdb, + "-ex", "set architecture", + "-ex", "quit"], stderr=STDOUT, encoding="utf-8") + except (OSError) as e: + sys.exit(e) + except CalledProcessError as e: + sys.exit(f'{e}. Output:\n\n{e.output}') + + found_gdb_archs = re.search(r'Valid arguments are (.*)', gdb_out) + + targets = set() + if found_gdb_archs: + gdb_archs = found_gdb_archs.group(1).split(", ") + mapped_gdb_archs = [arch for arch in gdb_archs if arch in MAP] + + targets = {target for arch in mapped_gdb_archs for target in MAP[arch]} + + # QEMU targets + return targets + + +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..79951a841f 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')) @@ -62,9 +64,10 @@ def gen_call(name: str, assert arg_type argstr = '&arg, ' elif arg_type: - assert not arg_type.variants + assert not arg_type.branches 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 737b059e62..d7c8aa3365 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -40,22 +40,28 @@ def camel_to_upper(value: str) -> str: ENUM_Name2 -> ENUM_NAME2 ENUM24_Name -> ENUM24_NAME """ - c_fun_str = c_name(value, False) - if value.isupper(): - return c_fun_str + ret = value[0] + upc = value[0].isupper() - new_name = '' - length = len(c_fun_str) - for i in range(length): - char = c_fun_str[i] - # When char is upper case and no '_' appears before, do more checks - if char.isupper() and (i > 0) and c_fun_str[i - 1] != '_': - if i < length - 1 and c_fun_str[i + 1].islower(): - new_name += '_' - elif c_fun_str[i - 1].isdigit(): - new_name += '_' - new_name += char - return new_name.lstrip('_').upper() + # Copy remainder of ``value`` to ``ret`` with '_' inserted + for ch in value[1:]: + if ch.isupper() == upc: + pass + elif upc: + # ``ret`` ends in upper case, next char isn't: insert '_' + # before the last upper case char unless there is one + # already, or it's at the beginning + if len(ret) > 2 and ret[-2].isalnum(): + ret = ret[:-1] + '_' + ret[-1] + else: + # ``ret`` doesn't end in upper case, next char is: insert + # '_' before it + if ret[-1].isalnum(): + ret += '_' + ret += ch + upc = ch.isupper() + + return c_name(ret.upper()).lstrip('_') def c_enum_const(type_name: str, @@ -68,9 +74,9 @@ def c_enum_const(type_name: str, :param const_name: The name of this constant. :param prefix: Optional, prefix that overrides the type_name. """ - if prefix is not None: - type_name = prefix - return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() + if prefix is None: + prefix = camel_to_upper(type_name) + return prefix + '_' + c_name(const_name, False).upper() def c_name(name: str, protect: bool = True) -> str: diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 27b44c49f5..d1f639981a 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -51,7 +51,7 @@ def gen_param_var(typ: QAPISchemaObjectType) -> str: Initialize it with the function arguments defined in `gen_event_send`. """ - assert not typ.variants + assert not typ.branches ret = mcgen(''' %(c_name)s param = { ''', @@ -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..6a8abe0041 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() @@ -117,11 +118,12 @@ def build_params(arg_type: Optional[QAPISchemaObjectType], ret += '%s arg' % arg_type.c_param_type() sep = ', ' elif arg_type: - assert not arg_type.variants + assert not arg_type.branches 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/introspect.py b/scripts/qapi/introspect.py index 67c7d89aae..ac14b20f30 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -26,7 +26,9 @@ from .common import c_name, mcgen from .gen import QAPISchemaMonolithicCVisitor from .schema import ( QAPISchema, + QAPISchemaAlternatives, QAPISchemaArrayType, + QAPISchemaBranches, QAPISchemaBuiltinType, QAPISchemaEntity, QAPISchemaEnumMember, @@ -36,7 +38,6 @@ from .schema import ( QAPISchemaObjectTypeMember, QAPISchemaType, QAPISchemaVariant, - QAPISchemaVariants, ) from .source import QAPISourceInfo @@ -227,10 +228,14 @@ const QLitObject %(c_name)s = %(c_string)s; # Map the various integer types to plain int if typ.json_type() == 'int': - typ = self._schema.lookup_type('int') + type_int = self._schema.lookup_type('int') + assert type_int + typ = type_int elif (isinstance(typ, QAPISchemaArrayType) and typ.element_type.json_type() == 'int'): - typ = self._schema.lookup_type('intList') + type_intlist = self._schema.lookup_type('intList') + assert type_intlist + typ = type_intlist # Add type to work queue if new if typ not in self._used_types: self._used_types.append(typ) @@ -331,24 +336,24 @@ const QLitObject %(c_name)s = %(c_string)s; ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> None: + branches: Optional[QAPISchemaBranches]) -> None: obj: SchemaInfoObject = { 'members': [self._gen_object_member(m) for m in members] } - if variants: - obj['tag'] = variants.tag_member.name - obj['variants'] = [self._gen_variant(v) for v in variants.variants] + if branches: + obj['tag'] = branches.tag_member.name + obj['variants'] = [self._gen_variant(v) for v in branches.variants] self._gen_tree(name, 'object', obj, ifcond, features) def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants) -> None: + alternatives: QAPISchemaAlternatives) -> None: self._gen_tree( name, 'alternate', {'members': [Annotated({'type': self._use_type(m.type)}, m.ifcond) - for m in variants.variants]}, + for m in alternatives.variants]}, ifcond, features ) 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..8109470a03 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -1,9 +1,4 @@ [mypy] strict = True disallow_untyped_calls = False -python_version = 3.6 - -[mypy-qapi.schema] -disallow_untyped_defs = False -disallow_incomplete_defs = False -check_untyped_defs = False +python_version = 3.8 diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 1b006cdc13..adc85b5b39 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -19,8 +19,11 @@ import os import re from typing import ( TYPE_CHECKING, + Any, Dict, List, + Mapping, + Match, Optional, Set, Union, @@ -37,15 +40,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, Any]): + # 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 +72,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 +107,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 +135,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 +154,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 +171,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 +239,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 +350,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 +415,205 @@ 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) + 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) + 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)' + r'(?!::): *', + line, + ): + # tagged section + + # Note: "sections" with two colons are left alone as + # rST markup and not interpreted as a section heading. + + # TODO: Remove these errors sometime in 2025 or so + # after we've fully transitioned to the new qapidoc + # generator. + + # See commit message for more markup suggestions O:-) + if 'Note' in match.group(1): + emsg = ( + f"The '{match.group(1)}' section is no longer " + "supported. Please use rST's '.. note::' or " + "'.. admonition:: notes' directives, or another " + "suitable admonition instead." + ) + raise QAPIParseError(self, emsg) + + if 'Example' in match.group(1): + emsg = ( + f"The '{match.group(1)}' section is no longer " + "supported. Please use the '.. qmp-example::' " + "directive, or other suitable markup instead." + ) + raise QAPIParseError(self, emsg) + + 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() + doc.end() + return doc class QAPIDoc: @@ -462,319 +637,111 @@ 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) + assert member.info + 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 +751,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.errors.info, + "'Errors' section is only valid for commands") def check(self) -> None: @@ -798,7 +776,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..c028a1f9f5 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -1,10 +1,5 @@ [MASTER] -# Add files or directories matching the regex patterns to the ignore list. -# The regex matches against base names, not paths. -ignore-patterns=schema.py, - - [MESSAGES CONTROL] # Disable the message, report, category or checker with the given id(s). You @@ -16,13 +11,14 @@ ignore-patterns=schema.py, # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=fixme, +disable=consider-using-f-string, + fixme, missing-docstring, too-many-arguments, too-many-branches, - too-many-statements, too-many-instance-attributes, - consider-using-f-string, + too-many-statements, + useless-option-value, [REPORTS] diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 3728340c37..e97c978d38 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -12,12 +12,25 @@ # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. +# pylint: disable=too-many-lines + # TODO catching name collisions in generated code would be nice +from __future__ import annotations + +from abc import ABC, abstractmethod from collections import OrderedDict import os import re -from typing import Optional +from typing import ( + Any, + Callable, + Dict, + List, + Optional, + Union, + cast, +) from .common import ( POINTER_SUFFIX, @@ -29,138 +42,230 @@ from .common import ( ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs -from .parser import QAPISchemaParser +from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser +from .source import QAPISourceInfo class QAPISchemaIfCond: - def __init__(self, ifcond=None): + def __init__( + self, + ifcond: Optional[Union[str, Dict[str, object]]] = None, + ) -> None: self.ifcond = ifcond - def _cgen(self): + def _cgen(self) -> str: return cgen_ifcond(self.ifcond) - def gen_if(self): + def gen_if(self) -> str: return gen_if(self._cgen()) - def gen_endif(self): + def gen_endif(self) -> str: return gen_endif(self._cgen()) - def docgen(self): + def docgen(self) -> str: return docgen_ifcond(self.ifcond) - def is_present(self): + def is_present(self) -> bool: return bool(self.ifcond) class QAPISchemaEntity: - meta: Optional[str] = None + """ + A schema entity. - def __init__(self, name: str, info, doc, ifcond=None, features=None): - assert name is None or isinstance(name, str) - for f in features or []: - assert isinstance(f, QAPISchemaFeature) - f.set_defined_in(name) - self.name = name - self._module = None + This is either a directive, such as include, or a definition. + The latter uses sub-class `QAPISchemaDefinition`. + """ + def __init__(self, info: Optional[QAPISourceInfo]): + self._module: Optional[QAPISchemaModule] = None # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. # For implicitly defined entities, info points to a place that # triggered the implicit definition (there may be more than one # such place). self.info = info - self.doc = doc - self._ifcond = ifcond or QAPISchemaIfCond() - self.features = features or [] self._checked = False - def c_name(self): - return c_name(self.name) + def __repr__(self) -> str: + return "<%s at 0x%x>" % (type(self).__name__, id(self)) - def check(self, schema): - assert not self._checked - seen = {} - for f in self.features: - f.check_clash(self.info, seen) + def check(self, schema: QAPISchema) -> None: + # pylint: disable=unused-argument self._checked = True - def connect_doc(self, doc=None): - doc = doc or self.doc - if doc: - for f in self.features: - doc.connect_feature(f) + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: + pass - def check_doc(self): - if self.doc: - self.doc.check() - - def _set_module(self, schema, info): + def _set_module( + self, schema: QAPISchema, info: Optional[QAPISourceInfo] + ) -> None: assert self._checked fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME self._module = schema.module_by_fname(fname) self._module.add_entity(self) - def set_module(self, schema): + def set_module(self, schema: QAPISchema) -> None: self._set_module(schema, self.info) + def visit(self, visitor: QAPISchemaVisitor) -> None: + # pylint: disable=unused-argument + assert self._checked + + +class QAPISchemaDefinition(QAPISchemaEntity): + meta: str + + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): + super().__init__(info) + for f in features or []: + f.set_defined_in(name) + self.name = name + self.doc = doc + self._ifcond = ifcond or QAPISchemaIfCond() + self.features = features or [] + + def __repr__(self) -> str: + return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, + id(self)) + + def c_name(self) -> str: + return c_name(self.name) + + def check(self, schema: QAPISchema) -> None: + assert not self._checked + super().check(schema) + seen: Dict[str, QAPISchemaMember] = {} + for f in self.features: + f.check_clash(self.info, seen) + + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: + super().connect_doc(doc) + doc = doc or self.doc + if doc: + for f in self.features: + doc.connect_feature(f) + @property - def ifcond(self): + def ifcond(self) -> QAPISchemaIfCond: assert self._checked return self._ifcond - def is_implicit(self): + def is_implicit(self) -> bool: return not self.info - def visit(self, visitor): - assert self._checked - - def describe(self): - assert self.meta + def describe(self) -> str: return "%s '%s'" % (self.meta, self.name) class QAPISchemaVisitor: - def visit_begin(self, schema): + def visit_begin(self, schema: QAPISchema) -> None: pass - def visit_end(self): + def visit_end(self) -> None: pass - def visit_module(self, name): + def visit_module(self, name: str) -> None: pass - def visit_needed(self, entity): + def visit_needed(self, entity: QAPISchemaEntity) -> bool: + # pylint: disable=unused-argument # Default to visiting everything return True - def visit_include(self, name, info): + def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None: pass - def visit_builtin_type(self, name, info, json_type): + def visit_builtin_type( + self, name: str, info: Optional[QAPISourceInfo], json_type: str + ) -> None: pass - def visit_enum_type(self, name, info, ifcond, features, members, prefix): + def visit_enum_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaEnumMember], + prefix: Optional[str], + ) -> None: pass - def visit_array_type(self, name, info, ifcond, element_type): + def visit_array_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + element_type: QAPISchemaType, + ) -> None: pass - def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + def visit_object_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + base: Optional[QAPISchemaObjectType], + members: List[QAPISchemaObjectTypeMember], + branches: Optional[QAPISchemaBranches], + ) -> None: pass - def visit_object_type_flat(self, name, info, ifcond, features, - members, variants): + def visit_object_type_flat( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + members: List[QAPISchemaObjectTypeMember], + branches: Optional[QAPISchemaBranches], + ) -> None: pass - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + alternatives: QAPISchemaAlternatives, + ) -> None: pass - def visit_command(self, name, info, ifcond, features, - arg_type, ret_type, gen, success_response, boxed, - allow_oob, allow_preconfig, coroutine): + def visit_command( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + ret_type: Optional[QAPISchemaType], + gen: bool, + success_response: bool, + boxed: bool, + allow_oob: bool, + allow_preconfig: bool, + coroutine: bool, + ) -> None: pass - def visit_event(self, name, info, ifcond, features, arg_type, boxed): + def visit_event( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[QAPISchemaObjectType], + boxed: bool, + ) -> None: pass @@ -168,9 +273,9 @@ class QAPISchemaModule: BUILTIN_MODULE_NAME = './builtin' - def __init__(self, name): + def __init__(self, name: str): self.name = name - self._entity_list = [] + self._entity_list: List[QAPISchemaEntity] = [] @staticmethod def is_system_module(name: str) -> bool: @@ -199,10 +304,10 @@ class QAPISchemaModule: """ return name == cls.BUILTIN_MODULE_NAME - def add_entity(self, ent): + def add_entity(self, ent: QAPISchemaEntity) -> None: self._entity_list.append(ent) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_module(self.name) for entity in self._entity_list: if visitor.visit_needed(entity): @@ -210,33 +315,35 @@ class QAPISchemaModule: class QAPISchemaInclude(QAPISchemaEntity): - def __init__(self, sub_module, info): - super().__init__(None, info, None) + def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo): + super().__init__(info) self._sub_module = sub_module - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_include(self._sub_module.name, self.info) -class QAPISchemaType(QAPISchemaEntity): +class QAPISchemaType(QAPISchemaDefinition, ABC): # Return the C type for common use. # For the types we commonly box, this is a pointer type. - def c_type(self): + @abstractmethod + def c_type(self) -> str: pass # Return the C type to be used in a parameter list. - def c_param_type(self): + def c_param_type(self) -> str: return self.c_type() # Return the C type to be used where we suppress boxing. - def c_unboxed_type(self): + def c_unboxed_type(self) -> str: return self.c_type() - def json_type(self): + @abstractmethod + def json_type(self) -> str: pass - def alternate_qtype(self): + def alternate_qtype(self) -> Optional[str]: json2qtype = { 'null': 'QTYPE_QNULL', 'string': 'QTYPE_QSTRING', @@ -248,53 +355,56 @@ class QAPISchemaType(QAPISchemaEntity): } return json2qtype.get(self.json_type()) - def doc_type(self): + def doc_type(self) -> Optional[str]: if self.is_implicit(): return None return self.name - def check(self, schema): - QAPISchemaEntity.check(self, schema) + def need_has_if_optional(self) -> bool: + # 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: QAPISchema) -> None: + super().check(schema) for feat in self.features: if feat.is_special(): raise QAPISemError( self.info, f"feature '{feat.name}' is not supported for types") - def describe(self): - assert self.meta + def describe(self) -> str: return "%s type '%s'" % (self.meta, self.name) class QAPISchemaBuiltinType(QAPISchemaType): meta = 'built-in' - def __init__(self, name, json_type, c_type): + def __init__(self, name: str, json_type: str, c_type: str): super().__init__(name, None, None) - assert not c_type or isinstance(c_type, str) assert json_type in ('string', 'number', 'int', 'boolean', 'null', 'value') self._json_type_name = json_type self._c_type_name = c_type - def c_name(self): + def c_name(self) -> str: return self.name - def c_type(self): + def c_type(self) -> str: return self._c_type_name - def c_param_type(self): + def c_param_type(self) -> str: if self.name == 'str': return 'const ' + self._c_type_name return self._c_type_name - def json_type(self): + def json_type(self) -> str: return self._json_type_name - def doc_type(self): + def doc_type(self) -> str: return self.json_type() - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_builtin_type(self.name, self.info, self.json_type()) @@ -302,41 +412,48 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, features, members, prefix): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: Optional[List[QAPISchemaFeature]], + members: List[QAPISchemaEnumMember], + prefix: Optional[str], + ): super().__init__(name, info, doc, ifcond, features) for m in members: - assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) - assert prefix is None or isinstance(prefix, str) self.members = members self.prefix = prefix - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for m in self.members: m.check_clash(self.info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc for m in self.members: m.connect_doc(doc) - def is_implicit(self): + def is_implicit(self) -> bool: # See QAPISchema._def_predefineds() return self.name == 'QType' - def c_type(self): + def c_type(self) -> str: return c_name(self.name) - def member_names(self): + def member_names(self) -> List[str]: return [m.name for m in self.members] - def json_type(self): + def json_type(self) -> str: return 'string' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_enum_type( self.name, self.info, self.ifcond, self.features, @@ -346,77 +463,89 @@ class QAPISchemaEnumType(QAPISchemaType): class QAPISchemaArrayType(QAPISchemaType): meta = 'array' - def __init__(self, name, info, element_type): + def __init__( + self, name: str, info: Optional[QAPISourceInfo], element_type: str + ): super().__init__(name, info, None) - assert isinstance(element_type, str) self._element_type_name = element_type - self.element_type = None + self.element_type: QAPISchemaType - def check(self, schema): + def need_has_if_optional(self) -> bool: + # 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: QAPISchema) -> None: super().check(schema) self.element_type = schema.resolve_type( self._element_type_name, self.info, - self.info and self.info.defn_meta) + self.info.defn_meta if self.info else None) assert not isinstance(self.element_type, QAPISchemaArrayType) - def set_module(self, schema): + def set_module(self, schema: QAPISchema) -> None: self._set_module(schema, self.element_type.info) @property - def ifcond(self): + def ifcond(self) -> QAPISchemaIfCond: assert self._checked return self.element_type.ifcond - def is_implicit(self): + def is_implicit(self) -> bool: return True - def c_type(self): + def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX - def json_type(self): + def json_type(self) -> str: return 'array' - def doc_type(self): + def doc_type(self) -> Optional[str]: elt_doc_type = self.element_type.doc_type() if not elt_doc_type: return None return 'array of ' + elt_doc_type - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_array_type(self.name, self.info, self.ifcond, self.element_type) - def describe(self): - assert self.meta + def describe(self) -> str: return "%s type ['%s']" % (self.meta, self._element_type_name) class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, features, - base, local_members, variants): - # struct has local_members, optional base, and no variants - # union has base, variants, and no local_members + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: Optional[List[QAPISchemaFeature]], + base: Optional[str], + local_members: List[QAPISchemaObjectTypeMember], + branches: Optional[QAPISchemaBranches], + ): + # struct has local_members, optional base, and no branches + # union has base, branches, and no local_members super().__init__(name, info, doc, ifcond, features) - self.meta = 'union' if variants else 'struct' - assert base is None or isinstance(base, str) + self.meta = 'union' if branches else 'struct' for m in local_members: - assert isinstance(m, QAPISchemaObjectTypeMember) m.set_defined_in(name) - if variants is not None: - assert isinstance(variants, QAPISchemaVariants) - variants.set_defined_in(name) + if branches is not None: + branches.set_defined_in(name) self._base_name = base self.base = None self.local_members = local_members - self.variants = variants - self.members = None + self.branches = branches + self.members: List[QAPISchemaObjectTypeMember] + self._check_complete = False - def check(self, schema): + def check(self, schema: QAPISchema) -> None: # This calls another type T's .check() exactly when the C # struct emitted by gen_object() contains that T's C struct # (pointers don't count). - if self.members is not None: + if self._check_complete: # A previous .check() completed: nothing to do return if self._checked: @@ -425,14 +554,14 @@ class QAPISchemaObjectType(QAPISchemaType): "object %s contains itself" % self.name) super().check(schema) - assert self._checked and self.members is None + assert self._checked and not self._check_complete seen = OrderedDict() if self._base_name: self.base = schema.resolve_type(self._base_name, self.info, "'base'") if (not isinstance(self.base, QAPISchemaObjectType) - or self.base.variants): + or self.base.branches): raise QAPISemError( self.info, "'base' requires a struct type, %s isn't" @@ -442,24 +571,34 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.local_members: m.check(schema) m.check_clash(self.info, seen) - members = seen.values() - if self.variants: - self.variants.check(schema, seen) - self.variants.check_clash(self.info, seen) + # self.check_clash() works in terms of the supertype, but + # self.members is declared List[QAPISchemaObjectTypeMember]. + # Cast down to the subtype. + members = cast(List[QAPISchemaObjectTypeMember], list(seen.values())) - self.members = members # mark completed + if self.branches: + self.branches.check(schema, seen) + self.branches.check_clash(self.info, seen) + + self.members = members + self._check_complete = True # mark completed # Check that the members of this type do not cause duplicate JSON members, # and update seen to track the members seen so far. Report any errors # on behalf of info, which is not necessarily self.info - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: assert self._checked - assert not self.variants # not implemented for m in self.members: m.check_clash(info, seen) + if self.branches: + self.branches.check_clash(info, seen) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if self.base and self.base.is_implicit(): @@ -467,61 +606,70 @@ class QAPISchemaObjectType(QAPISchemaType): for m in self.local_members: m.connect_doc(doc) - def is_implicit(self): + def is_implicit(self) -> bool: # See QAPISchema._make_implicit_object_type(), as well as # _def_predefineds() return self.name.startswith('q_') - def is_empty(self): - assert self.members is not None - return not self.members and not self.variants + def is_empty(self) -> bool: + return not self.members and not self.branches - def c_name(self): + def has_conditional_members(self) -> bool: + return any(m.ifcond.is_present() for m in self.members) + + def c_name(self) -> str: assert self.name != 'q_empty' return super().c_name() - def c_type(self): + def c_type(self) -> str: assert not self.is_implicit() return c_name(self.name) + POINTER_SUFFIX - def c_unboxed_type(self): + def c_unboxed_type(self) -> str: return c_name(self.name) - def json_type(self): + def json_type(self) -> str: return 'object' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_object_type( self.name, self.info, self.ifcond, self.features, - self.base, self.local_members, self.variants) + self.base, self.local_members, self.branches) visitor.visit_object_type_flat( self.name, self.info, self.ifcond, self.features, - self.members, self.variants) + self.members, self.branches) class QAPISchemaAlternateType(QAPISchemaType): meta = 'alternate' - def __init__(self, name, info, doc, ifcond, features, variants): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: Optional[QAPISchemaIfCond], + features: List[QAPISchemaFeature], + alternatives: QAPISchemaAlternatives, + ): super().__init__(name, info, doc, ifcond, features) - assert isinstance(variants, QAPISchemaVariants) - assert variants.tag_member - variants.set_defined_in(name) - variants.tag_member.set_defined_in(self.name) - self.variants = variants + assert alternatives.tag_member + alternatives.set_defined_in(name) + alternatives.tag_member.set_defined_in(self.name) + self.alternatives = alternatives - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) - self.variants.tag_member.check(schema) - # Not calling self.variants.check_clash(), because there's nothing - # to clash with - self.variants.check(schema, {}) + self.alternatives.tag_member.check(schema) + # Not calling self.alternatives.check_clash(), because there's + # nothing to clash with + self.alternatives.check(schema, {}) # Alternate branch names have no relation to the tag enum values; # so we have to check for potential name collisions ourselves. - seen = {} - types_seen = {} - for v in self.variants.variants: + seen: Dict[str, QAPISchemaMember] = {} + types_seen: Dict[str, str] = {} + for v in self.alternatives.variants: v.check_clash(self.info, seen) qtype = v.type.alternate_qtype() if not qtype: @@ -549,87 +697,102 @@ class QAPISchemaAlternateType(QAPISchemaType): % (v.describe(self.info), types_seen[qt])) types_seen[qt] = v.name - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc - for v in self.variants.variants: + for v in self.alternatives.variants: v.connect_doc(doc) - def c_type(self): + def c_type(self) -> str: return c_name(self.name) + POINTER_SUFFIX - def json_type(self): + def json_type(self) -> str: return 'value' - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_alternate_type( - self.name, self.info, self.ifcond, self.features, self.variants) + self.name, self.info, self.ifcond, self.features, + self.alternatives) class QAPISchemaVariants: - def __init__(self, tag_name, info, tag_member, variants): - # Unions pass tag_name but not tag_member. - # Alternates pass tag_member but not tag_name. - # After check(), tag_member is always set. - assert bool(tag_member) != bool(tag_name) - assert (isinstance(tag_name, str) or - isinstance(tag_member, QAPISchemaObjectTypeMember)) - for v in variants: - assert isinstance(v, QAPISchemaVariant) - self._tag_name = tag_name + def __init__( + self, + info: QAPISourceInfo, + variants: List[QAPISchemaVariant], + ): self.info = info - self.tag_member = tag_member + self.tag_member: QAPISchemaObjectTypeMember self.variants = variants - def set_defined_in(self, name): + def set_defined_in(self, name: str) -> None: for v in self.variants: v.set_defined_in(name) - def check(self, schema, seen): - if self._tag_name: # union - self.tag_member = seen.get(c_name(self._tag_name)) - base = "'base'" - # Pointing to the base type when not implicit would be - # nice, but we don't know it here - if not self.tag_member or self._tag_name != self.tag_member.name: - raise QAPISemError( - self.info, - "discriminator '%s' is not a member of %s" - % (self._tag_name, base)) - # Here we do: - base_type = schema.lookup_type(self.tag_member.defined_in) - assert base_type - if not base_type.is_implicit(): - base = "base type '%s'" % self.tag_member.defined_in - if not isinstance(self.tag_member.type, QAPISchemaEnumType): - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must be of enum type" - % (self._tag_name, base)) - if self.tag_member.optional: - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must not be optional" - % (self._tag_name, base)) - if self.tag_member.ifcond.is_present(): - raise QAPISemError( - self.info, - "discriminator member '%s' of %s must not be conditional" - % (self._tag_name, base)) - else: # alternate - assert isinstance(self.tag_member.type, QAPISchemaEnumType) - assert not self.tag_member.optional - assert not self.tag_member.ifcond.is_present() - if self._tag_name: # union - # branches that are not explicitly covered get an empty type - cases = {v.name for v in self.variants} - for m in self.tag_member.type.members: - if m.name not in cases: - v = QAPISchemaVariant(m.name, self.info, - 'q_empty', m.ifcond) - v.set_defined_in(self.tag_member.defined_in) - self.variants.append(v) + # pylint: disable=unused-argument + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: + for v in self.variants: + v.check(schema) + + +class QAPISchemaBranches(QAPISchemaVariants): + def __init__(self, + info: QAPISourceInfo, + variants: List[QAPISchemaVariant], + tag_name: str): + super().__init__(info, variants) + self._tag_name = tag_name + + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: + # We need to narrow the member type: + tag_member = seen.get(c_name(self._tag_name)) + assert (tag_member is None + or isinstance(tag_member, QAPISchemaObjectTypeMember)) + + base = "'base'" + # Pointing to the base type when not implicit would be + # nice, but we don't know it here + if not tag_member or self._tag_name != tag_member.name: + raise QAPISemError( + self.info, + "discriminator '%s' is not a member of %s" + % (self._tag_name, base)) + self.tag_member = tag_member + # Here we do: + assert tag_member.defined_in + base_type = schema.lookup_type(tag_member.defined_in) + assert base_type + if not base_type.is_implicit(): + base = "base type '%s'" % tag_member.defined_in + if not isinstance(tag_member.type, QAPISchemaEnumType): + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must be of enum type" + % (self._tag_name, base)) + if tag_member.optional: + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must not be optional" + % (self._tag_name, base)) + if tag_member.ifcond.is_present(): + raise QAPISemError( + self.info, + "discriminator member '%s' of %s must not be conditional" + % (self._tag_name, base)) + # branches that are not explicitly covered get an empty type + assert tag_member.defined_in + cases = {v.name for v in self.variants} + for m in tag_member.type.members: + if m.name not in cases: + v = QAPISchemaVariant(m.name, self.info, + 'q_empty', m.ifcond) + v.set_defined_in(tag_member.defined_in) + self.variants.append(v) if not self.variants: raise QAPISemError(self.info, "union has no branches") for v in self.variants: @@ -637,42 +800,73 @@ class QAPISchemaVariants: # Union names must match enum values; alternate names are # checked separately. Use 'seen' to tell the two apart. if seen: - if v.name not in self.tag_member.type.member_names(): + if v.name not in tag_member.type.member_names(): raise QAPISemError( 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): + % (v.name, tag_member.type.describe())) + if not isinstance(v.type, QAPISchemaObjectType): raise QAPISemError( self.info, "%s cannot use %s" % (v.describe(self.info), v.type.describe())) v.type.check(schema) - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: for v in self.variants: # Reset seen map for each variant, since qapi names from one - # branch do not affect another branch + # branch do not affect another branch. + # + # v.type's typing is enforced in check() above. + assert isinstance(v.type, QAPISchemaObjectType) v.type.check_clash(info, dict(seen)) +class QAPISchemaAlternatives(QAPISchemaVariants): + def __init__(self, + info: QAPISourceInfo, + variants: List[QAPISchemaVariant], + tag_member: QAPISchemaObjectTypeMember): + super().__init__(info, variants) + self.tag_member = tag_member + + def check( + self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] + ) -> None: + super().check(schema, seen) + assert isinstance(self.tag_member.type, QAPISchemaEnumType) + assert not self.tag_member.optional + assert not self.tag_member.ifcond.is_present() + + class QAPISchemaMember: """ Represents object members, enum members and features """ role = 'member' - def __init__(self, name, info, ifcond=None): - assert isinstance(name, str) + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: Optional[QAPISchemaIfCond] = None, + ): self.name = name self.info = info self.ifcond = ifcond or QAPISchemaIfCond() - self.defined_in = None + self.defined_in: Optional[str] = None - def set_defined_in(self, name): + def set_defined_in(self, name: str) -> None: assert not self.defined_in self.defined_in = name - def check_clash(self, info, seen): + def check_clash( + self, + info: Optional[QAPISourceInfo], + seen: Dict[str, QAPISchemaMember], + ) -> None: cname = c_name(self.name) if cname in seen: raise QAPISemError( @@ -681,12 +875,13 @@ class QAPISchemaMember: % (self.describe(info), seen[cname].describe(info))) seen[cname] = self - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: if doc: doc.connect_member(self) - def describe(self, info): + def describe(self, info: Optional[QAPISourceInfo]) -> str: role = self.role + meta = 'type' defined_in = self.defined_in assert defined_in @@ -698,27 +893,37 @@ 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) + + assert info is not None + if defined_in != info.defn_name: + return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) return "%s '%s'" % (role, self.name) class QAPISchemaEnumMember(QAPISchemaMember): role = 'value' - def __init__(self, name, info, ifcond=None, features=None): + def __init__( + self, + name: str, + info: Optional[QAPISourceInfo], + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): super().__init__(name, info, ifcond) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self.features = features or [] - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: super().connect_doc(doc) if doc: for f in self.features: @@ -728,32 +933,40 @@ class QAPISchemaEnumMember(QAPISchemaMember): class QAPISchemaFeature(QAPISchemaMember): role = 'feature' - def is_special(self): + def is_special(self) -> bool: return self.name in ('deprecated', 'unstable') class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, info, typ, optional, ifcond=None, features=None): + def __init__( + self, + name: str, + info: QAPISourceInfo, + typ: str, + optional: bool, + ifcond: Optional[QAPISchemaIfCond] = None, + features: Optional[List[QAPISchemaFeature]] = None, + ): super().__init__(name, info, ifcond) - assert isinstance(typ, str) - assert isinstance(optional, bool) for f in features or []: - assert isinstance(f, QAPISchemaFeature) f.set_defined_in(name) self._type_name = typ - self.type = None + self.type: QAPISchemaType # set during check() self.optional = optional self.features = features or [] - def check(self, schema): + def need_has(self) -> bool: + return self.optional and self.type.need_has_if_optional() + + def check(self, schema: QAPISchema) -> None: assert self.defined_in self.type = schema.resolve_type(self._type_name, self.info, self.describe) - seen = {} + seen: Dict[str, QAPISchemaMember] = {} for f in self.features: f.check_clash(self.info, seen) - def connect_doc(self, doc): + def connect_doc(self, doc: Optional[QAPIDoc]) -> None: super().connect_doc(doc) if doc: for f in self.features: @@ -763,24 +976,40 @@ class QAPISchemaObjectTypeMember(QAPISchemaMember): class QAPISchemaVariant(QAPISchemaObjectTypeMember): role = 'branch' - def __init__(self, name, info, typ, ifcond=None): + def __init__( + self, + name: str, + info: QAPISourceInfo, + typ: str, + ifcond: QAPISchemaIfCond, + ): super().__init__(name, info, typ, False, ifcond) -class QAPISchemaCommand(QAPISchemaEntity): +class QAPISchemaCommand(QAPISchemaDefinition): meta = 'command' - def __init__(self, name, info, doc, ifcond, features, - arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - coroutine): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[str], + ret_type: Optional[str], + gen: bool, + success_response: bool, + boxed: bool, + allow_oob: bool, + allow_preconfig: bool, + coroutine: bool, + ): super().__init__(name, info, doc, ifcond, features) - assert not arg_type or isinstance(arg_type, str) - assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type - self.arg_type = None + self.arg_type: Optional[QAPISchemaObjectType] = None self._ret_type_name = ret_type - self.ret_type = None + self.ret_type: Optional[QAPISchemaType] = None self.gen = gen self.success_response = success_response self.boxed = boxed @@ -788,43 +1017,49 @@ class QAPISchemaCommand(QAPISchemaEntity): self.allow_preconfig = allow_preconfig self.coroutine = coroutine - def check(self, schema): + def check(self, schema: QAPISchema) -> None: + assert self.info is not None super().check(schema) if self._arg_type_name: - self.arg_type = schema.resolve_type( + arg_type = schema.resolve_type( self._arg_type_name, self.info, "command's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): + if not isinstance(arg_type, QAPISchemaObjectType): raise QAPISemError( self.info, "command's 'data' cannot take %s" - % self.arg_type.describe()) - if self.arg_type.variants and not self.boxed: + % arg_type.describe()) + self.arg_type = arg_type + if self.arg_type.branches and not self.boxed: raise QAPISemError( 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'") if self.name not in self.info.pragma.command_returns_exceptions: typ = self.ret_type if isinstance(typ, QAPISchemaArrayType): - typ = self.ret_type.element_type - assert typ + typ = typ.element_type if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, "command's 'returns' cannot take %s" % self.ret_type.describe()) - def connect_doc(self, doc=None): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_command( self.name, self.info, self.ifcond, self.features, @@ -833,40 +1068,54 @@ class QAPISchemaCommand(QAPISchemaEntity): self.coroutine) -class QAPISchemaEvent(QAPISchemaEntity): +class QAPISchemaEvent(QAPISchemaDefinition): meta = 'event' - def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + def __init__( + self, + name: str, + info: QAPISourceInfo, + doc: Optional[QAPIDoc], + ifcond: QAPISchemaIfCond, + features: List[QAPISchemaFeature], + arg_type: Optional[str], + boxed: bool, + ): super().__init__(name, info, doc, ifcond, features) - assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type - self.arg_type = None + self.arg_type: Optional[QAPISchemaObjectType] = None self.boxed = boxed - def check(self, schema): + def check(self, schema: QAPISchema) -> None: super().check(schema) if self._arg_type_name: - self.arg_type = schema.resolve_type( + typ = schema.resolve_type( self._arg_type_name, self.info, "event's 'data'") - if not isinstance(self.arg_type, QAPISchemaObjectType): + if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, "event's 'data' cannot take %s" - % self.arg_type.describe()) - if self.arg_type.variants and not self.boxed: + % typ.describe()) + self.arg_type = typ + if self.arg_type.branches and not self.boxed: raise QAPISemError( 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): + def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: super().connect_doc(doc) doc = doc or self.doc if doc: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) - def visit(self, visitor): + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_event( self.name, self.info, self.ifcond, self.features, @@ -874,7 +1123,7 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema: - def __init__(self, fname): + def __init__(self, fname: str): self.fname = fname try: @@ -886,9 +1135,9 @@ class QAPISchema: exprs = check_exprs(parser.exprs) self.docs = parser.docs - self._entity_list = [] - self._entity_dict = {} - self._module_dict = OrderedDict() + self._entity_list: List[QAPISchemaEntity] = [] + self._entity_dict: Dict[str, QAPISchemaDefinition] = {} + self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() self._schema_dir = os.path.dirname(fname) self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) self._make_module(fname) @@ -898,37 +1147,44 @@ class QAPISchema: self._def_exprs(exprs) self.check() - def _def_entity(self, ent): - # Only the predefined types are allowed to not have info - assert ent.info or self._predefining + def _def_entity(self, ent: QAPISchemaEntity) -> None: self._entity_list.append(ent) - if ent.name is None: - return + + def _def_definition(self, defn: QAPISchemaDefinition) -> None: + # Only the predefined types are allowed to not have info + assert defn.info or self._predefining + self._def_entity(defn) # TODO reject names that differ only in '_' vs. '.' vs. '-', # because they're liable to clash in generated C. - other_ent = self._entity_dict.get(ent.name) - if other_ent: - if other_ent.info: - where = QAPISourceError(other_ent.info, "previous definition") + other_defn = self._entity_dict.get(defn.name) + if other_defn: + if other_defn.info: + where = QAPISourceError(other_defn.info, "previous definition") raise QAPISemError( - ent.info, - "'%s' is already defined\n%s" % (ent.name, where)) + defn.info, + "'%s' is already defined\n%s" % (defn.name, where)) raise QAPISemError( - ent.info, "%s is already defined" % other_ent.describe()) - self._entity_dict[ent.name] = ent + defn.info, "%s is already defined" % other_defn.describe()) + self._entity_dict[defn.name] = defn - def lookup_entity(self, name, typ=None): - ent = self._entity_dict.get(name) - if typ and not isinstance(ent, typ): - return None - return ent + def lookup_entity(self, name: str) -> Optional[QAPISchemaEntity]: + return self._entity_dict.get(name) - def lookup_type(self, name): - return self.lookup_entity(name, QAPISchemaType) + def lookup_type(self, name: str) -> Optional[QAPISchemaType]: + typ = self.lookup_entity(name) + if isinstance(typ, QAPISchemaType): + return typ + return None - def resolve_type(self, name, info, what): + def resolve_type( + self, + name: str, + info: Optional[QAPISourceInfo], + what: Union[None, str, Callable[[QAPISourceInfo], str]], + ) -> QAPISchemaType: typ = self.lookup_type(name) if not typ: + assert info and what # built-in types must not fail lookup if callable(what): what = what(info) raise QAPISemError( @@ -940,30 +1196,33 @@ class QAPISchema: return fname return os.path.relpath(fname, self._schema_dir) - def _make_module(self, fname): + def _make_module(self, fname: str) -> QAPISchemaModule: name = self._module_name(fname) if name not in self._module_dict: self._module_dict[name] = QAPISchemaModule(name) return self._module_dict[name] - def module_by_fname(self, fname): + def module_by_fname(self, fname: str) -> QAPISchemaModule: name = self._module_name(fname) return self._module_dict[name] - def _def_include(self, expr, info, doc): + def _def_include(self, expr: QAPIExpression) -> None: 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)) + def _def_builtin_type( + self, name: str, json_type: str, c_type: str + ) -> None: + self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type)) # Instantiating only the arrays that are actually used would # be nice, but we can't as long as their generated code # (qapi-builtin-types.[ch]) may be shared by some other # schema. self._make_array_type(name, None) - def _def_predefineds(self): + def _def_predefineds(self) -> None: for t in [('str', 'string', 'char' + POINTER_SUFFIX), ('number', 'number', 'double'), ('int', 'int', 'int64_t'), @@ -982,66 +1241,96 @@ class QAPISchema: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( 'q_empty', None, None, None, None, None, [], None) - self._def_entity(self.the_empty_object_type) + self._def_definition(self.the_empty_object_type) qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] qtype_values = self._make_enum_members( [{'name': n} for n in qtypes], None) - self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, - qtype_values, 'QTYPE')) + self._def_definition(QAPISchemaEnumType( + 'QType', None, None, None, None, qtype_values, None)) - def _make_features(self, features, info): + def _make_features( + self, + features: Optional[List[Dict[str, Any]]], + info: Optional[QAPISourceInfo], + ) -> List[QAPISchemaFeature]: if features is None: return [] return [QAPISchemaFeature(f['name'], info, QAPISchemaIfCond(f.get('if'))) for f in features] - def _make_enum_member(self, name, ifcond, features, info): + def _make_enum_member( + self, + name: str, + ifcond: Optional[Union[str, Dict[str, Any]]], + features: Optional[List[Dict[str, Any]]], + info: Optional[QAPISourceInfo], + ) -> QAPISchemaEnumMember: return QAPISchemaEnumMember(name, info, QAPISchemaIfCond(ifcond), self._make_features(features, info)) - def _make_enum_members(self, values, info): + def _make_enum_members( + self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo] + ) -> List[QAPISchemaEnumMember]: return [self._make_enum_member(v['name'], v.get('if'), v.get('features'), info) for v in values] - def _make_array_type(self, element_type, info): + def _make_array_type( + self, element_type: str, info: Optional[QAPISourceInfo] + ) -> str: name = element_type + 'List' # reserved by check_defn_name_str() if not self.lookup_type(name): - self._def_entity(QAPISchemaArrayType(name, info, element_type)) + self._def_definition(QAPISchemaArrayType( + name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, ifcond, role, members): + def _make_implicit_object_type( + self, + name: str, + info: QAPISourceInfo, + ifcond: QAPISchemaIfCond, + role: str, + members: List[QAPISchemaObjectTypeMember], + ) -> Optional[str]: if not members: return None # See also QAPISchemaObjectTypeMember.describe() name = 'q_obj_%s-%s' % (name, role) - typ = self.lookup_entity(name, QAPISchemaObjectType) + typ = self.lookup_entity(name) if typ: + assert isinstance(typ, QAPISchemaObjectType) # The implicit object type has multiple users. This can # only be a duplicate definition, which will be flagged # later. - pass else: - self._def_entity(QAPISchemaObjectType( + self._def_definition(QAPISchemaObjectType( 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) -> None: 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, + self._def_definition(QAPISchemaEnumType( + name, info, expr.doc, ifcond, features, self._make_enum_members(data, info), prefix)) - def _make_member(self, name, typ, ifcond, features, info): + def _make_member( + self, + name: str, + typ: Union[List[str], str], + ifcond: QAPISchemaIfCond, + features: Optional[List[Dict[str, Any]]], + info: QAPISourceInfo, + ) -> QAPISchemaObjectTypeMember: optional = False if name.startswith('*'): name = name[1:] @@ -1052,34 +1341,47 @@ class QAPISchema: return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, self._make_features(features, info)) - def _make_members(self, data, info): + def _make_members( + self, + data: Dict[str, Any], + info: QAPISourceInfo, + ) -> List[QAPISchemaObjectTypeMember]: return [self._make_member(key, value['type'], QAPISchemaIfCond(value.get('if')), 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) -> None: 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, + self._def_definition(QAPISchemaObjectType( + name, info, expr.doc, ifcond, features, base, self._make_members(data, info), None)) - def _make_variant(self, case, typ, ifcond, info): + def _make_variant( + self, + case: str, + typ: str, + ifcond: QAPISchemaIfCond, + info: QAPISourceInfo, + ) -> QAPISchemaVariant: if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) return QAPISchemaVariant(case, info, typ, ifcond) - def _def_union_type(self, expr, info, doc): + def _def_union_type(self, expr: QAPIExpression) -> None: 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 +1393,19 @@ class QAPISchema: QAPISchemaIfCond(value.get('if')), info) for (key, value) in data.items()] - members = [] - self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, features, + members: List[QAPISchemaObjectTypeMember] = [] + self._def_definition( + QAPISchemaObjectType(name, info, expr.doc, ifcond, features, base, members, - QAPISchemaVariants( - tag_name, info, None, variants))) + QAPISchemaBranches( + info, variants, tag_name))) - def _def_alternate_type(self, expr, info, doc): + def _def_alternate_type(self, expr: QAPIExpression) -> None: 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'], @@ -1109,12 +1413,12 @@ class QAPISchema: info) 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))) + self._def_definition( + QAPISchemaAlternateType( + name, info, expr.doc, ifcond, features, + QAPISchemaAlternatives(info, variants, tag_member))) - def _def_command(self, expr, info, doc): + def _def_command(self, expr: QAPIExpression) -> None: name = expr['command'] data = expr.get('data') rets = expr.get('returns') @@ -1125,6 +1429,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,56 +1438,54 @@ 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, - gen, success_response, - boxed, allow_oob, allow_preconfig, - coroutine)) + self._def_definition( + 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) -> None: 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_definition(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') + def _def_exprs(self, exprs: List[QAPIExpression]) -> None: + 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 - def check(self): + def check(self) -> None: 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): + def visit(self, visitor: QAPISchemaVisitor) -> None: visitor.visit_begin(self) for mod in self._module_dict.values(): mod.visit(visitor) 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..0dd0b00ada 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -23,6 +23,8 @@ from .gen import ( ) from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaEnumMember, QAPISchemaFeature, QAPISchemaIfCond, @@ -142,7 +144,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; ''', @@ -169,7 +171,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond, if not isinstance(obj, QAPISchemaObjectType): continue ret += gen_object(obj.name, obj.ifcond, obj.base, - obj.local_members, obj.variants) + obj.local_members, obj.branches) ret += mcgen(''' @@ -348,13 +350,13 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> None: + branches: Optional[QAPISchemaBranches]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, ifcond, base, members, variants)) + self._genh.add(gen_object(name, ifcond, base, members, branches)) with ifcontext(ifcond, self._genh, self._genc): if base and not base.is_implicit(): self._genh.add(gen_upcast(name, base)) @@ -369,11 +371,11 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants) -> None: + alternatives: QAPISchemaAlternatives) -> None: with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, - [variants.tag_member], variants)) + [alternatives.tag_member], alternatives)) with ifcontext(ifcond, self._genh, self._genc): self._gen_type_cleanup(name) diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 380fa197f5..12f92e429f 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -28,6 +28,8 @@ from .gen import ( ) from .schema import ( QAPISchema, + QAPISchemaAlternatives, + QAPISchemaBranches, QAPISchemaEnumMember, QAPISchemaEnumType, QAPISchemaFeature, @@ -35,7 +37,6 @@ from .schema import ( QAPISchemaObjectType, QAPISchemaObjectTypeMember, QAPISchemaType, - QAPISchemaVariants, ) from .source import QAPISourceInfo @@ -63,7 +64,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp); def gen_visit_object_members(name: str, base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> str: + branches: Optional[QAPISchemaBranches]) -> str: ret = mcgen(''' bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) @@ -71,6 +72,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 +95,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': @@ -116,8 +132,8 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''') ret += memb.ifcond.gen_endif() - if variants: - tag_member = variants.tag_member + if branches: + tag_member = branches.tag_member assert isinstance(tag_member.type, QAPISchemaEnumType) ret += mcgen(''' @@ -125,7 +141,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ''', c_name=c_name(tag_member.name)) - for var in variants.variants: + for var in branches.variants: case_str = c_enum_const(tag_member.type.name, var.name, tag_member.type.prefix) ret += var.ifcond.gen_if() @@ -207,7 +223,8 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, c_name=c_name(name)) -def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str: +def gen_visit_alternate(name: str, + alternatives: QAPISchemaAlternatives) -> str: ret = mcgen(''' bool visit_type_%(c_name)s(Visitor *v, const char *name, @@ -229,7 +246,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, ''', c_name=c_name(name)) - for var in variants.variants: + for var in alternatives.variants: ret += var.ifcond.gen_if() ret += mcgen(''' case %(case)s: @@ -263,8 +280,9 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, abort(); default: assert(visit_is_input(v)); - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", - "%(name)s"); + error_setg(errp, + "Invalid parameter type for '%%s', expected: %(name)s", + name ? name : "null"); /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */ g_free(*obj); *obj = NULL; @@ -341,7 +359,6 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "%(visit)s.h" ''', visit=visit)) @@ -379,14 +396,14 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): features: List[QAPISchemaFeature], base: Optional[QAPISchemaObjectType], members: List[QAPISchemaObjectTypeMember], - variants: Optional[QAPISchemaVariants]) -> None: + branches: Optional[QAPISchemaBranches]) -> None: # Nothing to do for the special empty builtin if name == 'q_empty': return with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_members_decl(name)) self._genc.add(gen_visit_object_members(name, base, - members, variants)) + members, branches)) # TODO Worth changing the visitor signature, so we could # directly use rather than repeat type.is_implicit()? if not name.startswith('q_'): @@ -399,10 +416,10 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): info: Optional[QAPISourceInfo], ifcond: QAPISchemaIfCond, features: List[QAPISchemaFeature], - variants: QAPISchemaVariants) -> None: + alternatives: QAPISchemaAlternatives) -> None: with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_alternate(name, variants)) + self._genc.add(gen_visit_alternate(name, alternatives)) def gen_visit(schema: QAPISchema, diff --git a/scripts/qemu-plugin-symbols.py b/scripts/qemu-plugin-symbols.py new file mode 100755 index 0000000000..e285ebb8f9 --- /dev/null +++ b/scripts/qemu-plugin-symbols.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Extract QEMU Plugin API symbols from a header file +# +# Copyright 2024 Linaro Ltd +# +# Author: Pierrick Bouvier +# +# 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 + +def extract_symbols(plugin_header): + with open(plugin_header) as file: + content = file.read() + # Remove QEMU_PLUGIN_API macro definition. + content = content.replace('#define QEMU_PLUGIN_API', '') + expected = content.count('QEMU_PLUGIN_API') + # Find last word between QEMU_PLUGIN_API and (, matching on several lines. + # We use *? non-greedy quantifier. + syms = re.findall(r'QEMU_PLUGIN_API.*?(\w+)\s*\(', content, re.DOTALL) + syms.sort() + # Ensure we found as many symbols as API markers. + assert len(syms) == expected + return syms + +def main() -> None: + parser = argparse.ArgumentParser(description='Extract QEMU plugin symbols') + parser.add_argument('plugin_header', help='Path to QEMU plugin header.') + args = parser.parse_args() + + syms = extract_symbols(args.plugin_header) + + print('{') + for s in syms: + print(" {};".format(s)) + print('};') + +if __name__ == '__main__': + main() 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..4ce7ff51cc 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -20,7 +20,10 @@ import argparse import struct +import os +import sys 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 +85,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") @@ -92,7 +101,7 @@ def call_decode(table, index, dumpfile): print("Could not decode index: %d" % (index)) print("Entry is: %s" % (decoder)) print("Decode Table is:\n%s" % (table)) - return False + raise(Exception("unknown event")) else: return decoder.fn(decoder.eid, decoder.name, dumpfile) @@ -111,9 +120,14 @@ 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 + raise(Exception("unhandled event")) + +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): @@ -122,6 +136,30 @@ def swallow_async_qword(eid, name, dumpfile): print(" %s(%d) @ %d" % (name, eid, step_id)) return True +def swallow_bytes(eid, name, dumpfile, nr): + """Swallow nr bytes of data without looking at it""" + dumpfile.seek(nr, os.SEEK_CUR) + +total_insns = 0 + +def decode_instruction(eid, name, dumpfile): + global total_insns + ins_diff = read_dword(dumpfile) + total_insns += ins_diff + print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns)) + return True + +def decode_interrupt(eid, name, dumpfile): + print_event(eid, name) + return True + +def decode_exception(eid, name, dumpfile): + print_event(eid, name) + return True + +# v12 does away with the additional event byte and encodes it in the main type +# Between v8 and v9, REPLAY_ASYNC_BH_ONESHOT was added, but we don't decode +# those versions so leave it out. async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), Decoder(1, "REPLAY_ASYNC_INPUT", decode_unimp), Decoder(2, "REPLAY_ASYNC_INPUT_SYNC", decode_unimp), @@ -130,8 +168,8 @@ async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), Decoder(5, "REPLAY_ASYNC_EVENT_NET", decode_unimp), ] # See replay_read_events/replay_read_event -def decode_async(eid, name, dumpfile): - """Decode an ASYNC event""" +def decode_async_old(eid, name, dumpfile): + """Decode an ASYNC event (pre-v8)""" print_event(eid, name) @@ -145,10 +183,43 @@ def decode_async(eid, name, dumpfile): return call_decode(async_decode_table, async_event_kind, dumpfile) +def decode_async_bh(eid, name, dumpfile): + op_id = read_qword(dumpfile) + print_event(eid, name) + return True -def decode_instruction(eid, name, dumpfile): - ins_diff = read_dword(dumpfile) - print_event(eid, name, "0x%x" % (ins_diff)) +def decode_async_bh_oneshot(eid, name, dumpfile): + op_id = read_qword(dumpfile) + print_event(eid, name) + return True + +def decode_async_char_read(eid, name, dumpfile): + char_id = read_byte(dumpfile) + size = read_dword(dumpfile) + print_event(eid, name, "device:%x chars:%s" % (char_id, dumpfile.read(size))) + return True + +def decode_async_block(eid, name, dumpfile): + op_id = read_qword(dumpfile) + print_event(eid, name) + return True + +def decode_async_net(eid, name, dumpfile): + net_id = read_byte(dumpfile) + flags = read_dword(dumpfile) + size = read_dword(dumpfile) + swallow_bytes(eid, name, dumpfile, size) + print_event(eid, name, "net:%x flags:%x bytes:%d" % (net_id, flags, size)) + return True + +def decode_shutdown(eid, name, dumpfile): + print_event(eid, name) + 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): @@ -156,7 +227,22 @@ def decode_audio_out(eid, name, dumpfile): print_event(eid, name, "%d" % (audio_data)) return True -def decode_checkpoint(eid, name, dumpfile): +def decode_random(eid, name, dumpfile): + ret = read_dword(dumpfile) + size = read_dword(dumpfile) + swallow_bytes(eid, name, dumpfile, size) + if (ret): + print_event(eid, name, "%d bytes (getrandom failed)" % (size)) + else: + print_event(eid, name, "%d bytes" % (size)) + return True + +def decode_clock(eid, name, dumpfile): + clock_data = read_qword(dumpfile) + print_event(eid, name, "0x%x" % (clock_data)) + return True + +def __decode_checkpoint(eid, name, dumpfile, old): """Decode a checkpoint. Checkpoints contain a series of async events with their own specific data. @@ -168,35 +254,35 @@ def decode_checkpoint(eid, name, dumpfile): # if the next event is EVENT_ASYNC there are a bunch of # async events to read, otherwise we are done - if next_event != 3: - print_event(eid, name, "no additional data", event_number) - else: + if (old and next_event == 3) or (not old and next_event >= 3 and next_event <= 9): print_event(eid, name, "more data follows", event_number) + else: + print_event(eid, name, "no additional data", event_number) replay_state.reuse_event(next_event) return True +def decode_checkpoint_old(eid, name, dumpfile): + return __decode_checkpoint(eid, name, dumpfile, False) + +def decode_checkpoint(eid, name, dumpfile): + return __decode_checkpoint(eid, name, dumpfile, True) + def decode_checkpoint_init(eid, name, dumpfile): print_event(eid, name) return True -def decode_interrupt(eid, name, dumpfile): +def decode_end(eid, name, dumpfile): print_event(eid, name) - return True - -def decode_clock(eid, name, dumpfile): - clock_data = read_qword(dumpfile) - print_event(eid, name, "0x%x" % (clock_data)) - return True - + return False # 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(3, "EVENT_ASYNC", decode_async), + Decoder(2, "EVENT_EXCEPTION", decode_plain), + Decoder(3, "EVENT_ASYNC", decode_async_old), 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 +301,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(3, "EVENT_ASYNC", decode_async), + Decoder(2, "EVENT_EXCEPTION", decode_plain), + Decoder(3, "EVENT_ASYNC", decode_async_old), 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), @@ -240,7 +326,7 @@ v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction), Decoder(1, "EVENT_INTERRUPT", decode_interrupt), Decoder(2, "EVENT_EXCEPTION", decode_unimp), - Decoder(3, "EVENT_ASYNC", decode_async), + Decoder(3, "EVENT_ASYNC", decode_async_old), Decoder(4, "EVENT_SHUTDOWN", decode_unimp), Decoder(5, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp), Decoder(6, "EVENT_SHUTDOWN_HOST_QMP", decode_unimp), @@ -250,7 +336,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 +354,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_exception), + Decoder(3, "EVENT_ASYNC_BH", decode_async_bh), + Decoder(4, "EVENT_ASYNC_BH_ONESHOT", decode_async_bh_oneshot), + Decoder(5, "EVENT_ASYNC_INPUT", decode_unimp), + Decoder(6, "EVENT_ASYNC_INPUT_SYNC", decode_unimp), + Decoder(7, "EVENT_ASYNC_CHAR_READ", decode_async_char_read), + Decoder(8, "EVENT_ASYNC_BLOCK", decode_async_block), + Decoder(9, "EVENT_ASYNC_NET", decode_async_net), + Decoder(10, "EVENT_SHUTDOWN", decode_shutdown), + Decoder(11, "EVENT_SHUTDOWN_HOST_ERR", decode_shutdown), + Decoder(12, "EVENT_SHUTDOWN_HOST_QMP_QUIT", decode_shutdown), + Decoder(13, "EVENT_SHUTDOWN_HOST_QMP_RESET", decode_shutdown), + Decoder(14, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_shutdown), + Decoder(15, "EVENT_SHUTDOWN_HOST_UI", decode_shutdown), + Decoder(16, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_shutdown), + Decoder(17, "EVENT_SHUTDOWN_GUEST_RESET", decode_shutdown), + Decoder(18, "EVENT_SHUTDOWN_GUEST_PANIC", decode_shutdown), + Decoder(19, "EVENT_SHUTDOWN_SUBSYS_RESET", decode_shutdown), + Decoder(20, "EVENT_SHUTDOWN_SNAPSHOT_LOAD", decode_shutdown), + Decoder(21, "EVENT_SHUTDOWN___MAX", decode_shutdown), + 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_OUT", decode_audio_out), + Decoder(26, "EVENT_AUDIO_IN", decode_unimp), + 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), + Decoder(39, "EVENT_END", decode_end), +] + def parse_arguments(): "Grab arguments for script" parser = argparse.ArgumentParser() @@ -278,14 +406,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 +431,14 @@ 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}") + sys.exit(1) + finally: + print(f"Reached {dumpfile.tell()} of {dumpsize} bytes") dumpfile.close() if __name__ == "__main__": diff --git a/scripts/rust/rust_root_crate.sh b/scripts/rust/rust_root_crate.sh new file mode 100755 index 0000000000..975bddf7f1 --- /dev/null +++ b/scripts/rust/rust_root_crate.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -eu + +cat < + +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 argparse +import logging + +from typing import List + + +def generate_cfg_flags(header: str) -> List[str]: + """Converts defines from config[..].h headers to rustc --cfg flags.""" + + def cfg_name(name: str) -> str: + """Filter function for C #defines""" + if ( + name.startswith("CONFIG_") + or name.startswith("TARGET_") + or name.startswith("HAVE_") + ): + return name + return "" + + with open(header, encoding="utf-8") as cfg: + config = [l.split()[1:] for l in cfg if l.startswith("#define")] + + cfg_list = [] + for cfg in config: + name = cfg_name(cfg[0]) + if not name: + continue + if len(cfg) >= 2 and cfg[1] != "1": + continue + cfg_list.append("--cfg") + cfg_list.append(name) + return cfg_list + + +def main() -> None: + # pylint: disable=missing-function-docstring + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", action="store_true") + parser.add_argument( + "--config-headers", + metavar="CONFIG_HEADER", + action="append", + dest="config_headers", + help="paths to any configuration C headers (*.h files), if any", + required=False, + default=[], + ) + args = parser.parse_args() + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + logging.debug("args: %s", args) + for header in args.config_headers: + for tok in generate_cfg_flags(header): + print(tok) + + +if __name__ == "__main__": + main() 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 index 67cb86dd52..b72563895c 100644 --- a/scripts/symlink-install-tree.py +++ b/scripts/symlink-install-tree.py @@ -4,6 +4,7 @@ from pathlib import PurePath import errno import json import os +import shlex import subprocess import sys @@ -14,7 +15,7 @@ def destdir_join(d1: str, d2: str) -> str: return str(PurePath(d1, *PurePath(d2).parts[1:])) introspect = os.environ.get('MESONINTROSPECT') -out = subprocess.run([*introspect.split(' '), '--installed'], +out = subprocess.run([*shlex.split(introspect), '--installed'], stdout=subprocess.PIPE, check=True).stdout for source, dest in json.loads(out).items(): bundle_dest = destdir_join('qemu-bundle', dest) @@ -28,5 +29,8 @@ for source, dest in json.loads(out).items(): 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..bc03238c0f 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"]) @@ -314,18 +301,14 @@ class Event(object): if fmt.endswith(r'\n"'): raise ValueError("Event format must not end with a newline " "character") + if '\\n' in fmt: + raise ValueError("Event format must not use new line character") if len(fmt_trans) > 0: fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) - event = Event(name, props, fmt, args, lineno, filename) - - # add implicit arguments when using the 'vcpu' property - import tracetool.vcpu - event = tracetool.vcpu.transform_event(event) - - return event + return Event(name, props, fmt, args, lineno, filename) def __repr__(self): """Evaluable string representation for this object.""" @@ -339,7 +322,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 +341,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/tracetool/vcpu.py b/scripts/tracetool/vcpu.py deleted file mode 100644 index d232cb1d06..0000000000 --- a/scripts/tracetool/vcpu.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Generic management for the 'vcpu' property. - -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2016, Lluís Vilanova " -__license__ = "GPL version 2 or (at your option) any later version" - -__maintainer__ = "Stefan Hajnoczi" -__email__ = "stefanha@redhat.com" - - -from tracetool import Arguments, try_import - - -def transform_event(event): - """Transform event to comply with the 'vcpu' property (if present).""" - if "vcpu" in event.properties: - event.args = Arguments([("void *", "__cpu"), event.args]) - fmt = "\"cpu=%p \"" - event.fmt = fmt + event.fmt - return event - - -def transform_args(format, event, *args, **kwargs): - """Transforms the arguments to suit the specified format. - - The format module must implement function 'vcpu_args', which receives the - implicit arguments added by the 'vcpu' property, and must return suitable - arguments for the given format. - - The function is only called for events with the 'vcpu' property. - - Parameters - ========== - format : str - Format module name. - event : Event - args, kwargs - Passed to 'vcpu_transform_args'. - - Returns - ======= - Arguments - The transformed arguments, including the non-implicit ones. - - """ - if "vcpu" in event.properties: - ok, func = try_import("tracetool.format." + format, - "vcpu_transform_args") - assert ok - assert func - return Arguments([func(event.args[:1], *args, **kwargs), - event.args[1:]]) - else: - return event.args diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index b1ad99cba8..99a8d9fa4c 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -27,6 +27,8 @@ # types like "__u64". This work is done in the cp_portable function. tmpdir=$(mktemp -d) +hdrdir="$tmpdir/headers" +blddir="$tmpdir/build" linux="$1" output="$2" @@ -55,13 +57,13 @@ cp_portable() { -e 'linux/if_ether' \ -e 'input-event-codes' \ -e 'sys/' \ - -e 'pvrdma_verbs' \ -e 'drm.h' \ -e 'limits' \ -e 'linux/const' \ -e 'linux/kernel' \ -e 'linux/sysinfo' \ - -e 'asm-generic/kvm_para' \ + -e 'asm/setup_data.h' \ + -e 'asm/kvm_para.h' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -69,6 +71,15 @@ cp_portable() { fi header=$(basename "$f"); + + if test -z "$arch"; then + # Let users of include/standard-headers/linux/ headers pick the + # asm-* header that they care about + arch_cmd='/]*\)>/d' + else + arch_cmd='s/]*\)>/"standard-headers\/asm-'$arch'\/\1"/' + fi + sed -e 's/__aligned_u64/__u64 __attribute__((aligned(8)))/g' \ -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/u\([0-9][0-9]*\)/uint\1_t/g' \ @@ -77,6 +88,7 @@ cp_portable() { -e 's/__be\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/"\(input-event-codes\.h\)"/"standard-headers\/linux\/\1"/' \ -e 's/]*\)>/"standard-headers\/linux\/\1"/' \ + -e "$arch_cmd" \ -e 's/__bitwise//' \ -e 's/__attribute__((packed))/QEMU_PACKED/' \ -e 's/__inline__/inline/' \ @@ -110,65 +122,92 @@ for arch in $ARCHLIST; do arch_var=ARCH fi - make -C "$linux" INSTALL_HDR_PATH="$tmpdir" $arch_var=$arch headers_install + rm -rf "$hdrdir" + make -C "$linux" O="$blddir" INSTALL_HDR_PATH="$hdrdir" $arch_var=$arch headers_install rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" for header in kvm.h unistd.h bitsperlong.h mman.h; do - cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" + if test -f "$hdrdir/include/asm/$header"; then + cp "$hdrdir/include/asm/$header" "$output/linux-headers/asm-$arch" + elif test -f "$hdrdir/include/asm-generic/$header"; then + # not installed as , but used as such in kernel sources + cat <$output/linux-headers/asm-$arch/$header +#include +EOF + fi done if [ $arch = mips ]; then - cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" - cp "$tmpdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/" - cp "$tmpdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/" - cp "$tmpdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/" + cp "$hdrdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/" fi if [ $arch = powerpc ]; then - cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/" - cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/" fi rm -rf "$output/include/standard-headers/asm-$arch" mkdir -p "$output/include/standard-headers/asm-$arch" if [ $arch = s390 ]; then - cp_portable "$tmpdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" - cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" - cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" + cp_portable "$hdrdir/include/asm/virtio-ccw.h" "$output/include/standard-headers/asm-s390/" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" fi if [ $arch = arm ]; then - cp "$tmpdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" - cp "$tmpdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/" - cp "$tmpdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" + cp "$hdrdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" + cp "$hdrdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/" + cp "$hdrdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" fi if [ $arch = arm64 ]; then - cp "$tmpdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/" + cp "$hdrdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-arm64/" fi if [ $arch = x86 ]; then - cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" - cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" - cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" - cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" + cp "$hdrdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + + cp_portable "$hdrdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" + cat <$output/linux-headers/asm-$arch/kvm_para.h +#include "standard-headers/asm-$arch/kvm_para.h" +EOF + # Remove everything except the macros from bootparam.h avoiding the # unnecessary import of several video/ist/etc headers sed -e '/__ASSEMBLY__/,/__ASSEMBLY__/d' \ - "$tmpdir/include/asm/bootparam.h" > "$tmpdir/bootparam.h" - cp_portable "$tmpdir/bootparam.h" \ + "$hdrdir/include/asm/bootparam.h" > "$hdrdir/bootparam.h" + cp_portable "$hdrdir/bootparam.h" \ "$output/include/standard-headers/asm-$arch" + cp_portable "$hdrdir/include/asm/setup_data.h" \ + "$output/include/standard-headers/asm-x86" + fi + if [ $arch = riscv ]; then + cp "$hdrdir/include/asm/ptrace.h" "$output/linux-headers/asm-riscv/" + cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-riscv/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-riscv/" + fi + if [ $arch = loongarch ]; then + cp "$hdrdir/include/asm/kvm_para.h" "$output/linux-headers/asm-loongarch/" + cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-loongarch/" fi done +arch= 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 vduse.h; do - cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" +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 bits.h; do + cp "$hdrdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" mkdir -p "$output/linux-headers/asm-generic" for header in unistd.h bitsperlong.h mman-common.h mman.h hugetlb_encode.h; do - cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" + cp "$hdrdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" done if [ -L "$linux/source" ]; then @@ -191,6 +230,10 @@ if [ -d "$linux/LICENSES" ]; then done fi +cat <$output/linux-headers/linux/kvm_para.h +#include "standard-headers/linux/kvm_para.h" +#include +EOF cat <$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF @@ -203,51 +246,28 @@ EOF rm -rf "$output/include/standard-headers/linux" mkdir -p "$output/include/standard-headers/linux" -for i in "$tmpdir"/include/linux/*virtio*.h \ - "$tmpdir/include/linux/qemu_fw_cfg.h" \ - "$tmpdir/include/linux/fuse.h" \ - "$tmpdir/include/linux/input.h" \ - "$tmpdir/include/linux/input-event-codes.h" \ - "$tmpdir/include/linux/udmabuf.h" \ - "$tmpdir/include/linux/pci_regs.h" \ - "$tmpdir/include/linux/ethtool.h" \ - "$tmpdir/include/linux/const.h" \ - "$tmpdir/include/linux/kernel.h" \ - "$tmpdir/include/linux/vhost_types.h" \ - "$tmpdir/include/linux/sysinfo.h" \ - "$tmpdir/include/misc/pvpanic.h"; do +for i in "$hdrdir"/include/linux/*virtio*.h \ + "$hdrdir/include/linux/qemu_fw_cfg.h" \ + "$hdrdir/include/linux/fuse.h" \ + "$hdrdir/include/linux/input.h" \ + "$hdrdir/include/linux/input-event-codes.h" \ + "$hdrdir/include/linux/udmabuf.h" \ + "$hdrdir/include/linux/pci_regs.h" \ + "$hdrdir/include/linux/ethtool.h" \ + "$hdrdir/include/linux/const.h" \ + "$hdrdir/include/linux/kernel.h" \ + "$hdrdir/include/linux/kvm_para.h" \ + "$hdrdir/include/linux/vhost_types.h" \ + "$hdrdir/include/linux/sysinfo.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done +mkdir -p "$output/include/standard-headers/misc" +cp_portable "$hdrdir/include/misc/pvpanic.h" \ + "$output/include/standard-headers/misc" mkdir -p "$output/include/standard-headers/drm" -cp_portable "$tmpdir/include/drm/drm_fourcc.h" \ +cp_portable "$hdrdir/include/drm/drm_fourcc.h" \ "$output/include/standard-headers/drm" -rm -rf "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma" -mkdir -p "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma" - -# Remove the unused functions from pvrdma_verbs.h avoiding the unnecessary -# import of several infiniband/networking/other headers -tmp_pvrdma_verbs="$tmpdir/pvrdma_verbs.h" -# Parse the entire file instead of single lines to match -# function declarations expanding over multiple lines -# and strip the declarations starting with pvrdma prefix. -sed -e '1h;2,$H;$!d;g' -e 's/[^};]*pvrdma[^(| ]*([^)]*);//g' \ - "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_verbs.h" > \ - "$tmp_pvrdma_verbs"; - -for i in "$linux/drivers/infiniband/hw/vmw_pvrdma/pvrdma_dev_api.h" \ - "$tmp_pvrdma_verbs"; do \ - cp_portable "$i" \ - "$output/include/standard-headers/drivers/infiniband/hw/vmw_pvrdma/" -done - -rm -rf "$output/include/standard-headers/rdma/" -mkdir -p "$output/include/standard-headers/rdma/" -for i in "$tmpdir/include/rdma/vmw_pvrdma-abi.h"; do - cp_portable "$i" \ - "$output/include/standard-headers/rdma/" -done - cat <$output/include/standard-headers/linux/types.h /* For QEMU all types are already defined via osdep.h, so this * header does not need to do anything. diff --git a/scripts/update-syscalltbl.sh b/scripts/update-syscalltbl.sh index 2d23e56800..f0927c544d 100755 --- a/scripts/update-syscalltbl.sh +++ b/scripts/update-syscalltbl.sh @@ -1,13 +1,18 @@ TBL_LIST="\ arch/alpha/kernel/syscalls/syscall.tbl,linux-user/alpha/syscall.tbl \ arch/arm/tools/syscall.tbl,linux-user/arm/syscall.tbl \ +scripts/syscall.tbl,linux-user/aarch64/syscall_64.tbl \ +scripts/syscall.tbl,linux-user/hexagon/syscall.tbl \ +scripts/syscall.tbl,linux-user/loongarch64/syscall.tbl \ arch/m68k/kernel/syscalls/syscall.tbl,linux-user/m68k/syscall.tbl \ arch/microblaze/kernel/syscalls/syscall.tbl,linux-user/microblaze/syscall.tbl \ arch/mips/kernel/syscalls/syscall_n32.tbl,linux-user/mips64/syscall_n32.tbl \ arch/mips/kernel/syscalls/syscall_n64.tbl,linux-user/mips64/syscall_n64.tbl \ arch/mips/kernel/syscalls/syscall_o32.tbl,linux-user/mips/syscall_o32.tbl \ +scripts/syscall.tbl,linux-user/openrisc/syscall.tbl \ arch/parisc/kernel/syscalls/syscall.tbl,linux-user/hppa/syscall.tbl \ arch/powerpc/kernel/syscalls/syscall.tbl,linux-user/ppc/syscall.tbl \ +scripts/syscall.tbl,linux-user/riscv/syscall.tbl \ arch/s390/kernel/syscalls/syscall.tbl,linux-user/s390x/syscall.tbl \ arch/sh/kernel/syscalls/syscall.tbl,linux-user/sh4/syscall.tbl \ arch/sparc/kernel/syscalls/syscall.tbl,linux-user/sparc64/syscall.tbl \ diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index dfeee8231a..9c0e6b81f2 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -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=' ') 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/Kconfig b/semihosting/Kconfig index eaf3a20ef5..fbe6ac87f9 100644 --- a/semihosting/Kconfig +++ b/semihosting/Kconfig @@ -1,6 +1,7 @@ config SEMIHOSTING bool + depends on TCG config ARM_COMPATIBLE_SEMIHOSTING bool diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 62d8bae97f..d78c6428b9 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -34,6 +34,7 @@ #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" @@ -201,19 +202,19 @@ static LayoutInfo common_semi_find_bases(CPUState *cs) * 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 "semihosting/softmmu-uaccess.h" +#include "semihosting/uaccess.h" #endif 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 @@ -225,7 +226,7 @@ static void common_semi_cb(CPUState *cs, uint64_t ret, int err) { if (err) { #ifdef CONFIG_USER_ONLY - TaskState *ts = cs->opaque; + TaskState *ts = get_task_state(cs); ts->swi_errno = err; #else syscall_err = err; @@ -250,7 +251,7 @@ static void common_semi_dead_cb(CPUState *cs, uint64_t ret, int err) static void common_semi_rw_cb(CPUState *cs, uint64_t ret, int err) { /* Recover the original length from the third argument. */ - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); target_ulong args = common_semi_arg(cs, 1); target_ulong arg2; GET_ARG(2); @@ -321,7 +322,7 @@ static void common_semi_readc_cb(CPUState *cs, uint64_t ret, int err) { if (!err) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); uint8_t ch; if (get_user_u8(ch, common_semi_stack_bottom(cs) - 1)) { @@ -360,13 +361,12 @@ static const uint8_t featurefile_data[] = { */ 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; int64_t elapsed; nr = common_semi_arg(cs, 0) & 0xffffffffU; @@ -586,7 +586,7 @@ void 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); @@ -664,7 +664,7 @@ void do_common_semihosting(CPUState *cs) 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); @@ -724,6 +724,9 @@ void do_common_semihosting(CPUState *cs) 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, @@ -751,6 +754,7 @@ void do_common_semihosting(CPUState *cs) } gdb_exit(ret); exit(ret); + } case TARGET_SYS_ELAPSED: elapsed = get_clock() - clock_start; diff --git a/semihosting/config.c b/semihosting/config.c index 89a1759687..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 @@ -113,12 +113,13 @@ 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, " "); @@ -131,10 +132,10 @@ void qemu_semihosting_enable(void) 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; @@ -155,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 { @@ -165,7 +166,7 @@ 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; } diff --git a/semihosting/console.c b/semihosting/console.c index 0f976fe8cb..60102bbab6 100644 --- a/semihosting/console.c +++ b/semihosting/console.c @@ -43,10 +43,8 @@ static SemihostingConsole console; static int console_can_read(void *opaque) { SemihostingConsole *c = opaque; - int ret; - g_assert(qemu_mutex_iothread_locked()); - ret = (int) fifo8_num_free(&c->fifo); - return ret; + g_assert(bql_locked()); + return (int)fifo8_num_free(&c->fifo); } static void console_wake_up(gpointer data, gpointer user_data) @@ -60,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++); } @@ -72,7 +70,7 @@ bool qemu_semihosting_console_ready(void) { SemihostingConsole *c = &console; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); return !fifo8_is_empty(&c->fifo); } @@ -80,7 +78,7 @@ void qemu_semihosting_console_block_until_ready(CPUState *cs) { SemihostingConsole *c = &console; - g_assert(qemu_mutex_iothread_locked()); + g_assert(bql_locked()); /* Block if the fifo is completely empty. */ if (fifo8_is_empty(&c->fifo)) { diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c index b05c52f26f..d3241434c5 100644 --- a/semihosting/guestfd.c +++ b/semihosting/guestfd.c @@ -9,13 +9,10 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "gdbstub/syscalls.h" #include "semihosting/semihost.h" #include "semihosting/guestfd.h" -#ifdef CONFIG_USER_ONLY -#include "qemu.h" -#else -#include "semihosting/softmmu-uaccess.h" +#ifndef CONFIG_USER_ONLY #include CONFIG_DEVICES #endif diff --git a/semihosting/meson.build b/semihosting/meson.build index 8057db5494..34933e5a19 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -3,11 +3,14 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'syscalls.c', )) -specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SOFTMMU'], if_true: files( +specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( 'config.c', 'console.c', 'uaccess.c', )) +common_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_false: files('stubs-all.c')) +system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_false: files('stubs-system.c')) + specific_ss.add(when: ['CONFIG_ARM_COMPATIBLE_SEMIHOSTING'], if_true: files('arm-compat-semi.c')) diff --git a/semihosting/stubs-all.c b/semihosting/stubs-all.c new file mode 100644 index 0000000000..a2a1fc9c6f --- /dev/null +++ b/semihosting/stubs-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/semihosting/stubs-system.c b/semihosting/stubs-system.c new file mode 100644 index 0000000000..f26cbb7c25 --- /dev/null +++ b/semihosting/stubs-system.c @@ -0,0 +1,65 @@ +/* + * Semihosting Stubs for system emulation + * + * Copyright (c) 2019 Linaro Ltd + * + * Stubs for system targets that don't actually do semihosting. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/option.h" +#include "qemu/error-report.h" +#include "semihosting/semihost.h" + +/* Empty config */ +QemuOptsList qemu_semihosting_config_opts = { + .name = "", + .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head), + .desc = { + { /* end of list */ } + }, +}; + +/* Queries to config status default to off */ +bool semihosting_enabled(bool is_user) +{ + return false; +} + +/* + * 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. + */ +void qemu_semihosting_enable(void) +{ +} + +int qemu_semihosting_config_options(const char *optstr) +{ + return 1; +} + +const char *semihosting_get_arg(int i) +{ + return NULL; +} + +int semihosting_get_argc(void) +{ + return 0; +} + +const char *semihosting_get_cmdline(void) +{ + return NULL; +} + +void semihosting_arg_fallback(const char *file, const char *cmd) +{ +} + +void qemu_semihosting_chardev_init(void) +{ +} diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c index 508a0ad88c..c40348f996 100644 --- a/semihosting/syscalls.c +++ b/semihosting/syscalls.c @@ -7,14 +7,15 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.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/softmmu-uaccess.h" +#include "semihosting/uaccess.h" #endif @@ -23,7 +24,7 @@ */ static int validate_strlen(CPUState *cs, target_ulong str, target_ulong tlen) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char c; if (tlen == 0) { @@ -53,7 +54,7 @@ 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 = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *str = NULL; if (ret > 0) { @@ -73,7 +74,7 @@ static int validate_lock_user_string(char **pstr, CPUState *cs, static int copy_stat_to_user(CPUState *cs, target_ulong addr, const struct stat *s) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct gdb_stat *p; if (s->st_dev != (uint32_t)s->st_dev || @@ -138,46 +139,48 @@ static void gdb_open(CPUState *cs, gdb_syscall_complete_cb complete, gdb_open_complete = complete; gdb_do_syscall(gdb_open_cb, "open,%s,%x,%x", - fname, len, (target_ulong)gdb_flags, (target_ulong)mode); + (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", (target_ulong)gf->hostfd); + 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,%x,%x", - (target_ulong)gf->hostfd, buf, 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,%x,%x", - (target_ulong)gf->hostfd, buf, 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", - (target_ulong)gf->hostfd, off, (target_ulong)gdb_whence); + (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", (target_ulong)gf->hostfd); + 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,%x", (target_ulong)gf->hostfd, 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, @@ -190,7 +193,8 @@ static void gdb_stat(CPUState *cs, gdb_syscall_complete_cb complete, return; } - gdb_do_syscall(complete, "stat,%s,%x", fname, len, addr); + 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, @@ -202,7 +206,7 @@ static void gdb_remove(CPUState *cs, gdb_syscall_complete_cb complete, return; } - gdb_do_syscall(complete, "unlink,%s", fname, len); + gdb_do_syscall(complete, "unlink,%s", (uint64_t)fname, (uint32_t)len); } static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, @@ -222,7 +226,9 @@ static void gdb_rename(CPUState *cs, gdb_syscall_complete_cb complete, return; } - gdb_do_syscall(complete, "rename,%s,%s", oname, olen, nname, nlen); + 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, @@ -234,13 +240,14 @@ static void gdb_system(CPUState *cs, gdb_syscall_complete_cb complete, return; } - gdb_do_syscall(complete, "system,%s", cmd, len); + 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,%x,%x", tv_addr, tz_addr); + gdb_do_syscall(complete, "gettimeofday,%lx,%lx", + (uint64_t)tv_addr, (uint64_t)tz_addr); } /* @@ -251,9 +258,9 @@ 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 = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; - int ret, host_flags; + int ret, host_flags = O_BINARY; ret = validate_lock_user_string(&p, cs, fname, fname_len); if (ret < 0) { @@ -262,11 +269,11 @@ static void host_open(CPUState *cs, gdb_syscall_complete_cb complete, } if (gdb_flags & GDB_O_WRONLY) { - host_flags = O_WRONLY; + host_flags |= O_WRONLY; } else if (gdb_flags & GDB_O_RDWR) { - host_flags = O_RDWR; + host_flags |= O_RDWR; } else { - host_flags = O_RDONLY; + host_flags |= O_RDONLY; } if (gdb_flags & GDB_O_CREAT) { host_flags |= O_CREAT; @@ -309,7 +316,7 @@ static void host_close(CPUState *cs, gdb_syscall_complete_cb complete, static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); void *ptr = lock_user(VERIFY_WRITE, buf, len, 0); ssize_t ret; @@ -317,22 +324,20 @@ static void host_read(CPUState *cs, gdb_syscall_complete_cb complete, complete(cs, -1, EFAULT); return; } - do { - ret = read(gf->hostfd, ptr, len); - } while (ret == -1 && errno == EINTR); + ret = RETRY_ON_EINTR(read(gf->hostfd, ptr, len)); if (ret == -1) { - complete(cs, -1, errno); unlock_user(ptr, buf, 0); + complete(cs, -1, errno); } else { - complete(cs, ret, 0); unlock_user(ptr, buf, ret); + complete(cs, ret, 0); } } static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); void *ptr = lock_user(VERIFY_READ, buf, len, 1); ssize_t ret; @@ -341,8 +346,8 @@ static void host_write(CPUState *cs, gdb_syscall_complete_cb complete, return; } ret = write(gf->hostfd, ptr, len); - complete(cs, ret, ret == -1 ? errno : 0); unlock_user(ptr, buf, 0); + complete(cs, ret, ret == -1 ? errno : 0); } static void host_lseek(CPUState *cs, gdb_syscall_complete_cb complete, @@ -406,7 +411,7 @@ 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 = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct stat buf; char *name; int ret, err; @@ -428,14 +433,14 @@ static void host_stat(CPUState *cs, gdb_syscall_complete_cb complete, ret = -1; } } - complete(cs, ret, err); unlock_user(name, fname, 0); + complete(cs, ret, err); } static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong fname, target_ulong fname_len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; int ret; @@ -446,15 +451,15 @@ static void host_remove(CPUState *cs, gdb_syscall_complete_cb complete, } ret = remove(p); - complete(cs, ret, ret ? errno : 0); unlock_user(p, fname, 0); + complete(cs, ret, ret ? errno : 0); } static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong oname, target_ulong oname_len, target_ulong nname, target_ulong nname_len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ostr, *nstr; int ret; @@ -471,15 +476,15 @@ static void host_rename(CPUState *cs, gdb_syscall_complete_cb complete, } ret = rename(ostr, nstr); - complete(cs, ret, ret ? errno : 0); unlock_user(ostr, oname, 0); unlock_user(nstr, nname, 0); + complete(cs, ret, ret ? errno : 0); } static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong cmd, target_ulong cmd_len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *p; int ret; @@ -490,14 +495,14 @@ static void host_system(CPUState *cs, gdb_syscall_complete_cb complete, } ret = system(p); - complete(cs, ret, ret == -1 ? errno : 0); unlock_user(p, cmd, 0); + complete(cs, ret, ret == -1 ? errno : 0); } static void host_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete, target_ulong tv_addr, target_ulong tz_addr) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); struct gdb_timeval *p; int64_t rt; @@ -542,7 +547,7 @@ static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete, static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); target_ulong rest = gf->staticfile.len - gf->staticfile.off; void *ptr; @@ -556,8 +561,8 @@ static void staticfile_read(CPUState *cs, gdb_syscall_complete_cb complete, } memcpy(ptr, gf->staticfile.data + gf->staticfile.off, len); gf->staticfile.off += len; - complete(cs, len, 0); unlock_user(ptr, buf, len); + complete(cs, len, 0); } static void staticfile_lseek(CPUState *cs, gdb_syscall_complete_cb complete, @@ -600,7 +605,7 @@ static void staticfile_flen(CPUState *cs, gdb_syscall_complete_cb complete, static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ptr; int ret; @@ -610,14 +615,14 @@ static void console_read(CPUState *cs, gdb_syscall_complete_cb complete, return; } ret = qemu_semihosting_console_read(cs, ptr, len); - complete(cs, ret, 0); unlock_user(ptr, buf, ret); + complete(cs, ret, 0); } static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, GuestFD *gf, target_ulong buf, target_ulong len) { - CPUArchState *env G_GNUC_UNUSED = cs->env_ptr; + CPUArchState *env G_GNUC_UNUSED = cpu_env(cs); char *ptr = lock_user(VERIFY_READ, buf, len, 1); int ret; @@ -626,8 +631,8 @@ static void console_write(CPUState *cs, gdb_syscall_complete_cb complete, return; } ret = qemu_semihosting_console_write(ptr, len); - complete(cs, ret ? ret : -1, ret ? 0 : EIO); unlock_user(ptr, buf, 0); + complete(cs, ret ? ret : -1, ret ? 0 : EIO); } static void console_fstat(CPUState *cs, gdb_syscall_complete_cb complete, @@ -715,7 +720,7 @@ 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 overlowing ssize_t. + * 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. */ @@ -756,7 +761,7 @@ 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 overlowing ssize_t. + * 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. */ diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 8018828069..dc587d73bc 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -9,9 +9,9 @@ #include "qemu/osdep.h" #include "exec/exec-all.h" -#include "semihosting/softmmu-uaccess.h" +#include "semihosting/uaccess.h" -void *softmmu_lock_user(CPUArchState *env, target_ulong addr, +void *uaccess_lock_user(CPUArchState *env, target_ulong addr, target_ulong len, bool copy) { void *p = malloc(len); @@ -24,9 +24,9 @@ void *softmmu_lock_user(CPUArchState *env, target_ulong addr, return p; } -ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr) +ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr) { - int mmu_idx = cpu_mmu_index(env, false); + int mmu_idx = cpu_mmu_index(env_cpu(env), false); size_t len = 0; while (1) { @@ -37,7 +37,7 @@ ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr) /* 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, MMU_DATA_LOAD, + flags = probe_access_flags(env, addr, 0, MMU_DATA_LOAD, mmu_idx, true, &h, 0); if (flags & TLB_INVALID_MASK) { return -1; @@ -72,16 +72,16 @@ ssize_t softmmu_strlen_user(CPUArchState *env, target_ulong addr) } } -char *softmmu_lock_user_string(CPUArchState *env, target_ulong addr) +char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr) { - ssize_t len = softmmu_strlen_user(env, addr); + ssize_t len = uaccess_strlen_user(env, addr); if (len < 0) { return NULL; } - return softmmu_lock_user(env, addr, len + 1, true); + return uaccess_lock_user(env, addr, len + 1, true); } -void softmmu_unlock_user(CPUArchState *env, void *p, +void uaccess_unlock_user(CPUArchState *env, void *p, target_ulong addr, target_ulong len) { if (len) { diff --git a/softmmu/cpu-throttle.c b/softmmu/cpu-throttle.c deleted file mode 100644 index d9bb30a223..0000000000 --- a/softmmu/cpu-throttle.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/thread.h" -#include "hw/core/cpu.h" -#include "qemu/main-loop.h" -#include "sysemu/cpus.h" -#include "sysemu/cpu-throttle.h" - -/* vcpu throttling controls */ -static QEMUTimer *throttle_timer; -static unsigned int throttle_percentage; - -#define CPU_THROTTLE_PCT_MIN 1 -#define CPU_THROTTLE_PCT_MAX 99 -#define CPU_THROTTLE_TIMESLICE_NS 10000000 - -static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque) -{ - double pct; - double throttle_ratio; - int64_t sleeptime_ns, endtime_ns; - - if (!cpu_throttle_get_percentage()) { - return; - } - - pct = (double)cpu_throttle_get_percentage() / 100; - throttle_ratio = pct / (1 - pct); - /* Add 1ns to fix double's rounding error (like 0.9999999...) */ - sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1); - 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, - sleeptime_ns / SCALE_MS); - } else { - qemu_mutex_unlock_iothread(); - g_usleep(sleeptime_ns / SCALE_US); - qemu_mutex_lock_iothread(); - } - sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - } - qatomic_set(&cpu->throttle_thread_scheduled, 0); -} - -static void cpu_throttle_timer_tick(void *opaque) -{ - CPUState *cpu; - double pct; - - /* Stop the timer if needed */ - if (!cpu_throttle_get_percentage()) { - return; - } - CPU_FOREACH(cpu) { - if (!qatomic_xchg(&cpu->throttle_thread_scheduled, 1)) { - async_run_on_cpu(cpu, cpu_throttle_thread, - RUN_ON_CPU_NULL); - } - } - - pct = (double)cpu_throttle_get_percentage() / 100; - timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + - CPU_THROTTLE_TIMESLICE_NS / (1 - pct)); -} - -void cpu_throttle_set(int new_throttle_pct) -{ - /* - * boolean to store whether throttle is already active or not, - * before modifying throttle_percentage - */ - bool throttle_active = cpu_throttle_active(); - - /* Ensure throttle percentage is within valid range */ - new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX); - new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN); - - qatomic_set(&throttle_percentage, new_throttle_pct); - - if (!throttle_active) { - cpu_throttle_timer_tick(NULL); - } -} - -void cpu_throttle_stop(void) -{ - qatomic_set(&throttle_percentage, 0); -} - -bool cpu_throttle_active(void) -{ - return (cpu_throttle_get_percentage() != 0); -} - -int cpu_throttle_get_percentage(void) -{ - return qatomic_read(&throttle_percentage); -} - -void cpu_throttle_init(void) -{ - throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, - cpu_throttle_timer_tick, NULL); -} diff --git a/softmmu/icount.c b/softmmu/icount.c deleted file mode 100644 index a5cef9c60a..0000000000 --- a/softmmu/icount.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "exec/exec-all.h" -#include "sysemu/cpus.h" -#include "sysemu/qtest.h" -#include "qemu/main-loop.h" -#include "qemu/option.h" -#include "qemu/seqlock.h" -#include "sysemu/replay.h" -#include "sysemu/runstate.h" -#include "hw/core/cpu.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/cpu-throttle.h" -#include "timers-state.h" - -/* - * ICOUNT: Instruction Counter - * - * this module is split off from cpu-timers because the icount part - * is TCG-specific, and does not need to be built for other accels. - */ -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; - -static void icount_enable_precise(void) -{ - use_icount = 1; -} - -static void icount_enable_adaptive(void) -{ - use_icount = 2; -} - -/* - * The current number of executed instructions is based on what we - * originally budgeted minus the current state of the decrementing - * icount counters in extra/u16.low. - */ -static int64_t icount_get_executed(CPUState *cpu) -{ - return (cpu->icount_budget - - (cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra)); -} - -/* - * Update the global shared timer_state.qemu_icount to take into - * account executed instructions. This is done by the TCG vCPU - * thread so the main-loop can see time has moved forward. - */ -static void icount_update_locked(CPUState *cpu) -{ - int64_t executed = icount_get_executed(cpu); - cpu->icount_budget -= executed; - - qatomic_set_i64(&timers_state.qemu_icount, - timers_state.qemu_icount + executed); -} - -/* - * Update the global shared timer_state.qemu_icount to take into - * account executed instructions. This is done by the TCG vCPU - * thread so the main-loop can see time has moved forward. - */ -void icount_update(CPUState *cpu) -{ - seqlock_write_lock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - icount_update_locked(cpu); - seqlock_write_unlock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); -} - -static int64_t icount_get_raw_locked(void) -{ - CPUState *cpu = current_cpu; - - if (cpu && cpu->running) { - if (!cpu->can_do_io) { - error_report("Bad icount read"); - exit(1); - } - /* Take into account what has run */ - icount_update_locked(cpu); - } - /* The read is protected by the seqlock, but needs atomic64 to avoid UB */ - return qatomic_read_i64(&timers_state.qemu_icount); -} - -static int64_t icount_get_locked(void) -{ - int64_t icount = icount_get_raw_locked(); - return qatomic_read_i64(&timers_state.qemu_icount_bias) + - icount_to_ns(icount); -} - -int64_t icount_get_raw(void) -{ - int64_t icount; - unsigned start; - - do { - start = seqlock_read_begin(&timers_state.vm_clock_seqlock); - icount = icount_get_raw_locked(); - } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); - - return icount; -} - -/* Return the virtual CPU time, based on the instruction counter. */ -int64_t icount_get(void) -{ - int64_t icount; - unsigned start; - - do { - start = seqlock_read_begin(&timers_state.vm_clock_seqlock); - icount = icount_get_locked(); - } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); - - return icount; -} - -int64_t icount_to_ns(int64_t icount) -{ - return icount << qatomic_read(&timers_state.icount_time_shift); -} - -/* - * Correlation between real and virtual time is always going to be - * fairly approximate, so ignore small variation. - * When the guest is idle real and virtual time will be aligned in - * the IO wait loop. - */ -#define ICOUNT_WOBBLE (NANOSECONDS_PER_SECOND / 10) - -static void icount_adjust(void) -{ - int64_t cur_time; - int64_t cur_icount; - int64_t delta; - - /* If the VM is not running, then do nothing. */ - if (!runstate_is_running()) { - return; - } - - seqlock_write_lock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - cur_time = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT, - cpu_get_clock_locked()); - cur_icount = icount_get_locked(); - - delta = cur_icount - cur_time; - /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ - if (delta > 0 - && timers_state.last_delta + ICOUNT_WOBBLE < delta * 2 - && timers_state.icount_time_shift > 0) { - /* The guest is getting too far ahead. Slow time down. */ - qatomic_set(&timers_state.icount_time_shift, - timers_state.icount_time_shift - 1); - } - if (delta < 0 - && timers_state.last_delta - ICOUNT_WOBBLE > delta * 2 - && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) { - /* The guest is getting too far behind. Speed time up. */ - qatomic_set(&timers_state.icount_time_shift, - timers_state.icount_time_shift + 1); - } - timers_state.last_delta = delta; - qatomic_set_i64(&timers_state.qemu_icount_bias, - cur_icount - (timers_state.qemu_icount - << timers_state.icount_time_shift)); - seqlock_write_unlock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); -} - -static void icount_adjust_rt(void *opaque) -{ - timer_mod(timers_state.icount_rt_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); - icount_adjust(); -} - -static void icount_adjust_vm(void *opaque) -{ - timer_mod(timers_state.icount_vm_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / 10); - icount_adjust(); -} - -int64_t icount_round(int64_t count) -{ - int shift = qatomic_read(&timers_state.icount_time_shift); - return (count + (1 << shift) - 1) >> shift; -} - -static void icount_warp_rt(void) -{ - unsigned seq; - int64_t warp_start; - - /* - * The icount_warp_timer is rescheduled soon after vm_clock_warp_start - * changes from -1 to another value, so the race here is okay. - */ - do { - seq = seqlock_read_begin(&timers_state.vm_clock_seqlock); - warp_start = timers_state.vm_clock_warp_start; - } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, seq)); - - if (warp_start == -1) { - return; - } - - seqlock_write_lock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - if (runstate_is_running()) { - int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT, - cpu_get_clock_locked()); - int64_t warp_delta; - - warp_delta = clock - timers_state.vm_clock_warp_start; - if (icount_enabled() == 2) { - /* - * 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, - timers_state.qemu_icount_bias + warp_delta); - } - timers_state.vm_clock_warp_start = -1; - seqlock_write_unlock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - - if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) { - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); - } -} - -static void icount_timer_cb(void *opaque) -{ - /* - * No need for a checkpoint because the timer already synchronizes - * with CHECKPOINT_CLOCK_VIRTUAL_RT. - */ - icount_warp_rt(); -} - -void icount_start_warp_timer(void) -{ - int64_t clock; - int64_t deadline; - - assert(icount_enabled()); - - /* - * Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers - * do not fire, so computing the deadline does not make sense. - */ - if (!runstate_is_running()) { - return; - } - - if (replay_mode != REPLAY_MODE_PLAY) { - if (!all_cpu_threads_idle()) { - return; - } - - if (qtest_enabled()) { - /* When testing, qtest commands advance icount. */ - return; - } - - replay_checkpoint(CHECKPOINT_CLOCK_WARP_START); - } else { - /* warp clock deterministically in record/replay mode */ - if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) { - /* - * 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. - */ - if (replay_has_event()) { - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); - } - return; - } - } - - /* We want to use the earliest deadline from ALL vm_clocks */ - clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT); - deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - ~QEMU_TIMER_ATTR_EXTERNAL); - if (deadline < 0) { - static bool notified; - if (!icount_sleep && !notified) { - warn_report("icount sleep disabled and no active timers"); - notified = true; - } - return; - } - - if (deadline > 0) { - /* - * Ensure QEMU_CLOCK_VIRTUAL proceeds even when the virtual CPU goes to - * sleep. Otherwise, the CPU might be waiting for a future timer - * interrupt to wake it up, but the interrupt never comes because - * the vCPU isn't running any insns and thus doesn't advance the - * QEMU_CLOCK_VIRTUAL. - */ - if (!icount_sleep) { - /* - * We never let VCPUs sleep in no sleep icount mode. - * If there is a pending QEMU_CLOCK_VIRTUAL timer we just advance - * to the next QEMU_CLOCK_VIRTUAL event and notify it. - * It is useful when we want a deterministic execution time, - * isolated from host latencies. - */ - seqlock_write_lock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - qatomic_set_i64(&timers_state.qemu_icount_bias, - timers_state.qemu_icount_bias + deadline); - seqlock_write_unlock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); - } else { - /* - * We do stop VCPUs and only advance QEMU_CLOCK_VIRTUAL after some - * "real" time, (related to the time left until the next event) has - * passed. The QEMU_CLOCK_VIRTUAL_RT clock will do this. - * This avoids that the warps are visible externally; for example, - * you will not be sending network packets continuously instead of - * every 100ms. - */ - seqlock_write_lock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - if (timers_state.vm_clock_warp_start == -1 - || timers_state.vm_clock_warp_start > clock) { - timers_state.vm_clock_warp_start = clock; - } - seqlock_write_unlock(&timers_state.vm_clock_seqlock, - &timers_state.vm_clock_lock); - timer_mod_anticipate(timers_state.icount_warp_timer, - clock + deadline); - } - } else if (deadline == 0) { - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); - } -} - -void icount_account_warp_timer(void) -{ - if (!icount_sleep) { - return; - } - - /* - * Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers - * do not fire, so computing the deadline does not make sense. - */ - if (!runstate_is_running()) { - return; - } - - replay_async_events(); - - /* warp clock deterministically in record/replay mode */ - if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) { - return; - } - - timer_del(timers_state.icount_warp_timer); - icount_warp_rt(); -} - -void icount_configure(QemuOpts *opts, Error **errp) -{ - const char *option = qemu_opt_get(opts, "shift"); - bool sleep = qemu_opt_get_bool(opts, "sleep", true); - bool align = qemu_opt_get_bool(opts, "align", false); - long time_shift = -1; - - if (!option) { - if (qemu_opt_get(opts, "align") != NULL) { - error_setg(errp, "Please specify shift option when using align"); - } - return; - } - - if (align && !sleep) { - error_setg(errp, "align=on and sleep=off are incompatible"); - return; - } - - 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; - } - } else if (icount_align_option) { - error_setg(errp, "shift=auto and align=on are incompatible"); - return; - } else if (!icount_sleep) { - error_setg(errp, "shift=auto and sleep=off are incompatible"); - return; - } - - icount_sleep = sleep; - if (icount_sleep) { - timers_state.icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, - icount_timer_cb, NULL); - } - - icount_align_option = align; - - if (time_shift >= 0) { - timers_state.icount_time_shift = time_shift; - icount_enable_precise(); - return; - } - - icount_enable_adaptive(); - - /* - * 125MIPS seems a reasonable initial guess at the guest speed. - * It will be corrected fairly quickly anyway. - */ - timers_state.icount_time_shift = 3; - - /* - * Have both realtime and virtual time triggers for speed adjustment. - * The realtime trigger catches emulated time passing too slowly, - * the virtual time trigger catches emulated time passing too fast. - * Realtime triggers occur even when idle, so use them less frequently - * than VM triggers. - */ - timers_state.vm_clock_warp_start = -1; - timers_state.icount_rt_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL_RT, - icount_adjust_rt, NULL); - timer_mod(timers_state.icount_rt_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL_RT) + 1000); - timers_state.icount_vm_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - icount_adjust_vm, NULL); - timer_mod(timers_state.icount_vm_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - NANOSECONDS_PER_SECOND / 10); -} - -void icount_notify_exit(void) -{ - if (icount_enabled() && current_cpu) { - qemu_cpu_kick(current_cpu); - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); - } -} diff --git a/softmmu/ioport.c b/softmmu/ioport.c deleted file mode 100644 index cb8adb0b93..0000000000 --- a/softmmu/ioport.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* - * splitted out ioport related stuffs from vl.c. - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/ioport.h" -#include "exec/memory.h" -#include "exec/address-spaces.h" -#include "trace.h" - -typedef struct MemoryRegionPortioList { - MemoryRegion mr; - void *portio_opaque; - MemoryRegionPortio ports[]; -} MemoryRegionPortioList; - -static uint64_t unassigned_io_read(void *opaque, hwaddr addr, unsigned size) -{ - return -1ULL; -} - -static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ -} - -const MemoryRegionOps unassigned_io_ops = { - .read = unassigned_io_read, - .write = unassigned_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void cpu_outb(uint32_t addr, uint8_t val) -{ - trace_cpu_out(addr, 'b', val); - address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, - &val, 1); -} - -void cpu_outw(uint32_t addr, uint16_t val) -{ - uint8_t buf[2]; - - trace_cpu_out(addr, 'w', val); - stw_p(buf, val); - address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, - buf, 2); -} - -void cpu_outl(uint32_t addr, uint32_t val) -{ - uint8_t buf[4]; - - trace_cpu_out(addr, 'l', val); - stl_p(buf, val); - address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, - buf, 4); -} - -uint8_t cpu_inb(uint32_t addr) -{ - uint8_t val; - - address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, - &val, 1); - trace_cpu_in(addr, 'b', val); - return val; -} - -uint16_t cpu_inw(uint32_t addr) -{ - uint8_t buf[2]; - uint16_t val; - - address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); - val = lduw_p(buf); - trace_cpu_in(addr, 'w', val); - return val; -} - -uint32_t cpu_inl(uint32_t addr) -{ - uint8_t buf[4]; - uint32_t val; - - address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); - val = ldl_p(buf); - trace_cpu_in(addr, 'l', val); - return val; -} - -void portio_list_init(PortioList *piolist, - Object *owner, - const MemoryRegionPortio *callbacks, - void *opaque, const char *name) -{ - unsigned n = 0; - - while (callbacks[n].size) { - ++n; - } - - piolist->ports = callbacks; - piolist->nr = 0; - piolist->regions = g_new0(MemoryRegion *, n); - piolist->address_space = NULL; - piolist->opaque = opaque; - piolist->owner = owner; - piolist->name = name; - piolist->flush_coalesced_mmio = false; -} - -void portio_list_set_flush_coalesced(PortioList *piolist) -{ - piolist->flush_coalesced_mmio = true; -} - -void portio_list_destroy(PortioList *piolist) -{ - MemoryRegionPortioList *mrpio; - unsigned i; - - for (i = 0; i < piolist->nr; ++i) { - mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); - object_unparent(OBJECT(&mrpio->mr)); - g_free(mrpio); - } - g_free(piolist->regions); -} - -static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio, - uint64_t offset, unsigned size, - bool write) -{ - const MemoryRegionPortio *mrp; - - for (mrp = mrpio->ports; mrp->size; ++mrp) { - if (offset >= mrp->offset && offset < mrp->offset + mrp->len && - size == mrp->size && - (write ? (bool)mrp->write : (bool)mrp->read)) { - return mrp; - } - } - return NULL; -} - -static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) -{ - MemoryRegionPortioList *mrpio = opaque; - const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false); - uint64_t data; - - data = ((uint64_t)1 << (size * 8)) - 1; - if (mrp) { - data = mrp->read(mrpio->portio_opaque, mrp->base + addr); - } else if (size == 2) { - mrp = find_portio(mrpio, addr, 1, false); - if (mrp) { - data = mrp->read(mrpio->portio_opaque, mrp->base + addr); - if (addr + 1 < mrp->offset + mrp->len) { - data |= mrp->read(mrpio->portio_opaque, mrp->base + addr + 1) << 8; - } else { - data |= 0xff00; - } - } - } - return data; -} - -static void portio_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) -{ - MemoryRegionPortioList *mrpio = opaque; - const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); - - if (mrp) { - mrp->write(mrpio->portio_opaque, mrp->base + 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); - if (addr + 1 < mrp->offset + mrp->len) { - mrp->write(mrpio->portio_opaque, mrp->base + addr + 1, data >> 8); - } - } - } -} - -static const MemoryRegionOps portio_ops = { - .read = portio_read, - .write = portio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid.unaligned = true, - .impl.unaligned = true, -}; - -static void portio_list_add_1(PortioList *piolist, - const MemoryRegionPortio *pio_init, - unsigned count, unsigned start, - unsigned off_low, unsigned off_high) -{ - MemoryRegionPortioList *mrpio; - unsigned i; - - /* Copy the sub-list and null-terminate it. */ - mrpio = g_malloc0(sizeof(MemoryRegionPortioList) + - sizeof(MemoryRegionPortio) * (count + 1)); - mrpio->portio_opaque = piolist->opaque; - 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, - piolist->name, off_high - off_low); - if (piolist->flush_coalesced_mmio) { - memory_region_set_flush_coalesced(&mrpio->mr); - } - memory_region_add_subregion(piolist->address_space, - start + off_low, &mrpio->mr); - piolist->regions[piolist->nr] = &mrpio->mr; - ++piolist->nr; -} - -void portio_list_add(PortioList *piolist, - MemoryRegion *address_space, - uint32_t start) -{ - const MemoryRegionPortio *pio, *pio_start = piolist->ports; - unsigned int off_low, off_high, off_last, count; - - piolist->address_space = address_space; - - /* Handle the first entry specially. */ - off_last = off_low = pio_start->offset; - off_high = off_low + pio_start->len + pio_start->size - 1; - count = 1; - - for (pio = pio_start + 1; pio->size != 0; pio++, count++) { - /* All entries must be sorted by offset. */ - assert(pio->offset >= off_last); - off_last = pio->offset; - - /* If we see a hole, break the region. */ - if (off_last > off_high) { - portio_list_add_1(piolist, pio_start, count, start, off_low, - off_high); - /* ... and start collecting anew. */ - pio_start = pio; - off_low = off_last; - off_high = off_low + pio->len + pio_start->size - 1; - count = 0; - } else if (off_last + pio->len > off_high) { - off_high = off_last + pio->len + pio_start->size - 1; - } - } - - /* There will always be an open sub-list. */ - portio_list_add_1(piolist, pio_start, count, start, off_low, off_high); -} - -void portio_list_del(PortioList *piolist) -{ - MemoryRegionPortioList *mrpio; - unsigned i; - - for (i = 0; i < piolist->nr; ++i) { - mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); - memory_region_del_subregion(piolist->address_space, &mrpio->mr); - } -} diff --git a/softmmu/main.c b/softmmu/main.c deleted file mode 100644 index e84cdc9438..0000000000 --- a/softmmu/main.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2020 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "qemu-main.h" -#include "sysemu/sysemu.h" - -#ifndef XBOX -#ifdef CONFIG_SDL -#include -#endif -#endif - -int qemu_default_main(void) -{ - int status; - - status = qemu_main_loop(); - qemu_cleanup(); - - return status; -} - -int (*qemu_main)(void) = qemu_default_main; - -#ifndef XBOX -int main(int argc, char **argv) -{ - qemu_init(argc, argv); - return qemu_main(); -} -#endif \ No newline at end of file diff --git a/softmmu/meson.build b/softmmu/meson.build deleted file mode 100644 index 3272af1f31..0000000000 --- a/softmmu/meson.build +++ /dev/null @@ -1,36 +0,0 @@ -specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: [files( - 'arch_init.c', - 'ioport.c', - 'memory.c', - 'physmem.c', - 'qtest.c', - 'dirtylimit.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-events b/softmmu/trace-events deleted file mode 100644 index 22606dc27b..0000000000 --- a/softmmu/trace-events +++ /dev/null @@ -1,40 +0,0 @@ -# See docs/devel/tracing.rst for syntax documentation. - -# balloon.c -# Since requests are raised via monitor, not many tracepoints are needed. -balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" - -# ioport.c -cpu_in(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" -cpu_out(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" - -# memory.c -memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" -memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" -memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr '%s' listener '%s' synced (global=%d)" -flatview_new(void *view, void *root) "%p (root %p)" -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 -vm_stop_flush_all(int ret) "ret %d" - -# vl.c -vm_state_notify(int running, int reason, const char *reason_str) "running %d reason %d (%s)" -load_file(const char *name, const char *path) "name %s location %s" -runstate_set(int current_state, const char *current_state_str, int new_state, const char *new_state_str) "current_run_state %d (%s) new_state %d (%s)" -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/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..5e61a9d1bd 100644 --- a/storage-daemon/meson.build +++ b/storage-daemon/meson.build @@ -1,13 +1,14 @@ qsd_ss = ss.source_set() qsd_ss.add(files('qemu-storage-daemon.c')) -qsd_ss.add(blockdev, chardev, qmp, qom, qemuutil, gnutls) +qsd_ss.add(blockdev, chardev, qmp, qom, qemuutil) 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(), + link_args: '@block.syms', link_depends: block_syms, dependencies: qsd_ss.dependencies(), install: true) endif 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 7718f6dcda..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" @@ -298,7 +299,7 @@ static void process_options(int argc, char *argv[], bool pre_init_pass) case OPTION_DAEMONIZE: if (os_set_daemonize(true) < 0) { /* - * --daemonize is parsed before monitor_init_globals_core(), so + * --daemonize is parsed before monitor_init_globals(), so * error_report() does not work yet */ fprintf(stderr, "--daemonize not supported in this build\n"); @@ -410,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/blk-exp-close-all.c b/stubs/blk-exp-close-all.c index 1c71316763..2f68e06d7d 100644 --- a/stubs/blk-exp-close-all.c +++ b/stubs/blk-exp-close-all.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "block/export.h" -/* Only used in programs that support block exports (libblockdev.fa) */ +/* Only used in programs that support block exports (libblockdev.a) */ void blk_exp_close_all(void) { } diff --git a/stubs/cpus-get-virtual-clock.c b/stubs/cpus-get-virtual-clock.c deleted file mode 100644 index fd447d53f3..0000000000 --- a/stubs/cpus-get-virtual-clock.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" -#include "qemu/main-loop.h" - -int64_t cpus_get_virtual_clock(void) -{ - return cpu_get_clock(); -} diff --git a/stubs/cpus-virtual-clock.c b/stubs/cpus-virtual-clock.c new file mode 100644 index 0000000000..af7c1a1d40 --- /dev/null +++ b/stubs/cpus-virtual-clock.c @@ -0,0 +1,13 @@ +#include "qemu/osdep.h" +#include "sysemu/cpu-timers.h" +#include "qemu/main-loop.h" + +int64_t cpus_get_virtual_clock(void) +{ + return cpu_get_clock(); +} + +void cpus_set_virtual_clock(int64_t new_time) +{ + /* do nothing */ +} diff --git a/stubs/fdset.c b/stubs/fdset.c index 56b3663d58..2950fd91fd 100644 --- a/stubs/fdset.c +++ b/stubs/fdset.c @@ -1,17 +1,18 @@ #include "qemu/osdep.h" +#include "qapi/error.h" #include "monitor/monitor.h" +#include "../monitor/monitor-internal.h" -int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags, Error **errp) { errno = ENOSYS; return -1; } -int64_t monitor_fdset_dup_fd_find(int dup_fd) -{ - return -1; -} - void monitor_fdset_dup_fd_remove(int dupfd) { } + +void monitor_fdsets_cleanup(void) +{ +} 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/hw/core/hotplug-stubs.c b/stubs/hotplug-stubs.c similarity index 100% rename from hw/core/hotplug-stubs.c rename to stubs/hotplug-stubs.c 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/isa-bus.c b/stubs/isa-bus.c deleted file mode 100644 index 522f448997..0000000000 --- a/stubs/isa-bus.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/isa/isa.h" - -ISADevice *isa_create_simple(ISABus *bus, const char *name) -{ - g_assert_not_reached(); -} diff --git a/stubs/meson.build b/stubs/meson.build index c96a74f095..e91614a874 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -1,64 +1,89 @@ -stub_ss.add(files('bdrv-next-monitor-owned.c')) -stub_ss.add(files('blk-commit-all.c')) -stub_ss.add(files('blk-exp-close-all.c')) -stub_ss.add(files('blockdev-close-all-bdrv-states.c')) -stub_ss.add(files('change-state-handler.c')) -stub_ss.add(files('cmos.c')) +# If possible, add new files to other directories, by using "if_false". +# If you need them here, try to add them under one of the if statements +# below, so that it is clear who needs the stubbed functionality. + stub_ss.add(files('cpu-get-clock.c')) -stub_ss.add(files('cpus-get-virtual-clock.c')) -stub_ss.add(files('qemu-timer-notify-cb.c')) -stub_ss.add(files('icount.c')) -stub_ss.add(files('dump.c')) 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')) -if linux_io_uring.found() - stub_ss.add(files('io_uring.c')) -endif stub_ss.add(files('iothread-lock.c')) -if have_block - stub_ss.add(files('iothread-lock-block.c')) -endif -stub_ss.add(files('isa-bus.c')) stub_ss.add(files('is-daemonized.c')) -if libaio.found() - stub_ss.add(files('linux-aio.c')) -endif -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('qmp-command-available.c')) -stub_ss.add(files('qmp-quit.c')) -stub_ss.add(files('qtest.c')) -stub_ss.add(files('ram-block.c')) -stub_ss.add(files('ramfb.c')) -stub_ss.add(files('replay.c')) -stub_ss.add(files('runstate-check.c')) -stub_ss.add(files('sysbus.c')) -stub_ss.add(files('target-get-monitor-def.c')) -stub_ss.add(files('target-monitor-defs.c')) +stub_ss.add(files('replay-mode.c')) stub_ss.add(files('trace-control.c')) -stub_ss.add(files('uuid.c')) -stub_ss.add(files('vmgenid.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 + stub_ss.add(files('bdrv-next-monitor-owned.c')) + stub_ss.add(files('blk-commit-all.c')) + stub_ss.add(files('blk-exp-close-all.c')) + stub_ss.add(files('blockdev-close-all-bdrv-states.c')) + stub_ss.add(files('change-state-handler.c')) + stub_ss.add(files('get-vm-name.c')) + stub_ss.add(files('iothread-lock-block.c')) + stub_ss.add(files('migr-blocker.c')) + stub_ss.add(files('physmem.c')) + stub_ss.add(files('ram-block.c')) + stub_ss.add(files('runstate-check.c')) + stub_ss.add(files('uuid.c')) +endif + if have_block or have_ga stub_ss.add(files('replay-tools.c')) + # stubs for hooks in util/main-loop.c, util/async.c etc. + stub_ss.add(files('cpus-virtual-clock.c')) + stub_ss.add(files('icount.c')) + stub_ss.add(files('graph-lock.c')) + if linux_io_uring.found() + stub_ss.add(files('io_uring.c')) + endif + if libaio.found() + stub_ss.add(files('linux-aio.c')) + endif + stub_ss.add(files('qemu-timer-notify-cb.c')) + + # stubs for monitor + stub_ss.add(files('monitor-internal.c')) + stub_ss.add(files('qmp-command-available.c')) + stub_ss.add(files('qmp-quit.c')) endif + +if have_block or have_user + stub_ss.add(files('qtest.c')) + stub_ss.add(files('vm-stop.c')) + stub_ss.add(files('vmstate.c')) +endif + +if have_user + # Symbols that are used by hw/core. + stub_ss.add(files('cpu-synchronize-state.c')) + + # Stubs for QAPI events. Those can always be included in the build, but + # they are not built at all for --disable-system --disable-tools builds. + if not (have_system or have_tools) + stub_ss.add(files('qdev.c')) + endif +endif + if have_system + # Symbols that are only needed in some configurations. Try not + # adding more of these. If the symbol is used in specific_ss, + # in particular, consider defining a preprocessor macro via + # Kconfig or configs/targets/. + stub_ss.add(files('dump.c')) + stub_ss.add(files('cmos.c')) stub_ss.add(files('fw_cfg.c')) - stub_ss.add(files('pci-bus.c')) - stub_ss.add(files('semihost.c')) - stub_ss.add(files('usb-dev-stub.c')) + stub_ss.add(files('target-get-monitor-def.c')) + stub_ss.add(files('target-monitor-defs.c')) + stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('xen-hw-stub.c')) -else - stub_ss.add(files('qdev.c')) endif -stub_ss.add(when: 'CONFIG_VFIO_USER_SERVER', if_false: files('vfio-user-obj.c')) + +if have_system or have_user + stub_ss.add(files('gdbstub.c')) + + # Also included in have_system for --disable-tcg builds + stub_ss.add(files('replay.c')) + + # Also included in have_system for tests/unit/test-qdev-global-props + stub_ss.add(files('hotplug-stubs.c')) + stub_ss.add(files('sysbus.c')) +endif 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/module-opts.c b/stubs/module-opts.c deleted file mode 100644 index 5412429ea8..0000000000 --- a/stubs/module-opts.c +++ /dev/null @@ -1,2 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/config-file.h" diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c index afa477aae6..1894cdfe1f 100644 --- a/stubs/monitor-core.c +++ b/stubs/monitor-core.c @@ -12,10 +12,6 @@ Monitor *monitor_set_cur(Coroutine *co, Monitor *mon) return NULL; } -void monitor_init_qmp(Chardev *chr, bool pretty, Error **errp) -{ -} - void qapi_event_emit(QAPIEvent event, QDict *qdict) { } @@ -24,5 +20,3 @@ int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); } - - diff --git a/stubs/monitor-internal.c b/stubs/monitor-internal.c new file mode 100644 index 0000000000..4fece49d53 --- /dev/null +++ b/stubs/monitor-internal.c @@ -0,0 +1,13 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "monitor/monitor.h" + +int monitor_get_fd(Monitor *mon, const char *name, Error **errp) +{ + error_setg(errp, "only QEMU supports file descriptor passing"); + return -1; +} + +void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) +{ +} diff --git a/stubs/monitor.c b/stubs/monitor.c deleted file mode 100644 index 20786ac4ff..0000000000 --- a/stubs/monitor.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "monitor/monitor.h" -#include "../monitor/monitor-internal.h" - -int monitor_get_fd(Monitor *mon, const char *name, Error **errp) -{ - error_setg(errp, "only QEMU supports file descriptor passing"); - return -1; -} - -void monitor_init_hmp(Chardev *chr, bool use_readline, Error **errp) -{ -} - -void monitor_fdsets_cleanup(void) -{ -} diff --git a/stubs/pci-bus.c b/stubs/pci-bus.c deleted file mode 100644 index a8932fa932..0000000000 --- a/stubs/pci-bus.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/pci/pci.h" - -PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) -{ - g_assert_not_reached(); -} diff --git a/stubs/qdev.c b/stubs/qdev.c index 187659f707..7e957b3e52 100644 --- a/stubs/qdev.c +++ b/stubs/qdev.c @@ -15,16 +15,8 @@ #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, - const char *path) -{ - /* Nothing to do. */ -} diff --git a/stubs/qmp_memory_device.c b/stubs/qmp_memory_device.c deleted file mode 100644 index e75cac62dc..0000000000 --- a/stubs/qmp_memory_device.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/mem/memory-device.h" - -MemoryDeviceInfoList *qmp_memory_device_list(void) -{ - return NULL; -} - -uint64_t get_plugged_memory_size(void) -{ - return (uint64_t)-1; -} diff --git a/stubs/qtest.c b/stubs/qtest.c index 4666a49d7d..39e376eb67 100644 --- a/stubs/qtest.c +++ b/stubs/qtest.c @@ -13,13 +13,3 @@ /* Needed for qtest_allowed() */ bool qtest_allowed; - -bool qtest_driver(void) -{ - return false; -} - -int64_t qtest_get_virtual_clock(void) -{ - return 0; -} diff --git a/stubs/ramfb.c b/stubs/ramfb.c deleted file mode 100644 index 48143f3354..0000000000 --- a/stubs/ramfb.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "hw/display/ramfb.h" - -void ramfb_display_update(QemuConsole *con, RAMFBState *s) -{ -} - -RAMFBState *ramfb_setup(Error **errp) -{ - error_setg(errp, "ramfb support not available"); - return NULL; -} diff --git a/stubs/replay-mode.c b/stubs/replay-mode.c new file mode 100644 index 0000000000..264be9d96c --- /dev/null +++ b/stubs/replay-mode.c @@ -0,0 +1,4 @@ +#include "qemu/osdep.h" +#include "sysemu/replay.h" + +ReplayMode replay_mode; diff --git a/stubs/replay.c b/stubs/replay.c index 9d5b4be339..b4dd6a566e 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -1,7 +1,5 @@ #include "qemu/osdep.h" -#include "sysemu/replay.h" - -ReplayMode replay_mode; +#include "exec/replay-core.h" void replay_finish(void) { diff --git a/stubs/semihost.c b/stubs/semihost.c deleted file mode 100644 index d65c9fd5dc..0000000000 --- a/stubs/semihost.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Semihosting Stubs for SoftMMU - * - * Copyright (c) 2019 Linaro Ltd - * - * Stubs for SoftMMU targets that don't actually do semihosting. - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qemu/option.h" -#include "qemu/error-report.h" -#include "semihosting/semihost.h" - -/* Empty config */ -QemuOptsList qemu_semihosting_config_opts = { - .name = "", - .head = QTAILQ_HEAD_INITIALIZER(qemu_semihosting_config_opts.head), - .desc = { - { /* end of list */ } - }, -}; - -/* Queries to config status default to off */ -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. - */ -void qemu_semihosting_enable(void) -{ -} - -int qemu_semihosting_config_options(const char *optarg) -{ - return 1; -} - -const char *semihosting_get_arg(int i) -{ - return NULL; -} - -int semihosting_get_argc(void) -{ - return 0; -} - -const char *semihosting_get_cmdline(void) -{ - return NULL; -} - -void semihosting_arg_fallback(const char *file, const char *cmd) -{ -} - -void qemu_semihosting_chardev_init(void) -{ -} diff --git a/stubs/target-monitor-defs.c b/stubs/target-monitor-defs.c index ac07b19064..35a0a34277 100644 --- a/stubs/target-monitor-defs.c +++ b/stubs/target-monitor-defs.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" - -const MonitorDef *target_monitor_defs(void); +#include "monitor/hmp-target.h" const MonitorDef *target_monitor_defs(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/usb-dev-stub.c b/stubs/usb-dev-stub.c deleted file mode 100644 index aa557692b7..0000000000 --- a/stubs/usb-dev-stub.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * QEMU USB device emulation stubs - * - * Copyright (C) 2021 Philippe Mathieu-Daudé - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "qemu/osdep.h" -#include "qemu/error-report.h" -#include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" -#include "sysemu/sysemu.h" -#include "monitor/monitor.h" -#include "hw/usb.h" - -USBDevice *usbdevice_create(const char *driver) -{ - error_report("Support for USB devices not built-in"); - - return NULL; -} - -HumanReadableText *qmp_x_query_usb(Error **errp) -{ - error_setg(errp, "Support for USB devices not built-in"); - return NULL; -} - -void hmp_info_usb(Monitor *mon, const QDict *qdict) -{ - monitor_printf(mon, "Support for USB devices not built-in\n"); -} 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 34a22f2ad7..6cf0e9a4c1 100644 --- a/stubs/xen-hw-stub.c +++ b/stubs/xen-hw-stub.c @@ -15,7 +15,7 @@ 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) { } @@ -24,10 +24,6 @@ int xen_set_pci_link_route(uint8_t link, uint8_t irq) return -1; } -void xen_hvm_inject_msi(uint64_t addr, uint32_t data) -{ -} - int xen_is_pirq_msi(uint32_t msi_data) { return 0; diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 103b99de1a..8930672a30 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -1,3 +1,23 @@ +/packagecache + +/berkeley-softfloat-3 +/berkeley-testfloat-3 +/dtc +/keycodemapdb +/libvfio-user +/slirp +/arbitrary-int-1.2.7 +/bilge-0.2.0 +/bilge-impl-0.2.0 +/either-1.12.0 +/itertools-0.11.0 +/proc-macro-error-1.0.4 +/proc-macro-error-attr-1.0.4 +/proc-macro2-1.0.84 +/quote-1.0.36 +/syn-2.0.66 +/unicode-ident-1.0.12 + glslang SPIRV-Reflect volk @@ -6,3 +26,6 @@ nv2a_vsh_cpu tomlplusplus cpp-httplib xxHash-* +imgui +implot +genconfig diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap new file mode 100644 index 0000000000..e580538a87 --- /dev/null +++ b/subprojects/arbitrary-int-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = arbitrary-int-1.2.7 +source_url = https://crates.io/api/v1/crates/arbitrary-int/1.2.7/download +source_filename = arbitrary-int-1.2.7.tar.gz +source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d +#method = cargo +patch_directory = arbitrary-int-1-rs 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/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap new file mode 100644 index 0000000000..7a4339d298 --- /dev/null +++ b/subprojects/bilge-0.2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = bilge-0.2.0 +source_url = https://crates.io/api/v1/crates/bilge/0.2.0/download +source_filename = bilge-0.2.0.tar.gz +source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 +#method = cargo +patch_directory = bilge-0.2-rs diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap new file mode 100644 index 0000000000..b24c34a904 --- /dev/null +++ b/subprojects/bilge-impl-0.2-rs.wrap @@ -0,0 +1,8 @@ +[wrap-file] +directory = bilge-impl-0.2.0 +source_url = https://crates.io/api/v1/crates/bilge-impl/0.2.0/download +source_filename = bilge-impl-0.2.0.tar.gz +source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 +#method = cargo +patch_directory = bilge-impl-0.2-rs +diff_files = bilge-impl-1.63.0.patch 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/either-1-rs.wrap b/subprojects/either-1-rs.wrap new file mode 100644 index 0000000000..6046712036 --- /dev/null +++ b/subprojects/either-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = either-1.12.0 +source_url = https://crates.io/api/v1/crates/either/1.12.0/download +source_filename = either-1.12.0.tar.gz +source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b +#method = cargo +patch_directory = either-1-rs diff --git a/subprojects/genconfig.wrap b/subprojects/genconfig.wrap new file mode 100644 index 0000000000..c4664941d5 --- /dev/null +++ b/subprojects/genconfig.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url=https://github.com/mborgerson/genconfig.git +revision=42f85f9a2457e61d7e32542c07723565a284fcd6 +depth=1 diff --git a/subprojects/imgui.wrap b/subprojects/imgui.wrap new file mode 100644 index 0000000000..7494556bf2 --- /dev/null +++ b/subprojects/imgui.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url=https://github.com/xemu-project/imgui +revision=80cbdab5ecd70db79917c448c333163995e605a5 +depth=1 diff --git a/subprojects/implot.wrap b/subprojects/implot.wrap new file mode 100644 index 0000000000..78c9322e66 --- /dev/null +++ b/subprojects/implot.wrap @@ -0,0 +1,4 @@ +[wrap-git] +url=https://github.com/xemu-project/implot +revision=006a1c23e5706bbe816968163b4d589162257a57 +depth=1 diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap new file mode 100644 index 0000000000..66b05252cd --- /dev/null +++ b/subprojects/itertools-0.11-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = itertools-0.11.0 +source_url = https://crates.io/api/v1/crates/itertools/0.11.0/download +source_filename = itertools-0.11.0.tar.gz +source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 +#method = cargo +patch_directory = itertools-0.11-rs 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/libvduse.c b/subprojects/libvduse/libvduse.c index e089d4d546..21ffbb5b8d 100644 --- a/subprojects/libvduse/libvduse.c +++ b/subprojects/libvduse/libvduse.c @@ -16,6 +16,10 @@ * later. See the COPYING file in the top-level directory. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -97,7 +101,7 @@ struct VduseVirtq { uint16_t signalled_used; bool signalled_used_valid; int index; - int inuse; + unsigned int inuse; bool ready; int fd; VduseDev *dev; @@ -578,7 +582,8 @@ void vduse_queue_notify(VduseVirtq *vq) static inline void vring_set_avail_event(VduseVirtq *vq, uint16_t val) { - *((uint16_t *)&vq->vring.used->ring[vq->vring.num]) = htole16(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, @@ -1026,7 +1031,7 @@ int vduse_dev_handler(VduseDev *dev) /* 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++) { - VduseVirtq *vq = &dev->vqs[i]; + vq = &dev->vqs[i]; if (vq->ready) { if (vduse_queue_update_vring(vq, vq->vring.desc_addr, vq->vring.avail_addr, diff --git a/subprojects/libvduse/meson.build b/subprojects/libvduse/meson.build index ba08f5ee1a..3e3b53da33 100644 --- a/subprojects/libvduse/meson.build +++ b/subprojects/libvduse/meson.build @@ -1,6 +1,12 @@ project('libvduse', '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') libvduse = static_library('vduse', files('libvduse.c'), diff --git a/subprojects/libvfio-user b/subprojects/libvfio-user deleted file mode 160000 index 0b28d20557..0000000000 --- a/subprojects/libvfio-user +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0b28d205572c80b568a1003db2c8f37ca333e4d7 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 b17e82b2b0..9c630c2170 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 @@ -58,8 +70,8 @@ #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 @@ -136,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), @@ -151,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 @@ -184,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++) { @@ -229,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) { @@ -312,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(vmsg->fd_num <= VHOST_MEMORY_BASELINE_NREGIONS); memcpy(vmsg->fds, CMSG_DATA(cmsg), fd_size); break; } @@ -335,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; @@ -375,12 +632,18 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) memcpy(CMSG_DATA(cmsg), vmsg->fds, fdsize); } else { msg.msg_controllen = 0; + msg.msg_control = NULL; } do { rc = sendmsg(conn_fd, &msg, 0); } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (rc <= 0) { + vu_panic(dev, "Error while writing: %s", strerror(errno)); + return false; + } + if (vmsg->size) { do { if (vmsg->data) { @@ -411,8 +674,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 @@ -426,7 +689,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; } @@ -439,7 +702,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; } @@ -600,28 +863,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. @@ -655,7 +905,7 @@ 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; @@ -670,7 +920,7 @@ generate_faults(VuDev *dev) { 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; @@ -696,11 +946,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); @@ -730,84 +976,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; DPRINT("Successfully added new region in postcopy\n"); - dev->nregions++; return true; - } 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; } + DPRINT("Successfully added new region\n"); + return false; } static inline bool reg_equal(VuDevRegion *vudev_reg, @@ -825,8 +1011,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); @@ -853,175 +1039,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++) { @@ -1359,7 +1461,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 = { @@ -1377,20 +1479,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) { @@ -1451,13 +1652,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; @@ -1473,6 +1674,17 @@ vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) features |= dev->iface->get_protocol_features(dev); } +#ifndef MFD_ALLOW_SEALING + /* + * If MFD_ALLOW_SEALING is not defined, we are not able to handle + * VHOST_USER_GET_INFLIGHT_FD messages, since we can't create a memfd. + * Those messages are used only if VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD + * is negotiated. A device implementation can enable it, so let's mask + * it to avoid a runtime panic. + */ + features &= ~(1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD); +#endif + vmsg_set_reply_u64(vmsg, features); return true; } @@ -1488,7 +1700,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 @@ -1496,12 +1708,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; } @@ -1538,18 +1750,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; } @@ -1565,7 +1777,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; } @@ -1593,12 +1805,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) { @@ -1903,8 +2116,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: @@ -1930,6 +2143,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); @@ -1976,16 +2191,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]; @@ -2025,11 +2233,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); @@ -2037,6 +2245,8 @@ vu_deinit(VuDev *dev) free(dev->vq); dev->vq = NULL; + free(dev->regions); + dev->regions = NULL; } bool @@ -2067,13 +2277,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; } @@ -2240,8 +2458,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; } @@ -2356,8 +2573,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; } @@ -2396,8 +2612,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; } @@ -2409,9 +2624,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 = { @@ -2426,9 +2641,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; } @@ -2448,6 +2663,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) { @@ -2471,14 +2696,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 @@ -2713,8 +2937,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; } @@ -2871,8 +3094,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; } @@ -2901,8 +3123,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/nv2a_vsh_cpu.wrap b/subprojects/nv2a_vsh_cpu.wrap index 705bfe5263..816847e673 100644 --- a/subprojects/nv2a_vsh_cpu.wrap +++ b/subprojects/nv2a_vsh_cpu.wrap @@ -1,4 +1,4 @@ [wrap-git] url=https://github.com/xemu-project/nv2a_vsh_cpu -revision=d5a7308809a80e1b01b5c016127d4f1b91c8673b +revision=561fe80da57a881f89000256b459440c0178a7ce depth=1 diff --git a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build new file mode 100644 index 0000000000..cff3f62ce7 --- /dev/null +++ b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build @@ -0,0 +1,20 @@ +project('arbitrary-int-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.2.7', + license: 'MIT', + default_options: []) + +_arbitrary_int_rs = static_library( + 'arbitrary_int', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [], +) + +arbitrary_int_dep = declare_dependency( + link_with: _arbitrary_int_rs, +) + +meson.override_dependency('arbitrary-int-1-rs', arbitrary_int_dep) 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/packagefiles/bilge-0.2-rs/meson.build b/subprojects/packagefiles/bilge-0.2-rs/meson.build new file mode 100644 index 0000000000..e69bac91b4 --- /dev/null +++ b/subprojects/packagefiles/bilge-0.2-rs/meson.build @@ -0,0 +1,30 @@ +project( + 'bilge-0.2-rs', + 'rust', + meson_version: '>=1.5.0', + version : '0.2.0', + license : 'MIT or Apache-2.0', +) + +subproject('arbitrary-int-1-rs', required: true) +subproject('bilge-impl-0.2-rs', required: true) + +arbitrary_int_dep = dependency('arbitrary-int-1-rs') +bilge_impl_dep = dependency('bilge-impl-0.2-rs') + +lib = static_library( + 'bilge', + 'src/lib.rs', + override_options : ['rust_std=2021', 'build.rust_std=2021'], + rust_abi : 'rust', + dependencies: [ + arbitrary_int_dep, + bilge_impl_dep, + ], +) + +bilge_dep = declare_dependency( + link_with : [lib], +) + +meson.override_dependency('bilge-0.2-rs', bilge_dep) diff --git a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build new file mode 100644 index 0000000000..f8f3486fc0 --- /dev/null +++ b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build @@ -0,0 +1,46 @@ +project('bilge-impl-0.2-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('itertools-0.11-rs', required: true) +subproject('proc-macro-error-attr-1-rs', required: true) +subproject('proc-macro-error-1-rs', required: true) +subproject('quote-1-rs', required: true) +subproject('syn-2-rs', required: true) +subproject('proc-macro2-1-rs', required: true) + +itertools_dep = dependency('itertools-0.11-rs', native: true) +proc_macro_error_attr_dep = dependency('proc-macro-error-attr-1-rs', native: true) +proc_macro_error_dep = dependency('proc-macro-error-1-rs', native: true) +quote_dep = dependency('quote-1-rs', native: true) +syn_dep = dependency('syn-2-rs', native: true) +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) + +rust = import('rust') + +_bilge_impl_rs = rust.proc_macro( + 'bilge_impl', + files('src/lib.rs'), + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_args: [ + '--cfg', 'use_fallback', + '--cfg', 'feature="syn-error"', + '--cfg', 'feature="proc-macro"', + ], + dependencies: [ + itertools_dep, + proc_macro_error_attr_dep, + proc_macro_error_dep, + quote_dep, + syn_dep, + proc_macro2_dep, + ], +) + +bilge_impl_dep = declare_dependency( + link_with: _bilge_impl_rs, +) + +meson.override_dependency('bilge-impl-0.2-rs', bilge_impl_dep) diff --git a/subprojects/packagefiles/bilge-impl-1.63.0.patch b/subprojects/packagefiles/bilge-impl-1.63.0.patch new file mode 100644 index 0000000000..987428a6d6 --- /dev/null +++ b/subprojects/packagefiles/bilge-impl-1.63.0.patch @@ -0,0 +1,45 @@ +--- a/src/shared/discriminant_assigner.rs ++++ b/src/shared/discriminant_assigner.rs +@@ -26,20 +26,20 @@ + let discriminant_expr = &discriminant.1; + let variant_name = &variant.ident; + +- let Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) = discriminant_expr else { ++ if let Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) = discriminant_expr { ++ let discriminant_value: u128 = int.base10_parse().unwrap_or_else(unreachable); ++ if discriminant_value > self.max_value() { ++ abort!(variant, "Value of variant exceeds the given number of bits") ++ } ++ ++ Some(discriminant_value) ++ } else { + abort!( + discriminant_expr, + "variant `{}` is not a number", variant_name; + help = "only literal integers currently supported" + ) +- }; +- +- let discriminant_value: u128 = int.base10_parse().unwrap_or_else(unreachable); +- if discriminant_value > self.max_value() { +- abort!(variant, "Value of variant exceeds the given number of bits") + } +- +- Some(discriminant_value) + } + + fn assign(&mut self, variant: &Variant) -> u128 { +--- a/src/shared/fallback.rs ++++ b/src/shared/fallback.rs +@@ -22,8 +22,9 @@ + } + Unnamed(fields) => { + let variant_fields = fields.unnamed.iter(); +- let Ok(fallback_value) = variant_fields.exactly_one() else { +- abort!(variant, "fallback variant must have exactly one field"; help = "use only one field or change to a unit variant") ++ let fallback_value = match variant_fields.exactly_one() { ++ Ok(ok) => ok, ++ _ => abort!(variant, "fallback variant must have exactly one field"; help = "use only one field or change to a unit variant") + }; + + if !is_last_variant { diff --git a/subprojects/packagefiles/either-1-rs/meson.build b/subprojects/packagefiles/either-1-rs/meson.build new file mode 100644 index 0000000000..608e64e31f --- /dev/null +++ b/subprojects/packagefiles/either-1-rs/meson.build @@ -0,0 +1,25 @@ +project('either-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.12.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +_either_rs = static_library( + 'either', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2018', 'build.rust_std=2018'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'feature="use_std"', + '--cfg', 'feature="use_alloc"', + ], + dependencies: [], + native: true, +) + +either_dep = declare_dependency( + link_with: _either_rs, +) + +meson.override_dependency('either-1-rs', either_dep, native: true) diff --git a/subprojects/packagefiles/itertools-0.11-rs/meson.build b/subprojects/packagefiles/itertools-0.11-rs/meson.build new file mode 100644 index 0000000000..30982a4ee7 --- /dev/null +++ b/subprojects/packagefiles/itertools-0.11-rs/meson.build @@ -0,0 +1,31 @@ +project('itertools-0.11-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.11.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('either-1-rs', required: true) + +either_dep = dependency('either-1-rs', native: true) + +_itertools_rs = static_library( + 'itertools', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2018', 'build.rust_std=2018'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'feature="use_std"', + '--cfg', 'feature="use_alloc"', + ], + dependencies: [ + either_dep, + ], + native: true, +) + +itertools_dep = declare_dependency( + link_with: _itertools_rs, +) + +meson.override_dependency('itertools-0.11-rs', itertools_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build new file mode 100644 index 0000000000..ae27a69686 --- /dev/null +++ b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build @@ -0,0 +1,41 @@ +project('proc-macro-error-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.0.4', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('proc-macro-error-attr-1-rs', required: true) +subproject('quote-1-rs', required: true) +subproject('syn-2-rs', required: true) +subproject('proc-macro2-1-rs', required: true) + +proc_macro_error_attr_dep = dependency('proc-macro-error-attr-1-rs', native: true) +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) +quote_dep = dependency('quote-1-rs', native: true) +syn_dep = dependency('syn-2-rs', native: true) + +_proc_macro_error_rs = static_library( + 'proc_macro_error', + files('src/lib.rs'), + override_options: ['rust_std=2018', 'build.rust_std=2018'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'use_fallback', + '--cfg', 'feature="syn-error"', + '--cfg', 'feature="proc-macro"', + '-A', 'non_fmt_panics' + ], + dependencies: [ + proc_macro_error_attr_dep, + proc_macro2_dep, + quote_dep, + syn_dep, + ], + native: true, +) + +proc_macro_error_dep = declare_dependency( + link_with: _proc_macro_error_rs, +) + +meson.override_dependency('proc-macro-error-1-rs', proc_macro_error_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build new file mode 100644 index 0000000000..3281b26433 --- /dev/null +++ b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build @@ -0,0 +1,33 @@ +project('proc-macro-error-attr-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.12.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('proc-macro2-1-rs', required: true) +subproject('quote-1-rs', required: true) + +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) +quote_dep = dependency('quote-1-rs', native: true) + +rust = import('rust') +_proc_macro_error_attr_rs = rust.proc_macro( + 'proc_macro_error_attr', + files('src/lib.rs'), + override_options: ['rust_std=2018', 'build.rust_std=2018'], + rust_args: [ + '--cfg', 'use_fallback', + '--cfg', 'feature="syn-error"', + '--cfg', 'feature="proc-macro"' + ], + dependencies: [ + proc_macro2_dep, + quote_dep, + ], +) + +proc_macro_error_attr_dep = declare_dependency( + link_with: _proc_macro_error_attr_rs, +) + +meson.override_dependency('proc-macro-error-attr-1-rs', proc_macro_error_attr_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build new file mode 100644 index 0000000000..f9c8675eba --- /dev/null +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -0,0 +1,34 @@ +project('proc-macro2-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.0.84', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('unicode-ident-1-rs', required: true) + +unicode_ident_dep = dependency('unicode-ident-1-rs', native: true) + +_proc_macro2_rs = static_library( + 'proc_macro2', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'feature="proc-macro"', + '--cfg', 'no_literal_byte_character', + '--cfg', 'no_literal_c_string', + '--cfg', 'no_source_text', + '--cfg', 'wrap_proc_macro', + ], + dependencies: [ + unicode_ident_dep, + ], + native: true, +) + +proc_macro2_dep = declare_dependency( + link_with: _proc_macro2_rs, +) + +meson.override_dependency('proc-macro2-1-rs', proc_macro2_dep, native: true) diff --git a/subprojects/packagefiles/quote-1-rs/meson.build b/subprojects/packagefiles/quote-1-rs/meson.build new file mode 100644 index 0000000000..7f7792569b --- /dev/null +++ b/subprojects/packagefiles/quote-1-rs/meson.build @@ -0,0 +1,30 @@ +project('quote-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.12.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('proc-macro2-1-rs', required: true) + +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) + +_quote_rs = static_library( + 'quote', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'feature="proc-macro"', + ], + dependencies: [ + proc_macro2_dep, + ], + native: true, +) + +quote_dep = declare_dependency( + link_with: _quote_rs, +) + +meson.override_dependency('quote-1-rs', quote_dep, native: true) diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build new file mode 100644 index 0000000000..2c62cf7e1b --- /dev/null +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -0,0 +1,42 @@ +project('syn-2-rs', 'rust', + meson_version: '>=1.5.0', + version: '2.0.66', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('proc-macro2-1-rs', required: true) +subproject('quote-1-rs', required: true) +subproject('unicode-ident-1-rs', required: true) + +proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) +quote_dep = dependency('quote-1-rs', native: true) +unicode_ident_dep = dependency('unicode-ident-1-rs', native: true) + +_syn_rs = static_library( + 'syn', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cfg', 'feature="full"', + '--cfg', 'feature="derive"', + '--cfg', 'feature="parsing"', + '--cfg', 'feature="printing"', + '--cfg', 'feature="clone-impls"', + '--cfg', 'feature="proc-macro"', + '--cfg', 'feature="extra-traits"', + ], + dependencies: [ + quote_dep, + proc_macro2_dep, + unicode_ident_dep, + ], + native: true, +) + +syn_dep = declare_dependency( + link_with: _syn_rs, +) + +meson.override_dependency('syn-2-rs', syn_dep, native: true) diff --git a/subprojects/packagefiles/unicode-ident-1-rs/meson.build b/subprojects/packagefiles/unicode-ident-1-rs/meson.build new file mode 100644 index 0000000000..9d76ebbd1a --- /dev/null +++ b/subprojects/packagefiles/unicode-ident-1-rs/meson.build @@ -0,0 +1,21 @@ +project('unicode-ident-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.0.12', + license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016', + default_options: []) + +_unicode_ident_rs = static_library( + 'unicode_ident', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [], + native: true, +) + +unicode_ident_dep = declare_dependency( + link_with: _unicode_ident_rs, +) + +meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true) diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap new file mode 100644 index 0000000000..b7db03b06a --- /dev/null +++ b/subprojects/proc-macro-error-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = proc-macro-error-1.0.4 +source_url = https://crates.io/api/v1/crates/proc-macro-error/1.0.4/download +source_filename = proc-macro-error-1.0.4.tar.gz +source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c +#method = cargo +patch_directory = proc-macro-error-1-rs diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap new file mode 100644 index 0000000000..d13d8a239a --- /dev/null +++ b/subprojects/proc-macro-error-attr-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = proc-macro-error-attr-1.0.4 +source_url = https://crates.io/api/v1/crates/proc-macro-error-attr/1.0.4/download +source_filename = proc-macro-error-attr-1.0.4.tar.gz +source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 +#method = cargo +patch_directory = proc-macro-error-attr-1-rs diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap new file mode 100644 index 0000000000..7053e2c013 --- /dev/null +++ b/subprojects/proc-macro2-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = proc-macro2-1.0.84 +source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.84/download +source_filename = proc-macro2-1.0.84.0.tar.gz +source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6 +#method = cargo +patch_directory = proc-macro2-1-rs diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap new file mode 100644 index 0000000000..6e7ea69049 --- /dev/null +++ b/subprojects/quote-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = quote-1.0.36 +source_url = https://crates.io/api/v1/crates/quote/1.0.36/download +source_filename = quote-1.0.36.0.tar.gz +source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7 +#method = cargo +patch_directory = quote-1-rs 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/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap new file mode 100644 index 0000000000..13ffdac3c3 --- /dev/null +++ b/subprojects/syn-2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = syn-2.0.66 +source_url = https://crates.io/api/v1/crates/syn/2.0.66/download +source_filename = syn-2.0.66.0.tar.gz +source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5 +#method = cargo +patch_directory = syn-2-rs diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap new file mode 100644 index 0000000000..4609f96ed9 --- /dev/null +++ b/subprojects/unicode-ident-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = unicode-ident-1.0.12 +source_url = https://crates.io/api/v1/crates/unicode-ident/1.0.12/download +source_filename = unicode-ident-1.0.12.tar.gz +source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b +#method = cargo +patch_directory = unicode-ident-1-rs diff --git a/subprojects/unicode-ident-1-rs/meson.build b/subprojects/unicode-ident-1-rs/meson.build new file mode 100644 index 0000000000..54f2376854 --- /dev/null +++ b/subprojects/unicode-ident-1-rs/meson.build @@ -0,0 +1,20 @@ +project('unicode-ident-1-rs', 'rust', + version: '1.0.12', + license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016', + default_options: []) + +_unicode_ident_rs = static_library( + 'unicode_ident', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [], + native: true, +) + +unicode_ident_dep = declare_dependency( + link_with: _unicode_ident_rs, +) + +meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true) 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..9148ee8d04 --- /dev/null +++ b/system/async-teardown.c @@ -0,0 +1,108 @@ +/* + * 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; + +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. + */ + qemu_close_all_open_fd(NULL, 0); + + /* 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-timers.c b/system/cpu-timers.c similarity index 95% rename from softmmu/cpu-timers.c rename to system/cpu-timers.c index 117408cb83..856e502e34 100644 --- a/softmmu/cpu-timers.c +++ b/system/cpu-timers.c @@ -35,8 +35,7 @@ #include "sysemu/runstate.h" #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 +153,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 +164,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 +176,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 +188,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 +203,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 +220,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 } @@ -272,6 +271,4 @@ void cpu_timers_init(void) seqlock_init(&timers_state.vm_clock_seqlock); qemu_spin_init(&timers_state.vm_clock_lock); vmstate_register(NULL, 0, &vmstate_timers, &timers_state); - - cpu_throttle_init(); } diff --git a/softmmu/cpus.c b/system/cpus.c similarity index 84% rename from softmmu/cpus.c rename to system/cpus.c index 5a584a8d57..1c818ff682 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) { /* @@ -220,6 +229,17 @@ int64_t cpus_get_virtual_clock(void) return cpu_get_clock(); } +/* + * Signal the new virtual time to the accelerator. This is only needed + * by accelerators that need to track the changes as we warp time. + */ +void cpus_set_virtual_clock(int64_t new_time) +{ + if (cpus_accel && cpus_accel->set_virtual_clock) { + cpus_accel->set_virtual_clock(new_time); + } +} + /* * return the time elapsed in VM between vm_start and vm_stop. Unless * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle @@ -251,14 +271,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(); @@ -381,14 +420,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 +443,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,18 +459,12 @@ 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); } @@ -479,46 +512,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 */ @@ -535,6 +568,22 @@ void cpu_thread_signal_destroyed(CPUState *cpu) qemu_cond_signal(&qemu_cpu_cond); } +void cpu_pause(CPUState *cpu) +{ + if (qemu_cpu_is_self(cpu)) { + qemu_cpu_stop(cpu, true); + } else { + cpu->stop = true; + qemu_cpu_kick(cpu); + } +} + +void cpu_resume(CPUState *cpu) +{ + cpu->stop = false; + cpu->stopped = false; + qemu_cpu_kick(cpu); +} static bool all_vcpus_paused(void) { @@ -555,12 +604,7 @@ void pause_all_vcpus(void) qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false); CPU_FOREACH(cpu) { - if (qemu_cpu_is_self(cpu)) { - qemu_cpu_stop(cpu, true); - } else { - cpu->stop = true; - qemu_cpu_kick(cpu); - } + cpu_pause(cpu); } /* We need to drop the replay_lock so any vCPU threads woken up @@ -569,22 +613,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(); -} - -void cpu_resume(CPUState *cpu) -{ - cpu->stop = false; - cpu->stopped = false; - qemu_cpu_kick(cpu); + bql_lock(); } void resume_all_vcpus(void) @@ -606,9 +643,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) @@ -629,7 +666,7 @@ 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(); @@ -647,7 +684,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); } } @@ -677,11 +714,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); @@ -712,9 +751,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) @@ -724,11 +764,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; @@ -743,14 +792,14 @@ int vm_stop_force_state(RunState state) } } -void qmp_memsave(int64_t addr, int64_t size, const char *filename, +void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, bool has_cpu, int64_t cpu_index, Error **errp) { FILE *f; - uint32_t l; + uint64_t l; CPUState *cpu; uint8_t buf[1024]; - int64_t orig_addr = addr, orig_size = size; + uint64_t orig_addr = addr, orig_size = size; if (!has_cpu) { cpu_index = 0; @@ -774,12 +823,13 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, if (l > size) l = size; if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { - error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRId64 + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 " specified", orig_addr, orig_size); goto exit; } if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "writing memory to '%s' failed", + filename); goto exit; } addr += l; @@ -790,11 +840,11 @@ exit: fclose(f); } -void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, +void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, Error **errp) { FILE *f; - uint32_t l; + uint64_t l; uint8_t buf[1024]; f = fopen(filename, "wb"); @@ -809,7 +859,8 @@ void qmp_pmemsave(int64_t addr, int64_t size, const char *filename, l = size; cpu_physical_memory_read(addr, buf, l); if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, QERR_IO_ERROR); + error_setg(errp, "writing memory to '%s' failed", + filename); goto exit; } addr += l; diff --git a/softmmu/datadir.c b/system/datadir.c similarity index 100% rename from softmmu/datadir.c rename to system/datadir.c diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c new file mode 100644 index 0000000000..bddda6fa37 --- /dev/null +++ b/system/device_tree-stub.c @@ -0,0 +1,10 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +#ifdef CONFIG_FDT +void qmp_dumpdtb(const char *filename, Error **errp) +{ + error_setg(errp, "This machine doesn't have a FDT"); +} +#endif diff --git a/softmmu/device_tree.c b/system/device_tree.c similarity index 97% rename from softmmu/device_tree.c rename to system/device_tree.c index 30aa3aea9f..2e38259d34 100644 --- a/softmmu/device_tree.c +++ b/system/device_tree.c @@ -418,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); @@ -668,20 +668,6 @@ void qmp_dumpdtb(const char *filename, Error **errp) } } -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; diff --git a/softmmu/dirtylimit.c b/system/dirtylimit.c similarity index 81% rename from softmmu/dirtylimit.c rename to system/dirtylimit.c index 12668555f2..ab20da34bb 100644 --- a/softmmu/dirtylimit.c +++ b/system/dirtylimit.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qapi/error.h" #include "qemu/main-loop.h" #include "qapi/qapi-commands-migration.h" #include "qapi/qmp/qdict.h" @@ -21,9 +20,11 @@ #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 @@ -77,12 +78,18 @@ 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(DIRTYLIMIT_CALC_TIME_MS, - &stat, - GLOBAL_DIRTY_LIMIT, - false); + 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; @@ -90,7 +97,7 @@ static void vcpu_dirty_rate_stat_collect(void) stat.rates[i].dirty_rate; } - free(stat.rates); + g_free(stat.rates); } static void *vcpu_dirty_rate_stat_thread(void *opaque) @@ -138,9 +145,9 @@ void vcpu_dirty_rate_stat_stop(void) { qatomic_set(&vcpu_dirty_rate_stat->running, 0); dirtylimit_state_unlock(); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_thread_join(&vcpu_dirty_rate_stat->thread); - qemu_mutex_lock_iothread(); + bql_lock(); dirtylimit_state_lock(); } @@ -161,10 +168,10 @@ void vcpu_dirty_rate_stat_initialize(void) void vcpu_dirty_rate_stat_finalize(void) { - free(vcpu_dirty_rate_stat->stat.rates); + g_free(vcpu_dirty_rate_stat->stat.rates); vcpu_dirty_rate_stat->stat.rates = NULL; - free(vcpu_dirty_rate_stat); + g_free(vcpu_dirty_rate_stat); vcpu_dirty_rate_stat = NULL; } @@ -210,10 +217,10 @@ void dirtylimit_state_initialize(void) void dirtylimit_state_finalize(void) { - free(dirtylimit_state->states); + g_free(dirtylimit_state->states); dirtylimit_state->states = NULL; - free(dirtylimit_state); + g_free(dirtylimit_state); dirtylimit_state = NULL; trace_dirtylimit_state_finalize(); @@ -232,18 +239,18 @@ bool dirtylimit_vcpu_index_valid(int cpu_index) cpu_index >= ms->smp.max_cpus); } -static inline int64_t dirtylimit_dirty_ring_full_time(uint64_t dirtyrate) +static uint64_t dirtylimit_dirty_ring_full_time(uint64_t dirtyrate) { static uint64_t max_dirtyrate; - uint32_t dirty_ring_size = kvm_dirty_ring_size(); - uint64_t dirty_ring_size_meory_MB = - dirty_ring_size * TARGET_PAGE_SIZE >> 20; + 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_meory_MB * 1000000 / max_dirtyrate; + return dirty_ring_size_MiB * 1000000 / max_dirtyrate; } static inline bool dirtylimit_done(uint64_t quota, @@ -401,12 +408,20 @@ void dirtylimit_set_all(uint64_t quota, void dirtylimit_vcpu_execute(CPUState *cpu) { - if (dirtylimit_in_service() && - dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled && - cpu->throttle_us_per_full) { - trace_dirtylimit_vcpu_execute(cpu->cpu_index, - cpu->throttle_us_per_full); - usleep(cpu->throttle_us_per_full); + 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(); } } @@ -426,6 +441,21 @@ static void dirtylimit_cleanup(void) 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) @@ -439,6 +469,12 @@ void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index, 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; } @@ -489,6 +525,12 @@ void qmp_set_vcpu_dirty_limit(bool has_cpu_index, 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; @@ -515,14 +557,54 @@ void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); Error *err = NULL; - qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); - if (err) { - hmp_handle_error(mon, err); - return; + if (dirty_rate < 0) { + error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate); + goto out; } - monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " - "dirty limit for virtual CPU]\n"); + 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) @@ -565,16 +647,13 @@ static struct DirtyLimitInfoList *dirtylimit_query_all(void) struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) { - if (!dirtylimit_in_service()) { - return NULL; - } - return dirtylimit_query_all(); } void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) { - DirtyLimitInfoList *limit, *head, *info = NULL; + DirtyLimitInfoList *info; + g_autoptr(DirtyLimitInfoList) head = NULL; Error *err = NULL; if (!dirtylimit_in_service()) { @@ -582,20 +661,17 @@ void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) return; } - info = qmp_query_vcpu_dirty_limit(&err); + head = qmp_query_vcpu_dirty_limit(&err); if (err) { hmp_handle_error(mon, err); return; } - head = info; - for (limit = head; limit != NULL; limit = limit->next) { + for (info = head; info != NULL; info = info->next) { monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," " current rate %"PRIi64 " (MB/s)\n", - limit->value->cpu_index, - limit->value->limit_rate, - limit->value->current_rate); + info->value->cpu_index, + info->value->limit_rate, + info->value->current_rate); } - - g_free(info); } diff --git a/softmmu/dma-helpers.c b/system/dma-helpers.c similarity index 95% rename from softmmu/dma-helpers.c rename to system/dma-helpers.c index 7820fec54c..cbcd89dfaa 100644 --- a/softmmu/dma-helpers.c +++ b/system/dma-helpers.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" -#include "trace/trace-root.h" +#include "trace.h" #include "qemu/thread.h" #include "qemu/main-loop.h" #include "sysemu/cpu-timers.h" @@ -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,8 +168,8 @@ 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); - cpu_register_map_client(dbs->bh); + dbs->bh = aio_bh_new(ctx, reschedule_dma, dbs); + address_space_register_map_client(dbs->sg->as, 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); } @@ -195,7 +197,7 @@ static void dma_aio_cancel(BlockAIOCB *acb) } if (dbs->bh) { - cpu_unregister_map_client(dbs->bh); + address_space_unregister_map_client(dbs->sg->as, dbs->bh); qemu_bh_delete(dbs->bh); dbs->bh = NULL; } @@ -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 89% rename from softmmu/globals.c rename to system/globals.c index 527edbefdd..84ce943ac9 100644 --- a/softmmu/globals.c +++ b/system/globals.c @@ -36,16 +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; int old_param; @@ -63,5 +57,10 @@ 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; +bool xen_is_stubdomain; +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/system/ioport.c b/system/ioport.c new file mode 100644 index 0000000000..fd551d0375 --- /dev/null +++ b/system/ioport.c @@ -0,0 +1,373 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +/* + * split out ioport related stuffs from vl.c. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/ioport.h" +#include "exec/memory.h" +#include "exec/address-spaces.h" +#include "trace.h" + +struct MemoryRegionPortioList { + Object obj; + + MemoryRegion mr; + void *portio_opaque; + 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) +{ + return -1ULL; +} + +static void unassigned_io_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ +} + +const MemoryRegionOps unassigned_io_ops = { + .read = unassigned_io_read, + .write = unassigned_io_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void cpu_outb(uint32_t addr, uint8_t val) +{ + trace_cpu_out(addr, 'b', val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + &val, 1); +} + +void cpu_outw(uint32_t addr, uint16_t val) +{ + uint8_t buf[2]; + + trace_cpu_out(addr, 'w', val); + stw_p(buf, val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + buf, 2); +} + +void cpu_outl(uint32_t addr, uint32_t val) +{ + uint8_t buf[4]; + + trace_cpu_out(addr, 'l', val); + stl_p(buf, val); + address_space_write(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + buf, 4); +} + +uint8_t cpu_inb(uint32_t addr) +{ + uint8_t val; + + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, + &val, 1); + trace_cpu_in(addr, 'b', val); + return val; +} + +uint16_t cpu_inw(uint32_t addr) +{ + uint8_t buf[2]; + uint16_t val; + + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 2); + val = lduw_p(buf); + trace_cpu_in(addr, 'w', val); + return val; +} + +uint32_t cpu_inl(uint32_t addr) +{ + uint8_t buf[4]; + uint32_t val; + + address_space_read(&address_space_io, addr, MEMTXATTRS_UNSPECIFIED, buf, 4); + val = ldl_p(buf); + trace_cpu_in(addr, 'l', val); + return val; +} + +void portio_list_init(PortioList *piolist, + Object *owner, + const MemoryRegionPortio *callbacks, + void *opaque, const char *name) +{ + unsigned n = 0; + + while (callbacks[n].size) { + ++n; + } + + piolist->ports = callbacks; + 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; + piolist->flush_coalesced_mmio = false; +} + +void portio_list_set_flush_coalesced(PortioList *piolist) +{ + piolist->flush_coalesced_mmio = true; +} + +void portio_list_destroy(PortioList *piolist) +{ + MemoryRegionPortioList *mrpio; + unsigned i; + + for (i = 0; i < piolist->nr; ++i) { + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + object_unparent(OBJECT(&mrpio->mr)); + object_unref(mrpio); + } + g_free(piolist->regions); +} + +static const MemoryRegionPortio *find_portio(MemoryRegionPortioList *mrpio, + uint64_t offset, unsigned size, + bool write) +{ + const MemoryRegionPortio *mrp; + + for (mrp = mrpio->ports; mrp->size; ++mrp) { + if (offset >= mrp->offset && offset < mrp->offset + mrp->len && + size == mrp->size && + (write ? (bool)mrp->write : (bool)mrp->read)) { + return mrp; + } + } + return NULL; +} + +static uint64_t portio_read(void *opaque, hwaddr addr, unsigned size) +{ + MemoryRegionPortioList *mrpio = opaque; + const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, false); + uint64_t data; + + data = ((uint64_t)1 << (size * 8)) - 1; + if (mrp) { + 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, mrpio->mr.addr + addr); + if (addr + 1 < mrp->offset + mrp->len) { + data |= mrp->read(mrpio->portio_opaque, mrpio->mr.addr + addr + 1) << 8; + } else { + data |= 0xff00; + } + } + } + return data; +} + +static void portio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + MemoryRegionPortioList *mrpio = opaque; + const MemoryRegionPortio *mrp = find_portio(mrpio, addr, size, true); + + if (mrp) { + 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, mrpio->mr.addr + addr, data & 0xff); + if (addr + 1 < mrp->offset + mrp->len) { + mrp->write(mrpio->portio_opaque, mrpio->mr.addr + addr + 1, data >> 8); + } + } + } +} + +static const MemoryRegionOps portio_ops = { + .read = portio_read, + .write = portio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.unaligned = true, + .impl.unaligned = true, +}; + +static void portio_list_add_1(PortioList *piolist, + const MemoryRegionPortio *pio_init, + unsigned count, unsigned start, + unsigned off_low, unsigned off_high) +{ + MemoryRegionPortioList *mrpio; + Object *owner; + char *name; + unsigned i; + + /* Copy the sub-list and null-terminate it. */ + 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; + } + + /* + * 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); + } + memory_region_add_subregion(piolist->address_space, + start + off_low, &mrpio->mr); + piolist->regions[piolist->nr] = &mrpio->mr; + ++piolist->nr; +} + +void portio_list_add(PortioList *piolist, + MemoryRegion *address_space, + uint32_t start) +{ + const MemoryRegionPortio *pio, *pio_start = piolist->ports; + 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; + off_high = off_low + pio_start->len + pio_start->size - 1; + count = 1; + + for (pio = pio_start + 1; pio->size != 0; pio++, count++) { + /* All entries must be sorted by offset. */ + assert(pio->offset >= off_last); + off_last = pio->offset; + + /* If we see a hole, break the region. */ + if (off_last > off_high) { + portio_list_add_1(piolist, pio_start, count, start, off_low, + off_high); + /* ... and start collecting anew. */ + pio_start = pio; + off_low = off_last; + off_high = off_low + pio->len + pio_start->size - 1; + count = 0; + } else if (off_last + pio->len > off_high) { + off_high = off_last + pio->len + pio_start->size - 1; + } + } + + /* There will always be an open sub-list. */ + portio_list_add_1(piolist, pio_start, count, start, off_low, off_high); +} + +void portio_list_del(PortioList *piolist) +{ + MemoryRegionPortioList *mrpio; + unsigned i; + + for (i = 0; i < piolist->nr; ++i) { + mrpio = container_of(piolist->regions[i], MemoryRegionPortioList, mr); + 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/system/main.c b/system/main.c new file mode 100644 index 0000000000..6e984770fe --- /dev/null +++ b/system/main.c @@ -0,0 +1,53 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2020 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-main.h" +#include "sysemu/sysemu.h" + +#ifndef XBOX +#ifdef CONFIG_SDL +#include +#endif +#endif + +int qemu_default_main(void) +{ + int status; + + status = qemu_main_loop(); + qemu_cleanup(status); + + return status; +} + +int (*qemu_main)(void) = qemu_default_main; + +#ifndef XBOX +int main(int argc, char **argv) +{ + qemu_init(argc, argv); + return qemu_main(); +} +#endif \ No newline at end of file diff --git a/softmmu/memory.c b/system/memory.c similarity index 90% rename from softmmu/memory.c rename to system/memory.c index 7eefde9914..2066c01f5e 100644 --- a/softmmu/memory.c +++ b/system/memory.c @@ -224,6 +224,7 @@ struct FlatRange { bool romd_mode; bool readonly; bool nonvolatile; + bool unmergeable; }; #define FOR_EACH_FLAT_RANGE(var, view) \ @@ -240,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, }; } @@ -250,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) @@ -323,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 */ @@ -534,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; @@ -542,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); @@ -556,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; } @@ -582,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; @@ -599,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); @@ -612,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) { @@ -635,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) { @@ -736,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); @@ -825,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 @@ -913,6 +941,38 @@ static void flat_range_coalesced_io_add(FlatRange *fr, AddressSpace *as) } } +static void +flat_range_coalesced_io_notify_listener_add_del(FlatRange *fr, + MemoryRegionSection *mrs, + MemoryListener *listener, + AddressSpace *as, bool add) +{ + CoalescedMemoryRange *cmr; + MemoryRegion *mr = fr->mr; + AddrRange tmp; + + QTAILQ_FOREACH(cmr, &mr->coalesced, link) { + tmp = addrrange_shift(cmr->addr, + int128_sub(fr->addr.start, + int128_make64(fr->offset_in_region))); + + if (!addrrange_intersects(tmp, fr->addr)) { + return; + } + tmp = addrrange_intersection(tmp, fr->addr); + + if (add && listener->coalesced_io_add) { + listener->coalesced_io_add(listener, mrs, + int128_get64(tmp.start), + int128_get64(tmp.size)); + } else if (!add && listener->coalesced_io_del) { + listener->coalesced_io_del(listener, mrs, + int128_get64(tmp.start), + int128_get64(tmp.size)); + } + } +} + static void address_space_update_topology_pass(AddressSpace *as, const FlatView *old_view, const FlatView *new_view, @@ -1091,7 +1151,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) { @@ -1170,6 +1230,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) { @@ -1281,7 +1342,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; } @@ -1290,7 +1351,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 } @@ -1310,22 +1371,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); @@ -1339,20 +1385,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 = { @@ -1506,7 +1539,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; } @@ -1540,16 +1578,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, @@ -1566,10 +1605,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, @@ -1590,37 +1631,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, @@ -1632,15 +1677,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 @@ -1657,7 +1705,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, @@ -1676,7 +1724,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, @@ -1691,17 +1739,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, @@ -1722,7 +1775,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, @@ -1837,6 +1892,11 @@ bool memory_region_is_protected(MemoryRegion *mr) return mr->ram && (mr->ram_block->flags & RAM_PROTECTED); } +bool memory_region_has_guest_memfd(MemoryRegion *mr) +{ + return mr->ram_block && mr->ram_block->guest_memfd >= 0; +} + uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) { uint8_t mask = mr->dirty_log_mask; @@ -1883,19 +1943,6 @@ static int memory_region_update_iommu_notify_flags(IOMMUMemoryRegion *iommu_mr, return ret; } -int memory_region_iommu_set_page_size_mask(IOMMUMemoryRegion *iommu_mr, - uint64_t page_size_mask, - Error **errp) -{ - IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); - int ret = 0; - - if (imrc->iommu_set_page_size_mask) { - ret = imrc->iommu_set_page_size_mask(iommu_mr, page_size_mask, errp); - } - return ret; -} - int memory_region_register_iommu_notifier(MemoryRegion *mr, IOMMUNotifier *n, Error **errp) { @@ -1975,9 +2022,9 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr, } void memory_region_notify_iommu_one(IOMMUNotifier *notifier, - IOMMUTLBEvent *event) + const IOMMUTLBEvent *event) { - IOMMUTLBEntry *entry = &event->entry; + const IOMMUTLBEntry *entry = &event->entry; hwaddr entry_end = entry->iova + entry->addr_mask; IOMMUTLBEntry tmp = *entry; @@ -2006,9 +2053,22 @@ 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) + const IOMMUTLBEvent event) { IOMMUNotifier *iommu_notifier; @@ -2059,7 +2119,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; @@ -2068,7 +2128,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; } @@ -2135,7 +2195,7 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, /* 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) + bool *mr_has_discard_manager, Error **errp) { MemoryRegion *mr; hwaddr xlat; @@ -2153,7 +2213,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, 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); + error_setg(errp, "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); @@ -2172,8 +2232,8 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, * 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 "", + error_setg(errp, "iommu map to discarded memory (e.g., unplugged" + " via virtio-mem): %" HWADDR_PRIx "", iotlb->translated_addr); return false; } @@ -2184,7 +2244,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, * check that it did not truncate too much. */ if (len & iotlb->addr_mask) { - error_report("iommu has granularity incompatible with target AS"); + error_setg(errp, "iommu has granularity incompatible with target AS"); return false; } @@ -2263,7 +2323,7 @@ void memory_region_set_client_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; @@ -2293,7 +2353,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); } } @@ -2308,7 +2368,7 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, size, client); } assert(mr->terminates); - memory_region_sync_dirty_bitmap(mr); + memory_region_sync_dirty_bitmap(mr, false); return cpu_physical_memory_test_and_clear_dirty( memory_region_get_ram_addr(mr) + addr, size, client); } @@ -2375,7 +2435,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; @@ -2437,35 +2497,24 @@ 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; - if (mr->alias) { - return memory_region_get_ram_ptr(mr->alias) + mr->alias_offset; - } - RCU_READ_LOCK_GUARD(); while (mr->alias) { offset += mr->alias_offset; 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) @@ -2583,8 +2632,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, @@ -2601,13 +2648,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); } @@ -2795,6 +2835,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; @@ -2920,9 +2972,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) @@ -2938,7 +2990,30 @@ static unsigned int postponed_stop_flags; static VMChangeStateEntry *vmstate_change; static void memory_global_dirty_log_stop_postponed_run(void); -void memory_global_dirty_log_start(unsigned int flags) +static bool memory_global_dirty_log_do_start(Error **errp) +{ + MemoryListener *listener; + + QTAILQ_FOREACH(listener, &memory_listeners, link) { + if (listener->log_global_start) { + if (!listener->log_global_start(listener, errp)) { + goto err; + } + } + } + return true; + +err: + while ((listener = QTAILQ_PREV(listener, link)) != NULL) { + if (listener->log_global_stop) { + listener->log_global_stop(listener); + } + } + + return false; +} + +bool memory_global_dirty_log_start(unsigned int flags, Error **errp) { unsigned int old_flags; @@ -2952,7 +3027,7 @@ void memory_global_dirty_log_start(unsigned int flags) flags &= ~global_dirty_tracking; if (!flags) { - return; + return true; } old_flags = global_dirty_tracking; @@ -2960,11 +3035,17 @@ void memory_global_dirty_log_start(unsigned int flags) trace_global_dirty_changed(global_dirty_tracking); if (!old_flags) { - MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward); + if (!memory_global_dirty_log_do_start(errp)) { + global_dirty_tracking &= ~flags; + trace_global_dirty_changed(global_dirty_tracking); + return false; + } + memory_region_transaction_begin(); memory_region_update_pending = true; memory_region_transaction_commit(); } + return true; } static void memory_global_dirty_log_do_stop(unsigned int flags) @@ -3031,15 +3112,24 @@ void memory_global_dirty_log_stop(unsigned int flags) static void listener_add_address_space(MemoryListener *listener, AddressSpace *as) { + unsigned i; FlatView *view; FlatRange *fr; + MemoryRegionIoeventfd *fd; if (listener->begin) { listener->begin(listener); } if (global_dirty_tracking) { + /* + * Currently only VFIO can fail log_global_start(), and it's not + * yet allowed to hotplug any PCI device during migration. So this + * should never fail when invoked, guard it with error_abort. If + * it can start to fail in the future, we need to be able to fail + * the whole listener_add_address_space() and its callers. + */ if (listener->log_global_start) { - listener->log_global_start(listener); + listener->log_global_start(listener, &error_abort); } } @@ -3050,10 +3140,34 @@ static void listener_add_address_space(MemoryListener *listener, if (listener->region_add) { listener->region_add(listener, §ion); } + + /* send coalesced io add notifications */ + flat_range_coalesced_io_notify_listener_add_del(fr, §ion, + listener, as, true); + if (fr->dirty_log_mask && listener->log_start) { listener->log_start(listener, §ion, 0, fr->dirty_log_mask); } } + + /* + * register all eventfds for this address space for the newly registered + * listener. + */ + for (i = 0; i < as->ioeventfd_nb; i++) { + fd = &as->ioeventfds[i]; + MemoryRegionSection section = (MemoryRegionSection) { + .fv = view, + .offset_within_address_space = int128_get64(fd->addr.start), + .size = fd->addr.size, + }; + + if (listener->eventfd_add) { + listener->eventfd_add(listener, §ion, + fd->match_data, fd->data, fd->e); + } + } + if (listener->commit) { listener->commit(listener); } @@ -3063,8 +3177,10 @@ static void listener_add_address_space(MemoryListener *listener, static void listener_del_address_space(MemoryListener *listener, AddressSpace *as) { + unsigned i; FlatView *view; FlatRange *fr; + MemoryRegionIoeventfd *fd; if (listener->begin) { listener->begin(listener); @@ -3076,10 +3192,33 @@ static void listener_del_address_space(MemoryListener *listener, if (fr->dirty_log_mask && listener->log_stop) { listener->log_stop(listener, §ion, fr->dirty_log_mask, 0); } + + /* send coalesced io del notifications */ + flat_range_coalesced_io_notify_listener_add_del(fr, §ion, + listener, as, false); if (listener->region_del) { listener->region_del(listener, §ion); } } + + /* + * de-register all eventfds for this address space for the current + * listener. + */ + for (i = 0; i < as->ioeventfd_nb; i++) { + fd = &as->ioeventfds[i]; + MemoryRegionSection section = (MemoryRegionSection) { + .fv = view, + .offset_within_address_space = int128_get64(fd->addr.start), + .size = fd->addr.size, + }; + + if (listener->eventfd_del) { + listener->eventfd_del(listener, §ion, + fd->match_data, fd->data, fd->e); + } + } + if (listener->commit) { listener->commit(listener); } @@ -3119,6 +3258,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) @@ -3127,6 +3270,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); @@ -3149,6 +3296,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) as->ioeventfds = NULL; QTAILQ_INIT(&as->listeners); QTAILQ_INSERT_TAIL(&address_spaces, as, address_spaces_link); + as->max_bounce_buffer_size = DEFAULT_MAX_BOUNCE_BUFFER_SIZE; + as->bounce_buffer_size = 0; + qemu_mutex_init(&as->map_client_list_lock); + QLIST_INIT(&as->map_client_list); as->name = g_strdup(name ? name : "anonymous"); address_space_update_topology(as); address_space_update_ioeventfds(as); @@ -3156,6 +3307,10 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) static void do_address_space_destroy(AddressSpace *as) { + assert(qatomic_read(&as->bounce_buffer_size) == 0); + assert(QLIST_EMPTY(&as->map_client_list)); + qemu_mutex_destroy(&as->map_client_list_lock); + assert(QTAILQ_EMPTY(&as->listeners)); flatview_unref(as->current_map); @@ -3277,7 +3432,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 */ @@ -3296,9 +3450,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-" : "", @@ -3318,7 +3472,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, @@ -3405,8 +3559,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), @@ -3416,7 +3570,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) @@ -3595,19 +3749,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 @@ -3617,21 +3768,21 @@ 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, - Object *owner, - const char *name, - uint64_t size, - Error **errp) +bool memory_region_init_ram_guest_memfd(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_ram_flags_nomigrate(mr, owner, name, size, + RAM_GUEST_MEMFD, 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 @@ -3641,9 +3792,34 @@ 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(MemoryRegion *mr, + Object *owner, + const char *name, + uint64_t size, + Error **errp) +{ + DeviceState *owner_dev; + + 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 + * unique name for migration. TODO: Ideally we should implement + * a naming scheme for Objects which are not DeviceStates, in + * which case we can relax this restriction. + */ + owner_dev = DEVICE(owner); + vmstate_register_ram(mr, owner_dev); + + return true; +} + +bool memory_region_init_rom_device(MemoryRegion *mr, Object *owner, const MemoryRegionOps *ops, void *opaque, @@ -3652,13 +3828,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 @@ -3668,10 +3841,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 94% rename from softmmu/memory_mapping.c rename to system/memory_mapping.c index f6f0a829fd..ca2390eb80 100644 --- a/softmmu/memory_mapping.c +++ b/system/memory_mapping.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include "qapi/error.h" #include "sysemu/memory_mapping.h" @@ -241,8 +242,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 +292,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 +305,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 +334,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, @@ -354,8 +354,7 @@ void memory_mapping_filter(MemoryMappingList *list, int64_t begin, MemoryMapping *cur, *next; QTAILQ_FOREACH_SAFE(cur, &list->head, next, next) { - if (cur->phys_addr >= begin + length || - cur->phys_addr + cur->length <= begin) { + if (!ranges_overlap(cur->phys_addr, cur->length, begin, length)) { QTAILQ_REMOVE(&list->head, cur, next); g_free(cur); list->num--; diff --git a/system/meson.build b/system/meson.build new file mode 100644 index 0000000000..4952f4b2c7 --- /dev/null +++ b/system/meson.build @@ -0,0 +1,39 @@ +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-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: 'CONFIG_DEVICE_TREE', + if_true: [fdt, files('device_tree.c')], + if_false: files('device_tree-stub.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 81% rename from softmmu/physmem.c rename to system/physmem.c index ca2a20d43b..c6543c388c 100644 --- a/softmmu/physmem.c +++ b/system/physmem.c @@ -23,18 +23,21 @@ #include "qemu/cutils.h" #include "qemu/cacheflush.h" +#include "qemu/hbitmap.h" #include "qemu/madvise.h" +#include "qemu/lockable.h" #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" #endif /* CONFIG_TCG */ #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/target_page.h" #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" @@ -50,7 +53,7 @@ #include "sysemu/hostmem.h" #include "sysemu/hw_accel.h" #include "sysemu/xen-mapcache.h" -#include "trace/trace-root.h" +#include "trace.h" #ifdef CONFIG_FALLOCATE_PUNCH_HOLE #include @@ -157,12 +160,12 @@ static void tcg_commit(MemoryListener *listener); * @memory_dispatch: its dispatch pointer (cached, RCU protected) * @tcg_as_listener: listener for tracking changes to the AddressSpace */ -struct CPUAddressSpace { +typedef struct CPUAddressSpace { CPUState *cpu; AddressSpace *as; struct AddressSpaceDispatch *memory_dispatch; MemoryListener tcg_as_listener; -}; +} CPUAddressSpace; struct DirtyBitmapSnapshot { ram_addr_t start; @@ -679,8 +682,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, IOMMUTLBEntry iotlb; int iommu_idx; hwaddr addr = orig_addr; - AddressSpaceDispatch *d = - qatomic_rcu_read(&cpu->cpu_ases[asidx].memory_dispatch); + AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch; for (;;) { section = address_space_translate_internal(d, addr, &addr, plen, false); @@ -761,6 +763,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx, if (!cpu->cpu_ases) { cpu->cpu_ases = g_new0(CPUAddressSpace, cpu->num_ases); + cpu->cpu_ases_count = cpu->num_ases; } newas = &cpu->cpu_ases[asidx]; @@ -774,88 +777,40 @@ void cpu_address_space_init(CPUState *cpu, int asidx, } } +void cpu_address_space_destroy(CPUState *cpu, int asidx) +{ + CPUAddressSpace *cpuas; + + assert(cpu->cpu_ases); + assert(asidx >= 0 && asidx < cpu->num_ases); + /* KVM cannot currently support multiple address spaces. */ + assert(asidx == 0 || !kvm_enabled()); + + cpuas = &cpu->cpu_ases[asidx]; + if (tcg_enabled()) { + memory_listener_unregister(&cpuas->tcg_as_listener); + } + + address_space_destroy(cpuas->as); + g_free_rcu(cpuas->as, rcu); + + if (asidx == 0) { + /* reset the convenience alias for address space 0 */ + cpu->as = NULL; + } + + if (--cpu->cpu_ases_count == 0) { + g_free(cpu->cpu_ases); + cpu->cpu_ases = NULL; + } +} + AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx) { /* Return the AddressSpace corresponding to the specified index */ 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 XBOX static inline bool access_callback_address_matches(MemAccessCallback *cb, @@ -899,7 +854,7 @@ int mem_access_callback_insert(CPUState *cpu, MemoryRegion *mr, hwaddr offset, } // FIXME: flush only applicable pages - tlb_flush(cpu); + tlb_flush_all_cpus_synced(cpu); return 0; } @@ -910,7 +865,7 @@ void mem_access_callback_remove_by_ref(CPUState *cpu, MemAccessCallback *cb) g_free(cb); // FIXME: flush only applicable pages - tlb_flush(cpu); + tlb_flush_all_cpus_synced(cpu); } void mem_check_access_callback_vaddr(CPUState *cpu, @@ -940,121 +895,6 @@ void mem_check_access_callback_ramaddr(CPUState *cpu, #endif // ifdef XBOX -#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) { @@ -1074,7 +914,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 @@ -1094,7 +934,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; @@ -1156,8 +996,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; @@ -1167,13 +1007,19 @@ DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty (MemoryRegion *mr, hwaddr offset, hwaddr length, unsigned client) { DirtyMemoryBlocks *blocks; - ram_addr_t start = memory_region_get_ram_addr(mr) + offset; + ram_addr_t start, first, last; unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL); - ram_addr_t first = QEMU_ALIGN_DOWN(start, align); - ram_addr_t last = QEMU_ALIGN_UP(start + length, align); DirtyBitmapSnapshot *snap; unsigned long page, end, dest; + start = memory_region_get_ram_addr(mr); + /* We know we're only called for RAM MemoryRegions */ + assert(start != RAM_ADDR_INVALID); + start += offset; + + first = QEMU_ALIGN_DOWN(start, align); + last = QEMU_ALIGN_UP(start + length, align); + snap = g_malloc0(sizeof(*snap) + ((last - first) >> (TARGET_PAGE_BITS + 3))); snap->start = first; @@ -1188,25 +1034,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); @@ -1400,15 +1244,21 @@ 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); } @@ -1557,8 +1407,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; @@ -1569,10 +1418,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) { @@ -1602,10 +1474,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 @@ -1619,7 +1488,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) @@ -1637,6 +1505,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__) @@ -1668,11 +1541,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; @@ -1684,6 +1557,7 @@ static void *file_ram_alloc(RAMBlock *block, } block->fd = fd; + block->fd_offset = offset; return area; } #endif @@ -1744,18 +1618,6 @@ static ram_addr_t find_ram_offset(ram_addr_t size) return offset; } -static unsigned long last_ram_page(void) -{ - RAMBlock *block; - ram_addr_t last = 0; - - RCU_READ_LOCK_GUARD(); - RAMBLOCK_FOREACH(block) { - last = MAX(last, block->offset + block->max_length); - } - return last >> TARGET_PAGE_BITS; -} - static void qemu_ram_setup_dump(void *addr, ram_addr_t size) { int ret; @@ -1766,7 +1628,7 @@ static void qemu_ram_setup_dump(void *addr, ram_addr_t size) if (ret) { perror("qemu_madvise"); fprintf(stderr, "madvise doesn't support MADV_DONTDUMP, " - "but dump_guest_core=off specified\n"); + "but dump-guest-core=off specified\n"); } } } @@ -1832,12 +1694,17 @@ void qemu_ram_unset_migratable(RAMBlock *rb) rb->flags &= ~RAM_MIGRATABLE; } +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 iothread lock held. */ +/* Called with the BQL held. */ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) { RAMBlock *block; @@ -1865,7 +1732,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 @@ -1920,7 +1787,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) { /* @@ -2003,13 +1871,11 @@ void qemu_ram_msync(RAMBlock *block, ram_addr_t start, ram_addr_t length) } /* Called with ram_list.mutex held */ -static void dirty_memory_extend(ram_addr_t old_ram_size, - ram_addr_t new_ram_size) +static void dirty_memory_extend(ram_addr_t new_ram_size) { - ram_addr_t old_num_blocks = DIV_ROUND_UP(old_ram_size, - DIRTY_MEMORY_BLOCK_SIZE); - ram_addr_t new_num_blocks = DIV_ROUND_UP(new_ram_size, - DIRTY_MEMORY_BLOCK_SIZE); + unsigned int old_num_blocks = ram_list.num_dirty_blocks; + unsigned int new_num_blocks = DIV_ROUND_UP(new_ram_size, + DIRTY_MEMORY_BLOCK_SIZE); int i; /* Only need to extend if block count increased */ @@ -2041,6 +1907,8 @@ static void dirty_memory_extend(ram_addr_t old_ram_size, g_free_rcu(old_blocks, rcu); } } + + ram_list.num_dirty_blocks = new_num_blocks; } static void ram_block_add(RAMBlock *new_block, Error **errp) @@ -2049,11 +1917,10 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) const bool shared = qemu_ram_is_shared(new_block); RAMBlock *block; RAMBlock *last_block = NULL; - ram_addr_t old_ram_size, new_ram_size; + bool free_on_error = false; + ram_addr_t ram_size; Error *err = NULL; - old_ram_size = last_ram_page(); - qemu_mutex_lock_ramlist(); new_block->offset = find_ram_offset(new_block->max_length); @@ -2078,14 +1945,34 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) return; } memory_try_enable_merging(new_block->host, new_block->max_length); + free_on_error = true; } } - new_ram_size = MAX(old_ram_size, - (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS); - if (new_ram_size > old_ram_size) { - dirty_memory_extend(old_ram_size, new_ram_size); + if (new_block->flags & RAM_GUEST_MEMFD) { + int ret; + + assert(kvm_enabled()); + assert(new_block->guest_memfd < 0); + + ret = ram_block_discard_require(true); + if (ret < 0) { + error_setg_errno(errp, -ret, + "cannot set up private guest memory: discard currently blocked"); + error_append_hint(errp, "Are you using assigned devices?\n"); + goto out_free; + } + + new_block->guest_memfd = kvm_create_guest_memfd(new_block->max_length, + 0, errp); + if (new_block->guest_memfd < 0) { + qemu_mutex_unlock_ramlist(); + goto out_free; + } } + + ram_size = (new_block->offset + new_block->max_length) >> TARGET_PAGE_BITS; + dirty_memory_extend(ram_size); /* Keep the list sorted from biggest to smallest block. Unlike QTAILQ, * QLIST (which has an RCU-friendly variant) does not have insertion at * tail, so save the last element in last_block. @@ -2129,12 +2016,19 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) ram_block_notify_add(new_block->host, new_block->used_length, new_block->max_length); } + return; + +out_free: + if (free_on_error) { + qemu_anon_ram_free(new_block->host, new_block->max_length); + new_block->host = NULL; + } } #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; @@ -2142,7 +2036,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 | RAM_GUEST_MEMFD)) == 0); if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); @@ -2155,9 +2050,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); @@ -2177,8 +2074,9 @@ 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->guest_memfd = -1; + new_block->host = file_ram_alloc(new_block, size, fd, !file_size, offset, + errp); if (!new_block->host) { g_free(new_block); return NULL; @@ -2197,19 +2095,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); @@ -2232,13 +2151,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); + RAM_NORESERVE | RAM_GUEST_MEMFD)) == 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; @@ -2246,6 +2169,7 @@ RAMBlock *qemu_ram_alloc_internal(ram_addr_t size, ram_addr_t max_size, new_block->max_length = max_size; assert(max_size >= size); new_block->fd = -1; + new_block->guest_memfd = -1; new_block->page_size = qemu_real_host_page_size(); new_block->host = host; new_block->flags = ram_flags; @@ -2268,7 +2192,7 @@ RAMBlock *qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, RAMBlock *qemu_ram_alloc(ram_addr_t size, uint32_t ram_flags, MemoryRegion *mr, Error **errp) { - assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE)) == 0); + assert((ram_flags & ~(RAM_SHARED | RAM_NORESERVE | RAM_GUEST_MEMFD)) == 0); return qemu_ram_alloc_internal(size, size, NULL, NULL, ram_flags, mr, errp); } @@ -2296,6 +2220,12 @@ static void reclaim_ramblock(RAMBlock *block) } else { qemu_anon_ram_free(block->host, block->max_length); } + + if (block->guest_memfd >= 0) { + close(block->guest_memfd); + ram_block_discard_require(false); + } + g_free(block); } @@ -2327,6 +2257,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; @@ -2341,13 +2272,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: " @@ -2363,7 +2295,62 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) } #endif /* !_WIN32 */ -/* Return a host pointer to ram allocated with qemu_ram_alloc. +/* + * Return a host pointer to guest's ram. + * For Xen, foreign mappings get created if they don't already exist. + * + * @block: block for the RAM to lookup (optional and may be NULL). + * @addr: address within the memory region. + * @size: pointer to requested size (optional and may be NULL). + * size may get modified and return a value smaller than + * what was requested. + * @lock: wether to lock the mapping in xen-mapcache until invalidated. + * @is_write: hint wether to map RW or RO in the xen-mapcache. + * (optional and may always be set to true). + * + * Called within RCU critical section. + */ +static void *qemu_ram_ptr_length(RAMBlock *block, ram_addr_t addr, + hwaddr *size, bool lock, + bool is_write) +{ + hwaddr len = 0; + + if (size && *size == 0) { + return NULL; + } + + if (block == NULL) { + block = qemu_get_ram_block(addr); + addr -= block->offset; + } + if (size) { + *size = MIN(*size, block->max_length - addr); + len = *size; + } + + if (xen_enabled() && block->host == NULL) { + /* We need to check if the requested address is in the RAM + * because we don't want to map the entire memory in QEMU. + * In that case just map the requested area. + */ + if (xen_mr_is_memory(block->mr)) { + return xen_map_cache(block->mr, block->offset + addr, + len, block->offset, + lock, lock, is_write); + } + + block->host = xen_map_cache(block->mr, block->offset, + block->max_length, + block->offset, + 1, lock, is_write); + } + + return ramblock_ptr(block, addr); +} + +/* + * Return a host pointer to ram allocated with qemu_ram_alloc. * This should not be used for general purpose DMA. Use address_space_map * or address_space_rw instead. For local memory (e.g. video ram) that the * device owns, use memory_region_get_ram_ptr. @@ -2372,59 +2359,7 @@ void qemu_ram_remap(ram_addr_t addr, ram_addr_t length) */ void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr) { - RAMBlock *block = ram_block; - - if (block == NULL) { - block = qemu_get_ram_block(addr); - addr -= block->offset; - } - - if (xen_enabled() && block->host == NULL) { - /* We need to check if the requested address is in the RAM - * because we don't want to map the entire memory in QEMU. - * In that case just map until the end of the page. - */ - if (block->offset == 0) { - return xen_map_cache(addr, 0, 0, false); - } - - block->host = xen_map_cache(block->offset, block->max_length, 1, false); - } - return ramblock_ptr(block, addr); -} - -/* Return a host pointer to guest's ram. Similar to qemu_map_ram_ptr - * but takes a size argument. - * - * Called within RCU critical section. - */ -static void *qemu_ram_ptr_length(RAMBlock *ram_block, ram_addr_t addr, - hwaddr *size, bool lock) -{ - RAMBlock *block = ram_block; - if (*size == 0) { - return NULL; - } - - if (block == NULL) { - block = qemu_get_ram_block(addr); - addr -= block->offset; - } - *size = MIN(*size, block->max_length - addr); - - if (xen_enabled() && block->host == NULL) { - /* We need to check if the requested address is in the RAM - * because we don't want to map the entire memory in QEMU. - * In that case just map the requested area. - */ - if (block->offset == 0) { - return xen_map_cache(addr, *size, lock, lock); - } - - block->host = xen_map_cache(block->offset, block->max_length, 1, lock); - } - - return ramblock_ptr(block, addr); + return qemu_ram_ptr_length(ram_block, addr, NULL, false, true); } /* Return the offset of a hostpointer within a ramblock */ @@ -2437,23 +2372,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) { @@ -2464,6 +2382,10 @@ RAMBlock *qemu_ram_block_from_host(void *ptr, bool round_offset, ram_addr_t ram_addr; RCU_READ_LOCK_GUARD(); ram_addr = xen_ram_addr_from_mapcache(ptr); + if (ram_addr == RAM_ADDR_INVALID) { + return NULL; + } + block = qemu_get_ram_block(ram_addr); if (block) { *offset = ram_addr - block->offset; @@ -2517,8 +2439,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; @@ -2559,7 +2483,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); @@ -2577,7 +2501,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 @@ -2591,7 +2515,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 @@ -2642,7 +2566,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 @@ -2668,10 +2592,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) @@ -2737,23 +2667,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) @@ -2783,7 +2732,11 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr length) { uint8_t dirty_log_mask = memory_region_get_dirty_log_mask(mr); - addr += memory_region_get_ram_addr(mr); + ram_addr_t ramaddr = memory_region_get_ram_addr(mr); + + /* We know we're only called for RAM MemoryRegions */ + assert(ramaddr != RAM_ADDR_INVALID); + addr += ramaddr; /* No early return if dirty_log_mask is or becomes 0, because * cpu_physical_memory_set_dirty_range will still call @@ -2795,7 +2748,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); @@ -2845,8 +2798,8 @@ 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) { @@ -2883,48 +2836,75 @@ 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, true); + + 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; #ifdef XBOX CPUState *cpu = qemu_get_cpu(0); - ram_addr_t ram_addr = addr1 + memory_region_get_ram_addr(mr); + ram_addr_t ram_addr = mr_addr + memory_region_get_ram_addr(mr); mem_check_access_callback_ramaddr(cpu, ram_addr, len, BP_MEM_WRITE); #endif 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; @@ -2935,7 +2915,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; @@ -2946,58 +2926,82 @@ 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, 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; #ifdef XBOX CPUState *cpu = qemu_get_cpu(0); - ram_addr_t ram_addr = addr1 + memory_region_get_ram_addr(mr); + ram_addr_t ram_addr = mr_addr + memory_region_get_ram_addr(mr); mem_check_access_callback_ramaddr(cpu, ram_addr, len, BP_MEM_READ); #endif 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; @@ -3008,7 +3012,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; @@ -3019,16 +3023,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, @@ -3170,53 +3174,50 @@ void cpu_flush_icache_range(hwaddr start, hwaddr len) NULL, len, FLUSH_CACHE); } +/* + * A magic value stored in the first 8 bytes of the bounce buffer struct. Used + * to detect illegal pointers passed to address_space_unmap. + */ +#define BOUNCE_BUFFER_MAGIC 0xb4017ceb4ffe12ed + typedef struct { + uint64_t magic; MemoryRegion *mr; - void *buffer; hwaddr addr; - hwaddr len; - bool in_use; + size_t len; + uint8_t buffer[]; } BounceBuffer; -static BounceBuffer bounce; - -typedef struct MapClient { - QEMUBH *bh; - QLIST_ENTRY(MapClient) link; -} MapClient; - -QemuMutex map_client_list_lock; -static QLIST_HEAD(, MapClient) map_client_list - = QLIST_HEAD_INITIALIZER(map_client_list); - -static void cpu_unregister_map_client_do(MapClient *client) +static void +address_space_unregister_map_client_do(AddressSpaceMapClient *client) { QLIST_REMOVE(client, link); g_free(client); } -static void cpu_notify_map_clients_locked(void) +static void address_space_notify_map_clients_locked(AddressSpace *as) { - MapClient *client; + AddressSpaceMapClient *client; - while (!QLIST_EMPTY(&map_client_list)) { - client = QLIST_FIRST(&map_client_list); + while (!QLIST_EMPTY(&as->map_client_list)) { + client = QLIST_FIRST(&as->map_client_list); qemu_bh_schedule(client->bh); - cpu_unregister_map_client_do(client); + address_space_unregister_map_client_do(client); } } -void cpu_register_map_client(QEMUBH *bh) +void address_space_register_map_client(AddressSpace *as, QEMUBH *bh) { - MapClient *client = g_malloc(sizeof(*client)); + AddressSpaceMapClient *client = g_malloc(sizeof(*client)); - qemu_mutex_lock(&map_client_list_lock); + QEMU_LOCK_GUARD(&as->map_client_list_lock); client->bh = bh; - QLIST_INSERT_HEAD(&map_client_list, client, link); - if (!qatomic_read(&bounce.in_use)) { - cpu_notify_map_clients_locked(); + QLIST_INSERT_HEAD(&as->map_client_list, client, link); + /* Write map_client_list before reading bounce_buffer_size. */ + smp_mb(); + if (qatomic_read(&as->bounce_buffer_size) < as->max_bounce_buffer_size) { + address_space_notify_map_clients_locked(as); } - qemu_mutex_unlock(&map_client_list_lock); } void cpu_exec_init_all(void) @@ -3232,28 +3233,25 @@ void cpu_exec_init_all(void) finalize_target_page_bits(); io_mem_init(); memory_map_init(); - qemu_mutex_init(&map_client_list_lock); } -void cpu_unregister_map_client(QEMUBH *bh) +void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh) { - MapClient *client; + AddressSpaceMapClient *client; - qemu_mutex_lock(&map_client_list_lock); - QLIST_FOREACH(client, &map_client_list, link) { + QEMU_LOCK_GUARD(&as->map_client_list_lock); + QLIST_FOREACH(client, &as->map_client_list, link) { if (client->bh == bh) { - cpu_unregister_map_client_do(client); + address_space_unregister_map_client_do(client); break; } } - qemu_mutex_unlock(&map_client_list_lock); } -static void cpu_notify_map_clients(void) +static void address_space_notify_map_clients(AddressSpace *as) { - qemu_mutex_lock(&map_client_list_lock); - cpu_notify_map_clients_locked(); - qemu_mutex_unlock(&map_client_list_lock); + QEMU_LOCK_GUARD(&as->map_client_list_lock); + address_space_notify_map_clients_locked(as); } static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, @@ -3320,8 +3318,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, * May map a subset of the requested range, given by and returned in *plen. * May return NULL if resources needed to perform the mapping are exhausted. * Use only for reads OR writes - not for read-modify-write operations. - * Use cpu_register_map_client() to know when retrying the map operation is - * likely to succeed. + * Use address_space_register_map_client() to know when retrying the map + * operation is likely to succeed. */ void *address_space_map(AddressSpace *as, hwaddr addr, @@ -3332,9 +3330,10 @@ void *address_space_map(AddressSpace *as, hwaddr len = *plen; hwaddr l, xlat; MemoryRegion *mr; - void *ptr; FlatView *fv; + trace_address_space_map(as, addr, len, is_write, *(uint32_t *) &attrs); + if (len == 0) { return NULL; } @@ -3345,35 +3344,45 @@ void *address_space_map(AddressSpace *as, mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { - if (qatomic_xchg(&bounce.in_use, true)) { + size_t used = qatomic_read(&as->bounce_buffer_size); + for (;;) { + hwaddr alloc = MIN(as->max_bounce_buffer_size - used, l); + size_t new_size = used + alloc; + size_t actual = + qatomic_cmpxchg(&as->bounce_buffer_size, used, new_size); + if (actual == used) { + l = alloc; + break; + } + used = actual; + } + + if (l == 0) { *plen = 0; return NULL; } - /* Avoid unbounded allocations */ - l = MIN(l, TARGET_PAGE_SIZE); - bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, l); - bounce.addr = addr; - bounce.len = l; + BounceBuffer *bounce = g_malloc0(l + sizeof(BounceBuffer)); + bounce->magic = BOUNCE_BUFFER_MAGIC; memory_region_ref(mr); - bounce.mr = mr; + bounce->mr = mr; + bounce->addr = addr; + bounce->len = l; + if (!is_write) { - flatview_read(fv, addr, MEMTXATTRS_UNSPECIFIED, - bounce.buffer, l); + flatview_read(fv, addr, attrs, + bounce->buffer, l); } *plen = l; - return bounce.buffer; + return bounce->buffer; } - memory_region_ref(mr); *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, is_write); } /* Unmaps a memory region previously mapped by address_space_map(). @@ -3383,12 +3392,11 @@ void *address_space_map(AddressSpace *as, void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, bool is_write, hwaddr access_len) { - if (buffer != bounce.buffer) { - MemoryRegion *mr; - ram_addr_t addr1; + MemoryRegion *mr; + ram_addr_t addr1; - mr = memory_region_from_host(buffer, &addr1); - assert(mr != NULL); + mr = memory_region_from_host(buffer, &addr1); + if (mr != NULL) { if (is_write) { invalidate_and_set_dirty(mr, addr1, access_len); } @@ -3398,15 +3406,23 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, memory_region_unref(mr); return; } + + + BounceBuffer *bounce = container_of(buffer, BounceBuffer, buffer); + assert(bounce->magic == BOUNCE_BUFFER_MAGIC); + if (is_write) { - address_space_write(as, bounce.addr, MEMTXATTRS_UNSPECIFIED, - bounce.buffer, access_len); + address_space_write(as, bounce->addr, MEMTXATTRS_UNSPECIFIED, + bounce->buffer, access_len); } - qemu_vfree(bounce.buffer); - bounce.buffer = NULL; - memory_region_unref(bounce.mr); - qatomic_mb_set(&bounce.in_use, false); - cpu_notify_map_clients(); + + qatomic_sub(&as->bounce_buffer_size, bounce->len); + bounce->magic = ~BOUNCE_BUFFER_MAGIC; + memory_region_unref(bounce->mr); + g_free(bounce); + /* Write bounce_buffer_size before reading map_client_list. */ + smp_mb(); + address_space_notify_map_clients(as); } void *cpu_physical_memory_map(hwaddr addr, @@ -3455,7 +3471,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; @@ -3468,7 +3484,8 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, l = flatview_extend_translation(cache->fv, addr, len, mr, cache->xlat, l, is_write, MEMTXATTRS_UNSPECIFIED); - cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); + cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true, + is_write); } else { cache->ptr = NULL; } @@ -3533,6 +3550,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. */ @@ -3540,15 +3610,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 @@ -3558,15 +3627,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 @@ -3618,38 +3686,17 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, return 0; } -/* - * Allows code that needs to deal with migration bitmaps etc to still be built - * target independent. - */ -size_t qemu_target_page_size(void) -{ - return TARGET_PAGE_SIZE; -} - -int qemu_target_page_bits(void) -{ - return TARGET_PAGE_BITS; -} - -int qemu_target_page_bits_min(void) -{ - return TARGET_PAGE_BITS_MIN; -} - 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) @@ -3682,16 +3729,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; } @@ -3702,7 +3748,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 @@ -3710,20 +3756,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 } @@ -3741,31 +3814,52 @@ 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: return ret; } +int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, + size_t length) +{ + int ret = -1; + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + ret = fallocate(rb->guest_memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + start, length); + + if (ret) { + ret = -errno; + error_report("%s: Failed to fallocate %s:%" PRIx64 " +%zx (%d)", + __func__, rb->idstr, start, length, ret); + } +#else + ret = -ENOSYS; + error_report("%s: fallocate not available %s:%" PRIx64 " +%zx (%d)", + __func__, rb->idstr, start, length, ret); +#endif + + return ret; +} + bool ramblock_is_pmem(RAMBlock *rb) { return rb->flags & RAM_PMEM; @@ -3804,7 +3898,7 @@ 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, diff --git a/softmmu/qdev-monitor.c b/system/qdev-monitor.c similarity index 81% rename from softmmu/qdev-monitor.c rename to system/qdev-monitor.c index 4b0ef65780..03ae610649 100644 --- a/softmmu/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -23,6 +23,7 @@ #include "monitor/monitor.h" #include "monitor/qdev.h" #include "sysemu/arch_init.h" +#include "sysemu/runstate.h" #include "qapi/error.h" #include "qapi/qapi-commands-qdev.h" #include "qapi/qmp/dispatch.h" @@ -38,7 +39,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" @@ -56,12 +56,18 @@ typedef struct QDevAlias } QDevAlias; /* default virtio transport per architecture */ -#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | QEMU_ARCH_ARM | \ - QEMU_ARCH_HPPA | QEMU_ARCH_I386 | \ - QEMU_ARCH_MIPS | QEMU_ARCH_PPC | \ - QEMU_ARCH_RISCV | QEMU_ARCH_SH4 | \ - QEMU_ARCH_SPARC | QEMU_ARCH_XTENSA | \ - QEMU_ARCH_LOONGARCH) +#define QEMU_ARCH_VIRTIO_PCI (QEMU_ARCH_ALPHA | \ + QEMU_ARCH_ARM | \ + QEMU_ARCH_HPPA | \ + QEMU_ARCH_I386 | \ + QEMU_ARCH_LOONGARCH | \ + QEMU_ARCH_MIPS | \ + QEMU_ARCH_OPENRISC | \ + QEMU_ARCH_PPC | \ + QEMU_ARCH_RISCV | \ + QEMU_ARCH_SH4 | \ + QEMU_ARCH_SPARC | \ + QEMU_ARCH_XTENSA) #define QEMU_ARCH_VIRTIO_CCW (QEMU_ARCH_S390X) #define QEMU_ARCH_VIRTIO_MMIO (QEMU_ARCH_M68K) @@ -86,6 +92,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 +117,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 }, @@ -656,7 +667,8 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, if (qdev_should_hide_device(opts, from_json, errp)) { if (bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); + error_setg(errp, "Bus '%s' does not support hotplugging", + bus->name); } return NULL; } else if (*errp) { @@ -664,11 +676,11 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, } if (phase_check(PHASE_MACHINE_READY) && bus && !qbus_is_hotpluggable(bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, bus->name); + error_setg(errp, "Bus '%s' does not support hotplugging", bus->name); return NULL; } - if (!migration_is_idle()) { + if (migration_is_running()) { error_setg(errp, "device_add not allowed while migrating"); return NULL; } @@ -711,7 +723,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,9 +751,8 @@ 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, +static void qdev_print_props(Monitor *mon, DeviceState *dev, const Property *props, int indent) { if (!props) @@ -779,13 +790,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 +816,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 +826,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) @@ -841,51 +856,45 @@ void hmp_info_qdm(Monitor *mon, const QDict *qdict) void qmp_device_add(QDict *qdict, QObject **ret_data, Error **errp) { - QemuOpts *opts; DeviceState *dev; - opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, errp); - if (!opts) { - return; - } - if (!monitor_cur_is_qmp() && qdev_device_help(opts)) { - qemu_opts_del(opts); - 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(); - + dev = qdev_device_add_from_qdict(qdict, true, errp); if (!dev) { - qemu_opts_del(opts); - return; + /* + * 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(); } object_unref(OBJECT(dev)); } -static DeviceState *find_device_state(const char *id, Error **errp) +/* + * Note that creating new APIs using error classes other than GenericError is + * not recommended. Set use_generic_error=true for new interfaces. + */ +static DeviceState *find_device_state(const char *id, bool use_generic_error, + Error **errp) { Object *obj = object_resolve_path_at(qdev_get_peripheral(), id); DeviceState *dev; if (!obj) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + error_set(errp, + (use_generic_error ? + ERROR_CLASS_GENERIC_ERROR : ERROR_CLASS_DEVICE_NOT_FOUND), "Device '%s' not found", id); return NULL; } 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; } @@ -904,17 +913,18 @@ void qdev_unplug(DeviceState *dev, Error **errp) } if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { - error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); + error_setg(errp, "Bus '%s' does not support hotplugging", + dev->parent_bus->name); return; } if (!dc->hotpluggable) { - error_setg(errp, QERR_DEVICE_NO_HOTPLUG, + error_setg(errp, "Device '%s' does not support hotplugging", object_get_typename(OBJECT(dev))); return; } - if (!migration_is_idle() && !dev->allow_unplug_during_migration) { + if (migration_is_running() && !dev->allow_unplug_during_migration) { error_setg(errp, "device_del not allowed while migrating"); return; } @@ -942,7 +952,7 @@ void qdev_unplug(DeviceState *dev, Error **errp) void qmp_device_del(const char *id, Error **errp) { - DeviceState *dev = find_device_state(id, errp); + DeviceState *dev = find_device_state(id, false, errp); if (dev != NULL) { if (dev->pending_deleted_event && (dev->pending_deleted_expires_ms == 0 || @@ -956,11 +966,74 @@ void qmp_device_del(const char *id, Error **errp) } } +int qdev_sync_config(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->sync_config) { + error_setg(errp, "device-sync-config is not supported for '%s'", + object_get_typename(OBJECT(dev))); + return -ENOTSUP; + } + + return dc->sync_config(dev, errp); +} + +void qmp_device_sync_config(const char *id, Error **errp) +{ + DeviceState *dev; + + /* + * During migration there is a race between syncing`configuration + * and migrating it (if migrate first, that target would get + * outdated version), so let's just not allow it. + */ + + if (migration_is_running()) { + error_setg(errp, "Config synchronization is not allowed " + "during migration"); + return; + } + + dev = find_device_state(id, true, errp); + if (!dev) { + return; + } + + qdev_sync_config(dev, errp); +} + void hmp_device_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; + QemuOpts *opts; + DeviceState *dev; - qmp_device_add((QDict *)qdict, NULL, &err); + opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &err); + if (!opts) { + goto out; + } + if (qdev_device_help(opts)) { + qemu_opts_del(opts); + return; + } + dev = qdev_device_add(opts, &err); + 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); + } + object_unref(dev); +out: hmp_handle_error(mon, err); } @@ -973,6 +1046,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; @@ -980,7 +1135,7 @@ BlockBackend *blk_by_qdev_id(const char *id, Error **errp) GLOBAL_STATE_CODE(); - dev = find_device_state(id, errp); + dev = find_device_state(id, false, errp); if (dev == NULL) { return NULL; } diff --git a/softmmu/qemu-seccomp.c b/system/qemu-seccomp.c similarity index 97% rename from softmmu/qemu-seccomp.c rename to system/qemu-seccomp.c index d66a2a1226..71ac444802 100644 --- a/softmmu/qemu-seccomp.c +++ b/system/qemu-seccomp.c @@ -47,10 +47,10 @@ const struct scmp_arg_cmp sched_setscheduler_arg[] = { }; /* - * See 'NOTES' in 'man 2 clone' - s390 & cross have 'flags' in + * See 'NOTES' in 'man 2 clone' - s390 has 'flags' in * different position to other architectures */ -#if defined(HOST_S390X) || defined(HOST_S390) || defined(HOST_CRIS) +#if defined(HOST_S390X) || defined(HOST_S390) #define CLONE_FLAGS_ARG 1 #else #define CLONE_FLAGS_ARG 0 @@ -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; diff --git a/softmmu/qtest.c b/system/qtest.c similarity index 88% rename from softmmu/qtest.c rename to system/qtest.c index d3e0ab4eda..12703a2045 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; @@ -330,36 +325,22 @@ static void qtest_irq_handler(void *opaque, int n, int level) } } -static int64_t qtest_clock_counter; +static bool (*process_command_cb)(CharBackend *chr, gchar **words); -int64_t qtest_get_virtual_clock(void) +void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)) { - return qatomic_read_i64(&qtest_clock_counter); + assert(!process_command_cb); /* Switch to a list if we need more than one */ + + process_command_cb = pc_cb; } -static void qtest_set_virtual_clock(int64_t count) +static void qtest_install_gpio_out_intercept(DeviceState *dev, const char *name, int n) { - qatomic_set_i64(&qtest_clock_counter, count); -} + qemu_irq *disconnected = g_new0(qemu_irq, 1); + qemu_irq icpt = qemu_allocate_irq(qtest_irq_handler, + disconnected, n); -static void qtest_clock_warp(int64_t dest) -{ - int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - AioContext *aio_context; - assert(qtest_enabled()); - aio_context = qemu_get_aio_context(); - while (clock < dest) { - int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, - QEMU_TIMER_ATTR_ALL); - int64_t warp = qemu_soonest_timeout(dest - clock, deadline); - - qtest_set_virtual_clock(qtest_get_virtual_clock() + warp); - - qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); - timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); - clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - } - qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + *disconnected = qdev_intercept_gpio_out(dev, icpt, name, n); } static void qtest_process_command(CharBackend *chr, gchar **words) @@ -385,8 +366,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 +380,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 +397,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; @@ -575,9 +569,9 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_send_prefix(chr); qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value); } else if (strcmp(words[0], "read") == 0) { - uint64_t addr, len, i; + g_autoptr(GString) enc = NULL; + uint64_t addr, len; uint8_t *data; - char *enc; int ret; g_assert(words[1] && words[2]); @@ -592,16 +586,12 @@ static void qtest_process_command(CharBackend *chr, gchar **words) address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, len); - enc = g_malloc(2 * len + 1); - for (i = 0; i < len; i++) { - sprintf(&enc[i * 2], "%02x", data[i]); - } + enc = qemu_hexdump_line(NULL, data, len, 0, 0); qtest_send_prefix(chr); - qtest_sendf(chr, "OK 0x%s\n", enc); + qtest_sendf(chr, "OK 0x%s\n", enc->str); g_free(data); - g_free(enc); } else if (strcmp(words[0], "b64read") == 0) { uint64_t addr, len; uint8_t *data; @@ -714,30 +704,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; @@ -748,7 +719,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, QEMU_TIMER_ATTR_ALL); } - qtest_clock_warp(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns); + qemu_clock_advance_virtual_time( + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns); qtest_send_prefix(chr); qtest_sendf(chr, "OK %"PRIi64"\n", (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); @@ -774,10 +746,12 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(words[1]); ret = qemu_strtoi64(words[1], NULL, 0, &ns); g_assert(ret == 0); - qtest_clock_warp(ns); + qemu_clock_advance_virtual_time(ns); 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]); @@ -858,7 +832,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) { @@ -867,18 +841,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) diff --git a/softmmu/rtc.c b/system/rtc.c similarity index 92% rename from softmmu/rtc.c rename to system/rtc.c index 7e2956f81e..216d2aee3a 100644 --- a/softmmu/rtc.c +++ b/system/rtc.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/timer.h" @@ -33,6 +32,7 @@ #include "sysemu/replay.h" #include "sysemu/sysemu.h" #include "sysemu/rtc.h" +#include "hw/rtc/mc146818rtc.h" static enum { RTC_BASE_UTC, @@ -62,12 +62,12 @@ static time_t qemu_ref_timedate(QEMUClockType clock) } break; default: - assert(0); + g_assert_not_reached(); } 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 +84,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 +151,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 +174,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 81% rename from softmmu/runstate.c rename to system/runstate.c index b70a741158..fb9c2cde76 100644 --- a/softmmu/runstate.c +++ b/system/runstate.c @@ -30,8 +30,9 @@ #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 "hw/resettable.h" #include "migration/misc.h" #include "migration/postcopy-ram.h" #include "monitor/monitor.h" @@ -40,13 +41,13 @@ #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,10 +123,17 @@ 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 }, @@ -142,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 }, @@ -154,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 }, @@ -167,6 +182,12 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE__MAX, RUN_STATE__MAX }, }; +static const RunStateTransition replay_play_runstate_transitions_def[] = { + { RUN_STATE_SHUTDOWN, RUN_STATE_RUNNING}, + + { RUN_STATE__MAX, RUN_STATE__MAX }, +}; + static bool runstate_valid_transitions[RUN_STATE__MAX][RUN_STATE__MAX]; bool runstate_check(RunState state) @@ -174,26 +195,33 @@ bool runstate_check(RunState state) return current_run_state == state; } -bool runstate_store(char *str, size_t size) +static void transitions_set_valid(const RunStateTransition *rst) { - const char *state = RunState_str(current_run_state); - size_t len = strlen(state) + 1; + const RunStateTransition *p; - if (len > size) { - return false; + for (p = rst; p->from != RUN_STATE__MAX; p++) { + runstate_valid_transitions[p->from][p->to] = true; + } +} + +void runstate_replay_enable(void) +{ + assert(replay_mode != REPLAY_MODE_NONE); + + if (replay_mode == REPLAY_MODE_PLAY) { + /* + * When reverse-debugging, it is possible to move state from + * shutdown to running. + */ + transitions_set_valid(&replay_play_runstate_transitions_def[0]); } - memcpy(str, state, len); - return true; } static void runstate_init(void) { - const RunStateTransition *p; - memset(&runstate_valid_transitions, 0, sizeof(runstate_valid_transitions)); - for (p = &runstate_transitions_def[0]; p->from != RUN_STATE__MAX; p++) { - runstate_valid_transitions[p->from][p->to] = true; - } + + transitions_set_valid(&runstate_transitions_def[0]); qemu_mutex_init(&vmstop_lock); } @@ -220,6 +248,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); @@ -236,7 +269,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; @@ -264,6 +296,7 @@ void qemu_system_vmstop_request(RunState state) } struct VMChangeStateEntry { VMChangeStateHandler *cb; + VMChangeStateHandler *prepare_cb; void *opaque; QTAILQ_ENTRY(VMChangeStateEntry) entries; int priority; @@ -286,12 +319,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; @@ -326,10 +386,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); } @@ -338,6 +410,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; @@ -435,15 +508,23 @@ static int qemu_debug_requested(void) void qemu_system_reset(ShutdownCause reason) { MachineClass *mc; + ResetType type; mc = current_machine ? MACHINE_GET_CLASS(current_machine) : NULL; cpu_synchronize_all_states(); + switch (reason) { + case SHUTDOWN_CAUSE_SNAPSHOT_LOAD: + type = RESET_TYPE_SNAPSHOT_LOAD; + break; + default: + type = RESET_TYPE_COLD; + } if (mc && mc->reset) { - mc->reset(current_machine, reason); + mc->reset(current_machine, type); } else { - qemu_devices_reset(reason); + qemu_devices_reset(type); } switch (reason) { case SHUTDOWN_CAUSE_NONE: @@ -453,7 +534,21 @@ void qemu_system_reset(ShutdownCause reason) default: qapi_event_send_reset(shutdown_caused_by_guest(reason), reason); } - cpu_synchronize_all_post_reset(); + + /* + * Some boards use the machine reset callback to point CPUs to the firmware + * entry point. Assume that this is not the case for boards that support + * non-resettable CPUs (currently used only for confidential guests), in + * which case cpu_synchronize_all_post_init() is enough because + * it does _more_ than cpu_synchronize_all_post_reset(). + */ + if (cpus_are_resettable()) { + cpu_synchronize_all_post_reset(); + } else { + assert(runstate_check(RUN_STATE_PRELAUNCH)); + } + + vm_set_suspended(false); } /* @@ -484,18 +579,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 || panic_action == PANIC_ACTION_EXIT_FAILURE) { - qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_POWEROFF, - !!info, info); + 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) { @@ -522,13 +614,14 @@ 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); + qapi_free_GuestPanicInformation(info); +} - qapi_event_send_guest_crashloaded(GUEST_PANIC_ACTION_RUN, - !!info, info); - - if (info) { - qapi_free_GuestPanicInformation(info); - } +void qemu_system_guest_pvshutdown(void) +{ + qapi_event_send_guest_pvshutdown(); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } void qemu_system_reset_request(ShutdownCause reason) @@ -625,6 +718,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); @@ -686,7 +786,9 @@ static bool main_loop_should_exit(int *status) if (shutdown_action == SHUTDOWN_ACTION_PAUSE) { vm_stop(RUN_STATE_SHUTDOWN); } else { - if (request == SHUTDOWN_CAUSE_GUEST_PANIC && + 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; } @@ -728,18 +830,9 @@ static bool main_loop_should_exit(int *status) int qemu_main_loop(void) { int status = EXIT_SUCCESS; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif while (!main_loop_should_exit(&status)) { -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif main_loop_wait(false); -#ifdef CONFIG_PROFILER - dev_time += profile_getclock() - ti; -#endif } return status; @@ -778,7 +871,7 @@ void qemu_init_subsystems(void) qemu_mutex_lock_main_loop(); #endif - qemu_mutex_lock_iothread(); + bql_lock(); atexit(qemu_run_exit_notifiers); @@ -804,9 +897,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 @@ -822,21 +915,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/system/trace-events b/system/trace-events new file mode 100644 index 0000000000..5bbc3fbffa --- /dev/null +++ b/system/trace-events @@ -0,0 +1,53 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# balloon.c +# Since requests are raised via monitor, not many tracepoints are needed. +balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" + +# dma-helpers.c +dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d" +dma_aio_cancel(void *dbs) "dbs=%p" +dma_complete(void *dbs, int ret, void *cb) "dbs=%p ret=%d cb=%p" +dma_blk_cb(void *dbs, int ret) "dbs=%p ret=%d" +dma_map_wait(void *dbs) "dbs=%p" + +# ioport.c +cpu_in(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" +cpu_out(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" + +# memory.c +memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" +memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" +memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" +memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" +memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" +memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" +memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr '%s' listener '%s' synced (global=%d)" +flatview_new(void *view, void *root) "%p (root %p)" +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 + +# physmem.c +address_space_map(void *as, uint64_t addr, uint64_t len, bool is_write, uint32_t attrs) "as:%p addr 0x%"PRIx64":%"PRIx64" write:%d attrs:0x%x" +find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64 +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" + +# cpus.c +vm_stop_flush_all(int ret) "ret %d" + +# vl.c +vm_state_notify(int running, int reason, const char *reason_str) "running %d reason %d (%s)" +load_file(const char *name, const char *path) "name %s location %s" +runstate_set(int current_state, const char *current_state_str, int new_state, const char *new_state_str) "current_run_state %d (%s) new_state %d (%s)" +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 91% rename from softmmu/vl.c rename to system/vl.c index badf207da0..9168fcec9a 100644 --- a/softmmu/vl.c +++ b/system/vl.c @@ -49,6 +49,7 @@ #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" @@ -67,6 +68,7 @@ #include "sysemu/numa.h" #include "sysemu/hostmem.h" #include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "qemu/timer.h" #include "chardev/char.h" #include "qemu/bitmap.h" @@ -86,16 +88,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" #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" @@ -126,15 +129,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" - #include "ui/xemu-settings.h" #include "ui/xemu-notifications.h" #include "ui/xemu-net.h" @@ -190,6 +190,7 @@ 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; @@ -199,10 +200,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 }, @@ -221,6 +223,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 = { @@ -617,7 +620,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); @@ -730,6 +733,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, @@ -739,6 +748,9 @@ static QemuOptsList qemu_smp_opts = { }, { .name = "clusters", .type = QEMU_OPT_NUMBER, + }, { + .name = "modules", + .type = QEMU_OPT_NUMBER, }, { .name = "cores", .type = QEMU_OPT_NUMBER, @@ -753,6 +765,37 @@ 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, + }, + { + .name = "user", + .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) { @@ -862,7 +905,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" @@ -871,6 +914,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 { @@ -889,7 +942,7 @@ static const QEMUOption qemu_options[] = { #define ARCHHEADING(text, arch_mask) #include "qemu-options.def" - { NULL }, + { /* end of list */ } }; typedef struct VGAInterfaceInfo { @@ -953,16 +1006,34 @@ static bool vga_interface_available(VGAInterfaceType t) const VGAInterfaceInfo *ti = &vga_interfaces[t]; assert(t < VGA_TYPE_MAX); - return !ti->class_names[0] || - module_object_class_by_name(ti->class_names[0]) || - module_object_class_by_name(ti->class_names[1]); + + if (!ti->class_names[0] || module_object_class_by_name(ti->class_names[0])) { + return true; + } + + if (ti->class_names[1] && module_object_class_by_name(ti->class_names[1])) { + return true; + } + + return false; } 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)) { @@ -1024,12 +1095,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); @@ -1045,13 +1116,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 @@ -1059,13 +1131,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 + + parse_display_qapi(p); } static inline bool nonempty_str(const char *str) @@ -1179,21 +1254,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); } } @@ -1285,15 +1360,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 @@ -1313,23 +1415,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) { @@ -1342,23 +1450,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); @@ -1371,18 +1462,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; @@ -1473,7 +1568,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); @@ -1584,28 +1680,27 @@ 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"); - GSList *machines = object_class_get_list(TYPE_MACHINE, false); - MachineClass *machine_class; - Error *local_err = NULL; + ERRP_GUARD(); + const char *machine_type = qdict_get_try_str(qdict, "type"); + g_autoptr(GSList) machines = object_class_get_list(TYPE_MACHINE, false); + MachineClass *machine_class = NULL; - if (optarg) { - machine_class = find_machine(optarg, machines); - qdict_del(qdict, "type"); + if (machine_type) { + machine_class = find_machine(machine_type, machines); if (!machine_class) { - error_setg(&local_err, "unsupported machine type"); + error_setg(errp, "unsupported machine type: \"%s\"", machine_type); } + qdict_del(qdict, "type"); } else { machine_class = find_default_machine(machines); if (!machine_class) { - error_setg(&local_err, "No machine specified, and there is no default"); + error_setg(errp, "No machine specified, and there is no default"); } } - g_slist_free(machines); - if (local_err) { - error_append_hint(&local_err, "Use -machine help to list supported machines\n"); - error_propagate(errp, local_err); + if (!machine_class) { + error_append_hint(errp, + "Use -machine help to list supported machines\n"); } return machine_class; } @@ -1734,27 +1829,28 @@ 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); } type = qemu_opt_get(opts, "qom-type"); if (!type) { - error_setg(&error_fatal, QERR_MISSING_PARAMETER, "qom-type"); + error_report(QERR_MISSING_PARAMETER, "qom-type"); + exit(1); } if (user_creatable_print_help(type, opts)) { exit(0); @@ -1841,7 +1937,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; @@ -1860,9 +1955,7 @@ static void qemu_apply_machine_options(QDict *qdict) } 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"); } } @@ -1885,14 +1978,14 @@ 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) { + if (dpy.has_gl && dpy.gl != DISPLAY_GL_MODE_OFF && display_opengl == 0) { #if defined(CONFIG_OPENGL) - error_report("OpenGL is not supported by the display"); + error_report("OpenGL is not supported by display backend '%s'", + DisplayType_str(dpy.type)); #else - error_report("OpenGL support is disabled"); + error_report("OpenGL support was not enabled in this build of QEMU"); #endif exit(1); } @@ -1917,8 +2010,9 @@ static void qemu_create_early_backends(void) * setting machine properties, so they can be referred to. */ configure_blockdev(&bdo_queue, machine_class, snapshot); - if (!audio_init_audiodevs()) { - exit(1); + audio_init_audiodevs(); + if (default_audio) { + audio_create_default_audiodevs(); } } @@ -1948,6 +2042,14 @@ static void qemu_create_late_backends(void) 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); } @@ -2046,7 +2148,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); @@ -2086,6 +2187,7 @@ 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")) { @@ -2101,6 +2203,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 @@ -2198,8 +2309,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, @@ -2218,14 +2328,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)); @@ -2235,14 +2349,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) @@ -2259,6 +2376,7 @@ static void configure_accelerators(const char *progname) /* Select the default accelerator */ bool have_tcg = accel_find("tcg"); bool have_kvm = accel_find("kvm"); + bool have_hvf = accel_find("hvf"); if (have_tcg && have_kvm) { if (g_str_has_suffix(progname, "kvm")) { @@ -2271,6 +2389,8 @@ static void configure_accelerators(const char *progname) accelerators = "kvm"; } else if (have_tcg) { accelerators = "tcg"; + } else if (have_hvf) { + accelerators = "hvf"; } else { error_report("No accelerator selected and" " no default accelerator available"); @@ -2357,17 +2477,21 @@ static void qemu_validate_options(const QDict *machine_opts) const char *kernel_cmdline = qdict_get_try_str(machine_opts, "append"); if (kernel_filename == NULL) { - if (kernel_cmdline != NULL) { - error_report("-append only allowed with -kernel option"); - exit(1); - } + if (kernel_cmdline != NULL) { + error_report("-append only allowed with -kernel option"); + exit(1); + } - if (initrd_filename != NULL) { - error_report("-initrd only allowed with -kernel option"); - exit(1); - } + if (initrd_filename != NULL) { + error_report("-initrd only allowed with -kernel option"); + exit(1); + } } + 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"); @@ -2474,7 +2598,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); } @@ -2507,10 +2631,11 @@ static void qemu_maybe_daemonize(const char *pid_file) pid_file_realpath = g_malloc0(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(1); + 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) { @@ -2556,11 +2681,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) @@ -2583,23 +2703,17 @@ static void qemu_create_cli_devices(void) qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, &error_fatal); QTAILQ_FOREACH(opt, &device_opts, next) { - DeviceState *dev; + QObject *ret_data = NULL; + loc_push_restore(&opt->loc); - /* - * TODO Eventually we should call qmp_device_add() here to make sure it - * behaves the same, but QMP still has to accept incorrectly typed - * options until libvirt is fixed and we want to be strict on the CLI - * from the start, so call qdev_device_add_from_qdict() directly for - * now. - */ - dev = qdev_device_add_from_qdict(opt->opts, true, &error_fatal); - object_unref(OBJECT(dev)); + qmp_device_add(opt->opts, &ret_data, &error_fatal); + assert(ret_data == NULL); /* error_fatal aborts */ loc_pop(&opt->loc); } 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()); @@ -2622,15 +2736,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) { @@ -2638,6 +2752,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) @@ -2649,10 +2764,14 @@ void qmp_x_exit_preconfig(Error **errp) qemu_init_board(); qemu_create_cli_devices(); - 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(); @@ -2661,7 +2780,8 @@ 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, true, true, + &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); @@ -2876,11 +2996,8 @@ void qemu_init(int argc, char **argv) fake_argv[fake_argc++] = strdup("-device"); fake_argv[fake_argc++] = strdup("usb-hub,port=1,ports=4"); -#ifdef _WIN32 - // FIXME: Create this dummy device to prevent logspam - fake_argv[fake_argc++] = strdup("-audiodev"); - fake_argv[fake_argc++] = strdup("none,id=snd0"); -#endif + fake_argv[fake_argc++] = strdup("-audio"); + fake_argv[fake_argc++] = strdup("none"); for (int i = 1; i < argc; i++) { if (argv[i] != NULL) { @@ -2931,11 +3048,14 @@ void qemu_init(int argc, char **argv) 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(); @@ -3031,13 +3151,8 @@ void qemu_init(int argc, char **argv) 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"), @@ -3054,17 +3169,6 @@ void qemu_init(int argc, char **argv) nographic = true; dpy.type = DISPLAY_TYPE_NONE; break; - case QEMU_OPTION_portrait: - graphic_rotate = 90; - break; - case QEMU_OPTION_rotate: - graphic_rotate = strtol(optarg, (char **) &optarg, 10); - if (graphic_rotate != 0 && graphic_rotate != 90 && - graphic_rotate != 180 && graphic_rotate != 270) { - error_report("only 90, 180, 270 deg rotation is available"); - exit(1); - } - break; case QEMU_OPTION_kernel: qdict_put_str(machine_opts_dict, "kernel", optarg); break; @@ -3089,7 +3193,7 @@ void qemu_init(int argc, char **argv) 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; @@ -3116,19 +3220,17 @@ void qemu_init(int argc, char **argv) } 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: { bool help; - char *model; + 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(); @@ -3137,22 +3239,25 @@ void qemu_init(int argc, char **argv) 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: @@ -3190,6 +3295,14 @@ void qemu_init(int argc, char **argv) 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; @@ -3209,9 +3322,6 @@ void qemu_init(int argc, char **argv) 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; @@ -3396,7 +3506,6 @@ void qemu_init(int argc, char **argv) } 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; @@ -3422,7 +3531,7 @@ void qemu_init(int argc, char **argv) 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"), @@ -3516,15 +3625,12 @@ void qemu_init(int argc, char **argv) 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); @@ -3614,7 +3720,7 @@ void qemu_init(int argc, char **argv) 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); } @@ -3645,12 +3751,7 @@ void qemu_init(int argc, char **argv) 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); } @@ -3700,21 +3801,21 @@ void qemu_init(int argc, char **argv) if (!opts) { exit(1); } - enable_mlock = qemu_opt_get_bool(opts, "mem-lock", false); - enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", false); + enable_mlock = qemu_opt_get_bool(opts, "mem-lock", enable_mlock); + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", enable_cpu_pm); 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; } @@ -3744,11 +3845,52 @@ void qemu_init(int argc, char **argv) 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: + warn_report("-runas is deprecated, use '-run-with user=...' instead"); + 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); + } + str = qemu_opt_get(opts, "user"); + if (str) { + if (!os_set_runas(str)) { + error_report("User \"%s\" doesn't exist" + " (and is not :)", + optarg); + exit(1); + } + } + + break; + } +#endif /* CONFIG_POSIX */ + + default: + error_report("Option not supported in this build"); + exit(1); } } } @@ -3799,6 +3941,7 @@ void qemu_init(int argc, char **argv) suspend_mux_open(); qemu_disable_default_devices(); + qemu_setup_display(); qemu_create_default_devices(); qemu_create_early_backends(); @@ -3836,16 +3979,22 @@ void qemu_init(int argc, char **argv) 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); } 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/Kconfig b/target/Kconfig index 83da0bd293..d0c7b59d9c 100644 --- a/target/Kconfig +++ b/target/Kconfig @@ -1,14 +1,12 @@ source alpha/Kconfig source arm/Kconfig source avr/Kconfig -source cris/Kconfig source hppa/Kconfig source i386/Kconfig source loongarch/Kconfig source m68k/Kconfig source microblaze/Kconfig source mips/Kconfig -source nios2/Kconfig source openrisc/Kconfig source ppc/Kconfig source riscv/Kconfig @@ -18,3 +16,6 @@ source sh4/Kconfig source sparc/Kconfig source tricore/Kconfig source xtensa/Kconfig + +config TARGET_BIG_ENDIAN + bool 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..c21ddf1afd 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -2,19 +2,32 @@ * Alpha cpu parameters for qemu. * * Copyright (c) 2007 Jocelyn Mayer - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef ALPHA_CPU_PARAM_H #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 + +/* Alpha processors have a weak memory model */ +#define TCG_GUEST_DEFAULT_MO (0) #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 270ae787b1..5d75c941f7 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -24,29 +24,42 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "exec/exec-all.h" +#include "fpu/softfloat.h" static void alpha_cpu_set_pc(CPUState *cs, vaddr value) { - AlphaCPU *cpu = ALPHA_CPU(cs); - - cpu->env.pc = value; + CPUAlphaState *env = cpu_env(cs); + env->pc = value; } static vaddr alpha_cpu_get_pc(CPUState *cs) { - AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = cpu_env(cs); + return env->pc; +} - return cpu->env.pc; +static void alpha_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)) { + CPUAlphaState *env = cpu_env(cs); + env->pc = tb->pc; + } } static void alpha_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = cpu_env(cs); - cpu->env.pc = data[0]; + if (tb_cflags(tb) & CF_PCREL) { + env->pc = (env->pc & TARGET_PAGE_MASK) | data[0]; + } else { + env->pc = data[0]; + } } static bool alpha_cpu_has_work(CPUState *cs) @@ -64,6 +77,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; @@ -76,6 +94,11 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) AlphaCPUClass *acc = ALPHA_CPU_GET_CLASS(dev); Error *local_err = NULL; +#ifndef CONFIG_USER_ONLY + /* Use pc-relative instructions in system-mode */ + cs->tcg_cflags |= CF_PCREL; +#endif + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -87,23 +110,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; @@ -126,8 +132,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; } @@ -142,55 +147,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; @@ -198,20 +181,24 @@ 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; + CPUAlphaState *env = cpu_env(CPU(obj)); - cpu_set_cpustate_pointers(cpu); + /* TODO all this should be done in reset, not init */ env->lock_addr = -1; + + /* + * TODO: this is incorrect. The Alpha Architecture Handbook version 4 + * describes NaN propagation in section 4.7.10.4. We should prefer + * the operand in Fb (whether it is a QNaN or an SNaN), then the + * operand in Fa. That is float_2nan_prop_ba. + */ + set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); #if defined(CONFIG_USER_ONLY) env->flags = ENV_FLAG_PS_USER | ENV_FLAG_FEN; cpu_alpha_store_fpcr(env, (uint64_t)(FPCR_INVD | FPCR_DZED | FPCR_OVFD @@ -232,8 +219,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, + .synchronize_from_tb = alpha_cpu_synchronize_from_tb, .restore_state_to_opc = alpha_restore_state_to_opc, #ifdef CONFIG_USER_ONLY @@ -242,6 +230,7 @@ static const struct TCGCPUOps alpha_tcg_ops = { #else .tlb_fill = alpha_cpu_tlb_fill, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, + .cpu_exec_halt = alpha_cpu_has_work, .do_interrupt = alpha_cpu_do_interrupt, .do_transaction_failed = alpha_cpu_do_transaction_failed, .do_unaligned_access = alpha_cpu_do_unaligned_access, @@ -259,6 +248,7 @@ 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; @@ -286,6 +276,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..3556d3227f 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -24,9 +24,6 @@ #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" -/* Alpha processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - #define ICACHE_LINE_SIZE 32 #define DCACHE_LINE_SIZE 32 @@ -191,7 +188,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 +256,37 @@ 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. + * + * An Alpha CPU model. + */ +struct AlphaCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; +}; #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 +365,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 +384,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 +432,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 +462,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..1a7e2dd920 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,9 +58,8 @@ 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; - target_ulong tmp = ldtul_p(mem_buf); + CPUAlphaState *env = cpu_env(cs); + target_ulong tmp = ldq_le_p(mem_buf); CPU_DoubleU d; switch (n) { diff --git a/target/alpha/helper.c b/target/alpha/helper.c index 970c869771..2f1000c99f 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "fpu/softfloat-types.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" @@ -124,7 +125,7 @@ void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, MMUAccessType access_type, bool maperr, uintptr_t retaddr) { - AlphaCPU *cpu = ALPHA_CPU(cs); + CPUAlphaState *env = cpu_env(cs); target_ulong mmcsr, cause; /* Assuming !maperr, infer the missing protection. */ @@ -155,9 +156,9 @@ void alpha_cpu_record_sigsegv(CPUState *cs, vaddr address, } /* Record the arguments that PALcode would give to the kernel. */ - cpu->env.trap_arg0 = address; - cpu->env.trap_arg1 = mmcsr; - cpu->env.trap_arg2 = cause; + env->trap_arg0 = address; + env->trap_arg1 = mmcsr; + env->trap_arg2 = cause; } #else /* Returns the OSF/1 entMM failure indication, or -1 on success. */ @@ -286,11 +287,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 +298,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 +324,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 +433,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 +484,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", 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 a39b52c5dd..872955f5e7 100644 --- a/target/alpha/mem_helper.c +++ b/target/alpha/mem_helper.c @@ -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 f9bcdeb717..fb6cac4b53 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -20,16 +20,17 @@ #include "qemu/osdep.h" #include "cpu.h" #include "sysemu/cpus.h" -#include "disas/disas.h" #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 @@ -52,6 +53,9 @@ struct DisasContext { uint32_t tbflags; int mem_idx; + /* True if generating pc-relative code. */ + bool pcrel; + /* implver and amask values for this CPU. */ int implver; int amask; @@ -72,7 +76,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 +97,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 +133,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 +148,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 +157,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 +181,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 +246,22 @@ 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_pc_disp(DisasContext *ctx, TCGv dest, int32_t disp) +{ + uint64_t addr = ctx->base.pc_next + disp; + if (ctx->pcrel) { + tcg_gen_addi_i64(dest, cpu_pc, addr - ctx->base.pc_first); + } else { + tcg_gen_movi_i64(dest, addr); + } } static void gen_excp_1(int exception, int error_code) @@ -259,12 +270,12 @@ 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) { - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + gen_pc_disp(ctx, cpu_pc, 0); gen_excp_1(exception, error_code); return DISAS_NORETURN; } @@ -279,7 +290,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 +297,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 +304,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 +319,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 +349,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 +356,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 +363,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 +370,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 +383,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 +400,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 +416,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 +425,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); @@ -439,133 +437,107 @@ static DisasJumpType gen_store_conditional(DisasContext *ctx, int ra, int rb, return DISAS_NEXT; } -static bool use_goto_tb(DisasContext *ctx, uint64_t dest) +static void gen_goto_tb(DisasContext *ctx, int idx, int32_t disp) { - return translator_use_goto_tb(&ctx->base, dest); + if (translator_use_goto_tb(&ctx->base, ctx->base.pc_next + disp)) { + /* With PCREL, PC must always be up-to-date. */ + if (ctx->pcrel) { + gen_pc_disp(ctx, cpu_pc, disp); + tcg_gen_goto_tb(idx); + } else { + tcg_gen_goto_tb(idx); + gen_pc_disp(ctx, cpu_pc, disp); + } + tcg_gen_exit_tb(ctx->base.tb, idx); + } else { + gen_pc_disp(ctx, cpu_pc, disp); + tcg_gen_lookup_and_goto_ptr(); + } } static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) { - uint64_t dest = ctx->base.pc_next + (disp << 2); - if (ra != 31) { - tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next); + gen_pc_disp(ctx, ctx->ir[ra], 0); } /* Notice branch-to-next; used to initialize RA with the PC. */ if (disp == 0) { - return 0; - } else if (use_goto_tb(ctx, dest)) { - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, 0); - return DISAS_NORETURN; - } else { - tcg_gen_movi_i64(cpu_pc, dest); - return DISAS_PC_UPDATED; + return DISAS_NEXT; } + gen_goto_tb(ctx, 0, disp); + return DISAS_NORETURN; } 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); + gen_goto_tb(ctx, 0, 0); + gen_set_label(lab_true); + gen_goto_tb(ctx, 1, disp); - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb(ctx->base.tb, 0); - - gen_set_label(lab_true); - tcg_gen_goto_tb(1); - tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, 1); - - return DISAS_NORETURN; - } else { - TCGv_i64 z = load_zero(ctx); - 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); - return DISAS_PC_UPDATED; - } + return DISAS_NORETURN; } 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 +573,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 +582,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 +602,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 +610,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 +627,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 +669,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 +684,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 +696,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 +723,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 +745,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 +774,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 +788,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 +817,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 +891,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 +911,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 +948,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 +975,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 +1005,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 +1024,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 +1050,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 +1079,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,11 +1104,10 @@ 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. */ - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + gen_pc_disp(ctx, cpu_pc, 0); return DISAS_PC_UPDATED_NOCHAIN; case 0x36: @@ -1166,23 +1117,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); @@ -1203,20 +1154,17 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) #else { TCGv tmp = tcg_temp_new(); - uint64_t exc_addr = ctx->base.pc_next; - uint64_t entry = ctx->palbr; + uint64_t entry; + gen_pc_disp(ctx, tmp, 0); if (ctx->tbflags & ENV_FLAG_PAL_MODE) { - exc_addr |= 1; + tcg_gen_ori_i64(tmp, tmp, 1); } else { - tcg_gen_movi_i64(tmp, 1); - st_flag_byte(tmp, ENV_FLAG_PAL_SHIFT); + st_flag_byte(tcg_constant_i64(1), ENV_FLAG_PAL_SHIFT); } + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPUAlphaState, exc_addr)); - tcg_gen_movi_i64(tmp, exc_addr); - tcg_gen_st_i64(tmp, cpu_env, offsetof(CPUAlphaState, exc_addr)); - tcg_temp_free(tmp); - + entry = ctx->palbr; entry += (palcode & 0x80 ? 0x2000 + (palcode - 0x80) * 64 : 0x1000 + palcode * 64); @@ -1273,8 +1221,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 +1243,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 +1261,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 +1282,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 +1316,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; @@ -1435,7 +1381,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) real_islit = islit = extract32(insn, 12, 1); lit = extract32(insn, 13, 8); - disp21 = sextract32(insn, 0, 21); + disp21 = sextract32(insn, 0, 21) * 4; disp16 = sextract32(insn, 0, 16); disp12 = sextract32(insn, 0, 12); @@ -1550,7 +1496,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 +1508,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 +1524,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 +1531,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 +1545,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 +1555,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 +1565,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 +1583,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 +1592,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 +1609,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 +1622,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 +1672,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 +1810,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 +1825,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 +1840,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 +1901,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 +1909,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 +1917,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 +1936,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 +1958,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 +1971,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 +1994,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 +2018,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 +2064,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 +2227,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 +2238,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 +2277,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 +2314,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 */ @@ -2444,9 +2358,13 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) /* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch prediction stack action, which of course we don't implement. */ vb = load_gpr(ctx, rb); - tcg_gen_andi_i64(cpu_pc, vb, ~3); if (ra != 31) { - tcg_gen_movi_i64(ctx->ir[ra], ctx->base.pc_next); + tmp = tcg_temp_new(); + tcg_gen_andi_i64(tmp, vb, ~3); + gen_pc_disp(ctx, ctx->ir[ra], 0); + tcg_gen_mov_i64(cpu_pc, tmp); + } else { + tcg_gen_andi_i64(cpu_pc, vb, ~3); } ret = DISAS_PC_UPDATED; break; @@ -2464,21 +2382,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 +2421,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 +2438,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 +2471,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 +2617,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 +2626,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 +2646,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 +2654,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 +2758,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 +2775,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 +2816,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 +2860,12 @@ 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->pcrel = ctx->base.tb->cflags & CF_PCREL; ctx->implver = env->implver; ctx->amask = env->amask; @@ -2963,7 +2881,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. */ @@ -2983,20 +2901,25 @@ static void alpha_tr_tb_start(DisasContextBase *db, CPUState *cpu) static void alpha_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { - tcg_gen_insn_start(dcbase->pc_next); + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + if (ctx->pcrel) { + tcg_gen_insn_start(dcbase->pc_next & ~TARGET_PAGE_MASK); + } else { + tcg_gen_insn_start(dcbase->pc_next); + } } 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) @@ -3007,14 +2930,10 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_NORETURN: break; case DISAS_TOO_MANY: - if (use_goto_tb(ctx, ctx->base.pc_next)) { - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb(ctx->base.tb, 0); - } - /* FALLTHRU */ + gen_goto_tb(ctx, 0, 0); + break; case DISAS_PC_STALE: - tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); + gen_pc_disp(ctx, cpu_pc, 0); /* FALLTHRU */ case DISAS_PC_UPDATED: tcg_gen_lookup_and_goto_ptr(); @@ -3027,24 +2946,16 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void alpha_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps alpha_tr_ops = { .init_disas_context = alpha_tr_init_disas_context, .tb_start = alpha_tr_tb_start, .insn_start = alpha_tr_insn_start, .translate_insn = alpha_tr_translate_insn, .tb_stop = alpha_tr_tb_stop, - .disas_log = alpha_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; 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..7f8a2217ae 100644 --- a/target/arm/Kconfig +++ b/target/arm/Kconfig @@ -1,6 +1,15 @@ 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 + + select DEVICE_TREE # needed by boot.c config AARCH64 bool select ARM + # kvm_arch_fixup_msi_route() needs to access PCIDevice + select PCI if KVM diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index 2d8e41ab8a..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 { 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/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c new file mode 100644 index 0000000000..3cc8cc738b --- /dev/null +++ b/target/arm/arm-qmp-cmds.c @@ -0,0 +1,248 @@ +/* + * QEMU monitor.c for ARM. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "kvm_arm.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qmp/qdict.h" +#include "qom/qom-qobject.h" + +static GICCapability *gic_cap_new(int version) +{ + GICCapability *cap = g_new0(GICCapability, 1); + cap->version = version; + /* by default, support none */ + cap->emulated = false; + cap->kernel = false; + return cap; +} + +static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) +{ +#ifdef CONFIG_KVM + int fdarray[3]; + + if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) { + return; + } + + /* Test KVM GICv2 */ + if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) { + v2->kernel = true; + } + + /* Test KVM GICv3 */ + if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) { + v3->kernel = true; + } + + kvm_arm_destroy_scratch_host_vcpu(fdarray); +#endif +} + +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + GICCapabilityList *head = NULL; + GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3); + + v2->emulated = true; + v3->emulated = true; + + gic_cap_kvm_probe(v2, v3); + + QAPI_LIST_PREPEND(head, v2); + QAPI_LIST_PREPEND(head, v3); + + return head; +} + +QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16); + +/* + * These are cpu model features we want to advertise. The order here + * matters as this is the order in which qmp_query_cpu_model_expansion + * will attempt to set them. If there are dependencies between features, + * then the order that considers those dependencies must be used. + */ +static const char *cpu_model_advertised_features[] = { + "aarch64", "pmu", "sve", + "sve128", "sve256", "sve384", "sve512", + "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", + "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", + "kvm-no-adjvtime", "kvm-steal-time", + "pauth", "pauth-impdef", "pauth-qarma3", + NULL +}; + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + CpuModelExpansionInfo *expansion_info; + const QDict *qdict_in; + QDict *qdict_out; + ObjectClass *oc; + Object *obj; + const char *name; + int i; + + if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { + error_setg(errp, "The requested expansion type is not supported"); + return NULL; + } + + if (!kvm_enabled() && !strcmp(model->name, "host")) { + error_setg(errp, "The CPU type '%s' requires KVM", model->name); + return NULL; + } + + oc = cpu_class_by_name(TYPE_ARM_CPU, model->name); + if (!oc) { + error_setg(errp, "The CPU type '%s' is not a recognized ARM CPU type", + model->name); + return NULL; + } + + if (kvm_enabled()) { + bool supported = false; + + if (!strcmp(model->name, "host") || !strcmp(model->name, "max")) { + /* These are kvmarm's recommended cpu types */ + supported = true; + } else if (current_machine->cpu_type) { + const char *cpu_type = current_machine->cpu_type; + int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); + + if (strlen(model->name) == len && + !strncmp(model->name, cpu_type, len)) { + /* KVM is enabled and we're using this type, so it works. */ + supported = true; + } + } + if (!supported) { + error_setg(errp, "We cannot guarantee the CPU type '%s' works " + "with KVM on this host", model->name); + return NULL; + } + } + + obj = object_new(object_class_get_name(oc)); + + if (model->props) { + Visitor *visitor; + Error *err = NULL; + + visitor = qobject_input_visitor_new(model->props); + 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)) { + if (!object_property_set(obj, name, visitor, &err)) { + break; + } + } + } + + if (!err) { + visit_check_struct(visitor, &err); + } + if (!err) { + arm_cpu_finalize_features(ARM_CPU(obj), &err); + } + visit_end_struct(visitor, NULL); + visit_free(visitor); + if (err) { + object_unref(obj); + error_propagate(errp, err); + return NULL; + } + } else { + arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); + } + + 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; +} + +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 index 629d75ca5a..da51f2d7f5 100644 --- a/target/arm/common-semi-target.h +++ b/target/arm/common-semi-target.h @@ -10,9 +10,7 @@ #ifndef TARGET_ARM_COMMON_SEMI_TARGET_H #define TARGET_ARM_COMMON_SEMI_TARGET_H -#ifndef CONFIG_USER_ONLY -#include "hw/arm/boot.h" -#endif +#include "target/arm/cpu-qom.h" static inline target_ulong common_semi_arg(CPUState *cs, int argno) { @@ -38,7 +36,7 @@ static inline void common_semi_set_ret(CPUState *cs, target_ulong ret) static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr) { - return (nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cs->env_ptr)); + return nr == TARGET_SYS_EXIT_EXTENDED || is_a64(cpu_env(cs)); } static inline bool is_64bit_semihosting(CPUArchState *env) 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 7e78c2c05c..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, /* @@ -118,8 +121,111 @@ enum { * 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. @@ -224,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; /* @@ -248,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) */ @@ -284,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 @@ -359,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 @@ -370,6 +968,7 @@ struct ARMCPRegInfo { */ CPReadFn *orig_readfn; CPWriteFn *orig_writefn; + CPAccessFn *orig_accessfn; }; /* @@ -493,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..e806f138b8 --- /dev/null +++ b/target/arm/cpu-features.h @@ -0,0 +1,1091 @@ +/* + * 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" +#include "qemu/host-utils.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_ebf16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) > 1; +} + +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_wfxt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, WFXT) >= 2; +} + +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_nmi(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, NMI) != 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_cmow(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, CMOW) != 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); +} + +typedef enum { + CCSIDR_FORMAT_LEGACY, + CCSIDR_FORMAT_CCIDX, +} CCSIDRFormat; + +static inline uint64_t make_ccsidr(CCSIDRFormat format, unsigned assoc, + unsigned linesize, unsigned cachesize, + uint8_t flags) +{ + unsigned lg_linesize = ctz32(linesize); + unsigned sets; + uint64_t ccsidr = 0; + + 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); + + if (format == CCSIDR_FORMAT_LEGACY) { + /* + * The 32-bit CCSIDR format is: + * [27:13] number of sets - 1 + * [12:3] associativity - 1 + * [2:0] log2(linesize) - 4 + * so 0 == 16 bytes, 1 == 32 bytes, 2 == 64 bytes, etc + */ + ccsidr = deposit32(ccsidr, 28, 4, flags); + ccsidr = deposit32(ccsidr, 13, 15, sets - 1); + ccsidr = deposit32(ccsidr, 3, 10, assoc - 1); + ccsidr = deposit32(ccsidr, 0, 3, lg_linesize - 4); + } else { + /* + * 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 + */ + ccsidr = deposit64(ccsidr, 32, 24, sets - 1); + ccsidr = deposit64(ccsidr, 3, 21, assoc - 1); + ccsidr = deposit64(ccsidr, 0, 3, lg_linesize - 4); + } + + return ccsidr; +} + +/* + * 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 53cac9c89b..bed29613c8 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -2,7 +2,7 @@ * ARM cpu parameters for qemu. * * Copyright (c) 2003 Fabrice Bellard - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef ARM_CPU_PARAM_H @@ -19,34 +19,28 @@ #endif #ifdef CONFIG_USER_ONLY -#define TARGET_PAGE_BITS 12 # ifdef TARGET_AARCH64 # define TARGET_TAGGED_ADDRESSES +# ifdef __FreeBSD__ +# define TARGET_PAGE_BITS 12 +# else +/* Allow user-only to vary page size from 4k */ +# define TARGET_PAGE_BITS_VARY +# define TARGET_PAGE_BITS_MIN 12 # endif -#else +# else +# define TARGET_PAGE_BITS 12 +# endif +#else /* !CONFIG_USER_ONLY */ /* * ARMv7 and later CPUs have 4K pages minimum, but ARMv5 and v6 * have to support 1K tiny pages. */ # define TARGET_PAGE_BITS_VARY # define TARGET_PAGE_BITS_MIN 10 +#endif /* !CONFIG_USER_ONLY */ -# define TARGET_TB_PCREL 1 - -/* - * 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. - */ -# define TARGET_PAGE_ENTRY_EXTRA \ - uint8_t pte_attrs; \ - uint8_t shareability; \ - bool guarded; -#endif - -#define NB_MMU_MODES 12 +/* ARM processors have a weak memory model */ +#define TCG_GUEST_DEFAULT_MO (0) #endif diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index 64c44cef2d..b497667d61 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,36 @@ 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 seven inbound GPIO lines */ +#define ARM_CPU_IRQ 0 +#define ARM_CPU_FIQ 1 +#define ARM_CPU_VIRQ 2 +#define ARM_CPU_VFIQ 3 +#define ARM_CPU_NMI 4 +#define ARM_CPU_VINMI 5 +#define ARM_CPU_VFNMI 6 + +/* 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 38d066c294..6938161b95 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -26,18 +26,21 @@ #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" @@ -45,6 +48,8 @@ #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) { @@ -76,17 +81,17 @@ static vaddr arm_cpu_get_pc(CPUState *cs) void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - /* The program counter is always up to date with TARGET_TB_PCREL. */ - if (!TARGET_TB_PCREL) { - CPUARMState *env = cs->env_ptr; + /* 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(tb); + env->pc = tb->pc; } else { - env->regs[15] = tb_pc(tb); + env->regs[15] = tb->pc; } } } @@ -95,10 +100,10 @@ void arm_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - CPUARMState *env = cs->env_ptr; + CPUARMState *env = cpu_env(cs); if (is_a64(env)) { - if (TARGET_TB_PCREL) { + if (tb_cflags(tb) & CF_PCREL) { env->pc = (env->pc & TARGET_PAGE_MASK) | data[0]; } else { env->pc = data[0]; @@ -106,7 +111,7 @@ void arm_restore_state_to_opc(CPUState *cs, env->condexec_bits = 0; env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; } else { - if (TARGET_TB_PCREL) { + if (tb_cflags(tb) & CF_PCREL) { env->regs[15] = (env->regs[15] & TARGET_PAGE_MASK) | data[0]; } else { env->regs[15] = data[0]; @@ -117,6 +122,13 @@ void arm_restore_state_to_opc(CPUState *cs, } #endif /* CONFIG_TCG */ +/* + * With SCTLR_ELx.NMI == 0, IRQ with Superpriority is masked identically with + * IRQ without Superpriority. Moreover, if the GIC is configured so that + * FEAT_GICv3_NMI is only set if FEAT_NMI is set, then we won't ever see + * CPU_INTERRUPT_*NMI anyway. So we might as well accept NMI here + * unconditionally. + */ static bool arm_cpu_has_work(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -124,10 +136,16 @@ static bool arm_cpu_has_work(CPUState *cs) return (cpu->power_state != PSCI_OFF) && cs->interrupt_request & (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR | 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) { @@ -150,6 +168,18 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node); } +/* + * Set the float_status behaviour to match the Arm defaults: + * * tininess-before-rounding + * * 2-input NaN propagation prefers SNaN over QNaN, and then + * operand A over operand B (see FPProcessNaNs() pseudocode) + */ +static void arm_set_default_fp_behaviours(float_status *s) +{ + set_float_detect_tininess(float_tininess_before_rounding, s); + set_float_2nan_prop_rule(float_2nan_prop_s_ab, s); +} + static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) { /* Reset a single ARMCPRegInfo register */ @@ -202,14 +232,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, ResetType type) { - 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, type); + } memset(env, 0, offsetof(CPUARMState, end_reset_fields)); @@ -221,7 +253,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'; @@ -239,6 +271,10 @@ 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); @@ -285,6 +321,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)) { @@ -307,6 +347,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) @@ -414,7 +458,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. @@ -427,8 +471,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, @@ -485,6 +529,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; @@ -509,35 +561,131 @@ static void arm_cpu_reset(DeviceState *dev) set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); set_default_nan_mode(1, &env->vfp.standard_fp_status); set_default_nan_mode(1, &env->vfp.standard_fp_status_f16); - set_float_detect_tininess(float_tininess_before_rounding, - &env->vfp.fp_status); - set_float_detect_tininess(float_tininess_before_rounding, - &env->vfp.standard_fp_status); - set_float_detect_tininess(float_tininess_before_rounding, - &env->vfp.fp_status_f16); - set_float_detect_tininess(float_tininess_before_rounding, - &env->vfp.standard_fp_status_f16); + arm_set_default_fp_behaviours(&env->vfp.fp_status); + arm_set_default_fp_behaviours(&env->vfp.standard_fp_status); + arm_set_default_fp_behaviours(&env->vfp.fp_status_f16); + arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16); + #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { kvm_arm_reset_vcpu(cpu); } #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; + bool allIntMask = false; /* * Don't take exceptions if they target a lower EL. @@ -548,13 +696,36 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, return false; } + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { + allIntMask = env->pstate & PSTATE_ALLINT || + ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && + (env->pstate & PSTATE_SP)); + } + switch (excp_idx) { + case EXCP_NMI: + pstate_unmasked = !allIntMask; + break; + + case EXCP_VINMI: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VINMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_VFNMI: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFNMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; case EXCP_FIQ: - pstate_unmasked = !(env->daif & PSTATE_F); + pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); break; case EXCP_IRQ: - pstate_unmasked = !(env->daif & PSTATE_I); + pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); break; case EXCP_VFIQ: @@ -562,13 +733,13 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, /* VFIQs are only taken when hypervized. */ return false; } - return !(env->daif & PSTATE_F); + return !(env->daif & PSTATE_F) && (!allIntMask); case EXCP_VIRQ: if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized. */ return false; } - return !(env->daif & PSTATE_I); + return !(env->daif & PSTATE_I) && (!allIntMask); case EXCP_VSERR: if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized. */ @@ -656,7 +827,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; @@ -665,7 +836,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); @@ -674,6 +845,48 @@ static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + (arm_sctlr(env, cur_el) & SCTLR_NMI)) { + if (interrupt_request & CPU_INTERRUPT_NMI) { + excp_idx = EXCP_NMI; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + excp_idx = EXCP_VINMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + excp_idx = EXCP_VFNMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + } else { + /* + * NMI disabled: interrupts with superpriority are handled + * as if they didn't have it + */ + if (interrupt_request & CPU_INTERRUPT_NMI) { + interrupt_request |= CPU_INTERRUPT_HARD; + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + interrupt_request |= CPU_INTERRUPT_VIRQ; + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + interrupt_request |= CPU_INTERRUPT_VFIQ; + } + } + if (interrupt_request & CPU_INTERRUPT_FIQ) { excp_idx = EXCP_FIQ; target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); @@ -725,7 +938,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) { @@ -736,7 +950,8 @@ void arm_cpu_update_virq(ARMCPU *cpu) CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool new_state = (env->cp15.hcr_el2 & HCR_VI) || + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VIRQ); if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { @@ -757,7 +972,8 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); - bool new_state = (env->cp15.hcr_el2 & HCR_VF) || + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && + !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || (env->irq_line_state & CPU_INTERRUPT_VFIQ); if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { @@ -769,6 +985,48 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) } } +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VINMI, which is the logical OR of + * the HCRX_EL2.VINMI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VINMI); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VINMI) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VINMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); + } + } +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && + (arm_hcrx_el2_eff(env) & HCRX_VFNMI); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFNMI) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); + } + } +} + void arm_cpu_update_vserr(ARMCPU *cpu) { /* @@ -798,7 +1056,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) [ARM_CPU_IRQ] = CPU_INTERRUPT_HARD, [ARM_CPU_FIQ] = CPU_INTERRUPT_FIQ, [ARM_CPU_VIRQ] = CPU_INTERRUPT_VIRQ, - [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ + [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ, + [ARM_CPU_NMI] = CPU_INTERRUPT_NMI, + [ARM_CPU_VINMI] = CPU_INTERRUPT_VINMI, }; if (!arm_feature(env, ARM_FEATURE_EL2) && @@ -824,8 +1084,12 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) case ARM_CPU_VFIQ: arm_cpu_update_vfiq(cpu); break; + case ARM_CPU_VINMI: + arm_cpu_update_vinmi(cpu); + break; case ARM_CPU_IRQ: case ARM_CPU_FIQ: + case ARM_CPU_NMI: if (level) { cpu_interrupt(cs, mask[irq]); } else { @@ -877,6 +1141,35 @@ static bool arm_cpu_virtio_is_big_endian(CPUState *cs) return arm_cpu_data_is_big_endian(env); } +#ifdef CONFIG_TCG +bool arm_cpu_exec_halt(CPUState *cs) +{ + bool leave_halt = cpu_has_work(cs); + + if (leave_halt) { + /* We're about to come out of WFI/WFE: disable the WFxT timer */ + ARMCPU *cpu = ARM_CPU(cs); + if (cpu->wfxt_timer) { + timer_del(cpu->wfxt_timer); + } + } + return leave_halt; +} +#endif + +static void arm_wfxt_timer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + + /* + * We expect the CPU to be halted; this will cause arm_cpu_is_work() + * to return true (so we will come out of halt even with no other + * pending interrupt), and the TCG accelerator's cpu_exec_interrupt() + * function auto-clears the CPU_INTERRUPT_EXITTB flag for us. + */ + cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); +} #endif static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) @@ -933,8 +1226,9 @@ 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; @@ -972,6 +1266,10 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) 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; @@ -992,7 +1290,7 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) } if (sve) { - int j, zcr_len = sve_vqm1_for_el(env, el); + int zcr_len = sve_vqm1_for_el(env, el); for (i = 0; i <= FFR_PRED_NUM; i++) { bool eol; @@ -1032,32 +1330,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"); } } } @@ -1068,6 +1358,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 @@ -1168,18 +1476,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); @@ -1200,12 +1512,13 @@ static void arm_cpu_initfn(Object *obj) #else /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { - /* VIRQ and VFIQ are unused with KVM but we add them to maintain - * the same interface as non-KVM CPUs. + /* + * VIRQ, VFIQ, NMI, VINMI are unused with KVM but we add + * them to maintain the same interface as non-KVM CPUs. */ - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 4); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 6); } else { - qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 4); + qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 6); } qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, @@ -1231,9 +1544,12 @@ static void arm_cpu_initfn(Object *obj) } } +/* + * 0 means "unset, use the default value". That default might vary depending + * on the CPU type, and is set in the realize fn. + */ static Property arm_cpu_gt_cntfrq_property = - DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, - NANOSECONDS_PER_SECOND / GTIMER_SCALE); + DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, 0); static Property arm_cpu_reset_cbar_property = DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0); @@ -1255,6 +1571,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); @@ -1321,17 +1640,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)) { @@ -1342,7 +1752,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); @@ -1377,12 +1787,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); + } } } @@ -1441,7 +1873,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 @@ -1483,6 +1915,9 @@ static void arm_cpu_finalizefn(Object *obj) if (cpu->pmu_timer) { timer_free(cpu->pmu_timer); } + if (cpu->wfxt_timer) { + timer_free(cpu->wfxt_timer); + } #endif } @@ -1498,6 +1933,16 @@ 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); @@ -1533,9 +1978,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 */ + tcg_cflags_set(cs, 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 @@ -1550,6 +1998,26 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (!cpu->gt_cntfrq_hz) { + /* + * 0 means "the board didn't set a value, use the default". (We also + * get here for the CONFIG_USER_ONLY case.) + * ARMv8.6 and later CPUs architecturally must use a 1GHz timer; before + * that it was an IMPDEF choice, and QEMU initially picked 62.5MHz, + * which gives a 16ns tick period. + * + * We will use the back-compat value: + * - for QEMU CPU types added before we standardized on 1GHz + * - for versioned machine types with a version of 9.0 or earlier + */ + if (arm_feature(env, ARM_FEATURE_BACKCOMPAT_CNTFRQ) || + cpu->backcompat_cntfrq) { + cpu->gt_cntfrq_hz = GTIMER_BACKCOMPAT_HZ; + } else { + cpu->gt_cntfrq_hz = GTIMER_DEFAULT_HZ; + } + } + #ifndef CONFIG_USER_ONLY /* The NVIC and M-profile CPU are two halves of a single piece of * hardware; trying to use one without the other is a command line @@ -1598,18 +2066,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } { - uint64_t scale; - - if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) { - if (!cpu->gt_cntfrq_hz) { - error_setg(errp, "Invalid CNTFRQ: %"PRId64"Hz", - cpu->gt_cntfrq_hz); - return; - } - scale = gt_cntfrq_period_ns(cpu); - } else { - scale = GTIMER_SCALE; - } + uint64_t scale = gt_cntfrq_period_ns(cpu); cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale, arm_gt_ptimer_cb, cpu); @@ -1636,6 +2093,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) { /* @@ -1647,6 +2115,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; @@ -1787,112 +2268,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 @@ -1900,8 +2314,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) { @@ -1909,7 +2323,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; @@ -1930,6 +2344,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) { @@ -1970,36 +2388,86 @@ 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 run with TCG and do not have tag-memory provided by + * the machine, then reduce MTE support to instructions enabled at EL0. + * This matches Cortex-A710 BROADCASTMTE input being LOW. + */ + if (tcg_enabled() && cpu->tag_memory == NULL) { + cpu->isar.id_aa64pfr1 = + FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 1); + } + + /* + * If MTE is supported by the host, however it should not be + * enabled on the guest (i.e mte=off), clear guest's MTE bits." + */ + if (kvm_enabled() && !cpu->kvm_mte) { + FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0); + } +#endif + } + +#ifndef CONFIG_USER_ONLY + if (tcg_enabled() && cpu_isar_feature(aa64_wfxt, cpu)) { + cpu->wfxt_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + arm_wfxt_timer_cb, cpu); } #endif if (tcg_enabled()) { /* - * Don't report the Statistical Profiling Extension in the ID - * registers, because TCG doesn't implement it yet (not even a - * minimal stub version) and guests will fall over when they - * try to access the non-existent system registers for it. + * 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) && @@ -2026,6 +2494,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)) { @@ -2046,8 +2527,15 @@ 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); + arm_cpu_register_gdb_commands(cpu); init_cpreg_list(cpu); @@ -2142,10 +2630,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; } @@ -2155,18 +2640,20 @@ static Property arm_cpu_properties[] = { mp_affinity, ARM64_AFFINITY_INVALID), DEFINE_PROP_INT32("node-id", ARMCPU, node_id, CPU_UNSET_NUMA_NODE_ID), DEFINE_PROP_INT32("core-count", ARMCPU, core_count, -1), + /* True to default to the backward-compat old CNTFRQ rather than 1Ghz */ + DEFINE_PROP_BOOL("backcompat-cntfrq", ARMCPU, backcompat_cntfrq, false), 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 @@ -2183,7 +2670,7 @@ 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, @@ -2193,8 +2680,9 @@ static const struct TCGCPUOps arm_tcg_ops = { .record_sigsegv = arm_cpu_record_sigsegv, .record_sigbus = arm_cpu_record_sigbus, #else - .tlb_fill = arm_cpu_tlb_fill, + .tlb_fill_align = arm_cpu_tlb_fill_align, .cpu_exec_interrupt = arm_cpu_exec_interrupt, + .cpu_exec_halt = arm_cpu_exec_halt, .do_interrupt = arm_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, .do_unaligned_access = arm_cpu_do_unaligned_access, @@ -2210,15 +2698,19 @@ 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; @@ -2227,10 +2719,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) #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; @@ -2250,18 +2739,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 a9cd7178f8..d86e641280 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -25,10 +25,11 @@ #include "hw/registerfields.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/gdbstub.h" +#include "exec/page-protection.h" #include "qapi/qapi-types-common.h" - -/* ARM processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) +#include "target/arm/multiprocessing.h" +#include "target/arm/gtimer.h" #ifdef TARGET_AARCH64 #define KVM_HAVE_MCE_INJECTION 1 @@ -57,6 +58,10 @@ #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 */ +#define EXCP_NMI 26 +#define EXCP_VINMI 27 +#define EXCP_VFNMI 28 /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 @@ -71,26 +76,14 @@ #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 #define CPU_INTERRUPT_VFIQ CPU_INTERRUPT_TGT_EXT_3 #define CPU_INTERRUPT_VSERR CPU_INTERRUPT_TGT_INT_0 +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_4 +#define CPU_INTERRUPT_VINMI CPU_INTERRUPT_TGT_EXT_0 +#define CPU_INTERRUPT_VFNMI CPU_INTERRUPT_TGT_INT_1 /* The usual mapping for an AArch64 system register to its AArch32 * counterpart is for the 32 bit world to have access to the lower @@ -106,12 +99,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 +106,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 +122,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,18 +144,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 - -#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. @@ -227,6 +200,8 @@ typedef struct CPUARMTBFlags { typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; +typedef struct NVICState NVICState; + typedef struct CPUArchState { /* Regs for current mode. */ uint32_t regs[16]; @@ -309,6 +284,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. */ @@ -478,8 +454,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. */ @@ -494,6 +471,7 @@ 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 */ @@ -527,6 +505,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 { @@ -623,6 +619,13 @@ typedef struct CPUArchState { int vec_len; int vec_stride; + /* + * Floating point status and control registers. Some bits are + * stored separately in other fields or in the float_status below. + */ + uint64_t fpsr; + uint64_t fpcr; + uint32_t xregs[16]; /* Scratch space for aa32 neon expansion. */ @@ -663,8 +666,16 @@ typedef struct CPUArchState { 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. */ @@ -709,11 +720,6 @@ typedef struct CPUArchState { ARMVectorReg zarray[ARM_MAX_VQ * 16]; #endif -#if defined(CONFIG_USER_ONLY) - /* For usermode syscall translation. */ - int eabi; -#endif - struct CPUBreakpoint *cpu_breakpoint[16]; struct CPUWatchpoint *cpu_watchpoint[16]; @@ -745,8 +751,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 */ @@ -757,10 +766,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 */ @@ -821,11 +835,8 @@ typedef struct { * An ARM CPU core. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUARMState env; /* Coprocessor information */ @@ -850,8 +861,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]; @@ -860,6 +873,9 @@ struct ArchCPU { * pmu_op_finish() - it does not need other handling during migration */ QEMUTimer *pmu_timer; + /* Timer used for WFxT timeouts */ + QEMUTimer *wfxt_timer; + /* GPIO outputs for generic timer */ qemu_irq gt_timer_outputs[NUM_GTIMERS]; /* GPIO output for GICv3 maintenance interrupt signal */ @@ -897,6 +913,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 */ @@ -904,8 +922,12 @@ struct ArchCPU { /* CPU has memory protection unit */ bool has_mpu; + /* CPU has MTE enabled in KVM mode */ + bool kvm_mte; /* 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; @@ -924,6 +946,7 @@ struct ArchCPU { */ uint32_t kvm_target; +#ifdef CONFIG_KVM /* KVM init features for this CPU */ uint32_t kvm_init_features[7]; @@ -936,6 +959,7 @@ struct ArchCPU { /* KVM steal time */ OnOffAuto kvm_steal_time; +#endif /* CONFIG_KVM */ /* Uniprocessor system with MP extensions */ bool mp_is_up; @@ -945,6 +969,9 @@ struct ArchCPU { */ bool host_cpu_probe_failed; + /* QOM property to indicate we should use the back-compat CNTFRQ default */ + bool backcompat_cntfrq; + /* Specify the number of cores in this CPU cluster. Used for the L2CTLR * register. */ @@ -994,11 +1021,13 @@ struct ArchCPU { 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; uint64_t id_aa64mmfr1; uint64_t id_aa64mmfr2; + uint64_t id_aa64mmfr3; uint64_t id_aa64dfr0; uint64_t id_aa64dfr1; uint64_t id_aa64zfr0; @@ -1024,6 +1053,7 @@ struct ArchCPU { uint64_t reset_cbar; uint32_t reset_auxcr; bool reset_hivecs; + uint8_t reset_l0gptsz; /* * Intermediate values used during property parsing. @@ -1031,10 +1061,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) */ @@ -1074,50 +1108,107 @@ struct ArchCPU { 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, DumpState *s); int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, 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); 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 arm_reset_sve_state(CPUARMState *env); +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. @@ -1226,7 +1317,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 */ @@ -1276,6 +1367,8 @@ 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_CMOW (1ULL << 32) /* FEAT_CMOW */ +#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 */ @@ -1299,73 +1392,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_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 CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -1387,6 +1413,8 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define CPSR_N (1U << 31) #define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V) #define CPSR_AIF (CPSR_A | CPSR_I | CPSR_F) +#define ISR_FS (1U << 9) +#define ISR_IS (1U << 10) #define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7) #define CACHED_CPSR_BITS (CPSR_T | CPSR_AIF | CPSR_GE | CPSR_IT | CPSR_Q \ @@ -1412,41 +1440,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) - -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) - /* Bit definitions for ARMv8 SPSR (PSTATE) format. * Only these are valid when in AArch64 mode; in * AArch32 mode SPSRs are basically CPSR-format. @@ -1460,6 +1453,7 @@ FIELD(VTCR, SL2, 33, 1) #define PSTATE_D (1U << 9) #define PSTATE_BTYPE (3U << 10) #define PSTATE_SSBS (1U << 12) +#define PSTATE_ALLINT (1U << 13) #define PSTATE_IL (1U << 20) #define PSTATE_SS (1U << 21) #define PSTATE_PAN (1U << 22) @@ -1631,7 +1625,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) @@ -1640,7 +1634,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) @@ -1654,21 +1648,6 @@ 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 (1ULL << 0) #define SCR_IRQ (1ULL << 1) #define SCR_FIQ (1ULL << 2) @@ -1705,69 +1684,106 @@ 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); void vfp_set_fpscr(CPUARMState *env, uint32_t val); -/* FPCR, Floating Point Control Register - * FPSR, Floating Poiht Status Register +/* + * FPCR, Floating Point Control Register + * FPSR, Floating Point Status Register * - * For A64 the FPSCR is split into two logically distinct registers, - * FPCR and FPSR. However since they still use non-overlapping bits - * we store the underlying state in fpscr and just mask on read/write. + * For A64 floating point control and status bits are stored in + * two logically distinct registers, FPCR and FPSR. We store these + * in QEMU in vfp.fpcr and vfp.fpsr. + * For A32 there was only one register, FPSCR. The bits are arranged + * such that FPSCR bits map to FPCR or FPSR bits in the same bit positions, + * so we can use appropriate masking to handle FPSCR reads and writes. + * Note that the FPCR has some bits which are not visible in the + * AArch32 view (for FEAT_AFP). Writing the FPSCR leaves these unchanged. */ -#define FPSR_MASK 0xf800009f -#define FPCR_MASK 0x07ff9f00 +/* FPCR bits */ #define FPCR_IOE (1 << 8) /* Invalid Operation exception trap enable */ #define FPCR_DZE (1 << 9) /* Divide by Zero exception trap enable */ #define FPCR_OFE (1 << 10) /* Overflow exception trap enable */ #define FPCR_UFE (1 << 11) /* Underflow exception trap enable */ #define FPCR_IXE (1 << 12) /* Inexact exception trap enable */ +#define FPCR_EBF (1 << 13) /* Extended BFloat16 behaviors */ #define FPCR_IDE (1 << 15) /* Input Denormal exception trap enable */ +#define FPCR_LEN_MASK (7 << 16) /* LEN, A-profile only */ #define FPCR_FZ16 (1 << 19) /* ARMv8.2+, FP16 flush-to-zero */ +#define FPCR_STRIDE_MASK (3 << 20) /* Stride */ #define FPCR_RMODE_MASK (3 << 22) /* Rounding mode */ #define FPCR_FZ (1 << 24) /* Flush-to-zero enable bit */ #define FPCR_DN (1 << 25) /* Default NaN enable bit */ #define FPCR_AHP (1 << 26) /* Alternative half-precision */ -#define FPCR_QC (1 << 27) /* Cumulative saturation bit */ -#define FPCR_V (1 << 28) /* FP overflow flag */ -#define FPCR_C (1 << 29) /* FP carry flag */ -#define FPCR_Z (1 << 30) /* FP zero flag */ -#define FPCR_N (1 << 31) /* FP negative flag */ #define FPCR_LTPSIZE_SHIFT 16 /* LTPSIZE, M-profile only */ #define FPCR_LTPSIZE_MASK (7 << FPCR_LTPSIZE_SHIFT) #define FPCR_LTPSIZE_LENGTH 3 -#define FPCR_NZCV_MASK (FPCR_N | FPCR_Z | FPCR_C | FPCR_V) -#define FPCR_NZCVQC_MASK (FPCR_NZCV_MASK | FPCR_QC) +/* Cumulative exception trap enable bits */ +#define FPCR_EEXC_MASK (FPCR_IOE | FPCR_DZE | FPCR_OFE | FPCR_UFE | FPCR_IXE | FPCR_IDE) -static inline uint32_t vfp_get_fpsr(CPUARMState *env) -{ - return vfp_get_fpscr(env) & FPSR_MASK; -} +/* FPSR bits */ +#define FPSR_IOC (1 << 0) /* Invalid Operation cumulative exception */ +#define FPSR_DZC (1 << 1) /* Divide by Zero cumulative exception */ +#define FPSR_OFC (1 << 2) /* Overflow cumulative exception */ +#define FPSR_UFC (1 << 3) /* Underflow cumulative exception */ +#define FPSR_IXC (1 << 4) /* Inexact cumulative exception */ +#define FPSR_IDC (1 << 7) /* Input Denormal cumulative exception */ +#define FPSR_QC (1 << 27) /* Cumulative saturation bit */ +#define FPSR_V (1 << 28) /* FP overflow flag */ +#define FPSR_C (1 << 29) /* FP carry flag */ +#define FPSR_Z (1 << 30) /* FP zero flag */ +#define FPSR_N (1 << 31) /* FP negative flag */ -static inline void vfp_set_fpsr(CPUARMState *env, uint32_t val) -{ - uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPSR_MASK) | (val & FPSR_MASK); - vfp_set_fpscr(env, new_fpscr); -} +/* Cumulative exception status bits */ +#define FPSR_CEXC_MASK (FPSR_IOC | FPSR_DZC | FPSR_OFC | FPSR_UFC | FPSR_IXC | FPSR_IDC) -static inline uint32_t vfp_get_fpcr(CPUARMState *env) -{ - return vfp_get_fpscr(env) & FPCR_MASK; -} +#define FPSR_NZCV_MASK (FPSR_N | FPSR_Z | FPSR_C | FPSR_V) +#define FPSR_NZCVQC_MASK (FPSR_NZCV_MASK | FPSR_QC) -static inline void vfp_set_fpcr(CPUARMState *env, uint32_t val) -{ - uint32_t new_fpscr = (vfp_get_fpscr(env) & ~FPCR_MASK) | (val & FPCR_MASK); - vfp_set_fpscr(env, new_fpscr); -} +/* A32 FPSCR bits which architecturally map to FPSR bits */ +#define FPSCR_FPSR_MASK (FPSR_NZCVQC_MASK | FPSR_CEXC_MASK) +/* A32 FPSCR bits which architecturally map to FPCR bits */ +#define FPSCR_FPCR_MASK (FPCR_EEXC_MASK | FPCR_LEN_MASK | FPCR_FZ16 | \ + FPCR_STRIDE_MASK | FPCR_RMODE_MASK | \ + FPCR_FZ | FPCR_DN | FPCR_AHP) +/* These masks don't overlap: each bit lives in only one place */ +QEMU_BUILD_BUG_ON(FPSCR_FPSR_MASK & FPSCR_FPCR_MASK); + +/** + * vfp_get_fpsr: read the AArch64 FPSR + * @env: CPU context + * + * Return the current AArch64 FPSR value + */ +uint32_t vfp_get_fpsr(CPUARMState *env); + +/** + * vfp_get_fpcr: read the AArch64 FPCR + * @env: CPU context + * + * Return the current AArch64 FPCR value + */ +uint32_t vfp_get_fpcr(CPUARMState *env); + +/** + * vfp_set_fpsr: write the AArch64 FPSR + * @env: CPU context + * @value: new value + */ +void vfp_set_fpsr(CPUARMState *env, uint32_t value); + +/** + * vfp_set_fpcr: write the AArch64 FPCR + * @env: CPU context + * @value: new value + */ +void vfp_set_fpcr(CPUARMState *env, uint32_t value); enum arm_cpu_mode { ARM_CPU_MODE_USR = 0x10, @@ -2123,6 +2139,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) @@ -2157,6 +2174,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) @@ -2171,6 +2195,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) @@ -2183,6 +2208,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) @@ -2214,6 +2245,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) @@ -2231,11 +2263,29 @@ FIELD(ID_AA64MMFR2, BBM, 52, 4) FIELD(ID_AA64MMFR2, EVT, 56, 4) FIELD(ID_AA64MMFR2, E0PD, 60, 4) +FIELD(ID_AA64MMFR3, TCRX, 0, 4) +FIELD(ID_AA64MMFR3, SCTLRX, 4, 4) +FIELD(ID_AA64MMFR3, S1PIE, 8, 4) +FIELD(ID_AA64MMFR3, S2PIE, 12, 4) +FIELD(ID_AA64MMFR3, S1POE, 16, 4) +FIELD(ID_AA64MMFR3, S2POE, 20, 4) +FIELD(ID_AA64MMFR3, AIE, 24, 4) +FIELD(ID_AA64MMFR3, MEC, 28, 4) +FIELD(ID_AA64MMFR3, D128, 32, 4) +FIELD(ID_AA64MMFR3, D128_2, 36, 4) +FIELD(ID_AA64MMFR3, SNERR, 40, 4) +FIELD(ID_AA64MMFR3, ANERR, 44, 4) +FIELD(ID_AA64MMFR3, SDERR, 52, 4) +FIELD(ID_AA64MMFR3, ADERR, 56, 4) +FIELD(ID_AA64MMFR3, SPEC_FPACC, 60, 4) + FIELD(ID_AA64DFR0, DEBUGVER, 0, 4) FIELD(ID_AA64DFR0, TRACEVER, 4, 4) FIELD(ID_AA64DFR0, PMUVER, 8, 4) FIELD(ID_AA64DFR0, BRPS, 12, 4) +FIELD(ID_AA64DFR0, 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) @@ -2243,12 +2293,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) @@ -2256,9 +2308,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) @@ -2292,6 +2348,8 @@ FIELD(DBGDEVID, DOUBLELOCK, 20, 4) FIELD(DBGDEVID, AUXREGS, 24, 4) FIELD(DBGDEVID, CIDMASK, 28, 4) +FIELD(DBGDEVID1, PCSROFFSET, 0, 4) + FIELD(MVFR0, SIMDREG, 0, 4) FIELD(MVFR0, FPSP, 4, 4) FIELD(MVFR0, FPDP, 8, 4) @@ -2315,6 +2373,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 @@ -2360,6 +2431,14 @@ enum arm_features { ARM_FEATURE_M_SECURITY, /* M profile Security Extension */ ARM_FEATURE_M_MAIN, /* M profile Main Extension */ ARM_FEATURE_V8_1M, /* M profile extras only in v8.1M and later */ + /* + * ARM_FEATURE_BACKCOMPAT_CNTFRQ makes the CPU default cntfrq be 62.5MHz + * if the board doesn't set a value, instead of 1GHz. It is for backwards + * compatibility and used only with CPU definitions that were already + * in QEMU before we changed the default. It should not be set on any + * CPU types added in future. + */ + ARM_FEATURE_BACKCOMPAT_CNTFRQ, /* 62.5MHz timer default */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -2369,28 +2448,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 */ @@ -2404,45 +2514,65 @@ 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_feature(env, ARM_FEATURE_M)) { - return env->v7m.secure; - } - 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, bool secure) +static inline bool arm_is_el2_enabled_secstate(CPUARMState *env, + ARMSecuritySpace space) { + assert(space != ARMSS_Root); return arm_feature(env, ARM_FEATURE_EL2) - && (!secure || (env->cp15.scr_el3 & SCR_EEL2)); + && (space != ARMSS_Secure || (env->cp15.scr_el3 & SCR_EEL2)); } static inline bool arm_is_el2_enabled(CPUARMState *env) { - return arm_is_el2_enabled_secstate(env, arm_is_secure_below_el3(env)); + 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, bool secure) +static inline bool arm_is_el2_enabled_secstate(CPUARMState *env, + ARMSecuritySpace space) { return false; } @@ -2459,7 +2589,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, bool secure); +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); @@ -2496,7 +2626,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 @@ -2539,224 +2669,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) { @@ -2850,14 +2765,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: @@ -2865,14 +2776,19 @@ bool write_cpustate_to_list(ARMCPU *cpu, bool kvm_sync); * + NonSecure EL1 & 0 stage 2 * + NonSecure EL2 * + NonSecure EL2 & 0 (ARMv8.1-VHE) - * + Secure EL1 & 0 - * + Secure EL3 + * + Secure EL1 & 0 stage 1 + * + Secure EL1 & 0 stage 2 (FEAT_SEL2) + * + Secure EL2 (FEAT_SEL2) + * + Secure EL2 & 0 (FEAT_SEL2) + * + Realm EL1 & 0 stage 1 (FEAT_RME) + * + Realm EL1 & 0 stage 2 (FEAT_RME) + * + Realm EL2 (FEAT_RME) + * + EL3 * If EL3 is 32-bit: * + NonSecure PL1 & 0 stage 1 * + NonSecure PL1 & 0 stage 2 * + NonSecure PL2 - * + Secure PL0 - * + Secure PL1 + * + Secure PL1 & 0 * (reminder: for 32 bit EL3, Secure PL1 is *EL3*, not EL1.) * * For QEMU, an mmu_idx is not quite the same as a translation regime because: @@ -2898,29 +2814,34 @@ 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, + * 7. we fold together most 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. + * 8. we cannot fold together Stage 2 Secure and Stage 2 NonSecure, + * because both are in use simultaneously for Secure EL2. * * This gives us the following list of cases: * - * 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 EL1&0 stage 1+2 (aka NS PL0 PL1&0 stage 1+2) + * EL1 EL1&0 stage 1+2 (aka NS PL1 PL1&0 stage 1+2) + * EL1 EL1&0 stage 1+2 +PAN (aka NS PL1 P1&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) + * EL3 (aka AArch32 S PL1 PL1&0) + * AArch32 S PL0 PL1&0 (we call this EL30_0) + * AArch32 S PL1 PL1&0 +PAN (we call this EL30_3_PAN) + * Stage2 Secure + * Stage2 NonSecure + * plus one TLB per Physical address space: S, NS, Realm, Root * - * for a total of 12 different mmu_idx. + * for a total of 16 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 EL0 and EL1 (and - * EL2 if we ever model a Cortex-R52). + * EL2 for cores like the Cortex-R52). * * M profile CPUs are rather different as they do not have a true MMU. * They have the following different MMU indexes: @@ -2980,10 +2901,8 @@ typedef enum ARMMMUIdx { ARMMMUIdx_E20_2_PAN = 5 | ARM_MMU_IDX_A, ARMMMUIdx_E2 = 6 | ARM_MMU_IDX_A, ARMMMUIdx_E3 = 7 | ARM_MMU_IDX_A, - - /* TLBs with 1-1 mapping to the physical address spaces. */ - ARMMMUIdx_Phys_NS = 8 | ARM_MMU_IDX_A, - ARMMMUIdx_Phys_S = 9 | ARM_MMU_IDX_A, + ARMMMUIdx_E30_0 = 8 | ARM_MMU_IDX_A, + ARMMMUIdx_E30_3_PAN = 9 | ARM_MMU_IDX_A, /* * Used for second stage of an S12 page table walk, or for descriptor @@ -2991,8 +2910,14 @@ typedef enum ARMMMUIdx { * 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 = 10 | ARM_MMU_IDX_A, - ARMMMUIdx_Stage2_S = 11 | ARM_MMU_IDX_A, + ARMMMUIdx_Stage2_S = 10 | ARM_MMU_IDX_A, + ARMMMUIdx_Stage2 = 11 | ARM_MMU_IDX_A, + + /* TLBs with 1-1 mapping to the physical address spaces. */ + ARMMMUIdx_Phys_S = 12 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_NS = 13 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Root = 14 | ARM_MMU_IDX_A, + ARMMMUIdx_Phys_Realm = 15 | ARM_MMU_IDX_A, /* * These are not allocated TLBs and are used only for AT system @@ -3031,6 +2956,8 @@ typedef enum ARMMMUIdxBit { TO_CORE_BIT(E20_2), TO_CORE_BIT(E20_2_PAN), TO_CORE_BIT(E3), + TO_CORE_BIT(E30_0), + TO_CORE_BIT(E30_3_PAN), TO_CORE_BIT(Stage2), TO_CORE_BIT(Stage2_S), @@ -3056,6 +2983,23 @@ typedef enum ARMASIdx { ARMASIdx_TagS = 3, } ARMASIdx; +static inline ARMMMUIdx arm_space_to_phys(ARMSecuritySpace space) +{ + /* 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); + + return ARMMMUIdx_Phys_S + space; +} + +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) { /* If all the CLIDR.Ctypem bits are 0 there are no caches, and @@ -3156,6 +3100,8 @@ FIELD(TBFLAG_ANY, FPEXC_EL, 8, 2) /* Memory operations require alignment: SCTLR_ELx.A or CCR.UNALIGN_TRP */ FIELD(TBFLAG_ANY, ALIGN_MEM, 10, 1) FIELD(TBFLAG_ANY, PSTATE__IL, 11, 1) +FIELD(TBFLAG_ANY, FGT_ACTIVE, 12, 1) +FIELD(TBFLAG_ANY, FGT_SVC, 13, 1) /* * Bit usage when in AArch32 state, both A- and M-profile. @@ -3230,14 +3176,26 @@ 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) \ @@ -3246,24 +3204,11 @@ FIELD(TBFLAG_A64, SME_TRAP_NONSTREAMING, 28, 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 @@ -3293,11 +3238,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 @@ -3309,16 +3250,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, @@ -3435,8 +3372,8 @@ extern const uint64_t pred_esz_masks[5]; */ static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) { - ARMCPU *cpu = ARM_CPU(cs); - if (cpu->env.tagged_addr_enable) { + CPUARMState *env = cpu_env(cs); + if (env->tagged_addr_enable) { /* * TBI is enabled for userspace but not kernelspace addresses. * Only clear the tag if bit 55 is clear. @@ -3447,879 +3384,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_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_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_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_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_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_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_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_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_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_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_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); -} - -static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) -{ - return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; -} - -/* - * Feature tests for "does this exist in either 32-bit or 64-bit?" - */ -static inline bool isar_feature_any_fp16(const ARMISARegisters *id) -{ - return isar_feature_aa64_fp16(id) || isar_feature_aa32_fp16_arith(id); -} - -static inline bool isar_feature_any_predinv(const ARMISARegisters *id) -{ - return isar_feature_aa64_predinv(id) || isar_feature_aa32_predinv(id); -} - -static inline bool isar_feature_any_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); -} - -/* - * 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 3d74f134f5..458d1cee01 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -21,100 +21,20 @@ #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 "qemu/units.h" #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_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); -} +#include "cpu-features.h" +#include "cpregs.h" void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { @@ -147,7 +67,7 @@ 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)); + cpu->sve_vq.supported = kvm_arm_sve_get_vls(cpu); vq_supported = cpu->sve_vq.supported; } else { assert(!cpu_isar_feature(aa64_sve, cpu)); @@ -177,7 +97,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; @@ -190,7 +110,11 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * No explicit bits enabled, and no implicit bits from sve-max-vq. */ if (!cpu_isar_feature(aa64_sve, cpu)) { - /* SVE is disabled and so are all vector lengths. Good. */ + /* + * SVE is disabled and so are all vector lengths. Good. + * Disable all SVE extensions as well. + */ + cpu->isar.id_aa64zfr0 = 0; return; } @@ -204,10 +128,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", @@ -315,47 +239,6 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) 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; -} - /* * Note that cpu_arm_{get,set}_vq cannot use the simpler * object_property_add_bool interface because they make use of the @@ -546,7 +429,7 @@ static void cpu_arm_get_default_vec_len(Object *obj, Visitor *v, } #endif -static void aarch64_add_sve_properties(Object *obj) +void aarch64_add_sve_properties(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); uint32_t vq; @@ -555,7 +438,7 @@ static void aarch64_add_sve_properties(Object *obj) for (vq = 1; vq <= ARM_MAX_VQ; ++vq) { char name[8]; - sprintf(name, "sve%d", vq * 128); + snprintf(name, sizeof(name), "sve%d", vq * 128); object_property_add(obj, name, "bool", cpu_arm_get_vq, cpu_arm_set_vq, NULL, &cpu->sve_vq); } @@ -569,7 +452,7 @@ static void aarch64_add_sve_properties(Object *obj) #endif } -static void aarch64_add_sme_properties(Object *obj) +void aarch64_add_sme_properties(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); uint32_t vq; @@ -580,7 +463,7 @@ static void aarch64_add_sme_properties(Object *obj) for (vq = 1; vq <= ARM_MAX_VQ; vq <<= 1) { char name[8]; - sprintf(name, "sme%d", vq * 128); + snprintf(name, sizeof(name), "sme%d", vq * 128); object_property_add(obj, name, "bool", cpu_arm_get_vq, cpu_arm_set_vq, NULL, &cpu->sme_vq); } @@ -596,45 +479,82 @@ static void aarch64_add_sme_properties(Object *obj) 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); -static void aarch64_add_pauth_properties(Object *obj) +void aarch64_add_pauth_properties(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -652,12 +572,10 @@ static 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; @@ -686,6 +604,7 @@ static void aarch64_a57_initfn(Object *obj) 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_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -724,9 +643,12 @@ static void aarch64_a57_initfn(Object *obj) 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 */ + /* 32KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); + /* 48KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 3, 64, 48 * KiB, 2); + /* 2048KB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 16, 64, 2 * MiB, 7); cpu->dcz_blocksize = 4; /* 64 bytes */ cpu->gic_num_lrs = 4; cpu->gic_vpribits = 5; @@ -743,6 +665,7 @@ static void aarch64_a53_initfn(Object *obj) 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_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -750,7 +673,7 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A53; cpu->midr = 0x410fd034; - cpu->revidr = 0x00000000; + cpu->revidr = 0x00000100; cpu->reset_fpsid = 0x41034070; cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x12111111; @@ -781,9 +704,12 @@ static void aarch64_a53_initfn(Object *obj) 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 */ + /* 32KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); + /* 32KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 1, 64, 32 * KiB, 2); + /* 1024KB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 16, 64, 1 * MiB, 7); cpu->dcz_blocksize = 4; /* 64 bytes */ cpu->gic_num_lrs = 4; cpu->gic_vpribits = 5; @@ -792,248 +718,6 @@ static void aarch64_a53_initfn(Object *obj) 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.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 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; -} - static void aarch64_host_initfn(Object *obj) { #if defined(CONFIG_KVM) @@ -1052,201 +736,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, 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 */ - 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, 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, ETS, 1); /* FEAT_ETS */ - 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 */ - 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 */ - 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->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); - qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); + if (tcg_enabled()) { + aarch64_max_tcg_initfn(obj); + } } static const ARMCPUInfo aarch64_cpus[] = { - { .name = "cortex-a35", .initfn = aarch64_a35_initfn }, { .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 }, @@ -1285,9 +795,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) @@ -1296,7 +806,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; @@ -1326,9 +835,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, }; @@ -1341,10 +848,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/cpu_tcg.c b/target/arm/cpu_tcg.c deleted file mode 100644 index 9a2cef7d05..0000000000 --- a/target/arm/cpu_tcg.c +++ /dev/null @@ -1,1207 +0,0 @@ -/* - * QEMU ARM TCG 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" -#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) -#include "hw/boards.h" -#endif -#include "cpregs.h" - - -/* Share AArch32 -cpu max features with AArch64. */ -void aa32_max_features(ARMCPU *cpu) -{ - uint32_t t; - - /* Add additional features supported by QEMU */ - t = cpu->isar.id_isar5; - t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ - t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ - t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ - t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); - t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ - t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ - cpu->isar.id_isar5 = t; - - t = cpu->isar.id_isar6; - t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ - t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ - t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ - t = FIELD_DP32(t, ID_ISAR6, SB, 1); /* FEAT_SB */ - t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ - t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ - cpu->isar.id_isar6 = t; - - t = cpu->isar.mvfr1; - t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ - t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* FEAT_FP16 */ - cpu->isar.mvfr1 = t; - - t = cpu->isar.mvfr2; - t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */ - t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ - cpu->isar.mvfr2 = t; - - t = cpu->isar.id_mmfr3; - t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ - 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, AC2, 1); /* ACTLR2, HACTLR2 */ - t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ - 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 */ - t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ - cpu->isar.id_pfr0 = t; - - t = cpu->isar.id_pfr2; - t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ - t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ - cpu->isar.id_pfr2 = t; - - 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, 6); /* FEAT_PMUv3p5 */ - cpu->isar.id_dfr0 = 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); - - cpu->dtb_compatible = "arm,arm926"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); - cpu->midr = 0x41069265; - cpu->reset_fpsid = 0x41011090; - cpu->ctr = 0x1dd20d2; - cpu->reset_sctlr = 0x00090078; - - /* - * ARMv5 does not have the ID_ISAR registers, but we can still - * set the field to indicate Jazelle support within QEMU. - */ - cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); - /* - * Similarly, we need to set MVFR0 fields to enable vfp and short vector - * support even though ARMv5 doesn't have this register. - */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); -} - -static void arm946_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,arm946"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_PMSA); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - cpu->midr = 0x41059461; - cpu->ctr = 0x0f004006; - cpu->reset_sctlr = 0x00000078; -} - -static void arm1026_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,arm1026"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_AUXCR); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); - cpu->midr = 0x4106a262; - cpu->reset_fpsid = 0x410110a0; - cpu->ctr = 0x1dd20d2; - cpu->reset_sctlr = 0x00090078; - cpu->reset_auxcr = 1; - - /* - * ARMv5 does not have the ID_ISAR registers, but we can still - * set the field to indicate Jazelle support within QEMU. - */ - cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); - /* - * Similarly, we need to set MVFR0 fields to enable vfp and short vector - * support even though ARMv5 doesn't have this register. - */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); - - { - /* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */ - ARMCPRegInfo ifar = { - .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.ifar_ns), - .resetvalue = 0 - }; - define_one_arm_cp_reg(cpu, &ifar); - } -} - -static void arm1136_r2_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - /* - * What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an - * older core than plain "arm1136". In particular this does not - * have the v6K features. - * These ID register values are correct for 1136 but may be wrong - * for 1136_r2 (in particular r0p2 does not actually implement most - * of the ID registers). - */ - - cpu->dtb_compatible = "arm,arm1136"; - set_feature(&cpu->env, ARM_FEATURE_V6); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); - set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); - cpu->midr = 0x4107b362; - cpu->reset_fpsid = 0x410120b4; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; - cpu->ctr = 0x1dd20d2; - cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; - cpu->isar.id_dfr0 = 0x2; - cpu->id_afr0 = 0x3; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222110; - cpu->isar.id_isar0 = 0x00140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231111; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; - cpu->reset_auxcr = 7; -} - -static void arm1136_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,arm1136"; - set_feature(&cpu->env, ARM_FEATURE_V6K); - set_feature(&cpu->env, ARM_FEATURE_V6); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); - set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); - cpu->midr = 0x4117b363; - cpu->reset_fpsid = 0x410120b4; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; - cpu->ctr = 0x1dd20d2; - cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; - cpu->isar.id_dfr0 = 0x2; - cpu->id_afr0 = 0x3; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222110; - cpu->isar.id_isar0 = 0x00140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231111; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; - cpu->reset_auxcr = 7; -} - -static void arm1176_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,arm1176"; - set_feature(&cpu->env, ARM_FEATURE_V6K); - set_feature(&cpu->env, ARM_FEATURE_VAPA); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); - set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); - set_feature(&cpu->env, ARM_FEATURE_EL3); - cpu->midr = 0x410fb767; - cpu->reset_fpsid = 0x410120b5; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; - cpu->ctr = 0x1dd20d2; - cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x11; - cpu->isar.id_dfr0 = 0x33; - cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222100; - cpu->isar.id_isar0 = 0x0140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231121; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x01141; - cpu->reset_auxcr = 7; -} - -static void arm11mpcore_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,arm11mpcore"; - set_feature(&cpu->env, ARM_FEATURE_V6K); - set_feature(&cpu->env, ARM_FEATURE_VAPA); - set_feature(&cpu->env, ARM_FEATURE_MPIDR); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - cpu->midr = 0x410fb022; - cpu->reset_fpsid = 0x410120b4; - cpu->isar.mvfr0 = 0x11111111; - cpu->isar.mvfr1 = 0x00000000; - cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; - cpu->isar.id_dfr0 = 0; - cpu->id_afr0 = 0x2; - cpu->isar.id_mmfr0 = 0x01100103; - cpu->isar.id_mmfr1 = 0x10020302; - cpu->isar.id_mmfr2 = 0x01222000; - cpu->isar.id_isar0 = 0x00100011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11221011; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; - cpu->reset_auxcr = 1; -} - -static const ARMCPRegInfo cortexa8_cp_reginfo[] = { - { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, - { .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, -}; - -static void cortex_a8_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a8"; - set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - set_feature(&cpu->env, ARM_FEATURE_EL3); - cpu->midr = 0x410fc080; - cpu->reset_fpsid = 0x410330c0; - cpu->isar.mvfr0 = 0x11110222; - cpu->isar.mvfr1 = 0x00011111; - cpu->ctr = 0x82048004; - cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x1031; - cpu->isar.id_pfr1 = 0x11; - cpu->isar.id_dfr0 = 0x400; - cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x31100003; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01202000; - cpu->isar.id_mmfr3 = 0x11; - cpu->isar.id_isar0 = 0x00101111; - cpu->isar.id_isar1 = 0x12112111; - cpu->isar.id_isar2 = 0x21232031; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x00111142; - cpu->isar.dbgdidr = 0x15141000; - cpu->clidr = (1 << 27) | (2 << 24) | 3; - cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ - cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ - cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */ - cpu->reset_auxcr = 2; - cpu->isar.reset_pmcr_el0 = 0x41002000; - define_arm_cp_regs(cpu, cortexa8_cp_reginfo); -} - -static const ARMCPRegInfo cortexa9_cp_reginfo[] = { - /* - * power_control should be set to maximum latency. Again, - * default to 0 and set by private hook - */ - { .name = "A9_PWRCTL", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.c15_power_control) }, - { .name = "A9_DIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.c15_diagnostic) }, - { .name = "A9_PWRDIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .resetvalue = 0, - .fieldoffset = offsetof(CPUARMState, cp15.c15_power_diagnostic) }, - { .name = "NEONBUSY", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, - .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, - /* TLB lockdown control */ - { .name = "TLB_LOCKR", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 2, - .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, - { .name = "TLB_LOCKW", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 4, - .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, - { .name = "TLB_VA", .cp = 15, .crn = 15, .crm = 5, .opc1 = 5, .opc2 = 2, - .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, - { .name = "TLB_PA", .cp = 15, .crn = 15, .crm = 6, .opc1 = 5, .opc2 = 2, - .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, - { .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2, - .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, -}; - -static void cortex_a9_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a9"; - set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_EL3); - /* - * Note that A9 supports the MP extensions even for - * A9UP and single-core A9MP (which are both different - * and valid configurations; we don't model A9UP). - */ - set_feature(&cpu->env, ARM_FEATURE_V7MP); - set_feature(&cpu->env, ARM_FEATURE_CBAR); - cpu->midr = 0x410fc090; - cpu->reset_fpsid = 0x41033090; - cpu->isar.mvfr0 = 0x11110222; - cpu->isar.mvfr1 = 0x01111111; - cpu->ctr = 0x80038003; - cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x1031; - cpu->isar.id_pfr1 = 0x11; - cpu->isar.id_dfr0 = 0x000; - cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x00100103; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01230000; - cpu->isar.id_mmfr3 = 0x00002111; - cpu->isar.id_isar0 = 0x00101111; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x00111142; - cpu->isar.dbgdidr = 0x35141000; - cpu->clidr = (1 << 27) | (1 << 24) | 3; - cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ - cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */ - cpu->isar.reset_pmcr_el0 = 0x41093000; - define_arm_cp_regs(cpu, cortexa9_cp_reginfo); -} - -#ifndef CONFIG_USER_ONLY -static uint64_t a15_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - - /* - * Linux wants the number of processors from here. - * Might as well set the interrupt-controller bit too. - */ - return ((ms->smp.cpus - 1) << 24) | (1 << 23); -} -#endif - -static const ARMCPRegInfo cortexa15_cp_reginfo[] = { -#ifndef CONFIG_USER_ONLY - { .name = "L2CTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, - .access = PL1_RW, .resetvalue = 0, .readfn = a15_l2ctlr_read, - .writefn = arm_cp_write_ignore, }, -#endif - { .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3, - .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, -}; - -static void cortex_a7_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a7"; - set_feature(&cpu->env, ARM_FEATURE_V7VE); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - 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_A7; - cpu->midr = 0x410fc075; - cpu->reset_fpsid = 0x41023075; - cpu->isar.mvfr0 = 0x10110222; - cpu->isar.mvfr1 = 0x11111111; - cpu->ctr = 0x84448003; - cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x00001131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x02010555; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01240000; - cpu->isar.id_mmfr3 = 0x02102211; - /* - * a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but - * table 4-41 gives 0x02101110, which includes the arm div insns. - */ - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x10011142; - cpu->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 */ - cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ - cpu->isar.reset_pmcr_el0 = 0x41072000; - define_arm_cp_regs(cpu, cortexa15_cp_reginfo); /* Same as A15 */ -} - -static void cortex_a15_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "arm,cortex-a15"; - set_feature(&cpu->env, ARM_FEATURE_V7VE); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - 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_A15; - /* 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; - cpu->ctr = 0x8444c004; - cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x00001131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_dfr0 = 0x02010555; - cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01240000; - cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x10011142; - cpu->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 */ - cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ - cpu->isar.reset_pmcr_el0 = 0x410F3000; - 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, - .access = PL1_RW, .type = ARM_CP_CONST }, - { .name = "BTCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, - .access = PL1_RW, .type = ARM_CP_CONST }, - { .name = "DCACHE_INVAL", .cp = 15, .opc1 = 0, .crn = 15, .crm = 5, - .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP }, -}; - -static void cortex_r5_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_V7MP); - set_feature(&cpu->env, ARM_FEATURE_PMSA); - set_feature(&cpu->env, ARM_FEATURE_PMU); - cpu->midr = 0x411fc153; /* r1p3 */ - cpu->isar.id_pfr0 = 0x0131; - cpu->isar.id_pfr1 = 0x001; - cpu->isar.id_dfr0 = 0x010400; - cpu->id_afr0 = 0x0; - cpu->isar.id_mmfr0 = 0x0210030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01200000; - cpu->isar.id_mmfr3 = 0x0211; - cpu->isar.id_isar0 = 0x02101111; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232141; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x0010142; - cpu->isar.id_isar5 = 0x0; - cpu->isar.id_isar6 = 0x0; - cpu->mp_is_up = true; - cpu->pmsav7_dregion = 16; - cpu->isar.reset_pmcr_el0 = 0x41151800; - define_arm_cp_regs(cpu, cortexr5_cp_reginfo); -} - -static void cortex_r5f_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cortex_r5_initfn(obj); - cpu->isar.mvfr0 = 0x10110221; - cpu->isar.mvfr1 = 0x00000011; -} - -static void ti925t_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_V4T); - set_feature(&cpu->env, ARM_FEATURE_OMAPCP); - cpu->midr = ARM_CPUID_TI925T; - cpu->ctr = 0x5109149; - cpu->reset_sctlr = 0x00000070; -} - -static void sa1100_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "intel,sa1100"; - set_feature(&cpu->env, ARM_FEATURE_STRONGARM); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - cpu->midr = 0x4401A11B; - cpu->reset_sctlr = 0x00000070; -} - -static void sa1110_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - set_feature(&cpu->env, ARM_FEATURE_STRONGARM); - set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); - cpu->midr = 0x6901B119; - cpu->reset_sctlr = 0x00000070; -} - -static void pxa250_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052100; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa255_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d00; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa260_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052903; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa261_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d05; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa262_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d06; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270a0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054110; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270a1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054111; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270b0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054112; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270b1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054113; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270c0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054114; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270c5_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054117; - cpu->ctr = 0xd172172; - 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, - .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 */ -}; -#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. - * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c; - * this only needs to handle 32 bits, and need not care about KVM. - */ -static void arm_max_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - /* aarch64_a57_initfn, advertising none of the aarch64 features */ - 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_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 = 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.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 */ - cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ - cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */ - define_cortex_a72_a57_a53_cp_reginfo(cpu); - - aa32_max_features(cpu); - -#ifdef CONFIG_USER_ONLY - /* - * Break with true ARMv8 and add back old-style VFP short-vector support. - * Only do this for user-mode, where -cpu max is the default, so that - * older v6 and v7 programs are more likely to work without adjustment. - */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); -#endif -} -#endif /* !TARGET_AARCH64 */ - -static const ARMCPUInfo arm_tcg_cpus[] = { - { .name = "arm926", .initfn = arm926_initfn }, - { .name = "arm946", .initfn = arm946_initfn }, - { .name = "arm1026", .initfn = arm1026_initfn }, - /* - * What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an - * older core than plain "arm1136". In particular this does not - * have the v6K features. - */ - { .name = "arm1136-r2", .initfn = arm1136_r2_initfn }, - { .name = "arm1136", .initfn = arm1136_initfn }, - { .name = "arm1176", .initfn = arm1176_initfn }, - { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, - { .name = "cortex-a7", .initfn = cortex_a7_initfn }, - { .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 = "ti925t", .initfn = ti925t_initfn }, - { .name = "sa1100", .initfn = sa1100_initfn }, - { .name = "sa1110", .initfn = sa1110_initfn }, - { .name = "pxa250", .initfn = pxa250_initfn }, - { .name = "pxa255", .initfn = pxa255_initfn }, - { .name = "pxa260", .initfn = pxa260_initfn }, - { .name = "pxa261", .initfn = pxa261_initfn }, - { .name = "pxa262", .initfn = pxa262_initfn }, - /* "pxa270" is an alias for "pxa270-a0" */ - { .name = "pxa270", .initfn = pxa270a0_initfn }, - { .name = "pxa270-a0", .initfn = pxa270a0_initfn }, - { .name = "pxa270-a1", .initfn = pxa270a1_initfn }, - { .name = "pxa270-b0", .initfn = pxa270b0_initfn }, - { .name = "pxa270-b1", .initfn = pxa270b1_initfn }, - { .name = "pxa270-c0", .initfn = pxa270c0_initfn }, - { .name = "pxa270-c5", .initfn = pxa270c5_initfn }, -#ifndef TARGET_AARCH64 - { .name = "max", .initfn = arm_max_initfn }, -#endif -#ifdef CONFIG_USER_ONLY - { .name = "any", .initfn = arm_max_initfn }, -#endif -}; - -static const TypeInfo idau_interface_type_info = { - .name = TYPE_IDAU_INTERFACE, - .parent = TYPE_INTERFACE, - .class_size = sizeof(IDAUInterfaceClass), -}; - -static void arm_tcg_cpu_register_types(void) -{ - size_t i; - - type_register_static(&idau_interface_type_info); - for (i = 0; i < ARRAY_SIZE(arm_tcg_cpus); ++i) { - arm_cpu_register(&arm_tcg_cpus[i]); - } -} - -type_init(arm_tcg_cpu_register_types) - -#endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ diff --git a/target/arm/crypto_helper.c b/target/arm/crypto_helper.c deleted file mode 100644 index d28690321f..0000000000 --- a/target/arm/crypto_helper.c +++ /dev/null @@ -1,778 +0,0 @@ -/* - * crypto_helper.c - emulate v8 Crypto Extensions instructions - * - * Copyright (C) 2013 - 2018 Linaro Ltd - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - */ - -#include "qemu/osdep.h" - -#include "cpu.h" -#include "exec/helper-proto.h" -#include "tcg/tcg-gvec-desc.h" -#include "crypto/aes.h" -#include "crypto/sm4.h" -#include "vec_internal.h" - -union CRYPTO_STATE { - uint8_t bytes[16]; - uint32_t words[4]; - uint64_t l[2]; -}; - -#if HOST_BIG_ENDIAN -#define CR_ST_BYTE(state, i) ((state).bytes[(15 - (i)) ^ 8]) -#define CR_ST_WORD(state, i) ((state).words[(3 - (i)) ^ 2]) -#else -#define CR_ST_BYTE(state, i) ((state).bytes[i]) -#define CR_ST_WORD(state, i) ((state).words[i]) -#endif - -/* - * The caller has not been converted to full gvec, and so only - * modifies the low 16 bytes of the vector register. - */ -static void clear_tail_16(void *vd, uint32_t desc) -{ - int opr_sz = simd_oprsz(desc); - int max_sz = simd_maxsz(desc); - - assert(opr_sz == 16); - 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]; -} - -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); - } - clear_tail(vd, opr_sz, simd_maxsz(desc)); -} - -static void do_crypto_aesmc(uint64_t *rd, uint64_t *rm, bool decrypt) -{ - 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, - } }; - - union CRYPTO_STATE st = { .l = { rm[0], rm[1] } }; - int i; - - 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); - } - - rd[0] = st.l[0]; - rd[1] = st.l[1]; -} - -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); - } - clear_tail(vd, opr_sz, simd_maxsz(desc)); -} - -/* - * SHA-1 logical functions - */ - -static uint32_t cho(uint32_t x, uint32_t y, uint32_t z) -{ - return (x & (y ^ z)) ^ z; -} - -static uint32_t par(uint32_t x, uint32_t y, uint32_t z) -{ - return x ^ y ^ z; -} - -static uint32_t maj(uint32_t x, uint32_t y, uint32_t z) -{ - return (x & y) | ((x | y) & z); -} - -void HELPER(crypto_sha1su0)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *d = vd, *n = vn, *m = vm; - uint64_t d0, d1; - - d0 = d[1] ^ d[0] ^ m[0]; - d1 = n[0] ^ d[1] ^ m[1]; - d[0] = d0; - d[1] = d1; - - clear_tail_16(vd, desc); -} - -static inline void crypto_sha1_3reg(uint64_t *rd, uint64_t *rn, - uint64_t *rm, uint32_t desc, - uint32_t (*fn)(union CRYPTO_STATE *d)) -{ - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - int i; - - for (i = 0; i < 4; i++) { - uint32_t t = fn(&d); - - t += rol32(CR_ST_WORD(d, 0), 5) + CR_ST_WORD(n, 0) - + CR_ST_WORD(m, i); - - CR_ST_WORD(n, 0) = CR_ST_WORD(d, 3); - CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); - CR_ST_WORD(d, 2) = ror32(CR_ST_WORD(d, 1), 2); - CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); - CR_ST_WORD(d, 0) = t; - } - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(rd, desc); -} - -static uint32_t do_sha1c(union CRYPTO_STATE *d) -{ - return cho(CR_ST_WORD(*d, 1), CR_ST_WORD(*d, 2), CR_ST_WORD(*d, 3)); -} - -void HELPER(crypto_sha1c)(void *vd, void *vn, void *vm, uint32_t desc) -{ - crypto_sha1_3reg(vd, vn, vm, desc, do_sha1c); -} - -static uint32_t do_sha1p(union CRYPTO_STATE *d) -{ - return par(CR_ST_WORD(*d, 1), CR_ST_WORD(*d, 2), CR_ST_WORD(*d, 3)); -} - -void HELPER(crypto_sha1p)(void *vd, void *vn, void *vm, uint32_t desc) -{ - crypto_sha1_3reg(vd, vn, vm, desc, do_sha1p); -} - -static uint32_t do_sha1m(union CRYPTO_STATE *d) -{ - return maj(CR_ST_WORD(*d, 1), CR_ST_WORD(*d, 2), CR_ST_WORD(*d, 3)); -} - -void HELPER(crypto_sha1m)(void *vd, void *vn, void *vm, uint32_t desc) -{ - crypto_sha1_3reg(vd, vn, vm, desc, do_sha1m); -} - -void HELPER(crypto_sha1h)(void *vd, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rm = vm; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - - CR_ST_WORD(m, 0) = ror32(CR_ST_WORD(m, 0), 2); - CR_ST_WORD(m, 1) = CR_ST_WORD(m, 2) = CR_ST_WORD(m, 3) = 0; - - rd[0] = m.l[0]; - rd[1] = m.l[1]; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha1su1)(void *vd, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - - CR_ST_WORD(d, 0) = rol32(CR_ST_WORD(d, 0) ^ CR_ST_WORD(m, 1), 1); - CR_ST_WORD(d, 1) = rol32(CR_ST_WORD(d, 1) ^ CR_ST_WORD(m, 2), 1); - CR_ST_WORD(d, 2) = rol32(CR_ST_WORD(d, 2) ^ CR_ST_WORD(m, 3), 1); - CR_ST_WORD(d, 3) = rol32(CR_ST_WORD(d, 3) ^ CR_ST_WORD(d, 0), 1); - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -/* - * The SHA-256 logical functions, according to - * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf - */ - -static uint32_t S0(uint32_t x) -{ - return ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22); -} - -static uint32_t S1(uint32_t x) -{ - return ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25); -} - -static uint32_t s0(uint32_t x) -{ - return ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3); -} - -static uint32_t s1(uint32_t x) -{ - return ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10); -} - -void HELPER(crypto_sha256h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - int i; - - for (i = 0; i < 4; i++) { - uint32_t t = cho(CR_ST_WORD(n, 0), CR_ST_WORD(n, 1), CR_ST_WORD(n, 2)) - + CR_ST_WORD(n, 3) + S1(CR_ST_WORD(n, 0)) - + CR_ST_WORD(m, i); - - CR_ST_WORD(n, 3) = CR_ST_WORD(n, 2); - CR_ST_WORD(n, 2) = CR_ST_WORD(n, 1); - CR_ST_WORD(n, 1) = CR_ST_WORD(n, 0); - CR_ST_WORD(n, 0) = CR_ST_WORD(d, 3) + t; - - t += maj(CR_ST_WORD(d, 0), CR_ST_WORD(d, 1), CR_ST_WORD(d, 2)) - + S0(CR_ST_WORD(d, 0)); - - CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); - CR_ST_WORD(d, 2) = CR_ST_WORD(d, 1); - CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); - CR_ST_WORD(d, 0) = t; - } - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha256h2)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - int i; - - for (i = 0; i < 4; i++) { - uint32_t t = cho(CR_ST_WORD(d, 0), CR_ST_WORD(d, 1), CR_ST_WORD(d, 2)) - + CR_ST_WORD(d, 3) + S1(CR_ST_WORD(d, 0)) - + CR_ST_WORD(m, i); - - CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); - CR_ST_WORD(d, 2) = CR_ST_WORD(d, 1); - CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); - CR_ST_WORD(d, 0) = CR_ST_WORD(n, 3 - i) + t; - } - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha256su0)(void *vd, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - - CR_ST_WORD(d, 0) += s0(CR_ST_WORD(d, 1)); - CR_ST_WORD(d, 1) += s0(CR_ST_WORD(d, 2)); - CR_ST_WORD(d, 2) += s0(CR_ST_WORD(d, 3)); - CR_ST_WORD(d, 3) += s0(CR_ST_WORD(m, 0)); - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha256su1)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - - CR_ST_WORD(d, 0) += s1(CR_ST_WORD(m, 2)) + CR_ST_WORD(n, 1); - CR_ST_WORD(d, 1) += s1(CR_ST_WORD(m, 3)) + CR_ST_WORD(n, 2); - CR_ST_WORD(d, 2) += s1(CR_ST_WORD(d, 0)) + CR_ST_WORD(n, 3); - CR_ST_WORD(d, 3) += s1(CR_ST_WORD(d, 1)) + CR_ST_WORD(m, 0); - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -/* - * The SHA-512 logical functions (same as above but using 64-bit operands) - */ - -static uint64_t cho512(uint64_t x, uint64_t y, uint64_t z) -{ - return (x & (y ^ z)) ^ z; -} - -static uint64_t maj512(uint64_t x, uint64_t y, uint64_t z) -{ - return (x & y) | ((x | y) & z); -} - -static uint64_t S0_512(uint64_t x) -{ - return ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39); -} - -static uint64_t S1_512(uint64_t x) -{ - return ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41); -} - -static uint64_t s0_512(uint64_t x) -{ - return ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7); -} - -static uint64_t s1_512(uint64_t x) -{ - return ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6); -} - -void HELPER(crypto_sha512h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - uint64_t d0 = rd[0]; - uint64_t d1 = rd[1]; - - d1 += S1_512(rm[1]) + cho512(rm[1], rn[0], rn[1]); - d0 += S1_512(d1 + rm[0]) + cho512(d1 + rm[0], rm[1], rn[0]); - - rd[0] = d0; - rd[1] = d1; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha512h2)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - uint64_t d0 = rd[0]; - uint64_t d1 = rd[1]; - - d1 += S0_512(rm[0]) + maj512(rn[0], rm[1], rm[0]); - d0 += S0_512(d1) + maj512(d1, rm[0], rm[1]); - - rd[0] = d0; - rd[1] = d1; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha512su0)(void *vd, void *vn, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t d0 = rd[0]; - uint64_t d1 = rd[1]; - - d0 += s0_512(rd[1]); - d1 += s0_512(rn[0]); - - rd[0] = d0; - rd[1] = d1; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sha512su1)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - - rd[0] += s1_512(rn[0]) + rm[0]; - rd[1] += s1_512(rn[1]) + rm[1]; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sm3partw1)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - uint32_t t; - - t = CR_ST_WORD(d, 0) ^ CR_ST_WORD(n, 0) ^ ror32(CR_ST_WORD(m, 1), 17); - CR_ST_WORD(d, 0) = t ^ ror32(t, 17) ^ ror32(t, 9); - - t = CR_ST_WORD(d, 1) ^ CR_ST_WORD(n, 1) ^ ror32(CR_ST_WORD(m, 2), 17); - CR_ST_WORD(d, 1) = t ^ ror32(t, 17) ^ ror32(t, 9); - - t = CR_ST_WORD(d, 2) ^ CR_ST_WORD(n, 2) ^ ror32(CR_ST_WORD(m, 3), 17); - CR_ST_WORD(d, 2) = t ^ ror32(t, 17) ^ ror32(t, 9); - - t = CR_ST_WORD(d, 3) ^ CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(d, 0), 17); - CR_ST_WORD(d, 3) = t ^ ror32(t, 17) ^ ror32(t, 9); - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -void HELPER(crypto_sm3partw2)(void *vd, void *vn, void *vm, uint32_t desc) -{ - uint64_t *rd = vd; - uint64_t *rn = vn; - uint64_t *rm = vm; - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - uint32_t t = CR_ST_WORD(n, 0) ^ ror32(CR_ST_WORD(m, 0), 25); - - CR_ST_WORD(d, 0) ^= t; - CR_ST_WORD(d, 1) ^= CR_ST_WORD(n, 1) ^ ror32(CR_ST_WORD(m, 1), 25); - CR_ST_WORD(d, 2) ^= CR_ST_WORD(n, 2) ^ ror32(CR_ST_WORD(m, 2), 25); - CR_ST_WORD(d, 3) ^= CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(m, 3), 25) ^ - ror32(t, 17) ^ ror32(t, 2) ^ ror32(t, 26); - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(vd, desc); -} - -static inline void QEMU_ALWAYS_INLINE -crypto_sm3tt(uint64_t *rd, uint64_t *rn, uint64_t *rm, - uint32_t desc, uint32_t opcode) -{ - union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - uint32_t imm2 = simd_data(desc); - uint32_t t; - - assert(imm2 < 4); - - if (opcode == 0 || opcode == 2) { - /* SM3TT1A, SM3TT2A */ - t = par(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1)); - } else if (opcode == 1) { - /* SM3TT1B */ - t = maj(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1)); - } else if (opcode == 3) { - /* SM3TT2B */ - t = cho(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1)); - } else { - qemu_build_not_reached(); - } - - t += CR_ST_WORD(d, 0) + CR_ST_WORD(m, imm2); - - CR_ST_WORD(d, 0) = CR_ST_WORD(d, 1); - - if (opcode < 2) { - /* SM3TT1A, SM3TT1B */ - t += CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(d, 3), 20); - - CR_ST_WORD(d, 1) = ror32(CR_ST_WORD(d, 2), 23); - } else { - /* SM3TT2A, SM3TT2B */ - t += CR_ST_WORD(n, 3); - t ^= rol32(t, 9) ^ rol32(t, 17); - - CR_ST_WORD(d, 1) = ror32(CR_ST_WORD(d, 2), 13); - } - - CR_ST_WORD(d, 2) = CR_ST_WORD(d, 3); - CR_ST_WORD(d, 3) = t; - - rd[0] = d.l[0]; - rd[1] = d.l[1]; - - clear_tail_16(rd, desc); -} - -#define DO_SM3TT(NAME, OPCODE) \ - void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ - { crypto_sm3tt(vd, vn, vm, desc, OPCODE); } - -DO_SM3TT(crypto_sm3tt1a, 0) -DO_SM3TT(crypto_sm3tt1b, 1) -DO_SM3TT(crypto_sm3tt2a, 2) -DO_SM3TT(crypto_sm3tt2b, 3) - -#undef DO_SM3TT - -static void do_crypto_sm4e(uint64_t *rd, uint64_t *rn, uint64_t *rm) -{ - union CRYPTO_STATE d = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE n = { .l = { rm[0], rm[1] } }; - uint32_t t, i; - - for (i = 0; i < 4; i++) { - t = CR_ST_WORD(d, (i + 1) % 4) ^ - CR_ST_WORD(d, (i + 2) % 4) ^ - 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; - - CR_ST_WORD(d, i) ^= t ^ rol32(t, 2) ^ rol32(t, 10) ^ rol32(t, 18) ^ - rol32(t, 24); - } - - rd[0] = d.l[0]; - rd[1] = d.l[1]; -} - -void HELPER(crypto_sm4e)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - - for (i = 0; i < opr_sz; i += 16) { - do_crypto_sm4e(vd + i, vn + i, vm + i); - } - clear_tail(vd, opr_sz, simd_maxsz(desc)); -} - -static void do_crypto_sm4ekey(uint64_t *rd, uint64_t *rn, uint64_t *rm) -{ - union CRYPTO_STATE d; - union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; - union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; - uint32_t t, i; - - d = n; - for (i = 0; i < 4; i++) { - t = CR_ST_WORD(d, (i + 1) % 4) ^ - CR_ST_WORD(d, (i + 2) % 4) ^ - 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; - - CR_ST_WORD(d, i) ^= t ^ rol32(t, 13) ^ rol32(t, 23); - } - - rd[0] = d.l[0]; - rd[1] = d.l[1]; -} - -void HELPER(crypto_sm4ekey)(void *vd, void *vn, void* vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - - for (i = 0; i < opr_sz; i += 16) { - do_crypto_sm4ekey(vd + i, vn + i, vm + i); - } - clear_tail(vd, opr_sz, simd_maxsz(desc)); -} - -void HELPER(crypto_rax1)(void *vd, void *vn, void *vm, uint32_t 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) { - d[i] = n[i] ^ rol64(m[i], 1); - } - clear_tail(vd, opr_sz, simd_maxsz(desc)); -} diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index c21739242c..7d856acddf 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -9,17 +9,23 @@ #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; @@ -433,15 +439,20 @@ 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 = false; + bool using_lpae; - if (target_el == 2 || arm_el_is_aa64(env, target_el)) { + 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 { - if (arm_feature(env, ARM_FEATURE_LPAE) && - (env->cp15.tcr_el[target_el] & TTBCR_EAE)) { - using_lpae = true; - } + using_lpae = false; } if (using_lpae) { @@ -533,195 +544,6 @@ void HELPER(exception_swstep)(CPUARMState *env, uint32_t syndrome) raise_exception_debug(env, EXCP_UDEF, syndrome); } -/* - * 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 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 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, - .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 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 }, -}; - void hw_watchpoint_update(ARMCPU *cpu, int n) { CPUARMState *env = &cpu->env; @@ -828,39 +650,6 @@ void hw_watchpoint_update_all(ARMCPU *cpu) } } -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); - 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; @@ -964,6 +753,377 @@ void hw_breakpoint_update_all(ARMCPU *cpu) } } +#if !defined(CONFIG_USER_ONLY) + +vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* + * In BE32 system mode, target memory is stored byteswapped (on a + * little-endian host system), and by the time we reach here (via an + * opcode helper) the addresses of subword accesses have been adjusted + * to account for that, which means that watchpoints will not match. + * Undo the adjustment here. + */ + if (arm_sctlr_b(env)) { + if (len == 1) { + addr ^= 3; + } else if (len == 2) { + addr ^= 2; + } + } + + return addr; +} + +#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) { @@ -971,7 +1131,9 @@ static void dbgbvr_write(CPUARMState *env, const ARMCPRegInfo *ri, int i = ri->crm; raw_write(env, ri, value); - hw_breakpoint_update(cpu, i); + if (tcg_enabled()) { + hw_breakpoint_update(cpu, i); + } } static void dbgbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -988,7 +1150,9 @@ static void dbgbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, value = deposit64(value, 8, 1, extract64(value, 7, 1)); raw_write(env, ri, value); - hw_breakpoint_update(cpu, i); + if (tcg_enabled()) { + hw_breakpoint_update(cpu, i); + } } void define_debug_regs(ARMCPU *cpu) @@ -1058,6 +1222,9 @@ void define_debug_regs(ARMCPU *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); @@ -1070,12 +1237,14 @@ void define_debug_regs(ARMCPU *cpu) { .name = dbgbvr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGBVRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), .writefn = dbgbvr_write, .raw_writefn = raw_write }, { .name = dbgbcr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGBCRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), .writefn = dbgbcr_write, .raw_writefn = raw_write }, @@ -1092,12 +1261,14 @@ void define_debug_regs(ARMCPU *cpu) { .name = dbgwvr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGWVRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), .writefn = dbgwvr_write, .raw_writefn = raw_write }, { .name = dbgwcr_el1_name, .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, .access = PL1_RW, .accessfn = access_tda, + .fgt = FGT_DBGWCRN_EL1, .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), .writefn = dbgwcr_write, .raw_writefn = raw_write }, @@ -1107,30 +1278,3 @@ void define_debug_regs(ARMCPU *cpu) g_free(dbgwcr_el1_name); } } - -#if !defined(CONFIG_USER_ONLY) - -vaddr arm_adjust_watchpoint_address(CPUState *cs, vaddr addr, int len) -{ - ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; - - /* - * In BE32 system mode, target memory is stored byteswapped (on a - * little-endian host system), and by the time we reach here (via an - * opcode helper) the addresses of subword accesses have been adjusted - * to account for that, which means that watchpoints will not match. - * Undo the adjustment here. - */ - if (arm_sctlr_b(env)) { - if (len == 1) { - addr ^= 3; - } else if (len == 2) { - addr ^= 2; - } - } - - return addr; -} - -#endif diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 2f806512d0..554b8736bb 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -20,14 +20,18 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "gdbstub/commands.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 +48,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 +88,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 +107,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 +132,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 +159,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 +173,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 +189,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 +202,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 +225,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 +244,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,164 +283,225 @@ 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); + } + } + + 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_commands(ARMCPU *cpu) +{ + g_autoptr(GPtrArray) query_table = g_ptr_array_new(); + g_autoptr(GPtrArray) set_table = g_ptr_array_new(); + g_autoptr(GString) qsupported_features = g_string_new(NULL); + + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + #ifdef TARGET_AARCH64 + aarch64_cpu_register_gdb_commands(cpu, qsupported_features, query_table, + set_table); + #endif + } + + /* Set arch-specific handlers for 'q' commands. */ + if (query_table->len) { + gdb_extend_query_table(query_table); + } + + /* Set arch-specific handlers for 'Q' commands. */ + if (set_table->len) { + gdb_extend_set_table(set_table); + } + + /* Set arch-specific qSupported feature. */ + if (qsupported_features->len) { + gdb_extend_qsupported_features(qsupported_features->str); } - return NULL; } void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) @@ -466,25 +516,50 @@ 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); + } + +#ifdef CONFIG_USER_ONLY + /* Memory Tagging Extension (MTE) 'tag_ctl' pseudo-register. */ + if (cpu_isar_feature(aa64_mte, cpu)) { + gdb_register_coprocessor(cs, aarch64_gdb_get_tag_ctl_reg, + aarch64_gdb_set_tag_ctl_reg, + gdb_find_static_feature("aarch64-mte.xml"), + 0); + } +#endif #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 +567,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..1a4dbec567 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -20,7 +20,13 @@ #include "qemu/log.h" #include "cpu.h" #include "internals.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "gdbstub/commands.h" +#include "tcg/mte_helper.h" +#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX) +#include +#include "mte_user_helper.h" +#endif int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { @@ -72,8 +78,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 +101,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 +128,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 +177,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 +223,398 @@ 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; +} + +#ifdef CONFIG_USER_ONLY +int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + uint64_t tcf0; + + assert(reg == 0); + + tcf0 = extract64(env->cp15.sctlr_el[1], 38, 2); + + return gdb_get_reg64(buf, tcf0); +} + +int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg) +{ +#if defined(CONFIG_LINUX) + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + uint8_t tcf; + + assert(reg == 0); + + tcf = *buf << PR_MTE_TCF_SHIFT; + + if (!tcf) { + return 0; + } + + /* + * 'tag_ctl' register is actually a "pseudo-register" provided by GDB to + * expose options regarding the type of MTE fault that can be controlled at + * runtime. + */ + arm_set_mte_tcf0(env, tcf); + + return 1; +#else + return 0; +#endif +} +#endif /* CONFIG_USER_ONLY */ + +#ifdef CONFIG_TCG +static void handle_q_memtag(GArray *params, void *user_ctx) +{ + ARMCPU *cpu = ARM_CPU(user_ctx); + CPUARMState *env = &cpu->env; + uint32_t mmu_index; + + uint64_t addr = gdb_get_cmd_param(params, 0)->val_ull; + uint64_t len = gdb_get_cmd_param(params, 1)->val_ul; + int type = gdb_get_cmd_param(params, 2)->val_ul; + + uint8_t *tags; + uint8_t addr_tag; + + g_autoptr(GString) str_buf = g_string_new(NULL); + + /* + * GDB does not query multiple tags for a memory range on remote targets, so + * that's not supported either by gdbstub. + */ + if (len != 1) { + gdb_put_packet("E02"); + } + + /* GDB never queries a tag different from an allocation tag (type 1). */ + if (type != 1) { + gdb_put_packet("E03"); + } + + /* Find out the current translation regime for probe. */ + mmu_index = cpu_mmu_index(env_cpu(env), false); + /* Note that tags are packed here (2 tags packed in one byte). */ + tags = allocation_tag_mem_probe(env, mmu_index, addr, MMU_DATA_LOAD, 1, + MMU_DATA_LOAD, true, 0); + if (!tags) { + /* Address is not in a tagged region. */ + gdb_put_packet("E04"); + return; + } + + /* Unpack tag from byte. */ + addr_tag = load_tag1(addr, tags); + g_string_printf(str_buf, "m%.2x", addr_tag); + + gdb_put_packet(str_buf->str); +} + +static void handle_q_isaddresstagged(GArray *params, void *user_ctx) +{ + ARMCPU *cpu = ARM_CPU(user_ctx); + CPUARMState *env = &cpu->env; + uint32_t mmu_index; + + uint64_t addr = gdb_get_cmd_param(params, 0)->val_ull; + + uint8_t *tags; + const char *reply; + + /* Find out the current translation regime for probe. */ + mmu_index = cpu_mmu_index(env_cpu(env), false); + tags = allocation_tag_mem_probe(env, mmu_index, addr, MMU_DATA_LOAD, 1, + MMU_DATA_LOAD, true, 0); + reply = tags ? "01" : "00"; + + gdb_put_packet(reply); +} + +static void handle_Q_memtag(GArray *params, void *user_ctx) +{ + ARMCPU *cpu = ARM_CPU(user_ctx); + CPUARMState *env = &cpu->env; + uint32_t mmu_index; + + uint64_t start_addr = gdb_get_cmd_param(params, 0)->val_ull; + uint64_t len = gdb_get_cmd_param(params, 1)->val_ul; + int type = gdb_get_cmd_param(params, 2)->val_ul; + char const *new_tags_str = gdb_get_cmd_param(params, 3)->data; + + uint64_t end_addr; + + int num_new_tags; + uint8_t *tags; + + g_autoptr(GByteArray) new_tags = g_byte_array_new(); + + /* + * Only the allocation tag (i.e. type 1) can be set at the stub side. + */ + if (type != 1) { + gdb_put_packet("E02"); + return; + } + + end_addr = start_addr + (len - 1); /* 'len' is always >= 1 */ + /* Check if request's memory range does not cross page boundaries. */ + if ((start_addr ^ end_addr) & TARGET_PAGE_MASK) { + gdb_put_packet("E03"); + return; + } + + /* + * Get all tags in the page starting from the tag of the start address. + * Note that there are two tags packed into a single byte here. + */ + /* Find out the current translation regime for probe. */ + mmu_index = cpu_mmu_index(env_cpu(env), false); + tags = allocation_tag_mem_probe(env, mmu_index, start_addr, MMU_DATA_STORE, + 1, MMU_DATA_STORE, true, 0); + if (!tags) { + /* Address is not in a tagged region. */ + gdb_put_packet("E04"); + return; + } + + /* Convert tags provided by GDB, 2 hex digits per tag. */ + num_new_tags = strlen(new_tags_str) / 2; + gdb_hextomem(new_tags, new_tags_str, num_new_tags); + + uint64_t address = start_addr; + int new_tag_index = 0; + while (address <= end_addr) { + uint8_t new_tag; + int packed_index; + + /* + * Find packed tag index from unpacked tag index. There are two tags + * in one packed index (one tag per nibble). + */ + packed_index = new_tag_index / 2; + + new_tag = new_tags->data[new_tag_index % num_new_tags]; + store_tag1(address, tags + packed_index, new_tag); + + address += TAG_GRANULE; + new_tag_index++; + } + + gdb_put_packet("OK"); +} + +enum Command { + qMemTags, + qIsAddressTagged, + QMemTags, + NUM_CMDS +}; + +static const GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = { + [qMemTags] = { + .handler = handle_q_memtag, + .cmd_startswith = true, + .cmd = "MemTags:", + .schema = "L,l:l0", + .need_cpu_context = true + }, + [qIsAddressTagged] = { + .handler = handle_q_isaddresstagged, + .cmd_startswith = true, + .cmd = "IsAddressTagged:", + .schema = "L0", + .need_cpu_context = true + }, + [QMemTags] = { + .handler = handle_Q_memtag, + .cmd_startswith = true, + .cmd = "MemTags:", + .schema = "L,l:l:s0", + .need_cpu_context = true + }, +}; +#endif /* CONFIG_TCG */ + +void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *qsupported, + GPtrArray *qtable, GPtrArray *stable) +{ + /* MTE */ +#ifdef CONFIG_TCG + if (cpu_isar_feature(aa64_mte, cpu)) { + g_string_append(qsupported, ";memory-tagging+"); + + g_ptr_array_add(qtable, (gpointer) &cmd_handler_table[qMemTags]); + g_ptr_array_add(qtable, (gpointer) &cmd_handler_table[qIsAddressTagged]); + g_ptr_array_add(stable, (gpointer) &cmd_handler_table[QMemTags]); + } +#endif +} 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 22bc935242..f38eb054c0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7,35 +7,30 @@ */ #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" #include "qemu/crc32c.h" #include "qemu/qemu-print.h" #include "exec/exec-all.h" -#include /* For crc32 */ +#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 */ @@ -83,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. @@ -101,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 @@ -184,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) */ @@ -202,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++; @@ -216,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++; } } @@ -237,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; @@ -265,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. */ @@ -279,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. @@ -301,7 +313,8 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_TRAP_UNCATEGORIZED; } -/* 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, @@ -320,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; @@ -362,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); @@ -375,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)); @@ -390,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. */ @@ -405,6 +444,9 @@ 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. + * + * For AArch32 this is only used for TLBIALLNSNH and VTTBR + * writes, so only needs to apply to NS PL1&0, not S PL1&0. */ return (ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_1_PAN | @@ -582,7 +624,8 @@ static void tlbiipas2is_hyp_write(CPUARMState *env, const ARMCPRegInfo *ri, } 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. @@ -597,7 +640,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 @@ -606,6 +650,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, }, @@ -618,7 +664,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 */ @@ -628,7 +675,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, @@ -646,7 +694,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, @@ -654,12 +703,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. */ @@ -673,14 +724,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, @@ -708,7 +761,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. */ @@ -724,7 +778,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))) { @@ -766,7 +821,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); @@ -807,7 +863,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. */ @@ -822,13 +879,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 }, }; @@ -892,16 +952,19 @@ 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 @@ -1027,7 +1090,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. */ @@ -1115,7 +1179,8 @@ static CPAccessResult pmreg_access_ccntr(CPUARMState *env, (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 +/* + * 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) @@ -1125,13 +1190,21 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) 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; @@ -1240,7 +1313,7 @@ static bool pmevcntr_is_64_bit(CPUARMState *env, int counter) bool hlp = env->cp15.mdcr_el2 & MDCR_HLP; int hpmn = env->cp15.mdcr_el2 & MDCR_HPMN; - if (hpmn != 0 && counter >= hpmn) { + if (counter >= hpmn) { return hlp; } } @@ -1431,6 +1504,22 @@ 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) { @@ -1479,7 +1568,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. @@ -1590,7 +1680,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. */ @@ -1629,7 +1720,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. @@ -1691,8 +1782,10 @@ static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri, } 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; } } @@ -1767,7 +1860,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 @@ -1807,6 +1901,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; @@ -1823,6 +1920,15 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_hcx, cpu)) { valid_mask |= SCR_HXEN; } + if (cpu_isar_feature(aa64_fgt, cpu)) { + valid_mask |= SCR_FGTEN; + } + 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)) { @@ -1833,7 +1939,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. @@ -1851,10 +1958,10 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) env->cp15.scr_el3 = value; /* - * If SCR_EL3.NS changes, i.e. arm_is_secure_below_el3, then + * If SCR_EL3.{NS,NSE} changes, i.e. change of security state, * we must invalidate all TLBs below EL3. */ - if (changed & SCR_NS) { + if (changed & (SCR_NS | SCR_NSE)) { tlb_flush_by_mmuidx(env_cpu(env), (ARMMMUIdxBit_E10_0 | ARMMMUIdxBit_E20_0 | ARMMMUIdxBit_E10_1 | @@ -1874,11 +1981,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; } @@ -1889,7 +1997,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, @@ -1915,16 +2024,29 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { ret |= CPSR_I; } + if (cs->interrupt_request & CPU_INTERRUPT_VINMI) { + ret |= ISR_IS; + ret |= CPSR_I; + } } else { if (cs->interrupt_request & CPU_INTERRUPT_HARD) { ret |= CPSR_I; } + + if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + ret |= ISR_IS; + ret |= CPSR_I; + } } if (hcr_el2 & HCR_FMO) { if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { ret |= CPSR_F; } + if (cs->interrupt_request & CPU_INTERRUPT_VFNMI) { + ret |= ISR_FS; + ret |= CPSR_F; + } } else { if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { ret |= CPSR_F; @@ -1964,7 +2086,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. * @@ -1980,21 +2103,25 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenset_write, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .raw_writefn = raw_write }, { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, .type = ARM_CP_IO, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, .writefn = pmcntenset_write, .raw_writefn = raw_write }, { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, .access = PL0_RW, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .writefn = pmcntenclr_write, .type = ARM_CP_ALIAS | ARM_CP_IO }, { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, @@ -2002,41 +2129,49 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, .access = PL0_RW, .type = ARM_CP_ALIAS, + .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), .accessfn = pmreg_access_selr, .writefn = pmselr_write, .raw_writefn = raw_write}, { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5, .access = PL0_RW, .accessfn = pmreg_access_selr, + .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fgt = FGT_PMCCNTR_EL0, .readfn = pmccntr_read, .writefn = pmccntr_write32, .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access_ccntr, + .fgt = FGT_PMCCNTR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), .readfn = pmccntr_read, .writefn = pmccntr_write, @@ -2044,32 +2179,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, @@ -2084,6 +2225,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, @@ -2091,68 +2233,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. */ @@ -2170,6 +2329,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, @@ -2209,16 +2369,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 }, }; @@ -2226,6 +2386,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, @@ -2233,6 +2394,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, @@ -2283,25 +2445,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, @@ -2310,12 +2477,20 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { .resetvalue = 0 }, }; +static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) +{ + ARMCPU *cpu = env_archcpu(env); + + cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz; +} + #ifndef CONFIG_USER_ONLY 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); @@ -2372,22 +2547,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 && @@ -2396,6 +2556,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; @@ -2439,6 +2604,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; @@ -2474,7 +2644,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. */ @@ -2497,42 +2668,109 @@ static CPAccessResult gt_stimer_access(CPUARMState *env, } } -static uint64_t gt_get_countervalue(CPUARMState *env) +uint64_t gt_get_countervalue(CPUARMState *env) { ARMCPU *cpu = env_archcpu(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. @@ -2542,14 +2780,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, @@ -2562,10 +2800,10 @@ 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) +uint64_t gt_virt_cnt_offset(CPUARMState *env) { uint64_t hcr; @@ -2611,6 +2849,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 - @@ -2628,6 +2869,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); @@ -2649,13 +2893,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); } } @@ -2781,6 +3024,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) { @@ -2952,15 +3238,9 @@ void arm_gt_hvtimer_cb(void *opaque) gt_recalc_timer(cpu, GTIMER_HYPVIRT); } -static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque) -{ - ARMCPU *cpu = env_archcpu(env); - - cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz; -} - static const ARMCPRegInfo generic_timer_cp_reginfo[] = { - /* Note that CNTFRQ is purely reads-as-written for the benefit + /* + * 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. */ @@ -3005,6 +3285,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, @@ -3022,6 +3303,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, @@ -3101,6 +3383,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, @@ -3118,12 +3401,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, @@ -3151,18 +3436,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 */ @@ -3170,7 +3504,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. */ @@ -3182,7 +3517,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, .type = ARM_CP_CONST, .access = PL0_R /* no PL1_RW in linux-user */, .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), - .resetvalue = NANOSECONDS_PER_SECOND / GTIMER_SCALE, + .resetfn = arm_gt_cntfrq_reset, }, { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, @@ -3191,6 +3526,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) @@ -3211,7 +3558,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. @@ -3219,9 +3567,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; } @@ -3230,9 +3578,22 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, } #ifdef CONFIG_TCG +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, - bool is_secure) + ARMSecuritySpace ss) { bool ret; uint64_t par64; @@ -3240,8 +3601,13 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, ARMMMUFaultInfo fi = {}; GetPhysAddrResult res = {}; - ret = get_phys_addr_with_secure(env, value, access_type, mmu_idx, - is_secure, &res, &fi); + /* + * I_MXTJT: Granule protection checks are not performed on the final + * address of a successful translation. This is a translation not a + * memory reference, so "memop = none = 0". + */ + ret = get_phys_addr_with_space_nogpc(env, value, access_type, 0, + mmu_idx, ss, &res, &fi); /* * ATS operations only do S1 or S1+S2 translations, so we never @@ -3358,7 +3724,7 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, par64 |= (1 << 9); /* NS */ } par64 |= (uint64_t)res.cacheattrs.attrs << 56; /* ATTR */ - par64 |= res.cacheattrs.shareability << 7; /* SH */ + par64 |= par_el1_shareability(&res) << 7; /* SH */ } else { uint32_t fsr = arm_fi_to_lfsc(&fi); @@ -3372,7 +3738,8 @@ 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. */ @@ -3405,21 +3772,24 @@ 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_E3; - secure = true; + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = ARMMMUIdx_E30_3_PAN; + } else { + 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)) { + if (ri->crm == 9 && arm_pan_enabled(env)) { mmu_idx = ARMMMUIdx_Stage1_E1_PAN; } else { mmu_idx = ARMMMUIdx_Stage1_E1; @@ -3433,11 +3803,10 @@ 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_E10_0; - secure = true; + mmu_idx = ARMMMUIdx_E30_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: @@ -3450,18 +3819,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; - secure = false; + ss = ARMSS_NonSecure; break; case 6: /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ mmu_idx = ARMMMUIdx_E10_0; - secure = false; + ss = ARMSS_NonSecure; break; default: g_assert_not_reached(); } - par64 = do_ats_write(env, value, access_type, mmu_idx, secure); + par64 = do_ats_write(env, value, access_type, mmu_idx, ss); A32_BANKED_CURRENT_REG_SET(env, par, par64); #else @@ -3478,7 +3847,8 @@ static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t par64; /* There is no SecureEL2 for AArch32. */ - par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, false); + par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, + ARMSS_NonSecure); A32_BANKED_CURRENT_REG_SET(env, par, par64); #else @@ -3487,6 +3857,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) { @@ -3494,7 +3880,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, @@ -3503,15 +3898,16 @@ 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)) { + if (ri->crm == 9 && arm_pan_enabled(env)) { mmu_idx = regime_e20 ? ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; } else { @@ -3523,7 +3919,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, break; case 6: /* AT S1E3R, AT S1E3W */ mmu_idx = ARMMMUIdx_E3; - secure = true; + for_el3 = true; break; default: g_assert_not_reached(); @@ -3542,8 +3938,8 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, g_assert_not_reached(); } - env->cp15.par_el[1] = do_ats_write(env, value, access_type, - mmu_idx, secure); + 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(); @@ -3551,20 +3947,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) { @@ -3660,8 +4042,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. */ @@ -3762,7 +4361,8 @@ static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *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)); @@ -3843,6 +4443,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, }, }; @@ -3851,22 +4453,30 @@ 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, .raw_writefn = raw_write, .resetvalue = 0, @@ -3879,7 +4489,8 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { 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 = { @@ -3917,7 +4528,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; @@ -3949,7 +4561,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. @@ -3978,7 +4591,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", @@ -3996,7 +4610,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. @@ -4022,21 +4637,22 @@ static const ARMCPRegInfo cache_block_ops_cp_reginfo[] = { .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, @@ -4072,7 +4688,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). */ @@ -4098,6 +4715,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, @@ -4112,13 +4731,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) @@ -4252,9 +4871,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)) { @@ -4265,8 +4882,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; @@ -4274,7 +4891,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) */ @@ -4283,11 +4913,14 @@ static int vae1_tlbmask(CPUARMState *env) uint64_t hcr = arm_hcr_el2_eff(env); uint16_t mask; + assert(arm_feature(env, ARM_FEATURE_AARCH64)); + if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { mask = ARMMMUIdxBit_E20_2 | ARMMMUIdxBit_E20_2_PAN | ARMMMUIdxBit_E20_0; } else { + /* This is AArch64 only, so we don't need to touch the EL30_x TLBs */ mask = ARMMMUIdxBit_E10_1 | ARMMMUIdxBit_E10_1_PAN | ARMMMUIdxBit_E10_0; @@ -4295,6 +4928,21 @@ static int vae1_tlbmask(CPUARMState *env) return mask; } +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; +} + /* Return 56 if TBI is enabled, 64 otherwise. */ static int tlbbits_for_regime(CPUARMState *env, ARMMMUIdx mmu_idx, uint64_t addr) @@ -4311,6 +4959,8 @@ static int vae1_tlbbits(CPUARMState *env, uint64_t addr) uint64_t hcr = arm_hcr_el2_eff(env); ARMMMUIdx mmu_idx; + assert(arm_feature(env, ARM_FEATURE_AARCH64)); + /* Only the regime of the mmu_idx below is significant. */ if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { mmu_idx = ARMMMUIdx_E20_0; @@ -4321,6 +4971,25 @@ static int vae1_tlbbits(CPUARMState *env, uint64_t addr) 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); +} + static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -4407,21 +5076,24 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, 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. */ @@ -4446,7 +5118,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. @@ -4467,11 +5140,11 @@ 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); - int bits = tlbbits_for_regime(env, ARMMMUIdx_E2, pageaddr); + int bits = vae2_tlbbits(env, pageaddr); - tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, - ARMMMUIdxBit_E2, bits); + tlb_flush_page_bits_by_mmuidx_all_cpus_synced(cs, pageaddr, mask, bits); } static void tlbi_aa64_vae3is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -4552,7 +5225,7 @@ static TLBIRange tlbi_aa64_get_range(CPUARMState *env, ARMMMUIdx mmuidx, 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; @@ -4643,11 +5316,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 ARMMMUIdxBit_E2; -} - static void tlbi_aa64_rvae2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -4767,7 +5435,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; @@ -4807,7 +5476,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; @@ -4818,7 +5488,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 @@ -4874,8 +5544,52 @@ static void mdcr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +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, @@ -4898,6 +5612,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, @@ -4905,97 +5620,131 @@ 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, @@ -5034,35 +5783,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, @@ -5076,15 +5829,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, @@ -5117,13 +5871,13 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .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, @@ -5137,7 +5891,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, @@ -5151,14 +5905,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. */ @@ -5169,26 +5926,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, + .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, @@ -5223,6 +5967,24 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .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); @@ -5236,7 +5998,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 @@ -5268,6 +6031,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. */ @@ -5280,9 +6058,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; @@ -5291,17 +6070,21 @@ 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. + * Note that if a write to HCR pends a VIRQ or VFIQ or VINMI or + * VFNMI, it is never possible for it to be taken immediately + * because VIRQ, VFIQ, VINMI and VFNMI 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); + if (cpu_isar_feature(aa64_nmi, cpu)) { + arm_cpu_update_vinmi(cpu); + arm_cpu_update_vfnmi(cpu); + } } static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -5330,11 +6113,13 @@ static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, * Bits that are not included here: * RW (read from SCR_EL3.RW as needed) */ -uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, bool secure) +uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, ARMSecuritySpace space) { uint64_t ret = env->cp15.hcr_el2; - if (!arm_is_el2_enabled_secstate(env, secure)) { + 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 @@ -5395,7 +6180,10 @@ uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, bool secure) uint64_t arm_hcr_el2_eff(CPUARMState *env) { - return arm_hcr_el2_eff_secstate(env, arm_is_secure_below_el3(env)); + if (arm_feature(env, ARM_FEATURE_M)) { + return 0; + } + return arm_hcr_el2_eff_secstate(env, arm_security_space_below_el3(env)); } /* @@ -5429,18 +6217,49 @@ bool el_is_in_host(CPUARMState *env, int el) static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + ARMCPU *cpu = env_archcpu(env); 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, cpu)) { + valid_mask |= HCRX_MSCEN | HCRX_MCE2; + } + + /* FEAT_NMI adds TALLINT, VINMI and VFNMI */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; + } + /* FEAT_CMOW adds CMOW */ + + if (cpu_isar_feature(aa64_cmow, cpu)) { + valid_mask |= HCRX_CMOW; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; + + /* + * Updates to VINMI and VFNMI require us to update the status of + * virtual NMI, which are the logical OR of these bits + * and the state of the input lines from the GIC. (This requires + * that we have the BQL, which is done by marking the + * reginfo structs as ARM_CP_IO.) + * Note that if a write to HCRX pends a VINMI or VFNMI it is never + * possible for it to be taken immediately, because VINMI and + * VFNMI are masked unless running at EL0 or EL1, and HCRX + * can only be written at EL2. + */ + if (cpu_isar_feature(aa64_nmi, cpu)) { + g_assert(bql_locked()); + arm_cpu_update_vinmi(cpu); + arm_cpu_update_vfnmi(cpu); + } } 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; @@ -5450,8 +6269,10 @@ static CPAccessResult access_hxen(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo hcrx_el2_reginfo = { .name = "HCRX_EL2", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_IO, .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), }; @@ -5460,13 +6281,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; @@ -5507,7 +6339,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, @@ -5517,14 +6350,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, @@ -5533,7 +6368,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]) }, @@ -5579,6 +6414,7 @@ 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, + .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, @@ -5588,6 +6424,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "VTCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 2, .crm = 1, .opc2 = 2, .access = PL2_RW, + .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, @@ -5595,10 +6432,11 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .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, @@ -5607,10 +6445,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, @@ -5660,7 +6500,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. */ @@ -5674,7 +6515,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. @@ -5687,16 +6529,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, @@ -5735,6 +6580,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) }, }; @@ -5760,17 +6606,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) { @@ -5793,12 +6642,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, @@ -5889,6 +6738,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) { @@ -5930,6 +6817,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 { @@ -6029,6 +6952,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); @@ -6145,6 +7103,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, @@ -6154,12 +7116,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) }, }; @@ -6267,32 +7232,6 @@ int sme_exception_el(CPUARMState *env, int el) return 0; } -/* 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; -} - /* * Given that SVE is enabled, return the vector length for EL. */ @@ -6311,7 +7250,7 @@ uint32_t sve_vqm1_for_el_sm(CPUARMState *env, int el, bool sm) if (el <= 1 && !el_is_in_host(env, el)) { len = MIN(len, 0xf & (uint32_t)cr[1]); } - if (el <= 2 && arm_feature(env, ARM_FEATURE_EL2)) { + if (el <= 2 && arm_is_el2_enabled(env)) { len = MIN(len, 0xf & (uint32_t)cr[2]); } if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -6357,6 +7296,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 }, @@ -6393,10 +7333,21 @@ static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -static CPAccessResult access_esm(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult access_smprimap(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* 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 CPAccessResult access_smpri(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) { - /* TODO: FEAT_FGT for SMPRI_EL1 but not SMPRIMAP_EL2 */ if (arm_current_el(env) < 3 && arm_feature(env, ARM_FEATURE_EL3) && !FIELD_EX64(env->cp15.cptr_el[3], CPTR_EL3, ESM)) { @@ -6405,12 +7356,49 @@ static CPAccessResult access_esm(CPUARMState *env, const ARMCPRegInfo *ri, 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); + } + + /* + * ResetSMEState. + * + * 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. + */ + 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) { - helper_set_pstate_sm(env, FIELD_EX64(value, SVCR, SM)); - helper_set_pstate_za(env, FIELD_EX64(value, SVCR, ZA)); - arm_rebuild_hflags(env); + aarch64_set_svcr(env, value, -1); } static void smcr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6441,6 +7429,7 @@ static const ARMCPRegInfo sme_reginfo[] = { { .name = "TPIDR2_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 13, .crm = 0, .opc2 = 5, .access = PL0_RW, .accessfn = access_tpidr2, + .fgt = FGT_NTPIDR2_EL0, .fieldoffset = offsetof(CPUARMState, cp15.tpidr2_el0) }, { .name = "SVCR", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 4, .crm = 2, .opc2 = 2, @@ -6449,6 +7438,7 @@ static const ARMCPRegInfo sme_reginfo[] = { .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 }, @@ -6477,13 +7467,123 @@ static const ARMCPRegInfo sme_reginfo[] = { */ { .name = "SMPRI_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 4, - .access = PL1_RW, .accessfn = access_esm, + .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, - .access = PL2_RW, .accessfn = access_esm, + .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 }, +}; + +static void aa64_allint_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + env->pstate = (env->pstate & ~PSTATE_ALLINT) | (value & PSTATE_ALLINT); +} + +static uint64_t aa64_allint_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return env->pstate & PSTATE_ALLINT; +} + +static CPAccessResult aa64_allint_access(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if (!isread && arm_current_el(env) == 1 && + (arm_hcrx_el2_eff(env) & HCRX_TALLINT)) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo nmi_reginfo[] = { + { .name = "ALLINT", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 0, .crn = 4, .crm = 3, + .type = ARM_CP_NO_RAW, + .access = PL1_RW, .accessfn = aa64_allint_access, + .fieldoffset = offsetof(CPUARMState, pstate), + .writefn = aa64_allint_write, .readfn = aa64_allint_read, + .resetfn = arm_cp_reset_ignore }, +}; #endif /* TARGET_AARCH64 */ static void define_pmu_regs(ARMCPU *cpu) @@ -6497,18 +7597,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, }; @@ -6523,23 +7627,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 }, @@ -6555,10 +7663,12 @@ static void define_pmu_regs(ARMCPU *cpu) { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid0, 32, 32) }, { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, .resetvalue = extract64(cpu->pmceid1, 32, 32) }, }; define_arm_cp_regs(cpu, v81_pmu_regs); @@ -6568,13 +7678,16 @@ static void define_pmu_regs(ARMCPU *cpu) .name = "PMMIR_EL1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 6, .access = PL1_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMMIR_EL1, .resetvalue = 0 }; define_one_arm_cp_reg(cpu, &v84_pmmir); } } -/* 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. @@ -6590,7 +7703,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); @@ -6603,7 +7715,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, @@ -6639,22 +7752,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 }, }; @@ -6681,93 +7799,115 @@ 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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAAE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 3, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAAE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 5, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RVAALE1", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 6, .opc2 = 7, .access = PL1_W, .accessfn = access_ttlb, .type = ARM_CP_NO_RAW, + .fgt = FGT_TLBIRVAALE1, .writefn = tlbi_aa64_rvae1_write }, { .name = "TLBI_RIPAS2E1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 0, .opc2 = 2, @@ -6838,27 +7978,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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, .accessfn = access_ttlb, .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, @@ -6944,21 +8090,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; @@ -6969,13 +8116,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 }, }; @@ -6983,9 +8136,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) @@ -7001,7 +8154,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))) { @@ -7033,11 +8225,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, @@ -7051,10 +8245,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, @@ -7062,28 +8252,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 }, }; @@ -7097,34 +8295,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, @@ -7132,6 +8338,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, @@ -7140,6 +8347,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 }, }; @@ -7171,14 +8379,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, @@ -7189,6 +8412,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, @@ -7213,24 +8497,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 }, }; @@ -7244,7 +8534,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 }, }; @@ -7319,6 +8609,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, @@ -7341,13 +8632,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, @@ -7364,27 +8657,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[] = { @@ -7430,7 +8727,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); @@ -7444,15 +8742,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, @@ -7544,7 +8851,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); @@ -7669,11 +8977,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, @@ -7714,11 +9022,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = cpu->isar.id_aa64mmfr2 }, - { .name = "ID_AA64MMFR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, + { .name = "ID_AA64MMFR3_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = 0 }, + .resetvalue = cpu->isar.id_aa64mmfr3 }, { .name = "ID_AA64MMFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, @@ -7809,64 +9117,150 @@ 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_AA64MMFR3_EL1", + .exported_bits = 0 }, { .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++) { /* @@ -7914,6 +9308,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, @@ -7925,6 +9320,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) }, }; /* @@ -7947,15 +9343,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); } } @@ -7966,8 +9372,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, @@ -7978,7 +9390,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 @@ -8040,7 +9453,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)) { @@ -8070,13 +9512,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. @@ -8113,12 +9557,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 }, @@ -8126,8 +9568,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", @@ -8137,6 +9585,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", @@ -8160,6 +9609,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, @@ -8168,15 +9624,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. @@ -8193,12 +9654,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); } @@ -8208,6 +9740,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 @@ -8225,6 +9758,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, @@ -8255,7 +9789,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); @@ -8276,7 +9810,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) }; @@ -8294,6 +9828,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 }, @@ -8307,19 +9844,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)) { @@ -8382,7 +9933,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); @@ -8391,7 +9941,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. @@ -8399,6 +9948,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)) { @@ -8409,6 +9965,25 @@ 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); + } + + if (cpu_isar_feature(aa64_nmi, cpu)) { + define_arm_cp_regs(cpu, nmi_reginfo); + } #endif if (cpu_isar_feature(any_predinv, cpu)) { @@ -8430,80 +10005,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; - CPUClass *cc = CPU_CLASS(oc); - const char *typename; - char *name; - - typename = object_class_get_name(oc); - name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_ARM_CPU)); - if (cc->deprecation_note) { - qemu_printf(" %s (deprecated)\n", name); - } else { - 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. @@ -8708,7 +10209,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. @@ -8775,7 +10277,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 @@ -8817,7 +10320,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))) { @@ -8842,7 +10346,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; @@ -8868,8 +10373,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); @@ -8955,7 +10462,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). */ @@ -8976,10 +10484,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 && @@ -9021,10 +10531,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; @@ -9037,7 +10549,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. @@ -9053,7 +10566,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)) { @@ -9065,7 +10579,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)) { @@ -9075,7 +10590,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. */ @@ -9095,7 +10611,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 @@ -9103,7 +10620,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 @@ -9133,66 +10651,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) @@ -9223,15 +10686,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); @@ -9246,7 +10710,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) ] * @@ -9308,7 +10773,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; @@ -9320,7 +10785,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. */ @@ -9330,6 +10796,7 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, hcr_el2 = arm_hcr_el2_eff(env); switch (excp_idx) { case EXCP_IRQ: + case EXCP_NMI: scr = ((env->cp15.scr_el3 & SCR_IRQ) == SCR_IRQ); hcr = hcr_el2 & HCR_IMO; break; @@ -9387,6 +10854,10 @@ 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", + [EXCP_NMI] = "NMI", + [EXCP_VINMI] = "Virtual IRQ NMI", + [EXCP_VFNMI] = "Virtual FIQ NMI", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { @@ -9595,7 +11066,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. */ @@ -9688,7 +11160,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) @@ -9818,6 +11293,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; } @@ -9827,10 +11320,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; @@ -9941,7 +11435,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. @@ -10068,14 +11563,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; @@ -10109,6 +11607,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: /* @@ -10171,10 +11673,13 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) break; case EXCP_IRQ: case EXCP_VIRQ: + case EXCP_NMI: + case EXCP_VINMI: addr += 0x80; break; case EXCP_FIQ: case EXCP_VFIQ: + case EXCP_VFNMI: addr += 0x100; break; case EXCP_VSERR: @@ -10191,6 +11696,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]; @@ -10201,6 +11720,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]); @@ -10236,10 +11756,21 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) } } + if (cpu_isar_feature(aa64_nmi, cpu)) { + if (!(env->cp15.sctlr_el[new_el] & SCTLR_SPINTMASK)) { + new_mode |= PSTATE_ALLINT; + } else { + new_mode &= ~PSTATE_ALLINT; + } + } + 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; @@ -10255,7 +11786,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; @@ -10276,7 +11807,8 @@ static void handle_semihosting(CPUState *cs) } #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. @@ -10303,7 +11835,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; @@ -10316,16 +11848,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); @@ -10346,10 +11879,20 @@ void arm_cpu_do_interrupt(CPUState *cs) uint64_t arm_sctlr(CPUARMState *env, int el) { - /* Only EL0 needs to be adjusted for EL1&0 or EL2&0. */ + /* Only EL0 needs to be adjusted for EL1&0 or EL2&0 or EL3&0 */ if (el == 0) { ARMMMUIdx mmu_idx = arm_mmu_idx_el(env, 0); - el = mmu_idx == ARMMMUIdx_E20_0 ? 2 : 1; + switch (mmu_idx) { + case ARMMMUIdx_E20_0: + el = 2; + break; + case ARMMMUIdx_E30_0: + el = 3; + break; + default: + el = 1; + break; + } } return env->cp15.sctlr_el[el]; } @@ -10378,7 +11921,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); @@ -10473,7 +12016,8 @@ static ARMGranuleSize sanitize_gran_size(ARMCPU *cpu, ARMGranuleSize gran, } ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, - ARMMMUIdx mmu_idx, bool data) + 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; @@ -10569,6 +12113,16 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, } } + 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; @@ -10602,9 +12156,11 @@ ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, }; } -/* 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. */ @@ -10615,10 +12171,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; } @@ -10630,10 +12187,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; } @@ -10645,10 +12203,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; } @@ -10660,10 +12219,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; } @@ -10681,34 +12241,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); @@ -10726,7 +12290,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; \ @@ -10734,7 +12298,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, +) @@ -10753,7 +12317,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; \ @@ -10761,7 +12325,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; \ @@ -10769,7 +12333,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; \ @@ -10777,7 +12341,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 @@ -10812,10 +12376,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. */ @@ -10835,18 +12400,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. */ @@ -10870,7 +12440,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) @@ -10878,7 +12449,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)) { @@ -10903,7 +12475,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 @@ -10987,6 +12560,7 @@ int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) switch (mmu_idx) { case ARMMMUIdx_E10_0: case ARMMMUIdx_E20_0: + case ARMMMUIdx_E30_0: return 0; case ARMMMUIdx_E10_1: case ARMMMUIdx_E10_1_PAN: @@ -10996,6 +12570,7 @@ int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) case ARMMMUIdx_E20_2_PAN: return 2; case ARMMMUIdx_E3: + case ARMMMUIdx_E30_3_PAN: return 3; default: g_assert_not_reached(); @@ -11009,15 +12584,6 @@ ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) } #endif -static bool arm_pan_enabled(CPUARMState *env) -{ - if (is_a64(env)) { - return env->pstate & PSTATE_PAN; - } else { - return env->uncached_cpsr & CPSR_PAN; - } -} - ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) { ARMMMUIdx idx; @@ -11033,6 +12599,9 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) hcr = arm_hcr_el2_eff(env); if ((hcr & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { idx = ARMMMUIdx_E20_0; + } else if (arm_is_secure_below_el3(env) && + !arm_el_is_aa64(env, 3)) { + idx = ARMMMUIdx_E30_0; } else { idx = ARMMMUIdx_E10_0; } @@ -11057,6 +12626,9 @@ ARMMMUIdx arm_mmu_idx_el(CPUARMState *env, int el) } break; case 3: + if (!arm_el_is_aa64(env, 3) && arm_pan_enabled(env)) { + return ARMMMUIdx_E30_3_PAN; + } return ARMMMUIdx_E3; default: g_assert_not_reached(); @@ -11070,339 +12642,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); - } - - 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); -} - -static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, - ARMMMUIdx mmu_idx) -{ - CPUARMTBFlags flags = {}; - 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); - } - - /* - * 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 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 (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: - /* TODO: ARMv8.3-NV */ - 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 (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) { /* @@ -11432,8 +12671,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; @@ -11628,3 +12867,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 92f36d9dbb..58919b670e 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -53,6 +53,7 @@ DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl) DEF_HELPER_1(setend, void, env) DEF_HELPER_2(wfi, void, env, i32) DEF_HELPER_1(wfe, void, env) +DEF_HELPER_2(wfit, void, env, i64) DEF_HELPER_1(yield, void, env) DEF_HELPER_1(pre_hvc, void, env) DEF_HELPER_2(pre_smc, void, env, i32) @@ -79,11 +80,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) @@ -129,12 +133,6 @@ DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, ptr) DEF_HELPER_3(vfp_minnumh, f16, f16, f16, ptr) DEF_HELPER_3(vfp_minnums, f32, f32, f32, ptr) DEF_HELPER_3(vfp_minnumd, f64, f64, f64, ptr) -DEF_HELPER_1(vfp_negh, f16, f16) -DEF_HELPER_1(vfp_negs, f32, f32) -DEF_HELPER_1(vfp_negd, f64, f64) -DEF_HELPER_1(vfp_absh, f16, f16) -DEF_HELPER_1(vfp_abss, f32, f32) -DEF_HELPER_1(vfp_absd, f64, f64) DEF_HELPER_2(vfp_sqrth, f16, f16, env) DEF_HELPER_2(vfp_sqrts, f32, f32, env) DEF_HELPER_2(vfp_sqrtd, f64, f64, env) @@ -271,50 +269,6 @@ DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) /* neon_helper.c */ -DEF_HELPER_FLAGS_3(neon_qadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_u16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_u32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_qadd_s32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_uqadd_s64, TCG_CALL_NO_RWG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(neon_sqadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_sqadd_u16, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_sqadd_u32, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(neon_sqadd_u64, TCG_CALL_NO_RWG, i64, env, i64, i64) -DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32) -DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64) -DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64) - -DEF_HELPER_2(neon_hadd_s8, i32, i32, i32) -DEF_HELPER_2(neon_hadd_u8, i32, i32, i32) -DEF_HELPER_2(neon_hadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_hadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_hadd_s32, s32, s32, s32) -DEF_HELPER_2(neon_hadd_u32, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s8, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_u8, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s16, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_u16, i32, i32, i32) -DEF_HELPER_2(neon_rhadd_s32, s32, s32, s32) -DEF_HELPER_2(neon_rhadd_u32, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s8, i32, i32, i32) -DEF_HELPER_2(neon_hsub_u8, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s16, i32, i32, i32) -DEF_HELPER_2(neon_hsub_u16, i32, i32, i32) -DEF_HELPER_2(neon_hsub_s32, s32, s32, s32) -DEF_HELPER_2(neon_hsub_u32, i32, i32, i32) - DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) @@ -354,11 +308,47 @@ DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(neon_sqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_urshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_2(neon_add_u8, i32, i32, i32) DEF_HELPER_2(neon_add_u16, i32, i32, i32) -DEF_HELPER_2(neon_padd_u8, i32, i32, i32) -DEF_HELPER_2(neon_padd_u16, i32, i32, i32) DEF_HELPER_2(neon_sub_u8, i32, i32, i32) DEF_HELPER_2(neon_sub_u16, i32, i32, i32) DEF_HELPER_2(neon_mul_u8, i32, i32, i32) @@ -385,17 +375,17 @@ DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) DEF_HELPER_4(neon_qrdmlah_s32, i32, env, s32, s32, s32) DEF_HELPER_4(neon_qrdmlsh_s32, i32, env, s32, s32, s32) -DEF_HELPER_1(neon_narrow_u8, i32, i64) -DEF_HELPER_1(neon_narrow_u16, i32, i64) -DEF_HELPER_2(neon_unarrow_sat8, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_u8, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_s8, i32, env, i64) -DEF_HELPER_2(neon_unarrow_sat16, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_u16, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_s16, i32, env, i64) -DEF_HELPER_2(neon_unarrow_sat32, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_u32, i32, env, i64) -DEF_HELPER_2(neon_narrow_sat_s32, i32, env, i64) +DEF_HELPER_1(neon_narrow_u8, i64, i64) +DEF_HELPER_1(neon_narrow_u16, i64, i64) +DEF_HELPER_2(neon_unarrow_sat8, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_u8, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_s8, i64, env, i64) +DEF_HELPER_2(neon_unarrow_sat16, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_u16, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_s16, i64, env, i64) +DEF_HELPER_2(neon_unarrow_sat32, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_u32, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_s32, i64, env, i64) DEF_HELPER_1(neon_narrow_high_u8, i32, i64) DEF_HELPER_1(neon_narrow_high_u16, i32, i64) DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) @@ -551,7 +541,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) @@ -651,13 +643,6 @@ DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_paddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pmaxh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pminh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_padds, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pmaxs, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_pmins, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -725,33 +710,43 @@ DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -767,9 +762,11 @@ DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -834,6 +831,22 @@ DEF_HELPER_FLAGS_5(gvec_sqsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_sqsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) @@ -968,6 +981,26 @@ DEF_HELPER_FLAGS_5(neon_sqrdmulh_h, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(neon_sqrdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve2_sqdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -1006,13 +1039,13 @@ DEF_HELPER_FLAGS_5(gvec_ummla_b, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_usmmla_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_bfdot, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_bfdot_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_bfdot, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(gvec_bfdot_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(gvec_bfmmla, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) @@ -1037,10 +1070,51 @@ DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_addp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_smaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 -#include "helper-a64.h" -#include "helper-sve.h" -#include "helper-sme.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..ca7ea92774 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -22,18 +22,135 @@ #include #include "exec/address-spaces.h" +#include "hw/boards.h" #include "hw/irq.h" #include "qemu/main-loop.h" #include "sysemu/cpus.h" #include "arm-powerctl.h" #include "target/arm/cpu.h" #include "target/arm/internals.h" -#include "trace/trace-target_arm_hvf.h" +#include "target/arm/multiprocessing.h" +#include "target/arm/gtimer.h" +#include "trace.h" #include "migration/vmstate.h" +#include "gdbstub/enums.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 #define SYSREG_OP0_SHIFT 20 #define SYSREG_OP0_MASK 0x3 @@ -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) @@ -88,6 +298,8 @@ static void hvf_wfi(CPUState *cpu); +static uint32_t chosen_ipa_bit_size; + typedef struct HVFVTimer { /* Vtimer value during migration and paused state */ uint64_t vtimer_val; @@ -187,85 +399,85 @@ struct hvf_sreg_match { }; static struct hvf_sreg_match hvf_sreg_match[] = { - { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR0_EL1, HVF_SYSREG(0, 0, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR0_EL1, HVF_SYSREG(0, 0, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR1_EL1, HVF_SYSREG(0, 1, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR1_EL1, HVF_SYSREG(0, 1, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR2_EL1, HVF_SYSREG(0, 2, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR2_EL1, HVF_SYSREG(0, 2, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR3_EL1, HVF_SYSREG(0, 3, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR3_EL1, HVF_SYSREG(0, 3, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR4_EL1, HVF_SYSREG(0, 4, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR4_EL1, HVF_SYSREG(0, 4, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR5_EL1, HVF_SYSREG(0, 5, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR5_EL1, HVF_SYSREG(0, 5, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR6_EL1, HVF_SYSREG(0, 6, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR6_EL1, HVF_SYSREG(0, 6, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR7_EL1, HVF_SYSREG(0, 7, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR7_EL1, HVF_SYSREG(0, 7, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR8_EL1, HVF_SYSREG(0, 8, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR8_EL1, HVF_SYSREG(0, 8, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR9_EL1, HVF_SYSREG(0, 9, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR9_EL1, HVF_SYSREG(0, 9, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR10_EL1, HVF_SYSREG(0, 10, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR10_EL1, HVF_SYSREG(0, 10, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR11_EL1, HVF_SYSREG(0, 11, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR11_EL1, HVF_SYSREG(0, 11, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR12_EL1, HVF_SYSREG(0, 12, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR12_EL1, HVF_SYSREG(0, 12, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR13_EL1, HVF_SYSREG(0, 13, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR13_EL1, HVF_SYSREG(0, 13, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR14_EL1, HVF_SYSREG(0, 14, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR14_EL1, HVF_SYSREG(0, 14, 2, 0, 7) }, - { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 14, 0, 4) }, - { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 14, 0, 5) }, - { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 14, 0, 6) }, - { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 14, 0, 7) }, + { HV_SYS_REG_DBGBVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 4) }, + { HV_SYS_REG_DBGBCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 5) }, + { HV_SYS_REG_DBGWVR15_EL1, HVF_SYSREG(0, 15, 2, 0, 6) }, + { HV_SYS_REG_DBGWCR15_EL1, HVF_SYSREG(0, 15, 2, 0, 7) }, #ifdef SYNC_NO_RAW_REGS /* @@ -277,7 +489,7 @@ static struct hvf_sreg_match hvf_sreg_match[] = { { HV_SYS_REG_MPIDR_EL1, HVF_SYSREG(0, 0, 3, 0, 5) }, { HV_SYS_REG_ID_AA64PFR0_EL1, HVF_SYSREG(0, 4, 3, 0, 0) }, #endif - { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 2) }, + { HV_SYS_REG_ID_AA64PFR1_EL1, HVF_SYSREG(0, 4, 3, 0, 1) }, { HV_SYS_REG_ID_AA64DFR0_EL1, HVF_SYSREG(0, 5, 3, 0, 0) }, { HV_SYS_REG_ID_AA64DFR1_EL1, HVF_SYSREG(0, 5, 3, 0, 1) }, { HV_SYS_REG_ID_AA64ISAR0_EL1, HVF_SYSREG(0, 6, 3, 0, 0) }, @@ -288,6 +500,7 @@ static struct hvf_sreg_match hvf_sreg_match[] = { #endif { HV_SYS_REG_ID_AA64MMFR1_EL1, HVF_SYSREG(0, 7, 3, 0, 1) }, { HV_SYS_REG_ID_AA64MMFR2_EL1, HVF_SYSREG(0, 7, 3, 0, 2) }, + /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ { HV_SYS_REG_MDSCR_EL1, HVF_SYSREG(0, 2, 2, 0, 2) }, { HV_SYS_REG_SCTLR_EL1, HVF_SYSREG(1, 0, 3, 0, 0) }, @@ -339,29 +552,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 +583,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 +692,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 +720,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; @@ -434,9 +809,9 @@ int hvf_put_registers(CPUState *cpu) static void flush_cpu_state(CPUState *cpu) { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { hvf_put_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } } @@ -447,7 +822,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,13 +835,23 @@ 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); } return val; } +static void clamp_id_aa64mmfr0_parange_to_ipa_size(uint64_t *id_aa64mmfr0) +{ + uint32_t ipa_size = chosen_ipa_bit_size ? + chosen_ipa_bit_size : hvf_arm_get_max_ipa_bit_size(); + + /* Clamp down the PARange to the IPA size the kernel supports. */ + uint8_t index = round_down_to_parange_index(ipa_size); + *id_aa64mmfr0 = (*id_aa64mmfr0 & ~R_ID_AA64MMFR0_PARANGE_MASK) | index; +} + static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { ARMISARegisters host_isar = {}; @@ -480,9 +865,11 @@ 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 }, + /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ }; hv_vcpu_t fd; hv_return_t r = HV_SUCCESS; @@ -508,6 +895,8 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) r |= hv_vcpu_get_sys_reg(fd, HV_SYS_REG_MIDR_EL1, &ahcf->midr); r |= hv_vcpu_destroy(fd); + clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar.id_aa64mmfr0); + ahcf->isar = host_isar; /* @@ -530,6 +919,30 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) return r == HV_SUCCESS; } +uint32_t hvf_arm_get_default_ipa_bit_size(void) +{ + uint32_t default_ipa_size; + hv_return_t ret = hv_vm_config_get_default_ipa_size(&default_ipa_size); + assert_hvf_ok(ret); + + return default_ipa_size; +} + +uint32_t hvf_arm_get_max_ipa_bit_size(void) +{ + uint32_t max_ipa_size; + hv_return_t ret = hv_vm_config_get_max_ipa_size(&max_ipa_size); + assert_hvf_ok(ret); + + /* + * We clamp any IPA size we want to back the VM with to a valid PARange + * value so the guest doesn't try and map memory outside of the valid range. + * This logic just clamps the passed in IPA bit size to the first valid + * PARange value <= to it. + */ + return round_down_to_parange_bit_size(max_ipa_size); +} + void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu) { if (!arm_host_cpu_features.dtb_compatible) { @@ -555,6 +968,25 @@ void hvf_arch_vcpu_destroy(CPUState *cpu) { } +hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) +{ + hv_return_t ret; + hv_vm_config_t config = hv_vm_config_create(); + + ret = hv_vm_config_set_ipa_size(config, pa_range); + if (ret != HV_SUCCESS) { + goto cleanup; + } + chosen_ipa_bit_size = pa_range; + + ret = hv_vm_create(config); + +cleanup: + os_release(config); + + return ret; +} + int hvf_arch_init_vcpu(CPUState *cpu) { ARMCPU *arm_cpu = ARM_CPU(cpu); @@ -602,32 +1034,37 @@ 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); + clamp_id_aa64mmfr0_parange_to_ipa_size(&arm_cpu->isar.id_aa64mmfr0); + ret = hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_ID_AA64MMFR0_EL1, + arm_cpu->isar.id_aa64mmfr0); + assert_hvf_ok(ret); + return 0; } 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 +1082,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 +1111,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,84 +1225,217 @@ static bool is_id_sysreg(uint32_t reg) SYSREG_CRM(reg) < 8; } -static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint32_t rt) +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; - uint64_t val = 0; + 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, uint64_t *val) +{ + ARMCPU *arm_cpu = ARM_CPU(cpu); + CPUARMState *env = &arm_cpu->env; + + if (arm_feature(env, ARM_FEATURE_PMU)) { + switch (reg) { + case SYSREG_PMCR_EL0: + *val = env->cp15.c9_pmcr; + return 0; + case SYSREG_PMCCNTR_EL0: + pmu_op_start(env); + *val = env->cp15.c15_ccnt; + pmu_op_finish(env); + return 0; + case SYSREG_PMCNTENCLR_EL0: + *val = env->cp15.c9_pmcnten; + return 0; + case SYSREG_PMOVSCLR_EL0: + *val = env->cp15.c9_pmovsr; + return 0; + case SYSREG_PMSELR_EL0: + *val = env->cp15.c9_pmselr; + return 0; + case SYSREG_PMINTENCLR_EL1: + *val = env->cp15.c9_pminten; + return 0; + case SYSREG_PMCCFILTR_EL0: + *val = env->cp15.pmccfiltr_el0; + return 0; + case SYSREG_PMCNTENSET_EL0: + *val = env->cp15.c9_pmcnten; + return 0; + case SYSREG_PMUSERENR_EL0: + *val = env->cp15.c9_pmuserenr; + return 0; + case SYSREG_PMCEID0_EL0: + case SYSREG_PMCEID1_EL0: + /* We can't really count anything yet, declare all events invalid */ + *val = 0; + return 0; + } + } switch (reg) { case SYSREG_CNTPCT_EL0: - val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / + *val = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(arm_cpu); - break; - case SYSREG_PMCR_EL0: - val = env->cp15.c9_pmcr; - break; - case SYSREG_PMCCNTR_EL0: - pmu_op_start(env); - val = env->cp15.c15_ccnt; - pmu_op_finish(env); - break; - case SYSREG_PMCNTENCLR_EL0: - val = env->cp15.c9_pmcnten; - break; - case SYSREG_PMOVSCLR_EL0: - val = env->cp15.c9_pmovsr; - break; - case SYSREG_PMSELR_EL0: - val = env->cp15.c9_pmselr; - break; - case SYSREG_PMINTENCLR_EL1: - val = env->cp15.c9_pminten; - break; - case SYSREG_PMCCFILTR_EL0: - val = env->cp15.pmccfiltr_el0; - break; - case SYSREG_PMCNTENSET_EL0: - val = env->cp15.c9_pmcnten; - break; - case SYSREG_PMUSERENR_EL0: - val = env->cp15.c9_pmuserenr; - break; - case SYSREG_PMCEID0_EL0: - case SYSREG_PMCEID1_EL0: - /* We can't really count anything yet, declare all events invalid */ - val = 0; - break; + return 0; case SYSREG_OSLSR_EL1: - val = env->cp15.oslsr_el1; - break; + *val = env->cp15.oslsr_el1; + return 0; case SYSREG_OSDLR_EL1: /* Dummy register */ + return 0; + 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)) { + return 0; + } 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)]; + return 0; + 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)]; + return 0; + 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)]; + return 0; + 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)]; + return 0; default: if (is_id_sysreg(reg)) { /* ID system registers read as RES0 */ - val = 0; - break; + *val = 0; + return 0; } - cpu_synchronize_state(cpu); - trace_hvf_unhandled_sysreg_read(env->pc, reg, - SYSREG_OP0(reg), - SYSREG_OP1(reg), - SYSREG_CRN(reg), - SYSREG_CRM(reg), - SYSREG_OP2(reg)); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); - return 1; } - trace_hvf_sysreg_read(reg, - SYSREG_OP0(reg), - SYSREG_OP1(reg), - SYSREG_CRN(reg), - SYSREG_CRM(reg), - SYSREG_OP2(reg), - val); - hvf_set_reg(cpu, rt, val); - - return 0; + cpu_synchronize_state(cpu); + trace_hvf_unhandled_sysreg_read(env->pc, reg, + SYSREG_OP0(reg), + SYSREG_OP1(reg), + SYSREG_CRN(reg), + SYSREG_CRM(reg), + SYSREG_OP2(reg)); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + return 1; } static void pmu_update_irq(CPUARMState *env) @@ -944,6 +1514,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); @@ -957,96 +1554,204 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) SYSREG_OP2(reg), val); - switch (reg) { - case SYSREG_PMCCNTR_EL0: - pmu_op_start(env); - env->cp15.c15_ccnt = val; - pmu_op_finish(env); - break; - case SYSREG_PMCR_EL0: - pmu_op_start(env); + if (arm_feature(env, ARM_FEATURE_PMU)) { + switch (reg) { + case SYSREG_PMCCNTR_EL0: + pmu_op_start(env); + env->cp15.c15_ccnt = val; + pmu_op_finish(env); + return 0; + case SYSREG_PMCR_EL0: + pmu_op_start(env); - if (val & PMCRC) { - /* The counter has been reset */ - env->cp15.c15_ccnt = 0; - } - - if (val & PMCRP) { - unsigned int i; - for (i = 0; i < pmu_num_counters(env); i++) { - env->cp15.c14_pmevcntr[i] = 0; + if (val & PMCRC) { + /* The counter has been reset */ + env->cp15.c15_ccnt = 0; } + + if (val & PMCRP) { + unsigned int i; + for (i = 0; i < pmu_num_counters(env); i++) { + env->cp15.c14_pmevcntr[i] = 0; + } + } + + env->cp15.c9_pmcr &= ~PMCR_WRITABLE_MASK; + env->cp15.c9_pmcr |= (val & PMCR_WRITABLE_MASK); + + pmu_op_finish(env); + return 0; + case SYSREG_PMUSERENR_EL0: + env->cp15.c9_pmuserenr = val & 0xf; + return 0; + case SYSREG_PMCNTENSET_EL0: + env->cp15.c9_pmcnten |= (val & pmu_counter_mask(env)); + return 0; + case SYSREG_PMCNTENCLR_EL0: + env->cp15.c9_pmcnten &= ~(val & pmu_counter_mask(env)); + return 0; + case SYSREG_PMINTENCLR_EL1: + pmu_op_start(env); + env->cp15.c9_pminten |= val; + pmu_op_finish(env); + return 0; + case SYSREG_PMOVSCLR_EL0: + pmu_op_start(env); + env->cp15.c9_pmovsr &= ~val; + pmu_op_finish(env); + return 0; + case SYSREG_PMSWINC_EL0: + pmu_op_start(env); + pmswinc_write(env, val); + pmu_op_finish(env); + return 0; + case SYSREG_PMSELR_EL0: + env->cp15.c9_pmselr = val & 0x1f; + return 0; + case SYSREG_PMCCFILTR_EL0: + pmu_op_start(env); + env->cp15.pmccfiltr_el0 = val & PMCCFILTR_EL0; + pmu_op_finish(env); + return 0; } - - env->cp15.c9_pmcr &= ~PMCR_WRITABLE_MASK; - env->cp15.c9_pmcr |= (val & PMCR_WRITABLE_MASK); - - pmu_op_finish(env); - break; - case SYSREG_PMUSERENR_EL0: - env->cp15.c9_pmuserenr = val & 0xf; - break; - case SYSREG_PMCNTENSET_EL0: - env->cp15.c9_pmcnten |= (val & pmu_counter_mask(env)); - break; - case SYSREG_PMCNTENCLR_EL0: - env->cp15.c9_pmcnten &= ~(val & pmu_counter_mask(env)); - break; - case SYSREG_PMINTENCLR_EL1: - pmu_op_start(env); - env->cp15.c9_pminten |= val; - pmu_op_finish(env); - break; - case SYSREG_PMOVSCLR_EL0: - pmu_op_start(env); - env->cp15.c9_pmovsr &= ~val; - pmu_op_finish(env); - break; - case SYSREG_PMSWINC_EL0: - pmu_op_start(env); - pmswinc_write(env, val); - pmu_op_finish(env); - break; - case SYSREG_PMSELR_EL0: - env->cp15.c9_pmselr = val & 0x1f; - break; - case SYSREG_PMCCFILTR_EL0: - pmu_op_start(env); - env->cp15.pmccfiltr_el0 = val & PMCCFILTR_EL0; - pmu_op_finish(env); - break; - case SYSREG_OSLAR_EL1: - env->cp15.oslsr_el1 = val & 1; - break; - case SYSREG_OSDLR_EL1: - /* Dummy register */ - break; - default: - cpu_synchronize_state(cpu); - trace_hvf_unhandled_sysreg_write(env->pc, reg, - SYSREG_OP0(reg), - SYSREG_OP1(reg), - SYSREG_CRN(reg), - SYSREG_CRM(reg), - SYSREG_OP2(reg)); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); - return 1; } - return 0; + switch (reg) { + case SYSREG_OSLAR_EL1: + env->cp15.oslsr_el1 = val & 1; + return 0; + case SYSREG_OSDLR_EL1: + /* Dummy register */ + return 0; + 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)) { + return 0; + } + break; + case SYSREG_MDSCR_EL1: + env->cp15.mdscr_el1 = val; + return 0; + 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; + return 0; + 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; + return 0; + 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; + return 0; + 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; + return 0; + } + + cpu_synchronize_state(cpu); + trace_hvf_unhandled_sysreg_write(env->pc, reg, + SYSREG_OP0(reg), + SYSREG_OP1(reg), + SYSREG_CRN(reg), + SYSREG_CRM(reg), + SYSREG_OP2(reg)); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + return 1; } 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 +1783,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 +1806,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 +1815,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 +1848,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 +1862,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 +1871,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 +1887,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 +1915,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 +2000,26 @@ 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, &val); + if (!sysreg_ret) { + trace_hvf_sysreg_read(reg, + SYSREG_OP0(reg), + SYSREG_OP1(reg), + SYSREG_CRN(reg), + SYSREG_CRM(reg), + SYSREG_OP2(reg), + val); + hvf_set_reg(cpu, rt, val); + } } 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 +2067,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 +2111,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/trace.h b/target/arm/hvf/trace.h new file mode 100644 index 0000000000..04a19c1d75 --- /dev/null +++ b/target/arm/hvf/trace.h @@ -0,0 +1 @@ +#include "trace/trace-target_arm_hvf.h" diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index 9a9d1a0bf5..26c717b382 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -13,6 +13,32 @@ #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); +#ifdef CONFIG_HVF + +uint32_t hvf_arm_get_default_ipa_bit_size(void); +uint32_t hvf_arm_get_max_ipa_bit_size(void); + +#else + +static inline uint32_t hvf_arm_get_default_ipa_bit_size(void) +{ + return 0; +} + +static inline uint32_t hvf_arm_get_max_ipa_bit_size(void) +{ + return 0; +} + +#endif + #endif diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c new file mode 100644 index 0000000000..1e861263b3 --- /dev/null +++ b/target/arm/hyp_gdbstub.c @@ -0,0 +1,252 @@ +/* + * 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 "gdbstub/enums.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(); + } + 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 161e42d50f..e37f459af3 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -25,9 +25,11 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +#include "exec/breakpoint.h" #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 +41,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 @@ -53,10 +60,19 @@ static inline bool excp_is_internal(int excp) || excp == EXCP_SEMIHOST; } -/* Scale factor for generic timers, ie number of ns per tick. - * This gives a 62.5MHz timer. +/* + * Default frequency for the generic timer, in Hz. + * ARMv8.6 and later CPUs architecturally must use a 1GHz timer; before + * that it was an IMPDEF choice, and QEMU initially picked 62.5MHz, + * which gives a 16ns tick period. + * + * We will use the back-compat value: + * - for QEMU CPU types added before we standardized on 1GHz + * - for versioned machine types with a version of 9.0 or earlier + * In any case, the machine model may override via the cntfrq property. */ -#define GTIMER_SCALE 16 +#define GTIMER_DEFAULT_HZ 1000000000 +#define GTIMER_BACKCOMPAT_HZ 62500000 /* Bit definitions for the v7M CONTROL register */ FIELD(V7M_CONTROL, NPRIV, 0, 1) @@ -93,6 +109,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,27 +349,46 @@ 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_cpu_register_gdb_commands(ARMCPU *cpu); +void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, + GPtrArray *, GPtrArray *); + 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); + +/* Our implementation of TCGCPUOps::cpu_exec_halt */ +bool arm_cpu_exec_halt(CPUState *cs); #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) { @@ -250,6 +436,25 @@ static inline void update_spsel(CPUARMState *env, uint32_t imm) */ unsigned int arm_pamax(ARMCPU *cpu); +/* + * round_down_to_parange_index + * @bit_size: uint8_t + * + * Rounds down the bit_size supplied to the first supported ARM physical + * address range and returns the index for this. The index is intended to + * be used to set ID_AA64MMFR0_EL1's PARANGE bits. + */ +uint8_t round_down_to_parange_index(uint8_t bit_size); + +/* + * round_down_to_parange_bit_size + * @bit_size: uint8_t + * + * Rounds down the bit_size supplied to the first supported ARM physical + * address range bit size and returns this. + */ +uint8_t round_down_to_parange_bit_size(uint8_t bit_size); + /* Return true if extended addresses are enabled. * This is always the case if our translation regime is 64 bit, * but depends on TTBCR.EAE for 32 bit. @@ -257,6 +462,10 @@ unsigned int arm_pamax(ARMCPU *cpu); static inline bool extended_addresses_enabled(CPUARMState *env) { 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 & TTBCR_EAE)); } @@ -348,14 +557,27 @@ typedef enum ARMFaultType { 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 @@ -364,7 +586,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; @@ -538,6 +763,17 @@ static inline uint32_t arm_fi_to_lfsc(ARMMMUFaultInfo *fi) 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. @@ -566,9 +802,9 @@ void arm_cpu_record_sigsegv(CPUState *cpu, vaddr addr, void arm_cpu_record_sigbus(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra); #else -bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); +bool arm_cpu_tlb_fill_align(CPUState *cs, CPUTLBEntryFull *out, vaddr addr, + MMUAccessType access_type, int mmu_idx, + MemOp memop, int size, bool probe, uintptr_t ra); #endif static inline int arm_to_core_mmu_idx(ARMMMUIdx mmu_idx) @@ -593,26 +829,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 @@ -624,6 +843,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 @@ -633,6 +853,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) @@ -650,7 +871,16 @@ static inline void arm_call_el_change_hook(ARMCPU *cpu) } } -/* Return true if this address translation regime has two ranges. */ +/* + * Return true if this address translation regime has two ranges. + * Note that this will not return the correct answer for AArch32 + * Secure PL1&0 (i.e. mmu indexes E3, E30_0, E30_3_PAN), but it is + * never called from a context where EL3 can be AArch32. (The + * correct return value for ARMMMUIdx_E3 would be different for + * that case, so we can't just make the function return the + * correct value anyway; we would need an extra "bool e3_is_aarch32" + * argument which all the current callsites would pass as 'false'.) + */ static inline bool regime_has_2_ranges(ARMMMUIdx mmu_idx) { switch (mmu_idx) { @@ -675,6 +905,7 @@ static inline bool regime_is_pan(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_Stage1_E1_PAN: case ARMMMUIdx_E10_1_PAN: case ARMMMUIdx_E20_2_PAN: + case ARMMMUIdx_E30_3_PAN: return true; default: return false; @@ -698,10 +929,11 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) case ARMMMUIdx_E2: return 2; case ARMMMUIdx_E3: + case ARMMMUIdx_E30_0: + case ARMMMUIdx_E30_3_PAN: return 3; 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_E10_1: @@ -723,7 +955,9 @@ 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_E10_0: case ARMMMUIdx_E20_0: + case ARMMMUIdx_E30_0: case ARMMMUIdx_Stage1_E0: case ARMMMUIdx_MUser: case ARMMMUIdx_MSUser: @@ -732,10 +966,6 @@ static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) return true; default: return false; - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - g_assert_not_reached(); } } @@ -777,6 +1007,24 @@ static inline uint64_t regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) return env->cp15.tcr_el[regime_el(env, mmu_idx)]; } +/* Return true if the translation regime is using LPAE format page tables */ +static inline 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_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; +} + /** * arm_num_brps: Return number of implemented breakpoints. * Note that the ID register BRPS field is "number of bps - 1", @@ -893,7 +1141,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); @@ -902,10 +1150,28 @@ 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); +/** + * arm_cpu_update_vinmi: Update CPU_INTERRUPT_VINMI bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VINMI bit in cs->interrupt_request, following + * a change to either the input VNMI line from the GIC or the HCRX_EL2.VINMI. + * Must be called with the BQL held. + */ +void arm_cpu_update_vinmi(ARMCPU *cpu); + +/** + * arm_cpu_update_vfnmi: Update CPU_INTERRUPT_VFNMI bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VFNMI bit in cs->interrupt_request, following + * a change to the HCRX_EL2.VFNMI. + * Must be called with the BQL held. + */ +void arm_cpu_update_vfnmi(ARMCPU *cpu); + /** * arm_cpu_update_vserr: Update CPU_INTERRUPT_VSERR bit * @@ -1026,6 +1292,9 @@ static inline uint32_t aarch64_pstate_valid_mask(const ARMISARegisters *id) if (isar_feature_aa64_mte(id)) { valid |= PSTATE_TCO; } + if (isar_feature_aa64_nmi(id)) { + valid |= PSTATE_ALLINT; + } return valid; } @@ -1078,11 +1347,22 @@ typedef struct ARMVAParameters { 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); +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, @@ -1129,7 +1409,6 @@ typedef struct ARMCacheAttrs { unsigned int attrs:8; unsigned int shareability:2; /* as in the SH field of the VMSAv8-64 PTEs */ bool is_s2_format:1; - bool guarded:1; /* guarded bit of the v8-64 PTE */ } ARMCacheAttrs; /* Fields that are valid upon success. */ @@ -1139,12 +1418,12 @@ typedef struct GetPhysAddrResult { } GetPhysAddrResult; /** - * get_phys_addr_with_secure: get the physical address for a virtual address + * 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 + * @memop: memory operation feeding this access, or 0 for none * @mmu_idx: MMU index indicating required translation regime - * @is_secure: security state for the access * @result: set on translation success. * @fi: set to fault info if the translation fails * @@ -1161,26 +1440,31 @@ typedef struct GetPhysAddrResult { * * for PSMAv5 based systems we don't bother to return a full FSR format * value. */ -bool get_phys_addr_with_secure(CPUARMState *env, target_ulong address, - MMUAccessType access_type, - ARMMMUIdx mmu_idx, bool is_secure, - GetPhysAddrResult *result, ARMMMUFaultInfo *fi) +bool get_phys_addr(CPUARMState *env, vaddr address, + MMUAccessType access_type, MemOp memop, ARMMMUIdx mmu_idx, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) __attribute__((nonnull)); /** - * get_phys_addr: get the physical address for a virtual address + * 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 + * @memop: memory operation feeding this access, or 0 for none * @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 * - * Similarly, but use the security regime of @mmu_idx. + * 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(CPUARMState *env, target_ulong address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - GetPhysAddrResult *result, ARMMMUFaultInfo *fi) +bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, + MMUAccessType access_type, MemOp memop, + ARMMMUIdx mmu_idx, ARMSecuritySpace space, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) __attribute__((nonnull)); bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, @@ -1192,12 +1476,6 @@ 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 - /* * SVE predicates are 1/8 the size of SVE vectors, and cannot use * the same simd_desc() encoding due to restrictions on size. @@ -1218,11 +1496,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); @@ -1336,21 +1670,37 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) } #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); +int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_tag_ctl_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); @@ -1359,6 +1709,21 @@ 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); @@ -1373,4 +1738,83 @@ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) ((1 << (1 - 1)) | (1 << (2 - 1)) | \ (1 << (4 - 1)) | (1 << (8 - 1)) | (1 << (16 - 1))) +/* + * Return true if it is possible to take a fine-grained-trap to EL2. + */ +static inline bool arm_fgt_active(CPUARMState *env, int el) +{ + /* + * The Arm ARM only requires the "{E2H,TGE} != {1,1}" test for traps + * that can affect EL0, but it is harmless to do the test also for + * traps on registers that are only accessible at EL1 because if the test + * returns true then we can't be executing at EL1 anyway. + * FGT traps only happen when EL2 is enabled and EL1 is AArch64; + * traps from AArch32 only happen for the EL0 is AArch32 case. + */ + return cpu_isar_feature(aa64_fgt, env_archcpu(env)) && + el < 2 && arm_is_el2_enabled(env) && + arm_el_is_aa64(env, 1) && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE) && + (!arm_feature(env, ARM_FEATURE_EL3) || (env->cp15.scr_el3 & SCR_FGTEN)); +} + +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); + +/* Return the current value of the system counter in ticks */ +uint64_t gt_get_countervalue(CPUARMState *env); +/* + * Return the currently applicable offset between the system counter + * and CNTVCT_EL0 (this will be either 0 or the value of CNTVOFF_EL2). + */ +uint64_t gt_virt_cnt_offset(CPUARMState *env); #endif diff --git a/target/arm/kvm-consts.h b/target/arm/kvm-consts.h index faacf96fdc..c44d23dbe7 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 COMPILING_PER_TARGET #ifdef CONFIG_KVM #include #include - #define MISMATCH_CHECK(X, Y) QEMU_BUILD_BUG_ON(X != Y) +#endif +#endif /* COMPILING_PER_TARGET */ -#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 84da49332c..7b6812c0de 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,11 +31,18 @@ #include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" +#include "gdbstub/enums.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" +#include "migration/blocker.h" const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_INFO(DEVICE_CTRL), KVM_CAP_LAST_INFO }; @@ -40,28 +50,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, @@ -85,6 +121,21 @@ bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, if (vmfd < 0) { goto err; } + + /* + * The MTE capability must be enabled by the VMM before creating + * any VCPUs in order to allow the MTE bits of the ID_AA64PFR1 + * register to be probed correctly, as they are masked if MTE + * is not enabled. + */ + if (kvm_arm_mte_supported()) { + KVMState kvm_state; + + kvm_state.fd = kvmfd; + kvm_state.vmfd = vmfd; + kvm_vm_enable_cap(&kvm_state, KVM_CAP_ARM_MTE, 0); + } + cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0); if (cpufd < 0) { goto err; @@ -166,6 +217,262 @@ 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; + features |= 1ULL << ARM_FEATURE_PMU; + } + + 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)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr3, + ARM64_SYS_REG(3, 0, 0, 7, 3)); + + /* + * 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_GENERIC_TIMER; + + ahcf->features = features; + + return true; +} + void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -209,10 +516,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; @@ -247,6 +554,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; @@ -263,6 +577,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 " @@ -280,7 +598,33 @@ int kvm_arch_init(MachineState *ms, KVMState *s) } } - kvm_arm_init_debug(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; } @@ -341,25 +685,18 @@ 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) { struct kvm_device_attr *attr = &kd->kdattr; int ret; + uint64_t addr = kd->kda.addr; - /* If the device control API is available and we have a device fd on the - * KVMDevice struct, let's use the newer API - */ - if (kd->dev_fd >= 0) { - uint64_t addr = kd->kda.addr; - - addr |= kd->kda_addr_ormask; - attr->addr = (uintptr_t)&addr; - ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); - } else { - ret = kvm_vm_ioctl(kvm_state, KVM_ARM_SET_DEVICE_ADDR, &kd->kda); - } + addr |= kd->kda_addr_ormask; + attr->addr = (uintptr_t)&addr; + ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); if (ret < 0) { fprintf(stderr, "Failed to set device address: %s\n", @@ -440,11 +777,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; @@ -517,6 +879,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); @@ -524,24 +908,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(); @@ -560,7 +939,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; @@ -569,19 +947,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 @@ -617,7 +993,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(); @@ -639,58 +1015,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(); @@ -699,20 +1067,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(); @@ -721,7 +1090,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; @@ -750,7 +1127,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; @@ -774,6 +1159,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); @@ -788,7 +1230,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 " @@ -820,7 +1262,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], @@ -849,41 +1291,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 @@ -899,7 +1339,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; } @@ -912,19 +1352,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: @@ -945,12 +1463,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); } @@ -1056,11 +1609,862 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) return (data - 32) & 0xffff; } -bool kvm_arch_cpu_check_are_resettable(void) +static void kvm_arch_get_eager_split_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { - return true; + 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); +} + +bool kvm_arm_mte_supported(void) +{ + return kvm_check_extension(kvm_state, KVM_CAP_ARM_MTE); +} + +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 (cpu->has_pmu) { + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3; + } + 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, Error **errp) +{ + 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, Error **errp) +{ + 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; +} + +void kvm_arm_enable_mte(Object *cpuobj, Error **errp) +{ + static bool tried_to_enable; + static bool succeeded_to_enable; + Error *mte_migration_blocker = NULL; + ARMCPU *cpu = ARM_CPU(cpuobj); + int ret; + + if (!tried_to_enable) { + /* + * MTE on KVM is enabled on a per-VM basis (and retrying doesn't make + * sense), and we only want a single migration blocker as well. + */ + tried_to_enable = true; + + ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_MTE, 0); + if (ret) { + error_setg_errno(errp, -ret, "Failed to enable KVM_CAP_ARM_MTE"); + return; + } + + /* TODO: Add migration support with MTE enabled */ + error_setg(&mte_migration_blocker, + "Live migration disabled due to MTE enabled"); + if (migrate_add_blocker(&mte_migration_blocker, errp)) { + error_free(mte_migration_blocker); + return; + } + + succeeded_to_enable = true; + } + + if (succeeded_to_enable) { + cpu->kvm_mte = true; + } } diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c deleted file mode 100644 index 810db33ccb..0000000000 --- a/target/arm/kvm64.c +++ /dev/null @@ -1,1622 +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)) - -void kvm_arm_init_debug(KVMState *s) -{ - have_guest_debug = kvm_check_extension(s, - KVM_CAP_SET_GUEST_DEBUG); - - 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; -} - -/** - * 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; - 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_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_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; - - /* 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 330fbe5c72..2e6b49bf13 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -12,98 +12,29 @@ #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_init_debug() - initialize guest debug capabilities - * @s: KVMState - * - * Should be called only once before using guest debug capabilities. - */ -void kvm_arm_init_debug(KVMState *s); - -/** - * 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 * @devid: the KVM device ID * @group: device control API group for setting addresses * @attr: device control API address type - * @dev_fd: device control device file descriptor (or -1 if not supported) + * @dev_fd: device control device file descriptor * @addr_ormask: value to be OR'ed with resolved address * - * Remember the memory region @mr, and when it is mapped by the - * machine model, tell the kernel that base address using the - * KVM_ARM_SET_DEVICE_ADDRESS ioctl or the newer device control API. @devid - * should be the ID of the device as defined by KVM_ARM_SET_DEVICE_ADDRESS or - * the arm-vgic device in the device control API. - * The machine model may map - * and unmap the device multiple times; the kernel will only be told the final - * address at the point where machine init is complete. + * Remember the memory region @mr, and when it is mapped by the machine + * model, tell the kernel that base address using the device control API. + * @devid should be the ID of the device as defined by the arm-vgic device + * in the device control API. The machine model may map and unmap the device + * multiple times; the kernel will only be told the final address at the + * point where machine init is complete. */ 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 @@ -163,34 +94,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: @@ -222,37 +125,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: @@ -265,12 +146,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: @@ -282,14 +163,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: * @@ -313,6 +186,13 @@ bool kvm_arm_pmu_supported(void); */ bool kvm_arm_sve_supported(void); +/** + * kvm_arm_mte_supported: + * + * Returns: true if KVM can enable MTE, and false otherwise. + */ +bool kvm_arm_mte_supported(void); + /** * kvm_arm_get_max_vm_ipa_size: * @ms: Machine state handle @@ -323,60 +203,24 @@ 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); +void kvm_arm_enable_mte(Object *cpuobj, Error **errp); + #else /* @@ -398,7 +242,7 @@ static inline bool kvm_arm_sve_supported(void) return false; } -static inline bool kvm_arm_steal_time_supported(void) +static inline bool kvm_arm_mte_supported(void) { return false; } @@ -411,7 +255,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(); } @@ -426,17 +270,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(); } @@ -446,93 +290,16 @@ 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(); +} + +static inline void kvm_arm_enable_mte(Object *cpuobj, Error **errp) { 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 54c5c62433..a3c1e05e65 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) { @@ -15,6 +18,35 @@ static bool vfp_needed(void *opaque) : cpu_isar_feature(aa32_vfp_simd, cpu)); } +static bool vfp_fpcr_fpsr_needed(void *opaque) +{ + /* + * If either the FPCR or the FPSR include set bits that are not + * visible in the AArch32 FPSCR view of floating point control/status + * then we must send the FPCR and FPSR as two separate fields in the + * cpu/vfp/fpcr_fpsr subsection, and we will send a 0 for the old + * FPSCR field in cpu/vfp. + * + * If all the set bits are representable in an AArch32 FPSCR then we + * send that value as the cpu/vfp FPSCR field, and don't send the + * cpu/vfp/fpcr_fpsr subsection. + * + * On incoming migration, if the cpu/vfp FPSCR field is non-zero we + * use it, and if the fpcr_fpsr subsection is present we use that. + * (The subsection will never be present with a non-zero FPSCR field, + * and if FPSCR is zero and the subsection is not present that means + * that FPSCR/FPSR/FPCR are zero.) + * + * This preserves migration compatibility with older QEMU versions, + * in both directions. + */ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + return (vfp_get_fpcr(env) & ~FPSCR_FPCR_MASK) || + (vfp_get_fpsr(env) & ~FPSCR_FPSR_MASK); +} + static int get_fpscr(QEMUFile *f, void *opaque, size_t size, const VMStateField *field) { @@ -22,7 +54,10 @@ static int get_fpscr(QEMUFile *f, void *opaque, size_t size, CPUARMState *env = &cpu->env; uint32_t val = qemu_get_be32(f); - vfp_set_fpscr(env, val); + if (val) { + /* 0 means we might have the data in the fpcr_fpsr subsection */ + vfp_set_fpscr(env, val); + } return 0; } @@ -31,8 +66,9 @@ static int put_fpscr(QEMUFile *f, void *opaque, size_t size, { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; + uint32_t fpscr = vfp_fpcr_fpsr_needed(opaque) ? 0 : vfp_get_fpscr(env); - qemu_put_be32(f, vfp_get_fpscr(env)); + qemu_put_be32(f, fpscr); return 0; } @@ -42,12 +78,92 @@ static const VMStateInfo vmstate_fpscr = { .put = put_fpscr, }; +static int get_fpcr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val = qemu_get_be64(f); + + vfp_set_fpcr(env, val); + return 0; +} + +static int put_fpcr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + qemu_put_be64(f, vfp_get_fpcr(env)); + return 0; +} + +static const VMStateInfo vmstate_fpcr = { + .name = "fpcr", + .get = get_fpcr, + .put = put_fpcr, +}; + +static int get_fpsr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + uint64_t val = qemu_get_be64(f); + + vfp_set_fpsr(env, val); + return 0; +} + +static int put_fpsr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + qemu_put_be64(f, vfp_get_fpsr(env)); + return 0; +} + +static const VMStateInfo vmstate_fpsr = { + .name = "fpsr", + .get = get_fpsr, + .put = put_fpsr, +}; + +static const VMStateDescription vmstate_vfp_fpcr_fpsr = { + .name = "cpu/vfp/fpcr_fpsr", + .version_id = 1, + .minimum_version_id = 1, + .needed = vfp_fpcr_fpsr_needed, + .fields = (const VMStateField[]) { + { + .name = "fpcr", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &vmstate_fpcr, + .flags = VMS_SINGLE, + .offset = 0, + }, + { + .name = "fpsr", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &vmstate_fpsr, + .flags = VMS_SINGLE, + .offset = 0, + }, + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_vfp = { .name = "cpu/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), @@ -97,6 +213,10 @@ static const VMStateDescription vmstate_vfp = { .offset = 0, }, VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_vfp_fpcr_fpsr, + NULL } }; @@ -113,7 +233,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 +258,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 +268,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 +279,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, @@ -172,7 +292,7 @@ static const VMStateDescription vmstate_vreg = { .name = "vreg", .version_id = 1, .minimum_version_id = 1, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(d, ARMVectorReg, ARM_MAX_VQ * 2), VMSTATE_END_OF_LIST() } @@ -194,7 +314,7 @@ static const VMStateDescription vmstate_za = { .version_id = 1, .minimum_version_id = 1, .needed = za_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_STRUCT_ARRAY(env.zarray, ARMCPU, ARM_MAX_VQ * 16, 0, vmstate_vreg, ARMVectorReg), VMSTATE_END_OF_LIST() @@ -215,7 +335,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), @@ -233,12 +353,31 @@ 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() } }; +static bool wfxt_timer_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + + /* We'll only have the timer object if FEAT_WFxT is implemented */ + return cpu->wfxt_timer; +} + +static const VMStateDescription vmstate_wfxt_timer = { + .name = "cpu/wfxt-timer", + .version_id = 1, + .minimum_version_id = 1, + .needed = wfxt_timer_needed, + .fields = (const VMStateField[]) { + VMSTATE_TIMER_PTR(wfxt_timer, ARMCPU), + VMSTATE_END_OF_LIST() + } +}; + static bool m_needed(void *opaque) { ARMCPU *cpu = opaque; @@ -252,7 +391,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() @@ -287,7 +426,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() @@ -299,7 +438,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() } @@ -310,7 +449,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() } @@ -329,7 +468,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() @@ -341,7 +480,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), @@ -363,7 +502,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() @@ -375,7 +514,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), @@ -389,7 +528,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, @@ -414,7 +553,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() @@ -443,7 +582,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, @@ -472,7 +611,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() } @@ -487,12 +626,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, @@ -500,6 +663,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 } }; @@ -530,7 +697,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), @@ -734,6 +901,20 @@ static int cpu_pre_load(void *opaque) ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; + /* + * In an inbound migration where on the source FPSCR/FPSR/FPCR are 0, + * there will be no fpcr_fpsr subsection so we won't call vfp_set_fpcr() + * and vfp_set_fpsr() from get_fpcr() and get_fpsr(); also the get_fpscr() + * function will not call vfp_set_fpscr() because it will see a 0 in the + * inbound data. Ensure that in this case we have a correctly set up + * zero FPSCR/FPCR/FPSR. + * + * This is not strictly needed because FPSCR is zero out of reset, but + * it avoids the possibility of future confusing migration bugs if some + * future architecture change makes the reset value non-zero. + */ + vfp_set_fpscr(env, 0); + /* * Pre-initialize irq_line_state to a value that's never valid as * real data, so cpu_post_load() can tell whether we've seen the @@ -742,7 +923,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; @@ -811,8 +992,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 @@ -828,19 +1020,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; } @@ -853,7 +1039,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), @@ -902,7 +1088,7 @@ const VMStateDescription vmstate_arm_cpu = { }, VMSTATE_END_OF_LIST() }, - .subsections = (const VMStateDescription*[]) { + .subsections = (const VMStateDescription * const []) { &vmstate_vfp, &vmstate_iwmmxt, &vmstate_m, @@ -921,6 +1107,7 @@ const VMStateDescription vmstate_arm_cpu = { #endif &vmstate_serror, &vmstate_irq_line_state, + &vmstate_wfxt_timer, NULL } }; diff --git a/target/arm/meson.build b/target/arm/meson.build index 87e911b27f..2e10464dbb 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -1,71 +1,41 @@ -gen = [ - 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'), - 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', - 'sme_helper.c', - 'translate-a64.c', - 'translate-sve.c', - 'translate-sme.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/monitor.c b/target/arm/monitor.c deleted file mode 100644 index 80c64fa355..0000000000 --- a/target/arm/monitor.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * QEMU monitor.c for ARM. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu/osdep.h" -#include "hw/boards.h" -#include "kvm_arm.h" -#include "qapi/error.h" -#include "qapi/visitor.h" -#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" - -static GICCapability *gic_cap_new(int version) -{ - GICCapability *cap = g_new0(GICCapability, 1); - cap->version = version; - /* by default, support none */ - cap->emulated = false; - cap->kernel = false; - return cap; -} - -static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) -{ -#ifdef CONFIG_KVM - int fdarray[3]; - - if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) { - return; - } - - /* Test KVM GICv2 */ - if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) { - v2->kernel = true; - } - - /* Test KVM GICv3 */ - if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) { - v3->kernel = true; - } - - kvm_arm_destroy_scratch_host_vcpu(fdarray); -#endif -} - -GICCapabilityList *qmp_query_gic_capabilities(Error **errp) -{ - GICCapabilityList *head = NULL; - GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3); - - v2->emulated = true; - v3->emulated = true; - - gic_cap_kvm_probe(v2, v3); - - QAPI_LIST_PREPEND(head, v2); - QAPI_LIST_PREPEND(head, v3); - - return head; -} - -QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16); - -/* - * These are cpu model features we want to advertise. The order here - * matters as this is the order in which qmp_query_cpu_model_expansion - * will attempt to set them. If there are dependencies between features, - * then the order that considers those dependencies must be used. - */ -static const char *cpu_model_advertised_features[] = { - "aarch64", "pmu", "sve", - "sve128", "sve256", "sve384", "sve512", - "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", - "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", - "kvm-no-adjvtime", "kvm-steal-time", - "pauth", "pauth-impdef", - NULL -}; - -CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *model, - Error **errp) -{ - CpuModelExpansionInfo *expansion_info; - const QDict *qdict_in = NULL; - QDict *qdict_out; - ObjectClass *oc; - Object *obj; - const char *name; - int i; - - if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { - error_setg(errp, "The requested expansion type is not supported"); - return NULL; - } - - if (!kvm_enabled() && !strcmp(model->name, "host")) { - error_setg(errp, "The CPU type '%s' requires KVM", model->name); - return NULL; - } - - oc = cpu_class_by_name(TYPE_ARM_CPU, model->name); - if (!oc) { - error_setg(errp, "The CPU type '%s' is not a recognized ARM CPU type", - model->name); - return NULL; - } - - if (kvm_enabled()) { - bool supported = false; - - if (!strcmp(model->name, "host") || !strcmp(model->name, "max")) { - /* These are kvmarm's recommended cpu types */ - supported = true; - } else if (current_machine->cpu_type) { - const char *cpu_type = current_machine->cpu_type; - int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); - - if (strlen(model->name) == len && - !strncmp(model->name, cpu_type, len)) { - /* KVM is enabled and we're using this type, so it works. */ - supported = true; - } - } - if (!supported) { - error_setg(errp, "We cannot guarantee the CPU type '%s' works " - "with KVM on this host", model->name); - return NULL; - } - } - - 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) { - Visitor *visitor; - Error *err = NULL; - - visitor = qobject_input_visitor_new(model->props); - if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { - visit_free(visitor); - object_unref(obj); - return NULL; - } - - i = 0; - while ((name = cpu_model_advertised_features[i++]) != NULL) { - if (qdict_get(qdict_in, name)) { - if (!object_property_set(obj, name, visitor, &err)) { - break; - } - } - } - - if (!err) { - visit_check_struct(visitor, &err); - } - if (!err) { - arm_cpu_finalize_features(ARM_CPU(obj), &err); - } - visit_end_struct(visitor, NULL); - visit_free(visitor); - if (err) { - object_unref(obj); - error_propagate(errp, err); - return NULL; - } - } else { - arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); - } - - 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); - expansion_info->model->has_props = true; - } - - object_unref(obj); - - return expansion_info; -} diff --git a/target/arm/mte_helper.c b/target/arm/mte_helper.c deleted file mode 100644 index 86b3754838..0000000000 --- a/target/arm/mte_helper.c +++ /dev/null @@ -1,908 +0,0 @@ -/* - * ARM v8.5-MemTag Operations - * - * Copyright (c) 2020 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 "qemu/log.h" -#include "cpu.h" -#include "internals.h" -#include "exec/exec-all.h" -#include "exec/ram_addr.h" -#include "exec/cpu_ldst.h" -#include "exec/helper-proto.h" -#include "qapi/error.h" -#include "qemu/guest-random.h" - - -static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) -{ - if (exclude == 0xffff) { - return 0; - } - if (offset == 0) { - while (exclude & (1 << tag)) { - tag = (tag + 1) & 15; - } - } else { - do { - do { - tag = (tag + 1) & 15; - } while (exclude & (1 << tag)); - } while (--offset > 0); - } - return tag; -} - -/** - * allocation_tag_mem: - * @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 - * @ra: the return address for exception handling - * - * Our tag memory is formatted as a sequence of little-endian nibbles. - * That is, the byte at (addr >> (LOG2_TAG_GRANULE + 1)) contains two - * tags, with the tag at [3:0] for the lower addr and the tag at [7:4] - * 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. - * - * If there is no tag storage corresponding to @ptr, return NULL. - */ -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) -{ -#ifdef CONFIG_USER_ONLY - uint64_t clean_ptr = useronly_clean_ptr(ptr); - int flags = page_get_flags(clean_ptr); - uint8_t *tags; - uintptr_t index; - - 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); - } - - /* Require both MAP_ANON and PROT_MTE for the page. */ - if (!(flags & PAGE_ANON) || !(flags & PAGE_MTE)) { - return NULL; - } - - tags = page_get_target_data(clean_ptr); - - index = extract32(ptr, LOG2_TAG_GRANULE + 1, - TARGET_PAGE_BITS - LOG2_TAG_GRANULE - 1); - return tags + index; -#else - CPUTLBEntryFull *full; - MemTxAttrs attrs; - int in_page, flags; - hwaddr ptr_paddr, tag_paddr, xlat; - MemoryRegion *mr; - ARMASIdx tag_asi; - AddressSpace *tag_as; - void *host; - - /* - * Probe the first byte of the virtual address. This raises an - * 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. - */ - flags = probe_access_full(env, ptr, ptr_access, ptr_mmu_idx, - ra == 0, &host, &full, ra); - assert(!(flags & TLB_INVALID_MASK)); - - /* If the virtual page MemAttr != Tagged, access unchecked. */ - if (full->pte_attrs != 0xf0) { - return NULL; - } - - /* - * If not backed by host ram, there is no tag storage: access unchecked. - * This is probably a guest os bug though, so log it. - */ - if (unlikely(flags & TLB_MMIO)) { - qemu_log_mask(LOG_GUEST_ERROR, - "Page @ 0x%" PRIx64 " indicates Tagged Normal memory " - "but is not backed by host ram\n", ptr); - return NULL; - } - - /* - * Remember these values across the second lookup below, - * which may invalidate this pointer via tlb resize. - */ - ptr_paddr = full->phys_addr; - 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 - * tag on the first page. - * Any page access exception has priority over tag check exception. - */ - in_page = -(ptr | TARGET_PAGE_MASK); - if (unlikely(ptr_size > in_page)) { - flags |= probe_access_full(env, ptr + in_page, 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)) { - 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, attrs, wp, ra); - } - - /* 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 = 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, attrs); - - /* - * Note that @mr will never be NULL. If there is nothing in the address - * space at @tag_paddr, the translation will return the unallocated memory - * region. For our purposes, the result must be ram. - */ - if (unlikely(!memory_region_is_ram(mr))) { - /* ??? Failure is a board configuration error. */ - qemu_log_mask(LOG_UNIMP, - "Tag Memory @ 0x%" HWADDR_PRIx " not found for " - "Normal Memory @ 0x%" HWADDR_PRIx "\n", - tag_paddr, ptr_paddr); - return NULL; - } - - /* - * Ensure the tag memory is dirty on write, for migration. - * Tag memory can never contain code or display memory (vga). - */ - if (tag_access == MMU_DATA_STORE) { - ram_addr_t tag_ra = memory_region_get_ram_addr(mr) + xlat; - cpu_physical_memory_set_dirty_flag(tag_ra, DIRTY_MEMORY_MIGRATION); - } - - return memory_region_get_ram_ptr(mr) + xlat; -#endif -} - -uint64_t HELPER(irg)(CPUARMState *env, uint64_t rn, uint64_t rm) -{ - uint16_t exclude = extract32(rm | env->cp15.gcr_el1, 0, 16); - int rrnd = extract32(env->cp15.gcr_el1, 16, 1); - int start = extract32(env->cp15.rgsr_el1, 0, 4); - int seed = extract32(env->cp15.rgsr_el1, 8, 16); - int offset, i, rtag; - - /* - * Our IMPDEF choice for GCR_EL1.RRND==1 is to continue to use the - * deterministic algorithm. Except that with RRND==1 the kernel is - * not required to have set RGSR_EL1.SEED != 0, which is required for - * the deterministic algorithm to function. So we force a non-zero - * SEED for that case. - */ - if (unlikely(seed == 0) && rrnd) { - do { - Error *err = NULL; - uint16_t two; - - if (qemu_guest_getrandom(&two, sizeof(two), &err) < 0) { - /* - * Failed, for unknown reasons in the crypto subsystem. - * Best we can do is log the reason and use a constant seed. - */ - qemu_log_mask(LOG_UNIMP, "IRG: Crypto failure: %s\n", - error_get_pretty(err)); - error_free(err); - two = 1; - } - seed = two; - } while (seed == 0); - } - - /* RandomTag */ - for (i = offset = 0; i < 4; ++i) { - /* NextRandomTagBit */ - int top = (extract32(seed, 5, 1) ^ extract32(seed, 3, 1) ^ - extract32(seed, 2, 1) ^ extract32(seed, 0, 1)); - seed = (top << 15) | (seed >> 1); - offset |= top << i; - } - rtag = choose_nonexcluded_tag(start, offset, exclude); - env->cp15.rgsr_el1 = rtag | (seed << 8); - - return address_with_allocation_tag(rn, rtag); -} - -uint64_t HELPER(addsubg)(CPUARMState *env, uint64_t ptr, - int32_t offset, uint32_t tag_offset) -{ - int start_tag = allocation_tag_from_addr(ptr); - uint16_t exclude = extract32(env->cp15.gcr_el1, 0, 16); - int rtag = choose_nonexcluded_tag(start_tag, tag_offset, exclude); - - return address_with_allocation_tag(ptr + offset, rtag); -} - -static int load_tag1(uint64_t ptr, uint8_t *mem) -{ - int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; - return extract32(*mem, ofs, 4); -} - -uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt) -{ - int mmu_idx = cpu_mmu_index(env, false); - 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()); - - /* Load if page supports tags. */ - if (mem) { - rtag = load_tag1(ptr, mem); - } - - return address_with_allocation_tag(xt, rtag); -} - -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); - g_assert_not_reached(); - } -} - -/* For use in a non-parallel context, store to the given nibble. */ -static void store_tag1(uint64_t ptr, uint8_t *mem, int tag) -{ - int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; - *mem = deposit32(*mem, ofs, 4, tag); -} - -/* For use in a parallel context, atomically store to the given nibble. */ -static void store_tag1_parallel(uint64_t ptr, uint8_t *mem, int tag) -{ - int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; - uint8_t old = qatomic_read(mem); - - while (1) { - uint8_t new = deposit32(old, ofs, 4, tag); - uint8_t cmp = qatomic_cmpxchg(mem, old, new); - if (likely(cmp == old)) { - return; - } - old = cmp; - } -} - -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); - 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); - - /* Store if page supports tags. */ - if (mem) { - store1(ptr, mem, allocation_tag_from_addr(xt)); - } -} - -void HELPER(stg)(CPUARMState *env, uint64_t ptr, uint64_t xt) -{ - do_stg(env, ptr, xt, GETPC(), store_tag1); -} - -void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) -{ - do_stg(env, ptr, xt, GETPC(), store_tag1_parallel); -} - -void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr) -{ - int mmu_idx = cpu_mmu_index(env, false); - uintptr_t ra = GETPC(); - - check_tag_aligned(env, ptr, ra); - probe_write(env, ptr, TAG_GRANULE, mmu_idx, ra); -} - -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 tag = allocation_tag_from_addr(xt); - uint8_t *mem1, *mem2; - - check_tag_aligned(env, ptr, ra); - - /* - * Trap if accessing an invalid page(s). - * This takes priority over !allocation_tag_access_enabled. - */ - 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); - mem2 = allocation_tag_mem(env, mmu_idx, ptr + TAG_GRANULE, - MMU_DATA_STORE, TAG_GRANULE, - MMU_DATA_STORE, 1, ra); - - /* Store if page(s) support tags. */ - if (mem1) { - store1(TAG_GRANULE, mem1, tag); - } - if (mem2) { - store1(0, mem2, tag); - } - } 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); - if (mem1) { - tag |= tag << 4; - qatomic_set(mem1, tag); - } - } -} - -void HELPER(st2g)(CPUARMState *env, uint64_t ptr, uint64_t xt) -{ - do_st2g(env, ptr, xt, GETPC(), store_tag1); -} - -void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) -{ - do_st2g(env, ptr, xt, GETPC(), store_tag1_parallel); -} - -void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr) -{ - int mmu_idx = cpu_mmu_index(env, false); - uintptr_t ra = GETPC(); - int in_page = -(ptr | TARGET_PAGE_MASK); - - check_tag_aligned(env, ptr, ra); - - if (likely(in_page >= 2 * TAG_GRANULE)) { - probe_write(env, ptr, 2 * TAG_GRANULE, mmu_idx, ra); - } else { - probe_write(env, ptr, TAG_GRANULE, mmu_idx, ra); - probe_write(env, ptr + TAG_GRANULE, TAG_GRANULE, mmu_idx, ra); - } -} - -#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); - uintptr_t ra = GETPC(); - void *tag_mem; - - ptr = QEMU_ALIGN_DOWN(ptr, LDGM_STGM_SIZE); - - /* 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); - - /* 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. - */ - return ldq_le_p(tag_mem); -} - -void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val) -{ - int mmu_idx = cpu_mmu_index(env, false); - uintptr_t ra = GETPC(); - void *tag_mem; - - ptr = QEMU_ALIGN_DOWN(ptr, LDGM_STGM_SIZE); - - /* 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); - - /* - * Tag store only happens if the page support tags, - * and if the OS has enabled access to the tags. - */ - if (!tag_mem) { - 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); -} - -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 log2_dcz_bytes, log2_tag_bytes; - intptr_t dcz_bytes, tag_bytes; - uint8_t *mem; - - /* - * In arm_cpu_realizefn, we assert that dcz > LOG2_TAG_GRANULE+1, - * i.e. 32 bytes, which is an unreasonably small dcz anyway, - * to make sure that we can access one complete tag byte here. - */ - log2_dcz_bytes = env_archcpu(env)->dcz_blocksize + 2; - log2_tag_bytes = log2_dcz_bytes - (LOG2_TAG_GRANULE + 1); - dcz_bytes = (intptr_t)1 << log2_dcz_bytes; - tag_bytes = (intptr_t)1 << log2_tag_bytes; - ptr &= -dcz_bytes; - - mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, dcz_bytes, - MMU_DATA_STORE, tag_bytes, ra); - if (mem) { - int tag_pair = (val & 0xf) * 0x11; - memset(mem, tag_pair, tag_bytes); - } -} - -static void mte_sync_check_fail(CPUARMState *env, uint32_t desc, - uint64_t dirty_ptr, uintptr_t ra) -{ - int is_write, syn; - - env->exception.vaddress = dirty_ptr; - - is_write = FIELD_EX32(desc, MTEDESC, WRITE); - syn = syn_data_abort_no_iss(arm_current_el(env) != 0, 0, 0, 0, 0, is_write, - 0x11); - raise_exception_ra(env, EXCP_DATA_ABORT, syn, exception_target_el(env), ra); - g_assert_not_reached(); -} - -static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, - uintptr_t ra, ARMMMUIdx arm_mmu_idx, int el) -{ - int select; - - if (regime_has_2_ranges(arm_mmu_idx)) { - select = extract64(dirty_ptr, 55, 1); - } else { - select = 0; - } - env->cp15.tfsr_el[el] |= 1 << select; -#ifdef CONFIG_USER_ONLY - /* - * Stand in for a timer irq, setting _TIF_MTE_ASYNC_FAULT, - * which then sends a SIGSEGV when the thread is next scheduled. - * This cpu will return to the main loop at the end of the TB, - * which is rather sooner than "normal". But the alternative - * is waiting until the next syscall. - */ - qemu_cpu_kick(env_cpu(env)); -#endif -} - -/* Record a tag check failure. */ -static 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); - int el, reg_el, tcf; - uint64_t sctlr; - - reg_el = regime_el(env, arm_mmu_idx); - sctlr = env->cp15.sctlr_el[reg_el]; - - switch (arm_mmu_idx) { - case ARMMMUIdx_E10_0: - case ARMMMUIdx_E20_0: - el = 0; - tcf = extract64(sctlr, 38, 2); - break; - default: - el = reg_el; - tcf = extract64(sctlr, 40, 2); - } - - switch (tcf) { - case 1: - /* Tag check fail causes a synchronous exception. */ - mte_sync_check_fail(env, desc, dirty_ptr, ra); - break; - - case 0: - /* - * Tag check fail does not affect the PE. - * We eliminate this case by not setting MTE_ACTIVE - * in tb_flags, so that we never make this runtime call. - */ - g_assert_not_reached(); - - case 2: - /* Tag check fail causes asynchronous flag set. */ - mte_async_check_fail(env, dirty_ptr, ra, arm_mmu_idx, el); - break; - - case 3: - /* - * Tag check fail causes asynchronous flag set for stores, or - * a synchronous exception for loads. - */ - if (FIELD_EX32(desc, MTEDESC, WRITE)) { - mte_async_check_fail(env, dirty_ptr, ra, arm_mmu_idx, el); - } else { - mte_sync_check_fail(env, desc, dirty_ptr, ra); - } - break; - } -} - -/** - * checkN: - * @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. - * - * A note about sizes: count is expected to be small. - * - * The most common use will be LDP/STP of two integer registers, - * which means 16 bytes of memory touching at most 2 tags, but - * often the access is aligned and thus just 1 tag. - * - * Using AdvSIMD LD/ST (multiple), one can access 64 bytes of memory, - * touching at most 5 tags. SVE LDR/STR (vector) with the default - * vector length is also 64 bytes; the maximum architectural length - * is 256 bytes touching at most 9 tags. - * - * The loop below uses 7 logical operations and 1 memory operation - * per tag pair. An implementation that loads an aligned word and - * uses masking to ignore adjacent tags requires 18 logical operations - * and thus does not begin to pay off until 6 tags. - * Which, according to the survey above, is unlikely to be common. - */ -static int checkN(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_odd; - } - - while (1) { - /* Test even tag. */ - if (unlikely((diff) & 0x0f)) { - break; - } - if (++n == count) { - break; - } - - start_odd: - /* Test odd tag. */ - if (unlikely((diff) & 0xf0)) { - break; - } - if (++n == count) { - break; - } - - diff = *mem++ ^ cmp; - } - return n; -} - -/** - * mte_probe_int() - helper for mte_probe and mte_check - * @env: CPU environment - * @desc: MTEDESC descriptor - * @ptr: virtual address of the base of the access - * @fault: return virtual address of the first check failure - * - * Internal routine for both mte_probe and mte_check. - * Return zero on failure, filling in *fault. - * Return negative on trivial success for tbi disabled. - * Return positive on success with tbi enabled. - */ -static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, - uintptr_t ra, uint64_t *fault) -{ - 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; - uint8_t *mem1, *mem2; - MMUAccessType type; - - bit55 = extract64(ptr, 55, 1); - *fault = ptr; - - /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */ - if (unlikely(!tbi_check(desc, bit55))) { - return -1; - } - - ptr_tag = allocation_tag_from_addr(ptr); - - if (tcma_check(desc, bit55, ptr_tag)) { - return 1; - } - - mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); - type = FIELD_EX32(desc, MTEDESC, WRITE) ? MMU_DATA_STORE : MMU_DATA_LOAD; - sizem1 = FIELD_EX32(desc, MTEDESC, SIZEM1); - - /* Find the addr of the end of the access */ - ptr_last = ptr + sizem1; - - /* Round the bounds to the tag granule, and compute the number of tags. */ - tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); - 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); - if (!mem1) { - return 1; - } - /* Perform all of the comparisons. */ - 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); - - 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); - - /* - * Perform all of the comparisons. - * Note the possible but unlikely case of the operation spanning - * two pages that do not both have tagging enabled. - */ - n = c = (next_page - tag_first) / TAG_GRANULE; - if (mem1) { - n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, c); - } - if (n == c) { - if (!mem2) { - return 1; - } - n += checkN(mem2, 0, ptr_tag, tag_count - c); - } - } - - if (likely(n == tag_count)) { - return 1; - } - - /* - * If we failed, we know which granule. For the first granule, the - * failure address is @ptr, the first byte accessed. Otherwise the - * failure address is the first byte of the nth granule. - */ - if (n > 0) { - *fault = tag_first + n * TAG_GRANULE; - } - return 0; -} - -uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra) -{ - uint64_t fault; - int ret = mte_probe_int(env, desc, ptr, ra, &fault); - - if (unlikely(ret == 0)) { - mte_check_fail(env, desc, fault, ra); - } else if (ret < 0) { - return ptr; - } - return useronly_clean_ptr(ptr); -} - -uint64_t HELPER(mte_check)(CPUARMState *env, uint32_t desc, uint64_t ptr) -{ - return mte_check(env, desc, ptr, GETPC()); -} - -/* - * No-fault version of mte_check, to be used by SVE for MemSingleNF. - * Returns false if the access is Checked and the check failed. This - * is only intended to probe the tag -- the validity of the page must - * be checked beforehand. - */ -bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr) -{ - uint64_t fault; - int ret = mte_probe_int(env, desc, ptr, 0, &fault); - - return ret != 0; -} - -/* - * Perform an MTE checked access for DC_ZVA. - */ -uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) -{ - uintptr_t ra = GETPC(); - int log2_dcz_bytes, log2_tag_bytes; - int mmu_idx, bit55; - intptr_t dcz_bytes, tag_bytes, i; - void *mem; - uint64_t ptr_tag, mem_tag, align_ptr; - - bit55 = extract64(ptr, 55, 1); - - /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */ - if (unlikely(!tbi_check(desc, bit55))) { - return ptr; - } - - ptr_tag = allocation_tag_from_addr(ptr); - - if (tcma_check(desc, bit55, ptr_tag)) { - goto done; - } - - /* - * In arm_cpu_realizefn, we asserted that dcz > LOG2_TAG_GRANULE+1, - * i.e. 32 bytes, which is an unreasonably small dcz anyway, to make - * sure that we can access one complete tag byte here. - */ - log2_dcz_bytes = env_archcpu(env)->dcz_blocksize + 2; - log2_tag_bytes = log2_dcz_bytes - (LOG2_TAG_GRANULE + 1); - dcz_bytes = (intptr_t)1 << log2_dcz_bytes; - tag_bytes = (intptr_t)1 << log2_tag_bytes; - align_ptr = ptr & -dcz_bytes; - - /* - * 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. - */ - 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); - if (!mem) { - goto done; - } - - /* - * Unlike the reasoning for checkN, DC_ZVA is always aligned, and thus - * it is quite easy to perform all of the comparisons at once without - * any extra masking. - * - * The most common zva block size is 64; some of the thunderx cpus use - * a block size of 128. For user-only, aarch64_max_initfn will set the - * block size to 512. Fill out the other cases for future-proofing. - * - * In order to be able to find the first miscompare later, we want the - * tag bytes to be in little-endian order. - */ - switch (log2_tag_bytes) { - case 0: /* zva_blocksize 32 */ - mem_tag = *(uint8_t *)mem; - ptr_tag *= 0x11u; - break; - case 1: /* zva_blocksize 64 */ - mem_tag = cpu_to_le16(*(uint16_t *)mem); - ptr_tag *= 0x1111u; - break; - case 2: /* zva_blocksize 128 */ - mem_tag = cpu_to_le32(*(uint32_t *)mem); - ptr_tag *= 0x11111111u; - break; - case 3: /* zva_blocksize 256 */ - mem_tag = cpu_to_le64(*(uint64_t *)mem); - ptr_tag *= 0x1111111111111111ull; - break; - - default: /* zva_blocksize 512, 1024, 2048 */ - ptr_tag *= 0x1111111111111111ull; - i = 0; - do { - mem_tag = cpu_to_le64(*(uint64_t *)(mem + i)); - if (unlikely(mem_tag != ptr_tag)) { - goto fail; - } - i += 8; - align_ptr += 16 * TAG_GRANULE; - } while (i < tag_bytes); - goto done; - } - - if (likely(mem_tag == ptr_tag)) { - goto done; - } - - fail: - /* Locate the first nibble that differs. */ - i = ctz64(mem_tag ^ ptr_tag) >> 4; - mte_check_fail(env, desc, align_ptr + i * TAG_GRANULE, ra); - - done: - return useronly_clean_ptr(ptr); -} 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/neon_helper.c b/target/arm/neon_helper.c deleted file mode 100644 index bc6c4a54e9..0000000000 --- a/target/arm/neon_helper.c +++ /dev/null @@ -1,1740 +0,0 @@ -/* - * ARM NEON vector operations. - * - * Copyright (c) 2007, 2008 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GNU GPL v2. - */ -#include "qemu/osdep.h" - -#include "cpu.h" -#include "exec/helper-proto.h" -#include "fpu/softfloat.h" -#include "vec_internal.h" - -#define SIGNBIT (uint32_t)0x80000000 -#define SIGNBIT64 ((uint64_t)1 << 63) - -#define SET_QC() env->vfp.qc[0] = 1 - -#define NEON_TYPE1(name, type) \ -typedef struct \ -{ \ - type v1; \ -} neon_##name; -#if HOST_BIG_ENDIAN -#define NEON_TYPE2(name, type) \ -typedef struct \ -{ \ - type v2; \ - type v1; \ -} neon_##name; -#define NEON_TYPE4(name, type) \ -typedef struct \ -{ \ - type v4; \ - type v3; \ - type v2; \ - type v1; \ -} neon_##name; -#else -#define NEON_TYPE2(name, type) \ -typedef struct \ -{ \ - type v1; \ - type v2; \ -} neon_##name; -#define NEON_TYPE4(name, type) \ -typedef struct \ -{ \ - type v1; \ - type v2; \ - type v3; \ - type v4; \ -} neon_##name; -#endif - -NEON_TYPE4(s8, int8_t) -NEON_TYPE4(u8, uint8_t) -NEON_TYPE2(s16, int16_t) -NEON_TYPE2(u16, uint16_t) -NEON_TYPE1(s32, int32_t) -NEON_TYPE1(u32, uint32_t) -#undef NEON_TYPE4 -#undef NEON_TYPE2 -#undef NEON_TYPE1 - -/* Copy from a uint32_t to a vector structure type. */ -#define NEON_UNPACK(vtype, dest, val) do { \ - union { \ - vtype v; \ - uint32_t i; \ - } conv_u; \ - conv_u.i = (val); \ - dest = conv_u.v; \ - } while(0) - -/* Copy from a vector structure type to a uint32_t. */ -#define NEON_PACK(vtype, dest, val) do { \ - union { \ - vtype v; \ - uint32_t i; \ - } conv_u; \ - conv_u.v = (val); \ - dest = conv_u.i; \ - } while(0) - -#define NEON_DO1 \ - NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); -#define NEON_DO2 \ - NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \ - NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2); -#define NEON_DO4 \ - NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \ - NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2); \ - NEON_FN(vdest.v3, vsrc1.v3, vsrc2.v3); \ - NEON_FN(vdest.v4, vsrc1.v4, vsrc2.v4); - -#define NEON_VOP_BODY(vtype, n) \ -{ \ - uint32_t res; \ - vtype vsrc1; \ - vtype vsrc2; \ - vtype vdest; \ - NEON_UNPACK(vtype, vsrc1, arg1); \ - NEON_UNPACK(vtype, vsrc2, arg2); \ - NEON_DO##n; \ - NEON_PACK(vtype, res, vdest); \ - return res; \ -} - -#define NEON_VOP(name, vtype, n) \ -uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \ -NEON_VOP_BODY(vtype, n) - -#define NEON_VOP_ENV(name, vtype, n) \ -uint32_t HELPER(glue(neon_,name))(CPUARMState *env, uint32_t arg1, uint32_t arg2) \ -NEON_VOP_BODY(vtype, n) - -/* Pairwise operations. */ -/* For 32-bit elements each segment only contains a single element, so - the elementwise and pairwise operations are the same. */ -#define NEON_PDO2 \ - NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \ - NEON_FN(vdest.v2, vsrc2.v1, vsrc2.v2); -#define NEON_PDO4 \ - NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \ - NEON_FN(vdest.v2, vsrc1.v3, vsrc1.v4); \ - NEON_FN(vdest.v3, vsrc2.v1, vsrc2.v2); \ - NEON_FN(vdest.v4, vsrc2.v3, vsrc2.v4); \ - -#define NEON_POP(name, vtype, n) \ -uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \ -{ \ - uint32_t res; \ - vtype vsrc1; \ - vtype vsrc2; \ - vtype vdest; \ - NEON_UNPACK(vtype, vsrc1, arg1); \ - NEON_UNPACK(vtype, vsrc2, arg2); \ - NEON_PDO##n; \ - NEON_PACK(vtype, res, vdest); \ - return res; \ -} - -/* Unary operators. */ -#define NEON_VOP1(name, vtype, n) \ -uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ -{ \ - vtype vsrc1; \ - vtype vdest; \ - NEON_UNPACK(vtype, vsrc1, arg); \ - NEON_DO##n; \ - NEON_PACK(vtype, arg, vdest); \ - return arg; \ -} - - -#define NEON_USAT(dest, src1, src2, type) do { \ - uint32_t tmp = (uint32_t)src1 + (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - dest = ~0; \ - } else { \ - dest = tmp; \ - }} while(0) -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t) -NEON_VOP_ENV(qadd_u8, neon_u8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t) -NEON_VOP_ENV(qadd_u16, neon_u16, 2) -#undef NEON_FN -#undef NEON_USAT - -uint32_t HELPER(neon_qadd_u32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (res < a) { - SET_QC(); - res = ~0; - } - return res; -} - -uint64_t HELPER(neon_qadd_u64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 + src2; - if (res < src1) { - SET_QC(); - res = ~(uint64_t)0; - } - return res; -} - -#define NEON_SSAT(dest, src1, src2, type) do { \ - int32_t tmp = (uint32_t)src1 + (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - if (src2 > 0) { \ - tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \ - } else { \ - tmp = 1 << (sizeof(type) * 8 - 1); \ - } \ - } \ - dest = tmp; \ - } while(0) -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t) -NEON_VOP_ENV(qadd_s8, neon_s8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t) -NEON_VOP_ENV(qadd_s16, neon_s16, 2) -#undef NEON_FN -#undef NEON_SSAT - -uint32_t HELPER(neon_qadd_s32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { - SET_QC(); - res = ~(((int32_t)a >> 31) ^ SIGNBIT); - } - return res; -} - -uint64_t HELPER(neon_qadd_s64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 + src2; - if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) { - SET_QC(); - res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; - } - return res; -} - -/* Unsigned saturating accumulate of signed value - * - * Op1/Rn is treated as signed - * Op2/Rd is treated as unsigned - * - * Explicit casting is used to ensure the correct sign extension of - * inputs. The result is treated as a unsigned value and saturated as such. - * - * We use a macro for the 8/16 bit cases which expects signed integers of va, - * vb, and vr for interim calculation and an unsigned 32 bit result value r. - */ - -#define USATACC(bits, shift) \ - do { \ - va = sextract32(a, shift, bits); \ - vb = extract32(b, shift, bits); \ - vr = va + vb; \ - if (vr > UINT##bits##_MAX) { \ - SET_QC(); \ - vr = UINT##bits##_MAX; \ - } else if (vr < 0) { \ - SET_QC(); \ - vr = 0; \ - } \ - r = deposit32(r, shift, bits, vr); \ - } while (0) - -uint32_t HELPER(neon_uqadd_s8)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int16_t va, vb, vr; - uint32_t r = 0; - - USATACC(8, 0); - USATACC(8, 8); - USATACC(8, 16); - USATACC(8, 24); - return r; -} - -uint32_t HELPER(neon_uqadd_s16)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int32_t va, vb, vr; - uint64_t r = 0; - - USATACC(16, 0); - USATACC(16, 16); - return r; -} - -#undef USATACC - -uint32_t HELPER(neon_uqadd_s32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int64_t va = (int32_t)a; - int64_t vb = (uint32_t)b; - int64_t vr = va + vb; - if (vr > UINT32_MAX) { - SET_QC(); - vr = UINT32_MAX; - } else if (vr < 0) { - SET_QC(); - vr = 0; - } - return vr; -} - -uint64_t HELPER(neon_uqadd_s64)(CPUARMState *env, uint64_t a, uint64_t b) -{ - uint64_t res; - res = a + b; - /* We only need to look at the pattern of SIGN bits to detect - * +ve/-ve saturation - */ - if (~a & b & ~res & SIGNBIT64) { - SET_QC(); - res = UINT64_MAX; - } else if (a & ~b & res & SIGNBIT64) { - SET_QC(); - res = 0; - } - return res; -} - -/* Signed saturating accumulate of unsigned value - * - * Op1/Rn is treated as unsigned - * Op2/Rd is treated as signed - * - * The result is treated as a signed value and saturated as such - * - * We use a macro for the 8/16 bit cases which expects signed integers of va, - * vb, and vr for interim calculation and an unsigned 32 bit result value r. - */ - -#define SSATACC(bits, shift) \ - do { \ - va = extract32(a, shift, bits); \ - vb = sextract32(b, shift, bits); \ - vr = va + vb; \ - if (vr > INT##bits##_MAX) { \ - SET_QC(); \ - vr = INT##bits##_MAX; \ - } else if (vr < INT##bits##_MIN) { \ - SET_QC(); \ - vr = INT##bits##_MIN; \ - } \ - r = deposit32(r, shift, bits, vr); \ - } while (0) - -uint32_t HELPER(neon_sqadd_u8)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int16_t va, vb, vr; - uint32_t r = 0; - - SSATACC(8, 0); - SSATACC(8, 8); - SSATACC(8, 16); - SSATACC(8, 24); - return r; -} - -uint32_t HELPER(neon_sqadd_u16)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int32_t va, vb, vr; - uint32_t r = 0; - - SSATACC(16, 0); - SSATACC(16, 16); - - return r; -} - -#undef SSATACC - -uint32_t HELPER(neon_sqadd_u32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - int64_t res; - int64_t op1 = (uint32_t)a; - int64_t op2 = (int32_t)b; - res = op1 + op2; - if (res > INT32_MAX) { - SET_QC(); - res = INT32_MAX; - } else if (res < INT32_MIN) { - SET_QC(); - res = INT32_MIN; - } - return res; -} - -uint64_t HELPER(neon_sqadd_u64)(CPUARMState *env, uint64_t a, uint64_t b) -{ - uint64_t res; - res = a + b; - /* We only need to look at the pattern of SIGN bits to detect an overflow */ - if (((a & res) - | (~b & res) - | (a & ~b)) & SIGNBIT64) { - SET_QC(); - res = INT64_MAX; - } - return res; -} - - -#define NEON_USAT(dest, src1, src2, type) do { \ - uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - dest = 0; \ - } else { \ - dest = tmp; \ - }} while(0) -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t) -NEON_VOP_ENV(qsub_u8, neon_u8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t) -NEON_VOP_ENV(qsub_u16, neon_u16, 2) -#undef NEON_FN -#undef NEON_USAT - -uint32_t HELPER(neon_qsub_u32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a - b; - if (res > a) { - SET_QC(); - res = 0; - } - return res; -} - -uint64_t HELPER(neon_qsub_u64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - if (src1 < src2) { - SET_QC(); - res = 0; - } else { - res = src1 - src2; - } - return res; -} - -#define NEON_SSAT(dest, src1, src2, type) do { \ - int32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ - if (tmp != (type)tmp) { \ - SET_QC(); \ - if (src2 < 0) { \ - tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \ - } else { \ - tmp = 1 << (sizeof(type) * 8 - 1); \ - } \ - } \ - dest = tmp; \ - } while(0) -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t) -NEON_VOP_ENV(qsub_s8, neon_s8, 4) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t) -NEON_VOP_ENV(qsub_s16, neon_s16, 2) -#undef NEON_FN -#undef NEON_SSAT - -uint32_t HELPER(neon_qsub_s32)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a - b; - if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { - SET_QC(); - res = ~(((int32_t)a >> 31) ^ SIGNBIT); - } - return res; -} - -uint64_t HELPER(neon_qsub_s64)(CPUARMState *env, uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 - src2; - if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) { - SET_QC(); - res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; - } - return res; -} - -#define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1 -NEON_VOP(hadd_s8, neon_s8, 4) -NEON_VOP(hadd_u8, neon_u8, 4) -NEON_VOP(hadd_s16, neon_s16, 2) -NEON_VOP(hadd_u16, neon_u16, 2) -#undef NEON_FN - -int32_t HELPER(neon_hadd_s32)(int32_t src1, int32_t src2) -{ - int32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if (src1 & src2 & 1) - dest++; - return dest; -} - -uint32_t HELPER(neon_hadd_u32)(uint32_t src1, uint32_t src2) -{ - uint32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if (src1 & src2 & 1) - dest++; - return dest; -} - -#define NEON_FN(dest, src1, src2) dest = (src1 + src2 + 1) >> 1 -NEON_VOP(rhadd_s8, neon_s8, 4) -NEON_VOP(rhadd_u8, neon_u8, 4) -NEON_VOP(rhadd_s16, neon_s16, 2) -NEON_VOP(rhadd_u16, neon_u16, 2) -#undef NEON_FN - -int32_t HELPER(neon_rhadd_s32)(int32_t src1, int32_t src2) -{ - int32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if ((src1 | src2) & 1) - dest++; - return dest; -} - -uint32_t HELPER(neon_rhadd_u32)(uint32_t src1, uint32_t src2) -{ - uint32_t dest; - - dest = (src1 >> 1) + (src2 >> 1); - if ((src1 | src2) & 1) - dest++; - return dest; -} - -#define NEON_FN(dest, src1, src2) dest = (src1 - src2) >> 1 -NEON_VOP(hsub_s8, neon_s8, 4) -NEON_VOP(hsub_u8, neon_u8, 4) -NEON_VOP(hsub_s16, neon_s16, 2) -NEON_VOP(hsub_u16, neon_u16, 2) -#undef NEON_FN - -int32_t HELPER(neon_hsub_s32)(int32_t src1, int32_t src2) -{ - int32_t dest; - - dest = (src1 >> 1) - (src2 >> 1); - if ((~src1) & src2 & 1) - dest--; - return dest; -} - -uint32_t HELPER(neon_hsub_u32)(uint32_t src1, uint32_t src2) -{ - uint32_t dest; - - dest = (src1 >> 1) - (src2 >> 1); - if ((~src1) & src2 & 1) - dest--; - return dest; -} - -#define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2 -NEON_POP(pmin_s8, neon_s8, 4) -NEON_POP(pmin_u8, neon_u8, 4) -NEON_POP(pmin_s16, neon_s16, 2) -NEON_POP(pmin_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? src1 : src2 -NEON_POP(pmax_s8, neon_s8, 4) -NEON_POP(pmax_u8, neon_u8, 4) -NEON_POP(pmax_s16, neon_s16, 2) -NEON_POP(pmax_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, false, NULL)) -NEON_VOP(shl_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, false, NULL)) -NEON_VOP(shl_s16, neon_s16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, true, NULL)) -NEON_VOP(rshl_s8, neon_s8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, true, NULL)) -NEON_VOP(rshl_s16, neon_s16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_rshl_s32)(uint32_t val, uint32_t shift) -{ - return do_sqrshl_bhs(val, (int8_t)shift, 32, true, NULL); -} - -uint64_t HELPER(neon_rshl_s64)(uint64_t val, uint64_t shift) -{ - return do_sqrshl_d(val, (int8_t)shift, true, NULL); -} - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, true, NULL)) -NEON_VOP(rshl_u8, neon_u8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, true, NULL)) -NEON_VOP(rshl_u16, neon_u16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shift) -{ - return do_uqrshl_bhs(val, (int8_t)shift, 32, true, NULL); -} - -uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shift) -{ - return do_uqrshl_d(val, (int8_t)shift, true, NULL); -} - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) -NEON_VOP_ENV(qshl_u8, neon_u8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) -NEON_VOP_ENV(qshl_u16, neon_u16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_qshl_u32)(CPUARMState *env, uint32_t val, uint32_t shift) -{ - return do_uqrshl_bhs(val, (int8_t)shift, 32, false, env->vfp.qc); -} - -uint64_t HELPER(neon_qshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) -{ - return do_uqrshl_d(val, (int8_t)shift, false, env->vfp.qc); -} - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) -NEON_VOP_ENV(qshl_s8, neon_s8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) -NEON_VOP_ENV(qshl_s16, neon_s16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_qshl_s32)(CPUARMState *env, uint32_t val, uint32_t shift) -{ - return do_sqrshl_bhs(val, (int8_t)shift, 32, false, env->vfp.qc); -} - -uint64_t HELPER(neon_qshl_s64)(CPUARMState *env, uint64_t val, uint64_t shift) -{ - return do_sqrshl_d(val, (int8_t)shift, false, env->vfp.qc); -} - -#define NEON_FN(dest, src1, src2) \ - (dest = do_suqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) -NEON_VOP_ENV(qshlu_s8, neon_s8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_suqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) -NEON_VOP_ENV(qshlu_s16, neon_s16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_qshlu_s32)(CPUARMState *env, uint32_t val, uint32_t shift) -{ - return do_suqrshl_bhs(val, (int8_t)shift, 32, false, env->vfp.qc); -} - -uint64_t HELPER(neon_qshlu_s64)(CPUARMState *env, uint64_t val, uint64_t shift) -{ - return do_suqrshl_d(val, (int8_t)shift, false, env->vfp.qc); -} - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, true, env->vfp.qc)) -NEON_VOP_ENV(qrshl_u8, neon_u8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, true, env->vfp.qc)) -NEON_VOP_ENV(qrshl_u16, neon_u16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_qrshl_u32)(CPUARMState *env, uint32_t val, uint32_t shift) -{ - return do_uqrshl_bhs(val, (int8_t)shift, 32, true, env->vfp.qc); -} - -uint64_t HELPER(neon_qrshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) -{ - return do_uqrshl_d(val, (int8_t)shift, true, env->vfp.qc); -} - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, true, env->vfp.qc)) -NEON_VOP_ENV(qrshl_s8, neon_s8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) \ - (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, true, env->vfp.qc)) -NEON_VOP_ENV(qrshl_s16, neon_s16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_qrshl_s32)(CPUARMState *env, uint32_t val, uint32_t shift) -{ - return do_sqrshl_bhs(val, (int8_t)shift, 32, true, env->vfp.qc); -} - -uint64_t HELPER(neon_qrshl_s64)(CPUARMState *env, uint64_t val, uint64_t shift) -{ - return do_sqrshl_d(val, (int8_t)shift, true, env->vfp.qc); -} - -uint32_t HELPER(neon_add_u8)(uint32_t a, uint32_t b) -{ - uint32_t mask; - mask = (a ^ b) & 0x80808080u; - a &= ~0x80808080u; - b &= ~0x80808080u; - return (a + b) ^ mask; -} - -uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b) -{ - uint32_t mask; - mask = (a ^ b) & 0x80008000u; - a &= ~0x80008000u; - b &= ~0x80008000u; - return (a + b) ^ mask; -} - -#define NEON_FN(dest, src1, src2) dest = src1 + src2 -NEON_POP(padd_u8, neon_u8, 4) -NEON_POP(padd_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) dest = src1 - src2 -NEON_VOP(sub_u8, neon_u8, 4) -NEON_VOP(sub_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) dest = src1 * src2 -NEON_VOP(mul_u8, neon_u8, 4) -NEON_VOP(mul_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0 -NEON_VOP(tst_u8, neon_u8, 4) -NEON_VOP(tst_u16, neon_u16, 2) -NEON_VOP(tst_u32, neon_u32, 1) -#undef NEON_FN - -/* Count Leading Sign/Zero Bits. */ -static inline int do_clz8(uint8_t x) -{ - int n; - for (n = 8; x; n--) - x >>= 1; - return n; -} - -static inline int do_clz16(uint16_t x) -{ - int n; - for (n = 16; x; n--) - x >>= 1; - return n; -} - -#define NEON_FN(dest, src, dummy) dest = do_clz8(src) -NEON_VOP1(clz_u8, neon_u8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src, dummy) dest = do_clz16(src) -NEON_VOP1(clz_u16, neon_u16, 2) -#undef NEON_FN - -#define NEON_FN(dest, src, dummy) dest = do_clz8((src < 0) ? ~src : src) - 1 -NEON_VOP1(cls_s8, neon_s8, 4) -#undef NEON_FN - -#define NEON_FN(dest, src, dummy) dest = do_clz16((src < 0) ? ~src : src) - 1 -NEON_VOP1(cls_s16, neon_s16, 2) -#undef NEON_FN - -uint32_t HELPER(neon_cls_s32)(uint32_t x) -{ - int count; - if ((int32_t)x < 0) - x = ~x; - for (count = 32; x; count--) - x = x >> 1; - return count - 1; -} - -/* Bit count. */ -uint32_t HELPER(neon_cnt_u8)(uint32_t x) -{ - x = (x & 0x55555555) + ((x >> 1) & 0x55555555); - x = (x & 0x33333333) + ((x >> 2) & 0x33333333); - x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f); - return x; -} - -/* Reverse bits in each 8 bit word */ -uint32_t HELPER(neon_rbit_u8)(uint32_t x) -{ - x = ((x & 0xf0f0f0f0) >> 4) - | ((x & 0x0f0f0f0f) << 4); - x = ((x & 0x88888888) >> 3) - | ((x & 0x44444444) >> 1) - | ((x & 0x22222222) << 1) - | ((x & 0x11111111) << 3); - return x; -} - -#define NEON_QDMULH16(dest, src1, src2, round) do { \ - uint32_t tmp = (int32_t)(int16_t) src1 * (int16_t) src2; \ - if ((tmp ^ (tmp << 1)) & SIGNBIT) { \ - SET_QC(); \ - tmp = (tmp >> 31) ^ ~SIGNBIT; \ - } else { \ - tmp <<= 1; \ - } \ - if (round) { \ - int32_t old = tmp; \ - tmp += 1 << 15; \ - if ((int32_t)tmp < old) { \ - SET_QC(); \ - tmp = SIGNBIT - 1; \ - } \ - } \ - dest = tmp >> 16; \ - } while(0) -#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0) -NEON_VOP_ENV(qdmulh_s16, neon_s16, 2) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1) -NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2) -#undef NEON_FN -#undef NEON_QDMULH16 - -#define NEON_QDMULH32(dest, src1, src2, round) do { \ - uint64_t tmp = (int64_t)(int32_t) src1 * (int32_t) src2; \ - if ((tmp ^ (tmp << 1)) & SIGNBIT64) { \ - SET_QC(); \ - tmp = (tmp >> 63) ^ ~SIGNBIT64; \ - } else { \ - tmp <<= 1; \ - } \ - if (round) { \ - int64_t old = tmp; \ - tmp += (int64_t)1 << 31; \ - if ((int64_t)tmp < old) { \ - SET_QC(); \ - tmp = SIGNBIT64 - 1; \ - } \ - } \ - dest = tmp >> 32; \ - } while(0) -#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0) -NEON_VOP_ENV(qdmulh_s32, neon_s32, 1) -#undef NEON_FN -#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1) -NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1) -#undef NEON_FN -#undef NEON_QDMULH32 - -uint32_t HELPER(neon_narrow_u8)(uint64_t x) -{ - return (x & 0xffu) | ((x >> 8) & 0xff00u) | ((x >> 16) & 0xff0000u) - | ((x >> 24) & 0xff000000u); -} - -uint32_t HELPER(neon_narrow_u16)(uint64_t x) -{ - return (x & 0xffffu) | ((x >> 16) & 0xffff0000u); -} - -uint32_t HELPER(neon_narrow_high_u8)(uint64_t x) -{ - return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00) - | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000); -} - -uint32_t HELPER(neon_narrow_high_u16)(uint64_t x) -{ - return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); -} - -uint32_t HELPER(neon_narrow_round_high_u8)(uint64_t x) -{ - x &= 0xff80ff80ff80ff80ull; - x += 0x0080008000800080ull; - return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00) - | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000); -} - -uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x) -{ - x &= 0xffff8000ffff8000ull; - x += 0x0000800000008000ull; - return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); -} - -uint32_t HELPER(neon_unarrow_sat8)(CPUARMState *env, uint64_t x) -{ - uint16_t s; - uint8_t d; - uint32_t res = 0; -#define SAT8(n) \ - s = x >> n; \ - if (s & 0x8000) { \ - SET_QC(); \ - } else { \ - if (s > 0xff) { \ - d = 0xff; \ - SET_QC(); \ - } else { \ - d = s; \ - } \ - res |= (uint32_t)d << (n / 2); \ - } - - SAT8(0); - SAT8(16); - SAT8(32); - SAT8(48); -#undef SAT8 - return res; -} - -uint32_t HELPER(neon_narrow_sat_u8)(CPUARMState *env, uint64_t x) -{ - uint16_t s; - uint8_t d; - uint32_t res = 0; -#define SAT8(n) \ - s = x >> n; \ - if (s > 0xff) { \ - d = 0xff; \ - SET_QC(); \ - } else { \ - d = s; \ - } \ - res |= (uint32_t)d << (n / 2); - - SAT8(0); - SAT8(16); - SAT8(32); - SAT8(48); -#undef SAT8 - return res; -} - -uint32_t HELPER(neon_narrow_sat_s8)(CPUARMState *env, uint64_t x) -{ - int16_t s; - uint8_t d; - uint32_t res = 0; -#define SAT8(n) \ - s = x >> n; \ - if (s != (int8_t)s) { \ - d = (s >> 15) ^ 0x7f; \ - SET_QC(); \ - } else { \ - d = s; \ - } \ - res |= (uint32_t)d << (n / 2); - - SAT8(0); - SAT8(16); - SAT8(32); - SAT8(48); -#undef SAT8 - return res; -} - -uint32_t HELPER(neon_unarrow_sat16)(CPUARMState *env, uint64_t x) -{ - uint32_t high; - uint32_t low; - low = x; - if (low & 0x80000000) { - low = 0; - SET_QC(); - } else if (low > 0xffff) { - low = 0xffff; - SET_QC(); - } - high = x >> 32; - if (high & 0x80000000) { - high = 0; - SET_QC(); - } else if (high > 0xffff) { - high = 0xffff; - SET_QC(); - } - return low | (high << 16); -} - -uint32_t HELPER(neon_narrow_sat_u16)(CPUARMState *env, uint64_t x) -{ - uint32_t high; - uint32_t low; - low = x; - if (low > 0xffff) { - low = 0xffff; - SET_QC(); - } - high = x >> 32; - if (high > 0xffff) { - high = 0xffff; - SET_QC(); - } - return low | (high << 16); -} - -uint32_t HELPER(neon_narrow_sat_s16)(CPUARMState *env, uint64_t x) -{ - int32_t low; - int32_t high; - low = x; - if (low != (int16_t)low) { - low = (low >> 31) ^ 0x7fff; - SET_QC(); - } - high = x >> 32; - if (high != (int16_t)high) { - high = (high >> 31) ^ 0x7fff; - SET_QC(); - } - return (uint16_t)low | (high << 16); -} - -uint32_t HELPER(neon_unarrow_sat32)(CPUARMState *env, uint64_t x) -{ - if (x & 0x8000000000000000ull) { - SET_QC(); - return 0; - } - if (x > 0xffffffffu) { - SET_QC(); - return 0xffffffffu; - } - return x; -} - -uint32_t HELPER(neon_narrow_sat_u32)(CPUARMState *env, uint64_t x) -{ - if (x > 0xffffffffu) { - SET_QC(); - return 0xffffffffu; - } - return x; -} - -uint32_t HELPER(neon_narrow_sat_s32)(CPUARMState *env, uint64_t x) -{ - if ((int64_t)x != (int32_t)x) { - SET_QC(); - return ((int64_t)x >> 63) ^ 0x7fffffff; - } - return x; -} - -uint64_t HELPER(neon_widen_u8)(uint32_t x) -{ - uint64_t tmp; - uint64_t ret; - ret = (uint8_t)x; - tmp = (uint8_t)(x >> 8); - ret |= tmp << 16; - tmp = (uint8_t)(x >> 16); - ret |= tmp << 32; - tmp = (uint8_t)(x >> 24); - ret |= tmp << 48; - return ret; -} - -uint64_t HELPER(neon_widen_s8)(uint32_t x) -{ - uint64_t tmp; - uint64_t ret; - ret = (uint16_t)(int8_t)x; - tmp = (uint16_t)(int8_t)(x >> 8); - ret |= tmp << 16; - tmp = (uint16_t)(int8_t)(x >> 16); - ret |= tmp << 32; - tmp = (uint16_t)(int8_t)(x >> 24); - ret |= tmp << 48; - return ret; -} - -uint64_t HELPER(neon_widen_u16)(uint32_t x) -{ - uint64_t high = (uint16_t)(x >> 16); - return ((uint16_t)x) | (high << 32); -} - -uint64_t HELPER(neon_widen_s16)(uint32_t x) -{ - uint64_t high = (int16_t)(x >> 16); - return ((uint32_t)(int16_t)x) | (high << 32); -} - -uint64_t HELPER(neon_addl_u16)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ b) & 0x8000800080008000ull; - a &= ~0x8000800080008000ull; - b &= ~0x8000800080008000ull; - return (a + b) ^ mask; -} - -uint64_t HELPER(neon_addl_u32)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ b) & 0x8000000080000000ull; - a &= ~0x8000000080000000ull; - b &= ~0x8000000080000000ull; - return (a + b) ^ mask; -} - -uint64_t HELPER(neon_paddl_u16)(uint64_t a, uint64_t b) -{ - uint64_t tmp; - uint64_t tmp2; - - tmp = a & 0x0000ffff0000ffffull; - tmp += (a >> 16) & 0x0000ffff0000ffffull; - tmp2 = b & 0xffff0000ffff0000ull; - tmp2 += (b << 16) & 0xffff0000ffff0000ull; - return ( tmp & 0xffff) - | ((tmp >> 16) & 0xffff0000ull) - | ((tmp2 << 16) & 0xffff00000000ull) - | ( tmp2 & 0xffff000000000000ull); -} - -uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b) -{ - uint32_t low = a + (a >> 32); - uint32_t high = b + (b >> 32); - return low + ((uint64_t)high << 32); -} - -uint64_t HELPER(neon_subl_u16)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ ~b) & 0x8000800080008000ull; - a |= 0x8000800080008000ull; - b &= ~0x8000800080008000ull; - return (a - b) ^ mask; -} - -uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b) -{ - uint64_t mask; - mask = (a ^ ~b) & 0x8000000080000000ull; - a |= 0x8000000080000000ull; - b &= ~0x8000000080000000ull; - return (a - b) ^ mask; -} - -uint64_t HELPER(neon_addl_saturate_s32)(CPUARMState *env, uint64_t a, uint64_t b) -{ - uint32_t x, y; - uint32_t low, high; - - x = a; - y = b; - low = x + y; - if (((low ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) { - SET_QC(); - low = ((int32_t)x >> 31) ^ ~SIGNBIT; - } - x = a >> 32; - y = b >> 32; - high = x + y; - if (((high ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) { - SET_QC(); - high = ((int32_t)x >> 31) ^ ~SIGNBIT; - } - return low | ((uint64_t)high << 32); -} - -uint64_t HELPER(neon_addl_saturate_s64)(CPUARMState *env, uint64_t a, uint64_t b) -{ - uint64_t result; - - result = a + b; - if (((result ^ a) & SIGNBIT64) && !((a ^ b) & SIGNBIT64)) { - SET_QC(); - result = ((int64_t)a >> 63) ^ ~SIGNBIT64; - } - return result; -} - -/* We have to do the arithmetic in a larger type than - * the input type, because for example with a signed 32 bit - * op the absolute difference can overflow a signed 32 bit value. - */ -#define DO_ABD(dest, x, y, intype, arithtype) do { \ - arithtype tmp_x = (intype)(x); \ - arithtype tmp_y = (intype)(y); \ - dest = ((tmp_x > tmp_y) ? tmp_x - tmp_y : tmp_y - tmp_x); \ - } while(0) - -uint64_t HELPER(neon_abdl_u16)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - DO_ABD(result, a, b, uint8_t, uint32_t); - DO_ABD(tmp, a >> 8, b >> 8, uint8_t, uint32_t); - result |= tmp << 16; - DO_ABD(tmp, a >> 16, b >> 16, uint8_t, uint32_t); - result |= tmp << 32; - DO_ABD(tmp, a >> 24, b >> 24, uint8_t, uint32_t); - result |= tmp << 48; - return result; -} - -uint64_t HELPER(neon_abdl_s16)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - DO_ABD(result, a, b, int8_t, int32_t); - DO_ABD(tmp, a >> 8, b >> 8, int8_t, int32_t); - result |= tmp << 16; - DO_ABD(tmp, a >> 16, b >> 16, int8_t, int32_t); - result |= tmp << 32; - DO_ABD(tmp, a >> 24, b >> 24, int8_t, int32_t); - result |= tmp << 48; - return result; -} - -uint64_t HELPER(neon_abdl_u32)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - DO_ABD(result, a, b, uint16_t, uint32_t); - DO_ABD(tmp, a >> 16, b >> 16, uint16_t, uint32_t); - return result | (tmp << 32); -} - -uint64_t HELPER(neon_abdl_s32)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - DO_ABD(result, a, b, int16_t, int32_t); - DO_ABD(tmp, a >> 16, b >> 16, int16_t, int32_t); - return result | (tmp << 32); -} - -uint64_t HELPER(neon_abdl_u64)(uint32_t a, uint32_t b) -{ - uint64_t result; - DO_ABD(result, a, b, uint32_t, uint64_t); - return result; -} - -uint64_t HELPER(neon_abdl_s64)(uint32_t a, uint32_t b) -{ - uint64_t result; - DO_ABD(result, a, b, int32_t, int64_t); - return result; -} -#undef DO_ABD - -/* Widening multiply. Named type is the source type. */ -#define DO_MULL(dest, x, y, type1, type2) do { \ - type1 tmp_x = x; \ - type1 tmp_y = y; \ - dest = (type2)((type2)tmp_x * (type2)tmp_y); \ - } while(0) - -uint64_t HELPER(neon_mull_u8)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - - DO_MULL(result, a, b, uint8_t, uint16_t); - DO_MULL(tmp, a >> 8, b >> 8, uint8_t, uint16_t); - result |= tmp << 16; - DO_MULL(tmp, a >> 16, b >> 16, uint8_t, uint16_t); - result |= tmp << 32; - DO_MULL(tmp, a >> 24, b >> 24, uint8_t, uint16_t); - result |= tmp << 48; - return result; -} - -uint64_t HELPER(neon_mull_s8)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - - DO_MULL(result, a, b, int8_t, uint16_t); - DO_MULL(tmp, a >> 8, b >> 8, int8_t, uint16_t); - result |= tmp << 16; - DO_MULL(tmp, a >> 16, b >> 16, int8_t, uint16_t); - result |= tmp << 32; - DO_MULL(tmp, a >> 24, b >> 24, int8_t, uint16_t); - result |= tmp << 48; - return result; -} - -uint64_t HELPER(neon_mull_u16)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - - DO_MULL(result, a, b, uint16_t, uint32_t); - DO_MULL(tmp, a >> 16, b >> 16, uint16_t, uint32_t); - return result | (tmp << 32); -} - -uint64_t HELPER(neon_mull_s16)(uint32_t a, uint32_t b) -{ - uint64_t tmp; - uint64_t result; - - DO_MULL(result, a, b, int16_t, uint32_t); - DO_MULL(tmp, a >> 16, b >> 16, int16_t, uint32_t); - return result | (tmp << 32); -} - -uint64_t HELPER(neon_negl_u16)(uint64_t x) -{ - uint16_t tmp; - uint64_t result; - result = (uint16_t)-x; - tmp = -(x >> 16); - result |= (uint64_t)tmp << 16; - tmp = -(x >> 32); - result |= (uint64_t)tmp << 32; - tmp = -(x >> 48); - result |= (uint64_t)tmp << 48; - return result; -} - -uint64_t HELPER(neon_negl_u32)(uint64_t x) -{ - uint32_t low = -x; - uint32_t high = -(x >> 32); - return low | ((uint64_t)high << 32); -} - -/* Saturating sign manipulation. */ -/* ??? Make these use NEON_VOP1 */ -#define DO_QABS8(x) do { \ - if (x == (int8_t)0x80) { \ - x = 0x7f; \ - SET_QC(); \ - } else if (x < 0) { \ - x = -x; \ - }} while (0) -uint32_t HELPER(neon_qabs_s8)(CPUARMState *env, uint32_t x) -{ - neon_s8 vec; - NEON_UNPACK(neon_s8, vec, x); - DO_QABS8(vec.v1); - DO_QABS8(vec.v2); - DO_QABS8(vec.v3); - DO_QABS8(vec.v4); - NEON_PACK(neon_s8, x, vec); - return x; -} -#undef DO_QABS8 - -#define DO_QNEG8(x) do { \ - if (x == (int8_t)0x80) { \ - x = 0x7f; \ - SET_QC(); \ - } else { \ - x = -x; \ - }} while (0) -uint32_t HELPER(neon_qneg_s8)(CPUARMState *env, uint32_t x) -{ - neon_s8 vec; - NEON_UNPACK(neon_s8, vec, x); - DO_QNEG8(vec.v1); - DO_QNEG8(vec.v2); - DO_QNEG8(vec.v3); - DO_QNEG8(vec.v4); - NEON_PACK(neon_s8, x, vec); - return x; -} -#undef DO_QNEG8 - -#define DO_QABS16(x) do { \ - if (x == (int16_t)0x8000) { \ - x = 0x7fff; \ - SET_QC(); \ - } else if (x < 0) { \ - x = -x; \ - }} while (0) -uint32_t HELPER(neon_qabs_s16)(CPUARMState *env, uint32_t x) -{ - neon_s16 vec; - NEON_UNPACK(neon_s16, vec, x); - DO_QABS16(vec.v1); - DO_QABS16(vec.v2); - NEON_PACK(neon_s16, x, vec); - return x; -} -#undef DO_QABS16 - -#define DO_QNEG16(x) do { \ - if (x == (int16_t)0x8000) { \ - x = 0x7fff; \ - SET_QC(); \ - } else { \ - x = -x; \ - }} while (0) -uint32_t HELPER(neon_qneg_s16)(CPUARMState *env, uint32_t x) -{ - neon_s16 vec; - NEON_UNPACK(neon_s16, vec, x); - DO_QNEG16(vec.v1); - DO_QNEG16(vec.v2); - NEON_PACK(neon_s16, x, vec); - return x; -} -#undef DO_QNEG16 - -uint32_t HELPER(neon_qabs_s32)(CPUARMState *env, uint32_t x) -{ - if (x == SIGNBIT) { - SET_QC(); - x = ~SIGNBIT; - } else if ((int32_t)x < 0) { - x = -x; - } - return x; -} - -uint32_t HELPER(neon_qneg_s32)(CPUARMState *env, uint32_t x) -{ - if (x == SIGNBIT) { - SET_QC(); - x = ~SIGNBIT; - } else { - x = -x; - } - return x; -} - -uint64_t HELPER(neon_qabs_s64)(CPUARMState *env, uint64_t x) -{ - if (x == SIGNBIT64) { - SET_QC(); - x = ~SIGNBIT64; - } else if ((int64_t)x < 0) { - x = -x; - } - return x; -} - -uint64_t HELPER(neon_qneg_s64)(CPUARMState *env, uint64_t x) -{ - if (x == SIGNBIT64) { - SET_QC(); - x = ~SIGNBIT64; - } else { - x = -x; - } - return x; -} - -/* NEON Float helpers. */ - -/* Floating point comparisons produce an integer result. - * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. - * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. - */ -uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - return -float32_eq_quiet(make_float32(a), make_float32(b), fpst); -} - -uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - return -float32_le(make_float32(b), make_float32(a), fpst); -} - -uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - return -float32_lt(make_float32(b), make_float32(a), fpst); -} - -uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - float32 f0 = float32_abs(make_float32(a)); - float32 f1 = float32_abs(make_float32(b)); - return -float32_le(f1, f0, fpst); -} - -uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp) -{ - float_status *fpst = fpstp; - float32 f0 = float32_abs(make_float32(a)); - float32 f1 = float32_abs(make_float32(b)); - return -float32_lt(f1, f0, fpst); -} - -uint64_t HELPER(neon_acge_f64)(uint64_t a, uint64_t b, void *fpstp) -{ - float_status *fpst = fpstp; - float64 f0 = float64_abs(make_float64(a)); - float64 f1 = float64_abs(make_float64(b)); - return -float64_le(f1, f0, fpst); -} - -uint64_t HELPER(neon_acgt_f64)(uint64_t a, uint64_t b, void *fpstp) -{ - float_status *fpst = fpstp; - float64 f0 = float64_abs(make_float64(a)); - float64 f1 = float64_abs(make_float64(b)); - return -float64_lt(f1, f0, fpst); -} - -#define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1)) - -void HELPER(neon_qunzip8)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd0 = rd[0], zd1 = rd[1]; - uint64_t zm0 = rm[0], zm1 = rm[1]; - - uint64_t d0 = ELEM(zd0, 0, 8) | (ELEM(zd0, 2, 8) << 8) - | (ELEM(zd0, 4, 8) << 16) | (ELEM(zd0, 6, 8) << 24) - | (ELEM(zd1, 0, 8) << 32) | (ELEM(zd1, 2, 8) << 40) - | (ELEM(zd1, 4, 8) << 48) | (ELEM(zd1, 6, 8) << 56); - uint64_t d1 = ELEM(zm0, 0, 8) | (ELEM(zm0, 2, 8) << 8) - | (ELEM(zm0, 4, 8) << 16) | (ELEM(zm0, 6, 8) << 24) - | (ELEM(zm1, 0, 8) << 32) | (ELEM(zm1, 2, 8) << 40) - | (ELEM(zm1, 4, 8) << 48) | (ELEM(zm1, 6, 8) << 56); - uint64_t m0 = ELEM(zd0, 1, 8) | (ELEM(zd0, 3, 8) << 8) - | (ELEM(zd0, 5, 8) << 16) | (ELEM(zd0, 7, 8) << 24) - | (ELEM(zd1, 1, 8) << 32) | (ELEM(zd1, 3, 8) << 40) - | (ELEM(zd1, 5, 8) << 48) | (ELEM(zd1, 7, 8) << 56); - uint64_t m1 = ELEM(zm0, 1, 8) | (ELEM(zm0, 3, 8) << 8) - | (ELEM(zm0, 5, 8) << 16) | (ELEM(zm0, 7, 8) << 24) - | (ELEM(zm1, 1, 8) << 32) | (ELEM(zm1, 3, 8) << 40) - | (ELEM(zm1, 5, 8) << 48) | (ELEM(zm1, 7, 8) << 56); - - rm[0] = m0; - rm[1] = m1; - rd[0] = d0; - rd[1] = d1; -} - -void HELPER(neon_qunzip16)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd0 = rd[0], zd1 = rd[1]; - uint64_t zm0 = rm[0], zm1 = rm[1]; - - uint64_t d0 = ELEM(zd0, 0, 16) | (ELEM(zd0, 2, 16) << 16) - | (ELEM(zd1, 0, 16) << 32) | (ELEM(zd1, 2, 16) << 48); - uint64_t d1 = ELEM(zm0, 0, 16) | (ELEM(zm0, 2, 16) << 16) - | (ELEM(zm1, 0, 16) << 32) | (ELEM(zm1, 2, 16) << 48); - uint64_t m0 = ELEM(zd0, 1, 16) | (ELEM(zd0, 3, 16) << 16) - | (ELEM(zd1, 1, 16) << 32) | (ELEM(zd1, 3, 16) << 48); - uint64_t m1 = ELEM(zm0, 1, 16) | (ELEM(zm0, 3, 16) << 16) - | (ELEM(zm1, 1, 16) << 32) | (ELEM(zm1, 3, 16) << 48); - - rm[0] = m0; - rm[1] = m1; - rd[0] = d0; - rd[1] = d1; -} - -void HELPER(neon_qunzip32)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd0 = rd[0], zd1 = rd[1]; - uint64_t zm0 = rm[0], zm1 = rm[1]; - - uint64_t d0 = ELEM(zd0, 0, 32) | (ELEM(zd1, 0, 32) << 32); - uint64_t d1 = ELEM(zm0, 0, 32) | (ELEM(zm1, 0, 32) << 32); - uint64_t m0 = ELEM(zd0, 1, 32) | (ELEM(zd1, 1, 32) << 32); - uint64_t m1 = ELEM(zm0, 1, 32) | (ELEM(zm1, 1, 32) << 32); - - rm[0] = m0; - rm[1] = m1; - rd[0] = d0; - rd[1] = d1; -} - -void HELPER(neon_unzip8)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd = rd[0], zm = rm[0]; - - uint64_t d0 = ELEM(zd, 0, 8) | (ELEM(zd, 2, 8) << 8) - | (ELEM(zd, 4, 8) << 16) | (ELEM(zd, 6, 8) << 24) - | (ELEM(zm, 0, 8) << 32) | (ELEM(zm, 2, 8) << 40) - | (ELEM(zm, 4, 8) << 48) | (ELEM(zm, 6, 8) << 56); - uint64_t m0 = ELEM(zd, 1, 8) | (ELEM(zd, 3, 8) << 8) - | (ELEM(zd, 5, 8) << 16) | (ELEM(zd, 7, 8) << 24) - | (ELEM(zm, 1, 8) << 32) | (ELEM(zm, 3, 8) << 40) - | (ELEM(zm, 5, 8) << 48) | (ELEM(zm, 7, 8) << 56); - - rm[0] = m0; - rd[0] = d0; -} - -void HELPER(neon_unzip16)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd = rd[0], zm = rm[0]; - - uint64_t d0 = ELEM(zd, 0, 16) | (ELEM(zd, 2, 16) << 16) - | (ELEM(zm, 0, 16) << 32) | (ELEM(zm, 2, 16) << 48); - uint64_t m0 = ELEM(zd, 1, 16) | (ELEM(zd, 3, 16) << 16) - | (ELEM(zm, 1, 16) << 32) | (ELEM(zm, 3, 16) << 48); - - rm[0] = m0; - rd[0] = d0; -} - -void HELPER(neon_qzip8)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd0 = rd[0], zd1 = rd[1]; - uint64_t zm0 = rm[0], zm1 = rm[1]; - - uint64_t d0 = ELEM(zd0, 0, 8) | (ELEM(zm0, 0, 8) << 8) - | (ELEM(zd0, 1, 8) << 16) | (ELEM(zm0, 1, 8) << 24) - | (ELEM(zd0, 2, 8) << 32) | (ELEM(zm0, 2, 8) << 40) - | (ELEM(zd0, 3, 8) << 48) | (ELEM(zm0, 3, 8) << 56); - uint64_t d1 = ELEM(zd0, 4, 8) | (ELEM(zm0, 4, 8) << 8) - | (ELEM(zd0, 5, 8) << 16) | (ELEM(zm0, 5, 8) << 24) - | (ELEM(zd0, 6, 8) << 32) | (ELEM(zm0, 6, 8) << 40) - | (ELEM(zd0, 7, 8) << 48) | (ELEM(zm0, 7, 8) << 56); - uint64_t m0 = ELEM(zd1, 0, 8) | (ELEM(zm1, 0, 8) << 8) - | (ELEM(zd1, 1, 8) << 16) | (ELEM(zm1, 1, 8) << 24) - | (ELEM(zd1, 2, 8) << 32) | (ELEM(zm1, 2, 8) << 40) - | (ELEM(zd1, 3, 8) << 48) | (ELEM(zm1, 3, 8) << 56); - uint64_t m1 = ELEM(zd1, 4, 8) | (ELEM(zm1, 4, 8) << 8) - | (ELEM(zd1, 5, 8) << 16) | (ELEM(zm1, 5, 8) << 24) - | (ELEM(zd1, 6, 8) << 32) | (ELEM(zm1, 6, 8) << 40) - | (ELEM(zd1, 7, 8) << 48) | (ELEM(zm1, 7, 8) << 56); - - rm[0] = m0; - rm[1] = m1; - rd[0] = d0; - rd[1] = d1; -} - -void HELPER(neon_qzip16)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd0 = rd[0], zd1 = rd[1]; - uint64_t zm0 = rm[0], zm1 = rm[1]; - - uint64_t d0 = ELEM(zd0, 0, 16) | (ELEM(zm0, 0, 16) << 16) - | (ELEM(zd0, 1, 16) << 32) | (ELEM(zm0, 1, 16) << 48); - uint64_t d1 = ELEM(zd0, 2, 16) | (ELEM(zm0, 2, 16) << 16) - | (ELEM(zd0, 3, 16) << 32) | (ELEM(zm0, 3, 16) << 48); - uint64_t m0 = ELEM(zd1, 0, 16) | (ELEM(zm1, 0, 16) << 16) - | (ELEM(zd1, 1, 16) << 32) | (ELEM(zm1, 1, 16) << 48); - uint64_t m1 = ELEM(zd1, 2, 16) | (ELEM(zm1, 2, 16) << 16) - | (ELEM(zd1, 3, 16) << 32) | (ELEM(zm1, 3, 16) << 48); - - rm[0] = m0; - rm[1] = m1; - rd[0] = d0; - rd[1] = d1; -} - -void HELPER(neon_qzip32)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd0 = rd[0], zd1 = rd[1]; - uint64_t zm0 = rm[0], zm1 = rm[1]; - - uint64_t d0 = ELEM(zd0, 0, 32) | (ELEM(zm0, 0, 32) << 32); - uint64_t d1 = ELEM(zd0, 1, 32) | (ELEM(zm0, 1, 32) << 32); - uint64_t m0 = ELEM(zd1, 0, 32) | (ELEM(zm1, 0, 32) << 32); - uint64_t m1 = ELEM(zd1, 1, 32) | (ELEM(zm1, 1, 32) << 32); - - rm[0] = m0; - rm[1] = m1; - rd[0] = d0; - rd[1] = d1; -} - -void HELPER(neon_zip8)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd = rd[0], zm = rm[0]; - - uint64_t d0 = ELEM(zd, 0, 8) | (ELEM(zm, 0, 8) << 8) - | (ELEM(zd, 1, 8) << 16) | (ELEM(zm, 1, 8) << 24) - | (ELEM(zd, 2, 8) << 32) | (ELEM(zm, 2, 8) << 40) - | (ELEM(zd, 3, 8) << 48) | (ELEM(zm, 3, 8) << 56); - uint64_t m0 = ELEM(zd, 4, 8) | (ELEM(zm, 4, 8) << 8) - | (ELEM(zd, 5, 8) << 16) | (ELEM(zm, 5, 8) << 24) - | (ELEM(zd, 6, 8) << 32) | (ELEM(zm, 6, 8) << 40) - | (ELEM(zd, 7, 8) << 48) | (ELEM(zm, 7, 8) << 56); - - rm[0] = m0; - rd[0] = d0; -} - -void HELPER(neon_zip16)(void *vd, void *vm) -{ - uint64_t *rd = vd, *rm = vm; - uint64_t zd = rd[0], zm = rm[0]; - - uint64_t d0 = ELEM(zd, 0, 16) | (ELEM(zm, 0, 16) << 16) - | (ELEM(zd, 1, 16) << 32) | (ELEM(zm, 1, 16) << 48); - uint64_t m0 = ELEM(zd, 2, 16) | (ELEM(zm, 2, 16) << 16) - | (ELEM(zd, 3, 16) << 32) | (ELEM(zm, 3, 16) << 48); - - rm[0] = m0; - rd[0] = d0; -} diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c deleted file mode 100644 index 70672bcd9f..0000000000 --- a/target/arm/op_helper.c +++ /dev/null @@ -1,1010 +0,0 @@ -/* - * ARM helper routines - * - * Copyright (c) 2005-2007 CodeSourcery, LLC - * - * 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 "cpu.h" -#include "exec/helper-proto.h" -#include "internals.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "cpregs.h" - -#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) -{ - CPUState *cs = env_cpu(env); - - if (target_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) { - /* - * Redirect NS EL1 exceptions to NS EL2. These are reported with - * their original syndrome register value, with the exception of - * SIMD/FP access traps, which are reported as uncategorized - * (see DDI0478C.a D1.10.4) - */ - target_el = 2; - if (syn_get_ec(syndrome) == EC_ADVSIMDFPACCESSTRAP) { - syndrome = syn_uncategorized(); - } - } - - assert(!excp_is_internal(excp)); - cs->exception_index = excp; - env->exception.syndrome = syndrome; - env->exception.target_el = target_el; - cpu_loop_exit(cs); -} - -void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, - uint32_t target_el, uintptr_t ra) -{ - CPUState *cs = env_cpu(env); - - /* - * restore_state_to_opc() will set env->exception.syndrome, so - * 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); - raise_exception(env, excp, syndrome, target_el); -} - -uint64_t HELPER(neon_tbl)(CPUARMState *env, uint32_t desc, - uint64_t ireg, uint64_t def) -{ - uint64_t tmp, val = 0; - uint32_t maxindex = ((desc & 3) + 1) * 8; - uint32_t base_reg = desc >> 2; - uint32_t shift, index, reg; - - for (shift = 0; shift < 64; shift += 8) { - index = (ireg >> shift) & 0xff; - if (index < maxindex) { - reg = base_reg + (index >> 3); - tmp = *aa32_vfp_dreg(env, reg); - tmp = ((tmp >> ((index & 7) << 3)) & 0xff) << shift; - } else { - tmp = def & (0xffull << shift); - } - val |= tmp; - } - return val; -} - -void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue) -{ - /* - * Perform the v8M stack limit check for SP updates from translated code, - * raising an exception if the limit is breached. - */ - if (newvalue < v7m_sp_limit(env)) { - /* - * Stack limit exceptions are a rare case, so rather than syncing - * PC/condbits before the call, we use raise_exception_ra() so - * that cpu_restore_state() will sort them out. - */ - raise_exception_ra(env, EXCP_STKOF, 0, 1, GETPC()); - } -} - -uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) - env->QF = 1; - return res; -} - -uint32_t HELPER(add_saturate)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { - env->QF = 1; - res = ~(((int32_t)a >> 31) ^ SIGNBIT); - } - return res; -} - -uint32_t HELPER(sub_saturate)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a - b; - if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { - env->QF = 1; - res = ~(((int32_t)a >> 31) ^ SIGNBIT); - } - return res; -} - -uint32_t HELPER(add_usaturate)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a + b; - if (res < a) { - env->QF = 1; - res = ~0; - } - return res; -} - -uint32_t HELPER(sub_usaturate)(CPUARMState *env, uint32_t a, uint32_t b) -{ - uint32_t res = a - b; - if (res > a) { - env->QF = 1; - res = 0; - } - return res; -} - -/* Signed saturation. */ -static inline uint32_t do_ssat(CPUARMState *env, int32_t val, int shift) -{ - int32_t top; - uint32_t mask; - - top = val >> shift; - mask = (1u << shift) - 1; - if (top > 0) { - env->QF = 1; - return mask; - } else if (top < -1) { - env->QF = 1; - return ~mask; - } - return val; -} - -/* Unsigned saturation. */ -static inline uint32_t do_usat(CPUARMState *env, int32_t val, int shift) -{ - uint32_t max; - - max = (1u << shift) - 1; - if (val < 0) { - env->QF = 1; - return 0; - } else if (val > max) { - env->QF = 1; - return max; - } - return val; -} - -/* Signed saturate. */ -uint32_t HELPER(ssat)(CPUARMState *env, uint32_t x, uint32_t shift) -{ - return do_ssat(env, x, shift); -} - -/* Dual halfword signed saturate. */ -uint32_t HELPER(ssat16)(CPUARMState *env, uint32_t x, uint32_t shift) -{ - uint32_t res; - - res = (uint16_t)do_ssat(env, (int16_t)x, shift); - res |= do_ssat(env, ((int32_t)x) >> 16, shift) << 16; - return res; -} - -/* Unsigned saturate. */ -uint32_t HELPER(usat)(CPUARMState *env, uint32_t x, uint32_t shift) -{ - return do_usat(env, x, shift); -} - -/* Dual halfword unsigned saturate. */ -uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift) -{ - uint32_t res; - - res = (uint16_t)do_usat(env, (int16_t)x, shift); - res |= do_usat(env, ((int32_t)x) >> 16, shift) << 16; - return res; -} - -void HELPER(setend)(CPUARMState *env) -{ - env->uncached_cpsr ^= CPSR_E; - arm_rebuild_hflags(env); -} - -void HELPER(check_bxj_trap)(CPUARMState *env, uint32_t rm) -{ - /* - * Only called if in NS EL0 or EL1 for a BXJ for a v7A CPU; - * check if HSTR.TJDBX means we need to trap to EL2. - */ - if (env->cp15.hstr_el2 & HSTR_TJDBX) { - /* - * We know the condition code check passed, so take the IMPDEF - * choice to always report CV=1 COND 0xe - */ - uint32_t syn = syn_bxjtrap(1, 0xe, rm); - raise_exception_ra(env, EXCP_HYP_TRAP, syn, 2, GETPC()); - } -} - -#ifndef CONFIG_USER_ONLY -/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. - * The function returns the target EL (1-3) if the instruction is to be trapped; - * otherwise it returns 0 indicating it is not trapped. - */ -static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) -{ - int cur_el = arm_current_el(env); - uint64_t mask; - - if (arm_feature(env, ARM_FEATURE_M)) { - /* M profile cores can never trap WFI/WFE. */ - return 0; - } - - /* If we are currently in EL0 then we need to check if SCTLR is set up for - * WFx instructions being trapped to EL1. These trap bits don't exist in v7. - */ - if (cur_el < 1 && arm_feature(env, ARM_FEATURE_V8)) { - int target_el; - - mask = is_wfe ? SCTLR_nTWE : SCTLR_nTWI; - if (arm_is_secure_below_el3(env) && !arm_el_is_aa64(env, 3)) { - /* Secure EL0 and Secure PL1 is at EL3 */ - target_el = 3; - } else { - target_el = 1; - } - - if (!(env->cp15.sctlr_el[target_el] & mask)) { - return target_el; - } - } - - /* We are not trapping to EL1; trap to EL2 if HCR_EL2 requires it - * No need for ARM_FEATURE check as if HCR_EL2 doesn't exist the - * bits will be zero indicating no trap. - */ - if (cur_el < 2) { - mask = is_wfe ? HCR_TWE : HCR_TWI; - if (arm_hcr_el2_eff(env) & mask) { - return 2; - } - } - - /* We are not trapping to EL1 or EL2; trap to EL3 if SCR_EL3 requires it */ - if (cur_el < 3) { - mask = (is_wfe) ? SCR_TWE : SCR_TWI; - if (env->cp15.scr_el3 & mask) { - return 3; - } - } - - return 0; -} -#endif - -void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) -{ -#ifdef CONFIG_USER_ONLY - /* - * WFI in the user-mode emulator is technically permitted but not - * something any real-world code would do. AArch64 Linux kernels - * trap it via SCTRL_EL1.nTWI and make it an (expensive) NOP; - * AArch32 kernels don't trap it so it will delay a bit. - * For QEMU, make it NOP here, because trying to raise EXCP_HLT - * would trigger an abort. - */ - return; -#else - CPUState *cs = env_cpu(env); - int target_el = check_wfx_trap(env, false); - - if (cpu_has_work(cs)) { - /* Don't bother to go into our "low power state" if - * we would just wake up immediately. - */ - return; - } - - if (target_el) { - if (env->aarch64) { - env->pc -= insn_len; - } else { - env->regs[15] -= insn_len; - } - - raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2), - target_el); - } - - cs->exception_index = EXCP_HLT; - cs->halted = 1; - cpu_loop_exit(cs); -#endif -} - -void HELPER(wfe)(CPUARMState *env) -{ - /* This is a hint instruction that is semantically different - * from YIELD even though we currently implement it identically. - * Don't actually halt the CPU, just yield back to top - * level loop. This is not going into a "low power state" - * (ie halting until some event occurs), so we never take - * a configurable trap to a different exception level. - */ - HELPER(yield)(env); -} - -void HELPER(yield)(CPUARMState *env) -{ - CPUState *cs = env_cpu(env); - - /* This is a non-trappable hint instruction that generally indicates - * that the guest is currently busy-looping. Yield control back to the - * top level loop so that a more deserving VCPU has a chance to run. - */ - cs->exception_index = EXCP_YIELD; - cpu_loop_exit(cs); -} - -/* Raise an internal-to-QEMU exception. This is limited to only - * 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*). - */ -void HELPER(exception_internal)(CPUARMState *env, uint32_t excp) -{ - CPUState *cs = env_cpu(env); - - assert(excp_is_internal(excp)); - cs->exception_index = excp; - cpu_loop_exit(cs); -} - -/* Raise an exception with the specified syndrome register value */ -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 exception with the specified syndrome register value - * to the default target el. - */ -void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp, - uint32_t syndrome) -{ - raise_exception(env, excp, syndrome, exception_target_el(env)); -} - -uint32_t HELPER(cpsr_read)(CPUARMState *env) -{ - return cpsr_read(env) & ~CPSR_EXEC; -} - -void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) -{ - cpsr_write(env, val, mask, CPSRWriteByInstr); - /* TODO: Not all cpsr bits are relevant to hflags. */ - arm_rebuild_hflags(env); -} - -/* Write the CPSR for a 32-bit exception return */ -void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) -{ - uint32_t mask; - - qemu_mutex_lock_iothread(); - arm_call_pre_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); - - mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); - cpsr_write(env, val, mask, CPSRWriteExceptionReturn); - - /* Generated code has already stored the new PC value, but - * without masking out its low bits, because which bits need - * masking depends on whether we're returning to Thumb or ARM - * state. Do the masking now. - */ - env->regs[15] &= (env->thumb ? ~1 : ~3); - arm_rebuild_hflags(env); - - qemu_mutex_lock_iothread(); - arm_call_el_change_hook(env_archcpu(env)); - qemu_mutex_unlock_iothread(); -} - -/* Access to user mode registers from privileged modes. */ -uint32_t HELPER(get_user_reg)(CPUARMState *env, uint32_t regno) -{ - uint32_t val; - - if (regno == 13) { - val = env->banked_r13[BANK_USRSYS]; - } else if (regno == 14) { - val = env->banked_r14[BANK_USRSYS]; - } else if (regno >= 8 - && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { - val = env->usr_regs[regno - 8]; - } else { - val = env->regs[regno]; - } - return val; -} - -void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) -{ - if (regno == 13) { - env->banked_r13[BANK_USRSYS] = val; - } else if (regno == 14) { - env->banked_r14[BANK_USRSYS] = val; - } else if (regno >= 8 - && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { - env->usr_regs[regno - 8] = val; - } else { - env->regs[regno] = val; - } -} - -void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) -{ - if ((env->uncached_cpsr & CPSR_M) == mode) { - env->regs[13] = val; - } else { - env->banked_r13[bank_number(mode)] = val; - } -} - -uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) -{ - if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SYS) { - /* SRS instruction is UNPREDICTABLE from System mode; we UNDEF. - * Other UNPREDICTABLE and UNDEF cases were caught at translate time. - */ - raise_exception(env, EXCP_UDEF, syn_uncategorized(), - exception_target_el(env)); - } - - if ((env->uncached_cpsr & CPSR_M) == mode) { - return env->regs[13]; - } else { - return env->banked_r13[bank_number(mode)]; - } -} - -static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, - uint32_t regno) -{ - /* Raise an exception if the requested access is one of the UNPREDICTABLE - * cases; otherwise return. This broadly corresponds to the pseudocode - * BankedRegisterAccessValid() and SPSRAccessValid(), - * except that we have already handled some cases at translate time. - */ - 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; - } - return; - } - - if (curmode == tgtmode) { - goto undef; - } - - if (tgtmode == ARM_CPU_MODE_USR) { - switch (regno) { - case 8 ... 12: - if (curmode != ARM_CPU_MODE_FIQ) { - goto undef; - } - break; - case 13: - if (curmode == ARM_CPU_MODE_SYS) { - goto undef; - } - break; - case 14: - if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) { - goto undef; - } - break; - default: - break; - } - } - - 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: - raise_exception(env, EXCP_UDEF, syn_uncategorized(), - exception_target_el(env)); -} - -void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, - uint32_t regno) -{ - msr_mrs_banked_exc_checks(env, tgtmode, regno); - - switch (regno) { - case 16: /* SPSRs */ - env->banked_spsr[bank_number(tgtmode)] = value; - break; - case 17: /* ELR_Hyp */ - env->elr_el[2] = value; - break; - case 13: - env->banked_r13[bank_number(tgtmode)] = value; - break; - case 14: - env->banked_r14[r14_bank_number(tgtmode)] = value; - break; - case 8 ... 12: - switch (tgtmode) { - case ARM_CPU_MODE_USR: - env->usr_regs[regno - 8] = value; - break; - case ARM_CPU_MODE_FIQ: - env->fiq_regs[regno - 8] = value; - break; - default: - g_assert_not_reached(); - } - break; - default: - g_assert_not_reached(); - } -} - -uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) -{ - msr_mrs_banked_exc_checks(env, tgtmode, regno); - - switch (regno) { - case 16: /* SPSRs */ - return env->banked_spsr[bank_number(tgtmode)]; - case 17: /* ELR_Hyp */ - return env->elr_el[2]; - case 13: - return env->banked_r13[bank_number(tgtmode)]; - case 14: - return env->banked_r14[r14_bank_number(tgtmode)]; - case 8 ... 12: - switch (tgtmode) { - case ARM_CPU_MODE_USR: - return env->usr_regs[regno - 8]; - case ARM_CPU_MODE_FIQ: - return env->fiq_regs[regno - 8]; - default: - g_assert_not_reached(); - } - default: - g_assert_not_reached(); - } -} - -void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, - uint32_t isread) -{ - ARMCPU *cpu = env_archcpu(env); - const ARMCPRegInfo *ri = rip; - CPAccessResult res = CP_ACCESS_OK; - int target_el; - - 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; - } - - /* - * Check for an EL2 trap due to HSTR_EL2. We expect EL0 accesses - * to sysregs non accessible at EL0 to have UNDEF-ed already. - */ - if (!is_a64(env) && arm_current_el(env) < 2 && ri->cp == 15 && - (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { - uint32_t mask = 1 << ri->crn; - - if (ri->type & ARM_CP_64BIT) { - mask = 1 << ri->crm; - } - - /* T4 and T14 are RES0 */ - mask &= ~((1 << 4) | (1 << 14)); - - if (env->cp15.hstr_el2 & mask) { - res = CP_ACCESS_TRAP_EL2; - goto fail; - } - } - - if (ri->accessfn) { - res = ri->accessfn(env, ri, isread); - } - if (likely(res == CP_ACCESS_OK)) { - return; - } - - fail: - switch (res & ~CP_ACCESS_EL_MASK) { - case CP_ACCESS_TRAP: - break; - case CP_ACCESS_TRAP_UNCATEGORIZED: - if (cpu_isar_feature(aa64_ids, cpu) && isread && - arm_cpreg_in_idspace(ri)) { - /* - * FEAT_IDST says this should be reported as EC_SYSTEMREGISTERTRAP, - * not EC_UNCATEGORIZED - */ - break; - } - syndrome = syn_uncategorized(); - break; - default: - g_assert_not_reached(); - } - - target_el = res & CP_ACCESS_EL_MASK; - switch (target_el) { - case 0: - target_el = exception_target_el(env); - break; - case 2: - assert(arm_current_el(env) != 3); - assert(arm_is_el2_enabled(env)); - break; - case 3: - assert(arm_feature(env, ARM_FEATURE_EL3)); - break; - default: - /* No "direct" traps to EL1 */ - g_assert_not_reached(); - } - - raise_exception(env, EXCP_UDEF, syndrome, target_el); -} - -void HELPER(set_cp_reg)(CPUARMState *env, void *rip, uint32_t value) -{ - const ARMCPRegInfo *ri = rip; - - if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); - ri->writefn(env, ri, value); - qemu_mutex_unlock_iothread(); - } else { - ri->writefn(env, ri, value); - } -} - -uint32_t HELPER(get_cp_reg)(CPUARMState *env, void *rip) -{ - const ARMCPRegInfo *ri = rip; - uint32_t res; - - if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); - res = ri->readfn(env, ri); - qemu_mutex_unlock_iothread(); - } else { - res = ri->readfn(env, ri); - } - - return res; -} - -void HELPER(set_cp_reg64)(CPUARMState *env, void *rip, uint64_t value) -{ - const ARMCPRegInfo *ri = rip; - - if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); - ri->writefn(env, ri, value); - qemu_mutex_unlock_iothread(); - } else { - ri->writefn(env, ri, value); - } -} - -uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) -{ - const ARMCPRegInfo *ri = rip; - uint64_t res; - - if (ri->type & ARM_CP_IO) { - qemu_mutex_lock_iothread(); - res = ri->readfn(env, ri); - qemu_mutex_unlock_iothread(); - } else { - res = ri->readfn(env, ri); - } - - return res; -} - -void HELPER(pre_hvc)(CPUARMState *env) -{ - ARMCPU *cpu = env_archcpu(env); - int cur_el = arm_current_el(env); - /* FIXME: Use actual secure state. */ - bool secure = false; - bool undef; - - if (arm_is_psci_call(cpu, EXCP_HVC)) { - /* If PSCI is enabled and this looks like a valid PSCI call then - * that overrides the architecturally mandated HVC behaviour. - */ - return; - } - - if (!arm_feature(env, ARM_FEATURE_EL2)) { - /* If EL2 doesn't exist, HVC always UNDEFs */ - undef = true; - } else if (arm_feature(env, ARM_FEATURE_EL3)) { - /* EL3.HCE has priority over EL2.HCD. */ - undef = !(env->cp15.scr_el3 & SCR_HCE); - } else { - undef = env->cp15.hcr_el2 & HCR_HCD; - } - - /* In ARMv7 and ARMv8/AArch32, HVC is undef in secure state. - * For ARMv8/AArch64, HVC is allowed in EL3. - * Note that we've already trapped HVC from EL0 at translation - * time. - */ - if (secure && (!is_a64(env) || cur_el == 1)) { - undef = true; - } - - if (undef) { - raise_exception(env, EXCP_UDEF, syn_uncategorized(), - exception_target_el(env)); - } -} - -void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) -{ - ARMCPU *cpu = env_archcpu(env); - int cur_el = arm_current_el(env); - bool secure = arm_is_secure(env); - bool smd_flag = env->cp15.scr_el3 & SCR_SMD; - - /* - * SMC behaviour is summarized in the following table. - * This helper handles the "Trap to EL2" and "Undef insn" cases. - * The "Trap to EL3" and "PSCI call" cases are handled in the exception - * helper. - * - * -> ARM_FEATURE_EL3 and !SMD - * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 - * - * Conduit SMC, valid call Trap to EL2 PSCI Call - * Conduit SMC, inval call Trap to EL2 Trap to EL3 - * Conduit not SMC Trap to EL2 Trap to EL3 - * - * - * -> ARM_FEATURE_EL3 and SMD - * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 - * - * Conduit SMC, valid call Trap to EL2 PSCI Call - * Conduit SMC, inval call Trap to EL2 Undef insn - * Conduit not SMC Trap to EL2 Undef insn - * - * - * -> !ARM_FEATURE_EL3 - * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 - * - * 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 - */ - - /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. - * On ARMv8 with EL3 AArch32, or ARMv7 with the Virtualization - * extensions, SMD only applies to NS state. - * On ARMv7 without the Virtualization extensions, the SMD bit - * doesn't exist, but we forbid the guest to set it to 1 in scr_write(), - * so we need not special case this here. - */ - bool smd = arm_feature(env, ARM_FEATURE_AARCH64) ? smd_flag - : smd_flag && !secure; - - if (!arm_feature(env, ARM_FEATURE_EL3) && - 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 - * 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 - * PSCI-via-SMC as implying an EL3. - * This handles the very last line of the previous table. - */ - raise_exception(env, EXCP_UDEF, syn_uncategorized(), - exception_target_el(env)); - } - - if (cur_el == 1 && (arm_hcr_el2_eff(env) & HCR_TSC)) { - /* In NS EL1, HCR controlled routing to EL2 has priority over SMD. - * We also want an EL2 guest to be able to forbid its EL1 from - * making PSCI calls into QEMU's "firmware" via HCR.TSC. - * This handles all the "Trap to EL2" cases of the previous table. - */ - raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); - } - - /* Catch the two remaining "Undef insn" cases of the previous table: - * - PSCI conduit is SMC but we don't have a valid PCSI call, - * - We don't have EL3 or SMD is set. - */ - if (!arm_is_psci_call(cpu, EXCP_SMC) && - (smd || !arm_feature(env, ARM_FEATURE_EL3))) { - raise_exception(env, EXCP_UDEF, syn_uncategorized(), - exception_target_el(env)); - } -} - -/* ??? Flag setting arithmetic is awkward because we need to do comparisons. - The only way to do that in TCG is a conditional branch, which clobbers - all our temporaries. For now implement these as helper functions. */ - -/* Similarly for variable shift instructions. */ - -uint32_t HELPER(shl_cc)(CPUARMState *env, uint32_t x, uint32_t i) -{ - int shift = i & 0xff; - if (shift >= 32) { - if (shift == 32) - env->CF = x & 1; - else - env->CF = 0; - return 0; - } else if (shift != 0) { - env->CF = (x >> (32 - shift)) & 1; - return x << shift; - } - return x; -} - -uint32_t HELPER(shr_cc)(CPUARMState *env, uint32_t x, uint32_t i) -{ - int shift = i & 0xff; - if (shift >= 32) { - if (shift == 32) - env->CF = (x >> 31) & 1; - else - env->CF = 0; - return 0; - } else if (shift != 0) { - env->CF = (x >> (shift - 1)) & 1; - return x >> shift; - } - return x; -} - -uint32_t HELPER(sar_cc)(CPUARMState *env, uint32_t x, uint32_t i) -{ - int shift = i & 0xff; - if (shift >= 32) { - env->CF = (x >> 31) & 1; - return (int32_t)x >> 31; - } else if (shift != 0) { - env->CF = (x >> (shift - 1)) & 1; - return (int32_t)x >> shift; - } - return x; -} - -uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i) -{ - int shift1, shift; - shift1 = i & 0xff; - shift = shift1 & 0x1f; - if (shift == 0) { - if (shift1 != 0) - env->CF = (x >> 31) & 1; - return x; - } else { - env->CF = (x >> (shift - 1)) & 1; - return ((uint32_t)x >> shift) | (x << (32 - shift)); - } -} - -void HELPER(probe_access)(CPUARMState *env, target_ulong ptr, - uint32_t access_type, uint32_t mmu_idx, - uint32_t size) -{ - uint32_t in_page = -((uint32_t)ptr | TARGET_PAGE_SIZE); - uintptr_t ra = GETPC(); - - if (likely(size <= in_page)) { - probe_access(env, ptr, size, access_type, mmu_idx, ra); - } else { - probe_access(env, ptr, in_page, access_type, mmu_idx, ra); - probe_access(env, ptr + in_page, size - in_page, - access_type, mmu_idx, ra); - } -} - -/* - * This function corresponds to AArch64.vESBOperation(). - * Note that the AArch32 version is not functionally different. - */ -void HELPER(vesb)(CPUARMState *env) -{ - /* - * The EL2Enabled() check is done inside arm_hcr_el2_eff, - * and will return HCR_EL2.VSE == 0, so nothing happens. - */ - uint64_t hcr = arm_hcr_el2_eff(env); - bool enabled = !(hcr & HCR_TGE) && (hcr & HCR_AMO); - bool pending = enabled && (hcr & HCR_VSE); - bool masked = (env->daif & PSTATE_A); - - /* If VSE pending and masked, defer the exception. */ - if (pending && masked) { - uint32_t syndrome; - - if (arm_el_is_aa64(env, 1)) { - /* Copy across IDS and ISS from VSESR. */ - syndrome = env->cp15.vsesr_el2 & 0x1ffffff; - } else { - ARMMMUFaultInfo fi = { .type = ARMFault_AsyncExternal }; - - if (extended_addresses_enabled(env)) { - syndrome = arm_fi_to_lfsc(&fi); - } else { - syndrome = arm_fi_to_sfsc(&fi); - } - /* Copy across AET and ExT from VSESR. */ - syndrome |= env->cp15.vsesr_el2 & 0xd000; - } - - /* Set VDISR_EL2.A along with the syndrome. */ - env->cp15.vdisr_el2 = syndrome | (1u << 31); - - /* Clear pending virtual SError */ - env->cp15.hcr_el2 &= ~HCR_VSE; - cpu_reset_interrupt(env_cpu(env), CPU_INTERRUPT_VSERR); - } -} diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c deleted file mode 100644 index d0483bf051..0000000000 --- a/target/arm/pauth_helper.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - * ARM v8.3-PAuth Operations - * - * Copyright (c) 2019 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 "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/helper-proto.h" -#include "tcg/tcg-gvec-desc.h" -#include "qemu/xxhash.h" - - -static uint64_t pac_cell_shuffle(uint64_t i) -{ - uint64_t o = 0; - - o |= extract64(i, 52, 4); - o |= extract64(i, 24, 4) << 4; - o |= extract64(i, 44, 4) << 8; - o |= extract64(i, 0, 4) << 12; - - o |= extract64(i, 28, 4) << 16; - o |= extract64(i, 48, 4) << 20; - o |= extract64(i, 4, 4) << 24; - o |= extract64(i, 40, 4) << 28; - - o |= extract64(i, 32, 4) << 32; - o |= extract64(i, 12, 4) << 36; - o |= extract64(i, 56, 4) << 40; - o |= extract64(i, 20, 4) << 44; - - o |= extract64(i, 8, 4) << 48; - o |= extract64(i, 36, 4) << 52; - o |= extract64(i, 16, 4) << 56; - o |= extract64(i, 60, 4) << 60; - - return o; -} - -static uint64_t pac_cell_inv_shuffle(uint64_t i) -{ - uint64_t o = 0; - - o |= extract64(i, 12, 4); - o |= extract64(i, 24, 4) << 4; - o |= extract64(i, 48, 4) << 8; - o |= extract64(i, 36, 4) << 12; - - o |= extract64(i, 56, 4) << 16; - o |= extract64(i, 44, 4) << 20; - o |= extract64(i, 4, 4) << 24; - o |= extract64(i, 16, 4) << 28; - - o |= i & MAKE_64BIT_MASK(32, 4); - o |= extract64(i, 52, 4) << 36; - o |= extract64(i, 28, 4) << 40; - o |= extract64(i, 8, 4) << 44; - - o |= extract64(i, 20, 4) << 48; - o |= extract64(i, 0, 4) << 52; - o |= extract64(i, 40, 4) << 56; - o |= i & MAKE_64BIT_MASK(60, 4); - - return o; -} - -static uint64_t pac_sub(uint64_t i) -{ - static const uint8_t sub[16] = { - 0xb, 0x6, 0x8, 0xf, 0xc, 0x0, 0x9, 0xe, - 0x3, 0x7, 0x4, 0x5, 0xd, 0x2, 0x1, 0xa, - }; - uint64_t o = 0; - int b; - - for (b = 0; b < 64; b += 4) { - o |= (uint64_t)sub[(i >> b) & 0xf] << b; - } - return o; -} - -static uint64_t pac_inv_sub(uint64_t i) -{ - static const uint8_t inv_sub[16] = { - 0x5, 0xe, 0xd, 0x8, 0xa, 0xb, 0x1, 0x9, - 0x2, 0x6, 0xf, 0x0, 0x4, 0xc, 0x7, 0x3, - }; - uint64_t o = 0; - int b; - - for (b = 0; b < 64; b += 4) { - o |= (uint64_t)inv_sub[(i >> b) & 0xf] << b; - } - return o; -} - -static int rot_cell(int cell, int n) -{ - /* 4-bit rotate left by n. */ - cell |= cell << 4; - return extract32(cell, 4 - n, 4); -} - -static uint64_t pac_mult(uint64_t i) -{ - uint64_t o = 0; - int b; - - for (b = 0; b < 4 * 4; b += 4) { - int i0, i4, i8, ic, t0, t1, t2, t3; - - i0 = extract64(i, b, 4); - i4 = extract64(i, b + 4 * 4, 4); - i8 = extract64(i, b + 8 * 4, 4); - ic = extract64(i, b + 12 * 4, 4); - - t0 = rot_cell(i8, 1) ^ rot_cell(i4, 2) ^ rot_cell(i0, 1); - t1 = rot_cell(ic, 1) ^ rot_cell(i4, 1) ^ rot_cell(i0, 2); - t2 = rot_cell(ic, 2) ^ rot_cell(i8, 1) ^ rot_cell(i0, 1); - t3 = rot_cell(ic, 1) ^ rot_cell(i8, 2) ^ rot_cell(i4, 1); - - o |= (uint64_t)t3 << b; - o |= (uint64_t)t2 << (b + 4 * 4); - o |= (uint64_t)t1 << (b + 8 * 4); - o |= (uint64_t)t0 << (b + 12 * 4); - } - return o; -} - -static uint64_t tweak_cell_rot(uint64_t cell) -{ - return (cell >> 1) | (((cell ^ (cell >> 1)) & 1) << 3); -} - -static uint64_t tweak_shuffle(uint64_t i) -{ - uint64_t o = 0; - - o |= extract64(i, 16, 4) << 0; - o |= extract64(i, 20, 4) << 4; - o |= tweak_cell_rot(extract64(i, 24, 4)) << 8; - o |= extract64(i, 28, 4) << 12; - - o |= tweak_cell_rot(extract64(i, 44, 4)) << 16; - o |= extract64(i, 8, 4) << 20; - o |= extract64(i, 12, 4) << 24; - o |= tweak_cell_rot(extract64(i, 32, 4)) << 28; - - o |= extract64(i, 48, 4) << 32; - o |= extract64(i, 52, 4) << 36; - o |= extract64(i, 56, 4) << 40; - o |= tweak_cell_rot(extract64(i, 60, 4)) << 44; - - o |= tweak_cell_rot(extract64(i, 0, 4)) << 48; - o |= extract64(i, 4, 4) << 52; - o |= tweak_cell_rot(extract64(i, 40, 4)) << 56; - o |= tweak_cell_rot(extract64(i, 36, 4)) << 60; - - return o; -} - -static uint64_t tweak_cell_inv_rot(uint64_t cell) -{ - return ((cell << 1) & 0xf) | ((cell & 1) ^ (cell >> 3)); -} - -static uint64_t tweak_inv_shuffle(uint64_t i) -{ - uint64_t o = 0; - - o |= tweak_cell_inv_rot(extract64(i, 48, 4)); - o |= extract64(i, 52, 4) << 4; - o |= extract64(i, 20, 4) << 8; - o |= extract64(i, 24, 4) << 12; - - o |= extract64(i, 0, 4) << 16; - o |= extract64(i, 4, 4) << 20; - o |= tweak_cell_inv_rot(extract64(i, 8, 4)) << 24; - o |= extract64(i, 12, 4) << 28; - - o |= tweak_cell_inv_rot(extract64(i, 28, 4)) << 32; - o |= tweak_cell_inv_rot(extract64(i, 60, 4)) << 36; - o |= tweak_cell_inv_rot(extract64(i, 56, 4)) << 40; - o |= tweak_cell_inv_rot(extract64(i, 16, 4)) << 44; - - o |= extract64(i, 32, 4) << 48; - o |= extract64(i, 36, 4) << 52; - o |= extract64(i, 40, 4) << 56; - o |= tweak_cell_inv_rot(extract64(i, 44, 4)) << 60; - - return o; -} - -static uint64_t pauth_computepac_architected(uint64_t data, uint64_t modifier, - ARMPACKey key) -{ - static const uint64_t RC[5] = { - 0x0000000000000000ull, - 0x13198A2E03707344ull, - 0xA4093822299F31D0ull, - 0x082EFA98EC4E6C89ull, - 0x452821E638D01377ull, - }; - const uint64_t alpha = 0xC0AC29B7C97C50DDull; - /* - * Note that in the ARM pseudocode, key0 contains bits <127:64> - * and key1 contains bits <63:0> of the 128-bit key. - */ - uint64_t key0 = key.hi, key1 = key.lo; - uint64_t workingval, runningmod, roundkey, modk0; - int i; - - modk0 = (key0 << 63) | ((key0 >> 1) ^ (key0 >> 63)); - runningmod = modifier; - workingval = data ^ key0; - - for (i = 0; i <= 4; ++i) { - roundkey = key1 ^ runningmod; - workingval ^= roundkey; - workingval ^= RC[i]; - if (i > 0) { - workingval = pac_cell_shuffle(workingval); - workingval = pac_mult(workingval); - } - 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); - workingval = pac_cell_shuffle(workingval); - workingval = pac_mult(workingval); - workingval ^= key1; - workingval = pac_cell_inv_shuffle(workingval); - 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) { - workingval = pac_mult(workingval); - workingval = pac_cell_inv_shuffle(workingval); - } - runningmod = tweak_inv_shuffle(runningmod); - roundkey = key1 ^ runningmod; - workingval ^= RC[4 - i]; - workingval ^= roundkey; - workingval ^= alpha; - } - workingval ^= modk0; - - return workingval; -} - -static uint64_t pauth_computepac_impdef(uint64_t data, uint64_t modifier, - ARMPACKey key) -{ - return qemu_xxhash64_4(data, modifier, key.lo, key.hi); -} - -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); - } else { - return pauth_computepac_impdef(data, modifier, key); - } -} - -static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier, - ARMPACKey *key, bool data) -{ - ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); - ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); - uint64_t pac, ext_ptr, ext, test; - int bot_bit, top_bit; - - /* If tagged pointers are in use, use ptr<55>, otherwise ptr<63>. */ - if (param.tbi) { - ext = sextract64(ptr, 55, 1); - } else { - ext = sextract64(ptr, 63, 1); - } - - /* Build a pointer with known good extension bits. */ - top_bit = 64 - 8 * param.tbi; - bot_bit = 64 - param.tsz; - ext_ptr = deposit64(ptr, bot_bit, top_bit - bot_bit, ext); - - pac = pauth_computepac(env, ext_ptr, modifier, *key); - - /* - * Check if the ptr has good extension bits and corrupt the - * pointer authentication code if not. - */ - 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); - } - - /* - * Preserve the determination between upper and lower at bit 55, - * and insert pointer authentication code. - */ - if (param.tbi) { - ptr &= ~MAKE_64BIT_MASK(bot_bit, 55 - bot_bit + 1); - pac &= MAKE_64BIT_MASK(bot_bit, 54 - bot_bit + 1); - } else { - ptr &= MAKE_64BIT_MASK(0, bot_bit); - pac &= ~(MAKE_64BIT_MASK(55, 1) | MAKE_64BIT_MASK(0, bot_bit)); - } - ext &= MAKE_64BIT_MASK(55, 1); - return pac | ext | ptr; -} - -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; - - return deposit64(ptr, bot_pac_bit, top_pac_bit - bot_pac_bit, extfield); -} - -static uint64_t pauth_auth(CPUARMState *env, uint64_t ptr, uint64_t modifier, - ARMPACKey *key, bool data, int keynumber) -{ - ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); - ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); - int bot_bit, top_bit; - uint64_t pac, orig_ptr, test; - - 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))) { - int error_code = (keynumber << 1) | (keynumber ^ 1); - if (param.tbi) { - return deposit64(orig_ptr, 53, 2, error_code); - } else { - return deposit64(orig_ptr, 61, 2, error_code); - } - } - return orig_ptr; -} - -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); - - return pauth_original_ptr(ptr, param); -} - -static G_NORETURN -void pauth_trap(CPUARMState *env, int target_el, uintptr_t ra) -{ - raise_exception_ra(env, EXCP_UDEF, syn_pactrap(), target_el, ra); -} - -static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra) -{ - if (el < 2 && arm_is_el2_enabled(env)) { - uint64_t hcr = arm_hcr_el2_eff(env); - bool trap = !(hcr & HCR_API); - if (el == 0) { - /* Trap only applies to EL1&0 regime. */ - trap &= (hcr & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE); - } - /* FIXME: ARMv8.3-NV: HCR_NV trap takes precedence for ERETA[AB]. */ - if (trap) { - pauth_trap(env, 2, ra); - } - } - if (el < 3 && arm_feature(env, ARM_FEATURE_EL3)) { - if (!(env->cp15.scr_el3 & SCR_API)) { - pauth_trap(env, 3, ra); - } - } -} - -static bool pauth_key_enabled(CPUARMState *env, int el, uint32_t bit) -{ - return (arm_sctlr(env, el) & bit) != 0; -} - -uint64_t HELPER(pacia)(CPUARMState *env, uint64_t x, uint64_t y) -{ - int el = arm_current_el(env); - if (!pauth_key_enabled(env, el, SCTLR_EnIA)) { - return x; - } - pauth_check_trap(env, el, GETPC()); - return pauth_addpac(env, x, y, &env->keys.apia, false); -} - -uint64_t HELPER(pacib)(CPUARMState *env, uint64_t x, uint64_t y) -{ - int el = arm_current_el(env); - if (!pauth_key_enabled(env, el, SCTLR_EnIB)) { - return x; - } - pauth_check_trap(env, el, GETPC()); - return pauth_addpac(env, x, y, &env->keys.apib, false); -} - -uint64_t HELPER(pacda)(CPUARMState *env, uint64_t x, uint64_t y) -{ - int el = arm_current_el(env); - if (!pauth_key_enabled(env, el, SCTLR_EnDA)) { - return x; - } - pauth_check_trap(env, el, GETPC()); - return pauth_addpac(env, x, y, &env->keys.apda, true); -} - -uint64_t HELPER(pacdb)(CPUARMState *env, uint64_t x, uint64_t y) -{ - int el = arm_current_el(env); - if (!pauth_key_enabled(env, el, SCTLR_EnDB)) { - return x; - } - pauth_check_trap(env, el, GETPC()); - return pauth_addpac(env, x, y, &env->keys.apdb, true); -} - -uint64_t HELPER(pacga)(CPUARMState *env, uint64_t x, uint64_t y) -{ - uint64_t pac; - - pauth_check_trap(env, arm_current_el(env), GETPC()); - pac = pauth_computepac(env, x, y, env->keys.apga); - - return pac & 0xffffffff00000000ull; -} - -uint64_t HELPER(autia)(CPUARMState *env, uint64_t x, uint64_t y) -{ - 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); -} - -uint64_t HELPER(autib)(CPUARMState *env, uint64_t x, uint64_t y) -{ - 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); -} - -uint64_t HELPER(autda)(CPUARMState *env, uint64_t x, uint64_t y) -{ - 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); -} - -uint64_t HELPER(autdb)(CPUARMState *env, uint64_t x, uint64_t y) -{ - 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); -} - -uint64_t HELPER(xpaci)(CPUARMState *env, uint64_t a) -{ - return pauth_strip(env, a, false); -} - -uint64_t HELPER(xpacd)(CPUARMState *env, uint64_t a) -{ - return pauth_strip(env, a, true); -} diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 0b16068557..64bb6878a4 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -11,36 +11,83 @@ #include "qemu/range.h" #include "qemu/main-loop.h" #include "exec/exec-all.h" +#include "exec/page-protection.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; - bool in_secure; + /* + * 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; - bool out_secure; + /* + * 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, S1Translate *ptw, - uint64_t address, - MMUAccessType access_type, bool s1_is_el0, - GetPhysAddrResult *result, ARMMMUFaultInfo *fi) - __attribute__((nonnull)); +static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, + vaddr address, + MMUAccessType access_type, MemOp memop, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi); -static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, - GetPhysAddrResult *result, - ARMMMUFaultInfo *fi) - __attribute__((nonnull)); +static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, + vaddr address, + MMUAccessType access_type, MemOp memop, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi); + +static int get_S1prot(CPUARMState *env, ARMMMUIdx mmu_idx, bool is_aa64, + int user_rw, int prot_rw, int xn, int pxn, + ARMSecuritySpace in_pa, ARMSecuritySpace out_pa); /* This mapping is common between ID_AA64MMFR0.PARANGE and TCR_ELx.{I}PS. */ static const uint8_t pamax_map[] = { @@ -53,7 +100,25 @@ static const uint8_t pamax_map[] = { [6] = 52, }; -/* The cpu-specific constant value of PAMax; also used by hw/arm/virt. */ +uint8_t round_down_to_parange_index(uint8_t bit_size) +{ + for (int i = ARRAY_SIZE(pamax_map) - 1; i >= 0; i--) { + if (pamax_map[i] <= bit_size) { + return i; + } + } + g_assert_not_reached(); +} + +uint8_t round_down_to_parange_bit_size(uint8_t bit_size) +{ + return pamax_map[round_down_to_parange_index(bit_size)]; +} + +/* + * 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) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { @@ -68,13 +133,8 @@ unsigned int arm_pamax(ARMCPU *cpu) return pamax_map[parange]; } - /* - * In machvirt_init, we call arm_pamax on a cpu that is not fully - * initialized, so we can't rely on the propagation done in realize. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_LPAE) || - arm_feature(&cpu->env, ARM_FEATURE_V7VE)) { - /* v7 with LPAE */ + if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) { + /* v7 or v8 with LPAE */ return 40; } /* Anything else */ @@ -103,6 +163,47 @@ 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; @@ -126,11 +227,12 @@ 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, - bool is_secure) + ARMSecuritySpace space) { uint64_t hcr_el2; if (arm_feature(env, ARM_FEATURE_M)) { + 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: @@ -149,18 +251,19 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, } } - hcr_el2 = arm_hcr_el2_eff_secstate(env, is_secure); 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; 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; } @@ -170,6 +273,7 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, 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; } @@ -180,10 +284,14 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, case ARMMMUIdx_E20_2_PAN: case ARMMMUIdx_E2: case ARMMMUIdx_E3: + case ARMMMUIdx_E30_0: + case ARMMMUIdx_E30_3_PAN: break; - case ARMMMUIdx_Phys_NS: case ARMMMUIdx_Phys_S: + case ARMMMUIdx_Phys_NS: + case ARMMMUIdx_Phys_Root: + case ARMMMUIdx_Phys_Realm: /* No translation for physical address spaces. */ return true; @@ -194,6 +302,207 @@ static bool regime_translation_disabled(CPUARMState *env, ARMMMUIdx mmu_idx, return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; } +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) { /* @@ -212,15 +521,53 @@ static bool S2_attrs_are_device(uint64_t hcr, uint8_t attrs) } } +static ARMSecuritySpace S2_security_space(ARMSecuritySpace s1_space, + ARMMMUIdx s2_mmu_idx) +{ + /* + * 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; + } + } 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) { - bool is_secure = ptw->in_secure; ARMMMUIdx mmu_idx = ptw->in_mmu_idx; ARMMMUIdx s2_mmu_idx = ptw->in_ptw_idx; uint8_t pte_attrs; - bool pte_secure; ptw->out_virt = addr; @@ -229,38 +576,33 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, * From gdbstub, do not use softmmu so that we don't modify the * state of the cpu at all, including softmmu tlb contents. */ - if (regime_is_stage2(s2_mmu_idx)) { - S1Translate s2ptw = { - .in_mmu_idx = s2_mmu_idx, - .in_ptw_idx = is_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS, - .in_secure = is_secure, - .in_debug = true, - }; - GetPhysAddrResult s2 = { }; + 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_lpae(env, &s2ptw, addr, MMU_DATA_LOAD, - false, &s2, fi)) { - goto fail; - } - ptw->out_phys = s2.f.phys_addr; - pte_attrs = s2.cacheattrs.attrs; - pte_secure = s2.f.attrs.secure; - } else { - /* Regime is physical. */ - ptw->out_phys = addr; - pte_attrs = 0; - pte_secure = is_secure; + if (get_phys_addr_gpc(env, &s2ptw, addr, MMU_DATA_LOAD, 0, &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(env, addr, MMU_DATA_LOAD, - arm_to_core_mmu_idx(s2_mmu_idx), - true, &ptw->out_host, &full, 0); + 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)) { @@ -268,12 +610,15 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, } ptw->out_phys = full->phys_addr | (addr & ~TARGET_PAGE_MASK); ptw->out_rw = full->prot & PAGE_WRITE; - pte_attrs = full->pte_attrs; - pte_secure = full->attrs.secure; + 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, is_secure); + uint64_t hcr = arm_hcr_el2_eff_secstate(env, ptw->in_space); if ((hcr & HCR_PTW) && S2_attrs_are_device(hcr, pte_attrs)) { /* @@ -284,25 +629,23 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, fi->s2addr = addr; fi->stage2 = true; fi->s1ptw = true; - fi->s1ns = !is_secure; + fi->s1ns = fault_s1ns(ptw->in_space, s2_mmu_idx); return false; } } - /* Check if page table walk is to secure or non-secure PA space. */ - ptw->out_secure = (is_secure - && !(pte_secure - ? env->cp15.vstcr_el2 & VSTCR_SW - : env->cp15.vtcr_el2 & VTCR_NSW)); 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 = true; - fi->s1ptw = true; - fi->s1ns = !is_secure; + fi->stage2 = regime_is_stage2(s2_mmu_idx); + fi->s1ptw = fi->stage2; + fi->s1ns = fault_s1ns(ptw->in_space, s2_mmu_idx); return false; } @@ -324,7 +667,10 @@ static uint32_t arm_ldl_ptw(CPUARMState *env, S1Translate *ptw, } } else { /* Page tables are in MMIO. */ - MemTxAttrs attrs = { .secure = ptw->out_secure }; + MemTxAttrs attrs = { + .space = ptw->out_space, + .secure = arm_space_is_secure(ptw->out_space), + }; AddressSpace *as = arm_addressspace(cs, attrs); MemTxResult result = MEMTX_OK; @@ -367,7 +713,10 @@ static uint64_t arm_ldq_ptw(CPUARMState *env, S1Translate *ptw, #endif } else { /* Page tables are in MMIO. */ - MemTxAttrs attrs = { .secure = ptw->out_secure }; + MemTxAttrs attrs = { + .space = ptw->out_space, + .secure = arm_space_is_secure(ptw->out_space), + }; AddressSpace *as = arm_addressspace(cs, attrs); MemTxResult result = MEMTX_OK; @@ -389,13 +738,73 @@ 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)) { - fi->type = ARMFault_UnsuppAtomicUpdate; - fi->s1ptw = true; - return 0; + /* 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; } /* @@ -404,20 +813,26 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, */ if (unlikely(!ptw->out_rw)) { int flags; - void *discard; env->tlb_fi = fi; - flags = probe_access_flags(env, ptw->out_virt, MMU_DATA_STORE, - arm_to_core_mmu_idx(ptw->in_ptw_idx), - true, &discard, 0); + 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 = !ptw->in_secure; + fi->s1ns = fault_s1ns(ptw->in_space, ptw->in_ptw_idx); return 0; } @@ -444,12 +859,12 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, * 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. */ -#ifndef TCG_OVERSIZED_GUEST +#if !TCG_OVERSIZED_GUEST # error "Unexpected configuration" #endif - bool locked = qemu_mutex_iothread_locked(); + bool locked = bql_locked(); if (!locked) { - qemu_mutex_lock_iothread(); + bql_lock(); } if (ptw->out_be) { cur_val = ldq_be_p(host); @@ -463,11 +878,15 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, } } if (!locked) { - qemu_mutex_unlock_iothread(); + 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, @@ -733,7 +1152,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, hwaddr phys_addr; uint32_t dacr; bool ns; - int user_prot; + ARMSecuritySpace out_space; /* Pagetable walk. */ /* Lookup l1 descriptor. */ @@ -825,16 +1244,19 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, g_assert_not_reached(); } } + out_space = ptw->in_space; + 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 output space will already be non-secure. + */ + out_space = ARMSS_NonSecure; + } if (domain_prot == 3) { result->f.prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; } else { - if (pxn && !regime_is_user(env, mmu_idx)) { - xn = 1; - } - if (xn && access_type == MMU_INST_FETCH) { - fi->type = ARMFault_Permission; - goto do_fault; - } + int user_rw, prot_rw; if (arm_feature(env, ARM_FEATURE_V6K) && (regime_sctlr(env, mmu_idx) & SCTLR_AFE)) { @@ -844,36 +1266,23 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, fi->type = ARMFault_AccessFlag; goto do_fault; } - 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); + prot_rw = simple_ap_to_rw_prot(env, mmu_idx, ap >> 1); + user_rw = simple_ap_to_rw_prot_is_user(ap >> 1, 1); } else { - 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 (result->f.prot && !xn) { - result->f.prot |= PAGE_EXEC; + prot_rw = ap_to_rw_prot(env, mmu_idx, ap, domain_prot); + user_rw = ap_to_rw_prot_is_user(env, mmu_idx, ap, domain_prot, 1); } + + result->f.prot = get_S1prot(env, mmu_idx, false, user_rw, prot_rw, + xn, pxn, result->f.attrs.space, out_space); 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. - */ - result->f.attrs.secure = false; } + result->f.attrs.space = out_space; + result->f.attrs.secure = arm_space_is_secure(out_space); result->f.phys_addr = phys_addr; return false; do_fault: @@ -889,7 +1298,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; @@ -899,6 +1308,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) { @@ -935,35 +1350,77 @@ static int get_S2prot(CPUARMState *env, int s2ap, int xn, bool s1_is_el0) * @env: CPUARMState * @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 + * @user_rw: Translated AP for user access + * @prot_rw: Translated AP for privileged access * @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 user_rw, int prot_rw, 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(!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 { - prot_rw = simple_ap_to_rw_prot_is_user(ap, false); + } 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; } } - 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 @@ -1077,70 +1534,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); } /** @@ -1156,22 +1676,17 @@ static bool check_s2_mmu_setup(ARMCPU *cpu, bool is_aa64, int level, * @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 - * @s1_is_el0: if @ptw->in_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. + * @memop: memory operation feeding this access, or 0 for none * @result: set on translation success, * @fi: set to fault info if the translation fails */ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, uint64_t address, - MMUAccessType access_type, bool s1_is_el0, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { ARMCPU *cpu = env_archcpu(env); ARMMMUIdx mmu_idx = ptw->in_mmu_idx; - bool is_secure = ptw->in_secure; int32_t level; ARMVAParameters param; uint64_t ttbr; @@ -1182,19 +1697,21 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, int32_t stride; int addrsize, inputsize, outputsize; uint64_t tcr = regime_tcr(env, mmu_idx); - int ap, ns, xn, pxn; + int ap, xn, pxn; uint32_t el = regime_el(env, mmu_idx); uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); uint64_t descriptor, new_descriptor; - bool nstable; + 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; /* @@ -1296,38 +1813,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, */ 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, 6, 2); - uint32_t sl2 = extract64(tcr, 33, 1); - int32_t startlevel; - bool ok; - - /* SL2 is RES0 unless DS=1 & 4kb granule. */ - if (param.ds && stride == 9 && sl2) { - if (sl0 != 0) { - level = 0; - goto do_translation_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) { + 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; @@ -1376,31 +1865,31 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, descaddrmask = MAKE_64BIT_MASK(0, 40); } descaddrmask &= ~indexmask_grainsize; - - /* - * Secure accesses start with the page table in secure memory and - * can be downgraded to non-secure at any step. Non-secure accesses - * remain non-secure. We implement this by just ORing in the NSTable/NS - * bits at each step. - */ - tableattrs = is_secure ? 0 : (1 << 4); + tableattrs = 0; next_level: descaddr |= (address >> (stride * (4 - level))) & indexmask; descaddr &= ~7ULL; - nstable = extract32(tableattrs, 4, 1); - if (nstable) { + + /* + * 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. + */ + if (ptw->in_space == ARMSS_Secure + && !regime_is_stage2(mmu_idx) + && extract32(tableattrs, 4, 1)) { /* * Stage2_S -> Stage2 or Phys_S -> Phys_NS - * Assert that the non-secure idx are even, and relative order. + * Assert the relative order of the secure/non-secure indexes. */ - QEMU_BUILD_BUG_ON((ARMMMUIdx_Phys_NS & 1) != 0); - QEMU_BUILD_BUG_ON((ARMMMUIdx_Stage2 & 1) != 0); - QEMU_BUILD_BUG_ON(ARMMMUIdx_Phys_NS + 1 != ARMMMUIdx_Phys_S); - QEMU_BUILD_BUG_ON(ARMMMUIdx_Stage2 + 1 != ARMMMUIdx_Stage2_S); - ptw->in_ptw_idx &= ~1; - ptw->in_secure = false; + 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; } + if (!S1_ptw_translate(env, ptw, descaddr, fi)) { goto do_fault; } @@ -1411,8 +1900,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, new_descriptor = descriptor; restart_atomic_update: - if (!(descriptor & 1) || (!(descriptor & 2) && (level == 3))) { - /* Invalid, or the Reserved level 3 encoding */ + 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; } @@ -1499,11 +1990,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * 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. + * 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)) { - attrs |= nstable << 5; /* NS */ if (!param.hpd) { attrs |= extract64(tableattrs, 0, 2) << 53; /* XN, PXN */ /* @@ -1516,15 +2006,153 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, } ap = extract32(attrs, 6, 2); + out_space = ptw->in_space; if (regime_is_stage2(mmu_idx)) { - ns = mmu_idx == ARMMMUIdx_Stage2; - xn = extract64(attrs, 53, 2); - result->f.prot = get_S2prot(env, ap, xn, s1_is_el0); + /* + * 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); + } + + result->cacheattrs.is_s2_format = true; + result->cacheattrs.attrs = extract32(attrs, 2, 4); + /* + * Security state does not really affect HCR_EL2.FWB; + * we only need to filter FWB for aa32 or other FEAT. + */ + device = S2_attrs_are_device(arm_hcr_el2_eff(env), + result->cacheattrs.attrs); } else { - ns = extract32(attrs, 5, 1); + int nse, ns = extract32(attrs, 5, 1); + uint8_t attrindx; + uint64_t mair; + int user_rw, prot_rw; + + 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); - result->f.prot = get_S1prot(env, mmu_idx, aarch64, ap, ns, xn, pxn); + + 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; + } + + user_rw = simple_ap_to_rw_prot_is_user(ap, true); + prot_rw = simple_ap_to_rw_prot_is_user(ap, false); + /* + * 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, user_rw, prot_rw, + xn, pxn, result->f.attrs.space, out_space); + + /* Index into MAIR registers for cache attributes */ + attrindx = extract32(attrs, 2, 3); + mair = env->cp15.mair_el[regime_el(env, mmu_idx)]; + assert(attrindx <= 7); + 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, the correct ordering for alignment, permission, + * and stage 2 faults is: + * - Alignment fault caused by the memory type + * - Permission fault + * - A stage 2 fault on the memory access + * Perform the alignment check now, so that we recognize it in + * the correct order. Set TLB_CHECK_ALIGNED so that any subsequent + * softmmu tlb hit will also check the alignment; clear along the + * non-device path so that tlb_fill_flags is consistent in the + * event of restart_atomic_update. + * + * 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) { + unsigned a_bits = memop_atomicity_bits(memop); + if (address & ((1 << a_bits) - 1)) { + fi->type = ARMFault_Alignment; + goto do_fault; + } + result->f.tlb_fill_flags = TLB_CHECK_ALIGNED; + } else { + result->f.tlb_fill_flags = 0; } if (!(result->f.prot & (1 << access_type))) { @@ -1551,31 +2179,8 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, } } - 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. - */ - result->f.attrs.secure = false; - } - - /* When in aarch64 mode, and BTI is enabled, remember GP in the TLB. */ - if (aarch64 && cpu_isar_feature(aa64_bti, cpu)) { - result->f.guarded = extract64(attrs, 50, 1); /* GP */ - } - - if (regime_is_stage2(mmu_idx)) { - result->cacheattrs.is_s2_format = true; - result->cacheattrs.attrs = extract32(attrs, 2, 4); - } else { - /* Index into MAIR registers for cache attributes */ - uint8_t attrindx = extract32(attrs, 2, 3); - uint64_t mair = env->cp15.mair_el[regime_el(env, mmu_idx)]; - assert(attrindx <= 7); - result->cacheattrs.is_s2_format = false; - result->cacheattrs.attrs = extract64(mair, attrindx * 8, 8); - } + result->f.attrs.space = out_space; + result->f.attrs.secure = arm_space_is_secure(out_space); /* * For FEAT_LPA2 and effective DS, the SH field in the attributes @@ -1595,24 +2200,31 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, do_translation_fault: fi->type = ARMFault_Translation; do_fault: - fi->level = level; - /* Tag the error as S2 for failed S1 PTW at S2 or ordinary S2. */ - fi->stage2 = fi->s1ptw || regime_is_stage2(mmu_idx); - fi->s1ns = mmu_idx == ARMMMUIdx_Stage2; + 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, - bool is_secure, GetPhysAddrResult *result, +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, is_secure)) { + if (regime_translation_disabled(env, mmu_idx, ptw->in_space)) { /* MPU disabled. */ result->f.phys_addr = address; result->f.prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; @@ -1758,25 +2370,33 @@ static bool pmsav7_use_background_region(ARMCPU *cpu, ARMMMUIdx mmu_idx, if (arm_feature(env, ARM_FEATURE_M)) { return env->v7m.mpu_ctrl[is_secure] & R_V7M_MPU_CTRL_PRIVDEFENA_MASK; - } else { - return regime_sctlr(env, mmu_idx) & SCTLR_BR; } + + 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, - bool secure, GetPhysAddrResult *result, +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); result->f.phys_addr = address; result->f.lg_page_size = TARGET_PAGE_BITS; result->f.prot = 0; - if (regime_translation_disabled(env, mmu_idx, secure) || + 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. @@ -1952,6 +2572,26 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, 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, bool secure, GetPhysAddrResult *result, @@ -1974,6 +2614,13 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, 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; + + 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; @@ -1982,6 +2629,10 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, *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 @@ -1989,7 +2640,8 @@ 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, secure)) { /* 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; @@ -1998,17 +2650,26 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, 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; } @@ -2042,7 +2703,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; } @@ -2052,8 +2715,11 @@ 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; } @@ -2061,12 +2727,14 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, /* hit using the background region */ 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)) { @@ -2074,21 +2742,46 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, xn = 1; } - result->f.prot = simple_ap_to_rw_prot(env, mmu_idx, ap); + 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); + } + + 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; } - /* - * We don't need to look the attribute up in the MAIR0/MAIR1 - * registers because that only tells us about cacheability. - */ + if (mregion) { *mregion = matchregion; } } fi->type = ARMFault_Permission; - fi->level = 1; + if (arm_feature(env, ARM_FEATURE_M)) { + fi->level = 1; + } return !(result->f.prot & (1 << access_type)); } @@ -2217,12 +2910,16 @@ 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, - bool secure, GetPhysAddrResult *result, +static bool get_phys_addr_pmsav8(CPUARMState *env, + S1Translate *ptw, + uint32_t address, + MMUAccessType access_type, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { V8M_SAttributes sattrs = {}; + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; + bool secure = arm_space_is_secure(ptw->in_space); bool ret; if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { @@ -2266,6 +2963,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, */ if (sattrs.ns) { result->f.attrs.secure = false; + result->f.attrs.space = ARMSS_NonSecure; } else if (!secure) { /* * NS access to S memory must fault. @@ -2361,7 +3059,11 @@ static uint8_t combined_attrs_nofwb(uint64_t hcr, { uint8_t s1lo, s2lo, s1hi, s2hi, s2_mair_attrs, ret_attrs; - s2_mair_attrs = convert_stage2_attrs(hcr, 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); @@ -2418,6 +3120,8 @@ static uint8_t force_cacheattr_nibble_wb(uint8_t attr) */ 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 */ @@ -2467,7 +3171,7 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, 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) { @@ -2518,21 +3222,25 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, * MMU disabled. S1 addresses within aa64 translation regimes are * still checked for bounds -- see AArch64.S1DisabledOutput(). */ -static bool get_phys_addr_disabled(CPUARMState *env, target_ulong address, +static bool get_phys_addr_disabled(CPUARMState *env, + S1Translate *ptw, + vaddr address, MMUAccessType access_type, - ARMMMUIdx mmu_idx, bool is_secure, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { + ARMMMUIdx mmu_idx = ptw->in_mmu_idx; uint8_t memattr = 0x00; /* Device nGnRnE */ - uint8_t shareability = 0; /* non-sharable */ + uint8_t shareability = 0; /* non-shareable */ int r_el; switch (mmu_idx) { case ARMMMUIdx_Stage2: case ARMMMUIdx_Stage2_S: - case ARMMMUIdx_Phys_NS: case ARMMMUIdx_Phys_S: + case ARMMMUIdx_Phys_NS: + case ARMMMUIdx_Phys_Root: + case ARMMMUIdx_Phys_Realm: break; default: @@ -2567,7 +3275,7 @@ static bool get_phys_addr_disabled(CPUARMState *env, target_ulong address, /* Fill in cacheattr a-la AArch64.TranslateAddressS1Off. */ if (r_el == 1) { - uint64_t hcr = arm_hcr_el2_eff_secstate(env, is_secure); + 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 */ @@ -2576,13 +3284,15 @@ static bool get_phys_addr_disabled(CPUARMState *env, target_ulong address, } } } - if (memattr == 0 && 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 */ + 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 sharable */ + shareability = 2; /* outer shareable */ } result->cacheattrs.is_s2_format = false; break; @@ -2597,20 +3307,21 @@ static bool get_phys_addr_disabled(CPUARMState *env, target_ulong address, } static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, + vaddr address, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { hwaddr ipa; int s1_prot, s1_lgpgsz; - bool is_secure = ptw->in_secure; - bool ret, ipa_secure, s2walk_secure; + ARMSecuritySpace in_space = ptw->in_space; + bool ret, ipa_secure, s1_guarded; ARMCacheAttrs cacheattrs1; - bool is_el0; + ARMSecuritySpace ipa_space; uint64_t hcr; - ret = get_phys_addr_with_struct(env, ptw, address, access_type, result, fi); + ret = get_phys_addr_nogpc(env, ptw, address, access_type, + memop, result, fi); /* If S1 fails, return early. */ if (ret) { @@ -2619,20 +3330,12 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, ipa = result->f.phys_addr; ipa_secure = result->f.attrs.secure; - if (is_secure) { - /* Select TCR based on the NS bit from the S1 walk. */ - s2walk_secure = !(ipa_secure - ? env->cp15.vstcr_el2 & VSTCR_SW - : env->cp15.vtcr_el2 & VTCR_NSW); - } else { - assert(!ipa_secure); - s2walk_secure = false; - } + ipa_space = result->f.attrs.space; - is_el0 = ptw->in_mmu_idx == ARMMMUIdx_Stage1_E0; - ptw->in_mmu_idx = s2walk_secure ? ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2; - ptw->in_ptw_idx = s2walk_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS; - ptw->in_secure = s2walk_secure; + 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. @@ -2640,10 +3343,12 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, */ 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_lpae(env, ptw, ipa, access_type, is_el0, result, fi); + ret = get_phys_addr_nogpc(env, ptw, ipa, access_type, + memop, result, fi); fi->s2addr = ipa; /* Combine the S1 and S2 perms. */ @@ -2655,15 +3360,25 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, } /* - * Use the maximum of the S1 & S2 page size, so that invalidation - * of pages > TARGET_PAGE_SIZE works correctly. + * 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.) */ - if (result->f.lg_page_size < s1_lgpgsz) { + 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, is_secure); + hcr = arm_hcr_el2_eff_secstate(env, in_space); if (hcr & HCR_DC) { /* * HCR.DC forces the first stage attributes to @@ -2680,48 +3395,69 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, 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. */ - result->f.attrs.secure = - (is_secure - && !(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW)) - && (ipa_secure - || !(env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW)))); + 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_with_struct(CPUARMState *env, S1Translate *ptw, - target_ulong address, - MMUAccessType access_type, +static bool get_phys_addr_nogpc(CPUARMState *env, S1Translate *ptw, + vaddr address, + MMUAccessType access_type, MemOp memop, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { ARMMMUIdx mmu_idx = ptw->in_mmu_idx; - bool is_secure = ptw->in_secure; ARMMMUIdx s1_mmu_idx; /* - * The page table entries may downgrade secure to non-secure, but - * cannot upgrade an non-secure translation regime's attributes - * to secure. + * 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.secure = is_secure; + 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, address, access_type, mmu_idx, - is_secure, result, fi); + 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. */ - ptw->in_ptw_idx = is_secure ? ARMMMUIdx_Stage2_S : ARMMMUIdx_Stage2; + /* + * 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: @@ -2740,15 +3476,15 @@ static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw, */ ptw->in_mmu_idx = mmu_idx = s1_mmu_idx; if (arm_feature(env, ARM_FEATURE_EL2) && - !regime_translation_disabled(env, ARMMMUIdx_Stage2, is_secure)) { + !regime_translation_disabled(env, ARMMMUIdx_Stage2, ptw->in_space)) { return get_phys_addr_twostage(env, ptw, address, access_type, - result, fi); + memop, result, fi); } /* fall through */ default: - /* Single stage and second stage uses physical for ptw. */ - ptw->in_ptw_idx = is_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS; + /* Single stage uses physical for ptw. */ + ptw->in_ptw_idx = arm_space_to_phys(ptw->in_space); break; } @@ -2773,16 +3509,16 @@ static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw, if (arm_feature(env, ARM_FEATURE_V8)) { /* PMSAv8 */ - ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx, - is_secure, result, 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, - is_secure, result, 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, - is_secure, result, 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", @@ -2799,14 +3535,14 @@ static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw, /* Definitely a real MMU, not an MPU */ - if (regime_translation_disabled(env, mmu_idx, is_secure)) { - return get_phys_addr_disabled(env, address, access_type, mmu_idx, - is_secure, result, fi); + 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, ptw, address, access_type, false, - result, fi); + return get_phys_addr_lpae(env, ptw, address, access_type, + memop, 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); @@ -2815,24 +3551,46 @@ static bool get_phys_addr_with_struct(CPUARMState *env, S1Translate *ptw, } } -bool get_phys_addr_with_secure(CPUARMState *env, target_ulong address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool is_secure, GetPhysAddrResult *result, - ARMMMUFaultInfo *fi) +static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, + vaddr address, + MMUAccessType access_type, MemOp memop, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) +{ + if (get_phys_addr_nogpc(env, ptw, address, access_type, + memop, 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, vaddr address, + MMUAccessType access_type, MemOp memop, + ARMMMUIdx mmu_idx, ARMSecuritySpace space, + GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) { S1Translate ptw = { .in_mmu_idx = mmu_idx, - .in_secure = is_secure, + .in_space = space, }; - return get_phys_addr_with_struct(env, &ptw, address, access_type, - result, fi); + return get_phys_addr_nogpc(env, &ptw, address, access_type, + memop, result, fi); } -bool get_phys_addr(CPUARMState *env, target_ulong address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, +bool get_phys_addr(CPUARMState *env, vaddr address, + MMUAccessType access_type, MemOp memop, ARMMMUIdx mmu_idx, GetPhysAddrResult *result, ARMMMUFaultInfo *fi) { - bool is_secure; + S1Translate ptw = { + .in_mmu_idx = mmu_idx, + }; + ARMSecuritySpace ss; switch (mmu_idx) { case ARMMMUIdx_E10_0: @@ -2845,30 +3603,56 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, case ARMMMUIdx_Stage1_E1: case ARMMMUIdx_Stage1_E1_PAN: case ARMMMUIdx_E2: - is_secure = arm_is_secure_below_el3(env); + 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: - is_secure = false; + ss = ARMSS_NonSecure; break; - case ARMMMUIdx_E3: case ARMMMUIdx_Stage2_S: case ARMMMUIdx_Phys_S: case ARMMMUIdx_MSPrivNegPri: case ARMMMUIdx_MSUserNegPri: case ARMMMUIdx_MSPriv: case ARMMMUIdx_MSUser: - is_secure = true; + ss = ARMSS_Secure; + break; + case ARMMMUIdx_E3: + case ARMMMUIdx_E30_0: + case ARMMMUIdx_E30_3_PAN: + 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(); } - return get_phys_addr_with_secure(env, address, access_type, mmu_idx, - is_secure, result, fi); + + ptw.in_space = ss; + return get_phys_addr_gpc(env, &ptw, address, access_type, + memop, result, fi); } hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, @@ -2876,16 +3660,18 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + ARMSecuritySpace ss = arm_security_space(env); S1Translate ptw = { - .in_mmu_idx = arm_mmu_idx(env), - .in_secure = arm_is_secure(env), + .in_mmu_idx = mmu_idx, + .in_space = ss, .in_debug = true, }; GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; bool ret; - ret = get_phys_addr_with_struct(env, &ptw, addr, MMU_DATA_LOAD, &res, &fi); + ret = get_phys_addr_gpc(env, &ptw, addr, MMU_DATA_LOAD, 0, &res, &fi); *attrs = res.f.attrs; if (ret) { diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 73df5e3793..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,13 +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, @@ -76,17 +82,26 @@ typedef enum { 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 @@ -212,7 +227,16 @@ 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) @@ -221,14 +245,20 @@ static inline uint32_t syn_smetrap(SMEExceptionType etype, bool is_16bit) | (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) @@ -237,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) @@ -267,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) @@ -307,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..331a8e180c --- /dev/null +++ b/target/arm/tcg/a64.decode @@ -0,0 +1,1395 @@ +# 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 +# + +%rd 0:5 +%esz_sd 22:1 !function=plus_2 +%esz_hsd 22:2 !function=xor_2 +%hl 11:1 21:1 +%hlm 11:1 20:2 + +&r rn +&ri rd imm +&rri_sf rd rn imm sf +&i imm +&rr_e rd rn esz +&rri_e rd rn imm esz +&rrr_e rd rn rm esz +&rrx_e rd rn rm idx esz +&rrrr_e rd rn rm ra esz +&qrr_e q rd rn esz +&qrri_e q rd rn imm esz +&qrrr_e q rd rn rm esz +&qrrx_e q rd rn rm idx esz +&qrrrr_e q rd rn rm ra esz + +@rr_h ........ ... ..... ...... rn:5 rd:5 &rr_e esz=1 +@rr_d ........ ... ..... ...... rn:5 rd:5 &rr_e esz=3 +@rr_sd ........ ... ..... ...... rn:5 rd:5 &rr_e esz=%esz_sd + +@rrr_h ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=1 +@rrr_d ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=3 +@rrr_sd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_sd +@rrr_hsd ........ ... rm:5 ...... rn:5 rd:5 &rrr_e esz=%esz_hsd +@rrr_e ........ esz:2 . rm:5 ...... rn:5 rd:5 &rrr_e +@r2r_e ........ esz:2 . ..... ...... rm:5 rd:5 &rrr_e rn=%rd + +@rrx_h ........ .. .. rm:4 .... . . rn:5 rd:5 &rrx_e esz=1 idx=%hlm +@rrx_s ........ .. . rm:5 .... . . rn:5 rd:5 &rrx_e esz=2 idx=%hl +@rrx_d ........ .. . rm:5 .... idx:1 . rn:5 rd:5 &rrx_e esz=3 + +@rr_q1e0 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=0 +@rr_q1e2 ........ ........ ...... rn:5 rd:5 &qrr_e q=1 esz=2 +@r2r_q1e0 ........ ........ ...... rm:5 rd:5 &qrrr_e rn=%rd q=1 esz=0 +@rrr_q1e0 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=0 +@rrr_q1e3 ........ ... rm:5 ...... rn:5 rd:5 &qrrr_e q=1 esz=3 +@rrrr_q1e3 ........ ... rm:5 . ra:5 rn:5 rd:5 &qrrrr_e q=1 esz=3 + +@qrr_h . q:1 ...... .. ...... ...... rn:5 rd:5 &qrr_e esz=1 +@qrr_e . q:1 ...... esz:2 ...... ...... rn:5 rd:5 &qrr_e + +@qrrr_b . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=0 +@qrrr_h . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=1 +@qrrr_s . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=2 +@qrrr_sd . q:1 ...... ... rm:5 ...... rn:5 rd:5 &qrrr_e esz=%esz_sd +@qrrr_e . q:1 ...... esz:2 . rm:5 ...... rn:5 rd:5 &qrrr_e +@qr2r_e . q:1 ...... esz:2 . ..... ...... rm:5 rd:5 &qrrr_e rn=%rd + +@qrrx_h . q:1 .. .... .. .. rm:4 .... . . rn:5 rd:5 \ + &qrrx_e esz=1 idx=%hlm +@qrrx_s . q:1 .. .... .. . rm:5 .... . . rn:5 rd:5 \ + &qrrx_e esz=2 idx=%hl +@qrrx_d . q:1 .. .... .. . rm:5 .... idx:1 . rn:5 rd:5 \ + &qrrx_e esz=3 + +### 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 +} + +# System instructions with register argument +WFET 1101 0101 0000 0011 0001 0000 000 rd:5 +WFIT 1101 0101 0000 0011 0001 0000 001 rd:5 + +# 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_ALLINT 1101 0101 0000 0 001 0100 000 imm:1 000 11111 +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:s9 .. 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 + +### Cryptographic AES + +AESE 01001110 00 10100 00100 10 ..... ..... @r2r_q1e0 +AESD 01001110 00 10100 00101 10 ..... ..... @r2r_q1e0 +AESMC 01001110 00 10100 00110 10 ..... ..... @rr_q1e0 +AESIMC 01001110 00 10100 00111 10 ..... ..... @rr_q1e0 + +### Cryptographic three-register SHA + +SHA1C 0101 1110 000 ..... 000000 ..... ..... @rrr_q1e0 +SHA1P 0101 1110 000 ..... 000100 ..... ..... @rrr_q1e0 +SHA1M 0101 1110 000 ..... 001000 ..... ..... @rrr_q1e0 +SHA1SU0 0101 1110 000 ..... 001100 ..... ..... @rrr_q1e0 +SHA256H 0101 1110 000 ..... 010000 ..... ..... @rrr_q1e0 +SHA256H2 0101 1110 000 ..... 010100 ..... ..... @rrr_q1e0 +SHA256SU1 0101 1110 000 ..... 011000 ..... ..... @rrr_q1e0 + +### Cryptographic two-register SHA + +SHA1H 0101 1110 0010 1000 0000 10 ..... ..... @rr_q1e0 +SHA1SU1 0101 1110 0010 1000 0001 10 ..... ..... @rr_q1e0 +SHA256SU0 0101 1110 0010 1000 0010 10 ..... ..... @rr_q1e0 + +### Cryptographic three-register SHA512 + +SHA512H 1100 1110 011 ..... 100000 ..... ..... @rrr_q1e0 +SHA512H2 1100 1110 011 ..... 100001 ..... ..... @rrr_q1e0 +SHA512SU1 1100 1110 011 ..... 100010 ..... ..... @rrr_q1e0 +RAX1 1100 1110 011 ..... 100011 ..... ..... @rrr_q1e3 +SM3PARTW1 1100 1110 011 ..... 110000 ..... ..... @rrr_q1e0 +SM3PARTW2 1100 1110 011 ..... 110001 ..... ..... @rrr_q1e0 +SM4EKEY 1100 1110 011 ..... 110010 ..... ..... @rrr_q1e0 + +### Cryptographic two-register SHA512 + +SHA512SU0 1100 1110 110 00000 100000 ..... ..... @rr_q1e0 +SM4E 1100 1110 110 00000 100001 ..... ..... @r2r_q1e0 + +### Cryptographic four-register + +EOR3 1100 1110 000 ..... 0 ..... ..... ..... @rrrr_q1e3 +BCAX 1100 1110 001 ..... 0 ..... ..... ..... @rrrr_q1e3 +SM3SS1 1100 1110 010 ..... 0 ..... ..... ..... @rrrr_q1e3 + +### Cryptographic three-register, imm2 + +&crypto3i rd rn rm imm +@crypto3i ........ ... rm:5 .. imm:2 .. rn:5 rd:5 &crypto3i + +SM3TT1A 11001110 010 ..... 10 .. 00 ..... ..... @crypto3i +SM3TT1B 11001110 010 ..... 10 .. 01 ..... ..... @crypto3i +SM3TT2A 11001110 010 ..... 10 .. 10 ..... ..... @crypto3i +SM3TT2B 11001110 010 ..... 10 .. 11 ..... ..... @crypto3i + +### Cryptographic XAR + +XAR 1100 1110 100 rm:5 imm:6 rn:5 rd:5 + +### Advanced SIMD scalar copy + +DUP_element_s 0101 1110 000 imm:5 0 0000 1 rn:5 rd:5 + +### Advanced SIMD copy + +DUP_element_v 0 q:1 00 1110 000 imm:5 0 0000 1 rn:5 rd:5 +DUP_general 0 q:1 00 1110 000 imm:5 0 0001 1 rn:5 rd:5 +INS_general 0 1 00 1110 000 imm:5 0 0011 1 rn:5 rd:5 +SMOV 0 q:1 00 1110 000 imm:5 0 0101 1 rn:5 rd:5 +UMOV 0 q:1 00 1110 000 imm:5 0 0111 1 rn:5 rd:5 +INS_element 0 1 10 1110 000 di:5 0 si:4 1 rn:5 rd:5 + +### Advanced SIMD scalar three same + +FADD_s 0001 1110 ..1 ..... 0010 10 ..... ..... @rrr_hsd +FSUB_s 0001 1110 ..1 ..... 0011 10 ..... ..... @rrr_hsd +FDIV_s 0001 1110 ..1 ..... 0001 10 ..... ..... @rrr_hsd +FMUL_s 0001 1110 ..1 ..... 0000 10 ..... ..... @rrr_hsd +FNMUL_s 0001 1110 ..1 ..... 1000 10 ..... ..... @rrr_hsd + +FMAX_s 0001 1110 ..1 ..... 0100 10 ..... ..... @rrr_hsd +FMIN_s 0001 1110 ..1 ..... 0101 10 ..... ..... @rrr_hsd +FMAXNM_s 0001 1110 ..1 ..... 0110 10 ..... ..... @rrr_hsd +FMINNM_s 0001 1110 ..1 ..... 0111 10 ..... ..... @rrr_hsd + +FMULX_s 0101 1110 010 ..... 00011 1 ..... ..... @rrr_h +FMULX_s 0101 1110 0.1 ..... 11011 1 ..... ..... @rrr_sd + +FCMEQ_s 0101 1110 010 ..... 00100 1 ..... ..... @rrr_h +FCMEQ_s 0101 1110 0.1 ..... 11100 1 ..... ..... @rrr_sd + +FCMGE_s 0111 1110 010 ..... 00100 1 ..... ..... @rrr_h +FCMGE_s 0111 1110 0.1 ..... 11100 1 ..... ..... @rrr_sd + +FCMGT_s 0111 1110 110 ..... 00100 1 ..... ..... @rrr_h +FCMGT_s 0111 1110 1.1 ..... 11100 1 ..... ..... @rrr_sd + +FACGE_s 0111 1110 010 ..... 00101 1 ..... ..... @rrr_h +FACGE_s 0111 1110 0.1 ..... 11101 1 ..... ..... @rrr_sd + +FACGT_s 0111 1110 110 ..... 00101 1 ..... ..... @rrr_h +FACGT_s 0111 1110 1.1 ..... 11101 1 ..... ..... @rrr_sd + +FABD_s 0111 1110 110 ..... 00010 1 ..... ..... @rrr_h +FABD_s 0111 1110 1.1 ..... 11010 1 ..... ..... @rrr_sd + +FRECPS_s 0101 1110 010 ..... 00111 1 ..... ..... @rrr_h +FRECPS_s 0101 1110 0.1 ..... 11111 1 ..... ..... @rrr_sd + +FRSQRTS_s 0101 1110 110 ..... 00111 1 ..... ..... @rrr_h +FRSQRTS_s 0101 1110 1.1 ..... 11111 1 ..... ..... @rrr_sd + +SQADD_s 0101 1110 ..1 ..... 00001 1 ..... ..... @rrr_e +UQADD_s 0111 1110 ..1 ..... 00001 1 ..... ..... @rrr_e +SQSUB_s 0101 1110 ..1 ..... 00101 1 ..... ..... @rrr_e +UQSUB_s 0111 1110 ..1 ..... 00101 1 ..... ..... @rrr_e + +SUQADD_s 0101 1110 ..1 00000 00111 0 ..... ..... @r2r_e +USQADD_s 0111 1110 ..1 00000 00111 0 ..... ..... @r2r_e + +SSHL_s 0101 1110 111 ..... 01000 1 ..... ..... @rrr_d +USHL_s 0111 1110 111 ..... 01000 1 ..... ..... @rrr_d +SRSHL_s 0101 1110 111 ..... 01010 1 ..... ..... @rrr_d +URSHL_s 0111 1110 111 ..... 01010 1 ..... ..... @rrr_d +SQSHL_s 0101 1110 ..1 ..... 01001 1 ..... ..... @rrr_e +UQSHL_s 0111 1110 ..1 ..... 01001 1 ..... ..... @rrr_e +SQRSHL_s 0101 1110 ..1 ..... 01011 1 ..... ..... @rrr_e +UQRSHL_s 0111 1110 ..1 ..... 01011 1 ..... ..... @rrr_e + +ADD_s 0101 1110 111 ..... 10000 1 ..... ..... @rrr_d +SUB_s 0111 1110 111 ..... 10000 1 ..... ..... @rrr_d +CMGT_s 0101 1110 111 ..... 00110 1 ..... ..... @rrr_d +CMHI_s 0111 1110 111 ..... 00110 1 ..... ..... @rrr_d +CMGE_s 0101 1110 111 ..... 00111 1 ..... ..... @rrr_d +CMHS_s 0111 1110 111 ..... 00111 1 ..... ..... @rrr_d +CMTST_s 0101 1110 111 ..... 10001 1 ..... ..... @rrr_d +CMEQ_s 0111 1110 111 ..... 10001 1 ..... ..... @rrr_d + +SQDMULH_s 0101 1110 ..1 ..... 10110 1 ..... ..... @rrr_e +SQRDMULH_s 0111 1110 ..1 ..... 10110 1 ..... ..... @rrr_e +SQRDMLAH_s 0111 1110 ..0 ..... 10000 1 ..... ..... @rrr_e +SQRDMLSH_s 0111 1110 ..0 ..... 10001 1 ..... ..... @rrr_e + +# Decode scalar x scalar as scalar x indexed, with index 0. +SQDMULL_si 0101 1110 011 rm:5 11010 0 rn:5 rd:5 &rrx_e idx=0 esz=1 +SQDMULL_si 0101 1110 101 rm:5 11010 0 rn:5 rd:5 &rrx_e idx=0 esz=2 +SQDMLAL_si 0101 1110 011 rm:5 10010 0 rn:5 rd:5 &rrx_e idx=0 esz=1 +SQDMLAL_si 0101 1110 101 rm:5 10010 0 rn:5 rd:5 &rrx_e idx=0 esz=2 +SQDMLSL_si 0101 1110 011 rm:5 10110 0 rn:5 rd:5 &rrx_e idx=0 esz=1 +SQDMLSL_si 0101 1110 101 rm:5 10110 0 rn:5 rd:5 &rrx_e idx=0 esz=2 + +### Advanced SIMD scalar pairwise + +FADDP_s 0101 1110 0011 0000 1101 10 ..... ..... @rr_h +FADDP_s 0111 1110 0.11 0000 1101 10 ..... ..... @rr_sd + +FMAXP_s 0101 1110 0011 0000 1111 10 ..... ..... @rr_h +FMAXP_s 0111 1110 0.11 0000 1111 10 ..... ..... @rr_sd + +FMINP_s 0101 1110 1011 0000 1111 10 ..... ..... @rr_h +FMINP_s 0111 1110 1.11 0000 1111 10 ..... ..... @rr_sd + +FMAXNMP_s 0101 1110 0011 0000 1100 10 ..... ..... @rr_h +FMAXNMP_s 0111 1110 0.11 0000 1100 10 ..... ..... @rr_sd + +FMINNMP_s 0101 1110 1011 0000 1100 10 ..... ..... @rr_h +FMINNMP_s 0111 1110 1.11 0000 1100 10 ..... ..... @rr_sd + +ADDP_s 0101 1110 1111 0001 1011 10 ..... ..... @rr_d + +### Advanced SIMD three same + +FADD_v 0.00 1110 010 ..... 00010 1 ..... ..... @qrrr_h +FADD_v 0.00 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd + +FSUB_v 0.00 1110 110 ..... 00010 1 ..... ..... @qrrr_h +FSUB_v 0.00 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd + +FDIV_v 0.10 1110 010 ..... 00111 1 ..... ..... @qrrr_h +FDIV_v 0.10 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd + +FMUL_v 0.10 1110 010 ..... 00011 1 ..... ..... @qrrr_h +FMUL_v 0.10 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd + +FMAX_v 0.00 1110 010 ..... 00110 1 ..... ..... @qrrr_h +FMAX_v 0.00 1110 0.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMIN_v 0.00 1110 110 ..... 00110 1 ..... ..... @qrrr_h +FMIN_v 0.00 1110 1.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMAXNM_v 0.00 1110 010 ..... 00000 1 ..... ..... @qrrr_h +FMAXNM_v 0.00 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd + +FMINNM_v 0.00 1110 110 ..... 00000 1 ..... ..... @qrrr_h +FMINNM_v 0.00 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd + +FMULX_v 0.00 1110 010 ..... 00011 1 ..... ..... @qrrr_h +FMULX_v 0.00 1110 0.1 ..... 11011 1 ..... ..... @qrrr_sd + +FMLA_v 0.00 1110 010 ..... 00001 1 ..... ..... @qrrr_h +FMLA_v 0.00 1110 0.1 ..... 11001 1 ..... ..... @qrrr_sd + +FMLS_v 0.00 1110 110 ..... 00001 1 ..... ..... @qrrr_h +FMLS_v 0.00 1110 1.1 ..... 11001 1 ..... ..... @qrrr_sd + +FMLAL_v 0.00 1110 001 ..... 11101 1 ..... ..... @qrrr_h +FMLSL_v 0.00 1110 101 ..... 11101 1 ..... ..... @qrrr_h +FMLAL2_v 0.10 1110 001 ..... 11001 1 ..... ..... @qrrr_h +FMLSL2_v 0.10 1110 101 ..... 11001 1 ..... ..... @qrrr_h + +FCMEQ_v 0.00 1110 010 ..... 00100 1 ..... ..... @qrrr_h +FCMEQ_v 0.00 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd + +FCMGE_v 0.10 1110 010 ..... 00100 1 ..... ..... @qrrr_h +FCMGE_v 0.10 1110 0.1 ..... 11100 1 ..... ..... @qrrr_sd + +FCMGT_v 0.10 1110 110 ..... 00100 1 ..... ..... @qrrr_h +FCMGT_v 0.10 1110 1.1 ..... 11100 1 ..... ..... @qrrr_sd + +FACGE_v 0.10 1110 010 ..... 00101 1 ..... ..... @qrrr_h +FACGE_v 0.10 1110 0.1 ..... 11101 1 ..... ..... @qrrr_sd + +FACGT_v 0.10 1110 110 ..... 00101 1 ..... ..... @qrrr_h +FACGT_v 0.10 1110 1.1 ..... 11101 1 ..... ..... @qrrr_sd + +FABD_v 0.10 1110 110 ..... 00010 1 ..... ..... @qrrr_h +FABD_v 0.10 1110 1.1 ..... 11010 1 ..... ..... @qrrr_sd + +FRECPS_v 0.00 1110 010 ..... 00111 1 ..... ..... @qrrr_h +FRECPS_v 0.00 1110 0.1 ..... 11111 1 ..... ..... @qrrr_sd + +FRSQRTS_v 0.00 1110 110 ..... 00111 1 ..... ..... @qrrr_h +FRSQRTS_v 0.00 1110 1.1 ..... 11111 1 ..... ..... @qrrr_sd + +FADDP_v 0.10 1110 010 ..... 00010 1 ..... ..... @qrrr_h +FADDP_v 0.10 1110 0.1 ..... 11010 1 ..... ..... @qrrr_sd + +FMAXP_v 0.10 1110 010 ..... 00110 1 ..... ..... @qrrr_h +FMAXP_v 0.10 1110 0.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMINP_v 0.10 1110 110 ..... 00110 1 ..... ..... @qrrr_h +FMINP_v 0.10 1110 1.1 ..... 11110 1 ..... ..... @qrrr_sd + +FMAXNMP_v 0.10 1110 010 ..... 00000 1 ..... ..... @qrrr_h +FMAXNMP_v 0.10 1110 0.1 ..... 11000 1 ..... ..... @qrrr_sd + +FMINNMP_v 0.10 1110 110 ..... 00000 1 ..... ..... @qrrr_h +FMINNMP_v 0.10 1110 1.1 ..... 11000 1 ..... ..... @qrrr_sd + +ADDP_v 0.00 1110 ..1 ..... 10111 1 ..... ..... @qrrr_e +SMAXP_v 0.00 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e +SMINP_v 0.00 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e +UMAXP_v 0.10 1110 ..1 ..... 10100 1 ..... ..... @qrrr_e +UMINP_v 0.10 1110 ..1 ..... 10101 1 ..... ..... @qrrr_e + +AND_v 0.00 1110 001 ..... 00011 1 ..... ..... @qrrr_b +BIC_v 0.00 1110 011 ..... 00011 1 ..... ..... @qrrr_b +ORR_v 0.00 1110 101 ..... 00011 1 ..... ..... @qrrr_b +ORN_v 0.00 1110 111 ..... 00011 1 ..... ..... @qrrr_b +EOR_v 0.10 1110 001 ..... 00011 1 ..... ..... @qrrr_b +BSL_v 0.10 1110 011 ..... 00011 1 ..... ..... @qrrr_b +BIT_v 0.10 1110 101 ..... 00011 1 ..... ..... @qrrr_b +BIF_v 0.10 1110 111 ..... 00011 1 ..... ..... @qrrr_b + +SQADD_v 0.00 1110 ..1 ..... 00001 1 ..... ..... @qrrr_e +UQADD_v 0.10 1110 ..1 ..... 00001 1 ..... ..... @qrrr_e +SQSUB_v 0.00 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e +UQSUB_v 0.10 1110 ..1 ..... 00101 1 ..... ..... @qrrr_e + +SUQADD_v 0.00 1110 ..1 00000 00111 0 ..... ..... @qr2r_e +USQADD_v 0.10 1110 ..1 00000 00111 0 ..... ..... @qr2r_e + +SSHL_v 0.00 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e +USHL_v 0.10 1110 ..1 ..... 01000 1 ..... ..... @qrrr_e +SRSHL_v 0.00 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e +URSHL_v 0.10 1110 ..1 ..... 01010 1 ..... ..... @qrrr_e +SQSHL_v 0.00 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e +UQSHL_v 0.10 1110 ..1 ..... 01001 1 ..... ..... @qrrr_e +SQRSHL_v 0.00 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e +UQRSHL_v 0.10 1110 ..1 ..... 01011 1 ..... ..... @qrrr_e + +ADD_v 0.00 1110 ..1 ..... 10000 1 ..... ..... @qrrr_e +SUB_v 0.10 1110 ..1 ..... 10000 1 ..... ..... @qrrr_e +CMGT_v 0.00 1110 ..1 ..... 00110 1 ..... ..... @qrrr_e +CMHI_v 0.10 1110 ..1 ..... 00110 1 ..... ..... @qrrr_e +CMGE_v 0.00 1110 ..1 ..... 00111 1 ..... ..... @qrrr_e +CMHS_v 0.10 1110 ..1 ..... 00111 1 ..... ..... @qrrr_e +CMTST_v 0.00 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e +CMEQ_v 0.10 1110 ..1 ..... 10001 1 ..... ..... @qrrr_e +SHADD_v 0.00 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e +UHADD_v 0.10 1110 ..1 ..... 00000 1 ..... ..... @qrrr_e +SHSUB_v 0.00 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e +UHSUB_v 0.10 1110 ..1 ..... 00100 1 ..... ..... @qrrr_e +SRHADD_v 0.00 1110 ..1 ..... 00010 1 ..... ..... @qrrr_e +URHADD_v 0.10 1110 ..1 ..... 00010 1 ..... ..... @qrrr_e +SMAX_v 0.00 1110 ..1 ..... 01100 1 ..... ..... @qrrr_e +UMAX_v 0.10 1110 ..1 ..... 01100 1 ..... ..... @qrrr_e +SMIN_v 0.00 1110 ..1 ..... 01101 1 ..... ..... @qrrr_e +UMIN_v 0.10 1110 ..1 ..... 01101 1 ..... ..... @qrrr_e +SABD_v 0.00 1110 ..1 ..... 01110 1 ..... ..... @qrrr_e +UABD_v 0.10 1110 ..1 ..... 01110 1 ..... ..... @qrrr_e +SABA_v 0.00 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e +UABA_v 0.10 1110 ..1 ..... 01111 1 ..... ..... @qrrr_e +MUL_v 0.00 1110 ..1 ..... 10011 1 ..... ..... @qrrr_e +PMUL_v 0.10 1110 001 ..... 10011 1 ..... ..... @qrrr_b +MLA_v 0.00 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e +MLS_v 0.10 1110 ..1 ..... 10010 1 ..... ..... @qrrr_e + +SQDMULH_v 0.00 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e +SQRDMULH_v 0.10 1110 ..1 ..... 10110 1 ..... ..... @qrrr_e +SQRDMLAH_v 0.10 1110 ..0 ..... 10000 1 ..... ..... @qrrr_e +SQRDMLSH_v 0.10 1110 ..0 ..... 10001 1 ..... ..... @qrrr_e + +SDOT_v 0.00 1110 100 ..... 10010 1 ..... ..... @qrrr_s +UDOT_v 0.10 1110 100 ..... 10010 1 ..... ..... @qrrr_s +USDOT_v 0.00 1110 100 ..... 10011 1 ..... ..... @qrrr_s +BFDOT_v 0.10 1110 010 ..... 11111 1 ..... ..... @qrrr_s +BFMLAL_v 0.10 1110 110 ..... 11111 1 ..... ..... @qrrr_h +BFMMLA 0110 1110 010 ..... 11101 1 ..... ..... @rrr_q1e0 +SMMLA 0100 1110 100 ..... 10100 1 ..... ..... @rrr_q1e0 +UMMLA 0110 1110 100 ..... 10100 1 ..... ..... @rrr_q1e0 +USMMLA 0100 1110 100 ..... 10101 1 ..... ..... @rrr_q1e0 + +FCADD_90 0.10 1110 ..0 ..... 11100 1 ..... ..... @qrrr_e +FCADD_270 0.10 1110 ..0 ..... 11110 1 ..... ..... @qrrr_e + +FCMLA_v 0 q:1 10 1110 esz:2 0 rm:5 110 rot:2 1 rn:5 rd:5 + +SMULL_v 0.00 1110 ..1 ..... 11000 0 ..... ..... @qrrr_e +UMULL_v 0.10 1110 ..1 ..... 11000 0 ..... ..... @qrrr_e +SMLAL_v 0.00 1110 ..1 ..... 10000 0 ..... ..... @qrrr_e +UMLAL_v 0.10 1110 ..1 ..... 10000 0 ..... ..... @qrrr_e +SMLSL_v 0.00 1110 ..1 ..... 10100 0 ..... ..... @qrrr_e +UMLSL_v 0.10 1110 ..1 ..... 10100 0 ..... ..... @qrrr_e + +SADDL_v 0.00 1110 ..1 ..... 00000 0 ..... ..... @qrrr_e +UADDL_v 0.10 1110 ..1 ..... 00000 0 ..... ..... @qrrr_e +SSUBL_v 0.00 1110 ..1 ..... 00100 0 ..... ..... @qrrr_e +USUBL_v 0.10 1110 ..1 ..... 00100 0 ..... ..... @qrrr_e +SABAL_v 0.00 1110 ..1 ..... 01010 0 ..... ..... @qrrr_e +UABAL_v 0.10 1110 ..1 ..... 01010 0 ..... ..... @qrrr_e +SABDL_v 0.00 1110 ..1 ..... 01110 0 ..... ..... @qrrr_e +UABDL_v 0.10 1110 ..1 ..... 01110 0 ..... ..... @qrrr_e + +SQDMULL_v 0.00 1110 011 ..... 11010 0 ..... ..... @qrrr_h +SQDMULL_v 0.00 1110 101 ..... 11010 0 ..... ..... @qrrr_s +SQDMLAL_v 0.00 1110 011 ..... 10010 0 ..... ..... @qrrr_h +SQDMLAL_v 0.00 1110 101 ..... 10010 0 ..... ..... @qrrr_s +SQDMLSL_v 0.00 1110 011 ..... 10110 0 ..... ..... @qrrr_h +SQDMLSL_v 0.00 1110 101 ..... 10110 0 ..... ..... @qrrr_s + +SADDW 0.00 1110 ..1 ..... 00010 0 ..... ..... @qrrr_e +UADDW 0.10 1110 ..1 ..... 00010 0 ..... ..... @qrrr_e +SSUBW 0.00 1110 ..1 ..... 00110 0 ..... ..... @qrrr_e +USUBW 0.10 1110 ..1 ..... 00110 0 ..... ..... @qrrr_e + +ADDHN 0.00 1110 ..1 ..... 01000 0 ..... ..... @qrrr_e +RADDHN 0.10 1110 ..1 ..... 01000 0 ..... ..... @qrrr_e +SUBHN 0.00 1110 ..1 ..... 01100 0 ..... ..... @qrrr_e +RSUBHN 0.10 1110 ..1 ..... 01100 0 ..... ..... @qrrr_e + +PMULL_p8 0.00 1110 001 ..... 11100 0 ..... ..... @qrrr_b +PMULL_p64 0.00 1110 111 ..... 11100 0 ..... ..... @qrrr_b + +### Advanced SIMD scalar x indexed element + +FMUL_si 0101 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h +FMUL_si 0101 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s +FMUL_si 0101 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d + +FMLA_si 0101 1111 00 .. .... 0001 . 0 ..... ..... @rrx_h +FMLA_si 0101 1111 10 .. .... 0001 . 0 ..... ..... @rrx_s +FMLA_si 0101 1111 11 0. .... 0001 . 0 ..... ..... @rrx_d + +FMLS_si 0101 1111 00 .. .... 0101 . 0 ..... ..... @rrx_h +FMLS_si 0101 1111 10 .. .... 0101 . 0 ..... ..... @rrx_s +FMLS_si 0101 1111 11 0. .... 0101 . 0 ..... ..... @rrx_d + +FMULX_si 0111 1111 00 .. .... 1001 . 0 ..... ..... @rrx_h +FMULX_si 0111 1111 10 . ..... 1001 . 0 ..... ..... @rrx_s +FMULX_si 0111 1111 11 0 ..... 1001 . 0 ..... ..... @rrx_d + +SQDMULH_si 0101 1111 01 .. .... 1100 . 0 ..... ..... @rrx_h +SQDMULH_si 0101 1111 10 .. .... 1100 . 0 ..... ..... @rrx_s + +SQRDMULH_si 0101 1111 01 .. .... 1101 . 0 ..... ..... @rrx_h +SQRDMULH_si 0101 1111 10 . ..... 1101 . 0 ..... ..... @rrx_s + +SQRDMLAH_si 0111 1111 01 .. .... 1101 . 0 ..... ..... @rrx_h +SQRDMLAH_si 0111 1111 10 .. .... 1101 . 0 ..... ..... @rrx_s + +SQRDMLSH_si 0111 1111 01 .. .... 1111 . 0 ..... ..... @rrx_h +SQRDMLSH_si 0111 1111 10 .. .... 1111 . 0 ..... ..... @rrx_s + +SQDMULL_si 0101 1111 01 .. .... 1011 . 0 ..... ..... @rrx_h +SQDMULL_si 0101 1111 10 . ..... 1011 . 0 ..... ..... @rrx_s + +SQDMLAL_si 0101 1111 01 .. .... 0011 . 0 ..... ..... @rrx_h +SQDMLAL_si 0101 1111 10 . ..... 0011 . 0 ..... ..... @rrx_s + +SQDMLSL_si 0101 1111 01 .. .... 0111 . 0 ..... ..... @rrx_h +SQDMLSL_si 0101 1111 10 . ..... 0111 . 0 ..... ..... @rrx_s + +### Advanced SIMD vector x indexed element + +FMUL_vi 0.00 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h +FMUL_vi 0.00 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s +FMUL_vi 0.00 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d + +FMLA_vi 0.00 1111 00 .. .... 0001 . 0 ..... ..... @qrrx_h +FMLA_vi 0.00 1111 10 . ..... 0001 . 0 ..... ..... @qrrx_s +FMLA_vi 0.00 1111 11 0 ..... 0001 . 0 ..... ..... @qrrx_d + +FMLS_vi 0.00 1111 00 .. .... 0101 . 0 ..... ..... @qrrx_h +FMLS_vi 0.00 1111 10 . ..... 0101 . 0 ..... ..... @qrrx_s +FMLS_vi 0.00 1111 11 0 ..... 0101 . 0 ..... ..... @qrrx_d + +FMULX_vi 0.10 1111 00 .. .... 1001 . 0 ..... ..... @qrrx_h +FMULX_vi 0.10 1111 10 . ..... 1001 . 0 ..... ..... @qrrx_s +FMULX_vi 0.10 1111 11 0 ..... 1001 . 0 ..... ..... @qrrx_d + +FMLAL_vi 0.00 1111 10 .. .... 0000 . 0 ..... ..... @qrrx_h +FMLSL_vi 0.00 1111 10 .. .... 0100 . 0 ..... ..... @qrrx_h +FMLAL2_vi 0.10 1111 10 .. .... 1000 . 0 ..... ..... @qrrx_h +FMLSL2_vi 0.10 1111 10 .. .... 1100 . 0 ..... ..... @qrrx_h + +MUL_vi 0.00 1111 01 .. .... 1000 . 0 ..... ..... @qrrx_h +MUL_vi 0.00 1111 10 . ..... 1000 . 0 ..... ..... @qrrx_s + +MLA_vi 0.10 1111 01 .. .... 0000 . 0 ..... ..... @qrrx_h +MLA_vi 0.10 1111 10 . ..... 0000 . 0 ..... ..... @qrrx_s + +MLS_vi 0.10 1111 01 .. .... 0100 . 0 ..... ..... @qrrx_h +MLS_vi 0.10 1111 10 . ..... 0100 . 0 ..... ..... @qrrx_s + +SQDMULH_vi 0.00 1111 01 .. .... 1100 . 0 ..... ..... @qrrx_h +SQDMULH_vi 0.00 1111 10 . ..... 1100 . 0 ..... ..... @qrrx_s + +SQRDMULH_vi 0.00 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h +SQRDMULH_vi 0.00 1111 10 . ..... 1101 . 0 ..... ..... @qrrx_s + +SQRDMLAH_vi 0.10 1111 01 .. .... 1101 . 0 ..... ..... @qrrx_h +SQRDMLAH_vi 0.10 1111 10 .. .... 1101 . 0 ..... ..... @qrrx_s + +SQRDMLSH_vi 0.10 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_h +SQRDMLSH_vi 0.10 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s + +SDOT_vi 0.00 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s +UDOT_vi 0.10 1111 10 .. .... 1110 . 0 ..... ..... @qrrx_s +SUDOT_vi 0.00 1111 00 .. .... 1111 . 0 ..... ..... @qrrx_s +USDOT_vi 0.00 1111 10 .. .... 1111 . 0 ..... ..... @qrrx_s +BFDOT_vi 0.00 1111 01 .. .... 1111 . 0 ..... ..... @qrrx_s +BFMLAL_vi 0.00 1111 11 .. .... 1111 . 0 ..... ..... @qrrx_h + +FCMLA_vi 0 0 10 1111 01 idx:1 rm:5 0 rot:2 1 0 0 rn:5 rd:5 esz=1 q=0 +FCMLA_vi 0 1 10 1111 01 . rm:5 0 rot:2 1 . 0 rn:5 rd:5 esz=1 idx=%hl q=1 +FCMLA_vi 0 1 10 1111 10 0 rm:5 0 rot:2 1 idx:1 0 rn:5 rd:5 esz=2 q=1 + +SMULL_vi 0.00 1111 01 .. .... 1010 . 0 ..... ..... @qrrx_h +SMULL_vi 0.00 1111 10 . ..... 1010 . 0 ..... ..... @qrrx_s +UMULL_vi 0.10 1111 01 .. .... 1010 . 0 ..... ..... @qrrx_h +UMULL_vi 0.10 1111 10 . ..... 1010 . 0 ..... ..... @qrrx_s + +SMLAL_vi 0.00 1111 01 .. .... 0010 . 0 ..... ..... @qrrx_h +SMLAL_vi 0.00 1111 10 . ..... 0010 . 0 ..... ..... @qrrx_s +UMLAL_vi 0.10 1111 01 .. .... 0010 . 0 ..... ..... @qrrx_h +UMLAL_vi 0.10 1111 10 . ..... 0010 . 0 ..... ..... @qrrx_s + +SMLSL_vi 0.00 1111 01 .. .... 0110 . 0 ..... ..... @qrrx_h +SMLSL_vi 0.00 1111 10 . ..... 0110 . 0 ..... ..... @qrrx_s +UMLSL_vi 0.10 1111 01 .. .... 0110 . 0 ..... ..... @qrrx_h +UMLSL_vi 0.10 1111 10 . ..... 0110 . 0 ..... ..... @qrrx_s + +SQDMULL_vi 0.00 1111 01 .. .... 1011 . 0 ..... ..... @qrrx_h +SQDMULL_vi 0.00 1111 10 . ..... 1011 . 0 ..... ..... @qrrx_s + +SQDMLAL_vi 0.00 1111 01 .. .... 0011 . 0 ..... ..... @qrrx_h +SQDMLAL_vi 0.00 1111 10 . ..... 0011 . 0 ..... ..... @qrrx_s + +SQDMLSL_vi 0.00 1111 01 .. .... 0111 . 0 ..... ..... @qrrx_h +SQDMLSL_vi 0.00 1111 10 . ..... 0111 . 0 ..... ..... @qrrx_s + +# Floating-point conditional select + +FCSEL 0001 1110 .. 1 rm:5 cond:4 11 rn:5 rd:5 esz=%esz_hsd + +# Floating-point data-processing (3 source) + +@rrrr_hsd .... .... .. . rm:5 . ra:5 rn:5 rd:5 &rrrr_e esz=%esz_hsd + +FMADD 0001 1111 .. 0 ..... 0 ..... ..... ..... @rrrr_hsd +FMSUB 0001 1111 .. 0 ..... 1 ..... ..... ..... @rrrr_hsd +FNMADD 0001 1111 .. 1 ..... 0 ..... ..... ..... @rrrr_hsd +FNMSUB 0001 1111 .. 1 ..... 1 ..... ..... ..... @rrrr_hsd + +# Advanced SIMD Extract + +EXT_d 0010 1110 00 0 rm:5 00 imm:3 0 rn:5 rd:5 +EXT_q 0110 1110 00 0 rm:5 0 imm:4 0 rn:5 rd:5 + +# Advanced SIMD Table Lookup + +TBL_TBX 0 q:1 00 1110 000 rm:5 0 len:2 tbx:1 00 rn:5 rd:5 + +# Advanced SIMD Permute + +UZP1 0.00 1110 .. 0 ..... 0 001 10 ..... ..... @qrrr_e +UZP2 0.00 1110 .. 0 ..... 0 101 10 ..... ..... @qrrr_e +TRN1 0.00 1110 .. 0 ..... 0 010 10 ..... ..... @qrrr_e +TRN2 0.00 1110 .. 0 ..... 0 110 10 ..... ..... @qrrr_e +ZIP1 0.00 1110 .. 0 ..... 0 011 10 ..... ..... @qrrr_e +ZIP2 0.00 1110 .. 0 ..... 0 111 10 ..... ..... @qrrr_e + +# Advanced SIMD Across Lanes + +ADDV 0.00 1110 .. 11000 11011 10 ..... ..... @qrr_e +SADDLV 0.00 1110 .. 11000 00011 10 ..... ..... @qrr_e +UADDLV 0.10 1110 .. 11000 00011 10 ..... ..... @qrr_e +SMAXV 0.00 1110 .. 11000 01010 10 ..... ..... @qrr_e +UMAXV 0.10 1110 .. 11000 01010 10 ..... ..... @qrr_e +SMINV 0.00 1110 .. 11000 11010 10 ..... ..... @qrr_e +UMINV 0.10 1110 .. 11000 11010 10 ..... ..... @qrr_e + +FMAXNMV_h 0.00 1110 00 11000 01100 10 ..... ..... @qrr_h +FMAXNMV_s 0110 1110 00 11000 01100 10 ..... ..... @rr_q1e2 + +FMINNMV_h 0.00 1110 10 11000 01100 10 ..... ..... @qrr_h +FMINNMV_s 0110 1110 10 11000 01100 10 ..... ..... @rr_q1e2 + +FMAXV_h 0.00 1110 00 11000 01111 10 ..... ..... @qrr_h +FMAXV_s 0110 1110 00 11000 01111 10 ..... ..... @rr_q1e2 + +FMINV_h 0.00 1110 10 11000 01111 10 ..... ..... @qrr_h +FMINV_s 0110 1110 10 11000 01111 10 ..... ..... @rr_q1e2 + +# Floating-point Immediate + +FMOVI_s 0001 1110 .. 1 imm:8 100 00000 rd:5 esz=%esz_hsd + +# Advanced SIMD Modified Immediate / Shift by Immediate + +%abcdefgh 16:3 5:5 + +# Right shifts are encoded as N - shift, where N is the element size in bits. +%neon_rshift_i6 16:6 !function=rsub_64 +%neon_rshift_i5 16:5 !function=rsub_32 +%neon_rshift_i4 16:4 !function=rsub_16 +%neon_rshift_i3 16:3 !function=rsub_8 + +@q_shri_b . q:1 .. ..... 0001 ... ..... . rn:5 rd:5 \ + &qrri_e esz=0 imm=%neon_rshift_i3 +@q_shri_h . q:1 .. ..... 001 .... ..... . rn:5 rd:5 \ + &qrri_e esz=1 imm=%neon_rshift_i4 +@q_shri_s . q:1 .. ..... 01 ..... ..... . rn:5 rd:5 \ + &qrri_e esz=2 imm=%neon_rshift_i5 +@q_shri_d . 1 .. ..... 1 ...... ..... . rn:5 rd:5 \ + &qrri_e esz=3 imm=%neon_rshift_i6 q=1 + +@q_shli_b . q:1 .. ..... 0001 imm:3 ..... . rn:5 rd:5 &qrri_e esz=0 +@q_shli_h . q:1 .. ..... 001 imm:4 ..... . rn:5 rd:5 &qrri_e esz=1 +@q_shli_s . q:1 .. ..... 01 imm:5 ..... . rn:5 rd:5 &qrri_e esz=2 +@q_shli_d . 1 .. ..... 1 imm:6 ..... . rn:5 rd:5 &qrri_e esz=3 q=1 + +FMOVI_v_h 0 q:1 00 1111 00000 ... 1111 11 ..... rd:5 %abcdefgh + +# MOVI, MVNI, ORR, BIC, FMOV are all intermixed via cmode. +Vimm 0 q:1 op:1 0 1111 00000 ... cmode:4 01 ..... rd:5 %abcdefgh + +SSHR_v 0.00 11110 .... ... 00000 1 ..... ..... @q_shri_b +SSHR_v 0.00 11110 .... ... 00000 1 ..... ..... @q_shri_h +SSHR_v 0.00 11110 .... ... 00000 1 ..... ..... @q_shri_s +SSHR_v 0.00 11110 .... ... 00000 1 ..... ..... @q_shri_d + +USHR_v 0.10 11110 .... ... 00000 1 ..... ..... @q_shri_b +USHR_v 0.10 11110 .... ... 00000 1 ..... ..... @q_shri_h +USHR_v 0.10 11110 .... ... 00000 1 ..... ..... @q_shri_s +USHR_v 0.10 11110 .... ... 00000 1 ..... ..... @q_shri_d + +SSRA_v 0.00 11110 .... ... 00010 1 ..... ..... @q_shri_b +SSRA_v 0.00 11110 .... ... 00010 1 ..... ..... @q_shri_h +SSRA_v 0.00 11110 .... ... 00010 1 ..... ..... @q_shri_s +SSRA_v 0.00 11110 .... ... 00010 1 ..... ..... @q_shri_d + +USRA_v 0.10 11110 .... ... 00010 1 ..... ..... @q_shri_b +USRA_v 0.10 11110 .... ... 00010 1 ..... ..... @q_shri_h +USRA_v 0.10 11110 .... ... 00010 1 ..... ..... @q_shri_s +USRA_v 0.10 11110 .... ... 00010 1 ..... ..... @q_shri_d + +SRSHR_v 0.00 11110 .... ... 00100 1 ..... ..... @q_shri_b +SRSHR_v 0.00 11110 .... ... 00100 1 ..... ..... @q_shri_h +SRSHR_v 0.00 11110 .... ... 00100 1 ..... ..... @q_shri_s +SRSHR_v 0.00 11110 .... ... 00100 1 ..... ..... @q_shri_d + +URSHR_v 0.10 11110 .... ... 00100 1 ..... ..... @q_shri_b +URSHR_v 0.10 11110 .... ... 00100 1 ..... ..... @q_shri_h +URSHR_v 0.10 11110 .... ... 00100 1 ..... ..... @q_shri_s +URSHR_v 0.10 11110 .... ... 00100 1 ..... ..... @q_shri_d + +SRSRA_v 0.00 11110 .... ... 00110 1 ..... ..... @q_shri_b +SRSRA_v 0.00 11110 .... ... 00110 1 ..... ..... @q_shri_h +SRSRA_v 0.00 11110 .... ... 00110 1 ..... ..... @q_shri_s +SRSRA_v 0.00 11110 .... ... 00110 1 ..... ..... @q_shri_d + +URSRA_v 0.10 11110 .... ... 00110 1 ..... ..... @q_shri_b +URSRA_v 0.10 11110 .... ... 00110 1 ..... ..... @q_shri_h +URSRA_v 0.10 11110 .... ... 00110 1 ..... ..... @q_shri_s +URSRA_v 0.10 11110 .... ... 00110 1 ..... ..... @q_shri_d + +SRI_v 0.10 11110 .... ... 01000 1 ..... ..... @q_shri_b +SRI_v 0.10 11110 .... ... 01000 1 ..... ..... @q_shri_h +SRI_v 0.10 11110 .... ... 01000 1 ..... ..... @q_shri_s +SRI_v 0.10 11110 .... ... 01000 1 ..... ..... @q_shri_d + +SHL_v 0.00 11110 .... ... 01010 1 ..... ..... @q_shli_b +SHL_v 0.00 11110 .... ... 01010 1 ..... ..... @q_shli_h +SHL_v 0.00 11110 .... ... 01010 1 ..... ..... @q_shli_s +SHL_v 0.00 11110 .... ... 01010 1 ..... ..... @q_shli_d + +SLI_v 0.10 11110 .... ... 01010 1 ..... ..... @q_shli_b +SLI_v 0.10 11110 .... ... 01010 1 ..... ..... @q_shli_h +SLI_v 0.10 11110 .... ... 01010 1 ..... ..... @q_shli_s +SLI_v 0.10 11110 .... ... 01010 1 ..... ..... @q_shli_d + +SSHLL_v 0.00 11110 .... ... 10100 1 ..... ..... @q_shli_b +SSHLL_v 0.00 11110 .... ... 10100 1 ..... ..... @q_shli_h +SSHLL_v 0.00 11110 .... ... 10100 1 ..... ..... @q_shli_s + +USHLL_v 0.10 11110 .... ... 10100 1 ..... ..... @q_shli_b +USHLL_v 0.10 11110 .... ... 10100 1 ..... ..... @q_shli_h +USHLL_v 0.10 11110 .... ... 10100 1 ..... ..... @q_shli_s + +SHRN_v 0.00 11110 .... ... 10000 1 ..... ..... @q_shri_b +SHRN_v 0.00 11110 .... ... 10000 1 ..... ..... @q_shri_h +SHRN_v 0.00 11110 .... ... 10000 1 ..... ..... @q_shri_s + +RSHRN_v 0.00 11110 .... ... 10001 1 ..... ..... @q_shri_b +RSHRN_v 0.00 11110 .... ... 10001 1 ..... ..... @q_shri_h +RSHRN_v 0.00 11110 .... ... 10001 1 ..... ..... @q_shri_s + +SQSHL_vi 0.00 11110 .... ... 01110 1 ..... ..... @q_shli_b +SQSHL_vi 0.00 11110 .... ... 01110 1 ..... ..... @q_shli_h +SQSHL_vi 0.00 11110 .... ... 01110 1 ..... ..... @q_shli_s +SQSHL_vi 0.00 11110 .... ... 01110 1 ..... ..... @q_shli_d + +UQSHL_vi 0.10 11110 .... ... 01110 1 ..... ..... @q_shli_b +UQSHL_vi 0.10 11110 .... ... 01110 1 ..... ..... @q_shli_h +UQSHL_vi 0.10 11110 .... ... 01110 1 ..... ..... @q_shli_s +UQSHL_vi 0.10 11110 .... ... 01110 1 ..... ..... @q_shli_d + +SQSHLU_vi 0.10 11110 .... ... 01100 1 ..... ..... @q_shli_b +SQSHLU_vi 0.10 11110 .... ... 01100 1 ..... ..... @q_shli_h +SQSHLU_vi 0.10 11110 .... ... 01100 1 ..... ..... @q_shli_s +SQSHLU_vi 0.10 11110 .... ... 01100 1 ..... ..... @q_shli_d + +SQSHRN_v 0.00 11110 .... ... 10010 1 ..... ..... @q_shri_b +SQSHRN_v 0.00 11110 .... ... 10010 1 ..... ..... @q_shri_h +SQSHRN_v 0.00 11110 .... ... 10010 1 ..... ..... @q_shri_s + +UQSHRN_v 0.10 11110 .... ... 10010 1 ..... ..... @q_shri_b +UQSHRN_v 0.10 11110 .... ... 10010 1 ..... ..... @q_shri_h +UQSHRN_v 0.10 11110 .... ... 10010 1 ..... ..... @q_shri_s + +SQSHRUN_v 0.10 11110 .... ... 10000 1 ..... ..... @q_shri_b +SQSHRUN_v 0.10 11110 .... ... 10000 1 ..... ..... @q_shri_h +SQSHRUN_v 0.10 11110 .... ... 10000 1 ..... ..... @q_shri_s + +SQRSHRN_v 0.00 11110 .... ... 10011 1 ..... ..... @q_shri_b +SQRSHRN_v 0.00 11110 .... ... 10011 1 ..... ..... @q_shri_h +SQRSHRN_v 0.00 11110 .... ... 10011 1 ..... ..... @q_shri_s + +UQRSHRN_v 0.10 11110 .... ... 10011 1 ..... ..... @q_shri_b +UQRSHRN_v 0.10 11110 .... ... 10011 1 ..... ..... @q_shri_h +UQRSHRN_v 0.10 11110 .... ... 10011 1 ..... ..... @q_shri_s + +SQRSHRUN_v 0.10 11110 .... ... 10001 1 ..... ..... @q_shri_b +SQRSHRUN_v 0.10 11110 .... ... 10001 1 ..... ..... @q_shri_h +SQRSHRUN_v 0.10 11110 .... ... 10001 1 ..... ..... @q_shri_s + +# Advanced SIMD scalar shift by immediate + +@shri_b .... ..... 0001 ... ..... . rn:5 rd:5 \ + &rri_e esz=0 imm=%neon_rshift_i3 +@shri_h .... ..... 001 .... ..... . rn:5 rd:5 \ + &rri_e esz=1 imm=%neon_rshift_i4 +@shri_s .... ..... 01 ..... ..... . rn:5 rd:5 \ + &rri_e esz=2 imm=%neon_rshift_i5 +@shri_d .... ..... 1 ...... ..... . rn:5 rd:5 \ + &rri_e esz=3 imm=%neon_rshift_i6 + +@shli_b .... ..... 0001 imm:3 ..... . rn:5 rd:5 &rri_e esz=0 +@shli_h .... ..... 001 imm:4 ..... . rn:5 rd:5 &rri_e esz=1 +@shli_s .... ..... 01 imm:5 ..... . rn:5 rd:5 &rri_e esz=2 +@shli_d .... ..... 1 imm:6 ..... . rn:5 rd:5 &rri_e esz=3 + +SSHR_s 0101 11110 .... ... 00000 1 ..... ..... @shri_d +USHR_s 0111 11110 .... ... 00000 1 ..... ..... @shri_d +SSRA_s 0101 11110 .... ... 00010 1 ..... ..... @shri_d +USRA_s 0111 11110 .... ... 00010 1 ..... ..... @shri_d +SRSHR_s 0101 11110 .... ... 00100 1 ..... ..... @shri_d +URSHR_s 0111 11110 .... ... 00100 1 ..... ..... @shri_d +SRSRA_s 0101 11110 .... ... 00110 1 ..... ..... @shri_d +URSRA_s 0111 11110 .... ... 00110 1 ..... ..... @shri_d +SRI_s 0111 11110 .... ... 01000 1 ..... ..... @shri_d + +SHL_s 0101 11110 .... ... 01010 1 ..... ..... @shli_d +SLI_s 0111 11110 .... ... 01010 1 ..... ..... @shli_d + +SQSHL_si 0101 11110 .... ... 01110 1 ..... ..... @shli_b +SQSHL_si 0101 11110 .... ... 01110 1 ..... ..... @shli_h +SQSHL_si 0101 11110 .... ... 01110 1 ..... ..... @shli_s +SQSHL_si 0101 11110 .... ... 01110 1 ..... ..... @shli_d + +UQSHL_si 0111 11110 .... ... 01110 1 ..... ..... @shli_b +UQSHL_si 0111 11110 .... ... 01110 1 ..... ..... @shli_h +UQSHL_si 0111 11110 .... ... 01110 1 ..... ..... @shli_s +UQSHL_si 0111 11110 .... ... 01110 1 ..... ..... @shli_d + +SQSHLU_si 0111 11110 .... ... 01100 1 ..... ..... @shli_b +SQSHLU_si 0111 11110 .... ... 01100 1 ..... ..... @shli_h +SQSHLU_si 0111 11110 .... ... 01100 1 ..... ..... @shli_s +SQSHLU_si 0111 11110 .... ... 01100 1 ..... ..... @shli_d + +SQSHRN_si 0101 11110 .... ... 10010 1 ..... ..... @shri_b +SQSHRN_si 0101 11110 .... ... 10010 1 ..... ..... @shri_h +SQSHRN_si 0101 11110 .... ... 10010 1 ..... ..... @shri_s + +UQSHRN_si 0111 11110 .... ... 10010 1 ..... ..... @shri_b +UQSHRN_si 0111 11110 .... ... 10010 1 ..... ..... @shri_h +UQSHRN_si 0111 11110 .... ... 10010 1 ..... ..... @shri_s + +SQSHRUN_si 0111 11110 .... ... 10000 1 ..... ..... @shri_b +SQSHRUN_si 0111 11110 .... ... 10000 1 ..... ..... @shri_h +SQSHRUN_si 0111 11110 .... ... 10000 1 ..... ..... @shri_s + +SQRSHRN_si 0101 11110 .... ... 10011 1 ..... ..... @shri_b +SQRSHRN_si 0101 11110 .... ... 10011 1 ..... ..... @shri_h +SQRSHRN_si 0101 11110 .... ... 10011 1 ..... ..... @shri_s + +UQRSHRN_si 0111 11110 .... ... 10011 1 ..... ..... @shri_b +UQRSHRN_si 0111 11110 .... ... 10011 1 ..... ..... @shri_h +UQRSHRN_si 0111 11110 .... ... 10011 1 ..... ..... @shri_s + +SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_b +SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_h +SQRSHRUN_si 0111 11110 .... ... 10001 1 ..... ..... @shri_s 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..58e54578d6 --- /dev/null +++ b/target/arm/tcg/cpu-v7m.c @@ -0,0 +1,291 @@ +/* + * 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_align = arm_cpu_tlb_fill_align, + .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, + .cpu_exec_halt = arm_cpu_exec_halt, + .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/tcg/cpu32.c b/target/arm/tcg/cpu32.c new file mode 100644 index 0000000000..2ad2182525 --- /dev/null +++ b/target/arm/tcg/cpu32.c @@ -0,0 +1,1068 @@ +/* + * QEMU ARM 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" +#include "target/arm/idau.h" +#if !defined(CONFIG_USER_ONLY) +#include "hw/boards.h" +#endif +#include "cpregs.h" + + +/* Share AArch32 -cpu max features with AArch64. */ +void aa32_max_features(ARMCPU *cpu) +{ + uint32_t t; + + /* Add additional features supported by QEMU */ + t = cpu->isar.id_isar5; + t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ + t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ + t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ + t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); + t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ + t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ + cpu->isar.id_isar5 = t; + + t = cpu->isar.id_isar6; + t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ + t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ + t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ + t = FIELD_DP32(t, ID_ISAR6, SB, 1); /* FEAT_SB */ + t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ + t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ + t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ + cpu->isar.id_isar6 = t; + + t = cpu->isar.mvfr1; + t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ + t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* FEAT_FP16 */ + cpu->isar.mvfr1 = t; + + t = cpu->isar.mvfr2; + t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */ + t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ + cpu->isar.mvfr2 = t; + + t = cpu->isar.id_mmfr3; + t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ + cpu->isar.id_mmfr3 = t; + + t = cpu->isar.id_mmfr4; + 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, EVT, 2); /* FEAT_EVT */ + cpu->isar.id_mmfr4 = t; + + t = cpu->isar.id_mmfr5; + t = FIELD_DP32(t, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ + cpu->isar.id_mmfr5 = t; + + t = cpu->isar.id_pfr0; + t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ + t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ + t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ + cpu->isar.id_pfr0 = t; + + t = cpu->isar.id_pfr2; + t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ + t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ + cpu->isar.id_pfr2 = t; + + t = cpu->isar.id_dfr0; + t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ + cpu->isar.id_dfr0 = t; + + /* Debug ID registers. */ + + /* Bit[15] is RES1, Bit[13] and Bits[11:0] are RES0. */ + t = 0x00008000; + t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, VERSION, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); + t = FIELD_DP32(t, DBGDIDR, BRPS, 5); + t = FIELD_DP32(t, DBGDIDR, WRPS, 3); + cpu->isar.dbgdidr = t; + + t = 0; + t = FIELD_DP32(t, DBGDEVID, PCSAMPLE, 3); + t = FIELD_DP32(t, DBGDEVID, WPADDRMASK, 1); + t = FIELD_DP32(t, DBGDEVID, BPADDRMASK, 15); + t = FIELD_DP32(t, DBGDEVID, VECTORCATCH, 0); + t = FIELD_DP32(t, DBGDEVID, VIRTEXTNS, 1); + t = FIELD_DP32(t, DBGDEVID, DOUBLELOCK, 1); + t = FIELD_DP32(t, DBGDEVID, AUXREGS, 0); + t = FIELD_DP32(t, DBGDEVID, CIDMASK, 0); + cpu->isar.dbgdevid = t; + + /* Bits[31:4] are RES0. */ + t = 0; + t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); + cpu->isar.dbgdevid1 = t; + + t = cpu->isar.id_dfr1; + t = FIELD_DP32(t, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ + cpu->isar.id_dfr1 = t; +} + +/* CPU models. These are not needed for the AArch64 linux-user build. */ +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) + +static void arm926_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm926"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); + cpu->midr = 0x41069265; + cpu->reset_fpsid = 0x41011090; + cpu->ctr = 0x1dd20d2; + cpu->reset_sctlr = 0x00090078; + + /* + * ARMv5 does not have the ID_ISAR registers, but we can still + * set the field to indicate Jazelle support within QEMU. + */ + cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + /* + * Similarly, we need to set MVFR0 fields to enable vfp and short vector + * support even though ARMv5 doesn't have this register. + */ + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); +} + +static void arm946_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm946"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_PMSA); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x41059461; + cpu->ctr = 0x0f004006; + cpu->reset_sctlr = 0x00000078; +} + +static void arm1026_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm1026"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_AUXCR); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); + cpu->midr = 0x4106a262; + cpu->reset_fpsid = 0x410110a0; + cpu->ctr = 0x1dd20d2; + cpu->reset_sctlr = 0x00090078; + cpu->reset_auxcr = 1; + + /* + * ARMv5 does not have the ID_ISAR registers, but we can still + * set the field to indicate Jazelle support within QEMU. + */ + cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + /* + * Similarly, we need to set MVFR0 fields to enable vfp and short vector + * support even though ARMv5 doesn't have this register. + */ + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSP, 1); + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPDP, 1); + + { + /* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */ + ARMCPRegInfo ifar = { + .name = "IFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, + .fieldoffset = offsetof(CPUARMState, cp15.ifar_ns), + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &ifar); + } +} + +static void arm1136_r2_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + /* + * What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an + * older core than plain "arm1136". In particular this does not + * have the v6K features. + * These ID register values are correct for 1136 but may be wrong + * for 1136_r2 (in particular r0p2 does not actually implement most + * of the ID registers). + */ + + cpu->dtb_compatible = "arm,arm1136"; + set_feature(&cpu->env, ARM_FEATURE_V6); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); + set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + cpu->midr = 0x4107b362; + cpu->reset_fpsid = 0x410120b4; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; + cpu->ctr = 0x1dd20d2; + cpu->reset_sctlr = 0x00050078; + cpu->isar.id_pfr0 = 0x111; + cpu->isar.id_pfr1 = 0x1; + cpu->isar.id_dfr0 = 0x2; + cpu->id_afr0 = 0x3; + cpu->isar.id_mmfr0 = 0x01130003; + cpu->isar.id_mmfr1 = 0x10030302; + cpu->isar.id_mmfr2 = 0x01222110; + cpu->isar.id_isar0 = 0x00140011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11231111; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x141; + cpu->reset_auxcr = 7; +} + +static void arm1136_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm1136"; + set_feature(&cpu->env, ARM_FEATURE_V6K); + set_feature(&cpu->env, ARM_FEATURE_V6); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); + set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + cpu->midr = 0x4117b363; + cpu->reset_fpsid = 0x410120b4; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; + cpu->ctr = 0x1dd20d2; + cpu->reset_sctlr = 0x00050078; + cpu->isar.id_pfr0 = 0x111; + cpu->isar.id_pfr1 = 0x1; + cpu->isar.id_dfr0 = 0x2; + cpu->id_afr0 = 0x3; + cpu->isar.id_mmfr0 = 0x01130003; + cpu->isar.id_mmfr1 = 0x10030302; + cpu->isar.id_mmfr2 = 0x01222110; + cpu->isar.id_isar0 = 0x00140011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11231111; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x141; + cpu->reset_auxcr = 7; +} + +static void arm1176_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm1176"; + set_feature(&cpu->env, ARM_FEATURE_V6K); + set_feature(&cpu->env, ARM_FEATURE_VAPA); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + set_feature(&cpu->env, ARM_FEATURE_CACHE_DIRTY_REG); + set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); + set_feature(&cpu->env, ARM_FEATURE_EL3); + cpu->midr = 0x410fb767; + cpu->reset_fpsid = 0x410120b5; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; + cpu->ctr = 0x1dd20d2; + cpu->reset_sctlr = 0x00050078; + cpu->isar.id_pfr0 = 0x111; + cpu->isar.id_pfr1 = 0x11; + cpu->isar.id_dfr0 = 0x33; + cpu->id_afr0 = 0; + cpu->isar.id_mmfr0 = 0x01130003; + cpu->isar.id_mmfr1 = 0x10030302; + cpu->isar.id_mmfr2 = 0x01222100; + cpu->isar.id_isar0 = 0x0140011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11231121; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x01141; + cpu->reset_auxcr = 7; +} + +static void arm11mpcore_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,arm11mpcore"; + set_feature(&cpu->env, ARM_FEATURE_V6K); + set_feature(&cpu->env, ARM_FEATURE_VAPA); + set_feature(&cpu->env, ARM_FEATURE_MPIDR); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x410fb022; + cpu->reset_fpsid = 0x410120b4; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; + cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ + cpu->isar.id_pfr0 = 0x111; + cpu->isar.id_pfr1 = 0x1; + cpu->isar.id_dfr0 = 0; + cpu->id_afr0 = 0x2; + cpu->isar.id_mmfr0 = 0x01100103; + cpu->isar.id_mmfr1 = 0x10020302; + cpu->isar.id_mmfr2 = 0x01222000; + cpu->isar.id_isar0 = 0x00100011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11221011; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x141; + cpu->reset_auxcr = 1; +} + +static const ARMCPRegInfo cortexa8_cp_reginfo[] = { + { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "L2AUXCR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void cortex_a8_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a8"; + set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_NEON); + 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; + cpu->isar.mvfr1 = 0x00011111; + cpu->ctr = 0x82048004; + cpu->reset_sctlr = 0x00c50078; + cpu->isar.id_pfr0 = 0x1031; + cpu->isar.id_pfr1 = 0x11; + cpu->isar.id_dfr0 = 0x400; + cpu->id_afr0 = 0; + cpu->isar.id_mmfr0 = 0x31100003; + cpu->isar.id_mmfr1 = 0x20000000; + cpu->isar.id_mmfr2 = 0x01202000; + cpu->isar.id_mmfr3 = 0x11; + cpu->isar.id_isar0 = 0x00101111; + cpu->isar.id_isar1 = 0x12112111; + cpu->isar.id_isar2 = 0x21232031; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x00111142; + cpu->isar.dbgdidr = 0x15141000; + cpu->clidr = (1 << 27) | (2 << 24) | 3; + cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ + cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ + cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */ + cpu->reset_auxcr = 2; + cpu->isar.reset_pmcr_el0 = 0x41002000; + define_arm_cp_regs(cpu, cortexa8_cp_reginfo); +} + +static const ARMCPRegInfo cortexa9_cp_reginfo[] = { + /* + * power_control should be set to maximum latency. Again, + * default to 0 and set by private hook + */ + { .name = "A9_PWRCTL", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_power_control) }, + { .name = "A9_DIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_diagnostic) }, + { .name = "A9_PWRDIAG", .cp = 15, .crn = 15, .crm = 0, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, + .fieldoffset = offsetof(CPUARMState, cp15.c15_power_diagnostic) }, + { .name = "NEONBUSY", .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + /* TLB lockdown control */ + { .name = "TLB_LOCKR", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 2, + .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, + { .name = "TLB_LOCKW", .cp = 15, .crn = 15, .crm = 4, .opc1 = 5, .opc2 = 4, + .access = PL1_W, .resetvalue = 0, .type = ARM_CP_NOP }, + { .name = "TLB_VA", .cp = 15, .crn = 15, .crm = 5, .opc1 = 5, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + { .name = "TLB_PA", .cp = 15, .crn = 15, .crm = 6, .opc1 = 5, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, + { .name = "TLB_ATTR", .cp = 15, .crn = 15, .crm = 7, .opc1 = 5, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .type = ARM_CP_CONST }, +}; + +static void cortex_a9_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a9"; + set_feature(&cpu->env, ARM_FEATURE_V7); + 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 + * and valid configurations; we don't model A9UP). + */ + set_feature(&cpu->env, ARM_FEATURE_V7MP); + set_feature(&cpu->env, ARM_FEATURE_CBAR); + cpu->midr = 0x410fc090; + cpu->reset_fpsid = 0x41033090; + cpu->isar.mvfr0 = 0x11110222; + cpu->isar.mvfr1 = 0x01111111; + cpu->ctr = 0x80038003; + cpu->reset_sctlr = 0x00c50078; + cpu->isar.id_pfr0 = 0x1031; + cpu->isar.id_pfr1 = 0x11; + cpu->isar.id_dfr0 = 0x000; + cpu->id_afr0 = 0; + cpu->isar.id_mmfr0 = 0x00100103; + cpu->isar.id_mmfr1 = 0x20000000; + cpu->isar.id_mmfr2 = 0x01230000; + cpu->isar.id_mmfr3 = 0x00002111; + cpu->isar.id_isar0 = 0x00101111; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232041; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x00111142; + cpu->isar.dbgdidr = 0x35141000; + cpu->clidr = (1 << 27) | (1 << 24) | 3; + cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ + cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */ + cpu->isar.reset_pmcr_el0 = 0x41093000; + define_arm_cp_regs(cpu, cortexa9_cp_reginfo); +} + +#ifndef CONFIG_USER_ONLY +static uint64_t a15_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + + /* + * Linux wants the number of processors from here. + * Might as well set the interrupt-controller bit too. + */ + return ((ms->smp.cpus - 1) << 24) | (1 << 23); +} +#endif + +static const ARMCPRegInfo cortexa15_cp_reginfo[] = { +#ifndef CONFIG_USER_ONLY + { .name = "L2CTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 2, + .access = PL1_RW, .resetvalue = 0, .readfn = a15_l2ctlr_read, + .writefn = arm_cp_write_ignore, }, +#endif + { .name = "L2ECTLR", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, +}; + +static void cortex_a7_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a7"; + set_feature(&cpu->env, ARM_FEATURE_V7VE); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + 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 = 0x410fc075; + cpu->reset_fpsid = 0x41023075; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x11111111; + cpu->ctr = 0x84448003; + cpu->reset_sctlr = 0x00c50078; + cpu->isar.id_pfr0 = 0x00001131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_dfr0 = 0x02010555; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_mmfr0 = 0x10101105; + cpu->isar.id_mmfr1 = 0x40000000; + cpu->isar.id_mmfr2 = 0x01240000; + cpu->isar.id_mmfr3 = 0x02102211; + /* + * a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but + * table 4-41 gives 0x02101110, which includes the arm div insns. + */ + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232041; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x10011142; + cpu->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 */ + cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ + cpu->isar.reset_pmcr_el0 = 0x41072000; + define_arm_cp_regs(cpu, cortexa15_cp_reginfo); /* Same as A15 */ +} + +static void cortex_a15_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a15"; + set_feature(&cpu->env, ARM_FEATURE_V7VE); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + 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); + /* 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; + cpu->ctr = 0x8444c004; + cpu->reset_sctlr = 0x00c50078; + cpu->isar.id_pfr0 = 0x00001131; + cpu->isar.id_pfr1 = 0x00011011; + cpu->isar.id_dfr0 = 0x02010555; + cpu->id_afr0 = 0x00000000; + cpu->isar.id_mmfr0 = 0x10201105; + cpu->isar.id_mmfr1 = 0x20000000; + cpu->isar.id_mmfr2 = 0x01240000; + cpu->isar.id_mmfr3 = 0x02102211; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232041; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x10011142; + cpu->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 */ + cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ + cpu->isar.reset_pmcr_el0 = 0x410F3000; + define_arm_cp_regs(cpu, cortexa15_cp_reginfo); +} + +static const ARMCPRegInfo cortexr5_cp_reginfo[] = { + /* Dummy the TCM region regs for the moment */ + { .name = "BTCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, + .access = PL1_RW, .type = ARM_CP_CONST }, + { .name = "ATCM", .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, + .access = PL1_RW, .type = ARM_CP_CONST }, + { .name = "DCACHE_INVAL", .cp = 15, .opc1 = 0, .crn = 15, .crm = 5, + .opc2 = 0, .access = PL1_W, .type = ARM_CP_NOP }, +}; + +static void cortex_r5_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_V7MP); + set_feature(&cpu->env, ARM_FEATURE_PMSA); + set_feature(&cpu->env, ARM_FEATURE_PMU); + cpu->midr = 0x411fc153; /* r1p3 */ + cpu->isar.id_pfr0 = 0x0131; + cpu->isar.id_pfr1 = 0x001; + cpu->isar.id_dfr0 = 0x010400; + cpu->id_afr0 = 0x0; + cpu->isar.id_mmfr0 = 0x0210030; + cpu->isar.id_mmfr1 = 0x00000000; + cpu->isar.id_mmfr2 = 0x01200000; + cpu->isar.id_mmfr3 = 0x0211; + cpu->isar.id_isar0 = 0x02101111; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232141; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x0010142; + cpu->isar.id_isar5 = 0x0; + cpu->isar.id_isar6 = 0x0; + cpu->mp_is_up = true; + cpu->pmsav7_dregion = 16; + cpu->isar.reset_pmcr_el0 = 0x41151800; + 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_BACKCOMPAT_CNTFRQ); + 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); + + cortex_r5_initfn(obj); + cpu->isar.mvfr0 = 0x10110221; + cpu->isar.mvfr1 = 0x00000011; +} + +static void ti925t_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + set_feature(&cpu->env, ARM_FEATURE_V4T); + set_feature(&cpu->env, ARM_FEATURE_OMAPCP); + cpu->midr = ARM_CPUID_TI925T; + cpu->ctr = 0x5109149; + cpu->reset_sctlr = 0x00000070; +} + +static void sa1100_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "intel,sa1100"; + set_feature(&cpu->env, ARM_FEATURE_STRONGARM); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x4401A11B; + cpu->reset_sctlr = 0x00000070; +} + +static void sa1110_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + set_feature(&cpu->env, ARM_FEATURE_STRONGARM); + set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); + cpu->midr = 0x6901B119; + cpu->reset_sctlr = 0x00000070; +} + +static void pxa250_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + cpu->midr = 0x69052100; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa255_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + cpu->midr = 0x69052d00; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa260_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + cpu->midr = 0x69052903; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa261_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + cpu->midr = 0x69052d05; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa262_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + cpu->midr = 0x69052d06; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa270a0_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + set_feature(&cpu->env, ARM_FEATURE_IWMMXT); + cpu->midr = 0x69054110; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa270a1_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + set_feature(&cpu->env, ARM_FEATURE_IWMMXT); + cpu->midr = 0x69054111; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa270b0_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + set_feature(&cpu->env, ARM_FEATURE_IWMMXT); + cpu->midr = 0x69054112; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa270b1_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + set_feature(&cpu->env, ARM_FEATURE_IWMMXT); + cpu->midr = 0x69054113; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa270c0_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + set_feature(&cpu->env, ARM_FEATURE_IWMMXT); + cpu->midr = 0x69054114; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +static void pxa270c5_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "marvell,xscale"; + set_feature(&cpu->env, ARM_FEATURE_V5); + set_feature(&cpu->env, ARM_FEATURE_XSCALE); + set_feature(&cpu->env, ARM_FEATURE_IWMMXT); + cpu->midr = 0x69054117; + cpu->ctr = 0xd172172; + cpu->reset_sctlr = 0x00000078; +} + +#ifndef TARGET_AARCH64 +/* + * -cpu max: a CPU with as many features enabled as our emulation supports. + * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c; + * this only needs to handle 32 bits, and need not care about KVM. + */ +static void arm_max_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + /* aarch64_a57_initfn, advertising none of the aarch64 features */ + 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_BACKCOMPAT_CNTFRQ); + 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 = 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.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 */ + define_cortex_a72_a57_a53_cp_reginfo(cpu); + + aa32_max_features(cpu); + +#ifdef CONFIG_USER_ONLY + /* + * Break with true ARMv8 and add back old-style VFP short-vector support. + * Only do this for user-mode, where -cpu max is the default, so that + * older v6 and v7 programs are more likely to work without adjustment. + */ + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); +#endif +} +#endif /* !TARGET_AARCH64 */ + +static const ARMCPUInfo arm_tcg_cpus[] = { + { .name = "arm926", .initfn = arm926_initfn }, + { .name = "arm946", .initfn = arm946_initfn }, + { .name = "arm1026", .initfn = arm1026_initfn }, + /* + * What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an + * older core than plain "arm1136". In particular this does not + * have the v6K features. + */ + { .name = "arm1136-r2", .initfn = arm1136_r2_initfn }, + { .name = "arm1136", .initfn = arm1136_initfn }, + { .name = "arm1176", .initfn = arm1176_initfn }, + { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, + { .name = "cortex-a7", .initfn = cortex_a7_initfn }, + { .name = "cortex-a8", .initfn = cortex_a8_initfn }, + { .name = "cortex-a9", .initfn = cortex_a9_initfn }, + { .name = "cortex-a15", .initfn = cortex_a15_initfn }, + { .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 }, + { .name = "pxa250", .initfn = pxa250_initfn }, + { .name = "pxa255", .initfn = pxa255_initfn }, + { .name = "pxa260", .initfn = pxa260_initfn }, + { .name = "pxa261", .initfn = pxa261_initfn }, + { .name = "pxa262", .initfn = pxa262_initfn }, + /* "pxa270" is an alias for "pxa270-a0" */ + { .name = "pxa270", .initfn = pxa270a0_initfn }, + { .name = "pxa270-a0", .initfn = pxa270a0_initfn }, + { .name = "pxa270-a1", .initfn = pxa270a1_initfn }, + { .name = "pxa270-b0", .initfn = pxa270b0_initfn }, + { .name = "pxa270-b1", .initfn = pxa270b1_initfn }, + { .name = "pxa270-c0", .initfn = pxa270c0_initfn }, + { .name = "pxa270-c5", .initfn = pxa270c5_initfn }, +#ifndef TARGET_AARCH64 + { .name = "max", .initfn = arm_max_initfn }, +#endif +#ifdef CONFIG_USER_ONLY + { .name = "any", .initfn = arm_max_initfn }, +#endif +}; + +static const TypeInfo idau_interface_type_info = { + .name = TYPE_IDAU_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(IDAUInterfaceClass), +}; + +static void arm_tcg_cpu_register_types(void) +{ + size_t i; + + type_register_static(&idau_interface_type_info); + for (i = 0; i < ARRAY_SIZE(arm_tcg_cpus); ++i) { + arm_cpu_register(&arm_tcg_cpus[i]); + } +} + +type_init(arm_tcg_cpu_register_types) + +#endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c new file mode 100644 index 0000000000..2963d7510f --- /dev/null +++ b/target/arm/tcg/cpu64.c @@ -0,0 +1,1320 @@ +/* + * 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 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_BACKCOMPAT_CNTFRQ); + 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 */ + /* 32KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); + /* 32KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 2); + /* 512KB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 16, 64, 512 * KiB, 7); + + /* 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_BACKCOMPAT_CNTFRQ); + 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 */ + /* 32KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); + /* 32KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 2); + /* 512KB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 16, 64, 512 * KiB, 7); + + /* 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_BACKCOMPAT_CNTFRQ); + 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; + /* 32KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); + /* 48KB L1 dcache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 3, 64, 48 * KiB, 2); + /* 1MB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 16, 64, 1 * MiB, 7); + 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_BACKCOMPAT_CNTFRQ); + 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 */ + /* 64KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 7); + /* 64KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 2); + /* 512KB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 8, 64, 512 * KiB, 7); + + /* 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_BACKCOMPAT_CNTFRQ); + 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; + /* 64KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 256, 64 * KiB, 7); + /* 64KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 256, 64 * KiB, 2); + /* 8MB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 16, 256, 8 * MiB, 7); + 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_BACKCOMPAT_CNTFRQ); + 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 */ + /* 64KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 7); + /* 64KB L1 icache */ + cpu->ccsidr[1] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 2); + /* 1MB L2 dcache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 8, 64, 1 * MiB, 7); + + /* 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_BACKCOMPAT_CNTFRQ); + 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 = 0x0011100001211032ull; + 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). + */ + /* 64KB L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_CCIDX, 4, 64, 64 * KiB, 0); + /* 64KB L1 icache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; + /* 1MB L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_CCIDX, 8, 64, 1 * MiB, 0); + + /* 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_BACKCOMPAT_CNTFRQ); + 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. + */ + /* L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_CCIDX, 4, 64, 64 * KiB, 0); + /* L1 icache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; + /* L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_CCIDX, 8, 64, 512 * KiB, 0); + + /* 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_BACKCOMPAT_CNTFRQ); + 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. + */ + /* L1 dcache */ + cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_CCIDX, 4, 64, 64 * KiB, 0); + /* L1 icache */ + cpu->ccsidr[1] = cpu->ccsidr[0]; + /* L2 cache */ + cpu->ccsidr[2] = make_ccsidr(CCSIDR_FORMAT_CCIDX, 8, 64, 512 * KiB, 0); + /* 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; + + /* + * Unset ARM_FEATURE_BACKCOMPAT_CNTFRQ, which we would otherwise default + * to because we started with aarch64_a57_initfn(). A 'max' CPU might + * be a v8.6-or-later one, in which case the cntfrq must be 1GHz; and + * because it is our "may change" CPU type we are OK with it not being + * backwards-compatible with how it worked in old QEMU. + */ + unset_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); + + /* + * 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, 2); /* FEAT_BF16, FEAT_EBF16 */ + 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 */ + t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ + 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, 3); /* FEAT_CSV2_3 */ + 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_3 */ + t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ + 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, 2); /* FEAT_ETS2 */ + t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ + t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ + t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* FEAT_CMOW */ + 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_aa64mmfr3; + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + cpu->isar.id_aa64mmfr3 = 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, 2); /* FEAT_BF16, FEAT_EBF16 */ + 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, 10); /* FEAT_Debugv8p8 */ + 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/tcg/crypto_helper.c b/target/arm/tcg/crypto_helper.c new file mode 100644 index 0000000000..7cadd61e12 --- /dev/null +++ b/target/arm/tcg/crypto_helper.c @@ -0,0 +1,679 @@ +/* + * crypto_helper.c - emulate v8 Crypto Extensions instructions + * + * Copyright (C) 2013 - 2018 Linaro Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" +#include "crypto/aes-round.h" +#include "crypto/sm4.h" +#include "vec_internal.h" + +union CRYPTO_STATE { + uint8_t bytes[16]; + uint32_t words[4]; + uint64_t l[2]; +}; + +#if HOST_BIG_ENDIAN +#define CR_ST_BYTE(state, i) ((state).bytes[(15 - (i)) ^ 8]) +#define CR_ST_WORD(state, i) ((state).words[(3 - (i)) ^ 2]) +#else +#define CR_ST_BYTE(state, i) ((state).bytes[i]) +#define CR_ST_WORD(state, i) ((state).words[i]) +#endif + +/* + * The caller has not been converted to full gvec, and so only + * modifies the low 16 bytes of the vector register. + */ +static void clear_tail_16(void *vd, uint32_t desc) +{ + int opr_sz = simd_oprsz(desc); + int max_sz = simd_maxsz(desc); + + assert(opr_sz == 16); + clear_tail(vd, opr_sz, max_sz); +} + +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); + + 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; + + /* + * 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)); +} + +void HELPER(crypto_aesd)(void *vd, void *vn, 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 *)(vn + i); + AESState *rk = (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] ^ 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); + } + } + 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); + + 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]; + 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)); +} + +/* + * SHA-1 logical functions + */ + +static uint32_t cho(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & (y ^ z)) ^ z; +} + +static uint32_t par(uint32_t x, uint32_t y, uint32_t z) +{ + return x ^ y ^ z; +} + +static uint32_t maj(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) | ((x | y) & z); +} + +void HELPER(crypto_sha1su0)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t d0, d1; + + d0 = d[1] ^ d[0] ^ m[0]; + d1 = n[0] ^ d[1] ^ m[1]; + d[0] = d0; + d[1] = d1; + + clear_tail_16(vd, desc); +} + +static inline void crypto_sha1_3reg(uint64_t *rd, uint64_t *rn, + uint64_t *rm, uint32_t desc, + uint32_t (*fn)(union CRYPTO_STATE *d)) +{ + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + int i; + + for (i = 0; i < 4; i++) { + uint32_t t = fn(&d); + + t += rol32(CR_ST_WORD(d, 0), 5) + CR_ST_WORD(n, 0) + + CR_ST_WORD(m, i); + + CR_ST_WORD(n, 0) = CR_ST_WORD(d, 3); + CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); + CR_ST_WORD(d, 2) = ror32(CR_ST_WORD(d, 1), 2); + CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); + CR_ST_WORD(d, 0) = t; + } + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(rd, desc); +} + +static uint32_t do_sha1c(union CRYPTO_STATE *d) +{ + return cho(CR_ST_WORD(*d, 1), CR_ST_WORD(*d, 2), CR_ST_WORD(*d, 3)); +} + +void HELPER(crypto_sha1c)(void *vd, void *vn, void *vm, uint32_t desc) +{ + crypto_sha1_3reg(vd, vn, vm, desc, do_sha1c); +} + +static uint32_t do_sha1p(union CRYPTO_STATE *d) +{ + return par(CR_ST_WORD(*d, 1), CR_ST_WORD(*d, 2), CR_ST_WORD(*d, 3)); +} + +void HELPER(crypto_sha1p)(void *vd, void *vn, void *vm, uint32_t desc) +{ + crypto_sha1_3reg(vd, vn, vm, desc, do_sha1p); +} + +static uint32_t do_sha1m(union CRYPTO_STATE *d) +{ + return maj(CR_ST_WORD(*d, 1), CR_ST_WORD(*d, 2), CR_ST_WORD(*d, 3)); +} + +void HELPER(crypto_sha1m)(void *vd, void *vn, void *vm, uint32_t desc) +{ + crypto_sha1_3reg(vd, vn, vm, desc, do_sha1m); +} + +void HELPER(crypto_sha1h)(void *vd, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rm = vm; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + + CR_ST_WORD(m, 0) = ror32(CR_ST_WORD(m, 0), 2); + CR_ST_WORD(m, 1) = CR_ST_WORD(m, 2) = CR_ST_WORD(m, 3) = 0; + + rd[0] = m.l[0]; + rd[1] = m.l[1]; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha1su1)(void *vd, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + + CR_ST_WORD(d, 0) = rol32(CR_ST_WORD(d, 0) ^ CR_ST_WORD(m, 1), 1); + CR_ST_WORD(d, 1) = rol32(CR_ST_WORD(d, 1) ^ CR_ST_WORD(m, 2), 1); + CR_ST_WORD(d, 2) = rol32(CR_ST_WORD(d, 2) ^ CR_ST_WORD(m, 3), 1); + CR_ST_WORD(d, 3) = rol32(CR_ST_WORD(d, 3) ^ CR_ST_WORD(d, 0), 1); + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +/* + * The SHA-256 logical functions, according to + * http://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf + */ + +static uint32_t S0(uint32_t x) +{ + return ror32(x, 2) ^ ror32(x, 13) ^ ror32(x, 22); +} + +static uint32_t S1(uint32_t x) +{ + return ror32(x, 6) ^ ror32(x, 11) ^ ror32(x, 25); +} + +static uint32_t s0(uint32_t x) +{ + return ror32(x, 7) ^ ror32(x, 18) ^ (x >> 3); +} + +static uint32_t s1(uint32_t x) +{ + return ror32(x, 17) ^ ror32(x, 19) ^ (x >> 10); +} + +void HELPER(crypto_sha256h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + int i; + + for (i = 0; i < 4; i++) { + uint32_t t = cho(CR_ST_WORD(n, 0), CR_ST_WORD(n, 1), CR_ST_WORD(n, 2)) + + CR_ST_WORD(n, 3) + S1(CR_ST_WORD(n, 0)) + + CR_ST_WORD(m, i); + + CR_ST_WORD(n, 3) = CR_ST_WORD(n, 2); + CR_ST_WORD(n, 2) = CR_ST_WORD(n, 1); + CR_ST_WORD(n, 1) = CR_ST_WORD(n, 0); + CR_ST_WORD(n, 0) = CR_ST_WORD(d, 3) + t; + + t += maj(CR_ST_WORD(d, 0), CR_ST_WORD(d, 1), CR_ST_WORD(d, 2)) + + S0(CR_ST_WORD(d, 0)); + + CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); + CR_ST_WORD(d, 2) = CR_ST_WORD(d, 1); + CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); + CR_ST_WORD(d, 0) = t; + } + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha256h2)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + int i; + + for (i = 0; i < 4; i++) { + uint32_t t = cho(CR_ST_WORD(d, 0), CR_ST_WORD(d, 1), CR_ST_WORD(d, 2)) + + CR_ST_WORD(d, 3) + S1(CR_ST_WORD(d, 0)) + + CR_ST_WORD(m, i); + + CR_ST_WORD(d, 3) = CR_ST_WORD(d, 2); + CR_ST_WORD(d, 2) = CR_ST_WORD(d, 1); + CR_ST_WORD(d, 1) = CR_ST_WORD(d, 0); + CR_ST_WORD(d, 0) = CR_ST_WORD(n, 3 - i) + t; + } + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha256su0)(void *vd, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + + CR_ST_WORD(d, 0) += s0(CR_ST_WORD(d, 1)); + CR_ST_WORD(d, 1) += s0(CR_ST_WORD(d, 2)); + CR_ST_WORD(d, 2) += s0(CR_ST_WORD(d, 3)); + CR_ST_WORD(d, 3) += s0(CR_ST_WORD(m, 0)); + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha256su1)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + + CR_ST_WORD(d, 0) += s1(CR_ST_WORD(m, 2)) + CR_ST_WORD(n, 1); + CR_ST_WORD(d, 1) += s1(CR_ST_WORD(m, 3)) + CR_ST_WORD(n, 2); + CR_ST_WORD(d, 2) += s1(CR_ST_WORD(d, 0)) + CR_ST_WORD(n, 3); + CR_ST_WORD(d, 3) += s1(CR_ST_WORD(d, 1)) + CR_ST_WORD(m, 0); + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +/* + * The SHA-512 logical functions (same as above but using 64-bit operands) + */ + +static uint64_t cho512(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & (y ^ z)) ^ z; +} + +static uint64_t maj512(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & y) | ((x | y) & z); +} + +static uint64_t S0_512(uint64_t x) +{ + return ror64(x, 28) ^ ror64(x, 34) ^ ror64(x, 39); +} + +static uint64_t S1_512(uint64_t x) +{ + return ror64(x, 14) ^ ror64(x, 18) ^ ror64(x, 41); +} + +static uint64_t s0_512(uint64_t x) +{ + return ror64(x, 1) ^ ror64(x, 8) ^ (x >> 7); +} + +static uint64_t s1_512(uint64_t x) +{ + return ror64(x, 19) ^ ror64(x, 61) ^ (x >> 6); +} + +void HELPER(crypto_sha512h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + uint64_t d0 = rd[0]; + uint64_t d1 = rd[1]; + + d1 += S1_512(rm[1]) + cho512(rm[1], rn[0], rn[1]); + d0 += S1_512(d1 + rm[0]) + cho512(d1 + rm[0], rm[1], rn[0]); + + rd[0] = d0; + rd[1] = d1; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha512h2)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + uint64_t d0 = rd[0]; + uint64_t d1 = rd[1]; + + d1 += S0_512(rm[0]) + maj512(rn[0], rm[1], rm[0]); + d0 += S0_512(d1) + maj512(d1, rm[0], rm[1]); + + rd[0] = d0; + rd[1] = d1; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha512su0)(void *vd, void *vn, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t d0 = rd[0]; + uint64_t d1 = rd[1]; + + d0 += s0_512(rd[1]); + d1 += s0_512(rn[0]); + + rd[0] = d0; + rd[1] = d1; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sha512su1)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + + rd[0] += s1_512(rn[0]) + rm[0]; + rd[1] += s1_512(rn[1]) + rm[1]; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sm3partw1)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + uint32_t t; + + t = CR_ST_WORD(d, 0) ^ CR_ST_WORD(n, 0) ^ ror32(CR_ST_WORD(m, 1), 17); + CR_ST_WORD(d, 0) = t ^ ror32(t, 17) ^ ror32(t, 9); + + t = CR_ST_WORD(d, 1) ^ CR_ST_WORD(n, 1) ^ ror32(CR_ST_WORD(m, 2), 17); + CR_ST_WORD(d, 1) = t ^ ror32(t, 17) ^ ror32(t, 9); + + t = CR_ST_WORD(d, 2) ^ CR_ST_WORD(n, 2) ^ ror32(CR_ST_WORD(m, 3), 17); + CR_ST_WORD(d, 2) = t ^ ror32(t, 17) ^ ror32(t, 9); + + t = CR_ST_WORD(d, 3) ^ CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(d, 0), 17); + CR_ST_WORD(d, 3) = t ^ ror32(t, 17) ^ ror32(t, 9); + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +void HELPER(crypto_sm3partw2)(void *vd, void *vn, void *vm, uint32_t desc) +{ + uint64_t *rd = vd; + uint64_t *rn = vn; + uint64_t *rm = vm; + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + uint32_t t = CR_ST_WORD(n, 0) ^ ror32(CR_ST_WORD(m, 0), 25); + + CR_ST_WORD(d, 0) ^= t; + CR_ST_WORD(d, 1) ^= CR_ST_WORD(n, 1) ^ ror32(CR_ST_WORD(m, 1), 25); + CR_ST_WORD(d, 2) ^= CR_ST_WORD(n, 2) ^ ror32(CR_ST_WORD(m, 2), 25); + CR_ST_WORD(d, 3) ^= CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(m, 3), 25) ^ + ror32(t, 17) ^ ror32(t, 2) ^ ror32(t, 26); + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(vd, desc); +} + +static inline void QEMU_ALWAYS_INLINE +crypto_sm3tt(uint64_t *rd, uint64_t *rn, uint64_t *rm, + uint32_t desc, uint32_t opcode) +{ + union CRYPTO_STATE d = { .l = { rd[0], rd[1] } }; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + uint32_t imm2 = simd_data(desc); + uint32_t t; + + assert(imm2 < 4); + + if (opcode == 0 || opcode == 2) { + /* SM3TT1A, SM3TT2A */ + t = par(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1)); + } else if (opcode == 1) { + /* SM3TT1B */ + t = maj(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1)); + } else if (opcode == 3) { + /* SM3TT2B */ + t = cho(CR_ST_WORD(d, 3), CR_ST_WORD(d, 2), CR_ST_WORD(d, 1)); + } else { + qemu_build_not_reached(); + } + + t += CR_ST_WORD(d, 0) + CR_ST_WORD(m, imm2); + + CR_ST_WORD(d, 0) = CR_ST_WORD(d, 1); + + if (opcode < 2) { + /* SM3TT1A, SM3TT1B */ + t += CR_ST_WORD(n, 3) ^ ror32(CR_ST_WORD(d, 3), 20); + + CR_ST_WORD(d, 1) = ror32(CR_ST_WORD(d, 2), 23); + } else { + /* SM3TT2A, SM3TT2B */ + t += CR_ST_WORD(n, 3); + t ^= rol32(t, 9) ^ rol32(t, 17); + + CR_ST_WORD(d, 1) = ror32(CR_ST_WORD(d, 2), 13); + } + + CR_ST_WORD(d, 2) = CR_ST_WORD(d, 3); + CR_ST_WORD(d, 3) = t; + + rd[0] = d.l[0]; + rd[1] = d.l[1]; + + clear_tail_16(rd, desc); +} + +#define DO_SM3TT(NAME, OPCODE) \ + void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ + { crypto_sm3tt(vd, vn, vm, desc, OPCODE); } + +DO_SM3TT(crypto_sm3tt1a, 0) +DO_SM3TT(crypto_sm3tt1b, 1) +DO_SM3TT(crypto_sm3tt2a, 2) +DO_SM3TT(crypto_sm3tt2b, 3) + +#undef DO_SM3TT + +static void do_crypto_sm4e(uint64_t *rd, uint64_t *rn, uint64_t *rm) +{ + union CRYPTO_STATE d = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE n = { .l = { rm[0], rm[1] } }; + uint32_t t, i; + + for (i = 0; i < 4; i++) { + t = CR_ST_WORD(d, (i + 1) % 4) ^ + CR_ST_WORD(d, (i + 2) % 4) ^ + CR_ST_WORD(d, (i + 3) % 4) ^ + CR_ST_WORD(n, i); + + t = sm4_subword(t); + + CR_ST_WORD(d, i) ^= t ^ rol32(t, 2) ^ rol32(t, 10) ^ rol32(t, 18) ^ + rol32(t, 24); + } + + rd[0] = d.l[0]; + rd[1] = d.l[1]; +} + +void HELPER(crypto_sm4e)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + + for (i = 0; i < opr_sz; i += 16) { + do_crypto_sm4e(vd + i, vn + i, vm + i); + } + clear_tail(vd, opr_sz, simd_maxsz(desc)); +} + +static void do_crypto_sm4ekey(uint64_t *rd, uint64_t *rn, uint64_t *rm) +{ + union CRYPTO_STATE d; + union CRYPTO_STATE n = { .l = { rn[0], rn[1] } }; + union CRYPTO_STATE m = { .l = { rm[0], rm[1] } }; + uint32_t t, i; + + d = n; + for (i = 0; i < 4; i++) { + t = CR_ST_WORD(d, (i + 1) % 4) ^ + CR_ST_WORD(d, (i + 2) % 4) ^ + CR_ST_WORD(d, (i + 3) % 4) ^ + CR_ST_WORD(m, i); + + t = sm4_subword(t); + + CR_ST_WORD(d, i) ^= t ^ rol32(t, 13) ^ rol32(t, 23); + } + + rd[0] = d.l[0]; + rd[1] = d.l[1]; +} + +void HELPER(crypto_sm4ekey)(void *vd, void *vn, void* vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + + for (i = 0; i < opr_sz; i += 16) { + do_crypto_sm4ekey(vd + i, vn + i, vm + i); + } + clear_tail(vd, opr_sz, simd_maxsz(desc)); +} + +void HELPER(crypto_rax1)(void *vd, void *vn, void *vm, uint32_t 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) { + d[i] = n[i] ^ rol64(m[i], 1); + } + clear_tail(vd, opr_sz, simd_maxsz(desc)); +} diff --git a/target/arm/tcg/gengvec.c b/target/arm/tcg/gengvec.c new file mode 100644 index 0000000000..f652520b65 --- /dev/null +++ b/target/arm/tcg/gengvec.c @@ -0,0 +1,2360 @@ +/* + * ARM generic vector expansion + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2005-2007 CodeSourcery + * Copyright (c) 2007 OpenedHand, 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" + + +static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz, + gen_helper_gvec_3_ptr *fn) +{ + TCGv_ptr qc_ptr = tcg_temp_new_ptr(); + + tcg_debug_assert(opr_sz <= sizeof_field(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); +} + +void gen_gvec_sqdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_neon_sqdmulh_h, gen_helper_neon_sqdmulh_s + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +void gen_gvec_sqrdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_neon_sqrdmulh_h, gen_helper_neon_sqrdmulh_s + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_gvec_qrdmlah_s16, gen_helper_gvec_qrdmlah_s32 + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_gvec_qrdmlsh_s16, gen_helper_gvec_qrdmlsh_s32 + }; + tcg_debug_assert(vece >= 1 && vece <= 2); + gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); +} + +#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); } + +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 + +void gen_gvec_sshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + /* Signed shift out of range results in all-sign-bits */ + shift = MIN(shift, (8 << vece) - 1); + tcg_gen_gvec_sari(vece, rd_ofs, rm_ofs, shift, opr_sz, max_sz); +} + +void gen_gvec_ushr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + /* Unsigned shift out of range results in all-zero-bits */ + if (shift >= (8 << vece)) { + tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0); + } else { + tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift, opr_sz, max_sz); + } +} + +static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_sar8i_i64(a, a, shift); + tcg_gen_vec_add8_i64(d, d, a); +} + +static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_sar16i_i64(a, a, shift); + tcg_gen_vec_add16_i64(d, d, a); +} + +static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_sari_i32(a, a, shift); + tcg_gen_add_i32(d, d, a); +} + +static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_sari_i64(a, a, shift); + tcg_gen_add_i64(d, d, a); +} + +static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + tcg_gen_sari_vec(vece, a, a, sh); + tcg_gen_add_vec(vece, d, d, a); +} + +void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_ssra8_i64, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_ssra16_i64, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_ssra32_i32, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_ssra64_i64, + .fniv = gen_ssra_vec, + .fno = gen_helper_gvec_ssra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize]. */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* + * Shifts larger than the element size are architecturally valid. + * Signed results in all sign bits. + */ + shift = MIN(shift, (8 << vece) - 1); + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); +} + +static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_shr8i_i64(a, a, shift); + tcg_gen_vec_add8_i64(d, d, a); +} + +static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_shr16i_i64(a, a, shift); + tcg_gen_vec_add16_i64(d, d, a); +} + +static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_shri_i32(a, a, shift); + tcg_gen_add_i32(d, d, a); +} + +static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_shri_i64(a, a, shift); + tcg_gen_add_i64(d, d, a); +} + +static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + tcg_gen_shri_vec(vece, a, a, sh); + tcg_gen_add_vec(vece, d, d, a); +} + +void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_usra8_i64, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8, }, + { .fni8 = gen_usra16_i64, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16, }, + { .fni4 = gen_usra32_i32, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32, }, + { .fni8 = gen_usra64_i64, + .fniv = gen_usra_vec, + .fno = gen_helper_gvec_usra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64, }, + }; + + /* tszimm encoding produces immediates in the range [1..esize]. */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* + * Shifts larger than the element size are architecturally valid. + * Unsigned results in all zeros as input to accumulate: nop. + */ + if (shift < (8 << vece)) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } else { + /* Nop, but we do need to clear the tail. */ + tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); + } +} + +/* + * Shift one less than the requested amount, and the low bit is + * the rounding bit. For the 8 and 16-bit operations, because we + * mask the low bit, we can perform a normal integer shift instead + * of a vector shift. + */ +static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + 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); +} + +static void gen_srshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + 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); +} + +void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t; + + /* Handle shift by the input size for the benefit of trans_SRSHR_ri */ + if (sh == 32) { + tcg_gen_movi_i32(d, 0); + return; + } + t = tcg_temp_new_i32(); + tcg_gen_extract_i32(t, a, sh - 1, 1); + tcg_gen_sari_i32(d, a, sh); + tcg_gen_add_i32(d, d, t); +} + +void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t, a, sh - 1, 1); + tcg_gen_sari_i64(d, a, sh); + tcg_gen_add_i64(d, d, t); +} + +static void gen_srshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec ones = tcg_constant_vec_matching(d, vece, 1); + + tcg_gen_shri_vec(vece, t, a, sh - 1); + tcg_gen_and_vec(vece, t, t, ones); + tcg_gen_sari_vec(vece, d, a, sh); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_srshr8_i64, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_srshr16_i64, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_srshr32_i32, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_srshr64_i64, + .fniv = gen_srshr_vec, + .fno = gen_helper_gvec_srshr_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + if (shift == (8 << vece)) { + /* + * Shifts larger than the element size are architecturally valid. + * Signed results in all sign bits. With rounding, this produces + * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. + * I.e. always zero. + */ + tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_srshr8_i64(t, a, sh); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_srsra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_srshr16_i64(t, a, sh); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_srsra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + gen_srshr32_i32(t, a, sh); + tcg_gen_add_i32(d, d, t); +} + +static void gen_srsra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + gen_srshr64_i64(t, a, sh); + tcg_gen_add_i64(d, d, t); +} + +static void gen_srsra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + gen_srshr_vec(vece, t, a, sh); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_srsra8_i64, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fni8 = gen_srsra16_i64, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_srsra32_i32, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_srsra64_i64, + .fniv = gen_srsra_vec, + .fno = gen_helper_gvec_srsra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* + * Shifts larger than the element size are architecturally valid. + * Signed results in all sign bits. With rounding, this produces + * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. + * I.e. always zero. With accumulation, this leaves D unchanged. + */ + if (shift == (8 << vece)) { + /* Nop, but we do need to clear the tail. */ + tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + 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); +} + +static void gen_urshr16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, sh - 1); + 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); +} + +void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t; + + /* Handle shift by the input size for the benefit of trans_URSHR_ri */ + if (sh == 32) { + tcg_gen_extract_i32(d, a, sh - 1, 1); + return; + } + t = tcg_temp_new_i32(); + tcg_gen_extract_i32(t, a, sh - 1, 1); + tcg_gen_shri_i32(d, a, sh); + tcg_gen_add_i32(d, d, t); +} + +void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_extract_i64(t, a, sh - 1, 1); + tcg_gen_shri_i64(d, a, sh); + tcg_gen_add_i64(d, d, t); +} + +static void gen_urshr_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t shift) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec ones = tcg_constant_vec_matching(d, vece, 1); + + tcg_gen_shri_vec(vece, t, a, shift - 1); + tcg_gen_and_vec(vece, t, t, ones); + tcg_gen_shri_vec(vece, d, a, shift); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_urshr8_i64, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_urshr16_i64, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_urshr32_i32, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_urshr64_i64, + .fniv = gen_urshr_vec, + .fno = gen_helper_gvec_urshr_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + if (shift == (8 << vece)) { + /* + * Shifts larger than the element size are architecturally valid. + * Unsigned results in zero. With rounding, this produces a + * copy of the most significant bit. + */ + tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift - 1, opr_sz, max_sz); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + if (sh == 8) { + tcg_gen_vec_shr8i_i64(t, a, 7); + } else { + gen_urshr8_i64(t, a, sh); + } + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_ursra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + if (sh == 16) { + tcg_gen_vec_shr16i_i64(t, a, 15); + } else { + gen_urshr16_i64(t, a, sh); + } + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_ursra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + if (sh == 32) { + tcg_gen_shri_i32(t, a, 31); + } else { + gen_urshr32_i32(t, a, sh); + } + tcg_gen_add_i32(d, d, t); +} + +static void gen_ursra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + if (sh == 64) { + tcg_gen_shri_i64(t, a, 63); + } else { + gen_urshr64_i64(t, a, sh); + } + tcg_gen_add_i64(d, d, t); +} + +static void gen_ursra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + if (sh == (8 << vece)) { + tcg_gen_shri_vec(vece, t, a, sh - 1); + } else { + gen_urshr_vec(vece, t, a, sh); + } + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen2i ops[4] = { + { .fni8 = gen_ursra8_i64, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fni8 = gen_ursra16_i64, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_ursra32_i32, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_ursra64_i64, + .fniv = gen_ursra_vec, + .fno = gen_helper_gvec_ursra_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize] */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); +} + +static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_8, 0xff >> shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_16, 0xffff >> shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_shri_i32(a, a, shift); + tcg_gen_deposit_i32(d, d, a, 0, 32 - shift); +} + +static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_shri_i64(a, a, shift); + tcg_gen_deposit_i64(d, d, a, 0, 64 - shift); +} + +static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + int64_t mi = MAKE_64BIT_MASK((8 << vece) - sh, sh); + TCGv_vec m = tcg_constant_vec_matching(d, vece, mi); + + tcg_gen_shri_vec(vece, t, a, sh); + tcg_gen_and_vec(vece, d, d, m); + tcg_gen_or_vec(vece, d, d, t); +} + +void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_shri_vec, 0 }; + const GVecGen2i ops[4] = { + { .fni8 = gen_shr8_ins_i64, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shr16_ins_i64, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shr32_ins_i32, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_shr64_ins_i64, + .fniv = gen_shr_ins_vec, + .fno = gen_helper_gvec_sri_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [1..esize]. */ + tcg_debug_assert(shift > 0); + tcg_debug_assert(shift <= (8 << vece)); + + /* Shift of esize leaves destination unchanged. */ + if (shift < (8 << vece)) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } else { + /* Nop, but we do need to clear the tail. */ + tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); + } +} + +static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_8, 0xff << shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_16, 0xffff << shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_deposit_i32(d, d, a, shift, 32 - shift); +} + +static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_deposit_i64(d, d, a, shift, 64 - shift); +} + +static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_constant_vec_matching(d, vece, MAKE_64BIT_MASK(0, sh)); + + tcg_gen_shli_vec(vece, t, a, sh); + tcg_gen_and_vec(vece, d, d, m); + tcg_gen_or_vec(vece, d, d, t); +} + +void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; + const GVecGen2i ops[4] = { + { .fni8 = gen_shl8_ins_i64, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_b, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shl16_ins_i64, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_h, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shl32_ins_i32, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_s, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_shl64_ins_i64, + .fniv = gen_shl_ins_vec, + .fno = gen_helper_gvec_sli_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + /* tszimm encoding produces immediates in the range [0..esize-1]. */ + tcg_debug_assert(shift >= 0); + tcg_debug_assert(shift < (8 << vece)); + + if (shift == 0) { + tcg_gen_gvec_mov(vece, rd_ofs, rm_ofs, opr_sz, max_sz); + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); + } +} + +static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u8(a, a, b); + gen_helper_neon_add_u8(d, d, a); +} + +static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u8(a, a, b); + gen_helper_neon_sub_u8(d, d, a); +} + +static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u16(a, a, b); + gen_helper_neon_add_u16(d, d, a); +} + +static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u16(a, a, b); + gen_helper_neon_sub_u16(d, d, a); +} + +static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mul_i32(a, a, b); + tcg_gen_add_i32(d, d, a); +} + +static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mul_i32(a, a, b); + tcg_gen_sub_i32(d, d, a); +} + +static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mul_i64(a, a, b); + tcg_gen_add_i64(d, d, a); +} + +static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mul_i64(a, a, b); + tcg_gen_sub_i64(d, d, a); +} + +static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_mul_vec(vece, a, a, b); + tcg_gen_add_vec(vece, d, d, a); +} + +static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_mul_vec(vece, a, a, b); + tcg_gen_sub_vec(vece, d, d, a); +} + +/* Note that while NEON does not support VMLA and VMLS as 64-bit ops, + * these tables are shared with AArch64 which does support them. + */ +void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fni4 = gen_mla8_i32, + .fniv = gen_mla_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni4 = gen_mla16_i32, + .fniv = gen_mla_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_mla32_i32, + .fniv = gen_mla_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_mla64_i64, + .fniv = gen_mla_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_mul_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fni4 = gen_mls8_i32, + .fniv = gen_mls_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni4 = gen_mls16_i32, + .fniv = gen_mls_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_mls32_i32, + .fniv = gen_mls_vec, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_mls64_i64, + .fniv = gen_mls_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +/* CMTST : test is "if (X & Y != 0)". */ +static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_negsetcond_i32(TCG_COND_TSTNE, d, a, b); +} + +void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_negsetcond_i64(TCG_COND_TSTNE, d, a, b); +} + +static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_cmp_vec(TCG_COND_TSTNE, vece, d, a, b); +} + +void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_cmp_vec, 0 }; + static const GVecGen3 ops[4] = { + { .fni4 = gen_helper_neon_tst_u8, + .fniv = gen_cmtst_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_tst_u16, + .fniv = gen_cmtst_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_cmtst_i32, + .fniv = gen_cmtst_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_cmtst_i64, + .fniv = gen_cmtst_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) +{ + TCGv_i32 lval = tcg_temp_new_i32(); + TCGv_i32 rval = tcg_temp_new_i32(); + TCGv_i32 lsh = tcg_temp_new_i32(); + TCGv_i32 rsh = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 max = tcg_constant_i32(32); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i32(lsh, shift); + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_shl_i32(lval, src, lsh); + tcg_gen_shr_i32(rval, src, rsh); + tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero); + tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst); +} + +void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) +{ + TCGv_i64 lval = tcg_temp_new_i64(); + TCGv_i64 rval = tcg_temp_new_i64(); + TCGv_i64 lsh = tcg_temp_new_i64(); + TCGv_i64 rsh = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 max = tcg_constant_i64(64); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i64(lsh, shift); + tcg_gen_neg_i64(rsh, lsh); + tcg_gen_shl_i64(lval, src, lsh); + tcg_gen_shr_i64(rval, src, rsh); + tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero); + tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst); +} + +static void gen_ushl_vec(unsigned vece, TCGv_vec dst, + TCGv_vec src, TCGv_vec shift) +{ + TCGv_vec lval = tcg_temp_new_vec_matching(dst); + TCGv_vec rval = tcg_temp_new_vec_matching(dst); + TCGv_vec lsh = tcg_temp_new_vec_matching(dst); + TCGv_vec rsh = tcg_temp_new_vec_matching(dst); + TCGv_vec max, zero; + + tcg_gen_neg_vec(vece, rsh, shift); + if (vece == MO_8) { + tcg_gen_mov_vec(lsh, shift); + } else { + TCGv_vec msk = tcg_constant_vec_matching(dst, vece, 0xff); + tcg_gen_and_vec(vece, lsh, shift, msk); + tcg_gen_and_vec(vece, rsh, rsh, msk); + } + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_shlv_vec(vece, lval, src, lsh); + tcg_gen_shrv_vec(vece, rval, src, rsh); + + /* + * The choice of GE (signed) and GEU (unsigned) are biased toward + * the instructions of the x86_64 host. For MO_8, the whole byte + * is significant so we must use an unsigned compare; otherwise we + * have already masked to a byte and so a signed compare works. + * Other tcg hosts have a full set of comparisons and do not care. + */ + zero = tcg_constant_vec_matching(dst, vece, 0); + max = tcg_constant_vec_matching(dst, vece, 8 << vece); + if (vece == MO_8) { + tcg_gen_cmpsel_vec(TCG_COND_GEU, vece, lval, lsh, max, zero, lval); + tcg_gen_cmpsel_vec(TCG_COND_GEU, vece, rval, rsh, max, zero, rval); + } else { + tcg_gen_cmpsel_vec(TCG_COND_GE, vece, lval, lsh, max, zero, lval); + tcg_gen_cmpsel_vec(TCG_COND_GE, vece, rval, rsh, max, zero, rval); + } + tcg_gen_or_vec(vece, dst, lval, rval); +} + +void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_shlv_vec, + INDEX_op_shrv_vec, INDEX_op_cmpsel_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_ushl_vec, + .fno = gen_helper_gvec_ushl_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_ushl_vec, + .fno = gen_helper_gvec_ushl_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_ushl_i32, + .fniv = gen_ushl_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_ushl_i64, + .fniv = gen_ushl_vec, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) +{ + TCGv_i32 lval = tcg_temp_new_i32(); + TCGv_i32 rval = tcg_temp_new_i32(); + TCGv_i32 lsh = tcg_temp_new_i32(); + TCGv_i32 rsh = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 max = tcg_constant_i32(31); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i32(lsh, shift); + tcg_gen_neg_i32(rsh, lsh); + tcg_gen_shl_i32(lval, src, lsh); + tcg_gen_umin_i32(rsh, rsh, max); + tcg_gen_sar_i32(rval, src, rsh); + tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero); + tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval); +} + +void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) +{ + TCGv_i64 lval = tcg_temp_new_i64(); + TCGv_i64 rval = tcg_temp_new_i64(); + TCGv_i64 lsh = tcg_temp_new_i64(); + TCGv_i64 rsh = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 max = tcg_constant_i64(63); + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_ext8s_i64(lsh, shift); + tcg_gen_neg_i64(rsh, lsh); + tcg_gen_shl_i64(lval, src, lsh); + tcg_gen_umin_i64(rsh, rsh, max); + tcg_gen_sar_i64(rval, src, rsh); + tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero); + tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval); +} + +static void gen_sshl_vec(unsigned vece, TCGv_vec dst, + TCGv_vec src, TCGv_vec shift) +{ + TCGv_vec lval = tcg_temp_new_vec_matching(dst); + TCGv_vec rval = tcg_temp_new_vec_matching(dst); + TCGv_vec lsh = tcg_temp_new_vec_matching(dst); + TCGv_vec rsh = tcg_temp_new_vec_matching(dst); + TCGv_vec max, zero; + + /* + * Rely on the TCG guarantee that out of range shifts produce + * unspecified results, not undefined behaviour (i.e. no trap). + * Discard out-of-range results after the fact. + */ + tcg_gen_neg_vec(vece, rsh, shift); + if (vece == MO_8) { + tcg_gen_mov_vec(lsh, shift); + } else { + TCGv_vec msk = tcg_constant_vec_matching(dst, vece, 0xff); + tcg_gen_and_vec(vece, lsh, shift, msk); + tcg_gen_and_vec(vece, rsh, rsh, msk); + } + + /* Bound rsh so out of bound right shift gets -1. */ + max = tcg_constant_vec_matching(dst, vece, (8 << vece) - 1); + tcg_gen_umin_vec(vece, rsh, rsh, max); + + tcg_gen_shlv_vec(vece, lval, src, lsh); + tcg_gen_sarv_vec(vece, rval, src, rsh); + + /* Select in-bound left shift. */ + zero = tcg_constant_vec_matching(dst, vece, 0); + tcg_gen_cmpsel_vec(TCG_COND_GT, vece, lval, lsh, max, zero, lval); + + /* Select between left and right shift. */ + if (vece == MO_8) { + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, zero, rval, lval); + } else { + TCGv_vec sgn = tcg_constant_vec_matching(dst, vece, 0x80); + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, sgn, lval, rval); + } +} + +void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec, + INDEX_op_sarv_vec, INDEX_op_cmpsel_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_sshl_vec, + .fno = gen_helper_gvec_sshl_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_sshl_vec, + .fno = gen_helper_gvec_sshl_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_sshl_i32, + .fniv = gen_sshl_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_sshl_i64, + .fniv = gen_sshl_vec, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_gvec_srshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[] = { + gen_helper_gvec_srshl_b, gen_helper_gvec_srshl_h, + gen_helper_gvec_srshl_s, gen_helper_gvec_srshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[] = { + gen_helper_gvec_urshl_b, gen_helper_gvec_urshl_h, + gen_helper_gvec_urshl_s, gen_helper_gvec_urshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_sqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_sqshl_b, gen_helper_neon_sqshl_h, + gen_helper_neon_sqshl_s, gen_helper_neon_sqshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_uqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_uqshl_b, gen_helper_neon_uqshl_h, + gen_helper_neon_uqshl_s, gen_helper_neon_uqshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_sqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_sqrshl_b, gen_helper_neon_sqrshl_h, + gen_helper_neon_sqrshl_s, gen_helper_neon_sqrshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_uqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3_ptr * const fns[] = { + gen_helper_neon_uqrshl_b, gen_helper_neon_uqrshl_h, + gen_helper_neon_uqrshl_s, gen_helper_neon_uqrshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ptr(rd_ofs, rn_ofs, rm_ofs, tcg_env, + opr_sz, max_sz, 0, fns[vece]); +} + +void gen_neon_sqshli(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + int64_t c, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_2_ptr * const fns[] = { + gen_helper_neon_sqshli_b, gen_helper_neon_sqshli_h, + gen_helper_neon_sqshli_s, gen_helper_neon_sqshli_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_debug_assert(c >= 0 && c <= (8 << vece)); + tcg_gen_gvec_2_ptr(rd_ofs, rn_ofs, tcg_env, opr_sz, max_sz, c, fns[vece]); +} + +void gen_neon_uqshli(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + int64_t c, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_2_ptr * const fns[] = { + gen_helper_neon_uqshli_b, gen_helper_neon_uqshli_h, + gen_helper_neon_uqshli_s, gen_helper_neon_uqshli_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_debug_assert(c >= 0 && c <= (8 << vece)); + tcg_gen_gvec_2_ptr(rd_ofs, rn_ofs, tcg_env, opr_sz, max_sz, c, fns[vece]); +} + +void gen_neon_sqshlui(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + int64_t c, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_2_ptr * const fns[] = { + gen_helper_neon_sqshlui_b, gen_helper_neon_sqshlui_h, + gen_helper_neon_sqshlui_s, gen_helper_neon_sqshlui_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_debug_assert(c >= 0 && c <= (8 << vece)); + tcg_gen_gvec_2_ptr(rd_ofs, rn_ofs, tcg_env, opr_sz, max_sz, c, fns[vece]); +} + +void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + uint64_t max = MAKE_64BIT_MASK(0, 8 << esz); + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_add_i64(tmp, a, b); + tcg_gen_umin_i64(res, tmp, tcg_constant_i64(max)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_uqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_add_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, res, t, a, + tcg_constant_i64(UINT64_MAX), t); + tcg_gen_xor_i64(t, t, res); + tcg_gen_or_i64(qc, qc, t); +} + +static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_add_vec(vece, x, a, b); + tcg_gen_usadd_vec(vece, t, a, b); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); +} + +void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_usadd_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_b, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_h, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_s, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fniv = gen_uqadd_vec, + .fni8 = gen_uqadd_d, + .fno = gen_helper_gvec_uqadd_d, + .write_aofs = true, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_sqadd_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + int64_t max = MAKE_64BIT_MASK(0, (8 << esz) - 1); + int64_t min = -1ll - max; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_add_i64(tmp, a, b); + tcg_gen_smin_i64(res, tmp, tcg_constant_i64(max)); + tcg_gen_smax_i64(res, res, tcg_constant_i64(min)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_sqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_add_i64(t0, a, b); + + /* Compute signed overflow indication into T1 */ + tcg_gen_xor_i64(t1, a, b); + tcg_gen_xor_i64(t2, t0, a); + tcg_gen_andc_i64(t1, t2, t1); + + /* Compute saturated value into T2 */ + tcg_gen_sari_i64(t2, a, 63); + tcg_gen_xori_i64(t2, t2, INT64_MAX); + + tcg_gen_movcond_i64(TCG_COND_LT, res, t1, tcg_constant_i64(0), t2, t0); + tcg_gen_xor_i64(t0, t0, res); + tcg_gen_or_i64(qc, qc, t0); +} + +static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_add_vec(vece, x, a, b); + tcg_gen_ssadd_vec(vece, t, a, b); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); +} + +void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_ssadd_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_sqadd_vec, + .fni8 = gen_sqadd_d, + .fno = gen_helper_gvec_sqadd_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_uqsub_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_sub_i64(tmp, a, b); + tcg_gen_smax_i64(res, tmp, tcg_constant_i64(0)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_uqsub_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, res, a, b, tcg_constant_i64(0), t); + tcg_gen_xor_i64(t, t, res); + tcg_gen_or_i64(qc, qc, t); +} + +static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_sub_vec(vece, x, a, b); + tcg_gen_ussub_vec(vece, t, a, b); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); +} + +void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_ussub_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_uqsub_vec, + .fni8 = gen_uqsub_d, + .fno = gen_helper_gvec_uqsub_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_sqsub_bhs(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + int64_t max = MAKE_64BIT_MASK(0, (8 << esz) - 1); + int64_t min = -1ll - max; + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_sub_i64(tmp, a, b); + tcg_gen_smin_i64(res, tmp, tcg_constant_i64(max)); + tcg_gen_smax_i64(res, res, tcg_constant_i64(min)); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_sqsub_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t0, a, b); + + /* Compute signed overflow indication into T1 */ + tcg_gen_xor_i64(t1, a, b); + tcg_gen_xor_i64(t2, t0, a); + tcg_gen_and_i64(t1, t1, t2); + + /* Compute saturated value into T2 */ + tcg_gen_sari_i64(t2, a, 63); + tcg_gen_xori_i64(t2, t2, INT64_MAX); + + tcg_gen_movcond_i64(TCG_COND_LT, res, t1, tcg_constant_i64(0), t2, t0); + tcg_gen_xor_i64(t0, t0, res); + tcg_gen_or_i64(qc, qc, t0); +} + +static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_sub_vec(vece, x, a, b); + tcg_gen_sssub_vec(vece, t, a, b); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); +} + +void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sssub_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_sqsub_vec, + .fni8 = gen_sqsub_d, + .fno = gen_helper_gvec_sqsub_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + 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); +} + +static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + 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); +} + +static void gen_sabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_smin_vec(vece, t, a, b); + tcg_gen_smax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_sabd_i32, + .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_sabd_i64, + .fniv = gen_sabd_vec, + .fno = gen_helper_gvec_sabd_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + 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); +} + +static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + 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); +} + +static void gen_uabd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_umin_vec(vece, t, a, b); + tcg_gen_umax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_uabd_i32, + .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_s, + .opt_opc = vecop_list, + .vece = MO_32 }, + { .fni8 = gen_uabd_i64, + .fniv = gen_uabd_vec, + .fno = gen_helper_gvec_uabd_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +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); +} + +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); +} + +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); +} + +void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_add_vec, + INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_saba_i32, + .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_saba_i64, + .fniv = gen_saba_vec, + .fno = gen_helper_gvec_saba_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +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); +} + +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); +} + +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); +} + +void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_add_vec, + INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen3 ops[4] = { + { .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_b, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_8 }, + { .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_h, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_uaba_i32, + .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_s, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_uaba_i64, + .fniv = gen_uaba_vec, + .fno = gen_helper_gvec_uaba_d, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .opt_opc = vecop_list, + .load_dest = true, + .vece = MO_64 }, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_addp_b, + gen_helper_gvec_addp_h, + gen_helper_gvec_addp_s, + gen_helper_gvec_addp_d, + }; + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_smaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_smaxp_b, + gen_helper_gvec_smaxp_h, + gen_helper_gvec_smaxp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_sminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_sminp_b, + gen_helper_gvec_sminp_h, + gen_helper_gvec_sminp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_umaxp_b, + gen_helper_gvec_umaxp_h, + gen_helper_gvec_umaxp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_gvec_uminp_b, + gen_helper_gvec_uminp_h, + gen_helper_gvec_uminp_s, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +static void gen_shadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_sar8i_i64(a, a, 1); + tcg_gen_vec_sar8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_shadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_sar16i_i64(a, a, 1); + tcg_gen_vec_sar16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_shadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_and_i32(t, a, b); + tcg_gen_sari_i32(a, a, 1); + tcg_gen_sari_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_shadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_and_vec(vece, t, a, b); + tcg_gen_sari_vec(vece, a, a, 1); + tcg_gen_sari_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_shadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_shadd8_i64, + .fniv = gen_shadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shadd16_i64, + .fniv = gen_shadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shadd_i32, + .fniv = gen_shadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uhadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_shr8i_i64(a, a, 1); + tcg_gen_vec_shr8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_uhadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_and_i64(t, a, b); + tcg_gen_vec_shr16i_i64(a, a, 1); + tcg_gen_vec_shr16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_uhadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_and_i32(t, a, b); + tcg_gen_shri_i32(a, a, 1); + tcg_gen_shri_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_uhadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_and_vec(vece, t, a, b); + tcg_gen_shri_vec(vece, a, a, 1); + tcg_gen_shri_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_uhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_uhadd8_i64, + .fniv = gen_uhadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_uhadd16_i64, + .fniv = gen_uhadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_uhadd_i32, + .fniv = gen_uhadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + tcg_debug_assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_shsub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_sar8i_i64(a, a, 1); + tcg_gen_vec_sar8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_sub8_i64(d, a, b); + tcg_gen_vec_sub8_i64(d, d, t); +} + +static void gen_shsub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_sar16i_i64(a, a, 1); + tcg_gen_vec_sar16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_sub16_i64(d, a, b); + tcg_gen_vec_sub16_i64(d, d, t); +} + +static void gen_shsub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_andc_i32(t, b, a); + tcg_gen_sari_i32(a, a, 1); + tcg_gen_sari_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_sub_i32(d, a, b); + tcg_gen_sub_i32(d, d, t); +} + +static void gen_shsub_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_andc_vec(vece, t, b, a); + tcg_gen_sari_vec(vece, a, a, 1); + tcg_gen_sari_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_sub_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_shsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 g[4] = { + { .fni8 = gen_shsub8_i64, + .fniv = gen_shsub_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_shsub16_i64, + .fniv = gen_shsub_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_shsub_i32, + .fniv = gen_shsub_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_uhsub8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_shr8i_i64(a, a, 1); + tcg_gen_vec_shr8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_sub8_i64(d, a, b); + tcg_gen_vec_sub8_i64(d, d, t); +} + +static void gen_uhsub16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_andc_i64(t, b, a); + tcg_gen_vec_shr16i_i64(a, a, 1); + tcg_gen_vec_shr16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_sub16_i64(d, a, b); + tcg_gen_vec_sub16_i64(d, d, t); +} + +static void gen_uhsub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_andc_i32(t, b, a); + tcg_gen_shri_i32(a, a, 1); + tcg_gen_shri_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_sub_i32(d, a, b); + tcg_gen_sub_i32(d, d, t); +} + +static void gen_uhsub_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_andc_vec(vece, t, b, a); + tcg_gen_shri_vec(vece, a, a, 1); + tcg_gen_shri_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_sub_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_uhsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_sub_vec, 0 + }; + static const GVecGen3 g[4] = { + { .fni8 = gen_uhsub8_i64, + .fniv = gen_uhsub_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_uhsub16_i64, + .fniv = gen_uhsub_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_uhsub_i32, + .fniv = gen_uhsub_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_srhadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_sar8i_i64(a, a, 1); + tcg_gen_vec_sar8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_srhadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_sar16i_i64(a, a, 1); + tcg_gen_vec_sar16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_srhadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_or_i32(t, a, b); + tcg_gen_sari_i32(a, a, 1); + tcg_gen_sari_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_srhadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_or_vec(vece, t, a, b); + tcg_gen_sari_vec(vece, a, a, 1); + tcg_gen_sari_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_srhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sari_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_srhadd8_i64, + .fniv = gen_srhadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_srhadd16_i64, + .fniv = gen_srhadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_srhadd_i32, + .fniv = gen_srhadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} + +static void gen_urhadd8_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_shr8i_i64(a, a, 1); + tcg_gen_vec_shr8i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_8, 1)); + tcg_gen_vec_add8_i64(d, a, b); + tcg_gen_vec_add8_i64(d, d, t); +} + +static void gen_urhadd16_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_or_i64(t, a, b); + tcg_gen_vec_shr16i_i64(a, a, 1); + tcg_gen_vec_shr16i_i64(b, b, 1); + tcg_gen_andi_i64(t, t, dup_const(MO_16, 1)); + tcg_gen_vec_add16_i64(d, a, b); + tcg_gen_vec_add16_i64(d, d, t); +} + +static void gen_urhadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_or_i32(t, a, b); + tcg_gen_shri_i32(a, a, 1); + tcg_gen_shri_i32(b, b, 1); + tcg_gen_andi_i32(t, t, 1); + tcg_gen_add_i32(d, a, b); + tcg_gen_add_i32(d, d, t); +} + +static void gen_urhadd_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_or_vec(vece, t, a, b); + tcg_gen_shri_vec(vece, a, a, 1); + tcg_gen_shri_vec(vece, b, b, 1); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(d, vece, 1)); + tcg_gen_add_vec(vece, d, a, b); + tcg_gen_add_vec(vece, d, d, t); +} + +void gen_gvec_urhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_shri_vec, INDEX_op_add_vec, 0 + }; + static const GVecGen3 g[] = { + { .fni8 = gen_urhadd8_i64, + .fniv = gen_urhadd_vec, + .opt_opc = vecop_list, + .vece = MO_8 }, + { .fni8 = gen_urhadd16_i64, + .fniv = gen_urhadd_vec, + .opt_opc = vecop_list, + .vece = MO_16 }, + { .fni4 = gen_urhadd_i32, + .fniv = gen_urhadd_vec, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + assert(vece <= MO_32); + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &g[vece]); +} diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c new file mode 100644 index 0000000000..2617cde0a5 --- /dev/null +++ b/target/arm/tcg/gengvec64.c @@ -0,0 +1,371 @@ +/* + * AArch64 generic vector expansion + * + * 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 "translate.h" +#include "translate-a64.h" + + +static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + tcg_gen_rotli_i64(d, m, 1); + tcg_gen_xor_i64(d, d, n); +} + +static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m) +{ + tcg_gen_rotli_vec(vece, d, m, 1); + tcg_gen_xor_vec(vece, d, d, n); +} + +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) +{ + static const TCGOpcode vecop_list[] = { INDEX_op_rotli_vec, 0 }; + static const GVecGen3 op = { + .fni8 = gen_rax1_i64, + .fniv = gen_rax1_vec, + .opt_opc = vecop_list, + .fno = gen_helper_crypto_rax1, + .vece = MO_64, + }; + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op); +} + +static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + uint64_t mask = dup_const(MO_8, 0xff >> sh); + + tcg_gen_xor_i64(t, n, m); + tcg_gen_shri_i64(d, t, sh); + tcg_gen_shli_i64(t, t, 8 - sh); + tcg_gen_andi_i64(d, d, mask); + tcg_gen_andi_i64(t, t, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_xar16_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) +{ + TCGv_i64 t = tcg_temp_new_i64(); + uint64_t mask = dup_const(MO_16, 0xffff >> sh); + + tcg_gen_xor_i64(t, n, m); + tcg_gen_shri_i64(d, t, sh); + tcg_gen_shli_i64(t, t, 16 - sh); + tcg_gen_andi_i64(d, d, mask); + tcg_gen_andi_i64(t, t, ~mask); + tcg_gen_or_i64(d, d, t); +} + +static void gen_xar_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, int32_t sh) +{ + tcg_gen_xor_i32(d, n, m); + tcg_gen_rotri_i32(d, d, sh); +} + +static void gen_xar_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) +{ + tcg_gen_xor_i64(d, n, m); + tcg_gen_rotri_i64(d, d, sh); +} + +static void gen_xar_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, int64_t sh) +{ + tcg_gen_xor_vec(vece, d, n, m); + tcg_gen_rotri_vec(vece, d, d, sh); +} + +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) +{ + static const TCGOpcode vecop[] = { INDEX_op_rotli_vec, 0 }; + static const GVecGen3i ops[4] = { + { .fni8 = gen_xar8_i64, + .fniv = gen_xar_vec, + .fno = gen_helper_sve2_xar_b, + .opt_opc = vecop, + .vece = MO_8 }, + { .fni8 = gen_xar16_i64, + .fniv = gen_xar_vec, + .fno = gen_helper_sve2_xar_h, + .opt_opc = vecop, + .vece = MO_16 }, + { .fni4 = gen_xar_i32, + .fniv = gen_xar_vec, + .fno = gen_helper_sve2_xar_s, + .opt_opc = vecop, + .vece = MO_32 }, + { .fni8 = gen_xar_i64, + .fniv = gen_xar_vec, + .fno = gen_helper_gvec_xar_d, + .opt_opc = vecop, + .vece = MO_64 } + }; + int esize = 8 << vece; + + /* The SVE2 range is 1 .. esize; the AdvSIMD range is 0 .. esize-1. */ + tcg_debug_assert(shift >= 0); + tcg_debug_assert(shift <= esize); + shift &= esize - 1; + + if (shift == 0) { + /* xar with no rotate devolves to xor. */ + tcg_gen_gvec_xor(vece, rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz); + } else { + tcg_gen_gvec_3i(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, + shift, &ops[vece]); + } +} + +static void gen_eor3_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) +{ + tcg_gen_xor_i64(d, n, m); + tcg_gen_xor_i64(d, d, k); +} + +static void gen_eor3_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec k) +{ + tcg_gen_xor_vec(vece, d, n, m); + tcg_gen_xor_vec(vece, d, d, k); +} + +void gen_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen4 op = { + .fni8 = gen_eor3_i64, + .fniv = gen_eor3_vec, + .fno = gen_helper_sve2_eor3, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); +} + +static void gen_bcax_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) +{ + tcg_gen_andc_i64(d, m, k); + tcg_gen_xor_i64(d, d, n); +} + +static void gen_bcax_vec(unsigned vece, TCGv_vec d, TCGv_vec n, + TCGv_vec m, TCGv_vec k) +{ + tcg_gen_andc_vec(vece, d, m, k); + tcg_gen_xor_vec(vece, d, d, n); +} + +void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen4 op = { + .fni8 = gen_bcax_i64, + .fniv = gen_bcax_vec, + .fno = gen_helper_sve2_bcax, + .vece = MO_64, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); +} + +/* + * Set @res to the correctly saturated result. + * Set @qc non-zero if saturation occured. + */ +void gen_suqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + TCGv_i64 max = tcg_constant_i64((1ull << ((8 << esz) - 1)) - 1); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_add_i64(t, a, b); + tcg_gen_smin_i64(res, t, max); + tcg_gen_xor_i64(t, t, res); + tcg_gen_or_i64(qc, qc, t); +} + +void gen_suqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 max = tcg_constant_i64(INT64_MAX); + TCGv_i64 t = tcg_temp_new_i64(); + + /* Maximum value that can be added to @a without overflow. */ + tcg_gen_sub_i64(t, max, a); + + /* Constrain addend so that the next addition never overflows. */ + tcg_gen_umin_i64(t, t, b); + tcg_gen_add_i64(res, a, t); + + tcg_gen_xor_i64(t, t, b); + tcg_gen_or_i64(qc, qc, t); +} + +static void gen_suqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec max = + tcg_constant_vec_matching(t, vece, (1ull << ((8 << vece) - 1)) - 1); + TCGv_vec u = tcg_temp_new_vec_matching(t); + + /* Maximum value that can be added to @a without overflow. */ + tcg_gen_sub_vec(vece, u, max, a); + + /* Constrain addend so that the next addition never overflows. */ + tcg_gen_umin_vec(vece, u, u, b); + tcg_gen_add_vec(vece, t, u, a); + + /* Compute QC by comparing the adjusted @b. */ + tcg_gen_xor_vec(vece, u, u, b); + tcg_gen_or_vec(vece, qc, qc, u); +} + +void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_add_vec, INDEX_op_sub_vec, INDEX_op_umin_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_suqadd_vec, + .fno = gen_helper_gvec_suqadd_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_suqadd_vec, + .fni8 = gen_suqadd_d, + .fno = gen_helper_gvec_suqadd_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} + +void gen_usqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz) +{ + TCGv_i64 max = tcg_constant_i64(MAKE_64BIT_MASK(0, 8 << esz)); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_add_i64(tmp, a, b); + tcg_gen_smin_i64(res, tmp, max); + tcg_gen_smax_i64(res, res, zero); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +void gen_usqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 tneg = tcg_temp_new_i64(); + TCGv_i64 tpos = tcg_temp_new_i64(); + TCGv_i64 max = tcg_constant_i64(UINT64_MAX); + TCGv_i64 zero = tcg_constant_i64(0); + + tcg_gen_add_i64(tmp, a, b); + + /* If @b is positive, saturate if (a + b) < a, aka unsigned overflow. */ + tcg_gen_movcond_i64(TCG_COND_LTU, tpos, tmp, a, max, tmp); + + /* If @b is negative, saturate if a < -b, ie subtraction is negative. */ + tcg_gen_neg_i64(tneg, b); + tcg_gen_movcond_i64(TCG_COND_LTU, tneg, a, tneg, zero, tmp); + + /* Select correct result from sign of @b. */ + tcg_gen_movcond_i64(TCG_COND_LT, res, b, zero, tneg, tpos); + tcg_gen_xor_i64(tmp, tmp, res); + tcg_gen_or_i64(qc, qc, tmp); +} + +static void gen_usqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec qc, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec u = tcg_temp_new_vec_matching(t); + TCGv_vec z = tcg_constant_vec_matching(t, vece, 0); + + /* Compute unsigned saturation of add for +b and sub for -b. */ + tcg_gen_neg_vec(vece, t, b); + tcg_gen_usadd_vec(vece, u, a, b); + tcg_gen_ussub_vec(vece, t, a, t); + + /* Select the correct result depending on the sign of b. */ + tcg_gen_cmpsel_vec(TCG_COND_LT, vece, t, b, z, t, u); + + /* Compute QC by comparing against the non-saturated result. */ + tcg_gen_add_vec(vece, u, a, b); + tcg_gen_xor_vec(vece, u, u, t); + tcg_gen_or_vec(vece, qc, qc, u); +} + +void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_neg_vec, INDEX_op_add_vec, + INDEX_op_usadd_vec, INDEX_op_ussub_vec, + INDEX_op_cmpsel_vec, 0 + }; + static const GVecGen4 ops[4] = { + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_b, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_h, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_usqadd_vec, + .fno = gen_helper_gvec_usqadd_s, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_usqadd_vec, + .fni8 = gen_usqadd_d, + .fno = gen_helper_gvec_usqadd_d, + .opt_opc = vecop_list, + .write_aofs = true, + .vece = MO_64 }, + }; + + tcg_debug_assert(opr_sz <= sizeof_field(CPUARMState, vfp.qc)); + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); +} diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c new file mode 100644 index 0000000000..8f42a28d07 --- /dev/null +++ b/target/arm/tcg/helper-a64.c @@ -0,0 +1,1918 @@ +/* + * 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); +} + +void HELPER(msr_set_allint_el1)(CPUARMState *env) +{ + /* ALLINT update to PSTATE. */ + if (arm_hcrx_el2_eff(env) & HCRX_TALLINT) { + raise_exception_ra(env, EXCP_UDEF, + syn_aa64_sysregtrap(0, 1, 0, 4, 1, 0x1f, 0), 2, + GETPC()); + } + + env->pstate |= PSTATE_ALLINT; +} + +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 | PSTATE_ALLINT; + spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF | PSTATE_ALLINT); + 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) +{ + uintptr_t ra = GETPC(); + + /* + * 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)) { + /* + * 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 + + set_helper_retaddr(ra); + memset(mem, 0, blocklen); + clear_helper_retaddr(); +} + +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 */ + set_helper_retaddr(ra); + memset(mem, data, setsize); + clear_helper_retaddr(); + 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 */ + set_helper_retaddr(ra); + memset(mem, data, setsize); + clear_helper_retaddr(); + 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, stagesetsize, 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 */ + set_helper_retaddr(ra); + memmove(wmem, rmem, copysize); + clear_helper_retaddr(); + 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. + */ + set_helper_retaddr(ra); + memmove(wmem - (copysize - 1), rmem - (copysize - 1), copysize); + clear_helper_retaddr(); + 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()); +} + +static bool is_guarded_page(CPUARMState *env, target_ulong addr, uintptr_t ra) +{ +#ifdef CONFIG_USER_ONLY + return page_get_flags(addr) & PAGE_BTI; +#else + CPUTLBEntryFull *full; + void *host; + int mmu_idx = cpu_mmu_index(env_cpu(env), true); + int flags = probe_access_full(env, addr, 0, MMU_INST_FETCH, mmu_idx, + false, &host, &full, ra); + + assert(!(flags & TLB_INVALID_MASK)); + return full->extra.arm.guarded; +#endif +} + +void HELPER(guarded_page_check)(CPUARMState *env) +{ + /* + * We have already verified that bti is enabled, and that the + * instruction at PC is not ok for BTYPE. This is always at + * the beginning of a block, so PC is always up-to-date and + * no unwind is required. + */ + if (is_guarded_page(env, env->pc, 0)) { + raise_exception(env, EXCP_UDEF, syn_btitrap(env->btype), + exception_target_el(env)); + } +} + +void HELPER(guarded_page_br)(CPUARMState *env, target_ulong pc) +{ + /* + * We have already checked for branch via x16 and x17. + * What remains for choosing BTYPE is checking for a guarded page. + */ + env->btype = is_guarded_page(env, pc, GETPC()) ? 3 : 1; +} diff --git a/target/arm/helper-a64.h b/target/arm/tcg/helper-a64.h similarity index 76% rename from target/arm/helper-a64.h rename to target/arm/tcg/helper-a64.h index 7b706571bb..481007bf39 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/tcg/helper-a64.h @@ -22,6 +22,7 @@ DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(msr_i_spsel, void, env, i32) DEF_HELPER_2(msr_i_daifset, void, env, i32) DEF_HELPER_2(msr_i_daifclear, void, env, i32) +DEF_HELPER_1(msr_set_allint_el1, void, env) DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) @@ -50,14 +51,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 +91,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 +115,35 @@ 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) + +DEF_HELPER_FLAGS_1(guarded_page_check, TCG_CALL_NO_WG, void, env) +DEF_HELPER_FLAGS_2(guarded_page_br, TCG_CALL_NO_RWG, void, env, tl) + +DEF_HELPER_FLAGS_5(gvec_fdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmulx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmulx_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, 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/helper-sme.h b/target/arm/tcg/helper-sme.h similarity index 96% rename from target/arm/helper-sme.h rename to target/arm/tcg/helper-sme.h index d2d544a696..59ecaa1548 100644 --- a/target/arm/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -17,8 +17,7 @@ * License along with this library; if not, see . */ -DEF_HELPER_FLAGS_2(set_pstate_sm, TCG_CALL_NO_RWG, void, env, i32) -DEF_HELPER_FLAGS_2(set_pstate_za, TCG_CALL_NO_RWG, void, env, i32) +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) @@ -122,13 +121,13 @@ 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) + void, ptr, ptr, ptr, ptr, ptr, env, 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_7(sme_bfmopa, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, env, 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, diff --git a/target/arm/helper-sve.h b/target/arm/tcg/helper-sve.h similarity index 100% rename from target/arm/helper-sve.h rename to target/arm/tcg/helper-sve.h diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c new file mode 100644 index 0000000000..f03977b4b0 --- /dev/null +++ b/target/arm/tcg/hflags.c @@ -0,0 +1,493 @@ +/* + * 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; + } + + /* + * With PMSA, when the MPU is disabled, all memory types in the + * default map are Normal, so don't need aligment enforcing. + */ + if (arm_feature(env, ARM_FEATURE_PMSA)) { + return false; + } + + /* + * With VMSA, 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 97% rename from target/arm/m_helper.c rename to target/arm/tcg/m_helper.c index 355cd4d60a..f7354f3c6e 100644 --- a/target/arm/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -7,33 +7,23 @@ */ #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" +#include "exec/page-protection.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 +59,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 +96,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 +150,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 @@ -190,7 +222,7 @@ static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, int exc; bool exc_secure; - if (get_phys_addr(env, addr, MMU_DATA_STORE, mmu_idx, &res, &fi)) { + if (get_phys_addr(env, addr, MMU_DATA_STORE, 0, mmu_idx, &res, &fi)) { /* MPU/SAU lookup failed */ if (fi.type == ARMFault_QEMU_SFault) { if (mode == STACK_LAZYFP) { @@ -279,7 +311,7 @@ static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr, bool exc_secure; uint32_t value; - if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &res, &fi)) { + if (get_phys_addr(env, addr, MMU_DATA_LOAD, 0, mmu_idx, &res, &fi)) { /* MPU/SAU lookup failed */ if (fi.type == ARMFault_QEMU_SFault) { qemu_log_mask(CPU_LOG_INT, @@ -342,8 +374,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)) { @@ -397,7 +429,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()); @@ -621,42 +653,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) { @@ -781,8 +777,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]; @@ -895,7 +891,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; @@ -989,7 +985,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 @@ -1627,10 +1623,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; @@ -1936,7 +1930,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; /* @@ -1945,8 +1939,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; @@ -2015,7 +2009,7 @@ static bool v7m_read_half_insn(ARMCPU *cpu, ARMMMUIdx mmu_idx, bool secure, "...really SecureFault with SFSR.INVEP\n"); return false; } - if (get_phys_addr(env, addr, MMU_INST_FETCH, mmu_idx, &res, &fi)) { + if (get_phys_addr(env, addr, MMU_INST_FETCH, 0, 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); @@ -2051,7 +2045,7 @@ static bool v7m_read_sg_stack_word(ARMCPU *cpu, ARMMMUIdx mmu_idx, ARMMMUFaultInfo fi = {}; uint32_t value; - if (get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &res, &fi)) { + if (get_phys_addr(env, addr, MMU_DATA_LOAD, 0, mmu_idx, &res, &fi)) { /* MPU/SAU lookup failed */ if (fi.type == ARMFault_QEMU_SFault) { qemu_log_mask(CPU_LOG_INT, @@ -2436,7 +2430,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 @@ -2481,11 +2475,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; } @@ -2531,8 +2531,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: @@ -2597,13 +2603,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; @@ -2854,39 +2866,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..508932a249 --- /dev/null +++ b/target/arm/tcg/meson.build @@ -0,0 +1,62 @@ +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', + 'gengvec.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', + 'gengvec64.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/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c new file mode 100644 index 0000000000..9d2ba287ee --- /dev/null +++ b/target/arm/tcg/mte_helper.c @@ -0,0 +1,1163 @@ +/* + * ARM v8.5-MemTag Operations + * + * Copyright (c) 2020 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 "qemu/log.h" +#include "cpu.h" +#include "internals.h" +#include "exec/exec-all.h" +#include "exec/page-protection.h" +#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" +#include "mte_helper.h" + + +static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) +{ + if (exclude == 0xffff) { + return 0; + } + if (offset == 0) { + while (exclude & (1 << tag)) { + tag = (tag + 1) & 15; + } + } else { + do { + do { + tag = (tag + 1) & 15; + } while (exclude & (1 << tag)); + } while (--offset > 0); + } + return tag; +} + +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); + int flags = page_get_flags(clean_ptr); + uint8_t *tags; + uintptr_t index; + + assert(!(probe && ra)); + + if (!(flags & (ptr_access == MMU_DATA_STORE ? PAGE_WRITE_ORG : PAGE_READ))) { + if (probe) { + return NULL; + } + cpu_loop_exit_sigsegv(env_cpu(env), ptr, ptr_access, + !(flags & PAGE_VALID), ra); + } + + /* Require both MAP_ANON and PROT_MTE for the page. */ + if (!(flags & PAGE_ANON) || !(flags & PAGE_MTE)) { + return NULL; + } + + tags = page_get_target_data(clean_ptr); + + index = extract32(ptr, LOG2_TAG_GRANULE + 1, + TARGET_PAGE_BITS - LOG2_TAG_GRANULE - 1); + return tags + index; +#else + CPUTLBEntryFull *full; + MemTxAttrs attrs; + int in_page, flags; + hwaddr ptr_paddr, tag_paddr, xlat; + MemoryRegion *mr; + ARMASIdx tag_asi; + AddressSpace *tag_as; + void *host; + + /* + * Probe the first byte of the virtual address. This raises an + * exception for inaccessible pages, and resolves the virtual address + * into the softmmu tlb. + * + * 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_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)); + + /* If the virtual page MemAttr != Tagged, access unchecked. */ + if (full->extra.arm.pte_attrs != 0xf0) { + return NULL; + } + + /* + * If not backed by host ram, there is no tag storage: access unchecked. + * This is probably a guest os bug though, so log it. + */ + if (unlikely(flags & TLB_MMIO)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Page @ 0x%" PRIx64 " indicates Tagged Normal memory " + "but is not backed by host ram\n", ptr); + 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 + * tag on the first page. + * Any page access exception has priority over tag check exception. + */ + in_page = -(ptr | TARGET_PAGE_MASK); + if (unlikely(ptr_size > in_page)) { + 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 (!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, attrs, wp, ra); + } + + /* 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 = 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, attrs); + + /* + * Note that @mr will never be NULL. If there is nothing in the address + * space at @tag_paddr, the translation will return the unallocated memory + * region. For our purposes, the result must be ram. + */ + if (unlikely(!memory_region_is_ram(mr))) { + /* ??? Failure is a board configuration error. */ + qemu_log_mask(LOG_UNIMP, + "Tag Memory @ 0x%" HWADDR_PRIx " not found for " + "Normal Memory @ 0x%" HWADDR_PRIx "\n", + tag_paddr, ptr_paddr); + return NULL; + } + + /* + * Ensure the tag memory is dirty on write, for migration. + * Tag memory can never contain code or display memory (vga). + */ + if (tag_access == MMU_DATA_STORE) { + ram_addr_t tag_ra = memory_region_get_ram_addr(mr) + xlat; + cpu_physical_memory_set_dirty_flag(tag_ra, DIRTY_MEMORY_MIGRATION); + } + + return memory_region_get_ram_ptr(mr) + xlat; +#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); + int rrnd = extract32(env->cp15.gcr_el1, 16, 1); + int start = extract32(env->cp15.rgsr_el1, 0, 4); + int seed = extract32(env->cp15.rgsr_el1, 8, 16); + int offset, i, rtag; + + /* + * Our IMPDEF choice for GCR_EL1.RRND==1 is to continue to use the + * deterministic algorithm. Except that with RRND==1 the kernel is + * not required to have set RGSR_EL1.SEED != 0, which is required for + * the deterministic algorithm to function. So we force a non-zero + * SEED for that case. + */ + if (unlikely(seed == 0) && rrnd) { + do { + Error *err = NULL; + uint16_t two; + + if (qemu_guest_getrandom(&two, sizeof(two), &err) < 0) { + /* + * Failed, for unknown reasons in the crypto subsystem. + * Best we can do is log the reason and use a constant seed. + */ + qemu_log_mask(LOG_UNIMP, "IRG: Crypto failure: %s\n", + error_get_pretty(err)); + error_free(err); + two = 1; + } + seed = two; + } while (seed == 0); + } + + /* RandomTag */ + for (i = offset = 0; i < 4; ++i) { + /* NextRandomTagBit */ + int top = (extract32(seed, 5, 1) ^ extract32(seed, 3, 1) ^ + extract32(seed, 2, 1) ^ extract32(seed, 0, 1)); + seed = (top << 15) | (seed >> 1); + offset |= top << i; + } + rtag = choose_nonexcluded_tag(start, offset, exclude); + env->cp15.rgsr_el1 = rtag | (seed << 8); + + return address_with_allocation_tag(rn, rtag); +} + +uint64_t HELPER(addsubg)(CPUARMState *env, uint64_t ptr, + int32_t offset, uint32_t tag_offset) +{ + int start_tag = allocation_tag_from_addr(ptr); + uint16_t exclude = extract32(env->cp15.gcr_el1, 0, 16); + int rtag = choose_nonexcluded_tag(start_tag, tag_offset, exclude); + + return address_with_allocation_tag(ptr + offset, rtag); +} + +int load_tag1(uint64_t ptr, uint8_t *mem) +{ + int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; + return extract32(*mem, ofs, 4); +} + +uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt) +{ + 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, GETPC()); + + /* Load if page supports tags. */ + if (mem) { + rtag = load_tag1(ptr, mem); + } + + return address_with_allocation_tag(xt, rtag); +} + +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, + arm_env_mmu_index(env), ra); + g_assert_not_reached(); + } +} + +/* For use in a non-parallel context, store to the given nibble. */ +void store_tag1(uint64_t ptr, uint8_t *mem, int tag) +{ + int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; + *mem = deposit32(*mem, ofs, 4, tag); +} + +/* For use in a parallel context, atomically store to the given nibble. */ +static void store_tag1_parallel(uint64_t ptr, uint8_t *mem, int tag) +{ + int ofs = extract32(ptr, LOG2_TAG_GRANULE, 1) * 4; + uint8_t old = qatomic_read(mem); + + while (1) { + uint8_t new = deposit32(old, ofs, 4, tag); + uint8_t cmp = qatomic_cmpxchg(mem, old, new); + if (likely(cmp == old)) { + return; + } + old = cmp; + } +} + +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 = 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, ra); + + /* Store if page supports tags. */ + if (mem) { + store1(ptr, mem, allocation_tag_from_addr(xt)); + } +} + +void HELPER(stg)(CPUARMState *env, uint64_t ptr, uint64_t xt) +{ + do_stg(env, ptr, xt, GETPC(), store_tag1); +} + +void HELPER(stg_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) +{ + do_stg(env, ptr, xt, GETPC(), store_tag1_parallel); +} + +void HELPER(stg_stub)(CPUARMState *env, uint64_t ptr) +{ + int mmu_idx = arm_env_mmu_index(env); + uintptr_t ra = GETPC(); + + check_tag_aligned(env, ptr, ra); + probe_write(env, ptr, TAG_GRANULE, mmu_idx, ra); +} + +static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, + uintptr_t ra, stg_store1 store1) +{ + int mmu_idx = arm_env_mmu_index(env); + int tag = allocation_tag_from_addr(xt); + uint8_t *mem1, *mem2; + + check_tag_aligned(env, ptr, ra); + + /* + * Trap if accessing an invalid page(s). + * This takes priority over !allocation_tag_access_enabled. + */ + 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, ra); + mem2 = allocation_tag_mem(env, mmu_idx, ptr + TAG_GRANULE, + MMU_DATA_STORE, TAG_GRANULE, + MMU_DATA_STORE, ra); + + /* Store if page(s) support tags. */ + if (mem1) { + store1(TAG_GRANULE, mem1, tag); + } + if (mem2) { + store1(0, mem2, tag); + } + } 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, ra); + if (mem1) { + tag |= tag << 4; + qatomic_set(mem1, tag); + } + } +} + +void HELPER(st2g)(CPUARMState *env, uint64_t ptr, uint64_t xt) +{ + do_st2g(env, ptr, xt, GETPC(), store_tag1); +} + +void HELPER(st2g_parallel)(CPUARMState *env, uint64_t ptr, uint64_t xt) +{ + do_st2g(env, ptr, xt, GETPC(), store_tag1_parallel); +} + +void HELPER(st2g_stub)(CPUARMState *env, uint64_t ptr) +{ + int mmu_idx = arm_env_mmu_index(env); + uintptr_t ra = GETPC(); + int in_page = -(ptr | TARGET_PAGE_MASK); + + check_tag_aligned(env, ptr, ra); + + if (likely(in_page >= 2 * TAG_GRANULE)) { + probe_write(env, ptr, 2 * TAG_GRANULE, mmu_idx, ra); + } else { + probe_write(env, ptr, TAG_GRANULE, mmu_idx, ra); + probe_write(env, ptr + TAG_GRANULE, TAG_GRANULE, mmu_idx, ra); + } +} + +uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr) +{ + 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, gm_bs_bytes); + + /* Trap if accessing an invalid page. */ + tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_LOAD, + 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; + } + + /* + * 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. + */ + 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 = 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, gm_bs_bytes); + + /* Trap if accessing an invalid page. */ + tag_mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, + gm_bs_bytes, MMU_DATA_LOAD, ra); + + /* + * Tag store only happens if the page support tags, + * and if the OS has enabled access to the tags. + */ + if (!tag_mem) { + return; + } + + /* 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 = arm_env_mmu_index(env); + int log2_dcz_bytes, log2_tag_bytes; + intptr_t dcz_bytes, tag_bytes; + uint8_t *mem; + + /* + * In arm_cpu_realizefn, we assert that dcz > LOG2_TAG_GRANULE+1, + * i.e. 32 bytes, which is an unreasonably small dcz anyway, + * to make sure that we can access one complete tag byte here. + */ + log2_dcz_bytes = env_archcpu(env)->dcz_blocksize + 2; + log2_tag_bytes = log2_dcz_bytes - (LOG2_TAG_GRANULE + 1); + dcz_bytes = (intptr_t)1 << log2_dcz_bytes; + tag_bytes = (intptr_t)1 << log2_tag_bytes; + ptr &= -dcz_bytes; + + mem = allocation_tag_mem(env, mmu_idx, ptr, MMU_DATA_STORE, dcz_bytes, + MMU_DATA_STORE, ra); + if (mem) { + int tag_pair = (val & 0xf) * 0x11; + memset(mem, tag_pair, tag_bytes); + } +} + +static void mte_sync_check_fail(CPUARMState *env, uint32_t desc, + uint64_t dirty_ptr, uintptr_t ra) +{ + int is_write, syn; + + env->exception.vaddress = dirty_ptr; + + is_write = FIELD_EX32(desc, MTEDESC, WRITE); + syn = syn_data_abort_no_iss(arm_current_el(env) != 0, 0, 0, 0, 0, is_write, + 0x11); + raise_exception_ra(env, EXCP_DATA_ABORT, syn, exception_target_el(env), ra); + g_assert_not_reached(); +} + +static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, + uintptr_t ra, ARMMMUIdx arm_mmu_idx, int el) +{ + int select; + + if (regime_has_2_ranges(arm_mmu_idx)) { + select = extract64(dirty_ptr, 55, 1); + } else { + select = 0; + } + env->cp15.tfsr_el[el] |= 1 << select; +#ifdef CONFIG_USER_ONLY + /* + * Stand in for a timer irq, setting _TIF_MTE_ASYNC_FAULT, + * which then sends a SIGSEGV when the thread is next scheduled. + * This cpu will return to the main loop at the end of the TB, + * which is rather sooner than "normal". But the alternative + * is waiting until the next syscall. + */ + qemu_cpu_kick(env_cpu(env)); +#endif +} + +/* Record a tag check failure. */ +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); + int el, reg_el, tcf; + uint64_t sctlr; + + reg_el = regime_el(env, arm_mmu_idx); + sctlr = env->cp15.sctlr_el[reg_el]; + + switch (arm_mmu_idx) { + case ARMMMUIdx_E10_0: + case ARMMMUIdx_E20_0: + el = 0; + tcf = extract64(sctlr, 38, 2); + break; + default: + el = reg_el; + tcf = extract64(sctlr, 40, 2); + } + + switch (tcf) { + case 1: + /* Tag check fail causes a synchronous exception. */ + mte_sync_check_fail(env, desc, dirty_ptr, ra); + break; + + case 0: + /* + * Tag check fail does not affect the PE. + * We eliminate this case by not setting MTE_ACTIVE + * in tb_flags, so that we never make this runtime call. + */ + g_assert_not_reached(); + + case 2: + /* Tag check fail causes asynchronous flag set. */ + mte_async_check_fail(env, dirty_ptr, ra, arm_mmu_idx, el); + break; + + case 3: + /* + * Tag check fail causes asynchronous flag set for stores, or + * a synchronous exception for loads. + */ + if (FIELD_EX32(desc, MTEDESC, WRITE)) { + mte_async_check_fail(env, dirty_ptr, ra, arm_mmu_idx, el); + } else { + mte_sync_check_fail(env, desc, dirty_ptr, ra); + } + break; + } +} + +/** + * checkN: + * @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. + * + * A note about sizes: count is expected to be small. + * + * The most common use will be LDP/STP of two integer registers, + * which means 16 bytes of memory touching at most 2 tags, but + * often the access is aligned and thus just 1 tag. + * + * Using AdvSIMD LD/ST (multiple), one can access 64 bytes of memory, + * touching at most 5 tags. SVE LDR/STR (vector) with the default + * vector length is also 64 bytes; the maximum architectural length + * is 256 bytes touching at most 9 tags. + * + * The loop below uses 7 logical operations and 1 memory operation + * per tag pair. An implementation that loads an aligned word and + * uses masking to ignore adjacent tags requires 18 logical operations + * and thus does not begin to pay off until 6 tags. + * Which, according to the survey above, is unlikely to be common. + */ +static int checkN(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_odd; + } + + while (1) { + /* Test even tag. */ + if (unlikely((diff) & 0x0f)) { + break; + } + if (++n == count) { + break; + } + + start_odd: + /* Test odd tag. */ + if (unlikely((diff) & 0xf0)) { + break; + } + if (++n == count) { + break; + } + + diff = *mem++ ^ cmp; + } + 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 + * @desc: MTEDESC descriptor + * @ptr: virtual address of the base of the access + * @fault: return virtual address of the first check failure + * + * Internal routine for both mte_probe and mte_check. + * Return zero on failure, filling in *fault. + * Return negative on trivial success for tbi disabled. + * Return positive on success with tbi enabled. + */ +static int mte_probe_int(CPUARMState *env, uint32_t desc, uint64_t ptr, + uintptr_t ra, uint64_t *fault) +{ + int mmu_idx, ptr_tag, bit55; + uint64_t ptr_last, prev_page, next_page; + uint64_t tag_first, tag_last; + uint32_t sizem1, tag_count, n, c; + uint8_t *mem1, *mem2; + MMUAccessType type; + + bit55 = extract64(ptr, 55, 1); + *fault = ptr; + + /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */ + if (unlikely(!tbi_check(desc, bit55))) { + return -1; + } + + ptr_tag = allocation_tag_from_addr(ptr); + + if (tcma_check(desc, bit55, ptr_tag)) { + return 1; + } + + mmu_idx = FIELD_EX32(desc, MTEDESC, MIDX); + type = FIELD_EX32(desc, MTEDESC, WRITE) ? MMU_DATA_STORE : MMU_DATA_LOAD; + sizem1 = FIELD_EX32(desc, MTEDESC, SIZEM1); + + /* Find the addr of the end of the access */ + ptr_last = ptr + sizem1; + + /* Round the bounds to the tag granule, and compute the number of tags. */ + tag_first = QEMU_ALIGN_DOWN(ptr, TAG_GRANULE); + tag_last = QEMU_ALIGN_DOWN(ptr_last, TAG_GRANULE); + tag_count = ((tag_last - tag_first) / TAG_GRANULE) + 1; + + /* 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. */ + mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, sizem1 + 1, + MMU_DATA_LOAD, ra); + if (!mem1) { + return 1; + } + /* Perform all of the comparisons. */ + n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, tag_count); + } else { + /* Memory access crosses to next page. */ + mem1 = allocation_tag_mem(env, mmu_idx, ptr, type, next_page - ptr, + MMU_DATA_LOAD, ra); + + mem2 = allocation_tag_mem(env, mmu_idx, next_page, type, + ptr_last - next_page + 1, + MMU_DATA_LOAD, ra); + + /* + * Perform all of the comparisons. + * Note the possible but unlikely case of the operation spanning + * two pages that do not both have tagging enabled. + */ + n = c = (next_page - tag_first) / TAG_GRANULE; + if (mem1) { + n = checkN(mem1, ptr & TAG_GRANULE, ptr_tag, c); + } + if (n == c) { + if (!mem2) { + return 1; + } + n += checkN(mem2, 0, ptr_tag, tag_count - c); + } + } + + if (likely(n == tag_count)) { + return 1; + } + + /* + * If we failed, we know which granule. For the first granule, the + * failure address is @ptr, the first byte accessed. Otherwise the + * failure address is the first byte of the nth granule. + */ + if (n > 0) { + *fault = tag_first + n * TAG_GRANULE; + } + return 0; +} + +uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra) +{ + uint64_t fault; + int ret = mte_probe_int(env, desc, ptr, ra, &fault); + + if (unlikely(ret == 0)) { + mte_check_fail(env, desc, fault, ra); + } else if (ret < 0) { + return ptr; + } + return useronly_clean_ptr(ptr); +} + +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()); +} + +/* + * No-fault version of mte_check, to be used by SVE for MemSingleNF. + * Returns false if the access is Checked and the check failed. This + * is only intended to probe the tag -- the validity of the page must + * be checked beforehand. + */ +bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr) +{ + uint64_t fault; + int ret = mte_probe_int(env, desc, ptr, 0, &fault); + + return ret != 0; +} + +/* + * Perform an MTE checked access for DC_ZVA. + */ +uint64_t HELPER(mte_check_zva)(CPUARMState *env, uint32_t desc, uint64_t ptr) +{ + uintptr_t ra = GETPC(); + int log2_dcz_bytes, log2_tag_bytes; + int mmu_idx, bit55; + intptr_t dcz_bytes, tag_bytes, i; + void *mem; + uint64_t ptr_tag, mem_tag, align_ptr; + + bit55 = extract64(ptr, 55, 1); + + /* If TBI is disabled, the access is unchecked, and ptr is not dirty. */ + if (unlikely(!tbi_check(desc, bit55))) { + return ptr; + } + + ptr_tag = allocation_tag_from_addr(ptr); + + if (tcma_check(desc, bit55, ptr_tag)) { + goto done; + } + + /* + * In arm_cpu_realizefn, we asserted that dcz > LOG2_TAG_GRANULE+1, + * i.e. 32 bytes, which is an unreasonably small dcz anyway, to make + * sure that we can access one complete tag byte here. + */ + log2_dcz_bytes = env_archcpu(env)->dcz_blocksize + 2; + log2_tag_bytes = log2_dcz_bytes - (LOG2_TAG_GRANULE + 1); + dcz_bytes = (intptr_t)1 << log2_dcz_bytes; + tag_bytes = (intptr_t)1 << log2_tag_bytes; + align_ptr = ptr & -dcz_bytes; + + /* + * 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. + */ + 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, ra); + if (!mem) { + goto done; + } + + /* + * Unlike the reasoning for checkN, DC_ZVA is always aligned, and thus + * it is quite easy to perform all of the comparisons at once without + * any extra masking. + * + * The most common zva block size is 64; some of the thunderx cpus use + * a block size of 128. For user-only, aarch64_max_initfn will set the + * block size to 512. Fill out the other cases for future-proofing. + * + * In order to be able to find the first miscompare later, we want the + * tag bytes to be in little-endian order. + */ + switch (log2_tag_bytes) { + case 0: /* zva_blocksize 32 */ + mem_tag = *(uint8_t *)mem; + ptr_tag *= 0x11u; + break; + case 1: /* zva_blocksize 64 */ + mem_tag = cpu_to_le16(*(uint16_t *)mem); + ptr_tag *= 0x1111u; + break; + case 2: /* zva_blocksize 128 */ + mem_tag = cpu_to_le32(*(uint32_t *)mem); + ptr_tag *= 0x11111111u; + break; + case 3: /* zva_blocksize 256 */ + mem_tag = cpu_to_le64(*(uint64_t *)mem); + ptr_tag *= 0x1111111111111111ull; + break; + + default: /* zva_blocksize 512, 1024, 2048 */ + ptr_tag *= 0x1111111111111111ull; + i = 0; + do { + mem_tag = cpu_to_le64(*(uint64_t *)(mem + i)); + if (unlikely(mem_tag != ptr_tag)) { + goto fail; + } + i += 8; + align_ptr += 16 * TAG_GRANULE; + } while (i < tag_bytes); + goto done; + } + + if (likely(mem_tag == ptr_tag)) { + goto done; + } + + fail: + /* Locate the first nibble that differs. */ + i = ctz64(mem_tag ^ ptr_tag) >> 4; + mte_check_fail(env, desc, align_ptr + i * TAG_GRANULE, ra); + + 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/tcg/mte_helper.h b/target/arm/tcg/mte_helper.h new file mode 100644 index 0000000000..1f471fb69b --- /dev/null +++ b/target/arm/tcg/mte_helper.h @@ -0,0 +1,66 @@ +/* + * ARM MemTag operation helpers. + * + * This code is licensed under the GNU GPL v2 or later. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef TARGET_ARM_MTE_H +#define TARGET_ARM_MTE_H + +#include "exec/mmu-access-type.h" + +/** + * 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 + * @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. + * That is, the byte at (addr >> (LOG2_TAG_GRANULE + 1)) contains two + * tags, with the tag at [3:0] for the lower addr and the tag at [7:4] + * for the higher addr. + * + * Here, resolve the physical address from the virtual address, and return + * 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.) + */ +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); + +/** + * load_tag1 - Load 1 tag (nibble) from byte + * @ptr: The tagged address + * @mem: The tag address (packed, 2 tags in byte) + */ +int load_tag1(uint64_t ptr, uint8_t *mem); + +/** + * store_tag1 - Store 1 tag (nibble) into byte + * @ptr: The tagged address + * @mem: The tag address (packed, 2 tags in byte) + * @tag: The tag to be stored in the nibble + */ +void store_tag1(uint64_t ptr, uint8_t *mem, int tag); + +#endif /* TARGET_ARM_MTE_H */ 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..03ebef5ef2 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, @@ -1121,21 +1115,21 @@ static void do_vadc(CPUARMState *env, uint32_t *d, uint32_t *n, uint32_t *m, if (update_flags) { /* Store C, clear NZV. */ - env->vfp.xregs[ARM_VFP_FPSCR] &= ~FPCR_NZCV_MASK; - env->vfp.xregs[ARM_VFP_FPSCR] |= carry_in * FPCR_C; + env->vfp.fpsr &= ~FPSR_NZCV_MASK; + env->vfp.fpsr |= carry_in * FPSR_C; } mve_advance_vpt(env); } void HELPER(mve_vadc)(CPUARMState *env, void *vd, void *vn, void *vm) { - bool carry_in = env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_C; + bool carry_in = env->vfp.fpsr & FPSR_C; do_vadc(env, vd, vn, vm, 0, carry_in, false); } void HELPER(mve_vsbc)(CPUARMState *env, void *vd, void *vn, void *vm) { - bool carry_in = env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_C; + bool carry_in = env->vfp.fpsr & FPSR_C; do_vadc(env, vd, vn, vm, -1, carry_in, false); } @@ -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; } @@ -3349,7 +3343,7 @@ static void do_vcvt_sh(CPUARMState *env, void *vd, void *vm, int top) uint32_t *m = vm; uint16_t r; uint16_t mask = mve_element_mask(env); - bool ieee = !(env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_AHP); + bool ieee = !(env->vfp.fpcr & FPCR_AHP); unsigned e; float_status *fpst; float_status scratch_fpst; @@ -3379,7 +3373,7 @@ static void do_vcvt_hs(CPUARMState *env, void *vd, void *vm, int top) uint16_t *m = vm; uint32_t r; uint16_t mask = mve_element_mask(env); - bool ieee = !(env->vfp.xregs[ARM_VFP_FPSCR] & FPCR_AHP); + bool ieee = !(env->vfp.fpcr & FPCR_AHP); unsigned e; float_status *fpst; float_status scratch_fpst; diff --git a/target/arm/neon-dp.decode b/target/arm/tcg/neon-dp.decode similarity index 95% rename from target/arm/neon-dp.decode rename to target/arm/tcg/neon-dp.decode index fd3a01bfa0..e883c6ab58 100644 --- a/target/arm/neon-dp.decode +++ b/target/arm/tcg/neon-dp.decode @@ -102,37 +102,12 @@ VCGE_U_3s 1111 001 1 0 . .. .... .... 0011 . . . 1 .... @3same VSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 0 .... @3same_rev VSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 0 .... @3same_rev - -# Insns operating on 64-bit elements (size!=0b11 handled elsewhere) -# The _rev suffix indicates that Vn and Vm are reversed (as explained -# by the comment for the @3same_rev format). -@3same_64_rev .... ... . . . 11 .... .... .... . q:1 . . .... \ - &3same vm=%vn_dp vn=%vm_dp vd=%vd_dp size=3 - -{ - VQSHL_S64_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_64_rev - VQSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_rev -} -{ - VQSHL_U64_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_64_rev - VQSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_rev -} -{ - VRSHL_S64_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_64_rev - VRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_rev -} -{ - VRSHL_U64_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_64_rev - VRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_rev -} -{ - VQRSHL_S64_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_64_rev - VQRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_rev -} -{ - VQRSHL_U64_3s 1111 001 1 0 . .. .... .... 0101 . . . 1 .... @3same_64_rev - VQRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 1 .... @3same_rev -} +VQSHL_S_3s 1111 001 0 0 . .. .... .... 0100 . . . 1 .... @3same_rev +VQSHL_U_3s 1111 001 1 0 . .. .... .... 0100 . . . 1 .... @3same_rev +VRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 0 .... @3same_rev +VRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 0 .... @3same_rev +VQRSHL_S_3s 1111 001 0 0 . .. .... .... 0101 . . . 1 .... @3same_rev +VQRSHL_U_3s 1111 001 1 0 . .. .... .... 0101 . . . 1 .... @3same_rev VMAX_S_3s 1111 001 0 0 . .. .... .... 0110 . . . 0 .... @3same VMAX_U_3s 1111 001 1 0 . .. .... .... 0110 . . . 0 .... @3same @@ -316,17 +291,17 @@ VSLI_2sh 1111 001 1 1 . ...... .... 0101 . . . 1 .... @2reg_shl_s VSLI_2sh 1111 001 1 1 . ...... .... 0101 . . . 1 .... @2reg_shl_h VSLI_2sh 1111 001 1 1 . ...... .... 0101 . . . 1 .... @2reg_shl_b -VQSHLU_64_2sh 1111 001 1 1 . ...... .... 0110 . . . 1 .... @2reg_shl_d +VQSHLU_2sh 1111 001 1 1 . ...... .... 0110 . . . 1 .... @2reg_shl_d VQSHLU_2sh 1111 001 1 1 . ...... .... 0110 . . . 1 .... @2reg_shl_s VQSHLU_2sh 1111 001 1 1 . ...... .... 0110 . . . 1 .... @2reg_shl_h VQSHLU_2sh 1111 001 1 1 . ...... .... 0110 . . . 1 .... @2reg_shl_b -VQSHL_S_64_2sh 1111 001 0 1 . ...... .... 0111 . . . 1 .... @2reg_shl_d +VQSHL_S_2sh 1111 001 0 1 . ...... .... 0111 . . . 1 .... @2reg_shl_d VQSHL_S_2sh 1111 001 0 1 . ...... .... 0111 . . . 1 .... @2reg_shl_s VQSHL_S_2sh 1111 001 0 1 . ...... .... 0111 . . . 1 .... @2reg_shl_h VQSHL_S_2sh 1111 001 0 1 . ...... .... 0111 . . . 1 .... @2reg_shl_b -VQSHL_U_64_2sh 1111 001 1 1 . ...... .... 0111 . . . 1 .... @2reg_shl_d +VQSHL_U_2sh 1111 001 1 1 . ...... .... 0111 . . . 1 .... @2reg_shl_d VQSHL_U_2sh 1111 001 1 1 . ...... .... 0111 . . . 1 .... @2reg_shl_s VQSHL_U_2sh 1111 001 1 1 . ...... .... 0111 . . . 1 .... @2reg_shl_h VQSHL_U_2sh 1111 001 1 1 . ...... .... 0111 . . . 1 .... @2reg_shl_b 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/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c new file mode 100644 index 0000000000..93b2076c64 --- /dev/null +++ b/target/arm/tcg/neon_helper.c @@ -0,0 +1,1467 @@ +/* + * ARM NEON vector operations. + * + * Copyright (c) 2007, 2008 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GNU GPL v2. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" +#include "fpu/softfloat.h" +#include "vec_internal.h" + +#define SIGNBIT (uint32_t)0x80000000 +#define SIGNBIT64 ((uint64_t)1 << 63) + +#define SET_QC() env->vfp.qc[0] = 1 + +#define NEON_TYPE1(name, type) \ +typedef struct \ +{ \ + type v1; \ +} neon_##name; +#if HOST_BIG_ENDIAN +#define NEON_TYPE2(name, type) \ +typedef struct \ +{ \ + type v2; \ + type v1; \ +} neon_##name; +#define NEON_TYPE4(name, type) \ +typedef struct \ +{ \ + type v4; \ + type v3; \ + type v2; \ + type v1; \ +} neon_##name; +#else +#define NEON_TYPE2(name, type) \ +typedef struct \ +{ \ + type v1; \ + type v2; \ +} neon_##name; +#define NEON_TYPE4(name, type) \ +typedef struct \ +{ \ + type v1; \ + type v2; \ + type v3; \ + type v4; \ +} neon_##name; +#endif + +NEON_TYPE4(s8, int8_t) +NEON_TYPE4(u8, uint8_t) +NEON_TYPE2(s16, int16_t) +NEON_TYPE2(u16, uint16_t) +NEON_TYPE1(s32, int32_t) +NEON_TYPE1(u32, uint32_t) +#undef NEON_TYPE4 +#undef NEON_TYPE2 +#undef NEON_TYPE1 + +/* Copy from a uint32_t to a vector structure type. */ +#define NEON_UNPACK(vtype, dest, val) do { \ + union { \ + vtype v; \ + uint32_t i; \ + } conv_u; \ + conv_u.i = (val); \ + dest = conv_u.v; \ + } while(0) + +/* Copy from a vector structure type to a uint32_t. */ +#define NEON_PACK(vtype, dest, val) do { \ + union { \ + vtype v; \ + uint32_t i; \ + } conv_u; \ + conv_u.v = (val); \ + dest = conv_u.i; \ + } while(0) + +#define NEON_DO1 \ + NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); +#define NEON_DO2 \ + NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \ + NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2); +#define NEON_DO4 \ + NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \ + NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2); \ + NEON_FN(vdest.v3, vsrc1.v3, vsrc2.v3); \ + NEON_FN(vdest.v4, vsrc1.v4, vsrc2.v4); + +#define NEON_VOP_BODY(vtype, n) \ +{ \ + uint32_t res; \ + vtype vsrc1; \ + vtype vsrc2; \ + vtype vdest; \ + NEON_UNPACK(vtype, vsrc1, arg1); \ + NEON_UNPACK(vtype, vsrc2, arg2); \ + NEON_DO##n; \ + NEON_PACK(vtype, res, vdest); \ + return res; \ +} + +#define NEON_VOP(name, vtype, n) \ +uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \ +NEON_VOP_BODY(vtype, n) + +#define NEON_VOP_ENV(name, vtype, n) \ +uint32_t HELPER(glue(neon_,name))(CPUARMState *env, uint32_t arg1, uint32_t arg2) \ +NEON_VOP_BODY(vtype, n) + +#define NEON_GVEC_VOP2(name, vtype) \ +void HELPER(name)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + vtype *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ + NEON_FN(d[i], n[i], m[i]); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +#define NEON_GVEC_VOP2_ENV(name, vtype) \ +void HELPER(name)(void *vd, void *vn, void *vm, void *venv, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + vtype *d = vd, *n = vn, *m = vm; \ + CPUARMState *env = venv; \ + for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ + NEON_FN(d[i], n[i], m[i]); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +#define NEON_GVEC_VOP2i_ENV(name, vtype) \ +void HELPER(name)(void *vd, void *vn, void *venv, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + int imm = simd_data(desc); \ + vtype *d = vd, *n = vn; \ + CPUARMState *env = venv; \ + for (i = 0; i < opr_sz / sizeof(vtype); i++) { \ + NEON_FN(d[i], n[i], imm); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +/* Pairwise operations. */ +/* For 32-bit elements each segment only contains a single element, so + the elementwise and pairwise operations are the same. */ +#define NEON_PDO2 \ + NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \ + NEON_FN(vdest.v2, vsrc2.v1, vsrc2.v2); +#define NEON_PDO4 \ + NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \ + NEON_FN(vdest.v2, vsrc1.v3, vsrc1.v4); \ + NEON_FN(vdest.v3, vsrc2.v1, vsrc2.v2); \ + NEON_FN(vdest.v4, vsrc2.v3, vsrc2.v4); \ + +#define NEON_POP(name, vtype, n) \ +uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \ +{ \ + uint32_t res; \ + vtype vsrc1; \ + vtype vsrc2; \ + vtype vdest; \ + NEON_UNPACK(vtype, vsrc1, arg1); \ + NEON_UNPACK(vtype, vsrc2, arg2); \ + NEON_PDO##n; \ + NEON_PACK(vtype, res, vdest); \ + return res; \ +} + +/* Unary operators. */ +#define NEON_VOP1(name, vtype, n) \ +uint32_t HELPER(glue(neon_,name))(uint32_t arg) \ +{ \ + vtype vsrc1; \ + vtype vdest; \ + NEON_UNPACK(vtype, vsrc1, arg); \ + NEON_DO##n; \ + NEON_PACK(vtype, arg, vdest); \ + return arg; \ +} + +#define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2 +NEON_POP(pmin_s8, neon_s8, 4) +NEON_POP(pmin_u8, neon_u8, 4) +NEON_POP(pmin_s16, neon_s16, 2) +NEON_POP(pmin_u16, neon_u16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? src1 : src2 +NEON_POP(pmax_s8, neon_s8, 4) +NEON_POP(pmax_u8, neon_u8, 4) +NEON_POP(pmax_s16, neon_s16, 2) +NEON_POP(pmax_u16, neon_u16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, false, NULL)) +NEON_VOP(shl_u16, neon_u16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, false, NULL)) +NEON_VOP(shl_s16, neon_s16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, true, NULL)) +NEON_VOP(rshl_s8, neon_s8, 4) +NEON_GVEC_VOP2(gvec_srshl_b, int8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, true, NULL)) +NEON_VOP(rshl_s16, neon_s16, 2) +NEON_GVEC_VOP2(gvec_srshl_h, int16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, true, NULL)) +NEON_GVEC_VOP2(gvec_srshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, (int8_t)src2, true, NULL)) +NEON_GVEC_VOP2(gvec_srshl_d, int64_t) +#undef NEON_FN + +uint32_t HELPER(neon_rshl_s32)(uint32_t val, uint32_t shift) +{ + return do_sqrshl_bhs(val, (int8_t)shift, 32, true, NULL); +} + +uint64_t HELPER(neon_rshl_s64)(uint64_t val, uint64_t shift) +{ + return do_sqrshl_d(val, (int8_t)shift, true, NULL); +} + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, true, NULL)) +NEON_VOP(rshl_u8, neon_u8, 4) +NEON_GVEC_VOP2(gvec_urshl_b, uint8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, true, NULL)) +NEON_VOP(rshl_u16, neon_u16, 2) +NEON_GVEC_VOP2(gvec_urshl_h, uint16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, true, NULL)) +NEON_GVEC_VOP2(gvec_urshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, (int8_t)src2, true, NULL)) +NEON_GVEC_VOP2(gvec_urshl_d, int64_t) +#undef NEON_FN + +uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shift) +{ + return do_uqrshl_bhs(val, (int8_t)shift, 32, true, NULL); +} + +uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shift) +{ + return do_uqrshl_d(val, (int8_t)shift, true, NULL); +} + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) +NEON_VOP_ENV(qshl_u8, neon_u8, 4) +NEON_GVEC_VOP2_ENV(neon_uqshl_b, uint8_t) +NEON_GVEC_VOP2i_ENV(neon_uqshli_b, uint8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) +NEON_VOP_ENV(qshl_u16, neon_u16, 2) +NEON_GVEC_VOP2_ENV(neon_uqshl_h, uint16_t) +NEON_GVEC_VOP2i_ENV(neon_uqshli_h, uint16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqshl_s, uint32_t) +NEON_GVEC_VOP2i_ENV(neon_uqshli_s, uint32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, (int8_t)src2, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqshl_d, uint64_t) +NEON_GVEC_VOP2i_ENV(neon_uqshli_d, uint64_t) +#undef NEON_FN + +uint32_t HELPER(neon_qshl_u32)(CPUARMState *env, uint32_t val, uint32_t shift) +{ + return do_uqrshl_bhs(val, (int8_t)shift, 32, false, env->vfp.qc); +} + +uint64_t HELPER(neon_qshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) +{ + return do_uqrshl_d(val, (int8_t)shift, false, env->vfp.qc); +} + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) +NEON_VOP_ENV(qshl_s8, neon_s8, 4) +NEON_GVEC_VOP2_ENV(neon_sqshl_b, int8_t) +NEON_GVEC_VOP2i_ENV(neon_sqshli_b, int8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) +NEON_VOP_ENV(qshl_s16, neon_s16, 2) +NEON_GVEC_VOP2_ENV(neon_sqshl_h, int16_t) +NEON_GVEC_VOP2i_ENV(neon_sqshli_h, int16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqshl_s, int32_t) +NEON_GVEC_VOP2i_ENV(neon_sqshli_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, (int8_t)src2, false, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqshl_d, int64_t) +NEON_GVEC_VOP2i_ENV(neon_sqshli_d, int64_t) +#undef NEON_FN + +uint32_t HELPER(neon_qshl_s32)(CPUARMState *env, uint32_t val, uint32_t shift) +{ + return do_sqrshl_bhs(val, (int8_t)shift, 32, false, env->vfp.qc); +} + +uint64_t HELPER(neon_qshl_s64)(CPUARMState *env, uint64_t val, uint64_t shift) +{ + return do_sqrshl_d(val, (int8_t)shift, false, env->vfp.qc); +} + +#define NEON_FN(dest, src1, src2) \ + (dest = do_suqrshl_bhs(src1, (int8_t)src2, 8, false, env->vfp.qc)) +NEON_VOP_ENV(qshlu_s8, neon_s8, 4) +NEON_GVEC_VOP2i_ENV(neon_sqshlui_b, int8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_suqrshl_bhs(src1, (int8_t)src2, 16, false, env->vfp.qc)) +NEON_VOP_ENV(qshlu_s16, neon_s16, 2) +NEON_GVEC_VOP2i_ENV(neon_sqshlui_h, int16_t) +#undef NEON_FN + +uint32_t HELPER(neon_qshlu_s32)(CPUARMState *env, uint32_t val, uint32_t shift) +{ + return do_suqrshl_bhs(val, (int8_t)shift, 32, false, env->vfp.qc); +} + +uint64_t HELPER(neon_qshlu_s64)(CPUARMState *env, uint64_t val, uint64_t shift) +{ + return do_suqrshl_d(val, (int8_t)shift, false, env->vfp.qc); +} + +#define NEON_FN(dest, src1, src2) \ + (dest = do_suqrshl_bhs(src1, (int8_t)src2, 32, false, env->vfp.qc)) +NEON_GVEC_VOP2i_ENV(neon_sqshlui_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_suqrshl_d(src1, (int8_t)src2, false, env->vfp.qc)) +NEON_GVEC_VOP2i_ENV(neon_sqshlui_d, int64_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 8, true, env->vfp.qc)) +NEON_VOP_ENV(qrshl_u8, neon_u8, 4) +NEON_GVEC_VOP2_ENV(neon_uqrshl_b, uint8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 16, true, env->vfp.qc)) +NEON_VOP_ENV(qrshl_u16, neon_u16, 2) +NEON_GVEC_VOP2_ENV(neon_uqrshl_h, uint16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqrshl_s, uint32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, (int8_t)src2, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_uqrshl_d, uint64_t) +#undef NEON_FN + +uint32_t HELPER(neon_qrshl_u32)(CPUARMState *env, uint32_t val, uint32_t shift) +{ + return do_uqrshl_bhs(val, (int8_t)shift, 32, true, env->vfp.qc); +} + +uint64_t HELPER(neon_qrshl_u64)(CPUARMState *env, uint64_t val, uint64_t shift) +{ + return do_uqrshl_d(val, (int8_t)shift, true, env->vfp.qc); +} + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 8, true, env->vfp.qc)) +NEON_VOP_ENV(qrshl_s8, neon_s8, 4) +NEON_GVEC_VOP2_ENV(neon_sqrshl_b, int8_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 16, true, env->vfp.qc)) +NEON_VOP_ENV(qrshl_s16, neon_s16, 2) +NEON_GVEC_VOP2_ENV(neon_sqrshl_h, int16_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqrshl_s, int32_t) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, (int8_t)src2, true, env->vfp.qc)) +NEON_GVEC_VOP2_ENV(neon_sqrshl_d, int64_t) +#undef NEON_FN + +uint32_t HELPER(neon_qrshl_s32)(CPUARMState *env, uint32_t val, uint32_t shift) +{ + return do_sqrshl_bhs(val, (int8_t)shift, 32, true, env->vfp.qc); +} + +uint64_t HELPER(neon_qrshl_s64)(CPUARMState *env, uint64_t val, uint64_t shift) +{ + return do_sqrshl_d(val, (int8_t)shift, true, env->vfp.qc); +} + +uint32_t HELPER(neon_add_u8)(uint32_t a, uint32_t b) +{ + uint32_t mask; + mask = (a ^ b) & 0x80808080u; + a &= ~0x80808080u; + b &= ~0x80808080u; + return (a + b) ^ mask; +} + +uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b) +{ + uint32_t mask; + mask = (a ^ b) & 0x80008000u; + a &= ~0x80008000u; + b &= ~0x80008000u; + return (a + b) ^ mask; +} + +#define NEON_FN(dest, src1, src2) dest = src1 - src2 +NEON_VOP(sub_u8, neon_u8, 4) +NEON_VOP(sub_u16, neon_u16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) dest = src1 * src2 +NEON_VOP(mul_u8, neon_u8, 4) +NEON_VOP(mul_u16, neon_u16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0 +NEON_VOP(tst_u8, neon_u8, 4) +NEON_VOP(tst_u16, neon_u16, 2) +NEON_VOP(tst_u32, neon_u32, 1) +#undef NEON_FN + +/* Count Leading Sign/Zero Bits. */ +static inline int do_clz8(uint8_t x) +{ + int n; + for (n = 8; x; n--) + x >>= 1; + return n; +} + +static inline int do_clz16(uint16_t x) +{ + int n; + for (n = 16; x; n--) + x >>= 1; + return n; +} + +#define NEON_FN(dest, src, dummy) dest = do_clz8(src) +NEON_VOP1(clz_u8, neon_u8, 4) +#undef NEON_FN + +#define NEON_FN(dest, src, dummy) dest = do_clz16(src) +NEON_VOP1(clz_u16, neon_u16, 2) +#undef NEON_FN + +#define NEON_FN(dest, src, dummy) dest = do_clz8((src < 0) ? ~src : src) - 1 +NEON_VOP1(cls_s8, neon_s8, 4) +#undef NEON_FN + +#define NEON_FN(dest, src, dummy) dest = do_clz16((src < 0) ? ~src : src) - 1 +NEON_VOP1(cls_s16, neon_s16, 2) +#undef NEON_FN + +uint32_t HELPER(neon_cls_s32)(uint32_t x) +{ + int count; + if ((int32_t)x < 0) + x = ~x; + for (count = 32; x; count--) + x = x >> 1; + return count - 1; +} + +/* Bit count. */ +uint32_t HELPER(neon_cnt_u8)(uint32_t x) +{ + x = (x & 0x55555555) + ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f); + return x; +} + +/* Reverse bits in each 8 bit word */ +uint32_t HELPER(neon_rbit_u8)(uint32_t x) +{ + x = ((x & 0xf0f0f0f0) >> 4) + | ((x & 0x0f0f0f0f) << 4); + x = ((x & 0x88888888) >> 3) + | ((x & 0x44444444) >> 1) + | ((x & 0x22222222) << 1) + | ((x & 0x11111111) << 3); + return x; +} + +#define NEON_QDMULH16(dest, src1, src2, round) do { \ + uint32_t tmp = (int32_t)(int16_t) src1 * (int16_t) src2; \ + if ((tmp ^ (tmp << 1)) & SIGNBIT) { \ + SET_QC(); \ + tmp = (tmp >> 31) ^ ~SIGNBIT; \ + } else { \ + tmp <<= 1; \ + } \ + if (round) { \ + int32_t old = tmp; \ + tmp += 1 << 15; \ + if ((int32_t)tmp < old) { \ + SET_QC(); \ + tmp = SIGNBIT - 1; \ + } \ + } \ + dest = tmp >> 16; \ + } while(0) +#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0) +NEON_VOP_ENV(qdmulh_s16, neon_s16, 2) +#undef NEON_FN +#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1) +NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2) +#undef NEON_FN +#undef NEON_QDMULH16 + +#define NEON_QDMULH32(dest, src1, src2, round) do { \ + uint64_t tmp = (int64_t)(int32_t) src1 * (int32_t) src2; \ + if ((tmp ^ (tmp << 1)) & SIGNBIT64) { \ + SET_QC(); \ + tmp = (tmp >> 63) ^ ~SIGNBIT64; \ + } else { \ + tmp <<= 1; \ + } \ + if (round) { \ + int64_t old = tmp; \ + tmp += (int64_t)1 << 31; \ + if ((int64_t)tmp < old) { \ + SET_QC(); \ + tmp = SIGNBIT64 - 1; \ + } \ + } \ + dest = tmp >> 32; \ + } while(0) +#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0) +NEON_VOP_ENV(qdmulh_s32, neon_s32, 1) +#undef NEON_FN +#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1) +NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1) +#undef NEON_FN +#undef NEON_QDMULH32 + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_u8)(uint64_t x) +{ + return (x & 0xffu) | ((x >> 8) & 0xff00u) | ((x >> 16) & 0xff0000u) + | ((x >> 24) & 0xff000000u); +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_u16)(uint64_t x) +{ + return (x & 0xffffu) | ((x >> 16) & 0xffff0000u); +} + +uint32_t HELPER(neon_narrow_high_u8)(uint64_t x) +{ + return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00) + | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000); +} + +uint32_t HELPER(neon_narrow_high_u16)(uint64_t x) +{ + return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); +} + +uint32_t HELPER(neon_narrow_round_high_u8)(uint64_t x) +{ + x &= 0xff80ff80ff80ff80ull; + x += 0x0080008000800080ull; + return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00) + | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000); +} + +uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x) +{ + x &= 0xffff8000ffff8000ull; + x += 0x0000800000008000ull; + return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000); +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_unarrow_sat8)(CPUARMState *env, uint64_t x) +{ + uint16_t s; + uint8_t d; + uint32_t res = 0; +#define SAT8(n) \ + s = x >> n; \ + if (s & 0x8000) { \ + SET_QC(); \ + } else { \ + if (s > 0xff) { \ + d = 0xff; \ + SET_QC(); \ + } else { \ + d = s; \ + } \ + res |= (uint32_t)d << (n / 2); \ + } + + SAT8(0); + SAT8(16); + SAT8(32); + SAT8(48); +#undef SAT8 + return res; +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_sat_u8)(CPUARMState *env, uint64_t x) +{ + uint16_t s; + uint8_t d; + uint32_t res = 0; +#define SAT8(n) \ + s = x >> n; \ + if (s > 0xff) { \ + d = 0xff; \ + SET_QC(); \ + } else { \ + d = s; \ + } \ + res |= (uint32_t)d << (n / 2); + + SAT8(0); + SAT8(16); + SAT8(32); + SAT8(48); +#undef SAT8 + return res; +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_sat_s8)(CPUARMState *env, uint64_t x) +{ + int16_t s; + uint8_t d; + uint32_t res = 0; +#define SAT8(n) \ + s = x >> n; \ + if (s != (int8_t)s) { \ + d = (s >> 15) ^ 0x7f; \ + SET_QC(); \ + } else { \ + d = s; \ + } \ + res |= (uint32_t)d << (n / 2); + + SAT8(0); + SAT8(16); + SAT8(32); + SAT8(48); +#undef SAT8 + return res; +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_unarrow_sat16)(CPUARMState *env, uint64_t x) +{ + uint32_t high; + uint32_t low; + low = x; + if (low & 0x80000000) { + low = 0; + SET_QC(); + } else if (low > 0xffff) { + low = 0xffff; + SET_QC(); + } + high = x >> 32; + if (high & 0x80000000) { + high = 0; + SET_QC(); + } else if (high > 0xffff) { + high = 0xffff; + SET_QC(); + } + return deposit32(low, 16, 16, high); +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_sat_u16)(CPUARMState *env, uint64_t x) +{ + uint32_t high; + uint32_t low; + low = x; + if (low > 0xffff) { + low = 0xffff; + SET_QC(); + } + high = x >> 32; + if (high > 0xffff) { + high = 0xffff; + SET_QC(); + } + return deposit32(low, 16, 16, high); +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_sat_s16)(CPUARMState *env, uint64_t x) +{ + int32_t low; + int32_t high; + low = x; + if (low != (int16_t)low) { + low = (low >> 31) ^ 0x7fff; + SET_QC(); + } + high = x >> 32; + if (high != (int16_t)high) { + high = (high >> 31) ^ 0x7fff; + SET_QC(); + } + return deposit32(low, 16, 16, high); +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_unarrow_sat32)(CPUARMState *env, uint64_t x) +{ + if (x & 0x8000000000000000ull) { + SET_QC(); + return 0; + } + if (x > 0xffffffffu) { + SET_QC(); + return 0xffffffffu; + } + return x; +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_sat_u32)(CPUARMState *env, uint64_t x) +{ + if (x > 0xffffffffu) { + SET_QC(); + return 0xffffffffu; + } + return x; +} + +/* Only the low 32-bits of output are significant. */ +uint64_t HELPER(neon_narrow_sat_s32)(CPUARMState *env, uint64_t x) +{ + if ((int64_t)x != (int32_t)x) { + SET_QC(); + return (uint32_t)((int64_t)x >> 63) ^ 0x7fffffff; + } + return (uint32_t)x; +} + +uint64_t HELPER(neon_widen_u8)(uint32_t x) +{ + uint64_t tmp; + uint64_t ret; + ret = (uint8_t)x; + tmp = (uint8_t)(x >> 8); + ret |= tmp << 16; + tmp = (uint8_t)(x >> 16); + ret |= tmp << 32; + tmp = (uint8_t)(x >> 24); + ret |= tmp << 48; + return ret; +} + +uint64_t HELPER(neon_widen_s8)(uint32_t x) +{ + uint64_t tmp; + uint64_t ret; + ret = (uint16_t)(int8_t)x; + tmp = (uint16_t)(int8_t)(x >> 8); + ret |= tmp << 16; + tmp = (uint16_t)(int8_t)(x >> 16); + ret |= tmp << 32; + tmp = (uint16_t)(int8_t)(x >> 24); + ret |= tmp << 48; + return ret; +} + +uint64_t HELPER(neon_widen_u16)(uint32_t x) +{ + uint64_t high = (uint16_t)(x >> 16); + return ((uint16_t)x) | (high << 32); +} + +uint64_t HELPER(neon_widen_s16)(uint32_t x) +{ + uint64_t high = (int16_t)(x >> 16); + return ((uint32_t)(int16_t)x) | (high << 32); +} + +uint64_t HELPER(neon_addl_u16)(uint64_t a, uint64_t b) +{ + uint64_t mask; + mask = (a ^ b) & 0x8000800080008000ull; + a &= ~0x8000800080008000ull; + b &= ~0x8000800080008000ull; + return (a + b) ^ mask; +} + +uint64_t HELPER(neon_addl_u32)(uint64_t a, uint64_t b) +{ + uint64_t mask; + mask = (a ^ b) & 0x8000000080000000ull; + a &= ~0x8000000080000000ull; + b &= ~0x8000000080000000ull; + return (a + b) ^ mask; +} + +uint64_t HELPER(neon_paddl_u16)(uint64_t a, uint64_t b) +{ + uint64_t tmp; + uint64_t tmp2; + + tmp = a & 0x0000ffff0000ffffull; + tmp += (a >> 16) & 0x0000ffff0000ffffull; + tmp2 = b & 0xffff0000ffff0000ull; + tmp2 += (b << 16) & 0xffff0000ffff0000ull; + return ( tmp & 0xffff) + | ((tmp >> 16) & 0xffff0000ull) + | ((tmp2 << 16) & 0xffff00000000ull) + | ( tmp2 & 0xffff000000000000ull); +} + +uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b) +{ + uint32_t low = a + (a >> 32); + uint32_t high = b + (b >> 32); + return low + ((uint64_t)high << 32); +} + +uint64_t HELPER(neon_subl_u16)(uint64_t a, uint64_t b) +{ + uint64_t mask; + mask = (a ^ ~b) & 0x8000800080008000ull; + a |= 0x8000800080008000ull; + b &= ~0x8000800080008000ull; + return (a - b) ^ mask; +} + +uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b) +{ + uint64_t mask; + mask = (a ^ ~b) & 0x8000000080000000ull; + a |= 0x8000000080000000ull; + b &= ~0x8000000080000000ull; + return (a - b) ^ mask; +} + +uint64_t HELPER(neon_addl_saturate_s32)(CPUARMState *env, uint64_t a, uint64_t b) +{ + uint32_t x, y; + uint32_t low, high; + + x = a; + y = b; + low = x + y; + if (((low ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) { + SET_QC(); + low = ((int32_t)x >> 31) ^ ~SIGNBIT; + } + x = a >> 32; + y = b >> 32; + high = x + y; + if (((high ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) { + SET_QC(); + high = ((int32_t)x >> 31) ^ ~SIGNBIT; + } + return low | ((uint64_t)high << 32); +} + +uint64_t HELPER(neon_addl_saturate_s64)(CPUARMState *env, uint64_t a, uint64_t b) +{ + uint64_t result; + + result = a + b; + if (((result ^ a) & SIGNBIT64) && !((a ^ b) & SIGNBIT64)) { + SET_QC(); + result = ((int64_t)a >> 63) ^ ~SIGNBIT64; + } + return result; +} + +/* We have to do the arithmetic in a larger type than + * the input type, because for example with a signed 32 bit + * op the absolute difference can overflow a signed 32 bit value. + */ +#define DO_ABD(dest, x, y, intype, arithtype) do { \ + arithtype tmp_x = (intype)(x); \ + arithtype tmp_y = (intype)(y); \ + dest = ((tmp_x > tmp_y) ? tmp_x - tmp_y : tmp_y - tmp_x); \ + } while(0) + +uint64_t HELPER(neon_abdl_u16)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + DO_ABD(result, a, b, uint8_t, uint32_t); + DO_ABD(tmp, a >> 8, b >> 8, uint8_t, uint32_t); + result |= tmp << 16; + DO_ABD(tmp, a >> 16, b >> 16, uint8_t, uint32_t); + result |= tmp << 32; + DO_ABD(tmp, a >> 24, b >> 24, uint8_t, uint32_t); + result |= tmp << 48; + return result; +} + +uint64_t HELPER(neon_abdl_s16)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + DO_ABD(result, a, b, int8_t, int32_t); + DO_ABD(tmp, a >> 8, b >> 8, int8_t, int32_t); + result |= tmp << 16; + DO_ABD(tmp, a >> 16, b >> 16, int8_t, int32_t); + result |= tmp << 32; + DO_ABD(tmp, a >> 24, b >> 24, int8_t, int32_t); + result |= tmp << 48; + return result; +} + +uint64_t HELPER(neon_abdl_u32)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + DO_ABD(result, a, b, uint16_t, uint32_t); + DO_ABD(tmp, a >> 16, b >> 16, uint16_t, uint32_t); + return result | (tmp << 32); +} + +uint64_t HELPER(neon_abdl_s32)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + DO_ABD(result, a, b, int16_t, int32_t); + DO_ABD(tmp, a >> 16, b >> 16, int16_t, int32_t); + return result | (tmp << 32); +} + +uint64_t HELPER(neon_abdl_u64)(uint32_t a, uint32_t b) +{ + uint64_t result; + DO_ABD(result, a, b, uint32_t, uint64_t); + return result; +} + +uint64_t HELPER(neon_abdl_s64)(uint32_t a, uint32_t b) +{ + uint64_t result; + DO_ABD(result, a, b, int32_t, int64_t); + return result; +} +#undef DO_ABD + +/* Widening multiply. Named type is the source type. */ +#define DO_MULL(dest, x, y, type1, type2) do { \ + type1 tmp_x = x; \ + type1 tmp_y = y; \ + dest = (type2)((type2)tmp_x * (type2)tmp_y); \ + } while(0) + +uint64_t HELPER(neon_mull_u8)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + + DO_MULL(result, a, b, uint8_t, uint16_t); + DO_MULL(tmp, a >> 8, b >> 8, uint8_t, uint16_t); + result |= tmp << 16; + DO_MULL(tmp, a >> 16, b >> 16, uint8_t, uint16_t); + result |= tmp << 32; + DO_MULL(tmp, a >> 24, b >> 24, uint8_t, uint16_t); + result |= tmp << 48; + return result; +} + +uint64_t HELPER(neon_mull_s8)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + + DO_MULL(result, a, b, int8_t, uint16_t); + DO_MULL(tmp, a >> 8, b >> 8, int8_t, uint16_t); + result |= tmp << 16; + DO_MULL(tmp, a >> 16, b >> 16, int8_t, uint16_t); + result |= tmp << 32; + DO_MULL(tmp, a >> 24, b >> 24, int8_t, uint16_t); + result |= tmp << 48; + return result; +} + +uint64_t HELPER(neon_mull_u16)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + + DO_MULL(result, a, b, uint16_t, uint32_t); + DO_MULL(tmp, a >> 16, b >> 16, uint16_t, uint32_t); + return result | (tmp << 32); +} + +uint64_t HELPER(neon_mull_s16)(uint32_t a, uint32_t b) +{ + uint64_t tmp; + uint64_t result; + + DO_MULL(result, a, b, int16_t, uint32_t); + DO_MULL(tmp, a >> 16, b >> 16, int16_t, uint32_t); + return result | (tmp << 32); +} + +uint64_t HELPER(neon_negl_u16)(uint64_t x) +{ + uint16_t tmp; + uint64_t result; + result = (uint16_t)-x; + tmp = -(x >> 16); + result |= (uint64_t)tmp << 16; + tmp = -(x >> 32); + result |= (uint64_t)tmp << 32; + tmp = -(x >> 48); + result |= (uint64_t)tmp << 48; + return result; +} + +uint64_t HELPER(neon_negl_u32)(uint64_t x) +{ + uint32_t low = -x; + uint32_t high = -(x >> 32); + return low | ((uint64_t)high << 32); +} + +/* Saturating sign manipulation. */ +/* ??? Make these use NEON_VOP1 */ +#define DO_QABS8(x) do { \ + if (x == (int8_t)0x80) { \ + x = 0x7f; \ + SET_QC(); \ + } else if (x < 0) { \ + x = -x; \ + }} while (0) +uint32_t HELPER(neon_qabs_s8)(CPUARMState *env, uint32_t x) +{ + neon_s8 vec; + NEON_UNPACK(neon_s8, vec, x); + DO_QABS8(vec.v1); + DO_QABS8(vec.v2); + DO_QABS8(vec.v3); + DO_QABS8(vec.v4); + NEON_PACK(neon_s8, x, vec); + return x; +} +#undef DO_QABS8 + +#define DO_QNEG8(x) do { \ + if (x == (int8_t)0x80) { \ + x = 0x7f; \ + SET_QC(); \ + } else { \ + x = -x; \ + }} while (0) +uint32_t HELPER(neon_qneg_s8)(CPUARMState *env, uint32_t x) +{ + neon_s8 vec; + NEON_UNPACK(neon_s8, vec, x); + DO_QNEG8(vec.v1); + DO_QNEG8(vec.v2); + DO_QNEG8(vec.v3); + DO_QNEG8(vec.v4); + NEON_PACK(neon_s8, x, vec); + return x; +} +#undef DO_QNEG8 + +#define DO_QABS16(x) do { \ + if (x == (int16_t)0x8000) { \ + x = 0x7fff; \ + SET_QC(); \ + } else if (x < 0) { \ + x = -x; \ + }} while (0) +uint32_t HELPER(neon_qabs_s16)(CPUARMState *env, uint32_t x) +{ + neon_s16 vec; + NEON_UNPACK(neon_s16, vec, x); + DO_QABS16(vec.v1); + DO_QABS16(vec.v2); + NEON_PACK(neon_s16, x, vec); + return x; +} +#undef DO_QABS16 + +#define DO_QNEG16(x) do { \ + if (x == (int16_t)0x8000) { \ + x = 0x7fff; \ + SET_QC(); \ + } else { \ + x = -x; \ + }} while (0) +uint32_t HELPER(neon_qneg_s16)(CPUARMState *env, uint32_t x) +{ + neon_s16 vec; + NEON_UNPACK(neon_s16, vec, x); + DO_QNEG16(vec.v1); + DO_QNEG16(vec.v2); + NEON_PACK(neon_s16, x, vec); + return x; +} +#undef DO_QNEG16 + +uint32_t HELPER(neon_qabs_s32)(CPUARMState *env, uint32_t x) +{ + if (x == SIGNBIT) { + SET_QC(); + x = ~SIGNBIT; + } else if ((int32_t)x < 0) { + x = -x; + } + return x; +} + +uint32_t HELPER(neon_qneg_s32)(CPUARMState *env, uint32_t x) +{ + if (x == SIGNBIT) { + SET_QC(); + x = ~SIGNBIT; + } else { + x = -x; + } + return x; +} + +uint64_t HELPER(neon_qabs_s64)(CPUARMState *env, uint64_t x) +{ + if (x == SIGNBIT64) { + SET_QC(); + x = ~SIGNBIT64; + } else if ((int64_t)x < 0) { + x = -x; + } + return x; +} + +uint64_t HELPER(neon_qneg_s64)(CPUARMState *env, uint64_t x) +{ + if (x == SIGNBIT64) { + SET_QC(); + x = ~SIGNBIT64; + } else { + x = -x; + } + return x; +} + +/* NEON Float helpers. */ + +/* Floating point comparisons produce an integer result. + * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. + * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. + */ +uint32_t HELPER(neon_ceq_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float32_eq_quiet(make_float32(a), make_float32(b), fpst); +} + +uint32_t HELPER(neon_cge_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float32_le(make_float32(b), make_float32(a), fpst); +} + +uint32_t HELPER(neon_cgt_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + return -float32_lt(make_float32(b), make_float32(a), fpst); +} + +uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float32 f0 = float32_abs(make_float32(a)); + float32 f1 = float32_abs(make_float32(b)); + return -float32_le(f1, f0, fpst); +} + +uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float32 f0 = float32_abs(make_float32(a)); + float32 f1 = float32_abs(make_float32(b)); + return -float32_lt(f1, f0, fpst); +} + +uint64_t HELPER(neon_acge_f64)(uint64_t a, uint64_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float64 f0 = float64_abs(make_float64(a)); + float64 f1 = float64_abs(make_float64(b)); + return -float64_le(f1, f0, fpst); +} + +uint64_t HELPER(neon_acgt_f64)(uint64_t a, uint64_t b, void *fpstp) +{ + float_status *fpst = fpstp; + float64 f0 = float64_abs(make_float64(a)); + float64 f1 = float64_abs(make_float64(b)); + return -float64_lt(f1, f0, fpst); +} + +#define ELEM(V, N, SIZE) (((V) >> ((N) * (SIZE))) & ((1ull << (SIZE)) - 1)) + +void HELPER(neon_qunzip8)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd0 = rd[0], zd1 = rd[1]; + uint64_t zm0 = rm[0], zm1 = rm[1]; + + uint64_t d0 = ELEM(zd0, 0, 8) | (ELEM(zd0, 2, 8) << 8) + | (ELEM(zd0, 4, 8) << 16) | (ELEM(zd0, 6, 8) << 24) + | (ELEM(zd1, 0, 8) << 32) | (ELEM(zd1, 2, 8) << 40) + | (ELEM(zd1, 4, 8) << 48) | (ELEM(zd1, 6, 8) << 56); + uint64_t d1 = ELEM(zm0, 0, 8) | (ELEM(zm0, 2, 8) << 8) + | (ELEM(zm0, 4, 8) << 16) | (ELEM(zm0, 6, 8) << 24) + | (ELEM(zm1, 0, 8) << 32) | (ELEM(zm1, 2, 8) << 40) + | (ELEM(zm1, 4, 8) << 48) | (ELEM(zm1, 6, 8) << 56); + uint64_t m0 = ELEM(zd0, 1, 8) | (ELEM(zd0, 3, 8) << 8) + | (ELEM(zd0, 5, 8) << 16) | (ELEM(zd0, 7, 8) << 24) + | (ELEM(zd1, 1, 8) << 32) | (ELEM(zd1, 3, 8) << 40) + | (ELEM(zd1, 5, 8) << 48) | (ELEM(zd1, 7, 8) << 56); + uint64_t m1 = ELEM(zm0, 1, 8) | (ELEM(zm0, 3, 8) << 8) + | (ELEM(zm0, 5, 8) << 16) | (ELEM(zm0, 7, 8) << 24) + | (ELEM(zm1, 1, 8) << 32) | (ELEM(zm1, 3, 8) << 40) + | (ELEM(zm1, 5, 8) << 48) | (ELEM(zm1, 7, 8) << 56); + + rm[0] = m0; + rm[1] = m1; + rd[0] = d0; + rd[1] = d1; +} + +void HELPER(neon_qunzip16)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd0 = rd[0], zd1 = rd[1]; + uint64_t zm0 = rm[0], zm1 = rm[1]; + + uint64_t d0 = ELEM(zd0, 0, 16) | (ELEM(zd0, 2, 16) << 16) + | (ELEM(zd1, 0, 16) << 32) | (ELEM(zd1, 2, 16) << 48); + uint64_t d1 = ELEM(zm0, 0, 16) | (ELEM(zm0, 2, 16) << 16) + | (ELEM(zm1, 0, 16) << 32) | (ELEM(zm1, 2, 16) << 48); + uint64_t m0 = ELEM(zd0, 1, 16) | (ELEM(zd0, 3, 16) << 16) + | (ELEM(zd1, 1, 16) << 32) | (ELEM(zd1, 3, 16) << 48); + uint64_t m1 = ELEM(zm0, 1, 16) | (ELEM(zm0, 3, 16) << 16) + | (ELEM(zm1, 1, 16) << 32) | (ELEM(zm1, 3, 16) << 48); + + rm[0] = m0; + rm[1] = m1; + rd[0] = d0; + rd[1] = d1; +} + +void HELPER(neon_qunzip32)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd0 = rd[0], zd1 = rd[1]; + uint64_t zm0 = rm[0], zm1 = rm[1]; + + uint64_t d0 = ELEM(zd0, 0, 32) | (ELEM(zd1, 0, 32) << 32); + uint64_t d1 = ELEM(zm0, 0, 32) | (ELEM(zm1, 0, 32) << 32); + uint64_t m0 = ELEM(zd0, 1, 32) | (ELEM(zd1, 1, 32) << 32); + uint64_t m1 = ELEM(zm0, 1, 32) | (ELEM(zm1, 1, 32) << 32); + + rm[0] = m0; + rm[1] = m1; + rd[0] = d0; + rd[1] = d1; +} + +void HELPER(neon_unzip8)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd = rd[0], zm = rm[0]; + + uint64_t d0 = ELEM(zd, 0, 8) | (ELEM(zd, 2, 8) << 8) + | (ELEM(zd, 4, 8) << 16) | (ELEM(zd, 6, 8) << 24) + | (ELEM(zm, 0, 8) << 32) | (ELEM(zm, 2, 8) << 40) + | (ELEM(zm, 4, 8) << 48) | (ELEM(zm, 6, 8) << 56); + uint64_t m0 = ELEM(zd, 1, 8) | (ELEM(zd, 3, 8) << 8) + | (ELEM(zd, 5, 8) << 16) | (ELEM(zd, 7, 8) << 24) + | (ELEM(zm, 1, 8) << 32) | (ELEM(zm, 3, 8) << 40) + | (ELEM(zm, 5, 8) << 48) | (ELEM(zm, 7, 8) << 56); + + rm[0] = m0; + rd[0] = d0; +} + +void HELPER(neon_unzip16)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd = rd[0], zm = rm[0]; + + uint64_t d0 = ELEM(zd, 0, 16) | (ELEM(zd, 2, 16) << 16) + | (ELEM(zm, 0, 16) << 32) | (ELEM(zm, 2, 16) << 48); + uint64_t m0 = ELEM(zd, 1, 16) | (ELEM(zd, 3, 16) << 16) + | (ELEM(zm, 1, 16) << 32) | (ELEM(zm, 3, 16) << 48); + + rm[0] = m0; + rd[0] = d0; +} + +void HELPER(neon_qzip8)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd0 = rd[0], zd1 = rd[1]; + uint64_t zm0 = rm[0], zm1 = rm[1]; + + uint64_t d0 = ELEM(zd0, 0, 8) | (ELEM(zm0, 0, 8) << 8) + | (ELEM(zd0, 1, 8) << 16) | (ELEM(zm0, 1, 8) << 24) + | (ELEM(zd0, 2, 8) << 32) | (ELEM(zm0, 2, 8) << 40) + | (ELEM(zd0, 3, 8) << 48) | (ELEM(zm0, 3, 8) << 56); + uint64_t d1 = ELEM(zd0, 4, 8) | (ELEM(zm0, 4, 8) << 8) + | (ELEM(zd0, 5, 8) << 16) | (ELEM(zm0, 5, 8) << 24) + | (ELEM(zd0, 6, 8) << 32) | (ELEM(zm0, 6, 8) << 40) + | (ELEM(zd0, 7, 8) << 48) | (ELEM(zm0, 7, 8) << 56); + uint64_t m0 = ELEM(zd1, 0, 8) | (ELEM(zm1, 0, 8) << 8) + | (ELEM(zd1, 1, 8) << 16) | (ELEM(zm1, 1, 8) << 24) + | (ELEM(zd1, 2, 8) << 32) | (ELEM(zm1, 2, 8) << 40) + | (ELEM(zd1, 3, 8) << 48) | (ELEM(zm1, 3, 8) << 56); + uint64_t m1 = ELEM(zd1, 4, 8) | (ELEM(zm1, 4, 8) << 8) + | (ELEM(zd1, 5, 8) << 16) | (ELEM(zm1, 5, 8) << 24) + | (ELEM(zd1, 6, 8) << 32) | (ELEM(zm1, 6, 8) << 40) + | (ELEM(zd1, 7, 8) << 48) | (ELEM(zm1, 7, 8) << 56); + + rm[0] = m0; + rm[1] = m1; + rd[0] = d0; + rd[1] = d1; +} + +void HELPER(neon_qzip16)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd0 = rd[0], zd1 = rd[1]; + uint64_t zm0 = rm[0], zm1 = rm[1]; + + uint64_t d0 = ELEM(zd0, 0, 16) | (ELEM(zm0, 0, 16) << 16) + | (ELEM(zd0, 1, 16) << 32) | (ELEM(zm0, 1, 16) << 48); + uint64_t d1 = ELEM(zd0, 2, 16) | (ELEM(zm0, 2, 16) << 16) + | (ELEM(zd0, 3, 16) << 32) | (ELEM(zm0, 3, 16) << 48); + uint64_t m0 = ELEM(zd1, 0, 16) | (ELEM(zm1, 0, 16) << 16) + | (ELEM(zd1, 1, 16) << 32) | (ELEM(zm1, 1, 16) << 48); + uint64_t m1 = ELEM(zd1, 2, 16) | (ELEM(zm1, 2, 16) << 16) + | (ELEM(zd1, 3, 16) << 32) | (ELEM(zm1, 3, 16) << 48); + + rm[0] = m0; + rm[1] = m1; + rd[0] = d0; + rd[1] = d1; +} + +void HELPER(neon_qzip32)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd0 = rd[0], zd1 = rd[1]; + uint64_t zm0 = rm[0], zm1 = rm[1]; + + uint64_t d0 = ELEM(zd0, 0, 32) | (ELEM(zm0, 0, 32) << 32); + uint64_t d1 = ELEM(zd0, 1, 32) | (ELEM(zm0, 1, 32) << 32); + uint64_t m0 = ELEM(zd1, 0, 32) | (ELEM(zm1, 0, 32) << 32); + uint64_t m1 = ELEM(zd1, 1, 32) | (ELEM(zm1, 1, 32) << 32); + + rm[0] = m0; + rm[1] = m1; + rd[0] = d0; + rd[1] = d1; +} + +void HELPER(neon_zip8)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd = rd[0], zm = rm[0]; + + uint64_t d0 = ELEM(zd, 0, 8) | (ELEM(zm, 0, 8) << 8) + | (ELEM(zd, 1, 8) << 16) | (ELEM(zm, 1, 8) << 24) + | (ELEM(zd, 2, 8) << 32) | (ELEM(zm, 2, 8) << 40) + | (ELEM(zd, 3, 8) << 48) | (ELEM(zm, 3, 8) << 56); + uint64_t m0 = ELEM(zd, 4, 8) | (ELEM(zm, 4, 8) << 8) + | (ELEM(zd, 5, 8) << 16) | (ELEM(zm, 5, 8) << 24) + | (ELEM(zd, 6, 8) << 32) | (ELEM(zm, 6, 8) << 40) + | (ELEM(zd, 7, 8) << 48) | (ELEM(zm, 7, 8) << 56); + + rm[0] = m0; + rd[0] = d0; +} + +void HELPER(neon_zip16)(void *vd, void *vm) +{ + uint64_t *rd = vd, *rm = vm; + uint64_t zd = rd[0], zm = rm[0]; + + uint64_t d0 = ELEM(zd, 0, 16) | (ELEM(zm, 0, 16) << 16) + | (ELEM(zd, 1, 16) << 32) | (ELEM(zm, 1, 16) << 48); + uint64_t m0 = ELEM(zd, 2, 16) | (ELEM(zm, 2, 16) << 16) + | (ELEM(zd, 3, 16) << 32) | (ELEM(zm, 3, 16) << 48); + + rm[0] = m0; + rd[0] = d0; +} diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c new file mode 100644 index 0000000000..1ecb465988 --- /dev/null +++ b/target/arm/tcg/op_helper.c @@ -0,0 +1,1251 @@ +/* + * ARM helper routines + * + * Copyright (c) 2005-2007 CodeSourcery, LLC + * + * 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 "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" + +#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) +{ + CPUState *cs = env_cpu(env); + + if (target_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) { + /* + * Redirect NS EL1 exceptions to NS EL2. These are reported with + * their original syndrome register value, with the exception of + * SIMD/FP access traps, which are reported as uncategorized + * (see DDI0478C.a D1.10.4) + */ + target_el = 2; + if (syn_get_ec(syndrome) == EC_ADVSIMDFPACCESSTRAP) { + syndrome = syn_uncategorized(); + } + } + + assert(!excp_is_internal(excp)); + cs->exception_index = excp; + env->exception.syndrome = syndrome; + env->exception.target_el = target_el; + cpu_loop_exit(cs); +} + +void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, + uint32_t target_el, uintptr_t ra) +{ + CPUState *cs = env_cpu(env); + + /* + * restore_state_to_opc() will set env->exception.syndrome, so + * 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); + raise_exception(env, excp, syndrome, target_el); +} + +uint64_t HELPER(neon_tbl)(CPUARMState *env, uint32_t desc, + uint64_t ireg, uint64_t def) +{ + uint64_t tmp, val = 0; + uint32_t maxindex = ((desc & 3) + 1) * 8; + uint32_t base_reg = desc >> 2; + uint32_t shift, index, reg; + + for (shift = 0; shift < 64; shift += 8) { + index = (ireg >> shift) & 0xff; + if (index < maxindex) { + reg = base_reg + (index >> 3); + tmp = *aa32_vfp_dreg(env, reg); + tmp = ((tmp >> ((index & 7) << 3)) & 0xff) << shift; + } else { + tmp = def & (0xffull << shift); + } + val |= tmp; + } + return val; +} + +void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue) +{ + /* + * Perform the v8M stack limit check for SP updates from translated code, + * raising an exception if the limit is breached. + */ + if (newvalue < v7m_sp_limit(env)) { + /* + * Stack limit exceptions are a rare case, so rather than syncing + * PC/condbits before the call, we use raise_exception_ra() so + * that cpu_restore_state() will sort them out. + */ + raise_exception_ra(env, EXCP_STKOF, 0, 1, GETPC()); + } +} + +/* 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; + if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) + env->QF = 1; + return res; +} + +uint32_t HELPER(add_saturate)(CPUARMState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { + env->QF = 1; + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; +} + +uint32_t HELPER(sub_saturate)(CPUARMState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a - b; + if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { + env->QF = 1; + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; +} + +uint32_t HELPER(add_usaturate)(CPUARMState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (res < a) { + env->QF = 1; + res = ~0; + } + return res; +} + +uint32_t HELPER(sub_usaturate)(CPUARMState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a - b; + if (res > a) { + env->QF = 1; + res = 0; + } + return res; +} + +/* Signed saturation. */ +static inline uint32_t do_ssat(CPUARMState *env, int32_t val, int shift) +{ + int32_t top; + uint32_t mask; + + top = val >> shift; + mask = (1u << shift) - 1; + if (top > 0) { + env->QF = 1; + return mask; + } else if (top < -1) { + env->QF = 1; + return ~mask; + } + return val; +} + +/* Unsigned saturation. */ +static inline uint32_t do_usat(CPUARMState *env, int32_t val, int shift) +{ + uint32_t max; + + max = (1u << shift) - 1; + if (val < 0) { + env->QF = 1; + return 0; + } else if (val > max) { + env->QF = 1; + return max; + } + return val; +} + +/* Signed saturate. */ +uint32_t HELPER(ssat)(CPUARMState *env, uint32_t x, uint32_t shift) +{ + return do_ssat(env, x, shift); +} + +/* Dual halfword signed saturate. */ +uint32_t HELPER(ssat16)(CPUARMState *env, uint32_t x, uint32_t shift) +{ + uint32_t res; + + res = (uint16_t)do_ssat(env, (int16_t)x, shift); + res |= do_ssat(env, ((int32_t)x) >> 16, shift) << 16; + return res; +} + +/* Unsigned saturate. */ +uint32_t HELPER(usat)(CPUARMState *env, uint32_t x, uint32_t shift) +{ + return do_usat(env, x, shift); +} + +/* Dual halfword unsigned saturate. */ +uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift) +{ + uint32_t res; + + res = (uint16_t)do_usat(env, (int16_t)x, shift); + res |= do_usat(env, ((int32_t)x) >> 16, shift) << 16; + return res; +} + +void HELPER(setend)(CPUARMState *env) +{ + env->uncached_cpsr ^= CPSR_E; + arm_rebuild_hflags(env); +} + +void HELPER(check_bxj_trap)(CPUARMState *env, uint32_t rm) +{ + /* + * Only called if in NS EL0 or EL1 for a BXJ for a v7A CPU; + * check if HSTR.TJDBX means we need to trap to EL2. + */ + if (env->cp15.hstr_el2 & HSTR_TJDBX) { + /* + * We know the condition code check passed, so take the IMPDEF + * choice to always report CV=1 COND 0xe + */ + uint32_t syn = syn_bxjtrap(1, 0xe, rm); + raise_exception_ra(env, EXCP_HYP_TRAP, syn, 2, GETPC()); + } +} + +#ifndef CONFIG_USER_ONLY +/* Function checks whether WFx (WFI/WFE) instructions are set up to be trapped. + * The function returns the target EL (1-3) if the instruction is to be trapped; + * otherwise it returns 0 indicating it is not trapped. + */ +static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) +{ + int cur_el = arm_current_el(env); + uint64_t mask; + + if (arm_feature(env, ARM_FEATURE_M)) { + /* M profile cores can never trap WFI/WFE. */ + return 0; + } + + /* If we are currently in EL0 then we need to check if SCTLR is set up for + * WFx instructions being trapped to EL1. These trap bits don't exist in v7. + */ + if (cur_el < 1 && arm_feature(env, ARM_FEATURE_V8)) { + int target_el; + + mask = is_wfe ? SCTLR_nTWE : SCTLR_nTWI; + if (arm_is_secure_below_el3(env) && !arm_el_is_aa64(env, 3)) { + /* Secure EL0 and Secure PL1 is at EL3 */ + target_el = 3; + } else { + target_el = 1; + } + + if (!(env->cp15.sctlr_el[target_el] & mask)) { + return target_el; + } + } + + /* We are not trapping to EL1; trap to EL2 if HCR_EL2 requires it + * No need for ARM_FEATURE check as if HCR_EL2 doesn't exist the + * bits will be zero indicating no trap. + */ + if (cur_el < 2) { + mask = is_wfe ? HCR_TWE : HCR_TWI; + if (arm_hcr_el2_eff(env) & mask) { + return 2; + } + } + + /* We are not trapping to EL1 or EL2; trap to EL3 if SCR_EL3 requires it */ + if (cur_el < 3) { + mask = (is_wfe) ? SCR_TWE : SCR_TWI; + if (env->cp15.scr_el3 & mask) { + return 3; + } + } + + return 0; +} +#endif + +void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) +{ +#ifdef CONFIG_USER_ONLY + /* + * WFI in the user-mode emulator is technically permitted but not + * something any real-world code would do. AArch64 Linux kernels + * trap it via SCTRL_EL1.nTWI and make it an (expensive) NOP; + * AArch32 kernels don't trap it so it will delay a bit. + * For QEMU, make it NOP here, because trying to raise EXCP_HLT + * would trigger an abort. + */ + return; +#else + CPUState *cs = env_cpu(env); + int target_el = check_wfx_trap(env, false); + + if (cpu_has_work(cs)) { + /* Don't bother to go into our "low power state" if + * we would just wake up immediately. + */ + return; + } + + if (target_el) { + if (env->aarch64) { + env->pc -= insn_len; + } else { + env->regs[15] -= insn_len; + } + + raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, insn_len == 2), + target_el); + } + + cs->exception_index = EXCP_HLT; + cs->halted = 1; + cpu_loop_exit(cs); +#endif +} + +void HELPER(wfit)(CPUARMState *env, uint64_t timeout) +{ +#ifdef CONFIG_USER_ONLY + /* + * WFI in the user-mode emulator is technically permitted but not + * something any real-world code would do. AArch64 Linux kernels + * trap it via SCTRL_EL1.nTWI and make it an (expensive) NOP; + * AArch32 kernels don't trap it so it will delay a bit. + * For QEMU, make it NOP here, because trying to raise EXCP_HLT + * would trigger an abort. + */ + return; +#else + ARMCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + int target_el = check_wfx_trap(env, false); + /* The WFIT should time out when CNTVCT_EL0 >= the specified value. */ + uint64_t cntval = gt_get_countervalue(env); + uint64_t offset = gt_virt_cnt_offset(env); + uint64_t cntvct = cntval - offset; + uint64_t nexttick; + + if (cpu_has_work(cs) || cntvct >= timeout) { + /* + * Don't bother to go into our "low power state" if + * we would just wake up immediately. + */ + return; + } + + if (target_el) { + env->pc -= 4; + raise_exception(env, EXCP_UDEF, syn_wfx(1, 0xe, 0, false), + target_el); + } + + if (uadd64_overflow(timeout, offset, &nexttick)) { + nexttick = UINT64_MAX; + } + if (nexttick > INT64_MAX / gt_cntfrq_period_ns(cpu)) { + /* + * If the timeout is too long for the signed 64-bit range + * of a QEMUTimer, let it expire early. + */ + timer_mod_ns(cpu->wfxt_timer, INT64_MAX); + } else { + timer_mod(cpu->wfxt_timer, nexttick); + } + cs->exception_index = EXCP_HLT; + cs->halted = 1; + cpu_loop_exit(cs); +#endif +} + +void HELPER(wfe)(CPUARMState *env) +{ + /* This is a hint instruction that is semantically different + * from YIELD even though we currently implement it identically. + * Don't actually halt the CPU, just yield back to top + * level loop. This is not going into a "low power state" + * (ie halting until some event occurs), so we never take + * a configurable trap to a different exception level. + */ + HELPER(yield)(env); +} + +void HELPER(yield)(CPUARMState *env) +{ + CPUState *cs = env_cpu(env); + + /* This is a non-trappable hint instruction that generally indicates + * that the guest is currently busy-looping. Yield control back to the + * top level loop so that a more deserving VCPU has a chance to run. + */ + cs->exception_index = EXCP_YIELD; + cpu_loop_exit(cs); +} + +/* Raise an internal-to-QEMU exception. This is limited to only + * 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*). + */ +void HELPER(exception_internal)(CPUARMState *env, uint32_t excp) +{ + CPUState *cs = env_cpu(env); + + assert(excp_is_internal(excp)); + cs->exception_index = excp; + cpu_loop_exit(cs); +} + +/* Raise an exception with the specified syndrome register value */ +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 exception with the specified syndrome register value + * to the default target el. + */ +void HELPER(exception_with_syndrome)(CPUARMState *env, uint32_t excp, + uint32_t syndrome) +{ + raise_exception(env, excp, syndrome, exception_target_el(env)); +} + +uint32_t HELPER(cpsr_read)(CPUARMState *env) +{ + return cpsr_read(env) & ~CPSR_EXEC; +} + +void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) +{ + cpsr_write(env, val, mask, CPSRWriteByInstr); + /* TODO: Not all cpsr bits are relevant to hflags. */ + arm_rebuild_hflags(env); +} + +/* Write the CPSR for a 32-bit exception return */ +void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) +{ + uint32_t mask; + + bql_lock(); + arm_call_pre_el_change_hook(env_archcpu(env)); + bql_unlock(); + + mask = aarch32_cpsr_valid_mask(env->features, &env_archcpu(env)->isar); + cpsr_write(env, val, mask, CPSRWriteExceptionReturn); + + /* Generated code has already stored the new PC value, but + * without masking out its low bits, because which bits need + * masking depends on whether we're returning to Thumb or ARM + * state. Do the masking now. + */ + env->regs[15] &= (env->thumb ? ~1 : ~3); + arm_rebuild_hflags(env); + + bql_lock(); + arm_call_el_change_hook(env_archcpu(env)); + bql_unlock(); +} + +/* Access to user mode registers from privileged modes. */ +uint32_t HELPER(get_user_reg)(CPUARMState *env, uint32_t regno) +{ + uint32_t val; + + if (regno == 13) { + val = env->banked_r13[BANK_USRSYS]; + } else if (regno == 14) { + val = env->banked_r14[BANK_USRSYS]; + } else if (regno >= 8 + && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { + val = env->usr_regs[regno - 8]; + } else { + val = env->regs[regno]; + } + return val; +} + +void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) +{ + if (regno == 13) { + env->banked_r13[BANK_USRSYS] = val; + } else if (regno == 14) { + env->banked_r14[BANK_USRSYS] = val; + } else if (regno >= 8 + && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { + env->usr_regs[regno - 8] = val; + } else { + env->regs[regno] = val; + } +} + +void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) +{ + if ((env->uncached_cpsr & CPSR_M) == mode) { + env->regs[13] = val; + } else { + env->banked_r13[bank_number(mode)] = val; + } +} + +uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) +{ + if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SYS) { + /* SRS instruction is UNPREDICTABLE from System mode; we UNDEF. + * Other UNPREDICTABLE and UNDEF cases were caught at translate time. + */ + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } + + if ((env->uncached_cpsr & CPSR_M) == mode) { + return env->regs[13]; + } else { + return env->banked_r13[bank_number(mode)]; + } +} + +static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, + uint32_t regno) +{ + /* Raise an exception if the requested access is one of the UNPREDICTABLE + * cases; otherwise return. This broadly corresponds to the pseudocode + * BankedRegisterAccessValid() and SPSRAccessValid(), + * except that we have already handled some cases at translate time. + */ + int curmode = env->uncached_cpsr & CPSR_M; + + 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; + } + + if (curmode == tgtmode) { + goto undef; + } + + if (tgtmode == ARM_CPU_MODE_USR) { + switch (regno) { + case 8 ... 12: + if (curmode != ARM_CPU_MODE_FIQ) { + goto undef; + } + break; + case 13: + if (curmode == ARM_CPU_MODE_SYS) { + goto undef; + } + break; + case 14: + if (curmode == ARM_CPU_MODE_HYP || curmode == ARM_CPU_MODE_SYS) { + goto undef; + } + break; + default: + break; + } + } + + return; + +undef: + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); +} + +void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, + uint32_t regno) +{ + msr_mrs_banked_exc_checks(env, tgtmode, regno); + + switch (regno) { + case 16: /* SPSRs */ + 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; + break; + case 13: + env->banked_r13[bank_number(tgtmode)] = value; + break; + case 14: + env->banked_r14[r14_bank_number(tgtmode)] = value; + break; + case 8 ... 12: + switch (tgtmode) { + case ARM_CPU_MODE_USR: + env->usr_regs[regno - 8] = value; + break; + case ARM_CPU_MODE_FIQ: + env->fiq_regs[regno - 8] = value; + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } +} + +uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) +{ + msr_mrs_banked_exc_checks(env, tgtmode, regno); + + switch (regno) { + case 16: /* SPSRs */ + 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: + return env->banked_r13[bank_number(tgtmode)]; + case 14: + return env->banked_r14[r14_bank_number(tgtmode)]; + case 8 ... 12: + switch (tgtmode) { + case ARM_CPU_MODE_USR: + return env->usr_regs[regno - 8]; + case ARM_CPU_MODE_FIQ: + return env->fiq_regs[regno - 8]; + default: + g_assert_not_reached(); + } + default: + g_assert_not_reached(); + } +} + +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 = 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); + } + + /* + * If the access function indicates a trap from EL0 to EL1 then + * that always takes priority over the HSTR_EL2 trap. (If it indicates + * a trap to EL3, then the HSTR_EL2 trap takes priority; if it indicates + * a trap to EL2, then the syndrome is the same either way so we don't + * care whether technically the architecture says that HSTR_EL2 trap or + * the other trap takes priority. So we take the "check HSTR_EL2" path + * for all of those cases.) + */ + if (res != CP_ACCESS_OK && ((res & CP_ACCESS_EL_MASK) == 0) && + arm_current_el(env) == 0) { + goto fail; + } + + /* + * 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; + + if (ri->type & ARM_CP_64BIT) { + mask = 1 << ri->crm; + } + + /* T4 and T14 are RES0 */ + mask &= ~((1 << 4) | (1 << 14)); + + if (env->cp15.hstr_el2 & mask) { + res = CP_ACCESS_TRAP_EL2; + goto fail; + } + } + + /* + * Fine-grained traps also are lower priority than undef-to-EL1, + * higher priority than trap-to-EL3, and we don't care about priority + * order with other EL2 traps because the syndrome value is the same. + */ + if (arm_fgt_active(env, arm_current_el(env))) { + uint64_t trapword = 0; + unsigned int idx = FIELD_EX32(ri->fgt, FGT, IDX); + unsigned int bitpos = FIELD_EX32(ri->fgt, FGT, BITPOS); + bool rev = FIELD_EX32(ri->fgt, FGT, REV); + bool trapbit; + + if (ri->fgt & FGT_EXEC) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_exec)); + trapword = env->cp15.fgt_exec[idx]; + } else if (isread && (ri->fgt & FGT_R)) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_read)); + trapword = env->cp15.fgt_read[idx]; + } else if (!isread && (ri->fgt & FGT_W)) { + assert(idx < ARRAY_SIZE(env->cp15.fgt_write)); + trapword = env->cp15.fgt_write[idx]; + } + + trapbit = extract64(trapword, bitpos, 1); + if (trapbit != rev) { + res = CP_ACCESS_TRAP_EL2; + goto fail; + } + } + + if (likely(res == CP_ACCESS_OK)) { + return ri; + } + + fail: + switch (res & ~CP_ACCESS_EL_MASK) { + 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)) { + /* + * FEAT_IDST says this should be reported as EC_SYSTEMREGISTERTRAP, + * not EC_UNCATEGORIZED + */ + break; + } + syndrome = syn_uncategorized(); + break; + default: + g_assert_not_reached(); + } + + target_el = res & CP_ACCESS_EL_MASK; + switch (target_el) { + case 0: + target_el = exception_target_el(env); + break; + case 2: + assert(arm_current_el(env) != 3); + assert(arm_is_el2_enabled(env)); + break; + case 3: + assert(arm_feature(env, ARM_FEATURE_EL3)); + break; + default: + /* No "direct" traps to EL1 */ + g_assert_not_reached(); + } + + raise_exception(env, EXCP_UDEF, syndrome, target_el); +} + +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; + + switch (mmu_idx) { + case ARMMMUIdx_E20_0: + target_el = 2; + break; + case ARMMMUIdx_E30_0: + target_el = 3; + break; + default: + target_el = 1; + break; + } + + /* + * 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) { + bql_lock(); + ri->writefn(env, ri, value); + bql_unlock(); + } else { + ri->writefn(env, ri, value); + } +} + +uint32_t HELPER(get_cp_reg)(CPUARMState *env, const void *rip) +{ + const ARMCPRegInfo *ri = rip; + uint32_t res; + + if (ri->type & ARM_CP_IO) { + bql_lock(); + res = ri->readfn(env, ri); + bql_unlock(); + } else { + res = ri->readfn(env, ri); + } + + return res; +} + +void HELPER(set_cp_reg64)(CPUARMState *env, const void *rip, uint64_t value) +{ + const ARMCPRegInfo *ri = rip; + + if (ri->type & ARM_CP_IO) { + bql_lock(); + ri->writefn(env, ri, value); + bql_unlock(); + } else { + ri->writefn(env, ri, value); + } +} + +uint64_t HELPER(get_cp_reg64)(CPUARMState *env, const void *rip) +{ + const ARMCPRegInfo *ri = rip; + uint64_t res; + + if (ri->type & ARM_CP_IO) { + bql_lock(); + res = ri->readfn(env, ri); + bql_unlock(); + } else { + res = ri->readfn(env, ri); + } + + return res; +} + +void HELPER(pre_hvc)(CPUARMState *env) +{ + ARMCPU *cpu = env_archcpu(env); + int cur_el = arm_current_el(env); + /* FIXME: Use actual secure state. */ + bool secure = false; + bool undef; + + if (arm_is_psci_call(cpu, EXCP_HVC)) { + /* If PSCI is enabled and this looks like a valid PSCI call then + * that overrides the architecturally mandated HVC behaviour. + */ + return; + } + + if (!arm_feature(env, ARM_FEATURE_EL2)) { + /* If EL2 doesn't exist, HVC always UNDEFs */ + undef = true; + } else if (arm_feature(env, ARM_FEATURE_EL3)) { + /* EL3.HCE has priority over EL2.HCD. */ + undef = !(env->cp15.scr_el3 & SCR_HCE); + } else { + undef = env->cp15.hcr_el2 & HCR_HCD; + } + + /* In ARMv7 and ARMv8/AArch32, HVC is undef in secure state. + * For ARMv8/AArch64, HVC is allowed in EL3. + * Note that we've already trapped HVC from EL0 at translation + * time. + */ + if (secure && (!is_a64(env) || cur_el == 1)) { + undef = true; + } + + if (undef) { + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } +} + +void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) +{ + ARMCPU *cpu = env_archcpu(env); + int cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + bool smd_flag = env->cp15.scr_el3 & SCR_SMD; + + /* + * SMC behaviour is summarized in the following table. + * This helper handles the "Trap to EL2" and "Undef insn" cases. + * The "Trap to EL3" and "PSCI call" cases are handled in the exception + * helper. + * + * -> ARM_FEATURE_EL3 and !SMD + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Trap to EL3 + * Conduit not SMC Trap to EL2 Trap to EL3 + * + * + * -> ARM_FEATURE_EL3 and SMD + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Undef insn + * Conduit not SMC Trap to EL2 Undef insn + * + * + * -> !ARM_FEATURE_EL3 + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 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. + * On ARMv8 with EL3 AArch32, or ARMv7 with the Virtualization + * extensions, SMD only applies to NS state. + * On ARMv7 without the Virtualization extensions, the SMD bit + * doesn't exist, but we forbid the guest to set it to 1 in scr_write(), + * so we need not special case this here. + */ + bool smd = arm_feature(env, ARM_FEATURE_AARCH64) ? smd_flag + : 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 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 + * PSCI-via-SMC as implying an EL3. + * This handles the very last line of the previous table. + */ + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } + + if (cur_el == 1 && (arm_hcr_el2_eff(env) & HCR_TSC)) { + /* In NS EL1, HCR controlled routing to EL2 has priority over SMD. + * We also want an EL2 guest to be able to forbid its EL1 from + * making PSCI calls into QEMU's "firmware" via HCR.TSC. + * This handles all the "Trap to EL2" cases of the previous table. + */ + raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); + } + + /* Catch the two remaining "Undef insn" cases of the previous table: + * - PSCI conduit is SMC but we don't have a valid PCSI call, + * - We don't have EL3 or SMD is set. + */ + if (!arm_is_psci_call(cpu, EXCP_SMC) && + (smd || !arm_feature(env, ARM_FEATURE_EL3))) { + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } +} + +/* ??? Flag setting arithmetic is awkward because we need to do comparisons. + The only way to do that in TCG is a conditional branch, which clobbers + all our temporaries. For now implement these as helper functions. */ + +/* Similarly for variable shift instructions. */ + +uint32_t HELPER(shl_cc)(CPUARMState *env, uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + if (shift == 32) + env->CF = x & 1; + else + env->CF = 0; + return 0; + } else if (shift != 0) { + env->CF = (x >> (32 - shift)) & 1; + return x << shift; + } + return x; +} + +uint32_t HELPER(shr_cc)(CPUARMState *env, uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + if (shift == 32) + env->CF = (x >> 31) & 1; + else + env->CF = 0; + return 0; + } else if (shift != 0) { + env->CF = (x >> (shift - 1)) & 1; + return x >> shift; + } + return x; +} + +uint32_t HELPER(sar_cc)(CPUARMState *env, uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + env->CF = (x >> 31) & 1; + return (int32_t)x >> 31; + } else if (shift != 0) { + env->CF = (x >> (shift - 1)) & 1; + return (int32_t)x >> shift; + } + return x; +} + +uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i) +{ + int shift1, shift; + shift1 = i & 0xff; + shift = shift1 & 0x1f; + if (shift == 0) { + if (shift1 != 0) + env->CF = (x >> 31) & 1; + return x; + } else { + env->CF = (x >> (shift - 1)) & 1; + return ((uint32_t)x >> shift) | (x << (32 - shift)); + } +} + +void HELPER(probe_access)(CPUARMState *env, target_ulong ptr, + uint32_t access_type, uint32_t mmu_idx, + uint32_t size) +{ + uint32_t in_page = -((uint32_t)ptr | TARGET_PAGE_SIZE); + uintptr_t ra = GETPC(); + + if (likely(size <= in_page)) { + probe_access(env, ptr, size, access_type, mmu_idx, ra); + } else { + probe_access(env, ptr, in_page, access_type, mmu_idx, ra); + probe_access(env, ptr + in_page, size - in_page, + access_type, mmu_idx, ra); + } +} + +/* + * This function corresponds to AArch64.vESBOperation(). + * Note that the AArch32 version is not functionally different. + */ +void HELPER(vesb)(CPUARMState *env) +{ + /* + * The EL2Enabled() check is done inside arm_hcr_el2_eff, + * and will return HCR_EL2.VSE == 0, so nothing happens. + */ + uint64_t hcr = arm_hcr_el2_eff(env); + bool enabled = !(hcr & HCR_TGE) && (hcr & HCR_AMO); + bool pending = enabled && (hcr & HCR_VSE); + bool masked = (env->daif & PSTATE_A); + + /* If VSE pending and masked, defer the exception. */ + if (pending && masked) { + uint32_t syndrome; + + if (arm_el_is_aa64(env, 1)) { + /* Copy across IDS and ISS from VSESR. */ + syndrome = env->cp15.vsesr_el2 & 0x1ffffff; + } else { + ARMMMUFaultInfo fi = { .type = ARMFault_AsyncExternal }; + + if (extended_addresses_enabled(env)) { + syndrome = arm_fi_to_lfsc(&fi); + } else { + syndrome = arm_fi_to_sfsc(&fi); + } + /* Copy across AET and ExT from VSESR. */ + syndrome |= env->cp15.vsesr_el2 & 0xd000; + } + + /* Set VDISR_EL2.A along with the syndrome. */ + env->cp15.vdisr_el2 = syndrome | (1u << 31); + + /* Clear pending virtual SError */ + env->cp15.hcr_el2 &= ~HCR_VSE; + cpu_reset_interrupt(env_cpu(env), CPU_INTERRUPT_VSERR); + } +} diff --git a/target/arm/tcg/pauth_helper.c b/target/arm/tcg/pauth_helper.c new file mode 100644 index 0000000000..c4b143024f --- /dev/null +++ b/target/arm/tcg/pauth_helper.c @@ -0,0 +1,632 @@ +/* + * ARM v8.3-PAuth Operations + * + * Copyright (c) 2019 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 "cpu-features.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" +#include "qemu/xxhash.h" + + +static uint64_t pac_cell_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= extract64(i, 52, 4); + o |= extract64(i, 24, 4) << 4; + o |= extract64(i, 44, 4) << 8; + o |= extract64(i, 0, 4) << 12; + + o |= extract64(i, 28, 4) << 16; + o |= extract64(i, 48, 4) << 20; + o |= extract64(i, 4, 4) << 24; + o |= extract64(i, 40, 4) << 28; + + o |= extract64(i, 32, 4) << 32; + o |= extract64(i, 12, 4) << 36; + o |= extract64(i, 56, 4) << 40; + o |= extract64(i, 20, 4) << 44; + + o |= extract64(i, 8, 4) << 48; + o |= extract64(i, 36, 4) << 52; + o |= extract64(i, 16, 4) << 56; + o |= extract64(i, 60, 4) << 60; + + return o; +} + +static uint64_t pac_cell_inv_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= extract64(i, 12, 4); + o |= extract64(i, 24, 4) << 4; + o |= extract64(i, 48, 4) << 8; + o |= extract64(i, 36, 4) << 12; + + o |= extract64(i, 56, 4) << 16; + o |= extract64(i, 44, 4) << 20; + o |= extract64(i, 4, 4) << 24; + o |= extract64(i, 16, 4) << 28; + + o |= i & MAKE_64BIT_MASK(32, 4); + o |= extract64(i, 52, 4) << 36; + o |= extract64(i, 28, 4) << 40; + o |= extract64(i, 8, 4) << 44; + + o |= extract64(i, 20, 4) << 48; + o |= extract64(i, 0, 4) << 52; + o |= extract64(i, 40, 4) << 56; + o |= i & MAKE_64BIT_MASK(60, 4); + + return o; +} + +static uint64_t pac_sub(uint64_t i) +{ + static const uint8_t sub[16] = { + 0xb, 0x6, 0x8, 0xf, 0xc, 0x0, 0x9, 0xe, + 0x3, 0x7, 0x4, 0x5, 0xd, 0x2, 0x1, 0xa, + }; + uint64_t o = 0; + int b; + + for (b = 0; b < 64; b += 4) { + o |= (uint64_t)sub[(i >> b) & 0xf] << b; + } + 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] = { + 0x5, 0xe, 0xd, 0x8, 0xa, 0xb, 0x1, 0x9, + 0x2, 0x6, 0xf, 0x0, 0x4, 0xc, 0x7, 0x3, + }; + uint64_t o = 0; + int b; + + for (b = 0; b < 64; b += 4) { + o |= (uint64_t)inv_sub[(i >> b) & 0xf] << b; + } + return o; +} + +static int rot_cell(int cell, int n) +{ + /* 4-bit rotate left by n. */ + cell |= cell << 4; + return extract32(cell, 4 - n, 4); +} + +static uint64_t pac_mult(uint64_t i) +{ + uint64_t o = 0; + int b; + + for (b = 0; b < 4 * 4; b += 4) { + int i0, i4, i8, ic, t0, t1, t2, t3; + + i0 = extract64(i, b, 4); + i4 = extract64(i, b + 4 * 4, 4); + i8 = extract64(i, b + 8 * 4, 4); + ic = extract64(i, b + 12 * 4, 4); + + t0 = rot_cell(i8, 1) ^ rot_cell(i4, 2) ^ rot_cell(i0, 1); + t1 = rot_cell(ic, 1) ^ rot_cell(i4, 1) ^ rot_cell(i0, 2); + t2 = rot_cell(ic, 2) ^ rot_cell(i8, 1) ^ rot_cell(i0, 1); + t3 = rot_cell(ic, 1) ^ rot_cell(i8, 2) ^ rot_cell(i4, 1); + + o |= (uint64_t)t3 << b; + o |= (uint64_t)t2 << (b + 4 * 4); + o |= (uint64_t)t1 << (b + 8 * 4); + o |= (uint64_t)t0 << (b + 12 * 4); + } + return o; +} + +static uint64_t tweak_cell_rot(uint64_t cell) +{ + return (cell >> 1) | (((cell ^ (cell >> 1)) & 1) << 3); +} + +static uint64_t tweak_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= extract64(i, 16, 4) << 0; + o |= extract64(i, 20, 4) << 4; + o |= tweak_cell_rot(extract64(i, 24, 4)) << 8; + o |= extract64(i, 28, 4) << 12; + + o |= tweak_cell_rot(extract64(i, 44, 4)) << 16; + o |= extract64(i, 8, 4) << 20; + o |= extract64(i, 12, 4) << 24; + o |= tweak_cell_rot(extract64(i, 32, 4)) << 28; + + o |= extract64(i, 48, 4) << 32; + o |= extract64(i, 52, 4) << 36; + o |= extract64(i, 56, 4) << 40; + o |= tweak_cell_rot(extract64(i, 60, 4)) << 44; + + o |= tweak_cell_rot(extract64(i, 0, 4)) << 48; + o |= extract64(i, 4, 4) << 52; + o |= tweak_cell_rot(extract64(i, 40, 4)) << 56; + o |= tweak_cell_rot(extract64(i, 36, 4)) << 60; + + return o; +} + +static uint64_t tweak_cell_inv_rot(uint64_t cell) +{ + return ((cell << 1) & 0xf) | ((cell & 1) ^ (cell >> 3)); +} + +static uint64_t tweak_inv_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= tweak_cell_inv_rot(extract64(i, 48, 4)); + o |= extract64(i, 52, 4) << 4; + o |= extract64(i, 20, 4) << 8; + o |= extract64(i, 24, 4) << 12; + + o |= extract64(i, 0, 4) << 16; + o |= extract64(i, 4, 4) << 20; + o |= tweak_cell_inv_rot(extract64(i, 8, 4)) << 24; + o |= extract64(i, 12, 4) << 28; + + o |= tweak_cell_inv_rot(extract64(i, 28, 4)) << 32; + o |= tweak_cell_inv_rot(extract64(i, 60, 4)) << 36; + o |= tweak_cell_inv_rot(extract64(i, 56, 4)) << 40; + o |= tweak_cell_inv_rot(extract64(i, 16, 4)) << 44; + + o |= extract64(i, 32, 4) << 48; + o |= extract64(i, 36, 4) << 52; + o |= extract64(i, 40, 4) << 56; + o |= tweak_cell_inv_rot(extract64(i, 44, 4)) << 60; + + return o; +} + +static uint64_t pauth_computepac_architected(uint64_t data, uint64_t modifier, + ARMPACKey key, bool isqarma3) +{ + static const uint64_t RC[5] = { + 0x0000000000000000ull, + 0x13198A2E03707344ull, + 0xA4093822299F31D0ull, + 0x082EFA98EC4E6C89ull, + 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. + */ + uint64_t key0 = key.hi, key1 = key.lo; + uint64_t workingval, runningmod, roundkey, modk0; + int i; + + modk0 = (key0 << 63) | ((key0 >> 1) ^ (key0 >> 63)); + runningmod = modifier; + workingval = data ^ key0; + + for (i = 0; i <= iterations; ++i) { + roundkey = key1 ^ runningmod; + workingval ^= roundkey; + workingval ^= RC[i]; + if (i > 0) { + workingval = pac_cell_shuffle(workingval); + workingval = pac_mult(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); + 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); + 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 <= 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[iterations - i]; + workingval ^= roundkey; + workingval ^= alpha; + } + workingval ^= modk0; + + return workingval; +} + +static uint64_t pauth_computepac_impdef(uint64_t data, uint64_t modifier, + ARMPACKey key) +{ + return qemu_xxhash64_4(data, modifier, key.lo, key.hi); +} + +static uint64_t pauth_computepac(CPUARMState *env, uint64_t data, + uint64_t modifier, ARMPACKey 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); + } +} + +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, false); + ARMPauthFeature pauth_feature = cpu_isar_feature(pauth_feature, cpu); + uint64_t pac, ext_ptr, ext, test; + int bot_bit, top_bit; + + /* If tagged pointers are in use, use ptr<55>, otherwise ptr<63>. */ + if (param.tbi) { + ext = sextract64(ptr, 55, 1); + } else { + ext = sextract64(ptr, 63, 1); + } + + /* Build a pointer with known good extension bits. */ + top_bit = 64 - 8 * param.tbi; + bot_bit = 64 - param.tsz; + ext_ptr = deposit64(ptr, bot_bit, top_bit - bot_bit, ext); + + pac = pauth_computepac(env, ext_ptr, modifier, *key); + + /* + * Check if the ptr has good extension bits and corrupt the + * pointer authentication code if not. + */ + test = sextract64(ptr, bot_bit, top_bit - bot_bit); + if (test != 0 && test != -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); + } else { + ptr &= MAKE_64BIT_MASK(0, bot_bit); + pac &= ~(MAKE_64BIT_MASK(55, 1) | MAKE_64BIT_MASK(0, bot_bit)); + } + ext &= MAKE_64BIT_MASK(55, 1); + return pac | ext | ptr; +} + +static uint64_t pauth_original_ptr(uint64_t ptr, ARMVAParameters param) +{ + uint64_t mask = pauth_ptr_mask(param); + + /* 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, + 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, false); + ARMPauthFeature pauth_feature = cpu_isar_feature(pauth_feature, cpu); + int bot_bit, top_bit; + 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; + + 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); + } else { + return deposit64(orig_ptr, 61, 2, error_code); + } + } + return orig_ptr; +} + +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, false); + + return pauth_original_ptr(ptr, param); +} + +static G_NORETURN +void pauth_trap(CPUARMState *env, int target_el, uintptr_t ra) +{ + raise_exception_ra(env, EXCP_UDEF, syn_pactrap(), target_el, ra); +} + +static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra) +{ + if (el < 2 && arm_is_el2_enabled(env)) { + uint64_t hcr = arm_hcr_el2_eff(env); + bool trap = !(hcr & HCR_API); + if (el == 0) { + /* Trap only applies to EL1&0 regime. */ + trap &= (hcr & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE); + } + /* FIXME: ARMv8.3-NV: HCR_NV trap takes precedence for ERETA[AB]. */ + if (trap) { + pauth_trap(env, 2, ra); + } + } + if (el < 3 && arm_feature(env, ARM_FEATURE_EL3)) { + if (!(env->cp15.scr_el3 & SCR_API)) { + pauth_trap(env, 3, ra); + } + } +} + +static bool pauth_key_enabled(CPUARMState *env, int el, uint32_t bit) +{ + return (arm_sctlr(env, el) & bit) != 0; +} + +uint64_t HELPER(pacia)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnIA)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->keys.apia, false); +} + +uint64_t HELPER(pacib)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnIB)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->keys.apib, false); +} + +uint64_t HELPER(pacda)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnDA)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->keys.apda, true); +} + +uint64_t HELPER(pacdb)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnDB)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->keys.apdb, true); +} + +uint64_t HELPER(pacga)(CPUARMState *env, uint64_t x, uint64_t y) +{ + uint64_t pac; + + pauth_check_trap(env, arm_current_el(env), GETPC()); + pac = pauth_computepac(env, x, y, env->keys.apga); + + return pac & 0xffffffff00000000ull; +} + +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, ra); + return pauth_auth(env, x, y, &env->keys.apia, false, 0, ra, is_combined); +} + +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, ra); + return pauth_auth(env, x, y, &env->keys.apib, false, 1, ra, is_combined); +} + +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, ra); + return pauth_auth(env, x, y, &env->keys.apda, true, 0, ra, is_combined); +} + +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, 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) +{ + return pauth_strip(env, a, false); +} + +uint64_t HELPER(xpacd)(CPUARMState *env, uint64_t a) +{ + return pauth_strip(env, a, true); +} 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/sme-fa64.decode b/target/arm/tcg/sme-fa64.decode similarity index 100% rename from target/arm/sme-fa64.decode rename to target/arm/tcg/sme-fa64.decode diff --git a/target/arm/sme.decode b/target/arm/tcg/sme.decode similarity index 100% rename from target/arm/sme.decode rename to target/arm/tcg/sme.decode diff --git a/target/arm/sme_helper.c b/target/arm/tcg/sme_helper.c similarity index 86% rename from target/arm/sme_helper.c rename to target/arm/tcg/sme_helper.c index f891306bb9..8cf12654e5 100644 --- a/target/arm/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -29,42 +29,9 @@ #include "vec_internal.h" #include "sve_ldst_internal.h" -/* ResetSVEState */ -void arm_reset_sve_state(CPUARMState *env) +void helper_set_svcr(CPUARMState *env, uint32_t val, uint32_t mask) { - 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 helper_set_pstate_sm(CPUARMState *env, uint32_t i) -{ - if (i == FIELD_EX64(env->svcr, SVCR, SM)) { - return; - } - env->svcr ^= R_SVCR_SM_MASK; - arm_reset_sve_state(env); -} - -void helper_set_pstate_za(CPUARMState *env, uint32_t i) -{ - if (i == FIELD_EX64(env->svcr, SVCR, ZA)) { - return; - } - env->svcr ^= R_SVCR_ZA_MASK; - - /* - * ResetSMEState. - * - * 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. - */ - if (i) { - memset(env->zarray, 0, sizeof(env->zarray)); - } + aarch64_set_svcr(env, val, mask); } void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl) @@ -412,7 +379,7 @@ static inline void HNAME##_host(void *za, intptr_t off, void *host) \ { \ uint64_t *ptr = za + off; \ HOST(host, ptr[BE]); \ - HOST(host + 1, ptr[!BE]); \ + HOST(host + 8, ptr[!BE]); \ } \ static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ { \ @@ -550,6 +517,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, clr_fn(za, 0, reg_off); } + set_helper_retaddr(ra); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -562,6 +531,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -576,6 +547,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(ra); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -587,6 +560,8 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, reg_off += esize; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -606,8 +581,8 @@ void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, 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; } @@ -734,6 +709,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(ra); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -744,6 +721,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -758,6 +737,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(ra); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -767,6 +748,8 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, reg_off += 1 << esz; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -783,8 +766,8 @@ void sme_st1_mte(CPUARMState *env, void *za, 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; } @@ -949,7 +932,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, 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); + *a = float32_muladd(n, *m, *a, 0, &fpst); } col += 4; pb >>= 4; @@ -1009,12 +992,23 @@ static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg) } static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, - float_status *s_std, float_status *s_odd) + float_status *s_f16, 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); + /* + * We need three different float_status for different parts of this + * operation: + * - the input conversion of the float16 values must use the + * f16-specific float_status, so that the FPCR.FZ16 control is applied + * - operations on float32 including the final accumulation must use + * the normal float_status, so that FPCR.FZ is applied + * - we have pre-set-up copy of s_std which is set to round-to-odd, + * for the multiply (see below) + */ + float64 e1r = float16_to_float64(e1 & 0xffff, true, s_f16); + float64 e1c = float16_to_float64(e1 >> 16, true, s_f16); + float64 e2r = float16_to_float64(e2 & 0xffff, true, s_f16); + float64 e2c = float16_to_float64(e2 >> 16, true, s_f16); float64 t64; float32 t32; @@ -1036,20 +1030,23 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, } void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, void *vst, uint32_t desc) + void *vpm, CPUARMState *env, 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; + float_status fpst_odd, fpst_std, fpst_f16; /* - * 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. + * Make copies of fp_status and fp_status_f16, because this operation + * does not update the cumulative fp exception status. It also + * produces default NaNs. We also need a second copy of fp_status with + * round-to-odd -- see above. */ - fpst_std = *(float_status *)vst; + fpst_f16 = env->vfp.fp_status_f16; + fpst_std = env->vfp.fp_status; set_default_nan_mode(true, &fpst_std); + set_default_nan_mode(true, &fpst_f16); fpst_odd = fpst_std; set_float_rounding_mode(float_round_to_odd, &fpst_odd); @@ -1069,11 +1066,11 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, 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; + *a = f16_dotadd(*a, n, m, + &fpst_f16, &fpst_std, &fpst_odd); } + col += 4; + pcol >>= 4; } while (col & 15); } row += 4; @@ -1082,47 +1079,97 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, } } -void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, uint32_t desc) +void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, + void *vpn, void *vpm, CPUARMState *env, 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, 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)); + if (is_ebf(env, &fpst, &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); + 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); + 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_ebf(*a, n, m, &fpst, &fpst_odd); + } col += 4; pcol >>= 4; - } - } while (col & 15); - } - row += 4; - prow >>= 4; - } while (row & 15); + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } + } else { + 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, &fpst); + } + col += 4; + pcol >>= 4; + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } } } -typedef uint64_t IMOPFn(uint64_t, uint64_t, uint64_t, uint8_t, bool); +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); -static inline void do_imopa(uint64_t *za, uint64_t *zn, uint64_t *zm, - uint8_t *pn, uint8_t *pm, - uint32_t desc, IMOPFn *fn) + 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); @@ -1142,25 +1189,16 @@ static inline void do_imopa(uint64_t *za, uint64_t *zn, uint64_t *zm, } #define DEF_IMOP_32(NAME, NTYPE, MTYPE) \ -static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ +static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \ { \ - uint32_t sum0 = 0, sum1 = 0; \ + uint32_t sum = 0; \ /* Apply P to N as a mask, making the inactive elements 0. */ \ n &= expand_pred_b(p); \ - sum0 += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ - sum0 += (NTYPE)(n >> 8) * (MTYPE)(m >> 8); \ - sum0 += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ - sum0 += (NTYPE)(n >> 24) * (MTYPE)(m >> 24); \ - sum1 += (NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ - sum1 += (NTYPE)(n >> 40) * (MTYPE)(m >> 40); \ - sum1 += (NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ - sum1 += (NTYPE)(n >> 56) * (MTYPE)(m >> 56); \ - if (neg) { \ - sum0 = (uint32_t)a - sum0, sum1 = (uint32_t)(a >> 32) - sum1; \ - } else { \ - sum0 = (uint32_t)a + sum0, sum1 = (uint32_t)(a >> 32) + sum1; \ - } \ - return ((uint64_t)sum1 << 32) | sum0; \ + 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) \ @@ -1169,10 +1207,10 @@ 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); \ + sum += (int64_t)(NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ + sum += (int64_t)(NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ + sum += (int64_t)(NTYPE)(n >> 32) * (MTYPE)(m >> 32); \ + sum += (int64_t)(NTYPE)(n >> 48) * (MTYPE)(m >> 48); \ return neg ? a - sum : a + sum; \ } @@ -1186,16 +1224,17 @@ 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) \ - void HELPER(sme_##NAME)(void *vza, void *vzn, void *vzm, void *vpn, \ - void *vpm, uint32_t desc) \ - { do_imopa(vza, vzn, vzm, vpn, vpm, desc, NAME); } +#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) +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 99% rename from target/arm/sve.decode rename to target/arm/tcg/sve.decode index 14b3a69c36..04b6fcc0cf 100644 --- a/target/arm/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1629,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 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 27838fb6e2..904296705c 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -21,12 +21,14 @@ #include "cpu.h" #include "internals.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" #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. @@ -5352,11 +5354,11 @@ bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env, addr = useronly_clean_ptr(addr); #ifdef CONFIG_USER_ONLY - flags = probe_access_flags(env, addr, access_type, mmu_idx, nofault, + flags = probe_access_flags(env, addr, 0, access_type, mmu_idx, nofault, &info->host, retaddr); #else CPUTLBEntryFull *full; - flags = probe_access_full(env, addr, access_type, mmu_idx, nofault, + flags = probe_access_full(env, addr, 0, access_type, mmu_idx, nofault, &info->host, &full, retaddr); #endif info->flags = flags; @@ -5372,7 +5374,7 @@ bool sve_probe_page(SVEHostPage *info, bool nofault, CPUARMState *env, info->tagged = (flags & PAGE_ANON) && (flags & PAGE_MTE); #else info->attrs = full->attrs; - info->tagged = full->pte_attrs == 0xf0; + info->tagged = full->extra.arm.pte_attrs == 0xf0; #endif /* Ensure that info->host[] is relative to addr, not addr + mem_off. */ @@ -5480,7 +5482,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; @@ -5687,9 +5689,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, @@ -5726,7 +5725,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. */ @@ -5740,6 +5738,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(retaddr); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -5754,6 +5754,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -5773,6 +5775,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(retaddr); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -5786,6 +5790,8 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, mem_off += N << msz; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -5803,8 +5809,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; } @@ -5936,15 +5942,11 @@ DO_LDN_2(4, dd, MO_64) /* * Load contiguous data, first-fault and no-fault. * - * For user-only, one could argue that we should hold the mmap_lock during - * the operation so that there is no race between page_check_range and the - * load operation. However, unmapping pages out from under a running thread - * is extraordinarily unlikely. This theoretical race condition also affects - * linux-user/ in its get_user/put_user macros. - * - * TODO: Construct some helpers, written in assembly, that interact with - * host_signal_handler to produce memory ops which can properly report errors - * without racing. + * For user-only, we control the race between page_check_range and + * another thread's munmap by using set/clear_helper_retaddr. Any + * SEGV that occurs between those markers is assumed to be because + * the guest page vanished. Keep that block as small as possible + * so that unrelated QEMU bugs are not blamed on the guest. */ /* Fault on byte I. All bits in FFR from I are cleared. The vector @@ -6095,6 +6097,8 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(retaddr); + do { uint64_t pg = *(uint64_t *)(vg + (reg_off >> 3)); do { @@ -6103,9 +6107,11 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, (cpu_watchpoint_address_matches (env_cpu(env), addr + mem_off, 1 << msz) & BP_MEM_READ)) { + clear_helper_retaddr(); goto do_fault; } if (mtedesc && !mte_probe(env, mtedesc, addr + mem_off)) { + clear_helper_retaddr(); goto do_fault; } host_fn(vd, reg_off, host + mem_off); @@ -6115,6 +6121,8 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, } while (reg_off <= reg_last && (reg_off & 63)); } while (reg_off <= reg_last); + clear_helper_retaddr(); + /* * MemSingleNF is allowed to fail for any reason. We have special * code above to handle the first element crossing a page boundary. @@ -6159,8 +6167,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; } @@ -6309,9 +6317,6 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, 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, @@ -6342,7 +6347,6 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, } while (reg_off & 63); } while (reg_off <= reg_last); return; -#endif } mem_off = info.mem_off_first[0]; @@ -6350,6 +6354,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, reg_last = info.reg_off_last[0]; host = info.page[0].host; + set_helper_retaddr(retaddr); + while (reg_off <= reg_last) { uint64_t pg = vg[reg_off >> 6]; do { @@ -6364,6 +6370,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, } while (reg_off <= reg_last && (reg_off & 63)); } + clear_helper_retaddr(); + /* * Use the slow path to manage the cross-page misalignment. * But we know this is RAM and cannot trap. @@ -6383,6 +6391,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, reg_last = info.reg_off_last[1]; host = info.page[1].host; + set_helper_retaddr(retaddr); + do { uint64_t pg = vg[reg_off >> 6]; do { @@ -6396,6 +6406,8 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, mem_off += N << msz; } while (reg_off & 63); } while (reg_off <= reg_last); + + clear_helper_retaddr(); } } @@ -6413,8 +6425,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; } @@ -6532,7 +6544,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; @@ -6562,7 +6574,9 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, if (unlikely(info.flags & TLB_MMIO)) { tlb_fn(env, &scratch, reg_off, addr, retaddr); } else { + set_helper_retaddr(retaddr); host_fn(&scratch, reg_off, info.host); + clear_helper_retaddr(); } } else { /* Element crosses the page boundary. */ @@ -6718,7 +6732,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; @@ -6784,7 +6798,9 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, goto fault; } + set_helper_retaddr(retaddr); host_fn(vd, reg_off, info.host); + clear_helper_retaddr(); } reg_off += esize; } while (reg_off & 63); @@ -6923,7 +6939,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]; @@ -6988,7 +7004,9 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, do { void *h = host[i]; if (likely(h != NULL)) { + set_helper_retaddr(retaddr); host_fn(vd, reg_off, h); + clear_helper_retaddr(); } else if ((vg[reg_off >> 6] >> (reg_off & 63)) & 1) { target_ulong addr = base + (off_fn(vm, reg_off) << scale); tlb_fn(env, vd, reg_off, addr, retaddr); diff --git a/target/arm/sve_ldst_internal.h b/target/arm/tcg/sve_ldst_internal.h similarity index 100% rename from target/arm/sve_ldst_internal.h rename to target/arm/tcg/sve_ldst_internal.h 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 97% rename from target/arm/t32.decode rename to target/arm/tcg/t32.decode index f21ad0167a..d327178829 100644 --- a/target/arm/t32.decode +++ b/target/arm/tcg/t32.decode @@ -458,41 +458,41 @@ STR_ri 1111 1000 1100 .... .... ............ @ldst_ri_pos # Note that Load, unsigned (literal) overlaps all other load encodings. { { - NOP 1111 1000 -001 1111 1111 ------------ # PLD + PLD 1111 1000 -001 1111 1111 ------------ # (literal) LDRB_ri 1111 1000 .001 1111 .... ............ @ldst_ri_lit } { - NOP 1111 1000 1001 ---- 1111 ------------ # PLD + PLD 1111 1000 1001 ---- 1111 ------------ # (immediate T1) LDRB_ri 1111 1000 1001 .... .... ............ @ldst_ri_pos } LDRB_ri 1111 1000 0001 .... .... 1..1 ........ @ldst_ri_idx { - NOP 1111 1000 0001 ---- 1111 1100 -------- # PLD + PLD 1111 1000 0001 ---- 1111 1100 -------- # (immediate T2) LDRB_ri 1111 1000 0001 .... .... 1100 ........ @ldst_ri_neg } LDRBT_ri 1111 1000 0001 .... .... 1110 ........ @ldst_ri_unp { - NOP 1111 1000 0001 ---- 1111 000000 -- ---- # PLD + PLD 1111 1000 0001 ---- 1111 000000 -- ---- # (register) LDRB_rr 1111 1000 0001 .... .... 000000 .. .... @ldst_rr } } { { - NOP 1111 1000 -011 1111 1111 ------------ # PLD + PLD 1111 1000 -011 1111 1111 ------------ # (literal) LDRH_ri 1111 1000 .011 1111 .... ............ @ldst_ri_lit } { - NOP 1111 1000 1011 ---- 1111 ------------ # PLDW + PLDW 1111 1000 1011 ---- 1111 ------------ # (immediate T1) LDRH_ri 1111 1000 1011 .... .... ............ @ldst_ri_pos } LDRH_ri 1111 1000 0011 .... .... 1..1 ........ @ldst_ri_idx { - NOP 1111 1000 0011 ---- 1111 1100 -------- # PLDW + PLDW 1111 1000 0011 ---- 1111 1100 -------- # (immediate T2) LDRH_ri 1111 1000 0011 .... .... 1100 ........ @ldst_ri_neg } LDRHT_ri 1111 1000 0011 .... .... 1110 ........ @ldst_ri_unp { - NOP 1111 1000 0011 ---- 1111 000000 -- ---- # PLDW + PLDW 1111 1000 0011 ---- 1111 000000 -- ---- # (register) LDRH_rr 1111 1000 0011 .... .... 000000 .. .... @ldst_rr } } @@ -504,24 +504,23 @@ STR_ri 1111 1000 1100 .... .... ............ @ldst_ri_pos LDRT_ri 1111 1000 0101 .... .... 1110 ........ @ldst_ri_unp LDR_rr 1111 1000 0101 .... .... 000000 .. .... @ldst_rr } -# NOPs here are PLI. { { - NOP 1111 1001 -001 1111 1111 ------------ + PLI 1111 1001 -001 1111 1111 ------------ # (literal T3) LDRSB_ri 1111 1001 .001 1111 .... ............ @ldst_ri_lit } { - NOP 1111 1001 1001 ---- 1111 ------------ + PLI 1111 1001 1001 ---- 1111 ------------ # (immediate T1) LDRSB_ri 1111 1001 1001 .... .... ............ @ldst_ri_pos } LDRSB_ri 1111 1001 0001 .... .... 1..1 ........ @ldst_ri_idx { - NOP 1111 1001 0001 ---- 1111 1100 -------- + PLI 1111 1001 0001 ---- 1111 1100 -------- # (immediate T2) LDRSB_ri 1111 1001 0001 .... .... 1100 ........ @ldst_ri_neg } LDRSBT_ri 1111 1001 0001 .... .... 1110 ........ @ldst_ri_unp { - NOP 1111 1001 0001 ---- 1111 000000 -- ---- + PLI 1111 1001 0001 ---- 1111 000000 -- ---- # (register) LDRSB_rr 1111 1001 0001 .... .... 000000 .. .... @ldst_rr } } diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c new file mode 100644 index 0000000000..8841f039bc --- /dev/null +++ b/target/arm/tcg/tlb_helper.c @@ -0,0 +1,387 @@ +/* + * ARM TLB (Translation lookaside buffer) helpers. + * + * 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/exec-all.h" +#include "exec/helper-proto.h" + + +/* + * Returns true if the stage 1 translation regime is using LPAE format page + * tables. Used when raising alignment exceptions, whose FSR changes depending + * on whether the long or short descriptor format is in use. + */ +bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) +{ + mmu_idx = stage_1_mmu_idx(mmu_idx); + return regime_using_lpae_format(env, mmu_idx); +} + +static inline uint32_t merge_syn_data_abort(uint32_t template_syn, + ARMMMUFaultInfo *fi, + unsigned int target_el, + bool same_el, bool is_write, + int fsc) +{ + uint32_t syn; + + /* + * 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 + * it cleared. + * + * 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_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, + fi->ea, 0, fi->s1ptw, is_write, fsc); + } else { + /* + * Fields: IL, ISV, SAS, SSE, SRT, SF and AR come from the template + * syndrome created at translation time. + * Now we create the runtime syndrome with the remaining fields. + */ + syn = syn_data_abort_with_iss(same_el, + 0, 0, 0, 0, 0, + fi->ea, 0, fi->s1ptw, is_write, fsc, + true); + /* Merge the runtime syndrome with the template syndrome. */ + syn |= template_syn; + } + return syn; +} + +static uint32_t compute_fsr_fsc(CPUARMState *env, ARMMMUFaultInfo *fi, + int target_el, int mmu_idx, uint32_t *ret_fsc) +{ + ARMMMUIdx arm_mmu_idx = core_to_arm_mmu_idx(env, mmu_idx); + uint32_t fsr, fsc; + + /* + * 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 + */ + fsr = arm_fi_to_lfsc(fi); + fsc = extract32(fsr, 0, 6); + } else { + fsr = arm_fi_to_sfsc(fi); + /* + * Short format FSR : this fault will never actually be reported + * to an EL that uses a syndrome register. Use a (currently) + * reserved FSR code in case the constructed syndrome does leak + * into the guest somehow. + */ + fsc = 0x3f; + } + + *ret_fsc = fsc; + 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 = 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; + } + } + + if (fi->stage2) { + target_el = 2; + env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4; + if (arm_is_secure_below_el3(env) && fi->s1ns) { + env->cp15.hpfar_el2 |= HPFAR_NS; + } + } + + 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, fi, target_el, + same_el, access_type == MMU_DATA_STORE, + fsc); + if (access_type == MMU_DATA_STORE + && arm_feature(env, ARM_FEATURE_V6)) { + fsr |= (1 << 11); + } + exc = EXCP_DATA_ABORT; + } + + do_raise: + env->exception.vaddress = addr; + env->exception.fsr = fsr; + raise_exception(env, exc, syn, target_el); +} + +/* Raise a data fault alignment exception for the specified virtual address */ +void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + ARMCPU *cpu = ARM_CPU(cs); + ARMMMUFaultInfo fi = {}; + + /* now we have a real cpu fault */ + cpu_restore_state(cs, retaddr); + + fi.type = ARMFault_Alignment; + arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi); +} + +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 = arm_env_mmu_index(env); + uint32_t fsc; + + env->exception.vaddress = pc; + + /* + * Note that the fsc is not applicable to this exception, + * since any syndrome is pcalignment not insn_abort. + */ + env->exception.fsr = compute_fsr_fsc(env, &fi, target_el, mmu_idx, &fsc); + raise_exception(env, EXCP_PREFETCH_ABORT, syn_pcalignment(), target_el); +} + +#if !defined(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 + */ +void arm_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) +{ + ARMCPU *cpu = ARM_CPU(cs); + ARMMMUFaultInfo fi = {}; + + /* now we have a real cpu fault */ + cpu_restore_state(cs, retaddr); + + fi.ea = arm_extabort_type(response); + fi.type = ARMFault_SyncExternal; + arm_deliver_fault(cpu, addr, access_type, mmu_idx, &fi); +} + +bool arm_cpu_tlb_fill_align(CPUState *cs, CPUTLBEntryFull *out, vaddr address, + MMUAccessType access_type, int mmu_idx, + MemOp memop, int size, bool probe, uintptr_t ra) +{ + ARMCPU *cpu = ARM_CPU(cs); + GetPhysAddrResult res = {}; + ARMMMUFaultInfo local_fi, *fi; + + /* + * 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)); + } + + /* + * Per R_XCHFJ, alignment fault not due to memory type has + * highest precedence. Otherwise, walk the page table and + * and collect the page description. + */ + if (address & ((1 << memop_alignment_bits(memop)) - 1)) { + fi->type = ARMFault_Alignment; + } else if (!get_phys_addr(&cpu->env, address, access_type, memop, + core_to_arm_mmu_idx(&cpu->env, mmu_idx), + &res, fi)) { + res.f.extra.arm.pte_attrs = res.cacheattrs.attrs; + res.f.extra.arm.shareability = res.cacheattrs.shareability; + *out = res.f; + return true; + } + if (probe) { + return false; + } + + /* Now we have a real cpu fault. */ + cpu_restore_state(cs, ra); + arm_deliver_fault(cpu, address, access_type, mmu_idx, fi); +} +#else +void arm_cpu_record_sigsegv(CPUState *cs, vaddr addr, + MMUAccessType access_type, + bool maperr, uintptr_t ra) +{ + ARMMMUFaultInfo fi = { + .type = maperr ? ARMFault_Translation : ARMFault_Permission, + .level = 3, + }; + ARMCPU *cpu = ARM_CPU(cs); + + /* + * We report both ESR and FAR to signal handlers. + * For now, it's easiest to deliver the fault normally. + */ + cpu_restore_state(cs, ra); + arm_deliver_fault(cpu, addr, access_type, MMU_USER_IDX, &fi); +} + +void arm_cpu_record_sigbus(CPUState *cs, vaddr addr, + MMUAccessType access_type, uintptr_t ra) +{ + arm_cpu_do_unaligned_access(cs, addr, access_type, MMU_USER_IDX, ra); +} +#endif /* !defined(CONFIG_USER_ONLY) */ diff --git a/target/arm/translate-a32.h b/target/arm/tcg/translate-a32.h similarity index 83% rename from target/arm/translate-a32.h rename to target/arm/tcg/translate-a32.h index 99eea85fa8..0b1fa57965 100644 --- a/target/arm/translate-a32.h +++ b/target/arm/tcg/translate-a32.h @@ -55,11 +55,16 @@ 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) \ @@ -70,9 +75,20 @@ static inline TCGv_i32 load_cpu_offset(int offset) 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)); \ + }) + +/* Store to the low half of a 64-bit field from a TCGv_i32 */ +#define store_cpu_field_low32(val, name) \ + ({ \ + QEMU_BUILD_BUG_ON(sizeof_field(CPUARMState, name) != 8); \ + store_cpu_offset(val, offsetoflow32(CPUARMState, name), 4); \ + }) #define store_cpu_field_constant(val, name) \ store_cpu_field(tcg_constant_i32(val), name) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c new file mode 100644 index 0000000000..b2851ea503 --- /dev/null +++ b/target/arm/tcg/translate-a64.c @@ -0,0 +1,11958 @@ +/* + * AArch64 translation + * + * 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 "exec/exec-all.h" +#include "translate.h" +#include "translate-a64.h" +#include "qemu/log.h" +#include "arm_ldst.h" +#include "semihosting/semihost.h" +#include "cpregs.h" + +static TCGv_i64 cpu_X[32]; +static TCGv_i64 cpu_pc; + +/* Load/store exclusive handling */ +static TCGv_i64 cpu_exclusive_high; + +static const char *regnames[] = { + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", + "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp" +}; + +enum a64_shift_type { + A64_SHIFT_TYPE_LSL = 0, + A64_SHIFT_TYPE_LSR = 1, + A64_SHIFT_TYPE_ASR = 2, + 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). + */ +typedef void AArch64DecodeFn(DisasContext *s, uint32_t insn); + +typedef struct AArch64DecodeTable { + uint32_t pattern; + uint32_t mask; + AArch64DecodeFn *disas_fn; +} AArch64DecodeTable; + +/* initialize TCG globals. */ +void a64_translate_init(void) +{ + int i; + + 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(tcg_env, + offsetof(CPUARMState, xregs[i]), + regnames[i]); + } + + 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 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, bool unpriv) +{ + /* + * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, + * which is the usual mmu_idx for this cpu state. + */ + ARMMMUIdx useridx = s->mmu_idx; + + if (unpriv && s->unpriv) { + /* + * We have pre-computed the condition for AccType_UNPRIV. + * Therefore we should never get here with a mmu_idx for + * which we do not know the corresponding user mmu_idx. + */ + switch (useridx) { + case ARMMMUIdx_E10_1: + case ARMMMUIdx_E10_1_PAN: + useridx = ARMMMUIdx_E10_0; + break; + case ARMMMUIdx_E20_2: + case ARMMMUIdx_E20_2_PAN: + useridx = ARMMMUIdx_E20_0; + break; + default: + g_assert_not_reached(); + } + } + return arm_to_core_mmu_idx(useridx); +} + +static void set_btype_raw(int val) +{ + tcg_gen_st_i32(tcg_constant_i32(val), tcg_env, + offsetof(CPUARMState, btype)); +} + +static void set_btype(DisasContext *s, int val) +{ + /* BTYPE is a 2-bit field, and 0 should be done with reset_btype. */ + tcg_debug_assert(val >= 1 && val <= 3); + set_btype_raw(val); + s->btype = -1; +} + +static void reset_btype(DisasContext *s) +{ + if (s->btype != 0) { + set_btype_raw(0); + s->btype = 0; + } +} + +static void gen_pc_plus_diff(DisasContext *s, TCGv_i64 dest, target_long diff) +{ + 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; +} + +/* + * Handle Top Byte Ignore (TBI) bits. + * + * If address tagging is enabled via the TCR TBI bits: + * + 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 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] + * + * Here We have concatenated TBI{1,0} into tbi. + */ +static void gen_top_byte_ignore(DisasContext *s, TCGv_i64 dst, + TCGv_i64 src, int tbi) +{ + if (tbi == 0) { + /* Load unmodified address */ + tcg_gen_mov_i64(dst, src); + } else if (!regime_has_2_ranges(s->mmu_idx)) { + /* Force tag byte to all zero */ + tcg_gen_extract_i64(dst, src, 0, 56); + } else { + /* Sign-extend from bit 55. */ + tcg_gen_sextract_i64(dst, src, 0, 56); + + switch (tbi) { + case 1: + /* tbi0 but !tbi1: only use the extension if positive */ + tcg_gen_and_i64(dst, dst, src); + break; + case 2: + /* !tbi0 but tbi1: only use the extension if negative */ + tcg_gen_or_i64(dst, dst, src); + break; + case 3: + /* tbi0 and tbi1: always use the extension */ + break; + default: + g_assert_not_reached(); + } + } +} + +static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) +{ + /* + * If address tagging is enabled for instructions via the TCR TBI bits, + * 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; +} + +/* + * Handle MTE and/or TBI. + * + * For TBI, ideally, we would do nothing. Proper behaviour on fault is + * for the tag to be present in the FAR_ELx register. But for user-only + * mode we do not have a TLB with which to implement this, so we must + * remove the top byte now. + * + * Always return a fresh temporary that we can increment independently + * of the write-back address. + */ + +TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr) +{ + TCGv_i64 clean = tcg_temp_new_i64(); +#ifdef CONFIG_USER_ONLY + gen_top_byte_ignore(s, clean, addr, s->tbid); +#else + tcg_gen_mov_i64(clean, addr); +#endif + return clean; +} + +/* Insert a zero tag into src, with the result at dst. */ +static void gen_address_with_allocation_tag0(TCGv_i64 dst, TCGv_i64 src) +{ + tcg_gen_andi_i64(dst, src, ~MAKE_64BIT_MASK(56, 4)); +} + +static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, + MMUAccessType acc, int log2_size) +{ + gen_helper_probe_access(tcg_env, ptr, + tcg_constant_i32(acc), + tcg_constant_i32(get_mem_index(s)), + tcg_constant_i32(1 << log2_size)); +} + +/* + * For MTE, check a single logical or atomic access. This probes a single + * address, the exact one specified. The size and alignment of the access + * is not relevant to MTE, per se, but watchpoints do require the size, + * and we want to recognize those before making any other changes to state. + */ +static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, + bool is_write, bool tag_checked, + MemOp memop, bool is_unpriv, + int core_idx) +{ + if (tag_checked && s->mte_active[is_unpriv]) { + TCGv_i64 ret; + int desc = 0; + + desc = FIELD_DP32(desc, MTEDESC, MIDX, core_idx); + 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, ALIGN, memop_alignment_bits(memop)); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, memop_size(memop) - 1); + + 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); +} + +TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, + bool tag_checked, MemOp memop) +{ + return gen_mte_check1_mmuidx(s, addr, is_write, tag_checked, memop, + false, get_mem_index(s)); +} + +/* + * For MTE, check multiple logical sequential accesses. + */ +TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, + bool tag_checked, int total_size, MemOp single_mop) +{ + if (tag_checked && s->mte_active[0]) { + TCGv_i64 ret; + int desc = 0; + + desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); + desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); + desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); + desc = FIELD_DP32(desc, MTEDESC, ALIGN, memop_alignment_bits(single_mop)); + desc = FIELD_DP32(desc, MTEDESC, SIZEM1, total_size - 1); + + 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; +} DisasCompare64; + +static void a64_test_cc(DisasCompare64 *c64, int cc) +{ + DisasCompare c32; + + 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. + */ + c64->cond = c32.cond; + c64->value = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(c64->value, c32.value); +} + +static void gen_rebuild_hflags(DisasContext *s) +{ + 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(tcg_env, tcg_constant_i32(excp)); +} + +static void gen_exception_internal_insn(DisasContext *s, int excp) +{ + 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_update_pc(s, 0); + gen_helper_exception_bkpt_insn(tcg_env, tcg_constant_i32(syndrome)); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_step_complete_exception(DisasContext *s) +{ + /* We just completed step of an insn. Move from Active-not-pending + * to Active-pending, and then also take the swstep exception. + * This corresponds to making the (IMPDEF) choice to prioritize + * swstep exceptions over asynchronous exceptions taken to an exception + * level where debug is disabled. This choice has the advantage that + * we do not need to maintain internal state corresponding to the + * ISV/EX syndrome bits between completion of the step and generation + * of the exception, and our syndrome information is always correct. + */ + gen_ss_advance(s); + gen_swstep_exception(s, 1, s->is_ldex); + s->base.is_jmp = DISAS_NORETURN; +} + +static inline bool use_goto_tb(DisasContext *s, uint64_t dest) +{ + if (s->ss_active) { + return false; + } + return translator_use_goto_tb(&s->base, dest); +} + +static void gen_goto_tb(DisasContext *s, int n, int64_t diff) +{ + 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_update_pc(s, diff); + if (s->ss_active) { + gen_step_complete_exception(s); + } else { + tcg_gen_lookup_and_goto_ptr(); + s->base.is_jmp = DISAS_NORETURN; + } + } +} + +/* + * Register access functions + * + * These functions are used for directly accessing a register in where + * changes to the final register value are likely to be made. If you + * need to use a register for temporary calculation (e.g. index type + * operations) use the read_* form. + * + * B1.2.1 Register mappings + * + * In instruction register encoding 31 can refer to ZR (zero register) or + * the SP (stack pointer) depending on context. In QEMU's case we map SP + * to cpu_X[31] and ZR accesses to a temporary which can be discarded. + * This is the point of the _sp forms. + */ +TCGv_i64 cpu_reg(DisasContext *s, int reg) +{ + if (reg == 31) { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_movi_i64(t, 0); + return t; + } else { + return cpu_X[reg]; + } +} + +/* register access for when 31 == SP */ +TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) +{ + return cpu_X[reg]; +} + +/* read a cpu register in 32bit/64bit mode. Returns a TCGv_i64 + * representing the register contents. This TCGv is an auto-freed + * temporary so it need not be explicitly freed, and may be modified. + */ +TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) +{ + TCGv_i64 v = tcg_temp_new_i64(); + if (reg != 31) { + if (sf) { + tcg_gen_mov_i64(v, cpu_X[reg]); + } else { + tcg_gen_ext32u_i64(v, cpu_X[reg]); + } + } else { + tcg_gen_movi_i64(v, 0); + } + return v; +} + +TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) +{ + TCGv_i64 v = tcg_temp_new_i64(); + if (sf) { + tcg_gen_mov_i64(v, cpu_X[reg]); + } else { + tcg_gen_ext32u_i64(v, cpu_X[reg]); + } + return v; +} + +/* Return the offset into CPUARMState of a slice (from + * the least significant end) of FP register Qn (ie + * Dn, Sn, Hn or Bn). + * (Note that this is not the same mapping as for A32; see cpu.h) + */ +static inline int fp_reg_offset(DisasContext *s, int regno, MemOp size) +{ + return vec_reg_offset(s, regno, 0, size); +} + +/* Offset of the high half of the 128 bit vector Qn */ +static inline int fp_reg_hi_offset(DisasContext *s, int regno) +{ + return vec_reg_offset(s, regno, 1, MO_64); +} + +/* Convenience accessors for reading and writing single and double + * FP registers. Writing clears the upper parts of the associated + * 128 bit vector register, as required by the architecture. + * Note that unlike the GP register accessors, the values returned + * by the read functions must be manually freed. + */ +static TCGv_i64 read_fp_dreg(DisasContext *s, int reg) +{ + TCGv_i64 v = tcg_temp_new_i64(); + + tcg_gen_ld_i64(v, tcg_env, fp_reg_offset(s, reg, MO_64)); + return v; +} + +static TCGv_i32 read_fp_sreg(DisasContext *s, int reg) +{ + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_ld_i32(v, tcg_env, fp_reg_offset(s, reg, MO_32)); + return v; +} + +static TCGv_i32 read_fp_hreg(DisasContext *s, int reg) +{ + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_ld16u_i32(v, tcg_env, fp_reg_offset(s, reg, MO_16)); + return v; +} + +/* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). + * If SVE is not enabled, then there are only 128 bits in the vector. + */ +static void clear_vec_high(DisasContext *s, bool is_q, int rd) +{ + unsigned ofs = fp_reg_offset(s, rd, MO_64); + unsigned vsz = vec_full_reg_size(s); + + /* Nop move, with side effect of clearing the tail. */ + tcg_gen_gvec_mov(MO_64, ofs, ofs, is_q ? 16 : 8, vsz); +} + +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, tcg_env, ofs); + clear_vec_high(s, false, reg); +} + +static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(tmp, v); + write_fp_dreg(s, reg, tmp); +} + +/* Expand a 2-operand AdvSIMD vector operation using an expander function. */ +static void gen_gvec_fn2(DisasContext *s, bool is_q, int rd, int rn, + GVecGen2Fn *gvec_fn, int vece) +{ + gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), + is_q ? 16 : 8, vec_full_reg_size(s)); +} + +/* Expand a 2-operand + immediate AdvSIMD vector operation using + * an expander function. + */ +static void gen_gvec_fn2i(DisasContext *s, bool is_q, int rd, int rn, + int64_t imm, GVecGen2iFn *gvec_fn, int vece) +{ + gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), + imm, is_q ? 16 : 8, vec_full_reg_size(s)); +} + +/* Expand a 3-operand AdvSIMD vector operation using an expander function. */ +static void gen_gvec_fn3(DisasContext *s, bool is_q, int rd, int rn, int rm, + GVecGen3Fn *gvec_fn, int vece) +{ + gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), is_q ? 16 : 8, vec_full_reg_size(s)); +} + +/* Expand a 4-operand AdvSIMD vector operation using an expander function. */ +static void gen_gvec_fn4(DisasContext *s, bool is_q, int rd, int rn, int rm, + int rx, GVecGen4Fn *gvec_fn, int vece) +{ + gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), vec_full_reg_offset(s, rx), + is_q ? 16 : 8, vec_full_reg_size(s)); +} + +/* Expand a 2-operand operation using an out-of-line helper. */ +static void gen_gvec_op2_ool(DisasContext *s, bool is_q, int rd, + int rn, int data, gen_helper_gvec_2 *fn) +{ + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + +/* Expand a 3-operand operation using an out-of-line helper. */ +static void gen_gvec_op3_ool(DisasContext *s, bool is_q, int rd, + int rn, int rm, int data, gen_helper_gvec_3 *fn) +{ + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + +/* Expand a 3-operand + fpstatus pointer + simd data value operation using + * an out-of-line helper. + */ +static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, + int rm, bool is_fp16, int data, + gen_helper_gvec_3_ptr *fn) +{ + TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + +/* Expand a 4-operand operation using an out-of-line helper. */ +static void gen_gvec_op4_ool(DisasContext *s, bool is_q, int rd, int rn, + int rm, int ra, int data, gen_helper_gvec_4 *fn) +{ + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + vec_full_reg_offset(s, ra), + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + +/* + * Expand a 4-operand operation using an out-of-line helper that takes + * a pointer to the CPU env. + */ +static void gen_gvec_op4_env(DisasContext *s, bool is_q, int rd, int rn, + int rm, int ra, int data, + gen_helper_gvec_4_ptr *fn) +{ + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + vec_full_reg_offset(s, ra), + tcg_env, + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + +/* + * Expand a 4-operand + fpstatus pointer + simd data value operation using + * an out-of-line helper. + */ +static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, + int rm, int ra, bool is_fp16, int data, + gen_helper_gvec_4_ptr *fn) +{ + TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, 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); +} + +/* Set ZF and NF based on a 64 bit result. This is alas fiddlier + * than the 32 bit equivalent. + */ +static inline void gen_set_NZ64(TCGv_i64 result) +{ + tcg_gen_extr_i64_i32(cpu_ZF, cpu_NF, result); + tcg_gen_or_i32(cpu_ZF, cpu_ZF, cpu_NF); +} + +/* Set NZCV as for a logical operation: NZ as per result, CV cleared. */ +static inline void gen_logic_CC(int sf, TCGv_i64 result) +{ + if (sf) { + gen_set_NZ64(result); + } else { + tcg_gen_extrl_i64_i32(cpu_ZF, result); + tcg_gen_mov_i32(cpu_NF, cpu_ZF); + } + tcg_gen_movi_i32(cpu_CF, 0); + tcg_gen_movi_i32(cpu_VF, 0); +} + +/* 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) { + gen_add64_CC(dest, t0, t1); + } else { + 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) { + gen_sub64_CC(dest, t0, t1); + } else { + gen_sub32_CC(dest, t0, t1); + } +} + +/* dest = T0 + T1 + CF; do not compute flags. */ +static void gen_adc(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + TCGv_i64 flag = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(flag, cpu_CF); + tcg_gen_add_i64(dest, t0, t1); + tcg_gen_add_i64(dest, dest, flag); + + if (!sf) { + tcg_gen_ext32u_i64(dest, dest); + } +} + +/* dest = T0 + T1 + CF; compute C, N, V and Z flags. */ +static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) +{ + if (sf) { + TCGv_i64 result = tcg_temp_new_i64(); + TCGv_i64 cf_64 = tcg_temp_new_i64(); + TCGv_i64 vf_64 = tcg_temp_new_i64(); + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + + tcg_gen_extu_i32_i64(cf_64, cpu_CF); + tcg_gen_add2_i64(result, cf_64, t0, zero, cf_64, zero); + tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, zero); + tcg_gen_extrl_i64_i32(cpu_CF, cf_64); + gen_set_NZ64(result); + + tcg_gen_xor_i64(vf_64, result, t0); + tcg_gen_xor_i64(tmp, t0, t1); + tcg_gen_andc_i64(vf_64, vf_64, tmp); + tcg_gen_extrh_i64_i32(cpu_VF, vf_64); + + tcg_gen_mov_i64(dest, result); + } else { + TCGv_i32 t0_32 = tcg_temp_new_i32(); + TCGv_i32 t1_32 = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + + tcg_gen_extrl_i64_i32(t0_32, t0); + tcg_gen_extrl_i64_i32(t1_32, t1); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, zero, cpu_CF, zero); + tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, zero); + + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); + 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); + } +} + +/* + * Load/Store generators + */ + +/* + * Store from GPR register to memory. + */ +static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source, + TCGv_i64 tcg_addr, MemOp memop, int memidx, + bool iss_valid, + unsigned int iss_srt, + bool iss_sf, bool iss_ar) +{ + tcg_gen_qemu_st_i64(source, tcg_addr, memidx, memop); + + if (iss_valid) { + uint32_t syn; + + syn = syn_data_abort_with_iss(0, + (memop & MO_SIZE), + false, + iss_srt, + iss_sf, + iss_ar, + 0, 0, 0, 0, 0, false); + disas_set_insn_syndrome(s, syn); + } +} + +static void do_gpr_st(DisasContext *s, TCGv_i64 source, + TCGv_i64 tcg_addr, MemOp memop, + bool iss_valid, + unsigned int iss_srt, + bool iss_sf, bool iss_ar) +{ + do_gpr_st_memidx(s, source, tcg_addr, memop, get_mem_index(s), + iss_valid, iss_srt, iss_sf, iss_ar); +} + +/* + * Load from memory to GPR register + */ +static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, + MemOp memop, bool extend, int memidx, + bool iss_valid, unsigned int iss_srt, + bool iss_sf, bool iss_ar) +{ + tcg_gen_qemu_ld_i64(dest, tcg_addr, memidx, memop); + + if (extend && (memop & MO_SIGN)) { + g_assert((memop & MO_SIZE) <= MO_32); + tcg_gen_ext32u_i64(dest, dest); + } + + if (iss_valid) { + uint32_t syn; + + syn = syn_data_abort_with_iss(0, + (memop & MO_SIZE), + (memop & MO_SIGN) != 0, + iss_srt, + iss_sf, + iss_ar, + 0, 0, 0, 0, 0, false); + disas_set_insn_syndrome(s, syn); + } +} + +static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, + MemOp memop, bool extend, + bool iss_valid, unsigned int iss_srt, + bool iss_sf, bool iss_ar) +{ + do_gpr_ld_memidx(s, dest, tcg_addr, memop, extend, get_mem_index(s), + iss_valid, iss_srt, iss_sf, iss_ar); +} + +/* + * Store from FP register to memory + */ +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(); + + tcg_gen_ld_i64(tmplo, tcg_env, fp_reg_offset(s, srcidx, MO_64)); + + if ((mop & MO_SIZE) < MO_128) { + tcg_gen_qemu_st_i64(tmplo, tcg_addr, get_mem_index(s), mop); + } else { + TCGv_i64 tmphi = tcg_temp_new_i64(); + TCGv_i128 t16 = tcg_temp_new_i128(); + + tcg_gen_ld_i64(tmphi, tcg_env, fp_reg_hi_offset(s, srcidx)); + tcg_gen_concat_i64_i128(t16, tmplo, tmphi); + + tcg_gen_qemu_st_i128(t16, tcg_addr, get_mem_index(s), mop); + } +} + +/* + * Load from memory to FP register + */ +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; + + if ((mop & MO_SIZE) < MO_128) { + tcg_gen_qemu_ld_i64(tmplo, tcg_addr, get_mem_index(s), mop); + } else { + 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_gen_extr_i128_i64(tmplo, tmphi, t16); + } + + tcg_gen_st_i64(tmplo, tcg_env, fp_reg_offset(s, destidx, MO_64)); + + if (tmphi) { + tcg_gen_st_i64(tmphi, tcg_env, fp_reg_hi_offset(s, destidx)); + } + clear_vec_high(s, tmphi != NULL, destidx); +} + +/* + * Vector load/store helpers. + * + * The principal difference between this and a FP load is that we don't + * zero extend as we are filling a partial chunk of the vector register. + * These functions don't support 128 bit loads/stores, which would be + * normal load/store operations. + * + * The _i32 versions are useful when operating on 32 bit quantities + * (eg for floating point single or using Neon helper functions). + */ + +/* Get value of an element within a vector register */ +static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, + int element, MemOp memop) +{ + int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); + switch ((unsigned)memop) { + case MO_8: + tcg_gen_ld8u_i64(tcg_dest, tcg_env, vect_off); + break; + case MO_16: + tcg_gen_ld16u_i64(tcg_dest, tcg_env, vect_off); + break; + case MO_32: + tcg_gen_ld32u_i64(tcg_dest, tcg_env, vect_off); + break; + case MO_8|MO_SIGN: + tcg_gen_ld8s_i64(tcg_dest, tcg_env, vect_off); + break; + case MO_16|MO_SIGN: + tcg_gen_ld16s_i64(tcg_dest, tcg_env, vect_off); + break; + case MO_32|MO_SIGN: + 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, tcg_env, vect_off); + break; + default: + g_assert_not_reached(); + } +} + +static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx, + int element, MemOp memop) +{ + int vect_off = vec_reg_offset(s, srcidx, element, memop & MO_SIZE); + switch (memop) { + case MO_8: + tcg_gen_ld8u_i32(tcg_dest, tcg_env, vect_off); + break; + case MO_16: + tcg_gen_ld16u_i32(tcg_dest, tcg_env, vect_off); + break; + case MO_8|MO_SIGN: + tcg_gen_ld8s_i32(tcg_dest, tcg_env, vect_off); + break; + case MO_16|MO_SIGN: + 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, tcg_env, vect_off); + break; + default: + g_assert_not_reached(); + } +} + +/* Set value of an element within a vector register */ +static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx, + int element, MemOp memop) +{ + int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); + switch (memop) { + case MO_8: + tcg_gen_st8_i64(tcg_src, tcg_env, vect_off); + break; + case MO_16: + tcg_gen_st16_i64(tcg_src, tcg_env, vect_off); + break; + case MO_32: + tcg_gen_st32_i64(tcg_src, tcg_env, vect_off); + break; + case MO_64: + tcg_gen_st_i64(tcg_src, tcg_env, vect_off); + break; + default: + g_assert_not_reached(); + } +} + +static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src, + int destidx, int element, MemOp memop) +{ + int vect_off = vec_reg_offset(s, destidx, element, memop & MO_SIZE); + switch (memop) { + case MO_8: + tcg_gen_st8_i32(tcg_src, tcg_env, vect_off); + break; + case MO_16: + tcg_gen_st16_i32(tcg_src, tcg_env, vect_off); + break; + case MO_32: + tcg_gen_st_i32(tcg_src, tcg_env, vect_off); + break; + default: + g_assert_not_reached(); + } +} + +/* Store from vector register to memory */ +static void do_vec_st(DisasContext *s, int srcidx, int element, + TCGv_i64 tcg_addr, MemOp mop) +{ + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + + 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); +} + +/* Load from memory to vector register */ +static void do_vec_ld(DisasContext *s, int destidx, int element, + TCGv_i64 tcg_addr, MemOp mop) +{ + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + + 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); +} + +/* Check that FP/Neon access is enabled. If it is, return + * true. If not, emit code to generate an appropriate exception, + * and return false; the caller should not emit any code for + * the instruction. Note that this check must happen after all + * unallocated-encoding checks (otherwise the syndrome information + * for the resulting exception will be incorrect). + */ +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_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; +} + +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->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; +} + +/* + * Expanders for AdvSIMD translation functions. + */ + +static bool do_gvec_op2_ool(DisasContext *s, arg_qrr_e *a, int data, + gen_helper_gvec_2 *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_op2_ool(s, a->q, a->rd, a->rn, data, fn); + } + return true; +} + +static bool do_gvec_op3_ool(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3 *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_op3_ool(s, a->q, a->rd, a->rn, a->rm, data, fn); + } + return true; +} + +static bool do_gvec_fn3(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn3(s, a->q, a->rd, a->rn, a->rm, fn, a->esz); + } + return true; +} + +static bool do_gvec_fn3_no64(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) +{ + if (a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn3(s, a->q, a->rd, a->rn, a->rm, fn, a->esz); + } + return true; +} + +static bool do_gvec_fn3_no8_no64(DisasContext *s, arg_qrrr_e *a, GVecGen3Fn *fn) +{ + if (a->esz == MO_8) { + return false; + } + return do_gvec_fn3_no64(s, a, fn); +} + +static bool do_gvec_fn4(DisasContext *s, arg_qrrrr_e *a, GVecGen4Fn *fn) +{ + if (!a->q && a->esz == MO_64) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_fn4(s, a->q, a->rd, a->rn, a->rm, a->ra, fn, a->esz); + } + return true; +} + +/* + * This utility function is for doing register extension with an + * optional shift. You will likely want to pass a temporary for the + * destination register. See DecodeRegExtend() in the ARM ARM. + */ +static void ext_and_shift_reg(TCGv_i64 tcg_out, TCGv_i64 tcg_in, + int option, unsigned int shift) +{ + int extsize = extract32(option, 0, 2); + bool is_signed = extract32(option, 2, 1); + + 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) +{ + /* The AArch64 architecture mandates that (if enabled via PSTATE + * or SCTLR bits) there is a check that SP is 16-aligned on every + * SP-relative load or store (with an exception generated if it is not). + * In line with general QEMU practice regarding misaligned accesses, + * we omit these checks for the sake of guest program performance. + * This function is provided as a hook so we can more easily add these + * checks in future (possibly as a "favour catching guest program bugs + * over speed" user selectable option). + */ +} + +/* + * This provides a simple table based table lookup decoder. It is + * intended to be used when the relevant bits for decode are too + * awkwardly placed and switch/if based logic would be confusing and + * deeply nested. Since it's a linear search through the table, tables + * should be kept small. + * + * It returns the first handler where insn & mask == pattern, or + * NULL if there is no match. + * The table is terminated by an empty mask (i.e. 0) + */ +static inline AArch64DecodeFn *lookup_disas_fn(const AArch64DecodeTable *table, + uint32_t insn) +{ + const AArch64DecodeTable *tptr = table; + + while (tptr->mask) { + if ((insn & tptr->mask) == tptr->pattern) { + return tptr->disas_fn; + } + tptr++; + } + return NULL; +} + +/* + * The instruction disassembly implemented here matches + * the instruction encoding classifications in chapter C4 + * of the ARM Architecture Reference Manual (DDI0487B_a); + * classification names and decode diagrams here should generally + * match up with those in the manual. + */ + +static bool trans_B(DisasContext *s, arg_i *a) +{ + reset_btype(s); + gen_goto_tb(s, 0, a->imm); + return true; +} + +static bool trans_BL(DisasContext *s, arg_i *a) +{ + gen_pc_plus_diff(s, cpu_reg(s, 30), curr_insn_len(s)); + reset_btype(s); + gen_goto_tb(s, 0, a->imm); + return true; +} + + +static bool trans_CBZ(DisasContext *s, arg_cbz *a) +{ + DisasLabel match; + TCGv_i64 tcg_cmp; + + 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, a->rt), 1ULL << a->bitpos); + + 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_B_cond(DisasContext *s, arg_B_cond *a) +{ + /* BC.cond is only present with FEAT_HBC */ + if (a->c && !dc_isar_feature(aa64_hbc, s)) { + return false; + } + reset_btype(s); + if (a->cond < 0x0e) { + /* genuinely conditional branches */ + 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, a->imm); + } + return true; +} + +static void set_btype_for_br(DisasContext *s, int rn) +{ + if (dc_isar_feature(aa64_bti, s)) { + /* BR to {x16,x17} or !guard -> 1, else 3. */ + if (rn == 16 || rn == 17) { + set_btype(s, 1); + } else { + TCGv_i64 pc = tcg_temp_new_i64(); + gen_pc_plus_diff(s, pc, 0); + gen_helper_guarded_page_br(tcg_env, pc); + s->btype = -1; + } + } +} + +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) +{ + set_btype_for_br(s, a->rn); + gen_a64_set_pc(s, cpu_reg(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); + set_btype_for_br(s, a->rn); + gen_a64_set_pc(s, dst); + 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_WFIT(DisasContext *s, arg_WFIT *a) +{ + if (!dc_isar_feature(aa64_wfxt, s)) { + return false; + } + + /* + * Because we need to pass the register value to the helper, + * it's easier to emit the code now, unlike trans_WFI which + * defers it to aarch64_tr_tb_stop(). That means we need to + * check ss_active so that single-stepping a WFIT doesn't halt. + */ + if (s->ss_active) { + /* Act like a NOP under architectural singlestep */ + return true; + } + + gen_a64_update_pc(s, 4); + gen_helper_wfit(tcg_env, cpu_reg(s, a->rd)); + /* Go back to the main loop to check for interrupts */ + s->base.is_jmp = DISAS_EXIT; + return true; +} + +static bool trans_WFET(DisasContext *s, arg_WFET *a) +{ + if (!dc_isar_feature(aa64_wfxt, s)) { + return false; + } + + /* + * We rely here on our WFE implementation being a NOP, so we + * don't need to do anything different to handle the WFET timeout + * from what trans_WFE does. + */ + 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; +} + +static bool trans_DSB_DMB(DisasContext *s, arg_DSB_DMB *a) +{ + /* We handle DSB and DMB the same way */ + TCGBar bar; + + 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 bool trans_ISB(DisasContext *s, arg_ISB *a) +{ + /* + * 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); + + /* + * (!C & !Z) << 31 + * (!(C | Z)) << 31 + * ~((C | Z) << 31) + * ~-(C | Z) + * (C | Z) - 1 + */ + tcg_gen_or_i32(cpu_NF, cpu_CF, z); + tcg_gen_subi_i32(cpu_NF, cpu_NF, 1); + + /* !(Z & C) */ + tcg_gen_and_i32(cpu_ZF, z, cpu_CF); + tcg_gen_xori_i32(cpu_ZF, cpu_ZF, 1); + + /* (!C & Z) << 31 -> -(Z & ~C) */ + tcg_gen_andc_i32(cpu_VF, z, cpu_CF); + tcg_gen_neg_i32(cpu_VF, cpu_VF); + + /* C | Z */ + tcg_gen_or_i32(cpu_CF, cpu_CF, z); + + return true; +} + +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 */ + + /* !(Z | V) -> !(!ZF | V) -> ZF & !V -> ZF & ~VF */ + tcg_gen_andc_i32(cpu_ZF, cpu_ZF, cpu_VF); + + tcg_gen_movi_i32(cpu_NF, 0); + tcg_gen_movi_i32(cpu_VF, 0); + + return true; +} + +static bool trans_MSR_i_UAO(DisasContext *s, arg_i *a) +{ + 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_ALLINT(DisasContext *s, arg_i *a) +{ + if (!dc_isar_feature(aa64_nmi, s) || s->current_el == 0) { + return false; + } + + if (a->imm == 0) { + clear_pstate_bits(PSTATE_ALLINT); + } else if (s->current_el > 1) { + set_pstate_bits(PSTATE_ALLINT); + } else { + gen_helper_msr_set_allint_el1(tcg_env); + } + + /* 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) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 nzcv = tcg_temp_new_i32(); + + /* build bit 31, N */ + tcg_gen_andi_i32(nzcv, cpu_NF, (1U << 31)); + /* build bit 30, Z */ + tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_ZF, 0); + tcg_gen_deposit_i32(nzcv, nzcv, tmp, 30, 1); + /* build bit 29, C */ + tcg_gen_deposit_i32(nzcv, nzcv, cpu_CF, 29, 1); + /* build bit 28, V */ + tcg_gen_shri_i32(tmp, cpu_VF, 31); + tcg_gen_deposit_i32(nzcv, nzcv, tmp, 28, 1); + /* generate result */ + tcg_gen_extu_i32_i64(tcg_rt, nzcv); +} + +static void gen_set_nzcv(TCGv_i64 tcg_rt) +{ + TCGv_i32 nzcv = tcg_temp_new_i32(); + + /* take NZCV from R[t] */ + tcg_gen_extrl_i64_i32(nzcv, tcg_rt); + + /* bit 31, N */ + tcg_gen_andi_i32(cpu_NF, nzcv, (1U << 31)); + /* bit 30, Z */ + tcg_gen_andi_i32(cpu_ZF, nzcv, (1 << 30)); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_ZF, cpu_ZF, 0); + /* bit 29, C */ + tcg_gen_andi_i32(cpu_CF, nzcv, (1 << 29)); + tcg_gen_shri_i32(cpu_CF, cpu_CF, 29); + /* bit 28, V */ + tcg_gen_andi_i32(cpu_VF, nzcv, (1 << 28)); + tcg_gen_shli_i32(cpu_VF, cpu_VF, 3); +} + +static void gen_sysreg_undef(DisasContext *s, bool isread, + uint8_t op0, uint8_t op1, uint8_t op2, + uint8_t crn, uint8_t crm, uint8_t rt) +{ + /* + * Generate code to emit an UNDEF with correct syndrome + * information for a failed system register access. + * This is EC_UNCATEGORIZED (ie a standard UNDEF) in most cases, + * but if FEAT_IDST is implemented then read accesses to registers + * in the feature ID space are reported with the EC_SYSTEMREGISTERTRAP + * syndrome. + */ + uint32_t syndrome; + + if (isread && dc_isar_feature(aa64_ids, s) && + arm_cpreg_encoding_in_idspace(op0, op1, op2, crn, crm)) { + syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); + } else { + syndrome = syn_uncategorized(); + } + gen_exception_insn(s, 0, EXCP_UDEF, syndrome); +} + +/* MRS - move from system register + * MSR (register) - move to system register + * SYS + * SYSL + * These are all essentially the same insn in 'read' and 'write' + * versions, with varying op0 fields. + */ +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) +{ + 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); + + 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 + * unimplemented feature. + */ + qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch64 " + "system register op0:%d op1:%d crn:%d crm:%d op2:%d\n", + isread ? "read" : "write", op0, op1, crn, crm, op2); + 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; + } + } + + /* 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. + */ + 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) { + /* + * The readfn or writefn might raise an exception; + * synchronize the CPU state in case it does. + */ + 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 */ + switch (ri->type & ARM_CP_SPECIAL_MASK) { + case 0: + break; + case ARM_CP_NOP: + return; + case ARM_CP_NZCV: + tcg_rt = cpu_reg(s, rt); + if (isread) { + gen_get_nzcv(tcg_rt); + } else { + gen_set_nzcv(tcg_rt); + } + return; + case ARM_CP_CURRENTEL: + { + /* + * 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, el << 2); + return; + } + case ARM_CP_DC_ZVA: + /* Writes clear the aligned block of memory which rt points into. */ + if (s->mte_active[0]) { + int desc = 0; + + desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); + desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); + desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); + + 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(tcg_env, tcg_rt); + return; + case ARM_CP_DC_GVA: + { + TCGv_i64 clean_addr, tag; + + /* + * DC_GVA, like DC_ZVA, requires that we supply the original + * pointer for an invalid page. Probe that address first. + */ + tcg_rt = cpu_reg(s, rt); + clean_addr = clean_data_tbi(s, tcg_rt); + gen_probe_access(s, clean_addr, MMU_DATA_STORE, MO_8); + + 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(tcg_env, clean_addr, tag); + } + } + return; + case ARM_CP_DC_GZVA: + { + TCGv_i64 clean_addr, tag; + + /* 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(tcg_env, clean_addr); + + 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(tcg_env, clean_addr, tag); + } + } + 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); + } + + tcg_rt = cpu_reg(s, rt); + + if (isread) { + if (ri->type & ARM_CP_CONST) { + tcg_gen_movi_i64(tcg_rt, ri->resetvalue); + } else if (ri->readfn) { + 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, 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) { + 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, tcg_env, ri->fieldoffset); + } + } + + 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); + /* + * 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) { + s->base.is_jmp = DISAS_UPDATE_EXIT; + } +} + +static bool trans_SYS(DisasContext *s, arg_SYS *a) +{ + handle_sys(s, a->l, a->op0, a->op1, a->op2, a->crn, a->crm, a->rt); + return true; +} + +static bool trans_SVC(DisasContext *s, arg_i *a) +{ + /* + * 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; +} + +static bool trans_HVC(DisasContext *s, arg_i *a) +{ + int target_el = s->current_el == 3 ? 3 : 2; + + if (s->current_el == 0) { + unallocated_encoding(s); + return true; + } + /* + * 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; +} + +static bool trans_SMC(DisasContext *s, arg_i *a) +{ + if (s->current_el == 0) { + unallocated_encoding(s); + 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; +} + +/* + * Load/Store exclusive instructions are implemented by remembering + * the value/address loaded, and seeing if these are the same + * when the store is performed. This is not actually the architecturally + * mandated semantics, but it works for typical guest code sequences + * and avoids having to monitor regular stores. + * + * The store exclusive uses the atomic cmpxchg primitives to avoid + * races in multi-threaded linux-user and when MTTCG softmmu is + * enabled. + */ +static void gen_load_exclusive(DisasContext *s, int rt, int rt2, int rn, + int size, bool is_pair) +{ + int idx = get_mem_index(s); + 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) { + 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); + } else { + tcg_gen_extract_i64(cpu_reg(s, rt), cpu_exclusive_val, 32, 32); + tcg_gen_extract_i64(cpu_reg(s, rt2), cpu_exclusive_val, 0, 32); + } + } else { + TCGv_i128 t16 = tcg_temp_new_i128(); + + 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 { + 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, clean_addr); +} + +static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, + int rn, int size, int is_pair) +{ + /* if (env->exclusive_addr == addr && env->exclusive_val == [addr] + * && (!is_pair || env->exclusive_high == [addr + datasize])) { + * [addr] = {Rt}; + * if (is_pair) { + * [addr + datasize] = {Rt2}; + * } + * {Rd} = 0; + * } else { + * {Rd} = 1; + * } + * env->exclusive_addr = -1; + */ + TCGLabel *fail_label = gen_new_label(); + TCGLabel *done_label = gen_new_label(); + TCGv_i64 tmp, clean_addr; + MemOp memop; + + /* + * 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) { + if (size == 2) { + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(tmp, cpu_reg(s, rt), cpu_reg(s, rt2)); + } else { + tcg_gen_concat32_i64(tmp, cpu_reg(s, rt2), cpu_reg(s, rt)); + } + tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, + cpu_exclusive_val, tmp, + get_mem_index(s), memop); + tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); + } else { + 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), memop); + tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); + } + tcg_gen_mov_i64(cpu_reg(s, rd), tmp); + tcg_gen_br(done_label); + + gen_set_label(fail_label); + tcg_gen_movi_i64(cpu_reg(s, rd), 1); + gen_set_label(done_label); + tcg_gen_movi_i64(cpu_exclusive_addr, -1); +} + +static void gen_compare_and_swap(DisasContext *s, int rs, int rt, + int rn, int size) +{ + TCGv_i64 tcg_rs = cpu_reg(s, rs); + 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); + } + 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, + int rn, int size) +{ + TCGv_i64 s1 = cpu_reg(s, rs); + TCGv_i64 s2 = cpu_reg(s, rs + 1); + TCGv_i64 t1 = cpu_reg(s, 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". */ + 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(); + TCGv_i64 val = tcg_temp_new_i64(); + + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(val, t1, t2); + tcg_gen_concat32_i64(cmp, s1, s2); + } else { + tcg_gen_concat32_i64(val, t2, t1); + tcg_gen_concat32_i64(cmp, s2, s1); + } + + 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); + } + } else { + TCGv_i128 cmp = tcg_temp_new_i128(); + TCGv_i128 val = tcg_temp_new_i128(); + + 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); + } + + tcg_gen_atomic_cmpxchg_i128(cmp, clean_addr, cmp, val, memidx, memop); + + if (s->be_data == MO_LE) { + tcg_gen_extr_i128_i64(s1, s2, cmp); + } else { + tcg_gen_extr_i128_i64(s2, s1, cmp); + } + } +} + +/* + * 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 ldst_iss_sf(int size, bool sign, bool ext) +{ + + 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.) + */ + 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; + } + + 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; +} + +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); + + 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, 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); + } + + clean_addr = clean_data_tbi(s, dirty_addr); + tcg_rt = cpu_reg(s, a->rt); + tcg_rt2 = cpu_reg(s, a->rt2); + + /* + * 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); + + 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); + + /* 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 { + gen_helper_stg(tcg_env, dirty_addr, dirty_addr); + } + } + + op_addr_ldstpair_post(s, a, dirty_addr, offset); + return true; +} + +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 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 (!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; +} + +static bool trans_LDR_v_i(DisasContext *s, arg_ldst_imm *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp mop; + + 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; +} + +static void op_addr_ldst_pre(DisasContext *s, arg_ldst *a, + TCGv_i64 *clean_addr, TCGv_i64 *dirty_addr, + bool is_store, MemOp memop) +{ + TCGv_i64 tcg_rm; + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + *dirty_addr = read_cpu_reg_sp(s, a->rn, 1); + + 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; + } + + 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; +} + +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 + * 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) { + 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; +} + +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) +{ + bool iss_sf = ldst_iss_sf(a->sz, false, false); + TCGv_i64 clean_addr; + MemOp mop; + + if (!dc_isar_feature(aa64_atomics, s) || + !dc_isar_feature(aa64_rcpc_8_3, s)) { + return false; + } + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + mop = 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, 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 (!a->m) { + gen_helper_autda_combined(dirty_addr, tcg_env, dirty_addr, + tcg_constant_i64(0)); + } else { + gen_helper_autdb_combined(dirty_addr, tcg_env, dirty_addr, + tcg_constant_i64(0)); + } + } + + 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, + a->w || a->rn != 31, memop); + + 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 (a->w) { + tcg_gen_mov_i64(cpu_reg_sp(s, a->rn), dirty_addr); + } + return true; +} + +static bool trans_LDAPR_i(DisasContext *s, arg_ldapr_stlr_i *a) +{ + TCGv_i64 clean_addr, dirty_addr; + 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)) { + return false; + } + + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + + 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); + + /* + * 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; +} + +static bool trans_STLR_i(DisasContext *s, arg_ldapr_stlr_i *a) +{ + TCGv_i64 clean_addr, dirty_addr; + MemOp mop = a->sz; + bool iss_sf = ldst_iss_sf(a->sz, a->sign, a->ext); + + if (!dc_isar_feature(aa64_rcpc_8_4, s)) { + return false; + } + + /* 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; +} + +static bool trans_LD_mult(DisasContext *s, arg_ldst_mult *a) +{ + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp endian, align, mop; + + 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, 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 (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_ld(s, tt, e, clean_addr, mop); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); + } + } + } + + /* + * 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 (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_mult(DisasContext *s, arg_ldst_mult *a) +{ + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + MemOp endian, align, mop; + + 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 (!a->p && a->rm != 0) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + 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, true, 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_st(s, rt, a->index, 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_LD_single(DisasContext *s, arg_ldst_single *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 (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) { + do_vec_ld(s, rt, a->index, 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_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 (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 { + 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 { + if (is_pair) { + gen_helper_st2g(tcg_env, addr, tcg_rt); + } else { + gen_helper_stg(tcg_env, addr, tcg_rt); + } + } + + if (is_zero) { + TCGv_i64 clean_addr = clean_data_tbi(s, addr); + TCGv_i64 zero64 = tcg_constant_i64(0); + TCGv_i128 zero128 = tcg_temp_new_i128(); + int mem_index = get_mem_index(s); + MemOp mop = finalize_memop(s, MO_128 | MO_ALIGN); + + 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 (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; +} + +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) +{ + 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; +} + +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 bool trans_ADR(DisasContext *s, arg_ri *a) +{ + gen_pc_plus_diff(s, cpu_reg(s, a->rd), a->imm); + return true; +} + +static bool trans_ADRP(DisasContext *s, arg_ri *a) +{ + int64_t offset = (int64_t)a->imm << 12; + + /* 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) + */ +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) + */ + +static bool gen_add_sub_imm_with_tags(DisasContext *s, arg_rri_tag *a, + bool sub_op) +{ + TCGv_i64 tcg_rn, tcg_rd; + int imm; + + imm = a->uimm6 << LOG2_TAG_GRANULE; + if (sub_op) { + imm = -imm; + } + + tcg_rn = cpu_reg_sp(s, a->rn); + tcg_rd = cpu_reg_sp(s, a->rd); + + if (s->ata[0]) { + gen_helper_addsubg(tcg_rd, tcg_env, tcg_rn, + tcg_constant_i32(imm), + 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. + */ +static uint64_t bitfield_replicate(uint64_t mask, unsigned int e) +{ + assert(e != 0); + while (e < 64) { + mask |= mask << e; + e *= 2; + } + return mask; +} + +/* + * Logical (immediate) + */ + +/* + * 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. + */ +bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + unsigned int imms, unsigned int immr) +{ + uint64_t mask; + unsigned e, levels, s, r; + int len; + + assert(immn < 2 && imms < 64 && immr < 64); + + /* The bit patterns we create here are 64 bit patterns which + * are vectors of identical elements of size e = 2, 4, 8, 16, 32 or + * 64 bits each. Each element contains the same value: a run + * of between 1 and e-1 non-zero bits, rotated within the + * element by between 0 and e-1 bits. + * + * The element size and run length are encoded into immn (1 bit) + * and imms (6 bits) as follows: + * 64 bit elements: immn = 1, imms = + * 32 bit elements: immn = 0, imms = 0 : + * 16 bit elements: immn = 0, imms = 10 : + * 8 bit elements: immn = 0, imms = 110 : + * 4 bit elements: immn = 0, imms = 1110 : + * 2 bit elements: immn = 0, imms = 11110 : + * Notice that immn = 0, imms = 11111x is the only combination + * not covered by one of the above options; this is reserved. + * Further, all-ones is a reserved pattern. + * + * In all cases the rotation is by immr % e (and immr is 6 bits). + */ + + /* First determine the element size */ + len = 31 - clz32((immn << 6) | (~imms & 0x3f)); + if (len < 1) { + /* This is the immn == 0, imms == 0x11111x case */ + return false; + } + e = 1 << len; + + levels = e - 1; + s = imms & levels; + r = immr & levels; + + if (s == levels) { + /* mustn't be all-ones. */ + return false; + } + + /* Create the value of one element: s+1 set bits rotated + * by r within the element (which is e bits wide)... + */ + mask = MAKE_64BIT_MASK(0, s + 1); + if (r) { + mask = (mask >> r) | (mask << (e - r)); + mask &= MAKE_64BIT_MASK(0, e); + } + /* ...then replicate the element over the whole 64 bit value */ + mask = bitfield_replicate(mask, e); + *result = mask; + return true; +} + +static bool gen_rri_log(DisasContext *s, arg_rri_log *a, bool set_cc, + void (*fn)(TCGv_i64, TCGv_i64, int64_t)) +{ + TCGv_i64 tcg_rd, tcg_rn; + uint64_t imm; + + /* 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; + } + + tcg_rd = set_cc ? cpu_reg(s, a->rd) : cpu_reg_sp(s, a->rd); + tcg_rn = cpu_reg(s, a->rn); + + fn(tcg_rd, tcg_rn, imm); + if (set_cc) { + gen_logic_CC(a->sf, tcg_rd); + } + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, 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) + */ + +static bool trans_MOVZ(DisasContext *s, arg_movw *a) +{ + int pos = a->hw << 4; + tcg_gen_movi_i64(cpu_reg(s, a->rd), (uint64_t)a->imm << pos); + return true; +} + +static bool trans_MOVN(DisasContext *s, arg_movw *a) +{ + int pos = a->hw << 4; + uint64_t imm = a->imm; + + imm = ~(imm << pos); + if (!a->sf) { + imm = (uint32_t)imm; + } + tcg_gen_movi_i64(cpu_reg(s, a->rd), imm); + return true; +} + +static bool trans_MOVK(DisasContext *s, arg_movw *a) +{ + int pos = a->hw << 4; + TCGv_i64 tcg_rd, tcg_im; + + 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; + + if (si >= ri) { + /* Wd = Wn */ + len = (si - ri) + 1; + tcg_gen_sextract_i64(tcg_rd, tcg_tmp, ri, len); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } 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 { + /* Wd<32+s-r,32-r> = Wn */ + len = si + 1; + pos = (bitsize - ri) & (bitsize - 1); + } + + 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; +} + +static bool trans_EXTR(DisasContext *s, arg_extract *a) +{ + TCGv_i64 tcg_rd, tcg_rm, tcg_rn; + + tcg_rd = cpu_reg(s, a->rd); + + 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_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 (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 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); + } + } + return true; +} + +static bool trans_TBL_TBX(DisasContext *s, arg_TBL_TBX *a) +{ + if (fp_access_check(s)) { + int len = (a->len + 1) * 16; + + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rm), tcg_env, + a->q ? 16 : 8, vec_full_reg_size(s), + (len << 6) | (a->tbx << 5) | a->rn, + gen_helper_simd_tblx); + } + return true; +} + +typedef int simd_permute_idx_fn(int i, int part, int elements); + +static bool do_simd_permute(DisasContext *s, arg_qrrr_e *a, + simd_permute_idx_fn *fn, int part) +{ + MemOp esz = a->esz; + int datasize = a->q ? 16 : 8; + int elements = datasize >> esz; + TCGv_i64 tcg_res[2], tcg_ele; + + if (esz == MO_64 && !a->q) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + tcg_res[0] = tcg_temp_new_i64(); + tcg_res[1] = a->q ? tcg_temp_new_i64() : NULL; + tcg_ele = tcg_temp_new_i64(); + + for (int i = 0; i < elements; i++) { + int o, w, idx; + + idx = fn(i, part, elements); + read_vec_element(s, tcg_ele, (idx & elements ? a->rm : a->rn), + idx & (elements - 1), esz); + + w = (i << (esz + 3)) / 64; + o = (i << (esz + 3)) % 64; + if (o == 0) { + tcg_gen_mov_i64(tcg_res[w], tcg_ele); + } else { + tcg_gen_deposit_i64(tcg_res[w], tcg_res[w], tcg_ele, o, 8 << esz); + } + } + + for (int i = a->q; i >= 0; --i) { + write_vec_element(s, tcg_res[i], a->rd, i, MO_64); + } + clear_vec_high(s, a->q, a->rd); + return true; +} + +static int permute_load_uzp(int i, int part, int elements) +{ + return 2 * i + part; +} + +TRANS(UZP1, do_simd_permute, a, permute_load_uzp, 0) +TRANS(UZP2, do_simd_permute, a, permute_load_uzp, 1) + +static int permute_load_trn(int i, int part, int elements) +{ + return (i & 1) * elements + (i & ~1) + part; +} + +TRANS(TRN1, do_simd_permute, a, permute_load_trn, 0) +TRANS(TRN2, do_simd_permute, a, permute_load_trn, 1) + +static int permute_load_zip(int i, int part, int elements) +{ + return (i & 1) * elements + ((part * elements + i) >> 1); +} + +TRANS(ZIP1, do_simd_permute, a, permute_load_zip, 0) +TRANS(ZIP2, do_simd_permute, a, permute_load_zip, 1) + +/* + * Cryptographic AES, SHA, SHA512 + */ + +TRANS_FEAT(AESE, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aese) +TRANS_FEAT(AESD, aa64_aes, do_gvec_op3_ool, a, 0, gen_helper_crypto_aesd) +TRANS_FEAT(AESMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesmc) +TRANS_FEAT(AESIMC, aa64_aes, do_gvec_op2_ool, a, 0, gen_helper_crypto_aesimc) + +TRANS_FEAT(SHA1C, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1c) +TRANS_FEAT(SHA1P, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1p) +TRANS_FEAT(SHA1M, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1m) +TRANS_FEAT(SHA1SU0, aa64_sha1, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha1su0) + +TRANS_FEAT(SHA256H, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h) +TRANS_FEAT(SHA256H2, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256h2) +TRANS_FEAT(SHA256SU1, aa64_sha256, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha256su1) + +TRANS_FEAT(SHA1H, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1h) +TRANS_FEAT(SHA1SU1, aa64_sha1, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha1su1) +TRANS_FEAT(SHA256SU0, aa64_sha256, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha256su0) + +TRANS_FEAT(SHA512H, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512h) +TRANS_FEAT(SHA512H2, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512h2) +TRANS_FEAT(SHA512SU1, aa64_sha512, do_gvec_op3_ool, a, 0, gen_helper_crypto_sha512su1) +TRANS_FEAT(RAX1, aa64_sha3, do_gvec_fn3, a, gen_gvec_rax1) +TRANS_FEAT(SM3PARTW1, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw1) +TRANS_FEAT(SM3PARTW2, aa64_sm3, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm3partw2) +TRANS_FEAT(SM4EKEY, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4ekey) + +TRANS_FEAT(SHA512SU0, aa64_sha512, do_gvec_op2_ool, a, 0, gen_helper_crypto_sha512su0) +TRANS_FEAT(SM4E, aa64_sm4, do_gvec_op3_ool, a, 0, gen_helper_crypto_sm4e) + +TRANS_FEAT(EOR3, aa64_sha3, do_gvec_fn4, a, gen_gvec_eor3) +TRANS_FEAT(BCAX, aa64_sha3, do_gvec_fn4, a, gen_gvec_bcax) + +static bool trans_SM3SS1(DisasContext *s, arg_SM3SS1 *a) +{ + if (!dc_isar_feature(aa64_sm3, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 tcg_op1 = tcg_temp_new_i32(); + TCGv_i32 tcg_op2 = tcg_temp_new_i32(); + TCGv_i32 tcg_op3 = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + unsigned vsz, dofs; + + read_vec_element_i32(s, tcg_op1, a->rn, 3, MO_32); + read_vec_element_i32(s, tcg_op2, a->rm, 3, MO_32); + read_vec_element_i32(s, tcg_op3, a->ra, 3, MO_32); + + tcg_gen_rotri_i32(tcg_res, tcg_op1, 20); + tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2); + tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3); + tcg_gen_rotri_i32(tcg_res, tcg_res, 25); + + /* Clear the whole register first, then store bits [127:96]. */ + vsz = vec_full_reg_size(s); + dofs = vec_full_reg_offset(s, a->rd); + tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0); + write_vec_element_i32(s, tcg_res, a->rd, 3, MO_32); + } + return true; +} + +static bool do_crypto3i(DisasContext *s, arg_crypto3i *a, gen_helper_gvec_3 *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op3_ool(s, true, a->rd, a->rn, a->rm, a->imm, fn); + } + return true; +} +TRANS_FEAT(SM3TT1A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1a) +TRANS_FEAT(SM3TT1B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt1b) +TRANS_FEAT(SM3TT2A, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2a) +TRANS_FEAT(SM3TT2B, aa64_sm3, do_crypto3i, a, gen_helper_crypto_sm3tt2b) + +static bool trans_XAR(DisasContext *s, arg_XAR *a) +{ + if (!dc_isar_feature(aa64_sha3, s)) { + return false; + } + if (fp_access_check(s)) { + gen_gvec_xar(MO_64, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), a->imm, 16, + vec_full_reg_size(s)); + } + return true; +} + +/* + * Advanced SIMD copy + */ + +static bool decode_esz_idx(int imm, MemOp *pesz, unsigned *pidx) +{ + unsigned esz = ctz32(imm); + if (esz <= MO_64) { + *pesz = esz; + *pidx = imm >> (esz + 1); + return true; + } + return false; +} + +static bool trans_DUP_element_s(DisasContext *s, arg_DUP_element_s *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (fp_access_check(s)) { + /* + * This instruction just extracts the specified element and + * zero-extends it into the bottom of the destination register. + */ + TCGv_i64 tmp = tcg_temp_new_i64(); + read_vec_element(s, tmp, a->rn, idx, esz); + write_fp_dreg(s, a->rd, tmp); + } + return true; +} + +static bool trans_DUP_element_v(DisasContext *s, arg_DUP_element_v *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (esz == MO_64 && !a->q) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_dup_mem(esz, vec_full_reg_offset(s, a->rd), + vec_reg_offset(s, a->rn, idx, esz), + a->q ? 16 : 8, vec_full_reg_size(s)); + } + return true; +} + +static bool trans_DUP_general(DisasContext *s, arg_DUP_general *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (esz == MO_64 && !a->q) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), + a->q ? 16 : 8, vec_full_reg_size(s), + cpu_reg(s, a->rn)); + } + return true; +} + +static bool do_smov_umov(DisasContext *s, arg_SMOV *a, MemOp is_signed) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (is_signed) { + if (esz == MO_64 || (esz == MO_32 && !a->q)) { + return false; + } + } else { + if (esz == MO_64 ? !a->q : a->q) { + return false; + } + } + if (fp_access_check(s)) { + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + read_vec_element(s, tcg_rd, a->rn, idx, esz | is_signed); + if (is_signed && !a->q) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } + return true; +} + +TRANS(SMOV, do_smov_umov, a, MO_SIGN) +TRANS(UMOV, do_smov_umov, a, 0) + +static bool trans_INS_general(DisasContext *s, arg_INS_general *a) +{ + MemOp esz; + unsigned idx; + + if (!decode_esz_idx(a->imm, &esz, &idx)) { + return false; + } + if (fp_access_check(s)) { + write_vec_element(s, cpu_reg(s, a->rn), a->rd, idx, esz); + clear_vec_high(s, true, a->rd); + } + return true; +} + +static bool trans_INS_element(DisasContext *s, arg_INS_element *a) +{ + MemOp esz; + unsigned didx, sidx; + + if (!decode_esz_idx(a->di, &esz, &didx)) { + return false; + } + sidx = a->si >> esz; + if (fp_access_check(s)) { + TCGv_i64 tmp = tcg_temp_new_i64(); + + read_vec_element(s, tmp, a->rn, sidx, esz); + write_vec_element(s, tmp, a->rd, didx, esz); + + /* INS is considered a 128-bit write for SVE. */ + clear_vec_high(s, true, a->rd); + } + return true; +} + +/* + * Advanced SIMD three same + */ + +typedef struct FPScalar { + void (*gen_h)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_s)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); +} FPScalar; + +static bool do_fp3_scalar(DisasContext *s, arg_rrr_e *a, const FPScalar *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = read_fp_dreg(s, a->rm); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rn); + TCGv_i32 t1 = read_fp_sreg(s, a->rm); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rn); + TCGv_i32 t1 = read_fp_hreg(s, a->rm); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + return false; + } + return true; +} + +static const FPScalar f_scalar_fadd = { + gen_helper_vfp_addh, + gen_helper_vfp_adds, + gen_helper_vfp_addd, +}; +TRANS(FADD_s, do_fp3_scalar, a, &f_scalar_fadd) + +static const FPScalar f_scalar_fsub = { + gen_helper_vfp_subh, + gen_helper_vfp_subs, + gen_helper_vfp_subd, +}; +TRANS(FSUB_s, do_fp3_scalar, a, &f_scalar_fsub) + +static const FPScalar f_scalar_fdiv = { + gen_helper_vfp_divh, + gen_helper_vfp_divs, + gen_helper_vfp_divd, +}; +TRANS(FDIV_s, do_fp3_scalar, a, &f_scalar_fdiv) + +static const FPScalar f_scalar_fmul = { + gen_helper_vfp_mulh, + gen_helper_vfp_muls, + gen_helper_vfp_muld, +}; +TRANS(FMUL_s, do_fp3_scalar, a, &f_scalar_fmul) + +static const FPScalar f_scalar_fmax = { + gen_helper_advsimd_maxh, + gen_helper_vfp_maxs, + gen_helper_vfp_maxd, +}; +TRANS(FMAX_s, do_fp3_scalar, a, &f_scalar_fmax) + +static const FPScalar f_scalar_fmin = { + gen_helper_advsimd_minh, + gen_helper_vfp_mins, + gen_helper_vfp_mind, +}; +TRANS(FMIN_s, do_fp3_scalar, a, &f_scalar_fmin) + +static const FPScalar f_scalar_fmaxnm = { + gen_helper_advsimd_maxnumh, + gen_helper_vfp_maxnums, + gen_helper_vfp_maxnumd, +}; +TRANS(FMAXNM_s, do_fp3_scalar, a, &f_scalar_fmaxnm) + +static const FPScalar f_scalar_fminnm = { + gen_helper_advsimd_minnumh, + gen_helper_vfp_minnums, + gen_helper_vfp_minnumd, +}; +TRANS(FMINNM_s, do_fp3_scalar, a, &f_scalar_fminnm) + +static const FPScalar f_scalar_fmulx = { + gen_helper_advsimd_mulxh, + gen_helper_vfp_mulxs, + gen_helper_vfp_mulxd, +}; +TRANS(FMULX_s, do_fp3_scalar, a, &f_scalar_fmulx) + +static void gen_fnmul_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_mulh(d, n, m, s); + gen_vfp_negh(d, d); +} + +static void gen_fnmul_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_muls(d, n, m, s); + gen_vfp_negs(d, d); +} + +static void gen_fnmul_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_muld(d, n, m, s); + gen_vfp_negd(d, d); +} + +static const FPScalar f_scalar_fnmul = { + gen_fnmul_h, + gen_fnmul_s, + gen_fnmul_d, +}; +TRANS(FNMUL_s, do_fp3_scalar, a, &f_scalar_fnmul) + +static const FPScalar f_scalar_fcmeq = { + gen_helper_advsimd_ceq_f16, + gen_helper_neon_ceq_f32, + gen_helper_neon_ceq_f64, +}; +TRANS(FCMEQ_s, do_fp3_scalar, a, &f_scalar_fcmeq) + +static const FPScalar f_scalar_fcmge = { + gen_helper_advsimd_cge_f16, + gen_helper_neon_cge_f32, + gen_helper_neon_cge_f64, +}; +TRANS(FCMGE_s, do_fp3_scalar, a, &f_scalar_fcmge) + +static const FPScalar f_scalar_fcmgt = { + gen_helper_advsimd_cgt_f16, + gen_helper_neon_cgt_f32, + gen_helper_neon_cgt_f64, +}; +TRANS(FCMGT_s, do_fp3_scalar, a, &f_scalar_fcmgt) + +static const FPScalar f_scalar_facge = { + gen_helper_advsimd_acge_f16, + gen_helper_neon_acge_f32, + gen_helper_neon_acge_f64, +}; +TRANS(FACGE_s, do_fp3_scalar, a, &f_scalar_facge) + +static const FPScalar f_scalar_facgt = { + gen_helper_advsimd_acgt_f16, + gen_helper_neon_acgt_f32, + gen_helper_neon_acgt_f64, +}; +TRANS(FACGT_s, do_fp3_scalar, a, &f_scalar_facgt) + +static void gen_fabd_h(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subh(d, n, m, s); + gen_vfp_absh(d, d); +} + +static void gen_fabd_s(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_ptr s) +{ + gen_helper_vfp_subs(d, n, m, s); + gen_vfp_abss(d, d); +} + +static void gen_fabd_d(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_ptr s) +{ + gen_helper_vfp_subd(d, n, m, s); + gen_vfp_absd(d, d); +} + +static const FPScalar f_scalar_fabd = { + gen_fabd_h, + gen_fabd_s, + gen_fabd_d, +}; +TRANS(FABD_s, do_fp3_scalar, a, &f_scalar_fabd) + +static const FPScalar f_scalar_frecps = { + gen_helper_recpsf_f16, + gen_helper_recpsf_f32, + gen_helper_recpsf_f64, +}; +TRANS(FRECPS_s, do_fp3_scalar, a, &f_scalar_frecps) + +static const FPScalar f_scalar_frsqrts = { + gen_helper_rsqrtsf_f16, + gen_helper_rsqrtsf_f32, + gen_helper_rsqrtsf_f64, +}; +TRANS(FRSQRTS_s, do_fp3_scalar, a, &f_scalar_frsqrts) + +static bool do_satacc_s(DisasContext *s, arg_rrr_e *a, + MemOp sgn_n, MemOp sgn_m, + void (*gen_bhs)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64, MemOp), + void (*gen_d)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t0, t1, t2, qc; + MemOp esz = a->esz; + + if (!fp_access_check(s)) { + return true; + } + + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + qc = tcg_temp_new_i64(); + read_vec_element(s, t1, a->rn, 0, esz | sgn_n); + read_vec_element(s, t2, a->rm, 0, esz | sgn_m); + tcg_gen_ld_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + + if (esz == MO_64) { + gen_d(t0, qc, t1, t2); + } else { + gen_bhs(t0, qc, t1, t2, esz); + tcg_gen_ext_i64(t0, t0, esz); + } + + write_fp_dreg(s, a->rd, t0); + tcg_gen_st_i64(qc, tcg_env, offsetof(CPUARMState, vfp.qc)); + return true; +} + +TRANS(SQADD_s, do_satacc_s, a, MO_SIGN, MO_SIGN, gen_sqadd_bhs, gen_sqadd_d) +TRANS(SQSUB_s, do_satacc_s, a, MO_SIGN, MO_SIGN, gen_sqsub_bhs, gen_sqsub_d) +TRANS(UQADD_s, do_satacc_s, a, 0, 0, gen_uqadd_bhs, gen_uqadd_d) +TRANS(UQSUB_s, do_satacc_s, a, 0, 0, gen_uqsub_bhs, gen_uqsub_d) +TRANS(SUQADD_s, do_satacc_s, a, MO_SIGN, 0, gen_suqadd_bhs, gen_suqadd_d) +TRANS(USQADD_s, do_satacc_s, a, 0, MO_SIGN, gen_usqadd_bhs, gen_usqadd_d) + +static bool do_int3_scalar_d(DisasContext *s, arg_rrr_e *a, + void (*fn)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rm, 0, MO_64); + fn(t0, t0, t1); + write_fp_dreg(s, a->rd, t0); + } + return true; +} + +TRANS(SSHL_s, do_int3_scalar_d, a, gen_sshl_i64) +TRANS(USHL_s, do_int3_scalar_d, a, gen_ushl_i64) +TRANS(SRSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_s64) +TRANS(URSHL_s, do_int3_scalar_d, a, gen_helper_neon_rshl_u64) +TRANS(ADD_s, do_int3_scalar_d, a, tcg_gen_add_i64) +TRANS(SUB_s, do_int3_scalar_d, a, tcg_gen_sub_i64) + +typedef struct ENVScalar2 { + NeonGenTwoOpEnvFn *gen_bhs[3]; + NeonGenTwo64OpEnvFn *gen_d; +} ENVScalar2; + +static bool do_env_scalar2(DisasContext *s, arg_rrr_e *a, const ENVScalar2 *f) +{ + if (!fp_access_check(s)) { + return true; + } + if (a->esz == MO_64) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = read_fp_dreg(s, a->rm); + f->gen_d(t0, tcg_env, t0, t1); + write_fp_dreg(s, a->rd, t0); + } else { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, 0, a->esz); + f->gen_bhs[a->esz](t0, tcg_env, t0, t1); + write_fp_sreg(s, a->rd, t0); + } + return true; +} + +static const ENVScalar2 f_scalar_sqshl = { + { gen_helper_neon_qshl_s8, + gen_helper_neon_qshl_s16, + gen_helper_neon_qshl_s32 }, + gen_helper_neon_qshl_s64, +}; +TRANS(SQSHL_s, do_env_scalar2, a, &f_scalar_sqshl) + +static const ENVScalar2 f_scalar_uqshl = { + { gen_helper_neon_qshl_u8, + gen_helper_neon_qshl_u16, + gen_helper_neon_qshl_u32 }, + gen_helper_neon_qshl_u64, +}; +TRANS(UQSHL_s, do_env_scalar2, a, &f_scalar_uqshl) + +static const ENVScalar2 f_scalar_sqrshl = { + { gen_helper_neon_qrshl_s8, + gen_helper_neon_qrshl_s16, + gen_helper_neon_qrshl_s32 }, + gen_helper_neon_qrshl_s64, +}; +TRANS(SQRSHL_s, do_env_scalar2, a, &f_scalar_sqrshl) + +static const ENVScalar2 f_scalar_uqrshl = { + { gen_helper_neon_qrshl_u8, + gen_helper_neon_qrshl_u16, + gen_helper_neon_qrshl_u32 }, + gen_helper_neon_qrshl_u64, +}; +TRANS(UQRSHL_s, do_env_scalar2, a, &f_scalar_uqrshl) + +static bool do_env_scalar2_hs(DisasContext *s, arg_rrr_e *a, + const ENVScalar2 *f) +{ + if (a->esz == MO_16 || a->esz == MO_32) { + return do_env_scalar2(s, a, f); + } + return false; +} + +static const ENVScalar2 f_scalar_sqdmulh = { + { NULL, gen_helper_neon_qdmulh_s16, gen_helper_neon_qdmulh_s32 } +}; +TRANS(SQDMULH_s, do_env_scalar2_hs, a, &f_scalar_sqdmulh) + +static const ENVScalar2 f_scalar_sqrdmulh = { + { NULL, gen_helper_neon_qrdmulh_s16, gen_helper_neon_qrdmulh_s32 } +}; +TRANS(SQRDMULH_s, do_env_scalar2_hs, a, &f_scalar_sqrdmulh) + +typedef struct ENVScalar3 { + NeonGenThreeOpEnvFn *gen_hs[2]; +} ENVScalar3; + +static bool do_env_scalar3_hs(DisasContext *s, arg_rrr_e *a, + const ENVScalar3 *f) +{ + TCGv_i32 t0, t1, t2; + + if (a->esz != MO_16 && a->esz != MO_32) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + t2 = tcg_temp_new_i32(); + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, 0, a->esz); + read_vec_element_i32(s, t2, a->rd, 0, a->esz); + f->gen_hs[a->esz - 1](t0, tcg_env, t0, t1, t2); + write_fp_sreg(s, a->rd, t0); + return true; +} + +static const ENVScalar3 f_scalar_sqrdmlah = { + { gen_helper_neon_qrdmlah_s16, gen_helper_neon_qrdmlah_s32 } +}; +TRANS_FEAT(SQRDMLAH_s, aa64_rdm, do_env_scalar3_hs, a, &f_scalar_sqrdmlah) + +static const ENVScalar3 f_scalar_sqrdmlsh = { + { gen_helper_neon_qrdmlsh_s16, gen_helper_neon_qrdmlsh_s32 } +}; +TRANS_FEAT(SQRDMLSH_s, aa64_rdm, do_env_scalar3_hs, a, &f_scalar_sqrdmlsh) + +static bool do_cmop_d(DisasContext *s, arg_rrr_e *a, TCGCond cond) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = read_fp_dreg(s, a->rm); + tcg_gen_negsetcond_i64(cond, t0, t0, t1); + write_fp_dreg(s, a->rd, t0); + } + return true; +} + +TRANS(CMGT_s, do_cmop_d, a, TCG_COND_GT) +TRANS(CMHI_s, do_cmop_d, a, TCG_COND_GTU) +TRANS(CMGE_s, do_cmop_d, a, TCG_COND_GE) +TRANS(CMHS_s, do_cmop_d, a, TCG_COND_GEU) +TRANS(CMEQ_s, do_cmop_d, a, TCG_COND_EQ) +TRANS(CMTST_s, do_cmop_d, a, TCG_COND_TSTNE) + +static bool do_fp3_vector(DisasContext *s, arg_qrrr_e *a, int data, + gen_helper_gvec_3_ptr * const fns[3]) +{ + MemOp esz = a->esz; + + switch (esz) { + case MO_64: + if (!a->q) { + return false; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + return false; + } + if (fp_access_check(s)) { + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16, data, fns[esz - 1]); + } + return true; +} + +static gen_helper_gvec_3_ptr * const f_vector_fadd[3] = { + gen_helper_gvec_fadd_h, + gen_helper_gvec_fadd_s, + gen_helper_gvec_fadd_d, +}; +TRANS(FADD_v, do_fp3_vector, a, 0, f_vector_fadd) + +static gen_helper_gvec_3_ptr * const f_vector_fsub[3] = { + gen_helper_gvec_fsub_h, + gen_helper_gvec_fsub_s, + gen_helper_gvec_fsub_d, +}; +TRANS(FSUB_v, do_fp3_vector, a, 0, f_vector_fsub) + +static gen_helper_gvec_3_ptr * const f_vector_fdiv[3] = { + gen_helper_gvec_fdiv_h, + gen_helper_gvec_fdiv_s, + gen_helper_gvec_fdiv_d, +}; +TRANS(FDIV_v, do_fp3_vector, a, 0, f_vector_fdiv) + +static gen_helper_gvec_3_ptr * const f_vector_fmul[3] = { + gen_helper_gvec_fmul_h, + gen_helper_gvec_fmul_s, + gen_helper_gvec_fmul_d, +}; +TRANS(FMUL_v, do_fp3_vector, a, 0, f_vector_fmul) + +static gen_helper_gvec_3_ptr * const f_vector_fmax[3] = { + gen_helper_gvec_fmax_h, + gen_helper_gvec_fmax_s, + gen_helper_gvec_fmax_d, +}; +TRANS(FMAX_v, do_fp3_vector, a, 0, f_vector_fmax) + +static gen_helper_gvec_3_ptr * const f_vector_fmin[3] = { + gen_helper_gvec_fmin_h, + gen_helper_gvec_fmin_s, + gen_helper_gvec_fmin_d, +}; +TRANS(FMIN_v, do_fp3_vector, a, 0, f_vector_fmin) + +static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[3] = { + gen_helper_gvec_fmaxnum_h, + gen_helper_gvec_fmaxnum_s, + gen_helper_gvec_fmaxnum_d, +}; +TRANS(FMAXNM_v, do_fp3_vector, a, 0, f_vector_fmaxnm) + +static gen_helper_gvec_3_ptr * const f_vector_fminnm[3] = { + gen_helper_gvec_fminnum_h, + gen_helper_gvec_fminnum_s, + gen_helper_gvec_fminnum_d, +}; +TRANS(FMINNM_v, do_fp3_vector, a, 0, f_vector_fminnm) + +static gen_helper_gvec_3_ptr * const f_vector_fmulx[3] = { + gen_helper_gvec_fmulx_h, + gen_helper_gvec_fmulx_s, + gen_helper_gvec_fmulx_d, +}; +TRANS(FMULX_v, do_fp3_vector, a, 0, f_vector_fmulx) + +static gen_helper_gvec_3_ptr * const f_vector_fmla[3] = { + gen_helper_gvec_vfma_h, + gen_helper_gvec_vfma_s, + gen_helper_gvec_vfma_d, +}; +TRANS(FMLA_v, do_fp3_vector, a, 0, f_vector_fmla) + +static gen_helper_gvec_3_ptr * const f_vector_fmls[3] = { + gen_helper_gvec_vfms_h, + gen_helper_gvec_vfms_s, + gen_helper_gvec_vfms_d, +}; +TRANS(FMLS_v, do_fp3_vector, a, 0, f_vector_fmls) + +static gen_helper_gvec_3_ptr * const f_vector_fcmeq[3] = { + gen_helper_gvec_fceq_h, + gen_helper_gvec_fceq_s, + gen_helper_gvec_fceq_d, +}; +TRANS(FCMEQ_v, do_fp3_vector, a, 0, f_vector_fcmeq) + +static gen_helper_gvec_3_ptr * const f_vector_fcmge[3] = { + gen_helper_gvec_fcge_h, + gen_helper_gvec_fcge_s, + gen_helper_gvec_fcge_d, +}; +TRANS(FCMGE_v, do_fp3_vector, a, 0, f_vector_fcmge) + +static gen_helper_gvec_3_ptr * const f_vector_fcmgt[3] = { + gen_helper_gvec_fcgt_h, + gen_helper_gvec_fcgt_s, + gen_helper_gvec_fcgt_d, +}; +TRANS(FCMGT_v, do_fp3_vector, a, 0, f_vector_fcmgt) + +static gen_helper_gvec_3_ptr * const f_vector_facge[3] = { + gen_helper_gvec_facge_h, + gen_helper_gvec_facge_s, + gen_helper_gvec_facge_d, +}; +TRANS(FACGE_v, do_fp3_vector, a, 0, f_vector_facge) + +static gen_helper_gvec_3_ptr * const f_vector_facgt[3] = { + gen_helper_gvec_facgt_h, + gen_helper_gvec_facgt_s, + gen_helper_gvec_facgt_d, +}; +TRANS(FACGT_v, do_fp3_vector, a, 0, f_vector_facgt) + +static gen_helper_gvec_3_ptr * const f_vector_fabd[3] = { + gen_helper_gvec_fabd_h, + gen_helper_gvec_fabd_s, + gen_helper_gvec_fabd_d, +}; +TRANS(FABD_v, do_fp3_vector, a, 0, f_vector_fabd) + +static gen_helper_gvec_3_ptr * const f_vector_frecps[3] = { + gen_helper_gvec_recps_h, + gen_helper_gvec_recps_s, + gen_helper_gvec_recps_d, +}; +TRANS(FRECPS_v, do_fp3_vector, a, 0, f_vector_frecps) + +static gen_helper_gvec_3_ptr * const f_vector_frsqrts[3] = { + gen_helper_gvec_rsqrts_h, + gen_helper_gvec_rsqrts_s, + gen_helper_gvec_rsqrts_d, +}; +TRANS(FRSQRTS_v, do_fp3_vector, a, 0, f_vector_frsqrts) + +static gen_helper_gvec_3_ptr * const f_vector_faddp[3] = { + gen_helper_gvec_faddp_h, + gen_helper_gvec_faddp_s, + gen_helper_gvec_faddp_d, +}; +TRANS(FADDP_v, do_fp3_vector, a, 0, f_vector_faddp) + +static gen_helper_gvec_3_ptr * const f_vector_fmaxp[3] = { + gen_helper_gvec_fmaxp_h, + gen_helper_gvec_fmaxp_s, + gen_helper_gvec_fmaxp_d, +}; +TRANS(FMAXP_v, do_fp3_vector, a, 0, f_vector_fmaxp) + +static gen_helper_gvec_3_ptr * const f_vector_fminp[3] = { + gen_helper_gvec_fminp_h, + gen_helper_gvec_fminp_s, + gen_helper_gvec_fminp_d, +}; +TRANS(FMINP_v, do_fp3_vector, a, 0, f_vector_fminp) + +static gen_helper_gvec_3_ptr * const f_vector_fmaxnmp[3] = { + gen_helper_gvec_fmaxnump_h, + gen_helper_gvec_fmaxnump_s, + gen_helper_gvec_fmaxnump_d, +}; +TRANS(FMAXNMP_v, do_fp3_vector, a, 0, f_vector_fmaxnmp) + +static gen_helper_gvec_3_ptr * const f_vector_fminnmp[3] = { + gen_helper_gvec_fminnump_h, + gen_helper_gvec_fminnump_s, + gen_helper_gvec_fminnump_d, +}; +TRANS(FMINNMP_v, do_fp3_vector, a, 0, f_vector_fminnmp) + +static bool do_fmlal(DisasContext *s, arg_qrrr_e *a, bool is_s, bool is_2) +{ + if (fp_access_check(s)) { + int data = (is_2 << 1) | is_s; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), tcg_env, + a->q ? 16 : 8, vec_full_reg_size(s), + data, gen_helper_gvec_fmlal_a64); + } + return true; +} + +TRANS_FEAT(FMLAL_v, aa64_fhm, do_fmlal, a, false, false) +TRANS_FEAT(FMLSL_v, aa64_fhm, do_fmlal, a, true, false) +TRANS_FEAT(FMLAL2_v, aa64_fhm, do_fmlal, a, false, true) +TRANS_FEAT(FMLSL2_v, aa64_fhm, do_fmlal, a, true, true) + +TRANS(ADDP_v, do_gvec_fn3, a, gen_gvec_addp) +TRANS(SMAXP_v, do_gvec_fn3_no64, a, gen_gvec_smaxp) +TRANS(SMINP_v, do_gvec_fn3_no64, a, gen_gvec_sminp) +TRANS(UMAXP_v, do_gvec_fn3_no64, a, gen_gvec_umaxp) +TRANS(UMINP_v, do_gvec_fn3_no64, a, gen_gvec_uminp) + +TRANS(AND_v, do_gvec_fn3, a, tcg_gen_gvec_and) +TRANS(BIC_v, do_gvec_fn3, a, tcg_gen_gvec_andc) +TRANS(ORR_v, do_gvec_fn3, a, tcg_gen_gvec_or) +TRANS(ORN_v, do_gvec_fn3, a, tcg_gen_gvec_orc) +TRANS(EOR_v, do_gvec_fn3, a, tcg_gen_gvec_xor) + +static bool do_bitsel(DisasContext *s, bool is_q, int d, int a, int b, int c) +{ + if (fp_access_check(s)) { + gen_gvec_fn4(s, is_q, d, a, b, c, tcg_gen_gvec_bitsel, 0); + } + return true; +} + +TRANS(BSL_v, do_bitsel, a->q, a->rd, a->rd, a->rn, a->rm) +TRANS(BIT_v, do_bitsel, a->q, a->rd, a->rm, a->rn, a->rd) +TRANS(BIF_v, do_bitsel, a->q, a->rd, a->rm, a->rd, a->rn) + +TRANS(SQADD_v, do_gvec_fn3, a, gen_gvec_sqadd_qc) +TRANS(UQADD_v, do_gvec_fn3, a, gen_gvec_uqadd_qc) +TRANS(SQSUB_v, do_gvec_fn3, a, gen_gvec_sqsub_qc) +TRANS(UQSUB_v, do_gvec_fn3, a, gen_gvec_uqsub_qc) +TRANS(SUQADD_v, do_gvec_fn3, a, gen_gvec_suqadd_qc) +TRANS(USQADD_v, do_gvec_fn3, a, gen_gvec_usqadd_qc) + +TRANS(SSHL_v, do_gvec_fn3, a, gen_gvec_sshl) +TRANS(USHL_v, do_gvec_fn3, a, gen_gvec_ushl) +TRANS(SRSHL_v, do_gvec_fn3, a, gen_gvec_srshl) +TRANS(URSHL_v, do_gvec_fn3, a, gen_gvec_urshl) +TRANS(SQSHL_v, do_gvec_fn3, a, gen_neon_sqshl) +TRANS(UQSHL_v, do_gvec_fn3, a, gen_neon_uqshl) +TRANS(SQRSHL_v, do_gvec_fn3, a, gen_neon_sqrshl) +TRANS(UQRSHL_v, do_gvec_fn3, a, gen_neon_uqrshl) + +TRANS(ADD_v, do_gvec_fn3, a, tcg_gen_gvec_add) +TRANS(SUB_v, do_gvec_fn3, a, tcg_gen_gvec_sub) +TRANS(SHADD_v, do_gvec_fn3_no64, a, gen_gvec_shadd) +TRANS(UHADD_v, do_gvec_fn3_no64, a, gen_gvec_uhadd) +TRANS(SHSUB_v, do_gvec_fn3_no64, a, gen_gvec_shsub) +TRANS(UHSUB_v, do_gvec_fn3_no64, a, gen_gvec_uhsub) +TRANS(SRHADD_v, do_gvec_fn3_no64, a, gen_gvec_srhadd) +TRANS(URHADD_v, do_gvec_fn3_no64, a, gen_gvec_urhadd) +TRANS(SMAX_v, do_gvec_fn3_no64, a, tcg_gen_gvec_smax) +TRANS(UMAX_v, do_gvec_fn3_no64, a, tcg_gen_gvec_umax) +TRANS(SMIN_v, do_gvec_fn3_no64, a, tcg_gen_gvec_smin) +TRANS(UMIN_v, do_gvec_fn3_no64, a, tcg_gen_gvec_umin) +TRANS(SABA_v, do_gvec_fn3_no64, a, gen_gvec_saba) +TRANS(UABA_v, do_gvec_fn3_no64, a, gen_gvec_uaba) +TRANS(SABD_v, do_gvec_fn3_no64, a, gen_gvec_sabd) +TRANS(UABD_v, do_gvec_fn3_no64, a, gen_gvec_uabd) +TRANS(MUL_v, do_gvec_fn3_no64, a, tcg_gen_gvec_mul) +TRANS(PMUL_v, do_gvec_op3_ool, a, 0, gen_helper_gvec_pmul_b) +TRANS(MLA_v, do_gvec_fn3_no64, a, gen_gvec_mla) +TRANS(MLS_v, do_gvec_fn3_no64, a, gen_gvec_mls) + +static bool do_cmop_v(DisasContext *s, arg_qrrr_e *a, TCGCond cond) +{ + if (a->esz == MO_64 && !a->q) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_cmp(cond, a->esz, + vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + a->q ? 16 : 8, vec_full_reg_size(s)); + } + return true; +} + +TRANS(CMGT_v, do_cmop_v, a, TCG_COND_GT) +TRANS(CMHI_v, do_cmop_v, a, TCG_COND_GTU) +TRANS(CMGE_v, do_cmop_v, a, TCG_COND_GE) +TRANS(CMHS_v, do_cmop_v, a, TCG_COND_GEU) +TRANS(CMEQ_v, do_cmop_v, a, TCG_COND_EQ) +TRANS(CMTST_v, do_gvec_fn3, a, gen_gvec_cmtst) + +TRANS(SQDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqdmulh_qc) +TRANS(SQRDMULH_v, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmulh_qc) +TRANS_FEAT(SQRDMLAH_v, aa64_rdm, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmlah_qc) +TRANS_FEAT(SQRDMLSH_v, aa64_rdm, do_gvec_fn3_no8_no64, a, gen_gvec_sqrdmlsh_qc) + +static bool do_dot_vector(DisasContext *s, arg_qrrr_e *a, + gen_helper_gvec_4 *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_ool(s, a->q, a->rd, a->rn, a->rm, a->rd, 0, fn); + } + return true; +} + +static bool do_dot_vector_env(DisasContext *s, arg_qrrr_e *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_env(s, a->q, a->rd, a->rn, a->rm, a->rd, 0, fn); + } + return true; +} + +TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) +TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) +TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) +TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector_env, a, gen_helper_gvec_bfdot) +TRANS_FEAT(BFMMLA, aa64_bf16, do_dot_vector_env, a, gen_helper_gvec_bfmmla) +TRANS_FEAT(SMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_smmla_b) +TRANS_FEAT(UMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_ummla_b) +TRANS_FEAT(USMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usmmla_b) + +static bool trans_BFMLAL_v(DisasContext *s, arg_qrrr_e *a) +{ + if (!dc_isar_feature(aa64_bf16, s)) { + return false; + } + if (fp_access_check(s)) { + /* Q bit selects BFMLALB vs BFMLALT. */ + gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, false, a->q, + gen_helper_gvec_bfmlal); + } + return true; +} + +static gen_helper_gvec_3_ptr * const f_vector_fcadd[3] = { + gen_helper_gvec_fcaddh, + gen_helper_gvec_fcadds, + gen_helper_gvec_fcaddd, +}; +TRANS_FEAT(FCADD_90, aa64_fcma, do_fp3_vector, a, 0, f_vector_fcadd) +TRANS_FEAT(FCADD_270, aa64_fcma, do_fp3_vector, a, 1, f_vector_fcadd) + +static bool trans_FCMLA_v(DisasContext *s, arg_FCMLA_v *a) +{ + gen_helper_gvec_4_ptr *fn; + + if (!dc_isar_feature(aa64_fcma, s)) { + return false; + } + switch (a->esz) { + case MO_64: + if (!a->q) { + return false; + } + fn = gen_helper_gvec_fcmlad; + break; + case MO_32: + fn = gen_helper_gvec_fcmlas; + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + fn = gen_helper_gvec_fcmlah; + break; + default: + return false; + } + if (fp_access_check(s)) { + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->esz == MO_16, a->rot, fn); + } + return true; +} + +/* + * Widening vector x vector/indexed. + * + * These read from the top or bottom half of a 128-bit vector. + * After widening, optionally accumulate with a 128-bit vector. + * Implement these inline, as the number of elements are limited + * and the related SVE and SME operations on larger vectors use + * even/odd elements instead of top/bottom half. + * + * If idx >= 0, operand 2 is indexed, otherwise vector. + * If acc, operand 0 is loaded with rd. + */ + +/* For low half, iterating up. */ +static bool do_3op_widening(DisasContext *s, MemOp memop, int top, + int rd, int rn, int rm, int idx, + NeonGenTwo64OpFn *fn, bool acc) +{ + TCGv_i64 tcg_op0 = tcg_temp_new_i64(); + TCGv_i64 tcg_op1 = tcg_temp_new_i64(); + TCGv_i64 tcg_op2 = tcg_temp_new_i64(); + MemOp esz = memop & MO_SIZE; + int half = 8 >> esz; + int top_swap, top_half; + + /* There are no 64x64->128 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + if (idx >= 0) { + read_vec_element(s, tcg_op2, rm, idx, memop); + } + + /* + * For top half inputs, iterate forward; backward for bottom half. + * This means the store to the destination will not occur until + * overlapping input inputs are consumed. + * Use top_swap to conditionally invert the forward iteration index. + */ + top_swap = top ? 0 : half - 1; + top_half = top ? half : 0; + + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; + + read_vec_element(s, tcg_op1, rn, elt + top_half, memop); + if (idx < 0) { + read_vec_element(s, tcg_op2, rm, elt + top_half, memop); + } + if (acc) { + read_vec_element(s, tcg_op0, rd, elt, memop + 1); + } + fn(tcg_op0, tcg_op1, tcg_op2); + write_vec_element(s, tcg_op0, rd, elt, esz + 1); + } + clear_vec_high(s, 1, rd); + return true; +} + +static void gen_muladd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_mul_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} + +static void gen_mulsub_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_mul_i64(t, n, m); + tcg_gen_sub_i64(d, d, t); +} + +TRANS(SMULL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_mul_i64, false) +TRANS(UMULL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_mul_i64, false) +TRANS(SMLAL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_muladd_i64, true) +TRANS(UMLAL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_muladd_i64, true) +TRANS(SMLSL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_mulsub_i64, true) +TRANS(UMLSL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_mulsub_i64, true) + +TRANS(SMULL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + tcg_gen_mul_i64, false) +TRANS(UMULL_vi, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, a->idx, + tcg_gen_mul_i64, false) +TRANS(SMLAL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + gen_muladd_i64, true) +TRANS(UMLAL_vi, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, a->idx, + gen_muladd_i64, true) +TRANS(SMLSL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + gen_mulsub_i64, true) +TRANS(UMLSL_vi, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, a->idx, + gen_mulsub_i64, true) + +static void gen_sabd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t1, n, m); + tcg_gen_sub_i64(t2, m, n); + tcg_gen_movcond_i64(TCG_COND_GE, d, n, m, t1, t2); +} + +static void gen_uabd_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + + tcg_gen_sub_i64(t1, n, m); + tcg_gen_sub_i64(t2, m, n); + tcg_gen_movcond_i64(TCG_COND_GEU, d, n, m, t1, t2); +} + +static void gen_saba_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + gen_sabd_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} + +static void gen_uaba_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + gen_uabd_i64(t, n, m); + tcg_gen_add_i64(d, d, t); +} + +TRANS(SADDL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_add_i64, false) +TRANS(UADDL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_add_i64, false) +TRANS(SSUBL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_sub_i64, false) +TRANS(USUBL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + tcg_gen_sub_i64, false) +TRANS(SABDL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_sabd_i64, false) +TRANS(UABDL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_uabd_i64, false) +TRANS(SABAL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + gen_saba_i64, true) +TRANS(UABAL_v, do_3op_widening, + a->esz, a->q, a->rd, a->rn, a->rm, -1, + gen_uaba_i64, true) + +static void gen_sqdmull_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + tcg_gen_mul_i64(d, n, m); + gen_helper_neon_addl_saturate_s32(d, tcg_env, d, d); +} + +static void gen_sqdmull_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + tcg_gen_mul_i64(d, n, m); + gen_helper_neon_addl_saturate_s64(d, tcg_env, d, d); +} + +static void gen_sqdmlal_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s32(t, tcg_env, t, t); + gen_helper_neon_addl_saturate_s32(d, tcg_env, d, t); +} + +static void gen_sqdmlal_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s64(t, tcg_env, t, t); + gen_helper_neon_addl_saturate_s64(d, tcg_env, d, t); +} + +static void gen_sqdmlsl_h(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s32(t, tcg_env, t, t); + tcg_gen_neg_i64(t, t); + gen_helper_neon_addl_saturate_s32(d, tcg_env, d, t); +} + +static void gen_sqdmlsl_s(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, n, m); + gen_helper_neon_addl_saturate_s64(t, tcg_env, t, t); + tcg_gen_neg_i64(t, t); + gen_helper_neon_addl_saturate_s64(d, tcg_env, d, t); +} + +TRANS(SQDMULL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + a->esz == MO_16 ? gen_sqdmull_h : gen_sqdmull_s, false) +TRANS(SQDMLAL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + a->esz == MO_16 ? gen_sqdmlal_h : gen_sqdmlal_s, true) +TRANS(SQDMLSL_v, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, -1, + a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) + +TRANS(SQDMULL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + a->esz == MO_16 ? gen_sqdmull_h : gen_sqdmull_s, false) +TRANS(SQDMLAL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + a->esz == MO_16 ? gen_sqdmlal_h : gen_sqdmlal_s, true) +TRANS(SQDMLSL_vi, do_3op_widening, + a->esz | MO_SIGN, a->q, a->rd, a->rn, a->rm, a->idx, + a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) + +static bool do_addsub_wide(DisasContext *s, arg_qrrr_e *a, + MemOp sign, bool sub) +{ + TCGv_i64 tcg_op0, tcg_op1; + MemOp esz = a->esz; + int half = 8 >> esz; + bool top = a->q; + int top_swap = top ? 0 : half - 1; + int top_half = top ? half : 0; + + /* There are no 64x64->128 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + tcg_op0 = tcg_temp_new_i64(); + tcg_op1 = tcg_temp_new_i64(); + + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; + + read_vec_element(s, tcg_op1, a->rm, elt + top_half, esz | sign); + read_vec_element(s, tcg_op0, a->rn, elt, esz + 1); + if (sub) { + tcg_gen_sub_i64(tcg_op0, tcg_op0, tcg_op1); + } else { + tcg_gen_add_i64(tcg_op0, tcg_op0, tcg_op1); + } + write_vec_element(s, tcg_op0, a->rd, elt, esz + 1); + } + clear_vec_high(s, 1, a->rd); + return true; +} + +TRANS(SADDW, do_addsub_wide, a, MO_SIGN, false) +TRANS(UADDW, do_addsub_wide, a, 0, false) +TRANS(SSUBW, do_addsub_wide, a, MO_SIGN, true) +TRANS(USUBW, do_addsub_wide, a, 0, true) + +static bool do_addsub_highnarrow(DisasContext *s, arg_qrrr_e *a, + bool sub, bool round) +{ + TCGv_i64 tcg_op0, tcg_op1; + MemOp esz = a->esz; + int half = 8 >> esz; + bool top = a->q; + int ebits = 8 << esz; + uint64_t rbit = 1ull << (ebits - 1); + int top_swap, top_half; + + /* There are no 128x128->64 bit operations. */ + if (esz >= MO_64) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + tcg_op0 = tcg_temp_new_i64(); + tcg_op1 = tcg_temp_new_i64(); + + /* + * For top half inputs, iterate backward; forward for bottom half. + * This means the store to the destination will not occur until + * overlapping input inputs are consumed. + */ + top_swap = top ? half - 1 : 0; + top_half = top ? half : 0; + + for (int elt_fwd = 0; elt_fwd < half; ++elt_fwd) { + int elt = elt_fwd ^ top_swap; + + read_vec_element(s, tcg_op1, a->rm, elt, esz + 1); + read_vec_element(s, tcg_op0, a->rn, elt, esz + 1); + if (sub) { + tcg_gen_sub_i64(tcg_op0, tcg_op0, tcg_op1); + } else { + tcg_gen_add_i64(tcg_op0, tcg_op0, tcg_op1); + } + if (round) { + tcg_gen_addi_i64(tcg_op0, tcg_op0, rbit); + } + tcg_gen_shri_i64(tcg_op0, tcg_op0, ebits); + write_vec_element(s, tcg_op0, a->rd, elt + top_half, esz); + } + clear_vec_high(s, top, a->rd); + return true; +} + +TRANS(ADDHN, do_addsub_highnarrow, a, false, false) +TRANS(SUBHN, do_addsub_highnarrow, a, true, false) +TRANS(RADDHN, do_addsub_highnarrow, a, false, true) +TRANS(RSUBHN, do_addsub_highnarrow, a, true, true) + +static bool do_pmull(DisasContext *s, arg_qrrr_e *a, gen_helper_gvec_3 *fn) +{ + if (fp_access_check(s)) { + /* The Q field specifies lo/hi half input for these insns. */ + gen_gvec_op3_ool(s, true, a->rd, a->rn, a->rm, a->q, fn); + } + return true; +} + +TRANS(PMULL_p8, do_pmull, a, gen_helper_neon_pmull_h) +TRANS_FEAT(PMULL_p64, aa64_pmull, do_pmull, a, gen_helper_gvec_pmull_q) + +/* + * Advanced SIMD scalar/vector x indexed element + */ + +static bool do_fp3_scalar_idx(DisasContext *s, arg_rrx_e *a, const FPScalar *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rn); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t1, a->rm, a->idx, MO_64); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rn); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t1, a->rm, a->idx, MO_32); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rn); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t1, a->rm, a->idx, MO_16); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + g_assert_not_reached(); + } + return true; +} + +TRANS(FMUL_si, do_fp3_scalar_idx, a, &f_scalar_fmul) +TRANS(FMULX_si, do_fp3_scalar_idx, a, &f_scalar_fmulx) + +static bool do_fmla_scalar_idx(DisasContext *s, arg_rrx_e *a, bool neg) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = read_fp_dreg(s, a->rd); + TCGv_i64 t1 = read_fp_dreg(s, a->rn); + TCGv_i64 t2 = tcg_temp_new_i64(); + + read_vec_element(s, t2, a->rm, a->idx, MO_64); + if (neg) { + gen_vfp_negd(t1, t1); + } + gen_helper_vfp_muladdd(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_sreg(s, a->rd); + TCGv_i32 t1 = read_fp_sreg(s, a->rn); + TCGv_i32 t2 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t2, a->rm, a->idx, MO_32); + if (neg) { + gen_vfp_negs(t1, t1); + } + gen_helper_vfp_muladds(t0, t1, t2, t0, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = read_fp_hreg(s, a->rd); + TCGv_i32 t1 = read_fp_hreg(s, a->rn); + TCGv_i32 t2 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t2, a->rm, a->idx, MO_16); + if (neg) { + gen_vfp_negh(t1, t1); + } + gen_helper_advsimd_muladdh(t0, t1, t2, t0, + fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + g_assert_not_reached(); + } + return true; +} + +TRANS(FMLA_si, do_fmla_scalar_idx, a, false) +TRANS(FMLS_si, do_fmla_scalar_idx, a, true) + +static bool do_env_scalar2_idx_hs(DisasContext *s, arg_rrx_e *a, + const ENVScalar2 *f) +{ + if (a->esz < MO_16 || a->esz > MO_32) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, a->idx, a->esz); + f->gen_bhs[a->esz](t0, tcg_env, t0, t1); + write_fp_sreg(s, a->rd, t0); + } + return true; +} + +TRANS(SQDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqdmulh) +TRANS(SQRDMULH_si, do_env_scalar2_idx_hs, a, &f_scalar_sqrdmulh) + +static bool do_env_scalar3_idx_hs(DisasContext *s, arg_rrx_e *a, + const ENVScalar3 *f) +{ + if (a->esz < MO_16 || a->esz > MO_32) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, a->esz); + read_vec_element_i32(s, t1, a->rm, a->idx, a->esz); + read_vec_element_i32(s, t2, a->rd, 0, a->esz); + f->gen_hs[a->esz - 1](t0, tcg_env, t0, t1, t2); + write_fp_sreg(s, a->rd, t0); + } + return true; +} + +TRANS_FEAT(SQRDMLAH_si, aa64_rdm, do_env_scalar3_idx_hs, a, &f_scalar_sqrdmlah) +TRANS_FEAT(SQRDMLSH_si, aa64_rdm, do_env_scalar3_idx_hs, a, &f_scalar_sqrdmlsh) + +static bool do_scalar_muladd_widening_idx(DisasContext *s, arg_rrx_e *a, + NeonGenTwo64OpFn *fn, bool acc) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); + unsigned vsz, dofs; + + if (acc) { + read_vec_element(s, t0, a->rd, 0, a->esz + 1); + } + read_vec_element(s, t1, a->rn, 0, a->esz | MO_SIGN); + read_vec_element(s, t2, a->rm, a->idx, a->esz | MO_SIGN); + fn(t0, t1, t2); + + /* Clear the whole register first, then store scalar. */ + vsz = vec_full_reg_size(s); + dofs = vec_full_reg_offset(s, a->rd); + tcg_gen_gvec_dup_imm(MO_64, dofs, vsz, vsz, 0); + write_vec_element(s, t0, a->rd, 0, a->esz + 1); + } + return true; +} + +TRANS(SQDMULL_si, do_scalar_muladd_widening_idx, a, + a->esz == MO_16 ? gen_sqdmull_h : gen_sqdmull_s, false) +TRANS(SQDMLAL_si, do_scalar_muladd_widening_idx, a, + a->esz == MO_16 ? gen_sqdmlal_h : gen_sqdmlal_s, true) +TRANS(SQDMLSL_si, do_scalar_muladd_widening_idx, a, + a->esz == MO_16 ? gen_sqdmlsl_h : gen_sqdmlsl_s, true) + +static bool do_fp3_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_3_ptr * const fns[3]) +{ + MemOp esz = a->esz; + + switch (esz) { + case MO_64: + if (!a->q) { + return false; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + g_assert_not_reached(); + } + if (fp_access_check(s)) { + gen_gvec_op3_fpst(s, a->q, a->rd, a->rn, a->rm, + esz == MO_16, a->idx, fns[esz - 1]); + } + return true; +} + +static gen_helper_gvec_3_ptr * const f_vector_idx_fmul[3] = { + gen_helper_gvec_fmul_idx_h, + gen_helper_gvec_fmul_idx_s, + gen_helper_gvec_fmul_idx_d, +}; +TRANS(FMUL_vi, do_fp3_vector_idx, a, f_vector_idx_fmul) + +static gen_helper_gvec_3_ptr * const f_vector_idx_fmulx[3] = { + gen_helper_gvec_fmulx_idx_h, + gen_helper_gvec_fmulx_idx_s, + gen_helper_gvec_fmulx_idx_d, +}; +TRANS(FMULX_vi, do_fp3_vector_idx, a, f_vector_idx_fmulx) + +static bool do_fmla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool neg) +{ + static gen_helper_gvec_4_ptr * const fns[3] = { + gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, + gen_helper_gvec_fmla_idx_d, + }; + MemOp esz = a->esz; + + switch (esz) { + case MO_64: + if (!a->q) { + return false; + } + break; + case MO_32: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + g_assert_not_reached(); + } + if (fp_access_check(s)) { + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + esz == MO_16, (a->idx << 1) | neg, + fns[esz - 1]); + } + return true; +} + +TRANS(FMLA_vi, do_fmla_vector_idx, a, false) +TRANS(FMLS_vi, do_fmla_vector_idx, a, true) + +static bool do_fmlal_idx(DisasContext *s, arg_qrrx_e *a, bool is_s, bool is_2) +{ + if (fp_access_check(s)) { + int data = (a->idx << 2) | (is_2 << 1) | is_s; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), tcg_env, + a->q ? 16 : 8, vec_full_reg_size(s), + data, gen_helper_gvec_fmlal_idx_a64); + } + return true; +} + +TRANS_FEAT(FMLAL_vi, aa64_fhm, do_fmlal_idx, a, false, false) +TRANS_FEAT(FMLSL_vi, aa64_fhm, do_fmlal_idx, a, true, false) +TRANS_FEAT(FMLAL2_vi, aa64_fhm, do_fmlal_idx, a, false, true) +TRANS_FEAT(FMLSL2_vi, aa64_fhm, do_fmlal_idx, a, true, true) + +static bool do_int3_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_3 * const fns[2]) +{ + assert(a->esz == MO_16 || a->esz == MO_32); + if (fp_access_check(s)) { + gen_gvec_op3_ool(s, a->q, a->rd, a->rn, a->rm, a->idx, fns[a->esz - 1]); + } + return true; +} + +static gen_helper_gvec_3 * const f_vector_idx_mul[2] = { + gen_helper_gvec_mul_idx_h, + gen_helper_gvec_mul_idx_s, +}; +TRANS(MUL_vi, do_int3_vector_idx, a, f_vector_idx_mul) + +static bool do_mla_vector_idx(DisasContext *s, arg_qrrx_e *a, bool sub) +{ + static gen_helper_gvec_4 * const fns[2][2] = { + { gen_helper_gvec_mla_idx_h, gen_helper_gvec_mls_idx_h }, + { gen_helper_gvec_mla_idx_s, gen_helper_gvec_mls_idx_s }, + }; + + assert(a->esz == MO_16 || a->esz == MO_32); + if (fp_access_check(s)) { + gen_gvec_op4_ool(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->idx, fns[a->esz - 1][sub]); + } + return true; +} + +TRANS(MLA_vi, do_mla_vector_idx, a, false) +TRANS(MLS_vi, do_mla_vector_idx, a, true) + +static bool do_int3_qc_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4 * const fns[2]) +{ + assert(a->esz == MO_16 || a->esz == MO_32); + if (fp_access_check(s)) { + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + offsetof(CPUARMState, vfp.qc), + a->q ? 16 : 8, vec_full_reg_size(s), + a->idx, fns[a->esz - 1]); + } + return true; +} + +static gen_helper_gvec_4 * const f_vector_idx_sqdmulh[2] = { + gen_helper_neon_sqdmulh_idx_h, + gen_helper_neon_sqdmulh_idx_s, +}; +TRANS(SQDMULH_vi, do_int3_qc_vector_idx, a, f_vector_idx_sqdmulh) + +static gen_helper_gvec_4 * const f_vector_idx_sqrdmulh[2] = { + gen_helper_neon_sqrdmulh_idx_h, + gen_helper_neon_sqrdmulh_idx_s, +}; +TRANS(SQRDMULH_vi, do_int3_qc_vector_idx, a, f_vector_idx_sqrdmulh) + +static gen_helper_gvec_4 * const f_vector_idx_sqrdmlah[2] = { + gen_helper_neon_sqrdmlah_idx_h, + gen_helper_neon_sqrdmlah_idx_s, +}; +TRANS_FEAT(SQRDMLAH_vi, aa64_rdm, do_int3_qc_vector_idx, a, + f_vector_idx_sqrdmlah) + +static gen_helper_gvec_4 * const f_vector_idx_sqrdmlsh[2] = { + gen_helper_neon_sqrdmlsh_idx_h, + gen_helper_neon_sqrdmlsh_idx_s, +}; +TRANS_FEAT(SQRDMLSH_vi, aa64_rdm, do_int3_qc_vector_idx, a, + f_vector_idx_sqrdmlsh) + +static bool do_dot_vector_idx(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4 *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_ool(s, a->q, a->rd, a->rn, a->rm, a->rd, a->idx, fn); + } + return true; +} + +static bool do_dot_vector_idx_env(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_env(s, a->q, a->rd, a->rn, a->rm, a->rd, a->idx, fn); + } + return true; +} + +TRANS_FEAT(SDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_sdot_idx_b) +TRANS_FEAT(UDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_udot_idx_b) +TRANS_FEAT(SUDOT_vi, aa64_i8mm, do_dot_vector_idx, a, + gen_helper_gvec_sudot_idx_b) +TRANS_FEAT(USDOT_vi, aa64_i8mm, do_dot_vector_idx, a, + gen_helper_gvec_usdot_idx_b) +TRANS_FEAT(BFDOT_vi, aa64_bf16, do_dot_vector_idx_env, a, + gen_helper_gvec_bfdot_idx) + +static bool trans_BFMLAL_vi(DisasContext *s, arg_qrrx_e *a) +{ + if (!dc_isar_feature(aa64_bf16, s)) { + return false; + } + if (fp_access_check(s)) { + /* Q bit selects BFMLALB vs BFMLALT. */ + gen_gvec_op4_fpst(s, true, a->rd, a->rn, a->rm, a->rd, 0, + (a->idx << 1) | a->q, + gen_helper_gvec_bfmlal_idx); + } + return true; +} + +static bool trans_FCMLA_vi(DisasContext *s, arg_FCMLA_vi *a) +{ + gen_helper_gvec_4_ptr *fn; + + if (!dc_isar_feature(aa64_fcma, s)) { + return false; + } + switch (a->esz) { + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + fn = gen_helper_gvec_fcmlah_idx; + break; + case MO_32: + fn = gen_helper_gvec_fcmlas_idx; + break; + default: + g_assert_not_reached(); + } + if (fp_access_check(s)) { + gen_gvec_op4_fpst(s, a->q, a->rd, a->rn, a->rm, a->rd, + a->esz == MO_16, (a->idx << 2) | a->rot, fn); + } + return true; +} + +/* + * Advanced SIMD scalar pairwise + */ + +static bool do_fp3_scalar_pair(DisasContext *s, arg_rr_e *a, const FPScalar *f) +{ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rn, 1, MO_64); + f->gen_d(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_dreg(s, a->rd, t0); + } + break; + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, MO_32); + read_vec_element_i32(s, t1, a->rn, 1, MO_32); + f->gen_s(t0, t0, t1, fpstatus_ptr(FPST_FPCR)); + write_fp_sreg(s, a->rd, t0); + } + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + read_vec_element_i32(s, t0, a->rn, 0, MO_16); + read_vec_element_i32(s, t1, a->rn, 1, MO_16); + f->gen_h(t0, t0, t1, fpstatus_ptr(FPST_FPCR_F16)); + write_fp_sreg(s, a->rd, t0); + } + break; + default: + g_assert_not_reached(); + } + return true; +} + +TRANS(FADDP_s, do_fp3_scalar_pair, a, &f_scalar_fadd) +TRANS(FMAXP_s, do_fp3_scalar_pair, a, &f_scalar_fmax) +TRANS(FMINP_s, do_fp3_scalar_pair, a, &f_scalar_fmin) +TRANS(FMAXNMP_s, do_fp3_scalar_pair, a, &f_scalar_fmaxnm) +TRANS(FMINNMP_s, do_fp3_scalar_pair, a, &f_scalar_fminnm) + +static bool trans_ADDP_s(DisasContext *s, arg_rr_e *a) +{ + if (fp_access_check(s)) { + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element(s, t0, a->rn, 0, MO_64); + read_vec_element(s, t1, a->rn, 1, MO_64); + tcg_gen_add_i64(t0, t0, t1); + write_fp_dreg(s, a->rd, t0); + } + return true; +} + +/* + * Floating-point conditional select + */ + +static bool trans_FCSEL(DisasContext *s, arg_FCSEL *a) +{ + TCGv_i64 t_true, t_false; + DisasCompare64 c; + + switch (a->esz) { + case MO_32: + case MO_64: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + return false; + } + + if (!fp_access_check(s)) { + return true; + } + + /* Zero extend sreg & hreg inputs to 64 bits now. */ + t_true = tcg_temp_new_i64(); + t_false = tcg_temp_new_i64(); + read_vec_element(s, t_true, a->rn, 0, a->esz); + read_vec_element(s, t_false, a->rm, 0, a->esz); + + a64_test_cc(&c, a->cond); + tcg_gen_movcond_i64(c.cond, t_true, c.value, tcg_constant_i64(0), + t_true, t_false); + + /* + * Note that sregs & hregs write back zeros to the high bits, + * and we've already done the zero-extension. + */ + write_fp_dreg(s, a->rd, t_true); + return true; +} + +/* + * Advanced SIMD Extract + */ + +static bool trans_EXT_d(DisasContext *s, arg_EXT_d *a) +{ + if (fp_access_check(s)) { + TCGv_i64 lo = read_fp_dreg(s, a->rn); + if (a->imm != 0) { + TCGv_i64 hi = read_fp_dreg(s, a->rm); + tcg_gen_extract2_i64(lo, lo, hi, a->imm * 8); + } + write_fp_dreg(s, a->rd, lo); + } + return true; +} + +static bool trans_EXT_q(DisasContext *s, arg_EXT_q *a) +{ + TCGv_i64 lo, hi; + int pos = (a->imm & 7) * 8; + int elt = a->imm >> 3; + + if (!fp_access_check(s)) { + return true; + } + + lo = tcg_temp_new_i64(); + hi = tcg_temp_new_i64(); + + read_vec_element(s, lo, a->rn, elt, MO_64); + elt++; + read_vec_element(s, hi, elt & 2 ? a->rm : a->rn, elt & 1, MO_64); + elt++; + + if (pos != 0) { + TCGv_i64 hh = tcg_temp_new_i64(); + tcg_gen_extract2_i64(lo, lo, hi, pos); + read_vec_element(s, hh, a->rm, elt & 1, MO_64); + tcg_gen_extract2_i64(hi, hi, hh, pos); + } + + write_vec_element(s, lo, a->rd, 0, MO_64); + write_vec_element(s, hi, a->rd, 1, MO_64); + clear_vec_high(s, true, a->rd); + return true; +} + +/* + * Floating-point data-processing (3 source) + */ + +static bool do_fmadd(DisasContext *s, arg_rrrr_e *a, bool neg_a, bool neg_n) +{ + TCGv_ptr fpst; + + /* + * These are fused multiply-add. Note that doing the negations here + * as separate steps is correct: an input NaN should come out with + * its sign bit flipped if it is a negated-input. + */ + switch (a->esz) { + case MO_64: + if (fp_access_check(s)) { + TCGv_i64 tn = read_fp_dreg(s, a->rn); + TCGv_i64 tm = read_fp_dreg(s, a->rm); + TCGv_i64 ta = read_fp_dreg(s, a->ra); + + if (neg_a) { + gen_vfp_negd(ta, ta); + } + if (neg_n) { + gen_vfp_negd(tn, tn); + } + fpst = fpstatus_ptr(FPST_FPCR); + gen_helper_vfp_muladdd(ta, tn, tm, ta, fpst); + write_fp_dreg(s, a->rd, ta); + } + break; + + case MO_32: + if (fp_access_check(s)) { + TCGv_i32 tn = read_fp_sreg(s, a->rn); + TCGv_i32 tm = read_fp_sreg(s, a->rm); + TCGv_i32 ta = read_fp_sreg(s, a->ra); + + if (neg_a) { + gen_vfp_negs(ta, ta); + } + if (neg_n) { + gen_vfp_negs(tn, tn); + } + fpst = fpstatus_ptr(FPST_FPCR); + gen_helper_vfp_muladds(ta, tn, tm, ta, fpst); + write_fp_sreg(s, a->rd, ta); + } + break; + + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + TCGv_i32 tn = read_fp_hreg(s, a->rn); + TCGv_i32 tm = read_fp_hreg(s, a->rm); + TCGv_i32 ta = read_fp_hreg(s, a->ra); + + if (neg_a) { + gen_vfp_negh(ta, ta); + } + if (neg_n) { + gen_vfp_negh(tn, tn); + } + fpst = fpstatus_ptr(FPST_FPCR_F16); + gen_helper_advsimd_muladdh(ta, tn, tm, ta, fpst); + write_fp_sreg(s, a->rd, ta); + } + break; + + default: + return false; + } + return true; +} + +TRANS(FMADD, do_fmadd, a, false, false) +TRANS(FNMADD, do_fmadd, a, true, true) +TRANS(FMSUB, do_fmadd, a, false, true) +TRANS(FNMSUB, do_fmadd, a, true, false) + +/* + * Advanced SIMD Across Lanes + */ + +static bool do_int_reduction(DisasContext *s, arg_qrr_e *a, bool widen, + MemOp src_sign, NeonGenTwo64OpFn *fn) +{ + TCGv_i64 tcg_res, tcg_elt; + MemOp src_mop = a->esz | src_sign; + int elements = (a->q ? 16 : 8) >> a->esz; + + /* Reject MO_64, and MO_32 without Q: a minimum of 4 elements. */ + if (elements < 4) { + return false; + } + if (!fp_access_check(s)) { + return true; + } + + tcg_res = tcg_temp_new_i64(); + tcg_elt = tcg_temp_new_i64(); + + read_vec_element(s, tcg_res, a->rn, 0, src_mop); + for (int i = 1; i < elements; i++) { + read_vec_element(s, tcg_elt, a->rn, i, src_mop); + fn(tcg_res, tcg_res, tcg_elt); + } + + tcg_gen_ext_i64(tcg_res, tcg_res, a->esz + widen); + write_fp_dreg(s, a->rd, tcg_res); + return true; +} + +TRANS(ADDV, do_int_reduction, a, false, 0, tcg_gen_add_i64) +TRANS(SADDLV, do_int_reduction, a, true, MO_SIGN, tcg_gen_add_i64) +TRANS(UADDLV, do_int_reduction, a, true, 0, tcg_gen_add_i64) +TRANS(SMAXV, do_int_reduction, a, false, MO_SIGN, tcg_gen_smax_i64) +TRANS(UMAXV, do_int_reduction, a, false, 0, tcg_gen_umax_i64) +TRANS(SMINV, do_int_reduction, a, false, MO_SIGN, tcg_gen_smin_i64) +TRANS(UMINV, do_int_reduction, a, false, 0, tcg_gen_umin_i64) + +/* + * do_fp_reduction helper + * + * This mirrors the Reduce() pseudocode in the ARM ARM. It is + * important for correct NaN propagation that we do these + * operations in exactly the order specified by the pseudocode. + * + * This is a recursive function. + */ +static TCGv_i32 do_reduction_op(DisasContext *s, int rn, MemOp esz, + int ebase, int ecount, TCGv_ptr fpst, + NeonGenTwoSingleOpFn *fn) +{ + if (ecount == 1) { + TCGv_i32 tcg_elem = tcg_temp_new_i32(); + read_vec_element_i32(s, tcg_elem, rn, ebase, esz); + return tcg_elem; + } else { + int half = ecount >> 1; + TCGv_i32 tcg_hi, tcg_lo, tcg_res; + + tcg_hi = do_reduction_op(s, rn, esz, ebase + half, half, fpst, fn); + tcg_lo = do_reduction_op(s, rn, esz, ebase, half, fpst, fn); + tcg_res = tcg_temp_new_i32(); + + fn(tcg_res, tcg_lo, tcg_hi, fpst); + return tcg_res; + } +} + +static bool do_fp_reduction(DisasContext *s, arg_qrr_e *a, + NeonGenTwoSingleOpFn *fn) +{ + if (fp_access_check(s)) { + MemOp esz = a->esz; + int elts = (a->q ? 16 : 8) >> esz; + TCGv_ptr fpst = fpstatus_ptr(esz == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_i32 res = do_reduction_op(s, a->rn, esz, 0, elts, fpst, fn); + write_fp_sreg(s, a->rd, res); + } + return true; +} + +TRANS_FEAT(FMAXNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_maxnumh) +TRANS_FEAT(FMINNMV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_minnumh) +TRANS_FEAT(FMAXV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_maxh) +TRANS_FEAT(FMINV_h, aa64_fp16, do_fp_reduction, a, gen_helper_advsimd_minh) + +TRANS(FMAXNMV_s, do_fp_reduction, a, gen_helper_vfp_maxnums) +TRANS(FMINNMV_s, do_fp_reduction, a, gen_helper_vfp_minnums) +TRANS(FMAXV_s, do_fp_reduction, a, gen_helper_vfp_maxs) +TRANS(FMINV_s, do_fp_reduction, a, gen_helper_vfp_mins) + +/* + * Floating-point Immediate + */ + +static bool trans_FMOVI_s(DisasContext *s, arg_FMOVI_s *a) +{ + switch (a->esz) { + case MO_32: + case MO_64: + break; + case MO_16: + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + break; + default: + return false; + } + if (fp_access_check(s)) { + uint64_t imm = vfp_expand_imm(a->esz, a->imm); + write_fp_dreg(s, a->rd, tcg_constant_i64(imm)); + } + return true; +} + +/* + * Advanced SIMD Modified Immediate + */ + +static bool trans_FMOVI_v_h(DisasContext *s, arg_FMOVI_v_h *a) +{ + if (!dc_isar_feature(aa64_fp16, s)) { + return false; + } + if (fp_access_check(s)) { + tcg_gen_gvec_dup_imm(MO_16, vec_full_reg_offset(s, a->rd), + a->q ? 16 : 8, vec_full_reg_size(s), + vfp_expand_imm(MO_16, a->abcdefgh)); + } + return true; +} + +static void gen_movi(unsigned vece, uint32_t dofs, uint32_t aofs, + int64_t c, uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_dup_imm(MO_64, dofs, oprsz, maxsz, c); +} + +static bool trans_Vimm(DisasContext *s, arg_Vimm *a) +{ + GVecGen2iFn *fn; + + /* Handle decode of cmode/op here between ORR/BIC/MOVI */ + if ((a->cmode & 1) && a->cmode < 12) { + /* For op=1, the imm will be inverted, so BIC becomes AND. */ + fn = a->op ? tcg_gen_gvec_andi : tcg_gen_gvec_ori; + } else { + /* There is one unallocated cmode/op combination in this space */ + if (a->cmode == 15 && a->op == 1 && a->q == 0) { + return false; + } + fn = gen_movi; + } + + if (fp_access_check(s)) { + uint64_t imm = asimd_imm_const(a->abcdefgh, a->cmode, a->op); + gen_gvec_fn2i(s, a->q, a->rd, a->rd, imm, fn, MO_64); + } + return true; +} + +/* + * Advanced SIMD Shift by Immediate + */ + +static bool do_vec_shift_imm(DisasContext *s, arg_qrri_e *a, GVecGen2iFn *fn) +{ + if (fp_access_check(s)) { + gen_gvec_fn2i(s, a->q, a->rd, a->rn, a->imm, fn, a->esz); + } + return true; +} + +TRANS(SSHR_v, do_vec_shift_imm, a, gen_gvec_sshr) +TRANS(USHR_v, do_vec_shift_imm, a, gen_gvec_ushr) +TRANS(SSRA_v, do_vec_shift_imm, a, gen_gvec_ssra) +TRANS(USRA_v, do_vec_shift_imm, a, gen_gvec_usra) +TRANS(SRSHR_v, do_vec_shift_imm, a, gen_gvec_srshr) +TRANS(URSHR_v, do_vec_shift_imm, a, gen_gvec_urshr) +TRANS(SRSRA_v, do_vec_shift_imm, a, gen_gvec_srsra) +TRANS(URSRA_v, do_vec_shift_imm, a, gen_gvec_ursra) +TRANS(SRI_v, do_vec_shift_imm, a, gen_gvec_sri) +TRANS(SHL_v, do_vec_shift_imm, a, tcg_gen_gvec_shli) +TRANS(SLI_v, do_vec_shift_imm, a, gen_gvec_sli); +TRANS(SQSHL_vi, do_vec_shift_imm, a, gen_neon_sqshli) +TRANS(UQSHL_vi, do_vec_shift_imm, a, gen_neon_uqshli) +TRANS(SQSHLU_vi, do_vec_shift_imm, a, gen_neon_sqshlui) + +static bool do_vec_shift_imm_wide(DisasContext *s, arg_qrri_e *a, bool is_u) +{ + TCGv_i64 tcg_rn, tcg_rd; + int esz = a->esz; + int esize; + + if (!fp_access_check(s)) { + return true; + } + + /* + * For the LL variants the store is larger than the load, + * so if rd == rn we would overwrite parts of our input. + * So load everything right now and use shifts in the main loop. + */ + tcg_rd = tcg_temp_new_i64(); + tcg_rn = tcg_temp_new_i64(); + read_vec_element(s, tcg_rn, a->rn, a->q, MO_64); + + esize = 8 << esz; + for (int i = 0, elements = 8 >> esz; i < elements; i++) { + if (is_u) { + tcg_gen_extract_i64(tcg_rd, tcg_rn, i * esize, esize); + } else { + tcg_gen_sextract_i64(tcg_rd, tcg_rn, i * esize, esize); + } + tcg_gen_shli_i64(tcg_rd, tcg_rd, a->imm); + write_vec_element(s, tcg_rd, a->rd, i, esz + 1); + } + clear_vec_high(s, true, a->rd); + return true; +} + +TRANS(SSHLL_v, do_vec_shift_imm_wide, a, false) +TRANS(USHLL_v, do_vec_shift_imm_wide, a, true) + +static void gen_sshr_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + assert(shift >= 0 && shift <= 64); + tcg_gen_sari_i64(dst, src, MIN(shift, 63)); +} + +static void gen_ushr_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + assert(shift >= 0 && shift <= 64); + if (shift == 64) { + tcg_gen_movi_i64(dst, 0); + } else { + tcg_gen_shri_i64(dst, src, shift); + } +} + +static void gen_ssra_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + gen_sshr_d(src, src, shift); + tcg_gen_add_i64(dst, dst, src); +} + +static void gen_usra_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + gen_ushr_d(src, src, shift); + tcg_gen_add_i64(dst, dst, src); +} + +static void gen_srshr_bhs(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + assert(shift >= 0 && shift <= 32); + if (shift) { + TCGv_i64 rnd = tcg_constant_i64(1ull << (shift - 1)); + tcg_gen_add_i64(dst, src, rnd); + tcg_gen_sari_i64(dst, dst, shift); + } else { + tcg_gen_mov_i64(dst, src); + } +} + +static void gen_urshr_bhs(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + assert(shift >= 0 && shift <= 32); + if (shift) { + TCGv_i64 rnd = tcg_constant_i64(1ull << (shift - 1)); + tcg_gen_add_i64(dst, src, rnd); + tcg_gen_shri_i64(dst, dst, shift); + } else { + tcg_gen_mov_i64(dst, src); + } +} + +static void gen_srshr_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + assert(shift >= 0 && shift <= 64); + if (shift == 0) { + tcg_gen_mov_i64(dst, src); + } else if (shift == 64) { + /* Extension of sign bit (0,-1) plus sign bit (0,1) is zero. */ + tcg_gen_movi_i64(dst, 0); + } else { + TCGv_i64 rnd = tcg_temp_new_i64(); + tcg_gen_extract_i64(rnd, src, shift - 1, 1); + tcg_gen_sari_i64(dst, src, shift); + tcg_gen_add_i64(dst, dst, rnd); + } +} + +static void gen_urshr_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + assert(shift >= 0 && shift <= 64); + if (shift == 0) { + tcg_gen_mov_i64(dst, src); + } else if (shift == 64) { + /* Rounding will propagate bit 63 into bit 64. */ + tcg_gen_shri_i64(dst, src, 63); + } else { + TCGv_i64 rnd = tcg_temp_new_i64(); + tcg_gen_extract_i64(rnd, src, shift - 1, 1); + tcg_gen_shri_i64(dst, src, shift); + tcg_gen_add_i64(dst, dst, rnd); + } +} + +static void gen_srsra_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + gen_srshr_d(src, src, shift); + tcg_gen_add_i64(dst, dst, src); +} + +static void gen_ursra_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + gen_urshr_d(src, src, shift); + tcg_gen_add_i64(dst, dst, src); +} + +static void gen_sri_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + /* If shift is 64, dst is unchanged. */ + if (shift != 64) { + tcg_gen_shri_i64(src, src, shift); + tcg_gen_deposit_i64(dst, dst, src, 0, 64 - shift); + } +} + +static void gen_sli_d(TCGv_i64 dst, TCGv_i64 src, int64_t shift) +{ + tcg_gen_deposit_i64(dst, dst, src, shift, 64 - shift); +} + +static bool do_vec_shift_imm_narrow(DisasContext *s, arg_qrri_e *a, + WideShiftImmFn * const fns[3], MemOp sign) +{ + TCGv_i64 tcg_rn, tcg_rd; + int esz = a->esz; + int esize; + WideShiftImmFn *fn; + + tcg_debug_assert(esz >= MO_8 && esz <= MO_32); + + if (!fp_access_check(s)) { + return true; + } + + tcg_rn = tcg_temp_new_i64(); + tcg_rd = tcg_temp_new_i64(); + tcg_gen_movi_i64(tcg_rd, 0); + + fn = fns[esz]; + esize = 8 << esz; + for (int i = 0, elements = 8 >> esz; i < elements; i++) { + read_vec_element(s, tcg_rn, a->rn, i, (esz + 1) | sign); + fn(tcg_rn, tcg_rn, a->imm); + tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_rn, esize * i, esize); + } + + write_vec_element(s, tcg_rd, a->rd, a->q, MO_64); + clear_vec_high(s, a->q, a->rd); + return true; +} + +static void gen_sqshrn_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + tcg_gen_sari_i64(d, s, i); + tcg_gen_ext16u_i64(d, d); + gen_helper_neon_narrow_sat_s8(d, tcg_env, d); +} + +static void gen_sqshrn_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + tcg_gen_sari_i64(d, s, i); + tcg_gen_ext32u_i64(d, d); + gen_helper_neon_narrow_sat_s16(d, tcg_env, d); +} + +static void gen_sqshrn_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_sshr_d(d, s, i); + gen_helper_neon_narrow_sat_s32(d, tcg_env, d); +} + +static void gen_uqshrn_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + tcg_gen_shri_i64(d, s, i); + gen_helper_neon_narrow_sat_u8(d, tcg_env, d); +} + +static void gen_uqshrn_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + tcg_gen_shri_i64(d, s, i); + gen_helper_neon_narrow_sat_u16(d, tcg_env, d); +} + +static void gen_uqshrn_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_ushr_d(d, s, i); + gen_helper_neon_narrow_sat_u32(d, tcg_env, d); +} + +static void gen_sqshrun_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + tcg_gen_sari_i64(d, s, i); + tcg_gen_ext16u_i64(d, d); + gen_helper_neon_unarrow_sat8(d, tcg_env, d); +} + +static void gen_sqshrun_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + tcg_gen_sari_i64(d, s, i); + tcg_gen_ext32u_i64(d, d); + gen_helper_neon_unarrow_sat16(d, tcg_env, d); +} + +static void gen_sqshrun_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_sshr_d(d, s, i); + gen_helper_neon_unarrow_sat32(d, tcg_env, d); +} + +static void gen_sqrshrn_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_srshr_bhs(d, s, i); + tcg_gen_ext16u_i64(d, d); + gen_helper_neon_narrow_sat_s8(d, tcg_env, d); +} + +static void gen_sqrshrn_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_srshr_bhs(d, s, i); + tcg_gen_ext32u_i64(d, d); + gen_helper_neon_narrow_sat_s16(d, tcg_env, d); +} + +static void gen_sqrshrn_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_srshr_d(d, s, i); + gen_helper_neon_narrow_sat_s32(d, tcg_env, d); +} + +static void gen_uqrshrn_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_urshr_bhs(d, s, i); + gen_helper_neon_narrow_sat_u8(d, tcg_env, d); +} + +static void gen_uqrshrn_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_urshr_bhs(d, s, i); + gen_helper_neon_narrow_sat_u16(d, tcg_env, d); +} + +static void gen_uqrshrn_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_urshr_d(d, s, i); + gen_helper_neon_narrow_sat_u32(d, tcg_env, d); +} + +static void gen_sqrshrun_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_srshr_bhs(d, s, i); + tcg_gen_ext16u_i64(d, d); + gen_helper_neon_unarrow_sat8(d, tcg_env, d); +} + +static void gen_sqrshrun_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_srshr_bhs(d, s, i); + tcg_gen_ext32u_i64(d, d); + gen_helper_neon_unarrow_sat16(d, tcg_env, d); +} + +static void gen_sqrshrun_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_srshr_d(d, s, i); + gen_helper_neon_unarrow_sat32(d, tcg_env, d); +} + +static WideShiftImmFn * const shrn_fns[] = { + tcg_gen_shri_i64, + tcg_gen_shri_i64, + gen_ushr_d, +}; +TRANS(SHRN_v, do_vec_shift_imm_narrow, a, shrn_fns, 0) + +static WideShiftImmFn * const rshrn_fns[] = { + gen_urshr_bhs, + gen_urshr_bhs, + gen_urshr_d, +}; +TRANS(RSHRN_v, do_vec_shift_imm_narrow, a, rshrn_fns, 0) + +static WideShiftImmFn * const sqshrn_fns[] = { + gen_sqshrn_b, + gen_sqshrn_h, + gen_sqshrn_s, +}; +TRANS(SQSHRN_v, do_vec_shift_imm_narrow, a, sqshrn_fns, MO_SIGN) + +static WideShiftImmFn * const uqshrn_fns[] = { + gen_uqshrn_b, + gen_uqshrn_h, + gen_uqshrn_s, +}; +TRANS(UQSHRN_v, do_vec_shift_imm_narrow, a, uqshrn_fns, 0) + +static WideShiftImmFn * const sqshrun_fns[] = { + gen_sqshrun_b, + gen_sqshrun_h, + gen_sqshrun_s, +}; +TRANS(SQSHRUN_v, do_vec_shift_imm_narrow, a, sqshrun_fns, MO_SIGN) + +static WideShiftImmFn * const sqrshrn_fns[] = { + gen_sqrshrn_b, + gen_sqrshrn_h, + gen_sqrshrn_s, +}; +TRANS(SQRSHRN_v, do_vec_shift_imm_narrow, a, sqrshrn_fns, MO_SIGN) + +static WideShiftImmFn * const uqrshrn_fns[] = { + gen_uqrshrn_b, + gen_uqrshrn_h, + gen_uqrshrn_s, +}; +TRANS(UQRSHRN_v, do_vec_shift_imm_narrow, a, uqrshrn_fns, 0) + +static WideShiftImmFn * const sqrshrun_fns[] = { + gen_sqrshrun_b, + gen_sqrshrun_h, + gen_sqrshrun_s, +}; +TRANS(SQRSHRUN_v, do_vec_shift_imm_narrow, a, sqrshrun_fns, MO_SIGN) + +/* + * Advanced SIMD Scalar Shift by Immediate + */ + +static bool do_scalar_shift_imm(DisasContext *s, arg_rri_e *a, + WideShiftImmFn *fn, bool accumulate, + MemOp sign) +{ + if (fp_access_check(s)) { + TCGv_i64 rd = tcg_temp_new_i64(); + TCGv_i64 rn = tcg_temp_new_i64(); + + read_vec_element(s, rn, a->rn, 0, a->esz | sign); + if (accumulate) { + read_vec_element(s, rd, a->rd, 0, a->esz | sign); + } + fn(rd, rn, a->imm); + write_fp_dreg(s, a->rd, rd); + } + return true; +} + +TRANS(SSHR_s, do_scalar_shift_imm, a, gen_sshr_d, false, 0) +TRANS(USHR_s, do_scalar_shift_imm, a, gen_ushr_d, false, 0) +TRANS(SSRA_s, do_scalar_shift_imm, a, gen_ssra_d, true, 0) +TRANS(USRA_s, do_scalar_shift_imm, a, gen_usra_d, true, 0) +TRANS(SRSHR_s, do_scalar_shift_imm, a, gen_srshr_d, false, 0) +TRANS(URSHR_s, do_scalar_shift_imm, a, gen_urshr_d, false, 0) +TRANS(SRSRA_s, do_scalar_shift_imm, a, gen_srsra_d, true, 0) +TRANS(URSRA_s, do_scalar_shift_imm, a, gen_ursra_d, true, 0) +TRANS(SRI_s, do_scalar_shift_imm, a, gen_sri_d, true, 0) + +TRANS(SHL_s, do_scalar_shift_imm, a, tcg_gen_shli_i64, false, 0) +TRANS(SLI_s, do_scalar_shift_imm, a, gen_sli_d, true, 0) + +static void trunc_i64_env_imm(TCGv_i64 d, TCGv_i64 s, int64_t i, + NeonGenTwoOpEnvFn *fn) +{ + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t, s); + fn(t, tcg_env, t, tcg_constant_i32(i)); + tcg_gen_extu_i32_i64(d, t); +} + +static void gen_sqshli_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_s8); +} + +static void gen_sqshli_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_s16); +} + +static void gen_sqshli_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_s32); +} + +static void gen_sqshli_d(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_helper_neon_qshl_s64(d, tcg_env, s, tcg_constant_i64(i)); +} + +static void gen_uqshli_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_u8); +} + +static void gen_uqshli_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_u16); +} + +static void gen_uqshli_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshl_u32); +} + +static void gen_uqshli_d(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_helper_neon_qshl_u64(d, tcg_env, s, tcg_constant_i64(i)); +} + +static void gen_sqshlui_b(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshlu_s8); +} + +static void gen_sqshlui_h(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshlu_s16); +} + +static void gen_sqshlui_s(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + trunc_i64_env_imm(d, s, i, gen_helper_neon_qshlu_s32); +} + +static void gen_sqshlui_d(TCGv_i64 d, TCGv_i64 s, int64_t i) +{ + gen_helper_neon_qshlu_s64(d, tcg_env, s, tcg_constant_i64(i)); +} + +static WideShiftImmFn * const f_scalar_sqshli[] = { + gen_sqshli_b, gen_sqshli_h, gen_sqshli_s, gen_sqshli_d +}; + +static WideShiftImmFn * const f_scalar_uqshli[] = { + gen_uqshli_b, gen_uqshli_h, gen_uqshli_s, gen_uqshli_d +}; + +static WideShiftImmFn * const f_scalar_sqshlui[] = { + gen_sqshlui_b, gen_sqshlui_h, gen_sqshlui_s, gen_sqshlui_d +}; + +/* Note that the helpers sign-extend their inputs, so don't do it here. */ +TRANS(SQSHL_si, do_scalar_shift_imm, a, f_scalar_sqshli[a->esz], false, 0) +TRANS(UQSHL_si, do_scalar_shift_imm, a, f_scalar_uqshli[a->esz], false, 0) +TRANS(SQSHLU_si, do_scalar_shift_imm, a, f_scalar_sqshlui[a->esz], false, 0) + +static bool do_scalar_shift_imm_narrow(DisasContext *s, arg_rri_e *a, + WideShiftImmFn * const fns[3], + MemOp sign, bool zext) +{ + MemOp esz = a->esz; + + tcg_debug_assert(esz >= MO_8 && esz <= MO_32); + + if (fp_access_check(s)) { + TCGv_i64 rd = tcg_temp_new_i64(); + TCGv_i64 rn = tcg_temp_new_i64(); + + read_vec_element(s, rn, a->rn, 0, (esz + 1) | sign); + fns[esz](rd, rn, a->imm); + if (zext) { + tcg_gen_ext_i64(rd, rd, esz); + } + write_fp_dreg(s, a->rd, rd); + } + return true; +} + +TRANS(SQSHRN_si, do_scalar_shift_imm_narrow, a, sqshrn_fns, MO_SIGN, true) +TRANS(SQRSHRN_si, do_scalar_shift_imm_narrow, a, sqrshrn_fns, MO_SIGN, true) +TRANS(UQSHRN_si, do_scalar_shift_imm_narrow, a, uqshrn_fns, 0, false) +TRANS(UQRSHRN_si, do_scalar_shift_imm_narrow, a, uqrshrn_fns, 0, false) +TRANS(SQSHRUN_si, do_scalar_shift_imm_narrow, a, sqshrun_fns, MO_SIGN, false) +TRANS(SQRSHRUN_si, do_scalar_shift_imm_narrow, a, sqrshrun_fns, MO_SIGN, false) + +/* Shift a TCGv src by TCGv shift_amount, put result in dst. + * Note that it is the caller's responsibility to ensure that the + * shift amount is in range (ie 0..31 or 0..63) and provide the ARM + * mandated semantics for out of range shifts. + */ +static void shift_reg(TCGv_i64 dst, TCGv_i64 src, int sf, + enum a64_shift_type shift_type, TCGv_i64 shift_amount) +{ + switch (shift_type) { + case A64_SHIFT_TYPE_LSL: + tcg_gen_shl_i64(dst, src, shift_amount); + break; + case A64_SHIFT_TYPE_LSR: + tcg_gen_shr_i64(dst, src, shift_amount); + break; + case A64_SHIFT_TYPE_ASR: + if (!sf) { + tcg_gen_ext32s_i64(dst, src); + } + tcg_gen_sar_i64(dst, sf ? src : dst, shift_amount); + break; + case A64_SHIFT_TYPE_ROR: + if (sf) { + tcg_gen_rotr_i64(dst, src, shift_amount); + } else { + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t0, src); + tcg_gen_extrl_i64_i32(t1, shift_amount); + tcg_gen_rotr_i32(t0, t0, t1); + tcg_gen_extu_i32_i64(dst, t0); + } + break; + default: + assert(FALSE); /* all shift types should be handled */ + break; + } + + if (!sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(dst, dst); + } +} + +/* Shift a TCGv src by immediate, put result in dst. + * The shift amount must be in range (this should always be true as the + * relevant instructions will UNDEF on bad shift immediates). + */ +static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf, + enum a64_shift_type shift_type, unsigned int shift_i) +{ + assert(shift_i < (sf ? 64 : 32)); + + if (shift_i == 0) { + tcg_gen_mov_i64(dst, src); + } else { + shift_reg(dst, src, sf, shift_type, tcg_constant_i64(shift_i)); + } +} + +/* Logical (shifted register) + * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 + * +----+-----+-----------+-------+---+------+--------+------+------+ + * | sf | opc | 0 1 0 1 0 | shift | N | Rm | imm6 | Rn | Rd | + * +----+-----+-----------+-------+---+------+--------+------+------+ + */ +static void disas_logic_reg(DisasContext *s, uint32_t insn) +{ + TCGv_i64 tcg_rd, tcg_rn, tcg_rm; + unsigned int sf, opc, shift_type, invert, rm, shift_amount, rn, rd; + + sf = extract32(insn, 31, 1); + opc = extract32(insn, 29, 2); + shift_type = extract32(insn, 22, 2); + invert = extract32(insn, 21, 1); + rm = extract32(insn, 16, 5); + shift_amount = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + if (!sf && (shift_amount & (1 << 5))) { + unallocated_encoding(s); + return; + } + + tcg_rd = cpu_reg(s, rd); + + if (opc == 1 && shift_amount == 0 && shift_type == 0 && rn == 31) { + /* Unshifted ORR and ORN with WZR/XZR is the standard encoding for + * register-register MOV and MVN, so it is worth special casing. + */ + tcg_rm = cpu_reg(s, rm); + if (invert) { + tcg_gen_not_i64(tcg_rd, tcg_rm); + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + } else { + if (sf) { + tcg_gen_mov_i64(tcg_rd, tcg_rm); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_rm); + } + } + return; + } + + tcg_rm = read_cpu_reg(s, rm, sf); + + if (shift_amount) { + shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, shift_amount); + } + + tcg_rn = cpu_reg(s, rn); + + switch (opc | (invert << 2)) { + case 0: /* AND */ + case 3: /* ANDS */ + tcg_gen_and_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 1: /* ORR */ + tcg_gen_or_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 2: /* EOR */ + tcg_gen_xor_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 4: /* BIC */ + case 7: /* BICS */ + tcg_gen_andc_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 5: /* ORN */ + tcg_gen_orc_i64(tcg_rd, tcg_rn, tcg_rm); + break; + case 6: /* EON */ + tcg_gen_eqv_i64(tcg_rd, tcg_rn, tcg_rm); + break; + default: + assert(FALSE); + break; + } + + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + + if (opc == 3) { + gen_logic_CC(sf, tcg_rd); + } +} + +/* + * Add/subtract (extended register) + * + * 31|30|29|28 24|23 22|21|20 16|15 13|12 10|9 5|4 0| + * +--+--+--+-----------+-----+--+-------+------+------+----+----+ + * |sf|op| S| 0 1 0 1 1 | opt | 1| Rm |option| imm3 | Rn | Rd | + * +--+--+--+-----------+-----+--+-------+------+------+----+----+ + * + * sf: 0 -> 32bit, 1 -> 64bit + * op: 0 -> add , 1 -> sub + * S: 1 -> set flags + * opt: 00 + * option: extension type (see DecodeRegExtend) + * imm3: optional shift to Rm + * + * Rd = Rn + LSL(extend(Rm), amount) + */ +static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int imm3 = extract32(insn, 10, 3); + int option = extract32(insn, 13, 3); + int rm = extract32(insn, 16, 5); + int opt = extract32(insn, 22, 2); + bool setflags = extract32(insn, 29, 1); + bool sub_op = extract32(insn, 30, 1); + bool sf = extract32(insn, 31, 1); + + TCGv_i64 tcg_rm, tcg_rn; /* temps */ + TCGv_i64 tcg_rd; + TCGv_i64 tcg_result; + + if (imm3 > 4 || opt != 0) { + unallocated_encoding(s); + return; + } + + /* non-flag setting ops may use SP */ + if (!setflags) { + tcg_rd = cpu_reg_sp(s, rd); + } else { + tcg_rd = cpu_reg(s, rd); + } + tcg_rn = read_cpu_reg_sp(s, rn, sf); + + tcg_rm = read_cpu_reg(s, rm, sf); + ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3); + + tcg_result = tcg_temp_new_i64(); + + if (!setflags) { + if (sub_op) { + tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); + } else { + tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); + } + } else { + if (sub_op) { + gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); + } else { + gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); + } + } + + if (sf) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } +} + +/* + * Add/subtract (shifted register) + * + * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 + * +--+--+--+-----------+-----+--+-------+---------+------+------+ + * |sf|op| S| 0 1 0 1 1 |shift| 0| Rm | imm6 | Rn | Rd | + * +--+--+--+-----------+-----+--+-------+---------+------+------+ + * + * sf: 0 -> 32bit, 1 -> 64bit + * op: 0 -> add , 1 -> sub + * S: 1 -> set flags + * shift: 00 -> LSL, 01 -> LSR, 10 -> ASR, 11 -> RESERVED + * imm6: Shift amount to apply to Rm before the add/sub + */ +static void disas_add_sub_reg(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int imm6 = extract32(insn, 10, 6); + int rm = extract32(insn, 16, 5); + int shift_type = extract32(insn, 22, 2); + bool setflags = extract32(insn, 29, 1); + bool sub_op = extract32(insn, 30, 1); + bool sf = extract32(insn, 31, 1); + + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_rn, tcg_rm; + TCGv_i64 tcg_result; + + if ((shift_type == 3) || (!sf && (imm6 > 31))) { + unallocated_encoding(s); + return; + } + + tcg_rn = read_cpu_reg(s, rn, sf); + tcg_rm = read_cpu_reg(s, rm, sf); + + shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, imm6); + + tcg_result = tcg_temp_new_i64(); + + if (!setflags) { + if (sub_op) { + tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); + } else { + tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); + } + } else { + if (sub_op) { + gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); + } else { + gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); + } + } + + if (sf) { + tcg_gen_mov_i64(tcg_rd, tcg_result); + } else { + tcg_gen_ext32u_i64(tcg_rd, tcg_result); + } +} + +/* Data-processing (3 source) + * + * 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0 + * +--+------+-----------+------+------+----+------+------+------+ + * |sf| op54 | 1 1 0 1 1 | op31 | Rm | o0 | Ra | Rn | Rd | + * +--+------+-----------+------+------+----+------+------+------+ + */ +static void disas_data_proc_3src(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int ra = extract32(insn, 10, 5); + int rm = extract32(insn, 16, 5); + int op_id = (extract32(insn, 29, 3) << 4) | + (extract32(insn, 21, 3) << 1) | + extract32(insn, 15, 1); + bool sf = extract32(insn, 31, 1); + bool is_sub = extract32(op_id, 0, 1); + bool is_high = extract32(op_id, 2, 1); + bool is_signed = false; + TCGv_i64 tcg_op1; + TCGv_i64 tcg_op2; + TCGv_i64 tcg_tmp; + + /* Note that op_id is sf:op54:op31:o0 so it includes the 32/64 size flag */ + switch (op_id) { + case 0x42: /* SMADDL */ + case 0x43: /* SMSUBL */ + case 0x44: /* SMULH */ + is_signed = true; + break; + case 0x0: /* MADD (32bit) */ + case 0x1: /* MSUB (32bit) */ + case 0x40: /* MADD (64bit) */ + case 0x41: /* MSUB (64bit) */ + case 0x4a: /* UMADDL */ + case 0x4b: /* UMSUBL */ + case 0x4c: /* UMULH */ + break; + default: + unallocated_encoding(s); + return; + } + + if (is_high) { + TCGv_i64 low_bits = tcg_temp_new_i64(); /* low bits discarded */ + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_rn = cpu_reg(s, rn); + TCGv_i64 tcg_rm = cpu_reg(s, rm); + + if (is_signed) { + tcg_gen_muls2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); + } else { + tcg_gen_mulu2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); + } + return; + } + + tcg_op1 = tcg_temp_new_i64(); + tcg_op2 = tcg_temp_new_i64(); + tcg_tmp = tcg_temp_new_i64(); + + if (op_id < 0x42) { + tcg_gen_mov_i64(tcg_op1, cpu_reg(s, rn)); + tcg_gen_mov_i64(tcg_op2, cpu_reg(s, rm)); + } else { + if (is_signed) { + tcg_gen_ext32s_i64(tcg_op1, cpu_reg(s, rn)); + tcg_gen_ext32s_i64(tcg_op2, cpu_reg(s, rm)); + } else { + tcg_gen_ext32u_i64(tcg_op1, cpu_reg(s, rn)); + tcg_gen_ext32u_i64(tcg_op2, cpu_reg(s, rm)); + } + } + + if (ra == 31 && !is_sub) { + /* Special-case MADD with rA == XZR; it is the standard MUL alias */ + tcg_gen_mul_i64(cpu_reg(s, rd), tcg_op1, tcg_op2); + } else { + tcg_gen_mul_i64(tcg_tmp, tcg_op1, tcg_op2); + if (is_sub) { + tcg_gen_sub_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp); + } else { + tcg_gen_add_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp); + } + } + + if (!sf) { + tcg_gen_ext32u_i64(cpu_reg(s, rd), cpu_reg(s, rd)); + } +} + +/* Add/subtract (with carry) + * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0 + * +--+--+--+------------------------+------+-------------+------+-----+ + * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | 0 0 0 0 0 0 | Rn | Rd | + * +--+--+--+------------------------+------+-------------+------+-----+ + */ + +static void disas_adc_sbc(DisasContext *s, uint32_t insn) +{ + unsigned int sf, op, setflags, rm, rn, rd; + TCGv_i64 tcg_y, tcg_rn, tcg_rd; + + sf = extract32(insn, 31, 1); + op = extract32(insn, 30, 1); + setflags = extract32(insn, 29, 1); + rm = extract32(insn, 16, 5); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (op) { + tcg_y = tcg_temp_new_i64(); + tcg_gen_not_i64(tcg_y, cpu_reg(s, rm)); + } else { + tcg_y = cpu_reg(s, rm); + } + + if (setflags) { + gen_adc_CC(sf, tcg_rd, tcg_rn, tcg_y); + } else { + gen_adc(sf, tcg_rd, tcg_rn, tcg_y); + } +} + +/* + * Rotate right into flags + * 31 30 29 21 15 10 5 4 0 + * +--+--+--+-----------------+--------+-----------+------+--+------+ + * |sf|op| S| 1 1 0 1 0 0 0 0 | imm6 | 0 0 0 0 1 | Rn |o2| mask | + * +--+--+--+-----------------+--------+-----------+------+--+------+ + */ +static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn) +{ + int mask = extract32(insn, 0, 4); + int o2 = extract32(insn, 4, 1); + int rn = extract32(insn, 5, 5); + int imm6 = extract32(insn, 15, 6); + int sf_op_s = extract32(insn, 29, 3); + TCGv_i64 tcg_rn; + TCGv_i32 nzcv; + + if (sf_op_s != 5 || o2 != 0 || !dc_isar_feature(aa64_condm_4, s)) { + unallocated_encoding(s); + return; + } + + tcg_rn = read_cpu_reg(s, rn, 1); + tcg_gen_rotri_i64(tcg_rn, tcg_rn, imm6); + + nzcv = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(nzcv, tcg_rn); + + if (mask & 8) { /* N */ + tcg_gen_shli_i32(cpu_NF, nzcv, 31 - 3); + } + if (mask & 4) { /* Z */ + tcg_gen_not_i32(cpu_ZF, nzcv); + tcg_gen_andi_i32(cpu_ZF, cpu_ZF, 4); + } + if (mask & 2) { /* C */ + tcg_gen_extract_i32(cpu_CF, nzcv, 1, 1); + } + if (mask & 1) { /* V */ + tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0); + } +} + +/* + * Evaluate into flags + * 31 30 29 21 15 14 10 5 4 0 + * +--+--+--+-----------------+---------+----+---------+------+--+------+ + * |sf|op| S| 1 1 0 1 0 0 0 0 | opcode2 | sz | 0 0 1 0 | Rn |o3| mask | + * +--+--+--+-----------------+---------+----+---------+------+--+------+ + */ +static void disas_evaluate_into_flags(DisasContext *s, uint32_t insn) +{ + int o3_mask = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int o2 = extract32(insn, 15, 6); + int sz = extract32(insn, 14, 1); + int sf_op_s = extract32(insn, 29, 3); + TCGv_i32 tmp; + int shift; + + if (sf_op_s != 1 || o2 != 0 || o3_mask != 0xd || + !dc_isar_feature(aa64_condm_4, s)) { + unallocated_encoding(s); + return; + } + shift = sz ? 16 : 24; /* SETF16 or SETF8 */ + + tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, cpu_reg(s, rn)); + tcg_gen_shli_i32(cpu_NF, tmp, shift); + 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); +} + +/* Conditional compare (immediate / register) + * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 + * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ + * |sf|op| S| 1 1 0 1 0 0 1 0 |imm5/rm | cond |i/r |o2| Rn |o3|nzcv | + * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ + * [1] y [0] [0] + */ +static void disas_cc(DisasContext *s, uint32_t insn) +{ + unsigned int sf, op, y, cond, rn, nzcv, is_imm; + TCGv_i32 tcg_t0, tcg_t1, tcg_t2; + TCGv_i64 tcg_tmp, tcg_y, tcg_rn; + DisasCompare c; + + if (!extract32(insn, 29, 1)) { + unallocated_encoding(s); + return; + } + if (insn & (1 << 10 | 1 << 4)) { + unallocated_encoding(s); + return; + } + sf = extract32(insn, 31, 1); + op = extract32(insn, 30, 1); + is_imm = extract32(insn, 11, 1); + y = extract32(insn, 16, 5); /* y = rm (reg) or imm5 (imm) */ + cond = extract32(insn, 12, 4); + rn = extract32(insn, 5, 5); + nzcv = extract32(insn, 0, 4); + + /* Set T0 = !COND. */ + 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); + + /* Load the arguments for the new comparison. */ + if (is_imm) { + tcg_y = tcg_temp_new_i64(); + tcg_gen_movi_i64(tcg_y, y); + } else { + tcg_y = cpu_reg(s, y); + } + tcg_rn = cpu_reg(s, rn); + + /* Set the flags for the new comparison. */ + tcg_tmp = tcg_temp_new_i64(); + if (op) { + gen_sub_CC(sf, tcg_tmp, tcg_rn, tcg_y); + } else { + gen_add_CC(sf, tcg_tmp, tcg_rn, tcg_y); + } + + /* If COND was false, force the flags to #nzcv. Compute two masks + * to help with this: T1 = (COND ? 0 : -1), T2 = (COND ? -1 : 0). + * For tcg hosts that support ANDC, we can make do with just T1. + * In either case, allow the tcg optimizer to delete any unused mask. + */ + tcg_t1 = tcg_temp_new_i32(); + tcg_t2 = tcg_temp_new_i32(); + tcg_gen_neg_i32(tcg_t1, tcg_t0); + tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); + + if (nzcv & 8) { /* N */ + tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_NF, cpu_NF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_NF, cpu_NF, tcg_t2); + } + } + if (nzcv & 4) { /* Z */ + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_ZF, cpu_ZF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_ZF, cpu_ZF, tcg_t2); + } + } else { + tcg_gen_or_i32(cpu_ZF, cpu_ZF, tcg_t0); + } + if (nzcv & 2) { /* C */ + tcg_gen_or_i32(cpu_CF, cpu_CF, tcg_t0); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_CF, cpu_CF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_CF, cpu_CF, tcg_t2); + } + } + if (nzcv & 1) { /* V */ + tcg_gen_or_i32(cpu_VF, cpu_VF, tcg_t1); + } else { + if (TCG_TARGET_HAS_andc_i32) { + tcg_gen_andc_i32(cpu_VF, cpu_VF, tcg_t1); + } else { + tcg_gen_and_i32(cpu_VF, cpu_VF, tcg_t2); + } + } +} + +/* Conditional select + * 31 30 29 28 21 20 16 15 12 11 10 9 5 4 0 + * +----+----+---+-----------------+------+------+-----+------+------+ + * | sf | op | S | 1 1 0 1 0 1 0 0 | Rm | cond | op2 | Rn | Rd | + * +----+----+---+-----------------+------+------+-----+------+------+ + */ +static void disas_cond_select(DisasContext *s, uint32_t insn) +{ + unsigned int sf, else_inv, rm, cond, else_inc, rn, rd; + TCGv_i64 tcg_rd, zero; + DisasCompare64 c; + + if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) { + /* S == 1 or op2<1> == 1 */ + unallocated_encoding(s); + return; + } + sf = extract32(insn, 31, 1); + else_inv = extract32(insn, 30, 1); + rm = extract32(insn, 16, 5); + cond = extract32(insn, 12, 4); + else_inc = extract32(insn, 10, 1); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + tcg_rd = cpu_reg(s, rd); + + a64_test_cc(&c, cond); + zero = tcg_constant_i64(0); + + if (rn == 31 && rm == 31 && (else_inc ^ else_inv)) { + /* CSET & CSETM. */ + if (else_inv) { + 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); + TCGv_i64 t_false = read_cpu_reg(s, rm, 1); + if (else_inv && else_inc) { + tcg_gen_neg_i64(t_false, t_false); + } else if (else_inv) { + tcg_gen_not_i64(t_false, t_false); + } else if (else_inc) { + tcg_gen_addi_i64(t_false, t_false, 1); + } + tcg_gen_movcond_i64(c.cond, tcg_rd, c.value, zero, t_true, t_false); + } + + if (!sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } +} + +static void handle_clz(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd, tcg_rn; + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (sf) { + tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); + } else { + TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); + 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); + } +} + +static void handle_cls(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd, tcg_rn; + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (sf) { + tcg_gen_clrsb_i64(tcg_rd, tcg_rn); + } else { + TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); + 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); + } +} + +static void handle_rbit(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd, tcg_rn; + tcg_rd = cpu_reg(s, rd); + tcg_rn = cpu_reg(s, rn); + + if (sf) { + gen_helper_rbit64(tcg_rd, tcg_rn); + } else { + TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); + 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); + } +} + +/* REV with sf==1, opcode==3 ("REV64") */ +static void handle_rev64(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + if (!sf) { + unallocated_encoding(s); + return; + } + tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn)); +} + +/* REV with sf==0, opcode==2 + * REV32 (sf==1, opcode==2) + */ +static void handle_rev32(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_rn = cpu_reg(s, rn); + + if (sf) { + tcg_gen_bswap64_i64(tcg_rd, tcg_rn); + tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); + } else { + tcg_gen_bswap32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); + } +} + +/* REV16 (opcode==1) */ +static void handle_rev16(DisasContext *s, unsigned int sf, + unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); + TCGv_i64 mask = tcg_constant_i64(sf ? 0x00ff00ff00ff00ffull : 0x00ff00ff); + + tcg_gen_shri_i64(tcg_tmp, tcg_rn, 8); + tcg_gen_and_i64(tcg_rd, tcg_rn, mask); + 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); +} + +/* Data-processing (1 source) + * 31 30 29 28 21 20 16 15 10 9 5 4 0 + * +----+---+---+-----------------+---------+--------+------+------+ + * | sf | 1 | S | 1 1 0 1 0 1 1 0 | opcode2 | opcode | Rn | Rd | + * +----+---+---+-----------------+---------+--------+------+------+ + */ +static void disas_data_proc_1src(DisasContext *s, uint32_t insn) +{ + unsigned int sf, opcode, opcode2, rn, rd; + TCGv_i64 tcg_rd; + + if (extract32(insn, 29, 1)) { + unallocated_encoding(s); + return; + } + + sf = extract32(insn, 31, 1); + opcode = extract32(insn, 10, 6); + opcode2 = extract32(insn, 16, 5); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + +#define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) + + switch (MAP(sf, opcode2, opcode)) { + case MAP(0, 0x00, 0x00): /* RBIT */ + case MAP(1, 0x00, 0x00): + handle_rbit(s, sf, rn, rd); + break; + case MAP(0, 0x00, 0x01): /* REV16 */ + case MAP(1, 0x00, 0x01): + handle_rev16(s, sf, rn, rd); + break; + case MAP(0, 0x00, 0x02): /* REV/REV32 */ + case MAP(1, 0x00, 0x02): + handle_rev32(s, sf, rn, rd); + break; + case MAP(1, 0x00, 0x03): /* REV64 */ + handle_rev64(s, sf, rn, rd); + break; + case MAP(0, 0x00, 0x04): /* CLZ */ + case MAP(1, 0x00, 0x04): + handle_clz(s, sf, rn, rd); + break; + case MAP(0, 0x00, 0x05): /* CLS */ + case MAP(1, 0x00, 0x05): + handle_cls(s, sf, rn, rd); + break; + case MAP(1, 0x01, 0x00): /* PACIA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x01): /* PACIB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x02): /* PACDA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x03): /* PACDB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x04): /* AUTIA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x05): /* AUTIB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x06): /* AUTDA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x07): /* AUTDB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + 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; + } + break; + case MAP(1, 0x01, 0x08): /* PACIZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacia(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x09): /* PACIZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacib(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x0a): /* PACDZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacda(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x0b): /* PACDZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacdb(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x0c): /* AUTIZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autia(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x0d): /* AUTIZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autib(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x0e): /* AUTDZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autda(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x0f): /* AUTDZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autdb(tcg_rd, tcg_env, tcg_rd, tcg_constant_i64(0)); + } + break; + case MAP(1, 0x01, 0x10): /* XPACI */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_xpaci(tcg_rd, tcg_env, tcg_rd); + } + break; + case MAP(1, 0x01, 0x11): /* XPACD */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_xpacd(tcg_rd, tcg_env, tcg_rd); + } + break; + default: + do_unallocated: + unallocated_encoding(s); + break; + } + +#undef MAP +} + +static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, + unsigned int rm, unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_n, tcg_m, tcg_rd; + tcg_rd = cpu_reg(s, rd); + + if (!sf && is_signed) { + 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 { + tcg_n = read_cpu_reg(s, rn, sf); + tcg_m = read_cpu_reg(s, rm, sf); + } + + if (is_signed) { + gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); + } else { + gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); + } + + if (!sf) { /* zero extend final result */ + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } +} + +/* LSLV, LSRV, ASRV, RORV */ +static void handle_shift_reg(DisasContext *s, + enum a64_shift_type shift_type, unsigned int sf, + unsigned int rm, unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_shift = tcg_temp_new_i64(); + TCGv_i64 tcg_rd = cpu_reg(s, rd); + TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); + + tcg_gen_andi_i64(tcg_shift, cpu_reg(s, rm), sf ? 63 : 31); + shift_reg(tcg_rd, tcg_rn, sf, shift_type, tcg_shift); +} + +/* CRC32[BHWX], CRC32C[BHWX] */ +static void handle_crc32(DisasContext *s, + unsigned int sf, unsigned int sz, bool crc32c, + unsigned int rm, unsigned int rn, unsigned int rd) +{ + TCGv_i64 tcg_acc, tcg_val; + TCGv_i32 tcg_bytes; + + if (!dc_isar_feature(aa64_crc32, s) + || (sf == 1 && sz != 3) + || (sf == 0 && sz == 3)) { + unallocated_encoding(s); + return; + } + + if (sz == 3) { + tcg_val = cpu_reg(s, rm); + } else { + uint64_t mask; + switch (sz) { + case 0: + mask = 0xFF; + break; + case 1: + mask = 0xFFFF; + break; + case 2: + mask = 0xFFFFFFFF; + break; + default: + g_assert_not_reached(); + } + tcg_val = tcg_temp_new_i64(); + tcg_gen_andi_i64(tcg_val, cpu_reg(s, rm), mask); + } + + tcg_acc = cpu_reg(s, rn); + tcg_bytes = tcg_constant_i32(1 << sz); + + if (crc32c) { + gen_helper_crc32c_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); + } else { + gen_helper_crc32_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); + } +} + +/* Data-processing (2 source) + * 31 30 29 28 21 20 16 15 10 9 5 4 0 + * +----+---+---+-----------------+------+--------+------+------+ + * | sf | 0 | S | 1 1 0 1 0 1 1 0 | Rm | opcode | Rn | Rd | + * +----+---+---+-----------------+------+--------+------+------+ + */ +static void disas_data_proc_2src(DisasContext *s, uint32_t insn) +{ + unsigned int sf, rm, opcode, rn, rd, setflag; + sf = extract32(insn, 31, 1); + setflag = extract32(insn, 29, 1); + rm = extract32(insn, 16, 5); + opcode = extract32(insn, 10, 6); + rn = extract32(insn, 5, 5); + rd = extract32(insn, 0, 5); + + if (setflag && opcode != 0) { + unallocated_encoding(s); + return; + } + + switch (opcode) { + case 0: /* SUBP(S) */ + if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { + goto do_unallocated; + } else { + TCGv_i64 tcg_n, tcg_m, tcg_d; + + tcg_n = read_cpu_reg_sp(s, rn, true); + tcg_m = read_cpu_reg_sp(s, rm, true); + tcg_gen_sextract_i64(tcg_n, tcg_n, 0, 56); + tcg_gen_sextract_i64(tcg_m, tcg_m, 0, 56); + tcg_d = cpu_reg(s, rd); + + if (setflag) { + gen_sub_CC(true, tcg_d, tcg_n, tcg_m); + } else { + tcg_gen_sub_i64(tcg_d, tcg_n, tcg_m); + } + } + break; + case 2: /* UDIV */ + handle_div(s, false, sf, rm, rn, rd); + break; + case 3: /* SDIV */ + handle_div(s, true, sf, rm, rn, rd); + break; + case 4: /* IRG */ + if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { + goto do_unallocated; + } + 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), + cpu_reg_sp(s, rn)); + } + break; + case 5: /* GMI */ + if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { + goto do_unallocated; + } else { + TCGv_i64 t = tcg_temp_new_i64(); + + 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); + } + break; + case 8: /* LSLV */ + handle_shift_reg(s, A64_SHIFT_TYPE_LSL, sf, rm, rn, rd); + break; + case 9: /* LSRV */ + handle_shift_reg(s, A64_SHIFT_TYPE_LSR, sf, rm, rn, rd); + break; + case 10: /* ASRV */ + handle_shift_reg(s, A64_SHIFT_TYPE_ASR, sf, rm, rn, rd); + break; + case 11: /* RORV */ + handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd); + break; + case 12: /* PACGA */ + if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + gen_helper_pacga(cpu_reg(s, rd), tcg_env, + cpu_reg(s, rn), cpu_reg_sp(s, rm)); + break; + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: /* CRC32 */ + { + int sz = extract32(opcode, 0, 2); + bool crc32c = extract32(opcode, 2, 1); + handle_crc32(s, sf, sz, crc32c, rm, rn, rd); + break; + } + default: + do_unallocated: + unallocated_encoding(s); + break; + } +} + +/* + * Data processing - register + * 31 30 29 28 25 21 20 16 10 0 + * +--+---+--+---+-------+-----+-------+-------+---------+ + * | |op0| |op1| 1 0 1 | op2 | | op3 | | + * +--+---+--+---+-------+-----+-------+-------+---------+ + */ +static void disas_data_proc_reg(DisasContext *s, uint32_t insn) +{ + int op0 = extract32(insn, 30, 1); + int op1 = extract32(insn, 28, 1); + int op2 = extract32(insn, 21, 4); + int op3 = extract32(insn, 10, 6); + + if (!op1) { + if (op2 & 8) { + if (op2 & 1) { + /* Add/sub (extended register) */ + disas_add_sub_ext_reg(s, insn); + } else { + /* Add/sub (shifted register) */ + disas_add_sub_reg(s, insn); + } + } else { + /* Logical (shifted register) */ + disas_logic_reg(s, insn); + } + return; + } + + switch (op2) { + case 0x0: + switch (op3) { + case 0x00: /* Add/subtract (with carry) */ + disas_adc_sbc(s, insn); + break; + + case 0x01: /* Rotate right into flags */ + case 0x21: + disas_rotate_right_into_flags(s, insn); + break; + + case 0x02: /* Evaluate into flags */ + case 0x12: + case 0x22: + case 0x32: + disas_evaluate_into_flags(s, insn); + break; + + default: + goto do_unallocated; + } + break; + + case 0x2: /* Conditional compare */ + disas_cc(s, insn); /* both imm and reg forms */ + break; + + case 0x4: /* Conditional select */ + disas_cond_select(s, insn); + break; + + case 0x6: /* Data-processing */ + if (op0) { /* (1 source) */ + disas_data_proc_1src(s, insn); + } else { /* (2 source) */ + disas_data_proc_2src(s, insn); + } + break; + case 0x8 ... 0xf: /* (3 source) */ + disas_data_proc_3src(s, insn); + break; + + default: + do_unallocated: + unallocated_encoding(s); + break; + } +} + +static void handle_fp_compare(DisasContext *s, int size, + unsigned int rn, unsigned int rm, + bool cmp_with_zero, bool signal_all_nans) +{ + TCGv_i64 tcg_flags = tcg_temp_new_i64(); + TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + + if (size == MO_64) { + TCGv_i64 tcg_vn, tcg_vm; + + tcg_vn = read_fp_dreg(s, rn); + if (cmp_with_zero) { + tcg_vm = tcg_constant_i64(0); + } else { + tcg_vm = read_fp_dreg(s, rm); + } + if (signal_all_nans) { + gen_helper_vfp_cmped_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmpd_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + } else { + TCGv_i32 tcg_vn = tcg_temp_new_i32(); + TCGv_i32 tcg_vm = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_vn, rn, 0, size); + if (cmp_with_zero) { + tcg_gen_movi_i32(tcg_vm, 0); + } else { + read_vec_element_i32(s, tcg_vm, rm, 0, size); + } + + switch (size) { + case MO_32: + if (signal_all_nans) { + gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + case MO_16: + if (signal_all_nans) { + gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + default: + g_assert_not_reached(); + } + } + + gen_set_nzcv(tcg_flags); +} + +/* Floating point compare + * 31 30 29 28 24 23 22 21 20 16 15 14 13 10 9 5 4 0 + * +---+---+---+-----------+------+---+------+-----+---------+------+-------+ + * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | op | 1 0 0 0 | Rn | op2 | + * +---+---+---+-----------+------+---+------+-----+---------+------+-------+ + */ +static void disas_fp_compare(DisasContext *s, uint32_t insn) +{ + unsigned int mos, type, rm, op, rn, opc, op2r; + int size; + + mos = extract32(insn, 29, 3); + type = extract32(insn, 22, 2); + rm = extract32(insn, 16, 5); + op = extract32(insn, 14, 2); + rn = extract32(insn, 5, 5); + opc = extract32(insn, 3, 2); + op2r = extract32(insn, 0, 3); + + if (mos || op || op2r) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + size = MO_32; + break; + case 1: + size = MO_64; + break; + case 3: + size = MO_16; + if (dc_isar_feature(aa64_fp16, s)) { + break; + } + /* fallthru */ + default: + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2); +} + +/* Floating point conditional compare + * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 + * +---+---+---+-----------+------+---+------+------+-----+------+----+------+ + * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 0 1 | Rn | op | nzcv | + * +---+---+---+-----------+------+---+------+------+-----+------+----+------+ + */ +static void disas_fp_ccomp(DisasContext *s, uint32_t insn) +{ + unsigned int mos, type, rm, cond, rn, op, nzcv; + TCGLabel *label_continue = NULL; + int size; + + mos = extract32(insn, 29, 3); + type = extract32(insn, 22, 2); + rm = extract32(insn, 16, 5); + cond = extract32(insn, 12, 4); + rn = extract32(insn, 5, 5); + op = extract32(insn, 4, 1); + nzcv = extract32(insn, 0, 4); + + if (mos) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + size = MO_32; + break; + case 1: + size = MO_64; + break; + case 3: + size = MO_16; + if (dc_isar_feature(aa64_fp16, s)) { + break; + } + /* fallthru */ + default: + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + if (cond < 0x0e) { /* not always */ + TCGLabel *label_match = gen_new_label(); + label_continue = gen_new_label(); + arm_gen_test_cc(cond, label_match); + /* nomatch: */ + gen_set_nzcv(tcg_constant_i64(nzcv << 28)); + tcg_gen_br(label_continue); + gen_set_label(label_match); + } + + handle_fp_compare(s, size, rn, rm, false, op); + + if (cond < 0x0e) { + gen_set_label(label_continue); + } +} + +/* Floating-point data-processing (1 source) - half precision */ +static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) +{ + TCGv_ptr fpst = NULL; + TCGv_i32 tcg_op = read_fp_hreg(s, rn); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + switch (opcode) { + case 0x0: /* FMOV */ + tcg_gen_mov_i32(tcg_res, tcg_op); + break; + case 0x1: /* FABS */ + gen_vfp_absh(tcg_res, tcg_op); + break; + case 0x2: /* FNEG */ + gen_vfp_negh(tcg_res, tcg_op); + break; + case 0x3: /* FSQRT */ + fpst = fpstatus_ptr(FPST_FPCR_F16); + gen_helper_sqrt_f16(tcg_res, tcg_op, fpst); + break; + case 0x8: /* FRINTN */ + case 0x9: /* FRINTP */ + case 0xa: /* FRINTM */ + case 0xb: /* FRINTZ */ + case 0xc: /* FRINTA */ + { + TCGv_i32 tcg_rmode; + + fpst = fpstatus_ptr(FPST_FPCR_F16); + tcg_rmode = gen_set_rmode(opcode & 7, fpst); + gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); + gen_restore_rmode(tcg_rmode, fpst); + break; + } + case 0xe: /* FRINTX */ + fpst = fpstatus_ptr(FPST_FPCR_F16); + gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, fpst); + break; + case 0xf: /* FRINTI */ + fpst = fpstatus_ptr(FPST_FPCR_F16); + gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); + break; + default: + g_assert_not_reached(); + } + + write_fp_sreg(s, rd, tcg_res); +} + +/* Floating-point data-processing (1 source) - single precision */ +static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) +{ + void (*gen_fpst)(TCGv_i32, TCGv_i32, TCGv_ptr); + TCGv_i32 tcg_op, tcg_res; + TCGv_ptr fpst; + int rmode = -1; + + tcg_op = read_fp_sreg(s, rn); + tcg_res = tcg_temp_new_i32(); + + switch (opcode) { + case 0x0: /* FMOV */ + tcg_gen_mov_i32(tcg_res, tcg_op); + goto done; + case 0x1: /* FABS */ + gen_vfp_abss(tcg_res, tcg_op); + goto done; + case 0x2: /* FNEG */ + gen_vfp_negs(tcg_res, tcg_op); + goto done; + case 0x3: /* FSQRT */ + gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); + goto done; + case 0x6: /* BFCVT */ + gen_fpst = gen_helper_bfcvt; + break; + case 0x8: /* FRINTN */ + case 0x9: /* FRINTP */ + case 0xa: /* FRINTM */ + case 0xb: /* FRINTZ */ + case 0xc: /* FRINTA */ + rmode = opcode & 7; + gen_fpst = gen_helper_rints; + break; + case 0xe: /* FRINTX */ + gen_fpst = gen_helper_rints_exact; + break; + case 0xf: /* FRINTI */ + gen_fpst = gen_helper_rints; + break; + case 0x10: /* FRINT32Z */ + rmode = FPROUNDING_ZERO; + gen_fpst = gen_helper_frint32_s; + break; + case 0x11: /* FRINT32X */ + gen_fpst = gen_helper_frint32_s; + break; + case 0x12: /* FRINT64Z */ + rmode = FPROUNDING_ZERO; + gen_fpst = gen_helper_frint64_s; + break; + case 0x13: /* FRINT64X */ + gen_fpst = gen_helper_frint64_s; + break; + default: + g_assert_not_reached(); + } + + fpst = fpstatus_ptr(FPST_FPCR); + if (rmode >= 0) { + TCGv_i32 tcg_rmode = gen_set_rmode(rmode, fpst); + gen_fpst(tcg_res, tcg_op, fpst); + gen_restore_rmode(tcg_rmode, fpst); + } else { + gen_fpst(tcg_res, tcg_op, fpst); + } + + done: + write_fp_sreg(s, rd, tcg_res); +} + +/* Floating-point data-processing (1 source) - double precision */ +static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) +{ + void (*gen_fpst)(TCGv_i64, TCGv_i64, TCGv_ptr); + TCGv_i64 tcg_op, tcg_res; + TCGv_ptr fpst; + int rmode = -1; + + switch (opcode) { + case 0x0: /* FMOV */ + gen_gvec_fn2(s, false, rd, rn, tcg_gen_gvec_mov, 0); + return; + } + + tcg_op = read_fp_dreg(s, rn); + tcg_res = tcg_temp_new_i64(); + + switch (opcode) { + case 0x1: /* FABS */ + gen_vfp_absd(tcg_res, tcg_op); + goto done; + case 0x2: /* FNEG */ + gen_vfp_negd(tcg_res, tcg_op); + goto done; + case 0x3: /* FSQRT */ + 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 = opcode & 7; + gen_fpst = gen_helper_rintd; + break; + case 0xe: /* FRINTX */ + gen_fpst = gen_helper_rintd_exact; + break; + case 0xf: /* FRINTI */ + gen_fpst = gen_helper_rintd; + break; + case 0x10: /* FRINT32Z */ + rmode = FPROUNDING_ZERO; + gen_fpst = gen_helper_frint32_d; + break; + case 0x11: /* FRINT32X */ + gen_fpst = gen_helper_frint32_d; + break; + case 0x12: /* FRINT64Z */ + rmode = FPROUNDING_ZERO; + gen_fpst = gen_helper_frint64_d; + break; + case 0x13: /* FRINT64X */ + gen_fpst = gen_helper_frint64_d; + break; + default: + g_assert_not_reached(); + } + + fpst = fpstatus_ptr(FPST_FPCR); + if (rmode >= 0) { + TCGv_i32 tcg_rmode = gen_set_rmode(rmode, fpst); + gen_fpst(tcg_res, tcg_op, fpst); + gen_restore_rmode(tcg_rmode, fpst); + } else { + gen_fpst(tcg_res, tcg_op, fpst); + } + + done: + write_fp_dreg(s, rd, tcg_res); +} + +static void handle_fp_fcvt(DisasContext *s, int opcode, + int rd, int rn, int dtype, int ntype) +{ + switch (ntype) { + case 0x0: + { + TCGv_i32 tcg_rn = read_fp_sreg(s, rn); + if (dtype == 1) { + /* Single to double */ + TCGv_i64 tcg_rd = tcg_temp_new_i64(); + gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, tcg_env); + write_fp_dreg(s, rd, tcg_rd); + } else { + /* Single to half */ + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + + 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); + } + break; + } + case 0x1: + { + TCGv_i64 tcg_rn = read_fp_dreg(s, rn); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + if (dtype == 0) { + /* Double to single */ + 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 */ + } + write_fp_sreg(s, rd, tcg_rd); + break; + } + case 0x3: + { + TCGv_i32 tcg_rn = read_fp_sreg(s, rn); + TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 tcg_ahp = get_ahp_flag(); + tcg_gen_ext16u_i32(tcg_rn, tcg_rn); + if (dtype == 0) { + /* Half to single */ + 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); + } 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); + } + break; + } + default: + g_assert_not_reached(); + } +} + +/* Floating point data-processing (1 source) + * 31 30 29 28 24 23 22 21 20 15 14 10 9 5 4 0 + * +---+---+---+-----------+------+---+--------+-----------+------+------+ + * | M | 0 | S | 1 1 1 1 0 | type | 1 | opcode | 1 0 0 0 0 | Rn | Rd | + * +---+---+---+-----------+------+---+--------+-----------+------+------+ + */ +static void disas_fp_1src(DisasContext *s, uint32_t insn) +{ + int mos = extract32(insn, 29, 3); + int type = extract32(insn, 22, 2); + int opcode = extract32(insn, 15, 6); + int rn = extract32(insn, 5, 5); + int rd = extract32(insn, 0, 5); + + if (mos) { + goto do_unallocated; + } + + switch (opcode) { + case 0x4: case 0x5: case 0x7: + { + /* FCVT between half, single and double precision */ + int dtype = extract32(opcode, 0, 2); + if (type == 2 || dtype == type) { + goto do_unallocated; + } + if (!fp_access_check(s)) { + return; + } + + handle_fp_fcvt(s, opcode, rd, rn, dtype, type); + break; + } + + case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */ + if (type > 1 || !dc_isar_feature(aa64_frint, s)) { + goto do_unallocated; + } + /* fall through */ + case 0x0 ... 0x3: + case 0x8 ... 0xc: + case 0xe ... 0xf: + /* 32-to-32 and 64-to-64 ops */ + switch (type) { + case 0: + if (!fp_access_check(s)) { + return; + } + handle_fp_1src_single(s, opcode, rd, rn); + break; + case 1: + if (!fp_access_check(s)) { + return; + } + handle_fp_1src_double(s, opcode, rd, rn); + break; + case 3: + if (!dc_isar_feature(aa64_fp16, s)) { + goto do_unallocated; + } + + if (!fp_access_check(s)) { + return; + } + handle_fp_1src_half(s, opcode, rd, rn); + break; + default: + goto do_unallocated; + } + break; + + case 0x6: + switch (type) { + case 1: /* BFCVT */ + if (!dc_isar_feature(aa64_bf16, s)) { + goto do_unallocated; + } + if (!fp_access_check(s)) { + return; + } + handle_fp_1src_single(s, opcode, rd, rn); + break; + default: + goto do_unallocated; + } + break; + + default: + do_unallocated: + unallocated_encoding(s); + break; + } +} + +/* Handle floating point <=> fixed point conversions. Note that we can + * also deal with fp <=> integer conversions as a special case (scale == 64) + * OPTME: consider handling that special case specially or at least skipping + * the call to scalbn in the helpers for zero shifts. + */ +static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, + bool itof, int rmode, int scale, int sf, int type) +{ + bool is_signed = !(opcode & 1); + TCGv_ptr tcg_fpstatus; + TCGv_i32 tcg_shift, tcg_single; + TCGv_i64 tcg_double; + + tcg_fpstatus = fpstatus_ptr(type == 3 ? FPST_FPCR_F16 : FPST_FPCR); + + tcg_shift = tcg_constant_i32(64 - scale); + + if (itof) { + TCGv_i64 tcg_int = cpu_reg(s, rn); + if (!sf) { + TCGv_i64 tcg_extend = tcg_temp_new_i64(); + + if (is_signed) { + tcg_gen_ext32s_i64(tcg_extend, tcg_int); + } else { + tcg_gen_ext32u_i64(tcg_extend, tcg_int); + } + + tcg_int = tcg_extend; + } + + switch (type) { + case 1: /* float64 */ + tcg_double = tcg_temp_new_i64(); + if (is_signed) { + gen_helper_vfp_sqtod(tcg_double, tcg_int, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtod(tcg_double, tcg_int, + tcg_shift, tcg_fpstatus); + } + write_fp_dreg(s, rd, tcg_double); + break; + + case 0: /* float32 */ + tcg_single = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_sqtos(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtos(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } + write_fp_sreg(s, rd, tcg_single); + break; + + case 3: /* float16 */ + tcg_single = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_sqtoh(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtoh(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } + write_fp_sreg(s, rd, tcg_single); + break; + + default: + g_assert_not_reached(); + } + } else { + TCGv_i64 tcg_int = cpu_reg(s, rd); + TCGv_i32 tcg_rmode; + + if (extract32(opcode, 2, 1)) { + /* There are too many rounding modes to all fit into rmode, + * so FCVTA[US] is a special case. + */ + rmode = FPROUNDING_TIEAWAY; + } + + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); + + switch (type) { + case 1: /* float64 */ + tcg_double = read_fp_dreg(s, rn); + if (is_signed) { + if (!sf) { + gen_helper_vfp_tosld(tcg_int, tcg_double, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_tosqd(tcg_int, tcg_double, + tcg_shift, tcg_fpstatus); + } + } else { + if (!sf) { + gen_helper_vfp_tould(tcg_int, tcg_double, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_touqd(tcg_int, tcg_double, + tcg_shift, tcg_fpstatus); + } + } + if (!sf) { + tcg_gen_ext32u_i64(tcg_int, tcg_int); + } + break; + + case 0: /* float32 */ + tcg_single = read_fp_sreg(s, rn); + if (sf) { + if (is_signed) { + gen_helper_vfp_tosqs(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_touqs(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } + } else { + TCGv_i32 tcg_dest = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_tosls(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_touls(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } + tcg_gen_extu_i32_i64(tcg_int, tcg_dest); + } + break; + + case 3: /* float16 */ + tcg_single = read_fp_sreg(s, rn); + if (sf) { + if (is_signed) { + gen_helper_vfp_tosqh(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_touqh(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } + } else { + TCGv_i32 tcg_dest = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_toslh(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_toulh(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } + tcg_gen_extu_i32_i64(tcg_int, tcg_dest); + } + break; + + default: + g_assert_not_reached(); + } + + gen_restore_rmode(tcg_rmode, tcg_fpstatus); + } +} + +/* Floating point <-> fixed point conversions + * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 + * +----+---+---+-----------+------+---+-------+--------+-------+------+------+ + * | sf | 0 | S | 1 1 1 1 0 | type | 0 | rmode | opcode | scale | Rn | Rd | + * +----+---+---+-----------+------+---+-------+--------+-------+------+------+ + */ +static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int scale = extract32(insn, 10, 6); + int opcode = extract32(insn, 16, 3); + int rmode = extract32(insn, 19, 2); + int type = extract32(insn, 22, 2); + bool sbit = extract32(insn, 29, 1); + bool sf = extract32(insn, 31, 1); + bool itof; + + if (sbit || (!sf && scale < 32)) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: /* float32 */ + case 1: /* float64 */ + break; + case 3: /* float16 */ + if (dc_isar_feature(aa64_fp16, s)) { + break; + } + /* fallthru */ + default: + unallocated_encoding(s); + return; + } + + switch ((rmode << 3) | opcode) { + case 0x2: /* SCVTF */ + case 0x3: /* UCVTF */ + itof = true; + break; + case 0x18: /* FCVTZS */ + case 0x19: /* FCVTZU */ + itof = false; + break; + default: + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + handle_fpfpcvt(s, rd, rn, opcode, itof, FPROUNDING_ZERO, scale, sf, type); +} + +static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) +{ + /* FMOV: gpr to or from float, double, or top half of quad fp reg, + * without conversion. + */ + + if (itof) { + TCGv_i64 tcg_rn = cpu_reg(s, rn); + TCGv_i64 tmp; + + switch (type) { + case 0: + /* 32 bit */ + tmp = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(tmp, tcg_rn); + write_fp_dreg(s, rd, tmp); + break; + case 1: + /* 64 bit */ + write_fp_dreg(s, rd, tcg_rn); + break; + case 2: + /* 64 bit to top half. */ + tcg_gen_st_i64(tcg_rn, tcg_env, fp_reg_hi_offset(s, rd)); + clear_vec_high(s, true, rd); + break; + case 3: + /* 16 bit */ + tmp = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(tmp, tcg_rn); + write_fp_dreg(s, rd, tmp); + break; + default: + g_assert_not_reached(); + } + } else { + TCGv_i64 tcg_rd = cpu_reg(s, rd); + + switch (type) { + case 0: + /* 32 bit */ + 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, tcg_env, fp_reg_offset(s, rn, MO_64)); + break; + case 2: + /* 64 bits from top half */ + 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, tcg_env, fp_reg_offset(s, rn, MO_16)); + break; + default: + g_assert_not_reached(); + } + } +} + +static void handle_fjcvtzs(DisasContext *s, int rd, int rn) +{ + TCGv_i64 t = read_fp_dreg(s, rn); + TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR); + + gen_helper_fjcvtzs(t, t, 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); +} + +/* Floating point <-> integer conversions + * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 + * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ + * | sf | 0 | S | 1 1 1 1 0 | type | 1 | rmode | opc | 0 0 0 0 0 0 | Rn | Rd | + * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ + */ +static void disas_fp_int_conv(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 16, 3); + int rmode = extract32(insn, 19, 2); + int type = extract32(insn, 22, 2); + bool sbit = extract32(insn, 29, 1); + bool sf = extract32(insn, 31, 1); + bool itof = false; + + if (sbit) { + goto do_unallocated; + } + + switch (opcode) { + case 2: /* SCVTF */ + case 3: /* UCVTF */ + itof = true; + /* fallthru */ + case 4: /* FCVTAS */ + case 5: /* FCVTAU */ + if (rmode != 0) { + goto do_unallocated; + } + /* fallthru */ + case 0: /* FCVT[NPMZ]S */ + case 1: /* FCVT[NPMZ]U */ + switch (type) { + case 0: /* float32 */ + case 1: /* float64 */ + break; + case 3: /* float16 */ + if (!dc_isar_feature(aa64_fp16, s)) { + goto do_unallocated; + } + break; + default: + goto do_unallocated; + } + if (!fp_access_check(s)) { + return; + } + handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type); + break; + + default: + switch (sf << 7 | type << 5 | rmode << 3 | opcode) { + case 0b01100110: /* FMOV half <-> 32-bit int */ + case 0b01100111: + case 0b11100110: /* FMOV half <-> 64-bit int */ + case 0b11100111: + if (!dc_isar_feature(aa64_fp16, s)) { + goto do_unallocated; + } + /* fallthru */ + case 0b00000110: /* FMOV 32-bit */ + case 0b00000111: + case 0b10100110: /* FMOV 64-bit */ + case 0b10100111: + case 0b11001110: /* FMOV top half of 128-bit */ + case 0b11001111: + if (!fp_access_check(s)) { + return; + } + itof = opcode & 1; + handle_fmov(s, rd, rn, type, itof); + break; + + case 0b00111110: /* FJCVTZS */ + if (!dc_isar_feature(aa64_jscvt, s)) { + goto do_unallocated; + } else if (fp_access_check(s)) { + handle_fjcvtzs(s, rd, rn); + } + break; + + default: + do_unallocated: + unallocated_encoding(s); + return; + } + break; + } +} + +/* FP-specific subcases of table C3-6 (SIMD and FP data processing) + * 31 30 29 28 25 24 0 + * +---+---+---+---------+-----------------------------+ + * | | 0 | | 1 1 1 1 | | + * +---+---+---+---------+-----------------------------+ + */ +static void disas_data_proc_fp(DisasContext *s, uint32_t insn) +{ + if (extract32(insn, 24, 1)) { + unallocated_encoding(s); /* in decodetree */ + } else if (extract32(insn, 21, 1) == 0) { + /* Floating point to fixed point conversions */ + disas_fp_fixed_conv(s, insn); + } else { + switch (extract32(insn, 10, 2)) { + case 1: + /* Floating point conditional compare */ + disas_fp_ccomp(s, insn); + break; + case 2: + /* Floating point data-processing (2 source) */ + unallocated_encoding(s); /* in decodetree */ + break; + case 3: + /* Floating point conditional select */ + unallocated_encoding(s); /* in decodetree */ + break; + case 0: + switch (ctz32(extract32(insn, 12, 4))) { + case 0: /* [15:12] == xxx1 */ + /* Floating point immediate */ + unallocated_encoding(s); /* in decodetree */ + break; + case 1: /* [15:12] == xx10 */ + /* Floating point compare */ + disas_fp_compare(s, insn); + break; + case 2: /* [15:12] == x100 */ + /* Floating point data-processing (1 source) */ + disas_fp_1src(s, insn); + break; + case 3: /* [15:12] == 1000 */ + unallocated_encoding(s); + break; + default: /* [15:12] == 0000 */ + /* Floating point <-> integer conversions */ + disas_fp_int_conv(s, insn); + break; + } + break; + } + } +} + +/* Common vector code for handling integer to FP conversion */ +static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, + int elements, int is_signed, + int fracbits, int size) +{ + TCGv_ptr tcg_fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + TCGv_i32 tcg_shift = NULL; + + MemOp mop = size | (is_signed ? MO_SIGN : 0); + int pass; + + if (fracbits || size == MO_64) { + tcg_shift = tcg_constant_i32(fracbits); + } + + if (size == MO_64) { + TCGv_i64 tcg_int64 = tcg_temp_new_i64(); + TCGv_i64 tcg_double = tcg_temp_new_i64(); + + for (pass = 0; pass < elements; pass++) { + read_vec_element(s, tcg_int64, rn, pass, mop); + + if (is_signed) { + gen_helper_vfp_sqtod(tcg_double, tcg_int64, + tcg_shift, tcg_fpst); + } else { + gen_helper_vfp_uqtod(tcg_double, tcg_int64, + tcg_shift, tcg_fpst); + } + if (elements == 1) { + write_fp_dreg(s, rd, tcg_double); + } else { + write_vec_element(s, tcg_double, rd, pass, MO_64); + } + } + } else { + TCGv_i32 tcg_int32 = tcg_temp_new_i32(); + TCGv_i32 tcg_float = tcg_temp_new_i32(); + + for (pass = 0; pass < elements; pass++) { + read_vec_element_i32(s, tcg_int32, rn, pass, mop); + + switch (size) { + case MO_32: + if (fracbits) { + if (is_signed) { + gen_helper_vfp_sltos(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } else { + gen_helper_vfp_ultos(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } + } else { + if (is_signed) { + gen_helper_vfp_sitos(tcg_float, tcg_int32, tcg_fpst); + } else { + gen_helper_vfp_uitos(tcg_float, tcg_int32, tcg_fpst); + } + } + break; + case MO_16: + if (fracbits) { + if (is_signed) { + gen_helper_vfp_sltoh(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } else { + gen_helper_vfp_ultoh(tcg_float, tcg_int32, + tcg_shift, tcg_fpst); + } + } else { + if (is_signed) { + gen_helper_vfp_sitoh(tcg_float, tcg_int32, tcg_fpst); + } else { + gen_helper_vfp_uitoh(tcg_float, tcg_int32, tcg_fpst); + } + } + break; + default: + g_assert_not_reached(); + } + + if (elements == 1) { + write_fp_sreg(s, rd, tcg_float); + } else { + write_vec_element_i32(s, tcg_float, rd, pass, size); + } + } + } + + clear_vec_high(s, elements << size == 16, rd); +} + +/* UCVTF/SCVTF - Integer to FP conversion */ +static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, + bool is_q, bool is_u, + int immh, int immb, int opcode, + int rn, int rd) +{ + int size, elements, fracbits; + int immhb = immh << 3 | immb; + + if (immh & 8) { + size = MO_64; + if (!is_scalar && !is_q) { + unallocated_encoding(s); + return; + } + } else if (immh & 4) { + size = MO_32; + } else if (immh & 2) { + size = MO_16; + if (!dc_isar_feature(aa64_fp16, s)) { + unallocated_encoding(s); + return; + } + } else { + /* immh == 0 would be a failure of the decode logic */ + g_assert(immh == 1); + unallocated_encoding(s); + return; + } + + if (is_scalar) { + elements = 1; + } else { + elements = (8 << is_q) >> size; + } + fracbits = (16 << size) - immhb; + + if (!fp_access_check(s)) { + return; + } + + handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size); +} + +/* FCVTZS, FVCVTZU - FP to fixedpoint conversion */ +static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, + bool is_q, bool is_u, + int immh, int immb, int rn, int rd) +{ + int immhb = immh << 3 | immb; + int pass, size, fracbits; + TCGv_ptr tcg_fpstatus; + TCGv_i32 tcg_rmode, tcg_shift; + + if (immh & 0x8) { + size = MO_64; + if (!is_scalar && !is_q) { + unallocated_encoding(s); + return; + } + } else if (immh & 0x4) { + size = MO_32; + } else if (immh & 0x2) { + size = MO_16; + if (!dc_isar_feature(aa64_fp16, s)) { + unallocated_encoding(s); + return; + } + } else { + /* Should have split out AdvSIMD modified immediate earlier. */ + assert(immh == 1); + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + assert(!(is_scalar && is_q)); + + tcg_fpstatus = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + tcg_rmode = gen_set_rmode(FPROUNDING_ZERO, tcg_fpstatus); + fracbits = (16 << size) - immhb; + tcg_shift = tcg_constant_i32(fracbits); + + if (size == MO_64) { + int maxpass = is_scalar ? 1 : 2; + + for (pass = 0; pass < maxpass; pass++) { + TCGv_i64 tcg_op = tcg_temp_new_i64(); + + read_vec_element(s, tcg_op, rn, pass, MO_64); + if (is_u) { + gen_helper_vfp_touqd(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_tosqd(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); + } + write_vec_element(s, tcg_op, rd, pass, MO_64); + } + clear_vec_high(s, is_q, rd); + } else { + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + int maxpass = is_scalar ? 1 : ((8 << is_q) >> size); + + switch (size) { + case MO_16: + if (is_u) { + fn = gen_helper_vfp_touhh; + } else { + fn = gen_helper_vfp_toshh; + } + break; + case MO_32: + if (is_u) { + fn = gen_helper_vfp_touls; + } else { + fn = gen_helper_vfp_tosls; + } + break; + default: + g_assert_not_reached(); + } + + for (pass = 0; pass < maxpass; pass++) { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, pass, size); + fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); + if (is_scalar) { + if (size == MO_16 && !is_u) { + tcg_gen_ext16u_i32(tcg_op, tcg_op); + } + write_fp_sreg(s, rd, tcg_op); + } else { + write_vec_element_i32(s, tcg_op, rd, pass, size); + } + } + if (!is_scalar) { + clear_vec_high(s, is_q, rd); + } + } + + gen_restore_rmode(tcg_rmode, tcg_fpstatus); +} + +/* AdvSIMD scalar shift by immediate + * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0 + * +-----+---+-------------+------+------+--------+---+------+------+ + * | 0 1 | U | 1 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd | + * +-----+---+-------------+------+------+--------+---+------+------+ + * + * This is the scalar version so it works on a fixed sized registers + */ +static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 11, 5); + int immb = extract32(insn, 16, 3); + int immh = extract32(insn, 19, 4); + bool is_u = extract32(insn, 29, 1); + + if (immh == 0) { + unallocated_encoding(s); + return; + } + + switch (opcode) { + case 0x1c: /* SCVTF, UCVTF */ + handle_simd_shift_intfp_conv(s, true, false, is_u, immh, immb, + opcode, rn, rd); + break; + case 0x1f: /* FCVTZS, FCVTZU */ + handle_simd_shift_fpint_conv(s, true, false, is_u, immh, immb, rn, rd); + break; + default: + case 0x00: /* SSHR / USHR */ + case 0x02: /* SSRA / USRA */ + case 0x04: /* SRSHR / URSHR */ + case 0x06: /* SRSRA / URSRA */ + case 0x08: /* SRI */ + case 0x0a: /* SHL / SLI */ + case 0x0c: /* SQSHLU */ + case 0x0e: /* SQSHL, UQSHL */ + case 0x10: /* SQSHRUN */ + case 0x11: /* SQRSHRUN */ + case 0x12: /* SQSHRN, UQSHRN */ + case 0x13: /* SQRSHRN, UQRSHRN */ + unallocated_encoding(s); + break; + } +} + +static void handle_2misc_64(DisasContext *s, int opcode, bool u, + TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, + TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) +{ + /* Handle 64->64 opcodes which are shared between the scalar and + * vector 2-reg-misc groups. We cover every integer opcode where size == 3 + * is valid in either group and also the double-precision fp ops. + * The caller only need provide tcg_rmode and tcg_fpstatus if the op + * requires them. + */ + TCGCond cond; + + switch (opcode) { + case 0x4: /* CLS, CLZ */ + if (u) { + tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); + } else { + tcg_gen_clrsb_i64(tcg_rd, tcg_rn); + } + break; + case 0x5: /* NOT */ + /* This opcode is shared with CNT and RBIT but we have earlier + * enforced that size == 3 if and only if this is the NOT insn. + */ + tcg_gen_not_i64(tcg_rd, tcg_rn); + break; + case 0x7: /* SQABS, SQNEG */ + if (u) { + gen_helper_neon_qneg_s64(tcg_rd, tcg_env, tcg_rn); + } else { + gen_helper_neon_qabs_s64(tcg_rd, tcg_env, tcg_rn); + } + break; + case 0xa: /* CMLT */ + cond = TCG_COND_LT; + do_cmop: + /* 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; + goto do_cmop; + case 0x9: /* CMEQ, CMLE */ + cond = u ? TCG_COND_LE : TCG_COND_EQ; + goto do_cmop; + case 0xb: /* ABS, NEG */ + if (u) { + tcg_gen_neg_i64(tcg_rd, tcg_rn); + } else { + tcg_gen_abs_i64(tcg_rd, tcg_rn); + } + break; + case 0x2f: /* FABS */ + gen_vfp_absd(tcg_rd, tcg_rn); + break; + case 0x6f: /* FNEG */ + gen_vfp_negd(tcg_rd, tcg_rn); + break; + case 0x7f: /* FSQRT */ + gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, tcg_env); + break; + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_vfp_tosqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_vfp_touqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); + break; + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + gen_helper_rintd(tcg_rd, tcg_rn, tcg_fpstatus); + break; + case 0x59: /* FRINTX */ + gen_helper_rintd_exact(tcg_rd, tcg_rn, tcg_fpstatus); + break; + case 0x1e: /* FRINT32Z */ + case 0x5e: /* FRINT32X */ + gen_helper_frint32_d(tcg_rd, tcg_rn, tcg_fpstatus); + break; + case 0x1f: /* FRINT64Z */ + case 0x5f: /* FRINT64X */ + gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } +} + +static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, + bool is_scalar, bool is_u, bool is_q, + int size, int rn, int rd) +{ + bool is_double = (size == MO_64); + TCGv_ptr fpst; + + if (!fp_access_check(s)) { + return; + } + + fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); + + if (is_double) { + TCGv_i64 tcg_op = tcg_temp_new_i64(); + TCGv_i64 tcg_zero = tcg_constant_i64(0); + TCGv_i64 tcg_res = tcg_temp_new_i64(); + NeonGenTwoDoubleOpFn *genfn; + bool swap = false; + int pass; + + switch (opcode) { + case 0x2e: /* FCMLT (zero) */ + swap = true; + /* fallthrough */ + case 0x2c: /* FCMGT (zero) */ + genfn = gen_helper_neon_cgt_f64; + break; + case 0x2d: /* FCMEQ (zero) */ + genfn = gen_helper_neon_ceq_f64; + break; + case 0x6d: /* FCMLE (zero) */ + swap = true; + /* fall through */ + case 0x6c: /* FCMGE (zero) */ + genfn = gen_helper_neon_cge_f64; + break; + default: + g_assert_not_reached(); + } + + for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { + read_vec_element(s, tcg_op, rn, pass, MO_64); + if (swap) { + genfn(tcg_res, tcg_zero, tcg_op, fpst); + } else { + genfn(tcg_res, tcg_op, tcg_zero, fpst); + } + write_vec_element(s, tcg_res, rd, pass, MO_64); + } + + clear_vec_high(s, !is_scalar, rd); + } else { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_zero = tcg_constant_i32(0); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + NeonGenTwoSingleOpFn *genfn; + bool swap = false; + int pass, maxpasses; + + if (size == MO_16) { + switch (opcode) { + case 0x2e: /* FCMLT (zero) */ + swap = true; + /* fall through */ + case 0x2c: /* FCMGT (zero) */ + genfn = gen_helper_advsimd_cgt_f16; + break; + case 0x2d: /* FCMEQ (zero) */ + genfn = gen_helper_advsimd_ceq_f16; + break; + case 0x6d: /* FCMLE (zero) */ + swap = true; + /* fall through */ + case 0x6c: /* FCMGE (zero) */ + genfn = gen_helper_advsimd_cge_f16; + break; + default: + g_assert_not_reached(); + } + } else { + switch (opcode) { + case 0x2e: /* FCMLT (zero) */ + swap = true; + /* fall through */ + case 0x2c: /* FCMGT (zero) */ + genfn = gen_helper_neon_cgt_f32; + break; + case 0x2d: /* FCMEQ (zero) */ + genfn = gen_helper_neon_ceq_f32; + break; + case 0x6d: /* FCMLE (zero) */ + swap = true; + /* fall through */ + case 0x6c: /* FCMGE (zero) */ + genfn = gen_helper_neon_cge_f32; + break; + default: + g_assert_not_reached(); + } + } + + if (is_scalar) { + maxpasses = 1; + } else { + int vector_size = 8 << is_q; + maxpasses = vector_size >> size; + } + + for (pass = 0; pass < maxpasses; pass++) { + read_vec_element_i32(s, tcg_op, rn, pass, size); + if (swap) { + genfn(tcg_res, tcg_zero, tcg_op, fpst); + } else { + genfn(tcg_res, tcg_op, tcg_zero, fpst); + } + if (is_scalar) { + write_fp_sreg(s, rd, tcg_res); + } else { + write_vec_element_i32(s, tcg_res, rd, pass, size); + } + } + + if (!is_scalar) { + clear_vec_high(s, is_q, rd); + } + } +} + +static void handle_2misc_reciprocal(DisasContext *s, int opcode, + bool is_scalar, bool is_u, bool is_q, + int size, int rn, int rd) +{ + bool is_double = (size == 3); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + + if (is_double) { + TCGv_i64 tcg_op = tcg_temp_new_i64(); + TCGv_i64 tcg_res = tcg_temp_new_i64(); + int pass; + + for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { + read_vec_element(s, tcg_op, rn, pass, MO_64); + switch (opcode) { + case 0x3d: /* FRECPE */ + gen_helper_recpe_f64(tcg_res, tcg_op, fpst); + break; + case 0x3f: /* FRECPX */ + gen_helper_frecpx_f64(tcg_res, tcg_op, fpst); + break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f64(tcg_res, tcg_op, fpst); + break; + default: + g_assert_not_reached(); + } + write_vec_element(s, tcg_res, rd, pass, MO_64); + } + clear_vec_high(s, !is_scalar, rd); + } else { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + int pass, maxpasses; + + if (is_scalar) { + maxpasses = 1; + } else { + maxpasses = is_q ? 4 : 2; + } + + for (pass = 0; pass < maxpasses; pass++) { + read_vec_element_i32(s, tcg_op, rn, pass, MO_32); + + switch (opcode) { + case 0x3c: /* URECPE */ + gen_helper_recpe_u32(tcg_res, tcg_op); + break; + case 0x3d: /* FRECPE */ + gen_helper_recpe_f32(tcg_res, tcg_op, fpst); + break; + case 0x3f: /* FRECPX */ + gen_helper_frecpx_f32(tcg_res, tcg_op, fpst); + break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f32(tcg_res, tcg_op, fpst); + break; + default: + g_assert_not_reached(); + } + + if (is_scalar) { + write_fp_sreg(s, rd, tcg_res); + } else { + write_vec_element_i32(s, tcg_res, rd, pass, MO_32); + } + } + if (!is_scalar) { + clear_vec_high(s, is_q, rd); + } + } +} + +static void handle_2misc_narrow(DisasContext *s, bool scalar, + int opcode, bool u, bool is_q, + int size, int rn, int rd) +{ + /* Handle 2-reg-misc ops which are narrowing (so each 2*size element + * in the source becomes a size element in the destination). + */ + int pass; + TCGv_i64 tcg_res[2]; + int destelt = is_q ? 2 : 0; + int passes = scalar ? 1 : 2; + + if (scalar) { + tcg_res[1] = tcg_constant_i64(0); + } + + for (pass = 0; pass < passes; pass++) { + TCGv_i64 tcg_op = tcg_temp_new_i64(); + NeonGenOne64OpFn *genfn = NULL; + NeonGenOne64OpEnvFn *genenvfn = NULL; + + if (scalar) { + read_vec_element(s, tcg_op, rn, pass, size + 1); + } else { + read_vec_element(s, tcg_op, rn, pass, MO_64); + } + tcg_res[pass] = tcg_temp_new_i64(); + + switch (opcode) { + case 0x12: /* XTN, SQXTUN */ + { + static NeonGenOne64OpFn * const xtnfns[3] = { + gen_helper_neon_narrow_u8, + gen_helper_neon_narrow_u16, + tcg_gen_ext32u_i64, + }; + static NeonGenOne64OpEnvFn * const sqxtunfns[3] = { + gen_helper_neon_unarrow_sat8, + gen_helper_neon_unarrow_sat16, + gen_helper_neon_unarrow_sat32, + }; + if (u) { + genenvfn = sqxtunfns[size]; + } else { + genfn = xtnfns[size]; + } + break; + } + case 0x14: /* SQXTN, UQXTN */ + { + static NeonGenOne64OpEnvFn * const fns[3][2] = { + { gen_helper_neon_narrow_sat_s8, + gen_helper_neon_narrow_sat_u8 }, + { gen_helper_neon_narrow_sat_s16, + gen_helper_neon_narrow_sat_u16 }, + { gen_helper_neon_narrow_sat_s32, + gen_helper_neon_narrow_sat_u32 }, + }; + genenvfn = fns[size][u]; + break; + } + case 0x16: /* FCVTN, FCVTN2 */ + /* 32 bit to 16 bit or 64 bit to 32 bit float conversion */ + if (size == 2) { + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_vfp_fcvtsd(tmp, tcg_op, tcg_env); + tcg_gen_extu_i32_i64(tcg_res[pass], tmp); + } else { + TCGv_i32 tcg_lo = tcg_temp_new_i32(); + TCGv_i32 tcg_hi = tcg_temp_new_i32(); + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 ahp = get_ahp_flag(); + + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); + 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_lo, tcg_lo, tcg_hi, 16, 16); + tcg_gen_extu_i32_i64(tcg_res[pass], tcg_lo); + } + break; + case 0x36: /* BFCVTN, BFCVTN2 */ + { + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_bfcvt_pair(tmp, tcg_op, fpst); + tcg_gen_extu_i32_i64(tcg_res[pass], tmp); + } + break; + case 0x56: /* FCVTXN, FCVTXN2 */ + { + /* + * 64 bit to 32 bit float conversion + * with von Neumann rounding (round to odd) + */ + TCGv_i32 tmp = tcg_temp_new_i32(); + assert(size == 2); + gen_helper_fcvtx_f64_to_f32(tmp, tcg_op, tcg_env); + tcg_gen_extu_i32_i64(tcg_res[pass], tmp); + } + break; + default: + g_assert_not_reached(); + } + + if (genfn) { + genfn(tcg_res[pass], tcg_op); + } else if (genenvfn) { + genenvfn(tcg_res[pass], tcg_env, tcg_op); + } + } + + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], rd, destelt + pass, MO_32); + } + clear_vec_high(s, is_q, rd); +} + +/* AdvSIMD scalar two reg misc + * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 + * +-----+---+-----------+------+-----------+--------+-----+------+------+ + * | 0 1 | U | 1 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd | + * +-----+---+-----------+------+-----------+--------+-----+------+------+ + */ +static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 12, 5); + int size = extract32(insn, 22, 2); + bool u = extract32(insn, 29, 1); + bool is_fcvt = false; + int rmode; + TCGv_i32 tcg_rmode; + TCGv_ptr tcg_fpstatus; + + switch (opcode) { + case 0x7: /* SQABS / SQNEG */ + break; + case 0xa: /* CMLT */ + if (u) { + unallocated_encoding(s); + return; + } + /* fall through */ + case 0x8: /* CMGT, CMGE */ + case 0x9: /* CMEQ, CMLE */ + case 0xb: /* ABS, NEG */ + if (size != 3) { + unallocated_encoding(s); + return; + } + break; + case 0x12: /* SQXTUN */ + if (!u) { + unallocated_encoding(s); + return; + } + /* fall through */ + case 0x14: /* SQXTN, UQXTN */ + if (size == 3) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_2misc_narrow(s, true, opcode, u, false, size, rn, rd); + return; + case 0xc ... 0xf: + case 0x16 ... 0x1d: + case 0x1f: + /* Floating point: U, size[1] and opcode indicate operation; + * size[0] indicates single or double precision. + */ + opcode |= (extract32(size, 1, 1) << 5) | (u << 6); + size = extract32(size, 0, 1) ? 3 : 2; + switch (opcode) { + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ + handle_2misc_fcmp_zero(s, opcode, true, u, true, size, rn, rd); + return; + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ + { + bool is_signed = (opcode == 0x1d); + if (!fp_access_check(s)) { + return; + } + handle_simd_intfp_conv(s, rd, rn, 1, is_signed, 0, size); + return; + } + case 0x3d: /* FRECPE */ + case 0x3f: /* FRECPX */ + case 0x7d: /* FRSQRTE */ + if (!fp_access_check(s)) { + return; + } + handle_2misc_reciprocal(s, opcode, true, u, true, size, rn, rd); + return; + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + is_fcvt = true; + rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); + break; + case 0x1c: /* FCVTAS */ + case 0x5c: /* FCVTAU */ + /* TIEAWAY doesn't fit in the usual rounding mode encoding */ + is_fcvt = true; + rmode = FPROUNDING_TIEAWAY; + break; + case 0x56: /* FCVTXN, FCVTXN2 */ + if (size == 2) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_2misc_narrow(s, true, opcode, u, false, size - 1, rn, rd); + return; + default: + unallocated_encoding(s); + return; + } + break; + default: + case 0x3: /* USQADD / SUQADD */ + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + if (is_fcvt) { + tcg_fpstatus = fpstatus_ptr(FPST_FPCR); + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); + } else { + tcg_fpstatus = NULL; + tcg_rmode = NULL; + } + + if (size == 3) { + TCGv_i64 tcg_rn = read_fp_dreg(s, rn); + TCGv_i64 tcg_rd = tcg_temp_new_i64(); + + handle_2misc_64(s, opcode, u, tcg_rd, tcg_rn, tcg_rmode, tcg_fpstatus); + write_fp_dreg(s, rd, tcg_rd); + } else { + TCGv_i32 tcg_rn = tcg_temp_new_i32(); + TCGv_i32 tcg_rd = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_rn, rn, 0, size); + + switch (opcode) { + case 0x7: /* SQABS, SQNEG */ + { + NeonGenOneOpEnvFn *genfn; + static NeonGenOneOpEnvFn * const fns[3][2] = { + { gen_helper_neon_qabs_s8, gen_helper_neon_qneg_s8 }, + { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, + { gen_helper_neon_qabs_s32, gen_helper_neon_qneg_s32 }, + }; + genfn = fns[size][u]; + genfn(tcg_rd, tcg_env, tcg_rn); + break; + } + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_vfp_tosls(tcg_rd, tcg_rn, tcg_constant_i32(0), + tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_vfp_touls(tcg_rd, tcg_rn, tcg_constant_i32(0), + tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + + write_fp_sreg(s, rd, tcg_rd); + } + + if (is_fcvt) { + gen_restore_rmode(tcg_rmode, tcg_fpstatus); + } +} + +/* AdvSIMD shift by immediate + * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0 + * +---+---+---+-------------+------+------+--------+---+------+------+ + * | 0 | Q | U | 0 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd | + * +---+---+---+-------------+------+------+--------+---+------+------+ + */ +static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) +{ + int rd = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int opcode = extract32(insn, 11, 5); + int immb = extract32(insn, 16, 3); + int immh = extract32(insn, 19, 4); + bool is_u = extract32(insn, 29, 1); + bool is_q = extract32(insn, 30, 1); + + if (immh == 0) { + unallocated_encoding(s); + return; + } + + switch (opcode) { + case 0x1c: /* SCVTF / UCVTF */ + handle_simd_shift_intfp_conv(s, false, is_q, is_u, immh, immb, + opcode, rn, rd); + break; + case 0x1f: /* FCVTZS/ FCVTZU */ + handle_simd_shift_fpint_conv(s, false, is_q, is_u, immh, immb, rn, rd); + return; + default: + case 0x00: /* SSHR / USHR */ + case 0x02: /* SSRA / USRA (accumulate) */ + case 0x04: /* SRSHR / URSHR (rounding) */ + case 0x06: /* SRSRA / URSRA (accum + rounding) */ + case 0x08: /* SRI */ + case 0x0a: /* SHL / SLI */ + case 0x0c: /* SQSHLU */ + case 0x0e: /* SQSHL, UQSHL */ + case 0x10: /* SHRN / SQSHRUN */ + case 0x11: /* RSHRN / SQRSHRUN */ + case 0x12: /* SQSHRN / UQSHRN */ + case 0x13: /* SQRSHRN / UQRSHRN */ + case 0x14: /* SSHLL / USHLL */ + unallocated_encoding(s); + return; + } +} + +static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, + int size, int rn, int rd) +{ + /* Handle 2-reg-misc ops which are widening (so each size element + * in the source becomes a 2*size element in the destination. + * The only instruction like this is FCVTL. + */ + int pass; + + if (size == 3) { + /* 32 -> 64 bit fp conversion */ + TCGv_i64 tcg_res[2]; + int srcelt = is_q ? 2 : 0; + + for (pass = 0; pass < 2; pass++) { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + 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, tcg_env); + } + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], rd, pass, MO_64); + } + } else { + /* 16 -> 32 bit fp conversion */ + int srcelt = is_q ? 4 : 0; + TCGv_i32 tcg_res[4]; + TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); + TCGv_i32 ahp = get_ahp_flag(); + + for (pass = 0; pass < 4; pass++) { + tcg_res[pass] = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_res[pass], rn, srcelt + pass, MO_16); + gen_helper_vfp_fcvt_f16_to_f32(tcg_res[pass], tcg_res[pass], + fpst, ahp); + } + for (pass = 0; pass < 4; pass++) { + write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); + } + } +} + +static void handle_rev(DisasContext *s, int opcode, bool u, + bool is_q, int size, int rn, int rd) +{ + int op = (opcode << 1) | u; + int opsz = op + size; + int grp_size = 3 - opsz; + int dsize = is_q ? 128 : 64; + int i; + + if (opsz >= 3) { + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + if (size == 0) { + /* Special case bytes, use bswap op on each group of elements */ + int groups = dsize / (8 << grp_size); + + for (i = 0; i < groups; i++) { + TCGv_i64 tcg_tmp = tcg_temp_new_i64(); + + read_vec_element(s, tcg_tmp, rn, i, grp_size); + switch (grp_size) { + case MO_16: + tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp, TCG_BSWAP_IZ); + break; + case MO_32: + tcg_gen_bswap32_i64(tcg_tmp, tcg_tmp, TCG_BSWAP_IZ); + break; + case MO_64: + tcg_gen_bswap64_i64(tcg_tmp, tcg_tmp); + break; + default: + g_assert_not_reached(); + } + write_vec_element(s, tcg_tmp, rd, i, grp_size); + } + clear_vec_high(s, is_q, rd); + } else { + int revmask = (1 << grp_size) - 1; + int esize = 8 << size; + int elements = dsize / esize; + TCGv_i64 tcg_rn = tcg_temp_new_i64(); + 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 w = (e_rev * esize) / 64; + int o = (e_rev * esize) % 64; + + 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); + } +} + +static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, + bool is_q, int size, int rn, int rd) +{ + /* Implement the pairwise operations from 2-misc: + * SADDLP, UADDLP, SADALP, UADALP. + * These all add pairs of elements in the input to produce a + * double-width result element in the output (possibly accumulating). + */ + bool accum = (opcode == 0x6); + int maxpass = is_q ? 2 : 1; + int pass; + TCGv_i64 tcg_res[2]; + + if (size == 2) { + /* 32 + 32 -> 64 op */ + MemOp memop = size + (u ? 0 : MO_SIGN); + + for (pass = 0; pass < maxpass; pass++) { + TCGv_i64 tcg_op1 = tcg_temp_new_i64(); + TCGv_i64 tcg_op2 = tcg_temp_new_i64(); + + tcg_res[pass] = tcg_temp_new_i64(); + + read_vec_element(s, tcg_op1, rn, pass * 2, memop); + read_vec_element(s, tcg_op2, rn, pass * 2 + 1, memop); + tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2); + if (accum) { + read_vec_element(s, tcg_op1, rd, pass, MO_64); + tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_op1); + } + } + } else { + for (pass = 0; pass < maxpass; pass++) { + TCGv_i64 tcg_op = tcg_temp_new_i64(); + NeonGenOne64OpFn *genfn; + static NeonGenOne64OpFn * const fns[2][2] = { + { gen_helper_neon_addlp_s8, gen_helper_neon_addlp_u8 }, + { gen_helper_neon_addlp_s16, gen_helper_neon_addlp_u16 }, + }; + + genfn = fns[size][u]; + + tcg_res[pass] = tcg_temp_new_i64(); + + read_vec_element(s, tcg_op, rn, pass, MO_64); + genfn(tcg_res[pass], tcg_op); + + if (accum) { + read_vec_element(s, tcg_op, rd, pass, MO_64); + if (size == 0) { + gen_helper_neon_addl_u16(tcg_res[pass], + tcg_res[pass], tcg_op); + } else { + gen_helper_neon_addl_u32(tcg_res[pass], + tcg_res[pass], tcg_op); + } + } + } + } + if (!is_q) { + tcg_res[1] = tcg_constant_i64(0); + } + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], rd, pass, MO_64); + } +} + +static void handle_shll(DisasContext *s, bool is_q, int size, int rn, int rd) +{ + /* Implement SHLL and SHLL2 */ + int pass; + int part = is_q ? 2 : 0; + TCGv_i64 tcg_res[2]; + + for (pass = 0; pass < 2; pass++) { + static NeonGenWidenFn * const widenfns[3] = { + gen_helper_neon_widen_u8, + gen_helper_neon_widen_u16, + tcg_gen_extu_i32_i64, + }; + NeonGenWidenFn *widenfn = widenfns[size]; + TCGv_i32 tcg_op = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, part + pass, MO_32); + 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); + } + + for (pass = 0; pass < 2; pass++) { + write_vec_element(s, tcg_res[pass], rd, pass, MO_64); + } +} + +/* AdvSIMD two reg misc + * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 + * +---+---+---+-----------+------+-----------+--------+-----+------+------+ + * | 0 | Q | U | 0 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd | + * +---+---+---+-----------+------+-----------+--------+-----+------+------+ + */ +static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) +{ + int size = extract32(insn, 22, 2); + int opcode = extract32(insn, 12, 5); + bool u = extract32(insn, 29, 1); + bool is_q = extract32(insn, 30, 1); + int rn = extract32(insn, 5, 5); + int rd = extract32(insn, 0, 5); + bool need_fpstatus = false; + int rmode = -1; + TCGv_i32 tcg_rmode; + TCGv_ptr tcg_fpstatus; + + switch (opcode) { + case 0x0: /* REV64, REV32 */ + case 0x1: /* REV16 */ + handle_rev(s, opcode, u, is_q, size, rn, rd); + return; + case 0x5: /* CNT, NOT, RBIT */ + if (u && size == 0) { + /* NOT */ + break; + } else if (u && size == 1) { + /* RBIT */ + break; + } else if (!u && size == 0) { + /* CNT */ + break; + } + unallocated_encoding(s); + return; + case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ + case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ + if (size == 3) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + + handle_2misc_narrow(s, false, opcode, u, is_q, size, rn, rd); + return; + case 0x4: /* CLS, CLZ */ + if (size == 3) { + unallocated_encoding(s); + return; + } + break; + case 0x2: /* SADDLP, UADDLP */ + case 0x6: /* SADALP, UADALP */ + if (size == 3) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_2misc_pairwise(s, opcode, u, is_q, size, rn, rd); + return; + case 0x13: /* SHLL, SHLL2 */ + if (u == 0 || size == 3) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_shll(s, is_q, size, rn, rd); + return; + case 0xa: /* CMLT */ + if (u == 1) { + unallocated_encoding(s); + return; + } + /* fall through */ + case 0x8: /* CMGT, CMGE */ + case 0x9: /* CMEQ, CMLE */ + case 0xb: /* ABS, NEG */ + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x7: /* SQABS, SQNEG */ + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0xc ... 0xf: + case 0x16 ... 0x1f: + { + /* Floating point: U, size[1] and opcode indicate operation; + * size[0] indicates single or double precision. + */ + int is_double = extract32(size, 0, 1); + opcode |= (extract32(size, 1, 1) << 5) | (u << 6); + size = is_double ? 3 : 2; + switch (opcode) { + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ + { + bool is_signed = (opcode == 0x1d) ? true : false; + int elements = is_double ? 2 : is_q ? 4 : 2; + if (is_double && !is_q) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_simd_intfp_conv(s, rd, rn, elements, is_signed, 0, size); + return; + } + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + handle_2misc_fcmp_zero(s, opcode, false, u, is_q, size, rn, rd); + return; + case 0x7f: /* FSQRT */ + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + need_fpstatus = true; + rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x5c: /* FCVTAU */ + case 0x1c: /* FCVTAS */ + need_fpstatus = true; + rmode = FPROUNDING_TIEAWAY; + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x3c: /* URECPE */ + if (size == 3) { + unallocated_encoding(s); + return; + } + /* fall through */ + case 0x3d: /* FRECPE */ + case 0x7d: /* FRSQRTE */ + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_2misc_reciprocal(s, opcode, false, u, is_q, size, rn, rd); + return; + case 0x56: /* FCVTXN, FCVTXN2 */ + if (size == 2) { + unallocated_encoding(s); + return; + } + /* fall through */ + case 0x16: /* FCVTN, FCVTN2 */ + /* handle_2misc_narrow does a 2*size -> size operation, but these + * instructions encode the source size rather than dest size. + */ + if (!fp_access_check(s)) { + return; + } + handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); + return; + case 0x36: /* BFCVTN, BFCVTN2 */ + if (!dc_isar_feature(aa64_bf16, s) || size != 2) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); + return; + case 0x17: /* FCVTL, FCVTL2 */ + if (!fp_access_check(s)) { + return; + } + handle_2misc_widening(s, opcode, is_q, size, rn, rd); + return; + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); + /* fall through */ + case 0x59: /* FRINTX */ + case 0x79: /* FRINTI */ + need_fpstatus = true; + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x58: /* FRINTA */ + rmode = FPROUNDING_TIEAWAY; + need_fpstatus = true; + if (size == 3 && !is_q) { + unallocated_encoding(s); + return; + } + break; + case 0x7c: /* URSQRTE */ + if (size == 3) { + unallocated_encoding(s); + return; + } + break; + case 0x1e: /* FRINT32Z */ + case 0x1f: /* FRINT64Z */ + rmode = FPROUNDING_ZERO; + /* fall through */ + case 0x5e: /* FRINT32X */ + case 0x5f: /* FRINT64X */ + need_fpstatus = true; + if ((size == 3 && !is_q) || !dc_isar_feature(aa64_frint, s)) { + unallocated_encoding(s); + return; + } + break; + default: + unallocated_encoding(s); + return; + } + break; + } + default: + case 0x3: /* SUQADD, USQADD */ + unallocated_encoding(s); + return; + } + + if (!fp_access_check(s)) { + return; + } + + if (need_fpstatus || rmode >= 0) { + tcg_fpstatus = fpstatus_ptr(FPST_FPCR); + } else { + tcg_fpstatus = NULL; + } + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); + } else { + tcg_rmode = NULL; + } + + switch (opcode) { + case 0x5: + if (u && size == 0) { /* NOT */ + gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_not, 0); + return; + } + break; + case 0x8: /* CMGT, CMGE */ + if (u) { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cge0, size); + } else { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cgt0, size); + } + return; + case 0x9: /* CMEQ, CMLE */ + if (u) { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cle0, size); + } else { + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_ceq0, size); + } + return; + case 0xa: /* CMLT */ + gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clt0, size); + return; + case 0xb: + if (u) { /* ABS, NEG */ + gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_neg, size); + } else { + gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_abs, size); + } + return; + } + + if (size == 3) { + /* All 64-bit element operations can be shared with scalar 2misc */ + int pass; + + /* Coverity claims (size == 3 && !is_q) has been eliminated + * from all paths leading to here. + */ + tcg_debug_assert(is_q); + for (pass = 0; pass < 2; pass++) { + TCGv_i64 tcg_op = tcg_temp_new_i64(); + TCGv_i64 tcg_res = tcg_temp_new_i64(); + + read_vec_element(s, tcg_op, rn, pass, MO_64); + + handle_2misc_64(s, opcode, u, tcg_res, tcg_op, + tcg_rmode, tcg_fpstatus); + + write_vec_element(s, tcg_res, rd, pass, MO_64); + } + } else { + int pass; + + for (pass = 0; pass < (is_q ? 4 : 2); pass++) { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, pass, MO_32); + + if (size == 2) { + /* Special cases for 32 bit elements */ + switch (opcode) { + case 0x4: /* CLS */ + if (u) { + tcg_gen_clzi_i32(tcg_res, tcg_op, 32); + } else { + tcg_gen_clrsb_i32(tcg_res, tcg_op); + } + break; + case 0x7: /* SQABS, SQNEG */ + if (u) { + gen_helper_neon_qneg_s32(tcg_res, tcg_env, tcg_op); + } else { + gen_helper_neon_qabs_s32(tcg_res, tcg_env, tcg_op); + } + break; + case 0x2f: /* FABS */ + gen_vfp_abss(tcg_res, tcg_op); + break; + case 0x6f: /* FNEG */ + gen_vfp_negs(tcg_res, tcg_op); + break; + case 0x7f: /* FSQRT */ + gen_helper_vfp_sqrts(tcg_res, tcg_op, tcg_env); + break; + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_vfp_tosls(tcg_res, tcg_op, + tcg_constant_i32(0), tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_vfp_touls(tcg_res, tcg_op, + tcg_constant_i32(0), tcg_fpstatus); + break; + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + gen_helper_rints(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x59: /* FRINTX */ + gen_helper_rints_exact(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x7c: /* URSQRTE */ + gen_helper_rsqrte_u32(tcg_res, tcg_op); + break; + case 0x1e: /* FRINT32Z */ + case 0x5e: /* FRINT32X */ + gen_helper_frint32_s(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x1f: /* FRINT64Z */ + case 0x5f: /* FRINT64X */ + gen_helper_frint64_s(tcg_res, tcg_op, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + } else { + /* Use helpers for 8 and 16 bit elements */ + switch (opcode) { + case 0x5: /* CNT, RBIT */ + /* For these two insns size is part of the opcode specifier + * (handled earlier); they always operate on byte elements. + */ + if (u) { + gen_helper_neon_rbit_u8(tcg_res, tcg_op); + } else { + gen_helper_neon_cnt_u8(tcg_res, tcg_op); + } + break; + case 0x7: /* SQABS, SQNEG */ + { + NeonGenOneOpEnvFn *genfn; + static NeonGenOneOpEnvFn * const fns[2][2] = { + { gen_helper_neon_qabs_s8, gen_helper_neon_qneg_s8 }, + { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, + }; + genfn = fns[size][u]; + genfn(tcg_res, tcg_env, tcg_op); + break; + } + case 0x4: /* CLS, CLZ */ + if (u) { + if (size == 0) { + gen_helper_neon_clz_u8(tcg_res, tcg_op); + } else { + gen_helper_neon_clz_u16(tcg_res, tcg_op); + } + } else { + if (size == 0) { + gen_helper_neon_cls_s8(tcg_res, tcg_op); + } else { + gen_helper_neon_cls_s16(tcg_res, tcg_op); + } + } + break; + default: + g_assert_not_reached(); + } + } + + write_vec_element_i32(s, tcg_res, rd, pass, MO_32); + } + } + clear_vec_high(s, is_q, rd); + + if (tcg_rmode) { + gen_restore_rmode(tcg_rmode, tcg_fpstatus); + } +} + +/* AdvSIMD [scalar] two register miscellaneous (FP16) + * + * 31 30 29 28 27 24 23 22 21 17 16 12 11 10 9 5 4 0 + * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ + * | 0 | Q | U | S | 1 1 1 0 | a | 1 1 1 1 0 0 | opcode | 1 0 | Rn | Rd | + * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ + * mask: 1000 1111 0111 1110 0000 1100 0000 0000 0x8f7e 0c00 + * val: 0000 1110 0111 1000 0000 1000 0000 0000 0x0e78 0800 + * + * This actually covers two groups where scalar access is governed by + * bit 28. A bunch of the instructions (float to integral) only exist + * in the vector form and are un-allocated for the scalar decode. Also + * in the scalar decode Q is always 1. + */ +static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) +{ + int fpop, opcode, a, u; + int rn, rd; + bool is_q; + bool is_scalar; + bool only_in_vector = false; + + int pass; + TCGv_i32 tcg_rmode = NULL; + TCGv_ptr tcg_fpstatus = NULL; + bool need_fpst = true; + int rmode = -1; + + if (!dc_isar_feature(aa64_fp16, s)) { + unallocated_encoding(s); + return; + } + + rd = extract32(insn, 0, 5); + rn = extract32(insn, 5, 5); + + a = extract32(insn, 23, 1); + u = extract32(insn, 29, 1); + is_scalar = extract32(insn, 28, 1); + is_q = extract32(insn, 30, 1); + + opcode = extract32(insn, 12, 5); + fpop = deposit32(opcode, 5, 1, a); + fpop = deposit32(fpop, 6, 1, u); + + switch (fpop) { + case 0x1d: /* SCVTF */ + case 0x5d: /* UCVTF */ + { + int elements; + + if (is_scalar) { + elements = 1; + } else { + elements = (is_q ? 8 : 4); + } + + if (!fp_access_check(s)) { + return; + } + handle_simd_intfp_conv(s, rd, rn, elements, !u, 0, MO_16); + return; + } + break; + case 0x2c: /* FCMGT (zero) */ + case 0x2d: /* FCMEQ (zero) */ + case 0x2e: /* FCMLT (zero) */ + case 0x6c: /* FCMGE (zero) */ + case 0x6d: /* FCMLE (zero) */ + handle_2misc_fcmp_zero(s, fpop, is_scalar, 0, is_q, MO_16, rn, rd); + return; + case 0x3d: /* FRECPE */ + case 0x3f: /* FRECPX */ + break; + case 0x18: /* FRINTN */ + only_in_vector = true; + rmode = FPROUNDING_TIEEVEN; + break; + case 0x19: /* FRINTM */ + only_in_vector = true; + rmode = FPROUNDING_NEGINF; + break; + case 0x38: /* FRINTP */ + only_in_vector = true; + rmode = FPROUNDING_POSINF; + break; + case 0x39: /* FRINTZ */ + only_in_vector = true; + rmode = FPROUNDING_ZERO; + break; + case 0x58: /* FRINTA */ + only_in_vector = true; + rmode = FPROUNDING_TIEAWAY; + break; + case 0x59: /* FRINTX */ + case 0x79: /* FRINTI */ + only_in_vector = true; + /* current rounding mode */ + break; + case 0x1a: /* FCVTNS */ + rmode = FPROUNDING_TIEEVEN; + break; + case 0x1b: /* FCVTMS */ + rmode = FPROUNDING_NEGINF; + break; + case 0x1c: /* FCVTAS */ + rmode = FPROUNDING_TIEAWAY; + break; + case 0x3a: /* FCVTPS */ + rmode = FPROUNDING_POSINF; + break; + case 0x3b: /* FCVTZS */ + rmode = FPROUNDING_ZERO; + break; + case 0x5a: /* FCVTNU */ + rmode = FPROUNDING_TIEEVEN; + break; + case 0x5b: /* FCVTMU */ + rmode = FPROUNDING_NEGINF; + break; + case 0x5c: /* FCVTAU */ + rmode = FPROUNDING_TIEAWAY; + break; + case 0x7a: /* FCVTPU */ + rmode = FPROUNDING_POSINF; + break; + case 0x7b: /* FCVTZU */ + rmode = FPROUNDING_ZERO; + break; + case 0x2f: /* FABS */ + case 0x6f: /* FNEG */ + need_fpst = false; + break; + case 0x7d: /* FRSQRTE */ + case 0x7f: /* FSQRT (vector) */ + break; + default: + unallocated_encoding(s); + return; + } + + + /* Check additional constraints for the scalar encoding */ + if (is_scalar) { + if (!is_q) { + unallocated_encoding(s); + return; + } + /* FRINTxx is only in the vector form */ + if (only_in_vector) { + unallocated_encoding(s); + return; + } + } + + if (!fp_access_check(s)) { + return; + } + + if (rmode >= 0 || need_fpst) { + tcg_fpstatus = fpstatus_ptr(FPST_FPCR_F16); + } + + if (rmode >= 0) { + tcg_rmode = gen_set_rmode(rmode, tcg_fpstatus); + } + + if (is_scalar) { + TCGv_i32 tcg_op = read_fp_hreg(s, rn); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + switch (fpop) { + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x3d: /* FRECPE */ + gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x3f: /* FRECPX */ + gen_helper_frecpx_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x6f: /* FNEG */ + tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); + break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + + /* limit any sign extension going on */ + tcg_gen_andi_i32(tcg_res, tcg_res, 0xffff); + write_fp_sreg(s, rd, tcg_res); + } else { + for (pass = 0; pass < (is_q ? 8 : 4); pass++) { + TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_res = tcg_temp_new_i32(); + + read_vec_element_i32(s, tcg_op, rn, pass, MO_16); + + switch (fpop) { + case 0x1a: /* FCVTNS */ + case 0x1b: /* FCVTMS */ + case 0x1c: /* FCVTAS */ + case 0x3a: /* FCVTPS */ + case 0x3b: /* FCVTZS */ + gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x3d: /* FRECPE */ + gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x5a: /* FCVTNU */ + case 0x5b: /* FCVTMU */ + case 0x5c: /* FCVTAU */ + case 0x7a: /* FCVTPU */ + case 0x7b: /* FCVTZU */ + gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x18: /* FRINTN */ + case 0x19: /* FRINTM */ + case 0x38: /* FRINTP */ + case 0x39: /* FRINTZ */ + case 0x58: /* FRINTA */ + case 0x79: /* FRINTI */ + gen_helper_advsimd_rinth(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x59: /* FRINTX */ + gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x2f: /* FABS */ + tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); + break; + case 0x6f: /* FNEG */ + tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); + break; + case 0x7d: /* FRSQRTE */ + gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x7f: /* FSQRT */ + gen_helper_sqrt_f16(tcg_res, tcg_op, tcg_fpstatus); + break; + default: + g_assert_not_reached(); + } + + write_vec_element_i32(s, tcg_res, rd, pass, MO_16); + } + + clear_vec_high(s, is_q, rd); + } + + if (tcg_rmode) { + gen_restore_rmode(tcg_rmode, tcg_fpstatus); + } +} + +/* C3.6 Data processing - SIMD, inc Crypto + * + * As the decode gets a little complex we are using a table based + * approach for this part of the decode. + */ +static const AArch64DecodeTable data_proc_simd[] = { + /* pattern , mask , fn */ + { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, + { 0x0f000400, 0x9f800400, disas_simd_shift_imm }, + { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, + { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, + { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, + { 0x00000000, 0x00000000, NULL } +}; + +static void disas_data_proc_simd(DisasContext *s, uint32_t insn) +{ + /* Note that this is called with all non-FP cases from + * table C3-6 so it must UNDEF for entries not specifically + * allocated to instructions in that table. + */ + AArch64DecodeFn *fn = lookup_disas_fn(&data_proc_simd[0], insn); + if (fn) { + fn(s, insn); + } else { + unallocated_encoding(s); + } +} + +/* C3.6 Data processing - SIMD and floating point */ +static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) +{ + if (extract32(insn, 28, 1) == 1 && extract32(insn, 30, 1) == 0) { + disas_data_proc_fp(s, insn); + } else { + /* SIMD, including crypto */ + disas_data_proc_simd(s, 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; +} + +/** + * btype_destination_ok: + * @insn: The instruction at the branch destination + * @bt: SCTLR_ELx.BT + * @btype: PSTATE.BTYPE, and is non-zero + * + * On a guarded page, there are a limited number of insns + * that may be present at the branch target: + * - branch target identifiers, + * - paciasp, pacibsp, + * - BRK insn + * - HLT insn + * Anything else causes a Branch Target Exception. + * + * Return true if the branch is compatible, false to raise BTITRAP. + */ +static bool btype_destination_ok(uint32_t insn, bool bt, int btype) +{ + if ((insn & 0xfffff01fu) == 0xd503201fu) { + /* HINT space */ + switch (extract32(insn, 5, 7)) { + case 0b011001: /* PACIASP */ + case 0b011011: /* PACIBSP */ + /* + * If SCTLR_ELx.BT, then PACI*SP are not compatible + * with btype == 3. Otherwise all btype are ok. + */ + return !bt || btype != 3; + case 0b100000: /* BTI */ + /* Not compatible with any btype. */ + return false; + case 0b100010: /* BTI c */ + /* Not compatible with btype == 3 */ + return btype != 3; + case 0b100100: /* BTI j */ + /* Not compatible with btype == 2 */ + return btype != 2; + case 0b100110: /* BTI jc */ + /* Compatible with any btype. */ + return true; + } + } else { + switch (insn & 0xffe0001fu) { + case 0xd4200000u: /* BRK */ + case 0xd4400000u: /* HLT */ + /* Give priority to the breakpoint exception. */ + return true; + } + } + 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(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; + dc->thumb = false; + dc->sctlr_b = 0; + dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; + dc->condexec_mask = 0; + dc->condexec_cond = 0; + core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); + dc->mmu_idx = core_to_aa64_mmu_idx(core_mmu_idx); + dc->tbii = EX_TBFLAG_A64(tb_flags, TBII); + dc->tbid = EX_TBFLAG_A64(tb_flags, TBID); + dc->tcma = EX_TBFLAG_A64(tb_flags, TCMA); + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); +#if !defined(CONFIG_USER_ONLY) + dc->user = (dc->current_el == 0); +#endif + 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[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 + * that anything that can make us go to SS_ACTIVE == 1 must end the TB; + * this happens anyway because those changes are all system register or + * PSTATE writes). + * SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending) + * emit code for one insn + * emit code to clear PSTATE.SS + * emit code to generate software step exception for completed step + * end TB (as usual for having generated an exception) + * SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending) + * emit code to generate a software step exception + * end the TB + */ + dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE); + dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS); + dc->is_ldex = false; + + /* Bound the number of insns to execute to those left on the page. */ + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + + /* If architectural single step active, limit to 1. */ + if (dc->ss_active) { + bound = 1; + } + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} + +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; + + 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(cpu); + uint64_t pc = s->base.pc_next; + uint32_t insn; + + /* Singlestep exceptions have the highest priority. */ + if (s->ss_active && !s->pstate_ss) { + /* Singlestep state is Active-pending. + * If we're in this state at the start of a TB then either + * a) we just took an exception to an EL which is being debugged + * and this is the first insn in the exception handler + * b) debug exceptions were masked and we just unmasked them + * without changing EL (eg by clearing PSTATE.D) + * In either case we're going to take a swstep exception in the + * "did not step an insn" case, and so the syndrome ISV and EX + * bits should be zero. + */ + assert(s->base.num_insns == 1); + gen_swstep_exception(s, 0, 0); + s->base.is_jmp = DISAS_NORETURN; + s->base.pc_next = pc + 4; + return; + } + + if (pc & 3) { + /* + * PC alignment fault. This has priority over the instruction abort + * that we would receive from a translation fault via arm_ldl_code. + * This should only be possible after an indirect branch, at the + * start of the TB. + */ + assert(s->base.num_insns == 1); + 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; + } + + s->pc_curr = pc; + insn = arm_ldl_code(env, &s->base, pc, s->sctlr_b); + s->insn = insn; + s->base.pc_next = pc + 4; + + s->fp_access_checked = false; + s->sve_access_checked = false; + + if (s->pstate_il) { + /* + * Illegal execution state. This has priority over BTI + * exceptions, but comes after instruction abort exceptions. + */ + gen_exception_insn(s, 0, EXCP_UDEF, syn_illegalstate()); + return; + } + + if (dc_isar_feature(aa64_bti, s)) { + if (s->base.num_insns == 1) { + /* First insn can have btype set to non-zero. */ + tcg_debug_assert(s->btype >= 0); + + /* + * Note that the Branch Target Exception has fairly high + * priority -- below debugging exceptions but above most + * everything else. This allows us to handle this now + * instead of waiting until the insn is otherwise decoded. + * + * We can check all but the guarded page check here; + * defer the latter to a helper. + */ + if (s->btype != 0 + && !btype_destination_ok(insn, s->bt, s->btype)) { + gen_helper_guarded_page_check(tcg_env); + } + } else { + /* Not the first insn: btype must be 0. */ + tcg_debug_assert(s->btype == 0); + } + } + + s->is_nonstreaming = false; + if (s->sme_trap_nonstreaming) { + disas_sme_fa64(s, insn); + } + + 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. + * Note that we set btype == -1 when the insn sets btype. + */ + if (s->btype > 0 && s->base.is_jmp != DISAS_NORETURN) { + reset_btype(s); + } +} + +static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (unlikely(dc->ss_active)) { + /* Note that this means single stepping WFI doesn't halt the CPU. + * For conditional branch insns this is harmless unreachable code as + * gen_goto_tb() has already handled emitting the debug exception + * (and thus a tb-jump is not possible when singlestepping). + */ + switch (dc->base.is_jmp) { + default: + gen_a64_update_pc(dc, 4); + /* fall through */ + case DISAS_EXIT: + case DISAS_JUMP: + gen_step_complete_exception(dc); + break; + case DISAS_NORETURN: + break; + } + } else { + switch (dc->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: + gen_goto_tb(dc, 1, 4); + break; + default: + case DISAS_UPDATE_EXIT: + gen_a64_update_pc(dc, 4); + /* fall through */ + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_UPDATE_NOCHAIN: + gen_a64_update_pc(dc, 4); + /* fall through */ + case DISAS_JUMP: + tcg_gen_lookup_and_goto_ptr(); + break; + case DISAS_NORETURN: + case DISAS_SWI: + break; + case DISAS_WFE: + gen_a64_update_pc(dc, 4); + gen_helper_wfe(tcg_env); + break; + case DISAS_YIELD: + 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_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. + */ + tcg_gen_exit_tb(NULL, 0); + break; + } + } +} + +const TranslatorOps aarch64_translator_ops = { + .init_disas_context = aarch64_tr_init_disas_context, + .tb_start = aarch64_tr_tb_start, + .insn_start = aarch64_tr_insn_start, + .translate_insn = aarch64_tr_translate_insn, + .tb_stop = aarch64_tr_tb_stop, +}; diff --git a/target/arm/translate-a64.h b/target/arm/tcg/translate-a64.h similarity index 83% rename from target/arm/translate-a64.h rename to target/arm/tcg/translate-a64.h index ad3762d1ac..0fcf7cb63a 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); @@ -31,6 +28,8 @@ bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, 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) @@ -52,9 +51,9 @@ static inline bool sme_smza_enabled_check(DisasContext *s) 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 @@ -118,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; } @@ -182,7 +181,7 @@ static inline int pred_gvec_reg_size(DisasContext *s) static inline TCGv_ptr pred_full_reg_ptr(DisasContext *s, int regno) { TCGv_ptr ret = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ret, cpu_env, pred_full_reg_offset(s, regno)); + tcg_gen_addi_ptr(ret, tcg_env, pred_full_reg_offset(s, regno)); return ret; } @@ -194,6 +193,24 @@ void gen_gvec_rax1(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, 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_gvec_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz); +void gen_gvec_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, + uint32_t a, uint32_t oprsz, uint32_t maxsz); + +void gen_suqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_suqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b); +void gen_gvec_suqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); + +void gen_usqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_usqadd_d(TCGv_i64 res, TCGv_i64 qc, TCGv_i64 a, TCGv_i64 b); +void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + 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); 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 5df7d46120..b92773b4af 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); @@ -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: @@ -338,7 +332,7 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, if (dc_isar_feature(aa32_mve, s)) { /* QC is only present for MVE; otherwise RES0 */ TCGv_i32 qc = tcg_temp_new_i32(); - tcg_gen_andi_i32(qc, tmp, FPCR_QC); + tcg_gen_andi_i32(qc, tmp, FPSR_QC); /* * The 4 vfp.qc[] fields need only be "zero" vs "non-zero"; * here writing the same value into all elements is simplest. @@ -346,12 +340,11 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, tcg_gen_gvec_dup_i32(MO_32, offsetof(CPUARMState, vfp.qc), 16, 16, qc); } - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); - fpscr = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); - tcg_gen_andi_i32(fpscr, fpscr, ~FPCR_NZCV_MASK); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCV_MASK); + fpscr = load_cpu_field_low32(vfp.fpsr); + tcg_gen_andi_i32(fpscr, fpscr, ~FPSR_NZCV_MASK); tcg_gen_or_i32(fpscr, fpscr, tmp); - store_cpu_field(fpscr, vfp.xregs[ARM_VFP_FPSCR]); - tcg_temp_free_i32(tmp); + store_cpu_field_low32(fpscr, vfp.fpsr); break; } case ARM_VFP_FPCXT_NS: @@ -397,11 +390,9 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno, tcg_gen_deposit_i32(control, control, sfpa, 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); + tcg_gen_andi_i32(tmp, tmp, ~FPSR_NZCV_MASK); + 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,13 +451,13 @@ 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); - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCVQC_MASK); + gen_helper_vfp_get_fpscr(tmp, tcg_env); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCVQC_MASK); storefn(s, opaque, tmp, true); break; case QEMU_VFP_FPSCR_NZCV: @@ -475,8 +465,8 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno, * Read just NZCV; this is a special case to avoid the * helper call for the "VMRS to CPSR.NZCV" insn. */ - tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); + tmp = load_cpu_field_low32(vfp.fpsr); + tcg_gen_andi_i32(tmp, tmp, FPSR_NZCV_MASK); storefn(s, opaque, tmp, true); break; case ARM_VFP_FPCXT_S: @@ -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); - tcg_gen_andi_i32(tmp, tmp, ~FPCR_NZCV_MASK); + gen_helper_vfp_get_fpscr(tmp, tcg_env); + tcg_gen_andi_i32(tmp, tmp, ~FPSR_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); @@ -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); - tcg_gen_andi_i32(tmp, fpscr, ~FPCR_NZCV_MASK); + gen_helper_vfp_get_fpscr(fpscr, tcg_env); + tcg_gen_andi_i32(tmp, fpscr, ~FPSR_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; } 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 db7ea3f603..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; } @@ -177,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, @@ -189,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; @@ -241,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; } @@ -340,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; } @@ -409,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; @@ -505,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; } @@ -533,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; @@ -602,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. @@ -630,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; } @@ -820,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; @@ -1075,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; } @@ -1172,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) || @@ -1199,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; } @@ -1285,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) || @@ -1305,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; @@ -1408,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); @@ -1425,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) { @@ -1442,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; @@ -1466,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)) { @@ -1488,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; } @@ -1542,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; @@ -1615,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; @@ -1722,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; } @@ -1866,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; } @@ -1896,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; } @@ -1932,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; } @@ -2000,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); @@ -2033,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); @@ -2136,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; } @@ -2201,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; } @@ -2232,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.) */ @@ -2275,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.) */ @@ -2297,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 83% rename from target/arm/translate-neon.c rename to target/arm/tcg/translate-neon.c index 4016339d46..9c8829ad7d 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(); @@ -152,6 +148,37 @@ static bool do_neon_ddda(DisasContext *s, int q, int vd, int vn, int vm, return true; } +static bool do_neon_ddda_env(DisasContext *s, int q, int vd, int vn, int vm, + int data, gen_helper_gvec_4_ptr *fn_gvec) +{ + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + return false; + } + + /* + * UNDEF accesses to odd registers for each bit of Q. + * Q will be 0b111 for all Q-reg instructions, otherwise + * when we have mixed Q- and D-reg inputs. + */ + if (((vd & 1) * 4 | (vn & 1) * 2 | (vm & 1)) & q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + int opr_sz = q ? 16 : 8; + tcg_gen_gvec_4_ptr(vfp_reg_offset(1, vd), + vfp_reg_offset(1, vn), + vfp_reg_offset(1, vm), + vfp_reg_offset(1, vd), + tcg_env, + opr_sz, opr_sz, data, fn_gvec); + return true; +} + static bool do_neon_ddda_fpst(DisasContext *s, int q, int vd, int vn, int vm, int data, ARMFPStatusFlavour fp_flavour, gen_helper_gvec_4_ptr *fn_gvec_ptr) @@ -182,7 +209,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 +262,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; } @@ -272,8 +297,8 @@ static bool trans_VDOT_b16(DisasContext *s, arg_VDOT_b16 *a) if (!dc_isar_feature(aa32_bf16, s)) { return false; } - return do_neon_ddda(s, a->q * 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_bfdot); + return do_neon_ddda_env(s, a->q * 7, a->vd, a->vn, a->vm, 0, + gen_helper_gvec_bfdot); } static bool trans_VFML(DisasContext *s, arg_VFML *a) @@ -302,7 +327,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; } @@ -366,8 +391,8 @@ static bool trans_VDOT_b16_scal(DisasContext *s, arg_VDOT_b16_scal *a) if (!dc_isar_feature(aa32_bf16, s)) { return false; } - return do_neon_ddda(s, a->q * 6, a->vd, a->vn, a->vm, a->index, - gen_helper_gvec_bfdot_idx); + return do_neon_ddda_env(s, a->q * 6, a->vd, a->vn, a->vm, a->index, + gen_helper_gvec_bfdot_idx); } static bool trans_VFML_scalar(DisasContext *s, arg_VFML_scalar *a) @@ -396,7 +421,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 +458,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 +560,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; @@ -630,8 +652,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); @@ -751,8 +771,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); @@ -807,6 +825,12 @@ DO_3SAME(VQADD_S, gen_gvec_sqadd_qc) DO_3SAME(VQADD_U, gen_gvec_uqadd_qc) DO_3SAME(VQSUB_S, gen_gvec_sqsub_qc) DO_3SAME(VQSUB_U, gen_gvec_uqsub_qc) +DO_3SAME(VRSHL_S, gen_gvec_srshl) +DO_3SAME(VRSHL_U, gen_gvec_urshl) +DO_3SAME(VQSHL_S, gen_neon_sqshl) +DO_3SAME(VQSHL_U, gen_neon_uqshl) +DO_3SAME(VQRSHL_S, gen_neon_sqrshl) +DO_3SAME(VQRSHL_U, gen_neon_uqrshl) /* These insns are all gvec_bitsel but with the inputs in various orders. */ #define DO_3SAME_BITSEL(INSN, O1, O2, O3) \ @@ -843,6 +867,17 @@ DO_3SAME_NO_SZ_3(VABD_S, gen_gvec_sabd) DO_3SAME_NO_SZ_3(VABA_S, gen_gvec_saba) DO_3SAME_NO_SZ_3(VABD_U, gen_gvec_uabd) DO_3SAME_NO_SZ_3(VABA_U, gen_gvec_uaba) +DO_3SAME_NO_SZ_3(VPADD, gen_gvec_addp) +DO_3SAME_NO_SZ_3(VPMAX_S, gen_gvec_smaxp) +DO_3SAME_NO_SZ_3(VPMIN_S, gen_gvec_sminp) +DO_3SAME_NO_SZ_3(VPMAX_U, gen_gvec_umaxp) +DO_3SAME_NO_SZ_3(VPMIN_U, gen_gvec_uminp) +DO_3SAME_NO_SZ_3(VHADD_S, gen_gvec_shadd) +DO_3SAME_NO_SZ_3(VHADD_U, gen_gvec_uhadd) +DO_3SAME_NO_SZ_3(VHSUB_S, gen_gvec_shsub) +DO_3SAME_NO_SZ_3(VHSUB_U, gen_gvec_uhsub) +DO_3SAME_NO_SZ_3(VRHADD_S, gen_gvec_srhadd) +DO_3SAME_NO_SZ_3(VRHADD_U, gen_gvec_urhadd) #define DO_3SAME_CMP(INSN, COND) \ static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ @@ -920,53 +955,8 @@ DO_SHA2(SHA256H, gen_helper_crypto_sha256h) DO_SHA2(SHA256H2, gen_helper_crypto_sha256h2) DO_SHA2(SHA256SU1, gen_helper_crypto_sha256su1) -#define DO_3SAME_64(INSN, FUNC) \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 op = { .fni8 = FUNC }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &op); \ - } \ - DO_3SAME(INSN, gen_##INSN##_3s) - -#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); \ - } \ - DO_3SAME_64(INSN, gen_##INSN##_elt) - -DO_3SAME_64(VRSHL_S64, gen_helper_neon_rshl_s64) -DO_3SAME_64(VRSHL_U64, gen_helper_neon_rshl_u64) -DO_3SAME_64_ENV(VQSHL_S64, gen_helper_neon_qshl_s64) -DO_3SAME_64_ENV(VQSHL_U64, gen_helper_neon_qshl_u64) -DO_3SAME_64_ENV(VQRSHL_S64, gen_helper_neon_qrshl_s64) -DO_3SAME_64_ENV(VQRSHL_U64, gen_helper_neon_qrshl_u64) - -#define DO_3SAME_32(INSN, FUNC) \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 ops[4] = { \ - { .fni4 = gen_helper_neon_##FUNC##8 }, \ - { .fni4 = gen_helper_neon_##FUNC##16 }, \ - { .fni4 = gen_helper_neon_##FUNC##32 }, \ - { 0 }, \ - }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &ops[vece]); \ - } \ - static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size > 2) { \ - return false; \ - } \ - return do_3same(s, a, gen_##INSN##_3s); \ - } - /* - * 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(). @@ -974,149 +964,15 @@ 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) \ - WRAP_ENV_FN(gen_##INSN##_tramp8, gen_helper_neon_##FUNC##8); \ - WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##16); \ - WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##32); \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 ops[4] = { \ - { .fni4 = gen_##INSN##_tramp8 }, \ - { .fni4 = gen_##INSN##_tramp16 }, \ - { .fni4 = gen_##INSN##_tramp32 }, \ - { 0 }, \ - }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &ops[vece]); \ - } \ - static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size > 2) { \ - return false; \ - } \ - return do_3same(s, a, gen_##INSN##_3s); \ - } - -DO_3SAME_32(VHADD_S, hadd_s) -DO_3SAME_32(VHADD_U, hadd_u) -DO_3SAME_32(VHSUB_S, hsub_s) -DO_3SAME_32(VHSUB_U, hsub_u) -DO_3SAME_32(VRHADD_S, rhadd_s) -DO_3SAME_32(VRHADD_U, rhadd_u) -DO_3SAME_32(VRSHL_S, rshl_s) -DO_3SAME_32(VRSHL_U, rshl_u) - -DO_3SAME_32_ENV(VQSHL_S, qshl_s) -DO_3SAME_32_ENV(VQSHL_U, qshl_u) -DO_3SAME_32_ENV(VQRSHL_S, qrshl_s) -DO_3SAME_32_ENV(VQRSHL_U, qrshl_u) - -static bool do_3same_pair(DisasContext *s, arg_3same *a, NeonGenTwoOpFn *fn) -{ - /* Operations handled pairwise 32 bits at a time */ - TCGv_i32 tmp, tmp2, tmp3; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { - return false; - } - - if (a->size == 3) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - assert(a->q == 0); /* enforced by decode patterns */ - - /* - * Note that we have to be careful not to clobber the source operands - * in the "vm == vd" case by storing the result of the first pass too - * early. Since Q is 0 there are always just two passes, so instead - * of a complicated loop over each pass we just unroll. - */ - tmp = tcg_temp_new_i32(); - tmp2 = tcg_temp_new_i32(); - tmp3 = tcg_temp_new_i32(); - - read_neon_element32(tmp, a->vn, 0, MO_32); - read_neon_element32(tmp2, a->vn, 1, MO_32); - fn(tmp, tmp, tmp2); - - read_neon_element32(tmp3, a->vm, 0, MO_32); - read_neon_element32(tmp2, a->vm, 1, MO_32); - fn(tmp3, tmp3, tmp2); - - 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; -} - -#define DO_3SAME_PAIR(INSN, func) \ - static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - static NeonGenTwoOpFn * const fns[] = { \ - gen_helper_neon_##func##8, \ - gen_helper_neon_##func##16, \ - gen_helper_neon_##func##32, \ - }; \ - if (a->size > 2) { \ - return false; \ - } \ - return do_3same_pair(s, a, fns[a->size]); \ - } - -/* 32-bit pairwise ops end up the same as the elementwise versions. */ -#define gen_helper_neon_pmax_s32 tcg_gen_smax_i32 -#define gen_helper_neon_pmax_u32 tcg_gen_umax_i32 -#define gen_helper_neon_pmin_s32 tcg_gen_smin_i32 -#define gen_helper_neon_pmin_u32 tcg_gen_umin_i32 -#define gen_helper_neon_padd_u32 tcg_gen_add_i32 - -DO_3SAME_PAIR(VPMAX_S, pmax_s) -DO_3SAME_PAIR(VPMIN_S, pmin_s) -DO_3SAME_PAIR(VPMAX_U, pmax_u) -DO_3SAME_PAIR(VPMIN_U, pmin_u) -DO_3SAME_PAIR(VPADD, padd_u) - #define DO_3SAME_VQDMULH(INSN, FUNC) \ - WRAP_ENV_FN(gen_##INSN##_tramp16, gen_helper_neon_##FUNC##_s16); \ - WRAP_ENV_FN(gen_##INSN##_tramp32, gen_helper_neon_##FUNC##_s32); \ - static void gen_##INSN##_3s(unsigned vece, uint32_t rd_ofs, \ - uint32_t rn_ofs, uint32_t rm_ofs, \ - uint32_t oprsz, uint32_t maxsz) \ - { \ - static const GVecGen3 ops[2] = { \ - { .fni4 = gen_##INSN##_tramp16 }, \ - { .fni4 = gen_##INSN##_tramp32 }, \ - }; \ - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, oprsz, maxsz, &ops[vece - 1]); \ - } \ static bool trans_##INSN##_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size != 1 && a->size != 2) { \ - return false; \ - } \ - return do_3same(s, a, gen_##INSN##_3s); \ - } + { return a->size >= 1 && a->size <= 2 && do_3same(s, a, FUNC); } -DO_3SAME_VQDMULH(VQDMULH, qdmulh) -DO_3SAME_VQDMULH(VQRDMULH, qrdmulh) +DO_3SAME_VQDMULH(VQDMULH, gen_gvec_sqdmulh_qc) +DO_3SAME_VQDMULH(VQRDMULH, gen_gvec_sqrdmulh_qc) #define WRAP_FP_GVEC(WRAPNAME, FPST, FUNC) \ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \ @@ -1126,7 +982,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) \ @@ -1161,6 +1016,9 @@ DO_3S_FP_GVEC(VFMA, gen_helper_gvec_vfma_s, gen_helper_gvec_vfma_h) DO_3S_FP_GVEC(VFMS, gen_helper_gvec_vfms_s, gen_helper_gvec_vfms_h) DO_3S_FP_GVEC(VRECPS, gen_helper_gvec_recps_nf_s, gen_helper_gvec_recps_nf_h) DO_3S_FP_GVEC(VRSQRTS, gen_helper_gvec_rsqrts_nf_s, gen_helper_gvec_rsqrts_nf_h) +DO_3S_FP_GVEC(VPADD, gen_helper_gvec_faddp_s, gen_helper_gvec_faddp_h) +DO_3S_FP_GVEC(VPMAX, gen_helper_gvec_fmaxp_s, gen_helper_gvec_fmaxp_h) +DO_3S_FP_GVEC(VPMIN, gen_helper_gvec_fminp_s, gen_helper_gvec_fminp_h) WRAP_FP_GVEC(gen_VMAXNM_fp32_3s, FPST_STD, gen_helper_gvec_fmaxnum_s) WRAP_FP_GVEC(gen_VMAXNM_fp16_3s, FPST_STD_F16, gen_helper_gvec_fmaxnum_h) @@ -1197,59 +1055,6 @@ static bool trans_VMINNM_fp_3s(DisasContext *s, arg_3same *a) return do_3same(s, a, gen_VMINNM_fp32_3s); } -static bool do_3same_fp_pair(DisasContext *s, arg_3same *a, - gen_helper_gvec_3_ptr *fn) -{ - /* FP pairwise operations */ - TCGv_ptr fpstatus; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - assert(a->q == 0); /* enforced by decode patterns */ - - - fpstatus = fpstatus_ptr(a->size == MO_16 ? FPST_STD_F16 : FPST_STD); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, a->vd), - vfp_reg_offset(1, a->vn), - vfp_reg_offset(1, a->vm), - fpstatus, 8, 8, 0, fn); - tcg_temp_free_ptr(fpstatus); - - return true; -} - -/* - * For all the functions using this macro, size == 1 means fp16, - * which is an architecture extension we don't implement yet. - */ -#define DO_3S_FP_PAIR(INSN,FUNC) \ - static bool trans_##INSN##_fp_3s(DisasContext *s, arg_3same *a) \ - { \ - if (a->size == MO_16) { \ - if (!dc_isar_feature(aa32_fp16_arith, s)) { \ - return false; \ - } \ - return do_3same_fp_pair(s, a, FUNC##h); \ - } \ - return do_3same_fp_pair(s, a, FUNC##s); \ - } - -DO_3S_FP_PAIR(VPADD, gen_helper_neon_padd) -DO_3S_FP_PAIR(VPMAX, gen_helper_neon_pmax) -DO_3S_FP_PAIR(VPMIN, gen_helper_neon_pmin) - static bool do_vector_2sh(DisasContext *s, arg_2reg_shift *a, GVecGen2iFn *fn) { /* Handle a 2-reg-shift insn which can be vectorized. */ @@ -1294,146 +1099,18 @@ DO_2SH(VRSHR_S, gen_gvec_srshr) DO_2SH(VRSHR_U, gen_gvec_urshr) DO_2SH(VRSRA_S, gen_gvec_srsra) DO_2SH(VRSRA_U, gen_gvec_ursra) - -static bool trans_VSHR_S_2sh(DisasContext *s, arg_2reg_shift *a) -{ - /* Signed shift out of range results in all-sign-bits */ - a->shift = MIN(a->shift, (8 << a->size) - 1); - return do_vector_2sh(s, a, tcg_gen_gvec_sari); -} - -static void gen_zero_rd_2sh(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t oprsz, uint32_t maxsz) -{ - tcg_gen_gvec_dup_imm(vece, rd_ofs, oprsz, maxsz, 0); -} - -static bool trans_VSHR_U_2sh(DisasContext *s, arg_2reg_shift *a) -{ - /* Shift out of range is architecturally valid and results in zero. */ - if (a->shift >= (8 << a->size)) { - return do_vector_2sh(s, a, gen_zero_rd_2sh); - } else { - return do_vector_2sh(s, a, tcg_gen_gvec_shri); - } -} - -static bool do_2shift_env_64(DisasContext *s, arg_2reg_shift *a, - NeonGenTwo64OpEnvFn *fn) -{ - /* - * 2-reg-and-shift operations, size == 3 case, where the - * function needs to be passed cpu_env. - */ - TCGv_i64 constimm; - int pass; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { - return false; - } - - if ((a->vm | a->vd) & a->q) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - /* - * To avoid excessive duplication of ops we implement shift - * by immediate using the variable shift operations. - */ - constimm = tcg_constant_i64(dup_const(a->size, a->shift)); - - for (pass = 0; pass < a->q + 1; pass++) { - TCGv_i64 tmp = tcg_temp_new_i64(); - - read_neon_element64(tmp, a->vm, pass, MO_64); - fn(tmp, cpu_env, tmp, constimm); - write_neon_element64(tmp, a->vd, pass, MO_64); - tcg_temp_free_i64(tmp); - } - return true; -} - -static bool do_2shift_env_32(DisasContext *s, arg_2reg_shift *a, - NeonGenTwoOpEnvFn *fn) -{ - /* - * 2-reg-and-shift operations, size < 3 case, where the - * helper needs to be passed cpu_env. - */ - TCGv_i32 constimm, tmp; - int pass; - - if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { - return false; - } - - /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { - return false; - } - - if ((a->vm | a->vd) & a->q) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - /* - * To avoid excessive duplication of ops we implement shift - * by immediate using the variable shift operations. - */ - constimm = tcg_constant_i32(dup_const(a->size, a->shift)); - tmp = tcg_temp_new_i32(); - - for (pass = 0; pass < (a->q ? 4 : 2); pass++) { - read_neon_element32(tmp, a->vm, pass, MO_32); - fn(tmp, cpu_env, tmp, constimm); - write_neon_element32(tmp, a->vd, pass, MO_32); - } - tcg_temp_free_i32(tmp); - return true; -} - -#define DO_2SHIFT_ENV(INSN, FUNC) \ - static bool trans_##INSN##_64_2sh(DisasContext *s, arg_2reg_shift *a) \ - { \ - return do_2shift_env_64(s, a, gen_helper_neon_##FUNC##64); \ - } \ - static bool trans_##INSN##_2sh(DisasContext *s, arg_2reg_shift *a) \ - { \ - static NeonGenTwoOpEnvFn * const fns[] = { \ - gen_helper_neon_##FUNC##8, \ - gen_helper_neon_##FUNC##16, \ - gen_helper_neon_##FUNC##32, \ - }; \ - assert(a->size < ARRAY_SIZE(fns)); \ - return do_2shift_env_32(s, a, fns[a->size]); \ - } - -DO_2SHIFT_ENV(VQSHLU, qshlu_s) -DO_2SHIFT_ENV(VQSHL_U, qshl_u) -DO_2SHIFT_ENV(VQSHL_S, qshl_s) +DO_2SH(VSHR_S, gen_gvec_sshr) +DO_2SH(VSHR_U, gen_gvec_ushr) +DO_2SH(VQSHLU, gen_neon_sqshlui) +DO_2SH(VQSHL_U, gen_neon_uqshli) +DO_2SH(VQSHL_S, gen_neon_sqshli) static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, NeonGenTwo64OpFn *shiftfn, - NeonGenNarrowEnvFn *narrowfn) + NeonGenOne64OpEnvFn *narrowfn) { /* 2-reg-and-shift narrowing-shift operations, size == 3 case */ - TCGv_i64 constimm, rm1, rm2; - TCGv_i32 rd; + TCGv_i64 constimm, rm1, rm2, rd; if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { return false; @@ -1460,30 +1137,26 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, constimm = tcg_constant_i64(-a->shift); rm1 = tcg_temp_new_i64(); rm2 = tcg_temp_new_i64(); - rd = tcg_temp_new_i32(); + rd = tcg_temp_new_i64(); /* Load both inputs first to avoid potential overwrite if rm == rd */ read_neon_element64(rm1, a->vm, 0, MO_64); read_neon_element64(rm2, a->vm, 1, MO_64); shiftfn(rm1, rm1, constimm); - narrowfn(rd, cpu_env, rm1); - write_neon_element32(rd, a->vd, 0, MO_32); + narrowfn(rd, tcg_env, rm1); + write_neon_element64(rd, a->vd, 0, MO_32); shiftfn(rm2, rm2, constimm); - narrowfn(rd, cpu_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); + narrowfn(rd, tcg_env, rm2); + write_neon_element64(rd, a->vd, 1, MO_32); return true; } static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, NeonGenTwoOpFn *shiftfn, - NeonGenNarrowEnvFn *narrowfn) + NeonGenOne64OpEnvFn *narrowfn) { /* 2-reg-and-shift narrowing-shift operations, size < 3 case */ TCGv_i32 constimm, rm1, rm2, rm3, rm4; @@ -1537,22 +1210,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); - write_neon_element32(rm1, a->vd, 0, MO_32); - tcg_temp_free_i32(rm1); + narrowfn(rtmp, tcg_env, rtmp); + write_neon_element64(rtmp, a->vd, 0, MO_32); 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); - write_neon_element32(rm3, a->vd, 1, MO_32); - tcg_temp_free_i32(rm3); + narrowfn(rtmp, tcg_env, rtmp); + write_neon_element64(rtmp, a->vd, 1, MO_32); return true; } @@ -1567,17 +1235,17 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, return do_2shift_narrow_32(s, a, FUNC, NARROWFUNC); \ } -static void gen_neon_narrow_u32(TCGv_i32 dest, TCGv_ptr env, TCGv_i64 src) +static void gen_neon_narrow_u32(TCGv_i64 dest, TCGv_ptr env, TCGv_i64 src) { - tcg_gen_extrl_i64_i32(dest, src); + tcg_gen_ext32u_i64(dest, src); } -static void gen_neon_narrow_u16(TCGv_i32 dest, TCGv_ptr env, TCGv_i64 src) +static void gen_neon_narrow_u16(TCGv_i64 dest, TCGv_ptr env, TCGv_i64 src) { gen_helper_neon_narrow_u16(dest, src); } -static void gen_neon_narrow_u8(TCGv_i32 dest, TCGv_ptr env, TCGv_i64 src) +static void gen_neon_narrow_u8(TCGv_i64 dest, TCGv_ptr env, TCGv_i64 src) { gen_helper_neon_narrow_u8(dest, src); } @@ -1660,7 +1328,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); @@ -1668,13 +1335,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; } @@ -1733,7 +1398,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; } @@ -1849,7 +1513,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); @@ -1857,7 +1520,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); @@ -1872,7 +1534,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); @@ -1880,7 +1541,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); @@ -1888,10 +1548,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; } @@ -1976,11 +1632,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; } @@ -2061,8 +1712,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) { @@ -2071,13 +1720,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; } @@ -2149,9 +1795,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) @@ -2161,9 +1804,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) @@ -2216,13 +1856,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) @@ -2239,12 +1879,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) @@ -2268,13 +1908,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) @@ -2344,7 +1984,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) @@ -2353,7 +1992,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) @@ -2417,12 +2055,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; } @@ -2516,7 +2151,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; } @@ -2613,13 +2247,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; } @@ -2692,8 +2322,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(); @@ -2701,13 +2329,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; } @@ -2842,10 +2467,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; @@ -2872,12 +2493,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; } @@ -2919,11 +2534,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; } @@ -3002,9 +2614,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; } @@ -3055,20 +2664,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; } @@ -3192,8 +2796,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; } @@ -3234,10 +2836,9 @@ static bool trans_VZIP(DisasContext *s, arg_2misc *a) } static bool do_vmovn(DisasContext *s, arg_2misc *a, - NeonGenNarrowEnvFn *narrowfn) + NeonGenOne64OpEnvFn *narrowfn) { - TCGv_i64 rm; - TCGv_i32 rd0, rd1; + TCGv_i64 rm, rd0, rd1; if (!arm_dc_feature(s, ARM_FEATURE_NEON)) { return false; @@ -3262,25 +2863,22 @@ static bool do_vmovn(DisasContext *s, arg_2misc *a, } rm = tcg_temp_new_i64(); - rd0 = tcg_temp_new_i32(); - rd1 = tcg_temp_new_i32(); + rd0 = tcg_temp_new_i64(); + rd1 = tcg_temp_new_i64(); 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); - 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); + narrowfn(rd1, tcg_env, rm); + write_neon_element64(rd0, a->vd, 0, MO_32); + write_neon_element64(rd1, a->vd, 1, MO_32); return true; } #define DO_VMOVN(INSN, FUNC) \ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \ { \ - static NeonGenNarrowEnvFn * const narrowfn[] = { \ + static NeonGenOne64OpEnvFn * const narrowfn[] = { \ FUNC##8, \ FUNC##16, \ FUNC##32, \ @@ -3341,10 +2939,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; } @@ -3385,11 +2979,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; } @@ -3432,16 +3021,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; } @@ -3482,18 +3065,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; } @@ -3570,9 +3147,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) @@ -3628,8 +3205,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; } @@ -3746,7 +3321,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) @@ -3790,7 +3365,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) \ { \ @@ -3841,7 +3415,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) \ { \ @@ -3908,11 +3481,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; @@ -3930,9 +3501,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) @@ -3949,9 +3517,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) @@ -4003,8 +3568,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; } @@ -4040,8 +3603,8 @@ static bool trans_VMMLA_b16(DisasContext *s, arg_VMMLA_b16 *a) if (!dc_isar_feature(aa32_bf16, s)) { return false; } - return do_neon_ddda(s, 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_bfmmla); + return do_neon_ddda_env(s, 7, a->vd, a->vn, a->vm, 0, + gen_helper_gvec_bfmmla); } static bool trans_VFMA_b16(DisasContext *s, arg_VFMA_b16 *a) diff --git a/target/arm/translate-sme.c b/target/arm/tcg/translate-sme.c similarity index 81% rename from target/arm/translate-sme.c rename to target/arm/tcg/translate-sme.c index 7b87a9df63..01ece57016 100644 --- a/target/arm/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -18,15 +18,8 @@ */ #include "qemu/osdep.h" -#include "cpu.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "tcg/tcg-gvec-desc.h" #include "translate.h" -#include "exec/helper-gen.h" #include "translate-a64.h" -#include "fpu/softfloat.h" - /* * Include the generated decoder. @@ -56,7 +49,15 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, /* Prepare a power-of-two modulo via extraction of @len bits. */ len = ctz32(streaming_vec_reg_size(s)) - esz; - if (vertical) { + if (!len) { + /* + * SVL is 128 and the element size is 128. There is exactly + * one 128x128 tile in the ZA storage, and so we calculate + * (Rs + imm) MOD 1, which is always 0. We need to special case + * this because TCG doesn't allow deposit ops with len 0. + */ + tcg_gen_movi_i32(tmp, 0); + } else if (vertical) { /* * Compute the byte offset of the index within the tile: * (index % (svl / size)) * size @@ -97,19 +98,33 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, /* Add the byte offset to env to produce the final pointer. */ addr = tcg_temp_new_ptr(); tcg_gen_ext_i32_ptr(addr, tmp); - tcg_temp_free_i32(tmp); - tcg_gen_add_ptr(addr, addr, cpu_env); + 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(cpu_env, tcg_constant_i32(a->imm), + gen_helper_sme_zero(tcg_env, tcg_constant_i32(a->imm), tcg_constant_i32(streaming_vec_reg_size(s))); } return true; @@ -166,11 +181,6 @@ static bool trans_MOVA(DisasContext *s, arg_MOVA *a) h_fns[a->esz](t_za, t_zr, t_za, t_pg, t_desc); } } - - tcg_temp_free_ptr(t_za); - tcg_temp_free_ptr(t_zr); - tcg_temp_free_ptr(t_pg); - return true; } @@ -204,7 +214,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) TCGv_ptr t_za, t_pg; TCGv_i64 addr; - int svl, desc = 0; + uint32_t desc; bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; @@ -222,25 +232,14 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) 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) { - 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, a->st); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (1 << a->esz) - 1); - desc <<= SVE_MTEDESC_SHIFT; - } else { + if (!mte) { addr = clean_data_tbi(s, addr); } - svl = streaming_vec_reg_size(s); - desc = simd_desc(svl, svl, desc); - fns[a->esz][be][a->v][mte][a->st](cpu_env, t_za, t_pg, 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)); - - tcg_temp_free_ptr(t_za); - tcg_temp_free_ptr(t_pg); - tcg_temp_free_i64(addr); return true; } @@ -260,8 +259,6 @@ static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) base = get_tile_rowcol(s, MO_8, a->rv, imm, false); fn(s, base, 0, svl, a->rn, imm * svl); - - tcg_temp_free_ptr(base); return true; } @@ -279,18 +276,12 @@ static bool do_adda(DisasContext *s, arg_adda *a, MemOp esz, return true; } - /* Sum XZR+zad to find ZAd. */ - za = get_tile_rowcol(s, esz, 31, a->zad, false); + 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)); - - tcg_temp_free_ptr(za); - tcg_temp_free_ptr(zn); - tcg_temp_free_ptr(pn); - tcg_temp_free_ptr(pm); return true; } @@ -310,23 +301,18 @@ static bool do_outprod(DisasContext *s, arg_op *a, MemOp esz, return true; } - /* Sum XZR+zad to find ZAd. */ - za = get_tile_rowcol(s, esz, 31, a->zad, false); + 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)); - - tcg_temp_free_ptr(za); - tcg_temp_free_ptr(zn); - tcg_temp_free_ptr(pn); - tcg_temp_free_ptr(pm); return true; } static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, + ARMFPStatusFlavour e_fpst, gen_helper_gvec_5_ptr *fn) { int svl = streaming_vec_reg_size(s); @@ -337,30 +323,46 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, return true; } - /* Sum XZR+zad to find ZAd. */ - za = get_tile_rowcol(s, esz, 31, a->zad, false); + 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); + fpst = fpstatus_ptr(e_fpst); fn(za, zn, zm, pn, pm, fpst, tcg_constant_i32(desc)); - - tcg_temp_free_ptr(za); - tcg_temp_free_ptr(zn); - tcg_temp_free_ptr(pn); - tcg_temp_free_ptr(pm); - tcg_temp_free_ptr(fpst); 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) +static bool do_outprod_env(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; -/* TODO: FEAT_EBF16 */ -TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa) + 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_env, tcg_constant_i32(desc)); + return true; +} + +TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_env, a, + MO_32, gen_helper_sme_fmopa_h) +TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, + MO_32, FPST_FPCR, gen_helper_sme_fmopa_s) +TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, + MO_64, FPST_FPCR, gen_helper_sme_fmopa_d) + +TRANS_FEAT(BFMOPA, aa64_sme, do_outprod_env, 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) diff --git a/target/arm/translate-sve.c b/target/arm/tcg/translate-sve.c similarity index 90% rename from target/arm/translate-sve.c rename to target/arm/tcg/translate-sve.c index 621a2abb22..49d32fabc9 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" @@ -61,13 +50,27 @@ static int tszimm_esz(DisasContext *s, int x) static int tszimm_shr(DisasContext *s, int x) { - return (16 << tszimm_esz(s, x)) - x; + /* + * We won't use the tszimm_shr() value if tszimm_esz() returns -1 (the + * trans function will check for esz < 0), so we can return any + * value we like from here in that case as long as we avoid UB. + */ + int esz = tszimm_esz(s, x); + if (esz < 0) { + return esz; + } + return (16 << esz) - x; } /* See e.g. LSL (immediate, predicated). */ static int tszimm_shl(DisasContext *s, int x) { - return x - (8 << tszimm_esz(s, x)); + /* As with tszimm_shr(), value will be unused if esz < 0 */ + int esz = tszimm_esz(s, x); + if (esz < 0) { + return esz; + } + return x - (8 << esz); } /* The SH bit is in bit 8. Extract the low 8 and shift. */ @@ -130,7 +133,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; } @@ -181,8 +183,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; } @@ -249,10 +249,28 @@ 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; } +static bool gen_gvec_env_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, + int rd, int rn, int rm, int ra, + int data) +{ + return gen_gvec_ptr_zzzz(s, fn, rd, rn, rm, ra, data, tcg_env); +} + +static bool gen_gvec_env_arg_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, + arg_rrrr_esz *a, int data) +{ + return gen_gvec_env_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, data); +} + +static bool gen_gvec_env_arg_zzxz(DisasContext *s, gen_helper_gvec_4_ptr *fn, + arg_rrxr_esz *a) +{ + return gen_gvec_env_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, a->index); +} + /* Invoke an out-of-line helper on 4 Zregs, 1 Preg, plus fpst. */ static bool gen_gvec_fpst_zzzzp(DisasContext *s, gen_helper_gvec_5_ptr *fn, int rd, int rn, int rm, int ra, int pg, @@ -271,8 +289,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; } @@ -321,7 +337,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; } @@ -374,7 +389,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; } @@ -508,7 +522,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) @@ -517,15 +530,12 @@ 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. */ @@ -550,96 +560,6 @@ TRANS_FEAT(ORR_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_or, a) TRANS_FEAT(EOR_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_xor, a) TRANS_FEAT(BIC_zzz, aa64_sve, gen_gvec_fn_arg_zzz, tcg_gen_gvec_andc, a) -static void gen_xar8_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - uint64_t mask = dup_const(MO_8, 0xff >> sh); - - tcg_gen_xor_i64(t, n, m); - tcg_gen_shri_i64(d, t, sh); - tcg_gen_shli_i64(t, t, 8 - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - uint64_t mask = dup_const(MO_16, 0xffff >> sh); - - tcg_gen_xor_i64(t, n, m); - tcg_gen_shri_i64(d, t, sh); - tcg_gen_shli_i64(t, t, 16 - 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) -{ - tcg_gen_xor_i32(d, n, m); - tcg_gen_rotri_i32(d, d, sh); -} - -static void gen_xar_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, int64_t sh) -{ - tcg_gen_xor_i64(d, n, m); - tcg_gen_rotri_i64(d, d, sh); -} - -static void gen_xar_vec(unsigned vece, TCGv_vec d, TCGv_vec n, - TCGv_vec m, int64_t sh) -{ - tcg_gen_xor_vec(vece, d, n, m); - tcg_gen_rotri_vec(vece, d, d, sh); -} - -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) -{ - static const TCGOpcode vecop[] = { INDEX_op_rotli_vec, 0 }; - static const GVecGen3i ops[4] = { - { .fni8 = gen_xar8_i64, - .fniv = gen_xar_vec, - .fno = gen_helper_sve2_xar_b, - .opt_opc = vecop, - .vece = MO_8 }, - { .fni8 = gen_xar16_i64, - .fniv = gen_xar_vec, - .fno = gen_helper_sve2_xar_h, - .opt_opc = vecop, - .vece = MO_16 }, - { .fni4 = gen_xar_i32, - .fniv = gen_xar_vec, - .fno = gen_helper_sve2_xar_s, - .opt_opc = vecop, - .vece = MO_32 }, - { .fni8 = gen_xar_i64, - .fniv = gen_xar_vec, - .fno = gen_helper_gvec_xar_d, - .opt_opc = vecop, - .vece = MO_64 } - }; - int esize = 8 << vece; - - /* The SVE2 range is 1 .. esize; the AdvSIMD range is 0 .. esize-1. */ - tcg_debug_assert(shift >= 0); - tcg_debug_assert(shift <= esize); - shift &= esize - 1; - - if (shift == 0) { - /* xar with no rotate devolves to xor. */ - tcg_gen_gvec_xor(vece, rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz); - } else { - tcg_gen_gvec_3i(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, - shift, &ops[vece]); - } -} - static bool trans_XAR(DisasContext *s, arg_rrri_esz *a) { if (a->esz < 0 || !dc_isar_feature(aa64_sve2, s)) { @@ -654,61 +574,8 @@ static bool trans_XAR(DisasContext *s, arg_rrri_esz *a) return true; } -static void gen_eor3_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) -{ - tcg_gen_xor_i64(d, n, m); - tcg_gen_xor_i64(d, d, k); -} - -static void gen_eor3_vec(unsigned vece, TCGv_vec d, TCGv_vec n, - TCGv_vec m, TCGv_vec k) -{ - tcg_gen_xor_vec(vece, d, n, m); - tcg_gen_xor_vec(vece, d, d, k); -} - -static void gen_eor3(unsigned vece, uint32_t d, uint32_t n, uint32_t m, - uint32_t a, uint32_t oprsz, uint32_t maxsz) -{ - static const GVecGen4 op = { - .fni8 = gen_eor3_i64, - .fniv = gen_eor3_vec, - .fno = gen_helper_sve2_eor3, - .vece = MO_64, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - }; - tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); -} - -TRANS_FEAT(EOR3, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_eor3, a) - -static void gen_bcax_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) -{ - tcg_gen_andc_i64(d, m, k); - tcg_gen_xor_i64(d, d, n); -} - -static void gen_bcax_vec(unsigned vece, TCGv_vec d, TCGv_vec n, - TCGv_vec m, TCGv_vec k) -{ - tcg_gen_andc_vec(vece, d, m, k); - tcg_gen_xor_vec(vece, d, d, n); -} - -static void gen_bcax(unsigned vece, uint32_t d, uint32_t n, uint32_t m, - uint32_t a, uint32_t oprsz, uint32_t maxsz) -{ - static const GVecGen4 op = { - .fni8 = gen_bcax_i64, - .fniv = gen_bcax_vec, - .fno = gen_helper_sve2_bcax, - .vece = MO_64, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - }; - tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &op); -} - -TRANS_FEAT(BCAX, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_bcax, a) +TRANS_FEAT(EOR3, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_gvec_eor3, a) +TRANS_FEAT(BCAX, aa64_sve2, gen_gvec_fn_arg_zzzz, gen_gvec_bcax, a) static void gen_bsl(unsigned vece, uint32_t d, uint32_t n, uint32_t m, uint32_t a, uint32_t oprsz, uint32_t maxsz) @@ -981,14 +848,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; } @@ -1237,7 +1101,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 { @@ -1253,11 +1117,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; } @@ -1411,19 +1271,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 @@ -1691,12 +1546,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); } @@ -1776,7 +1628,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; } @@ -1795,23 +1647,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)); @@ -1864,16 +1714,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; } @@ -1886,7 +1733,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) { @@ -1950,9 +1797,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. */ @@ -1966,8 +1811,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) { @@ -1982,7 +1827,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: @@ -1996,7 +1840,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: @@ -2011,7 +1854,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: @@ -2025,7 +1867,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); } @@ -2034,9 +1875,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) @@ -2217,15 +2055,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) @@ -2368,13 +2202,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) @@ -2384,9 +2215,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; } @@ -2471,15 +2301,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; } @@ -2495,17 +2321,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; } @@ -2594,11 +2417,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, @@ -2661,7 +2482,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 @@ -2674,12 +2494,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. */ @@ -2694,7 +2511,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); @@ -2709,11 +2526,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) { @@ -2751,18 +2566,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. */ @@ -2771,11 +2582,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; } @@ -2821,7 +2631,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) { @@ -2830,9 +2639,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. */ @@ -2841,7 +2648,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; } @@ -2855,7 +2661,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; } @@ -2881,9 +2686,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; } @@ -2935,21 +2739,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; } @@ -3015,19 +2812,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; } @@ -3072,23 +2863,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; } @@ -3107,21 +2893,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; } @@ -3154,12 +2936,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 @@ -3177,12 +2958,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); } } @@ -3212,7 +2991,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; } @@ -3297,7 +3075,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); @@ -3394,12 +3171,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); @@ -3408,7 +3183,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)); @@ -3416,9 +3191,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; } @@ -3450,7 +3222,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. */ @@ -3470,19 +3241,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; } @@ -3809,17 +3576,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; } @@ -3873,7 +3636,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; } @@ -3932,22 +3694,17 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) 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; } @@ -4013,18 +3770,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, @@ -4080,7 +3832,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; } @@ -4212,7 +3963,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; @@ -4226,32 +3977,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, @@ -4312,16 +4059,16 @@ TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, 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 @@ -4334,53 +4081,57 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, 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); + 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_addi_i64(clean_addr, clean_addr, 8); + 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); - - if (base != cpu_env) { - TCGv_ptr b = tcg_temp_local_new_ptr(); - tcg_gen_mov_ptr(b, base); - base = b; - } + 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, base, i); - tcg_gen_addi_ptr(i, i, 8); + 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); - - if (base != cpu_env) { - tcg_temp_free_ptr(base); - assert(len_remain == 0); - } } /* * 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) { @@ -4388,23 +4139,21 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, 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, base, vofs + len_align); - tcg_temp_free_i64(t0); } } @@ -4412,16 +4161,16 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, 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 @@ -4435,50 +4184,52 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, int i; t0 = tcg_temp_new_i64(); - for (i = 0; i < len_align; i += 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_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ); - tcg_gen_addi_i64(clean_addr, clean_addr, 8); + 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); - - if (base != cpu_env) { - TCGv_ptr b = tcg_temp_local_new_ptr(); - tcg_gen_mov_ptr(b, base); - base = b; - } + 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, 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); - - if (base != cpu_env) { - tcg_temp_free_ptr(base); - assert(len_remain == 0); - } } /* 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, base, vofs + len_align); @@ -4488,20 +4239,19 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, 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); } } @@ -4513,7 +4263,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); - gen_sve_ldr(s, cpu_env, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4526,7 +4276,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); - gen_sve_ldr(s, cpu_env, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4539,7 +4289,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); - gen_sve_str(s, cpu_env, off, size, a->rn, a->imm * size); + gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4552,7 +4302,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); - gen_sve_str(s, cpu_env, off, size, a->rn, a->imm * size); + gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size); } return true; } @@ -4579,39 +4329,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] */ @@ -4744,7 +4506,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) @@ -4753,7 +4515,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); @@ -4769,7 +4531,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)) @@ -4872,7 +4634,7 @@ static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a) } 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, @@ -4977,7 +4739,7 @@ static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a) 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, @@ -4991,8 +4753,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) { /* @@ -5005,21 +4772,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) { @@ -5035,7 +4800,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); @@ -5049,7 +4814,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); } @@ -5062,6 +4827,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) { /* @@ -5074,6 +4840,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) { @@ -5087,21 +4856,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. @@ -5129,7 +4896,7 @@ static bool trans_LD1RO_zprr(DisasContext *s, arg_rprr_load *a) } 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); @@ -5144,7 +4911,7 @@ static bool trans_LD1RO_zpri(DisasContext *s, arg_rpri_load *a) } 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); } @@ -5160,6 +4927,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; @@ -5177,29 +4945,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); @@ -5320,14 +5085,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) @@ -5339,7 +5103,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); @@ -5358,7 +5122,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); @@ -5375,30 +5139,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]. */ @@ -6330,10 +6081,9 @@ static void gen_sshll_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t imm) if (top) { if (shl == halfbits) { - 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); + tcg_gen_and_vec(vece, d, n, + tcg_constant_vec_matching(d, vece, + MAKE_64BIT_MASK(halfbits, halfbits))); } else { tcg_gen_sari_vec(vece, d, n, halfbits); tcg_gen_shli_vec(vece, d, d, shl); @@ -6388,20 +6138,18 @@ static void gen_ushll_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t imm) if (top) { if (shl == halfbits) { - 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); + tcg_gen_and_vec(vece, d, n, + tcg_constant_vec_matching(d, vece, + MAKE_64BIT_MASK(halfbits, halfbits))); } else { tcg_gen_shri_vec(vece, d, n, halfbits); tcg_gen_shli_vec(vece, d, d, shl); } } else { if (shl == 0) { - 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); + tcg_gen_and_vec(vece, d, n, + tcg_constant_vec_matching(d, vece, + MAKE_64BIT_MASK(0, halfbits))); } else { tcg_gen_shli_vec(vece, d, n, halfbits); tcg_gen_shri_vec(vece, d, d, halfbits - shl); @@ -6569,19 +6317,14 @@ static const TCGOpcode sqxtn_list[] = { static void gen_sqxtnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t mask = (1ull << halfbits) - 1; int64_t min = -1ull << (halfbits - 1); int64_t max = -min - 1; - tcg_gen_dupi_vec(vece, t, min); - tcg_gen_smax_vec(vece, d, n, t); - tcg_gen_dupi_vec(vece, t, max); - 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); + tcg_gen_smax_vec(vece, d, n, tcg_constant_vec_matching(d, vece, min)); + tcg_gen_smin_vec(vece, d, d, tcg_constant_vec_matching(d, vece, max)); + tcg_gen_and_vec(vece, d, d, tcg_constant_vec_matching(d, vece, mask)); } static const GVecGen2 sqxtnb_ops[3] = { @@ -6602,20 +6345,15 @@ TRANS_FEAT(SQXTNB, aa64_sve2, do_narrow_extract, a, sqxtnb_ops) static void gen_sqxtnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t mask = (1ull << halfbits) - 1; int64_t min = -1ull << (halfbits - 1); int64_t max = -min - 1; - tcg_gen_dupi_vec(vece, t, min); - tcg_gen_smax_vec(vece, n, n, t); - tcg_gen_dupi_vec(vece, t, max); - tcg_gen_smin_vec(vece, n, n, t); + tcg_gen_smax_vec(vece, n, n, tcg_constant_vec_matching(d, vece, min)); + tcg_gen_smin_vec(vece, n, n, tcg_constant_vec_matching(d, vece, max)); 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); + tcg_gen_bitsel_vec(vece, d, tcg_constant_vec_matching(d, vece, mask), d, n); } static const GVecGen2 sqxtnt_ops[3] = { @@ -6643,13 +6381,10 @@ static const TCGOpcode uqxtn_list[] = { static void gen_uqxtnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t max = (1ull << halfbits) - 1; - tcg_gen_dupi_vec(vece, t, max); - tcg_gen_umin_vec(vece, d, n, t); - tcg_temp_free_vec(t); + tcg_gen_umin_vec(vece, d, n, tcg_constant_vec_matching(d, vece, max)); } static const GVecGen2 uqxtnb_ops[3] = { @@ -6670,15 +6405,13 @@ TRANS_FEAT(UQXTNB, aa64_sve2, do_narrow_extract, a, uqxtnb_ops) static void gen_uqxtnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t max = (1ull << halfbits) - 1; + TCGv_vec maxv = tcg_constant_vec_matching(d, vece, max); - tcg_gen_dupi_vec(vece, t, max); - tcg_gen_umin_vec(vece, n, n, t); + tcg_gen_umin_vec(vece, n, n, maxv); tcg_gen_shli_vec(vece, n, n, halfbits); - tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); + tcg_gen_bitsel_vec(vece, d, maxv, d, n); } static const GVecGen2 uqxtnt_ops[3] = { @@ -6706,15 +6439,11 @@ static const TCGOpcode sqxtun_list[] = { static void gen_sqxtunb_vec(unsigned vece, TCGv_vec d, TCGv_vec n) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t max = (1ull << halfbits) - 1; - tcg_gen_dupi_vec(vece, t, 0); - 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); + tcg_gen_smax_vec(vece, d, n, tcg_constant_vec_matching(d, vece, 0)); + tcg_gen_umin_vec(vece, d, d, tcg_constant_vec_matching(d, vece, max)); } static const GVecGen2 sqxtunb_ops[3] = { @@ -6735,17 +6464,14 @@ TRANS_FEAT(SQXTUNB, aa64_sve2, do_narrow_extract, a, sqxtunb_ops) static void gen_sqxtunt_vec(unsigned vece, TCGv_vec d, TCGv_vec n) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t max = (1ull << halfbits) - 1; + TCGv_vec maxv = tcg_constant_vec_matching(d, vece, max); - tcg_gen_dupi_vec(vece, t, 0); - tcg_gen_smax_vec(vece, n, n, t); - tcg_gen_dupi_vec(vece, t, max); - tcg_gen_umin_vec(vece, n, n, t); + tcg_gen_smax_vec(vece, n, n, tcg_constant_vec_matching(d, vece, 0)); + tcg_gen_umin_vec(vece, n, n, maxv); tcg_gen_shli_vec(vece, n, n, halfbits); - tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); + tcg_gen_bitsel_vec(vece, d, maxv, d, n); } static const GVecGen2 sqxtunt_ops[3] = { @@ -6809,14 +6535,11 @@ static void gen_shrnb64_i64(TCGv_i64 d, TCGv_i64 n, int64_t shr) static void gen_shrnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; uint64_t mask = MAKE_64BIT_MASK(0, halfbits); 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); + tcg_gen_and_vec(vece, d, n, tcg_constant_vec_matching(d, vece, mask)); } static const TCGOpcode shrnb_vec_list[] = { INDEX_op_shri_vec, 0 }; @@ -6868,14 +6591,11 @@ static void gen_shrnt64_i64(TCGv_i64 d, TCGv_i64 n, int64_t shr) static void gen_shrnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; uint64_t mask = MAKE_64BIT_MASK(0, halfbits); 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); + tcg_gen_bitsel_vec(vece, d, tcg_constant_vec_matching(d, vece, mask), d, n); } static const TCGOpcode shrnt_vec_list[] = { INDEX_op_shli_vec, 0 }; @@ -6918,15 +6638,12 @@ TRANS_FEAT(RSHRNT, aa64_sve2, do_shr_narrow, a, rshrnt_ops) static void gen_sqshrunb_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; + uint64_t max = MAKE_64BIT_MASK(0, halfbits); tcg_gen_sari_vec(vece, n, n, shr); - tcg_gen_dupi_vec(vece, t, 0); - 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); + tcg_gen_smax_vec(vece, n, n, tcg_constant_vec_matching(d, vece, 0)); + tcg_gen_umin_vec(vece, d, n, tcg_constant_vec_matching(d, vece, max)); } static const TCGOpcode sqshrunb_vec_list[] = { @@ -6951,17 +6668,15 @@ TRANS_FEAT(SQSHRUNB, aa64_sve2, do_shr_narrow, a, sqshrunb_ops) static void gen_sqshrunt_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; + uint64_t max = MAKE_64BIT_MASK(0, halfbits); + TCGv_vec maxv = tcg_constant_vec_matching(d, vece, max); tcg_gen_sari_vec(vece, n, n, shr); - tcg_gen_dupi_vec(vece, t, 0); - tcg_gen_smax_vec(vece, n, n, t); - tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); - tcg_gen_umin_vec(vece, n, n, t); + tcg_gen_smax_vec(vece, n, n, tcg_constant_vec_matching(d, vece, 0)); + tcg_gen_umin_vec(vece, n, n, maxv); tcg_gen_shli_vec(vece, n, n, halfbits); - tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); + tcg_gen_bitsel_vec(vece, d, maxv, d, n); } static const TCGOpcode sqshrunt_vec_list[] = { @@ -7004,19 +6719,15 @@ TRANS_FEAT(SQRSHRUNT, aa64_sve2, do_shr_narrow, a, sqrshrunt_ops) static void gen_sqshrnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t max = MAKE_64BIT_MASK(0, halfbits - 1); int64_t min = -max - 1; + int64_t mask = MAKE_64BIT_MASK(0, halfbits); tcg_gen_sari_vec(vece, n, n, shr); - tcg_gen_dupi_vec(vece, t, min); - tcg_gen_smax_vec(vece, n, n, t); - tcg_gen_dupi_vec(vece, t, max); - 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); + tcg_gen_smax_vec(vece, n, n, tcg_constant_vec_matching(d, vece, min)); + tcg_gen_smin_vec(vece, n, n, tcg_constant_vec_matching(d, vece, max)); + tcg_gen_and_vec(vece, d, n, tcg_constant_vec_matching(d, vece, mask)); } static const TCGOpcode sqshrnb_vec_list[] = { @@ -7041,20 +6752,16 @@ TRANS_FEAT(SQSHRNB, aa64_sve2, do_shr_narrow, a, sqshrnb_ops) static void gen_sqshrnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; int64_t max = MAKE_64BIT_MASK(0, halfbits - 1); int64_t min = -max - 1; + int64_t mask = MAKE_64BIT_MASK(0, halfbits); tcg_gen_sari_vec(vece, n, n, shr); - tcg_gen_dupi_vec(vece, t, min); - tcg_gen_smax_vec(vece, n, n, t); - tcg_gen_dupi_vec(vece, t, max); - tcg_gen_smin_vec(vece, n, n, t); + tcg_gen_smax_vec(vece, n, n, tcg_constant_vec_matching(d, vece, min)); + tcg_gen_smin_vec(vece, n, n, tcg_constant_vec_matching(d, vece, max)); 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); + tcg_gen_bitsel_vec(vece, d, tcg_constant_vec_matching(d, vece, mask), d, n); } static const TCGOpcode sqshrnt_vec_list[] = { @@ -7097,13 +6804,11 @@ TRANS_FEAT(SQRSHRNT, aa64_sve2, do_shr_narrow, a, sqrshrnt_ops) static void gen_uqshrnb_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; + int64_t max = MAKE_64BIT_MASK(0, halfbits); 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); + tcg_gen_umin_vec(vece, d, n, tcg_constant_vec_matching(d, vece, max)); } static const TCGOpcode uqshrnb_vec_list[] = { @@ -7128,15 +6833,14 @@ TRANS_FEAT(UQSHRNB, aa64_sve2, do_shr_narrow, a, uqshrnb_ops) static void gen_uqshrnt_vec(unsigned vece, TCGv_vec d, TCGv_vec n, int64_t shr) { - TCGv_vec t = tcg_temp_new_vec_matching(d); int halfbits = 4 << vece; + int64_t max = MAKE_64BIT_MASK(0, halfbits); + TCGv_vec maxv = tcg_constant_vec_matching(d, vece, max); tcg_gen_shri_vec(vece, n, n, shr); - tcg_gen_dupi_vec(vece, t, MAKE_64BIT_MASK(0, halfbits)); - tcg_gen_umin_vec(vece, n, n, t); + tcg_gen_umin_vec(vece, n, n, maxv); tcg_gen_shli_vec(vece, n, n, halfbits); - tcg_gen_bitsel_vec(vece, d, t, d, n); - tcg_temp_free_vec(t); + tcg_gen_bitsel_vec(vece, d, maxv, d, n); } static const TCGOpcode uqshrnt_vec_list[] = { @@ -7325,12 +7029,14 @@ 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_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, - gen_helper_crypto_aesmc, a->rd, a->rd, a->decrypt) + 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_NONSTREAMING(AESE, aa64_sve2_aes, gen_gvec_ool_arg_zzz, - gen_helper_crypto_aese, a, false) + gen_helper_crypto_aese, a, 0) TRANS_FEAT_NONSTREAMING(AESD, aa64_sve2_aes, gen_gvec_ool_arg_zzz, - gen_helper_crypto_aese, a, true) + gen_helper_crypto_aesd, a, 0) TRANS_FEAT_NONSTREAMING(SM4E, aa64_sve2_sm4, gen_gvec_ool_arg_zzz, gen_helper_crypto_sm4e, a, 0) @@ -7354,9 +7060,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, @@ -7369,7 +7075,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) @@ -7381,7 +7087,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) @@ -7396,12 +7102,12 @@ TRANS_FEAT_NONSTREAMING(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, 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, +TRANS_FEAT(BFDOT_zzzz, aa64_sve_bf16, gen_gvec_env_arg_zzzz, gen_helper_gvec_bfdot, a, 0) -TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_env_arg_zzxz, gen_helper_gvec_bfdot_idx, a) -TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, +TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_env_arg_zzzz, gen_helper_gvec_bfmmla, a, 0) static bool do_BFMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) @@ -7461,7 +7167,7 @@ static bool trans_PSEL(DisasContext *s, arg_psel *a) /* Load the predicate word. */ tcg_gen_trunc_i64_ptr(ptr, didx); - tcg_gen_add_ptr(ptr, ptr, cpu_env); + 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. */ @@ -7472,11 +7178,6 @@ static bool trans_PSEL(DisasContext *s, arg_psel *a) /* 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); - - tcg_temp_free_i64(tmp); - tcg_temp_free_i64(dbit); - tcg_temp_free_i64(didx); - tcg_temp_free_ptr(ptr); return true; } diff --git a/target/arm/translate-vfp.c b/target/arm/tcg/translate-vfp.c similarity index 90% rename from target/arm/translate-vfp.c rename to target/arm/tcg/translate-vfp.c index 5c5d58d2c6..b6fa28a7bf 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,28 @@ 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)); +} + +static inline void vfp_load_reg16(TCGv_i32 var, int reg) +{ + tcg_gen_ld16u_i32(var, tcg_env, + vfp_reg_offset(false, reg) + HOST_BIG_ENDIAN * 2); } /* @@ -117,11 +119,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 +150,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 +178,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); } @@ -365,24 +365,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; @@ -405,14 +396,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 */ @@ -420,9 +409,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; @@ -479,8 +465,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; @@ -490,8 +475,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; @@ -504,14 +487,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; } @@ -555,9 +533,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; @@ -573,9 +549,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(); @@ -595,15 +568,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; } @@ -729,7 +696,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)) { @@ -777,8 +743,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; } @@ -857,7 +821,7 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) if (s->current_el == 1) { gen_set_condexec(s); gen_update_pc(s, 0); - gen_helper_check_hcr_el2_trap(cpu_env, + gen_helper_check_hcr_el2_trap(tcg_env, tcg_constant_i32(a->rt), tcg_constant_i32(a->reg)); } @@ -869,11 +833,11 @@ static bool trans_VMSR_VMRS(DisasContext *s, arg_VMSR_VMRS *a) break; case ARM_VFP_FPSCR: if (a->rt == 15) { - tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]); - tcg_gen_andi_i32(tmp, tmp, FPCR_NZCV_MASK); + tmp = load_cpu_field_low32(vfp.fpsr); + tcg_gen_andi_i32(tmp, tmp, FPSR_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: @@ -883,7 +847,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); } @@ -898,8 +861,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: @@ -946,15 +908,13 @@ static bool trans_VMOV_half(DisasContext *s, arg_VMOV_single *a) if (a->l) { /* VFP to general purpose register */ tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vn); - tcg_gen_andi_i32(tmp, tmp, 0xffff); + vfp_load_reg16(tmp, a->vn); store_reg(s, a->rt, tmp); } else { /* general purpose register to VFP */ 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; @@ -979,7 +939,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); } @@ -987,7 +946,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; @@ -1021,10 +979,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; @@ -1064,10 +1020,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; @@ -1102,9 +1056,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; } @@ -1136,9 +1087,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; } @@ -1177,9 +1125,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; } @@ -1229,7 +1174,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; @@ -1246,7 +1191,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) { @@ -1254,8 +1198,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); @@ -1315,7 +1257,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; @@ -1332,7 +1274,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) { @@ -1347,8 +1288,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); @@ -1485,12 +1424,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; } @@ -1525,20 +1458,14 @@ static bool do_vfp_3op_hp(DisasContext *s, VFPGen3OpSPFn *fn, fd = tcg_temp_new_i32(); fpst = fpstatus_ptr(FPST_FPCR_F16); - vfp_load_reg32(f0, vn); - vfp_load_reg32(f1, vm); + vfp_load_reg16(f0, vn); + vfp_load_reg16(f1, vm); if (reads_vd) { - vfp_load_reg32(fd, vd); + vfp_load_reg16(fd, vd); } 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; } @@ -1615,12 +1542,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; } @@ -1688,10 +1609,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; } @@ -1721,10 +1638,9 @@ static bool do_vfp_2op_hp(DisasContext *s, VFPGen2OpSPFn *fn, int vd, int vm) } f0 = tcg_temp_new_i32(); - vfp_load_reg32(f0, vm); + vfp_load_reg16(f0, vm); fn(f0, f0); vfp_store_reg32(f0, vd); - tcg_temp_free_i32(f0); return true; } @@ -1798,10 +1714,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; } @@ -1812,7 +1724,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) @@ -1827,7 +1738,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) @@ -1842,7 +1752,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) @@ -1859,9 +1768,8 @@ static void gen_VMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_mulh(tmp, vn, vm, fpst); - gen_helper_vfp_negh(tmp, tmp); + gen_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) @@ -1878,9 +1786,8 @@ static void gen_VMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_muls(tmp, vn, vm, fpst); - gen_helper_vfp_negs(tmp, tmp); + gen_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) @@ -1897,9 +1804,8 @@ static void gen_VMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) TCGv_i64 tmp = tcg_temp_new_i64(); gen_helper_vfp_muld(tmp, vn, vm, fpst); - gen_helper_vfp_negd(tmp, tmp); + gen_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) @@ -1918,9 +1824,8 @@ static void gen_VNMLS_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_mulh(tmp, vn, vm, fpst); - gen_helper_vfp_negh(vd, vd); + gen_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) @@ -1939,9 +1844,8 @@ static void gen_VNMLS_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_muls(tmp, vn, vm, fpst); - gen_helper_vfp_negs(vd, vd); + gen_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) @@ -1960,9 +1864,8 @@ static void gen_VNMLS_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) TCGv_i64 tmp = tcg_temp_new_i64(); gen_helper_vfp_muld(tmp, vn, vm, fpst); - gen_helper_vfp_negd(vd, vd); + gen_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) @@ -1976,10 +1879,9 @@ static void gen_VNMLA_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_mulh(tmp, vn, vm, fpst); - gen_helper_vfp_negh(tmp, tmp); - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(tmp, tmp); + gen_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) @@ -1993,10 +1895,9 @@ static void gen_VNMLA_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) TCGv_i32 tmp = tcg_temp_new_i32(); gen_helper_vfp_muls(tmp, vn, vm, fpst); - gen_helper_vfp_negs(tmp, tmp); - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(tmp, tmp); + gen_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) @@ -2010,10 +1911,9 @@ static void gen_VNMLA_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) TCGv_i64 tmp = tcg_temp_new_i64(); gen_helper_vfp_muld(tmp, vn, vm, fpst); - gen_helper_vfp_negd(tmp, tmp); - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(tmp, tmp); + gen_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) @@ -2040,7 +1940,7 @@ static void gen_VNMUL_hp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) { /* VNMUL: -(fn * fm) */ gen_helper_vfp_mulh(vd, vn, vm, fpst); - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(vd, vd); } static bool trans_VNMUL_hp(DisasContext *s, arg_VNMUL_sp *a) @@ -2052,7 +1952,7 @@ static void gen_VNMUL_sp(TCGv_i32 vd, TCGv_i32 vn, TCGv_i32 vm, TCGv_ptr fpst) { /* VNMUL: -(fn * fm) */ gen_helper_vfp_muls(vd, vn, vm, fpst); - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(vd, vd); } static bool trans_VNMUL_sp(DisasContext *s, arg_VNMUL_sp *a) @@ -2064,7 +1964,7 @@ static void gen_VNMUL_dp(TCGv_i64 vd, TCGv_i64 vn, TCGv_i64 vm, TCGv_ptr fpst) { /* VNMUL: -(fn * fm) */ gen_helper_vfp_muld(vd, vn, vm, fpst); - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(vd, vd); } static bool trans_VNMUL_dp(DisasContext *s, arg_VNMUL_dp *a) @@ -2211,26 +2111,20 @@ static bool do_vfm_hp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) vm = tcg_temp_new_i32(); vd = tcg_temp_new_i32(); - vfp_load_reg32(vn, a->vn); - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vn, a->vn); + vfp_load_reg16(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ - gen_helper_vfp_negh(vn, vn); + gen_vfp_negh(vn, vn); } - vfp_load_reg32(vd, a->vd); + vfp_load_reg16(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ - gen_helper_vfp_negh(vd, vd); + gen_vfp_negh(vd, vd); } 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; } @@ -2280,30 +2174,24 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) vfp_load_reg32(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ - gen_helper_vfp_negs(vn, vn); + gen_vfp_negs(vn, vn); } vfp_load_reg32(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ - gen_helper_vfp_negs(vd, vd); + gen_vfp_negs(vd, vd); } 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; } static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) { /* - * VFNMA : fd = muladd(-fd, fn, fm) - * VFNMS : fd = muladd(-fd, -fn, fm) + * VFNMA : fd = muladd(-fd, -fn, fm) + * VFNMS : fd = muladd(-fd, fn, fm) * VFMA : fd = muladd( fd, fn, fm) * VFMS : fd = muladd( fd, -fn, fm) * @@ -2351,22 +2239,16 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) vfp_load_reg64(vm, a->vm); if (neg_n) { /* VFNMS, VFMS */ - gen_helper_vfp_negd(vn, vn); + gen_vfp_negd(vn, vn); } vfp_load_reg64(vd, a->vd); if (neg_d) { /* VFNMA, VFNMS */ - gen_helper_vfp_negd(vd, vd); + gen_vfp_negd(vd, vd); } 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; } @@ -2380,8 +2262,8 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) #define MAKE_VFM_TRANS_FNS(PREC) \ MAKE_ONE_VFM_TRANS_FN(VFMA, PREC, false, false) \ MAKE_ONE_VFM_TRANS_FN(VFMS, PREC, true, false) \ - MAKE_ONE_VFM_TRANS_FN(VFNMA, PREC, false, true) \ - MAKE_ONE_VFM_TRANS_FN(VFNMS, PREC, true, true) + MAKE_ONE_VFM_TRANS_FN(VFNMS, PREC, false, true) \ + MAKE_ONE_VFM_TRANS_FN(VFNMA, PREC, true, true) MAKE_VFM_TRANS_FNS(hp) MAKE_VFM_TRANS_FNS(sp) @@ -2532,27 +2414,27 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) DO_VFP_VMOV(VMOV_reg, sp, tcg_gen_mov_i32) DO_VFP_VMOV(VMOV_reg, dp, tcg_gen_mov_i64) -DO_VFP_2OP(VABS, hp, gen_helper_vfp_absh, aa32_fp16_arith) -DO_VFP_2OP(VABS, sp, gen_helper_vfp_abss, aa32_fpsp_v2) -DO_VFP_2OP(VABS, dp, gen_helper_vfp_absd, aa32_fpdp_v2) +DO_VFP_2OP(VABS, hp, gen_vfp_absh, aa32_fp16_arith) +DO_VFP_2OP(VABS, sp, gen_vfp_abss, aa32_fpsp_v2) +DO_VFP_2OP(VABS, dp, gen_vfp_absd, aa32_fpdp_v2) -DO_VFP_2OP(VNEG, hp, gen_helper_vfp_negh, aa32_fp16_arith) -DO_VFP_2OP(VNEG, sp, gen_helper_vfp_negs, aa32_fpsp_v2) -DO_VFP_2OP(VNEG, dp, gen_helper_vfp_negd, aa32_fpdp_v2) +DO_VFP_2OP(VNEG, hp, gen_vfp_negh, aa32_fp16_arith) +DO_VFP_2OP(VNEG, sp, gen_vfp_negs, aa32_fpsp_v2) +DO_VFP_2OP(VNEG, dp, gen_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) @@ -2579,22 +2461,18 @@ static bool trans_VCMP_hp(DisasContext *s, arg_VCMP_sp *a) vd = tcg_temp_new_i32(); vm = tcg_temp_new_i32(); - vfp_load_reg32(vd, a->vd); + vfp_load_reg16(vd, a->vd); if (a->z) { tcg_gen_movi_i32(vm, 0); } else { - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vm, a->vm); } 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; } @@ -2626,14 +2504,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; } @@ -2670,14 +2544,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; } @@ -2699,12 +2569,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; } @@ -2736,14 +2603,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; } @@ -2765,9 +2628,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; } @@ -2791,10 +2652,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; } @@ -2829,11 +2687,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; } @@ -2851,12 +2705,10 @@ static bool trans_VRINTR_hp(DisasContext *s, arg_VRINTR_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); + vfp_load_reg16(tmp, a->vm); 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; } @@ -2878,8 +2730,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; } @@ -2910,8 +2760,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; } @@ -2930,16 +2778,12 @@ static bool trans_VRINTZ_hp(DisasContext *s, arg_VRINTZ_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); + vfp_load_reg16(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; } @@ -2960,14 +2804,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; } @@ -2997,14 +2837,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; } @@ -3022,12 +2858,10 @@ static bool trans_VRINTX_hp(DisasContext *s, arg_VRINTX_sp *a) } tmp = tcg_temp_new_i32(); - vfp_load_reg32(tmp, a->vm); + vfp_load_reg16(tmp, a->vm); 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; } @@ -3049,8 +2883,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; } @@ -3081,8 +2913,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; } @@ -3107,10 +2937,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; } @@ -3135,10 +2963,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; } @@ -3166,8 +2992,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; } @@ -3195,8 +3019,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; } @@ -3231,9 +3053,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; } @@ -3262,10 +3081,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; } @@ -3322,8 +3139,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; } @@ -3380,8 +3195,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; } @@ -3444,8 +3257,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; } @@ -3464,7 +3275,7 @@ static bool trans_VCVT_hp_int(DisasContext *s, arg_VCVT_sp_int *a) fpst = fpstatus_ptr(FPST_FPCR_F16); vm = tcg_temp_new_i32(); - vfp_load_reg32(vm, a->vm); + vfp_load_reg16(vm, a->vm); if (a->s) { if (a->rz) { @@ -3480,8 +3291,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; } @@ -3516,8 +3325,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; } @@ -3559,9 +3366,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; } @@ -3584,12 +3388,10 @@ static bool trans_VINS(DisasContext *s, arg_VINS *a) /* Insert low half of Vm into high half of Vd */ rm = tcg_temp_new_i32(); rd = tcg_temp_new_i32(); - vfp_load_reg32(rm, a->vm); - vfp_load_reg32(rd, a->vd); + vfp_load_reg16(rm, a->vm); + vfp_load_reg16(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; } @@ -3614,6 +3416,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/tcg/translate.c b/target/arm/tcg/translate.c new file mode 100644 index 0000000000..9ee761fc64 --- /dev/null +++ b/target/arm/tcg/translate.c @@ -0,0 +1,8114 @@ +/* + * ARM translation + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2005-2007 CodeSourcery + * Copyright (c) 2007 OpenedHand, 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-a32.h" +#include "qemu/log.h" +#include "arm_ldst.h" +#include "semihosting/semihost.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) +/* currently all emulated v5 cores are also v5TE, so don't bother */ +#define ENABLE_ARCH_5TE arm_dc_feature(s, ARM_FEATURE_V5) +#define ENABLE_ARCH_5J dc_isar_feature(aa32_jazelle, s) +#define ENABLE_ARCH_6 arm_dc_feature(s, ARM_FEATURE_V6) +#define ENABLE_ARCH_6K arm_dc_feature(s, ARM_FEATURE_V6K) +#define ENABLE_ARCH_6T2 arm_dc_feature(s, ARM_FEATURE_THUMB2) +#define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) +#define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) + +/* 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 */ +static TCGv_i32 cpu_R[16]; +TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; +TCGv_i64 cpu_exclusive_addr; +TCGv_i64 cpu_exclusive_val; + +static const char * const regnames[] = + { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; + + +/* initialize TCG globals. */ +void arm_translate_init(void) +{ + int i; + + for (i = 0; i < 16; i++) { + cpu_R[i] = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUARMState, regs[i]), + regnames[i]); + } + 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(tcg_env, + offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); + cpu_exclusive_val = tcg_global_mem_new_i64(tcg_env, + offsetof(CPUARMState, exclusive_val), "exclusive_val"); + + a64_translate_init(); +} + +uint64_t asimd_imm_const(uint32_t imm, int cmode, int op) +{ + /* Expand the encoded constant as per AdvSIMDExpandImm pseudocode */ + switch (cmode) { + case 0: case 1: + /* no-op */ + break; + case 2: case 3: + imm <<= 8; + break; + case 4: case 5: + imm <<= 16; + break; + case 6: case 7: + imm <<= 24; + break; + case 8: case 9: + imm |= imm << 16; + break; + case 10: case 11: + imm = (imm << 8) | (imm << 24); + break; + case 12: + imm = (imm << 8) | 0xff; + break; + case 13: + imm = (imm << 16) | 0xffff; + break; + case 14: + if (op) { + /* + * This and cmode == 15 op == 1 are the only cases where + * the top and bottom 32 bits of the encoded constant differ. + */ + uint64_t imm64 = 0; + int n; + + for (n = 0; n < 8; n++) { + if (imm & (1 << n)) { + imm64 |= (0xffULL << (n * 8)); + } + } + return imm64; + } + imm |= (imm << 8) | (imm << 16) | (imm << 24); + break; + case 15: + if (op) { + /* Reserved encoding for AArch32; valid for AArch64 */ + uint64_t imm64 = (uint64_t)(imm & 0x3f) << 48; + if (imm & 0x80) { + imm64 |= 0x8000000000000000ULL; + } + if (imm & 0x40) { + imm64 |= 0x3fc0000000000000ULL; + } else { + imm64 |= 0x4000000000000000ULL; + } + return imm64; + } + imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19) + | ((imm & 0x40) ? (0x1f << 25) : (1 << 30)); + break; + } + if (op) { + imm = ~imm; + } + return dup_const(MO_32, imm); +} + +/* Generate a label used for skipping this instruction */ +void arm_gen_condlabel(DisasContext *s) +{ + if (!s->condjmp) { + s->condlabel = gen_disas_label(s); + s->condjmp = 1; + } +} + +/* Flags for the disas_set_da_iss info argument: + * lower bits hold the Rt register number, higher bits are flags. + */ +typedef enum ISSInfo { + ISSNone = 0, + ISSRegMask = 0x1f, + ISSInvalid = (1 << 5), + ISSIsAcqRel = (1 << 6), + ISSIsWrite = (1 << 7), + ISSIs16Bit = (1 << 8), +} ISSInfo; + +/* + * Store var into env + offset to a member with size bytes. + * Free var after use. + */ +void store_cpu_offset(TCGv_i32 var, int offset, int size) +{ + switch (size) { + case 1: + tcg_gen_st8_i32(var, tcg_env, offset); + break; + case 4: + tcg_gen_st_i32(var, tcg_env, offset); + break; + default: + g_assert_not_reached(); + } +} + +/* Save the syndrome information for a Data Abort */ +static void disas_set_da_iss(DisasContext *s, MemOp memop, ISSInfo issinfo) +{ + uint32_t syn; + int sas = memop & MO_SIZE; + bool sse = memop & MO_SIGN; + bool is_acqrel = issinfo & ISSIsAcqRel; + bool is_write = issinfo & ISSIsWrite; + bool is_16bit = issinfo & ISSIs16Bit; + int srt = issinfo & ISSRegMask; + + if (issinfo & ISSInvalid) { + /* Some callsites want to conditionally provide ISS info, + * eg "only if this was not a writeback" + */ + return; + } + + if (srt == 15) { + /* For AArch32, insns where the src/dest is R15 never generate + * ISS information. Catching that here saves checking at all + * the call sites. + */ + return; + } + + syn = syn_data_abort_with_iss(0, sas, sse, srt, 0, is_acqrel, + 0, 0, 0, is_write, 0, is_16bit); + disas_set_insn_syndrome(s, syn); +} + +static inline int get_a32_user_mem_index(DisasContext *s) +{ + /* Return the core mmu_idx to use for A32/T32 "unprivileged load/store" + * insns: + * if PL2, UNPREDICTABLE (we choose to implement as if PL0) + * otherwise, access as if at PL0. + */ + switch (s->mmu_idx) { + case ARMMMUIdx_E3: + case ARMMMUIdx_E30_0: + case ARMMMUIdx_E30_3_PAN: + return arm_to_core_mmu_idx(ARMMMUIdx_E30_0); + 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_MUser: + case ARMMMUIdx_MPriv: + return arm_to_core_mmu_idx(ARMMMUIdx_MUser); + case ARMMMUIdx_MUserNegPri: + case ARMMMUIdx_MPrivNegPri: + return arm_to_core_mmu_idx(ARMMMUIdx_MUserNegPri); + case ARMMMUIdx_MSUser: + case ARMMMUIdx_MSPriv: + return arm_to_core_mmu_idx(ARMMMUIdx_MSUser); + case ARMMMUIdx_MSUserNegPri: + case ARMMMUIdx_MSPrivNegPri: + return arm_to_core_mmu_idx(ARMMMUIdx_MSUserNegPri); + default: + g_assert_not_reached(); + } +} + +/* The pc_curr difference for an architectural jump. */ +static target_long jmp_diff(DisasContext *s, target_long diff) +{ + 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) { + gen_pc_plus_diff(s, var, jmp_diff(s, 0)); + } else { + tcg_gen_mov_i32(var, cpu_R[reg]); + } +} + +/* + * Create a new temp, REG + OFS, except PC is ALIGN(PC, 4). + * This is used for load/store for which use of PC implies (literal), + * or ADD that implies ADR. + */ +TCGv_i32 add_reg_for_lit(DisasContext *s, int reg, int ofs) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + if (reg == 15) { + /* + * 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); + } + return tmp; +} + +/* Set a CPU register. The source must be a temporary and will be + marked as dead. */ +void store_reg(DisasContext *s, int reg, TCGv_i32 var) +{ + if (reg == 15) { + /* In Thumb mode, we must ignore bit 0. + * In ARM mode, for ARMv4 and ARMv5, it is UNPREDICTABLE if bits [1:0] + * are not 0b00, but for ARMv6 and above, we must ignore bits [1:0]. + * We choose to ignore [1:0] in ARM mode for all architecture versions. + */ + 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); +} + +/* + * Variant of store_reg which applies v8M stack-limit checks before updating + * SP. If the check fails this will result in an exception being taken. + * We disable the stack checks for CONFIG_USER_ONLY because we have + * no idea what the stack limits should be in that case. + * If stack checking is not being done this just acts like store_reg(). + */ +static void store_sp_checked(DisasContext *s, TCGv_i32 var) +{ +#ifndef CONFIG_USER_ONLY + if (s->v8m_stackcheck) { + gen_helper_v8m_stackcheck(tcg_env, var); + } +#endif + store_reg(s, 13, var); +} + +/* Value extensions. */ +#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var) +#define gen_uxth(var) tcg_gen_ext16u_i32(var, var) +#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var) +#define gen_sxth(var) tcg_gen_ext16s_i32(var, var) + +#define gen_sxtb16(var) gen_helper_sxtb16(var, var) +#define gen_uxtb16(var) gen_helper_uxtb16(var, var) + +void gen_set_cpsr(TCGv_i32 var, uint32_t mask) +{ + gen_helper_cpsr_write(tcg_env, var, tcg_constant_i32(mask)); +} + +static void gen_rebuild_hflags(DisasContext *s, bool new_el) +{ + bool m_profile = arm_dc_feature(s, ARM_FEATURE_M); + + if (new_el) { + if (m_profile) { + gen_helper_rebuild_hflags_m32_newel(tcg_env); + } else { + 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(tcg_env, tcg_el); + } else { + gen_helper_rebuild_hflags_a32(tcg_env, tcg_el); + } + } +} + +static void gen_exception_internal(int excp) +{ + assert(excp_is_internal(excp)); + gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); +} + +static void gen_singlestep_exception(DisasContext *s) +{ + /* We just completed step of an insn. Move from Active-not-pending + * to Active-pending, and then also take the swstep exception. + * This corresponds to making the (IMPDEF) choice to prioritize + * swstep exceptions over asynchronous exceptions taken to an exception + * level where debug is disabled. This choice has the advantage that + * we do not need to maintain internal state corresponding to the + * ISV/EX syndrome bits between completion of the step and generation + * of the exception, and our syndrome information is always correct. + */ + gen_ss_advance(s); + gen_swstep_exception(s, 1, s->is_ldex); + s->base.is_jmp = DISAS_NORETURN; +} + +void clear_eci_state(DisasContext *s) +{ + /* + * Clear any ECI/ICI state: used when a load multiple/store + * multiple insn executes. + */ + if (s->eci) { + store_cpu_field_constant(0, condexec_bits); + s->eci = 0; + } +} + +static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 tmp1 = tcg_temp_new_i32(); + TCGv_i32 tmp2 = tcg_temp_new_i32(); + tcg_gen_ext16s_i32(tmp1, a); + tcg_gen_ext16s_i32(tmp2, b); + tcg_gen_mul_i32(tmp1, tmp1, 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); +} + +/* Byteswap each halfword. */ +void gen_rev16(TCGv_i32 dest, TCGv_i32 var) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 mask = tcg_constant_i32(0x00ff00ff); + tcg_gen_shri_i32(tmp, var, 8); + tcg_gen_and_i32(tmp, tmp, mask); + tcg_gen_and_i32(var, var, mask); + tcg_gen_shli_i32(var, var, 8); + tcg_gen_or_i32(dest, var, tmp); +} + +/* Byteswap low halfword and sign extend. */ +static void gen_revsh(TCGv_i32 dest, TCGv_i32 var) +{ + tcg_gen_bswap16_i32(var, var, TCG_BSWAP_OS); +} + +/* Dual 16-bit add. Result placed in t0 and t1 is marked as dead. + tmp = (t0 ^ t1) & 0x8000; + t0 &= ~0x8000; + t1 &= ~0x8000; + t0 = (t0 + t1) ^ tmp; + */ + +static void gen_add16(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_xor_i32(tmp, t0, t1); + tcg_gen_andi_i32(tmp, tmp, 0x8000); + tcg_gen_andi_i32(t0, t0, ~0x8000); + tcg_gen_andi_i32(t1, t1, ~0x8000); + tcg_gen_add_i32(t0, t0, t1); + tcg_gen_xor_i32(dest, t0, tmp); +} + +/* Set N and Z flags from var. */ +static inline void gen_logic_CC(TCGv_i32 var) +{ + tcg_gen_mov_i32(cpu_NF, var); + tcg_gen_mov_i32(cpu_ZF, var); +} + +/* dest = T0 + T1 + CF. */ +static void gen_add_carry(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + tcg_gen_add_i32(dest, t0, t1); + tcg_gen_add_i32(dest, dest, cpu_CF); +} + +/* dest = T0 - T1 + CF - 1. */ +static void gen_sub_carry(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + tcg_gen_sub_i32(dest, t0, t1); + tcg_gen_add_i32(dest, dest, cpu_CF); + tcg_gen_subi_i32(dest, dest, 1); +} + +/* dest = T0 + T1. Compute C, N, V and Z flags */ +static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, 0); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, t1, tmp); + 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_gen_mov_i32(dest, cpu_NF); +} + +/* dest = T0 + T1 + CF. Compute C, N, V and Z flags */ +static void gen_adc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + if (TCG_TARGET_HAS_add2_i32) { + tcg_gen_movi_i32(tmp, 0); + tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, cpu_CF, tmp); + tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1, tmp); + } else { + TCGv_i64 q0 = tcg_temp_new_i64(); + TCGv_i64 q1 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(q0, t0); + tcg_gen_extu_i32_i64(q1, t1); + tcg_gen_add_i64(q0, q0, q1); + 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_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_gen_mov_i32(dest, cpu_NF); +} + +/* dest = T0 - T1. Compute C, N, V and Z flags */ +static void gen_sub_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 tmp; + tcg_gen_sub_i32(cpu_NF, t0, t1); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0, t1); + tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); + tmp = tcg_temp_new_i32(); + tcg_gen_xor_i32(tmp, t0, t1); + tcg_gen_and_i32(cpu_VF, cpu_VF, tmp); + tcg_gen_mov_i32(dest, cpu_NF); +} + +/* dest = T0 + ~T1 + CF. Compute C, N, V and Z flags */ +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); +} + +#define GEN_SHIFT(name) \ +static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \ +{ \ + TCGv_i32 tmpd = tcg_temp_new_i32(); \ + TCGv_i32 tmp1 = tcg_temp_new_i32(); \ + TCGv_i32 zero = tcg_constant_i32(0); \ + tcg_gen_andi_i32(tmp1, t1, 0x1f); \ + tcg_gen_##name##_i32(tmpd, t0, tmp1); \ + tcg_gen_andi_i32(tmp1, t1, 0xe0); \ + tcg_gen_movcond_i32(TCG_COND_NE, dest, tmp1, zero, zero, tmpd); \ +} +GEN_SHIFT(shl) +GEN_SHIFT(shr) +#undef GEN_SHIFT + +static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) +{ + TCGv_i32 tmp1 = tcg_temp_new_i32(); + + tcg_gen_andi_i32(tmp1, t1, 0xff); + tcg_gen_umin_i32(tmp1, tmp1, tcg_constant_i32(31)); + tcg_gen_sar_i32(dest, t0, tmp1); +} + +static void shifter_out_im(TCGv_i32 var, int shift) +{ + tcg_gen_extract_i32(cpu_CF, var, shift, 1); +} + +/* Shift by immediate. Includes special handling for shift == 0. */ +static inline void gen_arm_shift_im(TCGv_i32 var, int shiftop, + int shift, int flags) +{ + switch (shiftop) { + case 0: /* LSL */ + if (shift != 0) { + if (flags) + shifter_out_im(var, 32 - shift); + tcg_gen_shli_i32(var, var, shift); + } + break; + case 1: /* LSR */ + if (shift == 0) { + if (flags) { + tcg_gen_shri_i32(cpu_CF, var, 31); + } + tcg_gen_movi_i32(var, 0); + } else { + if (flags) + shifter_out_im(var, shift - 1); + tcg_gen_shri_i32(var, var, shift); + } + break; + case 2: /* ASR */ + if (shift == 0) + shift = 32; + if (flags) + shifter_out_im(var, shift - 1); + if (shift == 32) + shift = 31; + tcg_gen_sari_i32(var, var, shift); + break; + case 3: /* ROR/RRX */ + if (shift != 0) { + if (flags) + shifter_out_im(var, shift - 1); + tcg_gen_rotri_i32(var, var, shift); break; + } else { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, cpu_CF, 31); + if (flags) + shifter_out_im(var, 0); + tcg_gen_shri_i32(var, var, 1); + tcg_gen_or_i32(var, var, tmp); + } + } +}; + +static inline void gen_arm_shift_reg(TCGv_i32 var, int shiftop, + TCGv_i32 shift, int flags) +{ + if (flags) { + switch (shiftop) { + 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) { + case 0: + gen_shl(var, var, shift); + break; + case 1: + gen_shr(var, var, shift); + break; + case 2: + gen_sar(var, var, shift); + break; + case 3: tcg_gen_andi_i32(shift, shift, 0x1f); + tcg_gen_rotr_i32(var, var, shift); break; + } + } +} + +/* + * Generate a conditional based on ARM condition code cc. + * This is common between ARM and Aarch64 targets. + */ +void arm_test_cc(DisasCompare *cmp, int cc) +{ + TCGv_i32 value; + TCGCond cond; + + switch (cc) { + case 0: /* eq: Z */ + case 1: /* ne: !Z */ + cond = TCG_COND_EQ; + value = cpu_ZF; + break; + + case 2: /* cs: C */ + case 3: /* cc: !C */ + cond = TCG_COND_NE; + value = cpu_CF; + break; + + case 4: /* mi: N */ + case 5: /* pl: !N */ + cond = TCG_COND_LT; + value = cpu_NF; + break; + + case 6: /* vs: V */ + case 7: /* vc: !V */ + cond = TCG_COND_LT; + value = cpu_VF; + break; + + case 8: /* hi: C && !Z */ + case 9: /* ls: !C || Z -> !(C && !Z) */ + cond = TCG_COND_NE; + value = tcg_temp_new_i32(); + /* 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); + tcg_gen_and_i32(value, value, cpu_ZF); + break; + + case 10: /* ge: N == V -> N ^ V == 0 */ + case 11: /* lt: N != V -> N ^ V != 0 */ + /* Since we're only interested in the sign bit, == 0 is >= 0. */ + cond = TCG_COND_GE; + value = tcg_temp_new_i32(); + tcg_gen_xor_i32(value, cpu_VF, cpu_NF); + break; + + case 12: /* gt: !Z && N == V */ + case 13: /* le: Z || N != V */ + cond = TCG_COND_NE; + value = tcg_temp_new_i32(); + /* (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); + tcg_gen_sari_i32(value, value, 31); + tcg_gen_andc_i32(value, cpu_ZF, value); + break; + + case 14: /* always */ + case 15: /* always */ + /* Use the ALWAYS condition, which will fold early. + * It doesn't matter what we use for the value. */ + cond = TCG_COND_ALWAYS; + value = cpu_ZF; + goto no_invert; + + default: + fprintf(stderr, "Bad condition code 0x%x\n", cc); + abort(); + } + + if (cc & 1) { + cond = tcg_invert_cond(cond); + } + + no_invert: + cmp->cond = cond; + cmp->value = value; +} + +void arm_jump_cc(DisasCompare *cmp, TCGLabel *label) +{ + tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label); +} + +void arm_gen_test_cc(int cc, TCGLabel *label) +{ + DisasCompare cmp; + arm_test_cc(&cmp, cc); + arm_jump_cc(&cmp, label); +} + +void gen_set_condexec(DisasContext *s) +{ + if (s->condexec_mask) { + uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); + + store_cpu_field_constant(val, condexec_bits); + } +} + +void gen_update_pc(DisasContext *s, target_long diff) +{ + 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. */ +static inline void gen_bx(DisasContext *s, TCGv_i32 var) +{ + s->base.is_jmp = DISAS_JUMP; + 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; +} + +/* + * Set PC and Thumb state from var. var is marked as dead. + * For M-profile CPUs, include logic to detect exception-return + * branches and handle them. This is needed for Thumb POP/LDM to PC, LDR to PC, + * and BX reg, and no others, and happens only for code in Handler mode. + * The Security Extension also requires us to check for the FNC_RETURN + * which signals a function return from non-secure state; this can happen + * in both Handler and Thread mode. + * To avoid having to do multiple comparisons in inline generated code, + * we make the check we do here loose, so it will match for EXC_RETURN + * in Thread mode. For system emulation do_v7m_exception_exit() checks + * for these spurious cases and returns without doing anything (giving + * the same behaviour as for a branch to a non-magic address). + * + * In linux-user mode it is unclear what the right behaviour for an + * attempted FNC_RETURN should be, because in real hardware this will go + * directly to Secure code (ie not the Linux kernel) which will then treat + * the error in any way it chooses. For QEMU we opt to make the FNC_RETURN + * attempt behave the way it would on a CPU without the security extension, + * which is to say "like a normal branch". That means we can simply treat + * all branches as normal with no magic address behaviour. + */ +static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var) +{ + /* Generate the same code here as for a simple bx, but flag via + * s->base.is_jmp that we need to do the rest of the work later. + */ + gen_bx(s, var); +#ifndef CONFIG_USER_ONLY + if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY) || + (s->v7m_handler_mode && arm_dc_feature(s, ARM_FEATURE_M))) { + s->base.is_jmp = DISAS_BX_EXCRET; + } +#endif +} + +static inline void gen_bx_excret_final_code(DisasContext *s) +{ + /* Generate the code to finish possible exception return and end the TB */ + DisasLabel excret_label = gen_disas_label(s); + uint32_t min_magic; + + if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY)) { + /* Covers FNC_RETURN and EXC_RETURN magic */ + min_magic = FNC_RETURN_MIN_MAGIC; + } else { + /* EXC_RETURN magic only */ + min_magic = EXC_RETURN_MIN_MAGIC; + } + + /* 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.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); + } + 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() + * will read. Nothing else will be able to see those values because + * the cpu-exec main loop guarantees that we will always go straight + * from raising the exception to the exception-handling code. + * + * gen_ss_advance(s) does nothing on M profile currently but + * calling it is conceptually the right thing as we have executed + * this instruction (compare SWI, HVC, SMC handling). + */ + gen_ss_advance(s); + gen_exception_internal(EXCP_EXCEPTION_EXIT); +} + +static inline void gen_bxns(DisasContext *s, int rm) +{ + TCGv_i32 var = load_reg(s, 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_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, + * so we know that condexec == 0 (already set at the top of the TB) + * is correct in the non-UNPREDICTABLE cases, and we can choose + * "zeroes the IT bits" as our UNPREDICTABLE behaviour otherwise. + */ + gen_helper_v7m_bxns(tcg_env, var); + s->base.is_jmp = DISAS_EXIT; +} + +static inline void gen_blxns(DisasContext *s, int rm) +{ + TCGv_i32 var = load_reg(s, rm); + + /* We don't need to sync condexec state, for the same reason as bxns. + * We do however need to set the PC, because the blxns helper reads it. + * The blxns helper may throw an exception. + */ + gen_update_pc(s, curr_insn_len(s)); + gen_helper_v7m_blxns(tcg_env, var); + s->base.is_jmp = DISAS_EXIT; +} + +/* Variant of store_reg which uses branch&exchange logic when storing + to r15 in ARM architecture v7 and above. The source must be a temporary + and will be marked as dead. */ +static inline void store_reg_bx(DisasContext *s, int reg, TCGv_i32 var) +{ + if (reg == 15 && ENABLE_ARCH_7) { + gen_bx(s, var); + } else { + store_reg(s, reg, var); + } +} + +/* Variant of store_reg which uses branch&exchange logic when storing + * to r15 in ARM architecture v5T and above. This is used for storing + * the results of a LDR/LDM/POP into r15, and corresponds to the cases + * in the ARM ARM which use the LoadWritePC() pseudocode function. */ +static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) +{ + if (reg == 15 && ENABLE_ARCH_5) { + gen_bx_excret(s, var); + } else { + store_reg(s, reg, var); + } +} + +#ifdef CONFIG_USER_ONLY +#define IS_USER_ONLY 1 +#else +#define IS_USER_ONLY 0 +#endif + +MemOp pow2_align(unsigned i) +{ + static const MemOp mop_align[] = { + 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]; +} + +/* + * Abstractions of "generate code to do a guest load/store for + * AArch32", where a vaddr is always 32 bits (and is zero + * extended if we're a 64 bit core) and data is also + * 32 bits unless specifically doing a 64 bit access. + * These functions work like tcg_gen_qemu_{ld,st}* except + * that the address argument is TCGv_i32 rather than TCGv. + */ + +static TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, MemOp op) +{ + TCGv addr = tcg_temp_new(); + tcg_gen_extu_i32_tl(addr, a32); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b && (op & MO_SIZE) < MO_32) { + tcg_gen_xori_tl(addr, addr, 4 - (1 << (op & MO_SIZE))); + } + return addr; +} + +/* + * Internal routines are used for NEON cases where the endianness + * and/or alignment has already been taken into account and manipulated. + */ +void gen_aa32_ld_internal_i32(DisasContext *s, TCGv_i32 val, + TCGv_i32 a32, int index, MemOp opc) +{ + TCGv addr = gen_aa32_addr(s, a32, opc); + tcg_gen_qemu_ld_i32(val, addr, index, opc); +} + +void gen_aa32_st_internal_i32(DisasContext *s, TCGv_i32 val, + TCGv_i32 a32, int index, MemOp opc) +{ + TCGv addr = gen_aa32_addr(s, a32, opc); + tcg_gen_qemu_st_i32(val, addr, index, opc); +} + +void gen_aa32_ld_internal_i64(DisasContext *s, TCGv_i64 val, + TCGv_i32 a32, int index, MemOp opc) +{ + TCGv addr = gen_aa32_addr(s, a32, opc); + + tcg_gen_qemu_ld_i64(val, addr, index, opc); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b && (opc & MO_SIZE) == MO_64) { + tcg_gen_rotri_i64(val, val, 32); + } +} + +void gen_aa32_st_internal_i64(DisasContext *s, TCGv_i64 val, + TCGv_i32 a32, int index, MemOp opc) +{ + TCGv addr = gen_aa32_addr(s, a32, opc); + + /* Not needed for user-mode BE32, where we use MO_BE instead. */ + if (!IS_USER_ONLY && s->sctlr_b && (opc & MO_SIZE) == MO_64) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_rotri_i64(tmp, val, 32); + tcg_gen_qemu_st_i64(tmp, addr, index, opc); + } else { + tcg_gen_qemu_st_i64(val, addr, index, opc); + } +} + +void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, + int index, MemOp opc) +{ + gen_aa32_ld_internal_i32(s, val, a32, index, finalize_memop(s, opc)); +} + +void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, + int index, MemOp opc) +{ + gen_aa32_st_internal_i32(s, val, a32, index, finalize_memop(s, opc)); +} + +void gen_aa32_ld_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, + int index, MemOp opc) +{ + gen_aa32_ld_internal_i64(s, val, a32, index, finalize_memop(s, opc)); +} + +void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, + int index, MemOp opc) +{ + gen_aa32_st_internal_i64(s, val, a32, index, finalize_memop(s, opc)); +} + +#define DO_GEN_LD(SUFF, OPC) \ + static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 a32, int index) \ + { \ + gen_aa32_ld_i32(s, val, a32, index, OPC); \ + } + +#define DO_GEN_ST(SUFF, OPC) \ + static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ + TCGv_i32 a32, int index) \ + { \ + gen_aa32_st_i32(s, val, a32, index, OPC); \ + } + +static inline void gen_hvc(DisasContext *s, int imm16) +{ + /* The pre HVC helper handles cases when HVC gets trapped + * as an undefined insn by runtime configuration (ie before + * the insn really executes). + */ + 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_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_HVC; +} + +static inline void gen_smc(DisasContext *s) +{ + /* As with HVC, we may take an exception either before or after + * the insn executes. + */ + 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, int excp) +{ + gen_set_condexec(s); + gen_update_pc(s, 0); + gen_exception_internal(excp); + s->base.is_jmp = DISAS_NORETURN; +} + +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_update_pc(s, pc_diff); + } else { + gen_set_condexec(s); + gen_update_pc(s, pc_diff); + } + 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_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, 0, EXCP_UDEF, syn_uncategorized()); +} + +/* Force a TB lookup after an instruction that changes the CPU state. */ +void gen_lookup_tb(DisasContext *s) +{ + gen_pc_plus_diff(s, cpu_R[15], curr_insn_len(s)); + s->base.is_jmp = DISAS_EXIT; +} + +static inline void gen_hlt(DisasContext *s, int imm) +{ + /* 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 0x3C" is a T32 semihosting trap instruction, + * and "HLT 0xF000" is an A32 semihosting syscall. These traps + * must trigger semihosting even for ARMv7 and earlier, where + * HLT was an undefined encoding. + * In system mode, we don't allow userspace access to + * semihosting, to provide some semblance of security + * (and for consistency with our 32-bit semihosting). + */ + if (semihosting_enabled(s->current_el == 0) && + (imm == (s->thumb ? 0x3c : 0xf000))) { + gen_exception_internal_insn(s, EXCP_SEMIHOST); + return; + } + + unallocated_encoding(s); +} + +/* + * Return the offset of a "full" NEON Dreg. + */ +long neon_full_reg_offset(unsigned reg) +{ + return offsetof(CPUARMState, vfp.zregs[reg >> 1].d[reg & 1]); +} + +/* + * Return the offset of a 2**SIZE piece of a NEON register, at index ELE, + * where 0 is the least significant end of the register. + */ +long neon_element_offset(int reg, int element, MemOp memop) +{ + int element_size = 1 << (memop & MO_SIZE); + int ofs = element * element_size; +#if HOST_BIG_ENDIAN + /* + * Calculate the offset assuming fully little-endian, + * then XOR to account for the order of the 8-byte units. + */ + if (element_size < 8) { + ofs ^= 8 - element_size; + } +#endif + return neon_full_reg_offset(reg) + ofs; +} + +/* Return the offset of a VFP Dreg (dp = true) or VFP Sreg (dp = false). */ +long vfp_reg_offset(bool dp, unsigned reg) +{ + if (dp) { + return neon_element_offset(reg, 0, MO_64); + } else { + return neon_element_offset(reg >> 1, reg & 1, MO_32); + } +} + +void read_neon_element32(TCGv_i32 dest, int reg, int ele, MemOp memop) +{ + long off = neon_element_offset(reg, ele, memop); + + switch (memop) { + case MO_SB: + tcg_gen_ld8s_i32(dest, tcg_env, off); + break; + case MO_UB: + tcg_gen_ld8u_i32(dest, tcg_env, off); + break; + case MO_SW: + tcg_gen_ld16s_i32(dest, tcg_env, off); + break; + case MO_UW: + tcg_gen_ld16u_i32(dest, tcg_env, off); + break; + case MO_UL: + case MO_SL: + tcg_gen_ld_i32(dest, tcg_env, off); + break; + default: + g_assert_not_reached(); + } +} + +void read_neon_element64(TCGv_i64 dest, int reg, int ele, MemOp memop) +{ + long off = neon_element_offset(reg, ele, memop); + + switch (memop) { + case MO_SL: + tcg_gen_ld32s_i64(dest, tcg_env, off); + break; + case MO_UL: + tcg_gen_ld32u_i64(dest, tcg_env, off); + break; + case MO_UQ: + tcg_gen_ld_i64(dest, tcg_env, off); + break; + default: + g_assert_not_reached(); + } +} + +void write_neon_element32(TCGv_i32 src, int reg, int ele, MemOp memop) +{ + long off = neon_element_offset(reg, ele, memop); + + switch (memop) { + case MO_8: + tcg_gen_st8_i32(src, tcg_env, off); + break; + case MO_16: + tcg_gen_st16_i32(src, tcg_env, off); + break; + case MO_32: + tcg_gen_st_i32(src, tcg_env, off); + break; + default: + g_assert_not_reached(); + } +} + +void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) +{ + long off = neon_element_offset(reg, ele, memop); + + switch (memop) { + case MO_32: + tcg_gen_st32_i64(src, tcg_env, off); + break; + case MO_64: + tcg_gen_st_i64(src, tcg_env, off); + break; + default: + g_assert_not_reached(); + } +} + +#define ARM_CP_RW_BIT (1 << 20) + +static inline void iwmmxt_load_reg(TCGv_i64 var, int 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, 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, 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, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); +} + +static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) +{ + iwmmxt_store_reg(cpu_M0, rn); +} + +static inline void gen_op_iwmmxt_movq_M0_wRn(int rn) +{ + iwmmxt_load_reg(cpu_M0, rn); +} + +static inline void gen_op_iwmmxt_orq_M0_wRn(int rn) +{ + iwmmxt_load_reg(cpu_V1, rn); + tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1); +} + +static inline void gen_op_iwmmxt_andq_M0_wRn(int rn) +{ + iwmmxt_load_reg(cpu_V1, rn); + tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1); +} + +static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn) +{ + iwmmxt_load_reg(cpu_V1, rn); + tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1); +} + +#define IWMMXT_OP(name) \ +static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ +{ \ + iwmmxt_load_reg(cpu_V1, rn); \ + gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \ +} + +#define IWMMXT_OP_ENV(name) \ +static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ +{ \ + iwmmxt_load_reg(cpu_V1, rn); \ + gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0, cpu_V1); \ +} + +#define IWMMXT_OP_ENV_SIZE(name) \ +IWMMXT_OP_ENV(name##b) \ +IWMMXT_OP_ENV(name##w) \ +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, tcg_env, cpu_M0); \ +} + +IWMMXT_OP(maddsq) +IWMMXT_OP(madduq) +IWMMXT_OP(sadb) +IWMMXT_OP(sadw) +IWMMXT_OP(mulslw) +IWMMXT_OP(mulshw) +IWMMXT_OP(mululw) +IWMMXT_OP(muluhw) +IWMMXT_OP(macsw) +IWMMXT_OP(macuw) + +IWMMXT_OP_ENV_SIZE(unpackl) +IWMMXT_OP_ENV_SIZE(unpackh) + +IWMMXT_OP_ENV1(unpacklub) +IWMMXT_OP_ENV1(unpackluw) +IWMMXT_OP_ENV1(unpacklul) +IWMMXT_OP_ENV1(unpackhub) +IWMMXT_OP_ENV1(unpackhuw) +IWMMXT_OP_ENV1(unpackhul) +IWMMXT_OP_ENV1(unpacklsb) +IWMMXT_OP_ENV1(unpacklsw) +IWMMXT_OP_ENV1(unpacklsl) +IWMMXT_OP_ENV1(unpackhsb) +IWMMXT_OP_ENV1(unpackhsw) +IWMMXT_OP_ENV1(unpackhsl) + +IWMMXT_OP_ENV_SIZE(cmpeq) +IWMMXT_OP_ENV_SIZE(cmpgtu) +IWMMXT_OP_ENV_SIZE(cmpgts) + +IWMMXT_OP_ENV_SIZE(mins) +IWMMXT_OP_ENV_SIZE(minu) +IWMMXT_OP_ENV_SIZE(maxs) +IWMMXT_OP_ENV_SIZE(maxu) + +IWMMXT_OP_ENV_SIZE(subn) +IWMMXT_OP_ENV_SIZE(addn) +IWMMXT_OP_ENV_SIZE(subu) +IWMMXT_OP_ENV_SIZE(addu) +IWMMXT_OP_ENV_SIZE(subs) +IWMMXT_OP_ENV_SIZE(adds) + +IWMMXT_OP_ENV(avgb0) +IWMMXT_OP_ENV(avgb1) +IWMMXT_OP_ENV(avgw0) +IWMMXT_OP_ENV(avgw1) + +IWMMXT_OP_ENV(packuw) +IWMMXT_OP_ENV(packul) +IWMMXT_OP_ENV(packuq) +IWMMXT_OP_ENV(packsw) +IWMMXT_OP_ENV(packsl) +IWMMXT_OP_ENV(packsq) + +static void gen_op_iwmmxt_set_mup(void) +{ + TCGv_i32 tmp; + tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); + tcg_gen_ori_i32(tmp, tmp, 2); + store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); +} + +static void gen_op_iwmmxt_set_cup(void) +{ + TCGv_i32 tmp; + tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); + tcg_gen_ori_i32(tmp, tmp, 1); + store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); +} + +static void gen_op_iwmmxt_setpsr_nz(void) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0); + store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]); +} + +static inline void gen_op_iwmmxt_addl_M0_wRn(int rn) +{ + iwmmxt_load_reg(cpu_V1, rn); + tcg_gen_ext32u_i64(cpu_V1, cpu_V1); + tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); +} + +static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, + TCGv_i32 dest) +{ + int rd; + uint32_t offset; + TCGv_i32 tmp; + + rd = (insn >> 16) & 0xf; + tmp = load_reg(s, rd); + + offset = (insn & 0xff) << ((insn >> 7) & 2); + if (insn & (1 << 24)) { + /* Pre indexed */ + if (insn & (1 << 23)) + tcg_gen_addi_i32(tmp, tmp, offset); + else + tcg_gen_addi_i32(tmp, tmp, -offset); + tcg_gen_mov_i32(dest, tmp); + if (insn & (1 << 21)) { + store_reg(s, rd, tmp); + } + } else if (insn & (1 << 21)) { + /* Post indexed */ + tcg_gen_mov_i32(dest, tmp); + if (insn & (1 << 23)) + tcg_gen_addi_i32(tmp, tmp, offset); + else + tcg_gen_addi_i32(tmp, tmp, -offset); + store_reg(s, rd, tmp); + } else if (!(insn & (1 << 23))) + return 1; + return 0; +} + +static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) +{ + int rd = (insn >> 0) & 0xf; + TCGv_i32 tmp; + + if (insn & (1 << 8)) { + if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) { + return 1; + } else { + tmp = iwmmxt_load_creg(rd); + } + } else { + tmp = tcg_temp_new_i32(); + iwmmxt_load_reg(cpu_V0, rd); + tcg_gen_extrl_i64_i32(tmp, cpu_V0); + } + tcg_gen_andi_i32(tmp, tmp, mask); + tcg_gen_mov_i32(dest, tmp); + return 0; +} + +/* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred + (ie. an undefined instruction). */ +static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) +{ + int rd, wrd; + int rdhi, rdlo, rd0, rd1, i; + TCGv_i32 addr; + TCGv_i32 tmp, tmp2, tmp3; + + if ((insn & 0x0e000e00) == 0x0c000000) { + if ((insn & 0x0fe00ff0) == 0x0c400000) { + wrd = insn & 0xf; + rdlo = (insn >> 12) & 0xf; + rdhi = (insn >> 16) & 0xf; + if (insn & ARM_CP_RW_BIT) { /* TMRRC */ + iwmmxt_load_reg(cpu_V0, wrd); + tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); + tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); + } else { /* TMCRR */ + tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); + iwmmxt_store_reg(cpu_V0, wrd); + gen_op_iwmmxt_set_mup(); + } + return 0; + } + + wrd = (insn >> 12) & 0xf; + addr = tcg_temp_new_i32(); + if (gen_iwmmxt_address(s, insn, addr)) { + return 1; + } + if (insn & ARM_CP_RW_BIT) { + if ((insn >> 28) == 0xf) { /* WLDRW wCx */ + tmp = tcg_temp_new_i32(); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + iwmmxt_store_creg(wrd, tmp); + } else { + i = 1; + if (insn & (1 << 8)) { + if (insn & (1 << 22)) { /* WLDRD */ + gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); + i = 0; + } else { /* WLDRW wRd */ + tmp = tcg_temp_new_i32(); + gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); + } + } else { + tmp = tcg_temp_new_i32(); + if (insn & (1 << 22)) { /* WLDRH */ + gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); + } else { /* WLDRB */ + gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); + } + } + if (i) { + tcg_gen_extu_i32_i64(cpu_M0, tmp); + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + } + } else { + if ((insn >> 28) == 0xf) { /* WSTRW wCx */ + tmp = iwmmxt_load_creg(wrd); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + } else { + gen_op_iwmmxt_movq_M0_wRn(wrd); + tmp = tcg_temp_new_i32(); + if (insn & (1 << 8)) { + if (insn & (1 << 22)) { /* WSTRD */ + gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); + } else { /* WSTRW wRd */ + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + gen_aa32_st32(s, tmp, addr, get_mem_index(s)); + } + } else { + if (insn & (1 << 22)) { /* WSTRH */ + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + gen_aa32_st16(s, tmp, addr, get_mem_index(s)); + } else { /* WSTRB */ + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + gen_aa32_st8(s, tmp, addr, get_mem_index(s)); + } + } + } + } + return 0; + } + + if ((insn & 0x0f000000) != 0x0e000000) + return 1; + + switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { + case 0x000: /* WOR */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_orq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x011: /* TMCR */ + if (insn & 0xf) + return 1; + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + switch (wrd) { + case ARM_IWMMXT_wCID: + case ARM_IWMMXT_wCASF: + break; + case ARM_IWMMXT_wCon: + gen_op_iwmmxt_set_cup(); + /* Fall through. */ + case ARM_IWMMXT_wCSSF: + tmp = iwmmxt_load_creg(wrd); + tmp2 = load_reg(s, rd); + tcg_gen_andc_i32(tmp, tmp, tmp2); + iwmmxt_store_creg(wrd, tmp); + break; + case ARM_IWMMXT_wCGR0: + case ARM_IWMMXT_wCGR1: + case ARM_IWMMXT_wCGR2: + case ARM_IWMMXT_wCGR3: + gen_op_iwmmxt_set_cup(); + tmp = load_reg(s, rd); + iwmmxt_store_creg(wrd, tmp); + break; + default: + return 1; + } + break; + case 0x100: /* WXOR */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_xorq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x111: /* TMRC */ + if (insn & 0xf) + return 1; + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + tmp = iwmmxt_load_creg(wrd); + store_reg(s, rd, tmp); + break; + case 0x300: /* WANDN */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tcg_gen_neg_i64(cpu_M0, cpu_M0); + gen_op_iwmmxt_andq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x200: /* WAND */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + gen_op_iwmmxt_andq_M0_wRn(rd1); + gen_op_iwmmxt_setpsr_nz(); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x810: case 0xa10: /* WMADD */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 0) & 0xf; + rd1 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 21)) + gen_op_iwmmxt_maddsq_M0_wRn(rd1); + else + gen_op_iwmmxt_madduq_M0_wRn(rd1); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_unpacklb_M0_wRn(rd1); + break; + case 1: + gen_op_iwmmxt_unpacklw_M0_wRn(rd1); + break; + case 2: + gen_op_iwmmxt_unpackll_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_unpackhb_M0_wRn(rd1); + break; + case 1: + gen_op_iwmmxt_unpackhw_M0_wRn(rd1); + break; + case 2: + gen_op_iwmmxt_unpackhl_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 22)) + gen_op_iwmmxt_sadw_M0_wRn(rd1); + else + gen_op_iwmmxt_sadb_M0_wRn(rd1); + if (!(insn & (1 << 20))) + gen_op_iwmmxt_addl_M0_wRn(wrd); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 21)) { + if (insn & (1 << 20)) + gen_op_iwmmxt_mulshw_M0_wRn(rd1); + else + gen_op_iwmmxt_mulslw_M0_wRn(rd1); + } else { + if (insn & (1 << 20)) + gen_op_iwmmxt_muluhw_M0_wRn(rd1); + else + gen_op_iwmmxt_mululw_M0_wRn(rd1); + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 21)) + gen_op_iwmmxt_macsw_M0_wRn(rd1); + else + gen_op_iwmmxt_macuw_M0_wRn(rd1); + if (!(insn & (1 << 20))) { + iwmmxt_load_reg(cpu_V1, wrd); + tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_op_iwmmxt_cmpeqb_M0_wRn(rd1); + break; + case 1: + gen_op_iwmmxt_cmpeqw_M0_wRn(rd1); + break; + case 2: + gen_op_iwmmxt_cmpeql_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + if (insn & (1 << 22)) { + if (insn & (1 << 20)) + gen_op_iwmmxt_avgw1_M0_wRn(rd1); + else + gen_op_iwmmxt_avgw0_M0_wRn(rd1); + } else { + if (insn & (1 << 20)) + gen_op_iwmmxt_avgb1_M0_wRn(rd1); + else + gen_op_iwmmxt_avgb0_M0_wRn(rd1); + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tmp = iwmmxt_load_creg(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3)); + tcg_gen_andi_i32(tmp, tmp, 7); + iwmmxt_load_reg(cpu_V1, rd1); + gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ + if (((insn >> 6) & 3) == 3) + return 1; + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + tmp = load_reg(s, rd); + gen_op_iwmmxt_movq_M0_wRn(wrd); + switch ((insn >> 6) & 3) { + case 0: + tmp2 = tcg_constant_i32(0xff); + tmp3 = tcg_constant_i32((insn & 7) << 3); + break; + case 1: + tmp2 = tcg_constant_i32(0xffff); + tmp3 = tcg_constant_i32((insn & 3) << 4); + break; + case 2: + tmp2 = tcg_constant_i32(0xffffffff); + tmp3 = tcg_constant_i32((insn & 1) << 5); + break; + default: + g_assert_not_reached(); + } + gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + if (rd == 15 || ((insn >> 22) & 3) == 3) + return 1; + gen_op_iwmmxt_movq_M0_wRn(wrd); + tmp = tcg_temp_new_i32(); + switch ((insn >> 22) & 3) { + case 0: + tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + if (insn & 8) { + tcg_gen_ext8s_i32(tmp, tmp); + } else { + tcg_gen_andi_i32(tmp, tmp, 0xff); + } + break; + case 1: + tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + if (insn & 8) { + tcg_gen_ext16s_i32(tmp, tmp); + } else { + tcg_gen_andi_i32(tmp, tmp, 0xffff); + } + break; + case 2: + tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5); + tcg_gen_extrl_i64_i32(tmp, cpu_M0); + break; + } + store_reg(s, rd, tmp); + break; + case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ + if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) + return 1; + tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); + switch ((insn >> 22) & 3) { + case 0: + tcg_gen_shri_i32(tmp, tmp, ((insn & 7) << 2) + 0); + break; + case 1: + tcg_gen_shri_i32(tmp, tmp, ((insn & 3) << 3) + 4); + break; + case 2: + tcg_gen_shri_i32(tmp, tmp, ((insn & 1) << 4) + 12); + break; + } + tcg_gen_shli_i32(tmp, tmp, 28); + gen_set_nzcv(tmp); + break; + case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ + if (((insn >> 6) & 3) == 3) + return 1; + rd = (insn >> 12) & 0xf; + wrd = (insn >> 16) & 0xf; + tmp = load_reg(s, rd); + switch ((insn >> 6) & 3) { + case 0: + gen_helper_iwmmxt_bcstb(cpu_M0, tmp); + break; + case 1: + gen_helper_iwmmxt_bcstw(cpu_M0, tmp); + break; + case 2: + gen_helper_iwmmxt_bcstl(cpu_M0, tmp); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ + if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) + return 1; + tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); + tmp2 = tcg_temp_new_i32(); + tcg_gen_mov_i32(tmp2, tmp); + switch ((insn >> 22) & 3) { + case 0: + for (i = 0; i < 7; i ++) { + tcg_gen_shli_i32(tmp2, tmp2, 4); + tcg_gen_and_i32(tmp, tmp, tmp2); + } + break; + case 1: + for (i = 0; i < 3; i ++) { + tcg_gen_shli_i32(tmp2, tmp2, 8); + tcg_gen_and_i32(tmp, tmp, tmp2); + } + break; + case 2: + tcg_gen_shli_i32(tmp2, tmp2, 16); + tcg_gen_and_i32(tmp, tmp, tmp2); + break; + } + gen_set_nzcv(tmp); + break; + case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0); + break; + case 1: + gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0); + break; + case 2: + gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ + if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) + return 1; + tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); + tmp2 = tcg_temp_new_i32(); + tcg_gen_mov_i32(tmp2, tmp); + switch ((insn >> 22) & 3) { + case 0: + for (i = 0; i < 7; i ++) { + tcg_gen_shli_i32(tmp2, tmp2, 4); + tcg_gen_or_i32(tmp, tmp, tmp2); + } + break; + case 1: + for (i = 0; i < 3; i ++) { + tcg_gen_shli_i32(tmp2, tmp2, 8); + tcg_gen_or_i32(tmp, tmp, tmp2); + } + break; + case 2: + tcg_gen_shli_i32(tmp2, tmp2, 16); + tcg_gen_or_i32(tmp, tmp, tmp2); + break; + } + gen_set_nzcv(tmp); + break; + case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ + rd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) + return 1; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tmp = tcg_temp_new_i32(); + switch ((insn >> 22) & 3) { + case 0: + gen_helper_iwmmxt_msbb(tmp, cpu_M0); + break; + case 1: + gen_helper_iwmmxt_msbw(tmp, cpu_M0); + break; + case 2: + gen_helper_iwmmxt_msbl(tmp, cpu_M0); + break; + } + store_reg(s, rd, tmp); + break; + case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ + case 0x906: case 0xb06: case 0xd06: case 0xf06: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1); + else + gen_op_iwmmxt_cmpgtub_M0_wRn(rd1); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1); + else + gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1); + else + gen_op_iwmmxt_cmpgtul_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ + case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpacklsb_M0(); + else + gen_op_iwmmxt_unpacklub_M0(); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpacklsw_M0(); + else + gen_op_iwmmxt_unpackluw_M0(); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpacklsl_M0(); + else + gen_op_iwmmxt_unpacklul_M0(); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ + case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpackhsb_M0(); + else + gen_op_iwmmxt_unpackhub_M0(); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpackhsw_M0(); + else + gen_op_iwmmxt_unpackhuw_M0(); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_unpackhsl_M0(); + else + gen_op_iwmmxt_unpackhul_M0(); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ + case 0x214: case 0x614: case 0xa14: case 0xe14: + if (((insn >> 22) & 3) == 0) + return 1; + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tmp = tcg_temp_new_i32(); + if (gen_iwmmxt_shift(insn, 0xff, tmp)) { + return 1; + } + switch ((insn >> 22) & 3) { + case 1: + gen_helper_iwmmxt_srlw(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 2: + gen_helper_iwmmxt_srll(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 3: + gen_helper_iwmmxt_srlq(cpu_M0, tcg_env, cpu_M0, tmp); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ + case 0x014: case 0x414: case 0x814: case 0xc14: + if (((insn >> 22) & 3) == 0) + return 1; + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tmp = tcg_temp_new_i32(); + if (gen_iwmmxt_shift(insn, 0xff, tmp)) { + return 1; + } + switch ((insn >> 22) & 3) { + case 1: + gen_helper_iwmmxt_sraw(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 2: + gen_helper_iwmmxt_sral(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 3: + gen_helper_iwmmxt_sraq(cpu_M0, tcg_env, cpu_M0, tmp); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ + case 0x114: case 0x514: case 0x914: case 0xd14: + if (((insn >> 22) & 3) == 0) + return 1; + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tmp = tcg_temp_new_i32(); + if (gen_iwmmxt_shift(insn, 0xff, tmp)) { + return 1; + } + switch ((insn >> 22) & 3) { + case 1: + gen_helper_iwmmxt_sllw(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 2: + gen_helper_iwmmxt_slll(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 3: + gen_helper_iwmmxt_sllq(cpu_M0, tcg_env, cpu_M0, tmp); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ + case 0x314: case 0x714: case 0xb14: case 0xf14: + if (((insn >> 22) & 3) == 0) + return 1; + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + tmp = tcg_temp_new_i32(); + switch ((insn >> 22) & 3) { + case 1: + if (gen_iwmmxt_shift(insn, 0xf, tmp)) { + return 1; + } + gen_helper_iwmmxt_rorw(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 2: + if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { + return 1; + } + gen_helper_iwmmxt_rorl(cpu_M0, tcg_env, cpu_M0, tmp); + break; + case 3: + if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { + return 1; + } + gen_helper_iwmmxt_rorq(cpu_M0, tcg_env, cpu_M0, tmp); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ + case 0x916: case 0xb16: case 0xd16: case 0xf16: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_minsb_M0_wRn(rd1); + else + gen_op_iwmmxt_minub_M0_wRn(rd1); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_minsw_M0_wRn(rd1); + else + gen_op_iwmmxt_minuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_minsl_M0_wRn(rd1); + else + gen_op_iwmmxt_minul_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ + case 0x816: case 0xa16: case 0xc16: case 0xe16: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 0: + if (insn & (1 << 21)) + gen_op_iwmmxt_maxsb_M0_wRn(rd1); + else + gen_op_iwmmxt_maxub_M0_wRn(rd1); + break; + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_maxsw_M0_wRn(rd1); + else + gen_op_iwmmxt_maxuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_maxsl_M0_wRn(rd1); + else + gen_op_iwmmxt_maxul_M0_wRn(rd1); + break; + case 3: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ + case 0x402: case 0x502: case 0x602: case 0x702: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + iwmmxt_load_reg(cpu_V1, rd1); + gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, + tcg_constant_i32((insn >> 20) & 3)); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ + case 0x41a: case 0x51a: case 0x61a: case 0x71a: + case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: + case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 20) & 0xf) { + case 0x0: + gen_op_iwmmxt_subnb_M0_wRn(rd1); + break; + case 0x1: + gen_op_iwmmxt_subub_M0_wRn(rd1); + break; + case 0x3: + gen_op_iwmmxt_subsb_M0_wRn(rd1); + break; + case 0x4: + gen_op_iwmmxt_subnw_M0_wRn(rd1); + break; + case 0x5: + gen_op_iwmmxt_subuw_M0_wRn(rd1); + break; + case 0x7: + gen_op_iwmmxt_subsw_M0_wRn(rd1); + break; + case 0x8: + gen_op_iwmmxt_subnl_M0_wRn(rd1); + break; + case 0x9: + gen_op_iwmmxt_subul_M0_wRn(rd1); + break; + case 0xb: + gen_op_iwmmxt_subsl_M0_wRn(rd1); + break; + default: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ + case 0x41e: case 0x51e: case 0x61e: case 0x71e: + case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: + case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: + wrd = (insn >> 12) & 0xf; + 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, tcg_env, cpu_M0, tmp); + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ + case 0x418: case 0x518: case 0x618: case 0x718: + case 0x818: case 0x918: case 0xa18: case 0xb18: + case 0xc18: case 0xd18: case 0xe18: case 0xf18: + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 20) & 0xf) { + case 0x0: + gen_op_iwmmxt_addnb_M0_wRn(rd1); + break; + case 0x1: + gen_op_iwmmxt_addub_M0_wRn(rd1); + break; + case 0x3: + gen_op_iwmmxt_addsb_M0_wRn(rd1); + break; + case 0x4: + gen_op_iwmmxt_addnw_M0_wRn(rd1); + break; + case 0x5: + gen_op_iwmmxt_adduw_M0_wRn(rd1); + break; + case 0x7: + gen_op_iwmmxt_addsw_M0_wRn(rd1); + break; + case 0x8: + gen_op_iwmmxt_addnl_M0_wRn(rd1); + break; + case 0x9: + gen_op_iwmmxt_addul_M0_wRn(rd1); + break; + case 0xb: + gen_op_iwmmxt_addsl_M0_wRn(rd1); + break; + default: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ + case 0x408: case 0x508: case 0x608: case 0x708: + case 0x808: case 0x908: case 0xa08: case 0xb08: + case 0xc08: case 0xd08: case 0xe08: case 0xf08: + if (!(insn & (1 << 20)) || ((insn >> 22) & 3) == 0) + return 1; + wrd = (insn >> 12) & 0xf; + rd0 = (insn >> 16) & 0xf; + rd1 = (insn >> 0) & 0xf; + gen_op_iwmmxt_movq_M0_wRn(rd0); + switch ((insn >> 22) & 3) { + case 1: + if (insn & (1 << 21)) + gen_op_iwmmxt_packsw_M0_wRn(rd1); + else + gen_op_iwmmxt_packuw_M0_wRn(rd1); + break; + case 2: + if (insn & (1 << 21)) + gen_op_iwmmxt_packsl_M0_wRn(rd1); + else + gen_op_iwmmxt_packul_M0_wRn(rd1); + break; + case 3: + if (insn & (1 << 21)) + gen_op_iwmmxt_packsq_M0_wRn(rd1); + else + gen_op_iwmmxt_packuq_M0_wRn(rd1); + break; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + gen_op_iwmmxt_set_cup(); + break; + case 0x201: case 0x203: case 0x205: case 0x207: + case 0x209: case 0x20b: case 0x20d: case 0x20f: + case 0x211: case 0x213: case 0x215: case 0x217: + case 0x219: case 0x21b: case 0x21d: case 0x21f: + wrd = (insn >> 5) & 0xf; + rd0 = (insn >> 12) & 0xf; + rd1 = (insn >> 0) & 0xf; + if (rd0 == 0xf || rd1 == 0xf) + return 1; + gen_op_iwmmxt_movq_M0_wRn(wrd); + tmp = load_reg(s, rd0); + tmp2 = load_reg(s, rd1); + switch ((insn >> 16) & 0xf) { + case 0x0: /* TMIA */ + gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); + break; + case 0x8: /* TMIAPH */ + gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); + break; + case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ + if (insn & (1 << 16)) + tcg_gen_shri_i32(tmp, tmp, 16); + if (insn & (1 << 17)) + tcg_gen_shri_i32(tmp2, tmp2, 16); + gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); + break; + default: + return 1; + } + gen_op_iwmmxt_movq_wRn_M0(wrd); + gen_op_iwmmxt_set_mup(); + break; + default: + return 1; + } + + return 0; +} + +/* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred + (ie. an undefined instruction). */ +static int disas_dsp_insn(DisasContext *s, uint32_t insn) +{ + int acc, rd0, rd1, rdhi, rdlo; + TCGv_i32 tmp, tmp2; + + if ((insn & 0x0ff00f10) == 0x0e200010) { + /* Multiply with Internal Accumulate Format */ + rd0 = (insn >> 12) & 0xf; + rd1 = insn & 0xf; + acc = (insn >> 5) & 7; + + if (acc != 0) + return 1; + + tmp = load_reg(s, rd0); + tmp2 = load_reg(s, rd1); + switch ((insn >> 16) & 0xf) { + case 0x0: /* MIA */ + gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); + break; + case 0x8: /* MIAPH */ + gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); + break; + case 0xc: /* MIABB */ + case 0xd: /* MIABT */ + case 0xe: /* MIATB */ + case 0xf: /* MIATT */ + if (insn & (1 << 16)) + tcg_gen_shri_i32(tmp, tmp, 16); + if (insn & (1 << 17)) + tcg_gen_shri_i32(tmp2, tmp2, 16); + gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); + break; + default: + return 1; + } + + gen_op_iwmmxt_movq_wRn_M0(acc); + return 0; + } + + if ((insn & 0x0fe00ff8) == 0x0c400000) { + /* Internal Accumulator Access Format */ + rdhi = (insn >> 16) & 0xf; + rdlo = (insn >> 12) & 0xf; + acc = insn & 7; + + if (acc != 0) + return 1; + + if (insn & ARM_CP_RW_BIT) { /* MRA */ + iwmmxt_load_reg(cpu_V0, acc); + tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); + tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); + tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); + } else { /* MAR */ + tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); + iwmmxt_store_reg(cpu_V0, acc); + } + return 0; + } + + return 1; +} + +static void gen_goto_ptr(void) +{ + tcg_gen_lookup_and_goto_ptr(); +} + +/* This will end the TB but doesn't guarantee we'll return to + * 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_long diff) +{ + 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_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 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_update_pc(s, diff); + s->base.is_jmp = DISAS_JUMP; + return; + } + switch (s->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: + case DISAS_NORETURN: + /* + * The normal case: just go to the destination TB. + * NB: NORETURN happens if we generate code like + * gen_brcondi(l); + * gen_jmp(); + * gen_set_label(l); + * gen_jmp(); + * on the second call to gen_jmp(). + */ + gen_goto_tb(s, tbno, diff); + break; + case DISAS_UPDATE_NOCHAIN: + case DISAS_UPDATE_EXIT: + /* + * We already decided we're leaving the TB for some other reason. + * Avoid using goto_tb so we really do exit back to the main loop + * and don't chain to another TB. + */ + gen_update_pc(s, diff); + gen_goto_ptr(); + s->base.is_jmp = DISAS_NORETURN; + break; + default: + /* + * We shouldn't be emitting code for a jump and also have + * is_jmp set to one of the special cases like DISAS_SWI. + */ + g_assert_not_reached(); + } +} + +static inline void gen_jmp(DisasContext *s, target_long diff) +{ + gen_jmp_tb(s, diff, 0); +} + +static inline void gen_mulxy(TCGv_i32 t0, TCGv_i32 t1, int x, int y) +{ + if (x) + tcg_gen_sari_i32(t0, t0, 16); + else + gen_sxth(t0); + if (y) + tcg_gen_sari_i32(t1, t1, 16); + else + gen_sxth(t1); + tcg_gen_mul_i32(t0, t0, t1); +} + +/* Return the mask of PSR bits set by a MSR instruction. */ +static uint32_t msr_mask(DisasContext *s, int flags, int spsr) +{ + uint32_t mask = 0; + + if (flags & (1 << 0)) { + mask |= 0xff; + } + if (flags & (1 << 1)) { + mask |= 0xff00; + } + if (flags & (1 << 2)) { + mask |= 0xff0000; + } + if (flags & (1 << 3)) { + mask |= 0xff000000; + } + + /* Mask out undefined and reserved bits. */ + mask &= aarch32_cpsr_valid_mask(s->features, s->isar); + + /* Mask out execution state. */ + if (!spsr) { + mask &= ~CPSR_EXEC; + } + + /* Mask out privileged bits. */ + if (IS_USER(s)) { + mask &= CPSR_USER; + } + return mask; +} + +/* Returns nonzero if access to the PSR is not permitted. Marks t0 as dead. */ +static int gen_set_psr(DisasContext *s, uint32_t mask, int spsr, TCGv_i32 t0) +{ + TCGv_i32 tmp; + if (spsr) { + /* ??? This is also undefined in system mode. */ + if (IS_USER(s)) + return 1; + + tmp = load_cpu_field(spsr); + tcg_gen_andi_i32(tmp, tmp, ~mask); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_or_i32(tmp, tmp, t0); + store_cpu_field(tmp, spsr); + } else { + gen_set_cpsr(t0, mask); + } + gen_lookup_tb(s); + return 0; +} + +/* Returns nonzero if access to the PSR is not permitted. */ +static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val) +{ + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); + tcg_gen_movi_i32(tmp, val); + return gen_set_psr(s, mask, spsr, tmp); +} + +static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, + int *tgtmode, int *regno) +{ + /* Decode the r and sysm fields of MSR/MRS banked accesses into + * the target mode and register number, and identify the various + * unpredictable cases. + * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if: + * + executed in user mode + * + using R15 as the src/dest register + * + accessing an unimplemented register + * + accessing a register that's inaccessible at current PL/security state* + * + accessing a register that you could access with a different insn + * We choose to UNDEF in all these cases. + * Since we don't know which of the various AArch32 modes we are in + * we have to defer some checks to runtime. + * Accesses to Monitor mode registers from Secure EL1 (which implies + * that EL3 is AArch64) must trap to EL3. + * + * If the access checks fail this function will emit code to take + * an exception and return false. Otherwise it will return true, + * and set *tgtmode and *regno appropriately. + */ + /* These instructions are present only in ARMv8, or in ARMv7 with the + * Virtualization Extensions. + */ + if (!arm_dc_feature(s, ARM_FEATURE_V8) && + !arm_dc_feature(s, ARM_FEATURE_EL2)) { + goto undef; + } + + if (IS_USER(s) || rn == 15) { + goto undef; + } + + /* The table in the v8 ARM ARM section F5.2.3 describes the encoding + * of registers into (r, sysm). + */ + if (r) { + /* SPSRs for other modes */ + switch (sysm) { + case 0xe: /* SPSR_fiq */ + *tgtmode = ARM_CPU_MODE_FIQ; + break; + case 0x10: /* SPSR_irq */ + *tgtmode = ARM_CPU_MODE_IRQ; + break; + case 0x12: /* SPSR_svc */ + *tgtmode = ARM_CPU_MODE_SVC; + break; + case 0x14: /* SPSR_abt */ + *tgtmode = ARM_CPU_MODE_ABT; + break; + case 0x16: /* SPSR_und */ + *tgtmode = ARM_CPU_MODE_UND; + break; + case 0x1c: /* SPSR_mon */ + *tgtmode = ARM_CPU_MODE_MON; + break; + case 0x1e: /* SPSR_hyp */ + *tgtmode = ARM_CPU_MODE_HYP; + break; + default: /* unallocated */ + goto undef; + } + /* We arbitrarily assign SPSR a register number of 16. */ + *regno = 16; + } else { + /* general purpose registers for other modes */ + switch (sysm) { + case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */ + *tgtmode = ARM_CPU_MODE_USR; + *regno = sysm + 8; + break; + case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */ + *tgtmode = ARM_CPU_MODE_FIQ; + *regno = sysm; + break; + case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */ + *tgtmode = ARM_CPU_MODE_IRQ; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */ + *tgtmode = ARM_CPU_MODE_SVC; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */ + *tgtmode = ARM_CPU_MODE_ABT; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */ + *tgtmode = ARM_CPU_MODE_UND; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */ + *tgtmode = ARM_CPU_MODE_MON; + *regno = sysm & 1 ? 13 : 14; + break; + case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */ + *tgtmode = ARM_CPU_MODE_HYP; + /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */ + *regno = sysm & 1 ? 13 : 17; + break; + default: /* unallocated */ + goto undef; + } + } + + /* Catch the 'accessing inaccessible register' cases we can detect + * at translate time. + */ + switch (*tgtmode) { + case ARM_CPU_MODE_MON: + if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) { + goto undef; + } + if (s->current_el == 1) { + /* If we're in Secure EL1 (which implies that EL3 is AArch64) + * then accesses to Mon registers trap to Secure EL2, if it exists, + * otherwise EL3. + */ + TCGv_i32 tcg_el; + + 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_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_insn_el_v(s, 0, EXCP_UDEF, + syn_uncategorized(), tcg_el); + return false; + } + break; + case ARM_CPU_MODE_HYP: + /* + * 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 != 16 && *regno != 17)) { + goto undef; + } + break; + default: + break; + } + + return true; + +undef: + /* If we get here then some access check did not pass */ + gen_exception_insn(s, 0, EXCP_UDEF, syn_uncategorized()); + return false; +} + +static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) +{ + TCGv_i32 tcg_reg; + int tgtmode = 0, regno = 0; + + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } + + /* Sync state because msr_banked() can raise exceptions */ + gen_set_condexec(s); + gen_update_pc(s, 0); + tcg_reg = load_reg(s, rn); + gen_helper_msr_banked(tcg_env, tcg_reg, + tcg_constant_i32(tgtmode), + tcg_constant_i32(regno)); + s->base.is_jmp = DISAS_UPDATE_EXIT; +} + +static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) +{ + TCGv_i32 tcg_reg; + int tgtmode = 0, regno = 0; + + if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { + return; + } + + /* Sync state because mrs_banked() can raise exceptions */ + gen_set_condexec(s); + gen_update_pc(s, 0); + tcg_reg = tcg_temp_new_i32(); + gen_helper_mrs_banked(tcg_reg, tcg_env, + tcg_constant_i32(tgtmode), + tcg_constant_i32(regno)); + store_reg(s, rn, tcg_reg); + s->base.is_jmp = DISAS_UPDATE_EXIT; +} + +/* Store value to PC as for an exception return (ie don't + * mask bits). The subsequent call to gen_helper_cpsr_write_eret() + * will do the masking based on the new value of the Thumb bit. + */ +static void store_pc_exc_ret(DisasContext *s, TCGv_i32 pc) +{ + tcg_gen_mov_i32(cpu_R[15], pc); +} + +/* Generate a v6 exception return. Marks both values as dead. */ +static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) +{ + store_pc_exc_ret(s, pc); + /* The cpsr_write_eret helper will mask the low bits of PC + * appropriately depending on the new Thumb bit, so it must + * be called after storing the new PC. + */ + 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; +} + +/* Generate an old-style exception return. Marks pc as dead. */ +static void gen_exception_return(DisasContext *s, TCGv_i32 pc) +{ + gen_rfe(s, pc, load_cpu_field(spsr)); +} + +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) +{ + 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; + + /* + * Note that since we are an implementation which takes an + * exception on a trapped conditional instruction only if the + * instruction passes its condition code check, we can take + * advantage of the clause in the ARM ARM that allows us to set + * the COND field in the instruction to 0xE in all cases. + * We could fish the actual condition out of the insn (ARM) + * or the condexec bits (Thumb) but it isn't necessary. + */ + switch (cpnum) { + case 14: + if (is64) { + syndrome = syn_cp14_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, false); + } else { + syndrome = syn_cp14_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, false); + } + break; + case 15: + if (is64) { + syndrome = syn_cp15_rrt_trap(1, 0xe, opc1, crm, rt, rt2, + isread, false); + } else { + syndrome = syn_cp15_rt_trap(1, 0xe, opc1, opc2, crn, crm, + rt, isread, false); + } + break; + default: + /* + * ARMv8 defines that only coprocessors 14 and 15 exist, + * so this can only happen if this is an ARMv7 or earlier CPU, + * in which case the syndrome information won't actually be + * guest visible. + */ + assert(!arm_dc_feature(s, ARM_FEATURE_V8)); + syndrome = syn_uncategorized(); + break; + } + + if (s->hstr_active && cpnum == 15 && s->current_el == 1) { + /* + * At EL1, check for a HSTR_EL2 trap, which must take precedence + * over the UNDEF for "no such register" or the UNDEF for "access + * permissions forbid this EL1 access". HSTR_EL2 traps from EL0 + * only happen if the cpreg doesn't UNDEF at EL0, so we do those in + * access_check_cp_reg(), after the checks for whether the access + * configurably trapped to EL1. + */ + uint32_t maskbit = is64 ? crm : crn; + + if (maskbit != 4 && maskbit != 14) { + /* T4 and T14 are RES0 so never cause traps */ + TCGv_i32 t; + DisasLabel over = gen_disas_label(s); + + t = load_cpu_offset(offsetoflow32(CPUARMState, cp15.hstr_el2)); + tcg_gen_andi_i32(t, t, 1u << maskbit); + tcg_gen_brcondi_i32(TCG_COND_EQ, t, 0, over.label); + + gen_exception_insn_el(s, 0, EXCP_UDEF, syndrome, 2); + /* + * 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. + */ + 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; + } + + /* Check access permissions */ + if (!cp_access_ok(s->current_el, ri, isread)) { + 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) */ +static void disas_xscale_insn(DisasContext *s, uint32_t insn) +{ + int cpnum = (insn >> 8) & 0xf; + + if (extract32(s->c15_cpar, cpnum, 1) == 0) { + unallocated_encoding(s); + } else if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { + if (disas_iwmmxt_insn(s, insn)) { + unallocated_encoding(s); + } + } else if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { + if (disas_dsp_insn(s, insn)) { + unallocated_encoding(s); + } + } +} + +/* Store a 64-bit value to a register pair. Clobbers val. */ +static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) +{ + TCGv_i32 tmp; + tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, val); + store_reg(s, rlow, tmp); + tmp = tcg_temp_new_i32(); + tcg_gen_extrh_i64_i32(tmp, val); + store_reg(s, rhigh, tmp); +} + +/* load and add a 64-bit value from a register pair. */ +static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) +{ + TCGv_i64 tmp; + TCGv_i32 tmpl; + TCGv_i32 tmph; + + /* Load 64-bit value rd:rn. */ + tmpl = load_reg(s, rlow); + tmph = load_reg(s, rhigh); + tmp = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(tmp, tmpl, tmph); + tcg_gen_add_i64(val, val, tmp); +} + +/* Set N and Z flags from hi|lo. */ +static void gen_logicq_cc(TCGv_i32 lo, TCGv_i32 hi) +{ + tcg_gen_mov_i32(cpu_NF, hi); + tcg_gen_or_i32(cpu_ZF, lo, hi); +} + +/* Load/Store exclusive instructions are implemented by remembering + the value/address loaded, and seeing if these are the same + when the store is performed. This should be sufficient to implement + the architecturally mandated semantics, and avoids having to monitor + regular stores. The compare vs the remembered value is done during + the cmpxchg operation, but we must compare the addresses manually. */ +static void gen_load_exclusive(DisasContext *s, int rt, int rt2, + TCGv_i32 addr, int size) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + MemOp opc = size | MO_ALIGN | s->be_data; + + s->is_ldex = true; + + if (size == 3) { + TCGv_i32 tmp2 = tcg_temp_new_i32(); + TCGv_i64 t64 = tcg_temp_new_i64(); + + /* + * For AArch32, architecturally the 32-bit word at the lowest + * address is always Rt and the one at addr+4 is Rt2, even if + * the CPU is big-endian. That means we don't want to do a + * gen_aa32_ld_i64(), which checks SCTLR_B as if for an + * architecturally 64-bit access, but instead do a 64-bit access + * using MO_BE if appropriate and then split the two halves. + */ + TCGv taddr = gen_aa32_addr(s, addr, opc); + + tcg_gen_qemu_ld_i64(t64, taddr, get_mem_index(s), opc); + 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); + } + store_reg(s, rt2, tmp2); + } else { + gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), opc); + tcg_gen_extu_i32_i64(cpu_exclusive_val, tmp); + } + + store_reg(s, rt, tmp); + tcg_gen_extu_i32_i64(cpu_exclusive_addr, addr); +} + +static void gen_clrex(DisasContext *s) +{ + tcg_gen_movi_i64(cpu_exclusive_addr, -1); +} + +static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, + TCGv_i32 addr, int size) +{ + TCGv_i32 t0, t1, t2; + TCGv_i64 extaddr; + TCGv taddr; + TCGLabel *done_label; + TCGLabel *fail_label; + MemOp opc = size | MO_ALIGN | s->be_data; + + /* if (env->exclusive_addr == addr && env->exclusive_val == [addr]) { + [addr] = {Rt}; + {Rd} = 0; + } else { + {Rd} = 1; + } */ + fail_label = gen_new_label(); + done_label = gen_new_label(); + 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); + + taddr = gen_aa32_addr(s, addr, opc); + t0 = tcg_temp_new_i32(); + t1 = load_reg(s, rt); + if (size == 3) { + TCGv_i64 o64 = tcg_temp_new_i64(); + TCGv_i64 n64 = tcg_temp_new_i64(); + + t2 = load_reg(s, rt2); + + /* + * For AArch32, architecturally the 32-bit word at the lowest + * address is always Rt and the one at addr+4 is Rt2, even if + * the CPU is big-endian. Since we're going to treat this as a + * single 64-bit BE store, we need to put the two halves in the + * opposite order for BE to LE, so that they end up in the right + * places. We don't want gen_aa32_st_i64, because that checks + * SCTLR_B as if for an architectural 64-bit access. + */ + if (s->be_data == MO_BE) { + tcg_gen_concat_i32_i64(n64, t2, t1); + } else { + tcg_gen_concat_i32_i64(n64, t1, t2); + } + + tcg_gen_atomic_cmpxchg_i64(o64, taddr, cpu_exclusive_val, n64, + get_mem_index(s), opc); + + tcg_gen_setcond_i64(TCG_COND_NE, o64, o64, cpu_exclusive_val); + tcg_gen_extrl_i64_i32(t0, 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_gen_mov_i32(cpu_R[rd], t0); + tcg_gen_br(done_label); + + gen_set_label(fail_label); + tcg_gen_movi_i32(cpu_R[rd], 1); + gen_set_label(done_label); + tcg_gen_movi_i64(cpu_exclusive_addr, -1); +} + +/* gen_srs: + * @env: CPUARMState + * @s: DisasContext + * @mode: mode field from insn (which stack to store to) + * @amode: addressing mode (DA/IA/DB/IB), encoded as per P,U bits in ARM insn + * @writeback: true if writeback bit set + * + * Generate code for the SRS (Store Return State) insn. + */ +static void gen_srs(DisasContext *s, + uint32_t mode, uint32_t amode, bool writeback) +{ + int32_t offset; + TCGv_i32 addr, tmp; + bool undef = false; + + /* SRS is: + * - trapped to EL3 if EL3 is AArch64 and we are at Secure EL1 + * and specified mode is monitor mode + * - UNDEFINED in Hyp mode + * - UNPREDICTABLE in User or System mode + * - UNPREDICTABLE if the specified mode is: + * -- not implemented + * -- not a valid mode number + * -- a mode that's at a higher exception level + * -- Monitor, if we are Non-secure + * For the UNPREDICTABLE cases we choose to UNDEF. + */ + if (s->current_el == 1 && !s->ns && mode == ARM_CPU_MODE_MON) { + gen_exception_insn_el(s, 0, EXCP_UDEF, syn_uncategorized(), 3); + return; + } + + if (s->current_el == 0 || s->current_el == 2) { + undef = true; + } + + switch (mode) { + case ARM_CPU_MODE_USR: + 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: + break; + case ARM_CPU_MODE_HYP: + if (s->current_el == 1 || !arm_dc_feature(s, ARM_FEATURE_EL2)) { + undef = true; + } + break; + case ARM_CPU_MODE_MON: + /* No need to check specifically for "are we non-secure" because + * we've already made EL0 UNDEF and handled the trap for S-EL1; + * so if this isn't EL3 then we must be non-secure. + */ + if (s->current_el != 3) { + undef = true; + } + break; + default: + undef = true; + } + + if (undef) { + unallocated_encoding(s); + return; + } + + addr = tcg_temp_new_i32(); + /* get_r13_banked() will raise an exception if called from System mode */ + gen_set_condexec(s); + gen_update_pc(s, 0); + gen_helper_get_r13_banked(addr, tcg_env, tcg_constant_i32(mode)); + switch (amode) { + case 0: /* DA */ + offset = -4; + break; + case 1: /* IA */ + offset = 0; + break; + case 2: /* DB */ + offset = -8; + break; + case 3: /* IB */ + offset = 4; + break; + default: + g_assert_not_reached(); + } + 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); + 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); + if (writeback) { + switch (amode) { + case 0: + offset = -8; + break; + case 1: + offset = 4; + break; + case 2: + offset = -4; + break; + case 3: + offset = 0; + break; + default: + g_assert_not_reached(); + } + tcg_gen_addi_i32(addr, addr, offset); + gen_helper_set_r13_banked(tcg_env, tcg_constant_i32(mode), addr); + } + s->base.is_jmp = DISAS_UPDATE_EXIT; +} + +/* Skip this instruction if the ARM condition is false */ +static void arm_skip_unless(DisasContext *s, uint32_t cond) +{ + arm_gen_condlabel(s); + arm_gen_test_cc(cond ^ 1, s->condlabel.label); +} + + +/* + * Constant expanders used by T16/T32 decode + */ + +/* Return only the rotation part of T32ExpandImm. */ +static int t32_expandimm_rot(DisasContext *s, int x) +{ + return x & 0xc00 ? extract32(x, 7, 5) : 0; +} + +/* Return the unrotated immediate from T32ExpandImm. */ +static int t32_expandimm_imm(DisasContext *s, int x) +{ + int imm = extract32(x, 0, 8); + + switch (extract32(x, 8, 4)) { + case 0: /* XY */ + /* Nothing to do. */ + break; + case 1: /* 00XY00XY */ + imm *= 0x00010001; + break; + case 2: /* XY00XY00 */ + imm *= 0x01000100; + break; + case 3: /* XYXYXYXY */ + imm *= 0x01010101; + break; + default: + /* Rotated constant. */ + imm |= 0x80; + break; + } + return imm; +} + +static int t32_branch24(DisasContext *s, int x) +{ + /* Convert J1:J2 at x[22:21] to I2:I1, which involves I=J^~S. */ + x ^= !(x < 0) * (3 << 21); + /* Append the final zero. */ + return x << 1; +} + +static int t16_setflags(DisasContext *s) +{ + return s->condexec_mask == 0; +} + +static int t16_push_list(DisasContext *s, int x) +{ + return (x & 0xff) | (x & 0x100) << (14 - 8); +} + +static int t16_pop_list(DisasContext *s, int x) +{ + return (x & 0xff) | (x & 0x100) << (15 - 8); +} + +/* + * Include the generated decoders. + */ + +#include "decode-a32.c.inc" +#include "decode-a32-uncond.c.inc" +#include "decode-t32.c.inc" +#include "decode-t16.c.inc" + +static bool valid_cp(DisasContext *s, int cp) +{ + /* + * Return true if this coprocessor field indicates something + * that's really a possible coprocessor. + * For v7 and earlier, coprocessors 8..15 were reserved for Arm use, + * and of those only cp14 and cp15 were used for registers. + * cp10 and cp11 were used for VFP and Neon, whose decode is + * dealt with elsewhere. With the advent of fp16, cp9 is also + * now part of VFP. + * For v8A and later, the encoding has been tightened so that + * only cp14 and cp15 are valid, and other values aren't considered + * to be in the coprocessor-instruction space at all. v8M still + * permits coprocessors 0..7. + * For XScale, we must not decode the XScale cp0, cp1 space as + * a standard coprocessor insn, because we want to fall through to + * the legacy disas_xscale_insn() decoder after decodetree is done. + */ + if (arm_dc_feature(s, ARM_FEATURE_XSCALE) && (cp == 0 || cp == 1)) { + return false; + } + + if (arm_dc_feature(s, ARM_FEATURE_V8) && + !arm_dc_feature(s, ARM_FEATURE_M)) { + return cp >= 14; + } + return cp < 8 || cp >= 14; +} + +static bool trans_MCR(DisasContext *s, arg_MCR *a) +{ + if (!valid_cp(s, a->cp)) { + return false; + } + do_coproc_insn(s, a->cp, false, a->opc1, a->crn, a->crm, a->opc2, + false, a->rt, 0); + return true; +} + +static bool trans_MRC(DisasContext *s, arg_MRC *a) +{ + if (!valid_cp(s, a->cp)) { + return false; + } + do_coproc_insn(s, a->cp, false, a->opc1, a->crn, a->crm, a->opc2, + true, a->rt, 0); + return true; +} + +static bool trans_MCRR(DisasContext *s, arg_MCRR *a) +{ + if (!valid_cp(s, a->cp)) { + return false; + } + do_coproc_insn(s, a->cp, true, a->opc1, 0, a->crm, 0, + false, a->rt, a->rt2); + return true; +} + +static bool trans_MRRC(DisasContext *s, arg_MRRC *a) +{ + if (!valid_cp(s, a->cp)) { + return false; + } + do_coproc_insn(s, a->cp, true, a->opc1, 0, a->crm, 0, + true, a->rt, a->rt2); + return true; +} + +/* Helpers to swap operands for reverse-subtract. */ +static void gen_rsb(TCGv_i32 dst, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_sub_i32(dst, b, a); +} + +static void gen_rsb_CC(TCGv_i32 dst, TCGv_i32 a, TCGv_i32 b) +{ + gen_sub_CC(dst, b, a); +} + +static void gen_rsc(TCGv_i32 dest, TCGv_i32 a, TCGv_i32 b) +{ + gen_sub_carry(dest, b, a); +} + +static void gen_rsc_CC(TCGv_i32 dest, TCGv_i32 a, TCGv_i32 b) +{ + gen_sbc_CC(dest, b, a); +} + +/* + * Helpers for the data processing routines. + * + * After the computation store the results back. + * This may be suppressed altogether (STREG_NONE), require a runtime + * check against the stack limits (STREG_SP_CHECK), or generate an + * exception return. Oh, or store into a register. + * + * Always return true, indicating success for a trans_* function. + */ +typedef enum { + STREG_NONE, + STREG_NORMAL, + STREG_SP_CHECK, + STREG_EXC_RET, +} StoreRegKind; + +static bool store_reg_kind(DisasContext *s, int rd, + TCGv_i32 val, StoreRegKind kind) +{ + switch (kind) { + case STREG_NONE: + return true; + case STREG_NORMAL: + /* See ALUWritePC: Interworking only from a32 mode. */ + if (s->thumb) { + store_reg(s, rd, val); + } else { + store_reg_bx(s, rd, val); + } + return true; + case STREG_SP_CHECK: + store_sp_checked(s, val); + return true; + case STREG_EXC_RET: + gen_exception_return(s, val); + return true; + } + g_assert_not_reached(); +} + +/* + * Data Processing (register) + * + * Operate, with set flags, one register source, + * one immediate shifted register source, and a destination. + */ +static bool op_s_rrr_shi(DisasContext *s, arg_s_rrr_shi *a, + void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), + int logic_cc, StoreRegKind kind) +{ + TCGv_i32 tmp1, tmp2; + + tmp2 = load_reg(s, a->rm); + gen_arm_shift_im(tmp2, a->shty, a->shim, logic_cc); + tmp1 = load_reg(s, a->rn); + + gen(tmp1, tmp1, tmp2); + + if (logic_cc) { + gen_logic_CC(tmp1); + } + return store_reg_kind(s, a->rd, tmp1, kind); +} + +static bool op_s_rxr_shi(DisasContext *s, arg_s_rrr_shi *a, + void (*gen)(TCGv_i32, TCGv_i32), + int logic_cc, StoreRegKind kind) +{ + TCGv_i32 tmp; + + tmp = load_reg(s, a->rm); + gen_arm_shift_im(tmp, a->shty, a->shim, logic_cc); + + gen(tmp, tmp); + if (logic_cc) { + gen_logic_CC(tmp); + } + return store_reg_kind(s, a->rd, tmp, kind); +} + +/* + * Data-processing (register-shifted register) + * + * Operate, with set flags, one register source, + * one register shifted register source, and a destination. + */ +static bool op_s_rrr_shr(DisasContext *s, arg_s_rrr_shr *a, + void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), + int logic_cc, StoreRegKind kind) +{ + TCGv_i32 tmp1, tmp2; + + tmp1 = load_reg(s, a->rs); + tmp2 = load_reg(s, a->rm); + gen_arm_shift_reg(tmp2, a->shty, tmp1, logic_cc); + tmp1 = load_reg(s, a->rn); + + gen(tmp1, tmp1, tmp2); + + if (logic_cc) { + gen_logic_CC(tmp1); + } + return store_reg_kind(s, a->rd, tmp1, kind); +} + +static bool op_s_rxr_shr(DisasContext *s, arg_s_rrr_shr *a, + void (*gen)(TCGv_i32, TCGv_i32), + int logic_cc, StoreRegKind kind) +{ + TCGv_i32 tmp1, tmp2; + + tmp1 = load_reg(s, a->rs); + tmp2 = load_reg(s, a->rm); + gen_arm_shift_reg(tmp2, a->shty, tmp1, logic_cc); + + gen(tmp2, tmp2); + if (logic_cc) { + gen_logic_CC(tmp2); + } + return store_reg_kind(s, a->rd, tmp2, kind); +} + +/* + * Data-processing (immediate) + * + * Operate, with set flags, one register source, + * one rotated immediate, and a destination. + * + * Note that logic_cc && a->rot setting CF based on the msb of the + * immediate is the reason why we must pass in the unrotated form + * of the immediate. + */ +static bool op_s_rri_rot(DisasContext *s, arg_s_rri_rot *a, + void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), + int logic_cc, StoreRegKind kind) +{ + TCGv_i32 tmp1; + uint32_t imm; + + imm = ror32(a->imm, a->rot); + if (logic_cc && a->rot) { + tcg_gen_movi_i32(cpu_CF, imm >> 31); + } + tmp1 = load_reg(s, a->rn); + + gen(tmp1, tmp1, tcg_constant_i32(imm)); + + if (logic_cc) { + gen_logic_CC(tmp1); + } + return store_reg_kind(s, a->rd, tmp1, kind); +} + +static bool op_s_rxi_rot(DisasContext *s, arg_s_rri_rot *a, + void (*gen)(TCGv_i32, TCGv_i32), + int logic_cc, StoreRegKind kind) +{ + TCGv_i32 tmp; + uint32_t imm; + + imm = ror32(a->imm, a->rot); + if (logic_cc && a->rot) { + tcg_gen_movi_i32(cpu_CF, imm >> 31); + } + + tmp = tcg_temp_new_i32(); + gen(tmp, tcg_constant_i32(imm)); + + if (logic_cc) { + gen_logic_CC(tmp); + } + return store_reg_kind(s, a->rd, tmp, kind); +} + +#define DO_ANY3(NAME, OP, L, K) \ + static bool trans_##NAME##_rrri(DisasContext *s, arg_s_rrr_shi *a) \ + { StoreRegKind k = (K); return op_s_rrr_shi(s, a, OP, L, k); } \ + static bool trans_##NAME##_rrrr(DisasContext *s, arg_s_rrr_shr *a) \ + { StoreRegKind k = (K); return op_s_rrr_shr(s, a, OP, L, k); } \ + static bool trans_##NAME##_rri(DisasContext *s, arg_s_rri_rot *a) \ + { StoreRegKind k = (K); return op_s_rri_rot(s, a, OP, L, k); } + +#define DO_ANY2(NAME, OP, L, K) \ + static bool trans_##NAME##_rxri(DisasContext *s, arg_s_rrr_shi *a) \ + { StoreRegKind k = (K); return op_s_rxr_shi(s, a, OP, L, k); } \ + static bool trans_##NAME##_rxrr(DisasContext *s, arg_s_rrr_shr *a) \ + { StoreRegKind k = (K); return op_s_rxr_shr(s, a, OP, L, k); } \ + static bool trans_##NAME##_rxi(DisasContext *s, arg_s_rri_rot *a) \ + { StoreRegKind k = (K); return op_s_rxi_rot(s, a, OP, L, k); } + +#define DO_CMP2(NAME, OP, L) \ + static bool trans_##NAME##_xrri(DisasContext *s, arg_s_rrr_shi *a) \ + { return op_s_rrr_shi(s, a, OP, L, STREG_NONE); } \ + static bool trans_##NAME##_xrrr(DisasContext *s, arg_s_rrr_shr *a) \ + { return op_s_rrr_shr(s, a, OP, L, STREG_NONE); } \ + static bool trans_##NAME##_xri(DisasContext *s, arg_s_rri_rot *a) \ + { return op_s_rri_rot(s, a, OP, L, STREG_NONE); } + +DO_ANY3(AND, tcg_gen_and_i32, a->s, STREG_NORMAL) +DO_ANY3(EOR, tcg_gen_xor_i32, a->s, STREG_NORMAL) +DO_ANY3(ORR, tcg_gen_or_i32, a->s, STREG_NORMAL) +DO_ANY3(BIC, tcg_gen_andc_i32, a->s, STREG_NORMAL) + +DO_ANY3(RSB, a->s ? gen_rsb_CC : gen_rsb, false, STREG_NORMAL) +DO_ANY3(ADC, a->s ? gen_adc_CC : gen_add_carry, false, STREG_NORMAL) +DO_ANY3(SBC, a->s ? gen_sbc_CC : gen_sub_carry, false, STREG_NORMAL) +DO_ANY3(RSC, a->s ? gen_rsc_CC : gen_rsc, false, STREG_NORMAL) + +DO_CMP2(TST, tcg_gen_and_i32, true) +DO_CMP2(TEQ, tcg_gen_xor_i32, true) +DO_CMP2(CMN, gen_add_CC, false) +DO_CMP2(CMP, gen_sub_CC, false) + +DO_ANY3(ADD, a->s ? gen_add_CC : tcg_gen_add_i32, false, + a->rd == 13 && a->rn == 13 ? STREG_SP_CHECK : STREG_NORMAL) + +/* + * Note for the computation of StoreRegKind we return out of the + * middle of the functions that are expanded by DO_ANY3, and that + * we modify a->s via that parameter before it is used by OP. + */ +DO_ANY3(SUB, a->s ? gen_sub_CC : tcg_gen_sub_i32, false, + ({ + StoreRegKind ret = STREG_NORMAL; + if (a->rd == 15 && a->s) { + /* + * See ALUExceptionReturn: + * In User mode, UNPREDICTABLE; we choose UNDEF. + * In Hyp mode, UNDEFINED. + */ + if (IS_USER(s) || s->current_el == 2) { + unallocated_encoding(s); + return true; + } + /* There is no writeback of nzcv to PSTATE. */ + a->s = 0; + ret = STREG_EXC_RET; + } else if (a->rd == 13 && a->rn == 13) { + ret = STREG_SP_CHECK; + } + ret; + })) + +DO_ANY2(MOV, tcg_gen_mov_i32, a->s, + ({ + StoreRegKind ret = STREG_NORMAL; + if (a->rd == 15 && a->s) { + /* + * See ALUExceptionReturn: + * In User mode, UNPREDICTABLE; we choose UNDEF. + * In Hyp mode, UNDEFINED. + */ + if (IS_USER(s) || s->current_el == 2) { + unallocated_encoding(s); + return true; + } + /* There is no writeback of nzcv to PSTATE. */ + a->s = 0; + ret = STREG_EXC_RET; + } else if (a->rd == 13) { + ret = STREG_SP_CHECK; + } + ret; + })) + +DO_ANY2(MVN, tcg_gen_not_i32, a->s, STREG_NORMAL) + +/* + * ORN is only available with T32, so there is no register-shifted-register + * form of the insn. Using the DO_ANY3 macro would create an unused function. + */ +static bool trans_ORN_rrri(DisasContext *s, arg_s_rrr_shi *a) +{ + return op_s_rrr_shi(s, a, tcg_gen_orc_i32, a->s, STREG_NORMAL); +} + +static bool trans_ORN_rri(DisasContext *s, arg_s_rri_rot *a) +{ + return op_s_rri_rot(s, a, tcg_gen_orc_i32, a->s, STREG_NORMAL); +} + +#undef DO_ANY3 +#undef DO_ANY2 +#undef DO_CMP2 + +static bool trans_ADR(DisasContext *s, arg_ri *a) +{ + store_reg_bx(s, a->rd, add_reg_for_lit(s, 15, a->imm)); + return true; +} + +static bool trans_MOVW(DisasContext *s, arg_MOVW *a) +{ + if (!ENABLE_ARCH_6T2) { + return false; + } + + store_reg(s, a->rd, tcg_constant_i32(a->imm)); + return true; +} + +static bool trans_MOVT(DisasContext *s, arg_MOVW *a) +{ + TCGv_i32 tmp; + + if (!ENABLE_ARCH_6T2) { + return false; + } + + tmp = load_reg(s, a->rd); + tcg_gen_ext16u_i32(tmp, tmp); + tcg_gen_ori_i32(tmp, tmp, a->imm << 16); + store_reg(s, a->rd, tmp); + return true; +} + +/* + * v8.1M MVE wide-shifts + */ +static bool do_mve_shl_ri(DisasContext *s, arg_mve_shl_ri *a, + WideShiftImmFn *fn) +{ + TCGv_i64 rda; + TCGv_i32 rdalo, rdahi; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { + /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ + return false; + } + if (a->rdahi == 15) { + /* These are a different encoding (SQSHL/SRSHR/UQSHL/URSHR) */ + return false; + } + if (!dc_isar_feature(aa32_mve, s) || + !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || + a->rdahi == 13) { + /* RdaHi == 13 is UNPREDICTABLE; we choose to UNDEF */ + unallocated_encoding(s); + return true; + } + + if (a->shim == 0) { + a->shim = 32; + } + + rda = tcg_temp_new_i64(); + rdalo = load_reg(s, a->rdalo); + rdahi = load_reg(s, a->rdahi); + tcg_gen_concat_i32_i64(rda, rdalo, rdahi); + + fn(rda, rda, a->shim); + + 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); + + return true; +} + +static bool trans_ASRL_ri(DisasContext *s, arg_mve_shl_ri *a) +{ + return do_mve_shl_ri(s, a, tcg_gen_sari_i64); +} + +static bool trans_LSLL_ri(DisasContext *s, arg_mve_shl_ri *a) +{ + return do_mve_shl_ri(s, a, tcg_gen_shli_i64); +} + +static bool trans_LSRL_ri(DisasContext *s, arg_mve_shl_ri *a) +{ + return do_mve_shl_ri(s, a, tcg_gen_shri_i64); +} + +static void gen_mve_sqshll(TCGv_i64 r, TCGv_i64 n, int64_t 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) +{ + return do_mve_shl_ri(s, a, gen_mve_sqshll); +} + +static void gen_mve_uqshll(TCGv_i64 r, TCGv_i64 n, int64_t 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) +{ + return do_mve_shl_ri(s, a, gen_mve_uqshll); +} + +static bool trans_SRSHRL_ri(DisasContext *s, arg_mve_shl_ri *a) +{ + return do_mve_shl_ri(s, a, gen_srshr64_i64); +} + +static bool trans_URSHRL_ri(DisasContext *s, arg_mve_shl_ri *a) +{ + return do_mve_shl_ri(s, a, gen_urshr64_i64); +} + +static bool do_mve_shl_rr(DisasContext *s, arg_mve_shl_rr *a, WideShiftFn *fn) +{ + TCGv_i64 rda; + TCGv_i32 rdalo, rdahi; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { + /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ + return false; + } + if (a->rdahi == 15) { + /* These are a different encoding (SQSHL/SRSHR/UQSHL/URSHR) */ + return false; + } + if (!dc_isar_feature(aa32_mve, s) || + !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || + a->rdahi == 13 || a->rm == 13 || a->rm == 15 || + a->rm == a->rdahi || a->rm == a->rdalo) { + /* These rdahi/rdalo/rm cases are UNPREDICTABLE; we choose to UNDEF */ + unallocated_encoding(s); + return true; + } + + rda = tcg_temp_new_i64(); + rdalo = load_reg(s, a->rdalo); + rdahi = load_reg(s, a->rdahi); + 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, 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); + + return true; +} + +static bool trans_LSLL_rr(DisasContext *s, arg_mve_shl_rr *a) +{ + return do_mve_shl_rr(s, a, gen_helper_mve_ushll); +} + +static bool trans_ASRL_rr(DisasContext *s, arg_mve_shl_rr *a) +{ + return do_mve_shl_rr(s, a, gen_helper_mve_sshrl); +} + +static bool trans_UQRSHLL64_rr(DisasContext *s, arg_mve_shl_rr *a) +{ + return do_mve_shl_rr(s, a, gen_helper_mve_uqrshll); +} + +static bool trans_SQRSHRL64_rr(DisasContext *s, arg_mve_shl_rr *a) +{ + return do_mve_shl_rr(s, a, gen_helper_mve_sqrshrl); +} + +static bool trans_UQRSHLL48_rr(DisasContext *s, arg_mve_shl_rr *a) +{ + return do_mve_shl_rr(s, a, gen_helper_mve_uqrshll48); +} + +static bool trans_SQRSHRL48_rr(DisasContext *s, arg_mve_shl_rr *a) +{ + return do_mve_shl_rr(s, a, gen_helper_mve_sqrshrl48); +} + +static bool do_mve_sh_ri(DisasContext *s, arg_mve_sh_ri *a, ShiftImmFn *fn) +{ + if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { + /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ + return false; + } + if (!dc_isar_feature(aa32_mve, s) || + !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || + a->rda == 13 || a->rda == 15) { + /* These rda cases are UNPREDICTABLE; we choose to UNDEF */ + unallocated_encoding(s); + return true; + } + + if (a->shim == 0) { + a->shim = 32; + } + fn(cpu_R[a->rda], cpu_R[a->rda], a->shim); + + return true; +} + +static bool trans_URSHR_ri(DisasContext *s, arg_mve_sh_ri *a) +{ + return do_mve_sh_ri(s, a, gen_urshr32_i32); +} + +static bool trans_SRSHR_ri(DisasContext *s, arg_mve_sh_ri *a) +{ + return do_mve_sh_ri(s, a, gen_srshr32_i32); +} + +static void gen_mve_sqshl(TCGv_i32 r, TCGv_i32 n, int32_t 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) +{ + return do_mve_sh_ri(s, a, gen_mve_sqshl); +} + +static void gen_mve_uqshl(TCGv_i32 r, TCGv_i32 n, int32_t 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) +{ + return do_mve_sh_ri(s, a, gen_mve_uqshl); +} + +static bool do_mve_sh_rr(DisasContext *s, arg_mve_sh_rr *a, ShiftFn *fn) +{ + if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { + /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ + return false; + } + if (!dc_isar_feature(aa32_mve, s) || + !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || + a->rda == 13 || a->rda == 15 || a->rm == 13 || a->rm == 15 || + a->rm == a->rda) { + /* These rda/rm cases are UNPREDICTABLE; we choose to UNDEF */ + unallocated_encoding(s); + return true; + } + + /* The helper takes care of the sign-extension of the low 8 bits of Rm */ + fn(cpu_R[a->rda], tcg_env, cpu_R[a->rda], cpu_R[a->rm]); + return true; +} + +static bool trans_SQRSHR_rr(DisasContext *s, arg_mve_sh_rr *a) +{ + return do_mve_sh_rr(s, a, gen_helper_mve_sqrshr); +} + +static bool trans_UQRSHL_rr(DisasContext *s, arg_mve_sh_rr *a) +{ + return do_mve_sh_rr(s, a, gen_helper_mve_uqrshl); +} + +/* + * Multiply and multiply accumulate + */ + +static bool op_mla(DisasContext *s, arg_s_rrrr *a, bool add) +{ + TCGv_i32 t1, t2; + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + tcg_gen_mul_i32(t1, t1, t2); + if (add) { + t2 = load_reg(s, a->ra); + tcg_gen_add_i32(t1, t1, t2); + } + if (a->s) { + gen_logic_CC(t1); + } + store_reg(s, a->rd, t1); + return true; +} + +static bool trans_MUL(DisasContext *s, arg_MUL *a) +{ + return op_mla(s, a, false); +} + +static bool trans_MLA(DisasContext *s, arg_MLA *a) +{ + return op_mla(s, a, true); +} + +static bool trans_MLS(DisasContext *s, arg_MLS *a) +{ + TCGv_i32 t1, t2; + + if (!ENABLE_ARCH_6T2) { + return false; + } + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + tcg_gen_mul_i32(t1, t1, t2); + t2 = load_reg(s, a->ra); + tcg_gen_sub_i32(t1, t2, t1); + store_reg(s, a->rd, t1); + return true; +} + +static bool op_mlal(DisasContext *s, arg_s_rrrr *a, bool uns, bool add) +{ + TCGv_i32 t0, t1, t2, t3; + + t0 = load_reg(s, a->rm); + t1 = load_reg(s, a->rn); + if (uns) { + tcg_gen_mulu2_i32(t0, t1, t0, t1); + } else { + tcg_gen_muls2_i32(t0, t1, t0, t1); + } + if (add) { + t2 = load_reg(s, a->ra); + t3 = load_reg(s, a->rd); + tcg_gen_add2_i32(t0, t1, t0, t1, t2, t3); + } + if (a->s) { + gen_logicq_cc(t0, t1); + } + store_reg(s, a->ra, t0); + store_reg(s, a->rd, t1); + return true; +} + +static bool trans_UMULL(DisasContext *s, arg_UMULL *a) +{ + return op_mlal(s, a, true, false); +} + +static bool trans_SMULL(DisasContext *s, arg_SMULL *a) +{ + return op_mlal(s, a, false, false); +} + +static bool trans_UMLAL(DisasContext *s, arg_UMLAL *a) +{ + return op_mlal(s, a, true, true); +} + +static bool trans_SMLAL(DisasContext *s, arg_SMLAL *a) +{ + return op_mlal(s, a, false, true); +} + +static bool trans_UMAAL(DisasContext *s, arg_UMAAL *a) +{ + TCGv_i32 t0, t1, t2, zero; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_6) { + return false; + } + + t0 = load_reg(s, a->rm); + t1 = load_reg(s, a->rn); + tcg_gen_mulu2_i32(t0, t1, t0, t1); + zero = tcg_constant_i32(0); + t2 = load_reg(s, a->ra); + tcg_gen_add2_i32(t0, t1, t0, t1, t2, zero); + t2 = load_reg(s, a->rd); + tcg_gen_add2_i32(t0, t1, t0, t1, t2, zero); + store_reg(s, a->ra, t0); + store_reg(s, a->rd, t1); + return true; +} + +/* + * Saturating addition and subtraction + */ + +static bool op_qaddsub(DisasContext *s, arg_rrr *a, bool add, bool doub) +{ + TCGv_i32 t0, t1; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_5TE) { + return false; + } + + t0 = load_reg(s, a->rm); + t1 = load_reg(s, a->rn); + if (doub) { + gen_helper_add_saturate(t1, tcg_env, t1, t1); + } + if (add) { + gen_helper_add_saturate(t0, tcg_env, t0, t1); + } else { + gen_helper_sub_saturate(t0, tcg_env, t0, t1); + } + store_reg(s, a->rd, t0); + return true; +} + +#define DO_QADDSUB(NAME, ADD, DOUB) \ +static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ +{ \ + return op_qaddsub(s, a, ADD, DOUB); \ +} + +DO_QADDSUB(QADD, true, false) +DO_QADDSUB(QSUB, false, false) +DO_QADDSUB(QDADD, true, true) +DO_QADDSUB(QDSUB, false, true) + +#undef DO_QADDSUB + +/* + * Halfword multiply and multiply accumulate + */ + +static bool op_smlaxxx(DisasContext *s, arg_rrrr *a, + int add_long, bool nt, bool mt) +{ + TCGv_i32 t0, t1, tl, th; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_5TE) { + return false; + } + + t0 = load_reg(s, a->rn); + t1 = load_reg(s, a->rm); + gen_mulxy(t0, t1, nt, mt); + + switch (add_long) { + case 0: + store_reg(s, a->rd, t0); + break; + case 1: + t1 = load_reg(s, a->ra); + gen_helper_add_setq(t0, tcg_env, t0, t1); + store_reg(s, a->rd, t0); + break; + case 2: + tl = load_reg(s, a->ra); + th = load_reg(s, a->rd); + /* Sign-extend the 32-bit product to 64 bits. */ + t1 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t1, t0, 31); + tcg_gen_add2_i32(tl, th, tl, th, t0, t1); + store_reg(s, a->ra, tl); + store_reg(s, a->rd, th); + break; + default: + g_assert_not_reached(); + } + return true; +} + +#define DO_SMLAX(NAME, add, nt, mt) \ +static bool trans_##NAME(DisasContext *s, arg_rrrr *a) \ +{ \ + return op_smlaxxx(s, a, add, nt, mt); \ +} + +DO_SMLAX(SMULBB, 0, 0, 0) +DO_SMLAX(SMULBT, 0, 0, 1) +DO_SMLAX(SMULTB, 0, 1, 0) +DO_SMLAX(SMULTT, 0, 1, 1) + +DO_SMLAX(SMLABB, 1, 0, 0) +DO_SMLAX(SMLABT, 1, 0, 1) +DO_SMLAX(SMLATB, 1, 1, 0) +DO_SMLAX(SMLATT, 1, 1, 1) + +DO_SMLAX(SMLALBB, 2, 0, 0) +DO_SMLAX(SMLALBT, 2, 0, 1) +DO_SMLAX(SMLALTB, 2, 1, 0) +DO_SMLAX(SMLALTT, 2, 1, 1) + +#undef DO_SMLAX + +static bool op_smlawx(DisasContext *s, arg_rrrr *a, bool add, bool mt) +{ + TCGv_i32 t0, t1; + + if (!ENABLE_ARCH_5TE) { + return false; + } + + t0 = load_reg(s, a->rn); + t1 = load_reg(s, a->rm); + /* + * Since the nominal result is product<47:16>, shift the 16-bit + * input up by 16 bits, so that the result is at product<63:32>. + */ + if (mt) { + tcg_gen_andi_i32(t1, t1, 0xffff0000); + } else { + tcg_gen_shli_i32(t1, t1, 16); + } + tcg_gen_muls2_i32(t0, t1, t0, t1); + if (add) { + t0 = load_reg(s, a->ra); + gen_helper_add_setq(t1, tcg_env, t1, t0); + } + store_reg(s, a->rd, t1); + return true; +} + +#define DO_SMLAWX(NAME, add, mt) \ +static bool trans_##NAME(DisasContext *s, arg_rrrr *a) \ +{ \ + return op_smlawx(s, a, add, mt); \ +} + +DO_SMLAWX(SMULWB, 0, 0) +DO_SMLAWX(SMULWT, 0, 1) +DO_SMLAWX(SMLAWB, 1, 0) +DO_SMLAWX(SMLAWT, 1, 1) + +#undef DO_SMLAWX + +/* + * MSR (immediate) and hints + */ + +static bool trans_YIELD(DisasContext *s, arg_YIELD *a) +{ + /* + * When running single-threaded TCG code, use the helper to ensure that + * the next round-robin scheduled vCPU gets a crack. When running in + * MTTCG we don't generate jumps to the helper as it won't affect the + * scheduling of other vCPUs. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_YIELD; + } + return true; +} + +static bool trans_WFE(DisasContext *s, arg_WFE *a) +{ + /* + * When running single-threaded TCG code, use the helper to ensure that + * the next round-robin scheduled vCPU gets a crack. In MTTCG mode we + * just skip this instruction. Currently the SEV/SEVL instructions, + * which are *one* of many ways to wake the CPU from WFE, are not + * implemented so we can't sleep like WFI does. + */ + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFE; + } + return true; +} + +static bool trans_WFI(DisasContext *s, arg_WFI *a) +{ + /* For WFI, halt the vCPU until an IRQ. */ + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFI; + return true; +} + +static bool trans_ESB(DisasContext *s, arg_ESB *a) +{ + /* + * For M-profile, minimal-RAS ESB can be a NOP. + * Without RAS, we must implement this as NOP. + */ + if (!arm_dc_feature(s, ARM_FEATURE_M) && dc_isar_feature(aa32_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 + * AArch32.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_NOP(DisasContext *s, arg_NOP *a) +{ + return true; +} + +static bool trans_MSR_imm(DisasContext *s, arg_MSR_imm *a) +{ + uint32_t val = ror32(a->imm, a->rot * 2); + uint32_t mask = msr_mask(s, a->mask, a->r); + + if (gen_set_psr_im(s, mask, a->r, val)) { + unallocated_encoding(s); + } + return true; +} + +/* + * Cyclic Redundancy Check + */ + +static bool op_crc32(DisasContext *s, arg_rrr *a, bool c, MemOp sz) +{ + TCGv_i32 t1, t2, t3; + + if (!dc_isar_feature(aa32_crc32, s)) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + switch (sz) { + case MO_8: + gen_uxtb(t2); + break; + case MO_16: + gen_uxth(t2); + break; + case MO_32: + break; + default: + g_assert_not_reached(); + } + t3 = tcg_constant_i32(1 << sz); + if (c) { + gen_helper_crc32c(t1, t1, t2, t3); + } else { + gen_helper_crc32(t1, t1, t2, t3); + } + store_reg(s, a->rd, t1); + return true; +} + +#define DO_CRC32(NAME, c, sz) \ +static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ + { return op_crc32(s, a, c, sz); } + +DO_CRC32(CRC32B, false, MO_8) +DO_CRC32(CRC32H, false, MO_16) +DO_CRC32(CRC32W, false, MO_32) +DO_CRC32(CRC32CB, true, MO_8) +DO_CRC32(CRC32CH, true, MO_16) +DO_CRC32(CRC32CW, true, MO_32) + +#undef DO_CRC32 + +/* + * Miscellaneous instructions + */ + +static bool trans_MRS_bank(DisasContext *s, arg_MRS_bank *a) +{ + if (arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + gen_mrs_banked(s, a->r, a->sysm, a->rd); + return true; +} + +static bool trans_MSR_bank(DisasContext *s, arg_MSR_bank *a) +{ + if (arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + gen_msr_banked(s, a->r, a->sysm, a->rn); + return true; +} + +static bool trans_MRS_reg(DisasContext *s, arg_MRS_reg *a) +{ + TCGv_i32 tmp; + + if (arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + if (a->r) { + if (IS_USER(s)) { + unallocated_encoding(s); + return true; + } + tmp = load_cpu_field(spsr); + } else { + tmp = tcg_temp_new_i32(); + gen_helper_cpsr_read(tmp, tcg_env); + } + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_MSR_reg(DisasContext *s, arg_MSR_reg *a) +{ + TCGv_i32 tmp; + uint32_t mask = msr_mask(s, a->mask, a->r); + + if (arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + tmp = load_reg(s, a->rn); + if (gen_set_psr(s, mask, a->r, tmp)) { + unallocated_encoding(s); + } + return true; +} + +static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) +{ + TCGv_i32 tmp; + + if (!arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + tmp = tcg_temp_new_i32(); + gen_helper_v7m_mrs(tmp, tcg_env, tcg_constant_i32(a->sysm)); + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) +{ + TCGv_i32 addr, reg; + + if (!arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + addr = tcg_constant_i32((a->mask << 10) | a->sysm); + reg = load_reg(s, a->rn); + 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); + return true; +} + +static bool trans_BX(DisasContext *s, arg_BX *a) +{ + if (!ENABLE_ARCH_4T) { + return false; + } + gen_bx_excret(s, load_reg(s, a->rm)); + return true; +} + +static bool trans_BXJ(DisasContext *s, arg_BXJ *a) +{ + if (!ENABLE_ARCH_5J || arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + /* + * v7A allows BXJ to be trapped via HSTR.TJDBX. We don't waste a + * TBFLAGS bit on a basically-never-happens case, so call a helper + * function to check for the trap and raise the exception if needed + * (passing it the register number for the syndrome value). + * v8A doesn't have this HSTR bit. + */ + 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(tcg_env, tcg_constant_i32(a->rm)); + } + /* Trivial implementation equivalent to bx. */ + gen_bx(s, load_reg(s, a->rm)); + return true; +} + +static bool trans_BLX_r(DisasContext *s, arg_BLX_r *a) +{ + TCGv_i32 tmp; + + if (!ENABLE_ARCH_5) { + return false; + } + tmp = load_reg(s, a->rm); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); + gen_bx(s, tmp); + return true; +} + +/* + * BXNS/BLXNS: only exist for v8M with the security extensions, + * and always UNDEF if NonSecure. We don't implement these in + * the user-only mode either (in theory you can use them from + * Secure User mode but they are too tied in to system emulation). + */ +static bool trans_BXNS(DisasContext *s, arg_BXNS *a) +{ + if (!s->v8m_secure || IS_USER_ONLY) { + unallocated_encoding(s); + } else { + gen_bxns(s, a->rm); + } + return true; +} + +static bool trans_BLXNS(DisasContext *s, arg_BLXNS *a) +{ + if (!s->v8m_secure || IS_USER_ONLY) { + unallocated_encoding(s); + } else { + gen_blxns(s, a->rm); + } + return true; +} + +static bool trans_CLZ(DisasContext *s, arg_CLZ *a) +{ + TCGv_i32 tmp; + + if (!ENABLE_ARCH_5) { + return false; + } + tmp = load_reg(s, a->rm); + tcg_gen_clzi_i32(tmp, tmp, 32); + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_ERET(DisasContext *s, arg_ERET *a) +{ + TCGv_i32 tmp; + + if (!arm_dc_feature(s, ARM_FEATURE_V7VE)) { + return false; + } + if (IS_USER(s)) { + unallocated_encoding(s); + return true; + } + if (s->current_el == 2) { + /* ERET from Hyp uses ELR_Hyp, not LR */ + tmp = load_cpu_field_low32(elr_el[2]); + } else { + tmp = load_reg(s, 14); + } + gen_exception_return(s, tmp); + return true; +} + +static bool trans_HLT(DisasContext *s, arg_HLT *a) +{ + gen_hlt(s, a->imm); + return true; +} + +static bool trans_BKPT(DisasContext *s, arg_BKPT *a) +{ + if (!ENABLE_ARCH_5) { + return false; + } + /* BKPT is OK with ECI set and leaves it untouched */ + s->eci_handled = true; + if (arm_dc_feature(s, ARM_FEATURE_M) && + semihosting_enabled(s->current_el == 0) && + (a->imm == 0xab)) { + gen_exception_internal_insn(s, EXCP_SEMIHOST); + } else { + gen_exception_bkpt_insn(s, syn_aa32_bkpt(a->imm, false)); + } + return true; +} + +static bool trans_HVC(DisasContext *s, arg_HVC *a) +{ + if (!ENABLE_ARCH_7 || arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + if (IS_USER(s)) { + unallocated_encoding(s); + } else { + gen_hvc(s, a->imm); + } + return true; +} + +static bool trans_SMC(DisasContext *s, arg_SMC *a) +{ + if (!ENABLE_ARCH_6K || arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + if (IS_USER(s)) { + unallocated_encoding(s); + } else { + gen_smc(s); + } + return true; +} + +static bool trans_SG(DisasContext *s, arg_SG *a) +{ + if (!arm_dc_feature(s, ARM_FEATURE_M) || + !arm_dc_feature(s, ARM_FEATURE_V8)) { + return false; + } + /* + * SG (v8M only) + * The bulk of the behaviour for this instruction is implemented + * in v7m_handle_execute_nsc(), which deals with the insn when + * it is executed by a CPU in non-secure state from memory + * which is Secure & NonSecure-Callable. + * Here we only need to handle the remaining cases: + * * in NS memory (including the "security extension not + * implemented" case) : NOP + * * in S memory but CPU already secure (clear IT bits) + * We know that the attribute for the memory this insn is + * in must match the current CPU state, because otherwise + * get_phys_addr_pmsav8 would have generated an exception. + */ + if (s->v8m_secure) { + /* Like the IT insn, we don't need to generate any code */ + s->condexec_cond = 0; + s->condexec_mask = 0; + } + return true; +} + +static bool trans_TT(DisasContext *s, arg_TT *a) +{ + TCGv_i32 addr, tmp; + + if (!arm_dc_feature(s, ARM_FEATURE_M) || + !arm_dc_feature(s, ARM_FEATURE_V8)) { + return false; + } + if (a->rd == 13 || a->rd == 15 || a->rn == 15) { + /* We UNDEF for these UNPREDICTABLE cases */ + unallocated_encoding(s); + return true; + } + if (a->A && !s->v8m_secure) { + /* This case is UNDEFINED. */ + unallocated_encoding(s); + return true; + } + + addr = load_reg(s, a->rn); + tmp = tcg_temp_new_i32(); + gen_helper_v7m_tt(tmp, tcg_env, addr, tcg_constant_i32((a->A << 1) | a->T)); + store_reg(s, a->rd, tmp); + return true; +} + +/* + * Load/store register index + */ + +static ISSInfo make_issinfo(DisasContext *s, int rd, bool p, bool w) +{ + ISSInfo ret; + + /* ISS not valid if writeback */ + if (p && !w) { + ret = rd; + if (curr_insn_len(s) == 2) { + ret |= ISSIs16Bit; + } + } else { + ret = ISSInvalid; + } + return ret; +} + +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(tcg_env, addr); + } + + if (a->p) { + TCGv_i32 ofs = load_reg(s, a->rm); + gen_arm_shift_im(ofs, a->shtype, a->shimm, 0); + if (a->u) { + tcg_gen_add_i32(addr, addr, ofs); + } else { + tcg_gen_sub_i32(addr, addr, ofs); + } + } + return addr; +} + +static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, + TCGv_i32 addr, int address_offset) +{ + if (!a->p) { + TCGv_i32 ofs = load_reg(s, a->rm); + gen_arm_shift_im(ofs, a->shtype, a->shimm, 0); + if (a->u) { + tcg_gen_add_i32(addr, addr, ofs); + } else { + tcg_gen_sub_i32(addr, addr, ofs); + } + } else if (!a->w) { + return; + } + tcg_gen_addi_i32(addr, addr, address_offset); + store_reg(s, a->rn, addr); +} + +static bool op_load_rr(DisasContext *s, arg_ldst_rr *a, + MemOp mop, int mem_idx) +{ + ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w); + TCGv_i32 addr, tmp; + + addr = op_addr_rr_pre(s, a); + + tmp = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop); + disas_set_da_iss(s, mop, issinfo); + + /* + * Perform base writeback before the loaded value to + * ensure correct behavior with overlapping index registers. + */ + op_addr_rr_post(s, a, addr, 0); + store_reg_from_load(s, a->rt, tmp); + return true; +} + +static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, + MemOp mop, int mem_idx) +{ + ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w) | ISSIsWrite; + TCGv_i32 addr, tmp; + + /* + * In Thumb encodings of stores Rn=1111 is UNDEF; for Arm it + * is either UNPREDICTABLE or has defined behaviour + */ + if (s->thumb && a->rn == 15) { + return false; + } + + addr = op_addr_rr_pre(s, a); + + tmp = load_reg(s, a->rt); + gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); + disas_set_da_iss(s, mop, issinfo); + + op_addr_rr_post(s, a, addr, 0); + return true; +} + +static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) +{ + int mem_idx = get_mem_index(s); + TCGv_i32 addr, tmp; + + if (!ENABLE_ARCH_5TE) { + return false; + } + if (a->rt & 1) { + unallocated_encoding(s); + return true; + } + addr = op_addr_rr_pre(s, a); + + tmp = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + store_reg(s, a->rt, tmp); + + tcg_gen_addi_i32(addr, addr, 4); + + tmp = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + store_reg(s, a->rt + 1, tmp); + + /* LDRD w/ base writeback is undefined if the registers overlap. */ + op_addr_rr_post(s, a, addr, -4); + return true; +} + +static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) +{ + int mem_idx = get_mem_index(s); + TCGv_i32 addr, tmp; + + if (!ENABLE_ARCH_5TE) { + return false; + } + if (a->rt & 1) { + unallocated_encoding(s); + return true; + } + addr = op_addr_rr_pre(s, a); + + tmp = load_reg(s, a->rt); + gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + + 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); + + op_addr_rr_post(s, a, addr, -4); + return true; +} + +/* + * Load/store immediate index + */ + +static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) +{ + int ofs = a->imm; + + if (!a->u) { + ofs = -ofs; + } + + if (s->v8m_stackcheck && a->rn == 13 && a->w) { + /* + * Stackcheck. Here we know 'addr' is the current SP; + * U is set if we're moving SP up, else down. It is + * UNKNOWN whether the limit check triggers when SP starts + * below the limit and ends up above it; we chose to do so. + */ + if (!a->u) { + TCGv_i32 newsp = tcg_temp_new_i32(); + tcg_gen_addi_i32(newsp, cpu_R[13], ofs); + gen_helper_v8m_stackcheck(tcg_env, newsp); + } else { + gen_helper_v8m_stackcheck(tcg_env, cpu_R[13]); + } + } + + return add_reg_for_lit(s, a->rn, a->p ? ofs : 0); +} + +static void op_addr_ri_post(DisasContext *s, arg_ldst_ri *a, + TCGv_i32 addr, int address_offset) +{ + if (!a->p) { + if (a->u) { + address_offset += a->imm; + } else { + address_offset -= a->imm; + } + } else if (!a->w) { + return; + } + tcg_gen_addi_i32(addr, addr, address_offset); + store_reg(s, a->rn, addr); +} + +static bool op_load_ri(DisasContext *s, arg_ldst_ri *a, + MemOp mop, int mem_idx) +{ + ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w); + TCGv_i32 addr, tmp; + + addr = op_addr_ri_pre(s, a); + + tmp = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop); + disas_set_da_iss(s, mop, issinfo); + + /* + * Perform base writeback before the loaded value to + * ensure correct behavior with overlapping index registers. + */ + op_addr_ri_post(s, a, addr, 0); + store_reg_from_load(s, a->rt, tmp); + return true; +} + +static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, + MemOp mop, int mem_idx) +{ + ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w) | ISSIsWrite; + TCGv_i32 addr, tmp; + + /* + * In Thumb encodings of stores Rn=1111 is UNDEF; for Arm it + * is either UNPREDICTABLE or has defined behaviour + */ + if (s->thumb && a->rn == 15) { + return false; + } + + addr = op_addr_ri_pre(s, a); + + tmp = load_reg(s, a->rt); + gen_aa32_st_i32(s, tmp, addr, mem_idx, mop); + disas_set_da_iss(s, mop, issinfo); + + op_addr_ri_post(s, a, addr, 0); + return true; +} + +static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) +{ + int mem_idx = get_mem_index(s); + TCGv_i32 addr, tmp; + + addr = op_addr_ri_pre(s, a); + + tmp = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + store_reg(s, a->rt, tmp); + + tcg_gen_addi_i32(addr, addr, 4); + + tmp = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + store_reg(s, rt2, tmp); + + /* LDRD w/ base writeback is undefined if the registers overlap. */ + op_addr_ri_post(s, a, addr, -4); + return true; +} + +static bool trans_LDRD_ri_a32(DisasContext *s, arg_ldst_ri *a) +{ + if (!ENABLE_ARCH_5TE || (a->rt & 1)) { + return false; + } + return op_ldrd_ri(s, a, a->rt + 1); +} + +static bool trans_LDRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) +{ + arg_ldst_ri b = { + .u = a->u, .w = a->w, .p = a->p, + .rn = a->rn, .rt = a->rt, .imm = a->imm + }; + return op_ldrd_ri(s, &b, a->rt2); +} + +static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) +{ + int mem_idx = get_mem_index(s); + TCGv_i32 addr, tmp; + + addr = op_addr_ri_pre(s, a); + + tmp = load_reg(s, a->rt); + gen_aa32_st_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); + + 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); + + op_addr_ri_post(s, a, addr, -4); + return true; +} + +static bool trans_STRD_ri_a32(DisasContext *s, arg_ldst_ri *a) +{ + if (!ENABLE_ARCH_5TE || (a->rt & 1)) { + return false; + } + return op_strd_ri(s, a, a->rt + 1); +} + +static bool trans_STRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) +{ + arg_ldst_ri b = { + .u = a->u, .w = a->w, .p = a->p, + .rn = a->rn, .rt = a->rt, .imm = a->imm + }; + return op_strd_ri(s, &b, a->rt2); +} + +#define DO_LDST(NAME, WHICH, MEMOP) \ +static bool trans_##NAME##_ri(DisasContext *s, arg_ldst_ri *a) \ +{ \ + return op_##WHICH##_ri(s, a, MEMOP, get_mem_index(s)); \ +} \ +static bool trans_##NAME##T_ri(DisasContext *s, arg_ldst_ri *a) \ +{ \ + return op_##WHICH##_ri(s, a, MEMOP, get_a32_user_mem_index(s)); \ +} \ +static bool trans_##NAME##_rr(DisasContext *s, arg_ldst_rr *a) \ +{ \ + return op_##WHICH##_rr(s, a, MEMOP, get_mem_index(s)); \ +} \ +static bool trans_##NAME##T_rr(DisasContext *s, arg_ldst_rr *a) \ +{ \ + return op_##WHICH##_rr(s, a, MEMOP, get_a32_user_mem_index(s)); \ +} + +DO_LDST(LDR, load, MO_UL) +DO_LDST(LDRB, load, MO_UB) +DO_LDST(LDRH, load, MO_UW) +DO_LDST(LDRSB, load, MO_SB) +DO_LDST(LDRSH, load, MO_SW) + +DO_LDST(STR, store, MO_UL) +DO_LDST(STRB, store, MO_UB) +DO_LDST(STRH, store, MO_UW) + +#undef DO_LDST + +/* + * Synchronization primitives + */ + +static bool op_swp(DisasContext *s, arg_SWP *a, MemOp opc) +{ + TCGv_i32 addr, tmp; + TCGv taddr; + + opc |= s->be_data; + addr = load_reg(s, a->rn); + taddr = gen_aa32_addr(s, addr, opc); + + tmp = load_reg(s, a->rt2); + tcg_gen_atomic_xchg_i32(tmp, taddr, tmp, get_mem_index(s), opc); + + store_reg(s, a->rt, tmp); + return true; +} + +static bool trans_SWP(DisasContext *s, arg_SWP *a) +{ + return op_swp(s, a, MO_UL | MO_ALIGN); +} + +static bool trans_SWPB(DisasContext *s, arg_SWP *a) +{ + return op_swp(s, a, MO_UB); +} + +/* + * Load/Store Exclusive and Load-Acquire/Store-Release + */ + +static bool op_strex(DisasContext *s, arg_STREX *a, MemOp mop, bool rel) +{ + TCGv_i32 addr; + /* Some cases stopped being UNPREDICTABLE in v8A (but not v8M) */ + bool v8a = ENABLE_ARCH_8 && !arm_dc_feature(s, ARM_FEATURE_M); + + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rd == 15 || a->rn == 15 || a->rt == 15 + || a->rd == a->rn || a->rd == a->rt + || (!v8a && s->thumb && (a->rd == 13 || a->rt == 13)) + || (mop == MO_64 + && (a->rt2 == 15 + || a->rd == a->rt2 + || (!v8a && s->thumb && a->rt2 == 13)))) { + unallocated_encoding(s); + return true; + } + + if (rel) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + + 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); + return true; +} + +static bool trans_STREX(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_6) { + return false; + } + return op_strex(s, a, MO_32, false); +} + +static bool trans_STREXD_a32(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_6K) { + return false; + } + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rt & 1) { + unallocated_encoding(s); + return true; + } + a->rt2 = a->rt + 1; + return op_strex(s, a, MO_64, false); +} + +static bool trans_STREXD_t32(DisasContext *s, arg_STREX *a) +{ + return op_strex(s, a, MO_64, false); +} + +static bool trans_STREXB(DisasContext *s, arg_STREX *a) +{ + if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { + return false; + } + return op_strex(s, a, MO_8, false); +} + +static bool trans_STREXH(DisasContext *s, arg_STREX *a) +{ + if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { + return false; + } + return op_strex(s, a, MO_16, false); +} + +static bool trans_STLEX(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_strex(s, a, MO_32, true); +} + +static bool trans_STLEXD_a32(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rt & 1) { + unallocated_encoding(s); + return true; + } + a->rt2 = a->rt + 1; + return op_strex(s, a, MO_64, true); +} + +static bool trans_STLEXD_t32(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_strex(s, a, MO_64, true); +} + +static bool trans_STLEXB(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_strex(s, a, MO_8, true); +} + +static bool trans_STLEXH(DisasContext *s, arg_STREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_strex(s, a, MO_16, true); +} + +static bool op_stl(DisasContext *s, arg_STL *a, MemOp mop) +{ + TCGv_i32 addr, tmp; + + if (!ENABLE_ARCH_8) { + return false; + } + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rn == 15 || a->rt == 15) { + unallocated_encoding(s); + return true; + } + + addr = load_reg(s, a->rn); + tmp = load_reg(s, a->rt); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), mop | MO_ALIGN); + disas_set_da_iss(s, mop, a->rt | ISSIsAcqRel | ISSIsWrite); + + return true; +} + +static bool trans_STL(DisasContext *s, arg_STL *a) +{ + return op_stl(s, a, MO_UL); +} + +static bool trans_STLB(DisasContext *s, arg_STL *a) +{ + return op_stl(s, a, MO_UB); +} + +static bool trans_STLH(DisasContext *s, arg_STL *a) +{ + return op_stl(s, a, MO_UW); +} + +static bool op_ldrex(DisasContext *s, arg_LDREX *a, MemOp mop, bool acq) +{ + TCGv_i32 addr; + /* Some cases stopped being UNPREDICTABLE in v8A (but not v8M) */ + bool v8a = ENABLE_ARCH_8 && !arm_dc_feature(s, ARM_FEATURE_M); + + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rn == 15 || a->rt == 15 + || (!v8a && s->thumb && a->rt == 13) + || (mop == MO_64 + && (a->rt2 == 15 || a->rt == a->rt2 + || (!v8a && s->thumb && a->rt2 == 13)))) { + unallocated_encoding(s); + return true; + } + + 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); + + if (acq) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return true; +} + +static bool trans_LDREX(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_6) { + return false; + } + return op_ldrex(s, a, MO_32, false); +} + +static bool trans_LDREXD_a32(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_6K) { + return false; + } + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rt & 1) { + unallocated_encoding(s); + return true; + } + a->rt2 = a->rt + 1; + return op_ldrex(s, a, MO_64, false); +} + +static bool trans_LDREXD_t32(DisasContext *s, arg_LDREX *a) +{ + return op_ldrex(s, a, MO_64, false); +} + +static bool trans_LDREXB(DisasContext *s, arg_LDREX *a) +{ + if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { + return false; + } + return op_ldrex(s, a, MO_8, false); +} + +static bool trans_LDREXH(DisasContext *s, arg_LDREX *a) +{ + if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { + return false; + } + return op_ldrex(s, a, MO_16, false); +} + +static bool trans_LDAEX(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_ldrex(s, a, MO_32, true); +} + +static bool trans_LDAEXD_a32(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rt & 1) { + unallocated_encoding(s); + return true; + } + a->rt2 = a->rt + 1; + return op_ldrex(s, a, MO_64, true); +} + +static bool trans_LDAEXD_t32(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_ldrex(s, a, MO_64, true); +} + +static bool trans_LDAEXB(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_ldrex(s, a, MO_8, true); +} + +static bool trans_LDAEXH(DisasContext *s, arg_LDREX *a) +{ + if (!ENABLE_ARCH_8) { + return false; + } + return op_ldrex(s, a, MO_16, true); +} + +static bool op_lda(DisasContext *s, arg_LDA *a, MemOp mop) +{ + TCGv_i32 addr, tmp; + + if (!ENABLE_ARCH_8) { + return false; + } + /* We UNDEF for these UNPREDICTABLE cases. */ + if (a->rn == 15 || a->rt == 15) { + unallocated_encoding(s); + return true; + } + + addr = load_reg(s, a->rn); + 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); + + store_reg(s, a->rt, tmp); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + return true; +} + +static bool trans_LDA(DisasContext *s, arg_LDA *a) +{ + return op_lda(s, a, MO_UL); +} + +static bool trans_LDAB(DisasContext *s, arg_LDA *a) +{ + return op_lda(s, a, MO_UB); +} + +static bool trans_LDAH(DisasContext *s, arg_LDA *a) +{ + return op_lda(s, a, MO_UW); +} + +/* + * Media instructions + */ + +static bool trans_USADA8(DisasContext *s, arg_USADA8 *a) +{ + TCGv_i32 t1, t2; + + if (!ENABLE_ARCH_6) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + gen_helper_usad8(t1, t1, t2); + if (a->ra != 15) { + t2 = load_reg(s, a->ra); + tcg_gen_add_i32(t1, t1, t2); + } + store_reg(s, a->rd, t1); + return true; +} + +static bool op_bfx(DisasContext *s, arg_UBFX *a, bool u) +{ + TCGv_i32 tmp; + int width = a->widthm1 + 1; + int shift = a->lsb; + + if (!ENABLE_ARCH_6T2) { + return false; + } + if (shift + width > 32) { + /* UNPREDICTABLE; we choose to UNDEF */ + unallocated_encoding(s); + return true; + } + + tmp = load_reg(s, a->rn); + if (u) { + tcg_gen_extract_i32(tmp, tmp, shift, width); + } else { + tcg_gen_sextract_i32(tmp, tmp, shift, width); + } + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_SBFX(DisasContext *s, arg_SBFX *a) +{ + return op_bfx(s, a, false); +} + +static bool trans_UBFX(DisasContext *s, arg_UBFX *a) +{ + return op_bfx(s, a, true); +} + +static bool trans_BFCI(DisasContext *s, arg_BFCI *a) +{ + int msb = a->msb, lsb = a->lsb; + TCGv_i32 t_in, t_rd; + int width; + + if (!ENABLE_ARCH_6T2) { + return false; + } + if (msb < lsb) { + /* UNPREDICTABLE; we choose to UNDEF */ + unallocated_encoding(s); + return true; + } + + width = msb + 1 - lsb; + if (a->rn == 15) { + /* BFC */ + t_in = tcg_constant_i32(0); + } else { + /* BFI */ + t_in = load_reg(s, a->rn); + } + 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; +} + +static bool trans_UDF(DisasContext *s, arg_UDF *a) +{ + unallocated_encoding(s); + return true; +} + +/* + * Parallel addition and subtraction + */ + +static bool op_par_addsub(DisasContext *s, arg_rrr *a, + void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 t0, t1; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_6) { + return false; + } + + t0 = load_reg(s, a->rn); + t1 = load_reg(s, a->rm); + + gen(t0, t0, t1); + + store_reg(s, a->rd, t0); + return true; +} + +static bool op_par_addsub_ge(DisasContext *s, arg_rrr *a, + void (*gen)(TCGv_i32, TCGv_i32, + TCGv_i32, TCGv_ptr)) +{ + TCGv_i32 t0, t1; + TCGv_ptr ge; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_6) { + return false; + } + + t0 = load_reg(s, a->rn); + t1 = load_reg(s, a->rm); + + ge = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ge, tcg_env, offsetof(CPUARMState, GE)); + gen(t0, t0, t1, ge); + + store_reg(s, a->rd, t0); + return true; +} + +#define DO_PAR_ADDSUB(NAME, helper) \ +static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ +{ \ + return op_par_addsub(s, a, helper); \ +} + +#define DO_PAR_ADDSUB_GE(NAME, helper) \ +static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ +{ \ + return op_par_addsub_ge(s, a, helper); \ +} + +DO_PAR_ADDSUB_GE(SADD16, gen_helper_sadd16) +DO_PAR_ADDSUB_GE(SASX, gen_helper_saddsubx) +DO_PAR_ADDSUB_GE(SSAX, gen_helper_ssubaddx) +DO_PAR_ADDSUB_GE(SSUB16, gen_helper_ssub16) +DO_PAR_ADDSUB_GE(SADD8, gen_helper_sadd8) +DO_PAR_ADDSUB_GE(SSUB8, gen_helper_ssub8) + +DO_PAR_ADDSUB_GE(UADD16, gen_helper_uadd16) +DO_PAR_ADDSUB_GE(UASX, gen_helper_uaddsubx) +DO_PAR_ADDSUB_GE(USAX, gen_helper_usubaddx) +DO_PAR_ADDSUB_GE(USUB16, gen_helper_usub16) +DO_PAR_ADDSUB_GE(UADD8, gen_helper_uadd8) +DO_PAR_ADDSUB_GE(USUB8, gen_helper_usub8) + +DO_PAR_ADDSUB(QADD16, gen_helper_qadd16) +DO_PAR_ADDSUB(QASX, gen_helper_qaddsubx) +DO_PAR_ADDSUB(QSAX, gen_helper_qsubaddx) +DO_PAR_ADDSUB(QSUB16, gen_helper_qsub16) +DO_PAR_ADDSUB(QADD8, gen_helper_qadd8) +DO_PAR_ADDSUB(QSUB8, gen_helper_qsub8) + +DO_PAR_ADDSUB(UQADD16, gen_helper_uqadd16) +DO_PAR_ADDSUB(UQASX, gen_helper_uqaddsubx) +DO_PAR_ADDSUB(UQSAX, gen_helper_uqsubaddx) +DO_PAR_ADDSUB(UQSUB16, gen_helper_uqsub16) +DO_PAR_ADDSUB(UQADD8, gen_helper_uqadd8) +DO_PAR_ADDSUB(UQSUB8, gen_helper_uqsub8) + +DO_PAR_ADDSUB(SHADD16, gen_helper_shadd16) +DO_PAR_ADDSUB(SHASX, gen_helper_shaddsubx) +DO_PAR_ADDSUB(SHSAX, gen_helper_shsubaddx) +DO_PAR_ADDSUB(SHSUB16, gen_helper_shsub16) +DO_PAR_ADDSUB(SHADD8, gen_helper_shadd8) +DO_PAR_ADDSUB(SHSUB8, gen_helper_shsub8) + +DO_PAR_ADDSUB(UHADD16, gen_helper_uhadd16) +DO_PAR_ADDSUB(UHASX, gen_helper_uhaddsubx) +DO_PAR_ADDSUB(UHSAX, gen_helper_uhsubaddx) +DO_PAR_ADDSUB(UHSUB16, gen_helper_uhsub16) +DO_PAR_ADDSUB(UHADD8, gen_helper_uhadd8) +DO_PAR_ADDSUB(UHSUB8, gen_helper_uhsub8) + +#undef DO_PAR_ADDSUB +#undef DO_PAR_ADDSUB_GE + +/* + * Packing, unpacking, saturation, and reversal + */ + +static bool trans_PKH(DisasContext *s, arg_PKH *a) +{ + TCGv_i32 tn, tm; + int shift = a->imm; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_6) { + return false; + } + + tn = load_reg(s, a->rn); + tm = load_reg(s, a->rm); + if (a->tb) { + /* PKHTB */ + if (shift == 0) { + shift = 31; + } + tcg_gen_sari_i32(tm, tm, shift); + tcg_gen_deposit_i32(tn, tn, tm, 0, 16); + } else { + /* PKHBT */ + tcg_gen_shli_i32(tm, tm, shift); + tcg_gen_deposit_i32(tn, tm, tn, 0, 16); + } + store_reg(s, a->rd, tn); + return true; +} + +static bool op_sat(DisasContext *s, arg_sat *a, + void (*gen)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 tmp; + int shift = a->imm; + + if (!ENABLE_ARCH_6) { + return false; + } + + tmp = load_reg(s, a->rn); + if (a->sh) { + tcg_gen_sari_i32(tmp, tmp, shift ? shift : 31); + } else { + tcg_gen_shli_i32(tmp, tmp, shift); + } + + gen(tmp, tcg_env, tmp, tcg_constant_i32(a->satimm)); + + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_SSAT(DisasContext *s, arg_sat *a) +{ + return op_sat(s, a, gen_helper_ssat); +} + +static bool trans_USAT(DisasContext *s, arg_sat *a) +{ + return op_sat(s, a, gen_helper_usat); +} + +static bool trans_SSAT16(DisasContext *s, arg_sat *a) +{ + if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + return false; + } + return op_sat(s, a, gen_helper_ssat16); +} + +static bool trans_USAT16(DisasContext *s, arg_sat *a) +{ + if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + return false; + } + return op_sat(s, a, gen_helper_usat16); +} + +static bool op_xta(DisasContext *s, arg_rrr_rot *a, + void (*gen_extract)(TCGv_i32, TCGv_i32), + void (*gen_add)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 tmp; + + if (!ENABLE_ARCH_6) { + return false; + } + + tmp = load_reg(s, a->rm); + /* + * TODO: In many cases we could do a shift instead of a rotate. + * Combined with a simple extend, that becomes an extract. + */ + tcg_gen_rotri_i32(tmp, tmp, a->rot * 8); + gen_extract(tmp, tmp); + + if (a->rn != 15) { + TCGv_i32 tmp2 = load_reg(s, a->rn); + gen_add(tmp, tmp, tmp2); + } + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_SXTAB(DisasContext *s, arg_rrr_rot *a) +{ + return op_xta(s, a, tcg_gen_ext8s_i32, tcg_gen_add_i32); +} + +static bool trans_SXTAH(DisasContext *s, arg_rrr_rot *a) +{ + return op_xta(s, a, tcg_gen_ext16s_i32, tcg_gen_add_i32); +} + +static bool trans_SXTAB16(DisasContext *s, arg_rrr_rot *a) +{ + if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + return false; + } + return op_xta(s, a, gen_helper_sxtb16, gen_add16); +} + +static bool trans_UXTAB(DisasContext *s, arg_rrr_rot *a) +{ + return op_xta(s, a, tcg_gen_ext8u_i32, tcg_gen_add_i32); +} + +static bool trans_UXTAH(DisasContext *s, arg_rrr_rot *a) +{ + return op_xta(s, a, tcg_gen_ext16u_i32, tcg_gen_add_i32); +} + +static bool trans_UXTAB16(DisasContext *s, arg_rrr_rot *a) +{ + if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { + return false; + } + return op_xta(s, a, gen_helper_uxtb16, gen_add16); +} + +static bool trans_SEL(DisasContext *s, arg_rrr *a) +{ + TCGv_i32 t1, t2, t3; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_6) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + t3 = tcg_temp_new_i32(); + tcg_gen_ld_i32(t3, tcg_env, offsetof(CPUARMState, GE)); + gen_helper_sel_flags(t1, t3, t1, t2); + store_reg(s, a->rd, t1); + return true; +} + +static bool op_rr(DisasContext *s, arg_rr *a, + void (*gen)(TCGv_i32, TCGv_i32)) +{ + TCGv_i32 tmp; + + tmp = load_reg(s, a->rm); + gen(tmp, tmp); + store_reg(s, a->rd, tmp); + return true; +} + +static bool trans_REV(DisasContext *s, arg_rr *a) +{ + if (!ENABLE_ARCH_6) { + return false; + } + return op_rr(s, a, tcg_gen_bswap32_i32); +} + +static bool trans_REV16(DisasContext *s, arg_rr *a) +{ + if (!ENABLE_ARCH_6) { + return false; + } + return op_rr(s, a, gen_rev16); +} + +static bool trans_REVSH(DisasContext *s, arg_rr *a) +{ + if (!ENABLE_ARCH_6) { + return false; + } + return op_rr(s, a, gen_revsh); +} + +static bool trans_RBIT(DisasContext *s, arg_rr *a) +{ + if (!ENABLE_ARCH_6T2) { + return false; + } + return op_rr(s, a, gen_helper_rbit); +} + +/* + * Signed multiply, signed and unsigned divide + */ + +static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) +{ + TCGv_i32 t1, t2; + + if (!ENABLE_ARCH_6) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + if (m_swap) { + gen_swap_half(t2, t2); + } + gen_smul_dual(t1, t2); + + if (sub) { + /* + * This subtraction cannot overflow, so we can do a simple + * 32-bit subtraction and then a possible 32-bit saturating + * addition of Ra. + */ + tcg_gen_sub_i32(t1, t1, t2); + + if (a->ra != 15) { + t2 = load_reg(s, a->ra); + gen_helper_add_setq(t1, tcg_env, t1, t2); + } + } else if (a->ra == 15) { + /* Single saturation-checking addition */ + gen_helper_add_setq(t1, tcg_env, t1, t2); + } else { + /* + * We need to add the products and Ra together and then + * determine whether the final result overflowed. Doing + * this as two separate add-and-check-overflow steps incorrectly + * sets Q for cases like (-32768 * -32768) + (-32768 * -32768) + -1. + * Do all the arithmetic at 64-bits and then check for overflow. + */ + TCGv_i64 p64, q64; + TCGv_i32 t3, qf, one; + + p64 = tcg_temp_new_i64(); + q64 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(p64, t1); + tcg_gen_ext_i32_i64(q64, t2); + tcg_gen_add_i64(p64, p64, q64); + load_reg_var(s, t2, a->ra); + tcg_gen_ext_i32_i64(q64, t2); + tcg_gen_add_i64(p64, p64, q64); + + tcg_gen_extr_i64_i32(t1, t2, 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) + * is different from the sign-extension of t1. + */ + t3 = tcg_temp_new_i32(); + tcg_gen_sari_i32(t3, t1, 31); + qf = load_cpu_field(QF); + one = tcg_constant_i32(1); + tcg_gen_movcond_i32(TCG_COND_NE, qf, t2, t3, one, qf); + store_cpu_field(qf, QF); + } + store_reg(s, a->rd, t1); + return true; +} + +static bool trans_SMLAD(DisasContext *s, arg_rrrr *a) +{ + return op_smlad(s, a, false, false); +} + +static bool trans_SMLADX(DisasContext *s, arg_rrrr *a) +{ + return op_smlad(s, a, true, false); +} + +static bool trans_SMLSD(DisasContext *s, arg_rrrr *a) +{ + return op_smlad(s, a, false, true); +} + +static bool trans_SMLSDX(DisasContext *s, arg_rrrr *a) +{ + return op_smlad(s, a, true, true); +} + +static bool op_smlald(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) +{ + TCGv_i32 t1, t2; + TCGv_i64 l1, l2; + + if (!ENABLE_ARCH_6) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + if (m_swap) { + gen_swap_half(t2, t2); + } + gen_smul_dual(t1, t2); + + l1 = tcg_temp_new_i64(); + l2 = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(l1, t1); + tcg_gen_ext_i32_i64(l2, t2); + + if (sub) { + tcg_gen_sub_i64(l1, l1, l2); + } else { + tcg_gen_add_i64(l1, l1, l2); + } + + gen_addq(s, l1, a->ra, a->rd); + gen_storeq_reg(s, a->ra, a->rd, l1); + return true; +} + +static bool trans_SMLALD(DisasContext *s, arg_rrrr *a) +{ + return op_smlald(s, a, false, false); +} + +static bool trans_SMLALDX(DisasContext *s, arg_rrrr *a) +{ + return op_smlald(s, a, true, false); +} + +static bool trans_SMLSLD(DisasContext *s, arg_rrrr *a) +{ + return op_smlald(s, a, false, true); +} + +static bool trans_SMLSLDX(DisasContext *s, arg_rrrr *a) +{ + return op_smlald(s, a, true, true); +} + +static bool op_smmla(DisasContext *s, arg_rrrr *a, bool round, bool sub) +{ + TCGv_i32 t1, t2; + + if (s->thumb + ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) + : !ENABLE_ARCH_6) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + tcg_gen_muls2_i32(t2, t1, t1, t2); + + if (a->ra != 15) { + TCGv_i32 t3 = load_reg(s, a->ra); + if (sub) { + /* + * For SMMLS, we need a 64-bit subtract. Borrow caused by + * a non-zero multiplicand lowpart, and the correct result + * lowpart for rounding. + */ + tcg_gen_sub2_i32(t2, t1, tcg_constant_i32(0), t3, t2, t1); + } else { + tcg_gen_add_i32(t1, t1, t3); + } + } + if (round) { + /* + * Adding 0x80000000 to the 64-bit quantity means that we have + * carry in to the high word when the low word has the msb set. + */ + tcg_gen_shri_i32(t2, t2, 31); + tcg_gen_add_i32(t1, t1, t2); + } + store_reg(s, a->rd, t1); + return true; +} + +static bool trans_SMMLA(DisasContext *s, arg_rrrr *a) +{ + return op_smmla(s, a, false, false); +} + +static bool trans_SMMLAR(DisasContext *s, arg_rrrr *a) +{ + return op_smmla(s, a, true, false); +} + +static bool trans_SMMLS(DisasContext *s, arg_rrrr *a) +{ + return op_smmla(s, a, false, true); +} + +static bool trans_SMMLSR(DisasContext *s, arg_rrrr *a) +{ + return op_smmla(s, a, true, true); +} + +static bool op_div(DisasContext *s, arg_rrr *a, bool u) +{ + TCGv_i32 t1, t2; + + if (s->thumb + ? !dc_isar_feature(aa32_thumb_div, s) + : !dc_isar_feature(aa32_arm_div, s)) { + return false; + } + + t1 = load_reg(s, a->rn); + t2 = load_reg(s, a->rm); + if (u) { + gen_helper_udiv(t1, tcg_env, t1, t2); + } else { + gen_helper_sdiv(t1, tcg_env, t1, t2); + } + store_reg(s, a->rd, t1); + return true; +} + +static bool trans_SDIV(DisasContext *s, arg_rrr *a) +{ + return op_div(s, a, false); +} + +static bool trans_UDIV(DisasContext *s, arg_rrr *a) +{ + return op_div(s, a, true); +} + +/* + * Block data transfer + */ + +static TCGv_i32 op_addr_block_pre(DisasContext *s, arg_ldst_block *a, int n) +{ + TCGv_i32 addr = load_reg(s, a->rn); + + if (a->b) { + if (a->i) { + /* pre increment */ + tcg_gen_addi_i32(addr, addr, 4); + } else { + /* pre decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } + } else if (!a->i && n != 1) { + /* post decrement */ + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + + if (s->v8m_stackcheck && a->rn == 13 && a->w) { + /* + * 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 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(tcg_env, addr); + } + + return addr; +} + +static void op_addr_block_post(DisasContext *s, arg_ldst_block *a, + TCGv_i32 addr, int n) +{ + if (a->w) { + /* write back */ + if (!a->b) { + if (a->i) { + /* post increment */ + tcg_gen_addi_i32(addr, addr, 4); + } else { + /* post decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } + } else if (!a->i && n != 1) { + /* pre decrement */ + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + store_reg(s, a->rn, addr); + } +} + +static bool op_stm(DisasContext *s, arg_ldst_block *a) +{ + int i, j, n, list, mem_idx; + bool user = a->u; + TCGv_i32 addr, tmp; + + if (user) { + /* STM (user) */ + if (IS_USER(s)) { + /* Only usable in supervisor mode. */ + unallocated_encoding(s); + return true; + } + } + + list = a->list; + n = ctpop16(list); + /* + * 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; + } + + s->eci_handled = true; + + addr = op_addr_block_pre(s, a, n); + mem_idx = get_mem_index(s); + + for (i = j = 0; i < 16; i++) { + if (!(list & (1 << i))) { + continue; + } + + if (user && i != 15) { + tmp = tcg_temp_new_i32(); + 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); + + /* No need to add after the last transfer. */ + if (++j != n) { + tcg_gen_addi_i32(addr, addr, 4); + } + } + + op_addr_block_post(s, a, addr, n); + clear_eci_state(s); + return true; +} + +static bool trans_STM(DisasContext *s, arg_ldst_block *a) +{ + return op_stm(s, a); +} + +static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a) +{ + /* Writeback register in register list is UNPREDICTABLE for T32. */ + if (a->w && (a->list & (1 << a->rn))) { + unallocated_encoding(s); + return true; + } + return op_stm(s, a); +} + +static bool do_ldm(DisasContext *s, arg_ldst_block *a) +{ + int i, j, n, list, mem_idx; + bool loaded_base; + bool user = a->u; + bool exc_return = false; + TCGv_i32 addr, tmp, loaded_var; + + if (user) { + /* LDM (user), LDM (exception return) */ + if (IS_USER(s)) { + /* Only usable in supervisor mode. */ + unallocated_encoding(s); + return true; + } + if (extract32(a->list, 15, 1)) { + exc_return = true; + user = false; + } else { + /* LDM (user) does not allow writeback. */ + if (a->w) { + unallocated_encoding(s); + return true; + } + } + } + + list = a->list; + n = ctpop16(list); + /* + * 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; + } + + s->eci_handled = true; + + addr = op_addr_block_pre(s, a, n); + mem_idx = get_mem_index(s); + loaded_base = false; + loaded_var = NULL; + + for (i = j = 0; i < 16; i++) { + if (!(list & (1 << i))) { + continue; + } + + 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(tcg_env, tcg_constant_i32(i), tmp); + } else if (i == a->rn) { + loaded_var = tmp; + loaded_base = true; + } else if (i == 15 && exc_return) { + store_pc_exc_ret(s, tmp); + } else { + store_reg_from_load(s, i, tmp); + } + + /* No need to add after the last transfer. */ + if (++j != n) { + tcg_gen_addi_i32(addr, addr, 4); + } + } + + op_addr_block_post(s, a, addr, n); + + if (loaded_base) { + /* Note that we reject base == pc above. */ + store_reg(s, a->rn, loaded_var); + } + + if (exc_return) { + /* Restore CPSR from SPSR. */ + tmp = load_cpu_field(spsr); + 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; + } + clear_eci_state(s); + return true; +} + +static bool trans_LDM_a32(DisasContext *s, arg_ldst_block *a) +{ + /* + * Writeback register in register list is UNPREDICTABLE + * for ArchVersion() >= 7. Prior to v7, A32 would write + * an UNKNOWN value to the base register. + */ + if (ENABLE_ARCH_7 && a->w && (a->list & (1 << a->rn))) { + unallocated_encoding(s); + return true; + } + return do_ldm(s, a); +} + +static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a) +{ + /* Writeback register in register list is UNPREDICTABLE for T32. */ + if (a->w && (a->list & (1 << a->rn))) { + unallocated_encoding(s); + return true; + } + 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)); + return do_ldm(s, a); +} + +static bool trans_CLRM(DisasContext *s, arg_CLRM *a) +{ + int i; + TCGv_i32 zero; + + if (!dc_isar_feature(aa32_m_sec_state, s)) { + return false; + } + + if (extract32(a->list, 13, 1)) { + return false; + } + + if (!a->list) { + /* UNPREDICTABLE; we choose to UNDEF */ + return false; + } + + s->eci_handled = true; + + zero = tcg_constant_i32(0); + for (i = 0; i < 15; i++) { + if (extract32(a->list, i, 1)) { + /* Clear R[i] */ + tcg_gen_mov_i32(cpu_R[i], zero); + } + } + if (extract32(a->list, 15, 1)) { + /* + * 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(tcg_env, tcg_constant_i32(0xc00), zero); + } + clear_eci_state(s); + return true; +} + +/* + * Branch, branch with link + */ + +static bool trans_B(DisasContext *s, arg_i *a) +{ + gen_jmp(s, jmp_diff(s, a->imm)); + return true; +} + +static bool trans_B_cond_thumb(DisasContext *s, arg_ci *a) +{ + /* This has cond from encoding, required to be outside IT block. */ + if (a->cond >= 0xe) { + return false; + } + if (s->condexec_mask) { + unallocated_encoding(s); + return true; + } + arm_skip_unless(s, a->cond); + gen_jmp(s, jmp_diff(s, a->imm)); + return true; +} + +static bool trans_BL(DisasContext *s, arg_i *a) +{ + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); + gen_jmp(s, jmp_diff(s, a->imm)); + return true; +} + +static bool trans_BLX_i(DisasContext *s, arg_BLX_i *a) +{ + /* + * BLX would be useless on M-profile; the encoding space + * is used for other insns from v8.1M onward, and UNDEFs before that. + */ + if (arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + + /* For A32, ARM_FEATURE_V5 is checked near the start of the uncond block. */ + if (s->thumb && (a->imm & 2)) { + return false; + } + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); + store_cpu_field_constant(!s->thumb, thumb); + /* 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)); + gen_pc_plus_diff(s, cpu_R[14], jmp_diff(s, a->imm << 12)); + return true; +} + +static bool trans_BL_suffix(DisasContext *s, arg_BL_suffix *a) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + assert(!arm_dc_feature(s, ARM_FEATURE_THUMB2)); + tcg_gen_addi_i32(tmp, cpu_R[14], (a->imm << 1) | 1); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | 1); + gen_bx(s, tmp); + return true; +} + +static bool trans_BLX_suffix(DisasContext *s, arg_BLX_suffix *a) +{ + TCGv_i32 tmp; + + assert(!arm_dc_feature(s, ARM_FEATURE_THUMB2)); + if (!ENABLE_ARCH_5) { + return false; + } + tmp = tcg_temp_new_i32(); + tcg_gen_addi_i32(tmp, cpu_R[14], a->imm << 1); + tcg_gen_andi_i32(tmp, tmp, 0xfffffffc); + gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | 1); + gen_bx(s, tmp); + return true; +} + +static bool trans_BF(DisasContext *s, arg_BF *a) +{ + /* + * M-profile branch future insns. The architecture permits an + * implementation to implement these as NOPs (equivalent to + * discarding the LO_BRANCH_INFO cache immediately), and we + * take that IMPDEF option because for QEMU a "real" implementation + * would be complicated and wouldn't execute any faster. + */ + if (!dc_isar_feature(aa32_lob, s)) { + return false; + } + if (a->boff == 0) { + /* SEE "Related encodings" (loop insns) */ + return false; + } + /* Handle as NOP */ + return true; +} + +static bool trans_DLS(DisasContext *s, arg_DLS *a) +{ + /* M-profile low-overhead loop start */ + TCGv_i32 tmp; + + if (!dc_isar_feature(aa32_lob, s)) { + return false; + } + if (a->rn == 13 || a->rn == 15) { + /* + * For DLSTP rn == 15 is a related encoding (LCTP); the + * other cases caught by this condition are all + * CONSTRAINED UNPREDICTABLE: we choose to UNDEF + */ + return false; + } + + if (a->size != 4) { + /* DLSTP */ + if (!dc_isar_feature(aa32_mve, s)) { + return false; + } + if (!vfp_access_check(s)) { + return true; + } + } + + /* Not a while loop: set LR to the count, and set LTPSIZE for DLSTP */ + tmp = load_reg(s, a->rn); + store_reg(s, 14, tmp); + if (a->size != 4) { + /* DLSTP: set FPSCR.LTPSIZE */ + store_cpu_field(tcg_constant_i32(a->size), v7m.ltpsize); + s->base.is_jmp = DISAS_UPDATE_NOCHAIN; + } + return true; +} + +static bool trans_WLS(DisasContext *s, arg_WLS *a) +{ + /* M-profile low-overhead while-loop start */ + TCGv_i32 tmp; + DisasLabel nextlabel; + + if (!dc_isar_feature(aa32_lob, s)) { + return false; + } + if (a->rn == 13 || a->rn == 15) { + /* + * For WLSTP rn == 15 is a related encoding (LE); the + * other cases caught by this condition are all + * CONSTRAINED UNPREDICTABLE: we choose to UNDEF + */ + return false; + } + if (s->condexec_mask) { + /* + * WLS in an IT block is CONSTRAINED UNPREDICTABLE; + * we choose to UNDEF, because otherwise our use of + * gen_goto_tb(1) would clash with the use of TB exit 1 + * in the dc->condjmp condition-failed codepath in + * arm_tr_tb_stop() and we'd get an assertion. + */ + return false; + } + if (a->size != 4) { + /* WLSTP */ + if (!dc_isar_feature(aa32_mve, s)) { + return false; + } + /* + * We need to check that the FPU is enabled here, but mustn't + * call vfp_access_check() to do that because we don't want to + * do the lazy state preservation in the "loop count is zero" case. + * Do the check-and-raise-exception by hand. + */ + if (s->fp_excp_el) { + gen_exception_insn_el(s, 0, EXCP_NOCP, + syn_uncategorized(), s->fp_excp_el); + return true; + } + } + + 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) { + /* + * WLSTP: set FPSCR.LTPSIZE. This requires that we do the + * lazy state preservation, new FP context creation, etc, + * that vfp_access_check() does. We know that the actual + * access check will succeed (ie it won't generate code that + * throws an exception) because we did that check by hand earlier. + */ + bool ok = vfp_access_check(s); + assert(ok); + store_cpu_field(tcg_constant_i32(a->size), v7m.ltpsize); + /* + * LTPSIZE updated, but MVE_NO_PRED will always be the same thing (0) + * when we take this upcoming exit from this TB, so gen_jmp_tb() is OK. + */ + } + gen_jmp_tb(s, curr_insn_len(s), 1); + + set_disas_label(s, nextlabel); + gen_jmp(s, jmp_diff(s, a->imm)); + return true; +} + +static bool trans_LE(DisasContext *s, arg_LE *a) +{ + /* + * M-profile low-overhead loop end. The architecture permits an + * implementation to discard the LO_BRANCH_INFO cache at any time, + * and we take the IMPDEF option to never set it in the first place + * (equivalent to always discarding it immediately), because for QEMU + * a "real" implementation would be complicated and wouldn't execute + * any faster. + */ + TCGv_i32 tmp; + DisasLabel loopend; + bool fpu_active; + + if (!dc_isar_feature(aa32_lob, s)) { + return false; + } + if (a->f && a->tp) { + return false; + } + if (s->condexec_mask) { + /* + * LE in an IT block is CONSTRAINED UNPREDICTABLE; + * we choose to UNDEF, because otherwise our use of + * gen_goto_tb(1) would clash with the use of TB exit 1 + * in the dc->condjmp condition-failed codepath in + * arm_tr_tb_stop() and we'd get an assertion. + */ + return false; + } + if (a->tp) { + /* LETP */ + if (!dc_isar_feature(aa32_mve, s)) { + return false; + } + if (!vfp_access_check(s)) { + s->eci_handled = true; + return true; + } + } + + /* LE/LETP is OK with ECI set and leaves it untouched */ + s->eci_handled = true; + + /* + * With MVE, LTPSIZE might not be 4, and we must emit an INVSTATE + * UsageFault exception for the LE insn in that case. Note that we + * are not directly checking FPSCR.LTPSIZE but instead check the + * pseudocode LTPSIZE() function, which returns 4 if the FPU is + * not currently active (ie ActiveFPState() returns false). We + * can identify not-active purely from our TB state flags, as the + * FPU is active only if: + * the FPU is enabled + * AND lazy state preservation is not active + * AND we do not need a new fp context (this is the ASPEN/FPCA check) + * + * Usually we don't need to care about this distinction between + * LTPSIZE and FPSCR.LTPSIZE, because the code in vfp_access_check() + * will either take an exception or clear the conditions that make + * the FPU not active. But LE is an unusual case of a non-FP insn + * that looks at LTPSIZE. + */ + fpu_active = !s->fp_excp_el && !s->v7m_lspact && !s->v7m_new_fp_ctxt_needed; + + if (!a->tp && dc_isar_feature(aa32_mve, s) && fpu_active) { + /* Need to do a runtime check for LTPSIZE != 4 */ + DisasLabel skipexc = gen_disas_label(s); + tmp = load_cpu_field(v7m.ltpsize); + 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, jmp_diff(s, -a->imm)); + return true; + } + + /* + * Not loop-forever. If LR <= loop-decrement-value this is the last loop. + * For LE, we know at this point that LTPSIZE must be 4 and the + * loop decrement value is 1. For LETP we need to calculate the decrement + * value from LTPSIZE. + */ + loopend = gen_disas_label(s); + if (!a->tp) { + 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_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_gen_brcond_i32(TCG_COND_LEU, cpu_R[14], decr, loopend.label); + + tcg_gen_sub_i32(cpu_R[14], cpu_R[14], decr); + } + /* Jump back to the loop start */ + gen_jmp(s, jmp_diff(s, -a->imm)); + + 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, curr_insn_len(s), 1); + return true; +} + +static bool trans_LCTP(DisasContext *s, arg_LCTP *a) +{ + /* + * M-profile Loop Clear with Tail Predication. Since our implementation + * doesn't cache branch information, all we need to do is reset + * FPSCR.LTPSIZE to 4. + */ + + if (!dc_isar_feature(aa32_lob, s) || + !dc_isar_feature(aa32_mve, s)) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + store_cpu_field_constant(4, v7m.ltpsize); + return true; +} + +static bool trans_VCTP(DisasContext *s, arg_VCTP *a) +{ + /* + * M-profile Create Vector Tail Predicate. This insn is itself + * predicated and is subject to beatwise execution. + */ + TCGv_i32 rn_shifted, masklen; + + if (!dc_isar_feature(aa32_mve, s) || a->rn == 13 || a->rn == 15) { + return false; + } + + if (!mve_eci_check(s) || !vfp_access_check(s)) { + return true; + } + + /* + * We pre-calculate the mask length here to avoid having + * to have multiple helpers specialized for size. + * We pass the helper "rn <= (1 << (4 - size)) ? (rn << size) : 16". + */ + rn_shifted = tcg_temp_new_i32(); + masklen = load_reg(s, a->rn); + tcg_gen_shli_i32(rn_shifted, masklen, a->size); + 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(tcg_env, masklen); + /* This insn updates predication bits */ + s->base.is_jmp = DISAS_UPDATE_NOCHAIN; + mve_update_eci(s); + return true; +} + +static bool op_tbranch(DisasContext *s, arg_tbranch *a, bool half) +{ + TCGv_i32 addr, tmp; + + tmp = load_reg(s, a->rm); + if (half) { + tcg_gen_add_i32(tmp, tmp, tmp); + } + addr = load_reg(s, a->rn); + tcg_gen_add_i32(addr, addr, tmp); + + gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), half ? MO_UW : MO_UB); + + tcg_gen_add_i32(tmp, tmp, tmp); + gen_pc_plus_diff(s, addr, jmp_diff(s, 0)); + tcg_gen_add_i32(tmp, tmp, addr); + store_reg(s, 15, tmp); + return true; +} + +static bool trans_TBB(DisasContext *s, arg_tbranch *a) +{ + return op_tbranch(s, a, false); +} + +static bool trans_TBH(DisasContext *s, arg_tbranch *a) +{ + return op_tbranch(s, a, true); +} + +static bool trans_CBZ(DisasContext *s, arg_CBZ *a) +{ + TCGv_i32 tmp = load_reg(s, a->rn); + + arm_gen_condlabel(s); + tcg_gen_brcondi_i32(a->nz ? TCG_COND_EQ : TCG_COND_NE, + tmp, 0, s->condlabel.label); + gen_jmp(s, jmp_diff(s, a->imm)); + return true; +} + +/* + * Supervisor call - both T32 & A32 come here so we need to check + * which mode we are in when checking for semihosting. + */ + +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(s->current_el == 0) && + (a->imm == semihost_imm)) { + gen_exception_internal_insn(s, EXCP_SEMIHOST); + } else { + 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; +} + +/* + * Unconditional system instructions + */ + +static bool trans_RFE(DisasContext *s, arg_RFE *a) +{ + static const int8_t pre_offset[4] = { + /* DA */ -4, /* IA */ 0, /* DB */ -8, /* IB */ 4 + }; + static const int8_t post_offset[4] = { + /* DA */ -8, /* IA */ 4, /* DB */ -4, /* IB */ 0 + }; + TCGv_i32 addr, t1, t2; + + if (!ENABLE_ARCH_6 || arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + if (IS_USER(s)) { + unallocated_encoding(s); + return true; + } + + addr = load_reg(s, a->rn); + tcg_gen_addi_i32(addr, addr, pre_offset[a->pu]); + + /* Load PC into tmp and CPSR into tmp2. */ + t1 = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, t1, addr, get_mem_index(s), MO_UL | MO_ALIGN); + tcg_gen_addi_i32(addr, addr, 4); + t2 = tcg_temp_new_i32(); + gen_aa32_ld_i32(s, t2, addr, get_mem_index(s), MO_UL | MO_ALIGN); + + if (a->w) { + /* Base writeback. */ + tcg_gen_addi_i32(addr, addr, post_offset[a->pu]); + store_reg(s, a->rn, addr); + } + gen_rfe(s, t1, t2); + return true; +} + +static bool trans_SRS(DisasContext *s, arg_SRS *a) +{ + if (!ENABLE_ARCH_6 || arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + gen_srs(s, a->mode, a->pu, a->w); + return true; +} + +static bool trans_CPS(DisasContext *s, arg_CPS *a) +{ + uint32_t mask, val; + + if (!ENABLE_ARCH_6 || arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + if (IS_USER(s)) { + /* Implemented as NOP in user mode. */ + return true; + } + /* TODO: There are quite a lot of UNPREDICTABLE argument combinations. */ + + mask = val = 0; + if (a->imod & 2) { + if (a->A) { + mask |= CPSR_A; + } + if (a->I) { + mask |= CPSR_I; + } + if (a->F) { + mask |= CPSR_F; + } + if (a->imod & 1) { + val |= mask; + } + } + if (a->M) { + mask |= CPSR_M; + val |= a->mode; + } + if (mask) { + gen_set_psr_im(s, mask, 0, val); + } + return true; +} + +static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) +{ + TCGv_i32 tmp, addr; + + if (!arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + if (IS_USER(s)) { + /* Implemented as NOP in user mode. */ + return true; + } + + tmp = tcg_constant_i32(a->im); + /* FAULTMASK */ + if (a->F) { + addr = tcg_constant_i32(19); + gen_helper_v7m_msr(tcg_env, addr, tmp); + } + /* PRIMASK */ + if (a->I) { + addr = tcg_constant_i32(16); + gen_helper_v7m_msr(tcg_env, addr, tmp); + } + gen_rebuild_hflags(s, false); + gen_lookup_tb(s); + return true; +} + +/* + * Clear-Exclusive, Barriers + */ + +static bool trans_CLREX(DisasContext *s, arg_CLREX *a) +{ + if (s->thumb + ? !ENABLE_ARCH_7 && !arm_dc_feature(s, ARM_FEATURE_M) + : !ENABLE_ARCH_6K) { + return false; + } + gen_clrex(s); + return true; +} + +static bool trans_DSB(DisasContext *s, arg_DSB *a) +{ + if (!ENABLE_ARCH_7 && !arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + return true; +} + +static bool trans_DMB(DisasContext *s, arg_DMB *a) +{ + return trans_DSB(s, NULL); +} + +static bool trans_ISB(DisasContext *s, arg_ISB *a) +{ + if (!ENABLE_ARCH_7 && !arm_dc_feature(s, ARM_FEATURE_M)) { + return false; + } + /* + * We need to break the TB after this insn to execute + * self-modifying code correctly and also to take + * any pending interrupts immediately. + */ + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_SB(DisasContext *s, arg_SB *a) +{ + if (!dc_isar_feature(aa32_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); + s->base.is_jmp = DISAS_TOO_MANY; + return true; +} + +static bool trans_SETEND(DisasContext *s, arg_SETEND *a) +{ + if (!ENABLE_ARCH_6) { + return false; + } + if (a->E != (s->be_data == MO_BE)) { + gen_helper_setend(tcg_env); + s->base.is_jmp = DISAS_UPDATE_EXIT; + } + return true; +} + +/* + * Preload instructions + * All are nops, contingent on the appropriate arch level. + */ + +static bool trans_PLD(DisasContext *s, arg_PLD *a) +{ + return ENABLE_ARCH_5TE; +} + +static bool trans_PLDW(DisasContext *s, arg_PLDW *a) +{ + return arm_dc_feature(s, ARM_FEATURE_V7MP); +} + +static bool trans_PLI(DisasContext *s, arg_PLI *a) +{ + return ENABLE_ARCH_7; +} + +/* + * If-then + */ + +static bool trans_IT(DisasContext *s, arg_IT *a) +{ + int cond_mask = a->cond_mask; + + /* + * No actual code generated for this insn, just setup state. + * + * Combinations of firstcond and mask which set up an 0b1111 + * condition are UNPREDICTABLE; we take the CONSTRAINED + * UNPREDICTABLE choice to treat 0b1111 the same as 0b1110, + * i.e. both meaning "execute always". + */ + s->condexec_cond = (cond_mask >> 4) & 0xe; + s->condexec_mask = cond_mask & 0x1f; + return true; +} + +/* v8.1M CSEL/CSINC/CSNEG/CSINV */ +static bool trans_CSEL(DisasContext *s, arg_CSEL *a) +{ + TCGv_i32 rn, rm; + DisasCompare c; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { + return false; + } + + if (a->rm == 13) { + /* SEE "Related encodings" (MVE shifts) */ + return false; + } + + if (a->rd == 13 || a->rd == 15 || a->rn == 13 || a->fcond >= 14) { + /* CONSTRAINED UNPREDICTABLE: we choose to UNDEF */ + return false; + } + + /* In this insn input reg fields of 0b1111 mean "zero", not "PC" */ + rn = tcg_temp_new_i32(); + rm = tcg_temp_new_i32(); + if (a->rn == 15) { + tcg_gen_movi_i32(rn, 0); + } else { + load_reg_var(s, rn, a->rn); + } + if (a->rm == 15) { + tcg_gen_movi_i32(rm, 0); + } else { + load_reg_var(s, rm, a->rm); + } + + switch (a->op) { + case 0: /* CSEL */ + break; + case 1: /* CSINC */ + tcg_gen_addi_i32(rm, rm, 1); + break; + case 2: /* CSINV */ + tcg_gen_not_i32(rm, rm); + break; + case 3: /* CSNEG */ + tcg_gen_neg_i32(rm, rm); + break; + default: + g_assert_not_reached(); + } + + arm_test_cc(&c, a->fcond); + tcg_gen_movcond_i32(c.cond, rn, c.value, tcg_constant_i32(0), rn, rm); + + store_reg(s, a->rd, rn); + return true; +} + +/* + * Legacy decoder. + */ + +static void disas_arm_insn(DisasContext *s, unsigned int insn) +{ + unsigned int cond = insn >> 28; + + /* M variants do not implement ARM mode; this must raise the INVSTATE + * UsageFault exception. + */ + if (arm_dc_feature(s, ARM_FEATURE_M)) { + gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized()); + return; + } + + if (s->pstate_il) { + /* + * Illegal execution state. This has priority over BTI + * exceptions, but comes after instruction abort exceptions. + */ + gen_exception_insn(s, 0, EXCP_UDEF, syn_illegalstate()); + return; + } + + if (cond == 0xf) { + /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we + * choose to UNDEF. In ARMv5 and above the space is used + * for miscellaneous unconditional instructions. + */ + if (!arm_dc_feature(s, ARM_FEATURE_V5)) { + unallocated_encoding(s); + return; + } + + /* Unconditional instructions. */ + /* TODO: Perhaps merge these into one decodetree output file. */ + if (disas_a32_uncond(s, insn) || + disas_vfp_uncond(s, insn) || + disas_neon_dp(s, insn) || + disas_neon_ls(s, insn) || + disas_neon_shared(s, insn)) { + return; + } + /* fall back to legacy decoder */ + + if ((insn & 0x0e000f00) == 0x0c000100) { + if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { + /* iWMMXt register transfer. */ + if (extract32(s->c15_cpar, 1, 1)) { + if (!disas_iwmmxt_insn(s, insn)) { + return; + } + } + } + } + goto illegal_op; + } + if (cond != 0xe) { + /* if not always execute, we generate a conditional jump to + next instruction */ + arm_skip_unless(s, cond); + } + + /* TODO: Perhaps merge these into one decodetree output file. */ + if (disas_a32(s, insn) || + disas_vfp(s, insn)) { + return; + } + /* fall back to legacy decoder */ + /* TODO: convert xscale/iwmmxt decoder to decodetree ?? */ + if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { + if (((insn & 0x0c000e00) == 0x0c000000) + && ((insn & 0x03000000) != 0x03000000)) { + /* Coprocessor insn, coprocessor 0 or 1 */ + disas_xscale_insn(s, insn); + return; + } + } + +illegal_op: + unallocated_encoding(s); +} + +static bool thumb_insn_is_16bit(DisasContext *s, uint32_t pc, uint32_t insn) +{ + /* + * Return true if this is a 16 bit instruction. We must be precise + * about this (matching the decode). + */ + if ((insn >> 11) < 0x1d) { + /* Definitely a 16-bit instruction */ + return true; + } + + /* Top five bits 0b11101 / 0b11110 / 0b11111 : this is the + * first half of a 32-bit Thumb insn. Thumb-1 cores might + * end up actually treating this as two 16-bit insns, though, + * if it's half of a bl/blx pair that might span a page boundary. + */ + if (arm_dc_feature(s, ARM_FEATURE_THUMB2) || + arm_dc_feature(s, ARM_FEATURE_M)) { + /* Thumb2 cores (including all M profile ones) always treat + * 32-bit insns as 32-bit. + */ + return false; + } + + if ((insn >> 11) == 0x1e && pc - s->page_start < TARGET_PAGE_SIZE - 3) { + /* 0b1111_0xxx_xxxx_xxxx : BL/BLX prefix, and the suffix + * is not on the next page; we merge this into a 32-bit + * insn. + */ + return false; + } + /* 0b1110_1xxx_xxxx_xxxx : BLX suffix (or UNDEF); + * 0b1111_1xxx_xxxx_xxxx : BL suffix; + * 0b1111_0xxx_xxxx_xxxx : BL/BLX prefix on the end of a page + * -- handle as single 16 bit insn + */ + return true; +} + +/* Translate a 32-bit thumb instruction. */ +static void disas_thumb2_insn(DisasContext *s, uint32_t insn) +{ + /* + * ARMv6-M supports a limited subset of Thumb2 instructions. + * Other Thumb1 architectures allow only 32-bit + * combined BL/BLX prefix and suffix. + */ + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_V7)) { + int i; + bool found = false; + static const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, + 0xf3b08040 /* dsb */, + 0xf3b08050 /* dmb */, + 0xf3b08060 /* isb */, + 0xf3e08000 /* mrs */, + 0xf000d000 /* bl */}; + static const uint32_t armv6m_mask[] = {0xffe0d000, + 0xfff0d0f0, + 0xfff0d0f0, + 0xfff0d0f0, + 0xffe0d000, + 0xf800d000}; + + for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) { + if ((insn & armv6m_mask[i]) == armv6m_insn[i]) { + found = true; + break; + } + } + if (!found) { + goto illegal_op; + } + } else if ((insn & 0xf800e800) != 0xf000e800) { + if (!arm_dc_feature(s, ARM_FEATURE_THUMB2)) { + unallocated_encoding(s); + return; + } + } + + if (arm_dc_feature(s, ARM_FEATURE_M)) { + /* + * NOCP takes precedence over any UNDEF for (almost) the + * entire wide range of coprocessor-space encodings, so check + * for it first before proceeding to actually decode eg VFP + * insns. This decode also handles the few insns which are + * in copro space but do not have NOCP checks (eg VLLDM, VLSTM). + */ + if (disas_m_nocp(s, insn)) { + return; + } + } + + if ((insn & 0xef000000) == 0xef000000) { + /* + * T32 encodings 0b111p_1111_qqqq_qqqq_qqqq_qqqq_qqqq_qqqq + * transform into + * A32 encodings 0b1111_001p_qqqq_qqqq_qqqq_qqqq_qqqq_qqqq + */ + uint32_t a32_insn = (insn & 0xe2ffffff) | + ((insn & (1 << 28)) >> 4) | (1 << 28); + + if (disas_neon_dp(s, a32_insn)) { + return; + } + } + + if ((insn & 0xff100000) == 0xf9000000) { + /* + * T32 encodings 0b1111_1001_ppp0_qqqq_qqqq_qqqq_qqqq_qqqq + * transform into + * A32 encodings 0b1111_0100_ppp0_qqqq_qqqq_qqqq_qqqq_qqqq + */ + uint32_t a32_insn = (insn & 0x00ffffff) | 0xf4000000; + + if (disas_neon_ls(s, a32_insn)) { + return; + } + } + + /* + * TODO: Perhaps merge these into one decodetree output file. + * Note disas_vfp is written for a32 with cond field in the + * top nibble. The t32 encoding requires 0xe in the top nibble. + */ + if (disas_t32(s, insn) || + disas_vfp_uncond(s, insn) || + disas_neon_shared(s, insn) || + disas_mve(s, insn) || + ((insn >> 28) == 0xe && disas_vfp(s, insn))) { + return; + } + +illegal_op: + unallocated_encoding(s); +} + +static void disas_thumb_insn(DisasContext *s, uint32_t insn) +{ + if (!disas_t16(s, insn)) { + unallocated_encoding(s); + } +} + +static bool insn_crosses_page(CPUARMState *env, DisasContext *s) +{ + /* Return true if the insn at dc->base.pc_next might cross a page boundary. + * (False positives are OK, false negatives are not.) + * We know this is a Thumb insn, and our caller ensures we are + * only called if dc->base.pc_next is less than 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 = arm_lduw_code(env, &s->base, s->base.pc_next, s->sctlr_b); + + return !thumb_insn_is_16bit(s, s->base.pc_next, insn); +} + +static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + 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; + 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); + /* + * the CONDEXEC TB flags are CPSR bits [15:10][26:25]. On A-profile this + * is always the IT bits. On M-profile, some of the reserved encodings + * of IT are used instead to indicate either ICI or ECI, which + * indicate partial progress of a restartable insn that was interrupted + * partway through by an exception: + * * if CONDEXEC[3:0] != 0b0000 : CONDEXEC is IT bits + * * if CONDEXEC[3:0] == 0b0000 : CONDEXEC is ICI or ECI bits + * In all cases CONDEXEC == 0 means "not in IT block or restartable + * insn, behave normally". + */ + dc->eci = dc->condexec_mask = dc->condexec_cond = 0; + dc->eci_handled = false; + if (condexec & 0xf) { + dc->condexec_mask = (condexec & 0xf) << 1; + dc->condexec_cond = condexec >> 4; + } else { + if (arm_feature(env, ARM_FEATURE_M)) { + dc->eci = condexec >> 4; + } + } + + core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); + dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); + dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); +#if !defined(CONFIG_USER_ONLY) + dc->user = (dc->current_el == 0); +#endif + dc->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 = 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 = + EX_TBFLAG_M32(tb_flags, NEW_FP_CTXT_NEEDED); + dc->v7m_lspact = EX_TBFLAG_M32(tb_flags, LSPACT); + dc->mve_no_pred = EX_TBFLAG_M32(tb_flags, MVE_NO_PRED); + } else { + 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); + dc->vfp_enabled = EX_TBFLAG_A32(tb_flags, VFPEN); + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + dc->c15_cpar = EX_TBFLAG_A32(tb_flags, XSCALE_CPAR); + } else { + 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; + + /* Single step state. The code-generation logic here is: + * SS_ACTIVE == 0: + * generate code with no special handling for single-stepping (except + * that anything that can make us go to SS_ACTIVE == 1 must end the TB; + * this happens anyway because those changes are all system register or + * PSTATE writes). + * SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending) + * emit code for one insn + * emit code to clear PSTATE.SS + * emit code to generate software step exception for completed step + * end TB (as usual for having generated an exception) + * SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending) + * emit code to generate a software step exception + * end the TB + */ + 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->page_start = dc->base.pc_first & TARGET_PAGE_MASK; + + /* If architectural single step active, limit to 1. */ + if (dc->ss_active) { + dc->base.max_insns = 1; + } + + /* ARM is a fixed-length ISA. Bound the number of insns to execute + to those left on the page. */ + if (!dc->thumb) { + int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); + } + + cpu_V0 = tcg_temp_new_i64(); + cpu_V1 = tcg_temp_new_i64(); + cpu_M0 = tcg_temp_new_i64(); +} + +static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + /* A note on handling of the condexec (IT) bits: + * + * We want to avoid the overhead of having to write the updated condexec + * bits back to the CPUARMState for every instruction in an IT block. So: + * (1) if the condexec bits are not already zero then we write + * zero back into the CPUARMState now. This avoids complications trying + * to do it at the end of the block. (For example if we don't do this + * it's hard to identify whether we can safely skip writing condexec + * at the end of the TB, which we definitely want to do for the case + * where a TB doesn't do anything with the IT state at all.) + * (2) if we are going to leave the TB then we call gen_set_condexec() + * which will write the correct value into CPUARMState if zero is wrong. + * This is done both for leaving the TB at the end, and for leaving + * it because of an exception we know will happen, which is done in + * gen_exception_insn(). The latter is necessary because we need to + * leave the TB with the PC/IT state just prior to execution of the + * instruction which caused the exception. + * (3) if we leave the TB unexpectedly (eg a data abort on a load) + * then the CPUARMState will be wrong and we need to reset it. + * This is handled in the same way as restoration of the + * PC in these situations; we save the value of the condexec bits + * for each PC via tcg_gen_insn_start(), and restore_state_to_opc() + * then uses this to restore them after an exception. + * + * Note that there are no instructions which can read the condexec + * bits, and none which can write non-static values to them, so + * we don't need to care about whether CPUARMState is correct in the + * middle of a TB. + */ + + /* Reset the conditional execution bits immediately. This avoids + complications trying to do it at the end of the block. */ + if (dc->condexec_mask || dc->condexec_cond) { + store_cpu_field_constant(0, condexec_bits); + } +} + +static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + /* + * The ECI/ICI bits share PSR bits with the IT bits, so we + * need to reconstitute the bits from the split-out DisasContext + * 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(pc_arg, condexec_bits, 0); + dc->insn_start_updated = false; +} + +static bool arm_check_kernelpage(DisasContext *dc) +{ +#ifdef CONFIG_USER_ONLY + /* Intercept jump to the magic kernel page. */ + if (dc->base.pc_next >= 0xffff0000) { + /* We always get here via a jump, so know we are not in a + conditional execution block. */ + gen_exception_internal(EXCP_KERNEL_TRAP); + dc->base.is_jmp = DISAS_NORETURN; + return true; + } +#endif + return false; +} + +static bool arm_check_ss_active(DisasContext *dc) +{ + if (dc->ss_active && !dc->pstate_ss) { + /* Singlestep state is Active-pending. + * If we're in this state at the start of a TB then either + * a) we just took an exception to an EL which is being debugged + * and this is the first insn in the exception handler + * b) debug exceptions were masked and we just unmasked them + * without changing EL (eg by clearing PSTATE.D) + * In either case we're going to take a swstep exception in the + * "did not step an insn" case, and so the syndrome ISV and EX + * bits should be zero. + */ + assert(dc->base.num_insns == 1); + gen_swstep_exception(dc, 0, 0); + dc->base.is_jmp = DISAS_NORETURN; + return true; + } + + return false; +} + +static void arm_post_translate_insn(DisasContext *dc) +{ + 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; + } +} + +static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUARMState *env = cpu_env(cpu); + uint32_t pc = dc->base.pc_next; + unsigned int insn; + + /* Singlestep exceptions have the highest priority. */ + if (arm_check_ss_active(dc)) { + dc->base.pc_next = pc + 4; + return; + } + + if (pc & 3) { + /* + * PC alignment fault. This has priority over the instruction abort + * that we would receive from a translation fault via arm_ldl_code + * (or the execution of the kernelpage entrypoint). This should only + * be possible after an indirect branch, at the start of the TB. + */ + assert(dc->base.num_insns == 1); + 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; + } + + if (arm_check_kernelpage(dc)) { + dc->base.pc_next = pc + 4; + return; + } + + dc->pc_curr = pc; + insn = arm_ldl_code(env, &dc->base, pc, dc->sctlr_b); + dc->insn = insn; + dc->base.pc_next = pc + 4; + disas_arm_insn(dc, insn); + + arm_post_translate_insn(dc); + + /* ARM is a fixed-length ISA. We performed the cross-page check + in init_disas_context by adjusting max_insns. */ +} + +static bool thumb_insn_is_unconditional(DisasContext *s, uint32_t insn) +{ + /* Return true if this Thumb insn is always unconditional, + * even inside an IT block. This is true of only a very few + * instructions: BKPT, HLT, and SG. + * + * A larger class of instructions are UNPREDICTABLE if used + * inside an IT block; we do not need to detect those here, because + * what we do by default (perform the cc check and update the IT + * bits state machine) is a permitted CONSTRAINED UNPREDICTABLE + * choice for those situations. + * + * insn is either a 16-bit or a 32-bit instruction; the two are + * distinguishable because for the 16-bit case the top 16 bits + * are zeroes, and that isn't a valid 32-bit encoding. + */ + if ((insn & 0xffffff00) == 0xbe00) { + /* BKPT */ + return true; + } + + if ((insn & 0xffffffc0) == 0xba80 && arm_dc_feature(s, ARM_FEATURE_V8) && + !arm_dc_feature(s, ARM_FEATURE_M)) { + /* HLT: v8A only. This is unconditional even when it is going to + * UNDEF; see the v8A ARM ARM DDI0487B.a H3.3. + * For v7 cores this was a plain old undefined encoding and so + * honours its cc check. (We might be using the encoding as + * a semihosting trap, but we don't change the cc check behaviour + * on that account, because a debugger connected to a real v7A + * core and emulating semihosting traps by catching the UNDEF + * exception would also only see cases where the cc check passed. + * No guest code should be trying to do a HLT semihosting trap + * in an IT block anyway. + */ + return true; + } + + if (insn == 0xe97fe97f && arm_dc_feature(s, ARM_FEATURE_V8) && + arm_dc_feature(s, ARM_FEATURE_M)) { + /* SG: v8M only */ + return true; + } + + return false; +} + +static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + 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); + + if (arm_check_ss_active(dc) || arm_check_kernelpage(dc)) { + dc->base.pc_next = pc + 2; + return; + } + + dc->pc_curr = pc; + insn = arm_lduw_code(env, &dc->base, pc, dc->sctlr_b); + is_16bit = thumb_insn_is_16bit(dc, dc->base.pc_next, insn); + pc += 2; + if (!is_16bit) { + uint32_t insn2 = arm_lduw_code(env, &dc->base, pc, dc->sctlr_b); + insn = insn << 16 | insn2; + pc += 2; + } + dc->base.pc_next = pc; + dc->insn = insn; + + if (dc->pstate_il) { + /* + * Illegal execution state. This has priority over BTI + * exceptions, but comes after instruction abort exceptions. + */ + gen_exception_insn(dc, 0, EXCP_UDEF, syn_illegalstate()); + return; + } + + if (dc->eci) { + /* + * For M-profile continuable instructions, ECI/ICI handling + * falls into these cases: + * - interrupt-continuable instructions + * These are the various load/store multiple insns (both + * integer and fp). The ICI bits indicate the register + * where the load/store can resume. We make the IMPDEF + * choice to always do "instruction restart", ie ignore + * the ICI value and always execute the ldm/stm from the + * start. So all we need to do is zero PSR.ICI if the + * insn executes. + * - MVE instructions subject to beat-wise execution + * Here the ECI bits indicate which beats have already been + * executed, and we must honour this. Each insn of this + * type will handle it correctly. We will update PSR.ECI + * in the helper function for the insn (some ECI values + * mean that the following insn also has been partially + * executed). + * - Special cases which don't advance ECI + * The insns LE, LETP and BKPT leave the ECI/ICI state + * bits untouched. + * - all other insns (the common case) + * Non-zero ECI/ICI means an INVSTATE UsageFault. + * We place a rewind-marker here. Insns in the previous + * three categories will set a flag in the DisasContext. + * If the flag isn't set after we call disas_thumb_insn() + * or disas_thumb2_insn() then we know we have a "some other + * insn" case. We will rewind to the marker (ie throwing away + * all the generated code) and instead emit "take exception". + */ + insn_eci_rewind = tcg_last_op(); + insn_eci_pc_save = dc->pc_save; + } + + if (dc->condexec_mask && !thumb_insn_is_unconditional(dc, insn)) { + uint32_t cond = dc->condexec_cond; + + /* + * Conditionally skip the insn. Note that both 0xe and 0xf mean + * "always"; 0xf is not "never". + */ + if (cond < 0x0e) { + arm_skip_unless(dc, cond); + } + } + + if (is_16bit) { + disas_thumb_insn(dc, insn); + } else { + disas_thumb2_insn(dc, insn); + } + + /* Advance the Thumb condexec condition. */ + if (dc->condexec_mask) { + dc->condexec_cond = ((dc->condexec_cond & 0xe) | + ((dc->condexec_mask >> 4) & 1)); + dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f; + if (dc->condexec_mask == 0) { + dc->condexec_cond = 0; + } + } + + if (dc->eci && !dc->eci_handled) { + /* + * Insn wasn't valid for ECI/ICI at all: undo what we + * just generated and instead emit an exception + */ + tcg_remove_ops_after(insn_eci_rewind); + dc->pc_save = insn_eci_pc_save; + dc->condjmp = 0; + gen_exception_insn(dc, 0, EXCP_INVSTATE, syn_uncategorized()); + } + + arm_post_translate_insn(dc); + + /* Thumb is a variable-length ISA. Stop translation when the next insn + * will touch a new page. This ensures that prefetch aborts occur at + * the right place. + * + * We want to stop the TB if the next insn starts in a new page, + * or if it spans between this page and the next. This means that + * if we're looking at the last halfword in the page we need to + * see if it's a 16-bit Thumb insn (which will fit in this TB) + * or a 32-bit Thumb insn (which won't). + * This is to avoid generating a silly TB with a single 16-bit insn + * in it at the end of this page (which would execute correctly + * but isn't very efficient). + */ + if (dc->base.is_jmp == DISAS_NEXT + && (dc->base.pc_next - dc->page_start >= TARGET_PAGE_SIZE + || (dc->base.pc_next - dc->page_start >= TARGET_PAGE_SIZE - 3 + && insn_crosses_page(env, dc)))) { + dc->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + /* At this stage dc->condjmp will only be set when the skipped + instruction was a conditional branch or trap, and the PC has + already been written. */ + gen_set_condexec(dc); + if (dc->base.is_jmp == DISAS_BX_EXCRET) { + /* Exception return branches need some special case code at the + * end of the TB, which is complex enough that it has to + * handle the single-step vs not and the condition-failed + * insn codepath itself. + */ + gen_bx_excret_final_code(dc); + } else if (unlikely(dc->ss_active)) { + /* Unconditional and "condition passed" instruction codepath. */ + switch (dc->base.is_jmp) { + case DISAS_SWI: + gen_ss_advance(dc); + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); + break; + case DISAS_HVC: + gen_ss_advance(dc); + gen_exception_el(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); + break; + case DISAS_SMC: + gen_ss_advance(dc); + 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_update_pc(dc, curr_insn_len(dc)); + /* fall through */ + default: + /* FIXME: Single stepping a WFI insn will not halt the CPU. */ + gen_singlestep_exception(dc); + break; + case DISAS_NORETURN: + break; + } + } else { + /* While branches must always occur at the end of an IT block, + there are a few other things that can cause us to terminate + the TB in the middle of an IT block: + - Exception generating instructions (bkpt, swi, undefined). + - Page boundaries. + - Hardware watchpoints. + Hardware breakpoints have already been handled and skip this code. + */ + switch (dc->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: + gen_goto_tb(dc, 1, curr_insn_len(dc)); + break; + case DISAS_UPDATE_NOCHAIN: + gen_update_pc(dc, curr_insn_len(dc)); + /* fall through */ + case DISAS_JUMP: + gen_goto_ptr(); + break; + case DISAS_UPDATE_EXIT: + gen_update_pc(dc, curr_insn_len(dc)); + /* fall through */ + default: + /* indicate that the hash table must be used to find the next TB */ + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_NORETURN: + /* nothing more to generate */ + break; + case DISAS_WFI: + 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. + */ + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_WFE: + gen_helper_wfe(tcg_env); + break; + case DISAS_YIELD: + gen_helper_yield(tcg_env); + break; + case DISAS_SWI: + gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); + break; + case DISAS_HVC: + gen_exception_el(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); + break; + case DISAS_SMC: + gen_exception_el(EXCP_SMC, syn_aa32_smc(), 3); + break; + } + } + + if (dc->condjmp) { + /* "Condition failed" instruction codepath for the branch/trap insn */ + set_disas_label(dc, dc->condlabel); + gen_set_condexec(dc); + if (unlikely(dc->ss_active)) { + gen_update_pc(dc, curr_insn_len(dc)); + gen_singlestep_exception(dc); + } else { + gen_goto_tb(dc, 1, curr_insn_len(dc)); + } + } +} + +static const TranslatorOps arm_translator_ops = { + .init_disas_context = arm_tr_init_disas_context, + .tb_start = arm_tr_tb_start, + .insn_start = arm_tr_insn_start, + .translate_insn = arm_tr_translate_insn, + .tb_stop = arm_tr_tb_stop, +}; + +static const TranslatorOps thumb_translator_ops = { + .init_disas_context = arm_tr_init_disas_context, + .tb_start = arm_tr_tb_start, + .insn_start = arm_tr_insn_start, + .translate_insn = thumb_tr_translate_insn, + .tb_stop = arm_tr_tb_stop, +}; + +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) +{ + DisasContext dc = { }; + const TranslatorOps *ops = &arm_translator_ops; + CPUARMTBFlags tb_flags = arm_tbflags_from_tb(tb); + + if (EX_TBFLAG_AM32(tb_flags, THUMB)) { + ops = &thumb_translator_ops; + } +#ifdef TARGET_AARCH64 + if (EX_TBFLAG_ANY(tb_flags, AARCH64_STATE)) { + ops = &aarch64_translator_ops; + } +#endif + + translator_loop(cpu, tb, max_insns, pc, host_pc, ops, &dc.base); +} diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h new file mode 100644 index 0000000000..20cd0e851c --- /dev/null +++ b/target/arm/tcg/translate.h @@ -0,0 +1,842 @@ +#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. */ + DisasLabel condlabel; + /* Thumb-2 conditional execution bits. */ + int condexec_mask; + int condexec_cond; + /* M-profile ECI/ICI exception-continuable instruction state */ + int eci; + /* + * trans_ functions for insns which are continuable should set this true + * after decode (ie after any UNDEF checks) + */ + bool eci_handled; + int sctlr_b; + MemOp be_data; +#if !defined(CONFIG_USER_ONLY) + int user; +#endif + ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */ + uint8_t tbii; /* TBI1|TBI0 for insns */ + uint8_t tbid; /* TBI1|TBI0 for data */ + uint8_t tcma; /* TCMA1|TCMA0 for MTE */ + 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 */ + int svl; /* current streaming vector length in bytes */ + bool vfp_enabled; /* FP enabled via FPSCR.EN */ + int vec_len; + int vec_stride; + bool v7m_handler_mode; + bool v8m_secure; /* true if v8M and we're in Secure mode */ + bool v8m_stackcheck; /* true if we need to perform v8M stack limit checks */ + bool v8m_fpccr_s_wrong; /* true if v8M FPCCR.S != v8m_secure */ + bool v7m_new_fp_ctxt_needed; /* ASPEN set but no active FP context */ + bool v7m_lspact; /* FPCCR.LSPACT set */ + /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI + * so that top level loop can generate correct syndrome information. + */ + uint32_t svc_imm; + int current_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. + * To help in catching bugs where the access check was forgotten in some + * code path, we set this flag when the access check is done, and assert + * that it is set at the point where we actually touch the FP regs. + */ + bool fp_access_checked; + bool sve_access_checked; + /* ARMv8 single-step state (this is distinct from the QEMU gdbstub + * single-step support). + */ + bool ss_active; + bool pstate_ss; + /* True if the insn just emitted was a load-exclusive instruction + * (necessary for syndrome information for single step exceptions), + * ie A64 LDX*, LDAX*, A32/T32 LDREX*, LDAEX*. + */ + bool is_ldex; + /* True if AccType_UNPRIV should be used for LDTR et al */ + bool unpriv; + /* True if v8.3-PAuth is active. */ + bool pauth_active; + /* True 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. */ + bool bt; + /* True if any CP15 access is trapped by HSTR_EL2 */ + bool hstr_active; + /* True if memory operations require alignment */ + 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. + */ + 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 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; + /* 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; +} DisasCompare; + +/* Share the TCG temporaries common between 32 and 64 bit modes. */ +extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; +extern TCGv_i64 cpu_exclusive_addr; +extern TCGv_i64 cpu_exclusive_val; + +/* + * Constant expanders for the decoders. + */ + +static inline int negate(DisasContext *s, int x) +{ + return -x; +} + +static inline int plus_1(DisasContext *s, int x) +{ + return x + 1; +} + +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; +} + +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; +} + +static inline int rsub_64(DisasContext *s, int x) +{ + return 64 - x; +} + +static inline int rsub_32(DisasContext *s, int x) +{ + return 32 - x; +} + +static inline int rsub_16(DisasContext *s, int x) +{ + return 16 - x; +} + +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 xor_2(DisasContext *s, int x) +{ + return x ^ 2; +} + +static inline int neon_3same_fp_size(DisasContext *s, int x) +{ + /* Convert 0==fp32, 1==fp16 into a MO_* value */ + return MO_32 - x; +} + +static inline int arm_dc_feature(DisasContext *dc, int feature) +{ + return (dc->features & (1ULL << feature)) != 0; +} + +static inline int get_mem_index(DisasContext *s) +{ + return arm_to_core_mmu_idx(s->mmu_idx); +} + +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 + * out unneeded bits to help the sleb128 encoder do a better job. + */ + syn &= ARM_INSN_START_WORD2_MASK; + syn >>= ARM_INSN_START_WORD2_SHIFT; + + /* 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 */ +#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ +/* CPU state was modified dynamically; exit to main loop for interrupts. */ +#define DISAS_UPDATE_EXIT DISAS_TARGET_1 +/* These instructions trap after executing, so the A32/T32 decoder must + * defer them until after the conditional execution state has been updated. + * WFI also needs special handling when single-stepping. + */ +#define DISAS_WFI DISAS_TARGET_2 +#define DISAS_SWI DISAS_TARGET_3 +/* WFE */ +#define DISAS_WFE DISAS_TARGET_4 +#define DISAS_HVC DISAS_TARGET_5 +#define DISAS_SMC DISAS_TARGET_6 +#define DISAS_YIELD DISAS_TARGET_7 +/* M profile branch which might be an exception return (and so needs + * custom end-of-TB code) + */ +#define DISAS_BX_EXCRET DISAS_TARGET_8 +/* + * 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_update_pc or runtime helper) has done so before we reach + * return from cpu_tb_exec. + */ +#define DISAS_EXIT DISAS_TARGET_9 +/* CPU state was modified dynamically; no need to exit, but do not chain. */ +#define DISAS_UPDATE_NOCHAIN DISAS_TARGET_10 + +#ifdef TARGET_AARCH64 +void a64_translate_init(void); +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_update_pc(DisasContext *s, target_long diff) +{ +} +#endif + +void arm_test_cc(DisasCompare *cmp, int cc); +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_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, tcg_env, offsetoflow32(CPUARMState, vfp.fpcr)); + tcg_gen_extract_i32(ret, ret, 26, 1); + + return ret; +} + +/* Set bits within PSTATE. */ +static inline void set_pstate_bits(uint32_t bits) +{ + TCGv_i32 p = tcg_temp_new_i32(); + + tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); + + tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); + tcg_gen_ori_i32(p, p, bits); + tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); +} + +/* Clear bits within PSTATE. */ +static inline void clear_pstate_bits(uint32_t bits) +{ + TCGv_i32 p = tcg_temp_new_i32(); + + tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); + + tcg_gen_ld_i32(p, tcg_env, offsetof(CPUARMState, pstate)); + tcg_gen_andi_i32(p, p, ~bits); + tcg_gen_st_i32(p, tcg_env, offsetof(CPUARMState, pstate)); +} + +/* If the singlestep state is Active-not-pending, advance to Active-pending. */ +static inline void gen_ss_advance(DisasContext *s) +{ + if (s->ss_active) { + s->pstate_ss = 0; + clear_pstate_bits(PSTATE_SS); + } +} + +/* Generate an architectural singlestep exception */ +static inline void gen_swstep_exception(DisasContext *s, int isv, int ex) +{ + /* 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)); +} + +/* + * Given a VFP floating point constant encoded into an 8 bit immediate in an + * instruction, expand it to the actual constant value of the specified + * size, as per the VFPExpandImm() pseudocode in the Arm ARM. + */ +uint64_t vfp_expand_imm(int size, uint8_t imm8); + +static inline void gen_vfp_absh(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_andi_i32(d, s, INT16_MAX); +} + +static inline void gen_vfp_abss(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_andi_i32(d, s, INT32_MAX); +} + +static inline void gen_vfp_absd(TCGv_i64 d, TCGv_i64 s) +{ + tcg_gen_andi_i64(d, s, INT64_MAX); +} + +static inline void gen_vfp_negh(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_xori_i32(d, s, 1u << 15); +} + +static inline void gen_vfp_negs(TCGv_i32 d, TCGv_i32 s) +{ + tcg_gen_xori_i32(d, s, 1u << 31); +} + +static inline void gen_vfp_negd(TCGv_i64 d, TCGv_i64 s) +{ + tcg_gen_xori_i64(d, s, 1ull << 63); +} + +/* Vector operations shared between ARM and AArch64. */ +void gen_gvec_ceq0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_clt0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_cgt0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_cle0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_cge0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_srshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_sqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_uqshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_sqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_uqrshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_neon_sqshli(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + int64_t c, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_uqshli(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + int64_t c, uint32_t opr_sz, uint32_t max_sz); +void gen_neon_sqshlui(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + int64_t c, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_shadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_shsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uhsub(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_srhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_urhadd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void gen_sshl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); +void gen_ushl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); +void gen_sshl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); + +void gen_uqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_uqadd_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); +void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_sqadd_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_sqadd_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); +void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_uqsub_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_uqsub_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); +void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_sqsub_bhs(TCGv_i64 res, TCGv_i64 qc, + TCGv_i64 a, TCGv_i64 b, MemOp esz); +void gen_sqsub_d(TCGv_i64 d, TCGv_i64 q, TCGv_i64 a, TCGv_i64 b); +void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_sshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_ushr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); + +void gen_srshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh); +void gen_srshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh); +void gen_urshr32_i32(TCGv_i32 d, TCGv_i32 a, int32_t sh); +void gen_urshr64_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh); + +void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, + int64_t shift, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_sqdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sqrdmulh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +void gen_gvec_addp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_smaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_umaxp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_uminp(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); + +/* + * Forward to the isar_feature_* tests given a DisasContext pointer. + */ +#define dc_isar_feature(name, ctx) \ + ({ DisasContext *ctx_ = (ctx); isar_feature_##name(ctx_->isar); }) + +/* Note that the gvec expanders operate on offsets + sizes. */ +typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); +typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, + uint32_t, uint32_t); +typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t); +typedef void GVecGen4Fn(unsigned, uint32_t, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t); + +/* Function prototype for gen_ functions for calling Neon helpers */ +typedef void NeonGenOneOpFn(TCGv_i32, TCGv_i32); +typedef void NeonGenOneOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32); +typedef void NeonGenTwoOpFn(TCGv_i32, TCGv_i32, TCGv_i32); +typedef void NeonGenTwoOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); +typedef void NeonGenThreeOpEnvFn(TCGv_i32, TCGv_env, TCGv_i32, + TCGv_i32, TCGv_i32); +typedef void NeonGenTwo64OpFn(TCGv_i64, TCGv_i64, TCGv_i64); +typedef void NeonGenTwo64OpEnvFn(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64); +typedef void NeonGenNarrowFn(TCGv_i32, TCGv_i64); +typedef void NeonGenWidenFn(TCGv_i64, TCGv_i32); +typedef void NeonGenTwoOpWidenFn(TCGv_i64, TCGv_i32, TCGv_i32); +typedef void NeonGenOneSingleOpFn(TCGv_i32, TCGv_i32, TCGv_ptr); +typedef void NeonGenTwoSingleOpFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); +typedef void NeonGenTwoDoubleOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); +typedef void NeonGenOne64OpFn(TCGv_i64, TCGv_i64); +typedef void NeonGenOne64OpEnvFn(TCGv_i64, TCGv_env, TCGv_i64); +typedef void CryptoTwoOpFn(TCGv_ptr, TCGv_ptr); +typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32); +typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); +typedef void AtomicThreeOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGArg, MemOp); +typedef void WideShiftImmFn(TCGv_i64, TCGv_i64, int64_t shift); +typedef void WideShiftFn(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i32); +typedef void ShiftImmFn(TCGv_i32, TCGv_i32, int32_t shift); +typedef void ShiftFn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); + +/** + * arm_tbflags_from_tb: + * @tb: the TranslationBlock + * + * Extract the flag values from @tb. + */ +static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb) +{ + return (CPUARMTBFlags){ tb->flags, tb->cs_base }; +} + +/* + * Enum for argument to fpstatus_ptr(). + */ +typedef enum ARMFPStatusFlavour { + FPST_FPCR, + FPST_FPCR_F16, + FPST_STD, + FPST_STD_F16, +} ARMFPStatusFlavour; + +/** + * fpstatus_ptr: return TCGv_ptr to the specified fp_status field + * + * We have multiple softfloat float_status fields in the Arm CPU state struct + * (see the comment in cpu.h for details). Return a TCGv_ptr which has + * been set up to point to the requested field in the CPU state struct. + * The options are: + * + * FPST_FPCR + * for non-FP16 operations controlled by the FPCR + * FPST_FPCR_F16 + * for operations controlled by the FPCR where FPCR.FZ16 is to be used + * FPST_STD + * for A32/T32 Neon operations using the "standard FPSCR value" + * FPST_STD_F16 + * as FPST_STD, but where FPCR.FZ16 is to be used + */ +static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) +{ + TCGv_ptr statusptr = tcg_temp_new_ptr(); + int offset; + + switch (flavour) { + case FPST_FPCR: + offset = offsetof(CPUARMState, vfp.fp_status); + break; + case FPST_FPCR_F16: + offset = offsetof(CPUARMState, vfp.fp_status_f16); + break; + case FPST_STD: + offset = offsetof(CPUARMState, vfp.standard_fp_status); + break; + case FPST_STD_F16: + offset = offsetof(CPUARMState, vfp.standard_fp_status_f16); + break; + default: + g_assert_not_reached(); + } + tcg_gen_addi_ptr(statusptr, tcg_env, offset); + return statusptr; +} + +/** + * 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, + * endianness, and atomicity. + * + * If (op & MO_AMASK) then the operation already contains the required + * alignment, e.g. for AccType_ATOMIC. Otherwise, this an optionally + * unaligned operation, e.g. for AccType_NORMAL. + * + * In the latter case, there are configuration bits that require alignment, + * 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_atom(DisasContext *s, MemOp opc, MemOp atom) +{ + if (s->align_mem && !(opc & MO_AMASK)) { + opc |= MO_ALIGN; + } + 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); +} + +/** + * asimd_imm_const: Expand an encoded SIMD constant value + * + * Expand a SIMD constant value. This is essentially the pseudocode + * AdvSIMDExpandImm, except that we also perform the boolean NOT needed for + * VMVN and VBIC (when cmode < 14 && op == 1). + * + * The combination cmode == 15 op == 1 is a reserved encoding for AArch32; + * callers must catch this; we return the 64-bit constant value defined + * for AArch64. + * + * cmode = 2,3,4,5,6,7,10,11,12,13 imm=0 was UNPREDICTABLE in v7A but + * is either not unpredictable or merely CONSTRAINED UNPREDICTABLE in v8A; + * we produce an immediate constant value of 0 in these cases. + */ +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. + */ +#define TRANS(NAME, FUNC, ...) \ + static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ + { return FUNC(s, __VA_ARGS__); } +#define TRANS_FEAT(NAME, FEAT, FUNC, ...) \ + 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/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c new file mode 100644 index 0000000000..e825d501a2 --- /dev/null +++ b/target/arm/tcg/vec_helper.c @@ -0,0 +1,3074 @@ +/* + * ARM AdvSIMD / SVE Vector Operations + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" +#include "fpu/softfloat.h" +#include "qemu/int128.h" +#include "crypto/clmul.h" +#include "vec_internal.h" + +/* + * Data for expanding active predicate bits to bytes, for byte elements. + * + * for (i = 0; i < 256; ++i) { + * unsigned long m = 0; + * for (j = 0; j < 8; j++) { + * if ((i >> j) & 1) { + * m |= 0xfful << (j << 3); + * } + * } + * printf("0x%016lx,\n", m); + * } + */ +const uint64_t expand_pred_b_data[256] = { + 0x0000000000000000, 0x00000000000000ff, 0x000000000000ff00, + 0x000000000000ffff, 0x0000000000ff0000, 0x0000000000ff00ff, + 0x0000000000ffff00, 0x0000000000ffffff, 0x00000000ff000000, + 0x00000000ff0000ff, 0x00000000ff00ff00, 0x00000000ff00ffff, + 0x00000000ffff0000, 0x00000000ffff00ff, 0x00000000ffffff00, + 0x00000000ffffffff, 0x000000ff00000000, 0x000000ff000000ff, + 0x000000ff0000ff00, 0x000000ff0000ffff, 0x000000ff00ff0000, + 0x000000ff00ff00ff, 0x000000ff00ffff00, 0x000000ff00ffffff, + 0x000000ffff000000, 0x000000ffff0000ff, 0x000000ffff00ff00, + 0x000000ffff00ffff, 0x000000ffffff0000, 0x000000ffffff00ff, + 0x000000ffffffff00, 0x000000ffffffffff, 0x0000ff0000000000, + 0x0000ff00000000ff, 0x0000ff000000ff00, 0x0000ff000000ffff, + 0x0000ff0000ff0000, 0x0000ff0000ff00ff, 0x0000ff0000ffff00, + 0x0000ff0000ffffff, 0x0000ff00ff000000, 0x0000ff00ff0000ff, + 0x0000ff00ff00ff00, 0x0000ff00ff00ffff, 0x0000ff00ffff0000, + 0x0000ff00ffff00ff, 0x0000ff00ffffff00, 0x0000ff00ffffffff, + 0x0000ffff00000000, 0x0000ffff000000ff, 0x0000ffff0000ff00, + 0x0000ffff0000ffff, 0x0000ffff00ff0000, 0x0000ffff00ff00ff, + 0x0000ffff00ffff00, 0x0000ffff00ffffff, 0x0000ffffff000000, + 0x0000ffffff0000ff, 0x0000ffffff00ff00, 0x0000ffffff00ffff, + 0x0000ffffffff0000, 0x0000ffffffff00ff, 0x0000ffffffffff00, + 0x0000ffffffffffff, 0x00ff000000000000, 0x00ff0000000000ff, + 0x00ff00000000ff00, 0x00ff00000000ffff, 0x00ff000000ff0000, + 0x00ff000000ff00ff, 0x00ff000000ffff00, 0x00ff000000ffffff, + 0x00ff0000ff000000, 0x00ff0000ff0000ff, 0x00ff0000ff00ff00, + 0x00ff0000ff00ffff, 0x00ff0000ffff0000, 0x00ff0000ffff00ff, + 0x00ff0000ffffff00, 0x00ff0000ffffffff, 0x00ff00ff00000000, + 0x00ff00ff000000ff, 0x00ff00ff0000ff00, 0x00ff00ff0000ffff, + 0x00ff00ff00ff0000, 0x00ff00ff00ff00ff, 0x00ff00ff00ffff00, + 0x00ff00ff00ffffff, 0x00ff00ffff000000, 0x00ff00ffff0000ff, + 0x00ff00ffff00ff00, 0x00ff00ffff00ffff, 0x00ff00ffffff0000, + 0x00ff00ffffff00ff, 0x00ff00ffffffff00, 0x00ff00ffffffffff, + 0x00ffff0000000000, 0x00ffff00000000ff, 0x00ffff000000ff00, + 0x00ffff000000ffff, 0x00ffff0000ff0000, 0x00ffff0000ff00ff, + 0x00ffff0000ffff00, 0x00ffff0000ffffff, 0x00ffff00ff000000, + 0x00ffff00ff0000ff, 0x00ffff00ff00ff00, 0x00ffff00ff00ffff, + 0x00ffff00ffff0000, 0x00ffff00ffff00ff, 0x00ffff00ffffff00, + 0x00ffff00ffffffff, 0x00ffffff00000000, 0x00ffffff000000ff, + 0x00ffffff0000ff00, 0x00ffffff0000ffff, 0x00ffffff00ff0000, + 0x00ffffff00ff00ff, 0x00ffffff00ffff00, 0x00ffffff00ffffff, + 0x00ffffffff000000, 0x00ffffffff0000ff, 0x00ffffffff00ff00, + 0x00ffffffff00ffff, 0x00ffffffffff0000, 0x00ffffffffff00ff, + 0x00ffffffffffff00, 0x00ffffffffffffff, 0xff00000000000000, + 0xff000000000000ff, 0xff0000000000ff00, 0xff0000000000ffff, + 0xff00000000ff0000, 0xff00000000ff00ff, 0xff00000000ffff00, + 0xff00000000ffffff, 0xff000000ff000000, 0xff000000ff0000ff, + 0xff000000ff00ff00, 0xff000000ff00ffff, 0xff000000ffff0000, + 0xff000000ffff00ff, 0xff000000ffffff00, 0xff000000ffffffff, + 0xff0000ff00000000, 0xff0000ff000000ff, 0xff0000ff0000ff00, + 0xff0000ff0000ffff, 0xff0000ff00ff0000, 0xff0000ff00ff00ff, + 0xff0000ff00ffff00, 0xff0000ff00ffffff, 0xff0000ffff000000, + 0xff0000ffff0000ff, 0xff0000ffff00ff00, 0xff0000ffff00ffff, + 0xff0000ffffff0000, 0xff0000ffffff00ff, 0xff0000ffffffff00, + 0xff0000ffffffffff, 0xff00ff0000000000, 0xff00ff00000000ff, + 0xff00ff000000ff00, 0xff00ff000000ffff, 0xff00ff0000ff0000, + 0xff00ff0000ff00ff, 0xff00ff0000ffff00, 0xff00ff0000ffffff, + 0xff00ff00ff000000, 0xff00ff00ff0000ff, 0xff00ff00ff00ff00, + 0xff00ff00ff00ffff, 0xff00ff00ffff0000, 0xff00ff00ffff00ff, + 0xff00ff00ffffff00, 0xff00ff00ffffffff, 0xff00ffff00000000, + 0xff00ffff000000ff, 0xff00ffff0000ff00, 0xff00ffff0000ffff, + 0xff00ffff00ff0000, 0xff00ffff00ff00ff, 0xff00ffff00ffff00, + 0xff00ffff00ffffff, 0xff00ffffff000000, 0xff00ffffff0000ff, + 0xff00ffffff00ff00, 0xff00ffffff00ffff, 0xff00ffffffff0000, + 0xff00ffffffff00ff, 0xff00ffffffffff00, 0xff00ffffffffffff, + 0xffff000000000000, 0xffff0000000000ff, 0xffff00000000ff00, + 0xffff00000000ffff, 0xffff000000ff0000, 0xffff000000ff00ff, + 0xffff000000ffff00, 0xffff000000ffffff, 0xffff0000ff000000, + 0xffff0000ff0000ff, 0xffff0000ff00ff00, 0xffff0000ff00ffff, + 0xffff0000ffff0000, 0xffff0000ffff00ff, 0xffff0000ffffff00, + 0xffff0000ffffffff, 0xffff00ff00000000, 0xffff00ff000000ff, + 0xffff00ff0000ff00, 0xffff00ff0000ffff, 0xffff00ff00ff0000, + 0xffff00ff00ff00ff, 0xffff00ff00ffff00, 0xffff00ff00ffffff, + 0xffff00ffff000000, 0xffff00ffff0000ff, 0xffff00ffff00ff00, + 0xffff00ffff00ffff, 0xffff00ffffff0000, 0xffff00ffffff00ff, + 0xffff00ffffffff00, 0xffff00ffffffffff, 0xffffff0000000000, + 0xffffff00000000ff, 0xffffff000000ff00, 0xffffff000000ffff, + 0xffffff0000ff0000, 0xffffff0000ff00ff, 0xffffff0000ffff00, + 0xffffff0000ffffff, 0xffffff00ff000000, 0xffffff00ff0000ff, + 0xffffff00ff00ff00, 0xffffff00ff00ffff, 0xffffff00ffff0000, + 0xffffff00ffff00ff, 0xffffff00ffffff00, 0xffffff00ffffffff, + 0xffffffff00000000, 0xffffffff000000ff, 0xffffffff0000ff00, + 0xffffffff0000ffff, 0xffffffff00ff0000, 0xffffffff00ff00ff, + 0xffffffff00ffff00, 0xffffffff00ffffff, 0xffffffffff000000, + 0xffffffffff0000ff, 0xffffffffff00ff00, 0xffffffffff00ffff, + 0xffffffffffff0000, 0xffffffffffff00ff, 0xffffffffffffff00, + 0xffffffffffffffff, +}; + +/* + * Similarly for half-word elements. + * for (i = 0; i < 256; ++i) { + * unsigned long m = 0; + * if (i & 0xaa) { + * continue; + * } + * for (j = 0; j < 8; j += 2) { + * if ((i >> j) & 1) { + * m |= 0xfffful << (j << 3); + * } + * } + * printf("[0x%x] = 0x%016lx,\n", i, m); + * } + */ +const uint64_t expand_pred_h_data[0x55 + 1] = { + [0x01] = 0x000000000000ffff, [0x04] = 0x00000000ffff0000, + [0x05] = 0x00000000ffffffff, [0x10] = 0x0000ffff00000000, + [0x11] = 0x0000ffff0000ffff, [0x14] = 0x0000ffffffff0000, + [0x15] = 0x0000ffffffffffff, [0x40] = 0xffff000000000000, + [0x41] = 0xffff00000000ffff, [0x44] = 0xffff0000ffff0000, + [0x45] = 0xffff0000ffffffff, [0x50] = 0xffffffff00000000, + [0x51] = 0xffffffff0000ffff, [0x54] = 0xffffffffffff0000, + [0x55] = 0xffffffffffffffff, +}; + +/* Signed saturating rounding doubling multiply-accumulate high half, 8-bit */ +int8_t do_sqrdmlah_b(int8_t src1, int8_t src2, int8_t src3, + bool neg, bool round) +{ + /* + * Simplify: + * = ((a3 << 8) + ((e1 * e2) << 1) + (round << 7)) >> 8 + * = ((a3 << 7) + (e1 * e2) + (round << 6)) >> 7 + */ + int32_t ret = (int32_t)src1 * src2; + if (neg) { + ret = -ret; + } + ret += ((int32_t)src3 << 7) + (round << 6); + ret >>= 7; + + if (ret != (int8_t)ret) { + ret = (ret < 0 ? INT8_MIN : INT8_MAX); + } + return ret; +} + +void HELPER(sve2_sqrdmlah_b)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm, *a = va; + + for (i = 0; i < opr_sz; ++i) { + d[i] = do_sqrdmlah_b(n[i], m[i], a[i], false, true); + } +} + +void HELPER(sve2_sqrdmlsh_b)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm, *a = va; + + for (i = 0; i < opr_sz; ++i) { + d[i] = do_sqrdmlah_b(n[i], m[i], a[i], true, true); + } +} + +void HELPER(sve2_sqdmulh_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + d[i] = do_sqrdmlah_b(n[i], m[i], 0, false, false); + } +} + +void HELPER(sve2_sqrdmulh_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + d[i] = do_sqrdmlah_b(n[i], m[i], 0, false, true); + } +} + +/* Signed saturating rounding doubling multiply-accumulate high half, 16-bit */ +int16_t do_sqrdmlah_h(int16_t src1, int16_t src2, int16_t src3, + bool neg, bool round, uint32_t *sat) +{ + /* Simplify similarly to do_sqrdmlah_b above. */ + int32_t ret = (int32_t)src1 * src2; + if (neg) { + ret = -ret; + } + ret += ((int32_t)src3 << 15) + (round << 14); + ret >>= 15; + + if (ret != (int16_t)ret) { + *sat = 1; + ret = (ret < 0 ? INT16_MIN : INT16_MAX); + } + return ret; +} + +uint32_t HELPER(neon_qrdmlah_s16)(CPUARMState *env, uint32_t src1, + uint32_t src2, uint32_t src3) +{ + uint32_t *sat = &env->vfp.qc[0]; + uint16_t e1 = do_sqrdmlah_h(src1, src2, src3, false, true, sat); + uint16_t e2 = do_sqrdmlah_h(src1 >> 16, src2 >> 16, src3 >> 16, + false, true, sat); + return deposit32(e1, 16, 16, e2); +} + +void HELPER(gvec_qrdmlah_s16)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int16_t *d = vd; + int16_t *n = vn; + int16_t *m = vm; + uintptr_t i; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], d[i], false, true, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +uint32_t HELPER(neon_qrdmlsh_s16)(CPUARMState *env, uint32_t src1, + uint32_t src2, uint32_t src3) +{ + uint32_t *sat = &env->vfp.qc[0]; + uint16_t e1 = do_sqrdmlah_h(src1, src2, src3, true, true, sat); + uint16_t e2 = do_sqrdmlah_h(src1 >> 16, src2 >> 16, src3 >> 16, + true, true, sat); + return deposit32(e1, 16, 16, e2); +} + +void HELPER(gvec_qrdmlsh_s16)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int16_t *d = vd; + int16_t *n = vn; + int16_t *m = vm; + uintptr_t i; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], d[i], true, true, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqdmulh_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, false, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmulh_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, true, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqdmulh_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); + + for (i = 0; i < elements; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, false, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmulh_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); + + for (i = 0; i < elements; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmlah_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); + + for (i = 0; i < elements; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, d[i + j], false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmlsh_idx_h)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + intptr_t elements = opr_sz / 2; + intptr_t eltspersegment = MIN(16 / 2, elements); + + for (i = 0; i < elements; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, d[i + j], true, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(sve2_sqrdmlah_h)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm, *a = va; + uint32_t discard; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], a[i], false, true, &discard); + } +} + +void HELPER(sve2_sqrdmlsh_h)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm, *a = va; + uint32_t discard; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], a[i], true, true, &discard); + } +} + +void HELPER(sve2_sqdmulh_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + uint32_t discard; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, false, &discard); + } +} + +void HELPER(sve2_sqrdmulh_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + uint32_t discard; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, true, &discard); + } +} + +void HELPER(sve2_sqdmulh_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + uint32_t discard; + + for (i = 0; i < opr_sz / 2; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < 16 / 2; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, false, &discard); + } + } +} + +void HELPER(sve2_sqrdmulh_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); + uint32_t discard; + + for (i = 0; i < opr_sz / 2; i += 16 / 2) { + int16_t mm = m[i]; + for (j = 0; j < 16 / 2; ++j) { + d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, true, &discard); + } + } +} + +/* Signed saturating rounding doubling multiply-accumulate high half, 32-bit */ +int32_t do_sqrdmlah_s(int32_t src1, int32_t src2, int32_t src3, + bool neg, bool round, uint32_t *sat) +{ + /* Simplify similarly to do_sqrdmlah_b above. */ + int64_t ret = (int64_t)src1 * src2; + if (neg) { + ret = -ret; + } + ret += ((int64_t)src3 << 31) + (round << 30); + ret >>= 31; + + if (ret != (int32_t)ret) { + *sat = 1; + ret = (ret < 0 ? INT32_MIN : INT32_MAX); + } + return ret; +} + +uint32_t HELPER(neon_qrdmlah_s32)(CPUARMState *env, int32_t src1, + int32_t src2, int32_t src3) +{ + uint32_t *sat = &env->vfp.qc[0]; + return do_sqrdmlah_s(src1, src2, src3, false, true, sat); +} + +void HELPER(gvec_qrdmlah_s32)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int32_t *d = vd; + int32_t *n = vn; + int32_t *m = vm; + uintptr_t i; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], d[i], false, true, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +uint32_t HELPER(neon_qrdmlsh_s32)(CPUARMState *env, int32_t src1, + int32_t src2, int32_t src3) +{ + uint32_t *sat = &env->vfp.qc[0]; + return do_sqrdmlah_s(src1, src2, src3, true, true, sat); +} + +void HELPER(gvec_qrdmlsh_s32)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + int32_t *d = vd; + int32_t *n = vn; + int32_t *m = vm; + uintptr_t i; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], d[i], true, true, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqdmulh_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, false, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmulh_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, true, vq); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqdmulh_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + + for (i = 0; i < elements; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, false, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmulh_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + + for (i = 0; i < elements; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmlah_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + + for (i = 0; i < elements; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, d[i + j], false, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(neon_sqrdmlsh_idx_s)(void *vd, void *vn, void *vm, + void *vq, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + + for (i = 0; i < elements; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, d[i + j], true, true, vq); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(sve2_sqrdmlah_s)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm, *a = va; + uint32_t discard; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], a[i], false, true, &discard); + } +} + +void HELPER(sve2_sqrdmlsh_s)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm, *a = va; + uint32_t discard; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], a[i], true, true, &discard); + } +} + +void HELPER(sve2_sqdmulh_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm; + uint32_t discard; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, false, &discard); + } +} + +void HELPER(sve2_sqrdmulh_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm; + uint32_t discard; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, true, &discard); + } +} + +void HELPER(sve2_sqdmulh_idx_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + uint32_t discard; + + for (i = 0; i < opr_sz / 4; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < 16 / 4; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, false, &discard); + } + } +} + +void HELPER(sve2_sqrdmulh_idx_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); + uint32_t discard; + + for (i = 0; i < opr_sz / 4; i += 16 / 4) { + int32_t mm = m[i]; + for (j = 0; j < 16 / 4; ++j) { + d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, true, &discard); + } + } +} + +/* Signed saturating rounding doubling multiply-accumulate high half, 64-bit */ +static int64_t do_sat128_d(Int128 r) +{ + int64_t ls = int128_getlo(r); + int64_t hs = int128_gethi(r); + + if (unlikely(hs != (ls >> 63))) { + return hs < 0 ? INT64_MIN : INT64_MAX; + } + return ls; +} + +int64_t do_sqrdmlah_d(int64_t n, int64_t m, int64_t a, bool neg, bool round) +{ + uint64_t l, h; + Int128 r, t; + + /* As in do_sqrdmlah_b, but with 128-bit arithmetic. */ + muls64(&l, &h, m, n); + r = int128_make128(l, h); + if (neg) { + r = int128_neg(r); + } + if (a) { + t = int128_exts64(a); + t = int128_lshift(t, 63); + r = int128_add(r, t); + } + if (round) { + t = int128_exts64(1ll << 62); + r = int128_add(r, t); + } + r = int128_rshift(r, 63); + + return do_sat128_d(r); +} + +void HELPER(sve2_sqrdmlah_d)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm, *a = va; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = do_sqrdmlah_d(n[i], m[i], a[i], false, true); + } +} + +void HELPER(sve2_sqrdmlsh_d)(void *vd, void *vn, void *vm, + void *va, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm, *a = va; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = do_sqrdmlah_d(n[i], m[i], a[i], true, true); + } +} + +void HELPER(sve2_sqdmulh_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = do_sqrdmlah_d(n[i], m[i], 0, false, false); + } +} + +void HELPER(sve2_sqrdmulh_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = do_sqrdmlah_d(n[i], m[i], 0, false, true); + } +} + +void HELPER(sve2_sqdmulh_idx_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int64_t *d = vd, *n = vn, *m = (int64_t *)vm + idx; + + for (i = 0; i < opr_sz / 8; i += 16 / 8) { + int64_t mm = m[i]; + for (j = 0; j < 16 / 8; ++j) { + d[i + j] = do_sqrdmlah_d(n[i + j], mm, 0, false, false); + } + } +} + +void HELPER(sve2_sqrdmulh_idx_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + int idx = simd_data(desc); + int64_t *d = vd, *n = vn, *m = (int64_t *)vm + idx; + + for (i = 0; i < opr_sz / 8; i += 16 / 8) { + int64_t mm = m[i]; + for (j = 0; j < 16 / 8; ++j) { + d[i + j] = do_sqrdmlah_d(n[i + j], mm, 0, false, true); + } + } +} + +/* Integer 8 and 16-bit dot-product. + * + * Note that for the loops herein, host endianness does not matter + * with respect to the ordering of data within the quad-width lanes. + * All elements are treated equally, no matter where they are. + */ + +#define DO_DOT(NAME, TYPED, TYPEN, TYPEM) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPED *d = vd, *a = va; \ + TYPEN *n = vn; \ + TYPEM *m = vm; \ + for (i = 0; i < opr_sz / sizeof(TYPED); ++i) { \ + d[i] = (a[i] + \ + (TYPED)n[i * 4 + 0] * m[i * 4 + 0] + \ + (TYPED)n[i * 4 + 1] * m[i * 4 + 1] + \ + (TYPED)n[i * 4 + 2] * m[i * 4 + 2] + \ + (TYPED)n[i * 4 + 3] * m[i * 4 + 3]); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_DOT(gvec_sdot_b, int32_t, int8_t, int8_t) +DO_DOT(gvec_udot_b, uint32_t, uint8_t, uint8_t) +DO_DOT(gvec_usdot_b, uint32_t, uint8_t, int8_t) +DO_DOT(gvec_sdot_h, int64_t, int16_t, int16_t) +DO_DOT(gvec_udot_h, uint64_t, uint16_t, uint16_t) + +#define DO_DOT_IDX(NAME, TYPED, TYPEN, TYPEM, HD) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t i = 0, opr_sz = simd_oprsz(desc); \ + intptr_t opr_sz_n = opr_sz / sizeof(TYPED); \ + /* \ + * Special case: opr_sz == 8 from AA64/AA32 advsimd means the \ + * first iteration might not be a full 16 byte segment. But \ + * for vector lengths beyond that this must be SVE and we know \ + * opr_sz is a multiple of 16, so we need not clamp segend \ + * to opr_sz_n when we advance it at the end of the loop. \ + */ \ + intptr_t segend = MIN(16 / sizeof(TYPED), opr_sz_n); \ + intptr_t index = simd_data(desc); \ + TYPED *d = vd, *a = va; \ + TYPEN *n = vn; \ + TYPEM *m_indexed = (TYPEM *)vm + HD(index) * 4; \ + do { \ + TYPED m0 = m_indexed[i * 4 + 0]; \ + TYPED m1 = m_indexed[i * 4 + 1]; \ + TYPED m2 = m_indexed[i * 4 + 2]; \ + TYPED m3 = m_indexed[i * 4 + 3]; \ + do { \ + d[i] = (a[i] + \ + n[i * 4 + 0] * m0 + \ + n[i * 4 + 1] * m1 + \ + n[i * 4 + 2] * m2 + \ + n[i * 4 + 3] * m3); \ + } while (++i < segend); \ + segend = i + (16 / sizeof(TYPED)); \ + } while (i < opr_sz_n); \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_DOT_IDX(gvec_sdot_idx_b, int32_t, int8_t, int8_t, H4) +DO_DOT_IDX(gvec_udot_idx_b, uint32_t, uint8_t, uint8_t, H4) +DO_DOT_IDX(gvec_sudot_idx_b, int32_t, int8_t, uint8_t, H4) +DO_DOT_IDX(gvec_usdot_idx_b, int32_t, uint8_t, int8_t, H4) +DO_DOT_IDX(gvec_sdot_idx_h, int64_t, int16_t, int16_t, H8) +DO_DOT_IDX(gvec_udot_idx_h, uint64_t, uint16_t, uint16_t, H8) + +void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float16 *d = vd; + float16 *n = vn; + float16 *m = vm; + float_status *fpst = vfpst; + uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = neg_real ^ 1; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 15; + neg_imag <<= 15; + + for (i = 0; i < opr_sz / 2; i += 2) { + float16 e0 = n[H2(i)]; + float16 e1 = m[H2(i + 1)] ^ neg_imag; + float16 e2 = n[H2(i + 1)]; + float16 e3 = m[H2(i)] ^ neg_real; + + d[H2(i)] = float16_add(e0, e1, fpst); + d[H2(i + 1)] = float16_add(e2, e3, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float32 *d = vd; + float32 *n = vn; + float32 *m = vm; + float_status *fpst = vfpst; + uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = neg_real ^ 1; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 31; + neg_imag <<= 31; + + for (i = 0; i < opr_sz / 4; i += 2) { + float32 e0 = n[H4(i)]; + float32 e1 = m[H4(i + 1)] ^ neg_imag; + float32 e2 = n[H4(i + 1)]; + float32 e3 = m[H4(i)] ^ neg_real; + + d[H4(i)] = float32_add(e0, e1, fpst); + d[H4(i + 1)] = float32_add(e2, e3, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float64 *d = vd; + float64 *n = vn; + float64 *m = vm; + float_status *fpst = vfpst; + uint64_t neg_real = extract64(desc, SIMD_DATA_SHIFT, 1); + uint64_t neg_imag = neg_real ^ 1; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 63; + neg_imag <<= 63; + + for (i = 0; i < opr_sz / 8; i += 2) { + float64 e0 = n[i]; + float64 e1 = m[i + 1] ^ neg_imag; + float64 e2 = n[i + 1]; + float64 e3 = m[i] ^ neg_real; + + d[i] = float64_add(e0, e1, fpst); + d[i + 1] = float64_add(e2, e3, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, void *va, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float16 *d = vd, *n = vn, *m = vm, *a = va; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t neg_real = flip ^ neg_imag; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 15; + neg_imag <<= 15; + + for (i = 0; i < opr_sz / 2; i += 2) { + float16 e2 = n[H2(i + flip)]; + float16 e1 = m[H2(i + flip)] ^ neg_real; + float16 e4 = e2; + float16 e3 = m[H2(i + 1 - flip)] ^ neg_imag; + + d[H2(i)] = float16_muladd(e2, e1, a[H2(i)], 0, fpst); + d[H2(i + 1)] = float16_muladd(e4, e3, a[H2(i + 1)], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, void *va, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float16 *d = vd, *n = vn, *m = vm, *a = va; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); + uint32_t neg_real = flip ^ neg_imag; + intptr_t elements = opr_sz / sizeof(float16); + intptr_t eltspersegment = MIN(16 / sizeof(float16), elements); + intptr_t i, j; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 15; + neg_imag <<= 15; + + for (i = 0; i < elements; i += eltspersegment) { + float16 mr = m[H2(i + 2 * index + 0)]; + float16 mi = m[H2(i + 2 * index + 1)]; + float16 e1 = neg_real ^ (flip ? mi : mr); + float16 e3 = neg_imag ^ (flip ? mr : mi); + + for (j = i; j < i + eltspersegment; j += 2) { + float16 e2 = n[H2(j + flip)]; + float16 e4 = e2; + + d[H2(j)] = float16_muladd(e2, e1, a[H2(j)], 0, fpst); + d[H2(j + 1)] = float16_muladd(e4, e3, a[H2(j + 1)], 0, fpst); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlas)(void *vd, void *vn, void *vm, void *va, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float32 *d = vd, *n = vn, *m = vm, *a = va; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint32_t neg_real = flip ^ neg_imag; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 31; + neg_imag <<= 31; + + for (i = 0; i < opr_sz / 4; i += 2) { + float32 e2 = n[H4(i + flip)]; + float32 e1 = m[H4(i + flip)] ^ neg_real; + float32 e4 = e2; + float32 e3 = m[H4(i + 1 - flip)] ^ neg_imag; + + d[H4(i)] = float32_muladd(e2, e1, a[H4(i)], 0, fpst); + d[H4(i + 1)] = float32_muladd(e4, e3, a[H4(i + 1)], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, void *va, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float32 *d = vd, *n = vn, *m = vm, *a = va; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); + uint32_t neg_real = flip ^ neg_imag; + intptr_t elements = opr_sz / sizeof(float32); + intptr_t eltspersegment = MIN(16 / sizeof(float32), elements); + intptr_t i, j; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 31; + neg_imag <<= 31; + + for (i = 0; i < elements; i += eltspersegment) { + float32 mr = m[H4(i + 2 * index + 0)]; + float32 mi = m[H4(i + 2 * index + 1)]; + float32 e1 = neg_real ^ (flip ? mi : mr); + float32 e3 = neg_imag ^ (flip ? mr : mi); + + for (j = i; j < i + eltspersegment; j += 2) { + float32 e2 = n[H4(j + flip)]; + float32 e4 = e2; + + d[H4(j)] = float32_muladd(e2, e1, a[H4(j)], 0, fpst); + d[H4(j + 1)] = float32_muladd(e4, e3, a[H4(j + 1)], 0, fpst); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, void *va, + void *vfpst, uint32_t desc) +{ + uintptr_t opr_sz = simd_oprsz(desc); + float64 *d = vd, *n = vn, *m = vm, *a = va; + float_status *fpst = vfpst; + intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); + uint64_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + uint64_t neg_real = flip ^ neg_imag; + uintptr_t i; + + /* Shift boolean to the sign bit so we can xor to negate. */ + neg_real <<= 63; + neg_imag <<= 63; + + for (i = 0; i < opr_sz / 8; i += 2) { + float64 e2 = n[i + flip]; + float64 e1 = m[i + flip] ^ neg_real; + float64 e4 = e2; + float64 e3 = m[i + 1 - flip] ^ neg_imag; + + d[i] = float64_muladd(e2, e1, a[i], 0, fpst); + d[i + 1] = float64_muladd(e4, e3, a[i + 1], 0, fpst); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* + * Floating point comparisons producing an integer result (all 1s or all 0s). + * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. + * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. + */ +static uint16_t float16_ceq(float16 op1, float16 op2, float_status *stat) +{ + return -float16_eq_quiet(op1, op2, stat); +} + +static uint32_t float32_ceq(float32 op1, float32 op2, float_status *stat) +{ + return -float32_eq_quiet(op1, op2, stat); +} + +static uint64_t float64_ceq(float64 op1, float64 op2, float_status *stat) +{ + return -float64_eq_quiet(op1, op2, stat); +} + +static uint16_t float16_cge(float16 op1, float16 op2, float_status *stat) +{ + return -float16_le(op2, op1, stat); +} + +static uint32_t float32_cge(float32 op1, float32 op2, float_status *stat) +{ + return -float32_le(op2, op1, stat); +} + +static uint64_t float64_cge(float64 op1, float64 op2, float_status *stat) +{ + return -float64_le(op2, op1, stat); +} + +static uint16_t float16_cgt(float16 op1, float16 op2, float_status *stat) +{ + return -float16_lt(op2, op1, stat); +} + +static uint32_t float32_cgt(float32 op1, float32 op2, float_status *stat) +{ + return -float32_lt(op2, op1, stat); +} + +static uint64_t float64_cgt(float64 op1, float64 op2, float_status *stat) +{ + return -float64_lt(op2, op1, stat); +} + +static uint16_t float16_acge(float16 op1, float16 op2, float_status *stat) +{ + return -float16_le(float16_abs(op2), float16_abs(op1), stat); +} + +static uint32_t float32_acge(float32 op1, float32 op2, float_status *stat) +{ + return -float32_le(float32_abs(op2), float32_abs(op1), stat); +} + +static uint64_t float64_acge(float64 op1, float64 op2, float_status *stat) +{ + return -float64_le(float64_abs(op2), float64_abs(op1), stat); +} + +static uint16_t float16_acgt(float16 op1, float16 op2, float_status *stat) +{ + return -float16_lt(float16_abs(op2), float16_abs(op1), stat); +} + +static uint32_t float32_acgt(float32 op1, float32 op2, float_status *stat) +{ + return -float32_lt(float32_abs(op2), float32_abs(op1), stat); +} + +static uint64_t float64_acgt(float64 op1, float64 op2, float_status *stat) +{ + return -float64_lt(float64_abs(op2), float64_abs(op1), stat); +} + +static int16_t vfp_tosszh(float16 x, void *fpstp) +{ + float_status *fpst = fpstp; + if (float16_is_any_nan(x)) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int16_round_to_zero(x, fpst); +} + +static uint16_t vfp_touszh(float16 x, void *fpstp) +{ + float_status *fpst = fpstp; + if (float16_is_any_nan(x)) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint16_round_to_zero(x, fpst); +} + +#define DO_2OP(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], stat); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) +DO_2OP(gvec_frecpe_s, helper_recpe_f32, float32) +DO_2OP(gvec_frecpe_d, helper_recpe_f64, float64) + +DO_2OP(gvec_frsqrte_h, helper_rsqrte_f16, float16) +DO_2OP(gvec_frsqrte_s, helper_rsqrte_f32, float32) +DO_2OP(gvec_frsqrte_d, helper_rsqrte_f64, float64) + +DO_2OP(gvec_vrintx_h, float16_round_to_int, float16) +DO_2OP(gvec_vrintx_s, float32_round_to_int, float32) + +DO_2OP(gvec_sitos, helper_vfp_sitos, int32_t) +DO_2OP(gvec_uitos, helper_vfp_uitos, uint32_t) +DO_2OP(gvec_tosizs, helper_vfp_tosizs, float32) +DO_2OP(gvec_touizs, helper_vfp_touizs, float32) +DO_2OP(gvec_sstoh, int16_to_float16, int16_t) +DO_2OP(gvec_ustoh, uint16_to_float16, uint16_t) +DO_2OP(gvec_tosszh, vfp_tosszh, float16) +DO_2OP(gvec_touszh, vfp_touszh, float16) + +#define WRAP_CMP0_FWD(FN, CMPOP, TYPE) \ + static TYPE TYPE##_##FN##0(TYPE op, float_status *stat) \ + { \ + return TYPE##_##CMPOP(op, TYPE##_zero, stat); \ + } + +#define WRAP_CMP0_REV(FN, CMPOP, TYPE) \ + static TYPE TYPE##_##FN##0(TYPE op, float_status *stat) \ + { \ + return TYPE##_##CMPOP(TYPE##_zero, op, stat); \ + } + +#define DO_2OP_CMP0(FN, CMPOP, DIRN) \ + WRAP_CMP0_##DIRN(FN, CMPOP, float16) \ + WRAP_CMP0_##DIRN(FN, CMPOP, float32) \ + DO_2OP(gvec_f##FN##0_h, float16_##FN##0, float16) \ + DO_2OP(gvec_f##FN##0_s, float32_##FN##0, float32) + +DO_2OP_CMP0(cgt, cgt, FWD) +DO_2OP_CMP0(cge, cge, FWD) +DO_2OP_CMP0(ceq, ceq, FWD) +DO_2OP_CMP0(clt, cgt, REV) +DO_2OP_CMP0(cle, cge, REV) + +#undef DO_2OP +#undef DO_2OP_CMP0 + +/* Floating-point trigonometric starting value. + * See the ARM ARM pseudocode function FPTrigSMul. + */ +static float16 float16_ftsmul(float16 op1, uint16_t op2, float_status *stat) +{ + float16 result = float16_mul(op1, op1, stat); + if (!float16_is_any_nan(result)) { + result = float16_set_sign(result, op2 & 1); + } + return result; +} + +static float32 float32_ftsmul(float32 op1, uint32_t op2, float_status *stat) +{ + float32 result = float32_mul(op1, op1, stat); + if (!float32_is_any_nan(result)) { + result = float32_set_sign(result, op2 & 1); + } + return result; +} + +static float64 float64_ftsmul(float64 op1, uint64_t op2, float_status *stat) +{ + float64 result = float64_mul(op1, op1, stat); + if (!float64_is_any_nan(result)) { + result = float64_set_sign(result, op2 & 1); + } + return result; +} + +static float16 float16_abd(float16 op1, float16 op2, float_status *stat) +{ + return float16_abs(float16_sub(op1, op2, stat)); +} + +static float32 float32_abd(float32 op1, float32 op2, float_status *stat) +{ + return float32_abs(float32_sub(op1, op2, stat)); +} + +static float64 float64_abd(float64 op1, float64 op2, float_status *stat) +{ + return float64_abs(float64_sub(op1, op2, stat)); +} + +/* + * Reciprocal step. These are the AArch32 version which uses a + * non-fused multiply-and-subtract. + */ +static float16 float16_recps_nf(float16 op1, float16 op2, float_status *stat) +{ + op1 = float16_squash_input_denormal(op1, stat); + op2 = float16_squash_input_denormal(op2, stat); + + if ((float16_is_infinity(op1) && float16_is_zero(op2)) || + (float16_is_infinity(op2) && float16_is_zero(op1))) { + return float16_two; + } + return float16_sub(float16_two, float16_mul(op1, op2, stat), stat); +} + +static float32 float32_recps_nf(float32 op1, float32 op2, float_status *stat) +{ + op1 = float32_squash_input_denormal(op1, stat); + op2 = float32_squash_input_denormal(op2, stat); + + if ((float32_is_infinity(op1) && float32_is_zero(op2)) || + (float32_is_infinity(op2) && float32_is_zero(op1))) { + return float32_two; + } + return float32_sub(float32_two, float32_mul(op1, op2, stat), stat); +} + +/* Reciprocal square-root step. AArch32 non-fused semantics. */ +static float16 float16_rsqrts_nf(float16 op1, float16 op2, float_status *stat) +{ + op1 = float16_squash_input_denormal(op1, stat); + op2 = float16_squash_input_denormal(op2, stat); + + if ((float16_is_infinity(op1) && float16_is_zero(op2)) || + (float16_is_infinity(op2) && float16_is_zero(op1))) { + return float16_one_point_five; + } + op1 = float16_sub(float16_three, float16_mul(op1, op2, stat), stat); + return float16_div(op1, float16_two, stat); +} + +static float32 float32_rsqrts_nf(float32 op1, float32 op2, float_status *stat) +{ + op1 = float32_squash_input_denormal(op1, stat); + op2 = float32_squash_input_denormal(op2, stat); + + if ((float32_is_infinity(op1) && float32_is_zero(op2)) || + (float32_is_infinity(op2) && float32_is_zero(op1))) { + return float32_one_point_five; + } + op1 = float32_sub(float32_three, float32_mul(op1, op2, stat), stat); + return float32_div(op1, float32_two, stat); +} + +#define DO_3OP(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], m[i], stat); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_3OP(gvec_fadd_h, float16_add, float16) +DO_3OP(gvec_fadd_s, float32_add, float32) +DO_3OP(gvec_fadd_d, float64_add, float64) + +DO_3OP(gvec_fsub_h, float16_sub, float16) +DO_3OP(gvec_fsub_s, float32_sub, float32) +DO_3OP(gvec_fsub_d, float64_sub, float64) + +DO_3OP(gvec_fmul_h, float16_mul, float16) +DO_3OP(gvec_fmul_s, float32_mul, float32) +DO_3OP(gvec_fmul_d, float64_mul, float64) + +DO_3OP(gvec_ftsmul_h, float16_ftsmul, float16) +DO_3OP(gvec_ftsmul_s, float32_ftsmul, float32) +DO_3OP(gvec_ftsmul_d, float64_ftsmul, float64) + +DO_3OP(gvec_fabd_h, float16_abd, float16) +DO_3OP(gvec_fabd_s, float32_abd, float32) +DO_3OP(gvec_fabd_d, float64_abd, float64) + +DO_3OP(gvec_fceq_h, float16_ceq, float16) +DO_3OP(gvec_fceq_s, float32_ceq, float32) +DO_3OP(gvec_fceq_d, float64_ceq, float64) + +DO_3OP(gvec_fcge_h, float16_cge, float16) +DO_3OP(gvec_fcge_s, float32_cge, float32) +DO_3OP(gvec_fcge_d, float64_cge, float64) + +DO_3OP(gvec_fcgt_h, float16_cgt, float16) +DO_3OP(gvec_fcgt_s, float32_cgt, float32) +DO_3OP(gvec_fcgt_d, float64_cgt, float64) + +DO_3OP(gvec_facge_h, float16_acge, float16) +DO_3OP(gvec_facge_s, float32_acge, float32) +DO_3OP(gvec_facge_d, float64_acge, float64) + +DO_3OP(gvec_facgt_h, float16_acgt, float16) +DO_3OP(gvec_facgt_s, float32_acgt, float32) +DO_3OP(gvec_facgt_d, float64_acgt, float64) + +DO_3OP(gvec_fmax_h, float16_max, float16) +DO_3OP(gvec_fmax_s, float32_max, float32) +DO_3OP(gvec_fmax_d, float64_max, float64) + +DO_3OP(gvec_fmin_h, float16_min, float16) +DO_3OP(gvec_fmin_s, float32_min, float32) +DO_3OP(gvec_fmin_d, float64_min, float64) + +DO_3OP(gvec_fmaxnum_h, float16_maxnum, float16) +DO_3OP(gvec_fmaxnum_s, float32_maxnum, float32) +DO_3OP(gvec_fmaxnum_d, float64_maxnum, float64) + +DO_3OP(gvec_fminnum_h, float16_minnum, float16) +DO_3OP(gvec_fminnum_s, float32_minnum, float32) +DO_3OP(gvec_fminnum_d, float64_minnum, float64) + +DO_3OP(gvec_recps_nf_h, float16_recps_nf, float16) +DO_3OP(gvec_recps_nf_s, float32_recps_nf, float32) + +DO_3OP(gvec_rsqrts_nf_h, float16_rsqrts_nf, float16) +DO_3OP(gvec_rsqrts_nf_s, float32_rsqrts_nf, float32) + +#ifdef TARGET_AARCH64 +DO_3OP(gvec_fdiv_h, float16_div, float16) +DO_3OP(gvec_fdiv_s, float32_div, float32) +DO_3OP(gvec_fdiv_d, float64_div, float64) + +DO_3OP(gvec_fmulx_h, helper_advsimd_mulxh, float16) +DO_3OP(gvec_fmulx_s, helper_vfp_mulxs, float32) +DO_3OP(gvec_fmulx_d, helper_vfp_mulxd, float64) + +DO_3OP(gvec_recps_h, helper_recpsf_f16, float16) +DO_3OP(gvec_recps_s, helper_recpsf_f32, float32) +DO_3OP(gvec_recps_d, helper_recpsf_f64, float64) + +DO_3OP(gvec_rsqrts_h, helper_rsqrtsf_f16, float16) +DO_3OP(gvec_rsqrts_s, helper_rsqrtsf_f32, float32) +DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) + +#endif +#undef DO_3OP + +/* Non-fused multiply-add (unlike float16_muladd etc, which are fused) */ +static float16 float16_muladd_nf(float16 dest, float16 op1, float16 op2, + float_status *stat) +{ + return float16_add(dest, float16_mul(op1, op2, stat), stat); +} + +static float32 float32_muladd_nf(float32 dest, float32 op1, float32 op2, + float_status *stat) +{ + return float32_add(dest, float32_mul(op1, op2, stat), stat); +} + +static float16 float16_mulsub_nf(float16 dest, float16 op1, float16 op2, + float_status *stat) +{ + return float16_sub(dest, float16_mul(op1, op2, stat), stat); +} + +static float32 float32_mulsub_nf(float32 dest, float32 op1, float32 op2, + float_status *stat) +{ + return float32_sub(dest, float32_mul(op1, op2, stat), stat); +} + +/* Fused versions; these have the semantics Neon VFMA/VFMS want */ +static float16 float16_muladd_f(float16 dest, float16 op1, float16 op2, + float_status *stat) +{ + return float16_muladd(op1, op2, dest, 0, stat); +} + +static float32 float32_muladd_f(float32 dest, float32 op1, float32 op2, + float_status *stat) +{ + return float32_muladd(op1, op2, dest, 0, stat); +} + +static float64 float64_muladd_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(op1, op2, dest, 0, stat); +} + +static float16 float16_mulsub_f(float16 dest, float16 op1, float16 op2, + float_status *stat) +{ + return float16_muladd(float16_chs(op1), op2, dest, 0, stat); +} + +static float32 float32_mulsub_f(float32 dest, float32 op1, float32 op2, + float_status *stat) +{ + return float32_muladd(float32_chs(op1), op2, dest, 0, stat); +} + +static float64 float64_mulsub_f(float64 dest, float64 op1, float64 op2, + float_status *stat) +{ + return float64_muladd(float64_chs(op1), op2, dest, 0, stat); +} + +#define DO_MULADD(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(d[i], n[i], m[i], stat); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_MULADD(gvec_fmla_h, float16_muladd_nf, float16) +DO_MULADD(gvec_fmla_s, float32_muladd_nf, float32) + +DO_MULADD(gvec_fmls_h, float16_mulsub_nf, float16) +DO_MULADD(gvec_fmls_s, float32_mulsub_nf, float32) + +DO_MULADD(gvec_vfma_h, float16_muladd_f, float16) +DO_MULADD(gvec_vfma_s, float32_muladd_f, float32) +DO_MULADD(gvec_vfma_d, float64_muladd_f, float64) + +DO_MULADD(gvec_vfms_h, float16_mulsub_f, float16) +DO_MULADD(gvec_vfms_s, float32_mulsub_f, float32) +DO_MULADD(gvec_vfms_d, float64_mulsub_f, float64) + +/* For the indexed ops, SVE applies the index per 128-bit vector segment. + * For AdvSIMD, there is of course only one such vector segment. + */ + +#define DO_MUL_IDX(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc); \ + intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ + intptr_t idx = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = n[i + j] * mm; \ + } \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_MUL_IDX(gvec_mul_idx_h, uint16_t, H2) +DO_MUL_IDX(gvec_mul_idx_s, uint32_t, H4) +DO_MUL_IDX(gvec_mul_idx_d, uint64_t, H8) + +#undef DO_MUL_IDX + +#define DO_MLA_IDX(NAME, TYPE, OP, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc); \ + intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ + intptr_t idx = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm, *a = va; \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = a[i + j] OP n[i + j] * mm; \ + } \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_MLA_IDX(gvec_mla_idx_h, uint16_t, +, H2) +DO_MLA_IDX(gvec_mla_idx_s, uint32_t, +, H4) +DO_MLA_IDX(gvec_mla_idx_d, uint64_t, +, H8) + +DO_MLA_IDX(gvec_mls_idx_h, uint16_t, -, H2) +DO_MLA_IDX(gvec_mls_idx_s, uint32_t, -, H4) +DO_MLA_IDX(gvec_mls_idx_d, uint64_t, -, H8) + +#undef DO_MLA_IDX + +#define DO_FMUL_IDX(NAME, ADD, MUL, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc); \ + intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ + intptr_t idx = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = ADD(d[i + j], MUL(n[i + j], mm, stat), stat); \ + } \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +#define nop(N, M, S) (M) + +DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16_mul, float16, H2) +DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32_mul, float32, H4) +DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64_mul, float64, H8) + +#ifdef TARGET_AARCH64 + +DO_FMUL_IDX(gvec_fmulx_idx_h, nop, helper_advsimd_mulxh, float16, H2) +DO_FMUL_IDX(gvec_fmulx_idx_s, nop, helper_vfp_mulxs, float32, H4) +DO_FMUL_IDX(gvec_fmulx_idx_d, nop, helper_vfp_mulxd, float64, H8) + +#endif + +#undef nop + +/* + * Non-fused multiply-accumulate operations, for Neon. NB that unlike + * the fused ops below they assume accumulate both from and into Vd. + */ +DO_FMUL_IDX(gvec_fmla_nf_idx_h, float16_add, float16_mul, float16, H2) +DO_FMUL_IDX(gvec_fmla_nf_idx_s, float32_add, float32_mul, float32, H4) +DO_FMUL_IDX(gvec_fmls_nf_idx_h, float16_sub, float16_mul, float16, H2) +DO_FMUL_IDX(gvec_fmls_nf_idx_s, float32_sub, float32_mul, float32, H4) + +#undef DO_FMUL_IDX + +#define DO_FMLA_IDX(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ + void *stat, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc); \ + intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ + TYPE op1_neg = extract32(desc, SIMD_DATA_SHIFT, 1); \ + intptr_t idx = desc >> (SIMD_DATA_SHIFT + 1); \ + TYPE *d = vd, *n = vn, *m = vm, *a = va; \ + op1_neg <<= (8 * sizeof(TYPE) - 1); \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = TYPE##_muladd(n[i + j] ^ op1_neg, \ + mm, a[i + j], 0, stat); \ + } \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2) +DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4) +DO_FMLA_IDX(gvec_fmla_idx_d, float64, H8) + +#undef DO_FMLA_IDX + +#define DO_SAT(NAME, WTYPE, TYPEN, TYPEM, OP, MIN, MAX) \ +void HELPER(NAME)(void *vd, void *vq, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPEN *d = vd, *n = vn; TYPEM *m = vm; \ + bool q = false; \ + for (i = 0; i < oprsz / sizeof(TYPEN); i++) { \ + WTYPE dd = (WTYPE)n[i] OP m[i]; \ + if (dd < MIN) { \ + dd = MIN; \ + q = true; \ + } else if (dd > MAX) { \ + dd = MAX; \ + q = true; \ + } \ + d[i] = dd; \ + } \ + if (q) { \ + uint32_t *qc = vq; \ + qc[0] = 1; \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_SAT(gvec_uqadd_b, int, uint8_t, uint8_t, +, 0, UINT8_MAX) +DO_SAT(gvec_uqadd_h, int, uint16_t, uint16_t, +, 0, UINT16_MAX) +DO_SAT(gvec_uqadd_s, int64_t, uint32_t, uint32_t, +, 0, UINT32_MAX) + +DO_SAT(gvec_sqadd_b, int, int8_t, int8_t, +, INT8_MIN, INT8_MAX) +DO_SAT(gvec_sqadd_h, int, int16_t, int16_t, +, INT16_MIN, INT16_MAX) +DO_SAT(gvec_sqadd_s, int64_t, int32_t, int32_t, +, INT32_MIN, INT32_MAX) + +DO_SAT(gvec_uqsub_b, int, uint8_t, uint8_t, -, 0, UINT8_MAX) +DO_SAT(gvec_uqsub_h, int, uint16_t, uint16_t, -, 0, UINT16_MAX) +DO_SAT(gvec_uqsub_s, int64_t, uint32_t, uint32_t, -, 0, UINT32_MAX) + +DO_SAT(gvec_sqsub_b, int, int8_t, int8_t, -, INT8_MIN, INT8_MAX) +DO_SAT(gvec_sqsub_h, int, int16_t, int16_t, -, INT16_MIN, INT16_MAX) +DO_SAT(gvec_sqsub_s, int64_t, int32_t, int32_t, -, INT32_MIN, INT32_MAX) + +DO_SAT(gvec_usqadd_b, int, uint8_t, int8_t, +, 0, UINT8_MAX) +DO_SAT(gvec_usqadd_h, int, uint16_t, int16_t, +, 0, UINT16_MAX) +DO_SAT(gvec_usqadd_s, int64_t, uint32_t, int32_t, +, 0, UINT32_MAX) + +DO_SAT(gvec_suqadd_b, int, int8_t, uint8_t, +, INT8_MIN, INT8_MAX) +DO_SAT(gvec_suqadd_h, int, int16_t, uint16_t, +, INT16_MIN, INT16_MAX) +DO_SAT(gvec_suqadd_s, int64_t, int32_t, uint32_t, +, INT32_MIN, INT32_MAX) + +#undef DO_SAT + +void HELPER(gvec_uqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + uint64_t nn = n[i], mm = m[i], dd = nn + mm; + if (dd < nn) { + dd = UINT64_MAX; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_uqsub_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + uint64_t nn = n[i], mm = m[i], dd = nn - mm; + if (nn < mm) { + dd = 0; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_sqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + int64_t nn = n[i], mm = m[i], dd = nn + mm; + if (((dd ^ nn) & ~(nn ^ mm)) & INT64_MIN) { + dd = (nn >> 63) ^ ~INT64_MIN; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_sqsub_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + int64_t nn = n[i], mm = m[i], dd = nn - mm; + if (((dd ^ nn) & (nn ^ mm)) & INT64_MIN) { + dd = (nn >> 63) ^ ~INT64_MIN; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_usqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + uint64_t nn = n[i]; + int64_t mm = m[i]; + uint64_t dd = nn + mm; + + if (mm < 0) { + if (nn < (uint64_t)-mm) { + dd = 0; + q = true; + } + } else { + if (dd < nn) { + dd = UINT64_MAX; + q = true; + } + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_suqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + int64_t nn = n[i]; + uint64_t mm = m[i]; + int64_t dd = nn + mm; + + if (mm > (uint64_t)(INT64_MAX - nn)) { + dd = INT64_MAX; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +#define DO_SRA(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + int shift = simd_data(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] += n[i] >> shift; \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_SRA(gvec_ssra_b, int8_t) +DO_SRA(gvec_ssra_h, int16_t) +DO_SRA(gvec_ssra_s, int32_t) +DO_SRA(gvec_ssra_d, int64_t) + +DO_SRA(gvec_usra_b, uint8_t) +DO_SRA(gvec_usra_h, uint16_t) +DO_SRA(gvec_usra_s, uint32_t) +DO_SRA(gvec_usra_d, uint64_t) + +#undef DO_SRA + +#define DO_RSHR(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + int shift = simd_data(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + TYPE tmp = n[i] >> (shift - 1); \ + d[i] = (tmp >> 1) + (tmp & 1); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_RSHR(gvec_srshr_b, int8_t) +DO_RSHR(gvec_srshr_h, int16_t) +DO_RSHR(gvec_srshr_s, int32_t) +DO_RSHR(gvec_srshr_d, int64_t) + +DO_RSHR(gvec_urshr_b, uint8_t) +DO_RSHR(gvec_urshr_h, uint16_t) +DO_RSHR(gvec_urshr_s, uint32_t) +DO_RSHR(gvec_urshr_d, uint64_t) + +#undef DO_RSHR + +#define DO_RSRA(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + int shift = simd_data(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + TYPE tmp = n[i] >> (shift - 1); \ + d[i] += (tmp >> 1) + (tmp & 1); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_RSRA(gvec_srsra_b, int8_t) +DO_RSRA(gvec_srsra_h, int16_t) +DO_RSRA(gvec_srsra_s, int32_t) +DO_RSRA(gvec_srsra_d, int64_t) + +DO_RSRA(gvec_ursra_b, uint8_t) +DO_RSRA(gvec_ursra_h, uint16_t) +DO_RSRA(gvec_ursra_s, uint32_t) +DO_RSRA(gvec_ursra_d, uint64_t) + +#undef DO_RSRA + +#define DO_SRI(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + int shift = simd_data(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = deposit64(d[i], 0, sizeof(TYPE) * 8 - shift, n[i] >> shift); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_SRI(gvec_sri_b, uint8_t) +DO_SRI(gvec_sri_h, uint16_t) +DO_SRI(gvec_sri_s, uint32_t) +DO_SRI(gvec_sri_d, uint64_t) + +#undef DO_SRI + +#define DO_SLI(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + int shift = simd_data(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = deposit64(d[i], shift, sizeof(TYPE) * 8 - shift, n[i]); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_SLI(gvec_sli_b, uint8_t) +DO_SLI(gvec_sli_h, uint16_t) +DO_SLI(gvec_sli_s, uint32_t) +DO_SLI(gvec_sli_d, uint64_t) + +#undef DO_SLI + +/* + * Convert float16 to float32, raising no exceptions and + * preserving exceptional values, including SNaN. + * This is effectively an unpack+repack operation. + */ +static float32 float16_to_float32_by_bits(uint32_t f16, bool fz16) +{ + const int f16_bias = 15; + const int f32_bias = 127; + uint32_t sign = extract32(f16, 15, 1); + uint32_t exp = extract32(f16, 10, 5); + uint32_t frac = extract32(f16, 0, 10); + + if (exp == 0x1f) { + /* Inf or NaN */ + exp = 0xff; + } else if (exp == 0) { + /* Zero or denormal. */ + if (frac != 0) { + if (fz16) { + frac = 0; + } else { + /* + * Denormal; these are all normal float32. + * Shift the fraction so that the msb is at bit 11, + * then remove bit 11 as the implicit bit of the + * normalized float32. Note that we still go through + * the shift for normal numbers below, to put the + * float32 fraction at the right place. + */ + int shift = clz32(frac) - 21; + frac = (frac << shift) & 0x3ff; + exp = f32_bias - f16_bias - shift + 1; + } + } + } else { + /* Normal number; adjust the bias. */ + exp += f32_bias - f16_bias; + } + sign <<= 31; + exp <<= 23; + frac <<= 23 - 10; + + return sign | exp | frac; +} + +static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2) +{ + /* + * Branchless load of u32[0], u64[0], u32[1], or u64[1]. + * Load the 2nd qword iff is_q & is_2. + * Shift to the 2nd dword iff !is_q & is_2. + * For !is_q & !is_2, the upper bits of the result are garbage. + */ + return ptr[is_q & is_2] >> ((is_2 & ~is_q) << 5); +} + +/* + * Note that FMLAL requires oprsz == 8 or oprsz == 16, + * as there is not yet SVE versions that might use blocking. + */ + +static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, + uint32_t desc, bool fz16) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + int is_q = oprsz == 16; + uint64_t n_4, m_4; + + /* Pre-load all of the f16 data, avoiding overlap issues. */ + n_4 = load4_f16(vn, is_q, is_2); + m_4 = load4_f16(vm, is_q, is_2); + + /* Negate all inputs for FMLSL at once. */ + if (is_s) { + n_4 ^= 0x8000800080008000ull; + } + + for (i = 0; i < oprsz / 4; i++) { + float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); + float32 m_1 = float16_to_float32_by_bits(m_4 >> (i * 16), fz16); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal(vd, vn, vm, &env->vfp.fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, + void *venv, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); + CPUARMState *env = venv; + float_status *status = &env->vfp.fp_status; + bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); + + for (i = 0; i < oprsz; i += sizeof(float32)) { + float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negn; + float16 mm_16 = *(float16 *)(vm + H1_2(i + sel)); + float32 nn = float16_to_float32_by_bits(nn_16, fz16); + float32 mm = float16_to_float32_by_bits(mm_16, fz16); + float32 aa = *(float32 *)(va + H1_4(i)); + + *(float32 *)(vd + H1_4(i)) = float32_muladd(nn, mm, aa, 0, status); + } +} + +static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, + uint32_t desc, bool fz16) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + int index = extract32(desc, SIMD_DATA_SHIFT + 2, 3); + int is_q = oprsz == 16; + uint64_t n_4; + float32 m_1; + + /* Pre-load all of the f16 data, avoiding overlap issues. */ + n_4 = load4_f16(vn, is_q, is_2); + + /* Negate all inputs for FMLSL at once. */ + if (is_s) { + n_4 ^= 0x8000800080008000ull; + } + + m_1 = float16_to_float32_by_bits(((float16 *)vm)[H2(index)], fz16); + + for (i = 0; i < oprsz / 4; i++) { + float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, + void *venv, uint32_t desc) +{ + intptr_t i, j, oprsz = simd_oprsz(desc); + uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); + intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); + CPUARMState *env = venv; + float_status *status = &env->vfp.fp_status; + bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); + + for (i = 0; i < oprsz; i += 16) { + float16 mm_16 = *(float16 *)(vm + i + idx); + float32 mm = float16_to_float32_by_bits(mm_16, fz16); + + for (j = 0; j < 16; j += sizeof(float32)) { + float16 nn_16 = *(float16 *)(vn + H1_2(i + j + sel)) ^ negn; + float32 nn = float16_to_float32_by_bits(nn_16, fz16); + float32 aa = *(float32 *)(va + H1_4(i + j)); + + *(float32 *)(vd + H1_4(i + j)) = + float32_muladd(nn, mm, aa, 0, status); + } + } +} + +void HELPER(gvec_sshl_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + int8_t mm = m[i]; + int8_t nn = n[i]; + int8_t res = 0; + if (mm >= 0) { + if (mm < 8) { + res = nn << mm; + } + } else { + res = nn >> (mm > -8 ? -mm : 7); + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sshl_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + int8_t mm = m[i]; /* only 8 bits of shift are significant */ + int16_t nn = n[i]; + int16_t res = 0; + if (mm >= 0) { + if (mm < 16) { + res = nn << mm; + } + } else { + res = nn >> (mm > -16 ? -mm : 15); + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_ushl_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + int8_t mm = m[i]; + uint8_t nn = n[i]; + uint8_t res = 0; + if (mm >= 0) { + if (mm < 8) { + res = nn << mm; + } + } else { + if (mm > -8) { + res = nn >> -mm; + } + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_ushl_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + int8_t mm = m[i]; /* only 8 bits of shift are significant */ + uint16_t nn = n[i]; + uint16_t res = 0; + if (mm >= 0) { + if (mm < 16) { + res = nn << mm; + } + } else { + if (mm > -16) { + res = nn >> -mm; + } + } + d[i] = res; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* + * 8x8->8 polynomial multiply. + * + * Polynomial multiplication is like integer multiplication except the + * partial products are XORed, not added. + * + * TODO: expose this as a generic vector operation, as it is a common + * crypto building block. + */ +void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = clmul_8x8_low(n[i], m[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +/* + * 64x64->128 polynomial multiply. + * Because of the lanes are not accessed in strict columns, + * this probably cannot be turned into a generic helper. + */ +void HELPER(gvec_pmull_q)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, 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) { + 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)); +} + +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] = clmul_8x4_packed(nn, mm); + nn >>= 32; + mm >>= 32; + d[1] = clmul_8x4_packed(nn, mm); + + clear_tail(d, 16, simd_maxsz(desc)); +} + +#ifdef TARGET_AARCH64 +void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + int shift = simd_data(desc) * 8; + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = clmul_8x4_even(n[i] >> shift, m[i] >> shift); + } +} + +void HELPER(sve2_pmull_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t sel = H4(simd_data(desc)); + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *n = vn, *m = vm; + uint64_t *d = vd; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] = clmul_32(n[2 * i + sel], m[2 * i + sel]); + } +} +#endif + +#define DO_CMP0(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; i += sizeof(TYPE)) { \ + TYPE nn = *(TYPE *)(vn + i); \ + *(TYPE *)(vd + i) = -(nn OP 0); \ + } \ + clear_tail(vd, opr_sz, simd_maxsz(desc)); \ +} + +DO_CMP0(gvec_ceq0_b, int8_t, ==) +DO_CMP0(gvec_clt0_b, int8_t, <) +DO_CMP0(gvec_cle0_b, int8_t, <=) +DO_CMP0(gvec_cgt0_b, int8_t, >) +DO_CMP0(gvec_cge0_b, int8_t, >=) + +DO_CMP0(gvec_ceq0_h, int16_t, ==) +DO_CMP0(gvec_clt0_h, int16_t, <) +DO_CMP0(gvec_cle0_h, int16_t, <=) +DO_CMP0(gvec_cgt0_h, int16_t, >) +DO_CMP0(gvec_cge0_h, int16_t, >=) + +#undef DO_CMP0 + +#define DO_ABD(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + \ + for (i = 0; i < opr_sz / sizeof(TYPE); ++i) { \ + d[i] = n[i] < m[i] ? m[i] - n[i] : n[i] - m[i]; \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_ABD(gvec_sabd_b, int8_t) +DO_ABD(gvec_sabd_h, int16_t) +DO_ABD(gvec_sabd_s, int32_t) +DO_ABD(gvec_sabd_d, int64_t) + +DO_ABD(gvec_uabd_b, uint8_t) +DO_ABD(gvec_uabd_h, uint16_t) +DO_ABD(gvec_uabd_s, uint32_t) +DO_ABD(gvec_uabd_d, uint64_t) + +#undef DO_ABD + +#define DO_ABA(NAME, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + \ + for (i = 0; i < opr_sz / sizeof(TYPE); ++i) { \ + d[i] += n[i] < m[i] ? m[i] - n[i] : n[i] - m[i]; \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_ABA(gvec_saba_b, int8_t) +DO_ABA(gvec_saba_h, int16_t) +DO_ABA(gvec_saba_s, int32_t) +DO_ABA(gvec_saba_d, int64_t) + +DO_ABA(gvec_uaba_b, uint8_t) +DO_ABA(gvec_uaba_h, uint16_t) +DO_ABA(gvec_uaba_s, uint32_t) +DO_ABA(gvec_uaba_d, uint64_t) + +#undef DO_ABA + +#define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t half = oprsz / sizeof(TYPE) / 2; \ + TYPE *d = vd, *n = vn, *m = vm; \ + if (unlikely(d == m)) { \ + m = memcpy(&scratch, m, oprsz); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)], stat); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)], stat); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_3OP_PAIR(gvec_faddp_h, float16_add, float16, H2) +DO_3OP_PAIR(gvec_faddp_s, float32_add, float32, H4) +DO_3OP_PAIR(gvec_faddp_d, float64_add, float64, ) + +DO_3OP_PAIR(gvec_fmaxp_h, float16_max, float16, H2) +DO_3OP_PAIR(gvec_fmaxp_s, float32_max, float32, H4) +DO_3OP_PAIR(gvec_fmaxp_d, float64_max, float64, ) + +DO_3OP_PAIR(gvec_fminp_h, float16_min, float16, H2) +DO_3OP_PAIR(gvec_fminp_s, float32_min, float32, H4) +DO_3OP_PAIR(gvec_fminp_d, float64_min, float64, ) + +DO_3OP_PAIR(gvec_fmaxnump_h, float16_maxnum, float16, H2) +DO_3OP_PAIR(gvec_fmaxnump_s, float32_maxnum, float32, H4) +DO_3OP_PAIR(gvec_fmaxnump_d, float64_maxnum, float64, ) + +DO_3OP_PAIR(gvec_fminnump_h, float16_minnum, float16, H2) +DO_3OP_PAIR(gvec_fminnump_s, float32_minnum, float32, H4) +DO_3OP_PAIR(gvec_fminnump_d, float64_minnum, float64, ) + +#undef DO_3OP_PAIR + +#define DO_3OP_PAIR(NAME, FUNC, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t half = oprsz / sizeof(TYPE) / 2; \ + TYPE *d = vd, *n = vn, *m = vm; \ + if (unlikely(d == m)) { \ + m = memcpy(&scratch, m, oprsz); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i)] = FUNC(n[H(i * 2)], n[H(i * 2 + 1)]); \ + } \ + for (intptr_t i = 0; i < half; ++i) { \ + d[H(i + half)] = FUNC(m[H(i * 2)], m[H(i * 2 + 1)]); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +#define ADD(A, B) (A + B) +DO_3OP_PAIR(gvec_addp_b, ADD, uint8_t, H1) +DO_3OP_PAIR(gvec_addp_h, ADD, uint16_t, H2) +DO_3OP_PAIR(gvec_addp_s, ADD, uint32_t, H4) +DO_3OP_PAIR(gvec_addp_d, ADD, uint64_t, ) +#undef ADD + +DO_3OP_PAIR(gvec_smaxp_b, MAX, int8_t, H1) +DO_3OP_PAIR(gvec_smaxp_h, MAX, int16_t, H2) +DO_3OP_PAIR(gvec_smaxp_s, MAX, int32_t, H4) + +DO_3OP_PAIR(gvec_umaxp_b, MAX, uint8_t, H1) +DO_3OP_PAIR(gvec_umaxp_h, MAX, uint16_t, H2) +DO_3OP_PAIR(gvec_umaxp_s, MAX, uint32_t, H4) + +DO_3OP_PAIR(gvec_sminp_b, MIN, int8_t, H1) +DO_3OP_PAIR(gvec_sminp_h, MIN, int16_t, H2) +DO_3OP_PAIR(gvec_sminp_s, MIN, int32_t, H4) + +DO_3OP_PAIR(gvec_uminp_b, MIN, uint8_t, H1) +DO_3OP_PAIR(gvec_uminp_h, MIN, uint16_t, H2) +DO_3OP_PAIR(gvec_uminp_s, MIN, uint32_t, H4) + +#undef DO_3OP_PAIR + +#define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ + void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ + { \ + intptr_t i, oprsz = simd_oprsz(desc); \ + int shift = simd_data(desc); \ + TYPE *d = vd, *n = vn; \ + float_status *fpst = stat; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], shift, fpst); \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ + } + +DO_VCVT_FIXED(gvec_vcvt_sf, helper_vfp_sltos, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_uf, helper_vfp_ultos, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_fs, helper_vfp_tosls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_fu, helper_vfp_touls_round_to_zero, uint32_t) +DO_VCVT_FIXED(gvec_vcvt_sh, helper_vfp_shtoh, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_uh, helper_vfp_uhtoh, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_hs, helper_vfp_toshh_round_to_zero, uint16_t) +DO_VCVT_FIXED(gvec_vcvt_hu, helper_vfp_touhh_round_to_zero, uint16_t) + +#undef DO_VCVT_FIXED + +#define DO_VCVT_RMODE(NAME, FUNC, TYPE) \ + void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ + { \ + float_status *fpst = stat; \ + intptr_t i, oprsz = simd_oprsz(desc); \ + uint32_t rmode = simd_data(desc); \ + uint32_t prev_rmode = get_float_rounding_mode(fpst); \ + TYPE *d = vd, *n = vn; \ + set_float_rounding_mode(rmode, fpst); \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], 0, fpst); \ + } \ + set_float_rounding_mode(prev_rmode, fpst); \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ + } + +DO_VCVT_RMODE(gvec_vcvt_rm_ss, helper_vfp_tosls, uint32_t) +DO_VCVT_RMODE(gvec_vcvt_rm_us, helper_vfp_touls, uint32_t) +DO_VCVT_RMODE(gvec_vcvt_rm_sh, helper_vfp_toshh, uint16_t) +DO_VCVT_RMODE(gvec_vcvt_rm_uh, helper_vfp_touhh, uint16_t) + +#undef DO_VCVT_RMODE + +#define DO_VRINT_RMODE(NAME, FUNC, TYPE) \ + void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ + { \ + float_status *fpst = stat; \ + intptr_t i, oprsz = simd_oprsz(desc); \ + uint32_t rmode = simd_data(desc); \ + uint32_t prev_rmode = get_float_rounding_mode(fpst); \ + TYPE *d = vd, *n = vn; \ + set_float_rounding_mode(rmode, fpst); \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], fpst); \ + } \ + set_float_rounding_mode(prev_rmode, fpst); \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ + } + +DO_VRINT_RMODE(gvec_vrint_rm_h, helper_rinth, uint16_t) +DO_VRINT_RMODE(gvec_vrint_rm_s, helper_rints, uint32_t) + +#undef DO_VRINT_RMODE + +#ifdef TARGET_AARCH64 +void HELPER(simd_tblx)(void *vd, void *vm, void *venv, uint32_t desc) +{ + const uint8_t *indices = vm; + CPUARMState *env = venv; + size_t oprsz = simd_oprsz(desc); + uint32_t rn = extract32(desc, SIMD_DATA_SHIFT, 5); + bool is_tbx = extract32(desc, SIMD_DATA_SHIFT + 5, 1); + uint32_t table_len = desc >> (SIMD_DATA_SHIFT + 6); + union { + uint8_t b[16]; + uint64_t d[2]; + } result; + + /* + * We must construct the final result in a temp, lest the output + * overlaps the input table. For TBL, begin with zero; for TBX, + * begin with the original register contents. Note that we always + * copy 16 bytes here to avoid an extra branch; clearing the high + * bits of the register for oprsz == 8 is handled below. + */ + if (is_tbx) { + memcpy(&result, vd, 16); + } else { + memset(&result, 0, 16); + } + + for (size_t i = 0; i < oprsz; ++i) { + uint32_t index = indices[H1(i)]; + + if (index < table_len) { + /* + * Convert index (a byte offset into the virtual table + * which is a series of 128-bit vectors concatenated) + * into the correct register element, bearing in mind + * that the table can wrap around from V31 to V0. + */ + const uint8_t *table = (const uint8_t *) + aa64_vfp_qreg(env, (rn + (index >> 4)) % 32); + result.b[H1(i)] = table[H1(index % 16)]; + } + } + + memcpy(vd, &result, 16); + clear_tail(vd, oprsz, simd_maxsz(desc)); +} +#endif + +/* + * NxN -> N highpart multiply + * + * TODO: expose this as a generic vector operation. + */ + +void HELPER(gvec_smulh_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + d[i] = ((int32_t)n[i] * m[i]) >> 8; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_smulh_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = ((int32_t)n[i] * m[i]) >> 16; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_smulh_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + int32_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = ((int64_t)n[i] * m[i]) >> 32; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_smulh_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t discard; + + for (i = 0; i < opr_sz / 8; ++i) { + muls64(&discard, &d[i], n[i], m[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_umulh_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + d[i] = ((uint32_t)n[i] * m[i]) >> 8; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_umulh_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint16_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 2; ++i) { + d[i] = ((uint32_t)n[i] * m[i]) >> 16; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_umulh_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = ((uint64_t)n[i] * m[i]) >> 32; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_umulh_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t discard; + + for (i = 0; i < opr_sz / 8; ++i) { + mulu64(&discard, &d[i], n[i], m[i]); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_xar_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + int shr = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + + for (i = 0; i < opr_sz; ++i) { + d[i] = ror64(n[i] ^ m[i], shr); + } + clear_tail(d, opr_sz * 8, simd_maxsz(desc)); +} + +/* + * Integer matrix-multiply accumulate + */ + +static uint32_t do_smmla_b(uint32_t sum, void *vn, void *vm) +{ + int8_t *n = vn, *m = vm; + + for (intptr_t k = 0; k < 8; ++k) { + sum += n[H1(k)] * m[H1(k)]; + } + return sum; +} + +static uint32_t do_ummla_b(uint32_t sum, void *vn, void *vm) +{ + uint8_t *n = vn, *m = vm; + + for (intptr_t k = 0; k < 8; ++k) { + sum += n[H1(k)] * m[H1(k)]; + } + return sum; +} + +static uint32_t do_usmmla_b(uint32_t sum, void *vn, void *vm) +{ + uint8_t *n = vn; + int8_t *m = vm; + + for (intptr_t k = 0; k < 8; ++k) { + sum += n[H1(k)] * m[H1(k)]; + } + return sum; +} + +static void do_mmla_b(void *vd, void *vn, void *vm, void *va, uint32_t desc, + uint32_t (*inner_loop)(uint32_t, void *, void *)) +{ + intptr_t seg, opr_sz = simd_oprsz(desc); + + for (seg = 0; seg < opr_sz; seg += 16) { + uint32_t *d = vd + seg; + uint32_t *a = va + seg; + uint32_t sum0, sum1, sum2, sum3; + + /* + * Process the entire segment at once, writing back the + * results only after we've consumed all of the inputs. + * + * Key to indices by column: + * i j i j + */ + sum0 = a[H4(0 + 0)]; + sum0 = inner_loop(sum0, vn + seg + 0, vm + seg + 0); + sum1 = a[H4(0 + 1)]; + sum1 = inner_loop(sum1, vn + seg + 0, vm + seg + 8); + sum2 = a[H4(2 + 0)]; + sum2 = inner_loop(sum2, vn + seg + 8, vm + seg + 0); + sum3 = a[H4(2 + 1)]; + sum3 = inner_loop(sum3, vn + seg + 8, vm + seg + 8); + + d[H4(0)] = sum0; + d[H4(1)] = sum1; + d[H4(2)] = sum2; + d[H4(3)] = sum3; + } + clear_tail(vd, opr_sz, simd_maxsz(desc)); +} + +#define DO_MMLA_B(NAME, INNER) \ + void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ + { do_mmla_b(vd, vn, vm, va, desc, INNER); } + +DO_MMLA_B(gvec_smmla_b, do_smmla_b) +DO_MMLA_B(gvec_ummla_b, do_ummla_b) +DO_MMLA_B(gvec_usmmla_b, do_usmmla_b) + +/* + * BFloat16 Dot Product + */ + +bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) +{ + /* + * For BFDOT, BFMMLA, etc, the behaviour depends on FPCR.EBF. + * For EBF = 0, we ignore the FPCR bits which determine rounding + * mode and denormal-flushing, and we do unfused multiplies and + * additions with intermediate rounding of all products and sums. + * For EBF = 1, we honour FPCR rounding mode and denormal-flushing bits, + * and we perform a fused two-way sum-of-products without intermediate + * rounding of the products. + * In either case, we don't set fp exception flags. + * + * EBF is AArch64 only, so even if it's set in the FPCR it has + * no effect on AArch32 instructions. + */ + bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; + *statusp = (float_status){ + .tininess_before_rounding = float_tininess_before_rounding, + .float_rounding_mode = float_round_to_odd_inf, + .flush_to_zero = true, + .flush_inputs_to_zero = true, + .default_nan_mode = true, + }; + + if (ebf) { + float_status *fpst = &env->vfp.fp_status; + set_flush_to_zero(get_flush_to_zero(fpst), statusp); + set_flush_inputs_to_zero(get_flush_inputs_to_zero(fpst), statusp); + set_float_rounding_mode(get_float_rounding_mode(fpst), statusp); + + /* EBF=1 needs to do a step with round-to-odd semantics */ + *oddstatusp = *statusp; + set_float_rounding_mode(float_round_to_odd, oddstatusp); + } + + return ebf; +} + +float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst) +{ + float32 t1, t2; + + /* + * Extract each BFloat16 from the element pair, and shift + * them such that they become float32. + */ + t1 = float32_mul(e1 << 16, e2 << 16, fpst); + t2 = float32_mul(e1 & 0xffff0000u, e2 & 0xffff0000u, fpst); + t1 = float32_add(t1, t2, fpst); + t1 = float32_add(sum, t1, fpst); + + return t1; +} + +float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, + float_status *fpst, float_status *fpst_odd) +{ + /* + * Compare f16_dotadd() in sme_helper.c, but here we have + * bfloat16 inputs. In particular that means that we do not + * want the FPCR.FZ16 flush semantics, so we use the normal + * float_status for the input handling here. + */ + float64 e1r = float32_to_float64(e1 << 16, fpst); + float64 e1c = float32_to_float64(e1 & 0xffff0000u, fpst); + float64 e2r = float32_to_float64(e2 << 16, fpst); + float64 e2c = float32_to_float64(e2 & 0xffff0000u, fpst); + 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, fpst_odd); + t64 = float64r32_muladd(e1c, e2c, t64, 0, fpst); + + /* This conversion is exact, because we've already rounded. */ + t32 = float64_to_float32(t64, fpst); + + /* The final accumulation step is not fused. */ + return float32_add(sum, t32, fpst); +} + +void HELPER(gvec_bfdot)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + float32 *d = vd, *a = va; + uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; + + if (is_ebf(env, &fpst, &fpst_odd)) { + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = bfdotadd_ebf(a[i], n[i], m[i], &fpst, &fpst_odd); + } + } else { + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = bfdotadd(a[i], n[i], m[i], &fpst); + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_bfdot_idx)(void *vd, void *vn, void *vm, + void *va, CPUARMState *env, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + intptr_t index = simd_data(desc); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + float32 *d = vd, *a = va; + uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; + + if (is_ebf(env, &fpst, &fpst_odd)) { + for (i = 0; i < elements; i += eltspersegment) { + uint32_t m_idx = m[i + H4(index)]; + + for (j = i; j < i + eltspersegment; j++) { + d[j] = bfdotadd_ebf(a[j], n[j], m_idx, &fpst, &fpst_odd); + } + } + } else { + for (i = 0; i < elements; i += eltspersegment) { + uint32_t m_idx = m[i + H4(index)]; + + for (j = i; j < i + eltspersegment; j++) { + d[j] = bfdotadd(a[j], n[j], m_idx, &fpst); + } + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) +{ + intptr_t s, opr_sz = simd_oprsz(desc); + float32 *d = vd, *a = va; + uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; + + if (is_ebf(env, &fpst, &fpst_odd)) { + for (s = 0; s < opr_sz / 4; s += 4) { + float32 sum00, sum01, sum10, sum11; + + /* + * Process the entire segment at once, writing back the + * results only after we've consumed all of the inputs. + * + * Key to indices by column: + * i j i k j k + */ + sum00 = a[s + H4(0 + 0)]; + sum00 = bfdotadd_ebf(sum00, n[s + H4(0 + 0)], m[s + H4(0 + 0)], &fpst, &fpst_odd); + sum00 = bfdotadd_ebf(sum00, n[s + H4(0 + 1)], m[s + H4(0 + 1)], &fpst, &fpst_odd); + + sum01 = a[s + H4(0 + 1)]; + sum01 = bfdotadd_ebf(sum01, n[s + H4(0 + 0)], m[s + H4(2 + 0)], &fpst, &fpst_odd); + sum01 = bfdotadd_ebf(sum01, n[s + H4(0 + 1)], m[s + H4(2 + 1)], &fpst, &fpst_odd); + + sum10 = a[s + H4(2 + 0)]; + sum10 = bfdotadd_ebf(sum10, n[s + H4(2 + 0)], m[s + H4(0 + 0)], &fpst, &fpst_odd); + sum10 = bfdotadd_ebf(sum10, n[s + H4(2 + 1)], m[s + H4(0 + 1)], &fpst, &fpst_odd); + + sum11 = a[s + H4(2 + 1)]; + sum11 = bfdotadd_ebf(sum11, n[s + H4(2 + 0)], m[s + H4(2 + 0)], &fpst, &fpst_odd); + sum11 = bfdotadd_ebf(sum11, n[s + H4(2 + 1)], m[s + H4(2 + 1)], &fpst, &fpst_odd); + + d[s + H4(0 + 0)] = sum00; + d[s + H4(0 + 1)] = sum01; + d[s + H4(2 + 0)] = sum10; + d[s + H4(2 + 1)] = sum11; + } + } else { + for (s = 0; s < opr_sz / 4; s += 4) { + float32 sum00, sum01, sum10, sum11; + + /* + * Process the entire segment at once, writing back the + * results only after we've consumed all of the inputs. + * + * Key to indices by column: + * i j i k j k + */ + sum00 = a[s + H4(0 + 0)]; + sum00 = bfdotadd(sum00, n[s + H4(0 + 0)], m[s + H4(0 + 0)], &fpst); + sum00 = bfdotadd(sum00, n[s + H4(0 + 1)], m[s + H4(0 + 1)], &fpst); + + sum01 = a[s + H4(0 + 1)]; + sum01 = bfdotadd(sum01, n[s + H4(0 + 0)], m[s + H4(2 + 0)], &fpst); + sum01 = bfdotadd(sum01, n[s + H4(0 + 1)], m[s + H4(2 + 1)], &fpst); + + sum10 = a[s + H4(2 + 0)]; + sum10 = bfdotadd(sum10, n[s + H4(2 + 0)], m[s + H4(0 + 0)], &fpst); + sum10 = bfdotadd(sum10, n[s + H4(2 + 1)], m[s + H4(0 + 1)], &fpst); + + sum11 = a[s + H4(2 + 1)]; + sum11 = bfdotadd(sum11, n[s + H4(2 + 0)], m[s + H4(2 + 0)], &fpst); + sum11 = bfdotadd(sum11, n[s + H4(2 + 1)], m[s + H4(2 + 1)], &fpst); + + d[s + H4(0 + 0)] = sum00; + d[s + H4(0 + 1)] = sum01; + d[s + H4(2 + 0)] = sum10; + d[s + H4(2 + 1)] = sum11; + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, + void *stat, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + intptr_t sel = simd_data(desc); + float32 *d = vd, *a = va; + bfloat16 *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + float32 nn = n[H2(i * 2 + sel)] << 16; + float32 mm = m[H2(i * 2 + sel)] << 16; + d[H4(i)] = float32_muladd(nn, mm, a[H4(i)], 0, stat); + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, + void *va, void *stat, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 1, 3); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + float32 *d = vd, *a = va; + bfloat16 *n = vn, *m = vm; + + for (i = 0; i < elements; i += eltspersegment) { + float32 m_idx = m[H2(2 * i + index)] << 16; + + for (j = i; j < i + eltspersegment; j++) { + float32 n_j = n[H2(2 * j + sel)] << 16; + d[H4(j)] = float32_muladd(n_j, m_idx, a[H4(j)], 0, stat); + } + } + 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 81% rename from target/arm/vec_internal.h rename to target/arm/tcg/vec_internal.h index 1f4ed80ff7..094f5c169c 100644 --- a/target/arm/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -219,28 +219,50 @@ 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 * @e1, @e2: multiplicand vectors + * @fpst: floating-point status to use * * BFloat16 2-way dot product of @e1 & @e2, accumulating with @sum. * The @e1 and @e2 operands correspond to the 32-bit source vector * slots and contain two Bfloat16 values each. * - * Corresponds to the ARM pseudocode function BFDotAdd. + * Corresponds to the ARM pseudocode function BFDotAdd, specialized + * for the FPCR.EBF == 0 case. */ -float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2); +float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst); +/** + * bfdotadd_ebf: + * @sum: addend + * @e1, @e2: multiplicand vectors + * @fpst: floating-point status to use + * @fpst_odd: floating-point status to use for round-to-odd operations + * + * BFloat16 2-way dot product of @e1 & @e2, accumulating with @sum. + * The @e1 and @e2 operands correspond to the 32-bit source vector + * slots and contain two Bfloat16 values each. + * + * Corresponds to the ARM pseudocode function BFDotAdd, specialized + * for the FPCR.EBF == 1 case. + */ +float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, + float_status *fpst, float_status *fpst_odd); + +/** + * is_ebf: + * @env: CPU state + * @statusp: pointer to floating point status to fill in + * @oddstatusp: pointer to floating point status to fill in for round-to-odd + * + * Determine whether a BFDotAdd operation should use FPCR.EBF = 0 + * or FPCR.EBF = 1 semantics. On return, has initialized *statusp + * and *oddstatusp to suitable float_status arguments to use with either + * bfdotadd() or bfdotadd_ebf(). + * Returns true for EBF = 1, false for EBF = 0. (The caller should use this + * to decide whether to call bfdotadd() or bfdotadd_ebf().) + */ +bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp); #endif /* TARGET_ARM_VEC_INTERNAL_H */ 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 96% rename from target/arm/vfp.decode rename to target/arm/tcg/vfp.decode index 5405e80197..2dd87a2708 100644 --- a/target/arm/vfp.decode +++ b/target/arm/tcg/vfp.decode @@ -141,18 +141,18 @@ VDIV_dp ---- 1110 1.00 .... .... 1011 .0.0 .... @vfp_dnm_d VFMA_hp ---- 1110 1.10 .... .... 1001 .0. 0 .... @vfp_dnm_s VFMS_hp ---- 1110 1.10 .... .... 1001 .1. 0 .... @vfp_dnm_s -VFNMA_hp ---- 1110 1.01 .... .... 1001 .0. 0 .... @vfp_dnm_s -VFNMS_hp ---- 1110 1.01 .... .... 1001 .1. 0 .... @vfp_dnm_s +VFNMS_hp ---- 1110 1.01 .... .... 1001 .0. 0 .... @vfp_dnm_s +VFNMA_hp ---- 1110 1.01 .... .... 1001 .1. 0 .... @vfp_dnm_s VFMA_sp ---- 1110 1.10 .... .... 1010 .0. 0 .... @vfp_dnm_s VFMS_sp ---- 1110 1.10 .... .... 1010 .1. 0 .... @vfp_dnm_s -VFNMA_sp ---- 1110 1.01 .... .... 1010 .0. 0 .... @vfp_dnm_s -VFNMS_sp ---- 1110 1.01 .... .... 1010 .1. 0 .... @vfp_dnm_s +VFNMS_sp ---- 1110 1.01 .... .... 1010 .0. 0 .... @vfp_dnm_s +VFNMA_sp ---- 1110 1.01 .... .... 1010 .1. 0 .... @vfp_dnm_s VFMA_dp ---- 1110 1.10 .... .... 1011 .0.0 .... @vfp_dnm_d VFMS_dp ---- 1110 1.10 .... .... 1011 .1.0 .... @vfp_dnm_d -VFNMA_dp ---- 1110 1.01 .... .... 1011 .0.0 .... @vfp_dnm_d -VFNMS_dp ---- 1110 1.01 .... .... 1011 .1.0 .... @vfp_dnm_d +VFNMS_dp ---- 1110 1.01 .... .... 1011 .0.0 .... @vfp_dnm_d +VFNMA_dp ---- 1110 1.01 .... .... 1011 .1.0 .... @vfp_dnm_d VMOV_imm_hp ---- 1110 1.11 .... .... 1001 0000 .... \ vd=%vd_sp imm=%vmov_imm diff --git a/target/arm/tlb_helper.c b/target/arm/tlb_helper.c deleted file mode 100644 index 1384fe6f98..0000000000 --- a/target/arm/tlb_helper.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * ARM TLB (Translation lookaside buffer) helpers. - * - * 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 "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) & 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 - * on whether the long or short descriptor format is in use. - */ -bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - mmu_idx = stage_1_mmu_idx(mmu_idx); - return regime_using_lpae_format(env, mmu_idx); -} - -static inline uint32_t merge_syn_data_abort(uint32_t template_syn, - unsigned int target_el, - bool same_el, bool ea, - bool s1ptw, 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. - * - * Furthermore, ISV is only set for certain kinds of load/stores. - * If the template syndrome does not have ISV set, we should leave - * it cleared. - * - * See ARMv8 specs, D7-1974: - * ISS encoding for an exception from a Data Abort, the - * ISV field. - */ - if (!(template_syn & ARM_EL_ISV) || target_el != 2 || s1ptw) { - syn = syn_data_abort_no_iss(same_el, 0, - ea, 0, s1ptw, is_write, fsc); - } else { - /* - * Fields: IL, ISV, SAS, SSE, SRT, SF and AR come from the template - * syndrome created at translation time. - * Now we create the runtime syndrome with the remaining fields. - */ - syn = syn_data_abort_with_iss(same_el, - 0, 0, 0, 0, 0, - ea, 0, s1ptw, is_write, fsc, - true); - /* Merge the runtime syndrome with the template syndrome. */ - syn |= template_syn; - } - return syn; -} - -static uint32_t compute_fsr_fsc(CPUARMState *env, ARMMMUFaultInfo *fi, - int target_el, int mmu_idx, uint32_t *ret_fsc) -{ - ARMMMUIdx arm_mmu_idx = core_to_arm_mmu_idx(env, mmu_idx); - uint32_t fsr, fsc; - - /* - * 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 - */ - fsr = arm_fi_to_lfsc(fi); - fsc = extract32(fsr, 0, 6); - } else { - fsr = arm_fi_to_sfsc(fi); - /* - * Short format FSR : this fault will never actually be reported - * to an EL that uses a syndrome register. Use a (currently) - * reserved FSR code in case the constructed syndrome does leak - * into the guest somehow. - */ - fsc = 0x3f; - } - - *ret_fsc = fsc; - return fsr; -} - -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; - bool same_el; - uint32_t syn, exc, fsr, fsc; - - target_el = exception_target_el(env); - if (fi->stage2) { - target_el = 2; - env->cp15.hpfar_el2 = extract64(fi->s2addr, 12, 47) << 4; - if (arm_is_secure_below_el3(env) && fi->s1ns) { - env->cp15.hpfar_el2 |= HPFAR_NS; - } - } - same_el = (arm_current_el(env) == 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, - fsc); - if (access_type == MMU_DATA_STORE - && arm_feature(env, ARM_FEATURE_V6)) { - fsr |= (1 << 11); - } - exc = EXCP_DATA_ABORT; - } - - env->exception.vaddress = addr; - env->exception.fsr = fsr; - raise_exception(env, exc, syn, target_el); -} - -/* Raise a data fault alignment exception for the specified virtual address */ -void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - ARMCPU *cpu = ARM_CPU(cs); - ARMMMUFaultInfo fi = {}; - - /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr); - - fi.type = ARMFault_Alignment; - arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi); -} - -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); - uint32_t fsc; - - env->exception.vaddress = pc; - - /* - * Note that the fsc is not applicable to this exception, - * since any syndrome is pcalignment not insn_abort. - */ - env->exception.fsr = compute_fsr_fsc(env, &fi, target_el, mmu_idx, &fsc); - raise_exception(env, EXCP_PREFETCH_ABORT, syn_pcalignment(), target_el); -} - -#if !defined(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 - */ -void arm_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) -{ - ARMCPU *cpu = ARM_CPU(cs); - ARMMMUFaultInfo fi = {}; - - /* now we have a real cpu fault */ - cpu_restore_state(cs, retaddr); - - fi.ea = arm_extabort_type(response); - fi.type = ARMFault_SyncExternal; - arm_deliver_fault(cpu, addr, access_type, mmu_idx, &fi); -} - -bool arm_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - ARMCPU *cpu = ARM_CPU(cs); - 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 - * to the TLB. On success, return true. Otherwise, if probing, - * return false. Otherwise populate fsr with ARM DFSR/IFSR fault - * register format, and signal the fault. - */ - ret = get_phys_addr(&cpu->env, address, access_type, - core_to_arm_mmu_idx(&cpu->env, mmu_idx), - &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 (res.f.lg_page_size >= TARGET_PAGE_BITS) { - res.f.phys_addr &= TARGET_PAGE_MASK; - address &= TARGET_PAGE_MASK; - } - - res.f.pte_attrs = res.cacheattrs.attrs; - res.f.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); - arm_deliver_fault(cpu, address, access_type, mmu_idx, fi); - } -} -#else -void arm_cpu_record_sigsegv(CPUState *cs, vaddr addr, - MMUAccessType access_type, - bool maperr, uintptr_t ra) -{ - ARMMMUFaultInfo fi = { - .type = maperr ? ARMFault_Translation : ARMFault_Permission, - .level = 3, - }; - ARMCPU *cpu = ARM_CPU(cs); - - /* - * We report both ESR and FAR to signal handlers. - * For now, it's easiest to deliver the fault normally. - */ - cpu_restore_state(cs, ra); - arm_deliver_fault(cpu, addr, access_type, MMU_USER_IDX, &fi); -} - -void arm_cpu_record_sigbus(CPUState *cs, vaddr addr, - MMUAccessType access_type, uintptr_t ra) -{ - arm_cpu_do_unaligned_access(cs, addr, access_type, MMU_USER_IDX, ra); -} -#endif /* !defined(CONFIG_USER_ONLY) */ 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/translate-a64.c b/target/arm/translate-a64.c deleted file mode 100644 index f0b8db7ce5..0000000000 --- a/target/arm/translate-a64.c +++ /dev/null @@ -1,15063 +0,0 @@ -/* - * AArch64 translation - * - * 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 "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" - -static TCGv_i64 cpu_X[32]; -static TCGv_i64 cpu_pc; - -/* Load/store exclusive handling */ -static TCGv_i64 cpu_exclusive_high; - -static const char *regnames[] = { - "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", - "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", - "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", - "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp" -}; - -enum a64_shift_type { - A64_SHIFT_TYPE_LSL = 0, - A64_SHIFT_TYPE_LSR = 1, - A64_SHIFT_TYPE_ASR = 2, - A64_SHIFT_TYPE_ROR = 3 -}; - -/* Table based decoder typedefs - used when the relevant bits for decode - * are too awkwardly scattered across the instruction (eg SIMD). - */ -typedef void AArch64DecodeFn(DisasContext *s, uint32_t insn); - -typedef struct AArch64DecodeTable { - uint32_t pattern; - uint32_t mask; - AArch64DecodeFn *disas_fn; -} AArch64DecodeTable; - -/* initialize TCG globals. */ -void a64_translate_init(void) -{ - int i; - - cpu_pc = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUARMState, pc), - "pc"); - for (i = 0; i < 32; i++) { - cpu_X[i] = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUARMState, xregs[i]), - regnames[i]); - } - - cpu_exclusive_high = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUARMState, exclusive_high), "exclusive_high"); -} - -/* - * Return the core mmu_idx to use for A64 "unprivileged load/store" insns - */ -static int get_a64_user_mem_index(DisasContext *s) -{ - /* - * If AccType_UNPRIV is not used, the insn uses AccType_NORMAL, - * which is the usual mmu_idx for this cpu state. - */ - ARMMMUIdx useridx = s->mmu_idx; - - if (s->unpriv) { - /* - * We have pre-computed the condition for AccType_UNPRIV. - * Therefore we should never get here with a mmu_idx for - * which we do not know the corresponding user mmu_idx. - */ - switch (useridx) { - case ARMMMUIdx_E10_1: - case ARMMMUIdx_E10_1_PAN: - useridx = ARMMMUIdx_E10_0; - break; - case ARMMMUIdx_E20_2: - case ARMMMUIdx_E20_2_PAN: - useridx = ARMMMUIdx_E20_0; - break; - default: - g_assert_not_reached(); - } - } - return arm_to_core_mmu_idx(useridx); -} - -static void set_btype_raw(int val) -{ - tcg_gen_st_i32(tcg_constant_i32(val), cpu_env, - offsetof(CPUARMState, btype)); -} - -static void set_btype(DisasContext *s, int val) -{ - /* BTYPE is a 2-bit field, and 0 should be done with reset_btype. */ - tcg_debug_assert(val >= 1 && val <= 3); - set_btype_raw(val); - s->btype = -1; -} - -static void reset_btype(DisasContext *s) -{ - if (s->btype != 0) { - set_btype_raw(0); - s->btype = 0; - } -} - -static void gen_pc_plus_diff(DisasContext *s, TCGv_i64 dest, target_long diff) -{ - assert(s->pc_save != -1); - if (TARGET_TB_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; -} - -/* - * Handle Top Byte Ignore (TBI) bits. - * - * If address tagging is enabled via the TCR TBI bits: - * + 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. - * If the appropriate TBI bit is set for the address then - * the address is sign-extended from bit 55 into bits [63:56] - * - * Here We have concatenated TBI{1,0} into tbi. - */ -static void gen_top_byte_ignore(DisasContext *s, TCGv_i64 dst, - TCGv_i64 src, int tbi) -{ - if (tbi == 0) { - /* Load unmodified address */ - tcg_gen_mov_i64(dst, src); - } else if (!regime_has_2_ranges(s->mmu_idx)) { - /* Force tag byte to all zero */ - tcg_gen_extract_i64(dst, src, 0, 56); - } else { - /* Sign-extend from bit 55. */ - tcg_gen_sextract_i64(dst, src, 0, 56); - - switch (tbi) { - case 1: - /* tbi0 but !tbi1: only use the extension if positive */ - tcg_gen_and_i64(dst, dst, src); - break; - case 2: - /* !tbi0 but tbi1: only use the extension if negative */ - tcg_gen_or_i64(dst, dst, src); - break; - case 3: - /* tbi0 and tbi1: always use the extension */ - break; - default: - g_assert_not_reached(); - } - } -} - -static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) -{ - /* - * If address tagging is enabled for instructions via the TCR TBI bits, - * 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; -} - -/* - * Handle MTE and/or TBI. - * - * For TBI, ideally, we would do nothing. Proper behaviour on fault is - * for the tag to be present in the FAR_ELx register. But for user-only - * mode we do not have a TLB with which to implement this, so we must - * remove the top byte now. - * - * Always return a fresh temporary that we can increment independently - * of the write-back address. - */ - -TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr) -{ - TCGv_i64 clean = new_tmp_a64(s); -#ifdef CONFIG_USER_ONLY - gen_top_byte_ignore(s, clean, addr, s->tbid); -#else - tcg_gen_mov_i64(clean, addr); -#endif - return clean; -} - -/* Insert a zero tag into src, with the result at dst. */ -static void gen_address_with_allocation_tag0(TCGv_i64 dst, TCGv_i64 src) -{ - tcg_gen_andi_i64(dst, src, ~MAKE_64BIT_MASK(56, 4)); -} - -static void gen_probe_access(DisasContext *s, TCGv_i64 ptr, - MMUAccessType acc, int log2_size) -{ - gen_helper_probe_access(cpu_env, ptr, - tcg_constant_i32(acc), - tcg_constant_i32(get_mem_index(s)), - tcg_constant_i32(1 << log2_size)); -} - -/* - * For MTE, check a single logical or atomic access. This probes a single - * address, the exact one specified. The size and alignment of the access - * is not relevant to MTE, per se, but watchpoints do require the size, - * and we want to recognize those before making any other changes to state. - */ -static TCGv_i64 gen_mte_check1_mmuidx(DisasContext *s, TCGv_i64 addr, - bool is_write, bool tag_checked, - int log2_size, bool is_unpriv, - int core_idx) -{ - if (tag_checked && s->mte_active[is_unpriv]) { - TCGv_i64 ret; - int desc = 0; - - desc = FIELD_DP32(desc, MTEDESC, MIDX, core_idx); - 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); - - ret = new_tmp_a64(s); - gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); - - return ret; - } - return clean_data_tbi(s, addr); -} - -TCGv_i64 gen_mte_check1(DisasContext *s, TCGv_i64 addr, bool is_write, - bool tag_checked, int log2_size) -{ - return gen_mte_check1_mmuidx(s, addr, is_write, tag_checked, log2_size, - false, get_mem_index(s)); -} - -/* - * 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) -{ - if (tag_checked && s->mte_active[0]) { - TCGv_i64 ret; - int desc = 0; - - desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); - desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); - desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); - desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); - desc = FIELD_DP32(desc, MTEDESC, SIZEM1, size - 1); - - ret = new_tmp_a64(s); - gen_helper_mte_check(ret, cpu_env, tcg_constant_i32(desc), addr); - - return ret; - } - return clean_data_tbi(s, addr); -} - -typedef struct DisasCompare64 { - TCGCond cond; - TCGv_i64 value; -} DisasCompare64; - -static void a64_test_cc(DisasCompare64 *c64, int cc) -{ - DisasCompare c32; - - 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. */ - 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)); -} - -static void gen_exception_internal(int excp) -{ - assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); -} - -static void gen_exception_internal_insn(DisasContext *s, int excp) -{ - 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_update_pc(s, 0); - gen_helper_exception_bkpt_insn(cpu_env, tcg_constant_i32(syndrome)); - s->base.is_jmp = DISAS_NORETURN; -} - -static void gen_step_complete_exception(DisasContext *s) -{ - /* We just completed step of an insn. Move from Active-not-pending - * to Active-pending, and then also take the swstep exception. - * This corresponds to making the (IMPDEF) choice to prioritize - * swstep exceptions over asynchronous exceptions taken to an exception - * level where debug is disabled. This choice has the advantage that - * we do not need to maintain internal state corresponding to the - * ISV/EX syndrome bits between completion of the step and generation - * of the exception, and our syndrome information is always correct. - */ - gen_ss_advance(s); - gen_swstep_exception(s, 1, s->is_ldex); - s->base.is_jmp = DISAS_NORETURN; -} - -static inline bool use_goto_tb(DisasContext *s, uint64_t dest) -{ - if (s->ss_active) { - return false; - } - return translator_use_goto_tb(&s->base, dest); -} - -static void gen_goto_tb(DisasContext *s, int n, int64_t diff) -{ - 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 (TARGET_TB_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_update_pc(s, diff); - if (s->ss_active) { - gen_step_complete_exception(s); - } else { - tcg_gen_lookup_and_goto_ptr(); - s->base.is_jmp = DISAS_NORETURN; - } - } -} - -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 - * - * These functions are used for directly accessing a register in where - * changes to the final register value are likely to be made. If you - * need to use a register for temporary calculation (e.g. index type - * operations) use the read_* form. - * - * B1.2.1 Register mappings - * - * In instruction register encoding 31 can refer to ZR (zero register) or - * the SP (stack pointer) depending on context. In QEMU's case we map SP - * to cpu_X[31] and ZR accesses to a temporary which can be discarded. - * This is the point of the _sp forms. - */ -TCGv_i64 cpu_reg(DisasContext *s, int reg) -{ - if (reg == 31) { - return new_tmp_a64_zero(s); - } else { - return cpu_X[reg]; - } -} - -/* register access for when 31 == SP */ -TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) -{ - return cpu_X[reg]; -} - -/* read a cpu register in 32bit/64bit mode. Returns a TCGv_i64 - * representing the register contents. This TCGv is an auto-freed - * temporary so it need not be explicitly freed, and may be modified. - */ -TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) -{ - TCGv_i64 v = new_tmp_a64(s); - if (reg != 31) { - if (sf) { - tcg_gen_mov_i64(v, cpu_X[reg]); - } else { - tcg_gen_ext32u_i64(v, cpu_X[reg]); - } - } else { - tcg_gen_movi_i64(v, 0); - } - return v; -} - -TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) -{ - TCGv_i64 v = new_tmp_a64(s); - if (sf) { - tcg_gen_mov_i64(v, cpu_X[reg]); - } else { - tcg_gen_ext32u_i64(v, cpu_X[reg]); - } - return v; -} - -/* Return the offset into CPUARMState of a slice (from - * the least significant end) of FP register Qn (ie - * Dn, Sn, Hn or Bn). - * (Note that this is not the same mapping as for A32; see cpu.h) - */ -static inline int fp_reg_offset(DisasContext *s, int regno, MemOp size) -{ - return vec_reg_offset(s, regno, 0, size); -} - -/* Offset of the high half of the 128 bit vector Qn */ -static inline int fp_reg_hi_offset(DisasContext *s, int regno) -{ - return vec_reg_offset(s, regno, 1, MO_64); -} - -/* Convenience accessors for reading and writing single and double - * FP registers. Writing clears the upper parts of the associated - * 128 bit vector register, as required by the architecture. - * Note that unlike the GP register accessors, the values returned - * by the read functions must be manually freed. - */ -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)); - return v; -} - -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)); - return v; -} - -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)); - return v; -} - -/* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). - * If SVE is not enabled, then there are only 128 bits in the vector. - */ -static void clear_vec_high(DisasContext *s, bool is_q, int rd) -{ - unsigned ofs = fp_reg_offset(s, rd, MO_64); - unsigned vsz = vec_full_reg_size(s); - - /* Nop move, with side effect of clearing the tail. */ - tcg_gen_gvec_mov(MO_64, ofs, ofs, is_q ? 16 : 8, vsz); -} - -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); - clear_vec_high(s, false, reg); -} - -static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) -{ - TCGv_i64 tmp = tcg_temp_new_i64(); - - 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. */ -static void gen_gvec_fn2(DisasContext *s, bool is_q, int rd, int rn, - GVecGen2Fn *gvec_fn, int vece) -{ - gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), - is_q ? 16 : 8, vec_full_reg_size(s)); -} - -/* Expand a 2-operand + immediate AdvSIMD vector operation using - * an expander function. - */ -static void gen_gvec_fn2i(DisasContext *s, bool is_q, int rd, int rn, - int64_t imm, GVecGen2iFn *gvec_fn, int vece) -{ - gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), - imm, is_q ? 16 : 8, vec_full_reg_size(s)); -} - -/* Expand a 3-operand AdvSIMD vector operation using an expander function. */ -static void gen_gvec_fn3(DisasContext *s, bool is_q, int rd, int rn, int rm, - GVecGen3Fn *gvec_fn, int vece) -{ - gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), is_q ? 16 : 8, vec_full_reg_size(s)); -} - -/* Expand a 4-operand AdvSIMD vector operation using an expander function. */ -static void gen_gvec_fn4(DisasContext *s, bool is_q, int rd, int rn, int rm, - int rx, GVecGen4Fn *gvec_fn, int vece) -{ - gvec_fn(vece, vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), vec_full_reg_offset(s, rx), - is_q ? 16 : 8, vec_full_reg_size(s)); -} - -/* Expand a 2-operand operation using an out-of-line helper. */ -static void gen_gvec_op2_ool(DisasContext *s, bool is_q, int rd, - int rn, int data, gen_helper_gvec_2 *fn) -{ - tcg_gen_gvec_2_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - is_q ? 16 : 8, vec_full_reg_size(s), data, fn); -} - -/* Expand a 3-operand operation using an out-of-line helper. */ -static void gen_gvec_op3_ool(DisasContext *s, bool is_q, int rd, - int rn, int rm, int data, gen_helper_gvec_3 *fn) -{ - tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - is_q ? 16 : 8, vec_full_reg_size(s), data, fn); -} - -/* Expand a 3-operand + fpstatus pointer + simd data value operation using - * an out-of-line helper. - */ -static void gen_gvec_op3_fpst(DisasContext *s, bool is_q, int rd, int rn, - int rm, bool is_fp16, int data, - gen_helper_gvec_3_ptr *fn) -{ - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); - tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), - 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. */ -static void gen_gvec_op3_qc(DisasContext *s, bool is_q, int rd, int rn, - int rm, gen_helper_gvec_3_ptr *fn) -{ - TCGv_ptr qc_ptr = tcg_temp_new_ptr(); - - tcg_gen_addi_ptr(qc_ptr, cpu_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. */ -static void gen_gvec_op4_ool(DisasContext *s, bool is_q, int rd, int rn, - int rm, int ra, int data, gen_helper_gvec_4 *fn) -{ - tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, ra), - is_q ? 16 : 8, vec_full_reg_size(s), data, fn); -} - -/* - * Expand a 4-operand + fpstatus pointer + simd data value operation using - * an out-of-line helper. - */ -static void gen_gvec_op4_fpst(DisasContext *s, bool is_q, int rd, int rn, - int rm, int ra, bool is_fp16, int data, - gen_helper_gvec_4_ptr *fn) -{ - TCGv_ptr fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); - tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, 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 - * than the 32 bit equivalent. - */ -static inline void gen_set_NZ64(TCGv_i64 result) -{ - tcg_gen_extr_i64_i32(cpu_ZF, cpu_NF, result); - tcg_gen_or_i32(cpu_ZF, cpu_ZF, cpu_NF); -} - -/* Set NZCV as for a logical operation: NZ as per result, CV cleared. */ -static inline void gen_logic_CC(int sf, TCGv_i64 result) -{ - if (sf) { - gen_set_NZ64(result); - } else { - tcg_gen_extrl_i64_i32(cpu_ZF, result); - tcg_gen_mov_i32(cpu_NF, cpu_ZF); - } - tcg_gen_movi_i32(cpu_CF, 0); - tcg_gen_movi_i32(cpu_VF, 0); -} - -/* dest = T0 + T1; compute C, N, V and Z flags */ -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); - } 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); - } -} - -/* dest = T0 - T1; compute C, N, V and Z flags */ -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); - } 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); - } -} - -/* dest = T0 + T1 + CF; do not compute flags. */ -static void gen_adc(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) -{ - TCGv_i64 flag = tcg_temp_new_i64(); - 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); - } -} - -/* dest = T0 + T1 + CF; compute C, N, V and Z flags. */ -static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) -{ - if (sf) { - TCGv_i64 result = tcg_temp_new_i64(); - TCGv_i64 cf_64 = tcg_temp_new_i64(); - TCGv_i64 vf_64 = tcg_temp_new_i64(); - TCGv_i64 tmp = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); - - tcg_gen_extu_i32_i64(cf_64, cpu_CF); - tcg_gen_add2_i64(result, cf_64, t0, zero, cf_64, zero); - tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, zero); - tcg_gen_extrl_i64_i32(cpu_CF, cf_64); - gen_set_NZ64(result); - - tcg_gen_xor_i64(vf_64, result, t0); - tcg_gen_xor_i64(tmp, t0, t1); - tcg_gen_andc_i64(vf_64, vf_64, tmp); - 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(); - TCGv_i32 tmp = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_constant_i32(0); - - tcg_gen_extrl_i64_i32(t0_32, t0); - tcg_gen_extrl_i64_i32(t1_32, t1); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, zero, cpu_CF, zero); - tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, zero); - - tcg_gen_mov_i32(cpu_ZF, cpu_NF); - tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); - 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); - } -} - -/* - * Load/Store generators - */ - -/* - * Store from GPR register to memory. - */ -static void do_gpr_st_memidx(DisasContext *s, TCGv_i64 source, - TCGv_i64 tcg_addr, MemOp memop, int memidx, - bool iss_valid, - 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) { - uint32_t syn; - - syn = syn_data_abort_with_iss(0, - (memop & MO_SIZE), - false, - iss_srt, - iss_sf, - iss_ar, - 0, 0, 0, 0, 0, false); - disas_set_insn_syndrome(s, syn); - } -} - -static void do_gpr_st(DisasContext *s, TCGv_i64 source, - TCGv_i64 tcg_addr, MemOp memop, - bool iss_valid, - unsigned int iss_srt, - bool iss_sf, bool iss_ar) -{ - do_gpr_st_memidx(s, source, tcg_addr, memop, get_mem_index(s), - iss_valid, iss_srt, iss_sf, iss_ar); -} - -/* - * Load from memory to GPR register - */ -static void do_gpr_ld_memidx(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, - MemOp memop, bool extend, int memidx, - 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)) { - g_assert((memop & MO_SIZE) <= MO_32); - tcg_gen_ext32u_i64(dest, dest); - } - - if (iss_valid) { - uint32_t syn; - - syn = syn_data_abort_with_iss(0, - (memop & MO_SIZE), - (memop & MO_SIGN) != 0, - iss_srt, - iss_sf, - iss_ar, - 0, 0, 0, 0, 0, false); - disas_set_insn_syndrome(s, syn); - } -} - -static void do_gpr_ld(DisasContext *s, TCGv_i64 dest, TCGv_i64 tcg_addr, - MemOp memop, bool extend, - bool iss_valid, unsigned int iss_srt, - bool iss_sf, bool iss_ar) -{ - do_gpr_ld_memidx(s, dest, tcg_addr, memop, extend, get_mem_index(s), - iss_valid, iss_srt, iss_sf, iss_ar); -} - -/* - * Store from FP register to memory - */ -static void do_fp_st(DisasContext *s, int srcidx, TCGv_i64 tcg_addr, int size) -{ - /* 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)); - - if (size < 4) { - mop = finalize_memop(s, size); - 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(); - - tcg_gen_ld_i64(tmphi, cpu_env, fp_reg_hi_offset(s, srcidx)); - - 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_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) -{ - /* 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); - 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; - - 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_st_i64(tmplo, cpu_env, fp_reg_offset(s, destidx, MO_64)); - tcg_temp_free_i64(tmplo); - - if (tmphi) { - tcg_gen_st_i64(tmphi, cpu_env, fp_reg_hi_offset(s, destidx)); - tcg_temp_free_i64(tmphi); - } - clear_vec_high(s, tmphi != NULL, destidx); -} - -/* - * Vector load/store helpers. - * - * The principal difference between this and a FP load is that we don't - * zero extend as we are filling a partial chunk of the vector register. - * These functions don't support 128 bit loads/stores, which would be - * normal load/store operations. - * - * The _i32 versions are useful when operating on 32 bit quantities - * (eg for floating point single or using Neon helper functions). - */ - -/* Get value of an element within a vector register */ -static void read_vec_element(DisasContext *s, TCGv_i64 tcg_dest, int srcidx, - int element, MemOp memop) -{ - 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); - break; - case MO_16: - tcg_gen_ld16u_i64(tcg_dest, cpu_env, vect_off); - break; - case MO_32: - tcg_gen_ld32u_i64(tcg_dest, cpu_env, vect_off); - break; - case MO_8|MO_SIGN: - tcg_gen_ld8s_i64(tcg_dest, cpu_env, vect_off); - break; - case MO_16|MO_SIGN: - tcg_gen_ld16s_i64(tcg_dest, cpu_env, vect_off); - break; - case MO_32|MO_SIGN: - tcg_gen_ld32s_i64(tcg_dest, cpu_env, vect_off); - break; - case MO_64: - case MO_64|MO_SIGN: - tcg_gen_ld_i64(tcg_dest, cpu_env, vect_off); - break; - default: - g_assert_not_reached(); - } -} - -static void read_vec_element_i32(DisasContext *s, TCGv_i32 tcg_dest, int srcidx, - int element, MemOp memop) -{ - 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); - break; - case MO_16: - tcg_gen_ld16u_i32(tcg_dest, cpu_env, vect_off); - break; - case MO_8|MO_SIGN: - tcg_gen_ld8s_i32(tcg_dest, cpu_env, vect_off); - break; - case MO_16|MO_SIGN: - tcg_gen_ld16s_i32(tcg_dest, cpu_env, vect_off); - break; - case MO_32: - case MO_32|MO_SIGN: - tcg_gen_ld_i32(tcg_dest, cpu_env, vect_off); - break; - default: - g_assert_not_reached(); - } -} - -/* Set value of an element within a vector register */ -static void write_vec_element(DisasContext *s, TCGv_i64 tcg_src, int destidx, - int element, MemOp memop) -{ - 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); - break; - case MO_16: - tcg_gen_st16_i64(tcg_src, cpu_env, vect_off); - break; - case MO_32: - tcg_gen_st32_i64(tcg_src, cpu_env, vect_off); - break; - case MO_64: - tcg_gen_st_i64(tcg_src, cpu_env, vect_off); - break; - default: - g_assert_not_reached(); - } -} - -static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src, - int destidx, int element, MemOp memop) -{ - 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); - break; - case MO_16: - tcg_gen_st16_i32(tcg_src, cpu_env, vect_off); - break; - case MO_32: - tcg_gen_st_i32(tcg_src, cpu_env, vect_off); - break; - default: - g_assert_not_reached(); - } -} - -/* Store from vector register to memory */ -static void do_vec_st(DisasContext *s, int srcidx, int element, - TCGv_i64 tcg_addr, MemOp mop) -{ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - 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 */ -static void do_vec_ld(DisasContext *s, int destidx, int element, - TCGv_i64 tcg_addr, MemOp mop) -{ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - 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 - * true. If not, emit code to generate an appropriate exception, - * and return false; the caller should not emit any code for - * the instruction. Note that this check must happen after all - * unallocated-encoding checks (otherwise the syndrome information - * for the resulting exception will be incorrect). - */ -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_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; -} - -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->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; -} - -/* - * This utility function is for doing register extension with an - * optional shift. You will likely want to pass a temporary for the - * destination register. See DecodeRegExtend() in the ARM ARM. - */ -static void ext_and_shift_reg(TCGv_i64 tcg_out, TCGv_i64 tcg_in, - int option, unsigned int shift) -{ - 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); - } -} - -static inline void gen_check_sp_alignment(DisasContext *s) -{ - /* The AArch64 architecture mandates that (if enabled via PSTATE - * or SCTLR bits) there is a check that SP is 16-aligned on every - * SP-relative load or store (with an exception generated if it is not). - * In line with general QEMU practice regarding misaligned accesses, - * we omit these checks for the sake of guest program performance. - * This function is provided as a hook so we can more easily add these - * checks in future (possibly as a "favour catching guest program bugs - * over speed" user selectable option). - */ -} - -/* - * This provides a simple table based table lookup decoder. It is - * intended to be used when the relevant bits for decode are too - * awkwardly placed and switch/if based logic would be confusing and - * deeply nested. Since it's a linear search through the table, tables - * should be kept small. - * - * It returns the first handler where insn & mask == pattern, or - * NULL if there is no match. - * The table is terminated by an empty mask (i.e. 0) - */ -static inline AArch64DecodeFn *lookup_disas_fn(const AArch64DecodeTable *table, - uint32_t insn) -{ - const AArch64DecodeTable *tptr = table; - - while (tptr->mask) { - if ((insn & tptr->mask) == tptr->pattern) { - return tptr->disas_fn; - } - tptr++; - } - return NULL; -} - -/* - * The instruction disassembly implemented here matches - * the instruction encoding classifications in chapter C4 - * of the ARM Architecture Reference Manual (DDI0487B_a); - * classification names and decode diagrams here should generally - * 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) -{ - int64_t diff = sextract32(insn, 0, 26) * 4; - - if (insn & (1U << 31)) { - /* BL Branch with link */ - gen_pc_plus_diff(s, cpu_reg(s, 30), curr_insn_len(s)); - } - - /* B Branch / BL Branch with link */ - reset_btype(s); - gen_goto_tb(s, 0, diff); -} - -/* 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) -{ - unsigned int sf, op, rt; - int64_t diff; - DisasLabel match; - TCGv_i64 tcg_cmp; - - sf = extract32(insn, 31, 1); - op = extract32(insn, 24, 1); /* 0: CBZ; 1: CBNZ */ - rt = extract32(insn, 0, 5); - diff = sextract32(insn, 5, 19) * 4; - - tcg_cmp = read_cpu_reg(s, rt, sf); - reset_btype(s); - - match = gen_disas_label(s); - tcg_gen_brcondi_i64(op ? 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, diff); -} - -/* 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) -{ - unsigned int bit_pos, op, rt; - int64_t diff; - 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 */ - diff = sextract32(insn, 5, 14) * 4; - rt = extract32(insn, 0, 5); - - tcg_cmp = tcg_temp_new_i64(); - tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, rt), (1ULL << bit_pos)); - - reset_btype(s); - - match = gen_disas_label(s); - tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, - tcg_cmp, 0, match.label); - tcg_temp_free_i64(tcg_cmp); - gen_goto_tb(s, 0, 4); - set_disas_label(s, match); - gen_goto_tb(s, 1, diff); -} - -/* 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) -{ - unsigned int cond; - int64_t diff; - - if ((insn & (1 << 4)) || (insn & (1 << 24))) { - unallocated_encoding(s); - return; - } - diff = sextract32(insn, 5, 19) * 4; - cond = extract32(insn, 0, 4); - - reset_btype(s); - if (cond < 0x0e) { - /* genuinely conditional branches */ - DisasLabel match = gen_disas_label(s); - arm_gen_test_cc(cond, match.label); - gen_goto_tb(s, 0, 4); - set_disas_label(s, match); - gen_goto_tb(s, 1, diff); - } else { - /* 0xe and 0xf are both "always" conditions */ - gen_goto_tb(s, 0, diff); - } -} - -/* 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) -{ - 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; - } -} - -static void gen_clrex(DisasContext *s, uint32_t insn) -{ - tcg_gen_movi_i64(cpu_exclusive_addr, -1); -} - -/* CLREX, DSB, DMB, ISB */ -static void handle_sync(DisasContext *s, uint32_t insn, - unsigned int op1, unsigned int op2, unsigned int crm) -{ - 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, 4); - 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, 4); - return; - - default: - do_unallocated: - unallocated_encoding(s); - return; - } -} - -static void gen_xaflag(void) -{ - TCGv_i32 z = tcg_temp_new_i32(); - - tcg_gen_setcondi_i32(TCG_COND_EQ, z, cpu_ZF, 0); - - /* - * (!C & !Z) << 31 - * (!(C | Z)) << 31 - * ~((C | Z) << 31) - * ~-(C | Z) - * (C | Z) - 1 - */ - tcg_gen_or_i32(cpu_NF, cpu_CF, z); - tcg_gen_subi_i32(cpu_NF, cpu_NF, 1); - - /* !(Z & C) */ - tcg_gen_and_i32(cpu_ZF, z, cpu_CF); - tcg_gen_xori_i32(cpu_ZF, cpu_ZF, 1); - - /* (!C & Z) << 31 -> -(Z & ~C) */ - tcg_gen_andc_i32(cpu_VF, z, cpu_CF); - tcg_gen_neg_i32(cpu_VF, cpu_VF); - - /* C | Z */ - tcg_gen_or_i32(cpu_CF, cpu_CF, z); - - tcg_temp_free_i32(z); -} - -static void gen_axflag(void) -{ - tcg_gen_sari_i32(cpu_VF, cpu_VF, 31); /* V ? -1 : 0 */ - tcg_gen_andc_i32(cpu_CF, cpu_CF, cpu_VF); /* C & !V */ - - /* !(Z | V) -> !(!ZF | V) -> ZF & !V -> ZF & ~VF */ - tcg_gen_andc_i32(cpu_ZF, cpu_ZF, cpu_VF); - - tcg_gen_movi_i32(cpu_NF, 0); - tcg_gen_movi_i32(cpu_VF, 0); -} - -/* 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) -{ - 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; - - case 0x1b: /* SVCR* */ - if (!dc_isar_feature(aa64_sme, s) || crm < 2 || crm > 7) { - goto do_unallocated; - } - if (sme_access_check(s)) { - bool i = crm & 1; - bool changed = false; - - if ((crm & 2) && i != s->pstate_sm) { - gen_helper_set_pstate_sm(cpu_env, tcg_constant_i32(i)); - changed = true; - } - if ((crm & 4) && i != s->pstate_za) { - gen_helper_set_pstate_za(cpu_env, tcg_constant_i32(i)); - changed = true; - } - if (changed) { - gen_rebuild_hflags(s); - } else { - s->base.is_jmp = DISAS_NEXT; - } - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - return; - } -} - -static void gen_get_nzcv(TCGv_i64 tcg_rt) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - TCGv_i32 nzcv = tcg_temp_new_i32(); - - /* build bit 31, N */ - tcg_gen_andi_i32(nzcv, cpu_NF, (1U << 31)); - /* build bit 30, Z */ - tcg_gen_setcondi_i32(TCG_COND_EQ, tmp, cpu_ZF, 0); - tcg_gen_deposit_i32(nzcv, nzcv, tmp, 30, 1); - /* build bit 29, C */ - tcg_gen_deposit_i32(nzcv, nzcv, cpu_CF, 29, 1); - /* build bit 28, V */ - tcg_gen_shri_i32(tmp, cpu_VF, 31); - 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) -{ - TCGv_i32 nzcv = tcg_temp_new_i32(); - - /* take NZCV from R[t] */ - tcg_gen_extrl_i64_i32(nzcv, tcg_rt); - - /* bit 31, N */ - tcg_gen_andi_i32(cpu_NF, nzcv, (1U << 31)); - /* bit 30, Z */ - tcg_gen_andi_i32(cpu_ZF, nzcv, (1 << 30)); - tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_ZF, cpu_ZF, 0); - /* bit 29, C */ - tcg_gen_andi_i32(cpu_CF, nzcv, (1 << 29)); - tcg_gen_shri_i32(cpu_CF, cpu_CF, 29); - /* 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, - uint8_t op0, uint8_t op1, uint8_t op2, - uint8_t crn, uint8_t crm, uint8_t rt) -{ - /* - * Generate code to emit an UNDEF with correct syndrome - * information for a failed system register access. - * This is EC_UNCATEGORIZED (ie a standard UNDEF) in most cases, - * but if FEAT_IDST is implemented then read accesses to registers - * in the feature ID space are reported with the EC_SYSTEMREGISTERTRAP - * syndrome. - */ - uint32_t syndrome; - - if (isread && dc_isar_feature(aa64_ids, s) && - arm_cpreg_encoding_in_idspace(op0, op1, op2, crn, crm)) { - syndrome = syn_aa64_sysregtrap(op0, op1, op2, crn, crm, rt, isread); - } else { - syndrome = syn_uncategorized(); - } - gen_exception_insn(s, 0, EXCP_UDEF, syndrome); -} - -/* MRS - move from system register - * MSR (register) - move to system register - * SYS - * SYSL - * 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, - unsigned int op0, unsigned int op1, unsigned int op2, - unsigned int crn, unsigned int crm, unsigned int rt) -{ - const ARMCPRegInfo *ri; - TCGv_i64 tcg_rt; - - ri = get_arm_cp_reginfo(s->cp_regs, - ENCODE_AA64_CP_REG(CP_REG_ARM64_SYSREG_CP, - crn, crm, op0, op1, op2)); - - if (!ri) { - /* Unknown register; this might be a guest error or a QEMU - * unimplemented feature. - */ - qemu_log_mask(LOG_UNIMP, "%s access to unsupported AArch64 " - "system register op0:%d op1:%d crn:%d crm:%d op2:%d\n", - isread ? "read" : "write", op0, op1, crn, crm, op2); - gen_sysreg_undef(s, isread, op0, op1, op2, crn, crm, rt); - 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 (ri->accessfn) { - /* 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_update_pc(s, 0); - 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_a64_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_NZCV: - tcg_rt = cpu_reg(s, rt); - if (isread) { - gen_get_nzcv(tcg_rt); - } else { - gen_set_nzcv(tcg_rt); - } - return; - case ARM_CP_CURRENTEL: - /* Reads as current EL value from pstate, which is - * guaranteed to be constant by the tb flags. - */ - tcg_rt = cpu_reg(s, rt); - tcg_gen_movi_i64(tcg_rt, s->current_el << 2); - return; - case ARM_CP_DC_ZVA: - /* Writes clear the aligned block of memory which rt points into. */ - if (s->mte_active[0]) { - int desc = 0; - - desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); - desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid); - desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); - - tcg_rt = new_tmp_a64(s); - gen_helper_mte_check_zva(tcg_rt, cpu_env, - tcg_constant_i32(desc), cpu_reg(s, rt)); - } else { - tcg_rt = clean_data_tbi(s, cpu_reg(s, rt)); - } - gen_helper_dc_zva(cpu_env, tcg_rt); - return; - case ARM_CP_DC_GVA: - { - TCGv_i64 clean_addr, tag; - - /* - * DC_GVA, like DC_ZVA, requires that we supply the original - * pointer for an invalid page. Probe that address first. - */ - tcg_rt = cpu_reg(s, rt); - clean_addr = clean_data_tbi(s, tcg_rt); - gen_probe_access(s, clean_addr, MMU_DATA_STORE, MO_8); - - if (s->ata) { - /* 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); - } - } - return; - case ARM_CP_DC_GZVA: - { - TCGv_i64 clean_addr, tag; - - /* 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); - - if (s->ata) { - /* 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); - } - } - return; - default: - g_assert_not_reached(); - } - 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 ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { - gen_io_start(); - } - - tcg_rt = cpu_reg(s, rt); - - if (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)); - } else { - tcg_gen_ld_i64(tcg_rt, cpu_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); - } else { - tcg_gen_st_i64(tcg_rt, cpu_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 - * must rebuild the hflags for the next TB. - */ - gen_rebuild_hflags(s); - /* - * We default to ending the TB on a coprocessor register write, - * but allow this to be suppressed by the register definition - * (usually only necessary to work around guest bugs). - */ - 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) -{ - 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); -} - -/* 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) -{ - 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, 4, EXCP_SWI, syn_aa64_svc(imm16)); - 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_update_pc(s, 0); - gen_helper_pre_hvc(cpu_env); - gen_ss_advance(s); - gen_exception_insn_el(s, 4, EXCP_HVC, syn_aa64_hvc(imm16), 2); - break; - case 3: /* SMC */ - if (s->current_el == 0) { - unallocated_encoding(s); - break; - } - gen_a64_update_pc(s, 0); - gen_helper_pre_smc(cpu_env, tcg_constant_i32(syn_aa64_smc(imm16))); - gen_ss_advance(s); - gen_exception_insn_el(s, 4, 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(s->current_el == 0) && imm16 == 0xf000) { - gen_exception_internal_insn(s, 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; - } -} - -/* 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) -{ - unsigned int opc, op2, op3, rn, op4; - unsigned btype_mod = 2; /* 0: BR, 1: BLR, 2: other */ - TCGv_i64 dst; - TCGv_i64 modifier; - - 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; - } - /* BLR also needs to load return address */ - if (opc == 1) { - TCGv_i64 lr = cpu_reg(s, 30); - if (dst == lr) { - TCGv_i64 tmp = new_tmp_a64(s); - tcg_gen_mov_i64(tmp, dst); - dst = tmp; - } - gen_pc_plus_diff(s, lr, curr_insn_len(s)); - } - gen_a64_set_pc(s, dst); - 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); - } - /* BLRAA also needs to load return address */ - if (opc == 9) { - TCGv_i64 lr = cpu_reg(s, 30); - if (dst == lr) { - TCGv_i64 tmp = new_tmp_a64(s); - tcg_gen_mov_i64(tmp, dst); - dst = tmp; - } - gen_pc_plus_diff(s, lr, curr_insn_len(s)); - } - gen_a64_set_pc(s, dst); - 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: - unallocated_encoding(s); - return; - } - - 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; -} - -/* Branches, exception generating and system instructions */ -static void disas_b_exc_sys(DisasContext *s, uint32_t insn) -{ - 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: - unallocated_encoding(s); - break; - } -} - -/* - * Load/Store exclusive instructions are implemented by remembering - * the value/address loaded, and seeing if these are the same - * when the store is performed. This is not actually the architecturally - * mandated semantics, but it works for typical guest code sequences - * and avoids having to monitor regular stores. - * - * The store exclusive uses the atomic cmpxchg primitives to avoid - * 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) -{ - int idx = get_mem_index(s); - MemOp memop = s->be_data; - - 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); - 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); - } else { - tcg_gen_extract_i64(cpu_reg(s, rt), cpu_exclusive_val, 32, 32); - 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_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_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_mov_i64(cpu_reg(s, rt), cpu_exclusive_val); - } - tcg_gen_mov_i64(cpu_exclusive_addr, addr); -} - -static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, - TCGv_i64 addr, int size, int is_pair) -{ - /* if (env->exclusive_addr == addr && env->exclusive_val == [addr] - * && (!is_pair || env->exclusive_high == [addr + datasize])) { - * [addr] = {Rt}; - * if (is_pair) { - * [addr + datasize] = {Rt2}; - * } - * {Rd} = 0; - * } else { - * {Rd} = 1; - * } - * env->exclusive_addr = -1; - */ - TCGLabel *fail_label = gen_new_label(); - TCGLabel *done_label = gen_new_label(); - TCGv_i64 tmp; - - tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_exclusive_addr, fail_label); - - tmp = tcg_temp_new_i64(); - if (is_pair) { - if (size == 2) { - if (s->be_data == MO_LE) { - tcg_gen_concat32_i64(tmp, cpu_reg(s, rt), cpu_reg(s, rt2)); - } else { - tcg_gen_concat32_i64(tmp, cpu_reg(s, rt2), cpu_reg(s, rt)); - } - tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, - cpu_exclusive_val, tmp, - get_mem_index(s), - MO_64 | MO_ALIGN | s->be_data); - 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)); - } - } 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); - 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); - tcg_gen_movi_i64(cpu_reg(s, rd), 1); - gen_set_label(done_label); - tcg_gen_movi_i64(cpu_exclusive_addr, -1); -} - -static void gen_compare_and_swap(DisasContext *s, int rs, int rt, - int rn, int size) -{ - TCGv_i64 tcg_rs = cpu_reg(s, rs); - TCGv_i64 tcg_rt = cpu_reg(s, rt); - int memidx = get_mem_index(s); - TCGv_i64 clean_addr; - - 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); -} - -static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, - int rn, int size) -{ - TCGv_i64 s1 = cpu_reg(s, rs); - TCGv_i64 s2 = cpu_reg(s, rs + 1); - TCGv_i64 t1 = cpu_reg(s, rt); - TCGv_i64 t2 = cpu_reg(s, rt + 1); - TCGv_i64 clean_addr; - int memidx = get_mem_index(s); - - 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); - - if (size == 2) { - TCGv_i64 cmp = tcg_temp_new_i64(); - TCGv_i64 val = tcg_temp_new_i64(); - - if (s->be_data == MO_LE) { - tcg_gen_concat32_i64(val, t1, t2); - tcg_gen_concat32_i64(cmp, s1, s2); - } else { - tcg_gen_concat32_i64(val, t2, t1); - 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); - - 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); - - /* 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); - - /* 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); - - /* 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); - } -} - -/* Update the Sixty-Four bit (SF) registersize. 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) -{ - 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 = new_tmp_a64(s); - gen_pc_plus_diff(s, clean_addr, 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 (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; - } - - if (is_vector && !fp_access_check(s)) { - return; - } - - offset <<= (set_tag ? LOG2_TAG_GRANULE : size); - - if (rn == 31) { - gen_check_sp_alignment(s); - } - - dirty_addr = read_cpu_reg_sp(s, rn, 1); - if (!postindex) { - 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 = gen_mte_checkN(s, dirty_addr, !is_load, - (wback || rn != 31) && !set_tag, 2 << size); - - 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); - } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - TCGv_i64 tcg_rt2 = cpu_reg(s, rt2); - - 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); - } 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); - } - } - - if (wback) { - if (postindex) { - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); - } -} - -/* - * 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) -{ - 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; - bool post_index; - bool writeback; - int memidx; - - TCGv_i64 clean_addr, dirty_addr; - - 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(); - } - - iss_valid = !is_vector && !writeback; - - 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); - } -} - -/* - * 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) -{ - 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; - - 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); - } - } -} - -/* 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) -{ - 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; - - 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) { - gen_check_sp_alignment(s); - } - clean_addr = gen_mte_check1(s, cpu_reg_sp(s, rn), false, rn != 31, size); - - 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_rs = read_cpu_reg(s, rs, true); - tcg_rt = cpu_reg(s, rt); - - if (o3_opc == 1) { /* LDCLR */ - tcg_gen_not_i64(tcg_rs, tcg_rs); - } - - /* 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) { - switch (size) { - 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(); - } - } -} - -/* - * 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) -{ - 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; - - if (size != 3 || is_vector || !dc_isar_feature(aa64_pauth, s)) { - unallocated_encoding(s); - return; - } - - if (rn == 31) { - gen_check_sp_alignment(s); - } - dirty_addr = read_cpu_reg_sp(s, rn, 1); - - if (s->pauth_active) { - if (use_key_a) { - gen_helper_autda(dirty_addr, cpu_env, dirty_addr, - new_tmp_a64_zero(s)); - } else { - gen_helper_autdb(dirty_addr, cpu_env, dirty_addr, - new_tmp_a64_zero(s)); - } - } - - /* 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); - - /* 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); - - 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); - - if (is_wback) { - tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); - } -} - -/* - * LDAPR/STLR (unscaled immediate) - * - * 31 30 24 22 21 12 10 5 0 - * +------+-------------+-----+---+--------+-----+----+-----+ - * | size | 0 1 1 0 0 1 | opc | 0 | imm9 | 0 0 | Rn | Rt | - * +------+-------------+-----+---+--------+-----+----+-----+ - * - * Rt: source or destination register - * Rn: base register - * imm9: unscaled immediate offset - * opc: 00: STLUR*, 01/10/11: various LDAPUR* - * size: size of load/store - */ -static void disas_ldst_ldapr_stlr(DisasContext *s, uint32_t insn) -{ - int rt = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int offset = sextract32(insn, 12, 9); - int opc = extract32(insn, 22, 2); - int size = extract32(insn, 30, 2); - TCGv_i64 clean_addr, dirty_addr; - bool is_store = false; - bool extend = false; - bool iss_sf; - MemOp mop; - - if (!dc_isar_feature(aa64_rcpc_8_4, s)) { - unallocated_encoding(s); - return; - } - - /* 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) { - gen_check_sp_alignment(s); - } - - dirty_addr = read_cpu_reg_sp(s, rn, 1); - tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); - clean_addr = clean_data_tbi(s, dirty_addr); - - if (is_store) { - /* Store-Release semantics */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - do_gpr_st(s, cpu_reg(s, rt), clean_addr, 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/store register (all forms) */ -static void disas_ldst_reg(DisasContext *s, uint32_t insn) -{ - 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); - - 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; - } - unallocated_encoding(s); -} - -/* 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) -{ - 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; - - if (extract32(insn, 31, 1) || extract32(insn, 21, 1)) { - unallocated_encoding(s); - return; - } - - if (!is_postidx && rm != 0) { - unallocated_encoding(s); - return; - } - - /* 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; - } - - if (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 = rpt * selem * (is_q ? 16 : 8); - tcg_rn = cpu_reg_sp(s, 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); - - /* - * 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) { - align = pow2_align(size); - size = 3; - } - if (!s->align_mem) { - align = 0; - } - mop = endian | size | align; - - elements = (is_q ? 16 : 8) >> size; - tcg_ebytes = tcg_constant_i64(1 << size); - for (r = 0; r < 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); - } - 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); - } - } - - if (is_postidx) { - if (rm == 31) { - tcg_gen_addi_i64(tcg_rn, tcg_rn, total); - } else { - tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); - } - } -} - -/* 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) -{ - 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); - - 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; - TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; - MemOp mop; - - if (extract32(insn, 31, 1)) { - unallocated_encoding(s); - return; - } - 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; - } - - if (rn == 31) { - gen_check_sp_alignment(s); - } - - total = selem << scale; - tcg_rn = cpu_reg_sp(s, rn); - - clean_addr = gen_mte_checkN(s, tcg_rn, !is_load, is_postidx || rn != 31, - total); - mop = finalize_memop(s, scale); - - 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_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); - rt = (rt + 1) % 32; - } - - if (is_postidx) { - if (rm == 31) { - tcg_gen_addi_i64(tcg_rn, tcg_rn, total); - } else { - tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); - } - } -} - -/* - * 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) -{ - 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; - - /* We checked insn bits [29:24,21] in the caller. */ - if (extract32(insn, 30, 2) != 3) { - goto do_unallocated; - } - - /* - * @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) { - 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); - } - - 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); - } - } 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); - } - } - return; - } - - 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); - } 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); - } - } 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); - } - } else { - if (is_pair) { - gen_helper_st2g(cpu_env, addr, tcg_rt); - } else { - gen_helper_stg(cpu_env, addr, tcg_rt); - } - } - } - - if (is_zero) { - TCGv_i64 clean_addr = clean_data_tbi(s, addr); - TCGv_i64 tcg_zero = tcg_constant_i64(0); - int mem_index = get_mem_index(s); - int i, n = (1 + is_pair) << LOG2_TAG_GRANULE; - - 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); - } - } - - if (index != 0) { - /* pre-index or post-index */ - if (index < 0) { - /* post-index */ - tcg_gen_addi_i64(addr, addr, offset); - } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), addr); - } -} - -/* Loads and stores */ -static void disas_ldst(DisasContext *s, uint32_t insn) -{ - 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; - } -} - -/* PC-rel. addressing - * 31 30 29 28 24 23 5 4 0 - * +----+-------+-----------+-------------------+------+ - * | op | immlo | 1 0 0 0 0 | immhi | Rd | - * +----+-------+-----------+-------------------+------+ - */ -static void disas_pc_rel_adr(DisasContext *s, uint32_t insn) -{ - unsigned int page, rd; - int64_t offset; - - 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); - - if (page) { - /* ADRP (page based) */ - offset <<= 12; - /* The page offset is ok for TARGET_TB_PCREL. */ - offset -= s->pc_curr & 0xfff; - } - - gen_pc_plus_diff(s, cpu_reg(s, rd), offset); -} - -/* - * 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); -} - -/* - * 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) -{ - 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; - if (sub_op) { - imm = -imm; - } - - tcg_rn = cpu_reg_sp(s, rn); - tcg_rd = cpu_reg_sp(s, rd); - - if (s->ata) { - gen_helper_addsubg(tcg_rd, cpu_env, tcg_rn, - tcg_constant_i32(imm), - tcg_constant_i32(uimm4)); - } else { - tcg_gen_addi_i64(tcg_rd, tcg_rn, imm); - gen_address_with_allocation_tag0(tcg_rd, tcg_rd); - } -} - -/* 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. - */ -static uint64_t bitfield_replicate(uint64_t mask, unsigned int e) -{ - assert(e != 0); - while (e < 64) { - mask |= mask << e; - e *= 2; - } - 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); -} - -/* 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. - */ -bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, - unsigned int imms, unsigned int immr) -{ - uint64_t mask; - unsigned e, levels, s, r; - int len; - - assert(immn < 2 && imms < 64 && immr < 64); - - /* The bit patterns we create here are 64 bit patterns which - * are vectors of identical elements of size e = 2, 4, 8, 16, 32 or - * 64 bits each. Each element contains the same value: a run - * of between 1 and e-1 non-zero bits, rotated within the - * element by between 0 and e-1 bits. - * - * The element size and run length are encoded into immn (1 bit) - * and imms (6 bits) as follows: - * 64 bit elements: immn = 1, imms = - * 32 bit elements: immn = 0, imms = 0 : - * 16 bit elements: immn = 0, imms = 10 : - * 8 bit elements: immn = 0, imms = 110 : - * 4 bit elements: immn = 0, imms = 1110 : - * 2 bit elements: immn = 0, imms = 11110 : - * Notice that immn = 0, imms = 11111x is the only combination - * not covered by one of the above options; this is reserved. - * Further, all-ones is a reserved pattern. - * - * In all cases the rotation is by immr % e (and immr is 6 bits). - */ - - /* First determine the element size */ - len = 31 - clz32((immn << 6) | (~imms & 0x3f)); - if (len < 1) { - /* This is the immn == 0, imms == 0x11111x case */ - return false; - } - e = 1 << len; - - levels = e - 1; - s = imms & levels; - r = immr & levels; - - if (s == levels) { - /* mustn't be all-ones. */ - return false; - } - - /* 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); - if (r) { - mask = (mask >> r) | (mask << (e - r)); - mask &= bitmask64(e); - } - /* ...then replicate the element over the whole 64 bit value */ - mask = bitfield_replicate(mask, e); - *result = mask; - 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) -{ - unsigned int sf, opc, is_n, immr, imms, rn, rd; - TCGv_i64 tcg_rd, tcg_rn; - uint64_t wmask; - bool is_and = false; - - 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; - } - - if (opc == 0x3) { /* ANDS */ - tcg_rd = cpu_reg(s, rd); - } else { - tcg_rd = cpu_reg_sp(s, rd); - } - tcg_rn = cpu_reg(s, rn); - - if (!logic_imm_decode_wmask(&wmask, is_n, imms, immr)) { - /* some immediate field values are reserved */ - unallocated_encoding(s); - return; - } - - 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. - */ - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - - if (opc == 3) { /* ANDS */ - gen_logic_CC(sf, tcg_rd); - } -} - -/* - * 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) -{ - 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; - } -} - -/* 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) -{ - unsigned int sf, n, opc, ri, si, rn, rd, bitsize, pos, len; - TCGv_i64 tcg_rd, tcg_tmp; - - 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; - } - - tcg_rd = cpu_reg(s, rd); - - /* 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); - - /* 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; - } - /* opc == 1, BFXIL fall through to deposit */ - tcg_gen_shri_i64(tcg_tmp, tcg_tmp, ri); - pos = 0; - } else { - /* Handle the ri > si case with a deposit - * 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_ext32u_i64(tcg_rd, tcg_rd); - } -} - -/* 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) -{ - unsigned int sf, n, rm, imm, rn, rd, bitsize, op21, op0; - - 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; - - 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)); - } - } else { - tcg_rm = cpu_reg(s, rm); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - /* Specialization to ROR happens in EXTRACT2. */ - tcg_gen_extract2_i64(tcg_rd, tcg_rm, tcg_rn, 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); - } - } - } -} - -/* 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; - } -} - -/* Shift a TCGv src by TCGv shift_amount, put result in dst. - * Note that it is the caller's responsibility to ensure that the - * shift amount is in range (ie 0..31 or 0..63) and provide the ARM - * mandated semantics for out of range shifts. - */ -static void shift_reg(TCGv_i64 dst, TCGv_i64 src, int sf, - enum a64_shift_type shift_type, TCGv_i64 shift_amount) -{ - switch (shift_type) { - case A64_SHIFT_TYPE_LSL: - tcg_gen_shl_i64(dst, src, shift_amount); - break; - case A64_SHIFT_TYPE_LSR: - tcg_gen_shr_i64(dst, src, shift_amount); - break; - case A64_SHIFT_TYPE_ASR: - if (!sf) { - tcg_gen_ext32s_i64(dst, src); - } - tcg_gen_sar_i64(dst, sf ? src : dst, shift_amount); - break; - case A64_SHIFT_TYPE_ROR: - if (sf) { - tcg_gen_rotr_i64(dst, src, shift_amount); - } else { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t0, src); - 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: - assert(FALSE); /* all shift types should be handled */ - break; - } - - if (!sf) { /* zero extend final result */ - tcg_gen_ext32u_i64(dst, dst); - } -} - -/* Shift a TCGv src by immediate, put result in dst. - * The shift amount must be in range (this should always be true as the - * relevant instructions will UNDEF on bad shift immediates). - */ -static void shift_reg_imm(TCGv_i64 dst, TCGv_i64 src, int sf, - enum a64_shift_type shift_type, unsigned int shift_i) -{ - assert(shift_i < (sf ? 64 : 32)); - - if (shift_i == 0) { - tcg_gen_mov_i64(dst, src); - } else { - shift_reg(dst, src, sf, shift_type, tcg_constant_i64(shift_i)); - } -} - -/* Logical (shifted register) - * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 - * +----+-----+-----------+-------+---+------+--------+------+------+ - * | sf | opc | 0 1 0 1 0 | shift | N | Rm | imm6 | Rn | Rd | - * +----+-----+-----------+-------+---+------+--------+------+------+ - */ -static void disas_logic_reg(DisasContext *s, uint32_t insn) -{ - TCGv_i64 tcg_rd, tcg_rn, tcg_rm; - unsigned int sf, opc, shift_type, invert, rm, shift_amount, rn, rd; - - sf = extract32(insn, 31, 1); - opc = extract32(insn, 29, 2); - shift_type = extract32(insn, 22, 2); - invert = extract32(insn, 21, 1); - rm = extract32(insn, 16, 5); - shift_amount = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (!sf && (shift_amount & (1 << 5))) { - unallocated_encoding(s); - return; - } - - tcg_rd = cpu_reg(s, rd); - - if (opc == 1 && shift_amount == 0 && shift_type == 0 && rn == 31) { - /* Unshifted ORR and ORN with WZR/XZR is the standard encoding for - * register-register MOV and MVN, so it is worth special casing. - */ - tcg_rm = cpu_reg(s, rm); - if (invert) { - tcg_gen_not_i64(tcg_rd, tcg_rm); - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - } else { - if (sf) { - tcg_gen_mov_i64(tcg_rd, tcg_rm); - } else { - tcg_gen_ext32u_i64(tcg_rd, tcg_rm); - } - } - return; - } - - tcg_rm = read_cpu_reg(s, rm, sf); - - if (shift_amount) { - shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, shift_amount); - } - - tcg_rn = cpu_reg(s, rn); - - switch (opc | (invert << 2)) { - case 0: /* AND */ - case 3: /* ANDS */ - tcg_gen_and_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 1: /* ORR */ - tcg_gen_or_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 2: /* EOR */ - tcg_gen_xor_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 4: /* BIC */ - case 7: /* BICS */ - tcg_gen_andc_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 5: /* ORN */ - tcg_gen_orc_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 6: /* EON */ - tcg_gen_eqv_i64(tcg_rd, tcg_rn, tcg_rm); - break; - default: - assert(FALSE); - break; - } - - if (!sf) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } - - if (opc == 3) { - gen_logic_CC(sf, tcg_rd); - } -} - -/* - * Add/subtract (extended register) - * - * 31|30|29|28 24|23 22|21|20 16|15 13|12 10|9 5|4 0| - * +--+--+--+-----------+-----+--+-------+------+------+----+----+ - * |sf|op| S| 0 1 0 1 1 | opt | 1| Rm |option| imm3 | Rn | Rd | - * +--+--+--+-----------+-----+--+-------+------+------+----+----+ - * - * sf: 0 -> 32bit, 1 -> 64bit - * op: 0 -> add , 1 -> sub - * S: 1 -> set flags - * opt: 00 - * option: extension type (see DecodeRegExtend) - * imm3: optional shift to Rm - * - * Rd = Rn + LSL(extend(Rm), amount) - */ -static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm3 = extract32(insn, 10, 3); - int option = extract32(insn, 13, 3); - int rm = extract32(insn, 16, 5); - int opt = extract32(insn, 22, 2); - bool setflags = extract32(insn, 29, 1); - bool sub_op = extract32(insn, 30, 1); - bool sf = extract32(insn, 31, 1); - - TCGv_i64 tcg_rm, tcg_rn; /* temps */ - TCGv_i64 tcg_rd; - TCGv_i64 tcg_result; - - if (imm3 > 4 || opt != 0) { - unallocated_encoding(s); - return; - } - - /* non-flag setting ops may use SP */ - if (!setflags) { - tcg_rd = cpu_reg_sp(s, rd); - } else { - tcg_rd = cpu_reg(s, rd); - } - tcg_rn = read_cpu_reg_sp(s, rn, sf); - - tcg_rm = read_cpu_reg(s, rm, sf); - ext_and_shift_reg(tcg_rm, tcg_rm, option, imm3); - - tcg_result = tcg_temp_new_i64(); - - if (!setflags) { - if (sub_op) { - tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); - } else { - tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); - } - } else { - if (sub_op) { - gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); - } else { - gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); - } - } - - if (sf) { - tcg_gen_mov_i64(tcg_rd, tcg_result); - } else { - tcg_gen_ext32u_i64(tcg_rd, tcg_result); - } - - tcg_temp_free_i64(tcg_result); -} - -/* - * Add/subtract (shifted register) - * - * 31 30 29 28 24 23 22 21 20 16 15 10 9 5 4 0 - * +--+--+--+-----------+-----+--+-------+---------+------+------+ - * |sf|op| S| 0 1 0 1 1 |shift| 0| Rm | imm6 | Rn | Rd | - * +--+--+--+-----------+-----+--+-------+---------+------+------+ - * - * sf: 0 -> 32bit, 1 -> 64bit - * op: 0 -> add , 1 -> sub - * S: 1 -> set flags - * shift: 00 -> LSL, 01 -> LSR, 10 -> ASR, 11 -> RESERVED - * imm6: Shift amount to apply to Rm before the add/sub - */ -static void disas_add_sub_reg(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm6 = extract32(insn, 10, 6); - int rm = extract32(insn, 16, 5); - int shift_type = extract32(insn, 22, 2); - bool setflags = extract32(insn, 29, 1); - bool sub_op = extract32(insn, 30, 1); - bool sf = extract32(insn, 31, 1); - - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn, tcg_rm; - TCGv_i64 tcg_result; - - if ((shift_type == 3) || (!sf && (imm6 > 31))) { - unallocated_encoding(s); - return; - } - - tcg_rn = read_cpu_reg(s, rn, sf); - tcg_rm = read_cpu_reg(s, rm, sf); - - shift_reg_imm(tcg_rm, tcg_rm, sf, shift_type, imm6); - - tcg_result = tcg_temp_new_i64(); - - if (!setflags) { - if (sub_op) { - tcg_gen_sub_i64(tcg_result, tcg_rn, tcg_rm); - } else { - tcg_gen_add_i64(tcg_result, tcg_rn, tcg_rm); - } - } else { - if (sub_op) { - gen_sub_CC(sf, tcg_result, tcg_rn, tcg_rm); - } else { - gen_add_CC(sf, tcg_result, tcg_rn, tcg_rm); - } - } - - if (sf) { - tcg_gen_mov_i64(tcg_rd, tcg_result); - } else { - tcg_gen_ext32u_i64(tcg_rd, tcg_result); - } - - tcg_temp_free_i64(tcg_result); -} - -/* Data-processing (3 source) - * - * 31 30 29 28 24 23 21 20 16 15 14 10 9 5 4 0 - * +--+------+-----------+------+------+----+------+------+------+ - * |sf| op54 | 1 1 0 1 1 | op31 | Rm | o0 | Ra | Rn | Rd | - * +--+------+-----------+------+------+----+------+------+------+ - */ -static void disas_data_proc_3src(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int ra = extract32(insn, 10, 5); - int rm = extract32(insn, 16, 5); - int op_id = (extract32(insn, 29, 3) << 4) | - (extract32(insn, 21, 3) << 1) | - extract32(insn, 15, 1); - bool sf = extract32(insn, 31, 1); - bool is_sub = extract32(op_id, 0, 1); - bool is_high = extract32(op_id, 2, 1); - bool is_signed = false; - TCGv_i64 tcg_op1; - TCGv_i64 tcg_op2; - TCGv_i64 tcg_tmp; - - /* Note that op_id is sf:op54:op31:o0 so it includes the 32/64 size flag */ - switch (op_id) { - case 0x42: /* SMADDL */ - case 0x43: /* SMSUBL */ - case 0x44: /* SMULH */ - is_signed = true; - break; - case 0x0: /* MADD (32bit) */ - case 0x1: /* MSUB (32bit) */ - case 0x40: /* MADD (64bit) */ - case 0x41: /* MSUB (64bit) */ - case 0x4a: /* UMADDL */ - case 0x4b: /* UMSUBL */ - case 0x4c: /* UMULH */ - break; - default: - unallocated_encoding(s); - return; - } - - if (is_high) { - TCGv_i64 low_bits = tcg_temp_new_i64(); /* low bits discarded */ - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = cpu_reg(s, rn); - TCGv_i64 tcg_rm = cpu_reg(s, rm); - - if (is_signed) { - tcg_gen_muls2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); - } else { - tcg_gen_mulu2_i64(low_bits, tcg_rd, tcg_rn, tcg_rm); - } - - tcg_temp_free_i64(low_bits); - return; - } - - tcg_op1 = tcg_temp_new_i64(); - tcg_op2 = tcg_temp_new_i64(); - tcg_tmp = tcg_temp_new_i64(); - - if (op_id < 0x42) { - tcg_gen_mov_i64(tcg_op1, cpu_reg(s, rn)); - tcg_gen_mov_i64(tcg_op2, cpu_reg(s, rm)); - } else { - if (is_signed) { - tcg_gen_ext32s_i64(tcg_op1, cpu_reg(s, rn)); - tcg_gen_ext32s_i64(tcg_op2, cpu_reg(s, rm)); - } else { - tcg_gen_ext32u_i64(tcg_op1, cpu_reg(s, rn)); - tcg_gen_ext32u_i64(tcg_op2, cpu_reg(s, rm)); - } - } - - if (ra == 31 && !is_sub) { - /* Special-case MADD with rA == XZR; it is the standard MUL alias */ - tcg_gen_mul_i64(cpu_reg(s, rd), tcg_op1, tcg_op2); - } else { - tcg_gen_mul_i64(tcg_tmp, tcg_op1, tcg_op2); - if (is_sub) { - tcg_gen_sub_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp); - } else { - tcg_gen_add_i64(cpu_reg(s, rd), cpu_reg(s, ra), tcg_tmp); - } - } - - 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) - * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0 - * +--+--+--+------------------------+------+-------------+------+-----+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | 0 0 0 0 0 0 | Rn | Rd | - * +--+--+--+------------------------+------+-------------+------+-----+ - */ - -static void disas_adc_sbc(DisasContext *s, uint32_t insn) -{ - unsigned int sf, op, setflags, rm, rn, rd; - TCGv_i64 tcg_y, tcg_rn, tcg_rd; - - sf = extract32(insn, 31, 1); - op = extract32(insn, 30, 1); - setflags = extract32(insn, 29, 1); - rm = extract32(insn, 16, 5); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (op) { - tcg_y = new_tmp_a64(s); - tcg_gen_not_i64(tcg_y, cpu_reg(s, rm)); - } else { - tcg_y = cpu_reg(s, rm); - } - - if (setflags) { - gen_adc_CC(sf, tcg_rd, tcg_rn, tcg_y); - } else { - gen_adc(sf, tcg_rd, tcg_rn, tcg_y); - } -} - -/* - * Rotate right into flags - * 31 30 29 21 15 10 5 4 0 - * +--+--+--+-----------------+--------+-----------+------+--+------+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | imm6 | 0 0 0 0 1 | Rn |o2| mask | - * +--+--+--+-----------------+--------+-----------+------+--+------+ - */ -static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn) -{ - int mask = extract32(insn, 0, 4); - int o2 = extract32(insn, 4, 1); - int rn = extract32(insn, 5, 5); - int imm6 = extract32(insn, 15, 6); - int sf_op_s = extract32(insn, 29, 3); - TCGv_i64 tcg_rn; - TCGv_i32 nzcv; - - if (sf_op_s != 5 || o2 != 0 || !dc_isar_feature(aa64_condm_4, s)) { - unallocated_encoding(s); - return; - } - - tcg_rn = read_cpu_reg(s, rn, 1); - tcg_gen_rotri_i64(tcg_rn, tcg_rn, imm6); - - nzcv = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(nzcv, tcg_rn); - - if (mask & 8) { /* N */ - tcg_gen_shli_i32(cpu_NF, nzcv, 31 - 3); - } - if (mask & 4) { /* Z */ - tcg_gen_not_i32(cpu_ZF, nzcv); - tcg_gen_andi_i32(cpu_ZF, cpu_ZF, 4); - } - if (mask & 2) { /* C */ - tcg_gen_extract_i32(cpu_CF, nzcv, 1, 1); - } - if (mask & 1) { /* V */ - tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0); - } - - tcg_temp_free_i32(nzcv); -} - -/* - * Evaluate into flags - * 31 30 29 21 15 14 10 5 4 0 - * +--+--+--+-----------------+---------+----+---------+------+--+------+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | opcode2 | sz | 0 0 1 0 | Rn |o3| mask | - * +--+--+--+-----------------+---------+----+---------+------+--+------+ - */ -static void disas_evaluate_into_flags(DisasContext *s, uint32_t insn) -{ - int o3_mask = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int o2 = extract32(insn, 15, 6); - int sz = extract32(insn, 14, 1); - int sf_op_s = extract32(insn, 29, 3); - TCGv_i32 tmp; - int shift; - - if (sf_op_s != 1 || o2 != 0 || o3_mask != 0xd || - !dc_isar_feature(aa64_condm_4, s)) { - unallocated_encoding(s); - return; - } - shift = sz ? 16 : 24; /* SETF16 or SETF8 */ - - tmp = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tmp, cpu_reg(s, rn)); - tcg_gen_shli_i32(cpu_NF, tmp, shift); - 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) - * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 - * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ - * |sf|op| S| 1 1 0 1 0 0 1 0 |imm5/rm | cond |i/r |o2| Rn |o3|nzcv | - * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ - * [1] y [0] [0] - */ -static void disas_cc(DisasContext *s, uint32_t insn) -{ - unsigned int sf, op, y, cond, rn, nzcv, is_imm; - TCGv_i32 tcg_t0, tcg_t1, tcg_t2; - TCGv_i64 tcg_tmp, tcg_y, tcg_rn; - DisasCompare c; - - if (!extract32(insn, 29, 1)) { - unallocated_encoding(s); - return; - } - if (insn & (1 << 10 | 1 << 4)) { - unallocated_encoding(s); - return; - } - sf = extract32(insn, 31, 1); - op = extract32(insn, 30, 1); - is_imm = extract32(insn, 11, 1); - y = extract32(insn, 16, 5); /* y = rm (reg) or imm5 (imm) */ - cond = extract32(insn, 12, 4); - rn = extract32(insn, 5, 5); - nzcv = extract32(insn, 0, 4); - - /* Set T0 = !COND. */ - 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_gen_movi_i64(tcg_y, y); - } else { - tcg_y = cpu_reg(s, y); - } - tcg_rn = cpu_reg(s, rn); - - /* Set the flags for the new comparison. */ - tcg_tmp = tcg_temp_new_i64(); - if (op) { - gen_sub_CC(sf, tcg_tmp, tcg_rn, tcg_y); - } 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). - * For tcg hosts that support ANDC, we can make do with just T1. - * In either case, allow the tcg optimizer to delete any unused mask. - */ - tcg_t1 = tcg_temp_new_i32(); - tcg_t2 = tcg_temp_new_i32(); - tcg_gen_neg_i32(tcg_t1, tcg_t0); - tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); - - if (nzcv & 8) { /* N */ - tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); - } else { - if (TCG_TARGET_HAS_andc_i32) { - tcg_gen_andc_i32(cpu_NF, cpu_NF, tcg_t1); - } else { - tcg_gen_and_i32(cpu_NF, cpu_NF, tcg_t2); - } - } - if (nzcv & 4) { /* Z */ - if (TCG_TARGET_HAS_andc_i32) { - tcg_gen_andc_i32(cpu_ZF, cpu_ZF, tcg_t1); - } else { - tcg_gen_and_i32(cpu_ZF, cpu_ZF, tcg_t2); - } - } else { - tcg_gen_or_i32(cpu_ZF, cpu_ZF, tcg_t0); - } - if (nzcv & 2) { /* C */ - tcg_gen_or_i32(cpu_CF, cpu_CF, tcg_t0); - } else { - if (TCG_TARGET_HAS_andc_i32) { - tcg_gen_andc_i32(cpu_CF, cpu_CF, tcg_t1); - } else { - tcg_gen_and_i32(cpu_CF, cpu_CF, tcg_t2); - } - } - if (nzcv & 1) { /* V */ - tcg_gen_or_i32(cpu_VF, cpu_VF, tcg_t1); - } else { - if (TCG_TARGET_HAS_andc_i32) { - tcg_gen_andc_i32(cpu_VF, cpu_VF, tcg_t1); - } else { - 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 - * 31 30 29 28 21 20 16 15 12 11 10 9 5 4 0 - * +----+----+---+-----------------+------+------+-----+------+------+ - * | sf | op | S | 1 1 0 1 0 1 0 0 | Rm | cond | op2 | Rn | Rd | - * +----+----+---+-----------------+------+------+-----+------+------+ - */ -static void disas_cond_select(DisasContext *s, uint32_t insn) -{ - unsigned int sf, else_inv, rm, cond, else_inc, rn, rd; - TCGv_i64 tcg_rd, zero; - DisasCompare64 c; - - if (extract32(insn, 29, 1) || extract32(insn, 11, 1)) { - /* S == 1 or op2<1> == 1 */ - unallocated_encoding(s); - return; - } - sf = extract32(insn, 31, 1); - else_inv = extract32(insn, 30, 1); - rm = extract32(insn, 16, 5); - cond = extract32(insn, 12, 4); - else_inc = extract32(insn, 10, 1); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - tcg_rd = cpu_reg(s, rd); - - a64_test_cc(&c, cond); - zero = tcg_constant_i64(0); - - 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); - } - } else { - TCGv_i64 t_true = cpu_reg(s, rn); - TCGv_i64 t_false = read_cpu_reg(s, rm, 1); - if (else_inv && else_inc) { - tcg_gen_neg_i64(t_false, t_false); - } else if (else_inv) { - tcg_gen_not_i64(t_false, t_false); - } else if (else_inc) { - tcg_gen_addi_i64(t_false, t_false, 1); - } - 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); - } -} - -static void handle_clz(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); - } else { - TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - 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); - } -} - -static void handle_cls(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - tcg_gen_clrsb_i64(tcg_rd, tcg_rn); - } else { - TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - 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); - } -} - -static void handle_rbit(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd, tcg_rn; - tcg_rd = cpu_reg(s, rd); - tcg_rn = cpu_reg(s, rn); - - if (sf) { - gen_helper_rbit64(tcg_rd, tcg_rn); - } else { - TCGv_i32 tcg_tmp32 = tcg_temp_new_i32(); - 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); - } -} - -/* REV with sf==1, opcode==3 ("REV64") */ -static void handle_rev64(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - if (!sf) { - unallocated_encoding(s); - return; - } - tcg_gen_bswap64_i64(cpu_reg(s, rd), cpu_reg(s, rn)); -} - -/* REV with sf==0, opcode==2 - * REV32 (sf==1, opcode==2) - */ -static void handle_rev32(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = cpu_reg(s, rn); - - if (sf) { - tcg_gen_bswap64_i64(tcg_rd, tcg_rn); - tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); - } else { - tcg_gen_bswap32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); - } -} - -/* REV16 (opcode==1) */ -static void handle_rev16(DisasContext *s, unsigned int sf, - unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); - TCGv_i64 mask = tcg_constant_i64(sf ? 0x00ff00ff00ff00ffull : 0x00ff00ff); - - tcg_gen_shri_i64(tcg_tmp, tcg_rn, 8); - tcg_gen_and_i64(tcg_rd, tcg_rn, mask); - 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) - * 31 30 29 28 21 20 16 15 10 9 5 4 0 - * +----+---+---+-----------------+---------+--------+------+------+ - * | sf | 1 | S | 1 1 0 1 0 1 1 0 | opcode2 | opcode | Rn | Rd | - * +----+---+---+-----------------+---------+--------+------+------+ - */ -static void disas_data_proc_1src(DisasContext *s, uint32_t insn) -{ - unsigned int sf, opcode, opcode2, rn, rd; - TCGv_i64 tcg_rd; - - if (extract32(insn, 29, 1)) { - unallocated_encoding(s); - return; - } - - sf = extract32(insn, 31, 1); - opcode = extract32(insn, 10, 6); - opcode2 = extract32(insn, 16, 5); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - -#define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) - - switch (MAP(sf, opcode2, opcode)) { - case MAP(0, 0x00, 0x00): /* RBIT */ - case MAP(1, 0x00, 0x00): - handle_rbit(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x01): /* REV16 */ - case MAP(1, 0x00, 0x01): - handle_rev16(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x02): /* REV/REV32 */ - case MAP(1, 0x00, 0x02): - handle_rev32(s, sf, rn, rd); - break; - case MAP(1, 0x00, 0x03): /* REV64 */ - handle_rev64(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x04): /* CLZ */ - case MAP(1, 0x00, 0x04): - handle_clz(s, sf, rn, rd); - break; - case MAP(0, 0x00, 0x05): /* CLS */ - case MAP(1, 0x00, 0x05): - handle_cls(s, sf, rn, rd); - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - 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)); - } else if (!dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - break; - case MAP(1, 0x01, 0x08): /* PACIZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x09): /* PACIZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x0a): /* PACDZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x0b): /* PACDZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x0c): /* AUTIZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x0d): /* AUTIZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x0e): /* AUTDZA */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x0f): /* AUTDZB */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - 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)); - } - break; - case MAP(1, 0x01, 0x10): /* XPACI */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_xpaci(tcg_rd, cpu_env, tcg_rd); - } - break; - case MAP(1, 0x01, 0x11): /* XPACD */ - if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { - goto do_unallocated; - } else if (s->pauth_active) { - tcg_rd = cpu_reg(s, rd); - gen_helper_xpacd(tcg_rd, cpu_env, tcg_rd); - } - break; - default: - do_unallocated: - unallocated_encoding(s); - break; - } - -#undef MAP -} - -static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, - unsigned int rm, unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_n, tcg_m, tcg_rd; - tcg_rd = cpu_reg(s, rd); - - if (!sf && is_signed) { - tcg_n = new_tmp_a64(s); - tcg_m = new_tmp_a64(s); - tcg_gen_ext32s_i64(tcg_n, cpu_reg(s, rn)); - tcg_gen_ext32s_i64(tcg_m, cpu_reg(s, rm)); - } else { - tcg_n = read_cpu_reg(s, rn, sf); - tcg_m = read_cpu_reg(s, rm, sf); - } - - if (is_signed) { - gen_helper_sdiv64(tcg_rd, tcg_n, tcg_m); - } else { - gen_helper_udiv64(tcg_rd, tcg_n, tcg_m); - } - - if (!sf) { /* zero extend final result */ - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } -} - -/* LSLV, LSRV, ASRV, RORV */ -static void handle_shift_reg(DisasContext *s, - enum a64_shift_type shift_type, unsigned int sf, - unsigned int rm, unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_shift = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = cpu_reg(s, rd); - TCGv_i64 tcg_rn = read_cpu_reg(s, rn, sf); - - 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] */ -static void handle_crc32(DisasContext *s, - unsigned int sf, unsigned int sz, bool crc32c, - unsigned int rm, unsigned int rn, unsigned int rd) -{ - TCGv_i64 tcg_acc, tcg_val; - TCGv_i32 tcg_bytes; - - if (!dc_isar_feature(aa64_crc32, s) - || (sf == 1 && sz != 3) - || (sf == 0 && sz == 3)) { - unallocated_encoding(s); - return; - } - - if (sz == 3) { - tcg_val = cpu_reg(s, rm); - } else { - uint64_t mask; - switch (sz) { - case 0: - mask = 0xFF; - break; - case 1: - mask = 0xFFFF; - break; - case 2: - mask = 0xFFFFFFFF; - break; - default: - g_assert_not_reached(); - } - tcg_val = new_tmp_a64(s); - tcg_gen_andi_i64(tcg_val, cpu_reg(s, rm), mask); - } - - tcg_acc = cpu_reg(s, rn); - tcg_bytes = tcg_constant_i32(1 << sz); - - if (crc32c) { - gen_helper_crc32c_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); - } else { - gen_helper_crc32_64(cpu_reg(s, rd), tcg_acc, tcg_val, tcg_bytes); - } -} - -/* Data-processing (2 source) - * 31 30 29 28 21 20 16 15 10 9 5 4 0 - * +----+---+---+-----------------+------+--------+------+------+ - * | sf | 0 | S | 1 1 0 1 0 1 1 0 | Rm | opcode | Rn | Rd | - * +----+---+---+-----------------+------+--------+------+------+ - */ -static void disas_data_proc_2src(DisasContext *s, uint32_t insn) -{ - unsigned int sf, rm, opcode, rn, rd, setflag; - sf = extract32(insn, 31, 1); - setflag = extract32(insn, 29, 1); - rm = extract32(insn, 16, 5); - opcode = extract32(insn, 10, 6); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (setflag && opcode != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0: /* SUBP(S) */ - if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } else { - TCGv_i64 tcg_n, tcg_m, tcg_d; - - tcg_n = read_cpu_reg_sp(s, rn, true); - tcg_m = read_cpu_reg_sp(s, rm, true); - tcg_gen_sextract_i64(tcg_n, tcg_n, 0, 56); - tcg_gen_sextract_i64(tcg_m, tcg_m, 0, 56); - tcg_d = cpu_reg(s, rd); - - if (setflag) { - gen_sub_CC(true, tcg_d, tcg_n, tcg_m); - } else { - tcg_gen_sub_i64(tcg_d, tcg_n, tcg_m); - } - } - break; - case 2: /* UDIV */ - handle_div(s, false, sf, rm, rn, rd); - break; - case 3: /* SDIV */ - handle_div(s, true, sf, rm, rn, rd); - break; - case 4: /* IRG */ - 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, - cpu_reg_sp(s, rn), cpu_reg(s, rm)); - } else { - gen_address_with_allocation_tag0(cpu_reg_sp(s, rd), - cpu_reg_sp(s, rn)); - } - break; - case 5: /* GMI */ - if (sf == 0 || !dc_isar_feature(aa64_mte_insn_reg, s)) { - goto do_unallocated; - } else { - TCGv_i64 t = tcg_temp_new_i64(); - - 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 */ - handle_shift_reg(s, A64_SHIFT_TYPE_LSL, sf, rm, rn, rd); - break; - case 9: /* LSRV */ - handle_shift_reg(s, A64_SHIFT_TYPE_LSR, sf, rm, rn, rd); - break; - case 10: /* ASRV */ - handle_shift_reg(s, A64_SHIFT_TYPE_ASR, sf, rm, rn, rd); - break; - case 11: /* RORV */ - handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd); - break; - case 12: /* PACGA */ - if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { - goto do_unallocated; - } - gen_helper_pacga(cpu_reg(s, rd), cpu_env, - cpu_reg(s, rn), cpu_reg_sp(s, rm)); - break; - case 16: - case 17: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: /* CRC32 */ - { - int sz = extract32(opcode, 0, 2); - bool crc32c = extract32(opcode, 2, 1); - handle_crc32(s, sf, sz, crc32c, rm, rn, rd); - break; - } - default: - do_unallocated: - unallocated_encoding(s); - break; - } -} - -/* - * Data processing - register - * 31 30 29 28 25 21 20 16 10 0 - * +--+---+--+---+-------+-----+-------+-------+---------+ - * | |op0| |op1| 1 0 1 | op2 | | op3 | | - * +--+---+--+---+-------+-----+-------+-------+---------+ - */ -static void disas_data_proc_reg(DisasContext *s, uint32_t insn) -{ - int op0 = extract32(insn, 30, 1); - int op1 = extract32(insn, 28, 1); - int op2 = extract32(insn, 21, 4); - int op3 = extract32(insn, 10, 6); - - if (!op1) { - if (op2 & 8) { - if (op2 & 1) { - /* Add/sub (extended register) */ - disas_add_sub_ext_reg(s, insn); - } else { - /* Add/sub (shifted register) */ - disas_add_sub_reg(s, insn); - } - } else { - /* Logical (shifted register) */ - disas_logic_reg(s, insn); - } - return; - } - - switch (op2) { - case 0x0: - switch (op3) { - case 0x00: /* Add/subtract (with carry) */ - disas_adc_sbc(s, insn); - break; - - case 0x01: /* Rotate right into flags */ - case 0x21: - disas_rotate_right_into_flags(s, insn); - break; - - case 0x02: /* Evaluate into flags */ - case 0x12: - case 0x22: - case 0x32: - disas_evaluate_into_flags(s, insn); - break; - - default: - goto do_unallocated; - } - break; - - case 0x2: /* Conditional compare */ - disas_cc(s, insn); /* both imm and reg forms */ - break; - - case 0x4: /* Conditional select */ - disas_cond_select(s, insn); - break; - - case 0x6: /* Data-processing */ - if (op0) { /* (1 source) */ - disas_data_proc_1src(s, insn); - } else { /* (2 source) */ - disas_data_proc_2src(s, insn); - } - break; - case 0x8 ... 0xf: /* (3 source) */ - disas_data_proc_3src(s, insn); - break; - - default: - do_unallocated: - unallocated_encoding(s); - break; - } -} - -static void handle_fp_compare(DisasContext *s, int size, - unsigned int rn, unsigned int rm, - bool cmp_with_zero, bool signal_all_nans) -{ - TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - - if (size == MO_64) { - TCGv_i64 tcg_vn, tcg_vm; - - tcg_vn = read_fp_dreg(s, rn); - if (cmp_with_zero) { - tcg_vm = tcg_constant_i64(0); - } else { - tcg_vm = read_fp_dreg(s, rm); - } - if (signal_all_nans) { - gen_helper_vfp_cmped_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } 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(); - - read_vec_element_i32(s, tcg_vn, rn, 0, size); - if (cmp_with_zero) { - tcg_gen_movi_i32(tcg_vm, 0); - } else { - read_vec_element_i32(s, tcg_vm, rm, 0, size); - } - - switch (size) { - case MO_32: - if (signal_all_nans) { - gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } - break; - case MO_16: - if (signal_all_nans) { - gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } - break; - 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 - * 31 30 29 28 24 23 22 21 20 16 15 14 13 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+-----+---------+------+-------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | op | 1 0 0 0 | Rn | op2 | - * +---+---+---+-----------+------+---+------+-----+---------+------+-------+ - */ -static void disas_fp_compare(DisasContext *s, uint32_t insn) -{ - unsigned int mos, type, rm, op, rn, opc, op2r; - int size; - - mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); - rm = extract32(insn, 16, 5); - op = extract32(insn, 14, 2); - rn = extract32(insn, 5, 5); - opc = extract32(insn, 3, 2); - op2r = extract32(insn, 0, 3); - - if (mos || op || op2r) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - size = MO_32; - break; - case 1: - size = MO_64; - break; - case 3: - size = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2); -} - -/* Floating point conditional compare - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 - * +---+---+---+-----------+------+---+------+------+-----+------+----+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 0 1 | Rn | op | nzcv | - * +---+---+---+-----------+------+---+------+------+-----+------+----+------+ - */ -static void disas_fp_ccomp(DisasContext *s, uint32_t insn) -{ - unsigned int mos, type, rm, cond, rn, op, nzcv; - TCGLabel *label_continue = NULL; - int size; - - mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); - rm = extract32(insn, 16, 5); - cond = extract32(insn, 12, 4); - rn = extract32(insn, 5, 5); - op = extract32(insn, 4, 1); - nzcv = extract32(insn, 0, 4); - - if (mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - size = MO_32; - break; - case 1: - size = MO_64; - break; - case 3: - size = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (cond < 0x0e) { /* not always */ - TCGLabel *label_match = gen_new_label(); - label_continue = gen_new_label(); - arm_gen_test_cc(cond, label_match); - /* nomatch: */ - gen_set_nzcv(tcg_constant_i64(nzcv << 28)); - tcg_gen_br(label_continue); - gen_set_label(label_match); - } - - handle_fp_compare(s, size, rn, rm, false, op); - - if (cond < 0x0e) { - gen_set_label(label_continue); - } -} - -/* Floating point conditional select - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+------+-----+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | cond | 1 1 | Rn | Rd | - * +---+---+---+-----------+------+---+------+------+-----+------+------+ - */ -static void disas_fp_csel(DisasContext *s, uint32_t insn) -{ - unsigned int mos, type, rm, cond, rn, rd; - TCGv_i64 t_true, t_false; - DisasCompare64 c; - MemOp sz; - - mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); - rm = extract32(insn, 16, 5); - cond = extract32(insn, 12, 4); - rn = extract32(insn, 5, 5); - rd = extract32(insn, 0, 5); - - if (mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - sz = MO_32; - break; - case 1: - sz = MO_64; - break; - case 3: - sz = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - /* Zero extend sreg & hreg inputs to 64 bits now. */ - t_true = tcg_temp_new_i64(); - t_false = tcg_temp_new_i64(); - read_vec_element(s, t_true, rn, 0, sz); - read_vec_element(s, t_false, rm, 0, sz); - - 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 */ -static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) -{ - TCGv_ptr fpst = NULL; - TCGv_i32 tcg_op = read_fp_hreg(s, rn); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - switch (opcode) { - case 0x0: /* FMOV */ - tcg_gen_mov_i32(tcg_res, tcg_op); - break; - case 0x1: /* FABS */ - tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); - break; - case 0x2: /* FNEG */ - tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); - break; - case 0x3: /* FSQRT */ - fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_sqrt_f16(tcg_res, tcg_op, fpst); - break; - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - { - TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); - fpst = fpstatus_ptr(FPST_FPCR_F16); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, 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); - break; - } - case 0xe: /* FRINTX */ - fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, fpst); - break; - case 0xf: /* FRINTI */ - fpst = fpstatus_ptr(FPST_FPCR_F16); - gen_helper_advsimd_rinth(tcg_res, tcg_op, fpst); - break; - default: - g_assert_not_reached(); - } - - 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 */ -static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) -{ - void (*gen_fpst)(TCGv_i32, TCGv_i32, TCGv_ptr); - TCGv_i32 tcg_op, tcg_res; - TCGv_ptr fpst; - int rmode = -1; - - tcg_op = read_fp_sreg(s, rn); - tcg_res = tcg_temp_new_i32(); - - switch (opcode) { - case 0x0: /* FMOV */ - tcg_gen_mov_i32(tcg_res, tcg_op); - goto done; - case 0x1: /* FABS */ - gen_helper_vfp_abss(tcg_res, tcg_op); - goto done; - case 0x2: /* FNEG */ - gen_helper_vfp_negs(tcg_res, tcg_op); - goto done; - case 0x3: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env); - goto done; - case 0x6: /* BFCVT */ - gen_fpst = gen_helper_bfcvt; - break; - case 0x8: /* FRINTN */ - case 0x9: /* FRINTP */ - case 0xa: /* FRINTM */ - case 0xb: /* FRINTZ */ - case 0xc: /* FRINTA */ - rmode = arm_rmode_to_sf(opcode & 7); - gen_fpst = gen_helper_rints; - break; - case 0xe: /* FRINTX */ - gen_fpst = gen_helper_rints_exact; - break; - case 0xf: /* FRINTI */ - gen_fpst = gen_helper_rints; - break; - case 0x10: /* FRINT32Z */ - rmode = float_round_to_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; - gen_fpst = gen_helper_frint64_s; - break; - case 0x13: /* FRINT64X */ - gen_fpst = gen_helper_frint64_s; - break; - default: - g_assert_not_reached(); - } - - 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); - gen_fpst(tcg_res, tcg_op, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); - } 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 */ -static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) -{ - void (*gen_fpst)(TCGv_i64, TCGv_i64, TCGv_ptr); - TCGv_i64 tcg_op, tcg_res; - TCGv_ptr fpst; - int rmode = -1; - - switch (opcode) { - case 0x0: /* FMOV */ - gen_gvec_fn2(s, false, rd, rn, tcg_gen_gvec_mov, 0); - return; - } - - tcg_op = read_fp_dreg(s, rn); - tcg_res = tcg_temp_new_i64(); - - switch (opcode) { - case 0x1: /* FABS */ - gen_helper_vfp_absd(tcg_res, tcg_op); - goto done; - case 0x2: /* FNEG */ - gen_helper_vfp_negd(tcg_res, tcg_op); - goto done; - case 0x3: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_res, tcg_op, cpu_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); - gen_fpst = gen_helper_rintd; - break; - case 0xe: /* FRINTX */ - gen_fpst = gen_helper_rintd_exact; - break; - case 0xf: /* FRINTI */ - gen_fpst = gen_helper_rintd; - break; - case 0x10: /* FRINT32Z */ - rmode = float_round_to_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; - gen_fpst = gen_helper_frint64_d; - break; - case 0x13: /* FRINT64X */ - gen_fpst = gen_helper_frint64_d; - break; - default: - g_assert_not_reached(); - } - - 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); - gen_fpst(tcg_res, tcg_op, fpst); - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); - } 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, - int rd, int rn, int dtype, int ntype) -{ - switch (ntype) { - case 0x0: - { - TCGv_i32 tcg_rn = read_fp_sreg(s, rn); - if (dtype == 1) { - /* Single to double */ - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvtds(tcg_rd, tcg_rn, cpu_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(); - TCGv_i32 ahp = get_ahp_flag(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - 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: - { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - if (dtype == 0) { - /* Double to single */ - gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, cpu_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: - { - TCGv_i32 tcg_rn = read_fp_sreg(s, rn); - TCGv_ptr tcg_fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 tcg_ahp = get_ahp_flag(); - tcg_gen_ext16u_i32(tcg_rn, tcg_rn); - if (dtype == 0) { - /* Half to single */ - 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: - g_assert_not_reached(); - } -} - -/* Floating point data-processing (1 source) - * 31 30 29 28 24 23 22 21 20 15 14 10 9 5 4 0 - * +---+---+---+-----------+------+---+--------+-----------+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | opcode | 1 0 0 0 0 | Rn | Rd | - * +---+---+---+-----------+------+---+--------+-----------+------+------+ - */ -static void disas_fp_1src(DisasContext *s, uint32_t insn) -{ - int mos = extract32(insn, 29, 3); - int type = extract32(insn, 22, 2); - int opcode = extract32(insn, 15, 6); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (mos) { - goto do_unallocated; - } - - switch (opcode) { - case 0x4: case 0x5: case 0x7: - { - /* FCVT between half, single and double precision */ - int dtype = extract32(opcode, 0, 2); - if (type == 2 || dtype == type) { - goto do_unallocated; - } - if (!fp_access_check(s)) { - return; - } - - handle_fp_fcvt(s, opcode, rd, rn, dtype, type); - break; - } - - case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */ - if (type > 1 || !dc_isar_feature(aa64_frint, s)) { - goto do_unallocated; - } - /* fall through */ - case 0x0 ... 0x3: - case 0x8 ... 0xc: - case 0xe ... 0xf: - /* 32-to-32 and 64-to-64 ops */ - switch (type) { - case 0: - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_single(s, opcode, rd, rn); - break; - case 1: - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_double(s, opcode, rd, rn); - break; - case 3: - if (!dc_isar_feature(aa64_fp16, s)) { - goto do_unallocated; - } - - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_half(s, opcode, rd, rn); - break; - default: - goto do_unallocated; - } - break; - - case 0x6: - switch (type) { - case 1: /* BFCVT */ - if (!dc_isar_feature(aa64_bf16, s)) { - goto do_unallocated; - } - if (!fp_access_check(s)) { - return; - } - handle_fp_1src_single(s, opcode, rd, rn); - break; - default: - goto do_unallocated; - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - break; - } -} - -/* Floating-point data-processing (2 source) - single precision */ -static void handle_fp_2src_single(DisasContext *s, int opcode, - int rd, int rn, int rm) -{ - TCGv_i32 tcg_op1; - TCGv_i32 tcg_op2; - TCGv_i32 tcg_res; - TCGv_ptr fpst; - - tcg_res = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR); - tcg_op1 = read_fp_sreg(s, rn); - tcg_op2 = read_fp_sreg(s, rm); - - switch (opcode) { - case 0x0: /* FMUL */ - gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FDIV */ - gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2: /* FADD */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FSUB */ - gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x4: /* FMAX */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5: /* FMIN */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAXNM */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FMINNM */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x8: /* FNMUL */ - gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_negs(tcg_res, tcg_res); - break; - } - - 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 */ -static void handle_fp_2src_double(DisasContext *s, int opcode, - int rd, int rn, int rm) -{ - TCGv_i64 tcg_op1; - TCGv_i64 tcg_op2; - TCGv_i64 tcg_res; - TCGv_ptr fpst; - - tcg_res = tcg_temp_new_i64(); - fpst = fpstatus_ptr(FPST_FPCR); - tcg_op1 = read_fp_dreg(s, rn); - tcg_op2 = read_fp_dreg(s, rm); - - switch (opcode) { - case 0x0: /* FMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FDIV */ - gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2: /* FADD */ - gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FSUB */ - gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x4: /* FMAX */ - gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5: /* FMIN */ - gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAXNM */ - gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FMINNM */ - gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x8: /* FNMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_negd(tcg_res, tcg_res); - break; - } - - 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 */ -static void handle_fp_2src_half(DisasContext *s, int opcode, - int rd, int rn, int rm) -{ - TCGv_i32 tcg_op1; - TCGv_i32 tcg_op2; - TCGv_i32 tcg_res; - TCGv_ptr fpst; - - tcg_res = tcg_temp_new_i32(); - fpst = fpstatus_ptr(FPST_FPCR_F16); - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - - switch (opcode) { - case 0x0: /* FMUL */ - gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FDIV */ - gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2: /* FADD */ - gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FSUB */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x4: /* FMAX */ - gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5: /* FMIN */ - gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAXNM */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FMINNM */ - gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x8: /* FNMUL */ - gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000); - break; - default: - g_assert_not_reached(); - } - - 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) - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | Rm | opcode | 1 0 | Rn | Rd | - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - */ -static void disas_fp_2src(DisasContext *s, uint32_t insn) -{ - int mos = extract32(insn, 29, 3); - int type = extract32(insn, 22, 2); - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int opcode = extract32(insn, 12, 4); - - if (opcode > 8 || mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - if (!fp_access_check(s)) { - return; - } - handle_fp_2src_single(s, opcode, rd, rn, rm); - break; - case 1: - if (!fp_access_check(s)) { - return; - } - handle_fp_2src_double(s, opcode, rd, rn, rm); - break; - case 3: - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_fp_2src_half(s, opcode, rd, rn, rm); - break; - default: - unallocated_encoding(s); - } -} - -/* Floating-point data-processing (3 source) - single precision */ -static void handle_fp_3src_single(DisasContext *s, bool o0, bool o1, - int rd, int rn, int rm, int ra) -{ - TCGv_i32 tcg_op1, tcg_op2, tcg_op3; - TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - tcg_op1 = read_fp_sreg(s, rn); - tcg_op2 = read_fp_sreg(s, rm); - tcg_op3 = read_fp_sreg(s, ra); - - /* These are fused multiply-add, and must be done as one - * floating point operation with no rounding between the - * multiplication and addition steps. - * NB that doing the negations here as separate steps is - * correct : an input NaN should come out with its sign bit - * flipped if it is a negated-input. - */ - if (o1 == true) { - gen_helper_vfp_negs(tcg_op3, tcg_op3); - } - - if (o0 != o1) { - gen_helper_vfp_negs(tcg_op1, tcg_op1); - } - - 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 */ -static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, - int rd, int rn, int rm, int ra) -{ - TCGv_i64 tcg_op1, tcg_op2, tcg_op3; - TCGv_i64 tcg_res = tcg_temp_new_i64(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - tcg_op1 = read_fp_dreg(s, rn); - tcg_op2 = read_fp_dreg(s, rm); - tcg_op3 = read_fp_dreg(s, ra); - - /* These are fused multiply-add, and must be done as one - * floating point operation with no rounding between the - * multiplication and addition steps. - * NB that doing the negations here as separate steps is - * correct : an input NaN should come out with its sign bit - * flipped if it is a negated-input. - */ - if (o1 == true) { - gen_helper_vfp_negd(tcg_op3, tcg_op3); - } - - if (o0 != o1) { - gen_helper_vfp_negd(tcg_op1, tcg_op1); - } - - 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 */ -static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1, - int rd, int rn, int rm, int ra) -{ - TCGv_i32 tcg_op1, tcg_op2, tcg_op3; - TCGv_i32 tcg_res = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR_F16); - - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - tcg_op3 = read_fp_hreg(s, ra); - - /* These are fused multiply-add, and must be done as one - * floating point operation with no rounding between the - * multiplication and addition steps. - * NB that doing the negations here as separate steps is - * correct : an input NaN should come out with its sign bit - * flipped if it is a negated-input. - */ - if (o1 == true) { - tcg_gen_xori_i32(tcg_op3, tcg_op3, 0x8000); - } - - if (o0 != o1) { - tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); - } - - 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) - * 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0 - * +---+---+---+-----------+------+----+------+----+------+------+------+ - * | M | 0 | S | 1 1 1 1 1 | type | o1 | Rm | o0 | Ra | Rn | Rd | - * +---+---+---+-----------+------+----+------+----+------+------+------+ - */ -static void disas_fp_3src(DisasContext *s, uint32_t insn) -{ - int mos = extract32(insn, 29, 3); - int type = extract32(insn, 22, 2); - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int ra = extract32(insn, 10, 5); - int rm = extract32(insn, 16, 5); - bool o0 = extract32(insn, 15, 1); - bool o1 = extract32(insn, 21, 1); - - if (mos) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - if (!fp_access_check(s)) { - return; - } - handle_fp_3src_single(s, o0, o1, rd, rn, rm, ra); - break; - case 1: - if (!fp_access_check(s)) { - return; - } - handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); - break; - case 3: - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_fp_3src_half(s, o0, o1, rd, rn, rm, ra); - break; - default: - unallocated_encoding(s); - } -} - -/* Floating point immediate - * 31 30 29 28 24 23 22 21 20 13 12 10 9 5 4 0 - * +---+---+---+-----------+------+---+------------+-------+------+------+ - * | M | 0 | S | 1 1 1 1 0 | type | 1 | imm8 | 1 0 0 | imm5 | Rd | - * +---+---+---+-----------+------+---+------------+-------+------+------+ - */ -static void disas_fp_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int imm5 = extract32(insn, 5, 5); - int imm8 = extract32(insn, 13, 8); - int type = extract32(insn, 22, 2); - int mos = extract32(insn, 29, 3); - uint64_t imm; - MemOp sz; - - if (mos || imm5) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: - sz = MO_32; - break; - case 1: - sz = MO_64; - break; - case 3: - sz = MO_16; - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - imm = vfp_expand_imm(sz, imm8); - write_fp_dreg(s, rd, tcg_constant_i64(imm)); -} - -/* Handle floating point <=> fixed point conversions. Note that we can - * also deal with fp <=> integer conversions as a special case (scale == 64) - * OPTME: consider handling that special case specially or at least skipping - * the call to scalbn in the helpers for zero shifts. - */ -static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, - bool itof, int rmode, int scale, int sf, int type) -{ - bool is_signed = !(opcode & 1); - TCGv_ptr tcg_fpstatus; - TCGv_i32 tcg_shift, tcg_single; - TCGv_i64 tcg_double; - - tcg_fpstatus = fpstatus_ptr(type == 3 ? FPST_FPCR_F16 : FPST_FPCR); - - tcg_shift = tcg_constant_i32(64 - scale); - - if (itof) { - TCGv_i64 tcg_int = cpu_reg(s, rn); - if (!sf) { - TCGv_i64 tcg_extend = new_tmp_a64(s); - - if (is_signed) { - tcg_gen_ext32s_i64(tcg_extend, tcg_int); - } else { - tcg_gen_ext32u_i64(tcg_extend, tcg_int); - } - - tcg_int = tcg_extend; - } - - switch (type) { - case 1: /* float64 */ - tcg_double = tcg_temp_new_i64(); - if (is_signed) { - gen_helper_vfp_sqtod(tcg_double, tcg_int, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_uqtod(tcg_double, tcg_int, - tcg_shift, tcg_fpstatus); - } - write_fp_dreg(s, rd, tcg_double); - tcg_temp_free_i64(tcg_double); - break; - - case 0: /* float32 */ - tcg_single = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_sqtos(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_uqtos(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } - write_fp_sreg(s, rd, tcg_single); - tcg_temp_free_i32(tcg_single); - break; - - case 3: /* float16 */ - tcg_single = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_sqtoh(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_uqtoh(tcg_single, tcg_int, - tcg_shift, tcg_fpstatus); - } - write_fp_sreg(s, rd, tcg_single); - tcg_temp_free_i32(tcg_single); - break; - - default: - g_assert_not_reached(); - } - } else { - TCGv_i64 tcg_int = cpu_reg(s, rd); - TCGv_i32 tcg_rmode; - - if (extract32(opcode, 2, 1)) { - /* There are too many rounding modes to all fit into rmode, - * so FCVTA[US] is a special case. - */ - rmode = FPROUNDING_TIEAWAY; - } - - tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode)); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - - switch (type) { - case 1: /* float64 */ - tcg_double = read_fp_dreg(s, rn); - if (is_signed) { - if (!sf) { - gen_helper_vfp_tosld(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_tosqd(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } - } else { - if (!sf) { - gen_helper_vfp_tould(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touqd(tcg_int, tcg_double, - tcg_shift, tcg_fpstatus); - } - } - if (!sf) { - tcg_gen_ext32u_i64(tcg_int, tcg_int); - } - tcg_temp_free_i64(tcg_double); - break; - - case 0: /* float32 */ - tcg_single = read_fp_sreg(s, rn); - if (sf) { - if (is_signed) { - gen_helper_vfp_tosqs(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touqs(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } - } else { - TCGv_i32 tcg_dest = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_tosls(tcg_dest, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touls(tcg_dest, tcg_single, - 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 */ - tcg_single = read_fp_sreg(s, rn); - if (sf) { - if (is_signed) { - gen_helper_vfp_tosqh(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_touqh(tcg_int, tcg_single, - tcg_shift, tcg_fpstatus); - } - } else { - TCGv_i32 tcg_dest = tcg_temp_new_i32(); - if (is_signed) { - gen_helper_vfp_toslh(tcg_dest, tcg_single, - tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_toulh(tcg_dest, tcg_single, - 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); - } - - tcg_temp_free_ptr(tcg_fpstatus); -} - -/* Floating point <-> fixed point conversions - * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 - * +----+---+---+-----------+------+---+-------+--------+-------+------+------+ - * | sf | 0 | S | 1 1 1 1 0 | type | 0 | rmode | opcode | scale | Rn | Rd | - * +----+---+---+-----------+------+---+-------+--------+-------+------+------+ - */ -static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int scale = extract32(insn, 10, 6); - int opcode = extract32(insn, 16, 3); - int rmode = extract32(insn, 19, 2); - int type = extract32(insn, 22, 2); - bool sbit = extract32(insn, 29, 1); - bool sf = extract32(insn, 31, 1); - bool itof; - - if (sbit || (!sf && scale < 32)) { - unallocated_encoding(s); - return; - } - - switch (type) { - case 0: /* float32 */ - case 1: /* float64 */ - break; - case 3: /* float16 */ - if (dc_isar_feature(aa64_fp16, s)) { - break; - } - /* fallthru */ - default: - unallocated_encoding(s); - return; - } - - switch ((rmode << 3) | opcode) { - case 0x2: /* SCVTF */ - case 0x3: /* UCVTF */ - itof = true; - break; - case 0x18: /* FCVTZS */ - case 0x19: /* FCVTZU */ - itof = false; - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - handle_fpfpcvt(s, rd, rn, opcode, itof, FPROUNDING_ZERO, scale, sf, type); -} - -static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) -{ - /* FMOV: gpr to or from float, double, or top half of quad fp reg, - * without conversion. - */ - - if (itof) { - TCGv_i64 tcg_rn = cpu_reg(s, rn); - TCGv_i64 tmp; - - switch (type) { - case 0: - /* 32 bit */ - 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 */ - write_fp_dreg(s, rd, tcg_rn); - break; - case 2: - /* 64 bit to top half. */ - tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd)); - clear_vec_high(s, true, rd); - break; - case 3: - /* 16 bit */ - 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(); - } - } else { - TCGv_i64 tcg_rd = cpu_reg(s, rd); - - switch (type) { - case 0: - /* 32 bit */ - tcg_gen_ld32u_i64(tcg_rd, cpu_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)); - break; - case 2: - /* 64 bits from top half */ - tcg_gen_ld_i64(tcg_rd, cpu_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)); - break; - default: - g_assert_not_reached(); - } - } -} - -static void handle_fjcvtzs(DisasContext *s, int rd, int rn) -{ - TCGv_i64 t = read_fp_dreg(s, rn); - TCGv_ptr fpstatus = fpstatus_ptr(FPST_FPCR); - - 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 - * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 - * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ - * | sf | 0 | S | 1 1 1 1 0 | type | 1 | rmode | opc | 0 0 0 0 0 0 | Rn | Rd | - * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ - */ -static void disas_fp_int_conv(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 16, 3); - int rmode = extract32(insn, 19, 2); - int type = extract32(insn, 22, 2); - bool sbit = extract32(insn, 29, 1); - bool sf = extract32(insn, 31, 1); - bool itof = false; - - if (sbit) { - goto do_unallocated; - } - - switch (opcode) { - case 2: /* SCVTF */ - case 3: /* UCVTF */ - itof = true; - /* fallthru */ - case 4: /* FCVTAS */ - case 5: /* FCVTAU */ - if (rmode != 0) { - goto do_unallocated; - } - /* fallthru */ - case 0: /* FCVT[NPMZ]S */ - case 1: /* FCVT[NPMZ]U */ - switch (type) { - case 0: /* float32 */ - case 1: /* float64 */ - break; - case 3: /* float16 */ - if (!dc_isar_feature(aa64_fp16, s)) { - goto do_unallocated; - } - break; - default: - goto do_unallocated; - } - if (!fp_access_check(s)) { - return; - } - handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type); - break; - - default: - switch (sf << 7 | type << 5 | rmode << 3 | opcode) { - case 0b01100110: /* FMOV half <-> 32-bit int */ - case 0b01100111: - case 0b11100110: /* FMOV half <-> 64-bit int */ - case 0b11100111: - if (!dc_isar_feature(aa64_fp16, s)) { - goto do_unallocated; - } - /* fallthru */ - case 0b00000110: /* FMOV 32-bit */ - case 0b00000111: - case 0b10100110: /* FMOV 64-bit */ - case 0b10100111: - case 0b11001110: /* FMOV top half of 128-bit */ - case 0b11001111: - if (!fp_access_check(s)) { - return; - } - itof = opcode & 1; - handle_fmov(s, rd, rn, type, itof); - break; - - case 0b00111110: /* FJCVTZS */ - if (!dc_isar_feature(aa64_jscvt, s)) { - goto do_unallocated; - } else if (fp_access_check(s)) { - handle_fjcvtzs(s, rd, rn); - } - break; - - default: - do_unallocated: - unallocated_encoding(s); - return; - } - break; - } -} - -/* FP-specific subcases of table C3-6 (SIMD and FP data processing) - * 31 30 29 28 25 24 0 - * +---+---+---+---------+-----------------------------+ - * | | 0 | | 1 1 1 1 | | - * +---+---+---+---------+-----------------------------+ - */ -static void disas_data_proc_fp(DisasContext *s, uint32_t insn) -{ - if (extract32(insn, 24, 1)) { - /* Floating point data-processing (3 source) */ - disas_fp_3src(s, insn); - } else if (extract32(insn, 21, 1) == 0) { - /* Floating point to fixed point conversions */ - disas_fp_fixed_conv(s, insn); - } else { - switch (extract32(insn, 10, 2)) { - case 1: - /* Floating point conditional compare */ - disas_fp_ccomp(s, insn); - break; - case 2: - /* Floating point data-processing (2 source) */ - disas_fp_2src(s, insn); - break; - case 3: - /* Floating point conditional select */ - disas_fp_csel(s, insn); - break; - case 0: - switch (ctz32(extract32(insn, 12, 4))) { - case 0: /* [15:12] == xxx1 */ - /* Floating point immediate */ - disas_fp_imm(s, insn); - break; - case 1: /* [15:12] == xx10 */ - /* Floating point compare */ - disas_fp_compare(s, insn); - break; - case 2: /* [15:12] == x100 */ - /* Floating point data-processing (1 source) */ - disas_fp_1src(s, insn); - break; - case 3: /* [15:12] == 1000 */ - unallocated_encoding(s); - break; - default: /* [15:12] == 0000 */ - /* Floating point <-> integer conversions */ - disas_fp_int_conv(s, insn); - break; - } - break; - } - } -} - -static void do_ext64(DisasContext *s, TCGv_i64 tcg_left, TCGv_i64 tcg_right, - int pos) -{ - /* Extract 64 bits from the middle of two concatenated 64 bit - * vector register slices left:right. The extracted bits start - * at 'pos' bits into the right (least significant) side. - * We return the result in tcg_right, and guarantee not to - * trash tcg_left. - */ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - assert(pos > 0 && pos < 64); - - 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 - * 31 30 29 24 23 22 21 20 16 15 14 11 10 9 5 4 0 - * +---+---+-------------+-----+---+------+---+------+---+------+------+ - * | 0 | Q | 1 0 1 1 1 0 | op2 | 0 | Rm | 0 | imm4 | 0 | Rn | Rd | - * +---+---+-------------+-----+---+------+---+------+---+------+------+ - */ -static void disas_simd_ext(DisasContext *s, uint32_t insn) -{ - int is_q = extract32(insn, 30, 1); - int op2 = extract32(insn, 22, 2); - int imm4 = extract32(insn, 11, 4); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - int pos = imm4 << 3; - TCGv_i64 tcg_resl, tcg_resh; - - if (op2 != 0 || (!is_q && extract32(imm4, 3, 1))) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_resh = tcg_temp_new_i64(); - tcg_resl = tcg_temp_new_i64(); - - /* Vd gets bits starting at pos bits into Vm:Vn. This is - * either extracting 128 bits from a 128:128 concatenation, or - * extracting 64 bits from a 64:64 concatenation. - */ - if (!is_q) { - read_vec_element(s, tcg_resl, rn, 0, MO_64); - if (pos != 0) { - read_vec_element(s, tcg_resh, rm, 0, MO_64); - do_ext64(s, tcg_resh, tcg_resl, pos); - } - } else { - TCGv_i64 tcg_hh; - typedef struct { - int reg; - int elt; - } EltPosns; - EltPosns eltposns[] = { {rn, 0}, {rn, 1}, {rm, 0}, {rm, 1} }; - EltPosns *elt = eltposns; - - if (pos >= 64) { - elt++; - pos -= 64; - } - - read_vec_element(s, tcg_resl, elt->reg, elt->elt, MO_64); - elt++; - read_vec_element(s, tcg_resh, elt->reg, elt->elt, MO_64); - elt++; - if (pos != 0) { - do_ext64(s, tcg_resh, tcg_resl, pos); - 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); -} - -/* TBL/TBX - * 31 30 29 24 23 22 21 20 16 15 14 13 12 11 10 9 5 4 0 - * +---+---+-------------+-----+---+------+---+-----+----+-----+------+------+ - * | 0 | Q | 0 0 1 1 1 0 | op2 | 0 | Rm | 0 | len | op | 0 0 | Rn | Rd | - * +---+---+-------------+-----+---+------+---+-----+----+-----+------+------+ - */ -static void disas_simd_tb(DisasContext *s, uint32_t insn) -{ - int op2 = extract32(insn, 22, 2); - int is_q = extract32(insn, 30, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - int is_tbx = extract32(insn, 12, 1); - int len = (extract32(insn, 13, 2) + 1) * 16; - - if (op2 != 0) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rm), cpu_env, - is_q ? 16 : 8, vec_full_reg_size(s), - (len << 6) | (is_tbx << 5) | rn, - gen_helper_simd_tblx); -} - -/* ZIP/UZP/TRN - * 31 30 29 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0 - * +---+---+-------------+------+---+------+---+------------------+------+ - * | 0 | Q | 0 0 1 1 1 0 | size | 0 | Rm | 0 | opc | 1 0 | Rn | Rd | - * +---+---+-------------+------+---+------+---+------------------+------+ - */ -static void disas_simd_zip_trn(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - /* opc field bits [1:0] indicate ZIP/UZP/TRN; - * bit 2 indicates 1 vs 2 variant of the insn. - */ - int opcode = extract32(insn, 12, 2); - bool part = extract32(insn, 14, 1); - bool is_q = extract32(insn, 30, 1); - int esize = 8 << size; - int i, ofs; - int datasize = is_q ? 128 : 64; - int elements = datasize / esize; - TCGv_i64 tcg_res, tcg_resl, tcg_resh; - - if (opcode == 0 || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_resl = tcg_const_i64(0); - tcg_resh = is_q ? tcg_const_i64(0) : NULL; - tcg_res = tcg_temp_new_i64(); - - for (i = 0; i < elements; i++) { - switch (opcode) { - case 1: /* UZP1/2 */ - { - int midpoint = elements / 2; - if (i < midpoint) { - read_vec_element(s, tcg_res, rn, 2 * i + part, size); - } else { - read_vec_element(s, tcg_res, 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); - } else { - read_vec_element(s, tcg_res, 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); - } else { - read_vec_element(s, tcg_res, rn, base + (i >> 1), size); - } - break; - } - default: - 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); - } else { - tcg_gen_shli_i64(tcg_res, tcg_res, ofs - 64); - tcg_gen_or_i64(tcg_resh, tcg_resh, tcg_res); - } - } - - 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); - } - clear_vec_high(s, is_q, rd); -} - -/* - * do_reduction_op helper - * - * This mirrors the Reduce() pseudocode in the ARM ARM. It is - * important for correct NaN propagation that we do these - * operations in exactly the order specified by the pseudocode. - * - * This is a recursive function, TCG temps should be freed by the - * calling function once it is done with the values. - */ -static TCGv_i32 do_reduction_op(DisasContext *s, int fpopcode, int rn, - int esize, int size, int vmap, TCGv_ptr fpst) -{ - if (esize == size) { - int element; - MemOp msize = esize == 16 ? MO_16 : MO_32; - TCGv_i32 tcg_elem; - - /* We should have one register left here */ - assert(ctpop8(vmap) == 1); - element = ctz32(vmap); - assert(element < 8); - - tcg_elem = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_elem, rn, element, msize); - return tcg_elem; - } else { - int bits = size / 2; - int shift = ctpop8(vmap) / 2; - int vmap_lo = (vmap >> shift) & vmap; - int vmap_hi = (vmap & ~vmap_lo); - TCGv_i32 tcg_hi, tcg_lo, tcg_res; - - tcg_hi = do_reduction_op(s, fpopcode, rn, esize, bits, vmap_hi, fpst); - tcg_lo = do_reduction_op(s, fpopcode, rn, esize, bits, vmap_lo, fpst); - tcg_res = tcg_temp_new_i32(); - - switch (fpopcode) { - case 0x0c: /* fmaxnmv half-precision */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x0f: /* fmaxv half-precision */ - gen_helper_advsimd_maxh(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x1c: /* fminnmv half-precision */ - gen_helper_advsimd_minnumh(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x1f: /* fminv half-precision */ - gen_helper_advsimd_minh(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x2c: /* fmaxnmv */ - gen_helper_vfp_maxnums(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x2f: /* fmaxv */ - gen_helper_vfp_maxs(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x3c: /* fminnmv */ - gen_helper_vfp_minnums(tcg_res, tcg_lo, tcg_hi, fpst); - break; - case 0x3f: /* fminv */ - gen_helper_vfp_mins(tcg_res, tcg_lo, tcg_hi, fpst); - break; - default: - g_assert_not_reached(); - } - - tcg_temp_free_i32(tcg_hi); - tcg_temp_free_i32(tcg_lo); - return tcg_res; - } -} - -/* AdvSIMD across lanes - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd | - * +---+---+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - bool is_q = extract32(insn, 30, 1); - bool is_u = extract32(insn, 29, 1); - bool is_fp = false; - bool is_min = false; - int esize; - int elements; - int i; - TCGv_i64 tcg_res, tcg_elt; - - switch (opcode) { - case 0x1b: /* ADDV */ - if (is_u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x3: /* SADDLV, UADDLV */ - case 0xa: /* SMAXV, UMAXV */ - case 0x1a: /* SMINV, UMINV */ - if (size == 3 || (size == 2 && !is_q)) { - unallocated_encoding(s); - return; - } - break; - case 0xc: /* FMAXNMV, FMINNMV */ - case 0xf: /* FMAXV, FMINV */ - /* Bit 1 of size field encodes min vs max and the actual size - * depends on the encoding of the U bit. If not set (and FP16 - * enabled) then we do half-precision float instead of single - * precision. - */ - is_min = extract32(size, 1, 1); - is_fp = true; - if (!is_u && dc_isar_feature(aa64_fp16, s)) { - size = 1; - } else if (!is_u || !is_q || extract32(size, 0, 1)) { - unallocated_encoding(s); - return; - } else { - size = 2; - } - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - esize = 8 << size; - elements = (is_q ? 128 : 64) / esize; - - tcg_res = tcg_temp_new_i64(); - tcg_elt = tcg_temp_new_i64(); - - /* These instructions operate across all lanes of a vector - * to produce a single result. We can guarantee that a 64 - * bit intermediate is sufficient: - * + for [US]ADDLV the maximum element size is 32 bits, and - * the result type is 64 bits - * + for FMAX*V, FMIN*V, ADDV the intermediate type is the - * same as the element size, which is 32 bits at most - * For the integer operations we can choose to work at 64 - * or 32 bits and truncate at the end; for simplicity - * we use 64 bits always. The floating point - * ops do require 32 bit intermediates, though. - */ - if (!is_fp) { - read_vec_element(s, tcg_res, rn, 0, size | (is_u ? 0 : MO_SIGN)); - - for (i = 1; i < elements; i++) { - read_vec_element(s, tcg_elt, rn, i, size | (is_u ? 0 : MO_SIGN)); - - switch (opcode) { - case 0x03: /* SADDLV / UADDLV */ - case 0x1b: /* ADDV */ - tcg_gen_add_i64(tcg_res, tcg_res, tcg_elt); - break; - case 0x0a: /* SMAXV / UMAXV */ - if (is_u) { - tcg_gen_umax_i64(tcg_res, tcg_res, tcg_elt); - } else { - tcg_gen_smax_i64(tcg_res, tcg_res, tcg_elt); - } - break; - case 0x1a: /* SMINV / UMINV */ - if (is_u) { - tcg_gen_umin_i64(tcg_res, tcg_res, tcg_elt); - } else { - tcg_gen_smin_i64(tcg_res, tcg_res, tcg_elt); - } - break; - default: - g_assert_not_reached(); - } - - } - } else { - /* Floating point vector reduction ops which work across 32 - * bit (single) or 16 bit (half-precision) intermediates. - * Note that correct NaN propagation requires that we do these - * operations in exactly the order specified by the pseudocode. - */ - TCGv_ptr fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - int fpopcode = opcode | is_min << 4 | is_u << 5; - int vmap = (1 << elements) - 1; - 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 */ - size++; - } - - switch (size) { - case 0: - tcg_gen_ext8u_i64(tcg_res, tcg_res); - break; - case 1: - tcg_gen_ext16u_i64(tcg_res, tcg_res); - break; - case 2: - tcg_gen_ext32u_i64(tcg_res, tcg_res); - break; - case 3: - break; - default: - g_assert_not_reached(); - } - - write_fp_dreg(s, rd, tcg_res); - tcg_temp_free_i64(tcg_res); -} - -/* DUP (Element, Vector) - * - * 31 30 29 21 20 16 15 10 9 5 4 0 - * +---+---+-------------------+--------+-------------+------+------+ - * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd | - * +---+---+-------------------+--------+-------------+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - */ -static void handle_simd_dupe(DisasContext *s, int is_q, int rd, int rn, - int imm5) -{ - int size = ctz32(imm5); - int index; - - if (size > 3 || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - index = imm5 >> (size + 1); - tcg_gen_gvec_dup_mem(size, vec_full_reg_offset(s, rd), - vec_reg_offset(s, rn, index, size), - is_q ? 16 : 8, vec_full_reg_size(s)); -} - -/* DUP (element, scalar) - * 31 21 20 16 15 10 9 5 4 0 - * +-----------------------+--------+-------------+------+------+ - * | 0 1 0 1 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 0 1 | Rn | Rd | - * +-----------------------+--------+-------------+------+------+ - */ -static void handle_simd_dupes(DisasContext *s, int rd, int rn, - int imm5) -{ - int size = ctz32(imm5); - int index; - TCGv_i64 tmp; - - if (size > 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - index = imm5 >> (size + 1); - - /* This instruction just extracts the specified element and - * zero-extends it into the bottom of the destination register. - */ - 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) - * - * 31 30 29 21 20 16 15 10 9 5 4 0 - * +---+---+-------------------+--------+-------------+------+------+ - * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 0 1 1 | Rn | Rd | - * +---+---+-------------------+--------+-------------+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - */ -static void handle_simd_dupg(DisasContext *s, int is_q, int rd, int rn, - int imm5) -{ - int size = ctz32(imm5); - uint32_t dofs, oprsz, maxsz; - - if (size > 3 || ((size == 3) && !is_q)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - dofs = vec_full_reg_offset(s, rd); - oprsz = is_q ? 16 : 8; - maxsz = vec_full_reg_size(s); - - tcg_gen_gvec_dup_i64(size, dofs, oprsz, maxsz, cpu_reg(s, rn)); -} - -/* INS (Element) - * - * 31 21 20 16 15 14 11 10 9 5 4 0 - * +-----------------------+--------+------------+---+------+------+ - * | 0 1 1 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd | - * +-----------------------+--------+------------+---+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - * index: encoded in imm5<4:size+1> - */ -static void handle_simd_inse(DisasContext *s, int rd, int rn, - int imm4, int imm5) -{ - int size = ctz32(imm5); - int src_index, dst_index; - TCGv_i64 tmp; - - if (size > 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - dst_index = extract32(imm5, 1+size, 5); - src_index = extract32(imm4, size, 4); - - tmp = tcg_temp_new_i64(); - - 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); -} - - -/* INS (General) - * - * 31 21 20 16 15 10 9 5 4 0 - * +-----------------------+--------+-------------+------+------+ - * | 0 1 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 0 1 1 1 | Rn | Rd | - * +-----------------------+--------+-------------+------+------+ - * - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - * index: encoded in imm5<4:size+1> - */ -static void handle_simd_insg(DisasContext *s, int rd, int rn, int imm5) -{ - int size = ctz32(imm5); - int idx; - - if (size > 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - idx = extract32(imm5, 1 + size, 4 - size); - write_vec_element(s, cpu_reg(s, rn), rd, idx, size); - - /* INS is considered a 128-bit write for SVE. */ - clear_vec_high(s, true, rd); -} - -/* - * UMOV (General) - * SMOV (General) - * - * 31 30 29 21 20 16 15 12 10 9 5 4 0 - * +---+---+-------------------+--------+-------------+------+------+ - * | 0 | Q | 0 0 1 1 1 0 0 0 0 | imm5 | 0 0 1 U 1 1 | Rn | Rd | - * +---+---+-------------------+--------+-------------+------+------+ - * - * U: unsigned when set - * size: encoded in imm5 (see ARM ARM LowestSetBit()) - */ -static void handle_simd_umov_smov(DisasContext *s, int is_q, int is_signed, - int rn, int rd, int imm5) -{ - int size = ctz32(imm5); - int element; - TCGv_i64 tcg_rd; - - /* Check for UnallocatedEncodings */ - if (is_signed) { - if (size > 2 || (size == 2 && !is_q)) { - unallocated_encoding(s); - return; - } - } else { - if (size > 3 - || (size < 3 && is_q) - || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - } - - if (!fp_access_check(s)) { - return; - } - - element = extract32(imm5, 1+size, 4); - - tcg_rd = cpu_reg(s, rd); - read_vec_element(s, tcg_rd, rn, element, size | (is_signed ? MO_SIGN : 0)); - if (is_signed && !is_q) { - tcg_gen_ext32u_i64(tcg_rd, tcg_rd); - } -} - -/* AdvSIMD copy - * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0 - * +---+---+----+-----------------+------+---+------+---+------+------+ - * | 0 | Q | op | 0 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd | - * +---+---+----+-----------------+------+---+------+---+------+------+ - */ -static void disas_simd_copy(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm4 = extract32(insn, 11, 4); - int op = extract32(insn, 29, 1); - int is_q = extract32(insn, 30, 1); - int imm5 = extract32(insn, 16, 5); - - if (op) { - if (is_q) { - /* INS (element) */ - handle_simd_inse(s, rd, rn, imm4, imm5); - } else { - unallocated_encoding(s); - } - } else { - switch (imm4) { - case 0: - /* DUP (element - vector) */ - handle_simd_dupe(s, is_q, rd, rn, imm5); - break; - case 1: - /* DUP (general) */ - handle_simd_dupg(s, is_q, rd, rn, imm5); - break; - case 3: - if (is_q) { - /* INS (general) */ - handle_simd_insg(s, rd, rn, imm5); - } else { - unallocated_encoding(s); - } - break; - case 5: - case 7: - /* UMOV/SMOV (is_q indicates 32/64; imm4 indicates signedness) */ - handle_simd_umov_smov(s, is_q, (imm4 == 5), rn, rd, imm5); - break; - default: - unallocated_encoding(s); - break; - } - } -} - -/* AdvSIMD modified immediate - * 31 30 29 28 19 18 16 15 12 11 10 9 5 4 0 - * +---+---+----+---------------------+-----+-------+----+---+-------+------+ - * | 0 | Q | op | 0 1 1 1 1 0 0 0 0 0 | abc | cmode | o2 | 1 | defgh | Rd | - * +---+---+----+---------------------+-----+-------+----+---+-------+------+ - * - * There are a number of operations that can be carried out here: - * MOVI - move (shifted) imm into register - * MVNI - move inverted (shifted) imm into register - * ORR - bitwise OR of (shifted) imm with register - * BIC - bitwise clear of (shifted) imm with register - * With ARMv8.2 we also have: - * FMOV half-precision - */ -static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int cmode = extract32(insn, 12, 4); - int o2 = extract32(insn, 11, 1); - uint64_t abcdefgh = extract32(insn, 5, 5) | (extract32(insn, 16, 3) << 5); - bool is_neg = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - uint64_t imm = 0; - - if (o2 != 0 || ((cmode == 0xf) && is_neg && !is_q)) { - /* Check for FMOV (vector, immediate) - half-precision */ - if (!(dc_isar_feature(aa64_fp16, s) && o2 && cmode == 0xf)) { - unallocated_encoding(s); - return; - } - } - - if (!fp_access_check(s)) { - return; - } - - if (cmode == 15 && o2 && !is_neg) { - /* FMOV (vector, immediate) - half-precision */ - imm = vfp_expand_imm(MO_16, abcdefgh); - /* now duplicate across the lanes */ - imm = dup_const(MO_16, imm); - } else { - imm = asimd_imm_const(abcdefgh, cmode, is_neg); - } - - if (!((cmode & 0x9) == 0x1 || (cmode & 0xd) == 0x9)) { - /* MOVI or MVNI, with MVNI negation handled above. */ - tcg_gen_gvec_dup_imm(MO_64, vec_full_reg_offset(s, rd), is_q ? 16 : 8, - vec_full_reg_size(s), imm); - } else { - /* ORR or BIC, with BIC negation to AND handled above. */ - if (is_neg) { - gen_gvec_fn2i(s, is_q, rd, rd, imm, tcg_gen_gvec_andi, MO_64); - } else { - gen_gvec_fn2i(s, is_q, rd, rd, imm, tcg_gen_gvec_ori, MO_64); - } - } -} - -/* AdvSIMD scalar copy - * 31 30 29 28 21 20 16 15 14 11 10 9 5 4 0 - * +-----+----+-----------------+------+---+------+---+------+------+ - * | 0 1 | op | 1 1 1 1 0 0 0 0 | imm5 | 0 | imm4 | 1 | Rn | Rd | - * +-----+----+-----------------+------+---+------+---+------+------+ - */ -static void disas_simd_scalar_copy(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int imm4 = extract32(insn, 11, 4); - int imm5 = extract32(insn, 16, 5); - int op = extract32(insn, 29, 1); - - if (op != 0 || imm4 != 0) { - unallocated_encoding(s); - return; - } - - /* DUP (element, scalar) */ - handle_simd_dupes(s, rd, rn, imm5); -} - -/* AdvSIMD scalar pairwise - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 1 0 0 0 | opcode | 1 0 | Rn | Rd | - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) -{ - int u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - TCGv_ptr fpst; - - /* For some ops (the FP ones), size[1] is part of the encoding. - * For ADDP strictly it is not but size[1] is always 1 for valid - * encodings. - */ - opcode |= (extract32(size, 1, 1) << 5); - - switch (opcode) { - case 0x3b: /* ADDP */ - if (u || size != 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - fpst = NULL; - break; - case 0xc: /* FMAXNMP */ - case 0xd: /* FADDP */ - case 0xf: /* FMAXP */ - case 0x2c: /* FMINNMP */ - case 0x2f: /* FMINP */ - /* FP op, size[0] is 32 or 64 bit*/ - if (!u) { - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } else { - size = MO_16; - } - } else { - size = extract32(size, 0, 1) ? MO_64 : MO_32; - } - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - break; - default: - unallocated_encoding(s); - return; - } - - if (size == MO_64) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, 0, MO_64); - read_vec_element(s, tcg_op2, rn, 1, MO_64); - - switch (opcode) { - case 0x3b: /* ADDP */ - tcg_gen_add_i64(tcg_res, tcg_op1, tcg_op2); - break; - case 0xc: /* FMAXNMP */ - gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xd: /* FADDP */ - gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - - 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(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op1, rn, 0, size); - read_vec_element_i32(s, tcg_op2, rn, 1, size); - - if (size == MO_16) { - switch (opcode) { - case 0xc: /* FMAXNMP */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xd: /* FADDP */ - gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - } else { - switch (opcode) { - case 0xc: /* FMAXNMP */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xd: /* FADDP */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FMAXP */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2c: /* FMINNMP */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x2f: /* FMINP */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - } - - 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); - } -} - -/* - * Common SSHR[RA]/USHR[RA] - Shift right (optional rounding/accumulate) - * - * This code is handles the common shifting code and is used by both - * the vector and scalar code. - */ -static void handle_shri_with_rndacc(TCGv_i64 tcg_res, TCGv_i64 tcg_src, - TCGv_i64 tcg_rnd, bool accumulate, - bool is_u, int size, int shift) -{ - bool extended_result = false; - bool round = tcg_rnd != NULL; - int ext_lshift = 0; - TCGv_i64 tcg_src_hi; - - if (round && size == 3) { - extended_result = true; - ext_lshift = 64 - shift; - tcg_src_hi = tcg_temp_new_i64(); - } else if (shift == 64) { - if (!accumulate && is_u) { - /* result is zero */ - tcg_gen_movi_i64(tcg_res, 0); - return; - } - } - - /* Deal with the rounding step */ - if (round) { - if (extended_result) { - TCGv_i64 tcg_zero = tcg_constant_i64(0); - if (!is_u) { - /* take care of sign extending tcg_res */ - tcg_gen_sari_i64(tcg_src_hi, tcg_src, 63); - tcg_gen_add2_i64(tcg_src, tcg_src_hi, - tcg_src, tcg_src_hi, - tcg_rnd, tcg_zero); - } else { - tcg_gen_add2_i64(tcg_src, tcg_src_hi, - tcg_src, tcg_zero, - tcg_rnd, tcg_zero); - } - } else { - tcg_gen_add_i64(tcg_src, tcg_src, tcg_rnd); - } - } - - /* Now do the shift right */ - if (round && extended_result) { - /* extended case, >64 bit precision required */ - if (ext_lshift == 0) { - /* special case, only high bits matter */ - tcg_gen_mov_i64(tcg_src, tcg_src_hi); - } else { - tcg_gen_shri_i64(tcg_src, tcg_src, shift); - tcg_gen_shli_i64(tcg_src_hi, tcg_src_hi, ext_lshift); - tcg_gen_or_i64(tcg_src, tcg_src, tcg_src_hi); - } - } else { - if (is_u) { - if (shift == 64) { - /* essentially shifting in 64 zeros */ - tcg_gen_movi_i64(tcg_src, 0); - } else { - tcg_gen_shri_i64(tcg_src, tcg_src, shift); - } - } else { - if (shift == 64) { - /* effectively extending the sign-bit */ - tcg_gen_sari_i64(tcg_src, tcg_src, 63); - } else { - tcg_gen_sari_i64(tcg_src, tcg_src, shift); - } - } - } - - if (accumulate) { - tcg_gen_add_i64(tcg_res, tcg_res, 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) */ -static void handle_scalar_simd_shri(DisasContext *s, - bool is_u, int immh, int immb, - int opcode, int rn, int rd) -{ - const int size = 3; - int immhb = immh << 3 | immb; - int shift = 2 * (8 << size) - immhb; - bool accumulate = false; - bool round = false; - bool insert = false; - TCGv_i64 tcg_rn; - TCGv_i64 tcg_rd; - TCGv_i64 tcg_round; - - if (!extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0x02: /* SSRA / USRA (accumulate) */ - accumulate = true; - break; - case 0x04: /* SRSHR / URSHR (rounding) */ - round = true; - break; - case 0x06: /* SRSRA / URSRA (accum + rounding) */ - accumulate = round = true; - break; - case 0x08: /* SRI */ - insert = true; - break; - } - - if (round) { - tcg_round = tcg_constant_i64(1ULL << (shift - 1)); - } else { - tcg_round = NULL; - } - - tcg_rn = read_fp_dreg(s, rn); - tcg_rd = (accumulate || insert) ? read_fp_dreg(s, rd) : tcg_temp_new_i64(); - - if (insert) { - /* shift count same as element size is valid but does nothing; - * special case to avoid potential shift by 64. - */ - int esize = 8 << size; - if (shift != esize) { - tcg_gen_shri_i64(tcg_rn, tcg_rn, shift); - tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_rn, 0, esize - shift); - } - } else { - handle_shri_with_rndacc(tcg_rd, tcg_rn, tcg_round, - accumulate, is_u, size, shift); - } - - write_fp_dreg(s, rd, tcg_rd); - - tcg_temp_free_i64(tcg_rn); - tcg_temp_free_i64(tcg_rd); -} - -/* SHL/SLI - Scalar shift left */ -static void handle_scalar_simd_shli(DisasContext *s, bool insert, - int immh, int immb, int opcode, - int rn, int rd) -{ - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = immhb - (8 << size); - TCGv_i64 tcg_rn; - TCGv_i64 tcg_rd; - - if (!extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_rn = read_fp_dreg(s, rn); - tcg_rd = insert ? read_fp_dreg(s, rd) : tcg_temp_new_i64(); - - if (insert) { - tcg_gen_deposit_i64(tcg_rd, tcg_rd, tcg_rn, shift, 64 - shift); - } else { - tcg_gen_shli_i64(tcg_rd, tcg_rn, shift); - } - - 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 - * (signed/unsigned) narrowing */ -static void handle_vec_simd_sqshrn(DisasContext *s, bool is_scalar, bool is_q, - bool is_u_shift, bool is_u_narrow, - int immh, int immb, int opcode, - int rn, int rd) -{ - int immhb = immh << 3 | immb; - int size = 32 - clz32(immh) - 1; - int esize = 8 << size; - int shift = (2 * esize) - immhb; - int elements = is_scalar ? 1 : (64 / esize); - bool round = extract32(opcode, 0, 1); - MemOp ldop = (size + 1) | (is_u_shift ? 0 : MO_SIGN); - TCGv_i64 tcg_rn, tcg_rd, tcg_round; - TCGv_i32 tcg_rd_narrowed; - TCGv_i64 tcg_final; - - static NeonGenNarrowEnvFn * const signed_narrow_fns[4][2] = { - { gen_helper_neon_narrow_sat_s8, - gen_helper_neon_unarrow_sat8 }, - { gen_helper_neon_narrow_sat_s16, - gen_helper_neon_unarrow_sat16 }, - { gen_helper_neon_narrow_sat_s32, - gen_helper_neon_unarrow_sat32 }, - { NULL, NULL }, - }; - static NeonGenNarrowEnvFn * const unsigned_narrow_fns[4] = { - gen_helper_neon_narrow_sat_u8, - gen_helper_neon_narrow_sat_u16, - gen_helper_neon_narrow_sat_u32, - NULL - }; - NeonGenNarrowEnvFn *narrowfn; - - int i; - - assert(size < 4); - - if (extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (is_u_shift) { - narrowfn = unsigned_narrow_fns[size]; - } else { - narrowfn = signed_narrow_fns[size][is_u_narrow ? 1 : 0]; - } - - 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); - - if (round) { - tcg_round = tcg_constant_i64(1ULL << (shift - 1)); - } else { - tcg_round = NULL; - } - - for (i = 0; i < elements; i++) { - 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); - tcg_gen_extu_i32_i64(tcg_rd, tcg_rd_narrowed); - tcg_gen_deposit_i64(tcg_final, tcg_final, tcg_rd, esize * i, esize); - } - - if (!is_q) { - write_vec_element(s, tcg_final, rd, 0, MO_64); - } 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); -} - -/* SQSHLU, UQSHL, SQSHL: saturating left shifts */ -static void handle_simd_qshl(DisasContext *s, bool scalar, bool is_q, - bool src_unsigned, bool dst_unsigned, - int immh, int immb, int rn, int rd) -{ - int immhb = immh << 3 | immb; - int size = 32 - clz32(immh) - 1; - int shift = immhb - (8 << size); - int pass; - - assert(immh != 0); - assert(!(scalar && is_q)); - - if (!scalar) { - if (!is_q && extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } - - /* Since we use the variable-shift helpers we must - * replicate the shift count into each element of - * the tcg_shift value. - */ - switch (size) { - case 0: - shift |= shift << 8; - /* fall through */ - case 1: - shift |= shift << 16; - break; - case 2: - case 3: - break; - default: - g_assert_not_reached(); - } - } - - if (!fp_access_check(s)) { - return; - } - - if (size == 3) { - TCGv_i64 tcg_shift = tcg_constant_i64(shift); - static NeonGenTwo64OpEnvFn * const fns[2][2] = { - { gen_helper_neon_qshl_s64, gen_helper_neon_qshlu_s64 }, - { NULL, gen_helper_neon_qshl_u64 }, - }; - NeonGenTwo64OpEnvFn *genfn = fns[src_unsigned][dst_unsigned]; - int maxpass = is_q ? 2 : 1; - - for (pass = 0; pass < maxpass; pass++) { - 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); - write_vec_element(s, tcg_op, rd, pass, MO_64); - - tcg_temp_free_i64(tcg_op); - } - clear_vec_high(s, is_q, rd); - } else { - TCGv_i32 tcg_shift = tcg_constant_i32(shift); - static NeonGenTwoOpEnvFn * const fns[2][2][3] = { - { - { gen_helper_neon_qshl_s8, - gen_helper_neon_qshl_s16, - gen_helper_neon_qshl_s32 }, - { gen_helper_neon_qshlu_s8, - gen_helper_neon_qshlu_s16, - gen_helper_neon_qshlu_s32 } - }, { - { NULL, NULL, NULL }, - { gen_helper_neon_qshl_u8, - gen_helper_neon_qshl_u16, - gen_helper_neon_qshl_u32 } - } - }; - NeonGenTwoOpEnvFn *genfn = fns[src_unsigned][dst_unsigned][size]; - MemOp memop = scalar ? size : MO_32; - int maxpass = scalar ? 1 : is_q ? 4 : 2; - - for (pass = 0; pass < maxpass; pass++) { - 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); - if (scalar) { - switch (size) { - case 0: - tcg_gen_ext8u_i32(tcg_op, tcg_op); - break; - case 1: - tcg_gen_ext16u_i32(tcg_op, tcg_op); - break; - case 2: - break; - default: - g_assert_not_reached(); - } - write_fp_sreg(s, rd, tcg_op); - } else { - write_vec_element_i32(s, tcg_op, rd, pass, MO_32); - } - - tcg_temp_free_i32(tcg_op); - } - - if (!scalar) { - clear_vec_high(s, is_q, rd); - } - } -} - -/* Common vector code for handling integer to FP conversion */ -static void handle_simd_intfp_conv(DisasContext *s, int rd, int rn, - int elements, int is_signed, - int fracbits, int size) -{ - TCGv_ptr tcg_fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - TCGv_i32 tcg_shift = NULL; - - MemOp mop = size | (is_signed ? MO_SIGN : 0); - int pass; - - if (fracbits || size == MO_64) { - tcg_shift = tcg_constant_i32(fracbits); - } - - if (size == MO_64) { - TCGv_i64 tcg_int64 = tcg_temp_new_i64(); - TCGv_i64 tcg_double = tcg_temp_new_i64(); - - for (pass = 0; pass < elements; pass++) { - read_vec_element(s, tcg_int64, rn, pass, mop); - - if (is_signed) { - gen_helper_vfp_sqtod(tcg_double, tcg_int64, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_uqtod(tcg_double, tcg_int64, - tcg_shift, tcg_fpst); - } - if (elements == 1) { - write_fp_dreg(s, rd, tcg_double); - } else { - 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(); - - for (pass = 0; pass < elements; pass++) { - read_vec_element_i32(s, tcg_int32, rn, pass, mop); - - switch (size) { - case MO_32: - if (fracbits) { - if (is_signed) { - gen_helper_vfp_sltos(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_ultos(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } - } else { - if (is_signed) { - gen_helper_vfp_sitos(tcg_float, tcg_int32, tcg_fpst); - } else { - gen_helper_vfp_uitos(tcg_float, tcg_int32, tcg_fpst); - } - } - break; - case MO_16: - if (fracbits) { - if (is_signed) { - gen_helper_vfp_sltoh(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } else { - gen_helper_vfp_ultoh(tcg_float, tcg_int32, - tcg_shift, tcg_fpst); - } - } else { - if (is_signed) { - gen_helper_vfp_sitoh(tcg_float, tcg_int32, tcg_fpst); - } else { - gen_helper_vfp_uitoh(tcg_float, tcg_int32, tcg_fpst); - } - } - break; - default: - g_assert_not_reached(); - } - - if (elements == 1) { - write_fp_sreg(s, rd, tcg_float); - } else { - 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); -} - -/* UCVTF/SCVTF - Integer to FP conversion */ -static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, - bool is_q, bool is_u, - int immh, int immb, int opcode, - int rn, int rd) -{ - int size, elements, fracbits; - int immhb = immh << 3 | immb; - - if (immh & 8) { - size = MO_64; - if (!is_scalar && !is_q) { - unallocated_encoding(s); - return; - } - } else if (immh & 4) { - size = MO_32; - } else if (immh & 2) { - size = MO_16; - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - } else { - /* immh == 0 would be a failure of the decode logic */ - g_assert(immh == 1); - unallocated_encoding(s); - return; - } - - if (is_scalar) { - elements = 1; - } else { - elements = (8 << is_q) >> size; - } - fracbits = (16 << size) - immhb; - - if (!fp_access_check(s)) { - return; - } - - handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size); -} - -/* FCVTZS, FVCVTZU - FP to fixedpoint conversion */ -static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, - bool is_q, bool is_u, - int immh, int immb, int rn, int rd) -{ - int immhb = immh << 3 | immb; - int pass, size, fracbits; - TCGv_ptr tcg_fpstatus; - TCGv_i32 tcg_rmode, tcg_shift; - - if (immh & 0x8) { - size = MO_64; - if (!is_scalar && !is_q) { - unallocated_encoding(s); - return; - } - } else if (immh & 0x4) { - size = MO_32; - } else if (immh & 0x2) { - size = MO_16; - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - } else { - /* Should have split out AdvSIMD modified immediate earlier. */ - assert(immh == 1); - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - 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); - fracbits = (16 << size) - immhb; - tcg_shift = tcg_constant_i32(fracbits); - - if (size == MO_64) { - int maxpass = is_scalar ? 1 : 2; - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - if (is_u) { - gen_helper_vfp_touqd(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } else { - 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 { - void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); - int maxpass = is_scalar ? 1 : ((8 << is_q) >> size); - - switch (size) { - case MO_16: - if (is_u) { - fn = gen_helper_vfp_touhh; - } else { - fn = gen_helper_vfp_toshh; - } - break; - case MO_32: - if (is_u) { - fn = gen_helper_vfp_touls; - } else { - fn = gen_helper_vfp_tosls; - } - break; - default: - g_assert_not_reached(); - } - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, size); - fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - if (is_scalar) { - write_fp_sreg(s, rd, tcg_op); - } 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); -} - -/* AdvSIMD scalar shift by immediate - * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0 - * +-----+---+-------------+------+------+--------+---+------+------+ - * | 0 1 | U | 1 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd | - * +-----+---+-------------+------+------+--------+---+------+------+ - * - * This is the scalar version so it works on a fixed sized registers - */ -static void disas_simd_scalar_shift_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 5); - int immb = extract32(insn, 16, 3); - int immh = extract32(insn, 19, 4); - bool is_u = extract32(insn, 29, 1); - - if (immh == 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0x08: /* SRI */ - if (!is_u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x00: /* SSHR / USHR */ - case 0x02: /* SSRA / USRA */ - case 0x04: /* SRSHR / URSHR */ - case 0x06: /* SRSRA / URSRA */ - handle_scalar_simd_shri(s, is_u, immh, immb, opcode, rn, rd); - break; - case 0x0a: /* SHL / SLI */ - handle_scalar_simd_shli(s, is_u, immh, immb, opcode, rn, rd); - break; - case 0x1c: /* SCVTF, UCVTF */ - handle_simd_shift_intfp_conv(s, true, false, is_u, immh, immb, - opcode, rn, rd); - break; - case 0x10: /* SQSHRUN, SQSHRUN2 */ - case 0x11: /* SQRSHRUN, SQRSHRUN2 */ - if (!is_u) { - unallocated_encoding(s); - return; - } - handle_vec_simd_sqshrn(s, true, false, false, true, - immh, immb, opcode, rn, rd); - break; - case 0x12: /* SQSHRN, SQSHRN2, UQSHRN */ - case 0x13: /* SQRSHRN, SQRSHRN2, UQRSHRN, UQRSHRN2 */ - handle_vec_simd_sqshrn(s, true, false, is_u, is_u, - immh, immb, opcode, rn, rd); - break; - case 0xc: /* SQSHLU */ - if (!is_u) { - unallocated_encoding(s); - return; - } - handle_simd_qshl(s, true, false, false, true, immh, immb, rn, rd); - break; - case 0xe: /* SQSHL, UQSHL */ - handle_simd_qshl(s, true, false, is_u, is_u, immh, immb, rn, rd); - break; - case 0x1f: /* FCVTZS, FCVTZU */ - handle_simd_shift_fpint_conv(s, true, false, is_u, immh, immb, rn, rd); - break; - default: - unallocated_encoding(s); - break; - } -} - -/* AdvSIMD scalar three different - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +-----+---+-----------+------+---+------+--------+-----+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd | - * +-----+---+-----------+------+---+------+--------+-----+------+------+ - */ -static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) -{ - bool is_u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 4); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (is_u) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0x9: /* SQDMLAL, SQDMLAL2 */ - case 0xb: /* SQDMLSL, SQDMLSL2 */ - case 0xd: /* SQDMULL, SQDMULL2 */ - if (size == 0 || size == 3) { - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (size == 2) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, 0, MO_32 | MO_SIGN); - 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); - - switch (opcode) { - case 0xd: /* SQDMULL, SQDMULL2 */ - break; - case 0xb: /* SQDMLSL, SQDMLSL2 */ - tcg_gen_neg_i64(tcg_res, tcg_res); - /* 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, - tcg_res, tcg_op1); - break; - default: - g_assert_not_reached(); - } - - 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); - - switch (opcode) { - case 0xd: /* SQDMULL, SQDMULL2 */ - break; - case 0xb: /* SQDMLSL, SQDMLSL2 */ - gen_helper_neon_negl_u32(tcg_res, tcg_res); - /* fall through */ - case 0x9: /* SQDMLAL, SQDMLAL2 */ - { - 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, - tcg_res, tcg_op3); - tcg_temp_free_i64(tcg_op3); - break; - } - default: - g_assert_not_reached(); - } - - 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); - } -} - -static void handle_3same_64(DisasContext *s, int opcode, bool u, - TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 tcg_rm) -{ - /* Handle 64x64->64 opcodes which are shared between the scalar - * and vector 3-same groups. We cover every opcode where size == 3 - * is valid in either the three-reg-same (integer, not pairwise) - * or scalar-three-reg-same groups. - */ - TCGCond cond; - - switch (opcode) { - case 0x1: /* SQADD */ - if (u) { - gen_helper_neon_qadd_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } else { - gen_helper_neon_qadd_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } - break; - case 0x5: /* SQSUB */ - if (u) { - gen_helper_neon_qsub_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } else { - gen_helper_neon_qsub_s64(tcg_rd, cpu_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); - break; - case 0x7: /* CMGE, CMHS */ - cond = u ? TCG_COND_GEU : TCG_COND_GE; - goto do_cmop; - case 0x11: /* CMTST, CMEQ */ - if (u) { - cond = TCG_COND_EQ; - goto do_cmop; - } - gen_cmtst_i64(tcg_rd, tcg_rn, tcg_rm); - break; - case 0x8: /* SSHL, USHL */ - if (u) { - gen_ushl_i64(tcg_rd, tcg_rn, tcg_rm); - } else { - gen_sshl_i64(tcg_rd, tcg_rn, tcg_rm); - } - break; - case 0x9: /* SQSHL, UQSHL */ - if (u) { - gen_helper_neon_qshl_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } else { - gen_helper_neon_qshl_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } - break; - case 0xa: /* SRSHL, URSHL */ - if (u) { - gen_helper_neon_rshl_u64(tcg_rd, tcg_rn, tcg_rm); - } else { - gen_helper_neon_rshl_s64(tcg_rd, tcg_rn, tcg_rm); - } - break; - case 0xb: /* SQRSHL, UQRSHL */ - if (u) { - gen_helper_neon_qrshl_u64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } else { - gen_helper_neon_qrshl_s64(tcg_rd, cpu_env, tcg_rn, tcg_rm); - } - break; - case 0x10: /* ADD, SUB */ - if (u) { - tcg_gen_sub_i64(tcg_rd, tcg_rn, tcg_rm); - } else { - tcg_gen_add_i64(tcg_rd, tcg_rn, tcg_rm); - } - break; - default: - g_assert_not_reached(); - } -} - -/* Handle the 3-same-operands float operations; shared by the scalar - * and vector encodings. The caller must filter out any encodings - * not allocated for the encoding it is dealing with. - */ -static void handle_3same_float(DisasContext *s, int size, int elements, - int fpopcode, int rd, int rn, int rm) -{ - int pass; - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - for (pass = 0; pass < elements; pass++) { - if (size) { - /* Double */ - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - - switch (fpopcode) { - case 0x39: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negd(tcg_op1, tcg_op1); - /* fall through */ - case 0x19: /* FMLA */ - read_vec_element(s, tcg_res, rd, pass, MO_64); - gen_helper_vfp_muladdd(tcg_res, tcg_op1, tcg_op2, - tcg_res, fpst); - break; - case 0x18: /* FMAXNM */ - gen_helper_vfp_maxnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1a: /* FADD */ - gen_helper_vfp_addd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1b: /* FMULX */ - gen_helper_vfp_mulxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1c: /* FCMEQ */ - gen_helper_neon_ceq_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1e: /* FMAX */ - gen_helper_vfp_maxd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1f: /* FRECPS */ - gen_helper_recpsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x38: /* FMINNM */ - gen_helper_vfp_minnumd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3a: /* FSUB */ - gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3e: /* FMIN */ - gen_helper_vfp_mind(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3f: /* FRSQRTS */ - gen_helper_rsqrtsf_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5b: /* FMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5c: /* FCMGE */ - gen_helper_neon_cge_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5d: /* FACGE */ - gen_helper_neon_acge_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5f: /* FDIV */ - gen_helper_vfp_divd(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7a: /* FABD */ - gen_helper_vfp_subd(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_absd(tcg_res, tcg_res); - break; - case 0x7c: /* FCMGT */ - gen_helper_neon_cgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7d: /* FACGT */ - gen_helper_neon_acgt_f64(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - - 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(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op1, rn, pass, MO_32); - read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); - - switch (fpopcode) { - case 0x39: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negs(tcg_op1, tcg_op1); - /* fall through */ - case 0x19: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, MO_32); - gen_helper_vfp_muladds(tcg_res, tcg_op1, tcg_op2, - tcg_res, fpst); - break; - case 0x1a: /* FADD */ - gen_helper_vfp_adds(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1b: /* FMULX */ - gen_helper_vfp_mulxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1c: /* FCMEQ */ - gen_helper_neon_ceq_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1e: /* FMAX */ - gen_helper_vfp_maxs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1f: /* FRECPS */ - gen_helper_recpsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x18: /* FMAXNM */ - gen_helper_vfp_maxnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x38: /* FMINNM */ - gen_helper_vfp_minnums(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3a: /* FSUB */ - gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3e: /* FMIN */ - gen_helper_vfp_mins(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3f: /* FRSQRTS */ - gen_helper_rsqrtsf_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5b: /* FMUL */ - gen_helper_vfp_muls(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5c: /* FCMGE */ - gen_helper_neon_cge_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5d: /* FACGE */ - gen_helper_neon_acge_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x5f: /* FDIV */ - gen_helper_vfp_divs(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7a: /* FABD */ - gen_helper_vfp_subs(tcg_res, tcg_op1, tcg_op2, fpst); - gen_helper_vfp_abss(tcg_res, tcg_res); - break; - case 0x7c: /* FCMGT */ - gen_helper_neon_cgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7d: /* FACGT */ - gen_helper_neon_acgt_f32(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - - if (elements == 1) { - /* scalar single so clear high part */ - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - 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); -} - -/* AdvSIMD scalar three same - * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0 - * +-----+---+-----------+------+---+------+--------+---+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd | - * +-----+---+-----------+------+---+------+--------+---+------+------+ - */ -static void disas_simd_scalar_three_reg_same(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - TCGv_i64 tcg_rd; - - if (opcode >= 0x18) { - /* Floating point: U, size[1] and opcode indicate operation */ - int fpopcode = opcode | (extract32(size, 1, 1) << 5) | (u << 6); - switch (fpopcode) { - case 0x1b: /* FMULX */ - case 0x1f: /* FRECPS */ - case 0x3f: /* FRSQRTS */ - case 0x5d: /* FACGE */ - case 0x7d: /* FACGT */ - case 0x1c: /* FCMEQ */ - case 0x5c: /* FCMGE */ - case 0x7c: /* FCMGT */ - case 0x7a: /* FABD */ - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - handle_3same_float(s, extract32(size, 0, 1), 1, fpopcode, rd, rn, rm); - return; - } - - switch (opcode) { - case 0x1: /* SQADD, UQADD */ - case 0x5: /* SQSUB, UQSUB */ - case 0x9: /* SQSHL, UQSHL */ - case 0xb: /* SQRSHL, UQRSHL */ - break; - case 0x8: /* SSHL, USHL */ - case 0xa: /* SRSHL, URSHL */ - case 0x6: /* CMGT, CMHI */ - case 0x7: /* CMGE, CMHS */ - case 0x11: /* CMTST, CMEQ */ - case 0x10: /* ADD, SUB (vector) */ - if (size != 3) { - unallocated_encoding(s); - return; - } - break; - case 0x16: /* SQDMULH, SQRDMULH (vector) */ - if (size != 1 && size != 2) { - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_rd = tcg_temp_new_i64(); - - if (size == 3) { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - 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 - * no side effects for all these operations. - * OPTME: special-purpose helpers would avoid doing some - * unnecessary work in the helper for the 8 and 16 bit cases. - */ - NeonGenTwoOpEnvFn *genenvfn; - TCGv_i32 tcg_rn = tcg_temp_new_i32(); - TCGv_i32 tcg_rm = tcg_temp_new_i32(); - TCGv_i32 tcg_rd32 = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_rn, rn, 0, size); - read_vec_element_i32(s, tcg_rm, rm, 0, size); - - switch (opcode) { - case 0x1: /* SQADD, UQADD */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qadd_s8, gen_helper_neon_qadd_u8 }, - { gen_helper_neon_qadd_s16, gen_helper_neon_qadd_u16 }, - { gen_helper_neon_qadd_s32, gen_helper_neon_qadd_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - case 0x5: /* SQSUB, UQSUB */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qsub_s8, gen_helper_neon_qsub_u8 }, - { gen_helper_neon_qsub_s16, gen_helper_neon_qsub_u16 }, - { gen_helper_neon_qsub_s32, gen_helper_neon_qsub_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - case 0x9: /* SQSHL, UQSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qshl_s8, gen_helper_neon_qshl_u8 }, - { gen_helper_neon_qshl_s16, gen_helper_neon_qshl_u16 }, - { gen_helper_neon_qshl_s32, gen_helper_neon_qshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - case 0xb: /* SQRSHL, UQRSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qrshl_s8, gen_helper_neon_qrshl_u8 }, - { gen_helper_neon_qrshl_s16, gen_helper_neon_qrshl_u16 }, - { gen_helper_neon_qrshl_s32, gen_helper_neon_qrshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - case 0x16: /* SQDMULH, SQRDMULH */ - { - static NeonGenTwoOpEnvFn * const fns[2][2] = { - { gen_helper_neon_qdmulh_s16, gen_helper_neon_qrdmulh_s16 }, - { gen_helper_neon_qdmulh_s32, gen_helper_neon_qrdmulh_s32 }, - }; - assert(size == 1 || size == 2); - genenvfn = fns[size - 1][u]; - break; - } - default: - g_assert_not_reached(); - } - - genenvfn(tcg_rd32, cpu_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 - * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0 - * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+ - * | 0 1 | U | 1 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd | - * +-----+---+-----------+---+-----+------+-----+--------+---+----+----+ - * v: 0101 1110 0100 0000 0000 0100 0000 0000 => 5e400400 - * m: 1101 1111 0110 0000 1100 0100 0000 0000 => df60c400 - */ -static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, - uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 3); - int rm = extract32(insn, 16, 5); - bool u = extract32(insn, 29, 1); - bool a = extract32(insn, 23, 1); - int fpopcode = opcode | (a << 3) | (u << 4); - TCGv_ptr fpst; - TCGv_i32 tcg_op1; - TCGv_i32 tcg_op2; - TCGv_i32 tcg_res; - - switch (fpopcode) { - case 0x03: /* FMULX */ - case 0x04: /* FCMEQ (reg) */ - case 0x07: /* FRECPS */ - case 0x0f: /* FRSQRTS */ - case 0x14: /* FCMGE (reg) */ - case 0x15: /* FACGE */ - case 0x1a: /* FABD */ - case 0x1c: /* FCMGT (reg) */ - case 0x1d: /* FACGT */ - break; - default: - unallocated_encoding(s); - return; - } - - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - } - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(FPST_FPCR_F16); - - tcg_op1 = read_fp_hreg(s, rn); - tcg_op2 = read_fp_hreg(s, rm); - tcg_res = tcg_temp_new_i32(); - - switch (fpopcode) { - case 0x03: /* FMULX */ - gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x04: /* FCMEQ (reg) */ - gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x07: /* FRECPS */ - gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x0f: /* FRSQRTS */ - gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x14: /* FCMGE (reg) */ - gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x15: /* FACGE */ - gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1a: /* FABD */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); - break; - case 0x1c: /* FCMGT (reg) */ - gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1d: /* FACGT */ - gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - - 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 - * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 - * +-----+---+-----------+------+---+------+---+--------+---+----+----+ - * | 0 1 | U | 1 1 1 1 0 | size | 0 | Rm | 1 | opcode | 1 | Rn | Rd | - * +-----+---+-----------+------+---+------+---+--------+---+----+----+ - */ -static void disas_simd_scalar_three_reg_same_extra(DisasContext *s, - uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 4); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - TCGv_i32 ele1, ele2, ele3; - TCGv_i64 res; - bool feature; - - switch (u * 16 + opcode) { - case 0x10: /* SQRDMLAH (vector) */ - case 0x11: /* SQRDMLSH (vector) */ - if (size != 1 && size != 2) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_rdm, s); - break; - default: - unallocated_encoding(s); - return; - } - if (!feature) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - /* 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 no side effects for all these operations. - * OPTME: special-purpose helpers would avoid doing some - * unnecessary work in the helper for the 16 bit cases. - */ - ele1 = tcg_temp_new_i32(); - ele2 = tcg_temp_new_i32(); - ele3 = tcg_temp_new_i32(); - - read_vec_element_i32(s, ele1, rn, 0, size); - read_vec_element_i32(s, ele2, rm, 0, size); - read_vec_element_i32(s, ele3, rd, 0, size); - - switch (opcode) { - case 0x0: /* SQRDMLAH */ - if (size == 1) { - gen_helper_neon_qrdmlah_s16(ele3, cpu_env, ele1, ele2, ele3); - } else { - gen_helper_neon_qrdmlah_s32(ele3, cpu_env, ele1, ele2, ele3); - } - break; - case 0x1: /* SQRDMLSH */ - if (size == 1) { - gen_helper_neon_qrdmlsh_s16(ele3, cpu_env, ele1, ele2, ele3); - } else { - gen_helper_neon_qrdmlsh_s32(ele3, cpu_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, - TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, - TCGv_i32 tcg_rmode, TCGv_ptr tcg_fpstatus) -{ - /* Handle 64->64 opcodes which are shared between the scalar and - * vector 2-reg-misc groups. We cover every integer opcode where size == 3 - * is valid in either group and also the double-precision fp ops. - * The caller only need provide tcg_rmode and tcg_fpstatus if the op - * requires them. - */ - TCGCond cond; - - switch (opcode) { - case 0x4: /* CLS, CLZ */ - if (u) { - tcg_gen_clzi_i64(tcg_rd, tcg_rn, 64); - } else { - tcg_gen_clrsb_i64(tcg_rd, tcg_rn); - } - break; - case 0x5: /* NOT */ - /* This opcode is shared with CNT and RBIT but we have earlier - * enforced that size == 3 if and only if this is the NOT insn. - */ - tcg_gen_not_i64(tcg_rd, tcg_rn); - break; - case 0x7: /* SQABS, SQNEG */ - if (u) { - gen_helper_neon_qneg_s64(tcg_rd, cpu_env, tcg_rn); - } else { - gen_helper_neon_qabs_s64(tcg_rd, cpu_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); - break; - case 0x8: /* CMGT, CMGE */ - cond = u ? TCG_COND_GE : TCG_COND_GT; - goto do_cmop; - case 0x9: /* CMEQ, CMLE */ - cond = u ? TCG_COND_LE : TCG_COND_EQ; - goto do_cmop; - case 0xb: /* ABS, NEG */ - if (u) { - tcg_gen_neg_i64(tcg_rd, tcg_rn); - } else { - tcg_gen_abs_i64(tcg_rd, tcg_rn); - } - break; - case 0x2f: /* FABS */ - gen_helper_vfp_absd(tcg_rd, tcg_rn); - break; - case 0x6f: /* FNEG */ - gen_helper_vfp_negd(tcg_rd, tcg_rn); - break; - case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrtd(tcg_rd, tcg_rn, cpu_env); - break; - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_vfp_tosqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_vfp_touqd(tcg_rd, tcg_rn, tcg_constant_i32(0), tcg_fpstatus); - break; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - gen_helper_rintd(tcg_rd, tcg_rn, tcg_fpstatus); - break; - case 0x59: /* FRINTX */ - gen_helper_rintd_exact(tcg_rd, tcg_rn, tcg_fpstatus); - break; - case 0x1e: /* FRINT32Z */ - case 0x5e: /* FRINT32X */ - gen_helper_frint32_d(tcg_rd, tcg_rn, tcg_fpstatus); - break; - case 0x1f: /* FRINT64Z */ - case 0x5f: /* FRINT64X */ - gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus); - break; - default: - g_assert_not_reached(); - } -} - -static void handle_2misc_fcmp_zero(DisasContext *s, int opcode, - bool is_scalar, bool is_u, bool is_q, - int size, int rn, int rd) -{ - bool is_double = (size == MO_64); - TCGv_ptr fpst; - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(size == MO_16 ? FPST_FPCR_F16 : FPST_FPCR); - - if (is_double) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_zero = tcg_constant_i64(0); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - NeonGenTwoDoubleOpFn *genfn; - bool swap = false; - int pass; - - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fallthrough */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_neon_cgt_f64; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_neon_ceq_f64; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_neon_cge_f64; - break; - default: - g_assert_not_reached(); - } - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - read_vec_element(s, tcg_op, rn, pass, MO_64); - if (swap) { - genfn(tcg_res, tcg_zero, tcg_op, fpst); - } else { - genfn(tcg_res, tcg_op, tcg_zero, fpst); - } - 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(); - TCGv_i32 tcg_zero = tcg_constant_i32(0); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - NeonGenTwoSingleOpFn *genfn; - bool swap = false; - int pass, maxpasses; - - if (size == MO_16) { - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fall through */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_advsimd_cgt_f16; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_advsimd_ceq_f16; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_advsimd_cge_f16; - break; - default: - g_assert_not_reached(); - } - } else { - switch (opcode) { - case 0x2e: /* FCMLT (zero) */ - swap = true; - /* fall through */ - case 0x2c: /* FCMGT (zero) */ - genfn = gen_helper_neon_cgt_f32; - break; - case 0x2d: /* FCMEQ (zero) */ - genfn = gen_helper_neon_ceq_f32; - break; - case 0x6d: /* FCMLE (zero) */ - swap = true; - /* fall through */ - case 0x6c: /* FCMGE (zero) */ - genfn = gen_helper_neon_cge_f32; - break; - default: - g_assert_not_reached(); - } - } - - if (is_scalar) { - maxpasses = 1; - } else { - int vector_size = 8 << is_q; - maxpasses = vector_size >> size; - } - - for (pass = 0; pass < maxpasses; pass++) { - read_vec_element_i32(s, tcg_op, rn, pass, size); - if (swap) { - genfn(tcg_res, tcg_zero, tcg_op, fpst); - } else { - genfn(tcg_res, tcg_op, tcg_zero, fpst); - } - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } else { - 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, - bool is_scalar, bool is_u, bool is_q, - int size, int rn, int rd) -{ - bool is_double = (size == 3); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - - if (is_double) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - int pass; - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - read_vec_element(s, tcg_op, rn, pass, MO_64); - switch (opcode) { - case 0x3d: /* FRECPE */ - gen_helper_recpe_f64(tcg_res, tcg_op, fpst); - break; - case 0x3f: /* FRECPX */ - gen_helper_frecpx_f64(tcg_res, tcg_op, fpst); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f64(tcg_res, tcg_op, fpst); - break; - default: - g_assert_not_reached(); - } - 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(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - int pass, maxpasses; - - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; - } - - for (pass = 0; pass < maxpasses; pass++) { - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - - switch (opcode) { - case 0x3c: /* URECPE */ - gen_helper_recpe_u32(tcg_res, tcg_op); - break; - case 0x3d: /* FRECPE */ - gen_helper_recpe_f32(tcg_res, tcg_op, fpst); - break; - case 0x3f: /* FRECPX */ - gen_helper_frecpx_f32(tcg_res, tcg_op, fpst); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f32(tcg_res, tcg_op, fpst); - break; - default: - g_assert_not_reached(); - } - - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } else { - 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, - int opcode, bool u, bool is_q, - int size, int rn, int rd) -{ - /* Handle 2-reg-misc ops which are narrowing (so each 2*size element - * in the source becomes a size element in the destination). - */ - int pass; - TCGv_i32 tcg_res[2]; - int destelt = is_q ? 2 : 0; - int passes = scalar ? 1 : 2; - - if (scalar) { - tcg_res[1] = tcg_constant_i32(0); - } - - for (pass = 0; pass < passes; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - NeonGenNarrowFn *genfn = NULL; - NeonGenNarrowEnvFn *genenvfn = NULL; - - if (scalar) { - read_vec_element(s, tcg_op, rn, pass, size + 1); - } else { - read_vec_element(s, tcg_op, rn, pass, MO_64); - } - tcg_res[pass] = tcg_temp_new_i32(); - - switch (opcode) { - case 0x12: /* XTN, SQXTUN */ - { - static NeonGenNarrowFn * const xtnfns[3] = { - gen_helper_neon_narrow_u8, - gen_helper_neon_narrow_u16, - tcg_gen_extrl_i64_i32, - }; - static NeonGenNarrowEnvFn * const sqxtunfns[3] = { - gen_helper_neon_unarrow_sat8, - gen_helper_neon_unarrow_sat16, - gen_helper_neon_unarrow_sat32, - }; - if (u) { - genenvfn = sqxtunfns[size]; - } else { - genfn = xtnfns[size]; - } - break; - } - case 0x14: /* SQXTN, UQXTN */ - { - static NeonGenNarrowEnvFn * const fns[3][2] = { - { gen_helper_neon_narrow_sat_s8, - gen_helper_neon_narrow_sat_u8 }, - { gen_helper_neon_narrow_sat_s16, - gen_helper_neon_narrow_sat_u16 }, - { gen_helper_neon_narrow_sat_s32, - gen_helper_neon_narrow_sat_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - 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); - } else { - TCGv_i32 tcg_lo = tcg_temp_new_i32(); - TCGv_i32 tcg_hi = tcg_temp_new_i32(); - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 ahp = get_ahp_flag(); - - tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); - 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 */ - /* 64 bit to 32 bit float conversion - * with von Neumann rounding (round to odd) - */ - assert(size == 2); - gen_helper_fcvtx_f64_to_f32(tcg_res[pass], tcg_op, cpu_env); - break; - default: - g_assert_not_reached(); - } - - if (genfn) { - genfn(tcg_res[pass], tcg_op); - } else if (genenvfn) { - genenvfn(tcg_res[pass], cpu_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); -} - -/* Remaining saturating accumulating ops */ -static void handle_2misc_satacc(DisasContext *s, bool is_scalar, bool is_u, - bool is_q, int size, int rn, int rd) -{ - bool is_double = (size == 3); - - if (is_double) { - TCGv_i64 tcg_rn = tcg_temp_new_i64(); - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - int pass; - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - read_vec_element(s, tcg_rn, rn, pass, MO_64); - 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); - } else { /* SUQADD */ - gen_helper_neon_sqadd_u64(tcg_rd, cpu_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(); - TCGv_i32 tcg_rd = tcg_temp_new_i32(); - int pass, maxpasses; - - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; - } - - for (pass = 0; pass < maxpasses; pass++) { - if (is_scalar) { - read_vec_element_i32(s, tcg_rn, rn, pass, size); - read_vec_element_i32(s, tcg_rd, rd, pass, size); - } else { - read_vec_element_i32(s, tcg_rn, rn, pass, MO_32); - read_vec_element_i32(s, tcg_rd, rd, pass, MO_32); - } - - if (is_u) { /* USQADD */ - switch (size) { - case 0: - gen_helper_neon_uqadd_s8(tcg_rd, cpu_env, tcg_rn, tcg_rd); - break; - case 1: - gen_helper_neon_uqadd_s16(tcg_rd, cpu_env, tcg_rn, tcg_rd); - break; - case 2: - gen_helper_neon_uqadd_s32(tcg_rd, cpu_env, tcg_rn, tcg_rd); - break; - default: - g_assert_not_reached(); - } - } else { /* SUQADD */ - switch (size) { - case 0: - gen_helper_neon_sqadd_u8(tcg_rd, cpu_env, tcg_rn, tcg_rd); - break; - case 1: - gen_helper_neon_sqadd_u16(tcg_rd, cpu_env, tcg_rn, tcg_rd); - break; - case 2: - gen_helper_neon_sqadd_u32(tcg_rd, cpu_env, tcg_rn, tcg_rd); - break; - default: - g_assert_not_reached(); - } - } - - if (is_scalar) { - write_vec_element(s, tcg_constant_i64(0), rd, 0, MO_64); - } - 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); - } -} - -/* AdvSIMD scalar two reg misc - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 1 | U | 1 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd | - * +-----+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 12, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - bool is_fcvt = false; - int rmode; - TCGv_i32 tcg_rmode; - TCGv_ptr tcg_fpstatus; - - switch (opcode) { - case 0x3: /* USQADD / SUQADD*/ - if (!fp_access_check(s)) { - return; - } - handle_2misc_satacc(s, true, u, false, size, rn, rd); - return; - case 0x7: /* SQABS / SQNEG */ - break; - case 0xa: /* CMLT */ - if (u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - case 0xb: /* ABS, NEG */ - if (size != 3) { - unallocated_encoding(s); - return; - } - break; - case 0x12: /* SQXTUN */ - if (!u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x14: /* SQXTN, UQXTN */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, true, opcode, u, false, size, rn, rd); - return; - case 0xc ... 0xf: - case 0x16 ... 0x1d: - case 0x1f: - /* Floating point: U, size[1] and opcode indicate operation; - * size[0] indicates single or double precision. - */ - opcode |= (extract32(size, 1, 1) << 5) | (u << 6); - size = extract32(size, 0, 1) ? 3 : 2; - switch (opcode) { - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - handle_2misc_fcmp_zero(s, opcode, true, u, true, size, rn, rd); - return; - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - { - bool is_signed = (opcode == 0x1d); - if (!fp_access_check(s)) { - return; - } - handle_simd_intfp_conv(s, rd, rn, 1, is_signed, 0, size); - return; - } - case 0x3d: /* FRECPE */ - case 0x3f: /* FRECPX */ - case 0x7d: /* FRSQRTE */ - if (!fp_access_check(s)) { - return; - } - handle_2misc_reciprocal(s, opcode, true, u, true, size, rn, rd); - return; - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - is_fcvt = true; - rmode = extract32(opcode, 5, 1) | (extract32(opcode, 0, 1) << 1); - break; - case 0x1c: /* FCVTAS */ - case 0x5c: /* FCVTAU */ - /* TIEAWAY doesn't fit in the usual rounding mode encoding */ - is_fcvt = true; - rmode = FPROUNDING_TIEAWAY; - break; - case 0x56: /* FCVTXN, FCVTXN2 */ - if (size == 2) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, true, opcode, u, false, size - 1, rn, rd); - return; - default: - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - 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); - } else { - tcg_rmode = NULL; - tcg_fpstatus = NULL; - } - - if (size == 3) { - TCGv_i64 tcg_rn = read_fp_dreg(s, rn); - TCGv_i64 tcg_rd = tcg_temp_new_i64(); - - 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(); - - read_vec_element_i32(s, tcg_rn, rn, 0, size); - - switch (opcode) { - case 0x7: /* SQABS, SQNEG */ - { - NeonGenOneOpEnvFn *genfn; - static NeonGenOneOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qabs_s8, gen_helper_neon_qneg_s8 }, - { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, - { gen_helper_neon_qabs_s32, gen_helper_neon_qneg_s32 }, - }; - genfn = fns[size][u]; - genfn(tcg_rd, cpu_env, tcg_rn); - break; - } - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_vfp_tosls(tcg_rd, tcg_rn, tcg_constant_i32(0), - tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_vfp_touls(tcg_rd, tcg_rn, tcg_constant_i32(0), - tcg_fpstatus); - break; - default: - g_assert_not_reached(); - } - - 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); - } -} - -/* SSHR[RA]/USHR[RA] - Vector shift right (optional rounding/accumulate) */ -static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u, - int immh, int immb, int opcode, int rn, int rd) -{ - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = 2 * (8 << size) - immhb; - GVecGen2iFn *gvec_fn; - - if (extract32(immh, 3, 1) && !is_q) { - unallocated_encoding(s); - return; - } - tcg_debug_assert(size <= 3); - - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0x02: /* SSRA / USRA (accumulate) */ - gvec_fn = is_u ? gen_gvec_usra : gen_gvec_ssra; - break; - - case 0x08: /* SRI */ - gvec_fn = gen_gvec_sri; - break; - - case 0x00: /* SSHR / USHR */ - if (is_u) { - if (shift == 8 << size) { - /* Shift count the same size as element size produces zero. */ - tcg_gen_gvec_dup_imm(size, vec_full_reg_offset(s, rd), - is_q ? 16 : 8, vec_full_reg_size(s), 0); - return; - } - gvec_fn = tcg_gen_gvec_shri; - } else { - /* Shift count the same size as element size produces all sign. */ - if (shift == 8 << size) { - shift -= 1; - } - gvec_fn = tcg_gen_gvec_sari; - } - break; - - case 0x04: /* SRSHR / URSHR (rounding) */ - gvec_fn = is_u ? gen_gvec_urshr : gen_gvec_srshr; - break; - - case 0x06: /* SRSRA / URSRA (accum + rounding) */ - gvec_fn = is_u ? gen_gvec_ursra : gen_gvec_srsra; - break; - - default: - g_assert_not_reached(); - } - - gen_gvec_fn2i(s, is_q, rd, rn, shift, gvec_fn, size); -} - -/* SHL/SLI - Vector shift left */ -static void handle_vec_simd_shli(DisasContext *s, bool is_q, bool insert, - int immh, int immb, int opcode, int rn, int rd) -{ - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = immhb - (8 << size); - - /* Range of size is limited by decode: immh is a non-zero 4 bit field */ - assert(size >= 0 && size <= 3); - - if (extract32(immh, 3, 1) && !is_q) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (insert) { - gen_gvec_fn2i(s, is_q, rd, rn, shift, gen_gvec_sli, size); - } else { - gen_gvec_fn2i(s, is_q, rd, rn, shift, tcg_gen_gvec_shli, size); - } -} - -/* USHLL/SHLL - Vector shift left with widening */ -static void handle_vec_simd_wshli(DisasContext *s, bool is_q, bool is_u, - int immh, int immb, int opcode, int rn, int rd) -{ - int size = 32 - clz32(immh) - 1; - int immhb = immh << 3 | immb; - int shift = immhb - (8 << size); - 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); - int i; - - if (size >= 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - /* For the LL variants the store is larger than the load, - * so if rd == rn we would overwrite parts of our input. - * So load everything right now and use shifts in the main loop. - */ - read_vec_element(s, tcg_rn, rn, is_q ? 1 : 0, MO_64); - - for (i = 0; i < elements; i++) { - tcg_gen_shri_i64(tcg_rd, tcg_rn, i * esize); - ext_and_shift_reg(tcg_rd, tcg_rd, size | (!is_u << 2), 0); - tcg_gen_shli_i64(tcg_rd, tcg_rd, shift); - write_vec_element(s, tcg_rd, rd, i, size + 1); - } -} - -/* SHRN/RSHRN - Shift right with narrowing (and potential rounding) */ -static void handle_vec_simd_shrn(DisasContext *s, bool is_q, - int immh, int immb, int opcode, int rn, int rd) -{ - int immhb = immh << 3 | immb; - int size = 32 - clz32(immh) - 1; - int dsize = 64; - int esize = 8 << size; - int elements = dsize/esize; - int shift = (2 * esize) - immhb; - bool round = extract32(opcode, 0, 1); - TCGv_i64 tcg_rn, tcg_rd, tcg_final; - TCGv_i64 tcg_round; - int i; - - if (extract32(immh, 3, 1)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - tcg_rn = tcg_temp_new_i64(); - tcg_rd = tcg_temp_new_i64(); - tcg_final = tcg_temp_new_i64(); - read_vec_element(s, tcg_final, rd, is_q ? 1 : 0, MO_64); - - if (round) { - tcg_round = tcg_constant_i64(1ULL << (shift - 1)); - } else { - tcg_round = NULL; - } - - for (i = 0; i < elements; i++) { - read_vec_element(s, tcg_rn, rn, i, size+1); - handle_shri_with_rndacc(tcg_rd, tcg_rn, tcg_round, - false, true, size+1, shift); - - tcg_gen_deposit_i64(tcg_final, tcg_final, tcg_rd, esize * i, esize); - } - - if (!is_q) { - write_vec_element(s, tcg_final, rd, 0, MO_64); - } 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); -} - - -/* AdvSIMD shift by immediate - * 31 30 29 28 23 22 19 18 16 15 11 10 9 5 4 0 - * +---+---+---+-------------+------+------+--------+---+------+------+ - * | 0 | Q | U | 0 1 1 1 1 0 | immh | immb | opcode | 1 | Rn | Rd | - * +---+---+---+-------------+------+------+--------+---+------+------+ - */ -static void disas_simd_shift_imm(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 5); - int immb = extract32(insn, 16, 3); - int immh = extract32(insn, 19, 4); - bool is_u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - - /* data_proc_simd[] has sent immh == 0 to disas_simd_mod_imm. */ - assert(immh != 0); - - switch (opcode) { - case 0x08: /* SRI */ - if (!is_u) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x00: /* SSHR / USHR */ - case 0x02: /* SSRA / USRA (accumulate) */ - case 0x04: /* SRSHR / URSHR (rounding) */ - case 0x06: /* SRSRA / URSRA (accum + rounding) */ - handle_vec_simd_shri(s, is_q, is_u, immh, immb, opcode, rn, rd); - break; - case 0x0a: /* SHL / SLI */ - handle_vec_simd_shli(s, is_q, is_u, immh, immb, opcode, rn, rd); - break; - case 0x10: /* SHRN */ - case 0x11: /* RSHRN / SQRSHRUN */ - if (is_u) { - handle_vec_simd_sqshrn(s, false, is_q, false, true, immh, immb, - opcode, rn, rd); - } else { - handle_vec_simd_shrn(s, is_q, immh, immb, opcode, rn, rd); - } - break; - case 0x12: /* SQSHRN / UQSHRN */ - case 0x13: /* SQRSHRN / UQRSHRN */ - handle_vec_simd_sqshrn(s, false, is_q, is_u, is_u, immh, immb, - opcode, rn, rd); - break; - case 0x14: /* SSHLL / USHLL */ - handle_vec_simd_wshli(s, is_q, is_u, immh, immb, opcode, rn, rd); - break; - case 0x1c: /* SCVTF / UCVTF */ - handle_simd_shift_intfp_conv(s, false, is_q, is_u, immh, immb, - opcode, rn, rd); - break; - case 0xc: /* SQSHLU */ - if (!is_u) { - unallocated_encoding(s); - return; - } - handle_simd_qshl(s, false, is_q, false, true, immh, immb, rn, rd); - break; - case 0xe: /* SQSHL, UQSHL */ - handle_simd_qshl(s, false, is_q, is_u, is_u, immh, immb, rn, rd); - break; - case 0x1f: /* FCVTZS/ FCVTZU */ - handle_simd_shift_fpint_conv(s, false, is_q, is_u, immh, immb, rn, rd); - return; - default: - unallocated_encoding(s); - return; - } -} - -/* Generate code to do a "long" addition or subtraction, ie one done in - * TCGv_i64 on vector lanes twice the width specified by size. - */ -static void gen_neon_addl(int size, bool is_sub, TCGv_i64 tcg_res, - TCGv_i64 tcg_op1, TCGv_i64 tcg_op2) -{ - static NeonGenTwo64OpFn * const fns[3][2] = { - { gen_helper_neon_addl_u16, gen_helper_neon_subl_u16 }, - { gen_helper_neon_addl_u32, gen_helper_neon_subl_u32 }, - { tcg_gen_add_i64, tcg_gen_sub_i64 }, - }; - NeonGenTwo64OpFn *genfn; - assert(size < 3); - - genfn = fns[size][is_sub]; - genfn(tcg_res, tcg_op1, tcg_op2); -} - -static void handle_3rd_widening(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) -{ - /* 3-reg-different widening insns: 64 x 64 -> 128 */ - TCGv_i64 tcg_res[2]; - int pass, accop; - - tcg_res[0] = tcg_temp_new_i64(); - tcg_res[1] = tcg_temp_new_i64(); - - /* Does this op do an adding accumulate, a subtracting accumulate, - * or no accumulate at all? - */ - switch (opcode) { - case 5: - case 8: - case 9: - accop = 1; - break; - case 10: - case 11: - accop = -1; - break; - default: - accop = 0; - break; - } - - if (accop != 0) { - read_vec_element(s, tcg_res[0], rd, 0, MO_64); - read_vec_element(s, tcg_res[1], rd, 1, MO_64); - } - - /* size == 2 means two 32x32->64 operations; this is worth special - * casing because we can generally handle it inline. - */ - if (size == 2) { - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_passres; - MemOp memop = MO_32 | (is_u ? 0 : MO_SIGN); - - int elt = pass + is_q * 2; - - read_vec_element(s, tcg_op1, rn, elt, memop); - read_vec_element(s, tcg_op2, rm, elt, memop); - - if (accop == 0) { - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - switch (opcode) { - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - tcg_gen_add_i64(tcg_passres, tcg_op1, tcg_op2); - break; - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - tcg_gen_sub_i64(tcg_passres, tcg_op1, tcg_op2); - break; - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - { - TCGv_i64 tcg_tmp1 = tcg_temp_new_i64(); - TCGv_i64 tcg_tmp2 = tcg_temp_new_i64(); - - tcg_gen_sub_i64(tcg_tmp1, tcg_op1, tcg_op2); - tcg_gen_sub_i64(tcg_tmp2, tcg_op2, tcg_op1); - 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 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ - tcg_gen_mul_i64(tcg_passres, tcg_op1, tcg_op2); - break; - case 9: /* SQDMLAL, SQDMLAL2 */ - 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, - tcg_passres, tcg_passres); - break; - default: - g_assert_not_reached(); - } - - if (opcode == 9 || opcode == 11) { - /* saturating accumulate ops */ - if (accop < 0) { - tcg_gen_neg_i64(tcg_passres, tcg_passres); - } - gen_helper_neon_addl_saturate_s64(tcg_res[pass], cpu_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 */ - for (pass = 0; pass < 2; pass++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i64 tcg_passres; - int elt = pass + is_q * 2; - - read_vec_element_i32(s, tcg_op1, rn, elt, MO_32); - read_vec_element_i32(s, tcg_op2, rm, elt, MO_32); - - if (accop == 0) { - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - switch (opcode) { - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - { - TCGv_i64 tcg_op2_64 = tcg_temp_new_i64(); - static NeonGenWidenFn * const widenfns[2][2] = { - { gen_helper_neon_widen_s8, gen_helper_neon_widen_u8 }, - { gen_helper_neon_widen_s16, gen_helper_neon_widen_u16 }, - }; - NeonGenWidenFn *widenfn = widenfns[size][is_u]; - - widenfn(tcg_op2_64, tcg_op2); - 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 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - if (size == 0) { - if (is_u) { - gen_helper_neon_abdl_u16(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_abdl_s16(tcg_passres, tcg_op1, tcg_op2); - } - } else { - if (is_u) { - gen_helper_neon_abdl_u32(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_abdl_s32(tcg_passres, tcg_op1, tcg_op2); - } - } - break; - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* UMULL, UMULL2, SMULL, SMULL2 */ - if (size == 0) { - if (is_u) { - gen_helper_neon_mull_u8(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_mull_s8(tcg_passres, tcg_op1, tcg_op2); - } - } else { - if (is_u) { - gen_helper_neon_mull_u16(tcg_passres, tcg_op1, tcg_op2); - } else { - gen_helper_neon_mull_s16(tcg_passres, tcg_op1, tcg_op2); - } - } - break; - case 9: /* SQDMLAL, SQDMLAL2 */ - case 11: /* SQDMLSL, SQDMLSL2 */ - 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, - 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) { - /* saturating accumulate ops */ - if (accop < 0) { - gen_helper_neon_negl_u32(tcg_passres, tcg_passres); - } - gen_helper_neon_addl_saturate_s32(tcg_res[pass], cpu_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, - int opcode, int rd, int rn, int rm) -{ - TCGv_i64 tcg_res[2]; - int part = is_q ? 2 : 0; - int pass; - - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i64 tcg_op2_wide = tcg_temp_new_i64(); - static NeonGenWidenFn * const widenfns[3][2] = { - { gen_helper_neon_widen_s8, gen_helper_neon_widen_u8 }, - { gen_helper_neon_widen_s16, gen_helper_neon_widen_u16 }, - { tcg_gen_ext_i32_i64, tcg_gen_extu_i32_i64 }, - }; - NeonGenWidenFn *widenfn = widenfns[size][is_u]; - - 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]); - } -} - -static void do_narrow_round_high_u32(TCGv_i32 res, TCGv_i64 in) -{ - tcg_gen_addi_i64(in, in, 1U << 31); - tcg_gen_extrh_i64_i32(res, in); -} - -static void handle_3rd_narrowing(DisasContext *s, int is_q, int is_u, int size, - int opcode, int rd, int rn, int rm) -{ - TCGv_i32 tcg_res[2]; - int part = is_q ? 2 : 0; - int pass; - - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_wideres = tcg_temp_new_i64(); - static NeonGenNarrowFn * const narrowfns[3][2] = { - { gen_helper_neon_narrow_high_u8, - gen_helper_neon_narrow_round_high_u8 }, - { gen_helper_neon_narrow_high_u16, - gen_helper_neon_narrow_round_high_u16 }, - { tcg_gen_extrh_i64_i32, do_narrow_round_high_u32 }, - }; - NeonGenNarrowFn *gennarrow = narrowfns[size][is_u]; - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - - 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); -} - -/* AdvSIMD three different - * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 0 0 | Rn | Rd | - * +---+---+---+-----------+------+---+------+--------+-----+------+------+ - */ -static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) -{ - /* Instructions in this group fall into three basic classes - * (in each case with the operation working on each element in - * the input vectors): - * (1) widening 64 x 64 -> 128 (with possibly Vd as an extra - * 128 bit input) - * (2) wide 64 x 128 -> 128 - * (3) narrowing 128 x 128 -> 64 - * Here we do initial decode, catch unallocated cases and - * dispatch to separate functions for each class. - */ - int is_q = extract32(insn, 30, 1); - int is_u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 4); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - switch (opcode) { - case 1: /* SADDW, SADDW2, UADDW, UADDW2 */ - case 3: /* SSUBW, SSUBW2, USUBW, USUBW2 */ - /* 64 x 128 -> 128 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_3rd_wide(s, is_q, is_u, size, opcode, rd, rn, rm); - break; - case 4: /* ADDHN, ADDHN2, RADDHN, RADDHN2 */ - case 6: /* SUBHN, SUBHN2, RSUBHN, RSUBHN2 */ - /* 128 x 128 -> 64 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_3rd_narrowing(s, is_q, is_u, size, opcode, rd, rn, rm); - break; - case 14: /* PMULL, PMULL2 */ - if (is_u) { - unallocated_encoding(s); - return; - } - switch (size) { - case 0: /* PMULL.P8 */ - if (!fp_access_check(s)) { - return; - } - /* The Q field specifies lo/hi half input for this insn. */ - gen_gvec_op3_ool(s, true, rd, rn, rm, is_q, - gen_helper_neon_pmull_h); - break; - - case 3: /* PMULL.P64 */ - if (!dc_isar_feature(aa64_pmull, s)) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - /* The Q field specifies lo/hi half input for this insn. */ - gen_gvec_op3_ool(s, true, rd, rn, rm, is_q, - gen_helper_gvec_pmull_q); - break; - - default: - unallocated_encoding(s); - break; - } - return; - case 9: /* SQDMLAL, SQDMLAL2 */ - case 11: /* SQDMLSL, SQDMLSL2 */ - case 13: /* SQDMULL, SQDMULL2 */ - if (is_u || size == 0) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0: /* SADDL, SADDL2, UADDL, UADDL2 */ - case 2: /* SSUBL, SSUBL2, USUBL, USUBL2 */ - case 5: /* SABAL, SABAL2, UABAL, UABAL2 */ - case 7: /* SABDL, SABDL2, UABDL, UABDL2 */ - case 8: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - case 10: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - case 12: /* SMULL, SMULL2, UMULL, UMULL2 */ - /* 64 x 64 -> 128 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - handle_3rd_widening(s, is_q, is_u, size, opcode, rd, rn, rm); - break; - default: - /* opcode 15 not allocated */ - unallocated_encoding(s); - break; - } -} - -/* Logic op (opcode == 3) subgroup of C3.6.16. */ -static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool is_u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - - if (!fp_access_check(s)) { - return; - } - - switch (size + 4 * is_u) { - case 0: /* AND */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_and, 0); - return; - case 1: /* BIC */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_andc, 0); - return; - case 2: /* ORR */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_or, 0); - return; - case 3: /* ORN */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_orc, 0); - return; - case 4: /* EOR */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_xor, 0); - return; - - case 5: /* BSL bitwise select */ - gen_gvec_fn4(s, is_q, rd, rd, rn, rm, tcg_gen_gvec_bitsel, 0); - return; - case 6: /* BIT, bitwise insert if true */ - gen_gvec_fn4(s, is_q, rd, rm, rn, rd, tcg_gen_gvec_bitsel, 0); - return; - case 7: /* BIF, bitwise insert if false */ - gen_gvec_fn4(s, is_q, rd, rm, rd, rn, tcg_gen_gvec_bitsel, 0); - return; - - default: - g_assert_not_reached(); - } -} - -/* Pairwise op subgroup of C3.6.16. - * - * This is called directly or via the handle_3same_float for float pairwise - * operations where the opcode and size are calculated differently. - */ -static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, - int size, int rn, int rm, int rd) -{ - TCGv_ptr fpst; - int pass; - - /* Floating point operations need fpst */ - if (opcode >= 0x58) { - fpst = fpstatus_ptr(FPST_FPCR); - } else { - fpst = NULL; - } - - if (!fp_access_check(s)) { - return; - } - - /* These operations work on the concatenated rm:rn, with each pair of - * adjacent elements being operated on to produce an element in the result. - */ - if (size == 3) { - TCGv_i64 tcg_res[2]; - - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - int passreg = (pass == 0) ? rn : rm; - - read_vec_element(s, tcg_op1, passreg, 0, MO_64); - read_vec_element(s, tcg_op2, passreg, 1, MO_64); - tcg_res[pass] = tcg_temp_new_i64(); - - switch (opcode) { - case 0x17: /* ADDP */ - tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2); - break; - case 0x58: /* FMAXNMP */ - gen_helper_vfp_maxnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x5a: /* FADDP */ - gen_helper_vfp_addd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x5e: /* FMAXP */ - gen_helper_vfp_maxd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x78: /* FMINNMP */ - gen_helper_vfp_minnumd(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x7e: /* FMINP */ - gen_helper_vfp_mind(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - 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; - TCGv_i32 tcg_res[4]; - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - NeonGenTwoOpFn *genfn = NULL; - int passreg = pass < (maxpass / 2) ? rn : rm; - int passelt = (is_q && (pass & 1)) ? 2 : 0; - - read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_32); - read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_32); - tcg_res[pass] = tcg_temp_new_i32(); - - switch (opcode) { - case 0x17: /* ADDP */ - { - static NeonGenTwoOpFn * const fns[3] = { - gen_helper_neon_padd_u8, - gen_helper_neon_padd_u16, - tcg_gen_add_i32, - }; - genfn = fns[size]; - break; - } - case 0x14: /* SMAXP, UMAXP */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_pmax_s8, gen_helper_neon_pmax_u8 }, - { gen_helper_neon_pmax_s16, gen_helper_neon_pmax_u16 }, - { tcg_gen_smax_i32, tcg_gen_umax_i32 }, - }; - genfn = fns[size][u]; - break; - } - case 0x15: /* SMINP, UMINP */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_pmin_s8, gen_helper_neon_pmin_u8 }, - { gen_helper_neon_pmin_s16, gen_helper_neon_pmin_u16 }, - { tcg_gen_smin_i32, tcg_gen_umin_i32 }, - }; - genfn = fns[size][u]; - break; - } - /* The FP operations are all on single floats (32 bit) */ - case 0x58: /* FMAXNMP */ - gen_helper_vfp_maxnums(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x5a: /* FADDP */ - gen_helper_vfp_adds(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x5e: /* FMAXP */ - gen_helper_vfp_maxs(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x78: /* FMINNMP */ - gen_helper_vfp_minnums(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x7e: /* FMINP */ - gen_helper_vfp_mins(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - - /* FP ops called directly, otherwise call now */ - 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. */ -static void disas_simd_3same_float(DisasContext *s, uint32_t insn) -{ - /* For floating point ops, the U, size[1] and opcode bits - * together indicate the operation. size[0] indicates single - * or double. - */ - int fpopcode = extract32(insn, 11, 5) - | (extract32(insn, 23, 1) << 5) - | (extract32(insn, 29, 1) << 6); - int is_q = extract32(insn, 30, 1); - int size = extract32(insn, 22, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - int datasize = is_q ? 128 : 64; - int esize = 32 << size; - int elements = datasize / esize; - - if (size == 1 && !is_q) { - unallocated_encoding(s); - return; - } - - switch (fpopcode) { - case 0x58: /* FMAXNMP */ - case 0x5a: /* FADDP */ - case 0x5e: /* FMAXP */ - case 0x78: /* FMINNMP */ - case 0x7e: /* FMINP */ - if (size && !is_q) { - unallocated_encoding(s); - return; - } - handle_simd_3same_pair(s, is_q, 0, fpopcode, size ? MO_64 : MO_32, - rn, rm, rd); - return; - case 0x1b: /* FMULX */ - case 0x1f: /* FRECPS */ - case 0x3f: /* FRSQRTS */ - case 0x5d: /* FACGE */ - case 0x7d: /* FACGT */ - case 0x19: /* FMLA */ - case 0x39: /* FMLS */ - case 0x18: /* FMAXNM */ - case 0x1a: /* FADD */ - case 0x1c: /* FCMEQ */ - case 0x1e: /* FMAX */ - case 0x38: /* FMINNM */ - case 0x3a: /* FSUB */ - case 0x3e: /* FMIN */ - case 0x5b: /* FMUL */ - case 0x5c: /* FCMGE */ - case 0x5f: /* FDIV */ - case 0x7a: /* FABD */ - case 0x7c: /* FCMGT */ - if (!fp_access_check(s)) { - return; - } - handle_3same_float(s, size, elements, fpopcode, rd, rn, rm); - return; - - case 0x1d: /* FMLAL */ - case 0x3d: /* FMLSL */ - case 0x59: /* FMLAL2 */ - case 0x79: /* FMLSL2 */ - if (size & 1 || !dc_isar_feature(aa64_fhm, s)) { - unallocated_encoding(s); - return; - } - if (fp_access_check(s)) { - int is_s = extract32(insn, 23, 1); - int is_2 = extract32(insn, 29, 1); - 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, - is_q ? 16 : 8, vec_full_reg_size(s), - data, gen_helper_gvec_fmlal_a64); - } - return; - - default: - unallocated_encoding(s); - return; - } -} - -/* Integer op subgroup of C3.6.16. */ -static void disas_simd_3same_int(DisasContext *s, uint32_t insn) -{ - int is_q = extract32(insn, 30, 1); - int u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 11, 5); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - int pass; - TCGCond cond; - - switch (opcode) { - case 0x13: /* MUL, PMUL */ - if (u && size != 0) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x0: /* SHADD, UHADD */ - case 0x2: /* SRHADD, URHADD */ - case 0x4: /* SHSUB, UHSUB */ - case 0xc: /* SMAX, UMAX */ - case 0xd: /* SMIN, UMIN */ - case 0xe: /* SABD, UABD */ - case 0xf: /* SABA, UABA */ - case 0x12: /* MLA, MLS */ - if (size == 3) { - unallocated_encoding(s); - return; - } - break; - case 0x16: /* SQDMULH, SQRDMULH */ - if (size == 0 || size == 3) { - unallocated_encoding(s); - return; - } - break; - default: - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - } - - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0x01: /* SQADD, UQADD */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uqadd_qc, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqadd_qc, size); - } - return; - case 0x05: /* SQSUB, UQSUB */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uqsub_qc, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqsub_qc, size); - } - return; - case 0x08: /* SSHL, USHL */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_ushl, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sshl, size); - } - return; - case 0x0c: /* SMAX, UMAX */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_smax, size); - } - return; - case 0x0d: /* SMIN, UMIN */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umin, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_smin, size); - } - return; - case 0xe: /* SABD, UABD */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uabd, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sabd, size); - } - return; - case 0xf: /* SABA, UABA */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_uaba, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_saba, size); - } - return; - case 0x10: /* ADD, SUB */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_sub, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_add, size); - } - return; - case 0x13: /* MUL, PMUL */ - if (!u) { /* MUL */ - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_mul, size); - } else { /* PMUL */ - gen_gvec_op3_ool(s, is_q, rd, rn, rm, 0, gen_helper_gvec_pmul_b); - } - return; - case 0x12: /* MLA, MLS */ - if (u) { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_mls, size); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_mla, size); - } - return; - case 0x16: /* SQDMULH, SQRDMULH */ - { - static gen_helper_gvec_3_ptr * const fns[2][2] = { - { gen_helper_neon_sqdmulh_h, gen_helper_neon_sqrdmulh_h }, - { gen_helper_neon_sqdmulh_s, gen_helper_neon_sqrdmulh_s }, - }; - gen_gvec_op3_qc(s, is_q, rd, rn, rm, fns[size - 1][u]); - } - return; - case 0x11: - if (!u) { /* CMTST */ - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_cmtst, size); - return; - } - /* else CMEQ */ - cond = TCG_COND_EQ; - goto do_gvec_cmp; - case 0x06: /* CMGT, CMHI */ - cond = u ? TCG_COND_GTU : TCG_COND_GT; - goto do_gvec_cmp; - case 0x07: /* CMGE, CMHS */ - cond = u ? TCG_COND_GEU : TCG_COND_GE; - do_gvec_cmp: - tcg_gen_gvec_cmp(cond, size, vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - is_q ? 16 : 8, vec_full_reg_size(s)); - return; - } - - if (size == 3) { - assert(is_q); - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - - 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++) { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - NeonGenTwoOpFn *genfn = NULL; - NeonGenTwoOpEnvFn *genenvfn = NULL; - - read_vec_element_i32(s, tcg_op1, rn, pass, MO_32); - read_vec_element_i32(s, tcg_op2, rm, pass, MO_32); - - switch (opcode) { - case 0x0: /* SHADD, UHADD */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_hadd_s8, gen_helper_neon_hadd_u8 }, - { gen_helper_neon_hadd_s16, gen_helper_neon_hadd_u16 }, - { gen_helper_neon_hadd_s32, gen_helper_neon_hadd_u32 }, - }; - genfn = fns[size][u]; - break; - } - case 0x2: /* SRHADD, URHADD */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_rhadd_s8, gen_helper_neon_rhadd_u8 }, - { gen_helper_neon_rhadd_s16, gen_helper_neon_rhadd_u16 }, - { gen_helper_neon_rhadd_s32, gen_helper_neon_rhadd_u32 }, - }; - genfn = fns[size][u]; - break; - } - case 0x4: /* SHSUB, UHSUB */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_hsub_s8, gen_helper_neon_hsub_u8 }, - { gen_helper_neon_hsub_s16, gen_helper_neon_hsub_u16 }, - { gen_helper_neon_hsub_s32, gen_helper_neon_hsub_u32 }, - }; - genfn = fns[size][u]; - break; - } - case 0x9: /* SQSHL, UQSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qshl_s8, gen_helper_neon_qshl_u8 }, - { gen_helper_neon_qshl_s16, gen_helper_neon_qshl_u16 }, - { gen_helper_neon_qshl_s32, gen_helper_neon_qshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - case 0xa: /* SRSHL, URSHL */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_rshl_s8, gen_helper_neon_rshl_u8 }, - { gen_helper_neon_rshl_s16, gen_helper_neon_rshl_u16 }, - { gen_helper_neon_rshl_s32, gen_helper_neon_rshl_u32 }, - }; - genfn = fns[size][u]; - break; - } - case 0xb: /* SQRSHL, UQRSHL */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qrshl_s8, gen_helper_neon_qrshl_u8 }, - { gen_helper_neon_qrshl_s16, gen_helper_neon_qrshl_u16 }, - { gen_helper_neon_qrshl_s32, gen_helper_neon_qrshl_u32 }, - }; - genenvfn = fns[size][u]; - break; - } - default: - g_assert_not_reached(); - } - - if (genenvfn) { - genenvfn(tcg_res, cpu_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); -} - -/* AdvSIMD three same - * 31 30 29 28 24 23 22 21 20 16 15 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+--------+---+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 | Rm | opcode | 1 | Rn | Rd | - * +---+---+---+-----------+------+---+------+--------+---+------+------+ - */ -static void disas_simd_three_reg_same(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 11, 5); - - switch (opcode) { - case 0x3: /* logic ops */ - disas_simd_3same_logic(s, insn); - break; - case 0x17: /* ADDP */ - case 0x14: /* SMAXP, UMAXP */ - case 0x15: /* SMINP, UMINP */ - { - /* Pairwise operations */ - int is_q = extract32(insn, 30, 1); - int u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - if (opcode == 0x17) { - if (u || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - } else { - if (size == 3) { - unallocated_encoding(s); - return; - } - } - handle_simd_3same_pair(s, is_q, u, opcode, size, rn, rm, rd); - break; - } - case 0x18 ... 0x31: - /* floating point ops, sz[1] and U are part of opcode */ - disas_simd_3same_float(s, insn); - break; - default: - disas_simd_3same_int(s, insn); - break; - } -} - -/* - * Advanced SIMD three same (ARMv8.2 FP16 variants) - * - * 31 30 29 28 24 23 22 21 20 16 15 14 13 11 10 9 5 4 0 - * +---+---+---+-----------+---------+------+-----+--------+---+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | a | 1 0 | Rm | 0 0 | opcode | 1 | Rn | Rd | - * +---+---+---+-----------+---------+------+-----+--------+---+------+------+ - * - * This includes FMULX, FCMEQ (register), FRECPS, FRSQRTS, FCMGE - * (register), FACGE, FABD, FCMGT (register) and FACGT. - * - */ -static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 11, 3); - int u = extract32(insn, 29, 1); - int a = extract32(insn, 23, 1); - int is_q = extract32(insn, 30, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - /* - * For these floating point ops, the U, a and opcode bits - * together indicate the operation. - */ - int fpopcode = opcode | (a << 3) | (u << 4); - int datasize = is_q ? 128 : 64; - int elements = datasize / 16; - bool pairwise; - TCGv_ptr fpst; - int pass; - - switch (fpopcode) { - case 0x0: /* FMAXNM */ - case 0x1: /* FMLA */ - case 0x2: /* FADD */ - case 0x3: /* FMULX */ - case 0x4: /* FCMEQ */ - case 0x6: /* FMAX */ - case 0x7: /* FRECPS */ - case 0x8: /* FMINNM */ - case 0x9: /* FMLS */ - case 0xa: /* FSUB */ - case 0xe: /* FMIN */ - case 0xf: /* FRSQRTS */ - case 0x13: /* FMUL */ - case 0x14: /* FCMGE */ - case 0x15: /* FACGE */ - case 0x17: /* FDIV */ - case 0x1a: /* FABD */ - case 0x1c: /* FCMGT */ - case 0x1d: /* FACGT */ - pairwise = false; - break; - case 0x10: /* FMAXNMP */ - case 0x12: /* FADDP */ - case 0x16: /* FMAXP */ - case 0x18: /* FMINNMP */ - case 0x1e: /* FMINP */ - pairwise = true; - break; - default: - unallocated_encoding(s); - return; - } - - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - fpst = fpstatus_ptr(FPST_FPCR_F16); - - if (pairwise) { - int maxpass = is_q ? 8 : 4; - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res[8]; - - for (pass = 0; pass < maxpass; pass++) { - int passreg = pass < (maxpass / 2) ? rn : rm; - int passelt = (pass << 1) & (maxpass - 1); - - read_vec_element_i32(s, tcg_op1, passreg, passelt, MO_16); - read_vec_element_i32(s, tcg_op2, passreg, passelt + 1, MO_16); - tcg_res[pass] = tcg_temp_new_i32(); - - switch (fpopcode) { - case 0x10: /* FMAXNMP */ - gen_helper_advsimd_maxnumh(tcg_res[pass], tcg_op1, tcg_op2, - fpst); - break; - case 0x12: /* FADDP */ - gen_helper_advsimd_addh(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x16: /* FMAXP */ - gen_helper_advsimd_maxh(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - case 0x18: /* FMINNMP */ - gen_helper_advsimd_minnumh(tcg_res[pass], tcg_op1, tcg_op2, - fpst); - break; - case 0x1e: /* FMINP */ - gen_helper_advsimd_minh(tcg_res[pass], tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - } - - 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(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op1, rn, pass, MO_16); - read_vec_element_i32(s, tcg_op2, rm, pass, MO_16); - - switch (fpopcode) { - case 0x0: /* FMAXNM */ - gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, MO_16); - gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, - fpst); - break; - case 0x2: /* FADD */ - gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x3: /* FMULX */ - gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x4: /* FCMEQ */ - gen_helper_advsimd_ceq_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x6: /* FMAX */ - gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x7: /* FRECPS */ - gen_helper_recpsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x8: /* FMINNM */ - gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x9: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); - read_vec_element_i32(s, tcg_res, rd, pass, MO_16); - gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_res, - fpst); - break; - case 0xa: /* FSUB */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xe: /* FMIN */ - gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0xf: /* FRSQRTS */ - gen_helper_rsqrtsf_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x13: /* FMUL */ - gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x14: /* FCMGE */ - gen_helper_advsimd_cge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x15: /* FACGE */ - gen_helper_advsimd_acge_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x17: /* FDIV */ - gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1a: /* FABD */ - gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); - tcg_gen_andi_i32(tcg_res, tcg_res, 0x7fff); - break; - case 0x1c: /* FCMGT */ - gen_helper_advsimd_cgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - case 0x1d: /* FACGT */ - gen_helper_advsimd_acgt_f16(tcg_res, tcg_op1, tcg_op2, fpst); - break; - default: - g_assert_not_reached(); - } - - 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); -} - -/* AdvSIMD three same extra - * 31 30 29 28 24 23 22 21 20 16 15 14 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ - * | 0 | Q | U | 0 1 1 1 0 | size | 0 | Rm | 1 | opcode | 1 | Rn | Rd | - * +---+---+---+-----------+------+---+------+---+--------+---+----+----+ - */ -static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) -{ - int rd = extract32(insn, 0, 5); - int rn = extract32(insn, 5, 5); - int opcode = extract32(insn, 11, 4); - int rm = extract32(insn, 16, 5); - int size = extract32(insn, 22, 2); - bool u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - bool feature; - int rot; - - switch (u * 16 + opcode) { - case 0x10: /* SQRDMLAH (vector) */ - case 0x11: /* SQRDMLSH (vector) */ - if (size != 1 && size != 2) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_rdm, s); - break; - case 0x02: /* SDOT (vector) */ - case 0x12: /* UDOT (vector) */ - if (size != MO_32) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_dp, s); - break; - case 0x03: /* USDOT */ - if (size != MO_32) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_i8mm, s); - break; - case 0x04: /* SMMLA */ - case 0x14: /* UMMLA */ - case 0x05: /* USMMLA */ - if (!is_q || size != MO_32) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_i8mm, s); - break; - case 0x18: /* FCMLA, #0 */ - case 0x19: /* FCMLA, #90 */ - case 0x1a: /* FCMLA, #180 */ - case 0x1b: /* FCMLA, #270 */ - case 0x1c: /* FCADD, #90 */ - case 0x1e: /* FCADD, #270 */ - if (size == 0 - || (size == 1 && !dc_isar_feature(aa64_fp16, s)) - || (size == 3 && !is_q)) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_fcma, s); - break; - case 0x1d: /* BFMMLA */ - if (size != MO_16 || !is_q) { - unallocated_encoding(s); - return; - } - feature = dc_isar_feature(aa64_bf16, s); - break; - case 0x1f: - switch (size) { - case 1: /* BFDOT */ - case 3: /* BFMLAL{B,T} */ - feature = dc_isar_feature(aa64_bf16, s); - break; - default: - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - if (!feature) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0x0: /* SQRDMLAH (vector) */ - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqrdmlah_qc, size); - return; - - case 0x1: /* SQRDMLSH (vector) */ - gen_gvec_fn3(s, is_q, rd, rn, rm, gen_gvec_sqrdmlsh_qc, size); - return; - - case 0x2: /* SDOT / UDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, - u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b); - return; - - case 0x3: /* USDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_usdot_b); - return; - - case 0x04: /* SMMLA, UMMLA */ - gen_gvec_op4_ool(s, 1, rd, rn, rm, rd, 0, - u ? gen_helper_gvec_ummla_b - : gen_helper_gvec_smmla_b); - return; - case 0x05: /* USMMLA */ - gen_gvec_op4_ool(s, 1, rd, rn, rm, rd, 0, gen_helper_gvec_usmmla_b); - return; - - case 0x8: /* FCMLA, #0 */ - case 0x9: /* FCMLA, #90 */ - case 0xa: /* FCMLA, #180 */ - case 0xb: /* FCMLA, #270 */ - rot = extract32(opcode, 0, 2); - switch (size) { - case 1: - gen_gvec_op4_fpst(s, is_q, rd, rn, rm, rd, true, rot, - gen_helper_gvec_fcmlah); - break; - case 2: - gen_gvec_op4_fpst(s, is_q, rd, rn, rm, rd, false, rot, - gen_helper_gvec_fcmlas); - break; - case 3: - gen_gvec_op4_fpst(s, is_q, rd, rn, rm, rd, false, rot, - gen_helper_gvec_fcmlad); - break; - default: - g_assert_not_reached(); - } - return; - - case 0xc: /* FCADD, #90 */ - case 0xe: /* FCADD, #270 */ - rot = extract32(opcode, 1, 1); - switch (size) { - case 1: - gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, - gen_helper_gvec_fcaddh); - break; - case 2: - gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, - gen_helper_gvec_fcadds); - break; - case 3: - gen_gvec_op3_fpst(s, is_q, rd, rn, rm, size == 1, rot, - gen_helper_gvec_fcaddd); - break; - default: - g_assert_not_reached(); - } - return; - - case 0xd: /* BFMMLA */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_bfmmla); - return; - case 0xf: - switch (size) { - case 1: /* BFDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, 0, gen_helper_gvec_bfdot); - break; - case 3: /* BFMLAL{B,T} */ - gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, false, is_q, - gen_helper_gvec_bfmlal); - break; - default: - g_assert_not_reached(); - } - return; - - default: - g_assert_not_reached(); - } -} - -static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, - int size, int rn, int rd) -{ - /* Handle 2-reg-misc ops which are widening (so each size element - * in the source becomes a 2*size element in the destination. - * The only instruction like this is FCVTL. - */ - int pass; - - if (size == 3) { - /* 32 -> 64 bit fp conversion */ - TCGv_i64 tcg_res[2]; - int srcelt = is_q ? 2 : 0; - - for (pass = 0; pass < 2; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - 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); - } - 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 */ - int srcelt = is_q ? 4 : 0; - TCGv_i32 tcg_res[4]; - TCGv_ptr fpst = fpstatus_ptr(FPST_FPCR); - TCGv_i32 ahp = get_ahp_flag(); - - for (pass = 0; pass < 4; pass++) { - tcg_res[pass] = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_res[pass], rn, srcelt + pass, MO_16); - gen_helper_vfp_fcvt_f16_to_f32(tcg_res[pass], tcg_res[pass], - fpst, ahp); - } - 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); - } -} - -static void handle_rev(DisasContext *s, int opcode, bool u, - bool is_q, int size, int rn, int rd) -{ - int op = (opcode << 1) | u; - int opsz = op + size; - int grp_size = 3 - opsz; - int dsize = is_q ? 128 : 64; - int i; - - if (opsz >= 3) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (size == 0) { - /* Special case bytes, use bswap op on each group of elements */ - int groups = dsize / (8 << grp_size); - - for (i = 0; i < groups; i++) { - TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - - read_vec_element(s, tcg_tmp, rn, i, grp_size); - switch (grp_size) { - case MO_16: - tcg_gen_bswap16_i64(tcg_tmp, tcg_tmp, TCG_BSWAP_IZ); - break; - case MO_32: - tcg_gen_bswap32_i64(tcg_tmp, tcg_tmp, TCG_BSWAP_IZ); - break; - case MO_64: - tcg_gen_bswap64_i64(tcg_tmp, tcg_tmp); - break; - default: - 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 { - int revmask = (1 << grp_size) - 1; - 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); - - 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); - - tcg_temp_free_i64(tcg_rd_hi); - tcg_temp_free_i64(tcg_rd); - tcg_temp_free_i64(tcg_rn); - } -} - -static void handle_2misc_pairwise(DisasContext *s, int opcode, bool u, - bool is_q, int size, int rn, int rd) -{ - /* Implement the pairwise operations from 2-misc: - * SADDLP, UADDLP, SADALP, UADALP. - * These all add pairs of elements in the input to produce a - * double-width result element in the output (possibly accumulating). - */ - bool accum = (opcode == 0x6); - int maxpass = is_q ? 2 : 1; - int pass; - TCGv_i64 tcg_res[2]; - - if (size == 2) { - /* 32 + 32 -> 64 op */ - MemOp memop = size + (u ? 0 : MO_SIGN); - - for (pass = 0; pass < maxpass; pass++) { - TCGv_i64 tcg_op1 = tcg_temp_new_i64(); - TCGv_i64 tcg_op2 = tcg_temp_new_i64(); - - tcg_res[pass] = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op1, rn, pass * 2, memop); - read_vec_element(s, tcg_op2, rn, pass * 2 + 1, memop); - tcg_gen_add_i64(tcg_res[pass], tcg_op1, tcg_op2); - if (accum) { - 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++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - NeonGenOne64OpFn *genfn; - static NeonGenOne64OpFn * const fns[2][2] = { - { gen_helper_neon_addlp_s8, gen_helper_neon_addlp_u8 }, - { gen_helper_neon_addlp_s16, gen_helper_neon_addlp_u16 }, - }; - - genfn = fns[size][u]; - - tcg_res[pass] = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - genfn(tcg_res[pass], tcg_op); - - if (accum) { - read_vec_element(s, tcg_op, rd, pass, MO_64); - if (size == 0) { - gen_helper_neon_addl_u16(tcg_res[pass], - tcg_res[pass], tcg_op); - } else { - gen_helper_neon_addl_u32(tcg_res[pass], - tcg_res[pass], tcg_op); - } - } - tcg_temp_free_i64(tcg_op); - } - } - if (!is_q) { - tcg_res[1] = tcg_constant_i64(0); - } - for (pass = 0; pass < 2; pass++) { - write_vec_element(s, tcg_res[pass], rd, pass, MO_64); - tcg_temp_free_i64(tcg_res[pass]); - } -} - -static void handle_shll(DisasContext *s, bool is_q, int size, int rn, int rd) -{ - /* Implement SHLL and SHLL2 */ - int pass; - int part = is_q ? 2 : 0; - TCGv_i64 tcg_res[2]; - - for (pass = 0; pass < 2; pass++) { - static NeonGenWidenFn * const widenfns[3] = { - gen_helper_neon_widen_u8, - gen_helper_neon_widen_u16, - tcg_gen_extu_i32_i64, - }; - NeonGenWidenFn *widenfn = widenfns[size]; - TCGv_i32 tcg_op = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, part + pass, MO_32); - 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]); - } -} - -/* AdvSIMD two reg misc - * 31 30 29 28 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+-----------+--------+-----+------+------+ - * | 0 | Q | U | 0 1 1 1 0 | size | 1 0 0 0 0 | opcode | 1 0 | Rn | Rd | - * +---+---+---+-----------+------+-----------+--------+-----+------+------+ - */ -static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - bool u = extract32(insn, 29, 1); - bool is_q = extract32(insn, 30, 1); - 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; - - switch (opcode) { - case 0x0: /* REV64, REV32 */ - case 0x1: /* REV16 */ - handle_rev(s, opcode, u, is_q, size, rn, rd); - return; - case 0x5: /* CNT, NOT, RBIT */ - if (u && size == 0) { - /* NOT */ - break; - } else if (u && size == 1) { - /* RBIT */ - break; - } else if (!u && size == 0) { - /* CNT */ - break; - } - unallocated_encoding(s); - return; - case 0x12: /* XTN, XTN2, SQXTUN, SQXTUN2 */ - case 0x14: /* SQXTN, SQXTN2, UQXTN, UQXTN2 */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - - handle_2misc_narrow(s, false, opcode, u, is_q, size, rn, rd); - return; - case 0x4: /* CLS, CLZ */ - if (size == 3) { - unallocated_encoding(s); - return; - } - break; - case 0x2: /* SADDLP, UADDLP */ - case 0x6: /* SADALP, UADALP */ - if (size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_pairwise(s, opcode, u, is_q, size, rn, rd); - return; - case 0x13: /* SHLL, SHLL2 */ - if (u == 0 || size == 3) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_shll(s, is_q, size, rn, rd); - return; - case 0xa: /* CMLT */ - if (u == 1) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x8: /* CMGT, CMGE */ - case 0x9: /* CMEQ, CMLE */ - case 0xb: /* ABS, NEG */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x3: /* SUQADD, USQADD */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_satacc(s, false, u, is_q, size, rn, rd); - return; - case 0x7: /* SQABS, SQNEG */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0xc ... 0xf: - case 0x16 ... 0x1f: - { - /* Floating point: U, size[1] and opcode indicate operation; - * size[0] indicates single or double precision. - */ - int is_double = extract32(size, 0, 1); - opcode |= (extract32(size, 1, 1) << 5) | (u << 6); - size = is_double ? 3 : 2; - switch (opcode) { - case 0x2f: /* FABS */ - case 0x6f: /* FNEG */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - { - bool is_signed = (opcode == 0x1d) ? true : false; - int elements = is_double ? 2 : is_q ? 4 : 2; - if (is_double && !is_q) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_simd_intfp_conv(s, rd, rn, elements, is_signed, 0, size); - return; - } - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - handle_2misc_fcmp_zero(s, opcode, false, u, is_q, size, rn, rd); - return; - case 0x7f: /* FSQRT */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - 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); - return; - } - break; - case 0x5c: /* FCVTAU */ - case 0x1c: /* FCVTAS */ - need_fpstatus = true; - need_rmode = true; - rmode = FPROUNDING_TIEAWAY; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x3c: /* URECPE */ - if (size == 3) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x3d: /* FRECPE */ - case 0x7d: /* FRSQRTE */ - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_reciprocal(s, opcode, false, u, is_q, size, rn, rd); - return; - case 0x56: /* FCVTXN, FCVTXN2 */ - if (size == 2) { - unallocated_encoding(s); - return; - } - /* fall through */ - case 0x16: /* FCVTN, FCVTN2 */ - /* handle_2misc_narrow does a 2*size -> size operation, but these - * instructions encode the source size rather than dest size. - */ - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); - return; - case 0x36: /* BFCVTN, BFCVTN2 */ - if (!dc_isar_feature(aa64_bf16, s) || size != 2) { - unallocated_encoding(s); - return; - } - if (!fp_access_check(s)) { - return; - } - handle_2misc_narrow(s, false, opcode, 0, is_q, size - 1, rn, rd); - return; - case 0x17: /* FCVTL, FCVTL2 */ - if (!fp_access_check(s)) { - return; - } - handle_2misc_widening(s, opcode, is_q, size, rn, rd); - return; - case 0x18: /* FRINTN */ - 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 */ - case 0x79: /* FRINTI */ - need_fpstatus = true; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x58: /* FRINTA */ - need_rmode = true; - rmode = FPROUNDING_TIEAWAY; - need_fpstatus = true; - if (size == 3 && !is_q) { - unallocated_encoding(s); - return; - } - break; - case 0x7c: /* URSQRTE */ - if (size == 3) { - unallocated_encoding(s); - return; - } - break; - case 0x1e: /* FRINT32Z */ - case 0x1f: /* FRINT64Z */ - need_rmode = true; - rmode = FPROUNDING_ZERO; - /* fall through */ - case 0x5e: /* FRINT32X */ - case 0x5f: /* FRINT64X */ - need_fpstatus = true; - if ((size == 3 && !is_q) || !dc_isar_feature(aa64_frint, s)) { - unallocated_encoding(s); - return; - } - break; - default: - unallocated_encoding(s); - return; - } - break; - } - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (need_fpstatus || need_rmode) { - 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); - } else { - tcg_rmode = NULL; - } - - switch (opcode) { - case 0x5: - if (u && size == 0) { /* NOT */ - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_not, 0); - return; - } - break; - case 0x8: /* CMGT, CMGE */ - if (u) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cge0, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cgt0, size); - } - return; - case 0x9: /* CMEQ, CMLE */ - if (u) { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_cle0, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_ceq0, size); - } - return; - case 0xa: /* CMLT */ - gen_gvec_fn2(s, is_q, rd, rn, gen_gvec_clt0, size); - return; - case 0xb: - if (u) { /* ABS, NEG */ - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_neg, size); - } else { - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_abs, size); - } - return; - } - - if (size == 3) { - /* All 64-bit element operations can be shared with scalar 2misc */ - int pass; - - /* Coverity claims (size == 3 && !is_q) has been eliminated - * from all paths leading to here. - */ - tcg_debug_assert(is_q); - for (pass = 0; pass < 2; pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - - handle_2misc_64(s, opcode, u, tcg_res, tcg_op, - 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; - - for (pass = 0; pass < (is_q ? 4 : 2); pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - - if (size == 2) { - /* Special cases for 32 bit elements */ - switch (opcode) { - case 0x4: /* CLS */ - if (u) { - tcg_gen_clzi_i32(tcg_res, tcg_op, 32); - } else { - tcg_gen_clrsb_i32(tcg_res, tcg_op); - } - break; - case 0x7: /* SQABS, SQNEG */ - if (u) { - gen_helper_neon_qneg_s32(tcg_res, cpu_env, tcg_op); - } else { - gen_helper_neon_qabs_s32(tcg_res, cpu_env, tcg_op); - } - break; - case 0x2f: /* FABS */ - gen_helper_vfp_abss(tcg_res, tcg_op); - break; - case 0x6f: /* FNEG */ - gen_helper_vfp_negs(tcg_res, tcg_op); - break; - case 0x7f: /* FSQRT */ - gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env); - break; - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_vfp_tosls(tcg_res, tcg_op, - tcg_constant_i32(0), tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_vfp_touls(tcg_res, tcg_op, - tcg_constant_i32(0), tcg_fpstatus); - break; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - gen_helper_rints(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x59: /* FRINTX */ - gen_helper_rints_exact(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x7c: /* URSQRTE */ - gen_helper_rsqrte_u32(tcg_res, tcg_op); - break; - case 0x1e: /* FRINT32Z */ - case 0x5e: /* FRINT32X */ - gen_helper_frint32_s(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x1f: /* FRINT64Z */ - case 0x5f: /* FRINT64X */ - gen_helper_frint64_s(tcg_res, tcg_op, tcg_fpstatus); - break; - default: - g_assert_not_reached(); - } - } else { - /* Use helpers for 8 and 16 bit elements */ - switch (opcode) { - case 0x5: /* CNT, RBIT */ - /* For these two insns size is part of the opcode specifier - * (handled earlier); they always operate on byte elements. - */ - if (u) { - gen_helper_neon_rbit_u8(tcg_res, tcg_op); - } else { - gen_helper_neon_cnt_u8(tcg_res, tcg_op); - } - break; - case 0x7: /* SQABS, SQNEG */ - { - NeonGenOneOpEnvFn *genfn; - static NeonGenOneOpEnvFn * const fns[2][2] = { - { gen_helper_neon_qabs_s8, gen_helper_neon_qneg_s8 }, - { gen_helper_neon_qabs_s16, gen_helper_neon_qneg_s16 }, - }; - genfn = fns[size][u]; - genfn(tcg_res, cpu_env, tcg_op); - break; - } - case 0x4: /* CLS, CLZ */ - if (u) { - if (size == 0) { - gen_helper_neon_clz_u8(tcg_res, tcg_op); - } else { - gen_helper_neon_clz_u16(tcg_res, tcg_op); - } - } else { - if (size == 0) { - gen_helper_neon_cls_s8(tcg_res, tcg_op); - } else { - gen_helper_neon_cls_s16(tcg_res, tcg_op); - } - } - break; - default: - g_assert_not_reached(); - } - } - - 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); - } -} - -/* AdvSIMD [scalar] two register miscellaneous (FP16) - * - * 31 30 29 28 27 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ - * | 0 | Q | U | S | 1 1 1 0 | a | 1 1 1 1 0 0 | opcode | 1 0 | Rn | Rd | - * +---+---+---+---+---------+---+-------------+--------+-----+------+------+ - * mask: 1000 1111 0111 1110 0000 1100 0000 0000 0x8f7e 0c00 - * val: 0000 1110 0111 1000 0000 1000 0000 0000 0x0e78 0800 - * - * This actually covers two groups where scalar access is governed by - * bit 28. A bunch of the instructions (float to integral) only exist - * in the vector form and are un-allocated for the scalar decode. Also - * in the scalar decode Q is always 1. - */ -static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) -{ - int fpop, opcode, a, u; - int rn, rd; - bool is_q; - bool is_scalar; - bool only_in_vector = false; - - int pass; - TCGv_i32 tcg_rmode = NULL; - TCGv_ptr tcg_fpstatus = NULL; - bool need_rmode = false; - bool need_fpst = true; - int rmode; - - if (!dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - - rd = extract32(insn, 0, 5); - rn = extract32(insn, 5, 5); - - a = extract32(insn, 23, 1); - u = extract32(insn, 29, 1); - is_scalar = extract32(insn, 28, 1); - is_q = extract32(insn, 30, 1); - - opcode = extract32(insn, 12, 5); - fpop = deposit32(opcode, 5, 1, a); - fpop = deposit32(fpop, 6, 1, u); - - switch (fpop) { - case 0x1d: /* SCVTF */ - case 0x5d: /* UCVTF */ - { - int elements; - - if (is_scalar) { - elements = 1; - } else { - elements = (is_q ? 8 : 4); - } - - if (!fp_access_check(s)) { - return; - } - handle_simd_intfp_conv(s, rd, rn, elements, !u, 0, MO_16); - return; - } - break; - case 0x2c: /* FCMGT (zero) */ - case 0x2d: /* FCMEQ (zero) */ - case 0x2e: /* FCMLT (zero) */ - case 0x6c: /* FCMGE (zero) */ - case 0x6d: /* FCMLE (zero) */ - handle_2misc_fcmp_zero(s, fpop, is_scalar, 0, is_q, MO_16, rn, rd); - return; - case 0x3d: /* FRECPE */ - 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; - case 0x59: /* FRINTX */ - case 0x79: /* FRINTI */ - only_in_vector = true; - /* 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 */ - case 0x6f: /* FNEG */ - need_fpst = false; - break; - case 0x7d: /* FRSQRTE */ - case 0x7f: /* FSQRT (vector) */ - break; - default: - unallocated_encoding(s); - return; - } - - - /* Check additional constraints for the scalar encoding */ - if (is_scalar) { - if (!is_q) { - unallocated_encoding(s); - return; - } - /* FRINTxx is only in the vector form */ - if (only_in_vector) { - unallocated_encoding(s); - return; - } - } - - if (!fp_access_check(s)) { - return; - } - - if (need_rmode || 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 (is_scalar) { - TCGv_i32 tcg_op = read_fp_hreg(s, rn); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - switch (fpop) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x3d: /* FRECPE */ - gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x3f: /* FRECPX */ - gen_helper_frecpx_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x6f: /* FNEG */ - tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - default: - g_assert_not_reached(); - } - - /* 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(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, MO_16); - - switch (fpop) { - case 0x1a: /* FCVTNS */ - case 0x1b: /* FCVTMS */ - case 0x1c: /* FCVTAS */ - case 0x3a: /* FCVTPS */ - case 0x3b: /* FCVTZS */ - gen_helper_advsimd_f16tosinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x3d: /* FRECPE */ - gen_helper_recpe_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x5a: /* FCVTNU */ - case 0x5b: /* FCVTMU */ - case 0x5c: /* FCVTAU */ - case 0x7a: /* FCVTPU */ - case 0x7b: /* FCVTZU */ - gen_helper_advsimd_f16touinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x18: /* FRINTN */ - case 0x19: /* FRINTM */ - case 0x38: /* FRINTP */ - case 0x39: /* FRINTZ */ - case 0x58: /* FRINTA */ - case 0x79: /* FRINTI */ - gen_helper_advsimd_rinth(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x59: /* FRINTX */ - gen_helper_advsimd_rinth_exact(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x2f: /* FABS */ - tcg_gen_andi_i32(tcg_res, tcg_op, 0x7fff); - break; - case 0x6f: /* FNEG */ - tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); - break; - case 0x7d: /* FRSQRTE */ - gen_helper_rsqrte_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - case 0x7f: /* FSQRT */ - gen_helper_sqrt_f16(tcg_res, tcg_op, tcg_fpstatus); - break; - default: - g_assert_not_reached(); - } - - 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); - } -} - -/* AdvSIMD scalar x indexed element - * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0 - * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+ - * | 0 1 | U | 1 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd | - * +-----+---+-----------+------+---+---+------+-----+---+---+------+------+ - * AdvSIMD vector x indexed element - * 31 30 29 28 24 23 22 21 20 19 16 15 12 11 10 9 5 4 0 - * +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+ - * | 0 | Q | U | 0 1 1 1 1 | size | L | M | Rm | opc | H | 0 | Rn | Rd | - * +---+---+---+-----------+------+---+---+------+-----+---+---+------+------+ - */ -static void disas_simd_indexed(DisasContext *s, uint32_t insn) -{ - /* This encoding has two kinds of instruction: - * normal, where we perform elt x idxelt => elt for each - * element in the vector - * long, where we perform elt x idxelt and generate a result of - * double the width of the input element - * The long ops have a 'part' specifier (ie come in INSN, INSN2 pairs). - */ - bool is_scalar = extract32(insn, 28, 1); - bool is_q = extract32(insn, 30, 1); - bool u = extract32(insn, 29, 1); - int size = extract32(insn, 22, 2); - int l = extract32(insn, 21, 1); - int m = extract32(insn, 20, 1); - /* Note that the Rm field here is only 4 bits, not 5 as it usually is */ - int rm = extract32(insn, 16, 4); - int opcode = extract32(insn, 12, 4); - int h = extract32(insn, 11, 1); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool is_long = false; - int is_fp = 0; - bool is_fp16 = false; - int index; - TCGv_ptr fpst; - - switch (16 * u + opcode) { - case 0x08: /* MUL */ - case 0x10: /* MLA */ - case 0x14: /* MLS */ - if (is_scalar) { - unallocated_encoding(s); - return; - } - break; - case 0x02: /* SMLAL, SMLAL2 */ - case 0x12: /* UMLAL, UMLAL2 */ - case 0x06: /* SMLSL, SMLSL2 */ - case 0x16: /* UMLSL, UMLSL2 */ - case 0x0a: /* SMULL, SMULL2 */ - case 0x1a: /* UMULL, UMULL2 */ - if (is_scalar) { - unallocated_encoding(s); - return; - } - is_long = true; - break; - case 0x03: /* SQDMLAL, SQDMLAL2 */ - case 0x07: /* SQDMLSL, SQDMLSL2 */ - case 0x0b: /* SQDMULL, SQDMULL2 */ - is_long = true; - break; - case 0x0c: /* SQDMULH */ - case 0x0d: /* SQRDMULH */ - break; - case 0x01: /* FMLA */ - case 0x05: /* FMLS */ - case 0x09: /* FMUL */ - case 0x19: /* FMULX */ - is_fp = 1; - break; - case 0x1d: /* SQRDMLAH */ - case 0x1f: /* SQRDMLSH */ - if (!dc_isar_feature(aa64_rdm, s)) { - unallocated_encoding(s); - return; - } - break; - case 0x0e: /* SDOT */ - case 0x1e: /* UDOT */ - if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_dp, s)) { - unallocated_encoding(s); - return; - } - break; - case 0x0f: - switch (size) { - case 0: /* SUDOT */ - case 2: /* USDOT */ - if (is_scalar || !dc_isar_feature(aa64_i8mm, s)) { - unallocated_encoding(s); - return; - } - size = MO_32; - break; - case 1: /* BFDOT */ - if (is_scalar || !dc_isar_feature(aa64_bf16, s)) { - unallocated_encoding(s); - return; - } - size = MO_32; - break; - case 3: /* BFMLAL{B,T} */ - if (is_scalar || !dc_isar_feature(aa64_bf16, s)) { - unallocated_encoding(s); - return; - } - /* can't set is_fp without other incorrect size checks */ - size = MO_16; - break; - default: - unallocated_encoding(s); - return; - } - break; - case 0x11: /* FCMLA #0 */ - case 0x13: /* FCMLA #90 */ - case 0x15: /* FCMLA #180 */ - case 0x17: /* FCMLA #270 */ - if (is_scalar || !dc_isar_feature(aa64_fcma, s)) { - unallocated_encoding(s); - return; - } - is_fp = 2; - break; - case 0x00: /* FMLAL */ - case 0x04: /* FMLSL */ - case 0x18: /* FMLAL2 */ - case 0x1c: /* FMLSL2 */ - if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_fhm, s)) { - unallocated_encoding(s); - return; - } - size = MO_16; - /* is_fp, but we pass cpu_env not fp_status. */ - break; - default: - unallocated_encoding(s); - return; - } - - switch (is_fp) { - case 1: /* normal fp */ - /* convert insn encoded size to MemOp size */ - switch (size) { - case 0: /* half-precision */ - size = MO_16; - is_fp16 = true; - break; - case MO_32: /* single precision */ - case MO_64: /* double precision */ - break; - default: - unallocated_encoding(s); - return; - } - break; - - case 2: /* complex fp */ - /* Each indexable element is a complex pair. */ - size += 1; - switch (size) { - case MO_32: - if (h && !is_q) { - unallocated_encoding(s); - return; - } - is_fp16 = true; - break; - case MO_64: - break; - default: - unallocated_encoding(s); - return; - } - break; - - default: /* integer */ - switch (size) { - case MO_8: - case MO_64: - unallocated_encoding(s); - return; - } - break; - } - if (is_fp16 && !dc_isar_feature(aa64_fp16, s)) { - unallocated_encoding(s); - return; - } - - /* Given MemOp size, adjust register and indexing. */ - switch (size) { - case MO_16: - index = h << 2 | l << 1 | m; - break; - case MO_32: - index = h << 1 | l; - rm |= m << 4; - break; - case MO_64: - if (l || !is_q) { - unallocated_encoding(s); - return; - } - index = h; - rm |= m << 4; - break; - default: - g_assert_not_reached(); - } - - if (!fp_access_check(s)) { - return; - } - - if (is_fp) { - fpst = fpstatus_ptr(is_fp16 ? FPST_FPCR_F16 : FPST_FPCR); - } else { - fpst = NULL; - } - - switch (16 * u + opcode) { - case 0x0e: /* SDOT */ - case 0x1e: /* UDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - u ? gen_helper_gvec_udot_idx_b - : gen_helper_gvec_sdot_idx_b); - return; - case 0x0f: - switch (extract32(insn, 22, 2)) { - case 0: /* SUDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - gen_helper_gvec_sudot_idx_b); - return; - case 1: /* BFDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - gen_helper_gvec_bfdot_idx); - return; - case 2: /* USDOT */ - gen_gvec_op4_ool(s, is_q, rd, rn, rm, rd, index, - gen_helper_gvec_usdot_idx_b); - return; - case 3: /* BFMLAL{B,T} */ - gen_gvec_op4_fpst(s, 1, rd, rn, rm, rd, 0, (index << 1) | is_q, - gen_helper_gvec_bfmlal_idx); - return; - } - g_assert_not_reached(); - case 0x11: /* FCMLA #0 */ - case 0x13: /* FCMLA #90 */ - case 0x15: /* FCMLA #180 */ - case 0x17: /* FCMLA #270 */ - { - int rot = extract32(insn, 13, 2); - int data = (index << 2) | rot; - tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, rd), fpst, - is_q ? 16 : 8, vec_full_reg_size(s), data, - size == MO_64 - ? gen_helper_gvec_fcmlas_idx - : gen_helper_gvec_fcmlah_idx); - tcg_temp_free_ptr(fpst); - } - return; - - case 0x00: /* FMLAL */ - case 0x04: /* FMLSL */ - case 0x18: /* FMLAL2 */ - case 0x1c: /* FMLSL2 */ - { - int is_s = extract32(opcode, 2, 1); - int is_2 = u; - 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, - is_q ? 16 : 8, vec_full_reg_size(s), - data, gen_helper_gvec_fmlal_idx_a64); - } - return; - - case 0x08: /* MUL */ - if (!is_long && !is_scalar) { - static gen_helper_gvec_3 * const fns[3] = { - gen_helper_gvec_mul_idx_h, - gen_helper_gvec_mul_idx_s, - gen_helper_gvec_mul_idx_d, - }; - tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - is_q ? 16 : 8, vec_full_reg_size(s), - index, fns[size - 1]); - return; - } - break; - - case 0x10: /* MLA */ - if (!is_long && !is_scalar) { - static gen_helper_gvec_4 * const fns[3] = { - gen_helper_gvec_mla_idx_h, - gen_helper_gvec_mla_idx_s, - gen_helper_gvec_mla_idx_d, - }; - tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, rd), - is_q ? 16 : 8, vec_full_reg_size(s), - index, fns[size - 1]); - return; - } - break; - - case 0x14: /* MLS */ - if (!is_long && !is_scalar) { - static gen_helper_gvec_4 * const fns[3] = { - gen_helper_gvec_mls_idx_h, - gen_helper_gvec_mls_idx_s, - gen_helper_gvec_mls_idx_d, - }; - tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), - vec_full_reg_offset(s, rd), - is_q ? 16 : 8, vec_full_reg_size(s), - index, fns[size - 1]); - return; - } - break; - } - - if (size == 3) { - TCGv_i64 tcg_idx = tcg_temp_new_i64(); - int pass; - - assert(is_fp && is_q && !is_long); - - read_vec_element(s, tcg_idx, rm, index, MO_64); - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_res = tcg_temp_new_i64(); - - read_vec_element(s, tcg_op, rn, pass, MO_64); - - switch (16 * u + opcode) { - case 0x05: /* FMLS */ - /* As usual for ARM, separate negation for fused multiply-add */ - gen_helper_vfp_negd(tcg_op, tcg_op); - /* fall through */ - case 0x01: /* FMLA */ - read_vec_element(s, tcg_res, rd, pass, MO_64); - gen_helper_vfp_muladdd(tcg_res, tcg_op, tcg_idx, tcg_res, fpst); - break; - case 0x09: /* FMUL */ - gen_helper_vfp_muld(tcg_res, tcg_op, tcg_idx, fpst); - break; - case 0x19: /* FMULX */ - gen_helper_vfp_mulxd(tcg_res, tcg_op, tcg_idx, fpst); - break; - default: - g_assert_not_reached(); - } - - 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. - * For the 16 bit scalar case we use the usual Neon helpers and - * rely on the fact that 0 op 0 == 0 with no side effects. - */ - TCGv_i32 tcg_idx = tcg_temp_new_i32(); - int pass, maxpasses; - - if (is_scalar) { - maxpasses = 1; - } else { - maxpasses = is_q ? 4 : 2; - } - - read_vec_element_i32(s, tcg_idx, rm, index, size); - - if (size == 1 && !is_scalar) { - /* The simplest way to handle the 16x16 indexed ops is to duplicate - * the index into both halves of the 32 bit tcg_idx and then use - * the usual Neon helpers. - */ - tcg_gen_deposit_i32(tcg_idx, tcg_idx, tcg_idx, 16, 16); - } - - for (pass = 0; pass < maxpasses; pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i32 tcg_res = tcg_temp_new_i32(); - - read_vec_element_i32(s, tcg_op, rn, pass, is_scalar ? size : MO_32); - - switch (16 * u + opcode) { - case 0x08: /* MUL */ - case 0x10: /* MLA */ - case 0x14: /* MLS */ - { - static NeonGenTwoOpFn * const fns[2][2] = { - { gen_helper_neon_add_u16, gen_helper_neon_sub_u16 }, - { tcg_gen_add_i32, tcg_gen_sub_i32 }, - }; - NeonGenTwoOpFn *genfn; - bool is_sub = opcode == 0x4; - - if (size == 1) { - gen_helper_neon_mul_u16(tcg_res, tcg_op, tcg_idx); - } else { - tcg_gen_mul_i32(tcg_res, tcg_op, tcg_idx); - } - if (opcode == 0x8) { - break; - } - read_vec_element_i32(s, tcg_op, rd, pass, MO_32); - genfn = fns[size - 1][is_sub]; - genfn(tcg_res, tcg_op, tcg_res); - break; - } - case 0x05: /* FMLS */ - case 0x01: /* FMLA */ - read_vec_element_i32(s, tcg_res, rd, pass, - is_scalar ? size : MO_32); - switch (size) { - case 1: - if (opcode == 0x5) { - /* As usual for ARM, separate negation for fused - * multiply-add */ - tcg_gen_xori_i32(tcg_op, tcg_op, 0x80008000); - } - if (is_scalar) { - gen_helper_advsimd_muladdh(tcg_res, tcg_op, tcg_idx, - tcg_res, fpst); - } else { - gen_helper_advsimd_muladd2h(tcg_res, tcg_op, tcg_idx, - tcg_res, fpst); - } - break; - case 2: - if (opcode == 0x5) { - /* As usual for ARM, separate negation for - * fused multiply-add */ - tcg_gen_xori_i32(tcg_op, tcg_op, 0x80000000); - } - gen_helper_vfp_muladds(tcg_res, tcg_op, tcg_idx, - tcg_res, fpst); - break; - default: - g_assert_not_reached(); - } - break; - case 0x09: /* FMUL */ - switch (size) { - case 1: - if (is_scalar) { - gen_helper_advsimd_mulh(tcg_res, tcg_op, - tcg_idx, fpst); - } else { - gen_helper_advsimd_mul2h(tcg_res, tcg_op, - tcg_idx, fpst); - } - break; - case 2: - gen_helper_vfp_muls(tcg_res, tcg_op, tcg_idx, fpst); - break; - default: - g_assert_not_reached(); - } - break; - case 0x19: /* FMULX */ - switch (size) { - case 1: - if (is_scalar) { - gen_helper_advsimd_mulxh(tcg_res, tcg_op, - tcg_idx, fpst); - } else { - gen_helper_advsimd_mulx2h(tcg_res, tcg_op, - tcg_idx, fpst); - } - break; - case 2: - gen_helper_vfp_mulxs(tcg_res, tcg_op, tcg_idx, fpst); - break; - default: - g_assert_not_reached(); - } - break; - case 0x0c: /* SQDMULH */ - if (size == 1) { - gen_helper_neon_qdmulh_s16(tcg_res, cpu_env, - tcg_op, tcg_idx); - } else { - gen_helper_neon_qdmulh_s32(tcg_res, cpu_env, - tcg_op, tcg_idx); - } - break; - case 0x0d: /* SQRDMULH */ - if (size == 1) { - gen_helper_neon_qrdmulh_s16(tcg_res, cpu_env, - tcg_op, tcg_idx); - } else { - gen_helper_neon_qrdmulh_s32(tcg_res, cpu_env, - tcg_op, tcg_idx); - } - break; - case 0x1d: /* SQRDMLAH */ - 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, - tcg_op, tcg_idx, tcg_res); - } else { - gen_helper_neon_qrdmlah_s32(tcg_res, cpu_env, - tcg_op, tcg_idx, tcg_res); - } - break; - case 0x1f: /* SQRDMLSH */ - 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, - tcg_op, tcg_idx, tcg_res); - } else { - gen_helper_neon_qrdmlsh_s32(tcg_res, cpu_env, - tcg_op, tcg_idx, tcg_res); - } - break; - default: - g_assert_not_reached(); - } - - if (is_scalar) { - write_fp_sreg(s, rd, tcg_res); - } 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 */ - TCGv_i64 tcg_res[2]; - int pass; - bool satop = extract32(opcode, 0, 1); - MemOp memop = MO_32; - - if (satop || !u) { - memop |= MO_SIGN; - } - - if (size == 2) { - TCGv_i64 tcg_idx = tcg_temp_new_i64(); - - read_vec_element(s, tcg_idx, rm, index, memop); - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - TCGv_i64 tcg_op = tcg_temp_new_i64(); - TCGv_i64 tcg_passres; - int passelt; - - if (is_scalar) { - passelt = 0; - } else { - passelt = pass + (is_q * 2); - } - - read_vec_element(s, tcg_op, rn, passelt, memop); - - tcg_res[pass] = tcg_temp_new_i64(); - - if (opcode == 0xa || opcode == 0xb) { - /* Non-accumulating ops */ - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - 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, - tcg_passres, tcg_passres); - } - - if (opcode == 0xa || opcode == 0xb) { - continue; - } - - /* Accumulating op: handle accumulate step */ - read_vec_element(s, tcg_res[pass], rd, pass, MO_64); - - switch (opcode) { - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - tcg_gen_add_i64(tcg_res[pass], tcg_res[pass], tcg_passres); - break; - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - tcg_gen_sub_i64(tcg_res[pass], tcg_res[pass], tcg_passres); - break; - case 0x7: /* SQDMLSL, SQDMLSL2 */ - 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, - 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 { - TCGv_i32 tcg_idx = tcg_temp_new_i32(); - - assert(size == 1); - read_vec_element_i32(s, tcg_idx, rm, index, size); - - if (!is_scalar) { - /* The simplest way to handle the 16x16 indexed ops is to - * duplicate the index into both halves of the 32 bit tcg_idx - * and then use the usual Neon helpers. - */ - tcg_gen_deposit_i32(tcg_idx, tcg_idx, tcg_idx, 16, 16); - } - - for (pass = 0; pass < (is_scalar ? 1 : 2); pass++) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); - TCGv_i64 tcg_passres; - - if (is_scalar) { - read_vec_element_i32(s, tcg_op, rn, pass, size); - } else { - read_vec_element_i32(s, tcg_op, rn, - pass + (is_q * 2), MO_32); - } - - tcg_res[pass] = tcg_temp_new_i64(); - - if (opcode == 0xa || opcode == 0xb) { - /* Non-accumulating ops */ - tcg_passres = tcg_res[pass]; - } else { - tcg_passres = tcg_temp_new_i64(); - } - - if (memop & MO_SIGN) { - gen_helper_neon_mull_s16(tcg_passres, tcg_op, tcg_idx); - } else { - gen_helper_neon_mull_u16(tcg_passres, tcg_op, tcg_idx); - } - if (satop) { - gen_helper_neon_addl_saturate_s32(tcg_passres, cpu_env, - tcg_passres, tcg_passres); - } - tcg_temp_free_i32(tcg_op); - - if (opcode == 0xa || opcode == 0xb) { - continue; - } - - /* Accumulating op: handle accumulate step */ - read_vec_element(s, tcg_res[pass], rd, pass, MO_64); - - switch (opcode) { - case 0x2: /* SMLAL, SMLAL2, UMLAL, UMLAL2 */ - gen_helper_neon_addl_u32(tcg_res[pass], tcg_res[pass], - tcg_passres); - break; - case 0x6: /* SMLSL, SMLSL2, UMLSL, UMLSL2 */ - gen_helper_neon_subl_u32(tcg_res[pass], tcg_res[pass], - tcg_passres); - break; - case 0x7: /* SQDMLSL, SQDMLSL2 */ - 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, - 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]); - } - } - - if (is_scalar) { - tcg_res[1] = tcg_constant_i64(0); - } - - 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 - * 31 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----------------+------+-----------+--------+-----+------+------+ - * | 0 1 0 0 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd | - * +-----------------+------+-----------+--------+-----+------+------+ - */ -static void disas_crypto_aes(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - 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; - - if (!dc_isar_feature(aa64_aes, s) || size != 0) { - unallocated_encoding(s); - return; - } - - 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; - break; - case 0x7: /* AESIMC */ - decrypt = 1; - genfn2 = gen_helper_crypto_aesmc; - break; - default: - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - if (genfn2) { - gen_gvec_op2_ool(s, true, rd, rn, decrypt, genfn2); - } else { - gen_gvec_op3_ool(s, true, rd, rd, rn, decrypt, genfn3); - } -} - -/* Crypto three-reg SHA - * 31 24 23 22 21 20 16 15 14 12 11 10 9 5 4 0 - * +-----------------+------+---+------+---+--------+-----+------+------+ - * | 0 1 0 1 1 1 1 0 | size | 0 | Rm | 0 | opcode | 0 0 | Rn | Rd | - * +-----------------+------+---+------+---+--------+-----+------+------+ - */ -static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 3); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - gen_helper_gvec_3 *genfn; - bool feature; - - if (size != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0: /* SHA1C */ - genfn = gen_helper_crypto_sha1c; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 1: /* SHA1P */ - genfn = gen_helper_crypto_sha1p; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 2: /* SHA1M */ - genfn = gen_helper_crypto_sha1m; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 3: /* SHA1SU0 */ - genfn = gen_helper_crypto_sha1su0; - feature = dc_isar_feature(aa64_sha1, s); - break; - case 4: /* SHA256H */ - genfn = gen_helper_crypto_sha256h; - feature = dc_isar_feature(aa64_sha256, s); - break; - case 5: /* SHA256H2 */ - genfn = gen_helper_crypto_sha256h2; - feature = dc_isar_feature(aa64_sha256, s); - break; - case 6: /* SHA256SU1 */ - genfn = gen_helper_crypto_sha256su1; - feature = dc_isar_feature(aa64_sha256, s); - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - gen_gvec_op3_ool(s, true, rd, rn, rm, 0, genfn); -} - -/* Crypto two-reg SHA - * 31 24 23 22 21 17 16 12 11 10 9 5 4 0 - * +-----------------+------+-----------+--------+-----+------+------+ - * | 0 1 0 1 1 1 1 0 | size | 1 0 1 0 0 | opcode | 1 0 | Rn | Rd | - * +-----------------+------+-----------+--------+-----+------+------+ - */ -static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn) -{ - int size = extract32(insn, 22, 2); - int opcode = extract32(insn, 12, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - gen_helper_gvec_2 *genfn; - bool feature; - - if (size != 0) { - unallocated_encoding(s); - return; - } - - switch (opcode) { - case 0: /* SHA1H */ - feature = dc_isar_feature(aa64_sha1, s); - genfn = gen_helper_crypto_sha1h; - break; - case 1: /* SHA1SU1 */ - feature = dc_isar_feature(aa64_sha1, s); - genfn = gen_helper_crypto_sha1su1; - break; - case 2: /* SHA256SU0 */ - feature = dc_isar_feature(aa64_sha256, s); - genfn = gen_helper_crypto_sha256su0; - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - gen_gvec_op2_ool(s, true, rd, rn, 0, genfn); -} - -static void gen_rax1_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) -{ - tcg_gen_rotli_i64(d, m, 1); - tcg_gen_xor_i64(d, d, n); -} - -static void gen_rax1_vec(unsigned vece, TCGv_vec d, TCGv_vec n, TCGv_vec m) -{ - tcg_gen_rotli_vec(vece, d, m, 1); - tcg_gen_xor_vec(vece, d, d, n); -} - -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) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_rotli_vec, 0 }; - static const GVecGen3 op = { - .fni8 = gen_rax1_i64, - .fniv = gen_rax1_vec, - .opt_opc = vecop_list, - .fno = gen_helper_crypto_rax1, - .vece = MO_64, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &op); -} - -/* Crypto three-reg SHA512 - * 31 21 20 16 15 14 13 12 11 10 9 5 4 0 - * +-----------------------+------+---+---+-----+--------+------+------+ - * | 1 1 0 0 1 1 1 0 0 1 1 | Rm | 1 | O | 0 0 | opcode | Rn | Rd | - * +-----------------------+------+---+---+-----+--------+------+------+ - */ -static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 10, 2); - int o = extract32(insn, 14, 1); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; - gen_helper_gvec_3 *oolfn = NULL; - GVecGen3Fn *gvecfn = NULL; - - if (o == 0) { - switch (opcode) { - case 0: /* SHA512H */ - feature = dc_isar_feature(aa64_sha512, s); - oolfn = gen_helper_crypto_sha512h; - break; - case 1: /* SHA512H2 */ - feature = dc_isar_feature(aa64_sha512, s); - oolfn = gen_helper_crypto_sha512h2; - break; - case 2: /* SHA512SU1 */ - feature = dc_isar_feature(aa64_sha512, s); - oolfn = gen_helper_crypto_sha512su1; - break; - case 3: /* RAX1 */ - feature = dc_isar_feature(aa64_sha3, s); - gvecfn = gen_gvec_rax1; - break; - default: - g_assert_not_reached(); - } - } else { - switch (opcode) { - case 0: /* SM3PARTW1 */ - feature = dc_isar_feature(aa64_sm3, s); - oolfn = gen_helper_crypto_sm3partw1; - break; - case 1: /* SM3PARTW2 */ - feature = dc_isar_feature(aa64_sm3, s); - oolfn = gen_helper_crypto_sm3partw2; - break; - case 2: /* SM4EKEY */ - feature = dc_isar_feature(aa64_sm4, s); - oolfn = gen_helper_crypto_sm4ekey; - break; - default: - unallocated_encoding(s); - return; - } - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (oolfn) { - gen_gvec_op3_ool(s, true, rd, rn, rm, 0, oolfn); - } else { - gen_gvec_fn3(s, true, rd, rn, rm, gvecfn, MO_64); - } -} - -/* Crypto two-reg SHA512 - * 31 12 11 10 9 5 4 0 - * +-----------------------------------------+--------+------+------+ - * | 1 1 0 0 1 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 | opcode | Rn | Rd | - * +-----------------------------------------+--------+------+------+ - */ -static void disas_crypto_two_reg_sha512(DisasContext *s, uint32_t insn) -{ - int opcode = extract32(insn, 10, 2); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; - - switch (opcode) { - case 0: /* SHA512SU0 */ - feature = dc_isar_feature(aa64_sha512, s); - break; - case 1: /* SM4E */ - feature = dc_isar_feature(aa64_sm4, s); - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - switch (opcode) { - case 0: /* SHA512SU0 */ - gen_gvec_op2_ool(s, true, rd, rn, 0, gen_helper_crypto_sha512su0); - break; - case 1: /* SM4E */ - gen_gvec_op3_ool(s, true, rd, rd, rn, 0, gen_helper_crypto_sm4e); - break; - default: - g_assert_not_reached(); - } -} - -/* Crypto four-register - * 31 23 22 21 20 16 15 14 10 9 5 4 0 - * +-------------------+-----+------+---+------+------+------+ - * | 1 1 0 0 1 1 1 0 0 | Op0 | Rm | 0 | Ra | Rn | Rd | - * +-------------------+-----+------+---+------+------+------+ - */ -static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) -{ - int op0 = extract32(insn, 21, 2); - int rm = extract32(insn, 16, 5); - int ra = extract32(insn, 10, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - bool feature; - - switch (op0) { - case 0: /* EOR3 */ - case 1: /* BCAX */ - feature = dc_isar_feature(aa64_sha3, s); - break; - case 2: /* SM3SS1 */ - feature = dc_isar_feature(aa64_sm3, s); - break; - default: - unallocated_encoding(s); - return; - } - - if (!feature) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - if (op0 < 2) { - TCGv_i64 tcg_op1, tcg_op2, tcg_op3, tcg_res[2]; - int pass; - - tcg_op1 = tcg_temp_new_i64(); - tcg_op2 = tcg_temp_new_i64(); - tcg_op3 = tcg_temp_new_i64(); - tcg_res[0] = tcg_temp_new_i64(); - tcg_res[1] = tcg_temp_new_i64(); - - for (pass = 0; pass < 2; pass++) { - read_vec_element(s, tcg_op1, rn, pass, MO_64); - read_vec_element(s, tcg_op2, rm, pass, MO_64); - read_vec_element(s, tcg_op3, ra, pass, MO_64); - - if (op0 == 0) { - /* EOR3 */ - tcg_gen_xor_i64(tcg_res[pass], tcg_op2, tcg_op3); - } else { - /* BCAX */ - tcg_gen_andc_i64(tcg_res[pass], tcg_op2, tcg_op3); - } - tcg_gen_xor_i64(tcg_res[pass], tcg_res[pass], tcg_op1); - } - 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; - - tcg_op1 = tcg_temp_new_i32(); - tcg_op2 = tcg_temp_new_i32(); - tcg_op3 = tcg_temp_new_i32(); - tcg_res = tcg_temp_new_i32(); - tcg_zero = tcg_constant_i32(0); - - read_vec_element_i32(s, tcg_op1, rn, 3, MO_32); - read_vec_element_i32(s, tcg_op2, rm, 3, MO_32); - read_vec_element_i32(s, tcg_op3, ra, 3, MO_32); - - tcg_gen_rotri_i32(tcg_res, tcg_op1, 20); - tcg_gen_add_i32(tcg_res, tcg_res, tcg_op2); - tcg_gen_add_i32(tcg_res, tcg_res, tcg_op3); - tcg_gen_rotri_i32(tcg_res, tcg_res, 25); - - write_vec_element_i32(s, tcg_zero, rd, 0, MO_32); - 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); - } -} - -/* Crypto XAR - * 31 21 20 16 15 10 9 5 4 0 - * +-----------------------+------+--------+------+------+ - * | 1 1 0 0 1 1 1 0 1 0 0 | Rm | imm6 | Rn | Rd | - * +-----------------------+------+--------+------+------+ - */ -static void disas_crypto_xar(DisasContext *s, uint32_t insn) -{ - int rm = extract32(insn, 16, 5); - int imm6 = extract32(insn, 10, 6); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (!dc_isar_feature(aa64_sha3, s)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - gen_gvec_xar(MO_64, vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_full_reg_offset(s, rm), imm6, 16, - vec_full_reg_size(s)); -} - -/* Crypto three-reg imm2 - * 31 21 20 16 15 14 13 12 11 10 9 5 4 0 - * +-----------------------+------+-----+------+--------+------+------+ - * | 1 1 0 0 1 1 1 0 0 1 0 | Rm | 1 0 | imm2 | opcode | Rn | Rd | - * +-----------------------+------+-----+------+--------+------+------+ - */ -static void disas_crypto_three_reg_imm2(DisasContext *s, uint32_t insn) -{ - static gen_helper_gvec_3 * const fns[4] = { - gen_helper_crypto_sm3tt1a, gen_helper_crypto_sm3tt1b, - gen_helper_crypto_sm3tt2a, gen_helper_crypto_sm3tt2b, - }; - int opcode = extract32(insn, 10, 2); - int imm2 = extract32(insn, 12, 2); - int rm = extract32(insn, 16, 5); - int rn = extract32(insn, 5, 5); - int rd = extract32(insn, 0, 5); - - if (!dc_isar_feature(aa64_sm3, s)) { - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - - gen_gvec_op3_ool(s, true, rd, rn, rm, imm2, fns[opcode]); -} - -/* C3.6 Data processing - SIMD, inc Crypto - * - * As the decode gets a little complex we are using a table based - * approach for this part of the decode. - */ -static const AArch64DecodeTable data_proc_simd[] = { - /* pattern , mask , fn */ - { 0x0e200400, 0x9f200400, disas_simd_three_reg_same }, - { 0x0e008400, 0x9f208400, disas_simd_three_reg_same_extra }, - { 0x0e200000, 0x9f200c00, disas_simd_three_reg_diff }, - { 0x0e200800, 0x9f3e0c00, disas_simd_two_reg_misc }, - { 0x0e300800, 0x9f3e0c00, disas_simd_across_lanes }, - { 0x0e000400, 0x9fe08400, disas_simd_copy }, - { 0x0f000000, 0x9f000400, disas_simd_indexed }, /* vector indexed */ - /* simd_mod_imm decode is a subset of simd_shift_imm, so must precede it */ - { 0x0f000400, 0x9ff80400, disas_simd_mod_imm }, - { 0x0f000400, 0x9f800400, disas_simd_shift_imm }, - { 0x0e000000, 0xbf208c00, disas_simd_tb }, - { 0x0e000800, 0xbf208c00, disas_simd_zip_trn }, - { 0x2e000000, 0xbf208400, disas_simd_ext }, - { 0x5e200400, 0xdf200400, disas_simd_scalar_three_reg_same }, - { 0x5e008400, 0xdf208400, disas_simd_scalar_three_reg_same_extra }, - { 0x5e200000, 0xdf200c00, disas_simd_scalar_three_reg_diff }, - { 0x5e200800, 0xdf3e0c00, disas_simd_scalar_two_reg_misc }, - { 0x5e300800, 0xdf3e0c00, disas_simd_scalar_pairwise }, - { 0x5e000400, 0xdfe08400, disas_simd_scalar_copy }, - { 0x5f000000, 0xdf000400, disas_simd_indexed }, /* scalar indexed */ - { 0x5f000400, 0xdf800400, disas_simd_scalar_shift_imm }, - { 0x4e280800, 0xff3e0c00, disas_crypto_aes }, - { 0x5e000000, 0xff208c00, disas_crypto_three_reg_sha }, - { 0x5e280800, 0xff3e0c00, disas_crypto_two_reg_sha }, - { 0xce608000, 0xffe0b000, disas_crypto_three_reg_sha512 }, - { 0xcec08000, 0xfffff000, disas_crypto_two_reg_sha512 }, - { 0xce000000, 0xff808000, disas_crypto_four_reg }, - { 0xce800000, 0xffe00000, disas_crypto_xar }, - { 0xce408000, 0xffe0c000, disas_crypto_three_reg_imm2 }, - { 0x0e400400, 0x9f60c400, disas_simd_three_reg_same_fp16 }, - { 0x0e780800, 0x8f7e0c00, disas_simd_two_reg_misc_fp16 }, - { 0x5e400400, 0xdf60c400, disas_simd_scalar_three_reg_same_fp16 }, - { 0x00000000, 0x00000000, NULL } -}; - -static void disas_data_proc_simd(DisasContext *s, uint32_t insn) -{ - /* Note that this is called with all non-FP cases from - * table C3-6 so it must UNDEF for entries not specifically - * allocated to instructions in that table. - */ - AArch64DecodeFn *fn = lookup_disas_fn(&data_proc_simd[0], insn); - if (fn) { - fn(s, insn); - } else { - unallocated_encoding(s); - } -} - -/* C3.6 Data processing - SIMD and floating point */ -static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) -{ - if (extract32(insn, 28, 1) == 1 && extract32(insn, 30, 1) == 0) { - disas_data_proc_fp(s, insn); - } else { - /* SIMD, including crypto */ - disas_data_proc_simd(s, insn); - } -} - -/* - * Include the generated SME FA64 decoder. - */ - -#include "decode-sme-fa64.c.inc" - -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 - * @s: The DisasContext - * - * Return true if the page is guarded. - */ -static bool is_guarded_page(CPUARMState *env, DisasContext *s) -{ - uint64_t addr = s->base.pc_first; -#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); - int flags; - - /* - * We test this immediately after reading an insn, which means - * that the TLB entry must be present and valid, and thus this - * access will never raise an exception. - */ - flags = probe_access_full(env, addr, MMU_INST_FETCH, mmu_idx, - false, &host, &full, 0); - assert(!(flags & TLB_INVALID_MASK)); - - return full->guarded; -#endif -} - -/** - * btype_destination_ok: - * @insn: The instruction at the branch destination - * @bt: SCTLR_ELx.BT - * @btype: PSTATE.BTYPE, and is non-zero - * - * On a guarded page, there are a limited number of insns - * that may be present at the branch target: - * - branch target identifiers, - * - paciasp, pacibsp, - * - BRK insn - * - HLT insn - * Anything else causes a Branch Target Exception. - * - * Return true if the branch is compatible, false to raise BTITRAP. - */ -static bool btype_destination_ok(uint32_t insn, bool bt, int btype) -{ - if ((insn & 0xfffff01fu) == 0xd503201fu) { - /* HINT space */ - switch (extract32(insn, 5, 7)) { - case 0b011001: /* PACIASP */ - case 0b011011: /* PACIBSP */ - /* - * If SCTLR_ELx.BT, then PACI*SP are not compatible - * with btype == 3. Otherwise all btype are ok. - */ - return !bt || btype != 3; - case 0b100000: /* BTI */ - /* Not compatible with any btype. */ - return false; - case 0b100010: /* BTI c */ - /* Not compatible with btype == 3 */ - return btype != 3; - case 0b100100: /* BTI j */ - /* Not compatible with btype == 2 */ - return btype != 2; - case 0b100110: /* BTI jc */ - /* Compatible with any btype. */ - return true; - } - } else { - switch (insn & 0xffe0001fu) { - case 0xd4200000u: /* BRK */ - case 0xd4400000u: /* HLT */ - /* Give priority to the breakpoint exception. */ - return true; - } - } - return false; -} - -static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; - 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; - dc->thumb = false; - dc->sctlr_b = 0; - dc->be_data = EX_TBFLAG_ANY(tb_flags, BE_DATA) ? MO_BE : MO_LE; - dc->condexec_mask = 0; - dc->condexec_cond = 0; - core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); - dc->mmu_idx = core_to_aa64_mmu_idx(core_mmu_idx); - dc->tbii = EX_TBFLAG_A64(tb_flags, TBII); - dc->tbid = EX_TBFLAG_A64(tb_flags, TBID); - dc->tcma = EX_TBFLAG_A64(tb_flags, TCMA); - dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); -#if !defined(CONFIG_USER_ONLY) - dc->user = (dc->current_el == 0); -#endif - 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->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->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->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; - -#ifdef CONFIG_USER_ONLY - /* In sve_probe_page, we assume TBI is enabled. */ - tcg_debug_assert(dc->tbid & 1); -#endif - - /* Single step state. The code-generation logic here is: - * SS_ACTIVE == 0: - * generate code with no special handling for single-stepping (except - * that anything that can make us go to SS_ACTIVE == 1 must end the TB; - * this happens anyway because those changes are all system register or - * PSTATE writes). - * SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending) - * emit code for one insn - * emit code to clear PSTATE.SS - * emit code to generate software step exception for completed step - * end TB (as usual for having generated an exception) - * SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending) - * emit code to generate a software step exception - * end the TB - */ - dc->ss_active = EX_TBFLAG_ANY(tb_flags, SS_ACTIVE); - dc->pstate_ss = EX_TBFLAG_ANY(tb_flags, PSTATE__SS); - dc->is_ldex = false; - - /* Bound the number of insns to execute to those left on the page. */ - bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - - /* If architectural single step active, limit to 1. */ - if (dc->ss_active) { - 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) -{ -} - -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; - - if (TARGET_TB_PCREL) { - pc_arg &= ~TARGET_PAGE_MASK; - } - tcg_gen_insn_start(pc_arg, 0, 0); - dc->insn_start = tcg_last_op(); -} - -static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *s = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; - uint64_t pc = s->base.pc_next; - uint32_t insn; - - /* Singlestep exceptions have the highest priority. */ - if (s->ss_active && !s->pstate_ss) { - /* Singlestep state is Active-pending. - * If we're in this state at the start of a TB then either - * a) we just took an exception to an EL which is being debugged - * and this is the first insn in the exception handler - * b) debug exceptions were masked and we just unmasked them - * without changing EL (eg by clearing PSTATE.D) - * In either case we're going to take a swstep exception in the - * "did not step an insn" case, and so the syndrome ISV and EX - * bits should be zero. - */ - assert(s->base.num_insns == 1); - gen_swstep_exception(s, 0, 0); - s->base.is_jmp = DISAS_NORETURN; - s->base.pc_next = pc + 4; - return; - } - - if (pc & 3) { - /* - * PC alignment fault. This has priority over the instruction abort - * that we would receive from a translation fault via arm_ldl_code. - * This should only be possible after an indirect branch, at the - * start of the TB. - */ - assert(s->base.num_insns == 1); - gen_helper_exception_pc_alignment(cpu_env, tcg_constant_tl(pc)); - s->base.is_jmp = DISAS_NORETURN; - s->base.pc_next = QEMU_ALIGN_UP(pc, 4); - return; - } - - s->pc_curr = pc; - insn = arm_ldl_code(env, &s->base, pc, s->sctlr_b); - s->insn = insn; - s->base.pc_next = pc + 4; - - s->fp_access_checked = false; - s->sve_access_checked = false; - - if (s->pstate_il) { - /* - * Illegal execution state. This has priority over BTI - * exceptions, but comes after instruction abort exceptions. - */ - gen_exception_insn(s, 0, EXCP_UDEF, syn_illegalstate()); - return; - } - - if (dc_isar_feature(aa64_bti, s)) { - if (s->base.num_insns == 1) { - /* - * At the first insn of the TB, compute s->guarded_page. - * We delayed computing this until successfully reading - * the first insn of the TB, above. This (mostly) ensures - * that the softmmu tlb entry has been populated, and the - * page table GP bit is available. - * - * Note that we need to compute this even if btype == 0, - * because this value is used for BR instructions later - * where ENV is not available. - */ - s->guarded_page = is_guarded_page(env, s); - - /* First insn can have btype set to non-zero. */ - tcg_debug_assert(s->btype >= 0); - - /* - * Note that the Branch Target Exception has fairly high - * priority -- below debugging exceptions but above most - * everything else. This allows us to handle this now - * instead of waiting until the insn is otherwise decoded. - */ - if (s->btype != 0 - && s->guarded_page - && !btype_destination_ok(insn, s->bt, s->btype)) { - gen_exception_insn(s, 0, EXCP_UDEF, syn_btitrap(s->btype)); - return; - } - } else { - /* Not the first insn: btype must be 0. */ - tcg_debug_assert(s->btype == 0); - } - } - - s->is_nonstreaming = false; - if (s->sme_trap_nonstreaming) { - disas_sme_fa64(s, insn); - } - - switch (extract32(insn, 25, 4)) { - case 0x0: - if (!extract32(insn, 31, 1) || !disas_sme(s, insn)) { - unallocated_encoding(s); - } - break; - 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; - } - - /* if we allocated any temporaries, free them here */ - free_tmp_a64(s); - - /* - * After execution of most insns, btype is reset to 0. - * Note that we set btype == -1 when the insn sets btype. - */ - 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) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - if (unlikely(dc->ss_active)) { - /* Note that this means single stepping WFI doesn't halt the CPU. - * For conditional branch insns this is harmless unreachable code as - * gen_goto_tb() has already handled emitting the debug exception - * (and thus a tb-jump is not possible when singlestepping). - */ - switch (dc->base.is_jmp) { - default: - gen_a64_update_pc(dc, 4); - /* fall through */ - case DISAS_EXIT: - case DISAS_JUMP: - gen_step_complete_exception(dc); - break; - case DISAS_NORETURN: - break; - } - } else { - switch (dc->base.is_jmp) { - case DISAS_NEXT: - case DISAS_TOO_MANY: - gen_goto_tb(dc, 1, 4); - break; - default: - case DISAS_UPDATE_EXIT: - gen_a64_update_pc(dc, 4); - /* fall through */ - case DISAS_EXIT: - tcg_gen_exit_tb(NULL, 0); - break; - case DISAS_UPDATE_NOCHAIN: - gen_a64_update_pc(dc, 4); - /* fall through */ - case DISAS_JUMP: - tcg_gen_lookup_and_goto_ptr(); - break; - case DISAS_NORETURN: - case DISAS_SWI: - break; - case DISAS_WFE: - gen_a64_update_pc(dc, 4); - gen_helper_wfe(cpu_env); - break; - case DISAS_YIELD: - gen_a64_update_pc(dc, 4); - gen_helper_yield(cpu_env); - break; - case DISAS_WFI: - /* - * This is a special case because we don't want to just halt - * the CPU if trying to debug across a WFI. - */ - gen_a64_update_pc(dc, 4); - gen_helper_wfi(cpu_env, tcg_constant_i32(4)); - /* - * The helper doesn't necessarily throw an exception, but we - * must go back to the main loop to check for interrupts anyway. - */ - tcg_gen_exit_tb(NULL, 0); - break; - } - } -} - -static void aarch64_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); -} - -const TranslatorOps aarch64_translator_ops = { - .init_disas_context = aarch64_tr_init_disas_context, - .tb_start = aarch64_tr_tb_start, - .insn_start = aarch64_tr_insn_start, - .translate_insn = aarch64_tr_translate_insn, - .tb_stop = aarch64_tr_tb_stop, - .disas_log = aarch64_tr_disas_log, -}; diff --git a/target/arm/translate.c b/target/arm/translate.c deleted file mode 100644 index a06da05640..0000000000 --- a/target/arm/translate.c +++ /dev/null @@ -1,9941 +0,0 @@ -/* - * ARM translation - * - * Copyright (c) 2003 Fabrice Bellard - * Copyright (c) 2005-2007 CodeSourcery - * Copyright (c) 2007 OpenedHand, 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 "disas/disas.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "tcg/tcg-op-gvec.h" -#include "qemu/log.h" -#include "qemu/bitops.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" - - -#define ENABLE_ARCH_4T arm_dc_feature(s, ARM_FEATURE_V4T) -#define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) -/* currently all emulated v5 cores are also v5TE, so don't bother */ -#define ENABLE_ARCH_5TE arm_dc_feature(s, ARM_FEATURE_V5) -#define ENABLE_ARCH_5J dc_isar_feature(aa32_jazelle, s) -#define ENABLE_ARCH_6 arm_dc_feature(s, ARM_FEATURE_V6) -#define ENABLE_ARCH_6K arm_dc_feature(s, ARM_FEATURE_V6K) -#define ENABLE_ARCH_6T2 arm_dc_feature(s, ARM_FEATURE_THUMB2) -#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 */ -static TCGv_i32 cpu_R[16]; -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" }; - - -/* initialize TCG globals. */ -void arm_translate_init(void) -{ - int i; - - for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new_i32(cpu_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_exclusive_addr = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUARMState, exclusive_addr), "exclusive_addr"); - cpu_exclusive_val = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUARMState, exclusive_val), "exclusive_val"); - - a64_translate_init(); -} - -uint64_t asimd_imm_const(uint32_t imm, int cmode, int op) -{ - /* Expand the encoded constant as per AdvSIMDExpandImm pseudocode */ - switch (cmode) { - case 0: case 1: - /* no-op */ - break; - case 2: case 3: - imm <<= 8; - break; - case 4: case 5: - imm <<= 16; - break; - case 6: case 7: - imm <<= 24; - break; - case 8: case 9: - imm |= imm << 16; - break; - case 10: case 11: - imm = (imm << 8) | (imm << 24); - break; - case 12: - imm = (imm << 8) | 0xff; - break; - case 13: - imm = (imm << 16) | 0xffff; - break; - case 14: - if (op) { - /* - * This and cmode == 15 op == 1 are the only cases where - * the top and bottom 32 bits of the encoded constant differ. - */ - uint64_t imm64 = 0; - int n; - - for (n = 0; n < 8; n++) { - if (imm & (1 << n)) { - imm64 |= (0xffULL << (n * 8)); - } - } - return imm64; - } - imm |= (imm << 8) | (imm << 16) | (imm << 24); - break; - case 15: - if (op) { - /* Reserved encoding for AArch32; valid for AArch64 */ - uint64_t imm64 = (uint64_t)(imm & 0x3f) << 48; - if (imm & 0x80) { - imm64 |= 0x8000000000000000ULL; - } - if (imm & 0x40) { - imm64 |= 0x3fc0000000000000ULL; - } else { - imm64 |= 0x4000000000000000ULL; - } - return imm64; - } - imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19) - | ((imm & 0x40) ? (0x1f << 25) : (1 << 30)); - break; - } - if (op) { - imm = ~imm; - } - return dup_const(MO_32, imm); -} - -/* Generate a label used for skipping this instruction */ -void arm_gen_condlabel(DisasContext *s) -{ - if (!s->condjmp) { - s->condlabel = gen_disas_label(s); - s->condjmp = 1; - } -} - -/* Flags for the disas_set_da_iss info argument: - * lower bits hold the Rt register number, higher bits are flags. - */ -typedef enum ISSInfo { - ISSNone = 0, - ISSRegMask = 0x1f, - ISSInvalid = (1 << 5), - ISSIsAcqRel = (1 << 6), - ISSIsWrite = (1 << 7), - ISSIs16Bit = (1 << 8), -} ISSInfo; - -/* - * Store var into env + offset to a member with size bytes. - * Free var after use. - */ -void store_cpu_offset(TCGv_i32 var, int offset, int size) -{ - switch (size) { - case 1: - tcg_gen_st8_i32(var, cpu_env, offset); - break; - case 4: - tcg_gen_st_i32(var, cpu_env, offset); - break; - default: - g_assert_not_reached(); - } - tcg_temp_free_i32(var); -} - -/* Save the syndrome information for a Data Abort */ -static void disas_set_da_iss(DisasContext *s, MemOp memop, ISSInfo issinfo) -{ - uint32_t syn; - int sas = memop & MO_SIZE; - bool sse = memop & MO_SIGN; - bool is_acqrel = issinfo & ISSIsAcqRel; - bool is_write = issinfo & ISSIsWrite; - bool is_16bit = issinfo & ISSIs16Bit; - int srt = issinfo & ISSRegMask; - - if (issinfo & ISSInvalid) { - /* Some callsites want to conditionally provide ISS info, - * eg "only if this was not a writeback" - */ - return; - } - - if (srt == 15) { - /* For AArch32, insns where the src/dest is R15 never generate - * ISS information. Catching that here saves checking at all - * the call sites. - */ - return; - } - - syn = syn_data_abort_with_iss(0, sas, sse, srt, 0, is_acqrel, - 0, 0, 0, is_write, 0, is_16bit); - disas_set_insn_syndrome(s, syn); -} - -static inline int get_a32_user_mem_index(DisasContext *s) -{ - /* Return the core mmu_idx to use for A32/T32 "unprivileged load/store" - * insns: - * if PL2, UNPREDICTABLE (we choose to implement as if PL0) - * 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_MUser: - case ARMMMUIdx_MPriv: - return arm_to_core_mmu_idx(ARMMMUIdx_MUser); - case ARMMMUIdx_MUserNegPri: - case ARMMMUIdx_MPrivNegPri: - return arm_to_core_mmu_idx(ARMMMUIdx_MUserNegPri); - case ARMMMUIdx_MSUser: - case ARMMMUIdx_MSPriv: - return arm_to_core_mmu_idx(ARMMMUIdx_MSUser); - case ARMMMUIdx_MSUserNegPri: - case ARMMMUIdx_MSPrivNegPri: - return arm_to_core_mmu_idx(ARMMMUIdx_MSUserNegPri); - default: - g_assert_not_reached(); - } -} - -/* The pc_curr difference for an architectural jump. */ -static target_long jmp_diff(DisasContext *s, target_long diff) -{ - 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 (TARGET_TB_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) { - gen_pc_plus_diff(s, var, jmp_diff(s, 0)); - } else { - tcg_gen_mov_i32(var, cpu_R[reg]); - } -} - -/* - * Create a new temp, REG + OFS, except PC is ALIGN(PC, 4). - * This is used for load/store for which use of PC implies (literal), - * or ADD that implies ADR. - */ -TCGv_i32 add_reg_for_lit(DisasContext *s, int reg, int ofs) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - - if (reg == 15) { - /* - * 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); - } - return tmp; -} - -/* Set a CPU register. The source must be a temporary and will be - marked as dead. */ -void store_reg(DisasContext *s, int reg, TCGv_i32 var) -{ - if (reg == 15) { - /* In Thumb mode, we must ignore bit 0. - * In ARM mode, for ARMv4 and ARMv5, it is UNPREDICTABLE if bits [1:0] - * are not 0b00, but for ARMv6 and above, we must ignore bits [1:0]. - * We choose to ignore [1:0] in ARM mode for all architecture versions. - */ - 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); -} - -/* - * Variant of store_reg which applies v8M stack-limit checks before updating - * SP. If the check fails this will result in an exception being taken. - * We disable the stack checks for CONFIG_USER_ONLY because we have - * no idea what the stack limits should be in that case. - * If stack checking is not being done this just acts like store_reg(). - */ -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); - } -#endif - store_reg(s, 13, var); -} - -/* Value extensions. */ -#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var) -#define gen_uxth(var) tcg_gen_ext16u_i32(var, var) -#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var) -#define gen_sxth(var) tcg_gen_ext16s_i32(var, var) - -#define gen_sxtb16(var) gen_helper_sxtb16(var, var) -#define gen_uxtb16(var) gen_helper_uxtb16(var, var) - -void gen_set_cpsr(TCGv_i32 var, uint32_t mask) -{ - gen_helper_cpsr_write(cpu_env, var, tcg_constant_i32(mask)); -} - -static void gen_rebuild_hflags(DisasContext *s, bool new_el) -{ - bool m_profile = arm_dc_feature(s, ARM_FEATURE_M); - - if (new_el) { - if (m_profile) { - gen_helper_rebuild_hflags_m32_newel(cpu_env); - } else { - gen_helper_rebuild_hflags_a32_newel(cpu_env); - } - } else { - TCGv_i32 tcg_el = tcg_constant_i32(s->current_el); - if (m_profile) { - gen_helper_rebuild_hflags_m32(cpu_env, tcg_el); - } else { - gen_helper_rebuild_hflags_a32(cpu_env, tcg_el); - } - } -} - -static void gen_exception_internal(int excp) -{ - assert(excp_is_internal(excp)); - gen_helper_exception_internal(cpu_env, tcg_constant_i32(excp)); -} - -static void gen_singlestep_exception(DisasContext *s) -{ - /* We just completed step of an insn. Move from Active-not-pending - * to Active-pending, and then also take the swstep exception. - * This corresponds to making the (IMPDEF) choice to prioritize - * swstep exceptions over asynchronous exceptions taken to an exception - * level where debug is disabled. This choice has the advantage that - * we do not need to maintain internal state corresponding to the - * ISV/EX syndrome bits between completion of the step and generation - * of the exception, and our syndrome information is always correct. - */ - gen_ss_advance(s); - gen_swstep_exception(s, 1, s->is_ldex); - s->base.is_jmp = DISAS_NORETURN; -} - -void clear_eci_state(DisasContext *s) -{ - /* - * Clear any ECI/ICI state: used when a load multiple/store - * multiple insn executes. - */ - if (s->eci) { - store_cpu_field_constant(0, condexec_bits); - s->eci = 0; - } -} - -static void gen_smul_dual(TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 tmp1 = tcg_temp_new_i32(); - TCGv_i32 tmp2 = tcg_temp_new_i32(); - 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. */ -void gen_rev16(TCGv_i32 dest, TCGv_i32 var) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - TCGv_i32 mask = tcg_constant_i32(0x00ff00ff); - tcg_gen_shri_i32(tmp, var, 8); - tcg_gen_and_i32(tmp, tmp, mask); - 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. */ -static void gen_revsh(TCGv_i32 dest, TCGv_i32 var) -{ - tcg_gen_bswap16_i32(var, var, TCG_BSWAP_OS); -} - -/* Dual 16-bit add. Result placed in t0 and t1 is marked as dead. - tmp = (t0 ^ t1) & 0x8000; - t0 &= ~0x8000; - t1 &= ~0x8000; - t0 = (t0 + t1) ^ tmp; - */ - -static void gen_add16(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_xor_i32(tmp, t0, t1); - tcg_gen_andi_i32(tmp, tmp, 0x8000); - tcg_gen_andi_i32(t0, t0, ~0x8000); - 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. */ -static inline void gen_logic_CC(TCGv_i32 var) -{ - tcg_gen_mov_i32(cpu_NF, var); - tcg_gen_mov_i32(cpu_ZF, var); -} - -/* dest = T0 + T1 + CF. */ -static void gen_add_carry(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - tcg_gen_add_i32(dest, t0, t1); - tcg_gen_add_i32(dest, dest, cpu_CF); -} - -/* dest = T0 - T1 + CF - 1. */ -static void gen_sub_carry(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - tcg_gen_sub_i32(dest, t0, t1); - tcg_gen_add_i32(dest, dest, cpu_CF); - tcg_gen_subi_i32(dest, dest, 1); -} - -/* dest = T0 + T1. Compute C, N, V and Z flags */ -static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, 0); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, t1, tmp); - 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); -} - -/* dest = T0 + T1 + CF. Compute C, N, V and Z flags */ -static void gen_adc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - if (TCG_TARGET_HAS_add2_i32) { - tcg_gen_movi_i32(tmp, 0); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, cpu_CF, tmp); - tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1, tmp); - } else { - TCGv_i64 q0 = tcg_temp_new_i64(); - TCGv_i64 q1 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(q0, t0); - tcg_gen_extu_i32_i64(q1, t1); - tcg_gen_add_i64(q0, q0, q1); - 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); -} - -/* dest = T0 - T1. Compute C, N, V and Z flags */ -static void gen_sub_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 tmp; - tcg_gen_sub_i32(cpu_NF, t0, t1); - tcg_gen_mov_i32(cpu_ZF, cpu_NF); - tcg_gen_setcond_i32(TCG_COND_GEU, cpu_CF, t0, t1); - tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); - 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); -} - -/* dest = T0 + ~T1 + CF. Compute C, N, V and Z flags */ -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) \ -static void gen_##name(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) \ -{ \ - TCGv_i32 tmpd = tcg_temp_new_i32(); \ - TCGv_i32 tmp1 = tcg_temp_new_i32(); \ - TCGv_i32 zero = tcg_constant_i32(0); \ - tcg_gen_andi_i32(tmp1, t1, 0x1f); \ - tcg_gen_##name##_i32(tmpd, t0, tmp1); \ - tcg_gen_andi_i32(tmp1, t1, 0xe0); \ - tcg_gen_movcond_i32(TCG_COND_NE, dest, tmp1, zero, zero, tmpd); \ - tcg_temp_free_i32(tmpd); \ - tcg_temp_free_i32(tmp1); \ -} -GEN_SHIFT(shl) -GEN_SHIFT(shr) -#undef GEN_SHIFT - -static void gen_sar(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) -{ - TCGv_i32 tmp1 = tcg_temp_new_i32(); - - 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) -{ - tcg_gen_extract_i32(cpu_CF, var, shift, 1); -} - -/* Shift by immediate. Includes special handling for shift == 0. */ -static inline void gen_arm_shift_im(TCGv_i32 var, int shiftop, - int shift, int flags) -{ - switch (shiftop) { - case 0: /* LSL */ - if (shift != 0) { - if (flags) - shifter_out_im(var, 32 - shift); - tcg_gen_shli_i32(var, var, shift); - } - break; - case 1: /* LSR */ - if (shift == 0) { - if (flags) { - tcg_gen_shri_i32(cpu_CF, var, 31); - } - tcg_gen_movi_i32(var, 0); - } else { - if (flags) - shifter_out_im(var, shift - 1); - tcg_gen_shri_i32(var, var, shift); - } - break; - case 2: /* ASR */ - if (shift == 0) - shift = 32; - if (flags) - shifter_out_im(var, shift - 1); - if (shift == 32) - shift = 31; - tcg_gen_sari_i32(var, var, shift); - break; - case 3: /* ROR/RRX */ - if (shift != 0) { - if (flags) - shifter_out_im(var, shift - 1); - tcg_gen_rotri_i32(var, var, shift); break; - } else { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, cpu_CF, 31); - if (flags) - shifter_out_im(var, 0); - tcg_gen_shri_i32(var, var, 1); - tcg_gen_or_i32(var, var, tmp); - tcg_temp_free_i32(tmp); - } - } -}; - -static inline void gen_arm_shift_reg(TCGv_i32 var, int shiftop, - TCGv_i32 shift, int flags) -{ - 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; - } - } else { - switch (shiftop) { - case 0: - gen_shl(var, var, shift); - break; - case 1: - gen_shr(var, var, shift); - break; - case 2: - gen_sar(var, var, shift); - break; - case 3: tcg_gen_andi_i32(shift, shift, 0x1f); - tcg_gen_rotr_i32(var, var, shift); break; - } - } - tcg_temp_free_i32(shift); -} - -/* - * Generate a conditional based on ARM condition code cc. - * This is common between ARM and Aarch64 targets. - */ -void arm_test_cc(DisasCompare *cmp, int cc) -{ - TCGv_i32 value; - TCGCond cond; - bool global = true; - - switch (cc) { - case 0: /* eq: Z */ - case 1: /* ne: !Z */ - cond = TCG_COND_EQ; - value = cpu_ZF; - break; - - case 2: /* cs: C */ - case 3: /* cc: !C */ - cond = TCG_COND_NE; - value = cpu_CF; - break; - - case 4: /* mi: N */ - case 5: /* pl: !N */ - cond = TCG_COND_LT; - value = cpu_NF; - break; - - case 6: /* vs: V */ - case 7: /* vc: !V */ - cond = TCG_COND_LT; - value = cpu_VF; - break; - - case 8: /* hi: C && !Z */ - 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); - tcg_gen_and_i32(value, value, cpu_ZF); - break; - - case 10: /* ge: N == V -> N ^ V == 0 */ - case 11: /* lt: N != V -> N ^ V != 0 */ - /* 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; - - case 12: /* gt: !Z && N == V */ - 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); - tcg_gen_sari_i32(value, value, 31); - tcg_gen_andc_i32(value, cpu_ZF, value); - break; - - case 14: /* always */ - case 15: /* always */ - /* Use the ALWAYS condition, which will fold early. - * It doesn't matter what we use for the value. */ - cond = TCG_COND_ALWAYS; - value = cpu_ZF; - goto no_invert; - - default: - fprintf(stderr, "Bad condition code 0x%x\n", cc); - abort(); - } - - if (cc & 1) { - cond = tcg_invert_cond(cond); - } - - 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) -{ - tcg_gen_brcondi_i32(cmp->cond, cmp->value, 0, label); -} - -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) -{ - if (s->condexec_mask) { - uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1); - - store_cpu_field_constant(val, condexec_bits); - } -} - -void gen_update_pc(DisasContext *s, target_long diff) -{ - 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. */ -static inline void gen_bx(DisasContext *s, TCGv_i32 var) -{ - s->base.is_jmp = DISAS_JUMP; - 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; -} - -/* - * Set PC and Thumb state from var. var is marked as dead. - * For M-profile CPUs, include logic to detect exception-return - * branches and handle them. This is needed for Thumb POP/LDM to PC, LDR to PC, - * and BX reg, and no others, and happens only for code in Handler mode. - * The Security Extension also requires us to check for the FNC_RETURN - * which signals a function return from non-secure state; this can happen - * in both Handler and Thread mode. - * To avoid having to do multiple comparisons in inline generated code, - * we make the check we do here loose, so it will match for EXC_RETURN - * in Thread mode. For system emulation do_v7m_exception_exit() checks - * for these spurious cases and returns without doing anything (giving - * the same behaviour as for a branch to a non-magic address). - * - * In linux-user mode it is unclear what the right behaviour for an - * attempted FNC_RETURN should be, because in real hardware this will go - * directly to Secure code (ie not the Linux kernel) which will then treat - * the error in any way it chooses. For QEMU we opt to make the FNC_RETURN - * attempt behave the way it would on a CPU without the security extension, - * which is to say "like a normal branch". That means we can simply treat - * all branches as normal with no magic address behaviour. - */ -static inline void gen_bx_excret(DisasContext *s, TCGv_i32 var) -{ - /* Generate the same code here as for a simple bx, but flag via - * s->base.is_jmp that we need to do the rest of the work later. - */ - gen_bx(s, var); -#ifndef CONFIG_USER_ONLY - if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY) || - (s->v7m_handler_mode && arm_dc_feature(s, ARM_FEATURE_M))) { - s->base.is_jmp = DISAS_BX_EXCRET; - } -#endif -} - -static inline void gen_bx_excret_final_code(DisasContext *s) -{ - /* Generate the code to finish possible exception return and end the TB */ - DisasLabel excret_label = gen_disas_label(s); - uint32_t min_magic; - - if (arm_dc_feature(s, ARM_FEATURE_M_SECURITY)) { - /* Covers FNC_RETURN and EXC_RETURN magic */ - min_magic = FNC_RETURN_MIN_MAGIC; - } else { - /* EXC_RETURN magic only */ - min_magic = EXC_RETURN_MIN_MAGIC; - } - - /* 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.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); - } - 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() - * will read. Nothing else will be able to see those values because - * the cpu-exec main loop guarantees that we will always go straight - * from raising the exception to the exception-handling code. - * - * gen_ss_advance(s) does nothing on M profile currently but - * calling it is conceptually the right thing as we have executed - * this instruction (compare SWI, HVC, SMC handling). - */ - gen_ss_advance(s); - gen_exception_internal(EXCP_EXCEPTION_EXIT); -} - -static inline void gen_bxns(DisasContext *s, int rm) -{ - TCGv_i32 var = load_reg(s, 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_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, - * so we know that condexec == 0 (already set at the top of the TB) - * 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); - s->base.is_jmp = DISAS_EXIT; -} - -static inline void gen_blxns(DisasContext *s, int rm) -{ - TCGv_i32 var = load_reg(s, rm); - - /* We don't need to sync condexec state, for the same reason as bxns. - * We do however need to set the PC, because the blxns helper reads it. - * The blxns helper may throw an exception. - */ - gen_update_pc(s, curr_insn_len(s)); - gen_helper_v7m_blxns(cpu_env, var); - tcg_temp_free_i32(var); - s->base.is_jmp = DISAS_EXIT; -} - -/* Variant of store_reg which uses branch&exchange logic when storing - to r15 in ARM architecture v7 and above. The source must be a temporary - and will be marked as dead. */ -static inline void store_reg_bx(DisasContext *s, int reg, TCGv_i32 var) -{ - if (reg == 15 && ENABLE_ARCH_7) { - gen_bx(s, var); - } else { - store_reg(s, reg, var); - } -} - -/* Variant of store_reg which uses branch&exchange logic when storing - * to r15 in ARM architecture v5T and above. This is used for storing - * the results of a LDR/LDM/POP into r15, and corresponds to the cases - * in the ARM ARM which use the LoadWritePC() pseudocode function. */ -static inline void store_reg_from_load(DisasContext *s, int reg, TCGv_i32 var) -{ - if (reg == 15 && ENABLE_ARCH_5) { - gen_bx_excret(s, var); - } else { - store_reg(s, reg, var); - } -} - -#ifdef CONFIG_USER_ONLY -#define IS_USER_ONLY 1 -#else -#define IS_USER_ONLY 0 -#endif - -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 - }; - g_assert(i < ARRAY_SIZE(mop_align)); - return mop_align[i]; -} - -/* - * Abstractions of "generate code to do a guest load/store for - * AArch32", where a vaddr is always 32 bits (and is zero - * extended if we're a 64 bit core) and data is also - * 32 bits unless specifically doing a 64 bit access. - * These functions work like tcg_gen_qemu_{ld,st}* except - * that the address argument is TCGv_i32 rather than TCGv. - */ - -static TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, MemOp op) -{ - TCGv addr = tcg_temp_new(); - tcg_gen_extu_i32_tl(addr, a32); - - /* Not needed for user-mode BE32, where we use MO_BE instead. */ - if (!IS_USER_ONLY && s->sctlr_b && (op & MO_SIZE) < MO_32) { - tcg_gen_xori_tl(addr, addr, 4 - (1 << (op & MO_SIZE))); - } - return addr; -} - -/* - * Internal routines are used for NEON cases where the endianness - * and/or alignment has already been taken into account and manipulated. - */ -void gen_aa32_ld_internal_i32(DisasContext *s, TCGv_i32 val, - TCGv_i32 a32, int index, MemOp opc) -{ - 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, - TCGv_i32 a32, int index, MemOp opc) -{ - 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, - TCGv_i32 a32, int index, MemOp opc) -{ - TCGv addr = gen_aa32_addr(s, a32, opc); - - tcg_gen_qemu_ld_i64(val, addr, index, opc); - - /* Not needed for user-mode BE32, where we use MO_BE instead. */ - 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, - TCGv_i32 a32, int index, MemOp opc) -{ - TCGv addr = gen_aa32_addr(s, a32, opc); - - /* Not needed for user-mode BE32, where we use MO_BE instead. */ - if (!IS_USER_ONLY && s->sctlr_b && (opc & MO_SIZE) == MO_64) { - 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, - int index, MemOp opc) -{ - gen_aa32_ld_internal_i32(s, val, a32, index, finalize_memop(s, opc)); -} - -void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, - int index, MemOp opc) -{ - gen_aa32_st_internal_i32(s, val, a32, index, finalize_memop(s, opc)); -} - -void gen_aa32_ld_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, - int index, MemOp opc) -{ - gen_aa32_ld_internal_i64(s, val, a32, index, finalize_memop(s, opc)); -} - -void gen_aa32_st_i64(DisasContext *s, TCGv_i64 val, TCGv_i32 a32, - int index, MemOp opc) -{ - gen_aa32_st_internal_i64(s, val, a32, index, finalize_memop(s, opc)); -} - -#define DO_GEN_LD(SUFF, OPC) \ - static inline void gen_aa32_ld##SUFF(DisasContext *s, TCGv_i32 val, \ - TCGv_i32 a32, int index) \ - { \ - gen_aa32_ld_i32(s, val, a32, index, OPC); \ - } - -#define DO_GEN_ST(SUFF, OPC) \ - static inline void gen_aa32_st##SUFF(DisasContext *s, TCGv_i32 val, \ - TCGv_i32 a32, int index) \ - { \ - gen_aa32_st_i32(s, val, a32, index, OPC); \ - } - -static inline void gen_hvc(DisasContext *s, int imm16) -{ - /* The pre HVC helper handles cases when HVC gets trapped - * as an undefined insn by runtime configuration (ie before - * the insn really executes). - */ - gen_update_pc(s, 0); - gen_helper_pre_hvc(cpu_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_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_HVC; -} - -static inline void gen_smc(DisasContext *s) -{ - /* As with HVC, we may take an exception either before or after - * the insn executes. - */ - gen_update_pc(s, 0); - gen_helper_pre_smc(cpu_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, int excp) -{ - gen_set_condexec(s); - gen_update_pc(s, 0); - gen_exception_internal(excp); - s->base.is_jmp = DISAS_NORETURN; -} - -static void gen_exception_el_v(int excp, uint32_t syndrome, TCGv_i32 tcg_el) -{ - gen_helper_exception_with_syndrome_el(cpu_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(cpu_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_update_pc(s, pc_diff); - } else { - gen_set_condexec(s); - gen_update_pc(s, pc_diff); - } - 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_update_pc(s, 0); - gen_helper_exception_bkpt_insn(cpu_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, 0, EXCP_UDEF, syn_uncategorized()); -} - -/* Force a TB lookup after an instruction that changes the CPU state. */ -void gen_lookup_tb(DisasContext *s) -{ - gen_pc_plus_diff(s, cpu_R[15], curr_insn_len(s)); - s->base.is_jmp = DISAS_EXIT; -} - -static inline void gen_hlt(DisasContext *s, int imm) -{ - /* 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 0x3C" is a T32 semihosting trap instruction, - * and "HLT 0xF000" is an A32 semihosting syscall. These traps - * must trigger semihosting even for ARMv7 and earlier, where - * HLT was an undefined encoding. - * In system mode, we don't allow userspace access to - * semihosting, to provide some semblance of security - * (and for consistency with our 32-bit semihosting). - */ - if (semihosting_enabled(s->current_el == 0) && - (imm == (s->thumb ? 0x3c : 0xf000))) { - gen_exception_internal_insn(s, EXCP_SEMIHOST); - return; - } - - unallocated_encoding(s); -} - -/* - * Return the offset of a "full" NEON Dreg. - */ -long neon_full_reg_offset(unsigned reg) -{ - return offsetof(CPUARMState, vfp.zregs[reg >> 1].d[reg & 1]); -} - -/* - * Return the offset of a 2**SIZE piece of a NEON register, at index ELE, - * where 0 is the least significant end of the register. - */ -long neon_element_offset(int reg, int element, MemOp memop) -{ - int element_size = 1 << (memop & MO_SIZE); - int ofs = element * element_size; -#if HOST_BIG_ENDIAN - /* - * Calculate the offset assuming fully little-endian, - * then XOR to account for the order of the 8-byte units. - */ - if (element_size < 8) { - ofs ^= 8 - element_size; - } -#endif - return neon_full_reg_offset(reg) + ofs; -} - -/* Return the offset of a VFP Dreg (dp = true) or VFP Sreg (dp = false). */ -long vfp_reg_offset(bool dp, unsigned reg) -{ - if (dp) { - return neon_element_offset(reg, 0, MO_64); - } else { - return neon_element_offset(reg >> 1, reg & 1, MO_32); - } -} - -void read_neon_element32(TCGv_i32 dest, int reg, int ele, MemOp memop) -{ - long off = neon_element_offset(reg, ele, memop); - - switch (memop) { - case MO_SB: - tcg_gen_ld8s_i32(dest, cpu_env, off); - break; - case MO_UB: - tcg_gen_ld8u_i32(dest, cpu_env, off); - break; - case MO_SW: - tcg_gen_ld16s_i32(dest, cpu_env, off); - break; - case MO_UW: - tcg_gen_ld16u_i32(dest, cpu_env, off); - break; - case MO_UL: - case MO_SL: - tcg_gen_ld_i32(dest, cpu_env, off); - break; - default: - g_assert_not_reached(); - } -} - -void read_neon_element64(TCGv_i64 dest, int reg, int ele, MemOp memop) -{ - long off = neon_element_offset(reg, ele, memop); - - switch (memop) { - case MO_SL: - tcg_gen_ld32s_i64(dest, cpu_env, off); - break; - case MO_UL: - tcg_gen_ld32u_i64(dest, cpu_env, off); - break; - case MO_UQ: - tcg_gen_ld_i64(dest, cpu_env, off); - break; - default: - g_assert_not_reached(); - } -} - -void write_neon_element32(TCGv_i32 src, int reg, int ele, MemOp memop) -{ - long off = neon_element_offset(reg, ele, memop); - - switch (memop) { - case MO_8: - tcg_gen_st8_i32(src, cpu_env, off); - break; - case MO_16: - tcg_gen_st16_i32(src, cpu_env, off); - break; - case MO_32: - tcg_gen_st_i32(src, cpu_env, off); - break; - default: - g_assert_not_reached(); - } -} - -void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) -{ - long off = neon_element_offset(reg, ele, memop); - - switch (memop) { - case MO_32: - tcg_gen_st32_i64(src, cpu_env, off); - break; - case MO_64: - tcg_gen_st_i64(src, cpu_env, off); - break; - default: - g_assert_not_reached(); - } -} - -#define ARM_CP_RW_BIT (1 << 20) - -static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) -{ - tcg_gen_ld_i64(var, cpu_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])); -} - -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])); - 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); -} - -static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) -{ - iwmmxt_store_reg(cpu_M0, rn); -} - -static inline void gen_op_iwmmxt_movq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_M0, rn); -} - -static inline void gen_op_iwmmxt_orq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline void gen_op_iwmmxt_andq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1); -} - -#define IWMMXT_OP(name) \ -static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ -{ \ - iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \ -} - -#define IWMMXT_OP_ENV(name) \ -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); \ -} - -#define IWMMXT_OP_ENV_SIZE(name) \ -IWMMXT_OP_ENV(name##b) \ -IWMMXT_OP_ENV(name##w) \ -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); \ -} - -IWMMXT_OP(maddsq) -IWMMXT_OP(madduq) -IWMMXT_OP(sadb) -IWMMXT_OP(sadw) -IWMMXT_OP(mulslw) -IWMMXT_OP(mulshw) -IWMMXT_OP(mululw) -IWMMXT_OP(muluhw) -IWMMXT_OP(macsw) -IWMMXT_OP(macuw) - -IWMMXT_OP_ENV_SIZE(unpackl) -IWMMXT_OP_ENV_SIZE(unpackh) - -IWMMXT_OP_ENV1(unpacklub) -IWMMXT_OP_ENV1(unpackluw) -IWMMXT_OP_ENV1(unpacklul) -IWMMXT_OP_ENV1(unpackhub) -IWMMXT_OP_ENV1(unpackhuw) -IWMMXT_OP_ENV1(unpackhul) -IWMMXT_OP_ENV1(unpacklsb) -IWMMXT_OP_ENV1(unpacklsw) -IWMMXT_OP_ENV1(unpacklsl) -IWMMXT_OP_ENV1(unpackhsb) -IWMMXT_OP_ENV1(unpackhsw) -IWMMXT_OP_ENV1(unpackhsl) - -IWMMXT_OP_ENV_SIZE(cmpeq) -IWMMXT_OP_ENV_SIZE(cmpgtu) -IWMMXT_OP_ENV_SIZE(cmpgts) - -IWMMXT_OP_ENV_SIZE(mins) -IWMMXT_OP_ENV_SIZE(minu) -IWMMXT_OP_ENV_SIZE(maxs) -IWMMXT_OP_ENV_SIZE(maxu) - -IWMMXT_OP_ENV_SIZE(subn) -IWMMXT_OP_ENV_SIZE(addn) -IWMMXT_OP_ENV_SIZE(subu) -IWMMXT_OP_ENV_SIZE(addu) -IWMMXT_OP_ENV_SIZE(subs) -IWMMXT_OP_ENV_SIZE(adds) - -IWMMXT_OP_ENV(avgb0) -IWMMXT_OP_ENV(avgb1) -IWMMXT_OP_ENV(avgw0) -IWMMXT_OP_ENV(avgw1) - -IWMMXT_OP_ENV(packuw) -IWMMXT_OP_ENV(packul) -IWMMXT_OP_ENV(packuq) -IWMMXT_OP_ENV(packsw) -IWMMXT_OP_ENV(packsl) -IWMMXT_OP_ENV(packsq) - -static void gen_op_iwmmxt_set_mup(void) -{ - TCGv_i32 tmp; - tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); - tcg_gen_ori_i32(tmp, tmp, 2); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); -} - -static void gen_op_iwmmxt_set_cup(void) -{ - TCGv_i32 tmp; - tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); - tcg_gen_ori_i32(tmp, tmp, 1); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); -} - -static void gen_op_iwmmxt_setpsr_nz(void) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]); -} - -static inline void gen_op_iwmmxt_addl_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_ext32u_i64(cpu_V1, cpu_V1); - tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, - TCGv_i32 dest) -{ - int rd; - uint32_t offset; - TCGv_i32 tmp; - - rd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - - offset = (insn & 0xff) << ((insn >> 7) & 2); - if (insn & (1 << 24)) { - /* Pre indexed */ - if (insn & (1 << 23)) - tcg_gen_addi_i32(tmp, tmp, offset); - else - tcg_gen_addi_i32(tmp, tmp, -offset); - tcg_gen_mov_i32(dest, tmp); - 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); - if (insn & (1 << 23)) - tcg_gen_addi_i32(tmp, tmp, offset); - else - tcg_gen_addi_i32(tmp, tmp, -offset); - store_reg(s, rd, tmp); - } else if (!(insn & (1 << 23))) - return 1; - return 0; -} - -static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) -{ - int rd = (insn >> 0) & 0xf; - TCGv_i32 tmp; - - if (insn & (1 << 8)) { - if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) { - return 1; - } else { - tmp = iwmmxt_load_creg(rd); - } - } else { - tmp = tcg_temp_new_i32(); - iwmmxt_load_reg(cpu_V0, rd); - tcg_gen_extrl_i64_i32(tmp, cpu_V0); - } - tcg_gen_andi_i32(tmp, tmp, mask); - tcg_gen_mov_i32(dest, tmp); - tcg_temp_free_i32(tmp); - return 0; -} - -/* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred - (ie. an undefined instruction). */ -static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) -{ - int rd, wrd; - int rdhi, rdlo, rd0, rd1, i; - TCGv_i32 addr; - TCGv_i32 tmp, tmp2, tmp3; - - if ((insn & 0x0e000e00) == 0x0c000000) { - if ((insn & 0x0fe00ff0) == 0x0c400000) { - wrd = insn & 0xf; - rdlo = (insn >> 12) & 0xf; - rdhi = (insn >> 16) & 0xf; - if (insn & ARM_CP_RW_BIT) { /* TMRRC */ - iwmmxt_load_reg(cpu_V0, wrd); - tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); - tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); - } else { /* TMCRR */ - tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); - iwmmxt_store_reg(cpu_V0, wrd); - gen_op_iwmmxt_set_mup(); - } - return 0; - } - - 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) { - if ((insn >> 28) == 0xf) { /* WLDRW wCx */ - tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - iwmmxt_store_creg(wrd, tmp); - } else { - i = 1; - if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WLDRD */ - gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); - i = 0; - } else { /* WLDRW wRd */ - tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - } - } else { - tmp = tcg_temp_new_i32(); - if (insn & (1 << 22)) { /* WLDRH */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - } else { /* WLDRB */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - } - } - if (i) { - tcg_gen_extu_i32_i64(cpu_M0, tmp); - tcg_temp_free_i32(tmp); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - } - } else { - if ((insn >> 28) == 0xf) { /* WSTRW wCx */ - tmp = iwmmxt_load_creg(wrd); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - } else { - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = tcg_temp_new_i32(); - if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WSTRD */ - gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); - } else { /* WSTRW wRd */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - } - } else { - if (insn & (1 << 22)) { /* WSTRH */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - } else { /* WSTRB */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); - } - } - } - tcg_temp_free_i32(tmp); - } - tcg_temp_free_i32(addr); - return 0; - } - - if ((insn & 0x0f000000) != 0x0e000000) - return 1; - - switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { - case 0x000: /* WOR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_orq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x011: /* TMCR */ - if (insn & 0xf) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - switch (wrd) { - case ARM_IWMMXT_wCID: - case ARM_IWMMXT_wCASF: - break; - case ARM_IWMMXT_wCon: - gen_op_iwmmxt_set_cup(); - /* Fall through. */ - case ARM_IWMMXT_wCSSF: - 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: - case ARM_IWMMXT_wCGR1: - case ARM_IWMMXT_wCGR2: - case ARM_IWMMXT_wCGR3: - gen_op_iwmmxt_set_cup(); - tmp = load_reg(s, rd); - iwmmxt_store_creg(wrd, tmp); - break; - default: - return 1; - } - break; - case 0x100: /* WXOR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_xorq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x111: /* TMRC */ - if (insn & 0xf) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = iwmmxt_load_creg(wrd); - store_reg(s, rd, tmp); - break; - case 0x300: /* WANDN */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tcg_gen_neg_i64(cpu_M0, cpu_M0); - gen_op_iwmmxt_andq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x200: /* WAND */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_andq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x810: case 0xa10: /* WMADD */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) - gen_op_iwmmxt_maddsq_M0_wRn(rd1); - else - gen_op_iwmmxt_madduq_M0_wRn(rd1); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_unpacklb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_unpacklw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_unpackll_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_unpackhb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_unpackhw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_unpackhl_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 22)) - gen_op_iwmmxt_sadw_M0_wRn(rd1); - else - gen_op_iwmmxt_sadb_M0_wRn(rd1); - if (!(insn & (1 << 20))) - gen_op_iwmmxt_addl_M0_wRn(wrd); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) { - if (insn & (1 << 20)) - gen_op_iwmmxt_mulshw_M0_wRn(rd1); - else - gen_op_iwmmxt_mulslw_M0_wRn(rd1); - } else { - if (insn & (1 << 20)) - gen_op_iwmmxt_muluhw_M0_wRn(rd1); - else - gen_op_iwmmxt_mululw_M0_wRn(rd1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) - gen_op_iwmmxt_macsw_M0_wRn(rd1); - else - gen_op_iwmmxt_macuw_M0_wRn(rd1); - if (!(insn & (1 << 20))) { - iwmmxt_load_reg(cpu_V1, wrd); - tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_cmpeqb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_cmpeqw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_cmpeql_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 22)) { - if (insn & (1 << 20)) - gen_op_iwmmxt_avgw1_M0_wRn(rd1); - else - gen_op_iwmmxt_avgw0_M0_wRn(rd1); - } else { - if (insn & (1 << 20)) - gen_op_iwmmxt_avgb1_M0_wRn(rd1); - else - gen_op_iwmmxt_avgb0_M0_wRn(rd1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3)); - 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; - case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ - if (((insn >> 6) & 3) == 3) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - gen_op_iwmmxt_movq_M0_wRn(wrd); - switch ((insn >> 6) & 3) { - case 0: - tmp2 = tcg_constant_i32(0xff); - tmp3 = tcg_constant_i32((insn & 7) << 3); - break; - case 1: - tmp2 = tcg_constant_i32(0xffff); - tmp3 = tcg_constant_i32((insn & 3) << 4); - break; - case 2: - tmp2 = tcg_constant_i32(0xffffffff); - tmp3 = tcg_constant_i32((insn & 1) << 5); - break; - default: - 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; - case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - if (rd == 15 || ((insn >> 22) & 3) == 3) - return 1; - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 0: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - if (insn & 8) { - tcg_gen_ext8s_i32(tmp, tmp); - } else { - tcg_gen_andi_i32(tmp, tmp, 0xff); - } - break; - case 1: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - if (insn & 8) { - tcg_gen_ext16s_i32(tmp, tmp); - } else { - tcg_gen_andi_i32(tmp, tmp, 0xffff); - } - break; - case 2: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - break; - } - store_reg(s, rd, tmp); - break; - case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ - if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - switch ((insn >> 22) & 3) { - case 0: - tcg_gen_shri_i32(tmp, tmp, ((insn & 7) << 2) + 0); - break; - case 1: - tcg_gen_shri_i32(tmp, tmp, ((insn & 3) << 3) + 4); - break; - case 2: - tcg_gen_shri_i32(tmp, tmp, ((insn & 1) << 4) + 12); - break; - } - 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) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - switch ((insn >> 6) & 3) { - case 0: - gen_helper_iwmmxt_bcstb(cpu_M0, tmp); - break; - case 1: - gen_helper_iwmmxt_bcstw(cpu_M0, tmp); - break; - case 2: - 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; - case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ - if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - switch ((insn >> 22) & 3) { - case 0: - for (i = 0; i < 7; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 4); - tcg_gen_and_i32(tmp, tmp, tmp2); - } - break; - case 1: - for (i = 0; i < 3; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 8); - tcg_gen_and_i32(tmp, tmp, tmp2); - } - break; - case 2: - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_and_i32(tmp, tmp, tmp2); - 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; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0); - break; - case 1: - gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0); - break; - case 2: - gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ - if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - switch ((insn >> 22) & 3) { - case 0: - for (i = 0; i < 7; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 4); - tcg_gen_or_i32(tmp, tmp, tmp2); - } - break; - case 1: - for (i = 0; i < 3; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 8); - tcg_gen_or_i32(tmp, tmp, tmp2); - } - break; - case 2: - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_or_i32(tmp, tmp, tmp2); - 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; - rd0 = (insn >> 16) & 0xf; - if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) - return 1; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 0: - gen_helper_iwmmxt_msbb(tmp, cpu_M0); - break; - case 1: - gen_helper_iwmmxt_msbw(tmp, cpu_M0); - break; - case 2: - gen_helper_iwmmxt_msbl(tmp, cpu_M0); - break; - } - store_reg(s, rd, tmp); - break; - case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ - case 0x906: case 0xb06: case 0xd06: case 0xf06: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ - case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsb_M0(); - else - gen_op_iwmmxt_unpacklub_M0(); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsw_M0(); - else - gen_op_iwmmxt_unpackluw_M0(); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsl_M0(); - else - gen_op_iwmmxt_unpacklul_M0(); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ - case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsb_M0(); - else - gen_op_iwmmxt_unpackhub_M0(); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsw_M0(); - else - gen_op_iwmmxt_unpackhuw_M0(); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsl_M0(); - else - gen_op_iwmmxt_unpackhul_M0(); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ - case 0x214: case 0x614: case 0xa14: case 0xe14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - 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); - break; - case 2: - gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_srlq(cpu_M0, cpu_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(); - break; - case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ - case 0x014: case 0x414: case 0x814: case 0xc14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - 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); - break; - case 2: - gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_sraq(cpu_M0, cpu_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(); - break; - case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ - case 0x114: case 0x514: case 0x914: case 0xd14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - 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); - break; - case 2: - gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_sllq(cpu_M0, cpu_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(); - break; - case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ - case 0x314: case 0x714: case 0xb14: case 0xf14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - 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); - 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); - 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); - break; - } - tcg_temp_free_i32(tmp); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ - case 0x916: case 0xb16: case 0xd16: case 0xf16: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsb_M0_wRn(rd1); - else - gen_op_iwmmxt_minub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsw_M0_wRn(rd1); - else - gen_op_iwmmxt_minuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsl_M0_wRn(rd1); - else - gen_op_iwmmxt_minul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ - case 0x816: case 0xa16: case 0xc16: case 0xe16: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsb_M0_wRn(rd1); - else - gen_op_iwmmxt_maxub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsw_M0_wRn(rd1); - else - gen_op_iwmmxt_maxuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsl_M0_wRn(rd1); - else - gen_op_iwmmxt_maxul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ - case 0x402: case 0x502: case 0x602: case 0x702: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - iwmmxt_load_reg(cpu_V1, rd1); - gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, - tcg_constant_i32((insn >> 20) & 3)); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ - case 0x41a: case 0x51a: case 0x61a: case 0x71a: - case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: - case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 20) & 0xf) { - case 0x0: - gen_op_iwmmxt_subnb_M0_wRn(rd1); - break; - case 0x1: - gen_op_iwmmxt_subub_M0_wRn(rd1); - break; - case 0x3: - gen_op_iwmmxt_subsb_M0_wRn(rd1); - break; - case 0x4: - gen_op_iwmmxt_subnw_M0_wRn(rd1); - break; - case 0x5: - gen_op_iwmmxt_subuw_M0_wRn(rd1); - break; - case 0x7: - gen_op_iwmmxt_subsw_M0_wRn(rd1); - break; - case 0x8: - gen_op_iwmmxt_subnl_M0_wRn(rd1); - break; - case 0x9: - gen_op_iwmmxt_subul_M0_wRn(rd1); - break; - case 0xb: - gen_op_iwmmxt_subsl_M0_wRn(rd1); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ - case 0x41e: case 0x51e: case 0x61e: case 0x71e: - case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: - case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: - wrd = (insn >> 12) & 0xf; - 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_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ - case 0x418: case 0x518: case 0x618: case 0x718: - case 0x818: case 0x918: case 0xa18: case 0xb18: - case 0xc18: case 0xd18: case 0xe18: case 0xf18: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 20) & 0xf) { - case 0x0: - gen_op_iwmmxt_addnb_M0_wRn(rd1); - break; - case 0x1: - gen_op_iwmmxt_addub_M0_wRn(rd1); - break; - case 0x3: - gen_op_iwmmxt_addsb_M0_wRn(rd1); - break; - case 0x4: - gen_op_iwmmxt_addnw_M0_wRn(rd1); - break; - case 0x5: - gen_op_iwmmxt_adduw_M0_wRn(rd1); - break; - case 0x7: - gen_op_iwmmxt_addsw_M0_wRn(rd1); - break; - case 0x8: - gen_op_iwmmxt_addnl_M0_wRn(rd1); - break; - case 0x9: - gen_op_iwmmxt_addul_M0_wRn(rd1); - break; - case 0xb: - gen_op_iwmmxt_addsl_M0_wRn(rd1); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ - case 0x408: case 0x508: case 0x608: case 0x708: - case 0x808: case 0x908: case 0xa08: case 0xb08: - case 0xc08: case 0xd08: case 0xe08: case 0xf08: - if (!(insn & (1 << 20)) || ((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsw_M0_wRn(rd1); - else - gen_op_iwmmxt_packuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsl_M0_wRn(rd1); - else - gen_op_iwmmxt_packul_M0_wRn(rd1); - break; - case 3: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsq_M0_wRn(rd1); - else - gen_op_iwmmxt_packuq_M0_wRn(rd1); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x201: case 0x203: case 0x205: case 0x207: - case 0x209: case 0x20b: case 0x20d: case 0x20f: - case 0x211: case 0x213: case 0x215: case 0x217: - case 0x219: case 0x21b: case 0x21d: case 0x21f: - wrd = (insn >> 5) & 0xf; - rd0 = (insn >> 12) & 0xf; - rd1 = (insn >> 0) & 0xf; - if (rd0 == 0xf || rd1 == 0xf) - return 1; - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = load_reg(s, rd0); - tmp2 = load_reg(s, rd1); - switch ((insn >> 16) & 0xf) { - case 0x0: /* TMIA */ - gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0x8: /* TMIAPH */ - gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ - if (insn & (1 << 16)) - tcg_gen_shri_i32(tmp, tmp, 16); - if (insn & (1 << 17)) - tcg_gen_shri_i32(tmp2, tmp2, 16); - 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; - default: - return 1; - } - - return 0; -} - -/* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred - (ie. an undefined instruction). */ -static int disas_dsp_insn(DisasContext *s, uint32_t insn) -{ - int acc, rd0, rd1, rdhi, rdlo; - TCGv_i32 tmp, tmp2; - - if ((insn & 0x0ff00f10) == 0x0e200010) { - /* Multiply with Internal Accumulate Format */ - rd0 = (insn >> 12) & 0xf; - rd1 = insn & 0xf; - acc = (insn >> 5) & 7; - - if (acc != 0) - return 1; - - tmp = load_reg(s, rd0); - tmp2 = load_reg(s, rd1); - switch ((insn >> 16) & 0xf) { - case 0x0: /* MIA */ - gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0x8: /* MIAPH */ - gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0xc: /* MIABB */ - case 0xd: /* MIABT */ - case 0xe: /* MIATB */ - case 0xf: /* MIATT */ - if (insn & (1 << 16)) - tcg_gen_shri_i32(tmp, tmp, 16); - if (insn & (1 << 17)) - tcg_gen_shri_i32(tmp2, tmp2, 16); - gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); - break; - default: - return 1; - } - tcg_temp_free_i32(tmp2); - tcg_temp_free_i32(tmp); - - gen_op_iwmmxt_movq_wRn_M0(acc); - return 0; - } - - if ((insn & 0x0fe00ff8) == 0x0c400000) { - /* Internal Accumulator Access Format */ - rdhi = (insn >> 16) & 0xf; - rdlo = (insn >> 12) & 0xf; - acc = insn & 7; - - if (acc != 0) - return 1; - - if (insn & ARM_CP_RW_BIT) { /* MRA */ - iwmmxt_load_reg(cpu_V0, acc); - tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); - tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); - tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); - } else { /* MAR */ - tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); - iwmmxt_store_reg(cpu_V0, acc); - } - return 0; - } - - return 1; -} - -static void gen_goto_ptr(void) -{ - tcg_gen_lookup_and_goto_ptr(); -} - -/* This will end the TB but doesn't guarantee we'll return to - * 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_long diff) -{ - 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 (TARGET_TB_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_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 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_update_pc(s, diff); - s->base.is_jmp = DISAS_JUMP; - return; - } - switch (s->base.is_jmp) { - case DISAS_NEXT: - case DISAS_TOO_MANY: - case DISAS_NORETURN: - /* - * The normal case: just go to the destination TB. - * NB: NORETURN happens if we generate code like - * gen_brcondi(l); - * gen_jmp(); - * gen_set_label(l); - * gen_jmp(); - * on the second call to gen_jmp(). - */ - gen_goto_tb(s, tbno, diff); - break; - case DISAS_UPDATE_NOCHAIN: - case DISAS_UPDATE_EXIT: - /* - * We already decided we're leaving the TB for some other reason. - * Avoid using goto_tb so we really do exit back to the main loop - * and don't chain to another TB. - */ - gen_update_pc(s, diff); - gen_goto_ptr(); - s->base.is_jmp = DISAS_NORETURN; - break; - default: - /* - * We shouldn't be emitting code for a jump and also have - * is_jmp set to one of the special cases like DISAS_SWI. - */ - g_assert_not_reached(); - } -} - -static inline void gen_jmp(DisasContext *s, target_long diff) -{ - gen_jmp_tb(s, diff, 0); -} - -static inline void gen_mulxy(TCGv_i32 t0, TCGv_i32 t1, int x, int y) -{ - if (x) - tcg_gen_sari_i32(t0, t0, 16); - else - gen_sxth(t0); - if (y) - tcg_gen_sari_i32(t1, t1, 16); - else - gen_sxth(t1); - tcg_gen_mul_i32(t0, t0, t1); -} - -/* Return the mask of PSR bits set by a MSR instruction. */ -static uint32_t msr_mask(DisasContext *s, int flags, int spsr) -{ - uint32_t mask = 0; - - if (flags & (1 << 0)) { - mask |= 0xff; - } - if (flags & (1 << 1)) { - mask |= 0xff00; - } - if (flags & (1 << 2)) { - mask |= 0xff0000; - } - if (flags & (1 << 3)) { - mask |= 0xff000000; - } - - /* Mask out undefined and reserved bits. */ - mask &= aarch32_cpsr_valid_mask(s->features, s->isar); - - /* Mask out execution state. */ - if (!spsr) { - mask &= ~CPSR_EXEC; - } - - /* Mask out privileged bits. */ - if (IS_USER(s)) { - mask &= CPSR_USER; - } - return mask; -} - -/* Returns nonzero if access to the PSR is not permitted. Marks t0 as dead. */ -static int gen_set_psr(DisasContext *s, uint32_t mask, int spsr, TCGv_i32 t0) -{ - TCGv_i32 tmp; - if (spsr) { - /* ??? This is also undefined in system mode. */ - if (IS_USER(s)) - return 1; - - tmp = load_cpu_field(spsr); - tcg_gen_andi_i32(tmp, tmp, ~mask); - tcg_gen_andi_i32(t0, t0, mask); - tcg_gen_or_i32(tmp, tmp, t0); - store_cpu_field(tmp, spsr); - } else { - gen_set_cpsr(t0, mask); - } - tcg_temp_free_i32(t0); - gen_lookup_tb(s); - return 0; -} - -/* Returns nonzero if access to the PSR is not permitted. */ -static int gen_set_psr_im(DisasContext *s, uint32_t mask, int spsr, uint32_t val) -{ - TCGv_i32 tmp; - tmp = tcg_temp_new_i32(); - tcg_gen_movi_i32(tmp, val); - return gen_set_psr(s, mask, spsr, tmp); -} - -static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, - int *tgtmode, int *regno) -{ - /* Decode the r and sysm fields of MSR/MRS banked accesses into - * the target mode and register number, and identify the various - * unpredictable cases. - * MSR (banked) and MRS (banked) are CONSTRAINED UNPREDICTABLE if: - * + executed in user mode - * + using R15 as the src/dest register - * + accessing an unimplemented register - * + accessing a register that's inaccessible at current PL/security state* - * + accessing a register that you could access with a different insn - * We choose to UNDEF in all these cases. - * Since we don't know which of the various AArch32 modes we are in - * we have to defer some checks to runtime. - * Accesses to Monitor mode registers from Secure EL1 (which implies - * that EL3 is AArch64) must trap to EL3. - * - * If the access checks fail this function will emit code to take - * an exception and return false. Otherwise it will return true, - * and set *tgtmode and *regno appropriately. - */ - /* These instructions are present only in ARMv8, or in ARMv7 with the - * Virtualization Extensions. - */ - if (!arm_dc_feature(s, ARM_FEATURE_V8) && - !arm_dc_feature(s, ARM_FEATURE_EL2)) { - goto undef; - } - - if (IS_USER(s) || rn == 15) { - goto undef; - } - - /* The table in the v8 ARM ARM section F5.2.3 describes the encoding - * of registers into (r, sysm). - */ - if (r) { - /* SPSRs for other modes */ - switch (sysm) { - case 0xe: /* SPSR_fiq */ - *tgtmode = ARM_CPU_MODE_FIQ; - break; - case 0x10: /* SPSR_irq */ - *tgtmode = ARM_CPU_MODE_IRQ; - break; - case 0x12: /* SPSR_svc */ - *tgtmode = ARM_CPU_MODE_SVC; - break; - case 0x14: /* SPSR_abt */ - *tgtmode = ARM_CPU_MODE_ABT; - break; - case 0x16: /* SPSR_und */ - *tgtmode = ARM_CPU_MODE_UND; - break; - case 0x1c: /* SPSR_mon */ - *tgtmode = ARM_CPU_MODE_MON; - break; - case 0x1e: /* SPSR_hyp */ - *tgtmode = ARM_CPU_MODE_HYP; - break; - default: /* unallocated */ - goto undef; - } - /* We arbitrarily assign SPSR a register number of 16. */ - *regno = 16; - } else { - /* general purpose registers for other modes */ - switch (sysm) { - case 0x0 ... 0x6: /* 0b00xxx : r8_usr ... r14_usr */ - *tgtmode = ARM_CPU_MODE_USR; - *regno = sysm + 8; - break; - case 0x8 ... 0xe: /* 0b01xxx : r8_fiq ... r14_fiq */ - *tgtmode = ARM_CPU_MODE_FIQ; - *regno = sysm; - break; - case 0x10 ... 0x11: /* 0b1000x : r14_irq, r13_irq */ - *tgtmode = ARM_CPU_MODE_IRQ; - *regno = sysm & 1 ? 13 : 14; - break; - case 0x12 ... 0x13: /* 0b1001x : r14_svc, r13_svc */ - *tgtmode = ARM_CPU_MODE_SVC; - *regno = sysm & 1 ? 13 : 14; - break; - case 0x14 ... 0x15: /* 0b1010x : r14_abt, r13_abt */ - *tgtmode = ARM_CPU_MODE_ABT; - *regno = sysm & 1 ? 13 : 14; - break; - case 0x16 ... 0x17: /* 0b1011x : r14_und, r13_und */ - *tgtmode = ARM_CPU_MODE_UND; - *regno = sysm & 1 ? 13 : 14; - break; - case 0x1c ... 0x1d: /* 0b1110x : r14_mon, r13_mon */ - *tgtmode = ARM_CPU_MODE_MON; - *regno = sysm & 1 ? 13 : 14; - break; - case 0x1e ... 0x1f: /* 0b1111x : elr_hyp, r13_hyp */ - *tgtmode = ARM_CPU_MODE_HYP; - /* Arbitrarily pick 17 for ELR_Hyp (which is not a banked LR!) */ - *regno = sysm & 1 ? 13 : 17; - break; - default: /* unallocated */ - goto undef; - } - } - - /* Catch the 'accessing inaccessible register' cases we can detect - * at translate time. - */ - switch (*tgtmode) { - case ARM_CPU_MODE_MON: - if (!arm_dc_feature(s, ARM_FEATURE_EL3) || s->ns) { - goto undef; - } - if (s->current_el == 1) { - /* If we're in Secure EL1 (which implies that EL3 is AArch64) - * then accesses to Mon registers trap to Secure EL2, if it exists, - * otherwise EL3. - */ - TCGv_i32 tcg_el; - - 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_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_insn_el_v(s, 0, EXCP_UDEF, - syn_uncategorized(), tcg_el); - tcg_temp_free_i32(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. - */ - if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 || - (s->current_el < 3 && *regno != 17)) { - goto undef; - } - break; - default: - break; - } - - return true; - -undef: - /* If we get here then some access check did not pass */ - gen_exception_insn(s, 0, EXCP_UDEF, syn_uncategorized()); - return false; -} - -static void gen_msr_banked(DisasContext *s, int r, int sysm, int rn) -{ - TCGv_i32 tcg_reg; - int tgtmode = 0, regno = 0; - - if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { - return; - } - - /* Sync state because msr_banked() can raise exceptions */ - gen_set_condexec(s); - gen_update_pc(s, 0); - tcg_reg = load_reg(s, rn); - gen_helper_msr_banked(cpu_env, tcg_reg, - tcg_constant_i32(tgtmode), - tcg_constant_i32(regno)); - tcg_temp_free_i32(tcg_reg); - s->base.is_jmp = DISAS_UPDATE_EXIT; -} - -static void gen_mrs_banked(DisasContext *s, int r, int sysm, int rn) -{ - TCGv_i32 tcg_reg; - int tgtmode = 0, regno = 0; - - if (!msr_banked_access_decode(s, r, sysm, rn, &tgtmode, ®no)) { - return; - } - - /* Sync state because mrs_banked() can raise exceptions */ - gen_set_condexec(s); - gen_update_pc(s, 0); - tcg_reg = tcg_temp_new_i32(); - gen_helper_mrs_banked(tcg_reg, cpu_env, - tcg_constant_i32(tgtmode), - tcg_constant_i32(regno)); - store_reg(s, rn, tcg_reg); - s->base.is_jmp = DISAS_UPDATE_EXIT; -} - -/* Store value to PC as for an exception return (ie don't - * mask bits). The subsequent call to gen_helper_cpsr_write_eret() - * will do the masking based on the new value of the Thumb bit. - */ -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. */ -static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) -{ - store_pc_exc_ret(s, pc); - /* The cpsr_write_eret helper will mask the low bits of PC - * 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); - /* Must exit loop to check un-masked IRQs */ - s->base.is_jmp = DISAS_EXIT; -} - -/* Generate an old-style exception return. Marks pc as dead. */ -static void gen_exception_return(DisasContext *s, TCGv_i32 pc) -{ - gen_rfe(s, pc, load_cpu_field(spsr)); -} - -static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz, - gen_helper_gvec_3_ptr *fn) -{ - TCGv_ptr qc_ptr = tcg_temp_new_ptr(); - - tcg_gen_addi_ptr(qc_ptr, cpu_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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static gen_helper_gvec_3_ptr * const fns[2] = { - gen_helper_gvec_qrdmlah_s16, gen_helper_gvec_qrdmlah_s32 - }; - tcg_debug_assert(vece >= 1 && vece <= 2); - gen_gvec_fn3_qc(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, fns[vece - 1]); -} - -void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static gen_helper_gvec_3_ptr * const fns[2] = { - gen_helper_gvec_qrdmlsh_s16, gen_helper_gvec_qrdmlsh_s32 - }; - tcg_debug_assert(vece >= 1 && vece <= 2); - 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]); \ - } - -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) - -#undef GEN_CMP0 - -static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_sar8i_i64(a, a, shift); - tcg_gen_vec_add8_i64(d, d, a); -} - -static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_sar16i_i64(a, a, shift); - tcg_gen_vec_add16_i64(d, d, a); -} - -static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_sari_i32(a, a, shift); - tcg_gen_add_i32(d, d, a); -} - -static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_sari_i64(a, a, shift); - tcg_gen_add_i64(d, d, a); -} - -static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - tcg_gen_sari_vec(vece, a, a, sh); - tcg_gen_add_vec(vece, d, d, a); -} - -void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sari_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_ssra8_i64, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_ssra16_i64, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_ssra32_i32, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_ssra64_i64, - .fniv = gen_ssra_vec, - .fno = gen_helper_gvec_ssra_b, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize]. */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* - * Shifts larger than the element size are architecturally valid. - * Signed results in all sign bits. - */ - shift = MIN(shift, (8 << vece) - 1); - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); -} - -static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_shr8i_i64(a, a, shift); - tcg_gen_vec_add8_i64(d, d, a); -} - -static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_shr16i_i64(a, a, shift); - tcg_gen_vec_add16_i64(d, d, a); -} - -static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_shri_i32(a, a, shift); - tcg_gen_add_i32(d, d, a); -} - -static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_shri_i64(a, a, shift); - tcg_gen_add_i64(d, d, a); -} - -static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - tcg_gen_shri_vec(vece, a, a, sh); - tcg_gen_add_vec(vece, d, d, a); -} - -void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_usra8_i64, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8, }, - { .fni8 = gen_usra16_i64, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16, }, - { .fni4 = gen_usra32_i32, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32, }, - { .fni8 = gen_usra64_i64, - .fniv = gen_usra_vec, - .fno = gen_helper_gvec_usra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64, }, - }; - - /* tszimm encoding produces immediates in the range [1..esize]. */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* - * Shifts larger than the element size are architecturally valid. - * Unsigned results in all zeros as input to accumulate: nop. - */ - if (shift < (8 << vece)) { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } else { - /* Nop, but we do need to clear the tail. */ - tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); - } -} - -/* - * Shift one less than the requested amount, and the low bit is - * the rounding bit. For the 8 and 16-bit operations, because we - * mask the low bit, we can perform a normal integer shift instead - * of a vector shift. - */ -static void gen_srshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - 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) -{ - TCGv_i32 t; - - /* Handle shift by the input size for the benefit of trans_SRSHR_ri */ - if (sh == 32) { - tcg_gen_movi_i32(d, 0); - return; - } - t = tcg_temp_new_i32(); - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec ones = tcg_temp_new_vec_matching(d); - - tcg_gen_shri_vec(vece, t, a, sh - 1); - tcg_gen_dupi_vec(vece, ones, 1); - 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, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_srshr8_i64, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_srshr16_i64, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_srshr32_i32, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_srshr64_i64, - .fniv = gen_srshr_vec, - .fno = gen_helper_gvec_srshr_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - if (shift == (8 << vece)) { - /* - * Shifts larger than the element size are architecturally valid. - * Signed results in all sign bits. With rounding, this produces - * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. - * I.e. always zero. - */ - tcg_gen_gvec_dup_imm(vece, rd_ofs, opr_sz, max_sz, 0); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_srsra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - 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, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_sari_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_srsra8_i64, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fni8 = gen_srsra16_i64, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_srsra32_i32, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_srsra64_i64, - .fniv = gen_srsra_vec, - .fno = gen_helper_gvec_srsra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* - * Shifts larger than the element size are architecturally valid. - * Signed results in all sign bits. With rounding, this produces - * (-1 + 1) >> 1 == 0, or (0 + 1) >> 1 == 0. - * I.e. always zero. With accumulation, this leaves D unchanged. - */ - if (shift == (8 << vece)) { - /* Nop, but we do need to clear the tail. */ - tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_urshr8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, sh - 1); - 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) -{ - TCGv_i32 t; - - /* Handle shift by the input size for the benefit of trans_URSHR_ri */ - if (sh == 32) { - tcg_gen_extract_i32(d, a, sh - 1, 1); - return; - } - t = tcg_temp_new_i32(); - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec ones = tcg_temp_new_vec_matching(d); - - tcg_gen_shri_vec(vece, t, a, shift - 1); - tcg_gen_dupi_vec(vece, ones, 1); - 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, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_urshr8_i64, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_urshr16_i64, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_urshr32_i32, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_urshr64_i64, - .fniv = gen_urshr_vec, - .fno = gen_helper_gvec_urshr_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - if (shift == (8 << vece)) { - /* - * Shifts larger than the element size are architecturally valid. - * Unsigned results in zero. With rounding, this produces a - * copy of the most significant bit. - */ - tcg_gen_gvec_shri(vece, rd_ofs, rm_ofs, shift - 1, opr_sz, max_sz); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_ursra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t sh) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - if (sh == 8) { - tcg_gen_vec_shr8i_i64(t, a, 7); - } else { - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - if (sh == 16) { - tcg_gen_vec_shr16i_i64(t, a, 15); - } else { - 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) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - if (sh == 32) { - tcg_gen_shri_i32(t, a, 31); - } else { - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - if (sh == 64) { - tcg_gen_shri_i64(t, a, 63); - } else { - 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) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - if (sh == (8 << vece)) { - tcg_gen_shri_vec(vece, t, a, sh - 1); - } else { - 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, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_shri_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen2i ops[4] = { - { .fni8 = gen_ursra8_i64, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fni8 = gen_ursra16_i64, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_ursra32_i32, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_ursra64_i64, - .fniv = gen_ursra_vec, - .fno = gen_helper_gvec_ursra_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize] */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); -} - -static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_8, 0xff >> shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, 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) -{ - uint64_t mask = dup_const(MO_16, 0xffff >> shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, 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) -{ - tcg_gen_shri_i32(a, a, shift); - tcg_gen_deposit_i32(d, d, a, 0, 32 - shift); -} - -static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_shri_i64(a, a, shift); - tcg_gen_deposit_i64(d, d, a, 0, 64 - shift); -} - -static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_temp_new_vec_matching(d); - - tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK((8 << vece) - sh, 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, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_shri_vec, 0 }; - const GVecGen2i ops[4] = { - { .fni8 = gen_shr8_ins_i64, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_shr16_ins_i64, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_shr32_ins_i32, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_shr64_ins_i64, - .fniv = gen_shr_ins_vec, - .fno = gen_helper_gvec_sri_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [1..esize]. */ - tcg_debug_assert(shift > 0); - tcg_debug_assert(shift <= (8 << vece)); - - /* Shift of esize leaves destination unchanged. */ - if (shift < (8 << vece)) { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } else { - /* Nop, but we do need to clear the tail. */ - tcg_gen_gvec_mov(vece, rd_ofs, rd_ofs, opr_sz, max_sz); - } -} - -static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_8, 0xff << shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t, a, 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) -{ - uint64_t mask = dup_const(MO_16, 0xffff << shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t, a, 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) -{ - tcg_gen_deposit_i32(d, d, a, shift, 32 - shift); -} - -static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_deposit_i64(d, d, a, shift, 64 - shift); -} - -static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_temp_new_vec_matching(d); - - tcg_gen_shli_vec(vece, t, a, 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, - int64_t shift, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; - const GVecGen2i ops[4] = { - { .fni8 = gen_shl8_ins_i64, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_b, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni8 = gen_shl16_ins_i64, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_h, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_shl32_ins_i32, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_s, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_shl64_ins_i64, - .fniv = gen_shl_ins_vec, - .fno = gen_helper_gvec_sli_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - - /* tszimm encoding produces immediates in the range [0..esize-1]. */ - tcg_debug_assert(shift >= 0); - tcg_debug_assert(shift < (8 << vece)); - - if (shift == 0) { - tcg_gen_gvec_mov(vece, rd_ofs, rm_ofs, opr_sz, max_sz); - } else { - tcg_gen_gvec_2i(rd_ofs, rm_ofs, opr_sz, max_sz, shift, &ops[vece]); - } -} - -static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u8(a, a, b); - gen_helper_neon_add_u8(d, d, a); -} - -static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u8(a, a, b); - gen_helper_neon_sub_u8(d, d, a); -} - -static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u16(a, a, b); - gen_helper_neon_add_u16(d, d, a); -} - -static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u16(a, a, b); - gen_helper_neon_sub_u16(d, d, a); -} - -static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mul_i32(a, a, b); - tcg_gen_add_i32(d, d, a); -} - -static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mul_i32(a, a, b); - tcg_gen_sub_i32(d, d, a); -} - -static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mul_i64(a, a, b); - tcg_gen_add_i64(d, d, a); -} - -static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mul_i64(a, a, b); - tcg_gen_sub_i64(d, d, a); -} - -static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_mul_vec(vece, a, a, b); - tcg_gen_add_vec(vece, d, d, a); -} - -static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_mul_vec(vece, a, a, b); - tcg_gen_sub_vec(vece, d, d, a); -} - -/* Note that while NEON does not support VMLA and VMLS as 64-bit ops, - * these tables are shared with AArch64 which does support them. - */ -void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_mul_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fni4 = gen_mla8_i32, - .fniv = gen_mla_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni4 = gen_mla16_i32, - .fniv = gen_mla_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_mla32_i32, - .fniv = gen_mla_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_mla64_i64, - .fniv = gen_mla_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_mul_vec, INDEX_op_sub_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fni4 = gen_mls8_i32, - .fniv = gen_mls_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni4 = gen_mls16_i32, - .fniv = gen_mls_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_mls32_i32, - .fniv = gen_mls_vec, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_mls64_i64, - .fniv = gen_mls_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -/* CMTST : test is "if (X & Y != 0)". */ -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); -} - -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); -} - -static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_and_vec(vece, d, a, b); - tcg_gen_dupi_vec(vece, a, 0); - tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a); -} - -void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { INDEX_op_cmp_vec, 0 }; - static const GVecGen3 ops[4] = { - { .fni4 = gen_helper_neon_tst_u8, - .fniv = gen_cmtst_vec, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fni4 = gen_helper_neon_tst_u16, - .fniv = gen_cmtst_vec, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_cmtst_i32, - .fniv = gen_cmtst_vec, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_cmtst_i64, - .fniv = gen_cmtst_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -void gen_ushl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) -{ - TCGv_i32 lval = tcg_temp_new_i32(); - TCGv_i32 rval = tcg_temp_new_i32(); - TCGv_i32 lsh = tcg_temp_new_i32(); - TCGv_i32 rsh = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_constant_i32(0); - TCGv_i32 max = tcg_constant_i32(32); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i32(lsh, shift); - tcg_gen_neg_i32(rsh, lsh); - tcg_gen_shl_i32(lval, src, lsh); - tcg_gen_shr_i32(rval, src, rsh); - tcg_gen_movcond_i32(TCG_COND_LTU, dst, lsh, max, lval, zero); - tcg_gen_movcond_i32(TCG_COND_LTU, dst, rsh, max, rval, dst); - - tcg_temp_free_i32(lval); - tcg_temp_free_i32(rval); - tcg_temp_free_i32(lsh); - tcg_temp_free_i32(rsh); -} - -void gen_ushl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) -{ - TCGv_i64 lval = tcg_temp_new_i64(); - TCGv_i64 rval = tcg_temp_new_i64(); - TCGv_i64 lsh = tcg_temp_new_i64(); - TCGv_i64 rsh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); - TCGv_i64 max = tcg_constant_i64(64); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i64(lsh, shift); - tcg_gen_neg_i64(rsh, lsh); - tcg_gen_shl_i64(lval, src, lsh); - tcg_gen_shr_i64(rval, src, rsh); - tcg_gen_movcond_i64(TCG_COND_LTU, dst, lsh, max, lval, zero); - tcg_gen_movcond_i64(TCG_COND_LTU, dst, rsh, max, rval, dst); - - tcg_temp_free_i64(lval); - tcg_temp_free_i64(rval); - tcg_temp_free_i64(lsh); - tcg_temp_free_i64(rsh); -} - -static void gen_ushl_vec(unsigned vece, TCGv_vec dst, - TCGv_vec src, TCGv_vec shift) -{ - TCGv_vec lval = tcg_temp_new_vec_matching(dst); - TCGv_vec rval = tcg_temp_new_vec_matching(dst); - TCGv_vec lsh = tcg_temp_new_vec_matching(dst); - TCGv_vec rsh = tcg_temp_new_vec_matching(dst); - TCGv_vec msk, max; - - tcg_gen_neg_vec(vece, rsh, shift); - if (vece == MO_8) { - tcg_gen_mov_vec(lsh, shift); - } else { - msk = tcg_temp_new_vec_matching(dst); - tcg_gen_dupi_vec(vece, msk, 0xff); - tcg_gen_and_vec(vece, lsh, shift, msk); - tcg_gen_and_vec(vece, rsh, rsh, msk); - tcg_temp_free_vec(msk); - } - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_shlv_vec(vece, lval, src, lsh); - tcg_gen_shrv_vec(vece, rval, src, rsh); - - max = tcg_temp_new_vec_matching(dst); - tcg_gen_dupi_vec(vece, max, 8 << vece); - - /* - * The choice of LT (signed) and GEU (unsigned) are biased toward - * the instructions of the x86_64 host. For MO_8, the whole byte - * is significant so we must use an unsigned compare; otherwise we - * have already masked to a byte and so a signed compare works. - * Other tcg hosts have a full set of comparisons and do not care. - */ - if (vece == MO_8) { - tcg_gen_cmp_vec(TCG_COND_GEU, vece, lsh, lsh, max); - tcg_gen_cmp_vec(TCG_COND_GEU, vece, rsh, rsh, max); - tcg_gen_andc_vec(vece, lval, lval, lsh); - tcg_gen_andc_vec(vece, rval, rval, rsh); - } else { - tcg_gen_cmp_vec(TCG_COND_LT, vece, lsh, lsh, max); - tcg_gen_cmp_vec(TCG_COND_LT, vece, rsh, rsh, max); - tcg_gen_and_vec(vece, lval, lval, lsh); - tcg_gen_and_vec(vece, rval, rval, rsh); - } - tcg_gen_or_vec(vece, dst, lval, rval); - - tcg_temp_free_vec(max); - tcg_temp_free_vec(lval); - tcg_temp_free_vec(rval); - tcg_temp_free_vec(lsh); - tcg_temp_free_vec(rsh); -} - -void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_neg_vec, INDEX_op_shlv_vec, - INDEX_op_shrv_vec, INDEX_op_cmp_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_ushl_vec, - .fno = gen_helper_gvec_ushl_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_ushl_vec, - .fno = gen_helper_gvec_ushl_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_ushl_i32, - .fniv = gen_ushl_vec, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_ushl_i64, - .fniv = gen_ushl_vec, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -void gen_sshl_i32(TCGv_i32 dst, TCGv_i32 src, TCGv_i32 shift) -{ - TCGv_i32 lval = tcg_temp_new_i32(); - TCGv_i32 rval = tcg_temp_new_i32(); - TCGv_i32 lsh = tcg_temp_new_i32(); - TCGv_i32 rsh = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_constant_i32(0); - TCGv_i32 max = tcg_constant_i32(31); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i32(lsh, shift); - tcg_gen_neg_i32(rsh, lsh); - tcg_gen_shl_i32(lval, src, lsh); - tcg_gen_umin_i32(rsh, rsh, max); - tcg_gen_sar_i32(rval, src, rsh); - tcg_gen_movcond_i32(TCG_COND_LEU, lval, lsh, max, lval, zero); - tcg_gen_movcond_i32(TCG_COND_LT, dst, lsh, zero, rval, lval); - - tcg_temp_free_i32(lval); - tcg_temp_free_i32(rval); - tcg_temp_free_i32(lsh); - tcg_temp_free_i32(rsh); -} - -void gen_sshl_i64(TCGv_i64 dst, TCGv_i64 src, TCGv_i64 shift) -{ - TCGv_i64 lval = tcg_temp_new_i64(); - TCGv_i64 rval = tcg_temp_new_i64(); - TCGv_i64 lsh = tcg_temp_new_i64(); - TCGv_i64 rsh = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); - TCGv_i64 max = tcg_constant_i64(63); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_ext8s_i64(lsh, shift); - tcg_gen_neg_i64(rsh, lsh); - tcg_gen_shl_i64(lval, src, lsh); - tcg_gen_umin_i64(rsh, rsh, max); - tcg_gen_sar_i64(rval, src, rsh); - tcg_gen_movcond_i64(TCG_COND_LEU, lval, lsh, max, lval, zero); - tcg_gen_movcond_i64(TCG_COND_LT, dst, lsh, zero, rval, lval); - - tcg_temp_free_i64(lval); - tcg_temp_free_i64(rval); - tcg_temp_free_i64(lsh); - tcg_temp_free_i64(rsh); -} - -static void gen_sshl_vec(unsigned vece, TCGv_vec dst, - TCGv_vec src, TCGv_vec shift) -{ - TCGv_vec lval = tcg_temp_new_vec_matching(dst); - TCGv_vec rval = tcg_temp_new_vec_matching(dst); - TCGv_vec lsh = tcg_temp_new_vec_matching(dst); - TCGv_vec rsh = tcg_temp_new_vec_matching(dst); - TCGv_vec tmp = tcg_temp_new_vec_matching(dst); - - /* - * Rely on the TCG guarantee that out of range shifts produce - * unspecified results, not undefined behaviour (i.e. no trap). - * Discard out-of-range results after the fact. - */ - tcg_gen_neg_vec(vece, rsh, shift); - if (vece == MO_8) { - tcg_gen_mov_vec(lsh, shift); - } else { - tcg_gen_dupi_vec(vece, tmp, 0xff); - tcg_gen_and_vec(vece, lsh, shift, tmp); - tcg_gen_and_vec(vece, rsh, rsh, tmp); - } - - /* Bound rsh so out of bound right shift gets -1. */ - tcg_gen_dupi_vec(vece, tmp, (8 << vece) - 1); - tcg_gen_umin_vec(vece, rsh, rsh, tmp); - tcg_gen_cmp_vec(TCG_COND_GT, vece, tmp, lsh, tmp); - - tcg_gen_shlv_vec(vece, lval, src, lsh); - tcg_gen_sarv_vec(vece, rval, src, rsh); - - /* Select in-bound left shift. */ - tcg_gen_andc_vec(vece, lval, lval, tmp); - - /* Select between left and right shift. */ - if (vece == MO_8) { - tcg_gen_dupi_vec(vece, tmp, 0); - tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, rval, lval); - } else { - tcg_gen_dupi_vec(vece, tmp, 0x80); - tcg_gen_cmpsel_vec(TCG_COND_LT, vece, dst, lsh, tmp, lval, rval); - } - - tcg_temp_free_vec(lval); - tcg_temp_free_vec(rval); - tcg_temp_free_vec(lsh); - tcg_temp_free_vec(rsh); - tcg_temp_free_vec(tmp); -} - -void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_neg_vec, INDEX_op_umin_vec, INDEX_op_shlv_vec, - INDEX_op_sarv_vec, INDEX_op_cmp_vec, INDEX_op_cmpsel_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_sshl_vec, - .fno = gen_helper_gvec_sshl_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_sshl_vec, - .fno = gen_helper_gvec_sshl_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_sshl_i32, - .fniv = gen_sshl_vec, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_sshl_i64, - .fniv = gen_sshl_vec, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_add_vec(vece, x, a, b); - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_usadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_b, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_h, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_s, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fniv = gen_uqadd_vec, - .fno = gen_helper_gvec_uqadd_d, - .write_aofs = true, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_add_vec(vece, x, a, b); - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_ssadd_vec, INDEX_op_cmp_vec, INDEX_op_add_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_b, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_8 }, - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_h, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_16 }, - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_s, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_32 }, - { .fniv = gen_sqadd_vec, - .fno = gen_helper_gvec_sqadd_d, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_sub_vec(vece, x, a, b); - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_ussub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_b, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_8 }, - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_h, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_16 }, - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_s, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_32 }, - { .fniv = gen_uqsub_vec, - .fno = gen_helper_gvec_uqsub_d, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, - TCGv_vec a, TCGv_vec b) -{ - TCGv_vec x = tcg_temp_new_vec_matching(t); - tcg_gen_sub_vec(vece, x, a, b); - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sssub_vec, INDEX_op_cmp_vec, INDEX_op_sub_vec, 0 - }; - static const GVecGen4 ops[4] = { - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_b, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_8 }, - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_h, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_16 }, - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_s, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_32 }, - { .fniv = gen_sqsub_vec, - .fno = gen_helper_gvec_sqsub_d, - .opt_opc = vecop_list, - .write_aofs = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), - rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_sabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_sabd_i32, - .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_sabd_i64, - .fniv = gen_sabd_vec, - .fno = gen_helper_gvec_sabd_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -static void gen_uabd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - 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) -{ - TCGv_i64 t = tcg_temp_new_i64(); - - 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) -{ - TCGv_vec t = tcg_temp_new_vec_matching(d); - - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_b, - .opt_opc = vecop_list, - .vece = MO_8 }, - { .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_h, - .opt_opc = vecop_list, - .vece = MO_16 }, - { .fni4 = gen_uabd_i32, - .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_s, - .opt_opc = vecop_list, - .vece = MO_32 }, - { .fni8 = gen_uabd_i64, - .fniv = gen_uabd_vec, - .fno = gen_helper_gvec_uabd_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -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) -{ - 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) -{ - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_add_vec, - INDEX_op_smin_vec, INDEX_op_smax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_saba_i32, - .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_saba_i64, - .fniv = gen_saba_vec, - .fno = gen_helper_gvec_saba_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -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) -{ - 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) -{ - 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, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) -{ - static const TCGOpcode vecop_list[] = { - INDEX_op_sub_vec, INDEX_op_add_vec, - INDEX_op_umin_vec, INDEX_op_umax_vec, 0 - }; - static const GVecGen3 ops[4] = { - { .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_b, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_8 }, - { .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_h, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_uaba_i32, - .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_s, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_uaba_i64, - .fniv = gen_uaba_vec, - .fno = gen_helper_gvec_uaba_d, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .opt_opc = vecop_list, - .load_dest = true, - .vece = MO_64 }, - }; - tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); -} - -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; - - 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_update_pc(s, 0); - 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_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); - return; - } - gen_update_pc(s, curr_insn_len(s)); - 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); - } - } - } 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); - } - } - } - - /* 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 (!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); - } - - 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"); - } - - unallocated_encoding(s); - return; -} - -/* Decode XScale DSP or iWMMXt insn (in the copro space, cp=0 or 1) */ -static void disas_xscale_insn(DisasContext *s, uint32_t insn) -{ - int cpnum = (insn >> 8) & 0xf; - - if (extract32(s->c15_cpar, cpnum, 1) == 0) { - unallocated_encoding(s); - } else if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { - if (disas_iwmmxt_insn(s, insn)) { - unallocated_encoding(s); - } - } else if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { - if (disas_dsp_insn(s, insn)) { - unallocated_encoding(s); - } - } -} - -/* Store a 64-bit value to a register pair. Clobbers val. */ -static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) -{ - TCGv_i32 tmp; - tmp = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(tmp, val); - store_reg(s, rlow, tmp); - tmp = tcg_temp_new_i32(); - tcg_gen_extrh_i64_i32(tmp, val); - store_reg(s, rhigh, tmp); -} - -/* load and add a 64-bit value from a register pair. */ -static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) -{ - TCGv_i64 tmp; - TCGv_i32 tmpl; - TCGv_i32 tmph; - - /* Load 64-bit value rd:rn. */ - tmpl = load_reg(s, rlow); - 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. */ -static void gen_logicq_cc(TCGv_i32 lo, TCGv_i32 hi) -{ - tcg_gen_mov_i32(cpu_NF, hi); - tcg_gen_or_i32(cpu_ZF, lo, hi); -} - -/* Load/Store exclusive instructions are implemented by remembering - the value/address loaded, and seeing if these are the same - when the store is performed. This should be sufficient to implement - the architecturally mandated semantics, and avoids having to monitor - regular stores. The compare vs the remembered value is done during - the cmpxchg operation, but we must compare the addresses manually. */ -static void gen_load_exclusive(DisasContext *s, int rt, int rt2, - TCGv_i32 addr, int size) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - MemOp opc = size | MO_ALIGN | s->be_data; - - s->is_ldex = true; - - if (size == 3) { - TCGv_i32 tmp2 = tcg_temp_new_i32(); - TCGv_i64 t64 = tcg_temp_new_i64(); - - /* - * For AArch32, architecturally the 32-bit word at the lowest - * address is always Rt and the one at addr+4 is Rt2, even if - * the CPU is big-endian. That means we don't want to do a - * gen_aa32_ld_i64(), which checks SCTLR_B as if for an - * architecturally 64-bit access, but instead do a 64-bit access - * using MO_BE if appropriate and then split the two halves. - */ - 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); - tcg_gen_extu_i32_i64(cpu_exclusive_val, tmp); - } - - store_reg(s, rt, tmp); - tcg_gen_extu_i32_i64(cpu_exclusive_addr, addr); -} - -static void gen_clrex(DisasContext *s) -{ - tcg_gen_movi_i64(cpu_exclusive_addr, -1); -} - -static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, - TCGv_i32 addr, int size) -{ - TCGv_i32 t0, t1, t2; - TCGv_i64 extaddr; - TCGv taddr; - TCGLabel *done_label; - TCGLabel *fail_label; - MemOp opc = size | MO_ALIGN | s->be_data; - - /* if (env->exclusive_addr == addr && env->exclusive_val == [addr]) { - [addr] = {Rt}; - {Rd} = 0; - } else { - {Rd} = 1; - } */ - fail_label = gen_new_label(); - done_label = gen_new_label(); - 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(); - t1 = load_reg(s, rt); - if (size == 3) { - TCGv_i64 o64 = tcg_temp_new_i64(); - TCGv_i64 n64 = tcg_temp_new_i64(); - - t2 = load_reg(s, rt2); - - /* - * For AArch32, architecturally the 32-bit word at the lowest - * address is always Rt and the one at addr+4 is Rt2, even if - * the CPU is big-endian. Since we're going to treat this as a - * single 64-bit BE store, we need to put the two halves in the - * opposite order for BE to LE, so that they end up in the right - * places. We don't want gen_aa32_st_i64, because that checks - * SCTLR_B as if for an architectural 64-bit access. - */ - if (s->be_data == MO_BE) { - tcg_gen_concat_i32_i64(n64, t2, t1); - } 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); - tcg_gen_movi_i32(cpu_R[rd], 1); - gen_set_label(done_label); - tcg_gen_movi_i64(cpu_exclusive_addr, -1); -} - -/* gen_srs: - * @env: CPUARMState - * @s: DisasContext - * @mode: mode field from insn (which stack to store to) - * @amode: addressing mode (DA/IA/DB/IB), encoded as per P,U bits in ARM insn - * @writeback: true if writeback bit set - * - * Generate code for the SRS (Store Return State) insn. - */ -static void gen_srs(DisasContext *s, - uint32_t mode, uint32_t amode, bool writeback) -{ - int32_t offset; - TCGv_i32 addr, tmp; - bool undef = false; - - /* SRS is: - * - trapped to EL3 if EL3 is AArch64 and we are at Secure EL1 - * and specified mode is monitor mode - * - UNDEFINED in Hyp mode - * - UNPREDICTABLE in User or System mode - * - UNPREDICTABLE if the specified mode is: - * -- not implemented - * -- not a valid mode number - * -- a mode that's at a higher exception level - * -- Monitor, if we are Non-secure - * For the UNPREDICTABLE cases we choose to UNDEF. - */ - if (s->current_el == 1 && !s->ns && mode == ARM_CPU_MODE_MON) { - gen_exception_insn_el(s, 0, EXCP_UDEF, syn_uncategorized(), 3); - return; - } - - if (s->current_el == 0 || s->current_el == 2) { - undef = true; - } - - switch (mode) { - case ARM_CPU_MODE_USR: - 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: - break; - case ARM_CPU_MODE_HYP: - if (s->current_el == 1 || !arm_dc_feature(s, ARM_FEATURE_EL2)) { - undef = true; - } - break; - case ARM_CPU_MODE_MON: - /* No need to check specifically for "are we non-secure" because - * we've already made EL0 UNDEF and handled the trap for S-EL1; - * so if this isn't EL3 then we must be non-secure. - */ - if (s->current_el != 3) { - undef = true; - } - break; - default: - undef = true; - } - - if (undef) { - unallocated_encoding(s); - return; - } - - addr = tcg_temp_new_i32(); - /* get_r13_banked() will raise an exception if called from System mode */ - gen_set_condexec(s); - gen_update_pc(s, 0); - gen_helper_get_r13_banked(addr, cpu_env, tcg_constant_i32(mode)); - switch (amode) { - case 0: /* DA */ - offset = -4; - break; - case 1: /* IA */ - offset = 0; - break; - case 2: /* DB */ - offset = -8; - break; - case 3: /* IB */ - offset = 4; - break; - default: - g_assert_not_reached(); - } - 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: - offset = -8; - break; - case 1: - offset = 4; - break; - case 2: - offset = -4; - break; - case 3: - offset = 0; - break; - default: - g_assert_not_reached(); - } - tcg_gen_addi_i32(addr, addr, offset); - gen_helper_set_r13_banked(cpu_env, tcg_constant_i32(mode), addr); - } - tcg_temp_free_i32(addr); - s->base.is_jmp = DISAS_UPDATE_EXIT; -} - -/* Skip this instruction if the ARM condition is false */ -static void arm_skip_unless(DisasContext *s, uint32_t cond) -{ - arm_gen_condlabel(s); - arm_gen_test_cc(cond ^ 1, s->condlabel.label); -} - - -/* - * Constant expanders used by T16/T32 decode - */ - -/* Return only the rotation part of T32ExpandImm. */ -static int t32_expandimm_rot(DisasContext *s, int x) -{ - return x & 0xc00 ? extract32(x, 7, 5) : 0; -} - -/* Return the unrotated immediate from T32ExpandImm. */ -static int t32_expandimm_imm(DisasContext *s, int x) -{ - int imm = extract32(x, 0, 8); - - switch (extract32(x, 8, 4)) { - case 0: /* XY */ - /* Nothing to do. */ - break; - case 1: /* 00XY00XY */ - imm *= 0x00010001; - break; - case 2: /* XY00XY00 */ - imm *= 0x01000100; - break; - case 3: /* XYXYXYXY */ - imm *= 0x01010101; - break; - default: - /* Rotated constant. */ - imm |= 0x80; - break; - } - return imm; -} - -static int t32_branch24(DisasContext *s, int x) -{ - /* Convert J1:J2 at x[22:21] to I2:I1, which involves I=J^~S. */ - x ^= !(x < 0) * (3 << 21); - /* Append the final zero. */ - return x << 1; -} - -static int t16_setflags(DisasContext *s) -{ - return s->condexec_mask == 0; -} - -static int t16_push_list(DisasContext *s, int x) -{ - return (x & 0xff) | (x & 0x100) << (14 - 8); -} - -static int t16_pop_list(DisasContext *s, int x) -{ - return (x & 0xff) | (x & 0x100) << (15 - 8); -} - -/* - * Include the generated decoders. - */ - -#include "decode-a32.c.inc" -#include "decode-a32-uncond.c.inc" -#include "decode-t32.c.inc" -#include "decode-t16.c.inc" - -static bool valid_cp(DisasContext *s, int cp) -{ - /* - * Return true if this coprocessor field indicates something - * that's really a possible coprocessor. - * For v7 and earlier, coprocessors 8..15 were reserved for Arm use, - * and of those only cp14 and cp15 were used for registers. - * cp10 and cp11 were used for VFP and Neon, whose decode is - * dealt with elsewhere. With the advent of fp16, cp9 is also - * now part of VFP. - * For v8A and later, the encoding has been tightened so that - * only cp14 and cp15 are valid, and other values aren't considered - * to be in the coprocessor-instruction space at all. v8M still - * permits coprocessors 0..7. - * For XScale, we must not decode the XScale cp0, cp1 space as - * a standard coprocessor insn, because we want to fall through to - * the legacy disas_xscale_insn() decoder after decodetree is done. - */ - if (arm_dc_feature(s, ARM_FEATURE_XSCALE) && (cp == 0 || cp == 1)) { - return false; - } - - if (arm_dc_feature(s, ARM_FEATURE_V8) && - !arm_dc_feature(s, ARM_FEATURE_M)) { - return cp >= 14; - } - return cp < 8 || cp >= 14; -} - -static bool trans_MCR(DisasContext *s, arg_MCR *a) -{ - if (!valid_cp(s, a->cp)) { - return false; - } - do_coproc_insn(s, a->cp, false, a->opc1, a->crn, a->crm, a->opc2, - false, a->rt, 0); - return true; -} - -static bool trans_MRC(DisasContext *s, arg_MRC *a) -{ - if (!valid_cp(s, a->cp)) { - return false; - } - do_coproc_insn(s, a->cp, false, a->opc1, a->crn, a->crm, a->opc2, - true, a->rt, 0); - return true; -} - -static bool trans_MCRR(DisasContext *s, arg_MCRR *a) -{ - if (!valid_cp(s, a->cp)) { - return false; - } - do_coproc_insn(s, a->cp, true, a->opc1, 0, a->crm, 0, - false, a->rt, a->rt2); - return true; -} - -static bool trans_MRRC(DisasContext *s, arg_MRRC *a) -{ - if (!valid_cp(s, a->cp)) { - return false; - } - do_coproc_insn(s, a->cp, true, a->opc1, 0, a->crm, 0, - true, a->rt, a->rt2); - return true; -} - -/* Helpers to swap operands for reverse-subtract. */ -static void gen_rsb(TCGv_i32 dst, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_sub_i32(dst, b, a); -} - -static void gen_rsb_CC(TCGv_i32 dst, TCGv_i32 a, TCGv_i32 b) -{ - gen_sub_CC(dst, b, a); -} - -static void gen_rsc(TCGv_i32 dest, TCGv_i32 a, TCGv_i32 b) -{ - gen_sub_carry(dest, b, a); -} - -static void gen_rsc_CC(TCGv_i32 dest, TCGv_i32 a, TCGv_i32 b) -{ - gen_sbc_CC(dest, b, a); -} - -/* - * Helpers for the data processing routines. - * - * After the computation store the results back. - * This may be suppressed altogether (STREG_NONE), require a runtime - * check against the stack limits (STREG_SP_CHECK), or generate an - * exception return. Oh, or store into a register. - * - * Always return true, indicating success for a trans_* function. - */ -typedef enum { - STREG_NONE, - STREG_NORMAL, - STREG_SP_CHECK, - STREG_EXC_RET, -} StoreRegKind; - -static bool store_reg_kind(DisasContext *s, int rd, - TCGv_i32 val, StoreRegKind kind) -{ - switch (kind) { - case STREG_NONE: - tcg_temp_free_i32(val); - return true; - case STREG_NORMAL: - /* See ALUWritePC: Interworking only from a32 mode. */ - if (s->thumb) { - store_reg(s, rd, val); - } else { - store_reg_bx(s, rd, val); - } - return true; - case STREG_SP_CHECK: - store_sp_checked(s, val); - return true; - case STREG_EXC_RET: - gen_exception_return(s, val); - return true; - } - g_assert_not_reached(); -} - -/* - * Data Processing (register) - * - * Operate, with set flags, one register source, - * one immediate shifted register source, and a destination. - */ -static bool op_s_rrr_shi(DisasContext *s, arg_s_rrr_shi *a, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), - int logic_cc, StoreRegKind kind) -{ - TCGv_i32 tmp1, tmp2; - - tmp2 = load_reg(s, a->rm); - gen_arm_shift_im(tmp2, a->shty, a->shim, logic_cc); - tmp1 = load_reg(s, a->rn); - - gen(tmp1, tmp1, tmp2); - tcg_temp_free_i32(tmp2); - - if (logic_cc) { - gen_logic_CC(tmp1); - } - return store_reg_kind(s, a->rd, tmp1, kind); -} - -static bool op_s_rxr_shi(DisasContext *s, arg_s_rrr_shi *a, - void (*gen)(TCGv_i32, TCGv_i32), - int logic_cc, StoreRegKind kind) -{ - TCGv_i32 tmp; - - tmp = load_reg(s, a->rm); - gen_arm_shift_im(tmp, a->shty, a->shim, logic_cc); - - gen(tmp, tmp); - if (logic_cc) { - gen_logic_CC(tmp); - } - return store_reg_kind(s, a->rd, tmp, kind); -} - -/* - * Data-processing (register-shifted register) - * - * Operate, with set flags, one register source, - * one register shifted register source, and a destination. - */ -static bool op_s_rrr_shr(DisasContext *s, arg_s_rrr_shr *a, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), - int logic_cc, StoreRegKind kind) -{ - TCGv_i32 tmp1, tmp2; - - tmp1 = load_reg(s, a->rs); - tmp2 = load_reg(s, a->rm); - gen_arm_shift_reg(tmp2, a->shty, tmp1, logic_cc); - tmp1 = load_reg(s, a->rn); - - gen(tmp1, tmp1, tmp2); - tcg_temp_free_i32(tmp2); - - if (logic_cc) { - gen_logic_CC(tmp1); - } - return store_reg_kind(s, a->rd, tmp1, kind); -} - -static bool op_s_rxr_shr(DisasContext *s, arg_s_rrr_shr *a, - void (*gen)(TCGv_i32, TCGv_i32), - int logic_cc, StoreRegKind kind) -{ - TCGv_i32 tmp1, tmp2; - - tmp1 = load_reg(s, a->rs); - tmp2 = load_reg(s, a->rm); - gen_arm_shift_reg(tmp2, a->shty, tmp1, logic_cc); - - gen(tmp2, tmp2); - if (logic_cc) { - gen_logic_CC(tmp2); - } - return store_reg_kind(s, a->rd, tmp2, kind); -} - -/* - * Data-processing (immediate) - * - * Operate, with set flags, one register source, - * one rotated immediate, and a destination. - * - * Note that logic_cc && a->rot setting CF based on the msb of the - * immediate is the reason why we must pass in the unrotated form - * of the immediate. - */ -static bool op_s_rri_rot(DisasContext *s, arg_s_rri_rot *a, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32), - int logic_cc, StoreRegKind kind) -{ - TCGv_i32 tmp1; - uint32_t imm; - - imm = ror32(a->imm, a->rot); - if (logic_cc && a->rot) { - tcg_gen_movi_i32(cpu_CF, imm >> 31); - } - tmp1 = load_reg(s, a->rn); - - gen(tmp1, tmp1, tcg_constant_i32(imm)); - - if (logic_cc) { - gen_logic_CC(tmp1); - } - return store_reg_kind(s, a->rd, tmp1, kind); -} - -static bool op_s_rxi_rot(DisasContext *s, arg_s_rri_rot *a, - void (*gen)(TCGv_i32, TCGv_i32), - int logic_cc, StoreRegKind kind) -{ - TCGv_i32 tmp; - uint32_t imm; - - imm = ror32(a->imm, a->rot); - if (logic_cc && a->rot) { - tcg_gen_movi_i32(cpu_CF, imm >> 31); - } - - tmp = tcg_temp_new_i32(); - gen(tmp, tcg_constant_i32(imm)); - - if (logic_cc) { - gen_logic_CC(tmp); - } - return store_reg_kind(s, a->rd, tmp, kind); -} - -#define DO_ANY3(NAME, OP, L, K) \ - static bool trans_##NAME##_rrri(DisasContext *s, arg_s_rrr_shi *a) \ - { StoreRegKind k = (K); return op_s_rrr_shi(s, a, OP, L, k); } \ - static bool trans_##NAME##_rrrr(DisasContext *s, arg_s_rrr_shr *a) \ - { StoreRegKind k = (K); return op_s_rrr_shr(s, a, OP, L, k); } \ - static bool trans_##NAME##_rri(DisasContext *s, arg_s_rri_rot *a) \ - { StoreRegKind k = (K); return op_s_rri_rot(s, a, OP, L, k); } - -#define DO_ANY2(NAME, OP, L, K) \ - static bool trans_##NAME##_rxri(DisasContext *s, arg_s_rrr_shi *a) \ - { StoreRegKind k = (K); return op_s_rxr_shi(s, a, OP, L, k); } \ - static bool trans_##NAME##_rxrr(DisasContext *s, arg_s_rrr_shr *a) \ - { StoreRegKind k = (K); return op_s_rxr_shr(s, a, OP, L, k); } \ - static bool trans_##NAME##_rxi(DisasContext *s, arg_s_rri_rot *a) \ - { StoreRegKind k = (K); return op_s_rxi_rot(s, a, OP, L, k); } - -#define DO_CMP2(NAME, OP, L) \ - static bool trans_##NAME##_xrri(DisasContext *s, arg_s_rrr_shi *a) \ - { return op_s_rrr_shi(s, a, OP, L, STREG_NONE); } \ - static bool trans_##NAME##_xrrr(DisasContext *s, arg_s_rrr_shr *a) \ - { return op_s_rrr_shr(s, a, OP, L, STREG_NONE); } \ - static bool trans_##NAME##_xri(DisasContext *s, arg_s_rri_rot *a) \ - { return op_s_rri_rot(s, a, OP, L, STREG_NONE); } - -DO_ANY3(AND, tcg_gen_and_i32, a->s, STREG_NORMAL) -DO_ANY3(EOR, tcg_gen_xor_i32, a->s, STREG_NORMAL) -DO_ANY3(ORR, tcg_gen_or_i32, a->s, STREG_NORMAL) -DO_ANY3(BIC, tcg_gen_andc_i32, a->s, STREG_NORMAL) - -DO_ANY3(RSB, a->s ? gen_rsb_CC : gen_rsb, false, STREG_NORMAL) -DO_ANY3(ADC, a->s ? gen_adc_CC : gen_add_carry, false, STREG_NORMAL) -DO_ANY3(SBC, a->s ? gen_sbc_CC : gen_sub_carry, false, STREG_NORMAL) -DO_ANY3(RSC, a->s ? gen_rsc_CC : gen_rsc, false, STREG_NORMAL) - -DO_CMP2(TST, tcg_gen_and_i32, true) -DO_CMP2(TEQ, tcg_gen_xor_i32, true) -DO_CMP2(CMN, gen_add_CC, false) -DO_CMP2(CMP, gen_sub_CC, false) - -DO_ANY3(ADD, a->s ? gen_add_CC : tcg_gen_add_i32, false, - a->rd == 13 && a->rn == 13 ? STREG_SP_CHECK : STREG_NORMAL) - -/* - * Note for the computation of StoreRegKind we return out of the - * middle of the functions that are expanded by DO_ANY3, and that - * we modify a->s via that parameter before it is used by OP. - */ -DO_ANY3(SUB, a->s ? gen_sub_CC : tcg_gen_sub_i32, false, - ({ - StoreRegKind ret = STREG_NORMAL; - if (a->rd == 15 && a->s) { - /* - * See ALUExceptionReturn: - * In User mode, UNPREDICTABLE; we choose UNDEF. - * In Hyp mode, UNDEFINED. - */ - if (IS_USER(s) || s->current_el == 2) { - unallocated_encoding(s); - return true; - } - /* There is no writeback of nzcv to PSTATE. */ - a->s = 0; - ret = STREG_EXC_RET; - } else if (a->rd == 13 && a->rn == 13) { - ret = STREG_SP_CHECK; - } - ret; - })) - -DO_ANY2(MOV, tcg_gen_mov_i32, a->s, - ({ - StoreRegKind ret = STREG_NORMAL; - if (a->rd == 15 && a->s) { - /* - * See ALUExceptionReturn: - * In User mode, UNPREDICTABLE; we choose UNDEF. - * In Hyp mode, UNDEFINED. - */ - if (IS_USER(s) || s->current_el == 2) { - unallocated_encoding(s); - return true; - } - /* There is no writeback of nzcv to PSTATE. */ - a->s = 0; - ret = STREG_EXC_RET; - } else if (a->rd == 13) { - ret = STREG_SP_CHECK; - } - ret; - })) - -DO_ANY2(MVN, tcg_gen_not_i32, a->s, STREG_NORMAL) - -/* - * ORN is only available with T32, so there is no register-shifted-register - * form of the insn. Using the DO_ANY3 macro would create an unused function. - */ -static bool trans_ORN_rrri(DisasContext *s, arg_s_rrr_shi *a) -{ - return op_s_rrr_shi(s, a, tcg_gen_orc_i32, a->s, STREG_NORMAL); -} - -static bool trans_ORN_rri(DisasContext *s, arg_s_rri_rot *a) -{ - return op_s_rri_rot(s, a, tcg_gen_orc_i32, a->s, STREG_NORMAL); -} - -#undef DO_ANY3 -#undef DO_ANY2 -#undef DO_CMP2 - -static bool trans_ADR(DisasContext *s, arg_ri *a) -{ - store_reg_bx(s, a->rd, add_reg_for_lit(s, 15, a->imm)); - return true; -} - -static bool trans_MOVW(DisasContext *s, arg_MOVW *a) -{ - if (!ENABLE_ARCH_6T2) { - return false; - } - - store_reg(s, a->rd, tcg_constant_i32(a->imm)); - return true; -} - -static bool trans_MOVT(DisasContext *s, arg_MOVW *a) -{ - TCGv_i32 tmp; - - if (!ENABLE_ARCH_6T2) { - return false; - } - - tmp = load_reg(s, a->rd); - tcg_gen_ext16u_i32(tmp, tmp); - tcg_gen_ori_i32(tmp, tmp, a->imm << 16); - store_reg(s, a->rd, tmp); - return true; -} - -/* - * v8.1M MVE wide-shifts - */ -static bool do_mve_shl_ri(DisasContext *s, arg_mve_shl_ri *a, - WideShiftImmFn *fn) -{ - TCGv_i64 rda; - TCGv_i32 rdalo, rdahi; - - if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { - /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ - return false; - } - if (a->rdahi == 15) { - /* These are a different encoding (SQSHL/SRSHR/UQSHL/URSHR) */ - return false; - } - if (!dc_isar_feature(aa32_mve, s) || - !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || - a->rdahi == 13) { - /* RdaHi == 13 is UNPREDICTABLE; we choose to UNDEF */ - unallocated_encoding(s); - return true; - } - - if (a->shim == 0) { - a->shim = 32; - } - - rda = tcg_temp_new_i64(); - rdalo = load_reg(s, a->rdalo); - rdahi = load_reg(s, a->rdahi); - tcg_gen_concat_i32_i64(rda, rdalo, rdahi); - - fn(rda, rda, a->shim); - - 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; -} - -static bool trans_ASRL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, tcg_gen_sari_i64); -} - -static bool trans_LSLL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, tcg_gen_shli_i64); -} - -static bool trans_LSRL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, tcg_gen_shri_i64); -} - -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)); -} - -static bool trans_SQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, gen_mve_sqshll); -} - -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)); -} - -static bool trans_UQSHLL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, gen_mve_uqshll); -} - -static bool trans_SRSHRL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, gen_srshr64_i64); -} - -static bool trans_URSHRL_ri(DisasContext *s, arg_mve_shl_ri *a) -{ - return do_mve_shl_ri(s, a, gen_urshr64_i64); -} - -static bool do_mve_shl_rr(DisasContext *s, arg_mve_shl_rr *a, WideShiftFn *fn) -{ - TCGv_i64 rda; - TCGv_i32 rdalo, rdahi; - - if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { - /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ - return false; - } - if (a->rdahi == 15) { - /* These are a different encoding (SQSHL/SRSHR/UQSHL/URSHR) */ - return false; - } - if (!dc_isar_feature(aa32_mve, s) || - !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || - a->rdahi == 13 || a->rm == 13 || a->rm == 15 || - a->rm == a->rdahi || a->rm == a->rdalo) { - /* These rdahi/rdalo/rm cases are UNPREDICTABLE; we choose to UNDEF */ - unallocated_encoding(s); - return true; - } - - rda = tcg_temp_new_i64(); - rdalo = load_reg(s, a->rdalo); - rdahi = load_reg(s, a->rdahi); - 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]); - - 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; -} - -static bool trans_LSLL_rr(DisasContext *s, arg_mve_shl_rr *a) -{ - return do_mve_shl_rr(s, a, gen_helper_mve_ushll); -} - -static bool trans_ASRL_rr(DisasContext *s, arg_mve_shl_rr *a) -{ - return do_mve_shl_rr(s, a, gen_helper_mve_sshrl); -} - -static bool trans_UQRSHLL64_rr(DisasContext *s, arg_mve_shl_rr *a) -{ - return do_mve_shl_rr(s, a, gen_helper_mve_uqrshll); -} - -static bool trans_SQRSHRL64_rr(DisasContext *s, arg_mve_shl_rr *a) -{ - return do_mve_shl_rr(s, a, gen_helper_mve_sqrshrl); -} - -static bool trans_UQRSHLL48_rr(DisasContext *s, arg_mve_shl_rr *a) -{ - return do_mve_shl_rr(s, a, gen_helper_mve_uqrshll48); -} - -static bool trans_SQRSHRL48_rr(DisasContext *s, arg_mve_shl_rr *a) -{ - return do_mve_shl_rr(s, a, gen_helper_mve_sqrshrl48); -} - -static bool do_mve_sh_ri(DisasContext *s, arg_mve_sh_ri *a, ShiftImmFn *fn) -{ - if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { - /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ - return false; - } - if (!dc_isar_feature(aa32_mve, s) || - !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || - a->rda == 13 || a->rda == 15) { - /* These rda cases are UNPREDICTABLE; we choose to UNDEF */ - unallocated_encoding(s); - return true; - } - - if (a->shim == 0) { - a->shim = 32; - } - fn(cpu_R[a->rda], cpu_R[a->rda], a->shim); - - return true; -} - -static bool trans_URSHR_ri(DisasContext *s, arg_mve_sh_ri *a) -{ - return do_mve_sh_ri(s, a, gen_urshr32_i32); -} - -static bool trans_SRSHR_ri(DisasContext *s, arg_mve_sh_ri *a) -{ - return do_mve_sh_ri(s, a, gen_srshr32_i32); -} - -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)); -} - -static bool trans_SQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) -{ - return do_mve_sh_ri(s, a, gen_mve_sqshl); -} - -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)); -} - -static bool trans_UQSHL_ri(DisasContext *s, arg_mve_sh_ri *a) -{ - return do_mve_sh_ri(s, a, gen_mve_uqshl); -} - -static bool do_mve_sh_rr(DisasContext *s, arg_mve_sh_rr *a, ShiftFn *fn) -{ - if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { - /* Decode falls through to ORR/MOV UNPREDICTABLE handling */ - return false; - } - if (!dc_isar_feature(aa32_mve, s) || - !arm_dc_feature(s, ARM_FEATURE_M_MAIN) || - a->rda == 13 || a->rda == 15 || a->rm == 13 || a->rm == 15 || - a->rm == a->rda) { - /* These rda/rm cases are UNPREDICTABLE; we choose to UNDEF */ - unallocated_encoding(s); - return true; - } - - /* 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]); - return true; -} - -static bool trans_SQRSHR_rr(DisasContext *s, arg_mve_sh_rr *a) -{ - return do_mve_sh_rr(s, a, gen_helper_mve_sqrshr); -} - -static bool trans_UQRSHL_rr(DisasContext *s, arg_mve_sh_rr *a) -{ - return do_mve_sh_rr(s, a, gen_helper_mve_uqrshl); -} - -/* - * Multiply and multiply accumulate - */ - -static bool op_mla(DisasContext *s, arg_s_rrrr *a, bool add) -{ - TCGv_i32 t1, t2; - - 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); - } - store_reg(s, a->rd, t1); - return true; -} - -static bool trans_MUL(DisasContext *s, arg_MUL *a) -{ - return op_mla(s, a, false); -} - -static bool trans_MLA(DisasContext *s, arg_MLA *a) -{ - return op_mla(s, a, true); -} - -static bool trans_MLS(DisasContext *s, arg_MLS *a) -{ - TCGv_i32 t1, t2; - - if (!ENABLE_ARCH_6T2) { - return false; - } - 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; -} - -static bool op_mlal(DisasContext *s, arg_s_rrrr *a, bool uns, bool add) -{ - TCGv_i32 t0, t1, t2, t3; - - t0 = load_reg(s, a->rm); - t1 = load_reg(s, a->rn); - if (uns) { - tcg_gen_mulu2_i32(t0, t1, t0, t1); - } else { - tcg_gen_muls2_i32(t0, t1, t0, t1); - } - if (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); - } - store_reg(s, a->ra, t0); - store_reg(s, a->rd, t1); - return true; -} - -static bool trans_UMULL(DisasContext *s, arg_UMULL *a) -{ - return op_mlal(s, a, true, false); -} - -static bool trans_SMULL(DisasContext *s, arg_SMULL *a) -{ - return op_mlal(s, a, false, false); -} - -static bool trans_UMLAL(DisasContext *s, arg_UMLAL *a) -{ - return op_mlal(s, a, true, true); -} - -static bool trans_SMLAL(DisasContext *s, arg_SMLAL *a) -{ - return op_mlal(s, a, false, true); -} - -static bool trans_UMAAL(DisasContext *s, arg_UMAAL *a) -{ - TCGv_i32 t0, t1, t2, zero; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_6) { - return false; - } - - t0 = load_reg(s, a->rm); - t1 = load_reg(s, a->rn); - tcg_gen_mulu2_i32(t0, t1, t0, t1); - 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; -} - -/* - * Saturating addition and subtraction - */ - -static bool op_qaddsub(DisasContext *s, arg_rrr *a, bool add, bool doub) -{ - TCGv_i32 t0, t1; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_5TE) { - return false; - } - - t0 = load_reg(s, a->rm); - t1 = load_reg(s, a->rn); - if (doub) { - gen_helper_add_saturate(t1, cpu_env, t1, t1); - } - if (add) { - gen_helper_add_saturate(t0, cpu_env, t0, t1); - } else { - gen_helper_sub_saturate(t0, cpu_env, t0, t1); - } - tcg_temp_free_i32(t1); - store_reg(s, a->rd, t0); - return true; -} - -#define DO_QADDSUB(NAME, ADD, DOUB) \ -static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ -{ \ - return op_qaddsub(s, a, ADD, DOUB); \ -} - -DO_QADDSUB(QADD, true, false) -DO_QADDSUB(QSUB, false, false) -DO_QADDSUB(QDADD, true, true) -DO_QADDSUB(QDSUB, false, true) - -#undef DO_QADDSUB - -/* - * Halfword multiply and multiply accumulate - */ - -static bool op_smlaxxx(DisasContext *s, arg_rrrr *a, - int add_long, bool nt, bool mt) -{ - TCGv_i32 t0, t1, tl, th; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_5TE) { - return false; - } - - 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: - store_reg(s, a->rd, t0); - break; - case 1: - t1 = load_reg(s, a->ra); - gen_helper_add_setq(t0, cpu_env, t0, t1); - tcg_temp_free_i32(t1); - store_reg(s, a->rd, t0); - break; - case 2: - tl = load_reg(s, a->ra); - th = load_reg(s, a->rd); - /* Sign-extend the 32-bit product to 64 bits. */ - 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; - default: - g_assert_not_reached(); - } - return true; -} - -#define DO_SMLAX(NAME, add, nt, mt) \ -static bool trans_##NAME(DisasContext *s, arg_rrrr *a) \ -{ \ - return op_smlaxxx(s, a, add, nt, mt); \ -} - -DO_SMLAX(SMULBB, 0, 0, 0) -DO_SMLAX(SMULBT, 0, 0, 1) -DO_SMLAX(SMULTB, 0, 1, 0) -DO_SMLAX(SMULTT, 0, 1, 1) - -DO_SMLAX(SMLABB, 1, 0, 0) -DO_SMLAX(SMLABT, 1, 0, 1) -DO_SMLAX(SMLATB, 1, 1, 0) -DO_SMLAX(SMLATT, 1, 1, 1) - -DO_SMLAX(SMLALBB, 2, 0, 0) -DO_SMLAX(SMLALBT, 2, 0, 1) -DO_SMLAX(SMLALTB, 2, 1, 0) -DO_SMLAX(SMLALTT, 2, 1, 1) - -#undef DO_SMLAX - -static bool op_smlawx(DisasContext *s, arg_rrrr *a, bool add, bool mt) -{ - TCGv_i32 t0, t1; - - if (!ENABLE_ARCH_5TE) { - return false; - } - - t0 = load_reg(s, a->rn); - t1 = load_reg(s, a->rm); - /* - * Since the nominal result is product<47:16>, shift the 16-bit - * input up by 16 bits, so that the result is at product<63:32>. - */ - if (mt) { - tcg_gen_andi_i32(t1, t1, 0xffff0000); - } else { - 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); - } - store_reg(s, a->rd, t1); - return true; -} - -#define DO_SMLAWX(NAME, add, mt) \ -static bool trans_##NAME(DisasContext *s, arg_rrrr *a) \ -{ \ - return op_smlawx(s, a, add, mt); \ -} - -DO_SMLAWX(SMULWB, 0, 0) -DO_SMLAWX(SMULWT, 0, 1) -DO_SMLAWX(SMLAWB, 1, 0) -DO_SMLAWX(SMLAWT, 1, 1) - -#undef DO_SMLAWX - -/* - * MSR (immediate) and hints - */ - -static bool trans_YIELD(DisasContext *s, arg_YIELD *a) -{ - /* - * When running single-threaded TCG code, use the helper to ensure that - * the next round-robin scheduled vCPU gets a crack. When running in - * MTTCG we don't generate jumps to the helper as it won't affect the - * scheduling of other vCPUs. - */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_YIELD; - } - return true; -} - -static bool trans_WFE(DisasContext *s, arg_WFE *a) -{ - /* - * When running single-threaded TCG code, use the helper to ensure that - * the next round-robin scheduled vCPU gets a crack. In MTTCG mode we - * just skip this instruction. Currently the SEV/SEVL instructions, - * which are *one* of many ways to wake the CPU from WFE, are not - * implemented so we can't sleep like WFI does. - */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_WFE; - } - return true; -} - -static bool trans_WFI(DisasContext *s, arg_WFI *a) -{ - /* For WFI, halt the vCPU until an IRQ. */ - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_WFI; - return true; -} - -static bool trans_ESB(DisasContext *s, arg_ESB *a) -{ - /* - * For M-profile, minimal-RAS ESB can be a NOP. - * Without RAS, we must implement this as NOP. - */ - if (!arm_dc_feature(s, ARM_FEATURE_M) && dc_isar_feature(aa32_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 - * AArch32.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); - } - } - return true; -} - -static bool trans_NOP(DisasContext *s, arg_NOP *a) -{ - return true; -} - -static bool trans_MSR_imm(DisasContext *s, arg_MSR_imm *a) -{ - uint32_t val = ror32(a->imm, a->rot * 2); - uint32_t mask = msr_mask(s, a->mask, a->r); - - if (gen_set_psr_im(s, mask, a->r, val)) { - unallocated_encoding(s); - } - return true; -} - -/* - * Cyclic Redundancy Check - */ - -static bool op_crc32(DisasContext *s, arg_rrr *a, bool c, MemOp sz) -{ - TCGv_i32 t1, t2, t3; - - if (!dc_isar_feature(aa32_crc32, s)) { - return false; - } - - t1 = load_reg(s, a->rn); - t2 = load_reg(s, a->rm); - switch (sz) { - case MO_8: - gen_uxtb(t2); - break; - case MO_16: - gen_uxth(t2); - break; - case MO_32: - break; - default: - g_assert_not_reached(); - } - t3 = tcg_constant_i32(1 << sz); - if (c) { - gen_helper_crc32c(t1, t1, t2, t3); - } else { - gen_helper_crc32(t1, t1, t2, t3); - } - tcg_temp_free_i32(t2); - store_reg(s, a->rd, t1); - return true; -} - -#define DO_CRC32(NAME, c, sz) \ -static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ - { return op_crc32(s, a, c, sz); } - -DO_CRC32(CRC32B, false, MO_8) -DO_CRC32(CRC32H, false, MO_16) -DO_CRC32(CRC32W, false, MO_32) -DO_CRC32(CRC32CB, true, MO_8) -DO_CRC32(CRC32CH, true, MO_16) -DO_CRC32(CRC32CW, true, MO_32) - -#undef DO_CRC32 - -/* - * Miscellaneous instructions - */ - -static bool trans_MRS_bank(DisasContext *s, arg_MRS_bank *a) -{ - if (arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - gen_mrs_banked(s, a->r, a->sysm, a->rd); - return true; -} - -static bool trans_MSR_bank(DisasContext *s, arg_MSR_bank *a) -{ - if (arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - gen_msr_banked(s, a->r, a->sysm, a->rn); - return true; -} - -static bool trans_MRS_reg(DisasContext *s, arg_MRS_reg *a) -{ - TCGv_i32 tmp; - - if (arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - if (a->r) { - if (IS_USER(s)) { - unallocated_encoding(s); - return true; - } - tmp = load_cpu_field(spsr); - } else { - tmp = tcg_temp_new_i32(); - gen_helper_cpsr_read(tmp, cpu_env); - } - store_reg(s, a->rd, tmp); - return true; -} - -static bool trans_MSR_reg(DisasContext *s, arg_MSR_reg *a) -{ - TCGv_i32 tmp; - uint32_t mask = msr_mask(s, a->mask, a->r); - - if (arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - tmp = load_reg(s, a->rn); - if (gen_set_psr(s, mask, a->r, tmp)) { - unallocated_encoding(s); - } - return true; -} - -static bool trans_MRS_v7m(DisasContext *s, arg_MRS_v7m *a) -{ - TCGv_i32 tmp; - - if (!arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - tmp = tcg_temp_new_i32(); - gen_helper_v7m_mrs(tmp, cpu_env, tcg_constant_i32(a->sysm)); - store_reg(s, a->rd, tmp); - return true; -} - -static bool trans_MSR_v7m(DisasContext *s, arg_MSR_v7m *a) -{ - TCGv_i32 addr, reg; - - if (!arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - 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); - /* If we wrote to CONTROL, the EL might have changed */ - gen_rebuild_hflags(s, true); - gen_lookup_tb(s); - return true; -} - -static bool trans_BX(DisasContext *s, arg_BX *a) -{ - if (!ENABLE_ARCH_4T) { - return false; - } - gen_bx_excret(s, load_reg(s, a->rm)); - return true; -} - -static bool trans_BXJ(DisasContext *s, arg_BXJ *a) -{ - if (!ENABLE_ARCH_5J || arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - /* - * v7A allows BXJ to be trapped via HSTR.TJDBX. We don't waste a - * TBFLAGS bit on a basically-never-happens case, so call a helper - * function to check for the trap and raise the exception if needed - * (passing it the register number for the syndrome value). - * v8A doesn't have this HSTR bit. - */ - 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)); - } - /* Trivial implementation equivalent to bx. */ - gen_bx(s, load_reg(s, a->rm)); - return true; -} - -static bool trans_BLX_r(DisasContext *s, arg_BLX_r *a) -{ - TCGv_i32 tmp; - - if (!ENABLE_ARCH_5) { - return false; - } - tmp = load_reg(s, a->rm); - gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); - gen_bx(s, tmp); - return true; -} - -/* - * BXNS/BLXNS: only exist for v8M with the security extensions, - * and always UNDEF if NonSecure. We don't implement these in - * the user-only mode either (in theory you can use them from - * Secure User mode but they are too tied in to system emulation). - */ -static bool trans_BXNS(DisasContext *s, arg_BXNS *a) -{ - if (!s->v8m_secure || IS_USER_ONLY) { - unallocated_encoding(s); - } else { - gen_bxns(s, a->rm); - } - return true; -} - -static bool trans_BLXNS(DisasContext *s, arg_BLXNS *a) -{ - if (!s->v8m_secure || IS_USER_ONLY) { - unallocated_encoding(s); - } else { - gen_blxns(s, a->rm); - } - return true; -} - -static bool trans_CLZ(DisasContext *s, arg_CLZ *a) -{ - TCGv_i32 tmp; - - if (!ENABLE_ARCH_5) { - return false; - } - tmp = load_reg(s, a->rm); - tcg_gen_clzi_i32(tmp, tmp, 32); - store_reg(s, a->rd, tmp); - return true; -} - -static bool trans_ERET(DisasContext *s, arg_ERET *a) -{ - TCGv_i32 tmp; - - if (!arm_dc_feature(s, ARM_FEATURE_V7VE)) { - return false; - } - if (IS_USER(s)) { - unallocated_encoding(s); - return true; - } - if (s->current_el == 2) { - /* ERET from Hyp uses ELR_Hyp, not LR */ - tmp = load_cpu_field_low32(elr_el[2]); - } else { - tmp = load_reg(s, 14); - } - gen_exception_return(s, tmp); - return true; -} - -static bool trans_HLT(DisasContext *s, arg_HLT *a) -{ - gen_hlt(s, a->imm); - return true; -} - -static bool trans_BKPT(DisasContext *s, arg_BKPT *a) -{ - if (!ENABLE_ARCH_5) { - return false; - } - /* BKPT is OK with ECI set and leaves it untouched */ - s->eci_handled = true; - if (arm_dc_feature(s, ARM_FEATURE_M) && - semihosting_enabled(s->current_el == 0) && - (a->imm == 0xab)) { - gen_exception_internal_insn(s, EXCP_SEMIHOST); - } else { - gen_exception_bkpt_insn(s, syn_aa32_bkpt(a->imm, false)); - } - return true; -} - -static bool trans_HVC(DisasContext *s, arg_HVC *a) -{ - if (!ENABLE_ARCH_7 || arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - if (IS_USER(s)) { - unallocated_encoding(s); - } else { - gen_hvc(s, a->imm); - } - return true; -} - -static bool trans_SMC(DisasContext *s, arg_SMC *a) -{ - if (!ENABLE_ARCH_6K || arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - if (IS_USER(s)) { - unallocated_encoding(s); - } else { - gen_smc(s); - } - return true; -} - -static bool trans_SG(DisasContext *s, arg_SG *a) -{ - if (!arm_dc_feature(s, ARM_FEATURE_M) || - !arm_dc_feature(s, ARM_FEATURE_V8)) { - return false; - } - /* - * SG (v8M only) - * The bulk of the behaviour for this instruction is implemented - * in v7m_handle_execute_nsc(), which deals with the insn when - * it is executed by a CPU in non-secure state from memory - * which is Secure & NonSecure-Callable. - * Here we only need to handle the remaining cases: - * * in NS memory (including the "security extension not - * implemented" case) : NOP - * * in S memory but CPU already secure (clear IT bits) - * We know that the attribute for the memory this insn is - * in must match the current CPU state, because otherwise - * get_phys_addr_pmsav8 would have generated an exception. - */ - if (s->v8m_secure) { - /* Like the IT insn, we don't need to generate any code */ - s->condexec_cond = 0; - s->condexec_mask = 0; - } - return true; -} - -static bool trans_TT(DisasContext *s, arg_TT *a) -{ - TCGv_i32 addr, tmp; - - if (!arm_dc_feature(s, ARM_FEATURE_M) || - !arm_dc_feature(s, ARM_FEATURE_V8)) { - return false; - } - if (a->rd == 13 || a->rd == 15 || a->rn == 15) { - /* We UNDEF for these UNPREDICTABLE cases */ - unallocated_encoding(s); - return true; - } - if (a->A && !s->v8m_secure) { - /* This case is UNDEFINED. */ - unallocated_encoding(s); - return true; - } - - 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); - store_reg(s, a->rd, tmp); - return true; -} - -/* - * Load/store register index - */ - -static ISSInfo make_issinfo(DisasContext *s, int rd, bool p, bool w) -{ - ISSInfo ret; - - /* ISS not valid if writeback */ - if (p && !w) { - ret = rd; - if (curr_insn_len(s) == 2) { - ret |= ISSIs16Bit; - } - } else { - ret = ISSInvalid; - } - return ret; -} - -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); - } - - if (a->p) { - TCGv_i32 ofs = load_reg(s, a->rm); - gen_arm_shift_im(ofs, a->shtype, a->shimm, 0); - if (a->u) { - tcg_gen_add_i32(addr, addr, ofs); - } else { - tcg_gen_sub_i32(addr, addr, ofs); - } - tcg_temp_free_i32(ofs); - } - return addr; -} - -static void op_addr_rr_post(DisasContext *s, arg_ldst_rr *a, - TCGv_i32 addr, int address_offset) -{ - if (!a->p) { - TCGv_i32 ofs = load_reg(s, a->rm); - gen_arm_shift_im(ofs, a->shtype, a->shimm, 0); - if (a->u) { - tcg_gen_add_i32(addr, addr, ofs); - } 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); - store_reg(s, a->rn, addr); -} - -static bool op_load_rr(DisasContext *s, arg_ldst_rr *a, - MemOp mop, int mem_idx) -{ - ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w); - TCGv_i32 addr, tmp; - - addr = op_addr_rr_pre(s, a); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop); - disas_set_da_iss(s, mop, issinfo); - - /* - * Perform base writeback before the loaded value to - * ensure correct behavior with overlapping index registers. - */ - op_addr_rr_post(s, a, addr, 0); - store_reg_from_load(s, a->rt, tmp); - return true; -} - -static bool op_store_rr(DisasContext *s, arg_ldst_rr *a, - MemOp mop, int mem_idx) -{ - ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w) | ISSIsWrite; - TCGv_i32 addr, tmp; - - /* - * In Thumb encodings of stores Rn=1111 is UNDEF; for Arm it - * is either UNPREDICTABLE or has defined behaviour - */ - if (s->thumb && a->rn == 15) { - return false; - } - - addr = op_addr_rr_pre(s, 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; -} - -static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a) -{ - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; - - if (!ENABLE_ARCH_5TE) { - return false; - } - if (a->rt & 1) { - unallocated_encoding(s); - return true; - } - addr = op_addr_rr_pre(s, a); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt, tmp); - - tcg_gen_addi_i32(addr, addr, 4); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt + 1, tmp); - - /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_rr_post(s, a, addr, -4); - return true; -} - -static bool trans_STRD_rr(DisasContext *s, arg_ldst_rr *a) -{ - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; - - if (!ENABLE_ARCH_5TE) { - return false; - } - if (a->rt & 1) { - unallocated_encoding(s); - return true; - } - addr = op_addr_rr_pre(s, 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; -} - -/* - * Load/store immediate index - */ - -static TCGv_i32 op_addr_ri_pre(DisasContext *s, arg_ldst_ri *a) -{ - int ofs = a->imm; - - if (!a->u) { - ofs = -ofs; - } - - if (s->v8m_stackcheck && a->rn == 13 && a->w) { - /* - * Stackcheck. Here we know 'addr' is the current SP; - * U is set if we're moving SP up, else down. It is - * UNKNOWN whether the limit check triggers when SP starts - * below the limit and ends up above it; we chose to do so. - */ - 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); - } else { - gen_helper_v8m_stackcheck(cpu_env, cpu_R[13]); - } - } - - return add_reg_for_lit(s, a->rn, a->p ? ofs : 0); -} - -static void op_addr_ri_post(DisasContext *s, arg_ldst_ri *a, - TCGv_i32 addr, int address_offset) -{ - if (!a->p) { - if (a->u) { - address_offset += a->imm; - } else { - address_offset -= a->imm; - } - } else if (!a->w) { - tcg_temp_free_i32(addr); - return; - } - tcg_gen_addi_i32(addr, addr, address_offset); - store_reg(s, a->rn, addr); -} - -static bool op_load_ri(DisasContext *s, arg_ldst_ri *a, - MemOp mop, int mem_idx) -{ - ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w); - TCGv_i32 addr, tmp; - - addr = op_addr_ri_pre(s, a); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, mop); - disas_set_da_iss(s, mop, issinfo); - - /* - * Perform base writeback before the loaded value to - * ensure correct behavior with overlapping index registers. - */ - op_addr_ri_post(s, a, addr, 0); - store_reg_from_load(s, a->rt, tmp); - return true; -} - -static bool op_store_ri(DisasContext *s, arg_ldst_ri *a, - MemOp mop, int mem_idx) -{ - ISSInfo issinfo = make_issinfo(s, a->rt, a->p, a->w) | ISSIsWrite; - TCGv_i32 addr, tmp; - - /* - * In Thumb encodings of stores Rn=1111 is UNDEF; for Arm it - * is either UNPREDICTABLE or has defined behaviour - */ - if (s->thumb && a->rn == 15) { - return false; - } - - addr = op_addr_ri_pre(s, 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; -} - -static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) -{ - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; - - addr = op_addr_ri_pre(s, a); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, a->rt, tmp); - - tcg_gen_addi_i32(addr, addr, 4); - - tmp = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN); - store_reg(s, rt2, tmp); - - /* LDRD w/ base writeback is undefined if the registers overlap. */ - op_addr_ri_post(s, a, addr, -4); - return true; -} - -static bool trans_LDRD_ri_a32(DisasContext *s, arg_ldst_ri *a) -{ - if (!ENABLE_ARCH_5TE || (a->rt & 1)) { - return false; - } - return op_ldrd_ri(s, a, a->rt + 1); -} - -static bool trans_LDRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) -{ - arg_ldst_ri b = { - .u = a->u, .w = a->w, .p = a->p, - .rn = a->rn, .rt = a->rt, .imm = a->imm - }; - return op_ldrd_ri(s, &b, a->rt2); -} - -static bool op_strd_ri(DisasContext *s, arg_ldst_ri *a, int rt2) -{ - int mem_idx = get_mem_index(s); - TCGv_i32 addr, tmp; - - addr = op_addr_ri_pre(s, 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, 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; -} - -static bool trans_STRD_ri_a32(DisasContext *s, arg_ldst_ri *a) -{ - if (!ENABLE_ARCH_5TE || (a->rt & 1)) { - return false; - } - return op_strd_ri(s, a, a->rt + 1); -} - -static bool trans_STRD_ri_t32(DisasContext *s, arg_ldst_ri2 *a) -{ - arg_ldst_ri b = { - .u = a->u, .w = a->w, .p = a->p, - .rn = a->rn, .rt = a->rt, .imm = a->imm - }; - return op_strd_ri(s, &b, a->rt2); -} - -#define DO_LDST(NAME, WHICH, MEMOP) \ -static bool trans_##NAME##_ri(DisasContext *s, arg_ldst_ri *a) \ -{ \ - return op_##WHICH##_ri(s, a, MEMOP, get_mem_index(s)); \ -} \ -static bool trans_##NAME##T_ri(DisasContext *s, arg_ldst_ri *a) \ -{ \ - return op_##WHICH##_ri(s, a, MEMOP, get_a32_user_mem_index(s)); \ -} \ -static bool trans_##NAME##_rr(DisasContext *s, arg_ldst_rr *a) \ -{ \ - return op_##WHICH##_rr(s, a, MEMOP, get_mem_index(s)); \ -} \ -static bool trans_##NAME##T_rr(DisasContext *s, arg_ldst_rr *a) \ -{ \ - return op_##WHICH##_rr(s, a, MEMOP, get_a32_user_mem_index(s)); \ -} - -DO_LDST(LDR, load, MO_UL) -DO_LDST(LDRB, load, MO_UB) -DO_LDST(LDRH, load, MO_UW) -DO_LDST(LDRSB, load, MO_SB) -DO_LDST(LDRSH, load, MO_SW) - -DO_LDST(STR, store, MO_UL) -DO_LDST(STRB, store, MO_UB) -DO_LDST(STRH, store, MO_UW) - -#undef DO_LDST - -/* - * Synchronization primitives - */ - -static bool op_swp(DisasContext *s, arg_SWP *a, MemOp opc) -{ - TCGv_i32 addr, tmp; - TCGv taddr; - - 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; -} - -static bool trans_SWP(DisasContext *s, arg_SWP *a) -{ - return op_swp(s, a, MO_UL | MO_ALIGN); -} - -static bool trans_SWPB(DisasContext *s, arg_SWP *a) -{ - return op_swp(s, a, MO_UB); -} - -/* - * Load/Store Exclusive and Load-Acquire/Store-Release - */ - -static bool op_strex(DisasContext *s, arg_STREX *a, MemOp mop, bool rel) -{ - TCGv_i32 addr; - /* Some cases stopped being UNPREDICTABLE in v8A (but not v8M) */ - bool v8a = ENABLE_ARCH_8 && !arm_dc_feature(s, ARM_FEATURE_M); - - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rd == 15 || a->rn == 15 || a->rt == 15 - || a->rd == a->rn || a->rd == a->rt - || (!v8a && s->thumb && (a->rd == 13 || a->rt == 13)) - || (mop == MO_64 - && (a->rt2 == 15 - || a->rd == a->rt2 - || (!v8a && s->thumb && a->rt2 == 13)))) { - unallocated_encoding(s); - return true; - } - - if (rel) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - - addr = tcg_temp_local_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; -} - -static bool trans_STREX(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_6) { - return false; - } - return op_strex(s, a, MO_32, false); -} - -static bool trans_STREXD_a32(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_6K) { - return false; - } - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rt & 1) { - unallocated_encoding(s); - return true; - } - a->rt2 = a->rt + 1; - return op_strex(s, a, MO_64, false); -} - -static bool trans_STREXD_t32(DisasContext *s, arg_STREX *a) -{ - return op_strex(s, a, MO_64, false); -} - -static bool trans_STREXB(DisasContext *s, arg_STREX *a) -{ - if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { - return false; - } - return op_strex(s, a, MO_8, false); -} - -static bool trans_STREXH(DisasContext *s, arg_STREX *a) -{ - if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { - return false; - } - return op_strex(s, a, MO_16, false); -} - -static bool trans_STLEX(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_strex(s, a, MO_32, true); -} - -static bool trans_STLEXD_a32(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rt & 1) { - unallocated_encoding(s); - return true; - } - a->rt2 = a->rt + 1; - return op_strex(s, a, MO_64, true); -} - -static bool trans_STLEXD_t32(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_strex(s, a, MO_64, true); -} - -static bool trans_STLEXB(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_strex(s, a, MO_8, true); -} - -static bool trans_STLEXH(DisasContext *s, arg_STREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_strex(s, a, MO_16, true); -} - -static bool op_stl(DisasContext *s, arg_STL *a, MemOp mop) -{ - TCGv_i32 addr, tmp; - - if (!ENABLE_ARCH_8) { - return false; - } - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rn == 15 || a->rt == 15) { - unallocated_encoding(s); - return true; - } - - addr = load_reg(s, a->rn); - tmp = load_reg(s, a->rt); - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - 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; -} - -static bool trans_STL(DisasContext *s, arg_STL *a) -{ - return op_stl(s, a, MO_UL); -} - -static bool trans_STLB(DisasContext *s, arg_STL *a) -{ - return op_stl(s, a, MO_UB); -} - -static bool trans_STLH(DisasContext *s, arg_STL *a) -{ - return op_stl(s, a, MO_UW); -} - -static bool op_ldrex(DisasContext *s, arg_LDREX *a, MemOp mop, bool acq) -{ - TCGv_i32 addr; - /* Some cases stopped being UNPREDICTABLE in v8A (but not v8M) */ - bool v8a = ENABLE_ARCH_8 && !arm_dc_feature(s, ARM_FEATURE_M); - - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rn == 15 || a->rt == 15 - || (!v8a && s->thumb && a->rt == 13) - || (mop == MO_64 - && (a->rt2 == 15 || a->rt == a->rt2 - || (!v8a && s->thumb && a->rt2 == 13)))) { - unallocated_encoding(s); - return true; - } - - addr = tcg_temp_local_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); - } - return true; -} - -static bool trans_LDREX(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_6) { - return false; - } - return op_ldrex(s, a, MO_32, false); -} - -static bool trans_LDREXD_a32(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_6K) { - return false; - } - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rt & 1) { - unallocated_encoding(s); - return true; - } - a->rt2 = a->rt + 1; - return op_ldrex(s, a, MO_64, false); -} - -static bool trans_LDREXD_t32(DisasContext *s, arg_LDREX *a) -{ - return op_ldrex(s, a, MO_64, false); -} - -static bool trans_LDREXB(DisasContext *s, arg_LDREX *a) -{ - if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { - return false; - } - return op_ldrex(s, a, MO_8, false); -} - -static bool trans_LDREXH(DisasContext *s, arg_LDREX *a) -{ - if (s->thumb ? !ENABLE_ARCH_7 : !ENABLE_ARCH_6K) { - return false; - } - return op_ldrex(s, a, MO_16, false); -} - -static bool trans_LDAEX(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_ldrex(s, a, MO_32, true); -} - -static bool trans_LDAEXD_a32(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rt & 1) { - unallocated_encoding(s); - return true; - } - a->rt2 = a->rt + 1; - return op_ldrex(s, a, MO_64, true); -} - -static bool trans_LDAEXD_t32(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_ldrex(s, a, MO_64, true); -} - -static bool trans_LDAEXB(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_ldrex(s, a, MO_8, true); -} - -static bool trans_LDAEXH(DisasContext *s, arg_LDREX *a) -{ - if (!ENABLE_ARCH_8) { - return false; - } - return op_ldrex(s, a, MO_16, true); -} - -static bool op_lda(DisasContext *s, arg_LDA *a, MemOp mop) -{ - TCGv_i32 addr, tmp; - - if (!ENABLE_ARCH_8) { - return false; - } - /* We UNDEF for these UNPREDICTABLE cases. */ - if (a->rn == 15 || a->rt == 15) { - unallocated_encoding(s); - return true; - } - - addr = load_reg(s, a->rn); - 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); - return true; -} - -static bool trans_LDA(DisasContext *s, arg_LDA *a) -{ - return op_lda(s, a, MO_UL); -} - -static bool trans_LDAB(DisasContext *s, arg_LDA *a) -{ - return op_lda(s, a, MO_UB); -} - -static bool trans_LDAH(DisasContext *s, arg_LDA *a) -{ - return op_lda(s, a, MO_UW); -} - -/* - * Media instructions - */ - -static bool trans_USADA8(DisasContext *s, arg_USADA8 *a) -{ - TCGv_i32 t1, t2; - - if (!ENABLE_ARCH_6) { - return false; - } - - 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; -} - -static bool op_bfx(DisasContext *s, arg_UBFX *a, bool u) -{ - TCGv_i32 tmp; - int width = a->widthm1 + 1; - int shift = a->lsb; - - if (!ENABLE_ARCH_6T2) { - return false; - } - if (shift + width > 32) { - /* UNPREDICTABLE; we choose to UNDEF */ - unallocated_encoding(s); - return true; - } - - tmp = load_reg(s, a->rn); - if (u) { - tcg_gen_extract_i32(tmp, tmp, shift, width); - } else { - tcg_gen_sextract_i32(tmp, tmp, shift, width); - } - store_reg(s, a->rd, tmp); - return true; -} - -static bool trans_SBFX(DisasContext *s, arg_SBFX *a) -{ - return op_bfx(s, a, false); -} - -static bool trans_UBFX(DisasContext *s, arg_UBFX *a) -{ - return op_bfx(s, a, true); -} - -static bool trans_BFCI(DisasContext *s, arg_BFCI *a) -{ - TCGv_i32 tmp; - int msb = a->msb, lsb = a->lsb; - int width; - - if (!ENABLE_ARCH_6T2) { - return false; - } - if (msb < lsb) { - /* UNPREDICTABLE; we choose to UNDEF */ - unallocated_encoding(s); - return true; - } - - width = msb + 1 - lsb; - if (a->rn == 15) { - /* BFC */ - tmp = tcg_const_i32(0); - } else { - /* BFI */ - tmp = 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); - return true; -} - -static bool trans_UDF(DisasContext *s, arg_UDF *a) -{ - unallocated_encoding(s); - return true; -} - -/* - * Parallel addition and subtraction - */ - -static bool op_par_addsub(DisasContext *s, arg_rrr *a, - void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 t0, t1; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_6) { - return false; - } - - t0 = load_reg(s, a->rn); - t1 = load_reg(s, a->rm); - - gen(t0, t0, t1); - - tcg_temp_free_i32(t1); - store_reg(s, a->rd, t0); - return true; -} - -static bool op_par_addsub_ge(DisasContext *s, arg_rrr *a, - void (*gen)(TCGv_i32, TCGv_i32, - TCGv_i32, TCGv_ptr)) -{ - TCGv_i32 t0, t1; - TCGv_ptr ge; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_6) { - return false; - } - - t0 = load_reg(s, a->rn); - t1 = load_reg(s, a->rm); - - ge = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ge, cpu_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; -} - -#define DO_PAR_ADDSUB(NAME, helper) \ -static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ -{ \ - return op_par_addsub(s, a, helper); \ -} - -#define DO_PAR_ADDSUB_GE(NAME, helper) \ -static bool trans_##NAME(DisasContext *s, arg_rrr *a) \ -{ \ - return op_par_addsub_ge(s, a, helper); \ -} - -DO_PAR_ADDSUB_GE(SADD16, gen_helper_sadd16) -DO_PAR_ADDSUB_GE(SASX, gen_helper_saddsubx) -DO_PAR_ADDSUB_GE(SSAX, gen_helper_ssubaddx) -DO_PAR_ADDSUB_GE(SSUB16, gen_helper_ssub16) -DO_PAR_ADDSUB_GE(SADD8, gen_helper_sadd8) -DO_PAR_ADDSUB_GE(SSUB8, gen_helper_ssub8) - -DO_PAR_ADDSUB_GE(UADD16, gen_helper_uadd16) -DO_PAR_ADDSUB_GE(UASX, gen_helper_uaddsubx) -DO_PAR_ADDSUB_GE(USAX, gen_helper_usubaddx) -DO_PAR_ADDSUB_GE(USUB16, gen_helper_usub16) -DO_PAR_ADDSUB_GE(UADD8, gen_helper_uadd8) -DO_PAR_ADDSUB_GE(USUB8, gen_helper_usub8) - -DO_PAR_ADDSUB(QADD16, gen_helper_qadd16) -DO_PAR_ADDSUB(QASX, gen_helper_qaddsubx) -DO_PAR_ADDSUB(QSAX, gen_helper_qsubaddx) -DO_PAR_ADDSUB(QSUB16, gen_helper_qsub16) -DO_PAR_ADDSUB(QADD8, gen_helper_qadd8) -DO_PAR_ADDSUB(QSUB8, gen_helper_qsub8) - -DO_PAR_ADDSUB(UQADD16, gen_helper_uqadd16) -DO_PAR_ADDSUB(UQASX, gen_helper_uqaddsubx) -DO_PAR_ADDSUB(UQSAX, gen_helper_uqsubaddx) -DO_PAR_ADDSUB(UQSUB16, gen_helper_uqsub16) -DO_PAR_ADDSUB(UQADD8, gen_helper_uqadd8) -DO_PAR_ADDSUB(UQSUB8, gen_helper_uqsub8) - -DO_PAR_ADDSUB(SHADD16, gen_helper_shadd16) -DO_PAR_ADDSUB(SHASX, gen_helper_shaddsubx) -DO_PAR_ADDSUB(SHSAX, gen_helper_shsubaddx) -DO_PAR_ADDSUB(SHSUB16, gen_helper_shsub16) -DO_PAR_ADDSUB(SHADD8, gen_helper_shadd8) -DO_PAR_ADDSUB(SHSUB8, gen_helper_shsub8) - -DO_PAR_ADDSUB(UHADD16, gen_helper_uhadd16) -DO_PAR_ADDSUB(UHASX, gen_helper_uhaddsubx) -DO_PAR_ADDSUB(UHSAX, gen_helper_uhsubaddx) -DO_PAR_ADDSUB(UHSUB16, gen_helper_uhsub16) -DO_PAR_ADDSUB(UHADD8, gen_helper_uhadd8) -DO_PAR_ADDSUB(UHSUB8, gen_helper_uhsub8) - -#undef DO_PAR_ADDSUB -#undef DO_PAR_ADDSUB_GE - -/* - * Packing, unpacking, saturation, and reversal - */ - -static bool trans_PKH(DisasContext *s, arg_PKH *a) -{ - TCGv_i32 tn, tm; - int shift = a->imm; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_6) { - return false; - } - - tn = load_reg(s, a->rn); - tm = load_reg(s, a->rm); - if (a->tb) { - /* PKHTB */ - if (shift == 0) { - shift = 31; - } - tcg_gen_sari_i32(tm, tm, shift); - tcg_gen_deposit_i32(tn, tn, tm, 0, 16); - } else { - /* PKHBT */ - 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; -} - -static bool op_sat(DisasContext *s, arg_sat *a, - void (*gen)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 tmp; - int shift = a->imm; - - if (!ENABLE_ARCH_6) { - return false; - } - - tmp = load_reg(s, a->rn); - if (a->sh) { - tcg_gen_sari_i32(tmp, tmp, shift ? shift : 31); - } else { - tcg_gen_shli_i32(tmp, tmp, shift); - } - - gen(tmp, cpu_env, tmp, tcg_constant_i32(a->satimm)); - - store_reg(s, a->rd, tmp); - return true; -} - -static bool trans_SSAT(DisasContext *s, arg_sat *a) -{ - return op_sat(s, a, gen_helper_ssat); -} - -static bool trans_USAT(DisasContext *s, arg_sat *a) -{ - return op_sat(s, a, gen_helper_usat); -} - -static bool trans_SSAT16(DisasContext *s, arg_sat *a) -{ - if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { - return false; - } - return op_sat(s, a, gen_helper_ssat16); -} - -static bool trans_USAT16(DisasContext *s, arg_sat *a) -{ - if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { - return false; - } - return op_sat(s, a, gen_helper_usat16); -} - -static bool op_xta(DisasContext *s, arg_rrr_rot *a, - void (*gen_extract)(TCGv_i32, TCGv_i32), - void (*gen_add)(TCGv_i32, TCGv_i32, TCGv_i32)) -{ - TCGv_i32 tmp; - - if (!ENABLE_ARCH_6) { - return false; - } - - tmp = load_reg(s, a->rm); - /* - * TODO: In many cases we could do a shift instead of a rotate. - * Combined with a simple extend, that becomes an extract. - */ - tcg_gen_rotri_i32(tmp, tmp, a->rot * 8); - gen_extract(tmp, tmp); - - 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; -} - -static bool trans_SXTAB(DisasContext *s, arg_rrr_rot *a) -{ - return op_xta(s, a, tcg_gen_ext8s_i32, tcg_gen_add_i32); -} - -static bool trans_SXTAH(DisasContext *s, arg_rrr_rot *a) -{ - return op_xta(s, a, tcg_gen_ext16s_i32, tcg_gen_add_i32); -} - -static bool trans_SXTAB16(DisasContext *s, arg_rrr_rot *a) -{ - if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { - return false; - } - return op_xta(s, a, gen_helper_sxtb16, gen_add16); -} - -static bool trans_UXTAB(DisasContext *s, arg_rrr_rot *a) -{ - return op_xta(s, a, tcg_gen_ext8u_i32, tcg_gen_add_i32); -} - -static bool trans_UXTAH(DisasContext *s, arg_rrr_rot *a) -{ - return op_xta(s, a, tcg_gen_ext16u_i32, tcg_gen_add_i32); -} - -static bool trans_UXTAB16(DisasContext *s, arg_rrr_rot *a) -{ - if (s->thumb && !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP)) { - return false; - } - return op_xta(s, a, gen_helper_uxtb16, gen_add16); -} - -static bool trans_SEL(DisasContext *s, arg_rrr *a) -{ - TCGv_i32 t1, t2, t3; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_6) { - return false; - } - - 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)); - 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; -} - -static bool op_rr(DisasContext *s, arg_rr *a, - void (*gen)(TCGv_i32, TCGv_i32)) -{ - TCGv_i32 tmp; - - tmp = load_reg(s, a->rm); - gen(tmp, tmp); - store_reg(s, a->rd, tmp); - return true; -} - -static bool trans_REV(DisasContext *s, arg_rr *a) -{ - if (!ENABLE_ARCH_6) { - return false; - } - return op_rr(s, a, tcg_gen_bswap32_i32); -} - -static bool trans_REV16(DisasContext *s, arg_rr *a) -{ - if (!ENABLE_ARCH_6) { - return false; - } - return op_rr(s, a, gen_rev16); -} - -static bool trans_REVSH(DisasContext *s, arg_rr *a) -{ - if (!ENABLE_ARCH_6) { - return false; - } - return op_rr(s, a, gen_revsh); -} - -static bool trans_RBIT(DisasContext *s, arg_rr *a) -{ - if (!ENABLE_ARCH_6T2) { - return false; - } - return op_rr(s, a, gen_helper_rbit); -} - -/* - * Signed multiply, signed and unsigned divide - */ - -static bool op_smlad(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) -{ - TCGv_i32 t1, t2; - - if (!ENABLE_ARCH_6) { - return false; - } - - t1 = load_reg(s, a->rn); - t2 = load_reg(s, a->rm); - if (m_swap) { - gen_swap_half(t2, t2); - } - gen_smul_dual(t1, t2); - - if (sub) { - /* - * This subtraction cannot overflow, so we can do a simple - * 32-bit subtraction and then a possible 32-bit saturating - * 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); - } - } else if (a->ra == 15) { - /* Single saturation-checking addition */ - gen_helper_add_setq(t1, cpu_env, t1, t2); - tcg_temp_free_i32(t2); - } else { - /* - * We need to add the products and Ra together and then - * determine whether the final result overflowed. Doing - * this as two separate add-and-check-overflow steps incorrectly - * sets Q for cases like (-32768 * -32768) + (-32768 * -32768) + -1. - * Do all the arithmetic at 64-bits and then check for overflow. - */ - TCGv_i64 p64, q64; - TCGv_i32 t3, qf, one; - - p64 = tcg_temp_new_i64(); - q64 = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(p64, t1); - tcg_gen_ext_i32_i64(q64, t2); - tcg_gen_add_i64(p64, p64, q64); - 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) - * is different from the sign-extension of t1. - */ - t3 = tcg_temp_new_i32(); - tcg_gen_sari_i32(t3, t1, 31); - qf = load_cpu_field(QF); - 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; -} - -static bool trans_SMLAD(DisasContext *s, arg_rrrr *a) -{ - return op_smlad(s, a, false, false); -} - -static bool trans_SMLADX(DisasContext *s, arg_rrrr *a) -{ - return op_smlad(s, a, true, false); -} - -static bool trans_SMLSD(DisasContext *s, arg_rrrr *a) -{ - return op_smlad(s, a, false, true); -} - -static bool trans_SMLSDX(DisasContext *s, arg_rrrr *a) -{ - return op_smlad(s, a, true, true); -} - -static bool op_smlald(DisasContext *s, arg_rrrr *a, bool m_swap, bool sub) -{ - TCGv_i32 t1, t2; - TCGv_i64 l1, l2; - - if (!ENABLE_ARCH_6) { - return false; - } - - t1 = load_reg(s, a->rn); - t2 = load_reg(s, a->rm); - if (m_swap) { - gen_swap_half(t2, t2); - } - gen_smul_dual(t1, t2); - - l1 = tcg_temp_new_i64(); - 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; -} - -static bool trans_SMLALD(DisasContext *s, arg_rrrr *a) -{ - return op_smlald(s, a, false, false); -} - -static bool trans_SMLALDX(DisasContext *s, arg_rrrr *a) -{ - return op_smlald(s, a, true, false); -} - -static bool trans_SMLSLD(DisasContext *s, arg_rrrr *a) -{ - return op_smlald(s, a, false, true); -} - -static bool trans_SMLSLDX(DisasContext *s, arg_rrrr *a) -{ - return op_smlald(s, a, true, true); -} - -static bool op_smmla(DisasContext *s, arg_rrrr *a, bool round, bool sub) -{ - TCGv_i32 t1, t2; - - if (s->thumb - ? !arm_dc_feature(s, ARM_FEATURE_THUMB_DSP) - : !ENABLE_ARCH_6) { - return false; - } - - t1 = load_reg(s, a->rn); - t2 = load_reg(s, a->rm); - tcg_gen_muls2_i32(t2, t1, t1, t2); - - if (a->ra != 15) { - TCGv_i32 t3 = load_reg(s, a->ra); - if (sub) { - /* - * For SMMLS, we need a 64-bit subtract. Borrow caused by - * a non-zero multiplicand lowpart, and the correct result - * lowpart for rounding. - */ - tcg_gen_sub2_i32(t2, t1, tcg_constant_i32(0), t3, t2, t1); - } else { - tcg_gen_add_i32(t1, t1, t3); - } - tcg_temp_free_i32(t3); - } - if (round) { - /* - * Adding 0x80000000 to the 64-bit quantity means that we have - * carry in to the high word when the low word has the msb set. - */ - 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; -} - -static bool trans_SMMLA(DisasContext *s, arg_rrrr *a) -{ - return op_smmla(s, a, false, false); -} - -static bool trans_SMMLAR(DisasContext *s, arg_rrrr *a) -{ - return op_smmla(s, a, true, false); -} - -static bool trans_SMMLS(DisasContext *s, arg_rrrr *a) -{ - return op_smmla(s, a, false, true); -} - -static bool trans_SMMLSR(DisasContext *s, arg_rrrr *a) -{ - return op_smmla(s, a, true, true); -} - -static bool op_div(DisasContext *s, arg_rrr *a, bool u) -{ - TCGv_i32 t1, t2; - - if (s->thumb - ? !dc_isar_feature(aa32_thumb_div, s) - : !dc_isar_feature(aa32_arm_div, s)) { - return false; - } - - t1 = load_reg(s, a->rn); - t2 = load_reg(s, a->rm); - if (u) { - gen_helper_udiv(t1, cpu_env, t1, t2); - } else { - gen_helper_sdiv(t1, cpu_env, t1, t2); - } - tcg_temp_free_i32(t2); - store_reg(s, a->rd, t1); - return true; -} - -static bool trans_SDIV(DisasContext *s, arg_rrr *a) -{ - return op_div(s, a, false); -} - -static bool trans_UDIV(DisasContext *s, arg_rrr *a) -{ - return op_div(s, a, true); -} - -/* - * Block data transfer - */ - -static TCGv_i32 op_addr_block_pre(DisasContext *s, arg_ldst_block *a, int n) -{ - TCGv_i32 addr = load_reg(s, a->rn); - - if (a->b) { - if (a->i) { - /* pre increment */ - tcg_gen_addi_i32(addr, addr, 4); - } else { - /* pre decrement */ - tcg_gen_addi_i32(addr, addr, -(n * 4)); - } - } else if (!a->i && n != 1) { - /* post decrement */ - tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); - } - - if (s->v8m_stackcheck && a->rn == 13 && a->w) { - /* - * 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 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); - } - - return addr; -} - -static void op_addr_block_post(DisasContext *s, arg_ldst_block *a, - TCGv_i32 addr, int n) -{ - if (a->w) { - /* write back */ - if (!a->b) { - if (a->i) { - /* post increment */ - tcg_gen_addi_i32(addr, addr, 4); - } else { - /* post decrement */ - tcg_gen_addi_i32(addr, addr, -(n * 4)); - } - } else if (!a->i && n != 1) { - /* pre decrement */ - 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) -{ - int i, j, n, list, mem_idx; - bool user = a->u; - TCGv_i32 addr, tmp; - - if (user) { - /* STM (user) */ - if (IS_USER(s)) { - /* Only usable in supervisor mode. */ - unallocated_encoding(s); - return true; - } - } - - list = a->list; - n = ctpop16(list); - if (n < min_n || a->rn == 15) { - unallocated_encoding(s); - return true; - } - - s->eci_handled = true; - - addr = op_addr_block_pre(s, a, n); - mem_idx = get_mem_index(s); - - for (i = j = 0; i < 16; i++) { - if (!(list & (1 << i))) { - continue; - } - - if (user && i != 15) { - tmp = tcg_temp_new_i32(); - gen_helper_get_user_reg(tmp, cpu_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) { - tcg_gen_addi_i32(addr, addr, 4); - } - } - - op_addr_block_post(s, a, addr, n); - clear_eci_state(s); - return true; -} - -static bool trans_STM(DisasContext *s, arg_ldst_block *a) -{ - /* BitCount(list) < 1 is UNPREDICTABLE */ - return op_stm(s, a, 1); -} - -static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a) -{ - /* Writeback register in register list is UNPREDICTABLE for T32. */ - if (a->w && (a->list & (1 << a->rn))) { - unallocated_encoding(s); - return true; - } - /* BitCount(list) < 2 is UNPREDICTABLE */ - return op_stm(s, a, 2); -} - -static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n) -{ - int i, j, n, list, mem_idx; - bool loaded_base; - bool user = a->u; - bool exc_return = false; - TCGv_i32 addr, tmp, loaded_var; - - if (user) { - /* LDM (user), LDM (exception return) */ - if (IS_USER(s)) { - /* Only usable in supervisor mode. */ - unallocated_encoding(s); - return true; - } - if (extract32(a->list, 15, 1)) { - exc_return = true; - user = false; - } else { - /* LDM (user) does not allow writeback. */ - if (a->w) { - unallocated_encoding(s); - return true; - } - } - } - - list = a->list; - n = ctpop16(list); - if (n < min_n || a->rn == 15) { - unallocated_encoding(s); - return true; - } - - s->eci_handled = true; - - addr = op_addr_block_pre(s, a, n); - mem_idx = get_mem_index(s); - loaded_base = false; - loaded_var = NULL; - - for (i = j = 0; i < 16; i++) { - if (!(list & (1 << i))) { - continue; - } - - 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); - } else if (i == a->rn) { - loaded_var = tmp; - loaded_base = true; - } else if (i == 15 && exc_return) { - store_pc_exc_ret(s, tmp); - } else { - store_reg_from_load(s, i, tmp); - } - - /* No need to add after the last transfer. */ - if (++j != n) { - tcg_gen_addi_i32(addr, addr, 4); - } - } - - op_addr_block_post(s, a, addr, n); - - if (loaded_base) { - /* Note that we reject base == pc above. */ - store_reg(s, a->rn, loaded_var); - } - - 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); - /* Must exit loop to check un-masked IRQs */ - s->base.is_jmp = DISAS_EXIT; - } - clear_eci_state(s); - return true; -} - -static bool trans_LDM_a32(DisasContext *s, arg_ldst_block *a) -{ - /* - * Writeback register in register list is UNPREDICTABLE - * for ArchVersion() >= 7. Prior to v7, A32 would write - * an UNKNOWN value to the base register. - */ - if (ENABLE_ARCH_7 && a->w && (a->list & (1 << a->rn))) { - unallocated_encoding(s); - return true; - } - /* BitCount(list) < 1 is UNPREDICTABLE */ - return do_ldm(s, a, 1); -} - -static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a) -{ - /* Writeback register in register list is UNPREDICTABLE for T32. */ - if (a->w && (a->list & (1 << a->rn))) { - unallocated_encoding(s); - return true; - } - /* BitCount(list) < 2 is UNPREDICTABLE */ - return do_ldm(s, a, 2); -} - -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); -} - -static bool trans_CLRM(DisasContext *s, arg_CLRM *a) -{ - int i; - TCGv_i32 zero; - - if (!dc_isar_feature(aa32_m_sec_state, s)) { - return false; - } - - if (extract32(a->list, 13, 1)) { - return false; - } - - if (!a->list) { - /* UNPREDICTABLE; we choose to UNDEF */ - return false; - } - - s->eci_handled = true; - - zero = tcg_constant_i32(0); - for (i = 0; i < 15; i++) { - if (extract32(a->list, i, 1)) { - /* Clear R[i] */ - tcg_gen_mov_i32(cpu_R[i], zero); - } - } - if (extract32(a->list, 15, 1)) { - /* - * 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); - } - clear_eci_state(s); - return true; -} - -/* - * Branch, branch with link - */ - -static bool trans_B(DisasContext *s, arg_i *a) -{ - gen_jmp(s, jmp_diff(s, a->imm)); - return true; -} - -static bool trans_B_cond_thumb(DisasContext *s, arg_ci *a) -{ - /* This has cond from encoding, required to be outside IT block. */ - if (a->cond >= 0xe) { - return false; - } - if (s->condexec_mask) { - unallocated_encoding(s); - return true; - } - arm_skip_unless(s, a->cond); - gen_jmp(s, jmp_diff(s, a->imm)); - return true; -} - -static bool trans_BL(DisasContext *s, arg_i *a) -{ - gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); - gen_jmp(s, jmp_diff(s, a->imm)); - return true; -} - -static bool trans_BLX_i(DisasContext *s, arg_BLX_i *a) -{ - /* - * BLX would be useless on M-profile; the encoding space - * is used for other insns from v8.1M onward, and UNDEFs before that. - */ - if (arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - - /* For A32, ARM_FEATURE_V5 is checked near the start of the uncond block. */ - if (s->thumb && (a->imm & 2)) { - return false; - } - gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | s->thumb); - store_cpu_field_constant(!s->thumb, thumb); - /* 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)); - gen_pc_plus_diff(s, cpu_R[14], jmp_diff(s, a->imm << 12)); - return true; -} - -static bool trans_BL_suffix(DisasContext *s, arg_BL_suffix *a) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - - assert(!arm_dc_feature(s, ARM_FEATURE_THUMB2)); - tcg_gen_addi_i32(tmp, cpu_R[14], (a->imm << 1) | 1); - gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | 1); - gen_bx(s, tmp); - return true; -} - -static bool trans_BLX_suffix(DisasContext *s, arg_BLX_suffix *a) -{ - TCGv_i32 tmp; - - assert(!arm_dc_feature(s, ARM_FEATURE_THUMB2)); - if (!ENABLE_ARCH_5) { - return false; - } - tmp = tcg_temp_new_i32(); - tcg_gen_addi_i32(tmp, cpu_R[14], a->imm << 1); - tcg_gen_andi_i32(tmp, tmp, 0xfffffffc); - gen_pc_plus_diff(s, cpu_R[14], curr_insn_len(s) | 1); - gen_bx(s, tmp); - return true; -} - -static bool trans_BF(DisasContext *s, arg_BF *a) -{ - /* - * M-profile branch future insns. The architecture permits an - * implementation to implement these as NOPs (equivalent to - * discarding the LO_BRANCH_INFO cache immediately), and we - * take that IMPDEF option because for QEMU a "real" implementation - * would be complicated and wouldn't execute any faster. - */ - if (!dc_isar_feature(aa32_lob, s)) { - return false; - } - if (a->boff == 0) { - /* SEE "Related encodings" (loop insns) */ - return false; - } - /* Handle as NOP */ - return true; -} - -static bool trans_DLS(DisasContext *s, arg_DLS *a) -{ - /* M-profile low-overhead loop start */ - TCGv_i32 tmp; - - if (!dc_isar_feature(aa32_lob, s)) { - return false; - } - if (a->rn == 13 || a->rn == 15) { - /* - * For DLSTP rn == 15 is a related encoding (LCTP); the - * other cases caught by this condition are all - * CONSTRAINED UNPREDICTABLE: we choose to UNDEF - */ - return false; - } - - if (a->size != 4) { - /* DLSTP */ - if (!dc_isar_feature(aa32_mve, s)) { - return false; - } - if (!vfp_access_check(s)) { - return true; - } - } - - /* Not a while loop: set LR to the count, and set LTPSIZE for DLSTP */ - tmp = load_reg(s, a->rn); - store_reg(s, 14, tmp); - if (a->size != 4) { - /* DLSTP: set FPSCR.LTPSIZE */ - store_cpu_field(tcg_constant_i32(a->size), v7m.ltpsize); - s->base.is_jmp = DISAS_UPDATE_NOCHAIN; - } - return true; -} - -static bool trans_WLS(DisasContext *s, arg_WLS *a) -{ - /* M-profile low-overhead while-loop start */ - TCGv_i32 tmp; - DisasLabel nextlabel; - - if (!dc_isar_feature(aa32_lob, s)) { - return false; - } - if (a->rn == 13 || a->rn == 15) { - /* - * For WLSTP rn == 15 is a related encoding (LE); the - * other cases caught by this condition are all - * CONSTRAINED UNPREDICTABLE: we choose to UNDEF - */ - return false; - } - if (s->condexec_mask) { - /* - * WLS in an IT block is CONSTRAINED UNPREDICTABLE; - * we choose to UNDEF, because otherwise our use of - * gen_goto_tb(1) would clash with the use of TB exit 1 - * in the dc->condjmp condition-failed codepath in - * arm_tr_tb_stop() and we'd get an assertion. - */ - return false; - } - if (a->size != 4) { - /* WLSTP */ - if (!dc_isar_feature(aa32_mve, s)) { - return false; - } - /* - * We need to check that the FPU is enabled here, but mustn't - * call vfp_access_check() to do that because we don't want to - * do the lazy state preservation in the "loop count is zero" case. - * Do the check-and-raise-exception by hand. - */ - if (s->fp_excp_el) { - gen_exception_insn_el(s, 0, EXCP_NOCP, - syn_uncategorized(), s->fp_excp_el); - return true; - } - } - - 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) { - /* - * WLSTP: set FPSCR.LTPSIZE. This requires that we do the - * lazy state preservation, new FP context creation, etc, - * that vfp_access_check() does. We know that the actual - * access check will succeed (ie it won't generate code that - * throws an exception) because we did that check by hand earlier. - */ - bool ok = vfp_access_check(s); - assert(ok); - store_cpu_field(tcg_constant_i32(a->size), v7m.ltpsize); - /* - * LTPSIZE updated, but MVE_NO_PRED will always be the same thing (0) - * when we take this upcoming exit from this TB, so gen_jmp_tb() is OK. - */ - } - gen_jmp_tb(s, curr_insn_len(s), 1); - - set_disas_label(s, nextlabel); - gen_jmp(s, jmp_diff(s, a->imm)); - return true; -} - -static bool trans_LE(DisasContext *s, arg_LE *a) -{ - /* - * M-profile low-overhead loop end. The architecture permits an - * implementation to discard the LO_BRANCH_INFO cache at any time, - * and we take the IMPDEF option to never set it in the first place - * (equivalent to always discarding it immediately), because for QEMU - * a "real" implementation would be complicated and wouldn't execute - * any faster. - */ - TCGv_i32 tmp; - DisasLabel loopend; - bool fpu_active; - - if (!dc_isar_feature(aa32_lob, s)) { - return false; - } - if (a->f && a->tp) { - return false; - } - if (s->condexec_mask) { - /* - * LE in an IT block is CONSTRAINED UNPREDICTABLE; - * we choose to UNDEF, because otherwise our use of - * gen_goto_tb(1) would clash with the use of TB exit 1 - * in the dc->condjmp condition-failed codepath in - * arm_tr_tb_stop() and we'd get an assertion. - */ - return false; - } - if (a->tp) { - /* LETP */ - if (!dc_isar_feature(aa32_mve, s)) { - return false; - } - if (!vfp_access_check(s)) { - s->eci_handled = true; - return true; - } - } - - /* LE/LETP is OK with ECI set and leaves it untouched */ - s->eci_handled = true; - - /* - * With MVE, LTPSIZE might not be 4, and we must emit an INVSTATE - * UsageFault exception for the LE insn in that case. Note that we - * are not directly checking FPSCR.LTPSIZE but instead check the - * pseudocode LTPSIZE() function, which returns 4 if the FPU is - * not currently active (ie ActiveFPState() returns false). We - * can identify not-active purely from our TB state flags, as the - * FPU is active only if: - * the FPU is enabled - * AND lazy state preservation is not active - * AND we do not need a new fp context (this is the ASPEN/FPCA check) - * - * Usually we don't need to care about this distinction between - * LTPSIZE and FPSCR.LTPSIZE, because the code in vfp_access_check() - * will either take an exception or clear the conditions that make - * the FPU not active. But LE is an unusual case of a non-FP insn - * that looks at LTPSIZE. - */ - fpu_active = !s->fp_excp_el && !s->v7m_lspact && !s->v7m_new_fp_ctxt_needed; - - if (!a->tp && dc_isar_feature(aa32_mve, s) && fpu_active) { - /* Need to do a runtime check for LTPSIZE != 4 */ - DisasLabel skipexc = gen_disas_label(s); - tmp = load_cpu_field(v7m.ltpsize); - tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 4, skipexc.label); - tcg_temp_free_i32(tmp); - 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, jmp_diff(s, -a->imm)); - return true; - } - - /* - * Not loop-forever. If LR <= loop-decrement-value this is the last loop. - * For LE, we know at this point that LTPSIZE must be 4 and the - * loop decrement value is 1. For LETP we need to calculate the decrement - * value from LTPSIZE. - */ - loopend = gen_disas_label(s); - if (!a->tp) { - 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 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.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, jmp_diff(s, -a->imm)); - - 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, curr_insn_len(s), 1); - return true; -} - -static bool trans_LCTP(DisasContext *s, arg_LCTP *a) -{ - /* - * M-profile Loop Clear with Tail Predication. Since our implementation - * doesn't cache branch information, all we need to do is reset - * FPSCR.LTPSIZE to 4. - */ - - if (!dc_isar_feature(aa32_lob, s) || - !dc_isar_feature(aa32_mve, s)) { - return false; - } - - if (!vfp_access_check(s)) { - return true; - } - - store_cpu_field_constant(4, v7m.ltpsize); - return true; -} - -static bool trans_VCTP(DisasContext *s, arg_VCTP *a) -{ - /* - * M-profile Create Vector Tail Predicate. This insn is itself - * predicated and is subject to beatwise execution. - */ - TCGv_i32 rn_shifted, masklen; - - if (!dc_isar_feature(aa32_mve, s) || a->rn == 13 || a->rn == 15) { - return false; - } - - if (!mve_eci_check(s) || !vfp_access_check(s)) { - return true; - } - - /* - * We pre-calculate the mask length here to avoid having - * to have multiple helpers specialized for size. - * We pass the helper "rn <= (1 << (4 - size)) ? (rn << size) : 16". - */ - rn_shifted = tcg_temp_new_i32(); - masklen = load_reg(s, a->rn); - tcg_gen_shli_i32(rn_shifted, masklen, a->size); - 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); - /* This insn updates predication bits */ - s->base.is_jmp = DISAS_UPDATE_NOCHAIN; - mve_update_eci(s); - return true; -} - -static bool op_tbranch(DisasContext *s, arg_tbranch *a, bool half) -{ - TCGv_i32 addr, tmp; - - tmp = load_reg(s, a->rm); - if (half) { - tcg_gen_add_i32(tmp, tmp, tmp); - } - addr = load_reg(s, a->rn); - tcg_gen_add_i32(addr, addr, tmp); - - gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), half ? MO_UW : MO_UB); - - tcg_gen_add_i32(tmp, tmp, tmp); - gen_pc_plus_diff(s, addr, jmp_diff(s, 0)); - tcg_gen_add_i32(tmp, tmp, addr); - tcg_temp_free_i32(addr); - store_reg(s, 15, tmp); - return true; -} - -static bool trans_TBB(DisasContext *s, arg_tbranch *a) -{ - return op_tbranch(s, a, false); -} - -static bool trans_TBH(DisasContext *s, arg_tbranch *a) -{ - return op_tbranch(s, a, true); -} - -static bool trans_CBZ(DisasContext *s, arg_CBZ *a) -{ - TCGv_i32 tmp = load_reg(s, a->rn); - - arm_gen_condlabel(s); - tcg_gen_brcondi_i32(a->nz ? TCG_COND_EQ : TCG_COND_NE, - tmp, 0, s->condlabel.label); - tcg_temp_free_i32(tmp); - gen_jmp(s, jmp_diff(s, a->imm)); - return true; -} - -/* - * Supervisor call - both T32 & A32 come here so we need to check - * which mode we are in when checking for semihosting. - */ - -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(s->current_el == 0) && - (a->imm == semihost_imm)) { - gen_exception_internal_insn(s, EXCP_SEMIHOST); - } else { - gen_update_pc(s, curr_insn_len(s)); - s->svc_imm = a->imm; - s->base.is_jmp = DISAS_SWI; - } - return true; -} - -/* - * Unconditional system instructions - */ - -static bool trans_RFE(DisasContext *s, arg_RFE *a) -{ - static const int8_t pre_offset[4] = { - /* DA */ -4, /* IA */ 0, /* DB */ -8, /* IB */ 4 - }; - static const int8_t post_offset[4] = { - /* DA */ -8, /* IA */ 4, /* DB */ -4, /* IB */ 0 - }; - TCGv_i32 addr, t1, t2; - - if (!ENABLE_ARCH_6 || arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - if (IS_USER(s)) { - unallocated_encoding(s); - return true; - } - - addr = load_reg(s, a->rn); - tcg_gen_addi_i32(addr, addr, pre_offset[a->pu]); - - /* Load PC into tmp and CPSR into tmp2. */ - t1 = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, t1, addr, get_mem_index(s), MO_UL | MO_ALIGN); - tcg_gen_addi_i32(addr, addr, 4); - t2 = tcg_temp_new_i32(); - gen_aa32_ld_i32(s, t2, addr, get_mem_index(s), MO_UL | MO_ALIGN); - - if (a->w) { - /* 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; -} - -static bool trans_SRS(DisasContext *s, arg_SRS *a) -{ - if (!ENABLE_ARCH_6 || arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - gen_srs(s, a->mode, a->pu, a->w); - return true; -} - -static bool trans_CPS(DisasContext *s, arg_CPS *a) -{ - uint32_t mask, val; - - if (!ENABLE_ARCH_6 || arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - if (IS_USER(s)) { - /* Implemented as NOP in user mode. */ - return true; - } - /* TODO: There are quite a lot of UNPREDICTABLE argument combinations. */ - - mask = val = 0; - if (a->imod & 2) { - if (a->A) { - mask |= CPSR_A; - } - if (a->I) { - mask |= CPSR_I; - } - if (a->F) { - mask |= CPSR_F; - } - if (a->imod & 1) { - val |= mask; - } - } - if (a->M) { - mask |= CPSR_M; - val |= a->mode; - } - if (mask) { - gen_set_psr_im(s, mask, 0, val); - } - return true; -} - -static bool trans_CPS_v7m(DisasContext *s, arg_CPS_v7m *a) -{ - TCGv_i32 tmp, addr; - - if (!arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - if (IS_USER(s)) { - /* Implemented as NOP in user mode. */ - return true; - } - - tmp = tcg_constant_i32(a->im); - /* FAULTMASK */ - if (a->F) { - addr = tcg_constant_i32(19); - gen_helper_v7m_msr(cpu_env, addr, tmp); - } - /* PRIMASK */ - if (a->I) { - addr = tcg_constant_i32(16); - gen_helper_v7m_msr(cpu_env, addr, tmp); - } - gen_rebuild_hflags(s, false); - gen_lookup_tb(s); - return true; -} - -/* - * Clear-Exclusive, Barriers - */ - -static bool trans_CLREX(DisasContext *s, arg_CLREX *a) -{ - if (s->thumb - ? !ENABLE_ARCH_7 && !arm_dc_feature(s, ARM_FEATURE_M) - : !ENABLE_ARCH_6K) { - return false; - } - gen_clrex(s); - return true; -} - -static bool trans_DSB(DisasContext *s, arg_DSB *a) -{ - if (!ENABLE_ARCH_7 && !arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - return true; -} - -static bool trans_DMB(DisasContext *s, arg_DMB *a) -{ - return trans_DSB(s, NULL); -} - -static bool trans_ISB(DisasContext *s, arg_ISB *a) -{ - if (!ENABLE_ARCH_7 && !arm_dc_feature(s, ARM_FEATURE_M)) { - return false; - } - /* - * We need to break the TB after this insn to execute - * self-modifying code correctly and also to take - * any pending interrupts immediately. - */ - s->base.is_jmp = DISAS_TOO_MANY; - return true; -} - -static bool trans_SB(DisasContext *s, arg_SB *a) -{ - if (!dc_isar_feature(aa32_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); - s->base.is_jmp = DISAS_TOO_MANY; - return true; -} - -static bool trans_SETEND(DisasContext *s, arg_SETEND *a) -{ - if (!ENABLE_ARCH_6) { - return false; - } - if (a->E != (s->be_data == MO_BE)) { - gen_helper_setend(cpu_env); - s->base.is_jmp = DISAS_UPDATE_EXIT; - } - return true; -} - -/* - * Preload instructions - * All are nops, contingent on the appropriate arch level. - */ - -static bool trans_PLD(DisasContext *s, arg_PLD *a) -{ - return ENABLE_ARCH_5TE; -} - -static bool trans_PLDW(DisasContext *s, arg_PLD *a) -{ - return arm_dc_feature(s, ARM_FEATURE_V7MP); -} - -static bool trans_PLI(DisasContext *s, arg_PLD *a) -{ - return ENABLE_ARCH_7; -} - -/* - * If-then - */ - -static bool trans_IT(DisasContext *s, arg_IT *a) -{ - int cond_mask = a->cond_mask; - - /* - * No actual code generated for this insn, just setup state. - * - * Combinations of firstcond and mask which set up an 0b1111 - * condition are UNPREDICTABLE; we take the CONSTRAINED - * UNPREDICTABLE choice to treat 0b1111 the same as 0b1110, - * i.e. both meaning "execute always". - */ - s->condexec_cond = (cond_mask >> 4) & 0xe; - s->condexec_mask = cond_mask & 0x1f; - return true; -} - -/* v8.1M CSEL/CSINC/CSNEG/CSINV */ -static bool trans_CSEL(DisasContext *s, arg_CSEL *a) -{ - TCGv_i32 rn, rm, zero; - DisasCompare c; - - if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) { - return false; - } - - if (a->rm == 13) { - /* SEE "Related encodings" (MVE shifts) */ - return false; - } - - if (a->rd == 13 || a->rd == 15 || a->rn == 13 || a->fcond >= 14) { - /* CONSTRAINED UNPREDICTABLE: we choose to UNDEF */ - return false; - } - - /* In this insn input reg fields of 0b1111 mean "zero", not "PC" */ - zero = tcg_constant_i32(0); - if (a->rn == 15) { - rn = zero; - } else { - rn = load_reg(s, a->rn); - } - if (a->rm == 15) { - rm = zero; - } else { - rm = load_reg(s, a->rm); - } - - switch (a->op) { - case 0: /* CSEL */ - break; - case 1: /* CSINC */ - tcg_gen_addi_i32(rm, rm, 1); - break; - case 2: /* CSINV */ - tcg_gen_not_i32(rm, rm); - break; - case 3: /* CSNEG */ - tcg_gen_neg_i32(rm, rm); - break; - default: - g_assert_not_reached(); - } - - arm_test_cc(&c, a->fcond); - tcg_gen_movcond_i32(c.cond, rn, c.value, zero, rn, rm); - arm_free_cc(&c); - - store_reg(s, a->rd, rn); - tcg_temp_free_i32(rm); - - return true; -} - -/* - * Legacy decoder. - */ - -static void disas_arm_insn(DisasContext *s, unsigned int insn) -{ - unsigned int cond = insn >> 28; - - /* M variants do not implement ARM mode; this must raise the INVSTATE - * UsageFault exception. - */ - if (arm_dc_feature(s, ARM_FEATURE_M)) { - gen_exception_insn(s, 0, EXCP_INVSTATE, syn_uncategorized()); - return; - } - - if (s->pstate_il) { - /* - * Illegal execution state. This has priority over BTI - * exceptions, but comes after instruction abort exceptions. - */ - gen_exception_insn(s, 0, EXCP_UDEF, syn_illegalstate()); - return; - } - - if (cond == 0xf) { - /* In ARMv3 and v4 the NV condition is UNPREDICTABLE; we - * choose to UNDEF. In ARMv5 and above the space is used - * for miscellaneous unconditional instructions. - */ - if (!arm_dc_feature(s, ARM_FEATURE_V5)) { - unallocated_encoding(s); - return; - } - - /* Unconditional instructions. */ - /* TODO: Perhaps merge these into one decodetree output file. */ - if (disas_a32_uncond(s, insn) || - disas_vfp_uncond(s, insn) || - disas_neon_dp(s, insn) || - disas_neon_ls(s, insn) || - disas_neon_shared(s, insn)) { - return; - } - /* fall back to legacy decoder */ - - if ((insn & 0x0e000f00) == 0x0c000100) { - if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { - /* iWMMXt register transfer. */ - if (extract32(s->c15_cpar, 1, 1)) { - if (!disas_iwmmxt_insn(s, insn)) { - return; - } - } - } - } - goto illegal_op; - } - if (cond != 0xe) { - /* if not always execute, we generate a conditional jump to - next instruction */ - arm_skip_unless(s, cond); - } - - /* TODO: Perhaps merge these into one decodetree output file. */ - if (disas_a32(s, insn) || - disas_vfp(s, insn)) { - return; - } - /* fall back to legacy decoder */ - /* TODO: convert xscale/iwmmxt decoder to decodetree ?? */ - if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { - if (((insn & 0x0c000e00) == 0x0c000000) - && ((insn & 0x03000000) != 0x03000000)) { - /* Coprocessor insn, coprocessor 0 or 1 */ - disas_xscale_insn(s, insn); - return; - } - } - -illegal_op: - unallocated_encoding(s); -} - -static bool thumb_insn_is_16bit(DisasContext *s, uint32_t pc, uint32_t insn) -{ - /* - * Return true if this is a 16 bit instruction. We must be precise - * about this (matching the decode). - */ - if ((insn >> 11) < 0x1d) { - /* Definitely a 16-bit instruction */ - return true; - } - - /* Top five bits 0b11101 / 0b11110 / 0b11111 : this is the - * first half of a 32-bit Thumb insn. Thumb-1 cores might - * end up actually treating this as two 16-bit insns, though, - * if it's half of a bl/blx pair that might span a page boundary. - */ - if (arm_dc_feature(s, ARM_FEATURE_THUMB2) || - arm_dc_feature(s, ARM_FEATURE_M)) { - /* Thumb2 cores (including all M profile ones) always treat - * 32-bit insns as 32-bit. - */ - return false; - } - - if ((insn >> 11) == 0x1e && pc - s->page_start < TARGET_PAGE_SIZE - 3) { - /* 0b1111_0xxx_xxxx_xxxx : BL/BLX prefix, and the suffix - * is not on the next page; we merge this into a 32-bit - * insn. - */ - return false; - } - /* 0b1110_1xxx_xxxx_xxxx : BLX suffix (or UNDEF); - * 0b1111_1xxx_xxxx_xxxx : BL suffix; - * 0b1111_0xxx_xxxx_xxxx : BL/BLX prefix on the end of a page - * -- handle as single 16 bit insn - */ - return true; -} - -/* Translate a 32-bit thumb instruction. */ -static void disas_thumb2_insn(DisasContext *s, uint32_t insn) -{ - /* - * ARMv6-M supports a limited subset of Thumb2 instructions. - * Other Thumb1 architectures allow only 32-bit - * combined BL/BLX prefix and suffix. - */ - if (arm_dc_feature(s, ARM_FEATURE_M) && - !arm_dc_feature(s, ARM_FEATURE_V7)) { - int i; - bool found = false; - static const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, - 0xf3b08040 /* dsb */, - 0xf3b08050 /* dmb */, - 0xf3b08060 /* isb */, - 0xf3e08000 /* mrs */, - 0xf000d000 /* bl */}; - static const uint32_t armv6m_mask[] = {0xffe0d000, - 0xfff0d0f0, - 0xfff0d0f0, - 0xfff0d0f0, - 0xffe0d000, - 0xf800d000}; - - for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) { - if ((insn & armv6m_mask[i]) == armv6m_insn[i]) { - found = true; - break; - } - } - if (!found) { - goto illegal_op; - } - } else if ((insn & 0xf800e800) != 0xf000e800) { - if (!arm_dc_feature(s, ARM_FEATURE_THUMB2)) { - unallocated_encoding(s); - return; - } - } - - if (arm_dc_feature(s, ARM_FEATURE_M)) { - /* - * NOCP takes precedence over any UNDEF for (almost) the - * entire wide range of coprocessor-space encodings, so check - * for it first before proceeding to actually decode eg VFP - * insns. This decode also handles the few insns which are - * in copro space but do not have NOCP checks (eg VLLDM, VLSTM). - */ - if (disas_m_nocp(s, insn)) { - return; - } - } - - if ((insn & 0xef000000) == 0xef000000) { - /* - * T32 encodings 0b111p_1111_qqqq_qqqq_qqqq_qqqq_qqqq_qqqq - * transform into - * A32 encodings 0b1111_001p_qqqq_qqqq_qqqq_qqqq_qqqq_qqqq - */ - uint32_t a32_insn = (insn & 0xe2ffffff) | - ((insn & (1 << 28)) >> 4) | (1 << 28); - - if (disas_neon_dp(s, a32_insn)) { - return; - } - } - - if ((insn & 0xff100000) == 0xf9000000) { - /* - * T32 encodings 0b1111_1001_ppp0_qqqq_qqqq_qqqq_qqqq_qqqq - * transform into - * A32 encodings 0b1111_0100_ppp0_qqqq_qqqq_qqqq_qqqq_qqqq - */ - uint32_t a32_insn = (insn & 0x00ffffff) | 0xf4000000; - - if (disas_neon_ls(s, a32_insn)) { - return; - } - } - - /* - * TODO: Perhaps merge these into one decodetree output file. - * Note disas_vfp is written for a32 with cond field in the - * top nibble. The t32 encoding requires 0xe in the top nibble. - */ - if (disas_t32(s, insn) || - disas_vfp_uncond(s, insn) || - disas_neon_shared(s, insn) || - disas_mve(s, insn) || - ((insn >> 28) == 0xe && disas_vfp(s, insn))) { - return; - } - -illegal_op: - unallocated_encoding(s); -} - -static void disas_thumb_insn(DisasContext *s, uint32_t insn) -{ - if (!disas_t16(s, insn)) { - unallocated_encoding(s); - } -} - -static bool insn_crosses_page(CPUARMState *env, DisasContext *s) -{ - /* Return true if the insn at dc->base.pc_next might cross a page boundary. - * (False positives are OK, false negatives are not.) - * We know this is a Thumb insn, and our caller ensures we are - * only called if dc->base.pc_next is less than 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 = arm_lduw_code(env, &s->base, s->base.pc_next, s->sctlr_b); - - return !thumb_insn_is_16bit(s, s->base.pc_next, insn); -} - -static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cs->env_ptr; - 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; - 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); - /* - * the CONDEXEC TB flags are CPSR bits [15:10][26:25]. On A-profile this - * is always the IT bits. On M-profile, some of the reserved encodings - * of IT are used instead to indicate either ICI or ECI, which - * indicate partial progress of a restartable insn that was interrupted - * partway through by an exception: - * * if CONDEXEC[3:0] != 0b0000 : CONDEXEC is IT bits - * * if CONDEXEC[3:0] == 0b0000 : CONDEXEC is ICI or ECI bits - * In all cases CONDEXEC == 0 means "not in IT block or restartable - * insn, behave normally". - */ - dc->eci = dc->condexec_mask = dc->condexec_cond = 0; - dc->eci_handled = false; - if (condexec & 0xf) { - dc->condexec_mask = (condexec & 0xf) << 1; - dc->condexec_cond = condexec >> 4; - } else { - if (arm_feature(env, ARM_FEATURE_M)) { - dc->eci = condexec >> 4; - } - } - - core_mmu_idx = EX_TBFLAG_ANY(tb_flags, MMUIDX); - dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); - dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); -#if !defined(CONFIG_USER_ONLY) - dc->user = (dc->current_el == 0); -#endif - dc->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); - - 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 = 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 = - EX_TBFLAG_M32(tb_flags, NEW_FP_CTXT_NEEDED); - dc->v7m_lspact = EX_TBFLAG_M32(tb_flags, LSPACT); - dc->mve_no_pred = EX_TBFLAG_M32(tb_flags, MVE_NO_PRED); - } else { - 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); - dc->vfp_enabled = EX_TBFLAG_A32(tb_flags, VFPEN); - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - dc->c15_cpar = EX_TBFLAG_A32(tb_flags, XSCALE_CPAR); - } else { - 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->cp_regs = cpu->cp_regs; - dc->features = env->features; - - /* Single step state. The code-generation logic here is: - * SS_ACTIVE == 0: - * generate code with no special handling for single-stepping (except - * that anything that can make us go to SS_ACTIVE == 1 must end the TB; - * this happens anyway because those changes are all system register or - * PSTATE writes). - * SS_ACTIVE == 1, PSTATE.SS == 1: (active-not-pending) - * emit code for one insn - * emit code to clear PSTATE.SS - * emit code to generate software step exception for completed step - * end TB (as usual for having generated an exception) - * SS_ACTIVE == 1, PSTATE.SS == 0: (active-pending) - * emit code to generate a software step exception - * end the TB - */ - 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->page_start = dc->base.pc_first & TARGET_PAGE_MASK; - - /* If architectural single step active, limit to 1. */ - if (dc->ss_active) { - dc->base.max_insns = 1; - } - - /* ARM is a fixed-length ISA. Bound the number of insns to execute - to those left on the page. */ - if (!dc->thumb) { - int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - dc->base.max_insns = MIN(dc->base.max_insns, bound); - } - - cpu_V0 = tcg_temp_new_i64(); - cpu_V1 = tcg_temp_new_i64(); - cpu_M0 = tcg_temp_new_i64(); -} - -static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - /* A note on handling of the condexec (IT) bits: - * - * We want to avoid the overhead of having to write the updated condexec - * bits back to the CPUARMState for every instruction in an IT block. So: - * (1) if the condexec bits are not already zero then we write - * zero back into the CPUARMState now. This avoids complications trying - * to do it at the end of the block. (For example if we don't do this - * it's hard to identify whether we can safely skip writing condexec - * at the end of the TB, which we definitely want to do for the case - * where a TB doesn't do anything with the IT state at all.) - * (2) if we are going to leave the TB then we call gen_set_condexec() - * which will write the correct value into CPUARMState if zero is wrong. - * This is done both for leaving the TB at the end, and for leaving - * it because of an exception we know will happen, which is done in - * gen_exception_insn(). The latter is necessary because we need to - * leave the TB with the PC/IT state just prior to execution of the - * instruction which caused the exception. - * (3) if we leave the TB unexpectedly (eg a data abort on a load) - * then the CPUARMState will be wrong and we need to reset it. - * This is handled in the same way as restoration of the - * PC in these situations; we save the value of the condexec bits - * for each PC via tcg_gen_insn_start(), and restore_state_to_opc() - * then uses this to restore them after an exception. - * - * Note that there are no instructions which can read the condexec - * bits, and none which can write non-static values to them, so - * we don't need to care about whether CPUARMState is correct in the - * middle of a TB. - */ - - /* Reset the conditional execution bits immediately. This avoids - complications trying to do it at the end of the block. */ - if (dc->condexec_mask || dc->condexec_cond) { - store_cpu_field_constant(0, condexec_bits); - } -} - -static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - /* - * The ECI/ICI bits share PSR bits with the IT bits, so we - * need to reconstitute the bits from the split-out DisasContext - * fields here. - */ - uint32_t condexec_bits; - target_ulong pc_arg = dc->base.pc_next; - - if (TARGET_TB_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(pc_arg, condexec_bits, 0); - dc->insn_start = tcg_last_op(); -} - -static bool arm_check_kernelpage(DisasContext *dc) -{ -#ifdef CONFIG_USER_ONLY - /* Intercept jump to the magic kernel page. */ - if (dc->base.pc_next >= 0xffff0000) { - /* We always get here via a jump, so know we are not in a - conditional execution block. */ - gen_exception_internal(EXCP_KERNEL_TRAP); - dc->base.is_jmp = DISAS_NORETURN; - return true; - } -#endif - return false; -} - -static bool arm_check_ss_active(DisasContext *dc) -{ - if (dc->ss_active && !dc->pstate_ss) { - /* Singlestep state is Active-pending. - * If we're in this state at the start of a TB then either - * a) we just took an exception to an EL which is being debugged - * and this is the first insn in the exception handler - * b) debug exceptions were masked and we just unmasked them - * without changing EL (eg by clearing PSTATE.D) - * In either case we're going to take a swstep exception in the - * "did not step an insn" case, and so the syndrome ISV and EX - * bits should be zero. - */ - assert(dc->base.num_insns == 1); - gen_swstep_exception(dc, 0, 0); - dc->base.is_jmp = DISAS_NORETURN; - return true; - } - - return false; -} - -static void arm_post_translate_insn(DisasContext *dc) -{ - 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; - uint32_t pc = dc->base.pc_next; - unsigned int insn; - - /* Singlestep exceptions have the highest priority. */ - if (arm_check_ss_active(dc)) { - dc->base.pc_next = pc + 4; - return; - } - - if (pc & 3) { - /* - * PC alignment fault. This has priority over the instruction abort - * that we would receive from a translation fault via arm_ldl_code - * (or the execution of the kernelpage entrypoint). This should only - * 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)); - dc->base.is_jmp = DISAS_NORETURN; - dc->base.pc_next = QEMU_ALIGN_UP(pc, 4); - return; - } - - if (arm_check_kernelpage(dc)) { - dc->base.pc_next = pc + 4; - return; - } - - dc->pc_curr = pc; - insn = arm_ldl_code(env, &dc->base, pc, dc->sctlr_b); - dc->insn = insn; - dc->base.pc_next = pc + 4; - disas_arm_insn(dc, insn); - - arm_post_translate_insn(dc); - - /* ARM is a fixed-length ISA. We performed the cross-page check - in init_disas_context by adjusting max_insns. */ -} - -static bool thumb_insn_is_unconditional(DisasContext *s, uint32_t insn) -{ - /* Return true if this Thumb insn is always unconditional, - * even inside an IT block. This is true of only a very few - * instructions: BKPT, HLT, and SG. - * - * A larger class of instructions are UNPREDICTABLE if used - * inside an IT block; we do not need to detect those here, because - * what we do by default (perform the cc check and update the IT - * bits state machine) is a permitted CONSTRAINED UNPREDICTABLE - * choice for those situations. - * - * insn is either a 16-bit or a 32-bit instruction; the two are - * distinguishable because for the 16-bit case the top 16 bits - * are zeroes, and that isn't a valid 32-bit encoding. - */ - if ((insn & 0xffffff00) == 0xbe00) { - /* BKPT */ - return true; - } - - if ((insn & 0xffffffc0) == 0xba80 && arm_dc_feature(s, ARM_FEATURE_V8) && - !arm_dc_feature(s, ARM_FEATURE_M)) { - /* HLT: v8A only. This is unconditional even when it is going to - * UNDEF; see the v8A ARM ARM DDI0487B.a H3.3. - * For v7 cores this was a plain old undefined encoding and so - * honours its cc check. (We might be using the encoding as - * a semihosting trap, but we don't change the cc check behaviour - * on that account, because a debugger connected to a real v7A - * core and emulating semihosting traps by catching the UNDEF - * exception would also only see cases where the cc check passed. - * No guest code should be trying to do a HLT semihosting trap - * in an IT block anyway. - */ - return true; - } - - if (insn == 0xe97fe97f && arm_dc_feature(s, ARM_FEATURE_V8) && - arm_dc_feature(s, ARM_FEATURE_M)) { - /* SG: v8M only */ - return true; - } - - return false; -} - -static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUARMState *env = cpu->env_ptr; - 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); - - if (arm_check_ss_active(dc) || arm_check_kernelpage(dc)) { - dc->base.pc_next = pc + 2; - return; - } - - dc->pc_curr = pc; - insn = arm_lduw_code(env, &dc->base, pc, dc->sctlr_b); - is_16bit = thumb_insn_is_16bit(dc, dc->base.pc_next, insn); - pc += 2; - if (!is_16bit) { - uint32_t insn2 = arm_lduw_code(env, &dc->base, pc, dc->sctlr_b); - insn = insn << 16 | insn2; - pc += 2; - } - dc->base.pc_next = pc; - dc->insn = insn; - - if (dc->pstate_il) { - /* - * Illegal execution state. This has priority over BTI - * exceptions, but comes after instruction abort exceptions. - */ - gen_exception_insn(dc, 0, EXCP_UDEF, syn_illegalstate()); - return; - } - - if (dc->eci) { - /* - * For M-profile continuable instructions, ECI/ICI handling - * falls into these cases: - * - interrupt-continuable instructions - * These are the various load/store multiple insns (both - * integer and fp). The ICI bits indicate the register - * where the load/store can resume. We make the IMPDEF - * choice to always do "instruction restart", ie ignore - * the ICI value and always execute the ldm/stm from the - * start. So all we need to do is zero PSR.ICI if the - * insn executes. - * - MVE instructions subject to beat-wise execution - * Here the ECI bits indicate which beats have already been - * executed, and we must honour this. Each insn of this - * type will handle it correctly. We will update PSR.ECI - * in the helper function for the insn (some ECI values - * mean that the following insn also has been partially - * executed). - * - Special cases which don't advance ECI - * The insns LE, LETP and BKPT leave the ECI/ICI state - * bits untouched. - * - all other insns (the common case) - * Non-zero ECI/ICI means an INVSTATE UsageFault. - * We place a rewind-marker here. Insns in the previous - * three categories will set a flag in the DisasContext. - * If the flag isn't set after we call disas_thumb_insn() - * or disas_thumb2_insn() then we know we have a "some other - * insn" case. We will rewind to the marker (ie throwing away - * all the generated code) and instead emit "take exception". - */ - insn_eci_rewind = tcg_last_op(); - insn_eci_pc_save = dc->pc_save; - } - - if (dc->condexec_mask && !thumb_insn_is_unconditional(dc, insn)) { - uint32_t cond = dc->condexec_cond; - - /* - * Conditionally skip the insn. Note that both 0xe and 0xf mean - * "always"; 0xf is not "never". - */ - if (cond < 0x0e) { - arm_skip_unless(dc, cond); - } - } - - if (is_16bit) { - disas_thumb_insn(dc, insn); - } else { - disas_thumb2_insn(dc, insn); - } - - /* Advance the Thumb condexec condition. */ - if (dc->condexec_mask) { - dc->condexec_cond = ((dc->condexec_cond & 0xe) | - ((dc->condexec_mask >> 4) & 1)); - dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f; - if (dc->condexec_mask == 0) { - dc->condexec_cond = 0; - } - } - - if (dc->eci && !dc->eci_handled) { - /* - * Insn wasn't valid for ECI/ICI at all: undo what we - * just generated and instead emit an exception - */ - tcg_remove_ops_after(insn_eci_rewind); - dc->pc_save = insn_eci_pc_save; - dc->condjmp = 0; - gen_exception_insn(dc, 0, EXCP_INVSTATE, syn_uncategorized()); - } - - arm_post_translate_insn(dc); - - /* Thumb is a variable-length ISA. Stop translation when the next insn - * will touch a new page. This ensures that prefetch aborts occur at - * the right place. - * - * We want to stop the TB if the next insn starts in a new page, - * or if it spans between this page and the next. This means that - * if we're looking at the last halfword in the page we need to - * see if it's a 16-bit Thumb insn (which will fit in this TB) - * or a 32-bit Thumb insn (which won't). - * This is to avoid generating a silly TB with a single 16-bit insn - * in it at the end of this page (which would execute correctly - * but isn't very efficient). - */ - if (dc->base.is_jmp == DISAS_NEXT - && (dc->base.pc_next - dc->page_start >= TARGET_PAGE_SIZE - || (dc->base.pc_next - dc->page_start >= TARGET_PAGE_SIZE - 3 - && insn_crosses_page(env, dc)))) { - dc->base.is_jmp = DISAS_TOO_MANY; - } -} - -static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - /* At this stage dc->condjmp will only be set when the skipped - instruction was a conditional branch or trap, and the PC has - already been written. */ - gen_set_condexec(dc); - if (dc->base.is_jmp == DISAS_BX_EXCRET) { - /* Exception return branches need some special case code at the - * end of the TB, which is complex enough that it has to - * handle the single-step vs not and the condition-failed - * insn codepath itself. - */ - gen_bx_excret_final_code(dc); - } else if (unlikely(dc->ss_active)) { - /* Unconditional and "condition passed" instruction codepath. */ - switch (dc->base.is_jmp) { - case DISAS_SWI: - gen_ss_advance(dc); - gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); - break; - case DISAS_HVC: - gen_ss_advance(dc); - gen_exception_el(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); - break; - case DISAS_SMC: - gen_ss_advance(dc); - 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_update_pc(dc, curr_insn_len(dc)); - /* fall through */ - default: - /* FIXME: Single stepping a WFI insn will not halt the CPU. */ - gen_singlestep_exception(dc); - break; - case DISAS_NORETURN: - break; - } - } else { - /* While branches must always occur at the end of an IT block, - there are a few other things that can cause us to terminate - the TB in the middle of an IT block: - - Exception generating instructions (bkpt, swi, undefined). - - Page boundaries. - - Hardware watchpoints. - Hardware breakpoints have already been handled and skip this code. - */ - switch (dc->base.is_jmp) { - case DISAS_NEXT: - case DISAS_TOO_MANY: - gen_goto_tb(dc, 1, curr_insn_len(dc)); - break; - case DISAS_UPDATE_NOCHAIN: - gen_update_pc(dc, curr_insn_len(dc)); - /* fall through */ - case DISAS_JUMP: - gen_goto_ptr(); - break; - case DISAS_UPDATE_EXIT: - gen_update_pc(dc, curr_insn_len(dc)); - /* fall through */ - default: - /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(NULL, 0); - break; - case DISAS_NORETURN: - /* nothing more to generate */ - break; - case DISAS_WFI: - gen_helper_wfi(cpu_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. - */ - tcg_gen_exit_tb(NULL, 0); - break; - case DISAS_WFE: - gen_helper_wfe(cpu_env); - break; - case DISAS_YIELD: - gen_helper_yield(cpu_env); - break; - case DISAS_SWI: - gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb)); - break; - case DISAS_HVC: - gen_exception_el(EXCP_HVC, syn_aa32_hvc(dc->svc_imm), 2); - break; - case DISAS_SMC: - gen_exception_el(EXCP_SMC, syn_aa32_smc(), 3); - break; - } - } - - if (dc->condjmp) { - /* "Condition failed" instruction codepath for the branch/trap insn */ - set_disas_label(dc, dc->condlabel); - gen_set_condexec(dc); - if (unlikely(dc->ss_active)) { - gen_update_pc(dc, curr_insn_len(dc)); - gen_singlestep_exception(dc); - } else { - gen_goto_tb(dc, 1, curr_insn_len(dc)); - } - } -} - -static void arm_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); -} - -static const TranslatorOps arm_translator_ops = { - .init_disas_context = arm_tr_init_disas_context, - .tb_start = arm_tr_tb_start, - .insn_start = arm_tr_insn_start, - .translate_insn = arm_tr_translate_insn, - .tb_stop = arm_tr_tb_stop, - .disas_log = arm_tr_disas_log, -}; - -static const TranslatorOps thumb_translator_ops = { - .init_disas_context = arm_tr_init_disas_context, - .tb_start = arm_tr_tb_start, - .insn_start = arm_tr_insn_start, - .translate_insn = thumb_tr_translate_insn, - .tb_stop = arm_tr_tb_stop, - .disas_log = arm_tr_disas_log, -}; - -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) -{ - DisasContext dc = { }; - const TranslatorOps *ops = &arm_translator_ops; - CPUARMTBFlags tb_flags = arm_tbflags_from_tb(tb); - - if (EX_TBFLAG_AM32(tb_flags, THUMB)) { - ops = &thumb_translator_ops; - } -#ifdef TARGET_AARCH64 - if (EX_TBFLAG_ANY(tb_flags, AARCH64_STATE)) { - ops = &aarch64_translator_ops; - } -#endif - - translator_loop(cpu, tb, max_insns, pc, host_pc, ops, &dc.base); -} diff --git a/target/arm/translate.h b/target/arm/translate.h deleted file mode 100644 index 3cdc7dbc2f..0000000000 --- a/target/arm/translate.h +++ /dev/null @@ -1,631 +0,0 @@ -#ifndef TARGET_ARM_TRANSLATE_H -#define TARGET_ARM_TRANSLATE_H - -#include "exec/translator.h" -#include "internals.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 TARGET_TB_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. */ - DisasLabel condlabel; - /* Thumb-2 conditional execution bits. */ - int condexec_mask; - int condexec_cond; - /* M-profile ECI/ICI exception-continuable instruction state */ - int eci; - /* - * trans_ functions for insns which are continuable should set this true - * after decode (ie after any UNDEF checks) - */ - bool eci_handled; - int sctlr_b; - MemOp be_data; -#if !defined(CONFIG_USER_ONLY) - int user; -#endif - ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */ - uint8_t tbii; /* TBI1|TBI0 for insns */ - uint8_t tbid; /* TBI1|TBI0 for data */ - uint8_t tcma; /* TCMA1|TCMA0 for MTE */ - 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 */ - int svl; /* current streaming vector length in bytes */ - bool vfp_enabled; /* FP enabled via FPSCR.EN */ - int vec_len; - int vec_stride; - bool v7m_handler_mode; - bool v8m_secure; /* true if v8M and we're in Secure mode */ - bool v8m_stackcheck; /* true if we need to perform v8M stack limit checks */ - bool v8m_fpccr_s_wrong; /* true if v8M FPCCR.S != v8m_secure */ - bool v7m_new_fp_ctxt_needed; /* ASPEN set but no active FP context */ - bool v7m_lspact; /* FPCCR.LSPACT set */ - /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI - * so that top level loop can generate correct syndrome information. - */ - uint32_t svc_imm; - int current_el; - GHashTable *cp_regs; - uint64_t features; /* CPU features bits */ - bool aarch64; - bool thumb; - /* Because unallocated encodings generate different exception syndrome - * information from traps due to FP being disabled, we can't do a single - * "is fp access disabled" check at a high level in the decode tree. - * To help in catching bugs where the access check was forgotten in some - * code path, we set this flag when the access check is done, and assert - * that it is set at the point where we actually touch the FP regs. - */ - bool fp_access_checked; - bool sve_access_checked; - /* ARMv8 single-step state (this is distinct from the QEMU gdbstub - * single-step support). - */ - bool ss_active; - bool pstate_ss; - /* True if the insn just emitted was a load-exclusive instruction - * (necessary for syndrome information for single step exceptions), - * ie A64 LDX*, LDAX*, A32/T32 LDREX*, LDAEX*. - */ - bool is_ldex; - /* True if AccType_UNPRIV should be used for LDTR et al */ - bool unpriv; - /* True if v8.3-PAuth is active. */ - bool pauth_active; - /* True if v8.5-MTE access to tags is enabled. */ - bool ata; - /* 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. */ - bool bt; - /* True if any CP15 access is trapped by HSTR_EL2 */ - bool hstr_active; - /* True if memory operations require alignment */ - 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; - /* - * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. - * < 0, set by the current instruction. - */ - int8_t btype; - /* A copy of cpu->dcz_blocksize. */ - uint8_t dcz_blocksize; - /* True if this page is guarded. */ - bool guarded_page; - /* 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]; -} DisasContext; - -typedef struct DisasCompare { - TCGCond cond; - TCGv_i32 value; - bool value_global; -} DisasCompare; - -/* Share the TCG temporaries common between 32 and 64 bit modes. */ -extern TCGv_i32 cpu_NF, cpu_ZF, cpu_CF, cpu_VF; -extern TCGv_i64 cpu_exclusive_addr; -extern TCGv_i64 cpu_exclusive_val; - -/* - * Constant expanders for the decoders. - */ - -static inline int negate(DisasContext *s, int x) -{ - return -x; -} - -static inline int plus_1(DisasContext *s, int x) -{ - return x + 1; -} - -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; -} - -static inline int times_4(DisasContext *s, int x) -{ - return x * 4; -} - -static inline int times_2_plus_1(DisasContext *s, int x) -{ - return x * 2 + 1; -} - -static inline int rsub_64(DisasContext *s, int x) -{ - return 64 - x; -} - -static inline int rsub_32(DisasContext *s, int x) -{ - return 32 - x; -} - -static inline int rsub_16(DisasContext *s, int x) -{ - return 16 - x; -} - -static inline int rsub_8(DisasContext *s, int x) -{ - return 8 - x; -} - -static inline int neon_3same_fp_size(DisasContext *s, int x) -{ - /* Convert 0==fp32, 1==fp16 into a MO_* value */ - return MO_32 - x; -} - -static inline int arm_dc_feature(DisasContext *dc, int feature) -{ - return (dc->features & (1ULL << feature)) != 0; -} - -static inline int get_mem_index(DisasContext *s) -{ - return arm_to_core_mmu_idx(s->mmu_idx); -} - -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 - * out unneeded bits to help the sleb128 encoder do a better job. - */ - 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; -} - -static inline int curr_insn_len(DisasContext *s) -{ - return s->base.pc_next - s->pc_curr; -} - -/* is_jmp field values */ -#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ -/* CPU state was modified dynamically; exit to main loop for interrupts. */ -#define DISAS_UPDATE_EXIT DISAS_TARGET_1 -/* These instructions trap after executing, so the A32/T32 decoder must - * defer them until after the conditional execution state has been updated. - * WFI also needs special handling when single-stepping. - */ -#define DISAS_WFI DISAS_TARGET_2 -#define DISAS_SWI DISAS_TARGET_3 -/* WFE */ -#define DISAS_WFE DISAS_TARGET_4 -#define DISAS_HVC DISAS_TARGET_5 -#define DISAS_SMC DISAS_TARGET_6 -#define DISAS_YIELD DISAS_TARGET_7 -/* M profile branch which might be an exception return (and so needs - * custom end-of-TB code) - */ -#define DISAS_BX_EXCRET DISAS_TARGET_8 -/* - * 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_update_pc or runtime helper) has done so before we reach - * return from cpu_tb_exec. - */ -#define DISAS_EXIT DISAS_TARGET_9 -/* CPU state was modified dynamically; no need to exit, but do not chain. */ -#define DISAS_UPDATE_NOCHAIN DISAS_TARGET_10 - -#ifdef TARGET_AARCH64 -void a64_translate_init(void); -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_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_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, - offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPSCR])); - tcg_gen_extract_i32(ret, ret, 26, 1); - - return ret; -} - -/* Set bits within PSTATE. */ -static inline void set_pstate_bits(uint32_t bits) -{ - TCGv_i32 p = tcg_temp_new_i32(); - - tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - - tcg_gen_ld_i32(p, cpu_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); -} - -/* Clear bits within PSTATE. */ -static inline void clear_pstate_bits(uint32_t bits) -{ - TCGv_i32 p = tcg_temp_new_i32(); - - tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); - - tcg_gen_ld_i32(p, cpu_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); -} - -/* If the singlestep state is Active-not-pending, advance to Active-pending. */ -static inline void gen_ss_advance(DisasContext *s) -{ - if (s->ss_active) { - s->pstate_ss = 0; - clear_pstate_bits(PSTATE_SS); - } -} - -/* Generate an architectural singlestep exception */ -static inline void gen_swstep_exception(DisasContext *s, int isv, int ex) -{ - /* Fill in the same_el field of the syndrome in the helper. */ - uint32_t syn = syn_swstep(false, isv, ex); - gen_helper_exception_swstep(cpu_env, tcg_constant_i32(syn)); -} - -/* - * Given a VFP floating point constant encoded into an 8 bit immediate in an - * instruction, expand it to the actual constant value of the specified - * size, as per the VFPExpandImm() pseudocode in the Arm ARM. - */ -uint64_t vfp_expand_imm(int size, uint8_t imm8); - -/* Vector operations shared between ARM and AArch64. */ -void gen_gvec_ceq0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_clt0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_cgt0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_cle0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_cge0(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_mla(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_mls(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_cmtst(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_sshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_ushl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); - -void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void gen_ushl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); -void gen_sshl_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b); -void gen_ushl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); -void gen_sshl_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); - -void gen_gvec_uqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_sqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_uqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_sqsub_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_ssra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_usra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_srshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_urshr(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_srsra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_ursra(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_sri(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_sli(unsigned vece, uint32_t rd_ofs, uint32_t rm_ofs, - int64_t shift, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_sqrdmlah_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_sqrdmlsh_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_sabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_uabd(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); - -void gen_gvec_saba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_gvec_uaba(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, - uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); - -/* - * Forward to the isar_feature_* tests given a DisasContext pointer. - */ -#define dc_isar_feature(name, ctx) \ - ({ DisasContext *ctx_ = (ctx); isar_feature_##name(ctx_->isar); }) - -/* Note that the gvec expanders operate on offsets + sizes. */ -typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); -typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, - uint32_t, uint32_t); -typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, - uint32_t, uint32_t, uint32_t); -typedef void GVecGen4Fn(unsigned, uint32_t, uint32_t, uint32_t, - uint32_t, uint32_t, uint32_t); - -/* Function prototype for gen_ functions for calling Neon helpers */ -typedef void NeonGenOneOpFn(TCGv_i32, TCGv_i32); -typedef void NeonGenOneOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32); -typedef void NeonGenTwoOpFn(TCGv_i32, TCGv_i32, TCGv_i32); -typedef void NeonGenTwoOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); -typedef void NeonGenThreeOpEnvFn(TCGv_i32, TCGv_env, TCGv_i32, - TCGv_i32, TCGv_i32); -typedef void NeonGenTwo64OpFn(TCGv_i64, TCGv_i64, TCGv_i64); -typedef void NeonGenTwo64OpEnvFn(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64); -typedef void NeonGenNarrowFn(TCGv_i32, TCGv_i64); -typedef void NeonGenNarrowEnvFn(TCGv_i32, TCGv_ptr, TCGv_i64); -typedef void NeonGenWidenFn(TCGv_i64, TCGv_i32); -typedef void NeonGenTwoOpWidenFn(TCGv_i64, TCGv_i32, TCGv_i32); -typedef void NeonGenOneSingleOpFn(TCGv_i32, TCGv_i32, TCGv_ptr); -typedef void NeonGenTwoSingleOpFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); -typedef void NeonGenTwoDoubleOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr); -typedef void NeonGenOne64OpFn(TCGv_i64, TCGv_i64); -typedef void CryptoTwoOpFn(TCGv_ptr, TCGv_ptr); -typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32); -typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); -typedef void AtomicThreeOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGArg, MemOp); -typedef void WideShiftImmFn(TCGv_i64, TCGv_i64, int64_t shift); -typedef void WideShiftFn(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i32); -typedef void ShiftImmFn(TCGv_i32, TCGv_i32, int32_t shift); -typedef void ShiftFn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); - -/** - * arm_tbflags_from_tb: - * @tb: the TranslationBlock - * - * Extract the flag values from @tb. - */ -static inline CPUARMTBFlags arm_tbflags_from_tb(const TranslationBlock *tb) -{ - return (CPUARMTBFlags){ tb->flags, tb->cs_base }; -} - -/* - * Enum for argument to fpstatus_ptr(). - */ -typedef enum ARMFPStatusFlavour { - FPST_FPCR, - FPST_FPCR_F16, - FPST_STD, - FPST_STD_F16, -} ARMFPStatusFlavour; - -/** - * fpstatus_ptr: return TCGv_ptr to the specified fp_status field - * - * We have multiple softfloat float_status fields in the Arm CPU state struct - * (see the comment in cpu.h for details). Return a TCGv_ptr which has - * been set up to point to the requested field in the CPU state struct. - * The options are: - * - * FPST_FPCR - * for non-FP16 operations controlled by the FPCR - * FPST_FPCR_F16 - * for operations controlled by the FPCR where FPCR.FZ16 is to be used - * FPST_STD - * for A32/T32 Neon operations using the "standard FPSCR value" - * FPST_STD_F16 - * as FPST_STD, but where FPCR.FZ16 is to be used - */ -static inline TCGv_ptr fpstatus_ptr(ARMFPStatusFlavour flavour) -{ - TCGv_ptr statusptr = tcg_temp_new_ptr(); - int offset; - - switch (flavour) { - case FPST_FPCR: - offset = offsetof(CPUARMState, vfp.fp_status); - break; - case FPST_FPCR_F16: - offset = offsetof(CPUARMState, vfp.fp_status_f16); - break; - case FPST_STD: - offset = offsetof(CPUARMState, vfp.standard_fp_status); - break; - case FPST_STD_F16: - offset = offsetof(CPUARMState, vfp.standard_fp_status_f16); - break; - default: - g_assert_not_reached(); - } - tcg_gen_addi_ptr(statusptr, cpu_env, offset); - return statusptr; -} - -/** - * finalize_memop: - * @s: DisasContext - * @opc: size+sign+align of the memory operation - * - * Build the complete MemOp for a memory operation, including alignment - * and endianness. - * - * If (op & MO_AMASK) then the operation already contains the required - * alignment, e.g. for AccType_ATOMIC. Otherwise, this an optionally - * unaligned operation, e.g. for AccType_NORMAL. - * - * In the latter case, there are configuration bits that require alignment, - * 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) -{ - if (s->align_mem && !(opc & MO_AMASK)) { - opc |= MO_ALIGN; - } - return opc | s->be_data; -} - -/** - * asimd_imm_const: Expand an encoded SIMD constant value - * - * Expand a SIMD constant value. This is essentially the pseudocode - * AdvSIMDExpandImm, except that we also perform the boolean NOT needed for - * VMVN and VBIC (when cmode < 14 && op == 1). - * - * The combination cmode == 15 op == 1 is a reserved encoding for AArch32; - * callers must catch this; we return the 64-bit constant value defined - * for AArch64. - * - * cmode = 2,3,4,5,6,7,10,11,12,13 imm=0 was UNPREDICTABLE in v7A but - * is either not unpredictable or merely CONSTRAINED UNPREDICTABLE in v8A; - * we produce an immediate constant value of 0 in these cases. - */ -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; -} - -/* - * Helpers for implementing sets of trans_* functions. - * Defer the implementation of NAME to FUNC, with optional extra arguments. - */ -#define TRANS(NAME, FUNC, ...) \ - static bool trans_##NAME(DisasContext *s, arg_##NAME *a) \ - { return FUNC(s, __VA_ARGS__); } -#define TRANS_FEAT(NAME, FEAT, FUNC, ...) \ - 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/vec_helper.c deleted file mode 100644 index f59d3b26ea..0000000000 --- a/target/arm/vec_helper.c +++ /dev/null @@ -1,2716 +0,0 @@ -/* - * ARM AdvSIMD / SVE Vector Operations - * - * 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 . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "tcg/tcg-gvec-desc.h" -#include "fpu/softfloat.h" -#include "qemu/int128.h" -#include "vec_internal.h" - -/* - * Data for expanding active predicate bits to bytes, for byte elements. - * - * for (i = 0; i < 256; ++i) { - * unsigned long m = 0; - * for (j = 0; j < 8; j++) { - * if ((i >> j) & 1) { - * m |= 0xfful << (j << 3); - * } - * } - * printf("0x%016lx,\n", m); - * } - */ -const uint64_t expand_pred_b_data[256] = { - 0x0000000000000000, 0x00000000000000ff, 0x000000000000ff00, - 0x000000000000ffff, 0x0000000000ff0000, 0x0000000000ff00ff, - 0x0000000000ffff00, 0x0000000000ffffff, 0x00000000ff000000, - 0x00000000ff0000ff, 0x00000000ff00ff00, 0x00000000ff00ffff, - 0x00000000ffff0000, 0x00000000ffff00ff, 0x00000000ffffff00, - 0x00000000ffffffff, 0x000000ff00000000, 0x000000ff000000ff, - 0x000000ff0000ff00, 0x000000ff0000ffff, 0x000000ff00ff0000, - 0x000000ff00ff00ff, 0x000000ff00ffff00, 0x000000ff00ffffff, - 0x000000ffff000000, 0x000000ffff0000ff, 0x000000ffff00ff00, - 0x000000ffff00ffff, 0x000000ffffff0000, 0x000000ffffff00ff, - 0x000000ffffffff00, 0x000000ffffffffff, 0x0000ff0000000000, - 0x0000ff00000000ff, 0x0000ff000000ff00, 0x0000ff000000ffff, - 0x0000ff0000ff0000, 0x0000ff0000ff00ff, 0x0000ff0000ffff00, - 0x0000ff0000ffffff, 0x0000ff00ff000000, 0x0000ff00ff0000ff, - 0x0000ff00ff00ff00, 0x0000ff00ff00ffff, 0x0000ff00ffff0000, - 0x0000ff00ffff00ff, 0x0000ff00ffffff00, 0x0000ff00ffffffff, - 0x0000ffff00000000, 0x0000ffff000000ff, 0x0000ffff0000ff00, - 0x0000ffff0000ffff, 0x0000ffff00ff0000, 0x0000ffff00ff00ff, - 0x0000ffff00ffff00, 0x0000ffff00ffffff, 0x0000ffffff000000, - 0x0000ffffff0000ff, 0x0000ffffff00ff00, 0x0000ffffff00ffff, - 0x0000ffffffff0000, 0x0000ffffffff00ff, 0x0000ffffffffff00, - 0x0000ffffffffffff, 0x00ff000000000000, 0x00ff0000000000ff, - 0x00ff00000000ff00, 0x00ff00000000ffff, 0x00ff000000ff0000, - 0x00ff000000ff00ff, 0x00ff000000ffff00, 0x00ff000000ffffff, - 0x00ff0000ff000000, 0x00ff0000ff0000ff, 0x00ff0000ff00ff00, - 0x00ff0000ff00ffff, 0x00ff0000ffff0000, 0x00ff0000ffff00ff, - 0x00ff0000ffffff00, 0x00ff0000ffffffff, 0x00ff00ff00000000, - 0x00ff00ff000000ff, 0x00ff00ff0000ff00, 0x00ff00ff0000ffff, - 0x00ff00ff00ff0000, 0x00ff00ff00ff00ff, 0x00ff00ff00ffff00, - 0x00ff00ff00ffffff, 0x00ff00ffff000000, 0x00ff00ffff0000ff, - 0x00ff00ffff00ff00, 0x00ff00ffff00ffff, 0x00ff00ffffff0000, - 0x00ff00ffffff00ff, 0x00ff00ffffffff00, 0x00ff00ffffffffff, - 0x00ffff0000000000, 0x00ffff00000000ff, 0x00ffff000000ff00, - 0x00ffff000000ffff, 0x00ffff0000ff0000, 0x00ffff0000ff00ff, - 0x00ffff0000ffff00, 0x00ffff0000ffffff, 0x00ffff00ff000000, - 0x00ffff00ff0000ff, 0x00ffff00ff00ff00, 0x00ffff00ff00ffff, - 0x00ffff00ffff0000, 0x00ffff00ffff00ff, 0x00ffff00ffffff00, - 0x00ffff00ffffffff, 0x00ffffff00000000, 0x00ffffff000000ff, - 0x00ffffff0000ff00, 0x00ffffff0000ffff, 0x00ffffff00ff0000, - 0x00ffffff00ff00ff, 0x00ffffff00ffff00, 0x00ffffff00ffffff, - 0x00ffffffff000000, 0x00ffffffff0000ff, 0x00ffffffff00ff00, - 0x00ffffffff00ffff, 0x00ffffffffff0000, 0x00ffffffffff00ff, - 0x00ffffffffffff00, 0x00ffffffffffffff, 0xff00000000000000, - 0xff000000000000ff, 0xff0000000000ff00, 0xff0000000000ffff, - 0xff00000000ff0000, 0xff00000000ff00ff, 0xff00000000ffff00, - 0xff00000000ffffff, 0xff000000ff000000, 0xff000000ff0000ff, - 0xff000000ff00ff00, 0xff000000ff00ffff, 0xff000000ffff0000, - 0xff000000ffff00ff, 0xff000000ffffff00, 0xff000000ffffffff, - 0xff0000ff00000000, 0xff0000ff000000ff, 0xff0000ff0000ff00, - 0xff0000ff0000ffff, 0xff0000ff00ff0000, 0xff0000ff00ff00ff, - 0xff0000ff00ffff00, 0xff0000ff00ffffff, 0xff0000ffff000000, - 0xff0000ffff0000ff, 0xff0000ffff00ff00, 0xff0000ffff00ffff, - 0xff0000ffffff0000, 0xff0000ffffff00ff, 0xff0000ffffffff00, - 0xff0000ffffffffff, 0xff00ff0000000000, 0xff00ff00000000ff, - 0xff00ff000000ff00, 0xff00ff000000ffff, 0xff00ff0000ff0000, - 0xff00ff0000ff00ff, 0xff00ff0000ffff00, 0xff00ff0000ffffff, - 0xff00ff00ff000000, 0xff00ff00ff0000ff, 0xff00ff00ff00ff00, - 0xff00ff00ff00ffff, 0xff00ff00ffff0000, 0xff00ff00ffff00ff, - 0xff00ff00ffffff00, 0xff00ff00ffffffff, 0xff00ffff00000000, - 0xff00ffff000000ff, 0xff00ffff0000ff00, 0xff00ffff0000ffff, - 0xff00ffff00ff0000, 0xff00ffff00ff00ff, 0xff00ffff00ffff00, - 0xff00ffff00ffffff, 0xff00ffffff000000, 0xff00ffffff0000ff, - 0xff00ffffff00ff00, 0xff00ffffff00ffff, 0xff00ffffffff0000, - 0xff00ffffffff00ff, 0xff00ffffffffff00, 0xff00ffffffffffff, - 0xffff000000000000, 0xffff0000000000ff, 0xffff00000000ff00, - 0xffff00000000ffff, 0xffff000000ff0000, 0xffff000000ff00ff, - 0xffff000000ffff00, 0xffff000000ffffff, 0xffff0000ff000000, - 0xffff0000ff0000ff, 0xffff0000ff00ff00, 0xffff0000ff00ffff, - 0xffff0000ffff0000, 0xffff0000ffff00ff, 0xffff0000ffffff00, - 0xffff0000ffffffff, 0xffff00ff00000000, 0xffff00ff000000ff, - 0xffff00ff0000ff00, 0xffff00ff0000ffff, 0xffff00ff00ff0000, - 0xffff00ff00ff00ff, 0xffff00ff00ffff00, 0xffff00ff00ffffff, - 0xffff00ffff000000, 0xffff00ffff0000ff, 0xffff00ffff00ff00, - 0xffff00ffff00ffff, 0xffff00ffffff0000, 0xffff00ffffff00ff, - 0xffff00ffffffff00, 0xffff00ffffffffff, 0xffffff0000000000, - 0xffffff00000000ff, 0xffffff000000ff00, 0xffffff000000ffff, - 0xffffff0000ff0000, 0xffffff0000ff00ff, 0xffffff0000ffff00, - 0xffffff0000ffffff, 0xffffff00ff000000, 0xffffff00ff0000ff, - 0xffffff00ff00ff00, 0xffffff00ff00ffff, 0xffffff00ffff0000, - 0xffffff00ffff00ff, 0xffffff00ffffff00, 0xffffff00ffffffff, - 0xffffffff00000000, 0xffffffff000000ff, 0xffffffff0000ff00, - 0xffffffff0000ffff, 0xffffffff00ff0000, 0xffffffff00ff00ff, - 0xffffffff00ffff00, 0xffffffff00ffffff, 0xffffffffff000000, - 0xffffffffff0000ff, 0xffffffffff00ff00, 0xffffffffff00ffff, - 0xffffffffffff0000, 0xffffffffffff00ff, 0xffffffffffffff00, - 0xffffffffffffffff, -}; - -/* - * Similarly for half-word elements. - * for (i = 0; i < 256; ++i) { - * unsigned long m = 0; - * if (i & 0xaa) { - * continue; - * } - * for (j = 0; j < 8; j += 2) { - * if ((i >> j) & 1) { - * m |= 0xfffful << (j << 3); - * } - * } - * printf("[0x%x] = 0x%016lx,\n", i, m); - * } - */ -const uint64_t expand_pred_h_data[0x55 + 1] = { - [0x01] = 0x000000000000ffff, [0x04] = 0x00000000ffff0000, - [0x05] = 0x00000000ffffffff, [0x10] = 0x0000ffff00000000, - [0x11] = 0x0000ffff0000ffff, [0x14] = 0x0000ffffffff0000, - [0x15] = 0x0000ffffffffffff, [0x40] = 0xffff000000000000, - [0x41] = 0xffff00000000ffff, [0x44] = 0xffff0000ffff0000, - [0x45] = 0xffff0000ffffffff, [0x50] = 0xffffffff00000000, - [0x51] = 0xffffffff0000ffff, [0x54] = 0xffffffffffff0000, - [0x55] = 0xffffffffffffffff, -}; - -/* Signed saturating rounding doubling multiply-accumulate high half, 8-bit */ -int8_t do_sqrdmlah_b(int8_t src1, int8_t src2, int8_t src3, - bool neg, bool round) -{ - /* - * Simplify: - * = ((a3 << 8) + ((e1 * e2) << 1) + (round << 7)) >> 8 - * = ((a3 << 7) + (e1 * e2) + (round << 6)) >> 7 - */ - int32_t ret = (int32_t)src1 * src2; - if (neg) { - ret = -ret; - } - ret += ((int32_t)src3 << 7) + (round << 6); - ret >>= 7; - - if (ret != (int8_t)ret) { - ret = (ret < 0 ? INT8_MIN : INT8_MAX); - } - return ret; -} - -void HELPER(sve2_sqrdmlah_b)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int8_t *d = vd, *n = vn, *m = vm, *a = va; - - for (i = 0; i < opr_sz; ++i) { - d[i] = do_sqrdmlah_b(n[i], m[i], a[i], false, true); - } -} - -void HELPER(sve2_sqrdmlsh_b)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int8_t *d = vd, *n = vn, *m = vm, *a = va; - - for (i = 0; i < opr_sz; ++i) { - d[i] = do_sqrdmlah_b(n[i], m[i], a[i], true, true); - } -} - -void HELPER(sve2_sqdmulh_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int8_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - d[i] = do_sqrdmlah_b(n[i], m[i], 0, false, false); - } -} - -void HELPER(sve2_sqrdmulh_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int8_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - d[i] = do_sqrdmlah_b(n[i], m[i], 0, false, true); - } -} - -/* Signed saturating rounding doubling multiply-accumulate high half, 16-bit */ -int16_t do_sqrdmlah_h(int16_t src1, int16_t src2, int16_t src3, - bool neg, bool round, uint32_t *sat) -{ - /* Simplify similarly to do_sqrdmlah_b above. */ - int32_t ret = (int32_t)src1 * src2; - if (neg) { - ret = -ret; - } - ret += ((int32_t)src3 << 15) + (round << 14); - ret >>= 15; - - if (ret != (int16_t)ret) { - *sat = 1; - ret = (ret < 0 ? INT16_MIN : INT16_MAX); - } - return ret; -} - -uint32_t HELPER(neon_qrdmlah_s16)(CPUARMState *env, uint32_t src1, - uint32_t src2, uint32_t src3) -{ - uint32_t *sat = &env->vfp.qc[0]; - uint16_t e1 = do_sqrdmlah_h(src1, src2, src3, false, true, sat); - uint16_t e2 = do_sqrdmlah_h(src1 >> 16, src2 >> 16, src3 >> 16, - false, true, sat); - return deposit32(e1, 16, 16, e2); -} - -void HELPER(gvec_qrdmlah_s16)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - int16_t *d = vd; - int16_t *n = vn; - int16_t *m = vm; - uintptr_t i; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], d[i], false, true, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -uint32_t HELPER(neon_qrdmlsh_s16)(CPUARMState *env, uint32_t src1, - uint32_t src2, uint32_t src3) -{ - uint32_t *sat = &env->vfp.qc[0]; - uint16_t e1 = do_sqrdmlah_h(src1, src2, src3, true, true, sat); - uint16_t e2 = do_sqrdmlah_h(src1 >> 16, src2 >> 16, src3 >> 16, - true, true, sat); - return deposit32(e1, 16, 16, e2); -} - -void HELPER(gvec_qrdmlsh_s16)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - int16_t *d = vd; - int16_t *n = vn; - int16_t *m = vm; - uintptr_t i; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], d[i], true, true, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(neon_sqdmulh_h)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, false, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(neon_sqrdmulh_h)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, true, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(sve2_sqrdmlah_h)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm, *a = va; - uint32_t discard; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], a[i], false, true, &discard); - } -} - -void HELPER(sve2_sqrdmlsh_h)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm, *a = va; - uint32_t discard; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], a[i], true, true, &discard); - } -} - -void HELPER(sve2_sqdmulh_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm; - uint32_t discard; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, false, &discard); - } -} - -void HELPER(sve2_sqrdmulh_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm; - uint32_t discard; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = do_sqrdmlah_h(n[i], m[i], 0, false, true, &discard); - } -} - -void HELPER(sve2_sqdmulh_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - int idx = simd_data(desc); - int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); - uint32_t discard; - - for (i = 0; i < opr_sz / 2; i += 16 / 2) { - int16_t mm = m[i]; - for (j = 0; j < 16 / 2; ++j) { - d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, false, &discard); - } - } -} - -void HELPER(sve2_sqrdmulh_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - int idx = simd_data(desc); - int16_t *d = vd, *n = vn, *m = (int16_t *)vm + H2(idx); - uint32_t discard; - - for (i = 0; i < opr_sz / 2; i += 16 / 2) { - int16_t mm = m[i]; - for (j = 0; j < 16 / 2; ++j) { - d[i + j] = do_sqrdmlah_h(n[i + j], mm, 0, false, true, &discard); - } - } -} - -/* Signed saturating rounding doubling multiply-accumulate high half, 32-bit */ -int32_t do_sqrdmlah_s(int32_t src1, int32_t src2, int32_t src3, - bool neg, bool round, uint32_t *sat) -{ - /* Simplify similarly to do_sqrdmlah_b above. */ - int64_t ret = (int64_t)src1 * src2; - if (neg) { - ret = -ret; - } - ret += ((int64_t)src3 << 31) + (round << 30); - ret >>= 31; - - if (ret != (int32_t)ret) { - *sat = 1; - ret = (ret < 0 ? INT32_MIN : INT32_MAX); - } - return ret; -} - -uint32_t HELPER(neon_qrdmlah_s32)(CPUARMState *env, int32_t src1, - int32_t src2, int32_t src3) -{ - uint32_t *sat = &env->vfp.qc[0]; - return do_sqrdmlah_s(src1, src2, src3, false, true, sat); -} - -void HELPER(gvec_qrdmlah_s32)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - int32_t *d = vd; - int32_t *n = vn; - int32_t *m = vm; - uintptr_t i; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], d[i], false, true, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -uint32_t HELPER(neon_qrdmlsh_s32)(CPUARMState *env, int32_t src1, - int32_t src2, int32_t src3) -{ - uint32_t *sat = &env->vfp.qc[0]; - return do_sqrdmlah_s(src1, src2, src3, true, true, sat); -} - -void HELPER(gvec_qrdmlsh_s32)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - int32_t *d = vd; - int32_t *n = vn; - int32_t *m = vm; - uintptr_t i; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], d[i], true, true, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(neon_sqdmulh_s)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, false, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(neon_sqrdmulh_s)(void *vd, void *vn, void *vm, - void *vq, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, true, vq); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(sve2_sqrdmlah_s)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm, *a = va; - uint32_t discard; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], a[i], false, true, &discard); - } -} - -void HELPER(sve2_sqrdmlsh_s)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm, *a = va; - uint32_t discard; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], a[i], true, true, &discard); - } -} - -void HELPER(sve2_sqdmulh_s)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm; - uint32_t discard; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, false, &discard); - } -} - -void HELPER(sve2_sqrdmulh_s)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm; - uint32_t discard; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = do_sqrdmlah_s(n[i], m[i], 0, false, true, &discard); - } -} - -void HELPER(sve2_sqdmulh_idx_s)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - int idx = simd_data(desc); - int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); - uint32_t discard; - - for (i = 0; i < opr_sz / 4; i += 16 / 4) { - int32_t mm = m[i]; - for (j = 0; j < 16 / 4; ++j) { - d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, false, &discard); - } - } -} - -void HELPER(sve2_sqrdmulh_idx_s)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - int idx = simd_data(desc); - int32_t *d = vd, *n = vn, *m = (int32_t *)vm + H4(idx); - uint32_t discard; - - for (i = 0; i < opr_sz / 4; i += 16 / 4) { - int32_t mm = m[i]; - for (j = 0; j < 16 / 4; ++j) { - d[i + j] = do_sqrdmlah_s(n[i + j], mm, 0, false, true, &discard); - } - } -} - -/* Signed saturating rounding doubling multiply-accumulate high half, 64-bit */ -static int64_t do_sat128_d(Int128 r) -{ - int64_t ls = int128_getlo(r); - int64_t hs = int128_gethi(r); - - if (unlikely(hs != (ls >> 63))) { - return hs < 0 ? INT64_MIN : INT64_MAX; - } - return ls; -} - -int64_t do_sqrdmlah_d(int64_t n, int64_t m, int64_t a, bool neg, bool round) -{ - uint64_t l, h; - Int128 r, t; - - /* As in do_sqrdmlah_b, but with 128-bit arithmetic. */ - muls64(&l, &h, m, n); - r = int128_make128(l, h); - if (neg) { - r = int128_neg(r); - } - if (a) { - t = int128_exts64(a); - t = int128_lshift(t, 63); - r = int128_add(r, t); - } - if (round) { - t = int128_exts64(1ll << 62); - r = int128_add(r, t); - } - r = int128_rshift(r, 63); - - return do_sat128_d(r); -} - -void HELPER(sve2_sqrdmlah_d)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int64_t *d = vd, *n = vn, *m = vm, *a = va; - - for (i = 0; i < opr_sz / 8; ++i) { - d[i] = do_sqrdmlah_d(n[i], m[i], a[i], false, true); - } -} - -void HELPER(sve2_sqrdmlsh_d)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int64_t *d = vd, *n = vn, *m = vm, *a = va; - - for (i = 0; i < opr_sz / 8; ++i) { - d[i] = do_sqrdmlah_d(n[i], m[i], a[i], true, true); - } -} - -void HELPER(sve2_sqdmulh_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int64_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 8; ++i) { - d[i] = do_sqrdmlah_d(n[i], m[i], 0, false, false); - } -} - -void HELPER(sve2_sqrdmulh_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int64_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 8; ++i) { - d[i] = do_sqrdmlah_d(n[i], m[i], 0, false, true); - } -} - -void HELPER(sve2_sqdmulh_idx_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - int idx = simd_data(desc); - int64_t *d = vd, *n = vn, *m = (int64_t *)vm + idx; - - for (i = 0; i < opr_sz / 8; i += 16 / 8) { - int64_t mm = m[i]; - for (j = 0; j < 16 / 8; ++j) { - d[i + j] = do_sqrdmlah_d(n[i + j], mm, 0, false, false); - } - } -} - -void HELPER(sve2_sqrdmulh_idx_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - int idx = simd_data(desc); - int64_t *d = vd, *n = vn, *m = (int64_t *)vm + idx; - - for (i = 0; i < opr_sz / 8; i += 16 / 8) { - int64_t mm = m[i]; - for (j = 0; j < 16 / 8; ++j) { - d[i + j] = do_sqrdmlah_d(n[i + j], mm, 0, false, true); - } - } -} - -/* Integer 8 and 16-bit dot-product. - * - * Note that for the loops herein, host endianness does not matter - * with respect to the ordering of data within the quad-width lanes. - * All elements are treated equally, no matter where they are. - */ - -#define DO_DOT(NAME, TYPED, TYPEN, TYPEM) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ -{ \ - intptr_t i, opr_sz = simd_oprsz(desc); \ - TYPED *d = vd, *a = va; \ - TYPEN *n = vn; \ - TYPEM *m = vm; \ - for (i = 0; i < opr_sz / sizeof(TYPED); ++i) { \ - d[i] = (a[i] + \ - (TYPED)n[i * 4 + 0] * m[i * 4 + 0] + \ - (TYPED)n[i * 4 + 1] * m[i * 4 + 1] + \ - (TYPED)n[i * 4 + 2] * m[i * 4 + 2] + \ - (TYPED)n[i * 4 + 3] * m[i * 4 + 3]); \ - } \ - clear_tail(d, opr_sz, simd_maxsz(desc)); \ -} - -DO_DOT(gvec_sdot_b, int32_t, int8_t, int8_t) -DO_DOT(gvec_udot_b, uint32_t, uint8_t, uint8_t) -DO_DOT(gvec_usdot_b, uint32_t, uint8_t, int8_t) -DO_DOT(gvec_sdot_h, int64_t, int16_t, int16_t) -DO_DOT(gvec_udot_h, uint64_t, uint16_t, uint16_t) - -#define DO_DOT_IDX(NAME, TYPED, TYPEN, TYPEM, HD) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ -{ \ - intptr_t i = 0, opr_sz = simd_oprsz(desc); \ - intptr_t opr_sz_n = opr_sz / sizeof(TYPED); \ - intptr_t segend = MIN(16 / sizeof(TYPED), opr_sz_n); \ - intptr_t index = simd_data(desc); \ - TYPED *d = vd, *a = va; \ - TYPEN *n = vn; \ - TYPEM *m_indexed = (TYPEM *)vm + HD(index) * 4; \ - do { \ - TYPED m0 = m_indexed[i * 4 + 0]; \ - TYPED m1 = m_indexed[i * 4 + 1]; \ - TYPED m2 = m_indexed[i * 4 + 2]; \ - TYPED m3 = m_indexed[i * 4 + 3]; \ - do { \ - d[i] = (a[i] + \ - n[i * 4 + 0] * m0 + \ - n[i * 4 + 1] * m1 + \ - n[i * 4 + 2] * m2 + \ - n[i * 4 + 3] * m3); \ - } while (++i < segend); \ - segend = i + 4; \ - } while (i < opr_sz_n); \ - clear_tail(d, opr_sz, simd_maxsz(desc)); \ -} - -DO_DOT_IDX(gvec_sdot_idx_b, int32_t, int8_t, int8_t, H4) -DO_DOT_IDX(gvec_udot_idx_b, uint32_t, uint8_t, uint8_t, H4) -DO_DOT_IDX(gvec_sudot_idx_b, int32_t, int8_t, uint8_t, H4) -DO_DOT_IDX(gvec_usdot_idx_b, int32_t, uint8_t, int8_t, H4) -DO_DOT_IDX(gvec_sdot_idx_h, int64_t, int16_t, int16_t, H8) -DO_DOT_IDX(gvec_udot_idx_h, uint64_t, uint16_t, uint16_t, H8) - -void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float16 *d = vd; - float16 *n = vn; - float16 *m = vm; - float_status *fpst = vfpst; - uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = neg_real ^ 1; - uintptr_t i; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; - - for (i = 0; i < opr_sz / 2; i += 2) { - float16 e0 = n[H2(i)]; - float16 e1 = m[H2(i + 1)] ^ neg_imag; - float16 e2 = n[H2(i + 1)]; - float16 e3 = m[H2(i)] ^ neg_real; - - d[H2(i)] = float16_add(e0, e1, fpst); - d[H2(i + 1)] = float16_add(e2, e3, fpst); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcadds)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float32 *d = vd; - float32 *n = vn; - float32 *m = vm; - float_status *fpst = vfpst; - uint32_t neg_real = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = neg_real ^ 1; - uintptr_t i; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; - - for (i = 0; i < opr_sz / 4; i += 2) { - float32 e0 = n[H4(i)]; - float32 e1 = m[H4(i + 1)] ^ neg_imag; - float32 e2 = n[H4(i + 1)]; - float32 e3 = m[H4(i)] ^ neg_real; - - d[H4(i)] = float32_add(e0, e1, fpst); - d[H4(i + 1)] = float32_add(e2, e3, fpst); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcaddd)(void *vd, void *vn, void *vm, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float64 *d = vd; - float64 *n = vn; - float64 *m = vm; - float_status *fpst = vfpst; - uint64_t neg_real = extract64(desc, SIMD_DATA_SHIFT, 1); - uint64_t neg_imag = neg_real ^ 1; - uintptr_t i; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 63; - neg_imag <<= 63; - - for (i = 0; i < opr_sz / 8; i += 2) { - float64 e0 = n[i]; - float64 e1 = m[i + 1] ^ neg_imag; - float64 e2 = n[i + 1]; - float64 e3 = m[i] ^ neg_real; - - d[i] = float64_add(e0, e1, fpst); - d[i + 1] = float64_add(e2, e3, fpst); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcmlah)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float16 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; - intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - uint32_t neg_real = flip ^ neg_imag; - uintptr_t i; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; - - for (i = 0; i < opr_sz / 2; i += 2) { - float16 e2 = n[H2(i + flip)]; - float16 e1 = m[H2(i + flip)] ^ neg_real; - float16 e4 = e2; - float16 e3 = m[H2(i + 1 - flip)] ^ neg_imag; - - d[H2(i)] = float16_muladd(e2, e1, a[H2(i)], 0, fpst); - d[H2(i + 1)] = float16_muladd(e4, e3, a[H2(i + 1)], 0, fpst); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float16 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; - intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); - uint32_t neg_real = flip ^ neg_imag; - intptr_t elements = opr_sz / sizeof(float16); - intptr_t eltspersegment = 16 / sizeof(float16); - intptr_t i, j; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 15; - neg_imag <<= 15; - - for (i = 0; i < elements; i += eltspersegment) { - float16 mr = m[H2(i + 2 * index + 0)]; - float16 mi = m[H2(i + 2 * index + 1)]; - float16 e1 = neg_real ^ (flip ? mi : mr); - float16 e3 = neg_imag ^ (flip ? mr : mi); - - for (j = i; j < i + eltspersegment; j += 2) { - float16 e2 = n[H2(j + flip)]; - float16 e4 = e2; - - d[H2(j)] = float16_muladd(e2, e1, a[H2(j)], 0, fpst); - d[H2(j + 1)] = float16_muladd(e4, e3, a[H2(j + 1)], 0, fpst); - } - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcmlas)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float32 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; - intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - uint32_t neg_real = flip ^ neg_imag; - uintptr_t i; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; - - for (i = 0; i < opr_sz / 4; i += 2) { - float32 e2 = n[H4(i + flip)]; - float32 e1 = m[H4(i + flip)] ^ neg_real; - float32 e4 = e2; - float32 e3 = m[H4(i + 1 - flip)] ^ neg_imag; - - d[H4(i)] = float32_muladd(e2, e1, a[H4(i)], 0, fpst); - d[H4(i + 1)] = float32_muladd(e4, e3, a[H4(i + 1)], 0, fpst); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float32 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; - intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); - uint32_t neg_real = flip ^ neg_imag; - intptr_t elements = opr_sz / sizeof(float32); - intptr_t eltspersegment = 16 / sizeof(float32); - intptr_t i, j; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 31; - neg_imag <<= 31; - - for (i = 0; i < elements; i += eltspersegment) { - float32 mr = m[H4(i + 2 * index + 0)]; - float32 mi = m[H4(i + 2 * index + 1)]; - float32 e1 = neg_real ^ (flip ? mi : mr); - float32 e3 = neg_imag ^ (flip ? mr : mi); - - for (j = i; j < i + eltspersegment; j += 2) { - float32 e2 = n[H4(j + flip)]; - float32 e4 = e2; - - d[H4(j)] = float32_muladd(e2, e1, a[H4(j)], 0, fpst); - d[H4(j + 1)] = float32_muladd(e4, e3, a[H4(j + 1)], 0, fpst); - } - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, void *va, - void *vfpst, uint32_t desc) -{ - uintptr_t opr_sz = simd_oprsz(desc); - float64 *d = vd, *n = vn, *m = vm, *a = va; - float_status *fpst = vfpst; - intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); - uint64_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - uint64_t neg_real = flip ^ neg_imag; - uintptr_t i; - - /* Shift boolean to the sign bit so we can xor to negate. */ - neg_real <<= 63; - neg_imag <<= 63; - - for (i = 0; i < opr_sz / 8; i += 2) { - float64 e2 = n[i + flip]; - float64 e1 = m[i + flip] ^ neg_real; - float64 e4 = e2; - float64 e3 = m[i + 1 - flip] ^ neg_imag; - - d[i] = float64_muladd(e2, e1, a[i], 0, fpst); - d[i + 1] = float64_muladd(e4, e3, a[i + 1], 0, fpst); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -/* - * Floating point comparisons producing an integer result (all 1s or all 0s). - * Note that EQ doesn't signal InvalidOp for QNaNs but GE and GT do. - * Softfloat routines return 0/1, which we convert to the 0/-1 Neon requires. - */ -static uint16_t float16_ceq(float16 op1, float16 op2, float_status *stat) -{ - return -float16_eq_quiet(op1, op2, stat); -} - -static uint32_t float32_ceq(float32 op1, float32 op2, float_status *stat) -{ - return -float32_eq_quiet(op1, op2, stat); -} - -static uint16_t float16_cge(float16 op1, float16 op2, float_status *stat) -{ - return -float16_le(op2, op1, stat); -} - -static uint32_t float32_cge(float32 op1, float32 op2, float_status *stat) -{ - return -float32_le(op2, op1, stat); -} - -static uint16_t float16_cgt(float16 op1, float16 op2, float_status *stat) -{ - return -float16_lt(op2, op1, stat); -} - -static uint32_t float32_cgt(float32 op1, float32 op2, float_status *stat) -{ - return -float32_lt(op2, op1, stat); -} - -static uint16_t float16_acge(float16 op1, float16 op2, float_status *stat) -{ - return -float16_le(float16_abs(op2), float16_abs(op1), stat); -} - -static uint32_t float32_acge(float32 op1, float32 op2, float_status *stat) -{ - return -float32_le(float32_abs(op2), float32_abs(op1), stat); -} - -static uint16_t float16_acgt(float16 op1, float16 op2, float_status *stat) -{ - return -float16_lt(float16_abs(op2), float16_abs(op1), stat); -} - -static uint32_t float32_acgt(float32 op1, float32 op2, float_status *stat) -{ - return -float32_lt(float32_abs(op2), float32_abs(op1), stat); -} - -static int16_t vfp_tosszh(float16 x, void *fpstp) -{ - float_status *fpst = fpstp; - if (float16_is_any_nan(x)) { - float_raise(float_flag_invalid, fpst); - return 0; - } - return float16_to_int16_round_to_zero(x, fpst); -} - -static uint16_t vfp_touszh(float16 x, void *fpstp) -{ - float_status *fpst = fpstp; - if (float16_is_any_nan(x)) { - float_raise(float_flag_invalid, fpst); - return 0; - } - return float16_to_uint16_round_to_zero(x, fpst); -} - -#define DO_2OP(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - TYPE *d = vd, *n = vn; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = FUNC(n[i], stat); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) -DO_2OP(gvec_frecpe_s, helper_recpe_f32, float32) -DO_2OP(gvec_frecpe_d, helper_recpe_f64, float64) - -DO_2OP(gvec_frsqrte_h, helper_rsqrte_f16, float16) -DO_2OP(gvec_frsqrte_s, helper_rsqrte_f32, float32) -DO_2OP(gvec_frsqrte_d, helper_rsqrte_f64, float64) - -DO_2OP(gvec_vrintx_h, float16_round_to_int, float16) -DO_2OP(gvec_vrintx_s, float32_round_to_int, float32) - -DO_2OP(gvec_sitos, helper_vfp_sitos, int32_t) -DO_2OP(gvec_uitos, helper_vfp_uitos, uint32_t) -DO_2OP(gvec_tosizs, helper_vfp_tosizs, float32) -DO_2OP(gvec_touizs, helper_vfp_touizs, float32) -DO_2OP(gvec_sstoh, int16_to_float16, int16_t) -DO_2OP(gvec_ustoh, uint16_to_float16, uint16_t) -DO_2OP(gvec_tosszh, vfp_tosszh, float16) -DO_2OP(gvec_touszh, vfp_touszh, float16) - -#define WRAP_CMP0_FWD(FN, CMPOP, TYPE) \ - static TYPE TYPE##_##FN##0(TYPE op, float_status *stat) \ - { \ - return TYPE##_##CMPOP(op, TYPE##_zero, stat); \ - } - -#define WRAP_CMP0_REV(FN, CMPOP, TYPE) \ - static TYPE TYPE##_##FN##0(TYPE op, float_status *stat) \ - { \ - return TYPE##_##CMPOP(TYPE##_zero, op, stat); \ - } - -#define DO_2OP_CMP0(FN, CMPOP, DIRN) \ - WRAP_CMP0_##DIRN(FN, CMPOP, float16) \ - WRAP_CMP0_##DIRN(FN, CMPOP, float32) \ - DO_2OP(gvec_f##FN##0_h, float16_##FN##0, float16) \ - DO_2OP(gvec_f##FN##0_s, float32_##FN##0, float32) - -DO_2OP_CMP0(cgt, cgt, FWD) -DO_2OP_CMP0(cge, cge, FWD) -DO_2OP_CMP0(ceq, ceq, FWD) -DO_2OP_CMP0(clt, cgt, REV) -DO_2OP_CMP0(cle, cge, REV) - -#undef DO_2OP -#undef DO_2OP_CMP0 - -/* Floating-point trigonometric starting value. - * See the ARM ARM pseudocode function FPTrigSMul. - */ -static float16 float16_ftsmul(float16 op1, uint16_t op2, float_status *stat) -{ - float16 result = float16_mul(op1, op1, stat); - if (!float16_is_any_nan(result)) { - result = float16_set_sign(result, op2 & 1); - } - return result; -} - -static float32 float32_ftsmul(float32 op1, uint32_t op2, float_status *stat) -{ - float32 result = float32_mul(op1, op1, stat); - if (!float32_is_any_nan(result)) { - result = float32_set_sign(result, op2 & 1); - } - return result; -} - -static float64 float64_ftsmul(float64 op1, uint64_t op2, float_status *stat) -{ - float64 result = float64_mul(op1, op1, stat); - if (!float64_is_any_nan(result)) { - result = float64_set_sign(result, op2 & 1); - } - return result; -} - -static float16 float16_abd(float16 op1, float16 op2, float_status *stat) -{ - return float16_abs(float16_sub(op1, op2, stat)); -} - -static float32 float32_abd(float32 op1, float32 op2, float_status *stat) -{ - return float32_abs(float32_sub(op1, op2, stat)); -} - -/* - * Reciprocal step. These are the AArch32 version which uses a - * non-fused multiply-and-subtract. - */ -static float16 float16_recps_nf(float16 op1, float16 op2, float_status *stat) -{ - op1 = float16_squash_input_denormal(op1, stat); - op2 = float16_squash_input_denormal(op2, stat); - - if ((float16_is_infinity(op1) && float16_is_zero(op2)) || - (float16_is_infinity(op2) && float16_is_zero(op1))) { - return float16_two; - } - return float16_sub(float16_two, float16_mul(op1, op2, stat), stat); -} - -static float32 float32_recps_nf(float32 op1, float32 op2, float_status *stat) -{ - op1 = float32_squash_input_denormal(op1, stat); - op2 = float32_squash_input_denormal(op2, stat); - - if ((float32_is_infinity(op1) && float32_is_zero(op2)) || - (float32_is_infinity(op2) && float32_is_zero(op1))) { - return float32_two; - } - return float32_sub(float32_two, float32_mul(op1, op2, stat), stat); -} - -/* Reciprocal square-root step. AArch32 non-fused semantics. */ -static float16 float16_rsqrts_nf(float16 op1, float16 op2, float_status *stat) -{ - op1 = float16_squash_input_denormal(op1, stat); - op2 = float16_squash_input_denormal(op2, stat); - - if ((float16_is_infinity(op1) && float16_is_zero(op2)) || - (float16_is_infinity(op2) && float16_is_zero(op1))) { - return float16_one_point_five; - } - op1 = float16_sub(float16_three, float16_mul(op1, op2, stat), stat); - return float16_div(op1, float16_two, stat); -} - -static float32 float32_rsqrts_nf(float32 op1, float32 op2, float_status *stat) -{ - op1 = float32_squash_input_denormal(op1, stat); - op2 = float32_squash_input_denormal(op2, stat); - - if ((float32_is_infinity(op1) && float32_is_zero(op2)) || - (float32_is_infinity(op2) && float32_is_zero(op1))) { - return float32_one_point_five; - } - op1 = float32_sub(float32_three, float32_mul(op1, op2, stat), stat); - return float32_div(op1, float32_two, stat); -} - -#define DO_3OP(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - TYPE *d = vd, *n = vn, *m = vm; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = FUNC(n[i], m[i], stat); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_3OP(gvec_fadd_h, float16_add, float16) -DO_3OP(gvec_fadd_s, float32_add, float32) -DO_3OP(gvec_fadd_d, float64_add, float64) - -DO_3OP(gvec_fsub_h, float16_sub, float16) -DO_3OP(gvec_fsub_s, float32_sub, float32) -DO_3OP(gvec_fsub_d, float64_sub, float64) - -DO_3OP(gvec_fmul_h, float16_mul, float16) -DO_3OP(gvec_fmul_s, float32_mul, float32) -DO_3OP(gvec_fmul_d, float64_mul, float64) - -DO_3OP(gvec_ftsmul_h, float16_ftsmul, float16) -DO_3OP(gvec_ftsmul_s, float32_ftsmul, float32) -DO_3OP(gvec_ftsmul_d, float64_ftsmul, float64) - -DO_3OP(gvec_fabd_h, float16_abd, float16) -DO_3OP(gvec_fabd_s, float32_abd, float32) - -DO_3OP(gvec_fceq_h, float16_ceq, float16) -DO_3OP(gvec_fceq_s, float32_ceq, float32) - -DO_3OP(gvec_fcge_h, float16_cge, float16) -DO_3OP(gvec_fcge_s, float32_cge, float32) - -DO_3OP(gvec_fcgt_h, float16_cgt, float16) -DO_3OP(gvec_fcgt_s, float32_cgt, float32) - -DO_3OP(gvec_facge_h, float16_acge, float16) -DO_3OP(gvec_facge_s, float32_acge, float32) - -DO_3OP(gvec_facgt_h, float16_acgt, float16) -DO_3OP(gvec_facgt_s, float32_acgt, float32) - -DO_3OP(gvec_fmax_h, float16_max, float16) -DO_3OP(gvec_fmax_s, float32_max, float32) - -DO_3OP(gvec_fmin_h, float16_min, float16) -DO_3OP(gvec_fmin_s, float32_min, float32) - -DO_3OP(gvec_fmaxnum_h, float16_maxnum, float16) -DO_3OP(gvec_fmaxnum_s, float32_maxnum, float32) - -DO_3OP(gvec_fminnum_h, float16_minnum, float16) -DO_3OP(gvec_fminnum_s, float32_minnum, float32) - -DO_3OP(gvec_recps_nf_h, float16_recps_nf, float16) -DO_3OP(gvec_recps_nf_s, float32_recps_nf, float32) - -DO_3OP(gvec_rsqrts_nf_h, float16_rsqrts_nf, float16) -DO_3OP(gvec_rsqrts_nf_s, float32_rsqrts_nf, float32) - -#ifdef TARGET_AARCH64 - -DO_3OP(gvec_recps_h, helper_recpsf_f16, float16) -DO_3OP(gvec_recps_s, helper_recpsf_f32, float32) -DO_3OP(gvec_recps_d, helper_recpsf_f64, float64) - -DO_3OP(gvec_rsqrts_h, helper_rsqrtsf_f16, float16) -DO_3OP(gvec_rsqrts_s, helper_rsqrtsf_f32, float32) -DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) - -#endif -#undef DO_3OP - -/* Non-fused multiply-add (unlike float16_muladd etc, which are fused) */ -static float16 float16_muladd_nf(float16 dest, float16 op1, float16 op2, - float_status *stat) -{ - return float16_add(dest, float16_mul(op1, op2, stat), stat); -} - -static float32 float32_muladd_nf(float32 dest, float32 op1, float32 op2, - float_status *stat) -{ - return float32_add(dest, float32_mul(op1, op2, stat), stat); -} - -static float16 float16_mulsub_nf(float16 dest, float16 op1, float16 op2, - float_status *stat) -{ - return float16_sub(dest, float16_mul(op1, op2, stat), stat); -} - -static float32 float32_mulsub_nf(float32 dest, float32 op1, float32 op2, - float_status *stat) -{ - return float32_sub(dest, float32_mul(op1, op2, stat), stat); -} - -/* Fused versions; these have the semantics Neon VFMA/VFMS want */ -static float16 float16_muladd_f(float16 dest, float16 op1, float16 op2, - float_status *stat) -{ - return float16_muladd(op1, op2, dest, 0, stat); -} - -static float32 float32_muladd_f(float32 dest, float32 op1, float32 op2, - float_status *stat) -{ - return float32_muladd(op1, op2, dest, 0, stat); -} - -static float16 float16_mulsub_f(float16 dest, float16 op1, float16 op2, - float_status *stat) -{ - return float16_muladd(float16_chs(op1), op2, dest, 0, stat); -} - -static float32 float32_mulsub_f(float32 dest, float32 op1, float32 op2, - float_status *stat) -{ - return float32_muladd(float32_chs(op1), op2, dest, 0, stat); -} - -#define DO_MULADD(NAME, FUNC, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - TYPE *d = vd, *n = vn, *m = vm; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = FUNC(d[i], n[i], m[i], stat); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_MULADD(gvec_fmla_h, float16_muladd_nf, float16) -DO_MULADD(gvec_fmla_s, float32_muladd_nf, float32) - -DO_MULADD(gvec_fmls_h, float16_mulsub_nf, float16) -DO_MULADD(gvec_fmls_s, float32_mulsub_nf, float32) - -DO_MULADD(gvec_vfma_h, float16_muladd_f, float16) -DO_MULADD(gvec_vfma_s, float32_muladd_f, float32) - -DO_MULADD(gvec_vfms_h, float16_mulsub_f, float16) -DO_MULADD(gvec_vfms_s, float32_mulsub_f, float32) - -/* For the indexed ops, SVE applies the index per 128-bit vector segment. - * For AdvSIMD, there is of course only one such vector segment. - */ - -#define DO_MUL_IDX(NAME, TYPE, H) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ -{ \ - intptr_t i, j, oprsz = simd_oprsz(desc); \ - intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ - intptr_t idx = simd_data(desc); \ - TYPE *d = vd, *n = vn, *m = vm; \ - for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ - TYPE mm = m[H(i + idx)]; \ - for (j = 0; j < segment; j++) { \ - d[i + j] = n[i + j] * mm; \ - } \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_MUL_IDX(gvec_mul_idx_h, uint16_t, H2) -DO_MUL_IDX(gvec_mul_idx_s, uint32_t, H4) -DO_MUL_IDX(gvec_mul_idx_d, uint64_t, H8) - -#undef DO_MUL_IDX - -#define DO_MLA_IDX(NAME, TYPE, OP, H) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ -{ \ - intptr_t i, j, oprsz = simd_oprsz(desc); \ - intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ - intptr_t idx = simd_data(desc); \ - TYPE *d = vd, *n = vn, *m = vm, *a = va; \ - for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ - TYPE mm = m[H(i + idx)]; \ - for (j = 0; j < segment; j++) { \ - d[i + j] = a[i + j] OP n[i + j] * mm; \ - } \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_MLA_IDX(gvec_mla_idx_h, uint16_t, +, H2) -DO_MLA_IDX(gvec_mla_idx_s, uint32_t, +, H4) -DO_MLA_IDX(gvec_mla_idx_d, uint64_t, +, H8) - -DO_MLA_IDX(gvec_mls_idx_h, uint16_t, -, H2) -DO_MLA_IDX(gvec_mls_idx_s, uint32_t, -, H4) -DO_MLA_IDX(gvec_mls_idx_d, uint64_t, -, H8) - -#undef DO_MLA_IDX - -#define DO_FMUL_IDX(NAME, ADD, TYPE, H) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ -{ \ - intptr_t i, j, oprsz = simd_oprsz(desc); \ - intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ - intptr_t idx = simd_data(desc); \ - TYPE *d = vd, *n = vn, *m = vm; \ - for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ - TYPE mm = m[H(i + idx)]; \ - for (j = 0; j < segment; j++) { \ - d[i + j] = TYPE##_##ADD(d[i + j], \ - TYPE##_mul(n[i + j], mm, stat), stat); \ - } \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -#define float16_nop(N, M, S) (M) -#define float32_nop(N, M, S) (M) -#define float64_nop(N, M, S) (M) - -DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16, H2) -DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32, H4) -DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64, H8) - -/* - * Non-fused multiply-accumulate operations, for Neon. NB that unlike - * the fused ops below they assume accumulate both from and into Vd. - */ -DO_FMUL_IDX(gvec_fmla_nf_idx_h, add, float16, H2) -DO_FMUL_IDX(gvec_fmla_nf_idx_s, add, float32, H4) -DO_FMUL_IDX(gvec_fmls_nf_idx_h, sub, float16, H2) -DO_FMUL_IDX(gvec_fmls_nf_idx_s, sub, float32, H4) - -#undef float16_nop -#undef float32_nop -#undef float64_nop -#undef DO_FMUL_IDX - -#define DO_FMLA_IDX(NAME, TYPE, H) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ - void *stat, uint32_t desc) \ -{ \ - intptr_t i, j, oprsz = simd_oprsz(desc); \ - intptr_t segment = MIN(16, oprsz) / sizeof(TYPE); \ - TYPE op1_neg = extract32(desc, SIMD_DATA_SHIFT, 1); \ - intptr_t idx = desc >> (SIMD_DATA_SHIFT + 1); \ - TYPE *d = vd, *n = vn, *m = vm, *a = va; \ - op1_neg <<= (8 * sizeof(TYPE) - 1); \ - for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ - TYPE mm = m[H(i + idx)]; \ - for (j = 0; j < segment; j++) { \ - d[i + j] = TYPE##_muladd(n[i + j] ^ op1_neg, \ - mm, a[i + j], 0, stat); \ - } \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2) -DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4) -DO_FMLA_IDX(gvec_fmla_idx_d, float64, H8) - -#undef DO_FMLA_IDX - -#define DO_SAT(NAME, WTYPE, TYPEN, TYPEM, OP, MIN, MAX) \ -void HELPER(NAME)(void *vd, void *vq, void *vn, void *vm, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - TYPEN *d = vd, *n = vn; TYPEM *m = vm; \ - bool q = false; \ - for (i = 0; i < oprsz / sizeof(TYPEN); i++) { \ - WTYPE dd = (WTYPE)n[i] OP m[i]; \ - if (dd < MIN) { \ - dd = MIN; \ - q = true; \ - } else if (dd > MAX) { \ - dd = MAX; \ - q = true; \ - } \ - d[i] = dd; \ - } \ - if (q) { \ - uint32_t *qc = vq; \ - qc[0] = 1; \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_SAT(gvec_uqadd_b, int, uint8_t, uint8_t, +, 0, UINT8_MAX) -DO_SAT(gvec_uqadd_h, int, uint16_t, uint16_t, +, 0, UINT16_MAX) -DO_SAT(gvec_uqadd_s, int64_t, uint32_t, uint32_t, +, 0, UINT32_MAX) - -DO_SAT(gvec_sqadd_b, int, int8_t, int8_t, +, INT8_MIN, INT8_MAX) -DO_SAT(gvec_sqadd_h, int, int16_t, int16_t, +, INT16_MIN, INT16_MAX) -DO_SAT(gvec_sqadd_s, int64_t, int32_t, int32_t, +, INT32_MIN, INT32_MAX) - -DO_SAT(gvec_uqsub_b, int, uint8_t, uint8_t, -, 0, UINT8_MAX) -DO_SAT(gvec_uqsub_h, int, uint16_t, uint16_t, -, 0, UINT16_MAX) -DO_SAT(gvec_uqsub_s, int64_t, uint32_t, uint32_t, -, 0, UINT32_MAX) - -DO_SAT(gvec_sqsub_b, int, int8_t, int8_t, -, INT8_MIN, INT8_MAX) -DO_SAT(gvec_sqsub_h, int, int16_t, int16_t, -, INT16_MIN, INT16_MAX) -DO_SAT(gvec_sqsub_s, int64_t, int32_t, int32_t, -, INT32_MIN, INT32_MAX) - -#undef DO_SAT - -void HELPER(gvec_uqadd_d)(void *vd, void *vq, void *vn, - void *vm, uint32_t desc) -{ - intptr_t i, oprsz = simd_oprsz(desc); - uint64_t *d = vd, *n = vn, *m = vm; - bool q = false; - - for (i = 0; i < oprsz / 8; i++) { - uint64_t nn = n[i], mm = m[i], dd = nn + mm; - if (dd < nn) { - dd = UINT64_MAX; - q = true; - } - d[i] = dd; - } - if (q) { - uint32_t *qc = vq; - qc[0] = 1; - } - clear_tail(d, oprsz, simd_maxsz(desc)); -} - -void HELPER(gvec_uqsub_d)(void *vd, void *vq, void *vn, - void *vm, uint32_t desc) -{ - intptr_t i, oprsz = simd_oprsz(desc); - uint64_t *d = vd, *n = vn, *m = vm; - bool q = false; - - for (i = 0; i < oprsz / 8; i++) { - uint64_t nn = n[i], mm = m[i], dd = nn - mm; - if (nn < mm) { - dd = 0; - q = true; - } - d[i] = dd; - } - if (q) { - uint32_t *qc = vq; - qc[0] = 1; - } - clear_tail(d, oprsz, simd_maxsz(desc)); -} - -void HELPER(gvec_sqadd_d)(void *vd, void *vq, void *vn, - void *vm, uint32_t desc) -{ - intptr_t i, oprsz = simd_oprsz(desc); - int64_t *d = vd, *n = vn, *m = vm; - bool q = false; - - for (i = 0; i < oprsz / 8; i++) { - int64_t nn = n[i], mm = m[i], dd = nn + mm; - if (((dd ^ nn) & ~(nn ^ mm)) & INT64_MIN) { - dd = (nn >> 63) ^ ~INT64_MIN; - q = true; - } - d[i] = dd; - } - if (q) { - uint32_t *qc = vq; - qc[0] = 1; - } - clear_tail(d, oprsz, simd_maxsz(desc)); -} - -void HELPER(gvec_sqsub_d)(void *vd, void *vq, void *vn, - void *vm, uint32_t desc) -{ - intptr_t i, oprsz = simd_oprsz(desc); - int64_t *d = vd, *n = vn, *m = vm; - bool q = false; - - for (i = 0; i < oprsz / 8; i++) { - int64_t nn = n[i], mm = m[i], dd = nn - mm; - if (((dd ^ nn) & (nn ^ mm)) & INT64_MIN) { - dd = (nn >> 63) ^ ~INT64_MIN; - q = true; - } - d[i] = dd; - } - if (q) { - uint32_t *qc = vq; - qc[0] = 1; - } - clear_tail(d, oprsz, simd_maxsz(desc)); -} - - -#define DO_SRA(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - int shift = simd_data(desc); \ - TYPE *d = vd, *n = vn; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] += n[i] >> shift; \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_SRA(gvec_ssra_b, int8_t) -DO_SRA(gvec_ssra_h, int16_t) -DO_SRA(gvec_ssra_s, int32_t) -DO_SRA(gvec_ssra_d, int64_t) - -DO_SRA(gvec_usra_b, uint8_t) -DO_SRA(gvec_usra_h, uint16_t) -DO_SRA(gvec_usra_s, uint32_t) -DO_SRA(gvec_usra_d, uint64_t) - -#undef DO_SRA - -#define DO_RSHR(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - int shift = simd_data(desc); \ - TYPE *d = vd, *n = vn; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - TYPE tmp = n[i] >> (shift - 1); \ - d[i] = (tmp >> 1) + (tmp & 1); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_RSHR(gvec_srshr_b, int8_t) -DO_RSHR(gvec_srshr_h, int16_t) -DO_RSHR(gvec_srshr_s, int32_t) -DO_RSHR(gvec_srshr_d, int64_t) - -DO_RSHR(gvec_urshr_b, uint8_t) -DO_RSHR(gvec_urshr_h, uint16_t) -DO_RSHR(gvec_urshr_s, uint32_t) -DO_RSHR(gvec_urshr_d, uint64_t) - -#undef DO_RSHR - -#define DO_RSRA(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - int shift = simd_data(desc); \ - TYPE *d = vd, *n = vn; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - TYPE tmp = n[i] >> (shift - 1); \ - d[i] += (tmp >> 1) + (tmp & 1); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_RSRA(gvec_srsra_b, int8_t) -DO_RSRA(gvec_srsra_h, int16_t) -DO_RSRA(gvec_srsra_s, int32_t) -DO_RSRA(gvec_srsra_d, int64_t) - -DO_RSRA(gvec_ursra_b, uint8_t) -DO_RSRA(gvec_ursra_h, uint16_t) -DO_RSRA(gvec_ursra_s, uint32_t) -DO_RSRA(gvec_ursra_d, uint64_t) - -#undef DO_RSRA - -#define DO_SRI(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - int shift = simd_data(desc); \ - TYPE *d = vd, *n = vn; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = deposit64(d[i], 0, sizeof(TYPE) * 8 - shift, n[i] >> shift); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_SRI(gvec_sri_b, uint8_t) -DO_SRI(gvec_sri_h, uint16_t) -DO_SRI(gvec_sri_s, uint32_t) -DO_SRI(gvec_sri_d, uint64_t) - -#undef DO_SRI - -#define DO_SLI(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - int shift = simd_data(desc); \ - TYPE *d = vd, *n = vn; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = deposit64(d[i], shift, sizeof(TYPE) * 8 - shift, n[i]); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ -} - -DO_SLI(gvec_sli_b, uint8_t) -DO_SLI(gvec_sli_h, uint16_t) -DO_SLI(gvec_sli_s, uint32_t) -DO_SLI(gvec_sli_d, uint64_t) - -#undef DO_SLI - -/* - * Convert float16 to float32, raising no exceptions and - * preserving exceptional values, including SNaN. - * This is effectively an unpack+repack operation. - */ -static float32 float16_to_float32_by_bits(uint32_t f16, bool fz16) -{ - const int f16_bias = 15; - const int f32_bias = 127; - uint32_t sign = extract32(f16, 15, 1); - uint32_t exp = extract32(f16, 10, 5); - uint32_t frac = extract32(f16, 0, 10); - - if (exp == 0x1f) { - /* Inf or NaN */ - exp = 0xff; - } else if (exp == 0) { - /* Zero or denormal. */ - if (frac != 0) { - if (fz16) { - frac = 0; - } else { - /* - * Denormal; these are all normal float32. - * Shift the fraction so that the msb is at bit 11, - * then remove bit 11 as the implicit bit of the - * normalized float32. Note that we still go through - * the shift for normal numbers below, to put the - * float32 fraction at the right place. - */ - int shift = clz32(frac) - 21; - frac = (frac << shift) & 0x3ff; - exp = f32_bias - f16_bias - shift + 1; - } - } - } else { - /* Normal number; adjust the bias. */ - exp += f32_bias - f16_bias; - } - sign <<= 31; - exp <<= 23; - frac <<= 23 - 10; - - return sign | exp | frac; -} - -static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2) -{ - /* - * Branchless load of u32[0], u64[0], u32[1], or u64[1]. - * Load the 2nd qword iff is_q & is_2. - * Shift to the 2nd dword iff !is_q & is_2. - * For !is_q & !is_2, the upper bits of the result are garbage. - */ - return ptr[is_q & is_2] >> ((is_2 & ~is_q) << 5); -} - -/* - * Note that FMLAL requires oprsz == 8 or oprsz == 16, - * as there is not yet SVE versions that might use blocking. - */ - -static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, - uint32_t desc, bool fz16) -{ - intptr_t i, oprsz = simd_oprsz(desc); - int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); - int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - int is_q = oprsz == 16; - uint64_t n_4, m_4; - - /* Pre-load all of the f16 data, avoiding overlap issues. */ - n_4 = load4_f16(vn, is_q, is_2); - m_4 = load4_f16(vm, is_q, is_2); - - /* Negate all inputs for FMLSL at once. */ - if (is_s) { - n_4 ^= 0x8000800080008000ull; - } - - for (i = 0; i < oprsz / 4; i++) { - float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); - float32 m_1 = float16_to_float32_by_bits(m_4 >> (i * 16), fz16); - d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); - } - clear_tail(d, oprsz, simd_maxsz(desc)); -} - -void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) -{ - CPUARMState *env = venv; - do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); -} - -void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) -{ - CPUARMState *env = venv; - do_fmlal(vd, vn, vm, &env->vfp.fp_status, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); -} - -void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, - void *venv, uint32_t desc) -{ - intptr_t i, oprsz = simd_oprsz(desc); - uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; - intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - CPUARMState *env = venv; - float_status *status = &env->vfp.fp_status; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); - - for (i = 0; i < oprsz; i += sizeof(float32)) { - float16 nn_16 = *(float16 *)(vn + H1_2(i + sel)) ^ negn; - float16 mm_16 = *(float16 *)(vm + H1_2(i + sel)); - float32 nn = float16_to_float32_by_bits(nn_16, fz16); - float32 mm = float16_to_float32_by_bits(mm_16, fz16); - float32 aa = *(float32 *)(va + H1_4(i)); - - *(float32 *)(vd + H1_4(i)) = float32_muladd(nn, mm, aa, 0, status); - } -} - -static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, - uint32_t desc, bool fz16) -{ - intptr_t i, oprsz = simd_oprsz(desc); - int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); - int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); - int index = extract32(desc, SIMD_DATA_SHIFT + 2, 3); - int is_q = oprsz == 16; - uint64_t n_4; - float32 m_1; - - /* Pre-load all of the f16 data, avoiding overlap issues. */ - n_4 = load4_f16(vn, is_q, is_2); - - /* Negate all inputs for FMLSL at once. */ - if (is_s) { - n_4 ^= 0x8000800080008000ull; - } - - m_1 = float16_to_float32_by_bits(((float16 *)vm)[H2(index)], fz16); - - for (i = 0; i < oprsz / 4; i++) { - float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); - d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); - } - clear_tail(d, oprsz, simd_maxsz(desc)); -} - -void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) -{ - CPUARMState *env = venv; - do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); -} - -void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, - void *venv, uint32_t desc) -{ - CPUARMState *env = venv; - do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc, - get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); -} - -void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, - void *venv, uint32_t desc) -{ - intptr_t i, j, oprsz = simd_oprsz(desc); - uint16_t negn = extract32(desc, SIMD_DATA_SHIFT, 1) << 15; - intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); - CPUARMState *env = venv; - float_status *status = &env->vfp.fp_status; - bool fz16 = get_flush_inputs_to_zero(&env->vfp.fp_status_f16); - - for (i = 0; i < oprsz; i += 16) { - float16 mm_16 = *(float16 *)(vm + i + idx); - float32 mm = float16_to_float32_by_bits(mm_16, fz16); - - for (j = 0; j < 16; j += sizeof(float32)) { - float16 nn_16 = *(float16 *)(vn + H1_2(i + j + sel)) ^ negn; - float32 nn = float16_to_float32_by_bits(nn_16, fz16); - float32 aa = *(float32 *)(va + H1_4(i + j)); - - *(float32 *)(vd + H1_4(i + j)) = - float32_muladd(nn, mm, aa, 0, status); - } - } -} - -void HELPER(gvec_sshl_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int8_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - int8_t mm = m[i]; - int8_t nn = n[i]; - int8_t res = 0; - if (mm >= 0) { - if (mm < 8) { - res = nn << mm; - } - } else { - res = nn >> (mm > -8 ? -mm : 7); - } - d[i] = res; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_sshl_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 2; ++i) { - int8_t mm = m[i]; /* only 8 bits of shift are significant */ - int16_t nn = n[i]; - int16_t res = 0; - if (mm >= 0) { - if (mm < 16) { - res = nn << mm; - } - } else { - res = nn >> (mm > -16 ? -mm : 15); - } - d[i] = res; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_ushl_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint8_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - int8_t mm = m[i]; - uint8_t nn = n[i]; - uint8_t res = 0; - if (mm >= 0) { - if (mm < 8) { - res = nn << mm; - } - } else { - if (mm > -8) { - res = nn >> -mm; - } - } - d[i] = res; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_ushl_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint16_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 2; ++i) { - int8_t mm = m[i]; /* only 8 bits of shift are significant */ - uint16_t nn = n[i]; - uint16_t res = 0; - if (mm >= 0) { - if (mm < 16) { - res = nn << mm; - } - } else { - if (mm > -16) { - res = nn >> -mm; - } - } - d[i] = res; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -/* - * 8x8->8 polynomial multiply. - * - * Polynomial multiplication is like integer multiplication except the - * partial products are XORed, not added. - * - * TODO: expose this as a generic vector operation, as it is a common - * crypto building block. - */ -void HELPER(gvec_pmul_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - uint64_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 8; ++i) { - uint64_t nn = n[i]; - uint64_t mm = m[i]; - uint64_t rr = 0; - - for (j = 0; j < 8; ++j) { - uint64_t mask = (nn & 0x0101010101010101ull) * 0xff; - rr ^= mm & mask; - mm = (mm << 1) & 0xfefefefefefefefeull; - nn >>= 1; - } - d[i] = rr; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -/* - * 64x64->128 polynomial multiply. - * Because of the lanes are not accessed in strict columns, - * this probably cannot be turned into a generic helper. - */ -void HELPER(gvec_pmull_q)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - intptr_t hi = simd_data(desc); - uint64_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 8; i += 2) { - uint64_t nn = n[i + hi]; - uint64_t mm = m[i + hi]; - uint64_t rhi = 0; - uint64_t rlo = 0; - - /* Bit 0 can only influence the low 64-bit result. */ - if (nn & 1) { - rlo = mm; - } - - for (j = 1; j < 64; ++j) { - uint64_t mask = -((nn >> j) & 1); - rlo ^= (mm << j) & mask; - rhi ^= (mm >> (64 - j)) & mask; - } - d[i] = rlo; - d[i + 1] = rhi; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -/* - * 8x8->16 polynomial multiply. - * - * The byte inputs are expanded to (or extracted from) half-words. - * Note that neon and sve2 get the inputs from different positions. - * This allows 4 bytes to be processed in parallel with uint64_t. - */ - -static uint64_t expand_byte_to_half(uint64_t x) -{ - return (x & 0x000000ff) - | ((x & 0x0000ff00) << 8) - | ((x & 0x00ff0000) << 16) - | ((x & 0xff000000) << 24); -} - -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)); - nn >>= 32; - mm >>= 32; - d[1] = pmull_h(expand_byte_to_half(nn), expand_byte_to_half(mm)); - - clear_tail(d, 16, simd_maxsz(desc)); -} - -#ifdef TARGET_AARCH64 -void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - int shift = simd_data(desc) * 8; - intptr_t i, opr_sz = simd_oprsz(desc); - uint64_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 8; ++i) { - uint64_t nn = (n[i] >> shift) & 0x00ff00ff00ff00ffull; - uint64_t mm = (m[i] >> shift) & 0x00ff00ff00ff00ffull; - - d[i] = pmull_h(nn, mm); - } -} - -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)); - intptr_t i, opr_sz = simd_oprsz(desc); - uint32_t *n = vn, *m = vm; - uint64_t *d = vd; - - for (i = 0; i < opr_sz / 8; ++i) { - d[i] = pmull_d(n[2 * i + sel], m[2 * i + sel]); - } -} -#endif - -#define DO_CMP0(NAME, TYPE, OP) \ -void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ -{ \ - intptr_t i, opr_sz = simd_oprsz(desc); \ - for (i = 0; i < opr_sz; i += sizeof(TYPE)) { \ - TYPE nn = *(TYPE *)(vn + i); \ - *(TYPE *)(vd + i) = -(nn OP 0); \ - } \ - clear_tail(vd, opr_sz, simd_maxsz(desc)); \ -} - -DO_CMP0(gvec_ceq0_b, int8_t, ==) -DO_CMP0(gvec_clt0_b, int8_t, <) -DO_CMP0(gvec_cle0_b, int8_t, <=) -DO_CMP0(gvec_cgt0_b, int8_t, >) -DO_CMP0(gvec_cge0_b, int8_t, >=) - -DO_CMP0(gvec_ceq0_h, int16_t, ==) -DO_CMP0(gvec_clt0_h, int16_t, <) -DO_CMP0(gvec_cle0_h, int16_t, <=) -DO_CMP0(gvec_cgt0_h, int16_t, >) -DO_CMP0(gvec_cge0_h, int16_t, >=) - -#undef DO_CMP0 - -#define DO_ABD(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ -{ \ - intptr_t i, opr_sz = simd_oprsz(desc); \ - TYPE *d = vd, *n = vn, *m = vm; \ - \ - for (i = 0; i < opr_sz / sizeof(TYPE); ++i) { \ - d[i] = n[i] < m[i] ? m[i] - n[i] : n[i] - m[i]; \ - } \ - clear_tail(d, opr_sz, simd_maxsz(desc)); \ -} - -DO_ABD(gvec_sabd_b, int8_t) -DO_ABD(gvec_sabd_h, int16_t) -DO_ABD(gvec_sabd_s, int32_t) -DO_ABD(gvec_sabd_d, int64_t) - -DO_ABD(gvec_uabd_b, uint8_t) -DO_ABD(gvec_uabd_h, uint16_t) -DO_ABD(gvec_uabd_s, uint32_t) -DO_ABD(gvec_uabd_d, uint64_t) - -#undef DO_ABD - -#define DO_ABA(NAME, TYPE) \ -void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ -{ \ - intptr_t i, opr_sz = simd_oprsz(desc); \ - TYPE *d = vd, *n = vn, *m = vm; \ - \ - for (i = 0; i < opr_sz / sizeof(TYPE); ++i) { \ - d[i] += n[i] < m[i] ? m[i] - n[i] : n[i] - m[i]; \ - } \ - clear_tail(d, opr_sz, simd_maxsz(desc)); \ -} - -DO_ABA(gvec_saba_b, int8_t) -DO_ABA(gvec_saba_h, int16_t) -DO_ABA(gvec_saba_s, int32_t) -DO_ABA(gvec_saba_d, int64_t) - -DO_ABA(gvec_uaba_b, uint8_t) -DO_ABA(gvec_uaba_h, uint16_t) -DO_ABA(gvec_uaba_s, uint32_t) -DO_ABA(gvec_uaba_d, uint64_t) - -#undef DO_ABA - -#define DO_NEON_PAIRWISE(NAME, OP) \ - void HELPER(NAME##s)(void *vd, void *vn, void *vm, \ - void *stat, uint32_t oprsz) \ - { \ - float_status *fpst = stat; \ - float32 *d = vd; \ - float32 *n = vn; \ - float32 *m = vm; \ - float32 r0, r1; \ - \ - /* Read all inputs before writing outputs in case vm == vd */ \ - r0 = float32_##OP(n[H4(0)], n[H4(1)], fpst); \ - r1 = float32_##OP(m[H4(0)], m[H4(1)], fpst); \ - \ - d[H4(0)] = r0; \ - d[H4(1)] = r1; \ - } \ - \ - void HELPER(NAME##h)(void *vd, void *vn, void *vm, \ - void *stat, uint32_t oprsz) \ - { \ - float_status *fpst = stat; \ - float16 *d = vd; \ - float16 *n = vn; \ - float16 *m = vm; \ - float16 r0, r1, r2, r3; \ - \ - /* Read all inputs before writing outputs in case vm == vd */ \ - r0 = float16_##OP(n[H2(0)], n[H2(1)], fpst); \ - r1 = float16_##OP(n[H2(2)], n[H2(3)], fpst); \ - r2 = float16_##OP(m[H2(0)], m[H2(1)], fpst); \ - r3 = float16_##OP(m[H2(2)], m[H2(3)], fpst); \ - \ - d[H2(0)] = r0; \ - d[H2(1)] = r1; \ - d[H2(2)] = r2; \ - d[H2(3)] = r3; \ - } - -DO_NEON_PAIRWISE(neon_padd, add) -DO_NEON_PAIRWISE(neon_pmax, max) -DO_NEON_PAIRWISE(neon_pmin, min) - -#undef DO_NEON_PAIRWISE - -#define DO_VCVT_FIXED(NAME, FUNC, TYPE) \ - void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ - { \ - intptr_t i, oprsz = simd_oprsz(desc); \ - int shift = simd_data(desc); \ - TYPE *d = vd, *n = vn; \ - float_status *fpst = stat; \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = FUNC(n[i], shift, fpst); \ - } \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ - } - -DO_VCVT_FIXED(gvec_vcvt_sf, helper_vfp_sltos, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_uf, helper_vfp_ultos, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_fs, helper_vfp_tosls_round_to_zero, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_fu, helper_vfp_touls_round_to_zero, uint32_t) -DO_VCVT_FIXED(gvec_vcvt_sh, helper_vfp_shtoh, uint16_t) -DO_VCVT_FIXED(gvec_vcvt_uh, helper_vfp_uhtoh, uint16_t) -DO_VCVT_FIXED(gvec_vcvt_hs, helper_vfp_toshh_round_to_zero, uint16_t) -DO_VCVT_FIXED(gvec_vcvt_hu, helper_vfp_touhh_round_to_zero, uint16_t) - -#undef DO_VCVT_FIXED - -#define DO_VCVT_RMODE(NAME, FUNC, TYPE) \ - void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ - { \ - float_status *fpst = stat; \ - intptr_t i, oprsz = simd_oprsz(desc); \ - uint32_t rmode = simd_data(desc); \ - uint32_t prev_rmode = get_float_rounding_mode(fpst); \ - TYPE *d = vd, *n = vn; \ - set_float_rounding_mode(rmode, fpst); \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = FUNC(n[i], 0, fpst); \ - } \ - set_float_rounding_mode(prev_rmode, fpst); \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ - } - -DO_VCVT_RMODE(gvec_vcvt_rm_ss, helper_vfp_tosls, uint32_t) -DO_VCVT_RMODE(gvec_vcvt_rm_us, helper_vfp_touls, uint32_t) -DO_VCVT_RMODE(gvec_vcvt_rm_sh, helper_vfp_toshh, uint16_t) -DO_VCVT_RMODE(gvec_vcvt_rm_uh, helper_vfp_touhh, uint16_t) - -#undef DO_VCVT_RMODE - -#define DO_VRINT_RMODE(NAME, FUNC, TYPE) \ - void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ - { \ - float_status *fpst = stat; \ - intptr_t i, oprsz = simd_oprsz(desc); \ - uint32_t rmode = simd_data(desc); \ - uint32_t prev_rmode = get_float_rounding_mode(fpst); \ - TYPE *d = vd, *n = vn; \ - set_float_rounding_mode(rmode, fpst); \ - for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ - d[i] = FUNC(n[i], fpst); \ - } \ - set_float_rounding_mode(prev_rmode, fpst); \ - clear_tail(d, oprsz, simd_maxsz(desc)); \ - } - -DO_VRINT_RMODE(gvec_vrint_rm_h, helper_rinth, uint16_t) -DO_VRINT_RMODE(gvec_vrint_rm_s, helper_rints, uint32_t) - -#undef DO_VRINT_RMODE - -#ifdef TARGET_AARCH64 -void HELPER(simd_tblx)(void *vd, void *vm, void *venv, uint32_t desc) -{ - const uint8_t *indices = vm; - CPUARMState *env = venv; - size_t oprsz = simd_oprsz(desc); - uint32_t rn = extract32(desc, SIMD_DATA_SHIFT, 5); - bool is_tbx = extract32(desc, SIMD_DATA_SHIFT + 5, 1); - uint32_t table_len = desc >> (SIMD_DATA_SHIFT + 6); - union { - uint8_t b[16]; - uint64_t d[2]; - } result; - - /* - * We must construct the final result in a temp, lest the output - * overlaps the input table. For TBL, begin with zero; for TBX, - * begin with the original register contents. Note that we always - * copy 16 bytes here to avoid an extra branch; clearing the high - * bits of the register for oprsz == 8 is handled below. - */ - if (is_tbx) { - memcpy(&result, vd, 16); - } else { - memset(&result, 0, 16); - } - - for (size_t i = 0; i < oprsz; ++i) { - uint32_t index = indices[H1(i)]; - - if (index < table_len) { - /* - * Convert index (a byte offset into the virtual table - * which is a series of 128-bit vectors concatenated) - * into the correct register element, bearing in mind - * that the table can wrap around from V31 to V0. - */ - const uint8_t *table = (const uint8_t *) - aa64_vfp_qreg(env, (rn + (index >> 4)) % 32); - result.b[H1(i)] = table[H1(index % 16)]; - } - } - - memcpy(vd, &result, 16); - clear_tail(vd, oprsz, simd_maxsz(desc)); -} -#endif - -/* - * NxN -> N highpart multiply - * - * TODO: expose this as a generic vector operation. - */ - -void HELPER(gvec_smulh_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int8_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - d[i] = ((int32_t)n[i] * m[i]) >> 8; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_smulh_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int16_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = ((int32_t)n[i] * m[i]) >> 16; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_smulh_s)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - int32_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = ((int64_t)n[i] * m[i]) >> 32; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_smulh_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint64_t *d = vd, *n = vn, *m = vm; - uint64_t discard; - - for (i = 0; i < opr_sz / 8; ++i) { - muls64(&discard, &d[i], n[i], m[i]); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_umulh_b)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint8_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - d[i] = ((uint32_t)n[i] * m[i]) >> 8; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_umulh_h)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint16_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 2; ++i) { - d[i] = ((uint32_t)n[i] * m[i]) >> 16; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_umulh_s)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint32_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = ((uint64_t)n[i] * m[i]) >> 32; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_umulh_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - uint64_t *d = vd, *n = vn, *m = vm; - uint64_t discard; - - for (i = 0; i < opr_sz / 8; ++i) { - mulu64(&discard, &d[i], n[i], m[i]); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_xar_d)(void *vd, void *vn, void *vm, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc) / 8; - int shr = simd_data(desc); - uint64_t *d = vd, *n = vn, *m = vm; - - for (i = 0; i < opr_sz; ++i) { - d[i] = ror64(n[i] ^ m[i], shr); - } - clear_tail(d, opr_sz * 8, simd_maxsz(desc)); -} - -/* - * Integer matrix-multiply accumulate - */ - -static uint32_t do_smmla_b(uint32_t sum, void *vn, void *vm) -{ - int8_t *n = vn, *m = vm; - - for (intptr_t k = 0; k < 8; ++k) { - sum += n[H1(k)] * m[H1(k)]; - } - return sum; -} - -static uint32_t do_ummla_b(uint32_t sum, void *vn, void *vm) -{ - uint8_t *n = vn, *m = vm; - - for (intptr_t k = 0; k < 8; ++k) { - sum += n[H1(k)] * m[H1(k)]; - } - return sum; -} - -static uint32_t do_usmmla_b(uint32_t sum, void *vn, void *vm) -{ - uint8_t *n = vn; - int8_t *m = vm; - - for (intptr_t k = 0; k < 8; ++k) { - sum += n[H1(k)] * m[H1(k)]; - } - return sum; -} - -static void do_mmla_b(void *vd, void *vn, void *vm, void *va, uint32_t desc, - uint32_t (*inner_loop)(uint32_t, void *, void *)) -{ - intptr_t seg, opr_sz = simd_oprsz(desc); - - for (seg = 0; seg < opr_sz; seg += 16) { - uint32_t *d = vd + seg; - uint32_t *a = va + seg; - uint32_t sum0, sum1, sum2, sum3; - - /* - * Process the entire segment at once, writing back the - * results only after we've consumed all of the inputs. - * - * Key to indices by column: - * i j i j - */ - sum0 = a[H4(0 + 0)]; - sum0 = inner_loop(sum0, vn + seg + 0, vm + seg + 0); - sum1 = a[H4(0 + 1)]; - sum1 = inner_loop(sum1, vn + seg + 0, vm + seg + 8); - sum2 = a[H4(2 + 0)]; - sum2 = inner_loop(sum2, vn + seg + 8, vm + seg + 0); - sum3 = a[H4(2 + 1)]; - sum3 = inner_loop(sum3, vn + seg + 8, vm + seg + 8); - - d[H4(0)] = sum0; - d[H4(1)] = sum1; - d[H4(2)] = sum2; - d[H4(3)] = sum3; - } - clear_tail(vd, opr_sz, simd_maxsz(desc)); -} - -#define DO_MMLA_B(NAME, INNER) \ - void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ - { do_mmla_b(vd, vn, vm, va, desc, INNER); } - -DO_MMLA_B(gvec_smmla_b, do_smmla_b) -DO_MMLA_B(gvec_ummla_b, do_ummla_b) -DO_MMLA_B(gvec_usmmla_b, do_usmmla_b) - -/* - * BFloat16 Dot Product - */ - -float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2) -{ - /* FPCR is ignored for BFDOT and BFMMLA. */ - float_status bf_status = { - .tininess_before_rounding = float_tininess_before_rounding, - .float_rounding_mode = float_round_to_odd_inf, - .flush_to_zero = true, - .flush_inputs_to_zero = true, - .default_nan_mode = true, - }; - float32 t1, t2; - - /* - * Extract each BFloat16 from the element pair, and shift - * them such that they become float32. - */ - t1 = float32_mul(e1 << 16, e2 << 16, &bf_status); - t2 = float32_mul(e1 & 0xffff0000u, e2 & 0xffff0000u, &bf_status); - t1 = float32_add(t1, t2, &bf_status); - t1 = float32_add(sum, t1, &bf_status); - - return t1; -} - -void HELPER(gvec_bfdot)(void *vd, void *vn, void *vm, void *va, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - float32 *d = vd, *a = va; - uint32_t *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = bfdotadd(a[i], n[i], m[i]); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_bfdot_idx)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - intptr_t index = simd_data(desc); - intptr_t elements = opr_sz / 4; - intptr_t eltspersegment = MIN(16 / 4, elements); - float32 *d = vd, *a = va; - uint32_t *n = vn, *m = vm; - - for (i = 0; i < elements; i += eltspersegment) { - uint32_t m_idx = m[i + H4(index)]; - - for (j = i; j < i + eltspersegment; j++) { - d[j] = bfdotadd(a[j], n[j], m_idx); - } - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, uint32_t desc) -{ - intptr_t s, opr_sz = simd_oprsz(desc); - float32 *d = vd, *a = va; - uint32_t *n = vn, *m = vm; - - for (s = 0; s < opr_sz / 4; s += 4) { - float32 sum00, sum01, sum10, sum11; - - /* - * Process the entire segment at once, writing back the - * results only after we've consumed all of the inputs. - * - * Key to indicies by column: - * i j i k j k - */ - sum00 = a[s + H4(0 + 0)]; - sum00 = bfdotadd(sum00, n[s + H4(0 + 0)], m[s + H4(0 + 0)]); - sum00 = bfdotadd(sum00, n[s + H4(0 + 1)], m[s + H4(0 + 1)]); - - sum01 = a[s + H4(0 + 1)]; - sum01 = bfdotadd(sum01, n[s + H4(0 + 0)], m[s + H4(2 + 0)]); - sum01 = bfdotadd(sum01, n[s + H4(0 + 1)], m[s + H4(2 + 1)]); - - sum10 = a[s + H4(2 + 0)]; - sum10 = bfdotadd(sum10, n[s + H4(2 + 0)], m[s + H4(0 + 0)]); - sum10 = bfdotadd(sum10, n[s + H4(2 + 1)], m[s + H4(0 + 1)]); - - sum11 = a[s + H4(2 + 1)]; - sum11 = bfdotadd(sum11, n[s + H4(2 + 0)], m[s + H4(2 + 0)]); - sum11 = bfdotadd(sum11, n[s + H4(2 + 1)], m[s + H4(2 + 1)]); - - d[s + H4(0 + 0)] = sum00; - d[s + H4(0 + 1)] = sum01; - d[s + H4(2 + 0)] = sum10; - d[s + H4(2 + 1)] = sum11; - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, - void *stat, uint32_t desc) -{ - intptr_t i, opr_sz = simd_oprsz(desc); - intptr_t sel = simd_data(desc); - float32 *d = vd, *a = va; - bfloat16 *n = vn, *m = vm; - - for (i = 0; i < opr_sz / 4; ++i) { - float32 nn = n[H2(i * 2 + sel)] << 16; - float32 mm = m[H2(i * 2 + sel)] << 16; - d[H4(i)] = float32_muladd(nn, mm, a[H4(i)], 0, stat); - } - clear_tail(d, opr_sz, simd_maxsz(desc)); -} - -void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, - void *va, void *stat, uint32_t desc) -{ - intptr_t i, j, opr_sz = simd_oprsz(desc); - intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 1); - intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 1, 3); - intptr_t elements = opr_sz / 4; - intptr_t eltspersegment = MIN(16 / 4, elements); - float32 *d = vd, *a = va; - bfloat16 *n = vn, *m = vm; - - for (i = 0; i < elements; i += eltspersegment) { - float32 m_idx = m[H2(2 * i + index)] << 16; - - for (j = i; j < i + eltspersegment; j++) { - float32 n_j = n[H2(2 * j + sel)] << 16; - d[H4(j)] = float32_muladd(n_j, m_idx, a[H4(j)], 0, stat); - } - } - 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/vfp_helper.c b/target/arm/vfp_helper.c index 24e3d820a5..62638d2b1f 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" @@ -58,33 +59,7 @@ static inline int vfp_exceptbits_from_host(int host_bits) return target_bits; } -/* Convert vfp exception flags to target form. */ -static inline int vfp_exceptbits_to_host(int target_bits) -{ - int host_bits = 0; - - if (target_bits & 1) { - host_bits |= float_flag_invalid; - } - if (target_bits & 2) { - host_bits |= float_flag_divbyzero; - } - if (target_bits & 4) { - host_bits |= float_flag_overflow; - } - if (target_bits & 8) { - host_bits |= float_flag_underflow; - } - if (target_bits & 0x10) { - host_bits |= float_flag_inexact; - } - if (target_bits & 0x80) { - host_bits |= float_flag_input_denormal; - } - return host_bits; -} - -static uint32_t vfp_get_fpscr_from_host(CPUARMState *env) +static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { uint32_t i; @@ -98,14 +73,27 @@ static uint32_t vfp_get_fpscr_from_host(CPUARMState *env) return vfp_exceptbits_from_host(i); } -static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) +static void vfp_clear_float_status_exc_flags(CPUARMState *env) { - int i; - uint32_t changed = env->vfp.xregs[ARM_VFP_FPSCR]; + /* + * Clear out all the exception-flag information in the float_status + * values. The caller should have arranged for env->vfp.fpsr to + * be the architecturally up-to-date exception flag information first. + */ + set_float_exception_flags(0, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.fp_status_f16); + set_float_exception_flags(0, &env->vfp.standard_fp_status); + set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); +} + +static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) +{ + uint64_t changed = env->vfp.fpcr; changed ^= val; + changed &= mask; if (changed & (3 << 22)) { - i = (val >> 22) & 3; + int i = (val >> 22) & 3; switch (i) { case FPROUNDING_TIEEVEN: i = float_round_nearest_even; @@ -140,52 +128,56 @@ static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); } - - /* - * The exception flags are ORed together when we read fpscr so we - * only need to preserve the current state in one of our - * float_status values. - */ - i = vfp_exceptbits_to_host(val); - set_float_exception_flags(i, &env->vfp.fp_status); - set_float_exception_flags(0, &env->vfp.fp_status_f16); - set_float_exception_flags(0, &env->vfp.standard_fp_status); - set_float_exception_flags(0, &env->vfp.standard_fp_status_f16); } #else -static uint32_t vfp_get_fpscr_from_host(CPUARMState *env) +static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { return 0; } -static void vfp_set_fpscr_to_host(CPUARMState *env, uint32_t val) +static void vfp_clear_float_status_exc_flags(CPUARMState *env) +{ +} + +static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { } #endif -uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +uint32_t vfp_get_fpcr(CPUARMState *env) { - uint32_t i, fpscr; - - fpscr = env->vfp.xregs[ARM_VFP_FPSCR] - | (env->vfp.vec_len << 16) - | (env->vfp.vec_stride << 20); + uint32_t fpcr = env->vfp.fpcr + | (env->vfp.vec_len << 16) + | (env->vfp.vec_stride << 20); /* - * M-profile LTPSIZE overlaps A-profile Stride; whichever of the - * two is not applicable to this CPU will always be zero. + * M-profile LTPSIZE is the same bits [18:16] as A-profile Len; whichever + * of the two is not applicable to this CPU will always be zero. */ - fpscr |= env->v7m.ltpsize << 16; + fpcr |= env->v7m.ltpsize << 16; - fpscr |= vfp_get_fpscr_from_host(env); + return fpcr; +} + +uint32_t vfp_get_fpsr(CPUARMState *env) +{ + uint32_t fpsr = env->vfp.fpsr; + uint32_t i; + + fpsr |= vfp_get_fpsr_from_host(env); i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3]; - fpscr |= i ? FPCR_QC : 0; + fpsr |= i ? FPSR_QC : 0; + return fpsr; +} - return fpscr; +uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +{ + return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) | + (vfp_get_fpsr(env) & FPSCR_FPSR_MASK); } uint32_t vfp_get_fpscr(CPUARMState *env) @@ -193,56 +185,98 @@ uint32_t vfp_get_fpscr(CPUARMState *env) return HELPER(vfp_get_fpscr)(env); } -void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +void vfp_set_fpsr(CPUARMState *env, uint32_t val) { ARMCPU *cpu = env_archcpu(env); + if (arm_feature(env, ARM_FEATURE_NEON) || + cpu_isar_feature(aa32_mve, cpu)) { + /* + * The bit we set within vfp.qc[] is arbitrary; the array as a + * whole being zero/non-zero is what counts. + */ + env->vfp.qc[0] = val & FPSR_QC; + env->vfp.qc[1] = 0; + env->vfp.qc[2] = 0; + env->vfp.qc[3] = 0; + } + + /* + * NZCV lives only in env->vfp.fpsr. The cumulative exception flags + * IOC|DZC|OFC|UFC|IXC|IDC also live in env->vfp.fpsr, with possible + * extra pending exception information that hasn't yet been folded in + * living in the float_status values (for TCG). + * Since this FPSR write gives us the up to date values of the exception + * flags, we want to store into vfp.fpsr the NZCV and CEXC bits, zeroing + * anything else. We also need to clear out the float_status exception + * information so that the next vfp_get_fpsr does not fold in stale data. + */ + val &= FPSR_NZCV_MASK | FPSR_CEXC_MASK; + env->vfp.fpsr = val; + vfp_clear_float_status_exc_flags(env); +} + +static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) +{ + /* + * We only set FPCR bits defined by mask, and leave the others alone. + * We assume the mask is sensible (e.g. doesn't try to set only + * part of a field) + */ + ARMCPU *cpu = env_archcpu(env); + /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ if (!cpu_isar_feature(any_fp16, cpu)) { val &= ~FPCR_FZ16; } - vfp_set_fpscr_to_host(env, val); - - if (!arm_feature(env, ARM_FEATURE_M)) { - /* - * Short-vector length and stride; on M-profile these bits - * are used for different purposes. - * We can't make this conditional be "if MVFR0.FPShVec != 0", - * because in v7A no-short-vector-support cores still had to - * allow Stride/Len to be written with the only effect that - * some insns are required to UNDEF if the guest sets them. - */ - env->vfp.vec_len = extract32(val, 16, 3); - env->vfp.vec_stride = extract32(val, 20, 2); - } else if (cpu_isar_feature(aa32_mve, cpu)) { - env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT, - FPCR_LTPSIZE_LENGTH); + if (!cpu_isar_feature(aa64_ebf16, cpu)) { + val &= ~FPCR_EBF; } - if (arm_feature(env, ARM_FEATURE_NEON) || - cpu_isar_feature(aa32_mve, cpu)) { - /* - * The bit we set within fpscr_q is arbitrary; the register as a - * whole being zero/non-zero is what counts. - * TODO: M-profile MVE also has a QC bit. - */ - env->vfp.qc[0] = val & FPCR_QC; - env->vfp.qc[1] = 0; - env->vfp.qc[2] = 0; - env->vfp.qc[3] = 0; + vfp_set_fpcr_to_host(env, val, mask); + + if (mask & (FPCR_LEN_MASK | FPCR_STRIDE_MASK)) { + if (!arm_feature(env, ARM_FEATURE_M)) { + /* + * Short-vector length and stride; on M-profile these bits + * are used for different purposes. + * We can't make this conditional be "if MVFR0.FPShVec != 0", + * because in v7A no-short-vector-support cores still had to + * allow Stride/Len to be written with the only effect that + * some insns are required to UNDEF if the guest sets them. + */ + env->vfp.vec_len = extract32(val, 16, 3); + env->vfp.vec_stride = extract32(val, 20, 2); + } else if (cpu_isar_feature(aa32_mve, cpu)) { + env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT, + FPCR_LTPSIZE_LENGTH); + } } /* * We don't implement trapped exception handling, so the * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) * - * The exception flags IOC|DZC|OFC|UFC|IXC|IDC are stored in - * fp_status; QC, Len and Stride are stored separately earlier. - * Clear out all of those and the RES0 bits: only NZCV, AHP, DN, - * FZ, RMode and FZ16 are kept in vfp.xregs[FPSCR]. + * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF + * and FZ16. Len, Stride and LTPSIZE we just handled. Store those bits + * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI + * bits. */ - env->vfp.xregs[ARM_VFP_FPSCR] = val & 0xf7c80000; + val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | FPCR_EBF; + env->vfp.fpcr &= ~mask; + env->vfp.fpcr |= val; +} + +void vfp_set_fpcr(CPUARMState *env, uint32_t val) +{ + vfp_set_fpcr_masked(env, val, MAKE_64BIT_MASK(0, 32)); +} + +void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +{ + vfp_set_fpcr_masked(env, val, FPSCR_FPCR_MASK); + vfp_set_fpsr(env, val & FPSCR_FPSR_MASK); } void vfp_set_fpscr(CPUARMState *env, uint32_t val) @@ -280,36 +314,6 @@ VFP_BINOP(minnum) VFP_BINOP(maxnum) #undef VFP_BINOP -dh_ctype_f16 VFP_HELPER(neg, h)(dh_ctype_f16 a) -{ - return float16_chs(a); -} - -float32 VFP_HELPER(neg, s)(float32 a) -{ - return float32_chs(a); -} - -float64 VFP_HELPER(neg, d)(float64 a) -{ - return float64_chs(a); -} - -dh_ctype_f16 VFP_HELPER(abs, h)(dh_ctype_f16 a) -{ - return float16_abs(a); -} - -float32 VFP_HELPER(abs, s)(float32 a) -{ - return float32_abs(a); -} - -float64 VFP_HELPER(abs, d)(float64 a) -{ - return float64_abs(a); -} - dh_ctype_f16 VFP_HELPER(sqrt, h)(dh_ctype_f16 a, CPUARMState *env) { return float16_sqrt(a, &env->vfp.fp_status_f16); @@ -344,8 +348,7 @@ static void softfloat_to_vfp_compare(CPUARMState *env, FloatRelation cmp) default: g_assert_not_reached(); } - env->vfp.xregs[ARM_VFP_FPSCR] = - deposit32(env->vfp.xregs[ARM_VFP_FPSCR], 28, 4, flags); + env->vfp.fpsr = deposit64(env->vfp.fpsr, 28, 4, flags); /* NZCV */ } /* XXX: check quiet/signaling case */ @@ -1104,33 +1107,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,69 +1123,22 @@ 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 frac, e_old, e_new; + bool inexact; - 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; - } else { - int true_exp = exp - 1023; - int shift = true_exp - 52; + /* Normal inexact, denormal with flush-to-zero, or overflow or NaN */ + inexact = e_new & (float_flag_inexact | + float_flag_input_denormal | + float_flag_invalid); - /* 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; - } - } + /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ + inexact |= value == float64_chs(float64_zero); /* Pack the result and the env->ZF representation of Z together. */ return deposit64(frac, 32, 32, inexact); @@ -1214,8 +1151,7 @@ uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) uint32_t z = (pair >> 32) == 0; /* Store Z, clear NCV, in FPSCR.NZCV. */ - env->vfp.xregs[ARM_VFP_FPSCR] - = (env->vfp.xregs[ARM_VFP_FPSCR] & ~CPSR_NZCV) | (z * CPSR_Z); + env->vfp.fpsr = (env->vfp.fpsr & ~FPSR_NZCV_MASK) | (z * FPSR_Z); return result; } diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 7ef4e7c679..93c2f470d0 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -31,6 +31,7 @@ #define TARGET_PAGE_BITS 8 #define TARGET_PHYS_ADDR_SPACE_BITS 24 #define TARGET_VIRT_ADDR_SPACE_BITS 24 -#define NB_MMU_MODES 2 + +#define TCG_GUEST_DEFAULT_MO 0 #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 c7295b488d..3132842d56 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) { @@ -41,40 +43,39 @@ static vaddr avr_cpu_get_pc(CPUState *cs) static bool avr_cpu_has_work(CPUState *cs) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - 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(tb) / 2; /* internally PC points to words */ + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu_env(cs)->pc_w = tb->pc / 2; /* internally PC points to words */ } static void avr_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - - env->pc_w = data[0]; + cpu_env(cs)->pc_w = data[0]; } -static void avr_cpu_reset(DeviceState *ds) +static void avr_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *cs = CPU(ds); + 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, type); + } env->pc_w = 0; env->sregI = 1; @@ -91,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; @@ -143,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"); @@ -209,11 +205,12 @@ 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, + .cpu_exec_halt = avr_cpu_has_work, .tlb_fill = avr_cpu_tlb_fill, .do_interrupt = avr_cpu_do_interrupt, }; @@ -223,13 +220,19 @@ 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->get_pc = avr_cpu_get_pc; @@ -239,7 +242,6 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) 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; } @@ -270,8 +272,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); @@ -299,8 +300,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); @@ -329,8 +329,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); @@ -356,21 +355,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, \ @@ -383,6 +367,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 96419c0c2b..4725535102 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -28,12 +28,8 @@ #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 - /* * AVR has two memory spaces, data & code. * e.g. both have 0 address @@ -144,12 +140,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,17 +182,8 @@ 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); enum { @@ -190,8 +191,8 @@ enum { 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; @@ -215,8 +216,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 @@ -224,7 +224,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..aea71282a5 100644 --- a/target/avr/gdbstub.c +++ b/target/avr/gdbstub.c @@ -19,12 +19,12 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "cpu.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 +53,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) { @@ -70,13 +69,13 @@ int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* SP */ if (n == 33) { - env->sp = lduw_p(mem_buf); + env->sp = lduw_le_p(mem_buf); return 2; } /* PC */ if (n == 34) { - env->pc_w = ldl_p(mem_buf) / 2; + env->pc_w = ldl_le_p(mem_buf) / 2; return 4; } diff --git a/target/avr/helper.c b/target/avr/helper.c index 156dde4e92..345708a1b3 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -20,16 +20,18 @@ #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/page-protection.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) { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; + CPUAVRState *env = cpu_env(cs); /* * We cannot separate a skip from the next instruction, @@ -51,7 +53,7 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } 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); avr_cpu_do_interrupt(cs); @@ -67,8 +69,7 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 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; @@ -78,7 +79,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)) { @@ -142,9 +143,7 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (probe) { page_size = 1; } else { - AVRCPU *cpu = AVR_CPU(cs); - CPUAVRState *env = &cpu->env; - env->fullacc = 1; + cpu_env(cs)->fullacc = 1; cpu_loop_exit_restore(cs, retaddr); } } 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 2bed56f135..2d51892115 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -24,12 +24,15 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/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 +110,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 +119,6 @@ struct DisasContext { TCGv skip_var0; TCGv skip_var1; TCGCond skip_cond; - bool free_skip_var0; }; void avr_cpu_tcg_init(void) @@ -129,25 +126,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 @@ -175,7 +172,7 @@ static int to_regs_00_30_by_two(DisasContext *ctx, int indx) static uint16_t next_word(DisasContext *ctx) { - return cpu_lduw_code(ctx->env, ctx->npc++ * 2); + return translator_lduw(ctx->env, &ctx->base, ctx->npc++ * 2); } static int append_16(DisasContext *ctx, int x) @@ -186,7 +183,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 +224,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 +238,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 +255,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 +269,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 +306,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 +330,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 +368,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 +392,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 +403,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 +416,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 +428,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 +447,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 +456,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 +477,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 +515,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 +537,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 +576,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 +623,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 +630,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 +640,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 +653,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 +722,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 +752,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 +781,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 +811,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 +846,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 +879,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 +933,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 +974,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 +1201,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 +1214,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 +1229,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 +1241,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 +1251,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 +1264,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 +1279,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 +1291,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 +1309,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 +1493,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 +1584,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 +1618,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 +1630,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 +1641,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 +1678,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 +1689,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 +1699,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 +1740,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 +1752,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 +1762,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 +1788,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 +1818,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 +1829,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 +1840,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 +1874,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 +1885,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 +1895,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 +1930,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 +1942,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 +1952,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 +1982,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 +1999,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 +2016,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 +2048,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 +2061,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 +2074,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 +2127,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 +2140,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 +2210,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 +2239,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 +2269,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 +2299,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 +2357,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 +2381,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 +2398,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 +2408,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 +2423,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 +2455,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 +2550,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 +2576,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 +2588,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 +2607,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 +2649,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 +2656,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 +2702,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 +2713,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; } @@ -3033,24 +2786,16 @@ static void avr_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void avr_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps avr_tr_ops = { .init_disas_context = avr_tr_init_disas_context, .tb_start = avr_tr_tb_start, .insn_start = avr_tr_insn_start, .translate_insn = avr_tr_translate_insn, .tb_stop = avr_tr_tb_stop, - .disas_log = avr_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = { }; translator_loop(cs, tb, max_insns, pc, host_pc, &avr_tr_ops, &dc.base); diff --git a/target/cris/Kconfig b/target/cris/Kconfig deleted file mode 100644 index 3fdc309fbb..0000000000 --- a/target/cris/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -config CRIS - bool diff --git a/target/cris/cpu-param.h b/target/cris/cpu-param.h deleted file mode 100644 index 12ec22d8df..0000000000 --- a/target/cris/cpu-param.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * CRIS cpu parameters for qemu. - * - * Copyright (c) 2007 AXIS Communications AB - * SPDX-License-Identifier: LGPL-2.0+ - */ - -#ifndef CRIS_CPU_PARAM_H -#define CRIS_CPU_PARAM_H - -#define TARGET_LONG_BITS 32 -#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 deleted file mode 100644 index 71e8af0e70..0000000000 --- a/target/cris/cpu-qom.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * QEMU CRIS CPU - * - * Copyright (c) 2012 SUSE LINUX Products GmbH - * - * 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 QEMU_CRIS_CPU_QOM_H -#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; -}; - - -#endif diff --git a/target/cris/cpu.c b/target/cris/cpu.c deleted file mode 100644 index fb05dc6f9a..0000000000 --- a/target/cris/cpu.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * QEMU CRIS CPU - * - * Copyright (c) 2008 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * Copyright (c) 2012 SUSE LINUX Products GmbH - * - * 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/qemu-print.h" -#include "cpu.h" -#include "mmu.h" - - -static void cris_cpu_set_pc(CPUState *cs, vaddr value) -{ - CRISCPU *cpu = CRIS_CPU(cs); - - 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) -{ - CPUState *s = CPU(dev); - CRISCPU *cpu = CRIS_CPU(s); - CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(cpu); - CPUCRISState *env = &cpu->env; - uint32_t vr; - - ccc->parent_reset(dev); - - vr = env->pregs[PR_VR]; - memset(env, 0, offsetof(CPUCRISState, end_reset_fields)); - env->pregs[PR_VR] = vr; - -#if defined(CONFIG_USER_ONLY) - /* start in user mode with interrupts enabled. */ - env->pregs[PR_CCS] |= U_FLAG | I_FLAG | P_FLAG; -#else - cris_mmu_init(env); - env->pregs[PR_CCS] = 0; -#endif -} - -static ObjectClass *cris_cpu_class_by_name(const char *cpu_model) -{ - ObjectClass *oc; - char *typename; - -#if defined(CONFIG_USER_ONLY) - if (strcasecmp(cpu_model, "any") == 0) { - return object_class_by_name(CRIS_CPU_TYPE_NAME("crisv32")); - } -#endif - - 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); - CRISCPUClass *ccc = CRIS_CPU_GET_CLASS(dev); - Error *local_err = NULL; - - cpu_exec_realizefn(cs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - - cpu_reset(cs); - qemu_init_vcpu(cs); - - ccc->parent_realize(dev, errp); -} - -#ifndef CONFIG_USER_ONLY -static void cris_cpu_set_irq(void *opaque, int irq, int level) -{ - CRISCPU *cpu = opaque; - CPUState *cs = CPU(cpu); - int type = irq == CRIS_CPU_IRQ ? CPU_INTERRUPT_HARD : CPU_INTERRUPT_NMI; - - if (irq == CRIS_CPU_IRQ) { - /* - * The PIC passes us the vector for the IRQ as the value it sends - * over the qemu_irq line - */ - cpu->env.interrupt_vector = level; - } - - if (level) { - cpu_interrupt(cs, type); - } else { - cpu_reset_interrupt(cs, type); - } -} -#endif - -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) { - info->mach = bfd_mach_cris_v0_v10; - info->print_insn = print_insn_crisv10; - } else { - info->mach = bfd_mach_cris_v32; - info->print_insn = print_insn_crisv32; - } -} - -static void cris_cpu_initfn(Object *obj) -{ - CRISCPU *cpu = CRIS_CPU(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 - /* IRQ and NMI lines. */ - qdev_init_gpio_in(DEVICE(cpu), cris_cpu_set_irq, 2); -#endif -} - -#ifndef CONFIG_USER_ONLY -#include "hw/core/sysemu-cpu-ops.h" - -static const struct SysemuCPUOps cris_sysemu_ops = { - .get_phys_page_debug = cris_cpu_get_phys_page_debug, -}; -#endif - -#include "hw/core/tcg-cpu-ops.h" - -static const struct 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, - .cpu_exec_interrupt = cris_cpu_exec_interrupt, - .do_interrupt = crisv10_cpu_do_interrupt, -#endif /* !CONFIG_USER_ONLY */ -}; - -static const struct 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, - .cpu_exec_interrupt = cris_cpu_exec_interrupt, - .do_interrupt = cris_cpu_do_interrupt, -#endif /* !CONFIG_USER_ONLY */ -}; - -static void crisv8_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - - ccc->vr = 8; - cc->gdb_read_register = crisv10_cpu_gdb_read_register; - cc->tcg_ops = &crisv10_tcg_ops; -} - -static void crisv9_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - - ccc->vr = 9; - cc->gdb_read_register = crisv10_cpu_gdb_read_register; - cc->tcg_ops = &crisv10_tcg_ops; -} - -static void crisv10_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - - ccc->vr = 10; - cc->gdb_read_register = crisv10_cpu_gdb_read_register; - cc->tcg_ops = &crisv10_tcg_ops; -} - -static void crisv11_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - - ccc->vr = 11; - cc->gdb_read_register = crisv10_cpu_gdb_read_register; - cc->tcg_ops = &crisv10_tcg_ops; -} - -static void crisv17_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - - ccc->vr = 17; - cc->gdb_read_register = crisv10_cpu_gdb_read_register; - cc->tcg_ops = &crisv10_tcg_ops; -} - -static void crisv32_cpu_class_init(ObjectClass *oc, void *data) -{ - CPUClass *cc = CPU_CLASS(oc); - CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); - - ccc->vr = 32; - cc->tcg_ops = &crisv32_tcg_ops; -} - -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); - - device_class_set_parent_realize(dc, cris_cpu_realizefn, - &ccc->parent_realize); - - device_class_set_parent_reset(dc, cris_cpu_reset, &ccc->parent_reset); - - cc->class_by_name = cris_cpu_class_by_name; - cc->has_work = cris_cpu_has_work; - 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 - dc->vmsd = &vmstate_cris_cpu; - cc->sysemu_ops = &cris_sysemu_ops; -#endif - - cc->gdb_num_core_regs = 49; - cc->gdb_stop_before_watchpoint = true; - - cc->disas_set_info = cris_disas_set_info; -} - -#define DEFINE_CRIS_CPU_TYPE(cpu_model, initfn) \ - { \ - .parent = TYPE_CRIS_CPU, \ - .class_init = initfn, \ - .name = CRIS_CPU_TYPE_NAME(cpu_model), \ - } - -static const TypeInfo cris_cpu_model_type_infos[] = { - { - .name = TYPE_CRIS_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(CRISCPU), - .instance_init = cris_cpu_initfn, - .abstract = true, - .class_size = sizeof(CRISCPUClass), - .class_init = cris_cpu_class_init, - }, - DEFINE_CRIS_CPU_TYPE("crisv8", crisv8_cpu_class_init), - DEFINE_CRIS_CPU_TYPE("crisv9", crisv9_cpu_class_init), - DEFINE_CRIS_CPU_TYPE("crisv10", crisv10_cpu_class_init), - DEFINE_CRIS_CPU_TYPE("crisv11", crisv11_cpu_class_init), - DEFINE_CRIS_CPU_TYPE("crisv17", crisv17_cpu_class_init), - DEFINE_CRIS_CPU_TYPE("crisv32", crisv32_cpu_class_init), -}; - -DEFINE_TYPES(cris_cpu_model_type_infos) diff --git a/target/cris/cpu.h b/target/cris/cpu.h deleted file mode 100644 index e6776f25b1..0000000000 --- a/target/cris/cpu.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * CRIS virtual CPU header - * - * Copyright (c) 2007 AXIS Communications AB - * Written by Edgar E. Iglesias - * - * 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 - * 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 CRIS_CPU_H -#define CRIS_CPU_H - -#include "cpu-qom.h" -#include "exec/cpu-defs.h" - -#define EXCP_NMI 1 -#define EXCP_GURU 2 -#define EXCP_BUSFAULT 3 -#define EXCP_IRQ 4 -#define EXCP_BREAK 5 - -/* CRIS-specific interrupt pending bits. */ -#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 - -/* CRUS CPU device objects interrupt lines. */ -/* PIC passes the vector for the IRQ as the value of it sends over qemu_irq */ -#define CRIS_CPU_IRQ 0 -#define CRIS_CPU_NMI 1 - -/* Register aliases. R0 - R15 */ -#define R_FP 8 -#define R_SP 14 -#define R_ACR 15 - -/* Support regs, P0 - P15 */ -#define PR_BZ 0 -#define PR_VR 1 -#define PR_PID 2 -#define PR_SRS 3 -#define PR_WZ 4 -#define PR_EXS 5 -#define PR_EDA 6 -#define PR_PREFIX 6 /* On CRISv10 P6 is reserved, we use it as prefix. */ -#define PR_MOF 7 -#define PR_DZ 8 -#define PR_EBP 9 -#define PR_ERP 10 -#define PR_SRP 11 -#define PR_NRP 12 -#define PR_CCS 13 -#define PR_USP 14 -#define PRV10_BRP 14 -#define PR_SPC 15 - -/* CPU flags. */ -#define Q_FLAG 0x80000000 -#define M_FLAG_V32 0x40000000 -#define PFIX_FLAG 0x800 /* CRISv10 Only. */ -#define F_FLAG_V10 0x400 -#define P_FLAG_V10 0x200 -#define S_FLAG 0x200 -#define R_FLAG 0x100 -#define P_FLAG 0x80 -#define M_FLAG_V10 0x80 -#define U_FLAG 0x40 -#define I_FLAG 0x20 -#define X_FLAG 0x10 -#define N_FLAG 0x08 -#define Z_FLAG 0x04 -#define V_FLAG 0x02 -#define C_FLAG 0x01 -#define ALU_FLAGS 0x1F - -/* Condition codes. */ -#define CC_CC 0 -#define CC_CS 1 -#define CC_NE 2 -#define CC_EQ 3 -#define CC_VC 4 -#define CC_VS 5 -#define CC_PL 6 -#define CC_MI 7 -#define CC_LS 8 -#define CC_HI 9 -#define CC_GE 10 -#define CC_LT 11 -#define CC_GT 12 -#define CC_LE 13 -#define CC_A 14 -#define CC_P 15 - -typedef struct { - uint32_t hi; - uint32_t lo; -} TLBSet; - -typedef struct CPUArchState { - uint32_t regs[16]; - /* P0 - P15 are referred to as special registers in the docs. */ - uint32_t pregs[16]; - - /* Pseudo register for the PC. Not directly accessible on CRIS. */ - uint32_t pc; - - /* Pseudo register for the kernel stack. */ - uint32_t ksp; - - /* Branch. */ - int dslot; - int btaken; - uint32_t btarget; - - /* Condition flag tracking. */ - uint32_t cc_op; - uint32_t cc_mask; - uint32_t cc_dest; - uint32_t cc_src; - uint32_t cc_result; - /* size of the operation, 1 = byte, 2 = word, 4 = dword. */ - int cc_size; - /* X flag at the time of cc snapshot. */ - int cc_x; - - /* CRIS has certain insns that lockout interrupts. */ - int locked_irq; - int interrupt_vector; - int fault_vector; - int trap_vector; - - /* FIXME: add a check in the translator to avoid writing to support - register sets beyond the 4th. The ISA allows up to 256! but in - practice there is no core that implements more than 4. - - Support function registers are used to control units close to the - core. Accesses do not pass down the normal hierarchy. - */ - uint32_t sregs[4][16]; - - /* Linear feedback shift reg in the mmu. Used to provide pseudo - randomness for the 'hint' the mmu gives to sw for choosing valid - sets on TLB refills. */ - uint32_t mmu_rand_lfsr; - - /* - * We just store the stores to the tlbset here for later evaluation - * when the hw needs access to them. - * - * One for I and another for D. - */ - TLBSet tlbsets[2][4][16]; - - /* Fields up to this point are cleared by a CPU reset */ - struct {} end_reset_fields; - - /* Members from load_info on are preserved across resets. */ - void *load_info; -} CPUCRISState; - -/** - * CRISCPU: - * @env: #CPUCRISState - * - * A CRIS CPU. - */ -struct ArchCPU { - /*< private >*/ - CPUState parent_obj; - /*< public >*/ - - CPUNegativeOffsetState neg; - CPUCRISState env; -}; - - -#ifndef CONFIG_USER_ONLY -extern const VMStateDescription vmstate_cris_cpu; - -void cris_cpu_do_interrupt(CPUState *cpu); -void crisv10_cpu_do_interrupt(CPUState *cpu); -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); -#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); - -void cris_initialize_tcg(void); -void cris_initialize_crisv10_tcg(void); - -/* Instead of computing the condition codes after each CRIS instruction, - * QEMU just stores one operand (called CC_SRC), the result - * (called CC_DEST) and the type of operation (called CC_OP). When the - * condition codes are needed, the condition codes can be calculated - * using this information. Condition codes are not generated if they - * are only needed for conditional branches. - */ -enum { - CC_OP_DYNAMIC, /* Use env->cc_op */ - CC_OP_FLAGS, - CC_OP_CMP, - CC_OP_MOVE, - CC_OP_ADD, - CC_OP_ADDC, - CC_OP_MCP, - CC_OP_ADDU, - CC_OP_SUB, - CC_OP_SUBU, - CC_OP_NEG, - CC_OP_BTST, - CC_OP_MULS, - CC_OP_MULU, - CC_OP_DSTEP, - CC_OP_MSTEP, - CC_OP_BOUND, - - CC_OP_OR, - CC_OP_AND, - CC_OP_XOR, - CC_OP_LSL, - CC_OP_LSR, - CC_OP_ASR, - CC_OP_LZ -}; - -/* 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 -#define SFR_RW_MM_CFG env->pregs[PR_SRS]][0 -#define SFR_RW_MM_KBASE_LO env->pregs[PR_SRS]][1 -#define SFR_RW_MM_KBASE_HI env->pregs[PR_SRS]][2 -#define SFR_R_MM_CAUSE env->pregs[PR_SRS]][3 -#define SFR_RW_MM_TLB_SEL env->pregs[PR_SRS]][4 -#define SFR_RW_MM_TLB_LO env->pregs[PR_SRS]][5 -#define SFR_RW_MM_TLB_HI env->pregs[PR_SRS]][6 - -#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) -{ - *pc = env->pc; - *cs_base = 0; - *flags = env->dslot | - (env->pregs[PR_CCS] & (S_FLAG | P_FLAG | U_FLAG - | X_FLAG | PFIX_FLAG)); -} - -#define cpu_list cris_cpu_list -void cris_cpu_list(void); - -#endif diff --git a/target/cris/crisv10-decode.h b/target/cris/crisv10-decode.h deleted file mode 100644 index 9c531f36b4..0000000000 --- a/target/cris/crisv10-decode.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * CRISv10 insn decoding macros. - * - * Copyright (c) 2010 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * 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_CRIS_CRISV10_DECODE_H -#define TARGET_CRIS_CRISV10_DECODE_H - -#define CRISV10_MODE_QIMMEDIATE 0 -#define CRISV10_MODE_REG 1 -#define CRISV10_MODE_INDIRECT 2 -#define CRISV10_MODE_AUTOINC 3 - -/* Quick Immediate. */ -#define CRISV10_QIMM_BCC_R0 0 -#define CRISV10_QIMM_BCC_R1 1 -#define CRISV10_QIMM_BCC_R2 2 -#define CRISV10_QIMM_BCC_R3 3 - -#define CRISV10_QIMM_BDAP_R0 4 -#define CRISV10_QIMM_BDAP_R1 5 -#define CRISV10_QIMM_BDAP_R2 6 -#define CRISV10_QIMM_BDAP_R3 7 - -#define CRISV10_QIMM_ADDQ 8 -#define CRISV10_QIMM_MOVEQ 9 -#define CRISV10_QIMM_SUBQ 10 -#define CRISV10_QIMM_CMPQ 11 -#define CRISV10_QIMM_ANDQ 12 -#define CRISV10_QIMM_ORQ 13 -#define CRISV10_QIMM_ASHQ 14 -#define CRISV10_QIMM_LSHQ 15 - - -#define CRISV10_REG_ADDX 0 -#define CRISV10_REG_MOVX 1 -#define CRISV10_REG_SUBX 2 -#define CRISV10_REG_LSL 3 -#define CRISV10_REG_ADDI 4 -#define CRISV10_REG_BIAP 5 -#define CRISV10_REG_NEG 6 -#define CRISV10_REG_BOUND 7 -#define CRISV10_REG_ADD 8 -#define CRISV10_REG_MOVE_R 9 -#define CRISV10_REG_MOVE_SPR_R 9 -#define CRISV10_REG_MOVE_R_SPR 8 -#define CRISV10_REG_SUB 10 -#define CRISV10_REG_CMP 11 -#define CRISV10_REG_AND 12 -#define CRISV10_REG_OR 13 -#define CRISV10_REG_ASR 14 -#define CRISV10_REG_LSR 15 - -#define CRISV10_REG_BTST 3 -#define CRISV10_REG_SCC 4 -#define CRISV10_REG_SETF 6 -#define CRISV10_REG_CLEARF 7 -#define CRISV10_REG_BIAP 5 -#define CRISV10_REG_ABS 10 -#define CRISV10_REG_DSTEP 11 -#define CRISV10_REG_LZ 12 -#define CRISV10_REG_NOT 13 -#define CRISV10_REG_SWAP 13 -#define CRISV10_REG_XOR 14 -#define CRISV10_REG_MSTEP 15 - -/* Indirect, var size. */ -#define CRISV10_IND_TEST 14 -#define CRISV10_IND_MUL 4 -#define CRISV10_IND_BDAP_M 5 -#define CRISV10_IND_ADD 8 -#define CRISV10_IND_MOVE_M_R 9 - - -/* indirect fixed size. */ -#define CRISV10_IND_ADDX 0 -#define CRISV10_IND_MOVX 1 -#define CRISV10_IND_SUBX 2 -#define CRISV10_IND_CMPX 3 -#define CRISV10_IND_JUMP_M 4 -#define CRISV10_IND_DIP 5 -#define CRISV10_IND_JUMP_R 6 -#define CRISV17_IND_ADDC 6 -#define CRISV10_IND_BOUND 7 -#define CRISV10_IND_BCC_M 7 -#define CRISV10_IND_MOVE_M_SPR 8 -#define CRISV10_IND_MOVE_SPR_M 9 -#define CRISV10_IND_SUB 10 -#define CRISV10_IND_CMP 11 -#define CRISV10_IND_AND 12 -#define CRISV10_IND_OR 13 -#define CRISV10_IND_MOVE_R_M 15 - -#define CRISV10_IND_MOVEM_M_R 14 -#define CRISV10_IND_MOVEM_R_M 15 - -#endif diff --git a/target/cris/crisv32-decode.h b/target/cris/crisv32-decode.h deleted file mode 100644 index fa0a7f0d63..0000000000 --- a/target/cris/crisv32-decode.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * CRIS insn decoding macros. - * - * Copyright (c) 2007 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * 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 CRISV32_DECODE_H -#define CRISV32_DECODE_H - -/* Convenient binary macros. */ -#define HEX__(n) 0x##n##LU -#define B8__(x) ((x&0x0000000FLU)?1:0) \ - + ((x&0x000000F0LU)?2:0) \ - + ((x&0x00000F00LU)?4:0) \ - + ((x&0x0000F000LU)?8:0) \ - + ((x&0x000F0000LU)?16:0) \ - + ((x&0x00F00000LU)?32:0) \ - + ((x&0x0F000000LU)?64:0) \ - + ((x&0xF0000000LU)?128:0) -#define B8(d) ((unsigned char)B8__(HEX__(d))) - -/* Quick imm. */ -#define DEC_BCCQ {B8(00000000), B8(11110000)} -#define DEC_ADDOQ {B8(00010000), B8(11110000)} -#define DEC_ADDQ {B8(00100000), B8(11111100)} -#define DEC_MOVEQ {B8(00100100), B8(11111100)} -#define DEC_SUBQ {B8(00101000), B8(11111100)} -#define DEC_CMPQ {B8(00101100), B8(11111100)} -#define DEC_ANDQ {B8(00110000), B8(11111100)} -#define DEC_ORQ {B8(00110100), B8(11111100)} -#define DEC_BTSTQ {B8(00111000), B8(11111110)} -#define DEC_ASRQ {B8(00111010), B8(11111110)} -#define DEC_LSLQ {B8(00111100), B8(11111110)} -#define DEC_LSRQ {B8(00111110), B8(11111110)} - -/* Register. */ -#define DEC_MOVU_R {B8(01000100), B8(11111110)} -#define DEC_MOVU_R {B8(01000100), B8(11111110)} -#define DEC_MOVS_R {B8(01000110), B8(11111110)} -#define DEC_MOVE_R {B8(01100100), B8(11111100)} -#define DEC_MOVE_RP {B8(01100011), B8(11111111)} -#define DEC_MOVE_PR {B8(01100111), B8(11111111)} -#define DEC_DSTEP_R {B8(01101111), B8(11111111)} -#define DEC_MOVE_RS {B8(10110111), B8(11111111)} -#define DEC_MOVE_SR {B8(11110111), B8(11111111)} -#define DEC_ADDU_R {B8(01000000), B8(11111110)} -#define DEC_ADDS_R {B8(01000010), B8(11111110)} -#define DEC_ADD_R {B8(01100000), B8(11111100)} -#define DEC_ADDI_R {B8(01010000), B8(11111100)} -#define DEC_MULS_R {B8(11010000), B8(11111100)} -#define DEC_MULU_R {B8(10010000), B8(11111100)} -#define DEC_ADDI_ACR {B8(01010100), B8(11111100)} -#define DEC_NEG_R {B8(01011000), B8(11111100)} -#define DEC_BOUND_R {B8(01011100), B8(11111100)} -#define DEC_SUBU_R {B8(01001000), B8(11111110)} -#define DEC_SUBS_R {B8(01001010), B8(11111110)} -#define DEC_SUB_R {B8(01101000), B8(11111100)} -#define DEC_CMP_R {B8(01101100), B8(11111100)} -#define DEC_AND_R {B8(01110000), B8(11111100)} -#define DEC_ABS_R {B8(01101011), B8(11111111)} -#define DEC_LZ_R {B8(01110011), B8(11111111)} -#define DEC_MCP_R {B8(01111111), B8(11111111)} -#define DEC_SWAP_R {B8(01110111), B8(11111111)} -#define DEC_XOR_R {B8(01111011), B8(11111111)} -#define DEC_LSL_R {B8(01001100), B8(11111100)} -#define DEC_LSR_R {B8(01111100), B8(11111100)} -#define DEC_ASR_R {B8(01111000), B8(11111100)} -#define DEC_OR_R {B8(01110100), B8(11111100)} -#define DEC_BTST_R {B8(01001111), B8(11111111)} - -/* Fixed. */ -#define DEC_SETF {B8(01011011), B8(11111111)} -#define DEC_CLEARF {B8(01011111), B8(11111111)} - -/* Memory. */ -#define DEC_ADDU_M {B8(10000000), B8(10111110)} -#define DEC_ADDS_M {B8(10000010), B8(10111110)} -#define DEC_MOVU_M {B8(10000100), B8(10111110)} -#define DEC_MOVS_M {B8(10000110), B8(10111110)} -#define DEC_SUBU_M {B8(10001000), B8(10111110)} -#define DEC_SUBS_M {B8(10001010), B8(10111110)} -#define DEC_CMPU_M {B8(10001100), B8(10111110)} -#define DEC_CMPS_M {B8(10001110), B8(10111110)} -#define DEC_ADDO_M {B8(10010100), B8(10111100)} -#define DEC_BOUND_M {B8(10011100), B8(10111100)} -#define DEC_ADD_M {B8(10100000), B8(10111100)} -#define DEC_MOVE_MR {B8(10100100), B8(10111100)} -#define DEC_SUB_M {B8(10101000), B8(10111100)} -#define DEC_CMP_M {B8(10101100), B8(10111100)} -#define DEC_AND_M {B8(10110000), B8(10111100)} -#define DEC_OR_M {B8(10110100), B8(10111100)} -#define DEC_TEST_M {B8(10111000), B8(10111100)} -#define DEC_MOVE_RM {B8(10111100), B8(10111100)} - -#define DEC_ADDC_R {B8(01010111), B8(11111111)} -#define DEC_ADDC_MR {B8(10011010), B8(10111111)} -#define DEC_LAPCQ {B8(10010111), B8(11111111)} -#define DEC_LAPC_IM {B8(11010111), B8(11111111)} - -#define DEC_MOVE_MP {B8(10100011), B8(10111111)} -#define DEC_MOVE_PM {B8(10100111), B8(10111111)} - -#define DEC_SCC_R {B8(01010011), B8(11111111)} -#define DEC_RFE_ETC {B8(10010011), B8(11111111)} -#define DEC_JUMP_P {B8(10011111), B8(11111111)} -#define DEC_BCC_IM {B8(11011111), B8(11111111)} -#define DEC_JAS_R {B8(10011011), B8(11111111)} -#define DEC_JASC_R {B8(10110011), B8(11111111)} -#define DEC_JAS_IM {B8(11011011), B8(11111111)} -#define DEC_JASC_IM {B8(11110011), B8(11111111)} -#define DEC_BAS_IM {B8(11101011), B8(11111111)} -#define DEC_BASC_IM {B8(11101111), B8(11111111)} -#define DEC_MOVEM_MR {B8(10111011), B8(10111111)} -#define DEC_MOVEM_RM {B8(10111111), B8(10111111)} - -#define DEC_FTAG_FIDX_D_M {B8(10101011), B8(11111111)} -#define DEC_FTAG_FIDX_I_M {B8(11010011), B8(11111111)} - -#endif diff --git a/target/cris/gdbstub.c b/target/cris/gdbstub.c deleted file mode 100644 index 2418d575b1..0000000000 --- a/target/cris/gdbstub.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * CRIS gdb server stub - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2013 SUSE LINUX Products GmbH - * - * 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/gdbstub.h" - -int crisv10_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) -{ - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; - - if (n < 15) { - return gdb_get_reg32(mem_buf, env->regs[n]); - } - - if (n == 15) { - return gdb_get_reg32(mem_buf, env->pc); - } - - if (n < 32) { - switch (n) { - case 16: - return gdb_get_reg8(mem_buf, env->pregs[n - 16]); - case 17: - return gdb_get_reg8(mem_buf, env->pregs[n - 16]); - case 20: - case 21: - return gdb_get_reg16(mem_buf, env->pregs[n - 16]); - default: - if (n >= 23) { - return gdb_get_reg32(mem_buf, env->pregs[n - 16]); - } - break; - } - } - return 0; -} - -int cris_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) -{ - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; - uint8_t srs; - - srs = env->pregs[PR_SRS]; - if (n < 16) { - return gdb_get_reg32(mem_buf, env->regs[n]); - } - - if (n >= 21 && n < 32) { - return gdb_get_reg32(mem_buf, env->pregs[n - 16]); - } - if (n >= 33 && n < 49) { - return gdb_get_reg32(mem_buf, env->sregs[srs][n - 33]); - } - switch (n) { - case 16: - return gdb_get_reg8(mem_buf, env->pregs[0]); - case 17: - return gdb_get_reg8(mem_buf, env->pregs[1]); - case 18: - return gdb_get_reg32(mem_buf, env->pregs[2]); - case 19: - return gdb_get_reg8(mem_buf, srs); - case 20: - return gdb_get_reg16(mem_buf, env->pregs[4]); - case 32: - return gdb_get_reg32(mem_buf, env->pc); - } - - return 0; -} - -int cris_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) -{ - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; - uint32_t tmp; - - if (n > 49) { - return 0; - } - - tmp = ldl_p(mem_buf); - - if (n < 16) { - env->regs[n] = tmp; - } - - if (n >= 21 && n < 32) { - env->pregs[n - 16] = tmp; - } - - /* FIXME: Should support function regs be writable? */ - switch (n) { - case 16: - return 1; - case 17: - return 1; - case 18: - env->pregs[PR_PID] = tmp; - break; - case 19: - return 1; - case 20: - return 2; - case 32: - env->pc = tmp; - break; - } - - return 4; -} diff --git a/target/cris/helper.c b/target/cris/helper.c deleted file mode 100644 index 81a72699b5..0000000000 --- a/target/cris/helper.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * CRIS helper routines. - * - * Copyright (c) 2007 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * 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 "cpu.h" -#include "hw/core/tcg-cpu-ops.h" -#include "mmu.h" -#include "qemu/host-utils.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/helper-proto.h" - - -//#define CRIS_HELPER_DEBUG - - -#ifdef CRIS_HELPER_DEBUG -#define D(x) x -#define D_LOG(...) qemu_log(__VA_ARGS__) -#else -#define D(x) -#define D_LOG(...) do { } while (0) -#endif - -static void cris_shift_ccs(CPUCRISState *env) -{ - uint32_t ccs; - /* Apply the ccs shift. */ - ccs = env->pregs[PR_CCS]; - ccs = ((ccs & 0xc0000000) | ((ccs << 12) >> 2)) & ~0x3ff; - env->pregs[PR_CCS] = ccs; -} - -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; - struct cris_mmu_result res; - int prot, miss; - target_ulong phy; - - miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, - access_type, mmu_idx, 0); - if (likely(!miss)) { - /* - * Mask off the cache selection bit. The ETRAX busses do not - * see the top bit. - */ - phy = res.phy & ~0x80000000; - prot = res.prot; - tlb_set_page(cs, address & TARGET_PAGE_MASK, phy, - prot, mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - if (probe) { - return false; - } - - if (cs->exception_index == EXCP_BUSFAULT) { - cpu_abort(cs, "CRIS: Illegal recursive bus fault." - "addr=%" VADDR_PRIx " access_type=%d\n", - address, access_type); - } - - env->pregs[PR_EDA] = address; - cs->exception_index = EXCP_BUSFAULT; - env->fault_vector = res.bf_vec; - if (retaddr) { - if (cpu_restore_state(cs, retaddr)) { - /* Evaluate flags after retranslation. */ - helper_top_evaluate_flags(env); - } - } - cpu_loop_exit(cs); -} - -void crisv10_cpu_do_interrupt(CPUState *cs) -{ - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; - int ex_vec = -1; - - D_LOG("exception index=%d interrupt_req=%d\n", - cs->exception_index, - cs->interrupt_request); - - if (env->dslot) { - /* CRISv10 never takes interrupts while in a delay-slot. */ - cpu_abort(cs, "CRIS: Interrupt on delay-slot\n"); - } - - assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); - switch (cs->exception_index) { - case EXCP_BREAK: - /* These exceptions are genereated by the core itself. - ERP should point to the insn following the brk. */ - ex_vec = env->trap_vector; - env->pregs[PRV10_BRP] = env->pc; - break; - - case EXCP_NMI: - /* NMI is hardwired to vector zero. */ - ex_vec = 0; - env->pregs[PR_CCS] &= ~M_FLAG_V10; - env->pregs[PRV10_BRP] = env->pc; - break; - - case EXCP_BUSFAULT: - cpu_abort(cs, "Unhandled busfault"); - break; - - default: - /* The interrupt controller gives us the vector. */ - ex_vec = env->interrupt_vector; - /* Normal interrupts are taken between - TB's. env->pc is valid here. */ - env->pregs[PR_ERP] = env->pc; - break; - } - - if (env->pregs[PR_CCS] & U_FLAG) { - /* Swap stack pointers. */ - env->pregs[PR_USP] = env->regs[R_SP]; - env->regs[R_SP] = env->ksp; - } - - /* Now that we are in kernel mode, load the handlers address. */ - env->pc = cpu_ldl_code(env, env->pregs[PR_EBP] + ex_vec * 4); - env->locked_irq = 1; - env->pregs[PR_CCS] |= F_FLAG_V10; /* set F. */ - - qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", - __func__, env->pc, ex_vec, - env->pregs[PR_CCS], - env->pregs[PR_PID], - env->pregs[PR_ERP]); -} - -void cris_cpu_do_interrupt(CPUState *cs) -{ - CRISCPU *cpu = CRIS_CPU(cs); - CPUCRISState *env = &cpu->env; - int ex_vec = -1; - - D_LOG("exception index=%d interrupt_req=%d\n", - cs->exception_index, - cs->interrupt_request); - - switch (cs->exception_index) { - case EXCP_BREAK: - /* These exceptions are genereated by the core itself. - ERP should point to the insn following the brk. */ - ex_vec = env->trap_vector; - env->pregs[PR_ERP] = env->pc; - break; - - case EXCP_NMI: - /* NMI is hardwired to vector zero. */ - ex_vec = 0; - env->pregs[PR_CCS] &= ~M_FLAG_V32; - env->pregs[PR_NRP] = env->pc; - break; - - case EXCP_BUSFAULT: - ex_vec = env->fault_vector; - env->pregs[PR_ERP] = env->pc; - break; - - default: - /* The interrupt controller gives us the vector. */ - ex_vec = env->interrupt_vector; - /* Normal interrupts are taken between - TB's. env->pc is valid here. */ - env->pregs[PR_ERP] = env->pc; - break; - } - - /* Fill in the IDX field. */ - env->pregs[PR_EXS] = (ex_vec & 0xff) << 8; - - if (env->dslot) { - D_LOG("excp isr=%x PC=%x ds=%d SP=%x" - " ERP=%x pid=%x ccs=%x cc=%d %x\n", - ex_vec, env->pc, env->dslot, - env->regs[R_SP], - env->pregs[PR_ERP], env->pregs[PR_PID], - env->pregs[PR_CCS], - env->cc_op, env->cc_mask); - /* We loose the btarget, btaken state here so rexec the - branch. */ - env->pregs[PR_ERP] -= env->dslot; - /* Exception starts with dslot cleared. */ - env->dslot = 0; - } - - if (env->pregs[PR_CCS] & U_FLAG) { - /* Swap stack pointers. */ - env->pregs[PR_USP] = env->regs[R_SP]; - env->regs[R_SP] = env->ksp; - } - - /* Apply the CRIS CCS shift. Clears U if set. */ - cris_shift_ccs(env); - - /* Now that we are in kernel mode, load the handlers address. - This load may not fault, real hw leaves that behaviour as - 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 - bus faults. */ - cs->exception_index = -1; - - D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", - __func__, env->pc, ex_vec, - env->pregs[PR_CCS], - env->pregs[PR_PID], - env->pregs[PR_ERP]); -} - -hwaddr cris_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - CRISCPU *cpu = CRIS_CPU(cs); - uint32_t phy = addr; - struct cris_mmu_result res; - int miss; - - miss = cris_mmu_translate(&res, &cpu->env, addr, MMU_DATA_LOAD, 0, 1); - /* If D TLB misses, try I TLB. */ - if (miss) { - miss = cris_mmu_translate(&res, &cpu->env, addr, MMU_INST_FETCH, 0, 1); - } - - if (!miss) { - phy = res.phy; - } - D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy)); - return phy; -} - -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; - bool ret = false; - - if (interrupt_request & CPU_INTERRUPT_HARD - && (env->pregs[PR_CCS] & I_FLAG) - && !env->locked_irq) { - cs->exception_index = EXCP_IRQ; - cc->tcg_ops->do_interrupt(cs); - ret = true; - } - if (interrupt_request & CPU_INTERRUPT_NMI) { - unsigned int m_flag_archval; - if (env->pregs[PR_VR] < 32) { - m_flag_archval = M_FLAG_V10; - } else { - m_flag_archval = M_FLAG_V32; - } - if ((env->pregs[PR_CCS] & m_flag_archval)) { - cs->exception_index = EXCP_NMI; - cc->tcg_ops->do_interrupt(cs); - ret = true; - } - } - - return ret; -} diff --git a/target/cris/helper.h b/target/cris/helper.h deleted file mode 100644 index 3abf608682..0000000000 --- a/target/cris/helper.h +++ /dev/null @@ -1,23 +0,0 @@ -DEF_HELPER_2(raise_exception, noreturn, env, i32) -DEF_HELPER_2(tlb_flush_pid, void, env, i32) -DEF_HELPER_2(spc_write, void, env, i32) -DEF_HELPER_1(rfe, void, env) -DEF_HELPER_1(rfn, void, env) - -DEF_HELPER_3(movl_sreg_reg, void, env, i32, i32) -DEF_HELPER_3(movl_reg_sreg, void, env, i32, i32) - -DEF_HELPER_FLAGS_4(btst, TCG_CALL_NO_SE, i32, env, i32, i32, i32) - -DEF_HELPER_FLAGS_4(evaluate_flags_muls, TCG_CALL_NO_SE, i32, env, i32, i32, i32) -DEF_HELPER_FLAGS_4(evaluate_flags_mulu, TCG_CALL_NO_SE, i32, env, i32, i32, i32) -DEF_HELPER_FLAGS_5(evaluate_flags_mcp, TCG_CALL_NO_SE, i32, env, - i32, i32, i32, i32) -DEF_HELPER_FLAGS_5(evaluate_flags_alu_4, TCG_CALL_NO_SE, i32, env, - i32, i32, i32, i32) -DEF_HELPER_FLAGS_5(evaluate_flags_sub_4, TCG_CALL_NO_SE, i32, env, - i32, i32, i32, i32) -DEF_HELPER_FLAGS_3(evaluate_flags_move_4, TCG_CALL_NO_SE, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(evaluate_flags_move_2, TCG_CALL_NO_SE, i32, env, i32, i32) -DEF_HELPER_1(evaluate_flags, void, env) -DEF_HELPER_1(top_evaluate_flags, void, env) diff --git a/target/cris/machine.c b/target/cris/machine.c deleted file mode 100644 index f370f33486..0000000000 --- a/target/cris/machine.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * CRIS virtual CPU state save/load support - * - * Copyright (c) 2012 Red Hat, Inc. - * Written by Juan Quintela - * - * 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 - * 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 "migration/cpu.h" - -static const VMStateDescription vmstate_tlbset = { - .name = "cpu/tlbset", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(lo, TLBSet), - VMSTATE_UINT32(hi, TLBSet), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_cris_env = { - .name = "env", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, CPUCRISState, 16), - VMSTATE_UINT32_ARRAY(pregs, CPUCRISState, 16), - VMSTATE_UINT32(pc, CPUCRISState), - VMSTATE_UINT32(ksp, CPUCRISState), - VMSTATE_INT32(dslot, CPUCRISState), - VMSTATE_INT32(btaken, CPUCRISState), - VMSTATE_UINT32(btarget, CPUCRISState), - VMSTATE_UINT32(cc_op, CPUCRISState), - VMSTATE_UINT32(cc_mask, CPUCRISState), - VMSTATE_UINT32(cc_dest, CPUCRISState), - VMSTATE_UINT32(cc_src, CPUCRISState), - VMSTATE_UINT32(cc_result, CPUCRISState), - VMSTATE_INT32(cc_size, CPUCRISState), - VMSTATE_INT32(cc_x, CPUCRISState), - VMSTATE_INT32(locked_irq, CPUCRISState), - VMSTATE_INT32(interrupt_vector, CPUCRISState), - VMSTATE_INT32(fault_vector, CPUCRISState), - VMSTATE_INT32(trap_vector, CPUCRISState), - VMSTATE_UINT32_ARRAY(sregs[0], CPUCRISState, 16), - VMSTATE_UINT32_ARRAY(sregs[1], CPUCRISState, 16), - VMSTATE_UINT32_ARRAY(sregs[2], CPUCRISState, 16), - VMSTATE_UINT32_ARRAY(sregs[3], CPUCRISState, 16), - VMSTATE_UINT32(mmu_rand_lfsr, CPUCRISState), - VMSTATE_STRUCT_ARRAY(tlbsets[0][0], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[0][1], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[0][2], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[0][3], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[1][0], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[1][1], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[1][2], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_STRUCT_ARRAY(tlbsets[1][3], CPUCRISState, 16, 0, - vmstate_tlbset, TLBSet), - VMSTATE_END_OF_LIST() - } -}; - -const VMStateDescription vmstate_cris_cpu = { - .name = "cpu", - .version_id = 1, - .minimum_version_id = 1, - .fields = (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 deleted file mode 100644 index c1e326d950..0000000000 --- a/target/cris/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -cris_ss = ss.source_set() -cris_ss.add(files( - 'cpu.c', - 'gdbstub.c', - 'op_helper.c', - 'translate.c', -)) - -cris_softmmu_ss = ss.source_set() -cris_softmmu_ss.add(files( - 'helper.c', - 'machine.c', - 'mmu.c', -)) - -target_arch += {'cris': cris_ss} -target_softmmu_arch += {'cris': cris_softmmu_ss} diff --git a/target/cris/mmu.c b/target/cris/mmu.c deleted file mode 100644 index b574ec6e5b..0000000000 --- a/target/cris/mmu.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * CRIS mmu emulation. - * - * Copyright (c) 2007 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * 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 "mmu.h" - -#ifdef DEBUG -#define D(x) x -#define D_LOG(...) qemu_log(__VA_ARGS__) -#else -#define D(x) do { } while (0) -#define D_LOG(...) do { } while (0) -#endif - -void cris_mmu_init(CPUCRISState *env) -{ - env->mmu_rand_lfsr = 0xcccc; -} - -#define SR_POLYNOM 0x8805 -static inline unsigned int compute_polynom(unsigned int sr) -{ - unsigned int i; - unsigned int f; - - f = 0; - for (i = 0; i < 16; i++) { - f += ((SR_POLYNOM >> i) & 1) & ((sr >> i) & 1); - } - - return f; -} - -static void cris_mmu_update_rand_lfsr(CPUCRISState *env) -{ - unsigned int f; - - /* Update lfsr at every fault. */ - f = compute_polynom(env->mmu_rand_lfsr); - env->mmu_rand_lfsr >>= 1; - env->mmu_rand_lfsr |= (f << 15); - env->mmu_rand_lfsr &= 0xffff; -} - -static inline int cris_mmu_enabled(uint32_t rw_gc_cfg) -{ - return (rw_gc_cfg & 12) != 0; -} - -static inline int cris_mmu_segmented_addr(int seg, uint32_t rw_mm_cfg) -{ - return (1 << seg) & rw_mm_cfg; -} - -static uint32_t cris_mmu_translate_seg(CPUCRISState *env, int seg) -{ - uint32_t base; - int i; - - if (seg < 8) { - base = env->sregs[SFR_RW_MM_KBASE_LO]; - } else { - base = env->sregs[SFR_RW_MM_KBASE_HI]; - } - - i = seg & 7; - base >>= i * 4; - base &= 15; - - base <<= 28; - return base; -} - -/* Used by the tlb decoder. */ -#define EXTRACT_FIELD(src, start, end) \ - (((src) >> start) & ((1 << (end - start + 1)) - 1)) - -static inline void set_field(uint32_t *dst, unsigned int val, - unsigned int offset, unsigned int width) -{ - uint32_t mask; - - mask = (1 << width) - 1; - mask <<= offset; - val <<= offset; - - val &= mask; - *dst &= ~(mask); - *dst |= val; -} - -#ifdef DEBUG -static void dump_tlb(CPUCRISState *env, int mmu) -{ - int set; - int idx; - uint32_t hi, lo, tlb_vpn, tlb_pfn; - - for (set = 0; set < 4; set++) { - for (idx = 0; idx < 16; idx++) { - lo = env->tlbsets[mmu][set][idx].lo; - hi = env->tlbsets[mmu][set][idx].hi; - tlb_vpn = EXTRACT_FIELD(hi, 13, 31); - tlb_pfn = EXTRACT_FIELD(lo, 13, 31); - - printf("TLB: [%d][%d] hi=%x lo=%x v=%x p=%x\n", - set, idx, hi, lo, tlb_vpn, tlb_pfn); - } - } -} -#endif - -static int cris_mmu_translate_page(struct cris_mmu_result *res, - CPUCRISState *env, uint32_t vaddr, - MMUAccessType access_type, - int usermode, int debug) -{ - unsigned int vpage; - unsigned int idx; - uint32_t pid, lo, hi; - uint32_t tlb_vpn, tlb_pfn = 0; - int tlb_pid, tlb_g, tlb_v, tlb_k, tlb_w, tlb_x; - int cfg_v, cfg_k, cfg_w, cfg_x; - int set, match = 0; - uint32_t r_cause; - uint32_t r_cfg; - int rwcause; - int mmu = 1; /* Data mmu is default. */ - int vect_base; - - r_cause = env->sregs[SFR_R_MM_CAUSE]; - r_cfg = env->sregs[SFR_RW_MM_CFG]; - pid = env->pregs[PR_PID] & 0xff; - - switch (access_type) { - case MMU_INST_FETCH: - rwcause = CRIS_MMU_ERR_EXEC; - mmu = 0; - break; - case MMU_DATA_STORE: - rwcause = CRIS_MMU_ERR_WRITE; - break; - default: - case MMU_DATA_LOAD: - rwcause = CRIS_MMU_ERR_READ; - break; - } - - /* I exception vectors 4 - 7, D 8 - 11. */ - vect_base = (mmu + 1) * 4; - - vpage = vaddr >> 13; - - /* - * We know the index which to check on each set. - * Scan both I and D. - */ - idx = vpage & 15; - for (set = 0; set < 4; set++) { - lo = env->tlbsets[mmu][set][idx].lo; - hi = env->tlbsets[mmu][set][idx].hi; - - tlb_vpn = hi >> 13; - tlb_pid = EXTRACT_FIELD(hi, 0, 7); - tlb_g = EXTRACT_FIELD(lo, 4, 4); - - D_LOG("TLB[%d][%d][%d] v=%x vpage=%x lo=%x hi=%x\n", - mmu, set, idx, tlb_vpn, vpage, lo, hi); - if ((tlb_g || (tlb_pid == pid)) && tlb_vpn == vpage) { - match = 1; - break; - } - } - - res->bf_vec = vect_base; - if (match) { - cfg_w = EXTRACT_FIELD(r_cfg, 19, 19); - cfg_k = EXTRACT_FIELD(r_cfg, 18, 18); - cfg_x = EXTRACT_FIELD(r_cfg, 17, 17); - cfg_v = EXTRACT_FIELD(r_cfg, 16, 16); - - tlb_pfn = EXTRACT_FIELD(lo, 13, 31); - tlb_v = EXTRACT_FIELD(lo, 3, 3); - tlb_k = EXTRACT_FIELD(lo, 2, 2); - tlb_w = EXTRACT_FIELD(lo, 1, 1); - tlb_x = EXTRACT_FIELD(lo, 0, 0); - - /* - * set_exception_vector(0x04, i_mmu_refill); - * set_exception_vector(0x05, i_mmu_invalid); - * set_exception_vector(0x06, i_mmu_access); - * set_exception_vector(0x07, i_mmu_execute); - * set_exception_vector(0x08, d_mmu_refill); - * set_exception_vector(0x09, d_mmu_invalid); - * set_exception_vector(0x0a, d_mmu_access); - * set_exception_vector(0x0b, d_mmu_write); - */ - if (cfg_k && tlb_k && usermode) { - D(printf("tlb: kernel protected %x lo=%x pc=%x\n", - vaddr, lo, env->pc)); - match = 0; - res->bf_vec = vect_base + 2; - } else if (access_type == MMU_DATA_STORE && cfg_w && !tlb_w) { - D(printf("tlb: write protected %x lo=%x pc=%x\n", - vaddr, lo, env->pc)); - match = 0; - /* write accesses never go through the I mmu. */ - res->bf_vec = vect_base + 3; - } else if (access_type == MMU_INST_FETCH && cfg_x && !tlb_x) { - D(printf("tlb: exec protected %x lo=%x pc=%x\n", - vaddr, lo, env->pc)); - match = 0; - res->bf_vec = vect_base + 3; - } else if (cfg_v && !tlb_v) { - D(printf("tlb: invalid %x\n", vaddr)); - match = 0; - res->bf_vec = vect_base + 1; - } - - res->prot = 0; - if (match) { - res->prot |= PAGE_READ; - if (tlb_w) { - res->prot |= PAGE_WRITE; - } - if (mmu == 0 && (cfg_x || tlb_x)) { - res->prot |= PAGE_EXEC; - } - } else { - D(dump_tlb(env, mmu)); - } - } else { - /* If refill, provide a randomized set. */ - set = env->mmu_rand_lfsr & 3; - } - - if (!match && !debug) { - cris_mmu_update_rand_lfsr(env); - - /* Compute index. */ - idx = vpage & 15; - - /* Update RW_MM_TLB_SEL. */ - env->sregs[SFR_RW_MM_TLB_SEL] = 0; - set_field(&env->sregs[SFR_RW_MM_TLB_SEL], idx, 0, 4); - set_field(&env->sregs[SFR_RW_MM_TLB_SEL], set, 4, 2); - - /* Update RW_MM_CAUSE. */ - set_field(&r_cause, rwcause, 8, 2); - set_field(&r_cause, vpage, 13, 19); - set_field(&r_cause, pid, 0, 8); - env->sregs[SFR_R_MM_CAUSE] = r_cause; - D(printf("refill vaddr=%x pc=%x\n", vaddr, env->pc)); - } - - D(printf("%s access=%u mtch=%d pc=%x va=%x vpn=%x tlbvpn=%x pfn=%x pid=%x" - " %x cause=%x sel=%x sp=%x %x %x\n", - __func__, access_type, match, env->pc, - vaddr, vpage, - tlb_vpn, tlb_pfn, tlb_pid, - pid, - r_cause, - env->sregs[SFR_RW_MM_TLB_SEL], - env->regs[R_SP], env->pregs[PR_USP], env->ksp)); - - res->phy = tlb_pfn << TARGET_PAGE_BITS; - return !match; -} - -void cris_mmu_flush_pid(CPUCRISState *env, uint32_t pid) -{ - target_ulong vaddr; - unsigned int idx; - uint32_t lo, hi; - uint32_t tlb_vpn; - int tlb_pid, tlb_g, tlb_v; - unsigned int set; - unsigned int mmu; - - pid &= 0xff; - for (mmu = 0; mmu < 2; mmu++) { - for (set = 0; set < 4; set++) { - for (idx = 0; idx < 16; idx++) { - lo = env->tlbsets[mmu][set][idx].lo; - hi = env->tlbsets[mmu][set][idx].hi; - - tlb_vpn = EXTRACT_FIELD(hi, 13, 31); - tlb_pid = EXTRACT_FIELD(hi, 0, 7); - tlb_g = EXTRACT_FIELD(lo, 4, 4); - tlb_v = EXTRACT_FIELD(lo, 3, 3); - - if (tlb_v && !tlb_g && (tlb_pid == pid)) { - vaddr = tlb_vpn << TARGET_PAGE_BITS; - D_LOG("flush pid=%x vaddr=%x\n", pid, vaddr); - tlb_flush_page(env_cpu(env), vaddr); - } - } - } - } -} - -int cris_mmu_translate(struct cris_mmu_result *res, - CPUCRISState *env, uint32_t vaddr, - MMUAccessType access_type, int mmu_idx, int debug) -{ - int seg; - int miss = 0; - int is_user = mmu_idx == MMU_USER_IDX; - uint32_t old_srs; - - old_srs = env->pregs[PR_SRS]; - - env->pregs[PR_SRS] = access_type == MMU_INST_FETCH ? 1 : 2; - - if (!cris_mmu_enabled(env->sregs[SFR_RW_GC_CFG])) { - res->phy = vaddr; - res->prot = PAGE_BITS; - goto done; - } - - seg = vaddr >> 28; - if (!is_user && cris_mmu_segmented_addr(seg, env->sregs[SFR_RW_MM_CFG])) { - uint32_t base; - - miss = 0; - base = cris_mmu_translate_seg(env, seg); - res->phy = base | (0x0fffffff & vaddr); - res->prot = PAGE_BITS; - } else { - miss = cris_mmu_translate_page(res, env, vaddr, access_type, - is_user, debug); - } - done: - env->pregs[PR_SRS] = old_srs; - return miss; -} diff --git a/target/cris/mmu.h b/target/cris/mmu.h deleted file mode 100644 index d57386ec6c..0000000000 --- a/target/cris/mmu.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TARGET_CRIS_MMU_H -#define TARGET_CRIS_MMU_H - -#define CRIS_MMU_ERR_EXEC 0 -#define CRIS_MMU_ERR_READ 1 -#define CRIS_MMU_ERR_WRITE 2 -#define CRIS_MMU_ERR_FLUSH 3 - -struct cris_mmu_result -{ - uint32_t phy; - int prot; - int bf_vec; -}; - -void cris_mmu_init(CPUCRISState *env); -void cris_mmu_flush_pid(CPUCRISState *env, uint32_t pid); -int cris_mmu_translate(struct cris_mmu_result *res, - CPUCRISState *env, uint32_t vaddr, - MMUAccessType access_type, int mmu_idx, int debug); - -#endif diff --git a/target/cris/op_helper.c b/target/cris/op_helper.c deleted file mode 100644 index d55a18a213..0000000000 --- a/target/cris/op_helper.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * CRIS helper routines - * - * Copyright (c) 2007 AXIS Communications - * Written by Edgar E. Iglesias - * - * 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 "mmu.h" -#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 - - -#ifdef CRIS_OP_HELPER_DEBUG -#define D(x) x -#define D_LOG(...) qemu_log(__VA_ARGS__) -#else -#define D(x) -#define D_LOG(...) do { } while (0) -#endif - -void helper_raise_exception(CPUCRISState *env, uint32_t index) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = index; - cpu_loop_exit(cs); -} - -void helper_tlb_flush_pid(CPUCRISState *env, uint32_t pid) -{ -#if !defined(CONFIG_USER_ONLY) - pid &= 0xff; - if (pid != (env->pregs[PR_PID] & 0xff)) { - cris_mmu_flush_pid(env, env->pregs[PR_PID]); - } -#endif -} - -void helper_spc_write(CPUCRISState *env, uint32_t new_spc) -{ -#if !defined(CONFIG_USER_ONLY) - CPUState *cs = env_cpu(env); - - tlb_flush_page(cs, env->pregs[PR_SPC]); - tlb_flush_page(cs, new_spc); -#endif -} - -/* Used by the tlb decoder. */ -#define EXTRACT_FIELD(src, start, end) \ - (((src) >> start) & ((1 << (end - start + 1)) - 1)) - -void helper_movl_sreg_reg(CPUCRISState *env, uint32_t sreg, uint32_t reg) -{ - uint32_t srs; - srs = env->pregs[PR_SRS]; - srs &= 3; - env->sregs[srs][sreg] = env->regs[reg]; - -#if !defined(CONFIG_USER_ONLY) - if (srs == 1 || srs == 2) { - if (sreg == 6) { - /* Writes to tlb-hi write to mm_cause as a side effect. */ - env->sregs[SFR_RW_MM_TLB_HI] = env->regs[reg]; - env->sregs[SFR_R_MM_CAUSE] = env->regs[reg]; - } else if (sreg == 5) { - uint32_t set; - uint32_t idx; - uint32_t lo, hi; - uint32_t vaddr; - int tlb_v; - - idx = set = env->sregs[SFR_RW_MM_TLB_SEL]; - set >>= 4; - set &= 3; - - idx &= 15; - /* We've just made a write to tlb_lo. */ - lo = env->sregs[SFR_RW_MM_TLB_LO]; - /* Writes are done via r_mm_cause. */ - hi = env->sregs[SFR_R_MM_CAUSE]; - - vaddr = EXTRACT_FIELD(env->tlbsets[srs - 1][set][idx].hi, 13, 31); - vaddr <<= TARGET_PAGE_BITS; - tlb_v = EXTRACT_FIELD(env->tlbsets[srs - 1][set][idx].lo, 3, 3); - env->tlbsets[srs - 1][set][idx].lo = lo; - env->tlbsets[srs - 1][set][idx].hi = hi; - - D_LOG("tlb flush vaddr=%x v=%d pc=%x\n", - vaddr, tlb_v, env->pc); - if (tlb_v) { - tlb_flush_page(env_cpu(env), vaddr); - } - } - } -#endif -} - -void helper_movl_reg_sreg(CPUCRISState *env, uint32_t reg, uint32_t sreg) -{ - uint32_t srs; - env->pregs[PR_SRS] &= 3; - srs = env->pregs[PR_SRS]; - -#if !defined(CONFIG_USER_ONLY) - if (srs == 1 || srs == 2) { - uint32_t set; - uint32_t idx; - uint32_t lo, hi; - - idx = set = env->sregs[SFR_RW_MM_TLB_SEL]; - set >>= 4; - set &= 3; - idx &= 15; - - /* Update the mirror regs. */ - hi = env->tlbsets[srs - 1][set][idx].hi; - lo = env->tlbsets[srs - 1][set][idx].lo; - env->sregs[SFR_RW_MM_TLB_HI] = hi; - env->sregs[SFR_RW_MM_TLB_LO] = lo; - } -#endif - env->regs[reg] = env->sregs[srs][sreg]; -} - -static void cris_ccs_rshift(CPUCRISState *env) -{ - uint32_t ccs; - - /* Apply the ccs shift. */ - ccs = env->pregs[PR_CCS]; - ccs = (ccs & 0xc0000000) | ((ccs & 0x0fffffff) >> 10); - if (ccs & U_FLAG) { - /* Enter user mode. */ - env->ksp = env->regs[R_SP]; - env->regs[R_SP] = env->pregs[PR_USP]; - } - - env->pregs[PR_CCS] = ccs; -} - -void helper_rfe(CPUCRISState *env) -{ - int rflag = env->pregs[PR_CCS] & R_FLAG; - - D_LOG("rfe: erp=%x pid=%x ccs=%x btarget=%x\n", - env->pregs[PR_ERP], env->pregs[PR_PID], - env->pregs[PR_CCS], - env->btarget); - - cris_ccs_rshift(env); - - /* RFE sets the P_FLAG only if the R_FLAG is not set. */ - if (!rflag) { - env->pregs[PR_CCS] |= P_FLAG; - } -} - -void helper_rfn(CPUCRISState *env) -{ - int rflag = env->pregs[PR_CCS] & R_FLAG; - - D_LOG("rfn: erp=%x pid=%x ccs=%x btarget=%x\n", - env->pregs[PR_ERP], env->pregs[PR_PID], - env->pregs[PR_CCS], - env->btarget); - - cris_ccs_rshift(env); - - /* Set the P_FLAG only if the R_FLAG is not set. */ - if (!rflag) { - env->pregs[PR_CCS] |= P_FLAG; - } - - /* Always set the M flag. */ - env->pregs[PR_CCS] |= M_FLAG_V32; -} - -uint32_t helper_btst(CPUCRISState *env, uint32_t t0, uint32_t t1, uint32_t ccs) -{ - /* FIXME: clean this up. */ - - /* - * des ref: - * The N flag is set according to the selected bit in the dest reg. - * The Z flag is set if the selected bit and all bits to the right are - * zero. - * The X flag is cleared. - * Other flags are left untouched. - * The destination reg is not affected. - */ - unsigned int fz, sbit, bset, mask, masked_t0; - - sbit = t1 & 31; - bset = !!(t0 & (1 << sbit)); - mask = sbit == 31 ? -1 : (1 << (sbit + 1)) - 1; - masked_t0 = t0 & mask; - fz = !(masked_t0 | bset); - - /* Clear the X, N and Z flags. */ - ccs = ccs & ~(X_FLAG | N_FLAG | Z_FLAG); - if (env->pregs[PR_VR] < 32) { - ccs &= ~(V_FLAG | C_FLAG); - } - /* Set the N and Z flags accordingly. */ - ccs |= (bset << 3) | (fz << 2); - return ccs; -} - -static inline uint32_t evaluate_flags_writeback(CPUCRISState *env, - uint32_t flags, uint32_t ccs) -{ - unsigned int x, z, mask; - - /* Extended arithmetics, leave the z flag alone. */ - x = env->cc_x; - mask = env->cc_mask | X_FLAG; - if (x) { - z = flags & Z_FLAG; - mask = mask & ~z; - } - flags &= mask; - - /* all insn clear the x-flag except setf or clrf. */ - ccs &= ~mask; - ccs |= flags; - return ccs; -} - -uint32_t helper_evaluate_flags_muls(CPUCRISState *env, - uint32_t ccs, uint32_t res, uint32_t mof) -{ - uint32_t flags = 0; - int64_t tmp; - int dneg; - - dneg = ((int32_t)res) < 0; - - tmp = mof; - tmp <<= 32; - tmp |= res; - if (tmp == 0) { - flags |= Z_FLAG; - } else if (tmp < 0) { - flags |= N_FLAG; - } - if ((dneg && mof != -1) || (!dneg && mof != 0)) { - flags |= V_FLAG; - } - return evaluate_flags_writeback(env, flags, ccs); -} - -uint32_t helper_evaluate_flags_mulu(CPUCRISState *env, - uint32_t ccs, uint32_t res, uint32_t mof) -{ - uint32_t flags = 0; - uint64_t tmp; - - tmp = mof; - tmp <<= 32; - tmp |= res; - if (tmp == 0) { - flags |= Z_FLAG; - } else if (tmp >> 63) { - flags |= N_FLAG; - } - if (mof) { - flags |= V_FLAG; - } - - return evaluate_flags_writeback(env, flags, ccs); -} - -uint32_t helper_evaluate_flags_mcp(CPUCRISState *env, uint32_t ccs, - uint32_t src, uint32_t dst, uint32_t res) -{ - uint32_t flags = 0; - - src = src & 0x80000000; - dst = dst & 0x80000000; - - if ((res & 0x80000000L) != 0L) { - flags |= N_FLAG; - if (!src && !dst) { - flags |= V_FLAG; - } else if (src & dst) { - flags |= R_FLAG; - } - } else { - if (res == 0L) { - flags |= Z_FLAG; - } - if (src & dst) { - flags |= V_FLAG; - } - if (dst | src) { - flags |= R_FLAG; - } - } - - return evaluate_flags_writeback(env, flags, ccs); -} - -uint32_t helper_evaluate_flags_alu_4(CPUCRISState *env, uint32_t ccs, - uint32_t src, uint32_t dst, uint32_t res) -{ - uint32_t flags = 0; - - src = src & 0x80000000; - dst = dst & 0x80000000; - - if ((res & 0x80000000L) != 0L) { - flags |= N_FLAG; - if (!src && !dst) { - flags |= V_FLAG; - } else if (src & dst) { - flags |= C_FLAG; - } - } else { - if (res == 0L) { - flags |= Z_FLAG; - } - if (src & dst) { - flags |= V_FLAG; - } - if (dst | src) { - flags |= C_FLAG; - } - } - - return evaluate_flags_writeback(env, flags, ccs); -} - -uint32_t helper_evaluate_flags_sub_4(CPUCRISState *env, uint32_t ccs, - uint32_t src, uint32_t dst, uint32_t res) -{ - uint32_t flags = 0; - - src = (~src) & 0x80000000; - dst = dst & 0x80000000; - - if ((res & 0x80000000L) != 0L) { - flags |= N_FLAG; - if (!src && !dst) { - flags |= V_FLAG; - } else if (src & dst) { - flags |= C_FLAG; - } - } else { - if (res == 0L) { - flags |= Z_FLAG; - } - if (src & dst) { - flags |= V_FLAG; - } - if (dst | src) { - flags |= C_FLAG; - } - } - - flags ^= C_FLAG; - return evaluate_flags_writeback(env, flags, ccs); -} - -uint32_t helper_evaluate_flags_move_4(CPUCRISState *env, - uint32_t ccs, uint32_t res) -{ - uint32_t flags = 0; - - if ((int32_t)res < 0) { - flags |= N_FLAG; - } else if (res == 0L) { - flags |= Z_FLAG; - } - - return evaluate_flags_writeback(env, flags, ccs); -} - -uint32_t helper_evaluate_flags_move_2(CPUCRISState *env, - uint32_t ccs, uint32_t res) -{ - uint32_t flags = 0; - - if ((int16_t)res < 0L) { - flags |= N_FLAG; - } else if (res == 0) { - flags |= Z_FLAG; - } - - return evaluate_flags_writeback(env, flags, ccs); -} - -/* - * TODO: This is expensive. We could split things up and only evaluate part of - * CCR on a need to know basis. For now, we simply re-evaluate everything. - */ -void helper_evaluate_flags(CPUCRISState *env) -{ - uint32_t src, dst, res; - uint32_t flags = 0; - - src = env->cc_src; - dst = env->cc_dest; - res = env->cc_result; - - if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP) { - src = ~src; - } - - /* - * Now, evaluate the flags. This stuff is based on - * Per Zander's CRISv10 simulator. - */ - switch (env->cc_size) { - case 1: - if ((res & 0x80L) != 0L) { - flags |= N_FLAG; - if (((src & 0x80L) == 0L) && ((dst & 0x80L) == 0L)) { - flags |= V_FLAG; - } else if (((src & 0x80L) != 0L) && ((dst & 0x80L) != 0L)) { - flags |= C_FLAG; - } - } else { - if ((res & 0xFFL) == 0L) { - flags |= Z_FLAG; - } - if (((src & 0x80L) != 0L) && ((dst & 0x80L) != 0L)) { - flags |= V_FLAG; - } - if ((dst & 0x80L) != 0L || (src & 0x80L) != 0L) { - flags |= C_FLAG; - } - } - break; - case 2: - if ((res & 0x8000L) != 0L) { - flags |= N_FLAG; - if (((src & 0x8000L) == 0L) && ((dst & 0x8000L) == 0L)) { - flags |= V_FLAG; - } else if (((src & 0x8000L) != 0L) && ((dst & 0x8000L) != 0L)) { - flags |= C_FLAG; - } - } else { - if ((res & 0xFFFFL) == 0L) { - flags |= Z_FLAG; - } - if (((src & 0x8000L) != 0L) && ((dst & 0x8000L) != 0L)) { - flags |= V_FLAG; - } - if ((dst & 0x8000L) != 0L || (src & 0x8000L) != 0L) { - flags |= C_FLAG; - } - } - break; - case 4: - if ((res & 0x80000000L) != 0L) { - flags |= N_FLAG; - if (((src & 0x80000000L) == 0L) && ((dst & 0x80000000L) == 0L)) { - flags |= V_FLAG; - } else if (((src & 0x80000000L) != 0L) && - ((dst & 0x80000000L) != 0L)) { - flags |= C_FLAG; - } - } else { - if (res == 0L) { - flags |= Z_FLAG; - } - if (((src & 0x80000000L) != 0L) && ((dst & 0x80000000L) != 0L)) { - flags |= V_FLAG; - } - if ((dst & 0x80000000L) != 0L || (src & 0x80000000L) != 0L) { - flags |= C_FLAG; - } - } - break; - default: - break; - } - - if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP) { - flags ^= C_FLAG; - } - - env->pregs[PR_CCS] = evaluate_flags_writeback(env, flags, - env->pregs[PR_CCS]); -} - -void helper_top_evaluate_flags(CPUCRISState *env) -{ - switch (env->cc_op) { - case CC_OP_MCP: - env->pregs[PR_CCS] - = helper_evaluate_flags_mcp(env, env->pregs[PR_CCS], - env->cc_src, env->cc_dest, - env->cc_result); - break; - case CC_OP_MULS: - env->pregs[PR_CCS] - = helper_evaluate_flags_muls(env, env->pregs[PR_CCS], - env->cc_result, env->pregs[PR_MOF]); - break; - case CC_OP_MULU: - env->pregs[PR_CCS] - = helper_evaluate_flags_mulu(env, env->pregs[PR_CCS], - env->cc_result, env->pregs[PR_MOF]); - break; - case CC_OP_MOVE: - case CC_OP_AND: - case CC_OP_OR: - case CC_OP_XOR: - case CC_OP_ASR: - case CC_OP_LSR: - case CC_OP_LSL: - switch (env->cc_size) { - case 4: - env->pregs[PR_CCS] = - helper_evaluate_flags_move_4(env, - env->pregs[PR_CCS], - env->cc_result); - break; - case 2: - env->pregs[PR_CCS] = - helper_evaluate_flags_move_2(env, - env->pregs[PR_CCS], - env->cc_result); - break; - default: - helper_evaluate_flags(env); - break; - } - break; - case CC_OP_FLAGS: - /* live. */ - break; - case CC_OP_SUB: - case CC_OP_CMP: - if (env->cc_size == 4) { - env->pregs[PR_CCS] = - helper_evaluate_flags_sub_4(env, - env->pregs[PR_CCS], - env->cc_src, env->cc_dest, - env->cc_result); - } else { - helper_evaluate_flags(env); - } - break; - default: - switch (env->cc_size) { - case 4: - env->pregs[PR_CCS] = - helper_evaluate_flags_alu_4(env, - env->pregs[PR_CCS], - env->cc_src, env->cc_dest, - env->cc_result); - break; - default: - helper_evaluate_flags(env); - break; - } - break; - } -} diff --git a/target/cris/opcode-cris.h b/target/cris/opcode-cris.h deleted file mode 100644 index 40509c88db..0000000000 --- a/target/cris/opcode-cris.h +++ /dev/null @@ -1,355 +0,0 @@ -/* cris.h -- Header file for CRIS opcode and register tables. - Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc. - Contributed by Axis Communications AB, Lund, Sweden. - Originally written for GAS 1.38.1 by Mikael Asker. - Updated, BFDized and GNUified by Hans-Peter Nilsson. - -This file is part of GAS, GDB and the GNU binutils. - -GAS, GDB, and GNU binutils is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2, or (at your -option) any later version. - -GAS, GDB, and GNU binutils are distributed in the hope that they 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_CRIS_OPCODE_CRIS_H -#define TARGET_CRIS_OPCODE_CRIS_H - -#if !defined(__STDC__) && !defined(const) -#define const -#endif - - -/* Registers. */ -#define MAX_REG (15) -#define CRIS_REG_SP (14) -#define CRIS_REG_PC (15) - -/* CPU version control of disassembly and assembly of instructions. - May affect how the instruction is assembled, at least the size of - immediate operands. */ -enum cris_insn_version_usage -{ - /* Any version. */ - cris_ver_version_all=0, - - /* Indeterminate (intended for disassembly only, or obsolete). */ - cris_ver_warning, - - /* Only for v0..3 (Etrax 1..4). */ - cris_ver_v0_3, - - /* Only for v3 or higher (ETRAX 4 and beyond). */ - cris_ver_v3p, - - /* Only for v8 (Etrax 100). */ - cris_ver_v8, - - /* Only for v8 or higher (ETRAX 100, ETRAX 100 LX). */ - cris_ver_v8p, - - /* Only for v0..10. FIXME: Not sure what to do with this. */ - cris_ver_sim_v0_10, - - /* Only for v0..10. */ - cris_ver_v0_10, - - /* Only for v3..10. (ETRAX 4, ETRAX 100 and ETRAX 100 LX). */ - cris_ver_v3_10, - - /* Only for v8..10 (ETRAX 100 and ETRAX 100 LX). */ - cris_ver_v8_10, - - /* Only for v10 (ETRAX 100 LX) and same series. */ - cris_ver_v10, - - /* Only for v10 (ETRAX 100 LX) and same series. */ - cris_ver_v10p, - - /* Only for v32 or higher (codename GUINNESS). - Of course some or all these of may change to cris_ver_v32p if/when - there's a new revision. */ - cris_ver_v32p -}; - - -/* Special registers. */ -struct cris_spec_reg -{ - const char *const name; - unsigned int number; - - /* The size of the register. */ - unsigned int reg_size; - - /* What CPU version the special register of that name is implemented - in. If cris_ver_warning, emit an unimplemented-warning. */ - enum cris_insn_version_usage applicable_version; - - /* There might be a specific warning for using a special register - here. */ - const char *const warning; -}; -extern const struct cris_spec_reg cris_spec_regs[]; - - -/* Support registers (kind of special too, but not named as such). */ -struct cris_support_reg -{ - const char *const name; - unsigned int number; -}; -extern const struct cris_support_reg cris_support_regs[]; - -/* Opcode-dependent constants. */ -#define AUTOINCR_BIT (0x04) - -/* Prefixes. */ -#define BDAP_QUICK_OPCODE (0x0100) -#define BDAP_QUICK_Z_BITS (0x0e00) - -#define BIAP_OPCODE (0x0540) -#define BIAP_Z_BITS (0x0a80) - -#define DIP_OPCODE (0x0970) -#define DIP_Z_BITS (0xf280) - -#define BDAP_INDIR_LOW (0x40) -#define BDAP_INDIR_LOW_Z (0x80) -#define BDAP_INDIR_HIGH (0x09) -#define BDAP_INDIR_HIGH_Z (0x02) - -#define BDAP_INDIR_OPCODE (BDAP_INDIR_HIGH * 0x0100 + BDAP_INDIR_LOW) -#define BDAP_INDIR_Z_BITS (BDAP_INDIR_HIGH_Z * 0x100 + BDAP_INDIR_LOW_Z) -#define BDAP_PC_LOW (BDAP_INDIR_LOW + CRIS_REG_PC) -#define BDAP_INCR_HIGH (BDAP_INDIR_HIGH + AUTOINCR_BIT) - -/* No prefix must have this code for its "match" bits in the - opcode-table. "BCC .+2" will do nicely. */ -#define NO_CRIS_PREFIX 0 - -/* Definitions for condition codes. */ -#define CC_CC 0x0 -#define CC_HS 0x0 -#define CC_CS 0x1 -#define CC_LO 0x1 -#define CC_NE 0x2 -#define CC_EQ 0x3 -#define CC_VC 0x4 -#define CC_VS 0x5 -#define CC_PL 0x6 -#define CC_MI 0x7 -#define CC_LS 0x8 -#define CC_HI 0x9 -#define CC_GE 0xA -#define CC_LT 0xB -#define CC_GT 0xC -#define CC_LE 0xD -#define CC_A 0xE -#define CC_EXT 0xF - -/* A table of strings "cc", "cs"... indexed with condition code - values as above. */ -extern const char *const cris_cc_strings[]; - -/* Bcc quick. */ -#define BRANCH_QUICK_LOW (0) -#define BRANCH_QUICK_HIGH (0) -#define BRANCH_QUICK_OPCODE (BRANCH_QUICK_HIGH * 0x0100 + BRANCH_QUICK_LOW) -#define BRANCH_QUICK_Z_BITS (0x0F00) - -/* BA quick. */ -#define BA_QUICK_HIGH (BRANCH_QUICK_HIGH + CC_A * 0x10) -#define BA_QUICK_OPCODE (BA_QUICK_HIGH * 0x100 + BRANCH_QUICK_LOW) - -/* Bcc [PC+]. */ -#define BRANCH_PC_LOW (0xFF) -#define BRANCH_INCR_HIGH (0x0D) -#define BA_PC_INCR_OPCODE \ - ((BRANCH_INCR_HIGH + CC_A * 0x10) * 0x0100 + BRANCH_PC_LOW) - -/* Jump. */ -/* Note that old versions generated special register 8 (in high bits) - and not-that-old versions recognized it as a jump-instruction. - That opcode now belongs to JUMPU. */ -#define JUMP_INDIR_OPCODE (0x0930) -#define JUMP_INDIR_Z_BITS (0xf2c0) -#define JUMP_PC_INCR_OPCODE \ - (JUMP_INDIR_OPCODE + AUTOINCR_BIT * 0x0100 + CRIS_REG_PC) - -#define MOVE_M_TO_PREG_OPCODE 0x0a30 -#define MOVE_M_TO_PREG_ZBITS 0x01c0 - -/* BDAP.D N,PC. */ -#define MOVE_PC_INCR_OPCODE_PREFIX \ - (((BDAP_INCR_HIGH | (CRIS_REG_PC << 4)) << 8) | BDAP_PC_LOW | (2 << 4)) -#define MOVE_PC_INCR_OPCODE_SUFFIX \ - (MOVE_M_TO_PREG_OPCODE | CRIS_REG_PC | (AUTOINCR_BIT << 8)) - -#define JUMP_PC_INCR_OPCODE_V32 (0x0DBF) - -/* BA DWORD (V32). */ -#define BA_DWORD_OPCODE (0x0EBF) - -/* Nop. */ -#define NOP_OPCODE (0x050F) -#define NOP_Z_BITS (0xFFFF ^ NOP_OPCODE) - -#define NOP_OPCODE_V32 (0x05B0) -#define NOP_Z_BITS_V32 (0xFFFF ^ NOP_OPCODE_V32) - -/* For the compatibility mode, let's use "MOVE R0,P0". Doesn't affect - registers or flags. Unfortunately shuts off interrupts for one cycle - for < v32, but there doesn't seem to be any alternative without that - effect. */ -#define NOP_OPCODE_COMMON (0x630) -#define NOP_OPCODE_ZBITS_COMMON (0xffff & ~NOP_OPCODE_COMMON) - -/* LAPC.D */ -#define LAPC_DWORD_OPCODE (0x0D7F) -#define LAPC_DWORD_Z_BITS (0x0fff & ~LAPC_DWORD_OPCODE) - -/* Structure of an opcode table entry. */ -enum cris_imm_oprnd_size_type -{ - /* No size is applicable. */ - SIZE_NONE, - - /* Always 32 bits. */ - SIZE_FIX_32, - - /* Indicated by size of special register. */ - SIZE_SPEC_REG, - - /* Indicated by size field, signed. */ - SIZE_FIELD_SIGNED, - - /* Indicated by size field, unsigned. */ - SIZE_FIELD_UNSIGNED, - - /* Indicated by size field, no sign implied. */ - SIZE_FIELD -}; - -/* For GDB. FIXME: Is this the best way to handle opcode - interpretation? */ -enum cris_op_type -{ - cris_not_implemented_op = 0, - cris_abs_op, - cris_addi_op, - cris_asr_op, - cris_asrq_op, - cris_ax_ei_setf_op, - cris_bdap_prefix, - cris_biap_prefix, - cris_break_op, - cris_btst_nop_op, - cris_clearf_di_op, - cris_dip_prefix, - cris_dstep_logshift_mstep_neg_not_op, - cris_eight_bit_offset_branch_op, - cris_move_mem_to_reg_movem_op, - cris_move_reg_to_mem_movem_op, - cris_move_to_preg_op, - cris_muls_op, - cris_mulu_op, - cris_none_reg_mode_add_sub_cmp_and_or_move_op, - cris_none_reg_mode_clear_test_op, - cris_none_reg_mode_jump_op, - cris_none_reg_mode_move_from_preg_op, - cris_quick_mode_add_sub_op, - cris_quick_mode_and_cmp_move_or_op, - cris_quick_mode_bdap_prefix, - cris_reg_mode_add_sub_cmp_and_or_move_op, - cris_reg_mode_clear_op, - cris_reg_mode_jump_op, - cris_reg_mode_move_from_preg_op, - cris_reg_mode_test_op, - cris_scc_op, - cris_sixteen_bit_offset_branch_op, - cris_three_operand_add_sub_cmp_and_or_op, - cris_three_operand_bound_op, - cris_two_operand_bound_op, - cris_xor_op -}; - -struct cris_opcode -{ - /* The name of the insn. */ - const char *name; - - /* Bits that must be 1 for a match. */ - unsigned int match; - - /* Bits that must be 0 for a match. */ - unsigned int lose; - - /* See the table in "opcodes/cris-opc.c". */ - const char *args; - - /* Nonzero if this is a delayed branch instruction. */ - char delayed; - - /* Size of immediate operands. */ - enum cris_imm_oprnd_size_type imm_oprnd_size; - - /* Indicates which version this insn was first implemented in. */ - enum cris_insn_version_usage applicable_version; - - /* What kind of operation this is. */ - enum cris_op_type op; -}; -extern const struct cris_opcode cris_opcodes[]; - - -/* These macros are for the target-specific flags in disassemble_info - used at disassembly. */ - -/* This insn accesses memory. This flag is more trustworthy than - checking insn_type for "dis_dref" which does not work for - e.g. "JSR [foo]". */ -#define CRIS_DIS_FLAG_MEMREF (1 << 0) - -/* The "target" field holds a register number. */ -#define CRIS_DIS_FLAG_MEM_TARGET_IS_REG (1 << 1) - -/* The "target2" field holds a register number; add it to "target". */ -#define CRIS_DIS_FLAG_MEM_TARGET2_IS_REG (1 << 2) - -/* Yet another add-on: the register in "target2" must be multiplied - by 2 before adding to "target". */ -#define CRIS_DIS_FLAG_MEM_TARGET2_MULT2 (1 << 3) - -/* Yet another add-on: the register in "target2" must be multiplied - by 4 (mutually exclusive with .._MULT2). */ -#define CRIS_DIS_FLAG_MEM_TARGET2_MULT4 (1 << 4) - -/* The register in "target2" is an indirect memory reference (of the - register there), add to "target". Assumed size is dword (mutually - exclusive with .._MULT[24]). */ -#define CRIS_DIS_FLAG_MEM_TARGET2_MEM (1 << 5) - -/* Add-on to CRIS_DIS_FLAG_MEM_TARGET2_MEM; the memory access is "byte"; - sign-extended before adding to "target". */ -#define CRIS_DIS_FLAG_MEM_TARGET2_MEM_BYTE (1 << 6) - -/* Add-on to CRIS_DIS_FLAG_MEM_TARGET2_MEM; the memory access is "word"; - sign-extended before adding to "target". */ -#define CRIS_DIS_FLAG_MEM_TARGET2_MEM_WORD (1 << 7) - -#endif /* TARGET_CRIS_OPCODE_CRIS_H */ - -/* - * Local variables: - * eval: (c-set-style "gnu") - * indent-tabs-mode: t - * End: - */ diff --git a/target/cris/translate.c b/target/cris/translate.c deleted file mode 100644 index fbc3fd5865..0000000000 --- a/target/cris/translate.c +++ /dev/null @@ -1,3394 +0,0 @@ -/* - * CRIS emulation for qemu: main translation routines. - * - * Copyright (c) 2008 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * 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 . - */ - -/* - * FIXME: - * The condition code translation is in need of attention. - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "disas/disas.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "exec/helper-proto.h" -#include "mmu.h" -#include "exec/cpu_ldst.h" -#include "exec/translator.h" -#include "crisv32-decode.h" -#include "qemu/qemu-print.h" - -#include "exec/helper-gen.h" - -#include "exec/log.h" - - -#define DISAS_CRIS 0 -#if DISAS_CRIS -# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) -#else -# define LOG_DIS(...) do { } while (0) -#endif - -#define D(x) -#define BUG() (gen_BUG(dc, __FILE__, __LINE__)) -#define BUG_ON(x) ({if (x) BUG();}) - -/* - * Target-specific is_jmp field values - */ -/* Only pc was modified dynamically */ -#define DISAS_JUMP DISAS_TARGET_0 -/* Cpu state was modified dynamically, including pc */ -#define DISAS_UPDATE DISAS_TARGET_1 -/* Cpu state was modified dynamically, excluding pc -- use npc */ -#define DISAS_UPDATE_NEXT DISAS_TARGET_2 -/* PC update for delayed branch, see cpustate_changed otherwise */ -#define DISAS_DBRANCH DISAS_TARGET_3 - -/* Used by the decoder. */ -#define EXTRACT_FIELD(src, start, end) \ - (((src) >> start) & ((1 << (end - start + 1)) - 1)) - -#define CC_MASK_NZ 0xc -#define CC_MASK_NZV 0xe -#define CC_MASK_NZVC 0xf -#define CC_MASK_RNZV 0x10e - -static TCGv cpu_R[16]; -static TCGv cpu_PR[16]; -static TCGv cc_x; -static TCGv cc_src; -static TCGv cc_dest; -static TCGv cc_result; -static TCGv cc_op; -static TCGv cc_size; -static TCGv cc_mask; - -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; - - /* Decoder. */ - unsigned int (*decoder)(CPUCRISState *env, struct DisasContext *dc); - uint32_t ir; - uint32_t opcode; - unsigned int op1; - unsigned int op2; - unsigned int zsize, zzsize; - unsigned int mode; - unsigned int postinc; - - unsigned int size; - unsigned int src; - unsigned int dst; - unsigned int cond; - - int update_cc; - int cc_op; - int cc_size; - uint32_t cc_mask; - - int cc_size_uptodate; /* -1 invalid or last written value. */ - - int cc_x_uptodate; /* 1 - ccs, 2 - known | X_FLAG. 0 not up-to-date. */ - int flags_uptodate; /* Whether or not $ccs is up-to-date. */ - int flags_x; - - int clear_x; /* Clear x after this insn? */ - int clear_prefix; /* Clear prefix after this insn? */ - int clear_locked_irq; /* Clear the irq lockout. */ - int cpustate_changed; - unsigned int tb_flags; /* tb dependent flags. */ - -#define JMP_NOJMP 0 -#define JMP_DIRECT 1 -#define JMP_DIRECT_CC 2 -#define JMP_INDIRECT 3 - int jmp; /* 0=nojmp, 1=direct, 2=indirect. */ - uint32_t jmp_pc; - - int delayed_branch; -} DisasContext; - -static void gen_BUG(DisasContext *dc, const char *file, int line) -{ - cpu_abort(CPU(dc->cpu), "%s:%d pc=%x\n", file, line, dc->pc); -} - -static const char * const regnames_v32[] = -{ - "$r0", "$r1", "$r2", "$r3", - "$r4", "$r5", "$r6", "$r7", - "$r8", "$r9", "$r10", "$r11", - "$r12", "$r13", "$sp", "$acr", -}; - -static const char * const pregnames_v32[] = -{ - "$bz", "$vr", "$pid", "$srs", - "$wz", "$exs", "$eda", "$mof", - "$dz", "$ebp", "$erp", "$srp", - "$nrp", "$ccs", "$usp", "$spc", -}; - -/* We need this table to handle preg-moves with implicit width. */ -static const int preg_sizes[] = { - 1, /* bz. */ - 1, /* vr. */ - 4, /* pid. */ - 1, /* srs. */ - 2, /* wz. */ - 4, 4, 4, - 4, 4, 4, 4, - 4, 4, 4, 4, -}; - -#define t_gen_mov_TN_env(tn, member) \ - tcg_gen_ld_tl(tn, cpu_env, offsetof(CPUCRISState, member)) -#define t_gen_mov_env_TN(member, tn) \ - tcg_gen_st_tl(tn, cpu_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) - -static inline void t_gen_mov_TN_preg(TCGv tn, int r) -{ - assert(r >= 0 && r <= 15); - if (r == PR_BZ || r == PR_WZ || r == PR_DZ) { - tcg_gen_movi_tl(tn, 0); - } else if (r == PR_VR) { - tcg_gen_movi_tl(tn, 32); - } else { - tcg_gen_mov_tl(tn, cpu_PR[r]); - } -} -static inline void t_gen_mov_preg_TN(DisasContext *dc, int r, TCGv tn) -{ - assert(r >= 0 && r <= 15); - if (r == PR_BZ || r == PR_WZ || r == PR_DZ) { - return; - } else if (r == PR_SRS) { - tcg_gen_andi_tl(cpu_PR[r], tn, 3); - } else { - if (r == PR_PID) { - gen_helper_tlb_flush_pid(cpu_env, tn); - } - if (dc->tb_flags & S_FLAG && r == PR_SPC) { - gen_helper_spc_write(cpu_env, tn); - } else if (r == PR_CCS) { - dc->cpustate_changed = 1; - } - tcg_gen_mov_tl(cpu_PR[r], tn); - } -} - -/* Sign extend at translation time. */ -static int sign_extend(unsigned int val, unsigned int width) -{ - int sval; - - /* LSL. */ - val <<= 31 - width; - sval = val; - /* ASR. */ - sval >>= 31 - width; - return sval; -} - -static int cris_fetch(CPUCRISState *env, DisasContext *dc, uint32_t addr, - unsigned int size, unsigned int sign) -{ - int r; - - switch (size) { - case 4: - { - r = cpu_ldl_code(env, addr); - break; - } - case 2: - { - if (sign) { - r = cpu_ldsw_code(env, addr); - } else { - r = cpu_lduw_code(env, addr); - } - break; - } - case 1: - { - if (sign) { - r = cpu_ldsb_code(env, addr); - } else { - r = cpu_ldub_code(env, addr); - } - break; - } - default: - cpu_abort(CPU(dc->cpu), "Invalid fetch size %d\n", size); - break; - } - return r; -} - -static void cris_lock_irq(DisasContext *dc) -{ - dc->clear_locked_irq = 0; - t_gen_movi_env_TN(locked_irq, 1); -} - -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); -} - -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); - 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) -{ - TCGv t0, t_31; - - t0 = tcg_temp_new(); - t_31 = tcg_temp_new(); - tcg_gen_shr_tl(d, a, b); - - tcg_gen_movi_tl(t_31, 31); - 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_asr(TCGv d, TCGv a, TCGv b) -{ - TCGv t0, t_31; - - t0 = tcg_temp_new(); - t_31 = tcg_temp_new(); - tcg_gen_sar_tl(d, a, b); - - tcg_gen_movi_tl(t_31, 31); - 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) -{ - TCGv t = tcg_temp_new(); - - /* - * d <<= 1 - * if (d >= s) - * d -= s; - */ - 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) -{ - TCGv t; - - /* - * d <<= 1 - * if (n) - * d += s; - */ - t = tcg_temp_new(); - tcg_gen_shli_tl(d, a, 1); - tcg_gen_shli_tl(t, ccs, 31 - 3); - 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. */ -static inline void t_gen_add_flag(TCGv d, int flag) -{ - TCGv c; - - c = tcg_temp_new(); - t_gen_mov_TN_preg(c, PR_CCS); - /* Propagate carry into d. */ - tcg_gen_andi_tl(c, c, 1 << flag); - if (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) -{ - if (dc->flags_x) { - TCGv c = tcg_temp_new(); - - t_gen_mov_TN_preg(c, PR_CCS); - /* 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); - } -} - -static inline void t_gen_subx_carry(DisasContext *dc, TCGv d) -{ - if (dc->flags_x) { - TCGv c = tcg_temp_new(); - - t_gen_mov_TN_preg(c, PR_CCS); - /* 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); - } -} - -/* Swap the two bytes within each half word of the s operand. - T0 = ((T0 << 8) & 0xff00ff00) | ((T0 >> 8) & 0x00ff00ff) */ -static inline void t_gen_swapb(TCGv d, TCGv s) -{ - TCGv t, org_s; - - t = tcg_temp_new(); - org_s = tcg_temp_new(); - - /* d and s may refer to the same object. */ - tcg_gen_mov_tl(org_s, s); - tcg_gen_shli_tl(t, org_s, 8); - tcg_gen_andi_tl(d, t, 0xff00ff00); - 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. */ -static inline void t_gen_swapw(TCGv d, TCGv s) -{ - TCGv t; - /* d and s refer the same object. */ - t = tcg_temp_new(); - tcg_gen_mov_tl(t, 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)); - */ -static void t_gen_swapr(TCGv d, TCGv s) -{ - static const struct { - int shift; /* LSL when positive, LSR when negative. */ - uint32_t mask; - } bitrev[] = { - {7, 0x80808080}, - {5, 0x40404040}, - {3, 0x20202020}, - {1, 0x10101010}, - {-1, 0x08080808}, - {-3, 0x04040404}, - {-5, 0x02020202}, - {-7, 0x01010101} - }; - int i; - TCGv t, org_s; - - /* d and s refer the same object. */ - t = tcg_temp_new(); - org_s = tcg_temp_new(); - tcg_gen_mov_tl(org_s, s); - - tcg_gen_shli_tl(t, org_s, bitrev[0].shift); - tcg_gen_andi_tl(d, t, bitrev[0].mask); - for (i = 1; i < ARRAY_SIZE(bitrev); i++) { - if (bitrev[i].shift >= 0) { - tcg_gen_shli_tl(t, org_s, bitrev[i].shift); - } else { - tcg_gen_shri_tl(t, org_s, -bitrev[i].shift); - } - 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) -{ - return translator_use_goto_tb(&dc->base, dest); -} - -static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) -{ - if (use_goto_tb(dc, dest)) { - tcg_gen_goto_tb(n); - tcg_gen_movi_tl(env_pc, dest); - tcg_gen_exit_tb(dc->base.tb, n); - } else { - tcg_gen_movi_tl(env_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } -} - -static inline void cris_clear_x_flag(DisasContext *dc) -{ - if (dc->flags_x) { - dc->flags_uptodate = 0; - } - dc->flags_x = 0; -} - -static void cris_flush_cc_state(DisasContext *dc) -{ - if (dc->cc_size_uptodate != dc->cc_size) { - tcg_gen_movi_tl(cc_size, dc->cc_size); - dc->cc_size_uptodate = dc->cc_size; - } - tcg_gen_movi_tl(cc_op, dc->cc_op); - tcg_gen_movi_tl(cc_mask, dc->cc_mask); -} - -static void cris_evaluate_flags(DisasContext *dc) -{ - if (dc->flags_uptodate) { - return; - } - - cris_flush_cc_state(dc); - - switch (dc->cc_op) { - case CC_OP_MCP: - gen_helper_evaluate_flags_mcp(cpu_PR[PR_CCS], cpu_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, - 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, - cpu_PR[PR_CCS], cc_result, - cpu_PR[PR_MOF]); - break; - case CC_OP_MOVE: - case CC_OP_AND: - case CC_OP_OR: - case CC_OP_XOR: - case CC_OP_ASR: - case CC_OP_LSR: - case CC_OP_LSL: - switch (dc->cc_size) { - case 4: - gen_helper_evaluate_flags_move_4(cpu_PR[PR_CCS], - cpu_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); - break; - default: - gen_helper_evaluate_flags(cpu_env); - break; - } - break; - case CC_OP_FLAGS: - /* live. */ - break; - 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, - cpu_PR[PR_CCS], cc_src, cc_dest, cc_result); - } else { - gen_helper_evaluate_flags(cpu_env); - } - - break; - default: - switch (dc->cc_size) { - case 4: - gen_helper_evaluate_flags_alu_4(cpu_PR[PR_CCS], cpu_env, - cpu_PR[PR_CCS], cc_src, cc_dest, cc_result); - break; - default: - gen_helper_evaluate_flags(cpu_env); - break; - } - break; - } - - if (dc->flags_x) { - tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], X_FLAG); - } else if (dc->cc_op == CC_OP_FLAGS) { - tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~X_FLAG); - } - dc->flags_uptodate = 1; -} - -static void cris_cc_mask(DisasContext *dc, unsigned int mask) -{ - uint32_t ovl; - - if (!mask) { - dc->update_cc = 0; - return; - } - - /* Check if we need to evaluate the condition codes due to - CC overlaying. */ - ovl = (dc->cc_mask ^ mask) & ~mask; - if (ovl) { - /* TODO: optimize this case. It trigs all the time. */ - cris_evaluate_flags(dc); - } - dc->cc_mask = mask; - dc->update_cc = 1; -} - -static void cris_update_cc_op(DisasContext *dc, int op, int size) -{ - dc->cc_op = op; - dc->cc_size = size; - dc->flags_uptodate = 0; -} - -static inline void cris_update_cc_x(DisasContext *dc) -{ - /* Save the x flag state at the time of the cc snapshot. */ - if (dc->cc_x_uptodate == (2 | dc->flags_x)) { - return; - } - tcg_gen_movi_tl(cc_x, dc->flags_x); - dc->cc_x_uptodate = 2 | dc->flags_x; -} - -/* Update cc prior to executing ALU op. Needs source operands untouched. */ -static void cris_pre_alu_update_cc(DisasContext *dc, int op, - TCGv dst, TCGv src, int size) -{ - if (dc->update_cc) { - cris_update_cc_op(dc, op, size); - tcg_gen_mov_tl(cc_src, src); - - if (op != CC_OP_MOVE - && op != CC_OP_AND - && op != CC_OP_OR - && op != CC_OP_XOR - && op != CC_OP_ASR - && op != CC_OP_LSR - && op != CC_OP_LSL) { - tcg_gen_mov_tl(cc_dest, dst); - } - - cris_update_cc_x(dc); - } -} - -/* Update cc after executing ALU op. needs the result. */ -static inline void cris_update_result(DisasContext *dc, TCGv res) -{ - if (dc->update_cc) { - tcg_gen_mov_tl(cc_result, res); - } -} - -/* Returns one if the write back stage should execute. */ -static void cris_alu_op_exec(DisasContext *dc, int op, - TCGv dst, TCGv a, TCGv b, int size) -{ - /* Emit the ALU insns. */ - switch (op) { - case CC_OP_ADD: - tcg_gen_add_tl(dst, a, b); - /* Extended arithmetics. */ - t_gen_addx_carry(dc, dst); - break; - case CC_OP_ADDC: - tcg_gen_add_tl(dst, a, b); - t_gen_add_flag(dst, 0); /* C_FLAG. */ - break; - case CC_OP_MCP: - tcg_gen_add_tl(dst, a, b); - t_gen_add_flag(dst, 8); /* R_FLAG. */ - break; - case CC_OP_SUB: - tcg_gen_sub_tl(dst, a, b); - /* Extended arithmetics. */ - t_gen_subx_carry(dc, dst); - break; - case CC_OP_MOVE: - tcg_gen_mov_tl(dst, b); - break; - case CC_OP_OR: - tcg_gen_or_tl(dst, a, b); - break; - case CC_OP_AND: - tcg_gen_and_tl(dst, a, b); - break; - case CC_OP_XOR: - tcg_gen_xor_tl(dst, a, b); - break; - case CC_OP_LSL: - t_gen_lsl(dst, a, b); - break; - case CC_OP_LSR: - t_gen_lsr(dst, a, b); - break; - case CC_OP_ASR: - t_gen_asr(dst, a, b); - break; - case CC_OP_NEG: - tcg_gen_neg_tl(dst, b); - /* Extended arithmetics. */ - t_gen_subx_carry(dc, dst); - break; - case CC_OP_LZ: - tcg_gen_clzi_tl(dst, b, TARGET_LONG_BITS); - break; - case CC_OP_MULS: - tcg_gen_muls2_tl(dst, cpu_PR[PR_MOF], a, b); - break; - case CC_OP_MULU: - tcg_gen_mulu2_tl(dst, cpu_PR[PR_MOF], a, b); - break; - case CC_OP_DSTEP: - t_gen_cris_dstep(dst, a, b); - break; - case CC_OP_MSTEP: - t_gen_cris_mstep(dst, a, b, cpu_PR[PR_CCS]); - break; - case CC_OP_BOUND: - tcg_gen_movcond_tl(TCG_COND_LEU, dst, a, b, a, b); - break; - case CC_OP_CMP: - tcg_gen_sub_tl(dst, a, b); - /* Extended arithmetics. */ - t_gen_subx_carry(dc, dst); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "illegal ALU op.\n"); - BUG(); - break; - } - - if (size == 1) { - tcg_gen_andi_tl(dst, dst, 0xff); - } else if (size == 2) { - tcg_gen_andi_tl(dst, dst, 0xffff); - } -} - -static void cris_alu(DisasContext *dc, int op, - TCGv d, TCGv op_a, TCGv op_b, int size) -{ - TCGv tmp; - int writeback; - - writeback = 1; - - if (op == CC_OP_CMP) { - tmp = tcg_temp_new(); - writeback = 0; - } else if (size == 4) { - tmp = d; - writeback = 0; - } else { - tmp = tcg_temp_new(); - } - - - cris_pre_alu_update_cc(dc, op, op_a, op_b, size); - cris_alu_op_exec(dc, op, tmp, op_a, op_b, size); - cris_update_result(dc, tmp); - - /* Writeback. */ - if (writeback) { - if (size == 1) { - tcg_gen_andi_tl(d, d, ~0xff); - } else { - tcg_gen_andi_tl(d, d, ~0xffff); - } - tcg_gen_or_tl(d, d, tmp); - } - if (tmp != d) { - tcg_temp_free(tmp); - } -} - -static int arith_cc(DisasContext *dc) -{ - if (dc->update_cc) { - switch (dc->cc_op) { - case CC_OP_ADDC: return 1; - case CC_OP_ADD: return 1; - case CC_OP_SUB: return 1; - case CC_OP_DSTEP: return 1; - case CC_OP_LSL: return 1; - case CC_OP_LSR: return 1; - case CC_OP_ASR: return 1; - case CC_OP_CMP: return 1; - case CC_OP_NEG: return 1; - case CC_OP_OR: return 1; - case CC_OP_AND: return 1; - case CC_OP_XOR: return 1; - case CC_OP_MULU: return 1; - case CC_OP_MULS: return 1; - default: - return 0; - } - } - return 0; -} - -static void gen_tst_cc (DisasContext *dc, TCGv cc, int cond) -{ - int arith_opt, move_opt; - - /* TODO: optimize more condition codes. */ - - /* - * If the flags are live, we've gotta look into the bits of CCS. - * Otherwise, if we just did an arithmetic operation we try to - * evaluate the condition code faster. - * - * When this function is done, T0 should be non-zero if the condition - * code is true. - */ - arith_opt = arith_cc(dc) && !dc->flags_uptodate; - move_opt = (dc->cc_op == CC_OP_MOVE); - switch (cond) { - case CC_EQ: - if ((arith_opt || move_opt) - && dc->cc_x_uptodate != (2 | X_FLAG)) { - tcg_gen_setcondi_tl(TCG_COND_EQ, cc, cc_result, 0); - } else { - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cc, - cpu_PR[PR_CCS], Z_FLAG); - } - break; - case CC_NE: - if ((arith_opt || move_opt) - && dc->cc_x_uptodate != (2 | X_FLAG)) { - tcg_gen_mov_tl(cc, cc_result); - } else { - cris_evaluate_flags(dc); - tcg_gen_xori_tl(cc, cpu_PR[PR_CCS], - Z_FLAG); - tcg_gen_andi_tl(cc, cc, Z_FLAG); - } - break; - case CC_CS: - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], C_FLAG); - break; - case CC_CC: - cris_evaluate_flags(dc); - tcg_gen_xori_tl(cc, cpu_PR[PR_CCS], C_FLAG); - tcg_gen_andi_tl(cc, cc, C_FLAG); - break; - case CC_VS: - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], V_FLAG); - break; - case CC_VC: - cris_evaluate_flags(dc); - tcg_gen_xori_tl(cc, cpu_PR[PR_CCS], - V_FLAG); - tcg_gen_andi_tl(cc, cc, V_FLAG); - break; - case CC_PL: - if (arith_opt || move_opt) { - int bits = 31; - - if (dc->cc_size == 1) { - bits = 7; - } else if (dc->cc_size == 2) { - bits = 15; - } - - tcg_gen_shri_tl(cc, cc_result, bits); - tcg_gen_xori_tl(cc, cc, 1); - } else { - cris_evaluate_flags(dc); - tcg_gen_xori_tl(cc, cpu_PR[PR_CCS], - N_FLAG); - tcg_gen_andi_tl(cc, cc, N_FLAG); - } - break; - case CC_MI: - if (arith_opt || move_opt) { - int bits = 31; - - if (dc->cc_size == 1) { - bits = 7; - } else if (dc->cc_size == 2) { - bits = 15; - } - - tcg_gen_shri_tl(cc, cc_result, bits); - tcg_gen_andi_tl(cc, cc, 1); - } else { - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], - N_FLAG); - } - break; - case CC_LS: - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], - C_FLAG | Z_FLAG); - break; - case CC_HI: - cris_evaluate_flags(dc); - { - TCGv tmp; - - tmp = tcg_temp_new(); - tcg_gen_xori_tl(tmp, cpu_PR[PR_CCS], - C_FLAG | Z_FLAG); - /* Overlay the C flag on top of the Z. */ - 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: - cris_evaluate_flags(dc); - /* Overlay the V flag on top of the N. */ - tcg_gen_shli_tl(cc, cpu_PR[PR_CCS], 2); - tcg_gen_xor_tl(cc, - cpu_PR[PR_CCS], cc); - tcg_gen_andi_tl(cc, cc, N_FLAG); - tcg_gen_xori_tl(cc, cc, N_FLAG); - break; - case CC_LT: - cris_evaluate_flags(dc); - /* Overlay the V flag on top of the N. */ - tcg_gen_shli_tl(cc, cpu_PR[PR_CCS], 2); - tcg_gen_xor_tl(cc, - cpu_PR[PR_CCS], cc); - tcg_gen_andi_tl(cc, cc, N_FLAG); - break; - case CC_GT: - cris_evaluate_flags(dc); - { - TCGv n, z; - - n = tcg_temp_new(); - z = tcg_temp_new(); - - /* To avoid a shift we overlay everything on - the V flag. */ - tcg_gen_shri_tl(n, cpu_PR[PR_CCS], 2); - tcg_gen_shri_tl(z, cpu_PR[PR_CCS], 1); - /* invert Z. */ - tcg_gen_xori_tl(z, z, 2); - - tcg_gen_xor_tl(n, n, cpu_PR[PR_CCS]); - 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: - cris_evaluate_flags(dc); - { - TCGv n, z; - - n = tcg_temp_new(); - z = tcg_temp_new(); - - /* To avoid a shift we overlay everything on - the V flag. */ - tcg_gen_shri_tl(n, cpu_PR[PR_CCS], 2); - tcg_gen_shri_tl(z, cpu_PR[PR_CCS], 1); - - 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: - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cc, cpu_PR[PR_CCS], P_FLAG); - break; - case CC_A: - tcg_gen_movi_tl(cc, 1); - break; - default: - BUG(); - break; - }; -} - -static void cris_store_direct_jmp(DisasContext *dc) -{ - /* Store the direct jmp state into the cpu-state. */ - if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { - if (dc->jmp == JMP_DIRECT) { - tcg_gen_movi_tl(env_btaken, 1); - } - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); - dc->jmp = JMP_INDIRECT; - } -} - -static void cris_prepare_cc_branch (DisasContext *dc, - int offset, int cond) -{ - /* This helps us re-schedule the micro-code to insns in delay-slots - before the actual jump. */ - dc->delayed_branch = 2; - dc->jmp = JMP_DIRECT_CC; - dc->jmp_pc = dc->pc + offset; - - gen_tst_cc(dc, env_btaken, cond); - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); -} - - -/* jumps, when the dest is in a live reg for example. Direct should be set - when the dest addr is constant to allow tb chaining. */ -static inline void cris_prepare_jmp (DisasContext *dc, unsigned int type) -{ - /* This helps us re-schedule the micro-code to insns in delay-slots - before the actual jump. */ - dc->delayed_branch = 2; - dc->jmp = type; - if (type == JMP_INDIRECT) { - tcg_gen_movi_tl(env_btaken, 1); - } -} - -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); -} - -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, - 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) { - cris_store_direct_jmp(dc); - } - - - /* Conditional writes. We only support the kind were X and P are known - at translation time. */ - if (dc->flags_x && (dc->tb_flags & P_FLAG)) { - dc->postinc = 0; - cris_evaluate_flags(dc); - tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], C_FLAG); - return; - } - - tcg_gen_qemu_st_tl(val, addr, mem_index, MO_TE + ctz32(size)); - - if (dc->flags_x) { - cris_evaluate_flags(dc); - tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~C_FLAG); - } -} - -static inline void t_gen_sext(TCGv d, TCGv s, int size) -{ - if (size == 1) { - tcg_gen_ext8s_i32(d, s); - } else if (size == 2) { - tcg_gen_ext16s_i32(d, s); - } else { - tcg_gen_mov_tl(d, s); - } -} - -static inline void t_gen_zext(TCGv d, TCGv s, int size) -{ - if (size == 1) { - tcg_gen_ext8u_i32(d, s); - } else if (size == 2) { - tcg_gen_ext16u_i32(d, s); - } else { - tcg_gen_mov_tl(d, s); - } -} - -#if DISAS_CRIS -static char memsize_char(int size) -{ - switch (size) { - case 1: return 'b'; - case 2: return 'w'; - case 4: return 'd'; - default: - return 'x'; - } -} -#endif - -static inline unsigned int memsize_z(DisasContext *dc) -{ - return dc->zsize + 1; -} - -static inline unsigned int memsize_zz(DisasContext *dc) -{ - switch (dc->zzsize) { - case 0: return 1; - case 1: return 2; - default: - return 4; - } -} - -static inline void do_postinc (DisasContext *dc, int size) -{ - if (dc->postinc) { - tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], size); - } -} - -static inline void dec_prep_move_r(DisasContext *dc, int rs, int rd, - int size, int s_ext, TCGv dst) -{ - if (s_ext) { - t_gen_sext(dst, cpu_R[rs], size); - } else { - t_gen_zext(dst, cpu_R[rs], size); - } -} - -/* Prepare T0 and T1 for a register alu operation. - s_ext decides if the operand1 should be sign-extended or zero-extended when - needed. */ -static void dec_prep_alu_r(DisasContext *dc, int rs, int rd, - int size, int s_ext, TCGv dst, TCGv src) -{ - dec_prep_move_r(dc, rs, rd, size, s_ext, src); - - if (s_ext) { - t_gen_sext(dst, cpu_R[rd], size); - } else { - t_gen_zext(dst, cpu_R[rd], size); - } -} - -static int dec_prep_move_m(CPUCRISState *env, DisasContext *dc, - int s_ext, int memsize, TCGv dst) -{ - unsigned int rs; - uint32_t imm; - int is_imm; - int insn_len = 2; - - rs = dc->op1; - is_imm = rs == 15 && dc->postinc; - - /* Load [$rs] onto T1. */ - if (is_imm) { - insn_len = 2 + memsize; - if (memsize == 1) { - insn_len++; - } - - imm = cris_fetch(env, dc, dc->pc + 2, memsize, s_ext); - tcg_gen_movi_tl(dst, imm); - dc->postinc = 0; - } else { - cris_flush_cc_state(dc); - gen_load(dc, dst, cpu_R[rs], memsize, 0); - if (s_ext) { - t_gen_sext(dst, dst, memsize); - } else { - t_gen_zext(dst, dst, memsize); - } - } - return insn_len; -} - -/* Prepare T0 and T1 for a memory + alu operation. - s_ext decides if the operand1 should be sign-extended or zero-extended when - needed. */ -static int dec_prep_alu_m(CPUCRISState *env, DisasContext *dc, - int s_ext, int memsize, TCGv dst, TCGv src) -{ - int insn_len; - - insn_len = dec_prep_move_m(env, dc, s_ext, memsize, src); - tcg_gen_mov_tl(dst, cpu_R[dc->op2]); - return insn_len; -} - -#if DISAS_CRIS -static const char *cc_name(int cc) -{ - static const char * const cc_names[16] = { - "cc", "cs", "ne", "eq", "vc", "vs", "pl", "mi", - "ls", "hi", "ge", "lt", "gt", "le", "a", "p" - }; - assert(cc < 16); - return cc_names[cc]; -} -#endif - -/* Start of insn decoders. */ - -static int dec_bccq(CPUCRISState *env, DisasContext *dc) -{ - int32_t offset; - int sign; - uint32_t cond = dc->op2; - - offset = EXTRACT_FIELD(dc->ir, 1, 7); - sign = EXTRACT_FIELD(dc->ir, 0, 0); - - offset *= 2; - offset |= sign << 8; - offset = sign_extend(offset, 8); - - LOG_DIS("b%s %x\n", cc_name(cond), dc->pc + offset); - - /* op2 holds the condition-code. */ - cris_cc_mask(dc, 0); - cris_prepare_cc_branch(dc, offset, cond); - return 2; -} -static int dec_addoq(CPUCRISState *env, DisasContext *dc) -{ - int32_t imm; - - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 7); - imm = sign_extend(dc->op1, 7); - - LOG_DIS("addoq %d, $r%u\n", imm, dc->op2); - cris_cc_mask(dc, 0); - /* Fetch register operand, */ - tcg_gen_addi_tl(cpu_R[R_ACR], cpu_R[dc->op2], imm); - - return 2; -} -static int dec_addq(CPUCRISState *env, DisasContext *dc) -{ - TCGv c; - LOG_DIS("addq %u, $r%u\n", dc->op1, dc->op2); - - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); - - cris_cc_mask(dc, CC_MASK_NZVC); - - c = tcg_const_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) -{ - uint32_t imm; - - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); - imm = sign_extend(dc->op1, 5); - LOG_DIS("moveq %d, $r%u\n", imm, dc->op2); - - tcg_gen_movi_tl(cpu_R[dc->op2], imm); - return 2; -} -static int dec_subq(CPUCRISState *env, DisasContext *dc) -{ - TCGv c; - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); - - LOG_DIS("subq %u, $r%u\n", dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_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) -{ - uint32_t imm; - TCGv c; - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); - imm = sign_extend(dc->op1, 5); - - LOG_DIS("cmpq %d, $r%d\n", imm, dc->op2); - cris_cc_mask(dc, CC_MASK_NZVC); - - c = tcg_const_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) -{ - uint32_t imm; - TCGv c; - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); - imm = sign_extend(dc->op1, 5); - - LOG_DIS("andq %d, $r%d\n", imm, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - - c = tcg_const_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) -{ - uint32_t imm; - TCGv c; - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); - imm = sign_extend(dc->op1, 5); - LOG_DIS("orq %d, $r%d\n", imm, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - - c = tcg_const_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) -{ - TCGv c; - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); - LOG_DIS("btstq %u, $r%d\n", dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - c = tcg_const_tl(dc->op1); - cris_evaluate_flags(dc); - gen_helper_btst(cpu_PR[PR_CCS], cpu_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); - dc->flags_uptodate = 1; - return 2; -} -static int dec_asrq(CPUCRISState *env, DisasContext *dc) -{ - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); - LOG_DIS("asrq %u, $r%d\n", dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - - tcg_gen_sari_tl(cpu_R[dc->op2], cpu_R[dc->op2], dc->op1); - cris_alu(dc, CC_OP_MOVE, - cpu_R[dc->op2], - cpu_R[dc->op2], cpu_R[dc->op2], 4); - return 2; -} -static int dec_lslq(CPUCRISState *env, DisasContext *dc) -{ - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); - LOG_DIS("lslq %u, $r%d\n", dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - - tcg_gen_shli_tl(cpu_R[dc->op2], cpu_R[dc->op2], dc->op1); - - cris_alu(dc, CC_OP_MOVE, - cpu_R[dc->op2], - cpu_R[dc->op2], cpu_R[dc->op2], 4); - return 2; -} -static int dec_lsrq(CPUCRISState *env, DisasContext *dc) -{ - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); - LOG_DIS("lsrq %u, $r%d\n", dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - - tcg_gen_shri_tl(cpu_R[dc->op2], cpu_R[dc->op2], dc->op1); - cris_alu(dc, CC_OP_MOVE, - cpu_R[dc->op2], - cpu_R[dc->op2], cpu_R[dc->op2], 4); - return 2; -} - -static int dec_move_r(CPUCRISState *env, DisasContext *dc) -{ - int size = memsize_zz(dc); - - LOG_DIS("move.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - if (size == 4) { - dec_prep_move_r(dc, dc->op1, dc->op2, size, 0, cpu_R[dc->op2]); - cris_cc_mask(dc, CC_MASK_NZ); - cris_update_cc_op(dc, CC_OP_MOVE, 4); - cris_update_cc_x(dc); - cris_update_result(dc, cpu_R[dc->op2]); - } else { - TCGv t0; - - 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, size); - tcg_temp_free(t0); - } - return 2; -} - -static int dec_scc_r(CPUCRISState *env, DisasContext *dc) -{ - int cond = dc->op2; - - LOG_DIS("s%s $r%u\n", - cc_name(cond), dc->op1); - - gen_tst_cc(dc, cpu_R[dc->op1], cond); - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_R[dc->op1], cpu_R[dc->op1], 0); - - cris_cc_mask(dc, 0); - return 2; -} - -static inline void cris_alu_alloc_temps(DisasContext *dc, int size, TCGv *t) -{ - if (size == 4) { - t[0] = cpu_R[dc->op2]; - t[1] = cpu_R[dc->op1]; - } else { - t[0] = tcg_temp_new(); - t[1] = tcg_temp_new(); - } -} - -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]; - int size = memsize_zz(dc); - - LOG_DIS("and.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - - 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; -} - -static int dec_lz_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - LOG_DIS("lz $r%u, $r%u\n", - dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - 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; -} - -static int dec_lsl_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - - LOG_DIS("lsl.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - cris_alu_alloc_temps(dc, size, t); - 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; -} - -static int dec_lsr_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - - LOG_DIS("lsr.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - cris_alu_alloc_temps(dc, size, t); - 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; -} - -static int dec_asr_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - - LOG_DIS("asr.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - cris_alu_alloc_temps(dc, size, t); - 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; -} - -static int dec_muls_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - - LOG_DIS("muls.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZV); - cris_alu_alloc_temps(dc, size, t); - 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; -} - -static int dec_mulu_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - - LOG_DIS("mulu.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZV); - 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_MULU, cpu_R[dc->op2], t[0], t[1], 4); - cris_alu_free_temps(dc, size, t); - return 2; -} - - -static int dec_dstep_r(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("dstep $r%u, $r%u\n", dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - cris_alu(dc, CC_OP_DSTEP, - cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op1], 4); - return 2; -} - -static int dec_xor_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - LOG_DIS("xor.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - BUG_ON(size != 4); /* xor is dword. */ - cris_cc_mask(dc, CC_MASK_NZ); - 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_XOR, cpu_R[dc->op2], t[0], t[1], 4); - cris_alu_free_temps(dc, size, t); - return 2; -} - -static int dec_bound_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv l0; - int size = memsize_zz(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(); - 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; -} - -static int dec_cmp_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - LOG_DIS("cmp.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZVC); - 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_CMP, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); - return 2; -} - -static int dec_abs_r(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("abs $r%u, $r%u\n", - dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - - tcg_gen_abs_tl(cpu_R[dc->op2], cpu_R[dc->op1]); - cris_alu(dc, CC_OP_MOVE, - cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op2], 4); - return 2; -} - -static int dec_add_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - LOG_DIS("add.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZVC); - 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_ADD, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); - return 2; -} - -static int dec_addc_r(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("addc $r%u, $r%u\n", - dc->op1, dc->op2); - cris_evaluate_flags(dc); - - /* Set for this insn. */ - dc->flags_x = X_FLAG; - - cris_cc_mask(dc, CC_MASK_NZVC); - cris_alu(dc, CC_OP_ADDC, - cpu_R[dc->op2], cpu_R[dc->op2], cpu_R[dc->op1], 4); - return 2; -} - -static int dec_mcp_r(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("mcp $p%u, $r%u\n", - dc->op2, dc->op1); - cris_evaluate_flags(dc); - cris_cc_mask(dc, CC_MASK_RNZV); - cris_alu(dc, CC_OP_MCP, - cpu_R[dc->op1], cpu_R[dc->op1], cpu_PR[dc->op2], 4); - return 2; -} - -#if DISAS_CRIS -static char * swapmode_name(int mode, char *modename) { - int i = 0; - if (mode & 8) { - modename[i++] = 'n'; - } - if (mode & 4) { - modename[i++] = 'w'; - } - if (mode & 2) { - modename[i++] = 'b'; - } - if (mode & 1) { - modename[i++] = 'r'; - } - modename[i++] = 0; - return modename; -} -#endif - -static int dec_swap_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; -#if DISAS_CRIS - char modename[4]; -#endif - LOG_DIS("swap%s $r%u\n", - swapmode_name(dc->op2, modename), dc->op1); - - cris_cc_mask(dc, CC_MASK_NZ); - t0 = tcg_temp_new(); - tcg_gen_mov_tl(t0, cpu_R[dc->op1]); - if (dc->op2 & 8) { - tcg_gen_not_tl(t0, t0); - } - if (dc->op2 & 4) { - t_gen_swapw(t0, t0); - } - if (dc->op2 & 2) { - t_gen_swapb(t0, t0); - } - if (dc->op2 & 1) { - 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; -} - -static int dec_or_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - LOG_DIS("or.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZ); - 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; -} - -static int dec_addi_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - LOG_DIS("addi.%c $r%u, $r%u\n", - memsize_char(memsize_zz(dc)), dc->op2, dc->op1); - cris_cc_mask(dc, 0); - 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; -} - -static int dec_addi_acr(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - LOG_DIS("addi.%c $r%u, $r%u, $acr\n", - memsize_char(memsize_zz(dc)), dc->op2, dc->op1); - cris_cc_mask(dc, 0); - 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; -} - -static int dec_neg_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - LOG_DIS("neg.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZVC); - 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_NEG, cpu_R[dc->op2], t[0], t[1], size); - cris_alu_free_temps(dc, size, t); - return 2; -} - -static int dec_btst_r(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("btst $r%u, $r%u\n", - 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], - 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); - cris_update_cc_op(dc, CC_OP_FLAGS, 4); - dc->flags_uptodate = 1; - return 2; -} - -static int dec_sub_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int size = memsize_zz(dc); - LOG_DIS("sub.%c $r%u, $r%u\n", - memsize_char(size), dc->op1, dc->op2); - cris_cc_mask(dc, CC_MASK_NZVC); - 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; -} - -/* Zero extension. From size to dword. */ -static int dec_movu_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int size = memsize_z(dc); - LOG_DIS("movu.%c $r%u, $r%u\n", - memsize_char(size), - dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - 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; -} - -/* Sign extension. From size to dword. */ -static int dec_movs_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int size = memsize_z(dc); - LOG_DIS("movs.%c $r%u, $r%u\n", - memsize_char(size), - dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZ); - t0 = tcg_temp_new(); - /* Size can only be qi or hi. */ - 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; -} - -/* zero extension. From size to dword. */ -static int dec_addu_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int size = memsize_z(dc); - LOG_DIS("addu.%c $r%u, $r%u\n", - memsize_char(size), - dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZVC); - t0 = tcg_temp_new(); - /* 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; -} - -/* Sign extension. From size to dword. */ -static int dec_adds_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int size = memsize_z(dc); - LOG_DIS("adds.%c $r%u, $r%u\n", - memsize_char(size), - dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZVC); - t0 = tcg_temp_new(); - /* Size can only be qi or hi. */ - 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; -} - -/* Zero extension. From size to dword. */ -static int dec_subu_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int size = memsize_z(dc); - LOG_DIS("subu.%c $r%u, $r%u\n", - memsize_char(size), - dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZVC); - t0 = tcg_temp_new(); - /* Size can only be qi or hi. */ - 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; -} - -/* Sign extension. From size to dword. */ -static int dec_subs_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int size = memsize_z(dc); - LOG_DIS("subs.%c $r%u, $r%u\n", - memsize_char(size), - dc->op1, dc->op2); - - cris_cc_mask(dc, CC_MASK_NZVC); - t0 = tcg_temp_new(); - /* Size can only be qi or hi. */ - 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; -} - -static int dec_setclrf(CPUCRISState *env, DisasContext *dc) -{ - uint32_t flags; - int set = (~dc->opcode >> 2) & 1; - - - flags = (EXTRACT_FIELD(dc->ir, 12, 15) << 4) - | EXTRACT_FIELD(dc->ir, 0, 3); - if (set && flags == 0) { - LOG_DIS("nop\n"); - return 2; - } else if (!set && (flags & 0x20)) { - LOG_DIS("di\n"); - } else { - LOG_DIS("%sf %x\n", set ? "set" : "clr", flags); - } - - /* User space is not allowed to touch these. Silently ignore. */ - if (dc->tb_flags & U_FLAG) { - flags &= ~(S_FLAG | I_FLAG | U_FLAG); - } - - if (flags & X_FLAG) { - if (set) { - dc->flags_x = X_FLAG; - } else { - dc->flags_x = 0; - } - } - - /* Break the TB if any of the SPI flag changes. */ - if (flags & (P_FLAG | S_FLAG)) { - tcg_gen_movi_tl(env_pc, dc->pc + 2); - dc->base.is_jmp = DISAS_UPDATE; - dc->cpustate_changed = 1; - } - - /* For the I flag, only act on posedge. */ - if ((flags & I_FLAG)) { - tcg_gen_movi_tl(env_pc, dc->pc + 2); - dc->base.is_jmp = DISAS_UPDATE; - dc->cpustate_changed = 1; - } - - - /* Simply decode the flags. */ - cris_evaluate_flags(dc); - cris_update_cc_op(dc, CC_OP_FLAGS, 4); - cris_update_cc_x(dc); - tcg_gen_movi_tl(cc_op, dc->cc_op); - - if (set) { - if (!(dc->tb_flags & U_FLAG) && (flags & U_FLAG)) { - /* Enter user mode. */ - t_gen_mov_env_TN(ksp, cpu_R[R_SP]); - tcg_gen_mov_tl(cpu_R[R_SP], cpu_PR[PR_USP]); - dc->cpustate_changed = 1; - } - tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags); - } else { - tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~flags); - } - - dc->flags_uptodate = 1; - dc->clear_x = 0; - return 2; -} - -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); - cris_cc_mask(dc, 0); - gen_helper_movl_sreg_reg(cpu_env, c2, c1); - tcg_temp_free(c1); - tcg_temp_free(c2); - 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); - cris_cc_mask(dc, 0); - gen_helper_movl_reg_sreg(cpu_env, c1, c2); - tcg_temp_free(c1); - tcg_temp_free(c2); - return 2; -} - -static int dec_move_rp(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - LOG_DIS("move $r%u, $p%u\n", dc->op1, dc->op2); - cris_cc_mask(dc, 0); - - t[0] = tcg_temp_new(); - if (dc->op2 == PR_CCS) { - cris_evaluate_flags(dc); - tcg_gen_mov_tl(t[0], cpu_R[dc->op1]); - if (dc->tb_flags & U_FLAG) { - t[1] = tcg_temp_new(); - /* User space is not allowed to touch all flags. */ - 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]); - } - - t_gen_mov_preg_TN(dc, dc->op2, t[0]); - if (dc->op2 == PR_CCS) { - 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) -{ - TCGv t0; - LOG_DIS("move $p%u, $r%u\n", dc->op2, dc->op1); - cris_cc_mask(dc, 0); - - if (dc->op2 == PR_CCS) { - cris_evaluate_flags(dc); - } - - if (dc->op2 == PR_DZ) { - tcg_gen_movi_tl(cpu_R[dc->op1], 0); - } else { - t0 = tcg_temp_new(); - t_gen_mov_TN_preg(t0, dc->op2); - 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; -} - -static int dec_move_mr(CPUCRISState *env, DisasContext *dc) -{ - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("move.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - if (memsize == 4) { - insn_len = dec_prep_move_m(env, dc, 0, 4, cpu_R[dc->op2]); - cris_cc_mask(dc, CC_MASK_NZ); - cris_update_cc_op(dc, CC_OP_MOVE, 4); - cris_update_cc_x(dc); - cris_update_result(dc, cpu_R[dc->op2]); - } else { - TCGv t0; - - t0 = tcg_temp_new(); - insn_len = dec_prep_move_m(env, dc, 0, memsize, t0); - 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; -} - -static inline void cris_alu_m_alloc_temps(TCGv *t) -{ - t[0] = tcg_temp_new(); - 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]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("movs.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - /* sign extend. */ - insn_len = dec_prep_alu_m(env, dc, 1, memsize, t[0], t[1]); - 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; -} - -static int dec_addu_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("addu.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - /* sign extend. */ - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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; -} - -static int dec_adds_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("adds.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - /* sign extend. */ - insn_len = dec_prep_alu_m(env, dc, 1, memsize, t[0], t[1]); - 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; -} - -static int dec_subu_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("subu.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - /* sign extend. */ - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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; -} - -static int dec_subs_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("subs.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - /* sign extend. */ - insn_len = dec_prep_alu_m(env, dc, 1, memsize, t[0], t[1]); - 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; -} - -static int dec_movu_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - - LOG_DIS("movu.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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; -} - -static int dec_cmpu_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("cmpu.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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; -} - -static int dec_cmps_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_z(dc); - int insn_len; - LOG_DIS("cmps.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 1, memsize, t[0], t[1]); - cris_cc_mask(dc, CC_MASK_NZVC); - cris_alu(dc, CC_OP_CMP, - 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; -} - -static int dec_cmp_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("cmp.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - cris_cc_mask(dc, CC_MASK_NZVC); - cris_alu(dc, CC_OP_CMP, - 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; -} - -static int dec_test_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2], c; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("test.%c [$r%u%s] op2=%x\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_evaluate_flags(dc); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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); - 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; -} - -static int dec_and_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("and.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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; -} - -static int dec_add_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("add.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - cris_cc_mask(dc, CC_MASK_NZVC); - 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; -} - -static int dec_addo_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("add.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 1, memsize, t[0], t[1]); - 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; -} - -static int dec_bound_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv l[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("bound.%c [$r%u%s, $r%u\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - l[0] = tcg_temp_local_new(); - l[1] = tcg_temp_local_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; -} - -static int dec_addc_mr(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int insn_len = 2; - LOG_DIS("addc [$r%u%s, $r%u\n", - dc->op1, dc->postinc ? "+]" : "]", - dc->op2); - - cris_evaluate_flags(dc); - - /* Set for this insn. */ - dc->flags_x = X_FLAG; - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, 4, t[0], t[1]); - 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; -} - -static int dec_sub_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("sub.%c [$r%u%s, $r%u ir=%x zz=%x\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2, dc->ir, dc->zzsize); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - 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; -} - -static int dec_or_m(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len; - LOG_DIS("or.%c [$r%u%s, $r%u pc=%x\n", - memsize_char(memsize), - dc->op1, dc->postinc ? "+]" : "]", - dc->op2, dc->pc); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - cris_cc_mask(dc, CC_MASK_NZ); - 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; -} - -static int dec_move_mp(CPUCRISState *env, DisasContext *dc) -{ - TCGv t[2]; - int memsize = memsize_zz(dc); - int insn_len = 2; - - LOG_DIS("move.%c [$r%u%s, $p%u\n", - memsize_char(memsize), - dc->op1, - dc->postinc ? "+]" : "]", - dc->op2); - - cris_alu_m_alloc_temps(t); - insn_len = dec_prep_alu_m(env, dc, 0, memsize, t[0], t[1]); - cris_cc_mask(dc, 0); - if (dc->op2 == PR_CCS) { - cris_evaluate_flags(dc); - if (dc->tb_flags & U_FLAG) { - /* User space is not allowed to touch all flags. */ - tcg_gen_andi_tl(t[1], t[1], 0x39f); - tcg_gen_andi_tl(t[0], cpu_PR[PR_CCS], ~0x39f); - tcg_gen_or_tl(t[1], t[0], t[1]); - } - } - - t_gen_mov_preg_TN(dc, dc->op2, t[1]); - - do_postinc(dc, memsize); - cris_alu_m_free_temps(t); - return insn_len; -} - -static int dec_move_pm(CPUCRISState *env, DisasContext *dc) -{ - TCGv t0; - int memsize; - - memsize = preg_sizes[dc->op2]; - - LOG_DIS("move.%c $p%u, [$r%u%s\n", - memsize_char(memsize), - dc->op2, dc->op1, dc->postinc ? "+]" : "]"); - - /* prepare store. Address in T0, value in T1. */ - if (dc->op2 == PR_CCS) { - cris_evaluate_flags(dc); - } - t0 = tcg_temp_new(); - 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) { - tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], memsize); - } - return 2; -} - -static int dec_movem_mr(CPUCRISState *env, DisasContext *dc) -{ - TCGv_i64 tmp[16]; - TCGv tmp32; - TCGv addr; - int i; - int nr = dc->op2 + 1; - - LOG_DIS("movem [$r%u%s, $r%u\n", dc->op1, - dc->postinc ? "+]" : "]", dc->op2); - - addr = tcg_temp_new(); - /* There are probably better ways of doing this. */ - cris_flush_cc_state(dc); - for (i = 0; i < (nr >> 1); i++) { - tmp[i] = tcg_temp_new_i64(); - tcg_gen_addi_tl(addr, cpu_R[dc->op1], i * 8); - gen_load64(dc, tmp[i], addr); - } - if (nr & 1) { - tmp32 = tcg_temp_new_i32(); - tcg_gen_addi_tl(addr, cpu_R[dc->op1], i * 8); - gen_load(dc, tmp32, addr, 4, 0); - } 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. */ - if (dc->postinc) { - tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], nr * 4); - } - - /* gen_load might want to evaluate the previous insns flags. */ - cris_cc_mask(dc, 0); - return 2; -} - -static int dec_movem_rm(CPUCRISState *env, DisasContext *dc) -{ - TCGv tmp; - TCGv addr; - int i; - - LOG_DIS("movem $r%u, [$r%u%s\n", dc->op2, dc->op1, - dc->postinc ? "+]" : "]"); - - cris_flush_cc_state(dc); - - tmp = tcg_temp_new(); - addr = tcg_temp_new(); - tcg_gen_movi_tl(tmp, 4); - tcg_gen_mov_tl(addr, cpu_R[dc->op1]); - for (i = 0; i <= dc->op2; i++) { - /* Displace addr. */ - /* Perform the store. */ - gen_store(dc, addr, cpu_R[i], 4); - tcg_gen_add_tl(addr, addr, tmp); - } - if (dc->postinc) { - tcg_gen_mov_tl(cpu_R[dc->op1], addr); - } - cris_cc_mask(dc, 0); - tcg_temp_free(tmp); - tcg_temp_free(addr); - return 2; -} - -static int dec_move_rm(CPUCRISState *env, DisasContext *dc) -{ - int memsize; - - memsize = memsize_zz(dc); - - LOG_DIS("move.%c $r%u, [$r%u]\n", - memsize_char(memsize), dc->op2, dc->op1); - - /* prepare store. */ - cris_flush_cc_state(dc); - gen_store(dc, cpu_R[dc->op1], cpu_R[dc->op2], memsize); - - if (dc->postinc) { - tcg_gen_addi_tl(cpu_R[dc->op1], cpu_R[dc->op1], memsize); - } - cris_cc_mask(dc, 0); - return 2; -} - -static int dec_lapcq(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("lapcq %x, $r%u\n", - dc->pc + dc->op1*2, dc->op2); - cris_cc_mask(dc, 0); - tcg_gen_movi_tl(cpu_R[dc->op2], dc->pc + dc->op1 * 2); - return 2; -} - -static int dec_lapc_im(CPUCRISState *env, DisasContext *dc) -{ - unsigned int rd; - int32_t imm; - int32_t pc; - - rd = dc->op2; - - cris_cc_mask(dc, 0); - imm = cris_fetch(env, dc, dc->pc + 2, 4, 0); - LOG_DIS("lapc 0x%x, $r%u\n", imm + dc->pc, dc->op2); - - pc = dc->pc; - pc += imm; - tcg_gen_movi_tl(cpu_R[rd], pc); - return 6; -} - -/* Jump to special reg. */ -static int dec_jump_p(CPUCRISState *env, DisasContext *dc) -{ - LOG_DIS("jump $p%u\n", dc->op2); - - if (dc->op2 == PR_CCS) { - cris_evaluate_flags(dc); - } - t_gen_mov_TN_preg(env_btarget, dc->op2); - /* rete will often have low bit set to indicate delayslot. */ - tcg_gen_andi_tl(env_btarget, env_btarget, ~1); - cris_cc_mask(dc, 0); - cris_prepare_jmp(dc, JMP_INDIRECT); - return 2; -} - -/* Jump and save. */ -static int dec_jas_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv c; - LOG_DIS("jas $r%u, $p%u\n", dc->op1, dc->op2); - cris_cc_mask(dc, 0); - /* Store the return address in Pd. */ - tcg_gen_mov_tl(env_btarget, cpu_R[dc->op1]); - if (dc->op2 > 15) { - abort(); - } - c = tcg_const_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; -} - -static int dec_jas_im(CPUCRISState *env, DisasContext *dc) -{ - uint32_t imm; - TCGv c; - - imm = cris_fetch(env, dc, dc->pc + 2, 4, 0); - - LOG_DIS("jas 0x%x\n", imm); - cris_cc_mask(dc, 0); - c = tcg_const_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); - return 6; -} - -static int dec_jasc_im(CPUCRISState *env, DisasContext *dc) -{ - uint32_t imm; - TCGv c; - - imm = cris_fetch(env, dc, dc->pc + 2, 4, 0); - - LOG_DIS("jasc 0x%x\n", imm); - cris_cc_mask(dc, 0); - c = tcg_const_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); - return 6; -} - -static int dec_jasc_r(CPUCRISState *env, DisasContext *dc) -{ - TCGv c; - LOG_DIS("jasc_r $r%u, $p%u\n", dc->op1, dc->op2); - 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); - t_gen_mov_preg_TN(dc, dc->op2, c); - tcg_temp_free(c); - cris_prepare_jmp(dc, JMP_INDIRECT); - return 2; -} - -static int dec_bcc_im(CPUCRISState *env, DisasContext *dc) -{ - int32_t offset; - uint32_t cond = dc->op2; - - offset = cris_fetch(env, dc, dc->pc + 2, 2, 1); - - LOG_DIS("b%s %d pc=%x dst=%x\n", - cc_name(cond), offset, - dc->pc, dc->pc + offset); - - cris_cc_mask(dc, 0); - /* op2 holds the condition-code. */ - cris_prepare_cc_branch(dc, offset, cond); - return 4; -} - -static int dec_bas_im(CPUCRISState *env, DisasContext *dc) -{ - int32_t simm; - TCGv c; - - simm = cris_fetch(env, dc, dc->pc + 2, 4, 0); - - 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); - /* 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); - return 6; -} - -static int dec_basc_im(CPUCRISState *env, DisasContext *dc) -{ - int32_t simm; - TCGv c; - simm = cris_fetch(env, dc, dc->pc + 2, 4, 0); - - 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); - /* 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); - return 6; -} - -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, - -offsetof(CRISCPU, env) + offsetof(CPUState, halted)); - tcg_gen_movi_tl(env_pc, dc->pc + 2); - t_gen_raise_exception(EXCP_HLT); - dc->base.is_jmp = DISAS_NORETURN; - return 2; - } - - switch (dc->op2 & 7) { - case 2: - /* rfe. */ - LOG_DIS("rfe\n"); - cris_evaluate_flags(dc); - gen_helper_rfe(cpu_env); - dc->base.is_jmp = DISAS_UPDATE; - dc->cpustate_changed = true; - break; - case 5: - /* rfn. */ - LOG_DIS("rfn\n"); - cris_evaluate_flags(dc); - gen_helper_rfn(cpu_env); - dc->base.is_jmp = DISAS_UPDATE; - dc->cpustate_changed = true; - break; - case 6: - LOG_DIS("break %d\n", dc->op1); - cris_evaluate_flags(dc); - /* break. */ - tcg_gen_movi_tl(env_pc, dc->pc + 2); - - /* Breaks start at 16 in the exception vector. */ - t_gen_movi_env_TN(trap_vector, dc->op1 + 16); - t_gen_raise_exception(EXCP_BREAK); - dc->base.is_jmp = DISAS_NORETURN; - break; - default: - printf("op2=%x\n", dc->op2); - BUG(); - break; - - } - return 2; -} - -static int dec_ftag_fidx_d_m(CPUCRISState *env, DisasContext *dc) -{ - return 2; -} - -static int dec_ftag_fidx_i_m(CPUCRISState *env, DisasContext *dc) -{ - return 2; -} - -static int dec_null(CPUCRISState *env, DisasContext *dc) -{ - printf("unknown insn pc=%x opc=%x op1=%x op2=%x\n", - dc->pc, dc->opcode, dc->op1, dc->op2); - fflush(NULL); - BUG(); - return 2; -} - -static const struct decoder_info { - struct { - uint32_t bits; - uint32_t mask; - }; - int (*dec)(CPUCRISState *env, DisasContext *dc); -} decinfo[] = { - /* Order matters here. */ - {DEC_MOVEQ, dec_moveq}, - {DEC_BTSTQ, dec_btstq}, - {DEC_CMPQ, dec_cmpq}, - {DEC_ADDOQ, dec_addoq}, - {DEC_ADDQ, dec_addq}, - {DEC_SUBQ, dec_subq}, - {DEC_ANDQ, dec_andq}, - {DEC_ORQ, dec_orq}, - {DEC_ASRQ, dec_asrq}, - {DEC_LSLQ, dec_lslq}, - {DEC_LSRQ, dec_lsrq}, - {DEC_BCCQ, dec_bccq}, - - {DEC_BCC_IM, dec_bcc_im}, - {DEC_JAS_IM, dec_jas_im}, - {DEC_JAS_R, dec_jas_r}, - {DEC_JASC_IM, dec_jasc_im}, - {DEC_JASC_R, dec_jasc_r}, - {DEC_BAS_IM, dec_bas_im}, - {DEC_BASC_IM, dec_basc_im}, - {DEC_JUMP_P, dec_jump_p}, - {DEC_LAPC_IM, dec_lapc_im}, - {DEC_LAPCQ, dec_lapcq}, - - {DEC_RFE_ETC, dec_rfe_etc}, - {DEC_ADDC_MR, dec_addc_mr}, - - {DEC_MOVE_MP, dec_move_mp}, - {DEC_MOVE_PM, dec_move_pm}, - {DEC_MOVEM_MR, dec_movem_mr}, - {DEC_MOVEM_RM, dec_movem_rm}, - {DEC_MOVE_PR, dec_move_pr}, - {DEC_SCC_R, dec_scc_r}, - {DEC_SETF, dec_setclrf}, - {DEC_CLEARF, dec_setclrf}, - - {DEC_MOVE_SR, dec_move_sr}, - {DEC_MOVE_RP, dec_move_rp}, - {DEC_SWAP_R, dec_swap_r}, - {DEC_ABS_R, dec_abs_r}, - {DEC_LZ_R, dec_lz_r}, - {DEC_MOVE_RS, dec_move_rs}, - {DEC_BTST_R, dec_btst_r}, - {DEC_ADDC_R, dec_addc_r}, - - {DEC_DSTEP_R, dec_dstep_r}, - {DEC_XOR_R, dec_xor_r}, - {DEC_MCP_R, dec_mcp_r}, - {DEC_CMP_R, dec_cmp_r}, - - {DEC_ADDI_R, dec_addi_r}, - {DEC_ADDI_ACR, dec_addi_acr}, - - {DEC_ADD_R, dec_add_r}, - {DEC_SUB_R, dec_sub_r}, - - {DEC_ADDU_R, dec_addu_r}, - {DEC_ADDS_R, dec_adds_r}, - {DEC_SUBU_R, dec_subu_r}, - {DEC_SUBS_R, dec_subs_r}, - {DEC_LSL_R, dec_lsl_r}, - - {DEC_AND_R, dec_and_r}, - {DEC_OR_R, dec_or_r}, - {DEC_BOUND_R, dec_bound_r}, - {DEC_ASR_R, dec_asr_r}, - {DEC_LSR_R, dec_lsr_r}, - - {DEC_MOVU_R, dec_movu_r}, - {DEC_MOVS_R, dec_movs_r}, - {DEC_NEG_R, dec_neg_r}, - {DEC_MOVE_R, dec_move_r}, - - {DEC_FTAG_FIDX_I_M, dec_ftag_fidx_i_m}, - {DEC_FTAG_FIDX_D_M, dec_ftag_fidx_d_m}, - - {DEC_MULS_R, dec_muls_r}, - {DEC_MULU_R, dec_mulu_r}, - - {DEC_ADDU_M, dec_addu_m}, - {DEC_ADDS_M, dec_adds_m}, - {DEC_SUBU_M, dec_subu_m}, - {DEC_SUBS_M, dec_subs_m}, - - {DEC_CMPU_M, dec_cmpu_m}, - {DEC_CMPS_M, dec_cmps_m}, - {DEC_MOVU_M, dec_movu_m}, - {DEC_MOVS_M, dec_movs_m}, - - {DEC_CMP_M, dec_cmp_m}, - {DEC_ADDO_M, dec_addo_m}, - {DEC_BOUND_M, dec_bound_m}, - {DEC_ADD_M, dec_add_m}, - {DEC_SUB_M, dec_sub_m}, - {DEC_AND_M, dec_and_m}, - {DEC_OR_M, dec_or_m}, - {DEC_MOVE_RM, dec_move_rm}, - {DEC_TEST_M, dec_test_m}, - {DEC_MOVE_MR, dec_move_mr}, - - {{0, 0}, dec_null} -}; - -static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) -{ - int insn_len = 2; - int i; - - /* Load a halfword onto the instruction register. */ - dc->ir = cris_fetch(env, dc, dc->pc, 2, 0); - - /* Now decode it. */ - dc->opcode = EXTRACT_FIELD(dc->ir, 4, 11); - dc->op1 = EXTRACT_FIELD(dc->ir, 0, 3); - dc->op2 = EXTRACT_FIELD(dc->ir, 12, 15); - dc->zsize = EXTRACT_FIELD(dc->ir, 4, 4); - dc->zzsize = EXTRACT_FIELD(dc->ir, 4, 5); - dc->postinc = EXTRACT_FIELD(dc->ir, 10, 10); - - /* Large switch for all insns. */ - for (i = 0; i < ARRAY_SIZE(decinfo); i++) { - if ((dc->opcode & decinfo[i].mask) == decinfo[i].bits) { - insn_len = decinfo[i].dec(env, dc); - break; - } - } - -#if !defined(CONFIG_USER_ONLY) - /* Single-stepping ? */ - if (dc->tb_flags & S_FLAG) { - TCGLabel *l1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_PR[PR_SPC], dc->pc, l1); - /* We treat SPC as a break with an odd trap vector. */ - cris_evaluate_flags(dc); - t_gen_movi_env_TN(trap_vector, 3); - tcg_gen_movi_tl(env_pc, dc->pc + insn_len); - tcg_gen_movi_tl(cpu_PR[PR_SPC], dc->pc + insn_len); - t_gen_raise_exception(EXCP_BREAK); - gen_set_label(l1); - } -#endif - return insn_len; -} - -#include "translate_v10.c.inc" - -/* - * Delay slots on QEMU/CRIS. - * - * If an exception hits on a delayslot, the core will let ERP (the Exception - * Return Pointer) point to the branch (the previous) insn and set the lsb to - * to give SW a hint that the exception actually hit on the dslot. - * - * CRIS expects all PC addresses to be 16-bit aligned. The lsb is ignored by - * the core and any jmp to an odd addresses will mask off that lsb. It is - * simply there to let sw know there was an exception on a dslot. - * - * When the software returns from an exception, the branch will re-execute. - * 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 - * 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 - * 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 - * insn ahead. - * - * When exceptions occur, we check for env->dslot in do_interrupt to detect - * broken branch sequences and setup $erp accordingly (i.e let it point to the - * branch and set lsb). Then env->dslot gets cleared so that the exception - * handler can enter. When returning from exceptions (jump $erp) the lsb gets - * masked off and we will reexecute the branch insn. - * - */ - -static void cris_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUCRISState *env = cs->env_ptr; - uint32_t tb_flags = dc->base.tb->flags; - uint32_t pc_start; - - if (env->pregs[PR_VR] == 32) { - dc->decoder = crisv32_decoder; - dc->clear_locked_irq = 0; - } else { - dc->decoder = crisv10_decoder; - dc->clear_locked_irq = 1; - } - - /* - * Odd PC indicates that branch is rexecuting due to exception in the - * delayslot, like in real hw. - */ - pc_start = dc->base.pc_first & ~1; - dc->base.pc_first = pc_start; - dc->base.pc_next = pc_start; - - dc->cpu = env_archcpu(env); - dc->ppc = pc_start; - dc->pc = pc_start; - dc->flags_uptodate = 1; - dc->flags_x = tb_flags & X_FLAG; - dc->cc_x_uptodate = 0; - dc->cc_mask = 0; - dc->update_cc = 0; - dc->clear_prefix = 0; - dc->cpustate_changed = 0; - - cris_update_cc_op(dc, CC_OP_FLAGS, 4); - dc->cc_size_uptodate = -1; - - /* Decode TB flags. */ - dc->tb_flags = tb_flags & (S_FLAG | P_FLAG | U_FLAG | X_FLAG | PFIX_FLAG); - dc->delayed_branch = !!(tb_flags & 7); - if (dc->delayed_branch) { - dc->jmp = JMP_INDIRECT; - } else { - dc->jmp = JMP_NOJMP; - } -} - -static void cris_tr_tb_start(DisasContextBase *db, CPUState *cpu) -{ -} - -static void cris_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - tcg_gen_insn_start(dc->delayed_branch == 1 ? dc->ppc | 1 : dc->pc); -} - -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. */ - LOG_DIS("%8.8x:\t", dc->pc); - - dc->clear_x = 1; - - insn_len = dc->decoder(env, dc); - dc->ppc = dc->pc; - dc->pc += insn_len; - dc->base.pc_next += insn_len; - - if (dc->base.is_jmp == DISAS_NORETURN) { - return; - } - - if (dc->clear_x) { - cris_clear_x_flag(dc); - } - - /* - * All branches are delayed branches, handled immediately below. - * We don't expect to see odd combinations of exit conditions. - */ - assert(dc->base.is_jmp == DISAS_NEXT || dc->cpustate_changed); - - if (dc->delayed_branch && --dc->delayed_branch == 0) { - dc->base.is_jmp = DISAS_DBRANCH; - return; - } - - if (dc->base.is_jmp != DISAS_NEXT) { - return; - } - - /* Force an update if the per-tb cpu state has changed. */ - if (dc->cpustate_changed) { - dc->base.is_jmp = DISAS_UPDATE_NEXT; - return; - } - - /* - * FIXME: Only the first insn in the TB should cross a page boundary. - * If we can detect the length of the next insn easily, we should. - * In the meantime, simply stop when we do cross. - */ - if ((dc->pc ^ dc->base.pc_first) & TARGET_PAGE_MASK) { - dc->base.is_jmp = DISAS_TOO_MANY; - } -} - -static void cris_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - DisasJumpType is_jmp = dc->base.is_jmp; - target_ulong npc = dc->pc; - - if (is_jmp == DISAS_NORETURN) { - /* If we have a broken branch+delayslot sequence, it's too late. */ - assert(dc->delayed_branch != 1); - return; - } - - if (dc->clear_locked_irq) { - t_gen_movi_env_TN(locked_irq, 0); - } - - /* Broken branch+delayslot sequence. */ - if (dc->delayed_branch == 1) { - /* Set env->dslot to the size of the branch insn. */ - t_gen_movi_env_TN(dslot, dc->pc - dc->ppc); - cris_store_direct_jmp(dc); - } - - cris_evaluate_flags(dc); - - /* Evaluate delayed branch destination and fold to another is_jmp case. */ - if (is_jmp == DISAS_DBRANCH) { - if (dc->base.tb->flags & 7) { - t_gen_movi_env_TN(dslot, 0); - } - - switch (dc->jmp) { - case JMP_DIRECT: - npc = dc->jmp_pc; - is_jmp = dc->cpustate_changed ? DISAS_UPDATE_NEXT : DISAS_TOO_MANY; - break; - - case JMP_DIRECT_CC: - /* - * Use a conditional branch if either taken or not-taken path - * can use goto_tb. If neither can, then treat it as indirect. - */ - if (likely(!dc->cpustate_changed) - && (use_goto_tb(dc, dc->jmp_pc) || use_goto_tb(dc, npc))) { - TCGLabel *not_taken = gen_new_label(); - - tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, not_taken); - gen_goto_tb(dc, 1, dc->jmp_pc); - gen_set_label(not_taken); - - /* not-taken case handled below. */ - is_jmp = DISAS_TOO_MANY; - break; - } - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); - /* fall through */ - - case JMP_INDIRECT: - tcg_gen_movcond_tl(TCG_COND_NE, env_pc, - env_btaken, tcg_constant_tl(0), - env_btarget, tcg_constant_tl(npc)); - is_jmp = dc->cpustate_changed ? DISAS_UPDATE : DISAS_JUMP; - - /* - * We have now consumed btaken and btarget. Hint to the - * tcg compiler that the writeback to env may be dropped. - */ - tcg_gen_discard_tl(env_btaken); - tcg_gen_discard_tl(env_btarget); - break; - - default: - g_assert_not_reached(); - } - } - - switch (is_jmp) { - case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, npc); - break; - case DISAS_UPDATE_NEXT: - tcg_gen_movi_tl(env_pc, npc); - /* fall through */ - case DISAS_JUMP: - tcg_gen_lookup_and_goto_ptr(); - break; - case DISAS_UPDATE: - /* Indicate that interupts must be re-evaluated before the next TB. */ - tcg_gen_exit_tb(NULL, 0); - break; - default: - g_assert_not_reached(); - } -} - -static void cris_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - if (!DISAS_CRIS) { - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); - } -} - -static const TranslatorOps cris_tr_ops = { - .init_disas_context = cris_tr_init_disas_context, - .tb_start = cris_tr_tb_start, - .insn_start = cris_tr_insn_start, - .translate_insn = cris_tr_translate_insn, - .tb_stop = cris_tr_tb_stop, - .disas_log = cris_tr_disas_log, -}; - -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) -{ - DisasContext dc; - 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; - const char * const *regnames; - const char * const *pregnames; - int i; - - if (!env) { - return; - } - if (env->pregs[PR_VR] < 32) { - pregnames = pregnames_v10; - regnames = regnames_v10; - } else { - pregnames = pregnames_v32; - regnames = regnames_v32; - } - - qemu_fprintf(f, "PC=%x CCS=%x btaken=%d btarget=%x\n" - "cc_op=%d cc_src=%d cc_dest=%d cc_result=%x cc_mask=%x\n", - env->pc, env->pregs[PR_CCS], env->btaken, env->btarget, - env->cc_op, - env->cc_src, env->cc_dest, env->cc_result, env->cc_mask); - - - for (i = 0; i < 16; i++) { - qemu_fprintf(f, "%s=%8.8x ", regnames[i], env->regs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - qemu_fprintf(f, "\nspecial regs:\n"); - for (i = 0; i < 16; i++) { - qemu_fprintf(f, "%s=%8.8x ", pregnames[i], env->pregs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - if (env->pregs[PR_VR] >= 32) { - uint32_t srs = env->pregs[PR_SRS]; - qemu_fprintf(f, "\nsupport function regs bank %x:\n", srs); - if (srs < ARRAY_SIZE(env->sregs)) { - for (i = 0; i < 16; i++) { - qemu_fprintf(f, "s%2.2d=%8.8x ", - i, env->sregs[srs][i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - } - } - qemu_fprintf(f, "\n\n"); - -} - -void cris_initialize_tcg(void) -{ - int i; - - cc_x = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_x), "cc_x"); - cc_src = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_src), "cc_src"); - cc_dest = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_dest), - "cc_dest"); - cc_result = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_result), - "cc_result"); - cc_op = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_op), "cc_op"); - cc_size = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_size), - "cc_size"); - cc_mask = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_mask), - "cc_mask"); - - env_pc = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, pc), - "pc"); - env_btarget = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, btarget), - "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, btaken), - "btaken"); - for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, regs[i]), - regnames_v32[i]); - } - for (i = 0; i < 16; i++) { - cpu_PR[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, pregs[i]), - pregnames_v32[i]); - } -} diff --git a/target/cris/translate_v10.c.inc b/target/cris/translate_v10.c.inc deleted file mode 100644 index f500e93447..0000000000 --- a/target/cris/translate_v10.c.inc +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * CRISv10 emulation for qemu: main translation routines. - * - * Copyright (c) 2010 AXIS Communications AB - * Written by Edgar E. Iglesias. - * - * 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 "crisv10-decode.h" - -static const char * const regnames_v10[] = -{ - "$r0", "$r1", "$r2", "$r3", - "$r4", "$r5", "$r6", "$r7", - "$r8", "$r9", "$r10", "$r11", - "$r12", "$r13", "$sp", "$pc", -}; - -static const char * const pregnames_v10[] = -{ - "$bz", "$vr", "$p2", "$p3", - "$wz", "$ccr", "$p6-prefix", "$mof", - "$dz", "$ibr", "$irp", "$srp", - "$bar", "$dccr", "$brp", "$usp", -}; - -/* We need this table to handle preg-moves with implicit width. */ -static const int preg_sizes_v10[] = { - 1, /* bz. */ - 1, /* vr. */ - 1, /* pid. */ - 1, /* srs. */ - 2, /* wz. */ - 2, 2, 4, - 4, 4, 4, 4, - 4, 4, 4, 4, -}; - -static inline int dec10_size(unsigned int size) -{ - size++; - if (size == 3) - size++; - return size; -} - -static inline void cris_illegal_insn(DisasContext *dc) -{ - qemu_log_mask(LOG_GUEST_ERROR, "illegal insn at pc=%x\n", dc->pc); - t_gen_raise_exception(EXCP_BREAK); - dc->base.is_jmp = DISAS_NORETURN; -} - -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(); - dc->postinc = 0; - cris_evaluate_flags(dc); - - tcg_gen_mov_tl(taddr, addr); - tcg_gen_mov_tl(tval, 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); - } - 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) { - cris_store_direct_jmp(dc); - } - - /* Conditional writes. */ - if (dc->flags_x) { - gen_store_v10_conditional(dc, addr, val, size, 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); - } -} - - -/* Prefix flag and register are used to handle the more complex - addressing modes. */ -static void cris_set_prefix(DisasContext *dc) -{ - dc->clear_prefix = 0; - dc->tb_flags |= PFIX_FLAG; - tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], PFIX_FLAG); - - /* prefix insns don't clear the x flag. */ - dc->clear_x = 0; - cris_lock_irq(dc); -} - -static void crisv10_prepare_memaddr(DisasContext *dc, - TCGv addr, unsigned int size) -{ - if (dc->tb_flags & PFIX_FLAG) { - tcg_gen_mov_tl(addr, cpu_PR[PR_PREFIX]); - } else { - tcg_gen_mov_tl(addr, cpu_R[dc->src]); - } -} - -static unsigned int crisv10_post_memaddr(DisasContext *dc, unsigned int size) -{ - unsigned int insn_len = 0; - - if (dc->tb_flags & PFIX_FLAG) { - if (dc->mode == CRISV10_MODE_AUTOINC) { - tcg_gen_mov_tl(cpu_R[dc->src], cpu_PR[PR_PREFIX]); - } - } else { - if (dc->mode == CRISV10_MODE_AUTOINC) { - if (dc->src == 15) { - insn_len += size & ~1; - } else { - tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], size); - } - } - } - return insn_len; -} - -static int dec10_prep_move_m(CPUCRISState *env, DisasContext *dc, - int s_ext, int memsize, TCGv dst) -{ - unsigned int rs; - uint32_t imm; - int is_imm; - int insn_len = 0; - - rs = dc->src; - is_imm = rs == 15 && !(dc->tb_flags & PFIX_FLAG); - LOG_DIS("rs=%d rd=%d is_imm=%d mode=%d pfix=%d\n", - rs, dc->dst, is_imm, dc->mode, dc->tb_flags & PFIX_FLAG); - - /* Load [$rs] onto T1. */ - if (is_imm) { - if (memsize != 4) { - if (s_ext) { - if (memsize == 1) - imm = cpu_ldsb_code(env, dc->pc + 2); - else - imm = cpu_ldsw_code(env, dc->pc + 2); - } else { - if (memsize == 1) - imm = cpu_ldub_code(env, dc->pc + 2); - else - imm = cpu_lduw_code(env, dc->pc + 2); - } - } else - imm = cpu_ldl_code(env, dc->pc + 2); - - tcg_gen_movi_tl(dst, imm); - - if (dc->mode == CRISV10_MODE_AUTOINC) { - insn_len += memsize; - if (memsize == 1) - insn_len++; - tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len); - } - } else { - TCGv addr; - - addr = tcg_temp_new(); - cris_flush_cc_state(dc); - crisv10_prepare_memaddr(dc, addr, memsize); - gen_load(dc, dst, addr, memsize, 0); - if (s_ext) - t_gen_sext(dst, dst, memsize); - 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)) { - dc->dst = dc->src; - } - return insn_len; -} - -static unsigned int dec10_quick_imm(DisasContext *dc) -{ - int32_t imm, simm; - int op; - TCGv c; - - /* sign extend. */ - imm = dc->ir & ((1 << 6) - 1); - simm = (int8_t) (imm << 2); - simm >>= 2; - switch (dc->opcode) { - case CRISV10_QIMM_BDAP_R0: - case CRISV10_QIMM_BDAP_R1: - case CRISV10_QIMM_BDAP_R2: - case CRISV10_QIMM_BDAP_R3: - simm = (int8_t)dc->ir; - LOG_DIS("bdap %d $r%d\n", simm, dc->dst); - LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n", - dc->pc, dc->mode, dc->opcode, dc->src, dc->dst); - cris_set_prefix(dc); - if (dc->dst == 15) { - tcg_gen_movi_tl(cpu_PR[PR_PREFIX], dc->pc + 2 + simm); - } else { - tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm); - } - break; - - case CRISV10_QIMM_MOVEQ: - LOG_DIS("moveq %d, $r%d\n", simm, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_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); - 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); - 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); - 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); - - cris_cc_mask(dc, CC_MASK_NZVC); - op = imm & (1 << 5); - imm &= 0x1f; - c = tcg_const_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], - c, cpu_PR[PR_CCS]); - } - tcg_temp_free(c); - break; - case CRISV10_QIMM_LSHQ: - LOG_DIS("lshq %d, $r%d\n", simm, dc->dst); - - op = CC_OP_LSL; - if (imm & (1 << 5)) { - op = CC_OP_LSR; - } - imm &= 0x1f; - cris_cc_mask(dc, CC_MASK_NZVC); - c = tcg_const_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); - 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); - 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: - case CRISV10_QIMM_BCC_R1: - case CRISV10_QIMM_BCC_R2: - case CRISV10_QIMM_BCC_R3: - imm = dc->ir & 0xff; - /* bit 0 is a sign bit. */ - if (imm & 1) { - imm |= 0xffffff00; /* sign extend. */ - imm &= ~1; /* get rid of the sign bit. */ - } - imm += 2; - LOG_DIS("b%s %d\n", cc_name(dc->cond), imm); - - cris_cc_mask(dc, 0); - cris_prepare_cc_branch(dc, imm, dc->cond); - break; - - default: - LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n", - dc->pc, dc->mode, dc->opcode, dc->src, dc->dst); - cpu_abort(CPU(dc->cpu), "Unhandled quickimm\n"); - break; - } - return 2; -} - -static unsigned int dec10_setclrf(DisasContext *dc) -{ - uint32_t flags; - unsigned int set = ~dc->opcode & 1; - - flags = EXTRACT_FIELD(dc->ir, 0, 3) - | (EXTRACT_FIELD(dc->ir, 12, 15) << 4); - LOG_DIS("%s set=%d flags=%x\n", __func__, set, flags); - - - if (flags & X_FLAG) { - if (set) - dc->flags_x = X_FLAG; - else - dc->flags_x = 0; - } - - cris_evaluate_flags (dc); - cris_update_cc_op(dc, CC_OP_FLAGS, 4); - cris_update_cc_x(dc); - tcg_gen_movi_tl(cc_op, dc->cc_op); - - if (set) { - tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags); - } else { - tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], - ~(flags|F_FLAG_V10|P_FLAG_V10)); - } - - dc->flags_uptodate = 1; - dc->clear_x = 0; - cris_lock_irq(dc); - return 2; -} - -static inline void dec10_reg_prep_sext(DisasContext *dc, int size, int sext, - TCGv dd, TCGv ds, TCGv sd, TCGv ss) -{ - if (sext) { - t_gen_sext(dd, sd, size); - t_gen_sext(ds, ss, size); - } else { - t_gen_zext(dd, sd, size); - t_gen_zext(ds, ss, size); - } -} - -static void dec10_reg_alu(DisasContext *dc, int op, int size, int sext) -{ - TCGv t[2]; - - t[0] = tcg_temp_new(); - t[1] = tcg_temp_new(); - dec10_reg_prep_sext(dc, size, sext, - t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]); - - if (op == CC_OP_LSL || op == CC_OP_LSR || op == CC_OP_ASR) { - tcg_gen_andi_tl(t[1], t[1], 63); - } - - 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_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) -{ - int op = sext ? CC_OP_MULS : CC_OP_MULU; - TCGv t[2]; - - t[0] = tcg_temp_new(); - t[1] = tcg_temp_new(); - dec10_reg_prep_sext(dc, size, 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]); -} - - -static void dec10_reg_movs(DisasContext *dc) -{ - int size = (dc->size & 1) + 1; - TCGv t; - - LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - - t = tcg_temp_new(); - if (dc->ir & 32) - t_gen_sext(t, cpu_R[dc->src], size); - else - 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) -{ - int size = (dc->size & 1) + 1; - TCGv t; - - LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - - t = tcg_temp_new(); - if (dc->ir & 32) - t_gen_sext(t, cpu_R[dc->src], size); - else - 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) -{ - LOG_DIS("move p%d r%d sz=%d\n", dc->dst, dc->src, preg_sizes_v10[dc->dst]); - cris_lock_irq(dc); - if (dc->src == 15) { - tcg_gen_mov_tl(env_btarget, cpu_PR[dc->dst]); - cris_prepare_jmp(dc, JMP_INDIRECT); - return; - } - if (dc->dst == PR_CCS) { - cris_evaluate_flags(dc); - } - cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src], - cpu_R[dc->src], cpu_PR[dc->dst], preg_sizes_v10[dc->dst]); -} - -static void dec10_reg_abs(DisasContext *dc) -{ - TCGv t0; - - LOG_DIS("abs $r%u, $r%u\n", dc->src, dc->dst); - - assert(dc->dst != 15); - t0 = tcg_temp_new(); - tcg_gen_sari_tl(t0, cpu_R[dc->src], 31); - tcg_gen_xor_tl(cpu_R[dc->dst], cpu_R[dc->src], t0); - 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) -{ - TCGv t0; - - LOG_DIS("not $r%d, $r%d\n", dc->src, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - t0 = tcg_temp_new(); - tcg_gen_mov_tl(t0, cpu_R[dc->src]); - if (dc->dst & 8) - tcg_gen_not_tl(t0, t0); - if (dc->dst & 4) - t_gen_swapw(t0, t0); - if (dc->dst & 2) - t_gen_swapb(t0, t0); - 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) -{ - int cond = dc->dst; - - LOG_DIS("s%s $r%u\n", cc_name(cond), dc->src); - - gen_tst_cc(dc, cpu_R[dc->src], cond); - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_R[dc->src], cpu_R[dc->src], 0); - - cris_cc_mask(dc, 0); -} - -static unsigned int dec10_reg(DisasContext *dc) -{ - TCGv t; - unsigned int insn_len = 2; - unsigned int size = dec10_size(dc->size); - unsigned int tmp; - - if (dc->size != 3) { - switch (dc->opcode) { - case CRISV10_REG_MOVE_R: - LOG_DIS("move.%d $r%d, $r%d\n", dc->size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_MOVE, size, 0); - if (dc->dst == 15) { - tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); - cris_prepare_jmp(dc, JMP_INDIRECT); - dc->delayed_branch = 1; - } - break; - case CRISV10_REG_MOVX: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_movs(dc); - break; - case CRISV10_REG_ADDX: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alux(dc, CC_OP_ADD); - break; - case CRISV10_REG_SUBX: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alux(dc, CC_OP_SUB); - break; - case CRISV10_REG_ADD: - LOG_DIS("add $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_ADD, size, 0); - break; - case CRISV10_REG_SUB: - LOG_DIS("sub $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_SUB, size, 0); - break; - case CRISV10_REG_CMP: - LOG_DIS("cmp $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_CMP, size, 0); - break; - case CRISV10_REG_BOUND: - LOG_DIS("bound $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_bound(dc, size); - break; - case CRISV10_REG_AND: - LOG_DIS("and $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_AND, size, 0); - break; - case CRISV10_REG_ADDI: - if (dc->src == 15) { - /* nop. */ - return 2; - } - t = tcg_temp_new(); - 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); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_LSL, size, 0); - break; - case CRISV10_REG_LSR: - LOG_DIS("lsr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_LSR, size, 0); - break; - case CRISV10_REG_ASR: - LOG_DIS("asr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_ASR, size, 1); - break; - case CRISV10_REG_OR: - LOG_DIS("or $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_OR, size, 0); - break; - case CRISV10_REG_NEG: - LOG_DIS("neg $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_NEG, size, 0); - break; - case CRISV10_REG_BIAP: - LOG_DIS("BIAP pc=%x reg %d r%d r%d size=%d\n", dc->pc, - dc->opcode, dc->src, dc->dst, size); - switch (size) { - case 4: tmp = 2; break; - case 2: tmp = 1; break; - case 1: tmp = 0; break; - default: - cpu_abort(CPU(dc->cpu), "Unhandled BIAP"); - break; - } - - t = tcg_temp_new(); - tcg_gen_shli_tl(t, cpu_R[dc->dst], tmp); - if (dc->src == 15) { - tcg_gen_addi_tl(cpu_PR[PR_PREFIX], t, ((dc->pc +2)| 1) + 1); - } else { - tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_R[dc->src], t); - } - tcg_temp_free(t); - cris_set_prefix(dc); - break; - - default: - LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc, - dc->opcode, dc->src, dc->dst); - cpu_abort(CPU(dc->cpu), "Unhandled opcode"); - break; - } - } else { - switch (dc->opcode) { - case CRISV10_REG_MOVX: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_movs(dc); - break; - case CRISV10_REG_ADDX: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alux(dc, CC_OP_ADD); - break; - case CRISV10_REG_SUBX: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alux(dc, CC_OP_SUB); - break; - case CRISV10_REG_MOVE_SPR_R: - cris_evaluate_flags(dc); - cris_cc_mask(dc, 0); - dec10_reg_mov_pr(dc); - break; - case CRISV10_REG_MOVE_R_SPR: - LOG_DIS("move r%d p%d\n", dc->src, dc->dst); - cris_evaluate_flags(dc); - if (dc->src != 11) /* fast for srp. */ - dc->cpustate_changed = 1; - t_gen_mov_preg_TN(dc, dc->dst, cpu_R[dc->src]); - break; - case CRISV10_REG_SETF: - case CRISV10_REG_CLEARF: - dec10_setclrf(dc); - break; - case CRISV10_REG_SWAP: - dec10_reg_swap(dc); - break; - case CRISV10_REG_ABS: - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_abs(dc); - break; - case CRISV10_REG_LZ: - LOG_DIS("lz $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_LZ, 4, 0); - break; - case CRISV10_REG_XOR: - LOG_DIS("xor $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_alu(dc, CC_OP_XOR, 4, 0); - break; - case CRISV10_REG_BTST: - 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], - cpu_R[dc->src], cpu_PR[PR_CCS]); - break; - case CRISV10_REG_DSTEP: - LOG_DIS("dstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_cc_mask(dc, CC_MASK_NZVC); - cris_alu(dc, CC_OP_DSTEP, cpu_R[dc->dst], - cpu_R[dc->dst], cpu_R[dc->src], 4); - break; - case CRISV10_REG_MSTEP: - LOG_DIS("mstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); - cris_evaluate_flags(dc); - cris_cc_mask(dc, CC_MASK_NZVC); - cris_alu(dc, CC_OP_MSTEP, cpu_R[dc->dst], - cpu_R[dc->dst], cpu_R[dc->src], 4); - break; - case CRISV10_REG_SCC: - dec10_reg_scc(dc); - break; - default: - LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc, - dc->opcode, dc->src, dc->dst); - cpu_abort(CPU(dc->cpu), "Unhandled opcode"); - break; - } - } - return insn_len; -} - -static unsigned int dec10_ind_move_m_r(CPUCRISState *env, DisasContext *dc, - unsigned int size) -{ - unsigned int insn_len = 2; - TCGv t; - - LOG_DIS("%s: move.%d [$r%d], $r%d\n", __func__, - size, dc->src, dc->dst); - - cris_cc_mask(dc, CC_MASK_NZVC); - t = tcg_temp_new(); - insn_len += dec10_prep_move_m(env, dc, 0, size, t); - cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, size); - if (dc->dst == 15) { - tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); - cris_prepare_jmp(dc, JMP_INDIRECT); - dc->delayed_branch = 1; - } - - tcg_temp_free(t); - return insn_len; -} - -static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size) -{ - unsigned int insn_len = 2; - TCGv addr; - - LOG_DIS("move.%d $r%d, [$r%d]\n", dc->size, dc->src, dc->dst); - addr = tcg_temp_new(); - 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; -} - -static unsigned int dec10_ind_move_m_pr(CPUCRISState *env, DisasContext *dc) -{ - unsigned int insn_len = 2, rd = dc->dst; - TCGv t, addr; - - 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) { - tcg_gen_mov_tl(env_btarget, t); - cris_prepare_jmp(dc, JMP_INDIRECT); - dc->delayed_branch = 1; - } else { - tcg_gen_mov_tl(cpu_PR[rd], t); - dc->cpustate_changed = 1; - } - tcg_temp_free(addr); - tcg_temp_free(t); - return insn_len; -} - -static unsigned int dec10_ind_move_pr_m(DisasContext *dc) -{ - unsigned int insn_len = 2, size = preg_sizes_v10[dc->dst]; - TCGv addr, t0; - - LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src); - - addr = tcg_temp_new(); - crisv10_prepare_memaddr(dc, addr, size); - if (dc->dst == PR_CCS) { - t0 = tcg_temp_new(); - 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; -} - -static void dec10_movem_r_m(DisasContext *dc) -{ - int i, pfix = dc->tb_flags & PFIX_FLAG; - TCGv addr, t0; - - LOG_DIS("%s r%d, [r%d] pi=%d ir=%x\n", __func__, - dc->dst, dc->src, dc->postinc, dc->ir); - - addr = tcg_temp_new(); - t0 = tcg_temp_new(); - crisv10_prepare_memaddr(dc, addr, 4); - tcg_gen_mov_tl(t0, addr); - for (i = dc->dst; i >= 0; i--) { - if ((pfix && dc->mode == CRISV10_MODE_AUTOINC) && dc->src == i) { - gen_store_v10(dc, addr, t0, 4); - } else { - gen_store_v10(dc, addr, cpu_R[i], 4); - } - tcg_gen_addi_tl(addr, addr, 4); - } - - if (pfix && dc->mode == CRISV10_MODE_AUTOINC) { - tcg_gen_mov_tl(cpu_R[dc->src], t0); - } - - 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) -{ - int i, pfix = dc->tb_flags & PFIX_FLAG; - TCGv addr, t0; - - LOG_DIS("%s [r%d], r%d pi=%d ir=%x\n", __func__, - dc->src, dc->dst, dc->postinc, dc->ir); - - addr = tcg_temp_new(); - t0 = tcg_temp_new(); - crisv10_prepare_memaddr(dc, addr, 4); - tcg_gen_mov_tl(t0, addr); - for (i = dc->dst; i >= 0; i--) { - gen_load(dc, cpu_R[i], addr, 4, 0); - tcg_gen_addi_tl(addr, addr, 4); - } - - if (pfix && dc->mode == CRISV10_MODE_AUTOINC) { - tcg_gen_mov_tl(cpu_R[dc->src], t0); - } - - 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, - int op, unsigned int size) -{ - int insn_len = 0; - int rd = dc->dst; - TCGv t[2]; - - cris_alu_m_alloc_temps(t); - insn_len += dec10_prep_move_m(env, dc, 0, size, t[0]); - cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t[0], size); - if (dc->dst == 15) { - tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); - cris_prepare_jmp(dc, JMP_INDIRECT); - dc->delayed_branch = 1; - return insn_len; - } - - cris_alu_m_free_temps(t); - - return insn_len; -} - -static int dec10_ind_bound(CPUCRISState *env, DisasContext *dc, - unsigned int size) -{ - int insn_len = 0; - int rd = dc->dst; - TCGv t; - - t = tcg_temp_local_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) { - tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); - cris_prepare_jmp(dc, JMP_INDIRECT); - dc->delayed_branch = 1; - } - - tcg_temp_free(t); - return insn_len; -} - -static int dec10_alux_m(CPUCRISState *env, DisasContext *dc, int op) -{ - unsigned int size = (dc->size & 1) ? 2 : 1; - unsigned int sx = !!(dc->size & 2); - int insn_len = 2; - int rd = dc->dst; - TCGv t; - - LOG_DIS("addx size=%d sx=%d op=%d %d\n", size, sx, dc->src, dc->dst); - - t = tcg_temp_new(); - - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_prep_move_m(env, dc, sx, size, t); - cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t, 4); - if (dc->dst == 15) { - tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); - cris_prepare_jmp(dc, JMP_INDIRECT); - dc->delayed_branch = 1; - } - - tcg_temp_free(t); - return insn_len; -} - -static int dec10_dip(CPUCRISState *env, DisasContext *dc) -{ - int insn_len = 2; - uint32_t imm; - - LOG_DIS("dip pc=%x opcode=%d r%d r%d\n", - dc->pc, dc->opcode, dc->src, dc->dst); - if (dc->src == 15) { - imm = cpu_ldl_code(env, dc->pc + 2); - tcg_gen_movi_tl(cpu_PR[PR_PREFIX], imm); - if (dc->postinc) - insn_len += 4; - tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len - 2); - } else { - gen_load(dc, cpu_PR[PR_PREFIX], cpu_R[dc->src], 4, 0); - if (dc->postinc) - tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], 4); - } - - cris_set_prefix(dc); - return insn_len; -} - -static int dec10_bdap_m(CPUCRISState *env, DisasContext *dc, int size) -{ - int insn_len = 2; - int rd = dc->dst; - - LOG_DIS("bdap_m pc=%x opcode=%d r%d r%d sz=%d\n", - dc->pc, dc->opcode, dc->src, dc->dst, size); - - assert(dc->dst != 15); -#if 0 - /* 8bit embedded offset? */ - if (!dc->postinc && (dc->ir & (1 << 11))) { - int simm = dc->ir & 0xff; - - /* cpu_abort(CPU(dc->cpu), "Unhandled opcode"); */ - /* sign extended. */ - simm = (int8_t)simm; - - tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm); - - cris_set_prefix(dc); - return insn_len; - } -#endif - /* Now the rest of the modes are truly indirect. */ - insn_len += dec10_prep_move_m(env, dc, 1, size, cpu_PR[PR_PREFIX]); - tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_PR[PR_PREFIX], cpu_R[rd]); - cris_set_prefix(dc); - return insn_len; -} - -static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) -{ - unsigned int insn_len = 2; - unsigned int size = dec10_size(dc->size); - uint32_t imm; - int32_t simm; - TCGv t[2], c; - - if (dc->size != 3) { - switch (dc->opcode) { - case CRISV10_IND_MOVE_M_R: - return dec10_ind_move_m_r(env, dc, size); - case CRISV10_IND_MOVE_R_M: - return dec10_ind_move_r_m(dc, size); - case CRISV10_IND_CMP: - LOG_DIS("cmp size=%d op=%d %d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_alu(env, dc, CC_OP_CMP, size); - break; - case CRISV10_IND_TEST: - LOG_DIS("test size=%d op=%d %d\n", size, dc->src, dc->dst); - - cris_evaluate_flags(dc); - cris_cc_mask(dc, CC_MASK_NZVC); - 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); - 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); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_alu(env, dc, CC_OP_ADD, size); - break; - case CRISV10_IND_SUB: - LOG_DIS("sub size=%d op=%d %d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_alu(env, dc, CC_OP_SUB, size); - break; - case CRISV10_IND_BOUND: - LOG_DIS("bound size=%d op=%d %d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_bound(env, dc, size); - break; - case CRISV10_IND_AND: - LOG_DIS("and size=%d op=%d %d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_alu(env, dc, CC_OP_AND, size); - break; - case CRISV10_IND_OR: - LOG_DIS("or size=%d op=%d %d\n", size, dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_alu(env, dc, CC_OP_OR, size); - break; - case CRISV10_IND_MOVX: - insn_len = dec10_alux_m(env, dc, CC_OP_MOVE); - break; - case CRISV10_IND_ADDX: - insn_len = dec10_alux_m(env, dc, CC_OP_ADD); - break; - case CRISV10_IND_SUBX: - insn_len = dec10_alux_m(env, dc, CC_OP_SUB); - break; - case CRISV10_IND_CMPX: - insn_len = dec10_alux_m(env, dc, CC_OP_CMP); - break; - case CRISV10_IND_MUL: - /* This is a reg insn coded in the mem indir space. */ - LOG_DIS("mul pc=%x opcode=%d\n", dc->pc, dc->opcode); - cris_cc_mask(dc, CC_MASK_NZVC); - dec10_reg_mul(dc, size, dc->ir & (1 << 10)); - break; - case CRISV10_IND_BDAP_M: - insn_len = dec10_bdap_m(env, dc, size); - break; - default: - /* - * ADDC for v17: - * - * Instruction format: ADDC [Rs],Rd - * - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+ - * |Destination(Rd)| 1 0 0 1 1 0 1 0 | Source(Rs)| - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+--+--+ - * - * Instruction format: ADDC [Rs+],Rd - * - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+ - * |Destination(Rd)| 1 1 0 1 1 0 1 0 | Source(Rs)| - * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+ - */ - if (dc->opcode == CRISV17_IND_ADDC && dc->size == 2 && - env->pregs[PR_VR] == 17) { - LOG_DIS("addc op=%d %d\n", dc->src, dc->dst); - cris_cc_mask(dc, CC_MASK_NZVC); - insn_len += dec10_ind_alu(env, dc, CC_OP_ADDC, size); - break; - } - - LOG_DIS("pc=%x var-ind.%d %d r%d r%d\n", - dc->pc, size, dc->opcode, dc->src, dc->dst); - cpu_abort(CPU(dc->cpu), "Unhandled opcode"); - break; - } - return insn_len; - } - - switch (dc->opcode) { - case CRISV10_IND_MOVE_M_SPR: - insn_len = dec10_ind_move_m_pr(env, dc); - break; - case CRISV10_IND_MOVE_SPR_M: - insn_len = dec10_ind_move_pr_m(dc); - break; - case CRISV10_IND_JUMP_M: - if (dc->src == 15) { - LOG_DIS("jump.%d %d r%d r%d direct\n", size, - dc->opcode, dc->src, dc->dst); - imm = cpu_ldl_code(env, dc->pc + 2); - if (dc->mode == CRISV10_MODE_AUTOINC) - insn_len += size; - - c = tcg_const_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. */ - } else { - if (dc->dst == 14) { - 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); - 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; - } - 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); - 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; - - case CRISV10_IND_MOVEM_R_M: - LOG_DIS("movem_r_m pc=%x opcode=%d r%d r%d\n", - dc->pc, dc->opcode, dc->dst, dc->src); - dec10_movem_r_m(dc); - break; - case CRISV10_IND_MOVEM_M_R: - LOG_DIS("movem_m_r pc=%x opcode=%d\n", dc->pc, dc->opcode); - dec10_movem_m_r(dc); - break; - case CRISV10_IND_JUMP_R: - 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); - 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; - case CRISV10_IND_MOVX: - insn_len = dec10_alux_m(env, dc, CC_OP_MOVE); - break; - case CRISV10_IND_ADDX: - insn_len = dec10_alux_m(env, dc, CC_OP_ADD); - break; - case CRISV10_IND_SUBX: - insn_len = dec10_alux_m(env, dc, CC_OP_SUB); - break; - case CRISV10_IND_CMPX: - insn_len = dec10_alux_m(env, dc, CC_OP_CMP); - break; - case CRISV10_IND_DIP: - insn_len = dec10_dip(env, dc); - break; - case CRISV10_IND_BCC_M: - - cris_cc_mask(dc, 0); - simm = cpu_ldsw_code(env, dc->pc + 2); - simm += 4; - - LOG_DIS("bcc_m: b%s %x\n", cc_name(dc->cond), dc->pc + simm); - cris_prepare_cc_branch(dc, simm, dc->cond); - insn_len = 4; - break; - default: - LOG_DIS("ERROR pc=%x opcode=%d\n", dc->pc, dc->opcode); - cpu_abort(CPU(dc->cpu), "Unhandled opcode"); - break; - } - - return insn_len; -} - -static unsigned int crisv10_decoder(CPUCRISState *env, DisasContext *dc) -{ - unsigned int insn_len = 2; - - /* Load a halfword onto the instruction register. */ - dc->ir = cpu_lduw_code(env, dc->pc); - - /* Now decode it. */ - dc->opcode = EXTRACT_FIELD(dc->ir, 6, 9); - dc->mode = EXTRACT_FIELD(dc->ir, 10, 11); - dc->src = EXTRACT_FIELD(dc->ir, 0, 3); - dc->size = EXTRACT_FIELD(dc->ir, 4, 5); - dc->cond = dc->dst = EXTRACT_FIELD(dc->ir, 12, 15); - dc->postinc = EXTRACT_FIELD(dc->ir, 10, 10); - - dc->clear_prefix = 1; - - /* FIXME: What if this insn insn't 2 in length?? */ - if (dc->src == 15 || dc->dst == 15) - tcg_gen_movi_tl(cpu_R[15], dc->pc + 2); - - switch (dc->mode) { - case CRISV10_MODE_QIMMEDIATE: - insn_len = dec10_quick_imm(dc); - break; - case CRISV10_MODE_REG: - insn_len = dec10_reg(dc); - break; - case CRISV10_MODE_AUTOINC: - case CRISV10_MODE_INDIRECT: - insn_len = dec10_ind(env, dc); - break; - } - - if (dc->clear_prefix && dc->tb_flags & PFIX_FLAG) { - dc->tb_flags &= ~PFIX_FLAG; - tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~PFIX_FLAG); - if (dc->tb_flags != dc->base.tb->flags) { - dc->cpustate_changed = 1; - } - } - - /* CRISv10 locks out interrupts on dslots. */ - if (dc->delayed_branch == 2) { - cris_lock_irq(dc); - } - return insn_len; -} - -void cris_initialize_crisv10_tcg(void) -{ - int i; - - cc_x = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_x), "cc_x"); - cc_src = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_src), "cc_src"); - cc_dest = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_dest), - "cc_dest"); - cc_result = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_result), - "cc_result"); - cc_op = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_op), "cc_op"); - cc_size = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_size), - "cc_size"); - cc_mask = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, cc_mask), - "cc_mask"); - - env_pc = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, pc), - "pc"); - env_btarget = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, btarget), - "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, btaken), - "btaken"); - for (i = 0; i < 16; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, regs[i]), - regnames_v10[i]); - } - for (i = 0; i < 16; i++) { - cpu_PR[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUCRISState, pregs[i]), - pregnames_v10[i]); - } -} diff --git a/target/hexagon/README b/target/hexagon/README index 372e24747c..7ffd517d70 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 @@ -39,14 +43,14 @@ target/hexagon/gen_semantics.c. This step produces That file is consumed by the following python scripts to produce the indicated header files in /target/hexagon gen_opcodes_def.py -> opcodes_def_generated.h.inc - gen_op_regs.py -> op_regs_generated.h.inc gen_printinsn.py -> printinsn_generated.h.inc gen_op_attribs.py -> op_attribs_generated.h.inc gen_helper_protos.py -> helper_protos_generated.h.inc - gen_shortcode.py -> shortcode_generated.h.inc 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 +80,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 +134,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 +165,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 +178,27 @@ 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 reads and 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. The analysis determines if the packet +semantics can be short-circuited. If not, 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 +244,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 +277,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 +309,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 5d2a102c18..9e3a05f882 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 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 @@ -44,6 +44,7 @@ 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", "", "") @@ -51,6 +52,12 @@ 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", "", "") @@ -62,23 +69,27 @@ 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", "", "") @@ -101,8 +112,12 @@ 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(IMPLICIT_READS_SP, "Reads the SP register", "", "") DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "") DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "") DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "") @@ -139,6 +154,8 @@ 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", "", "") @@ -147,7 +164,11 @@ 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.", "", "") 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..0b149bd5fe --- /dev/null +++ b/target/hexagon/cpu-qom.h @@ -0,0 +1,28 @@ +/* + * 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_V66 HEXAGON_CPU_TYPE_NAME("v66") +#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 03221fbdc2..020038fc49 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,15 @@ #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_v66_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,18 +44,17 @@ 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; } -static Property hexagon_lldb_compat_property = - DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false); -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_cpu_properties[] = { + DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), + DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, + qdev_prop_uint32, target_ulong), + DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true), + DEFINE_PROP_END_OF_LIST() +}; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -86,7 +90,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 +106,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 +202,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 +237,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,24 +247,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) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; - return env->gpr[HEX_REG_PC]; + 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(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc; } static bool hexagon_cpu_has_work(CPUState *cs) @@ -275,20 +271,18 @@ static void hexagon_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - HexagonCPU *cpu = HEXAGON_CPU(cs); - CPUHexagonState *env = &cpu->env; - - 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, ResetType type) { - 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, type); + } set_default_nan_mode(1, &env->fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); @@ -311,6 +305,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); @@ -319,16 +317,11 @@ 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); } #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, @@ -339,11 +332,14 @@ 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); + device_class_set_props(dc, hexagon_cpu_properties); + 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; @@ -352,8 +348,8 @@ static void hexagon_cpu_class_init(ObjectClass *c, void *data) 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; } @@ -370,12 +366,18 @@ 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_V66, hexagon_v66_cpu_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..764f3c38cc 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,44 @@ 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) + +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc); + +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; + if (*pc & PCALIGN_MASK) { + hexagon_raise_exception_err(env, HEX_EXCP_PC_NOT_ALIGNED, 0); + } } typedef HexagonCPU ArchCPU; diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 96fef71729..4279281a71 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -20,9 +20,13 @@ #include "qemu/bitops.h" +#define PCALIGN 4 +#define PCALIGN_MASK (PCALIGN - 1) + #define HEX_EXCP_FETCH_NO_UPAGE 0x012 #define HEX_EXCP_INVALID_PACKET 0x015 #define HEX_EXCP_INVALID_OPCODE 0x015 +#define HEX_EXCP_PC_NOT_ALIGNED 0x01e #define HEX_EXCP_PRIV_NO_UREAD 0x024 #define HEX_EXCP_PRIV_NO_UWRITE 0x025 diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 6b73b5c60c..23deba2426 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.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 @@ -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) { @@ -248,22 +115,13 @@ static void decode_fill_newvalue_regno(Packet *packet) { int i, use_regidx, offset, def_idx, dst_idx; - uint16_t def_opcode, use_opcode; - char *dststr; for (i = 1; i < packet->num_insns; i++) { if (GET_ATTRIB(packet->insn[i].opcode, A_DOTNEWVALUE) && !GET_ATTRIB(packet->insn[i].opcode, A_EXTENSION)) { - use_opcode = packet->insn[i].opcode; - /* It's a store, so we're adjusting the Nt field */ - if (GET_ATTRIB(use_opcode, A_STORE)) { - use_regidx = strchr(opcode_reginfo[use_opcode], 't') - - opcode_reginfo[use_opcode]; - } else { /* It's a Jump, so we're adjusting the Ns field */ - use_regidx = strchr(opcode_reginfo[use_opcode], 's') - - opcode_reginfo[use_opcode]; - } + g_assert(packet->insn[i].new_read_idx != -1); + use_regidx = packet->insn[i].new_read_idx; /* * What's encoded at the N-field is the offset to who's producing @@ -284,37 +142,9 @@ decode_fill_newvalue_regno(Packet *packet) */ g_assert(!((def_idx < 0) || (def_idx > (packet->num_insns - 1)))); - /* - * packet->insn[def_idx] is the producer - * Figure out which type of destination it produces - * and the corresponding index in the reginfo - */ - def_opcode = packet->insn[def_idx].opcode; - dststr = strstr(opcode_wregs[def_opcode], "Rd"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'd'); - } else { - dststr = strstr(opcode_wregs[def_opcode], "Rx"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'x'); - } else { - dststr = strstr(opcode_wregs[def_opcode], "Re"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'e'); - } else { - dststr = strstr(opcode_wregs[def_opcode], "Ry"); - if (dststr) { - dststr = strchr(opcode_reginfo[def_opcode], 'y'); - } else { - g_assert_not_reached(); - } - } - } - } - g_assert(dststr != NULL); - /* Now patch up the consumer with the register number */ - dst_idx = dststr - opcode_reginfo[def_opcode]; + g_assert(packet->insn[def_idx].dest_idx != -1); + dst_idx = packet->insn[def_idx].dest_idx; packet->insn[i].regno[use_regidx] = packet->insn[def_idx].regno[dst_idx]; /* @@ -388,6 +218,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; @@ -412,13 +243,23 @@ static void decode_set_insn_attr_fields(Packet *pkt) } } - 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; + } } } @@ -484,8 +325,7 @@ static void decode_shuffle_for_execution(Packet *packet) for (flag = false, i = 0; i < last_insn + 1; i++) { int opcode = packet->insn[i].opcode; - if ((strstr(opcode_wregs[opcode], "Pd4") || - strstr(opcode_wregs[opcode], "Pe4")) && + if (packet->insn[i].has_pred_dest && GET_ATTRIB(opcode, A_STORE) == 0) { /* This should be a compare (not a store conditional) */ if (flag) { @@ -539,7 +379,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); @@ -582,188 +422,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) { @@ -786,7 +536,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; @@ -875,6 +644,8 @@ decode_set_slot_number(Packet *pkt) /* Then push it to slot0 */ pkt->insn[slot1_iidx].slot = 0; } + + return has_valid_slot_assignment(pkt); } /* @@ -884,8 +655,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; @@ -898,9 +669,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 @@ -950,8 +723,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) { @@ -971,9 +747,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..12d6b3bbcb 100644 --- a/target/hexagon/gdbstub.c +++ b/target/hexagon/gdbstub.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2021 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 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,30 +16,135 @@ */ #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]); } + n -= TOTAL_PER_THREAD_REGS; + + if (n < NUM_PREGS) { + return gdb_get_reg8(mem_buf, env->pred[n]); + } + + n -= NUM_PREGS; + g_assert_not_reached(); } int hexagon_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + CPUHexagonState *env = cpu_env(cs); + + if (n == HEX_REG_P3_0_ALIASED) { + uint32_t p3_0 = ldl_le_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] = ldl_le_p(mem_buf); + return sizeof(target_ulong); + } + + n -= TOTAL_PER_THREAD_REGS; + + if (n < NUM_PREGS) { + env->pred[n] = ldl_le_p(mem_buf) & 0xff; + return sizeof(uint8_t); + } + + n -= NUM_PREGS; + + 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 < TOTAL_PER_THREAD_REGS) { - env->gpr[n] = ldtul_p(mem_buf); - return sizeof(target_ulong); + 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] = ldl_le_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] = ldl_le_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..54bac19724 --- /dev/null +++ b/target/hexagon/gen_analyze_funcs.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +## +## Copyright(c) 2022-2024 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") + if (hex_common.is_hvx_insn(tag)): + if hex_common.has_hvx_helper(tag): + f.write( + " const bool G_GNUC_UNUSED insn_has_hvx_helper = true;\n" + ) + f.write(" ctx_start_hvx_insn(ctx);\n") + else: + f.write( + " const bool G_GNUC_UNUSED insn_has_hvx_helper = false;\n" + ) + + ## Declare all the registers + for regno, register in enumerate(regs): + reg_type, reg_id = register + reg = hex_common.get_register(tag, reg_type, reg_id) + reg.decl_reg_num(f, regno) + + ## Analyze the register reads + for regno, register in enumerate(regs): + reg_type, reg_id = register + reg = hex_common.get_register(tag, reg_type, reg_id) + if reg.is_read(): + reg.analyze_read(f, regno) + + ## Analyze the register writes + for regno, register in enumerate(regs): + reg_type, reg_id = register + reg = hex_common.get_register(tag, reg_type, reg_id) + if reg.is_written(): + reg.analyze_write(f, tag, regno) + + f.write("}\n\n") + + +def main(): + hex_common.read_common_files() + tagregs = hex_common.get_tagregs() + tagimms = hex_common.get_tagimms() + + with open(sys.argv[-1], "w") as f: + f.write("#ifndef HEXAGON_ANALYZE_FUNCS_C_INC\n") + f.write("#define HEXAGON_ANALYZE_FUNCS_C_INC\n\n") + + for tag in hex_common.tags: + gen_analyze_func(f, tag, tagregs[tag], tagimms[tag]) + + f.write("#endif /* HEXAGON_ANALYZE_FUNCS_C_INC */\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..e9685bff2f 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-2024 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,97 @@ 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]) - hex_common.calculate_attribs() + hex_common.read_common_files() 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..fd2bfd0f36 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-2024 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,54 @@ 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) + for arg in hex_common.helper_args(tag, regs, imms): + declared.append(arg.proto_arg) + + arguments = ", ".join(declared) + + ## Add the TCG_CALL_NO_RWG_SE flag to helpers that don't take the env + ## argument and aren't HVX instructions. Since HVX instructions take + ## pointers to their arguments, they will have side effects. + if hex_common.need_env(tag) or hex_common.is_hvx_insn(tag): + f.write(f"DEF_HELPER_{len(declared) - 1}({tag}, {arguments})\n") 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)) + f.write(f"DEF_HELPER_FLAGS_{len(declared) - 1}({tag}, " + f"TCG_CALL_NO_RWG_SE, {arguments})\n") - ## 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 - - ## 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]) - hex_common.calculate_attribs() + hex_common.read_common_files() 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..72f11c68ca --- /dev/null +++ b/target/hexagon/gen_idef_parser_funcs.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 + +## +## Copyright(c) 2019-2024 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.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('#include "macros.h.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..99448220da 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-2024 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,18 +22,21 @@ 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]) hex_common.calculate_attribs() ## ## Generate all the attributes associated with each instruction ## - with open(sys.argv[3], 'w') as f: + with open(sys.argv[-1], "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 deleted file mode 100755 index e8137d4a12..0000000000 --- a/target/hexagon/gen_op_regs.py +++ /dev/null @@ -1,110 +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 sys -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' - 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 reg_rd and reg_wr: - retstr += srcdst_lett - mapdict[srcdst_lett] = reg - srcdst_lett = letter_inc(srcdst_lett) - elif reg_rd: - retstr += src_lett - mapdict[src_lett] = reg - src_lett = letter_inc(src_lett) - elif reg_wr: - retstr += dst_lett - mapdict[dst_lett] = reg - dst_lett = letter_inc(dst_lett) - return retstr,mapdict - -def calculate_regid_letters(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.','') - -def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - tagregs = hex_common.get_tagregs() - tagimms = hex_common.get_tagimms() - - 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 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'])) - 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))) - - for tag in hex_common.tags: - imms = tagimms[tag] - f.write( 'IMMINFO(%s' % tag) - if not imms: - 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)) - if len(imms) == 1: - if sign.isupper(): - myu = 'u' - else: - myu = 'U' - f.write(''','%s',0,0''' % myu) - 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..536f0eb68a 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-2024 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[-1], "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..8bf4d0985c 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-2024 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,32 @@ 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[-1], "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 +113,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 +160,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 deleted file mode 100755 index 9b589d0189..0000000000 --- a/target/hexagon/gen_shortcode.py +++ /dev/null @@ -1,60 +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 sys -import re -import string -import hex_common - -def gen_shortcode(f, tag): - f.write('DEF_SHORTCODE(%s, %s)\n' % (tag, hex_common.semdict[tag])) - -def main(): - hex_common.read_semantics_file(sys.argv[1]) - hex_common.read_attribs_file(sys.argv[2]) - hex_common.calculate_attribs() - tagregs = hex_common.get_tagregs() - tagimms = hex_common.get_tagimms() - - 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] ) : - continue - ## Skip the guest instructions - if ( "A_GUEST" in hex_common.attribdict[tag] ) : - continue - ## Skip the diag instructions - if ( tag == "Y6_diag" ) : - continue - if ( tag == "Y6_diag0" ) : - continue - 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 50634ac459..3fc1f4e281 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-2024 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,17 +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(); \ tcg_gen_movi_tl(EA, 0); \ PRED; \ CHECK_NOSHUF_PRED(GET_EA, SIZE, LSB); \ - PRED_LOAD_CANCEL(LSB, EA); \ - tcg_gen_movi_tl(RdV, 0); \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \ fLOAD(1, SIZE, SIGN, EA, RdV); \ gen_set_label(label); \ - tcg_temp_free(LSB); \ } while (0) #define fGEN_TCG_L2_ploadrubt_pi(SHORTCODE) \ @@ -397,17 +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(); \ tcg_gen_movi_tl(EA, 0); \ PRED; \ CHECK_NOSHUF_PRED(GET_EA, 8, LSB); \ - PRED_LOAD_CANCEL(LSB, EA); \ - tcg_gen_movi_i64(RddV, 0); \ tcg_gen_brcondi_tl(TCG_COND_EQ, LSB, 0, label); \ fLOAD(1, 8, u, EA, RddV); \ gen_set_label(label); \ - tcg_temp_free(LSB); \ } while (0) #define fGEN_TCG_L2_ploadrdt_pi(SHORTCODE) \ @@ -431,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) \ @@ -508,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) /* @@ -528,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) /* @@ -544,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) /* @@ -565,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 */ @@ -581,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) /* @@ -607,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) \ @@ -741,5 +1331,44 @@ 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 + +#define fGEN_TCG_A2_nop(SHORTCODE) do { } while (0) +#define fGEN_TCG_SA1_setin1(SHORTCODE) tcg_gen_movi_tl(RdV, -1) diff --git a/target/hexagon/gen_tcg_func_table.py b/target/hexagon/gen_tcg_func_table.py index 4809d3273e..978ac1819b 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-2024 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,38 @@ 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]) hex_common.calculate_attribs() tagregs = hex_common.get_tagregs() tagimms = hex_common.get_tagimms() - with open(sys.argv[3], 'w') as f: + with open(sys.argv[-1], "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 6dea02b0b9..05aa0a7855 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-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 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,685 +22,122 @@ 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 (regid == "y"): - f.write(" offsetof(CPUHexagonState, vtmp);\n") - elif (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") - 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") - elif (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() + is_idef_parser_enabled = hex_common.read_common_files() 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..30f0c73e0c --- /dev/null +++ b/target/hexagon/gen_trans_funcs.py @@ -0,0 +1,143 @@ +#!/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; +## insn->new_read_idx = -1; +## insn->dest_idx = 0; +## insn->has_pred_dest = false; +## 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}; + """)) + + new_read_idx = -1 + dest_idx = -1 + dest_idx_reg_id = None + has_pred_dest = "false" + for regno, (reg_type, reg_id, *_) in enumerate(regs): + reg = hex_common.get_register(tag, reg_type, reg_id) + f.write(code_fmt(f"""\ + insn->regno[{regno}] = args->{reg_type}{reg_id}; + """)) + if reg.is_read() and reg.is_new(): + new_read_idx = regno + if reg.is_written(): + # dest_idx should be the first destination alphabetically + if dest_idx_reg_id is None or reg_id < dest_idx_reg_id: + dest_idx = regno + dest_idx_reg_id = reg_id + if reg_type == "P" and reg.is_written() and not reg.is_read(): + has_pred_dest = "true" + + 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(code_fmt(f"""\ + insn->new_read_idx = {new_read_idx}; + insn->dest_idx = {dest_idx}; + insn->has_pred_dest = {has_pred_dest}; + """)) + f.write(textwrap.dedent(f"""\ + return true; + {close_curly} + """)) + + +if __name__ == "__main__": + hex_common.read_semantics_file(sys.argv[1]) + hex_common.init_registers() + 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 806d0974ff..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,60 +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, int slot) +void gen_store1(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 1, slot); } -static inline void gen_store1i(TCGv_env cpu_env, TCGv vaddr, int32_t src, 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, slot); + gen_store1(tcg_env, vaddr, tmp, slot); } -static inline void gen_store2(TCGv_env cpu_env, TCGv vaddr, TCGv src, int slot) +void gen_store2(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 2, slot); } -static inline void gen_store2i(TCGv_env cpu_env, TCGv vaddr, int32_t src, 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, slot); + gen_store2(tcg_env, vaddr, tmp, slot); } -static inline void gen_store4(TCGv_env cpu_env, TCGv vaddr, TCGv src, int slot) +void gen_store4(TCGv_env tcg_env, TCGv vaddr, TCGv src, uint32_t slot) { gen_store32(vaddr, src, 4, slot); } -static inline void gen_store4i(TCGv_env cpu_env, TCGv vaddr, int32_t src, 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, slot); + gen_store4(tcg_env, vaddr, tmp, slot); } -static inline void gen_store8(TCGv_env cpu_env, TCGv vaddr, TCGv_i64 src, 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); } -static inline void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, 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, 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); @@ -456,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]); @@ -470,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]); } } @@ -544,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; } @@ -609,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) { @@ -618,19 +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); } -static void probe_noshuf_load(TCGv va, int s, int mi) +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(cpu_env, va, size, mem_idx); + 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 368f0b5708..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) @@ -107,4 +109,4 @@ 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 d9ba7df786..15ed4980e4 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 ## -## Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. +## Copyright(c) 2019-2024 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,17 @@ 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... +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,85 +39,109 @@ 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('fSTORE', 'A_SCALAR_STORE') + 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') + add_qemu_macro_attrib('fREAD_SP', 'A_IMPLICIT_READS_SP') # 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 @@ -122,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 @@ -157,72 +189,95 @@ 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 need_p0(tag): + return "A_IMPLICIT_READS_P0" in attribdict[tag] -def is_written(regid): - return regid[0] in "dexy" -def is_writeonly(regid): - return regid[0] in "de" +def need_sp(tag): + return "A_IMPLICIT_READS_SP" in attribdict[tag] -def is_read(regid): - return regid[0] in "stuvwxy" -def is_readwrite(regid): - return regid[0] in "xy" +def is_hvx_insn(tag): + return "A_CVI" in attribdict[tag] -def is_scalar_reg(regtype): - return regtype in "RPC" -def is_hvx_reg(regtype): - return regtype in "VQ" +def need_env(tag): + return ("A_STORE" in attribdict[tag] or + "A_LOAD" in attribdict[tag] or + "A_CVI_GATHER" in attribdict[tag] or + "A_CVI_SCATTER" in attribdict[tag] or + "A_IMPLICIT_WRITES_USR" in attribdict[tag]) -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 is_hvx_insn(tag): + return "A_CVI" in attribdict[tag] + + +def has_hvx_helper(tag): + return (is_hvx_insn(tag) and + not skip_qemu_helper(tag) and + not is_idef_parser_enabled(tag)) + 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"): @@ -231,20 +286,917 @@ 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(): - 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} 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" + def is_old(self): + return True + def is_new(self): + return False + +class NewSource(Source): + def reg_tcg(self): + return f"{self.regtype}{self.regid}N" + def is_old(self): + return False + def is_new(self): + return True + +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 + def is_old(self): + return True + def is_new(self): + return False + +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): + 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): + 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): + f.write(code_fmt(f"""\ + ctx_log_reg_read_new(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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_reg_read(ctx, {self.reg_num}); + """)) + def analyze_write(self, f, tag, 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): + 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): + 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): + 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): + 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): + 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): + f.write(code_fmt(f"""\ + ctx_log_pred_read_new(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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_pred_read(ctx, {self.reg_num}); + """)) + def analyze_write(self, f, tag, 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): + 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): + 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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_reg_read_pair(ctx, {self.reg_num}); + """)) + def analyze_write(self, f, tag, 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): + 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): + 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): + 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}, + insn_has_hvx_helper); + """)) + +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): + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + +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): + f.write(code_fmt(f"""\ + ctx_log_vreg_read_new(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + +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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + def analyze_write(self, f, tag, 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}, + insn_has_hvx_helper); + """)) + +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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_vreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + def analyze_write(self, f, tag, 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}, + insn_has_hvx_helper); + """)) + +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): + 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}, + insn_has_hvx_helper); + """)) + +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): + f.write(code_fmt(f"""\ + ctx_log_vreg_read_pair(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + +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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_vreg_read_pair(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + def analyze_write(self, f, tag, 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}, + insn_has_hvx_helper); + """)) + +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): + f.write(code_fmt(f"""\ + ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + +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): + f.write(code_fmt(f"""\ + ctx_log_qreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + +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_read(self, f, regno): + f.write(code_fmt(f"""\ + ctx_log_qreg_read(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + def analyze_write(self, f, tag, regno): + f.write(code_fmt(f"""\ + ctx_log_qreg_write(ctx, {self.reg_num}, insn_has_hvx_helper); + """)) + +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 + if need_env(tag): + 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_p0(tag): + args.append(HelperArg( + "i32", + "hex_pred[0]", + "uint32_t P0" + )) + if need_sp(tag): + args.append(HelperArg( + "i32", + "hex_gpr[HEX_REG_SP]", + "uint32_t SP" + )) + 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 + + +def read_common_files(): + read_semantics_file(sys.argv[1]) + read_overrides_file(sys.argv[2]) + read_overrides_file(sys.argv[3]) + ## Whether or not idef-parser is enabled is + ## determined by the number of arguments to + ## this script: + ## + ## 4 args. -> not enabled, + ## 5 args. -> idef-parser enabled. + ## + ## The 5:th arg. then holds a list of the successfully + ## parsed instructions. + is_idef_parser_enabled = len(sys.argv) > 5 + if is_idef_parser_enabled: + read_idef_parser_enabled_file(sys.argv[4]) + calculate_attribs() + init_registers() + return is_idef_parser_enabled 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..7199177ee3 --- /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.h.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.h.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..8594cbe3a2 --- /dev/null +++ b/target/hexagon/idef-parser/idef-parser.h @@ -0,0 +1,241 @@ +/* + * 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 + +/* 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..c6f17c6afa --- /dev/null +++ b/target/hexagon/idef-parser/idef-parser.y @@ -0,0 +1,912 @@ +%{ +/* + * 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); + } + | 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 + { + 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.h.inc b/target/hexagon/idef-parser/macros.h.inc new file mode 100644 index 0000000000..94975d9583 --- /dev/null +++ b/target/hexagon/idef-parser/macros.h.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..a7dcd85fe4 --- /dev/null +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -0,0 +1,2148 @@ +/* + * 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 instruction arguments. Only needed for + * predicate arguments, initialization of registers is handled by the Hexagon + * frontend. + */ +void gen_inst_init_args(Context *c, YYLTYPE *locp) +{ + HexValue *val = NULL; + char suffix; + + /* If init_list is NULL arguments have already been initialized */ + if (!c->inst.init_list) { + return; + } + + for (unsigned i = 0; i < c->inst.init_list->len; i++) { + val = &g_array_index(c->inst.init_list, HexValue, i); + suffix = val->is_dotnew ? 'N' : 'V'; + yyassert(c, locp, val->type == PREDICATE, + "Only predicates need to be initialized!"); + yyassert(c, locp, val->bit_width == 32, + "Predicates should always be 32 bits"); + EMIT_HEAD(c, "tcg_gen_movi_i32(P%c%c, 0);\n", val->pred.id, suffix); + } + + /* 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 list of arguments that might need initialization, if they haven't + * already been freed. + */ + if (c->inst.init_list) { + g_array_free(c->inst.init_list, TRUE); + } + /* Free INAME token value */ + g_string_free(c->inst.name, TRUE); + /* Free declared TCGv variables */ + 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..2087d534a9 --- /dev/null +++ b/target/hexagon/idef-parser/parser-helpers.h @@ -0,0 +1,353 @@ +/* + * 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__) + +/** + * 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); + +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_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_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 d71c04cd30..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) ") diff --git a/target/hexagon/imported/ldst.idef b/target/hexagon/imported/ldst.idef index 237634bdd9..53198176a9 100644 --- a/target/hexagon/imported/ldst.idef +++ b/target/hexagon/imported/ldst.idef @@ -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 @@ -128,6 +128,24 @@ Q6INSN(S2_allocframe,"allocframe(Rx32,#u11:3):raw", ATTRIBS(A_REGWRSIZE_8B,A_MEM #define A_RETURN A_RESTRICT_COF_MAX1,A_RESTRICT_SLOT0ONLY,A_RESTRICT_NOSLOT1_STORE,A_RET_TYPE,A_DEALLOCRET +/**** Load Acquire Store Release Instructions****/ + + + +Q6INSN(L2_loadw_aq,"Rd32=memw_aq(Rs32)",ATTRIBS(A_REGWRSIZE_4B,A_ACQUIRE,A_RESTRICT_SLOT0ONLY,A_MEMSIZE_4B,A_LOAD),"Load Acquire Word", +{ fEA_REG(RsV); fLOAD(1,4,u,EA,RdV); }) +Q6INSN(L4_loadd_aq,"Rdd32=memd_aq(Rs32)",ATTRIBS(A_REGWRSIZE_8B,A_ACQUIRE,A_RESTRICT_SLOT0ONLY,A_MEMSIZE_8B,A_LOAD),"Load Acquire Double integer", +{ fEA_REG(RsV); fLOAD(1,8,u,EA,RddV); }) + +Q6INSN(R6_release_at_vi,"release(Rs32):at",ATTRIBS(A_MEMSIZE_0B,A_RELEASE,A_STORE,A_VTCM_ALLBANK_ACCESS,A_RLS_INNER,A_RLS_ALL_THREAD,A_RESTRICT_NOPACKET,A_RESTRICT_SLOT0ONLY), "Release lock", {fEA_REG(RsV); fSTORE(1,0,EA,RsV); }) +Q6INSN(R6_release_st_vi,"release(Rs32):st",ATTRIBS(A_MEMSIZE_0B,A_RELEASE,A_STORE,A_VTCM_ALLBANK_ACCESS,A_RLS_INNER,A_RLS_SAME_THREAD,A_RESTRICT_NOPACKET,A_RESTRICT_SLOT0ONLY), "Release lock", {fEA_REG(RsV); fSTORE(1,0,EA,RsV); }) + +Q6INSN(S2_storew_rl_at_vi,"memw_rl(Rs32):at=Rt32",ATTRIBS(A_REGWRSIZE_4B,A_RELEASE,A_VTCM_ALLBANK_ACCESS,A_RLS_INNER,A_RLS_ALL_THREAD,A_RESTRICT_NOPACKET,A_MEMSIZE_4B,A_STORE,A_RESTRICT_SLOT0ONLY),"Store Release Word", { fEA_REG(RsV); fSTORE(1,4,EA,RtV); }) +Q6INSN(S4_stored_rl_at_vi,"memd_rl(Rs32):at=Rtt32",ATTRIBS(A_REGWRSIZE_8B,A_RELEASE,A_VTCM_ALLBANK_ACCESS,A_RLS_INNER,A_RLS_ALL_THREAD,A_RESTRICT_NOPACKET,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY),"Store Release Double integer", { fEA_REG(RsV); fSTORE(1,8,EA,RttV); }) + +Q6INSN(S2_storew_rl_st_vi,"memw_rl(Rs32):st=Rt32",ATTRIBS(A_REGWRSIZE_4B,A_RELEASE,A_VTCM_ALLBANK_ACCESS,A_RLS_INNER,A_RLS_SAME_THREAD,A_RESTRICT_NOPACKET,A_MEMSIZE_4B,A_STORE,A_RESTRICT_SLOT0ONLY),"Store Release Word", { fEA_REG(RsV); fSTORE(1,4,EA,RtV); }) +Q6INSN(S4_stored_rl_st_vi,"memd_rl(Rs32):st=Rtt32",ATTRIBS(A_REGWRSIZE_8B,A_RELEASE,A_VTCM_ALLBANK_ACCESS,A_RLS_INNER,A_RLS_SAME_THREAD,A_RESTRICT_NOPACKET,A_MEMSIZE_8B,A_STORE,A_RESTRICT_SLOT0ONLY),"Store Release Double integer", { fEA_REG(RsV); fSTORE(1,8,EA,RttV); }) + Q6INSN(L2_deallocframe,"Rdd32=deallocframe(Rs32):raw", ATTRIBS(A_REGWRSIZE_8B,A_MEMSIZE_8B,A_LOAD,A_DEALLOCFRAME), "Deallocate stack frame", { fHIDE(size8u_t tmp;) fEA_REG(RsV); fLOAD(1,8,u,EA,tmp); diff --git a/target/hexagon/imported/macros.def b/target/hexagon/imported/macros.def index e23f91562e..4bbcfdd5e1 100755 --- a/target/hexagon/imported/macros.def +++ b/target/hexagon/imported/macros.def @@ -902,7 +902,7 @@ DEF_MACRO( ) DEF_MACRO( - fEA_GPI, /* Calculate EA with Global Poitner + Immediate */ + fEA_GPI, /* Calculate EA with Global Pointer + Immediate */ do { EA=fREAD_GP()+IMM; fGP_DOCHKPAGECROSS(fREAD_GP(),EA); } while (0), () ) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 6fbbe2c422..402438f566 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.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 @@ -257,6 +257,11 @@ DEF_ENC(V6_vasruhubrndsat, ICLASS_CJ" 1 000 vvv vvttt PP 0 uuuuu 111 ddd DEF_ENC(V6_vasruwuhsat, ICLASS_CJ" 1 000 vvv vvttt PP 1 uuuuu 100 ddddd") // DEF_ENC(V6_vasruhubsat, ICLASS_CJ" 1 000 vvv vvttt PP 1 uuuuu 101 ddddd") // +DEF_ENC(V6_vasrvuhubrndsat,"00011101000vvvvvPP0uuuuu011ddddd") +DEF_ENC(V6_vasrvuhubsat,"00011101000vvvvvPP0uuuuu010ddddd") +DEF_ENC(V6_vasrvwuhrndsat,"00011101000vvvvvPP0uuuuu001ddddd") +DEF_ENC(V6_vasrvwuhsat,"00011101000vvvvvPP0uuuuu000ddddd") + /*************************************************************** * * Group #1, Uses Q6 Rt32 @@ -716,6 +721,7 @@ DEF_ENC(V6_vaddclbw, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 001 ddddd") // DEF_ENC(V6_vavguw, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 010 ddddd") // DEF_ENC(V6_vavguwrnd, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 011 ddddd") // +DEF_ENC(V6_vassign_tmp,"00011110--0---01PP0uuuuu110ddddd") DEF_ENC(V6_vavgb, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 100 ddddd") // DEF_ENC(V6_vavgbrnd, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 101 ddddd") // DEF_ENC(V6_vnavgb, ICLASS_CJ" 1 111 000 vvvvv PP 1 uuuuu 110 ddddd") // @@ -730,6 +736,8 @@ DEF_ENC(V6_vmaxb, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 101 ddddd") // DEF_ENC(V6_vsatuwuh, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 110 ddddd") // DEF_ENC(V6_vdealb4w, ICLASS_CJ" 1 111 001 vvvvv PP 0 uuuuu 111 ddddd") // +DEF_ENC(V6_v6mpyvubs10_vxx, ICLASS_CJ" 1 111 001 vvvvv PP 1 uuuuu 0ii xxxxx") +DEF_ENC(V6_v6mpyhubs10_vxx, ICLASS_CJ" 1 111 001 vvvvv PP 1 uuuuu 1ii xxxxx") DEF_ENC(V6_vmpyowh_rnd, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 000 ddddd") // DEF_ENC(V6_vshuffeb, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 001 ddddd") // @@ -739,6 +747,11 @@ DEF_ENC(V6_vshufoh, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 100 ddddd") // DEF_ENC(V6_vshufoeh, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 101 ddddd") // DEF_ENC(V6_vshufoeb, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 110 ddddd") // DEF_ENC(V6_vcombine, ICLASS_CJ" 1 111 010 vvvvv PP 0 uuuuu 111 ddddd") // +DEF_ENC(V6_vcombine_tmp,"00011110101vvvvvPP0uuuuu111ddddd") + +DEF_ENC(V6_v6mpyvubs10, ICLASS_CJ" 1 111 010 vvvvv PP 1 uuuuu 0ii ddddd") +DEF_ENC(V6_v6mpyhubs10, ICLASS_CJ" 1 111 010 vvvvv PP 1 uuuuu 1ii ddddd") + DEF_ENC(V6_vmpyieoh, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 000 ddddd") // DEF_ENC(V6_vadduwsat, ICLASS_CJ" 1 111 011 vvvvv PP 0 uuuuu 001 ddddd") // @@ -789,6 +802,7 @@ DEF_ENC(V6_vrounduhub, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 011 ddddd") // DEF_ENC(V6_vrounduwuh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 100 ddddd") // DEF_ENC(V6_vmpyewuh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 101 ddddd") DEF_ENC(V6_vmpyowh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 111 ddddd") +DEF_ENC(V6_vmpyuhvs,"00011111110vvvvvPP1uuuuu111ddddd") #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 8ca5a606e1..03d31f6181 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.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 @@ -17,7 +17,7 @@ /****************************************************************************** * - * HOYA: MULTI MEDIA INSTRUCITONS + * HOYA: MULTI MEDIA INSTRUCTIONS * ******************************************************************************/ @@ -62,6 +62,9 @@ EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +#define ITERATOR_INSN_SHIFT3_SLOT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_CVI_VS_3SRC,A_NOTE_SHIFT_RESOURCE,A_NOTE_NOVP,A_NOTE_VA_UNARY), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) #define ITERATOR_INSN_SHIFT_SLOT_VV_LATE(WIDTH,TAG,SYNTAX,DESCR,CODE) \ EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ @@ -116,6 +119,10 @@ ITERATOR_INSN_MPY_SLOT_LATE(WIDTH,TAG, SYNTAX2,DESCR,CODE) EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX_DV), \ DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +#define ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC_VX_FWD(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VX_DV), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + #define ITERATOR_INSN2_MPY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ ITERATOR_INSN_MPY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX2,DESCR,CODE) @@ -288,7 +295,7 @@ MMVEC_COND_EACH_EA(vS32Ub,"Unaligned Vector Store",ATTRIBS(ATTR_VMEMU,A_STORE,A_ MMVEC_EACH_EA(vS32b_new,"Aligned Vector Store New",ATTRIBS(ATTR_VMEM,A_STORE,A_CVI_NEW,A_DOTNEWVALUE,A_RESTRICT_SLOT0ONLY),,"vmem","=Os8.new",fSTOREMMV(EA,fNEWVREG(OsN))) -// V65 store relase, zero byte store +// V65 store release, zero byte store MMVEC_EACH_EA(vS32b_srls,"Aligned Vector Scatter Release",ATTRIBS(ATTR_VMEM,A_STORE,A_CVI_SCATTER_RELEASE,A_CVI_NEW,A_RESTRICT_SLOT0ONLY),,"vmem",":scatter_release",fSTORERELEASE(EA,0)) @@ -976,6 +983,22 @@ NARROWING_SHIFT(16,vasrhubrndsat,fSETBYTE,ub,h,:rnd:sat,fVSATUB,fVROUND,0x7) NARROWING_SHIFT(16,vasrhbsat,fSETBYTE,b,h,:sat,fVSATB,fVNOROUND,0x7) NARROWING_SHIFT(16,vasrhbrndsat,fSETBYTE,b,h,:rnd:sat,fVSATB,fVROUND,0x7) +#define NARROWING_VECTOR_SHIFT(ITERSIZE,TAG,DSTM,DSTTYPE,SRCTYPE,SRCTYPE2,SYNOPTS,SATFUNC,RNDFUNC,SHAMTMASK) \ +ITERATOR_INSN_SHIFT3_SLOT(ITERSIZE,TAG, \ +"Vd32." #DSTTYPE "=vasr(Vuu32." #SRCTYPE ",Vv32." #SRCTYPE2 ")" #SYNOPTS, \ +"Vector shift by vector right and shuffle", \ + fHIDE(int )shamt = VvV.SRCTYPE2[2*i+0] & SHAMTMASK; \ + DSTM(0,VdV.SRCTYPE[i],SATFUNC(RNDFUNC(VuuV.v[0].SRCTYPE[i],shamt) >> 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", { @@ -2536,7 +2855,7 @@ EXTINSN(V6_vscattermhw_add, "vscatter(Rt32,Mu2,Vvv32.w).h+=Vw32", ATTRIBS(A_EXT fVALIGN(RtV, element_size); fVFOREACH(32, i) { for(j = 0; j < 2; j++) { - EA = RtV + fVALIGN(VvvV.v[j].uw[i],ALIGNMENT);; + EA = RtV + fVALIGN(VvvV.v[j].uw[i],ALIGNMENT); fVLOG_VTCM_HALFWORD_INCREMENT_DV(EA,VvvV.v[j].uw[i],VwV,(2*i+j),i,j,ALIGNMENT,MuV); } } diff --git a/target/hexagon/insn.h b/target/hexagon/insn.h index aa26389147..24dcf7fe9f 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-2024 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 */ @@ -42,6 +39,9 @@ struct Instruction { uint32_t slot:3; uint32_t which_extended:1; /* If has an extender, which immediate */ uint32_t new_value_producer_slot:4; + int32_t new_read_idx; + int32_t dest_idx; + bool has_pred_dest; bool part1; /* * cmp-jumps are split into two insns. @@ -57,9 +57,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 c8805bdaeb..ee3d4c88e7 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-2024 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,19 +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) - #define GET_FIELD(FIELD, REGIN) \ fEXTRACTU_BITS(REGIN, reg_field_info[FIELD].width, \ reg_field_info[FIELD].offset) @@ -48,22 +35,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 @@ -94,60 +82,60 @@ */ #define CHECK_NOSHUF(VA, SIZE) \ do { \ - if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ probe_noshuf_load(VA, SIZE, ctx->mem_idx); \ - process_store(ctx, pkt, 1); \ + process_store(ctx, 1); \ } \ } while (0) #define CHECK_NOSHUF_PRED(GET_EA, SIZE, PRED) \ do { \ - TCGLabel *label = gen_new_label(); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, label); \ + TCGLabel *noshuf_label = gen_new_label(); \ + tcg_gen_brcondi_tl(TCG_COND_EQ, PRED, 0, noshuf_label); \ GET_EA; \ - if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ + if (insn->slot == 0 && ctx->pkt->pkt_has_store_s1) { \ probe_noshuf_load(EA, SIZE, ctx->mem_idx); \ } \ - gen_set_label(label); \ - if (insn->slot == 0 && pkt->pkt_has_store_s1) { \ - process_store(ctx, pkt, 1); \ + 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(VA, 1); \ - tcg_gen_qemu_ld8s(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_SB); \ } while (0) #define MEM_LOAD1u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 1); \ - tcg_gen_qemu_ld8u(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_UB); \ } while (0) #define MEM_LOAD2s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld16s(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESW); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld16u(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUW); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld32s(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESL); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld32s(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUL); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 8); \ - tcg_gen_qemu_ld64(DST, VA, ctx->mem_idx); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_TEUQ); \ } while (0) #define MEM_STORE1_FUNC(X) \ @@ -156,7 +144,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, SLOT) + MEM_STORE1_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #define MEM_STORE2_FUNC(X) \ __builtin_choose_expr(TYPE_INT(X), \ @@ -164,7 +152,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, SLOT) + MEM_STORE2_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #define MEM_STORE4_FUNC(X) \ __builtin_choose_expr(TYPE_INT(X), \ @@ -172,7 +160,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, SLOT) + MEM_STORE4_FUNC(DATA)(tcg_env, VA, DATA, SLOT) #define MEM_STORE8_FUNC(X) \ __builtin_choose_expr(TYPE_INT(X), \ @@ -180,44 +168,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, 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)) @@ -253,12 +224,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 @@ -367,81 +334,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() (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() (P0) #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)) @@ -503,7 +429,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 { \ @@ -534,8 +459,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) @@ -593,13 +517,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) @@ -709,22 +641,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..f1723778a6 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-2024 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,9 +18,9 @@ hexagon_ss = ss.source_set() 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 @@ -41,85 +41,35 @@ 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 # opcodes_def_generated.h.inc # -shortcode_generated = custom_target( - 'shortcode_generated.h.inc', - output: 'shortcode_generated.h.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_shortcode.py'), semantics_generated, attribs_def, '@OUTPUT@'], -) -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', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_tcg_func_table.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_tcg_func_table.py'), semantics_generated, '@OUTPUT@'], ) 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', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_printinsn.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_printinsn.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(printinsn_generated) -op_regs_generated = custom_target( - 'op_regs_generated.h.inc', - output: 'op_regs_generated.h.inc', - depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_op_regs.py'), semantics_generated, attribs_def, '@OUTPUT@'], -) -hexagon_ss.add(op_regs_generated) - op_attribs_generated = custom_target( 'op_attribs_generated.h.inc', output: 'op_attribs_generated.h.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_op_attribs.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_op_attribs.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(op_attribs_generated) @@ -127,8 +77,8 @@ opcodes_def_generated = custom_target( 'opcodes_def_generated.h.inc', output: 'opcodes_def_generated.h.inc', depends: [semantics_generated], - depend_files: [hex_common_py, attribs_def], - command: [python, files('gen_opcodes_def.py'), semantics_generated, attribs_def, '@OUTPUT@'], + depend_files: [hex_common_py], + command: [python, files('gen_opcodes_def.py'), semantics_generated, '@OUTPUT@'], ) hexagon_ss.add(opcodes_def_generated) @@ -139,7 +89,7 @@ hexagon_ss.add(opcodes_def_generated) # gen_dectree_import = executable( 'gen_dectree_import', - 'gen_dectree_import.c', opcodes_def_generated, op_regs_generated, + 'gen_dectree_import.c', opcodes_def_generated, native: true, build_by_default: false) iset_py = custom_target( @@ -151,16 +101,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 +262,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, '@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.h.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, static: false) + + 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, 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, 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, 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, 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, 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, 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..f850d0154d 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 @@ -28,20 +28,15 @@ check_new_value(Packet *pkt) { /* .new value for a MMVector store */ int i, j; - const char *reginfo; - const char *destletters; - 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; if (GET_ATTRIB(use_opcode, A_DOTNEWVALUE) && GET_ATTRIB(use_opcode, A_CVI) && GET_ATTRIB(use_opcode, A_STORE)) { - int use_regidx = strchr(opcode_reginfo[use_opcode], 's') - - opcode_reginfo[use_opcode]; + int use_regidx = pkt->insn[i].new_read_idx; + g_assert(pkt->insn[i].new_read_idx != -1); /* * What's encoded at the N-field is the offset to who's producing * the value. @@ -69,32 +64,19 @@ check_new_value(Packet *pkt) /* def_idx is the index of the producer */ def_opcode = pkt->insn[def_idx].opcode; - reginfo = opcode_reginfo[def_opcode]; - destletters = "dexy"; - for (j = 0; (letter = destletters[j]) != 0; j++) { - dststr = strchr(reginfo, letter); - if (dststr != NULL) { - break; - } - } - if ((dststr == NULL) && GET_ATTRIB(def_opcode, A_CVI_GATHER)) { - def_regnum = 0; + if ((pkt->insn[def_idx].dest_idx == -1) && + GET_ATTRIB(def_opcode, A_CVI_GATHER)) { pkt->insn[i].regno[use_regidx] = def_oreg; pkt->insn[i].new_value_producer_slot = pkt->insn[def_idx].slot; } else { - if (dststr == NULL) { + if (pkt->insn[def_idx].dest_idx == -1) { /* 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[pkt->insn[def_idx].dest_idx]; /* Now patch up the consumer with the register number */ pkt->insn[i].regno[use_regidx] = def_regnum ^ def_oreg; - /* special case for (Vx,Vy) */ - dststr = strchr(reginfo, 'y'); - if (def_oreg && strchr(reginfo, 'x') && dststr) { - def_regnum = pkt->insn[def_idx].regno[dststr - reginfo]; - pkt->insn[i].regno[use_regidx] = def_regnum; - } /* * We need to remember who produces this value to later * check if it was dynamically cancelled @@ -148,9 +130,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 085afc3274..90e7aaa097 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-2024 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,15 +29,16 @@ #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 /* Exceptions processing helpers */ -static G_NORETURN -void do_raise_exception_err(CPUHexagonState *env, - uint32_t exception, - uintptr_t pc) +G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, + uint32_t exception, + uintptr_t pc) { CPUState *cs = env_cpu(env); qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); @@ -47,43 +48,11 @@ void do_raise_exception_err(CPUHexagonState *env, G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp) { - do_raise_exception_err(env, excp, 0); + hexagon_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 +62,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 +73,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 +94,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 +117,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 +131,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 +160,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 +206,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 +223,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 +248,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 +355,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,13 +388,94 @@ 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); } } @@ -454,18 +491,20 @@ void HELPER(probe_noshuf_load)(CPUHexagonState *env, target_ulong va, } /* Called during packet commit when there are two scalar stores */ -void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int mmu_idx) +void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args) { - probe_store(env, 0, mmu_idx); + 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); } -void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx) +static void probe_hvx_stores(CPUHexagonState *env, int mmu_idx, + uintptr_t retaddr) { - uintptr_t retaddr = 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]) { target_ulong va = env->vstore[i].va; int size = env->vstore[i].size; @@ -500,24 +539,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 @@ -525,47 +575,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, - target_ulong vaddr, int size) +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(probe_noshuf_load)(env, vaddr, size, MMU_USER_IDX); - 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, vaddr, 1); - 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, vaddr, 2); - 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, vaddr, 4); - 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, vaddr, 8); - return cpu_ldq_data_ra(env, vaddr, ra); -} +#endif /* Floating point */ float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV) @@ -663,7 +683,7 @@ uint32_t HELPER(conv_sf2uw)(CPUHexagonState *env, float32 RsV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -693,7 +713,7 @@ uint64_t HELPER(conv_sf2ud)(CPUHexagonState *env, float32 RsV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -723,7 +743,7 @@ uint32_t HELPER(conv_df2uw)(CPUHexagonState *env, float64 RssV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -753,7 +773,7 @@ uint64_t HELPER(conv_df2ud)(CPUHexagonState *env, float64 RssV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -783,7 +803,7 @@ uint32_t HELPER(conv_sf2uw_chop)(CPUHexagonState *env, float32 RsV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -813,7 +833,7 @@ uint64_t HELPER(conv_sf2ud_chop)(CPUHexagonState *env, float32 RsV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float32_is_neg(RsV) && !float32_is_any_nan(RsV)) { + if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -843,7 +863,7 @@ uint32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV) uint32_t RdV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RdV = 0; } else { @@ -873,7 +893,7 @@ uint64_t HELPER(conv_df2ud_chop)(CPUHexagonState *env, float64 RssV) uint64_t RddV; arch_fpop_start(env); /* Hexagon checks the sign before rounding */ - if (float64_is_neg(RssV) && !float64_is_any_nan(RssV)) { + if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) { float_raise(float_flag_invalid, &env->fp_status); RddV = 0; } else { @@ -1190,7 +1210,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; @@ -1465,12 +1485,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..c8bde2f9e9 100644 --- a/target/hexagon/opcodes.c +++ b/target/hexagon/opcodes.c @@ -36,41 +36,6 @@ const char * const opcode_names[] = { #undef OPCODE }; -const char * const opcode_reginfo[] = { -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#define REGINFO(TAG, REGINFO, RREGS, WREGS) REGINFO, -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - - -const char * const opcode_rregs[] = { -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#define REGINFO(TAG, REGINFO, RREGS, WREGS) RREGS, -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - - -const char * const opcode_wregs[] = { -#define IMMINFO(TAG, SIGN, SIZE, SHAMT, SIGN2, SIZE2, SHAMT2) /* nothing */ -#define REGINFO(TAG, REGINFO, RREGS, WREGS) WREGS, -#include "op_regs_generated.h.inc" - NULL -#undef REGINFO -#undef IMMINFO -}; - -const char * const opcode_short_semantics[] = { -#define DEF_SHORTCODE(TAG, SHORTCODE) [TAG] = #SHORTCODE, -#include "shortcode_generated.h.inc" -#undef DEF_SHORTCODE - NULL -}; DECLARE_BITMAP(opcode_attribs[XX_LAST_OPCODE], A_ZZ_LASTATTRIB); @@ -111,33 +76,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..0ee11bd445 100644 --- a/target/hexagon/opcodes.h +++ b/target/hexagon/opcodes.h @@ -40,10 +40,6 @@ typedef enum { extern const char * const opcode_names[]; -extern const char * const opcode_reginfo[]; -extern const char * const opcode_rregs[]; -extern const char * const opcode_wregs[]; - typedef struct { const char * const encoding; const EncClass enc_class; @@ -53,6 +49,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 2329177537..4b1bee3c6d 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2019-2022 Qualcomm Innovation Center, Inc. All Rights Reserved. + * Copyright(c) 2019-2024 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,9 @@ #include "cpu.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" +#include "exec/helper-gen.h" +#include "exec/helper-proto.h" +#include "exec/translation-block.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "internal.h" @@ -27,29 +30,34 @@ #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 +71,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 +116,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 +129,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 +235,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 +256,295 @@ 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 (ctx->read_after_write || ctx->has_hvx_overlap) { + 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->read_after_write = false; + ctx->has_hvx_overlap = 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->predicated_regs, TOTAL_PER_THREAD_REGS); ctx->preg_log_idx = 0; bitmap_zero(ctx->pregs_written, NUM_PREGS); ctx->future_vregs_idx = 0; ctx->tmp_vregs_idx = 0; ctx->vreg_log_idx = 0; + bitmap_zero(ctx->vregs_written, NUM_VREGS); 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->qregs_written, 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 +558,16 @@ 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]); - } - - ctx_log_reg_write(ctx, rnum); - } -} - -static void mark_implicit_pred_write(DisasContext *ctx, Insn *insn, - int attrib, int pnum) -{ - 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 mark_store_width(DisasContext *ctx, Insn *insn) -{ - uint16_t opcode = insn->opcode; - uint32_t slot = insn->slot; + 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; + } if (GET_ATTRIB(opcode, A_MEMSIZE_1B)) { width |= 1; } @@ -351,14 +585,11 @@ static void mark_store_width(DisasContext *ctx, Insn *insn) } } -static void gen_insn(CPUHexagonState *env, DisasContext *ctx, - Insn *insn, Packet *pkt) +static void gen_insn(DisasContext *ctx) { - if (insn->generate) { - mark_implicit_reg_writes(ctx, insn); - insn->generate(env, ctx, insn, pkt); - mark_implicit_pred_writes(ctx, insn); - mark_store_width(ctx, insn); + if (ctx->insn->generate) { + ctx->insn->generate(ctx); + mark_store_width(ctx); } else { gen_exception_end_tb(ctx, HEX_EXCP_INVALID_OPCODE); } @@ -371,60 +602,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); } } @@ -439,9 +655,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; /* @@ -460,10 +676,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]); /* @@ -477,27 +692,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: { @@ -507,51 +722,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. */ + Packet *pkt = ctx->pkt; if (pkt->pkt_has_store_s1) { g_assert(!pkt->pkt_has_dczeroa); - process_store(ctx, pkt, 1); + process_store(ctx, 1); } if (pkt->pkt_has_store_s0) { g_assert(!pkt->pkt_has_dczeroa); - process_store(ctx, pkt, 0); + 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); } } @@ -567,83 +780,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; @@ -664,8 +848,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 @@ -684,6 +867,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); @@ -693,45 +877,66 @@ static void gen_commit_packet(CPUHexagonState *env, DisasContext *ctx, * a store in slot 1 or an HVX store. */ g_assert(!has_store_s1 && !has_hvx_store); - process_dczeroa(ctx, pkt); + 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); @@ -739,12 +944,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) { @@ -765,13 +972,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); @@ -782,11 +992,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) @@ -807,7 +1022,7 @@ static bool pkt_crosses_page(CPUHexagonState *env, DisasContext *ctx) int nwords; for (nwords = 0; !found_end && nwords < PACKET_WORDS_MAX; nwords++) { - uint32_t word = cpu_ldl_code(env, + uint32_t word = translator_ldl(env, &ctx->base, ctx->base.pc_next + nwords * sizeof(uint32_t)); found_end = is_packet_end(word); } @@ -818,7 +1033,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); @@ -860,25 +1075,16 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void hexagon_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - - static const TranslatorOps hexagon_tr_ops = { .init_disas_context = hexagon_tr_init_disas_context, .tb_start = hexagon_tr_tb_start, .insn_start = hexagon_tr_insn_start, .translate_insn = hexagon_tr_translate_packet, .tb_stop = hexagon_tr_tb_stop, - .disas_log = hexagon_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; @@ -887,9 +1093,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, } #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]; @@ -905,92 +1109,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..00cc2bcd63 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-2024 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,6 +38,7 @@ typedef struct DisasContext { int reg_log[REG_WRITES_MAX]; int reg_log_idx; DECLARE_BITMAP(regs_written, 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); @@ -44,43 +49,100 @@ 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_written, NUM_VREGS); + DECLARE_BITMAP(insn_vregs_written, NUM_VREGS); 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(insn_vregs_read, NUM_VREGS); int qreg_log[NUM_QREGS]; - bool qreg_is_predicated[NUM_QREGS]; int qreg_log_idx; + DECLARE_BITMAP(qregs_written, NUM_QREGS); + DECLARE_BITMAP(insn_qregs_written, NUM_QREGS); + DECLARE_BITMAP(insn_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 read_after_write; + bool has_hvx_overlap; + 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); -} +bool is_gather_store_insn(DisasContext *ctx); 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); + if (test_bit(pnum, ctx->pregs_written)) { + ctx->read_after_write = true; + } +} + +static inline void ctx_log_pred_read_new(DisasContext *ctx, int pnum) +{ + g_assert(test_bit(pnum, ctx->pregs_written)); +} + +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) +{ + if (test_bit(rnum, ctx->regs_written)) { + ctx->read_after_write = true; + } +} + +static inline void ctx_log_reg_read_new(DisasContext *ctx, int rnum) +{ + g_assert(test_bit(rnum, ctx->regs_written)); +} + +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, @@ -88,65 +150,149 @@ intptr_t ctx_future_vreg_off(DisasContext *ctx, int regnum, intptr_t ctx_tmp_vreg_off(DisasContext *ctx, int regnum, int num, bool alloc_ok); +static inline void ctx_start_hvx_insn(DisasContext *ctx) +{ + bitmap_zero(ctx->insn_vregs_written, NUM_VREGS); + bitmap_zero(ctx->insn_vregs_read, NUM_VREGS); + bitmap_zero(ctx->insn_qregs_written, NUM_QREGS); + bitmap_zero(ctx->insn_qregs_read, NUM_QREGS); +} + static inline void ctx_log_vreg_write(DisasContext *ctx, int rnum, VRegWriteType type, - bool is_predicated) + bool is_predicated, bool has_helper) { + if (has_helper) { + set_bit(rnum, ctx->insn_vregs_written); + if (test_bit(rnum, ctx->insn_vregs_read)) { + ctx->has_hvx_overlap = true; + } + } + set_bit(rnum, ctx->vregs_written); 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); + } } } static inline void ctx_log_vreg_write_pair(DisasContext *ctx, int rnum, VRegWriteType type, - bool is_predicated) + bool is_predicated, bool has_helper) { - ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated); - ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated); + ctx_log_vreg_write(ctx, rnum ^ 0, type, is_predicated, has_helper); + ctx_log_vreg_write(ctx, rnum ^ 1, type, is_predicated, has_helper); +} + +static inline void ctx_log_vreg_read(DisasContext *ctx, int rnum, + bool has_helper) +{ + if (has_helper) { + set_bit(rnum, ctx->insn_vregs_read); + if (test_bit(rnum, ctx->insn_vregs_written)) { + ctx->has_hvx_overlap = true; + } + } + if (test_bit(rnum, ctx->vregs_written)) { + ctx->read_after_write = true; + } +} + +static inline void ctx_log_vreg_read_new(DisasContext *ctx, int rnum, + bool has_helper) +{ + g_assert(is_gather_store_insn(ctx) || + test_bit(rnum, ctx->vregs_updated) || + test_bit(rnum, ctx->vregs_select) || + test_bit(rnum, ctx->vregs_updated_tmp)); + if (has_helper) { + set_bit(rnum, ctx->insn_vregs_read); + if (test_bit(rnum, ctx->insn_vregs_written)) { + ctx->has_hvx_overlap = true; + } + } + if (is_gather_store_insn(ctx)) { + ctx->read_after_write = true; + } +} + +static inline void ctx_log_vreg_read_pair(DisasContext *ctx, int rnum, + bool has_helper) +{ + ctx_log_vreg_read(ctx, rnum ^ 0, has_helper); + ctx_log_vreg_read(ctx, rnum ^ 1, has_helper); } static inline void ctx_log_qreg_write(DisasContext *ctx, - int rnum, bool is_predicated) + int rnum, bool has_helper) { + if (has_helper) { + set_bit(rnum, ctx->insn_qregs_written); + if (test_bit(rnum, ctx->insn_qregs_read)) { + ctx->has_hvx_overlap = true; + } + } + set_bit(rnum, ctx->qregs_written); 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, bool has_helper) +{ + if (has_helper) { + set_bit(qnum, ctx->insn_qregs_read); + if (test_bit(qnum, ctx->insn_qregs_written)) { + ctx->has_hvx_overlap = true; + } + } + if (test_bit(qnum, ctx->qregs_written)) { + ctx->read_after_write = true; + } +} + 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); +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..ef3200f0f3 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -2,33 +2,31 @@ * PA-RISC cpu parameters for qemu. * * Copyright (c) 2016 Richard Henderson - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #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 + +/* PA-RISC 1.x processors have a strong memory model. */ +/* + * ??? While we do not yet implement PA-RISC 2.0, those processors have + * a weak memory model, but with TLB bits that force ordering on a per-page + * basis. It's probably easier to fall back to a strong memory model. + */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL #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 55c190280e..c38439c180 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -26,21 +26,73 @@ #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) { HPPACPU *cpu = HPPA_CPU(cs); +#ifdef CONFIG_USER_ONLY + value |= PRIV_USER; +#endif cpu->env.iaoq_f = value; cpu->env.iaoq_b = value + 4; } static vaddr hppa_cpu_get_pc(CPUState *cs) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); - return cpu->env.iaoq_f; + return hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0), + env->iaoq_f & -4); +} + +void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *pcsbase, uint32_t *pflags) +{ + uint32_t flags = 0; + uint64_t cs_base = 0; + + /* + * 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 privilege from the low bits of IAOQ_F. + */ + *pc = hppa_cpu_get_pc(env_cpu(env)); + flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; + + /* + * The only really interesting case is if IAQ_Back is on the same page + * as IAQ_Front, so that we can use goto_tb between the blocks. In all + * other cases, we'll be ending the TranslationBlock with one insn and + * not linking between them. + */ + if (env->iasq_f != env->iasq_b) { + cs_base |= CS_BASE_DIFFSPACE; + } else if ((env->iaoq_f ^ env->iaoq_b) & TARGET_PAGE_MASK) { + cs_base |= CS_BASE_DIFFPAGE; + } else { + cs_base |= env->iaoq_b & ~TARGET_PAGE_MASK; + } + + /* ??? E, T, H, L bits need to be here, when implemented. */ + flags |= env->psw_n * PSW_N; + flags |= env->psw_xb; + flags |= env->psw & (PSW_W | PSW_C | PSW_D | PSW_P); + +#ifdef CONFIG_USER_ONLY + flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; +#else + if ((env->sr[4] == env->sr[5]) + & (env->sr[4] == env->sr[6]) + & (env->sr[4] == env->sr[7])) { + flags |= TB_FLAG_SR_SAME; + } +#endif + + *pcsbase = cs_base; + *pflags = flags; } static void hppa_cpu_synchronize_from_tb(CPUState *cs, @@ -48,42 +100,28 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, { HPPACPU *cpu = HPPA_CPU(cs); -#ifdef CONFIG_USER_ONLY - cpu->env.iaoq_f = tb_pc(tb); - cpu->env.iaoq_b = tb->cs_base; -#else - /* Recover the IAOQ values from the GVA + PRIV. */ - uint32_t priv = (tb->flags >> TB_FLAG_PRIV_SHIFT) & 3; - target_ulong cs_base = tb->cs_base; - target_ulong iasq_f = cs_base & ~0xffffffffull; - int32_t diff = cs_base; - - cpu->env.iasq_f = iasq_f; - cpu->env.iaoq_f = (tb_pc(tb) & ~iasq_f) + priv; - if (diff) { - cpu->env.iaoq_b = cpu->env.iaoq_f + diff; - } -#endif - + /* IAQ is always up-to-date before goto_tb. */ cpu->env.psw_n = (tb->flags & PSW_N) != 0; + cpu->env.psw_xb = tb->flags & (PSW_X | PSW_B); } static void hppa_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = cpu_env(cs); - cpu->env.iaoq_f = data[0]; - if (data[1] != (target_ureg)-1) { - cpu->env.iaoq_b = data[1]; + env->iaoq_f = (env->iaoq_f & TARGET_PAGE_MASK) | data[0]; + if (data[1] != INT32_MIN) { + env->iaoq_b = env->iaoq_f + data[1]; } + 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; + env->psw_n = 0; } static bool hppa_cpu_has_work(CPUState *cs) @@ -91,6 +129,17 @@ 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; @@ -107,13 +156,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 */ @@ -135,10 +181,15 @@ 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 + + /* Use pc-relative instructions always to simplify the translator. */ + tcg_cflags_set(cs, CF_PCREL); } static void hppa_cpu_initfn(Object *obj) @@ -147,7 +198,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); @@ -155,7 +205,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 @@ -168,16 +220,18 @@ 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, + .tlb_fill_align = hppa_cpu_tlb_fill_align, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, + .cpu_exec_halt = hppa_cpu_has_work, .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 */ }; @@ -192,6 +246,7 @@ 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; @@ -206,19 +261,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..e45ba50a59 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -23,19 +23,43 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +#include "qemu/interval-tree.h" +#include "hw/registerfields.h" -/* PA-RISC 1.x processors have a strong memory model. */ -/* ??? While we do not yet implement PA-RISC 2.0, those processors have - a weak memory model, but with TLB bits that force ordering on a per-page - basis. It's probably easier to fall back to a strong memory model. */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL +#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 -#define MMU_KERNEL_IDX 0 -#define MMU_USER_IDX 3 -#define MMU_PHYS_IDX 4 -#define TARGET_INSN_START_EXTRA_WORDS 1 +#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) -/* Hardware exceptions, interupts, faults, and traps. */ +#define PRIV_KERNEL 0 +#define PRIV_USER 3 + +#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 +120,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 +133,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 +156,62 @@ #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 +FIELD(FPSR, ENA_I, 0, 1) +FIELD(FPSR, ENA_U, 1, 1) +FIELD(FPSR, ENA_O, 2, 1) +FIELD(FPSR, ENA_Z, 3, 1) +FIELD(FPSR, ENA_V, 4, 1) +FIELD(FPSR, ENABLES, 0, 5) +FIELD(FPSR, D, 5, 1) +FIELD(FPSR, T, 6, 1) +FIELD(FPSR, RM, 9, 2) +FIELD(FPSR, CQ, 11, 11) +FIELD(FPSR, CQ0_6, 15, 7) +FIELD(FPSR, CQ0_4, 17, 5) +FIELD(FPSR, CQ0_2, 19, 3) +FIELD(FPSR, CQ0, 21, 1) +FIELD(FPSR, CA, 15, 7) +FIELD(FPSR, CA0, 21, 1) +FIELD(FPSR, C, 26, 1) +FIELD(FPSR, FLG_I, 27, 1) +FIELD(FPSR, FLG_U, 28, 1) +FIELD(FPSR, FLG_O, 29, 1) +FIELD(FPSR, FLG_Z, 30, 1) +FIELD(FPSR, FLG_V, 31, 1) +FIELD(FPSR, FLAGS, 27, 5) + +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 */ + uint32_t psw; /* All psw bits except the following: */ + uint32_t psw_xb; /* X and B, in their normal positions */ + target_ulong psw_n; /* boolean */ + target_long psw_v; /* in bit 31 */ /* 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 +220,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 +272,66 @@ 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. + * + * An HPPA CPU model. + */ +struct HPPACPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; +}; + #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; + return off & gva_offset_mask(psw); #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 @@ -267,53 +340,14 @@ static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc, #define TB_FLAG_SR_SAME PSW_I #define TB_FLAG_PRIV_SHIFT 8 #define TB_FLAG_UNALIGN 0x400 +#define CS_BASE_DIFFPAGE (1 << 12) +#define CS_BASE_DIFFSPACE (1 << 13) -static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc, - target_ulong *cs_base, - uint32_t *pflags) -{ - uint32_t flags = env->psw_n * PSW_N; +void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags); - /* 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. */ -#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); - 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); - *cs_base = env->iasq_f; - - /* Insert a difference between IAOQ_B and IAOQ_F within the otherwise zero - low 32-bits of CS_BASE. This will succeed for all direct branches, - 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) { - *cs_base |= (uint32_t)diff; - } - } - if ((env->sr[4] == env->sr[5]) - & (env->sr[4] == env->sr[6]) - & (env->sr[4] == env->sr[7])) { - flags |= TB_FLAG_SR_SAME; - } -#endif - - *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,23 +356,31 @@ 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 -bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); +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_align(CPUState *cs, CPUTLBEntryFull *out, vaddr addr, + MMUAccessType access_type, int mmu_idx, + MemOp memop, int size, bool probe, uintptr_t ra); 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, MemOp mop, hwaddr *pphys, int *pprot); +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 *); -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..0e44074ba8 --- /dev/null +++ b/target/hppa/fpu_helper.c @@ -0,0 +1,456 @@ +/* + * 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 (FIELD_EX32(shadow, FPSR, RM)) { + 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 = FIELD_EX32(shadow, FPSR, D); + set_flush_to_zero(d, &env->fp_status); + set_flush_inputs_to_zero(d, &env->fp_status); + + /* + * TODO: we only need to do this at CPU reset, but currently + * HPPA does note implement a CPU reset method at all... + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); +} + +void cpu_hppa_loaded_fr0(CPUHPPAState *env) +{ + helper_loaded_fr0(env); +} + +#define CONVERT_BIT(X, SRC, DST) \ + ((unsigned)(SRC) > (unsigned)(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, R_FPSR_ENA_I_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK); + shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); + 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, R_FPSR_CA0_SHIFT - (y - 1), 1, c); + } else { + /* queued comparison */ + /* shift cq right by one place */ + shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK); + /* move fpsr[c] to fpsr[cq[0]] */ + shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C)); + /* set fpsr[c] to current compare */ + shadow = FIELD_DP32(shadow, FPSR, C, 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..0daa52f7af 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,15 +160,21 @@ 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: +#ifdef CONFIG_USER_ONLY + val |= PRIV_USER; +#endif env->iaoq_f = val; break; case 34: env->iasq_f = (uint64_t)val << 32; break; case 35: +#ifdef CONFIG_USER_ONLY + val |= PRIV_USER; +#endif env->iaoq_b = val; break; case 36: @@ -278,5 +278,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 74b8747083..d4b1a3cd5a 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -25,40 +25,71 @@ #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; - psw |= env->psw; + psw |= ((env->psw_v >> 31) & 1) * PSW_V; + psw |= env->psw | env->psw_xb; 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; - env->psw = psw & ~(PSW_N | PSW_V | PSW_CB); + /* 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_B | PSW_N | PSW_V | PSW_X | PSW_CB); + env->psw_xb = psw & (PSW_X | PSW_B); 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,29 +98,44 @@ 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; - char psw_c[20]; - int i; +#ifndef CONFIG_USER_ONLY + static const char cr_name[32][5] = { + "RC", "CR1", "CR2", "CR3", + "CR4", "CR5", "CR6", "CR7", + "PID1", "PID2", "CCR", "SAR", + "PID3", "PID4", "IVA", "EIEM", + "ITMR", "ISQF", "IOQF", "IIR", + "ISR", "IOR", "IPSW", "EIRR", + "TR0", "TR1", "TR2", "TR3", + "TR4", "TR5", "TR6", "TR7", + }; +#endif - qemu_fprintf(f, "IA_F " TARGET_FMT_lx " IA_B " TARGET_FMT_lx - " IIR " TREG_FMT_lx "\n", + CPUHPPAState *env = cpu_env(cs); + target_ulong psw = cpu_hppa_get_psw(env); + target_ulong psw_cb; + char psw_c[20]; + int i, w; + uint64_t m; + + if (hppa_is_pa20(env)) { + w = 16; + m = UINT64_MAX; + } else { + w = 8; + m = UINT32_MAX; + } + + qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n" + "IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n", + env->iasq_f >> 32, w, m & env->iaoq_f, hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f), - hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b), - env->cr[CR_IIR]); + env->iasq_b >> 32, w, m & env->iaoq_b, + hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b)); psw_c[0] = (psw & PSW_W ? 'W' : '-'); psw_c[1] = (psw & PSW_E ? 'E' : '-'); @@ -110,22 +156,58 @@ 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 + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "%-4s %0*" PRIx64 "%c", + cr_name[i], w, m & env->cr[i], + (i & 3) == 3 ? '\n' : ' '); + } + qemu_fprintf(f, "ISQB %0*" PRIx64 " IOQB %0*" PRIx64 "\n", + w, m & env->cr_back[0], w, m & env->cr_back[1]); for (i = 0; i < 8; i++) { qemu_fprintf(f, "SR%02d %08x%c", i, (uint32_t)(env->sr[i] >> 32), (i & 3) == 3 ? '\n' : ' '); } #endif - qemu_fprintf(f, "\n"); - /* ??? FR */ + if (flags & CPU_DUMP_FPU) { + static const char rm[4][4] = { "RN", "RZ", "R+", "R-" }; + char flg[6], ena[6]; + uint32_t fpsr = env->fr0_shadow; + + flg[0] = (fpsr & R_FPSR_FLG_V_MASK ? 'V' : '-'); + flg[1] = (fpsr & R_FPSR_FLG_Z_MASK ? 'Z' : '-'); + flg[2] = (fpsr & R_FPSR_FLG_O_MASK ? 'O' : '-'); + flg[3] = (fpsr & R_FPSR_FLG_U_MASK ? 'U' : '-'); + flg[4] = (fpsr & R_FPSR_FLG_I_MASK ? 'I' : '-'); + flg[5] = '\0'; + + ena[0] = (fpsr & R_FPSR_ENA_V_MASK ? 'V' : '-'); + ena[1] = (fpsr & R_FPSR_ENA_Z_MASK ? 'Z' : '-'); + ena[2] = (fpsr & R_FPSR_ENA_O_MASK ? 'O' : '-'); + ena[3] = (fpsr & R_FPSR_ENA_U_MASK ? 'U' : '-'); + ena[4] = (fpsr & R_FPSR_ENA_I_MASK ? 'I' : '-'); + ena[5] = '\0'; + + qemu_fprintf(f, "FPSR %08x flag %s enable %s %s\n", + fpsr, flg, ena, rm[FIELD_EX32(fpsr, FPSR, RM)]); + + for (i = 0; i < 32; i++) { + qemu_fprintf(f, "FR%02d %016" PRIx64 "%c", + i, env->fr[i], (i & 3) == 3 ? '\n' : ' '); + } + } + + qemu_fprintf(f, "\n"); } diff --git a/target/hppa/helper.h b/target/hppa/helper.h index c7e35ce8c7..de411923d9 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,24 +1,26 @@ -#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_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 +79,26 @@ 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(b_gate_priv, TCG_CALL_NO_WG, i64, env, i64) +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..58695def82 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 */ @@ -121,13 +134,13 @@ void hppa_cpu_do_interrupt(CPUState *cs) switch (i) { case EXCP_ILL: case EXCP_BREAK: + case EXCP_OVERFLOW: + case EXCP_COND: case EXCP_PRIV_REG: case EXCP_PRIV_OPR: /* IIR set via translate.c. */ break; - case EXCP_OVERFLOW: - case EXCP_COND: case EXCP_ASSIST: case EXCP_DTLB_MISS: case EXCP_NA_ITLB_MISS: @@ -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, 0, &paddr, &prot); 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; @@ -229,25 +241,22 @@ void hppa_cpu_do_interrupt(CPUState *cs) [EXCP_SYSCALL_LWS] = "syscall-lws", [EXCP_TOC] = "TOC (transfer of control)", }; - static int count; - const char *name = NULL; - char unknown[16]; - if (i >= 0 && i < ARRAY_SIZE(names)) { - name = names[i]; + FILE *logfile = qemu_log_trylock(); + if (logfile) { + const char *name = NULL; + + if (i >= 0 && i < ARRAY_SIZE(names)) { + name = names[i]; + } + if (name) { + fprintf(logfile, "INT: cpu %d %s\n", cs->cpu_index, name); + } else { + fprintf(logfile, "INT: cpu %d unknown %d\n", cs->cpu_index, i); + } + hppa_cpu_dump_state(cs, logfile, 0); + qemu_log_unlock(logfile); } - if (!name) { - 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])); } cs->exception_index = -1; } @@ -266,12 +275,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..b8c3e55170 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -21,73 +21,212 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/helper-proto.h" #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, MemOp mop, hwaddr *pphys, int *pprot) { 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; + /* 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; + goto egress_align; } /* 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; @@ -95,12 +234,13 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, } /* 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,57 +259,73 @@ 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; } 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; } - /* In reverse priority order, check for conditions which raise faults. - As we go, remove PROT bits that cover the condition we want to check. - In this way, the resulting PROT will force a re-check of the - architectural TLB entry for the next access. */ - if (unlikely(!ent->d)) { - if (type & PAGE_WRITE) { - /* The D bit is not set -- TLB Dirty Bit Fault. */ - ret = EXCP_TLB_DIRTY; + /* 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; } - prot &= PAGE_READ | PAGE_EXEC; - } - if (unlikely(ent->b)) { - if (type & PAGE_WRITE) { - /* The B bit is set -- Data Memory Break Fault. */ - ret = EXCP_DMB; - } - prot &= PAGE_READ | PAGE_EXEC; + /* Otherwise exclude permissions not allowed (i.e WD). */ + prot &= access_prot; } + + /* + * In reverse priority order, check for conditions which raise faults. + * Remove PROT bits that cover the condition we want to check, + * so that the resulting PROT will force a re-check of the + * architectural TLB entry for the next access. + */ if (unlikely(ent->t)) { + prot &= PAGE_EXEC; if (!(type & PAGE_EXEC)) { /* The T bit is set -- Page Reference Fault. */ ret = EXCP_PAGE_REF; } - prot &= PAGE_EXEC; + } + if (unlikely(!ent->d)) { + prot &= PAGE_READ | PAGE_EXEC; + if (type & PAGE_WRITE) { + /* The D bit is not set -- TLB Dirty Bit Fault. */ + ret = EXCP_TLB_DIRTY; + } + } + if (unlikely(ent->b)) { + prot &= PAGE_READ | PAGE_EXEC; + if (type & PAGE_WRITE) { + /* + * The B bit is set -- Data Memory Break Fault. + * Except when PSW_X is set, allow this single access to succeed. + * The write bit will be invalidated for subsequent accesses. + */ + if (env->psw_xb & PSW_X) { + prot |= PAGE_WRITE_INV; + } else { + ret = EXCP_DMB; + } + } + } + + egress_align: + if (addr & ((1u << memop_alignment_bits(mop)) - 1)) { + ret = EXCP_UNALIGN; } egress: @@ -183,16 +339,15 @@ 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, + excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0, 0, &phys, &prot); /* Since we're translating for debugging, the only error that is a @@ -201,12 +356,79 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) return excp == EXCP_DTLB_MISS ? -1 : phys; } -bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, - MMUAccessType type, int mmu_idx, - bool probe, uintptr_t retaddr) +void hppa_set_ior_and_isr(CPUHPPAState *env, vaddr addr, bool mmu_disabled) { - HPPACPU *cpu = HPPA_CPU(cs); - CPUHPPAState *env = &cpu->env; + 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_align(CPUState *cs, CPUTLBEntryFull *out, vaddr addr, + MMUAccessType type, int mmu_idx, + MemOp memop, int size, bool probe, uintptr_t ra) +{ + CPUHPPAState *env = cpu_env(cs); int prot, excp, a_prot; hwaddr phys; @@ -222,72 +444,63 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, break; } - excp = hppa_get_physical_address(env, addr, mmu_idx, - a_prot, &phys, &prot); + excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, memop, + &phys, &prot); 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, ra, 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. */ - tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, - prot, mmu_idx, TARGET_PAGE_SIZE); + + /* + * 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. + */ + memset(out, 0, sizeof(*out)); + out->phys_addr = phys; + out->prot = prot; + out->attrs = MEMTXATTRS_UNSPECIFIED; + out->lg_page_size = TARGET_PAGE_BITS; + 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 +510,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 +670,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,32 +684,143 @@ 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, + excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0, 0, &phys, &prot); 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; } -/* Return the ar_type of the TLB at VADDR, or -1. */ -int hppa_artype_for_page(CPUHPPAState *env, target_ulong vaddr) +/* + * 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) { - hppa_tlb_entry *ent = hppa_find_tlb(env, vaddr); - return ent ? ent->ar_type : -1; + 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; + } +} + +uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f) +{ + uint64_t gva = hppa_form_gva(env, env->iasq_f, iaoq_f); + HPPATLBEntry *ent = hppa_find_tlb(env, gva); + + if (ent == NULL) { + raise_exception_with_ior(env, EXCP_ITLB_MISS, GETPC(), gva, false); + } + + /* + * There should be no need to check page permissions, as that will + * already have been done by tb_lookup via get_page_addr_code. + * All we need at this point is to check the ar_type. + * + * No change for non-gateway pages or for priv decrease. + */ + if (ent->ar_type & 4) { + int old_priv = iaoq_f & 3; + int new_priv = ent->ar_type & 3; + + if (new_priv < old_priv) { + iaoq_f = (iaoq_f & -4) | new_priv; + } + } + return iaoq_f; } 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 fbd80e4248..744325969f 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,11 @@ 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) +static void atomic_store_mask32(CPUHPPAState *env, target_ulong addr, + uint32_t val, uint32_t mask, uintptr_t ra) { - if (unlikely((target_sreg)cond < 0)) { - hppa_dynamic_excp(env, EXCP_OVERFLOW, GETPC()); - } -} - -void HELPER(tcond)(CPUHPPAState *env, target_ureg 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) -{ - 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 +67,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 +109,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 +121,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 +221,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 +318,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) == 0) ? 1 : 0; + 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 +333,21 @@ 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, 0, &phys, &prot); 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 +361,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..9b43b556fd --- /dev/null +++ b/target/hppa/sys_helper.c @@ -0,0 +1,159 @@ +/* + * 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 "qemu/log.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); + + if (qemu_loglevel_mask(CPU_LOG_INT)) { + FILE *logfile = qemu_log_trylock(); + if (logfile) { + CPUState *cs = env_cpu(env); + + fprintf(logfile, "RFI: cpu %d\n", cs->cpu_index); + hppa_cpu_dump_state(cs, logfile, 0); + qemu_log_unlock(logfile); + } + } +} + +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 1af77473da..51c1762435 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -19,261 +19,75 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" +#include "exec/page-protection.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 DisasIAQE { + /* IASQ; may be null for no change from TB. */ + TCGv_i64 space; + /* IAOQ base; may be null for relative address. */ + TCGv_i64 base; + /* IAOQ addend; if base is null, relative to cpu_iaoq_f. */ + int64_t disp; +} DisasIAQE; + +typedef struct DisasDelayException { + struct DisasDelayException *next; + TCGLabel *lab; + uint32_t insn; + bool set_iir; + int8_t set_n; + uint8_t excp; + /* Saved state at parent insn. */ + DisasIAQE iaq_f, iaq_b; +} DisasDelayException; + typedef struct DisasContext { DisasContextBase base; CPUState *cs; - target_ureg iaoq_f; - target_ureg iaoq_b; - target_ureg iaoq_n; - TCGv_reg iaoq_n_var; + /* IAQ_Front, IAQ_Back. */ + DisasIAQE iaq_f, iaq_b; + /* IAQ_Next, for jumps, otherwise null for simple advance. */ + DisasIAQE iaq_j, *iaq_n; - int ntempr, ntempl; - TCGv_reg tempr[8]; - TCGv_tl templ[4]; + /* IAOQ_Front at entry to TB. */ + uint64_t iaoq_first; DisasCond null_cond; TCGLabel *null_lab; + DisasDelayException *delay_excp_list; + TCGv_i64 zero; + uint32_t insn; uint32_t tb_flags; int mmu_idx; int privilege; + uint32_t psw_xb; bool psw_n_nonzero; + bool psw_b_next; + bool is_pa20; + bool insn_start_updated; #ifdef CONFIG_USER_ONLY MemOp unalign; @@ -281,19 +95,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 +147,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 +252,25 @@ 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; +static TCGv_i32 cpu_psw_xb; 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 +299,42 @@ 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_psw_xb = tcg_global_mem_new_i32(tcg_env, + offsetof(CPUHPPAState, psw_xb), + "psw_xb"); + 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,111 +358,67 @@ 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_tt(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_ti(TCGCond c, TCGv_i64 a0, uint64_t imm) { - TCGv_reg tmp = tcg_temp_new(); - tcg_gen_mov_reg(tmp, a0); - return cond_make_0_tmp(c, tmp); + return cond_make_tt(c, a0, tcg_constant_i64(imm)); } -static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) +static DisasCond cond_make_vi(TCGCond c, TCGv_i64 a0, uint64_t imm) { - DisasCond r = { .c = c }; - - 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; + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_mov_i64(tmp, a0); + return cond_make_ti(c, tmp, imm); } -static void cond_free(DisasCond *cond) +static DisasCond cond_make_vv(TCGCond c, TCGv_i64 a0, TCGv_i64 a1) { - 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 */ - case TCG_COND_ALWAYS: - cond->c = TCG_COND_NEVER; - break; - case TCG_COND_NEVER: - break; - } + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mov_i64(t0, a0); + tcg_gen_mov_i64(t1, a1); + return cond_make_tt(c, t0, t1); } -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 +436,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 +445,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 +455,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 +479,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 +496,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,11 +509,30 @@ 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 } +/* + * Write a value to psw_xb, bearing in mind the known value. + * To be used just before exiting the TB, so do not update the known value. + */ +static void store_psw_xb(DisasContext *ctx, uint32_t xb) +{ + tcg_debug_assert(xb == 0 || xb == PSW_B); + if (ctx->psw_xb != xb) { + tcg_gen_movi_i32(cpu_psw_xb, xb); + } +} + +/* Write a value to psw_xb, and update the known value. */ +static void set_psw_xb(DisasContext *ctx, uint32_t xb) +{ + store_psw_xb(ctx, xb); + ctx->psw_xb = xb; +} + /* Skip over the implementation of an insn that has been nullified. Use this when the insn is too complex for a conditional move. */ static void nullify_over(DisasContext *ctx) @@ -663,20 +545,20 @@ 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); + ctx->null_cond = cond_make_f(); } } @@ -685,16 +567,16 @@ 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; } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); } /* Set a PSW[N] to X. The intention is that this is used immediately @@ -703,7 +585,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); } } @@ -718,6 +600,8 @@ static bool nullify_end(DisasContext *ctx) /* For NEXT, NORETURN, STALE, we can easily continue (or exit). For UPDATED, we cannot update on the nullified path. */ assert(status != DISAS_IAQ_N_UPDATED); + /* Taken branches are handled manually. */ + assert(!ctx->psw_b_next); if (likely(null_lab == NULL)) { /* The current insn wasn't conditional or handled the condition @@ -746,41 +630,141 @@ static bool nullify_end(DisasContext *ctx) return true; } -static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval) +static bool iaqe_variable(const DisasIAQE *e) { - if (unlikely(ival == -1)) { - tcg_gen_mov_reg(dest, vval); + return e->base || e->space; +} + +static DisasIAQE iaqe_incr(const DisasIAQE *e, int64_t disp) +{ + return (DisasIAQE){ + .space = e->space, + .base = e->base, + .disp = e->disp + disp, + }; +} + +static DisasIAQE iaqe_branchi(DisasContext *ctx, int64_t disp) +{ + return (DisasIAQE){ + .space = ctx->iaq_b.space, + .disp = ctx->iaq_f.disp + 8 + disp, + }; +} + +static DisasIAQE iaqe_next_absv(DisasContext *ctx, TCGv_i64 var) +{ + return (DisasIAQE){ + .space = ctx->iaq_b.space, + .base = var, + }; +} + +static void copy_iaoq_entry(DisasContext *ctx, TCGv_i64 dest, + const DisasIAQE *src) +{ + tcg_gen_addi_i64(dest, src->base ? : cpu_iaoq_f, src->disp); +} + +static void install_iaq_entries(DisasContext *ctx, const DisasIAQE *f, + const DisasIAQE *b) +{ + DisasIAQE b_next; + + if (b == NULL) { + b_next = iaqe_incr(f, 4); + b = &b_next; + } + + /* + * There is an edge case + * bv r0(rN) + * b,l disp,r0 + * for which F will use cpu_iaoq_b (from the indirect branch), + * and B will use cpu_iaoq_f (from the direct branch). + * In this case we need an extra temporary. + */ + if (f->base != cpu_iaoq_b) { + copy_iaoq_entry(ctx, cpu_iaoq_b, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + } else if (f->base == b->base) { + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + tcg_gen_addi_i64(cpu_iaoq_b, cpu_iaoq_f, b->disp - f->disp); } else { - tcg_gen_movi_reg(dest, ival); + TCGv_i64 tmp = tcg_temp_new_i64(); + copy_iaoq_entry(ctx, tmp, b); + copy_iaoq_entry(ctx, cpu_iaoq_f, f); + tcg_gen_mov_i64(cpu_iaoq_b, tmp); + } + + if (f->space) { + tcg_gen_mov_i64(cpu_iasq_f, f->space); + } + if (b->space || f->space) { + tcg_gen_mov_i64(cpu_iasq_b, b->space ? : f->space); } } -static inline target_ureg iaoq_dest(DisasContext *ctx, target_sreg disp) +static void install_link(DisasContext *ctx, unsigned link, bool with_sr0) { - return ctx->iaoq_f + disp + 8; + tcg_debug_assert(ctx->null_cond.c == TCG_COND_NEVER); + if (!link) { + return; + } + DisasIAQE next = iaqe_incr(&ctx->iaq_b, 4); + copy_iaoq_entry(ctx, cpu_gr[link], &next); +#ifndef CONFIG_USER_ONLY + if (with_sr0) { + tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_b); + } +#endif } 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); + install_iaq_entries(ctx, &ctx->iaq_f, &ctx->iaq_b); nullify_save(ctx); gen_excp_1(exception); ctx->base.is_jmp = DISAS_NORETURN; } +static DisasDelayException *delay_excp(DisasContext *ctx, uint8_t excp) +{ + DisasDelayException *e = tcg_malloc(sizeof(DisasDelayException)); + + memset(e, 0, sizeof(*e)); + e->next = ctx->delay_excp_list; + ctx->delay_excp_list = e; + + e->lab = gen_new_label(); + e->insn = ctx->insn; + e->set_iir = true; + e->set_n = ctx->psw_n_nonzero ? 0 : -1; + e->excp = excp; + e->iaq_f = ctx->iaq_f; + e->iaq_b = ctx->iaq_b; + + return e; +} + 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])); - gen_excp(ctx, exc); - return nullify_end(ctx); + if (ctx->null_cond.c == TCG_COND_NEVER) { + tcg_gen_st_i64(tcg_constant_i64(ctx->insn), + tcg_env, offsetof(CPUHPPAState, cr[CR_IIR])); + gen_excp(ctx, exc); + } else { + DisasDelayException *e = delay_excp(ctx, exc); + tcg_gen_brcond_i64(tcg_invert_cond(ctx->null_cond.c), + ctx->null_cond.a0, ctx->null_cond.a1, e->lab); + ctx->null_cond = cond_make_f(); + } + return true; } static bool gen_illegal(DisasContext *ctx) @@ -800,9 +784,12 @@ 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, const DisasIAQE *f, + const DisasIAQE *b) { - return translator_use_goto_tb(&ctx->base, dest); + return (!iaqe_variable(f) && + (b == NULL || !iaqe_variable(b)) && + translator_use_goto_tb(&ctx->base, ctx->iaoq_first + f->disp)); } /* If the next insn is to be nullified, and it's on the same page, @@ -811,21 +798,20 @@ static bool use_goto_tb(DisasContext *ctx, target_ureg dest) executing a TB that merely branches to the next TB. */ static bool use_nullify_skip(DisasContext *ctx) { - return (((ctx->iaoq_b ^ ctx->iaoq_f) & TARGET_PAGE_MASK) == 0 - && !cpu_breakpoint_test(ctx->cs, ctx->iaoq_b, BP_ANY)); + return (!(tb_cflags(ctx->base.tb) & CF_BP_PAGE) + && !iaqe_variable(&ctx->iaq_b) + && (((ctx->iaoq_first + ctx->iaq_b.disp) ^ ctx->iaoq_first) + & TARGET_PAGE_MASK) == 0); } static void gen_goto_tb(DisasContext *ctx, int which, - target_ureg f, target_ureg b) + const DisasIAQE *f, const DisasIAQE *b) { - if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { + install_iaq_entries(ctx, f, b); + if (use_goto_tb(ctx, f, b)) { tcg_gen_goto_tb(which); - tcg_gen_movi_reg(cpu_iaoq_f, f); - tcg_gen_movi_reg(cpu_iaoq_b, b); 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); tcg_gen_lookup_and_goto_ptr(); } } @@ -845,23 +831,39 @@ 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) { + TCGCond sign_cond, zero_cond; + uint64_t sign_imm, zero_imm; DisasCond cond; - TCGv_reg tmp; + TCGv_i64 tmp; + + if (d) { + /* 64-bit condition. */ + sign_imm = 0; + sign_cond = TCG_COND_LT; + zero_imm = 0; + zero_cond = TCG_COND_EQ; + } else { + /* 32-bit condition. */ + sign_imm = 1ull << 31; + sign_cond = TCG_COND_TSTNE; + zero_imm = UINT32_MAX; + zero_cond = TCG_COND_TSTEQ; + } switch (cf >> 1) { case 0: /* Never / TR (0 / 1) */ cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ - cond = cond_make_0(TCG_COND_EQ, res); + cond = cond_make_vi(zero_cond, res, zero_imm); break; case 2: /* < / >= (N ^ V / !(N ^ V) */ - tmp = tcg_temp_new(); - tcg_gen_xor_reg(tmp, res, sv); - cond = cond_make_0_tmp(TCG_COND_LT, tmp); + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, res, sv); + cond = cond_make_ti(sign_cond, tmp, sign_imm); break; case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ /* @@ -869,32 +871,29 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, * (N ^ V) | Z * ((res < 0) ^ (sv < 0)) | !res * ((res ^ sv) < 0) | !res - * (~(res ^ sv) >= 0) | !res - * !(~(res ^ sv) >> 31) | !res - * !(~(res ^ sv) >> 31 & res) + * ((res ^ sv) < 0 ? 1 : !res) + * !((res ^ sv) < 0 ? 0 : 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); - cond = cond_make_0_tmp(TCG_COND_EQ, tmp); + tmp = tcg_temp_new_i64(); + tcg_gen_xor_i64(tmp, res, sv); + tcg_gen_movcond_i64(sign_cond, tmp, + tmp, tcg_constant_i64(sign_imm), + ctx->zero, res); + cond = cond_make_ti(zero_cond, tmp, zero_imm); 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_vi(TCG_COND_EQ, uv, 0); 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); - cond = cond_make_0_tmp(TCG_COND_EQ, tmp); + 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); + cond = cond_make_ti(zero_cond, tmp, zero_imm); break; case 6: /* SV / NSV (V / !V) */ - cond = cond_make_0(TCG_COND_LT, sv); + cond = cond_make_vi(sign_cond, sv, sign_imm); break; case 7: /* OD / EV */ - tmp = tcg_temp_new(); - tcg_gen_andi_reg(tmp, res, 1); - cond = cond_make_0_tmp(TCG_COND_NE, tmp); + cond = cond_make_vi(TCG_COND_TSTNE, res, 1); break; default: g_assert_not_reached(); @@ -910,35 +909,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_tt(tc, t1, t2); + } + return cond_make_vv(tc, in1, in2); } /* @@ -950,46 +969,51 @@ 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) { - switch (cf) { - case 0: /* never */ - case 9: /* undef, C */ - case 11: /* undef, C & !Z */ - case 12: /* undef, V */ - return cond_make_f(); - - case 1: /* true */ - case 8: /* undef, !C */ - case 10: /* undef, !C | Z */ - case 13: /* undef, !V */ - return cond_make_t(); - - case 2: /* == */ - return cond_make_0(TCG_COND_EQ, res); - case 3: /* <> */ - return cond_make_0(TCG_COND_NE, res); - case 4: /* < */ - return cond_make_0(TCG_COND_LT, res); - case 5: /* >= */ - return cond_make_0(TCG_COND_GE, res); - case 6: /* <= */ - return cond_make_0(TCG_COND_LE, res); - case 7: /* > */ - return cond_make_0(TCG_COND_GT, res); - - case 14: /* OD */ - case 15: /* EV */ - return do_cond(cf, res, NULL, NULL); + TCGCond tc; + uint64_t imm; + switch (cf >> 1) { + case 0: /* never / always */ + case 4: /* undef, C */ + case 5: /* undef, C & !Z */ + case 6: /* undef, V */ + return cf & 1 ? cond_make_t() : cond_make_f(); + case 1: /* == / <> */ + tc = d ? TCG_COND_EQ : TCG_COND_TSTEQ; + imm = d ? 0 : UINT32_MAX; + break; + case 2: /* < / >= */ + tc = d ? TCG_COND_LT : TCG_COND_TSTNE; + imm = d ? 0 : 1ull << 31; + break; + case 3: /* <= / > */ + tc = cf & 1 ? TCG_COND_GT : TCG_COND_LE; + if (!d) { + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_ext32s_i64(tmp, res); + return cond_make_ti(tc, tmp, 0); + } + return cond_make_vi(tc, res, 0); + case 7: /* OD / EV */ + tc = TCG_COND_TSTNE; + imm = 1; + break; default: g_assert_not_reached(); } + if (cf & 1) { + tc = tcg_invert_cond(tc); + } + return cond_make_vi(tc, res, imm); } /* 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 +1026,219 @@ 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); + + return cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, tmp, sgns); +} + +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 gen_tc(DisasContext *ctx, DisasCond *cond) { - TCGv_reg dest, cb, cb_msb, sv, tmp; + DisasDelayException *e; + + switch (cond->c) { + case TCG_COND_NEVER: + break; + case TCG_COND_ALWAYS: + gen_excp_iir(ctx, EXCP_COND); + break; + default: + e = delay_excp(ctx, EXCP_COND); + tcg_gen_brcond_i64(cond->c, cond->a0, cond->a1, e->lab); + /* In the non-trap path, the condition is known false. */ + *cond = cond_make_f(); + break; + } +} + +static void gen_tsv(DisasContext *ctx, TCGv_i64 *sv, bool d) +{ + DisasCond cond = do_cond(ctx, /* SV */ 12, d, NULL, NULL, *sv); + DisasDelayException *e = delay_excp(ctx, EXCP_OVERFLOW); + + tcg_gen_brcond_i64(cond.c, cond.a0, cond.a1, e->lab); + + /* In the non-trap path, V is known zero. */ + *sv = tcg_constant_i64(0); +} + +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_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); + gen_tsv(ctx, &sv, d); } } + /* 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); + gen_tc(ctx, &cond); } /* Write back the result. */ @@ -1175,68 +1247,78 @@ 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 (unlikely(is_tc && a->cf == 1)) { + /* Unconditional trap on condition. */ + return gen_excp_iir(ctx, EXCP_COND); + } 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 (unlikely(is_tc && a->cf == 1)) { + /* Unconditional trap on condition. */ + return gen_excp_iir(ctx, EXCP_COND); + } 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; 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 +1326,67 @@ 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); + gen_tsv(ctx, &sv, d); } } /* 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); + gen_tc(ctx, &cond); } /* 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,78 +1395,121 @@ 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); save_gpr(ctx, rt, dest); /* 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); + } + + cond = cond_make_ti(cf & 1 ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + cb, test_cb); + } + + if (is_tc) { + gen_tc(ctx, &cond); + } + save_gpr(ctx, rt, dest); + + ctx->null_cond = cond; } #ifndef CONFIG_USER_ONLY @@ -1398,17 +1517,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 +1536,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 +1590,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 +1626,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 +1644,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 +1674,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 +1693,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 +1708,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 +1718,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 +1733,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 +1751,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 +1762,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 +1771,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 +1789,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 +1805,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 +1819,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 +1835,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 +1851,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,46 +1867,51 @@ 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, int64_t disp, unsigned link, bool is_n) { + ctx->iaq_j = iaqe_branchi(ctx, disp); + 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); - } - ctx->iaoq_n = dest; + install_link(ctx, link, false); if (is_n) { + if (use_nullify_skip(ctx)) { + nullify_set(ctx, 0); + store_psw_xb(ctx, 0); + gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); + ctx->base.is_jmp = DISAS_NORETURN; + return true; + } ctx->null_cond.c = TCG_COND_ALWAYS; } + ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; } else { nullify_over(ctx); - if (link != 0) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); - } - + install_link(ctx, link, false); if (is_n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 0, dest, dest + 4); + store_psw_xb(ctx, 0); + gen_goto_tb(ctx, 0, &ctx->iaq_j, NULL); } else { nullify_set(ctx, is_n); - gen_goto_tb(ctx, 0, ctx->iaoq_b, dest); + store_psw_xb(ctx, PSW_B); + gen_goto_tb(ctx, 0, &ctx->iaq_b, &ctx->iaq_j); } - nullify_end(ctx); nullify_set(ctx, 0); - gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n); + store_psw_xb(ctx, 0); + gen_goto_tb(ctx, 1, &ctx->iaq_b, NULL); ctx->base.is_jmp = DISAS_NORETURN; } return true; @@ -1818,10 +1919,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); + DisasIAQE next; TCGLabel *taken = NULL; TCGCond c = cond->c; bool n; @@ -1830,45 +1931,43 @@ static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, /* Handle TRUE and NEVER as direct branches. */ if (c == TCG_COND_ALWAYS) { - return do_dbranch(ctx, dest, 0, is_n && disp >= 0); - } - if (c == TCG_COND_NEVER) { - return do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0); + return do_dbranch(ctx, disp, 0, is_n && disp >= 0); } taken = gen_new_label(); - tcg_gen_brcond_reg(c, cond->a0, cond->a1, taken); - cond_free(cond); + tcg_gen_brcond_i64(c, cond->a0, cond->a1, taken); /* Not taken: Condition not satisfied; nullify on backward branches. */ n = is_n && disp < 0; if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 0, ctx->iaoq_n, ctx->iaoq_n + 4); + store_psw_xb(ctx, 0); + next = iaqe_incr(&ctx->iaq_b, 4); + gen_goto_tb(ctx, 0, &next, NULL); } else { if (!n && ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; } nullify_set(ctx, 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); - } - gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); + store_psw_xb(ctx, 0); + gen_goto_tb(ctx, 0, &ctx->iaq_b, NULL); } gen_set_label(taken); /* Taken: Condition satisfied; nullify on forward branches. */ n = is_n && disp >= 0; + + next = iaqe_branchi(ctx, disp); if (n && use_nullify_skip(ctx)) { nullify_set(ctx, 0); - gen_goto_tb(ctx, 1, dest, dest + 4); + store_psw_xb(ctx, 0); + gen_goto_tb(ctx, 1, &next, NULL); } else { nullify_set(ctx, n); - gen_goto_tb(ctx, 1, ctx->iaoq_b, dest); + store_psw_xb(ctx, PSW_B); + gen_goto_tb(ctx, 1, &ctx->iaq_b, &next); } /* Not taken: the branch itself was nullified. */ @@ -1882,86 +1981,45 @@ static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, return true; } -/* Emit an unconditional branch to an indirect target. This handles - nullification of the branch itself. */ -static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, - unsigned link, bool is_n) +/* + * Emit an unconditional branch to an indirect target, in ctx->iaq_j. + * This handles nullification of the branch itself. + */ +static bool do_ibranch(DisasContext *ctx, unsigned link, + bool with_sr0, bool is_n) { - TCGv_reg 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); - } - next = get_temp(ctx); - tcg_gen_mov_reg(next, dest); + if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { + install_link(ctx, link, with_sr0); 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); + install_iaq_entries(ctx, &ctx->iaq_j, NULL); nullify_set(ctx, 0); ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } - ctx->iaoq_n = -1; - ctx->iaoq_n_var = next; - } else if (is_n && use_nullify_skip(ctx)) { - /* The (conditional) branch, B, nullifies the next insn, N, - and we're allowed to skip execution N (no single-step or - tracepoint in effect). Since the goto_ptr that we must use - for the indirect branch consumes no special resources, we - can (conditionally) skip B and continue execution. */ - /* The use_nullify_skip test implies we have a known control path. */ - tcg_debug_assert(ctx->iaoq_b != -1); - tcg_debug_assert(ctx->iaoq_n != -1); - - /* 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); - - nullify_over(ctx); - if (link != 0) { - tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n); - } - tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx); - } else { - c = ctx->null_cond.c; - a0 = ctx->null_cond.a0; - a1 = ctx->null_cond.a1; - - tmp = tcg_temp_new(); - next = get_temp(ctx); - - copy_iaoq_entry(tmp, ctx->iaoq_n, ctx->iaoq_n_var); - tcg_gen_movcond_reg(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); - } - - 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); - cond_free(&ctx->null_cond); - ctx->null_cond = cond_make_n(); - ctx->psw_n_nonzero = true; - } else { - cond_free(&ctx->null_cond); - } + ctx->iaq_n = &ctx->iaq_j; + ctx->psw_b_next = true; + return true; } - return true; + + nullify_over(ctx); + + install_link(ctx, link, with_sr0); + if (is_n && use_nullify_skip(ctx)) { + install_iaq_entries(ctx, &ctx->iaq_j, NULL); + nullify_set(ctx, 0); + store_psw_xb(ctx, 0); + } else { + install_iaq_entries(ctx, &ctx->iaq_b, &ctx->iaq_j); + nullify_set(ctx, is_n); + store_psw_xb(ctx, PSW_B); + } + + tcg_gen_lookup_and_goto_ptr(); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); } /* Implement @@ -1971,23 +2029,22 @@ 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 = tcg_temp_new_i64(); switch (ctx->privilege) { case 0: /* Privilege 0 is maximum and is allowed to decrease. */ - return offset; + tcg_gen_mov_i64(dest, offset); + break; case 3: /* Privilege 3 is minimum and is never allowed to increase. */ - dest = get_temp(ctx); - tcg_gen_ori_reg(dest, offset, 3); + 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); + tcg_gen_andi_i64(dest, offset, -4); + tcg_gen_ori_i64(dest, dest, ctx->privilege); + tcg_gen_umax_i64(dest, dest, offset); break; } return dest; @@ -2003,14 +2060,16 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) aforementioned BE. */ static void do_page_zero(DisasContext *ctx) { + assert(ctx->iaq_f.disp == 0); + /* 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 @@ -2018,15 +2077,12 @@ static void do_page_zero(DisasContext *ctx) g_assert_not_reached(); } - /* Check that we didn't arrive here via some means that allowed - non-sequential instruction execution. Normally the PSW[B] bit - detects this by disallowing the B,GATE instruction to execute - under such conditions. */ - if (ctx->iaoq_b != ctx->iaoq_f + 4) { + /* If PSW[B] is set, the B,GATE insn would trap. */ + if (ctx->psw_xb & PSW_B) { goto do_sigill; } - switch (ctx->iaoq_f & -4) { + switch (ctx->base.pc_first) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); ctx->base.is_jmp = DISAS_NORETURN; @@ -2038,10 +2094,15 @@ 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); - ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + { + DisasIAQE next = { .base = tcg_temp_new_i64() }; + + tcg_gen_st_i64(cpu_gr[26], tcg_env, + offsetof(CPUHPPAState, cr[27])); + tcg_gen_ori_i64(next.base, cpu_gr[31], PRIV_USER); + install_iaq_entries(ctx, &next, NULL); + ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + } break; case 0x100: /* SYSCALL */ @@ -2060,7 +2121,7 @@ static void do_page_zero(DisasContext *ctx) static bool trans_nop(DisasContext *ctx, arg_nop *a) { - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2074,18 +2135,19 @@ static bool trans_sync(DisasContext *ctx, arg_sync *a) /* No point in nullifying the memory barrier. */ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } 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); - save_gpr(ctx, rt, tmp); + TCGv_i64 dest = dest_gpr(ctx, a->t); - cond_free(&ctx->null_cond); + copy_iaoq_entry(ctx, dest, &ctx->iaq_f); + tcg_gen_andi_i64(dest, dest, -4); + + save_gpr(ctx, a->t, dest); + ctx->null_cond = cond_make_f(); return true; } @@ -2094,17 +2156,13 @@ 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); + ctx->null_cond = cond_make_f(); return true; } @@ -2112,32 +2170,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,12 +2202,12 @@ 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: - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } @@ -2162,24 +2215,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,17 +2238,16 @@ 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); + ctx->null_cond = cond_make_f(); return true; } @@ -2206,17 +2256,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 +2283,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 +2295,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,51 +2315,51 @@ 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); + ctx->null_cond = cond_make_f(); return true; } 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); - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } 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 +2372,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 +2392,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 +2412,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); @@ -2375,8 +2438,9 @@ static bool trans_halt(DisasContext *ctx, arg_halt *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY + set_psw_xb(ctx, 0); nullify_over(ctx); - gen_helper_halt(cpu_env); + gen_helper_halt(tcg_env); ctx->base.is_jmp = DISAS_NORETURN; return nullify_end(ctx); #endif @@ -2386,43 +2450,74 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY + set_psw_xb(ctx, 0); 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); + ctx->null_cond = cond_make_f(); 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 +2525,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 +2541,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 +2567,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 +2640,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 +2654,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 +2684,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 +2742,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); + ctx->null_cond = cond_make_f(); 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; @@ -2664,18 +2821,18 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) unsigned rt = a->t; if (rt == 0) { /* NOP */ - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } 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]); } - cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_f(); return true; } #ifndef CONFIG_USER_ONLY @@ -2688,15 +2845,17 @@ static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */ /* No need to check for supervisor, as userland can only pause until the next timer interrupt. */ + + set_psw_xb(ctx, 0); + 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); + install_iaq_entries(ctx, &ctx->iaq_b, NULL); 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 +2864,177 @@ 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); + dest = dest_gpr(ctx, a->t); + + tcg_gen_xor_i64(dest, tcg_r1, tcg_r2); + save_gpr(ctx, a->t, dest); + + ctx->null_cond = do_unit_zero_cond(a->cf, a->d, dest); return nullify_end(ctx); } -static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) +static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf_d *a, bool is_tc) { - TCGv_reg tcg_r1, tcg_r2, tmp; + TCGv_i64 tcg_r1, tcg_r2, tmp; - if (a->cf) { - nullify_over(ctx); + 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); + ctx->null_cond = cond_make_f(); + return true; } + + nullify_over(ctx); 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); + 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 *a) +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 +3068,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 +3310,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 +3349,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 +3361,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 +3429,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 +3440,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,98 +3448,122 @@ 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); + ctx->null_cond = cond_make_f(); return true; } 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); + ctx->null_cond = cond_make_f(); return true; } 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); + ctx->null_cond = cond_make_f(); 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 +3576,271 @@ 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_ti(a->c ? TCG_COND_GE : TCG_COND_LT, tmp, 0); 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; DisasCond cond; + int p = a->p | (a->d ? 0 : 32); nullify_over(ctx); - - tmp = tcg_temp_new(); - tcg_r = load_gpr(ctx, a->r); - tcg_gen_shli_reg(tmp, tcg_r, a->p); - - cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); - tcg_temp_free(tmp); + cond = cond_make_vi(a->c ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + load_gpr(ctx, a->r), 1ull << (63 - p)); 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,278 +3848,231 @@ 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; - -#ifdef CONFIG_USER_ONLY - /* ??? It seems like there should be a good way of using - "be disp(sr2, r0)", the canonical gateway entry mechanism - to our advantage. But that appears to be inconvenient to - manage along side branch delay slots. Therefore we handle - entry into the gateway page via absolute address. */ - /* Since we don't implement spaces, just branch. Do notice the special - case of "be disp(*,r0)" using a direct branch to disp, so that we can - goto_tb to the TB containing the syscall. */ - if (a->b == 0) { - return do_dbranch(ctx, a->disp, a->l, a->n); - } -#else - nullify_over(ctx); +#ifndef CONFIG_USER_ONLY + ctx->iaq_j.space = tcg_temp_new_i64(); + load_spr(ctx, ctx->iaq_j.space, a->sp); #endif - tmp = get_temp(ctx); - tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp); - tmp = do_ibranch_priv(ctx, tmp); + ctx->iaq_j.base = tcg_temp_new_i64(); + ctx->iaq_j.disp = 0; -#ifdef CONFIG_USER_ONLY - return do_ibranch(ctx, tmp, a->l, a->n); -#else - TCGv_i64 new_spc = tcg_temp_new_i64(); + tcg_gen_addi_i64(ctx->iaq_j.base, load_gpr(ctx, a->b), a->disp); + ctx->iaq_j.base = do_ibranch_priv(ctx, ctx->iaq_j.base); - 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); - } - 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); - tcg_gen_mov_i64(cpu_iasq_f, new_spc); - tcg_gen_mov_i64(cpu_iasq_b, cpu_iasq_f); - } else { - copy_iaoq_entry(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); - 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); -#endif + return do_ibranch(ctx, a->l, true, a->n); } static bool trans_bl(DisasContext *ctx, arg_bl *a) { - return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n); + return do_dbranch(ctx, a->disp, a->l, a->n); } static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { - target_ureg dest = iaoq_dest(ctx, a->disp); + int64_t disp = a->disp; + bool indirect = false; - nullify_over(ctx); - - /* Make sure the caller hasn't done something weird with the queue. - * ??? This is not quite the same as the PSW[B] bit, which would be - * expensive to track. Real hardware will trap for - * b gateway - * b gateway+4 (in delay slot of first branch) - * However, checking for a non-sequential instruction queue *will* - * diagnose the security hole - * b gateway - * b evil - * in which instructions at evil would run with increased privs. - */ - if (ctx->iaoq_b == -1 || ctx->iaoq_b != ctx->iaoq_f + 4) { + /* Trap if PSW[B] is set. */ + if (ctx->psw_xb & PSW_B) { return gen_illegal(ctx); } + nullify_over(ctx); + #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); - /* 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 - we will re-translate, at which point we *will* be able to find - the TLB entry and determine if this is in fact a gateway page. */ - if (type < 0) { - gen_excp(ctx, EXCP_ITLB_MISS); - return true; - } - /* No change for non-gateway pages or for priv decrease. */ - if (type >= 4 && type - 4 < ctx->privilege) { - dest = deposit32(dest, 0, 2, type - 4); - } + if (ctx->privilege == 0) { + /* Privilege cannot decrease. */ + } else if (!(ctx->tb_flags & PSW_C)) { + /* With paging disabled, priv becomes 0. */ + disp -= ctx->privilege; } else { - dest &= -4; /* priv = 0 */ + /* Adjust the dest offset for the privilege change from the PTE. */ + TCGv_i64 off = tcg_temp_new_i64(); + + copy_iaoq_entry(ctx, off, &ctx->iaq_f); + gen_helper_b_gate_priv(off, tcg_env, off); + + ctx->iaq_j.base = off; + ctx->iaq_j.disp = disp + 8; + indirect = true; } #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); } - return do_dbranch(ctx, dest, 0, a->n); + if (indirect) { + return do_ibranch(ctx, 0, false, a->n); + } + return do_dbranch(ctx, disp, 0, a->n); } 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); + DisasIAQE next = iaqe_incr(&ctx->iaq_f, 8); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + /* The computation here never changes privilege level. */ - return do_ibranch(ctx, tmp, a->l, a->n); + copy_iaoq_entry(ctx, t0, &next); + tcg_gen_shli_i64(t1, load_gpr(ctx, a->x), 3); + tcg_gen_add_i64(t0, t0, t1); + + ctx->iaq_j = iaqe_next_absv(ctx, t0); + return do_ibranch(ctx, a->l, false, a->n); } else { /* BLR R0,RX is a good way to load PC+8 into RX. */ - return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n); + return do_dbranch(ctx, 0, a->l, a->n); } } 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); + ctx->iaq_j = iaqe_next_absv(ctx, dest); + + return do_ibranch(ctx, 0, false, a->n); } static bool trans_bve(DisasContext *ctx, arg_bve *a) { - TCGv_reg dest; + TCGv_i64 b = load_gpr(ctx, a->b); -#ifdef CONFIG_USER_ONLY - dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); - return do_ibranch(ctx, dest, a->l, a->n); -#else - 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); - if (ctx->iaoq_b == -1) { - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); - } - copy_iaoq_entry(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); - } - nullify_set(ctx, a->n); - tcg_gen_lookup_and_goto_ptr(); - ctx->base.is_jmp = DISAS_NORETURN; - return nullify_end(ctx); +#ifndef CONFIG_USER_ONLY + ctx->iaq_j.space = space_select(ctx, 0, b); #endif + ctx->iaq_j.base = do_ibranch_priv(ctx, b); + ctx->iaq_j.disp = 0; + + return do_ibranch(ctx, a->l, false, a->n); +} + +static bool trans_nopbts(DisasContext *ctx, arg_nopbts *a) +{ + /* All branch target stack instructions implement as nop. */ + return ctx->is_pa20; } /* @@ -3614,6 +4084,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 +4343,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,74 +4360,58 @@ 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; + TCGCond tc = TCG_COND_TSTNE; + uint32_t mask; + 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; - bool inv = false; - switch (a->c) { case 0: /* simple */ - tcg_gen_andi_reg(t, t, 0x4000000); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); - goto done; + mask = R_FPSR_C_MASK; + break; case 2: /* rej */ - inv = true; + tc = TCG_COND_TSTEQ; /* fallthru */ case 1: /* acc */ - mask = 0x43ff800; + mask = R_FPSR_C_MASK | R_FPSR_CQ_MASK; break; case 6: /* rej8 */ - inv = true; + tc = TCG_COND_TSTEQ; /* fallthru */ case 5: /* acc8 */ - mask = 0x43f8000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_6_MASK; break; case 9: /* acc6 */ - mask = 0x43e0000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_4_MASK; break; case 13: /* acc4 */ - mask = 0x4380000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_2_MASK; break; case 17: /* acc2 */ - mask = 0x4200000; + mask = R_FPSR_C_MASK | R_FPSR_CQ0_MASK; break; default: gen_illegal(ctx); return true; } - if (inv) { - TCGv_reg c = load_const(ctx, mask); - tcg_gen_or_reg(t, t, c); - ctx->null_cond = cond_make(TCG_COND_EQ, t, c); - } else { - tcg_gen_andi_reg(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); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); - tcg_temp_free(t); + mask = R_FPSR_CA0_MASK >> cbit; } - done: + ctx->null_cond = cond_make_ti(tc, t, mask); return nullify_end(ctx); } @@ -4003,8 +4469,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 +4537,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,62 +4556,105 @@ 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; } static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint64_t cs_base; int bound; ctx->cs = cs; ctx->tb_flags = ctx->base.tb->flags; + ctx->is_pa20 = hppa_is_pa20(cpu_env(cs)); + ctx->psw_xb = ctx->tb_flags & (PSW_X | PSW_B); #ifdef CONFIG_USER_ONLY - ctx->privilege = MMU_USER_IDX; + ctx->privilege = PRIV_USER; 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->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); - - /* Recover the IAOQ values from the GVA + PRIV. */ - uint64_t cs_base = ctx->base.tb->cs_base; - uint64_t iasq_f = cs_base & ~0xffffffffull; - int32_t diff = cs_base; - - ctx->iaoq_f = (ctx->base.pc_first & ~iasq_f) + ctx->privilege; - ctx->iaoq_b = (diff ? ctx->iaoq_f + diff : -1); + 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); #endif - ctx->iaoq_n = -1; - ctx->iaoq_n_var = NULL; + + cs_base = ctx->base.tb->cs_base; + ctx->iaoq_first = ctx->base.pc_first + ctx->privilege; + + if (unlikely(cs_base & CS_BASE_DIFFSPACE)) { + ctx->iaq_b.space = cpu_iasq_b; + ctx->iaq_b.base = cpu_iaoq_b; + } else if (unlikely(cs_base & CS_BASE_DIFFPAGE)) { + ctx->iaq_b.base = cpu_iaoq_b; + } else { + uint64_t iaoq_f_pgofs = ctx->iaoq_first & ~TARGET_PAGE_MASK; + uint64_t iaoq_b_pgofs = cs_base & ~TARGET_PAGE_MASK; + ctx->iaq_b.disp = iaoq_b_pgofs - iaoq_f_pgofs; + } + + 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) @@ -4170,16 +4674,31 @@ static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + uint64_t iaoq_f, iaoq_b; + int64_t diff; - tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b); + tcg_debug_assert(!iaqe_variable(&ctx->iaq_f)); + + iaoq_f = ctx->iaoq_first + ctx->iaq_f.disp; + if (iaqe_variable(&ctx->iaq_b)) { + diff = INT32_MIN; + } else { + iaoq_b = ctx->iaoq_first + ctx->iaq_b.disp; + diff = iaoq_b - iaoq_f; + /* Direct branches can only produce a 24-bit displacement. */ + tcg_debug_assert(diff == (int32_t)diff); + tcg_debug_assert(diff != INT32_MIN); + } + + tcg_gen_insn_start(iaoq_f & ~TARGET_PAGE_MASK, diff, 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 @@ -4194,16 +4713,13 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) the page permissions for execute. */ uint32_t insn = translator_ldl(env, &ctx->base, ctx->base.pc_next); - /* Set up the IA queue for the next insn. - 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); - } else { - ctx->iaoq_n = ctx->iaoq_b + 4; - ctx->iaoq_n_var = NULL; - } + /* + * Set up the IA queue for the next insn. + * This will be overwritten by a branch. + */ + ctx->iaq_n = NULL; + memset(&ctx->iaq_j, 0, sizeof(ctx->iaq_j)); + ctx->psw_b_next = false; if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) { ctx->null_cond.c = TCG_COND_NEVER; @@ -4216,63 +4732,47 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ret = ctx->base.is_jmp; assert(ctx->null_lab == NULL); } - } - /* 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) { - if (ctx->iaoq_b != -1 && ctx->iaoq_n != -1 - && use_goto_tb(ctx, ctx->iaoq_b) - && (ctx->null_cond.c == TCG_COND_NEVER - || ctx->null_cond.c == TCG_COND_ALWAYS)) { - nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); - gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); - ctx->base.is_jmp = ret = DISAS_NORETURN; - } else { - ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE; + if (ret != DISAS_NORETURN) { + set_psw_xb(ctx, ctx->psw_b_next ? PSW_B : 0); } } - ctx->iaoq_f = ctx->iaoq_b; - ctx->iaoq_b = ctx->iaoq_n; + + /* If the TranslationBlock must end, do so. */ ctx->base.pc_next += 4; + if (ret != DISAS_NEXT) { + return; + } + /* Note this also detects a priority change. */ + if (iaqe_variable(&ctx->iaq_b) + || ctx->iaq_b.disp != ctx->iaq_f.disp + 4) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + return; + } - switch (ret) { - case DISAS_NORETURN: - case DISAS_IAQ_N_UPDATED: - break; - - case DISAS_NEXT: - 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); -#ifndef CONFIG_USER_ONLY - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); -#endif - nullify_save(ctx); - ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT - ? DISAS_EXIT - : DISAS_IAQ_N_UPDATED); - } else if (ctx->iaoq_b == -1) { - tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var); - } - break; - - default: - g_assert_not_reached(); + /* + * Advance the insn queue. + * The only exit now is DISAS_TOO_MANY from the translator loop. + */ + ctx->iaq_f.disp = ctx->iaq_b.disp; + if (!ctx->iaq_n) { + ctx->iaq_b.disp += 4; + return; + } + /* + * If IAQ_Next is variable in any way, we need to copy into the + * IAQ_Back globals, in case the next insn raises an exception. + */ + if (ctx->iaq_n->base) { + copy_iaoq_entry(ctx, cpu_iaoq_b, ctx->iaq_n); + ctx->iaq_b.base = cpu_iaoq_b; + ctx->iaq_b.disp = 0; + } else { + ctx->iaq_b.disp = ctx->iaq_n->disp; + } + if (ctx->iaq_n->space) { + tcg_gen_mov_i64(cpu_iasq_b, ctx->iaq_n->space); + ctx->iaq_b.space = cpu_iasq_b; } } @@ -4280,56 +4780,82 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); DisasJumpType is_jmp = ctx->base.is_jmp; + /* Assume the insn queue has not been advanced. */ + DisasIAQE *f = &ctx->iaq_b; + DisasIAQE *b = ctx->iaq_n; switch (is_jmp) { case DISAS_NORETURN: break; 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); - nullify_save(ctx); + /* The insn queue has not been advanced. */ + f = &ctx->iaq_f; + b = &ctx->iaq_b; /* FALLTHRU */ - case DISAS_IAQ_N_UPDATED: - if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_lookup_and_goto_ptr(); + case DISAS_IAQ_N_STALE: + if (use_goto_tb(ctx, f, b) + && (ctx->null_cond.c == TCG_COND_NEVER + || ctx->null_cond.c == TCG_COND_ALWAYS)) { + nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); + gen_goto_tb(ctx, 0, f, b); break; } /* FALLTHRU */ + case DISAS_IAQ_N_STALE_EXIT: + install_iaq_entries(ctx, f, b); + nullify_save(ctx); + if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { + tcg_gen_exit_tb(NULL, 0); + break; + } + /* FALLTHRU */ + case DISAS_IAQ_N_UPDATED: + tcg_gen_lookup_and_goto_ptr(); + break; case DISAS_EXIT: tcg_gen_exit_tb(NULL, 0); break; default: g_assert_not_reached(); } + + for (DisasDelayException *e = ctx->delay_excp_list; e ; e = e->next) { + gen_set_label(e->lab); + if (e->set_n >= 0) { + tcg_gen_movi_i64(cpu_psw_n, e->set_n); + } + if (e->set_iir) { + tcg_gen_st_i64(tcg_constant_i64(e->insn), tcg_env, + offsetof(CPUHPPAState, cr[CR_IIR])); + } + install_iaq_entries(ctx, &e->iaq_f, &e->iaq_b); + gen_excp_1(e->excp); + } } -static void hppa_tr_disas_log(const DisasContextBase *dcbase, +#ifdef CONFIG_USER_ONLY +static bool hppa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs, FILE *logfile) { target_ulong pc = dcbase->pc_first; -#ifdef CONFIG_USER_ONLY switch (pc) { case 0x00: fprintf(logfile, "IN:\n0x00000000: (null)\n"); - return; + return true; case 0xb0: fprintf(logfile, "IN:\n0x000000b0: light-weight-syscall\n"); - return; + return true; case 0xe0: fprintf(logfile, "IN:\n0x000000e0: set-thread-pointer-syscall\n"); - return; + return true; case 0x100: fprintf(logfile, "IN:\n0x00000100: syscall\n"); - return; + return true; } -#endif - - fprintf(logfile, "IN: %s\n", lookup_symbol(pc)); - target_disas(logfile, cs, pc, dcbase->tb->size); + return false; } +#endif static const TranslatorOps hppa_tr_ops = { .init_disas_context = hppa_tr_init_disas_context, @@ -4337,12 +4863,14 @@ static const TranslatorOps hppa_tr_ops = { .insn_start = hppa_tr_insn_start, .translate_insn = hppa_tr_translate_insn, .tb_stop = hppa_tr_tb_stop, +#ifdef CONFIG_USER_ONLY .disas_log = hppa_tr_disas_log, +#endif }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { - DisasContext ctx; + DisasContext ctx = { }; translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base); } diff --git a/target/i386/Kconfig b/target/i386/Kconfig index ce6968906e..6b0feef029 100644 --- a/target/i386/Kconfig +++ b/target/i386/Kconfig @@ -1,5 +1,9 @@ config I386 bool + select APIC + # kvm_arch_fixup_msi_route() needs to access PCIDevice + select PCI if KVM config X86_64 bool + select I386 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/confidential-guest.c b/target/i386/confidential-guest.c new file mode 100644 index 0000000000..b3727845ad --- /dev/null +++ b/target/i386/confidential-guest.c @@ -0,0 +1,33 @@ +/* + * QEMU Confidential Guest support + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Authors: + * 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. + * + */ + +#include "qemu/osdep.h" + +#include "confidential-guest.h" + +OBJECT_DEFINE_ABSTRACT_TYPE(X86ConfidentialGuest, + x86_confidential_guest, + X86_CONFIDENTIAL_GUEST, + CONFIDENTIAL_GUEST_SUPPORT) + +static void x86_confidential_guest_class_init(ObjectClass *oc, void *data) +{ +} + +static void x86_confidential_guest_init(Object *obj) +{ +} + +static void x86_confidential_guest_finalize(Object *obj) +{ +} diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h new file mode 100644 index 0000000000..7342d2843a --- /dev/null +++ b/target/i386/confidential-guest.h @@ -0,0 +1,83 @@ +/* + * x86-specific confidential guest methods. + * + * Copyright (c) 2024 Red Hat Inc. + * + * Authors: + * 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. + */ +#ifndef TARGET_I386_CG_H +#define TARGET_I386_CG_H + +#include "qom/object.h" + +#include "exec/confidential-guest-support.h" + +#define TYPE_X86_CONFIDENTIAL_GUEST "x86-confidential-guest" + +OBJECT_DECLARE_TYPE(X86ConfidentialGuest, + X86ConfidentialGuestClass, + X86_CONFIDENTIAL_GUEST) + +struct X86ConfidentialGuest { + /* */ + ConfidentialGuestSupport parent_obj; +}; + +/** + * X86ConfidentialGuestClass: + * + * Class to be implemented by confidential-guest-support concrete objects + * for the x86 target. + */ +struct X86ConfidentialGuestClass { + /* */ + ConfidentialGuestSupportClass parent; + + /* */ + int (*kvm_type)(X86ConfidentialGuest *cg); + uint32_t (*mask_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, + int reg, uint32_t value); +}; + +/** + * x86_confidential_guest_kvm_type: + * + * Calls #X86ConfidentialGuestClass.unplug callback of @plug_handler. + */ +static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->kvm_type) { + return klass->kvm_type(cg); + } else { + return 0; + } +} + +/** + * x86_confidential_guest_mask_cpuid_features: + * + * Removes unsupported features from a confidential guest's CPUID values, returns + * the value with the bits removed. The bits removed should be those that KVM + * provides independent of host-supported CPUID features, but are not supported by + * the confidential computing firmware. + */ +static inline int x86_confidential_guest_mask_cpuid_features(X86ConfidentialGuest *cg, + uint32_t feature, uint32_t index, + int reg, uint32_t value) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->mask_cpuid_features) { + return klass->mask_cpuid_features(cg, feature, index, reg, value); + } else { + return value; + } +} + +#endif diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c new file mode 100644 index 0000000000..d397ec94dc --- /dev/null +++ b/target/i386/cpu-apic.c @@ -0,0 +1,112 @@ +/* + * QEMU x86 CPU <-> APIC + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qapi/qmp/qdict.h" +#include "qapi/error.h" +#include "monitor/monitor.h" +#include "monitor/hmp-target.h" +#include "sysemu/hw_accel.h" +#include "sysemu/kvm.h" +#include "sysemu/xen.h" +#include "exec/address-spaces.h" +#include "hw/qdev-properties.h" +#include "hw/i386/apic_internal.h" +#include "cpu-internal.h" + +APICCommonClass *apic_get_class(Error **errp) +{ + const char *apic_type = "apic"; + + /* TODO: in-kernel irqchip for hvf */ + 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"; + } else if (whpx_apic_in_platform()) { + apic_type = "whpx-apic"; + } + + return APIC_COMMON_CLASS(object_class_by_name(apic_type)); +} + +void x86_cpu_apic_create(X86CPU *cpu, Error **errp) +{ + APICCommonState *apic; + APICCommonClass *apic_class = apic_get_class(errp); + + 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)); + + /* 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) +{ + APICCommonState *apic; + static bool apic_mmio_map_once; + + if (cpu->apic_state == NULL) { + return; + } + qdev_realize(DEVICE(cpu->apic_state), NULL, errp); + + /* Map APIC MMIO area */ + apic = APIC_COMMON(cpu->apic_state); + if (!apic_mmio_map_once) { + memory_region_add_subregion_overlap(get_system_memory(), + apic->apicbase & + MSR_IA32_APICBASE_BASE, + &apic->io_memory, + 0x1000); + apic_mmio_map_once = true; + } +} + +void hmp_info_local_apic(Monitor *mon, const QDict *qdict) +{ + CPUState *cs; + + 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); + } + + + if (!cs) { + monitor_printf(mon, "No CPU available\n"); + return; + } + x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU); +} diff --git a/target/i386/cpu-dump.c b/target/i386/cpu-dump.c index 08ac957e99..a72ed93bd2 100644 --- a/target/i386/cpu-dump.c +++ b/target/i386/cpu-dump.c @@ -27,70 +27,70 @@ /***********************************************************/ /* x86 debug */ -static const char *cc_op_str[CC_OP_NB] = { - "DYNAMIC", - "EFLAGS", +static const char * const cc_op_str[] = { + [CC_OP_DYNAMIC] = "DYNAMIC", - "MULB", - "MULW", - "MULL", - "MULQ", + [CC_OP_EFLAGS] = "EFLAGS", + [CC_OP_ADCX] = "ADCX", + [CC_OP_ADOX] = "ADOX", + [CC_OP_ADCOX] = "ADCOX", - "ADDB", - "ADDW", - "ADDL", - "ADDQ", + [CC_OP_MULB] = "MULB", + [CC_OP_MULW] = "MULW", + [CC_OP_MULL] = "MULL", + [CC_OP_MULQ] = "MULQ", - "ADCB", - "ADCW", - "ADCL", - "ADCQ", + [CC_OP_ADDB] = "ADDB", + [CC_OP_ADDW] = "ADDW", + [CC_OP_ADDL] = "ADDL", + [CC_OP_ADDQ] = "ADDQ", - "SUBB", - "SUBW", - "SUBL", - "SUBQ", + [CC_OP_ADCB] = "ADCB", + [CC_OP_ADCW] = "ADCW", + [CC_OP_ADCL] = "ADCL", + [CC_OP_ADCQ] = "ADCQ", - "SBBB", - "SBBW", - "SBBL", - "SBBQ", + [CC_OP_SUBB] = "SUBB", + [CC_OP_SUBW] = "SUBW", + [CC_OP_SUBL] = "SUBL", + [CC_OP_SUBQ] = "SUBQ", - "LOGICB", - "LOGICW", - "LOGICL", - "LOGICQ", + [CC_OP_SBBB] = "SBBB", + [CC_OP_SBBW] = "SBBW", + [CC_OP_SBBL] = "SBBL", + [CC_OP_SBBQ] = "SBBQ", - "INCB", - "INCW", - "INCL", - "INCQ", + [CC_OP_LOGICB] = "LOGICB", + [CC_OP_LOGICW] = "LOGICW", + [CC_OP_LOGICL] = "LOGICL", + [CC_OP_LOGICQ] = "LOGICQ", - "DECB", - "DECW", - "DECL", - "DECQ", + [CC_OP_INCB] = "INCB", + [CC_OP_INCW] = "INCW", + [CC_OP_INCL] = "INCL", + [CC_OP_INCQ] = "INCQ", - "SHLB", - "SHLW", - "SHLL", - "SHLQ", + [CC_OP_DECB] = "DECB", + [CC_OP_DECW] = "DECW", + [CC_OP_DECL] = "DECL", + [CC_OP_DECQ] = "DECQ", - "SARB", - "SARW", - "SARL", - "SARQ", + [CC_OP_SHLB] = "SHLB", + [CC_OP_SHLW] = "SHLW", + [CC_OP_SHLL] = "SHLL", + [CC_OP_SHLQ] = "SHLQ", - "BMILGB", - "BMILGW", - "BMILGL", - "BMILGQ", + [CC_OP_SARB] = "SARB", + [CC_OP_SARW] = "SARW", + [CC_OP_SARL] = "SARL", + [CC_OP_SARQ] = "SARQ", - "ADCX", - "ADOX", - "ADCOX", + [CC_OP_BMILGB] = "BMILGB", + [CC_OP_BMILGW] = "BMILGW", + [CC_OP_BMILGL] = "BMILGL", + [CC_OP_BMILGQ] = "BMILGQ", - "CLR", + [CC_OP_POPCNT] = "POPCNT", }; static void @@ -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 @@ -349,7 +346,6 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; int eflags, i, nb; - char cc_op_name[32]; static const char *seg_name[6] = { "ES", "CS", "SS", "DS", "FS", "GS" }; eflags = cpu_compute_eflags(env); @@ -458,10 +454,16 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags) env->dr[6], env->dr[7]); } if (flags & CPU_DUMP_CCOP) { - if ((unsigned)env->cc_op < CC_OP_NB) - snprintf(cc_op_name, sizeof(cc_op_name), "%s", cc_op_str[env->cc_op]); - else - snprintf(cc_op_name, sizeof(cc_op_name), "[%d]", env->cc_op); + const char *cc_op_name = NULL; + char cc_op_buf[32]; + + if ((unsigned)env->cc_op < ARRAY_SIZE(cc_op_str)) { + cc_op_name = cc_op_str[env->cc_op]; + } + if (cc_op_name == NULL) { + snprintf(cc_op_buf, sizeof(cc_op_buf), "[%d]", env->cc_op); + cc_op_name = cc_op_buf; + } #ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { qemu_fprintf(f, "CCS=%016" PRIx64 " CCD=%016" PRIx64 " CCO=%s\n", diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index f579b16bd2..8c75abe141 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -2,7 +2,7 @@ * i386 cpu parameters for qemu. * * Copyright (c) 2003 Fabrice Bellard - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef I386_CPU_PARAM_H @@ -23,10 +23,8 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif #define TARGET_PAGE_BITS 12 -#define NB_MMU_MODES 5 -#ifndef CONFIG_USER_ONLY -# define TARGET_TB_PCREL 1 -#endif +/* 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) #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..227ac021f6 100644 --- a/target/i386/cpu-sysemu.c +++ b/target/i386/cpu-sysemu.c @@ -19,18 +19,12 @@ #include "qemu/osdep.h" #include "cpu.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" - -#include "exec/address-spaces.h" -#include "hw/i386/apic_internal.h" #include "cpu-internal.h" @@ -129,20 +123,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 +166,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 +197,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 +205,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 +244,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,62 +266,6 @@ void x86_cpu_machine_reset_cb(void *opaque) cpu_reset(CPU(cpu)); } -APICCommonClass *apic_get_class(void) -{ - const char *apic_type = "apic"; - - /* TODO: in-kernel irqchip for hvf */ - if (kvm_apic_in_kernel()) { - apic_type = "kvm-apic"; - } else if (xen_enabled()) { - apic_type = "xen-apic"; - } else if (whpx_apic_in_platform()) { - apic_type = "whpx-apic"; - } - - return APIC_COMMON_CLASS(object_class_by_name(apic_type)); -} - -void x86_cpu_apic_create(X86CPU *cpu, Error **errp) -{ - APICCommonState *apic; - ObjectClass *apic_class = OBJECT_CLASS(apic_get_class()); - - cpu->apic_state = DEVICE(object_new_with_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; -} - -void x86_cpu_apic_realize(X86CPU *cpu, Error **errp) -{ - APICCommonState *apic; - static bool apic_mmio_map_once; - - if (cpu->apic_state == NULL) { - return; - } - qdev_realize(DEVICE(cpu->apic_state), NULL, errp); - - /* Map APIC MMIO area */ - apic = APIC_COMMON(cpu->apic_state); - if (!apic_mmio_map_once) { - memory_region_add_subregion_overlap(get_system_memory(), - apic->apicbase & - MSR_IA32_APICBASE_BASE, - &apic->io_memory, - 0x1000); - apic_mmio_map_once = true; - } -} - GuestPanicInformation *x86_cpu_get_crash_info(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); @@ -349,4 +309,3 @@ void x86_cpu_get_crash_info_qom(Object *obj, Visitor *v, errp); qapi_free_GuestPanicInformation(panic_info); } - diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 9664e1bd96..48d9974fe2 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -24,18 +24,19 @@ #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 +45,11 @@ #include "disas/capstone.h" #include "cpu-internal.h" +static void x86_cpu_realizefn(DeviceState *dev, Error **errp); +static void x86_cpu_get_supported_cpuid(uint32_t func, uint32_t index, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx); + /* Helpers for building CPUID[2] descriptors: */ struct CPUID2CacheDescriptorInfo { @@ -233,22 +239,53 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ 0 /* Invalid value */) +static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, + enum CpuTopologyLevel share_level) +{ + uint32_t num_ids = 0; + + switch (share_level) { + case CPU_TOPOLOGY_LEVEL_CORE: + num_ids = 1 << apicid_core_offset(topo_info); + break; + case CPU_TOPOLOGY_LEVEL_DIE: + num_ids = 1 << apicid_die_offset(topo_info); + break; + case CPU_TOPOLOGY_LEVEL_SOCKET: + num_ids = 1 << apicid_pkg_offset(topo_info); + break; + default: + /* + * Currently there is no use case for THREAD and MODULE, so use + * assert directly to facilitate debugging. + */ + g_assert_not_reached(); + } + + return num_ids - 1; +} + +static uint32_t max_core_ids_in_package(X86CPUTopoInfo *topo_info) +{ + uint32_t num_cores = 1 << (apicid_pkg_offset(topo_info) - + apicid_core_offset(topo_info)); + return num_cores - 1; +} /* Encode cache info for CPUID[4] */ static void encode_cache_cpuid4(CPUCacheInfo *cache, - int num_apic_ids, int num_cores, + X86CPUTopoInfo *topo_info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); - assert(num_apic_ids > 0); *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | - ((num_cores - 1) << 26) | - ((num_apic_ids - 1) << 14); + (max_core_ids_in_package(topo_info) << 26) | + (max_thread_ids_for_cache(topo_info, cache->share_level) << 14); assert(cache->line_size > 0); assert(cache->partitions > 0); @@ -267,6 +304,125 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache, (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); } +static uint32_t num_threads_by_topo_level(X86CPUTopoInfo *topo_info, + enum CpuTopologyLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPOLOGY_LEVEL_THREAD: + return 1; + case CPU_TOPOLOGY_LEVEL_CORE: + return topo_info->threads_per_core; + case CPU_TOPOLOGY_LEVEL_MODULE: + return topo_info->threads_per_core * topo_info->cores_per_module; + case CPU_TOPOLOGY_LEVEL_DIE: + return topo_info->threads_per_core * topo_info->cores_per_module * + topo_info->modules_per_die; + case CPU_TOPOLOGY_LEVEL_SOCKET: + return topo_info->threads_per_core * topo_info->cores_per_module * + topo_info->modules_per_die * topo_info->dies_per_pkg; + default: + g_assert_not_reached(); + } + return 0; +} + +static uint32_t apicid_offset_by_topo_level(X86CPUTopoInfo *topo_info, + enum CpuTopologyLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPOLOGY_LEVEL_THREAD: + return 0; + case CPU_TOPOLOGY_LEVEL_CORE: + return apicid_core_offset(topo_info); + case CPU_TOPOLOGY_LEVEL_MODULE: + return apicid_module_offset(topo_info); + case CPU_TOPOLOGY_LEVEL_DIE: + return apicid_die_offset(topo_info); + case CPU_TOPOLOGY_LEVEL_SOCKET: + return apicid_pkg_offset(topo_info); + default: + g_assert_not_reached(); + } + return 0; +} + +static uint32_t cpuid1f_topo_type(enum CpuTopologyLevel topo_level) +{ + switch (topo_level) { + case CPU_TOPOLOGY_LEVEL_INVALID: + return CPUID_1F_ECX_TOPO_LEVEL_INVALID; + case CPU_TOPOLOGY_LEVEL_THREAD: + return CPUID_1F_ECX_TOPO_LEVEL_SMT; + case CPU_TOPOLOGY_LEVEL_CORE: + return CPUID_1F_ECX_TOPO_LEVEL_CORE; + case CPU_TOPOLOGY_LEVEL_MODULE: + return CPUID_1F_ECX_TOPO_LEVEL_MODULE; + case CPU_TOPOLOGY_LEVEL_DIE: + return CPUID_1F_ECX_TOPO_LEVEL_DIE; + default: + /* Other types are not supported in QEMU. */ + g_assert_not_reached(); + } + return 0; +} + +static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, + X86CPUTopoInfo *topo_info, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + X86CPU *cpu = env_archcpu(env); + unsigned long level, base_level, next_level; + uint32_t num_threads_next_level, offset_next_level; + + assert(count <= CPU_TOPOLOGY_LEVEL_SOCKET); + + /* + * Find the No.(count + 1) topology level in avail_cpu_topo bitmap. + * The search starts from bit 0 (CPU_TOPOLOGY_LEVEL_THREAD). + */ + level = CPU_TOPOLOGY_LEVEL_THREAD; + base_level = level; + for (int i = 0; i <= count; i++) { + level = find_next_bit(env->avail_cpu_topo, + CPU_TOPOLOGY_LEVEL_SOCKET, + base_level); + + /* + * CPUID[0x1f] doesn't explicitly encode the package level, + * and it just encodes the invalid level (all fields are 0) + * into the last subleaf of 0x1f. + */ + if (level == CPU_TOPOLOGY_LEVEL_SOCKET) { + level = CPU_TOPOLOGY_LEVEL_INVALID; + break; + } + /* Search the next level. */ + base_level = level + 1; + } + + if (level == CPU_TOPOLOGY_LEVEL_INVALID) { + num_threads_next_level = 0; + offset_next_level = 0; + } else { + next_level = find_next_bit(env->avail_cpu_topo, + CPU_TOPOLOGY_LEVEL_SOCKET, + level + 1); + num_threads_next_level = num_threads_by_topo_level(topo_info, + next_level); + offset_next_level = apicid_offset_by_topo_level(topo_info, + next_level); + } + + *eax = offset_next_level; + /* The count (bits 15-00) doesn't need to be reliable. */ + *ebx = num_threads_next_level & 0xffff; + *ecx = (count & 0xff) | (cpuid1f_topo_type(level) << 8); + *edx = cpu->apic_id; + + assert(!(*eax & ~0x1f)); +} + /* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) { @@ -329,20 +485,12 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { - uint32_t l3_threads; assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); - - /* L3 is shared among multiple cores */ - if (cache->level == 3) { - l3_threads = topo_info->cores_per_die * topo_info->threads_per_core; - *eax |= (l3_threads - 1) << 14; - } else { - *eax |= ((topo_info->threads_per_core - 1) << 14); - } + *eax |= max_thread_ids_for_cache(topo_info, cache->share_level) << 14; assert(cache->line_size > 0); assert(cache->partitions > 0); @@ -396,12 +544,9 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * 31:11 Reserved. * 10:8 NodesPerProcessor: Node per processor. Read-only. Reset: XXXb. * ValidValues: - * Value Description - * 000b 1 node per processor. - * 001b 2 nodes per processor. - * 010b Reserved. - * 011b 4 nodes per processor. - * 111b-100b Reserved. + * Value Description + * 0h 1 node per processor. + * 7h-1h Reserved. * 7:0 NodeId: Node ID. Read-only. Reset: XXh. * * NOTE: Hardware reserves 3 bits for number of nodes per processor. @@ -410,8 +555,12 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * NodeId is combination of node and socket_id which is already decoded * in apic_id. Just use it by shifting. */ - *ecx = ((topo_info->dies_per_pkg - 1) << 8) | - ((cpu->apic_id >> apicid_die_offset(topo_info)) & 0xFF); + if (cpu->legacy_multi_node) { + *ecx = ((topo_info->dies_per_pkg - 1) << 8) | + ((cpu->apic_id >> apicid_die_offset(topo_info)) & 0xFF); + } else { + *ecx = (cpu->apic_id >> apicid_pkg_offset(topo_info)) & 0xFF; + } *edx = 0; } @@ -435,6 +584,7 @@ static CPUCacheInfo legacy_l1d_cache = { .sets = 64, .partitions = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ @@ -449,6 +599,7 @@ static CPUCacheInfo legacy_l1d_cache_amd = { .partitions = 1, .lines_per_tag = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /* L1 instruction cache: */ @@ -462,6 +613,7 @@ static CPUCacheInfo legacy_l1i_cache = { .sets = 64, .partitions = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ @@ -476,6 +628,7 @@ static CPUCacheInfo legacy_l1i_cache_amd = { .partitions = 1, .lines_per_tag = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /* Level 2 unified cache: */ @@ -489,6 +642,7 @@ static CPUCacheInfo legacy_l2_cache = { .sets = 4096, .partitions = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ @@ -498,6 +652,7 @@ static CPUCacheInfo legacy_l2_cache_cpuid2 = { .size = 2 * MiB, .line_size = 64, .associativity = 8, + .share_level = CPU_TOPOLOGY_LEVEL_INVALID, }; @@ -511,6 +666,7 @@ static CPUCacheInfo legacy_l2_cache_amd = { .associativity = 16, .sets = 512, .partitions = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; /* Level 3 unified cache: */ @@ -526,6 +682,7 @@ static CPUCacheInfo legacy_l3_cache = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }; /* TLB definitions: */ @@ -624,49 +781,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_AVX | CPUID_EXT_F16C | \ - CPUID_EXT_FMA) + 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_XTPR, CPUID_EXT_PDCM, CPUID_EXT_PCID, CPUID_EXT_DCA, - CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER */ + 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_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_AVX2) + 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_INVPCID, CPUID_7_0_EBX_RTM, - CPUID_7_0_EBX_RDSEED */ + 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 | CPUID_7_0_ECX_VAES) -#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) @@ -676,6 +908,19 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_SGX_12_0_EAX_FEATURES 0 #define TCG_SGX_12_0_EBX_FEATURES 0 #define TCG_SGX_12_1_EAX_FEATURES 0 +#define TCG_24_0_EBX_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] = { @@ -692,6 +937,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, @@ -769,7 +1015,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, @@ -808,7 +1054,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, }, @@ -818,9 +1064,9 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .type = CPUID_FEATURE_WORD, .feat_names = { "fsgsbase", "tsc-adjust", "sgx", "bmi1", - "hle", "avx2", NULL, "smep", + "hle", "avx2", "fdp-excptn-only", "smep", "bmi2", "erms", "invpcid", "rtm", - NULL, NULL, "mpx", NULL, + NULL, "zero-fcs-fds", "mpx", NULL, "avx512f", "avx512dq", "rdseed", "adx", "smap", "avx512ifma", "pcommit", "clflushopt", "clwb", "intel-pt", "avx512pf", "avx512er", @@ -862,7 +1108,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, @@ -874,8 +1120,46 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_7_1_EAX] = { .type = CPUID_FEATURE_WORD, .feat_names = { + "sha512", "sm3", "sm4", NULL, + "avx-vnni", "avx512-bf16", NULL, "cmpccxadd", + NULL, NULL, "fzrm", "fsrs", + "fsrc", NULL, NULL, NULL, + NULL, "fred", "lkgs", "wrmsrns", + NULL, "amx-fp16", NULL, "avx-ifma", + NULL, NULL, "lam", NULL, NULL, NULL, NULL, NULL, - "avx-vnni", "avx512-bf16", NULL, NULL, + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 1, + .reg = R_EAX, + }, + .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, "avx-vnni-int16", NULL, + NULL, NULL, "prefetchiti", NULL, + NULL, NULL, NULL, "avx10", + 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 = { + "intel-psfd", "ipred-ctrl", "rrsba-ctrl", "ddpd-u", + "bhi-ctrl", "mcdt-no", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -885,10 +1169,24 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .cpuid = { .eax = 7, - .needs_ecx = true, .ecx = 1, - .reg = R_EAX, + .needs_ecx = true, .ecx = 2, + .reg = R_EDX, }, - .tcg_features = TCG_7_1_EAX_FEATURES, + .tcg_features = TCG_7_2_EDX_FEATURES, + }, + [FEAT_24_0_EBX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + [16] = "avx10-128", + [17] = "avx10-256", + [18] = "avx10-512", + }, + .cpuid = { + .eax = 0x24, + .needs_ecx = true, .ecx = 0, + .reg = R_EBX, + }, + .tcg_features = TCG_24_0_EBX_FEATURES, }, [FEAT_8000_0007_EDX] = { .type = CPUID_FEATURE_WORD, @@ -906,6 +1204,22 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .tcg_features = TCG_APM_FEATURES, .unmigratable_flags = CPUID_APM_INVTSC, }, + [FEAT_8000_0007_EBX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + "overflow-recov", "succor", 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, + NULL, NULL, NULL, NULL, + }, + .cpuid = { .eax = 0x80000007, .reg = R_EBX, }, + .tcg_features = 0, + .unmigratable_flags = 0, + }, [FEAT_8000_0008_EBX] = { .type = CPUID_FEATURE_WORD, .feat_names = { @@ -913,12 +1227,50 @@ 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, + "eraps", NULL, NULL, "sbpb", + "ibpb-brtype", "srso-no", "srso-user-kernel-no", NULL, + }, + .cpuid = { .eax = 0x80000021, .reg = R_EAX, }, + .tcg_features = 0, + .unmigratable_flags = 0, + }, + [FEAT_8000_0021_EBX] = { + .type = CPUID_FEATURE_WORD, + .cpuid = { .eax = 0x80000021, .reg = R_EBX, }, + .tcg_features = 0, + .unmigratable_flags = 0, + }, + [FEAT_8000_0022_EAX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + "perfmon-v2", 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, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { .eax = 0x80000022, .reg = R_EAX, }, .tcg_features = 0, .unmigratable_flags = 0, }, @@ -991,7 +1343,9 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .needs_ecx = true, .ecx = 0, .reg = R_EAX, }, - .tcg_features = ~0U, + .tcg_features = XSTATE_FP_MASK | XSTATE_SSE_MASK | + XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK | + XSTATE_PKRU_MASK, .migratable_flags = XSTATE_FP_MASK | XSTATE_SSE_MASK | XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK | XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | XSTATE_Hi16_ZMM_MASK | @@ -1004,7 +1358,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .needs_ecx = true, .ecx = 0, .reg = R_EDX, }, - .tcg_features = ~0U, + .tcg_features = 0U, }, /*Below are MSR exposed features*/ [FEAT_ARCH_CAPABILITIES] = { @@ -1013,15 +1367,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, - NULL, NULL, NULL, NULL, + "pbrsb-no", NULL, "gds-no", "rfds-no", + "rfds-clear", 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, @@ -1082,7 +1443,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 = { @@ -1122,7 +1483,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "vmx-exit-save-efer", "vmx-exit-load-efer", "vmx-exit-save-preemption-timer", "vmx-exit-clear-bndcfgs", NULL, "vmx-exit-clear-rtit-ctl", NULL, NULL, - NULL, "vmx-exit-load-pkrs", NULL, NULL, + NULL, "vmx-exit-load-pkrs", NULL, "vmx-exit-secondary-ctls", }, .msr = { .index = MSR_IA32_VMX_TRUE_EXIT_CTLS, @@ -1137,7 +1498,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, "vmx-entry-ia32e-mode", NULL, NULL, NULL, "vmx-entry-load-perf-global-ctrl", "vmx-entry-load-pat", "vmx-entry-load-efer", "vmx-entry-load-bndcfgs", NULL, "vmx-entry-load-rtit-ctl", NULL, - NULL, NULL, "vmx-entry-load-pkrs", NULL, + NULL, NULL, "vmx-entry-load-pkrs", "vmx-entry-load-fred", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, @@ -1194,6 +1555,8 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .feat_names = { [54] = "vmx-ins-outs", [55] = "vmx-true-ctls", + [56] = "vmx-any-errcode", + [58] = "vmx-nested-exception", }, .msr = { .index = MSR_IA32_VMX_BASIC, @@ -1237,7 +1600,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, @@ -1277,7 +1640,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, @@ -1399,6 +1762,54 @@ static FeatureDep feature_dependencies[] = { .from = { FEAT_8000_0001_ECX, CPUID_EXT3_SVM }, .to = { FEAT_SVM, ~0ull }, }, + { + .from = { FEAT_7_0_ECX, CPUID_7_0_ECX_WAITPKG }, + .to = { FEAT_VMX_SECONDARY_CTLS, VMX_SECONDARY_EXEC_ENABLE_USER_WAIT_PAUSE }, + }, + { + .from = { FEAT_8000_0001_EDX, CPUID_EXT2_LM }, + .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, + }, + { + .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_LKGS }, + .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, + }, + { + .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_WRMSRNS }, + .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, + }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_SGX_LC }, + }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_SGX_12_0_EAX, ~0ull }, + }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_SGX_12_0_EBX, ~0ull }, + }, + { + .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, + .to = { FEAT_SGX_12_1_EAX, ~0ull }, + }, + { + .from = { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_128 }, + .to = { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_256 }, + }, + { + .from = { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_256 }, + .to = { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_512 }, + }, + { + .from = { FEAT_24_0_EBX, CPUID_24_0_EBX_AVX10_VL_MASK }, + .to = { FEAT_7_1_EDX, CPUID_7_1_EDX_AVX10 }, + }, + { + .from = { FEAT_7_1_EDX, CPUID_7_1_EDX_AVX10 }, + .to = { FEAT_24_0_EBX, ~0ull }, + }, }; typedef struct X86RegisterInfo32 { @@ -1471,8 +1882,6 @@ ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = { }, }; -#ifndef XBOX - uint32_t xsave_area_size(uint64_t mask, bool compacted) { uint64_t ret = x86_ext_save_areas[0].size; @@ -1490,8 +1899,6 @@ uint32_t xsave_area_size(uint64_t mask, bool compacted) return ret; } -#endif /* XBOX */ - static inline bool accel_uses_host_cpuid(void) { return kvm_enabled() || hvf_enabled(); @@ -1530,9 +1937,10 @@ static inline uint64_t x86_cpu_xsave_xss_components(X86CPU *cpu) * Returns the set of feature flags that are supported and migratable by * QEMU, for a given FeatureWord. */ -static uint64_t x86_cpu_get_migratable_flags(FeatureWord w) +static uint64_t x86_cpu_get_migratable_flags(X86CPU *cpu, FeatureWord w) { FeatureWordInfo *wi = &feature_word_info[w]; + CPUX86State *env = &cpu->env; uint64_t r = 0; int i; @@ -1546,6 +1954,12 @@ static uint64_t x86_cpu_get_migratable_flags(FeatureWord w) r |= f; } } + + /* when tsc-khz is set explicitly, invtsc is migratable */ + if ((w == FEAT_8000_0007_EDX) && env->user_tsc_khz) { + r |= CPUID_APM_INVTSC; + } + return r; } @@ -1603,8 +2017,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 { @@ -1612,6 +2025,7 @@ typedef struct X86CPUVersionDefinition { const char *alias; const char *note; PropValue *props; + const CPUCaches *const cache_info; } X86CPUVersionDefinition; /* Base definition for a CPU model */ @@ -1624,6 +2038,7 @@ typedef struct X86CPUDefinition { int family; int model; int stepping; + uint8_t avx10_version; FeatureWordArray features; const char *model_id; const CPUCaches *const cache_info; @@ -1684,6 +2099,7 @@ static const CPUCaches epyc_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -1696,6 +2112,7 @@ static const CPUCaches epyc_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1706,6 +2123,7 @@ static const CPUCaches epyc_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1719,6 +2137,61 @@ static const CPUCaches epyc_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -1734,6 +2207,7 @@ static const CPUCaches epyc_rome_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -1746,6 +2220,7 @@ static const CPUCaches epyc_rome_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1756,6 +2231,7 @@ static const CPUCaches epyc_rome_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1769,6 +2245,61 @@ static const CPUCaches epyc_rome_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -1784,6 +2315,7 @@ static const CPUCaches epyc_milan_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l1i_cache = &(CPUCacheInfo) { .type = INSTRUCTION_CACHE, @@ -1796,6 +2328,7 @@ static const CPUCaches epyc_milan_cache_info = { .lines_per_tag = 1, .self_init = 1, .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l2_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1806,6 +2339,7 @@ static const CPUCaches epyc_milan_cache_info = { .partitions = 1, .sets = 1024, .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, }, .l3_cache = &(CPUCacheInfo) { .type = UNIFIED_CACHE, @@ -1819,6 +2353,115 @@ static const CPUCaches epyc_milan_cache_info = { .self_init = true, .inclusive = true, .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + +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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 1 * MiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 2048, + .lines_per_tag = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .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, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; @@ -1841,7 +2484,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 @@ -3492,9 +4135,447 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } }, }, + { + .version = 7, + .note = "TSX, taa-no", + .props = (PropValue[]) { + /* Restore TSX features removed by -v2 above */ + { "hle", "on" }, + { "rtm", "on" }, + { /* end of list */ } + }, + }, { /* 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 */ } + } + }, + { + .version = 3, + .props = (PropValue[]) { + { "ss", "on" }, + { "tsc-adjust", "on" }, + { "cldemote", "on" }, + { "movdiri", "on" }, + { "movdir64b", "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 }, + { + .version = 2, + .props = (PropValue[]) { + { "ss", "on" }, + { "tsc-adjust", "on" }, + { "cldemote", "on" }, + { "movdiri", "on" }, + { "movdir64b", "on" }, + { "avx10", "on" }, + { "avx10-128", "on" }, + { "avx10-256", "on" }, + { "avx10-512", "on" }, + { "avx10-version", "1" }, + { "stepping", "1" }, + { /* end of list */ } + } + }, + { /* end of list */ }, + }, + }, + { + .name = "SierraForest", + .level = 0x23, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 175, + .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_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_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_SHA_NI, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + 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_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_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, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_CMPCCXADD | + CPUID_7_1_EAX_FSRS | CPUID_7_1_EAX_AVX_IFMA, + .features[FEAT_7_1_EDX] = + CPUID_7_1_EDX_AVX_VNNI_INT8 | CPUID_7_1_EDX_AVX_NE_CONVERT, + .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_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 (SierraForest)", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { /* end of list */ }, + }, + }, { .name = "Denverton", .level = 21, @@ -3987,6 +5068,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 */ } } }, @@ -4106,6 +5196,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 */ } } }, @@ -4163,6 +5272,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, }, #endif }; @@ -4214,6 +5415,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); @@ -4225,6 +5445,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) @@ -4243,15 +5464,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); @@ -4331,13 +5543,13 @@ static void x86_cpuid_version_get_family(Object *obj, Visitor *v, { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; - int64_t value; + uint64_t value; value = (env->cpuid_version >> 8) & 0xf; if (value == 0xf) { value += (env->cpuid_version >> 20) & 0xff; } - visit_type_int(v, name, &value, errp); + visit_type_uint64(v, name, &value, errp); } static void x86_cpuid_version_set_family(Object *obj, Visitor *v, @@ -4346,16 +5558,15 @@ static void x86_cpuid_version_set_family(Object *obj, Visitor *v, { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; - const int64_t min = 0; - const int64_t max = 0xff + 0xf; - int64_t value; + const uint64_t max = 0xff + 0xf; + uint64_t value; - if (!visit_type_int(v, name, &value, errp)) { + if (!visit_type_uint64(v, name, &value, errp)) { return; } - if (value < min || value > max) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", - name ? name : "null", value, min, max); + if (value > max) { + error_setg(errp, "parameter '%s' can be at most %" PRIu64, + name ? name : "null", max); return; } @@ -4373,11 +5584,11 @@ static void x86_cpuid_version_get_model(Object *obj, Visitor *v, { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; - int64_t value; + uint64_t value; value = (env->cpuid_version >> 4) & 0xf; value |= ((env->cpuid_version >> 16) & 0xf) << 4; - visit_type_int(v, name, &value, errp); + visit_type_uint64(v, name, &value, errp); } static void x86_cpuid_version_set_model(Object *obj, Visitor *v, @@ -4386,16 +5597,15 @@ static void x86_cpuid_version_set_model(Object *obj, Visitor *v, { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; - const int64_t min = 0; - const int64_t max = 0xff; - int64_t value; + const uint64_t max = 0xff; + uint64_t value; - if (!visit_type_int(v, name, &value, errp)) { + if (!visit_type_uint64(v, name, &value, errp)) { return; } - if (value < min || value > max) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", - name ? name : "null", value, min, max); + if (value > max) { + error_setg(errp, "parameter '%s' can be at most %" PRIu64, + name ? name : "null", max); return; } @@ -4409,10 +5619,10 @@ static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v, { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; - int64_t value; + uint64_t value; value = env->cpuid_version & 0xf; - visit_type_int(v, name, &value, errp); + visit_type_uint64(v, name, &value, errp); } static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v, @@ -4421,16 +5631,15 @@ static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v, { X86CPU *cpu = X86_CPU(obj); CPUX86State *env = &cpu->env; - const int64_t min = 0; - const int64_t max = 0xf; - int64_t value; + const uint64_t max = 0xf; + uint64_t value; - if (!visit_type_int(v, name, &value, errp)) { + if (!visit_type_uint64(v, name, &value, errp)) { return; } - if (value < min || value > max) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", - name ? name : "null", value, min, max); + if (value > max) { + error_setg(errp, "parameter '%s' can be at most %" PRIu64, + name ? name : "null", max); return; } @@ -4458,7 +5667,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; } @@ -4523,16 +5733,15 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { X86CPU *cpu = X86_CPU(obj); - const int64_t min = 0; const int64_t max = INT64_MAX; int64_t value; if (!visit_type_int(v, name, &value, errp)) { return; } - if (value < min || value > max) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", - name ? name : "null", value, min, max); + if (value < 0 || value > max) { + error_setg(errp, "parameter '%s' can be at most %" PRId64, + name ? name : "null", max); return; } @@ -4609,7 +5818,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) @@ -4711,7 +5920,7 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features, } } -static void x86_cpu_filter_features(X86CPU *cpu, bool verbose); +static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose); /* Build a list with the name of all features on a feature word array */ static void x86_cpu_list_feature_names(FeatureWordArray features, @@ -4742,40 +5951,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) @@ -4871,7 +6046,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = g_strdup_printf("%s (deprecated)", olddesc); } - qemu_printf("x86 %-20s %s\n", name, desc); + qemu_printf(" %-20s %s\n", name, desc); } /* list available CPU models and flags */ @@ -4904,6 +6079,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; @@ -4930,7 +6141,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); @@ -4945,11 +6155,13 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } -uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, - bool migratable_only) +#endif /* !CONFIG_USER_ONLY */ + +uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w) { FeatureWordInfo *wi = &feature_word_info[w]; uint64_t r = 0; + uint64_t unavail = 0; if (kvm_enabled()) { switch (wi->type) { @@ -4975,22 +6187,59 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, } else { return ~0; } + + switch (w) { #ifndef TARGET_X86_64 - if (w == FEAT_8000_0001_EDX) { - r &= ~CPUID_EXT2_LM; - } + case FEAT_8000_0001_EDX: + /* + * 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. + */ + unavail = tcg_enabled() + ? CPUID_EXT2_LM & ~CPUID_EXT2_KERNEL_FEATURES + : CPUID_EXT2_LM; + break; #endif - if (migratable_only) { - r &= x86_cpu_get_migratable_flags(w); + + case FEAT_8000_0007_EBX: + if (cpu && !IS_AMD_CPU(&cpu->env)) { + /* Disable AMD machine check architecture for Intel CPU. */ + unavail = ~0; + } + break; + + case FEAT_7_0_EBX: +#ifndef CONFIG_USER_ONLY + if (!check_sgx_support()) { + unavail = CPUID_7_0_EBX_SGX; + } +#endif + break; + case FEAT_7_0_ECX: +#ifndef CONFIG_USER_ONLY + if (!check_sgx_support()) { + unavail = CPUID_7_0_ECX_SGX_LC; + } +#endif + break; + + default: + break; + } + + r &= ~unavail; + if (cpu && cpu->migratable) { + r &= x86_cpu_get_migratable_flags(cpu, w); } return r; } -#ifndef XBOX static void x86_cpu_get_supported_cpuid(uint32_t func, uint32_t index, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { +#ifndef XBOX if (kvm_enabled()) { *eax = kvm_arch_get_supported_cpuid(kvm_state, func, index, R_EAX); *ebx = kvm_arch_get_supported_cpuid(kvm_state, func, index, R_EBX); @@ -5002,13 +6251,17 @@ static void x86_cpu_get_supported_cpuid(uint32_t func, uint32_t index, *ecx = hvf_get_supported_cpuid(func, index, R_ECX); *edx = hvf_get_supported_cpuid(func, index, R_EDX); } else { +#endif *eax = 0; *ebx = 0; *ecx = 0; *edx = 0; +#ifndef XBOX } +#endif } +#ifndef XBOX static void x86_cpu_get_cache_cpuid(uint32_t func, uint32_t index, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) @@ -5089,6 +6342,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. @@ -5121,7 +6399,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); #ifndef XBOX env->features[FEAT_1_ECX] |= CPUID_EXT_HYPERVISOR; @@ -5141,6 +6419,9 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model) */ object_property_set_str(OBJECT(cpu), "vendor", def->vendor, &error_abort); + object_property_set_uint(OBJECT(cpu), "avx10-version", def->avx10_version, + &error_abort); + x86_cpu_apply_version_props(cpu, model); /* @@ -5151,12 +6432,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 } @@ -5212,9 +6493,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; @@ -5250,12 +6532,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, #ifndef XBOX uint32_t die_offset; uint32_t signature[3]; +#endif X86CPUTopoInfo topo_info; + uint32_t cores_per_pkg; + uint32_t threads_per_pkg; topo_info.dies_per_pkg = env->nr_dies; - topo_info.cores_per_die = cs->nr_cores; + topo_info.modules_per_die = env->nr_modules; + topo_info.cores_per_module = cs->nr_cores / env->nr_dies / env->nr_modules; topo_info.threads_per_core = cs->nr_threads; -#endif + + cores_per_pkg = topo_info.cores_per_module * topo_info.modules_per_die * + topo_info.dies_per_pkg; + threads_per_pkg = cores_per_pkg * topo_info.threads_per_core; /* Calculate & apply limits for different index ranges */ if (index >= 0xC0000000) { @@ -5296,8 +6585,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= CPUID_EXT_OSXSAVE; } *edx = env->features[FEAT_1_EDX]; - if (cs->nr_cores * cs->nr_threads > 1) { - *ebx |= (cs->nr_cores * cs->nr_threads) << 16; + if (threads_per_pkg > 1) { + *ebx |= threads_per_pkg << 16; *edx |= CPUID_HT; } if (!cpu->enable_pmu) { @@ -5343,42 +6632,48 @@ 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; - if (cs->nr_cores > 1) { - *eax &= ~0xFC000000; - *eax |= (pow2ceil(cs->nr_cores) - 1) << 26; - } - if (host_vcpus_per_cache > vcpus_per_socket) { + + *eax &= ~0xFC000000; + *eax |= max_core_ids_in_package(&topo_info) << 26; + if (host_vcpus_per_cache > threads_per_pkg) { *eax &= ~0x3FFC000; - *eax |= (pow2ceil(vcpus_per_socket) - 1) << 14; + + /* Share the cache at package level. */ + *eax |= max_thread_ids_for_cache(&topo_info, + CPU_TOPOLOGY_LEVEL_SOCKET) << 14; } } } else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) { *eax = *ebx = *ecx = *edx = 0; } else { *eax = 0; + switch (count) { case 0: /* L1 dcache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - 1, cs->nr_cores, + &topo_info, eax, ebx, ecx, edx); + if (!cpu->l1_cache_per_core) { + *eax &= ~MAKE_64BIT_MASK(14, 12); + } break; case 1: /* L1 icache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - 1, cs->nr_cores, + &topo_info, eax, ebx, ecx, edx); + if (!cpu->l1_cache_per_core) { + *eax &= ~MAKE_64BIT_MASK(14, 12); + } break; case 2: /* L2 cache info */ encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - cs->nr_threads, cs->nr_cores, + &topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - die_offset = apicid_die_offset(&topo_info); if (cpu->enable_l3_cache) { encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - (1 << die_offset), cs->nr_cores, + &topo_info, eax, ebx, ecx, edx); break; } @@ -5414,30 +6709,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx |= CPUID_7_0_ECX_OSPKE; } *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ - - /* - * SGX cannot be emulated in software. If hardware does not - * 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))) { - *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))) { - *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; @@ -5454,7 +6735,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; @@ -5476,62 +6757,37 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: *eax = apicid_core_offset(&topo_info); - *ebx = cs->nr_threads; - *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; + *ebx = topo_info.threads_per_core; + *ecx |= CPUID_B_ECX_TOPO_LEVEL_SMT << 8; break; case 1: *eax = apicid_pkg_offset(&topo_info); - *ebx = cs->nr_cores * cs->nr_threads; - *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; + *ebx = threads_per_pkg; + *ecx |= CPUID_B_ECX_TOPO_LEVEL_CORE << 8; break; default: *eax = 0; *ebx = 0; - *ecx |= CPUID_TOPOLOGY_LEVEL_INVALID; + *ecx |= CPUID_B_ECX_TOPO_LEVEL_INVALID << 8; } assert(!(*eax & ~0x1f)); *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; } break; case 0x1F: /* V2 Extended Topology Enumeration Leaf */ - if (env->nr_dies < 2) { + if (!x86_has_extended_topo(env->avail_cpu_topo)) { *eax = *ebx = *ecx = *edx = 0; break; } - *ecx = count & 0xff; - *edx = cpu->apic_id; - switch (count) { - case 0: - *eax = apicid_core_offset(&topo_info); - *ebx = cs->nr_threads; - *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; - break; - case 1: - *eax = apicid_die_offset(&topo_info); - *ebx = cs->nr_cores * cs->nr_threads; - *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; - break; - case 2: - *eax = apicid_pkg_offset(&topo_info); - *ebx = env->nr_dies * cs->nr_cores * cs->nr_threads; - *ecx |= CPUID_TOPOLOGY_LEVEL_DIE; - break; - default: - *eax = 0; - *ebx = 0; - *ecx |= CPUID_TOPOLOGY_LEVEL_INVALID; - } - assert(!(*eax & ~0x1f)); - *ebx &= 0xffff; /* The count doesn't need to be reliable. */ + encode_topo_cpuid1f(env, count, &topo_info, eax, ebx, ecx, edx); break; case 0xD: { /* Processor Extended State */ @@ -5551,7 +6807,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) { @@ -5569,9 +6825,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]; @@ -5657,6 +6912,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; @@ -5671,7 +6930,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; @@ -5692,7 +6951,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; @@ -5707,6 +6966,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x24: { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + if ((env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) && count == 0) { + *ebx = env->features[FEAT_24_0_EBX] | env->avx10_version; + } + break; + } case 0x40000000: /* * CPUID code in kvm_arch_init_vcpu() ignores stuff @@ -5747,13 +7016,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * discards multiple thread information if it is set. * So don't set it here for Intel to make Linux guests happy. */ - if (cs->nr_cores * cs->nr_threads > 1) { + if (threads_per_pkg > 1) { if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { *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: @@ -5797,7 +7070,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x80000007: *eax = 0; - *ebx = 0; + *ebx = env->features[FEAT_8000_0007_EBX]; *ecx = 0; *edx = env->features[FEAT_8000_0007_EDX]; break; @@ -5807,9 +7080,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { /* 64 bit processor */ *eax |= (cpu_x86_virtual_addr_width(env) << 8); + *eax |= (cpu->guest_phys_bits << 16); } *ebx = env->features[FEAT_8000_0008_EBX]; - if (cs->nr_cores * cs->nr_threads > 1) { + if (threads_per_pkg > 1) { /* * Bits 15:12 is "The number of bits in the initial * Core::X86::Apic::ApicId[ApicId] value that indicate @@ -5817,7 +7091,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * Bits 7:0 is "The number of threads in the package is NC+1" */ *ecx = (apicid_pkg_offset(&topo_info) << 12) | - ((cs->nr_cores * cs->nr_threads) - 1); + (threads_per_pkg - 1); } else { *ecx = 0; } @@ -5863,6 +7137,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; break; } + if (cpu->amd_topoext_features_only) { + *edx &= CACHE_NO_INVD_SHARING | CACHE_INCLUSIVE; + } break; case 0x8000001E: if (cpu->core_id <= 255) { @@ -5874,6 +7151,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; + case 0x80000022: + *eax = *ebx = *ecx = *edx = 0; + /* AMD Extended Performance Monitoring and Debug */ + if (kvm_enabled() && cpu->enable_pmu && + (env->features[FEAT_8000_0022_EAX] & CPUID_8000_0022_EAX_PERFMON_V2)) { + *eax |= CPUID_8000_0022_EAX_PERFMON_V2; + *ebx |= kvm_arch_get_supported_cpuid(cs->kvm_state, index, count, + R_EBX) & 0xf; + } + break; case 0xC0000000: *eax = env->cpuid_xlevel2; *ebx = 0; @@ -5901,10 +7188,16 @@ 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; + *eax |= sev_snp_enabled() ? 0x10 : 0; + *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ + *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ } break; + case 0x80000021: + *eax = *ebx = *ecx = *edx = 0; + *eax = env->features[FEAT_8000_0021_EAX]; + *ebx = env->features[FEAT_8000_0021_EBX]; + break; #endif /* XBOX */ default: /* reserved values: zero */ @@ -5927,20 +7220,43 @@ static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) #endif } -static void x86_cpu_reset(DeviceState *dev) +static bool cpuid_has_xsave_feature(CPUX86State *env, const ExtSaveArea *esa) { - CPUState *s = CPU(dev); - X86CPU *cpu = X86_CPU(s); - X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); + if (!esa->size) { + return false; + } + + if (env->features[esa->feature] & esa->bits) { + return true; + } + if (esa->feature == FEAT_7_0_EBX && esa->bits == CPUID_7_0_EBX_AVX512F + && (env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10)) { + return true; + } + + return false; +} + +static void x86_cpu_reset_hold(Object *obj, ResetType type) +{ + 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, type); + } memset(env, 0, offsetof(CPUX86State, end_reset_fields)); + if (tcg_enabled()) { + cpu_init_fp_statuses(env); + } + env->old_exception = -1; /* init to reset state */ @@ -6017,8 +7333,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; @@ -6033,7 +7349,7 @@ static void x86_cpu_reset(DeviceState *dev) if (!((1 << i) & CPUID_XSTATE_XCR0_MASK)) { continue; } - if (env->features[esa->feature] & esa->bits) { + if (cpuid_has_xsave_feature(env, esa)) { xcr0 |= 1ull << i; } } @@ -6069,9 +7385,9 @@ static void x86_cpu_reset(DeviceState *dev) 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); @@ -6163,13 +7479,15 @@ 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; } mask = 0; for (i = 0; i < ARRAY_SIZE(x86_ext_save_areas); i++) { const ExtSaveArea *esa = &x86_ext_save_areas[i]; - if (env->features[esa->feature] & esa->bits) { + if (cpuid_has_xsave_feature(env, esa)) { mask |= (1ULL << i); } } @@ -6181,9 +7499,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 @@ -6258,10 +7576,16 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) * by the user. */ env->features[w] |= - x86_cpu_get_supported_feature_word(w, cpu->migratable) & + x86_cpu_get_supported_feature_word(cpu, w) & ~env->user_features[w] & ~feature_word_info[w].no_autoenable_flags; } + + if ((env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) && !env->avx10_version) { + uint32_t eax, ebx, ecx, edx; + x86_cpu_get_supported_cpuid(0x24, 0, &eax, &ebx, &ecx, &edx); + env->avx10_version = ebx & 0xff; + } } for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { @@ -6292,6 +7616,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); @@ -6318,11 +7644,16 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) * cpu->vendor_cpuid_only has been unset for compatibility with older * machine types. */ - if ((env->nr_dies > 1) && + if (x86_has_extended_topo(env->avail_cpu_topo) && (IS_INTEL_CPU(env) || !cpu->vendor_cpuid_only)) { x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F); } + /* Advanced Vector Extensions 10 (AVX10) requires CPUID[0x24] */ + if (env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) { + x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x24); + } + /* SVM requires CPUID[0x8000000A] */ if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A); @@ -6333,6 +7664,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); @@ -6353,8 +7688,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; } } @@ -6362,13 +7697,17 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) * Finishes initialization of CPUID data, filters CPU feature * words based on host availability of each feature. * - * Returns: 0 if all flags are supported by the host, non-zero otherwise. + * Returns: true if any flag is not supported by the host, false otherwise. */ -static void x86_cpu_filter_features(X86CPU *cpu, bool verbose) +static bool x86_cpu_filter_features(X86CPU *cpu, bool verbose) { CPUX86State *env = &cpu->env; FeatureWord w; const char *prefix = NULL; + bool have_filtered_features; + + uint32_t eax_0, ebx_0, ecx_0, edx_0; + uint32_t eax_1, ebx_1, ecx_1, edx_1; if (verbose) { prefix = accel_uses_host_cpuid() @@ -6378,20 +7717,22 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose) for (w = 0; w < FEATURE_WORDS; w++) { uint64_t host_feat = - x86_cpu_get_supported_feature_word(w, false); + x86_cpu_get_supported_feature_word(NULL, w); uint64_t requested_features = env->features[w]; uint64_t unavailable_features = requested_features & ~host_feat; 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); + x86_cpu_get_supported_cpuid(0x14, 0, + &eax_0, &ebx_0, &ecx_0, &edx_0); + x86_cpu_get_supported_cpuid(0x14, 1, + &eax_1, &ebx_1, &ecx_1, &edx_1); if (!eax_0 || ((ebx_0 & INTEL_PT_MINIMAL_EBX) != INTEL_PT_MINIMAL_EBX) || @@ -6411,6 +7752,28 @@ static void x86_cpu_filter_features(X86CPU *cpu, bool verbose) mark_unavailable_features(cpu, FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT, prefix); } } + + have_filtered_features = x86_cpu_have_filtered_features(cpu); + + if (env->features[FEAT_7_1_EDX] & CPUID_7_1_EDX_AVX10) { + x86_cpu_get_supported_cpuid(0x24, 0, + &eax_0, &ebx_0, &ecx_0, &edx_0); + uint8_t version = ebx_0 & 0xff; + + if (version < env->avx10_version) { + if (prefix) { + warn_report("%s: avx10.%d. Adjust to avx10.%d", + prefix, env->avx10_version, version); + } + env->avx10_version = version; + have_filtered_features = true; + } + } else if (env->avx10_version && prefix) { + warn_report("%s: avx10.%d.", prefix, env->avx10_version); + have_filtered_features = true; + } + + return have_filtered_features; } static void x86_cpu_hyperv_realize(X86CPU *cpu) @@ -6449,9 +7812,13 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) X86CPUClass *xcc = X86_CPU_GET_CLASS(dev); CPUX86State *env = &cpu->env; Error *local_err = NULL; - static bool ht_warned; unsigned requested_lbr_fmt; +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) + /* Use pc-relative instructions in system-mode */ + tcg_cflags_set(cs, CF_PCREL); +#endif + if (cpu->apic_id == UNASSIGNED_APIC_ID) { error_setg(errp, "apic-id property was not initialized properly"); return; @@ -6489,7 +7856,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) env->features[FEAT_PERF_CAPABILITIES] & PERF_CAP_LBR_FMT; if (requested_lbr_fmt && kvm_enabled()) { uint64_t host_perf_cap = - x86_cpu_get_supported_feature_word(FEAT_PERF_CAPABILITIES, false); + x86_cpu_get_supported_feature_word(NULL, FEAT_PERF_CAPABILITIES); unsigned host_lbr_fmt = host_perf_cap & PERF_CAP_LBR_FMT; if (!cpu->enable_pmu) { @@ -6504,14 +7871,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) } } - x86_cpu_filter_features(cpu, cpu->check_cpuid || cpu->enforce_cpuid); - - if (cpu->enforce_cpuid && x86_cpu_have_filtered_features(cpu)) { - error_setg(&local_err, - accel_uses_host_cpuid() ? + if (x86_cpu_filter_features(cpu, cpu->check_cpuid || cpu->enforce_cpuid)) { + if (cpu->enforce_cpuid) { + error_setg(&local_err, + accel_uses_host_cpuid() ? "Host doesn't support requested features" : "TCG doesn't support requested features"); - goto out; + goto out; + } } /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on @@ -6543,6 +7910,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) goto out; } + if (cpu->guest_phys_bits == -1) { + /* + * If it was not set by the user, or by the accelerator via + * cpu_exec_realizefn, clear. + */ + cpu->guest_phys_bits = 0; + } + if (cpu->ucode_rev == 0) { /* * The default is the same as KVM's. Note that this check @@ -6593,6 +7968,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (cpu->phys_bits == 0) { cpu->phys_bits = TCG_PHYS_ADDR_BITS; } + if (cpu->guest_phys_bits && + (cpu->guest_phys_bits > cpu->phys_bits || + cpu->guest_phys_bits < 32)) { + error_setg(errp, "guest-phys-bits should be between 32 and %u " + " (but is %u)", + cpu->phys_bits, cpu->guest_phys_bits); + return; + } } else { /* For 32 bit systems don't use the user set value, but keep * phys_bits consistent with what we tell the guest. @@ -6601,8 +7984,12 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) error_setg(errp, "phys-bits is not user-configurable in 32 bit"); return; } + if (cpu->guest_phys_bits != 0) { + error_setg(errp, "guest-phys-bits is not user-configurable in 32 bit"); + 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; @@ -6611,14 +7998,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; @@ -6651,6 +8041,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) mce_init(cpu); + x86_cpu_gdb_init(cs); qemu_init_vcpu(cs); /* @@ -6664,13 +8055,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) */ if (IS_AMD_CPU(env) && !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && - cs->nr_threads > 1 && !ht_warned) { - warn_report("This family of AMD CPU doesn't support " - "hyperthreading(%d)", - cs->nr_threads); - error_printf("Please configure -smp options properly" - " or try enabling topoext feature.\n"); - ht_warned = true; + cs->nr_threads > 1) { + warn_report_once("This family of AMD CPU doesn't support " + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext " + "feature.", cs->nr_threads); } #ifndef CONFIG_USER_ONLY @@ -6804,17 +8193,49 @@ static void x86_cpu_register_feature_bit_props(X86CPUClass *xcc, static void x86_cpu_post_initfn(Object *obj) { + static bool first = true; + uint64_t supported_xcr0; + int i; + + if (first) { + first = false; + + supported_xcr0 = + ((uint64_t) x86_cpu_get_supported_feature_word(NULL, FEAT_XSAVE_XCR0_HI) << 32) | + x86_cpu_get_supported_feature_word(NULL, FEAT_XSAVE_XCR0_LO); + + for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) { + ExtSaveArea *esa = &x86_ext_save_areas[i]; + + if (!(supported_xcr0 & (1 << i))) { + esa->size = 0; + } + } + } + accel_cpu_instance_init(CPU(obj)); } +static void x86_cpu_init_default_topo(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + + env->nr_modules = 1; + env->nr_dies = 1; + + /* thread, core and socket levels are set by default. */ + set_bit(CPU_TOPOLOGY_LEVEL_THREAD, env->avail_cpu_topo); + set_bit(CPU_TOPOLOGY_LEVEL_CORE, env->avail_cpu_topo); + set_bit(CPU_TOPOLOGY_LEVEL_SOCKET, env->avail_cpu_topo); +} + static void x86_cpu_initfn(Object *obj) { X86CPU *cpu = X86_CPU(obj); X86CPUClass *xcc = X86_CPU_GET_CLASS(obj); CPUX86State *env = &cpu->env; - env->nr_dies = 1; - cpu_set_cpustate_pointers(cpu); + x86_cpu_init_default_topo(cpu); object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo", x86_cpu_get_feature_words, @@ -6944,6 +8365,39 @@ static bool x86_cpu_has_work(CPUState *cs) return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0; } +int x86_mmu_index_pl(CPUX86State *env, unsigned pl) +{ + int mmu_index_32 = (env->hflags & HF_CS64_MASK) ? 0 : 1; + int mmu_index_base = + pl == 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 int x86_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUX86State *env = cpu_env(cs); + return x86_mmu_index_pl(env, env->hflags & HF_CPL_MASK); +} + +static int x86_mmu_index_kernel_pl(CPUX86State *env, unsigned pl) +{ + int mmu_index_32 = (env->hflags & HF_LMA_MASK) ? 0 : 1; + int mmu_index_base = + !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : + (pl < 3 && (env->eflags & AC_MASK) + ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX); + + return mmu_index_base + mmu_index_32; +} + +int cpu_mmu_index_kernel(CPUX86State *env) +{ + return x86_mmu_index_kernel_pl(env, env->hflags & HF_CPL_MASK); +} + static void x86_disas_set_info(CPUState *cs, disassemble_info *info) { X86CPU *cpu = X86_CPU(cs); @@ -7009,12 +8463,14 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, 0), DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, 0), DEFINE_PROP_INT32("core-id", X86CPU, core_id, 0), + DEFINE_PROP_INT32("module-id", X86CPU, module_id, 0), DEFINE_PROP_INT32("die-id", X86CPU, die_id, 0), DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, 0), #else DEFINE_PROP_UINT32("apic-id", X86CPU, apic_id, UNASSIGNED_APIC_ID), DEFINE_PROP_INT32("thread-id", X86CPU, thread_id, -1), DEFINE_PROP_INT32("core-id", X86CPU, core_id, -1), + DEFINE_PROP_INT32("module-id", X86CPU, module_id, -1), DEFINE_PROP_INT32("die-id", X86CPU, die_id, -1), DEFINE_PROP_INT32("socket-id", X86CPU, socket_id, -1), #endif @@ -7066,8 +8522,10 @@ static Property x86_cpu_properties[] = { HYPERV_FEAT_TLBFLUSH_DIRECT, 0), DEFINE_PROP_ON_OFF_AUTO("hv-no-nonarch-coresharing", X86CPU, hyperv_no_nonarch_cs, ON_OFF_AUTO_OFF), +#ifdef CONFIG_SYNDBG DEFINE_PROP_BIT64("hv-syndbg", X86CPU, hyperv_features, HYPERV_FEAT_SYNDBG, 0), +#endif DEFINE_PROP_BOOL("hv-passthrough", X86CPU, hyperv_passthrough, false), DEFINE_PROP_BOOL("hv-enforce-cpuid", X86CPU, hyperv_enforce_cpuid, false), @@ -7087,6 +8545,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("x-force-features", X86CPU, force_features, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0), + DEFINE_PROP_UINT32("guest-phys-bits", X86CPU, guest_phys_bits, -1), DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0), DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), @@ -7098,15 +8557,15 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("min-level", X86CPU, env.cpuid_min_level, 0), DEFINE_PROP_UINT32("min-xlevel", X86CPU, env.cpuid_min_xlevel, 0), DEFINE_PROP_UINT32("min-xlevel2", X86CPU, env.cpuid_min_xlevel2, 0), + DEFINE_PROP_UINT8("avx10-version", X86CPU, env.avx10_version, 0), DEFINE_PROP_UINT64("ucode-rev", X86CPU, ucode_rev, 0), DEFINE_PROP_BOOL("full-cpuid-auto-level", X86CPU, full_cpuid_auto_level, true), DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("x-vendor-cpuid-only", X86CPU, vendor_cpuid_only, true), + DEFINE_PROP_BOOL("x-amd-topoext-features-only", X86CPU, amd_topoext_features_only, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), - DEFINE_PROP_BOOL("kvm-no-smi-migration", X86CPU, kvm_no_smi_migration, - false), DEFINE_PROP_BOOL("kvm-pv-enforce-cpuid", X86CPU, kvm_pv_enforce_cpuid, false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), @@ -7118,6 +8577,8 @@ 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("legacy-multi-node", X86CPU, legacy_multi_node, false), + DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), /* * From "Requirements for Implementing the Microsoft @@ -7136,6 +8597,7 @@ static Property x86_cpu_properties[] = { false), DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, true), + DEFINE_PROP_BOOL("x-l1-cache-per-thread", X86CPU, l1_cache_per_core, true), DEFINE_PROP_END_OF_LIST() }; @@ -7161,6 +8623,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, @@ -7169,12 +8632,14 @@ 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; @@ -7189,10 +8654,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; @@ -7243,6 +8706,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 eb5e6a5e22..a6d4b019bb 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -24,11 +24,13 @@ #include "cpu-qom.h" #include "kvm/hyperv-proto.h" #include "exec/cpu-defs.h" +#include "exec/memop.h" +#include "hw/i386/topology.h" #include "qapi/qapi-types-common.h" #include "qemu/cpu-float.h" +#include "qemu/timer.h" -/* The x86 has a strong memory model with some store-after-load re-ordering */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) +#define XEN_NR_VIRQS 24 #define KVM_HAVE_MCE_INJECTION 1 @@ -260,6 +262,13 @@ typedef enum X86Seg { #define CR4_SMAP_MASK (1U << 21) #define CR4_PKE_MASK (1U << 22) #define CR4_PKS_MASK (1U << 24) +#define CR4_LAM_SUP_MASK (1U << 28) + +#ifdef TARGET_X86_64 +#define CR4_FRED_MASK (1ULL << 32) +#else +#define CR4_FRED_MASK 0 +#endif #define CR4_RESERVED_MASK \ (~(target_ulong)(CR4_VME_MASK | CR4_PVI_MASK | CR4_TSD_MASK \ @@ -268,7 +277,8 @@ typedef enum X86Seg { | CR4_OSFXSR_MASK | CR4_OSXMMEXCPT_MASK | CR4_UMIP_MASK \ | CR4_LA57_MASK \ | CR4_FSGSBASE_MASK | CR4_PCIDE_MASK | CR4_OSXSAVE_MASK \ - | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK)) + | CR4_SMEP_MASK | CR4_SMAP_MASK | CR4_PKE_MASK | CR4_PKS_MASK \ + | CR4_LAM_SUP_MASK | CR4_FRED_MASK)) #define DR6_BD (1 << 13) #define DR6_BS (1 << 14) @@ -338,6 +348,7 @@ typedef enum X86Seg { #define PG_MODE_PKE (1 << 17) #define PG_MODE_PKS (1 << 18) #define PG_MODE_SMEP (1 << 19) +#define PG_MODE_PG (1 << 20) #define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */ #define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */ @@ -364,6 +375,8 @@ typedef enum X86Seg { #define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */ #define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */ #define MCI_STATUS_AR (1ULL<<55) /* Action required */ +#define MCI_STATUS_DEFERRED (1ULL<<44) /* Deferred error */ +#define MCI_STATUS_POISON (1ULL<<43) /* Poisoned data consumed */ /* MISC register defines */ #define MCM_ADDR_SEGOFF 0 /* segment offset */ @@ -379,6 +392,10 @@ typedef enum X86Seg { #define MSR_IA32_APICBASE_EXTD (1 << 10) #define MSR_IA32_APICBASE_BASE (0xfffffU<<12) #define MSR_IA32_EBL_CR_POWERON 0x2a +#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 @@ -396,6 +413,10 @@ typedef enum X86Seg { #define MSR_IA32_TSX_CTRL 0x122 #define MSR_IA32_TSCDEADLINE 0x6e0 #define MSR_IA32_PKRS 0x6e1 +#define MSR_RAPL_POWER_UNIT 0x00000606 +#define MSR_PKG_POWER_LIMIT 0x00000610 +#define MSR_PKG_ENERGY_STATUS 0x00000611 +#define MSR_PKG_POWER_INFO 0x00000614 #define MSR_ARCH_LBR_CTL 0x000014ce #define MSR_ARCH_LBR_DEPTH 0x000014cf #define MSR_ARCH_LBR_FROM_0 0x00001500 @@ -517,11 +538,24 @@ typedef enum X86Seg { #define MSR_AMD64_TSC_RATIO_DEFAULT 0x100000000ULL +#define MSR_K7_HWCR 0xc0010015 + #define MSR_VM_HSAVE_PA 0xc0010117 #define MSR_IA32_XFD 0x000001c4 #define MSR_IA32_XFD_ERR 0x000001c5 +/* FRED MSRs */ +#define MSR_IA32_FRED_RSP0 0x000001cc /* Stack level 0 regular stack pointer */ +#define MSR_IA32_FRED_RSP1 0x000001cd /* Stack level 1 regular stack pointer */ +#define MSR_IA32_FRED_RSP2 0x000001ce /* Stack level 2 regular stack pointer */ +#define MSR_IA32_FRED_RSP3 0x000001cf /* Stack level 3 regular stack pointer */ +#define MSR_IA32_FRED_STKLVLS 0x000001d0 /* FRED exception stack levels */ +#define MSR_IA32_FRED_SSP1 0x000001d1 /* Stack level 1 shadow stack pointer in ring 0 */ +#define MSR_IA32_FRED_SSP2 0x000001d2 /* Stack level 2 shadow stack pointer in ring 0 */ +#define MSR_IA32_FRED_SSP3 0x000001d3 /* Stack level 3 shadow stack pointer in ring 0 */ +#define MSR_IA32_FRED_CONFIG 0x000001d4 /* FRED Entrypoint and interrupt stack level */ + #define MSR_IA32_BNDCFGS 0x00000d90 #define MSR_IA32_XSS 0x00000da0 #define MSR_IA32_UMWAIT_CONTROL 0xe1 @@ -545,6 +579,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 @@ -598,8 +635,12 @@ typedef enum FeatureWord { FEAT_7_1_EAX, /* CPUID[EAX=7,ECX=1].EAX */ FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */ FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ + FEAT_8000_0007_EBX, /* CPUID[8000_0007].EBX */ 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_8000_0021_EBX, /* CPUID[8000_0021].EBX */ + FEAT_8000_0022_EAX, /* CPUID[8000_0022].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 */ @@ -626,12 +667,14 @@ 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 */ + FEAT_24_0_EBX, /* CPUID[EAX=0x24,ECX=0].EBX */ FEATURE_WORDS, } FeatureWord; typedef uint64_t FeatureWordArray[FEATURE_WORDS]; -uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, - bool migratable_only); +uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* cpuid_features bits */ #define CPUID_FP87 (1U << 0) @@ -725,7 +768,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 | \ @@ -772,10 +815,13 @@ 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 */ #define CPUID_7_0_EBX_FSGSBASE (1U << 0) +/* Support TSC adjust MSR */ +#define CPUID_7_0_EBX_TSC_ADJUST (1U << 1) /* Support SGX */ #define CPUID_7_0_EBX_SGX (1U << 2) /* 1st Group of Advanced Bit Manipulation Extensions */ @@ -784,6 +830,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EBX_HLE (1U << 4) /* Intel Advanced Vector Extensions 2 */ #define CPUID_7_0_EBX_AVX2 (1U << 5) +/* FPU data pointer updated only on x87 exceptions */ +#define CPUID_7_0_EBX_FDP_EXCPTN_ONLY (1u << 6) /* Supervisor-mode Execution Prevention */ #define CPUID_7_0_EBX_SMEP (1U << 7) /* 2nd Group of Advanced Bit Manipulation Extensions */ @@ -794,6 +842,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EBX_INVPCID (1U << 10) /* Restricted Transactional Memory */ #define CPUID_7_0_EBX_RTM (1U << 11) +/* Zero out FPU CS and FPU DS */ +#define CPUID_7_0_EBX_ZERO_FCS_FDS (1U << 13) /* Memory Protection Extension */ #define CPUID_7_0_EBX_MPX (1U << 14) /* AVX-512 Foundation */ @@ -808,8 +858,6 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_7_0_EBX_SMAP (1U << 20) /* AVX-512 Integer Fused Multiply Add */ #define CPUID_7_0_EBX_AVX512IFMA (1U << 21) -/* Persistent Commit */ -#define CPUID_7_0_EBX_PCOMMIT (1U << 22) /* Flush a Cache Line Optimized */ #define CPUID_7_0_EBX_CLFLUSHOPT (1U << 23) /* Cache Line Write Back */ @@ -884,14 +932,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 */ @@ -903,12 +957,62 @@ 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) +/* Linear Address Masking */ +#define CPUID_7_1_EAX_LAM (1U << 26) + +/* 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) +/* Support for Advanced Vector Extensions 10 */ +#define CPUID_7_1_EDX_AVX10 (1U << 19) +/* Flexible return and event delivery (FRED) */ +#define CPUID_7_1_EAX_FRED (1U << 17) +/* Load into IA32_KERNEL_GS_BASE (LKGS) */ +#define CPUID_7_1_EAX_LKGS (1U << 18) +/* Non-Serializing Write to Model Specific Register (WRMSRNS) */ +#define CPUID_7_1_EAX_WRMSRNS (1U << 19) + +/* 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) /* Packets which contain IP payload have LIP values */ #define CPUID_14_0_ECX_LIP (1U << 31) +/* AVX10 128-bit vector support is present */ +#define CPUID_24_0_EBX_AVX10_128 (1U << 16) +/* AVX10 256-bit vector support is present */ +#define CPUID_24_0_EBX_AVX10_256 (1U << 17) +/* AVX10 512-bit vector support is present */ +#define CPUID_24_0_EBX_AVX10_512 (1U << 18) +/* AVX10 vector length support mask */ +#define CPUID_24_0_EBX_AVX10_VL_MASK (CPUID_24_0_EBX_AVX10_128 | \ + CPUID_24_0_EBX_AVX10_256 | \ + CPUID_24_0_EBX_AVX10_512) + +/* RAS Features */ +#define CPUID_8000_0007_EBX_OVERFLOW_RECOV (1U << 0) +#define CPUID_8000_0007_EBX_SUCCOR (1U << 1) + /* CLZERO instruction */ #define CPUID_8000_0008_EBX_CLZERO (1U << 0) /* Always save/restore FP error pointers */ @@ -921,8 +1025,42 @@ 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) +/* Paravirtualized Speculative Store Bypass Disable MSR */ +#define CPUID_8000_0008_EBX_VIRT_SSBD (1U << 25) +/* 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) +/* Enhanced Return Address Predictor Scurity */ +#define CPUID_8000_0021_EAX_ERAPS (1U << 24) +/* Selective Branch Predictor Barrier */ +#define CPUID_8000_0021_EAX_SBPB (1U << 27) +/* IBPB includes branch type prediction flushing */ +#define CPUID_8000_0021_EAX_IBPB_BRTYPE (1U << 28) +/* Not vulnerable to Speculative Return Stack Overflow */ +#define CPUID_8000_0021_EAX_SRSO_NO (1U << 29) +/* Not vulnerable to SRSO at the user-kernel boundary */ +#define CPUID_8000_0021_EAX_SRSO_USER_KERNEL_NO (1U << 30) + +/* + * Return Address Predictor size. RapSize x 8 is the minimum number of + * CALL instructions software needs to execute to flush the RAP. + */ +#define CPUID_8000_0021_EBX_RAPSIZE (8U << 16) + +/* Performance Monitoring Version 2 */ +#define CPUID_8000_0022_EAX_PERFMON_V2 (1U << 0) #define CPUID_XSAVE_XSAVEOPT (1U << 0) #define CPUID_XSAVE_XSAVEC (1U << 1) @@ -961,10 +1099,16 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */ /* CPUID[0xB].ECX level types */ -#define CPUID_TOPOLOGY_LEVEL_INVALID (0U << 8) -#define CPUID_TOPOLOGY_LEVEL_SMT (1U << 8) -#define CPUID_TOPOLOGY_LEVEL_CORE (2U << 8) -#define CPUID_TOPOLOGY_LEVEL_DIE (5U << 8) +#define CPUID_B_ECX_TOPO_LEVEL_INVALID 0 +#define CPUID_B_ECX_TOPO_LEVEL_SMT 1 +#define CPUID_B_ECX_TOPO_LEVEL_CORE 2 + +/* COUID[0x1F].ECX level types */ +#define CPUID_1F_ECX_TOPO_LEVEL_INVALID CPUID_B_ECX_TOPO_LEVEL_INVALID +#define CPUID_1F_ECX_TOPO_LEVEL_SMT CPUID_B_ECX_TOPO_LEVEL_SMT +#define CPUID_1F_ECX_TOPO_LEVEL_CORE CPUID_B_ECX_TOPO_LEVEL_CORE +#define CPUID_1F_ECX_TOPO_LEVEL_MODULE 3 +#define CPUID_1F_ECX_TOPO_LEVEL_DIE 5 /* MSR Feature Bits */ #define MSR_ARCH_CAP_RDCL_NO (1U << 0) @@ -976,6 +1120,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) @@ -986,6 +1135,8 @@ 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_BASIC_NESTED_EXCEPTION (1ULL << 58) #define MSR_VMX_MISC_PREEMPTION_TIMER_SHIFT_MASK 0x1Full #define MSR_VMX_MISC_STORE_LMA (1ULL << 5) @@ -1060,6 +1211,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 @@ -1080,6 +1232,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, #define VMX_VM_EXIT_PT_CONCEAL_PIP 0x01000000 #define VMX_VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 #define VMX_VM_EXIT_LOAD_IA32_PKRS 0x20000000 +#define VMX_VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 #define VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS 0x00000004 #define VMX_VM_ENTRY_IA32E_MODE 0x00000200 @@ -1155,6 +1308,8 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, /* Use a clearer name for this. */ #define CPU_INTERRUPT_INIT CPU_INTERRUPT_RESET +#define CC_OP_HAS_EFLAGS(op) ((op) >= CC_OP_EFLAGS && (op) <= CC_OP_ADCOX) + /* Instead of computing the condition codes after each x86 instruction, * QEMU just stores one operand (called CC_SRC), the result * (called CC_DST) and the type of operation (called CC_OP). When the @@ -1163,10 +1318,14 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, * are only needed for conditional branches. */ typedef enum { - CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ - CC_OP_EFLAGS, /* all cc are explicitly computed, CC_SRC = flags */ + CC_OP_EFLAGS = 0, /* all cc are explicitly computed, CC_SRC = flags */ + CC_OP_ADCX = 1, /* CC_DST = C, CC_SRC = rest. */ + CC_OP_ADOX = 2, /* CC_SRC2 = O, CC_SRC = rest. */ + CC_OP_ADCOX = 3, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest. */ - CC_OP_MULB, /* modify all flags, C, O = (CC_SRC != 0) */ + /* Low 2 bits = MemOp constant for the size */ +#define CC_OP_FIRST_BWLQ CC_OP_MULB + CC_OP_MULB = 4, /* modify all flags, C, O = (CC_SRC != 0) */ CC_OP_MULW, CC_OP_MULL, CC_OP_MULQ, @@ -1221,16 +1380,40 @@ typedef enum { CC_OP_BMILGL, CC_OP_BMILGQ, - CC_OP_ADCX, /* CC_DST = C, CC_SRC = rest. */ - CC_OP_ADOX, /* CC_DST = O, CC_SRC = rest. */ - CC_OP_ADCOX, /* CC_DST = C, CC_SRC2 = O, CC_SRC = rest. */ + CC_OP_BLSIB, /* Z,S via CC_DST, C = SRC!=0; O=0; P,A undefined */ + CC_OP_BLSIW, + CC_OP_BLSIL, + CC_OP_BLSIQ, - CC_OP_CLR, /* Z set, all other flags clear. */ - CC_OP_POPCNT, /* Z via CC_SRC, all other flags clear. */ + /* + * Note that only CC_OP_POPCNT (i.e. the one with MO_TL size) + * is used or implemented, because the translation needs + * to zero-extend CC_DST anyway. + */ + CC_OP_POPCNTB__, /* Z via CC_DST, all other flags clear. */ + CC_OP_POPCNTW__, + CC_OP_POPCNTL__, + CC_OP_POPCNTQ__, + CC_OP_POPCNT = sizeof(target_ulong) == 8 ? CC_OP_POPCNTQ__ : CC_OP_POPCNTL__, +#define CC_OP_LAST_BWLQ CC_OP_POPCNTQ__ - CC_OP_NB, + CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ } CCOp; +/* See X86DecodedInsn.cc_op, using int8_t. */ +QEMU_BUILD_BUG_ON(CC_OP_DYNAMIC > INT8_MAX); + +static inline MemOp cc_op_size(CCOp op) +{ + MemOp size = op & 3; + + QEMU_BUILD_BUG_ON(CC_OP_FIRST_BWLQ & 3); + assert(op >= CC_OP_FIRST_BWLQ && op <= CC_OP_LAST_BWLQ); + assert(size <= MO_TL); + + return size; +} + typedef struct SegmentCache { uint32_t selector; target_ulong base; @@ -1356,23 +1539,34 @@ typedef struct { */ #define UNASSIGNED_APIC_ID 0xFFFFFFFF -typedef union X86LegacyXSaveArea { - struct { - uint16_t fcw; - uint16_t fsw; - uint8_t ftw; - uint8_t reserved; - uint16_t fpop; - uint64_t fpip; - uint64_t fpdp; - uint32_t mxcsr; - uint32_t mxcsr_mask; - FPReg fpregs[8]; - uint8_t xmm_regs[16][16]; +typedef struct X86LegacyXSaveArea { + uint16_t fcw; + uint16_t fsw; + uint8_t ftw; + uint8_t reserved; + uint16_t fpop; + union { + struct { + uint64_t fpip; + uint64_t fpdp; + }; + struct { + uint32_t fip; + uint32_t fcs; + uint32_t foo; + uint32_t fos; + }; }; - uint8_t data[512]; + uint32_t mxcsr; + uint32_t mxcsr_mask; + FPReg fpregs[8]; + uint8_t xmm_regs[16][16]; + uint32_t hw_reserved[12]; + uint32_t sw_reserved[12]; } X86LegacyXSaveArea; +QEMU_BUILD_BUG_ON(sizeof(X86LegacyXSaveArea) != 512); + typedef struct X86XSaveHeader { uint64_t xstate_bv; uint64_t xcomp_bv; @@ -1520,6 +1714,13 @@ typedef struct CPUCacheInfo { * address bits. CPUID[4].EDX[bit 2]. */ bool complex_indexing; + + /* + * Cache Topology. The level that cache is shared in. + * Used to encode CPUID[4].EAX[bits 25:14] or + * CPUID[0x8000001D].EAX[bits 25:14]. + */ + CpuTopologyLevel share_level; } CPUCacheInfo; @@ -1617,6 +1818,17 @@ typedef struct CPUArchState { target_ulong cstar; target_ulong fmask; target_ulong kernelgsbase; + + /* FRED MSRs */ + uint64_t fred_rsp0; + uint64_t fred_rsp1; + uint64_t fred_rsp2; + uint64_t fred_rsp3; + uint64_t fred_stklvls; + uint64_t fred_ssp1; + uint64_t fred_ssp2; + uint64_t fred_ssp3; + uint64_t fred_config; #endif uint64_t tsc_adjust; @@ -1702,6 +1914,9 @@ typedef struct CPUArchState { uint64_t msr_lbr_depth; LBREntry lbr_records[ARCH_LBR_NR_ENTRIES]; + /* AMD MSRC001_0015 Hardware Configuration */ + uint64_t msr_hwcr; + /* exception/interrupt handling */ int error_code; int exception_is_int; @@ -1732,6 +1947,10 @@ typedef struct CPUArchState { uintptr_t retaddr; + /* RAPL MSR */ + uint64_t msr_rapl_power_unit; + uint64_t msr_pkg_energy_status; + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -1753,6 +1972,8 @@ typedef struct CPUArchState { uint32_t cpuid_vendor3; uint32_t cpuid_version; FeatureWordArray features; + /* AVX10 version */ + uint8_t avx10_version; /* Features that were explicitly enabled/disabled */ FeatureWordArray user_features; uint32_t cpuid_model[12]; @@ -1791,6 +2012,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; @@ -1813,7 +2048,14 @@ typedef struct CPUArchState { TPRAccess tpr_access_type; + /* Number of dies within this CPU package. */ unsigned nr_dies; + + /* Number of modules within one die. */ + unsigned nr_modules; + + /* Bitmap of available CPU topology levels for this CPU. */ + DECLARE_BITMAP(avail_cpu_topo, CPU_TOPOLOGY_LEVEL__MAX); } CPUX86State; struct kvm_msrs; @@ -1828,11 +2070,8 @@ struct kvm_msrs; * An x86 CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUX86State env; VMChangeStateEntry *vmsentry; @@ -1917,11 +2156,22 @@ struct ArchCPU { */ bool enable_l3_cache; + /* Compatibility bits for old machine types. + * If true present L1 cache as per-thread, not per-core. + */ + bool l1_cache_per_core; + /* Compatibility bits for old machine types. * If true present the old cache topology information */ bool legacy_cache; + /* Compatibility bits for old machine types. + * If true decode the CPUID Function 0x8000001E_ECX to support multiple + * nodes per processor + */ + bool legacy_multi_node; + /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; @@ -1931,6 +2181,9 @@ struct ArchCPU { /* Only advertise CPUID leaves defined by the vendor */ bool vendor_cpuid_only; + /* Only advertise TOPOEXT features that AMD defines */ + bool amd_topoext_features_only; + /* Enable auto level-increase for Intel Processor Trace leave */ bool intel_pt_auto_level; @@ -1943,15 +2196,20 @@ struct ArchCPU { /* if set, limit maximum value for phys_bits when host_phys_bits is true */ uint8_t host_phys_bits_limit; - /* Stop SMI delivery for migration compatibility with old machines */ - bool kvm_no_smi_migration; - /* Forcefully disable KVM PV features not exposed in guest CPUIDs */ bool kvm_pv_enforce_cpuid; /* Number of physical address bits supported */ uint32_t phys_bits; + /* + * Number of guest physical address bits available. Usually this is + * identical to host physical address bits. With NPT or EPT 4-level + * paging, guest physical address space might be restricted to 48 bits + * even if the host cpu supports more physical address bits. + */ + uint32_t guest_phys_bits; + /* in order to simplify APIC support, we leave this pointer to the user */ struct DeviceState *apic_state; @@ -1963,12 +2221,53 @@ struct ArchCPU { int32_t node_id; /* NUMA node this CPU belongs to */ int32_t socket_id; int32_t die_id; + int32_t module_id; int32_t core_id; 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; @@ -1985,24 +2284,24 @@ int x86_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, int x86_cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, 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); +void x86_cpu_gdb_init(CPUState *cs); 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); @@ -2102,15 +2401,17 @@ int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, /* used for debug or cpu save/restore */ /* cpu-exec.c */ -/* the following helpers are only usable in user mode simulation as - they can trigger unexpected exceptions */ +/* + * The following helpers are only usable in user mode simulation. + * The host pointers should come from lock_user(). + */ void cpu_x86_load_seg(CPUX86State *s, X86Seg seg_reg, int selector); -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); +void cpu_x86_fsave(CPUX86State *s, void *host, size_t len); +void cpu_x86_frstor(CPUX86State *s, void *host, size_t len); +void cpu_x86_fxsave(CPUX86State *s, void *host, size_t len); +void cpu_x86_fxrstor(CPUX86State *s, void *host, size_t len); +void cpu_x86_xsave(CPUX86State *s, void *host, size_t len, uint64_t rbfm); +bool cpu_x86_xrstor(CPUX86State *s, void *host, size_t len, uint64_t rbfm); /* cpu.c */ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, @@ -2129,8 +2430,10 @@ 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); @@ -2172,8 +2475,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 @@ -2185,26 +2486,42 @@ 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 -#define MMU_NESTED_IDX 3 -#define MMU_PHYS_IDX 4 +#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 -static inline int cpu_mmu_index(CPUX86State *env, bool ifetch) +#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 int cpu_mmu_index_kernel(CPUX86State *env) +static inline bool is_mmu_index_user(int mmu_index) { - 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; + 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; +} + +int x86_mmu_index_pl(CPUX86State *env, unsigned pl); +int cpu_mmu_index_kernel(CPUX86State *env); + #define CC_DST (env->cc_dst) #define CC_SRC (env->cc_src) #define CC_SRC2 (env->cc_src2) @@ -2217,17 +2534,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 @@ -2236,13 +2557,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; } @@ -2297,6 +2618,9 @@ static inline bool cpu_vmx_maybe_enabled(CPUX86State *env) int get_pg_mode(CPUX86State *env); /* fpu_helper.c */ + +/* Set all non-runtime-variable float_status fields to x86 handling */ +void cpu_init_fp_statuses(CPUX86State *env); void update_fp_status(CPUX86State *env); void update_mxcsr_status(CPUX86State *env); void update_mxcsr_from_sse_status(CPUX86State *env); @@ -2324,9 +2648,6 @@ static inline void cpu_set_fpuc(CPUX86State *env, uint16_t fpuc) env->hflags |= ((env->fpuc >> 9) & 1) << HF_FPU_PC_SHIFT; } -/* mem_helper.c */ -void helper_lock_init(void); - /* svm_helper.c */ #ifdef CONFIG_USER_ONLY static inline void @@ -2369,12 +2690,18 @@ 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); @@ -2415,6 +2742,12 @@ static inline uint64_t cr4_reserved_bits(CPUX86State *env) if (!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS)) { reserved_bits |= CR4_PKS_MASK; } + if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { + reserved_bits |= CR4_LAM_SUP_MASK; + } + if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED)) { + reserved_bits |= CR4_FRED_MASK; + } return reserved_bits; } diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index c3a2cf6f28..04c49e802d 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -18,8 +18,13 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "accel/tcg/vcpu-state.h" #include "cpu.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#ifdef CONFIG_LINUX_USER +#include "linux-user/qemu.h" +#endif #ifdef TARGET_X86_64 static const int gpr_map[16] = { @@ -96,6 +101,19 @@ static int gdb_write_reg_cs64(uint32_t hflags, uint8_t *buf, target_ulong *val) return 4; } +static int gdb_get_reg(CPUX86State *env, GByteArray *mem_buf, target_ulong val) +{ + if (TARGET_LONG_BITS == 64) { + if (env->hflags & HF_CS64_MASK) { + return gdb_get_reg64(mem_buf, val); + } else { + return gdb_get_reg64(mem_buf, val & 0xffffffffUL); + } + } else { + return gdb_get_reg32(mem_buf, val); + } +} + int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { X86CPU *cpu = X86_CPU(cs); @@ -121,7 +139,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; @@ -135,15 +155,7 @@ int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) } else { switch (n) { case IDX_IP_REG: - if (TARGET_LONG_BITS == 64) { - if (env->hflags & HF_CS64_MASK) { - return gdb_get_reg64(mem_buf, env->eip); - } else { - return gdb_get_reg64(mem_buf, env->eip & 0xffffffffUL); - } - } else { - return gdb_get_reg32(mem_buf, env->eip); - } + return gdb_get_reg(env, mem_buf, env->eip); case IDX_FLAGS_REG: return gdb_get_reg32(mem_buf, env->eflags); @@ -246,6 +258,21 @@ static int x86_cpu_gdb_load_seg(X86CPU *cpu, X86Seg sreg, uint8_t *mem_buf) return 4; } +static int gdb_write_reg(CPUX86State *env, uint8_t *mem_buf, target_ulong *val) +{ + if (TARGET_LONG_BITS == 64) { + if (env->hflags & HF_CS64_MASK) { + *val = ldq_p(mem_buf); + } else { + *val = ldq_p(mem_buf) & 0xffffffffUL; + } + return 8; + } else { + *val = (uint32_t)ldl_p(mem_buf); + return 4; + } +} + int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { X86CPU *cpu = X86_CPU(cs); @@ -286,18 +313,7 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } else { switch (n) { case IDX_IP_REG: - if (TARGET_LONG_BITS == 64) { - if (env->hflags & HF_CS64_MASK) { - env->eip = ldq_p(mem_buf); - } else { - env->eip = ldq_p(mem_buf) & 0xffffffffUL; - } - return 8; - } else { - env->eip &= ~0xffffffffUL; - env->eip |= (uint32_t)ldl_p(mem_buf); - return 4; - } + return gdb_write_reg(env, mem_buf, &env->eip); case IDX_FLAGS_REG: env->eflags = ldl_p(mem_buf); return 4; @@ -395,3 +411,49 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* Unrecognised register. */ return 0; } + +#ifdef CONFIG_LINUX_USER + +#define IDX_ORIG_AX 0 + +static int x86_cpu_gdb_read_linux_register(CPUState *cs, GByteArray *mem_buf, + int n) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + switch (n) { + case IDX_ORIG_AX: + return gdb_get_reg(env, mem_buf, get_task_state(cs)->orig_ax); + } + return 0; +} + +static int x86_cpu_gdb_write_linux_register(CPUState *cs, uint8_t *mem_buf, + int n) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + switch (n) { + case IDX_ORIG_AX: + return gdb_write_reg(env, mem_buf, &get_task_state(cs)->orig_ax); + } + return 0; +} + +#endif + +void x86_cpu_gdb_init(CPUState *cs) +{ +#ifdef CONFIG_LINUX_USER + gdb_register_coprocessor(cs, x86_cpu_gdb_read_linux_register, + x86_cpu_gdb_write_linux_register, +#ifdef TARGET_X86_64 + gdb_find_static_feature("i386-64bit-linux.xml"), +#else + gdb_find_static_feature("i386-32bit-linux.xml"), +#endif + 0); +#endif +} 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 c6ddaa8acd..0000000000 --- a/target/i386/hax/hax-mem.c +++ /dev/null @@ -1,339 +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; - } - -#ifndef XBOX - /* FIXME: Marking all pages as always dirty has some really bad consequences - * for the current NV2A emulation. The lesser of two evils for now is to - * leave the pages as-is. Eventually this will be replaced by proper dirty - * page tracking once HAXM supports it. - */ - memory_region_set_dirty(mr, 0, int128_get64(section->size)); -#endif -} - -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(); - } -} - -#ifdef XBOX -static void hax_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size) -{ -} -#endif - -static struct RAMBlockNotifier hax_ram_notifier = { - .ram_block_added = hax_ram_block_added, -#ifdef XBOX - .ram_block_removed = hax_ram_block_removed, -#endif -}; - -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 0ac2da066d..01a268a30b 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -22,12 +22,15 @@ #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) { @@ -88,6 +91,10 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env) int family = 0; int model = 0; + if (IS_AMD_CPU(env)) { + return 0; + } + cpu_x86_version(env, &family, &model); if ((family == 6 && model >= 14) || family > 6) { return 1; @@ -216,6 +223,10 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) new_cr4 &= ~CR4_PKS_MASK; } + if (!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_LAM)) { + new_cr4 &= ~CR4_LAM_SUP_MASK; + } + env->cr[4] = new_cr4; env->hflags = hflags; @@ -427,7 +438,7 @@ static void do_inject_x86_mce(CPUState *cs, run_on_cpu_data data) if (need_reset) { emit_guest_memory_failure(MEMORY_FAILURE_ACTION_RESET, ar, recursive); - monitor_puts(params->mon, msg); + monitor_printf(params->mon, "%s", msg); qemu_log_mask(CPU_LOG_RESET, "%s\n", msg); qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; @@ -520,7 +531,7 @@ static inline target_ulong get_memio_eip(CPUX86State *env) } /* Per x86_restore_state_to_opc. */ - if (TARGET_TB_PCREL) { + if (tcg_cflags_has(cs, CF_PCREL)) { return (env->eip & TARGET_PAGE_MASK) | data[0]; } else { return data[0] - env->segs[R_CS].base; @@ -577,9 +588,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); @@ -598,22 +609,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 2dd9b3ee37..dde92f246d 100644 --- a/target/i386/helper.h +++ b/target/i386/helper.h @@ -1,5 +1,6 @@ DEF_HELPER_FLAGS_4(cc_compute_all, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) DEF_HELPER_FLAGS_4(cc_compute_c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl, int) +DEF_HELPER_FLAGS_3(cc_compute_nz, TCG_CALL_NO_RWG_SE, tl, tl, tl, int) DEF_HELPER_3(write_eflags, void, env, tl, i32) DEF_HELPER_1(read_eflags, tl, env) @@ -22,8 +23,8 @@ DEF_HELPER_FLAGS_5(bndstx32, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) DEF_HELPER_FLAGS_5(bndstx64, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) DEF_HELPER_1(bnd_jmp, void, env) -DEF_HELPER_2(aam, void, env, int) -DEF_HELPER_2(aad, void, env, int) +DEF_HELPER_FLAGS_2(aam, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(aad, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_1(aaa, void, env) DEF_HELPER_1(aas, void, env) DEF_HELPER_1(daa, void, env) @@ -51,13 +52,12 @@ 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_FLAGS_1(pause, TCG_CALL_NO_WG, noreturn, 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_FLAGS_1(icebp, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_3(boundw, void, env, tl, int) DEF_HELPER_3(boundl, void, env, tl, int) @@ -66,17 +66,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 @@ -97,12 +91,12 @@ DEF_HELPER_2(vmsave, void, env, int) DEF_HELPER_1(stgi, void, env) DEF_HELPER_1(clgi, void, env) DEF_HELPER_FLAGS_2(flush_page, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_2(hlt, TCG_CALL_NO_WG, noreturn, env, int) +DEF_HELPER_FLAGS_1(hlt, TCG_CALL_NO_WG, noreturn, env) DEF_HELPER_FLAGS_2(monitor, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(mwait, TCG_CALL_NO_WG, noreturn, env, int) DEF_HELPER_1(rdmsr, void, env) DEF_HELPER_1(wrmsr, void, env) -DEF_HELPER_FLAGS_2(read_crN, TCG_CALL_NO_RWG, tl, env, int) +DEF_HELPER_FLAGS_1(read_cr8, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_3(write_crN, TCG_CALL_NO_RWG, void, env, int, tl) #endif /* !CONFIG_USER_ONLY */ @@ -232,21 +226,10 @@ DEF_HELPER_1(enter_mmx, void, env) DEF_HELPER_1(emms, void, env) #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 "ops_sse_header.h" - -DEF_HELPER_3(rclb, tl, env, tl, tl) -DEF_HELPER_3(rclw, tl, env, tl, tl) -DEF_HELPER_3(rcll, tl, env, tl, tl) -DEF_HELPER_3(rcrb, tl, env, tl, tl) -DEF_HELPER_3(rcrw, tl, env, tl, tl) -DEF_HELPER_3(rcrl, tl, env, tl, tl) -#ifdef TARGET_X86_64 -DEF_HELPER_3(rclq, tl, env, tl, tl) -DEF_HELPER_3(rcrq, tl, env, tl, tl) -#endif +#include "tcg/ops_sse_header.h.inc" DEF_HELPER_1(rdrand, tl, env) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 10f8aba86e..03b9d1b169 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 */ @@ -41,31 +42,19 @@ static uint32_t host_cpu_phys_bits(void) return host_phys_bits; } -static void host_cpu_enable_cpu_pm(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - - host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, - &cpu->mwait.ecx, &cpu->mwait.edx); - env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; -} - -static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu) +static void host_cpu_adjust_phys_bits(X86CPU *cpu) { uint32_t host_phys_bits = host_cpu_phys_bits(); uint32_t phys_bits = cpu->phys_bits; - static bool warned; /* * Print a warning if the user set it to a value that's not the * host value. */ - if (phys_bits != host_phys_bits && phys_bits != 0 && - !warned) { - warn_report("Host physical bits (%u)" - " does not match phys-bits property (%u)", - host_phys_bits, phys_bits); - warned = true; + if (phys_bits != host_phys_bits && phys_bits != 0) { + warn_report_once("Host physical bits (%u)" + " does not match phys-bits property (%u)", + host_phys_bits, phys_bits); } if (cpu->host_phys_bits) { @@ -77,7 +66,7 @@ static uint32_t host_cpu_adjust_phys_bits(X86CPU *cpu) } } - return phys_bits; + cpu->phys_bits = phys_bits; } bool host_cpu_realizefn(CPUState *cs, Error **errp) @@ -85,21 +74,8 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (cpu->max_features && enable_cpu_pm) { - host_cpu_enable_cpu_pm(cpu); - } if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { - uint32_t phys_bits = host_cpu_adjust_phys_bits(cpu); - - if (phys_bits && - (phys_bits > TARGET_PHYS_ADDR_SPACE_BITS || - phys_bits < 32)) { - error_setg(errp, "phys-bits should be between 32 and %u " - " (but is %u)", - TARGET_PHYS_ADDR_SPACE_BITS, phys_bits); - return false; - } - cpu->phys_bits = phys_bits; + host_cpu_adjust_phys_bits(cpu); } return true; } 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 8d2248bb3f..c5d025d557 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -49,6 +49,8 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/memalign.h" +#include "qapi/error.h" +#include "migration/blocker.h" #include "sysemu/hvf.h" #include "sysemu/hvf_int.h" @@ -74,6 +76,8 @@ #include "qemu/accel.h" #include "target/i386/cpu.h" +static Error *invtsc_mig_blocker; + void vmx_update_tpr(CPUState *cpu) { /* TODO: need integrate APIC handling */ @@ -81,11 +85,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 +97,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); } @@ -131,9 +135,10 @@ static bool ept_emulation_fault(hvf_slot *slot, uint64_t gpa, uint64_t ept_qual) if (write && slot) { if (slot->flags & HVF_SLOT_LOG) { + uint64_t dirty_page_start = gpa & ~(TARGET_PAGE_SIZE - 1u); memory_region_set_dirty(slot->region, gpa - slot->start, 1); - hv_vm_protect((hv_gpaddr_t)slot->start, (size_t)slot->size, - HV_MEMORY_READ | HV_MEMORY_WRITE); + hv_vm_protect(dirty_page_start, TARGET_PAGE_SIZE, + HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC); } } @@ -210,6 +215,7 @@ static inline bool apic_bus_freq_is_known(CPUX86State *env) void hvf_kick_vcpu_thread(CPUState *cpu) { cpus_kick_thread(cpu); + hv_vcpu_interrupt(&cpu->accel->fd, 1); } int hvf_arch_init(void) @@ -217,16 +223,25 @@ int hvf_arch_init(void) return 0; } +hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) +{ + return hv_vm_create(HV_VM_DEFAULT); +} + int hvf_arch_init_vcpu(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); CPUX86State *env = &x86cpu->env; + Error *local_err = NULL; + int r; uint64_t reqCap; init_emu(); init_decoder(); - hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1); + if (hvf_state->hvf_caps == NULL) { + hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1); + } env->hvf_mmio_buf = g_new(char, 4096); if (x86cpu->vmware_cpuid_freq) { @@ -238,6 +253,18 @@ int hvf_arch_init_vcpu(CPUState *cpu) } } + if ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC) && + invtsc_mig_blocker == NULL) { + error_setg(&invtsc_mig_blocker, + "State blocked by non-migratable CPU device (invtsc flag)"); + r = migrate_add_blocker(&invtsc_mig_blocker, &local_err); + if (r < 0) { + error_report_err(local_err); + return r; + } + } + + if (hv_vmx_read_capability(HV_VMX_CAP_PINBASED, &hvf_state->hvf_caps->vmx_cap_pinbased)) { abort(); @@ -256,12 +283,12 @@ 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, + 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 | @@ -276,14 +303,14 @@ int hvf_arch_init_vcpu(CPUState *cpu) reqCap |= VMCS_PRI_PROC_BASED2_CTLS_RDTSCP; } - wvmcs(cpu->hvf->fd, VMCS_SEC_PROC_BASED_CTLS, + wvmcs(cpu->accel->fd, VMCS_SEC_PROC_BASED_CTLS, cap2ctrl(hvf_state->hvf_caps->vmx_cap_procbased2, reqCap)); - 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 */ + 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->hvf->fd, VMCS_TPR_THRESHOLD, 0); + wvmcs(cpu->accel->fd, VMCS_TPR_THRESHOLD, 0); x86cpu = X86_CPU(cpu); x86cpu->env.xsave_buf_len = 4096; @@ -295,18 +322,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; } @@ -347,16 +374,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; @@ -419,9 +446,9 @@ int hvf_vcpu_exec(CPUState *cpu) } do { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { hvf_put_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } if (hvf_inject_interrupts(cpu)) { @@ -429,28 +456,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_until(cpu->accel->fd, HV_DEADLINE_FOREVER); 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; @@ -478,7 +505,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)) { @@ -523,7 +550,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; @@ -539,38 +566,36 @@ 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; } 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; } @@ -591,9 +616,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); @@ -609,15 +634,14 @@ 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: { - X86CPU *x86_cpu = X86_CPU(cpu); if (exit_qual & 0x10) { RRX(env, reg) = cpu_get_apic_tpr(x86_cpu->apic_state); } else { @@ -649,7 +673,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 @@ -662,8 +686,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: @@ -679,3 +703,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/vmx.h b/target/i386/hvf/vmx.h index fcd9a95e5b..3954ef883d 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -95,8 +95,7 @@ static void enter_long_mode(hv_vcpuid_t vcpu, uint64_t cr0, uint64_t efer) efer |= MSR_EFER_LMA; wvmcs(vcpu, VMCS_GUEST_IA32_EFER, efer); entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS); - wvmcs(vcpu, VMCS_ENTRY_CTLS, rvmcs(vcpu, VMCS_ENTRY_CTLS) | - VM_ENTRY_GUEST_LMA); + wvmcs(vcpu, VMCS_ENTRY_CTLS, entry_ctls | VM_ENTRY_GUEST_LMA); uint64_t guest_tr_ar = rvmcs(vcpu, VMCS_GUEST_TR_ACCESS_RIGHTS); if ((efer & MSR_EFER_LME) && @@ -180,15 +179,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 +199,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 +210,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 +228,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 7323a7a94b..af9ee17a11 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -21,27 +21,38 @@ */ #include "qemu/osdep.h" +#include "qemu/cpuid.h" +#include "host/cpuinfo.h" #include "cpu.h" #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) +static bool cached_xcr0; +static uint64_t supported_xcr0; + +static void cache_host_xcr0(void) { - uint32_t xcrl, xcrh; - - if (cpuid_ecx & CPUID_EXT_OSXSAVE) { - /* - * 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" (idx)); - - *xcr = (((uint64_t)xcrh) << 32) | xcrl; - return true; + if (cached_xcr0) { + return; } - return false; + if (cpuinfo & CPUINFO_OSXSAVE) { + uint64_t host_xcr0 = xgetbv_low(0); + + /* Only show xcr0 bits corresponding to usable features. */ + supported_xcr0 = host_xcr0 & (XSTATE_FP_MASK | + XSTATE_SSE_MASK | XSTATE_YMM_MASK | + XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | + XSTATE_Hi16_ZMM_MASK); + if ((supported_xcr0 & (XSTATE_FP_MASK | XSTATE_SSE_MASK)) != + (XSTATE_FP_MASK | XSTATE_SSE_MASK)) { + supported_xcr0 = 0; + } + } + + cached_xcr0 = true; } uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, @@ -50,6 +61,7 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, uint64_t cap; uint32_t eax, ebx, ecx, edx; + cache_host_xcr0(); host_cpuid(func, idx, &eax, &ebx, &ecx, &edx); switch (func) { @@ -65,7 +77,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, 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_MOVBE | - CPUID_EXT_POPCNT | CPUID_EXT_AES | CPUID_EXT_XSAVE | + CPUID_EXT_POPCNT | CPUID_EXT_AES | CPUID_EXT_X2APIC | + (supported_xcr0 ? CPUID_EXT_XSAVE : 0) | CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND; ecx |= CPUID_EXT_HYPERVISOR; break; @@ -106,16 +119,14 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, eax = 0; break; case 0xD: + if (!supported_xcr0 || idx >= 63 || + (idx > 1 && !(supported_xcr0 & (UINT64_C(1) << idx)))) { + eax = ebx = ecx = edx = 0; + break; + } + if (idx == 0) { - uint64_t host_xcr0; - if (xgetbv(ecx, 0, &host_xcr0)) { - uint64_t supp_xcr0 = host_xcr0 & (XSTATE_FP_MASK | - XSTATE_SSE_MASK | XSTATE_YMM_MASK | - XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK | - XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | - XSTATE_Hi16_ZMM_MASK); - eax &= supp_xcr0; - } + eax = supported_xcr0; } else if (idx == 1) { hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap); eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1; @@ -145,6 +156,10 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_OSVW | CPUID_EXT3_XOP | CPUID_EXT3_FMA4 | CPUID_EXT3_TBM; break; + case 0x80000007: + edx &= CPUID_APM_INVTSC; + eax = ebx = ecx = 0; + break; default: return 0; } diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index 5cc5cb29ad..a4a28f113f 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -1684,10 +1684,10 @@ calc_addr: } } -uintptr_t get_reg_ref(CPUX86State *env, int reg, int rex_present, +target_ulong get_reg_ref(CPUX86State *env, int reg, int rex_present, int is_extended, int size) { - uintptr_t ptr = 0; + target_ulong ptr = 0; if (is_extended) { reg |= R_R8; @@ -1696,13 +1696,13 @@ uintptr_t get_reg_ref(CPUX86State *env, int reg, int rex_present, switch (size) { case 1: if (is_extended || reg < 4 || rex_present) { - ptr = (uintptr_t)&RL(env, reg); + ptr = (target_ulong)&RL(env, reg); } else { - ptr = (uintptr_t)&RH(env, reg - 4); + ptr = (target_ulong)&RH(env, reg - 4); } break; default: - ptr = (uintptr_t)&RRX(env, reg); + ptr = (target_ulong)&RRX(env, reg); break; } return ptr; @@ -2111,7 +2111,7 @@ uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode) return decode->len; } -void init_decoder() +void init_decoder(void) { int i; diff --git a/target/i386/hvf/x86_decode.h b/target/i386/hvf/x86_decode.h index bddcf92fd5..a2d7a2a27b 100644 --- a/target/i386/hvf/x86_decode.h +++ b/target/i386/hvf/x86_decode.h @@ -266,7 +266,7 @@ typedef struct x86_decode_op { int reg; target_ulong val; - uintptr_t ptr; + target_ulong ptr; } x86_decode_op; typedef struct x86_decode { @@ -303,7 +303,7 @@ uint64_t sign(uint64_t val, int size); uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode); -uintptr_t get_reg_ref(CPUX86State *env, int reg, int rex_present, +target_ulong get_reg_ref(CPUX86State *env, int reg, int rex_present, int is_extended, int size); target_ulong get_reg_val(CPUX86State *env, int reg, int rex_present, int is_extended, int size); 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..015f760acb 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,54 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -void simulate_rdmsr(struct CPUState *cpu) +static void raise_exception(CPUX86State *env, int exception_index, + int error_code) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUX86State *env = &x86_cpu->env; + env->exception_nr = exception_index; + env->error_code = error_code; + env->has_error_code = true; + env->exception_injected = 1; +} + +void simulate_rdmsr(CPUX86State *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_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_read(index, &val); + if (ret < 0) { + raise_exception(env, EXCP0D_GPF, 0); + } + + 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 +765,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,31 +780,49 @@ 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); switch (msr) { case MSR_IA32_TSC: break; - case MSR_IA32_APICBASE: - cpu_set_apic_base(X86_CPU(cpu)->apic_state, data); + case MSR_IA32_APICBASE: { + int r; + + r = cpu_set_apic_base(cpu->apic_state, data); + if (r < 0) { + raise_exception(env, EXCP0D_GPF, 0); + } + break; + } + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_write(index, data); + if (ret < 0) { + raise_exception(env, EXCP0D_GPF, 0); + } + + 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 +834,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 +886,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; } @@ -1410,7 +1447,7 @@ static struct cmd_handler { static struct cmd_handler _cmd_handler[X86_DECODE_CMD_LAST]; -static void init_cmd_handler() +static void init_cmd_handler(void) { int i; for (i = 0; i < ARRAY_SIZE(handlers); i++) { @@ -1418,56 +1455,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) { @@ -1482,7 +1519,7 @@ bool exec_instruction(CPUX86State *env, struct x86_decode *ins) return true; } -void init_emu() +void init_emu(void) { init_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 06f4bcaed8..579d0c3a4c 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -38,6 +38,7 @@ #define LEGACY_PTE_PAGE_MASK (0xffffffffllu << 12) #define PAE_PTE_PAGE_MASK ((-1llu << 12) & ((1llu << 52) - 1)) #define PAE_PTE_LARGE_PAGE_MASK ((-1llu << (21)) & ((1llu << 52) - 1)) +#define PAE_PTE_SUPER_PAGE_MASK ((-1llu << (30)) & ((1llu << 52) - 1)) struct gpt_translation { target_ulong gva; @@ -49,7 +50,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 +74,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,8 +96,8 @@ 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, - int level, bool *is_large, bool pae) +static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt, + int level, int *largeness, bool pae) { uint64_t pte = pt->pte[level]; @@ -118,15 +119,15 @@ static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt, goto exit; } - if (1 == level && pte_large_page(pte)) { + if (level && pte_large_page(pte)) { pt->err_code |= MMU_PAGE_PT; - *is_large = true; + *largeness = level; } if (!level) { 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)) { @@ -152,9 +153,18 @@ static inline uint64_t pse_pte_to_page(uint64_t pte) return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000); } -static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae) +static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae, + int largeness) { - VM_PANIC_ON(!pte_large_page(pt->pte[1])) + VM_PANIC_ON(!pte_large_page(pt->pte[largeness])) + + /* 1Gib large page */ + if (pae && largeness == 2) { + return (pt->pte[2] & PAE_PTE_SUPER_PAGE_MASK) | (pt->gva & 0x3fffffff); + } + + VM_PANIC_ON(largeness != 1) + /* 2Mb large page */ if (pae) { return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff); @@ -166,12 +176,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); + int largeness = 0; + 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)); @@ -186,26 +196,26 @@ static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code, for (level = top_level; level > 0; level--) { get_pt_entry(cpu, pt, level, pae); - if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) { + if (!test_pt_entry(cpu, pt, level - 1, &largeness, pae)) { return false; } - if (is_large) { + if (largeness) { break; } } - if (!is_large) { + if (!largeness) { pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff); } else { - pt->gpa = large_page_gpa(pt, pae); + pt->gpa = large_page_gpa(pt, pae, largeness); } return true; } -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 +235,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; @@ -234,7 +244,7 @@ void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { - VM_PANIC_EX("%s: mmu_gva_to_gpa 0x" TARGET_FMT_lx " failed\n", __func__, gva); + VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); } else { address_space_write(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, data, copy); @@ -246,7 +256,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; @@ -255,7 +265,7 @@ void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes) int copy = MIN(bytes, 0x1000 - (gva & 0xfff)); if (!mmu_gva_to_gpa(cpu, gva, &gpa)) { - VM_PANIC_EX("%s: mmu_gva_to_gpa 0x" TARGET_FMT_lx " failed\n", __func__, gva); + VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva); } address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED, data, copy); 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..cdea2ea69d 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; } @@ -122,7 +122,6 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea load_regs(cpu); struct x86_segment_descriptor curr_tss_desc, next_tss_desc; - int ret; x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); uint32_t desc_limit; @@ -138,7 +137,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea if (reason == TSR_IDT_GATE && gate_valid) { int dpl; - ret = x86_read_call_gate(cpu, &task_gate_desc, gate); + x86_read_call_gate(cpu, &task_gate_desc, gate); dpl = task_gate_desc.dpl; x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); @@ -167,18 +166,19 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea x86_write_segment_descriptor(cpu, &next_tss_desc, tss_sel); } - if (next_tss_desc.type & 8) - ret = task_switch_32(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); - else + if (next_tss_desc.type & 8) { + task_switch_32(cpu, tss_sel, old_tss_sel, old_tss_base, &next_tss_desc); + } else { //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..1569f860eb 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->accel->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..5836f53c23 100644 --- a/target/i386/kvm/hyperv-stub.c +++ b/target/i386/kvm/hyperv-stub.c @@ -52,3 +52,12 @@ void hyperv_x86_synic_reset(X86CPU *cpu) void hyperv_x86_synic_update(X86CPU *cpu) { } + +void hyperv_x86_set_vmbus_recommended_features_enabled(void) +{ +} + +uint64_t hyperv_syndbg_query_options(void) +{ + return 0; +} diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index e3ac978648..70b89cacf9 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -45,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) @@ -80,8 +80,9 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) * necessary because memory hierarchy is being changed */ async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL); + cpu_exit(CPU(cpu)); - return 0; + return EXCP_INTERRUPT; case KVM_EXIT_HYPERV_HCALL: { uint16_t code = exit->u.hcall.input & 0xffff; bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST; @@ -149,3 +150,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..99d1941cf5 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -10,7 +10,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "host-cpu.h" -#include "kvm-cpu.h" #include "qapi/error.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -18,10 +17,32 @@ #include "kvm_i386.h" #include "hw/core/accel-cpu.h" +static void kvm_set_guest_phys_bits(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + uint32_t eax, guest_phys_bits; + + eax = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x80000008, 0, R_EAX); + guest_phys_bits = (eax >> 16) & 0xff; + if (!guest_phys_bits) { + return; + } + cpu->guest_phys_bits = guest_phys_bits; + if (cpu->guest_phys_bits > cpu->phys_bits) { + cpu->guest_phys_bits = cpu->phys_bits; + } + + if (cpu->host_phys_bits && cpu->host_phys_bits_limit && + cpu->guest_phys_bits > cpu->host_phys_bits_limit) { + cpu->guest_phys_bits = cpu->host_phys_bits_limit; + } +} + static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + bool ret; /* * The realize order is important, since x86_cpu_realize() checks if @@ -32,16 +53,26 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * * realize order: * - * x86_cpu_realize(): - * -> x86_cpu_expand_features() - * -> cpu_exec_realizefn(): - * -> accel_cpu_realizefn() - * kvm_cpu_realizefn() -> host_cpu_realizefn() - * -> check/update ucode_rev, phys_bits, mwait + * x86_cpu_realizefn(): + * x86_cpu_expand_features() + * cpu_exec_realizefn(): + * accel_cpu_common_realize() + * kvm_cpu_realizefn() + * host_cpu_realizefn() + * kvm_set_guest_phys_bits() + * check/update ucode_rev, phys_bits, guest_phys_bits, mwait + * cpu_common_realizefn() (via xcc->parent_realize) */ if (cpu->max_features) { - if (enable_cpu_pm && kvm_has_waitpkg()) { - env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; + if (enable_cpu_pm) { + if (kvm_has_waitpkg()) { + env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; + } + + if (env->features[FEAT_1_ECX] & CPUID_EXT_MONITOR) { + host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, + &cpu->mwait.ecx, &cpu->mwait.edx); + } } if (cpu->ucode_rev == 0) { cpu->ucode_rev = @@ -49,7 +80,17 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) MSR_IA32_UCODE_REV); } } - return host_cpu_realizefn(cs, errp); + ret = host_cpu_realizefn(cs, errp); + if (!ret) { + return ret; + } + + if ((env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) && + cpu->guest_phys_bits == -1) { + kvm_set_guest_phys_bits(cs); + } + + return true; } static bool lmce_supported(void) @@ -102,10 +143,6 @@ static void kvm_cpu_xsave_init(void) if (!esa->size) { continue; } - if ((x86_cpu_get_supported_feature_word(esa->feature, false) & esa->bits) - != esa->bits) { - continue; - } host_cpuid(0xd, i, &eax, &ebx, &ecx, &edx); if (eax != 0) { assert(esa->size == eax); @@ -143,7 +180,7 @@ static PropValue kvm_default_props[] = { /* * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ -void x86_cpu_change_kvm_default(const char *prop, const char *value) +static void x86_cpu_change_kvm_default(const char *prop, const char *value) { PropValue *pv; for (pv = kvm_default_props; pv->prop; pv++) { @@ -190,7 +227,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-cpu.h b/target/i386/kvm/kvm-cpu.h deleted file mode 100644 index e858ca21e5..0000000000 --- a/target/i386/kvm/kvm-cpu.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * i386 KVM CPU type and functions - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef KVM_CPU_H -#define KVM_CPU_H - -#ifdef CONFIG_KVM -/* - * Change the value of a KVM-specific default - * - * If value is NULL, no default will be set and the original - * value from the CPU model table will be kept. - * - * It is valid to call this function only for properties that - * are already present in the kvm_default_props table. - */ -void x86_cpu_change_kvm_default(const char *prop, const char *value); - -#else /* !CONFIG_KVM */ - -#define x86_cpu_change_kvm_default(a, b) - -#endif /* CONFIG_KVM */ - -#endif /* KVM_CPU_H */ diff --git a/target/i386/kvm/kvm-stub.c b/target/i386/kvm/kvm-stub.c deleted file mode 100644 index e052f1c7b0..0000000000 --- a/target/i386/kvm/kvm-stub.c +++ /dev/null @@ -1,51 +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(); -} - -void kvm_set_max_apic_id(uint32_t max_apic_id) -{ - return; -} diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a213209379..8e17942c3b 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -16,38 +16,52 @@ #include "qapi/qapi-events-run-state.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include #include #include #include +#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" +#include "vmsr_energy.h" #include "sysemu/sysemu.h" #include "sysemu/hw_accel.h" #include "sysemu/kvm_int.h" #include "sysemu/runstate.h" #include "kvm_i386.h" +#include "../confidential-guest.h" #include "sev.h" +#include "xen-emu.h" #include "hyperv.h" #include "hyperv-proto.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.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" #include "hw/i386/intel_iommu.h" +#include "hw/i386/topology.h" #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" @@ -67,6 +81,16 @@ do { } while (0) #endif +/* + * On older Intel CPUs, KVM uses vm86 mode to emulate 16-bit code directly. + * 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, + * so this value allows up to 16M BIOSes. + */ +#define KVM_IDENTITY_BASE 0xfeffc000 + /* From arch/x86/kvm/lapic.h */ #define KVM_APIC_BUS_CYCLE_NS 1 #define KVM_APIC_BUS_FREQUENCY (1000000000ULL / KVM_APIC_BUS_CYCLE_NS) @@ -78,12 +102,31 @@ * 255 kvm_msr_entry structs */ #define MSR_BUF_SIZE 4096 +typedef bool QEMURDMSRHandler(X86CPU *cpu, uint32_t msr, uint64_t *val); +typedef bool QEMUWRMSRHandler(X86CPU *cpu, uint32_t msr, uint64_t val); +typedef struct { + uint32_t msr; + QEMURDMSRHandler *rdmsr; + QEMUWRMSRHandler *wrmsr; +} KVMMSRHandlers; + static void kvm_init_msrs(X86CPU *cpu); +static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, + QEMUWRMSRHandler *wrmsr); 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 }; @@ -122,15 +165,14 @@ static bool has_msr_ucode_rev; static bool has_msr_vmx_procbased_ctls2; static bool has_msr_perf_capabs; static bool has_msr_pkrs; +static bool has_msr_hwcr; 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; @@ -147,9 +189,57 @@ static KVMMSRHandlers msr_handlers[KVM_MSR_FILTER_MAX_RANGES]; 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) +static const char *vm_type_name[] = { + [KVM_X86_DEFAULT_VM] = "default", + [KVM_X86_SEV_VM] = "SEV", + [KVM_X86_SEV_ES_VM] = "SEV-ES", + [KVM_X86_SNP_VM] = "SEV-SNP", +}; + +bool kvm_is_vm_type_supported(int type) { - return has_pit_state2; + uint32_t machine_types; + + /* + * old KVM doesn't support KVM_CAP_VM_TYPES but KVM_X86_DEFAULT_VM + * is always supported + */ + if (type == KVM_X86_DEFAULT_VM) { + return true; + } + + machine_types = kvm_check_extension(KVM_STATE(current_machine->accelerator), + KVM_CAP_VM_TYPES); + return !!(machine_types & BIT(type)); +} + +int kvm_get_vm_type(MachineState *ms) +{ + int kvm_type = KVM_X86_DEFAULT_VM; + + if (ms->cgs) { + if (!object_dynamic_cast(OBJECT(ms->cgs), TYPE_X86_CONFIDENTIAL_GUEST)) { + error_report("configuration type %s not supported for x86 guests", + object_get_typename(OBJECT(ms->cgs))); + exit(1); + } + kvm_type = x86_confidential_guest_kvm_type( + X86_CONFIDENTIAL_GUEST(ms->cgs)); + } + + if (!kvm_is_vm_type_supported(kvm_type)) { + error_report("vm-type %s not supported by KVM", vm_type_name[kvm_type]); + exit(1); + } + + return kvm_type; +} + +bool kvm_enable_hypercall(uint64_t enable_mask) +{ + KVMState *s = KVM_STATE(current_accel()); + + return !kvm_vm_enable_cap(s, KVM_CAP_EXIT_HYPERCALL, 0, enable_mask); } bool kvm_has_smm(void) @@ -164,11 +254,6 @@ bool kvm_has_adjust_clock_stable(void) return (ret & KVM_CLOCK_TSC_STABLE); } -bool kvm_has_adjust_clock(void) -{ - return kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK); -} - bool kvm_has_exception_payload(void) { return has_exception_payload; @@ -351,7 +436,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); @@ -366,6 +451,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 @@ -398,10 +485,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 @@ -410,6 +507,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)) { /* @@ -451,6 +557,8 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, */ cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); ret |= cpuid_1_edx & CPUID_EXT2_AMD_ALIASES; + } else if (function == 0x80000007 && reg == R_EBX) { + ret |= CPUID_8000_0007_EBX_OVERFLOW_RECOV | CPUID_8000_0007_EBX_SUCCOR; } else if (function == KVM_CPUID_FEATURES && reg == R_EAX) { /* kvm_pv_unhalt is reported by GET_SUPPORTED_CPUID, but it can't * be enabled without the in-kernel irqchip @@ -465,6 +573,11 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, ret |= 1U << KVM_HINTS_REALTIME; } + if (current_machine->cgs) { + ret = x86_confidential_guest_mask_cpuid_features( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), + function, index, reg, ret); + } return ret; } @@ -549,31 +662,48 @@ 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) { CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | - MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S; - uint64_t mcg_status = MCG_STATUS_MCIP; + uint64_t status = MCI_STATUS_VAL | MCI_STATUS_EN | MCI_STATUS_MISCV | + MCI_STATUS_ADDRV; + uint64_t mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV; int flags = 0; - if (code == BUS_MCEERR_AR) { - status |= MCI_STATUS_AR | 0x134; - mcg_status |= MCG_STATUS_RIPV | MCG_STATUS_EIPV; + if (!IS_AMD_CPU(env)) { + status |= MCI_STATUS_S | MCI_STATUS_UC; + if (code == BUS_MCEERR_AR) { + status |= MCI_STATUS_AR | 0x134; + mcg_status |= MCG_STATUS_EIPV; + } else { + status |= 0xc0; + } } else { - status |= 0xc0; - mcg_status |= MCG_STATUS_RIPV; + if (code == BUS_MCEERR_AR) { + status |= MCI_STATUS_UC | MCI_STATUS_POISON; + mcg_status |= MCG_STATUS_EIPV; + } else { + /* Setting the POISON bit for deferred errors indicates to the + * guest kernel that the address provided by the MCE is valid + * and usable which will ensure that the guest kernel will send + * a SIGBUS_AO signal to the guest process. This allows for + * more desirable behavior in the case that the guest process + * with poisoned memory has set the MCE_KILL_EARLY prctl flag + * which indicates that the process would prefer to handle or + * shutdown due to the poisoned memory condition before the + * memory has been accessed. + * + * While the POISON bit would not be set in a deferred error + * sent from hardware, the bit is not meaningful for deferred + * errors and can be reused in this scenario. + */ + status |= MCI_STATUS_DEFERRED | MCI_STATUS_POISON; + } } flags = cpu_x86_support_mca_broadcast(env) ? MCE_INJECT_BROADCAST : 0; @@ -659,15 +789,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, @@ -700,38 +821,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; @@ -845,6 +934,7 @@ static struct { uint32_t bits; } flags[2]; uint64_t dependencies; + bool skip_passthrough; } kvm_hyperv_properties[] = { [HYPERV_FEAT_RELAXED] = { .desc = "relaxed timing (hv-relaxed)", @@ -967,16 +1057,15 @@ static struct { .bits = HV_DEPRECATING_AEOI_RECOMMENDED} } }, -#ifdef CONFIG_SYNDBG [HYPERV_FEAT_SYNDBG] = { .desc = "Enable synthetic kernel debugger channel (hv-syndbg)", .flags = { {.func = HV_CPUID_FEATURES, .reg = R_EDX, .bits = HV_FEATURE_DEBUG_MSRS_AVAILABLE} }, - .dependencies = BIT(HYPERV_FEAT_SYNIC) | BIT(HYPERV_FEAT_RELAXED) + .dependencies = BIT(HYPERV_FEAT_SYNIC) | BIT(HYPERV_FEAT_RELAXED), + .skip_passthrough = true, }, -#endif [HYPERV_FEAT_MSR_BITMAP] = { .desc = "enlightened MSR-Bitmap (hv-emsr-bitmap)", .flags = { @@ -1228,6 +1317,13 @@ static bool hyperv_feature_supported(CPUState *cs, int feature) uint32_t func, bits; int i, reg; + /* + * kvm_hyperv_properties needs to define at least one CPUID flag which + * must be used to detect the feature, it's hard to say whether it is + * supported or not otherwise. + */ + assert(kvm_hyperv_properties[feature].flags[0].func); + for (i = 0; i < ARRAY_SIZE(kvm_hyperv_properties[feature].flags); i++) { func = kvm_hyperv_properties[feature].flags[i].func; @@ -1377,7 +1473,8 @@ bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp) * hv_build_cpuid_leaf() uses this info to build guest CPUIDs. */ for (feat = 0; feat < ARRAY_SIZE(kvm_hyperv_properties); feat++) { - if (hyperv_feature_supported(cs, feat)) { + if (hyperv_feature_supported(cs, feat) && + !kvm_hyperv_properties[feat].skip_passthrough) { cpu->hyperv_features |= BIT(feat); } } @@ -1575,7 +1672,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; @@ -1589,7 +1686,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; @@ -1672,6 +1769,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; } @@ -1683,10 +1787,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); @@ -1723,6 +1825,235 @@ static void kvm_init_nested_state(CPUX86State *env) } } +static uint32_t kvm_x86_build_cpuid(CPUX86State *env, + struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i) +{ + uint32_t limit, i, j; + uint32_t unused; + struct kvm_cpuid_entry2 *c; + + cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); + + for (i = 0; i <= limit; i++) { + j = 0; + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + switch (i) { + case 2: { + /* Keep reading function 2 till all the input is received */ + int times; + + c->function = i; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + times = c->eax & 0xff; + if (times > 1) { + c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC | + KVM_CPUID_FLAG_STATE_READ_NEXT; + } + + for (j = 1; j < times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + c->function = i; + c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + break; + } + case 0x1f: + if (!x86_has_extended_topo(env->avail_cpu_topo)) { + cpuid_i--; + break; + } + /* fallthrough */ + case 4: + case 0xb: + case 0xd: + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (i == 4 && c->eax == 0) { + break; + } + if (i == 0xb && !(c->ecx & 0xff00)) { + break; + } + if (i == 0x1f && !(c->ecx & 0xff00)) { + break; + } + if (i == 0xd && c->eax == 0) { + if (j < 63) { + continue; + } else { + cpuid_i--; + break; + } + } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + } + break; + case 0x12: + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (j > 1 && (c->eax & 0xf) != 1) { + break; + } + + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + } + break; + case 0x7: + case 0x14: + case 0x1d: + case 0x1e: + case 0x24: { + uint32_t times; + + c->function = i; + c->index = 0; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + times = c->eax; + + for (j = 1; j <= times; ++j) { + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + c->function = i; + c->index = j; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + break; + } + default: + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + if (!c->eax && !c->ebx && !c->ecx && !c->edx) { + /* + * KVM already returns all zeroes if a CPUID entry is missing, + * so we can omit it and avoid hitting KVM's 80-entry limit. + */ + cpuid_i--; + } + break; + } + } + + if (limit >= 0x0a) { + uint32_t eax, edx; + + cpu_x86_cpuid(env, 0x0a, 0, &eax, &unused, &unused, &edx); + + has_architectural_pmu_version = eax & 0xff; + if (has_architectural_pmu_version > 0) { + num_architectural_pmu_gp_counters = (eax & 0xff00) >> 8; + + /* Shouldn't be more than 32, since that's the number of bits + * available in EBX to tell us _which_ counters are available. + * Play it safe. + */ + if (num_architectural_pmu_gp_counters > MAX_GP_COUNTERS) { + num_architectural_pmu_gp_counters = MAX_GP_COUNTERS; + } + + if (has_architectural_pmu_version > 1) { + num_architectural_pmu_fixed_counters = edx & 0x1f; + + if (num_architectural_pmu_fixed_counters > MAX_FIXED_COUNTERS) { + num_architectural_pmu_fixed_counters = MAX_FIXED_COUNTERS; + } + } + } + } + + cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); + + for (i = 0x80000000; i <= limit; i++) { + j = 0; + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + + switch (i) { + case 0x8000001d: + /* Query for all AMD cache information leaves */ + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (c->eax == 0) { + break; + } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + } + break; + default: + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + if (!c->eax && !c->ebx && !c->ecx && !c->edx) { + /* + * KVM already returns all zeroes if a CPUID entry is missing, + * so we can omit it and avoid hitting KVM's 80-entry limit. + */ + cpuid_i--; + } + break; + } + } + + /* Call Centaur's CPUID instructions they are supported. */ + if (env->cpuid_xlevel2 > 0) { + cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); + + for (i = 0xC0000000; i <= limit; i++) { + j = 0; + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + goto full; + } + c = &entries[cpuid_i++]; + + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + } + } + + return cpuid_i; + +full: + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); + abort(); +} + int kvm_arch_init_vcpu(CPUState *cs) { struct { @@ -1739,8 +2070,7 @@ int kvm_arch_init_vcpu(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - uint32_t limit, i, j, cpuid_i; - uint32_t unused; + uint32_t cpuid_i; struct kvm_cpuid_entry2 *c; uint32_t signature[3]; int kvm_base = KVM_CPUID_SIGNATURE; @@ -1799,7 +2129,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; @@ -1814,8 +2223,6 @@ int kvm_arch_init_vcpu(CPUState *cs) c->edx = env->features[FEAT_KVM_HINTS]; } - cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); - if (cpu->kvm_pv_enforce_cpuid) { r = kvm_vcpu_enable_cap(cs, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 0, 1); if (r < 0) { @@ -1826,233 +2233,12 @@ int kvm_arch_init_vcpu(CPUState *cs) } } - for (i = 0; i <= limit; i++) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported level value: 0x%x\n", limit); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - - switch (i) { - case 2: { - /* Keep reading function 2 till all the input is received */ - int times; - - c->function = i; - c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC | - KVM_CPUID_FLAG_STATE_READ_NEXT; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - times = c->eax & 0xff; - - for (j = 1; j < times; ++j) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:2):eax & 0xf = 0x%x\n", times); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - c->function = i; - c->flags = KVM_CPUID_FLAG_STATEFUL_FUNC; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - } - break; - } - case 0x1f: - if (env->nr_dies < 2) { - break; - } - /* fallthrough */ - case 4: - case 0xb: - case 0xd: - for (j = 0; ; j++) { - if (i == 0xd && j == 64) { - break; - } - - if (i == 0x1f && j == 64) { - break; - } - - c->function = i; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - c->index = j; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - - if (i == 4 && c->eax == 0) { - break; - } - if (i == 0xb && !(c->ecx & 0xff00)) { - break; - } - if (i == 0x1f && !(c->ecx & 0xff00)) { - break; - } - if (i == 0xd && c->eax == 0) { - continue; - } - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - } - break; - case 0x7: - case 0x12: - for (j = 0; ; j++) { - c->function = i; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - c->index = j; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - - if (j > 1 && (c->eax & 0xf) != 1) { - break; - } - - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x12,ecx:0x%x)\n", j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - } - break; - case 0x14: - case 0x1d: - case 0x1e: { - uint32_t times; - - c->function = i; - c->index = 0; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - times = c->eax; - - for (j = 1; j <= times; ++j) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - c->function = i; - c->index = j; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - } - break; - } - default: - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - if (!c->eax && !c->ebx && !c->ecx && !c->edx) { - /* - * KVM already returns all zeroes if a CPUID entry is missing, - * so we can omit it and avoid hitting KVM's 80-entry limit. - */ - cpuid_i--; - } - break; - } - } - - if (limit >= 0x0a) { - uint32_t eax, edx; - - cpu_x86_cpuid(env, 0x0a, 0, &eax, &unused, &unused, &edx); - - has_architectural_pmu_version = eax & 0xff; - if (has_architectural_pmu_version > 0) { - num_architectural_pmu_gp_counters = (eax & 0xff00) >> 8; - - /* Shouldn't be more than 32, since that's the number of bits - * available in EBX to tell us _which_ counters are available. - * Play it safe. - */ - if (num_architectural_pmu_gp_counters > MAX_GP_COUNTERS) { - num_architectural_pmu_gp_counters = MAX_GP_COUNTERS; - } - - if (has_architectural_pmu_version > 1) { - num_architectural_pmu_fixed_counters = edx & 0x1f; - - if (num_architectural_pmu_fixed_counters > MAX_FIXED_COUNTERS) { - num_architectural_pmu_fixed_counters = MAX_FIXED_COUNTERS; - } - } - } - } - - cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); - - for (i = 0x80000000; i <= limit; i++) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported xlevel value: 0x%x\n", limit); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - - switch (i) { - case 0x8000001d: - /* Query for all AMD cache information leaves */ - for (j = 0; ; j++) { - c->function = i; - c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - c->index = j; - cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - - if (c->eax == 0) { - break; - } - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "cpuid_data is full, no space for " - "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - } - break; - default: - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - if (!c->eax && !c->ebx && !c->ecx && !c->edx) { - /* - * KVM already returns all zeroes if a CPUID entry is missing, - * so we can omit it and avoid hitting KVM's 80-entry limit. - */ - cpuid_i--; - } - break; - } - } - - /* Call Centaur's CPUID instructions they are supported. */ - if (env->cpuid_xlevel2 > 0) { - cpu_x86_cpuid(env, 0xC0000000, 0, &limit, &unused, &unused, &unused); - - for (i = 0xC0000000; i <= limit; i++) { - if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { - fprintf(stderr, "unsupported xlevel2 value: 0x%x\n", limit); - abort(); - } - c = &cpuid_data.entries[cpuid_i++]; - - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); - } - } - + cpuid_i = kvm_x86_build_cpuid(env, cpuid_data.entries, cpuid_i); cpuid_data.cpuid.nent = cpuid_i; 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; @@ -2110,7 +2296,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; @@ -2168,7 +2354,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; } @@ -2401,6 +2587,8 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_PKRS: has_msr_pkrs = true; break; + case MSR_K7_HWCR: + has_msr_hwcr = true; } } } @@ -2410,7 +2598,8 @@ static int kvm_get_supported_msrs(KVMState *s) return ret; } -static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, +static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, + uint32_t msr, uint64_t *val) { CPUState *cs = CPU(cpu); @@ -2421,6 +2610,53 @@ static bool kvm_rdmsr_core_thread_count(X86CPU *cpu, uint32_t msr, return true; } +static bool kvm_rdmsr_rapl_power_unit(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + + *val = cs->kvm_state->msr_energy.msr_unit; + + return true; +} + +static bool kvm_rdmsr_pkg_power_limit(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + + *val = cs->kvm_state->msr_energy.msr_limit; + + return true; +} + +static bool kvm_rdmsr_pkg_power_info(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + + *val = cs->kvm_state->msr_energy.msr_info; + + return true; +} + +static bool kvm_rdmsr_pkg_energy_status(X86CPU *cpu, + uint32_t msr, + uint64_t *val) +{ + + CPUState *cs = CPU(cpu); + *val = cs->kvm_state->msr_energy.msr_value[cs->cpu_index]; + + return true; +} + static Notifier smram_machine_done; static KVMMemoryListener smram_listener; static AddressSpace smram_address_space; @@ -2455,10 +2691,513 @@ static void register_smram_listener(Notifier *n, void *unused) &smram_address_space, 1, "kvm-smram"); } +static void *kvm_msr_energy_thread(void *data) +{ + KVMState *s = data; + struct KVMMsrEnergy *vmsr = &s->msr_energy; + + g_autofree vmsr_package_energy_stat *pkg_stat = NULL; + g_autofree vmsr_thread_stat *thd_stat = NULL; + g_autofree CPUState *cpu = NULL; + g_autofree unsigned int *vpkgs_energy_stat = NULL; + unsigned int num_threads = 0; + + X86CPUTopoIDs topo_ids; + + rcu_register_thread(); + + /* Allocate memory for each package energy status */ + pkg_stat = g_new0(vmsr_package_energy_stat, vmsr->host_topo.maxpkgs); + + /* Allocate memory for thread stats */ + thd_stat = g_new0(vmsr_thread_stat, 1); + + /* Allocate memory for holding virtual package energy counter */ + vpkgs_energy_stat = g_new0(unsigned int, vmsr->guest_vsockets); + + /* Populate the max tick of each packages */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + /* + * Max numbers of ticks per package + * Time in second * Number of ticks/second * Number of cores/package + * ex: 100 ticks/second/CPU, 12 CPUs per Package gives 1200 ticks max + */ + vmsr->host_topo.maxticks[i] = (MSR_ENERGY_THREAD_SLEEP_US / 1000000) + * sysconf(_SC_CLK_TCK) + * vmsr->host_topo.pkg_cpu_count[i]; + } + + while (true) { + /* Get all qemu threads id */ + g_autofree pid_t *thread_ids + = vmsr_get_thread_ids(vmsr->pid, &num_threads); + + if (thread_ids == NULL) { + goto clean; + } + + thd_stat = g_renew(vmsr_thread_stat, thd_stat, num_threads); + /* Unlike g_new0, g_renew0 function doesn't exist yet... */ + memset(thd_stat, 0, num_threads * sizeof(vmsr_thread_stat)); + + /* Populate all the thread stats */ + for (int i = 0; i < num_threads; i++) { + thd_stat[i].utime = g_new0(unsigned long long, 2); + thd_stat[i].stime = g_new0(unsigned long long, 2); + thd_stat[i].thread_id = thread_ids[i]; + vmsr_read_thread_stat(vmsr->pid, + thd_stat[i].thread_id, + &thd_stat[i].utime[0], + &thd_stat[i].stime[0], + &thd_stat[i].cpu_id); + thd_stat[i].pkg_id = + vmsr_get_physical_package_id(thd_stat[i].cpu_id); + } + + /* Retrieve all packages power plane energy counter */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + for (int j = 0; j < num_threads; j++) { + /* + * Use the first thread we found that ran on the CPU + * of the package to read the packages energy counter + */ + if (thd_stat[j].pkg_id == i) { + pkg_stat[i].e_start = + vmsr_read_msr(MSR_PKG_ENERGY_STATUS, + thd_stat[j].cpu_id, + thd_stat[j].thread_id, + s->msr_energy.sioc); + break; + } + } + } + + /* Sleep a short period while the other threads are working */ + usleep(MSR_ENERGY_THREAD_SLEEP_US); + + /* + * Retrieve all packages power plane energy counter + * Calculate the delta of all packages + */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + for (int j = 0; j < num_threads; j++) { + /* + * Use the first thread we found that ran on the CPU + * of the package to read the packages energy counter + */ + if (thd_stat[j].pkg_id == i) { + pkg_stat[i].e_end = + vmsr_read_msr(MSR_PKG_ENERGY_STATUS, + thd_stat[j].cpu_id, + thd_stat[j].thread_id, + s->msr_energy.sioc); + /* + * Prevent the case we have migrate the VM + * during the sleep period or any other cases + * were energy counter might be lower after + * the sleep period. + */ + if (pkg_stat[i].e_end > pkg_stat[i].e_start) { + pkg_stat[i].e_delta = + pkg_stat[i].e_end - pkg_stat[i].e_start; + } else { + pkg_stat[i].e_delta = 0; + } + break; + } + } + } + + /* Delta of ticks spend by each thread between the sample */ + for (int i = 0; i < num_threads; i++) { + vmsr_read_thread_stat(vmsr->pid, + thd_stat[i].thread_id, + &thd_stat[i].utime[1], + &thd_stat[i].stime[1], + &thd_stat[i].cpu_id); + + if (vmsr->pid < 0) { + /* + * We don't count the dead thread + * i.e threads that existed before the sleep + * and not anymore + */ + thd_stat[i].delta_ticks = 0; + } else { + vmsr_delta_ticks(thd_stat, i); + } + } + + /* + * Identify the vcpu threads + * Calculate the number of vcpu per package + */ + CPU_FOREACH(cpu) { + for (int i = 0; i < num_threads; i++) { + if (cpu->thread_id == thd_stat[i].thread_id) { + thd_stat[i].is_vcpu = true; + thd_stat[i].vcpu_id = cpu->cpu_index; + pkg_stat[thd_stat[i].pkg_id].nb_vcpu++; + thd_stat[i].acpi_id = kvm_arch_vcpu_id(cpu); + break; + } + } + } + + /* Retrieve the virtual package number of each vCPU */ + for (int i = 0; i < vmsr->guest_cpu_list->len; i++) { + for (int j = 0; j < num_threads; j++) { + if ((thd_stat[j].acpi_id == + vmsr->guest_cpu_list->cpus[i].arch_id) + && (thd_stat[j].is_vcpu == true)) { + x86_topo_ids_from_apicid(thd_stat[j].acpi_id, + &vmsr->guest_topo_info, &topo_ids); + thd_stat[j].vpkg_id = topo_ids.pkg_id; + } + } + } + + /* Calculate the total energy of all non-vCPU thread */ + for (int i = 0; i < num_threads; i++) { + if ((thd_stat[i].is_vcpu != true) && + (thd_stat[i].delta_ticks > 0)) { + double temp; + temp = vmsr_get_ratio(pkg_stat[thd_stat[i].pkg_id].e_delta, + thd_stat[i].delta_ticks, + vmsr->host_topo.maxticks[thd_stat[i].pkg_id]); + pkg_stat[thd_stat[i].pkg_id].e_ratio + += (uint64_t)lround(temp); + } + } + + /* Calculate the ratio per non-vCPU thread of each package */ + for (int i = 0; i < vmsr->host_topo.maxpkgs; i++) { + if (pkg_stat[i].nb_vcpu > 0) { + pkg_stat[i].e_ratio = pkg_stat[i].e_ratio / pkg_stat[i].nb_vcpu; + } + } + + /* + * Calculate the energy for each Package: + * Energy Package = sum of each vCPU energy that belongs to the package + */ + for (int i = 0; i < num_threads; i++) { + if ((thd_stat[i].is_vcpu == true) && \ + (thd_stat[i].delta_ticks > 0)) { + double temp; + temp = vmsr_get_ratio(pkg_stat[thd_stat[i].pkg_id].e_delta, + thd_stat[i].delta_ticks, + vmsr->host_topo.maxticks[thd_stat[i].pkg_id]); + vpkgs_energy_stat[thd_stat[i].vpkg_id] += + (uint64_t)lround(temp); + vpkgs_energy_stat[thd_stat[i].vpkg_id] += + pkg_stat[thd_stat[i].pkg_id].e_ratio; + } + } + + /* + * Finally populate the vmsr register of each vCPU with the total + * package value to emulate the real hardware where each CPU return the + * value of the package it belongs. + */ + for (int i = 0; i < num_threads; i++) { + if ((thd_stat[i].is_vcpu == true) && \ + (thd_stat[i].delta_ticks > 0)) { + vmsr->msr_value[thd_stat[i].vcpu_id] = \ + vpkgs_energy_stat[thd_stat[i].vpkg_id]; + } + } + + /* Freeing memory before zeroing the pointer */ + for (int i = 0; i < num_threads; i++) { + g_free(thd_stat[i].utime); + g_free(thd_stat[i].stime); + } + } + +clean: + rcu_unregister_thread(); + return NULL; +} + +static int kvm_msr_energy_thread_init(KVMState *s, MachineState *ms) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + struct KVMMsrEnergy *r = &s->msr_energy; + int ret = 0; + + /* + * Sanity check + * 1. Host cpu must be Intel cpu + * 2. RAPL must be enabled on the Host + */ + if (!is_host_cpu_intel()) { + error_report("The RAPL feature can only be enabled on hosts " + "with Intel CPU models"); + ret = 1; + goto out; + } + + if (!is_rapl_enabled()) { + ret = 1; + goto out; + } + + /* Retrieve the virtual topology */ + vmsr_init_topo_info(&r->guest_topo_info, ms); + + /* Retrieve the number of vcpu */ + r->guest_vcpus = ms->smp.cpus; + + /* Retrieve the number of virtual sockets */ + r->guest_vsockets = ms->smp.sockets; + + /* Allocate register memory (MSR_PKG_STATUS) for each vcpu */ + r->msr_value = g_new0(uint64_t, r->guest_vcpus); + + /* Retrieve the CPUArchIDlist */ + r->guest_cpu_list = mc->possible_cpu_arch_ids(ms); + + /* Max number of cpus on the Host */ + r->host_topo.maxcpus = vmsr_get_maxcpus(); + if (r->host_topo.maxcpus == 0) { + error_report("host max cpus = 0"); + ret = 1; + goto out; + } + + /* Max number of packages on the host */ + r->host_topo.maxpkgs = vmsr_get_max_physical_package(r->host_topo.maxcpus); + if (r->host_topo.maxpkgs == 0) { + error_report("host max pkgs = 0"); + ret = 1; + goto out; + } + + /* Allocate memory for each package on the host */ + r->host_topo.pkg_cpu_count = g_new0(unsigned int, r->host_topo.maxpkgs); + r->host_topo.maxticks = g_new0(unsigned int, r->host_topo.maxpkgs); + + vmsr_count_cpus_per_package(r->host_topo.pkg_cpu_count, + r->host_topo.maxpkgs); + for (int i = 0; i < r->host_topo.maxpkgs; i++) { + if (r->host_topo.pkg_cpu_count[i] == 0) { + error_report("cpu per packages = 0 on package_%d", i); + ret = 1; + goto out; + } + } + + /* Get QEMU PID*/ + r->pid = getpid(); + + /* Compute the socket path if necessary */ + if (s->msr_energy.socket_path == NULL) { + s->msr_energy.socket_path = vmsr_compute_default_paths(); + } + + /* Open socket with vmsr helper */ + s->msr_energy.sioc = vmsr_open_socket(s->msr_energy.socket_path); + + if (s->msr_energy.sioc == NULL) { + error_report("vmsr socket opening failed"); + ret = 1; + goto out; + } + + /* Those MSR values should not change */ + r->msr_unit = vmsr_read_msr(MSR_RAPL_POWER_UNIT, 0, r->pid, + s->msr_energy.sioc); + r->msr_limit = vmsr_read_msr(MSR_PKG_POWER_LIMIT, 0, r->pid, + s->msr_energy.sioc); + r->msr_info = vmsr_read_msr(MSR_PKG_POWER_INFO, 0, r->pid, + s->msr_energy.sioc); + if (r->msr_unit == 0 || r->msr_limit == 0 || r->msr_info == 0) { + error_report("can't read any virtual msr"); + ret = 1; + goto out; + } + + qemu_thread_create(&r->msr_thr, "kvm-msr", + kvm_msr_energy_thread, + s, QEMU_THREAD_JOINABLE); +out: + return ret; +} + +int kvm_arch_get_default_type(MachineState *ms) +{ + return 0; +} + +static int kvm_vm_enable_exception_payload(KVMState *s) +{ + int ret = 0; + has_exception_payload = kvm_check_extension(s, KVM_CAP_EXCEPTION_PAYLOAD); + if (has_exception_payload) { + ret = kvm_vm_enable_cap(s, KVM_CAP_EXCEPTION_PAYLOAD, 0, true); + if (ret < 0) { + error_report("kvm: Failed to enable exception payload cap: %s", + strerror(-ret)); + } + } + + return ret; +} + +static int kvm_vm_enable_triple_fault_event(KVMState *s) +{ + int ret = 0; + 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; +} + +static int kvm_vm_set_identity_map_addr(KVMState *s, uint64_t identity_base) +{ + return kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); +} + +static int kvm_vm_set_nr_mmu_pages(KVMState *s) +{ + uint64_t shadow_mem; + int ret = 0; + shadow_mem = object_property_get_int(OBJECT(s), + "kvm-shadow-mem", + &error_abort); + if (shadow_mem != -1) { + shadow_mem /= 4096; + ret = kvm_vm_ioctl(s, KVM_SET_NR_MMU_PAGES, shadow_mem); + } + return ret; +} + +static int kvm_vm_set_tss_addr(KVMState *s, uint64_t tss_base) +{ + return kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, tss_base); +} + +static int kvm_vm_enable_disable_exits(KVMState *s) +{ + int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); +/* 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 +#endif + if (disable_exits) { + disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT | + KVM_X86_DISABLE_EXITS_HLT | + KVM_X86_DISABLE_EXITS_PAUSE | + KVM_X86_DISABLE_EXITS_CSTATE); + } + + return kvm_vm_enable_cap(s, KVM_CAP_X86_DISABLE_EXITS, 0, + disable_exits); +} + +static int kvm_vm_enable_bus_lock_exit(KVMState *s) +{ + int ret = 0; + ret = kvm_check_extension(s, KVM_CAP_X86_BUS_LOCK_EXIT); + if (!(ret & KVM_BUS_LOCK_DETECTION_EXIT)) { + error_report("kvm: bus lock detection unsupported"); + return -ENOTSUP; + } + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_BUS_LOCK_EXIT, 0, + KVM_BUS_LOCK_DETECTION_EXIT); + if (ret < 0) { + error_report("kvm: Failed to enable bus lock detection cap: %s", + strerror(-ret)); + } + + return ret; +} + +static int kvm_vm_enable_notify_vmexit(KVMState *s) +{ + int ret = 0; + if (s->notify_vmexit != NOTIFY_VMEXIT_OPTION_DISABLE) { + 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; +} + +static int kvm_vm_enable_userspace_msr(KVMState *s) +{ + int ret = kvm_vm_enable_cap(s, KVM_CAP_X86_USER_SPACE_MSR, 0, + KVM_MSR_EXIT_REASON_FILTER); + if (ret < 0) { + error_report("Could not enable user space MSRs: %s", + strerror(-ret)); + exit(1); + } + + if (!kvm_filter_msr(s, MSR_CORE_THREAD_COUNT, + kvm_rdmsr_core_thread_count, NULL)) { + error_report("Could not install MSR_CORE_THREAD_COUNT handler!"); + exit(1); + } + + return 0; +} + +static void kvm_vm_enable_energy_msrs(KVMState *s) +{ + bool r; + if (s->msr_energy.enable == true) { + r = kvm_filter_msr(s, MSR_RAPL_POWER_UNIT, + kvm_rdmsr_rapl_power_unit, NULL); + if (!r) { + error_report("Could not install MSR_RAPL_POWER_UNIT \ + handler"); + exit(1); + } + + r = kvm_filter_msr(s, MSR_PKG_POWER_LIMIT, + kvm_rdmsr_pkg_power_limit, NULL); + if (!r) { + error_report("Could not install MSR_PKG_POWER_LIMIT \ + handler"); + exit(1); + } + + r = kvm_filter_msr(s, MSR_PKG_POWER_INFO, + kvm_rdmsr_pkg_power_info, NULL); + if (!r) { + error_report("Could not install MSR_PKG_POWER_INFO \ + handler"); + exit(1); + } + r = kvm_filter_msr(s, MSR_PKG_ENERGY_STATUS, + kvm_rdmsr_pkg_energy_status, NULL); + if (!r) { + error_report("Could not install MSR_PKG_ENERGY_STATUS \ + handler"); + exit(1); + } + } + return; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { - uint64_t identity_base = 0xfffbc000; - uint64_t shadow_mem; int ret; struct utsname utsname; Error *local_err = NULL; @@ -2475,42 +3214,45 @@ int kvm_arch_init(MachineState *ms, KVMState *s) * mechanisms are supported in future (e.g. TDX), they'll need * their own initialization either here or elsewhere. */ - ret = sev_kvm_init(ms->cgs, &local_err); - if (ret < 0) { - error_report_err(local_err); - return ret; + if (ms->cgs) { + ret = confidential_guest_kvm_init(ms->cgs, &local_err); + if (ret < 0) { + error_report_err(local_err); + 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); - has_exception_payload = kvm_check_extension(s, KVM_CAP_EXCEPTION_PAYLOAD); - if (has_exception_payload) { - ret = kvm_vm_enable_cap(s, KVM_CAP_EXCEPTION_PAYLOAD, 0, true); - if (ret < 0) { - error_report("kvm: Failed to enable exception payload cap: %s", - strerror(-ret)); - return ret; - } + ret = kvm_vm_enable_exception_payload(s); + if (ret < 0) { + return ret; } - 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); + ret = kvm_vm_enable_triple_fault_event(s); + if (ret < 0) { + 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) { - error_report("kvm: Failed to enable triple fault event cap: %s", - strerror(-ret)); return ret; } +#else + error_report("kvm: Xen support not enabled in qemu"); + return -ENOTSUP; +#endif } ret = kvm_get_supported_msrs(s); @@ -2523,47 +3265,23 @@ int kvm_arch_init(MachineState *ms, KVMState *s) uname(&utsname); lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; - /* - * On older Intel CPUs, KVM uses vm86 mode to emulate 16-bit code directly. - * 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. - */ - 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; - } + ret = kvm_vm_set_identity_map_addr(s, KVM_IDENTITY_BASE); + if (ret < 0) { + return ret; } /* Set TSS base one page after EPT identity map. */ - ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, identity_base + 0x1000); + ret = kvm_vm_set_tss_addr(s, KVM_IDENTITY_BASE + 0x1000); if (ret < 0) { return ret; } /* Tell fw_cfg to notify the BIOS to reserve the range. */ - ret = e820_add_entry(identity_base, 0x4000, E820_RESERVED); - if (ret < 0) { - fprintf(stderr, "e820_add_entry() table is full\n"); - return ret; - } + e820_add_entry(KVM_IDENTITY_BASE, 0x4000, E820_RESERVED); - shadow_mem = object_property_get_int(OBJECT(s), "kvm-shadow-mem", &error_abort); - if (shadow_mem != -1) { - shadow_mem /= 4096; - ret = kvm_vm_ioctl(s, KVM_SET_NR_MMU_PAGES, shadow_mem); - if (ret < 0) { - return ret; - } + ret = kvm_vm_set_nr_mmu_pages(s); + if (ret < 0) { + return ret; } if (kvm_check_extension(s, KVM_CAP_X86_SMM) && @@ -2574,22 +3292,7 @@ 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 -#endif - if (disable_exits) { - disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT | - KVM_X86_DISABLE_EXITS_HLT | - KVM_X86_DISABLE_EXITS_PAUSE | - KVM_X86_DISABLE_EXITS_CSTATE); - } - - ret = kvm_vm_enable_cap(s, KVM_CAP_X86_DISABLE_EXITS, 0, - disable_exits); + ret = kvm_vm_enable_disable_exits(s); if (ret < 0) { error_report("kvm: guest stopping CPU not supported: %s", strerror(-ret)); @@ -2600,16 +3303,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) X86MachineState *x86ms = X86_MACHINE(ms); if (x86ms->bus_lock_ratelimit > 0) { - ret = kvm_check_extension(s, KVM_CAP_X86_BUS_LOCK_EXIT); - if (!(ret & KVM_BUS_LOCK_DETECTION_EXIT)) { - error_report("kvm: bus lock detection unsupported"); - return -ENOTSUP; - } - ret = kvm_vm_enable_cap(s, KVM_CAP_X86_BUS_LOCK_EXIT, 0, - KVM_BUS_LOCK_DETECTION_EXIT); + ret = kvm_vm_enable_bus_lock_exit(s); if (ret < 0) { - error_report("kvm: Failed to enable bus lock detection cap: %s", - strerror(-ret)); return ret; } ratelimit_init(&bus_lock_ratelimit_ctrl); @@ -2618,37 +3313,25 @@ 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_check_extension(s, KVM_CAP_X86_NOTIFY_VMEXIT)) { + ret = kvm_vm_enable_notify_vmexit(s); + if (ret < 0) { + 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); + if (kvm_vm_check_extension(s, KVM_CAP_X86_USER_SPACE_MSR)) { + ret = kvm_vm_enable_userspace_msr(s); + if (ret < 0) { + return ret; } - 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); + if (s->msr_energy.enable == true) { + kvm_vm_enable_energy_msrs(s); + if (kvm_msr_energy_thread_init(s, ms)) { + error_report("kvm : error RAPL feature requirement not met"); + exit(1); + } } } @@ -2755,40 +3438,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); @@ -3140,7 +3794,14 @@ static void kvm_msr_entry_add_vmx(X86CPU *cpu, FeatureWordArray f) kvm_msr_entry_add(cpu, MSR_IA32_VMX_CR4_FIXED0, CR4_VMXE_MASK); - if (f[FEAT_VMX_SECONDARY_CTLS] & VMX_SECONDARY_EXEC_TSC_SCALING) { + if (f[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED) { + /* FRED injected-event data (0x2052). */ + kvm_msr_entry_add(cpu, MSR_IA32_VMX_VMCS_ENUM, 0x52); + } else if (f[FEAT_VMX_EXIT_CTLS] & + VMX_VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { + /* Secondary VM-exit controls (0x2044). */ + kvm_msr_entry_add(cpu, MSR_IA32_VMX_VMCS_ENUM, 0x44); + } else if (f[FEAT_VMX_SECONDARY_CTLS] & VMX_SECONDARY_EXEC_TSC_SCALING) { /* TSC multiplier (0x2032). */ kvm_msr_entry_add(cpu, MSR_IA32_VMX_VMCS_ENUM, 0x32); } else { @@ -3270,6 +3931,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_virt_ssbd) { kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, env->virt_ssbd); } + if (has_msr_hwcr) { + kvm_msr_entry_add(cpu, MSR_K7_HWCR, env->msr_hwcr); + } #ifdef TARGET_X86_64 if (lm_capable_kernel) { @@ -3277,6 +3941,17 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, MSR_KERNELGSBASE, env->kernelgsbase); kvm_msr_entry_add(cpu, MSR_FMASK, env->fmask); kvm_msr_entry_add(cpu, MSR_LSTAR, env->lstar); + if (env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED) { + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP0, env->fred_rsp0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP1, env->fred_rsp1); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP2, env->fred_rsp2); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP3, env->fred_rsp3); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_STKLVLS, env->fred_stklvls); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP1, env->fred_ssp1); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP2, env->fred_ssp2); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP3, env->fred_ssp3); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_CONFIG, env->fred_config); + } } #endif @@ -3359,13 +4034,11 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, env->msr_hv_tsc_emulation_status); } -#ifdef CONFIG_SYNDBG if (hyperv_feat_enabled(cpu, HYPERV_FEAT_SYNDBG) && has_msr_hv_syndbg_options) { kvm_msr_entry_add(cpu, HV_X64_MSR_SYNDBG_OPTIONS, hyperv_syndbg_query_options()); } -#endif } if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VAPIC)) { kvm_msr_entry_add(cpu, HV_X64_MSR_APIC_ASSIST_PAGE, @@ -3486,7 +4159,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 @@ -3519,8 +4192,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) { @@ -3535,45 +4206,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); - } + unsigned long type; + int ret; type = has_xsave2 ? KVM_GET_XSAVE2 : KVM_GET_XSAVE; ret = kvm_vcpu_ioctl(CPU(cpu), type, xsave); @@ -3647,6 +4285,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); @@ -3686,6 +4328,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; @@ -3770,6 +4416,9 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, MSR_IA32_TSC, 0); env->tsc_valid = !runstate_is_running(); } + if (has_msr_hwcr) { + kvm_msr_entry_add(cpu, MSR_K7_HWCR, 0); + } #ifdef TARGET_X86_64 if (lm_capable_kernel) { @@ -3777,6 +4426,17 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, MSR_KERNELGSBASE, 0); kvm_msr_entry_add(cpu, MSR_FMASK, 0); kvm_msr_entry_add(cpu, MSR_LSTAR, 0); + if (env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED) { + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP0, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP1, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP2, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_RSP3, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_STKLVLS, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP1, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP2, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_SSP3, 0); + kvm_msr_entry_add(cpu, MSR_IA32_FRED_CONFIG, 0); + } } #endif kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); @@ -3917,7 +4577,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) { @@ -3999,6 +4658,33 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_LSTAR: env->lstar = msrs[i].data; break; + case MSR_IA32_FRED_RSP0: + env->fred_rsp0 = msrs[i].data; + break; + case MSR_IA32_FRED_RSP1: + env->fred_rsp1 = msrs[i].data; + break; + case MSR_IA32_FRED_RSP2: + env->fred_rsp2 = msrs[i].data; + break; + case MSR_IA32_FRED_RSP3: + env->fred_rsp3 = msrs[i].data; + break; + case MSR_IA32_FRED_STKLVLS: + env->fred_stklvls = msrs[i].data; + break; + case MSR_IA32_FRED_SSP1: + env->fred_ssp1 = msrs[i].data; + break; + case MSR_IA32_FRED_SSP2: + env->fred_ssp2 = msrs[i].data; + break; + case MSR_IA32_FRED_SSP3: + env->fred_ssp3 = msrs[i].data; + break; + case MSR_IA32_FRED_CONFIG: + env->fred_config = msrs[i].data; + break; #endif case MSR_IA32_TSC: env->tsc = msrs[i].data; @@ -4252,6 +4938,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_ARCH_LBR_INFO_0 ... MSR_ARCH_LBR_INFO_0 + 31: env->lbr_records[index - MSR_ARCH_LBR_INFO_0].info = msrs[i].data; break; + case MSR_K7_HWCR: + env->msr_hwcr = msrs[i].data; + break; } } @@ -4306,10 +4995,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) { @@ -4334,6 +5019,7 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) events.sipi_vector = env->sipi_vector; if (has_msr_smbase) { + events.flags |= KVM_VCPUEVENT_VALID_SMM; events.smi.smm = !!(env->hflags & HF_SMM_MASK); events.smi.smm_inside_nmi = !!(env->hflags2 & HF2_SMM_INSIDE_NMI_MASK); if (kvm_irqchip_in_kernel()) { @@ -4348,12 +5034,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) events.smi.pending = 0; events.smi.latched_init = 0; } - /* Stop SMI delivery on old machine types to avoid a reboot - * on an inward migration of an old VM. - */ - if (!cpu->kvm_no_smi_migration) { - events.flags |= KVM_VCPUEVENT_VALID_SMM; - } } if (level >= KVM_PUT_RESET_STATE) { @@ -4377,10 +5057,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) { @@ -4446,47 +5122,12 @@ static int kvm_get_vcpu_events(X86CPU *cpu) 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]; @@ -4504,10 +5145,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; @@ -4595,7 +5232,7 @@ static int kvm_get_nested_state(X86CPU *cpu) return ret; } -int kvm_arch_put_registers(CPUState *cpu, int level) +int kvm_arch_put_registers(CPUState *cpu, int level, Error **errp) { X86CPU *x86_cpu = X86_CPU(cpu); int ret; @@ -4605,11 +5242,12 @@ int kvm_arch_put_registers(CPUState *cpu, int level) /* * 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 - * preceed kvm_put_nested_state() when 'real' nested state is set. + * 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) { + error_setg_errno(errp, -ret, "Failed to set feature control MSR"); return ret; } } @@ -4617,12 +5255,14 @@ int kvm_arch_put_registers(CPUState *cpu, int level) /* 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) { + error_setg_errno(errp, -ret, "Failed to set special registers"); return ret; } if (level >= KVM_PUT_RESET_STATE) { ret = kvm_put_nested_state(x86_cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set nested state"); return ret; } } @@ -4636,55 +5276,63 @@ 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) { + error_setg_errno(errp, -ret, "Failed to set Xen state"); + return ret; + } + } +#endif + ret = kvm_getput_regs(x86_cpu, 1); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set general purpose registers"); return ret; } ret = kvm_put_xsave(x86_cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set XSAVE"); return ret; } ret = kvm_put_xcrs(x86_cpu); if (ret < 0) { - return ret; - } - /* must be before kvm_put_msrs */ - ret = kvm_inject_mce_oldstyle(x86_cpu); - if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set XCRs"); return ret; } ret = kvm_put_msrs(x86_cpu, level); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set MSRs"); return ret; } ret = kvm_put_vcpu_events(x86_cpu, level); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set vCPU events"); return ret; } if (level >= KVM_PUT_RESET_STATE) { ret = kvm_put_mp_state(x86_cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set MP state"); return ret; } } ret = kvm_put_tscdeadline_msr(x86_cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set TSC deadline MSR"); return ret; } ret = kvm_put_debugregs(x86_cpu); if (ret < 0) { - return ret; - } - /* must be last */ - ret = kvm_guest_debug_workarounds(x86_cpu); - if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to set debug registers"); return ret; } return 0; } -int kvm_arch_get_registers(CPUState *cs) +int kvm_arch_get_registers(CPUState *cs, Error **errp) { X86CPU *cpu = X86_CPU(cs); int ret; @@ -4693,6 +5341,7 @@ int kvm_arch_get_registers(CPUState *cs) ret = kvm_get_vcpu_events(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get vCPU events"); goto out; } /* @@ -4701,40 +5350,58 @@ int kvm_arch_get_registers(CPUState *cs) */ ret = kvm_get_mp_state(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get MP state"); goto out; } ret = kvm_getput_regs(cpu, 0); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get general purpose registers"); goto out; } ret = kvm_get_xsave(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get XSAVE"); goto out; } ret = kvm_get_xcrs(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get XCRs"); goto out; } ret = has_sregs2 ? kvm_get_sregs2(cpu) : kvm_get_sregs(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get special registers"); goto out; } ret = kvm_get_msrs(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get MSRs"); goto out; } ret = kvm_get_apic(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get APIC"); goto out; } ret = kvm_get_debugregs(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get debug registers"); goto out; } ret = kvm_get_nested_state(cpu); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get nested state"); goto out; } +#ifdef CONFIG_XEN_EMU + if (xen_mode == XEN_EMULATE) { + ret = kvm_get_xen_state(cs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to get Xen state"); + goto out; + } + } +#endif ret = 0; out: cpu_sync_bndcs_hflags(&cpu->env); @@ -4750,9 +5417,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) { @@ -4761,9 +5428,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) { @@ -4774,7 +5441,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 @@ -4827,7 +5494,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(); } } @@ -4859,15 +5526,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); } @@ -5007,8 +5687,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: @@ -5048,8 +5727,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; @@ -5188,7 +5866,7 @@ static bool kvm_install_msr_filters(KVMState *s) return true; } -bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, +static bool kvm_filter_msr(KVMState *s, uint32_t msr, QEMURDMSRHandler *rdmsr, QEMUWRMSRHandler *wrmsr) { int i; @@ -5230,7 +5908,7 @@ static int kvm_handle_rdmsr(X86CPU *cpu, struct kvm_run *run) } } - assert(false); + g_assert_not_reached(); } static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run) @@ -5249,7 +5927,7 @@ static int kvm_handle_wrmsr(X86CPU *cpu, struct kvm_run *run) } } - assert(false); + g_assert_not_reached(); } static bool has_sgx_provisioning; @@ -5289,6 +5967,50 @@ static bool host_supports_vmx(void) return ecx & CPUID_EXT_VMX; } +/* + * Currently the handling here only supports use of KVM_HC_MAP_GPA_RANGE + * to service guest-initiated memory attribute update requests so that + * KVM_SET_MEMORY_ATTRIBUTES can update whether or not a page should be + * backed by the private memory pool provided by guest_memfd, and as such + * is only applicable to guest_memfd-backed guests (e.g. SNP/TDX). + * + * Other other use-cases for KVM_HC_MAP_GPA_RANGE, such as for SEV live + * migration, are not implemented here currently. + * + * For the guest_memfd use-case, these exits will generally be synthesized + * by KVM based on platform-specific hypercalls, like GHCB requests in the + * case of SEV-SNP, and not issued directly within the guest though the + * KVM_HC_MAP_GPA_RANGE hypercall. So in this case, KVM_HC_MAP_GPA_RANGE is + * not actually advertised to guests via the KVM CPUID feature bit, as + * opposed to SEV live migration where it would be. Since it is unlikely the + * SEV live migration use-case would be useful for guest-memfd backed guests, + * because private/shared page tracking is already provided through other + * means, these 2 use-cases should be treated as being mutually-exclusive. + */ +static int kvm_handle_hc_map_gpa_range(struct kvm_run *run) +{ + uint64_t gpa, size, attributes; + + if (!machine_require_guest_memfd(current_machine)) + return -EINVAL; + + gpa = run->hypercall.args[0]; + size = run->hypercall.args[1] * TARGET_PAGE_SIZE; + attributes = run->hypercall.args[2]; + + trace_kvm_hc_map_gpa_range(gpa, size, attributes, run->hypercall.flags); + + return kvm_convert_memory(gpa, size, attributes & KVM_MAP_GPA_RANGE_ENCRYPTED); +} + +static int kvm_handle_hypercall(struct kvm_run *run) +{ + if (run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) + return kvm_handle_hc_map_gpa_range(run); + + return -EINVAL; +} + #define VMX_INVALID_GUEST_STATE 0x80000021 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) @@ -5297,23 +6019,22 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) 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; @@ -5339,9 +6060,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); @@ -5357,15 +6078,15 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) 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); + warn_report("KVM internal error: Encountered a notify exit " + "with invalid context in guest."); ret = -1; } else { - warn_report_once("KVM: %s", str); + warn_report_once("KVM: Encountered a notify exit with valid " + "context in guest. " + "The guest could be misbehaving."); ret = 0; } break; @@ -5379,6 +6100,14 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) 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 + case KVM_EXIT_HYPERCALL: + ret = kvm_handle_hypercall(run); + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; @@ -5496,7 +6225,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); @@ -5507,6 +6236,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; @@ -5526,8 +6269,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; @@ -5615,11 +6358,6 @@ bool kvm_has_waitpkg(void) return has_msr_umwait; } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return !sev_es_enabled(); -} - #define ARCH_REQ_XCOMP_GUEST_PERM 0x1025 void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask) @@ -5689,7 +6427,6 @@ static void kvm_arch_set_notify_window(Object *obj, Visitor *v, Error **errp) { KVMState *s = KVM_STATE(obj); - Error *error = NULL; uint32_t value; if (s->fd != -1) { @@ -5697,13 +6434,95 @@ static void kvm_arch_set_notify_window(Object *obj, Visitor *v, 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->notify_window = value; + 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) @@ -5722,6 +6541,29 @@ void kvm_arch_accel_class_init(ObjectClass *oc) 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) diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 6a5c24e3dc..9de9c0d303 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,39 +31,43 @@ #endif /* CONFIG_KVM */ bool kvm_has_smm(void); -bool kvm_has_adjust_clock(void); -bool kvm_has_adjust_clock_stable(void); -bool kvm_has_exception_payload(void); -void kvm_synchronize_all_tsc(void); +bool kvm_enable_x2apic(void); +bool kvm_hv_vpindex_settable(void); +bool kvm_enable_hypercall(uint64_t enable_mask); + +bool kvm_enable_sgx_provisioning(KVMState *s); +bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); + +int kvm_get_vm_type(MachineState *ms); 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_is_vm_type_supported(int type); +bool kvm_has_adjust_clock_stable(void); +bool kvm_has_exception_payload(void); +void kvm_synchronize_all_tsc(void); + +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); +#endif /* CONFIG_KVM */ -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); - -void kvm_set_max_apic_id(uint32_t max_apic_id); +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..3996cafaf2 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -1,14 +1,13 @@ -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', + 'vmsr_energy.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_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) -i386_softmmu_ss.add_all(when: 'CONFIG_KVM', if_true: i386_softmmu_kvm_ss) +i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/target/i386/kvm/sev-stub.c b/target/i386/kvm/sev-stub.c deleted file mode 100644 index 1be5341e8a..0000000000 --- a/target/i386/kvm/sev-stub.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * QEMU SEV stub - * - * Copyright Advanced Micro Devices 2018 - * - * Authors: - * Brijesh Singh - * - * 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 "sev.h" - -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - /* If we get here, cgs must be some non-SEV thing */ - return 0; -} diff --git a/target/i386/kvm/trace-events b/target/i386/kvm/trace-events index 7c369db1e1..74a6234ff7 100644 --- a/target/i386/kvm/trace-events +++ b/target/i386/kvm/trace-events @@ -5,3 +5,11 @@ 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" +kvm_hc_map_gpa_range(uint64_t gpa, uint64_t size, uint64_t attributes, uint64_t flags) "gpa 0x%" PRIx64 " size 0x%" PRIx64 " attributes 0x%" PRIx64 " flags 0x%" PRIx64 + +# 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/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c new file mode 100644 index 0000000000..31508d4e77 --- /dev/null +++ b/target/i386/kvm/vmsr_energy.c @@ -0,0 +1,346 @@ +/* + * QEMU KVM support -- x86 virtual RAPL msr + * + * Copyright 2024 Red Hat, Inc. 2024 + * + * Author: + * Anthony Harivel + * + * 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 "vmsr_energy.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "hw/boards.h" +#include "cpu.h" +#include "host-cpu.h" + +char *vmsr_compute_default_paths(void) +{ + g_autofree char *state = qemu_get_local_state_dir(); + + return g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL); +} + +bool is_host_cpu_intel(void) +{ + int family, model, stepping; + char vendor[CPUID_VENDOR_SZ + 1]; + + host_cpu_vendor_fms(vendor, &family, &model, &stepping); + + return g_str_equal(vendor, CPUID_VENDOR_INTEL); +} + +int is_rapl_enabled(void) +{ + const char *path = "/sys/class/powercap/intel-rapl/enabled"; + FILE *file = fopen(path, "r"); + int value = 0; + + if (file != NULL) { + if (fscanf(file, "%d", &value) != 1) { + error_report("INTEL RAPL not enabled"); + } + fclose(file); + } else { + error_report("Error opening %s", path); + } + + return value; +} + +QIOChannelSocket *vmsr_open_socket(const char *path) +{ + g_autofree char *socket_path = NULL; + + socket_path = g_strdup(path); + + SocketAddress saddr = { + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = socket_path + }; + + QIOChannelSocket *sioc = qio_channel_socket_new(); + Error *local_err = NULL; + + qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper"); + qio_channel_socket_connect_sync(sioc, + &saddr, + &local_err); + if (local_err) { + /* Close socket. */ + qio_channel_close(QIO_CHANNEL(sioc), NULL); + object_unref(OBJECT(sioc)); + sioc = NULL; + goto out; + } + + qio_channel_set_delay(QIO_CHANNEL(sioc), false); +out: + return sioc; +} + +uint64_t vmsr_read_msr(uint32_t reg, uint32_t cpu_id, uint32_t tid, + QIOChannelSocket *sioc) +{ + uint64_t data = 0; + int r = 0; + Error *local_err = NULL; + uint32_t buffer[3]; + /* + * Send the required arguments: + * 1. RAPL MSR register to read + * 2. On which CPU ID + * 3. From which vCPU (Thread ID) + */ + buffer[0] = reg; + buffer[1] = cpu_id; + buffer[2] = tid; + + r = qio_channel_write_all(QIO_CHANNEL(sioc), + (char *)buffer, sizeof(buffer), + &local_err); + if (r < 0) { + goto out_close; + } + + r = qio_channel_read(QIO_CHANNEL(sioc), + (char *)&data, sizeof(data), + &local_err); + if (r < 0) { + data = 0; + goto out_close; + } + +out_close: + return data; +} + +/* Retrieve the max number of physical package */ +unsigned int vmsr_get_max_physical_package(unsigned int max_cpus) +{ + const char *dir = "/sys/devices/system/cpu/"; + const char *topo_path = "topology/physical_package_id"; + g_autofree int *uniquePackages = g_new0(int, max_cpus); + unsigned int packageCount = 0; + FILE *file = NULL; + + for (int i = 0; i < max_cpus; i++) { + g_autofree char *filePath = NULL; + g_autofree char *cpuid = g_strdup_printf("cpu%d", i); + + filePath = g_build_filename(dir, cpuid, topo_path, NULL); + + file = fopen(filePath, "r"); + + if (file == NULL) { + error_report("Error opening physical_package_id file"); + return 0; + } + + char packageId[10]; + if (fgets(packageId, sizeof(packageId), file) == NULL) { + packageCount = 0; + } + + fclose(file); + + int currentPackageId = atoi(packageId); + + bool isUnique = true; + for (int j = 0; j < packageCount; j++) { + if (uniquePackages[j] == currentPackageId) { + isUnique = false; + break; + } + } + + if (isUnique) { + uniquePackages[packageCount] = currentPackageId; + packageCount++; + + if (packageCount >= max_cpus) { + break; + } + } + } + + return (packageCount == 0) ? 1 : packageCount; +} + +/* Retrieve the max number of physical cpu on the host */ +unsigned int vmsr_get_maxcpus(void) +{ + GDir *dir; + const gchar *entry_name; + unsigned int cpu_count = 0; + const char *path = "/sys/devices/system/cpu/"; + + dir = g_dir_open(path, 0, NULL); + if (dir == NULL) { + error_report("Unable to open cpu directory"); + return -1; + } + + while ((entry_name = g_dir_read_name(dir)) != NULL) { + if (g_ascii_strncasecmp(entry_name, "cpu", 3) == 0 && + isdigit(entry_name[3])) { + cpu_count++; + } + } + + g_dir_close(dir); + + return cpu_count; +} + +/* Count the number of physical cpu on each packages */ +unsigned int vmsr_count_cpus_per_package(unsigned int *package_count, + unsigned int max_pkgs) +{ + g_autofree char *file_contents = NULL; + g_autofree char *path = NULL; + g_autofree char *path_name = NULL; + gsize length; + + /* Iterate over cpus and count cpus in each package */ + for (int cpu_id = 0; ; cpu_id++) { + path_name = g_strdup_printf("/sys/devices/system/cpu/cpu%d/" + "topology/physical_package_id", cpu_id); + + path = g_build_filename(path_name, NULL); + + if (!g_file_get_contents(path, &file_contents, &length, NULL)) { + break; /* No more cpus */ + } + + /* Get the physical package ID for this CPU */ + int package_id = atoi(file_contents); + + /* Check if the package ID is within the known number of packages */ + if (package_id >= 0 && package_id < max_pkgs) { + /* If yes, count the cpu for this package*/ + package_count[package_id]++; + } + } + + return 0; +} + +/* Get the physical package id from a given cpu id */ +int vmsr_get_physical_package_id(int cpu_id) +{ + g_autofree char *file_contents = NULL; + g_autofree char *file_path = NULL; + int package_id = -1; + gsize length; + + file_path = g_strdup_printf("/sys/devices/system/cpu/cpu%d" + "/topology/physical_package_id", cpu_id); + + if (!g_file_get_contents(file_path, &file_contents, &length, NULL)) { + goto out; + } + + package_id = atoi(file_contents); + +out: + return package_id; +} + +/* Read the scheduled time for a given thread of a give pid */ +void vmsr_read_thread_stat(pid_t pid, + unsigned int thread_id, + unsigned long long *utime, + unsigned long long *stime, + unsigned int *cpu_id) +{ + g_autofree char *path = NULL; + g_autofree char *path_name = NULL; + + path_name = g_strdup_printf("/proc/%u/task/%d/stat", pid, thread_id); + + path = g_build_filename(path_name, NULL); + + FILE *file = fopen(path, "r"); + if (file == NULL) { + error_report("Error opening %s", path_name); + return; + } + + if (fscanf(file, "%*d (%*[^)]) %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u" + " %llu %llu %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %*u" + " %*u %*u %*u %*u %*u %*u %*u %*u %*u %*d %*u %*u %u", + utime, stime, cpu_id) != 3) + { + fclose(file); + error_report("Error fscanf did not report the right amount of items"); + return; + } + + fclose(file); + return; +} + +/* Read QEMU stat task folder to retrieve all QEMU threads ID */ +pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads) +{ + g_autofree char *task_path = g_strdup_printf("%d/task", pid); + g_autofree char *path = g_build_filename("/proc", task_path, NULL); + + DIR *dir = opendir(path); + if (dir == NULL) { + error_report("Error opening /proc/qemu/task"); + return NULL; + } + + pid_t *thread_ids = NULL; + unsigned int thread_count = 0; + + g_autofree struct dirent *ent = NULL; + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] == '.') { + continue; + } + pid_t tid = atoi(ent->d_name); + if (pid != tid) { + thread_ids = g_renew(pid_t, thread_ids, (thread_count + 1)); + thread_ids[thread_count] = tid; + thread_count++; + } + } + + closedir(dir); + + *num_threads = thread_count; + return thread_ids; +} + +void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i) +{ + thd_stat[i].delta_ticks = (thd_stat[i].utime[1] + thd_stat[i].stime[1]) + - (thd_stat[i].utime[0] + thd_stat[i].stime[0]); +} + +double vmsr_get_ratio(uint64_t e_delta, + unsigned long long delta_ticks, + unsigned int maxticks) +{ + return (e_delta / 100.0) * ((100.0 / maxticks) * delta_ticks); +} + +void vmsr_init_topo_info(X86CPUTopoInfo *topo_info, + const MachineState *ms) +{ + topo_info->dies_per_pkg = ms->smp.dies; + topo_info->modules_per_die = ms->smp.modules; + topo_info->cores_per_module = ms->smp.cores; + topo_info->threads_per_core = ms->smp.threads; +} + diff --git a/target/i386/kvm/vmsr_energy.h b/target/i386/kvm/vmsr_energy.h new file mode 100644 index 0000000000..16cc1f4814 --- /dev/null +++ b/target/i386/kvm/vmsr_energy.h @@ -0,0 +1,99 @@ +/* + * QEMU KVM support -- x86 virtual energy-related MSR. + * + * Copyright 2024 Red Hat, Inc. 2024 + * + * Author: + * Anthony Harivel + * + * 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 VMSR_ENERGY_H +#define VMSR_ENERGY_H + +#include +#include "qemu/osdep.h" +#include "io/channel-socket.h" +#include "hw/i386/topology.h" + +/* + * Define the interval time in micro seconds between 2 samples of + * energy related MSRs + */ +#define MSR_ENERGY_THREAD_SLEEP_US 1000000.0 + +/* + * Thread statistic + * @ thread_id: TID (thread ID) + * @ is_vcpu: true if TID is vCPU thread + * @ cpu_id: CPU number last executed on + * @ pkg_id: package number of the CPU + * @ vcpu_id: vCPU ID + * @ vpkg: virtual package number + * @ acpi_id: APIC id of the vCPU + * @ utime: amount of clock ticks the thread + * has been scheduled in User mode + * @ stime: amount of clock ticks the thread + * has been scheduled in System mode + * @ delta_ticks: delta of utime+stime between + * the two samples (before/after sleep) + */ +struct vmsr_thread_stat { + unsigned int thread_id; + bool is_vcpu; + unsigned int cpu_id; + unsigned int pkg_id; + unsigned int vpkg_id; + unsigned int vcpu_id; + unsigned long acpi_id; + unsigned long long *utime; + unsigned long long *stime; + unsigned long long delta_ticks; +}; + +/* + * Package statistic + * @ e_start: package energy counter before the sleep + * @ e_end: package energy counter after the sleep + * @ e_delta: delta of package energy counter + * @ e_ratio: store the energy ratio of non-vCPU thread + * @ nb_vcpu: number of vCPU running on this package + */ +struct vmsr_package_energy_stat { + uint64_t e_start; + uint64_t e_end; + uint64_t e_delta; + uint64_t e_ratio; + unsigned int nb_vcpu; +}; + +typedef struct vmsr_thread_stat vmsr_thread_stat; +typedef struct vmsr_package_energy_stat vmsr_package_energy_stat; + +char *vmsr_compute_default_paths(void); +void vmsr_read_thread_stat(pid_t pid, + unsigned int thread_id, + unsigned long long *utime, + unsigned long long *stime, + unsigned int *cpu_id); + +QIOChannelSocket *vmsr_open_socket(const char *path); +uint64_t vmsr_read_msr(uint32_t reg, uint32_t cpu_id, + uint32_t tid, QIOChannelSocket *sioc); +void vmsr_delta_ticks(vmsr_thread_stat *thd_stat, int i); +unsigned int vmsr_get_maxcpus(void); +unsigned int vmsr_get_max_physical_package(unsigned int max_cpus); +unsigned int vmsr_count_cpus_per_package(unsigned int *package_count, + unsigned int max_pkgs); +int vmsr_get_physical_package_id(int cpu_id); +pid_t *vmsr_get_thread_ids(pid_t pid, unsigned int *num_threads); +double vmsr_get_ratio(uint64_t e_delta, + unsigned long long delta_ticks, + unsigned int maxticks); +void vmsr_init_topo_info(X86CPUTopoInfo *topo_info, const MachineState *ms); +bool is_host_cpu_intel(void); +int is_rapl_enabled(void); +#endif /* VMSR_ENERGY_H */ 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..2f89dc628e --- /dev/null +++ b/target/i386/kvm/xen-emu.c @@ -0,0 +1,1940 @@ +/* + * 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. */ + e820_add_entry(XEN_SPECIAL_AREA_ADDR, XEN_SPECIAL_AREA_SIZE, E820_RESERVED); + + /* 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 310b125235..b4610325aa 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,14 +1536,60 @@ 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() } }; +static bool msr_hwcr_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->msr_hwcr != 0; +} + +static const VMStateDescription vmstate_msr_hwcr = { + .name = "cpu/msr_hwcr", + .version_id = 1, + .minimum_version_id = 1, + .needed = msr_hwcr_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_hwcr, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + #ifdef TARGET_X86_64 +static bool intel_fred_msrs_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->features[FEAT_7_1_EAX] & CPUID_7_1_EAX_FRED); +} + +static const VMStateDescription vmstate_msr_fred = { + .name = "cpu/fred", + .version_id = 1, + .minimum_version_id = 1, + .needed = intel_fred_msrs_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.fred_rsp0, X86CPU), + VMSTATE_UINT64(env.fred_rsp1, X86CPU), + VMSTATE_UINT64(env.fred_rsp2, X86CPU), + VMSTATE_UINT64(env.fred_rsp3, X86CPU), + VMSTATE_UINT64(env.fred_stklvls, X86CPU), + VMSTATE_UINT64(env.fred_ssp1, X86CPU), + VMSTATE_UINT64(env.fred_ssp2, X86CPU), + VMSTATE_UINT64(env.fred_ssp3, X86CPU), + VMSTATE_UINT64(env.fred_config, X86CPU), + VMSTATE_END_OF_LIST() + } + }; + static bool amx_xtile_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1533,7 +1603,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 +1624,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), @@ -1575,7 +1645,7 @@ static const VMStateDescription vmstate_triple_fault = { .version_id = 1, .minimum_version_id = 1, .needed = triple_fault_needed, - .fields = (VMStateField[]) { + .fields = (const VMStateField[]) { VMSTATE_UINT8(env.triple_fault_pending, X86CPU), VMSTATE_END_OF_LIST() } @@ -1587,7 +1657,7 @@ const VMStateDescription vmstate_x86_cpu = { .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), @@ -1675,7 +1745,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, @@ -1716,12 +1786,15 @@ const VMStateDescription vmstate_x86_cpu = { #endif #ifdef CONFIG_KVM &vmstate_nested_state, + &vmstate_xen_vcpu, #endif &vmstate_msr_tsx_ctrl, &vmstate_msr_intel_sgx, &vmstate_pdptrs, &vmstate_msr_xfd, + &vmstate_msr_hwcr, #ifdef TARGET_X86_64 + &vmstate_msr_fred, &vmstate_amx_xtile, #endif &vmstate_arch_lbr, diff --git a/target/i386/meson.build b/target/i386/meson.build index ae38dc9563..075117989b 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -6,31 +6,31 @@ i386_ss.add(files( 'xsave_helper.c', 'cpu-dump.c', )) -i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest.c')) # x86 cpu type 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-apic.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..2d766b2637 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -28,12 +28,9 @@ #include "monitor/hmp-target.h" #include "monitor/hmp.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qerror.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 +54,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 +255,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), @@ -648,22 +645,3 @@ const MonitorDef *target_monitor_defs(void) { return monitor_defs; } - -void hmp_info_local_apic(Monitor *mon, const QDict *qdict) -{ - CPUState *cs; - - if (qdict_haskey(qdict, "apic-id")) { - int id = qdict_get_try_int(qdict, "apic-id", 0); - cs = cpu_by_arch_id(id); - } else { - cs = mon_get_cpu(mon); - } - - - if (!cs) { - monitor_printf(mon, "No CPU available\n"); - return; - } - x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU); -} 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..0ba31201e2 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; } @@ -64,9 +64,6 @@ static void nvmm_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/NVMM", cpu->cpu_index); qemu_thread_create(cpu->thread, thread_name, qemu_nvmm_cpu_thread_fn, diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index b75738ee9c..65768aca03 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -26,10 +26,11 @@ #include -struct qemu_vcpu { +struct AccelCPUState { struct nvmm_vcpu vcpu; uint8_t tpr; bool stop; + bool dirty; /* Window-exiting for INTs/NMIs. */ bool int_window_exit; @@ -49,12 +50,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 +79,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 +216,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 +341,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 +349,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 +366,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 +387,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 +399,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 +462,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* @@ -478,9 +472,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 +485,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(); } } @@ -514,7 +508,7 @@ nvmm_io_callback(struct nvmm_io *io) } /* Needed, otherwise infinite loop. */ - current_cpu->vcpu_dirty = false; + current_cpu->accel->dirty = false; } static void @@ -523,7 +517,7 @@ nvmm_mem_callback(struct nvmm_mem *mem) cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write); /* Needed, otherwise infinite loop. */ - current_cpu->vcpu_dirty = false; + current_cpu->accel->dirty = false; } static struct nvmm_assist_callbacks nvmm_callbacks = { @@ -565,7 +559,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 +604,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 +645,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 +677,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,16 +720,16 @@ nvmm_vcpu_loop(CPUState *cpu) return 0; } - qemu_mutex_unlock_iothread(); + bql_unlock(); cpu_exec_start(cpu); /* * Inner VCPU loop. */ do { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { nvmm_set_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } if (qcpu->stop) { @@ -812,16 +805,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); @@ -834,32 +827,32 @@ static void do_nvmm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { nvmm_get_registers(cpu); - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } static void do_nvmm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_nvmm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } void nvmm_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { run_on_cpu(cpu, do_nvmm_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -892,7 +885,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 +919,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 +928,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) { @@ -994,8 +982,8 @@ nvmm_init_vcpu(CPUState *cpu) } } - cpu->vcpu_dirty = true; - cpu->hax_vcpu = (struct hax_vcpu_state *)qcpu; + qcpu->dirty = true; + cpu->accel = qcpu; return 0; } @@ -1027,10 +1015,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 +1126,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_fpu.h b/target/i386/ops_fpu.h index 7deb487289..5f79f50412 100644 --- a/target/i386/ops_fpu.h +++ b/target/i386/ops_fpu.h @@ -22,7 +22,6 @@ #define PREC_SUFFIX glue(_, fPREC) #define PREC_TYPE glue(TCGv_, fPREC) #define tcg_temp_new_fp glue(tcg_temp_new_, fPREC) -#define tcg_temp_free_fp glue(tcg_temp_free_, fPREC) #define tcg_gen_st80f_fp glue(tcg_gen_st80f, PREC_SUFFIX) #define tcg_gen_ld80f_fp glue(tcg_gen_ld80f, PREC_SUFFIX) #define get_ft0 glue(get_ft0, PREC_SUFFIX) @@ -39,7 +38,6 @@ static PREC_TYPE get_ft0(DisasContext *s) *v = tcg_temp_new_fp(); TCGv_ptr p = gen_ft0_ptr(); tcg_gen_ld80f_fp(*v, p); - tcg_temp_free_ptr(p); } return *v; @@ -56,7 +54,6 @@ static PREC_TYPE get_stn(DisasContext *s, int opreg) *t = tcg_temp_new_fp(); TCGv_ptr p = gen_stn_ptr(opreg); tcg_gen_ld80f_fp(*t, p); - tcg_temp_free_ptr(p); } return *t; @@ -74,8 +71,6 @@ static void glue(flush_fp_regs, PREC_SUFFIX)(DisasContext *s) if (*t) { TCGv_ptr ptr = gen_stn_ptr(i); tcg_gen_st80f_fp(*t, ptr); - tcg_temp_free_fp(*t); - tcg_temp_free_ptr(ptr); *t = NULL; } } @@ -83,7 +78,6 @@ static void glue(flush_fp_regs, PREC_SUFFIX)(DisasContext *s) if (s->ft0) { TCGv_ptr ptr = gen_ft0_ptr(); tcg_gen_st80f_fp((PREC_TYPE)s->ft0, ptr); - tcg_temp_free_ptr(ptr); s->ft0 = NULL; } } @@ -92,7 +86,6 @@ static void glue(gen_fpop, PREC_SUFFIX)(DisasContext *s) { PREC_TYPE *t = (PREC_TYPE *)&s->fpregs[s->fpstt_delta & 7]; if (*t) { - tcg_temp_free_fp(*t); *t = NULL; } } @@ -123,13 +116,11 @@ static void glue(gen_fcom, PREC_SUFFIX)(DisasContext *s, PREC_TYPE arg1, tcg_gen_shli_i64(res, res, 8); TCGv_i64 fpus = tcg_temp_new_i64(); - tcg_gen_ld16u_i64(fpus, cpu_env, offsetof(CPUX86State, fpus)); + tcg_gen_ld16u_i64(fpus, tcg_env, offsetof(CPUX86State, fpus)); tcg_gen_andi_i64(fpus, fpus, ~0x4500); tcg_gen_or_i64(fpus, fpus, res); - tcg_gen_st16_i64(fpus, cpu_env, offsetof(CPUX86State, fpus)); + tcg_gen_st16_i64(fpus, tcg_env, offsetof(CPUX86State, fpus)); - tcg_temp_free_i64(fpus); - tcg_temp_free_i64(res); /* FIXME: Exceptions */ } diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index 44c1e70093..f0aa1894aa 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -19,6 +19,8 @@ */ #include "crypto/aes.h" +#include "crypto/aes-round.h" +#include "crypto/clmul.h" #if SHIFT == 0 #define Reg MMXReg @@ -1109,6 +1111,7 @@ void helper_ucomiss(CPUX86State *env, Reg *d, Reg *s) s1 = s->ZMM_S(0); ret = float32_compare_quiet(s0, s1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } void helper_comiss(CPUX86State *env, Reg *d, Reg *s) @@ -1120,6 +1123,7 @@ void helper_comiss(CPUX86State *env, Reg *d, Reg *s) s1 = s->ZMM_S(0); ret = float32_compare(s0, s1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } void helper_ucomisd(CPUX86State *env, Reg *d, Reg *s) @@ -1131,6 +1135,7 @@ void helper_ucomisd(CPUX86State *env, Reg *d, Reg *s) d1 = s->ZMM_D(0); ret = float64_compare_quiet(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } void helper_comisd(CPUX86State *env, Reg *d, Reg *s) @@ -1142,6 +1147,7 @@ void helper_comisd(CPUX86State *env, Reg *d, Reg *s) d1 = s->ZMM_D(0); ret = float64_compare(d0, d1, &env->sse_status); CC_SRC = comis_eflags[ret + 1]; + CC_OP = CC_OP_EFLAGS; } #endif @@ -1608,6 +1614,7 @@ void glue(helper_ptest, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) cf |= (s->Q(i) & ~d->Q(i)); } CC_SRC = (zf ? 0 : CC_Z) | (cf ? 0 : CC_C); + CC_OP = CC_OP_EFLAGS; } #define FMOVSLDUP(i) s->L((i) & ~1) @@ -1964,6 +1971,7 @@ static inline unsigned pcmpxstrx(CPUX86State *env, Reg *d, Reg *s, validd--; CC_SRC = (valids < upper ? CC_Z : 0) | (validd < upper ? CC_S : 0); + CC_OP = CC_OP_EFLAGS; switch ((ctrl >> 2) & 3) { case 0: @@ -2121,108 +2129,72 @@ target_ulong helper_crc32(uint32_t crc1, target_ulong msg, uint32_t len) #endif -#if SHIFT == 1 -static void clmulq(uint64_t *dest_l, uint64_t *dest_h, - uint64_t a, uint64_t b) -{ - uint64_t al, ah, resh, resl; - - ah = 0; - al = a; - resh = resl = 0; - - while (b) { - if (b & 1) { - resl ^= al; - resh ^= ah; - } - ah = (ah << 1) | (al >> 63); - al <<= 1; - b >>= 1; - } - - *dest_l = resl; - *dest_h = resh; -} -#endif - void glue(helper_pclmulqdq, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t ctrl) { - uint64_t a, b; - int i; + int a_idx = (ctrl & 1) != 0; + int b_idx = (ctrl & 16) != 0; - for (i = 0; i < 1 << SHIFT; i += 2) { - a = v->Q(((ctrl & 1) != 0) + i); - b = s->Q(((ctrl & 16) != 0) + i); - clmulq(&d->Q(i), &d->Q(i + 1), a, b); + 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); + + *r = clmul_64(a, b); } } void glue(helper_aesdec, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *v; - 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 < 2 << SHIFT ; i++) { - int j = i & 3; - d->L(i) = rk.L(i) ^ bswap32(AES_Td0[st.B(AES_ishifts[4 * j + 0])] ^ - AES_Td1[st.B(AES_ishifts[4 * j + 1])] ^ - AES_Td2[st.B(AES_ishifts[4 * j + 2])] ^ - AES_Td3[st.B(AES_ishifts[4 * j + 3])]); + aesdec_ISB_ISR_IMC_AK(ad, st, rk, false); } } void glue(helper_aesdeclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *v; - 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 < 8 << SHIFT; i++) { - d->B(i) = rk.B(i) ^ (AES_isbox[st.B(AES_ishifts[i & 15] + (i & ~15))]); + aesdec_ISB_ISR_AK(ad, st, rk, false); } } void glue(helper_aesenc, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *v; - 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 < 2 << SHIFT ; i++) { - int j = i & 3; - d->L(i) = rk.L(i) ^ bswap32(AES_Te0[st.B(AES_shifts[4 * j + 0])] ^ - AES_Te1[st.B(AES_shifts[4 * j + 1])] ^ - AES_Te2[st.B(AES_shifts[4 * j + 2])] ^ - AES_Te3[st.B(AES_shifts[4 * j + 3])]); + aesenc_SB_SR_MC_AK(ad, st, rk, false); } } void glue(helper_aesenclast, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s) { - int i; - Reg st = *v; - 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 < 8 << SHIFT; i++) { - d->B(i) = rk.B(i) ^ (AES_sbox[st.B(AES_shifts[i & 15] + (i & ~15))]); + 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,6 +2303,7 @@ void glue(helper_vtestps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) cf |= (s->L(i) & ~d->L(i)); } CC_SRC = ((zf >> 31) ? 0 : CC_Z) | ((cf >> 31) ? 0 : CC_C); + CC_OP = CC_OP_EFLAGS; } void glue(helper_vtestpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) @@ -2343,6 +2316,7 @@ void glue(helper_vtestpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) cf |= (s->Q(i) & ~d->Q(i)); } CC_SRC = ((zf >> 63) ? 0 : CC_Z) | ((cf >> 63) ? 0 : CC_C); + CC_OP = CC_OP_EFLAGS; } void glue(helper_vpmaskmovd_st, SUFFIX)(CPUX86State *env, @@ -2470,6 +2444,8 @@ void helper_vpermdq_ymm(Reg *d, Reg *v, Reg *s, uint32_t order) 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: @@ -2488,6 +2464,8 @@ void helper_vpermdq_ymm(Reg *d, Reg *v, Reg *s, uint32_t order) 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; @@ -2557,6 +2535,134 @@ 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 diff --git a/target/i386/ops_sse_header.h b/target/i386/ops_sse_header.h deleted file mode 100644 index 8a7b2f4e2f..0000000000 --- a/target/i386/ops_sse_header.h +++ /dev/null @@ -1,415 +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 -#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 - -#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/sev-sysemu-stub.c b/target/i386/sev-sysemu-stub.c index 7a29295d1e..d5bf886e79 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" @@ -43,7 +42,7 @@ void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, error_setg(errp, "SEV is not available in this QEMU"); } -int sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { g_assert_not_reached(); } @@ -68,3 +67,7 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "SEV is not available in this QEMU\n"); } + +void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size) +{ +} diff --git a/target/i386/sev.c b/target/i386/sev.c index 32f7dbac4e..1a4eb1ada6 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include +#include #include #include @@ -23,8 +24,10 @@ #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 "kvm/kvm_i386.h" #include "sev.h" #include "sysemu/sysemu.h" #include "sysemu/runstate.h" @@ -34,67 +37,14 @@ #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 "confidential-guest.h" #include "hw/i386/pc.h" #include "exec/address-spaces.h" +#include "qemu/queue.h" -#define TYPE_SEV_GUEST "sev-guest" -OBJECT_DECLARE_SIMPLE_TYPE(SevGuestState, SEV_GUEST) - - -/** - * SevGuestState: - * - * The SevGuestState object is used for creating and managing a SEV - * guest. - * - * # $QEMU \ - * -object sev-guest,id=sev0 \ - * -machine ...,memory-encryption=sev0 - */ -struct SevGuestState { - ConfidentialGuestSupport parent_obj; - - /* configuration parameters */ - char *sev_device; - uint32_t policy; - char *dh_cert_file; - char *session_file; - uint32_t cbitpos; - uint32_t reduced_phys_bits; - bool kernel_hashes; - - /* runtime state */ - uint32_t handle; - uint8_t api_major; - uint8_t api_minor; - uint8_t build_id; - int sev_fd; - SevState state; - gchar *measurement; - - uint32_t reset_cs; - uint32_t reset_ip; - bool reset_data_valid; -}; - -#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ -#define DEFAULT_SEV_DEVICE "/dev/sev" - -#define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" -typedef struct __attribute__((__packed__)) SevInfoBlock { - /* SEV-ES Reset Vector Address */ - uint32_t reset_addr; -} SevInfoBlock; - -#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454" -typedef struct QEMU_PACKED SevHashTableDescriptor { - /* SEV hash table area guest address */ - uint32_t base; - /* SEV hash table area size (in bytes) */ - uint32_t size; -} SevHashTableDescriptor; +OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) +OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) +OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST) /* hard code sha256 digest size */ #define HASH_SIZE 32 @@ -124,7 +74,111 @@ typedef struct QEMU_PACKED PaddedSevHashTable { QEMU_BUILD_BUG_ON(sizeof(PaddedSevHashTable) % 16 != 0); -static SevGuestState *sev_guest; +#define SEV_INFO_BLOCK_GUID "00f771de-1a7e-4fcb-890e-68c77e2fb44e" +typedef struct __attribute__((__packed__)) SevInfoBlock { + /* SEV-ES Reset Vector Address */ + uint32_t reset_addr; +} SevInfoBlock; + +#define SEV_HASH_TABLE_RV_GUID "7255371f-3a3b-4b04-927b-1da6efa8d454" +typedef struct QEMU_PACKED SevHashTableDescriptor { + /* SEV hash table area guest address */ + uint32_t base; + /* SEV hash table area size (in bytes) */ + uint32_t size; +} SevHashTableDescriptor; + +struct SevCommonState { + X86ConfidentialGuest parent_obj; + + int kvm_type; + + /* configuration parameters */ + char *sev_device; + uint32_t cbitpos; + uint32_t reduced_phys_bits; + bool kernel_hashes; + + /* runtime state */ + uint8_t api_major; + uint8_t api_minor; + uint8_t build_id; + int sev_fd; + SevState state; + + uint32_t reset_cs; + uint32_t reset_ip; + bool reset_data_valid; +}; + +struct SevCommonStateClass { + X86ConfidentialGuestClass parent_class; + + /* public */ + bool (*build_kernel_loader_hashes)(SevCommonState *sev_common, + SevHashTableDescriptor *area, + SevKernelLoaderContext *ctx, + Error **errp); + int (*launch_start)(SevCommonState *sev_common); + void (*launch_finish)(SevCommonState *sev_common); + int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, size_t len); + int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); +}; + +/** + * SevGuestState: + * + * The SevGuestState object is used for creating and managing a SEV + * guest. + * + * # $QEMU \ + * -object sev-guest,id=sev0 \ + * -machine ...,memory-encryption=sev0 + */ +struct SevGuestState { + SevCommonState parent_obj; + gchar *measurement; + + /* configuration parameters */ + uint32_t handle; + uint32_t policy; + char *dh_cert_file; + char *session_file; + OnOffAuto legacy_vm_type; +}; + +struct SevSnpGuestState { + SevCommonState parent_obj; + + /* configuration parameters */ + char *guest_visible_workarounds; + char *id_block_base64; + uint8_t *id_block; + char *id_auth_base64; + uint8_t *id_auth; + char *host_data; + + struct kvm_sev_snp_launch_start kvm_start_conf; + struct kvm_sev_snp_launch_finish kvm_finish_conf; + + uint32_t kernel_hashes_offset; + PaddedSevHashTable *kernel_hashes_data; +}; + +#define DEFAULT_GUEST_POLICY 0x1 /* disable debug */ +#define DEFAULT_SEV_DEVICE "/dev/sev" +#define DEFAULT_SEV_SNP_POLICY 0x30000 + +typedef struct SevLaunchUpdateData { + QTAILQ_ENTRY(SevLaunchUpdateData) next; + hwaddr gpa; + void *hva; + size_t len; + int type; +} SevLaunchUpdateData; + +static QTAILQ_HEAD(, SevLaunchUpdateData) launch_update; + static Error *sev_mig_blocker; static const char *const sev_fw_errlist[] = { @@ -157,6 +211,36 @@ static const char *const sev_fw_errlist[] = { #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) +/* doesn't expose this, so re-use the max from kvm.c */ +#define KVM_MAX_CPUID_ENTRIES 100 + +typedef struct KvmCpuidInfo { + struct kvm_cpuid2 cpuid; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; +} KvmCpuidInfo; + +#define SNP_CPUID_FUNCTION_MAXCOUNT 64 +#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF + +typedef struct { + uint32_t eax_in; + uint32_t ecx_in; + uint64_t xcr0_in; + uint64_t xss_in; + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint64_t reserved; +} __attribute__((packed)) SnpCpuidFunc; + +typedef struct { + uint32_t count; + uint32_t reserved1; + uint64_t reserved2; + SnpCpuidFunc entries[SNP_CPUID_FUNCTION_MAXCOUNT]; +} __attribute__((packed)) SnpCpuidInfo; + static int sev_ioctl(int fd, int cmd, void *data, int *error) { @@ -167,7 +251,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); @@ -205,21 +289,21 @@ fw_error_to_str(int code) } static bool -sev_check_state(const SevGuestState *sev, SevState state) +sev_check_state(const SevCommonState *sev_common, SevState state) { - assert(sev); - return sev->state == state ? true : false; + assert(sev_common); + return sev_common->state == state ? true : false; } static void -sev_set_guest_state(SevGuestState *sev, SevState new_state) +sev_set_guest_state(SevCommonState *sev_common, SevState new_state) { assert(new_state < SEV_STATE__MAX); - assert(sev); + assert(sev_common); - trace_kvm_sev_change_state(SevState_str(sev->state), + trace_kvm_sev_change_state(SevState_str(sev_common->state), SevState_str(new_state)); - sev->state = new_state; + sev_common->state = new_state; } static void @@ -240,7 +324,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 +354,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); @@ -286,168 +370,72 @@ static struct RAMBlockNotifier sev_ram_notifier = { .ram_block_removed = sev_ram_block_removed, }; -static void -sev_guest_finalize(Object *obj) -{ -} - -static char * -sev_guest_get_session_file(Object *obj, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - return s->session_file ? g_strdup(s->session_file) : NULL; -} - -static void -sev_guest_set_session_file(Object *obj, const char *value, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - s->session_file = g_strdup(value); -} - -static char * -sev_guest_get_dh_cert_file(Object *obj, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - return g_strdup(s->dh_cert_file); -} - -static void -sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) -{ - SevGuestState *s = SEV_GUEST(obj); - - s->dh_cert_file = g_strdup(value); -} - -static char * -sev_guest_get_sev_device(Object *obj, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - return g_strdup(sev->sev_device); -} - -static void -sev_guest_set_sev_device(Object *obj, const char *value, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - sev->sev_device = g_strdup(value); -} - -static bool sev_guest_get_kernel_hashes(Object *obj, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - return sev->kernel_hashes; -} - -static void sev_guest_set_kernel_hashes(Object *obj, bool value, Error **errp) -{ - SevGuestState *sev = SEV_GUEST(obj); - - sev->kernel_hashes = value; -} - -static void -sev_guest_class_init(ObjectClass *oc, void *data) -{ - object_class_property_add_str(oc, "sev-device", - sev_guest_get_sev_device, - sev_guest_set_sev_device); - object_class_property_set_description(oc, "sev-device", - "SEV device to use"); - object_class_property_add_str(oc, "dh-cert-file", - sev_guest_get_dh_cert_file, - sev_guest_set_dh_cert_file); - object_class_property_set_description(oc, "dh-cert-file", - "guest owners DH certificate (encoded with base64)"); - object_class_property_add_str(oc, "session-file", - sev_guest_get_session_file, - sev_guest_set_session_file); - object_class_property_set_description(oc, "session-file", - "guest owners session parameters (encoded with base64)"); - object_class_property_add_bool(oc, "kernel-hashes", - sev_guest_get_kernel_hashes, - sev_guest_set_kernel_hashes); - object_class_property_set_description(oc, "kernel-hashes", - "add kernel hashes to guest firmware for measured Linux boot"); -} - -static void -sev_guest_instance_init(Object *obj) -{ - SevGuestState *sev = SEV_GUEST(obj); - - sev->sev_device = g_strdup(DEFAULT_SEV_DEVICE); - sev->policy = DEFAULT_GUEST_POLICY; - object_property_add_uint32_ptr(obj, "policy", &sev->policy, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "handle", &sev->handle, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "cbitpos", &sev->cbitpos, - OBJ_PROP_FLAG_READWRITE); - object_property_add_uint32_ptr(obj, "reduced-phys-bits", - &sev->reduced_phys_bits, - OBJ_PROP_FLAG_READWRITE); -} - -/* sev guest info */ -static const TypeInfo sev_guest_info = { - .parent = TYPE_CONFIDENTIAL_GUEST_SUPPORT, - .name = TYPE_SEV_GUEST, - .instance_size = sizeof(SevGuestState), - .instance_finalize = sev_guest_finalize, - .class_init = sev_guest_class_init, - .instance_init = sev_guest_instance_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_USER_CREATABLE }, - { } - } -}; - bool sev_enabled(void) { - return !!sev_guest; + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + + return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON); +} + +bool +sev_snp_enabled(void) +{ + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + + return !!object_dynamic_cast(OBJECT(cgs), TYPE_SEV_SNP_GUEST); } bool sev_es_enabled(void) { - return sev_enabled() && (sev_guest->policy & SEV_POLICY_ES); + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + + return sev_snp_enabled() || + (sev_enabled() && SEV_GUEST(cgs)->policy & SEV_POLICY_ES); } uint32_t sev_get_cbit_position(void) { - return sev_guest ? sev_guest->cbitpos : 0; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + + return sev_common ? sev_common->cbitpos : 0; } uint32_t sev_get_reduced_phys_bits(void) { - return sev_guest ? sev_guest->reduced_phys_bits : 0; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + + return sev_common ? sev_common->reduced_phys_bits : 0; } static SevInfo *sev_get_info(void) { SevInfo *info; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); info = g_new0(SevInfo, 1); info->enabled = sev_enabled(); if (info->enabled) { - info->api_major = sev_guest->api_major; - info->api_minor = sev_guest->api_minor; - info->build_id = sev_guest->build_id; - info->policy = sev_guest->policy; - info->state = sev_guest->state; - info->handle = sev_guest->handle; + info->api_major = sev_common->api_major; + info->api_minor = sev_common->api_minor; + info->build_id = sev_common->build_id; + info->state = sev_common->state; + + if (sev_snp_enabled()) { + info->sev_type = SEV_GUEST_TYPE_SEV_SNP; + info->u.sev_snp.snp_policy = + object_property_get_uint(OBJECT(sev_common), "policy", NULL); + } else { + info->sev_type = SEV_GUEST_TYPE_SEV; + info->u.sev.handle = SEV_GUEST(sev_common)->handle; + info->u.sev.policy = + (uint32_t)object_property_get_uint(OBJECT(sev_common), + "policy", NULL); + } } return info; @@ -470,20 +458,33 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict) { SevInfo *info = sev_get_info(); - if (info && info->enabled) { - monitor_printf(mon, "handle: %d\n", info->handle); - monitor_printf(mon, "state: %s\n", SevState_str(info->state)); - monitor_printf(mon, "build: %d\n", info->build_id); - monitor_printf(mon, "api version: %d.%d\n", - info->api_major, info->api_minor); - monitor_printf(mon, "debug: %s\n", - info->policy & SEV_POLICY_NODBG ? "off" : "on"); - monitor_printf(mon, "key-sharing: %s\n", - info->policy & SEV_POLICY_NOKS ? "off" : "on"); - } else { + if (!info || !info->enabled) { monitor_printf(mon, "SEV is not enabled\n"); + goto out; } + monitor_printf(mon, "SEV type: %s\n", SevGuestType_str(info->sev_type)); + monitor_printf(mon, "state: %s\n", SevState_str(info->state)); + monitor_printf(mon, "build: %d\n", info->build_id); + monitor_printf(mon, "api version: %d.%d\n", info->api_major, + info->api_minor); + + if (sev_snp_enabled()) { + monitor_printf(mon, "debug: %s\n", + info->u.sev_snp.snp_policy & SEV_SNP_POLICY_DBG ? "on" + : "off"); + monitor_printf(mon, "SMT allowed: %s\n", + info->u.sev_snp.snp_policy & SEV_SNP_POLICY_SMT ? "on" + : "off"); + } else { + monitor_printf(mon, "handle: %d\n", info->u.sev.handle); + monitor_printf(mon, "debug: %s\n", + info->u.sev.policy & SEV_POLICY_NODBG ? "off" : "on"); + monitor_printf(mon, "key-sharing: %s\n", + info->u.sev.policy & SEV_POLICY_NOKS ? "off" : "on"); + } + +out: qapi_free_SevInfo(info); } @@ -573,6 +574,8 @@ static SevCapability *sev_get_capabilities(Error **errp) size_t pdh_len = 0, cert_chain_len = 0, cpu0_id_len = 0; uint32_t ebx; int fd; + SevCommonState *sev_common; + char *sev_device; if (!kvm_enabled()) { error_setg(errp, "KVM not enabled"); @@ -583,12 +586,22 @@ static SevCapability *sev_get_capabilities(Error **errp) return NULL; } - fd = open(DEFAULT_SEV_DEVICE, O_RDWR); + sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + if (sev_common) { + sev_device = object_property_get_str(OBJECT(sev_common), "sev-device", + &error_abort); + } else { + sev_device = g_strdup(DEFAULT_SEV_DEVICE); + } + + fd = open(sev_device, O_RDWR); if (fd < 0) { error_setg_errno(errp, errno, "SEV: Failed to open %s", - DEFAULT_SEV_DEVICE); + sev_device); + g_free(sev_device); return NULL; } + g_free(sev_device); if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, &cert_chain_data, &cert_chain_len, errp)) { @@ -626,12 +639,44 @@ SevCapability *qmp_query_sev_capabilities(Error **errp) return sev_get_capabilities(errp); } +static OvmfSevMetadata *ovmf_sev_metadata_table; + +#define OVMF_SEV_META_DATA_GUID "dc886566-984a-4798-A75e-5585a7bf67cc" +typedef struct __attribute__((__packed__)) OvmfSevMetadataOffset { + uint32_t offset; +} OvmfSevMetadataOffset; + +OvmfSevMetadata *pc_system_get_ovmf_sev_metadata_ptr(void) +{ + return ovmf_sev_metadata_table; +} + +void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size) +{ + OvmfSevMetadata *metadata; + OvmfSevMetadataOffset *data; + + if (!pc_system_ovmf_table_find(OVMF_SEV_META_DATA_GUID, (uint8_t **)&data, + NULL)) { + return; + } + + metadata = (OvmfSevMetadata *)(flash_ptr + flash_size - data->offset); + if (memcmp(metadata->signature, "ASEV", 4) != 0 || + metadata->len < sizeof(OvmfSevMetadata) || + metadata->len > flash_size - data->offset) { + return; + } + + ovmf_sev_metadata_table = g_memdup2(metadata, metadata->len); +} + static SevAttestationReport *sev_get_attestation_report(const char *mnonce, Error **errp) { struct kvm_sev_attestation_report input = {}; SevAttestationReport *report = NULL; - SevGuestState *sev = sev_guest; + SevCommonState *sev_common; g_autofree guchar *data = NULL; g_autofree guchar *buf = NULL; gsize len; @@ -656,8 +701,10 @@ static SevAttestationReport *sev_get_attestation_report(const char *mnonce, return NULL; } + sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + /* Query the report length */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, &input, &err); if (ret < 0) { if (err != SEV_RET_INVALID_LEN) { @@ -673,7 +720,7 @@ static SevAttestationReport *sev_get_attestation_report(const char *mnonce, memcpy(input.mnonce, buf, sizeof(input.mnonce)); /* Query the report */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_GET_ATTESTATION_REPORT, &input, &err); if (ret) { error_setg_errno(errp, errno, "SEV: Failed to get attestation report" @@ -713,26 +760,56 @@ sev_read_file_base64(const char *filename, guchar **data, gsize *len) } static int -sev_launch_start(SevGuestState *sev) +sev_snp_launch_start(SevCommonState *sev_common) +{ + int fw_error, rc; + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common); + struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf; + + trace_kvm_sev_snp_launch_start(start->policy, + sev_snp_guest->guest_visible_workarounds); + + if (!kvm_enable_hypercall(BIT_ULL(KVM_HC_MAP_GPA_RANGE))) { + return 1; + } + + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START, + start, &fw_error); + if (rc < 0) { + error_report("%s: SNP_LAUNCH_START ret=%d fw_error=%d '%s'", + __func__, rc, fw_error, fw_error_to_str(fw_error)); + return 1; + } + + QTAILQ_INIT(&launch_update); + + sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE); + + return 0; +} + +static int +sev_launch_start(SevCommonState *sev_common) { gsize sz; int ret = 1; int fw_error, rc; + SevGuestState *sev_guest = SEV_GUEST(sev_common); struct kvm_sev_launch_start start = { - .handle = sev->handle, .policy = sev->policy + .handle = sev_guest->handle, .policy = sev_guest->policy }; guchar *session = NULL, *dh_cert = NULL; - if (sev->session_file) { - if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { + if (sev_guest->session_file) { + if (sev_read_file_base64(sev_guest->session_file, &session, &sz) < 0) { goto out; } start.session_uaddr = (unsigned long)session; start.session_len = sz; } - if (sev->dh_cert_file) { - if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { + if (sev_guest->dh_cert_file) { + if (sev_read_file_base64(sev_guest->dh_cert_file, &dh_cert, &sz) < 0) { goto out; } start.dh_uaddr = (unsigned long)dh_cert; @@ -740,15 +817,15 @@ sev_launch_start(SevGuestState *sev) } trace_kvm_sev_launch_start(start.policy, session, dh_cert); - rc = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); if (rc < 0) { error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); goto out; } - sev_set_guest_state(sev, SEV_STATE_LAUNCH_UPDATE); - sev->handle = start.handle; + sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_UPDATE); + sev_guest->handle = start.handle; ret = 0; out: @@ -757,8 +834,152 @@ out: return ret; } +static void +sev_snp_cpuid_report_mismatches(SnpCpuidInfo *old, + SnpCpuidInfo *new) +{ + size_t i; + + if (old->count != new->count) { + error_report("SEV-SNP: CPUID validation failed due to count mismatch, " + "provided: %d, expected: %d", old->count, new->count); + return; + } + + for (i = 0; i < old->count; i++) { + SnpCpuidFunc *old_func, *new_func; + + old_func = &old->entries[i]; + new_func = &new->entries[i]; + + if (memcmp(old_func, new_func, sizeof(SnpCpuidFunc))) { + error_report("SEV-SNP: CPUID validation failed for function 0x%x, index: 0x%x, " + "provided: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x, " + "expected: eax:0x%08x, ebx: 0x%08x, ecx: 0x%08x, edx: 0x%08x", + old_func->eax_in, old_func->ecx_in, + old_func->eax, old_func->ebx, old_func->ecx, old_func->edx, + new_func->eax, new_func->ebx, new_func->ecx, new_func->edx); + } + } +} + +static const char * +snp_page_type_to_str(int type) +{ + switch (type) { + case KVM_SEV_SNP_PAGE_TYPE_NORMAL: return "Normal"; + case KVM_SEV_SNP_PAGE_TYPE_ZERO: return "Zero"; + case KVM_SEV_SNP_PAGE_TYPE_UNMEASURED: return "Unmeasured"; + case KVM_SEV_SNP_PAGE_TYPE_SECRETS: return "Secrets"; + case KVM_SEV_SNP_PAGE_TYPE_CPUID: return "Cpuid"; + default: return "unknown"; + } +} + static int -sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) +sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, + SevLaunchUpdateData *data) +{ + int ret, fw_error; + SnpCpuidInfo snp_cpuid_info; + struct kvm_sev_snp_launch_update update = {0}; + + if (!data->hva || !data->len) { + error_report("SNP_LAUNCH_UPDATE called with invalid address" + "/ length: %p / %zx", + data->hva, data->len); + return 1; + } + + if (data->type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { + /* Save a copy for comparison in case the LAUNCH_UPDATE fails */ + memcpy(&snp_cpuid_info, data->hva, sizeof(snp_cpuid_info)); + } + + update.uaddr = (__u64)(unsigned long)data->hva; + update.gfn_start = data->gpa >> TARGET_PAGE_BITS; + update.len = data->len; + update.type = data->type; + + /* + * KVM_SEV_SNP_LAUNCH_UPDATE requires that GPA ranges have the private + * memory attribute set in advance. + */ + ret = kvm_set_memory_attributes_private(data->gpa, data->len); + if (ret) { + error_report("SEV-SNP: failed to configure initial" + "private guest memory"); + goto out; + } + + while (update.len || ret == -EAGAIN) { + trace_kvm_sev_snp_launch_update(update.uaddr, update.gfn_start << + TARGET_PAGE_BITS, update.len, + snp_page_type_to_str(update.type)); + + ret = sev_ioctl(SEV_COMMON(sev_snp_guest)->sev_fd, + KVM_SEV_SNP_LAUNCH_UPDATE, + &update, &fw_error); + if (ret && ret != -EAGAIN) { + error_report("SNP_LAUNCH_UPDATE ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); + + if (data->type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { + sev_snp_cpuid_report_mismatches(&snp_cpuid_info, data->hva); + error_report("SEV-SNP: failed update CPUID page"); + } + break; + } + } + +out: + if (!ret && update.gfn_start << TARGET_PAGE_BITS != data->gpa + data->len) { + error_report("SEV-SNP: expected update of GPA range %" + HWADDR_PRIx "-%" HWADDR_PRIx "," + "got GPA range %" HWADDR_PRIx "-%llx", + data->gpa, data->gpa + data->len, data->gpa, + update.gfn_start << TARGET_PAGE_BITS); + ret = -EIO; + } + + return ret; +} + +static uint32_t +sev_snp_mask_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, + int reg, uint32_t value) +{ + switch (feature) { + case 1: + if (reg == R_ECX) { + return value & ~CPUID_EXT_TSC_DEADLINE_TIMER; + } + break; + case 7: + if (index == 0 && reg == R_EBX) { + return value & ~CPUID_7_0_EBX_TSC_ADJUST; + } + if (index == 0 && reg == R_EDX) { + return value & ~(CPUID_7_0_EDX_SPEC_CTRL | + CPUID_7_0_EDX_STIBP | + CPUID_7_0_EDX_FLUSH_L1D | + CPUID_7_0_EDX_ARCH_CAPABILITIES | + CPUID_7_0_EDX_CORE_CAPABILITY | + CPUID_7_0_EDX_SPEC_CTRL_SSBD); + } + break; + case 0x80000008: + if (reg == R_EBX) { + return value & ~CPUID_8000_0008_EBX_VIRT_SSBD; + } + break; + } + return value; +} + +static int +sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *addr, size_t len) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -767,10 +988,10 @@ 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, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", @@ -781,11 +1002,12 @@ sev_launch_update_data(SevGuestState *sev, uint8_t *addr, uint64_t len) } static int -sev_launch_update_vmsa(SevGuestState *sev) +sev_launch_update_vmsa(SevGuestState *sev_guest) { int ret, fw_error; - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); + ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, + NULL, &fw_error); if (ret) { error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); @@ -797,25 +1019,27 @@ sev_launch_update_vmsa(SevGuestState *sev) static void sev_launch_get_measure(Notifier *notifier, void *unused) { - SevGuestState *sev = sev_guest; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevGuestState *sev_guest = SEV_GUEST(sev_common); int ret, error; g_autofree guchar *data = NULL; struct kvm_sev_launch_measure measurement = {}; - if (!sev_check_state(sev, SEV_STATE_LAUNCH_UPDATE)) { + if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { return; } if (sev_es_enabled()) { /* measure all the VM save areas before getting launch_measure */ - ret = sev_launch_update_vmsa(sev); + ret = sev_launch_update_vmsa(sev_guest); if (ret) { exit(1); } + kvm_mark_guest_state_protected(); } /* query the measurement blob length */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (!measurement.len) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", @@ -827,7 +1051,7 @@ sev_launch_get_measure(Notifier *notifier, void *unused) measurement.uaddr = (unsigned long)data; /* get the measurement blob */ - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_MEASURE, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (ret) { error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", @@ -835,17 +1059,21 @@ sev_launch_get_measure(Notifier *notifier, void *unused) return; } - sev_set_guest_state(sev, SEV_STATE_LAUNCH_SECRET); + sev_set_guest_state(sev_common, SEV_STATE_LAUNCH_SECRET); /* encode the measurement value and emit the event */ - sev->measurement = g_base64_encode(data, measurement.len); - trace_kvm_sev_launch_measurement(sev->measurement); + sev_guest->measurement = g_base64_encode(data, measurement.len); + trace_kvm_sev_launch_measurement(sev_guest->measurement); } static char *sev_get_launch_measurement(void) { + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + SevGuestState *sev_guest = + (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); + if (sev_guest && - sev_guest->state >= SEV_STATE_LAUNCH_SECRET) { + SEV_COMMON(sev_guest)->state >= SEV_STATE_LAUNCH_SECRET) { return g_strdup(sev_guest->measurement); } @@ -874,153 +1102,527 @@ static Notifier sev_machine_done_notify = { }; static void -sev_launch_finish(SevGuestState *sev) +sev_launch_finish(SevCommonState *sev_common) { int ret, error; trace_kvm_sev_launch_finish(); - ret = sev_ioctl(sev->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, + &error); if (ret) { error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", __func__, ret, error, fw_error_to_str(error)); exit(1); } - sev_set_guest_state(sev, SEV_STATE_RUNNING); + sev_set_guest_state(sev_common, SEV_STATE_RUNNING); /* 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 int +snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) +{ + SevLaunchUpdateData *data; + + data = g_new0(SevLaunchUpdateData, 1); + data->gpa = gpa; + data->hva = hva; + data->len = len; + data->type = type; + + QTAILQ_INSERT_TAIL(&launch_update, data, next); + + return 0; +} + +static int +sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, size_t len) +{ + int ret = snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_NORMAL); + return ret; +} + +static int +sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, + const KvmCpuidInfo *kvm_cpuid_info) +{ + size_t i; + + if (kvm_cpuid_info->cpuid.nent > SNP_CPUID_FUNCTION_MAXCOUNT) { + error_report("SEV-SNP: CPUID entry count (%d) exceeds max (%d)", + kvm_cpuid_info->cpuid.nent, SNP_CPUID_FUNCTION_MAXCOUNT); + return -1; + } + + memset(snp_cpuid_info, 0, sizeof(*snp_cpuid_info)); + + for (i = 0; i < kvm_cpuid_info->cpuid.nent; i++) { + const struct kvm_cpuid_entry2 *kvm_cpuid_entry; + SnpCpuidFunc *snp_cpuid_entry; + + kvm_cpuid_entry = &kvm_cpuid_info->entries[i]; + snp_cpuid_entry = &snp_cpuid_info->entries[i]; + + snp_cpuid_entry->eax_in = kvm_cpuid_entry->function; + if (kvm_cpuid_entry->flags == KVM_CPUID_FLAG_SIGNIFCANT_INDEX) { + snp_cpuid_entry->ecx_in = kvm_cpuid_entry->index; + } + snp_cpuid_entry->eax = kvm_cpuid_entry->eax; + snp_cpuid_entry->ebx = kvm_cpuid_entry->ebx; + snp_cpuid_entry->ecx = kvm_cpuid_entry->ecx; + snp_cpuid_entry->edx = kvm_cpuid_entry->edx; + + /* + * Guest kernels will calculate EBX themselves using the 0xD + * subfunctions corresponding to the individual XSAVE areas, so only + * encode the base XSAVE size in the initial leaves, corresponding + * to the initial XCR0=1 state. + */ + if (snp_cpuid_entry->eax_in == 0xD && + (snp_cpuid_entry->ecx_in == 0x0 || snp_cpuid_entry->ecx_in == 0x1)) { + snp_cpuid_entry->ebx = 0x240; + snp_cpuid_entry->xcr0_in = 1; + snp_cpuid_entry->xss_in = 0; + } + } + + snp_cpuid_info->count = i; + + return 0; +} + +static int +snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) +{ + KvmCpuidInfo kvm_cpuid_info = {0}; + SnpCpuidInfo snp_cpuid_info; + CPUState *cs = first_cpu; + int ret; + uint32_t i = 0; + + assert(sizeof(snp_cpuid_info) <= cpuid_len); + + /* get the cpuid list from KVM */ + do { + kvm_cpuid_info.cpuid.nent = ++i; + ret = kvm_vcpu_ioctl(cs, KVM_GET_CPUID2, &kvm_cpuid_info); + } while (ret == -E2BIG); + + if (ret) { + error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'", + strerror(-ret)); + return 1; + } + + ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info); + if (ret) { + error_report("SEV-SNP: failed to generate CPUID table information"); + return 1; + } + + memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info)); + + return snp_launch_update_data(cpuid_addr, hva, cpuid_len, + KVM_SEV_SNP_PAGE_TYPE_CPUID); +} + +static int +snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, + void *hva, uint32_t len) +{ + int type = KVM_SEV_SNP_PAGE_TYPE_ZERO; + if (sev_snp->parent_obj.kernel_hashes) { + assert(sev_snp->kernel_hashes_data); + assert((sev_snp->kernel_hashes_offset + + sizeof(*sev_snp->kernel_hashes_data)) <= len); + memset(hva, 0, len); + memcpy(hva + sev_snp->kernel_hashes_offset, sev_snp->kernel_hashes_data, + sizeof(*sev_snp->kernel_hashes_data)); + type = KVM_SEV_SNP_PAGE_TYPE_NORMAL; + } + return snp_launch_update_data(addr, hva, len, type); +} + +static int +snp_metadata_desc_to_page_type(int desc_type) +{ + switch (desc_type) { + /* Add the umeasured prevalidated pages as a zero page */ + case SEV_DESC_TYPE_SNP_SEC_MEM: return KVM_SEV_SNP_PAGE_TYPE_ZERO; + case SEV_DESC_TYPE_SNP_SECRETS: return KVM_SEV_SNP_PAGE_TYPE_SECRETS; + case SEV_DESC_TYPE_CPUID: return KVM_SEV_SNP_PAGE_TYPE_CPUID; + default: + return KVM_SEV_SNP_PAGE_TYPE_ZERO; + } } static void -sev_vm_state_change(void *opaque, bool running, RunState state) +snp_populate_metadata_pages(SevSnpGuestState *sev_snp, + OvmfSevMetadata *metadata) { - SevGuestState *sev = opaque; + OvmfSevMetadataDesc *desc; + int type, ret, i; + void *hva; + MemoryRegion *mr = NULL; - if (running) { - if (!sev_check_state(sev, SEV_STATE_RUNNING)) { - sev_launch_finish(sev); + for (i = 0; i < metadata->num_desc; i++) { + desc = &metadata->descs[i]; + + type = snp_metadata_desc_to_page_type(desc->type); + + hva = gpa2hva(&mr, desc->base, desc->len, NULL); + if (!hva) { + error_report("%s: Failed to get HVA for GPA 0x%x sz 0x%x", + __func__, desc->base, desc->len); + exit(1); + } + + if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { + ret = snp_launch_update_cpuid(desc->base, hva, desc->len); + } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) { + ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva, + desc->len); + } else { + ret = snp_launch_update_data(desc->base, hva, desc->len, type); + } + + if (ret) { + error_report("%s: Failed to add metadata page gpa 0x%x+%x type %d", + __func__, desc->base, desc->len, desc->type); + exit(1); } } } -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +static void +sev_snp_launch_finish(SevCommonState *sev_common) +{ + int ret, error; + Error *local_err = NULL; + OvmfSevMetadata *metadata; + SevLaunchUpdateData *data; + SevSnpGuestState *sev_snp = SEV_SNP_GUEST(sev_common); + struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf; + + /* + * To boot the SNP guest, the hypervisor is required to populate the CPUID + * and Secrets page before finalizing the launch flow. The location of + * the secrets and CPUID page is available through the OVMF metadata GUID. + */ + metadata = pc_system_get_ovmf_sev_metadata_ptr(); + if (metadata == NULL) { + error_report("%s: Failed to locate SEV metadata header", __func__); + exit(1); + } + + /* Populate all the metadata pages */ + snp_populate_metadata_pages(sev_snp, metadata); + + QTAILQ_FOREACH(data, &launch_update, next) { + ret = sev_snp_launch_update(sev_snp, data); + if (ret) { + exit(1); + } + } + + trace_kvm_sev_snp_launch_finish(sev_snp->id_block_base64, sev_snp->id_auth_base64, + sev_snp->host_data); + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, + finish, &error); + if (ret) { + error_report("SNP_LAUNCH_FINISH ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(error)); + exit(1); + } + + kvm_mark_guest_state_protected(); + sev_set_guest_state(sev_common, SEV_STATE_RUNNING); + + /* add migration blocker */ + error_setg(&sev_mig_blocker, + "SEV-SNP: Migration is not implemented"); + ret = migrate_add_blocker(&sev_mig_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(sev_mig_blocker); + exit(1); + } +} + + +static void +sev_vm_state_change(void *opaque, bool running, RunState state) +{ + SevCommonState *sev_common = opaque; + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(opaque); + + if (running) { + if (!sev_check_state(sev_common, SEV_STATE_RUNNING)) { + klass->launch_finish(sev_common); + } + } +} + +/* + * This helper is to examine sev-guest properties and determine if any options + * have been set which rely on the newer KVM_SEV_INIT2 interface and associated + * KVM VM types. + */ +static bool sev_init2_required(SevGuestState *sev_guest) +{ + /* Currently no KVM_SEV_INIT2-specific options are exposed via QEMU */ + return false; +} + +static int sev_kvm_type(X86ConfidentialGuest *cg) +{ + SevCommonState *sev_common = SEV_COMMON(cg); + SevGuestState *sev_guest = SEV_GUEST(sev_common); + int kvm_type; + + if (sev_common->kvm_type != -1) { + goto out; + } + + /* These are the only cases where legacy VM types can be used. */ + if (sev_guest->legacy_vm_type == ON_OFF_AUTO_ON || + (sev_guest->legacy_vm_type == ON_OFF_AUTO_AUTO && + !sev_init2_required(sev_guest))) { + sev_common->kvm_type = KVM_X86_DEFAULT_VM; + goto out; + } + + /* + * Newer VM types are required, either explicitly via legacy-vm-type=on, or + * implicitly via legacy-vm-type=auto along with additional sev-guest + * properties that require the newer VM types. + */ + kvm_type = (sev_guest->policy & SEV_POLICY_ES) ? + KVM_X86_SEV_ES_VM : KVM_X86_SEV_VM; + if (!kvm_is_vm_type_supported(kvm_type)) { + if (sev_guest->legacy_vm_type == ON_OFF_AUTO_AUTO) { + error_report("SEV: host kernel does not support requested %s VM type, which is required " + "for the set of options specified. To allow use of the legacy " + "KVM_X86_DEFAULT_VM VM type, please disable any options that are not " + "compatible with the legacy VM type, or upgrade your kernel.", + kvm_type == KVM_X86_SEV_VM ? "KVM_X86_SEV_VM" : "KVM_X86_SEV_ES_VM"); + } else { + error_report("SEV: host kernel does not support requested %s VM type. To allow use of " + "the legacy KVM_X86_DEFAULT_VM VM type, the 'legacy-vm-type' argument " + "must be set to 'on' or 'auto' for the sev-guest object.", + kvm_type == KVM_X86_SEV_VM ? "KVM_X86_SEV_VM" : "KVM_X86_SEV_ES_VM"); + } + + return -1; + } + + sev_common->kvm_type = kvm_type; +out: + return sev_common->kvm_type; +} + +static int sev_snp_kvm_type(X86ConfidentialGuest *cg) +{ + return KVM_X86_SNP_VM; +} + +static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { - SevGuestState *sev - = (SevGuestState *)object_dynamic_cast(OBJECT(cgs), TYPE_SEV_GUEST); char *devname; int ret, fw_error, cmd; uint32_t ebx; uint32_t host_cbitpos; struct sev_user_data_status status = {}; + SevCommonState *sev_common = SEV_COMMON(cgs); + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(cgs); + X86ConfidentialGuestClass *x86_klass = + X86_CONFIDENTIAL_GUEST_GET_CLASS(cgs); - if (!sev) { - return 0; - } - - ret = ram_block_discard_disable(true); - if (ret) { - error_report("%s: cannot disable RAM discard", __func__); - return -1; - } - - sev_guest = sev; - sev->state = SEV_STATE_UNINIT; + sev_common->state = SEV_STATE_UNINIT; host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); host_cbitpos = ebx & 0x3f; - if (host_cbitpos != sev->cbitpos) { + /* + * 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_common->cbitpos) { error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", - __func__, host_cbitpos, sev->cbitpos); - goto err; + __func__, host_cbitpos, sev_common->cbitpos); + return -1; } - 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); - goto err; + /* + * 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_common->reduced_phys_bits < 1 || + sev_common->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_common->reduced_phys_bits); + return -1; } - devname = object_property_get_str(OBJECT(sev), "sev-device", NULL); - sev->sev_fd = open(devname, O_RDWR); - if (sev->sev_fd < 0) { + devname = object_property_get_str(OBJECT(sev_common), "sev-device", NULL); + sev_common->sev_fd = open(devname, O_RDWR); + if (sev_common->sev_fd < 0) { error_setg(errp, "%s: Failed to open %s '%s'", __func__, devname, strerror(errno)); g_free(devname); - goto err; + return -1; } g_free(devname); - ret = sev_platform_ioctl(sev->sev_fd, SEV_PLATFORM_STATUS, &status, + ret = sev_platform_ioctl(sev_common->sev_fd, SEV_PLATFORM_STATUS, &status, &fw_error); if (ret) { error_setg(errp, "%s: failed to get platform status ret=%d " "fw_error='%d: %s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); - goto err; + return -1; } - sev->build_id = status.build; - sev->api_major = status.api_major; - sev->api_minor = status.api_minor; + sev_common->build_id = status.build; + sev_common->api_major = status.api_major; + sev_common->api_minor = status.api_minor; if (sev_es_enabled()) { if (!kvm_kernel_irqchip_allowed()) { - error_report("%s: SEV-ES guests require in-kernel irqchip support", - __func__); - goto err; + error_setg(errp, "%s: SEV-ES guests require in-kernel irqchip" + "support", __func__); + return -1; } + } + if (sev_es_enabled() && !sev_snp_enabled()) { if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { - error_report("%s: guest policy requires SEV-ES, but " + error_setg(errp, "%s: guest policy requires SEV-ES, but " "host SEV-ES support unavailable", __func__); - goto err; + return -1; } - cmd = KVM_SEV_ES_INIT; - } else { - cmd = KVM_SEV_INIT; } trace_kvm_sev_init(); - ret = sev_ioctl(sev->sev_fd, cmd, NULL, &fw_error); + switch (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common))) { + case KVM_X86_DEFAULT_VM: + cmd = sev_es_enabled() ? KVM_SEV_ES_INIT : KVM_SEV_INIT; + + ret = sev_ioctl(sev_common->sev_fd, cmd, NULL, &fw_error); + break; + case KVM_X86_SEV_VM: + case KVM_X86_SEV_ES_VM: + case KVM_X86_SNP_VM: { + struct kvm_sev_init args = { 0 }; + + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); + break; + } + default: + error_setg(errp, "%s: host kernel does not support the requested SEV configuration.", + __func__); + return -1; + } + if (ret) { error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); - goto err; + return -1; } - ret = sev_launch_start(sev); + ret = klass->launch_start(sev_common); + if (ret) { error_setg(errp, "%s: failed to create encryption context", __func__); - goto err; + return -1; } - ram_block_notifier_add(&sev_ram_notifier); - qemu_add_machine_init_done_notifier(&sev_machine_done_notify); - qemu_add_vm_change_state_handler(sev_vm_state_change, sev); + if (klass->kvm_init && klass->kvm_init(cgs, errp)) { + return -1; + } + + qemu_add_vm_change_state_handler(sev_vm_state_change, sev_common); cgs->ready = true; return 0; -err: - sev_guest = NULL; - ram_block_discard_disable(false); - return -1; +} + +static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + int ret; + + /* + * SEV/SEV-ES rely on pinned memory to back guest RAM so discarding + * isn't actually possible. With SNP, only guest_memfd pages are used + * for private guest memory, so discarding of shared memory is still + * possible.. + */ + ret = ram_block_discard_disable(true); + if (ret) { + error_setg(errp, "%s: cannot disable RAM discard", __func__); + return -1; + } + + /* + * SEV uses these notifiers to register/pin pages prior to guest use, + * but SNP relies on guest_memfd for private pages, which has its + * own internal mechanisms for registering/pinning private memory. + */ + ram_block_notifier_add(&sev_ram_notifier); + + /* + * The machine done notify event is used for SEV guests to get the + * measurement of the encrypted images. When SEV-SNP is enabled, the + * measurement is part of the guest attestation process where it can + * be collected without any reliance on the VMM. So skip registering + * the notifier for SNP in favor of using guest attestation instead. + */ + qemu_add_machine_init_done_notifier(&sev_machine_done_notify); + + return 0; +} + +static int sev_snp_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + X86MachineState *x86ms = X86_MACHINE(ms); + + if (x86ms->smm == ON_OFF_AUTO_AUTO) { + x86ms->smm = ON_OFF_AUTO_OFF; + } else if (x86ms->smm == ON_OFF_AUTO_ON) { + error_setg(errp, "SEV-SNP does not support SMM."); + return -1; + } + + return 0; } int -sev_encrypt_flash(uint8_t *ptr, uint64_t len, Error **errp) +sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { - if (!sev_guest) { + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevCommonStateClass *klass; + + if (!sev_common) { return 0; } + klass = SEV_COMMON_GET_CLASS(sev_common); /* if SEV is in update state then encrypt the data else do nothing */ - if (sev_check_state(sev_guest, SEV_STATE_LAUNCH_UPDATE)) { - int ret = sev_launch_update_data(sev_guest, ptr, len); + if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { + int ret; + + ret = klass->launch_update_data(sev_common, gpa, ptr, len); if (ret < 0) { error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; @@ -1033,22 +1635,24 @@ 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; void *hva; gsize hdr_sz = 0, data_sz = 0; MemoryRegion *mr = NULL; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); - if (!sev_guest) { + if (!sev_common) { error_setg(errp, "SEV not enabled for guest"); return 1; } /* secret can be injected only in this state */ - if (!sev_check_state(sev_guest, SEV_STATE_LAUNCH_SECRET)) { + if (!sev_check_state(sev_common, SEV_STATE_LAUNCH_SECRET)) { error_setg(errp, "SEV: Not in correct state. (LSECRET) %x", - sev_guest->state); + sev_common->state); return 1; } @@ -1082,7 +1686,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, trace_kvm_sev_launch_secret(gpa, input.guest_uaddr, input.trans_uaddr, input.trans_len); - ret = sev_ioctl(sev_guest->sev_fd, KVM_SEV_LAUNCH_SECRET, + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_SECRET, &input, &error); if (ret) { error_setg(errp, "SEV: failed to inject secret ret=%d fw_error=%d '%s'", @@ -1189,9 +1793,12 @@ void sev_es_set_reset_vector(CPUState *cpu) { X86CPU *x86; CPUX86State *env; + ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; + SevCommonState *sev_common = SEV_COMMON( + object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON)); /* Only update if we have valid reset information */ - if (!sev_guest || !sev_guest->reset_data_valid) { + if (!sev_common || !sev_common->reset_data_valid) { return; } @@ -1203,11 +1810,11 @@ void sev_es_set_reset_vector(CPUState *cpu) x86 = X86_CPU(cpu); env = &x86->env; - cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_guest->reset_cs, 0xffff, + cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_common->reset_cs, 0xffff, DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - env->eip = sev_guest->reset_ip; + env->eip = sev_common->reset_ip; } int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) @@ -1215,6 +1822,7 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) CPUState *cpu; uint32_t addr; int ret; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_es_enabled()) { return 0; @@ -1228,9 +1836,9 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) } if (addr) { - sev_guest->reset_cs = addr & 0xffff0000; - sev_guest->reset_ip = addr & 0x0000ffff; - sev_guest->reset_data_valid = true; + sev_common->reset_cs = addr & 0xffff0000; + sev_common->reset_ip = addr & 0x0000ffff; + sev_common->reset_data_valid = true; CPU_FOREACH(cpu) { sev_es_set_reset_vector(cpu); @@ -1258,44 +1866,16 @@ static const QemuUUID sev_cmdline_entry_guid = { 0x4d, 0x36, 0xab, 0x2a) }; -/* - * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page - * which is included in SEV's initial memory measurement. - */ -bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +static bool build_kernel_loader_hashes(PaddedSevHashTable *padded_ht, + SevKernelLoaderContext *ctx, + Error **errp) { - uint8_t *data; - SevHashTableDescriptor *area; SevHashTable *ht; - PaddedSevHashTable *padded_ht; uint8_t cmdline_hash[HASH_SIZE]; uint8_t initrd_hash[HASH_SIZE]; uint8_t kernel_hash[HASH_SIZE]; uint8_t *hashp; size_t hash_len = HASH_SIZE; - hwaddr mapped_len = sizeof(*padded_ht); - MemTxAttrs attrs = { 0 }; - bool ret = true; - - /* - * Only add the kernel hashes if the sev-guest configuration explicitly - * stated kernel-hashes=on. - */ - if (!sev_guest->kernel_hashes) { - return false; - } - - if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { - error_setg(errp, "SEV: kernel specified but guest firmware " - "has no hashes table GUID"); - return false; - } - area = (SevHashTableDescriptor *)data; - if (!area->base || area->size < sizeof(PaddedSevHashTable)) { - error_setg(errp, "SEV: guest firmware hashes table area is invalid " - "(base=0x%x size=0x%x)", area->base, area->size); - return false; - } /* * Calculate hash of kernel command-line with the terminating null byte. If @@ -1303,7 +1883,7 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) * be used. */ hashp = cmdline_hash; - if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->cmdline_data, + if (qcrypto_hash_bytes(QCRYPTO_HASH_ALGO_SHA256, ctx->cmdline_data, ctx->cmdline_size, &hashp, &hash_len, errp) < 0) { return false; } @@ -1314,7 +1894,7 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) * -initrd, an empty buffer will be used (ctx->initrd_size == 0). */ hashp = initrd_hash; - if (qcrypto_hash_bytes(QCRYPTO_HASH_ALG_SHA256, ctx->initrd_data, + if (qcrypto_hash_bytes(QCRYPTO_HASH_ALGO_SHA256, ctx->initrd_data, ctx->initrd_size, &hashp, &hash_len, errp) < 0) { return false; } @@ -1326,22 +1906,12 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) { .iov_base = ctx->setup_data, .iov_len = ctx->setup_size }, { .iov_base = ctx->kernel_data, .iov_len = ctx->kernel_size } }; - if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALG_SHA256, iov, ARRAY_SIZE(iov), + if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA256, iov, ARRAY_SIZE(iov), &hashp, &hash_len, errp) < 0) { return false; } assert(hash_len == HASH_SIZE); - /* - * Populate the hashes table in the guest's memory at the OVMF-designated - * area for the SEV hashes table - */ - padded_ht = address_space_map(&address_space_memory, area->base, - &mapped_len, true, attrs); - if (!padded_ht || mapped_len != sizeof(*padded_ht)) { - error_setg(errp, "SEV: cannot map hashes table guest memory area"); - return false; - } ht = &padded_ht->ht; ht->guid = sev_hash_table_header_guid; @@ -1362,7 +1932,52 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) /* zero the excess data so the measurement can be reliably calculated */ memset(padded_ht->padding, 0, sizeof(padded_ht->padding)); - if (sev_encrypt_flash((uint8_t *)padded_ht, sizeof(*padded_ht), errp) < 0) { + return true; +} + +static bool sev_snp_build_kernel_loader_hashes(SevCommonState *sev_common, + SevHashTableDescriptor *area, + SevKernelLoaderContext *ctx, + Error **errp) +{ + /* + * SNP: Populate the hashes table in an area that later in + * snp_launch_update_kernel_hashes() will be copied to the guest memory + * and encrypted. + */ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(sev_common); + sev_snp_guest->kernel_hashes_offset = area->base & ~TARGET_PAGE_MASK; + sev_snp_guest->kernel_hashes_data = g_new0(PaddedSevHashTable, 1); + return build_kernel_loader_hashes(sev_snp_guest->kernel_hashes_data, ctx, errp); +} + +static bool sev_build_kernel_loader_hashes(SevCommonState *sev_common, + SevHashTableDescriptor *area, + SevKernelLoaderContext *ctx, + Error **errp) +{ + PaddedSevHashTable *padded_ht; + hwaddr mapped_len = sizeof(*padded_ht); + MemTxAttrs attrs = { 0 }; + bool ret = true; + + /* + * Populate the hashes table in the guest's memory at the OVMF-designated + * area for the SEV hashes table + */ + padded_ht = address_space_map(&address_space_memory, area->base, + &mapped_len, true, attrs); + if (!padded_ht || mapped_len != sizeof(*padded_ht)) { + error_setg(errp, "SEV: cannot map hashes table guest memory area"); + return false; + } + + if (build_kernel_loader_hashes(padded_ht, ctx, errp)) { + if (sev_encrypt_flash(area->base, (uint8_t *)padded_ht, + sizeof(*padded_ht), errp) < 0) { + ret = false; + } + } else { ret = false; } @@ -1372,10 +1987,476 @@ bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) return ret; } +/* + * Add the hashes of the linux kernel/initrd/cmdline to an encrypted guest page + * which is included in SEV's initial memory measurement. + */ +bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp) +{ + uint8_t *data; + SevHashTableDescriptor *area; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); + + /* + * Only add the kernel hashes if the sev-guest configuration explicitly + * stated kernel-hashes=on. + */ + if (!sev_common->kernel_hashes) { + return false; + } + + if (!pc_system_ovmf_table_find(SEV_HASH_TABLE_RV_GUID, &data, NULL)) { + error_setg(errp, "SEV: kernel specified but guest firmware " + "has no hashes table GUID"); + return false; + } + + area = (SevHashTableDescriptor *)data; + if (!area->base || area->size < sizeof(PaddedSevHashTable)) { + error_setg(errp, "SEV: guest firmware hashes table area is invalid " + "(base=0x%x size=0x%x)", area->base, area->size); + return false; + } + + return klass->build_kernel_loader_hashes(sev_common, area, ctx, errp); +} + +static char * +sev_common_get_sev_device(Object *obj, Error **errp) +{ + return g_strdup(SEV_COMMON(obj)->sev_device); +} + +static void +sev_common_set_sev_device(Object *obj, const char *value, Error **errp) +{ + SEV_COMMON(obj)->sev_device = g_strdup(value); +} + +static bool sev_common_get_kernel_hashes(Object *obj, Error **errp) +{ + return SEV_COMMON(obj)->kernel_hashes; +} + +static void sev_common_set_kernel_hashes(Object *obj, bool value, Error **errp) +{ + SEV_COMMON(obj)->kernel_hashes = value; +} + +static void +sev_common_class_init(ObjectClass *oc, void *data) +{ + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = sev_common_kvm_init; + + object_class_property_add_str(oc, "sev-device", + sev_common_get_sev_device, + sev_common_set_sev_device); + object_class_property_set_description(oc, "sev-device", + "SEV device to use"); + object_class_property_add_bool(oc, "kernel-hashes", + sev_common_get_kernel_hashes, + sev_common_set_kernel_hashes); + object_class_property_set_description(oc, "kernel-hashes", + "add kernel hashes to guest firmware for measured Linux boot"); +} + +static void +sev_common_instance_init(Object *obj) +{ + SevCommonState *sev_common = SEV_COMMON(obj); + + sev_common->kvm_type = -1; + + sev_common->sev_device = g_strdup(DEFAULT_SEV_DEVICE); + + object_property_add_uint32_ptr(obj, "cbitpos", &sev_common->cbitpos, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint32_ptr(obj, "reduced-phys-bits", + &sev_common->reduced_phys_bits, + OBJ_PROP_FLAG_READWRITE); +} + +/* sev guest info common to sev/sev-es/sev-snp */ +static const TypeInfo sev_common_info = { + .parent = TYPE_X86_CONFIDENTIAL_GUEST, + .name = TYPE_SEV_COMMON, + .instance_size = sizeof(SevCommonState), + .instance_init = sev_common_instance_init, + .class_size = sizeof(SevCommonStateClass), + .class_init = sev_common_class_init, + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static char * +sev_guest_get_dh_cert_file(Object *obj, Error **errp) +{ + return g_strdup(SEV_GUEST(obj)->dh_cert_file); +} + +static void +sev_guest_set_dh_cert_file(Object *obj, const char *value, Error **errp) +{ + SEV_GUEST(obj)->dh_cert_file = g_strdup(value); +} + +static char * +sev_guest_get_session_file(Object *obj, Error **errp) +{ + SevGuestState *sev_guest = SEV_GUEST(obj); + + return sev_guest->session_file ? g_strdup(sev_guest->session_file) : NULL; +} + +static void +sev_guest_set_session_file(Object *obj, const char *value, Error **errp) +{ + SEV_GUEST(obj)->session_file = g_strdup(value); +} + +static void sev_guest_get_legacy_vm_type(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + SevGuestState *sev_guest = SEV_GUEST(obj); + OnOffAuto legacy_vm_type = sev_guest->legacy_vm_type; + + visit_type_OnOffAuto(v, name, &legacy_vm_type, errp); +} + +static void sev_guest_set_legacy_vm_type(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + SevGuestState *sev_guest = SEV_GUEST(obj); + + visit_type_OnOffAuto(v, name, &sev_guest->legacy_vm_type, errp); +} + +static void +sev_guest_class_init(ObjectClass *oc, void *data) +{ + SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + + klass->build_kernel_loader_hashes = sev_build_kernel_loader_hashes; + klass->launch_start = sev_launch_start; + klass->launch_finish = sev_launch_finish; + klass->launch_update_data = sev_launch_update_data; + klass->kvm_init = sev_kvm_init; + x86_klass->kvm_type = sev_kvm_type; + + object_class_property_add_str(oc, "dh-cert-file", + sev_guest_get_dh_cert_file, + sev_guest_set_dh_cert_file); + object_class_property_set_description(oc, "dh-cert-file", + "guest owners DH certificate (encoded with base64)"); + object_class_property_add_str(oc, "session-file", + sev_guest_get_session_file, + sev_guest_set_session_file); + object_class_property_set_description(oc, "session-file", + "guest owners session parameters (encoded with base64)"); + object_class_property_add(oc, "legacy-vm-type", "OnOffAuto", + sev_guest_get_legacy_vm_type, + sev_guest_set_legacy_vm_type, NULL, NULL); + object_class_property_set_description(oc, "legacy-vm-type", + "use legacy VM type to maintain measurement compatibility with older QEMU or kernel versions."); +} + +static void +sev_guest_instance_init(Object *obj) +{ + SevGuestState *sev_guest = SEV_GUEST(obj); + + sev_guest->policy = DEFAULT_GUEST_POLICY; + object_property_add_uint32_ptr(obj, "handle", &sev_guest->handle, + OBJ_PROP_FLAG_READWRITE); + object_property_add_uint32_ptr(obj, "policy", &sev_guest->policy, + OBJ_PROP_FLAG_READWRITE); + object_apply_compat_props(obj); + + sev_guest->legacy_vm_type = ON_OFF_AUTO_AUTO; +} + +/* guest info specific sev/sev-es */ +static const TypeInfo sev_guest_info = { + .parent = TYPE_SEV_COMMON, + .name = TYPE_SEV_GUEST, + .instance_size = sizeof(SevGuestState), + .instance_init = sev_guest_instance_init, + .class_init = sev_guest_class_init, +}; + +static void +sev_snp_guest_get_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint64(v, name, + (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy, + errp); +} + +static void +sev_snp_guest_set_policy(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + visit_type_uint64(v, name, + (uint64_t *)&SEV_SNP_GUEST(obj)->kvm_start_conf.policy, + errp); +} + +static char * +sev_snp_guest_get_guest_visible_workarounds(Object *obj, Error **errp) +{ + return g_strdup(SEV_SNP_GUEST(obj)->guest_visible_workarounds); +} + +static void +sev_snp_guest_set_guest_visible_workarounds(Object *obj, const char *value, + Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_start *start = &sev_snp_guest->kvm_start_conf; + g_autofree guchar *blob; + gsize len; + + g_free(sev_snp_guest->guest_visible_workarounds); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->guest_visible_workarounds = g_strdup(value); + + blob = qbase64_decode(sev_snp_guest->guest_visible_workarounds, + -1, &len, errp); + if (!blob) { + return; + } + + if (len != sizeof(start->gosvw)) { + error_setg(errp, "parameter length of %" G_GSIZE_FORMAT + " exceeds max of %zu", + len, sizeof(start->gosvw)); + return; + } + + memcpy(start->gosvw, blob, len); +} + +static char * +sev_snp_guest_get_id_block(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return g_strdup(sev_snp_guest->id_block_base64); +} + +static void +sev_snp_guest_set_id_block(Object *obj, const char *value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + gsize len; + + finish->id_block_en = 0; + g_free(sev_snp_guest->id_block); + g_free(sev_snp_guest->id_block_base64); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->id_block_base64 = g_strdup(value); + sev_snp_guest->id_block = + qbase64_decode(sev_snp_guest->id_block_base64, -1, &len, errp); + + if (!sev_snp_guest->id_block) { + return; + } + + if (len != KVM_SEV_SNP_ID_BLOCK_SIZE) { + error_setg(errp, "parameter length of %" G_GSIZE_FORMAT + " not equal to %u", + len, KVM_SEV_SNP_ID_BLOCK_SIZE); + return; + } + + finish->id_block_en = 1; + finish->id_block_uaddr = (uintptr_t)sev_snp_guest->id_block; +} + +static char * +sev_snp_guest_get_id_auth(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return g_strdup(sev_snp_guest->id_auth_base64); +} + +static void +sev_snp_guest_set_id_auth(Object *obj, const char *value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + gsize len; + + finish->id_auth_uaddr = 0; + g_free(sev_snp_guest->id_auth); + g_free(sev_snp_guest->id_auth_base64); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->id_auth_base64 = g_strdup(value); + sev_snp_guest->id_auth = + qbase64_decode(sev_snp_guest->id_auth_base64, -1, &len, errp); + + if (!sev_snp_guest->id_auth) { + return; + } + + if (len > KVM_SEV_SNP_ID_AUTH_SIZE) { + error_setg(errp, "parameter length:ID_AUTH %" G_GSIZE_FORMAT + " exceeds max of %u", + len, KVM_SEV_SNP_ID_AUTH_SIZE); + return; + } + + finish->id_auth_uaddr = (uintptr_t)sev_snp_guest->id_auth; +} + +static bool +sev_snp_guest_get_author_key_enabled(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return !!sev_snp_guest->kvm_finish_conf.auth_key_en; +} + +static void +sev_snp_guest_set_author_key_enabled(Object *obj, bool value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + sev_snp_guest->kvm_finish_conf.auth_key_en = value; +} + +static bool +sev_snp_guest_get_vcek_disabled(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return !!sev_snp_guest->kvm_finish_conf.vcek_disabled; +} + +static void +sev_snp_guest_set_vcek_disabled(Object *obj, bool value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + sev_snp_guest->kvm_finish_conf.vcek_disabled = value; +} + +static char * +sev_snp_guest_get_host_data(Object *obj, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + return g_strdup(sev_snp_guest->host_data); +} + +static void +sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) +{ + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + struct kvm_sev_snp_launch_finish *finish = &sev_snp_guest->kvm_finish_conf; + g_autofree guchar *blob; + gsize len; + + g_free(sev_snp_guest->host_data); + + /* store the base64 str so we don't need to re-encode in getter */ + sev_snp_guest->host_data = g_strdup(value); + + blob = qbase64_decode(sev_snp_guest->host_data, -1, &len, errp); + + if (!blob) { + return; + } + + if (len != sizeof(finish->host_data)) { + error_setg(errp, "parameter length of %" G_GSIZE_FORMAT + " not equal to %zu", + len, sizeof(finish->host_data)); + return; + } + + memcpy(finish->host_data, blob, len); +} + +static void +sev_snp_guest_class_init(ObjectClass *oc, void *data) +{ + SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + + klass->build_kernel_loader_hashes = sev_snp_build_kernel_loader_hashes; + klass->launch_start = sev_snp_launch_start; + klass->launch_finish = sev_snp_launch_finish; + klass->launch_update_data = sev_snp_launch_update_data; + klass->kvm_init = sev_snp_kvm_init; + x86_klass->mask_cpuid_features = sev_snp_mask_cpuid_features; + x86_klass->kvm_type = sev_snp_kvm_type; + + object_class_property_add(oc, "policy", "uint64", + sev_snp_guest_get_policy, + sev_snp_guest_set_policy, NULL, NULL); + object_class_property_add_str(oc, "guest-visible-workarounds", + sev_snp_guest_get_guest_visible_workarounds, + sev_snp_guest_set_guest_visible_workarounds); + object_class_property_add_str(oc, "id-block", + sev_snp_guest_get_id_block, + sev_snp_guest_set_id_block); + object_class_property_add_str(oc, "id-auth", + sev_snp_guest_get_id_auth, + sev_snp_guest_set_id_auth); + object_class_property_add_bool(oc, "author-key-enabled", + sev_snp_guest_get_author_key_enabled, + sev_snp_guest_set_author_key_enabled); + object_class_property_add_bool(oc, "vcek-disabled", + sev_snp_guest_get_vcek_disabled, + sev_snp_guest_set_vcek_disabled); + object_class_property_add_str(oc, "host-data", + sev_snp_guest_get_host_data, + sev_snp_guest_set_host_data); +} + +static void +sev_snp_guest_instance_init(Object *obj) +{ + ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj); + SevSnpGuestState *sev_snp_guest = SEV_SNP_GUEST(obj); + + cgs->require_guest_memfd = true; + + /* default init/start/finish params for kvm */ + sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY; +} + +/* guest info specific to sev-snp */ +static const TypeInfo sev_snp_guest_info = { + .parent = TYPE_SEV_COMMON, + .name = TYPE_SEV_SNP_GUEST, + .instance_size = sizeof(SevSnpGuestState), + .class_init = sev_snp_guest_class_init, + .instance_init = sev_snp_guest_instance_init, +}; + static void sev_register_types(void) { + type_register_static(&sev_common_info); type_register_static(&sev_guest_info); + type_register_static(&sev_snp_guest_info); } type_init(sev_register_types); diff --git a/target/i386/sev.h b/target/i386/sev.h index 7b1528248a..858005a119 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -20,6 +20,10 @@ #include "exec/confidential-guest-support.h" +#define TYPE_SEV_COMMON "sev-common" +#define TYPE_SEV_GUEST "sev-guest" +#define TYPE_SEV_SNP_GUEST "sev-snp-guest" + #define SEV_POLICY_NODBG 0x1 #define SEV_POLICY_NOKS 0x2 #define SEV_POLICY_ES 0x4 @@ -27,6 +31,9 @@ #define SEV_POLICY_DOMAIN 0x10 #define SEV_POLICY_SEV 0x20 +#define SEV_SNP_POLICY_SMT 0x10000 +#define SEV_SNP_POLICY_DBG 0x80000 + typedef struct SevKernelLoaderContext { char *setup_data; size_t setup_size; @@ -41,22 +48,24 @@ typedef struct SevKernelLoaderContext { #ifdef CONFIG_SEV bool sev_enabled(void); bool sev_es_enabled(void); +bool sev_snp_enabled(void); #else #define sev_enabled() 0 #define sev_es_enabled() 0 +#define sev_snp_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_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp); int sev_inject_launch_secret(const char *hdr, const char *secret, uint64_t gpa, Error **errp); int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size); void sev_es_set_reset_vector(CPUState *cpu); -int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); +void pc_system_parse_sev_metadata(uint8_t *flash_ptr, size_t flash_size); #endif diff --git a/target/i386/shift_helper_template.h b/target/i386/shift_helper_template.h deleted file mode 100644 index 54f15d6e05..0000000000 --- a/target/i386/shift_helper_template.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * x86 shift helpers - * - * Copyright (c) 2008 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 . - */ - -#define DATA_BITS (1 << (3 + SHIFT)) -#define SHIFT_MASK (DATA_BITS - 1) -#if DATA_BITS <= 32 -#define SHIFT1_MASK 0x1f -#else -#define SHIFT1_MASK 0x3f -#endif - -#if DATA_BITS == 8 -#define SUFFIX b -#define DATA_MASK 0xff -#elif DATA_BITS == 16 -#define SUFFIX w -#define DATA_MASK 0xffff -#elif DATA_BITS == 32 -#define SUFFIX l -#define DATA_MASK 0xffffffff -#elif DATA_BITS == 64 -#define SUFFIX q -#define DATA_MASK 0xffffffffffffffffULL -#else -#error unhandled operand size -#endif - -target_ulong glue(helper_rcl, SUFFIX)(CPUX86State *env, target_ulong t0, - target_ulong t1) -{ - int count, eflags; - target_ulong src; - target_long res; - - count = t1 & SHIFT1_MASK; -#if DATA_BITS == 16 - count = rclw_table[count]; -#elif DATA_BITS == 8 - count = rclb_table[count]; -#endif - if (count) { - eflags = env->cc_src; - t0 &= DATA_MASK; - src = t0; - res = (t0 << count) | ((target_ulong)(eflags & CC_C) << (count - 1)); - if (count > 1) { - res |= t0 >> (DATA_BITS + 1 - count); - } - t0 = res; - env->cc_src = (eflags & ~(CC_C | CC_O)) | - (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | - ((src >> (DATA_BITS - count)) & CC_C); - } - return t0; -} - -target_ulong glue(helper_rcr, SUFFIX)(CPUX86State *env, target_ulong t0, - target_ulong t1) -{ - int count, eflags; - target_ulong src; - target_long res; - - count = t1 & SHIFT1_MASK; -#if DATA_BITS == 16 - count = rclw_table[count]; -#elif DATA_BITS == 8 - count = rclb_table[count]; -#endif - if (count) { - eflags = env->cc_src; - t0 &= DATA_MASK; - src = t0; - res = (t0 >> count) | - ((target_ulong)(eflags & CC_C) << (DATA_BITS - count)); - if (count > 1) { - res |= t0 << (DATA_BITS + 1 - count); - } - t0 = res; - env->cc_src = (eflags & ~(CC_C | CC_O)) | - (lshift(src ^ t0, 11 - (DATA_BITS - 1)) & CC_O) | - ((src >> (count - 1)) & CC_C); - } - return t0; -} - -#undef DATA_BITS -#undef SHIFT_MASK -#undef SHIFT1_MASK -#undef DATA_TYPE -#undef DATA_MASK -#undef SUFFIX 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/access.c b/target/i386/tcg/access.c new file mode 100644 index 0000000000..e68b73a24b --- /dev/null +++ b/target/i386/tcg/access.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Access guest memory in blocks. */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" +#include "access.h" + + +void access_prepare_mmu(X86Access *ret, CPUX86State *env, + vaddr vaddr, unsigned size, + MMUAccessType type, int mmu_idx, uintptr_t ra) +{ + int size1, size2; + void *haddr1, *haddr2; + + assert(size > 0 && size <= TARGET_PAGE_SIZE); + + size1 = MIN(size, -(vaddr | TARGET_PAGE_MASK)), + size2 = size - size1; + + memset(ret, 0, sizeof(*ret)); + ret->vaddr = vaddr; + ret->size = size; + ret->size1 = size1; + ret->mmu_idx = mmu_idx; + ret->env = env; + ret->ra = ra; + + haddr1 = probe_access(env, vaddr, size1, type, mmu_idx, ra); + ret->haddr1 = haddr1; + + if (unlikely(size2)) { + haddr2 = probe_access(env, vaddr + size1, size2, type, mmu_idx, ra); + if (haddr2 == haddr1 + size1) { + ret->size1 = size; + } else { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + ret->haddr2 = haddr2; +#endif + } + } +} + +void access_prepare(X86Access *ret, CPUX86State *env, vaddr vaddr, + unsigned size, MMUAccessType type, uintptr_t ra) +{ + int mmu_idx = cpu_mmu_index(env_cpu(env), false); + access_prepare_mmu(ret, env, vaddr, size, type, mmu_idx, ra); +} + +static void *access_ptr(X86Access *ac, vaddr addr, unsigned len) +{ + vaddr offset = addr - ac->vaddr; + + assert(addr >= ac->vaddr); + + /* No haddr means probe_access wants to force slow path */ + if (!ac->haddr1) { + return NULL; + } + +#ifdef CONFIG_USER_ONLY + assert(offset <= ac->size1 - len); + return ac->haddr1 + offset; +#else + if (likely(offset <= ac->size1 - len)) { + return ac->haddr1 + offset; + } + assert(offset <= ac->size - len); + /* + * If the address is not naturally aligned, it might span both pages. + * Only return ac->haddr2 if the area is entirely within the second page, + * otherwise fall back to slow accesses. + */ + if (likely(offset >= ac->size1)) { + return ac->haddr2 + (offset - ac->size1); + } + return NULL; +#endif +} + +uint8_t access_ldb(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint8_t)); + + if (likely(p)) { + return ldub_p(p); + } + return cpu_ldub_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +uint16_t access_ldw(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint16_t)); + + if (likely(p)) { + return lduw_le_p(p); + } + return cpu_lduw_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +uint32_t access_ldl(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint32_t)); + + if (likely(p)) { + return ldl_le_p(p); + } + return cpu_ldl_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +uint64_t access_ldq(X86Access *ac, vaddr addr) +{ + void *p = access_ptr(ac, addr, sizeof(uint64_t)); + + if (likely(p)) { + return ldq_le_p(p); + } + return cpu_ldq_le_mmuidx_ra(ac->env, addr, ac->mmu_idx, ac->ra); +} + +void access_stb(X86Access *ac, vaddr addr, uint8_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint8_t)); + + if (likely(p)) { + stb_p(p, val); + } else { + cpu_stb_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} + +void access_stw(X86Access *ac, vaddr addr, uint16_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint16_t)); + + if (likely(p)) { + stw_le_p(p, val); + } else { + cpu_stw_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} + +void access_stl(X86Access *ac, vaddr addr, uint32_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint32_t)); + + if (likely(p)) { + stl_le_p(p, val); + } else { + cpu_stl_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} + +void access_stq(X86Access *ac, vaddr addr, uint64_t val) +{ + void *p = access_ptr(ac, addr, sizeof(uint64_t)); + + if (likely(p)) { + stq_le_p(p, val); + } else { + cpu_stq_le_mmuidx_ra(ac->env, addr, val, ac->mmu_idx, ac->ra); + } +} diff --git a/target/i386/tcg/access.h b/target/i386/tcg/access.h new file mode 100644 index 0000000000..d70808a3a3 --- /dev/null +++ b/target/i386/tcg/access.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Access guest memory in blocks. */ + +#ifndef X86_TCG_ACCESS_H +#define X86_TCG_ACCESS_H + +/* An access covers at most sizeof(X86XSaveArea), at most 2 pages. */ +typedef struct X86Access { + target_ulong vaddr; + void *haddr1; + void *haddr2; + uint16_t size; + uint16_t size1; + /* + * 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 rest + * to do the access here instead of passing it around in the helpers. + */ + int mmu_idx; + CPUX86State *env; + uintptr_t ra; +} X86Access; + +void access_prepare_mmu(X86Access *ret, CPUX86State *env, + vaddr vaddr, unsigned size, + MMUAccessType type, int mmu_idx, uintptr_t ra); +void access_prepare(X86Access *ret, CPUX86State *env, vaddr vaddr, + unsigned size, MMUAccessType type, uintptr_t ra); + +uint8_t access_ldb(X86Access *ac, vaddr addr); +uint16_t access_ldw(X86Access *ac, vaddr addr); +uint32_t access_ldl(X86Access *ac, vaddr addr); +uint64_t access_ldq(X86Access *ac, vaddr addr); + +void access_stb(X86Access *ac, vaddr addr, uint8_t val); +void access_stw(X86Access *ac, vaddr addr, uint16_t val); +void access_stl(X86Access *ac, vaddr addr, uint32_t val); +void access_stq(X86Access *ac, vaddr addr, uint64_t val); + +#endif diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c index 6227dbb30b..f1940b4092 100644 --- a/target/i386/tcg/cc_helper.c +++ b/target/i386/tcg/cc_helper.c @@ -22,57 +22,22 @@ #include "exec/helper-proto.h" #include "helper-tcg.h" -const uint8_t parity_table[256] = { - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, -}; - #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 @@ -95,6 +60,19 @@ static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1, return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O); } +target_ulong helper_cc_compute_nz(target_ulong dst, target_ulong src1, + int op) +{ + if (CC_OP_HAS_EFLAGS(op)) { + return ~src1 & CC_Z; + } else { + MemOp size = cc_op_size(op); + target_ulong mask = MAKE_64BIT_MASK(0, 8 << size); + + return dst & mask; + } +} + target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, target_ulong src2, int op) { @@ -104,10 +82,8 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, case CC_OP_EFLAGS: return src1; - case CC_OP_CLR: - return CC_Z | CC_P; case CC_OP_POPCNT: - return src1 ? 0 : CC_Z; + return dst ? 0 : CC_Z; case CC_OP_MULB: return compute_all_mulb(dst, src1); @@ -186,6 +162,13 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, case CC_OP_BMILGL: return compute_all_bmilgl(dst, src1); + case CC_OP_BLSIB: + return compute_all_blsib(dst, src1); + case CC_OP_BLSIW: + return compute_all_blsiw(dst, src1); + case CC_OP_BLSIL: + return compute_all_blsil(dst, src1); + case CC_OP_ADCX: return compute_all_adcx(dst, src1, src2); case CC_OP_ADOX: @@ -216,13 +199,15 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, return compute_all_sarq(dst, src1); case CC_OP_BMILGQ: return compute_all_bmilgq(dst, src1); + case CC_OP_BLSIQ: + return compute_all_blsiq(dst, src1); #endif } } -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, @@ -234,7 +219,6 @@ target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, case CC_OP_LOGICW: case CC_OP_LOGICL: case CC_OP_LOGICQ: - case CC_OP_CLR: case CC_OP_POPCNT: return 0; @@ -308,6 +292,13 @@ target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, case CC_OP_BMILGL: return compute_c_bmilgl(dst, src1); + case CC_OP_BLSIB: + return compute_c_blsib(dst, src1); + case CC_OP_BLSIW: + return compute_c_blsiw(dst, src1); + case CC_OP_BLSIL: + return compute_c_blsil(dst, src1); + #ifdef TARGET_X86_64 case CC_OP_ADDQ: return compute_c_addq(dst, src1); @@ -321,6 +312,8 @@ target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, return compute_c_shlq(dst, src1); case CC_OP_BMILGQ: return compute_c_bmilgq(dst, src1); + case CC_OP_BLSIQ: + return compute_c_blsiq(dst, src1); #endif } } @@ -335,7 +328,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; diff --git a/target/i386/tcg/cc_helper_template.h b/target/i386/tcg/cc_helper_template.h deleted file mode 100644 index bb611feb04..0000000000 --- a/target/i386/tcg/cc_helper_template.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * x86 condition code helpers - * - * Copyright (c) 2008 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 . - */ - -#define DATA_BITS (1 << (3 + SHIFT)) - -#if DATA_BITS == 8 -#define SUFFIX b -#define DATA_TYPE uint8_t -#elif DATA_BITS == 16 -#define SUFFIX w -#define DATA_TYPE uint16_t -#elif DATA_BITS == 32 -#define SUFFIX l -#define DATA_TYPE uint32_t -#elif DATA_BITS == 64 -#define SUFFIX q -#define DATA_TYPE uint64_t -#else -#error unhandled operand size -#endif - -#define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) - -/* dynamic flags computation */ - -static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2 = dst - src1; - - cf = dst < src1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - return dst < src1; -} - -static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, - DATA_TYPE src3) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2 = dst - src1 - src3; - - cf = (src3 ? dst <= src1 : dst < src1); - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & 0x10; - zf = (dst == 0) << 6; - sf = lshift(dst, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, - DATA_TYPE src3) -{ - return src3 ? dst <= src1 : dst < src1; -} - -static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src1 = dst + src2; - - cf = src1 < src2; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) -{ - DATA_TYPE src1 = dst + src2; - - return src1 < src2; -} - -static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, - DATA_TYPE src3) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src1 = dst + src2 + src3; - - cf = (src3 ? src1 <= src2 : src1 < src2); - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & 0x10; - zf = (dst == 0) << 6; - sf = lshift(dst, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, - DATA_TYPE src3) -{ - DATA_TYPE src1 = dst + src2 + src3; - - return (src3 ? src1 <= src2 : src1 < src2); -} - -static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = 0; - pf = parity_table[(uint8_t)dst]; - af = 0; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = 0; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2; - - cf = src1; - src1 = dst - 1; - src2 = 1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = (dst == SIGN_MASK) * CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2; - - cf = src1; - src1 = dst + 1; - src2 = 1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = (dst == SIGN_MASK - 1) * CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = (src1 >> (DATA_BITS - 1)) & CC_C; - pf = parity_table[(uint8_t)dst]; - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - /* of is defined iff shift count == 1 */ - of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - return (src1 >> (DATA_BITS - 1)) & CC_C; -} - -static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = src1 & 1; - pf = parity_table[(uint8_t)dst]; - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - /* of is defined iff shift count == 1 */ - of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and - CF are modified and it is slower to do that. Note as well that we - don't truncate SRC1 for computing carry to DATA_TYPE. */ -static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) -{ - int cf, pf, af, zf, sf, of; - - cf = (src1 != 0); - pf = parity_table[(uint8_t)dst]; - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = cf * CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = (src1 == 0); - pf = 0; /* undefined */ - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = 0; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - return src1 == 0; -} - -#undef DATA_BITS -#undef SIGN_MASK -#undef DATA_TYPE -#undef DATA_MASK -#undef SUFFIX diff --git a/target/i386/tcg/cc_helper_template.h.inc b/target/i386/tcg/cc_helper_template.h.inc new file mode 100644 index 0000000000..9aff16b880 --- /dev/null +++ b/target/i386/tcg/cc_helper_template.h.inc @@ -0,0 +1,297 @@ +/* + * x86 condition code helpers + * + * Copyright (c) 2008 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 . + */ + +#define DATA_BITS (1 << (3 + SHIFT)) + +#if DATA_BITS == 8 +#define SUFFIX b +#define DATA_TYPE uint8_t +#define WIDER_TYPE uint32_t +#elif DATA_BITS == 16 +#define SUFFIX w +#define DATA_TYPE uint16_t +#define WIDER_TYPE uint32_t +#elif DATA_BITS == 32 +#define SUFFIX l +#define DATA_TYPE uint32_t +#if HOST_LONG_BITS >= 64 +#define WIDER_TYPE uint64_t +#endif +#elif DATA_BITS == 64 +#define SUFFIX q +#define DATA_TYPE uint64_t +#else +#error unhandled operand size +#endif + +#define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) + +/* dynamic flags computation */ + +static uint32_t glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + DATA_TYPE src2 = dst - src1; + + cf = dst < src1; + pf = compute_pf(dst); + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return dst < src1; +} + +static uint32_t glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, + DATA_TYPE src3) +{ + uint32_t cf, pf, af, zf, sf, of; + +#ifdef WIDER_TYPE + WIDER_TYPE src13 = (WIDER_TYPE) src1 + (WIDER_TYPE) src3; + DATA_TYPE src2 = dst - src13; + + cf = dst < src13; +#else + DATA_TYPE src2 = dst - src1 - src3; + + cf = (src3 ? dst <= src1 : dst < src1); +#endif + + pf = compute_pf(dst); + af = (dst ^ src1 ^ src2) & 0x10; + zf = (dst == 0) << 6; + sf = lshift(dst, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, + DATA_TYPE src3) +{ +#ifdef WIDER_TYPE + WIDER_TYPE src13 = (WIDER_TYPE) src1 + (WIDER_TYPE) src3; + + return dst < src13; +#else + return src3 ? dst <= src1 : dst < src1; +#endif +} + +static uint32_t glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +{ + uint32_t cf, pf, af, zf, sf, of; + DATA_TYPE src1 = dst + src2; + + cf = src1 < src2; + pf = compute_pf(dst); + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +{ + DATA_TYPE src1 = dst + src2; + + return src1 < src2; +} + +static uint32_t glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, + DATA_TYPE src3) +{ + uint32_t cf, pf, af, zf, sf, of; + +#ifdef WIDER_TYPE + WIDER_TYPE src23 = (WIDER_TYPE) src2 + (WIDER_TYPE) src3; + DATA_TYPE src1 = dst + src23; + + cf = src1 < src23; +#else + DATA_TYPE src1 = dst + src2 + src3; + + cf = (src3 ? src1 <= src2 : src1 < src2); +#endif + + pf = compute_pf(dst); + af = (dst ^ src1 ^ src2) & 0x10; + zf = (dst == 0) << 6; + sf = lshift(dst, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, + DATA_TYPE src3) +{ +#ifdef WIDER_TYPE + WIDER_TYPE src23 = (WIDER_TYPE) src2 + (WIDER_TYPE) src3; + DATA_TYPE src1 = dst + src23; + + return src1 < src23; +#else + DATA_TYPE src1 = dst + src2 + src3; + + return (src3 ? src1 <= src2 : src1 < src2); +#endif +} + +static uint32_t glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + + cf = 0; + pf = compute_pf(dst); + af = 0; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf + pf + af + zf + sf + of; +} + +static uint32_t glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + DATA_TYPE src2; + + cf = src1; + src1 = dst - 1; + src2 = 1; + pf = compute_pf(dst); + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = (dst == SIGN_MASK) * CC_O; + return cf + pf + af + zf + sf + of; +} + +static uint32_t glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + DATA_TYPE src2; + + cf = src1; + src1 = dst + 1; + src2 = 1; + pf = compute_pf(dst); + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = (dst == SIGN_MASK - 1) * CC_O; + return cf + pf + af + zf + sf + of; +} + +static uint32_t glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + + cf = (src1 >> (DATA_BITS - 1)) & CC_C; + pf = compute_pf(dst); + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + /* of is defined iff shift count == 1 */ + of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return (src1 >> (DATA_BITS - 1)) & CC_C; +} + +static uint32_t glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + + cf = src1 & 1; + pf = compute_pf(dst); + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + /* of is defined iff shift count == 1 */ + of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; + return cf + pf + af + zf + sf + of; +} + +/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and + CF are modified and it is slower to do that. Note as well that we + don't truncate SRC1 for computing carry to DATA_TYPE. */ +static uint32_t glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) +{ + uint32_t cf, pf, af, zf, sf, of; + + cf = (src1 != 0); + pf = compute_pf(dst); + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = cf * CC_O; + return cf + pf + af + zf + sf + of; +} + +static uint32_t glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + + cf = (src1 == 0); + pf = 0; /* undefined */ + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return src1 == 0; +} + +static int glue(compute_all_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + uint32_t cf, pf, af, zf, sf, of; + + cf = (src1 != 0); + pf = 0; /* undefined */ + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf + pf + af + zf + sf + of; +} + +static int glue(compute_c_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return src1 != 0; +} + +#undef DATA_BITS +#undef SIGN_MASK +#undef DATA_TYPE +#undef DATA_MASK +#undef SUFFIX +#undef WIDER_TYPE diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 6099919bc5..75f99301b3 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -23,7 +23,44 @@ * 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. + * 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. + * + * The manual lists immediate far destinations as Ap (technically an implicit + * argument). The decoder splits them into two immediates, using "Ip" for + * the offset part (that comes first in the instruction stream) and "Iw" for + * the segment/selector part. The size of the offset is given by s->dflag + * and the instructions are illegal in 64-bit mode, so the choice of "Ip" + * is somewhat arbitrary; "Iv" or "Iz" would work just as well. + * + * Operand types + * ------------- + * + * For memory-only operands, if the emitter functions wants to rely on + * generic load and writeback, the decoder needs to know the type of the + * operand. Therefore, M is often replaced by the more specific EM and WM + * (respectively selecting an ALU operand, like the operand type E, or a + * vector operand like the operand type W). + * + * Immediates are almost always signed or masked away in helpers. Two + * common exceptions are IN/OUT and absolute jumps. For these, there is + * an additional custom operand type "I_unsigned". Alternatively, the + * mask could be applied (and the original sign-extended value would be + * optimized away by TCG) in the emitter function. + * + * Finally, a "nop" operand type is used for multi-byte NOPs. It accepts + * any value of mod including 11b (unlike M) but it does not try to + * interpret the operand (like M). + * + * 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 @@ -40,9 +77,89 @@ * 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 + * + * Instructions still in translate.c + * --------------------------------- + * Generation of TCG opcodes for almost all instructions is in emit.c.inc; + * this file interprets the prefixes and opcode bytes down to individual + * instruction mnemonics. There is only a handful of opcodes still using + * a switch statement to decode modrm bits 3-5 and prefixes after decoding + * is complete; these are relics of the older x86 decoder and their code + * generation is performed in translate.c. + * + * These unconverted opcodes also perform their own effective address + * generation using the gen_lea_modrm() function. + * + * There is nothing particularly complicated about them; simply, they don't + * need any nasty hacks in the decoder, and they shouldn't get in the way + * of the implementation of new x86 instructions, so they are left alone + * for the time being. + * + * x87: + * 0xD8 - 0xDF + * + * privileged/system: + * 0x0F 0x00 group 6 (SLDT, STR, LLDT, LTR, VERR, VERW) + * 0x0F 0x01 group 7 (SGDT, SIDT, LGDT, LIDT, SMSW, LMSW, INVLPG, + * MONITOR, MWAIT, CLAC, STAC, XGETBV, XSETBV, + * SWAPGS, RDTSCP) + * 0x0F 0xC7 (reg operand) group 9 (RDRAND, RDSEED, RDPID) + * + * MPX: + * 0x0F 0x1A BNDLDX, BNDMOV, BNDCL, BNDCU + * 0x0F 0x1B BNDSTX, BNDMOV, BNDMK, BNDCN */ #define X86_OP_NONE { 0 }, @@ -59,8 +176,14 @@ ## __VA_ARGS__ \ } +#define X86_OP_GROUP1(op, op0, s0, ...) \ + X86_OP_GROUP3(op, op0, s0, 2op, s0, None, None, ## __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_GROUPw(op, op0, s0, ...) \ + X86_OP_GROUP3(op, op0, s0, None, None, None, None, ## __VA_ARGS__) +#define X86_OP_GROUPwr(op, op0, s0, op1, s1, ...) \ + X86_OP_GROUP3(op, op0, s0, op1, s1, None, None, ## __VA_ARGS__) #define X86_OP_GROUP0(op, ...) \ X86_OP_GROUP3(op, None, None, None, None, None, None, ## __VA_ARGS__) @@ -80,23 +203,40 @@ .op3 = X86_TYPE_I, .s3 = X86_SIZE_b, \ ## __VA_ARGS__) +/* + * Short forms that are mostly useful for ALU opcodes and other + * one-byte opcodes. For vector instructions it is usually + * clearer to write all three operands explicitly, because the + * corresponding gen_* function will use OP_PTRn rather than s->T0 + * and s->T1. + */ +#define X86_OP_ENTRYrr(op, op0, s0, op1, s1, ...) \ + X86_OP_ENTRY3(op, None, None, op0, s0, op1, s1, ## __VA_ARGS__) +#define X86_OP_ENTRYwr(op, op0, s0, op1, s1, ...) \ + X86_OP_ENTRY3(op, op0, s0, op1, s1, None, None, ## __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__) + X86_OP_ENTRY3(op, None, None, op0, s0, None, None, ## __VA_ARGS__) +#define X86_OP_ENTRY1(op, op0, s0, ...) \ + X86_OP_ENTRY3(op, op0, s0, 2op, s0, None, None, ## __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 i64 .special = X86_SPECIAL_i64, -#define o64 .special = X86_SPECIAL_o64, +#define nolea .special = X86_SPECIAL_NoLoadEA, #define xchg .special = X86_SPECIAL_Locked, +#define lock .special = X86_SPECIAL_HasLock, #define mmx .special = X86_SPECIAL_MMX, -#define zext0 .special = X86_SPECIAL_ZExtOp0, -#define zext2 .special = X86_SPECIAL_ZExtOp2, +#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 op0_Mw .special = X86_SPECIAL_Op0_Mw, +#define btEvGv .special = X86_SPECIAL_BitTest, #define vex1 .vex_class = 1, #define vex1_rep3 .vex_class = 1, .vex_special = X86_VEX_REPScalar, @@ -105,6 +245,7 @@ #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, @@ -113,6 +254,11 @@ #define vex12 .vex_class = 12, #define vex13 .vex_class = 13, +#define chk(a) .check = X86_CHECK_##a, +#define chk2(a, b) .check = X86_CHECK_##a | X86_CHECK_##b, +#define chk3(a, b, c) .check = X86_CHECK_##a | X86_CHECK_##b | X86_CHECK_##c, +#define svm(a) .intercept = SVM_EXIT_##a, .has_intercept = true, + #define avx2_256 .vex_special = X86_VEX_AVX2_256, #define P_00 1 @@ -131,6 +277,8 @@ #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, +#define UNKNOWN_OPCODE ((X86OpEntry) {}) + static uint8_t get_modrm(DisasContext *s, CPUX86State *env) { if (!s->has_modrm) { @@ -153,22 +301,80 @@ static inline const X86OpEntry *decode_by_prefix(DisasContext *s, const X86OpEnt } } +static void decode_group8(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86GenFunc group8_gen[8] = { + NULL, NULL, NULL, NULL, + gen_BT, gen_BTS, gen_BTR, gen_BTC, + }; + int op = (get_modrm(s, env) >> 3) & 7; + entry->gen = group8_gen[op]; + if (op == 4) { + /* prevent writeback and LOCK for BT */ + entry->op1 = entry->op0; + entry->op0 = X86_TYPE_None; + entry->s0 = X86_SIZE_None; + } else { + entry->special = X86_SPECIAL_HasLock; + } +} + +static void decode_group9(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry group9_reg = + X86_OP_ENTRY0(multi0F); /* unconverted */ + static const X86OpEntry cmpxchg8b = + X86_OP_ENTRY1(CMPXCHG8B, M,q, lock p_00 cpuid(CX8)); + static const X86OpEntry cmpxchg16b = + X86_OP_ENTRY1(CMPXCHG16B, M,dq, lock p_00 cpuid(CX16)); + + int modrm = get_modrm(s, env); + int op = (modrm >> 3) & 7; + + if ((modrm >> 6) == 3) { + *entry = group9_reg; + } else if (op == 1) { + *entry = REX_W(s) ? cmpxchg16b : cmpxchg8b; + } +} + 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] = { + [0] = X86_OP_ENTRYw(RDxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3), + [1] = X86_OP_ENTRYw(RDxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3), + [2] = X86_OP_ENTRYr(WRxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0), + [3] = X86_OP_ENTRYr(WRxxBASE, R,y, cpuid(FSGSBASE) chk(o64) p_f3 zextT0), + [5] = X86_OP_ENTRY0(LFENCE, cpuid(SSE) p_00), + [6] = X86_OP_ENTRY0(MFENCE, cpuid(SSE2) p_00), + [7] = X86_OP_ENTRY0(SFENCE, cpuid(SSE) p_00), }; static const X86OpEntry group15_mem[8] = { - [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5), - [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5), + [0] = X86_OP_ENTRYw(FXSAVE, M,y, cpuid(FXSR) p_00), + [1] = X86_OP_ENTRYr(FXRSTOR, M,y, cpuid(FXSR) p_00), + [2] = X86_OP_ENTRYr(LDMXCSR, E,d, vex5 chk(VEX128) p_00), + [3] = X86_OP_ENTRYw(STMXCSR, E,d, vex5 chk(VEX128) p_00), + [4] = X86_OP_ENTRYw(XSAVE, M,y, cpuid(XSAVE) p_00), + [5] = X86_OP_ENTRYr(XRSTOR, M,y, cpuid(XSAVE) p_00), + [6] = X86_OP_ENTRYw(XSAVEOPT, M,b, cpuid(XSAVEOPT) p_00), + [7] = X86_OP_ENTRYw(NOP, M,b, cpuid(CLFLUSH) p_00), + }; + + static const X86OpEntry group15_mem_66[8] = { + [6] = X86_OP_ENTRYw(NOP, M,b, cpuid(CLWB)), + [7] = X86_OP_ENTRYw(NOP, M,b, cpuid(CLFLUSHOPT)), }; uint8_t modrm = get_modrm(s, env); + int op = (modrm >> 3) & 7; + if ((modrm >> 6) == 3) { - *entry = group15_reg[(modrm >> 3) & 7]; + *entry = group15_reg[op]; + } else if (s->prefix & PREFIX_DATA) { + *entry = group15_mem_66[op]; } else { - *entry = group15_mem[(modrm >> 3) & 7]; + *entry = group15_mem[op]; } } @@ -236,7 +442,7 @@ static void decode_group14(DisasContext *s, CPUX86State *env, X86OpEntry *entry, 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, vex1 mmx), /* movq */ + 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 */ {}, @@ -273,9 +479,9 @@ static void decode_0F78(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui { static const X86OpEntry opcodes_0F78[4] = { {}, - X86_OP_ENTRY3(EXTRQ_i, V,x, None,None, I,w, cpuid(SSE4A)), + 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)), + X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)), /* AMD extension */ }; *entry = *decode_by_prefix(s, opcodes_0F78); } @@ -283,9 +489,9 @@ static void decode_0F78(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui static void decode_0F79(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { if (s->prefix & PREFIX_REPNZ) { - entry->gen = gen_INSERTQ_r; + entry->gen = gen_INSERTQ_r; /* AMD extension */ } else if (s->prefix & PREFIX_DATA) { - entry->gen = gen_EXTRQ_r; + entry->gen = gen_EXTRQ_r; /* AMD extension */ } else { entry->gen = NULL; }; @@ -305,7 +511,7 @@ static void decode_0F7E(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui 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, vex1 mmx), /* movq */ + 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 */ {}, @@ -313,6 +519,50 @@ static void decode_0F7F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui *entry = *decode_by_prefix(s, opcodes_0F7F); } +static void decode_0FB8(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry popcnt = + X86_OP_ENTRYwr(POPCNT, G,v, E,v, cpuid(POPCNT) zextT0); + + if (s->prefix & PREFIX_REPZ) { + *entry = popcnt; + } else { + memset(entry, 0, sizeof(*entry)); + } +} + +static void decode_0FBC(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* For BSF, pass 2op as the third operand so that we can use zextT0 */ + static const X86OpEntry opcodes_0FBC[4] = { + X86_OP_ENTRY3(BSF, G,v, E,v, 2op,v, zextT0), + X86_OP_ENTRY3(BSF, G,v, E,v, 2op,v, zextT0), /* 0x66 */ + X86_OP_ENTRYwr(TZCNT, G,v, E,v, zextT0), /* 0xf3 */ + X86_OP_ENTRY3(BSF, G,v, E,v, 2op,v, zextT0), /* 0xf2 */ + }; + if (!(s->cpuid_ext3_features & CPUID_EXT3_ABM)) { + *entry = opcodes_0FBC[0]; + } else { + *entry = *decode_by_prefix(s, opcodes_0FBC); + } +} + +static void decode_0FBD(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + /* For BSR, pass 2op as the third operand so that we can use zextT0 */ + static const X86OpEntry opcodes_0FBD[4] = { + X86_OP_ENTRY3(BSR, G,v, E,v, 2op,v, zextT0), + X86_OP_ENTRY3(BSR, G,v, E,v, 2op,v, zextT0), /* 0x66 */ + X86_OP_ENTRYwr(LZCNT, G,v, E,v, zextT0), /* 0xf3 */ + X86_OP_ENTRY3(BSR, G,v, E,v, 2op,v, zextT0), /* 0xf2 */ + }; + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { + *entry = opcodes_0FBD[0]; + } else { + *entry = *decode_by_prefix(s, opcodes_0FBD); + } +} + static void decode_0FD6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { static const X86OpEntry movq[4] = { @@ -336,11 +586,11 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [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,ph, vex11 cpuid(F16C) 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 cpuid(AVX2) p_66), + [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), /* @@ -361,14 +611,14 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [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 cpuid(AVX2) 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 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 */ @@ -390,14 +640,15 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [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), - [0x0c] = X86_OP_ENTRY3(VPERMILPS, V,x, H,x, W,x, vex4 cpuid(AVX) p_00_66), - [0x0d] = X86_OP_ENTRY3(VPERMILPD, V,x, H,x, W,x, vex4 cpuid(AVX) p_66), - [0x0e] = X86_OP_ENTRY3(VTESTPS, None,None, V,x, W,x, vex4 cpuid(AVX) p_66), - [0x0f] = X86_OP_ENTRY3(VTESTPD, None,None, V,x, W,x, vex4 cpuid(AVX) p_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 cpuid(AVX) p_66), /* vbroadcastss */ - [0x19] = X86_OP_ENTRY3(VPBROADCASTQ, V,qq, None,None, W,q, vex6 cpuid(AVX) p_66), /* vbroadcastsd */ - [0x1a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 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), @@ -406,11 +657,11 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [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 cpuid(AVX) p_66), - [0x2d] = X86_OP_ENTRY3(VMASKMOVPD, V,x, H,x, WM,x, vex6 cpuid(AVX) 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 cpuid(AVX) p_66), - [0x2f] = X86_OP_ENTRY3(VMASKMOVPD_st, M,x, V,x, H,x, vex6 cpuid(AVX) p_66), + [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), @@ -421,12 +672,13 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [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), - [0x58] = X86_OP_ENTRY3(VPBROADCASTD, V,x, None,None, W,d, vex6 cpuid(AVX2) p_66), - [0x59] = X86_OP_ENTRY3(VPBROADCASTQ, V,x, None,None, W,q, vex6 cpuid(AVX2) p_66), - [0x5a] = X86_OP_ENTRY3(VBROADCASTx128, V,qq, None,None, WM,dq,vex6 cpuid(AVX2) 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 cpuid(AVX2) p_66), - [0x79] = X86_OP_ENTRY3(VPBROADCASTW, V,x, None,None, W,w, vex6 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), @@ -459,25 +711,54 @@ static const X86OpEntry opcodes_0F38_00toEF[240] = { [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_ENTRYwr(MOVBE, G,y, M,y, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, G,w, M,w, 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_ENTRYwr(MOVBE, M,y, G,y, cpuid(MOVBE)), + X86_OP_ENTRYwr(MOVBE, M,w, G,w, cpuid(MOVBE)), {}, X86_OP_ENTRY2(CRC32, G,d, E,y, cpuid(SSE42)), X86_OP_ENTRY2(CRC32, G,d, E,w, cpuid(SSE42)), @@ -490,7 +771,7 @@ static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { {}, }, [3] = { - X86_OP_GROUP3(group17, B,y, E,y, None,None, vex13 cpuid(BMI1)), + X86_OP_GROUP3(group17, B,y, None,None, E,y, vex13 cpuid(BMI1)), {}, {}, {}, @@ -499,8 +780,8 @@ static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { [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 cpuid(BMI2)), - X86_OP_ENTRY3(PDEP, G,y, B,y, E,y, vex13 cpuid(BMI2)), + 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] = { @@ -511,10 +792,10 @@ static const X86OpEntry opcodes_0F38_F0toFF[16][5] = { {}, }, [7] = { - X86_OP_ENTRY3(BEXTR, G,y, E,y, B,y, vex13 cpuid(BMI1)), + 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 cpuid(BMI1)), - X86_OP_ENTRY3(SHRX, 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)), {}, }, }; @@ -553,20 +834,20 @@ static const X86OpEntry opcodes_0F3A[256] = { * 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 cpuid(AVX2) p_66), - [0x01] = X86_OP_ENTRY3(VPERMQ, V,qq, W,qq, I,b, vex6 cpuid(AVX2) p_66), /* VPERMPD */ - [0x02] = X86_OP_ENTRY4(VBLENDPS, V,x, H,x, W,x, vex6 cpuid(AVX2) p_66), /* VPBLENDD */ - [0x04] = X86_OP_ENTRY3(VPERMILPS_i, V,x, W,x, I,b, vex6 cpuid(AVX) p_66), - [0x05] = X86_OP_ENTRY3(VPERMILPD_i, V,x, W,x, I,b, vex6 cpuid(AVX) p_66), - [0x06] = X86_OP_ENTRY4(VPERM2x128, V,qq, H,qq, W,qq, vex6 cpuid(AVX) p_66), + [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) zext0 p_66), - [0x15] = X86_OP_ENTRY3(PEXTRW, E,w, V,dq, I,b, vex5 cpuid(SSE41) zext0 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,ph, V,x, I,b, vex11 cpuid(F16C) 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) zext2 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), @@ -574,7 +855,7 @@ static const X86OpEntry opcodes_0F3A[256] = { [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 cpuid(AVX2) 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), @@ -597,16 +878,18 @@ static const X86OpEntry opcodes_0F3A[256] = { [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 cpuid(AVX) p_66), - [0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 cpuid(AVX) p_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 cpuid(AVX2) p_66), - [0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 cpuid(AVX2) 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 cpuid(AVX) p_66), - [0x4b] = X86_OP_ENTRY4(VBLENDVPD, V,x, H,x, W,x, vex6 cpuid(AVX) p_66), - [0x4c] = X86_OP_ENTRY4(VPBLENDVB, V,x, H,x, W,x, vex6 cpuid(AVX) p_66 avx2_256), + [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), @@ -638,15 +921,15 @@ static void decode_0F10(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui 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, vex4), - X86_OP_ENTRY3(VMOVLPx, V,x, H,x, W,x, vex4), /* MOVSD */ + 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, vex4), - X86_OP_ENTRY3(VMOVSD_ld, V,x, H,x, M,sd, vex4), + 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) { @@ -659,17 +942,17 @@ static void decode_0F10(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui 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), /* MOVPS */ - X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4), /* MOVPD */ - X86_OP_ENTRY3(VMOVSS, W,x, H,x, V,x, vex4), - X86_OP_ENTRY3(VMOVLPx, W,x, H,x, V,q, vex4), /* MOVSD */ + 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), /* MOVPS */ - X86_OP_ENTRY3(MOVDQ, W,x, None,None, V,x, vex4), /* MOVPD */ - X86_OP_ENTRY3(VMOVSS_st, M,ss, None,None, V,x, vex4), - X86_OP_ENTRY3(VMOVLPx_st, M,sd, None,None, V,x, vex4), /* MOVSD */ + 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) { @@ -686,16 +969,16 @@ static void decode_0F12(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui * 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, vex4), /* MOVLPS */ - X86_OP_ENTRY3(VMOVLPx_ld, V,dq, H,dq, M,q, vex4), /* MOVLPD */ + 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, vex4 cpuid(SSE3)), /* qq if VEX.256 */ + 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, vex4), - X86_OP_ENTRY3(VMOVLPx, W,x, H,x, U,q, vex4), /* MOVLPD */ + 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, vex4 cpuid(SSE3)), + X86_OP_ENTRY3(VMOVDDUP, V,x, None,None, U,x, vex5 cpuid(SSE3)), }; if ((get_modrm(s, env) >> 6) == 3) { @@ -715,15 +998,15 @@ static void decode_0F16(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui * 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, vex4), /* MOVHPS */ - X86_OP_ENTRY3(VMOVHPx_ld, V,dq, H,dq, M,q, vex4), /* MOVHPD */ + 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, vex4), - X86_OP_ENTRY3(VMOVHPx, V,x, H,x, U,x, vex4), /* MOVHPD */ + 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)), {}, }; @@ -749,8 +1032,9 @@ static void decode_0F2A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui 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, vex4), /* MOVNTPS */ - X86_OP_ENTRY3(MOVDQ, M,x, None,None, V,x, vex4), /* MOVNTPD */ + 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 */ }; @@ -803,10 +1087,20 @@ static void decode_sse_unary(DisasContext *s, CPUX86State *env, X86OpEntry *entr case 0x51: entry->gen = gen_VSQRT; break; case 0x52: entry->gen = gen_VRSQRT; break; case 0x53: entry->gen = gen_VRCP; break; - case 0x5A: entry->gen = gen_VCVTfp2fp; 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] = { @@ -823,35 +1117,78 @@ static void decode_0FE6(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui static const X86OpEntry opcodes_0FE6[4] = { {}, X86_OP_ENTRY2(VCVTTPD2DQ, V,x, W,x, vex2), - X86_OP_ENTRY2(VCVTDQ2PD, 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 */ +/* + * These ignore the mod bits (assume (modrm&0xc0)==0xc0), so group the + * pre-decode tweak here for all MOVs from/to CR and DR. + * + * AMD documentation (24594.pdf) and testing of Intel 386 and 486 + * processors all show that the mod bits are assumed to be 1's, + * regardless of actual values. + */ +static void decode_MOV_CR_DR(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ /* - * 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)), + get_modrm(s, env); + s->modrm |= 0xC0; + + entry->gen = gen_MOV; +} + +static const X86OpEntry opcodes_0F[256] = { + [0x00] = X86_OP_ENTRY1(multi0F, nop,v, nolea), /* unconverted */ + [0x01] = X86_OP_ENTRY1(multi0F, nop,v, nolea), /* unconverted */ + [0x02] = X86_OP_ENTRYwr(LAR, G,v, E,w, chk(prot)), + [0x03] = X86_OP_ENTRYwr(LSL, G,v, E,w, chk(prot)), + [0x05] = X86_OP_ENTRY0(SYSCALL, chk(o64_intel)), + [0x06] = X86_OP_ENTRY0(CLTS, chk(cpl0) svm(WRITE_CR0)), + [0x07] = X86_OP_ENTRY0(SYSRET, chk3(o64_intel, prot, cpl0)), [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, vex4 p_00_66), + [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, vex4 p_00_66), + [0x17] = X86_OP_ENTRY3(VMOVHPx_st, M,q, None,None, V,dq, vex5 p_00_66), + + /* + * Incorrectly listed as using "d" operand type in the manual. In reality + * there's no 16-bit version (like y) and it does not use REX.W (like d64). + */ + [0x20] = X86_OP_GROUPwr(MOV_CR_DR, R,y_d64, C,y_d64, chk(cpl0) svm(READ_CR0)), + [0x21] = X86_OP_GROUPwr(MOV_CR_DR, R,y_d64, D,y_d64, chk(cpl0) svm(READ_DR0)), + [0x22] = X86_OP_GROUPwr(MOV_CR_DR, C,y_d64, R,y_d64, zextT0 chk(cpl0) svm(WRITE_CR0)), + [0x23] = X86_OP_GROUPwr(MOV_CR_DR, D,y_d64, R,y_d64, zextT0 chk(cpl0) svm(WRITE_DR0)), + + [0x30] = X86_OP_ENTRY0(WRMSR, chk(cpl0)), + [0x31] = X86_OP_ENTRY0(RDTSC), + [0x32] = X86_OP_ENTRY0(RDMSR, chk(cpl0)), + [0x33] = X86_OP_ENTRY0(RDPMC), + [0x34] = X86_OP_ENTRY0(SYSENTER, chk2(i64_amd, prot_or_vm86)), + [0x35] = X86_OP_ENTRY0(SYSEXIT, chk3(i64_amd, prot, cpl0)), + + [0x40] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x41] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x42] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x43] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x44] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x45] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x46] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x47] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), [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), - [0x52] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex5 p_00_f3), - [0x53] = X86_OP_GROUP3(sse_unary, V,x, H,x, W,x, vex5 p_00_f3), + [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 */ @@ -875,49 +1212,48 @@ static const X86OpEntry opcodes_0F[256] = { [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 */ + [0x80] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x81] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x82] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x83] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x84] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x85] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x86] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x87] = X86_OP_ENTRYr(Jcc, J,z_f64), - [0x38] = X86_OP_GROUP0(0F38), - [0x3a] = X86_OP_GROUP0(0F3A), + [0x90] = X86_OP_ENTRYw(SETcc, E,b), + [0x91] = X86_OP_ENTRYw(SETcc, E,b), + [0x92] = X86_OP_ENTRYw(SETcc, E,b), + [0x93] = X86_OP_ENTRYw(SETcc, E,b), + [0x94] = X86_OP_ENTRYw(SETcc, E,b), + [0x95] = X86_OP_ENTRYw(SETcc, E,b), + [0x96] = X86_OP_ENTRYw(SETcc, E,b), + [0x97] = X86_OP_ENTRYw(SETcc, E,b), - [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_GROUP3(sse_unary, V,x, H,x, W,x, vex3 p_00_66_f3_f2), - [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), + [0xa0] = X86_OP_ENTRYr(PUSH, FS, w), + [0xa1] = X86_OP_ENTRYw(POP, FS, w), + [0xa2] = X86_OP_ENTRY0(CPUID), + [0xa3] = X86_OP_ENTRYrr(BT, E,v, G,v, btEvGv), + [0xa4] = X86_OP_ENTRY4(SHLD, E,v, 2op,v, G,v), + [0xa5] = X86_OP_ENTRY3(SHLD, E,v, 2op,v, G,v), - [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), + [0xb0] = X86_OP_ENTRY2(CMPXCHG,E,b, G,b, lock), + [0xb1] = X86_OP_ENTRY2(CMPXCHG,E,v, G,v, lock), + [0xb2] = X86_OP_ENTRY3(LSS, G,v, EM,p, None, None), + [0xb3] = X86_OP_ENTRY2(BTR, E,v, G,v, btEvGv), + [0xb4] = X86_OP_ENTRY3(LFS, G,v, EM,p, None, None), + [0xb5] = X86_OP_ENTRY3(LGS, G,v, EM,p, None, None), + [0xb6] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, zextT0), /* MOVZX */ + [0xb7] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, zextT0), /* MOVZX */ + [0xc0] = X86_OP_ENTRY2(XADD, E,b, G,b, lock), + [0xc1] = X86_OP_ENTRY2(XADD, E,v, G,v, lock), [0xc2] = X86_OP_ENTRY4(VCMP, V,x, H,x, W,x, vex2_rep3 p_00_66_f3_f2), + [0xc3] = X86_OP_ENTRY3(MOV, EM,y,G,y, None,None, cpuid(SSE2)), /* MOVNTI */ [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), + [0xc7] = X86_OP_GROUP0(group9), [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), @@ -946,6 +1282,123 @@ static const X86OpEntry opcodes_0F[256] = { [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), + [0x08] = X86_OP_ENTRY0(NOP, svm(INVD)), + [0x09] = X86_OP_ENTRY0(NOP, svm(WBINVD)), + [0x0b] = X86_OP_ENTRY0(UD), /* UD2 */ + [0x0d] = X86_OP_ENTRY1(NOP, M,v), /* 3DNow! prefetch */ + [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)), + + [0x18] = X86_OP_ENTRY1(NOP, nop,v), /* prefetch/reserved NOP */ + [0x19] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1a] = X86_OP_ENTRY1(multi0F, nop,v, nolea), /* unconverted MPX */ + [0x1b] = X86_OP_ENTRY1(multi0F, nop,v, nolea), /* unconverted MPX */ + [0x1c] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1d] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1e] = X86_OP_ENTRY1(NOP, nop,v), /* reserved NOP */ + [0x1f] = X86_OP_ENTRY1(NOP, nop,v), /* NOP/reserved NOP */ + + [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), + + [0x48] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x49] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4a] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4b] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4c] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4d] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4e] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + [0x4f] = X86_OP_ENTRY2(CMOVcc, G,v, E,v, cpuid(CMOV)), + + [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), + + [0x88] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x89] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8a] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8b] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8c] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8d] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8e] = X86_OP_ENTRYr(Jcc, J,z_f64), + [0x8f] = X86_OP_ENTRYr(Jcc, J,z_f64), + + [0x98] = X86_OP_ENTRYw(SETcc, E,b), + [0x99] = X86_OP_ENTRYw(SETcc, E,b), + [0x9a] = X86_OP_ENTRYw(SETcc, E,b), + [0x9b] = X86_OP_ENTRYw(SETcc, E,b), + [0x9c] = X86_OP_ENTRYw(SETcc, E,b), + [0x9d] = X86_OP_ENTRYw(SETcc, E,b), + [0x9e] = X86_OP_ENTRYw(SETcc, E,b), + [0x9f] = X86_OP_ENTRYw(SETcc, E,b), + + [0xa8] = X86_OP_ENTRYr(PUSH, GS, w), + [0xa9] = X86_OP_ENTRYw(POP, GS, w), + [0xaa] = X86_OP_ENTRY0(RSM, chk(smm) svm(RSM)), + [0xab] = X86_OP_ENTRY2(BTS, E,v, G,v, btEvGv), + [0xac] = X86_OP_ENTRY4(SHRD, E,v, 2op,v, G,v), + [0xad] = X86_OP_ENTRY3(SHRD, E,v, 2op,v, G,v), + [0xae] = X86_OP_GROUP0(group15), + /* + * It's slightly more efficient to put Ev operand in T0 and allow gen_IMUL3 + * to assume sextT0. Multiplication is commutative anyway. + */ + [0xaf] = X86_OP_ENTRY3(IMUL3, G,v, E,v, 2op,v, sextT0), + + [0xb8] = X86_OP_GROUP0(0FB8), + /* decoded as modrm, which is visible as a difference between page fault and #UD */ + [0xb9] = X86_OP_ENTRYr(UD, nop,v), /* UD1 */ + [0xba] = X86_OP_GROUP2(group8, E,v, I,b), + [0xbb] = X86_OP_ENTRY2(BTC, E,v, G,v, btEvGv), + [0xbc] = X86_OP_GROUP0(0FBC), + [0xbd] = X86_OP_GROUP0(0FBD), + [0xbe] = X86_OP_ENTRY3(MOV, G,v, E,b, None, None, sextT0), /* MOVSX */ + [0xbf] = X86_OP_ENTRY3(MOV, G,v, E,w, None, None, sextT0), /* MOVSX */ + + [0xc8] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xc9] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xca] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcb] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcc] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcd] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xce] = X86_OP_ENTRY1(BSWAP, LoBits,y), + [0xcf] = X86_OP_ENTRY1(BSWAP, LoBits,y), + /* 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), @@ -972,7 +1425,7 @@ static const X86OpEntry opcodes_0F[256] = { [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 */ + [0xff] = X86_OP_ENTRYr(UD, nop,v), /* UD0 */ }; static void do_decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) @@ -986,8 +1439,432 @@ static void decode_0F(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint do_decode_0F(s, env, entry, b); } +static void decode_63(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry arpl = X86_OP_ENTRY2(ARPL, E,w, G,w, chk(prot)); + static const X86OpEntry mov = X86_OP_ENTRY3(MOV, G,v, E,v, None, None); + static const X86OpEntry movsxd = X86_OP_ENTRY3(MOV, G,v, E,d, None, None, sextT0); + if (!CODE64(s)) { + *entry = arpl; + } else if (REX_W(s)) { + *entry = movsxd; + } else { + *entry = mov; + } +} + +static void decode_group1(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86GenFunc group1_gen[8] = { + gen_ADD, gen_OR, gen_ADC, gen_SBB, gen_AND, gen_SUB, gen_XOR, gen_SUB, + }; + int op = (get_modrm(s, env) >> 3) & 7; + entry->gen = group1_gen[op]; + + if (op == 7) { + /* prevent writeback for CMP */ + entry->op1 = entry->op0; + entry->op0 = X86_TYPE_None; + entry->s0 = X86_SIZE_None; + } else { + entry->special = X86_SPECIAL_HasLock; + } +} + +static void decode_group1A(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + int op = (get_modrm(s, env) >> 3) & 7; + if (op != 0) { + /* could be XOP prefix too */ + *entry = UNKNOWN_OPCODE; + } else { + entry->gen = gen_POP; + /* The address must use the value of ESP after the pop. */ + s->popl_esp_hack = 1 << mo_pushpop(s, s->dflag); + } +} + +static void decode_group2(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86GenFunc group2_gen[8] = { + gen_ROL, gen_ROR, gen_RCL, gen_RCR, + gen_SHL, gen_SHR, gen_SHL /* SAL, undocumented */, gen_SAR, + }; + int op = (get_modrm(s, env) >> 3) & 7; + entry->gen = group2_gen[op]; + if (op == 7) { + entry->special = X86_SPECIAL_SExtT0; + } else { + entry->special = X86_SPECIAL_ZExtT0; + } +} + +static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_grp3[16] = { + /* 0xf6 */ + [0x00] = X86_OP_ENTRYrr(AND, E,b, I,b), + [0x02] = X86_OP_ENTRY1(NOT, E,b, lock), + [0x03] = X86_OP_ENTRY1(NEG, E,b, lock), + [0x04] = X86_OP_ENTRYrr(MUL, E,b, 0,b, zextT0), + [0x05] = X86_OP_ENTRYrr(IMUL,E,b, 0,b, sextT0), + [0x06] = X86_OP_ENTRYr(DIV, E,b), + [0x07] = X86_OP_ENTRYr(IDIV, E,b), + + /* 0xf7 */ + [0x08] = X86_OP_ENTRYrr(AND, E,v, I,z), + [0x0a] = X86_OP_ENTRY1(NOT, E,v, lock), + [0x0b] = X86_OP_ENTRY1(NEG, E,v, lock), + [0x0c] = X86_OP_ENTRYrr(MUL, E,v, 0,v, zextT0), + [0x0d] = X86_OP_ENTRYrr(IMUL,E,v, 0,v, sextT0), + [0x0e] = X86_OP_ENTRYr(DIV, E,v), + [0x0f] = X86_OP_ENTRYr(IDIV, E,v), + }; + + int w = (*b & 1); + int reg = (get_modrm(s, env) >> 3) & 7; + + *entry = opcodes_grp3[(w << 3) | reg]; +} + +static void decode_group4_5(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static const X86OpEntry opcodes_grp4_5[16] = { + /* 0xfe */ + [0x00] = X86_OP_ENTRY1(INC, E,b, lock), + [0x01] = X86_OP_ENTRY1(DEC, E,b, lock), + + /* 0xff */ + [0x08] = X86_OP_ENTRY1(INC, E,v, lock), + [0x09] = X86_OP_ENTRY1(DEC, E,v, lock), + [0x0a] = X86_OP_ENTRYr(CALL_m, E,f64, zextT0), + [0x0b] = X86_OP_ENTRYr(CALLF_m, M,p), + [0x0c] = X86_OP_ENTRYr(JMP_m, E,f64, zextT0), + [0x0d] = X86_OP_ENTRYr(JMPF_m, M,p), + [0x0e] = X86_OP_ENTRYr(PUSH, E,f64), + }; + + int w = (*b & 1); + int reg = (get_modrm(s, env) >> 3) & 7; + + *entry = opcodes_grp4_5[(w << 3) | reg]; +} + + +static void decode_group11(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + int op = (get_modrm(s, env) >> 3) & 7; + if (op != 0) { + *entry = UNKNOWN_OPCODE; + } else { + entry->gen = gen_MOV; + } +} + +static void decode_90(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) +{ + static X86OpEntry pause = X86_OP_ENTRY0(PAUSE, svm(PAUSE)); + static X86OpEntry nop = X86_OP_ENTRY0(NOP); + static X86OpEntry xchg_ax = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v); + + if (REX_B(s)) { + *entry = xchg_ax; + } else { + *entry = (s->prefix & PREFIX_REPZ) ? pause : nop; + } +} + static const X86OpEntry opcodes_root[256] = { + [0x00] = X86_OP_ENTRY2(ADD, E,b, G,b, lock), + [0x01] = X86_OP_ENTRY2(ADD, E,v, G,v, lock), + [0x02] = X86_OP_ENTRY2(ADD, G,b, E,b, lock), + [0x03] = X86_OP_ENTRY2(ADD, G,v, E,v, lock), + [0x04] = X86_OP_ENTRY2(ADD, 0,b, I,b, lock), /* AL, Ib */ + [0x05] = X86_OP_ENTRY2(ADD, 0,v, I,z, lock), /* rAX, Iz */ + [0x06] = X86_OP_ENTRYr(PUSH, ES, w, chk(i64)), + [0x07] = X86_OP_ENTRYw(POP, ES, w, chk(i64)), + + [0x10] = X86_OP_ENTRY2(ADC, E,b, G,b, lock), + [0x11] = X86_OP_ENTRY2(ADC, E,v, G,v, lock), + [0x12] = X86_OP_ENTRY2(ADC, G,b, E,b, lock), + [0x13] = X86_OP_ENTRY2(ADC, G,v, E,v, lock), + [0x14] = X86_OP_ENTRY2(ADC, 0,b, I,b, lock), /* AL, Ib */ + [0x15] = X86_OP_ENTRY2(ADC, 0,v, I,z, lock), /* rAX, Iz */ + [0x16] = X86_OP_ENTRYr(PUSH, SS, w, chk(i64)), + [0x17] = X86_OP_ENTRYw(POP, SS, w, chk(i64)), + + [0x20] = X86_OP_ENTRY2(AND, E,b, G,b, lock), + [0x21] = X86_OP_ENTRY2(AND, E,v, G,v, lock), + [0x22] = X86_OP_ENTRY2(AND, G,b, E,b, lock), + [0x23] = X86_OP_ENTRY2(AND, G,v, E,v, lock), + [0x24] = X86_OP_ENTRY2(AND, 0,b, I,b, lock), /* AL, Ib */ + [0x25] = X86_OP_ENTRY2(AND, 0,v, I,z, lock), /* rAX, Iz */ + [0x26] = {}, + [0x27] = X86_OP_ENTRY0(DAA, chk(i64)), + + [0x30] = X86_OP_ENTRY2(XOR, E,b, G,b, lock), + [0x31] = X86_OP_ENTRY2(XOR, E,v, G,v, lock), + [0x32] = X86_OP_ENTRY2(XOR, G,b, E,b, lock), + [0x33] = X86_OP_ENTRY2(XOR, G,v, E,v, lock), + [0x34] = X86_OP_ENTRY2(XOR, 0,b, I,b, lock), /* AL, Ib */ + [0x35] = X86_OP_ENTRY2(XOR, 0,v, I,z, lock), /* rAX, Iz */ + [0x36] = {}, + [0x37] = X86_OP_ENTRY0(AAA, chk(i64)), + + [0x40] = X86_OP_ENTRY1(INC, 0,v, chk(i64)), + [0x41] = X86_OP_ENTRY1(INC, 1,v, chk(i64)), + [0x42] = X86_OP_ENTRY1(INC, 2,v, chk(i64)), + [0x43] = X86_OP_ENTRY1(INC, 3,v, chk(i64)), + [0x44] = X86_OP_ENTRY1(INC, 4,v, chk(i64)), + [0x45] = X86_OP_ENTRY1(INC, 5,v, chk(i64)), + [0x46] = X86_OP_ENTRY1(INC, 6,v, chk(i64)), + [0x47] = X86_OP_ENTRY1(INC, 7,v, chk(i64)), + + [0x50] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x51] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x52] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x53] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x54] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x55] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x56] = X86_OP_ENTRYr(PUSH, LoBits,d64), + [0x57] = X86_OP_ENTRYr(PUSH, LoBits,d64), + + [0x60] = X86_OP_ENTRY0(PUSHA, chk(i64)), + [0x61] = X86_OP_ENTRY0(POPA, chk(i64)), + [0x62] = X86_OP_ENTRYrr(BOUND, G,v, M,a, chk(i64)), + [0x63] = X86_OP_GROUP0(63), + [0x64] = {}, + [0x65] = {}, + [0x66] = {}, + [0x67] = {}, + + [0x70] = X86_OP_ENTRYr(Jcc, J,b), + [0x71] = X86_OP_ENTRYr(Jcc, J,b), + [0x72] = X86_OP_ENTRYr(Jcc, J,b), + [0x73] = X86_OP_ENTRYr(Jcc, J,b), + [0x74] = X86_OP_ENTRYr(Jcc, J,b), + [0x75] = X86_OP_ENTRYr(Jcc, J,b), + [0x76] = X86_OP_ENTRYr(Jcc, J,b), + [0x77] = X86_OP_ENTRYr(Jcc, J,b), + + [0x80] = X86_OP_GROUP2(group1, E,b, I,b), + [0x81] = X86_OP_GROUP2(group1, E,v, I,z), + [0x82] = X86_OP_GROUP2(group1, E,b, I,b, chk(i64)), + [0x83] = X86_OP_GROUP2(group1, E,v, I,b), + [0x84] = X86_OP_ENTRYrr(AND, E,b, G,b), + [0x85] = X86_OP_ENTRYrr(AND, E,v, G,v), + [0x86] = X86_OP_ENTRY2(XCHG, E,b, G,b, xchg), + [0x87] = X86_OP_ENTRY2(XCHG, E,v, G,v, xchg), + + [0x90] = X86_OP_GROUP0(90), + [0x91] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x92] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x93] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x94] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x95] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x96] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + [0x97] = X86_OP_ENTRY2(XCHG, 0,v, LoBits,v), + + [0xA0] = X86_OP_ENTRY3(MOV, 0,b, O,b, None, None), /* AL, Ob */ + [0xA1] = X86_OP_ENTRY3(MOV, 0,v, O,v, None, None), /* rAX, Ov */ + [0xA2] = X86_OP_ENTRY3(MOV, O,b, 0,b, None, None), /* Ob, AL */ + [0xA3] = X86_OP_ENTRY3(MOV, O,v, 0,v, None, None), /* Ov, rAX */ + [0xA4] = X86_OP_ENTRYrr(MOVS, Y,b, X,b), + [0xA5] = X86_OP_ENTRYrr(MOVS, Y,v, X,v), + [0xA6] = X86_OP_ENTRYrr(CMPS, Y,b, X,b), + [0xA7] = X86_OP_ENTRYrr(CMPS, Y,v, X,v), + + [0xB0] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB1] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB2] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB3] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB4] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB5] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB6] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + [0xB7] = X86_OP_ENTRY3(MOV, LoBits,b, I,b, None, None), + + [0xC0] = X86_OP_GROUP2(group2, E,b, I,b), + [0xC1] = X86_OP_GROUP2(group2, E,v, I,b), + [0xC2] = X86_OP_ENTRYr(RET, I,w), + [0xC3] = X86_OP_ENTRY0(RET), + [0xC4] = X86_OP_ENTRY3(LES, G,z, EM,p, None, None, chk(i64)), + [0xC5] = X86_OP_ENTRY3(LDS, G,z, EM,p, None, None, chk(i64)), + [0xC6] = X86_OP_GROUP3(group11, E,b, I,b, None, None), /* reg=000b */ + [0xC7] = X86_OP_GROUP3(group11, E,v, I,z, None, None), /* reg=000b */ + + [0xD0] = X86_OP_GROUP1(group2, E,b), + [0xD1] = X86_OP_GROUP1(group2, E,v), + [0xD2] = X86_OP_GROUP2(group2, E,b, 1,b), /* CL */ + [0xD3] = X86_OP_GROUP2(group2, E,v, 1,b), /* CL */ + [0xD4] = X86_OP_ENTRY2(AAM, 0,w, I,b), + [0xD5] = X86_OP_ENTRY2(AAD, 0,w, I,b), + [0xD6] = X86_OP_ENTRYw(SALC, 0,b), + [0xD7] = X86_OP_ENTRY1(XLAT, 0,b, zextT0), /* AL read/written */ + + [0xE0] = X86_OP_ENTRYr(LOOPNE, J,b), /* implicit: CX with aflag size */ + [0xE1] = X86_OP_ENTRYr(LOOPE, J,b), /* implicit: CX with aflag size */ + [0xE2] = X86_OP_ENTRYr(LOOP, J,b), /* implicit: CX with aflag size */ + [0xE3] = X86_OP_ENTRYr(JCXZ, J,b), /* implicit: CX with aflag size */ + [0xE4] = X86_OP_ENTRYwr(IN, 0,b, I_unsigned,b), /* AL */ + [0xE5] = X86_OP_ENTRYwr(IN, 0,z, I_unsigned,b), /* AX/EAX */ + [0xE6] = X86_OP_ENTRYrr(OUT, 0,b, I_unsigned,b), /* AL */ + [0xE7] = X86_OP_ENTRYrr(OUT, 0,z, I_unsigned,b), /* AX/EAX */ + + [0xF1] = X86_OP_ENTRY0(INT1, svm(ICEBP)), + [0xF4] = X86_OP_ENTRY0(HLT, chk(cpl0) svm(HLT)), + [0xF5] = X86_OP_ENTRY0(CMC), + [0xF6] = X86_OP_GROUP1(group3, E,b), + [0xF7] = X86_OP_GROUP1(group3, E,v), + + [0x08] = X86_OP_ENTRY2(OR, E,b, G,b, lock), + [0x09] = X86_OP_ENTRY2(OR, E,v, G,v, lock), + [0x0A] = X86_OP_ENTRY2(OR, G,b, E,b, lock), + [0x0B] = X86_OP_ENTRY2(OR, G,v, E,v, lock), + [0x0C] = X86_OP_ENTRY2(OR, 0,b, I,b, lock), /* AL, Ib */ + [0x0D] = X86_OP_ENTRY2(OR, 0,v, I,z, lock), /* rAX, Iz */ + [0x0E] = X86_OP_ENTRYr(PUSH, CS, w, chk(i64)), [0x0F] = X86_OP_GROUP0(0F), + + [0x18] = X86_OP_ENTRY2(SBB, E,b, G,b, lock), + [0x19] = X86_OP_ENTRY2(SBB, E,v, G,v, lock), + [0x1A] = X86_OP_ENTRY2(SBB, G,b, E,b, lock), + [0x1B] = X86_OP_ENTRY2(SBB, G,v, E,v, lock), + [0x1C] = X86_OP_ENTRY2(SBB, 0,b, I,b, lock), /* AL, Ib */ + [0x1D] = X86_OP_ENTRY2(SBB, 0,v, I,z, lock), /* rAX, Iz */ + [0x1E] = X86_OP_ENTRYr(PUSH, DS, w, chk(i64)), + [0x1F] = X86_OP_ENTRYw(POP, DS, w, chk(i64)), + + [0x28] = X86_OP_ENTRY2(SUB, E,b, G,b, lock), + [0x29] = X86_OP_ENTRY2(SUB, E,v, G,v, lock), + [0x2A] = X86_OP_ENTRY2(SUB, G,b, E,b, lock), + [0x2B] = X86_OP_ENTRY2(SUB, G,v, E,v, lock), + [0x2C] = X86_OP_ENTRY2(SUB, 0,b, I,b, lock), /* AL, Ib */ + [0x2D] = X86_OP_ENTRY2(SUB, 0,v, I,z, lock), /* rAX, Iz */ + [0x2E] = {}, + [0x2F] = X86_OP_ENTRY0(DAS, chk(i64)), + + [0x38] = X86_OP_ENTRYrr(SUB, E,b, G,b), + [0x39] = X86_OP_ENTRYrr(SUB, E,v, G,v), + [0x3A] = X86_OP_ENTRYrr(SUB, G,b, E,b), + [0x3B] = X86_OP_ENTRYrr(SUB, G,v, E,v), + [0x3C] = X86_OP_ENTRYrr(SUB, 0,b, I,b), /* AL, Ib */ + [0x3D] = X86_OP_ENTRYrr(SUB, 0,v, I,z), /* rAX, Iz */ + [0x3E] = {}, + [0x3F] = X86_OP_ENTRY0(AAS, chk(i64)), + + [0x48] = X86_OP_ENTRY1(DEC, 0,v, chk(i64)), + [0x49] = X86_OP_ENTRY1(DEC, 1,v, chk(i64)), + [0x4A] = X86_OP_ENTRY1(DEC, 2,v, chk(i64)), + [0x4B] = X86_OP_ENTRY1(DEC, 3,v, chk(i64)), + [0x4C] = X86_OP_ENTRY1(DEC, 4,v, chk(i64)), + [0x4D] = X86_OP_ENTRY1(DEC, 5,v, chk(i64)), + [0x4E] = X86_OP_ENTRY1(DEC, 6,v, chk(i64)), + [0x4F] = X86_OP_ENTRY1(DEC, 7,v, chk(i64)), + + [0x58] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x59] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5A] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5B] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5C] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5D] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5E] = X86_OP_ENTRYw(POP, LoBits,d64), + [0x5F] = X86_OP_ENTRYw(POP, LoBits,d64), + + [0x68] = X86_OP_ENTRYr(PUSH, I,z), + [0x69] = X86_OP_ENTRY3(IMUL3, G,v, E,v, I,z, sextT0), + [0x6A] = X86_OP_ENTRYr(PUSH, I,b), + [0x6B] = X86_OP_ENTRY3(IMUL3, G,v, E,v, I,b, sextT0), + [0x6C] = X86_OP_ENTRYrr(INS, Y,b, 2,w), /* DX */ + [0x6D] = X86_OP_ENTRYrr(INS, Y,z, 2,w), /* DX */ + [0x6E] = X86_OP_ENTRYrr(OUTS, X,b, 2,w), /* DX */ + [0x6F] = X86_OP_ENTRYrr(OUTS, X,z, 2,w), /* DX */ + + [0x78] = X86_OP_ENTRYr(Jcc, J,b), + [0x79] = X86_OP_ENTRYr(Jcc, J,b), + [0x7A] = X86_OP_ENTRYr(Jcc, J,b), + [0x7B] = X86_OP_ENTRYr(Jcc, J,b), + [0x7C] = X86_OP_ENTRYr(Jcc, J,b), + [0x7D] = X86_OP_ENTRYr(Jcc, J,b), + [0x7E] = X86_OP_ENTRYr(Jcc, J,b), + [0x7F] = X86_OP_ENTRYr(Jcc, J,b), + + [0x88] = X86_OP_ENTRYwr(MOV, E,b, G,b), + [0x89] = X86_OP_ENTRYwr(MOV, E,v, G,v), + [0x8A] = X86_OP_ENTRYwr(MOV, G,b, E,b), + [0x8B] = X86_OP_ENTRYwr(MOV, G,v, E,v), + /* Missing in Table A-2: memory destination is always 16-bit. */ + [0x8C] = X86_OP_ENTRYwr(MOV, E,v, S,w, op0_Mw), + [0x8D] = X86_OP_ENTRYwr(LEA, G,v, M,v, nolea), + [0x8E] = X86_OP_ENTRYwr(MOV, S,w, E,w), + [0x8F] = X86_OP_GROUPw(group1A, E,d64), + + [0x98] = X86_OP_ENTRY1(CBW, 0,v), /* rAX */ + [0x99] = X86_OP_ENTRYwr(CWD, 2,v, 0,v), /* rDX, rAX */ + [0x9A] = X86_OP_ENTRYrr(CALLF, I_unsigned,p, I_unsigned,w, chk(i64)), + [0x9B] = X86_OP_ENTRY0(WAIT), + [0x9C] = X86_OP_ENTRY0(PUSHF, chk(vm86_iopl) svm(PUSHF)), + [0x9D] = X86_OP_ENTRY0(POPF, chk(vm86_iopl) svm(POPF)), + [0x9E] = X86_OP_ENTRY0(SAHF), + [0x9F] = X86_OP_ENTRY0(LAHF), + + [0xA8] = X86_OP_ENTRYrr(AND, 0,b, I,b), /* AL, Ib */ + [0xA9] = X86_OP_ENTRYrr(AND, 0,v, I,z), /* rAX, Iz */ + [0xAA] = X86_OP_ENTRYwr(STOS, Y,b, 0,b), + [0xAB] = X86_OP_ENTRYwr(STOS, Y,v, 0,v), + /* Manual writeback because REP LODS (!) has to write EAX/RAX after every LODS. */ + [0xAC] = X86_OP_ENTRYr(LODS, X,b), + [0xAD] = X86_OP_ENTRYr(LODS, X,v), + [0xAE] = X86_OP_ENTRYrr(SCAS, 0,b, Y,b), + [0xAF] = X86_OP_ENTRYrr(SCAS, 0,v, Y,v), + + [0xB8] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xB9] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBA] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBB] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBC] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBD] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBE] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + [0xBF] = X86_OP_ENTRYwr(MOV, LoBits,v, I,v), + + [0xC8] = X86_OP_ENTRYrr(ENTER, I,w, I,b), + [0xC9] = X86_OP_ENTRY1(LEAVE, A,d64), + [0xCA] = X86_OP_ENTRYr(RETF, I,w), + [0xCB] = X86_OP_ENTRY0(RETF), + [0xCC] = X86_OP_ENTRY0(INT3), + [0xCD] = X86_OP_ENTRYr(INT, I,b, chk(vm86_iopl)), + [0xCE] = X86_OP_ENTRY0(INTO), + [0xCF] = X86_OP_ENTRY0(IRET, chk(vm86_iopl) svm(IRET)), + + /* + * x87 is nolea because it needs the address without segment base, + * in order to store it in fdp. + */ + [0xD8] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xD9] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xDA] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xDB] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xDC] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xDD] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xDE] = X86_OP_ENTRY1(x87, nop,v, nolea), + [0xDF] = X86_OP_ENTRY1(x87, nop,v, nolea), + + [0xE8] = X86_OP_ENTRYr(CALL, J,z_f64), + [0xE9] = X86_OP_ENTRYr(JMP, J,z_f64), + [0xEA] = X86_OP_ENTRYrr(JMPF, I_unsigned,p, I_unsigned,w, chk(i64)), + [0xEB] = X86_OP_ENTRYr(JMP, J,b), + [0xEC] = X86_OP_ENTRYwr(IN, 0,b, 2,w), /* AL, DX */ + [0xED] = X86_OP_ENTRYwr(IN, 0,z, 2,w), /* AX/EAX, DX */ + [0xEE] = X86_OP_ENTRYrr(OUT, 0,b, 2,w), /* DX, AL */ + [0xEF] = X86_OP_ENTRYrr(OUT, 0,z, 2,w), /* DX, AX/EAX */ + + [0xF8] = X86_OP_ENTRY0(CLC), + [0xF9] = X86_OP_ENTRY0(STC), + [0xFA] = X86_OP_ENTRY0(CLI, chk(iopl)), + [0xFB] = X86_OP_ENTRY0(STI, chk(iopl)), + [0xFC] = X86_OP_ENTRY0(CLD), + [0xFD] = X86_OP_ENTRY0(STD), + [0xFE] = X86_OP_GROUP1(group4_5, E,b), + [0xFF] = X86_OP_GROUP1(group4_5, E,v), }; #undef mmx @@ -1014,23 +1891,20 @@ static void decode_root(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui } -static int decode_modrm(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, - X86DecodedOp *op, X86OpType type) +static int decode_modrm(DisasContext *s, CPUX86State *env, + X86DecodedInsn *decode, X86DecodedOp *op) { int modrm = get_modrm(s, env); if ((modrm >> 6) == 3) { - if (s->prefix & PREFIX_LOCK) { - decode->e.gen = gen_illegal; - return 0xff; - } op->n = (modrm & 7); - if (type != X86_TYPE_Q && type != X86_TYPE_N) { + if (op->unit != X86_OP_MMX) { 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)); + decode->mem = gen_lea_modrm_0(env, s, modrm, + decode->e.vex_class == 12); } return modrm; } @@ -1067,10 +1941,18 @@ static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp *ot = s->dflag == MO_16 ? MO_32 : s->dflag; return true; + case X86_SIZE_y_d64: /* Full (not 16-bit) register access */ + *ot = CODE64(s) ? MO_64 : MO_32; + 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_z_f64: /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */ + *ot = !CODE64(s) && 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))) { @@ -1102,7 +1984,7 @@ static bool decode_op_size(DisasContext *s, X86OpEntry *e, X86OpSize size, MemOp *ot = s->vex_l ? MO_256 : MO_128; return true; - case X86_SIZE_ph: /* SSE/AVX packed half precision */ + case X86_SIZE_xh: /* SSE/AVX packed half register */ *ot = s->vex_l ? MO_128 : MO_64; return true; @@ -1129,6 +2011,8 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, 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 */ @@ -1138,11 +2022,34 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, case X86_TYPE_C: /* REG in the modrm byte selects a control register */ op->unit = X86_OP_CR; - goto get_reg; + op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + if (op->n == 0 && (s->prefix & PREFIX_LOCK) && + (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { + op->n = 8; + s->prefix &= ~PREFIX_LOCK; + } + if (op->n != 0 && op->n != 2 && op->n != 3 && op->n != 4 && op->n != 8) { + return false; + } + if (decode->e.intercept) { + decode->e.intercept += op->n; + } + break; case X86_TYPE_D: /* REG in the modrm byte selects a debug register */ op->unit = X86_OP_DR; - goto get_reg; + op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + if (op->n >= 8) { + /* + * illegal opcode. The DR4 and DR5 case is checked in the generated + * code instead, to save on hflags bits. + */ + return false; + } + if (decode->e.intercept) { + decode->e.intercept += op->n; + } + break; case X86_TYPE_G: /* REG in the modrm byte selects a GPR */ op->unit = X86_OP_INT; @@ -1164,7 +2071,10 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, op->unit = X86_OP_SSE; } get_reg: - op->n = ((get_modrm(s, env) >> 3) & 7) | REX_R(s); + op->n = ((get_modrm(s, env) >> 3) & 7); + if (op->unit != X86_OP_MMX) { + op->n |= REX_R(s); + } break; case X86_TYPE_E: /* ALU modrm operand */ @@ -1208,14 +2118,21 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, case X86_TYPE_WM: /* modrm byte selects an XMM/YMM memory operand */ op->unit = X86_OP_SSE; + goto get_modrm_mem; + + case X86_TYPE_EM: /* modrm byte selects an ALU memory operand */ + op->unit = X86_OP_INT; /* fall through */ case X86_TYPE_M: /* modrm byte selects a memory operand */ + get_modrm_mem: modrm = get_modrm(s, env); if ((modrm >> 6) == 3) { return false; } + /* fall through */ + case X86_TYPE_nop: /* modrm operand decoded but not fetched */ get_modrm: - decode_modrm(s, env, decode, op, type); + decode_modrm(s, env, decode, op); break; case X86_TYPE_O: /* Absolute address encoded in the instruction */ @@ -1244,43 +2161,20 @@ static bool decode_op(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, } case X86_TYPE_I: /* Immediate */ - op->unit = X86_OP_IMM; - decode->immediate = insn_get_signed(env, s, op->ot); - break; - case X86_TYPE_J: /* Relative offset for a jump */ op->unit = X86_OP_IMM; - decode->immediate = insn_get_signed(env, s, op->ot); - decode->immediate += s->pc - s->cs_base; - if (s->dflag == MO_16) { - decode->immediate &= 0xffff; - } else if (!CODE64(s)) { - decode->immediate &= 0xffffffffu; - } + decode->immediate = op->imm = insn_get_signed(env, s, op->ot); + break; + + case X86_TYPE_I_unsigned: /* Immediate */ + op->unit = X86_OP_IMM; + decode->immediate = op->imm = insn_get(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_X: /* string source */ - op->n = -1; - decode->mem = (AddressParts) { - .def_seg = R_DS, - .base = R_ESI, - .index = -1, - }; - break; - - case X86_TYPE_Y: /* string destination */ - op->n = -1; - decode->mem = (AddressParts) { - .def_seg = R_ES, - .base = R_EDI, - .index = -1, - }; - break; - case X86_TYPE_2op: *op = decode->op[0]; break; @@ -1397,6 +2291,16 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) switch (cpuid) { case X86_FEAT_None: return true; + case X86_FEAT_CMOV: + return (s->cpuid_features & CPUID_CMOV); + case X86_FEAT_CLFLUSH: + return (s->cpuid_features & CPUID_CLFLUSH); + case X86_FEAT_CX8: + return (s->cpuid_features & CPUID_CX8); + case X86_FEAT_FXSR: + return (s->cpuid_features & CPUID_FXSR); + case X86_FEAT_CX16: + return (s->cpuid_ext_features & CPUID_EXT_CX16); case X86_FEAT_F16C: return (s->cpuid_ext_features & CPUID_EXT_F16C); case X86_FEAT_FMA: @@ -1405,10 +2309,12 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) return (s->cpuid_ext_features & CPUID_EXT_MOVBE); case X86_FEAT_PCLMULQDQ: return (s->cpuid_ext_features & CPUID_EXT_PCLMULQDQ); + case X86_FEAT_POPCNT: + return (s->cpuid_ext_features & CPUID_EXT_POPCNT); case X86_FEAT_SSE: - return (s->cpuid_ext_features & CPUID_SSE); + return (s->cpuid_features & CPUID_SSE); case X86_FEAT_SSE2: - return (s->cpuid_ext_features & CPUID_SSE2); + return (s->cpuid_features & CPUID_SSE2); case X86_FEAT_SSE3: return (s->cpuid_ext_features & CPUID_EXT_SSE3); case X86_FEAT_SSSE3: @@ -1430,6 +2336,8 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) case X86_FEAT_AVX: return (s->cpuid_ext_features & CPUID_EXT_AVX); + case X86_FEAT_XSAVE: + return (s->cpuid_ext_features & CPUID_EXT_XSAVE); case X86_FEAT_3DNOW: return (s->cpuid_ext2_features & CPUID_EXT2_3DNOW); @@ -1444,6 +2352,20 @@ static bool has_cpuid_feature(DisasContext *s, X86CPUIDFeature cpuid) 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_CLFLUSHOPT: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT); + case X86_FEAT_CLWB: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB); + case X86_FEAT_FSGSBASE: + return (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE); + 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); + + case X86_FEAT_XSAVEOPT: + return (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT); } g_assert_not_reached(); } @@ -1458,9 +2380,9 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) * 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); + assert(e->vex_class == 1 || e->vex_class == 2 || e->vex_class == 4); if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { - e->vex_class = 3; + e->vex_class = e->vex_class < 4 ? 3 : 5; if (s->vex_l) { goto illegal; } @@ -1481,8 +2403,6 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) } } - /* TODO: instructions that require VEX.W=0 (Table 2-16) */ - switch (e->vex_class) { case 0: if (s->prefix & PREFIX_VEX) { @@ -1567,6 +2487,24 @@ static bool validate_vex(DisasContext *s, X86DecodedInsn *decode) 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: @@ -1577,39 +2515,36 @@ illegal: return false; } -static void decode_temp_free(X86DecodedOp *op) -{ - if (op->v_ptr) { - tcg_temp_free_ptr(op->v_ptr); - } -} - -static void decode_temps_free(X86DecodedInsn *decode) -{ - decode_temp_free(&decode->op[0]); - decode_temp_free(&decode->op[1]); - decode_temp_free(&decode->op[2]); -} - /* * 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) +static void disas_insn(DisasContext *s, CPUState *cpu) { - CPUX86State *env = cpu->env_ptr; - bool first = true; + CPUX86State *env = cpu_env(cpu); X86DecodedInsn decode; X86DecodeFunc decode_func = decode_root; + bool accept_lock = false; + uint8_t cc_live, b; + s->pc = s->base.pc_next; + s->override = -1; + s->popl_esp_hack = 0; +#ifdef TARGET_X86_64 + s->rex_r = 0; + s->rex_x = 0; + s->rex_b = 0; +#endif + s->rip_offset = 0; /* for relative ip address */ + s->vex_l = 0; + s->vex_v = 0; + s->vex_w = false; s->has_modrm = false; + s->prefix = 0; next_byte: - if (first) { - first = false; - } else { - b = x86_ldub_code(env, s); - } + b = x86_ldub_code(env, s); + /* Collect prefixes. */ switch (b) { case 0xf3: @@ -1721,10 +2656,6 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } break; default: - if (b >= 0x100) { - b -= 0x100; - decode_func = do_decode_0F; - } break; } @@ -1754,6 +2685,7 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } memset(&decode, 0, sizeof(decode)); + decode.cc_op = -1; decode.b = b; if (!decode_insn(s, env, decode_func, &decode)) { goto illegal_op; @@ -1766,6 +2698,35 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) goto illegal_op; } + /* Checks that result in #UD come first. */ + if (decode.e.check) { + if (CODE64(s)) { + if (decode.e.check & X86_CHECK_i64) { + goto illegal_op; + } + if ((decode.e.check & X86_CHECK_i64_amd) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { + goto illegal_op; + } + } else { + if (decode.e.check & X86_CHECK_o64) { + goto illegal_op; + } + if ((decode.e.check & X86_CHECK_o64_intel) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_prot_or_vm86) { + if (!PE(s)) { + goto illegal_op; + } + } + if (decode.e.check & X86_CHECK_no_vm86) { + if (VM86(s)) { + goto illegal_op; + } + } + } + switch (decode.e.special) { case X86_SPECIAL_None: break; @@ -1774,36 +2735,23 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) if (decode.op[0].has_ea) { s->prefix |= PREFIX_LOCK; } + /* fallthrough */ + case X86_SPECIAL_HasLock: + case X86_SPECIAL_BitTest: + accept_lock = decode.op[0].has_ea; break; - case X86_SPECIAL_ProtMode: - if (!PE(s) || VM86(s)) { - goto illegal_op; - } - break; - - case X86_SPECIAL_i64: - if (CODE64(s)) { - goto illegal_op; - } - break; - case X86_SPECIAL_o64: - if (!CODE64(s)) { - goto illegal_op; - } - break; - - case X86_SPECIAL_ZExtOp0: + 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_ZExtOp2: + case X86_SPECIAL_Op2_Ry: assert(decode.op[2].unit == X86_OP_INT); if (!decode.op[2].has_ea) { - decode.op[2].ot = MO_32; + decode.op[2].ot = s->dflag == MO_16 ? MO_32 : s->dflag; } break; @@ -1815,28 +2763,76 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } break; + case X86_SPECIAL_SExtT0: + case X86_SPECIAL_ZExtT0: + /* Handled in gen_load. */ + assert(decode.op[1].unit == X86_OP_INT); + break; + + case X86_SPECIAL_Op0_Mw: + assert(decode.op[0].unit == X86_OP_INT); + if (decode.op[0].has_ea) { + decode.op[0].ot = MO_16; + } + break; + default: break; } + if ((s->prefix & PREFIX_LOCK) && !accept_lock) { + 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. after all + * exceptions if there is no memory operand). Exceptions are + * vm86 checks (INTn, IRET, PUSHF/POPF), RSM and XSETBV (!). + * + * XSETBV will check for CPL0 in the gen_* function instead of using chk(). + */ + if (decode.e.check & X86_CHECK_cpl0) { + if (CPL(s) != 0) { + goto gp_fault; + } + } + if (decode.e.has_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_smm) && !(s->flags & HF_SMM_MASK)) { + goto illegal_op; + } + 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_enter_mmx(s); } - 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 (decode.e.special != X86_SPECIAL_NoLoadEA && + (decode.op[0].has_ea || decode.op[1].has_ea || decode.op[2].has_ea)) { + gen_lea_modrm(s, &decode); } if (s->prefix & PREFIX_LOCK) { - if (decode.op[0].unit != X86_OP_INT || !decode.op[0].has_ea) { - goto illegal_op; - } + assert(decode.op[0].has_ea && !decode.op[2].has_ea); gen_load(s, &decode, 2, s->T1); - decode.e.gen(s, env, &decode); + decode.e.gen(s, &decode); } else { if (decode.op[0].unit == X86_OP_MMX) { compute_mmx_offset(&decode.op[0]); @@ -1845,10 +2841,44 @@ static void disas_insn_new(DisasContext *s, CPUState *cpu, int b) } gen_load(s, &decode, 1, s->T0); gen_load(s, &decode, 2, s->T1); - decode.e.gen(s, env, &decode); + decode.e.gen(s, &decode); gen_writeback(s, &decode, 0, s->T0); } - decode_temps_free(&decode); + + /* + * Write back flags after last memory access. Some older 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); diff --git a/target/i386/tcg/decode-new.h b/target/i386/tcg/decode-new.h index cb6b8bcf67..7f23d373ea 100644 --- a/target/i386/tcg/decode-new.h +++ b/target/i386/tcg/decode-new.h @@ -47,7 +47,10 @@ typedef enum X86OpType { X86_TYPE_Y, /* string destination */ /* Custom */ + X86_TYPE_EM, /* modrm byte selects an ALU memory operand */ X86_TYPE_WM, /* modrm byte selects an XMM/YMM memory operand */ + X86_TYPE_I_unsigned, /* Immediate, zero-extended */ + X86_TYPE_nop, /* modrm operand decoded but not loaded into s->T{0,1} */ 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) */ @@ -87,12 +90,14 @@ typedef enum X86OpSize { 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_y_d64, /* 32/64-bit, based on 64-bit mode */ X86_SIZE_z, /* 16-bit for 16-bit operand size, else 32-bit */ + X86_SIZE_z_f64, /* 32-bit for 32-bit operand size or 64-bit mode, else 16-bit */ /* Custom */ X86_SIZE_d64, X86_SIZE_f64, - X86_SIZE_ph, /* SSE/AVX packed half precision */ + X86_SIZE_xh, /* SSE/AVX packed half register */ } X86OpSize; typedef enum X86CPUIDFeature { @@ -104,10 +109,21 @@ typedef enum X86CPUIDFeature { X86_FEAT_AVX2, X86_FEAT_BMI1, X86_FEAT_BMI2, + X86_FEAT_CLFLUSH, + X86_FEAT_CLFLUSHOPT, + X86_FEAT_CLWB, + X86_FEAT_CMOV, + X86_FEAT_CMPCCXADD, + X86_FEAT_CX8, + X86_FEAT_CX16, X86_FEAT_F16C, X86_FEAT_FMA, + X86_FEAT_FSGSBASE, + X86_FEAT_FXSR, X86_FEAT_MOVBE, X86_FEAT_PCLMULQDQ, + X86_FEAT_POPCNT, + X86_FEAT_SHA_NI, X86_FEAT_SSE, X86_FEAT_SSE2, X86_FEAT_SSE3, @@ -115,6 +131,8 @@ typedef enum X86CPUIDFeature { X86_FEAT_SSE41, X86_FEAT_SSE42, X86_FEAT_SSE4A, + X86_FEAT_XSAVE, + X86_FEAT_XSAVEOPT, } X86CPUIDFeature; /* Execution flags */ @@ -130,21 +148,71 @@ typedef enum X86OpUnit { 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 in vm86 mode */ + X86_CHECK_no_vm86 = 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, + + /* Fault outside protected mode, possibly including vm86 mode */ + X86_CHECK_prot_or_vm86 = 512, + X86_CHECK_prot = X86_CHECK_prot_or_vm86 | X86_CHECK_no_vm86, + + /* Fault outside SMM */ + X86_CHECK_smm = 1024, + + /* Vendor-specific checks for Intel/AMD differences */ + X86_CHECK_i64_amd = 2048, + X86_CHECK_o64_intel = 4096, +} 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, - /* Fault outside protected mode */ - X86_SPECIAL_ProtMode, + /* Like HasLock, but also operand 2 provides bit displacement into memory. */ + X86_SPECIAL_BitTest, + + /* Do not load effective address in s->A0 */ + X86_SPECIAL_NoLoadEA, /* - * Register operand 0/2 is zero extended to 32 bits. Rd/Mb or Rd/Mw - * in the manual. + * 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_ZExtOp0, - X86_SPECIAL_ZExtOp2, + 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 @@ -158,9 +226,12 @@ typedef enum X86InsnSpecial { */ X86_SPECIAL_MMX, - /* Illegal or exclusive to 64-bit mode */ - X86_SPECIAL_i64, - X86_SPECIAL_o64, + /* When loaded into s->T0, register operand 1 is zero/sign extended. */ + X86_SPECIAL_SExtT0, + X86_SPECIAL_ZExtT0, + + /* Memory operand size of MOV from segment register is MO_16 */ + X86_SPECIAL_Op0_Mw, } X86InsnSpecial; /* @@ -195,12 +266,13 @@ typedef enum X86VEXSpecial { typedef struct X86OpEntry X86OpEntry; typedef struct X86DecodedInsn X86DecodedInsn; +struct DisasContext; /* Decode function for multibyte opcodes. */ -typedef void (*X86DecodeFunc)(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b); +typedef void (*X86DecodeFunc)(struct DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b); /* Code generation function. */ -typedef void (*X86GenFunc)(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode); +typedef void (*X86GenFunc)(struct DisasContext *s, X86DecodedInsn *decode); struct X86OpEntry { /* Based on the is_decode flags. */ @@ -223,7 +295,10 @@ struct X86OpEntry { X86CPUIDFeature cpuid:8; unsigned vex_class:8; X86VEXSpecial vex_special:8; - uint16_t valid_prefix:16; + unsigned valid_prefix:16; + unsigned check:16; + unsigned intercept:8; + bool has_intercept:1; bool is_decode:1; }; @@ -234,19 +309,39 @@ typedef struct X86DecodedOp { 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; + union { + target_ulong imm; + /* + * This field is used internally by macros OP0_PTR/OP1_PTR/OP2_PTR, + * do not access directly! + */ + TCGv_ptr v_ptr; + }; } X86DecodedOp; +typedef struct AddressParts { + int def_seg; + int base; + int index; + int scale; + target_long disp; +} AddressParts; + struct X86DecodedInsn { X86OpEntry e; X86DecodedOp op[3]; + /* + * Rightmost immediate, for convenience since most instructions have + * one (and also for 4-operand instructions). + */ target_ulong immediate; AddressParts mem; + TCGv cc_dst, cc_src, cc_src2; + TCGv_i32 cc_op_dynamic; + int8_t cc_op; + uint8_t b; }; +static void gen_lea_modrm(struct DisasContext *s, X86DecodedInsn *decode); diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index b1f9494aaa..85bdb2aac4 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -19,7 +19,27 @@ * License along with this library; if not, see . */ -#define ZMM_OFFSET(reg) offsetof(CPUX86State, xmm_regs[reg]) +/* + * Sometimes, knowing what the backend has can produce better code. + * The exact opcode to check depends on 32- vs. 64-bit. + */ +#ifdef TARGET_X86_64 +#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i64 +#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i64_valid +#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i64_valid +#else +#define TCG_TARGET_HAS_extract2_tl TCG_TARGET_HAS_extract2_i32 +#define TCG_TARGET_deposit_tl_valid TCG_TARGET_deposit_i32_valid +#define TCG_TARGET_extract_tl_valid TCG_TARGET_extract_i32_valid +#endif + +#define MMX_OFFSET(reg) \ + ({ assert((reg) >= 0 && (reg) <= 7); \ + offsetof(CPUX86State, fpregs[reg].mmx); }) + +#define ZMM_OFFSET(reg) \ + ({ assert((reg) >= 0 && (reg) <= 15); \ + 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); @@ -45,6 +65,9 @@ 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 void gen_JMP_m(DisasContext *s, X86DecodedInsn *decode); +static void gen_JMP(DisasContext *s, X86DecodedInsn *decode); + static inline TCGv_i32 tcg_constant8u_i32(uint8_t val) { return tcg_constant_i32(val); @@ -55,15 +78,27 @@ static void gen_NM_exception(DisasContext *s) gen_exception(s, EXCP07_PREX); } -static void gen_illegal(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_lea_modrm(DisasContext *s, X86DecodedInsn *decode) { - gen_illegal_opcode(s); -} + AddressParts *mem = &decode->mem; + TCGv ea; -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); + ea = gen_lea_modrm_1(s, *mem, decode->e.vex_class == 12); + if (decode->e.special == X86_SPECIAL_BitTest) { + MemOp ot = decode->op[1].ot; + int poslen = 8 << ot; + int opn = decode->op[2].n; + TCGv ofs = tcg_temp_new(); + + /* Extract memory displacement from the second operand. */ + assert(decode->op[2].unit == X86_OP_INT && decode->op[2].ot != MO_8); + tcg_gen_sextract_tl(ofs, cpu_regs[opn], 3, poslen - 3); + tcg_gen_andi_tl(ofs, ofs, -1 << ot); + tcg_gen_add_tl(s->A0, ea, ofs); + ea = s->A0; + } + + gen_lea_v_seg(s, ea, mem->def_seg, s->override); } static inline int mmx_offset(MemOp ot) @@ -155,7 +190,7 @@ static int vector_elem_offset(X86DecodedOp *op, MemOp ot, int n) static void compute_mmx_offset(X86DecodedOp *op) { if (!op->has_ea) { - op->offset = offsetof(CPUX86State, fpregs[op->n].mmx) + mmx_offset(op->ot); + op->offset = MMX_OFFSET(op->n) + mmx_offset(op->ot); } else { op->offset = offsetof(CPUX86State, mmx_t0) + mmx_offset(op->ot); } @@ -175,15 +210,15 @@ static void gen_load_sse(DisasContext *s, TCGv temp, MemOp ot, int dest_ofs, boo switch(ot) { case MO_8: gen_op_ld_v(s, MO_8, temp, s->A0); - tcg_gen_st8_tl(temp, cpu_env, dest_ofs); + 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, cpu_env, dest_ofs); + 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, cpu_env, dest_ofs); + tcg_gen_st32_tl(temp, tcg_env, dest_ofs); break; case MO_64: gen_ldq_env_A0(s, dest_ofs); @@ -226,24 +261,53 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) case X86_OP_SKIP: return; case X86_OP_SEG: - tcg_gen_ld32u_tl(v, cpu_env, + tcg_gen_ld32u_tl(v, tcg_env, offsetof(CPUX86State,segs[op->n].selector)); break; +#ifndef CONFIG_USER_ONLY case X86_OP_CR: - tcg_gen_ld_tl(v, cpu_env, offsetof(CPUX86State, cr[op->n])); + if (op->n == 8) { + translator_io_start(&s->base); + gen_helper_read_cr8(v, tcg_env); + } else { + tcg_gen_ld_tl(v, tcg_env, offsetof(CPUX86State, cr[op->n])); + } break; case X86_OP_DR: - tcg_gen_ld_tl(v, cpu_env, offsetof(CPUX86State, dr[op->n])); + /* CR4.DE tested in the helper. */ + gen_helper_get_dr(v, tcg_env, tcg_constant_i32(op->n)); break; +#endif case X86_OP_INT: if (op->has_ea) { - gen_op_ld_v(s, op->ot, v, s->A0); + 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 { - gen_op_mov_v_reg(s, op->ot, v, op->n); + tcg_gen_mov_tl(v, cpu_regs[op->n]); } break; case X86_OP_IMM: - tcg_gen_movi_tl(v, decode->immediate); + tcg_gen_movi_tl(v, op->imm); break; case X86_OP_MMX: @@ -267,13 +331,15 @@ static void gen_load(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv v) static TCGv_ptr op_ptr(X86DecodedInsn *decode, int opn) { X86DecodedOp *op = &decode->op[opn]; + + assert(op->unit == X86_OP_MMX || op->unit == X86_OP_SSE); 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, cpu_env, vector_reg_offset(op)); + tcg_gen_addi_ptr(op->v_ptr, tcg_env, vector_reg_offset(op)); return op->v_ptr; } @@ -288,8 +354,8 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv 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); + /* Note that gen_movl_seg takes care of interrupt shadow and TF. */ + gen_movl_seg(s, op->n, s->T0); break; case X86_OP_INT: if (op->has_ea) { @@ -307,11 +373,24 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv 16, 16, 0); } break; +#ifndef CONFIG_USER_ONLY case X86_OP_CR: + if (op->n == 8) { + translator_io_start(&s->base); + } + gen_helper_write_crN(tcg_env, tcg_constant_i32(op->n), v); + s->base.is_jmp = DISAS_EOB_NEXT; + break; case X86_OP_DR: + /* CR4.DE tested in the helper. */ + gen_helper_set_dr(tcg_env, tcg_constant_i32(op->n), v); + s->base.is_jmp = DISAS_EOB_NEXT; + break; +#endif default: g_assert_not_reached(); } + op->unit = X86_OP_SKIP; } static inline int vector_len(DisasContext *s, X86DecodedInsn *decode) @@ -323,6 +402,59 @@ static inline int vector_len(DisasContext *s, X86DecodedInsn *decode) 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 prepare_update_cc_incdec(X86DecodedInsn *decode, DisasContext *s, CCOp op) +{ + gen_compute_eflags_c(s, s->T1); + prepare_update2_cc(decode, s, op); +} + +static void prepare_update3_cc(X86DecodedInsn *decode, DisasContext *s, CCOp op, TCGv reg) +{ + decode->cc_src2 = reg; + decode->cc_src = s->T1; + decode->cc_dst = s->T0; + decode->cc_op = op; +} + +/* Set up decode->cc_* to modify CF while keeping other flags unchanged. */ +static void prepare_update_cf(X86DecodedInsn *decode, DisasContext *s, TCGv cf) +{ + switch (s->cc_op) { + case CC_OP_ADOX: + case CC_OP_ADCOX: + decode->cc_src2 = cpu_cc_src2; + decode->cc_src = cpu_cc_src; + decode->cc_op = CC_OP_ADCOX; + break; + + case CC_OP_EFLAGS: + case CC_OP_ADCX: + decode->cc_src = cpu_cc_src; + decode->cc_op = CC_OP_ADCX; + break; + + default: + decode->cc_src = tcg_temp_new(); + gen_mov_eflags(s, decode->cc_src); + decode->cc_op = CC_OP_ADCX; + break; + } + decode->cc_dst = cf; +} + static void gen_store_sse(DisasContext *s, X86DecodedInsn *decode, int src_ofs) { MemOp ot = decode->op[0].ot; @@ -382,7 +514,7 @@ static const SSEFunc_0_epp fns_3dnow[] = { [0xbf] = gen_helper_pavgusb, }; -static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_3dnow(DisasContext *s, X86DecodedInsn *decode) { uint8_t b = decode->immediate; SSEFunc_0_epp fn = b < ARRAY_SIZE(fns_3dnow) ? fns_3dnow[b] : NULL; @@ -402,10 +534,10 @@ static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) gen_enter_mmx(s); if (fn == FN_3DNOW_MOVE) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, decode->op[1].offset); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset); + 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(cpu_env, OP_PTR0, OP_PTR1); + fn(tcg_env, OP_PTR0, OP_PTR1); } } @@ -415,7 +547,7 @@ static void gen_3dnow(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) * 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, +static inline void gen_unary_fp_sse(DisasContext *s, 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) @@ -426,7 +558,7 @@ static inline void gen_unary_fp_sse(DisasContext *s, CPUX86State *env, X86Decode gen_illegal_opcode(s); return; } - fn(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + fn(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } else { SSEFunc_0_epp ps, pd, fn; ps = s->vex_l ? ps_ymm : ps_xmm; @@ -436,13 +568,13 @@ static inline void gen_unary_fp_sse(DisasContext *s, CPUX86State *env, X86Decode gen_illegal_opcode(s); return; } - fn(cpu_env, OP_PTR0, OP_PTR2); + fn(tcg_env, OP_PTR0, OP_PTR2); } } #define UNARY_FP_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_fp_sse(s, env, decode, \ + gen_unary_fp_sse(s, decode, \ gen_helper_##lname##pd_xmm, \ gen_helper_##lname##ps_xmm, \ gen_helper_##lname##pd_ymm, \ @@ -458,7 +590,7 @@ UNARY_FP_SSE(VSQRT, sqrt) * f3 = v*ss Vss, Hss, Wps * f2 = v*sd Vsd, Hsd, Wps */ -static inline void gen_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_fp_sse(DisasContext *s, 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) @@ -472,16 +604,16 @@ static inline void gen_fp_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn fn = s->prefix & PREFIX_DATA ? pd : ps; } if (fn) { - fn(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_fp_sse(s, env, decode, \ + gen_fp_sse(s, decode, \ gen_helper_##lname##pd_xmm, \ gen_helper_##lname##ps_xmm, \ gen_helper_##lname##pd_ymm, \ @@ -497,24 +629,24 @@ 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) \ +static void gen_##uname##Px(DisasContext *s, 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(cpu_env, OP_PTR0, ptr0, ptr1, ptr2, \ + 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) \ +static void gen_##uname##Sx(DisasContext *s, X86DecodedInsn *decode) \ { \ SSEFunc_0_eppppi fn = s->vex_w ? gen_helper_fma4sd : gen_helper_fma4ss; \ \ - fn(cpu_env, OP_PTR0, ptr0, ptr1, ptr2, \ + fn(tcg_env, OP_PTR0, ptr0, ptr1, ptr2, \ tcg_constant_i32(flags)); \ } \ @@ -543,10 +675,10 @@ 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ /* PS maps to the DQ integer instruction, PD maps to QDQ. */ \ - gen_fp_sse(s, env, decode, \ + gen_fp_sse(s, decode, \ gen_helper_##lname##qdq_xmm, \ gen_helper_##lname##dq_xmm, \ gen_helper_##lname##qdq_ymm, \ @@ -560,7 +692,7 @@ 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, +static inline void gen_unary_fp32_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epp ps_xmm, SSEFunc_0_epp ps_ymm, SSEFunc_0_eppp ss) @@ -571,13 +703,13 @@ static inline void gen_unary_fp32_sse(DisasContext *s, CPUX86State *env, X86Deco if (!ss) { goto illegal_op; } - ss(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + 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(cpu_env, OP_PTR0, OP_PTR2); + fn(tcg_env, OP_PTR0, OP_PTR2); } return; @@ -585,9 +717,9 @@ illegal_op: gen_illegal_opcode(s); } #define UNARY_FP32_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_fp32_sse(s, env, decode, \ + gen_unary_fp32_sse(s, decode, \ gen_helper_##lname##ps_xmm, \ gen_helper_##lname##ps_ymm, \ gen_helper_##lname##ss); \ @@ -599,7 +731,7 @@ 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, +static inline void gen_horizontal_fp_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppp pd_xmm, SSEFunc_0_eppp ps_xmm, SSEFunc_0_eppp pd_ymm, SSEFunc_0_eppp ps_ymm) { @@ -607,12 +739,12 @@ static inline void gen_horizontal_fp_sse(DisasContext *s, CPUX86State *env, X86D ps = s->vex_l ? ps_ymm : ps_xmm; pd = s->vex_l ? pd_ymm : pd_xmm; fn = s->prefix & PREFIX_DATA ? pd : ps; - fn(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_horizontal_fp_sse(s, env, decode, \ + gen_horizontal_fp_sse(s, decode, \ gen_helper_##lname##pd_xmm, gen_helper_##lname##ps_xmm, \ gen_helper_##lname##pd_ymm, gen_helper_##lname##ps_ymm); \ } @@ -620,47 +752,46 @@ 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, +static inline void gen_ternary_sse(DisasContext *s, 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, cpu_env, ZMM_OFFSET(op3)); - fn(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, ptr3); - tcg_temp_free_ptr(ptr3); + 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) \ +static void gen_##uvname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_ternary_sse(s, env, decode, (uint8_t)decode->immediate >> 4, \ + gen_ternary_sse(s, decode, (uint8_t)decode->immediate >> 4, \ gen_helper_##lname##_xmm, gen_helper_##lname##_ymm); \ } \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_ternary_sse(s, env, decode, 0, \ + gen_ternary_sse(s, 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, +static inline void gen_binary_imm_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epppi xmm, SSEFunc_0_epppi ymm) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); if (!s->vex_l) { - xmm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } else { - ymm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_binary_imm_sse(s, env, decode, \ + gen_binary_imm_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -676,7 +807,7 @@ BINARY_IMM_SSE(PCLMULQDQ, pclmulqdq) #define UNARY_INT_GVEC(uname, func, ...) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ int vec_len = vector_len(s, decode); \ \ @@ -694,7 +825,7 @@ 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ int vec_len = vector_len(s, decode); \ \ @@ -753,7 +884,7 @@ BINARY_INT_GVEC(PXOR, tcg_gen_gvec_xor, MO_64) * 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, +static inline void gen_binary_int_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppp mmx, SSEFunc_0_eppp xmm, SSEFunc_0_eppp ymm) { assert(!!mmx == !!(decode->e.special == X86_SPECIAL_MMX)); @@ -764,19 +895,19 @@ static inline void gen_binary_int_sse(DisasContext *s, CPUX86State *env, X86Deco return; } if (!(s->prefix & PREFIX_DATA)) { - mmx(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + mmx(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } else if (!s->vex_l) { - xmm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } else { - ymm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_binary_int_sse(s, env, decode, \ + gen_binary_int_sse(s, decode, \ gen_helper_##lname##_mmx, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ @@ -823,9 +954,9 @@ 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_binary_int_sse(s, env, decode, \ + gen_binary_int_sse(s, decode, \ NULL, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ @@ -848,33 +979,33 @@ 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ if (!s->vex_l) { \ - gen_helper_##lname##_xmm(cpu_env, OP_PTR1, OP_PTR2); \ + gen_helper_##lname##_xmm(tcg_env, OP_PTR1, OP_PTR2); \ } else { \ - gen_helper_##lname##_ymm(cpu_env, OP_PTR1, OP_PTR2); \ + gen_helper_##lname##_ymm(tcg_env, OP_PTR1, OP_PTR2); \ } \ - set_cc_op(s, CC_OP_EFLAGS); \ + assume_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, +static inline void gen_unary_int_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epp xmm, SSEFunc_0_epp ymm) { if (!s->vex_l) { - xmm(cpu_env, OP_PTR0, OP_PTR2); + xmm(tcg_env, OP_PTR0, OP_PTR2); } else { - ymm(cpu_env, OP_PTR0, OP_PTR2); + ymm(tcg_env, OP_PTR0, OP_PTR2); } } #define UNARY_INT_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_int_sse(s, env, decode, \ + gen_unary_int_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -906,7 +1037,7 @@ UNARY_INT_SSE(VCVTTPS2DQ, cvttps2dq) UNARY_INT_SSE(VCVTPH2PS, cvtph2ps) -static inline void gen_unary_imm_sse(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_unary_imm_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_ppi xmm, SSEFunc_0_ppi ymm) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); @@ -918,9 +1049,9 @@ static inline void gen_unary_imm_sse(DisasContext *s, CPUX86State *env, X86Decod } #define UNARY_IMM_SSE(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_imm_sse(s, env, decode, \ + gen_unary_imm_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -933,21 +1064,21 @@ 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, +static inline void gen_unary_imm_fp_sse(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppi xmm, SSEFunc_0_eppi ymm) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); if (!s->vex_l) { - xmm(cpu_env, OP_PTR0, OP_PTR1, imm); + xmm(tcg_env, OP_PTR0, OP_PTR1, imm); } else { - ymm(cpu_env, OP_PTR0, OP_PTR1, imm); + 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_unary_imm_fp_sse(s, env, decode, \ + gen_unary_imm_fp_sse(s, decode, \ gen_helper_##lname##_xmm, \ gen_helper_##lname##_ymm); \ } @@ -955,21 +1086,21 @@ static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod UNARY_IMM_FP_SSE(VROUNDPS, roundps) UNARY_IMM_FP_SSE(VROUNDPD, roundpd) -static inline void gen_vexw_avx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_vexw_avx(DisasContext *s, 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(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + 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) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_vexw_avx(s, env, decode, \ + gen_vexw_avx(s, decode, \ gen_helper_##lname##d_xmm, gen_helper_##lname##q_xmm, \ gen_helper_##lname##d_ymm, gen_helper_##lname##q_ymm); \ } @@ -979,7 +1110,7 @@ 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, +static inline void gen_vsib_avx(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_epppti d_xmm, SSEFunc_0_epppti q_xmm, SSEFunc_0_epppti d_ymm, SSEFunc_0_epppti q_ymm) { @@ -990,8 +1121,8 @@ static inline void gen_vsib_avx(DisasContext *s, CPUX86State *env, X86DecodedIns TCGv_ptr index = tcg_temp_new_ptr(); /* Pass third input as (index, base, scale) */ - tcg_gen_addi_ptr(index, cpu_env, ZMM_OFFSET(decode->mem.index)); - fn(cpu_env, OP_PTR0, OP_PTR1, index, s->A0, 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 @@ -1001,35 +1132,99 @@ static inline void gen_vsib_avx(DisasContext *s, CPUX86State *env, X86DecodedIns int ymmh_ofs = vector_elem_offset(&decode->op[1], MO_128, 1); tcg_gen_gvec_dup_imm(MO_64, ymmh_ofs, 16, 16, 0); } - tcg_temp_free_ptr(index); } #define VSIB_AVX(uname, lname) \ -static void gen_##uname(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) \ +static void gen_##uname(DisasContext *s, X86DecodedInsn *decode) \ { \ - gen_vsib_avx(s, env, decode, \ + gen_vsib_avx(s, 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) -static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) +static void gen_AAA(DisasContext *s, X86DecodedInsn *decode) { - int opposite_cc_op; + gen_update_cc_op(s); + gen_helper_aaa(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_AAD(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_aad(s->T0, s->T0, s->T1); + prepare_update1_cc(decode, s, CC_OP_LOGICB); +} + +static void gen_AAM(DisasContext *s, X86DecodedInsn *decode) +{ + if (decode->immediate == 0) { + gen_exception(s, EXCP00_DIVZ); + } else { + gen_helper_aam(s->T0, s->T0, s->T1); + prepare_update1_cc(decode, s, CC_OP_LOGICB); + } +} + +static void gen_AAS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_aas(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_ADC(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv c_in = tcg_temp_new(); + + gen_compute_eflags_c(s, c_in); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(s->T0, c_in, s->T1); + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s->T0, s->T0, s->T1); + tcg_gen_add_tl(s->T0, s->T0, c_in); + } + prepare_update3_cc(decode, s, CC_OP_ADCB + ot, c_in); +} + +static void gen_ADCOX(DisasContext *s, X86DecodedInsn *decode, int cc_op) +{ + MemOp ot = decode->op[0].ot; TCGv carry_in = NULL; - TCGv carry_out = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2); + TCGv *carry_out = (cc_op == CC_OP_ADCX ? &decode->cc_dst : &decode->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); + decode->cc_op = cc_op; + *carry_out = tcg_temp_new(); + if (CC_OP_HAS_EFLAGS(s->cc_op)) { + decode->cc_src = cpu_cc_src; + + /* Re-use the carry-out from a previous round? */ + if (s->cc_op == cc_op || s->cc_op == CC_OP_ADCOX) { + carry_in = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2); } - carry_in = s->tmp0; - tcg_gen_extract_tl(carry_in, cpu_cc_src, + + /* Preserve the opposite carry from previous rounds? */ + if (s->cc_op != cc_op && s->cc_op != CC_OP_EFLAGS) { + decode->cc_op = CC_OP_ADCOX; + if (carry_out == &decode->cc_dst) { + decode->cc_src2 = cpu_cc_src2; + } else { + decode->cc_dst = cpu_cc_dst; + } + } + } else { + decode->cc_src = tcg_temp_new(); + gen_mov_eflags(s, decode->cc_src); + } + + if (!carry_in) { + /* Get carry_in out of EFLAGS. */ + carry_in = tcg_temp_new(); + tcg_gen_extract_tl(carry_in, decode->cc_src, ctz32(cc_op == CC_OP_ADCX ? CC_C : CC_O), 1); } @@ -1037,47 +1232,87 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) #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); + 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); + 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); +static void gen_ADCX(DisasContext *s, X86DecodedInsn *decode) +{ + gen_ADCOX(s, decode, CC_OP_ADCX); +} + +static void gen_ADD(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); } else { - set_cc_op(s, cc_op); + tcg_gen_add_tl(s->T0, s->T0, s->T1); } + prepare_update2_cc(decode, s, CC_OP_ADDB + ot); } -static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ADOX(DisasContext *s, X86DecodedInsn *decode) { - gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADCX); + gen_ADCOX(s, decode, CC_OP_ADOX); } -static void gen_ADOX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_AND(DisasContext *s, X86DecodedInsn *decode) { - gen_ADCOX(s, env, decode->op[0].ot, CC_OP_ADOX); + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_and_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_and_tl(s->T0, s->T0, s->T1); + } + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -static void gen_ANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ANDN(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; tcg_gen_andc_tl(s->T0, s->T1, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_ARPL(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv zf = tcg_temp_new(); + TCGv flags = tcg_temp_new(); + + gen_mov_eflags(s, flags); + + /* Compute adjusted DST in T1, merging in SRC[RPL]. */ + tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 0, 2); + + /* Z flag set if DST[RPL] < SRC[RPL] */ + tcg_gen_setcond_tl(TCG_COND_LTU, zf, s->T0, s->T1); + tcg_gen_deposit_tl(flags, flags, zf, ctz32(CC_Z), 1); + + /* Place maximum RPL in DST */ + tcg_gen_umax_tl(s->T0, s->T0, s->T1); + + decode->cc_src = flags; + decode->cc_op = CC_OP_EFLAGS; +} + +static void gen_BEXTR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); @@ -1089,9 +1324,6 @@ static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) * Shifts larger than operand size get zeros. */ tcg_gen_ext8u_tl(s->A0, s->T1); - if (TARGET_LONG_BITS == 64 && ot == MO_32) { - tcg_gen_ext32u_tl(s->T0, s->T0); - } 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); @@ -1105,44 +1337,198 @@ static void gen_BEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) 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); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); } -static void gen_BLSI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BLSI(DisasContext *s, 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); + /* input in T1, which is ready for prepare_update2_cc */ + tcg_gen_neg_tl(s->T0, s->T1); 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); + prepare_update2_cc(decode, s, CC_OP_BLSIB + ot); } -static void gen_BLSMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BLSMSK(DisasContext *s, 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); + /* input in T1, which is ready for prepare_update2_cc */ + tcg_gen_subi_tl(s->T0, s->T1, 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); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -static void gen_BLSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BLSR(DisasContext *s, 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); + /* input in T1, which is ready for prepare_update2_cc */ + tcg_gen_subi_tl(s->T0, s->T1, 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); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_BOUND(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i32 op = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(op, s->T0); + if (decode->op[1].ot == MO_16) { + gen_helper_boundw(tcg_env, s->A0, op); + } else { + gen_helper_boundl(tcg_env, s->A0, op); + } +} + +/* Non-standard convention - on entry T0 is zero-extended input, T1 is the output. */ +static void gen_BSF(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* Only the Z bit is defined and it is related to the input. */ + decode->cc_dst = tcg_temp_new(); + decode->cc_op = CC_OP_LOGICB + ot; + tcg_gen_mov_tl(decode->cc_dst, s->T0); + + /* + * The manual says that the output is undefined when the + * input is zero, but real hardware leaves it unchanged, and + * real programs appear to depend on that. Accomplish this + * by passing the output as the value to return upon zero. + */ + tcg_gen_ctz_tl(s->T0, s->T0, s->T1); +} + +/* Non-standard convention - on entry T0 is zero-extended input, T1 is the output. */ +static void gen_BSR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* Only the Z bit is defined and it is related to the input. */ + decode->cc_dst = tcg_temp_new(); + decode->cc_op = CC_OP_LOGICB + ot; + tcg_gen_mov_tl(decode->cc_dst, s->T0); + + /* + * The manual says that the output is undefined when the + * input is zero, but real hardware leaves it unchanged, and + * real programs appear to depend on that. Accomplish this + * by passing the output as the value to return upon zero. + * Plus, return the bit index of the first 1 bit. + */ + tcg_gen_xori_tl(s->T1, s->T1, TARGET_LONG_BITS - 1); + tcg_gen_clz_tl(s->T0, s->T0, s->T1); + tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); +} + +static void gen_BSWAP(DisasContext *s, X86DecodedInsn *decode) +{ +#ifdef TARGET_X86_64 + if (s->dflag == MO_64) { + tcg_gen_bswap64_i64(s->T0, s->T0); + return; + } +#endif + tcg_gen_bswap32_tl(s->T0, s->T0, TCG_BSWAP_OZ); +} + +static TCGv gen_bt_mask(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv mask = tcg_temp_new(); + + tcg_gen_andi_tl(s->T1, s->T1, (8 << ot) - 1); + tcg_gen_shl_tl(mask, tcg_constant_tl(1), s->T1); + return mask; +} + +/* Expects truncated bit index in s->T1, 1 << s->T1 in MASK. */ +static void gen_bt_flags(DisasContext *s, X86DecodedInsn *decode, TCGv src, TCGv mask) +{ + TCGv cf; + + /* + * C is the result of the test, Z is unchanged, and the others + * are all undefined. + */ + if (s->cc_op == CC_OP_DYNAMIC || CC_OP_HAS_EFLAGS(s->cc_op)) { + /* Generate EFLAGS and replace the C bit. */ + cf = tcg_temp_new(); + tcg_gen_setcond_tl(TCG_COND_TSTNE, cf, src, mask); + prepare_update_cf(decode, s, cf); + } else { + /* + * Z was going to be computed from the non-zero status of CC_DST. + * We can get that same Z value (and the new C value) by leaving + * CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the + * same width. + */ + decode->cc_src = tcg_temp_new(); + decode->cc_dst = cpu_cc_dst; + decode->cc_op = CC_OP_SARB + cc_op_size(s->cc_op); + tcg_gen_shr_tl(decode->cc_src, src, s->T1); + } +} + +static void gen_BT(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv mask = gen_bt_mask(s, decode); + + gen_bt_flags(s, decode, s->T0, mask); +} + +static void gen_BTC(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv old = tcg_temp_new(); + TCGv mask = gen_bt_mask(s, decode); + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_xor_tl(old, s->A0, mask, s->mem_index, ot | MO_LE); + } else { + tcg_gen_mov_tl(old, s->T0); + tcg_gen_xor_tl(s->T0, s->T0, mask); + } + + gen_bt_flags(s, decode, old, mask); +} + +static void gen_BTR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv old = tcg_temp_new(); + TCGv mask = gen_bt_mask(s, decode); + + if (s->prefix & PREFIX_LOCK) { + TCGv maskc = tcg_temp_new(); + tcg_gen_not_tl(maskc, mask); + tcg_gen_atomic_fetch_and_tl(old, s->A0, maskc, s->mem_index, ot | MO_LE); + } else { + tcg_gen_mov_tl(old, s->T0); + tcg_gen_andc_tl(s->T0, s->T0, mask); + } + + gen_bt_flags(s, decode, old, mask); +} + +static void gen_BTS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv old = tcg_temp_new(); + TCGv mask = gen_bt_mask(s, decode); + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_or_tl(old, s->A0, mask, s->mem_index, ot | MO_LE); + } else { + tcg_gen_mov_tl(old, s->T0); + tcg_gen_or_tl(s->T0, s->T0, mask); + } + + gen_bt_flags(s, decode, old, mask); +} + +static void gen_BZHI(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; TCGv bound = tcg_constant_tl(ot == MO_64 ? 63 : 31); @@ -1151,21 +1537,353 @@ static void gen_BZHI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) 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, cpu_cc_src, s->T1, bound); - - 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); - - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_BMILGB + ot); + tcg_gen_setcond_tl(TCG_COND_LEU, s->T1, s->T1, bound); + prepare_update2_cc(decode, s, CC_OP_BMILGB + ot); } -static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CALL(DisasContext *s, X86DecodedInsn *decode) +{ + gen_push_v(s, eip_next_tl(s)); + gen_JMP(s, decode); +} + +static void gen_CALL_m(DisasContext *s, X86DecodedInsn *decode) +{ + gen_push_v(s, eip_next_tl(s)); + gen_JMP_m(s, decode); +} + +static void gen_CALLF(DisasContext *s, X86DecodedInsn *decode) +{ + gen_far_call(s); +} + +static void gen_CALLF_m(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_far_call(s); +} + +static void gen_CBW(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp src_ot = decode->op[0].ot - 1; + + tcg_gen_ext_tl(s->T0, s->T0, src_ot | MO_SIGN); +} + +static void gen_CLC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_compute_eflags(s); + tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); +} + +static void gen_CLD(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_st_i32(tcg_constant_i32(1), tcg_env, offsetof(CPUX86State, df)); +} + +static void gen_CLI(DisasContext *s, X86DecodedInsn *decode) +{ + gen_reset_eflags(s, IF_MASK); +} + +static void gen_CLTS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_clts(tcg_env); + /* abort block because static cpu state changed */ + s->base.is_jmp = DISAS_EOB_NEXT; +} + +static void gen_CMC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_compute_eflags(s); + tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); +} + +static void gen_CMOVcc(DisasContext *s, X86DecodedInsn *decode) +{ + gen_cmovcc1(s, decode->b & 0xf, s->T0, s->T1); +} + +static void gen_CMPccXADD(DisasContext *s, 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_TSTEQ, /* 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); + cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(1); + 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_CMPS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_nz(s, ot, gen_cmps); + } else { + gen_cmps(s, ot); + } +} + +static void gen_CMPXCHG(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + TCGv cmpv = tcg_temp_new(); + TCGv oldv = tcg_temp_new(); + TCGv newv = tcg_temp_new(); + TCGv dest; + + tcg_gen_ext_tl(cmpv, cpu_regs[R_EAX], ot); + tcg_gen_ext_tl(newv, s->T1, ot); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_ext_tl(oldv, s->T0, ot); + if (decode->op[0].has_ea) { + /* + * 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); + } else { + /* + * 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. In case rm is part of RAX, note that this + * movcond and the one below are mutually exclusive is executed. + */ + dest = gen_op_deposit_reg_v(s, ot, decode->op[0].n, newv, newv); + tcg_gen_movcond_tl(TCG_COND_EQ, dest, oldv, cmpv, newv, dest); + } + decode->op[0].unit = X86_OP_SKIP; + } + + /* Write RAX only if the cmpxchg fails. */ + dest = gen_op_deposit_reg_v(s, ot, R_EAX, s->T0, oldv); + tcg_gen_movcond_tl(TCG_COND_NE, dest, oldv, cmpv, s->T0, dest); + + tcg_gen_mov_tl(s->cc_srcT, cmpv); + tcg_gen_sub_tl(cmpv, cmpv, oldv); + decode->cc_dst = cmpv; + decode->cc_src = oldv; + decode->cc_op = CC_OP_SUBB + ot; +} + +static void gen_CMPXCHG16B(DisasContext *s, X86DecodedInsn *decode) +{ +#ifdef TARGET_X86_64 + MemOp mop = MO_TE | MO_128 | MO_ALIGN; + TCGv_i64 t0, t1; + TCGv_i128 cmp, val; + + 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); +#else + abort(); +#endif +} + +static void gen_CMPXCHG8B(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 cmp, val, old; + TCGv Z; + + cmp = tcg_temp_new_i64(); + val = tcg_temp_new_i64(); + old = tcg_temp_new_i64(); + + /* 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]); + + /* 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 { + tcg_gen_nonatomic_cmpxchg_i64(old, s->A0, cmp, val, + s->mem_index, MO_TEUQ); + } + + /* 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); + + /* + * 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 { + TCGv zero = tcg_constant_tl(0); + + 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); +} + +static void gen_CPUID(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_cpuid(tcg_env); +} + +static void gen_CRC32(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; @@ -1173,91 +1891,621 @@ static void gen_CRC32(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) 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) +static void gen_CVTPI2Px(DisasContext *s, X86DecodedInsn *decode) { gen_enter_mmx(s); if (s->prefix & PREFIX_DATA) { - gen_helper_cvtpi2pd(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtpi2pd(tcg_env, OP_PTR0, OP_PTR2); } else { - gen_helper_cvtpi2ps(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtpi2ps(tcg_env, OP_PTR0, OP_PTR2); } } -static void gen_CVTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CVTPx2PI(DisasContext *s, X86DecodedInsn *decode) { gen_enter_mmx(s); if (s->prefix & PREFIX_DATA) { - gen_helper_cvtpd2pi(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtpd2pi(tcg_env, OP_PTR0, OP_PTR2); } else { - gen_helper_cvtps2pi(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvtps2pi(tcg_env, OP_PTR0, OP_PTR2); } } -static void gen_CVTTPx2PI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CVTTPx2PI(DisasContext *s, X86DecodedInsn *decode) { gen_enter_mmx(s); if (s->prefix & PREFIX_DATA) { - gen_helper_cvttpd2pi(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvttpd2pi(tcg_env, OP_PTR0, OP_PTR2); } else { - gen_helper_cvttps2pi(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_cvttps2pi(tcg_env, OP_PTR0, OP_PTR2); } } -static void gen_EMMS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_CWD(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_emms(cpu_env); + int shift = 8 << decode->op[0].ot; + + tcg_gen_sextract_tl(s->T0, s->T0, shift - 1, 1); } -static void gen_EXTRQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_DAA(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_daa(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_DAS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_das(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_DEC(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + tcg_gen_movi_tl(s->T1, -1); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s->T0, s->T0, s->T1); + } + prepare_update_cc_incdec(decode, s, CC_OP_DECB + ot); +} + +static void gen_DIV(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + switch(ot) { + case MO_8: + gen_helper_divb_AL(tcg_env, s->T0); + break; + case MO_16: + gen_helper_divw_AX(tcg_env, s->T0); + break; + default: + case MO_32: + gen_helper_divl_EAX(tcg_env, s->T0); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_divq_EAX(tcg_env, s->T0); + break; +#endif + } +} + +static void gen_EMMS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_emms(tcg_env); +} + +static void gen_ENTER(DisasContext *s, X86DecodedInsn *decode) +{ + gen_enter(s, decode->op[1].imm, decode->op[2].imm); +} + +static void gen_EXTRQ_i(DisasContext *s, 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(cpu_env, OP_PTR0, index, length); + gen_helper_extrq_i(tcg_env, OP_PTR0, index, length); } -static void gen_EXTRQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_EXTRQ_r(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_extrq_r(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_extrq_r(tcg_env, OP_PTR0, OP_PTR2); } -static void gen_INSERTQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_FXRSTOR(DisasContext *s, 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(cpu_env, OP_PTR0, OP_PTR1, index, length); + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_NM_exception(s); + } else { + gen_helper_fxrstor(tcg_env, s->A0); + } } -static void gen_INSERTQ_r(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_FXSAVE(DisasContext *s, X86DecodedInsn *decode) { - gen_helper_insertq_r(cpu_env, OP_PTR0, OP_PTR2); + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_NM_exception(s); + } else { + gen_helper_fxsave(tcg_env, s->A0); + } } -static void gen_LDMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_HLT(DisasContext *s, X86DecodedInsn *decode) { - if (s->vex_l) { - gen_illegal_opcode(s); +#ifdef CONFIG_SYSTEM_ONLY + gen_update_cc_op(s); + gen_update_eip_next(s); + gen_helper_hlt(tcg_env); + s->base.is_jmp = DISAS_NORETURN; +#endif +} + +static void gen_IDIV(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + switch(ot) { + case MO_8: + gen_helper_idivb_AL(tcg_env, s->T0); + break; + case MO_16: + gen_helper_idivw_AX(tcg_env, s->T0); + break; + default: + case MO_32: + gen_helper_idivl_EAX(tcg_env, s->T0); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_idivq_EAX(tcg_env, s->T0); + break; +#endif + } +} + +static void gen_IMUL3(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv cc_src_rhs; + + switch (ot) { + case MO_16: + /* s->T0 already sign-extended */ + tcg_gen_ext16s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext16s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_32: +#ifdef TARGET_X86_64 + if (TCG_TARGET_REG_BITS == 64) { + /* + * This produces fewer TCG ops, and better code if flags are needed, + * but it requires a 64-bit multiply even if they are not. Use it + * only if the target has 64-bits registers. + * + * s->T0 is already sign-extended. + */ + tcg_gen_ext32s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext32s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + } else { + /* Variant that only needs a 32-bit widening multiply. */ + TCGv_i32 hi = tcg_temp_new_i32(); + TCGv_i32 lo = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(lo, s->T0); + tcg_gen_trunc_tl_i32(hi, s->T1); + tcg_gen_muls2_i32(lo, hi, lo, hi); + tcg_gen_extu_i32_tl(s->T0, lo); + + cc_src_rhs = tcg_temp_new(); + tcg_gen_extu_i32_tl(cc_src_rhs, hi); + /* Compare the high part to the sign bit of the truncated result */ + tcg_gen_sari_i32(lo, lo, 31); + tcg_gen_extu_i32_tl(s->T1, lo); + } + break; + + case MO_64: +#endif + cc_src_rhs = tcg_temp_new(); + tcg_gen_muls2_tl(s->T0, cc_src_rhs, s->T0, s->T1); + /* Compare the high part to the sign bit of the truncated result */ + tcg_gen_sari_tl(s->T1, s->T0, TARGET_LONG_BITS - 1); + break; + + default: + g_assert_not_reached(); + } + + tcg_gen_sub_tl(s->T1, s->T1, cc_src_rhs); + prepare_update2_cc(decode, s, CC_OP_MULB + ot); +} + +static void gen_IMUL(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv cc_src_rhs; + + switch (ot) { + case MO_8: + /* s->T0 already sign-extended */ + tcg_gen_ext8s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext8s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_16: + /* s->T0 already sign-extended */ + tcg_gen_ext16s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_shri_tl(s->T1, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T1); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext16s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_32: +#ifdef TARGET_X86_64 + /* s->T0 already sign-extended */ + tcg_gen_ext32s_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + tcg_gen_ext32u_tl(cpu_regs[R_EAX], s->T0); + tcg_gen_shri_tl(cpu_regs[R_EDX], s->T0, 32); + /* Compare the full result to the extension of the truncated result. */ + tcg_gen_ext32s_tl(s->T1, s->T0); + cc_src_rhs = s->T0; + break; + + case MO_64: +#endif + tcg_gen_muls2_tl(s->T0, cpu_regs[R_EDX], s->T0, s->T1); + tcg_gen_mov_tl(cpu_regs[R_EAX], s->T0); + + /* Compare the high part to the sign bit of the truncated result */ + tcg_gen_negsetcondi_tl(TCG_COND_LT, s->T1, s->T0, 0); + cc_src_rhs = cpu_regs[R_EDX]; + break; + + default: + g_assert_not_reached(); + } + + tcg_gen_sub_tl(s->T1, s->T1, cc_src_rhs); + prepare_update2_cc(decode, s, CC_OP_MULB + ot); +} + +static void gen_IN(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv_i32 port = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T0); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, SVM_IOIO_TYPE_MASK)) { return; } - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T1); - gen_helper_ldmxcsr(cpu_env, s->tmp2_i32); + translator_io_start(&s->base); + gen_helper_in_func(ot, s->T0, port); + gen_writeback(s, decode, 0, s->T0); + gen_bpt_io(s, port, ot); } -static void gen_MASKMOV(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INC(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_mov_tl(s->A0, cpu_regs[R_EDI]); - gen_extu(s->aflag, s->A0); - gen_add_A0_ds_seg(s); + MemOp ot = decode->op[1].ot; - if (s->prefix & PREFIX_DATA) { - gen_helper_maskmov_xmm(cpu_env, OP_PTR1, OP_PTR2, s->A0); + tcg_gen_movi_tl(s->T1, 1); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); } else { - gen_helper_maskmov_mmx(cpu_env, OP_PTR1, OP_PTR2, s->A0); + tcg_gen_add_tl(s->T0, s->T0, s->T1); + } + prepare_update_cc_incdec(decode, s, CC_OP_INCB + ot); +} + +static void gen_INS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, + SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) { + return; + } + + translator_io_start(&s->base); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz(s, ot, gen_ins); + } else { + gen_ins(s, ot); } } -static void gen_MOVBE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_INSERTQ_i(DisasContext *s, 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, X86DecodedInsn *decode) +{ + gen_helper_insertq_r(tcg_env, OP_PTR0, OP_PTR2); +} + +static void gen_INT(DisasContext *s, X86DecodedInsn *decode) +{ + gen_interrupt(s, decode->immediate); +} + +static void gen_INT1(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_next(s); + gen_helper_icebp(tcg_env); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_INT3(DisasContext *s, X86DecodedInsn *decode) +{ + gen_interrupt(s, EXCP03_INT3); +} + +static void gen_INTO(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_into(tcg_env, cur_insn_len_i32(s)); +} + +static void gen_IRET(DisasContext *s, X86DecodedInsn *decode) +{ + if (!PE(s) || VM86(s)) { + gen_helper_iret_real(tcg_env, tcg_constant_i32(s->dflag - 1)); + } else { + gen_helper_iret_protected(tcg_env, tcg_constant_i32(s->dflag - 1), + eip_next_i32(s)); + } + assume_cc_op(s, CC_OP_EFLAGS); + s->base.is_jmp = DISAS_EOB_ONLY; +} + +static void gen_Jcc(DisasContext *s, X86DecodedInsn *decode) +{ + gen_bnd_jmp(s); + gen_jcc(s, decode->b & 0xf, decode->immediate); +} + +static void gen_JCXZ(DisasContext *s, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_jz_ecx(s, taken); + gen_conditional_jump_labels(s, decode->immediate, NULL, taken); +} + +static void gen_JMP(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_jmp_rel(s, s->dflag, decode->immediate, 0); +} + +static void gen_JMP_m(DisasContext *s, X86DecodedInsn *decode) +{ + gen_op_jmp_v(s, s->T0); + gen_bnd_jmp(s); + s->base.is_jmp = DISAS_JUMP; +} + +static void gen_JMPF(DisasContext *s, X86DecodedInsn *decode) +{ + gen_far_jmp(s); +} + +static void gen_JMPF_m(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_far_jmp(s); +} + +static void gen_LAHF(DisasContext *s, X86DecodedInsn *decode) +{ + if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { + return gen_illegal_opcode(s); + } + gen_compute_eflags(s); + /* Note: gen_compute_eflags() only gives the condition codes */ + tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); + tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); +} + +static void gen_LAR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv result = tcg_temp_new(); + TCGv dest; + + gen_compute_eflags(s); + gen_update_cc_op(s); + gen_helper_lar(result, tcg_env, s->T0); + + /* Perform writeback here to skip it if ZF=0. */ + decode->op[0].unit = X86_OP_SKIP; + dest = gen_op_deposit_reg_v(s, ot, decode->op[0].n, result, result); + tcg_gen_movcond_tl(TCG_COND_TSTNE, dest, cpu_cc_src, tcg_constant_tl(CC_Z), + result, dest); +} + +static void gen_LDMXCSR(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); +} + +static void gen_lxx_seg(DisasContext *s, X86DecodedInsn *decode, int seg) +{ + MemOp ot = decode->op[0].ot; + + /* Offset already in s->T0. */ + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + + /* load the segment here to handle exceptions properly */ + gen_movl_seg(s, seg, s->T1); +} + +static void gen_LDS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, decode, R_DS); +} + +static void gen_LEA(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv ea = gen_lea_modrm_1(s, decode->mem, false); + gen_lea_v_seg_dest(s, s->aflag, s->T0, ea, -1, -1); +} + +static void gen_LEAVE(DisasContext *s, X86DecodedInsn *decode) +{ + gen_leave(s); +} + +static void gen_LES(DisasContext *s, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, decode, R_ES); +} + +static void gen_LFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_LD_LD | TCG_BAR_SC); +} + +static void gen_LFS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, decode, R_FS); +} + +static void gen_LGS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, decode, R_GS); +} + +static void gen_LODS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz(s, ot, gen_lods); + } else { + gen_lods(s, ot); + } +} + +static void gen_LOOP(DisasContext *s, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jnz_ecx(s, taken); + gen_conditional_jump_labels(s, decode->immediate, NULL, taken); +} + +static void gen_LOOPE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + TCGLabel *not_taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jz_ecx(s, not_taken); + gen_jcc1(s, (JCC_Z << 1), taken); /* jz taken */ + gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); +} + +static void gen_LOOPNE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGLabel *taken = gen_new_label(); + TCGLabel *not_taken = gen_new_label(); + + gen_update_cc_op(s); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jz_ecx(s, not_taken); + gen_jcc1(s, (JCC_Z << 1) | 1, taken); /* jnz taken */ + gen_conditional_jump_labels(s, decode->immediate, not_taken, taken); +} + +static void gen_LSL(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv result = tcg_temp_new(); + TCGv dest; + + gen_compute_eflags(s); + gen_update_cc_op(s); + gen_helper_lsl(result, tcg_env, s->T0); + + /* Perform writeback here to skip it if ZF=0. */ + decode->op[0].unit = X86_OP_SKIP; + dest = gen_op_deposit_reg_v(s, ot, decode->op[0].n, result, result); + tcg_gen_movcond_tl(TCG_COND_TSTNE, dest, cpu_cc_src, tcg_constant_tl(CC_Z), + result, dest); +} + +static void gen_LSS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_lxx_seg(s, decode, R_SS); +} + +static void gen_LZCNT(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* C bit (cc_src) is defined related to the input. */ + decode->cc_src = tcg_temp_new(); + decode->cc_dst = s->T0; + decode->cc_op = CC_OP_BMILGB + ot; + tcg_gen_mov_tl(decode->cc_src, s->T0); + + /* + * Reduce the target_ulong result by the number of zeros that + * we expect to find at the top. + */ + tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); + tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - (8 << ot)); +} + +static void gen_MFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); +} + +static void gen_MOV(DisasContext *s, X86DecodedInsn *decode) +{ + /* nothing to do! */ +} +#define gen_NOP gen_MOV + +static void gen_MASKMOV(DisasContext *s, X86DecodedInsn *decode) +{ + gen_lea_v_seg(s, 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, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; @@ -1269,25 +2517,25 @@ static void gen_MOVBE(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) } } -static void gen_MOVD_from(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVD_from(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; switch (ot) { case MO_32: #ifdef TARGET_X86_64 - tcg_gen_ld32u_tl(s->T0, cpu_env, decode->op[2].offset); + tcg_gen_ld32u_tl(s->T0, tcg_env, decode->op[2].offset); break; case MO_64: #endif - tcg_gen_ld_tl(s->T0, cpu_env, decode->op[2].offset); + 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) +static void gen_MOVD_to(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; int vec_len = vector_len(s, decode); @@ -1298,38 +2546,38 @@ static void gen_MOVD_to(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod switch (ot) { case MO_32: #ifdef TARGET_X86_64 - tcg_gen_st32_tl(s->T1, cpu_env, lo_ofs); + tcg_gen_st32_tl(s->T1, tcg_env, lo_ofs); break; case MO_64: #endif - tcg_gen_st_tl(s->T1, cpu_env, lo_ofs); + 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) +static void gen_MOVDQ(DisasContext *s, X86DecodedInsn *decode) { gen_store_sse(s, decode, decode->op[2].offset); } -static void gen_MOVMSK(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVMSK(DisasContext *s, 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, cpu_env, OP_PTR2); + 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) +static void gen_MOVQ(DisasContext *s, 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, cpu_env, decode->op[2].offset); + 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 { @@ -1342,24 +2590,86 @@ static void gen_MOVQ(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) * 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, cpu_env, lo_ofs); + tcg_gen_st_i64(s->tmp1_i64, tcg_env, lo_ofs); } } -static void gen_MOVq_dq(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVq_dq(DisasContext *s, X86DecodedInsn *decode) { gen_enter_mmx(s); /* Otherwise the same as any other movq. */ - return gen_MOVQ(s, env, decode); + return gen_MOVQ(s, decode); } -static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_MOVS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz(s, ot, gen_movs); + } else { + gen_movs(s, ot); + } +} + +static void gen_MUL(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + switch (ot) { + case MO_8: + /* s->T0 already zero-extended */ + tcg_gen_ext8u_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_andi_tl(s->T1, s->T0, 0xff00); + decode->cc_dst = s->T0; + decode->cc_src = s->T1; + break; + + case MO_16: + /* s->T0 already zero-extended */ + tcg_gen_ext16u_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_shri_tl(s->T1, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T1); + decode->cc_dst = s->T0; + decode->cc_src = s->T1; + break; + + case MO_32: +#ifdef TARGET_X86_64 + /* s->T0 already zero-extended */ + tcg_gen_ext32u_tl(s->T1, s->T1); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + tcg_gen_ext32u_tl(cpu_regs[R_EAX], s->T0); + tcg_gen_shri_tl(cpu_regs[R_EDX], s->T0, 32); + decode->cc_dst = cpu_regs[R_EAX]; + decode->cc_src = cpu_regs[R_EDX]; + break; + + case MO_64: +#endif + tcg_gen_mulu2_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->T0, s->T1); + decode->cc_dst = cpu_regs[R_EAX]; + decode->cc_src = cpu_regs[R_EDX]; + break; + + default: + g_assert_not_reached(); + } + + decode->cc_op = CC_OP_MULB + ot; +} + +static void gen_MULX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; /* low part of result in VEX.vvvv, high in MODRM */ switch (ot) { - default: + 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, @@ -1367,28 +2677,119 @@ static void gen_MULX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); tcg_gen_extu_i32_tl(s->T0, s->tmp3_i32); break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(cpu_regs[s->vex_v], s->T0, s->T0, s->T1); - break; -#endif - } + 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) +static void gen_NEG(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv oldv = tcg_temp_new(); + + if (s->prefix & PREFIX_LOCK) { + TCGv newv = tcg_temp_new(); + TCGv cmpv = tcg_temp_new(); + TCGLabel *label1 = gen_new_label(); + + gen_set_label(label1); + gen_op_ld_v(s, ot, oldv, s->A0); + tcg_gen_neg_tl(newv, oldv); + tcg_gen_atomic_cmpxchg_tl(cmpv, s->A0, oldv, newv, + s->mem_index, ot | MO_LE); + tcg_gen_brcond_tl(TCG_COND_NE, oldv, cmpv, label1); + } else { + tcg_gen_mov_tl(oldv, s->T0); + } + tcg_gen_neg_tl(s->T0, oldv); + + decode->cc_dst = s->T0; + decode->cc_src = oldv; + tcg_gen_movi_tl(s->cc_srcT, 0); + decode->cc_op = CC_OP_SUBB + ot; +} + +static void gen_NOT(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_movi_tl(s->T0, ~0); + tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_not_tl(s->T0, s->T0); + } +} + +static void gen_OR(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_or_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_or_tl(s->T0, s->T0, s->T1); + } + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); +} + +static void gen_OUT(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); + TCGv_i32 value = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, 0)) { + return; + } + tcg_gen_trunc_tl_i32(value, s->T0); + translator_io_start(&s->base); + gen_helper_out_func(ot, port, value); + gen_bpt_io(s, port, ot); +} + +static void gen_OUTS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + TCGv_i32 port = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(port, s->T1); + tcg_gen_ext16u_i32(port, port); + if (!gen_check_io(s, ot, port, SVM_IOIO_STR_MASK)) { + return; + } + + translator_io_start(&s->base); + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz(s, ot, gen_outs); + } else { + gen_outs(s, ot); + } +} + +static void gen_PALIGNR(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); if (!(s->prefix & PREFIX_DATA)) { - gen_helper_palignr_mmx(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + gen_helper_palignr_mmx(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } else if (!s->vex_l) { - gen_helper_palignr_xmm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + gen_helper_palignr_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } else { - gen_helper_palignr_ymm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + gen_helper_palignr_ymm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } } -static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PANDN(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1398,61 +2799,61 @@ static void gen_PANDN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) decode->op[1].offset, vec_len, vec_len); } -static void gen_PCMPESTRI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PAUSE(DisasContext *s, X86DecodedInsn *decode) { - TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); - gen_helper_pcmpestri_xmm(cpu_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + gen_update_cc_op(s); + gen_update_eip_next(s); + gen_helper_pause(tcg_env); + s->base.is_jmp = DISAS_NORETURN; } -static void gen_PCMPESTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PCMPESTRI(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); - gen_helper_pcmpestrm_xmm(cpu_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + gen_helper_pcmpestri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + assume_cc_op(s, CC_OP_EFLAGS); +} + +static void gen_PCMPESTRM(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); + gen_helper_pcmpestrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + assume_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) +static void gen_PCMPISTRI(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); - gen_helper_pcmpistri_xmm(cpu_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + gen_helper_pcmpistri_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_PCMPISTRM(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PCMPISTRM(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); - gen_helper_pcmpistrm_xmm(cpu_env, OP_PTR1, OP_PTR2, imm); - set_cc_op(s, CC_OP_EFLAGS); + gen_helper_pcmpistrm_xmm(tcg_env, OP_PTR1, OP_PTR2, imm); + assume_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) +static void gen_PDEP(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[1].ot; - if (ot < MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); - } gen_helper_pdep(s->T0, s->T0, s->T1); } -static void gen_PEXT(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXT(DisasContext *s, X86DecodedInsn *decode) { - MemOp ot = decode->op[1].ot; - if (ot < MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); - } gen_helper_pext(s->T0, s->T0, s->T1); } -static inline void gen_pextr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +static inline void gen_pextr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) { int vec_len = vector_len(s, decode); int mask = (vec_len >> ot) - 1; @@ -1460,41 +2861,41 @@ static inline void gen_pextr(DisasContext *s, CPUX86State *env, X86DecodedInsn * switch (ot) { case MO_8: - tcg_gen_ld8u_tl(s->T0, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + 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, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + 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, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + 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, cpu_env, vector_elem_offset(&decode->op[1], ot, val)); + 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) +static void gen_PEXTRB(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_8); + gen_pextr(s, decode, MO_8); } -static void gen_PEXTRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXTRW(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_16); + gen_pextr(s, decode, MO_16); } -static void gen_PEXTR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PEXTR(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; - gen_pextr(s, env, decode, ot); + gen_pextr(s, decode, ot); } -static inline void gen_pinsr(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, MemOp ot) +static inline void gen_pinsr(DisasContext *s, X86DecodedInsn *decode, MemOp ot) { int vec_len = vector_len(s, decode); int mask = (vec_len >> ot) - 1; @@ -1507,37 +2908,37 @@ static inline void gen_pinsr(DisasContext *s, CPUX86State *env, X86DecodedInsn * switch (ot) { case MO_8: - tcg_gen_st8_tl(s->T1, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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, cpu_env, vector_elem_offset(&decode->op[0], ot, val)); + 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) +static void gen_PINSRB(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, MO_8); + gen_pinsr(s, decode, MO_8); } -static void gen_PINSRW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PINSRW(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, MO_16); + gen_pinsr(s, decode, MO_16); } -static void gen_PINSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PINSR(DisasContext *s, X86DecodedInsn *decode) { - gen_pinsr(s, env, decode, decode->op[2].ot); + gen_pinsr(s, decode, decode->op[2].ot); } static void gen_pmovmskb_i64(TCGv_i64 d, TCGv_i64 s) @@ -1577,13 +2978,7 @@ static void gen_pmovmskb_vec(unsigned vece, TCGv_vec d, TCGv_vec s) 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 void gen_PMOVMSKB(DisasContext *s, X86DecodedInsn *decode) { static const TCGOpcode vecop_list[] = { INDEX_op_shli_vec, 0 }; static const GVecGen2 g = { @@ -1599,7 +2994,7 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco 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, cpu_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); + 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) { @@ -1609,9 +3004,9 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco * loading the whole word, the shift left is avoided. */ #ifdef TARGET_X86_64 - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUX86State, xmm_t0.ZMM_Q((vec_len - 1) / 8))); + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_Q((vec_len - 1) / 8))); #else - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUX86State, xmm_t0.ZMM_L((vec_len - 1) / 4))); + 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); @@ -1621,20 +3016,70 @@ static void gen_PMOVMSKB(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco * those bits are known to be zero after ld8u, this becomes a shift+or * if deposit is not available. */ - tcg_gen_ld8u_tl(t, cpu_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); + 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); } } - tcg_temp_free(t); } -static void gen_PSHUFW(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_POP(DisasContext *s, X86DecodedInsn *decode) +{ + X86DecodedOp *op = &decode->op[0]; + MemOp ot = gen_pop_T0(s); + + assert(ot >= op->ot); + if (op->has_ea || op->unit == X86_OP_SEG) { + /* NOTE: order is important for MMU exceptions */ + gen_writeback(s, decode, 0, s->T0); + } + + /* NOTE: writing back registers after update is important for pop %sp */ + gen_pop_update(s, ot); +} + +static void gen_POPA(DisasContext *s, X86DecodedInsn *decode) +{ + gen_popa(s); +} + +static void gen_POPCNT(DisasContext *s, X86DecodedInsn *decode) +{ + decode->cc_dst = tcg_temp_new(); + decode->cc_op = CC_OP_POPCNT; + + tcg_gen_mov_tl(decode->cc_dst, s->T0); + tcg_gen_ctpop_tl(s->T0, s->T0); +} + +static void gen_POPF(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot; + int mask = TF_MASK | AC_MASK | ID_MASK | NT_MASK; + + if (CPL(s) == 0) { + mask |= IF_MASK | IOPL_MASK; + } else if (CPL(s) <= IOPL(s)) { + mask |= IF_MASK; + } + if (s->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 */ + s->base.is_jmp = DISAS_EOB_NEXT; +} + +static void gen_PSHUFW(DisasContext *s, 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) +static void gen_PSRLW_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1647,7 +3092,7 @@ static void gen_PSRLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSLLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLW_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1660,7 +3105,7 @@ static void gen_PSLLW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSRAW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRAW_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1672,7 +3117,7 @@ static void gen_PSRAW_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod decode->immediate, vec_len, vec_len); } -static void gen_PSRLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRLD_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1685,7 +3130,7 @@ static void gen_PSRLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSLLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLD_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1698,7 +3143,7 @@ static void gen_PSLLD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSRAD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRAD_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1710,7 +3155,7 @@ static void gen_PSRAD_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod decode->immediate, vec_len, vec_len); } -static void gen_PSRLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSRLQ_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1723,7 +3168,7 @@ static void gen_PSRLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decod } } -static void gen_PSLLQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLQ_i(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -1745,65 +3190,786 @@ static TCGv_ptr make_imm8u_xmm_vec(uint8_t imm, int vec_len) tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUX86State, xmm_t0) + xmm_offset(ot), vec_len, vec_len, 0); - tcg_gen_addi_ptr(ptr, cpu_env, offsetof(CPUX86State, xmm_t0)); - tcg_gen_st_i32(imm_v, cpu_env, offsetof(CPUX86State, xmm_t0.ZMM_L(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) +static void gen_PSRLDQ_i(DisasContext *s, 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(cpu_env, OP_PTR0, OP_PTR1, imm_vec); + gen_helper_psrldq_ymm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); } else { - gen_helper_psrldq_xmm(cpu_env, OP_PTR0, OP_PTR1, imm_vec); + gen_helper_psrldq_xmm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); } - tcg_temp_free_ptr(imm_vec); } -static void gen_PSLLDQ_i(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PSLLDQ_i(DisasContext *s, 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(cpu_env, OP_PTR0, OP_PTR1, imm_vec); + gen_helper_pslldq_ymm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); } else { - gen_helper_pslldq_xmm(cpu_env, OP_PTR0, OP_PTR1, imm_vec); + gen_helper_pslldq_xmm(tcg_env, OP_PTR0, OP_PTR1, imm_vec); } - tcg_temp_free_ptr(imm_vec); } -static void gen_RORX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_PUSH(DisasContext *s, X86DecodedInsn *decode) +{ + gen_push_v(s, s->T0); +} + +static void gen_PUSHA(DisasContext *s, X86DecodedInsn *decode) +{ + gen_pusha(s); +} + +static void gen_PUSHF(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_helper_read_eflags(s->T0, tcg_env); + gen_push_v(s, s->T0); +} + +static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, + bool *can_be_zero, TCGv *count, int unit) { MemOp ot = decode->op[0].ot; - int b = decode->immediate; + int mask = (ot <= MO_32 ? 0x1f : 0x3f); - 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); + *can_be_zero = false; + switch (unit) { + case X86_OP_INT: + *count = tcg_temp_new(); + tcg_gen_andi_tl(*count, cpu_regs[R_ECX], mask); + *can_be_zero = true; + break; + + case X86_OP_IMM: + if ((decode->immediate & mask) == 0) { + *count = NULL; + break; + } + *count = tcg_temp_new(); + tcg_gen_movi_tl(*count, decode->immediate & mask); + break; + + case X86_OP_SKIP: + *count = tcg_temp_new(); + tcg_gen_movi_tl(*count, 1); + break; + + default: + g_assert_not_reached(); + } + + return ot; +} + +/* + * Compute existing flags in decode->cc_src, for gen_* functions that wants + * to set the cc_op set to CC_OP_ADCOX. In particular, this allows rotate + * operations to compute the carry in decode->cc_dst and the overflow in + * decode->cc_src2. + * + * If need_flags is true, decode->cc_dst and decode->cc_src2 are preloaded + * with the value of CF and OF before the instruction, so that it is possible + * to keep the flags unmodified. + * + * Return true if carry could be made available cheaply as a 1-bit value in + * decode->cc_dst (trying a bit harder if want_carry is true). If false is + * returned, decode->cc_dst is uninitialized and the carry is only available + * as bit 0 of decode->cc_src. + */ +static bool gen_eflags_adcox(DisasContext *s, X86DecodedInsn *decode, bool want_carry, bool need_flags) +{ + bool got_cf = false; + bool got_of = false; + + decode->cc_dst = tcg_temp_new(); + decode->cc_src = tcg_temp_new(); + decode->cc_src2 = tcg_temp_new(); + decode->cc_op = CC_OP_ADCOX; + + /* A lot more cc_ops could be "optimized" to avoid the extracts at + * the end (INC/DEC, BMILG, MUL), but they are all really unlikely + * to be followed by rotations within the same basic block. + */ + switch (s->cc_op) { + case CC_OP_ADCOX: + /* No need to compute the full EFLAGS, CF/OF are already isolated. */ + tcg_gen_mov_tl(decode->cc_src, cpu_cc_src); + if (need_flags) { + tcg_gen_mov_tl(decode->cc_src2, cpu_cc_src2); + got_of = true; + } + if (want_carry || need_flags) { + tcg_gen_mov_tl(decode->cc_dst, cpu_cc_dst); + got_cf = true; + } + break; + + case CC_OP_LOGICB ... CC_OP_LOGICQ: + /* CF and OF are zero, do it just because it's easy. */ + gen_mov_eflags(s, decode->cc_src); + if (need_flags) { + tcg_gen_movi_tl(decode->cc_src2, 0); + got_of = true; + } + if (want_carry || need_flags) { + tcg_gen_movi_tl(decode->cc_dst, 0); + got_cf = true; + } + break; + + case CC_OP_SARB ... CC_OP_SARQ: + /* + * SHR/RCR/SHR/RCR/... is a relatively common occurrence of RCR. + * By computing CF without using eflags, the calls to cc_compute_all + * can be eliminated as dead code (except for the last RCR). + */ + if (want_carry || need_flags) { + tcg_gen_andi_tl(decode->cc_dst, cpu_cc_src, 1); + got_cf = true; + } + gen_mov_eflags(s, decode->cc_src); + break; + + case CC_OP_SHLB ... CC_OP_SHLQ: + /* + * Likewise for SHL/RCL/SHL/RCL/... but, if CF is not in the sign + * bit, we might as well fish CF out of EFLAGS and save a shift. + */ + if (want_carry && (!need_flags || s->cc_op == CC_OP_SHLB + MO_TL)) { + MemOp size = cc_op_size(s->cc_op); + tcg_gen_shri_tl(decode->cc_dst, cpu_cc_src, (8 << size) - 1); + got_cf = true; + } + gen_mov_eflags(s, decode->cc_src); + break; + + default: + gen_mov_eflags(s, decode->cc_src); + break; + } + + if (need_flags) { + /* If the flags could be left unmodified, always load them. */ + if (!got_of) { + tcg_gen_extract_tl(decode->cc_src2, decode->cc_src, ctz32(CC_O), 1); + got_of = true; + } + if (!got_cf) { + tcg_gen_extract_tl(decode->cc_dst, decode->cc_src, ctz32(CC_C), 1); + got_cf = true; + } + } + return got_cf; +} + +static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, + bool can_be_zero, TCGv count) +{ + MemOp ot = decode->op[0].ot; + TCGv temp = can_be_zero ? tcg_temp_new() : decode->cc_src2; + + tcg_gen_xor_tl(temp, old, result); + tcg_gen_extract_tl(temp, temp, (8 << ot) - 1, 1); + if (can_be_zero) { + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src2, count, tcg_constant_tl(0), + decode->cc_src2, temp); } } -static void gen_SARX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +/* + * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands, + * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further. + */ +static void gen_rotc_mod(MemOp ot, TCGv count) +{ + TCGv temp; + + switch (ot) { + case MO_8: + temp = tcg_temp_new(); + tcg_gen_subi_tl(temp, count, 18); + tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); + tcg_gen_subi_tl(temp, count, 9); + tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); + break; + + case MO_16: + temp = tcg_temp_new(); + tcg_gen_subi_tl(temp, count, 17); + tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); + break; + + default: + break; + } +} + +/* + * The idea here is that the bit to the right of the new bit 0 is the + * new carry, and the bit to the right of the old bit 0 is the old carry. + * Just like a regular rotation, the result of the rotation is composed + * from a right shifted part and a left shifted part of s->T0. The new carry + * is extracted from the right-shifted portion, and the old carry is + * inserted at the end of the left-shifted portion. + * + * Because of the separate shifts involving the carry, gen_RCL and gen_RCR + * mostly operate on count-1. This also comes in handy when computing + * length - count, because (length-1) - (count-1) can be computed with + * a XOR, and that is commutative unlike subtraction. + */ +static void gen_RCL(DisasContext *s, X86DecodedInsn *decode) +{ + bool have_1bit_cin, can_be_zero; + TCGv count; + TCGLabel *zero_label = NULL; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + TCGv low, high, low_count; + + if (!count) { + return; + } + + low = tcg_temp_new(); + high = tcg_temp_new(); + low_count = tcg_temp_new(); + + gen_rotc_mod(ot, count); + have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero); + if (can_be_zero) { + zero_label = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, count, 0, zero_label); + } + + /* Compute high part, including incoming carry. */ + if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) { + /* high = (T0 << 1) | cin */ + TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src; + tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1); + } else { + /* Same as above but without deposit; cin in cc_dst. */ + tcg_gen_add_tl(high, s->T0, decode->cc_dst); + tcg_gen_add_tl(high, high, s->T0); + } + tcg_gen_subi_tl(count, count, 1); + tcg_gen_shl_tl(high, high, count); + + /* Compute low part and outgoing carry, incoming s->T0 is zero extended */ + tcg_gen_xori_tl(low_count, count, (8 << ot) - 1); /* LENGTH - 1 - (count - 1) */ + tcg_gen_shr_tl(low, s->T0, low_count); + tcg_gen_andi_tl(decode->cc_dst, low, 1); + tcg_gen_shri_tl(low, low, 1); + + /* Compute result and outgoing overflow */ + tcg_gen_mov_tl(decode->cc_src2, s->T0); + tcg_gen_or_tl(s->T0, low, high); + gen_rot_overflow(decode, s->T0, decode->cc_src2, false, NULL); + + if (zero_label) { + gen_set_label(zero_label); + } +} + +static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) +{ + bool have_1bit_cin, can_be_zero; + TCGv count; + TCGLabel *zero_label = NULL; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + TCGv low, high, high_count; + + if (!count) { + return; + } + + low = tcg_temp_new(); + high = tcg_temp_new(); + high_count = tcg_temp_new(); + + gen_rotc_mod(ot, count); + have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero); + if (can_be_zero) { + zero_label = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, count, 0, zero_label); + } + + /* Save incoming carry into high, it will be shifted later. */ + if (!have_1bit_cin || TCG_TARGET_deposit_tl_valid(1, TARGET_LONG_BITS - 1)) { + TCGv cin = have_1bit_cin ? decode->cc_dst : decode->cc_src; + tcg_gen_deposit_tl(high, cin, s->T0, 1, TARGET_LONG_BITS - 1); + } else { + /* Same as above but without deposit; cin in cc_dst. */ + tcg_gen_add_tl(high, s->T0, decode->cc_dst); + tcg_gen_add_tl(high, high, s->T0); + } + + /* Compute low part and outgoing carry, incoming s->T0 is zero extended */ + tcg_gen_subi_tl(count, count, 1); + tcg_gen_shr_tl(low, s->T0, count); + tcg_gen_andi_tl(decode->cc_dst, low, 1); + tcg_gen_shri_tl(low, low, 1); + + /* Move high part to the right position */ + tcg_gen_xori_tl(high_count, count, (8 << ot) - 1); /* LENGTH - 1 - (count - 1) */ + tcg_gen_shl_tl(high, high, high_count); + + /* Compute result and outgoing overflow */ + tcg_gen_mov_tl(decode->cc_src2, s->T0); + tcg_gen_or_tl(s->T0, low, high); + gen_rot_overflow(decode, s->T0, decode->cc_src2, false, NULL); + + if (zero_label) { + gen_set_label(zero_label); + } +} + +#ifdef CONFIG_USER_ONLY +static void gen_unreachable(DisasContext *s, X86DecodedInsn *decode) +{ + g_assert_not_reached(); +} +#endif + +#ifndef CONFIG_USER_ONLY +static void gen_RDMSR(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_rdmsr(tcg_env); +} +#else +#define gen_RDMSR gen_unreachable +#endif + +static void gen_RDPMC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + translator_io_start(&s->base); + gen_helper_rdpmc(tcg_env); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_RDTSC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + translator_io_start(&s->base); + gen_helper_rdtsc(tcg_env); +} + +static void gen_RDxxBASE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv base = cpu_seg_base[s->modrm & 8 ? R_GS : R_FS]; + + /* Preserve hflags bits by testing CR4 at runtime. */ + gen_helper_cr4_testbit(tcg_env, tcg_constant_i32(CR4_FSGSBASE_MASK)); + tcg_gen_mov_tl(s->T0, base); +} + +static void gen_RET(DisasContext *s, X86DecodedInsn *decode) +{ + int16_t adjust = decode->e.op1 == X86_TYPE_I ? decode->immediate : 0; + + MemOp ot = gen_pop_T0(s); + gen_stack_update(s, adjust + (1 << ot)); + gen_op_jmp_v(s, s->T0); + gen_bnd_jmp(s); + s->base.is_jmp = DISAS_JUMP; +} + +static void gen_RETF(DisasContext *s, X86DecodedInsn *decode) +{ + int16_t adjust = decode->e.op1 == X86_TYPE_I ? decode->immediate : 0; + + if (!PE(s) || VM86(s)) { + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], 0); + /* pop offset */ + gen_op_ld_v(s, s->dflag, s->T0, s->A0); + /* NOTE: keeping EIP updated is not a problem in case of + exception */ + gen_op_jmp_v(s, s->T0); + /* pop selector */ + gen_add_A0_im(s, 1 << s->dflag); + gen_op_ld_v(s, s->dflag, s->T0, s->A0); + gen_op_movl_seg_real(s, R_CS, s->T0); + /* add stack offset */ + gen_stack_update(s, adjust + (2 << s->dflag)); + } else { + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_lret_protected(tcg_env, tcg_constant_i32(s->dflag - 1), + tcg_constant_i32(adjust)); + } + s->base.is_jmp = DISAS_EOB_ONLY; +} + +/* + * Return non-NULL if a 32-bit rotate works, after possibly replicating the input. + * The input has already been zero-extended upon operand decode. + */ +static TCGv_i32 gen_rot_replicate(MemOp ot, TCGv in) +{ + TCGv_i32 temp; + switch (ot) { + case MO_8: + temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, in); + tcg_gen_muli_i32(temp, temp, 0x01010101); + return temp; + + case MO_16: + temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, in); + tcg_gen_deposit_i32(temp, temp, temp, 16, 16); + return temp; + +#ifdef TARGET_X86_64 + case MO_32: + temp = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(temp, in); + return temp; +#endif + + default: + return NULL; + } +} + +static void gen_rot_carry(X86DecodedInsn *decode, TCGv result, + bool can_be_zero, TCGv count, int bit) +{ + if (!can_be_zero) { + tcg_gen_extract_tl(decode->cc_dst, result, bit, 1); + } else { + TCGv temp = tcg_temp_new(); + tcg_gen_extract_tl(temp, result, bit, 1); + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0), + decode->cc_dst, temp); + } +} + +static void gen_ROL(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + TCGv_i32 temp32, count32; + TCGv old = tcg_temp_new(); + + if (!count) { + return; + } + + gen_eflags_adcox(s, decode, false, can_be_zero); + tcg_gen_mov_tl(old, s->T0); + temp32 = gen_rot_replicate(ot, s->T0); + if (temp32) { + count32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(count32, count); + tcg_gen_rotl_i32(temp32, temp32, count32); + /* Zero extend to facilitate later optimization. */ + tcg_gen_extu_i32_tl(s->T0, temp32); + } else { + tcg_gen_rotl_tl(s->T0, s->T0, count); + } + gen_rot_carry(decode, s->T0, can_be_zero, count, 0); + gen_rot_overflow(decode, s->T0, old, can_be_zero, count); +} + +static void gen_ROR(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + TCGv_i32 temp32, count32; + TCGv old = tcg_temp_new(); + + if (!count) { + return; + } + + gen_eflags_adcox(s, decode, false, can_be_zero); + tcg_gen_mov_tl(old, s->T0); + temp32 = gen_rot_replicate(ot, s->T0); + if (temp32) { + count32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(count32, count); + tcg_gen_rotr_i32(temp32, temp32, count32); + /* Zero extend to facilitate later optimization. */ + tcg_gen_extu_i32_tl(s->T0, temp32); + gen_rot_carry(decode, s->T0, can_be_zero, count, 31); + } else { + tcg_gen_rotr_tl(s->T0, s->T0, count); + gen_rot_carry(decode, s->T0, can_be_zero, count, TARGET_LONG_BITS - 1); + } + gen_rot_overflow(decode, s->T0, old, can_be_zero, count); +} + +static void gen_RORX(DisasContext *s, 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(); + } +} + +#ifndef CONFIG_USER_ONLY +static void gen_RSM(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_rsm(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); + s->base.is_jmp = DISAS_EOB_ONLY; +} +#else +#define gen_RSM gen_UD +#endif + +static void gen_SAHF(DisasContext *s, X86DecodedInsn *decode) +{ + if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) { + return gen_illegal_opcode(s); + } + 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); + tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); +} + +static void gen_SALC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_compute_eflags_c(s, s->T0); + tcg_gen_neg_tl(s->T0, s->T0); +} + +static void gen_shift_dynamic_flags(DisasContext *s, X86DecodedInsn *decode, TCGv count, CCOp cc_op) +{ + TCGv_i32 count32 = tcg_temp_new_i32(); + TCGv_i32 old_cc_op; + + decode->cc_op = CC_OP_DYNAMIC; + decode->cc_op_dynamic = tcg_temp_new_i32(); + + assert(decode->cc_dst == s->T0); + if (cc_op_live(s->cc_op) & USES_CC_DST) { + decode->cc_dst = tcg_temp_new(); + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_dst, count, tcg_constant_tl(0), + cpu_cc_dst, s->T0); + } + + if (cc_op_live(s->cc_op) & USES_CC_SRC) { + tcg_gen_movcond_tl(TCG_COND_EQ, decode->cc_src, count, tcg_constant_tl(0), + cpu_cc_src, decode->cc_src); + } + + tcg_gen_trunc_tl_i32(count32, count); + if (s->cc_op == CC_OP_DYNAMIC) { + old_cc_op = cpu_cc_op; + } else { + old_cc_op = tcg_constant_i32(s->cc_op); + } + tcg_gen_movcond_i32(TCG_COND_EQ, decode->cc_op_dynamic, count32, tcg_constant_i32(0), + old_cc_op, tcg_constant_i32(cc_op)); +} + +static void gen_SAR(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = tcg_temp_new(); + tcg_gen_subi_tl(decode->cc_src, count, 1); + tcg_gen_sar_tl(decode->cc_src, s->T0, decode->cc_src); + tcg_gen_sar_tl(s->T0, s->T0, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); + } else { + decode->cc_op = CC_OP_SARB + ot; + } +} + +static void gen_SARX(DisasContext *s, 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); - if (ot != MO_64) { - tcg_gen_ext32s_tl(s->T0, s->T0); - } tcg_gen_sar_tl(s->T0, s->T0, s->T1); } -static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SBB(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + TCGv c_in = tcg_temp_new(); + + gen_compute_eflags_c(s, c_in); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(s->T0, s->T1, c_in); + tcg_gen_neg_tl(s->T0, s->T0); + tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + /* + * TODO: SBB reg, reg could use gen_prepare_eflags_c followed by + * negsetcond, and CC_OP_SUBB as the cc_op. + */ + tcg_gen_sub_tl(s->T0, s->T0, s->T1); + tcg_gen_sub_tl(s->T0, s->T0, c_in); + } + prepare_update3_cc(decode, s, CC_OP_SBBB + ot, c_in); +} + +static void gen_SCAS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[2].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_nz(s, ot, gen_scas); + } else { + gen_scas(s, ot); + } +} + +static void gen_SETcc(DisasContext *s, X86DecodedInsn *decode) +{ + gen_setcc1(s, decode->b & 0xf, s->T0); +} + +static void gen_SFENCE(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); +} + +static void gen_SHA1NEXTE(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sha1nexte(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1MSG1(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sha1msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1MSG2(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sha1msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA1RNDS4(DisasContext *s, 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, X86DecodedInsn *decode) +{ + gen_helper_sha256msg1(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA256MSG2(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sha256msg2(OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_SHA256RNDS2(DisasContext *s, 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_SHL(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = tcg_temp_new(); + tcg_gen_subi_tl(decode->cc_src, count, 1); + tcg_gen_shl_tl(decode->cc_src, s->T0, decode->cc_src); + tcg_gen_shl_tl(s->T0, s->T0, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot); + } else { + decode->cc_op = CC_OP_SHLB + ot; + } +} + +static void gen_SHLD(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + int unit = decode->e.op3 == X86_TYPE_I ? X86_OP_IMM : X86_OP_INT; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = s->tmp0; + gen_shiftd_rm_T1(s, ot, false, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot); + } else { + decode->cc_op = CC_OP_SHLB + ot; + } +} + +static void gen_SHLX(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; int mask; @@ -1813,40 +3979,183 @@ static void gen_SHLX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) tcg_gen_shl_tl(s->T0, s->T0, s->T1); } -static void gen_SHRX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_SHR(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = tcg_temp_new(); + tcg_gen_subi_tl(decode->cc_src, count, 1); + tcg_gen_shr_tl(decode->cc_src, s->T0, decode->cc_src); + tcg_gen_shr_tl(s->T0, s->T0, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); + } else { + decode->cc_op = CC_OP_SARB + ot; + } +} + +static void gen_SHRD(DisasContext *s, X86DecodedInsn *decode) +{ + bool can_be_zero; + TCGv count; + int unit = decode->e.op3 == X86_TYPE_I ? X86_OP_IMM : X86_OP_INT; + MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, unit); + + if (!count) { + return; + } + + decode->cc_dst = s->T0; + decode->cc_src = s->tmp0; + gen_shiftd_rm_T1(s, ot, true, count); + if (can_be_zero) { + gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); + } else { + decode->cc_op = CC_OP_SARB + ot; + } +} + +static void gen_SHRX(DisasContext *s, 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); - if (ot != MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); - } tcg_gen_shr_tl(s->T0, s->T0, s->T1); } -static void gen_VAESKEYGEN(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STC(DisasContext *s, X86DecodedInsn *decode) +{ + gen_compute_eflags(s); + tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); +} + +static void gen_STD(DisasContext *s, X86DecodedInsn *decode) +{ + tcg_gen_st_i32(tcg_constant_i32(-1), tcg_env, offsetof(CPUX86State, df)); +} + +static void gen_STI(DisasContext *s, X86DecodedInsn *decode) +{ + gen_set_eflags(s, IF_MASK); + s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; +} + +static void gen_VAESKEYGEN(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(!s->vex_l); - gen_helper_aeskeygenassist_xmm(cpu_env, OP_PTR0, OP_PTR1, imm); + gen_helper_aeskeygenassist_xmm(tcg_env, OP_PTR0, OP_PTR1, imm); } -static void gen_STMXCSR(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_STMXCSR(DisasContext *s, X86DecodedInsn *decode) { - if (s->vex_l) { - gen_illegal_opcode(s); - return; - } - gen_helper_update_mxcsr(cpu_env); - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr)); + 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) +static void gen_STOS(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + if (s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz(s, ot, gen_stos); + } else { + gen_stos(s, ot); + } +} + +static void gen_SUB(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_neg_tl(s->T0, s->T1); + tcg_gen_atomic_fetch_add_tl(s->cc_srcT, s->A0, s->T0, + s->mem_index, ot | MO_LE); + tcg_gen_sub_tl(s->T0, s->cc_srcT, s->T1); + } else { + tcg_gen_mov_tl(s->cc_srcT, s->T0); + tcg_gen_sub_tl(s->T0, s->T0, s->T1); + } + prepare_update2_cc(decode, s, CC_OP_SUBB + ot); +} + +static void gen_SYSCALL(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_syscall(tcg_env, cur_insn_len_i32(s)); + if (LMA(s)) { + assume_cc_op(s, CC_OP_EFLAGS); + } + + /* + * 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. + */ + s->base.is_jmp = DISAS_EOB_RECHECK_TF; +} + +static void gen_SYSENTER(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sysenter(tcg_env); + s->base.is_jmp = DISAS_EOB_ONLY; +} + +static void gen_SYSEXIT(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sysexit(tcg_env, tcg_constant_i32(s->dflag - 1)); + s->base.is_jmp = DISAS_EOB_ONLY; +} + +static void gen_SYSRET(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_sysret(tcg_env, tcg_constant_i32(s->dflag - 1)); + if (LMA(s)) { + assume_cc_op(s, CC_OP_EFLAGS); + } + + /* + * TF handling for the sysret insn is different. The TF bit is checked + * after the sysret insn completes. This allows #DB to be + * generated "as if" the syscall insn in userspace has just + * completed. + */ + s->base.is_jmp = DISAS_EOB_RECHECK_TF; +} + +static void gen_TZCNT(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[0].ot; + + /* C bit (cc_src) is defined related to the input. */ + decode->cc_src = tcg_temp_new(); + decode->cc_dst = s->T0; + decode->cc_op = CC_OP_BMILGB + ot; + tcg_gen_mov_tl(decode->cc_src, s->T0); + + /* A zero input returns the operand size. */ + tcg_gen_ctzi_tl(s->T0, s->T0, 8 << ot); +} + +static void gen_UD(DisasContext *s, X86DecodedInsn *decode) +{ + gen_illegal_opcode(s); +} + +static void gen_VAESIMC(DisasContext *s, X86DecodedInsn *decode) { assert(!s->vex_l); - gen_helper_aesimc_xmm(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_aesimc_xmm(tcg_env, OP_PTR0, OP_PTR2); } /* @@ -1898,7 +4207,7 @@ static const SSEFunc_0_eppp gen_helper_cmp_funcs[32][6] = { }; #undef SSE_CMP -static void gen_VCMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCMP(DisasContext *s, X86DecodedInsn *decode) { int index = decode->immediate & (s->prefix & PREFIX_VEX ? 31 : 7); int b = @@ -1906,28 +4215,38 @@ static void gen_VCMP(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) s->prefix & PREFIX_REPNZ ? 3 /* sd */ : !!(s->prefix & PREFIX_DATA) /* pd */ + (s->vex_l << 2); - gen_helper_cmp_funcs[index][b](cpu_env, OP_PTR0, OP_PTR1, OP_PTR2); + gen_helper_cmp_funcs[index][b](tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); } -static void gen_VCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCOMI(DisasContext *s, X86DecodedInsn *decode) { SSEFunc_0_epp fn; fn = s->prefix & PREFIX_DATA ? gen_helper_comisd : gen_helper_comiss; - fn(cpu_env, OP_PTR1, OP_PTR2); - set_cc_op(s, CC_OP_EFLAGS); + fn(tcg_env, OP_PTR1, OP_PTR2); + assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_VCVTfp2fp(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPD2PS(DisasContext *s, X86DecodedInsn *decode) { - gen_unary_fp_sse(s, env, decode, - gen_helper_cvtpd2ps_xmm, gen_helper_cvtps2pd_xmm, - gen_helper_cvtpd2ps_ymm, gen_helper_cvtps2pd_ymm, - gen_helper_cvtsd2ss, gen_helper_cvtss2sd); + 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_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTPS2PD(DisasContext *s, X86DecodedInsn *decode) { - gen_unary_imm_fp_sse(s, env, 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, X86DecodedInsn *decode) +{ + gen_unary_imm_fp_sse(s, decode, gen_helper_cvtps2ph_xmm, gen_helper_cvtps2ph_ymm); /* @@ -1939,7 +4258,17 @@ static void gen_VCVTPS2PH(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } } -static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSD2SS(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_cvtsd2ss(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_VCVTSS2SD(DisasContext *s, X86DecodedInsn *decode) +{ + gen_helper_cvtss2sd(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2); +} + +static void gen_VCVTSI2Sx(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); TCGv_i32 in; @@ -1950,9 +4279,9 @@ static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec MemOp ot = decode->op[2].ot; if (ot == MO_64) { if (s->prefix & PREFIX_REPNZ) { - gen_helper_cvtsq2sd(cpu_env, OP_PTR0, s->T1); + gen_helper_cvtsq2sd(tcg_env, OP_PTR0, s->T1); } else { - gen_helper_cvtsq2ss(cpu_env, OP_PTR0, s->T1); + gen_helper_cvtsq2ss(tcg_env, OP_PTR0, s->T1); } return; } @@ -1963,13 +4292,13 @@ static void gen_VCVTSI2Sx(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec #endif if (s->prefix & PREFIX_REPNZ) { - gen_helper_cvtsi2sd(cpu_env, OP_PTR0, in); + gen_helper_cvtsi2sd(tcg_env, OP_PTR0, in); } else { - gen_helper_cvtsi2ss(cpu_env, OP_PTR0, in); + gen_helper_cvtsi2ss(tcg_env, OP_PTR0, in); } } -static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_VCVTtSx2SI(DisasContext *s, X86DecodedInsn *decode, SSEFunc_i_ep ss2si, SSEFunc_l_ep ss2sq, SSEFunc_i_ep sd2si, SSEFunc_l_ep sd2sq) { @@ -1979,9 +4308,9 @@ static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedI MemOp ot = decode->op[0].ot; if (ot == MO_64) { if (s->prefix & PREFIX_REPNZ) { - sd2sq(s->T0, cpu_env, OP_PTR2); + sd2sq(s->T0, tcg_env, OP_PTR2); } else { - ss2sq(s->T0, cpu_env, OP_PTR2); + ss2sq(s->T0, tcg_env, OP_PTR2); } return; } @@ -1991,9 +4320,9 @@ static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedI out = s->T0; #endif if (s->prefix & PREFIX_REPNZ) { - sd2si(out, cpu_env, OP_PTR2); + sd2si(out, tcg_env, OP_PTR2); } else { - ss2si(out, cpu_env, OP_PTR2); + ss2si(out, tcg_env, OP_PTR2); } #ifdef TARGET_X86_64 tcg_gen_extu_i32_tl(s->T0, out); @@ -2007,21 +4336,21 @@ static inline void gen_VCVTtSx2SI(DisasContext *s, CPUX86State *env, X86DecodedI #define gen_helper_cvttsd2sq NULL #endif -static void gen_VCVTSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTSx2SI(DisasContext *s, X86DecodedInsn *decode) { - gen_VCVTtSx2SI(s, env, decode, + gen_VCVTtSx2SI(s, decode, gen_helper_cvtss2si, gen_helper_cvtss2sq, gen_helper_cvtsd2si, gen_helper_cvtsd2sq); } -static void gen_VCVTTSx2SI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VCVTTSx2SI(DisasContext *s, X86DecodedInsn *decode) { - gen_VCVTtSx2SI(s, env, decode, + gen_VCVTtSx2SI(s, decode, gen_helper_cvttss2si, gen_helper_cvttss2sq, gen_helper_cvttsd2si, gen_helper_cvttsd2sq); } -static void gen_VEXTRACTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VEXTRACTx128(DisasContext *s, X86DecodedInsn *decode) { int mask = decode->immediate & 1; int src_ofs = vector_elem_offset(&decode->op[1], MO_128, mask); @@ -2033,12 +4362,12 @@ static void gen_VEXTRACTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn * } } -static void gen_VEXTRACTPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VEXTRACTPS(DisasContext *s, X86DecodedInsn *decode) { - gen_pextr(s, env, decode, MO_32); + gen_pextr(s, decode, MO_32); } -static void gen_vinsertps(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_vinsertps(DisasContext *s, X86DecodedInsn *decode) { int val = decode->immediate; int dest_word = (val >> 4) & 3; @@ -2055,7 +4384,7 @@ static void gen_vinsertps(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec } if (new_mask != (val & 15)) { - tcg_gen_st_i32(s->tmp2_i32, cpu_env, + tcg_gen_st_i32(s->tmp2_i32, tcg_env, vector_elem_offset(&decode->op[0], MO_32, dest_word)); } @@ -2064,28 +4393,28 @@ static void gen_vinsertps(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec int i; for (i = 0; i < 4; i++) { if ((val >> i) & 1) { - tcg_gen_st_i32(zero, cpu_env, + 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) +static void gen_VINSERTPS_r(DisasContext *s, X86DecodedInsn *decode) { int val = decode->immediate; - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, + 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); + gen_vinsertps(s, decode); } -static void gen_VINSERTPS_m(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VINSERTPS_m(DisasContext *s, X86DecodedInsn *decode) { tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_vinsertps(s, env, decode); + gen_vinsertps(s, decode); } -static void gen_VINSERTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VINSERTx128(DisasContext *s, X86DecodedInsn *decode) { int mask = decode->immediate & 1; tcg_gen_gvec_mov(MO_64, @@ -2096,69 +4425,69 @@ static void gen_VINSERTx128(DisasContext *s, CPUX86State *env, X86DecodedInsn *d decode->op[1].offset + offsetof(YMMReg, YMM_X(!mask)), 16, 16); } -static inline void gen_maskmov(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode, +static inline void gen_maskmov(DisasContext *s, X86DecodedInsn *decode, SSEFunc_0_eppt xmm, SSEFunc_0_eppt ymm) { if (!s->vex_l) { - xmm(cpu_env, OP_PTR2, OP_PTR1, s->A0); + xmm(tcg_env, OP_PTR2, OP_PTR1, s->A0); } else { - ymm(cpu_env, OP_PTR2, OP_PTR1, s->A0); + ymm(tcg_env, OP_PTR2, OP_PTR1, s->A0); } } -static void gen_VMASKMOVPD_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMASKMOVPD_st(DisasContext *s, X86DecodedInsn *decode) { - gen_maskmov(s, env, decode, gen_helper_vpmaskmovq_st_xmm, gen_helper_vpmaskmovq_st_ymm); + gen_maskmov(s, decode, gen_helper_vpmaskmovq_st_xmm, gen_helper_vpmaskmovq_st_ymm); } -static void gen_VMASKMOVPS_st(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMASKMOVPS_st(DisasContext *s, X86DecodedInsn *decode) { - gen_maskmov(s, env, decode, gen_helper_vpmaskmovd_st_xmm, gen_helper_vpmaskmovd_st_ymm); + gen_maskmov(s, decode, gen_helper_vpmaskmovd_st_xmm, gen_helper_vpmaskmovd_st_ymm); } -static void gen_VMOVHPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVHPx_ld(DisasContext *s, 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, cpu_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(0))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + 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) +static void gen_VMOVHPx_st(DisasContext *s, 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) +static void gen_VMOVHPx(DisasContext *s, X86DecodedInsn *decode) { if (decode->op[0].offset != decode->op[2].offset) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + 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, cpu_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(0))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + 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) +static void gen_VMOVHLPS(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(1))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + 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, cpu_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(1))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + 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) +static void gen_VMOVLHPS(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, decode->op[2].offset); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(1))); + 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, cpu_env, decode->op[1].offset + offsetof(XMMReg, XMM_Q(0))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, decode->op[0].offset + offsetof(XMMReg, XMM_Q(0))); + 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))); } } @@ -2167,16 +4496,16 @@ static void gen_VMOVLHPS(DisasContext *s, CPUX86State *env, X86DecodedInsn *deco * Use a gvec move to move everything above the bottom 64 bits. */ -static void gen_VMOVLPx(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVLPx(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, decode->op[2].offset + offsetof(XMMReg, XMM_Q(0))); + 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, cpu_env, decode->op[0].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_VMOVLPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVLPx_ld(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2185,13 +4514,13 @@ static void gen_VMOVLPx_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *de 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) +static void gen_VMOVLPx_st(DisasContext *s, 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) +static void gen_VMOVSD_ld(DisasContext *s, X86DecodedInsn *decode) { TCGv_i64 zero = tcg_constant_i64(0); @@ -2200,7 +4529,7 @@ static void gen_VMOVSD_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec tcg_gen_st_i64(s->tmp1_i64, OP_PTR0, offsetof(ZMMReg, ZMM_Q(0))); } -static void gen_VMOVSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VMOVSS(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2209,7 +4538,7 @@ static void gen_VMOVSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode 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) +static void gen_VMOVSS_ld(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); @@ -2218,55 +4547,55 @@ static void gen_VMOVSS_ld(DisasContext *s, CPUX86State *env, X86DecodedInsn *dec 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) +static void gen_VMOVSS_st(DisasContext *s, 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) +static void gen_VPMASKMOV_st(DisasContext *s, X86DecodedInsn *decode) { if (s->vex_w) { - gen_VMASKMOVPD_st(s, env, decode); + gen_VMASKMOVPD_st(s, decode); } else { - gen_VMASKMOVPS_st(s, env, decode); + gen_VMASKMOVPS_st(s, decode); } } -static void gen_VPERMD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VPERMD(DisasContext *s, 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) +static void gen_VPERM2x128(DisasContext *s, 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) +static void gen_VPHMINPOSUW(DisasContext *s, X86DecodedInsn *decode) { assert(!s->vex_l); - gen_helper_phminposuw_xmm(cpu_env, OP_PTR0, OP_PTR2); + gen_helper_phminposuw_xmm(tcg_env, OP_PTR0, OP_PTR2); } -static void gen_VROUNDSD(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VROUNDSD(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(!s->vex_l); - gen_helper_roundsd_xmm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + gen_helper_roundsd_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VROUNDSS(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VROUNDSS(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant8u_i32(decode->immediate); assert(!s->vex_l); - gen_helper_roundss_xmm(cpu_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); + gen_helper_roundss_xmm(tcg_env, OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VSHUF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VSHUF(DisasContext *s, X86DecodedInsn *decode) { TCGv_i32 imm = tcg_constant_i32(decode->immediate); SSEFunc_0_pppi ps, pd, fn; @@ -2276,25 +4605,24 @@ static void gen_VSHUF(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) fn(OP_PTR0, OP_PTR1, OP_PTR2, imm); } -static void gen_VUCOMI(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VUCOMI(DisasContext *s, X86DecodedInsn *decode) { SSEFunc_0_epp fn; fn = s->prefix & PREFIX_DATA ? gen_helper_ucomisd : gen_helper_ucomiss; - fn(cpu_env, OP_PTR1, OP_PTR2); - set_cc_op(s, CC_OP_EFLAGS); + fn(tcg_env, OP_PTR1, OP_PTR2); + assume_cc_op(s, CC_OP_EFLAGS); } -static void gen_VZEROALL(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VZEROALL(DisasContext *s, X86DecodedInsn *decode) { TCGv_ptr ptr = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ptr, cpu_env, offsetof(CPUX86State, xmm_regs)); + 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))); - tcg_temp_free_ptr(ptr); } -static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) +static void gen_VZEROUPPER(DisasContext *s, X86DecodedInsn *decode) { int i; @@ -2303,3 +4631,134 @@ static void gen_VZEROUPPER(DisasContext *s, CPUX86State *env, X86DecodedInsn *de tcg_gen_gvec_dup_imm(MO_64, offset, 16, 16, 0); } } + +static void gen_WAIT(DisasContext *s, X86DecodedInsn *decode) +{ + if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == (HF_MP_MASK | HF_TS_MASK)) { + gen_NM_exception(s); + } else { + /* needs to be treated as I/O because of ferr_irq */ + translator_io_start(&s->base); + gen_helper_fwait(tcg_env); + } +} + +#ifndef CONFIG_USER_ONLY +static void gen_WRMSR(DisasContext *s, X86DecodedInsn *decode) +{ + gen_update_cc_op(s); + gen_update_eip_cur(s); + gen_helper_wrmsr(tcg_env); + s->base.is_jmp = DISAS_EOB_NEXT; +} +#else +#define gen_WRMSR gen_unreachable +#endif + +static void gen_WRxxBASE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv base = cpu_seg_base[s->modrm & 8 ? R_GS : R_FS]; + + /* Preserve hflags bits by testing CR4 at runtime. */ + gen_helper_cr4_testbit(tcg_env, tcg_constant_i32(CR4_FSGSBASE_MASK)); + tcg_gen_mov_tl(base, s->T0); +} + +static void gen_XADD(DisasContext *s, X86DecodedInsn *decode) +{ + MemOp ot = decode->op[1].ot; + + decode->cc_dst = tcg_temp_new(); + decode->cc_src = s->T1; + decode->cc_op = CC_OP_ADDB + ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_add_tl(s->T0, s->A0, s->T1, s->mem_index, ot | MO_LE); + tcg_gen_add_tl(decode->cc_dst, s->T0, s->T1); + } else { + tcg_gen_add_tl(decode->cc_dst, s->T0, s->T1); + /* + * NOTE: writing memory first is important for MMU exceptions, + * but "new result" wins for XADD AX, AX. + */ + gen_writeback(s, decode, 0, decode->cc_dst); + } + if (decode->op[0].has_ea || decode->op[2].n != decode->op[0].n) { + gen_writeback(s, decode, 2, s->T0); + } +} + +static void gen_XCHG(DisasContext *s, X86DecodedInsn *decode) +{ + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_xchg_tl(s->T0, s->A0, s->T1, + s->mem_index, decode->op[0].ot | MO_LE); + /* now store old value into register operand */ + gen_op_mov_reg_v(s, decode->op[2].ot, decode->op[2].n, s->T0); + } else { + /* move destination value into source operand, source preserved in T1 */ + gen_op_mov_reg_v(s, decode->op[2].ot, decode->op[2].n, s->T0); + tcg_gen_mov_tl(s->T0, s->T1); + } +} + +static void gen_XLAT(DisasContext *s, X86DecodedInsn *decode) +{ + /* AL is already zero-extended into s->T0. */ + tcg_gen_add_tl(s->A0, cpu_regs[R_EBX], s->T0); + gen_lea_v_seg(s, s->A0, R_DS, s->override); + gen_op_ld_v(s, MO_8, s->T0, s->A0); +} + +static void gen_XOR(DisasContext *s, X86DecodedInsn *decode) +{ + /* special case XOR reg, reg */ + if (decode->op[1].unit == X86_OP_INT && + decode->op[2].unit == X86_OP_INT && + decode->op[1].n == decode->op[2].n) { + tcg_gen_movi_tl(s->T0, 0); + decode->cc_op = CC_OP_EFLAGS; + decode->cc_src = tcg_constant_tl(CC_Z | CC_P); + } else { + MemOp ot = decode->op[1].ot; + + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T1, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_xor_tl(s->T0, s->T0, s->T1); + } + prepare_update1_cc(decode, s, CC_OP_LOGICB + ot); + } +} + +static void gen_XRSTOR(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 features = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(features, cpu_regs[R_EAX], cpu_regs[R_EDX]); + gen_helper_xrstor(tcg_env, s->A0, features); + if (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_MPX) { + /* + * XRSTOR is how MPX is enabled, which changes how + * we translate. Thus we need to end the TB. + */ + s->base.is_jmp = DISAS_EOB_NEXT; + } +} + +static void gen_XSAVE(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 features = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(features, cpu_regs[R_EAX], cpu_regs[R_EDX]); + gen_helper_xsave(tcg_env, s->A0, features); +} + +static void gen_XSAVEOPT(DisasContext *s, X86DecodedInsn *decode) +{ + TCGv_i64 features = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(features, cpu_regs[R_EAX], cpu_regs[R_EDX]); + gen_helper_xsave(tcg_env, s->A0, features); +} diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c index 7c3c8dc7fe..72387aac24 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, @@ -141,6 +140,26 @@ G_NORETURN void raise_exception_ra(CPUX86State *env, int exception_index, raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); } +G_NORETURN void helper_icebp(CPUX86State *env) +{ + CPUState *cs = env_cpu(env); + + do_end_instruction(env); + + /* + * INT1 aka ICEBP generates a trap-like #DB, but it is pretty special. + * + * "Although the ICEBP instruction dispatches through IDT vector 1, + * that event is not interceptable by means of the #DB exception + * intercept". Instead there is a separate fault-like ICEBP intercept. + */ + cs->exception_index = EXCP01_DB; + env->error_code = 0; + env->exception_is_int = 0; + env->exception_next_eip = env->eip; + cpu_loop_exit(cs); +} + G_NORETURN void handle_unaligned_access(CPUX86State *env, vaddr vaddr, MMUAccessType access_type, uintptr_t retaddr) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 64dca26840..9c6fd0dc2b 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -21,10 +21,13 @@ #include #include "cpu.h" #include "tcg-cpu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "fpu/softfloat-macros.h" #include "helper-tcg.h" +#include "access.h" /* float macros */ #define FT0 (env->ft0) @@ -275,23 +278,22 @@ static inline void fpop(CPUX86State *env) env->fpstt = (env->fpstt + 1) & 7; } -static floatx80 do_fldt(CPUX86State *env, target_ulong ptr, uintptr_t retaddr) +static floatx80 do_fldt(X86Access *ac, target_ulong ptr) { CPU_LDoubleU temp; - temp.l.lower = cpu_ldq_data_ra(env, ptr, retaddr); - temp.l.upper = cpu_lduw_data_ra(env, ptr + 8, retaddr); + temp.l.lower = access_ldq(ac, ptr); + temp.l.upper = access_ldw(ac, ptr + 8); return temp.d; } -static void do_fstt(CPUX86State *env, floatx80 f, target_ulong ptr, - uintptr_t retaddr) +static void do_fstt(X86Access *ac, target_ulong ptr, floatx80 f) { CPU_LDoubleU temp; temp.d = f; - cpu_stq_data_ra(env, ptr, temp.l.lower, retaddr); - cpu_stw_data_ra(env, ptr + 8, temp.l.upper, retaddr); + access_stq(ac, ptr, temp.l.lower); + access_stw(ac, ptr + 8, temp.l.upper); } /* x87 FPU helpers */ @@ -326,6 +328,46 @@ static void fpu_set_exception(CPUX86State *env, int mask) env->fpus |= FPUS_SE | FPUS_B; } } + +void cpu_init_fp_statuses(CPUX86State *env) +{ + /* + * Initialise the non-runtime-varying fields of the various + * float_status words to x86 behaviour. This must be called at + * CPU reset because the float_status words are in the + * "zeroed on reset" portion of the CPU state struct. + * Fields in float_status that vary under guest control are set + * via the codepath for setting that register, eg cpu_set_fpuc(). + */ + /* + * Use x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + /* + * TODO: These are incorrect: the x86 Software Developer's Manual vol 1 + * section 4.8.3.5 "Operating on SNaNs and QNaNs" says that the + * "larger significand" behaviour is only used for x87 FPU operations. + * For SSE the required behaviour is to always return the first NaN, + * which is float_2nan_prop_ab. + * + * mmx_status is used only for the AMD 3DNow! instructions, which + * are documented in the "3DNow! Technology Manual" as not supporting + * NaNs or infinities as inputs. The result of passing two NaNs is + * documented as "undefined", so we can do what we choose. + * (Strictly there is some behaviour we don't implement correctly + * for these "unsupported" NaN and Inf values, like "NaN * 0 == 0".) + */ + set_float_2nan_prop_rule(float_2nan_prop_x87, &env->mmx_status); + set_float_2nan_prop_rule(float_2nan_prop_x87, &env->sse_status); +} #endif static inline uint8_t save_exception_flags(CPUX86State *env) @@ -577,16 +619,22 @@ int64_t helper_fisttll_ST0(CPUX86State *env) void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) { int new_fpstt; + X86Access ac; + + access_prepare(&ac, env, ptr, 10, MMU_DATA_LOAD, GETPC()); new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = do_fldt(env, ptr, GETPC()); + env->fpregs[new_fpstt].d = do_fldt(&ac, ptr); env->fpstt = new_fpstt; env->fptags[new_fpstt] = 0; /* validate stack entry */ } void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) { - do_fstt(env, ST0, ptr, GETPC()); + X86Access ac; + + access_prepare(&ac, env, ptr, 10, MMU_DATA_STORE, GETPC()); + do_fstt(&ac, ptr, ST0); } void helper_fpush(CPUX86State *env) @@ -680,9 +728,9 @@ 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]; + CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } @@ -693,9 +741,9 @@ 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]; + CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } @@ -966,18 +1014,21 @@ void helper_fninit(CPUX86State *env) void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) { + X86Access ac; floatx80 tmp; uint64_t val; unsigned int v; int i; + access_prepare(&ac, env, ptr, 10, MMU_DATA_LOAD, GETPC()); + val = 0; for (i = 8; i >= 0; i--) { - v = cpu_ldub_data_ra(env, ptr + i, GETPC()); + v = access_ldb(&ac, ptr + i); val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); } tmp = int64_to_floatx80(val, &env->fp_status); - if (cpu_ldub_data_ra(env, ptr + 9, GETPC()) & 0x80) { + if (access_ldb(&ac, ptr + 9) & 0x80) { tmp = floatx80_chs(tmp); } fpush(env); @@ -991,7 +1042,9 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) target_ulong mem_ref, mem_end; int64_t val; CPU_LDoubleU temp; + X86Access ac; + access_prepare(&ac, env, ptr, 10, MMU_DATA_STORE, GETPC()); temp.d = ST0; val = floatx80_to_int64(ST0, &env->fp_status); @@ -999,20 +1052,20 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) if (val >= 1000000000000000000LL || val <= -1000000000000000000LL) { set_float_exception_flags(float_flag_invalid, &env->fp_status); while (mem_ref < ptr + 7) { - cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); + access_stb(&ac, mem_ref++, 0); } - cpu_stb_data_ra(env, mem_ref++, 0xc0, GETPC()); - cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); - cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); + access_stb(&ac, mem_ref++, 0xc0); + access_stb(&ac, mem_ref++, 0xff); + access_stb(&ac, mem_ref++, 0xff); merge_exception_flags(env, old_flags); return; } mem_end = mem_ref + 9; if (SIGND(temp)) { - cpu_stb_data_ra(env, mem_end, 0x80, GETPC()); + access_stb(&ac, mem_end, 0x80); val = -val; } else { - cpu_stb_data_ra(env, mem_end, 0x00, GETPC()); + access_stb(&ac, mem_end, 0x00); } while (mem_ref < mem_end) { if (val == 0) { @@ -1021,10 +1074,10 @@ void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) v = val % 100; val = val / 100; v = ((v / 10) << 4) | (v % 10); - cpu_stb_data_ra(env, mem_ref++, v, GETPC()); + access_stb(&ac, mem_ref++, v); } while (mem_ref < mem_end) { - cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); + access_stb(&ac, mem_ref++, 0); } merge_exception_flags(env, old_flags); } @@ -2561,9 +2614,9 @@ void helper_fxam_ST0(CPUX86State *env) } } -static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_fstenv(X86Access *ac, target_ulong ptr, int data32) { + CPUX86State *env = ac->env; int fpus, fptag, exp, i; uint64_t mant; CPU_LDoubleU tmp; @@ -2590,28 +2643,31 @@ static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, } if (data32) { /* 32 bit */ - cpu_stl_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stl_data_ra(env, ptr + 4, fpus, retaddr); - cpu_stl_data_ra(env, ptr + 8, fptag, retaddr); - cpu_stl_data_ra(env, ptr + 12, env->fpip, retaddr); /* fpip */ - cpu_stl_data_ra(env, ptr + 16, env->fpcs, retaddr); /* fpcs */ - cpu_stl_data_ra(env, ptr + 20, env->fpdp, retaddr); /* fpoo */ - cpu_stl_data_ra(env, ptr + 24, env->fpds, retaddr); /* fpos */ + access_stl(ac, ptr, env->fpuc); + access_stl(ac, ptr + 4, fpus); + access_stl(ac, ptr + 8, fptag); + access_stl(ac, ptr + 12, env->fpip); /* fpip */ + access_stl(ac, ptr + 16, env->fpcs); /* fpcs */ + access_stl(ac, ptr + 20, env->fpdp); /* fpoo */ + access_stl(ac, ptr + 24, env->fpds); /* fpos */ } else { /* 16 bit */ - cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); - cpu_stw_data_ra(env, ptr + 4, fptag, retaddr); - cpu_stw_data_ra(env, ptr + 6, env->fpip, retaddr); - cpu_stw_data_ra(env, ptr + 8, env->fpcs, retaddr); - cpu_stw_data_ra(env, ptr + 10, env->fpdp, retaddr); - cpu_stw_data_ra(env, ptr + 12, env->fpds, retaddr); + access_stw(ac, ptr, env->fpuc); + access_stw(ac, ptr + 2, fpus); + access_stw(ac, ptr + 4, fptag); + access_stw(ac, ptr + 6, env->fpip); + access_stw(ac, ptr + 8, env->fpcs); + access_stw(ac, ptr + 10, env->fpdp); + access_stw(ac, ptr + 12, env->fpds); } } void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) { - do_fstenv(env, ptr, data32, GETPC()); + X86Access ac; + + access_prepare(&ac, env, ptr, 14 << data32, MMU_DATA_STORE, GETPC()); + do_fstenv(&ac, ptr, data32); } static void cpu_set_fpus(CPUX86State *env, uint16_t fpus) @@ -2630,20 +2686,15 @@ static void cpu_set_fpus(CPUX86State *env, uint16_t fpus) #endif } -static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_fldenv(X86Access *ac, target_ulong ptr, int data32) { int i, fpus, fptag; + CPUX86State *env = ac->env; + + cpu_set_fpuc(env, access_ldw(ac, ptr)); + fpus = access_ldw(ac, ptr + (2 << data32)); + fptag = access_ldw(ac, ptr + (4 << data32)); - if (data32) { - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 4, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 8, retaddr); - } else { - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); - } cpu_set_fpus(env, fpus); for (i = 0; i < 8; i++) { env->fptags[i] = ((fptag & 3) == 3); @@ -2653,21 +2704,22 @@ static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) { - do_fldenv(env, ptr, data32, GETPC()); + X86Access ac; + + access_prepare(&ac, env, ptr, 14 << data32, MMU_DATA_STORE, GETPC()); + do_fldenv(&ac, ptr, data32); } -static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_fsave(X86Access *ac, target_ulong ptr, int data32) { - floatx80 tmp; - int i; + CPUX86State *env = ac->env; - do_fstenv(env, ptr, data32, retaddr); + do_fstenv(ac, ptr, data32); + ptr += 14 << data32; - ptr += (target_ulong)14 << data32; - for (i = 0; i < 8; i++) { - tmp = ST(i); - do_fstt(env, tmp, ptr, retaddr); + for (int i = 0; i < 8; i++) { + floatx80 tmp = ST(i); + do_fstt(ac, ptr, tmp); ptr += 10; } @@ -2676,20 +2728,22 @@ static void do_fsave(CPUX86State *env, target_ulong ptr, int data32, void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) { - do_fsave(env, ptr, data32, GETPC()); + int size = (14 << data32) + 80; + X86Access ac; + + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, GETPC()); + do_fsave(&ac, ptr, data32); } -static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) +static void do_frstor(X86Access *ac, target_ulong ptr, int data32) { - floatx80 tmp; - int i; + CPUX86State *env = ac->env; - do_fldenv(env, ptr, data32, retaddr); - ptr += (target_ulong)14 << data32; + do_fldenv(ac, ptr, data32); + ptr += 14 << data32; - for (i = 0; i < 8; i++) { - tmp = do_fldt(env, ptr, retaddr); + for (int i = 0; i < 8; i++) { + floatx80 tmp = do_fldt(ac, ptr); ST(i) = tmp; ptr += 10; } @@ -2697,15 +2751,20 @@ static void do_frstor(CPUX86State *env, target_ulong ptr, int data32, void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) { - do_frstor(env, ptr, data32, GETPC()); + int size = (14 << data32) + 80; + X86Access ac; + + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, GETPC()); + do_frstor(&ac, ptr, data32); } #ifndef USE_HARD_FPU #define XO(X) offsetof(X86XSaveArea, X) -static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_fpu(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int fpus, fptag, i; target_ulong addr; @@ -2715,33 +2774,37 @@ static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) fptag |= (env->fptags[i] << i); } - cpu_stw_data_ra(env, ptr + XO(legacy.fcw), env->fpuc, ra); - cpu_stw_data_ra(env, ptr + XO(legacy.fsw), fpus, ra); - cpu_stw_data_ra(env, ptr + XO(legacy.ftw), fptag ^ 0xff, ra); + access_stw(ac, ptr + XO(legacy.fcw), env->fpuc); + access_stw(ac, ptr + XO(legacy.fsw), fpus); + access_stw(ac, ptr + XO(legacy.ftw), fptag ^ 0xff); /* In 32-bit mode this is eip, sel, dp, sel. In 64-bit mode this is rip, rdp. But in either case we don't write actual data, just zeros. */ - cpu_stq_data_ra(env, ptr + XO(legacy.fpip), 0, ra); /* eip+sel; rip */ - cpu_stq_data_ra(env, ptr + XO(legacy.fpdp), 0, ra); /* edp+sel; rdp */ + access_stq(ac, ptr + XO(legacy.fpip), 0); /* eip+sel; rip */ + access_stq(ac, ptr + XO(legacy.fpdp), 0); /* edp+sel; rdp */ addr = ptr + XO(legacy.fpregs); + for (i = 0; i < 8; i++) { floatx80 tmp = ST(i); - do_fstt(env, tmp, addr, ra); + do_fstt(ac, addr, tmp); addr += 16; } } -static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_mxcsr(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; + update_mxcsr_from_sse_status(env); - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr), env->mxcsr, ra); - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra); + access_stl(ac, ptr + XO(legacy.mxcsr), env->mxcsr); + access_stl(ac, ptr + XO(legacy.mxcsr_mask), 0x0000ffff); } -static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_sse(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; target_ulong addr; @@ -2753,14 +2816,15 @@ static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) addr = ptr + XO(legacy.xmm_regs); for (i = 0; i < nb_xmm_regs; i++) { - cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), ra); - cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), ra); + access_stq(ac, addr, env->xmm_regs[i].ZMM_Q(0)); + access_stq(ac, addr + 8, env->xmm_regs[i].ZMM_Q(1)); addr += 16; } } -static void do_xsave_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_ymmh(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; if (env->hflags & HF_CS64_MASK) { @@ -2770,58 +2834,67 @@ static void do_xsave_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) } 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); + access_stq(ac, ptr, env->xmm_regs[i].ZMM_Q(2)); + access_stq(ac, ptr + 8, env->xmm_regs[i].ZMM_Q(3)); } } -static void do_xsave_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_bndregs(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); int i; for (i = 0; i < 4; i++, addr += 16) { - cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra); - cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra); + access_stq(ac, addr, env->bnd_regs[i].lb); + access_stq(ac, addr + 8, env->bnd_regs[i].ub); } } -static void do_xsave_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_bndcsr(X86Access *ac, target_ulong ptr) { - cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), - env->bndcs_regs.cfgu, ra); - cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), - env->bndcs_regs.sts, ra); + CPUX86State *env = ac->env; + + access_stq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), + env->bndcs_regs.cfgu); + access_stq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), + env->bndcs_regs.sts); } -static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xsave_pkru(X86Access *ac, target_ulong ptr) { - cpu_stq_data_ra(env, ptr, env->pkru, ra); + access_stq(ac, ptr, ac->env->pkru); } -static void do_fxsave(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_fxsave(X86Access *ac, target_ulong ptr) { - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - do_xsave_fpu(env, ptr, ra); + CPUX86State *env = ac->env; + do_xsave_fpu(ac, ptr); if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xsave_mxcsr(env, ptr, ra); + do_xsave_mxcsr(ac, ptr); /* Fast FXSAVE leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - do_xsave_sse(env, ptr, ra); + do_xsave_sse(ac, ptr); } } } void helper_fxsave(CPUX86State *env, target_ulong ptr) { - do_fxsave(env, ptr, GETPC()); + uintptr_t ra = GETPC(); + X86Access ac; + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_STORE, ra); + do_fxsave(&ac, ptr); } static uint64_t get_xinuse(CPUX86State *env) @@ -2838,11 +2911,42 @@ static uint64_t get_xinuse(CPUX86State *env) return inuse; } -static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, - uint64_t inuse, uint64_t opt, uintptr_t ra) +static void do_xsave_access(X86Access *ac, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt) { uint64_t old_bv, new_bv; + if (opt & XSTATE_FP_MASK) { + do_xsave_fpu(ac, ptr); + } + if (rfbm & XSTATE_SSE_MASK) { + /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ + do_xsave_mxcsr(ac, ptr); + } + if (opt & XSTATE_SSE_MASK) { + do_xsave_sse(ac, ptr); + } + if (opt & XSTATE_YMM_MASK) { + do_xsave_ymmh(ac, ptr + XO(avx_state)); + } + if (opt & XSTATE_BNDREGS_MASK) { + do_xsave_bndregs(ac, ptr + XO(bndreg_state)); + } + if (opt & XSTATE_BNDCSR_MASK) { + do_xsave_bndcsr(ac, ptr + XO(bndcsr_state)); + } + if (opt & XSTATE_PKRU_MASK) { + do_xsave_pkru(ac, ptr + XO(pkru_state)); + } + + /* Update the XSTATE_BV field. */ + old_bv = access_ldq(ac, ptr + XO(header.xstate_bv)); + new_bv = (old_bv & ~rfbm) | (inuse & rfbm); + access_stq(ac, ptr + XO(header.xstate_bv), new_bv); +} + +static void do_xsave_chk(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ /* The OS must have enabled XSAVE. */ if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { raise_exception_ra(env, EXCP06_ILLOP, ra); @@ -2852,43 +2956,28 @@ static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, if (ptr & 63) { raise_exception_ra(env, EXCP0D_GPF, ra); } +} + +static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt, uintptr_t ra) +{ + X86Access ac; + unsigned size; + + do_xsave_chk(env, ptr, ra); /* Never save anything not enabled by XCR0. */ rfbm &= env->xcr0; opt &= rfbm; + size = xsave_area_size(opt, false); - if (opt & XSTATE_FP_MASK) { - do_xsave_fpu(env, ptr, ra); - } - if (rfbm & XSTATE_SSE_MASK) { - /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ - do_xsave_mxcsr(env, ptr, ra); - } - 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); - } - if (opt & XSTATE_BNDCSR_MASK) { - do_xsave_bndcsr(env, ptr + XO(bndcsr_state), ra); - } - if (opt & XSTATE_PKRU_MASK) { - do_xsave_pkru(env, ptr + XO(pkru_state), ra); - } - - /* Update the XSTATE_BV field. */ - old_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); - new_bv = (old_bv & ~rfbm) | (inuse & rfbm); - cpu_stq_data_ra(env, ptr + XO(header.xstate_bv), new_bv, ra); + access_prepare(&ac, env, ptr, size, MMU_DATA_STORE, ra); + do_xsave_access(&ac, ptr, rfbm, inuse, opt); } void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); + do_xsave(env, ptr, rfbm, get_xinuse(env), rfbm, GETPC()); } void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) @@ -2897,36 +2986,41 @@ void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); } -static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_fpu(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, fpuc, fpus, fptag; target_ulong addr; - fpuc = cpu_lduw_data_ra(env, ptr + XO(legacy.fcw), ra); - fpus = cpu_lduw_data_ra(env, ptr + XO(legacy.fsw), ra); - fptag = cpu_lduw_data_ra(env, ptr + XO(legacy.ftw), ra); + fpuc = access_ldw(ac, ptr + XO(legacy.fcw)); + fpus = access_ldw(ac, ptr + XO(legacy.fsw)); + fptag = access_ldw(ac, ptr + XO(legacy.ftw)); cpu_set_fpuc(env, fpuc); cpu_set_fpus(env, fpus); + fptag ^= 0xff; for (i = 0; i < 8; i++) { env->fptags[i] = ((fptag >> i) & 1); } addr = ptr + XO(legacy.fpregs); + for (i = 0; i < 8; i++) { - floatx80 tmp = do_fldt(env, addr, ra); + floatx80 tmp = do_fldt(ac, addr); ST(i) = tmp; addr += 16; } } -static void do_xrstor_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_mxcsr(X86Access *ac, target_ulong ptr) { - cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + XO(legacy.mxcsr), ra)); + CPUX86State *env = ac->env; + cpu_set_mxcsr(env, access_ldl(ac, ptr + XO(legacy.mxcsr))); } -static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_sse(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; target_ulong addr; @@ -2938,8 +3032,8 @@ static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) addr = ptr + XO(legacy.xmm_regs); for (i = 0; i < nb_xmm_regs; i++) { - env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, ra); - env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, ra); + env->xmm_regs[i].ZMM_Q(0) = access_ldq(ac, addr); + env->xmm_regs[i].ZMM_Q(1) = access_ldq(ac, addr + 8); addr += 16; } } @@ -2960,8 +3054,9 @@ static void do_clear_sse(CPUX86State *env) } } -static void do_xrstor_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_ymmh(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; int i, nb_xmm_regs; if (env->hflags & HF_CS64_MASK) { @@ -2971,8 +3066,8 @@ static void do_xrstor_ymmh(CPUX86State *env, target_ulong ptr, uintptr_t ra) } 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); + env->xmm_regs[i].ZMM_Q(2) = access_ldq(ac, ptr); + env->xmm_regs[i].ZMM_Q(3) = access_ldq(ac, ptr + 8); } } @@ -2992,100 +3087,97 @@ static void do_clear_ymmh(CPUX86State *env) } } -static void do_xrstor_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_bndregs(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); int i; for (i = 0; i < 4; i++, addr += 16) { - env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra); - env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra); + env->bnd_regs[i].lb = access_ldq(ac, addr); + env->bnd_regs[i].ub = access_ldq(ac, addr + 8); } } -static void do_xrstor_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_bndcsr(X86Access *ac, target_ulong ptr) { + CPUX86State *env = ac->env; + /* FIXME: Extend highest implemented bit of linear address. */ env->bndcs_regs.cfgu - = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), ra); + = access_ldq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu)); env->bndcs_regs.sts - = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), ra); + = access_ldq(ac, ptr + offsetof(XSaveBNDCSR, bndcsr.sts)); } -static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_xrstor_pkru(X86Access *ac, target_ulong ptr) { - env->pkru = cpu_ldq_data_ra(env, ptr, ra); + ac->env->pkru = access_ldq(ac, ptr); } -static void do_fxrstor(CPUX86State *env, target_ulong ptr, uintptr_t ra) +static void do_fxrstor(X86Access *ac, target_ulong ptr) { - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - do_xrstor_fpu(env, ptr, ra); + CPUX86State *env = ac->env; + do_xrstor_fpu(ac, ptr); if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xrstor_mxcsr(env, ptr, ra); + do_xrstor_mxcsr(ac, ptr); /* Fast FXRSTOR leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - do_xrstor_sse(env, ptr, ra); + do_xrstor_sse(ac, ptr); } } } void helper_fxrstor(CPUX86State *env, target_ulong ptr) { - do_fxrstor(env, ptr, GETPC()); + uintptr_t ra = GETPC(); + X86Access ac; + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + access_prepare(&ac, env, ptr, sizeof(X86LegacyXSaveArea), + MMU_DATA_LOAD, ra); + do_fxrstor(&ac, ptr); } -static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr_t ra) +static bool valid_xrstor_header(X86Access *ac, uint64_t *pxsbv, + target_ulong ptr) { uint64_t xstate_bv, xcomp_bv, reserve0; - rfbm &= env->xcr0; + xstate_bv = access_ldq(ac, ptr + XO(header.xstate_bv)); + xcomp_bv = access_ldq(ac, ptr + XO(header.xcomp_bv)); + reserve0 = access_ldq(ac, ptr + XO(header.reserve0)); + *pxsbv = xstate_bv; - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); + /* + * XCOMP_BV bit 63 indicates compact form, which we do not support, + * and thus must raise #GP. That leaves us in standard form. + * In standard form, bytes 23:8 must be zero -- which is both + * XCOMP_BV and the following 64-bit field. + */ + if (xcomp_bv || reserve0) { + return false; } - /* The operand must be 64 byte aligned. */ - if (ptr & 63) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - xstate_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); - - if ((int64_t)xstate_bv < 0) { - /* FIXME: Compact form. */ - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - /* Standard form. */ - /* The XSTATE_BV field must not set bits not present in XCR0. */ - if (xstate_bv & ~env->xcr0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } + return (xstate_bv & ~ac->env->xcr0) == 0; +} - /* The XCOMP_BV field must be zero. Note that, as of the April 2016 - revision, the description of the XSAVE Header (Vol 1, Sec 13.4.2) - describes only XCOMP_BV, but the description of the standard form - of XRSTOR (Vol 1, Sec 13.8.1) checks bytes 23:8 for zero, which - includes the next 64-bit field. */ - xcomp_bv = cpu_ldq_data_ra(env, ptr + XO(header.xcomp_bv), ra); - reserve0 = cpu_ldq_data_ra(env, ptr + XO(header.reserve0), ra); - if (xcomp_bv || reserve0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } +static void do_xrstor(X86Access *ac, target_ulong ptr, + uint64_t rfbm, uint64_t xstate_bv) +{ + CPUX86State *env = ac->env; if (rfbm & XSTATE_FP_MASK) { if (xstate_bv & XSTATE_FP_MASK) { - do_xrstor_fpu(env, ptr, ra); + do_xrstor_fpu(ac, ptr); } else { do_fninit(env); memset(env->fpregs, 0, sizeof(env->fpregs)); @@ -3094,23 +3186,23 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr if (rfbm & XSTATE_SSE_MASK) { /* Note that the standard form of XRSTOR loads MXCSR from memory whether or not the XSTATE_BV bit is set. */ - do_xrstor_mxcsr(env, ptr, ra); + do_xrstor_mxcsr(ac, ptr); if (xstate_bv & XSTATE_SSE_MASK) { - do_xrstor_sse(env, ptr, ra); + do_xrstor_sse(ac, ptr); } else { do_clear_sse(env); } } if (rfbm & XSTATE_YMM_MASK) { if (xstate_bv & XSTATE_YMM_MASK) { - do_xrstor_ymmh(env, ptr + XO(avx_state), ra); + do_xrstor_ymmh(ac, ptr + XO(avx_state)); } else { do_clear_ymmh(env); } } if (rfbm & XSTATE_BNDREGS_MASK) { if (xstate_bv & XSTATE_BNDREGS_MASK) { - do_xrstor_bndregs(env, ptr + XO(bndreg_state), ra); + do_xrstor_bndregs(ac, ptr + XO(bndreg_state)); env->hflags |= HF_MPX_IU_MASK; } else { memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); @@ -3119,7 +3211,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr } if (rfbm & XSTATE_BNDCSR_MASK) { if (xstate_bv & XSTATE_BNDCSR_MASK) { - do_xrstor_bndcsr(env, ptr + XO(bndcsr_state), ra); + do_xrstor_bndcsr(ac, ptr + XO(bndcsr_state)); } else { memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); } @@ -3128,7 +3220,7 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr if (rfbm & XSTATE_PKRU_MASK) { uint64_t old_pkru = env->pkru; if (xstate_bv & XSTATE_PKRU_MASK) { - do_xrstor_pkru(env, ptr + XO(pkru_state), ra); + do_xrstor_pkru(ac, ptr + XO(pkru_state)); } else { env->pkru = 0; } @@ -3143,38 +3235,117 @@ static void do_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm, uintptr void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_xrstor(env, ptr, rfbm, GETPC()); + uintptr_t ra = GETPC(); + X86Access ac; + uint64_t xstate_bv; + unsigned size, size_ext; + + do_xsave_chk(env, ptr, ra); + + /* Begin with just the minimum size to validate the header. */ + size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); + access_prepare(&ac, env, ptr, size, MMU_DATA_LOAD, ra); + if (!valid_xrstor_header(&ac, &xstate_bv, ptr)) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + rfbm &= env->xcr0; + size_ext = xsave_area_size(rfbm & xstate_bv, false); + if (size < size_ext) { + /* TODO: See if existing page probe has covered extra size. */ + access_prepare(&ac, env, ptr, size_ext, MMU_DATA_LOAD, ra); + } + + do_xrstor(&ac, ptr, rfbm, xstate_bv); } #if defined(CONFIG_USER_ONLY) -void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) +void cpu_x86_fsave(CPUX86State *env, void *host, size_t len) { - do_fsave(env, ptr, data32, 0); + X86Access ac = { + .haddr1 = host, + .size = 4 * 7 + 8 * 10, + .env = env, + }; + + assert(ac.size <= len); + do_fsave(&ac, 0, true); } -void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) +void cpu_x86_frstor(CPUX86State *env, void *host, size_t len) { - do_frstor(env, ptr, data32, 0); + X86Access ac = { + .haddr1 = host, + .size = 4 * 7 + 8 * 10, + .env = env, + }; + + assert(ac.size <= len); + do_frstor(&ac, 0, true); } -void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) +void cpu_x86_fxsave(CPUX86State *env, void *host, size_t len) { - do_fxsave(env, ptr, 0); + X86Access ac = { + .haddr1 = host, + .size = sizeof(X86LegacyXSaveArea), + .env = env, + }; + + assert(ac.size <= len); + do_fxsave(&ac, 0); } -void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) +void cpu_x86_fxrstor(CPUX86State *env, void *host, size_t len) { - do_fxrstor(env, ptr, 0); + X86Access ac = { + .haddr1 = host, + .size = sizeof(X86LegacyXSaveArea), + .env = env, + }; + + assert(ac.size <= len); + do_fxrstor(&ac, 0); } -void cpu_x86_xsave(CPUX86State *env, target_ulong ptr) +void cpu_x86_xsave(CPUX86State *env, void *host, size_t len, uint64_t rfbm) { - do_xsave(env, ptr, -1, get_xinuse(env), -1, 0); + X86Access ac = { + .haddr1 = host, + .env = env, + }; + + /* + * Since this is only called from user-level signal handling, + * we should have done the job correctly there. + */ + assert((rfbm & ~env->xcr0) == 0); + ac.size = xsave_area_size(rfbm, false); + assert(ac.size <= len); + do_xsave_access(&ac, 0, rfbm, get_xinuse(env), rfbm); } -void cpu_x86_xrstor(CPUX86State *env, target_ulong ptr) +bool cpu_x86_xrstor(CPUX86State *env, void *host, size_t len, uint64_t rfbm) { - do_xrstor(env, ptr, -1, 0); + X86Access ac = { + .haddr1 = host, + .env = env, + }; + uint64_t xstate_bv; + + /* + * Since this is only called from user-level signal handling, + * we should have done the job correctly there. + */ + assert((rfbm & ~env->xcr0) == 0); + ac.size = xsave_area_size(rfbm, false); + assert(ac.size <= len); + + if (!valid_xrstor_header(&ac, &xstate_bv, 0)) { + return false; + } + do_xrstor(&ac, 0, rfbm, xstate_bv); + return true; } #endif @@ -3212,6 +3383,11 @@ void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) goto do_gpf; } + /* SSE can be disabled, but only if AVX is disabled too. */ + if ((mask & (XSTATE_SSE_MASK | XSTATE_YMM_MASK)) == XSTATE_YMM_MASK) { + goto do_gpf; + } + /* Disallow enabling unimplemented features. */ cpu_x86_cpuid(env, 0x0d, 0, &ena_lo, &dummy, &dummy, &ena_hi); ena = ((uint64_t)ena_hi << 32) | ena_lo; diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index cd1723389a..696d6ef016 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -21,6 +21,7 @@ #define I386_HELPER_TCG_H #include "exec/exec-all.h" +#include "qemu/host-utils.h" /* Maximum instruction code size */ #define TARGET_MAX_INSN_SIZE 16 @@ -39,6 +40,8 @@ QEMU_BUILD_BUG_ON(TCG_PHYS_ADDR_BITS > TARGET_PHYS_ADDR_SPACE_BITS); */ void x86_cpu_do_interrupt(CPUState *cpu); #ifndef CONFIG_USER_ONLY +bool 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 @@ -65,8 +68,7 @@ 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); @@ -86,11 +88,13 @@ G_NORETURN void x86_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, #endif /* cc_helper.c */ -extern const uint8_t parity_table[256]; +static inline unsigned int compute_pf(uint8_t x) +{ + return !parity8(x) * CC_P; +} /* misc_helper.c */ void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask); -G_NORETURN void do_pause(CPUX86State *env); /* sysemu/svm_helper.c */ #ifndef CONFIG_USER_ONLY @@ -110,7 +114,17 @@ int exception_has_error_code(int intno); /* smm_helper.c */ void do_smm_enter(X86CPU *cpu); -/* bpt_helper.c */ +/* sysemu/bpt_helper.c */ bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update); +/* + * Do the tasks usually performed by gen_eob(). Callers of this function + * should also handle TF as appropriate. + */ +static inline void do_end_instruction(CPUX86State *env) +{ + /* needed if sti is just before */ + env->hflags &= ~HF_INHIBIT_IRQ_MASK; + env->eflags &= ~HF_RF_MASK; +} #endif /* I386_HELPER_TCG_H */ diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index 599ac968b0..1a02e9d843 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -29,22 +29,6 @@ //#define DEBUG_MULDIV -/* modulo 9 table */ -static const uint8_t rclb_table[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 0, 1, 2, 3, 4, 5, - 6, 7, 8, 0, 1, 2, 3, 4, -}; - -/* modulo 17 table */ -static const uint8_t rclw_table[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, -}; - /* division, flags are undefined */ void helper_divb_AL(CPUX86State *env, target_ulong t0) @@ -161,27 +145,24 @@ void helper_idivl_EAX(CPUX86State *env, target_ulong t0) /* bcd */ -/* XXX: exception */ -void helper_aam(CPUX86State *env, int base) +target_ulong helper_aam(target_ulong al, target_ulong base) { - int al, ah; + int ah; - al = env->regs[R_EAX] & 0xff; + al &= 0xff; ah = al / base; al = al % base; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); - CC_DST = al; + return al | (ah << 8); } -void helper_aad(CPUX86State *env, int base) +target_ulong helper_aad(target_ulong ax, target_ulong base) { int al, ah; - al = env->regs[R_EAX] & 0xff; - ah = (env->regs[R_EAX] >> 8) & 0xff; + al = ax & 0xff; + ah = (ax >> 8) & 0xff; al = ((ah * base) + al) & 0xff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al; - CC_DST = al; + return al; } void helper_aaa(CPUX86State *env) @@ -190,7 +171,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; @@ -206,6 +187,7 @@ void helper_aaa(CPUX86State *env) } env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_aas(CPUX86State *env) @@ -214,7 +196,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; @@ -230,6 +212,7 @@ void helper_aas(CPUX86State *env) } env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_daa(CPUX86State *env) @@ -237,7 +220,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; @@ -254,9 +237,10 @@ void helper_daa(CPUX86State *env) env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; /* well, speed is not an issue here, so we compute the flags by hand */ eflags |= (al == 0) << 6; /* zf */ - eflags |= parity_table[al]; /* pf */ + eflags |= compute_pf(al); eflags |= (al & 0x80); /* sf */ CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_das(CPUX86State *env) @@ -264,7 +248,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; @@ -285,9 +269,10 @@ void helper_das(CPUX86State *env) env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; /* well, speed is not an issue here, so we compute the flags by hand */ eflags |= (al == 0) << 6; /* zf */ - eflags |= parity_table[al]; /* pf */ + eflags |= compute_pf(al); eflags |= (al & 0x80); /* sf */ CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } #ifdef TARGET_X86_64 @@ -447,24 +432,6 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) return dest; } -#define SHIFT 0 -#include "shift_helper_template.h" -#undef SHIFT - -#define SHIFT 1 -#include "shift_helper_template.h" -#undef SHIFT - -#define SHIFT 2 -#include "shift_helper_template.h" -#undef SHIFT - -#ifdef TARGET_X86_64 -#define SHIFT 3 -#include "shift_helper_template.h" -#undef SHIFT -#endif - /* Test that BIT is enabled in CR4. If not, raise an illegal opcode exception. This reduces the requirements for rare CR4 bits being mapped into HFLAGS. */ @@ -486,10 +453,11 @@ target_ulong HELPER(rdrand)(CPUX86State *env) error_free(err); /* Failure clears CF and all other flags, and returns 0. */ env->cc_src = 0; - return 0; + ret = 0; + } else { + /* Success sets CF and clears all others. */ + env->cc_src = CC_C; } - - /* Success sets CF and clears all others. */ - env->cc_src = CC_C; + env->cc_op = CC_OP_EFLAGS; return ret; } 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/meson.build b/target/i386/tcg/meson.build index cbf3c6a920..e7a48b3679 100644 --- a/target/i386/tcg/meson.build +++ b/target/i386/tcg/meson.build @@ -1,4 +1,5 @@ i386_ss.add(when: 'CONFIG_TCG', if_true: files( + 'access.c', 'bpt_helper.c', 'cc_helper.c', 'excp_helper.c', diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c index 5f7a3061ca..ed4cda8001 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 ) && @@ -94,23 +88,19 @@ G_NORETURN void helper_rdpmc(CPUX86State *env) raise_exception_err(env, EXCP06_ILLOP, 0); } -G_NORETURN void do_pause(CPUX86State *env) +G_NORETURN void helper_pause(CPUX86State *env) { CPUState *cs = env_cpu(env); + /* Do gen_eob() tasks before going back to the main loop. */ + do_end_instruction(env); + helper_rechecking_single_step(env); + /* Just let another CPU run. */ cs->exception_index = EXCP_INTERRUPT; cpu_loop_exit(cs); } -G_NORETURN void helper_pause(CPUX86State *env, int next_eip_addend) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0, GETPC()); - env->eip += next_eip_addend; - - do_pause(env); -} - uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx) { if ((env->cr[4] & CR4_PKE_MASK) == 0) { @@ -137,3 +127,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 539189b4d1..71962113fb 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -27,10 +27,74 @@ #include "exec/log.h" #include "helper-tcg.h" #include "seg_helper.h" +#include "access.h" + +#ifdef TARGET_X86_64 +#define SET_ESP(val, sp_mask) \ + do { \ + if ((sp_mask) == 0xffff) { \ + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | \ + ((val) & 0xffff); \ + } else if ((sp_mask) == 0xffffffffLL) { \ + env->regs[R_ESP] = (uint32_t)(val); \ + } else { \ + env->regs[R_ESP] = (val); \ + } \ + } while (0) +#else +#define SET_ESP(val, sp_mask) \ + do { \ + env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) | \ + ((val) & (sp_mask)); \ + } while (0) +#endif + +/* XXX: use mmu_index to have proper DPL support */ +typedef struct StackAccess +{ + CPUX86State *env; + uintptr_t ra; + target_ulong ss_base; + target_ulong sp; + target_ulong sp_mask; + int mmu_index; +} StackAccess; + +static void pushw(StackAccess *sa, uint16_t val) +{ + sa->sp -= 2; + cpu_stw_mmuidx_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), + val, sa->mmu_index, sa->ra); +} + +static void pushl(StackAccess *sa, uint32_t val) +{ + sa->sp -= 4; + cpu_stl_mmuidx_ra(sa->env, sa->ss_base + (sa->sp & sa->sp_mask), + val, sa->mmu_index, sa->ra); +} + +static uint16_t popw(StackAccess *sa) +{ + uint16_t ret = cpu_lduw_mmuidx_ra(sa->env, + sa->ss_base + (sa->sp & sa->sp_mask), + sa->mmu_index, sa->ra); + sa->sp += 2; + return ret; +} + +static uint32_t popl(StackAccess *sa) +{ + uint32_t ret = cpu_ldl_mmuidx_ra(sa->env, + sa->ss_base + (sa->sp & sa->sp_mask), + sa->mmu_index, sa->ra); + sa->sp += 4; + return ret; +} int get_pg_mode(CPUX86State *env) { - int pg_mode = 0; + int pg_mode = PG_MODE_PG; if (!(env->cr[0] & CR0_PG_MASK)) { return 0; } @@ -226,23 +290,39 @@ 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; + int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, i; target_ulong tss_base; uint32_t new_regs[8], new_segs[6]; uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; uint32_t old_eflags, eflags_mask; SegmentCache *dt; - int index; + int mmu_index, index; target_ulong ptr; + X86Access old, new; type = (e2 >> DESC_TYPE_SHIFT) & 0xf; LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, @@ -291,35 +371,87 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, old_tss_limit_max = 43; } + /* new TSS must be busy iff the source is an IRET instruction */ + if (!!(e2 & DESC_TSS_BUSY_MASK) != (source == SWITCH_TSS_IRET)) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); + } + + /* X86Access avoids memory exceptions during the task switch */ + mmu_index = cpu_mmu_index_kernel(env); + access_prepare_mmu(&old, env, env->tr.base, old_tss_limit_max + 1, + MMU_DATA_STORE, mmu_index, retaddr); + + if (source == SWITCH_TSS_CALL) { + /* Probe for future write of parent task */ + probe_access(env, tss_base, 2, MMU_DATA_STORE, + mmu_index, retaddr); + } + /* While true tss_limit may be larger, we don't access the iopb here. */ + access_prepare_mmu(&new, env, tss_base, tss_limit_max + 1, + MMU_DATA_LOAD, mmu_index, retaddr); + + /* save the current state in the old TSS */ + old_eflags = cpu_compute_eflags(env); + if (old_type & 8) { + /* 32 bit */ + access_stl(&old, env->tr.base + 0x20, next_eip); + access_stl(&old, env->tr.base + 0x24, old_eflags); + access_stl(&old, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX]); + access_stl(&old, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX]); + access_stl(&old, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX]); + access_stl(&old, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX]); + access_stl(&old, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP]); + access_stl(&old, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP]); + access_stl(&old, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI]); + access_stl(&old, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI]); + for (i = 0; i < 6; i++) { + access_stw(&old, env->tr.base + (0x48 + i * 4), + env->segs[i].selector); + } + } else { + /* 16 bit */ + access_stw(&old, env->tr.base + 0x0e, next_eip); + access_stw(&old, env->tr.base + 0x10, old_eflags); + access_stw(&old, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX]); + access_stw(&old, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX]); + access_stw(&old, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX]); + access_stw(&old, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX]); + access_stw(&old, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP]); + access_stw(&old, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP]); + access_stw(&old, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI]); + access_stw(&old, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI]); + for (i = 0; i < 4; i++) { + access_stw(&old, env->tr.base + (0x22 + i * 2), + env->segs[i].selector); + } + } + /* read all the registers from the new TSS */ if (type & 8) { /* 32 bit */ - new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr); - new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr); - new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr); + new_cr3 = access_ldl(&new, tss_base + 0x1c); + new_eip = access_ldl(&new, tss_base + 0x20); + new_eflags = access_ldl(&new, tss_base + 0x24); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4), - retaddr); + new_regs[i] = access_ldl(&new, tss_base + (0x28 + i * 4)); } for (i = 0; i < 6; i++) { - new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4), - retaddr); + new_segs[i] = access_ldw(&new, tss_base + (0x48 + i * 4)); } - new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr); - new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr); + new_ldt = access_ldw(&new, tss_base + 0x60); + new_trap = access_ldl(&new, tss_base + 0x64); } else { /* 16 bit */ new_cr3 = 0; - new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr); - new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr); + new_eip = access_ldw(&new, tss_base + 0x0e); + new_eflags = access_ldw(&new, tss_base + 0x10); for (i = 0; i < 8; i++) { - new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2), retaddr); + new_regs[i] = access_ldw(&new, tss_base + (0x12 + i * 2)); } for (i = 0; i < 4; i++) { - new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 2), - retaddr); + new_segs[i] = access_ldw(&new, tss_base + (0x22 + i * 2)); } - new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr); + new_ldt = access_ldw(&new, tss_base + 0x2a); new_segs[R_FS] = 0; new_segs[R_GS] = 0; new_trap = 0; @@ -329,87 +461,38 @@ static void switch_tss_ra(CPUX86State *env, int tss_selector, chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */ (void)new_trap; - /* NOTE: we must avoid memory exceptions during the task switch, - so we make dummy accesses before */ - /* XXX: it can still fail in some cases, so a bigger hack is - necessary to valid the TLB after having done the accesses */ - - v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr); - v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr); - cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr); - cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr); - /* 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) { old_eflags &= ~NT_MASK; + if (old_type & 8) { + access_stl(&old, env->tr.base + 0x24, old_eflags); + } else { + access_stw(&old, env->tr.base + 0x10, old_eflags); + } } - /* save the current state in the old TSS */ - if (old_type & 8) { - /* 32 bit */ - cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr); - cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr); - for (i = 0; i < 6; i++) { - cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4), - env->segs[i].selector, retaddr); - } - } else { - /* 16 bit */ - cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr); - cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr); - for (i = 0; i < 4; i++) { - cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 2), - env->segs[i].selector, retaddr); - } - } - - /* now if an exception occurs, it will occurs in the next task - context */ - if (source == SWITCH_TSS_CALL) { - cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr); + /* + * Thanks to the probe_access above, we know the first two + * bytes addressed by &new are writable too. + */ + access_stw(&new, tss_base, env->tr.selector); new_eflags |= NT_MASK; } /* 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 */ - /* from this point, any exception which occurs can give problems */ + + /* now if an exception occurs, it will occur in the next task context */ + env->cr[0] |= CR0_TS_MASK; env->hflags |= HF_TS_MASK; env->tr.selector = tss_selector; @@ -499,13 +582,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) @@ -522,6 +606,24 @@ static inline unsigned int get_sp_mask(unsigned int e2) } } +static int exception_is_fault(int intno) +{ + switch (intno) { + /* + * #DB can be both fault- and trap-like, but it never sets RF=1 + * in the RFLAGS value pushed on the stack. + */ + case EXCP01_DB: + case EXCP03_INT3: + case EXCP04_INTO: + case EXCP08_DBLE: + case EXCP12_MCHK: + return 0; + } + /* Everything else including reserved exception is a fault. */ + return 1; +} + int exception_has_error_code(int intno) { switch (intno) { @@ -537,72 +639,20 @@ int exception_has_error_code(int intno) return 0; } -#ifdef TARGET_X86_64 -#define SET_ESP(val, sp_mask) \ - do { \ - if ((sp_mask) == 0xffff) { \ - env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | \ - ((val) & 0xffff); \ - } else if ((sp_mask) == 0xffffffffLL) { \ - env->regs[R_ESP] = (uint32_t)(val); \ - } else { \ - env->regs[R_ESP] = (val); \ - } \ - } while (0) -#else -#define SET_ESP(val, sp_mask) \ - do { \ - env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) | \ - ((val) & (sp_mask)); \ - } while (0) -#endif - -/* in 64-bit machines, this can overflow. So this segment addition macro - * can be used to trim the value to 32-bit whenever needed */ -#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) - -/* XXX: add a is_user flag to have proper security support */ -#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ - { \ - sp -= 2; \ - cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ - } - -#define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ - { \ - sp -= 4; \ - cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \ - } - -#define POPW_RA(ssp, sp, sp_mask, val, ra) \ - { \ - val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ - sp += 2; \ - } - -#define POPL_RA(ssp, sp, sp_mask, val, ra) \ - { \ - val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \ - sp += 4; \ - } - -#define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0) -#define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0) -#define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0) -#define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0) - /* protected mode interrupt */ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, int error_code, unsigned int next_eip, int is_hw) { SegmentCache *dt; - target_ulong ptr, ssp; + target_ulong ptr; int type, dpl, selector, ss_dpl, cpl; int has_error_code, new_stack, shift; - uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; - uint32_t old_eip, sp_mask; + uint32_t e1, e2, offset, ss = 0, ss_e1 = 0, ss_e2 = 0; + uint32_t old_eip, eflags; int vm86 = env->eflags & VM_MASK; + StackAccess sa; + bool set_rf; has_error_code = 0; if (!is_int && !is_hw) { @@ -610,8 +660,10 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } if (is_int) { old_eip = next_eip; + set_rf = false; } else { old_eip = env->eip; + set_rf = exception_is_fault(intno); } dt = &env->idt; @@ -641,33 +693,33 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); } + sa.env = env; + sa.ra = 0; + if (type == 5) { /* task gate */ /* must do that check here to return the correct error code */ 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; + /* push the error code on the destination stack */ + cpl = env->hflags & HF_CPL_MASK; + sa.mmu_index = x86_mmu_index_pl(env, cpl); if (env->segs[R_SS].flags & DESC_B_MASK) { - mask = 0xffffffff; + sa.sp_mask = 0xffffffff; } else { - mask = 0xffff; + sa.sp_mask = 0xffff; } - esp = (env->regs[R_ESP] - (2 << shift)) & mask; - ssp = env->segs[R_SS].base + esp; + sa.sp = env->regs[R_ESP]; + sa.ss_base = env->segs[R_SS].base; if (shift) { - cpu_stl_kernel(env, ssp, error_code); + pushl(&sa, error_code); } else { - cpu_stw_kernel(env, ssp, error_code); + pushw(&sa, error_code); } - SET_ESP(esp, mask); + SET_ESP(sa.sp, sa.sp_mask); } return; } @@ -699,8 +751,10 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, if (e2 & DESC_C_MASK) { dpl = cpl; } + sa.mmu_index = x86_mmu_index_pl(env, dpl); if (dpl < cpl) { /* to inner privilege */ + uint32_t esp; get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); if ((ss & 0xfffc) == 0) { raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); @@ -724,17 +778,18 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); } new_stack = 1; - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); + sa.sp = esp; + sa.sp_mask = get_sp_mask(ss_e2); + sa.ss_base = get_seg_base(ss_e1, ss_e2); } else { /* to same privilege */ if (vm86) { raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); } new_stack = 0; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - esp = env->regs[R_ESP]; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; } shift = type >> 3; @@ -747,39 +802,48 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, } push_size <<= shift; #endif + eflags = cpu_compute_eflags(env); + /* + * AMD states that code breakpoint #DBs clear RF=0, Intel leaves it + * as is. AMD behavior could be implemented in check_hw_breakpoints(). + */ + if (set_rf) { + eflags |= RF_MASK; + } + if (shift == 1) { if (new_stack) { if (vm86) { - PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector); + pushl(&sa, env->segs[R_GS].selector); + pushl(&sa, env->segs[R_FS].selector); + pushl(&sa, env->segs[R_DS].selector); + pushl(&sa, env->segs[R_ES].selector); } - PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); - PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]); + pushl(&sa, env->segs[R_SS].selector); + pushl(&sa, env->regs[R_ESP]); } - PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env)); - PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, esp, sp_mask, old_eip); + pushl(&sa, eflags); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, old_eip); if (has_error_code) { - PUSHL(ssp, esp, sp_mask, error_code); + pushl(&sa, error_code); } } else { if (new_stack) { if (vm86) { - PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector); + pushw(&sa, env->segs[R_GS].selector); + pushw(&sa, env->segs[R_FS].selector); + pushw(&sa, env->segs[R_DS].selector); + pushw(&sa, env->segs[R_ES].selector); } - PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); - PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]); + pushw(&sa, env->segs[R_SS].selector); + pushw(&sa, env->regs[R_ESP]); } - PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env)); - PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, esp, sp_mask, old_eip); + pushw(&sa, eflags); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, old_eip); if (has_error_code) { - PUSHW(ssp, esp, sp_mask, error_code); + pushw(&sa, error_code); } } @@ -797,10 +861,10 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0); } ss = (ss & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); + cpu_x86_load_seg_cache(env, R_SS, ss, sa.ss_base, + get_seg_limit(ss_e1, ss_e2), ss_e2); } - SET_ESP(esp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); selector = (selector & ~3) | dpl; cpu_x86_load_seg_cache(env, R_CS, selector, @@ -812,20 +876,18 @@ static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, #ifdef TARGET_X86_64 -#define PUSHQ_RA(sp, val, ra) \ - { \ - sp -= 8; \ - cpu_stq_kernel_ra(env, sp, (val), ra); \ - } +static void pushq(StackAccess *sa, uint64_t val) +{ + sa->sp -= 8; + cpu_stq_mmuidx_ra(sa->env, sa->sp, val, sa->mmu_index, sa->ra); +} -#define POPQ_RA(sp, val, ra) \ - { \ - val = cpu_ldq_kernel_ra(env, sp, ra); \ - sp += 8; \ - } - -#define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0) -#define POPQ(sp, val) POPQ_RA(sp, val, 0) +static uint64_t popq(StackAccess *sa) +{ + uint64_t ret = cpu_ldq_mmuidx_ra(sa->env, sa->sp, sa->mmu_index, sa->ra); + sa->sp += 8; + return ret; +} static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) { @@ -867,8 +929,10 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, target_ulong ptr; int type, dpl, selector, cpl, ist; int has_error_code, new_stack; - uint32_t e1, e2, e3, ss; - target_ulong old_eip, esp, offset; + uint32_t e1, e2, e3, eflags; + target_ulong old_eip, offset; + bool set_rf; + StackAccess sa; has_error_code = 0; if (!is_int && !is_hw) { @@ -876,13 +940,15 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, } if (is_int) { old_eip = next_eip; + set_rf = false; } else { old_eip = env->eip; + set_rf = exception_is_fault(intno); } 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 +961,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); @@ -934,28 +1000,39 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, if (e2 & DESC_C_MASK) { dpl = cpl; } + + sa.env = env; + sa.ra = 0; + sa.mmu_index = x86_mmu_index_pl(env, dpl); + sa.sp_mask = -1; + sa.ss_base = 0; if (dpl < cpl || ist != 0) { /* to inner privilege */ new_stack = 1; - esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); - ss = 0; + sa.sp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); } else { /* to same privilege */ if (env->eflags & VM_MASK) { raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); } new_stack = 0; - esp = env->regs[R_ESP]; + sa.sp = env->regs[R_ESP]; } - esp &= ~0xfLL; /* align stack */ + sa.sp &= ~0xfLL; /* align stack */ - PUSHQ(esp, env->segs[R_SS].selector); - PUSHQ(esp, env->regs[R_ESP]); - PUSHQ(esp, cpu_compute_eflags(env)); - PUSHQ(esp, env->segs[R_CS].selector); - PUSHQ(esp, old_eip); + /* See do_interrupt_protected. */ + eflags = cpu_compute_eflags(env); + if (set_rf) { + eflags |= RF_MASK; + } + + pushq(&sa, env->segs[R_SS].selector); + pushq(&sa, env->regs[R_ESP]); + pushq(&sa, eflags); + pushq(&sa, env->segs[R_CS].selector); + pushq(&sa, old_eip); if (has_error_code) { - PUSHQ(esp, error_code); + pushq(&sa, error_code); } /* interrupt gate clear IF mask */ @@ -965,10 +1042,10 @@ static void do_interrupt64(CPUX86State *env, int intno, int is_int, env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); if (new_stack) { - ss = 0 | dpl; + uint32_t ss = 0 | dpl; /* SS = NULL selector with RPL = new CPL */ cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT); } - env->regs[R_ESP] = esp; + env->regs[R_ESP] = sa.sp; selector = (selector & ~3) | dpl; cpu_x86_load_seg_cache(env, R_CS, selector, @@ -977,6 +1054,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 +1068,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 +1094,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,17 +1111,17 @@ 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, int error_code, unsigned int next_eip) { SegmentCache *dt; - target_ulong ptr, ssp; + target_ulong ptr; int selector; - uint32_t offset, esp; + uint32_t offset; uint32_t old_cs, old_eip; + StackAccess sa; /* real mode (simpler!) */ dt = &env->idt; @@ -1050,8 +1131,14 @@ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, ptr = dt->base + intno * 4; offset = cpu_lduw_kernel(env, ptr); selector = cpu_lduw_kernel(env, ptr + 2); - esp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; + + sa.env = env; + sa.ra = 0; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = 0xffff; + sa.ss_base = env->segs[R_SS].base; + sa.mmu_index = x86_mmu_index_pl(env, 0); + if (is_int) { old_eip = next_eip; } else { @@ -1059,12 +1146,12 @@ static void do_interrupt_real(CPUX86State *env, int intno, int is_int, } old_cs = env->segs[R_CS].selector; /* XXX: use SS segment size? */ - PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env)); - PUSHW(ssp, esp, 0xffff, old_cs); - PUSHW(ssp, esp, 0xffff, old_eip); + pushw(&sa, cpu_compute_eflags(env)); + pushw(&sa, old_cs); + pushw(&sa, old_eip); /* update processor state */ - env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff); + SET_ESP(sa.sp, sa.sp_mask); env->eip = offset; env->segs[R_CS].selector = selector; env->segs[R_CS].base = (selector << 4); @@ -1507,21 +1594,24 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, void helper_lcall_real(CPUX86State *env, uint32_t new_cs, uint32_t new_eip, int shift, uint32_t next_eip) { - uint32_t esp, esp_mask; - target_ulong ssp; + StackAccess sa; + + sa.env = env; + sa.ra = GETPC(); + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; + sa.mmu_index = x86_mmu_index_pl(env, 0); - esp = env->regs[R_ESP]; - esp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; if (shift) { - PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC()); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, next_eip); } else { - PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC()); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, next_eip); } - SET_ESP(esp, esp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->eip = new_eip; env->segs[R_CS].selector = new_cs; env->segs[R_CS].base = (new_cs << 4); @@ -1533,9 +1623,10 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, { int new_stack, i; uint32_t e1, e2, cpl, dpl, rpl, selector, param_count; - uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask; + uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl; uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp, offset, sp; + target_ulong old_ssp, offset; + StackAccess sa; LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift); LOG_PCALL_STATE(env_cpu(env)); @@ -1547,7 +1638,12 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } cpl = env->hflags & HF_CPL_MASK; LOG_PCALL("desc=%08x:%08x\n", e1, e2); + + sa.env = env; + sa.ra = GETPC(); + if (e2 & DESC_S_MASK) { + /* "normal" far call, no stack switch possible */ if (!(e2 & DESC_CS_MASK)) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } @@ -1571,17 +1667,18 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } + sa.mmu_index = x86_mmu_index_pl(env, cpl); #ifdef TARGET_X86_64 /* XXX: check 16/32 bit cases in long mode */ if (shift == 2) { - target_ulong rsp; - /* 64 bit case */ - rsp = env->regs[R_ESP]; - PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC()); - PUSHQ_RA(rsp, next_eip, GETPC()); + sa.sp = env->regs[R_ESP]; + sa.sp_mask = -1; + sa.ss_base = 0; + pushq(&sa, env->segs[R_CS].selector); + pushq(&sa, next_eip); /* from this point, not restartable */ - env->regs[R_ESP] = rsp; + env->regs[R_ESP] = sa.sp; cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); @@ -1589,15 +1686,15 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } else #endif { - sp = env->regs[R_ESP]; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; if (shift) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, next_eip); } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, next_eip); } limit = get_seg_limit(e1, e2); @@ -1605,7 +1702,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); } /* from this point, not restartable */ - SET_ESP(sp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); env->eip = new_eip; @@ -1698,15 +1795,16 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (!(e2 & DESC_C_MASK) && dpl < cpl) { /* to inner privilege */ + sa.mmu_index = x86_mmu_index_pl(env, dpl); #ifdef TARGET_X86_64 if (shift == 2) { - sp = get_rsp_from_tss(env, dpl); ss = dpl; /* SS = NULL selector with RPL = new CPL */ new_stack = 1; - sp_mask = 0; - ssp = 0; /* SS base is always zero in IA-32e mode */ + sa.sp = get_rsp_from_tss(env, dpl); + sa.sp_mask = -1; + sa.ss_base = 0; /* SS base is always zero in IA-32e mode */ LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]=" - TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]); + TARGET_FMT_lx "\n", ss, sa.sp, env->regs[R_ESP]); } else #endif { @@ -1715,7 +1813,6 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" TARGET_FMT_lx "\n", ss, sp32, param_count, env->regs[R_ESP]); - sp = sp32; if ((ss & 0xfffc) == 0) { raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } @@ -1738,63 +1835,65 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); } - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); + sa.sp = sp32; + sa.sp_mask = get_sp_mask(ss_e2); + sa.ss_base = get_seg_base(ss_e1, ss_e2); } /* push_size = ((param_count * 2) + 8) << shift; */ - old_sp_mask = get_sp_mask(env->segs[R_SS].flags); old_ssp = env->segs[R_SS].base; + #ifdef TARGET_X86_64 if (shift == 2) { /* XXX: verify if new stack address is canonical */ - PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC()); - PUSHQ_RA(sp, env->regs[R_ESP], GETPC()); + pushq(&sa, env->segs[R_SS].selector); + pushq(&sa, env->regs[R_ESP]); /* parameters aren't supported for 64-bit call gates */ } else #endif if (shift == 1) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); + pushl(&sa, env->segs[R_SS].selector); + pushl(&sa, env->regs[R_ESP]); for (i = param_count - 1; i >= 0; i--) { - val = cpu_ldl_kernel_ra(env, old_ssp + - ((env->regs[R_ESP] + i * 4) & - old_sp_mask), GETPC()); - PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); + val = cpu_ldl_data_ra(env, + old_ssp + ((env->regs[R_ESP] + i * 4) & old_sp_mask), + GETPC()); + pushl(&sa, val); } } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); + pushw(&sa, env->segs[R_SS].selector); + pushw(&sa, env->regs[R_ESP]); for (i = param_count - 1; i >= 0; i--) { - val = cpu_lduw_kernel_ra(env, old_ssp + - ((env->regs[R_ESP] + i * 2) & - old_sp_mask), GETPC()); - PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); + val = cpu_lduw_data_ra(env, + old_ssp + ((env->regs[R_ESP] + i * 2) & old_sp_mask), + GETPC()); + pushw(&sa, val); } } new_stack = 1; } else { /* to same privilege */ - sp = env->regs[R_ESP]; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; + sa.mmu_index = x86_mmu_index_pl(env, cpl); + sa.sp = env->regs[R_ESP]; + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.ss_base = env->segs[R_SS].base; /* push_size = (4 << shift); */ new_stack = 0; } #ifdef TARGET_X86_64 if (shift == 2) { - PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC()); - PUSHQ_RA(sp, next_eip, GETPC()); + pushq(&sa, env->segs[R_CS].selector); + pushq(&sa, next_eip); } else #endif if (shift == 1) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushl(&sa, env->segs[R_CS].selector); + pushl(&sa, next_eip); } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); + pushw(&sa, env->segs[R_CS].selector); + pushw(&sa, next_eip); } /* from this point, not restartable */ @@ -1808,7 +1907,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, { ss = (ss & ~3) | dpl; cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, + sa.ss_base, get_seg_limit(ss_e1, ss_e2), ss_e2); } @@ -1819,7 +1918,7 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); - SET_ESP(sp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->eip = offset; } } @@ -1827,26 +1926,29 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* real and vm86 mode iret */ void helper_iret_real(CPUX86State *env, int shift) { - uint32_t sp, new_cs, new_eip, new_eflags, sp_mask; - target_ulong ssp; + uint32_t new_cs, new_eip, new_eflags; int eflags_mask; + StackAccess sa; + + sa.env = env; + sa.ra = GETPC(); + sa.mmu_index = x86_mmu_index_pl(env, 0); + sa.sp_mask = 0xffff; /* XXXX: use SS segment size? */ + sa.sp = env->regs[R_ESP]; + sa.ss_base = env->segs[R_SS].base; - sp_mask = 0xffff; /* XXXX: use SS segment size? */ - sp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; if (shift == 1) { /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_eip, GETPC()); - POPL_RA(ssp, sp, sp_mask, new_cs, GETPC()); - new_cs &= 0xffff; - POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC()); + new_eip = popl(&sa); + new_cs = popl(&sa) & 0xffff; + new_eflags = popl(&sa); } else { /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_eip, GETPC()); - POPW_RA(ssp, sp, sp_mask, new_cs, GETPC()); - POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC()); + new_eip = popw(&sa); + new_cs = popw(&sa); + new_eflags = popw(&sa); } - env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->segs[R_CS].selector = new_cs; env->segs[R_CS].base = (new_cs << 4); env->eip = new_eip; @@ -1899,47 +2001,52 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, uint32_t new_es, new_ds, new_fs, new_gs; uint32_t e1, e2, ss_e1, ss_e2; int cpl, dpl, rpl, eflags_mask, iopl; - target_ulong ssp, sp, new_eip, new_esp, sp_mask; + target_ulong new_eip, new_esp; + StackAccess sa; + + cpl = env->hflags & HF_CPL_MASK; + + sa.env = env; + sa.ra = retaddr; + sa.mmu_index = x86_mmu_index_pl(env, cpl); #ifdef TARGET_X86_64 if (shift == 2) { - sp_mask = -1; + sa.sp_mask = -1; } else #endif { - sp_mask = get_sp_mask(env->segs[R_SS].flags); + sa.sp_mask = get_sp_mask(env->segs[R_SS].flags); } - sp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; + sa.sp = env->regs[R_ESP]; + sa.ss_base = env->segs[R_SS].base; new_eflags = 0; /* avoid warning */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ_RA(sp, new_eip, retaddr); - POPQ_RA(sp, new_cs, retaddr); - new_cs &= 0xffff; + new_eip = popq(&sa); + new_cs = popq(&sa) & 0xffff; if (is_iret) { - POPQ_RA(sp, new_eflags, retaddr); + new_eflags = popq(&sa); } } else #endif { if (shift == 1) { /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_eip, retaddr); - POPL_RA(ssp, sp, sp_mask, new_cs, retaddr); - new_cs &= 0xffff; + new_eip = popl(&sa); + new_cs = popl(&sa) & 0xffff; if (is_iret) { - POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr); + new_eflags = popl(&sa); if (new_eflags & VM_MASK) { goto return_to_vm86; } } } else { /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_eip, retaddr); - POPW_RA(ssp, sp, sp_mask, new_cs, retaddr); + new_eip = popw(&sa); + new_cs = popw(&sa); if (is_iret) { - POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr); + new_eflags = popw(&sa); } } } @@ -1956,7 +2063,6 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, !(e2 & DESC_CS_MASK)) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); } - cpl = env->hflags & HF_CPL_MASK; rpl = new_cs & 3; if (rpl < cpl) { raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); @@ -1975,7 +2081,7 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr); } - sp += addend; + sa.sp += addend; if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || ((env->hflags & HF_CS64_MASK) && !is_iret))) { /* return to same privilege level */ @@ -1987,21 +2093,19 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, /* return to different privilege level */ #ifdef TARGET_X86_64 if (shift == 2) { - POPQ_RA(sp, new_esp, retaddr); - POPQ_RA(sp, new_ss, retaddr); - new_ss &= 0xffff; + new_esp = popq(&sa); + new_ss = popq(&sa) & 0xffff; } else #endif { if (shift == 1) { /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); - new_ss &= 0xffff; + new_esp = popl(&sa); + new_ss = popl(&sa) & 0xffff; } else { /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPW_RA(ssp, sp, sp_mask, new_ss, retaddr); + new_esp = popw(&sa); + new_ss = popw(&sa); } } LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", @@ -2051,14 +2155,14 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, get_seg_base(e1, e2), get_seg_limit(e1, e2), e2); - sp = new_esp; + sa.sp = new_esp; #ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { - sp_mask = -1; + sa.sp_mask = -1; } else #endif { - sp_mask = get_sp_mask(ss_e2); + sa.sp_mask = get_sp_mask(ss_e2); } /* validate data segments */ @@ -2067,9 +2171,9 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, validate_seg(env, R_FS, rpl); validate_seg(env, R_GS, rpl); - sp += addend; + sa.sp += addend; } - SET_ESP(sp, sp_mask); + SET_ESP(sa.sp, sa.sp_mask); env->eip = new_eip; if (is_iret) { /* NOTE: 'cpl' is the _old_ CPL */ @@ -2089,12 +2193,12 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, return; return_to_vm86: - POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); - POPL_RA(ssp, sp, sp_mask, new_es, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ds, retaddr); - POPL_RA(ssp, sp, sp_mask, new_fs, retaddr); - POPL_RA(ssp, sp, sp_mask, new_gs, retaddr); + new_esp = popl(&sa); + new_ss = popl(&sa); + new_es = popl(&sa); + new_ds = popl(&sa); + new_fs = popl(&sa); + new_gs = popl(&sa); /* modify processor state */ cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | @@ -2222,11 +2326,11 @@ void helper_sysexit(CPUX86State *env, int dflag) target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) { unsigned int limit; - uint32_t e1, e2, eflags, selector; + uint32_t e1, e2, selector; int rpl, dpl, cpl, type; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); + assert(CC_OP == CC_OP_EFLAGS); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2258,22 +2362,22 @@ target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) } if (dpl < cpl || dpl < rpl) { fail: - CC_SRC = eflags & ~CC_Z; + CC_SRC &= ~CC_Z; return 0; } } limit = get_seg_limit(e1, e2); - CC_SRC = eflags | CC_Z; + CC_SRC |= CC_Z; return limit; } target_ulong helper_lar(CPUX86State *env, target_ulong selector1) { - uint32_t e1, e2, eflags, selector; + uint32_t e1, e2, selector; int rpl, dpl, cpl, type; selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); + assert(CC_OP == CC_OP_EFLAGS); if ((selector & 0xfffc) == 0) { goto fail; } @@ -2308,11 +2412,11 @@ target_ulong helper_lar(CPUX86State *env, target_ulong selector1) } if (dpl < cpl || dpl < rpl) { fail: - CC_SRC = eflags & ~CC_Z; + CC_SRC &= ~CC_Z; return 0; } } - CC_SRC = eflags | CC_Z; + CC_SRC |= CC_Z; return e2 & 0x00f0ff00; } @@ -2322,7 +2426,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) | CC_Z; if ((selector & 0xfffc) == 0) { goto fail; } @@ -2347,11 +2451,11 @@ void helper_verr(CPUX86State *env, target_ulong selector1) } else { if (dpl < cpl || dpl < rpl) { fail: - CC_SRC = eflags & ~CC_Z; - return; + eflags &= ~CC_Z; } } - CC_SRC = eflags | CC_Z; + CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } void helper_verw(CPUX86State *env, target_ulong selector1) @@ -2360,7 +2464,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) | CC_Z; if ((selector & 0xfffc) == 0) { goto fail; } @@ -2381,9 +2485,9 @@ void helper_verw(CPUX86State *env, target_ulong selector1) } if (!(e2 & DESC_W_MASK)) { fail: - CC_SRC = eflags & ~CC_Z; - return; + eflags &= ~CC_Z; } } - CC_SRC = eflags | CC_Z; + CC_SRC = eflags; + CC_OP = CC_OP_EFLAGS; } diff --git a/target/i386/tcg/sysemu/bpt_helper.c b/target/i386/tcg/sysemu/bpt_helper.c index 4d96a48a3c..b29acf41c3 100644 --- a/target/i386/tcg/sysemu/bpt_helper.c +++ b/target/i386/tcg/sysemu/bpt_helper.c @@ -215,6 +215,12 @@ void breakpoint_handler(CPUState *cs) if (cs->watchpoint_hit->flags & BP_CPU) { cs->watchpoint_hit = NULL; if (check_hw_breakpoints(env, false)) { + /* + * FIXME: #DB should be delayed by one instruction if + * INHIBIT_IRQ is set (STI cannot trigger a watchpoint). + * The delayed #DB should also fuse with one generated + * by ICEBP (aka INT1). + */ raise_exception(env, EXCP01_DB); } else { cpu_loop_exit_noexc(cs); @@ -238,6 +244,12 @@ target_ulong helper_get_dr(CPUX86State *env, int reg) } } + if (env->dr[7] & DR7_GD) { + env->dr[7] &= ~DR7_GD; + env->dr[6] |= DR6_BD; + raise_exception_ra(env, EXCP01_DB, GETPC()); + } + return env->dr[reg]; } @@ -251,6 +263,12 @@ void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) } } + if (env->dr[7] & DR7_GD) { + env->dr[7] &= ~DR7_GD; + env->dr[6] |= DR6_BD; + raise_exception_ra(env, EXCP01_DB, GETPC()); + } + if (reg < 4) { if (hw_breakpoint_enabled(env->dr[7], reg) && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { diff --git a/target/i386/tcg/sysemu/excp_helper.c b/target/i386/tcg/sysemu/excp_helper.c index 55bd1194d3..b1f40040f8 100644 --- a/target/i386/tcg/sysemu/excp_helper.c +++ b/target/i386/tcg/sysemu/excp_helper.c @@ -19,7 +19,9 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/cpu_ldst.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { @@ -60,12 +62,11 @@ typedef struct PTETranslate { static bool ptw_translate(PTETranslate *inout, hwaddr addr) { - CPUTLBEntryFull *full; int flags; inout->gaddr = addr; - flags = probe_access_full(inout->env, addr, MMU_DATA_STORE, - inout->ptw_idx, true, &inout->haddr, &full, 0); + flags = probe_access_full_mmu(inout->env, addr, 0, MMU_DATA_STORE, + inout->ptw_idx, &inout->haddr, NULL); if (unlikely(flags & TLB_INVALID_MASK)) { TranslateFault *err = inout->err; @@ -81,20 +82,20 @@ static bool ptw_translate(PTETranslate *inout, hwaddr addr) return true; } -static inline uint32_t ptw_ldl(const PTETranslate *in) +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, 0); + return cpu_ldl_mmuidx_ra(in->env, in->gaddr, in->ptw_idx, ra); } -static inline uint64_t ptw_ldq(const PTETranslate *in) +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, 0); + return cpu_ldq_mmuidx_ra(in->env, in->gaddr, in->ptw_idx, ra); } /* @@ -106,6 +107,10 @@ static bool ptw_setl_slow(const PTETranslate *in, uint32_t old, uint32_t new) { uint32_t cmp; + CPUState *cpu = env_cpu(in->env); + /* We are in cpu_exec, and start_exclusive can't be called directly.*/ + g_assert(cpu->running); + cpu_exec_end(cpu); /* 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); @@ -113,6 +118,7 @@ static bool ptw_setl_slow(const PTETranslate *in, uint32_t old, uint32_t new) cpu_stl_mmuidx_ra(in->env, in->gaddr, new, in->ptw_idx, 0); } end_exclusive(); + cpu_exec_start(cpu); return cmp == old; } @@ -131,12 +137,12 @@ static inline bool ptw_setl(const PTETranslate *in, uint32_t old, uint32_t set) } static bool mmu_translate(CPUX86State *env, const TranslateParams *in, - TranslateResult *out, TranslateFault *err) + TranslateResult *out, TranslateFault *err, + uint64_t ra) { - const int32_t a20_mask = x86_get_a20_mask(env); const target_ulong addr = in->addr; const int pg_mode = in->pg_mode; - const bool is_user = (in->mmu_idx == MMU_USER_IDX); + 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 = { @@ -147,6 +153,8 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, hwaddr pte_addr, paddr; uint32_t pkr; int page_size; + int error_code; + int prot; restart_all: rsvd_mask = ~MAKE_64BIT_MASK(0, env_archcpu(env)->phys_bits); @@ -162,13 +170,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 5 */ - pte_addr = ((in->cr3 & ~0xfff) + - (((addr >> 48) & 0x1ff) << 3)) & a20_mask; + pte_addr = (in->cr3 & ~0xfff) + (((addr >> 48) & 0x1ff) << 3); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_5: - pte = ptw_ldq(&pte_trans); + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -187,13 +194,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 4 */ - pte_addr = ((pte & PG_ADDRESS_MASK) + - (((addr >> 39) & 0x1ff) << 3)) & a20_mask; + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 39) & 0x1ff) << 3); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_4: - pte = ptw_ldq(&pte_trans); + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -208,13 +214,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 3 */ - pte_addr = ((pte & PG_ADDRESS_MASK) + - (((addr >> 30) & 0x1ff) << 3)) & a20_mask; + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_3_lma: - pte = ptw_ldq(&pte_trans); + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -236,13 +241,13 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 3 */ - pte_addr = ((in->cr3 & ~0x1f) + ((addr >> 27) & 0x18)) & a20_mask; + pte_addr = (in->cr3 & 0xffffffe0ULL) + ((addr >> 27) & 0x18); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } rsvd_mask |= PG_HI_USER_MASK; restart_3_nolma: - pte = ptw_ldq(&pte_trans); + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -258,13 +263,12 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 2 */ - pte_addr = ((pte & PG_ADDRESS_MASK) + - (((addr >> 21) & 0x1ff) << 3)) & a20_mask; + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_2_pae: - pte = ptw_ldq(&pte_trans); + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -285,12 +289,11 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 1 */ - pte_addr = ((pte & PG_ADDRESS_MASK) + - (((addr >> 12) & 0x1ff) << 3)) & a20_mask; + pte_addr = (pte & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } - pte = ptw_ldq(&pte_trans); + pte = ptw_ldq(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -300,16 +303,16 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* combine pde and pte nx, user and rw protections */ ptep &= pte ^ PG_NX_MASK; page_size = 4096; - } else { + } else if (pg_mode & PG_MODE_PG) { /* * Page table level 2 */ - pte_addr = ((in->cr3 & ~0xfff) + ((addr >> 20) & 0xffc)) & a20_mask; + pte_addr = (in->cr3 & 0xfffff000ULL) + ((addr >> 20) & 0xffc); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } restart_2_nopae: - pte = ptw_ldl(&pte_trans); + pte = ptw_ldl(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -333,11 +336,11 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, /* * Page table level 1 */ - pte_addr = ((pte & ~0xfffu) + ((addr >> 10) & 0xffc)) & a20_mask; + pte_addr = (pte & ~0xfffu) + ((addr >> 10) & 0xffc); if (!ptw_translate(&pte_trans, pte_addr)) { return false; } - pte = ptw_ldl(&pte_trans); + pte = ptw_ldl(&pte_trans, ra); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; } @@ -345,6 +348,15 @@ static bool mmu_translate(CPUX86State *env, const TranslateParams *in, ptep &= pte | PG_NX_MASK; page_size = 4096; rsvd_mask = 0; + } else { + /* + * No paging (real mode), let's tentatively resolve the address as 1:1 + * here, but conditionally still perform an NPT walk on it later. + */ + page_size = 0x40000000; + paddr = in->addr; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + goto stage2; } do_check_protect: @@ -360,8 +372,8 @@ do_check_protect_pse36: goto do_fault_protect; } - int prot = 0; - if (in->mmu_idx != MMU_KSMAP_IDX || !(ptep & PG_USER_MASK)) { + 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; @@ -420,17 +432,20 @@ do_check_protect_pse36: } } - /* align to page_size */ - paddr = (pte & a20_mask & PG_ADDRESS_MASK & ~(page_size - 1)) - | (addr & (page_size - 1)); + /* merge offset within page */ + paddr = (pte & PG_ADDRESS_MASK & ~(page_size - 1)) | (addr & (page_size - 1)); + stage2: + /* + * 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, access_type, - MMU_NESTED_IDX, true, - &pte_trans.haddr, &full, 0); + flags = probe_access_full_mmu(env, paddr, 0, access_type, + MMU_NESTED_IDX, &pte_trans.haddr, &full); if (unlikely(flags & TLB_INVALID_MASK)) { *err = (TranslateFault){ .error_code = env->error_code, @@ -462,12 +477,11 @@ do_check_protect_pse36: } } - out->paddr = paddr; + out->paddr = paddr & x86_get_a20_mask(env); out->prot = prot; out->page_size = page_size; return true; - int error_code; do_fault_rsvd: error_code = PG_ERROR_RSVD_MASK; goto do_fault_cont; @@ -528,7 +542,8 @@ static G_NORETURN void raise_stage2(CPUX86State *env, TranslateFault *err, static bool get_physical_address(CPUX86State *env, vaddr addr, MMUAccessType access_type, int mmu_idx, - TranslateResult *out, TranslateFault *err) + TranslateResult *out, TranslateFault *err, + uint64_t ra) { TranslateParams in; bool use_stage2 = env->hflags2 & HF2_NPT_MASK; @@ -544,10 +559,11 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, if (likely(use_stage2)) { in.cr3 = env->nested_cr3; in.pg_mode = env->nested_pg_mode; - in.mmu_idx = MMU_USER_IDX; + 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)) { + if (!mmu_translate(env, &in, out, err, ra)) { err->stage2 = S2_GPA; return false; } @@ -556,7 +572,11 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, break; default: - if (likely(env->cr[0] & CR0_PG_MASK)) { + if (is_mmu_index_32(mmu_idx)) { + addr = (uint32_t)addr; + } + + if (likely(env->cr[0] & CR0_PG_MASK || use_stage2)) { in.cr3 = env->cr[3]; in.mmu_idx = mmu_idx; in.ptw_idx = use_stage2 ? MMU_NESTED_IDX : MMU_PHYS_IDX; @@ -574,19 +594,13 @@ static bool get_physical_address(CPUX86State *env, vaddr addr, return false; } } - return mmu_translate(env, &in, out, err); + return mmu_translate(env, &in, out, err, ra); } break; } - /* Translation disabled. */ + /* No translation needed. */ out->paddr = addr & x86_get_a20_mask(env); -#ifdef TARGET_X86_64 - if (!(env->hflags & HF_LMA_MASK)) { - /* Without long mode we can only address 32bits in real mode */ - out->paddr = (uint32_t)out->paddr; - } -#endif out->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; out->page_size = TARGET_PAGE_SIZE; return true; @@ -596,11 +610,12 @@ bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - CPUX86State *env = cs->env_ptr; + CPUX86State *env = cpu_env(cs); TranslateResult out; TranslateFault err; - if (get_physical_address(env, addr, access_type, mmu_idx, &out, &err)) { + 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. 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 e75b2feebf..d31c6a2c56 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) { @@ -62,23 +63,13 @@ target_ulong helper_inl(CPUX86State *env, uint32_t port) cpu_get_mem_attrs(env), NULL); } -target_ulong helper_read_crN(CPUX86State *env, int reg) +target_ulong helper_read_cr8(CPUX86State *env) { - target_ulong val; - - switch (reg) { - default: - val = env->cr[reg]; - break; - case 8: - if (!(env->hflags2 & HF2_VINTR_MASK)) { - val = cpu_get_apic_tpr(env_archcpu(env)->apic_state); - } else { - val = env->int_ctl & V_TPR_MASK; - } - break; + if (!(env->hflags2 & HF2_VINTR_MASK)) { + return cpu_get_apic_tpr(env_archcpu(env)->apic_state); + } else { + return env->int_ctl & V_TPR_MASK; } - return val; } void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) @@ -118,9 +109,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 +148,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 +202,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 +293,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 + @@ -460,6 +477,19 @@ void helper_rdmsr(CPUX86State *env) 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 + @@ -481,25 +511,16 @@ void helper_flush_page(CPUX86State *env, target_ulong addr) tlb_flush_page(env_cpu(env), addr); } -static G_NORETURN -void do_hlt(CPUX86State *env) +G_NORETURN void helper_hlt(CPUX86State *env) { CPUState *cs = env_cpu(env); - env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ + do_end_instruction(env); cs->halted = 1; cs->exception_index = EXCP_HLT; cpu_loop_exit(cs); } -G_NORETURN void helper_hlt(CPUX86State *env, int next_eip_addend) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0, GETPC()); - env->eip += next_eip_addend; - - do_hlt(env); -} - void helper_monitor(CPUX86State *env, target_ulong ptr) { if ((uint32_t)env->regs[R_ECX] != 0) { @@ -521,8 +542,8 @@ G_NORETURN void helper_mwait(CPUX86State *env, int next_eip_addend) /* XXX: not complete but not completely erroneous */ if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) { - do_pause(env); + helper_pause(env); } else { - do_hlt(env); + helper_hlt(env); } } diff --git a/target/i386/tcg/sysemu/seg_helper.c b/target/i386/tcg/sysemu/seg_helper.c index 2c9bd007ad..05174a79e7 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,40 @@ void x86_cpu_do_interrupt(CPUState *cs) } } +bool x86_cpu_exec_halt(CPUState *cpu) +{ + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + + if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + bql_lock(); + apic_poll_irq(x86_cpu->apic_state); + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); + bql_unlock(); + } + + if (!cpu_has_work(cpu)) { + return false; + } + + /* Complete HLT instruction. */ + if (env->eflags & TF_MASK) { + env->dr[6] |= DR6_BS; + do_interrupt_all(x86_cpu, EXCP01_DB, 0, 0, env->eip, 0); + } + return true; +} + +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 2d27731b60..9db8ad62a0 100644 --- a/target/i386/tcg/sysemu/svm_helper.c +++ b/target/i386/tcg/sysemu/svm_helper.c @@ -163,8 +163,8 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) uint64_t new_cr0; uint64_t new_cr3; uint64_t new_cr4; - - cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); + uint64_t new_dr6; + uint64_t new_dr7; if (aflag == 2) { addr = env->regs[R_EAX]; @@ -172,6 +172,13 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) 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; @@ -247,6 +254,13 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) control.intercept_exceptions )); + env->hflags &= ~HF_INHIBIT_IRQ_MASK; + if (x86_ldl_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.int_state)) & + SVM_INTERRUPT_SHADOW_MASK) { + env->hflags |= HF_INHIBIT_IRQ_MASK; + } + nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.nested_ctl)); asid = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, @@ -356,20 +370,22 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->vm_vmcb + offsetof(struct vmcb, save.rsp)); env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rax)); - env->dr[7] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr7)); - env->dr[6] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr6)); + + new_dr7 = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr7)); + new_dr6 = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.dr6)); #ifdef TARGET_X86_64 - if (env->dr[6] & DR_RESERVED_MASK) { + if (new_dr7 & DR_RESERVED_MASK) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } - if (env->dr[7] & DR_RESERVED_MASK) { + if (new_dr6 & DR_RESERVED_MASK) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } #endif + cpu_x86_update_dr7(env, new_dr7); + env->dr[6] = new_dr6; + if (is_efer_invalid_state(env)) { cpu_vmexit(env, SVM_EXIT_ERR, 0, GETPC()); } @@ -387,8 +403,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; } @@ -465,14 +479,19 @@ void helper_vmload(CPUX86State *env, int aflag) int mmu_idx = MMU_PHYS_IDX; target_ulong addr; - cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 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_VMLOAD, 0, GETPC()); + if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMLOAD, GETPC())) { mmu_idx = MMU_NESTED_IDX; } @@ -521,14 +540,19 @@ void helper_vmsave(CPUX86State *env, int aflag) int mmu_idx = MMU_PHYS_IDX; target_ulong addr; - cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 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_VMSAVE, 0, GETPC()); + if (virtual_vm_load_save_enabled(env, SVM_EXIT_VMSAVE, GETPC())) { mmu_idx = MMU_NESTED_IDX; } @@ -798,8 +822,12 @@ void do_vmexit(CPUX86State *env) env->hflags &= ~HF_GUEST_MASK; env->intercept = 0; env->intercept_exceptions = 0; + + /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; env->int_ctl = 0; + + /* Clears the TSC_OFFSET inside the processor. */ env->tsc_offset = 0; env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, @@ -819,6 +847,15 @@ void do_vmexit(CPUX86State *env) cpu_x86_update_cr4(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr4))); + + /* + * Resets the current ASID register to zero (host ASID; TLB flush). + * + * If the host is in PAE mode, the processor reloads the host's PDPEs + * from the page table indicated the host's CR3. FIXME: If the PDPEs + * contain illegal state, the processor causes a shutdown (QEMU does + * not implement PDPTRs). + */ cpu_x86_update_cr3(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.cr3))); @@ -826,12 +863,14 @@ void do_vmexit(CPUX86State *env) set properly */ cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.efer))); + + /* Completion of the VMRUN instruction clears the host EFLAGS.RF bit. */ env->eflags = 0; cpu_load_eflags(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rflags)), ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | - VM_MASK)); + RF_MASK | VM_MASK)); svm_load_seg_cache(env, MMU_PHYS_IDX, env->vm_hsave + offsetof(struct vmcb, save.es), R_ES); @@ -851,8 +890,11 @@ void do_vmexit(CPUX86State *env) env->dr[6] = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.dr6)); - env->dr[7] = x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.dr7)); + + /* Disables all breakpoints in the host DR7 register. */ + cpu_x86_update_dr7(env, + x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.dr7)) & ~0xff); /* other setups */ x86_stl_phys(cs, @@ -868,21 +910,17 @@ void do_vmexit(CPUX86State *env) env->hflags2 &= ~HF2_GIF_MASK; env->hflags2 &= ~HF2_VGIF_MASK; - /* FIXME: Resets the current ASID register to zero (host ASID). */ - /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ - /* Clears the TSC_OFFSET inside the processor. */ + /* FIXME: Checks the reloaded host state for consistency. */ - /* If the host is in PAE mode, the processor reloads the host's PDPEs - from the page table indicated the host's CR3. If the PDPEs contain - illegal state, the processor causes a shutdown. */ - - /* Disables all breakpoints in the host DR7 register. */ - - /* Checks the reloaded host state for consistency. */ - - /* If the host's rIP reloaded by #VMEXIT is outside the limit of the - host's code segment or non-canonical (in the case of long mode), a - #GP fault is delivered inside the host. */ + /* + * EFLAGS.TF causes a #DB trap after the VMRUN completes on the host + * side (i.e., after the #VMEXIT from the guest). Since we're running + * in the main loop, call do_interrupt_all directly. + */ + if ((env->eflags & TF_MASK) != 0) { + env->dr[6] |= DR6_BS; + do_interrupt_all(X86_CPU(cs), EXCP01_DB, 0, 0, env->eip, 0); + } } diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 79ac5908f7..cca19cd40e 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -49,10 +49,15 @@ static void x86_cpu_exec_exit(CPUState *cs) static void x86_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { - /* The instruction pointer is always up to date with TARGET_TB_PCREL. */ - if (!TARGET_TB_PCREL) { - CPUX86State *env = cs->env_ptr; - env->eip = tb_pc(tb) - tb->cs_base; + /* The instruction pointer is always up to date with CF_PCREL. */ + if (!(tb_cflags(tb) & CF_PCREL)) { + CPUX86State *env = cpu_env(cs); + + if (tb->flags & HF_CS64_MASK) { + env->eip = tb->pc; + } else { + env->eip = (uint32_t)(tb->pc - tb->cs_base); + } } } @@ -63,12 +68,26 @@ static void x86_restore_state_to_opc(CPUState *cs, X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; int cc_op = data[1]; + uint64_t new_pc; - if (TARGET_TB_PCREL) { - env->eip = (env->eip & TARGET_PAGE_MASK) | data[0]; + 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 { - env->eip = data[0] - tb->cs_base; + 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; } @@ -87,7 +106,7 @@ 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, @@ -100,25 +119,27 @@ static const struct TCGCPUOps x86_tcg_ops = { #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); @@ -140,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 a9dbd5cf85..ca089c8b28 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -20,17 +20,16 @@ #include "qemu/host-utils.h" #include "cpu.h" -#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" #include "helper-tcg.h" +#include "decode-new.h" #include "exec/log.h" @@ -122,6 +121,14 @@ static int g_use_hard_fpu; #define gen_helper_frstor MAP_GEN_HELPER_SOFT_HARD(frstor) #endif /* defined(XBOX) && defined(__x86_64__) */ +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H + +/* Fixes for Windows namespace pollution. */ +#undef IN +#undef OUT + #define PREFIX_REPZ 0x01 #define PREFIX_REPNZ 0x02 #define PREFIX_LOCK 0x04 @@ -162,9 +169,6 @@ static TCGv_i64 cpu_bndl[4]; static TCGv_i64 cpu_bndu[4]; static TCGv_i32 fpstt; - -#include "exec/gen-icount.h" - typedef struct TCGv_fp_d *TCGv_fp; typedef struct DisasContext { @@ -211,6 +215,7 @@ typedef struct DisasContext { 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 */ @@ -227,6 +232,7 @@ typedef struct DisasContext { TCGv_i64 tmp1_i64; sigjmp_buf jmpbuf; + TCGOp *prev_insn_start; TCGOp *prev_insn_end; /* Floating point */ @@ -236,11 +242,36 @@ typedef struct DisasContext { TCGv_fp ft0; } DisasContext; -#define DISAS_EOB_ONLY DISAS_TARGET_0 -#define DISAS_EOB_NEXT DISAS_TARGET_1 -#define DISAS_EOB_INHIBIT_IRQ DISAS_TARGET_2 +/* + * Point EIP to next instruction before ending translation. + * For instructions that can change hflags. + */ +#define DISAS_EOB_NEXT DISAS_TARGET_0 + +/* + * Point EIP to next instruction and set HF_INHIBIT_IRQ if not + * already set. For instructions that activate interrupt shadow. + */ +#define DISAS_EOB_INHIBIT_IRQ DISAS_TARGET_1 + +/* + * Return to the main loop; EIP might have already been updated + * but even in that case do not use lookup_and_goto_ptr(). + */ +#define DISAS_EOB_ONLY DISAS_TARGET_2 + +/* + * EIP has already been updated. For jumps that wish to use + * lookup_and_goto_ptr() + */ #define DISAS_JUMP DISAS_TARGET_3 +/* + * EIP has already been updated. Use updated value of + * EFLAGS.TF to determine singlestep trap (SYSCALL/SYSRET). + */ +#define DISAS_EOB_RECHECK_TF DISAS_TARGET_4 + /* The environment in which user-only runs is constrained. */ #ifdef CONFIG_USER_ONLY #define PE(S) true @@ -268,13 +299,15 @@ 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 @@ -303,7 +336,6 @@ typedef struct DisasContext { #ifdef CONFIG_USER_ONLY STUB_HELPER(clgi, TCGv_env env) STUB_HELPER(flush_page, TCGv_env env, TCGv addr) -STUB_HELPER(hlt, TCGv_env env, TCGv_i32 pc_ofs) STUB_HELPER(inb, TCGv ret, TCGv_env env, TCGv_i32 port) STUB_HELPER(inw, TCGv ret, TCGv_env env, TCGv_i32 port) STUB_HELPER(inl, TCGv ret, TCGv_env env, TCGv_i32 port) @@ -312,10 +344,6 @@ STUB_HELPER(mwait, TCGv_env env, TCGv_i32 pc_ofs) STUB_HELPER(outb, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outw, TCGv_env env, TCGv_i32 port, TCGv_i32 val) STUB_HELPER(outl, TCGv_env env, TCGv_i32 port, TCGv_i32 val) -STUB_HELPER(rdmsr, TCGv_env env) -STUB_HELPER(read_crN, TCGv ret, TCGv_env env, TCGv_i32 reg) -STUB_HELPER(get_dr, TCGv ret, TCGv_env env, TCGv_i32 reg) -STUB_HELPER(set_dr, TCGv_env env, TCGv_i32 reg, TCGv val) STUB_HELPER(stgi, TCGv_env env) STUB_HELPER(svm_check_intercept, TCGv_env env, TCGv_i32 type) STUB_HELPER(vmload, TCGv_env env, TCGv_i32 aflag) @@ -323,28 +351,12 @@ STUB_HELPER(vmmcall, TCGv_env env) STUB_HELPER(vmrun, TCGv_env env, TCGv_i32 aflag, TCGv_i32 pc_ofs) STUB_HELPER(vmsave, TCGv_env env, TCGv_i32 aflag) STUB_HELPER(write_crN, TCGv_env env, TCGv_i32 reg, TCGv val) -STUB_HELPER(wrmsr, TCGv_env env) #endif -static void gen_eob(DisasContext *s); -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); -/* i386 arith/logic operations */ -enum { - OP_ADDL, - OP_ORL, - OP_ADCL, - OP_SBBL, - OP_ANDL, - OP_SUBL, - OP_XORL, - OP_CMPL, -}; - /* i386 shift ops */ enum { OP_ROL, @@ -368,22 +380,6 @@ enum { JCC_LE, }; -enum { - /* I386 int registers */ - OR_EAX, /* MUST be even numbered */ - OR_ECX, - OR_EDX, - OR_EBX, - OR_ESP, - OR_EBP, - OR_ESI, - OR_EDI, - - OR_TMP0 = 16, /* temporary operand register */ - OR_TMP1, - OR_A0, /* temporary register used when doing address evaluation */ -}; - enum { USES_CC_DST = 1, USES_CC_SRC = 2, @@ -392,7 +388,7 @@ enum { }; /* Bit set if the global variable is live after setting CC_OP to X. */ -static const uint8_t cc_op_live[CC_OP_NB] = { +static const uint8_t cc_op_live_[] = { [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, [CC_OP_EFLAGS] = USES_CC_SRC, [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC, @@ -406,14 +402,29 @@ static const uint8_t cc_op_live[CC_OP_NB] = { [CC_OP_SHLB ... CC_OP_SHLQ] = USES_CC_DST | USES_CC_SRC, [CC_OP_SARB ... CC_OP_SARQ] = USES_CC_DST | USES_CC_SRC, [CC_OP_BMILGB ... CC_OP_BMILGQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_BLSIB ... CC_OP_BLSIQ] = USES_CC_DST | USES_CC_SRC, [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC, [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, - [CC_OP_CLR] = 0, - [CC_OP_POPCNT] = USES_CC_SRC, + [CC_OP_POPCNT] = USES_CC_DST, }; -static void set_cc_op(DisasContext *s, CCOp op) +static uint8_t cc_op_live(CCOp op) +{ + uint8_t result; + assert(op >= 0 && op < ARRAY_SIZE(cc_op_live_)); + + /* + * Check that the array is fully populated. A zero entry would correspond + * to a fixed value of EFLAGS, which can be obtained with CC_OP_EFLAGS + * as well. + */ + result = cc_op_live_[op]; + assert(result); + return result; +} + +static void set_cc_op_1(DisasContext *s, CCOp op, bool dirty) { int dead; @@ -422,7 +433,7 @@ static void set_cc_op(DisasContext *s, CCOp op) } /* Discard CC computation that will no longer be used. */ - dead = cc_op_live[s->cc_op] & ~cc_op_live[op]; + dead = cc_op_live(s->cc_op) & ~cc_op_live(op); if (dead & USES_CC_DST) { tcg_gen_discard_tl(cpu_cc_dst); } @@ -436,20 +447,27 @@ static void set_cc_op(DisasContext *s, CCOp op) tcg_gen_discard_tl(s->cc_srcT); } - if (op == CC_OP_DYNAMIC) { - /* The DYNAMIC setting is translator only, and should never be - stored. Thus we always consider it clean. */ - s->cc_op_dirty = false; - } else { - /* Discard any computed CC_OP value (see shifts). */ - if (s->cc_op == CC_OP_DYNAMIC) { - tcg_gen_discard_i32(cpu_cc_op); - } - s->cc_op_dirty = true; + if (dirty && s->cc_op == CC_OP_DYNAMIC) { + tcg_gen_discard_i32(cpu_cc_op); } + s->cc_op_dirty = dirty; s->cc_op = op; } +static void set_cc_op(DisasContext *s, CCOp op) +{ + /* + * The DYNAMIC setting is translator only, everything else + * will be spilled later. + */ + set_cc_op_1(s, op, op != CC_OP_DYNAMIC); +} + +static void assume_cc_op(DisasContext *s, CCOp op) +{ + set_cc_op_1(s, op, false); +} + static void gen_update_cc_op(DisasContext *s) { if (s->cc_op_dirty) { @@ -513,30 +531,6 @@ static inline MemOp mo_stacksize(DisasContext *s) return CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16; } -/* Select only size 64 else 32. Used for SSE operand sizes. */ -static inline MemOp mo_64_32(MemOp ot) -{ -#ifdef TARGET_X86_64 - return ot == MO_64 ? MO_64 : MO_32; -#else - return MO_32; -#endif -} - -/* Select size 8 if lsb of B is clear, else OT. Used for decoding - byte vs word opcodes. */ -static inline MemOp mo_b_d(int b, MemOp ot) -{ - return b & 1 ? ot : MO_8; -} - -/* Select size 8 if lsb of B is clear, else OT capped at 32. - Used for decoding operand size of port opcodes. */ -static inline MemOp mo_b_d32(int b, MemOp ot) -{ - return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; -} - /* 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 @@ -574,7 +568,7 @@ static TCGv gen_op_deposit_reg_v(DisasContext *s, MemOp ot, int reg, TCGv dest, break; #endif default: - tcg_abort(); + g_assert_not_reached(); } return cpu_regs[reg]; } @@ -615,9 +609,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); } @@ -631,37 +625,32 @@ static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) tcg_gen_qemu_st_tl(t0, a0, s->mem_index, idx | MO_LE); } -static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) +static void gen_update_eip_next(DisasContext *s) { - if (d == OR_TMP0) { - gen_op_st_v(s, idx, s->T0, s->A0); + 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 { - gen_op_mov_reg_v(s, idx, d, s->T0); + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->pc - s->cs_base)); } + s->pc_save = s->pc; } static void gen_update_eip_cur(DisasContext *s) { assert(s->pc_save != -1); - if (TARGET_TB_PCREL) { + 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, s->base.pc_next - s->cs_base); + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); } s->pc_save = s->base.pc_next; } -static void gen_update_eip_next(DisasContext *s) -{ - assert(s->pc_save != -1); - if (TARGET_TB_PCREL) { - tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); - } else { - tcg_gen_movi_tl(cpu_eip, s->pc - s->cs_base); - } - s->pc_save = s->pc; -} - static int cur_insn_len(DisasContext *s) { return s->pc - s->base.pc_next; @@ -686,7 +675,7 @@ static TCGv_i32 eip_next_i32(DisasContext *s) if (CODE64(s)) { return tcg_constant_i32(-1); } - if (TARGET_TB_PCREL) { + 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); @@ -699,38 +688,42 @@ static TCGv_i32 eip_next_i32(DisasContext *s) static TCGv eip_next_tl(DisasContext *s) { assert(s->pc_save != -1); - if (TARGET_TB_PCREL) { + 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(s->pc - s->cs_base); + return tcg_constant_tl((uint32_t)(s->pc - s->cs_base)); } } static TCGv eip_cur_tl(DisasContext *s) { assert(s->pc_save != -1); - if (TARGET_TB_PCREL) { + 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(s->base.pc_next - s->cs_base); + return tcg_constant_tl((uint32_t)(s->base.pc_next - s->cs_base)); } } -/* Compute SEG:REG into A0. SEG is selected from the override segment +/* 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; @@ -741,14 +734,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; @@ -758,86 +751,65 @@ 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, TCGv a0, + int def_seg, int ovr_seg) +{ + gen_lea_v_seg_dest(s, 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); + gen_lea_v_seg(s, cpu_regs[R_ESI], R_DS, s->override); } static inline void gen_string_movl_A0_EDI(DisasContext *s) { - gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); + gen_lea_v_seg(s, 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; } -} - -static void gen_extu(MemOp ot, TCGv reg) -{ - gen_ext_tl(reg, reg, ot, false); -} - -static void gen_exts(MemOp ot, TCGv reg) -{ - gen_ext_tl(reg, reg, ot, true); + if (!dst) { + dst = tcg_temp_new(); + } + tcg_gen_ext_tl(dst, src, size | (sign ? MO_SIGN : 0)); + return dst; } static void gen_op_j_ecx(DisasContext *s, TCGCond cond, TCGLabel *label1) { - tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); - gen_extu(s->aflag, s->tmp0); - tcg_gen_brcondi_tl(cond, 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, TCGLabel *label1) @@ -854,16 +826,16 @@ 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(); } } @@ -871,16 +843,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(); } } @@ -900,7 +872,7 @@ 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)) { gen_update_cc_op(s); @@ -909,7 +881,7 @@ static bool gen_check_io(DisasContext *s, MemOp ot, TCGv_i32 port, 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), cur_insn_len_i32(s)); } @@ -919,70 +891,39 @@ static bool gen_check_io(DisasContext *s, MemOp ot, TCGv_i32 port, 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) +/* compute all eflags to reg */ +static void gen_mov_eflags(DisasContext *s, TCGv reg) { - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static void gen_op_update2_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_src, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static void gen_op_update3_cc(DisasContext *s, TCGv reg) -{ - tcg_gen_mov_tl(cpu_cc_src2, reg); - tcg_gen_mov_tl(cpu_cc_src, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static inline void gen_op_testl_T0_T1_cc(DisasContext *s) -{ - tcg_gen_and_tl(cpu_cc_dst, s->T0, s->T1); -} - -static void gen_op_update_neg_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_neg_tl(cpu_cc_src, s->T0); - tcg_gen_movi_tl(s->cc_srcT, 0); -} - -/* compute all eflags to cc_src */ -static void gen_compute_eflags(DisasContext *s) -{ - TCGv zero, dst, src1, src2; + TCGv dst, src1, src2; + TCGv_i32 cc_op; int live, dead; if (s->cc_op == CC_OP_EFLAGS) { - 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_mov_tl(reg, cpu_cc_src); return; } - zero = NULL; dst = cpu_cc_dst; src1 = cpu_cc_src; src2 = cpu_cc_src2; /* Take care to not read values that are not live. */ - live = cc_op_live[s->cc_op] & ~USES_CC_SRCT; + 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; } @@ -994,13 +935,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 { @@ -1008,94 +955,114 @@ typedef struct CCPrepare { TCGv reg; TCGv reg2; target_ulong imm; - target_ulong mask; bool use_reg2; bool no_setcond; } CCPrepare; -/* compute eflags.C to reg */ +static CCPrepare gen_prepare_sign_nz(TCGv src, MemOp size) +{ + if (size == MO_TL) { + return (CCPrepare) { .cond = TCG_COND_LT, .reg = src }; + } else { + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = src, + .imm = 1ull << ((8 << size) - 1) }; + } +} + +static CCPrepare gen_prepare_val_nz(TCGv src, MemOp size, bool eqz) +{ + if (size == MO_TL) { + return (CCPrepare) { .cond = eqz ? TCG_COND_EQ : TCG_COND_NE, + .reg = src }; + } else { + return (CCPrepare) { .cond = eqz ? TCG_COND_TSTEQ : TCG_COND_TSTNE, + .imm = MAKE_64BIT_MASK(0, 8 << size), + .reg = src }; + } +} + +/* compute eflags.C, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) { - TCGv t0, t1; - int size, shift; + MemOp size; switch (s->cc_op) { case CC_OP_SUBB ... CC_OP_SUBQ: /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ size = s->cc_op - CC_OP_SUBB; - t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - /* If no temporary was used, be careful not to alias t1 and t0. */ - t0 = t1 == cpu_cc_src ? s->tmp0 : reg; - tcg_gen_mov_tl(t0, s->cc_srcT); - gen_extu(size, t0); - goto add_sub; + tcg_gen_ext_tl(s->cc_srcT, s->cc_srcT, size); + tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size); + return (CCPrepare) { .cond = TCG_COND_LTU, .reg = s->cc_srcT, + .reg2 = cpu_cc_src, .use_reg2 = true }; case CC_OP_ADDB ... CC_OP_ADDQ: /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ - size = s->cc_op - CC_OP_ADDB; - t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); - add_sub: - return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, - .reg2 = t1, .mask = -1, .use_reg2 = true }; + size = cc_op_size(s->cc_op); + tcg_gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size); + tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size); + return (CCPrepare) { .cond = TCG_COND_LTU, .reg = cpu_cc_dst, + .reg2 = cpu_cc_src, .use_reg2 = true }; case CC_OP_LOGICB ... CC_OP_LOGICQ: - case CC_OP_CLR: case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NEVER }; case CC_OP_INCB ... CC_OP_INCQ: case CC_OP_DECB ... CC_OP_DECQ: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; case CC_OP_SHLB ... CC_OP_SHLQ: /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ - size = s->cc_op - CC_OP_SHLB; - shift = (8 << size) - 1; - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = (target_ulong)1 << shift }; + size = cc_op_size(s->cc_op); + return gen_prepare_sign_nz(cpu_cc_src, size); case CC_OP_MULB ... CC_OP_MULQ: return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = -1 }; + .reg = cpu_cc_src }; case CC_OP_BMILGB ... CC_OP_BMILGQ: - size = s->cc_op - CC_OP_BMILGB; - t0 = gen_ext_tl(reg, cpu_cc_src, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + size = cc_op_size(s->cc_op); + return gen_prepare_val_nz(cpu_cc_src, size, true); + + case CC_OP_BLSIB ... CC_OP_BLSIQ: + size = cc_op_size(s->cc_op); + return gen_prepare_val_nz(cpu_cc_src, size, false); case CC_OP_ADCX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; case CC_OP_EFLAGS: case CC_OP_SARB ... CC_OP_SARQ: /* CC_SRC & 1 */ - return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = CC_C }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, + .reg = cpu_cc_src, .imm = CC_C }; default: /* The need to compute only C from CC_OP_DYNAMIC is important in efficiently implementing e.g. INC at the start of a TB. */ gen_update_cc_op(s); + if (!reg) { + reg = tcg_temp_new(); + } gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_op); return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = -1, .no_setcond = true }; + .no_setcond = true }; } } -/* compute eflags.P to reg */ +/* compute eflags.P, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) { gen_compute_eflags(s); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_P }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .imm = CC_P }; } -/* compute eflags.S to reg */ +/* compute eflags.S, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) { switch (s->cc_op) { @@ -1106,73 +1073,70 @@ static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) case CC_OP_ADCX: case CC_OP_ADOX: case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_S }; - case CC_OP_CLR: + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .imm = CC_S }; case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NEVER }; default: - { - MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true); - return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 }; - } + return gen_prepare_sign_nz(cpu_cc_dst, cc_op_size(s->cc_op)); } } -/* compute eflags.O to reg */ +/* compute eflags.O, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) { switch (s->cc_op) { case CC_OP_ADOX: case CC_OP_ADCOX: return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2, - .mask = -1, .no_setcond = true }; - case CC_OP_CLR: + .no_setcond = true }; + case CC_OP_LOGICB ... CC_OP_LOGICQ: case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_NEVER }; + case CC_OP_MULB ... CC_OP_MULQ: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src }; default: gen_compute_eflags(s); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_O }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .imm = CC_O }; } } -/* compute eflags.Z to reg */ +/* compute eflags.Z, trying to store it in reg if not NULL */ static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) { switch (s->cc_op) { - case CC_OP_DYNAMIC: - gen_compute_eflags(s); - /* FALLTHRU */ case CC_OP_EFLAGS: case CC_OP_ADCX: case CC_OP_ADOX: case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_Z }; - case CC_OP_CLR: - return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .imm = CC_Z }; + case CC_OP_DYNAMIC: + gen_update_cc_op(s); + if (!reg) { + reg = tcg_temp_new(); + } + gen_helper_cc_compute_nz(reg, cpu_cc_dst, cpu_cc_src, cpu_cc_op); + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = reg, .imm = 0 }; case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src, - .mask = -1 }; + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_dst }; default: { - MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + MemOp size = cc_op_size(s->cc_op); + return gen_prepare_val_nz(cpu_cc_dst, size, true); } } } -/* perform a conditional store into register 'reg' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. */ +/* return how to compute jump opcode 'b'. 'reg' can be clobbered + * if needed; it may be used for CCPrepare.reg if that will + * provide more freedom in the translation of a subsequent setcond. */ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) { int inv, jcc_op, cond; MemOp size; CCPrepare cc; - TCGv t0; inv = b & 1; jcc_op = (b >> 1) & 7; @@ -1180,27 +1144,24 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) switch (s->cc_op) { case CC_OP_SUBB ... CC_OP_SUBQ: /* We optimize relational operators for the cmp/jcc case. */ - size = s->cc_op - CC_OP_SUBB; + size = cc_op_size(s->cc_op); switch (jcc_op) { case JCC_BE: - tcg_gen_mov_tl(s->tmp4, s->cc_srcT); - gen_extu(size, s->tmp4); - t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4, - .reg2 = t0, .mask = -1, .use_reg2 = true }; + tcg_gen_ext_tl(s->cc_srcT, s->cc_srcT, size); + tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size); + cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->cc_srcT, + .reg2 = cpu_cc_src, .use_reg2 = true }; break; - case JCC_L: cond = TCG_COND_LT; goto fast_jcc_l; case JCC_LE: cond = TCG_COND_LE; fast_jcc_l: - tcg_gen_mov_tl(s->tmp4, s->cc_srcT); - gen_exts(size, s->tmp4); - t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true); - cc = (CCPrepare) { .cond = cond, .reg = s->tmp4, - .reg2 = t0, .mask = -1, .use_reg2 = true }; + tcg_gen_ext_tl(s->cc_srcT, s->cc_srcT, size | MO_SIGN); + tcg_gen_ext_tl(cpu_cc_src, cpu_cc_src, size | MO_SIGN); + cc = (CCPrepare) { .cond = cond, .reg = s->cc_srcT, + .reg2 = cpu_cc_src, .use_reg2 = true }; break; default: @@ -1208,6 +1169,28 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) } break; + case CC_OP_LOGICB ... CC_OP_LOGICQ: + /* Mostly used for test+jump */ + size = s->cc_op - CC_OP_LOGICB; + switch (jcc_op) { + case JCC_BE: + /* CF = 0, becomes jz/je */ + jcc_op = JCC_Z; + goto slow_jcc; + case JCC_L: + /* OF = 0, becomes js/jns */ + jcc_op = JCC_S; + goto slow_jcc; + case JCC_LE: + /* SF or ZF, becomes signed <= 0 */ + tcg_gen_ext_tl(cpu_cc_dst, cpu_cc_dst, size | MO_SIGN); + cc = (CCPrepare) { .cond = TCG_COND_LE, .reg = cpu_cc_dst }; + break; + default: + goto slow_jcc; + } + break; + default: slow_jcc: /* This actually generates good code for JC, JZ and JS. */ @@ -1223,8 +1206,8 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) break; case JCC_BE: gen_compute_eflags(s); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_Z | CC_C }; + cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = cpu_cc_src, + .imm = CC_Z | CC_C }; break; case JCC_S: cc = gen_prepare_eflags_s(s, reg); @@ -1234,24 +1217,22 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) break; case JCC_L: gen_compute_eflags(s); - if (reg == cpu_cc_src) { - reg = s->tmp0; + if (!reg || reg == cpu_cc_src) { + reg = tcg_temp_new(); } - tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ - tcg_gen_xor_tl(reg, reg, cpu_cc_src); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_S }; + tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); + cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, + .imm = CC_O }; break; default: case JCC_LE: gen_compute_eflags(s); - if (reg == cpu_cc_src) { - reg = s->tmp0; + if (!reg || reg == cpu_cc_src) { + reg = tcg_temp_new(); } - tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ - tcg_gen_xor_tl(reg, reg, cpu_cc_src); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_S | CC_Z }; + tcg_gen_addi_tl(reg, cpu_cc_src, CC_O - CC_S); + cc = (CCPrepare) { .cond = TCG_COND_TSTNE, .reg = reg, + .imm = CC_O | CC_Z }; break; } break; @@ -1276,16 +1257,6 @@ static void gen_setcc1(DisasContext *s, int b, TCGv reg) return; } - if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 && - cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) { - tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask)); - tcg_gen_andi_tl(reg, reg, 1); - return; - } - if (cc.mask != -1) { - tcg_gen_andi_tl(reg, cc.reg, cc.mask); - cc.reg = reg; - } if (cc.use_reg2) { tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); } else { @@ -1299,15 +1270,11 @@ 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); + CCPrepare cc = gen_prepare_cc(s, b, NULL); - if (cc.mask != -1) { - tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); - cc.reg = s->T0; - } if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); } else { @@ -1316,18 +1283,18 @@ 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. - A translation block must end soon. */ + value 'b'. In the fast case, T0 is guaranteed not to be used. + One or both of the branches will call gen_jmp_rel, so ensure + cc_op is clean. */ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) { - CCPrepare cc = gen_prepare_cc(s, b, s->T0); + CCPrepare cc = gen_prepare_cc(s, b, NULL); + /* + * Note that this must be _after_ gen_prepare_cc, because it + * can change the cc_op from CC_OP_DYNAMIC to CC_OP_EFLAGS! + */ gen_update_cc_op(s); - if (cc.mask != -1) { - tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); - cc.reg = s->T0; - } - set_cc_op(s, CC_OP_DYNAMIC); if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); } else { @@ -1336,11 +1303,15 @@ static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) } /* XXX: does not work with gdbstub "ice" single step - not a - serious problem */ + serious problem. The caller can jump to the returned label + to stop the REP but, if the flags have changed, it has to call + gen_update_cc_op before doing so. */ static TCGLabel *gen_jz_ecx_string(DisasContext *s) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); + + gen_update_cc_op(s); gen_op_jnz_ecx(s, l1); gen_set_label(l2); gen_jmp_rel_csize(s, 0, 1); @@ -1350,11 +1321,9 @@ static TCGLabel *gen_jz_ecx_string(DisasContext *s) 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 void gen_lods(DisasContext *s, MemOp ot) @@ -1362,28 +1331,37 @@ 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 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 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); + gen_op_ld_v(s, ot, s->T0, s->A0); + 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); + + 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) @@ -1395,7 +1373,7 @@ static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) #else TCGv_i32 t_size = tcg_constant_i32(1 << ot); TCGv t_next = eip_next_tl(s); - gen_helper_bpt_io(cpu_env, t_port, t_size, t_next); + gen_helper_bpt_io(tcg_env, t_port, t_size, t_next); #endif /* CONFIG_USER_ONLY */ } } @@ -1411,8 +1389,7 @@ static 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); } @@ -1425,8 +1402,7 @@ static 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); } @@ -1435,7 +1411,6 @@ 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); @@ -1449,38 +1424,27 @@ static void gen_repz(DisasContext *s, MemOp ot, gen_jmp_rel_csize(s, -cur_insn_len(s), 0); } -#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)) +static void gen_repz_nz(DisasContext *s, MemOp ot, + void (*fn)(DisasContext *s, MemOp ot)) { TCGLabel *l2; - gen_update_cc_op(s); + int nz = (s->prefix & PREFIX_REPNZ) ? 1 : 0; + 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); } + /* + * Only one iteration is done at a time, so the translation + * block ends unconditionally after this instruction and there + * is no control flow junction - no need to set CC_OP_DYNAMIC. + */ 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) -GEN_REPZ(ins) -GEN_REPZ(outs) -GEN_REPZ2(scas) -GEN_REPZ2(cmps) - static TCGv_ptr gen_stn_ptr(int opreg) { TCGv_i32 offset = tcg_temp_new_i32(); @@ -1495,16 +1459,15 @@ static TCGv_ptr gen_stn_ptr(int opreg) tcg_gen_addi_i32(offset, offset, offsetof(CPUX86State, fpregs[0].d)); TCGv_ptr ptr = tcg_temp_new_ptr(); tcg_gen_ext_i32_ptr(ptr, offset); - tcg_gen_add_ptr(ptr, ptr, cpu_env); + tcg_gen_add_ptr(ptr, ptr, tcg_env); - tcg_temp_free_i32(offset); return ptr; } static TCGv_ptr gen_ft0_ptr(void) { TCGv_ptr ft0 = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ft0, cpu_env, offsetof(CPUX86State, ft0)); + tcg_gen_addi_ptr(ft0, tcg_env, offsetof(CPUX86State, ft0)); return ft0; } @@ -1512,11 +1475,9 @@ static void gen_set_fptag(int offs, int value) { TCGv_ptr p = tcg_temp_new_ptr(); tcg_gen_ext_i32_ptr(p, fpstt); - tcg_gen_add_ptr(p, cpu_env, p); - TCGv_i32 tmp = tcg_const_i32(value); + tcg_gen_add_ptr(p, tcg_env, p); + TCGv_i32 tmp = tcg_constant_i32(value); tcg_gen_st8_i32(tmp, p, offsetof(CPUX86State, fptags[0]) + offs); - tcg_temp_free_i32(tmp); - tcg_temp_free_ptr(p); } static bool fpu_using_double_precision(DisasContext *s) @@ -1546,12 +1507,11 @@ static void gen_flcr(DisasContext *s) } TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld16u_i32(v, cpu_env, offsetof(CPUX86State, fpuc)); + tcg_gen_ld16u_i32(v, tcg_env, offsetof(CPUX86State, fpuc)); tcg_gen_andi_i32(v, v, 0xc00); tcg_gen_shli_i32(v, v, 3); tcg_gen_ori_i32(v, v, 0x1f80); tcg_gen_flcr(v); - tcg_temp_free_i32(v); s->flcr_set = true; } @@ -1560,7 +1520,6 @@ static void gen_mov32f_i64(TCGv_i64 ret, TCGv_f32 arg) TCGv_f64 t = tcg_temp_new_f64(); tcg_gen_cvt32f_f64(t, arg); tcg_gen_mov64f_i64(ret, t); - tcg_temp_free_f64(t); } static void gen_mov32f_i32(TCGv_i32 ret, TCGv_f32 arg) @@ -1573,7 +1532,6 @@ static void gen_mov32i_f64(TCGv_f64 ret, TCGv_i32 arg) TCGv_f32 t = tcg_temp_new_f32(); tcg_gen_mov32i_f32(t, arg); tcg_gen_cvt32f_f64(ret, t); - tcg_temp_free_f32(t); } static void gen_mov32i_f32(TCGv_f32 ret, TCGv_i32 arg) @@ -1586,7 +1544,6 @@ static void gen_mov64f_i32(TCGv_i32 ret, TCGv_f64 arg) TCGv_f32 t = tcg_temp_new_f32(); tcg_gen_cvt64f_f32(t, arg); tcg_gen_mov32f_i32(ret, t); - tcg_temp_free_f32(t); } static void gen_mov64f_i64(TCGv_i64 ret, TCGv_f64 arg) @@ -1599,7 +1556,6 @@ static void gen_mov64i_f32(TCGv_f32 ret, TCGv_i64 arg) TCGv_f64 t = tcg_temp_new_f64(); tcg_gen_mov64i_f64(t, arg); tcg_gen_cvt64f_f32(ret, t); - tcg_temp_free_f64(t); } static void gen_mov64i_f64(TCGv_f64 ret, TCGv_i64 arg) @@ -1630,25 +1586,25 @@ static void gen_flush_fp(DisasContext *s) */ #define GEN_HELPER_FALLBACK_v_v(func) do { \ if (!g_use_hard_fpu) { \ - gen_helper_ ## func(cpu_env); \ + gen_helper_ ## func(tcg_env); \ return; \ }} while(0) #define GEN_HELPER_FALLBACK_v_i(func, arg) do { \ if (!g_use_hard_fpu) { \ - gen_helper_ ## func(cpu_env, tcg_const_i32(arg)); \ + gen_helper_ ## func(tcg_env, tcg_constant_i32(arg)); \ return; \ }} while(0) #define GEN_HELPER_FALLBACK_v_T(func, arg) do { \ if (!g_use_hard_fpu) { \ - gen_helper_ ## func(cpu_env, arg); \ + gen_helper_ ## func(tcg_env, arg); \ return; \ }} while(0) #define GEN_HELPER_FALLBACK_T_v(func, arg) do { \ if (!g_use_hard_fpu) { \ - gen_helper_ ## func(arg, cpu_env); \ + gen_helper_ ## func(arg, tcg_env); \ return; \ }} while(0) @@ -1719,11 +1675,10 @@ static void gen_enter_mmx(DisasContext *s) tcg_gen_movi_i32(fpstt, 0); - TCGv_i32 v = tcg_const_i32(0); + TCGv_i32 v = tcg_constant_i32(0); for (int i = 0; i < 8; i++) { - tcg_gen_st8_i32(v, cpu_env, offsetof(CPUX86State, fptags[0]) + i); + tcg_gen_st8_i32(v, tcg_env, offsetof(CPUX86State, fptags[0]) + i); } - tcg_temp_free_i32(v); } static void gen_flds_FT0(DisasContext *s, TCGv_i32 arg) @@ -1817,9 +1772,9 @@ static void gen_fsqrt(DisasContext *s) static void gen_clear_fpus_c2(DisasContext *s) { TCGv_i32 v = tcg_temp_new_i32(); - tcg_gen_ld16u_i32(v, cpu_env, offsetof(CPUX86State, fpus)); + tcg_gen_ld16u_i32(v, tcg_env, offsetof(CPUX86State, fpus)); tcg_gen_andi_i32(v, v, ~0x400); /* C2 <-- 0 */ - tcg_gen_st16_i32(v, cpu_env, offsetof(CPUX86State, fpus)); + tcg_gen_st16_i32(v, tcg_env, offsetof(CPUX86State, fpus)); } static void gen_fsin(DisasContext *s) @@ -1843,28 +1798,28 @@ static void gen_helper_fp_arith_ST0_FT0(DisasContext *s, int op) } else { 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; } } @@ -1881,26 +1836,26 @@ static void gen_helper_fp_arith_STN_ST0(DisasContext *s, int op, int opreg) if (g_use_hard_fpu) { fp_pc_wrapper(gen_helper_fp_arith_STN_ST0)(s, op, opreg); } else { - 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; } } @@ -1929,7 +1884,7 @@ static void gen_exception(DisasContext *s, int trapno) { gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); + gen_helper_raise_exception(tcg_env, tcg_constant_i32(trapno)); s->base.is_jmp = DISAS_NORETURN; } @@ -1956,519 +1911,11 @@ static bool check_cpl0(DisasContext *s) return false; } -/* If vm86, check for iopl == 3; if not, raise #GP and return false. */ -static bool check_vm86_iopl(DisasContext *s) -{ - if (!VM86(s) || IOPL(s) == 3) { - return true; - } - gen_exception_gpf(s); - return false; -} - -/* Check for iopl allowing access; if not, raise #GP and return false. */ -static bool check_iopl(DisasContext *s) -{ - if (VM86(s) ? IOPL(s) == 3 : CPL(s) <= IOPL(s)) { - return true; - } - gen_exception_gpf(s); - return false; -} - -/* if d == OR_TMP0, it means memory operand (address in A0) */ -static void gen_op(DisasContext *s1, int op, MemOp ot, int d) -{ - 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); - } - switch(op) { - case OP_ADCL: - gen_compute_eflags_c(s1, s1->tmp4); - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(s1->T0, s1->tmp4, s1->T1); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); - tcg_gen_add_tl(s1->T0, s1->T0, s1->tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update3_cc(s1, s1->tmp4); - set_cc_op(s1, CC_OP_ADCB + ot); - break; - case OP_SBBL: - gen_compute_eflags_c(s1, s1->tmp4); - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(s1->T0, s1->T1, s1->tmp4); - tcg_gen_neg_tl(s1->T0, s1->T0); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); - tcg_gen_sub_tl(s1->T0, s1->T0, s1->tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update3_cc(s1, s1->tmp4); - set_cc_op(s1, CC_OP_SBBB + ot); - break; - case OP_ADDL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update2_cc(s1); - set_cc_op(s1, CC_OP_ADDB + ot); - break; - case OP_SUBL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_neg_tl(s1->T0, s1->T1); - tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1); - } else { - tcg_gen_mov_tl(s1->cc_srcT, s1->T0); - tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update2_cc(s1); - set_cc_op(s1, CC_OP_SUBB + ot); - break; - default: - case OP_ANDL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_and_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_and_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_ORL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_or_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_or_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_XORL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_xor_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_xor_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_CMPL: - tcg_gen_mov_tl(cpu_cc_src, s1->T1); - tcg_gen_mov_tl(s1->cc_srcT, s1->T0); - tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1); - set_cc_op(s1, CC_OP_SUBB + ot); - break; - } -} - -/* if d == OR_TMP0, it means memory operand (address in A0) */ -static void gen_inc(DisasContext *s1, MemOp ot, int d, int c) -{ - if (s1->prefix & PREFIX_LOCK) { - if (d != OR_TMP0) { - /* Lock prefix when destination is not memory */ - gen_illegal_opcode(s1); - return; - } - tcg_gen_movi_tl(s1->T0, c > 0 ? 1 : -1); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - if (d != OR_TMP0) { - gen_op_mov_v_reg(s1, ot, s1->T0, d); - } else { - gen_op_ld_v(s1, ot, s1->T0, s1->A0); - } - tcg_gen_addi_tl(s1->T0, s1->T0, (c > 0 ? 1 : -1)); - gen_op_st_rm_T0_A0(s1, ot, d); - } - - gen_compute_eflags_c(s1, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, s1->T0); - set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot); -} - -static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, - TCGv shm1, TCGv count, bool is_right) -{ - TCGv_i32 z32, s32, oldop; - TCGv z_tl; - - /* 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); - 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); - } else { - tcg_gen_mov_tl(cpu_cc_dst, result); - } - if (cc_op_live[s->cc_op] & USES_CC_SRC) { - tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_src, count, z_tl, - shm1, cpu_cc_src); - } 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); - if (s->cc_op == CC_OP_DYNAMIC) { - oldop = cpu_cc_op; - } else { - tcg_gen_movi_i32(s->tmp3_i32, s->cc_op); - oldop = s->tmp3_i32; - } - - /* Conditionally store the CC_OP value. */ - z32 = tcg_const_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); -} - -static void gen_shift_rm_T1(DisasContext *s, MemOp ot, int op1, - int is_right, int is_arith) -{ - target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - tcg_gen_andi_tl(s->T1, s->T1, mask); - tcg_gen_subi_tl(s->tmp0, s->T1, 1); - - if (is_right) { - if (is_arith) { - gen_exts(ot, s->T0); - tcg_gen_sar_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_sar_tl(s->T0, s->T0, s->T1); - } else { - gen_extu(ot, s->T0); - tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_shr_tl(s->T0, s->T0, s->T1); - } - } else { - tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_shl_tl(s->T0, s->T0, s->T1); - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - gen_shift_flags(s, ot, s->T0, s->tmp0, s->T1, is_right); -} - -static void gen_shift_rm_im(DisasContext *s, MemOp ot, int op1, int op2, - int is_right, int is_arith) -{ - int mask = (ot == MO_64 ? 0x3f : 0x1f); - - /* load */ - if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, s->T0, s->A0); - else - gen_op_mov_v_reg(s, ot, s->T0, op1); - - op2 &= mask; - if (op2 != 0) { - if (is_right) { - if (is_arith) { - gen_exts(ot, s->T0); - tcg_gen_sari_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_sari_tl(s->T0, s->T0, op2); - } else { - gen_extu(ot, s->T0); - tcg_gen_shri_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_shri_tl(s->T0, s->T0, op2); - } - } else { - tcg_gen_shli_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_shli_tl(s->T0, s->T0, op2); - } - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - /* update eflags if non zero shift */ - if (op2 != 0) { - tcg_gen_mov_tl(cpu_cc_src, s->tmp4); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); - } -} - -static void gen_rot_rm_T1(DisasContext *s, MemOp ot, int op1, int is_right) -{ - target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); - TCGv_i32 t0, t1; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - tcg_gen_andi_tl(s->T1, s->T1, mask); - - switch (ot) { - case MO_8: - /* Replicate the 8-bit input so that a 32-bit rotate works. */ - tcg_gen_ext8u_tl(s->T0, s->T0); - tcg_gen_muli_tl(s->T0, s->T0, 0x01010101); - goto do_long; - case MO_16: - /* Replicate the 16-bit input so that a 32-bit rotate works. */ - tcg_gen_deposit_tl(s->T0, s->T0, s->T0, 16, 16); - goto do_long; - do_long: -#ifdef TARGET_X86_64 - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - if (is_right) { - tcg_gen_rotr_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - } else { - tcg_gen_rotl_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - } - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; -#endif - default: - if (is_right) { - tcg_gen_rotr_tl(s->T0, s->T0, s->T1); - } else { - tcg_gen_rotl_tl(s->T0, s->T0, s->T1); - } - break; - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - /* We'll need the flags computed into CC_SRC. */ - gen_compute_eflags(s); - - /* The value that was "rotated out" is now present at the other end - of the word. Compute C into CC_DST and O into CC_SRC2. Note that - since we've computed the flags into CC_SRC, these variables are - currently dead. */ - if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); - } else { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); - } - tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); - tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - - /* Now conditionally store the new CC_OP value. If the shift count - 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); - 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. */ - set_cc_op(s, CC_OP_DYNAMIC); -} - -static void gen_rot_rm_im(DisasContext *s, MemOp ot, int op1, int op2, - int is_right) -{ - int mask = (ot == MO_64 ? 0x3f : 0x1f); - int shift; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - op2 &= mask; - if (op2 != 0) { - switch (ot) { -#ifdef TARGET_X86_64 - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - if (is_right) { - tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, op2); - } else { - tcg_gen_rotli_i32(s->tmp2_i32, s->tmp2_i32, op2); - } - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; -#endif - default: - if (is_right) { - tcg_gen_rotri_tl(s->T0, s->T0, op2); - } else { - tcg_gen_rotli_tl(s->T0, s->T0, op2); - } - break; - case MO_8: - mask = 7; - goto do_shifts; - case MO_16: - mask = 15; - do_shifts: - shift = op2 & mask; - if (is_right) { - shift = mask + 1 - shift; - } - gen_extu(ot, s->T0); - tcg_gen_shli_tl(s->tmp0, s->T0, shift); - tcg_gen_shri_tl(s->T0, s->T0, mask + 1 - shift); - tcg_gen_or_tl(s->T0, s->T0, s->tmp0); - break; - } - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - if (op2 != 0) { - /* Compute the flags into CC_SRC. */ - gen_compute_eflags(s); - - /* The value that was "rotated out" is now present at the other end - of the word. Compute C into CC_DST and O into CC_SRC2. Note that - since we've computed the flags into CC_SRC, these variables are - currently dead. */ - if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); - } else { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); - } - tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); - tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - set_cc_op(s, CC_OP_ADCOX); - } -} - -/* XXX: add faster immediate = 1 case */ -static void gen_rotc_rm_T1(DisasContext *s, MemOp ot, int op1, - int is_right) -{ - gen_compute_eflags(s); - assert(s->cc_op == CC_OP_EFLAGS); - - /* load */ - if (op1 == OR_TMP0) - 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); - break; - case MO_16: - gen_helper_rcrw(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_32: - gen_helper_rcrl(s->T0, cpu_env, s->T0, s->T1); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_rcrq(s->T0, cpu_env, s->T0, s->T1); - break; -#endif - default: - tcg_abort(); - } - } else { - switch (ot) { - case MO_8: - gen_helper_rclb(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_16: - gen_helper_rclw(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_32: - gen_helper_rcll(s->T0, cpu_env, s->T0, s->T1); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_rclq(s->T0, cpu_env, s->T0, s->T1); - break; -#endif - default: - tcg_abort(); - } - } - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); -} - /* XXX: add faster immediate case */ -static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, - bool is_right, TCGv count_in) +static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, + bool is_right, TCGv count) { target_ulong mask = (ot == MO_64 ? 63 : 31); - TCGv count; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - count = tcg_temp_new(); - tcg_gen_andi_tl(count, count_in, mask); switch (ot) { case MO_16: @@ -2530,69 +1977,6 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, tcg_gen_or_tl(s->T0, s->T0, s->T1); break; } - - /* store */ - 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) -{ - if (s != OR_TMP1) - gen_op_mov_v_reg(s1, ot, s1->T1, s); - switch(op) { - case OP_ROL: - gen_rot_rm_T1(s1, ot, d, 0); - break; - case OP_ROR: - gen_rot_rm_T1(s1, ot, d, 1); - break; - case OP_SHL: - case OP_SHL1: - gen_shift_rm_T1(s1, ot, d, 0, 0); - break; - case OP_SHR: - gen_shift_rm_T1(s1, ot, d, 1, 0); - break; - case OP_SAR: - gen_shift_rm_T1(s1, ot, d, 1, 1); - break; - case OP_RCL: - gen_rotc_rm_T1(s1, ot, d, 0); - break; - case OP_RCR: - gen_rotc_rm_T1(s1, ot, d, 1); - break; - } -} - -static void gen_shifti(DisasContext *s1, int op, MemOp ot, int d, int c) -{ - switch(op) { - case OP_ROL: - gen_rot_rm_im(s1, ot, d, c, 0); - break; - case OP_ROR: - gen_rot_rm_im(s1, ot, d, c, 1); - break; - case OP_SHL: - case OP_SHL1: - gen_shift_rm_im(s1, ot, d, c, 0, 0); - break; - case OP_SHR: - gen_shift_rm_im(s1, ot, d, c, 1, 0); - break; - case OP_SAR: - gen_shift_rm_im(s1, ot, d, c, 1, 1); - break; - default: - /* currently not optimized */ - tcg_gen_movi_tl(s1->T1, c); - gen_shift(s1, op, ot, d, OR_TMP1); - break; - } } #define X86_MAX_INSN_LENGTH 15 @@ -2615,9 +1999,8 @@ static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) * This can happen even if the operand is only one byte long! */ if (((s->pc - 1) ^ (pc - 1)) & TARGET_PAGE_MASK) { - volatile uint8_t unused = - cpu_ldub_code(env, (s->pc - 1) & TARGET_PAGE_MASK); - (void) unused; + (void)translator_ldub(env, &s->base, + (s->pc - 1) & TARGET_PAGE_MASK); } siglongjmp(s->jmpbuf, 1); } @@ -2630,11 +2013,6 @@ static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s) return translator_ldub(env, &s->base, advance_pc(env, s, 1)); } -static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s) -{ - return translator_lduw(env, &s->base, advance_pc(env, s, 2)); -} - static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s) { return translator_lduw(env, &s->base, advance_pc(env, s, 2)); @@ -2654,16 +2032,8 @@ static inline uint64_t x86_ldq_code(CPUX86State *env, DisasContext *s) /* Decompose an address. */ -typedef struct AddressParts { - int def_seg; - int base; - int index; - int scale; - target_long disp; -} AddressParts; - static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, - int modrm) + int modrm, bool is_vsib) { int def_seg, base, index, scale, mod, rm; target_long disp; @@ -2692,7 +2062,7 @@ static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, int code = x86_ldub_code(env, s); scale = (code >> 6) & 3; index = ((code >> 3) & 7) | REX_X(s); - if (index == 4) { + if (index == 4 && !is_vsib) { index = -1; /* no index */ } base = (code & 7) | REX_B(s); @@ -2778,7 +2148,7 @@ static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, break; default: - tcg_abort(); + g_assert_not_reached(); } done: @@ -2805,7 +2175,7 @@ static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a, bool is_vsib) ea = cpu_regs[a.base]; } if (!ea) { - if (TARGET_TB_PCREL && a.base == -2) { + 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 { @@ -2820,24 +2190,11 @@ static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a, bool is_vsib) return ea; } -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, false); - gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); -} - -static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) -{ - (void)gen_lea_modrm_0(env, s, modrm); -} - /* Used for BNDCL, BNDCU, BNDCN. */ -static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, +static void gen_bndck(DisasContext *s, X86DecodedInsn *decode, TCGCond cond, TCGv_i64 bndv) { - AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a, false); + TCGv ea = gen_lea_modrm_1(s, decode->mem, false); tcg_gen_extu_tl_i64(s->tmp1_i64, ea); if (!CODE64(s)) { @@ -2845,45 +2202,38 @@ 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 */ -static void gen_add_A0_ds_seg(DisasContext *s) -{ - gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); -} - -/* generate modrm memory load or store of 'reg'. TMP0 is used if reg == - OR_TMP0 */ -static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, - MemOp ot, int reg, int is_store) +/* generate modrm load of memory or register. */ +static void gen_ld_modrm(DisasContext *s, X86DecodedInsn *decode, MemOp ot) { + int modrm = s->modrm; int mod, rm; mod = (modrm >> 6) & 3; rm = (modrm & 7) | REX_B(s); if (mod == 3) { - if (is_store) { - if (reg != OR_TMP0) - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - if (reg != OR_TMP0) - gen_op_mov_reg_v(s, ot, reg, s->T0); - } + gen_op_mov_v_reg(s, ot, s->T0, rm); } else { - gen_lea_modrm(env, s, modrm); - if (is_store) { - if (reg != OR_TMP0) - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_ld_v(s, ot, s->T0, s->A0); - if (reg != OR_TMP0) - gen_op_mov_reg_v(s, ot, reg, s->T0); - } + gen_lea_modrm(s, decode); + gen_op_ld_v(s, ot, s->T0, s->A0); + } +} + +/* generate modrm store of memory or register. */ +static void gen_st_modrm(DisasContext *s, X86DecodedInsn *decode, MemOp ot) +{ + int modrm = s->modrm; + int mod, rm; + + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod == 3) { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + gen_lea_modrm(s, decode); + gen_op_st_v(s, ot, s->T0, s->A0); } } @@ -2930,7 +2280,7 @@ 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; } @@ -2960,13 +2310,16 @@ static target_long insn_get_signed(CPUX86State *env, DisasContext *s, MemOp ot) return ret; } -static inline int insn_const_size(MemOp ot) +static void gen_conditional_jump_labels(DisasContext *s, target_long diff, + TCGLabel *not_taken, TCGLabel *taken) { - if (ot <= MO_32) { - return 1 << ot; - } else { - return 4; + if (not_taken) { + gen_set_label(not_taken); } + gen_jmp_rel_csize(s, 0, 1); + + gen_set_label(taken); + gen_jmp_rel(s, s->dflag, diff, 0); } static void gen_jcc(DisasContext *s, int b, int diff) @@ -2974,61 +2327,36 @@ static void gen_jcc(DisasContext *s, int b, int diff) TCGLabel *l1 = gen_new_label(); gen_jcc1(s, b, l1); - gen_jmp_rel_csize(s, 0, 1); - gen_set_label(l1); - gen_jmp_rel(s, s->dflag, diff, 0); + gen_conditional_jump_labels(s, diff, NULL, l1); } -static void gen_cmovcc1(CPUX86State *env, DisasContext *s, MemOp ot, int b, - int modrm, int reg) +static void gen_cmovcc1(DisasContext *s, int b, TCGv dest, TCGv src) { - CCPrepare cc; + CCPrepare cc = gen_prepare_cc(s, b, NULL); - 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) +static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) { - tcg_gen_ld32u_tl(s->T0, cpu_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, + TCGv selector = tcg_temp_new(); + tcg_gen_ext16u_tl(selector, seg); + tcg_gen_st32_tl(selector, tcg_env, offsetof(CPUX86State,segs[seg_reg].selector)); - tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4); + tcg_gen_shli_tl(cpu_seg_base[seg_reg], selector, 4); } -/* move T0 to seg_reg and compute if the CPU state may change. Never +/* move SRC to seg_reg and compute if the CPU state may change. Never call this function with seg_reg == R_CS */ -static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg) +static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src) { 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); + tcg_gen_trunc_tl_i32(s->tmp2_i32, src); + 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 @@ -3039,20 +2367,52 @@ static void gen_movl_seg_T0(DisasContext *s, X86Seg seg_reg) s->base.is_jmp = DISAS_EOB_NEXT; } } else { - gen_op_movl_seg_T0_vm(s, seg_reg); + gen_op_movl_seg_real(s, seg_reg, src); if (seg_reg == R_SS) { s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } } } +static void gen_far_call(DisasContext *s) +{ + TCGv_i32 new_cs = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(new_cs, s->T1); + if (PE(s) && !VM86(s)) { + gen_helper_lcall_protected(tcg_env, new_cs, s->T0, + tcg_constant_i32(s->dflag - 1), + eip_next_tl(s)); + } else { + TCGv_i32 new_eip = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(new_eip, s->T0); + gen_helper_lcall_real(tcg_env, new_cs, new_eip, + tcg_constant_i32(s->dflag - 1), + eip_next_i32(s)); + } + s->base.is_jmp = DISAS_JUMP; +} + +static void gen_far_jmp(DisasContext *s) +{ + if (PE(s) && !VM86(s)) { + TCGv_i32 new_cs = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(new_cs, s->T1); + gen_helper_ljmp_protected(tcg_env, new_cs, s->T0, + eip_next_tl(s)); + } else { + gen_op_movl_seg_real(s, R_CS, s->T1); + gen_op_jmp_v(s, s->T0); + } + s->base.is_jmp = DISAS_JUMP; +} + static void gen_svm_check_intercept(DisasContext *s, uint32_t type) { /* no SVM activated; fast case */ 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) @@ -3060,24 +2420,27 @@ static inline void gen_stack_update(DisasContext *s, int addend) gen_op_add_reg_im(s, mo_stacksize(s), R_ESP, addend); } +static void gen_lea_ss_ofs(DisasContext *s, TCGv dest, TCGv src, target_ulong offset) +{ + if (offset) { + tcg_gen_addi_tl(dest, src, offset); + src = dest; + } + gen_lea_v_seg_dest(s, mo_stacksize(s), dest, src, R_SS, -1); +} + /* Generate a push. It depends on ss32, addseg and dflag. */ static void gen_push_v(DisasContext *s, TCGv val) { MemOp d_ot = mo_pushpop(s, s->dflag); MemOp a_ot = mo_stacksize(s); int size = 1 << d_ot; - TCGv new_esp = s->A0; + TCGv new_esp = tcg_temp_new(); - tcg_gen_subi_tl(s->A0, cpu_regs[R_ESP], size); - - if (!CODE64(s)) { - if (ADDSEG(s)) { - new_esp = s->tmp4; - tcg_gen_mov_tl(new_esp, s->A0); - } - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); - } + tcg_gen_subi_tl(new_esp, cpu_regs[R_ESP], size); + /* Now reduce the value to the address size and apply SS base. */ + gen_lea_ss_ofs(s, s->A0, new_esp, 0); gen_op_st_v(s, d_ot, val, s->A0); gen_op_mov_reg_v(s, a_ot, R_ESP, new_esp); } @@ -3087,8 +2450,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_ss_ofs(s, s->T0, cpu_regs[R_ESP], 0); + gen_op_ld_v(s, d_ot, s->T0, s->T0); return d_ot; } @@ -3098,21 +2461,14 @@ static inline void gen_pop_update(DisasContext *s, MemOp ot) gen_stack_update(s, 1 << ot); } -static inline void gen_stack_A0(DisasContext *s) -{ - gen_lea_v_seg(s, SS32(s) ? MO_32 : MO_16, cpu_regs[R_ESP], R_SS, -1); -} - static void gen_pusha(DisasContext *s) { - MemOp s_ot = SS32(s) ? MO_32 : MO_16; MemOp d_ot = s->dflag; int size = 1 << d_ot; int i; for (i = 0; i < 8; i++) { - tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], (i - 8) * size); - gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], (i - 8) * size); gen_op_st_v(s, d_ot, cpu_regs[7 - i], s->A0); } @@ -3121,7 +2477,6 @@ static void gen_pusha(DisasContext *s) static void gen_popa(DisasContext *s) { - MemOp s_ot = SS32(s) ? MO_32 : MO_16; MemOp d_ot = s->dflag; int size = 1 << d_ot; int i; @@ -3131,8 +2486,7 @@ static void gen_popa(DisasContext *s) if (7 - i == R_ESP) { continue; } - tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], i * size); - gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_ESP], i * size); gen_op_ld_v(s, d_ot, s->T0, s->A0); gen_op_mov_reg_v(s, d_ot, 7 - i, s->T0); } @@ -3143,12 +2497,12 @@ static void gen_popa(DisasContext *s) static void gen_enter(DisasContext *s, int esp_addend, int level) { MemOp d_ot = mo_pushpop(s, s->dflag); - MemOp a_ot = CODE64(s) ? MO_64 : SS32(s) ? MO_32 : MO_16; + MemOp a_ot = mo_stacksize(s); int size = 1 << d_ot; /* Push BP; compute FrameTemp into T1. */ tcg_gen_subi_tl(s->T1, cpu_regs[R_ESP], size); - gen_lea_v_seg(s, a_ot, s->T1, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, s->T1, 0); gen_op_st_v(s, d_ot, cpu_regs[R_EBP], s->A0); level &= 31; @@ -3157,23 +2511,20 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) /* Copy level-1 pointers from the previous frame. */ for (i = 1; i < level; ++i) { - tcg_gen_subi_tl(s->A0, cpu_regs[R_EBP], size * i); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], -size * i); gen_op_ld_v(s, d_ot, s->tmp0, s->A0); - tcg_gen_subi_tl(s->A0, s->T1, size * i); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, s->T1, -size * i); gen_op_st_v(s, d_ot, s->tmp0, s->A0); } /* Push the current FrameTemp as the last level. */ - tcg_gen_subi_tl(s->A0, s->T1, size * level); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_lea_ss_ofs(s, s->A0, s->T1, -size * level); gen_op_st_v(s, d_ot, s->T1, s->A0); } /* Copy the FrameTemp value to EBP. */ - gen_op_mov_reg_v(s, a_ot, R_EBP, s->T1); + gen_op_mov_reg_v(s, d_ot, R_EBP, s->T1); /* Compute the final value of ESP. */ tcg_gen_subi_tl(s->T1, s->T1, esp_addend + size * level); @@ -3185,7 +2536,7 @@ static void gen_leave(DisasContext *s) MemOp d_ot = mo_pushpop(s, s->dflag); MemOp a_ot = mo_stacksize(s); - gen_lea_v_seg(s, a_ot, cpu_regs[R_EBP], R_SS, -1); + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], 0); gen_op_ld_v(s, d_ot, s->T0, s->A0); tcg_gen_addi_tl(s->T1, cpu_regs[R_EBP], 1 << d_ot); @@ -3208,7 +2559,7 @@ static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) fprintf(logfile, "ILLOPC: " TARGET_FMT_lx ":", pc); for (; pc < end; ++pc) { - fprintf(logfile, " %02x", cpu_ldub_code(env, pc)); + fprintf(logfile, " %02x", translator_ldub(env, &s->base, pc)); } fprintf(logfile, "\n"); qemu_log_unlock(logfile); @@ -3218,11 +2569,11 @@ 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) +static void gen_interrupt(DisasContext *s, uint8_t intno) { gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_raise_interrupt(cpu_env, tcg_constant_i32(intno), + gen_helper_raise_interrupt(tcg_env, tcg_constant_i32(intno), cur_insn_len_i32(s)); s->base.is_jmp = DISAS_NORETURN; } @@ -3231,10 +2582,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; } } @@ -3243,10 +2593,9 @@ 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; } } @@ -3255,20 +2604,18 @@ static void gen_set_eflags(DisasContext *s, target_ulong mask) { TCGv t = tcg_temp_new(); - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUX86State, eflags)); + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); tcg_gen_ori_tl(t, t, mask); - tcg_gen_st_tl(t, cpu_env, offsetof(CPUX86State, eflags)); - tcg_temp_free(t); + 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, cpu_env, offsetof(CPUX86State, eflags)); + tcg_gen_ld_tl(t, tcg_env, offsetof(CPUX86State, eflags)); tcg_gen_andi_tl(t, t, ~mask); - tcg_gen_st_tl(t, cpu_env, offsetof(CPUX86State, eflags)); - tcg_temp_free(t); + tcg_gen_st_tl(t, tcg_env, offsetof(CPUX86State, eflags)); } /* Clear BND registers during legacy branches. */ @@ -3280,67 +2627,50 @@ 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); } } -/* Generate an end of block. Trace exception is also generated if needed. - If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. - If RECHECK_TF, emit a rechecking helper for #DB, ignoring the state of - S->TF. This is used by the syscall/sysret insns. */ +/* + * Generate an end of block, including common tasks such as generating + * single step traps, resetting the RF flag, and handling the interrupt + * shadow. + */ static void -do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) +gen_eob(DisasContext *s, int mode) { + bool inhibit_reset; + gen_update_cc_op(s); /* If several instructions disable interrupts, only the first does it. */ - if (inhibit && !(s->flags & HF_INHIBIT_IRQ_MASK)) { - gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); - } else { + inhibit_reset = false; + if (s->flags & HF_INHIBIT_IRQ_MASK) { gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); + inhibit_reset = true; + } else if (mode == DISAS_EOB_INHIBIT_IRQ) { + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); } if (s->base.tb->flags & HF_RF_MASK) { gen_reset_eflags(s, RF_MASK); } - if (recheck_tf) { - gen_helper_rechecking_single_step(cpu_env); + if (mode == DISAS_EOB_RECHECK_TF) { + 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); - } else if (jr) { + } else if ((s->flags & HF_TF_MASK) && mode != DISAS_EOB_INHIBIT_IRQ) { + gen_helper_single_step(tcg_env); + } else if (mode == DISAS_JUMP && + /* give irqs a chance to happen */ + !inhibit_reset) { tcg_gen_lookup_and_goto_ptr(); } else { tcg_gen_exit_tb(NULL, 0); } + s->base.is_jmp = DISAS_NORETURN; } -static inline void -gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf) -{ - do_gen_eob_worker(s, inhibit, recheck_tf, false); -} - -/* End of block. - If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. */ -static void gen_eob_inhibit_irq(DisasContext *s, bool inhibit) -{ - gen_eob_worker(s, inhibit, false); -} - -/* End of block, resetting the inhibit irq flag. */ -static void gen_eob(DisasContext *s) -{ - gen_eob_worker(s, false, false); -} - -/* Jump to register */ -static void gen_jr(DisasContext *s) -{ - do_gen_eob_worker(s, false, false, true); -} - /* Jump to eip+diff, truncating the result to OT. */ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) { @@ -3349,11 +2679,13 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) target_ulong new_pc = s->pc + diff; target_ulong new_eip = new_pc - s->cs_base; + assert(!s->cc_op_dirty); + /* In 64-bit mode, operand size is fixed at 64 bits. */ if (!CODE64(s)) { if (ot == MO_16) { mask = 0xffff; - if (TARGET_TB_PCREL && CODE32(s)) { + if (tb_cflags(s->base.tb) & CF_PCREL && CODE32(s)) { use_goto_tb = false; } } else { @@ -3362,10 +2694,7 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) } new_eip &= mask; - gen_update_cc_op(s); - set_cc_op(s, CC_OP_DYNAMIC); - - if (TARGET_TB_PCREL) { + 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 @@ -3376,25 +2705,26 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) 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_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 (!TARGET_TB_PCREL) { + 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 { - if (!TARGET_TB_PCREL) { + 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 */ + gen_eob(s, DISAS_JUMP); /* jump to another page */ } else { - gen_eob(s); /* exit to main loop */ + gen_eob(s, DISAS_EOB_ONLY); /* exit to main loop */ } } } @@ -3409,966 +2739,680 @@ static void gen_jmp_rel_csize(DisasContext *s, int diff, int 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, 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 | (align ? MO_ALIGN_16 : 0)); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_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(XMMReg, XMM_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, 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(XMMReg, XMM_Q(0))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, - MO_LEUQ | (align ? MO_ALIGN_16 : 0)); - tcg_gen_addi_tl(s->tmp0, s->A0, 8); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(XMMReg, XMM_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 void gen_ldy_env_A0(DisasContext *s, int offset, bool align) { + MemOp mop = MO_128 | MO_LE | MO_ATOM_IFALIGN_PAIR; int mem_index = s->mem_index; - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index, - MO_LEUQ | (align ? MO_ALIGN_32 : 0)); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_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(YMMReg, YMM_Q(1))); + 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_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(2))); - tcg_gen_addi_tl(s->tmp0, s->A0, 24); - 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(YMMReg, YMM_Q(3))); + 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 void gen_sty_env_A0(DisasContext *s, int offset, bool align) { + MemOp mop = MO_128 | MO_LE | MO_ATOM_IFALIGN_PAIR; int mem_index = s->mem_index; - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(0))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, - MO_LEUQ | (align ? MO_ALIGN_32 : 0)); - tcg_gen_addi_tl(s->tmp0, s->A0, 8); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_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 + 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_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(2))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); - tcg_gen_addi_tl(s->tmp0, s->A0, 24); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(YMMReg, YMM_Q(3))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEUQ); + tcg_gen_ld_i128(t, tcg_env, offset + offsetof(YMMReg, YMM_X(1))); + tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop); } -#include "decode-new.h" #include "emit.c.inc" -#include "decode-new.c.inc" -/* convert one instruction. s->base.is_jmp is set if the translation must - be stopped. Return the next pc value */ -static bool disas_insn(DisasContext *s, CPUState *cpu) +static void gen_x87(DisasContext *s, X86DecodedInsn *decode) { - CPUX86State *env = cpu->env_ptr; - int b, prefixes; - int shift; - MemOp ot, aflag, dflag; - int modrm, reg, rm, mod, op, opreg, val; - bool orig_cc_op_dirty = s->cc_op_dirty; - CCOp orig_cc_op = s->cc_op; - target_ulong orig_pc_save = s->pc_save; + bool update_fip = true; + int b = decode->b; + int modrm = s->modrm; + int mod, rm, op; - s->pc = s->base.pc_next; - s->override = -1; -#ifdef TARGET_X86_64 - s->rex_r = 0; - s->rex_x = 0; - s->rex_b = 0; -#endif - s->rip_offset = 0; /* for relative ip address */ - s->vex_l = 0; - s->vex_v = 0; - s->vex_w = false; - switch (sigsetjmp(s->jmpbuf, 0)) { - case 0: - break; - case 1: - gen_exception_gpf(s); - 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.is_jmp = DISAS_TOO_MANY; - return false; - default: - g_assert_not_reached(); + 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); + return; } + mod = (modrm >> 6) & 3; + rm = modrm & 7; + op = ((b & 7) << 3) | ((modrm >> 3) & 7); + if (mod != 3) { + /* memory op */ + TCGv ea = gen_lea_modrm_1(s, decode->mem, false); + TCGv last_addr = tcg_temp_new(); + bool update_fdp = true; - prefixes = 0; + tcg_gen_mov_tl(last_addr, ea); + gen_lea_v_seg(s, ea, decode->mem.def_seg, s->override); - 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; - 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: - prefixes |= PREFIX_DATA; - goto next_byte; - case 0x67: - prefixes |= PREFIX_ADR; - goto next_byte; -#ifdef TARGET_X86_64 - case 0x40 ... 0x4f: - if (CODE64(s)) { - /* REX prefix */ - prefixes |= 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 */ - if (CODE32(s) && !VM86(s)) { - int vex2 = x86_ldub_code(env, s); - s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ + switch (op) { + case 0x00 ... 0x07: /* fxxxs */ + case 0x10 ... 0x17: /* fixxxl */ + case 0x20 ... 0x27: /* fxxxl */ + case 0x30 ... 0x37: /* fixxx */ + { + int op1; + op1 = op & 7; - 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. */ + switch (op >> 4) { + case 0: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_flds_FT0(s, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_fildl_FT0(s, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_fldl_FT0(s, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_fildl_FT0(s, s->tmp2_i32); + break; + } + + gen_helper_fp_arith_ST0_FT0(s, op1); + if (op1 == 3) { + /* fcomp needs pop */ + gen_fpop(s); + } + } + break; + case 0x08: /* flds */ + case 0x0a: /* fsts */ + case 0x0b: /* fstps */ + case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ + case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ + case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ + switch (op & 7) { + case 0: + switch (op >> 4) { + case 0: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_flds_ST0(s, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_fildl_ST0(s, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_fldl_ST0(s, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_fildl_ST0(s, s->tmp2_i32); + break; + } + break; + case 1: + /* XXX: the corresponding CPUID bit must be tested ! */ + switch (op >> 4) { + case 1: + 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, 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, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + gen_fpop(s); + break; + default: + switch (op >> 4) { + case 0: + gen_fsts_ST0(s, s->tmp2_i32); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 1: + gen_fistl_ST0(s, s->tmp2_i32); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 2: + gen_fstl_ST0(s, s->tmp1_i64); + 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, tcg_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + if ((op & 7) == 3) { + gen_fpop(s); + } break; } - disas_insn_new(s, cpu, b); - return s->pc; + break; + case 0x0c: /* fldenv mem */ + gen_helper_fldenv(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + gen_update_eip_next(s); + gen_eob(s, DISAS_EOB_ONLY); + break; + case 0x0d: /* fldcw mem */ + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + gen_helper_fldcw(tcg_env, s->tmp2_i32); + update_fip = update_fdp = false; + gen_update_eip_next(s); + gen_eob(s, DISAS_EOB_ONLY); + break; + case 0x0e: /* fnstenv mem */ + gen_helper_fstenv(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x0f: /* fnstcw mem */ + 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(tcg_env, s->A0); + break; + case 0x1f: /* fstpt mem */ + gen_helper_fstt_ST0(tcg_env, s->A0); + gen_fpop(s); + break; + case 0x2c: /* frstor mem */ + gen_helper_frstor(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x2e: /* fnsave mem */ + gen_helper_fsave(tcg_env, s->A0, + tcg_constant_i32(s->dflag - 1)); + update_fip = update_fdp = false; + break; + case 0x2f: /* fnstsw mem */ + 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(tcg_env, s->A0); + break; + case 0x3e: /* fbstp */ + gen_helper_fbst_ST0(tcg_env, s->A0); + gen_fpop(s); + break; + case 0x3d: /* fildll */ + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_fildll_ST0(s, s->tmp1_i64); + break; + case 0x3f: /* fistpll */ + gen_fistll_ST0(s, s->tmp1_i64); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEUQ); + gen_fpop(s); + break; + default: + goto illegal_op; } - 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. */ - dflag = (REX_W(s) ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32); - /* In 64-bit mode, 0x67 selects 32-bit addressing. */ - aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64); + if (update_fdp) { + int last_seg = s->override >= 0 ? s->override : decode->mem.def_seg; + + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, + segs[last_seg].selector)); + tcg_gen_st16_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, fpds)); + tcg_gen_st_tl(last_addr, tcg_env, + offsetof(CPUX86State, fpdp)); + } } else { - /* In 16/32-bit mode, 0x66 selects the opposite data size. */ - if (CODE32(s) ^ ((prefixes & PREFIX_DATA) != 0)) { - dflag = MO_32; - } else { - dflag = MO_16; - } - /* In 16/32-bit mode, 0x67 selects the opposite addressing. */ - if (CODE32(s) ^ ((prefixes & PREFIX_ADR) != 0)) { - aflag = MO_32; - } else { - aflag = MO_16; + /* register float ops */ + int opreg = rm; + + switch (op) { + case 0x08: /* fld sti */ + gen_fpush(s); + gen_fmov_ST0_STN(s, (opreg + 1) & 7); + break; + case 0x09: /* fxchg sti */ + case 0x29: /* fxchg4 sti, undocumented op */ + case 0x39: /* fxchg7 sti, undocumented op */ + gen_fxchg_ST0_STN(s, opreg); + break; + case 0x0a: /* grp d9/2 */ + switch (rm) { + case 0: /* fnop */ + /* + * 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: + goto illegal_op; + } + break; + case 0x0c: /* grp d9/4 */ + switch (rm) { + case 0: /* fchs */ + gen_fchs_ST0(s); + break; + case 1: /* fabs */ + gen_fabs_ST0(s); + break; + case 4: /* ftst */ + gen_fldz_FT0(s); + gen_fcom_ST0_FT0(s); + break; + case 5: /* fxam */ + gen_helper_fxam_ST0(tcg_env); + break; + default: + goto illegal_op; + } + break; + case 0x0d: /* grp d9/5 */ + { + switch (rm) { + case 0: + gen_fpush(s); + gen_fld1_ST0(s); + break; + case 1: + gen_fpush(s); + gen_helper_fldl2t_ST0(tcg_env); + break; + case 2: + gen_fpush(s); + gen_helper_fldl2e_ST0(tcg_env); + break; + case 3: + gen_fpush(s); + gen_helper_fldpi_ST0(tcg_env); + break; + case 4: + gen_fpush(s); + gen_helper_fldlg2_ST0(tcg_env); + break; + case 5: + gen_fpush(s); + gen_helper_fldln2_ST0(tcg_env); + break; + case 6: + gen_fpush(s); + gen_fldz_ST0(s); + break; + default: + goto illegal_op; + } + } + break; + case 0x0e: /* grp d9/6 */ + switch (rm) { + case 0: /* f2xm1 */ + gen_helper_f2xm1(tcg_env); + break; + case 1: /* fyl2x */ + gen_helper_fyl2x(tcg_env); + break; + case 2: /* fptan */ + gen_helper_fptan(tcg_env); + break; + case 3: /* fpatan */ + gen_helper_fpatan(tcg_env); + break; + case 4: /* fxtract */ + gen_helper_fxtract(tcg_env); + break; + case 5: /* fprem1 */ + gen_helper_fprem1(tcg_env); + break; + case 6: /* fdecstp */ + gen_helper_fdecstp(tcg_env); + break; + default: + case 7: /* fincstp */ + gen_helper_fincstp(tcg_env); + break; + } + break; + case 0x0f: /* grp d9/7 */ + switch (rm) { + case 0: /* fprem */ + gen_helper_fprem(tcg_env); + break; + case 1: /* fyl2xp1 */ + gen_helper_fyl2xp1(tcg_env); + break; + case 2: /* fsqrt */ + gen_fsqrt(s); + break; + case 3: /* fsincos */ + gen_helper_fsincos(tcg_env); + break; + case 5: /* fscale */ + gen_helper_fscale(tcg_env); + break; + case 4: /* frndint */ + gen_helper_frndint(tcg_env); + break; + case 6: /* fsin */ + gen_fsin(s); + break; + default: + case 7: /* fcos */ + gen_fcos(s); + break; + } + break; + case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ + case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ + case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ + { + int op1; + + op1 = op & 7; + if (op >= 0x20) { + gen_helper_fp_arith_STN_ST0(s, op1, opreg); + if (op >= 0x30) { + gen_fpop(s); + } + } else { + gen_fmov_FT0_STN(s, opreg); + gen_helper_fp_arith_ST0_FT0(s, op1); + } + } + break; + case 0x02: /* fcom */ + case 0x22: /* fcom2, undocumented op */ + gen_fmov_FT0_STN(s, opreg); + gen_fcom_ST0_FT0(s); + break; + case 0x03: /* fcomp */ + case 0x23: /* fcomp3, undocumented op */ + case 0x32: /* fcomp5, undocumented op */ + gen_fmov_FT0_STN(s, opreg); + gen_fcom_ST0_FT0(s); + gen_fpop(s); + break; + case 0x15: /* da/5 */ + switch (rm) { + case 1: /* fucompp */ + gen_fmov_FT0_STN(s, 1); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_fpop(s); + gen_fpop(s); + break; + default: + goto illegal_op; + } + break; + case 0x1c: + switch (rm) { + case 0: /* feni (287 only, just do nop here) */ + break; + case 1: /* fdisi (287 only, just do nop here) */ + break; + case 2: /* fclex */ + gen_helper_fclex(tcg_env); + update_fip = false; + break; + case 3: /* fninit */ + gen_helper_fninit(tcg_env); + update_fip = false; + gen_update_eip_next(s); + gen_eob(s, DISAS_EOB_ONLY); + break; + case 4: /* fsetpm (287 only, just do nop here) */ + break; + default: + goto illegal_op; + } + break; + case 0x1d: /* fucomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_fmov_FT0_STN(s, opreg); + gen_helper_fucomi_ST0_FT0(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); + break; + case 0x1e: /* fcomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_fmov_FT0_STN(s, opreg); + gen_helper_fcomi_ST0_FT0(tcg_env); + assume_cc_op(s, CC_OP_EFLAGS); + break; + case 0x28: /* ffree sti */ + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); + break; + case 0x2a: /* fst sti */ + gen_fmov_STN_ST0(s, 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_fmov_STN_ST0(s, opreg); + gen_fpop(s); + break; + case 0x2c: /* fucom st(i) */ + gen_fmov_FT0_STN(s, opreg); + gen_helper_fucom_ST0_FT0(tcg_env); + break; + case 0x2d: /* fucomp st(i) */ + gen_fmov_FT0_STN(s, opreg); + gen_helper_fucom_ST0_FT0(tcg_env); + gen_fpop(s); + break; + case 0x33: /* de/3 */ + switch (rm) { + case 1: /* fcompp */ + gen_fmov_FT0_STN(s, 1); + gen_fcom_ST0_FT0(s); + gen_fpop(s); + gen_fpop(s); + break; + default: + goto illegal_op; + } + break; + case 0x38: /* ffreep sti, undocumented op */ + gen_helper_ffree_STN(tcg_env, tcg_constant_i32(opreg)); + gen_fpop(s); + break; + case 0x3c: /* df/4 */ + switch (rm) { + case 0: + 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; + default: + goto illegal_op; + } + break; + case 0x3d: /* fucomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_fmov_FT0_STN(s, opreg); + gen_helper_fucomi_ST0_FT0(tcg_env); + gen_fpop(s); + assume_cc_op(s, CC_OP_EFLAGS); + break; + case 0x3e: /* fcomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_fmov_FT0_STN(s, opreg); + gen_helper_fcomi_ST0_FT0(tcg_env); + gen_fpop(s); + assume_cc_op(s, CC_OP_EFLAGS); + break; + case 0x10 ... 0x13: /* fcmovxx */ + case 0x18 ... 0x1b: + { + int op1; + TCGLabel *l1; + static const uint8_t fcmov_cc[8] = { + (JCC_B << 1), + (JCC_Z << 1), + (JCC_BE << 1), + (JCC_P << 1), + }; + + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); + l1 = gen_new_label(); + gen_jcc1_noeob(s, op1, l1); + gen_fmov_ST0_STN(s, opreg); + gen_set_label(l1); + } + break; + default: + goto illegal_op; } } - s->prefix = prefixes; - s->aflag = aflag; - s->dflag = dflag; + if (update_fip) { + tcg_gen_ld_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, segs[R_CS].selector)); + tcg_gen_st16_i32(s->tmp2_i32, tcg_env, + offsetof(CPUX86State, fpcs)); + tcg_gen_st_tl(eip_cur_tl(s), + tcg_env, offsetof(CPUX86State, fpip)); + } + return; + + illegal_op: + gen_illegal_opcode(s); +} + +static void gen_multi0F(DisasContext *s, X86DecodedInsn *decode) +{ + int prefixes = s->prefix; + MemOp dflag = s->dflag; + int b = decode->b + 0x100; + int modrm = s->modrm; + MemOp ot; + int reg, rm, mod, op; /* now check op code */ switch (b) { - /**************************/ - /* arith & logic */ - case 0x00 ... 0x05: - case 0x08 ... 0x0d: - case 0x10 ... 0x15: - case 0x18 ... 0x1d: - case 0x20 ... 0x25: - case 0x28 ... 0x2d: - case 0x30 ... 0x35: - case 0x38 ... 0x3d: - { - int op, f, val; - op = (b >> 3) & 7; - f = (b >> 1) & 3; - - ot = mo_b_d(b, dflag); - - switch(f) { - case 0: /* OP Ev, Gv */ - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else if (op == OP_XORL && rm == reg) { - xor_zero: - /* xor reg, reg optimisation */ - set_cc_op(s, CC_OP_CLR); - tcg_gen_movi_tl(s->T0, 0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - } else { - opreg = rm; - } - gen_op_mov_v_reg(s, ot, s->T1, reg); - gen_op(s, op, ot, opreg); - break; - case 1: /* OP Gv, Ev */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - reg = ((modrm >> 3) & 7) | REX_R(s); - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, s->T1, s->A0); - } else if (op == OP_XORL && rm == reg) { - goto xor_zero; - } else { - gen_op_mov_v_reg(s, ot, s->T1, rm); - } - gen_op(s, op, ot, reg); - break; - case 2: /* OP A, Iv */ - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, OR_EAX); - break; - } - } - break; - - case 0x82: - if (CODE64(s)) - goto illegal_op; - /* fall through */ - case 0x80: /* GRP1 */ - case 0x81: - case 0x83: - { - int val; - - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - - if (mod != 3) { - if (b == 0x83) - s->rip_offset = 1; - else - s->rip_offset = insn_const_size(ot); - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = rm; - } - - switch(b) { - default: - case 0x80: - case 0x81: - case 0x82: - val = insn_get(env, s, ot); - break; - case 0x83: - val = (int8_t)insn_get(env, s, MO_8); - break; - } - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, opreg); - } - break; - - /**************************/ - /* inc, dec, and other misc arith */ - case 0x40 ... 0x47: /* inc Gv */ - ot = dflag; - gen_inc(s, ot, OR_EAX + (b & 7), 1); - break; - case 0x48 ... 0x4f: /* dec Gv */ - ot = dflag; - gen_inc(s, ot, OR_EAX + (b & 7), -1); - break; - case 0xf6: /* GRP3 */ - case 0xf7: - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - if (mod != 3) { - if (op == 0) { - s->rip_offset = insn_const_size(ot); - } - gen_lea_modrm(env, s, modrm); - /* For those below that handle locked memory, don't load here. */ - if (!(s->prefix & PREFIX_LOCK) - || op != 2) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - - switch(op) { - case 0: /* test */ - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - case 2: /* not */ - if (s->prefix & PREFIX_LOCK) { - if (mod == 3) { - goto illegal_op; - } - tcg_gen_movi_tl(s->T0, ~0); - tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, - s->mem_index, ot | MO_LE); - } else { - tcg_gen_not_tl(s->T0, s->T0); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - break; - case 3: /* neg */ - if (s->prefix & PREFIX_LOCK) { - TCGLabel *label1; - TCGv a0, t0, t1, t2; - - if (mod == 3) { - goto illegal_op; - } - a0 = tcg_temp_local_new(); - t0 = tcg_temp_local_new(); - 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(); - tcg_gen_mov_tl(t2, t0); - 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_neg_tl(s->T0, t0); - tcg_temp_free(t0); - } else { - tcg_gen_neg_tl(s->T0, s->T0); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - gen_op_update_neg_cc(s); - set_cc_op(s, CC_OP_SUBB + ot); - break; - case 4: /* mul */ - switch(ot) { - case MO_8: - gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); - tcg_gen_ext8u_tl(s->T0, s->T0); - tcg_gen_ext8u_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_andi_tl(cpu_cc_src, s->T0, 0xff00); - set_cc_op(s, CC_OP_MULB); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); - tcg_gen_ext16u_tl(s->T0, s->T0); - tcg_gen_ext16u_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_shri_tl(s->T0, s->T0, 16); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - tcg_gen_mov_tl(cpu_cc_src, s->T0); - set_cc_op(s, CC_OP_MULW); - break; - default: - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULL); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - s->T0, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULQ); - break; -#endif - } - break; - case 5: /* imul */ - switch(ot) { - case MO_8: - gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); - tcg_gen_ext8s_tl(s->T0, s->T0); - tcg_gen_ext8s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext8s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - set_cc_op(s, CC_OP_MULB); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_ext16s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext16s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - tcg_gen_shri_tl(s->T0, s->T0, 16); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - set_cc_op(s, CC_OP_MULW); - break; - default: - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); - tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); - tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); - set_cc_op(s, CC_OP_MULL); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - s->T0, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULQ); - break; -#endif - } - break; - case 6: /* div */ - switch(ot) { - case MO_8: - gen_helper_divb_AL(cpu_env, s->T0); - break; - case MO_16: - gen_helper_divw_AX(cpu_env, s->T0); - break; - default: - case MO_32: - gen_helper_divl_EAX(cpu_env, s->T0); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_divq_EAX(cpu_env, s->T0); - break; -#endif - } - break; - case 7: /* idiv */ - switch(ot) { - case MO_8: - gen_helper_idivb_AL(cpu_env, s->T0); - break; - case MO_16: - gen_helper_idivw_AX(cpu_env, s->T0); - break; - default: - case MO_32: - gen_helper_idivl_EAX(cpu_env, s->T0); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_idivq_EAX(cpu_env, s->T0); - break; -#endif - } - break; - default: - goto unknown_op; - } - break; - - case 0xfe: /* GRP4 */ - case 0xff: /* GRP5 */ - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - if (op >= 2 && b == 0xfe) { - goto unknown_op; - } - if (CODE64(s)) { - if (op == 2 || op == 4) { - /* operand size for jumps is 64 bit */ - ot = MO_64; - } else if (op == 3 || op == 5) { - ot = dflag != MO_16 ? MO_32 + REX_W(s) : MO_16; - } else if (op == 6) { - /* default push size is 64 bit */ - ot = mo_pushpop(s, dflag); - } - } - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - if (op >= 2 && op != 3 && op != 5) - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - - switch(op) { - case 0: /* inc Ev */ - if (mod != 3) - opreg = OR_TMP0; - else - opreg = rm; - gen_inc(s, ot, opreg, 1); - break; - case 1: /* dec Ev */ - if (mod != 3) - opreg = OR_TMP0; - else - opreg = rm; - gen_inc(s, ot, opreg, -1); - break; - case 2: /* call Ev */ - /* XXX: optimize if memory (no 'and' is necessary) */ - if (dflag == MO_16) { - tcg_gen_ext16u_tl(s->T0, s->T0); - } - gen_push_v(s, eip_next_tl(s)); - gen_op_jmp_v(s, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; - break; - case 3: /* lcall Ev */ - if (mod == 3) { - goto illegal_op; - } - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T0, s->A0); - 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_constant_i32(dflag - 1), - eip_next_tl(s)); - } else { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_lcall_real(cpu_env, s->tmp2_i32, s->tmp3_i32, - tcg_constant_i32(dflag - 1), - eip_next_i32(s)); - } - 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, s->T0); - gen_bnd_jmp(s); - s->base.is_jmp = DISAS_JUMP; - break; - case 5: /* ljmp Ev */ - if (mod == 3) { - goto illegal_op; - } - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T0, s->A0); - 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, - eip_next_tl(s)); - } else { - gen_op_movl_seg_T0_vm(s, R_CS); - gen_op_jmp_v(s, s->T1); - } - s->base.is_jmp = DISAS_JUMP; - break; - case 6: /* push Ev */ - gen_push_v(s, s->T0); - break; - default: - goto unknown_op; - } - break; - - case 0x84: /* test Ev, Gv */ - case 0x85: - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_v_reg(s, ot, s->T1, reg); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0xa8: /* test eAX, Iv */ - case 0xa9: - ot = mo_b_d(b, dflag); - val = insn_get(env, s, ot); - - gen_op_mov_v_reg(s, ot, s->T0, OR_EAX); - tcg_gen_movi_tl(s->T1, val); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0x98: /* CWDE/CBW */ - switch (dflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - tcg_gen_ext32s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_64, R_EAX, s->T0); - break; -#endif - case MO_32: - gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_32, R_EAX, s->T0); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_8, s->T0, R_EAX); - tcg_gen_ext8s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - break; - default: - tcg_abort(); - } - break; - case 0x99: /* CDQ/CWD */ - switch (dflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_mov_v_reg(s, MO_64, s->T0, R_EAX); - tcg_gen_sari_tl(s->T0, s->T0, 63); - gen_op_mov_reg_v(s, MO_64, R_EDX, s->T0); - break; -#endif - case MO_32: - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - tcg_gen_ext32s_tl(s->T0, s->T0); - tcg_gen_sari_tl(s->T0, s->T0, 31); - gen_op_mov_reg_v(s, MO_32, R_EDX, s->T0); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_sari_tl(s->T0, s->T0, 15); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - break; - default: - tcg_abort(); - } - break; - case 0x1af: /* imul Gv, Ev */ - case 0x69: /* imul Gv, Ev, I */ - case 0x6b: - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - if (b == 0x69) - s->rip_offset = insn_const_size(ot); - else if (b == 0x6b) - s->rip_offset = 1; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - if (b == 0x69) { - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - } else if (b == 0x6b) { - val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T1, val); - } else { - gen_op_mov_v_reg(s, ot, s->T1, reg); - } - switch (ot) { -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_muls2_i64(cpu_regs[reg], s->T1, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, s->T1); - break; -#endif - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); - break; - default: - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_ext16s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext16s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - } - set_cc_op(s, CC_OP_MULB + ot); - break; - case 0x1c0: - case 0x1c1: /* xadd Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - gen_op_mov_v_reg(s, ot, s->T0, reg); - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, ot, s->T1, rm); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, ot, reg, s->T1); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - if (s->prefix & PREFIX_LOCK) { - tcg_gen_atomic_fetch_add_tl(s->T1, s->A0, s->T0, - s->mem_index, ot | MO_LE); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - } else { - gen_op_ld_v(s, ot, s->T1, s->A0); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - gen_op_st_v(s, ot, s->T0, s->A0); - } - gen_op_mov_reg_v(s, ot, reg, s->T1); - } - gen_op_update2_cc(s); - set_cc_op(s, CC_OP_ADDB + ot); - break; - case 0x1b0: - case 0x1b1: /* cmpxchg Ev, Gv */ - { - TCGv oldv, newv, cmpv, dest; - - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - oldv = tcg_temp_new(); - newv = tcg_temp_new(); - 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; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, - s->mem_index, ot | MO_LE); - } 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); - - /* - * 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); - } - } - /* - * 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 */ - modrm = x86_ldub_code(env, s); + case 0x1c7: /* RDSEED, RDPID with f3 prefix */ mod = (modrm >> 6) & 3; switch ((modrm >> 3) & 7) { - case 1: /* CMPXCHG8, CMPXCHG16 */ - if (mod == 3) { + case 7: + if (mod != 3 || + (s->prefix & PREFIX_REPNZ)) { goto illegal_op; } -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) { + if (s->prefix & PREFIX_REPZ) { + if (!(s->cpuid_7_0_ecx_features & CPUID_7_0_ECX_RDPID)) { 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_helper_rdpid(s->T0, tcg_env); + rm = (modrm & 7) | REX_B(s); + gen_op_mov_reg_v(s, dflag, rm, s->T0); break; - } -#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); + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_RDSEED)) { + goto illegal_op; + } + goto do_rdrand; } - set_cc_op(s, CC_OP_EFLAGS); - break; - case 7: /* RDSEED */ case 6: /* RDRAND */ if (mod != 3 || - (s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) || + (s->prefix & (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(); - s->base.is_jmp = DISAS_TOO_MANY; - } - 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); + assume_cc_op(s, CC_OP_EFLAGS); break; default: @@ -4376,1847 +3420,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } break; - /**************************/ - /* push/pop */ - case 0x50 ... 0x57: /* push */ - gen_op_mov_v_reg(s, MO_32, s->T0, (b & 7) | REX_B(s)); - gen_push_v(s, s->T0); - break; - case 0x58 ... 0x5f: /* pop */ - ot = gen_pop_T0(s); - /* NOTE: order is important for pop %sp */ - gen_pop_update(s, ot); - gen_op_mov_reg_v(s, ot, (b & 7) | REX_B(s), s->T0); - break; - case 0x60: /* pusha */ - if (CODE64(s)) - goto illegal_op; - gen_pusha(s); - break; - case 0x61: /* popa */ - if (CODE64(s)) - goto illegal_op; - gen_popa(s); - break; - case 0x68: /* push Iv */ - case 0x6a: - ot = mo_pushpop(s, dflag); - if (b == 0x68) - val = insn_get(env, s, ot); - else - val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T0, val); - gen_push_v(s, s->T0); - break; - case 0x8f: /* pop Ev */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - ot = gen_pop_T0(s); - if (mod == 3) { - /* NOTE: order is important for pop %sp */ - gen_pop_update(s, ot); - rm = (modrm & 7) | REX_B(s); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - /* NOTE: order is important too for MMU exceptions */ - s->popl_esp_hack = 1 << ot; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - s->popl_esp_hack = 0; - gen_pop_update(s, ot); - } - break; - case 0xc8: /* enter */ - { - int level; - val = x86_lduw_code(env, s); - level = x86_ldub_code(env, s); - gen_enter(s, val, level); - } - break; - case 0xc9: /* leave */ - gen_leave(s); - break; - case 0x06: /* push es */ - case 0x0e: /* push cs */ - case 0x16: /* push ss */ - case 0x1e: /* push ds */ - if (CODE64(s)) - goto illegal_op; - gen_op_movl_T0_seg(s, b >> 3); - gen_push_v(s, s->T0); - break; - case 0x1a0: /* push fs */ - case 0x1a8: /* push gs */ - gen_op_movl_T0_seg(s, (b >> 3) & 7); - gen_push_v(s, s->T0); - break; - case 0x07: /* pop es */ - case 0x17: /* pop ss */ - case 0x1f: /* pop ds */ - if (CODE64(s)) - goto illegal_op; - reg = b >> 3; - ot = gen_pop_T0(s); - gen_movl_seg_T0(s, reg); - gen_pop_update(s, ot); - 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); - break; - - /**************************/ - /* mov */ - case 0x88: - case 0x89: /* mov Gv, Ev */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - /* generate a generic store */ - gen_ldst_modrm(env, s, modrm, ot, reg, 1); - break; - case 0xc6: - case 0xc7: /* mov Ev, Iv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod != 3) { - s->rip_offset = insn_const_size(ot); - gen_lea_modrm(env, s, modrm); - } - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T0, val); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, (modrm & 7) | REX_B(s), s->T0); - } - break; - case 0x8a: - case 0x8b: /* mov Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0x8e: /* mov seg, Gv */ - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - if (reg >= 6 || reg == R_CS) - goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_movl_seg_T0(s, reg); - break; - case 0x8c: /* mov Gv, seg */ - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - if (reg >= 6) - goto illegal_op; - gen_op_movl_T0_seg(s, reg); - ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - break; - - case 0x1b6: /* movzbS Gv, Eb */ - case 0x1b7: /* movzwS Gv, Eb */ - case 0x1be: /* movsbS Gv, Eb */ - case 0x1bf: /* movswS Gv, Eb */ - { - MemOp d_ot; - MemOp s_ot; - - /* d_ot is the size of destination */ - d_ot = dflag; - /* ot is the size of source */ - ot = (b & 1) + MO_8; - /* s_ot is the sign+size of source */ - s_ot = b & 8 ? MO_SIGN | ot : ot; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - - if (mod == 3) { - if (s_ot == MO_SB && byte_reg_is_xH(s, rm)) { - tcg_gen_sextract_tl(s->T0, cpu_regs[rm - 4], 8, 8); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - switch (s_ot) { - case MO_UB: - tcg_gen_ext8u_tl(s->T0, s->T0); - break; - case MO_SB: - tcg_gen_ext8s_tl(s->T0, s->T0); - break; - case MO_UW: - tcg_gen_ext16u_tl(s->T0, s->T0); - break; - default: - case MO_SW: - tcg_gen_ext16s_tl(s->T0, s->T0); - break; - } - } - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, s_ot, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } - } - break; - - case 0x8d: /* lea */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - reg = ((modrm >> 3) & 7) | REX_R(s); - { - AddressParts a = gen_lea_modrm_0(env, s, modrm); - 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); - } - break; - - case 0xa0: /* mov EAX, Ov */ - case 0xa1: - case 0xa2: /* mov Ov, EAX */ - case 0xa3: - { - target_ulong offset_addr; - - ot = mo_b_d(b, dflag); - 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) { - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_op_mov_reg_v(s, ot, R_EAX, s->T0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, R_EAX); - gen_op_st_v(s, ot, s->T0, s->A0); - } - } - break; - case 0xd7: /* xlat */ - 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); - break; - case 0xb0 ... 0xb7: /* mov R, Ib */ - val = insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T0, val); - gen_op_mov_reg_v(s, MO_8, (b & 7) | REX_B(s), s->T0); - break; - case 0xb8 ... 0xbf: /* mov R, Iv */ -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - uint64_t tmp; - /* 64 bit case */ - tmp = x86_ldq_code(env, s); - reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(s->T0, tmp); - gen_op_mov_reg_v(s, MO_64, reg, s->T0); - } else -#endif - { - ot = dflag; - val = insn_get(env, s, ot); - reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(s->T0, val); - gen_op_mov_reg_v(s, ot, reg, s->T0); - } - break; - - case 0x91 ... 0x97: /* xchg R, EAX */ - do_xchg_reg_eax: - ot = dflag; - reg = (b & 7) | REX_B(s); - rm = R_EAX; - goto do_xchg_reg; - case 0x86: - case 0x87: /* xchg Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - do_xchg_reg: - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_mov_v_reg(s, ot, s->T1, rm); - gen_op_mov_reg_v(s, ot, rm, s->T0); - gen_op_mov_reg_v(s, ot, reg, s->T1); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(s, ot, s->T0, reg); - /* for xchg, lock is implicit */ - tcg_gen_atomic_xchg_tl(s->T1, s->A0, s->T0, - s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(s, ot, reg, s->T1); - } - break; - case 0xc4: /* les Gv */ - /* In CODE64 this is VEX3; see above. */ - op = R_ES; - goto do_lxx; - case 0xc5: /* lds Gv */ - /* In CODE64 this is VEX2; see above. */ - op = R_DS; - goto do_lxx; - case 0x1b2: /* lss Gv */ - op = R_SS; - goto do_lxx; - case 0x1b4: /* lfs Gv */ - op = R_FS; - goto do_lxx; - case 0x1b5: /* lgs Gv */ - op = R_GS; - do_lxx: - ot = dflag != MO_16 ? MO_32 : MO_16; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - /* load the segment first to handle exceptions properly */ - gen_op_ld_v(s, MO_16, s->T0, s->A0); - gen_movl_seg_T0(s, op); - /* then put the data */ - gen_op_mov_reg_v(s, ot, reg, s->T1); - break; - - /************************/ - /* shifts */ - case 0xc0: - case 0xc1: - /* shift Ev,Ib */ - shift = 2; - grp2: - { - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - - if (mod != 3) { - if (shift == 2) { - s->rip_offset = 1; - } - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = (modrm & 7) | REX_B(s); - } - - /* simpler op */ - if (shift == 0) { - gen_shift(s, op, ot, opreg, OR_ECX); - } else { - if (shift == 2) { - shift = x86_ldub_code(env, s); - } - gen_shifti(s, op, ot, opreg, shift); - } - } - break; - case 0xd0: - case 0xd1: - /* shift Ev,1 */ - shift = 1; - goto grp2; - case 0xd2: - case 0xd3: - /* shift Ev,cl */ - shift = 0; - goto grp2; - - case 0x1a4: /* shld imm */ - op = 0; - shift = 1; - goto do_shiftd; - case 0x1a5: /* shld cl */ - op = 0; - shift = 0; - goto do_shiftd; - case 0x1ac: /* shrd imm */ - op = 1; - shift = 1; - goto do_shiftd; - case 0x1ad: /* shrd cl */ - op = 1; - shift = 0; - do_shiftd: - ot = dflag; - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | REX_R(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = rm; - } - gen_op_mov_v_reg(s, ot, s->T1, reg); - - if (shift) { - TCGv imm = tcg_const_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]); - } - break; - - /************************/ - /* floats */ - case 0xd8 ... 0xdf: - { - bool update_fip = true; - - 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); - break; - } - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = modrm & 7; - op = ((b & 7) << 3) | ((modrm >> 3) & 7); - if (mod != 3) { - /* memory op */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a, false); - TCGv last_addr = tcg_temp_new(); - bool update_fdp = true; - - tcg_gen_mov_tl(last_addr, ea); - gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); - - switch (op) { - case 0x00 ... 0x07: /* fxxxs */ - case 0x10 ... 0x17: /* fixxxl */ - case 0x20 ... 0x27: /* fxxxl */ - case 0x30 ... 0x37: /* fixxx */ - { - int op1; - op1 = op & 7; - - switch (op >> 4) { - case 0: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_flds_FT0(s, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_fildl_FT0(s, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_fldl_FT0(s, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_fildl_FT0(s, s->tmp2_i32); - break; - } - - gen_helper_fp_arith_ST0_FT0(s, op1); - if (op1 == 3) { - /* fcomp needs pop */ - gen_fpop(s); - } - } - break; - case 0x08: /* flds */ - case 0x0a: /* fsts */ - case 0x0b: /* fstps */ - case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ - case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ - case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ - switch (op & 7) { - case 0: - switch (op >> 4) { - case 0: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_flds_ST0(s, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_fildl_ST0(s, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_fldl_ST0(s, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_fildl_ST0(s, s->tmp2_i32); - break; - } - break; - case 1: - /* XXX: the corresponding CPUID bit must be tested ! */ - switch (op >> 4) { - case 1: - gen_helper_fisttl_ST0(s->tmp2_i32, cpu_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); - 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); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - } - gen_fpop(s); - break; - default: - switch (op >> 4) { - case 0: - gen_fsts_ST0(s, s->tmp2_i32); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 1: - gen_fistl_ST0(s, s->tmp2_i32); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 2: - gen_fstl_ST0(s, s->tmp1_i64); - 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); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - } - if ((op & 7) == 3) { - gen_fpop(s); - } - break; - } - break; - case 0x0c: /* fldenv mem */ - gen_helper_fldenv(cpu_env, s->A0, - tcg_const_i32(dflag - 1)); - update_fip = update_fdp = false; - gen_update_eip_next(s); - gen_eob(s); - 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); - update_fip = update_fdp = false; - gen_update_eip_next(s); - gen_eob(s); - break; - case 0x0e: /* fnstenv mem */ - gen_helper_fstenv(cpu_env, s->A0, - tcg_const_i32(dflag - 1)); - update_fip = update_fdp = false; - break; - case 0x0f: /* fnstcw mem */ - gen_helper_fnstcw(s->tmp2_i32, cpu_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); - break; - case 0x1f: /* fstpt mem */ - gen_helper_fstt_ST0(cpu_env, s->A0); - gen_fpop(s); - break; - case 0x2c: /* frstor mem */ - gen_helper_frstor(cpu_env, s->A0, - tcg_const_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)); - update_fip = update_fdp = false; - break; - case 0x2f: /* fnstsw mem */ - gen_helper_fnstsw(s->tmp2_i32, cpu_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); - break; - case 0x3e: /* fbstp */ - gen_helper_fbst_ST0(cpu_env, s->A0); - gen_fpop(s); - break; - case 0x3d: /* fildll */ - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_fildll_ST0(s, s->tmp1_i64); - break; - case 0x3f: /* fistpll */ - gen_fistll_ST0(s, s->tmp1_i64); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEUQ); - gen_fpop(s); - break; - default: - goto unknown_op; - } - - if (update_fdp) { - int last_seg = s->override >= 0 ? s->override : a.def_seg; - - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, - segs[last_seg].selector)); - tcg_gen_st16_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, fpds)); - tcg_gen_st_tl(last_addr, cpu_env, - offsetof(CPUX86State, fpdp)); - } - tcg_temp_free(last_addr); - } else { - /* register float ops */ - opreg = rm; - - switch (op) { - case 0x08: /* fld sti */ - gen_fpush(s); - gen_fmov_ST0_STN(s, (opreg + 1) & 7); - break; - case 0x09: /* fxchg sti */ - case 0x29: /* fxchg4 sti, undocumented op */ - case 0x39: /* fxchg7 sti, undocumented op */ - gen_fxchg_ST0_STN(s, opreg); - break; - case 0x0a: /* grp d9/2 */ - switch (rm) { - case 0: /* fnop */ - /* check exceptions (FreeBSD FPU probe) */ - gen_helper_fwait(cpu_env); - update_fip = false; - break; - default: - goto unknown_op; - } - break; - case 0x0c: /* grp d9/4 */ - switch (rm) { - case 0: /* fchs */ - gen_fchs_ST0(s); - break; - case 1: /* fabs */ - gen_fabs_ST0(s); - break; - case 4: /* ftst */ - gen_fldz_FT0(s); - gen_fcom_ST0_FT0(s); - break; - case 5: /* fxam */ - gen_helper_fxam_ST0(cpu_env); - break; - default: - goto unknown_op; - } - break; - case 0x0d: /* grp d9/5 */ - { - switch (rm) { - case 0: - gen_fpush(s); - gen_fld1_ST0(s); - break; - case 1: - gen_fpush(s); - gen_helper_fldl2t_ST0(cpu_env); - break; - case 2: - gen_fpush(s); - gen_helper_fldl2e_ST0(cpu_env); - break; - case 3: - gen_fpush(s); - gen_helper_fldpi_ST0(cpu_env); - break; - case 4: - gen_fpush(s); - gen_helper_fldlg2_ST0(cpu_env); - break; - case 5: - gen_fpush(s); - gen_helper_fldln2_ST0(cpu_env); - break; - case 6: - gen_fpush(s); - gen_fldz_ST0(s); - break; - default: - goto unknown_op; - } - } - break; - case 0x0e: /* grp d9/6 */ - switch (rm) { - case 0: /* f2xm1 */ - gen_helper_f2xm1(cpu_env); - break; - case 1: /* fyl2x */ - gen_helper_fyl2x(cpu_env); - break; - case 2: /* fptan */ - gen_helper_fptan(cpu_env); - break; - case 3: /* fpatan */ - gen_helper_fpatan(cpu_env); - break; - case 4: /* fxtract */ - gen_helper_fxtract(cpu_env); - break; - case 5: /* fprem1 */ - gen_helper_fprem1(cpu_env); - break; - case 6: /* fdecstp */ - gen_helper_fdecstp(cpu_env); - break; - default: - case 7: /* fincstp */ - gen_helper_fincstp(cpu_env); - break; - } - break; - case 0x0f: /* grp d9/7 */ - switch (rm) { - case 0: /* fprem */ - gen_helper_fprem(cpu_env); - break; - case 1: /* fyl2xp1 */ - gen_helper_fyl2xp1(cpu_env); - break; - case 2: /* fsqrt */ - gen_fsqrt(s); - break; - case 3: /* fsincos */ - gen_helper_fsincos(cpu_env); - break; - case 5: /* fscale */ - gen_helper_fscale(cpu_env); - break; - case 4: /* frndint */ - gen_helper_frndint(cpu_env); - break; - case 6: /* fsin */ - gen_fsin(s); - break; - default: - case 7: /* fcos */ - gen_fcos(s); - break; - } - break; - case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ - case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ - case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ - { - int op1; - - op1 = op & 7; - if (op >= 0x20) { - gen_helper_fp_arith_STN_ST0(s, op1, opreg); - if (op >= 0x30) { - gen_fpop(s); - } - } else { - gen_fmov_FT0_STN(s, opreg); - gen_helper_fp_arith_ST0_FT0(s, op1); - } - } - break; - case 0x02: /* fcom */ - case 0x22: /* fcom2, undocumented op */ - gen_fmov_FT0_STN(s, opreg); - gen_fcom_ST0_FT0(s); - break; - case 0x03: /* fcomp */ - case 0x23: /* fcomp3, undocumented op */ - case 0x32: /* fcomp5, undocumented op */ - gen_fmov_FT0_STN(s, opreg); - gen_fcom_ST0_FT0(s); - gen_fpop(s); - break; - case 0x15: /* da/5 */ - switch (rm) { - case 1: /* fucompp */ - gen_fmov_FT0_STN(s, 1); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_fpop(s); - gen_fpop(s); - break; - default: - goto unknown_op; - } - break; - case 0x1c: - switch (rm) { - case 0: /* feni (287 only, just do nop here) */ - break; - case 1: /* fdisi (287 only, just do nop here) */ - break; - case 2: /* fclex */ - gen_helper_fclex(cpu_env); - update_fip = false; - break; - case 3: /* fninit */ - gen_helper_fninit(cpu_env); - update_fip = false; - gen_update_eip_next(s); - gen_eob(s); - break; - case 4: /* fsetpm (287 only, just do nop here) */ - break; - default: - goto unknown_op; - } - break; - case 0x1d: /* fucomi */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_fmov_FT0_STN(s, opreg); - gen_helper_fucomi_ST0_FT0(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x1e: /* fcomi */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_fmov_FT0_STN(s, opreg); - gen_helper_fcomi_ST0_FT0(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x28: /* ffree sti */ - gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); - break; - case 0x2a: /* fst sti */ - gen_fmov_STN_ST0(s, 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_fmov_STN_ST0(s, opreg); - gen_fpop(s); - break; - case 0x2c: /* fucom st(i) */ - gen_fmov_FT0_STN(s, opreg); - gen_helper_fucom_ST0_FT0(cpu_env); - break; - case 0x2d: /* fucomp st(i) */ - gen_fmov_FT0_STN(s, opreg); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_fpop(s); - break; - case 0x33: /* de/3 */ - switch (rm) { - case 1: /* fcompp */ - gen_fmov_FT0_STN(s, 1); - gen_fcom_ST0_FT0(s); - gen_fpop(s); - gen_fpop(s); - break; - default: - goto unknown_op; - } - break; - case 0x38: /* ffreep sti, undocumented op */ - gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); - gen_fpop(s); - break; - case 0x3c: /* df/4 */ - switch (rm) { - case 0: - gen_helper_fnstsw(s->tmp2_i32, cpu_env); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - break; - default: - goto unknown_op; - } - break; - case 0x3d: /* fucomip */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_fmov_FT0_STN(s, opreg); - gen_helper_fucomi_ST0_FT0(cpu_env); - gen_fpop(s); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x3e: /* fcomip */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_fmov_FT0_STN(s, opreg); - gen_helper_fcomi_ST0_FT0(cpu_env); - gen_fpop(s); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x10 ... 0x13: /* fcmovxx */ - case 0x18 ... 0x1b: - { - int op1; - TCGLabel *l1; - static const uint8_t fcmov_cc[8] = { - (JCC_B << 1), - (JCC_Z << 1), - (JCC_BE << 1), - (JCC_P << 1), - }; - - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); - l1 = gen_new_label(); - gen_jcc1_noeob(s, op1, l1); - gen_fmov_ST0_STN(s, opreg); - gen_set_label(l1); - } - break; - default: - goto unknown_op; - } - } - - if (update_fip) { - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, segs[R_CS].selector)); - tcg_gen_st16_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, fpcs)); - tcg_gen_st_tl(eip_cur_tl(s), - cpu_env, offsetof(CPUX86State, fpip)); - } - } - break; - /************************/ - /* string ops */ - - case 0xa4: /* movsS */ - case 0xa5: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_movs(s, ot); - } else { - gen_movs(s, ot); - } - break; - - case 0xaa: /* stosS */ - case 0xab: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_stos(s, ot); - } else { - gen_stos(s, ot); - } - break; - case 0xac: /* lodsS */ - case 0xad: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_lods(s, ot); - } else { - gen_lods(s, ot); - } - break; - case 0xae: /* scasS */ - case 0xaf: - ot = mo_b_d(b, dflag); - if (prefixes & PREFIX_REPNZ) { - gen_repz_scas(s, ot, 1); - } else if (prefixes & PREFIX_REPZ) { - gen_repz_scas(s, ot, 0); - } else { - gen_scas(s, ot); - } - break; - - case 0xa6: /* cmpsS */ - case 0xa7: - ot = mo_b_d(b, dflag); - if (prefixes & PREFIX_REPNZ) { - gen_repz_cmps(s, ot, 1); - } else if (prefixes & PREFIX_REPZ) { - gen_repz_cmps(s, ot, 0); - } else { - gen_cmps(s, ot); - } - break; - case 0x6c: /* insS */ - case 0x6d: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - if (!gen_check_io(s, ot, s->tmp2_i32, - SVM_IOIO_TYPE_MASK | SVM_IOIO_STR_MASK)) { - break; - } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_ins(s, ot); - } else { - gen_ins(s, ot); - } - break; - case 0x6e: /* outsS */ - case 0x6f: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - 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(); - s->base.is_jmp = DISAS_TOO_MANY; - } - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_outs(s, ot); - } else { - gen_outs(s, ot); - } - break; - - /************************/ - /* port I/O */ - - case 0xe4: - case 0xe5: - ot = mo_b_d32(b, dflag); - val = x86_ldub_code(env, s); - tcg_gen_movi_i32(s->tmp2_i32, val); - 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(); - s->base.is_jmp = DISAS_TOO_MANY; - } - 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); - break; - case 0xe6: - case 0xe7: - ot = mo_b_d32(b, dflag); - val = x86_ldub_code(env, s); - tcg_gen_movi_i32(s->tmp2_i32, val); - if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { - break; - } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } - 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); - break; - case 0xec: - case 0xed: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - 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(); - s->base.is_jmp = DISAS_TOO_MANY; - } - 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); - break; - case 0xee: - case 0xef: - ot = mo_b_d32(b, dflag); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_ext16u_i32(s->tmp2_i32, s->tmp2_i32); - if (!gen_check_io(s, ot, s->tmp2_i32, 0)) { - break; - } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } - 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); - break; - - /************************/ - /* control */ - case 0xc2: /* ret im */ - val = x86_ldsw_code(env, s); - 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, s->T0); - gen_bnd_jmp(s); - 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, s->T0); - gen_bnd_jmp(s); - 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_update_eip_cur(s); - gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1), - tcg_const_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, s->T0); - /* pop selector */ - gen_add_A0_im(s, 1 << dflag); - gen_op_ld_v(s, dflag, s->T0, s->A0); - gen_op_movl_seg_T0_vm(s, R_CS); - /* add stack offset */ - gen_stack_update(s, val + (2 << dflag)); - } - s->base.is_jmp = DISAS_EOB_ONLY; - break; - case 0xcb: /* lret */ - val = 0; - goto do_lret; - case 0xcf: /* iret */ - gen_svm_check_intercept(s, SVM_EXIT_IRET); - if (!PE(s) || VM86(s)) { - /* real mode or vm86 mode */ - if (!check_vm86_iopl(s)) { - break; - } - gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); - } else { - gen_helper_iret_protected(cpu_env, tcg_constant_i32(dflag - 1), - eip_next_i32(s)); - } - set_cc_op(s, CC_OP_EFLAGS); - s->base.is_jmp = DISAS_EOB_ONLY; - break; - case 0xe8: /* call im */ - { - 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_rel(s, dflag, diff, 0); - } - break; - case 0x9a: /* lcall im */ - { - unsigned int selector, offset; - - if (CODE64(s)) - goto illegal_op; - ot = dflag; - offset = insn_get(env, s, ot); - selector = insn_get(env, s, MO_16); - - tcg_gen_movi_tl(s->T0, selector); - tcg_gen_movi_tl(s->T1, offset); - } - goto do_lcall; - case 0xe9: /* jmp im */ - { - 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); - } - break; - case 0xea: /* ljmp im */ - { - unsigned int selector, offset; - - if (CODE64(s)) - goto illegal_op; - ot = dflag; - offset = insn_get(env, s, ot); - selector = insn_get(env, s, MO_16); - - tcg_gen_movi_tl(s->T0, selector); - tcg_gen_movi_tl(s->T1, offset); - } - goto do_ljmp; - case 0xeb: /* jmp Jb */ - { - int diff = (int8_t)insn_get(env, s, MO_8); - gen_jmp_rel(s, dflag, diff, 0); - } - break; - case 0x70 ... 0x7f: /* jcc Jb */ - { - 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 */ - { - 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); - } - break; - - case 0x190 ... 0x19f: /* setcc Gv */ - modrm = x86_ldub_code(env, s); - gen_setcc1(s, b, s->T0); - gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); - break; - case 0x140 ... 0x14f: /* cmov Gv, Ev */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_cmovcc1(env, s, ot, b, modrm, reg); - break; - - /************************/ - /* flags */ - case 0x9c: /* pushf */ - 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_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); - 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)); - } - } - } - gen_pop_update(s, ot); - set_cc_op(s, CC_OP_EFLAGS); - /* abort translation because TF/AC flag may change */ - 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; - 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); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); - break; - case 0x9f: /* lahf */ - if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) - goto illegal_op; - gen_compute_eflags(s); - /* Note: gen_compute_eflags() only gives the condition codes */ - tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); - tcg_gen_deposit_tl(cpu_regs[R_EAX], cpu_regs[R_EAX], s->T0, 8, 8); - break; - case 0xf5: /* cmc */ - gen_compute_eflags(s); - tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); - break; - case 0xf8: /* clc */ - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); - break; - case 0xf9: /* stc */ - gen_compute_eflags(s); - tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); - break; - case 0xfc: /* cld */ - tcg_gen_movi_i32(s->tmp2_i32, 1); - tcg_gen_st_i32(s->tmp2_i32, cpu_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)); - break; - - /************************/ - /* bit operations */ - case 0x1ba: /* bt/bts/btr/btc Gv, im */ - ot = dflag; - modrm = x86_ldub_code(env, s); - op = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - s->rip_offset = 1; - gen_lea_modrm(env, s, modrm); - if (!(s->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - /* load shift */ - val = x86_ldub_code(env, s); - tcg_gen_movi_tl(s->T1, val); - if (op < 4) - goto unknown_op; - op -= 4; - goto bt_op; - case 0x1a3: /* bt Gv, Ev */ - op = 0; - goto do_btx; - case 0x1ab: /* bts */ - op = 1; - goto do_btx; - case 0x1b3: /* btr */ - op = 2; - goto do_btx; - case 0x1bb: /* btc */ - op = 3; - do_btx: - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, MO_32, s->T1, reg); - if (mod != 3) { - AddressParts a = gen_lea_modrm_0(env, s, modrm); - /* specific case: we need to add a displacement */ - 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, 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); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - bt_op: - tcg_gen_andi_tl(s->T1, s->T1, (1 << (3 + ot)) - 1); - tcg_gen_movi_tl(s->tmp0, 1); - tcg_gen_shl_tl(s->tmp0, s->tmp0, s->T1); - if (s->prefix & PREFIX_LOCK) { - switch (op) { - case 0: /* bt */ - /* Needs no atomic ops; we surpressed the normal - memory load for LOCK above so do it now. */ - gen_op_ld_v(s, ot, s->T0, s->A0); - break; - case 1: /* bts */ - tcg_gen_atomic_fetch_or_tl(s->T0, s->A0, s->tmp0, - s->mem_index, ot | MO_LE); - break; - case 2: /* btr */ - tcg_gen_not_tl(s->tmp0, s->tmp0); - tcg_gen_atomic_fetch_and_tl(s->T0, s->A0, s->tmp0, - s->mem_index, ot | MO_LE); - break; - default: - case 3: /* btc */ - tcg_gen_atomic_fetch_xor_tl(s->T0, s->A0, s->tmp0, - s->mem_index, ot | MO_LE); - break; - } - tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); - } else { - tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); - switch (op) { - case 0: /* bt */ - /* Data already loaded; nothing to do. */ - break; - case 1: /* bts */ - tcg_gen_or_tl(s->T0, s->T0, s->tmp0); - break; - case 2: /* btr */ - tcg_gen_andc_tl(s->T0, s->T0, s->tmp0); - break; - default: - case 3: /* btc */ - tcg_gen_xor_tl(s->T0, s->T0, s->tmp0); - break; - } - if (op != 0) { - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - } - - /* Delay all CC updates until after the store above. Note that - C is the result of the test, Z is unchanged, and the others - are all undefined. */ - switch (s->cc_op) { - case CC_OP_MULB ... CC_OP_MULQ: - case CC_OP_ADDB ... CC_OP_ADDQ: - case CC_OP_ADCB ... CC_OP_ADCQ: - case CC_OP_SUBB ... CC_OP_SUBQ: - case CC_OP_SBBB ... CC_OP_SBBQ: - case CC_OP_LOGICB ... CC_OP_LOGICQ: - case CC_OP_INCB ... CC_OP_INCQ: - case CC_OP_DECB ... CC_OP_DECQ: - case CC_OP_SHLB ... CC_OP_SHLQ: - case CC_OP_SARB ... CC_OP_SARQ: - case CC_OP_BMILGB ... CC_OP_BMILGQ: - /* Z was going to be computed from the non-zero status of CC_DST. - We can get that same Z value (and the new C value) by leaving - CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the - same width. */ - tcg_gen_mov_tl(cpu_cc_src, s->tmp4); - set_cc_op(s, ((s->cc_op - CC_OP_MULB) & 3) + CC_OP_SARB); - break; - default: - /* Otherwise, generate EFLAGS and replace the C bit. */ - gen_compute_eflags(s); - tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, s->tmp4, - ctz32(CC_C), 1); - break; - } - break; - case 0x1bc: /* bsf / tzcnt */ - case 0x1bd: /* bsr / lzcnt */ - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, s->T0); - - /* Note that lzcnt and tzcnt are in different extensions. */ - if ((prefixes & PREFIX_REPZ) - && (b & 1 - ? s->cpuid_ext3_features & CPUID_EXT3_ABM - : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { - int size = 8 << ot; - /* For lzcnt/tzcnt, C bit is defined related to the input. */ - tcg_gen_mov_tl(cpu_cc_src, s->T0); - if (b & 1) { - /* For lzcnt, reduce the target_ulong result by the - number of zeros that we expect to find at the top. */ - tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); - tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - size); - } else { - /* For tzcnt, a zero input must return the operand size. */ - tcg_gen_ctzi_tl(s->T0, s->T0, size); - } - /* For lzcnt/tzcnt, Z bit is defined related to the result. */ - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_BMILGB + ot); - } else { - /* For bsr/bsf, only the Z bit is defined and it is related - to the input and not the result. */ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, CC_OP_LOGICB + ot); - - /* ??? The manual says that the output is undefined when the - input is zero, but real hardware leaves it unchanged, and - real programs appear to depend on that. Accomplish this - by passing the output as the value to return upon zero. */ - if (b & 1) { - /* For bsr, return the bit index of the first 1 bit, - not the count of leading zeros. */ - tcg_gen_xori_tl(s->T1, cpu_regs[reg], TARGET_LONG_BITS - 1); - tcg_gen_clz_tl(s->T0, s->T0, s->T1); - tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); - } else { - tcg_gen_ctz_tl(s->T0, s->T0, cpu_regs[reg]); - } - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - /************************/ - /* bcd */ - case 0x27: /* daa */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_daa(cpu_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); - 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); - 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); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0xd4: /* aam */ - if (CODE64(s)) - goto illegal_op; - val = x86_ldub_code(env, s); - if (val == 0) { - gen_exception(s, EXCP00_DIVZ); - } else { - gen_helper_aam(cpu_env, tcg_const_i32(val)); - set_cc_op(s, CC_OP_LOGICB); - } - break; - case 0xd5: /* aad */ - if (CODE64(s)) - goto illegal_op; - val = x86_ldub_code(env, s); - gen_helper_aad(cpu_env, tcg_const_i32(val)); - set_cc_op(s, CC_OP_LOGICB); - break; - /************************/ - /* misc */ - case 0x90: /* nop */ - /* XXX: correct lock test for all insn */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - /* If REX_B is set, then this is xchg eax, r8d, not a nop. */ - if (REX_B(s)) { - goto do_xchg_reg_eax; - } - if (prefixes & PREFIX_REPZ) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_pause(cpu_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); - } else { - gen_helper_fwait(cpu_env); - } - break; - case 0xcc: /* int3 */ - 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); - } - break; - case 0xce: /* into */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_into(cpu_env, cur_insn_len_i32(s)); - break; -#ifdef WANT_ICEBP - case 0xf1: /* icebp (undocumented, exits to external debugger) */ - gen_svm_check_intercept(s, SVM_EXIT_ICEBP); - gen_debug(s); - break; -#endif - case 0xfa: /* cli */ - if (check_iopl(s)) { - gen_reset_eflags(s, IF_MASK); - } - break; - case 0xfb: /* sti */ - if (check_iopl(s)) { - gen_set_eflags(s, IF_MASK); - /* interruptions are enabled only the first insn after sti */ - gen_update_eip_next(s); - gen_eob_inhibit_irq(s, true); - } - break; - case 0x62: /* bound */ - if (CODE64(s)) - goto illegal_op; - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_op_mov_v_reg(s, ot, s->T0, reg); - 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); - } else { - gen_helper_boundl(cpu_env, s->A0, s->tmp2_i32); - } - break; - case 0x1c8 ... 0x1cf: /* bswap reg */ - reg = (b & 7) | REX_B(s); -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - tcg_gen_bswap64_i64(cpu_regs[reg], cpu_regs[reg]); - break; - } -#endif - tcg_gen_bswap32_tl(cpu_regs[reg], cpu_regs[reg], TCG_BSWAP_OZ); - break; - case 0xd6: /* salc */ - if (CODE64(s)) - goto illegal_op; - gen_compute_eflags_c(s, s->T0); - tcg_gen_neg_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); - break; - case 0xe0: /* loopnz */ - case 0xe1: /* loopz */ - case 0xe2: /* loop */ - case 0xe3: /* jecxz */ - { - TCGLabel *l1, *l2; - int diff = (int8_t)insn_get(env, s, MO_8); - - l1 = gen_new_label(); - l2 = 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, 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, l1); - break; - default: - case 3: /* jcxz */ - gen_op_jz_ecx(s, l1); - break; - } - - gen_set_label(l2); - gen_jmp_rel_csize(s, 0, 1); - - gen_set_label(l1); - gen_jmp_rel(s, dflag, diff, 0); - } - break; - case 0x130: /* wrmsr */ - case 0x132: /* rdmsr */ - if (check_cpl0(s)) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - if (b & 2) { - gen_helper_rdmsr(cpu_env); - } else { - gen_helper_wrmsr(cpu_env); - s->base.is_jmp = DISAS_EOB_NEXT; - } - } - break; - case 0x131: /* rdtsc */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } - gen_helper_rdtsc(cpu_env); - break; - case 0x133: /* rdpmc */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_rdpmc(cpu_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) - goto illegal_op; - if (!PE(s)) { - gen_exception_gpf(s); - } else { - gen_helper_sysenter(cpu_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) - goto illegal_op; - if (!PE(s)) { - gen_exception_gpf(s); - } else { - gen_helper_sysexit(cpu_env, tcg_const_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 ? */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_syscall(cpu_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)) { - gen_exception_gpf(s); - } else { - gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); - /* condition codes are modified only in long mode */ - if (LMA(s)) { - set_cc_op(s, CC_OP_EFLAGS); - } - /* TF handling for the sysret insn is different. The TF bit is - checked after the sysret insn completes. This allows #DB to be - generated "as if" the syscall insn in userspace has just - completed. */ - gen_eob_worker(s, false, true); - } - break; -#endif - case 0x1a2: /* cpuid */ - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_cpuid(cpu_env); - break; - case 0xf4: /* hlt */ - if (check_cpl0(s)) { - gen_update_cc_op(s); - gen_update_eip_cur(s); - gen_helper_hlt(cpu_env, cur_insn_len_i32(s)); - s->base.is_jmp = DISAS_NORETURN; - } - break; case 0x100: - modrm = x86_ldub_code(env, s); mod = (modrm >> 6) & 3; op = (modrm >> 3) & 7; switch(op) { @@ -6227,19 +3431,19 @@ static bool 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); + gen_st_modrm(s, decode, ot); break; case 2: /* lldt */ if (!PE(s) || VM86(s)) goto illegal_op; if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_LDTR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ld_modrm(s, decode, MO_16); 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 */ @@ -6249,56 +3453,56 @@ static bool 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); + gen_st_modrm(s, decode, ot); break; case 3: /* ltr */ if (!PE(s) || VM86(s)) goto illegal_op; if (check_cpl0(s)) { gen_svm_check_intercept(s, SVM_EXIT_TR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ld_modrm(s, decode, MO_16); 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 */ case 5: /* verw */ if (!PE(s) || VM86(s)) goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ld_modrm(s, decode, MO_16); 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); + assume_cc_op(s, CC_OP_EFLAGS); break; default: - goto unknown_op; + goto illegal_op; } break; case 0x101: - modrm = x86_ldub_code(env, s); switch (modrm) { CASE_MODRM_MEM_OP(0): /* sgdt */ if (s->flags & HF_UMIP_MASK && !check_cpl0(s)) { break; } gen_svm_check_intercept(s, SVM_EXIT_GDTR_READ); - gen_lea_modrm(env, s, modrm); + gen_lea_modrm(s, decode); 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)); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } + tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, gdt.base)); + /* + * NB: Despite a confusing description in Intel CPU documentation, + * all 32-bits are written regardless of operand size. + */ gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); break; @@ -6308,10 +3512,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); 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_lea_v_seg(s, cpu_regs[R_EAX], R_DS, s->override); + gen_helper_monitor(tcg_env, s->A0); break; case 0xc9: /* mwait */ @@ -6320,7 +3522,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_mwait(cpu_env, cur_insn_len_i32(s)); + gen_helper_mwait(tcg_env, cur_insn_len_i32(s)); s->base.is_jmp = DISAS_NORETURN; break; @@ -6347,41 +3549,41 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; } 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)); + gen_lea_modrm(s, decode); + 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)); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } + tcg_gen_ld_tl(s->T0, tcg_env, offsetof(CPUX86State, idt.base)); + /* + * NB: Despite a confusing description in Intel CPU documentation, + * all 32-bits are written regardless of operand size. + */ gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); break; case 0xd0: /* xgetbv */ if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->prefix & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { + || (s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ))) { 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; case 0xd1: /* xsetbv */ if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->prefix & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { + || (s->prefix & (PREFIX_DATA | 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. */ s->base.is_jmp = DISAS_EOB_NEXT; break; @@ -6395,7 +3597,12 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), + /* + * Reloads INHIBIT_IRQ mask as well as TF and RF with guest state. + * The usual gen_eob() handling is performed on vmexit after + * host state is reloaded. + */ + 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; @@ -6407,7 +3614,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_vmmcall(cpu_env); + gen_helper_vmmcall(tcg_env); break; case 0xda: /* VMLOAD */ @@ -6419,7 +3626,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); + gen_helper_vmload(tcg_env, tcg_constant_i32(s->aflag - 1)); break; case 0xdb: /* VMSAVE */ @@ -6431,7 +3638,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); + gen_helper_vmsave(tcg_env, tcg_constant_i32(s->aflag - 1)); break; case 0xdc: /* STGI */ @@ -6443,7 +3650,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_helper_stgi(cpu_env); + gen_helper_stgi(tcg_env); s->base.is_jmp = DISAS_EOB_NEXT; break; @@ -6456,7 +3663,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - gen_helper_clgi(cpu_env); + gen_helper_clgi(tcg_env); break; case 0xde: /* SKINIT */ @@ -6481,7 +3688,7 @@ static bool 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_helper_flush_page(tcg_env, s->A0); s->base.is_jmp = DISAS_EOB_NEXT; break; @@ -6490,15 +3697,15 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_GDTR_WRITE); - gen_lea_modrm(env, s, modrm); + gen_lea_modrm(s, decode); gen_op_ld_v(s, MO_16, s->T1, s->A0); gen_add_A0_im(s, 2); gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); 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 */ @@ -6506,15 +3713,15 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_IDTR_WRITE); - gen_lea_modrm(env, s, modrm); + gen_lea_modrm(s, decode); gen_op_ld_v(s, MO_16, s->T1, s->A0); gen_add_A0_im(s, 2); gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); 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 */ @@ -6522,7 +3729,7 @@ static bool 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 @@ -6530,24 +3737,24 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) */ mod = (modrm >> 6) & 3; ot = (mod != 3 ? MO_16 : s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + gen_st_modrm(s, decode, ot); break; case 0xee: /* rdpkru */ - if (prefixes & PREFIX_LOCK) { + if (s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ)) { 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 */ - if (prefixes & PREFIX_LOCK) { + if (s->prefix & (PREFIX_DATA | PREFIX_REPZ | PREFIX_REPNZ)) { goto illegal_op; } 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 */ @@ -6555,16 +3762,16 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_ld_modrm(s, decode, MO_16); /* * 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_helper_write_crN(tcg_env, tcg_constant_i32(0), s->T0); s->base.is_jmp = DISAS_EOB_NEXT; break; @@ -6573,8 +3780,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) break; } gen_svm_check_intercept(s, SVM_EXIT_INVLPG); - gen_lea_modrm(env, s, modrm); - gen_helper_flush_page(cpu_env, s->A0); + gen_lea_modrm(s, decode); + gen_helper_flush_page(tcg_env, s->A0); s->base.is_jmp = DISAS_EOB_NEXT; break; @@ -6583,9 +3790,9 @@ static bool 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; @@ -6599,168 +3806,37 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_update_eip_cur(s); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } - gen_helper_rdtscp(cpu_env); + 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: - goto unknown_op; + goto illegal_op; } break; - case 0x108: /* invd */ - case 0x109: /* wbinvd */ - if (check_cpl0(s)) { - gen_svm_check_intercept(s, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD); - /* nothing to do */ - } - break; - case 0x63: /* arpl or movslS (x86_64) */ -#ifdef TARGET_X86_64 - if (CODE64(s)) { - int d_ot; - /* d_ot is the size of destination */ - d_ot = dflag; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - - if (mod == 3) { - gen_op_mov_v_reg(s, MO_32, s->T0, rm); - /* sign extend */ - if (d_ot == MO_64) { - tcg_gen_ext32s_tl(s->T0, s->T0); - } - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32 | MO_SIGN, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } - } else -#endif - { - TCGLabel *label1; - TCGv t0, t1, t2, a0; - - if (!PE(s) || VM86(s)) - goto illegal_op; - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); - ot = MO_16; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - rm = modrm & 7; - 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); - tcg_gen_andi_tl(t1, t1, 3); - tcg_gen_movi_tl(t2, 0); - label1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GE, s->tmp0, t1, label1); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_or_tl(t0, t0, t1); - 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); - } 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 */ - case 0x103: /* lsl */ - { - TCGLabel *label1; - TCGv t0; - if (!PE(s) || VM86(s)) - goto illegal_op; - ot = dflag != MO_16 ? MO_32 : MO_16; - 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(); - gen_update_cc_op(s); - if (b == 0x102) { - gen_helper_lar(t0, cpu_env, s->T0); - } else { - gen_helper_lsl(t0, cpu_env, s->T0); - } - tcg_gen_andi_tl(s->tmp0, cpu_cc_src, CC_Z); - label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); - 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: - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* prefetchnta */ - case 1: /* prefetchnt0 */ - case 2: /* prefetchnt0 */ - case 3: /* prefetchnt0 */ - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - /* nothing more to do */ - break; - default: /* nop (multi byte) */ - gen_nop_modrm(env, s, modrm); - break; - } - break; case 0x11a: - modrm = x86_ldub_code(env, s); if (s->flags & HF_MPX_EN_MASK) { mod = (modrm >> 6) & 3; reg = ((modrm >> 3) & 7) | REX_R(s); if (prefixes & PREFIX_REPZ) { /* bndcl */ if (reg >= 4 - || (prefixes & PREFIX_LOCK) || s->aflag == MO_16) { goto illegal_op; } - gen_bndck(env, s, modrm, TCG_COND_LTU, cpu_bndl[reg]); + gen_bndck(s, decode, TCG_COND_LTU, cpu_bndl[reg]); } else if (prefixes & PREFIX_REPNZ) { /* bndcu */ if (reg >= 4 - || (prefixes & PREFIX_LOCK) || s->aflag == MO_16) { goto illegal_op; } 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); + gen_bndck(s, decode, TCG_COND_GTU, notu); } else if (prefixes & PREFIX_DATA) { /* bndmov -- from reg/mem */ if (reg >= 4 || s->aflag == MO_16) { @@ -6768,7 +3844,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } if (mod == 3) { int reg2 = (modrm & 7) | REX_B(s); - if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + if (reg2 >= 4) { goto illegal_op; } if (s->flags & HF_MPX_IU_MASK) { @@ -6776,7 +3852,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_mov_i64(cpu_bndu[reg], cpu_bndu[reg2]); } } else { - gen_lea_modrm(env, s, modrm); + gen_lea_modrm(s, decode); if (CODE64(s)) { tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->A0, s->mem_index, MO_LEUQ); @@ -6795,9 +3871,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } } else if (mod != 3) { /* bndldx */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = decode->mem; if (reg >= 4 - || (prefixes & PREFIX_LOCK) || s->aflag == MO_16 || a.base < -1) { goto illegal_op; @@ -6807,39 +3882,36 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } else { tcg_gen_movi_tl(s->A0, 0); } - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->A0, a.def_seg, s->override); if (a.index >= 0) { tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); } else { 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); } gen_set_hflag(s, HF_MPX_IU_MASK); } } - gen_nop_modrm(env, s, modrm); break; case 0x11b: - modrm = x86_ldub_code(env, s); if (s->flags & HF_MPX_EN_MASK) { mod = (modrm >> 6) & 3; reg = ((modrm >> 3) & 7) | REX_R(s); if (mod != 3 && (prefixes & PREFIX_REPZ)) { /* bndmk */ if (reg >= 4 - || (prefixes & PREFIX_LOCK) || s->aflag == MO_16) { goto illegal_op; } - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = decode->mem; if (a.base >= 0) { tcg_gen_extu_tl_i64(cpu_bndl[reg], cpu_regs[a.base]); if (!CODE64(s)) { @@ -6852,7 +3924,7 @@ static bool 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, false)); + tcg_gen_not_tl(s->A0, gen_lea_modrm_1(s, decode->mem, false)); if (!CODE64(s)) { tcg_gen_ext32u_tl(s->A0, s->A0); } @@ -6863,11 +3935,10 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } else if (prefixes & PREFIX_REPNZ) { /* bndcn */ if (reg >= 4 - || (prefixes & PREFIX_LOCK) || s->aflag == MO_16) { goto illegal_op; } - gen_bndck(env, s, modrm, TCG_COND_GTU, cpu_bndu[reg]); + gen_bndck(s, decode, TCG_COND_GTU, cpu_bndu[reg]); } else if (prefixes & PREFIX_DATA) { /* bndmov -- to reg/mem */ if (reg >= 4 || s->aflag == MO_16) { @@ -6875,7 +3946,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } if (mod == 3) { int reg2 = (modrm & 7) | REX_B(s); - if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + if (reg2 >= 4) { goto illegal_op; } if (s->flags & HF_MPX_IU_MASK) { @@ -6883,7 +3954,7 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_mov_i64(cpu_bndu[reg2], cpu_bndu[reg]); } } else { - gen_lea_modrm(env, s, modrm); + gen_lea_modrm(s, decode); if (CODE64(s)) { tcg_gen_qemu_st_i64(cpu_bndl[reg], s->A0, s->mem_index, MO_LEUQ); @@ -6900,9 +3971,8 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } } else if (mod != 3) { /* bndstx */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); + AddressParts a = decode->mem; if (reg >= 4 - || (prefixes & PREFIX_LOCK) || s->aflag == MO_16 || a.base < -1) { goto illegal_op; @@ -6912,394 +3982,33 @@ static bool disas_insn(DisasContext *s, CPUState *cpu) } else { tcg_gen_movi_tl(s->A0, 0); } - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->A0, a.def_seg, s->override); if (a.index >= 0) { tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); } else { 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]); } } } - gen_nop_modrm(env, s, modrm); - break; - case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ - modrm = x86_ldub_code(env, s); - gen_nop_modrm(env, s, modrm); - break; - - case 0x120: /* mov reg, crN */ - case 0x122: /* mov crN, reg */ - if (!check_cpl0(s)) { - break; - } - modrm = x86_ldub_code(env, s); - /* - * Ignore the mod bits (assume (modrm&0xc0)==0xc0). - * AMD documentation (24594.pdf) and testing of Intel 386 and 486 - * processors all show that the mod bits are assumed to be 1's, - * regardless of actual values. - */ - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | REX_R(s); - switch (reg) { - case 0: - if ((prefixes & PREFIX_LOCK) && - (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { - reg = 8; - } - break; - case 2: - case 3: - case 4: - case 8: - break; - default: - goto unknown_op; - } - ot = (CODE64(s) ? MO_64 : MO_32); - - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - s->base.is_jmp = DISAS_TOO_MANY; - } - 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); - 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_op_mov_reg_v(s, ot, rm, s->T0); - } - break; - - case 0x121: /* mov reg, drN */ - case 0x123: /* mov drN, reg */ - if (check_cpl0(s)) { - modrm = x86_ldub_code(env, s); - /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). - * AMD documentation (24594.pdf) and testing of - * intel 386 and 486 processors all show that the mod bits - * are assumed to be 1's, regardless of actual values. - */ - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | REX_R(s); - if (CODE64(s)) - ot = MO_64; - else - ot = MO_32; - if (reg >= 8) { - goto illegal_op; - } - if (b & 2) { - 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); - 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_op_mov_reg_v(s, ot, rm, s->T0); - } - } - break; - case 0x106: /* clts */ - if (check_cpl0(s)) { - gen_svm_check_intercept(s, SVM_EXIT_WRITE_CR0); - gen_helper_clts(cpu_env); - /* abort block because static cpu state changed */ - s->base.is_jmp = DISAS_EOB_NEXT; - } - break; - /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ - case 0x1c3: /* MOVNTI reg, mem */ - if (!(s->cpuid_features & CPUID_SSE2)) - goto illegal_op; - ot = mo_64_32(dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - reg = ((modrm >> 3) & 7) | REX_R(s); - /* generate a generic store */ - gen_ldst_modrm(env, s, modrm, ot, reg, 1); - break; - case 0x1ae: - modrm = x86_ldub_code(env, s); - switch (modrm) { - CASE_MODRM_MEM_OP(0): /* fxsave */ - if (!(s->cpuid_features & CPUID_FXSR) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_lea_modrm(env, s, modrm); - gen_helper_fxsave(cpu_env, s->A0); - break; - - CASE_MODRM_MEM_OP(1): /* fxrstor */ - if (!(s->cpuid_features & CPUID_FXSR) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_lea_modrm(env, s, modrm); - gen_helper_fxrstor(cpu_env, s->A0); - gen_update_eip_next(s); - gen_eob(s); - break; - - CASE_MODRM_MEM_OP(2): /* ldmxcsr */ - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { - goto illegal_op; - } - if (s->flags & HF_TS_MASK) { - 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); - break; - - CASE_MODRM_MEM_OP(3): /* stmxcsr */ - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { - goto illegal_op; - } - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX); - break; - } - gen_helper_update_mxcsr(cpu_env); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr)); - gen_op_st_v(s, MO_32, s->T0, s->A0); - break; - - CASE_MODRM_MEM_OP(4): /* xsave */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (prefixes & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - 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); - break; - - CASE_MODRM_MEM_OP(5): /* xrstor */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (prefixes & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - 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); - /* XRSTOR is how MPX is enabled, which changes how - we translate. Thus we need to end the TB. */ - s->base.is_jmp = DISAS_EOB_NEXT; - break; - - CASE_MODRM_MEM_OP(6): /* xsaveopt / clwb */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - if (prefixes & PREFIX_DATA) { - /* clwb */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) { - goto illegal_op; - } - gen_nop_modrm(env, s, modrm); - } else { - /* xsaveopt */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 - || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - 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); - } - break; - - CASE_MODRM_MEM_OP(7): /* clflush / clflushopt */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - if (prefixes & PREFIX_DATA) { - /* clflushopt */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) { - goto illegal_op; - } - } else { - /* clflush */ - if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) - || !(s->cpuid_features & CPUID_CLFLUSH)) { - goto illegal_op; - } - } - gen_nop_modrm(env, s, modrm); - break; - - case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */ - case 0xc8 ... 0xcf: /* rdgsbase (f3 0f ae /1) */ - case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */ - case 0xd8 ... 0xdf: /* wrgsbase (f3 0f ae /3) */ - if (CODE64(s) - && (prefixes & PREFIX_REPZ) - && !(prefixes & PREFIX_LOCK) - && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) { - TCGv base, treg, src, dst; - - /* 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); - - base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; - treg = cpu_regs[(modrm & 7) | REX_B(s)]; - - if (modrm & 0x10) { - /* wr*base */ - dst = base, src = treg; - } else { - /* rd*base */ - dst = treg, src = base; - } - - if (s->dflag == MO_32) { - tcg_gen_ext32u_tl(dst, src); - } else { - tcg_gen_mov_tl(dst, src); - } - break; - } - goto unknown_op; - - case 0xf8: /* sfence / pcommit */ - if (prefixes & PREFIX_DATA) { - /* pcommit */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - break; - } - /* fallthru */ - case 0xf9 ... 0xff: /* sfence */ - if (!(s->cpuid_features & CPUID_SSE) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); - break; - case 0xe8 ... 0xef: /* lfence */ - if (!(s->cpuid_features & CPUID_SSE) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_LD_LD | TCG_BAR_SC); - break; - case 0xf0 ... 0xf7: /* mfence */ - if (!(s->cpuid_features & CPUID_SSE2) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - break; - - default: - goto unknown_op; - } - break; - - case 0x10d: /* 3DNow! prefetch(w) */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - break; - case 0x1aa: /* rsm */ - gen_svm_check_intercept(s, SVM_EXIT_RSM); - if (!(s->flags & HF_SMM_MASK)) - goto illegal_op; -#ifdef CONFIG_USER_ONLY - /* we should not be in SMM mode */ - g_assert_not_reached(); -#else - gen_update_cc_op(s); - gen_update_eip_next(s); - gen_helper_rsm(cpu_env); -#endif /* CONFIG_USER_ONLY */ - s->base.is_jmp = DISAS_EOB_ONLY; - break; - case 0x1b8: /* SSE4.2 popcnt */ - if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != - PREFIX_REPZ) - goto illegal_op; - if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT)) - goto illegal_op; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | REX_R(s); - - if (s->prefix & PREFIX_DATA) { - ot = MO_16; - } else { - ot = mo_64_32(dflag); - } - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, s->T0); - tcg_gen_mov_tl(cpu_cc_src, s->T0); - tcg_gen_ctpop_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - - set_cc_op(s, CC_OP_POPCNT); - break; - case 0x10e ... 0x117: - case 0x128 ... 0x12f: - case 0x138 ... 0x13a: - case 0x150 ... 0x179: - case 0x17c ... 0x17f: - case 0x1c2: - case 0x1c4 ... 0x1c6: - case 0x1d0 ... 0x1fe: - disas_insn_new(s, cpu, b); break; default: - goto unknown_op; + g_assert_not_reached(); } - return true; + return; illegal_op: gen_illegal_opcode(s); - return true; - unknown_op: - gen_unknown_opcode(env, s); - return true; + return; } +#include "decode-new.c.inc" + void tcg_x86_init(void) { static const char reg_names[CPU_NB_REGS][4] = { @@ -7354,41 +4063,41 @@ 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(cpu_env, offsetof(CPUX86State, eip), eip_name); + 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]); } - fpstt = tcg_global_mem_new_i32(cpu_env, + fpstt = tcg_global_mem_new_i32(tcg_env, offsetof(CPUX86State, fpstt), "fpstt"); #if defined(XBOX) && defined(__x86_64__) @@ -7399,7 +4108,7 @@ 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; @@ -7428,25 +4137,30 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->cc_op = CC_OP_DYNAMIC; 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))); + (flags & (HF_RF_MASK | HF_TF_MASK | HF_INHIBIT_IRQ_MASK))); /* * If jmp_opt, we want to handle each string instruction individually. * For icount also disable repz optimization so that each iteration * is accounted separately. + * + * FIXME: this is messy; it makes REP string instructions a lot less + * efficient than they should be and it gets in the way of correct + * handling of RF (interrupts or traps arriving after any iteration + * of a repeated string instruction but the last should set RF to 1). + * Perhaps it would be more efficient if REP string instructions were + * always at the beginning of the TB, or even their own TB? That + * would even allow accounting up to 64k iterations at once for icount. */ dc->repz_opt = !dc->jmp_opt && !(cflags & CF_USE_ICOUNT); @@ -7459,8 +4173,8 @@ 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->cc_srcT = tcg_temp_local_new(); - + dc->cc_srcT = tcg_temp_new(); + for (int i = 0; i < 8; i++) { dc->fpregs[i] = NULL; } @@ -7478,9 +4192,9 @@ 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; + dc->prev_insn_start = dc->base.insn_start; dc->prev_insn_end = tcg_last_op(); - if (TARGET_TB_PCREL) { - pc_arg -= dc->cs_base; + if (tb_cflags(dcbase->tb) & CF_PCREL) { pc_arg &= ~TARGET_PAGE_MASK; } tcg_gen_insn_start(pc_arg, dc->cc_op); @@ -7489,6 +4203,9 @@ static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); + bool orig_cc_op_dirty = dc->cc_op_dirty; + CCOp orig_cc_op = dc->cc_op; + target_ulong orig_pc_save = dc->pc_save; #ifdef TARGET_VSYSCALL_PAGE /* @@ -7501,23 +4218,45 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) } #endif - if (disas_insn(dc, cpu)) { - target_ulong pc_next = dc->pc; - dc->base.pc_next = pc_next; + switch (sigsetjmp(dc->jmpbuf, 0)) { + case 0: + disas_insn(dc, cpu); + break; + case 1: + gen_exception_gpf(dc); + break; + case 2: + /* Restore state that may affect the next instruction. */ + dc->pc = dc->base.pc_next; + assert(dc->cc_op_dirty == orig_cc_op_dirty); + assert(dc->cc_op == orig_cc_op); + assert(dc->pc_save == orig_pc_save); + dc->base.num_insns--; + tcg_remove_ops_after(dc->prev_insn_end); + dc->base.insn_start = dc->prev_insn_start; + dc->base.is_jmp = DISAS_TOO_MANY; + return; + default: + g_assert_not_reached(); + } - 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; - } + /* + * Instruction decoding completed (possibly with #GP if the + * 15-byte boundary was exceeded). + */ + dc->base.pc_next = dc->pc; + 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, dc->base.pc_next)) { + dc->base.is_jmp = DISAS_TOO_MANY; } } } @@ -7530,52 +4269,48 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (dc->base.is_jmp) { case DISAS_NORETURN: + /* + * Most instructions should not use DISAS_NORETURN, as that suppresses + * the handling of hflags normally done by gen_eob(). We can + * get here: + * - for exception and interrupts + * - for jump optimization (which is disabled by INHIBIT_IRQ/RF/TF) + * - for VMRUN because RF/TF handling for the host is done after vmexit, + * and INHIBIT_IRQ is loaded from the VMCB + * - for HLT/PAUSE/MWAIT to exit the main loop with specific EXCP_* values; + * the helpers handle themselves the tasks normally done by gen_eob(). + */ 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); + case DISAS_EOB_INHIBIT_IRQ: + assert(dc->base.pc_next == dc->pc); 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_EOB_RECHECK_TF: case DISAS_JUMP: - gen_jr(dc); + gen_eob(dc, dc->base.is_jmp); break; default: g_assert_not_reached(); } } -static void i386_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cpu, dc->base.pc_first, dc->base.tb->size); -} - static const TranslatorOps i386_tr_ops = { .init_disas_context = i386_tr_init_disas_context, .tb_start = i386_tr_tb_start, .insn_start = i386_tr_insn_start, .translate_insn = i386_tr_translate_insn, .tb_stop = i386_tr_tb_stop, - .disas_log = i386_tr_disas_log, }; /* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; 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/trace-events b/target/i386/trace-events index 2cd8726eeb..51301673f0 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -6,8 +6,11 @@ kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zx" kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zx" kvm_sev_change_state(const char *old, const char *new) "%s -> %s" kvm_sev_launch_start(int policy, void *session, void *pdh) "policy 0x%x session %p pdh %p" -kvm_sev_launch_update_data(void *addr, uint64_t len) "addr %p len 0x%" PRIx64 +kvm_sev_launch_update_data(void *addr, size_t len) "addr %p len 0x%zx" kvm_sev_launch_measurement(const char *value) "data %s" kvm_sev_launch_finish(void) "" kvm_sev_launch_secret(uint64_t hpa, uint64_t hva, uint64_t secret, int len) "hpa 0x%" PRIx64 " hva 0x%" PRIx64 " data 0x%" PRIx64 " len %d" kvm_sev_attestation_report(const char *mnonce, const char *data) "mnonce %s data %s" +kvm_sev_snp_launch_start(uint64_t policy, char *gosvw) "policy 0x%" PRIx64 " gosvw %s" +kvm_sev_snp_launch_update(uint64_t src, uint64_t gpa, uint64_t len, const char *type) "src 0x%" PRIx64 " gpa 0x%" PRIx64 " len 0x%" PRIx64 " (%s page)" +kvm_sev_snp_launch_finish(char *id_block, char *id_auth, char *host_data) "id_block %s id_auth %s host_data %s" 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..1a2b4e1c43 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; } @@ -64,16 +64,10 @@ static void whpx_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/WHPX", 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 e738d83e81..a6674a826d 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; @@ -237,6 +237,7 @@ struct whpx_vcpu { uint64_t tpr; uint64_t apic_base; bool interruption_pending; + bool dirty; /* Must be the last field as it may have a tail */ WHV_RUN_VP_EXIT_CONTEXT exit_ctx; @@ -256,15 +257,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 +301,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 +311,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 +321,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 +344,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 +380,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 +555,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 +567,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 +591,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; @@ -852,7 +840,7 @@ static HRESULT CALLBACK whpx_emu_setreg_callback( * The emulator just successfully wrote the register state. We clear the * dirty state so we avoid the double write on resume of the VP. */ - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; return hr; } @@ -892,7 +880,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 +905,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( @@ -1333,7 +1321,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 && @@ -1407,17 +1395,16 @@ static int whpx_last_vcpu_stopping(CPUState *cpu) /* Returns the address of the next instruction that is about to be executed. */ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) { - if (cpu->vcpu_dirty) { + if (cpu->accel->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 { /* @@ -1448,18 +1435,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; } @@ -1468,9 +1454,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; @@ -1481,7 +1467,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 && @@ -1572,7 +1558,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) { @@ -1590,18 +1576,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 = @@ -1615,9 +1601,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)) { @@ -1656,12 +1642,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. */ @@ -1699,7 +1685,7 @@ static int whpx_vcpu_run(CPUState *cpu) } } - qemu_mutex_unlock_iothread(); + bql_unlock(); if (exclusive_step_mode != WHPX_STEP_NONE) { start_exclusive(); @@ -1728,9 +1714,9 @@ static int whpx_vcpu_run(CPUState *cpu) } do { - if (cpu->vcpu_dirty) { + if (cpu->accel->dirty) { whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } if (exclusive_step_mode == WHPX_STEP_NONE) { @@ -2037,9 +2023,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; } @@ -2064,7 +2050,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) { @@ -2078,9 +2064,9 @@ static int whpx_vcpu_run(CPUState *cpu) static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { whpx_get_registers(cpu); - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } } @@ -2088,20 +2074,20 @@ static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_RESET_STATE); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_FULL_STATE); - cpu->vcpu_dirty = false; + cpu->accel->dirty = false; } static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->vcpu_dirty = true; + cpu->accel->dirty = true; } /* @@ -2110,7 +2096,7 @@ static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, void whpx_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->vcpu_dirty) { + if (!cpu->accel->dirty) { run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -2154,10 +2140,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; @@ -2169,21 +2155,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, @@ -2257,10 +2236,10 @@ int whpx_init_vcpu(CPUState *cpu) } vcpu->interruptable = true; - cpu->vcpu_dirty = true; - cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu; + vcpu->dirty = true; + 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; @@ -2296,11 +2275,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; } @@ -2427,7 +2406,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) @@ -2613,8 +2592,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/arch_dump.c b/target/loongarch/arch_dump.c new file mode 100644 index 0000000000..d9e1120333 --- /dev/null +++ b/target/loongarch/arch_dump.c @@ -0,0 +1,163 @@ +/* + * Support for writing ELF notes for LoongArch architectures + * + * Copyright (c) 2023 Loongarch Technology + * + * 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 "elf.h" +#include "sysemu/dump.h" +#include "internals.h" + +/* struct user_pt_regs from arch/loongarch/include/uapi/asm/ptrace.h */ +struct loongarch_user_regs { + uint64_t gpr[32]; + uint64_t pad1[1]; + /* Special CSR registers. */ + uint64_t csr_era; + uint64_t csr_badv; + uint64_t pad2[10]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct loongarch_user_regs) != 360); + +/* struct elf_prstatus from include/uapi/linux/elfcore.h */ +struct loongarch_elf_prstatus { + char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pr_pid; + /* + * 76 == offsetof(struct elf_prstatus, pr_reg) - + * offsetof(struct elf_prstatus, pr_ppid) + */ + char pad2[76]; + struct loongarch_user_regs pr_reg; + uint32_t pr_fpvalid; + char pad3[4]; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct loongarch_elf_prstatus) != 480); + +/* struct user_fp_state from arch/loongarch/include/uapi/asm/ptrace.h */ +struct loongarch_fpu_struct { + uint64_t fpr[32]; + uint64_t fcc; + unsigned int fcsr; +} QEMU_PACKED; + +QEMU_BUILD_BUG_ON(sizeof(struct loongarch_fpu_struct) != 268); + +struct loongarch_note { + Elf64_Nhdr hdr; + char name[8]; /* align_up(sizeof("CORE"), 4) */ + union { + struct loongarch_elf_prstatus prstatus; + struct loongarch_fpu_struct fpu; + }; +} QEMU_PACKED; + +#define LOONGARCH_NOTE_HEADER_SIZE offsetof(struct loongarch_note, prstatus) +#define LOONGARCH_PRSTATUS_NOTE_SIZE \ + (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_elf_prstatus)) +#define LOONGARCH_PRFPREG_NOTE_SIZE \ + (LOONGARCH_NOTE_HEADER_SIZE + sizeof(struct loongarch_fpu_struct)) + +static void loongarch_note_init(struct loongarch_note *note, DumpState *s, + const char *name, Elf64_Word namesz, + Elf64_Word type, Elf64_Word descsz) +{ + memset(note, 0, sizeof(*note)); + + note->hdr.n_namesz = cpu_to_dump32(s, namesz); + note->hdr.n_descsz = cpu_to_dump32(s, descsz); + note->hdr.n_type = cpu_to_dump32(s, type); + + memcpy(note->name, name, namesz); +} + +static int loongarch_write_elf64_fprpreg(WriteCoreDumpFunction f, + CPULoongArchState *env, int cpuid, + DumpState *s) +{ + struct loongarch_note note; + int ret, i; + + loongarch_note_init(¬e, s, "CORE", 5, NT_PRFPREG, sizeof(note.fpu)); + note.fpu.fcsr = cpu_to_dump64(s, env->fcsr0); + note.fpu.fcc = cpu_to_dump64(s, read_fcc(env)); + + for (i = 0; i < 32; ++i) { + note.fpu.fpr[i] = cpu_to_dump64(s, env->fpr[i].vreg.UD[0]); + } + + ret = f(¬e, LOONGARCH_PRFPREG_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + return 0; +} + +int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, DumpState *s) +{ + struct loongarch_note note; + CPULoongArchState *env = &LOONGARCH_CPU(cs)->env; + int ret, i; + + loongarch_note_init(¬e, s, "CORE", 5, NT_PRSTATUS, + sizeof(note.prstatus)); + note.prstatus.pr_pid = cpu_to_dump32(s, cpuid); + note.prstatus.pr_fpvalid = cpu_to_dump32(s, 1); + + for (i = 0; i < 32; ++i) { + note.prstatus.pr_reg.gpr[i] = cpu_to_dump64(s, env->gpr[i]); + } + note.prstatus.pr_reg.csr_era = cpu_to_dump64(s, env->CSR_ERA); + note.prstatus.pr_reg.csr_badv = cpu_to_dump64(s, env->CSR_BADV); + ret = f(¬e, LOONGARCH_PRSTATUS_NOTE_SIZE, s); + if (ret < 0) { + return -1; + } + + ret = loongarch_write_elf64_fprpreg(f, env, cpuid, s); + if (ret < 0) { + return -1; + } + + return ret; +} + +int cpu_get_dump_info(ArchDumpInfo *info, + const GuestPhysBlockList *guest_phys_blocks) +{ + info->d_machine = EM_LOONGARCH; + info->d_endian = ELFDATA2LSB; + info->d_class = ELFCLASS64; + + return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ + size_t note_size = 0; + + if (class == ELFCLASS64) { + note_size = LOONGARCH_PRSTATUS_NOTE_SIZE + LOONGARCH_PRFPREG_NOTE_SIZE; + } + + return note_size * nr_cpus; +} 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..db5ad1c69f 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -12,7 +12,8 @@ #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 + +#define TCG_GUEST_DEFAULT_MO (0) #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 46b04cbdad..57cc4f314b 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -11,13 +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" +#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", @@ -33,31 +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_FPD] = "Floating Point Disabled", - [EXCCODE_FPE] = "Floating Point Exception", - [EXCCODE_DBP] = "Debug breakpoint", - [EXCCODE_BCE] = "Bound Check Exception", +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, @@ -66,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: exception: %d (%s)\n", __func__, exception, loongarch_exception_name(exception)); @@ -77,18 +103,12 @@ 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) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - - return env->pc; + return cpu_env(cs)->pc; } #ifndef CONFIG_USER_ONLY @@ -104,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); + } } } @@ -128,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) { @@ -169,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)) { @@ -188,9 +205,11 @@ static void loongarch_cpu_do_interrupt(CPUState *cs) case EXCCODE_IPE: case EXCCODE_FPD: case EXCCODE_FPE: - case EXCCODE_BCE: + 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: @@ -249,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 " @@ -260,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 += EXCODE_MCODE(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 @@ -288,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); @@ -301,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)) { @@ -316,24 +333,18 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #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; - - env->pc = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + set_pc(cpu_env(cs), tb->pc); } static void loongarch_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; - - env->pc = data[0]; + set_pc(cpu_env(cs), data[0]); } #endif /* CONFIG_TCG */ @@ -342,12 +353,10 @@ static bool loongarch_cpu_has_work(CPUState *cs) #ifdef CONFIG_USER_ONLY return true; #else - LoongArchCPU *cpu = LOONGARCH_CPU(cs); - CPULoongArchState *env = &cpu->env; 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; } @@ -355,6 +364,16 @@ static bool loongarch_cpu_has_work(CPUState *cs) #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) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); @@ -387,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; @@ -435,43 +457,79 @@ static void loongarch_la464_initfn(Object *obj) env->cpucfg[20] = data; env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); + + env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, SAVE_NUM, 8); + env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, TIMER_BITS, 0x2f); + env->CSR_PRCFG1 = FIELD_DP64(env->CSR_PRCFG1, CSR_PRCFG1, VSMAX, 7); + + env->CSR_PRCFG2 = 0x3ffff000; + + 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); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); + env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); + + 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, ResetType type) +{ + 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, type); + } + +#ifdef CONFIG_TCG env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; +#endif env->fcsr0 = 0x0; int n; - /* Set csr registers value after reset */ + /* Set csr registers value after reset, see the manual 6.4. */ env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 0); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 0); env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0); env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0); @@ -485,15 +543,26 @@ 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_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); - env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); + env->CSR_TID = cs->cpu_index; + /* + * Workaround for edk2-stable202408, CSR PGD register is set only if + * its value is equal to zero for boot cpu, it causes reboot issue. + * + * Here clear CSR registers relative with TLB. + */ + env->CSR_PGDH = 0; + env->CSR_PGDL = 0; + env->CSR_PWCL = 0; + env->CSR_PWCH = 0; + env->CSR_STLBPS = 0; + env->CSR_EENTRY = 0; + env->CSR_TLBRENTRY = 0; + env->CSR_MERRENTRY = 0; for (n = 0; n < 4; n++) { env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); @@ -504,10 +573,17 @@ static void loongarch_cpu_reset(DeviceState *dev) #ifndef CONFIG_USER_ONLY env->pc = 0x1c000000; +#ifdef CONFIG_TCG memset(env->tlb, 0, sizeof(env->tlb)); #endif + if (kvm_enabled()) { + kvm_arch_reset_vcpu(cs); + } +#endif +#ifdef CONFIG_TCG restore_fp_status(env); +#endif cs->exception_index = -1; } @@ -536,60 +612,119 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) lacc->parent_realize(dev, errp); } -#ifndef CONFIG_USER_ONLY -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, - }, -}; -#endif - -static void loongarch_cpu_init(Object *obj) +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); - cpu_set_cpustate_pointers(cpu); + 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); + } +} + +static bool loongarch_get_lbt(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lbt(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +static bool loongarch_get_pmu(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_pmu(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + /* lbt is enabled only in kvm mode, not supported in tcg mode */ + if (kvm_enabled()) { + cpu->lbt = ON_OFF_AUTO_AUTO; + object_property_add_bool(obj, "lbt", loongarch_get_lbt, + loongarch_set_lbt); + object_property_set_description(obj, "lbt", + "Set off to disable Binary Tranlation."); + + cpu->pmu = ON_OFF_AUTO_AUTO; + object_property_add_bool(obj, "pmu", loongarch_get_pmu, + loongarch_set_pmu); + object_property_set_description(obj, "pmu", + "Set off to performance monitor unit."); + + } else { + cpu->lbt = ON_OFF_AUTO_OFF; + } +} + +static void loongarch_cpu_init(Object *obj) +{ #ifndef CONFIG_USER_ONLY - CPULoongArchState *env = &cpu->env; + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + 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 } @@ -599,30 +734,21 @@ static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) oc = object_class_by_name(cpu_model); if (!oc) { - g_autofree char *typename + g_autofree char *typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); oc = object_class_by_name(typename); - if (!oc) { - return NULL; - } } - if (object_class_dynamic_cast(oc, TYPE_LOONGARCH_CPU) - && !object_class_is_abstract(oc)) { - return oc; - } - return NULL; + 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); - qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, - get_float_exception_flags(&env->fp_status)); + qemu_fprintf(f, " FCSR0 0x%08x\n", env->fcsr0); /* gpr */ for (i = 0; i < 32; i++) { @@ -645,15 +771,17 @@ void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY); qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 "," " PRCFG3=%016" PRIx64 "\n", - env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3); + env->CSR_PRCFG1, env->CSR_PRCFG2, env->CSR_PRCFG3); 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"); } @@ -664,7 +792,7 @@ 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, @@ -672,6 +800,7 @@ static struct TCGCPUOps loongarch_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, + .cpu_exec_halt = loongarch_cpu_has_work, .do_interrupt = loongarch_cpu_do_interrupt, .do_transaction_failed = loongarch_cpu_do_transaction_failed, #endif @@ -682,51 +811,80 @@ static struct TCGCPUOps loongarch_tcg_ops = { #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps loongarch_sysemu_ops = { + .write_elf64_note = loongarch_cpu_write_elf64_note, .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, }; -#endif -static gchar *loongarch_gdb_arch_name(CPUState *cs) +static int64_t loongarch_cpu_get_arch_id(CPUState *cs) { - return g_strdup("loongarch64"); + 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 = 35; - cc->gdb_core_xml_file = "loongarch-base64.xml"; cc->gdb_stop_before_watchpoint = true; - cc->gdb_arch_name = loongarch_gdb_arch_name; #ifdef CONFIG_TCG cc->tcg_ops = &loongarch_tcg_ops; #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), \ } @@ -736,39 +894,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 e15c633b0b..86c86c6c95 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -8,13 +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 @@ -27,16 +30,16 @@ #define IOCSRF_GMOD 9 #define IOCSRF_VM 11 +#define VERSION_REG 0x0 #define FEATURE_REG 0x8 #define VENDOR_REG 0x10 #define CPUNAME_REG 0x20 #define MISC_FUNC_REG 0x420 #define IOCSRM_EXTIOI_EN 48 +#define IOCSRM_EXTIOI_INT_ENCODE 49 #define IOCSR_MEM_SIZE 0x428 -#define TCG_GUEST_DEFAULT_MO (0) - #define FCSR0_M1 0x1f /* FCSR1 mask, Enables */ #define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */ #define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */ @@ -52,6 +55,10 @@ FIELD(FCSR0, CAUSE, 24, 5) 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) \ @@ -125,6 +132,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) @@ -141,6 +153,7 @@ FIELD(CPUCFG2, LLFTP_VER, 15, 3) FIELD(CPUCFG2, LBT_X86, 18, 1) FIELD(CPUCFG2, LBT_ARM, 19, 1) FIELD(CPUCFG2, LBT_MIPS, 20, 1) +FIELD(CPUCFG2, LBT_ALL, 18, 3) FIELD(CPUCFG2, LSPW, 21, 1) FIELD(CPUCFG2, LAM, 22, 1) @@ -239,6 +252,27 @@ 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; +}; + +#ifdef CONFIG_TCG struct LoongArchTLB { uint64_t tlb_misc; /* Fields corresponding to CSR_TLBELO0/1 */ @@ -246,23 +280,35 @@ struct LoongArchTLB { uint64_t tlb_entry1; }; typedef struct LoongArchTLB LoongArchTLB; +#endif + +enum loongarch_features { + LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ + LOONGARCH_FEATURE_PMU, +}; + +typedef struct LoongArchBT { + /* scratch registers */ + uint64_t scr0; + uint64_t scr1; + uint64_t scr2; + uint64_t scr3; + /* loongarch eflags */ + uint32_t eflags; + uint32_t ftop; +} lbt_t; typedef struct CPUArchState { uint64_t gpr[32]; uint64_t pc; - uint64_t fpr[32]; - float_status fp_status; + fpr_t fpr[32]; bool cf[8]; - uint32_t fcsr0; - uint32_t fcsr0_mask; + lbt_t lbt; uint32_t cpucfg[21]; - uint64_t lladdr; /* LL virtual address compared against SC */ - uint64_t llval; - /* LoongArch CSRs */ uint64_t CSR_CRMD; uint64_t CSR_PRMD; @@ -286,6 +332,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; @@ -317,15 +364,29 @@ typedef struct CPUArchState { uint64_t CSR_DBG; uint64_t CSR_DERA; uint64_t CSR_DSAVE; + struct { + uint64_t guest_addr; + } stealtime; +#ifdef CONFIG_TCG + float_status fp_status; + uint32_t fcsr0_mask; + uint64_t lladdr; /* LL virtual address compared against SC */ + uint64_t llval; +#endif #ifndef CONFIG_USER_ONLY +#ifdef CONFIG_TCG LoongArchTLB tlb[LOONGARCH_TLB_MAX]; +#endif - 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; + + struct loongarch_boot_info *boot_info; #endif } CPULoongArchState; @@ -336,37 +397,32 @@ typedef struct CPUArchState { * A LoongArch CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPULoongArchState env; QEMUTimer timer; + uint32_t phy_id; + OnOffAuto lbt; + OnOffAuto pmu; /* '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; }; /* @@ -376,48 +432,61 @@ struct LoongArchCPUClass { */ #define MMU_PLV_KERNEL 0 #define MMU_PLV_USER 3 -#define MMU_IDX_KERNEL MMU_PLV_KERNEL -#define MMU_IDX_USER MMU_PLV_USER -#define MMU_IDX_DA 4 +#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) { -#ifdef CONFIG_USER_ONLY - return MMU_IDX_USER; -#else - if (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG)) { - return FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); + return FIELD_EX32(env->cpucfg[1], CPUCFG1, ARCH) == CPUCFG1_ARCH_LA64; +} + +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; } - return MMU_IDX_DA; -#endif } /* * LoongArch CPUs hardware flags. */ #define HW_FLAGS_PLV_MASK R_CSR_CRMD_PLV_MASK /* 0x03 */ -#define HW_FLAGS_CRMD_PG R_CSR_CRMD_PG_MASK /* 0x10 */ #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, - target_ulong *pc, - target_ulong *cs_base, - uint32_t *flags) +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 = 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..580362ac3e --- /dev/null +++ b/target/loongarch/cpu_helper.c @@ -0,0 +1,239 @@ +/* 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" + +#ifdef CONFIG_TCG +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; +} +#else +static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + return TLBRET_NOMATCH; +} +#endif + +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/fpu_helper.c b/target/loongarch/fpu_helper.c deleted file mode 100644 index 4b9637210a..0000000000 --- a/target/loongarch/fpu_helper.c +++ /dev/null @@ -1,879 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * LoongArch float point emulation helpers for QEMU - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "fpu/softfloat.h" -#include "internals.h" - -static inline uint64_t nanbox_s(float32 fp) -{ - return fp | MAKE_64BIT_MASK(32, 32); -} - -/* Convert loongarch rounding mode in fcsr0 to IEEE library */ -static const FloatRoundMode ieee_rm[4] = { - float_round_nearest_even, - float_round_to_zero, - float_round_up, - float_round_down -}; - -void restore_fp_status(CPULoongArchState *env) -{ - set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], - &env->fp_status); - set_flush_to_zero(0, &env->fp_status); -} - -static int ieee_ex_to_loongarch(int xcpt) -{ - int ret = 0; - if (xcpt & float_flag_invalid) { - ret |= FP_INVALID; - } - if (xcpt & float_flag_overflow) { - ret |= FP_OVERFLOW; - } - if (xcpt & float_flag_underflow) { - ret |= FP_UNDERFLOW; - } - if (xcpt & float_flag_divbyzero) { - ret |= FP_DIV0; - } - if (xcpt & float_flag_inexact) { - ret |= FP_INEXACT; - } - return ret; -} - -static void 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) { - SET_FP_CAUSE(env->fcsr0, flags); - return; - } else { - flags = ieee_ex_to_loongarch(flags); - SET_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 update_fcsr0(CPULoongArchState *env, uintptr_t pc) -{ - update_fcsr0_mask(env, pc, 0); -} - -uint64_t helper_fadd_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_add((uint32_t)fj, (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fadd_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_add(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fsub_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_sub((uint32_t)fj, (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fsub_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_sub(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmul_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_mul((uint32_t)fj, (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmul_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_mul(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fdiv_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_div((uint32_t)fj, (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fdiv_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_div(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmax_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_maxnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmax_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_maxnum(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmin_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_minnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmin_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_minnum(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmaxa_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_maxnummag((uint32_t)fj, - (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmaxa_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_maxnummag(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmina_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = nanbox_s(float32_minnummag((uint32_t)fj, - (uint32_t)fk, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmina_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - - fd = float64_minnummag(fj, fk, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fscaleb_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - int32_t n = (int32_t)fk; - - fd = nanbox_s(float32_scalbn((uint32_t)fj, - n > 0x200 ? 0x200 : - n < -0x200 ? -0x200 : n, - &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fscaleb_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) -{ - uint64_t fd; - int64_t n = (int64_t)fk; - - fd = float64_scalbn(fj, - n > 0x1000 ? 0x1000 : - n < -0x1000 ? -0x1000 : n, - &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fsqrt_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = nanbox_s(float32_sqrt((uint32_t)fj, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fsqrt_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = float64_sqrt(fj, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_frecip_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = nanbox_s(float32_div(float32_one, (uint32_t)fj, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_frecip_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = float64_div(float64_one, fj, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_frsqrt_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - uint32_t fp; - - fp = float32_sqrt((uint32_t)fj, &env->fp_status); - fd = nanbox_s(float32_div(float32_one, fp, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_frsqrt_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fp, fd; - - fp = float64_sqrt(fj, &env->fp_status); - fd = float64_div(float64_one, fp, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_flogb_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - uint32_t fp; - float_status *status = &env->fp_status; - FloatRoundMode old_mode = get_float_rounding_mode(status); - - set_float_rounding_mode(float_round_down, status); - fp = float32_log2((uint32_t)fj, status); - fd = nanbox_s(float32_round_to_int(fp, status)); - set_float_rounding_mode(old_mode, status); - update_fcsr0_mask(env, GETPC(), float_flag_inexact); - return fd; -} - -uint64_t helper_flogb_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - float_status *status = &env->fp_status; - FloatRoundMode old_mode = get_float_rounding_mode(status); - - set_float_rounding_mode(float_round_down, status); - fd = float64_log2(fj, status); - fd = float64_round_to_int(fd, status); - set_float_rounding_mode(old_mode, status); - update_fcsr0_mask(env, GETPC(), float_flag_inexact); - return fd; -} - -uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj) -{ - float32 f = fj; - bool sign = float32_is_neg(f); - - if (float32_is_infinity(f)) { - return sign ? 1 << 2 : 1 << 6; - } else if (float32_is_zero(f)) { - return sign ? 1 << 5 : 1 << 9; - } else if (float32_is_zero_or_denormal(f)) { - return sign ? 1 << 4 : 1 << 8; - } else if (float32_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; - } else { - return sign ? 1 << 3 : 1 << 7; - } -} - -uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj) -{ - float64 f = fj; - bool sign = float64_is_neg(f); - - if (float64_is_infinity(f)) { - return sign ? 1 << 2 : 1 << 6; - } else if (float64_is_zero(f)) { - return sign ? 1 << 5 : 1 << 9; - } else if (float64_is_zero_or_denormal(f)) { - return sign ? 1 << 4 : 1 << 8; - } else if (float64_is_any_nan(f)) { - float_status s = { }; /* for snan_bit_is_one */ - return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; - } else { - return sign ? 1 << 3 : 1 << 7; - } -} - -uint64_t helper_fmuladd_s(CPULoongArchState *env, uint64_t fj, - uint64_t fk, uint64_t fa, uint32_t flag) -{ - uint64_t fd; - - fd = nanbox_s(float32_muladd((uint32_t)fj, (uint32_t)fk, - (uint32_t)fa, flag, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fmuladd_d(CPULoongArchState *env, uint64_t fj, - uint64_t fk, uint64_t fa, uint32_t flag) -{ - uint64_t fd; - - fd = float64_muladd(fj, fk, fa, flag, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -static uint64_t fcmp_common(CPULoongArchState *env, FloatRelation cmp, - uint32_t flags) -{ - bool ret; - - 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(); - } - update_fcsr0(env, GETPC()); - - return ret; -} - -/* fcmp_cXXX_s */ -uint64_t helper_fcmp_c_s(CPULoongArchState *env, uint64_t fj, - uint64_t fk, uint32_t flags) -{ - FloatRelation cmp = float32_compare_quiet((uint32_t)fj, - (uint32_t)fk, &env->fp_status); - return fcmp_common(env, cmp, flags); -} - -/* fcmp_sXXX_s */ -uint64_t helper_fcmp_s_s(CPULoongArchState *env, uint64_t fj, - uint64_t fk, uint32_t flags) -{ - FloatRelation cmp = float32_compare((uint32_t)fj, - (uint32_t)fk, &env->fp_status); - return fcmp_common(env, cmp, flags); -} - -/* fcmp_cXXX_d */ -uint64_t helper_fcmp_c_d(CPULoongArchState *env, uint64_t fj, - uint64_t fk, uint32_t flags) -{ - FloatRelation cmp = float64_compare_quiet(fj, fk, &env->fp_status); - return fcmp_common(env, cmp, flags); -} - -/* fcmp_sXXX_d */ -uint64_t helper_fcmp_s_d(CPULoongArchState *env, uint64_t fj, - uint64_t fk, uint32_t flags) -{ - FloatRelation cmp = float64_compare(fj, fk, &env->fp_status); - return fcmp_common(env, cmp, flags); -} - -/* floating point conversion */ -uint64_t helper_fcvt_s_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = nanbox_s(float64_to_float32(fj, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_fcvt_d_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = float32_to_float64((uint32_t)fj, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ffint_s_w(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = nanbox_s(int32_to_float32((int32_t)fj, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ffint_s_l(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = nanbox_s(int64_to_float32(fj, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ffint_d_w(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = int32_to_float64((int32_t)fj, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ffint_d_l(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = int64_to_float64(fj, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_frint_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = (uint64_t)(float32_round_to_int((uint32_t)fj, &env->fp_status)); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_frint_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - - fd = float64_round_to_int(fj, &env->fp_status); - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_down, &env->fp_status); - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_down, &env->fp_status); - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_down, &env->fp_status); - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_down, &env->fp_status); - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_up, &env->fp_status); - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_up, &env->fp_status); - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_up, &env->fp_status); - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_up, &env->fp_status); - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj) -{ - uint32_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return (uint64_t)fd; -} - -uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj) -{ - uint64_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - 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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj) -{ - uint32_t fd; - FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); - - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); - 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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return (uint64_t)fd; -} - -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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -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)) { - if (float32_is_any_nan((uint32_t)fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -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)) { - if (float64_is_any_nan(fj)) { - fd = 0; - } - } - update_fcsr0(env, GETPC()); - return fd; -} - -void helper_set_rounding_mode(CPULoongArchState *env) -{ - set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], - &env->fp_status); -} diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index a4d1e28e36..dafa4feb75 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -10,6 +10,8 @@ #include "cpu.h" #include "internals.h" #include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "vec.h" uint64_t read_fcc(CPULoongArchState *env) { @@ -31,74 +33,162 @@ void write_fcc(CPULoongArchState *env, uint64_t val) 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); - if (0 <= n && n < 32) { - return gdb_get_regl(mem_buf, env->gpr[n]); - } else if (n == 32) { - /* orig_a0 */ - return gdb_get_regl(mem_buf, 0); - } else if (n == 33) { - return gdb_get_regl(mem_buf, env->pc); - } else if (n == 34) { - return gdb_get_regl(mem_buf, env->CSR_BADV); + if (0 <= n && n <= 34) { + uint64_t val; + + if (n < 32) { + val = env->gpr[n]; + } else if (n == 32) { + /* orig_a0 */ + val = 0; + } else if (n == 33) { + val = env->pc; + } else /* if (n == 34) */ { + val = env->CSR_BADV; + } + + 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_le_p(mem_buf); + read_length = 8; + } else { + tmp = ldl_le_p(mem_buf); + read_length = 4; + } + if (0 <= n && n < 32) { env->gpr[n] = tmp; - length = sizeof(target_ulong); + length = read_length; } else if (n == 33) { - env->pc = tmp; - length = sizeof(target_ulong); + 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]); - } else if (n == 32) { - uint64_t val = read_fcc(env); - return gdb_get_reg64(mem_buf, val); - } else if (n == 33) { + 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) { return gdb_get_reg32(mem_buf, env->fcsr0); } 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_le_p(mem_buf); length = 8; - } else if (n == 32) { - uint64_t val = ldq_p(mem_buf); - write_fcc(env, val); - length = 8; - } else if (n == 33) { - env->fcsr0 = ldl_p(mem_buf); + } else if (32 <= n && n < 40) { + env->cf[n - 32] = ldub_p(mem_buf); + length = 1; + } else if (n == 40) { + env->fcsr0 = ldl_le_p(mem_buf); length = 4; } return length; } +#define VREG_NUM 32 +#define REG64_LEN 64 + +static int loongarch_gdb_get_vec(CPUState *cs, GByteArray *mem_buf, int n, int vl) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + int i, length = 0; + + if (0 <= n && n < VREG_NUM) { + for (i = 0; i < vl / REG64_LEN; i++) { + length += gdb_get_reg64(mem_buf, env->fpr[n].vreg.D(i)); + } + } + + return length; +} + +static int loongarch_gdb_set_vec(CPUState *cs, uint8_t *mem_buf, int n, int vl) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + int i, length = 0; + + if (0 <= n && n < VREG_NUM) { + for (i = 0; i < vl / REG64_LEN; i++) { + env->fpr[n].vreg.D(i) = ldq_le_p(mem_buf + 8 * i); + length += 8; + } + } + + return length; +} + +static int loongarch_gdb_get_lsx(CPUState *cs, GByteArray *mem_buf, int n) +{ + return loongarch_gdb_get_vec(cs, mem_buf, n, LSX_LEN); +} + +static int loongarch_gdb_set_lsx(CPUState *cs, uint8_t *mem_buf, int n) +{ + return loongarch_gdb_set_vec(cs, mem_buf, n, LSX_LEN); +} + +static int loongarch_gdb_get_lasx(CPUState *cs, GByteArray *mem_buf, int n) +{ + return loongarch_gdb_get_vec(cs, mem_buf, n, LASX_LEN); +} + +static int loongarch_gdb_set_lasx(CPUState *cs, uint8_t *mem_buf, int n) +{ + return loongarch_gdb_set_vec(cs, mem_buf, n, LASX_LEN); +} + void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs) { - gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu, - 41, "loongarch-fpu.xml", 0); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, FP)) { + gdb_register_coprocessor(cs, loongarch_gdb_get_fpu, loongarch_gdb_set_fpu, + gdb_find_static_feature("loongarch-fpu.xml"), 0); + } + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LSX)) { + gdb_register_coprocessor(cs, loongarch_gdb_get_lsx, loongarch_gdb_set_lsx, + gdb_find_static_feature("loongarch-lsx.xml"), 0); + } + + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LASX)) { + gdb_register_coprocessor(cs, loongarch_gdb_get_lasx, loongarch_gdb_set_lasx, + gdb_find_static_feature("loongarch-lasx.xml"), 0); + } } diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h index 9c01823a26..b3b64a0215 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -98,6 +98,7 @@ 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) @@ -130,3 +131,590 @@ 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_arith.c.inc b/target/loongarch/insn_trans/trans_arith.c.inc deleted file mode 100644 index 8e45eadbc8..0000000000 --- a/target/loongarch/insn_trans/trans_arith.c.inc +++ /dev/null @@ -1,304 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static bool gen_rrr(DisasContext *ctx, arg_rrr *a, - DisasExtend src1_ext, DisasExtend src2_ext, - DisasExtend dst_ext, void (*func)(TCGv, TCGv, TCGv)) -{ - TCGv dest = gpr_dst(ctx, a->rd, dst_ext); - TCGv src1 = gpr_src(ctx, a->rj, src1_ext); - TCGv src2 = gpr_src(ctx, a->rk, src2_ext); - - func(dest, src1, src2); - gen_set_gpr(a->rd, dest, dst_ext); - - return true; -} - -static bool gen_rri_v(DisasContext *ctx, arg_rr_i *a, - DisasExtend src_ext, DisasExtend dst_ext, - void (*func)(TCGv, TCGv, TCGv)) -{ - TCGv dest = gpr_dst(ctx, a->rd, dst_ext); - TCGv src1 = gpr_src(ctx, a->rj, src_ext); - TCGv src2 = tcg_constant_tl(a->imm); - - func(dest, src1, src2); - gen_set_gpr(a->rd, dest, dst_ext); - - return true; -} - -static bool gen_rri_c(DisasContext *ctx, arg_rr_i *a, - DisasExtend src_ext, DisasExtend dst_ext, - void (*func)(TCGv, TCGv, target_long)) -{ - TCGv dest = gpr_dst(ctx, a->rd, dst_ext); - TCGv src1 = gpr_src(ctx, a->rj, src_ext); - - func(dest, src1, a->imm); - gen_set_gpr(a->rd, dest, dst_ext); - - return true; -} - -static bool gen_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, - DisasExtend src_ext, DisasExtend dst_ext, - void (*func)(TCGv, TCGv, TCGv, target_long)) -{ - TCGv dest = gpr_dst(ctx, a->rd, dst_ext); - TCGv src1 = gpr_src(ctx, a->rj, src_ext); - TCGv src2 = gpr_src(ctx, a->rk, src_ext); - - func(dest, src1, src2, a->sa); - gen_set_gpr(a->rd, dest, dst_ext); - - return true; -} - -static bool trans_lu12i_w(DisasContext *ctx, arg_lu12i_w *a) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - - tcg_gen_movi_tl(dest, a->imm << 12); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -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); - - tcg_gen_movi_tl(dest, addr); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static void gen_slt(TCGv dest, TCGv src1, TCGv src2) -{ - tcg_gen_setcond_tl(TCG_COND_LT, dest, src1, src2); -} - -static void gen_sltu(TCGv dest, TCGv src1, TCGv src2) -{ - tcg_gen_setcond_tl(TCG_COND_LTU, dest, src1, src2); -} - -static void gen_mulh_w(TCGv dest, TCGv src1, TCGv src2) -{ - tcg_gen_mul_i64(dest, src1, src2); - tcg_gen_sari_i64(dest, dest, 32); -} - -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) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv zero = tcg_constant_tl(0); - - /* - * If min / -1, set the divisor to 1. - * This avoids potential host overflow trap and produces min. - * If x / 0, set the divisor to 1. - * This avoids potential host overflow trap; - * the required result is undefined. - */ - tcg_gen_setcondi_tl(TCG_COND_EQ, ret, src1, INT64_MIN); - tcg_gen_setcondi_tl(TCG_COND_EQ, t0, src2, -1); - tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src2, 0); - 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) -{ - TCGv zero = tcg_constant_tl(0); - TCGv one = tcg_constant_tl(1); - - /* - * If x / 0, set the divisor to 1. - * This avoids potential host overflow trap; - * the required result is undefined. - */ - tcg_gen_movcond_tl(TCG_COND_EQ, ret, src2, zero, one, src2); -} - -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) -{ - 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) -{ - 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) -{ - 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) -{ - TCGv t0 = tcg_temp_new(); - /* 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) -{ - TCGv t0 = tcg_temp_new(); - /* 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) -{ - 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) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rd, EXT_NONE); - TCGv src2 = tcg_constant_tl(a->imm); - - tcg_gen_deposit_tl(dest, src1, src2, 32, 32); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = tcg_constant_tl(a->imm); - - tcg_gen_deposit_tl(dest, src1, src2, 52, 12); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static target_ulong gen_pcaddi(target_ulong pc, int imm) -{ - return pc + (imm << 2); -} - -static target_ulong gen_pcalau12i(target_ulong pc, int imm) -{ - return (pc + (imm << 12)) & ~0xfff; -} - -static target_ulong gen_pcaddu12i(target_ulong pc, int imm) -{ - return pc + (imm << 12); -} - -static target_ulong gen_pcaddu18i(target_ulong pc, int imm) -{ - return pc + ((target_ulong)(imm) << 18); -} - -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); - - 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) 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_bit.c.inc b/target/loongarch/insn_trans/trans_bit.c.inc deleted file mode 100644 index b01e4aeb23..0000000000 --- a/target/loongarch/insn_trans/trans_bit.c.inc +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static bool gen_rr(DisasContext *ctx, arg_rr *a, - DisasExtend src_ext, DisasExtend dst_ext, - void (*func)(TCGv, TCGv)) -{ - TCGv dest = gpr_dst(ctx, a->rd, dst_ext); - TCGv src1 = gpr_src(ctx, a->rj, src_ext); - - func(dest, src1); - gen_set_gpr(a->rd, dest, dst_ext); - - return true; -} - -static void gen_bytepick_w(TCGv dest, TCGv src1, TCGv src2, target_long sa) -{ - tcg_gen_concat_tl_i64(dest, src1, src2); - tcg_gen_sextract_i64(dest, dest, (32 - sa * 8), 32); -} - -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 bool gen_bstrins(DisasContext *ctx, arg_rr_ms_ls *a, - DisasExtend dst_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; - } - - 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; -} - -static void gen_clz_w(TCGv dest, TCGv src1) -{ - tcg_gen_clzi_tl(dest, src1, TARGET_LONG_BITS); - tcg_gen_subi_tl(dest, dest, TARGET_LONG_BITS - 32); -} - -static void gen_clo_w(TCGv dest, TCGv src1) -{ - tcg_gen_not_tl(dest, src1); - tcg_gen_ext32u_tl(dest, dest); - gen_clz_w(dest, dest); -} - -static void gen_ctz_w(TCGv dest, TCGv src1) -{ - tcg_gen_ori_tl(dest, src1, (target_ulong)MAKE_64BIT_MASK(32, 32)); - tcg_gen_ctzi_tl(dest, dest, TARGET_LONG_BITS); -} - -static void gen_cto_w(TCGv dest, TCGv src1) -{ - tcg_gen_not_tl(dest, src1); - gen_ctz_w(dest, dest); -} - -static void gen_clz_d(TCGv dest, TCGv src1) -{ - tcg_gen_clzi_i64(dest, src1, TARGET_LONG_BITS); -} - -static void gen_clo_d(TCGv dest, TCGv src1) -{ - tcg_gen_not_tl(dest, src1); - gen_clz_d(dest, dest); -} - -static void gen_ctz_d(TCGv dest, TCGv src1) -{ - tcg_gen_ctzi_tl(dest, src1, TARGET_LONG_BITS); -} - -static void gen_cto_d(TCGv dest, TCGv src1) -{ - tcg_gen_not_tl(dest, src1); - gen_ctz_d(dest, dest); -} - -static void gen_revb_2w(TCGv dest, TCGv src1) -{ - tcg_gen_bswap64_i64(dest, src1); - tcg_gen_rotri_i64(dest, dest, 32); -} - -static void gen_revb_2h(TCGv dest, TCGv src1) -{ - TCGv mask = tcg_constant_tl(0x00FF00FF); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - - tcg_gen_shri_tl(t0, src1, 8); - tcg_gen_and_tl(t0, t0, mask); - 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) -{ - TCGv mask = tcg_constant_tl(0x00FF00FF00FF00FFULL); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - - tcg_gen_shri_tl(t0, src1, 8); - tcg_gen_and_tl(t0, t0, mask); - 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) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 mask = tcg_constant_i64(0x0000ffff0000ffffull); - - tcg_gen_shri_i64(t0, src1, 16); - tcg_gen_and_i64(t1, src1, mask); - 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) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv mask = tcg_constant_tl(0x0000FFFF0000FFFFULL); - - tcg_gen_shri_tl(t1, src1, 16); - tcg_gen_and_tl(t1, t1, mask); - tcg_gen_and_tl(t0, src1, mask); - 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) -{ - TCGv zero = tcg_constant_tl(0); - - tcg_gen_movcond_tl(TCG_COND_EQ, dest, src2, zero, zero, src1); -} - -static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) -{ - TCGv zero = tcg_constant_tl(0); - - 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_bstrins, EXT_SIGN) -TRANS(bstrins_d, gen_bstrins, EXT_NONE) -TRANS(bstrpick_w, gen_bstrpick, EXT_SIGN) -TRANS(bstrpick_d, gen_bstrpick, EXT_NONE) diff --git a/target/loongarch/insn_trans/trans_branch.c.inc b/target/loongarch/insn_trans/trans_branch.c.inc deleted file mode 100644 index 65dbdff41e..0000000000 --- a/target/loongarch/insn_trans/trans_branch.c.inc +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static bool trans_b(DisasContext *ctx, arg_b *a) -{ - gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); - ctx->base.is_jmp = DISAS_NORETURN; - return true; -} - -static bool trans_bl(DisasContext *ctx, arg_bl *a) -{ - tcg_gen_movi_tl(cpu_gpr[1], ctx->base.pc_next + 4); - gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); - ctx->base.is_jmp = DISAS_NORETURN; - return true; -} - -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); - gen_set_gpr(a->rd, dest, EXT_NONE); - tcg_gen_lookup_and_goto_ptr(); - ctx->base.is_jmp = DISAS_NORETURN; - return true; -} - -static void gen_bc(DisasContext *ctx, TCGv src1, TCGv src2, - target_long offs, TCGCond cond) -{ - TCGLabel *l = gen_new_label(); - tcg_gen_brcond_tl(cond, src1, src2, l); - gen_goto_tb(ctx, 1, ctx->base.pc_next + 4); - gen_set_label(l); - gen_goto_tb(ctx, 0, ctx->base.pc_next + offs); - ctx->base.is_jmp = DISAS_NORETURN; -} - -static bool gen_rr_bc(DisasContext *ctx, arg_rr_offs *a, TCGCond cond) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); - - gen_bc(ctx, src1, src2, a->offs, cond); - return true; -} - -static bool gen_rz_bc(DisasContext *ctx, arg_r_offs *a, TCGCond cond) -{ - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = tcg_constant_tl(0); - - gen_bc(ctx, src1, src2, a->offs, cond); - return true; -} - -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, - 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) diff --git a/target/loongarch/insn_trans/trans_extra.c.inc b/target/loongarch/insn_trans/trans_extra.c.inc deleted file mode 100644 index ad713cd61e..0000000000 --- a/target/loongarch/insn_trans/trans_extra.c.inc +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -static bool trans_break(DisasContext *ctx, arg_break *a) -{ - generate_exception(ctx, EXCCODE_BRK); - return true; -} - -static bool trans_syscall(DisasContext *ctx, arg_syscall *a) -{ - generate_exception(ctx, EXCCODE_SYS); - return true; -} - -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); - return true; -} - -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); - return true; -} - -static bool gen_rdtime(DisasContext *ctx, arg_rr *a, - bool word, bool high) -{ - 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); - if (word) { - tcg_gen_sextract_tl(dst1, dst1, high ? 32 : 0, 32); - } - tcg_gen_ld_i64(dst2, cpu_env, offsetof(CPULoongArchState, CSR_TID)); - - return true; -} - -static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a) -{ - return gen_rdtime(ctx, a, 1, 0); -} - -static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) -{ - return gen_rdtime(ctx, a, 1, 1); -} - -static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) -{ - return gen_rdtime(ctx, a, 0, 0); -} - -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_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static bool gen_crc(DisasContext *ctx, arg_rrr *a, - void (*func)(TCGv, TCGv, TCGv, TCGv), - TCGv tsz) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_SIGN); - TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); - TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); - - func(dest, src2, src1, tsz); - gen_set_gpr(a->rd, dest, EXT_SIGN); - - 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)) 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 7081fbb89b..0000000000 --- a/target/loongarch/insn_trans/trans_farith.c.inc +++ /dev/null @@ -1,135 +0,0 @@ -/* 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)) -{ - CHECK_FPE; - - 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)) -{ - CHECK_FPE; - - 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); - - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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_result) -TRANS(fnmadd_d, gen_muladd, gen_helper_fmuladd_d, float_muladd_negate_result) -TRANS(fnmsub_s, gen_muladd, gen_helper_fmuladd_s, - float_muladd_negate_c | float_muladd_negate_result) -TRANS(fnmsub_d, 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/insn_trans/trans_fcmp.c.inc deleted file mode 100644 index 2ccf646ccb..0000000000 --- a/target/loongarch/insn_trans/trans_fcmp.c.inc +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -/* bit0(signaling/quiet) bit1(lt) bit2(eq) bit3(un) bit4(neq) */ -static uint32_t get_fcmp_flags(int cond) -{ - uint32_t flags = 0; - - if (cond & 0x1) { - flags |= FCMP_LT; - } - if (cond & 0x2) { - flags |= FCMP_EQ; - } - if (cond & 0x4) { - flags |= FCMP_UN; - } - if (cond & 0x8) { - flags |= FCMP_GT | FCMP_LT; - } - return flags; -} - -static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) -{ - TCGv var; - uint32_t flags; - void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); - - CHECK_FPE; - - var = tcg_temp_new(); - 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)); - - tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd])); - tcg_temp_free(var); - return true; -} - -static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a) -{ - TCGv var; - uint32_t flags; - void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); - - CHECK_FPE; - - var = tcg_temp_new(); - 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)); - - tcg_gen_st8_tl(var, cpu_env, offsetof(CPULoongArchState, cf[a->cd])); - - tcg_temp_free(var); - return true; -} 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 3025a1d3e9..0000000000 --- a/target/loongarch/insn_trans/trans_fmemory.c.inc +++ /dev/null @@ -1,175 +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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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 8e5106db4e..0000000000 --- a/target/loongarch/insn_trans/trans_fmov.c.inc +++ /dev/null @@ -1,181 +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; - - CHECK_FPE; - - 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]; - - CHECK_FPE; - - 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); - - CHECK_FPE; - - 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); - - CHECK_FPE; - - 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); - - CHECK_FPE; - - if (mask == UINT32_MAX) { - tcg_gen_st32_i64(Rj, cpu_env, offsetof(CPULoongArchState, fcsr0)); - } else { - TCGv_i32 fcsr0 = tcg_temp_new_i32(); - TCGv_i32 temp = tcg_temp_new_i32(); - - tcg_gen_ld_i32(fcsr0, cpu_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, cpu_env, offsetof(CPULoongArchState, fcsr0)); - - tcg_temp_free_i32(temp); - tcg_temp_free_i32(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(cpu_env); - } - return true; -} - -static bool trans_movfcsr2gr(DisasContext *ctx, arg_movfcsr2gr *a) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - - CHECK_FPE; - - tcg_gen_ld32u_i64(dest, cpu_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; - - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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; - - CHECK_FPE; - - 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) -{ - CHECK_FPE; - - 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/insn_trans/trans_memory.c.inc b/target/loongarch/insn_trans/trans_memory.c.inc deleted file mode 100644 index d5eb31147c..0000000000 --- a/target/loongarch/insn_trans/trans_memory.c.inc +++ /dev/null @@ -1,229 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -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; - } - - 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; -} - -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; - } - - tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); - - if (temp) { - tcg_temp_free(temp); - } - - return true; -} - -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(); - - 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; -} - -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(); - - tcg_gen_add_tl(addr, src1, src2); - tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); - tcg_temp_free(addr); - - return true; -} - -static bool gen_load_gt(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); - - gen_helper_asrtgt_d(cpu_env, src1, src2); - tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static bool gen_load_le(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); - - gen_helper_asrtle_d(cpu_env, src1, src2); - tcg_gen_qemu_ld_tl(dest, src1, ctx->mem_idx, mop); - gen_set_gpr(a->rd, dest, EXT_NONE); - - return true; -} - -static bool gen_store_gt(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); - - gen_helper_asrtgt_d(cpu_env, src1, src2); - tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); - - return true; -} - -static bool gen_store_le(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); - - gen_helper_asrtle_d(cpu_env, src1, src2); - tcg_gen_qemu_st_tl(data, src1, ctx->mem_idx, mop); - - return true; -} - -static bool trans_preld(DisasContext *ctx, arg_preld *a) -{ - return true; -} - -static bool trans_dbar(DisasContext *ctx, arg_dbar * a) -{ - tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); - return true; -} - -static bool trans_ibar(DisasContext *ctx, arg_ibar *a) -{ - ctx->base.is_jmp = DISAS_STOP; - return true; -} - -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; - } - - 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; -} - -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; - } - - 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) diff --git a/target/loongarch/insn_trans/trans_shift.c.inc b/target/loongarch/insn_trans/trans_shift.c.inc deleted file mode 100644 index 5260af2337..0000000000 --- a/target/loongarch/insn_trans/trans_shift.c.inc +++ /dev/null @@ -1,106 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -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) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - TCGv_i32 t1 = tcg_temp_new_i32(); - TCGv_i32 t2 = tcg_temp_new_i32(); - TCGv t0 = tcg_temp_new(); - - tcg_gen_andi_tl(t0, src2, 0x1f); - - tcg_gen_trunc_tl_i32(t1, src1); - tcg_gen_trunc_tl_i32(t2, t0); - - 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) -{ - 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) -{ - TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); - TCGv src1 = gpr_src(ctx, a->rj, EXT_ZERO); - - 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) 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 f01635aed6..1a02427627 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,9 +26,23 @@ 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; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -43,12 +52,18 @@ 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); - -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +#endif #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); @@ -57,5 +72,7 @@ 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); void loongarch_cpu_register_gdb_regs_for_features(CPUState *cs); +int loongarch_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, DumpState *s); #endif diff --git a/target/loongarch/iocsr_helper.c b/target/loongarch/iocsr_helper.c deleted file mode 100644 index 505853e17b..0000000000 --- a/target/loongarch/iocsr_helper.c +++ /dev/null @@ -1,70 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - * - * Helpers for IOCSR reads/writes - */ - -#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, - 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, - 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, - 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, - 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, 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, 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, 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, GET_MEMTXATTRS(env), NULL); -} diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c new file mode 100644 index 0000000000..ff81806ca3 --- /dev/null +++ b/target/loongarch/kvm/kvm.c @@ -0,0 +1,1087 @@ +/* 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 "qapi/error.h" +#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; +static unsigned int brk_insn; +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +static int kvm_get_stealtime(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + int err; + struct kvm_device_attr attr = { + .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL, + .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA, + .addr = (uint64_t)&env->stealtime.guest_addr, + }; + + err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); + if (err) { + return 0; + } + + err = kvm_vcpu_ioctl(cs, KVM_GET_DEVICE_ATTR, attr); + if (err) { + error_report("PVTIME: KVM_GET_DEVICE_ATTR: %s", strerror(errno)); + return err; + } + + return 0; +} + +static int kvm_set_stealtime(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + int err; + struct kvm_device_attr attr = { + .group = KVM_LOONGARCH_VCPU_PVTIME_CTRL, + .attr = KVM_LOONGARCH_VCPU_PVTIME_GPA, + .addr = (uint64_t)&env->stealtime.guest_addr, + }; + + err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); + if (err) { + return 0; + } + + err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr); + if (err) { + error_report("PVTIME: KVM_SET_DEVICE_ATTR %s with gpa "TARGET_FMT_lx, + strerror(errno), env->stealtime.guest_addr); + return err; + } + + return 0; +} + +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]; + env->fpr[i].vreg.UD[1] = fpu.fpr[i].val64[1]; + env->fpr[i].vreg.UD[2] = fpu.fpr[i].val64[2]; + env->fpr[i].vreg.UD[3] = fpu.fpr[i].val64[3]; + } + 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]; + fpu.fpr[i].val64[1] = env->fpr[i].vreg.UD[1]; + fpu.fpr[i].val64[2] = env->fpr[i].vreg.UD[2]; + fpu.fpr[i].val64[3] = env->fpr[i].vreg.UD[3]; + } + + 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; +} + +static int kvm_loongarch_put_lbt(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + uint64_t val; + int ret; + + /* check whether vm support LBT firstly */ + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LBT_ALL) != 7) { + return 0; + } + + /* set six LBT registers including scr0-scr3, eflags, ftop */ + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR0, &env->lbt.scr0); + ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR1, &env->lbt.scr1); + ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR2, &env->lbt.scr2); + ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR3, &env->lbt.scr3); + /* + * Be cautious, KVM_REG_LOONGARCH_LBT_FTOP is defined as 64-bit however + * lbt.ftop is 32-bit; the same with KVM_REG_LOONGARCH_LBT_EFLAGS register + */ + val = env->lbt.eflags; + ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_EFLAGS, &val); + val = env->lbt.ftop; + ret |= kvm_set_one_reg(cs, KVM_REG_LOONGARCH_LBT_FTOP, &val); + + return ret; +} + +static int kvm_loongarch_get_lbt(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + uint64_t val; + int ret; + + /* check whether vm support LBT firstly */ + if (FIELD_EX32(env->cpucfg[2], CPUCFG2, LBT_ALL) != 7) { + return 0; + } + + /* get six LBT registers including scr0-scr3, eflags, ftop */ + ret = kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR0, &env->lbt.scr0); + ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR1, &env->lbt.scr1); + ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR2, &env->lbt.scr2); + ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_SCR3, &env->lbt.scr3); + ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_EFLAGS, &val); + env->lbt.eflags = (uint32_t)val; + ret |= kvm_get_one_reg(cs, KVM_REG_LOONGARCH_LBT_FTOP, &val); + env->lbt.ftop = (uint32_t)val; + + return ret; +} + +void kvm_arch_reset_vcpu(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + + env->mp_state = KVM_MP_STATE_RUNNABLE; + kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0); +} + +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, Error **errp) +{ + int ret; + + ret = kvm_loongarch_get_regs_core(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_get_cpucfg(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_lbt(cs); + if (ret) { + return ret; + } + + ret = kvm_get_stealtime(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_get_mpstate(cs); + return ret; +} + +int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) +{ + int ret; + + ret = kvm_loongarch_put_regs_core(cs); + if (ret) { + return ret; + } + + ret = kvm_loongarch_put_cpucfg(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_lbt(cs); + if (ret) { + return ret; + } + + if (level >= KVM_PUT_FULL_STATE) { + /* + * only KVM_PUT_FULL_STATE is required, kvm kernel will clear + * guest_addr for KVM_PUT_RESET_STATE + */ + ret = kvm_set_stealtime(cs); + if (ret) { + return ret; + } + } + + ret = kvm_loongarch_put_mpstate(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)); + } + } +} + +static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) +{ + int ret; + struct kvm_device_attr attr; + + switch (feature) { + case LOONGARCH_FEATURE_LBT: + /* + * Return all if all the LBT features are supported such as: + * KVM_LOONGARCH_VM_FEAT_X86BT + * KVM_LOONGARCH_VM_FEAT_ARMBT + * KVM_LOONGARCH_VM_FEAT_MIPSBT + */ + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_X86BT; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + attr.attr = KVM_LOONGARCH_VM_FEAT_ARMBT; + ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + attr.attr = KVM_LOONGARCH_VM_FEAT_MIPSBT; + ret |= kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + + case LOONGARCH_FEATURE_PMU: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PMU; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + + default: + return false; + } + + return false; +} + +static int kvm_cpu_check_lbt(CPUState *cs, Error **errp) +{ + CPULoongArchState *env = cpu_env(cs); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_LBT); + if (cpu->lbt == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LBT_ALL, 7); + } else { + error_setg(errp, "'lbt' feature not supported by KVM on this host"); + return -ENOTSUP; + } + } else if ((cpu->lbt == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[2] = FIELD_DP32(env->cpucfg[2], CPUCFG2, LBT_ALL, 7); + } + + return 0; +} + +static int kvm_cpu_check_pmu(CPUState *cs, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = cpu_env(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PMU); + if (cpu->pmu == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'pmu' feature not supported by KVM on the host"); + return -ENOTSUP; + } + } else if (cpu->pmu != ON_OFF_AUTO_AUTO) { + /* disable pmu if ON_OFF_AUTO_OFF is set */ + kvm_supported = false; + } + + if (kvm_supported) { + env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMP, 1); + env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMNUM, 3); + env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, PMBITS, 63); + env->cpucfg[6] = FIELD_DP32(env->cpucfg[6], CPUCFG6, UPM, 1); + } + return 0; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ + uint64_t val; + int ret; + Error *local_err = NULL; + + ret = 0; + qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); + + if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { + brk_insn = val; + } + + ret = kvm_cpu_check_lbt(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + } + + ret = kvm_cpu_check_pmu(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + } + + return ret; +} + +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; +} + +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) +{ + if (kvm_sw_breakpoints_active(cpu)) { + dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + } +} + +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)) { + error_report("%s failed", __func__); + 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)) { + error_report("%s failed", __func__); + return -EINVAL; + } + return 0; +} + +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + return -ENOSYS; +} + +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + return -ENOSYS; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ +} + +static bool kvm_loongarch_handle_debug(CPUState *cs, struct kvm_run *run) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = &cpu->env; + + kvm_cpu_synchronize_state(cs); + if (cs->singlestep_enabled) { + return true; + } + + if (kvm_find_sw_breakpoint(cs, env->pc)) { + return true; + } + + return false; +} + +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; + + case KVM_EXIT_DEBUG: + if (kvm_loongarch_handle_debug(cs, run)) { + ret = EXCP_DEBUG; + } + 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..1051a341ec --- /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(CPUState *cs); + +#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..782fd511fd --- /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", "lbt", "pmu", 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..efb20e2fbe 100644 --- a/target/loongarch/machine.c +++ b/target/loongarch/machine.c @@ -8,14 +8,143 @@ #include "qemu/osdep.h" #include "cpu.h" #include "migration/cpu.h" -#include "internals.h" +#include "sysemu/tcg.h" +#include "vec.h" -/* TLB state */ -const VMStateDescription vmstate_tlb = { - .name = "cpu/tlb", +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() + }, +}; + +static bool lbt_needed(void *opaque) +{ + LoongArchCPU *cpu = opaque; + + return !!FIELD_EX64(cpu->env.cpucfg[2], CPUCFG2, LBT_ALL); +} + +static const VMStateDescription vmstate_lbt = { + .name = "cpu/lbt", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .needed = lbt_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(env.lbt.scr0, LoongArchCPU), + VMSTATE_UINT64(env.lbt.scr1, LoongArchCPU), + VMSTATE_UINT64(env.lbt.scr2, LoongArchCPU), + VMSTATE_UINT64(env.lbt.scr3, LoongArchCPU), + VMSTATE_UINT32(env.lbt.eflags, LoongArchCPU), + VMSTATE_UINT32(env.lbt.ftop, LoongArchCPU), + VMSTATE_END_OF_LIST() + }, +}; + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +static bool tlb_needed(void *opaque) +{ + return tcg_enabled(); +} + +/* TLB state */ +static const VMStateDescription vmstate_tlb_entry = { + .name = "cpu/tlb_entry", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { VMSTATE_UINT64(tlb_misc, LoongArchTLB), VMSTATE_UINT64(tlb_entry0, LoongArchTLB), VMSTATE_UINT64(tlb_entry1, LoongArchTLB), @@ -23,19 +152,27 @@ const VMStateDescription vmstate_tlb = { } }; -/* LoongArch CPU state */ - -const VMStateDescription vmstate_loongarch_cpu = { - .name = "cpu", +static const VMStateDescription vmstate_tlb = { + .name = "cpu/tlb", .version_id = 0, .minimum_version_id = 0, - .fields = (VMStateField[]) { + .needed = tlb_needed, + .fields = (const VMStateField[]) { + VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, + 0, vmstate_tlb_entry, LoongArchTLB), + VMSTATE_END_OF_LIST() + } +}; +#endif +/* LoongArch CPU state */ +const VMStateDescription vmstate_loongarch_cpu = { + .name = "cpu", + .version_id = 3, + .minimum_version_id = 3, + .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), @@ -93,10 +230,21 @@ const VMStateDescription vmstate_loongarch_cpu = { VMSTATE_UINT64(env.CSR_DBG, LoongArchCPU), VMSTATE_UINT64(env.CSR_DERA, LoongArchCPU), VMSTATE_UINT64(env.CSR_DSAVE, LoongArchCPU), - /* TLB */ - VMSTATE_STRUCT_ARRAY(env.tlb, LoongArchCPU, LOONGARCH_TLB_MAX, - 0, vmstate_tlb, LoongArchTLB), + + VMSTATE_UINT64(kvm_state_counter, LoongArchCPU), + /* PV steal time */ + VMSTATE_UINT64(env.stealtime.guest_addr, LoongArchCPU), VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * const []) { + &vmstate_fpu, + &vmstate_lsx, + &vmstate_lasx, +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) + &vmstate_tlb, +#endif + &vmstate_lbt, + NULL + } }; diff --git a/target/loongarch/meson.build b/target/loongarch/meson.build index 6376f9e84b..7817318287 100644 --- a/target/loongarch/meson.build +++ b/target/loongarch/meson.build @@ -3,28 +3,21 @@ 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( + 'arch_dump.c', + '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/op_helper.c b/target/loongarch/op_helper.c deleted file mode 100644 index 568c071601..0000000000 --- a/target/loongarch/op_helper.c +++ /dev/null @@ -1,139 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * LoongArch emulation helpers for QEMU. - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -#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" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "internals.h" -#include "qemu/crc32c.h" -#include -#include "cpu-csr.h" - -/* Exceptions helpers */ -void helper_raise_exception(CPULoongArchState *env, uint32_t exception) -{ - do_raise_exception(env, exception, GETPC()); -} - -target_ulong helper_bitrev_w(target_ulong rj) -{ - return (int32_t)revbit32(rj); -} - -target_ulong helper_bitrev_d(target_ulong rj) -{ - return revbit64(rj); -} - -target_ulong helper_bitswap(target_ulong v) -{ - v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | - ((v & (target_ulong)0x5555555555555555ULL) << 1); - v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | - ((v & (target_ulong)0x3333333333333333ULL) << 2); - v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | - ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); - return v; -} - -/* loongarch assert op */ -void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) -{ - if (rj > rk) { - do_raise_exception(env, EXCCODE_BCE, 0); - } -} - -void helper_asrtgt_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) -{ - if (rj <= rk) { - do_raise_exception(env, EXCCODE_BCE, 0); - } -} - -target_ulong helper_crc32(target_ulong val, target_ulong m, uint64_t sz) -{ - uint8_t buf[8]; - target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); - - m &= mask; - stq_le_p(buf, m); - return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); -} - -target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz) -{ - uint8_t buf[8]; - target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); - m &= mask; - stq_le_p(buf, m); - return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); -} - -target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong 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); - - plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); - if (extract64(env->CSR_MISC, R_CSR_MISC_DRDTL_SHIFT + plv, 1)) { - do_raise_exception(env, EXCCODE_IPE, GETPC()); - } - - return cpu_loongarch_get_constant_timer_counter(cpu); -#endif -} - -#ifndef CONFIG_USER_ONLY -void helper_ertn(CPULoongArchState *env) -{ - uint64_t csr_pplv, csr_pie; - if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { - csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV); - csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE); - - 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; - 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; - qemu_log_mask(CPU_LOG_INT, "%s: ERA " TARGET_FMT_lx "\n", - __func__, env->CSR_ERA); - } - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv); - env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie); - - env->lladdr = 1; -} - -void helper_idle(CPULoongArchState *env) -{ - CPUState *cs = env_cpu(env); - - cs->halted = 1; - do_raise_exception(env, EXCP_HLT, 0); -} -#endif 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 7e02787895..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,9 +89,9 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) int64_t old_v = 0; if (val & 0x1) { - qemu_mutex_lock_iothread(); + bql_lock(); loongarch_cpu_set_irq(cpu, IRQ_TIMER, 0); - qemu_mutex_unlock_iothread(); + bql_unlock(); } return old_v; } diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c new file mode 100644 index 0000000000..21bc3b04a9 --- /dev/null +++ b/target/loongarch/tcg/fpu_helper.c @@ -0,0 +1,880 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch float point emulation helpers for QEMU + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "fpu/softfloat.h" +#include "internals.h" + +static inline uint64_t nanbox_s(float32 fp) +{ + return fp | MAKE_64BIT_MASK(32, 32); +} + +/* Convert loongarch rounding mode in fcsr0 to IEEE library */ +static const FloatRoundMode ieee_rm[4] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down +}; + +void restore_fp_status(CPULoongArchState *env) +{ + set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], + &env->fp_status); + set_flush_to_zero(0, &env->fp_status); + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status); +} + +int ieee_ex_to_loongarch(int xcpt) +{ + int ret = 0; + if (xcpt & float_flag_invalid) { + ret |= FP_INVALID; + } + if (xcpt & float_flag_overflow) { + ret |= FP_OVERFLOW; + } + if (xcpt & float_flag_underflow) { + ret |= FP_UNDERFLOW; + } + if (xcpt & float_flag_divbyzero) { + ret |= FP_DIV0; + } + if (xcpt & float_flag_inexact) { + ret |= FP_INEXACT; + } + return ret; +} + +static void 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) { + SET_FP_CAUSE(env->fcsr0, flags); + return; + } else { + flags = ieee_ex_to_loongarch(flags); + SET_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 update_fcsr0(CPULoongArchState *env, uintptr_t pc) +{ + update_fcsr0_mask(env, pc, 0); +} + +uint64_t helper_fadd_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_add((uint32_t)fj, (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fadd_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_add(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fsub_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_sub((uint32_t)fj, (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fsub_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_sub(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmul_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_mul((uint32_t)fj, (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmul_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_mul(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fdiv_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_div((uint32_t)fj, (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fdiv_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_div(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmax_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_maxnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmax_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_maxnum(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmin_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_minnum((uint32_t)fj, (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmin_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_minnum(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmaxa_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_maxnummag((uint32_t)fj, + (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmaxa_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_maxnummag(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmina_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = nanbox_s(float32_minnummag((uint32_t)fj, + (uint32_t)fk, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmina_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + + fd = float64_minnummag(fj, fk, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fscaleb_s(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + int32_t n = (int32_t)fk; + + fd = nanbox_s(float32_scalbn((uint32_t)fj, + n > 0x200 ? 0x200 : + n < -0x200 ? -0x200 : n, + &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fscaleb_d(CPULoongArchState *env, uint64_t fj, uint64_t fk) +{ + uint64_t fd; + int64_t n = (int64_t)fk; + + fd = float64_scalbn(fj, + n > 0x1000 ? 0x1000 : + n < -0x1000 ? -0x1000 : n, + &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fsqrt_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = nanbox_s(float32_sqrt((uint32_t)fj, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fsqrt_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = float64_sqrt(fj, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_frecip_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = nanbox_s(float32_div(float32_one, (uint32_t)fj, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_frecip_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = float64_div(float64_one, fj, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_frsqrt_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + uint32_t fp; + + fp = float32_sqrt((uint32_t)fj, &env->fp_status); + fd = nanbox_s(float32_div(float32_one, fp, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_frsqrt_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fp, fd; + + fp = float64_sqrt(fj, &env->fp_status); + fd = float64_div(float64_one, fp, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_flogb_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + uint32_t fp; + float_status *status = &env->fp_status; + FloatRoundMode old_mode = get_float_rounding_mode(status); + + set_float_rounding_mode(float_round_down, status); + fp = float32_log2((uint32_t)fj, status); + fd = nanbox_s(float32_round_to_int(fp, status)); + set_float_rounding_mode(old_mode, status); + update_fcsr0_mask(env, GETPC(), float_flag_inexact); + return fd; +} + +uint64_t helper_flogb_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + float_status *status = &env->fp_status; + FloatRoundMode old_mode = get_float_rounding_mode(status); + + set_float_rounding_mode(float_round_down, status); + fd = float64_log2(fj, status); + fd = float64_round_to_int(fd, status); + set_float_rounding_mode(old_mode, status); + update_fcsr0_mask(env, GETPC(), float_flag_inexact); + return fd; +} + +uint64_t helper_fclass_s(CPULoongArchState *env, uint64_t fj) +{ + float32 f = fj; + bool sign = float32_is_neg(f); + + if (float32_is_infinity(f)) { + return sign ? 1 << 2 : 1 << 6; + } else if (float32_is_zero(f)) { + return sign ? 1 << 5 : 1 << 9; + } else if (float32_is_zero_or_denormal(f)) { + return sign ? 1 << 4 : 1 << 8; + } else if (float32_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float32_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; + } else { + return sign ? 1 << 3 : 1 << 7; + } +} + +uint64_t helper_fclass_d(CPULoongArchState *env, uint64_t fj) +{ + float64 f = fj; + bool sign = float64_is_neg(f); + + if (float64_is_infinity(f)) { + return sign ? 1 << 2 : 1 << 6; + } else if (float64_is_zero(f)) { + return sign ? 1 << 5 : 1 << 9; + } else if (float64_is_zero_or_denormal(f)) { + return sign ? 1 << 4 : 1 << 8; + } else if (float64_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float64_is_quiet_nan(f, &s) ? 1 << 1 : 1 << 0; + } else { + return sign ? 1 << 3 : 1 << 7; + } +} + +uint64_t helper_fmuladd_s(CPULoongArchState *env, uint64_t fj, + uint64_t fk, uint64_t fa, uint32_t flag) +{ + uint64_t fd; + + fd = nanbox_s(float32_muladd((uint32_t)fj, (uint32_t)fk, + (uint32_t)fa, flag, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fmuladd_d(CPULoongArchState *env, uint64_t fj, + uint64_t fk, uint64_t fa, uint32_t flag) +{ + uint64_t fd; + + fd = float64_muladd(fj, fk, fa, flag, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +static uint64_t fcmp_common(CPULoongArchState *env, FloatRelation cmp, + uint32_t flags) +{ + bool ret; + + 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(); + } + update_fcsr0(env, GETPC()); + + return ret; +} + +/* fcmp_cXXX_s */ +uint64_t helper_fcmp_c_s(CPULoongArchState *env, uint64_t fj, + uint64_t fk, uint32_t flags) +{ + FloatRelation cmp = float32_compare_quiet((uint32_t)fj, + (uint32_t)fk, &env->fp_status); + return fcmp_common(env, cmp, flags); +} + +/* fcmp_sXXX_s */ +uint64_t helper_fcmp_s_s(CPULoongArchState *env, uint64_t fj, + uint64_t fk, uint32_t flags) +{ + FloatRelation cmp = float32_compare((uint32_t)fj, + (uint32_t)fk, &env->fp_status); + return fcmp_common(env, cmp, flags); +} + +/* fcmp_cXXX_d */ +uint64_t helper_fcmp_c_d(CPULoongArchState *env, uint64_t fj, + uint64_t fk, uint32_t flags) +{ + FloatRelation cmp = float64_compare_quiet(fj, fk, &env->fp_status); + return fcmp_common(env, cmp, flags); +} + +/* fcmp_sXXX_d */ +uint64_t helper_fcmp_s_d(CPULoongArchState *env, uint64_t fj, + uint64_t fk, uint32_t flags) +{ + FloatRelation cmp = float64_compare(fj, fk, &env->fp_status); + return fcmp_common(env, cmp, flags); +} + +/* floating point conversion */ +uint64_t helper_fcvt_s_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = nanbox_s(float64_to_float32(fj, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_fcvt_d_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = float32_to_float64((uint32_t)fj, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ffint_s_w(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = nanbox_s(int32_to_float32((int32_t)fj, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ffint_s_l(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = nanbox_s(int64_to_float32(fj, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ffint_d_w(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = int32_to_float64((int32_t)fj, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ffint_d_l(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = int64_to_float64(fj, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_frint_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = (uint64_t)(float32_round_to_int((uint32_t)fj, &env->fp_status)); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_frint_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + + fd = float64_round_to_int(fj, &env->fp_status); + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrm_l_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_down, &env->fp_status); + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrm_l_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_down, &env->fp_status); + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrm_w_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_down, &env->fp_status); + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrm_w_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_down, &env->fp_status); + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrp_l_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_up, &env->fp_status); + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrp_l_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_up, &env->fp_status); + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrp_w_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_up, &env->fp_status); + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrp_w_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_up, &env->fp_status); + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrz_l_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrz_l_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrz_w_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrz_w_s(CPULoongArchState *env, uint64_t fj) +{ + uint32_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return (uint64_t)fd; +} + +uint64_t helper_ftintrne_l_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrne_l_s(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrne_w_d(CPULoongArchState *env, uint64_t fj) +{ + uint64_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + 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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +uint64_t helper_ftintrne_w_s(CPULoongArchState *env, uint64_t fj) +{ + uint32_t fd; + FloatRoundMode old_mode = get_float_rounding_mode(&env->fp_status); + + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + 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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return (uint64_t)fd; +} + +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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +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)) { + if (float32_is_any_nan((uint32_t)fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +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)) { + if (float64_is_any_nan(fj)) { + fd = 0; + } + } + update_fcsr0(env, GETPC()); + return fd; +} + +void helper_set_rounding_mode(CPULoongArchState *env) +{ + set_float_rounding_mode(ieee_rm[(env->fcsr0 >> FCSR0_RM) & 0x3], + &env->fp_status); +} diff --git a/target/loongarch/tcg/insn_trans/trans_arith.c.inc b/target/loongarch/tcg/insn_trans/trans_arith.c.inc new file mode 100644 index 0000000000..2be057e932 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_arith.c.inc @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static bool gen_rrr(DisasContext *ctx, arg_rrr *a, + DisasExtend src1_ext, DisasExtend src2_ext, + DisasExtend dst_ext, void (*func)(TCGv, TCGv, TCGv)) +{ + TCGv dest = gpr_dst(ctx, a->rd, dst_ext); + TCGv src1 = gpr_src(ctx, a->rj, src1_ext); + TCGv src2 = gpr_src(ctx, a->rk, src2_ext); + + func(dest, src1, src2); + gen_set_gpr(a->rd, dest, dst_ext); + + return true; +} + +static bool gen_rri_v(DisasContext *ctx, arg_rr_i *a, + DisasExtend src_ext, DisasExtend dst_ext, + void (*func)(TCGv, TCGv, TCGv)) +{ + TCGv dest = gpr_dst(ctx, a->rd, dst_ext); + TCGv src1 = gpr_src(ctx, a->rj, src_ext); + TCGv src2 = tcg_constant_tl(a->imm); + + func(dest, src1, src2); + gen_set_gpr(a->rd, dest, dst_ext); + + return true; +} + +static bool gen_rri_c(DisasContext *ctx, arg_rr_i *a, + DisasExtend src_ext, DisasExtend dst_ext, + void (*func)(TCGv, TCGv, target_long)) +{ + TCGv dest = gpr_dst(ctx, a->rd, dst_ext); + TCGv src1 = gpr_src(ctx, a->rj, src_ext); + + func(dest, src1, a->imm); + gen_set_gpr(a->rd, dest, dst_ext); + + return true; +} + +static bool gen_rrr_sa(DisasContext *ctx, arg_rrr_sa *a, + DisasExtend src_ext, DisasExtend dst_ext, + void (*func)(TCGv, TCGv, TCGv, target_long)) +{ + TCGv dest = gpr_dst(ctx, a->rd, dst_ext); + TCGv src1 = gpr_src(ctx, a->rj, src_ext); + TCGv src2 = gpr_src(ctx, a->rk, src_ext); + + func(dest, src1, src2, a->sa); + gen_set_gpr(a->rd, dest, dst_ext); + + return true; +} + +static bool trans_lu12i_w(DisasContext *ctx, arg_lu12i_w *a) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + + tcg_gen_movi_tl(dest, a->imm << 12); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +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 = 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); + + return true; +} + +static void gen_slt(TCGv dest, TCGv src1, TCGv src2) +{ + tcg_gen_setcond_tl(TCG_COND_LT, dest, src1, src2); +} + +static void gen_sltu(TCGv dest, TCGv src1, TCGv src2) +{ + tcg_gen_setcond_tl(TCG_COND_LTU, dest, src1, src2); +} + +static void gen_mulh_w(TCGv dest, TCGv src1, TCGv src2) +{ + tcg_gen_mul_i64(dest, src1, src2); + tcg_gen_sari_i64(dest, dest, 32); +} + +static void gen_mulh_d(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv discard = tcg_temp_new(); + tcg_gen_muls2_tl(discard, dest, src1, src2); +} + +static void gen_mulh_du(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv discard = tcg_temp_new(); + tcg_gen_mulu2_tl(discard, dest, src1, src2); +} + +static void prep_divisor_d(TCGv ret, TCGv src1, TCGv src2) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv zero = tcg_constant_tl(0); + + /* + * If min / -1, set the divisor to 1. + * This avoids potential host overflow trap and produces min. + * If x / 0, set the divisor to 1. + * This avoids potential host overflow trap; + * the required result is undefined. + */ + tcg_gen_setcondi_tl(TCG_COND_EQ, ret, src1, INT64_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t0, src2, -1); + tcg_gen_setcondi_tl(TCG_COND_EQ, t1, src2, 0); + 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); +} + +static void prep_divisor_du(TCGv ret, TCGv src2) +{ + TCGv zero = tcg_constant_tl(0); + TCGv one = tcg_constant_tl(1); + + /* + * If x / 0, set the divisor to 1. + * This avoids potential host overflow trap; + * the required result is undefined. + */ + tcg_gen_movcond_tl(TCG_COND_EQ, ret, src2, zero, one, src2); +} + +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); +} + +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); +} + +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); +} + +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); +} + +static void gen_div_w(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv t0 = tcg_temp_new(); + /* We need not check for integer overflow for div_w. */ + prep_divisor_du(t0, src2); + tcg_gen_div_tl(dest, src1, t0); +} + +static void gen_rem_w(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv t0 = tcg_temp_new(); + /* We need not check for integer overflow for rem_w. */ + prep_divisor_du(t0, src2); + tcg_gen_rem_tl(dest, src1, t0); +} + +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); +} + +static bool trans_lu32i_d(DisasContext *ctx, arg_lu32i_d *a) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + 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); + + return true; +} + +static bool trans_lu52i_d(DisasContext *ctx, arg_lu52i_d *a) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); + 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); + + return true; +} + +static target_ulong gen_pcaddi(target_ulong pc, int imm) +{ + return pc + (imm << 2); +} + +static target_ulong gen_pcalau12i(target_ulong pc, int imm) +{ + return (pc + (imm << 12)) & ~0xfff; +} + +static target_ulong gen_pcaddu12i(target_ulong pc, int imm) +{ + return pc + (imm << 12); +} + +static target_ulong gen_pcaddu18i(target_ulong pc, int imm) +{ + return pc + ((target_ulong)(imm) << 18); +} + +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, 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/tcg/insn_trans/trans_bit.c.inc b/target/loongarch/tcg/insn_trans/trans_bit.c.inc new file mode 100644 index 0000000000..ee5fa003ce --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_bit.c.inc @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static bool gen_rr(DisasContext *ctx, arg_rr *a, + DisasExtend src_ext, DisasExtend dst_ext, + void (*func)(TCGv, TCGv)) +{ + TCGv dest = gpr_dst(ctx, a->rd, dst_ext); + TCGv src1 = gpr_src(ctx, a->rj, src_ext); + + func(dest, src1); + gen_set_gpr(a->rd, dest, dst_ext); + + return true; +} + +static void gen_bytepick_w(TCGv dest, TCGv src1, TCGv src2, target_long sa) +{ + tcg_gen_concat_tl_i64(dest, src1, src2); + tcg_gen_sextract_i64(dest, dest, (32 - sa * 8), 32); +} + +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 bool gen_bstrins(DisasContext *ctx, arg_rr_ms_ls *a, + DisasExtend dst_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; + } + + 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; +} + +static void gen_clz_w(TCGv dest, TCGv src1) +{ + tcg_gen_clzi_tl(dest, src1, TARGET_LONG_BITS); + tcg_gen_subi_tl(dest, dest, TARGET_LONG_BITS - 32); +} + +static void gen_clo_w(TCGv dest, TCGv src1) +{ + tcg_gen_not_tl(dest, src1); + tcg_gen_ext32u_tl(dest, dest); + gen_clz_w(dest, dest); +} + +static void gen_ctz_w(TCGv dest, TCGv src1) +{ + tcg_gen_ori_tl(dest, src1, (target_ulong)MAKE_64BIT_MASK(32, 32)); + tcg_gen_ctzi_tl(dest, dest, TARGET_LONG_BITS); +} + +static void gen_cto_w(TCGv dest, TCGv src1) +{ + tcg_gen_not_tl(dest, src1); + gen_ctz_w(dest, dest); +} + +static void gen_clz_d(TCGv dest, TCGv src1) +{ + tcg_gen_clzi_i64(dest, src1, TARGET_LONG_BITS); +} + +static void gen_clo_d(TCGv dest, TCGv src1) +{ + tcg_gen_not_tl(dest, src1); + gen_clz_d(dest, dest); +} + +static void gen_ctz_d(TCGv dest, TCGv src1) +{ + tcg_gen_ctzi_tl(dest, src1, TARGET_LONG_BITS); +} + +static void gen_cto_d(TCGv dest, TCGv src1) +{ + tcg_gen_not_tl(dest, src1); + gen_ctz_d(dest, dest); +} + +static void gen_revb_2w(TCGv dest, TCGv src1) +{ + tcg_gen_bswap64_i64(dest, src1); + tcg_gen_rotri_i64(dest, dest, 32); +} + +static void gen_revb_2h(TCGv dest, TCGv src1) +{ + TCGv mask = tcg_constant_tl(0x00FF00FF); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_shri_tl(t0, src1, 8); + tcg_gen_and_tl(t0, t0, mask); + tcg_gen_and_tl(t1, src1, mask); + tcg_gen_shli_tl(t1, t1, 8); + tcg_gen_or_tl(dest, t0, t1); +} + +static void gen_revb_4h(TCGv dest, TCGv src1) +{ + TCGv mask = tcg_constant_tl(0x00FF00FF00FF00FFULL); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_shri_tl(t0, src1, 8); + tcg_gen_and_tl(t0, t0, mask); + tcg_gen_and_tl(t1, src1, mask); + tcg_gen_shli_tl(t1, t1, 8); + tcg_gen_or_tl(dest, t0, t1); +} + +static void gen_revh_2w(TCGv dest, TCGv src1) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 mask = tcg_constant_i64(0x0000ffff0000ffffull); + + tcg_gen_shri_i64(t0, src1, 16); + tcg_gen_and_i64(t1, src1, mask); + tcg_gen_and_i64(t0, t0, mask); + tcg_gen_shli_i64(t1, t1, 16); + tcg_gen_or_i64(dest, t1, t0); +} + +static void gen_revh_d(TCGv dest, TCGv src1) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv mask = tcg_constant_tl(0x0000FFFF0000FFFFULL); + + tcg_gen_shri_tl(t1, src1, 16); + tcg_gen_and_tl(t1, t1, mask); + tcg_gen_and_tl(t0, src1, mask); + tcg_gen_shli_tl(t0, t0, 16); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_rotri_tl(dest, t0, 32); +} + +static void gen_maskeqz(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv zero = tcg_constant_tl(0); + + tcg_gen_movcond_tl(TCG_COND_EQ, dest, src2, zero, zero, src1); +} + +static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv zero = tcg_constant_tl(0); + + tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1); +} + +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/tcg/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc new file mode 100644 index 0000000000..221e5159db --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static bool trans_b(DisasContext *ctx, arg_b *a) +{ + gen_goto_tb(ctx, 0, ctx->base.pc_next + a->offs); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +static bool trans_bl(DisasContext *ctx, arg_bl *a) +{ + 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; +} + +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); + + 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; + return true; +} + +static void gen_bc(DisasContext *ctx, TCGv src1, TCGv src2, + target_long offs, TCGCond cond) +{ + TCGLabel *l = gen_new_label(); + tcg_gen_brcond_tl(cond, src1, src2, l); + gen_goto_tb(ctx, 1, ctx->base.pc_next + 4); + gen_set_label(l); + gen_goto_tb(ctx, 0, ctx->base.pc_next + offs); + ctx->base.is_jmp = DISAS_NORETURN; +} + +static bool gen_rr_bc(DisasContext *ctx, arg_rr_offs *a, TCGCond cond) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rd, EXT_NONE); + + gen_bc(ctx, src1, src2, a->offs, cond); + return true; +} + +static bool gen_rz_bc(DisasContext *ctx, arg_r_offs *a, TCGCond cond) +{ + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = tcg_constant_tl(0); + + gen_bc(ctx, src1, src2, a->offs, cond); + return true; +} + +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, tcg_env, + offsetof(CPULoongArchState, cf[a->cj])); + gen_bc(ctx, src1, src2, a->offs, cond); + return true; +} + +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/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc new file mode 100644 index 0000000000..cfa361fecf --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +static bool trans_break(DisasContext *ctx, arg_break *a) +{ + generate_exception(ctx, EXCCODE_BRK); + return true; +} + +static bool trans_syscall(DisasContext *ctx, arg_syscall *a) +{ + generate_exception(ctx, EXCCODE_SYS); + return true; +} + +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); + + if (!avail_64(ctx)) { + return false; + } + + gen_helper_asrtle_d(tcg_env, src1, src2); + return true; +} + +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); + + if (!avail_64(ctx)) { + return false; + } + + gen_helper_asrtgt_d(tcg_env, src1, src2); + return true; +} + +static bool gen_rdtime(DisasContext *ctx, arg_rr *a, + bool word, bool high) +{ + TCGv dst1 = gpr_dst(ctx, a->rd, EXT_NONE); + TCGv dst2 = gpr_dst(ctx, a->rj, EXT_NONE); + + 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, tcg_env, offsetof(CPULoongArchState, CSR_TID)); + + return true; +} + +static bool trans_rdtimel_w(DisasContext *ctx, arg_rdtimel_w *a) +{ + return gen_rdtime(ctx, a, 1, 0); +} + +static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) +{ + return gen_rdtime(ctx, a, 1, 1); +} + +static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) +{ + return gen_rdtime(ctx, a, 0, 0); +} + +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, tcg_env, src1); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +static bool gen_crc(DisasContext *ctx, arg_rrr *a, + void (*func)(TCGv, TCGv, TCGv, TCGv), + TCGv tsz) +{ + TCGv dest = gpr_dst(ctx, a->rd, EXT_SIGN); + TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); + TCGv src2 = gpr_src(ctx, a->rk, EXT_NONE); + + func(dest, src2, src1, tsz); + gen_set_gpr(a->rd, dest, EXT_SIGN); + + return true; +} + +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/tcg/insn_trans/trans_fcmp.c.inc b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc new file mode 100644 index 0000000000..3babf69e4a --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +/* bit0(signaling/quiet) bit1(lt) bit2(eq) bit3(un) bit4(neq) */ +static uint32_t get_fcmp_flags(int cond) +{ + uint32_t flags = 0; + + if (cond & 0x1) { + flags |= FCMP_LT; + } + if (cond & 0x2) { + flags |= FCMP_EQ; + } + if (cond & 0x4) { + flags |= FCMP_UN; + } + if (cond & 0x8) { + flags |= FCMP_GT | FCMP_LT; + } + return flags; +} + +static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) +{ + 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, tcg_env, src1, src2, tcg_constant_i32(flags)); + + 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, 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, tcg_env, src1, src2, tcg_constant_i32(flags)); + + 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/tcg/insn_trans/trans_memory.c.inc b/target/loongarch/tcg/insn_trans/trans_memory.c.inc new file mode 100644 index 0000000000..42f4e74012 --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_memory.c.inc @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +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); + + 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); + return true; +} + +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); + + addr = make_address_i(ctx, addr, a->imm); + + tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); + return true; +} + +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 = make_address_x(ctx, src1, src2); + + tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, mop); + gen_set_gpr(a->rd, dest, EXT_NONE); + + return true; +} + +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 = make_address_x(ctx, src1, src2); + + tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); + + return true; +} + +static bool gen_load_gt(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); + + 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); + + return true; +} + +static bool gen_load_le(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); + + 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); + + return true; +} + +static bool gen_store_gt(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); + + 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; +} + +static bool gen_store_le(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); + + 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; +} + +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); + return true; +} + +static bool trans_ibar(DisasContext *ctx, arg_ibar *a) +{ + ctx->base.is_jmp = DISAS_STOP; + return true; +} + +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); + + 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); + return true; +} + +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); + + addr = make_address_i(ctx, addr, a->imm); + + tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, mop); + return true; +} + +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 83% rename from target/loongarch/insn_trans/trans_privileged.c.inc rename to target/loongarch/tcg/insn_trans/trans_privileged.c.inc index 40f82becb0..7e4ec93edb 100644 --- a/target/loongarch/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -99,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), @@ -185,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; @@ -211,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); @@ -241,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; @@ -280,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; } @@ -307,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; } @@ -320,22 +308,22 @@ 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) { - if (ctx->mem_idx != MMU_IDX_DA) { + if (ctx->mem_idx != MMU_DA_IDX) { tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; } @@ -346,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; } @@ -355,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; } @@ -364,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; } @@ -374,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; } @@ -384,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; } @@ -394,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; } @@ -411,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; @@ -449,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; } @@ -462,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; } @@ -474,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; } @@ -495,7 +491,7 @@ 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; } diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc new file mode 100644 index 0000000000..377307785a --- /dev/null +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +static void gen_rotr_w(TCGv dest, TCGv src1, TCGv src2) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv t0 = tcg_temp_new(); + + tcg_gen_andi_tl(t0, src2, 0x1f); + + tcg_gen_trunc_tl_i32(t1, src1); + tcg_gen_trunc_tl_i32(t2, t0); + + tcg_gen_rotr_i32(t1, t1, t2); + tcg_gen_ext_i32_tl(dest, t1); +} + +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); +} + +static void gen_sari_w(TCGv dest, TCGv src1, target_long imm) +{ + tcg_gen_sextract_tl(dest, src1, imm, 32 - imm); +} + +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_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w) +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/tcg/iocsr_helper.c b/target/loongarch/tcg/iocsr_helper.c new file mode 100644 index 0000000000..b6916f53d2 --- /dev/null +++ b/target/loongarch/tcg/iocsr_helper.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + * + * Helpers for IOCSR reads/writes + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_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, + 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, + 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, + 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, + 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, 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, 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, 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, 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/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c new file mode 100644 index 0000000000..b17208e5b9 --- /dev/null +++ b/target/loongarch/tcg/op_helper.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch emulation helpers for QEMU. + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu/log.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 "internals.h" +#include "qemu/crc32c.h" +#include /* for crc32 */ +#include "cpu-csr.h" + +/* Exceptions helpers */ +void helper_raise_exception(CPULoongArchState *env, uint32_t exception) +{ + do_raise_exception(env, exception, GETPC()); +} + +target_ulong helper_bitrev_w(target_ulong rj) +{ + return (int32_t)revbit32(rj); +} + +target_ulong helper_bitrev_d(target_ulong rj) +{ + return revbit64(rj); +} + +target_ulong helper_bitswap(target_ulong v) +{ + v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | + ((v & (target_ulong)0x5555555555555555ULL) << 1); + v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | + ((v & (target_ulong)0x3333333333333333ULL) << 2); + v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | + ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); + return v; +} + +/* loongarch assert op */ +void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) +{ + if (rj > rk) { + 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) { + env->CSR_BADV = rj; + do_raise_exception(env, EXCCODE_BCE, GETPC()); + } +} + +target_ulong helper_crc32(target_ulong val, target_ulong m, uint64_t sz) +{ + uint8_t buf[8]; + target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); + + m &= mask; + stq_le_p(buf, m); + return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); +} + +target_ulong helper_crc32c(target_ulong val, target_ulong m, uint64_t sz) +{ + uint8_t buf[8]; + target_ulong mask = ((sz * 8) == 64) ? -1ULL : ((1ULL << (sz * 8)) - 1); + m &= mask; + stq_le_p(buf, m); + return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); +} + +target_ulong helper_cpucfg(CPULoongArchState *env, target_ulong 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); + + plv = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV); + if (extract64(env->CSR_MISC, R_CSR_MISC_DRDTL_SHIFT + plv, 1)) { + do_raise_exception(env, EXCCODE_IPE, GETPC()); + } + + return cpu_loongarch_get_constant_timer_counter(cpu); +#endif +} + +#ifndef CONFIG_USER_ONLY +void helper_ertn(CPULoongArchState *env) +{ + uint64_t csr_pplv, csr_pie; + if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { + csr_pplv = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV); + csr_pie = FIELD_EX64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE); + + 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); + 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); + + set_pc(env, env->CSR_ERA); + qemu_log_mask(CPU_LOG_INT, "%s: ERA " TARGET_FMT_lx "\n", + __func__, env->CSR_ERA); + } + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, csr_pplv); + env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, csr_pie); + + env->lladdr = 1; +} + +void helper_idle(CPULoongArchState *env) +{ + CPUState *cs = env_cpu(env); + + cs->halted = 1; + do_raise_exception(env, EXCP_HLT, 0); +} +#endif diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c new file mode 100644 index 0000000000..97f38fc391 --- /dev/null +++ b/target/loongarch/tcg/tlb_helper.c @@ -0,0 +1,619 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch TLB helpers + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + * + */ + +#include "qemu/osdep.h" +#include "qemu/guest-random.h" + +#include "cpu.h" +#include "internals.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/page-protection.h" +#include "exec/cpu_ldst.h" +#include "exec/log.h" +#include "cpu-csr.h" + +static void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level) +{ + 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; + } +} + +static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, + MMUAccessType access_type, int tlb_error) +{ + CPUState *cs = env_cpu(env); + + switch (tlb_error) { + default: + case TLBRET_BADADDR: + cs->exception_index = access_type == MMU_INST_FETCH + ? EXCCODE_ADEF : EXCCODE_ADEM; + break; + case TLBRET_NOMATCH: + /* No TLB match for a mapped address */ + if (access_type == MMU_DATA_LOAD) { + cs->exception_index = EXCCODE_PIL; + } else if (access_type == MMU_DATA_STORE) { + cs->exception_index = EXCCODE_PIS; + } else if (access_type == MMU_INST_FETCH) { + cs->exception_index = EXCCODE_PIF; + } + env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1); + break; + case TLBRET_INVALID: + /* TLB match with no valid bit */ + if (access_type == MMU_DATA_LOAD) { + cs->exception_index = EXCCODE_PIL; + } else if (access_type == MMU_DATA_STORE) { + cs->exception_index = EXCCODE_PIS; + } else if (access_type == MMU_INST_FETCH) { + cs->exception_index = EXCCODE_PIF; + } + break; + case TLBRET_DIRTY: + /* TLB match but 'D' bit is cleared */ + cs->exception_index = EXCCODE_PME; + break; + case TLBRET_XI: + /* Execute-Inhibit Exception */ + cs->exception_index = EXCCODE_PNX; + break; + case TLBRET_RI: + /* Read-Inhibit Exception */ + cs->exception_index = EXCCODE_PNR; + break; + case TLBRET_PE: + /* Privileged Exception */ + cs->exception_index = EXCCODE_PPI; + break; + } + + if (tlb_error == TLBRET_NOMATCH) { + env->CSR_TLBRBADV = address; + 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; + } + env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1); + } +} + +static void invalidate_tlb_entry(CPULoongArchState *env, int index) +{ + target_ulong addr, mask, pagesize; + uint8_t tlb_ps; + LoongArchTLB *tlb = &env->tlb[index]; + + 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); + + 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); + } + pagesize = MAKE_64BIT_MASK(tlb_ps, 1); + mask = MAKE_64BIT_MASK(0, tlb_ps + 1); + + if (tlb_v0) { + addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ + tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, + mmu_idx, TARGET_LONG_BITS); + } + + if (tlb_v1) { + addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ + tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, + mmu_idx, TARGET_LONG_BITS); + } +} + +static void invalidate_tlb(CPULoongArchState *env, int index) +{ + LoongArchTLB *tlb; + uint16_t csr_asid, tlb_asid, tlb_g; + + csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); + tlb = &env->tlb[index]; + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + if (tlb_g == 0 && tlb_asid != csr_asid) { + return; + } + invalidate_tlb_entry(env, index); +} + +static void fill_tlb_entry(CPULoongArchState *env, int index) +{ + LoongArchTLB *tlb = &env->tlb[index]; + uint64_t lo0, lo1, csr_vppn; + uint16_t csr_asid; + uint8_t csr_ps; + + if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { + csr_ps = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); + 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); + 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; + } + + if (csr_ps == 0) { + qemu_log_mask(CPU_LOG_MMU, "page size is 0\n"); + } + + /* Only MTLB has the ps fields */ + if (index >= LOONGARCH_STLB) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); + } + + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); + csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid); + + tlb->tlb_entry0 = lo0; + tlb->tlb_entry1 = lo1; +} + +/* Return an random value between low and high */ +static uint32_t get_random_tlb(uint32_t low, uint32_t high) +{ + uint32_t val; + + qemu_guest_getrandom_nofail(&val, sizeof(val)); + return val % (high - low + 1) + low; +} + +void helper_tlbsrch(CPULoongArchState *env) +{ + int index, match; + + if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { + match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index); + } else { + match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index); + } + + if (match) { + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index); + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); + return; + } + + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); +} + +void helper_tlbrd(CPULoongArchState *env) +{ + LoongArchTLB *tlb; + int index; + uint8_t tlb_ps, tlb_e; + + index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + tlb = &env->tlb[index]; + + 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); + } + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + + if (!tlb_e) { + /* Invalid TLB entry */ + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); + env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0); + env->CSR_TLBEHI = 0; + env->CSR_TLBELO0 = 0; + env->CSR_TLBELO1 = 0; + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0); + } else { + /* Valid TLB entry */ + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); + env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, + PS, (tlb_ps & 0x3f)); + env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << + R_TLB_MISC_VPPN_SHIFT; + env->CSR_TLBELO0 = tlb->tlb_entry0; + env->CSR_TLBELO1 = tlb->tlb_entry1; + } +} + +void helper_tlbwr(CPULoongArchState *env) +{ + int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + + invalidate_tlb(env, index); + + if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { + env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, + TLB_MISC, E, 0); + return; + } + + fill_tlb_entry(env, index); +} + +void helper_tlbfill(CPULoongArchState *env) +{ + uint64_t address, entryhi; + int index, set, stlb_idx; + uint16_t pagesize, stlb_ps; + + if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { + entryhi = env->CSR_TLBREHI; + pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); + } else { + entryhi = env->CSR_TLBEHI; + pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); + } + + stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + + if (pagesize == stlb_ps) { + /* Only write into STLB bits [47:13] */ + address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); + + /* Choose one set ramdomly */ + set = get_random_tlb(0, 7); + + /* Index in one set */ + stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */ + + index = set * 256 + stlb_idx; + } else { + /* Only write into MTLB */ + index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); + } + + invalidate_tlb(env, index); + fill_tlb_entry(env, index); +} + +void helper_tlbclr(CPULoongArchState *env) +{ + LoongArchTLB *tlb; + int i, index; + uint16_t csr_asid, tlb_asid, tlb_g; + + csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); + index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + + if (index < LOONGARCH_STLB) { + /* STLB. One line per operation */ + for (i = 0; i < 8; i++) { + tlb = &env->tlb[i * 256 + (index % 256)]; + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + if (!tlb_g && tlb_asid == csr_asid) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + } + } + } else if (index < LOONGARCH_TLB_MAX) { + /* All MTLB entries */ + for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { + tlb = &env->tlb[i]; + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + if (!tlb_g && tlb_asid == csr_asid) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + } + } + } + + tlb_flush(env_cpu(env)); +} + +void helper_tlbflush(CPULoongArchState *env) +{ + int i, index; + + index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); + + if (index < LOONGARCH_STLB) { + /* STLB. One line per operation */ + for (i = 0; i < 8; i++) { + int s_idx = i * 256 + (index % 256); + env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc, + TLB_MISC, E, 0); + } + } else if (index < LOONGARCH_TLB_MAX) { + /* All MTLB entries */ + for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { + env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, + TLB_MISC, E, 0); + } + } + + tlb_flush(env_cpu(env)); +} + +void helper_invtlb_all(CPULoongArchState *env) +{ + for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { + env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, + TLB_MISC, E, 0); + } + tlb_flush(env_cpu(env)); +} + +void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g) +{ + for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { + LoongArchTLB *tlb = &env->tlb[i]; + uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + + if (tlb_g == g) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + } + } + tlb_flush(env_cpu(env)); +} + +void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info) +{ + uint16_t asid = info & R_CSR_ASID_ASID_MASK; + + for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { + LoongArchTLB *tlb = &env->tlb[i]; + uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + + if (!tlb_g && (tlb_asid == asid)) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + } + } + tlb_flush(env_cpu(env)); +} + +void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, + target_ulong addr) +{ + uint16_t asid = info & 0x3ff; + + for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { + LoongArchTLB *tlb = &env->tlb[i]; + uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + uint64_t vpn, tlb_vppn; + uint8_t tlb_ps, compare_shift; + + if (i >= LOONGARCH_STLB) { + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + } else { + tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + } + tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); + compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + + if (!tlb_g && (tlb_asid == asid) && + (vpn == (tlb_vppn >> compare_shift))) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + } + } + tlb_flush(env_cpu(env)); +} + +void helper_invtlb_page_asid_or_g(CPULoongArchState *env, + target_ulong info, target_ulong addr) +{ + uint16_t asid = info & 0x3ff; + + for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { + LoongArchTLB *tlb = &env->tlb[i]; + uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + uint64_t vpn, tlb_vppn; + uint8_t tlb_ps, compare_shift; + + if (i >= LOONGARCH_STLB) { + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + } else { + tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + } + tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); + compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + + if ((tlb_g || (tlb_asid == asid)) && + (vpn == (tlb_vppn >> compare_shift))) { + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); + } + } + tlb_flush(env_cpu(env)); +} + +bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + CPULoongArchState *env = cpu_env(cs); + hwaddr physical; + int prot; + int ret; + + /* Data access */ + ret = get_physical_address(env, &physical, &prot, address, + access_type, mmu_idx); + + if (ret == TLBRET_MATCH) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " physical " HWADDR_FMT_plx + " prot %d\n", __func__, address, physical, prot); + return true; + } else { + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, + ret); + } + if (probe) { + return false; + } + raise_mmu_exception(env, address, access_type, ret); + cpu_loop_exit_restore(cs, retaddr); +} + +target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, + target_ulong level, uint32_t mem_idx) +{ + CPUState *cs = env_cpu(env); + target_ulong badvaddr, index, phys, ret; + int shift; + uint64_t dir_base, dir_width; + + 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"); + return base; + } + + 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; + + /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ + shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); + shift = (shift + 1) * 3; + + 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; + return ret; +} + +void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, + uint32_t mem_idx) +{ + CPUState *cs = env_cpu(env); + target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; + int shift; + 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)); + + 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 += MAKE_64BIT_MASK(ps, 1); + } + } else { + /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ + shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); + shift = (shift + 1) * 3; + badv = env->CSR_TLBRBADV; + + ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); + ptindex = ptindex & ~0x1; /* clear bit 0 */ + ptoffset0 = ptindex << shift; + ptoffset1 = (ptindex + 1) << shift; + + phys = base | (odd ? ptoffset1 : ptoffset0); + tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; + ps = ptbase; + } + + if (odd) { + env->CSR_TLBRELO1 = tmp0; + } else { + env->CSR_TLBRELO0 = tmp0; + } + env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); +} diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c new file mode 100644 index 0000000000..1fca4afc73 --- /dev/null +++ b/target/loongarch/tcg/translate.c @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch emulation for QEMU - main translation routines. + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#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/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; + +#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. + */ +static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) +{ + tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); +} + +void generate_exception(DisasContext *ctx, int excp) +{ + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + 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); + tcg_gen_exit_tb(ctx->base.tb, n); + } else { + tcg_gen_movi_tl(cpu_pc, dest); + tcg_gen_lookup_and_goto_ptr(); + } +} + +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->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); + + 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) +{ +} + +static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next); +} + +/* + * Wrappers for getting reg values. + * + * The $zero register does not have cpu_gpr[0] allocated -- we supply the + * constant zero as a source, and an uninitialized sink as destination. + * + * Further, we may provide an extension for word operations. + */ +static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) +{ + TCGv t; + + if (reg_num == 0) { + return ctx->zero; + } + + switch (src_ext) { + case EXT_NONE: + return cpu_gpr[reg_num]; + case EXT_SIGN: + t = tcg_temp_new(); + tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); + return t; + case EXT_ZERO: + t = tcg_temp_new(); + tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); + return t; + } + g_assert_not_reached(); +} + +static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) +{ + if (reg_num == 0 || dst_ext) { + return tcg_temp_new(); + } + return cpu_gpr[reg_num]; +} + +static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) +{ + if (reg_num != 0) { + switch (dst_ext) { + case EXT_NONE: + tcg_gen_mov_tl(cpu_gpr[reg_num], t); + break; + case EXT_SIGN: + tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); + break; + case EXT_ZERO: + tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); + break; + default: + g_assert_not_reached(); + } + } +} + +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" +#include "insn_trans/trans_bit.c.inc" +#include "insn_trans/trans_memory.c.inc" +#include "insn_trans/trans_atomic.c.inc" +#include "insn_trans/trans_extra.c.inc" +#include "insn_trans/trans_farith.c.inc" +#include "insn_trans/trans_fcmp.c.inc" +#include "insn_trans/trans_fcnv.c.inc" +#include "insn_trans/trans_fmov.c.inc" +#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) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + 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. " + TARGET_FMT_lx ": 0x%x\n", + ctx->base.pc_next, ctx->opcode); + generate_exception(ctx, EXCCODE_INE); + } + + 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) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + switch (ctx->base.is_jmp) { + case DISAS_STOP: + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + tcg_gen_lookup_and_goto_ptr(); + break; + case DISAS_TOO_MANY: + gen_goto_tb(ctx, 0, ctx->base.pc_next); + break; + case DISAS_NORETURN: + break; + case DISAS_EXIT_UPDATE: + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + QEMU_FALLTHROUGH; + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + default: + g_assert_not_reached(); + } +} + +static const TranslatorOps loongarch_tr_ops = { + .init_disas_context = loongarch_tr_init_disas_context, + .tb_start = loongarch_tr_tb_start, + .insn_start = loongarch_tr_insn_start, + .translate_insn = loongarch_tr_translate_insn, + .tb_stop = loongarch_tr_tb_stop, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) +{ + DisasContext ctx; + + translator_loop(cs, tb, max_insns, pc, host_pc, + &loongarch_tr_ops, &ctx.base); +} + +void loongarch_translate_init(void) +{ + int i; + + cpu_gpr[0] = NULL; + for (i = 1; i < 32; i++) { + cpu_gpr[i] = tcg_global_mem_new(tcg_env, + offsetof(CPULoongArchState, gpr[i]), + regnames[i]); + } + + 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(tcg_env, + offsetof(CPULoongArchState, llval), "llval"); +} 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/tlb_helper.c b/target/loongarch/tlb_helper.c deleted file mode 100644 index c6d1de50fe..0000000000 --- a/target/loongarch/tlb_helper.c +++ /dev/null @@ -1,764 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * QEMU LoongArch TLB helpers - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - * - */ - -#include "qemu/osdep.h" -#include "qemu/guest-random.h" - -#include "cpu.h" -#include "internals.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#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) -{ - 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); - 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_IDX_USER; - int kernel_mode = mmu_idx == MMU_IDX_KERNEL; - 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, - MMUAccessType access_type, int tlb_error) -{ - CPUState *cs = env_cpu(env); - - switch (tlb_error) { - default: - case TLBRET_BADADDR: - cs->exception_index = access_type == MMU_INST_FETCH - ? EXCCODE_ADEF : EXCCODE_ADEM; - break; - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (access_type == MMU_DATA_LOAD) { - cs->exception_index = EXCCODE_PIL; - } else if (access_type == MMU_DATA_STORE) { - cs->exception_index = EXCCODE_PIS; - } else if (access_type == MMU_INST_FETCH) { - cs->exception_index = EXCCODE_PIF; - } - env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 1); - break; - case TLBRET_INVALID: - /* TLB match with no valid bit */ - if (access_type == MMU_DATA_LOAD) { - cs->exception_index = EXCCODE_PIL; - } else if (access_type == MMU_DATA_STORE) { - cs->exception_index = EXCCODE_PIS; - } else if (access_type == MMU_INST_FETCH) { - cs->exception_index = EXCCODE_PIF; - } - break; - case TLBRET_DIRTY: - /* TLB match but 'D' bit is cleared */ - cs->exception_index = EXCCODE_PME; - break; - case TLBRET_XI: - /* Execute-Inhibit Exception */ - cs->exception_index = EXCCODE_PNX; - break; - case TLBRET_RI: - /* Read-Inhibit Exception */ - cs->exception_index = EXCCODE_PNR; - break; - case TLBRET_PE: - /* Privileged Exception */ - cs->exception_index = EXCCODE_PPI; - break; - } - - if (tlb_error == TLBRET_NOMATCH) { - env->CSR_TLBRBADV = address; - env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, VPPN, - extract64(address, 13, 35)); - } else { - if (!FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { - env->CSR_BADV = address; - } - env->CSR_TLBEHI = address & (TARGET_PAGE_MASK << 1); - } -} - -static void invalidate_tlb_entry(CPULoongArchState *env, int index) -{ - target_ulong addr, mask, pagesize; - uint8_t tlb_ps; - LoongArchTLB *tlb = &env->tlb[index]; - - int mmu_idx = cpu_mmu_index(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); - - 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); - } - pagesize = MAKE_64BIT_MASK(tlb_ps, 1); - mask = MAKE_64BIT_MASK(0, tlb_ps + 1); - - if (tlb_v0) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ - tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, - mmu_idx, TARGET_LONG_BITS); - } - - if (tlb_v1) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ - tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, - mmu_idx, TARGET_LONG_BITS); - } -} - -static void invalidate_tlb(CPULoongArchState *env, int index) -{ - LoongArchTLB *tlb; - uint16_t csr_asid, tlb_asid, tlb_g; - - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - tlb = &env->tlb[index]; - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - if (tlb_g == 0 && tlb_asid != csr_asid) { - return; - } - invalidate_tlb_entry(env, index); -} - -static void fill_tlb_entry(CPULoongArchState *env, int index) -{ - LoongArchTLB *tlb = &env->tlb[index]; - uint64_t lo0, lo1, csr_vppn; - uint16_t csr_asid; - uint8_t csr_ps; - - 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); - 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); - lo0 = env->CSR_TLBELO0; - lo1 = env->CSR_TLBELO1; - } - - if (csr_ps == 0) { - qemu_log_mask(CPU_LOG_MMU, "page size is 0\n"); - } - - /* Only MTLB has the ps fields */ - if (index >= LOONGARCH_STLB) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); - } - - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, ASID, csr_asid); - - tlb->tlb_entry0 = lo0; - tlb->tlb_entry1 = lo1; -} - -/* Return an random value between low and high */ -static uint32_t get_random_tlb(uint32_t low, uint32_t high) -{ - uint32_t val; - - qemu_guest_getrandom_nofail(&val, sizeof(val)); - return val % (high - low + 1) + low; -} - -void helper_tlbsrch(CPULoongArchState *env) -{ - int index, match; - - if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { - match = loongarch_tlb_search(env, env->CSR_TLBREHI, &index); - } else { - match = loongarch_tlb_search(env, env->CSR_TLBEHI, &index); - } - - if (match) { - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX, index); - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); - return; - } - - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); -} - -void helper_tlbrd(CPULoongArchState *env) -{ - LoongArchTLB *tlb; - int index; - uint8_t tlb_ps, tlb_e; - - index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); - tlb = &env->tlb[index]; - - 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); - } - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - - if (!tlb_e) { - /* Invalid TLB entry */ - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 1); - env->CSR_ASID = FIELD_DP64(env->CSR_ASID, CSR_ASID, ASID, 0); - env->CSR_TLBEHI = 0; - env->CSR_TLBELO0 = 0; - env->CSR_TLBELO1 = 0; - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, PS, 0); - } else { - /* Valid TLB entry */ - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, NE, 0); - env->CSR_TLBIDX = FIELD_DP64(env->CSR_TLBIDX, CSR_TLBIDX, - PS, (tlb_ps & 0x3f)); - env->CSR_TLBEHI = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN) << - R_TLB_MISC_VPPN_SHIFT; - env->CSR_TLBELO0 = tlb->tlb_entry0; - env->CSR_TLBELO1 = tlb->tlb_entry1; - } -} - -void helper_tlbwr(CPULoongArchState *env) -{ - int index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); - - invalidate_tlb(env, index); - - if (FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, NE)) { - env->tlb[index].tlb_misc = FIELD_DP64(env->tlb[index].tlb_misc, - TLB_MISC, E, 0); - return; - } - - fill_tlb_entry(env, index); -} - -void helper_tlbfill(CPULoongArchState *env) -{ - uint64_t address, entryhi; - int index, set, stlb_idx; - uint16_t pagesize, stlb_ps; - - if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { - entryhi = env->CSR_TLBREHI; - pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); - } else { - entryhi = env->CSR_TLBEHI; - pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); - } - - stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - - if (pagesize == stlb_ps) { - /* Only write into STLB bits [47:13] */ - address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_VPPN_SHIFT); - - /* Choose one set ramdomly */ - set = get_random_tlb(0, 7); - - /* Index in one set */ - stlb_idx = (address >> (stlb_ps + 1)) & 0xff; /* [0,255] */ - - index = set * 256 + stlb_idx; - } else { - /* Only write into MTLB */ - index = get_random_tlb(LOONGARCH_STLB, LOONGARCH_TLB_MAX - 1); - } - - invalidate_tlb(env, index); - fill_tlb_entry(env, index); -} - -void helper_tlbclr(CPULoongArchState *env) -{ - LoongArchTLB *tlb; - int i, index; - uint16_t csr_asid, tlb_asid, tlb_g; - - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); - - if (index < LOONGARCH_STLB) { - /* STLB. One line per operation */ - for (i = 0; i < 8; i++) { - tlb = &env->tlb[i * 256 + (index % 256)]; - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - if (!tlb_g && tlb_asid == csr_asid) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } - } - } else if (index < LOONGARCH_TLB_MAX) { - /* All MTLB entries */ - for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { - tlb = &env->tlb[i]; - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - if (!tlb_g && tlb_asid == csr_asid) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } - } - } - - tlb_flush(env_cpu(env)); -} - -void helper_tlbflush(CPULoongArchState *env) -{ - int i, index; - - index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); - - if (index < LOONGARCH_STLB) { - /* STLB. One line per operation */ - for (i = 0; i < 8; i++) { - int s_idx = i * 256 + (index % 256); - env->tlb[s_idx].tlb_misc = FIELD_DP64(env->tlb[s_idx].tlb_misc, - TLB_MISC, E, 0); - } - } else if (index < LOONGARCH_TLB_MAX) { - /* All MTLB entries */ - for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; i++) { - env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, - TLB_MISC, E, 0); - } - } - - tlb_flush(env_cpu(env)); -} - -void helper_invtlb_all(CPULoongArchState *env) -{ - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - env->tlb[i].tlb_misc = FIELD_DP64(env->tlb[i].tlb_misc, - TLB_MISC, E, 0); - } - tlb_flush(env_cpu(env)); -} - -void helper_invtlb_all_g(CPULoongArchState *env, uint32_t g) -{ - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - LoongArchTLB *tlb = &env->tlb[i]; - uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - - if (tlb_g == g) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } - } - tlb_flush(env_cpu(env)); -} - -void helper_invtlb_all_asid(CPULoongArchState *env, target_ulong info) -{ - uint16_t asid = info & R_CSR_ASID_ASID_MASK; - - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - LoongArchTLB *tlb = &env->tlb[i]; - uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - - if (!tlb_g && (tlb_asid == asid)) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } - } - tlb_flush(env_cpu(env)); -} - -void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, - target_ulong addr) -{ - uint16_t asid = info & 0x3ff; - - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - LoongArchTLB *tlb = &env->tlb[i]; - uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - uint64_t vpn, tlb_vppn; - uint8_t tlb_ps, compare_shift; - - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - - if (!tlb_g && (tlb_asid == asid) && - (vpn == (tlb_vppn >> compare_shift))) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } - } - tlb_flush(env_cpu(env)); -} - -void helper_invtlb_page_asid_or_g(CPULoongArchState *env, - target_ulong info, target_ulong addr) -{ - uint16_t asid = info & 0x3ff; - - for (int i = 0; i < LOONGARCH_TLB_MAX; i++) { - LoongArchTLB *tlb = &env->tlb[i]; - uint8_t tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - uint16_t tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - uint64_t vpn, tlb_vppn; - uint8_t tlb_ps, compare_shift; - - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - - if ((tlb_g || (tlb_asid == asid)) && - (vpn == (tlb_vppn >> compare_shift))) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 0); - } - } - tlb_flush(env_cpu(env)); -} - -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; - hwaddr physical; - int prot; - int ret; - - /* Data access */ - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx); - - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " physical " TARGET_FMT_plx - " prot %d\n", __func__, address, physical, prot); - return true; - } else { - qemu_log_mask(CPU_LOG_MMU, - "%s address=%" VADDR_PRIx " ret %d\n", __func__, address, - ret); - } - if (probe) { - return false; - } - raise_mmu_exception(env, address, access_type, ret); - cpu_loop_exit_restore(cs, retaddr); -} - -target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, - target_ulong level, uint32_t mem_idx) -{ - CPUState *cs = env_cpu(env); - target_ulong badvaddr, index, phys, ret; - int shift; - uint64_t dir_base, dir_width; - bool huge = (base >> LOONGARCH_PAGE_HUGE_SHIFT) & 0x1; - - badvaddr = env->CSR_TLBRBADV; - base = base & TARGET_PHYS_MASK; - - /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ - 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; - } - index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); - phys = base | index << shift; - ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; - return ret; -} - -void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, - uint32_t mem_idx) -{ - 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); - - base = base & TARGET_PHYS_MASK; - - 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; - if (odd) { - tmp0 += MAKE_64BIT_MASK(ps, 1); - } - } else { - /* 0:64bit, 1:128bit, 2:192bit, 3:256bit */ - shift = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTEWIDTH); - shift = (shift + 1) * 3; - badv = env->CSR_TLBRBADV; - - ptindex = (badv >> ptbase) & ((1 << ptwidth) - 1); - ptindex = ptindex & ~0x1; /* clear bit 0 */ - ptoffset0 = ptindex << shift; - ptoffset1 = (ptindex + 1) << shift; - - phys = base | (odd ? ptoffset1 : ptoffset0); - tmp0 = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; - ps = ptbase; - } - - if (odd) { - env->CSR_TLBRELO1 = tmp0; - } else { - env->CSR_TLBRELO0 = tmp0; - } - env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); -} 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.c b/target/loongarch/translate.c deleted file mode 100644 index 38ced69803..0000000000 --- a/target/loongarch/translate.c +++ /dev/null @@ -1,279 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * LoongArch emulation for QEMU - main translation routines. - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "tcg/tcg-op.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" - -/* Global register indices */ -TCGv cpu_gpr[32], cpu_pc; -static TCGv cpu_lladdr, cpu_llval; -TCGv_i64 cpu_fpr[32]; - -#include "exec/gen-icount.h" - -#define DISAS_STOP DISAS_TARGET_0 -#define DISAS_EXIT DISAS_TARGET_1 -#define DISAS_EXIT_UPDATE DISAS_TARGET_2 - -static inline int plus_1(DisasContext *ctx, int x) -{ - return x + 1; -} - -static inline int shl_2(DisasContext *ctx, int x) -{ - return x << 2; -} - -/* - * 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. - */ -static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) -{ - tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); -} - -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)); - ctx->base.is_jmp = DISAS_NORETURN; -} - -static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) -{ - if (translator_use_goto_tb(&ctx->base, dest)) { - tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb(ctx->base.tb, n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } -} - -static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs) -{ - int64_t bound; - DisasContext *ctx = container_of(dcbase, DisasContext, base); - - ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; - 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_IDX_DA; - } - - /* 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)); - - ctx->zero = tcg_constant_tl(0); -} - -static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) -{ -} - -static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *ctx = container_of(dcbase, DisasContext, base); - - tcg_gen_insn_start(ctx->base.pc_next); -} - -/* - * Wrappers for getting reg values. - * - * The $zero register does not have cpu_gpr[0] allocated -- we supply the - * constant zero as a source, and an uninitialized sink as destination. - * - * 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; - - if (reg_num == 0) { - return ctx->zero; - } - - switch (src_ext) { - case EXT_NONE: - return cpu_gpr[reg_num]; - case EXT_SIGN: - t = temp_new(ctx); - tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); - return t; - case EXT_ZERO: - t = temp_new(ctx); - tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); - return t; - } - g_assert_not_reached(); -} - -static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) -{ - if (reg_num == 0 || dst_ext) { - return temp_new(ctx); - } - return cpu_gpr[reg_num]; -} - -static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) -{ - if (reg_num != 0) { - switch (dst_ext) { - case EXT_NONE: - tcg_gen_mov_tl(cpu_gpr[reg_num], t); - break; - case EXT_SIGN: - tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); - break; - case EXT_ZERO: - tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); - break; - default: - g_assert_not_reached(); - } - } -} - -#include "decode-insns.c.inc" -#include "insn_trans/trans_arith.c.inc" -#include "insn_trans/trans_shift.c.inc" -#include "insn_trans/trans_bit.c.inc" -#include "insn_trans/trans_memory.c.inc" -#include "insn_trans/trans_atomic.c.inc" -#include "insn_trans/trans_extra.c.inc" -#include "insn_trans/trans_farith.c.inc" -#include "insn_trans/trans_fcmp.c.inc" -#include "insn_trans/trans_fcnv.c.inc" -#include "insn_trans/trans_fmov.c.inc" -#include "insn_trans/trans_fmemory.c.inc" -#include "insn_trans/trans_branch.c.inc" -#include "insn_trans/trans_privileged.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); - - if (!decode(ctx, ctx->opcode)) { - qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " - TARGET_FMT_lx ": 0x%x\n", - ctx->base.pc_next, ctx->opcode); - 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; -} - -static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) -{ - DisasContext *ctx = container_of(dcbase, DisasContext, base); - - switch (ctx->base.is_jmp) { - case DISAS_STOP: - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - tcg_gen_lookup_and_goto_ptr(); - break; - case DISAS_TOO_MANY: - gen_goto_tb(ctx, 0, ctx->base.pc_next); - break; - case DISAS_NORETURN: - break; - case DISAS_EXIT_UPDATE: - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - QEMU_FALLTHROUGH; - case DISAS_EXIT: - tcg_gen_exit_tb(NULL, 0); - break; - default: - g_assert_not_reached(); - } -} - -static void loongarch_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - -static const TranslatorOps loongarch_tr_ops = { - .init_disas_context = loongarch_tr_init_disas_context, - .tb_start = loongarch_tr_tb_start, - .insn_start = loongarch_tr_insn_start, - .translate_insn = loongarch_tr_translate_insn, - .tb_stop = loongarch_tr_tb_stop, - .disas_log = loongarch_tr_disas_log, -}; - -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) -{ - DisasContext ctx; - - translator_loop(cs, tb, max_insns, pc, host_pc, - &loongarch_tr_ops, &ctx.base); -} - -void loongarch_translate_init(void) -{ - int i; - - cpu_gpr[0] = NULL; - for (i = 1; i < 32; i++) { - cpu_gpr[i] = tcg_global_mem_new(cpu_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_lladdr = tcg_global_mem_new(cpu_env, - offsetof(CPULoongArchState, lladdr), "lladdr"); - cpu_llval = tcg_global_mem_new(cpu_env, - offsetof(CPULoongArchState, llval), "llval"); -} diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h index 6d2e382e8b..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, @@ -31,10 +43,12 @@ typedef struct DisasContext { uint32_t opcode; 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..23aae24ebe 100644 --- a/target/m68k/Kconfig +++ b/target/m68k/Kconfig @@ -1,2 +1,3 @@ config M68K bool + imply SEMIHOSTING if TCG diff --git a/target/m68k/cpu-param.h b/target/m68k/cpu-param.h index 44a8d193f0..5bbe623ba7 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -2,7 +2,7 @@ * m68k cpu parameters for qemu. * * Copyright (c) 2005-2007 CodeSourcery - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef 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 b67ddea2ae..5fe335558a 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -56,6 +56,11 @@ 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 |= BIT_ULL(feature); @@ -66,27 +71,44 @@ static void m68k_unset_feature(CPUM68KState *env, int feature) env->features &= ~BIT_ULL(feature); } -static void m68k_cpu_reset(DeviceState *dev) +static void m68k_cpu_reset_hold(Object *obj, ResetType type) { - 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, type); + } 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; } cpu_m68k_set_fpcr(env, 0); + /* + * M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL + * 3.4 FLOATING-POINT INSTRUCTION DETAILS + * If either operand, but not both operands, of an operation is a + * nonsignaling NaN, then that NaN is returned as the result. If both + * operands are nonsignaling NaNs, then the destination operand + * nonsignaling NaN is returned as the result. + * If either operand to an operation is a signaling NaN (SNaN), then the + * SNaN bit is set in the FPSR EXC byte. If the SNaN exception enable bit + * is set in the FPCR ENABLE byte, then the exception is taken and the + * destination is not modified. If the SNaN exception enable bit is not + * set, setting the SNaN bit in the operand to a one converts the SNaN to + * a nonsignaling NaN. The operation then continues as described in the + * preceding paragraph for nonsignaling NaNs. + */ + set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); env->fpsr = 0; /* TODO: We should set PC from the interrupt vector. */ @@ -109,17 +131,13 @@ 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); @@ -128,8 +146,7 @@ static void m5206_cpu_initfn(Object *obj) /* 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_M68K); m68k_set_feature(env, M68K_FEATURE_USP); @@ -138,12 +155,12 @@ static void m68000_cpu_initfn(Object *obj) } /* - * 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); @@ -151,6 +168,7 @@ static void m68010_cpu_initfn(Object *obj) 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); } /* @@ -162,8 +180,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); @@ -193,8 +210,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); @@ -220,8 +236,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); @@ -241,8 +256,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); @@ -255,8 +269,7 @@ 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); @@ -268,8 +281,7 @@ static void m5208_cpu_initfn(Object *obj) 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); @@ -282,8 +294,7 @@ static void cfv4e_cpu_initfn(Object *obj) 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); @@ -325,14 +336,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; @@ -387,7 +391,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() @@ -396,18 +400,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; } @@ -416,8 +427,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), @@ -438,7 +450,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), @@ -460,7 +472,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), @@ -485,7 +497,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), @@ -498,7 +510,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), @@ -515,7 +527,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, @@ -523,25 +535,24 @@ 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, .cpu_exec_interrupt = m68k_cpu_exec_interrupt, + .cpu_exec_halt = m68k_cpu_has_work, .do_interrupt = m68k_cpu_do_interrupt, .do_transaction_failed = m68k_cpu_transaction_failed, #endif /* !CONFIG_USER_ONLY */ @@ -552,25 +563,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; } @@ -609,7 +622,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 3a9cfe2f33..b5bbeedb7a 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -66,7 +66,7 @@ #define EXCP_MMU_ACCESS 58 /* MMU Access Level Violation Error */ #define EXCP_RTE 0x100 -#define EXCP_HALT_INSN 0x101 +#define EXCP_SEMIHOSTING 0x101 #define M68K_DTTR0 0 #define M68K_DTTR1 1 @@ -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. @@ -539,6 +551,8 @@ enum m68k_features { 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 bool m68k_feature(CPUM68KState *env, int feature) @@ -546,8 +560,6 @@ static inline bool m68k_feature(CPUM68KState *env, int feature) return (env->features & BIT_ULL(feature)) != 0; } -void m68k_cpu_list(void); - void register_m68k_insns (CPUM68KState *env); enum { @@ -564,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" @@ -599,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..a605162b71 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,51 @@ 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_2nan_prop_rule(float_2nan_prop_ab, &fp_status); + 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..136159f98f 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,11 +49,10 @@ 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); + tmp = ldl_be_p(mem_buf); if (n < 8) { /* D0-D7 */ diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 4621cf2402..9bfc6ae97c 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -21,57 +21,22 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.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; + float_status s = {}; return gdb_get_reg64(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); } switch (n) { @@ -85,19 +50,22 @@ 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); + float_status s = {}; + env->fregs[n].d = float64_to_floatx80(ldq_be_p(mem_buf), &s); return 8; } switch (n) { case 8: /* fpcontrol */ - cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); + cpu_m68k_set_fpcr(env, ldl_be_p(mem_buf)); return 4; case 9: /* fpstatus */ - env->fpsr = ldl_p(mem_buf); + env->fpsr = ldl_be_p(mem_buf); return 4; case 10: /* fpiar, not implemented */ return 4; @@ -105,8 +73,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 +88,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); @@ -133,10 +107,10 @@ static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) } switch (n) { case 8: /* fpcontrol */ - cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); + cpu_m68k_set_fpcr(env, ldl_be_p(mem_buf)); return 4; case 9: /* fpstatus */ - env->fpsr = ldl_p(mem_buf); + cpu_m68k_set_fpsr(env, ldl_be_p(mem_buf)); return 4; case 10: /* fpiar, not implemented */ return 4; @@ -151,10 +125,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. */ } @@ -505,7 +479,6 @@ static void print_address_zone(uint32_t logical, uint32_t physical, static void dump_address_map(CPUM68KState *env, uint32_t root_pointer) { - int i, j, k; int tic_size, tic_shift; uint32_t tib_mask; uint32_t tia, tib, tic; @@ -528,19 +501,19 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer) tic_shift = 12; tib_mask = M68K_4K_PAGE_MASK; } - for (i = 0; i < M68K_ROOT_POINTER_ENTRIES; i++) { + for (unsigned i = 0; i < M68K_ROOT_POINTER_ENTRIES; i++) { tia = address_space_ldl(cs->as, M68K_POINTER_BASE(root_pointer) + i * 4, MEMTXATTRS_UNSPECIFIED, &txres); if (txres != MEMTX_OK || !M68K_UDT_VALID(tia)) { continue; } - for (j = 0; j < M68K_ROOT_POINTER_ENTRIES; j++) { + for (unsigned j = 0; j < M68K_ROOT_POINTER_ENTRIES; j++) { tib = address_space_ldl(cs->as, M68K_POINTER_BASE(tia) + j * 4, MEMTXATTRS_UNSPECIFIED, &txres); if (txres != MEMTX_OK || !M68K_UDT_VALID(tib)) { continue; } - for (k = 0; k < tic_size; k++) { + for (unsigned k = 0; k < tic_size; k++) { tic = address_space_ldl(cs->as, (tib & tib_mask) + k * 4, MEMTXATTRS_UNSPECIFIED, &txres); if (txres != MEMTX_OK || !M68K_PDT_VALID(tic)) { @@ -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 87b1314925..6fbbd140f3 100644 --- a/target/m68k/m68k-semi.c +++ b/target/m68k/m68k-semi.c @@ -15,14 +15,19 @@ * * 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" +#include "gdbstub/syscalls.h" +#include "gdbstub/helpers.h" #include "semihosting/syscalls.h" -#include "semihosting/softmmu-uaccess.h" +#include "semihosting/uaccess.h" #include "hw/boards.h" #include "qemu/log.h" @@ -72,8 +77,7 @@ static int host_to_gdb_errno(int err) static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); target_ulong args = env->dregs[1]; if (put_user_u32(ret, args) || @@ -90,8 +94,7 @@ static void m68k_semi_u32_cb(CPUState *cs, uint64_t ret, int err) static void m68k_semi_u64_cb(CPUState *cs, uint64_t ret, int err) { - M68kCPU *cpu = M68K_CPU(cs); - CPUM68KState *env = &cpu->env; + CPUM68KState *env = cpu_env(cs); target_ulong args = env->dregs[1]; if (put_user_u32(ret >> 32, args) || @@ -129,8 +132,8 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) args = env->dregs[1]; switch (nr) { case HOSTED_EXIT: - gdb_exit(env->dregs[0]); - exit(env->dregs[0]); + gdb_exit(env->dregs[1]); + exit(env->dregs[1]); case HOSTED_OPEN: GET_ARG(0); @@ -165,7 +168,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr) GET_ARG64(2); GET_ARG64(3); semihost_sys_lseek(cs, m68k_semi_u64_cb, arg0, - deposit64(arg2, arg1, 32, 32), arg3); + deposit64(arg2, 32, 32, arg1), arg3); break; case HOSTED_RENAME: diff --git a/target/m68k/meson.build b/target/m68k/meson.build index 27d2d7ba87..4d213daaf6 100644 --- a/target/m68k/meson.build +++ b/target/m68k/meson.build @@ -9,11 +9,14 @@ m68k_ss.add(files( 'translate.c', )) -m68k_softmmu_ss = ss.source_set() -m68k_softmmu_ss.add(files( - 'm68k-semi.c', +m68k_system_ss = ss.source_set() +m68k_system_ss.add(files( 'monitor.c' )) +m68k_system_ss.add(when: ['CONFIG_SEMIHOSTING'], + if_true: files('m68k-semi.c'), + if_false: files('semihosting-stub.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 1ce850bbc5..15bad5dd46 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; @@ -202,18 +202,8 @@ static void cf_interrupt_all(CPUM68KState *env, int is_hw) /* Return from an exception. */ cf_rte(env); return; - case EXCP_HALT_INSN: - 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) { - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - return; - } - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); + case EXCP_SEMIHOSTING: + do_m68k_semihosting(env, env->dregs[0]); return; } } @@ -256,7 +246,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) { @@ -441,10 +431,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) @@ -457,8 +444,7 @@ 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); @@ -511,8 +497,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) { @@ -811,7 +796,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/semihosting-stub.c b/target/m68k/semihosting-stub.c new file mode 100644 index 0000000000..d6a5965e29 --- /dev/null +++ b/target/m68k/semihosting-stub.c @@ -0,0 +1,15 @@ +/* + * m68k/ColdFire semihosting stub + * + * SPDX-FileContributor: Philippe Mathieu-Daudé + * SPDX-FileCopyrightText: 2024 Linaro Ltd. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" + +void do_m68k_semihosting(CPUM68KState *env, int nr) +{ + g_assert_not_reached(); +} diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 18418312b1..ad3ce34501 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -20,20 +20,20 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" #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" #include "exec/helper-gen.h" - #include "exec/log.h" #include "fpu/softfloat.h" +#include "semihosting/semihost.h" +#define HELPER_H "helper.h" +#include "exec/helper-info.c.inc" +#undef HELPER_H //#define DEBUG_DISPATCH 1 @@ -62,27 +62,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.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 +88,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 +119,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 +136,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 +160,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 +262,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 +274,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 +302,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 +321,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 +349,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); } } @@ -491,7 +443,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 +453,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 +469,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 +498,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 +518,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 +549,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 +564,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 +578,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 +588,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 +609,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 +682,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: @@ -796,13 +720,15 @@ static TCGv gen_lea_mode(CPUM68KState *env, DisasContext *s, } /* fallthru */ case 2: /* Indirect register */ - return get_areg(s, reg0); + tmp = tcg_temp_new(); + tcg_gen_mov_i32(tmp, get_areg(s, reg0)); + return tmp; case 4: /* Indirect predecrememnt. */ if (opsize == OS_UNSIZED) { 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_M68K)) { tcg_gen_subi_i32(tmp, reg, 2); @@ -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,20 +749,23 @@ 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)); + break; case 1: /* Absolute long. */ offset = read_im32(env, s); - return mark_to_release(s, tcg_const_i32(offset)); + break; case 2: /* pc displacement */ offset = s->pc; offset += (int16_t)read_im16(env, s); - return mark_to_release(s, tcg_const_i32(offset)); + break; case 3: /* pc index+displacement. */ return gen_lea_indexed(env, s, NULL_QREG); case 4: /* Immediate. */ default: return NULL_QREG; } + tmp = tcg_temp_new(); + tcg_gen_movi_i32(tmp, offset); + return tmp; } /* Should never happen. */ return NULL_QREG; @@ -886,7 +815,7 @@ 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_M68K)) { tcg_gen_addi_i32(tmp, reg, 2); @@ -958,7 +887,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 +907,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 +926,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 +942,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 +977,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 +989,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 +1009,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 +1024,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 +1051,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 +1063,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 +1117,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 +1174,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 +1186,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 +1203,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 +1219,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 +1302,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 +1329,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 +1350,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 +1357,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. */ @@ -1528,6 +1404,40 @@ static void gen_jmp_tb(DisasContext *s, int n, target_ulong dest, s->base.is_jmp = DISAS_NORETURN; } +#ifndef CONFIG_USER_ONLY +static bool semihosting_test(DisasContext *s) +{ + uint32_t test; + + if (!semihosting_enabled(IS_USER(s))) { + return false; + } + + /* + * "The semihosting instruction is immediately preceded by a + * nop aligned to a 4-byte boundary..." + * The preceding 2-byte (aligned) nop plus the 2-byte halt/bkpt + * means that we have advanced 4 bytes from the required nop. + */ + if (s->pc % 4 != 0) { + return false; + } + test = translator_lduw(s->env, &s->base, s->pc - 4); + if (test != 0x4e71) { + return false; + } + /* "... and followed by an invalid sentinel instruction movec %sp,0." */ + test = translator_ldl(s->env, &s->base, s->pc); + if (test != 0x4e7bf000) { + return false; + } + + /* Consume the sentinel. */ + s->pc += 4; + return true; +} +#endif /* !CONFIG_USER_ONLY */ + DISAS_INSN(scc) { DisasCompare c; @@ -1538,12 +1448,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 +1494,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 +1517,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 +1536,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 +1567,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 +1583,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 +1613,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 +1647,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 +1654,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 +1702,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 +1800,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 +1845,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 +1879,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 +1898,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 +1920,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 +1982,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 +1995,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 +2021,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 +2036,6 @@ DISAS_INSN(movem) } } } - - tcg_temp_free(incr); - tcg_temp_free(addr); } DISAS_INSN(movep) @@ -2175,22 +2065,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) @@ -2249,7 +2137,6 @@ DISAS_INSN(bitop_im) break; } DEST_EA(env, insn, opsize, tmp, &addr); - tcg_temp_free(tmp); } } @@ -2259,7 +2146,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 +2159,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; } @@ -2287,7 +2173,7 @@ static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only) } else { /* Must writeback before changing security state. */ do_writebacks(s); - gen_helper_set_sr(cpu_env, tcg_constant_i32(val)); + gen_helper_set_sr(tcg_env, tcg_constant_i32(val)); } set_cc_op(s, CC_OP_FLAGS); } @@ -2295,11 +2181,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 { /* Must writeback before changing security state. */ do_writebacks(s); - gen_helper_set_sr(cpu_env, val); + gen_helper_set_sr(tcg_env, val); } set_cc_op(s, CC_OP_FLAGS); } @@ -2332,13 +2218,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(); @@ -2421,8 +2307,6 @@ DISAS_INSN(arith_im) default: abort(); } - tcg_temp_free(im); - tcg_temp_free(dest); } DISAS_INSN(cas) @@ -2478,8 +2362,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)); @@ -2494,7 +2376,6 @@ DISAS_INSN(cas2w) { uint16_t ext1, ext2; TCGv addr1, addr2; - TCGv regs; /* cas2 Dc1:Dc2,Du1:Du2,(Rn1):(Rn2) */ @@ -2526,16 +2407,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; @@ -2577,16 +2457,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; @@ -2655,10 +2534,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); @@ -2701,12 +2579,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) @@ -2732,7 +2608,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) @@ -2767,14 +2642,21 @@ 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 + /* BKPT #0 is the alternate semihosting instruction. */ + if ((insn & 7) == 0 && semihosting_test(s)) { + gen_exception(s, s->pc, EXCP_SEMIHOSTING); + return; + } + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); +#endif } DISAS_INSN(pea) @@ -2807,7 +2689,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) @@ -2852,7 +2733,6 @@ DISAS_INSN(tas) tcg_gen_atomic_fetch_or_tl(src1, addr, tcg_constant_tl(0x80), IS_USER(s), MO_SB); gen_logic_cc(s, src1, OS_BYTE); - tcg_temp_free(src1); switch (mode) { case 3: /* Indirect postincrement. */ @@ -2906,13 +2786,14 @@ DISAS_INSN(mull) 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); @@ -2941,7 +2822,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) @@ -2972,11 +2852,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)) { @@ -2984,7 +2862,7 @@ DISAS_INSN(reset) return; } - gen_helper_reset(cpu_env); + gen_helper_reset(tcg_env); } #endif @@ -3013,10 +2891,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); } @@ -3045,7 +2921,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); } @@ -3070,7 +2946,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) { @@ -3095,9 +2971,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) @@ -3116,7 +2990,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 */ @@ -3175,7 +3049,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) @@ -3190,7 +3063,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 */ @@ -3199,18 +3072,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 */ @@ -3258,9 +3131,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) @@ -3269,12 +3139,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) @@ -3334,7 +3204,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) @@ -3343,7 +3212,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) @@ -3386,7 +3254,6 @@ DISAS_INSN(and) gen_partset_reg(opsize, reg, dest); } gen_logic_cc(s, dest, opsize); - tcg_temp_free(dest); } DISAS_INSN(adda) @@ -3401,7 +3268,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 */ @@ -3410,17 +3277,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 */ @@ -3468,9 +3335,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) @@ -3498,15 +3362,13 @@ static inline void shift_im(DisasContext *s, uint16_t insn, int opsize) 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); @@ -3557,12 +3419,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); @@ -3580,10 +3441,9 @@ static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize) * V = ((s ^ t) & (-1 << (bits - 1))) != 0 */ if (!logical && m68k_feature(s->env, M68K_FEATURE_M68K)) { - TCGv_i64 tt = tcg_const_i64(32); + 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); @@ -3592,9 +3452,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); @@ -3615,10 +3474,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); @@ -3766,7 +3621,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(); @@ -3777,27 +3632,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 */ @@ -3831,7 +3681,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] */ @@ -3845,7 +3694,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] */ @@ -3859,17 +3707,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; } @@ -3885,15 +3729,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); } @@ -3912,15 +3754,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); } @@ -3938,15 +3778,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); } @@ -3978,10 +3816,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); } @@ -4012,10 +3847,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); } @@ -4047,10 +3879,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); } @@ -4064,15 +3893,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); } @@ -4113,7 +3940,6 @@ DISAS_INSN(bfext_reg) } else { tcg_gen_shr_i32(dst, tmp, shift); } - tcg_temp_free(shift); } else { /* Immediate width. */ if (ext & 0x800) { @@ -4142,7 +3968,6 @@ DISAS_INSN(bfext_reg) } } - tcg_temp_free(tmp); set_cc_op(s, CC_OP_LOGIC); } @@ -4162,31 +3987,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) @@ -4195,14 +4012,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. */ @@ -4213,48 +4024,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); @@ -4267,8 +4081,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); @@ -4279,7 +4091,6 @@ DISAS_INSN(bfop_reg) default: g_assert_not_reached(); } - tcg_temp_free(mask); } DISAS_INSN(bfop_mem) @@ -4297,44 +4108,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) @@ -4404,11 +4207,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) @@ -4426,23 +4225,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) @@ -4476,7 +4268,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) @@ -4511,9 +4303,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) { @@ -4523,10 +4313,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) @@ -4540,18 +4327,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) @@ -4572,7 +4355,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) @@ -4581,7 +4363,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) */ @@ -4591,8 +4373,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); @@ -4632,7 +4412,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; @@ -4677,7 +4457,6 @@ DISAS_INSN(moves) } else { gen_partset_reg(opsize, reg, tmp); } - tcg_temp_free(tmp); } switch (extract32(insn, 3, 3)) { case 3: /* Indirect postincrement. */ @@ -4708,7 +4487,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])); } @@ -4718,7 +4497,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])); } @@ -4728,8 +4507,12 @@ DISAS_INSN(halt) gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - - gen_exception(s, s->pc, EXCP_HALT_INSN); + if (semihosting_test(s)) { + gen_exception(s, s->pc, EXCP_SEMIHOSTING); + return; + } + tcg_gen_movi_i32(cpu_halted, 1); + gen_exception(s, s->pc, EXCP_HLT); } DISAS_INSN(stop) @@ -4774,14 +4557,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); @@ -4795,10 +4578,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); } @@ -4839,7 +4623,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; @@ -4849,9 +4633,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) @@ -4862,9 +4645,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 @@ -4910,7 +4692,6 @@ static void do_trapcc(DisasContext *s, DisasCompare *c) s->base.is_jmp = DISAS_NEXT; } } - free_cond(c); } DISAS_INSN(trapcc) @@ -4951,10 +4732,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; } } @@ -4965,10 +4746,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; } } @@ -4980,8 +4761,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) @@ -4990,9 +4770,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); } @@ -5036,9 +4815,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; @@ -5091,7 +4869,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, @@ -5129,30 +4906,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); } /* @@ -5176,11 +4952,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; @@ -5191,8 +4965,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. */ @@ -5226,175 +4999,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? */ @@ -5405,141 +5175,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) @@ -5549,7 +5294,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) @@ -5584,12 +5328,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) @@ -5620,7 +5361,7 @@ DISAS_INSN(ftrapcc) do_trapcc(s, &c); } -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) DISAS_INSN(frestore) { TCGv addr; @@ -5646,9 +5387,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); } @@ -5747,12 +5487,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); @@ -5777,7 +5517,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 @@ -5788,11 +5528,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. */ @@ -5811,7 +5551,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 @@ -5820,18 +5560,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; @@ -5848,7 +5588,6 @@ DISAS_INSN(mac) case 4: /* Pre-decrement. */ tcg_gen_mov_i32(AREG(insn, 0), addr); } - tcg_temp_free(loadval); } } @@ -5862,7 +5601,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) { @@ -5882,10 +5621,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) @@ -5908,11 +5647,11 @@ 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) @@ -5921,8 +5660,7 @@ DISAS_INSN(macsr_to_ccr) /* 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(cpu_env, tmp); - tcg_temp_free(tmp); + gen_helper_set_ccr(tcg_env, tmp); set_cc_op(s, CC_OP_FLAGS); } @@ -5944,14 +5682,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); } @@ -5967,13 +5705,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]; @@ -6066,7 +5804,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(bitop_im, 08c0, ffc0); INSN(arith_im, 0a80, fff8, CF_ISA_A); INSN(arith_im, 0a00, ff00, M68K); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(moves, 0e00, ff00, M68K); #endif INSN(cas, 0ac0, ffc0, CAS); @@ -6095,7 +5833,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(move_to_ccr, 44c0, ffc0); INSN(not, 4680, fff8, CF_ISA_A); INSN(not, 4600, ff00, M68K); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) BASE(move_to_sr, 46c0, ffc0); #endif INSN(nbcd, 4800, ffc0, M68K); @@ -6112,7 +5850,7 @@ void register_m68k_insns (CPUM68KState *env) BASE(tst, 4a00, ff00); INSN(tas, 4ac0, ffc0, CF_ISA_B); INSN(tas, 4ac0, ffc0, M68K); -#if defined(CONFIG_SOFTMMU) +#if !defined(CONFIG_USER_ONLY) INSN(halt, 4ac8, ffff, CF_ISA_A); INSN(halt, 4ac8, ffff, M68K); #endif @@ -6126,7 +5864,7 @@ 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, M68K); @@ -6251,7 +5989,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); @@ -6273,7 +6011,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; @@ -6283,7 +6021,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 */ @@ -6305,12 +6042,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; @@ -6373,24 +6109,16 @@ static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void m68k_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps m68k_tr_ops = { .init_disas_context = m68k_tr_init_disas_context, .tb_start = m68k_tr_tb_start, .insn_start = m68k_tr_insn_start, .translate_insn = m68k_tr_translate_insn, .tb_stop = m68k_tr_tb_stop, - .disas_log = m68k_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; translator_loop(cpu, tb, max_insns, pc, host_pc, &m68k_tr_ops, &dc.base); @@ -6410,8 +6138,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++) { @@ -6463,7 +6190,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], @@ -6477,5 +6204,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 +#endif /* !CONFIG_USER_ONLY */ } diff --git a/target/meson.build b/target/meson.build index a53a60486f..b29598e7c5 100644 --- a/target/meson.build +++ b/target/meson.build @@ -1,7 +1,6 @@ subdir('alpha') subdir('arm') subdir('avr') -subdir('cris') subdir('hexagon') subdir('hppa') subdir('i386') @@ -9,7 +8,6 @@ subdir('loongarch') subdir('m68k') subdir('microblaze') subdir('mips') -subdir('nios2') subdir('openrisc') subdir('ppc') subdir('riscv') diff --git a/target/microblaze/Kconfig b/target/microblaze/Kconfig index a5410d9218..e91d58d88f 100644 --- a/target/microblaze/Kconfig +++ b/target/microblaze/Kconfig @@ -1,2 +1,3 @@ config MICROBLAZE bool + select DEVICE_TREE # needed by boot.c diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index 5e54ea0108..00efb509e3 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -2,7 +2,7 @@ * MicroBlaze cpu parameters for qemu. * * Copyright (c) 2009 Edgar E. Iglesias - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef MICROBLAZE_CPU_PARAM_H @@ -28,6 +28,8 @@ /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 -#define NB_MMU_MODES 3 + +/* MicroBlaze is always in-order. */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL #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 89e493f3ff..710eb1146c 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -28,7 +28,10 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/gdbstub.h" #include "fpu/softfloat-helpers.h" +#include "tcg/tcg.h" static const struct { const char *name; @@ -96,7 +99,8 @@ static void mb_cpu_synchronize_from_tb(CPUState *cs, { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - cpu->env.pc = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu->env.pc = tb->pc; cpu->env.iflags = tb->flags & IFLAGS_TB_MASK; } @@ -115,6 +119,22 @@ 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) { @@ -162,14 +182,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, ResetType type) { - 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, type); + } memset(env, 0, offsetof(CPUMBState, end_reset_fields)); env->res_addr = RES_ADDR_NONE; @@ -179,6 +201,13 @@ static void mb_cpu_reset(DeviceState *dev) env->pc = cpu->cfg.base_vectors; + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + /* + * TODO: this is probably not the correct NaN propagation rule for + * this architecture. + */ + set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); + #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ mb_cpu_write_msr(env, MSR_EE | MSR_IE | MSR_VM | MSR_UM); @@ -289,11 +318,11 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) static void mb_cpu_initfn(Object *obj) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(obj); - CPUMBState *env = &cpu->env; - cpu_set_cpustate_pointers(cpu); - - set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + 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); #ifndef CONFIG_USER_ONLY /* Inbound IRQ and FIR lines */ @@ -303,9 +332,16 @@ static void mb_cpu_initfn(Object *obj) qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_dc, "ns_axi_dc", 1); qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_ic, "ns_axi_ic", 1); #endif + + /* Restricted 'endianness' property is equivalent of 'little-endian' */ + object_property_add_alias(obj, "little-endian", obj, "endianness"); } static Property mb_properties[] = { + /* + * Following properties are used by Xilinx DTS conversion tool + * do not rename them. + */ DEFINE_PROP_UINT32("base-vectors", MicroBlazeCPU, cfg.base_vectors, 0), DEFINE_PROP_BOOL("use-stack-protection", MicroBlazeCPU, cfg.stackprot, false), @@ -362,6 +398,9 @@ static Property mb_properties[] = { DEFINE_PROP_UINT8("pvr", MicroBlazeCPU, cfg.pvr, C_PVR_FULL), DEFINE_PROP_UINT8("pvr-user1", MicroBlazeCPU, cfg.pvr_user1, 0), DEFINE_PROP_UINT32("pvr-user2", MicroBlazeCPU, cfg.pvr_user2, 0), + /* + * End of properties reserved by Xilinx DTS conversion tool. + */ DEFINE_PROP_END_OF_LIST(), }; @@ -380,7 +419,7 @@ 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, @@ -388,6 +427,7 @@ static const struct TCGCPUOps mb_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = mb_cpu_tlb_fill, .cpu_exec_interrupt = mb_cpu_exec_interrupt, + .cpu_exec_halt = mb_cpu_has_work, .do_interrupt = mb_cpu_do_interrupt, .do_transaction_failed = mb_cpu_transaction_failed, .do_unaligned_access = mb_cpu_do_unaligned_access, @@ -399,14 +439,16 @@ 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; @@ -418,7 +460,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; @@ -428,6 +470,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..3e5a3e5c60 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -202,7 +202,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 +340,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 +405,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 +431,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 98bdb82de8..5d3259ce31 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/host-utils.h" #include "exec/log.h" @@ -51,7 +52,7 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (mmu_idx == MMU_NOMMU_IDX) { /* MMU disabled or not available. */ address &= TARGET_PAGE_MASK; - prot = PAGE_BITS; + prot = PAGE_RWX; tlb_set_page_with_attrs(cs, address, address, attrs, prot, mmu_idx, TARGET_PAGE_SIZE); return true; @@ -228,10 +229,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 +253,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) 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..2423ac6172 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -22,6 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" static unsigned int tlb_decode_size(unsigned int f) { @@ -305,7 +306,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 974f21eb31..4beaf69e76 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -20,17 +20,20 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.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 = translator_ldl(cpu_env(cs), &dc->base, 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; } @@ -1833,24 +1770,16 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) } } -static void mb_tr_disas_log(const DisasContextBase *dcb, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcb->pc_first)); - target_disas(logfile, cs, dcb->pc_first, dcb->tb->size); -} - static const TranslatorOps mb_tr_ops = { .init_disas_context = mb_tr_init_disas_context, .tb_start = mb_tr_tb_start, .insn_start = mb_tr_insn_start, .translate_insn = mb_tr_translate_insn, .tb_stop = mb_tr_tb_stop, - .disas_log = mb_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; translator_loop(cpu, tb, max_insns, pc, host_pc, &mb_tr_ops, &dc.base); @@ -1858,8 +1787,7 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, 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; @@ -1940,9 +1868,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"); + tcg_global_mem_new(tcg_env, offsetof(CPUMBState, res_addr), "res_addr"); } diff --git a/target/mips/Kconfig b/target/mips/Kconfig index 6adf145354..876048b150 100644 --- a/target/mips/Kconfig +++ b/target/mips/Kconfig @@ -1,5 +1,6 @@ config MIPS bool + imply SEMIHOSTING if TCG config MIPS64 bool diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index 480e60aeec..922fc39138 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, @@ -294,7 +314,7 @@ const mips_def_t mips_defs[] = (0x3fe << CP0SRSC4_SRS14) | (0x3fe << CP0SRSC4_SRS13), .SEGBITS = 32, .PABITS = 32, - .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_MT, + .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP, .mmu_type = MMU_TYPE_R4000, }, { @@ -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, @@ -423,14 +478,15 @@ const mips_def_t mips_defs[] = (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), .CP0_Config2 = MIPS_CONFIG2, - .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_BP) | (1 << CP0C3_BI) | + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_MSAP) | + (1 << CP0C3_BP) | (1 << CP0C3_BI) | (2 << CP0C3_ISA) | (1 << CP0C3_ULRI) | (1 << CP0C3_RXI) | (1U << CP0C3_M), .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | (3 << CP0C4_IE) | (1U << CP0C4_M), .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_LLB), - .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | - (1 << CP0C5_UFE), + .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_UFE) | + (1 << CP0C5_FRE) | (1 << CP0C5_SBRI), .CP0_LLAddr_rw_bitmask = 0, .CP0_LLAddr_shift = 0, .SYNCI_Step = 32, @@ -444,6 +500,7 @@ const mips_def_t mips_defs[] = (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), .CP1_fcr31_rw_bitmask = 0x0103FFFF, + .MSAIR = 0x03 << MSAIR_ProcID, .SEGBITS = 32, .PABITS = 32, .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, @@ -486,7 +543,7 @@ const mips_def_t mips_defs[] = .SEGBITS = 32, .PABITS = 32, .insn_flags = CPU_MIPS32R6 | ISA_NANOMIPS32 | - ASE_DSP | ASE_DSP_R2 | ASE_DSP_R3 | ASE_MT, + ASE_DSP | ASE_DSP_R2 | ASE_DSP_R3, .mmu_type = MMU_TYPE_R4000, }, #if defined(TARGET_MIPS64) @@ -606,7 +663,7 @@ const mips_def_t mips_defs[] = .CP1_fcr31_rw_bitmask = 0xFF83FFFF, .SEGBITS = 40, .PABITS = 36, - .insn_flags = CPU_MIPS64R1 | ASE_MIPS3D, + .insn_flags = CPU_MIPS64R1, .mmu_type = MMU_TYPE_R4000, }, { @@ -635,7 +692,7 @@ const mips_def_t mips_defs[] = .CP1_fcr31_rw_bitmask = 0xFF83FFFF, .SEGBITS = 42, .PABITS = 36, - .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D, + .insn_flags = CPU_MIPS64R2, .mmu_type = MMU_TYPE_R4000, }, { @@ -700,7 +757,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 +797,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 +943,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 | @@ -954,15 +1020,6 @@ const mips_def_t mips_defs[] = }; 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; @@ -981,7 +1038,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..f3a37e2dbe 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -1,7 +1,7 @@ /* * MIPS cpu parameters for qemu. * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef MIPS_CPU_PARAM_H @@ -29,6 +29,7 @@ #define TARGET_PAGE_BITS_VARY #define TARGET_PAGE_BITS_MIN 12 #endif -#define NB_MMU_MODES 4 + +#define TCG_GUEST_DEFAULT_MO (0) #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 7a565466cb..d0a43b6d5c 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 @@ -123,9 +122,7 @@ void cpu_set_exception_base(int vp_index, target_ulong address) static void mips_cpu_set_pc(CPUState *cs, vaddr value) { - MIPSCPU *cpu = MIPS_CPU(cs); - - mips_env_set_pc(&cpu->env, value); + mips_env_set_pc(cpu_env(cs), value); } static vaddr mips_cpu_get_pc(CPUState *cs) @@ -137,18 +134,19 @@ static vaddr mips_cpu_get_pc(CPUState *cs) 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; } @@ -180,25 +178,30 @@ 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, ResetType type) { - 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, type); + } memset(env, 0, offsetof(CPUMIPSState, end_reset_fields)); /* Reset registers to their default values */ env->CP0_PRid = env->cpu_model->CP0_PRid; - env->CP0_Config0 = env->cpu_model->CP0_Config0; -#if TARGET_BIG_ENDIAN - env->CP0_Config0 |= (1 << CP0C0_BE); -#endif + env->CP0_Config0 = deposit32(env->cpu_model->CP0_Config0, + CP0C0_BE, 1, cpu->is_big_endian); env->CP0_Config1 = env->cpu_model->CP0_Config1; env->CP0_Config2 = env->cpu_model->CP0_Config2; env->CP0_Config3 = env->cpu_model->CP0_Config3; @@ -240,6 +243,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; @@ -290,12 +295,7 @@ 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; } @@ -407,9 +407,9 @@ static void mips_cpu_reset(DeviceState *dev) } msa_reset(env); + fp_reset(env); compute_hflags(env); - restore_fp_status(env); restore_pamask(env); cs->exception_index = EXCP_NONE; @@ -427,19 +427,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 } } @@ -452,9 +447,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) @@ -505,9 +500,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) @@ -535,13 +539,18 @@ static const struct SysemuCPUOps mips_sysemu_ops = { }; #endif +static Property mips_cpu_properties[] = { + DEFINE_PROP_BOOL("big-endian", MIPSCPU, is_big_endian, TARGET_BIG_ENDIAN), + DEFINE_PROP_END_OF_LIST(), +}; + #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" /* * 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, @@ -549,6 +558,7 @@ static const struct TCGCPUOps mips_tcg_ops = { #if !defined(CONFIG_USER_ONLY) .tlb_fill = mips_cpu_tlb_fill, .cpu_exec_interrupt = mips_cpu_exec_interrupt, + .cpu_exec_halt = mips_cpu_has_work, .do_interrupt = mips_cpu_do_interrupt, .do_transaction_failed = mips_cpu_do_transaction_failed, .do_unaligned_access = mips_cpu_do_unaligned_access, @@ -562,13 +572,17 @@ 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_props(dc, mips_cpu_properties); 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; @@ -589,6 +603,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), @@ -627,41 +642,16 @@ 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) +MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk, + bool is_big_endian) { DeviceState *cpu; cpu = DEVICE(object_new(cpu_type)); qdev_connect_clock_in(cpu, "clk-in", cpu_refclk); + object_property_set_bool(OBJECT(cpu), "big-endian", is_big_endian, + &error_abort); qdev_realize(cpu, NULL, &error_abort); return MIPS_CPU(cpu); diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 0a085643a3..f6877ece8b 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -3,12 +3,13 @@ #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" -#define TCG_GUEST_DEFAULT_MO (0) - typedef struct CPUMIPSTLBContext CPUMIPSTLBContext; /* MSA Context */ @@ -529,7 +530,6 @@ typedef struct CPUArchState { CPUMIPSFPUContext active_fpu; uint32_t current_tc; - uint32_t current_fpu; uint32_t SEGBITS; uint32_t PABITS; @@ -744,9 +744,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 +978,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 @@ -1067,6 +1066,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]; @@ -1143,7 +1169,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; @@ -1152,15 +1177,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; /** @@ -1172,39 +1202,56 @@ 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 */ + + /* Properties */ + bool is_big_endian; }; +/** + * 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); } @@ -1265,20 +1312,30 @@ 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); bool cpu_supports_isa(const CPUMIPSState *env, uint64_t isa_mask); bool cpu_type_supports_isa(const char *cpu_type, uint64_t isa); +/* Check presence of MIPS-3D ASE */ +static inline bool ase_3d_available(const CPUMIPSState *env) +{ + return env->active_fpu.fcr0 & (1 << FCR0_3D); +} + /* Check presence of MSA implementation */ 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) { @@ -1296,27 +1353,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; @@ -1328,12 +1381,14 @@ static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, * mips_cpu_create_with_clock: * @typename: a MIPS CPU type. * @cpu_refclk: this cpu input clock (an output clock of another device) + * @is_big_endian: whether this CPU is configured in big endianness * * Instantiates a MIPS CPU, set the input clock of the CPU to @cpu_refclk, * then realizes the CPU. * * Returns: A #CPUState or %NULL if an error occurred. */ -MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk); +MIPSCPU *mips_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk, + bool is_big_endian); #endif /* MIPS_CPU_H */ diff --git a/target/mips/fpu_helper.h b/target/mips/fpu_helper.h index ad1116e8c1..7c3c7897b4 100644 --- a/target/mips/fpu_helper.h +++ b/target/mips/fpu_helper.h @@ -44,6 +44,28 @@ static inline void restore_fp_status(CPUMIPSState *env) restore_snan_bit_mode(env); } +static inline void fp_reset(CPUMIPSState *env) +{ + restore_fp_status(env); + + /* + * According to MIPS specifications, if one of the two operands is + * a sNaN, a new qNaN has to be generated. This is done in + * floatXX_silence_nan(). For qNaN inputs the specifications + * says: "When possible, this QNaN result is one of the operand QNaN + * values." In practice it seems that most implementations choose + * the first operand if both operands are qNaN. In short this gives + * the following rules: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always silenced before returning it. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, + &env->active_fpu.fp_status); +} + /* MSA */ enum CPUMIPSMSADataFormat { 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..91c786cff8 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 { @@ -227,6 +225,16 @@ static inline void mips_env_set_pc(CPUMIPSState *env, target_ulong value) } } +static inline bool mips_env_is_bigendian(CPUMIPSState *env) +{ + return extract32(env->CP0_Config0, CP0C0_BE, 1); +} + +static inline MemOp mo_endian_env(CPUMIPSState *env) +{ + return mips_env_is_bigendian(env) ? MO_BE : MO_LE; +} + static inline void restore_pamask(CPUMIPSState *env) { if (env->hflags & MIPS_HFLAG_ELPA) { diff --git a/target/mips/kvm.c b/target/mips/kvm.c index bcb8e06b2c..a98798c669 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); @@ -1179,10 +1172,9 @@ static int kvm_mips_get_cp0_registers(CPUState *cs) return ret; } -int kvm_arch_put_registers(CPUState *cs, int level) +int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); struct kvm_regs regs; int ret; int i; @@ -1215,10 +1207,9 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } -int kvm_arch_get_registers(CPUState *cs) +int kvm_arch_get_registers(CPUState *cs, Error **errp) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); int ret = 0; struct kvm_regs regs; int i; @@ -1266,35 +1257,22 @@ 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; } -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 a6cebe0265..9d4d292586 100644 --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -26,12 +26,10 @@ * bits 24-39: MIPS ASEs */ #define ASE_MIPS16 0x0000000001000000ULL -#define ASE_MIPS3D 0x0000000002000000ULL #define ASE_MDMX 0x0000000004000000ULL #define ASE_DSP 0x0000000008000000ULL #define ASE_DSP_R2 0x0000000010000000ULL #define ASE_DSP_R3 0x0000000020000000ULL -#define ASE_MT 0x0000000040000000ULL #define ASE_SMARTMIPS 0x0000000080000000ULL #define ASE_MICROMIPS 0x0000000100000000ULL /* diff --git a/target/mips/msa.c b/target/mips/msa.c index 61f1a9a593..9dffc428f5 100644 --- a/target/mips/msa.c +++ b/target/mips/msa.c @@ -49,6 +49,23 @@ void msa_reset(CPUMIPSState *env) set_float_detect_tininess(float_tininess_after_rounding, &env->active_tc.msa_fp_status); + /* + * According to MIPS specifications, if one of the two operands is + * a sNaN, a new qNaN has to be generated. This is done in + * floatXX_silence_nan(). For qNaN inputs the specifications + * says: "When possible, this QNaN result is one of the operand QNaN + * values." In practice it seems that most implementations choose + * the first operand if both operands are qNaN. In short this gives + * the following rules: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always silenced before returning it. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, + &env->active_tc.msa_fp_status); + /* clear float_status exception flags */ set_float_exception_flags(0, &env->active_tc.msa_fp_status); 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..8af11fd896 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), @@ -142,6 +142,7 @@ static int get_tlb(QEMUFile *f, void *pv, size_t size, qemu_get_betls(f, &v->VPN); qemu_get_be32s(f, &v->PageMask); qemu_get_be16s(f, &v->ASID); + qemu_get_be32s(f, &v->MMID); qemu_get_be16s(f, &flags); v->G = (flags >> 10) & 1; v->C0 = (flags >> 7) & 3; @@ -167,6 +168,7 @@ static int put_tlb(QEMUFile *f, void *pv, size_t size, r4k_tlb_t *v = pv; uint16_t asid = v->ASID; + uint32_t mmid = v->MMID; uint16_t flags = ((v->EHINV << 15) | (v->RI1 << 14) | (v->RI0 << 13) | @@ -183,6 +185,7 @@ static int put_tlb(QEMUFile *f, void *pv, size_t size, qemu_put_betls(f, &v->VPN); qemu_put_be32s(f, &v->PageMask); qemu_put_be16s(f, &asid); + qemu_put_be32s(f, &mmid); qemu_put_be16s(f, &flags); qemu_put_be64s(f, &v->PFN[0]); qemu_put_be64s(f, &v->PFN[1]); @@ -190,7 +193,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 +205,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[]) { + .version_id = 3, + .minimum_version_id = 3, + .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 +224,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), @@ -239,7 +242,7 @@ const VMStateDescription vmstate_mips_cpu = { /* CPU metastate */ VMSTATE_UINT32(env.current_tc, MIPSCPU), - VMSTATE_UINT32(env.current_fpu, MIPSCPU), + VMSTATE_UNUSED(sizeof(uint32_t)), /* was current_fpu */ VMSTATE_INT32(env.error_code, MIPSCPU), VMSTATE_UINTTL(env.btarget, MIPSCPU), VMSTATE_UINTTL(env.bcond, MIPSCPU), @@ -281,8 +284,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..505781d84c 100644 --- a/target/mips/sysemu/physaddr.c +++ b/target/mips/sysemu/physaddr.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "../internal.h" static int is_seg_am_mapped(unsigned int am, bool eu, int mmu_idx) @@ -70,8 +71,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 +130,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 +231,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 96e61170e6..4886d087b2 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -79,10 +79,10 @@ 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); - env->active_tc.PC = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + env->active_tc.PC = tb->pc; env->hflags &= ~MIPS_HFLAG_BMASK; env->hflags |= tb->flags & MIPS_HFLAG_BMASK; } 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/godson2.decode b/target/mips/tcg/godson2.decode new file mode 100644 index 0000000000..25b396b682 --- /dev/null +++ b/target/mips/tcg/godson2.decode @@ -0,0 +1,27 @@ +# Godson2 64-bit Integer instructions +# +# Copyright (C) 2021 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Reference: +# Godson-2E Software Manual +# (Document Number: godson2e-user-manual-V0.6) +# + +&muldiv rs rt rd + +@rs_rt_rd ...... rs:5 rt:5 rd:5 ..... ...... &muldiv + +MULTu_G 011111 ..... ..... ..... 00000 01100- @rs_rt_rd +DMULTu_G 011111 ..... ..... ..... 00000 01110- @rs_rt_rd + +DIV_G 011111 ..... ..... ..... 00000 011010 @rs_rt_rd +DIVU_G 011111 ..... ..... ..... 00000 011011 @rs_rt_rd +DDIV_G 011111 ..... ..... ..... 00000 011110 @rs_rt_rd +DDIVU_G 011111 ..... ..... ..... 00000 011111 @rs_rt_rd + +MOD_G 011111 ..... ..... ..... 00000 100010 @rs_rt_rd +MODU_G 011111 ..... ..... ..... 00000 100011 @rs_rt_rd +DMOD_G 011111 ..... ..... ..... 00000 100110 @rs_rt_rd +DMODU_G 011111 ..... ..... ..... 00000 100111 @rs_rt_rd 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..f92a923d7a 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" @@ -52,11 +53,6 @@ HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong)) #endif /* !CONFIG_USER_ONLY */ -static inline bool cpu_is_bigendian(CPUMIPSState *env) -{ - return extract32(env->CP0_Config0, CP0C0_BE, 1); -} - static inline target_ulong get_lmask(CPUMIPSState *env, target_ulong value, unsigned bits) { @@ -64,7 +60,7 @@ static inline target_ulong get_lmask(CPUMIPSState *env, value &= mask; - if (!cpu_is_bigendian(env)) { + if (!mips_env_is_bigendian(env)) { value ^= mask; } @@ -75,7 +71,7 @@ void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { target_ulong lmask = get_lmask(env, arg2, 32); - int dir = cpu_is_bigendian(env) ? 1 : -1; + int dir = mips_env_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); @@ -99,7 +95,7 @@ void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { target_ulong lmask = get_lmask(env, arg2, 32); - int dir = cpu_is_bigendian(env) ? 1 : -1; + int dir = mips_env_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); @@ -129,7 +125,7 @@ void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { target_ulong lmask = get_lmask(env, arg2, 64); - int dir = cpu_is_bigendian(env) ? 1 : -1; + int dir = mips_env_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); @@ -173,7 +169,7 @@ void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, int mem_idx) { target_ulong lmask = get_lmask(env, arg2, 64); - int dir = cpu_is_bigendian(env) ? 1 : -1; + int dir = mips_env_is_bigendian(env) ? 1 : -1; cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); @@ -248,14 +244,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/loong-ext.decode b/target/mips/tcg/loong-ext.decode new file mode 100644 index 0000000000..b43979d0ef --- /dev/null +++ b/target/mips/tcg/loong-ext.decode @@ -0,0 +1,28 @@ +# Loongson 64-bit Extension instructions +# +# Copyright (C) 2021 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Reference: +# STLS2F01 User Manual +# Appendix A: new integer instructions +# (Document Number: UM0447) +# + +&muldiv rs rt rd !extern + +@rs_rt_rd ...... rs:5 rt:5 rd:5 ..... ...... &muldiv + +MULTu_G 011100 ..... ..... ..... 00000 0100-0 @rs_rt_rd +DMULTu_G 011100 ..... ..... ..... 00000 0100-1 @rs_rt_rd + +DIV_G 011100 ..... ..... ..... 00000 010100 @rs_rt_rd +DDIV_G 011100 ..... ..... ..... 00000 010101 @rs_rt_rd +DIVU_G 011100 ..... ..... ..... 00000 010110 @rs_rt_rd +DDIVU_G 011100 ..... ..... ..... 00000 010111 @rs_rt_rd + +MOD_G 011100 ..... ..... ..... 00000 011100 @rs_rt_rd +DMOD_G 011100 ..... ..... ..... 00000 011101 @rs_rt_rd +MODU_G 011100 ..... ..... ..... 00000 011110 @rs_rt_rd +DMODU_G 011100 ..... ..... ..... 00000 011111 @rs_rt_rd diff --git a/target/mips/tcg/loong_translate.c b/target/mips/tcg/loong_translate.c new file mode 100644 index 0000000000..7d74cc34f8 --- /dev/null +++ b/target/mips/tcg/loong_translate.c @@ -0,0 +1,271 @@ +/* + * MIPS Loongson 64-bit translation routines + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * Copyright (c) 2006 Marius Groeger (FPU operations) + * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) + * Copyright (c) 2011 Richard Henderson + * Copyright (c) 2021 Philippe Mathieu-Daudé + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "translate.h" + +/* Include the auto-generated decoder. */ +#include "decode-godson2.c.inc" +#include "decode-loong-ext.c.inc" + +/* + * Word or double-word Fixed-point instructions. + * --------------------------------------------- + * + * Fixed-point multiplies and divisions write only + * one result into general-purpose registers. + */ + +static bool gen_lext_DIV_G(DisasContext *s, int rd, int rs, int rt, + bool is_double) +{ + TCGv t0, t1; + TCGLabel *l1, *l2, *l3; + + if (rd == 0) { + /* Treat as NOP. */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + l1 = gen_new_label(); + l2 = gen_new_label(); + l3 = gen_new_label(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if (!is_double) { + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_ext32s_tl(t1, t1); + } + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l3); + gen_set_label(l1); + + tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_div_tl(cpu_gpr[rd], t0, t1); + if (!is_double) { + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + } + gen_set_label(l3); + + return true; +} + +static bool trans_DIV_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, false); +} + +static bool trans_DDIV_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_DIV_G(s, a->rd, a->rs, a->rt, true); +} + +static bool gen_lext_DIVU_G(DisasContext *s, int rd, int rs, int rt, + bool is_double) +{ + TCGv t0, t1; + TCGLabel *l1, *l2; + + if (rd == 0) { + /* Treat as NOP. */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + l1 = gen_new_label(); + l2 = gen_new_label(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if (!is_double) { + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + } + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); + if (!is_double) { + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + } + gen_set_label(l2); + + return true; +} + +static bool trans_DIVU_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, false); +} + +static bool trans_DDIVU_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_DIVU_G(s, a->rd, a->rs, a->rt, true); +} + +static bool gen_lext_MOD_G(DisasContext *s, int rd, int rs, int rt, + bool is_double) +{ + TCGv t0, t1; + TCGLabel *l1, *l2, *l3; + + if (rd == 0) { + /* Treat as NOP. */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + l1 = gen_new_label(); + l2 = gen_new_label(); + l3 = gen_new_label(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if (!is_double) { + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + } + tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, is_double ? LLONG_MIN : INT_MIN, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); + if (!is_double) { + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + } + gen_set_label(l3); + + return true; +} + +static bool trans_MOD_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, false); +} + +static bool trans_DMOD_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_MOD_G(s, a->rd, a->rs, a->rt, true); +} + +static bool gen_lext_MODU_G(DisasContext *s, int rd, int rs, int rt, + bool is_double) +{ + TCGv t0, t1; + TCGLabel *l1, *l2; + + if (rd == 0) { + /* Treat as NOP. */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + l1 = gen_new_label(); + l2 = gen_new_label(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if (!is_double) { + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + } + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); + if (!is_double) { + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + } + gen_set_label(l2); + + return true; +} + +static bool trans_MODU_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, false); +} + +static bool trans_DMODU_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_MODU_G(s, a->rd, a->rs, a->rt, true); +} + +static bool gen_lext_MULT_G(DisasContext *s, int rd, int rs, int rt, + bool is_double) +{ + TCGv t0, t1; + + if (rd == 0) { + /* Treat as NOP. */ + return true; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); + if (!is_double) { + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + } + + return true; +} + +static bool trans_MULTu_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, false); +} + +static bool trans_DMULTu_G(DisasContext *s, arg_muldiv *a) +{ + return gen_lext_MULT_G(s, a->rd, a->rs, a->rt, true); +} + +bool decode_ext_loongson(DisasContext *ctx, uint32_t insn) +{ + if (!decode_64bit_enabled(ctx)) { + return false; + } + if ((ctx->insn_flags & INSN_LOONGSON2E) && decode_godson2(ctx, ctx->opcode)) { + return true; + } + if ((ctx->insn_flags & ASE_LEXT) && decode_loong_ext(ctx, ctx->opcode)) { + return true; + } + return false; +} diff --git a/target/mips/tcg/meson.build b/target/mips/tcg/meson.build index 7ee969ec8f..7b18e6c4c8 100644 --- a/target/mips/tcg/meson.build +++ b/target/mips/tcg/meson.build @@ -4,6 +4,9 @@ gen = [ 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'), + decodetree.process('godson2.decode', extra_args: ['--static-decode=decode_godson2']), + decodetree.process('loong-ext.decode', extra_args: ['--static-decode=decode_loong_ext']), ] mips_ss.add(gen) @@ -26,6 +29,8 @@ mips_ss.add(files( mips_ss.add(when: 'TARGET_MIPS64', if_true: files( 'tx79_translate.c', 'octeon_translate.c', + 'lcsr_translate.c', + 'loong_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 632895cc9e..c479bec108 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); } @@ -980,20 +977,22 @@ 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_endian(ctx) | MO_SL | + 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); + gen_op_addr_addi(ctx, t0, t0, 4); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL | + 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_movi_tl(t1, 4); - gen_op_addr_add(ctx, t0, t0, t1); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | + ctx->default_tcg_memop_mask); + gen_op_addr_addi(ctx, t0, t0, 4); 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_endian(ctx) | MO_UL | + ctx->default_tcg_memop_mask); break; #ifdef TARGET_MIPS64 case LDP: @@ -1001,25 +1000,25 @@ 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_endian(ctx) | MO_UQ | + 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); + gen_op_addr_addi(ctx, t0, t0, 8); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | + 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_movi_tl(t1, 8); - gen_op_addr_add(ctx, t0, t0, t1); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | + ctx->default_tcg_memop_mask); + gen_op_addr_addi(ctx, t0, t0, 8); 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_endian(ctx) | MO_UQ | + 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 +1066,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 +1267,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 +1282,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 +1290,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: @@ -2488,7 +2484,10 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) mips32_op = OPC_BC1TANY4; do_cp1mips3d: check_cop1x(ctx); - check_insn(ctx, ASE_MIPS3D); + if (!ase_3d_available(env)) { + gen_reserved_instruction(ctx); + break; + } /* Fall through */ do_cp1branch: if (env->CP0_Config1 & (1 << CP0C1_FP)) { @@ -2572,13 +2571,13 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) gen_st(ctx, mips32_op, rt, rs, offset); break; case SC: - gen_st_cond(ctx, rt, rs, offset, MO_TESL, false); + gen_st_cond(ctx, rt, rs, offset, mo_endian(ctx) | MO_SL, false); break; #if defined(TARGET_MIPS64) case SCD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_st_cond(ctx, rt, rs, offset, MO_TEUQ, false); + gen_st_cond(ctx, rt, rs, offset, mo_endian(ctx) | MO_UQ, false); break; #endif case LD_EVA: @@ -2659,7 +2658,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) mips32_op = OPC_SHE; goto do_st_lr; case SCE: - gen_st_cond(ctx, rt, rs, offset, MO_TESL, true); + gen_st_cond(ctx, rt, rs, offset, mo_endian(ctx) | MO_SL, true); break; case SWE: mips32_op = OPC_SWE; diff --git a/target/mips/tcg/mips16e_translate.c.inc b/target/mips/tcg/mips16e_translate.c.inc index 918b15d55c..a9af8f1e74 100644 --- a/target/mips/tcg/mips16e_translate.c.inc +++ b/target/mips/tcg/mips16e_translate.c.inc @@ -122,11 +122,21 @@ enum { static int xlat(int r) { - static int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; + static const int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; return map[r]; } +static void decr_and_store(DisasContext *ctx, unsigned regidx, TCGv t0) +{ + TCGv t1 = tcg_temp_new(); + + gen_op_addr_addi(ctx, t0, t0, -4); + gen_load_gpr(t1, regidx); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | + ctx->default_tcg_memop_mask); +} + static void gen_mips16_save(DisasContext *ctx, int xsregs, int aregs, int do_ra, int do_s0, int do_s1, @@ -134,7 +144,6 @@ static void gen_mips16_save(DisasContext *ctx, { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); int args, astatic; switch (aregs) { @@ -172,65 +181,62 @@ 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_endian(ctx) | MO_UL | + 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_endian(ctx) | MO_UL | + 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_endian(ctx) | MO_UL | + 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_endian(ctx) | MO_UL | + ctx->default_tcg_memop_mask); } gen_load_gpr(t0, 29); -#define DECR_AND_STORE(reg) do { \ - 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); \ - } while (0) - if (do_ra) { - DECR_AND_STORE(31); + decr_and_store(ctx, 31, t0); } switch (xsregs) { case 7: - DECR_AND_STORE(30); + decr_and_store(ctx, 30, t0); /* Fall through */ case 6: - DECR_AND_STORE(23); + decr_and_store(ctx, 23, t0); /* Fall through */ case 5: - DECR_AND_STORE(22); + decr_and_store(ctx, 22, t0); /* Fall through */ case 4: - DECR_AND_STORE(21); + decr_and_store(ctx, 21, t0); /* Fall through */ case 3: - DECR_AND_STORE(20); + decr_and_store(ctx, 20, t0); /* Fall through */ case 2: - DECR_AND_STORE(19); + decr_and_store(ctx, 19, t0); /* Fall through */ case 1: - DECR_AND_STORE(18); + decr_and_store(ctx, 18, t0); } if (do_s1) { - DECR_AND_STORE(17); + decr_and_store(ctx, 17, t0); } if (do_s0) { - DECR_AND_STORE(16); + decr_and_store(ctx, 16, t0); } switch (aregs) { @@ -265,24 +271,31 @@ static void gen_mips16_save(DisasContext *ctx, } if (astatic > 0) { - DECR_AND_STORE(7); + decr_and_store(ctx, 7, t0); if (astatic > 1) { - DECR_AND_STORE(6); + decr_and_store(ctx, 6, t0); if (astatic > 2) { - DECR_AND_STORE(5); + decr_and_store(ctx, 5, t0); if (astatic > 3) { - DECR_AND_STORE(4); + decr_and_store(ctx, 4, t0); } } } } -#undef DECR_AND_STORE - 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); + gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], -framesize); +} + +static void decr_and_load(DisasContext *ctx, unsigned regidx, TCGv t0) +{ + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + 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_TE | MO_SL | + ctx->default_tcg_memop_mask); + gen_store_gpr(t1, regidx); } static void gen_mips16_restore(DisasContext *ctx, @@ -292,51 +305,41 @@ static void gen_mips16_restore(DisasContext *ctx, { int astatic; TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - tcg_gen_movi_tl(t2, framesize); - gen_op_addr_add(ctx, t0, cpu_gpr[29], t2); - -#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); \ - gen_store_gpr(t1, reg); \ - } while (0) + gen_op_addr_addi(ctx, t0, cpu_gpr[29], -framesize); if (do_ra) { - DECR_AND_LOAD(31); + decr_and_load(ctx, 31, t0); } switch (xsregs) { case 7: - DECR_AND_LOAD(30); + decr_and_load(ctx, 30, t0); /* Fall through */ case 6: - DECR_AND_LOAD(23); + decr_and_load(ctx, 23, t0); /* Fall through */ case 5: - DECR_AND_LOAD(22); + decr_and_load(ctx, 22, t0); /* Fall through */ case 4: - DECR_AND_LOAD(21); + decr_and_load(ctx, 21, t0); /* Fall through */ case 3: - DECR_AND_LOAD(20); + decr_and_load(ctx, 20, t0); /* Fall through */ case 2: - DECR_AND_LOAD(19); + decr_and_load(ctx, 19, t0); /* Fall through */ case 1: - DECR_AND_LOAD(18); + decr_and_load(ctx, 18, t0); } if (do_s1) { - DECR_AND_LOAD(17); + decr_and_load(ctx, 17, t0); } if (do_s0) { - DECR_AND_LOAD(16); + decr_and_load(ctx, 16, t0); } switch (aregs) { @@ -371,24 +374,19 @@ static void gen_mips16_restore(DisasContext *ctx, } if (astatic > 0) { - DECR_AND_LOAD(7); + decr_and_load(ctx, 7, t0); if (astatic > 1) { - DECR_AND_LOAD(6); + decr_and_load(ctx, 6, t0); if (astatic > 2) { - DECR_AND_LOAD(5); + decr_and_load(ctx, 5, t0); if (astatic > 3) { - DECR_AND_LOAD(4); + decr_and_load(ctx, 4, t0); } } } } -#undef DECR_AND_LOAD - 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); + gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], -framesize); } #if defined(TARGET_MIPS64) 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 736283e2af..1d40383ca4 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()); @@ -8210,14 +8211,6 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, /* Element-by-element access macros */ #define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) -#if !defined(CONFIG_USER_ONLY) -#define MEMOP_IDX(DF) \ - MemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN, \ - cpu_mmu_index(env, false)); -#else -#define MEMOP_IDX(DF) -#endif - #if TARGET_BIG_ENDIAN static inline uint64_t bswap16x4(uint64_t x) { @@ -8322,7 +8315,7 @@ 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); @@ -8336,7 +8329,7 @@ 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 +8350,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 +8371,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 1bcdbb1121..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); @@ -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), @@ -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), @@ -603,7 +593,7 @@ static bool trans_msa_elm_fn(DisasContext *ctx, arg_msa_elm_df *a, 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)); @@ -708,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; } @@ -728,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)); @@ -743,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)); @@ -780,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..35ebb0397d 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_SL | mo_endian_rev(ctx, reversed) | + 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_SL | mo_endian_rev(ctx, reversed) | + 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_SL | mo_endian_rev(ctx, reversed) | + 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_SL | mo_endian_rev(ctx, reversed) | + 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_endian(ctx) | MO_UL); + break; + case OPC_MXU_LXB: + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_SB); + break; + case OPC_MXU_LXH: + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_SW); + break; + case OPC_MXU_LXBU: + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | MO_UB); + break; + case OPC_MXU_LXHU: + gen_mxu_lxx(ctx, strd2, mo_endian(ctx) | 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 812c111e3c..1e274143bb 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -998,27 +998,24 @@ 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); - if (cpu_is_bigendian(ctx)) { + tcg_gen_qemu_ld_i64(tval, taddr, ctx->mem_idx, + mo_endian(ctx) | MO_UQ | MO_ALIGN); + if (disas_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,21 +1026,22 @@ 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); gen_load_gpr(tmp2, reg2); - if (cpu_is_bigendian(ctx)) { + if (disas_is_bigendian(ctx)) { tcg_gen_concat_tl_i64(tval, tmp2, tmp1); } else { 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); } @@ -1055,8 +1053,7 @@ static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, tcg_gen_movi_tl(cpu_gpr[reg1], 0); } 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(tcg_constant_tl(-1), tcg_env, offsetof(CPUMIPSState, lladdr)); } static void gen_adjust_sp(DisasContext *ctx, int u) @@ -1078,15 +1075,12 @@ static void gen_save(DisasContext *ctx, uint8_t rt, uint8_t count, gen_base_offset_addr(ctx, va, 29, this_offset); gen_load_gpr(t0, this_rt); tcg_gen_qemu_st_tl(t0, va, ctx->mem_idx, - (MO_TEUL | ctx->default_tcg_memop_mask)); + mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); counter++; } /* 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, @@ -1101,8 +1095,8 @@ static void gen_restore(DisasContext *ctx, uint8_t rt, uint8_t count, int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); int this_offset = u - ((counter + 1) << 2); gen_base_offset_addr(ctx, va, 29, this_offset); - tcg_gen_qemu_ld_tl(t0, va, ctx->mem_idx, MO_TESL | - ctx->default_tcg_memop_mask); + tcg_gen_qemu_ld_tl(t0, va, ctx->mem_idx, + mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); tcg_gen_ext32s_tl(t0, t0); gen_store_gpr(t0, this_rt); counter++; @@ -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); } @@ -1570,7 +1543,6 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, { int16_t imm; TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); TCGv v0_t = tcg_temp_new(); gen_load_gpr(v0_t, v1); @@ -1597,12 +1569,10 @@ 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 >> 3); - gen_helper_mthlip(t0, v0_t, cpu_env); + gen_helper_mthlip(tcg_constant_tl(v2 >> 3), 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(tcg_constant_tl(v2 >> 3), v0_t, tcg_env); break; default: gen_reserved_instruction(ctx); @@ -1614,39 +1584,34 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, imm = extract32(ctx->opcode, 14, 7); 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, tcg_constant_tl(imm), 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, tcg_constant_tl(imm), 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, tcg_constant_tl(v2 >> 3), + tcg_constant_tl(v1), 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, tcg_constant_tl(v2 >> 3), + tcg_constant_tl(v1), tcg_env); gen_store_gpr(t0, ret); break; } break; case NM_POOL32AXF_1_4: check_dsp(ctx); - 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, tcg_constant_tl(v2 >> 2), v0_t, tcg_env); gen_store_gpr(t0, ret); break; case NM_SHRL_QB: - gen_helper_shrl_qb(t0, t0, v0_t); + gen_helper_shrl_qb(t0, tcg_constant_tl(v2 >> 2), v0_t); gen_store_gpr(t0, ret); break; } @@ -1657,23 +1622,25 @@ static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, break; case NM_POOL32AXF_1_7: check_dsp(ctx); - tcg_gen_movi_tl(t0, v2 >> 3); - 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, tcg_constant_tl(v2 >> 3), + tcg_constant_tl(v1), 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, tcg_constant_tl(v2 >> 3), + tcg_constant_tl(v1), 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, tcg_constant_tl(v2 >> 3), + tcg_constant_tl(v1), 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, tcg_constant_tl(v2 >> 3), + tcg_constant_tl(v1), tcg_env); gen_store_gpr(t0, ret); break; } @@ -1682,10 +1649,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 +1665,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 +1688,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 +1711,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 +1738,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 +1765,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 +1816,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 +1837,12 @@ 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, tcg_constant_tl(rd >> 3), v1_t, tcg_env); gen_store_gpr(t0, ret); break; } @@ -1915,10 +1871,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 +1892,11 @@ 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, tcg_constant_tl(rd >> 3), v1_t, tcg_env); gen_store_gpr(t0, ret); break; default: @@ -1964,8 +1915,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, break; 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, tcg_constant_tl(rd >> 3), v1_t, tcg_env); gen_store_gpr(t0, ret); break; case NM_MSUB: @@ -1982,16 +1932,13 @@ 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, tcg_constant_tl(rd >> 3), v1_t, tcg_env); gen_store_gpr(t0, ret); break; } @@ -2007,8 +1954,7 @@ static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, break; 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, tcg_constant_tl(rd >> 3), v1_t, tcg_env); gen_store_gpr(t0, ret); break; case NM_MSUBU: @@ -2027,16 +1973,13 @@ 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, v1_t, cpu_env); + gen_helper_extr_s_h(t0, tcg_constant_tl(rd >> 3), v1_t, tcg_env); gen_store_gpr(t0, ret); break; } @@ -2045,12 +1988,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 +2002,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 +2097,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 +2124,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, @@ -2204,24 +2137,22 @@ static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, switch (opc) { case NM_SHRA_R_QB: check_dsp_r2(ctx); - tcg_gen_movi_tl(t0, rd >> 2); switch (extract32(ctx->opcode, 12, 1)) { case 0: /* NM_SHRA_QB */ - gen_helper_shra_qb(t0, t0, rs_t); + gen_helper_shra_qb(t0, tcg_constant_tl(rd >> 2), rs_t); gen_store_gpr(t0, rt); break; case 1: /* NM_SHRA_R_QB */ - gen_helper_shra_r_qb(t0, t0, rs_t); + gen_helper_shra_r_qb(t0, tcg_constant_tl(rd >> 2), rs_t); gen_store_gpr(t0, rt); break; } break; case NM_SHRL_PH: check_dsp_r2(ctx); - tcg_gen_movi_tl(t0, rd >> 1); - gen_helper_shrl_ph(t0, t0, rs_t); + gen_helper_shrl_ph(t0, tcg_constant_tl(rd >> 1), rs_t); gen_store_gpr(t0, rt); break; case NM_REPL_QB: @@ -2235,16 +2166,13 @@ static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, (uint32_t)imm << 8 | (uint32_t)imm; result = (int32_t)result; - tcg_gen_movi_tl(t0, result); - gen_store_gpr(t0, rt); + gen_store_gpr(tcg_constant_tl(result), rt); } break; default: gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); - tcg_temp_free(rs_t); } @@ -2300,11 +2228,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 +2240,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: @@ -2361,10 +2287,9 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, { TCGCond cond = TCG_COND_ALWAYS; TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); + TCGv timm = tcg_constant_tl(imm); gen_load_gpr(t0, rt); - tcg_gen_movi_tl(t1, imm); ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); /* Load needed operands and calculate btarget */ @@ -2374,7 +2299,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,16 +2309,16 @@ 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); - tcg_gen_movi_tl(t1, 0); + timm = tcg_constant_tl(0); if (opc == NM_BBEQZC) { cond = TCG_COND_EQ; } else { @@ -2404,7 +2329,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 +2359,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 */ @@ -2448,17 +2373,13 @@ static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, /* Conditional compact branch */ TCGLabel *fs = gen_new_label(); - tcg_gen_brcond_tl(tcg_invert_cond(cond), t0, t1, fs); + tcg_gen_brcond_tl(tcg_invert_cond(cond), t0, timm, fs); gen_goto_tb(ctx, 1, ctx->btarget); gen_set_label(fs); 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 */ @@ -2466,7 +2387,6 @@ static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs, int rt) { TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); /* load rs */ gen_load_gpr(t0, rs); @@ -2478,8 +2398,7 @@ static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs, /* calculate btarget */ tcg_gen_shli_tl(t0, t0, 1); - tcg_gen_movi_tl(t1, ctx->base.pc_next + 4); - gen_op_addr_add(ctx, btarget, t1, t0); + gen_op_addr_add(ctx, btarget, tcg_constant_tl(ctx->base.pc_next + 4), t0); /* branch completion */ clear_branch_hflags(ctx); @@ -2488,9 +2407,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 */ @@ -2535,19 +2451,15 @@ static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, } else { /* OPC_JIC, OPC_JIALC */ TCGv tbase = tcg_temp_new(); - TCGv toffset = tcg_temp_new(); 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); + gen_op_addr_addi(ctx, btarget, tbase, offset); } break; default: MIPS_INVAL("Compact branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } if (bcond_compute == 0) { @@ -2559,7 +2471,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 +2532,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 +2545,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 +2572,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); } @@ -2709,59 +2614,56 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) break; default: gen_reserved_instruction(ctx); - goto out; + 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_endian(ctx) | MO_SW | 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_endian(ctx) | MO_SL | 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_endian(ctx) | MO_UW | 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_endian(ctx) | MO_UW | 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_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); break; case NM_LWC1X: /*case NM_LWC1XS:*/ @@ -2799,10 +2701,6 @@ static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) gen_reserved_instruction(ctx); break; } - -out: - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_pool32f_nanomips_insn(DisasContext *ctx) @@ -3118,27 +3016,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); @@ -3180,32 +3078,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: @@ -3213,12 +3111,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; } @@ -3258,12 +3156,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; } @@ -3273,12 +3171,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; } @@ -3333,12 +3231,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; } @@ -3378,12 +3276,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; } @@ -3393,12 +3291,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; } @@ -3423,12 +3321,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; } @@ -3439,53 +3337,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: @@ -3518,36 +3414,31 @@ 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: check_dsp(ctx); { - TCGv tv0 = tcg_temp_new(); - TCGv tv1 = tcg_temp_new(); int16_t imm = extract32(ctx->opcode, 16, 7); - 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(tcg_constant_tl(rd >> 3), + tcg_constant_tl(imm), 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: @@ -3555,12 +3446,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; } @@ -3582,18 +3473,17 @@ 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: check_dsp(ctx); - tcg_gen_movi_tl(t0, rd); - gen_helper_shra_r_w(v1_t, t0, v1_t); + gen_helper_shra_r_w(v1_t, tcg_constant_tl(rd), v1_t); gen_store_gpr(v1_t, rt); break; case NM_SHRA_R_PH: @@ -3618,12 +3508,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: @@ -3633,8 +3523,7 @@ static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, break; 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, tcg_constant_tl(rd), v1_t, tcg_env); gen_store_gpr(v1_t, rt); break; case NM_REPL_PH: @@ -3652,10 +3541,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) @@ -3819,34 +3704,29 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) case NM_LWPC48: check_nms(ctx); if (rt != 0) { - TCGv t0; - t0 = tcg_temp_new(); - target_long addr = addr_add(ctx, ctx->base.pc_next + 6, 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], tcg_constant_tl(addr), + ctx->mem_idx, + mo_endian(ctx) | MO_SL + | ctx->default_tcg_memop_mask); } break; case NM_SWPC48: check_nms(ctx); { - TCGv t0, t1; - t0 = tcg_temp_new(); + TCGv t1; t1 = tcg_temp_new(); target_long addr = addr_add(ctx, ctx->base.pc_next + 6, addr_off); - 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, tcg_constant_tl(addr), ctx->mem_idx, + mo_endian(ctx) | MO_UL + | ctx->default_tcg_memop_mask); } break; default: @@ -3908,8 +3788,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: @@ -3958,18 +3836,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: @@ -4229,18 +4104,16 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) switch (extract32(ctx->opcode, 11, 4)) { case NM_UALH: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | - MO_UNALN); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + mo_endian(ctx) | MO_SW | MO_UNALN); gen_store_gpr(t0, rt); break; case NM_UASH: gen_load_gpr(t1, rt); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | - MO_UNALN); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + mo_endian(ctx) | MO_UW | MO_UNALN); break; } - tcg_temp_free(t0); - tcg_temp_free(t1); } break; case NM_P_LL: @@ -4260,7 +4133,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) case NM_P_SC: switch (ctx->opcode & 0x03) { case NM_SC: - gen_st_cond(ctx, rt, rs, s, MO_TESL, false); + gen_st_cond(ctx, rt, rs, s, mo_endian(ctx) | MO_SL, + false); break; case NM_SCWP: check_xnp(ctx); @@ -4373,7 +4247,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) check_xnp(ctx); check_eva(ctx); check_cp0_enabled(ctx); - gen_st_cond(ctx, rt, rs, s, MO_TESL, true); + gen_st_cond(ctx, rt, rs, s, mo_endian(ctx) | MO_SL, + true); break; case NM_SCWPE: check_xnp(ctx); @@ -4404,7 +4279,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) { @@ -4416,7 +4291,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) switch (extract32(ctx->opcode, 11, 1)) { case NM_LWM: tcg_gen_qemu_ld_tl(t1, va, ctx->mem_idx, - memop | MO_TESL); + memop | mo_endian(ctx) | MO_SL); gen_store_gpr(t1, this_rt); if ((this_rt == rs) && (counter != (count - 1))) { @@ -4427,13 +4302,11 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) this_rt = (rt == 0) ? 0 : this_rt; gen_load_gpr(t1, this_rt); tcg_gen_qemu_st_tl(t1, va, ctx->mem_idx, - memop | MO_TEUL); + memop | mo_endian(ctx) | MO_UL); break; } counter++; } - tcg_temp_free(va); - tcg_temp_free(t1); } break; default: @@ -4454,7 +4327,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: @@ -4509,8 +4381,8 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) 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); @@ -4604,9 +4476,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; } @@ -4738,7 +4609,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); @@ -4941,8 +4812,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_translate.c b/target/mips/tcg/octeon_translate.c index 6a207d2e7e..e25c4cbaa0 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -7,10 +7,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-octeon.c.inc" @@ -40,8 +38,6 @@ static bool trans_BBIT(DisasContext *ctx, arg_BBIT *a) ctx->hflags |= MIPS_HFLAG_BC; ctx->btarget = ctx->base.pc_next + 4 + a->offset * 4; ctx->hflags |= MIPS_HFLAG_BDS32; - - tcg_temp_free(t0); return true; } @@ -61,10 +57,6 @@ static bool trans_BADDU(DisasContext *ctx, arg_BADDU *a) tcg_gen_add_tl(t0, t0, t1); tcg_gen_andi_i64(cpu_gpr[a->rd], t0, 0xff); - - tcg_temp_free(t0); - tcg_temp_free(t1); - return true; } @@ -83,10 +75,6 @@ static bool trans_DMUL(DisasContext *ctx, arg_DMUL *a) gen_load_gpr(t1, a->rt); tcg_gen_mul_i64(cpu_gpr[a->rd], t0, t1); - - tcg_temp_free(t0); - tcg_temp_free(t1); - return true; } @@ -103,8 +91,6 @@ static bool trans_EXTS(DisasContext *ctx, arg_EXTS *a) gen_load_gpr(t0, a->rs); tcg_gen_sextract_tl(t0, t0, a->p, a->lenm1 + 1); gen_store_gpr(t0, a->rt); - tcg_temp_free(t0); - return true; } @@ -121,8 +107,6 @@ static bool trans_CINS(DisasContext *ctx, arg_CINS *a) gen_load_gpr(t0, a->rs); tcg_gen_deposit_z_tl(t0, t0, a->p, a->lenm1 + 1); gen_store_gpr(t0, a->rt); - tcg_temp_free(t0); - return true; } @@ -142,8 +126,6 @@ static bool trans_POP(DisasContext *ctx, arg_POP *a) } tcg_gen_ctpop_tl(t0, t0); gen_store_gpr(t0, a->rd); - tcg_temp_free(t0); - return true; } @@ -167,10 +149,6 @@ static bool trans_SEQNE(DisasContext *ctx, arg_SEQNE *a) } else { tcg_gen_setcond_tl(TCG_COND_EQ, cpu_gpr[a->rd], t1, t0); } - - tcg_temp_free(t0); - tcg_temp_free(t1); - return true; } @@ -194,8 +172,5 @@ static bool trans_SEQNEI(DisasContext *ctx, arg_SEQNEI *a) } else { tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_gpr[a->rt], t0, imm); } - - tcg_temp_free(t0); - 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 5da1124589..79a5c833ce 100644 --- a/target/mips/tcg/sysemu/cp0_helper.c +++ b/target/mips/tcg/sysemu/cp0_helper.c @@ -58,9 +58,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 +370,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 +497,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 +1076,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 +1138,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; 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..911341ac37 100644 --- a/target/mips/tcg/sysemu/meson.build +++ b/target/mips/tcg/sysemu/meson.build @@ -1,6 +1,12 @@ -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: ['CONFIG_SEMIHOSTING'], + if_true: files('mips-semi.c'), + if_false: files('semihosting-stub.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 85f0567a7f..5ba06e9573 100644 --- a/target/mips/tcg/sysemu/mips-semi.c +++ b/target/mips/tcg/sysemu/mips-semi.c @@ -20,8 +20,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/log.h" -#include "exec/gdbstub.h" -#include "semihosting/softmmu-uaccess.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" @@ -125,7 +126,7 @@ static void report_fault(CPUMIPSState *env) static void uhi_cb(CPUState *cs, uint64_t ret, int err) { - CPUMIPSState *env = cs->env_ptr; + CPUMIPSState *env = cpu_env(cs); #define E(N) case E##N: err = UHI_E##N; break @@ -166,7 +167,7 @@ static void uhi_fstat_cb(CPUState *cs, uint64_t ret, int err) QEMU_BUILD_BUG_ON(sizeof(UHIStat) < sizeof(struct gdb_stat)); if (!err) { - CPUMIPSState *env = cs->env_ptr; + 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; diff --git a/target/mips/tcg/sysemu/semihosting-stub.c b/target/mips/tcg/sysemu/semihosting-stub.c new file mode 100644 index 0000000000..7ae27d746f --- /dev/null +++ b/target/mips/tcg/sysemu/semihosting-stub.c @@ -0,0 +1,15 @@ +/* + * MIPS semihosting stub + * + * SPDX-FileContributor: Philippe Mathieu-Daudé + * SPDX-FileCopyrightText: 2024 Linaro Ltd. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "internal.h" + +void mips_semihosting(CPUMIPSState *env) +{ + g_assert_not_reached(); +} diff --git a/target/mips/tcg/sysemu/special_helper.c b/target/mips/tcg/sysemu/special_helper.c index 3c5f35c759..9ce5e2ceac 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(tb)) { + && !tcg_cflags_has(cs, 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 9d16859c0a..e98bb95951 100644 --- a/target/mips/tcg/sysemu/tlb_helper.c +++ b/target/mips/tcg/sysemu/tlb_helper.c @@ -22,9 +22,9 @@ #include "cpu.h" #include "internal.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "exec/log.h" -#include "hw/mips/cpudevs.h" #include "exec/helper-proto.h" /* TLB management */ @@ -592,23 +592,29 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, * resulting in a TLB or XTLB Refill exception. */ -static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, - uint64_t *pte) +static bool get_pte(CPUMIPSState *env, uint64_t vaddr, MemOp op, + uint64_t *pte, unsigned ptw_mmu_idx) { - if ((vaddr & ((entry_size >> 3) - 1)) != 0) { + MemOpIdx oi; + + if ((vaddr & (memop_size(op) - 1)) != 0) { return false; } - if (entry_size == 64) { - *pte = cpu_ldq_code(env, vaddr); + + oi = make_memop_idx(op | mo_endian_env(env), ptw_mmu_idx); + if (op == MO_64) { + *pte = cpu_ldq_mmu(env, vaddr, oi, 0); } else { - *pte = cpu_ldl_code(env, vaddr); + *pte = cpu_ldl_mmu(env, vaddr, oi, 0); } + return true; } static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, - int entry_size, int ptei) + MemOp op, int ptei) { + unsigned entry_size = memop_size(op) << 3; uint64_t result = entry; uint64_t rixi; if (ptei > entry_size) { @@ -623,20 +629,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, + MemOp directory_mop, MemOp leaf_mop, 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; uint64_t paddr; int prot; @@ -644,19 +643,18 @@ 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; } - if (!get_pte(env, *vaddr, direntry_size, &entry)) { + if (!get_pte(env, *vaddr, directory_mop, &entry, ptw_mmu_idx)) { return 0; } if ((entry & (1 << psn)) && hugepg) { *huge_page = true; *hgpg_directory_hit = true; - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); + entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); w = directory_index - 1; if (directory_index & 0x1) { /* Generate adjacent page from same PTE for odd TLB page */ @@ -664,7 +662,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, *pw_entrylo0 = entry & ~lsb; /* even page */ *pw_entrylo1 = entry | lsb; /* odd page */ } else if (dph) { - int oddpagebit = 1 << leaf_shift; + int oddpagebit = 1 << leaf_mop; uint64_t vaddr2 = *vaddr ^ oddpagebit; if (*vaddr & oddpagebit) { *pw_entrylo1 = entry; @@ -672,14 +670,13 @@ 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)) { + if (!get_pte(env, vaddr2, leaf_mop, &entry, ptw_mmu_idx)) { return 0; } - entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); + entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); if (*vaddr & oddpagebit) { *pw_entrylo0 = entry; } else { @@ -696,7 +693,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; @@ -718,7 +715,7 @@ static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, /* Native pointer size */ /*For the 32-bit architectures, this bit is fixed to 0.*/ - int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; + MemOp native_op = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? MO_32 : MO_64; /* Indices from PWField */ int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; @@ -735,21 +732,10 @@ 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; + MemOp directory_mop, leaf_mop; /* 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; /* Starting address - Page Table Base */ uint64_t vaddr = env->CP0_PWBase; @@ -771,15 +757,26 @@ 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_mop = (hugepg && (ptew == 1)) ? native_op + 1 : native_op; + leaf_mop = (ptew == 1) ? native_op + 1 : native_op; + + goffset = gindex << directory_mop; + uoffset = uindex << directory_mop; + moffset = mindex << directory_mop; + ptoffset0 = (ptindex >> 1) << (leaf_mop + 1); + ptoffset1 = ptoffset0 | (1 << (leaf_mop)); + /* 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_mop, leaf_mop, ptw_mmu_idx)) { case 0: return false; @@ -795,7 +792,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_mop, leaf_mop, ptw_mmu_idx)) { case 0: return false; @@ -811,7 +809,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_mop, leaf_mop, ptw_mmu_idx)) { case 0: return false; @@ -826,27 +825,25 @@ 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)) { + if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { return false; } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); + dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); pw_entrylo0 = dir_entry; /* 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)) { + if (!get_pte(env, vaddr, leaf_mop, &dir_entry, ptw_mmu_idx)) { return false; } - dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); + dir_entry = get_tlb_entry_layout(env, dir_entry, leaf_mop, pf_ptew); pw_entrylo1 = dir_entry; refill: @@ -911,8 +908,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 +920,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 +941,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 +974,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; } @@ -1347,8 +1341,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 af585b5d9c..1861d538de 100644 --- a/target/mips/tcg/sysemu_helper.h.inc +++ b/target/mips/tcg/sysemu_helper.h.inc @@ -31,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) @@ -57,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) @@ -103,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) @@ -181,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/translate.c b/target/mips/tcg/translate.c index 624e6b7786..de7045874d 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -23,20 +23,18 @@ */ #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 "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. @@ -329,19 +327,6 @@ enum { OPC_MUL = 0x02 | OPC_SPECIAL2, OPC_MSUB = 0x04 | OPC_SPECIAL2, OPC_MSUBU = 0x05 | OPC_SPECIAL2, - /* Loongson 2F */ - OPC_MULT_G_2F = 0x10 | OPC_SPECIAL2, - OPC_DMULT_G_2F = 0x11 | OPC_SPECIAL2, - OPC_MULTU_G_2F = 0x12 | OPC_SPECIAL2, - OPC_DMULTU_G_2F = 0x13 | OPC_SPECIAL2, - OPC_DIV_G_2F = 0x14 | OPC_SPECIAL2, - OPC_DDIV_G_2F = 0x15 | OPC_SPECIAL2, - OPC_DIVU_G_2F = 0x16 | OPC_SPECIAL2, - OPC_DDIVU_G_2F = 0x17 | OPC_SPECIAL2, - OPC_MOD_G_2F = 0x1c | OPC_SPECIAL2, - OPC_DMOD_G_2F = 0x1d | OPC_SPECIAL2, - OPC_MODU_G_2F = 0x1e | OPC_SPECIAL2, - OPC_DMODU_G_2F = 0x1f | OPC_SPECIAL2, /* Misc */ OPC_CLZ = 0x20 | OPC_SPECIAL2, OPC_CLO = 0x21 | OPC_SPECIAL2, @@ -370,20 +355,6 @@ enum { OPC_RDHWR = 0x3B | OPC_SPECIAL3, OPC_GINV = 0x3D | OPC_SPECIAL3, - /* Loongson 2E */ - OPC_MULT_G_2E = 0x18 | OPC_SPECIAL3, - OPC_MULTU_G_2E = 0x19 | OPC_SPECIAL3, - OPC_DIV_G_2E = 0x1A | OPC_SPECIAL3, - OPC_DIVU_G_2E = 0x1B | OPC_SPECIAL3, - OPC_DMULT_G_2E = 0x1C | OPC_SPECIAL3, - OPC_DMULTU_G_2E = 0x1D | OPC_SPECIAL3, - OPC_DDIV_G_2E = 0x1E | OPC_SPECIAL3, - OPC_DDIVU_G_2E = 0x1F | OPC_SPECIAL3, - OPC_MOD_G_2E = 0x22 | OPC_SPECIAL3, - OPC_MODU_G_2E = 0x23 | OPC_SPECIAL3, - OPC_DMOD_G_2E = 0x26 | OPC_SPECIAL3, - OPC_DMODU_G_2E = 0x27 | OPC_SPECIAL3, - /* MIPS DSP Load */ OPC_LX_DSP = 0x0A | OPC_SPECIAL3, /* MIPS DSP Arithmetic */ @@ -391,16 +362,14 @@ enum { OPC_ADDU_OB_DSP = 0x14 | OPC_SPECIAL3, OPC_ABSQ_S_PH_DSP = 0x12 | OPC_SPECIAL3, OPC_ABSQ_S_QH_DSP = 0x16 | OPC_SPECIAL3, - /* OPC_ADDUH_QB_DSP is same as OPC_MULT_G_2E. */ - /* OPC_ADDUH_QB_DSP = 0x18 | OPC_SPECIAL3, */ + OPC_ADDUH_QB_DSP = 0x18 | OPC_SPECIAL3, OPC_CMPU_EQ_QB_DSP = 0x11 | OPC_SPECIAL3, OPC_CMPU_EQ_OB_DSP = 0x15 | OPC_SPECIAL3, /* MIPS DSP GPR-Based Shift Sub-class */ OPC_SHLL_QB_DSP = 0x13 | OPC_SPECIAL3, OPC_SHLL_OB_DSP = 0x17 | OPC_SPECIAL3, /* MIPS DSP Multiply Sub-class insns */ - /* OPC_MUL_PH_DSP is same as OPC_ADDUH_QB_DSP. */ - /* OPC_MUL_PH_DSP = 0x18 | OPC_SPECIAL3, */ + OPC_MUL_PH_DSP = 0x18 | OPC_SPECIAL3, OPC_DPA_W_PH_DSP = 0x30 | OPC_SPECIAL3, OPC_DPAQ_W_QH_DSP = 0x34 | OPC_SPECIAL3, /* DSP Bit/Manipulation Sub-class */ @@ -558,7 +527,6 @@ enum { OPC_MULQ_S_PH = (0x1E << 6) | OPC_ADDU_QB_DSP, }; -#define OPC_ADDUH_QB_DSP OPC_MULT_G_2E #define MASK_ADDUH_QB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) enum { /* MIPS DSP Arithmetic Sub-class */ @@ -1211,8 +1179,6 @@ static TCGv_i32 hflags; TCGv_i32 fpu_fcr0, fpu_fcr31; TCGv_i64 fpu_f64[32]; -#include "exec/gen-icount.h" - static const char regnames_HI[][4] = { "HI0", "HI1", "HI2", "HI3", }; @@ -1224,6 +1190,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 { @@ -1233,6 +1200,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); } @@ -1241,6 +1209,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 { @@ -1250,6 +1219,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); } @@ -1267,19 +1237,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) @@ -1290,17 +1257,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); } } @@ -1349,14 +1313,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) @@ -1368,7 +1332,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); @@ -1397,7 +1361,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) @@ -1415,7 +1378,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); } @@ -1440,7 +1402,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); } } @@ -1465,8 +1426,7 @@ void gen_op_addr_add(DisasContext *ctx, TCGv ret, TCGv arg0, TCGv arg1) #endif } -static inline void gen_op_addr_addi(DisasContext *ctx, TCGv ret, TCGv base, - target_long ofs) +void gen_op_addr_addi(DisasContext *ctx, TCGv ret, TCGv base, target_long ofs) { tcg_gen_addi_tl(ret, base, ofs); @@ -1655,13 +1615,18 @@ static inline void check_ps(DisasContext *ctx) check_cp1_64bitmode(ctx); } +bool decode_64bit_enabled(DisasContext *ctx) +{ + return ctx->hflags & MIPS_HFLAG_64; +} + /* * This code generates a "reserved instruction" exception if cpu is not * 64-bit or 64-bit instructions are not enabled. */ void check_mips_64(DisasContext *ctx) { - if (unlikely((TARGET_LONG_BITS != 64) || !(ctx->hflags & MIPS_HFLAG_64))) { + if (unlikely((TARGET_LONG_BITS != 64) || !decode_64bit_enabled(ctx))) { gen_reserved_instruction(ctx); } } @@ -1853,8 +1818,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) @@ -1878,77 +1841,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)) @@ -1959,28 +1920,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) \ +#define OP_LD_ATOMIC(insn, ignored_memop) \ 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_endian(ctx) | MO_SL); #if defined(TARGET_MIPS64) -OP_LD_ATOMIC(lld, ld64); +OP_LD_ATOMIC(lld, mo_endian(ctx) | MO_UQ); #endif #undef OP_LD_ATOMIC @@ -2010,11 +1970,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 (!disas_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 (disas_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 | @@ -2033,12 +2047,12 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, switch (opc) { #if defined(TARGET_MIPS64) case OPC_LWU: - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUL | + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; case OPC_LD: - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -2049,73 +2063,34 @@ 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_endian(ctx) | MO_UQ); + 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_endian(ctx) | MO_UQ); + 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); + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_UQ); 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); + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_SL); gen_store_gpr(t0, rt); break; case OPC_LWE: mem_idx = MIPS_HFLAG_UM; /* fall through */ case OPC_LW: - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TESL | + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -2123,7 +2098,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, mem_idx = MIPS_HFLAG_UM; /* fall through */ case OPC_LH: - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TESW | + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_SW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -2131,7 +2106,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, mem_idx = MIPS_HFLAG_UM; /* fall through */ case OPC_LHU: - tcg_gen_qemu_ld_tl(t0, t0, mem_idx, MO_TEUW | + tcg_gen_qemu_ld_tl(t0, t0, mem_idx, mo_endian(ctx) | MO_UW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -2154,57 +2129,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_endian(ctx) | MO_UL); + 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_endian(ctx) | MO_UL); + tcg_gen_ext32s_tl(t1, t1); + gen_store_gpr(t1, rt); break; case OPC_LLE: mem_idx = MIPS_HFLAG_UM; @@ -2215,7 +2153,6 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rt); break; } - tcg_temp_free(t0); } /* Store */ @@ -2231,7 +2168,7 @@ static void gen_st(DisasContext *ctx, uint32_t opc, int rt, switch (opc) { #if defined(TARGET_MIPS64) case OPC_SD: - tcg_gen_qemu_st_tl(t1, t0, mem_idx, MO_TEUQ | + tcg_gen_qemu_st_tl(t1, t0, mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); break; case OPC_SDL: @@ -2245,14 +2182,14 @@ static void gen_st(DisasContext *ctx, uint32_t opc, int rt, mem_idx = MIPS_HFLAG_UM; /* fall through */ case OPC_SW: - tcg_gen_qemu_st_tl(t1, t0, mem_idx, MO_TEUL | + tcg_gen_qemu_st_tl(t1, t0, mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); break; case OPC_SHE: mem_idx = MIPS_HFLAG_UM; /* fall through */ case OPC_SH: - tcg_gen_qemu_st_tl(t1, t0, mem_idx, MO_TEUW | + tcg_gen_qemu_st_tl(t1, t0, mem_idx, mo_endian(ctx) | MO_UW | ctx->default_tcg_memop_mask); break; case OPC_SBE: @@ -2274,8 +2211,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); } @@ -2292,9 +2227,7 @@ 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); + gen_store_gpr(tcg_constant_tl(0), rt); tcg_gen_br(done); gen_set_label(l1); @@ -2305,10 +2238,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 */ @@ -2323,37 +2254,33 @@ static void gen_flt_ldst(DisasContext *ctx, uint32_t opc, int ft, case OPC_LWC1: { TCGv_i32 fp0 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL | + tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_fpr32(ctx, fp0, ft); - tcg_temp_free_i32(fp0); } break; case OPC_SWC1: { TCGv_i32 fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, ft); - tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL | + tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); - tcg_temp_free_i32(fp0); } break; case OPC_LDC1: { TCGv_i64 fp0 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_store_fpr64(ctx, fp0, ft); - tcg_temp_free_i64(fp0); } break; case OPC_SDC1: { TCGv_i64 fp0 = tcg_temp_new_i64(); gen_load_fpr64(ctx, fp0, ft); - tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); - tcg_temp_free_i64(fp0); } break; default: @@ -2382,7 +2309,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 */ @@ -2401,7 +2327,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(); @@ -2413,15 +2339,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: @@ -2435,7 +2358,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(); @@ -2446,14 +2369,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: @@ -2536,7 +2456,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 */ @@ -2576,7 +2495,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); } @@ -2612,7 +2530,6 @@ static void gen_shift_imm(DisasContext *ctx, uint32_t opc, break; #endif } - tcg_temp_free(t0); } /* Arithmetic */ @@ -2631,7 +2548,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(); @@ -2643,14 +2560,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: @@ -2667,7 +2581,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(); @@ -2679,9 +2593,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 @@ -2689,7 +2601,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: @@ -2708,7 +2619,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(); @@ -2719,14 +2630,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: @@ -2742,7 +2650,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(); @@ -2753,9 +2661,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. @@ -2763,7 +2669,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: @@ -2802,7 +2707,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) { @@ -2819,9 +2724,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 */ @@ -2900,8 +2802,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 */ @@ -2948,8 +2848,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) @@ -2971,8 +2869,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 */ @@ -3042,10 +2938,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, @@ -3065,14 +2960,14 @@ static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, case R6_OPC_LWPC: offset = sextract32(ctx->opcode << 2, 0, 21); addr = addr_add(ctx, pc, offset); - gen_r6_ld(addr, rs, ctx->mem_idx, MO_TESL); + gen_r6_ld(addr, rs, ctx->mem_idx, mo_endian(ctx) | MO_SL); break; #if defined(TARGET_MIPS64) case OPC_LWUPC: check_mips_64(ctx); offset = sextract32(ctx->opcode << 2, 0, 21); addr = addr_add(ctx, pc, offset); - gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEUL); + gen_r6_ld(addr, rs, ctx->mem_idx, mo_endian(ctx) | MO_UL); break; #endif default: @@ -3099,7 +2994,7 @@ static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, check_mips_64(ctx); offset = sextract32(ctx->opcode << 3, 0, 21); addr = addr_add(ctx, (pc & ~0x7), offset); - gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEUQ); + gen_r6_ld(addr, rs, ctx->mem_idx, mo_endian(ctx) | MO_UQ); break; #endif default: @@ -3138,12 +3033,9 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), 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: @@ -3157,38 +3049,29 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), 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); 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_movcond_tl(TCG_COND_EQ, t1, t1, + tcg_constant_tl(0), tcg_constant_tl(1), 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); 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_movcond_tl(TCG_COND_EQ, t1, t1, + tcg_constant_tl(0), tcg_constant_tl(1), 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: @@ -3199,8 +3082,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: @@ -3211,8 +3092,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: @@ -3223,8 +3102,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: @@ -3235,8 +3112,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) @@ -3249,11 +3124,8 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), t2, t1); tcg_gen_div_tl(cpu_gpr[rd], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DMOD: @@ -3265,31 +3137,22 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), 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); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, + tcg_constant_tl(0), tcg_constant_tl(1), 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); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, + tcg_constant_tl(0), tcg_constant_tl(1), t1); tcg_gen_remu_i64(cpu_gpr[rd], t0, t1); - tcg_temp_free(t3); - tcg_temp_free(t2); } break; case R6_OPC_DMUL: @@ -3299,7 +3162,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: @@ -3309,18 +3171,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) @@ -3346,20 +3204,17 @@ static void gen_div1_tx79(DisasContext *ctx, uint32_t opc, int rs, int rt) tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), t2, t1); tcg_gen_div_tl(cpu_LO[1], t0, t1); 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); @@ -3367,18 +3222,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 @@ -3409,20 +3259,17 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), t2, t1); tcg_gen_div_tl(cpu_LO[acc], t0, t1); 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); @@ -3430,8 +3277,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: @@ -3443,8 +3288,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: @@ -3456,8 +3299,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) @@ -3470,23 +3311,17 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); - tcg_gen_movi_tl(t3, 0); - tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, tcg_constant_tl(0), 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); - tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, + tcg_constant_tl(0), tcg_constant_tl(1), 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: @@ -3506,10 +3341,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: @@ -3524,10 +3357,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: @@ -3540,10 +3371,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: @@ -3558,20 +3387,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); } /* @@ -3626,8 +3450,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: @@ -3645,8 +3467,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: @@ -3662,13 +3482,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: @@ -3686,24 +3504,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, @@ -3749,205 +3561,6 @@ static void gen_cl(DisasContext *ctx, uint32_t opc, } } -/* Godson integer instructions */ -static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, - int rd, int rs, int rt) -{ - TCGv t0, t1; - - if (rd == 0) { - /* Treat as NOP. */ - 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; - } - - gen_load_gpr(t0, rs); - gen_load_gpr(t1, rt); - - switch (opc) { - case OPC_MULT_G_2E: - case OPC_MULT_G_2F: - tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - break; - case OPC_MULTU_G_2E: - case OPC_MULTU_G_2F: - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - break; - case OPC_DIV_G_2E: - case OPC_DIV_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGLabel *l3 = gen_new_label(); - tcg_gen_ext32s_tl(t0, t0); - tcg_gen_ext32s_tl(t1, t1); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l3); - gen_set_label(l1); - tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2); - tcg_gen_mov_tl(cpu_gpr[rd], t0); - tcg_gen_br(l3); - gen_set_label(l2); - tcg_gen_div_tl(cpu_gpr[rd], t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - gen_set_label(l3); - } - break; - case OPC_DIVU_G_2E: - case OPC_DIVU_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - gen_set_label(l2); - } - break; - case OPC_MOD_G_2E: - case OPC_MOD_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGLabel *l3 = gen_new_label(); - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); - tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l3); - gen_set_label(l2); - tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - gen_set_label(l3); - } - break; - case OPC_MODU_G_2E: - case OPC_MODU_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); - tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); - gen_set_label(l2); - } - break; -#if defined(TARGET_MIPS64) - case OPC_DMULT_G_2E: - case OPC_DMULT_G_2F: - tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); - break; - case OPC_DMULTU_G_2E: - case OPC_DMULTU_G_2F: - tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); - break; - case OPC_DDIV_G_2E: - case OPC_DDIV_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGLabel *l3 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l3); - gen_set_label(l1); - tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); - tcg_gen_mov_tl(cpu_gpr[rd], t0); - tcg_gen_br(l3); - gen_set_label(l2); - tcg_gen_div_tl(cpu_gpr[rd], t0, t1); - gen_set_label(l3); - } - break; - case OPC_DDIVU_G_2E: - case OPC_DDIVU_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); - gen_set_label(l2); - } - break; - case OPC_DMOD_G_2E: - case OPC_DMOD_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGLabel *l3 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); - tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l3); - gen_set_label(l2); - tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); - gen_set_label(l3); - } - break; - case OPC_DMODU_G_2E: - case OPC_DMODU_G_2F: - { - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rd], 0); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); - gen_set_label(l2); - } - break; -#endif - } - - tcg_temp_free(t0); - tcg_temp_free(t1); -} - /* Loongson multimedia instructions */ static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) { @@ -3956,21 +3569,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); @@ -4251,7 +3853,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); @@ -4272,7 +3873,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); @@ -4314,12 +3914,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); @@ -4327,16 +3923,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; @@ -4351,52 +3943,48 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, case OPC_GSLQ: t1 = tcg_temp_new(); gen_base_offset_addr(ctx, t0, rs, lsq_offset); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | 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); t1 = tcg_temp_new(); gen_base_offset_addr(ctx, t0, rs, lsq_offset); - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | 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(); gen_base_offset_addr(ctx, t0, rs, lsq_offset); gen_load_gpr(t1, rt); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8); gen_load_gpr(t1, lsq_rt1); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; case OPC_GSSQC1: check_cp1_enabled(ctx); t1 = tcg_temp_new(); gen_base_offset_addr(ctx, t0, rs, lsq_offset); gen_load_fpr64(ctx, t1, rt); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_base_offset_addr(ctx, t0, rs, lsq_offset + 8); gen_load_fpr64(ctx, t1, lsq_rt1); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #endif case OPC_GSSHFL: @@ -4404,109 +3992,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_endian(ctx) | MO_UL); + 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_endian(ctx) | MO_UL); + 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_endian(ctx) | MO_UQ); + 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_endian(ctx) | MO_UQ); + gen_store_fpr64(ctx, t1, rt); break; #endif default: @@ -4525,8 +4045,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); @@ -4536,8 +4054,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: @@ -4546,7 +4062,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); @@ -4554,7 +4069,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: @@ -4568,7 +4082,6 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt, gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); } /* Loongson EXT LDC2/SDC2 */ @@ -4630,7 +4143,7 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, gen_store_gpr(t0, rt); break; case OPC_GSLHX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SW | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -4639,7 +4152,7 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, if (rd) { gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0); } - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL | + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -4649,7 +4162,7 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, if (rd) { gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0); } - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_store_gpr(t0, rt); break; @@ -4660,10 +4173,9 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0); } fp0 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL | + tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL | ctx->default_tcg_memop_mask); gen_store_fpr32(ctx, fp0, rt); - tcg_temp_free_i32(fp0); break; #if defined(TARGET_MIPS64) case OPC_GSLDXC1: @@ -4671,7 +4183,7 @@ static void gen_loongson_lsdc2(DisasContext *ctx, int rt, if (rd) { gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0); } - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); gen_store_fpr64(ctx, t0, rt); break; @@ -4680,52 +4192,44 @@ 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 | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UW | 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 | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #if defined(TARGET_MIPS64) case OPC_GSSDX: t1 = tcg_temp_new(); gen_load_gpr(t1, rt); - tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #endif case OPC_GSSWXC1: fp0 = tcg_temp_new_i32(); gen_load_fpr32(ctx, fp0, rt); - tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL | + tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL | ctx->default_tcg_memop_mask); - tcg_temp_free_i32(fp0); break; #if defined(TARGET_MIPS64) case OPC_GSSDXC1: t1 = tcg_temp_new(); gen_load_fpr64(ctx, t1, rt); - tcg_gen_qemu_st_i64(t1, t0, ctx->mem_idx, MO_TEUQ | + tcg_gen_qemu_st_i64(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ | ctx->default_tcg_memop_mask); - tcg_temp_free(t1); break; #endif default: break; } - - tcg_temp_free(t0); } /* Traps */ @@ -4777,7 +4281,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); @@ -4822,7 +4326,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. */ @@ -4835,8 +4339,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) @@ -4865,8 +4367,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; @@ -4917,6 +4419,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) | @@ -5102,8 +4612,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); } @@ -5172,13 +4680,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) @@ -5196,15 +4700,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; @@ -5218,21 +4720,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); @@ -5242,18 +4742,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, @@ -5292,7 +4788,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) @@ -5303,10 +4798,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) @@ -5333,7 +4825,6 @@ static void gen_bitswap(DisasContext *ctx, int opc, int rd, int rt) break; #endif } - tcg_temp_free(t0); } #ifndef CONFIG_USER_ONLY @@ -5344,15 +4835,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) @@ -5361,49 +4850,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); } @@ -5412,8 +4896,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) \ @@ -5450,17 +4933,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: @@ -5470,7 +4942,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: @@ -5551,17 +5023,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: @@ -5575,7 +5036,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: @@ -5651,18 +5112,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Index"; break; case CP0_REG00__MVPCONTROL: - CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpcontrol(arg, cpu_env); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mfc0_mvpconf1(arg, tcg_env); register_name = "MVPConf1"; break; case CP0_REG00__VPCONTROL: @@ -5678,41 +5139,41 @@ 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: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); register_name = "VPEControl"; break; case CP0_REG01__VPECONF0: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); register_name = "VPEConf0"; break; case CP0_REG01__VPECONF1: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); register_name = "VPEConf1"; break; case CP0_REG01__YQMASK: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask)); register_name = "YQMask"; break; case CP0_REG01__VPESCHEDULE: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); register_name = "VPESchedule"; break; case CP0_REG01__VPESCHEFBACK: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); register_name = "VPEScheFBack"; break; case CP0_REG01__VPEOPT: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); register_name = "VPEOpt"; break; @@ -5725,7 +5186,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) { @@ -5735,43 +5196,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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mfc0_tcschefback(arg, tcg_env); register_name = "TCScheFBack"; break; default: @@ -5783,7 +5243,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) { @@ -5793,7 +5253,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; @@ -5809,7 +5268,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; @@ -5820,14 +5279,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: @@ -5847,19 +5306,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; @@ -5936,7 +5395,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; @@ -5964,10 +5423,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 @@ -5977,16 +5435,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; } @@ -5994,7 +5442,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; @@ -6051,7 +5499,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; @@ -6067,14 +5515,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; @@ -6124,12 +5572,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: @@ -6182,7 +5630,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; @@ -6210,7 +5658,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: @@ -6246,7 +5694,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; @@ -6320,9 +5768,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; @@ -6360,7 +5807,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; @@ -6382,7 +5829,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"; @@ -6406,34 +5853,33 @@ 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mtc0_mvpcontrol(tcg_env, arg); register_name = "MVPControl"; break; case CP0_REG00__MVPCONF0: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); /* ignored */ register_name = "MVPConf0"; break; case CP0_REG00__MVPCONF1: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); /* ignored */ register_name = "MVPConf1"; break; @@ -6453,40 +5899,40 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Random"; break; case CP0_REG01__VPECONTROL: - CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpecontrol(cpu_env, arg); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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, + CP0_CHECK(disas_mt_available(ctx)); + 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, + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mtc0_vpeopt(tcg_env, arg); register_name = "VPEOpt"; break; default: @@ -6496,42 +5942,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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mtc0_tcschefback(tcg_env, arg); register_name = "TCScheFBack"; break; default: @@ -6541,7 +5987,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: @@ -6556,7 +6002,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: @@ -6566,7 +6012,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; @@ -6582,28 +6028,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: @@ -6613,12 +6059,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: @@ -6628,37 +6074,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: @@ -6669,7 +6115,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; @@ -6702,19 +6148,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; } @@ -6722,7 +6158,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: @@ -6732,7 +6168,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 */ @@ -6744,7 +6180,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; @@ -6752,14 +6188,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"; @@ -6779,7 +6215,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 @@ -6796,7 +6232,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: @@ -6811,7 +6247,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: @@ -6821,7 +6257,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; @@ -6831,24 +6267,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; @@ -6870,17 +6306,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: @@ -6928,7 +6364,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 @@ -6941,7 +6377,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: @@ -6955,7 +6391,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; @@ -6963,14 +6399,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; @@ -6979,21 +6415,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"; @@ -7006,7 +6442,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: @@ -7016,7 +6452,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: @@ -7054,7 +6490,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; @@ -7078,14 +6514,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: @@ -7098,14 +6534,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: @@ -7116,7 +6552,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: @@ -7137,7 +6573,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; @@ -7151,7 +6587,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. @@ -7183,18 +6619,18 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Index"; break; case CP0_REG00__MVPCONTROL: - CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mfc0_mvpcontrol(arg, cpu_env); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mfc0_mvpconf1(arg, tcg_env); register_name = "MVPConf1"; break; case CP0_REG00__VPCONTROL: @@ -7210,44 +6646,44 @@ 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: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); register_name = "VPEControl"; break; case CP0_REG01__VPECONF0: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); register_name = "VPEConf0"; break; case CP0_REG01__VPECONF1: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); register_name = "VPEConf1"; break; case CP0_REG01__YQMASK: - CP0_CHECK(ctx->insn_flags & ASE_MT); - tcg_gen_ld_tl(arg, cpu_env, + CP0_CHECK(disas_mt_available(ctx)); + 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, + CP0_CHECK(disas_mt_available(ctx)); + 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, + CP0_CHECK(disas_mt_available(ctx)); + tcg_gen_ld_tl(arg, tcg_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); register_name = "VPEScheFBack"; break; case CP0_REG01__VPEOPT: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); register_name = "VPEOpt"; break; @@ -7258,43 +6694,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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_dmfc0_tcschefback(arg, tcg_env); register_name = "TCScheFBack"; break; default: @@ -7304,7 +6740,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: @@ -7319,7 +6755,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: @@ -7329,13 +6765,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: @@ -7355,32 +6791,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: @@ -7441,7 +6877,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: @@ -7468,10 +6904,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 @@ -7481,16 +6915,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; } @@ -7498,7 +6922,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: @@ -7554,7 +6978,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: @@ -7569,13 +6993,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: @@ -7624,12 +7048,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: @@ -7681,7 +7105,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: @@ -7707,32 +7131,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: @@ -7743,7 +7167,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: @@ -7852,7 +7276,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: @@ -7873,7 +7297,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; @@ -7896,34 +7320,33 @@ 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mtc0_mvpcontrol(tcg_env, arg); register_name = "MVPControl"; break; case CP0_REG00__MVPCONF0: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); /* ignored */ register_name = "MVPConf0"; break; case CP0_REG00__MVPCONF1: - CP0_CHECK(ctx->insn_flags & ASE_MT); + CP0_CHECK(disas_mt_available(ctx)); /* ignored */ register_name = "MVPConf1"; break; @@ -7943,40 +7366,40 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) register_name = "Random"; break; case CP0_REG01__VPECONTROL: - CP0_CHECK(ctx->insn_flags & ASE_MT); - gen_helper_mtc0_vpecontrol(cpu_env, arg); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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, + CP0_CHECK(disas_mt_available(ctx)); + 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, + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mtc0_vpeopt(tcg_env, arg); register_name = "VPEOpt"; break; default: @@ -7986,42 +7409,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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + 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); + CP0_CHECK(disas_mt_available(ctx)); + gen_helper_mtc0_tcschefback(tcg_env, arg); register_name = "TCScheFBack"; break; default: @@ -8031,7 +7454,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: @@ -8046,7 +7469,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: @@ -8056,7 +7479,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; @@ -8072,42 +7495,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: @@ -8117,37 +7540,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: @@ -8158,7 +7581,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; @@ -8191,19 +7614,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; } @@ -8213,7 +7626,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: @@ -8223,7 +7636,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 */ @@ -8237,7 +7650,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; @@ -8245,14 +7658,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"; @@ -8272,7 +7685,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 @@ -8289,7 +7702,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: @@ -8304,7 +7717,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: @@ -8314,7 +7727,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; @@ -8324,13 +7737,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; @@ -8340,7 +7753,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; @@ -8354,17 +7767,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: @@ -8411,7 +7824,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: @@ -8423,7 +7836,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: @@ -8437,7 +7850,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; @@ -8445,35 +7858,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"; @@ -8486,7 +7899,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: @@ -8496,35 +7909,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: @@ -8534,7 +7947,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; @@ -8558,14 +7971,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: @@ -8578,14 +7991,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: @@ -8596,7 +8009,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: @@ -8617,7 +8030,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; @@ -8631,7 +8044,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. @@ -8651,7 +8064,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)) != @@ -8665,10 +8078,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; @@ -8678,25 +8091,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); @@ -8706,7 +8119,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); @@ -8716,7 +8129,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); @@ -8726,7 +8139,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; @@ -8736,7 +8149,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; @@ -8746,7 +8159,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; @@ -8763,7 +8176,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; @@ -8773,7 +8186,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); @@ -8829,7 +8242,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; @@ -8843,13 +8256,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: @@ -8866,11 +8277,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); } @@ -8879,7 +8288,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 && @@ -8896,10 +8305,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; @@ -8909,25 +8318,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); @@ -8937,7 +8346,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); @@ -8947,7 +8356,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); @@ -8957,7 +8366,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; @@ -8967,7 +8376,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; @@ -8977,7 +8386,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); @@ -9033,7 +8442,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; @@ -9047,13 +8456,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: @@ -9071,11 +8478,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); } @@ -9101,7 +8506,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; @@ -9122,7 +8526,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; @@ -9142,7 +8545,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; @@ -9167,7 +8569,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"; @@ -9175,7 +8577,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: @@ -9184,7 +8586,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: @@ -9192,21 +8594,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) && @@ -9218,12 +8620,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; } @@ -9239,7 +8641,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; @@ -9254,7 +8656,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: @@ -9276,7 +8678,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) { @@ -9316,7 +8718,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); } @@ -9327,7 +8728,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); } @@ -9342,7 +8742,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); } @@ -9357,7 +8756,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); } @@ -9367,12 +8765,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 */ @@ -9385,11 +8781,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); @@ -9409,7 +8805,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); @@ -9424,9 +8820,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) */ @@ -9654,7 +9047,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; @@ -9665,7 +9057,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: @@ -9695,7 +9086,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; @@ -9706,17 +9096,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) @@ -9740,7 +9126,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); } @@ -9763,7 +9148,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, @@ -9782,11 +9166,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); } @@ -9814,14 +9196,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(); @@ -9849,16 +9230,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(); @@ -9886,10 +9263,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, @@ -9904,10 +9277,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: @@ -9917,10 +9288,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: @@ -9930,10 +9299,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: @@ -9943,10 +9310,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: @@ -9954,9 +9319,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: @@ -9970,7 +9334,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: @@ -9979,7 +9342,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: @@ -9993,7 +9355,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: @@ -10004,13 +9365,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: @@ -10021,13 +9380,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: @@ -10038,13 +9395,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: @@ -10055,13 +9410,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: @@ -10070,12 +9423,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: @@ -10084,12 +9436,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: @@ -10098,12 +9449,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: @@ -10112,12 +9462,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: @@ -10148,7 +9497,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; @@ -10163,7 +9511,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); } } @@ -10173,9 +9520,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: @@ -10183,9 +9529,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: @@ -10197,11 +9542,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: @@ -10213,11 +9555,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: @@ -10225,9 +9564,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: @@ -10235,9 +9573,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 */ @@ -10248,11 +9585,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); @@ -10262,10 +9596,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; @@ -10277,11 +9609,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); @@ -10289,9 +9618,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; @@ -10302,10 +9630,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); @@ -10313,9 +9639,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; @@ -10326,10 +9651,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); @@ -10339,10 +9662,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; @@ -10353,10 +9674,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: @@ -10365,12 +9684,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: @@ -10381,13 +9699,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: @@ -10400,10 +9716,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: @@ -10437,10 +9750,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: @@ -10451,10 +9762,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: @@ -10465,10 +9774,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: @@ -10479,10 +9786,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: @@ -10491,9 +9796,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: @@ -10508,7 +9812,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: @@ -10518,7 +9821,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: @@ -10533,7 +9835,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: @@ -10543,12 +9844,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: @@ -10558,12 +9858,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: @@ -10573,12 +9872,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: @@ -10588,12 +9886,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: @@ -10604,13 +9901,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: @@ -10621,13 +9916,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: @@ -10638,13 +9931,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: @@ -10655,13 +9946,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: @@ -10692,7 +9981,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; @@ -10707,7 +9995,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); } } @@ -10718,9 +10005,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: @@ -10729,9 +10015,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: @@ -10743,11 +10028,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: @@ -10759,11 +10041,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: @@ -10771,9 +10050,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: @@ -10781,9 +10059,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 */ @@ -10793,10 +10070,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); @@ -10806,10 +10081,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; @@ -10820,10 +10093,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); @@ -10831,9 +10102,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; @@ -10844,10 +10114,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); @@ -10855,9 +10123,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; @@ -10868,10 +10135,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); @@ -10881,10 +10146,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; @@ -10918,10 +10181,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: @@ -10932,13 +10193,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: @@ -10948,12 +10207,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: @@ -10961,9 +10219,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: @@ -10973,10 +10230,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: @@ -10986,10 +10241,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: @@ -10998,9 +10251,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: @@ -11009,9 +10261,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: @@ -11022,10 +10273,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: @@ -11036,10 +10285,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: @@ -11050,10 +10297,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: @@ -11064,7 +10309,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: @@ -11074,7 +10318,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: @@ -11085,7 +10328,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: @@ -11104,7 +10346,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; @@ -11119,7 +10360,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); } } @@ -11132,10 +10372,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: @@ -11146,10 +10384,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: @@ -11160,10 +10396,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: @@ -11172,9 +10406,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: @@ -11183,9 +10416,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: @@ -11196,10 +10428,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: @@ -11208,9 +10438,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: @@ -11219,9 +10448,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: @@ -11230,9 +10458,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: @@ -11245,8 +10472,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: @@ -11259,8 +10484,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: @@ -11273,8 +10496,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: @@ -11287,8 +10508,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: @@ -11343,10 +10562,9 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, { TCGv_i32 fp0 = tcg_temp_new_i32(); - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL); tcg_gen_trunc_tl_i32(fp0, t0); gen_store_fpr32(ctx, fp0, fd); - tcg_temp_free_i32(fp0); } break; case OPC_LDXC1: @@ -11354,9 +10572,8 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, check_cp1_registers(ctx, fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_LUXC1: @@ -11365,9 +10582,8 @@ 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); + tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_fpr64(ctx, fp0, fd); - tcg_temp_free_i64(fp0); } break; case OPC_SWXC1: @@ -11375,8 +10591,7 @@ 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); + tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UL); } break; case OPC_SDXC1: @@ -11385,8 +10600,7 @@ 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); + tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); } break; case OPC_SUXC1: @@ -11395,12 +10609,10 @@ 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); + tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); } break; } - tcg_temp_free(t0); } static void gen_flt3_arith(DisasContext *ctx, uint32_t opc, @@ -11410,7 +10622,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(); @@ -11427,8 +10639,7 @@ 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)) { + if (disas_is_bigendian(ctx)) { gen_load_fpr32(ctx, fp, fs); gen_load_fpr32h(ctx, fph, ft); gen_store_fpr32h(ctx, fp, fd); @@ -11440,8 +10651,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: @@ -11454,11 +10663,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: @@ -11472,11 +10678,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: @@ -11489,11 +10692,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: @@ -11506,11 +10706,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: @@ -11524,11 +10721,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: @@ -11541,11 +10735,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: @@ -11558,11 +10749,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: @@ -11576,11 +10764,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: @@ -11593,11 +10778,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: @@ -11610,11 +10792,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: @@ -11628,11 +10807,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: @@ -11645,11 +10821,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: @@ -11674,18 +10847,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 @@ -11696,7 +10867,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: @@ -11708,24 +10879,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 { @@ -11738,7 +10909,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) @@ -11762,7 +10932,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); @@ -11797,11 +10966,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 { @@ -11827,11 +10994,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 */ @@ -11881,17 +11048,15 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, } else { /* OPC_JIC, OPC_JIALC */ TCGv tbase = tcg_temp_new(); - TCGv toffset = tcg_constant_tl(offset); gen_load_gpr(tbase, rt); - gen_op_addr_add(ctx, btarget, tbase, toffset); - tcg_temp_free(tbase); + gen_op_addr_addi(ctx, btarget, tbase, offset); } break; default: MIPS_INVAL("Compact branch/jump"); gen_reserved_instruction(ctx); - goto out; + return; } if (bcond_compute == 0) { @@ -11912,7 +11077,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 */ @@ -12002,10 +11167,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) { @@ -12035,7 +11196,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 */ @@ -12044,42 +11205,32 @@ 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, int is_64_bit, int extended) { - TCGv t0; + target_ulong npc; if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { gen_reserved_instruction(ctx); return; } - t0 = tcg_temp_new(); - - tcg_gen_movi_tl(t0, pc_relative_pc(ctx)); - tcg_gen_addi_tl(cpu_gpr[rx], t0, imm); + npc = pc_relative_pc(ctx) + imm; if (!is_64_bit) { - tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); + npc = (int32_t)npc; } - - tcg_temp_free(t0); + tcg_gen_movi_tl(cpu_gpr[rx], npc); } 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(DisasContext *ctx, int sdbbp_code) @@ -12105,11 +11256,8 @@ void gen_ldxs(DisasContext *ctx, int base, int index, int rd) gen_op_addr_add(ctx, t0, t1, t0); } - tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL); gen_store_gpr(t1, rd); - - tcg_temp_free(t0); - tcg_temp_free(t1); } static void gen_sync(int stype) @@ -12199,21 +11347,20 @@ static void gen_mips_lx(DisasContext *ctx, uint32_t opc, gen_store_gpr(t0, rd); break; case OPC_LHX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SW); gen_store_gpr(t0, rd); break; case OPC_LWX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL); gen_store_gpr(t0, rd); break; #if defined(TARGET_MIPS64) case OPC_LDX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_gpr(t0, rd); break; #endif } - tcg_temp_free(t0); } static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, @@ -12234,8 +11381,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_load_gpr(v2_t, v2); switch (op1) { - /* OPC_MULT_G_2E is equal OPC_ADDUH_QB_DSP */ - case OPC_MULT_G_2E: + case OPC_ADDUH_QB_DSP: check_dsp_r2(ctx); switch (op2) { case OPC_ADDUH_QB: @@ -12280,15 +11426,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); @@ -12339,67 +11485,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); @@ -12424,19 +11570,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: @@ -12445,11 +11589,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; @@ -12514,15 +11658,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; @@ -12534,35 +11678,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); @@ -12574,35 +11718,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); @@ -12623,17 +11767,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: @@ -12650,19 +11792,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, @@ -12693,35 +11832,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); @@ -12792,43 +11931,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); @@ -12902,10 +12041,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, @@ -12929,24 +12064,20 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_load_gpr(v2_t, v2); switch (op1) { - /* - * OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have - * the same mask and op1. - */ - case OPC_MULT_G_2E: + case OPC_MUL_PH_DSP: 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; @@ -12954,91 +12085,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; @@ -13051,107 +12182,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; } } @@ -13161,27 +12292,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; @@ -13190,32 +12321,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, @@ -13352,8 +12479,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, @@ -13381,15 +12506,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); @@ -13429,23 +12554,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); @@ -13458,39 +12583,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); @@ -13506,15 +12631,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); @@ -13522,24 +12647,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, @@ -13627,7 +12748,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, @@ -13657,80 +12777,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; @@ -13740,7 +12860,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: { @@ -13748,106 +12868,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. */ @@ -14174,7 +13290,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: @@ -14300,15 +13416,6 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) case OPC_MUL: gen_arith(ctx, op1, rd, rs, rt); break; - case OPC_DIV_G_2F: - case OPC_DIVU_G_2F: - case OPC_MULT_G_2F: - case OPC_MULTU_G_2F: - case OPC_MOD_G_2F: - case OPC_MODU_G_2F: - check_insn(ctx, INSN_LOONGSON2F | ASE_LEXT); - gen_loongson_integer(ctx, op1, rd, rs, rt); - break; case OPC_CLO: case OPC_CLZ: check_insn(ctx, ISA_MIPS_R1); @@ -14333,15 +13440,6 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_cl(ctx, op1, rd, rs); break; - case OPC_DMULT_G_2F: - case OPC_DMULTU_G_2F: - case OPC_DDIV_G_2F: - case OPC_DDIVU_G_2F: - case OPC_DMOD_G_2F: - case OPC_DMODU_G_2F: - check_insn(ctx, INSN_LOONGSON2F | ASE_LEXT); - gen_loongson_integer(ctx, op1, rd, rs, rt); - break; #endif default: /* Invalid */ MIPS_INVAL("special2_legacy"); @@ -14378,7 +13476,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) } break; case R6_OPC_SC: - gen_st_cond(ctx, rt, rs, imm, MO_TESL, false); + gen_st_cond(ctx, rt, rs, imm, mo_endian(ctx) | MO_SL, false); break; case R6_OPC_LL: gen_ld(ctx, op1, rt, rs, imm); @@ -14424,7 +13522,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) #endif #if defined(TARGET_MIPS64) case R6_OPC_SCD: - gen_st_cond(ctx, rt, rs, imm, MO_TEUQ, false); + gen_st_cond(ctx, rt, rs, imm, mo_endian(ctx) | MO_UQ, false); break; case R6_OPC_LLD: gen_ld(ctx, op1, rt, rs, imm); @@ -14474,17 +13572,12 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) op1 = MASK_SPECIAL3(ctx->opcode); switch (op1) { - case OPC_DIV_G_2E: - case OPC_DIVU_G_2E: - case OPC_MOD_G_2E: - case OPC_MODU_G_2E: - case OPC_MULT_G_2E: - case OPC_MULTU_G_2E: + case OPC_MUL_PH_DSP: /* - * OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have + * OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have * the same mask and op1. */ - if ((ctx->insn_flags & ASE_DSP_R2) && (op1 == OPC_MULT_G_2E)) { + if ((ctx->insn_flags & ASE_DSP_R2) && (op1 == OPC_MUL_PH_DSP)) { op2 = MASK_ADDUH_QB(ctx->opcode); switch (op2) { case OPC_ADDUH_QB: @@ -14512,8 +13605,6 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) gen_reserved_instruction(ctx); break; } - } else if (ctx->insn_flags & INSN_LOONGSON2E) { - gen_loongson_integer(ctx, op1, rd, rs, rt); } else { gen_reserved_instruction(ctx); } @@ -14697,10 +13788,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 */ @@ -14745,15 +13833,6 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } break; #if defined(TARGET_MIPS64) - case OPC_DDIV_G_2E: - case OPC_DDIVU_G_2E: - case OPC_DMULT_G_2E: - case OPC_DMULTU_G_2E: - case OPC_DMOD_G_2E: - case OPC_DMODU_G_2E: - check_insn(ctx, INSN_LOONGSON2E); - gen_loongson_integer(ctx, op1, rd, rs, rt); - break; case OPC_ABSQ_S_QH_DSP: op2 = MASK_ABSQ_S_QH(ctx->opcode); switch (op2) { @@ -14969,10 +14048,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 */ @@ -15113,7 +14189,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) return; case OPC_SCE: check_cp0_enabled(ctx); - gen_st_cond(ctx, rt, rs, imm, MO_TESL, true); + gen_st_cond(ctx, rt, rs, imm, mo_endian(ctx) | MO_SL, true); return; case OPC_CACHEE: check_eva(ctx); @@ -15199,8 +14275,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: @@ -15209,9 +14283,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: @@ -15248,12 +14321,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; @@ -15404,32 +14474,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 @@ -15440,7 +14510,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 @@ -15454,7 +14524,6 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) gen_reserved_instruction(ctx); break; } - tcg_temp_free(t0); } #endif /* !CONFIG_USER_ONLY */ break; @@ -15584,7 +14653,7 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) if (ctx->insn_flags & INSN_R5900) { check_insn_opc_user_only(ctx, INSN_R5900); } - gen_st_cond(ctx, rt, rs, imm, MO_TESL, false); + gen_st_cond(ctx, rt, rs, imm, mo_endian(ctx) | MO_SL, false); break; case OPC_CACHE: check_cp0_enabled(ctx); @@ -15641,7 +14710,9 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) } else { /* OPC_BC1ANY2 */ check_cop1x(ctx); - check_insn(ctx, ASE_MIPS3D); + if (!ase_3d_available(env)) { + return false; + } gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), (rt >> 2) & 0x7, imm << 2); } @@ -15656,7 +14727,9 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) check_cp1_enabled(ctx); check_insn_opc_removed(ctx, ISA_MIPS_R6); check_cop1x(ctx); - check_insn(ctx, ASE_MIPS3D); + if (!ase_3d_available(env)) { + return false; + } /* fall through */ case OPC_BC1: check_cp1_enabled(ctx); @@ -15863,7 +14936,7 @@ static bool decode_opc_legacy(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_user_only(ctx, INSN_R5900); } check_mips_64(ctx); - gen_st_cond(ctx, rt, rs, imm, MO_TEUQ, false); + gen_st_cond(ctx, rt, rs, imm, mo_endian(ctx) | MO_UQ, false); break; case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC, OPC_DADDI */ if (ctx->insn_flags & ISA_MIPS_R6) { @@ -15902,7 +14975,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); @@ -15957,7 +15029,13 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) if (cpu_supports_isa(env, INSN_VR54XX) && decode_ext_vr54xx(ctx, ctx->opcode)) { return; } + if (TARGET_LONG_BITS == 64 && decode_ext_loongson(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; } @@ -15983,7 +15061,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; @@ -16032,7 +15110,8 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) * hardware does (e.g. if a delay slot instruction faults, the * reported PC is the PC of the branch). */ - if (ctx->base.singlestep_enabled && (ctx->hflags & MIPS_HFLAG_BMASK)) { + if ((tb_cflags(ctx->base.tb) & CF_SINGLE_STEP) && + (ctx->hflags & MIPS_HFLAG_BMASK)) { ctx->base.max_insns = 2; } @@ -16054,7 +15133,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; @@ -16115,7 +15194,7 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) * together with its delay slot. */ if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE - && !ctx->base.singlestep_enabled) { + && !(tb_cflags(ctx->base.tb) & CF_SINGLE_STEP)) { ctx->base.is_jmp = DISAS_TOO_MANY; } } @@ -16144,24 +15223,16 @@ static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void mips_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps mips_tr_ops = { .init_disas_context = mips_tr_init_disas_context, .tb_start = mips_tr_tb_start, .insn_start = mips_tr_insn_start, .translate_insn = mips_tr_translate_insn, .tb_stop = mips_tr_tb_stop, - .disas_log = mips_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; @@ -16170,11 +15241,9 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, 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]); @@ -16184,48 +15253,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) { @@ -16237,8 +15306,7 @@ void mips_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - MIPSCPU *cpu = MIPS_CPU(cs); - CPUMIPSState *env = &cpu->env; + CPUMIPSState *env = cpu_env(cs); env->active_tc.PC = data[0]; env->hflags &= ~MIPS_HFLAG_BMASK; diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 69f85841d2..1bf153d183 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,7 +49,6 @@ typedef struct DisasContext { bool mrp; bool nan2008; bool abs2008; - bool saar; bool mi; int gi; } DisasContext; @@ -120,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); @@ -174,6 +176,7 @@ void gen_addiupc(DisasContext *ctx, int rx, int imm, * Address Computation and Large Constant Instructions */ void gen_op_addr_add(DisasContext *ctx, TCGv ret, TCGv arg0, TCGv arg1); +void gen_op_addr_addi(DisasContext *ctx, TCGv ret, TCGv base, target_long ofs); bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa); bool gen_dlsa(DisasContext *ctx, int rd, int rt, int rs, int sa); @@ -199,7 +202,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)); \ @@ -213,16 +217,25 @@ void msa_translate_init(void); void mxu_translate_init(void); bool decode_ase_mxu(DisasContext *ctx, uint32_t insn); +bool decode_64bit_enabled(DisasContext *ctx); + /* decodetree generated */ 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); +bool decode_ext_loongson(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); +static inline bool disas_mt_available(DisasContext *ctx) +{ + return ctx->CP0_Config3 & (1 << CP0C3_MT); +} + /* * Helpers for implementing sets of trans_* functions. * Defer the implementation of NAME to FUNC, with optional extra arguments. @@ -231,9 +244,19 @@ bool decode_ext_vr54xx(DisasContext *ctx, uint32_t insn); static bool trans_##NAME(DisasContext *ctx, arg_##NAME *a) \ { return FUNC(ctx, a, __VA_ARGS__); } -static inline bool cpu_is_bigendian(DisasContext *ctx) +static inline bool disas_is_bigendian(DisasContext *ctx) { return extract32(ctx->CP0_Config0, CP0C0_BE, 1); } +static inline MemOp mo_endian(DisasContext *dc) +{ + return disas_is_bigendian(dc) ? MO_BE : MO_LE; +} + +static inline MemOp mo_endian_rev(DisasContext *dc, bool reversed) +{ + return disas_is_bigendian(dc) ^ reversed ? MO_BE : MO_LE; +} + #endif 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..ae3f5e19c4 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; } @@ -355,17 +340,13 @@ static bool trans_LQ(DisasContext *ctx, arg_i *a) tcg_gen_andi_tl(addr, addr, ~0xf); /* Lower half */ - tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_gpr(t0, a->rt); /* Upper half */ tcg_gen_addi_i64(addr, addr, 8); - tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(t0, addr, ctx->mem_idx, mo_endian(ctx) | MO_UQ); gen_store_gpr_hi(t0, a->rt); - - tcg_temp_free(t0); - tcg_temp_free(addr); - return true; } @@ -383,16 +364,12 @@ static bool trans_SQ(DisasContext *ctx, arg_i *a) /* Lower half */ gen_load_gpr(t0, a->rt); - tcg_gen_qemu_st_i64(t0, addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_st_i64(t0, addr, ctx->mem_idx, mo_endian(ctx) | MO_UQ); /* Upper half */ 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); - + tcg_gen_qemu_st_i64(t0, addr, ctx->mem_idx, mo_endian(ctx) | MO_UQ); 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 deleted file mode 100644 index 1529ab8950..0000000000 --- a/target/nios2/Kconfig +++ /dev/null @@ -1,2 +0,0 @@ -config NIOS2 - bool diff --git a/target/nios2/cpu-param.h b/target/nios2/cpu-param.h deleted file mode 100644 index 177d720864..0000000000 --- a/target/nios2/cpu-param.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Altera Nios II cpu parameters for qemu. - * - * Copyright (c) 2012 Chris Wulff - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef NIOS2_CPU_PARAM_H -#define NIOS2_CPU_PARAM_H - -#define TARGET_LONG_BITS 32 -#define TARGET_PAGE_BITS 12 -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#ifdef CONFIG_USER_ONLY -# define TARGET_VIRT_ADDR_SPACE_BITS 31 -#else -# define TARGET_VIRT_ADDR_SPACE_BITS 32 -#endif -#define NB_MMU_MODES 2 - -#endif diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c deleted file mode 100644 index 9a5351bc81..0000000000 --- a/target/nios2/cpu.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * QEMU Nios II CPU - * - * Copyright (c) 2012 Chris Wulff - * - * 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/module.h" -#include "qapi/error.h" -#include "cpu.h" -#include "exec/log.h" -#include "exec/gdbstub.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; - - env->pc = value; -} - -static vaddr nios2_cpu_get_pc(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - - return env->pc; -} - -static void nios2_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - - env->pc = data[0]; -} - -static bool nios2_cpu_has_work(CPUState *cs) -{ - return cs->interrupt_request & CPU_INTERRUPT_HARD; -} - -static void nios2_cpu_reset(DeviceState *dev) -{ - CPUState *cs = CPU(dev); - Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu); - CPUNios2State *env = &cpu->env; - - ncc->parent_reset(dev); - - memset(env->ctrl, 0, sizeof(env->ctrl)); - env->pc = cpu->reset_addr; - -#if defined(CONFIG_USER_ONLY) - /* Start in user mode with interrupts enabled. */ - env->ctrl[CR_STATUS] = CR_STATUS_RSIE | CR_STATUS_U | CR_STATUS_PIE; - memset(env->regs, 0, sizeof(env->regs)); -#else - env->ctrl[CR_STATUS] = CR_STATUS_RSIE; - nios2_update_crs(env); - memset(env->shadow_regs, 0, sizeof(env->shadow_regs)); -#endif -} - -#ifndef CONFIG_USER_ONLY -static void eic_set_irq(void *opaque, int irq, int level) -{ - Nios2CPU *cpu = opaque; - CPUState *cs = CPU(cpu); - - if (level) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} - -static void iic_set_irq(void *opaque, int irq, int level) -{ - Nios2CPU *cpu = opaque; - CPUNios2State *env = &cpu->env; - CPUState *cs = CPU(cpu); - - env->ctrl[CR_IPENDING] = deposit32(env->ctrl[CR_IPENDING], irq, 1, !!level); - - if (env->ctrl[CR_IPENDING]) { - cpu_interrupt(cs, CPU_INTERRUPT_HARD); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); - } -} -#endif - -static void nios2_cpu_initfn(Object *obj) -{ - Nios2CPU *cpu = NIOS2_CPU(obj); - - cpu_set_cpustate_pointers(cpu); - -#if !defined(CONFIG_USER_ONLY) - mmu_init(&cpu->env); -#endif -} - -static ObjectClass *nios2_cpu_class_by_name(const char *cpu_model) -{ - return object_class_by_name(TYPE_NIOS2_CPU); -} - -static void realize_cr_status(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - - /* Begin with all fields of all registers are reserved. */ - memset(cpu->cr_state, 0, sizeof(cpu->cr_state)); - - /* - * The combination of writable and readonly is the set of all - * non-reserved fields. We apply writable as a mask to bits, - * and merge in existing readonly bits, before storing. - */ -#define WR_REG(C) cpu->cr_state[C].writable = -1 -#define RO_REG(C) cpu->cr_state[C].readonly = -1 -#define WR_FIELD(C, F) cpu->cr_state[C].writable |= R_##C##_##F##_MASK -#define RO_FIELD(C, F) cpu->cr_state[C].readonly |= R_##C##_##F##_MASK - - WR_FIELD(CR_STATUS, PIE); - WR_REG(CR_ESTATUS); - WR_REG(CR_BSTATUS); - RO_REG(CR_CPUID); - RO_REG(CR_EXCEPTION); - WR_REG(CR_BADADDR); - - if (cpu->eic_present) { - WR_FIELD(CR_STATUS, RSIE); - RO_FIELD(CR_STATUS, NMI); - WR_FIELD(CR_STATUS, PRS); - RO_FIELD(CR_STATUS, CRS); - WR_FIELD(CR_STATUS, IL); - WR_FIELD(CR_STATUS, IH); - } else { - RO_FIELD(CR_STATUS, RSIE); - WR_REG(CR_IENABLE); - RO_REG(CR_IPENDING); - } - - if (cpu->mmu_present) { - WR_FIELD(CR_STATUS, U); - WR_FIELD(CR_STATUS, EH); - - WR_FIELD(CR_PTEADDR, VPN); - WR_FIELD(CR_PTEADDR, PTBASE); - - RO_FIELD(CR_TLBMISC, D); - RO_FIELD(CR_TLBMISC, PERM); - RO_FIELD(CR_TLBMISC, BAD); - RO_FIELD(CR_TLBMISC, DBL); - WR_FIELD(CR_TLBMISC, PID); - WR_FIELD(CR_TLBMISC, WE); - WR_FIELD(CR_TLBMISC, RD); - WR_FIELD(CR_TLBMISC, WAY); - - WR_REG(CR_TLBACC); - } - - /* - * TODO: ECC (config, eccinj) and MPU (config, mpubase, mpuacc) are - * unimplemented, so their corresponding control regs remain reserved. - */ - -#undef WR_REG -#undef RO_REG -#undef WR_FIELD -#undef RO_FIELD -} - -static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) -{ - CPUState *cs = CPU(dev); - Nios2CPU *cpu = NIOS2_CPU(cs); - Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev); - Error *local_err = NULL; - -#ifndef CONFIG_USER_ONLY - if (cpu->eic_present) { - qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1); - } else { - qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32); - } -#endif - - cpu_exec_realizefn(cs, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - return; - } - - realize_cr_status(cs); - qemu_init_vcpu(cs); - cpu_reset(cs); - - /* We have reserved storage for cpuid; might as well use it. */ - cpu->env.ctrl[CR_CPUID] = cs->cpu_index; - - ncc->parent_realize(dev, errp); -} - -#ifndef CONFIG_USER_ONLY -static bool eic_take_interrupt(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - const uint32_t status = env->ctrl[CR_STATUS]; - - if (cpu->rnmi) { - return !(status & CR_STATUS_NMI); - } - if (!(status & CR_STATUS_PIE)) { - return false; - } - if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) { - return false; - } - if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) { - return true; - } - return status & CR_STATUS_RSIE; -} - -static bool iic_take_interrupt(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - - if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) { - return false; - } - return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE]; -} - -static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - - if (interrupt_request & CPU_INTERRUPT_HARD) { - if (cpu->eic_present - ? eic_take_interrupt(cpu) - : iic_take_interrupt(cpu)) { - cs->exception_index = EXCP_IRQ; - nios2_cpu_do_interrupt(cs); - return true; - } - } - return false; -} -#endif /* !CONFIG_USER_ONLY */ - -static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) -{ - /* NOTE: NiosII R2 is not supported yet. */ - info->mach = bfd_arch_nios2; - info->print_insn = print_insn_nios2; -} - -static int nios2_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - uint32_t val; - - if (n < 32) { /* GP regs */ - val = env->regs[n]; - } else if (n == 32) { /* PC */ - val = env->pc; - } else if (n < 49) { /* Status regs */ - unsigned cr = n - 33; - if (nios2_cr_reserved(&cpu->cr_state[cr])) { - val = 0; - } else { - val = env->ctrl[n - 33]; - } - } else { - /* Invalid regs */ - return 0; - } - - return gdb_get_reg32(mem_buf, val); -} - -static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUClass *cc = CPU_GET_CLASS(cs); - CPUNios2State *env = &cpu->env; - uint32_t val; - - if (n > cc->gdb_num_core_regs) { - return 0; - } - val = ldl_p(mem_buf); - - if (n < 32) { /* GP regs */ - env->regs[n] = val; - } else if (n == 32) { /* PC */ - env->pc = val; - } else if (n < 49) { /* Status regs */ - unsigned cr = n - 33; - /* ??? Maybe allow the debugger to write to readonly fields. */ - val &= cpu->cr_state[cr].writable; - val |= cpu->cr_state[cr].readonly & env->ctrl[cr]; - env->ctrl[cr] = val; - } else { - g_assert_not_reached(); - } - - return 4; -} - -static Property nios2_properties[] = { - DEFINE_PROP_BOOL("diverr_present", Nios2CPU, diverr_present, true), - DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true), - /* ALTR,pid-num-bits */ - DEFINE_PROP_UINT32("mmu_pid_num_bits", Nios2CPU, pid_num_bits, 8), - /* ALTR,tlb-num-ways */ - DEFINE_PROP_UINT32("mmu_tlb_num_ways", Nios2CPU, tlb_num_ways, 16), - /* ALTR,tlb-num-entries */ - DEFINE_PROP_UINT32("mmu_pid_num_entries", Nios2CPU, tlb_num_entries, 256), - DEFINE_PROP_END_OF_LIST(), -}; - -#ifndef CONFIG_USER_ONLY -#include "hw/core/sysemu-cpu-ops.h" - -static const struct SysemuCPUOps nios2_sysemu_ops = { - .get_phys_page_debug = nios2_cpu_get_phys_page_debug, -}; -#endif - -#include "hw/core/tcg-cpu-ops.h" - -static const struct 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, - .cpu_exec_interrupt = nios2_cpu_exec_interrupt, - .do_interrupt = nios2_cpu_do_interrupt, - .do_unaligned_access = nios2_cpu_do_unaligned_access, -#endif /* !CONFIG_USER_ONLY */ -}; - -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); - - 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); - - cc->class_by_name = nios2_cpu_class_by_name; - cc->has_work = nios2_cpu_has_work; - 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; -#endif - cc->gdb_read_register = nios2_cpu_gdb_read_register; - cc->gdb_write_register = nios2_cpu_gdb_write_register; - cc->gdb_num_core_regs = 49; - cc->tcg_ops = &nios2_tcg_ops; -} - -static const TypeInfo nios2_cpu_type_info = { - .name = TYPE_NIOS2_CPU, - .parent = TYPE_CPU, - .instance_size = sizeof(Nios2CPU), - .instance_init = nios2_cpu_initfn, - .class_size = sizeof(Nios2CPUClass), - .class_init = nios2_cpu_class_init, -}; - -static void nios2_cpu_register_types(void) -{ - type_register_static(&nios2_cpu_type_info); -} - -type_init(nios2_cpu_register_types) diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h deleted file mode 100644 index f85581ee56..0000000000 --- a/target/nios2/cpu.h +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Altera Nios II virtual CPU header - * - * Copyright (c) 2012 Chris Wulff - * - * 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 NIOS2_CPU_H -#define NIOS2_CPU_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. - * - * A Nios2 CPU model. - */ -struct Nios2CPUClass { - /*< private >*/ - CPUClass parent_class; - /*< public >*/ - - DeviceRealize parent_realize; - DeviceReset parent_reset; -}; - -#define TARGET_HAS_ICE 1 - -/* Configuration options for Nios II */ -#define RESET_ADDRESS 0x00000000 -#define EXCEPTION_ADDRESS 0x00000004 -#define FAST_TLB_MISS_ADDRESS 0x00000008 - -#define NUM_GP_REGS 32 -#define NUM_CR_REGS 32 - -#ifndef CONFIG_USER_ONLY -/* 63 shadow register sets; index 0 is the primary register set. */ -#define NUM_REG_SETS 64 -#endif - -/* General purpose register aliases */ -enum { - R_ZERO = 0, - R_AT = 1, - R_RET0 = 2, - R_RET1 = 3, - R_ARG0 = 4, - R_ARG1 = 5, - R_ARG2 = 6, - R_ARG3 = 7, - R_ET = 24, - R_BT = 25, - R_GP = 26, - R_SP = 27, - R_FP = 28, - R_EA = 29, - R_BA = 30, - R_SSTATUS = 30, - R_RA = 31, -}; - -/* Control register aliases */ -enum { - CR_STATUS = 0, - CR_ESTATUS = 1, - CR_BSTATUS = 2, - CR_IENABLE = 3, - CR_IPENDING = 4, - CR_CPUID = 5, - CR_EXCEPTION = 7, - CR_PTEADDR = 8, - CR_TLBACC = 9, - CR_TLBMISC = 10, - CR_ENCINJ = 11, - CR_BADADDR = 12, - CR_CONFIG = 13, - CR_MPUBASE = 14, - CR_MPUACC = 15, -}; - -FIELD(CR_STATUS, PIE, 0, 1) -FIELD(CR_STATUS, U, 1, 1) -FIELD(CR_STATUS, EH, 2, 1) -FIELD(CR_STATUS, IH, 3, 1) -FIELD(CR_STATUS, IL, 4, 6) -FIELD(CR_STATUS, CRS, 10, 6) -FIELD(CR_STATUS, PRS, 16, 6) -FIELD(CR_STATUS, NMI, 22, 1) -FIELD(CR_STATUS, RSIE, 23, 1) -FIELD(CR_STATUS, SRS, 31, 1) /* only in sstatus */ - -#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK -#define CR_STATUS_U R_CR_STATUS_U_MASK -#define CR_STATUS_EH R_CR_STATUS_EH_MASK -#define CR_STATUS_IH R_CR_STATUS_IH_MASK -#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK -#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK -#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK - -FIELD(CR_EXCEPTION, CAUSE, 2, 5) -FIELD(CR_EXCEPTION, ECCFTL, 31, 1) - -FIELD(CR_PTEADDR, VPN, 2, 20) -FIELD(CR_PTEADDR, PTBASE, 22, 10) - -FIELD(CR_TLBACC, PFN, 0, 20) -FIELD(CR_TLBACC, G, 20, 1) -FIELD(CR_TLBACC, X, 21, 1) -FIELD(CR_TLBACC, W, 22, 1) -FIELD(CR_TLBACC, R, 23, 1) -FIELD(CR_TLBACC, C, 24, 1) -FIELD(CR_TLBACC, IG, 25, 7) - -#define CR_TLBACC_C R_CR_TLBACC_C_MASK -#define CR_TLBACC_R R_CR_TLBACC_R_MASK -#define CR_TLBACC_W R_CR_TLBACC_W_MASK -#define CR_TLBACC_X R_CR_TLBACC_X_MASK -#define CR_TLBACC_G R_CR_TLBACC_G_MASK - -FIELD(CR_TLBMISC, D, 0, 1) -FIELD(CR_TLBMISC, PERM, 1, 1) -FIELD(CR_TLBMISC, BAD, 2, 1) -FIELD(CR_TLBMISC, DBL, 3, 1) -FIELD(CR_TLBMISC, PID, 4, 14) -FIELD(CR_TLBMISC, WE, 18, 1) -FIELD(CR_TLBMISC, RD, 19, 1) -FIELD(CR_TLBMISC, WAY, 20, 4) -FIELD(CR_TLBMISC, EE, 24, 1) - -#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK -#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK -#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK -#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK -#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK -#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK -#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK - -/* Exceptions */ -#define EXCP_BREAK 0x1000 -#define EXCP_SEMIHOST 0x1001 -#define EXCP_RESET 0 -#define EXCP_PRESET 1 -#define EXCP_IRQ 2 -#define EXCP_TRAP 3 -#define EXCP_UNIMPL 4 -#define EXCP_ILLEGAL 5 -#define EXCP_UNALIGN 6 -#define EXCP_UNALIGND 7 -#define EXCP_DIV 8 -#define EXCP_SUPERA_X 9 -#define EXCP_SUPERI 10 -#define EXCP_SUPERA_D 11 -#define EXCP_TLB_X 12 -#define EXCP_TLB_D (0x1000 | EXCP_TLB_X) -#define EXCP_PERM_X 13 -#define EXCP_PERM_R 14 -#define EXCP_PERM_W 15 -#define EXCP_MPUI 16 -#define EXCP_MPUD 17 - -struct CPUArchState { -#ifdef CONFIG_USER_ONLY - uint32_t regs[NUM_GP_REGS]; -#else - uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS]; - /* Pointer into shadow_regs for the current register set. */ - uint32_t *regs; -#endif - uint32_t ctrl[NUM_CR_REGS]; - uint32_t pc; - -#if !defined(CONFIG_USER_ONLY) - Nios2MMU mmu; -#endif - int error_code; -}; - -typedef struct { - uint32_t writable; - uint32_t readonly; -} ControlRegState; - -/** - * Nios2CPU: - * @env: #CPUNios2State - * - * A Nios2 CPU. - */ -struct ArchCPU { - /*< private >*/ - CPUState parent_obj; - /*< public >*/ - - CPUNegativeOffsetState neg; - CPUNios2State env; - - bool diverr_present; - bool mmu_present; - bool eic_present; - - uint32_t pid_num_bits; - uint32_t tlb_num_ways; - uint32_t tlb_num_entries; - - /* Addresses that are hard-coded in the FPGA build settings */ - uint32_t reset_addr; - uint32_t exception_addr; - uint32_t fast_tlb_miss_addr; - - /* Bits within each control register which are reserved or readonly. */ - ControlRegState cr_state[NUM_CR_REGS]; - - /* External Interrupt Controller Interface */ - uint32_t rha; /* Requested handler address */ - uint32_t ril; /* Requested interrupt level */ - uint32_t rrs; /* Requested register set */ - bool rnmi; /* Requested nonmaskable interrupt */ -}; - - -static inline bool nios2_cr_reserved(const ControlRegState *s) -{ - return (s->writable | s->readonly) == 0; -} - -static inline void nios2_update_crs(CPUNios2State *env) -{ -#ifndef CONFIG_USER_ONLY - unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); - env->regs = env->shadow_regs[crs]; -#endif -} - -void nios2_tcg_init(void); -void nios2_cpu_do_interrupt(CPUState *cs); -void dump_mmu(CPUNios2State *env); -void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags); -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, - MMUAccessType access_type, int mmu_idx, - uintptr_t retaddr); -G_NORETURN void nios2_cpu_loop_exit_advance(CPUNios2State *env, - uintptr_t retaddr); - -void do_nios2_semihosting(CPUNios2State *env); - -#define CPU_RESOLVING_TYPE TYPE_NIOS2_CPU - -#define cpu_gen_code cpu_nios2_gen_code - -#define CPU_SAVE_VERSION 1 - -/* MMU modes definitions */ -#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 -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); -#endif - -typedef CPUNios2State CPUArchState; -typedef Nios2CPU ArchCPU; - -#include "exec/cpu-all.h" - -FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */ -FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */ -FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */ - -static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, - target_ulong *cs_base, uint32_t *flags) -{ - unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS); - - *pc = env->pc; - *cs_base = 0; - *flags = (env->ctrl[CR_STATUS] & CR_STATUS_U) - | (crs ? 0 : R_TBFLAGS_CRS0_MASK) - | (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK); -} - -#endif /* NIOS2_CPU_H */ diff --git a/target/nios2/helper.c b/target/nios2/helper.c deleted file mode 100644 index bb3b09e5a7..0000000000 --- a/target/nios2/helper.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Altera Nios II helper routines. - * - * Copyright (c) 2012 Chris Wulff - * - * 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 "qemu/host-utils.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/log.h" -#include "exec/helper-proto.h" -#include "semihosting/semihost.h" - - -static void do_exception(Nios2CPU *cpu, uint32_t exception_addr, - uint32_t tlbmisc_set, bool is_break) -{ - CPUNios2State *env = &cpu->env; - CPUState *cs = CPU(cpu); - uint32_t old_status = env->ctrl[CR_STATUS]; - uint32_t new_status = old_status; - - /* With shadow regs, exceptions are always taken into CRS 0. */ - new_status &= ~R_CR_STATUS_CRS_MASK; - env->regs = env->shadow_regs[0]; - - if ((old_status & CR_STATUS_EH) == 0) { - int r_ea = R_EA, cr_es = CR_ESTATUS; - - if (is_break) { - r_ea = R_BA; - cr_es = CR_BSTATUS; - } - env->ctrl[cr_es] = old_status; - env->regs[r_ea] = env->pc; - - if (cpu->mmu_present) { - new_status |= CR_STATUS_EH; - - /* - * There are 4 bits that are always written. - * Explicitly clear them, to be set via the argument. - */ - env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | - CR_TLBMISC_PERM | - CR_TLBMISC_BAD | - CR_TLBMISC_DBL); - env->ctrl[CR_TLBMISC] |= tlbmisc_set; - } - - /* - * With shadow regs, and EH == 0, PRS is set from CRS. - * At least, so says Table 3-9, and some other text, - * though Table 3-38 says otherwise. - */ - new_status = FIELD_DP32(new_status, CR_STATUS, PRS, - FIELD_EX32(old_status, CR_STATUS, CRS)); - } - - new_status &= ~(CR_STATUS_PIE | CR_STATUS_U); - - env->ctrl[CR_STATUS] = new_status; - if (!is_break) { - env->ctrl[CR_EXCEPTION] = FIELD_DP32(0, CR_EXCEPTION, CAUSE, - cs->exception_index); - } - env->pc = exception_addr; -} - -static void do_iic_irq(Nios2CPU *cpu) -{ - do_exception(cpu, cpu->exception_addr, 0, false); -} - -static void do_eic_irq(Nios2CPU *cpu) -{ - CPUNios2State *env = &cpu->env; - uint32_t old_status = env->ctrl[CR_STATUS]; - uint32_t new_status = old_status; - uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS); - uint32_t new_rs = cpu->rrs; - - new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs); - new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril); - new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi); - new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U); - new_status |= CR_STATUS_IH; - - if (!(new_status & CR_STATUS_EH)) { - new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs); - if (new_rs == 0) { - env->ctrl[CR_ESTATUS] = old_status; - } else { - if (new_rs != old_rs) { - old_status |= CR_STATUS_SRS; - } - env->shadow_regs[new_rs][R_SSTATUS] = old_status; - } - env->shadow_regs[new_rs][R_EA] = env->pc; - } - - env->ctrl[CR_STATUS] = new_status; - nios2_update_crs(env); - - env->pc = cpu->rha; -} - -void nios2_cpu_do_interrupt(CPUState *cs) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - uint32_t tlbmisc_set = 0; - - if (qemu_loglevel_mask(CPU_LOG_INT)) { - const char *name = NULL; - - switch (cs->exception_index) { - case EXCP_IRQ: - name = "interrupt"; - break; - case EXCP_TLB_X: - case EXCP_TLB_D: - if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { - name = "TLB MISS (double)"; - } else { - name = "TLB MISS (fast)"; - } - break; - case EXCP_PERM_R: - case EXCP_PERM_W: - case EXCP_PERM_X: - name = "TLB PERM"; - break; - case EXCP_SUPERA_X: - case EXCP_SUPERA_D: - name = "SUPERVISOR (address)"; - break; - case EXCP_SUPERI: - name = "SUPERVISOR (insn)"; - break; - case EXCP_ILLEGAL: - name = "ILLEGAL insn"; - break; - case EXCP_UNALIGN: - name = "Misaligned (data)"; - break; - case EXCP_UNALIGND: - name = "Misaligned (destination)"; - break; - case EXCP_DIV: - name = "DIV error"; - break; - case EXCP_TRAP: - name = "TRAP insn"; - break; - case EXCP_BREAK: - name = "BREAK insn"; - break; - case EXCP_SEMIHOST: - name = "SEMIHOST insn"; - break; - } - if (name) { - qemu_log("%s at pc=0x%08x\n", name, env->pc); - } else { - qemu_log("Unknown exception %d at pc=0x%08x\n", - cs->exception_index, env->pc); - } - } - - switch (cs->exception_index) { - case EXCP_IRQ: - /* Note that PC is advanced for interrupts as well. */ - env->pc += 4; - if (cpu->eic_present) { - do_eic_irq(cpu); - } else { - do_iic_irq(cpu); - } - break; - - case EXCP_TLB_D: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_TLB_X: - if (env->ctrl[CR_STATUS] & CR_STATUS_EH) { - tlbmisc_set |= CR_TLBMISC_DBL; - /* - * Normally, we don't write to tlbmisc unless !EH, - * so do it manually for the double-tlb miss exception. - */ - env->ctrl[CR_TLBMISC] &= ~(CR_TLBMISC_D | - CR_TLBMISC_PERM | - CR_TLBMISC_BAD); - env->ctrl[CR_TLBMISC] |= tlbmisc_set; - do_exception(cpu, cpu->exception_addr, 0, false); - } else { - tlbmisc_set |= CR_TLBMISC_WE; - do_exception(cpu, cpu->fast_tlb_miss_addr, tlbmisc_set, false); - } - break; - - case EXCP_PERM_R: - case EXCP_PERM_W: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_PERM_X: - tlbmisc_set |= CR_TLBMISC_PERM; - if (!(env->ctrl[CR_STATUS] & CR_STATUS_EH)) { - tlbmisc_set |= CR_TLBMISC_WE; - } - do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); - break; - - case EXCP_SUPERA_D: - case EXCP_UNALIGN: - tlbmisc_set = CR_TLBMISC_D; - /* fall through */ - case EXCP_SUPERA_X: - case EXCP_UNALIGND: - tlbmisc_set |= CR_TLBMISC_BAD; - do_exception(cpu, cpu->exception_addr, tlbmisc_set, false); - break; - - case EXCP_SUPERI: - case EXCP_ILLEGAL: - case EXCP_DIV: - case EXCP_TRAP: - do_exception(cpu, cpu->exception_addr, 0, false); - break; - - case EXCP_BREAK: - do_exception(cpu, cpu->exception_addr, 0, true); - break; - - case EXCP_SEMIHOST: - do_nios2_semihosting(env); - break; - - default: - cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); - } -} - -hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - target_ulong vaddr, paddr = 0; - Nios2MMULookup lu; - unsigned int hit; - - if (cpu->mmu_present && (addr < 0xC0000000)) { - hit = mmu_translate(env, &lu, addr, 0, 0); - if (hit) { - vaddr = addr & TARGET_PAGE_MASK; - paddr = lu.paddr + vaddr - lu.vaddr; - } else { - paddr = -1; - qemu_log("cpu_get_phys_page debug MISS: %#" PRIx64 "\n", addr); - } - } else { - paddr = addr & TARGET_PAGE_MASK; - } - - return paddr; -} - -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; - - env->ctrl[CR_BADADDR] = addr; - cs->exception_index = EXCP_UNALIGN; - nios2_cpu_loop_exit_advance(env, retaddr); -} - -bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - unsigned int excp; - target_ulong vaddr, paddr; - Nios2MMULookup lu; - unsigned int hit; - - if (!cpu->mmu_present) { - /* No MMU */ - address &= TARGET_PAGE_MASK; - tlb_set_page(cs, address, address, PAGE_BITS, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - if (MMU_SUPERVISOR_IDX == mmu_idx) { - if (address >= 0xC0000000) { - /* Kernel physical page - TLB bypassed */ - address &= TARGET_PAGE_MASK; - tlb_set_page(cs, address, address, PAGE_BITS, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - } else { - if (address >= 0x80000000) { - /* Illegal access from user mode */ - if (probe) { - return false; - } - cs->exception_index = (access_type == MMU_INST_FETCH - ? EXCP_SUPERA_X : EXCP_SUPERA_D); - env->ctrl[CR_BADADDR] = address; - nios2_cpu_loop_exit_advance(env, retaddr); - } - } - - /* Virtual page. */ - hit = mmu_translate(env, &lu, address, access_type, mmu_idx); - if (hit) { - vaddr = address & TARGET_PAGE_MASK; - paddr = lu.paddr + vaddr - lu.vaddr; - - if (((access_type == MMU_DATA_LOAD) && (lu.prot & PAGE_READ)) || - ((access_type == MMU_DATA_STORE) && (lu.prot & PAGE_WRITE)) || - ((access_type == MMU_INST_FETCH) && (lu.prot & PAGE_EXEC))) { - tlb_set_page(cs, vaddr, paddr, lu.prot, - mmu_idx, TARGET_PAGE_SIZE); - return true; - } - - /* Permission violation */ - excp = (access_type == MMU_DATA_LOAD ? EXCP_PERM_R : - access_type == MMU_DATA_STORE ? EXCP_PERM_W : EXCP_PERM_X); - } else { - excp = (access_type == MMU_INST_FETCH ? EXCP_TLB_X: EXCP_TLB_D); - } - - if (probe) { - return false; - } - - env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], CR_TLBMISC, D, - access_type != MMU_INST_FETCH); - env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], CR_PTEADDR, VPN, - address >> TARGET_PAGE_BITS); - env->mmu.pteaddr_wr = env->ctrl[CR_PTEADDR]; - - cs->exception_index = excp; - env->ctrl[CR_BADADDR] = address; - nios2_cpu_loop_exit_advance(env, retaddr); -} diff --git a/target/nios2/helper.h b/target/nios2/helper.h deleted file mode 100644 index 1648d76ade..0000000000 --- a/target/nios2/helper.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Altera Nios II helper routines header. - * - * Copyright (c) 2012 Chris Wulff - * - * 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_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, s32, env, s32, s32) -DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32) - -#if !defined(CONFIG_USER_ONLY) -DEF_HELPER_3(eret, noreturn, env, i32, i32) -DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32) -DEF_HELPER_3(wrprs, void, env, i32, i32) -DEF_HELPER_2(mmu_write_tlbacc, void, env, i32) -DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32) -DEF_HELPER_2(mmu_write_pteaddr, void, env, i32) -#endif diff --git a/target/nios2/meson.build b/target/nios2/meson.build deleted file mode 100644 index c6e2243cc3..0000000000 --- a/target/nios2/meson.build +++ /dev/null @@ -1,17 +0,0 @@ -nios2_ss = ss.source_set() -nios2_ss.add(files( - 'cpu.c', - 'op_helper.c', - 'translate.c', -)) - -nios2_softmmu_ss = ss.source_set() -nios2_softmmu_ss.add(files( - 'helper.c', - 'monitor.c', - 'mmu.c', - 'nios2-semi.c', -)) - -target_arch += {'nios2': nios2_ss} -target_softmmu_arch += {'nios2': nios2_softmmu_ss} diff --git a/target/nios2/mmu.c b/target/nios2/mmu.c deleted file mode 100644 index d9b690b78e..0000000000 --- a/target/nios2/mmu.c +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Altera Nios II MMU emulation for qemu. - * - * Copyright (C) 2012 Chris Wulff - * - * 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/qemu-print.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "mmu.h" -#include "exec/helper-proto.h" -#include "trace/trace-target_nios2.h" - - -/* rw - 0 = read, 1 = write, 2 = fetch. */ -unsigned int mmu_translate(CPUNios2State *env, - Nios2MMULookup *lu, - target_ulong vaddr, int rw, int mmu_idx) -{ - Nios2CPU *cpu = env_archcpu(env); - int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - int vpn = vaddr >> 12; - int way, n_ways = cpu->tlb_num_ways; - - for (way = 0; way < n_ways; way++) { - uint32_t index = (way * n_ways) + (vpn & env->mmu.tlb_entry_mask); - Nios2TLBEntry *entry = &env->mmu.tlb[index]; - - if (((entry->tag >> 12) != vpn) || - (((entry->tag & (1 << 11)) == 0) && - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) != pid))) { - trace_nios2_mmu_translate_miss(vaddr, pid, index, entry->tag); - continue; - } - - lu->vaddr = vaddr & TARGET_PAGE_MASK; - lu->paddr = FIELD_EX32(entry->data, CR_TLBACC, PFN) << TARGET_PAGE_BITS; - lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) | - ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) | - ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0); - - trace_nios2_mmu_translate_hit(vaddr, pid, index, lu->paddr, lu->prot); - return 1; - } - return 0; -} - -static void mmu_flush_pid(CPUNios2State *env, uint32_t pid) -{ - CPUState *cs = env_cpu(env); - Nios2CPU *cpu = env_archcpu(env); - int idx; - - for (idx = 0; idx < cpu->tlb_num_entries; idx++) { - Nios2TLBEntry *entry = &env->mmu.tlb[idx]; - - if ((entry->tag & (1 << 10)) && (!(entry->tag & (1 << 11))) && - ((entry->tag & ((1 << cpu->pid_num_bits) - 1)) == pid)) { - uint32_t vaddr = entry->tag & TARGET_PAGE_MASK; - - trace_nios2_mmu_flush_pid_hit(pid, idx, vaddr); - tlb_flush_page(cs, vaddr); - } else { - trace_nios2_mmu_flush_pid_miss(pid, idx, entry->tag); - } - } -} - -void helper_mmu_write_tlbacc(CPUNios2State *env, uint32_t v) -{ - CPUState *cs = env_cpu(env); - Nios2CPU *cpu = env_archcpu(env); - - trace_nios2_mmu_write_tlbacc(FIELD_EX32(v, CR_TLBACC, IG), - (v & CR_TLBACC_C) ? 'C' : '.', - (v & CR_TLBACC_R) ? 'R' : '.', - (v & CR_TLBACC_W) ? 'W' : '.', - (v & CR_TLBACC_X) ? 'X' : '.', - (v & CR_TLBACC_G) ? 'G' : '.', - FIELD_EX32(v, CR_TLBACC, PFN)); - - /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */ - if (env->ctrl[CR_TLBMISC] & CR_TLBMISC_WE) { - int way = FIELD_EX32(env->ctrl[CR_TLBMISC], CR_TLBMISC, WAY); - int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); - int pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - int g = FIELD_EX32(v, CR_TLBACC, G); - int valid = FIELD_EX32(vpn, CR_TLBACC, PFN) < 0xC0000; - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid; - uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W | - CR_TLBACC_X | R_CR_TLBACC_PFN_MASK); - - if ((entry->tag != newTag) || (entry->data != newData)) { - if (entry->tag & (1 << 10)) { - /* Flush existing entry */ - tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK); - } - entry->tag = newTag; - entry->data = newData; - } - /* Auto-increment tlbmisc.WAY */ - env->ctrl[CR_TLBMISC] = FIELD_DP32(env->ctrl[CR_TLBMISC], - CR_TLBMISC, WAY, - (way + 1) & (cpu->tlb_num_ways - 1)); - } - - /* Writes to TLBACC don't change the read-back value */ - env->mmu.tlbacc_wr = v; -} - -void helper_mmu_write_tlbmisc(CPUNios2State *env, uint32_t v) -{ - Nios2CPU *cpu = env_archcpu(env); - uint32_t new_pid = FIELD_EX32(v, CR_TLBMISC, PID); - uint32_t old_pid = FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID); - uint32_t way = FIELD_EX32(v, CR_TLBMISC, WAY); - - trace_nios2_mmu_write_tlbmisc(way, - (v & CR_TLBMISC_RD) ? 'R' : '.', - (v & CR_TLBMISC_WE) ? 'W' : '.', - (v & CR_TLBMISC_DBL) ? '2' : '.', - (v & CR_TLBMISC_BAD) ? 'B' : '.', - (v & CR_TLBMISC_PERM) ? 'P' : '.', - (v & CR_TLBMISC_D) ? 'D' : '.', - new_pid); - - if (new_pid != old_pid) { - mmu_flush_pid(env, old_pid); - } - - /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */ - if (v & CR_TLBMISC_RD) { - int vpn = FIELD_EX32(env->mmu.pteaddr_wr, CR_PTEADDR, VPN); - Nios2TLBEntry *entry = - &env->mmu.tlb[(way * cpu->tlb_num_ways) + - (vpn & env->mmu.tlb_entry_mask)]; - - env->ctrl[CR_TLBACC] &= R_CR_TLBACC_IG_MASK; - env->ctrl[CR_TLBACC] |= entry->data; - env->ctrl[CR_TLBACC] |= (entry->tag & (1 << 11)) ? CR_TLBACC_G : 0; - env->ctrl[CR_TLBMISC] = FIELD_DP32(v, CR_TLBMISC, PID, - entry->tag & - ((1 << cpu->pid_num_bits) - 1)); - env->ctrl[CR_PTEADDR] = FIELD_DP32(env->ctrl[CR_PTEADDR], - CR_PTEADDR, VPN, - entry->tag >> TARGET_PAGE_BITS); - } else { - env->ctrl[CR_TLBMISC] = v; - } - - env->mmu.tlbmisc_wr = v; -} - -void helper_mmu_write_pteaddr(CPUNios2State *env, uint32_t v) -{ - trace_nios2_mmu_write_pteaddr(FIELD_EX32(v, CR_PTEADDR, PTBASE), - FIELD_EX32(v, CR_PTEADDR, VPN)); - - /* Writes to PTEADDR don't change the read-back VPN value */ - env->ctrl[CR_PTEADDR] = ((v & ~R_CR_PTEADDR_VPN_MASK) | - (env->ctrl[CR_PTEADDR] & R_CR_PTEADDR_VPN_MASK)); - env->mmu.pteaddr_wr = v; -} - -void mmu_init(CPUNios2State *env) -{ - Nios2CPU *cpu = env_archcpu(env); - Nios2MMU *mmu = &env->mmu; - - mmu->tlb_entry_mask = (cpu->tlb_num_entries / cpu->tlb_num_ways) - 1; - mmu->tlb = g_new0(Nios2TLBEntry, cpu->tlb_num_entries); -} - -void dump_mmu(CPUNios2State *env) -{ - Nios2CPU *cpu = env_archcpu(env); - int i; - - qemu_printf("MMU: ways %d, entries %d, pid bits %d\n", - cpu->tlb_num_ways, cpu->tlb_num_entries, - cpu->pid_num_bits); - - for (i = 0; i < cpu->tlb_num_entries; i++) { - Nios2TLBEntry *entry = &env->mmu.tlb[i]; - qemu_printf("TLB[%d] = %08X %08X %c VPN %05X " - "PID %02X %c PFN %05X %c%c%c%c\n", - i, entry->tag, entry->data, - (entry->tag & (1 << 10)) ? 'V' : '-', - entry->tag >> 12, - entry->tag & ((1 << cpu->pid_num_bits) - 1), - (entry->tag & (1 << 11)) ? 'G' : '-', - FIELD_EX32(entry->data, CR_TLBACC, PFN), - (entry->data & CR_TLBACC_C) ? 'C' : '-', - (entry->data & CR_TLBACC_R) ? 'R' : '-', - (entry->data & CR_TLBACC_W) ? 'W' : '-', - (entry->data & CR_TLBACC_X) ? 'X' : '-'); - } -} diff --git a/target/nios2/mmu.h b/target/nios2/mmu.h deleted file mode 100644 index 5b085900fb..0000000000 --- a/target/nios2/mmu.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Altera Nios II MMU emulation for qemu. - * - * Copyright (C) 2012 Chris Wulff - * - * 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 NIOS2_MMU_H -#define NIOS2_MMU_H - -#include "cpu.h" - -typedef struct Nios2TLBEntry { - target_ulong tag; - target_ulong data; -} Nios2TLBEntry; - -typedef struct Nios2MMU { - int tlb_entry_mask; - uint32_t pteaddr_wr; - uint32_t tlbacc_wr; - uint32_t tlbmisc_wr; - Nios2TLBEntry *tlb; -} Nios2MMU; - -typedef struct Nios2MMULookup { - target_ulong vaddr; - target_ulong paddr; - int prot; -} Nios2MMULookup; - -void mmu_flip_um(CPUNios2State *env, unsigned int um); -unsigned int mmu_translate(CPUNios2State *env, - Nios2MMULookup *lu, - target_ulong vaddr, int rw, int mmu_idx); -void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v); -void mmu_init(CPUNios2State *env); - -#endif /* NIOS2_MMU_H */ diff --git a/target/nios2/monitor.c b/target/nios2/monitor.c deleted file mode 100644 index 0152dec3fa..0000000000 --- a/target/nios2/monitor.c +++ /dev/null @@ -1,35 +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 "cpu.h" -#include "monitor/monitor.h" -#include "monitor/hmp-target.h" -#include "monitor/hmp.h" - -void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env1 = mon_get_cpu_env(mon); - - dump_mmu(env1); -} diff --git a/target/nios2/nios2-semi.c b/target/nios2/nios2-semi.c deleted file mode 100644 index f76e8588c5..0000000000 --- a/target/nios2/nios2-semi.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Nios II Semihosting syscall interface. - * This code is derived from m68k-semi.c. - * The semihosting protocol implemented here is described in the - * libgloss sources: - * https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=libgloss/nios2/nios2-semi.txt;hb=HEAD - * - * Copyright (c) 2017-2019 Mentor Graphics - * - * 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 "cpu.h" -#include "exec/gdbstub.h" -#include "semihosting/syscalls.h" -#include "semihosting/softmmu-uaccess.h" -#include "qemu/log.h" - -#define HOSTED_EXIT 0 -#define HOSTED_INIT_SIM 1 -#define HOSTED_OPEN 2 -#define HOSTED_CLOSE 3 -#define HOSTED_READ 4 -#define HOSTED_WRITE 5 -#define HOSTED_LSEEK 6 -#define HOSTED_RENAME 7 -#define HOSTED_UNLINK 8 -#define HOSTED_STAT 9 -#define HOSTED_FSTAT 10 -#define HOSTED_GETTIMEOFDAY 11 -#define HOSTED_ISATTY 12 -#define HOSTED_SYSTEM 13 - -static int host_to_gdb_errno(int err) -{ -#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 nios2_semi_u32_cb(CPUState *cs, uint64_t ret, int err) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - target_ulong args = env->regs[R_ARG1]; - - if (put_user_u32(ret, args) || - 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. - * It is always a guest error not to pass us a valid argument block. - */ - qemu_log_mask(LOG_GUEST_ERROR, "nios2-semihosting: return value " - "discarded because argument block not writable\n"); - } -} - -static void nios2_semi_u64_cb(CPUState *cs, uint64_t ret, int err) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - target_ulong args = env->regs[R_ARG1]; - - if (put_user_u32(ret >> 32, args) || - put_user_u32(ret, args + 4) || - 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"); - } -} - -/* - * 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)) { \ - 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; - - 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]); - - case HOSTED_OPEN: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - semihost_sys_open(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); - break; - - case HOSTED_CLOSE: - 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); - semihost_sys_read(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_WRITE: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_write(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_LSEEK: - GET_ARG64(0); - GET_ARG64(1); - GET_ARG64(2); - GET_ARG64(3); - semihost_sys_lseek(cs, nios2_semi_u64_cb, arg0, - deposit64(arg2, arg1, 32, 32), arg3); - break; - - case HOSTED_RENAME: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - GET_ARG(3); - semihost_sys_rename(cs, nios2_semi_u32_cb, arg0, arg1, arg2, arg3); - break; - - case HOSTED_UNLINK: - GET_ARG(0); - GET_ARG(1); - semihost_sys_remove(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_STAT: - GET_ARG(0); - GET_ARG(1); - GET_ARG(2); - semihost_sys_stat(cs, nios2_semi_u32_cb, arg0, arg1, arg2); - break; - - case HOSTED_FSTAT: - GET_ARG(0); - GET_ARG(1); - semihost_sys_fstat(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_GETTIMEOFDAY: - GET_ARG(0); - GET_ARG(1); - semihost_sys_gettimeofday(cs, nios2_semi_u32_cb, arg0, arg1); - break; - - case HOSTED_ISATTY: - GET_ARG(0); - semihost_sys_isatty(cs, nios2_semi_u32_cb, arg0); - break; - - case HOSTED_SYSTEM: - GET_ARG(0); - GET_ARG(1); - 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); - 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; - } -} diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c deleted file mode 100644 index 0aaf33ffc2..0000000000 --- a/target/nios2/op_helper.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Altera Nios II helper routines. - * - * Copyright (C) 2012 Chris Wulff - * - * 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" -#include "exec/exec-all.h" -#include "qemu/main-loop.h" - -void helper_raise_exception(CPUNios2State *env, uint32_t index) -{ - CPUState *cs = env_cpu(env); - cs->exception_index = index; - cpu_loop_exit(cs); -} - -void nios2_cpu_loop_exit_advance(CPUNios2State *env, uintptr_t retaddr) -{ - CPUState *cs = env_cpu(env); - - /* - * Note that PC is advanced for all hardware exceptions. - * Do this here, rather than in restore_state_to_opc(), - * lest we affect QEMU internal exceptions, like EXCP_DEBUG. - */ - cpu_restore_state(cs, retaddr); - env->pc += 4; - cpu_loop_exit(cs); -} - -static void maybe_raise_div(CPUNios2State *env, uintptr_t ra) -{ - Nios2CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (cpu->diverr_present) { - cs->exception_index = EXCP_DIV; - nios2_cpu_loop_exit_advance(env, ra); - } -} - -int32_t helper_divs(CPUNios2State *env, int32_t num, int32_t den) -{ - if (unlikely(den == 0) || unlikely(den == -1 && num == INT32_MIN)) { - maybe_raise_div(env, GETPC()); - return num; /* undefined */ - } - return num / den; -} - -uint32_t helper_divu(CPUNios2State *env, uint32_t num, uint32_t den) -{ - if (unlikely(den == 0)) { - maybe_raise_div(env, GETPC()); - return num; /* undefined */ - } - return num / den; -} - -#ifndef CONFIG_USER_ONLY -void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc) -{ - Nios2CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - - if (unlikely(new_pc & 3)) { - env->ctrl[CR_BADADDR] = new_pc; - cs->exception_index = EXCP_UNALIGND; - nios2_cpu_loop_exit_advance(env, GETPC()); - } - - /* - * None of estatus, bstatus, or sstatus have constraints on write; - * do not allow reserved fields in status to be set. - * When shadow registers are enabled, eret *does* restore CRS. - * Rather than testing eic_present to decide, mask CRS out of - * the set of readonly fields. - */ - new_status &= cpu->cr_state[CR_STATUS].writable | - (cpu->cr_state[CR_STATUS].readonly & R_CR_STATUS_CRS_MASK); - - env->ctrl[CR_STATUS] = new_status; - env->pc = new_pc; - nios2_update_crs(env); - cpu_loop_exit(cs); -} - -/* - * RDPRS and WRPRS are implemented out of line so that if PRS == CRS, - * all of the tcg global temporaries are synced back to ENV. - */ -uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno) -{ - unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); - return env->shadow_regs[prs][regno]; -} - -void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val) -{ - unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS); - env->shadow_regs[prs][regno] = val; -} -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/nios2/trace-events b/target/nios2/trace-events deleted file mode 100644 index 07f1f0a5e7..0000000000 --- a/target/nios2/trace-events +++ /dev/null @@ -1,10 +0,0 @@ -# mmu.c -nios2_mmu_translate_miss(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t tag) "mmu_translate: MISS vaddr=0x%08x pid=%u TLB[%u] tag=0x%08x" -nios2_mmu_translate_hit(uint32_t vaddr, uint32_t pid, uint32_t index, uint32_t paddr, uint32_t prot) "mmu_translate: HIT vaddr=0x%08x pid=%u TLB[%u] paddr=0x%08x prot=0x%x" - -nios2_mmu_flush_pid_miss(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: MISS pid=%u TLB[%u] tag=0x%08x" -nios2_mmu_flush_pid_hit(uint32_t pid, uint32_t index, uint32_t vaddr) "mmu_flush: HIT pid=%u TLB[%u] vaddr=0x%08x" - -nios2_mmu_write_tlbacc(uint32_t ig, char c, char r, char w, char x, char g, uint32_t pfn) "mmu_write_tlbacc: ig=0x%02x flags=%c%c%c%c%c pfn=0x%08x" -nios2_mmu_write_tlbmisc(uint32_t way, char r, char w, char t, char b, char p, char d, uint32_t pid) "mmu_write_tlbmisc: way=0x%x flags=%c%c%c%c%c%c pid=%u" -nios2_mmu_write_pteaddr(uint32_t ptb, uint32_t vpn) "mmu_write_pteaddr: ptbase=0x%03x vpn=0x%05x" diff --git a/target/nios2/translate.c b/target/nios2/translate.c deleted file mode 100644 index 4db8b47744..0000000000 --- a/target/nios2/translate.c +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * Altera Nios II emulation for qemu: main translation routines. - * - * Copyright (C) 2016 Marek Vasut - * Copyright (C) 2012 Chris Wulff - * Copyright (C) 2010 Tobias Klauser - * (Portions of this file that were originally from nios2sim-ng.) - * - * 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 "tcg/tcg-op.h" -#include "exec/exec-all.h" -#include "disas/disas.h" -#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" - -/* is_jmp field values */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ - -#define INSTRUCTION_FLG(func, flags) { (func), (flags) } -#define INSTRUCTION(func) \ - INSTRUCTION_FLG(func, 0) -#define INSTRUCTION_NOP() \ - INSTRUCTION_FLG(nop, 0) -#define INSTRUCTION_UNIMPLEMENTED() \ - INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL) -#define INSTRUCTION_ILLEGAL() \ - INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL) - -/* Special R-Type instruction opcode */ -#define INSN_R_TYPE 0x3A - -/* I-Type instruction parsing */ -typedef struct { - uint8_t op; - union { - uint16_t u; - int16_t s; - } imm16; - uint8_t b; - uint8_t a; -} InstrIType; - -#define I_TYPE(instr, code) \ - InstrIType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm16.u = extract32((code), 6, 16), \ - .b = extract32((code), 22, 5), \ - .a = extract32((code), 27, 5), \ - } - -typedef target_ulong ImmFromIType(const InstrIType *); - -static target_ulong imm_unsigned(const InstrIType *i) -{ - return i->imm16.u; -} - -static target_ulong imm_signed(const InstrIType *i) -{ - return i->imm16.s; -} - -static target_ulong imm_shifted(const InstrIType *i) -{ - return i->imm16.u << 16; -} - -/* R-Type instruction parsing */ -typedef struct { - uint8_t op; - uint8_t imm5; - uint8_t opx; - uint8_t c; - uint8_t b; - uint8_t a; -} InstrRType; - -#define R_TYPE(instr, code) \ - InstrRType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm5 = extract32((code), 6, 5), \ - .opx = extract32((code), 11, 6), \ - .c = extract32((code), 17, 5), \ - .b = extract32((code), 22, 5), \ - .a = extract32((code), 27, 5), \ - } - -/* J-Type instruction parsing */ -typedef struct { - uint8_t op; - uint32_t imm26; -} InstrJType; - -#define J_TYPE(instr, code) \ - InstrJType (instr) = { \ - .op = extract32((code), 0, 6), \ - .imm26 = extract32((code), 6, 26), \ - } - -typedef void GenFn2i(TCGv, TCGv, target_long); -typedef void GenFn3(TCGv, TCGv, TCGv); -typedef void GenFn4(TCGv, TCGv, TCGv, TCGv); - -typedef struct DisasContext { - DisasContextBase base; - target_ulong pc; - int mem_idx; - uint32_t tb_flags; - TCGv sink; - const ControlRegState *cr_state; - bool eic_present; -} DisasContext; - -static TCGv cpu_R[NUM_GP_REGS]; -static TCGv cpu_pc; -#ifndef CONFIG_USER_ONLY -static TCGv cpu_crs_R[NUM_GP_REGS]; -#endif - -typedef struct Nios2Instruction { - void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); - uint32_t flags; -} Nios2Instruction; - -static uint8_t get_opcode(uint32_t code) -{ - I_TYPE(instr, code); - return instr.op; -} - -static uint8_t get_opxcode(uint32_t code) -{ - R_TYPE(instr, code); - return instr.opx; -} - -static TCGv load_gpr(DisasContext *dc, unsigned reg) -{ - assert(reg < NUM_GP_REGS); - - /* - * With shadow register sets, register r0 does not necessarily contain 0, - * but it is overwhelmingly likely that it does -- software is supposed - * to have set r0 to 0 in every shadow register set before use. - */ - if (unlikely(reg == R_ZERO) && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { - return tcg_constant_tl(0); - } - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - return cpu_R[reg]; - } -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - return cpu_crs_R[reg]; -#endif -} - -static TCGv dest_gpr(DisasContext *dc, unsigned reg) -{ - assert(reg < NUM_GP_REGS); - - /* - * The spec for shadow register sets isn't clear, but we assume that - * writes to r0 are discarded regardless of CRS. - */ - if (unlikely(reg == R_ZERO)) { - if (dc->sink == NULL) { - dc->sink = tcg_temp_new(); - } - return dc->sink; - } - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - return cpu_R[reg]; - } -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - return cpu_crs_R[reg]; -#endif -} - -static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) -{ - /* Note that PC is advanced for all hardware exceptions. */ - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - gen_helper_raise_exception(cpu_env, tcg_constant_i32(index)); - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) -{ - const TranslationBlock *tb = dc->base.tb; - - if (translator_use_goto_tb(&dc->base, dest)) { - tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb(tb, n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_lookup_and_goto_ptr(); - } - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_jumpr(DisasContext *dc, int regno, bool is_call) -{ - TCGLabel *l = gen_new_label(); - TCGv test = tcg_temp_new(); - TCGv dest = load_gpr(dc, regno); - - tcg_gen_andi_tl(test, dest, 3); - tcg_gen_brcondi_tl(TCG_COND_NE, test, 0, l); - tcg_temp_free(test); - - tcg_gen_mov_tl(cpu_pc, dest); - if (is_call) { - tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); - } - tcg_gen_lookup_and_goto_ptr(); - - gen_set_label(l); - tcg_gen_st_tl(dest, cpu_env, offsetof(CPUNios2State, ctrl[CR_BADADDR])); - t_gen_helper_raise_exception(dc, EXCP_UNALIGND); - - dc->base.is_jmp = DISAS_NORETURN; -} - -static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) -{ - t_gen_helper_raise_exception(dc, flags); -} - -static bool gen_check_supervisor(DisasContext *dc) -{ - if (FIELD_EX32(dc->tb_flags, TBFLAGS, U)) { - /* CPU in user mode, privileged instruction called, stop. */ - t_gen_helper_raise_exception(dc, EXCP_SUPERI); - return false; - } - return true; -} - -/* - * Used as a placeholder for all instructions which do not have - * an effect on the simulator (e.g. flush, sync) - */ -static void nop(DisasContext *dc, uint32_t code, uint32_t flags) -{ - /* Nothing to do here */ -} - -/* - * J-Type instructions - */ -static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) -{ - J_TYPE(instr, code); - gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); -} - -static void call(DisasContext *dc, uint32_t code, uint32_t flags) -{ - tcg_gen_movi_tl(dest_gpr(dc, R_RA), dc->base.pc_next); - jmpi(dc, code, flags); -} - -/* - * I-Type instructions - */ -/* Load instructions */ -static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - TCGv addr = tcg_temp_new(); - TCGv data = dest_gpr(dc, instr.b); - - tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); - tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); - tcg_temp_free(addr); -} - -/* Store instructions */ -static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - TCGv val = load_gpr(dc, instr.b); - - TCGv addr = tcg_temp_new(); - tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); - tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); - tcg_temp_free(addr); -} - -/* Branch instructions */ -static void br(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4)); -} - -static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - I_TYPE(instr, code); - - TCGLabel *l1 = gen_new_label(); - tcg_gen_brcond_tl(flags, load_gpr(dc, instr.a), load_gpr(dc, instr.b), l1); - gen_goto_tb(dc, 0, dc->base.pc_next); - gen_set_label(l1); - gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4)); -} - -/* Comparison instructions */ -static void do_i_cmpxx(DisasContext *dc, uint32_t insn, - TCGCond cond, ImmFromIType *imm) -{ - I_TYPE(instr, insn); - tcg_gen_setcondi_tl(cond, dest_gpr(dc, instr.b), - load_gpr(dc, instr.a), imm(&instr)); -} - -#define gen_i_cmpxx(fname, imm) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_i_cmpxx(dc, code, flags, imm); } - -gen_i_cmpxx(gen_cmpxxsi, imm_signed) -gen_i_cmpxx(gen_cmpxxui, imm_unsigned) - -/* Math/logic instructions */ -static void do_i_math_logic(DisasContext *dc, uint32_t insn, - GenFn2i *fn, ImmFromIType *imm, - bool x_op_0_eq_x) -{ - I_TYPE(instr, insn); - target_ulong val; - - if (unlikely(instr.b == R_ZERO)) { - /* Store to R_ZERO is ignored -- this catches the canonical NOP. */ - return; - } - - val = imm(&instr); - - if (instr.a == R_ZERO && FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0)) { - /* This catches the canonical expansions of movi and movhi. */ - tcg_gen_movi_tl(dest_gpr(dc, instr.b), x_op_0_eq_x ? val : 0); - } else { - fn(dest_gpr(dc, instr.b), load_gpr(dc, instr.a), val); - } -} - -#define gen_i_math_logic(fname, insn, x_op_0, imm) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_i_math_logic(dc, code, tcg_gen_##insn##_tl, imm, x_op_0); } - -gen_i_math_logic(addi, addi, 1, imm_signed) -gen_i_math_logic(muli, muli, 0, imm_signed) - -gen_i_math_logic(andi, andi, 0, imm_unsigned) -gen_i_math_logic(ori, ori, 1, imm_unsigned) -gen_i_math_logic(xori, xori, 1, imm_unsigned) - -gen_i_math_logic(andhi, andi, 0, imm_shifted) -gen_i_math_logic(orhi , ori, 1, imm_shifted) -gen_i_math_logic(xorhi, xori, 1, imm_shifted) - -/* rB <- prs.rA + sigma(IMM16) */ -static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!dc->eic_present) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - I_TYPE(instr, code); - TCGv dest = dest_gpr(dc, instr.b); - gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a)); - tcg_gen_addi_tl(dest, dest, instr.imm16.s); -#endif -} - -/* Prototype only, defined below */ -static void handle_r_type_instr(DisasContext *dc, uint32_t code, - uint32_t flags); - -static const Nios2Instruction i_type_instructions[] = { - INSTRUCTION(call), /* call */ - INSTRUCTION(jmpi), /* jmpi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbu */ - INSTRUCTION(addi), /* addi */ - INSTRUCTION_FLG(gen_stx, MO_UB), /* stb */ - INSTRUCTION(br), /* br */ - INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldb */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhu */ - INSTRUCTION(andi), /* andi */ - INSTRUCTION_FLG(gen_stx, MO_UW), /* sth */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ - INSTRUCTION_FLG(gen_ldx, MO_SW), /* 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_bxx, TCG_COND_LT), /* blt */ - INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldw */ - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_NOP(), /* flushda */ - INSTRUCTION(xori), /* xori */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_bxx, TCG_COND_NE), /* bne */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ), /* cmpeqi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbuio */ - INSTRUCTION(muli), /* muli */ - INSTRUCTION_FLG(gen_stx, MO_UB), /* stbio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ), /* beq */ - INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldbio */ - INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhuio */ - INSTRUCTION(andhi), /* andhi */ - INSTRUCTION_FLG(gen_stx, MO_UW), /* sthio */ - INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ - INSTRUCTION_FLG(gen_ldx, MO_SW), /* 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_bxx, TCG_COND_LTU), /* bltu */ - INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ - INSTRUCTION(rdprs), /* rdprs */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ - INSTRUCTION_NOP(), /* flushd */ - INSTRUCTION(xorhi), /* xorhi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), -}; - -/* - * R-Type instructions - */ -/* - * status <- estatus - * PC <- ea - */ -static void eret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - if (FIELD_EX32(dc->tb_flags, TBFLAGS, CRS0)) { - TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_ESTATUS])); - gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_EA)); - tcg_temp_free(tmp); - } else { - gen_helper_eret(cpu_env, load_gpr(dc, R_SSTATUS), load_gpr(dc, R_EA)); - } - dc->base.is_jmp = DISAS_NORETURN; -#endif -} - -/* PC <- ra */ -static void ret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - gen_jumpr(dc, R_RA, false); -} - -/* - * status <- bstatus - * PC <- ba - */ -static void bret(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - TCGv tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPUNios2State, ctrl[CR_BSTATUS])); - gen_helper_eret(cpu_env, tmp, load_gpr(dc, R_BA)); - tcg_temp_free(tmp); - - dc->base.is_jmp = DISAS_NORETURN; -#endif -} - -/* PC <- rA */ -static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - gen_jumpr(dc, instr.a, false); -} - -/* rC <- PC + 4 */ -static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - tcg_gen_movi_tl(dest_gpr(dc, instr.c), dc->base.pc_next); -} - -/* - * ra <- PC + 4 - * PC <- rA - */ -static void callr(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - - gen_jumpr(dc, instr.a, true); -} - -/* rC <- ctlN */ -static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - TCGv t1, t2, dest = dest_gpr(dc, instr.c); - - /* Reserved registers read as zero. */ - if (nios2_cr_reserved(&dc->cr_state[instr.imm5])) { - tcg_gen_movi_tl(dest, 0); - return; - } - - switch (instr.imm5) { - case CR_IPENDING: - /* - * The value of the ipending register is synthetic. - * In hw, this is the AND of a set of hardware irq lines - * with the ienable register. In qemu, we re-use the space - * of CR_IPENDING to store the set of irq lines, and so we - * must perform the AND here, and anywhere else we need the - * guest value of ipending. - */ - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - tcg_gen_ld_tl(t1, cpu_env, offsetof(CPUNios2State, ctrl[CR_IPENDING])); - tcg_gen_ld_tl(t2, cpu_env, offsetof(CPUNios2State, ctrl[CR_IENABLE])); - tcg_gen_and_tl(dest, t1, t2); - tcg_temp_free(t1); - tcg_temp_free(t2); - break; - default: - tcg_gen_ld_tl(dest, cpu_env, - offsetof(CPUNios2State, ctrl[instr.imm5])); - break; - } -#endif -} - -/* ctlN <- rA */ -static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - TCGv v = load_gpr(dc, instr.a); - uint32_t ofs = offsetof(CPUNios2State, ctrl[instr.imm5]); - uint32_t wr = dc->cr_state[instr.imm5].writable; - uint32_t ro = dc->cr_state[instr.imm5].readonly; - - /* Skip reserved or readonly registers. */ - if (wr == 0) { - return; - } - - switch (instr.imm5) { - case CR_PTEADDR: - gen_helper_mmu_write_pteaddr(cpu_env, v); - break; - case CR_TLBACC: - gen_helper_mmu_write_tlbacc(cpu_env, v); - break; - case CR_TLBMISC: - gen_helper_mmu_write_tlbmisc(cpu_env, v); - break; - case CR_STATUS: - case CR_IENABLE: - /* If interrupts were enabled using WRCTL, trigger them. */ - dc->base.is_jmp = DISAS_UPDATE; - /* fall through */ - default: - if (wr == -1) { - /* The register is entirely writable. */ - tcg_gen_st_tl(v, cpu_env, ofs); - } else { - /* - * The register is partially read-only or reserved: - * merge the value. - */ - TCGv n = tcg_temp_new(); - - tcg_gen_andi_tl(n, v, wr); - - if (ro != 0) { - TCGv o = tcg_temp_new(); - tcg_gen_ld_tl(o, cpu_env, ofs); - tcg_gen_andi_tl(o, o, ro); - tcg_gen_or_tl(n, n, o); - tcg_temp_free(o); - } - - tcg_gen_st_tl(n, cpu_env, ofs); - tcg_temp_free(n); - } - break; - } -#endif -} - -/* prs.rC <- rA */ -static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - if (!dc->eic_present) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - if (!gen_check_supervisor(dc)) { - return; - } - -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - R_TYPE(instr, code); - gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c), - load_gpr(dc, instr.a)); - /* - * The expected write to PRS[r0] is 0, from CRS[r0]. - * If not, and CRS == PRS (which we cannot tell from here), - * we may now have a non-zero value in our current r0. - * By ending the TB, we re-evaluate tb_flags and find out. - */ - if (instr.c == 0 - && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) { - dc->base.is_jmp = DISAS_UPDATE; - } -#endif -} - -/* Comparison instructions */ -static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, code); - tcg_gen_setcond_tl(flags, dest_gpr(dc, instr.c), - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -/* Math/logic instructions */ -static void do_ri_math_logic(DisasContext *dc, uint32_t insn, GenFn2i *fn) -{ - R_TYPE(instr, insn); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), instr.imm5); -} - -static void do_rr_math_logic(DisasContext *dc, uint32_t insn, GenFn3 *fn) -{ - R_TYPE(instr, insn); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -#define gen_ri_math_logic(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_ri_math_logic(dc, code, tcg_gen_##insn##_tl); } - -#define gen_rr_math_logic(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_math_logic(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_math_logic(add, add) -gen_rr_math_logic(sub, sub) -gen_rr_math_logic(mul, mul) - -gen_rr_math_logic(and, and) -gen_rr_math_logic(or, or) -gen_rr_math_logic(xor, xor) -gen_rr_math_logic(nor, nor) - -gen_ri_math_logic(srai, sari) -gen_ri_math_logic(srli, shri) -gen_ri_math_logic(slli, shli) -gen_ri_math_logic(roli, rotli) - -static void do_rr_mul_high(DisasContext *dc, uint32_t insn, GenFn4 *fn) -{ - R_TYPE(instr, insn); - TCGv discard = tcg_temp_new(); - - fn(discard, dest_gpr(dc, instr.c), - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); - tcg_temp_free(discard); -} - -#define gen_rr_mul_high(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_mul_high(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_mul_high(mulxss, muls2) -gen_rr_mul_high(mulxuu, mulu2) -gen_rr_mul_high(mulxsu, mulsu2) - -static void do_rr_shift(DisasContext *dc, uint32_t insn, GenFn3 *fn) -{ - R_TYPE(instr, insn); - TCGv sh = tcg_temp_new(); - - tcg_gen_andi_tl(sh, load_gpr(dc, instr.b), 31); - fn(dest_gpr(dc, instr.c), load_gpr(dc, instr.a), sh); - tcg_temp_free(sh); -} - -#define gen_rr_shift(fname, insn) \ - static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ - { do_rr_shift(dc, code, tcg_gen_##insn##_tl); } - -gen_rr_shift(sra, sar) -gen_rr_shift(srl, shr) -gen_rr_shift(sll, shl) -gen_rr_shift(rol, rotl) -gen_rr_shift(ror, rotr) - -static void divs(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, (code)); - gen_helper_divs(dest_gpr(dc, instr.c), cpu_env, - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -static void divu(DisasContext *dc, uint32_t code, uint32_t flags) -{ - R_TYPE(instr, (code)); - gen_helper_divu(dest_gpr(dc, instr.c), cpu_env, - load_gpr(dc, instr.a), load_gpr(dc, instr.b)); -} - -static void trap(DisasContext *dc, uint32_t code, uint32_t flags) -{ -#ifdef CONFIG_USER_ONLY - /* - * The imm5 field is not stored anywhere on real hw; the kernel - * has to load the insn and extract the field. But we can make - * 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, - offsetof(CPUNios2State, error_code)); -#endif - t_gen_helper_raise_exception(dc, EXCP_TRAP); -} - -static void gen_break(DisasContext *dc, uint32_t code, uint32_t flags) -{ -#ifndef CONFIG_USER_ONLY - /* The semihosting instruction is "break 1". */ - bool is_user = FIELD_EX32(dc->tb_flags, TBFLAGS, U); - R_TYPE(instr, code); - if (semihosting_enabled(is_user) && instr.imm5 == 1) { - t_gen_helper_raise_exception(dc, EXCP_SEMIHOST); - return; - } -#endif - - t_gen_helper_raise_exception(dc, EXCP_BREAK); -} - -static const Nios2Instruction r_type_instructions[] = { - INSTRUCTION_ILLEGAL(), - INSTRUCTION(eret), /* eret */ - INSTRUCTION(roli), /* roli */ - INSTRUCTION(rol), /* rol */ - INSTRUCTION_NOP(), /* flushp */ - INSTRUCTION(ret), /* ret */ - INSTRUCTION(nor), /* nor */ - INSTRUCTION(mulxuu), /* mulxuu */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE), /* cmpge */ - INSTRUCTION(bret), /* bret */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(ror), /* ror */ - INSTRUCTION_NOP(), /* flushi */ - INSTRUCTION(jmp), /* jmp */ - INSTRUCTION(and), /* and */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT), /* cmplt */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(slli), /* slli */ - INSTRUCTION(sll), /* sll */ - INSTRUCTION(wrprs), /* wrprs */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(or), /* or */ - INSTRUCTION(mulxsu), /* mulxsu */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE), /* cmpne */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(srli), /* srli */ - INSTRUCTION(srl), /* srl */ - INSTRUCTION(nextpc), /* nextpc */ - INSTRUCTION(callr), /* callr */ - INSTRUCTION(xor), /* xor */ - INSTRUCTION(mulxss), /* mulxss */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ), /* cmpeq */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(divu), /* divu */ - INSTRUCTION(divs), /* div */ - INSTRUCTION(rdctl), /* rdctl */ - INSTRUCTION(mul), /* mul */ - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU), /* cmpgeu */ - INSTRUCTION_NOP(), /* initi */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(trap), /* trap */ - INSTRUCTION(wrctl), /* wrctl */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ - INSTRUCTION(add), /* add */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(gen_break), /* break */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION(nop), /* nop */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION(sub), /* sub */ - INSTRUCTION(srai), /* srai */ - INSTRUCTION(sra), /* sra */ - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), - INSTRUCTION_ILLEGAL(), -}; - -static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags) -{ - uint8_t opx; - const Nios2Instruction *instr; - - opx = get_opxcode(code); - if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) { - goto illegal_op; - } - - instr = &r_type_instructions[opx]; - instr->handler(dc, code, instr->flags); - - return; - -illegal_op: - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); -} - -static const char * const gr_regnames[NUM_GP_REGS] = { - "zero", "at", "r2", "r3", - "r4", "r5", "r6", "r7", - "r8", "r9", "r10", "r11", - "r12", "r13", "r14", "r15", - "r16", "r17", "r18", "r19", - "r20", "r21", "r22", "r23", - "et", "bt", "gp", "sp", - "fp", "ea", "ba", "ra", -}; - -#ifndef CONFIG_USER_ONLY -static const char * const cr_regnames[NUM_CR_REGS] = { - "status", "estatus", "bstatus", "ienable", - "ipending", "cpuid", "res6", "exception", - "pteaddr", "tlbacc", "tlbmisc", "reserved1", - "badaddr", "config", "mpubase", "mpuacc", - "res16", "res17", "res18", "res19", - "res20", "res21", "res22", "res23", - "res24", "res25", "res26", "res27", - "res28", "res29", "res30", "res31", -}; -#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; - Nios2CPU *cpu = env_archcpu(env); - int page_insns; - - dc->mem_idx = cpu_mmu_index(env, false); - dc->cr_state = cpu->cr_state; - dc->tb_flags = dc->base.tb->flags; - dc->eic_present = cpu->eic_present; - - /* Bound the number of insns to execute to those left on the page. */ - page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; - dc->base.max_insns = MIN(page_insns, dc->base.max_insns); -} - -static void nios2_tr_tb_start(DisasContextBase *db, CPUState *cs) -{ -} - -static void nios2_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) -{ - tcg_gen_insn_start(dcbase->pc_next); -} - -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; - - pc = dc->base.pc_next; - dc->pc = pc; - dc->base.pc_next = pc + 4; - - /* Decode an instruction */ - code = cpu_ldl_code(env, pc); - op = get_opcode(code); - - if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { - t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); - return; - } - - dc->sink = NULL; - - 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) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - /* Indicate where the next block should start */ - switch (dc->base.is_jmp) { - case DISAS_TOO_MANY: - gen_goto_tb(dc, 0, dc->base.pc_next); - break; - - case DISAS_UPDATE: - /* Save the current PC, and return to the main loop. */ - tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); - tcg_gen_exit_tb(NULL, 0); - break; - - case DISAS_NORETURN: - /* nothing more to generate */ - break; - - default: - g_assert_not_reached(); - } -} - -static void nios2_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - -static const TranslatorOps nios2_tr_ops = { - .init_disas_context = nios2_tr_init_disas_context, - .tb_start = nios2_tr_tb_start, - .insn_start = nios2_tr_insn_start, - .translate_insn = nios2_tr_translate_insn, - .tb_stop = nios2_tr_tb_stop, - .disas_log = nios2_tr_disas_log, -}; - -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) -{ - DisasContext dc; - 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) -{ - Nios2CPU *cpu = NIOS2_CPU(cs); - CPUNios2State *env = &cpu->env; - int i; - - qemu_fprintf(f, "IN: PC=%x %s\n", env->pc, lookup_symbol(env->pc)); - - for (i = 0; i < NUM_GP_REGS; i++) { - qemu_fprintf(f, "%9s=%8.8x ", gr_regnames[i], env->regs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - -#if !defined(CONFIG_USER_ONLY) - int j; - - for (i = j = 0; i < NUM_CR_REGS; i++) { - if (!nios2_cr_reserved(&cpu->cr_state[i])) { - qemu_fprintf(f, "%9s=%8.8x ", cr_regnames[i], env->ctrl[i]); - if (++j % 4 == 0) { - qemu_fprintf(f, "\n"); - } - } - } - if (j % 4 != 0) { - qemu_fprintf(f, "\n"); - } - if (cpu->mmu_present) { - qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", - env->mmu.pteaddr_wr & R_CR_PTEADDR_VPN_MASK, - FIELD_EX32(env->mmu.tlbmisc_wr, CR_TLBMISC, PID), - env->mmu.tlbacc_wr); - } -#endif - qemu_fprintf(f, "\n\n"); -} - -void nios2_tcg_init(void) -{ -#ifndef CONFIG_USER_ONLY - TCGv_ptr crs = tcg_global_mem_new_ptr(cpu_env, - offsetof(CPUNios2State, regs), "crs"); - - for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_crs_R[i] = tcg_global_mem_new(crs, 4 * i, gr_regnames[i]); - } - -#define offsetof_regs0(N) offsetof(CPUNios2State, shadow_regs[0][N]) -#else -#define offsetof_regs0(N) offsetof(CPUNios2State, regs[N]) -#endif - - for (int i = 0; i < NUM_GP_REGS; i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof_regs0(i), - gr_regnames[i]); - } - -#undef offsetof_regs0 - - cpu_pc = tcg_global_mem_new(cpu_env, - offsetof(CPUNios2State, pc), "pc"); -} diff --git a/target/openrisc/Kconfig b/target/openrisc/Kconfig index e0da4ac1df..cd66c2e3b6 100644 --- a/target/openrisc/Kconfig +++ b/target/openrisc/Kconfig @@ -1,2 +1,3 @@ config OPENRISC bool + select DEVICE_TREE # needed by boot.c diff --git a/target/openrisc/cpu-param.h b/target/openrisc/cpu-param.h index 73be699f36..6169ed9f55 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -2,7 +2,7 @@ * OpenRISC cpu parameters for qemu. * * Copyright (c) 2011-2012 Jia Liu - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef OPENRISC_CPU_PARAM_H @@ -12,6 +12,7 @@ #define TARGET_PAGE_BITS 13 #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define NB_MMU_MODES 3 + +#define TCG_GUEST_DEFAULT_MO (0) #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 de0176cd20..b96561d1f2 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) { @@ -43,7 +45,8 @@ static void openrisc_cpu_synchronize_from_tb(CPUState *cs, { OpenRISCCPU *cpu = OPENRISC_CPU(cs); - cpu->env.pc = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu->env.pc = tb->pc; } static void openrisc_restore_state_to_opc(CPUState *cs, @@ -65,27 +68,50 @@ 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, ResetType type) { - 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, type); + } 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); + /* + * TODO: this is probably not the correct NaN propagation rule for + * this architecture. + */ + set_float_2nan_prop_rule(float_2nan_prop_x87, &cpu->env.fp_status); + + #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; cpu->env.picsr = 0x00000000; @@ -141,12 +167,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 } @@ -160,10 +182,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; } @@ -212,7 +231,7 @@ 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, @@ -220,6 +239,7 @@ static const struct TCGCPUOps openrisc_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = openrisc_cpu_tlb_fill, .cpu_exec_interrupt = openrisc_cpu_exec_interrupt, + .cpu_exec_halt = openrisc_cpu_has_work, .do_interrupt = openrisc_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; @@ -229,13 +249,16 @@ 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; @@ -250,48 +273,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, \ @@ -304,6 +285,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 1d5efa5ca2..c9fe9ae12d 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -20,31 +20,22 @@ #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 TCG_GUEST_DEFAULT_MO (0) - -#define TYPE_OPENRISC_CPU "or1k-cpu" - -OBJECT_DECLARE_CPU_TYPE(OpenRISCCPU, OpenRISCCPUClass, OPENRISC_CPU) /** * 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 @@ -290,7 +281,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; @@ -301,26 +292,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); @@ -343,8 +328,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" @@ -366,9 +349,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; @@ -377,18 +359,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 c31c6f12c4..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) { @@ -104,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 0b8afdbacf..c632d5230b 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -22,7 +22,8 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/gdbstub.h" +#include "exec/page-protection.h" +#include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "hw/loader.h" diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index ec145960e3..77567afba4 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -26,20 +26,41 @@ #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; @@ -139,20 +160,20 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) break; case TO_SPR(9, 0): /* PICMR */ env->picmr = rb; - qemu_mutex_lock_iothread(); + bql_lock(); if (env->picsr & env->picmr) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } - qemu_mutex_unlock_iothread(); + bql_unlock(); break; case TO_SPR(9, 2): /* PICSR */ env->picsr &= ~rb; break; case TO_SPR(10, 0): /* TTMR */ { - qemu_mutex_lock_iothread(); + bql_lock(); if ((env->ttmr & TTMR_M) ^ (rb & TTMR_M)) { switch (rb & TTMR_M) { case TIMER_NONE: @@ -177,37 +198,43 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) cs->interrupt_request &= ~CPU_INTERRUPT_TIMER; } cpu_openrisc_timer_update(cpu); - qemu_mutex_unlock_iothread(); + bql_unlock(); } break; case TO_SPR(10, 1): /* TTCR */ - qemu_mutex_lock_iothread(); + bql_lock(); cpu_openrisc_count_set(cpu, rb); cpu_openrisc_timer_update(cpu); - qemu_mutex_unlock_iothread(); - 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) { + OpenRISCCPU *cpu = env_archcpu(env); #ifndef CONFIG_USER_ONLY uint64_t data[TARGET_INSN_START_WORDS]; MachineState *ms = MACHINE(qdev_get_machine()); - OpenRISCCPU *cpu = env_archcpu(env); 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; @@ -320,15 +347,12 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, return env->ttmr; case TO_SPR(10, 1): /* TTCR */ - qemu_mutex_lock_iothread(); + bql_lock(); cpu_openrisc_count_update(cpu); - qemu_mutex_unlock_iothread(); + 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 2f3d7c5fd1..ca566847cb 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -21,20 +21,22 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "disas/disas.h" #include "tcg/tcg-op.h" #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 +94,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 +133,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 +181,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 +208,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 +224,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 +241,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 +251,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 +270,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 +284,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 +306,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 +329,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 +348,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 +366,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 +389,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 +412,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 +650,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 +661,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 +710,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 +731,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 +819,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 +831,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 +1101,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 +1114,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 +1126,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 +1139,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 +1192,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 +1308,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 +1329,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 +1353,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 +1411,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 +1430,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 +1454,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 +1524,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 +1563,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); @@ -1687,26 +1637,16 @@ static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void openrisc_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - DisasContext *s = container_of(dcbase, DisasContext, base); - - fprintf(logfile, "IN: %s\n", lookup_symbol(s->base.pc_first)); - target_disas(logfile, cs, s->base.pc_first, s->base.tb->size); -} - static const TranslatorOps openrisc_tr_ops = { .init_disas_context = openrisc_tr_init_disas_context, .tb_start = openrisc_tr_tb_start, .insn_start = openrisc_tr_insn_start, .translate_insn = openrisc_tr_translate_insn, .tb_stop = openrisc_tr_tb_stop, - .disas_log = openrisc_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; @@ -1716,8 +1656,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, 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); diff --git a/target/ppc/Kconfig b/target/ppc/Kconfig index 3ff152051a..0283711673 100644 --- a/target/ppc/Kconfig +++ b/target/ppc/Kconfig @@ -3,3 +3,4 @@ config PPC config PPC64 bool + select PPC diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index f58e6359d5..f45474133a 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -47,9 +47,14 @@ struct PPCUserRegStruct { } QEMU_PACKED; struct PPCElfPrstatus { - char pad1[112]; + char pad1[32]; /* 32 == offsetof(struct elf_prstatus, pr_pid) */ + uint32_t pid; + char pad2[76]; /* 76 == offsetof(struct elf_prstatus, pr_reg) - + offsetof(struct elf_prstatus, pr_ppid) */ struct PPCUserRegStruct pr_reg; - char pad2[40]; + char pad3[40]; /* 40 == sizeof(struct elf_prstatus) - + offsetof(struct elf_prstatus, pr_reg) - + sizeof(struct user_pt_regs) */ } QEMU_PACKED; @@ -96,7 +101,7 @@ typedef struct NoteFuncArg { DumpState *state; } NoteFuncArg; -static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; reg_t cr; @@ -109,6 +114,7 @@ static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) prstatus = ¬e->contents.prstatus; memset(prstatus, 0, sizeof(*prstatus)); + prstatus->pid = cpu_to_dump32(s, id); reg = &prstatus->pr_reg; for (i = 0; i < 32; i++) { @@ -127,7 +133,7 @@ static void ppc_write_elf_prstatus(NoteFuncArg *arg, PowerPCCPU *cpu) reg->ccr = cpu_to_dump_reg(s, cr); } -static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfFpregset *fpregset; @@ -146,7 +152,7 @@ static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) fpregset->fpscr = cpu_to_dump_reg(s, cpu->env.fpscr); } -static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfVmxregset *vmxregset; @@ -178,7 +184,7 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) vmxregset->vscr.u32[3] = cpu_to_dump32(s, ppc_get_vscr(&cpu->env)); } -static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { int i; struct PPCElfVsxregset *vsxregset; @@ -195,7 +201,7 @@ static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) } } -static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) +static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu, int id) { struct PPCElfSperegset *speregset; Note *note = &arg->note; @@ -211,7 +217,7 @@ static void ppc_write_elf_speregset(NoteFuncArg *arg, PowerPCCPU *cpu) static const struct NoteFuncDescStruct { int contents_size; - void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu); + void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu, int id); } note_func[] = { {sizeof_field(Note, contents.prstatus), ppc_write_elf_prstatus}, {sizeof_field(Note, contents.fpregset), ppc_write_elf_fpregset}, @@ -237,7 +243,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; @@ -282,7 +288,7 @@ static int ppc_write_all_elf_notes(const char *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); + (*nf->note_contents_func)(&arg, cpu, id); note_size = sizeof(arg.note) - sizeof(arg.note.contents) + nf->contents_size; diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 7949a24f5a..0cec1bde91 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -100,6 +100,13 @@ static const CompatInfo compat_table[] = { .pcr_level = PCR_COMPAT_3_10, .max_vthreads = 8, }, + { /* POWER11, ISA3.10 */ + .name = "power11", + .pvr = CPU_POWERPC_LOGICAL_3_10_P11, + .pcr = PCR_COMPAT_3_10, + .pcr_level = PCR_COMPAT_3_10, + .max_vthreads = 8, + }, }; static const CompatInfo *compat_by_pvr(uint32_t pvr) @@ -132,6 +139,10 @@ static bool pcc_compat(PowerPCCPUClass *pcc, uint32_t compat_pvr, /* Outside specified range */ return false; } + if (compat->pvr > pcc->spapr_logical_pvr) { + /* Older CPU cannot support a newer processor's compat mode */ + return false; + } if (!(pcc->pcr_supported & compat->pcr_level)) { /* Not supported by this CPU */ return false; @@ -229,6 +240,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 912b037c63..ece3481781 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -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,14 +728,14 @@ "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") + POWERPC_DEF("power11_v2.0", CPU_POWERPC_POWER11_DD20, POWER11, + "POWER11_v2.0") #endif /* defined (TARGET_PPC64) */ /***************************************************************************/ @@ -900,15 +900,18 @@ 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" }, + { "power11", "power11_v2.0" }, #endif /* Generic PowerPCs */ diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index 1326493a9a..72ad31ba50 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, @@ -348,11 +348,14 @@ 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_POWER11_BASE = 0x00820000, + CPU_POWERPC_POWER11_DD20 = 0x00821200, CPU_POWERPC_970_v22 = 0x00390202, CPU_POWERPC_970FX_v10 = 0x00391100, CPU_POWERPC_970FX_v20 = 0x003C0200, @@ -390,6 +393,7 @@ enum { CPU_POWERPC_LOGICAL_2_07 = 0x0F000004, CPU_POWERPC_LOGICAL_3_00 = 0x0F000005, CPU_POWERPC_LOGICAL_3_10 = 0x0F000006, + CPU_POWERPC_LOGICAL_3_10_P11 = 0x0F000007, }; /* System version register (used on MPC 8xxx) */ diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index ea377b7d06..9c481b9f6c 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -2,7 +2,7 @@ * PowerPC cpu parameters for qemu. * * Copyright (c) 2007 Jocelyn Mayer - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef PPC_CPU_PARAM_H @@ -31,7 +31,15 @@ # 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 + +#define TCG_GUEST_DEFAULT_MO 0 #endif diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 89ff88f28c..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,163 +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; - /* - * 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 - 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 1a97b41c6b..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,6 +68,23 @@ 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) { @@ -85,6 +103,92 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) 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) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 81d4263a07..2ffac2ed03 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -27,7 +27,7 @@ #include "qom/object.h" #include "hw/registerfields.h" -#define TCG_GUEST_DEFAULT_MO 0 +#define CPU_RESOLVING_TYPE TYPE_POWERPC_CPU #define TARGET_PAGE_BITS_64K 16 #define TARGET_PAGE_BITS_16M 24 @@ -40,6 +40,7 @@ #define PPC_BIT_NR(bit) (63 - (bit)) #define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT32_NR(bit) (31 - (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)) @@ -190,6 +191,97 @@ 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, + /* POWER11 exception model */ + POWERPC_EXCP_POWER11, +} 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) /*****************************************************************************/ @@ -198,9 +290,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 { @@ -428,7 +525,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 */ @@ -439,6 +536,9 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR0_FC56 PPC_BIT(59) /* PMC Freeze Counters 5-6 bit */ #define MMCR0_PMC1CE PPC_BIT(48) /* MMCR0 PMC1 Condition Enabled */ #define MMCR0_PMCjCE PPC_BIT(49) /* MMCR0 PMCj Condition Enabled */ +#define MMCR0_FCP PPC_BIT(34) /* Freeze Counters/BHRB if PR=1 */ +#define MMCR0_FCPC PPC_BIT(51) /* Condition for FCP bit */ +#define MMCR0_BHRBA_NR PPC_BIT_NR(42) /* BHRB Available */ /* MMCR0 userspace r/w mask */ #define MMCR0_UREG_MASK (MMCR0_FC | MMCR0_PMAO | MMCR0_PMAE) /* MMCR2 userspace r/w mask */ @@ -451,6 +551,10 @@ FIELD(MSR, LE, MSR_LE, 1) #define MMCR2_UREG_MASK (MMCR2_FC1P0 | MMCR2_FC2P0 | MMCR2_FC3P0 | \ MMCR2_FC4P0 | MMCR2_FC5P0 | MMCR2_FC6P0) +#define MMCRA_BHRBRD PPC_BIT(26) /* BHRB Recording Disable */ +#define MMCRA_IFM_MASK PPC_BITMASK(32, 33) /* BHRB Instruction Filtering */ +#define MMCRA_IFM_SHIFT PPC_BIT_NR(33) + #define MMCR1_EVT_SIZE 8 /* extract64() does a right shift before extracting */ #define MMCR1_PMC1SEL_START 32 @@ -533,7 +637,8 @@ FIELD(MSR, LE, MSR_LE, 1) #define PSSCR_EC PPC_BIT(43) /* Exit Criterion */ /* HFSCR bits */ -#define HFSCR_MSGP PPC_BIT(53) /* Privileged Message Send Facilities */ +#define HFSCR_MSGP PPC_BIT_NR(53) /* Privileged Message Send Facilities */ +#define HFSCR_BHRB PPC_BIT_NR(59) /* BHRB Instructions */ #define HFSCR_IC_MSGP 0xA #define DBCR0_ICMP (1 << 27) @@ -672,6 +777,12 @@ 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, + /* Has BHRB */ + POWERPC_FLAG_BHRB = 0x01000000, }; /* @@ -699,6 +810,7 @@ enum { 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_BHRB_ENABLE = 20, /* Summary flag for enabling BHRB */ HFLAGS_VSX = 23, /* MSR_VSX if cpu has VSX */ HFLAGS_VR = 25, /* MSR_VR if cpu has VRE */ @@ -1054,7 +1166,11 @@ FIELD(FPSCR, FI, FPSCR_FI, 1) #define DBELL_TYPE_DBELL_SERVER (0x05 << DBELL_TYPE_SHIFT) -#define DBELL_BRDCAST PPC_BIT(37) +#define DBELL_BRDCAST_MASK PPC_BITMASK(37, 38) +#define DBELL_BRDCAST_SHIFT 25 +#define DBELL_BRDCAST_SUBPROC (0x1 << DBELL_BRDCAST_SHIFT) +#define DBELL_BRDCAST_CORE (0x2 << DBELL_BRDCAST_SHIFT) + #define DBELL_LPIDTAG_SHIFT 14 #define DBELL_LPIDTAG_MASK (0xfff << DBELL_LPIDTAG_SHIFT) #define DBELL_PIRTAG_MASK 0x3fff @@ -1068,6 +1184,21 @@ 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) + /*****************************************************************************/ /* The whole PowerPC CPU context */ @@ -1082,6 +1213,9 @@ struct ppc_radix_page_info { #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 +#define BHRB_MAX_NUM_ENTRIES_LOG2 (5) +#define BHRB_MAX_NUM_ENTRIES (1 << BHRB_MAX_NUM_ENTRIES_LOG2) + struct CPUArchState { /* Most commonly used resources during translated code execution first */ target_ulong gpr[32]; /* general purpose registers */ @@ -1099,9 +1233,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 */ @@ -1109,15 +1246,21 @@ 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; + /* For SMT processors */ + bool has_smt_siblings; + int core_index; + int chip_index; + #if !defined(CONFIG_USER_ONLY) /* 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 */ @@ -1128,12 +1271,13 @@ struct CPUArchState { int tlb_per_way; /* Speed-up helper: used to avoid divisions at run time */ int nb_ways; /* Number of ways in the TLB set */ int last_way; /* Last used way used to allocate TLB in a LRU way */ - int id_tlbs; /* If 1, MMU has separated TLBs for instructions & data */ int nb_pids; /* Number of available PID registers */ int tlb_type; /* Type of TLB we're dealing with */ ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */ +#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 @@ -1166,6 +1310,16 @@ struct CPUArchState { int dcache_line_size; int icache_line_size; +#ifdef TARGET_PPC64 + /* Branch History Rolling Buffer (BHRB) resources */ + target_ulong bhrb_num_entries; + intptr_t bhrb_base; + target_ulong bhrb_filter; + target_ulong bhrb_offset; + target_ulong bhrb_offset_mask; + uint64_t bhrb[BHRB_MAX_NUM_ENTRIES]; +#endif + /* These resources are used during exception processing */ /* CPU model definition */ target_ulong msr_mask; @@ -1180,6 +1334,7 @@ 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 @@ -1201,6 +1356,7 @@ struct CPUArchState { * special way (such as routing some resume causes to 0x100, i.e. sreset). */ bool resume_as_sreset; + bool quiesced; #endif /* These resources are used only in TCG */ @@ -1210,6 +1366,9 @@ struct CPUArchState { /* Power management */ int (*check_pow)(CPUPPCState *env); + /* attn instruction enable */ + int (*check_attn)(CPUPPCState *env); + #if !defined(CONFIG_USER_ONLY) void *load_info; /* holds boot loading state */ #endif @@ -1252,6 +1411,13 @@ struct CPUArchState { uint64_t pmu_base_time; }; +#define THREAD_SIBLING_FOREACH(cs, cs_sibling) \ + CPU_FOREACH(cs_sibling) \ + if ((POWERPC_CPU(cs)->env.chip_index == \ + POWERPC_CPU(cs_sibling)->env.chip_index) && \ + (POWERPC_CPU(cs)->env.core_index == \ + POWERPC_CPU(cs_sibling)->env.core_index)) + #define SET_FIT_PERIOD(a_, b_, c_, d_) \ do { \ env->fit_period[0] = (a_); \ @@ -1280,16 +1446,14 @@ typedef struct PPCVirtualHypervisorClass PPCVirtualHypervisorClass; * A PowerPC CPU. */ struct ArchCPU { - /*< private >*/ CPUState parent_obj; - /*< public >*/ - CPUNegativeOffsetState neg; CPUPPCState env; int vcpu_id; uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; + PPCVirtualHypervisorClass *vhyp_class; void *machine_data; int32_t node_id; /* NUMA node this CPU belongs to */ PPCHash64Options *hash64_opts; @@ -1297,19 +1461,68 @@ struct ArchCPU { /* Those resources are used only during code translation */ /* opcode handlers */ opc_handler_t *opcodes[PPC_CPU_OPCODES_LEN]; - - /* Fields related to migration compatibility hacks */ - bool pre_2_8_migration; - target_ulong mig_msr_mask; - uint64_t mig_insns_flags; - uint64_t mig_insns_flags2; - uint32_t mig_nb_BATs; - bool pre_2_10_migration; - bool pre_3_0_migration; - 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; + uint32_t spapr_logical_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); + int (*check_attn)(CPUPPCState *env); +}; + +static inline bool ppc_cpu_core_single_threaded(CPUState *cs) +{ + return !POWERPC_CPU(cs)->env.has_smt_siblings; +} + +static inline bool ppc_cpu_lpar_single_threaded(CPUState *cs) +{ + return !(POWERPC_CPU(cs)->env.flags & POWERPC_FLAG_SMT_1LPAR) || + ppc_cpu_core_single_threaded(cs); +} + +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); @@ -1341,19 +1554,17 @@ DECLARE_OBJ_CHECKERS(PPCVirtualHypervisor, PPCVirtualHypervisorClass, static inline bool vhyp_cpu_in_nested(PowerPCCPU *cpu) { - return PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp)->cpu_in_nested(cpu); + return cpu->vhyp_class->cpu_in_nested(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, DumpState *s); @@ -1374,13 +1585,17 @@ 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_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); @@ -1389,6 +1604,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); @@ -1411,16 +1628,7 @@ 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); #endif void ppc_store_fpscr(CPUPPCState *env, target_ulong val); @@ -1448,15 +1656,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; @@ -1476,6 +1680,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, @@ -1568,9 +1773,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) @@ -1581,22 +1788,22 @@ 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) #define SPR_SPRG3 (0x113) #define SPR_SPRG4 (0x114) -#define SPR_SCOMC (0x114) +#define SPR_POWER_SPRC (0x114) #define SPR_SPRG5 (0x115) -#define SPR_SCOMD (0x115) +#define SPR_POWER_SPRD (0x115) #define SPR_SPRG6 (0x116) #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) @@ -1638,6 +1845,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) @@ -1674,9 +1882,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) @@ -1761,6 +1971,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) @@ -1865,15 +2081,20 @@ 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_LDBAR (0x352) #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) @@ -1889,6 +2110,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_POWER_MMCRS (0x37E) #define SPR_WORT (0x37F) #define SPR_PPR (0x380) +#define SPR_PPR32 (0x382) #define SPR_750_GQR0 (0x390) #define SPR_440_DNV0 (0x390) #define SPR_750_GQR1 (0x391) @@ -1912,6 +2134,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_440_IVLIM (0x399) #define SPR_TSCR (0x399) #define SPR_750_DMAU (0x39A) +#define SPR_POWER_TTR (0x39A) #define SPR_750_DMAL (0x39B) #define SPR_440_RSTCFG (0x39B) #define SPR_BOOKE_DCDBTRL (0x39C) @@ -2093,6 +2316,8 @@ void ppc_compat_add_property(Object *obj, const char *name, #define HID0_NAP (1 << 22) /* pre-2.06 */ #define HID0_HILE PPC_BIT(19) /* POWER8 */ #define HID0_POWER9_HILE PPC_BIT(4) +#define HID0_ENABLE_ATTN PPC_BIT(31) /* POWER8 */ +#define HID0_POWER9_ENABLE_ATTN PPC_BIT(3) /*****************************************************************************/ /* PowerPC Instructions types definitions */ @@ -2472,6 +2697,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)) @@ -2485,11 +2738,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; @@ -2626,6 +2879,10 @@ static inline void booke206_fixed_size_tlbn(CPUPPCState *env, const int tlbn, tlb->mas1 |= ((uint32_t)tsize) << MAS1_TSIZE_SHIFT; } +static inline bool ppc_is_split_tlb(PowerPCCPU *cpu) +{ + return cpu->env.tlb_type == TLB_6XX; +} #endif static inline bool msr_is_64bit(CPUPPCState *env, target_ulong msr) @@ -2726,6 +2983,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); @@ -2754,6 +3012,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 */ @@ -2767,6 +3027,12 @@ static inline int check_pow_nocheck(CPUPPCState *env) return 1; } +/* attn enable check */ +static inline int check_attn_none(CPUPPCState *env) +{ + return 0; +} + /*****************************************************************************/ /* PowerPC implementations definitions */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 294a18a5b7..efcb80d1c2 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -1,3 +1,4 @@ + /* * PowerPC CPU initialization for qemu. * @@ -20,8 +21,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,7 +40,6 @@ #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" @@ -49,8 +48,11 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" +#include "hw/intc/intc.h" +#include "kvm_ppc.h" #endif +#include "cpu_init.h" /* #define PPC_DEBUG_SPR */ /* #define USE_APPLE_GDB */ @@ -246,7 +248,7 @@ static void register_amr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_AMOR, "AMOR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_lpar_write_generic, 0); #endif /* !CONFIG_USER_ONLY */ } @@ -792,7 +794,7 @@ static void register_BookE_sprs(CPUPPCState *env, uint64_t ivor_mask) 0x00000000); spr_register(env, SPR_BOOKE_DECAR, "DECAR", SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_generic, + SPR_NOACCESS, &spr_write_generic32, 0x00000000); /* SPRGs */ spr_register(env, SPR_USPRG0, "USPRG0", @@ -1630,6 +1632,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) @@ -1641,7 +1644,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; @@ -2106,20 +2109,43 @@ static int check_pow_hid0_74xx(CPUPPCState *env) return 0; } +#if defined(TARGET_PPC64) +static int check_attn_hid0(CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & HID0_ENABLE_ATTN) { + return 1; + } + + return 0; +} + +static int check_attn_hid0_power9(CPUPPCState *env) +{ + if (env->spr[SPR_HID0] & HID0_POWER9_ENABLE_ATTN) { + return 1; + } + + return 0; +} +#endif + +static void init_tlbs_emb(CPUPPCState *env) +{ +#ifndef CONFIG_USER_ONLY + env->nb_tlb = 64; + env->nb_ways = 1; + env->tlb_type = TLB_EMB; +#endif +} + static void init_proc_405(CPUPPCState *env) { register_40x_sprs(env); register_405_sprs(env); register_usprgh_sprs(env); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->id_tlbs = 0; - env->tlb_type = TLB_EMB; -#endif - init_excp_4xx_softmmu(env); + init_tlbs_emb(env); + init_excp_4xx(env); env->dcache_line_size = 32; env->icache_line_size = 32; /* Allocate hardware IRQ controller */ @@ -2137,6 +2163,7 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) dc->desc = "PowerPC 405"; pcc->init_proc = init_proc_405; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_DCR | PPC_WRTEE | PPC_CACHE | PPC_CACHE_ICBI | PPC_40x_ICBT | @@ -2185,13 +2212,8 @@ static void init_proc_440EP(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->id_tlbs = 0; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2209,6 +2231,7 @@ POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440 EP"; pcc->init_proc = init_proc_440EP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -2247,6 +2270,7 @@ POWERPC_FAMILY(460EX)(ObjectClass *oc, void *data) dc->desc = "PowerPC 460 EX"; pcc->init_proc = init_proc_440EP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FRES | PPC_FLOAT_FSEL | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -2283,13 +2307,7 @@ static void init_proc_440GP(CPUPPCState *env) register_440_sprs(env); register_usprgh_sprs(env); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->id_tlbs = 0; - env->tlb_type = TLB_EMB; -#endif + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2307,6 +2325,7 @@ POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440 GP"; pcc->init_proc = init_proc_440GP; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_DCR | PPC_DCRX | PPC_WRTEE | PPC_MFAPIDI | PPC_CACHE | PPC_CACHE_ICBI | @@ -2357,13 +2376,8 @@ static void init_proc_440x5(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->id_tlbs = 0; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2381,6 +2395,7 @@ POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440x5"; pcc->init_proc = init_proc_440x5; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_DCR | PPC_WRTEE | PPC_RFMCI | PPC_CACHE | PPC_CACHE_ICBI | @@ -2416,6 +2431,7 @@ POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) dc->desc = "PowerPC 440x5 with double precision FPU"; pcc->init_proc = init_proc_440x5; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_FLOAT | PPC_FLOAT_FSQRT | PPC_FLOAT_STFIWX | @@ -2464,6 +2480,7 @@ POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) dc->desc = "Freescale 5xx cores (aka RCPU)"; pcc->init_proc = init_proc_MPC5xx; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MEM_EIEIO | PPC_MEM_SYNC | PPC_CACHE_ICBI | PPC_FLOAT | PPC_FLOAT_STFIWX | @@ -2506,6 +2523,7 @@ POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) dc->desc = "Freescale 8xx cores (aka PowerQUICC)"; pcc->init_proc = init_proc_MPC8xx; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MEM_EIEIO | PPC_MEM_SYNC | PPC_CACHE_ICBI | PPC_MFTB; @@ -2556,6 +2574,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) dc->desc = "PowerPC G2"; pcc->init_proc = init_proc_G2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -2594,6 +2613,7 @@ POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) dc->desc = "PowerPC G2LE"; pcc->init_proc = init_proc_G2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -2720,12 +2740,8 @@ static void init_proc_e200(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); -#if !defined(CONFIG_USER_ONLY) - env->nb_tlb = 64; - env->nb_ways = 1; - env->id_tlbs = 0; - env->tlb_type = TLB_EMB; -#endif + + init_tlbs_emb(env); init_excp_e200(env, 0xFFFF0000UL); env->dcache_line_size = 32; env->icache_line_size = 32; @@ -2740,6 +2756,7 @@ POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) dc->desc = "e200 core"; pcc->init_proc = init_proc_e200; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; /* * XXX: unimplemented instructions: * dcblc @@ -2842,7 +2859,6 @@ static void init_proc_e500(CPUPPCState *env, int version) /* Memory management */ env->nb_pids = 3; env->nb_ways = 2; - env->id_tlbs = 0; switch (version) { case fsl_e500v1: tlbncfg[0] = register_tlbncfg(2, 1, 1, 0, 256); @@ -3028,6 +3044,7 @@ POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) dc->desc = "e500v1 core"; pcc->init_proc = init_proc_e500v1; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_SPE | PPC_SPE_SINGLE | PPC_WRTEE | PPC_RFDI | @@ -3071,6 +3088,7 @@ POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) dc->desc = "e500v2 core"; pcc->init_proc = init_proc_e500v2; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE | PPC_WRTEE | PPC_RFDI | @@ -3114,6 +3132,7 @@ POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) dc->desc = "e500mc core"; pcc->init_proc = init_proc_e500mc; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3160,6 +3179,7 @@ POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) dc->desc = "e5500 core"; pcc->init_proc = init_proc_e5500; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3208,6 +3228,7 @@ POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) dc->desc = "e6500 core"; pcc->init_proc = init_proc_e6500; pcc->check_pow = check_pow_none; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_MFTB | PPC_WRTEE | PPC_RFDI | PPC_RFMCI | PPC_CACHE | PPC_CACHE_LOCK | PPC_CACHE_ICBI | @@ -3270,6 +3291,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) dc->desc = "PowerPC 603"; pcc->init_proc = init_proc_603; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3309,6 +3331,7 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) dc->desc = "PowerPC 603e"; pcc->init_proc = init_proc_603; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3354,6 +3377,7 @@ POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) dc->desc = "e300 core"; pcc->init_proc = init_proc_e300; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_STFIWX | @@ -3409,6 +3433,7 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) dc->desc = "PowerPC 604"; pcc->init_proc = init_proc_604; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3454,6 +3479,7 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) dc->desc = "PowerPC 604E"; pcc->init_proc = init_proc_604E; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3510,6 +3536,7 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) dc->desc = "PowerPC 740"; pcc->init_proc = init_proc_740; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3575,6 +3602,7 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750"; pcc->init_proc = init_proc_750; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3721,6 +3749,7 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750 CL"; pcc->init_proc = init_proc_750cl; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; /* * XXX: not implemented: * cache lock instructions: @@ -3828,6 +3857,7 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750CX"; pcc->init_proc = init_proc_750cx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3900,6 +3930,7 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750FX"; pcc->init_proc = init_proc_750fx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -3972,6 +4003,7 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) dc->desc = "PowerPC 750GX"; pcc->init_proc = init_proc_750gx; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4031,6 +4063,7 @@ POWERPC_FAMILY(745)(ObjectClass *oc, void *data) dc->desc = "PowerPC 745"; pcc->init_proc = init_proc_745; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4076,6 +4109,7 @@ POWERPC_FAMILY(755)(ObjectClass *oc, void *data) dc->desc = "PowerPC 755"; pcc->init_proc = init_proc_755; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | @@ -4142,6 +4176,7 @@ POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7400 (aka G4)"; pcc->init_proc = init_proc_7400; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4221,6 +4256,7 @@ POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7410 (aka G4)"; pcc->init_proc = init_proc_7410; pcc->check_pow = check_pow_hid0; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4321,6 +4357,7 @@ POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7440 (aka G4)"; pcc->init_proc = init_proc_7440; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4443,6 +4480,7 @@ POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7450 (aka G4)"; pcc->init_proc = init_proc_7450; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4572,6 +4610,7 @@ POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7445 (aka G4)"; pcc->init_proc = init_proc_7445; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4703,6 +4742,7 @@ POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7455 (aka G4)"; pcc->init_proc = init_proc_7455; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4854,6 +4894,7 @@ POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) dc->desc = "PowerPC 7457 (aka G4)"; pcc->init_proc = init_proc_7457; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -4988,6 +5029,7 @@ POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) dc->desc = "PowerPC e600"; pcc->init_proc = init_proc_e600; pcc->check_pow = check_pow_hid0_74xx; + pcc->check_attn = check_attn_none; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5061,7 +5103,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); @@ -5069,11 +5111,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); @@ -5086,8 +5128,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); } @@ -5116,17 +5158,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); } @@ -5151,7 +5193,7 @@ static void register_book3s_pmu_sup_sprs(CPUPPCState *env) KVM_REG_PPC_MMCR1, 0x00000000); spr_register_kvm(env, SPR_POWER_MMCRA, "MMCRA", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_MMCRA, KVM_REG_PPC_MMCRA, 0x00000000); spr_register_kvm(env, SPR_POWER_PMC1, "PMC1", SPR_NOACCESS, SPR_NOACCESS, @@ -5307,6 +5349,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 */ @@ -5346,7 +5420,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, @@ -5369,31 +5443,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, @@ -5407,7 +5456,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_core_write_generic32, 0x00000000); spr_register_hv(env, SPR_MMCRH, "MMCRH", SPR_NOACCESS, SPR_NOACCESS, @@ -5442,12 +5491,12 @@ 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, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000000); } @@ -5489,7 +5538,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); @@ -5507,7 +5556,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); @@ -5523,6 +5572,32 @@ 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_power7_common_sprs(CPUPPCState *env) +{ + spr_register(env, SPR_PPR32, "PPR32", + &spr_read_ppr32, &spr_write_ppr32, + &spr_read_ppr32, &spr_write_ppr32, + 0x00000000); +} + static void register_power8_tce_address_control_sprs(CPUPPCState *env) { spr_register_kvm(env, SPR_TAR, "TAR", @@ -5637,18 +5712,63 @@ static void register_power8_ic_sprs(CPUPPCState *env) #endif } -static void register_power8_book4_sprs(CPUPPCState *env) +/* SPRs specific to IBM POWER CPUs */ +static void register_power_common_book4_sprs(CPUPPCState *env) { - /* Add a number of P8 book4 registers */ #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", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_pidr, - KVM_REG_PPC_PID, 0); + 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_core_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_core_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); + spr_register_hv(env, SPR_LDBAR, "LDBAR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_lpar_write_generic, + 0x00000000); + spr_register_hv(env, SPR_POWER_TTR, "TTR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_core_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, @@ -5656,17 +5776,51 @@ static void register_power8_book4_sprs(CPUPPCState *env) #endif } -static void register_power7_book4_sprs(CPUPPCState *env) +static void register_power8_book4_sprs(CPUPPCState *env) { - /* Add a number of P7 book4 registers */ + /* 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); + spr_register_kvm(env, SPR_WORT, "WORT", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, + KVM_REG_PPC_WORT, 0); + /* SPRC/SPRD exist in earlier CPUs but only tested on POWER9/10 */ + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_sprc, + 0x00000000); + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_sprd, &spr_write_sprd, + 0x00000000); +#endif +} + +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); + /* 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_generic32, KVM_REG_PPC_PID, 0); #endif } @@ -5677,7 +5831,7 @@ static void register_power8_rpr_sprs(CPUPPCState *env) spr_register_hv(env, SPR_RPR, "RPR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_core_write_generic, 0x00000103070F1F3F); #endif } @@ -5697,13 +5851,18 @@ 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 responsability to generate a random value for the registers + * 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; @@ -5716,15 +5875,39 @@ static void register_power10_hash_sprs(CPUPPCState *env) ((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_register_kvm(env, SPR_HASHKEYR, "HASHKEYR", SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - hashkeyr_initial_value); - spr_register_hv(env, SPR_HASHPKEYR, "HASHPKEYR", + KVM_REG_PPC_HASHKEYR, hashkeyr_initial_value); + spr_register_kvm_hv(env, SPR_HASHPKEYR, "HASHPKEYR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, - hashpkeyr_initial_value); + KVM_REG_PPC_HASHPKEYR, hashpkeyr_initial_value); +} + +static void register_power10_dexcr_sprs(CPUPPCState *env) +{ + spr_register_kvm(env, SPR_DEXCR, "DEXCR", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, KVM_REG_PPC_DEXCR, + 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); } /* @@ -5791,6 +5974,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) dc->desc = "PowerPC 970"; pcc->init_proc = init_proc_970; pcc->check_pow = check_pow_970; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5817,7 +6001,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; @@ -5866,6 +6050,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) dc->desc = "POWER5+"; pcc->init_proc = init_proc_power5plus; pcc->check_pow = check_pow_970; + pcc->check_attn = check_attn_hid0; pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | @@ -5896,7 +6081,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 @@ -5927,7 +6112,9 @@ 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_common_sprs(env); register_power7_book4_sprs(env); /* env variables */ @@ -5967,11 +6154,13 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; + pcc->spapr_logical_pvr = CPU_POWERPC_LOGICAL_2_06_PLUS; pcc->pvr_match = ppc_pvr_match_power7; pcc->pcr_mask = PCR_VEC_DIS | PCR_VSX_DIS | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0; 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 | @@ -6013,7 +6202,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 @@ -6028,6 +6217,28 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->l1_icache_size = 0x8000; } +static void bhrb_init_state(CPUPPCState *env, target_long num_entries_log2) +{ + if (env->flags & POWERPC_FLAG_BHRB) { + if (num_entries_log2 > BHRB_MAX_NUM_ENTRIES_LOG2) { + num_entries_log2 = BHRB_MAX_NUM_ENTRIES_LOG2; + } + env->bhrb_num_entries = 1 << num_entries_log2; + env->bhrb_base = (intptr_t)&env->bhrb[0]; + env->bhrb_offset_mask = (env->bhrb_num_entries * sizeof(uint64_t)) - 1; + } +} + +static void bhrb_reset_state(CPUPPCState *env) +{ + if (env->flags & POWERPC_FLAG_BHRB) { + env->bhrb_offset = 0; + env->bhrb_filter = 0; + memset(env->bhrb, 0, sizeof(env->bhrb)); + } +} + +#define POWER8_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER8(CPUPPCState *env) { /* Common Registers */ @@ -6049,7 +6260,9 @@ 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_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6068,6 +6281,8 @@ static void init_proc_POWER8(CPUPPCState *env) env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER8_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER8(env); ppcPOWER7_irq_init(env_archcpu(env)); @@ -6103,11 +6318,13 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; + pcc->spapr_logical_pvr = CPU_POWERPC_LOGICAL_2_07; pcc->pvr_match = ppc_pvr_match_power8; pcc->pcr_mask = PCR_TM_DIS | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05; pcc->init_proc = init_proc_POWER8; pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0; 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 | @@ -6157,7 +6374,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; @@ -6173,7 +6390,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: @@ -6190,9 +6407,10 @@ 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) +#define POWER9_BHRB_ENTRIES_LOG2 5 +static void register_power9_common_sprs(CPUPPCState *env) { /* Common Registers */ init_proc_book3s_common(env); @@ -6212,6 +6430,7 @@ static void init_proc_POWER9(CPUPPCState *env) register_power5p_tb_sprs(env); register_power6_common_sprs(env); register_power6_dbg_sprs(env); + register_power7_common_sprs(env); register_power8_tce_address_control_sprs(env); register_power8_ids_sprs(env); register_power8_ebb_sprs(env); @@ -6223,24 +6442,31 @@ 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); - /* POWER9 Specific registers */ - spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL, - spr_read_generic, spr_write_generic, - KVM_REG_PPC_TIDR, 0); - /* FIXME: Filter fields properly based on privilege level */ spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL, spr_read_generic, spr_write_generic, KVM_REG_PPC_PSSCR, 0); +} + +static void init_proc_POWER9(CPUPPCState *env) +{ + register_power9_common_sprs(env); + register_HEIR32_spr(env); + /* POWER9 Specific registers */ + spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL, + spr_read_generic, spr_write_generic, + KVM_REG_PPC_TIDR, 0); /* env variables */ env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER9_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER9(env); ppcPOWER9_irq_init(env_archcpu(env)); @@ -6261,9 +6487,23 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return false; } - if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) { - /* Major DD version matches to power9_v1.0 and power9_v2.0 */ - return true; + 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; + } + } } return false; @@ -6276,61 +6516,20 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER9"; dc->desc = "POWER9"; + pcc->spapr_logical_pvr = CPU_POWERPC_LOGICAL_3_00; pcc->pvr_match = ppc_pvr_match_power9; - pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07; - pcc->pcr_supported = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | - PCR_COMPAT_2_05; + pcc->pcr_mask = PPC_PCR_MASK_POWER9; + pcc->pcr_supported = PPC_PCR_SUPPORTED_POWER9; pcc->init_proc = init_proc_POWER9; pcc->check_pow = check_pow_nocheck; - pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | - PPC_FLOAT_FRSQRTES | - PPC_FLOAT_STFIWX | - PPC_FLOAT_EXT | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | - PPC_SEGMENT_64B | PPC_SLBI | - PPC_POPCNTB | PPC_POPCNTWD | - PPC_CILDST; - pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | - PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | - PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | - PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | - PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL | 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) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_SE) | - (1ull << MSR_DE) | - (1ull << MSR_FE1) | - (1ull << MSR_IR) | - (1ull << MSR_DR) | - (1ull << MSR_PMM) | - (1ull << MSR_RI) | - (1ull << MSR_LE); - pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | - (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | - LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | - (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | - LPCR_DEE | LPCR_OEE)) - | LPCR_MER | LPCR_GTSE | LPCR_TC | - LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; + pcc->check_attn = check_attn_hid0_power9; + pcc->insns_flags = PPC_INSNS_FLAGS_POWER9; + pcc->insns_flags2 = PPC_INSNS_FLAGS2_POWER9; + pcc->msr_mask = PPC_MSR_MASK_POWER9; + pcc->lpcr_mask = PPC_LPCR_MASK_POWER9; 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; @@ -6340,15 +6539,12 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) pcc->excp_model = POWERPC_EXCP_POWER9; pcc->bus_model = PPC_FLAGS_INPUT_POWER9; pcc->bfd_mach = bfd_mach_ppc64; - pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | - POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | - POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV; + pcc->flags = POWERPC_FLAGS_POWER9; pcc->l1_dcache_size = 0x8000; 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: @@ -6365,53 +6561,23 @@ static struct ppc_radix_page_info POWER10_radix_page_info = { 0x4000001e /* 1G - enc: 0x2 */ } }; -#endif /* CONFIG_SOFTMMU */ +#endif /* !CONFIG_USER_ONLY */ +#define POWER10_BHRB_ENTRIES_LOG2 5 static void init_proc_POWER10(CPUPPCState *env) { - /* Common Registers */ - init_proc_book3s_common(env); - register_book3s_207_dbg_sprs(env); - - /* Common TCG PMU */ - init_tcg_pmu_power8(env); - - /* POWER8 Specific Registers */ - register_book3s_ids_sprs(env); - register_amr_sprs(env); - register_iamr_sprs(env); - register_book3s_purr_sprs(env); - register_power5p_common_sprs(env); - register_power5p_lpar_sprs(env); - register_power5p_ear_sprs(env); - register_power5p_tb_sprs(env); - register_power6_common_sprs(env); - register_power6_dbg_sprs(env); - register_power8_tce_address_control_sprs(env); - register_power8_ids_sprs(env); - register_power8_ebb_sprs(env); - register_power8_fscr_sprs(env); - register_power8_pmu_sup_sprs(env); - register_power8_pmu_user_sprs(env); - register_power8_tm_sprs(env); - register_power8_pspb_sprs(env); - register_power8_dpdes_sprs(env); - register_vtb_sprs(env); - register_power8_ic_sprs(env); - register_power8_book4_sprs(env); - register_power8_rpr_sprs(env); - register_power9_mmu_sprs(env); + register_power9_common_sprs(env); + register_HEIR64_spr(env); register_power10_hash_sprs(env); - - /* FIXME: Filter fields properly based on privilege level */ - spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL, - spr_read_generic, spr_write_generic, - KVM_REG_PPC_PSSCR, 0); - + register_power10_dexcr_sprs(env); + register_power10_pmu_sup_sprs(env); + register_power10_pmu_user_sprs(env); /* env variables */ env->dcache_line_size = 128; env->icache_line_size = 128; + bhrb_init_state(env, POWER10_BHRB_ENTRIES_LOG2); + /* Allocate hardware IRQ controller */ init_excp_POWER10(env); ppcPOWER9_irq_init(env_archcpu(env)); @@ -6433,7 +6599,7 @@ static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best) } if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) { - /* Major DD version matches to power10_v1.0 and power10_v2.0 */ + /* Major DD version matches power10_v2.0 */ return true; } @@ -6447,65 +6613,21 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) dc->fw_name = "PowerPC,POWER10"; dc->desc = "POWER10"; + pcc->spapr_logical_pvr = CPU_POWERPC_LOGICAL_3_10; pcc->pvr_match = ppc_pvr_match_power10; - pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07 | - PCR_COMPAT_3_00; - pcc->pcr_supported = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | - PCR_COMPAT_2_06 | PCR_COMPAT_2_05; + pcc->pcr_mask = PPC_PCR_MASK_POWER10; + pcc->pcr_supported = PPC_PCR_SUPPORTED_POWER10; pcc->init_proc = init_proc_POWER10; pcc->check_pow = check_pow_nocheck; - pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | - PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | - PPC_FLOAT_FRSQRTES | - PPC_FLOAT_STFIWX | - PPC_FLOAT_EXT | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBIE | PPC_MEM_TLBSYNC | - PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | - PPC_SEGMENT_64B | PPC_SLBI | - PPC_POPCNTB | PPC_POPCNTWD | - PPC_CILDST; - pcc->insns_flags2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | - PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | - PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 | - PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | - PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL | 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) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_SE) | - (1ull << MSR_DE) | - (1ull << MSR_FE1) | - (1ull << MSR_IR) | - (1ull << MSR_DR) | - (1ull << MSR_PMM) | - (1ull << MSR_RI) | - (1ull << MSR_LE); - pcc->lpcr_mask = LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | - (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | - LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | - (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | - LPCR_DEE | LPCR_OEE)) - | LPCR_MER | LPCR_GTSE | LPCR_TC | - LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE; - /* DD2 adds an extra HAIL bit */ - pcc->lpcr_mask |= LPCR_HAIL; + pcc->check_attn = check_attn_hid0_power9; + pcc->insns_flags = PPC_INSNS_FLAGS_POWER10; + pcc->insns_flags2 = PPC_INSNS_FLAGS2_POWER10; + pcc->msr_mask = PPC_MSR_MASK_POWER10; + pcc->lpcr_mask = PPC_LPCR_MASK_POWER10; 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; @@ -6514,10 +6636,67 @@ POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) pcc->excp_model = POWERPC_EXCP_POWER10; pcc->bus_model = PPC_FLAGS_INPUT_POWER9; pcc->bfd_mach = bfd_mach_ppc64; - pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | - POWERPC_FLAG_BE | POWERPC_FLAG_PMM | - POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | - POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV; + pcc->flags = POWERPC_FLAGS_POWER10; + pcc->l1_dcache_size = 0x8000; + pcc->l1_icache_size = 0x8000; +} + +static void init_proc_POWER11(CPUPPCState *env) +{ + init_proc_POWER10(env); +} + +static bool ppc_pvr_match_power11(PowerPCCPUClass *pcc, uint32_t pvr, bool best) +{ + uint32_t base = pvr & CPU_POWERPC_POWER_SERVER_MASK; + uint32_t pcc_base = pcc->pvr & CPU_POWERPC_POWER_SERVER_MASK; + + if (!best && (base == CPU_POWERPC_POWER11_BASE)) { + return true; + } + + if (base != pcc_base) { + return false; + } + + if ((pvr & 0x0f00) == (pcc->pvr & 0x0f00)) { + return true; + } + + return false; +} + +POWERPC_FAMILY(POWER11)(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + + dc->fw_name = "PowerPC,POWER11"; + dc->desc = "POWER11"; + pcc->spapr_logical_pvr = CPU_POWERPC_LOGICAL_3_10_P11; + pcc->pvr_match = ppc_pvr_match_power11; + pcc->pcr_mask = PPC_PCR_MASK_POWER11; + pcc->pcr_supported = PPC_PCR_SUPPORTED_POWER11; + pcc->init_proc = init_proc_POWER11; + pcc->check_pow = check_pow_nocheck; + pcc->check_attn = check_attn_hid0_power9; + pcc->insns_flags = PPC_INSNS_FLAGS_POWER11; + pcc->insns_flags2 = PPC_INSNS_FLAGS2_POWER11; + pcc->msr_mask = PPC_MSR_MASK_POWER11; + pcc->lpcr_mask = PPC_LPCR_MASK_POWER11; + + pcc->lpcr_pm = LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE; + pcc->mmu_model = POWERPC_MMU_3_00; +#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; + pcc->lrg_decr_bits = 56; +#endif + pcc->excp_model = POWERPC_EXCP_POWER11; + pcc->bus_model = PPC_FLAGS_INPUT_POWER9; + pcc->bfd_mach = bfd_mach_ppc64; + pcc->flags = POWERPC_FLAGS_POWER11; pcc->l1_dcache_size = 0x8000; pcc->l1_icache_size = 0x8000; } @@ -6528,6 +6707,7 @@ void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) CPUPPCState *env = &cpu->env; cpu->vhyp = vhyp; + cpu->vhyp_class = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(vhyp); /* * With a virtual hypervisor mode we never allow the CPU to go @@ -6536,6 +6716,19 @@ 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. powernv be in either mode, and it mostly affects + * supervisor visible registers and instructions. + */ + if (env->flags & POWERPC_FLAG_SMT) { + env->flags |= POWERPC_FLAG_SMT_1LPAR; + } +} #endif /* !defined(CONFIG_USER_ONLY) */ #endif /* defined(TARGET_PPC64) */ @@ -6567,10 +6760,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)) { @@ -6659,20 +6848,17 @@ static void init_ppc_proc(PowerPCCPU *cpu) } /* Allocate TLBs buffer when needed */ #if !defined(CONFIG_USER_ONLY) - if (env->nb_tlb != 0) { - int nb_tlb = env->nb_tlb; - if (env->id_tlbs != 0) { - nb_tlb *= 2; - } + if (env->nb_tlb) { switch (env->tlb_type) { case TLB_6XX: - env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, nb_tlb); + /* 6xx has separate TLBs for instructions and data hence times 2 */ + env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, 2 * env->nb_tlb); break; case TLB_EMB: - env->tlb.tlbe = g_new0(ppcemb_tlb_t, nb_tlb); + env->tlb.tlbe = g_new0(ppcemb_tlb_t, env->nb_tlb); break; case TLB_MAS: - env->tlb.tlbm = g_new0(ppcmas_tlb_t, nb_tlb); + env->tlb.tlbm = g_new0(ppcmas_tlb_t, env->nb_tlb); break; } /* Pre-compute some useful values */ @@ -6683,6 +6869,11 @@ static void init_ppc_proc(PowerPCCPU *cpu) warn_report("no power management check handler registered." " Attempt QEMU to crash very soon !"); } + + if (env->check_attn == NULL) { + warn_report("no attn check handler registered." + " Attempt QEMU to crash very soon !"); + } } @@ -6690,6 +6881,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; @@ -6721,6 +6913,10 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) pcc->parent_realize(dev, errp); + if (!ppc_cpu_core_single_threaded(cs)) { + env->flags |= POWERPC_FLAG_SMT; + } + return; unrealize: @@ -6816,7 +7012,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; @@ -6916,9 +7112,8 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) return; } - name = g_strndup(typename, - strlen(typename) - strlen(POWERPC_CPU_TYPE_SUFFIX)); - qemu_printf("PowerPC %-16s PVR %08x\n", name, pcc->pvr); + name = cpu_model_from_type(typename); + qemu_printf(" %-16s PVR %08x\n", name, pcc->pvr); for (i = 0; ppc_cpu_aliases[i].alias != NULL; i++) { PowerPCCPUAlias *alias = &ppc_cpu_aliases[i]; ObjectClass *alias_oc = ppc_cpu_class_by_name(alias->model); @@ -6931,10 +7126,10 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) * avoid printing the wrong alias here and use "preferred" instead */ if (strcmp(alias->alias, family->desc) == 0) { - qemu_printf("PowerPC %-16s (alias for preferred %s CPU)\n", + qemu_printf(" %-16s (alias for preferred %s CPU)\n", alias->alias, family->desc); } else { - qemu_printf("PowerPC %-16s (alias for %s)\n", + qemu_printf(" %-16s (alias for %s)\n", alias->alias, name); } } @@ -6945,6 +7140,7 @@ void ppc_cpu_list(void) { GSList *list; + qemu_printf("Available CPUs:\n"); list = object_class_get_list(TYPE_POWERPC_CPU, false); list = g_slist_sort(list, ppc_cpu_list_compare); g_slist_foreach(list, ppc_cpu_list_entry, NULL); @@ -6952,55 +7148,10 @@ void ppc_cpu_list(void) #ifdef CONFIG_KVM qemu_printf("\n"); - qemu_printf("PowerPC %s\n", "host"); + qemu_printf(" %s\n", "host"); #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); @@ -7031,16 +7182,23 @@ static bool ppc_cpu_has_work(CPUState *cs) return cs->interrupt_request & CPU_INTERRUPT_HARD; } -static void ppc_cpu_reset(DeviceState *dev) +static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) { - CPUState *s = CPU(dev); - PowerPCCPU *cpu = POWERPC_CPU(s); - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + return ppc_env_mmu_index(cpu_env(cs), ifetch); +} + +static void ppc_cpu_reset_hold(Object *obj, ResetType type) +{ + 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, type); + } msr = (target_ulong)0; msr |= (target_ulong)MSR_HVB; @@ -7082,10 +7240,12 @@ 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_mmcr01_updated(env); + pmu_mmcr01a_updated(env); } /* clean any pending stop state */ @@ -7095,13 +7255,21 @@ static void ppc_cpu_reset(DeviceState *dev) 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); /* tininess for underflow is detected before rounding */ set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); + /* + * PowerPC propagation rules: + * 1. A if it sNaN or qNaN + * 2. B if it sNaN or qNaN + * A signaling NaN is always silenced before returning it. + */ + set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); + set_float_2nan_prop_rule(float_2nan_prop_ab, &env->vec_status); for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; @@ -7111,18 +7279,29 @@ static void ppc_cpu_reset(DeviceState *dev) } env->spr[i] = spr->default_value; } + +#if defined(TARGET_PPC64) + bhrb_reset_state(env); +#endif } #ifndef CONFIG_USER_ONLY 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 @@ -7131,9 +7310,7 @@ static void ppc_cpu_exec_enter(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->cpu_exec_enter(cpu->vhyp, cpu); + cpu->vhyp_class->cpu_exec_enter(cpu->vhyp, cpu); } } @@ -7142,9 +7319,7 @@ static void ppc_cpu_exec_exit(CPUState *cs) PowerPCCPU *cpu = POWERPC_CPU(cs); if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->cpu_exec_exit(cpu->vhyp, cpu); + cpu->vhyp_class->cpu_exec_exit(cpu->vhyp, cpu); } } #endif /* CONFIG_TCG */ @@ -7157,7 +7332,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; @@ -7169,6 +7343,7 @@ static void ppc_cpu_instance_init(Object *obj) env->flags = pcc->flags; env->bfd_mach = pcc->bfd_mach; env->check_pow = pcc->check_pow; + env->check_attn = pcc->check_attn; /* * Mark HV mode as supported if the CPU has an MSR_HV bit in the @@ -7200,8 +7375,7 @@ static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr, bool best) 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; @@ -7222,11 +7396,7 @@ static void ppc_disas_set_info(CPUState *cs, disassemble_info *info) } static Property ppc_cpu_properties[] = { - DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), - DEFINE_PROP_BOOL("pre-2.10-migration", PowerPCCPU, pre_2_10_migration, - false), - DEFINE_PROP_BOOL("pre-3.0-migration", PowerPCCPU, pre_3_0_migration, - false), + /* add default property here */ DEFINE_PROP_END_OF_LIST(), }; @@ -7245,7 +7415,7 @@ 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, @@ -7254,10 +7424,15 @@ static const struct TCGCPUOps ppc_tcg_ops = { #else .tlb_fill = ppc_cpu_tlb_fill, .cpu_exec_interrupt = ppc_cpu_exec_interrupt, + .cpu_exec_halt = ppc_cpu_has_work, .do_interrupt = ppc_cpu_do_interrupt, .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 */ @@ -7267,6 +7442,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); @@ -7275,10 +7451,12 @@ 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; @@ -7286,12 +7464,15 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) 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; + + /* check_prot_access_type relies on MMU access and PAGE bits relations */ + qemu_build_assert(MMU_DATA_LOAD == 0 && MMU_DATA_STORE == 1 && + MMU_INST_FETCH == 2 && PAGE_READ == 1 && + PAGE_WRITE == 2 && PAGE_EXEC == 4); #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; @@ -7323,6 +7504,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 @@ -7346,8 +7533,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 " @@ -7357,7 +7543,7 @@ 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)); + ppc_env_mmu_index(env, true), ppc_env_mmu_index(env, false)); #if !defined(CONFIG_USER_ONLY) if (env->tb_env) { qemu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 @@ -7392,8 +7578,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/cpu_init.h b/target/ppc/cpu_init.h new file mode 100644 index 0000000000..f8fd6ff5cd --- /dev/null +++ b/target/ppc/cpu_init.h @@ -0,0 +1,91 @@ +#ifndef TARGET_PPC_CPU_INIT_H +#define TARGET_PPC_CPU_INIT_H + +#define PPC_INSNS_FLAGS_POWER9 \ + (PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB | \ + PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | \ + PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES | \ + PPC_FLOAT_STFIWX | PPC_FLOAT_EXT | PPC_CACHE | PPC_CACHE_ICBI | \ + PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | \ + PPC_MEM_TLBSYNC | PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | \ + PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD | \ + PPC_CILDST) + +#define PPC_INSNS_FLAGS_POWER10 PPC_INSNS_FLAGS_POWER9 +#define PPC_INSNS_FLAGS_POWER11 PPC_INSNS_FLAGS_POWER10 + +#define PPC_INSNS_FLAGS2_POWER_COMMON \ + (PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX | \ + PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | \ + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | \ + PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA205 | \ + PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_ISA300 | PPC2_PRCNTL | \ + PPC2_MEM_LWSYNC | PPC2_BCDA_ISA206) + +#define PPC_INSNS_FLAGS2_POWER9 \ + (PPC_INSNS_FLAGS2_POWER_COMMON | PPC2_TM) +#define PPC_INSNS_FLAGS2_POWER10 \ + (PPC_INSNS_FLAGS2_POWER_COMMON | PPC2_ISA310) +#define PPC_INSNS_FLAGS2_POWER11 PPC_INSNS_FLAGS2_POWER10 + +#define PPC_MSR_MASK_POWER_COMMON \ + ((1ull << MSR_SF) | \ + (1ull << MSR_HV) | \ + (1ull << MSR_VR) | \ + (1ull << MSR_VSX) | \ + (1ull << MSR_EE) | \ + (1ull << MSR_PR) | \ + (1ull << MSR_FP) | \ + (1ull << MSR_ME) | \ + (1ull << MSR_FE0) | \ + (1ull << MSR_SE) | \ + (1ull << MSR_DE) | \ + (1ull << MSR_FE1) | \ + (1ull << MSR_IR) | \ + (1ull << MSR_DR) | \ + (1ull << MSR_PMM) | \ + (1ull << MSR_RI) | \ + (1ull << MSR_LE)) + +#define PPC_MSR_MASK_POWER9 \ + (PPC_MSR_MASK_POWER_COMMON | (1ull << MSR_TM)) +#define PPC_MSR_MASK_POWER10 \ + PPC_MSR_MASK_POWER_COMMON +#define PPC_MSR_MASK_POWER11 PPC_MSR_MASK_POWER10 + +#define PPC_PCR_MASK_POWER9 \ + (PCR_COMPAT_2_05 | PCR_COMPAT_2_06 | PCR_COMPAT_2_07) +#define PPC_PCR_MASK_POWER10 \ + (PPC_PCR_MASK_POWER9 | PCR_COMPAT_3_00) +#define PPC_PCR_MASK_POWER11 PPC_PCR_MASK_POWER10 + +#define PPC_PCR_SUPPORTED_POWER9 \ + (PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05) +#define PPC_PCR_SUPPORTED_POWER10 \ + (PPC_PCR_SUPPORTED_POWER9 | PCR_COMPAT_3_10) +#define PPC_PCR_SUPPORTED_POWER11 PPC_PCR_SUPPORTED_POWER10 + +#define PPC_LPCR_MASK_POWER9 \ + (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | \ + (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | \ + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | \ + (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | \ + LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC | \ + LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE) +/* DD2 adds an extra HAIL bit */ +#define PPC_LPCR_MASK_POWER10 \ + (PPC_LPCR_MASK_POWER9 | LPCR_HAIL) +#define PPC_LPCR_MASK_POWER11 PPC_LPCR_MASK_POWER10 + +#define POWERPC_FLAGS_POWER_COMMON \ + (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | POWERPC_FLAG_BE | \ + POWERPC_FLAG_PMM | POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR | \ + POWERPC_FLAG_VSX | POWERPC_FLAG_SCV) + +#define POWERPC_FLAGS_POWER9 \ + (POWERPC_FLAGS_POWER_COMMON | POWERPC_FLAG_TM) +#define POWERPC_FLAGS_POWER10 \ + (POWERPC_FLAGS_POWER_COMMON | POWERPC_FLAG_BHRB) +#define POWERPC_FLAGS_POWER11 POWERPC_FLAGS_POWER10 + +#endif /* TARGET_PPC_CPU_INIT_H */ diff --git a/target/ppc/dfp_helper.c b/target/ppc/dfp_helper.c index cc024316d5..ecc3f79326 100644 --- a/target/ppc/dfp_helper.c +++ b/target/ppc/dfp_helper.c @@ -121,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) { @@ -138,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); @@ -249,7 +249,7 @@ static void dfp_set_FPRF_from_FRT_with_context(struct PPC_DFP *dfp, fprf = 0x05; break; default: - assert(0); /* should never get here */ + g_assert_not_reached(); } dfp->env->fpscr &= ~FP_FPRF; dfp->env->fpscr |= (fprf << FPSCR_FPRF); @@ -1243,7 +1243,7 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ } else if (decNumberIsQNaN(&dfp.b)) { \ vt.VsrD(1) = -2; \ } else { \ - assert(0); \ + g_assert_not_reached(); \ } \ set_dfp64(t, &vt); \ } else { \ @@ -1252,7 +1252,7 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *b) \ } else if ((size) == 128) { \ vt.VsrD(1) = dfp.b.exponent + 6176; \ } else { \ - assert(0); \ + g_assert_not_reached(); \ } \ set_dfp64(t, &vt); \ } \ @@ -1300,7 +1300,7 @@ void helper_##op(CPUPPCState *env, ppc_fprp_t *t, ppc_fprp_t *a, \ raw_inf = 0x1e000; \ bias = 6176; \ } else { \ - assert(0); \ + g_assert_not_reached(); \ } \ \ if (unlikely((exp < 0) || (exp > max_exp))) { \ diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 6cf88f635a..9f811af0a4 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,6 +19,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "sysemu/sysemu.h" +#include "sysemu/runstate.h" #include "cpu.h" #include "exec/exec-all.h" #include "internal.h" @@ -28,13 +30,14 @@ #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) { @@ -133,6 +136,27 @@ 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, target_ulong 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; @@ -165,9 +189,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; @@ -202,8 +225,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; } @@ -301,10 +324,7 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp, target_ulong msr, } ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; - if (ail == 0) { - return; - } - if (ail == 1) { + if (ail == 0 || ail == 1) { /* AIL=1 is reserved, treat it like AIL=0 */ return; } @@ -328,10 +348,7 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp, target_ulong msr, } else { ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; } - if (ail == 0) { - return; - } - if (ail == 1 || ail == 2) { + if (ail == 0 || ail == 1 || ail == 2) { /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */ return; } @@ -360,7 +377,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) { @@ -383,9 +400,8 @@ 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; @@ -404,40 +420,81 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, env->reserve_addr = -1; } +#ifdef CONFIG_TCG +/* + * This stops the machine and logs CPU state without killing QEMU (like + * cpu_abort()) because it is often a guest error as opposed to a QEMU error, + * so the machine can still be debugged. + */ +static G_NORETURN void powerpc_checkstop(CPUPPCState *env, const char *reason) +{ + CPUState *cs = env_cpu(env); + FILE *f; + + f = qemu_log_trylock(); + if (f) { + fprintf(f, "Entering checkstop state: %s\n", reason); + cpu_dump_state(cs, f, CPU_DUMP_FPU | CPU_DUMP_CCOP); + qemu_log_unlock(f); + } + + /* + * This stops the machine and logs CPU state without killing QEMU + * (like cpu_abort()) so the machine can still be debugged (because + * it is often a guest error). + */ + qemu_system_guest_panicked(NULL); + cpu_loop_exit_noexc(cs); +} + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +void helper_attn(CPUPPCState *env) +{ + /* POWER attn is unprivileged when enabled by HID, otherwise illegal */ + if ((*env->check_attn)(env)) { + powerpc_checkstop(env, "host executed attn"); + } else { + raise_exception_err(env, POWERPC_EXCP_HV_EMU, + POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); + } +} +#endif +#endif /* CONFIG_TCG */ + +static void powerpc_mcheck_checkstop(CPUPPCState *env) +{ + /* KVM guests always have MSR[ME] enabled */ +#ifdef CONFIG_TCG + if (FIELD_EX64(env->msr, MSR, ME)) { + return; + } + + powerpc_checkstop(env, "machine check with MSR[ME]=0"); +#endif +} + 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) { @@ -446,24 +503,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; @@ -498,7 +540,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; } @@ -525,76 +567,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]); @@ -622,11 +638,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; @@ -642,7 +656,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; } @@ -664,8 +678,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 */ @@ -692,82 +707,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]); @@ -795,11 +779,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; @@ -815,7 +797,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; } @@ -843,9 +825,8 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) * HV mode, we need to keep hypercall support. */ if (lev == 1 && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); + powerpc_reset_excp_state(cpu); return; } @@ -856,8 +837,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 */ @@ -866,93 +848,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]); @@ -980,11 +929,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; @@ -1000,7 +947,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; } @@ -1009,7 +956,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); @@ -1027,10 +974,9 @@ 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) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + if (lev == 1 && cpu->vhyp) { + cpu->vhyp_class->hypercall(cpu->vhyp, cpu); + powerpc_reset_excp_state(cpu); return; } @@ -1041,7 +987,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; @@ -1054,54 +1001,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; } @@ -1118,10 +1050,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) { @@ -1130,21 +1061,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); @@ -1164,6 +1081,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); } @@ -1178,11 +1096,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; @@ -1202,7 +1118,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; } @@ -1243,7 +1159,8 @@ 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 */ @@ -1257,21 +1174,23 @@ static void powerpc_excp_booke(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; 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; @@ -1280,12 +1199,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); } @@ -1317,7 +1232,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) @@ -1328,32 +1243,102 @@ 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); } /* @@ -1369,40 +1354,32 @@ static void powerpc_excp_books(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; + 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; @@ -1414,33 +1391,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) { @@ -1450,11 +1424,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; @@ -1470,7 +1442,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; } @@ -1478,7 +1450,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); @@ -1491,12 +1463,15 @@ 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)) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); + if (lev == 1 && books_vhyp_handles_hcall(cpu)) { + cpu->vhyp_class->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; } @@ -1531,14 +1506,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; @@ -1546,13 +1526,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 */ @@ -1581,55 +1576,40 @@ 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; } if ((new_msr & MSR_HVB) && books_vhyp_handles_hv_excp(cpu)) { - PPCVirtualHypervisorClass *vhc = - 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); - + cpu->vhyp_class->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); } } @@ -1638,20 +1618,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: @@ -1674,6 +1655,7 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_POWER8: case POWERPC_EXCP_POWER9: case POWERPC_EXCP_POWER10: + case POWERPC_EXCP_POWER11: powerpc_excp_books(cpu, excp); break; default: @@ -1688,58 +1670,61 @@ void ppc_cpu_do_interrupt(CPUState *cs) powerpc_excp(cpu, cs->exception_index); } -#if defined(TARGET_PPC64) +#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) +static int p7_interrupt_powersave(uint32_t pending_interrupts, + target_ulong lpcr) { - if ((env->pending_interrupts & PPC_INTERRUPT_EXT) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) { + if ((pending_interrupts & PPC_INTERRUPT_EXT) && + (lpcr & LPCR_P7_PECE0)) { return PPC_INTERRUPT_EXT; } - if ((env->pending_interrupts & PPC_INTERRUPT_DECR) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) { + if ((pending_interrupts & PPC_INTERRUPT_DECR) && + (lpcr & LPCR_P7_PECE1)) { return PPC_INTERRUPT_DECR; } - if ((env->pending_interrupts & PPC_INTERRUPT_MCK) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + if ((pending_interrupts & PPC_INTERRUPT_MCK) && + (lpcr & LPCR_P7_PECE2)) { return PPC_INTERRUPT_MCK; } - if ((env->pending_interrupts & PPC_INTERRUPT_HMI) && - (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) { + if ((pending_interrupts & PPC_INTERRUPT_HMI) && + (lpcr & LPCR_P7_PECE2)) { return PPC_INTERRUPT_HMI; } - if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + if (pending_interrupts & PPC_INTERRUPT_RESET) { return PPC_INTERRUPT_RESET; } return 0; } -static int p7_next_unmasked_interrupt(CPUPPCState *env) +static int p7_next_unmasked_interrupt(CPUPPCState *env, + uint32_t pending_interrupts, + target_ulong lpcr) { - PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + 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); + assert((pending_interrupts & P7_UNUSED_INTERRUPTS) == 0); if (cs->halted) { /* LPCR[PECE] controls which interrupts can exit power-saving mode */ - return p7_interrupt_powersave(env); + return p7_interrupt_powersave(pending_interrupts, lpcr); } /* Machine check exception */ - if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + if (pending_interrupts & PPC_INTERRUPT_MCK) { return PPC_INTERRUPT_MCK; } /* Hypervisor decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + if (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) { @@ -1749,9 +1734,9 @@ static int p7_next_unmasked_interrupt(CPUPPCState *env) } /* 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); + if (pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(lpcr & LPCR_LPES0); + bool heic = !!(lpcr & LPCR_HEIC); /* HEIC blocks delivery to the hypervisor */ if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) && !FIELD_EX64(env->msr, MSR, PR))) || @@ -1761,10 +1746,10 @@ static int p7_next_unmasked_interrupt(CPUPPCState *env) } if (msr_ee != 0) { /* Decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + if (pending_interrupts & PPC_INTERRUPT_DECR) { return PPC_INTERRUPT_DECR; } - if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + if (pending_interrupts & PPC_INTERRUPT_PERFM) { return PPC_INTERRUPT_PERFM; } } @@ -1777,42 +1762,45 @@ static int p7_next_unmasked_interrupt(CPUPPCState *env) 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) +static int p8_interrupt_powersave(uint32_t pending_interrupts, + target_ulong lpcr) { - if ((env->pending_interrupts & PPC_INTERRUPT_EXT) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) { + if ((pending_interrupts & PPC_INTERRUPT_EXT) && + (lpcr & LPCR_P8_PECE2)) { return PPC_INTERRUPT_EXT; } - if ((env->pending_interrupts & PPC_INTERRUPT_DECR) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) { + if ((pending_interrupts & PPC_INTERRUPT_DECR) && + (lpcr & LPCR_P8_PECE3)) { return PPC_INTERRUPT_DECR; } - if ((env->pending_interrupts & PPC_INTERRUPT_MCK) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + if ((pending_interrupts & PPC_INTERRUPT_MCK) && + (lpcr & LPCR_P8_PECE4)) { return PPC_INTERRUPT_MCK; } - if ((env->pending_interrupts & PPC_INTERRUPT_HMI) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) { + if ((pending_interrupts & PPC_INTERRUPT_HMI) && + (lpcr & LPCR_P8_PECE4)) { return PPC_INTERRUPT_HMI; } - if ((env->pending_interrupts & PPC_INTERRUPT_DOORBELL) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) { + if ((pending_interrupts & PPC_INTERRUPT_DOORBELL) && + (lpcr & LPCR_P8_PECE0)) { return PPC_INTERRUPT_DOORBELL; } - if ((env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) && - (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) { + if ((pending_interrupts & PPC_INTERRUPT_HDOORBELL) && + (lpcr & LPCR_P8_PECE1)) { return PPC_INTERRUPT_HDOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + if (pending_interrupts & PPC_INTERRUPT_RESET) { return PPC_INTERRUPT_RESET; } return 0; } -static int p8_next_unmasked_interrupt(CPUPPCState *env) +static int p8_next_unmasked_interrupt(CPUPPCState *env, + uint32_t pending_interrupts, + target_ulong lpcr) { - PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + 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; @@ -1820,18 +1808,18 @@ static int p8_next_unmasked_interrupt(CPUPPCState *env) if (cs->halted) { /* LPCR[PECE] controls which interrupts can exit power-saving mode */ - return p8_interrupt_powersave(env); + return p8_interrupt_powersave(pending_interrupts, lpcr); } /* Machine check exception */ - if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + if (pending_interrupts & PPC_INTERRUPT_MCK) { return PPC_INTERRUPT_MCK; } /* Hypervisor decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + if (pending_interrupts & PPC_INTERRUPT_HDECR) { /* LPCR will be clear when not supported so this will work */ - bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + bool hdice = !!(lpcr & LPCR_HDICE); if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) { /* HDEC clears on delivery */ return PPC_INTERRUPT_HDECR; @@ -1839,9 +1827,9 @@ static int p8_next_unmasked_interrupt(CPUPPCState *env) } /* 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); + if (pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(lpcr & LPCR_LPES0); + bool heic = !!(lpcr & LPCR_HEIC); /* HEIC blocks delivery to the hypervisor */ if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) && !FIELD_EX64(env->msr, MSR, PR))) || @@ -1851,20 +1839,20 @@ static int p8_next_unmasked_interrupt(CPUPPCState *env) } if (msr_ee != 0) { /* Decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + if (pending_interrupts & PPC_INTERRUPT_DECR) { return PPC_INTERRUPT_DECR; } - if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_DOORBELL) { return PPC_INTERRUPT_DOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_HDOORBELL) { return PPC_INTERRUPT_HDOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + if (pending_interrupts & PPC_INTERRUPT_PERFM) { return PPC_INTERRUPT_PERFM; } /* EBB exception */ - if (env->pending_interrupts & PPC_INTERRUPT_EBB) { + if (pending_interrupts & PPC_INTERRUPT_EBB) { /* * EBB exception must be taken in problem state and * with BESCR_GE set. @@ -1884,60 +1872,65 @@ static int p8_next_unmasked_interrupt(CPUPPCState *env) PPC_INTERRUPT_WDT | PPC_INTERRUPT_CDOORBELL | PPC_INTERRUPT_FIT | \ PPC_INTERRUPT_PIT | PPC_INTERRUPT_THERM) -static int p9_interrupt_powersave(CPUPPCState *env) +static int p9_interrupt_powersave(CPUPPCState *env, + uint32_t pending_interrupts, + target_ulong lpcr) { + /* External Exception */ - if ((env->pending_interrupts & PPC_INTERRUPT_EXT) && - (env->spr[SPR_LPCR] & LPCR_EEE)) { - bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + if ((pending_interrupts & PPC_INTERRUPT_EXT) && + (lpcr & LPCR_EEE)) { + bool heic = !!(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)) { + if ((pending_interrupts & PPC_INTERRUPT_DECR) && + (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) { + if (lpcr & LPCR_OEE) { + if (pending_interrupts & PPC_INTERRUPT_MCK) { return PPC_INTERRUPT_MCK; } - if (env->pending_interrupts & PPC_INTERRUPT_HMI) { + if (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)) { + if ((pending_interrupts & PPC_INTERRUPT_DOORBELL) && + (lpcr & LPCR_PDEE)) { return PPC_INTERRUPT_DOORBELL; } /* Hypervisor Doorbell Exception */ - if ((env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) && - (env->spr[SPR_LPCR] & LPCR_HDEE)) { + if ((pending_interrupts & PPC_INTERRUPT_HDOORBELL) && + (lpcr & LPCR_HDEE)) { return PPC_INTERRUPT_HDOORBELL; } /* Hypervisor virtualization exception */ - if ((env->pending_interrupts & PPC_INTERRUPT_HVIRT) && - (env->spr[SPR_LPCR] & LPCR_HVEE)) { + if ((pending_interrupts & PPC_INTERRUPT_HVIRT) && + (lpcr & LPCR_HVEE)) { return PPC_INTERRUPT_HVIRT; } - if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + if (pending_interrupts & PPC_INTERRUPT_RESET) { return PPC_INTERRUPT_RESET; } return 0; } -static int p9_next_unmasked_interrupt(CPUPPCState *env) +static int p9_next_unmasked_interrupt(CPUPPCState *env, + uint32_t pending_interrupts, + target_ulong lpcr) { - PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = CPU(cpu); + 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); + assert((pending_interrupts & P9_UNUSED_INTERRUPTS) == 0); if (cs->halted) { if (env->spr[SPR_PSSCR] & PSSCR_EC) { @@ -1945,7 +1938,7 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env) * When PSSCR[EC] is set, LPCR[PECE] controls which interrupts can * wakeup the processor */ - return p9_interrupt_powersave(env); + return p9_interrupt_powersave(env, pending_interrupts, lpcr); } else { /* * When it's clear, any system-caused exception exits power-saving @@ -1956,14 +1949,14 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env) } /* Machine check exception */ - if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + if (pending_interrupts & PPC_INTERRUPT_MCK) { return PPC_INTERRUPT_MCK; } /* Hypervisor decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + if (pending_interrupts & PPC_INTERRUPT_HDECR) { /* LPCR will be clear when not supported so this will work */ - bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + bool hdice = !!(lpcr & LPCR_HDICE); if ((msr_ee || !FIELD_EX64_HV(env->msr)) && hdice) { /* HDEC clears on delivery */ return PPC_INTERRUPT_HDECR; @@ -1971,18 +1964,18 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env) } /* Hypervisor virtualization interrupt */ - if (env->pending_interrupts & PPC_INTERRUPT_HVIRT) { + if (pending_interrupts & PPC_INTERRUPT_HVIRT) { /* LPCR will be clear when not supported so this will work */ - bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); + bool hvice = !!(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); + if (pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(lpcr & LPCR_LPES0); + bool heic = !!(lpcr & LPCR_HEIC); /* HEIC blocks delivery to the hypervisor */ if ((msr_ee && !(heic && FIELD_EX64_HV(env->msr) && !FIELD_EX64(env->msr, MSR, PR))) || @@ -1992,20 +1985,20 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env) } if (msr_ee != 0) { /* Decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + if (pending_interrupts & PPC_INTERRUPT_DECR) { return PPC_INTERRUPT_DECR; } - if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_DOORBELL) { return PPC_INTERRUPT_DOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_HDOORBELL) { return PPC_INTERRUPT_HDOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + if (pending_interrupts & PPC_INTERRUPT_PERFM) { return PPC_INTERRUPT_PERFM; } /* EBB exception */ - if (env->pending_interrupts & PPC_INTERRUPT_EBB) { + if (pending_interrupts & PPC_INTERRUPT_EBB) { /* * EBB exception must be taken in problem state and * with BESCR_GE set. @@ -2019,18 +2012,35 @@ static int p9_next_unmasked_interrupt(CPUPPCState *env) return 0; } -#endif +#endif /* TARGET_PPC64 */ -static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env) +static int ppc_next_unmasked_interrupt(CPUPPCState *env) { + uint32_t pending_interrupts = env->pending_interrupts; + target_ulong lpcr = env->spr[SPR_LPCR]; bool async_deliver; +#ifdef TARGET_PPC64 + switch (env->excp_model) { + case POWERPC_EXCP_POWER7: + return p7_next_unmasked_interrupt(env, pending_interrupts, lpcr); + case POWERPC_EXCP_POWER8: + return p8_next_unmasked_interrupt(env, pending_interrupts, lpcr); + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + case POWERPC_EXCP_POWER11: + return p9_next_unmasked_interrupt(env, pending_interrupts, lpcr); + default: + break; + } +#endif + /* External reset */ - if (env->pending_interrupts & PPC_INTERRUPT_RESET) { + if (pending_interrupts & PPC_INTERRUPT_RESET) { return PPC_INTERRUPT_RESET; } /* Machine check exception */ - if (env->pending_interrupts & PPC_INTERRUPT_MCK) { + if (pending_interrupts & PPC_INTERRUPT_MCK) { return PPC_INTERRUPT_MCK; } #if 0 /* TODO */ @@ -2049,9 +2059,9 @@ static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env) async_deliver = FIELD_EX64(env->msr, MSR, EE) || env->resume_as_sreset; /* Hypervisor decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_HDECR) { + if (pending_interrupts & PPC_INTERRUPT_HDECR) { /* LPCR will be clear when not supported so this will work */ - bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); + bool hdice = !!(lpcr & LPCR_HDICE); if ((async_deliver || !FIELD_EX64_HV(env->msr)) && hdice) { /* HDEC clears on delivery */ return PPC_INTERRUPT_HDECR; @@ -2059,18 +2069,18 @@ static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env) } /* Hypervisor virtualization interrupt */ - if (env->pending_interrupts & PPC_INTERRUPT_HVIRT) { + if (pending_interrupts & PPC_INTERRUPT_HVIRT) { /* LPCR will be clear when not supported so this will work */ - bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); + bool hvice = !!(lpcr & LPCR_HVICE); if ((async_deliver || !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); + if (pending_interrupts & PPC_INTERRUPT_EXT) { + bool lpes0 = !!(lpcr & LPCR_LPES0); + bool heic = !!(lpcr & LPCR_HEIC); /* HEIC blocks delivery to the hypervisor */ if ((async_deliver && !(heic && FIELD_EX64_HV(env->msr) && !FIELD_EX64(env->msr, MSR, PR))) || @@ -2080,45 +2090,45 @@ static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env) } if (FIELD_EX64(env->msr, MSR, CE)) { /* External critical interrupt */ - if (env->pending_interrupts & PPC_INTERRUPT_CEXT) { + if (pending_interrupts & PPC_INTERRUPT_CEXT) { return PPC_INTERRUPT_CEXT; } } if (async_deliver != 0) { /* Watchdog timer on embedded PowerPC */ - if (env->pending_interrupts & PPC_INTERRUPT_WDT) { + if (pending_interrupts & PPC_INTERRUPT_WDT) { return PPC_INTERRUPT_WDT; } - if (env->pending_interrupts & PPC_INTERRUPT_CDOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_CDOORBELL) { return PPC_INTERRUPT_CDOORBELL; } /* Fixed interval timer on embedded PowerPC */ - if (env->pending_interrupts & PPC_INTERRUPT_FIT) { + if (pending_interrupts & PPC_INTERRUPT_FIT) { return PPC_INTERRUPT_FIT; } /* Programmable interval timer on embedded PowerPC */ - if (env->pending_interrupts & PPC_INTERRUPT_PIT) { + if (pending_interrupts & PPC_INTERRUPT_PIT) { return PPC_INTERRUPT_PIT; } /* Decrementer exception */ - if (env->pending_interrupts & PPC_INTERRUPT_DECR) { + if (pending_interrupts & PPC_INTERRUPT_DECR) { return PPC_INTERRUPT_DECR; } - if (env->pending_interrupts & PPC_INTERRUPT_DOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_DOORBELL) { return PPC_INTERRUPT_DOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_HDOORBELL) { + if (pending_interrupts & PPC_INTERRUPT_HDOORBELL) { return PPC_INTERRUPT_HDOORBELL; } - if (env->pending_interrupts & PPC_INTERRUPT_PERFM) { + if (pending_interrupts & PPC_INTERRUPT_PERFM) { return PPC_INTERRUPT_PERFM; } /* Thermal interrupt */ - if (env->pending_interrupts & PPC_INTERRUPT_THERM) { + if (pending_interrupts & PPC_INTERRUPT_THERM) { return PPC_INTERRUPT_THERM; } /* EBB exception */ - if (env->pending_interrupts & PPC_INTERRUPT_EBB) { + if (pending_interrupts & PPC_INTERRUPT_EBB) { /* * EBB exception must be taken in problem state and * with BESCR_GE set. @@ -2133,23 +2143,6 @@ static int ppc_next_unmasked_interrupt_generic(CPUPPCState *env) return 0; } -static int ppc_next_unmasked_interrupt(CPUPPCState *env) -{ - switch (env->excp_model) { -#if defined(TARGET_PPC64) - 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); -#endif - default: - return ppc_next_unmasked_interrupt_generic(env); - } -} - /* * Sets CPU_INTERRUPT_HARD if there is at least one unmasked interrupt to be * delivered and clears CPU_INTERRUPT_HARD otherwise. @@ -2166,29 +2159,19 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env) void ppc_maybe_interrupt(CPUPPCState *env) { CPUState *cs = env_cpu(env); - bool locked = false; - - if (!qemu_mutex_iothread_locked()) { - locked = true; - qemu_mutex_lock_iothread(); - } + BQL_LOCK_GUARD(); if (ppc_next_unmasked_interrupt(env)) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } - - if (locked) { - qemu_mutex_unlock_iothread(); - } } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 static void p7_deliver_interrupt(CPUPPCState *env, int interrupt) { PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); switch (interrupt) { case PPC_INTERRUPT_MCK: /* Machine check exception */ @@ -2214,7 +2197,6 @@ static void p7_deliver_interrupt(CPUPPCState *env, int interrupt) 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: @@ -2232,14 +2214,14 @@ static void p7_deliver_interrupt(CPUPPCState *env, int interrupt) assert(!env->resume_as_sreset); break; default: - cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt); + 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); - CPUState *cs = env_cpu(env); switch (interrupt) { case PPC_INTERRUPT_MCK: /* Machine check exception */ @@ -2265,7 +2247,9 @@ static void p8_deliver_interrupt(CPUPPCState *env, int interrupt) powerpc_excp(cpu, POWERPC_EXCP_DECR); break; case PPC_INTERRUPT_DOORBELL: - env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + if (!env->resume_as_sreset) { + env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + } if (is_book3s_arch2x(env)) { powerpc_excp(cpu, POWERPC_EXCP_SDOOR); } else { @@ -2273,11 +2257,12 @@ static void p8_deliver_interrupt(CPUPPCState *env, int interrupt) } break; case PPC_INTERRUPT_HDOORBELL: - env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL; + if (!env->resume_as_sreset) { + 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 */ @@ -2303,7 +2288,8 @@ static void p8_deliver_interrupt(CPUPPCState *env, int interrupt) assert(!env->resume_as_sreset); break; default: - cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt); + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); } } @@ -2329,6 +2315,7 @@ static void p9_deliver_interrupt(CPUPPCState *env, int interrupt) case PPC_INTERRUPT_HDECR: /* Hypervisor decrementer exception */ /* HDEC clears on delivery */ + /* XXX: should not see an HDEC if resume_as_sreset. assert? */ env->pending_interrupts &= ~PPC_INTERRUPT_HDECR; powerpc_excp(cpu, POWERPC_EXCP_HDECR); break; @@ -2348,15 +2335,18 @@ static void p9_deliver_interrupt(CPUPPCState *env, int interrupt) powerpc_excp(cpu, POWERPC_EXCP_DECR); break; case PPC_INTERRUPT_DOORBELL: - env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + if (!env->resume_as_sreset) { + env->pending_interrupts &= ~PPC_INTERRUPT_DOORBELL; + } powerpc_excp(cpu, POWERPC_EXCP_SDOOR); break; case PPC_INTERRUPT_HDOORBELL: - env->pending_interrupts &= ~PPC_INTERRUPT_HDOORBELL; + if (!env->resume_as_sreset) { + 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 */ @@ -2382,15 +2372,29 @@ static void p9_deliver_interrupt(CPUPPCState *env, int interrupt) assert(!env->resume_as_sreset); break; default: - cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt); + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); } } -#endif +#endif /* TARGET_PPC64 */ -static void ppc_deliver_interrupt_generic(CPUPPCState *env, int interrupt) +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: + case POWERPC_EXCP_POWER11: + return p9_deliver_interrupt(env, interrupt); + default: + break; + } +#endif PowerPCCPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); switch (interrupt) { case PPC_INTERRUPT_RESET: /* External reset */ @@ -2457,7 +2461,6 @@ static void ppc_deliver_interrupt_generic(CPUPPCState *env, int interrupt) 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 */ @@ -2487,34 +2490,21 @@ static void ppc_deliver_interrupt_generic(CPUPPCState *env, int interrupt) assert(!env->resume_as_sreset); break; default: - cpu_abort(cs, "Invalid PowerPC interrupt %d. Aborting\n", interrupt); - } -} - -static void ppc_deliver_interrupt(CPUPPCState *env, int interrupt) -{ - switch (env->excp_model) { -#if defined(TARGET_PPC64) - case POWERPC_EXCP_POWER7: - p7_deliver_interrupt(env, interrupt); - break; - case POWERPC_EXCP_POWER8: - p8_deliver_interrupt(env, interrupt); - break; - case POWERPC_EXCP_POWER9: - case POWERPC_EXCP_POWER10: - p9_deliver_interrupt(env, interrupt); - break; -#endif - default: - ppc_deliver_interrupt_generic(env, interrupt); + cpu_abort(env_cpu(env), "Invalid PowerPC interrupt %d. Aborting\n", + interrupt); } } +/* + * system reset is not delivered via normal irq method, so have to set + * halted = 0 to resume CPU running if it was halted. Possibly we should + * move it over to using PPC_INTERRUPT_RESET rather than async_run_on_cpu. + */ void ppc_cpu_do_system_reset(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); + cs->halted = 0; powerpc_excp(cpu, POWERPC_EXCP_RESET); } @@ -2536,13 +2526,13 @@ void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) /* Anything for nested required here? MSR[HV] bit? */ + cs->halted = 0; powerpc_set_excp_state(cpu, vector, msr); } 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) == 0) { @@ -2604,17 +2594,14 @@ 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); } } @@ -2624,7 +2611,7 @@ void helper_ppc_maybe_interrupt(CPUPPCState *env) ppc_maybe_interrupt(env); } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 void helper_scv(CPUPPCState *env, uint32_t lev) { if (env->spr[SPR_FSCR] & (1ull << FSCR_SCV)) { @@ -2636,23 +2623,26 @@ void helper_scv(CPUPPCState *env, uint32_t lev) 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); @@ -2660,7 +2650,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; @@ -2676,7 +2666,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; @@ -2689,7 +2679,7 @@ void helper_rfi(CPUPPCState *env) do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); } -#if defined(TARGET_PPC64) +#ifdef TARGET_PPC64 void helper_rfid(CPUPPCState *env) { /* @@ -2710,9 +2700,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; @@ -2787,7 +2775,7 @@ void raise_ebb_perfm_exception(CPUPPCState *env) do_ebb(env, POWERPC_EXCP_PERFM_EBB); } -#endif +#endif /* TARGET_PPC64 */ /*****************************************************************************/ /* Embedded PowerPC specific helpers */ @@ -2812,11 +2800,9 @@ 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, +void helper_TW(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || @@ -2829,8 +2815,8 @@ void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, } } -#if defined(TARGET_PPC64) -void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, +#ifdef TARGET_PPC64 +void helper_TD(CPUPPCState *env, target_ulong arg1, target_ulong arg2, uint32_t flags) { if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || @@ -2842,10 +2828,8 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, POWERPC_EXCP_TRAP, GETPC()); } } -#endif -#endif +#endif /* TARGET_PPC64 */ -#ifdef CONFIG_TCG static uint32_t helper_SIMON_LIKE_32_64(uint32_t x, uint64_t key, uint32_t lane) { const uint16_t c = 0xfffc; @@ -2905,35 +2889,59 @@ static uint64_t hash_digest(uint64_t ra, uint64_t rb, uint64_t key) 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" -#define HELPER_HASH(op, key, store) \ +#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) \ { \ - 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()); \ - } \ + 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) -HELPER_HASH(HASHCHK, env->spr[SPR_HASHKEYR], false) -HELPER_HASH(HASHSTP, env->spr[SPR_HASHPKEYR], true) -HELPER_HASH(HASHCHKP, env->spr[SPR_HASHPKEYR], false) -#endif /* CONFIG_TCG */ - -#if !defined(CONFIG_USER_ONLY) - -#ifdef CONFIG_TCG +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) { @@ -2979,16 +2987,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)) { + if ((rb & DBELL_BRDCAST_MASK) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { ppc_set_irq(cpu, irq, 1); } } - qemu_mutex_unlock_iothread(); + bql_unlock(); } /* Server Processor Control */ @@ -3003,6 +3011,44 @@ static bool dbell_type_server(target_ulong rb) return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; } +static inline bool dbell_bcast_core(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_CORE; +} + +static inline bool dbell_bcast_subproc(target_ulong rb) +{ + return (rb & DBELL_BRDCAST_MASK) == DBELL_BRDCAST_SUBPROC; +} + +/* + * Send an interrupt to a thread in the same core as env). + */ +static void msgsnd_core_tir(CPUPPCState *env, uint32_t target_tir, int irq) +{ + PowerPCCPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + + if (ppc_cpu_lpar_single_threaded(cs)) { + if (target_tir == 0) { + ppc_set_irq(cpu, irq, 1); + } + } else { + CPUState *ccs; + + /* Does iothread need to be locked for walking CPU list? */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + if (target_tir == ppc_cpu_tir(ccpu)) { + ppc_set_irq(ccpu, irq, 1); + break; + } + } + bql_unlock(); + } +} + void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) { if (!dbell_type_server(rb)) { @@ -3012,35 +3058,53 @@ void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) 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(); - 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) { - ppc_set_irq(cpu, irq, 1); - } - } - qemu_mutex_unlock_iothread(); -} - -void helper_book3s_msgsnd(target_ulong rb) +void helper_book3s_msgsnd(CPUPPCState *env, target_ulong rb) { int pir = rb & DBELL_PROCIDTAG_MASK; + bool brdcast = false; + CPUState *cs, *ccs; + PowerPCCPU *cpu; if (!dbell_type_server(rb)) { return; } - book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); + /* POWER8 msgsnd is like msgsndp (targets a thread within core) */ + if (!(env->insns_flags2 & PPC2_ISA300)) { + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_HDOORBELL); + return; + } + + /* POWER9 and later msgsnd is a global (targets any thread) */ + cpu = ppc_get_vcpu_by_pir(pir); + if (!cpu) { + return; + } + cs = CPU(cpu); + + if (dbell_bcast_core(rb) || (dbell_bcast_subproc(rb) && + (env->flags & POWERPC_FLAG_SMT_1LPAR))) { + brdcast = true; + } + + if (ppc_cpu_core_single_threaded(cs) || !brdcast) { + ppc_set_irq(cpu, PPC_INTERRUPT_HDOORBELL, 1); + return; + } + + /* + * Why is bql needed for walking CPU list? Answer seems to be because ppc + * irq handling needs it, but ppc_set_irq takes the lock itself if needed, + * so could this be removed? + */ + bql_lock(); + THREAD_SIBLING_FOREACH(cs, ccs) { + ppc_set_irq(POWERPC_CPU(ccs), PPC_INTERRUPT_HDOORBELL, 1); + } + bql_unlock(); } -#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); @@ -3049,39 +3113,47 @@ void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) return; } - ppc_set_irq(env_archcpu(env), PPC_INTERRUPT_HDOORBELL, 0); + 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; - helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); if (!dbell_type_server(rb)) { return; } - /* TODO: TCG supports only one thread */ - - book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL); + msgsnd_core_tir(env, rb & PPC_BITMASK(57, 63), PPC_INTERRUPT_DOORBELL); } #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); - insn = cpu_ldl_code(env, env->nip); + insn = ppc_ldl_code(env, env->nip); switch (env->mmu_model) { case POWERPC_MMU_SOFT_4xx: @@ -3100,5 +3172,150 @@ 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: + case POWERPC_EXCP_POWER11: + /* + * 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 a66e16c212..230466a87f 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) @@ -524,54 +490,12 @@ static void float_invalid_op_addsub(CPUPPCState *env, int flags, } } -/* fadd - fadd. */ -float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void addsub_flags_handler(CPUPPCState *env, int flags, + uintptr_t ra) { - float64 ret = float64_add(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); + float_invalid_op_addsub(env, flags, 1, ra); } - - return ret; -} - -/* fadds - fadds. */ -float64 helper_fadds(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_add(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - return ret; -} - -/* fsub - fsub. */ -float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64_sub(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - - return ret; -} - -/* fsubs - fsubs. */ -float64 helper_fsubs(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_sub(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_addsub(env, flags, 1, GETPC()); - } - return ret; } static void float_invalid_op_mul(CPUPPCState *env, int flags, @@ -584,29 +508,11 @@ static void float_invalid_op_mul(CPUPPCState *env, int flags, } } -/* fmul - fmul. */ -float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void mul_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - float64 ret = float64_mul(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_mul(env, flags, 1, GETPC()); + float_invalid_op_mul(env, flags, 1, ra); } - - return ret; -} - -/* fmuls - fmuls. */ -float64 helper_fmuls(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_mul(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_mul(env, flags, 1, GETPC()); - } - return ret; } static void float_invalid_op_div(CPUPPCState *env, int flags, @@ -621,36 +527,14 @@ static void float_invalid_op_div(CPUPPCState *env, int flags, } } -/* fdiv - fdiv. */ -float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2) +static inline void div_flags_handler(CPUPPCState *env, int flags, uintptr_t ra) { - float64 ret = float64_div(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_div(env, flags, 1, GETPC()); + float_invalid_op_div(env, flags, 1, ra); } if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); + float_zero_divide_excp(env, ra); } - - return ret; -} - -/* fdivs - fdivs. */ -float64 helper_fdivs(CPUPPCState *env, float64 arg1, float64 arg2) -{ - float64 ret = float64r32_div(arg1, arg2, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid)) { - float_invalid_op_div(env, flags, 1, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - } - - return ret; } static uint64_t float_invalid_cvt(CPUPPCState *env, int flags, @@ -789,7 +673,7 @@ static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b, uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \ uint64_t arg2, uint64_t arg3) \ { return do_fmadd(env, arg1, arg2, arg3, madd_flags, GETPC()); } \ - uint64_t helper_##op##s(CPUPPCState *env, uint64_t arg1, \ + uint64_t helper_##op##S(CPUPPCState *env, uint64_t arg1, \ uint64_t arg2, uint64_t arg3) \ { return do_fmadds(env, arg1, arg2, arg3, madd_flags, GETPC()); } @@ -798,10 +682,10 @@ static uint64_t do_fmadds(CPUPPCState *env, float64 a, float64 b, #define NMADD_FLGS float_muladd_negate_result #define NMSUB_FLGS (float_muladd_negate_c | float_muladd_negate_result) -FPU_FMADD(fmadd, MADD_FLGS) -FPU_FMADD(fnmadd, NMADD_FLGS) -FPU_FMADD(fmsub, MSUB_FLGS) -FPU_FMADD(fnmsub, NMSUB_FLGS) +FPU_FMADD(FMADD, MADD_FLGS) +FPU_FMADD(FNMADD, NMADD_FLGS) +FPU_FMADD(FMSUB, MSUB_FLGS) +FPU_FMADD(FNMSUB, NMSUB_FLGS) /* frsp - frsp. */ static uint64_t do_frsp(CPUPPCState *env, uint64_t arg, uintptr_t retaddr) @@ -846,81 +730,66 @@ float64 helper_##name(CPUPPCState *env, float64 arg) \ FPU_FSQRT(FSQRT, float64_sqrt) FPU_FSQRT(FSQRTS, float64r32_sqrt) -/* fre - fre. */ -float64 helper_fre(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 ret = float64_div(float64_one, arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid_snan)) { - float_invalid_op_vxsnan(env, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - /* For FPSCR.ZE == 0, the result is 1/2. */ - ret = float64_set_sign(float64_half, float64_is_neg(arg)); - } - - return ret; +#define FPU_FRE(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + /* "Estimate" the reciprocal with actual division. */ \ + float64 ret = op(float64_one, arg, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + \ + if (unlikely(flags & float_flag_invalid_snan)) { \ + float_invalid_op_vxsnan(env, GETPC()); \ + } \ + if (unlikely(flags & float_flag_divbyzero)) { \ + float_zero_divide_excp(env, GETPC()); \ + /* For FPSCR.ZE == 0, the result is 1/2. */ \ + ret = float64_set_sign(float64_half, float64_is_neg(arg)); \ + } \ + \ + return ret; \ } -/* fres - fres. */ -uint64_t helper_fres(CPUPPCState *env, uint64_t arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 ret = float64r32_div(float64_one, arg, &env->fp_status); - int flags = get_float_exception_flags(&env->fp_status); - - if (unlikely(flags & float_flag_invalid_snan)) { - float_invalid_op_vxsnan(env, GETPC()); - } - if (unlikely(flags & float_flag_divbyzero)) { - float_zero_divide_excp(env, GETPC()); - /* For FPSCR.ZE == 0, the result is 1/2. */ - ret = float64_set_sign(float64_half, float64_is_neg(arg)); - } - - return ret; +#define FPU_FRSQRTE(name, op) \ +float64 helper_##name(CPUPPCState *env, float64 arg) \ +{ \ + /* "Estimate" the reciprocal with actual division. */ \ + float64 rets = float64_sqrt(arg, &env->fp_status); \ + float64 retd = op(float64_one, rets, &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()); \ + } \ + if (unlikely(flags & float_flag_divbyzero)) { \ + /* Reciprocal of (square root of) zero. */ \ + float_zero_divide_excp(env, GETPC()); \ + } \ + \ + return retd; \ } -/* frsqrte - frsqrte. */ -float64 helper_frsqrte(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 rets = float64_sqrt(arg, &env->fp_status); - float64 retd = float64_div(float64_one, rets, &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()); - } - if (unlikely(flags & float_flag_divbyzero)) { - /* Reciprocal of (square root of) zero. */ - float_zero_divide_excp(env, GETPC()); - } - - return retd; +#define FPU_HELPER(name, op, flags_handler) \ +float64 helper_##name(CPUPPCState *env, float64 arg1, float64 arg2) \ +{ \ + float64 ret = op(arg1, arg2, &env->fp_status); \ + int flags = get_float_exception_flags(&env->fp_status); \ + uintptr_t ra = GETPC(); \ + flags_handler(env, flags, ra); \ + return ret; \ } -/* frsqrtes - frsqrtes. */ -float64 helper_frsqrtes(CPUPPCState *env, float64 arg) -{ - /* "Estimate" the reciprocal with actual division. */ - float64 rets = float64_sqrt(arg, &env->fp_status); - float64 retd = float64r32_div(float64_one, rets, &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()); - } - if (unlikely(flags & float_flag_divbyzero)) { - /* Reciprocal of (square root of) zero. */ - float_zero_divide_excp(env, GETPC()); - } - - return retd; -} +FPU_FRE(FRE, float64_div) +FPU_FRE(FRES, float64r32_div) +FPU_FRSQRTE(FRSQRTE, float64_div) +FPU_FRSQRTE(FRSQRTES, float64r32_div) +FPU_HELPER(FADD, float64_add, addsub_flags_handler) +FPU_HELPER(FADDS, float64r32_add, addsub_flags_handler) +FPU_HELPER(FSUB, float64_sub, addsub_flags_handler) +FPU_HELPER(FSUBS, float64r32_sub, addsub_flags_handler) +FPU_HELPER(FMUL, float64_mul, mul_flags_handler) +FPU_HELPER(FMULS, float64r32_mul, mul_flags_handler) +FPU_HELPER(FDIV, float64_div, div_flags_handler) +FPU_HELPER(FDIVS, float64r32_div, div_flags_handler) /* fsel - fsel. */ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) @@ -937,7 +806,7 @@ uint64_t helper_FSEL(uint64_t a, uint64_t b, uint64_t c) } } -uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) +uint32_t helper_FTDIV(uint64_t fra, uint64_t frb) { int fe_flag = 0; int fg_flag = 0; @@ -973,7 +842,7 @@ uint32_t helper_ftdiv(uint64_t fra, uint64_t frb) return 0x8 | (fg_flag ? 4 : 0) | (fe_flag ? 2 : 0); } -uint32_t helper_ftsqrt(uint64_t frb) +uint32_t helper_FTSQRT(uint64_t frb) { int fe_flag = 0; int fg_flag = 0; @@ -1730,14 +1599,14 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_ADD_SUB(xsadddp, add, 1, float64, VsrD(0), 1, 0) -VSX_ADD_SUB(xsaddsp, add, 1, float64, VsrD(0), 1, 1) -VSX_ADD_SUB(xvadddp, add, 2, float64, VsrD(i), 0, 0) -VSX_ADD_SUB(xvaddsp, add, 4, float32, VsrW(i), 0, 0) -VSX_ADD_SUB(xssubdp, sub, 1, float64, VsrD(0), 1, 0) -VSX_ADD_SUB(xssubsp, sub, 1, float64, VsrD(0), 1, 1) -VSX_ADD_SUB(xvsubdp, sub, 2, float64, VsrD(i), 0, 0) -VSX_ADD_SUB(xvsubsp, sub, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(XSADDDP, add, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(XSADDSP, add, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(XVADDDP, add, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(XVADDSP, add, 4, float32, VsrW(i), 0, 0) +VSX_ADD_SUB(XSSUBDP, sub, 1, float64, VsrD(0), 1, 0) +VSX_ADD_SUB(XSSUBSP, sub, 1, float64, VsrD(0), 1, 1) +VSX_ADD_SUB(XVSUBDP, sub, 2, float64, VsrD(i), 0, 0) +VSX_ADD_SUB(XVSUBSP, sub, 4, float32, VsrW(i), 0, 0) void helper_xsaddqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -1807,10 +1676,10 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_MUL(xsmuldp, 1, float64, VsrD(0), 1, 0) -VSX_MUL(xsmulsp, 1, float64, VsrD(0), 1, 1) -VSX_MUL(xvmuldp, 2, float64, VsrD(i), 0, 0) -VSX_MUL(xvmulsp, 4, float32, VsrW(i), 0, 0) +VSX_MUL(XSMULDP, 1, float64, VsrD(0), 1, 0) +VSX_MUL(XSMULSP, 1, float64, VsrD(0), 1, 1) +VSX_MUL(XVMULDP, 2, float64, VsrD(i), 0, 0) +VSX_MUL(XVMULSP, 4, float32, VsrW(i), 0, 0) void helper_xsmulqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -1881,10 +1750,10 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, sfifprf, GETPC()); \ } -VSX_DIV(xsdivdp, 1, float64, VsrD(0), 1, 0) -VSX_DIV(xsdivsp, 1, float64, VsrD(0), 1, 1) -VSX_DIV(xvdivdp, 2, float64, VsrD(i), 0, 0) -VSX_DIV(xvdivsp, 4, float32, VsrW(i), 0, 0) +VSX_DIV(XSDIVDP, 1, float64, VsrD(0), 1, 0) +VSX_DIV(XSDIVSP, 1, float64, VsrD(0), 1, 1) +VSX_DIV(XVDIVDP, 2, float64, VsrD(i), 0, 0) +VSX_DIV(XVDIVSP, 4, float32, VsrW(i), 0, 0) void helper_xsdivqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) @@ -2514,12 +2383,12 @@ void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ do_float_check_status(env, false, GETPC()); \ } -VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, VsrD(0)) -VSX_MAX_MIN(xvmaxdp, maxnum, 2, float64, VsrD(i)) -VSX_MAX_MIN(xvmaxsp, maxnum, 4, float32, VsrW(i)) -VSX_MAX_MIN(xsmindp, minnum, 1, float64, VsrD(0)) -VSX_MAX_MIN(xvmindp, minnum, 2, float64, VsrD(i)) -VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(XSMAXDP, maxnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(XVMAXDP, maxnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(XVMAXSP, maxnum, 4, float32, VsrW(i)) +VSX_MAX_MIN(XSMINDP, minnum, 1, float64, VsrD(0)) +VSX_MAX_MIN(XVMINDP, minnum, 2, float64, VsrD(i)) +VSX_MAX_MIN(XVMINSP, minnum, 4, float32, VsrW(i)) #define VSX_MAX_MINC(name, max, tp, fld) \ void helper_##name(CPUPPCState *env, \ @@ -2658,14 +2527,14 @@ uint32_t helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ return crf6; \ } -VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) -VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1) -VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1) -VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0) -VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1) -VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1) -VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1) -VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) +VSX_CMP(XVCMPEQDP, 2, float64, VsrD(i), eq, 0, 1) +VSX_CMP(XVCMPGEDP, 2, float64, VsrD(i), le, 1, 1) +VSX_CMP(XVCMPGTDP, 2, float64, VsrD(i), lt, 1, 1) +VSX_CMP(XVCMPNEDP, 2, float64, VsrD(i), eq, 0, 0) +VSX_CMP(XVCMPEQSP, 4, float32, VsrW(i), eq, 0, 1) +VSX_CMP(XVCMPGESP, 4, float32, VsrW(i), le, 1, 1) +VSX_CMP(XVCMPGTSP, 4, float32, VsrW(i), lt, 1, 1) +VSX_CMP(XVCMPNESP, 4, float32, VsrW(i), eq, 0, 0) /* * VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion @@ -2914,20 +2783,22 @@ 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 = 0; \ ppc_vsr_t t = { }; \ int i, flags; \ \ - helper_reset_fpstatus(env); \ - \ for (i = 0; i < nels; i++) { \ + 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());\ } \ } \ \ *xt = t; \ + env->fp_status.float_exception_flags = all_flags; \ do_float_check_status(env, sfi, GETPC()); \ } @@ -2979,15 +2850,16 @@ 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 = 0; \ ppc_vsr_t t = { }; \ int i, flags; \ \ - helper_reset_fpstatus(env); \ - \ for (i = 0; i < nels; i++) { \ + 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()); \ @@ -2996,6 +2868,7 @@ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ } \ \ *xt = t; \ + env->fp_status.float_exception_flags = all_flags; \ do_float_check_status(env, sfi, GETPC()); \ } 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 8dd22a35e4..5a77e761bd 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -1,8 +1,8 @@ DEF_HELPER_FLAGS_3(raise_exception_err, TCG_CALL_NO_WG, noreturn, env, i32, i32) DEF_HELPER_FLAGS_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_FLAGS_4(tw, TCG_CALL_NO_WG, void, env, tl, tl, i32) +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) +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) @@ -25,13 +25,18 @@ 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_2(store_mmcrA, 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 @@ -41,21 +46,23 @@ DEF_HELPER_FLAGS_3(stmw, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_4(lsw, void, env, tl, i32, i32) DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) DEF_HELPER_FLAGS_4(stsw, TCG_CALL_NO_WG, void, env, tl, i32, i32) -DEF_HELPER_FLAGS_3(dcbz, TCG_CALL_NO_WG, void, env, tl, i32) -DEF_HELPER_FLAGS_3(dcbzep, TCG_CALL_NO_WG, void, env, tl, i32) +DEF_HELPER_FLAGS_3(dcbz, TCG_CALL_NO_WG, void, env, tl, int) +#ifdef TARGET_PPC64 +DEF_HELPER_FLAGS_2(dcbzl, TCG_CALL_NO_WG, void, env, tl) +#endif DEF_HELPER_FLAGS_2(icbi, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) -DEF_HELPER_4(divdeu, i64, env, i64, i64, i32) -DEF_HELPER_4(divde, i64, env, i64, i64, i32) +DEF_HELPER_4(DIVDEU, i64, env, i64, i64, i32) +DEF_HELPER_4(DIVDE, i64, env, i64, i64, i32) #endif -DEF_HELPER_4(divweu, tl, env, tl, tl, i32) -DEF_HELPER_4(divwe, tl, env, tl, tl, i32) +DEF_HELPER_4(DIVWEU, tl, env, tl, tl, i32) +DEF_HELPER_4(DIVWE, tl, env, tl, tl, i32) -DEF_HELPER_FLAGS_1(popcntb, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_2(cmpb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_1(POPCNTB, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_2(CMPB, TCG_CALL_NO_RWG_SE, tl, tl, tl) 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) @@ -63,12 +70,12 @@ 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) -DEF_HELPER_FLAGS_2(bpermd, TCG_CALL_NO_RWG_SE, i64, i64, i64) +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) +DEF_HELPER_FLAGS_2(BPERMD, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_3(srad, tl, env, tl, tl) -DEF_HELPER_FLAGS_0(darn32, TCG_CALL_NO_RWG, tl) -DEF_HELPER_FLAGS_0(darn64, TCG_CALL_NO_RWG, tl) +DEF_HELPER_FLAGS_0(DARN32, TCG_CALL_NO_RWG, tl) +DEF_HELPER_FLAGS_0(DARN64, TCG_CALL_NO_RWG, tl) #endif DEF_HELPER_FLAGS_1(cntlsw32, TCG_CALL_NO_RWG_SE, i32, i32) @@ -106,32 +113,32 @@ DEF_HELPER_2(friz, i64, env, i64) DEF_HELPER_2(frip, i64, env, i64) DEF_HELPER_2(frim, i64, env, i64) -DEF_HELPER_3(fadd, f64, env, f64, f64) -DEF_HELPER_3(fadds, f64, env, f64, f64) -DEF_HELPER_3(fsub, f64, env, f64, f64) -DEF_HELPER_3(fsubs, f64, env, f64, f64) -DEF_HELPER_3(fmul, f64, env, f64, f64) -DEF_HELPER_3(fmuls, f64, env, f64, f64) -DEF_HELPER_3(fdiv, f64, env, f64, f64) -DEF_HELPER_3(fdivs, f64, env, f64, f64) -DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) -DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) -DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) -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_3(FADD, f64, env, f64, f64) +DEF_HELPER_3(FADDS, f64, env, f64, f64) +DEF_HELPER_3(FSUB, f64, env, f64, f64) +DEF_HELPER_3(FSUBS, f64, env, f64, f64) +DEF_HELPER_3(FMUL, f64, env, f64, f64) +DEF_HELPER_3(FMULS, f64, env, f64, f64) +DEF_HELPER_3(FDIV, f64, env, f64, f64) +DEF_HELPER_3(FDIVS, f64, env, f64, f64) +DEF_HELPER_4(FMADD, i64, env, i64, i64, i64) +DEF_HELPER_4(FMSUB, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMADD, i64, env, i64, i64, i64) +DEF_HELPER_4(FNMSUB, i64, env, i64, i64, i64) +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(fre, i64, env, i64) -DEF_HELPER_2(fres, i64, env, i64) -DEF_HELPER_2(frsqrte, i64, env, i64) -DEF_HELPER_2(frsqrtes, i64, env, i64) +DEF_HELPER_2(FRE, i64, env, i64) +DEF_HELPER_2(FRES, i64, env, i64) +DEF_HELPER_2(FRSQRTE, i64, env, i64) +DEF_HELPER_2(FRSQRTES, i64, env, i64) DEF_HELPER_FLAGS_3(FSEL, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) -DEF_HELPER_FLAGS_2(ftdiv, TCG_CALL_NO_RWG_SE, i32, i64, i64) -DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) +DEF_HELPER_FLAGS_2(FTDIV, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_1(FTSQRT, TCG_CALL_NO_RWG_SE, i32, i64) #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * @@ -196,18 +203,18 @@ 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(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) -DEF_HELPER_FLAGS_5(vsubsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vsubsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vaddubs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -DEF_HELPER_FLAGS_5(vadduhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) -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_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) +DEF_HELPER_FLAGS_5(VSUBSBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VSUBSWS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUBS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(VADDUHS, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +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) @@ -263,17 +270,17 @@ DEF_HELPER_5(VMSUMSHS, void, env, 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) -DEF_HELPER_3(lvehx, void, env, avr, tl) -DEF_HELPER_3(lvewx, void, env, avr, tl) -DEF_HELPER_3(stvebx, void, env, avr, tl) -DEF_HELPER_3(stvehx, void, env, avr, tl) -DEF_HELPER_3(stvewx, void, env, avr, tl) +DEF_HELPER_3(LVEBX, void, env, avr, tl) +DEF_HELPER_3(LVEHX, void, env, avr, tl) +DEF_HELPER_3(LVEWX, void, env, avr, tl) +DEF_HELPER_3(STVEBX, void, env, avr, tl) +DEF_HELPER_3(STVEHX, void, env, avr, tl) +DEF_HELPER_3(STVEWX, void, env, avr, tl) #if defined(TARGET_PPC64) -DEF_HELPER_4(lxvl, void, env, tl, vsr, tl) -DEF_HELPER_4(lxvll, void, env, tl, vsr, tl) -DEF_HELPER_4(stxvl, void, env, tl, vsr, tl) -DEF_HELPER_4(stxvll, void, env, tl, vsr, tl) +DEF_HELPER_4(LXVL, void, env, tl, vsr, tl) +DEF_HELPER_4(LXVLL, void, env, tl, vsr, tl) +DEF_HELPER_4(STXVL, void, env, tl, vsr, tl) +DEF_HELPER_4(STXVLL, void, env, tl, vsr, tl) #endif DEF_HELPER_4(vsumsws, void, env, avr, avr, avr) DEF_HELPER_4(vsum2sws, void, env, avr, avr, avr) @@ -357,12 +364,12 @@ DEF_HELPER_FLAGS_4(bcdsr, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) DEF_HELPER_FLAGS_4(bcdtrunc, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) DEF_HELPER_FLAGS_4(bcdutrunc, TCG_CALL_NO_RWG, i32, avr, avr, avr, i32) -DEF_HELPER_4(xsadddp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSADDDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsaddqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xssubdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmuldp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSSUBDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMULDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsmulqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xsdivdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSDIVDP, void, env, vsr, vsr, vsr) DEF_HELPER_5(xsdivqp, void, env, i32, vsr, vsr, vsr) DEF_HELPER_3(xsredp, void, env, vsr, vsr) DEF_HELPER_3(xssqrtdp, void, env, vsr, vsr) @@ -385,8 +392,8 @@ DEF_HELPER_4(xscmpodp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpudp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpoqp, void, env, i32, vsr, vsr) DEF_HELPER_4(xscmpuqp, void, env, i32, vsr, vsr) -DEF_HELPER_4(xsmaxdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmindp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMAXDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMINDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMAXCDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMINCDP, void, env, vsr, vsr, vsr) DEF_HELPER_4(XSMAXJDP, void, env, vsr, vsr, vsr) @@ -432,10 +439,10 @@ DEF_HELPER_4(xsrqpxp, void, env, i32, vsr, vsr) DEF_HELPER_4(xssqrtqp, void, env, i32, vsr, vsr) DEF_HELPER_5(xssubqp, void, env, i32, vsr, vsr, vsr) -DEF_HELPER_4(xsaddsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xssubsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsmulsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xsdivsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSADDSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSSUBSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSMULSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XSDIVSP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xsresp, void, env, vsr, vsr) DEF_HELPER_2(xsrsp, i64, env, i64) DEF_HELPER_3(xssqrtsp, void, env, vsr, vsr) @@ -454,10 +461,10 @@ DEF_HELPER_5(XSNMADDQPO, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(XSNMSUBQP, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(XSNMSUBQPO, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvadddp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvsubdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmuldp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvdivdp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVADDDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVSUBDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMULDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVDIVDP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xvredp, void, env, vsr, vsr) DEF_HELPER_3(xvsqrtdp, void, env, vsr, vsr) DEF_HELPER_3(xvrsqrtedp, void, env, vsr, vsr) @@ -467,12 +474,12 @@ DEF_HELPER_5(xvmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvmsubdp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmadddp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubdp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvmaxdp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmindp, void, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpeqdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgtdp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpnedp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMAXDP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMINDP, void, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPEQDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGEDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGTDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPNEDP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvdpsp, void, env, vsr, vsr) DEF_HELPER_3(xvcvdpsxds, void, env, vsr, vsr) DEF_HELPER_3(xvcvdpsxws, void, env, vsr, vsr) @@ -488,10 +495,10 @@ DEF_HELPER_3(xvrdpim, void, env, vsr, vsr) DEF_HELPER_3(xvrdpip, void, env, vsr, vsr) DEF_HELPER_3(xvrdpiz, void, env, vsr, vsr) -DEF_HELPER_4(xvaddsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvsubsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvmulsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvdivsp, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVADDSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVSUBSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMULSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVDIVSP, void, env, vsr, vsr, vsr) DEF_HELPER_3(xvresp, void, env, vsr, vsr) DEF_HELPER_3(xvsqrtsp, void, env, vsr, vsr) DEF_HELPER_3(xvrsqrtesp, void, env, vsr, vsr) @@ -501,12 +508,12 @@ DEF_HELPER_5(xvmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvmsubsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmaddsp, void, env, vsr, vsr, vsr, vsr) DEF_HELPER_5(xvnmsubsp, void, env, vsr, vsr, vsr, vsr) -DEF_HELPER_4(xvmaxsp, void, env, vsr, vsr, vsr) -DEF_HELPER_4(xvminsp, void, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpeqsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpgtsp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) -DEF_HELPER_FLAGS_4(xvcmpnesp, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMAXSP, void, env, vsr, vsr, vsr) +DEF_HELPER_4(XVMINSP, void, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPEQSP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGESP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPGTSP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) +DEF_HELPER_FLAGS_4(XVCMPNESP, TCG_CALL_NO_RWG, i32, env, vsr, vsr, vsr) DEF_HELPER_3(xvcvspdp, void, env, vsr, vsr) DEF_HELPER_3(xvcvsphp, void, env, vsr, vsr) DEF_HELPER_3(xvcvhpsp, void, env, vsr, vsr) @@ -690,20 +697,21 @@ DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) DEF_HELPER_1(msgsnd, void, tl) DEF_HELPER_2(msgclr, void, env, tl) -DEF_HELPER_1(book3s_msgsnd, void, tl) +DEF_HELPER_2(book3s_msgsnd, void, env, tl) DEF_HELPER_2(book3s_msgclr, void, env, tl) #endif DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(rac, tl, env, tl) - 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) @@ -720,6 +728,11 @@ 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) +DEF_HELPER_FLAGS_2(store_sprc, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_FLAGS_1(load_sprd, TCG_CALL_NO_RWG_SE, tl, env) +DEF_HELPER_FLAGS_2(store_sprd, TCG_CALL_NO_RWG, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) @@ -811,13 +824,10 @@ 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) +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +DEF_HELPER_1(clrbhrb, void, env) +DEF_HELPER_FLAGS_2(mfbhrbe, TCG_CALL_NO_WG, i64, env, i32) +DEF_HELPER_1(attn, noreturn, env) +#endif #endif diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index e200091a23..42c681ca4a 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,27 +47,64 @@ void hreg_swap_gpr_tgpr(CPUPPCState *env) env->tgpr[3] = tmp; } +#if defined(TARGET_PPC64) +static bool hreg_check_bhrb_enable(CPUPPCState *env) +{ + bool pr = !!(env->msr & (1 << MSR_PR)); + target_long mmcr0; + bool fcp; + bool hv; + + /* ISA 3.1 adds the PMCRA[BRHBRD] and problem state checks */ + if ((env->insns_flags2 & PPC2_ISA310) && + ((env->spr[SPR_POWER_MMCRA] & MMCRA_BHRBRD) || !pr)) { + return false; + } + + /* Check for BHRB "frozen" conditions */ + mmcr0 = env->spr[SPR_POWER_MMCR0]; + fcp = !!(mmcr0 & MMCR0_FCP); + if (mmcr0 & MMCR0_FCPC) { + hv = !!(env->msr & (1ull << MSR_HV)); + if (fcp) { + if (hv && pr) { + return false; + } + } else if (!hv && pr) { + return false; + } + } else if (fcp && pr) { + return false; + } + return true; +} +#endif + 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) { + target_ulong mmcr0 = env->spr[SPR_POWER_MMCR0]; + + if (mmcr0 & MMCR0_PMCC0) { hflags |= 1 << HFLAGS_PMCC0; } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { + if (mmcr0 & MMCR0_PMCC1) { hflags |= 1 << HFLAGS_PMCC1; } - if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCjCE) { + if (mmcr0 & MMCR0_PMCjCE) { hflags |= 1 << HFLAGS_PMCJCE; } + if (hreg_check_bhrb_enable(env)) { + hflags |= 1 << HFLAGS_BHRB_ENABLE; + } #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; + if (env->pmc_ins_cnt & 0x1e) { + hflags |= 1 << HFLAGS_PMC_OTHER; + } } #endif #endif @@ -84,6 +122,7 @@ static uint32_t hreg_compute_pmu_hflags_mask(CPUPPCState *env) hflags_mask |= 1 << HFLAGS_PMCJCE; hflags_mask |= 1 << HFLAGS_INSN_CNT; hflags_mask |= 1 << HFLAGS_PMC_OTHER; + hflags_mask |= 1 << HFLAGS_BHRB_ENABLE; #endif return hflags_mask; } @@ -105,10 +144,10 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) if (ppc_flags & POWERPC_FLAG_DE) { target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0]; - if ((dbcr0 & DBCR0_ICMP) && FIELD_EX64(env->msr, MSR, DE)) { + if ((dbcr0 & DBCR0_ICMP) && FIELD_EX64(msr, MSR, DE)) { hflags |= 1 << HFLAGS_SE; } - if ((dbcr0 & DBCR0_BRT) && FIELD_EX64(env->msr, MSR, DE)) { + if ((dbcr0 & DBCR0_BRT) && FIELD_EX64(msr, MSR, DE)) { hflags |= 1 << HFLAGS_BE; } } else { @@ -217,8 +256,8 @@ void hreg_update_pmu_hflags(CPUPPCState *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; @@ -240,17 +279,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); } } @@ -270,6 +302,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); } @@ -316,7 +353,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 */ @@ -326,9 +363,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); @@ -337,7 +372,7 @@ void check_tlb_flush(CPUPPCState *env, bool global) if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; - tlb_flush_all_cpus(cs); + tlb_flush_all_cpus_synced(cs); return; } @@ -347,7 +382,7 @@ void check_tlb_flush(CPUPPCState *env, bool global) tlb_flush(cs); } } -#endif +#endif /* !CONFIG_USER_ONLY */ /** * _spr_register @@ -468,22 +503,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) @@ -491,14 +545,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); @@ -677,7 +731,6 @@ void register_6xx_7xx_soft_tlb(CPUPPCState *env, int nb_tlbs, int nb_ways) #if !defined(CONFIG_USER_ONLY) env->nb_tlb = nb_tlbs; env->nb_ways = nb_ways; - env->id_tlbs = 1; env->tlb_type = TLB_6XX; spr_register(env, SPR_DMISS, "DMISS", SPR_NOACCESS, SPR_NOACCESS, diff --git a/target/ppc/insn32.decode b/target/ppc/insn32.decode index f8f589e9fd..e53fd2840d 100644 --- a/target/ppc/insn32.decode +++ b/target/ppc/insn32.decode @@ -20,12 +20,24 @@ &A frt fra frb frc rc:bool @A ...... frt:5 fra:5 frb:5 frc:5 ..... rc:1 &A +&A_tab frt fra frb rc:bool +@A_tab ...... frt:5 fra:5 frb:5 ..... ..... rc:1 &A_tab + +&A_tac frt fra frc rc:bool +@A_tac ...... frt:5 fra:5 ..... frc:5 ..... rc:1 &A_tac + &A_tb frt frb rc:bool @A_tb ...... frt:5 ..... frb:5 ..... ..... rc:1 &A_tb +&A_tab_bc rt ra rb bc +@A_tab_bc ...... rt:5 ra:5 rb:5 bc:5 ..... . &A_tab_bc + &D rt ra si:int64_t @D ...... rt:5 ra:5 si:s16 &D +&D_ui rt ra ui:uint64_t +@D_ui ...... rt:5 ra:5 ui:16 &D_ui + &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 @@ -93,6 +105,9 @@ &X_sa rs ra @X_sa ...... rs:5 ra:5 ..... .......... . &X_sa +&X_sa_rc rs ra rc +@X_sa_rc ...... rs:5 ra:5 ..... .......... rc:1 &X_sa_rc + %x_frtp 22:4 !function=times_2 %x_frap 17:4 !function=times_2 %x_frbp 12:4 !function=times_2 @@ -124,6 +139,9 @@ &X_bf bf ra rb @X_bf ...... bf:3 .. ra:5 rb:5 .......... . &X_bf +&X_bf_b bf rb +@X_bf_b ...... bf:3 .. ..... rb:5 .......... . &X_bf_b + @X_bf_ap_bp ...... bf:3 .. ....0 ....0 .......... . &X_bf ra=%x_frap rb=%x_frbp @X_bf_a_bp ...... bf:3 .. ra:5 ....0 .......... . &X_bf rb=%x_frbp @@ -187,6 +205,18 @@ &X_a ra @X_a ...... ra:3 .. ..... ..... .......... . &X_a +&X_tl rt l +@X_tl ...... rt:5 ... l:2 ..... .......... . &X_tl + +&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 + +&XO_tab_rc rt ra rb rc:bool +@XO_tab_rc ...... rt:5 ra:5 rb:5 . ......... rc:1 &XO_tab_rc + %xx_xt 0:1 21:5 %xx_xb 1:1 11:5 %xx_xa 2:1 16:5 @@ -211,6 +241,9 @@ &XX3 xt xa xb @XX3 ...... ..... ..... ..... ........ ... &XX3 xt=%xx_xt xa=%xx_xa xb=%xx_xb +&XX3_rc xt xa xb rc:bool +@XX3_rc ...... ..... ..... ..... rc:1 ....... ... &XX3_rc xt=%xx_xt xa=%xx_xa xb=%xx_xb + # 32 bit GER instructions have all mask bits considered 1 &MMIRR_XX3 xa xb xt pmsk xmsk ymsk %xx_at 23:3 @@ -319,16 +352,114 @@ CMP 011111 ... - . ..... ..... 0000000000 - @X_bfl CMPL 011111 ... - . ..... ..... 0000100000 - @X_bfl CMPI 001011 ... - . ..... ................ @D_bfs CMPLI 001010 ... - . ..... ................ @D_bfu +CMPRB 011111 ... - . ..... ..... 0011000000 - @X_bfl +CMPEQB 011111 ... -- ..... ..... 0011100000 - @X_bf + +### Fixed-Point Trap Instructions + +TW 011111 ..... ..... ..... 0000000100 - @X +TD 011111 ..... ..... ..... 0001000100 - @X +TWI 000011 ..... ..... ................ @D +TDI 000010 ..... ..... ................ @D + +### Fixed-Point Select Instruction + +ISEL 011111 ..... ..... ..... ..... 01111 - @A_tab_bc ### 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 + +MULLI 000111 ..... ..... ................ @D +MULLW 011111 ..... ..... ..... 0 011101011 . @XO_tab_rc +MULLWO 011111 ..... ..... ..... 1 011101011 . @XO_tab_rc +MULHW 011111 ..... ..... ..... - 001001011 . @XO_tab_rc +MULHWU 011111 ..... ..... ..... - 000001011 . @XO_tab_rc + +DIVW 011111 ..... ..... ..... . 111101011 . @XO +DIVWU 011111 ..... ..... ..... . 111001011 . @XO +DIVWE 011111 ..... ..... ..... . 110101011 . @XO +DIVWEU 011111 ..... ..... ..... . 110001011 . @XO + +MODSW 011111 ..... ..... ..... 1100001011 - @X +MODUW 011111 ..... ..... ..... 0100001011 - @X +DARN 011111 ..... --- .. ----- 1011110011 - @X_tl +NEG 011111 ..... ..... ----- . 001101000 . @XO_ta + +MULLD 011111 ..... ..... ..... 0 011101001 . @XO_tab_rc +MULLDO 011111 ..... ..... ..... 1 011101001 . @XO_tab_rc +MULHD 011111 ..... ..... ..... - 001001001 . @XO_tab_rc +MULHDU 011111 ..... ..... ..... - 000001001 . @XO_tab_rc + +MADDLD 000100 ..... ..... ..... ..... 110011 @VA +MADDHD 000100 ..... ..... ..... ..... 110000 @VA +MADDHDU 000100 ..... ..... ..... ..... 110001 @VA + +DIVD 011111 ..... ..... ..... . 111101001 . @XO +DIVDU 011111 ..... ..... ..... . 111001001 . @XO +DIVDE 011111 ..... ..... ..... . 110101001 . @XO +DIVDEU 011111 ..... ..... ..... . 110001001 . @XO + +MODSD 011111 ..... ..... ..... 1100001001 - @X +MODUD 011111 ..... ..... ..... 0100001001 - @X ## Fixed-Point Logical Instructions +ANDI_ 011100 ..... ..... ................ @D_ui +ANDIS_ 011101 ..... ..... ................ @D_ui +ORI 011000 ..... ..... ................ @D_ui +ORIS 011001 ..... ..... ................ @D_ui +XORI 011010 ..... ..... ................ @D_ui +XORIS 011011 ..... ..... ................ @D_ui + +AND 011111 ..... ..... ..... 0000011100 . @X_rc +ANDC 011111 ..... ..... ..... 0000111100 . @X_rc +NAND 011111 ..... ..... ..... 0111011100 . @X_rc +OR 011111 ..... ..... ..... 0110111100 . @X_rc +ORC 011111 ..... ..... ..... 0110011100 . @X_rc +NOR 011111 ..... ..... ..... 0001111100 . @X_rc +XOR 011111 ..... ..... ..... 0100111100 . @X_rc +EQV 011111 ..... ..... ..... 0100011100 . @X_rc +CMPB 011111 ..... ..... ..... 0111111100 . @X_rc + +EXTSB 011111 ..... ..... ----- 1110111010 . @X_sa_rc +EXTSH 011111 ..... ..... ----- 1110011010 . @X_sa_rc +EXTSW 011111 ..... ..... ----- 1111011010 . @X_sa_rc +CNTLZW 011111 ..... ..... ----- 0000011010 . @X_sa_rc +CNTTZW 011111 ..... ..... ----- 1000011010 . @X_sa_rc +CNTLZD 011111 ..... ..... ----- 0000111010 . @X_sa_rc +CNTTZD 011111 ..... ..... ----- 1000111010 . @X_sa_rc +POPCNTB 011111 ..... ..... ----- 0001111010 . @X_sa_rc + +POPCNTW 011111 ..... ..... ----- 0101111010 - @X_sa +POPCNTD 011111 ..... ..... ----- 0111111010 - @X_sa +PRTYW 011111 ..... ..... ----- 0010011010 - @X_sa +PRTYD 011111 ..... ..... ----- 0010111010 - @X_sa + +BPERMD 011111 ..... ..... ..... 0011111100 - @X CFUGED 011111 ..... ..... ..... 0011011100 - @X CNTLZDM 011111 ..... ..... ..... 0000111011 - @X CNTTZDM 011111 ..... ..... ..... 1000111011 - @X @@ -374,9 +505,42 @@ STFDUX 011111 ..... ...... .... 1011110111 - @X ### Floating-Point Arithmetic Instructions +FADD 111111 ..... ..... ..... ----- 10101 . @A_tab +FADDS 111011 ..... ..... ..... ----- 10101 . @A_tab + +FSUB 111111 ..... ..... ..... ----- 10100 . @A_tab +FSUBS 111011 ..... ..... ..... ----- 10100 . @A_tab + +FMUL 111111 ..... ..... ----- ..... 11001 . @A_tac +FMULS 111011 ..... ..... ----- ..... 11001 . @A_tac + +FDIV 111111 ..... ..... ..... ----- 10010 . @A_tab +FDIVS 111011 ..... ..... ..... ----- 10010 . @A_tab + FSQRT 111111 ..... ----- ..... ----- 10110 . @A_tb FSQRTS 111011 ..... ----- ..... ----- 10110 . @A_tb +FRE 111111 ..... ----- ..... ----- 11000 . @A_tb +FRES 111011 ..... ----- ..... ----- 11000 . @A_tb + +FRSQRTE 111111 ..... ----- ..... ----- 11010 . @A_tb +FRSQRTES 111011 ..... ----- ..... ----- 11010 . @A_tb + +FTDIV 111111 ... -- ..... ..... 0010000000 - @X_bf +FTSQRT 111111 ... -- ----- ..... 0010100000 - @X_bf_b + +FMADD 111111 ..... ..... ..... ..... 11101 . @A +FMADDS 111011 ..... ..... ..... ..... 11101 . @A + +FMSUB 111111 ..... ..... ..... ..... 11100 . @A +FMSUBS 111011 ..... ..... ..... ..... 11100 . @A + +FNMADD 111111 ..... ..... ..... ..... 11111 . @A +FNMADDS 111011 ..... ..... ..... ..... 11111 . @A + +FNMSUB 111111 ..... ..... ..... ..... 11110 . @A +FNMSUBS 111011 ..... ..... ..... ..... 11110 . @A + ### Floating-Point Select Instruction FSEL 111111 ..... ..... ..... ..... 10111 . @A @@ -390,13 +554,19 @@ SETNBCR 011111 ..... ..... ----- 0111100000 - @X_bi ### Move To/From FPSCR -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 +{ + # 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 @@ -494,6 +664,23 @@ DSCRIQ 111111 ..... ..... ...... 001100010 . @Z22_tap_sh_rc VPMSUMD 000100 ..... ..... ..... 10011001000 @VX +## Vector Load/Store Instructions + +LVEBX 011111 ..... ..... ..... 0000000111 - @X +LVEHX 011111 ..... ..... ..... 0000100111 - @X +LVEWX 011111 ..... ..... ..... 0001000111 - @X +LVX 011111 ..... ..... ..... 0001100111 - @X +LVXL 011111 ..... ..... ..... 0101100111 - @X + +STVEBX 011111 ..... ..... ..... 0010000111 - @X +STVEHX 011111 ..... ..... ..... 0010100111 - @X +STVEWX 011111 ..... ..... ..... 0011000111 - @X +STVX 011111 ..... ..... ..... 0011100111 - @X +STVXL 011111 ..... ..... ..... 0111100111 - @X + +LVSL 011111 ..... ..... ..... 0000000110 - @X +LVSR 011111 ..... ..... ..... 0000100110 - @X + ## Vector Integer Instructions VCMPEQUB 000100 ..... ..... ..... . 0000000110 @VC @@ -525,6 +712,17 @@ VCMPNEZW 000100 ..... ..... ..... . 0110000111 @VC VCMPSQ 000100 ... -- ..... ..... 00101000001 @VX_bf VCMPUQ 000100 ... -- ..... ..... 00100000001 @VX_bf +## Vector Integer Logical Instructions + +VAND 000100 ..... ..... ..... 10000000100 @VX +VANDC 000100 ..... ..... ..... 10001000100 @VX +VNAND 000100 ..... ..... ..... 10110000100 @VX +VOR 000100 ..... ..... ..... 10010000100 @VX +VORC 000100 ..... ..... ..... 10101000100 @VX +VNOR 000100 ..... ..... ..... 10100000100 @VX +VXOR 000100 ..... ..... ..... 10011000100 @VX +VEQV 000100 ..... ..... ..... 11010000100 @VX + ## Vector Integer Average Instructions VAVGSB 000100 ..... ..... ..... 10100000010 @VX @@ -637,6 +835,14 @@ VADDCUW 000100 ..... ..... ..... 00110000000 @VX VADDCUQ 000100 ..... ..... ..... 00101000000 @VX VADDUQM 000100 ..... ..... ..... 00100000000 @VX +VADDSBS 000100 ..... ..... ..... 01100000000 @VX +VADDSHS 000100 ..... ..... ..... 01101000000 @VX +VADDSWS 000100 ..... ..... ..... 01110000000 @VX + +VADDUBS 000100 ..... ..... ..... 01000000000 @VX +VADDUHS 000100 ..... ..... ..... 01001000000 @VX +VADDUWS 000100 ..... ..... ..... 01010000000 @VX + VADDEUQM 000100 ..... ..... ..... ..... 111100 @VA VADDECUQ 000100 ..... ..... ..... ..... 111101 @VA @@ -644,6 +850,14 @@ VSUBCUW 000100 ..... ..... ..... 10110000000 @VX VSUBCUQ 000100 ..... ..... ..... 10101000000 @VX VSUBUQM 000100 ..... ..... ..... 10100000000 @VX +VSUBSBS 000100 ..... ..... ..... 11100000000 @VX +VSUBSHS 000100 ..... ..... ..... 11101000000 @VX +VSUBSWS 000100 ..... ..... ..... 11110000000 @VX + +VSUBUBS 000100 ..... ..... ..... 11000000000 @VX +VSUBUHS 000100 ..... ..... ..... 11001000000 @VX +VSUBUWS 000100 ..... ..... ..... 11010000000 @VX + VSUBECUQ 000100 ..... ..... ..... ..... 111111 @VA VSUBEUQM 000100 ..... ..... ..... ..... 111110 @VA @@ -657,6 +871,28 @@ VEXTSD2Q 000100 ..... 11011 ..... 11000000010 @VX_tb VNEGD 000100 ..... 00111 ..... 11000000010 @VX_tb VNEGW 000100 ..... 00110 ..... 11000000010 @VX_tb +## Vector Integer Maximum/Minimum Instructions + +VMAXUB 000100 ..... ..... ..... 00000000010 @VX +VMAXUH 000100 ..... ..... ..... 00001000010 @VX +VMAXUW 000100 ..... ..... ..... 00010000010 @VX +VMAXUD 000100 ..... ..... ..... 00011000010 @VX + +VMAXSB 000100 ..... ..... ..... 00100000010 @VX +VMAXSH 000100 ..... ..... ..... 00101000010 @VX +VMAXSW 000100 ..... ..... ..... 00110000010 @VX +VMAXSD 000100 ..... ..... ..... 00111000010 @VX + +VMINUB 000100 ..... ..... ..... 01000000010 @VX +VMINUH 000100 ..... ..... ..... 01001000010 @VX +VMINUW 000100 ..... ..... ..... 01010000010 @VX +VMINUD 000100 ..... ..... ..... 01011000010 @VX + +VMINSB 000100 ..... ..... ..... 01100000010 @VX +VMINSH 000100 ..... ..... ..... 01101000010 @VX +VMINSW 000100 ..... ..... ..... 01110000010 @VX +VMINSD 000100 ..... ..... ..... 01111000010 @VX + ## Vector Mask Manipulation Instructions MTVSRBM 000100 ..... 10000 ..... 11001000010 @VX_tb @@ -760,6 +996,35 @@ STXVRHX 011111 ..... ..... ..... 0010101101 . @X_TSX STXVRWX 011111 ..... ..... ..... 0011001101 . @X_TSX STXVRDX 011111 ..... ..... ..... 0011101101 . @X_TSX +LXSDX 011111 ..... ..... ..... 1001001100 . @X_TSX +LXSIWAX 011111 ..... ..... ..... 0001001100 . @X_TSX +LXSIBZX 011111 ..... ..... ..... 1100001101 . @X_TSX +LXSIHZX 011111 ..... ..... ..... 1100101101 . @X_TSX +LXSIWZX 011111 ..... ..... ..... 0000001100 . @X_TSX +LXSSPX 011111 ..... ..... ..... 1000001100 . @X_TSX + +STXSDX 011111 ..... ..... ..... 1011001100 . @X_TSX +STXSIBX 011111 ..... ..... ..... 1110001101 . @X_TSX +STXSIHX 011111 ..... ..... ..... 1110101101 . @X_TSX +STXSIWX 011111 ..... ..... ..... 0010001100 . @X_TSX +STXSSPX 011111 ..... ..... ..... 1010001100 . @X_TSX + +LXVB16X 011111 ..... ..... ..... 1101101100 . @X_TSX +LXVD2X 011111 ..... ..... ..... 1101001100 . @X_TSX +LXVH8X 011111 ..... ..... ..... 1100101100 . @X_TSX +LXVW4X 011111 ..... ..... ..... 1100001100 . @X_TSX +LXVDSX 011111 ..... ..... ..... 0101001100 . @X_TSX +LXVWSX 011111 ..... ..... ..... 0101101100 . @X_TSX +LXVL 011111 ..... ..... ..... 0100001101 . @X_TSX +LXVLL 011111 ..... ..... ..... 0100101101 . @X_TSX + +STXVB16X 011111 ..... ..... ..... 1111101100 . @X_TSX +STXVD2X 011111 ..... ..... ..... 1111001100 . @X_TSX +STXVH8X 011111 ..... ..... ..... 1110101100 . @X_TSX +STXVW4X 011111 ..... ..... ..... 1110001100 . @X_TSX +STXVL 011111 ..... ..... ..... 0110001101 . @X_TSX +STXVLL 011111 ..... ..... ..... 0110101101 . @X_TSX + ## VSX Vector Binary Floating-Point Sign Manipulation Instructions XVABSDP 111100 ..... 00000 ..... 111011001 .. @XX2 @@ -771,6 +1036,28 @@ XVNEGSP 111100 ..... 00000 ..... 110111001 .. @XX2 XVCPSGNDP 111100 ..... ..... ..... 11110000 ... @XX3 XVCPSGNSP 111100 ..... ..... ..... 11010000 ... @XX3 +## VSX Binary Floating-Point Arithmetic Instructions + +XSADDSP 111100 ..... ..... ..... 00000000 ... @XX3 +XSSUBSP 111100 ..... ..... ..... 00001000 ... @XX3 +XSMULSP 111100 ..... ..... ..... 00010000 ... @XX3 +XSDIVSP 111100 ..... ..... ..... 00011000 ... @XX3 + +XSADDDP 111100 ..... ..... ..... 00100000 ... @XX3 +XSSUBDP 111100 ..... ..... ..... 00101000 ... @XX3 +XSMULDP 111100 ..... ..... ..... 00110000 ... @XX3 +XSDIVDP 111100 ..... ..... ..... 00111000 ... @XX3 + +XVADDSP 111100 ..... ..... ..... 01000000 ... @XX3 +XVSUBSP 111100 ..... ..... ..... 01001000 ... @XX3 +XVMULSP 111100 ..... ..... ..... 01010000 ... @XX3 +XVDIVSP 111100 ..... ..... ..... 01011000 ... @XX3 + +XVADDDP 111100 ..... ..... ..... 01100000 ... @XX3 +XVSUBDP 111100 ..... ..... ..... 01101000 ... @XX3 +XVMULDP 111100 ..... ..... ..... 01110000 ... @XX3 +XVDIVDP 111100 ..... ..... ..... 01111000 ... @XX3 + ## VSX Scalar Multiply-Add Instructions XSMADDADP 111100 ..... ..... ..... 00100001 . . . @XX3 @@ -840,6 +1127,23 @@ XSCMPEQQP 111111 ..... ..... ..... 0001000100 - @X XSCMPGEQP 111111 ..... ..... ..... 0011000100 - @X XSCMPGTQP 111111 ..... ..... ..... 0011100100 - @X +XVCMPEQSP 111100 ..... ..... ..... . 1000011 ... @XX3_rc +XVCMPGTSP 111100 ..... ..... ..... . 1001011 ... @XX3_rc +XVCMPGESP 111100 ..... ..... ..... . 1010011 ... @XX3_rc +XVCMPNESP 111100 ..... ..... ..... . 1011011 ... @XX3_rc +XVCMPEQDP 111100 ..... ..... ..... . 1100011 ... @XX3_rc +XVCMPGTDP 111100 ..... ..... ..... . 1101011 ... @XX3_rc +XVCMPGEDP 111100 ..... ..... ..... . 1110011 ... @XX3_rc +XVCMPNEDP 111100 ..... ..... ..... . 1111011 ... @XX3_rc + +XSMAXDP 111100 ..... ..... ..... 10100000 ... @XX3 +XSMINDP 111100 ..... ..... ..... 10101000 ... @XX3 + +XVMAXSP 111100 ..... ..... ..... 11000000 ... @XX3 +XVMINSP 111100 ..... ..... ..... 11001000 ... @XX3 +XVMAXDP 111100 ..... ..... ..... 11100000 ... @XX3 +XVMINDP 111100 ..... ..... ..... 11101000 ... @XX3 + ## VSX Binary Floating-Point Convert Instructions XSCVQPDP 111111 ..... 10100 ..... 1101000100 . @X_tb_rc @@ -875,6 +1179,17 @@ XXMFACC 011111 ... -- 00000 ----- 0010110001 - @X_a XXMTACC 011111 ... -- 00001 ----- 0010110001 - @X_a XXSETACCZ 011111 ... -- 00011 ----- 0010110001 - @X_a +## VSX Vector Logical instructions + +XXLAND 111100 ..... ..... ..... 10000010 ... @XX3 +XXLANDC 111100 ..... ..... ..... 10001010 ... @XX3 +XXLOR 111100 ..... ..... ..... 10010010 ... @XX3 +XXLXOR 111100 ..... ..... ..... 10011010 ... @XX3 +XXLNOR 111100 ..... ..... ..... 10100010 ... @XX3 +XXLEQV 111100 ..... ..... ..... 10111010 ... @XX3 +XXLNAND 111100 ..... ..... ..... 10110010 ... @XX3 +XXLORC 111100 ..... ..... ..... 10101010 ... @XX3 + ## VSX GER instruction XVI4GER8 111011 ... -- ..... ..... 00100011 ..- @XX3_at xa=%xx_xa @@ -966,3 +1281,22 @@ MSGSND 011111 ----- ----- ..... 0011001110 - @X_rb MSGCLRP 011111 ----- ----- ..... 0010101110 - @X_rb MSGSNDP 011111 ----- ----- ..... 0010001110 - @X_rb MSGSYNC 011111 ----- ----- ----- 1101110110 - + +# Memory Barrier Instructions + +&X_sync l sc +@X_sync ...... .. l:3 ... sc:2 ..... .......... . &X_sync +SYNC 011111 -- ... --- .. ----- 1001010110 - @X_sync +EIEIO 011111 ----- ----- ----- 1101010110 - + +# Branch History Rolling Buffer (BHRB) Instructions + +&XFX_bhrbe rt bhrbe +@XFX_bhrbe ...... rt:5 bhrbe:10 .......... - &XFX_bhrbe + +MFBHRBE 011111 ..... ..... ..... 0100101110 - @XFX_bhrbe +CLRBHRB 011111 ----- ----- ----- 0110101110 - + +## Misc POWER instructions + +ATTN 000000 00000 00000 00000 0100000000 0 diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d97a7f1f28..ef4b2e75d6 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" @@ -43,7 +44,7 @@ static inline void helper_update_ov_legacy(CPUPPCState *env, int ov) } } -target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, +target_ulong helper_DIVWEU(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) { uint64_t rt = 0; @@ -70,7 +71,7 @@ target_ulong helper_divweu(CPUPPCState *env, target_ulong ra, target_ulong rb, return (target_ulong)rt; } -target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, +target_ulong helper_DIVWE(CPUPPCState *env, target_ulong ra, target_ulong rb, uint32_t oe) { int64_t rt = 0; @@ -100,7 +101,7 @@ target_ulong helper_divwe(CPUPPCState *env, target_ulong ra, target_ulong rb, #if defined(TARGET_PPC64) -uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) +uint64_t helper_DIVDEU(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) { uint64_t rt = 0; int overflow = 0; @@ -119,7 +120,7 @@ uint64_t helper_divdeu(CPUPPCState *env, uint64_t ra, uint64_t rb, uint32_t oe) return rt; } -uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) +uint64_t helper_DIVDE(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) { uint64_t rt = 0; int64_t ra = (int64_t)rau; @@ -158,7 +159,7 @@ uint64_t helper_divde(CPUPPCState *env, uint64_t rau, uint64_t rbu, uint32_t oe) /* When you XOR the pattern and there is a match, that byte will be zero */ #define hasvalue(x, n) (haszero((x) ^ pattern(n))) -uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) +uint32_t helper_CMPEQB(target_ulong ra, target_ulong rb) { return hasvalue(rb, ra) ? CRF_GT : 0; } @@ -170,7 +171,7 @@ uint32_t helper_cmpeqb(target_ulong ra, target_ulong rb) /* * Return a random number. */ -uint64_t helper_darn32(void) +uint64_t helper_DARN32(void) { Error *err = NULL; uint32_t ret; @@ -185,7 +186,7 @@ uint64_t helper_darn32(void) return ret; } -uint64_t helper_darn64(void) +uint64_t helper_DARN64(void) { Error *err = NULL; uint64_t ret; @@ -200,7 +201,7 @@ uint64_t helper_darn64(void) return ret; } -uint64_t helper_bpermd(uint64_t rs, uint64_t rb) +uint64_t helper_BPERMD(uint64_t rs, uint64_t rb) { int i; uint64_t ra = 0; @@ -218,7 +219,7 @@ uint64_t helper_bpermd(uint64_t rs, uint64_t rb) #endif -target_ulong helper_cmpb(target_ulong rs, target_ulong rb) +target_ulong helper_CMPB(target_ulong rs, target_ulong rb) { target_ulong mask = 0xff; target_ulong ra = 0; @@ -287,7 +288,7 @@ target_ulong helper_srad(CPUPPCState *env, target_ulong value, #endif #if defined(TARGET_PPC64) -target_ulong helper_popcntb(target_ulong val) +target_ulong helper_POPCNTB(target_ulong val) { /* Note that we don't fold past bytes */ val = (val & 0x5555555555555555ULL) + ((val >> 1) & @@ -299,7 +300,7 @@ target_ulong helper_popcntb(target_ulong val) return val; } -target_ulong helper_popcntw(target_ulong val) +target_ulong helper_POPCNTW(target_ulong val) { /* Note that we don't fold past words. */ val = (val & 0x5555555555555555ULL) + ((val >> 1) & @@ -315,7 +316,7 @@ target_ulong helper_popcntw(target_ulong val) return val; } #else -target_ulong helper_popcntb(target_ulong val) +target_ulong helper_POPCNTB(target_ulong val) { /* Note that we don't fold past bytes */ val = (val & 0x55555555) + ((val >> 1) & 0x55555555); @@ -540,7 +541,7 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } #define VARITHSAT_DO(name, op, optype, cvt, element) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ + void helper_V##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ ppc_avr_t *a, ppc_avr_t *b, uint32_t desc) \ { \ int sat = 0; \ @@ -554,17 +555,17 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } \ } #define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(adds##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subs##suffix##s, -, optype, cvt, element) + VARITHSAT_DO(ADDS##suffix##S, +, optype, cvt, element) \ + VARITHSAT_DO(SUBS##suffix##S, -, optype, cvt, element) #define VARITHSAT_UNSIGNED(suffix, element, optype, cvt) \ - VARITHSAT_DO(addu##suffix##s, +, optype, cvt, element) \ - VARITHSAT_DO(subu##suffix##s, -, optype, cvt, element) -VARITHSAT_SIGNED(b, s8, int16_t, cvtshsb) -VARITHSAT_SIGNED(h, s16, int32_t, cvtswsh) -VARITHSAT_SIGNED(w, s32, int64_t, cvtsdsw) -VARITHSAT_UNSIGNED(b, u8, uint16_t, cvtshub) -VARITHSAT_UNSIGNED(h, u16, uint32_t, cvtswuh) -VARITHSAT_UNSIGNED(w, u32, uint64_t, cvtsduw) + VARITHSAT_DO(ADDU##suffix##S, +, optype, cvt, element) \ + VARITHSAT_DO(SUBU##suffix##S, -, optype, cvt, element) +VARITHSAT_SIGNED(B, s8, int16_t, cvtshsb) +VARITHSAT_SIGNED(H, s16, int32_t, cvtswsh) +VARITHSAT_SIGNED(W, s32, int64_t, cvtsdsw) +VARITHSAT_UNSIGNED(B, u8, uint16_t, cvtshub) +VARITHSAT_UNSIGNED(H, u16, uint32_t, cvtswuh) +VARITHSAT_UNSIGNED(W, u32, uint64_t, cvtsduw) #undef VARITHSAT_CASE #undef VARITHSAT_DO #undef VARITHSAT_SIGNED @@ -1424,46 +1425,39 @@ 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]; \ - } \ +/* + * 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) +{ + 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); + } } -PMSUM(vpmsumb, u8, u16, uint16_t) -PMSUM(vpmsumh, u16, u32, uint32_t) -PMSUM(vpmsumw, u32, u64, uint64_t) +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) { - int i, j; - Int128 tmp, prod[2] = {int128_zero(), int128_zero()}; - - for (j = 0; j < 64; j++) { - for (i = 0; i < ARRAY_SIZE(r->u64); i++) { - if (a->VsrD(i) & (1ull << j)) { - tmp = int128_make64(b->VsrD(i)); - tmp = int128_lshift(tmp, j); - prod[i] = int128_xor(prod[i], tmp); - } - } - } - - r->s128 = int128_xor(prod[0], prod[1]); + 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 @@ -2026,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; \ } @@ -2932,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 337a362205..20fb2ec593 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -18,7 +18,18 @@ #ifndef PPC_INTERNAL_H #define PPC_INTERNAL_H +#include "exec/breakpoint.h" #include "hw/registerfields.h" +#include "exec/page-protection.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, \ @@ -221,50 +232,27 @@ 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: - * @access_type: Access type - * - * Return the protection bit required for the given access type. - */ -static inline int prot_for_access_type(MMUAccessType access_type) +#ifndef CONFIG_USER_ONLY + +/* Check if permission bit required for the access_type is set in prot */ +static inline int check_prot_access_type(int prot, MMUAccessType access_type) { - switch (access_type) { - case MMU_INST_FETCH: - return PAGE_EXEC; - case MMU_DATA_LOAD: - return PAGE_READ; - case MMU_DATA_STORE: - return PAGE_WRITE; - } - g_assert_not_reached(); + return prot & (1 << access_type); } /* 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); -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx); + /* Software driven TLB helpers */ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code); -/* Context used internally during MMU translations */ -struct mmu_ctx_t { - hwaddr raddr; /* Real address */ - hwaddr eaddr; /* Effective address */ - int prot; /* Protection bits */ - hwaddr hash[2]; /* Pagetable hash values */ - target_ulong ptem; /* Virtual segment ID | API */ - int key; /* Access key */ - 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 +279,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 7c25348b7b..3efc28f18b 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" @@ -40,7 +39,7 @@ #include "migration/qemu-file-types.h" #include "sysemu/watchdog.h" #include "trace.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "exec/memattrs.h" #include "exec/ram_addr.h" #include "sysemu/hostmem.h" @@ -49,6 +48,10 @@ #include "qemu/mmap-alloc.h" #include "elf.h" #include "sysemu/kvm_int.h" +#include "sysemu/kvm.h" +#include "hw/core/accel-cpu.h" + +#include CONFIG_DEVICES #define PROC_DEVTREE_CPU "/proc/device-tree/cpus/" @@ -72,7 +75,6 @@ static int cap_hior; static int cap_one_reg; static int cap_epr; static int cap_ppc_watchdog; -static int cap_papr; static int cap_htab_fd; static int cap_fixup_hcalls; static int cap_htm; /* Hardware transactional memory support */ @@ -89,6 +91,13 @@ 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; + +#ifdef CONFIG_PSERIES +static int cap_papr; +#else +#define cap_papr (0) +#endif static uint32_t debug_inst_opcode; @@ -108,6 +117,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 +167,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 +277,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 +555,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 +589,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 +622,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 +641,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 +688,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 +709,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); @@ -856,9 +867,7 @@ int kvmppc_put_books_sregs(PowerPCCPU *cpu) sregs.pvr = env->spr[SPR_PVR]; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - sregs.u.s.sdr1 = vhc->encode_hpt_for_kvm_pr(cpu->vhyp); + sregs.u.s.sdr1 = cpu->vhyp_class->encode_hpt_for_kvm_pr(cpu->vhyp); } else { sregs.u.s.sdr1 = env->spr[SPR_SDR1]; } @@ -891,7 +900,7 @@ int kvmppc_put_books_sregs(PowerPCCPU *cpu) return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs); } -int kvm_arch_put_registers(CPUState *cs, int level) +int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; @@ -928,10 +937,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 +963,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 @@ -1201,12 +1205,11 @@ static int kvmppc_get_books_sregs(PowerPCCPU *cpu) return 0; } -int kvm_arch_get_registers(CPUState *cs) +int kvm_arch_get_registers(CPUState *cs, Error **errp) { 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 +1217,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 +1263,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 +1319,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 +1448,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 +1492,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 +1659,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: @@ -1680,7 +1675,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) trace_kvm_handle_halt(); ret = kvmppc_handle_halt(cpu); break; -#if defined(TARGET_PPC64) +#if defined(CONFIG_PSERIES) case KVM_EXIT_PAPR_HCALL: trace_kvm_handle_papr_hcall(run->papr_hcall.nr); run->papr_hcall.ret = spapr_hypercall(cpu, @@ -1710,7 +1705,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) ret = 0; break; -#if defined(TARGET_PPC64) +#if defined(CONFIG_PSERIES) case KVM_EXIT_NMI: trace_kvm_handle_nmi_exception(); ret = kvm_handle_nmi(cpu, run); @@ -1723,7 +1718,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; } - qemu_mutex_unlock_iothread(); + bql_unlock(); return ret; } @@ -1736,6 +1731,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 +1748,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 +1766,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, ®); } @@ -2054,6 +2061,7 @@ void kvmppc_enable_h_rpt_invalidate(void) kvmppc_enable_hcall(kvm_state, H_RPT_INVALIDATE); } +#ifdef CONFIG_PSERIES void kvmppc_set_papr(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -2075,6 +2083,7 @@ void kvmppc_set_papr(PowerPCCPU *cpu) */ cap_papr = 1; } +#endif int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr) { @@ -2339,6 +2348,30 @@ static void alter_insns(uint64_t *word, uint64_t flags, bool on) } } +static bool kvmppc_cpu_realize(CPUState *cs, Error **errp) +{ + int ret; + const char *vcpu_str = (cs->parent_obj.hotplugged == true) ? + "hotplug" : "create"; + cs->cpu_index = cpu_get_free_index(); + + POWERPC_CPU(cs)->vcpu_id = cs->cpu_index; + + /* create and park to fail gracefully in case vcpu hotplug fails */ + ret = kvm_create_and_park_vcpu(cs); + if (ret) { + /* + * This causes QEMU to terminate if initial CPU creation + * fails, and only CPU hotplug failure if the error happens + * there. + */ + error_setg(errp, "%s: vcpu %s failed with %d", + __func__, vcpu_str, ret); + return false; + } + return true; +} + static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2364,18 +2397,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) */ } @@ -2570,6 +2592,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(); @@ -2679,7 +2706,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 { @@ -2761,9 +2788,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"); } @@ -2843,7 +2870,7 @@ int kvm_arch_msi_data_to_gsi(uint32_t data) return data & 0xffff; } -#if defined(TARGET_PPC64) +#if defined(CONFIG_PSERIES) int kvm_handle_nmi(PowerPCCPU *cpu, struct kvm_run *run) { uint16_t flags = run->flags & KVM_RUN_PPC_NMI_DISP_MASK; @@ -2962,11 +2989,26 @@ void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) } } -bool kvm_arch_cpu_check_are_resettable(void) -{ - return true; -} - void kvm_arch_accel_class_init(ObjectClass *oc) { } + +static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_target_realize = kvmppc_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); 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 134b16c625..717bf93e88 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) { @@ -118,43 +118,11 @@ static const VMStateInfo vmstate_info_vsr = { #define VMSTATE_VSR_ARRAY(_f, _s, _n) \ VMSTATE_VSR_ARRAY_V(_f, _s, _n, 0) -static bool cpu_pre_2_8_migration(void *opaque, int version_id) -{ - PowerPCCPU *cpu = opaque; - - return cpu->pre_2_8_migration; -} - -#if defined(TARGET_PPC64) -static bool cpu_pre_3_0_migration(void *opaque, int version_id) -{ - PowerPCCPU *cpu = opaque; - - return cpu->pre_3_0_migration; -} -#endif - static int cpu_pre_save(void *opaque) { PowerPCCPU *cpu = opaque; CPUPPCState *env = &cpu->env; int i; - uint64_t insns_compat_mask = - PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB - | PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES - | PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES - | PPC_FLOAT_STFIWX | PPC_FLOAT_EXT - | PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ - | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | PPC_MEM_TLBSYNC - | PPC_64B | PPC_64BX | PPC_ALTIVEC - | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD; - uint64_t insns_compat_mask2 = PPC2_VSX | PPC2_VSX207 | PPC2_DFP | PPC2_DBRX - | PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 - | PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 - | PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 - | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 - | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | PPC2_TM - | PPC2_MEM_LWSYNC; env->spr[SPR_LR] = env->lr; env->spr[SPR_CTR] = env->ctr; @@ -177,38 +145,17 @@ static int cpu_pre_save(void *opaque) env->spr[SPR_IBAT4U + 2 * i + 1] = env->IBAT[1][i + 4]; } - /* Hacks for migration compatibility between 2.6, 2.7 & 2.8 */ - if (cpu->pre_2_8_migration) { - /* - * Mask out bits that got added to msr_mask since the versions - * which stupidly included it in the migration stream. - */ - target_ulong metamask = 0 -#if defined(TARGET_PPC64) - | (1ULL << MSR_TS0) - | (1ULL << MSR_TS1) -#endif - ; - cpu->mig_msr_mask = env->msr_mask & ~metamask; - cpu->mig_insns_flags = env->insns_flags & insns_compat_mask; - /* - * CPU models supported by old machines all have - * PPC_MEM_TLBIE, so we set it unconditionally to allow - * backward migration from a POWER9 host to a POWER8 host. - */ - cpu->mig_insns_flags |= PPC_MEM_TLBIE; - cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; - cpu->mig_nb_BATs = env->nb_BATs; - } - if (cpu->pre_3_0_migration) { - if (cpu->hash64_opts) { - cpu->mig_slb_nr = cpu->hash64_opts->slb_size; - } - } - /* 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; } @@ -314,7 +261,18 @@ static int cpu_post_load(void *opaque, int version_id) post_load_update_msr(env); if (tcg_enabled()) { - pmu_mmcr01_updated(env); + /* 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_mmcr01a_updated(env); } return 0; @@ -332,7 +290,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 +331,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 +364,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 +384,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 +418,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() }, @@ -530,12 +488,11 @@ static int slb_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_slb = { .name = "cpu/slb", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .needed = slb_needed, .post_load = slb_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_3_0_migration), + .fields = (const VMStateField[]) { VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } @@ -546,7 +503,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 +524,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 +539,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), @@ -602,11 +559,11 @@ static bool tlbemb_needed(void *opaque) } static const VMStateDescription vmstate_tlbemb = { - .name = "cpu/tlb6xx", + .name = "cpu/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 +577,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 +599,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, @@ -657,7 +614,7 @@ static bool compat_needed(void *opaque) PowerPCCPU *cpu = opaque; assert(!(cpu->compat_pvr && !cpu->vhyp)); - return !cpu->pre_2_10_migration && cpu->compat_pvr != 0; + return cpu->compat_pvr != 0; } static const VMStateDescription vmstate_compat = { @@ -665,19 +622,60 @@ 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() + } +}; + +#ifdef TARGET_PPC64 +static bool bhrb_needed(void *opaque) +{ + PowerPCCPU *cpu = opaque; + return (cpu->env.flags & POWERPC_FLAG_BHRB) != 0; +} + +static const VMStateDescription vmstate_bhrb = { + .name = "cpu/bhrb", + .version_id = 1, + .minimum_version_id = 1, + .needed = bhrb_needed, + .fields = (VMStateField[]) { + VMSTATE_UINTTL(env.bhrb_offset, PowerPCCPU), + VMSTATE_UINT64_ARRAY(env.bhrb, PowerPCCPU, BHRB_MAX_NUM_ENTRIES), + VMSTATE_END_OF_LIST() + } +}; +#endif + 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 +690,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), @@ -701,15 +698,9 @@ const VMStateDescription vmstate_ppc_cpu = { /* Backward compatible internal state */ VMSTATE_UINTTL(env.hflags_compat_nmsr, PowerPCCPU), - /* Sanity checking */ - VMSTATE_UINTTL_TEST(mig_msr_mask, PowerPCCPU, cpu_pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_insns_flags, PowerPCCPU, cpu_pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_insns_flags2, PowerPCCPU, - cpu_pre_2_8_migration), - 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, @@ -717,11 +708,13 @@ const VMStateDescription vmstate_ppc_cpu = { #ifdef TARGET_PPC64 &vmstate_tm, &vmstate_slb, + &vmstate_bhrb, #endif /* TARGET_PPC64 */ &vmstate_tlb6xx, &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..51b137febd 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)) { @@ -272,51 +271,59 @@ void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, } static void dcbz_common(CPUPPCState *env, target_ulong addr, - uint32_t opcode, bool epid, uintptr_t retaddr) + int mmu_idx, int dcbz_size, uintptr_t retaddr) { - target_ulong mask, dcbz_size = env->dcache_line_size; - uint32_t i; + target_ulong mask = ~(target_ulong)(dcbz_size - 1); void *haddr; - int mmu_idx = epid ? PPC_TLB_EPID_STORE : cpu_mmu_index(env, false); - -#if defined(TARGET_PPC64) - /* Check for dcbz vs dcbzl on 970 */ - if (env->excp_model == POWERPC_EXCP_970 && - !(opcode & 0x00200000) && ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { - dcbz_size = 32; - } -#endif /* Align address */ - mask = ~(dcbz_size - 1); addr &= mask; /* Check reservation */ - if ((env->reserve_addr & mask) == addr) { + if (unlikely((env->reserve_addr & mask) == addr)) { env->reserve_addr = (target_ulong)-1ULL; } /* Try fast path translate */ +#ifdef CONFIG_USER_ONLY + haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, mmu_idx); +#else haddr = probe_write(env, addr, dcbz_size, mmu_idx, retaddr); - if (haddr) { - memset(haddr, 0, dcbz_size); - } else { + if (unlikely(!haddr)) { /* Slow path */ - for (i = 0; i < dcbz_size; i += 8) { + for (int i = 0; i < dcbz_size; i += 8) { cpu_stq_mmuidx_ra(env, addr + i, 0, mmu_idx, retaddr); } + return; } +#endif + + set_helper_retaddr(retaddr); + memset(haddr, 0, dcbz_size); + clear_helper_retaddr(); } -void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) +void helper_dcbz(CPUPPCState *env, target_ulong addr, int mmu_idx) { - dcbz_common(env, addr, opcode, false, GETPC()); + dcbz_common(env, addr, mmu_idx, env->dcache_line_size, GETPC()); } -void helper_dcbzep(CPUPPCState *env, target_ulong addr, uint32_t opcode) +#ifdef TARGET_PPC64 +void helper_dcbzl(CPUPPCState *env, target_ulong addr) { - dcbz_common(env, addr, opcode, true, GETPC()); + int dcbz_size = env->dcache_line_size; + + /* + * The translator checked for POWERPC_EXCP_970. + * All that's left is to check HID5. + */ + if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { + dcbz_size = 32; + } + + dcbz_common(env, addr, ppc_env_mmu_index(env, false), dcbz_size, GETPC()); } +#endif void helper_icbi(CPUPPCState *env, target_ulong addr) { @@ -367,98 +374,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 @@ -497,9 +412,9 @@ uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr, } \ } #define I(x) (x) -LVE(lvebx, cpu_ldub_data_ra, I, u8) -LVE(lvehx, cpu_lduw_data_ra, bswap16, u16) -LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) +LVE(LVEBX, cpu_ldub_data_ra, I, u8) +LVE(LVEHX, cpu_lduw_data_ra, bswap16, u16) +LVE(LVEWX, cpu_ldl_data_ra, bswap32, u32) #undef I #undef LVE @@ -525,9 +440,9 @@ LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) } \ } #define I(x) (x) -STVE(stvebx, cpu_stb_data_ra, I, u8) -STVE(stvehx, cpu_stw_data_ra, bswap16, u16) -STVE(stvewx, cpu_stl_data_ra, bswap32, u32) +STVE(STVEBX, cpu_stb_data_ra, I, u8) +STVE(STVEHX, cpu_stw_data_ra, bswap16, u16) +STVE(STVEWX, cpu_stl_data_ra, bswap32, u32) #undef I #undef LVE @@ -560,8 +475,8 @@ void helper_##name(CPUPPCState *env, target_ulong addr, \ *xt = t; \ } -VSX_LXVL(lxvl, 0) -VSX_LXVL(lxvll, 1) +VSX_LXVL(LXVL, 0) +VSX_LXVL(LXVLL, 1) #undef VSX_LXVL #define VSX_STXVL(name, lj) \ @@ -589,8 +504,8 @@ void helper_##name(CPUPPCState *env, target_ulong addr, \ } \ } -VSX_STXVL(stxvl, 0) -VSX_STXVL(stxvll, 1) +VSX_STXVL(STXVL, 0) +VSX_STXVL(STXVLL, 1) #undef VSX_STXVL #undef GET_NB #endif /* TARGET_PPC64 */ diff --git a/target/ppc/meson.build b/target/ppc/meson.build index 79beaff147..db3b7a0c33 100644 --- a/target/ppc/meson.build +++ b/target/ppc/meson.build @@ -28,26 +28,27 @@ 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-booke.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 +56,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 a9bc1522e2..f0ca80153b 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -43,6 +43,48 @@ 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; + + if (ppc_cpu_core_single_threaded(cs)) { + 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, @@ -107,6 +149,17 @@ void helper_msr_facility_check(CPUPPCState *env, uint32_t bit, #if !defined(CONFIG_USER_ONLY) +#ifdef TARGET_PPC64 +static void helper_mmcr0_facility_check(CPUPPCState *env, uint32_t bit, + uint32_t sprn, uint32_t cause) +{ + if (FIELD_EX64(env->msr, MSR, PR) && + !(env->spr[SPR_POWER_MMCR0] & (1ULL << bit))) { + raise_fu_exception(env, bit, sprn, cause, GETPC()); + } +} +#endif + void helper_store_sdr1(CPUPPCState *env, target_ulong val) { if (env->spr[SPR_SDR1] != val) { @@ -119,6 +172,7 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) void helper_store_ptcr(CPUPPCState *env, target_ulong val) { if (env->spr[SPR_PTCR] != val) { + CPUState *cs = env_cpu(env); PowerPCCPU *cpu = env_archcpu(env); target_ulong ptcr_mask = PTCR_PATB | PTCR_PATS; target_ulong patbsize = val & PTCR_PATS; @@ -140,8 +194,19 @@ void helper_store_ptcr(CPUPPCState *env, target_ulong val) return; } - env->spr[SPR_PTCR] = val; - tlb_flush(env_cpu(env)); + if (ppc_cpu_lpar_single_threaded(cs)) { + env->spr[SPR_PTCR] = val; + tlb_flush(cs); + } else { + CPUState *ccs; + + THREAD_SIBLING_FOREACH(cs, ccs) { + PowerPCCPU *ccpu = POWERPC_CPU(ccs); + CPUPPCState *cenv = &ccpu->env; + cenv->spr[SPR_PTCR] = val; + tlb_flush(ccs); + } + } } } @@ -153,50 +218,175 @@ 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; 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 & PPC_INTERRUPT_DOORBELL) { - dpdes = 1; + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { + 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 = env_cpu(env); + CPUState *ccs; helper_hfscr_facility_check(env, HFSCR_MSGP, "store DPDES", HFSCR_IC_MSGP); - /* TODO: TCG supports only one thread */ - if (val & ~0x1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid DPDES register value " - TARGET_FMT_lx"\n", val); + /* DPDES behaves as 1-thread in LPAR-per-thread mode */ + if (ppc_cpu_lpar_single_threaded(cs)) { + ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1); return; } - ppc_set_irq(cpu, PPC_INTERRUPT_DOORBELL, val & 0x1); + /* 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(ccpu, PPC_INTERRUPT_DOORBELL, val & (0x1 << thread_id)); + } + bql_unlock(); +} + +/* + * qemu-user breaks with pnv headers, so they go under ifdefs for now. + * A clean up may be to move powernv specific registers and helpers into + * target/ppc/pnv_helper.c + */ +#include "hw/ppc/pnv_core.h" + +/* Indirect SCOM (SPRC/SPRD) access to SCRATCH0-7 are implemented. */ +void helper_store_sprc(CPUPPCState *env, target_ulong val) +{ + if (val & ~0x3f8ULL) { + qemu_log_mask(LOG_GUEST_ERROR, "Invalid SPRC register value " + TARGET_FMT_lx"\n", val); + return; + } + env->spr[SPR_POWER_SPRC] = val; +} + +target_ulong helper_load_sprd(CPUPPCState *env) +{ + /* + * SPRD is a HV-only register for Power CPUs, so this will only be + * accessed by powernv machines. + */ + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + target_ulong sprc = env->spr[SPR_POWER_SPRC]; + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + return pc->scratch[(sprc >> 3) & 0x7]; + + case 0x1e0: /* core thread state */ + if (env->excp_model == POWERPC_EXCP_POWER9) { + /* + * Only implement for POWER9 because skiboot uses it to check + * big-core mode. Other bits are unimplemented so we would + * prefer to get unimplemented message on POWER10 if it were + * used anywhere. + */ + if (pc->big_core) { + return PPC_BIT(63); + } else { + return 0; + } + } + /* fallthru */ + + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } + return 0; +} + +void helper_store_sprd(CPUPPCState *env, target_ulong val) +{ + target_ulong sprc = env->spr[SPR_POWER_SPRC]; + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + int nr; + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + /* + * Log stores to SCRATCH, because some firmware uses these for + * debugging and logging, but they would normally be read by the BMC, + * which is not implemented in QEMU yet. This gives a way to get at the + * information. Could also dump these upon checkstop. + */ + nr = (sprc >> 3) & 0x7; + qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, nr); + pc->scratch[nr] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } } #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 @@ -265,3 +455,42 @@ void helper_fixup_thrm(CPUPPCState *env) env->spr[i] = v; } } + +#if !defined(CONFIG_USER_ONLY) +#if defined(TARGET_PPC64) +void helper_clrbhrb(CPUPPCState *env) +{ + helper_hfscr_facility_check(env, HFSCR_BHRB, "clrbhrb", FSCR_IC_BHRB); + + helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB); + + if (env->flags & POWERPC_FLAG_BHRB) { + memset(env->bhrb, 0, sizeof(env->bhrb)); + } +} + +uint64_t helper_mfbhrbe(CPUPPCState *env, uint32_t bhrbe) +{ + unsigned int index; + + helper_hfscr_facility_check(env, HFSCR_BHRB, "mfbhrbe", FSCR_IC_BHRB); + + helper_mmcr0_facility_check(env, MMCR0_BHRBA_NR, 0, FSCR_IC_BHRB); + + if (!(env->flags & POWERPC_FLAG_BHRB) || + (bhrbe >= env->bhrb_num_entries) || + (env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) { + return 0; + } + + /* + * Note: bhrb_offset is the byte offset for writing the + * next entry (over the oldest entry), which is why we + * must offset bhrbe by 1 to get to the 0th entry. + */ + index = ((env->bhrb_offset / sizeof(uint64_t)) - (bhrbe + 1)) % + env->bhrb_num_entries; + return env->bhrb[index]; +} +#endif +#endif diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index c8f69b3df9..a812cb5113 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" -#include "mmu-radix64.h" bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 674377a19e..be66e26604 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -20,9 +20,6 @@ #ifndef PPC_MMU_BOOK3S_V3_H #define PPC_MMU_BOOK3S_V3_H -#include "mmu-hash64.h" -#include "mmu-books.h" - #ifndef CONFIG_USER_ONLY /* @@ -83,48 +80,6 @@ static inline bool ppc64_v3_radix(PowerPCCPU *cpu) return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR); } -static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) -{ - uint64_t base; - - if (cpu->vhyp) { - return 0; - } - if (cpu->env.mmu_model == POWERPC_MMU_3_00) { - ppc_v3_pate_t pate; - - if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { - return 0; - } - base = pate.dw0; - } else { - base = cpu->env.spr[SPR_SDR1]; - } - return base & SDR_64_HTABORG; -} - -static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) -{ - uint64_t base; - - if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->hpt_mask(cpu->vhyp); - } - if (cpu->env.mmu_model == POWERPC_MMU_3_00) { - ppc_v3_pate_t pate; - - if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { - return 0; - } - base = pate.dw0; - } else { - base = cpu->env.spr[SPR_SDR1]; - } - return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; -} - #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu-booke.c b/target/ppc/mmu-booke.c new file mode 100644 index 0000000000..55e5dd7c6b --- /dev/null +++ b/target/ppc/mmu-booke.c @@ -0,0 +1,531 @@ +/* + * PowerPC BookE MMU, TLB emulation helpers for QEMU. + * + * Copyright (c) 2003-2007 Jocelyn Mayer + * + * 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 "exec/page-protection.h" +#include "exec/log.h" +#include "cpu.h" +#include "internal.h" +#include "mmu-booke.h" + +/* Generic TLB check function for embedded PowerPC implementations */ +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 false; + } + mask = ~(tlb->size - 1); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx + " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n", + __func__, i, address, pid, tlb->EPN, + mask, (uint32_t)tlb->PID, tlb->prot); + /* Check PID */ + if (tlb->PID != 0 && tlb->PID != pid) { + return false; + } + /* Check effective address */ + if ((address & mask) != tlb->EPN) { + return false; + } + *raddrp = (tlb->RPN & mask) | (address & ~mask); + return true; +} + +/* 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; +} + +int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + int i, ret, zsel, zpr, pr; + + ret = -1; + 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], i)) { + continue; + } + zsel = (tlb->attr >> 4) & 0xF; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; + qemu_log_mask(CPU_LOG_MMU, + "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", + __func__, i, zsel, zpr, access_type, tlb->attr); + /* Check execute enable bit */ + switch (zpr) { + case 0x2: + if (pr != 0) { + goto check_perms; + } + /* fall through */ + case 0x3: + /* All accesses granted */ + *prot = PAGE_RWX; + ret = 0; + break; + + case 0x0: + if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; + *prot = 0; + ret = -2; + break; + } + /* fall through */ + case 0x1: +check_perms: + /* Check from TLB entry */ + *prot = tlb->prot; + if (check_prot_access_type(*prot, access_type)) { + ret = 0; + } else { + env->spr[SPR_40x_ESR] = 0; + ret = -2; + } + break; + } + } + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? 0 : *raddr, *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) +{ + if (!mmubooke_check_pid(env, tlb, raddr, address, i)) { + qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); + return -1; + } + + /* Check the address space */ + if ((access_type == MMU_INST_FETCH ? + FIELD_EX64(env->msr, MSR, IR) : + FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + + if (FIELD_EX64(env->msr, MSR, PR)) { + *prot = tlb->prot & 0xF; + } else { + *prot = (tlb->prot >> 4) & 0xF; + } + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + return 0; + } + + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, + MMUAccessType access_type) +{ + ppcemb_tlb_t *tlb; + int i, ret = -1; + + for (i = 0; i < env->nb_tlb; i++) { + tlb = &env->tlb.tlbe[i]; + ret = mmubooke_check_tlb(env, tlb, raddr, prot, address, + access_type, i); + if (ret != -1) { + break; + } + } + qemu_log_mask(CPU_LOG_MMU, + "%s: access %s " TARGET_FMT_lx " => " HWADDR_FMT_plx + " %d %d\n", __func__, ret < 0 ? "refused" : "granted", + address, ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); + return ret; +} + +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb) +{ + int tlbm_size; + + tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; + + return 1024ULL << tlbm_size; +} + +/* 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) +{ + hwaddr mask; + uint32_t tlb_pid; + + if (!FIELD_EX64(env->msr, MSR, CM)) { + /* In 32bit mode we can only address 32bit EAs */ + address = (uint32_t)address; + } + + /* Check valid flag */ + if (!(tlb->mas1 & MAS1_VALID)) { + return -1; + } + + mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); + qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx + " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" + HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", + __func__, address, pid, tlb->mas1, tlb->mas2, mask, + tlb->mas7_3, tlb->mas8); + + /* Check PID */ + tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; + if (tlb_pid != 0 && tlb_pid != pid) { + return -1; + } + + /* Check effective address */ + if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { + return -1; + } + + if (raddrp) { + *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); + } + + return 0; +} + +static bool is_epid_mmu(int mmu_idx) +{ + return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; +} + +static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) +{ + uint32_t esr = 0; + if (access_type == MMU_DATA_STORE) { + esr |= ESR_ST; + } + if (is_epid_mmu(mmu_idx)) { + esr |= ESR_EPID; + } + return esr; +} + +/* + * Get EPID register given the mmu_idx. If this is regular load, + * construct the EPID access bits from current processor state + * + * Get the effective AS and PR bits and the PID. The PID is returned + * only if EPID load is requested, otherwise the caller must detect + * the correct EPID. Return true if valid EPID is returned. + */ +static bool mmubooke206_get_as(CPUPPCState *env, + int mmu_idx, uint32_t *epid_out, + bool *as_out, bool *pr_out) +{ + if (is_epid_mmu(mmu_idx)) { + uint32_t epidr; + if (mmu_idx == PPC_TLB_EPID_STORE) { + epidr = env->spr[SPR_BOOKE_EPSC]; + } else { + epidr = env->spr[SPR_BOOKE_EPLC]; + } + *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; + *as_out = !!(epidr & EPID_EAS); + *pr_out = !!(epidr & EPID_EPR); + return true; + } else { + *as_out = FIELD_EX64(env->msr, MSR, DS); + *pr_out = FIELD_EX64(env->msr, MSR, PR); + return false; + } +} + +/* Check if the tlb found by hashing really matches */ +static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, + hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (!use_epid) { + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } + + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + } else { + if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { + goto found_tlb; + } + } + + 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: + + /* Check the address space and permissions */ + if (access_type == MMU_INST_FETCH) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); + as = FIELD_EX64(env->msr, MSR, IR); + } + + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); + return -1; + } + + *prot = 0; + if (pr) { + if (tlb->mas7_3 & MAS3_UR) { + *prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_UW) { + *prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_UX) { + *prot |= PAGE_EXEC; + } + } else { + if (tlb->mas7_3 & MAS3_SR) { + *prot |= PAGE_READ; + } + if (tlb->mas7_3 & MAS3_SW) { + *prot |= PAGE_WRITE; + } + if (tlb->mas7_3 & MAS3_SX) { + *prot |= PAGE_EXEC; + } + } + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + return 0; + } + + qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, *prot); + return access_type == MMU_INST_FETCH ? -3 : -2; +} + +static int mmubooke206_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong address, + MMUAccessType access_type, + int mmu_idx) +{ + ppcmas_tlb_t *tlb; + int i, j, ret = -1; + + for (i = 0; i < BOOKE206_MAX_TLBN; i++) { + int ways = booke206_tlb_ways(env, i); + for (j = 0; j < ways; j++) { + tlb = booke206_get_tlbm(env, i, address, j); + if (!tlb) { + continue; + } + ret = mmubooke206_check_tlb(env, tlb, raddr, prot, address, + access_type, mmu_idx); + if (ret != -1) { + goto found_tlb; + } + } + } + +found_tlb: + + qemu_log_mask(CPU_LOG_MMU, "%s: access %s " TARGET_FMT_lx " => " + HWADDR_FMT_plx " %d %d\n", __func__, + ret < 0 ? "refused" : "granted", address, + ret < 0 ? -1 : *raddr, ret == -1 ? 0 : *prot, ret); + return ret; +} + +static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + uint32_t epid; + bool as, pr; + uint32_t missed_tid = 0; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + + if (access_type == MMU_INST_FETCH) { + as = FIELD_EX64(env->msr, MSR, IR); + } + env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; + env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; + env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; + env->spr[SPR_BOOKE_MAS3] = 0; + env->spr[SPR_BOOKE_MAS6] = 0; + env->spr[SPR_BOOKE_MAS7] = 0; + + /* AS */ + if (as) { + env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; + env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; + } + + env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; + env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; + + if (!use_epid) { + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + missed_tid = env->spr[SPR_BOOKE_PID]; + break; + case MAS4_TIDSELD_PID1: + missed_tid = env->spr[SPR_BOOKE_PID1]; + break; + case MAS4_TIDSELD_PID2: + missed_tid = env->spr[SPR_BOOKE_PID2]; + break; + } + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + } else { + missed_tid = epid; + env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; + } + env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); + + + /* next victim logic */ + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; + env->last_way++; + env->last_way &= booke206_tlb_ways(env, 0) - 1; + env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; +} + +bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, + bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + hwaddr raddr; + int prot, ret; + + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + ret = mmubooke206_get_physical_address(env, &raddr, &prot, eaddr, + access_type, mmu_idx); + } else { + ret = mmubooke_get_physical_address(env, &raddr, &prot, eaddr, + access_type); + } + if (ret == 0) { + *raddrp = raddr; + *protp = prot; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + env->error_code = 0; + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + if (env->mmu_model == POWERPC_MMU_BOOKE206) { + booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); + } + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ITLB : POWERPC_EXCP_DTLB; + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + break; + case -2: + /* Access rights violation */ + cs->exception_index = (access_type == MMU_INST_FETCH) ? + POWERPC_EXCP_ISI : POWERPC_EXCP_DSI; + if (access_type != MMU_INST_FETCH) { + env->spr[SPR_BOOKE_DEAR] = eaddr; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + } + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->spr[SPR_BOOKE_ESR] = 0; + break; + } + + return false; +} diff --git a/target/ppc/mmu-booke.h b/target/ppc/mmu-booke.h new file mode 100644 index 0000000000..f972843bbb --- /dev/null +++ b/target/ppc/mmu-booke.h @@ -0,0 +1,17 @@ +#ifndef PPC_MMU_BOOKE_H +#define PPC_MMU_BOOKE_H + +#include "cpu.h" + +int ppcemb_tlb_search(CPUPPCState *env, target_ulong address, uint32_t pid); +int mmu40x_get_physical_address(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong address, + MMUAccessType access_type); +hwaddr booke206_tlb_to_page_size(CPUPPCState *env, ppcmas_tlb_t *tlb); +int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddrp, + target_ulong address, uint32_t pid); +bool ppc_booke_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, + bool guest_visible); + +#endif diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index cc091c3e62..44b16142ab 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "internal.h" @@ -36,68 +37,6 @@ # define LOG_BATS(...) do { } while (0) #endif -struct mmu_ctx_hash32 { - hwaddr raddr; /* Real address */ - int prot; /* Protection bits */ - int key; /* Access key */ -}; - -static int ppc_hash32_pp_prot(int key, int pp, int nx) -{ - int prot; - - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - case 0x3: - prot = PAGE_READ; - break; - - default: - abort(); - } - } else { - switch (pp) { - case 0x0: - prot = 0; - break; - - case 0x1: - case 0x3: - prot = PAGE_READ; - break; - - case 0x2: - prot = PAGE_READ | PAGE_WRITE; - break; - - default: - abort(); - } - } - if (nx == 0) { - prot |= PAGE_EXEC; - } - - return prot; -} - -static int ppc_hash32_pte_prot(int mmu_idx, - target_ulong sr, ppc_hash_pte32_t pte) -{ - unsigned pp, key; - - key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); - pp = pte.pte1 & HPTE32_R_PP; - - return ppc_hash32_pp_prot(key, pp, !!(sr & SR32_NX)); -} - static target_ulong hash32_bat_size(int mmu_idx, target_ulong batu, target_ulong batl) { @@ -109,22 +48,6 @@ static target_ulong hash32_bat_size(int mmu_idx, return BATU32_BEPI & ~((batu & BATU32_BL) << 15); } -static int hash32_bat_prot(PowerPCCPU *cpu, - target_ulong batu, target_ulong batl) -{ - int pp, prot; - - prot = 0; - pp = batl & BATL32_PP; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; - } - } - return prot; -} - static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, MMUAccessType access_type, int *prot, int mmu_idx) @@ -156,7 +79,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, if (mask && ((ea & mask) == (batu & BATU32_BEPI))) { hwaddr raddr = (batl & mask) | (ea & ~mask); - *prot = hash32_bat_prot(cpu, batu, batl); + *prot = ppc_hash32_bat_prot(batu, batl); return raddr & TARGET_PAGE_MASK; } @@ -195,7 +118,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int key = !!(mmuidx_pr(mmu_idx) ? (sr & SR32_KP) : (sr & SR32_KS)); qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); @@ -256,8 +178,12 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, cpu_abort(cs, "ERROR: insn should not need address translation\n"); } - *prot = key ? PAGE_READ | PAGE_WRITE : PAGE_READ; - if (*prot & prot_for_access_type(access_type)) { + if (ppc_hash32_key(mmuidx_pr(mmu_idx), sr)) { + *prot = PAGE_READ | PAGE_WRITE; + } else { + *prot = PAGE_READ; + } + if (check_prot_access_type(*prot, access_type)) { *raddr = eaddr; return true; } @@ -275,13 +201,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, return false; } -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) -{ - target_ulong mask = ppc_hash32_hpt_mask(cpu); - - return (hash * HASH_PTEG_SIZE_32) & mask; -} - static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, bool secondary, target_ulong ptem, ppc_hash_pte32_t *pte) @@ -346,24 +265,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); @@ -372,15 +291,6 @@ static hwaddr ppc_hash32_htab_lookup(PowerPCCPU *cpu, return pte_offset; } -static hwaddr ppc_hash32_pte_raddr(target_ulong sr, ppc_hash_pte32_t pte, - target_ulong eaddr) -{ - hwaddr rpn = pte.pte1 & HPTE32_R_RPN; - hwaddr mask = ~TARGET_PAGE_MASK; - - return (rpn & ~mask) | (eaddr & mask); -} - bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible) @@ -388,11 +298,10 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong sr; - hwaddr pte_offset; + hwaddr pte_offset, raddr; ppc_hash_pte32_t pte; + bool key; int prot; - int need_prot; - hwaddr raddr; /* There are no hash32 large pages. */ *psizep = TARGET_PAGE_BITS; @@ -405,13 +314,11 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, return true; } - need_prot = prot_for_access_type(access_type); - /* 2. Check Block Address Translation entries (BATs) */ if (env->nb_BATs != 0) { raddr = ppc_hash32_bat_lookup(cpu, eaddr, access_type, protp, mmu_idx); if (raddr != -1) { - if (need_prot & ~*protp) { + if (!check_prot_access_type(*protp, access_type)) { if (guest_visible) { if (access_type == MMU_INST_FETCH) { cs->exception_index = POWERPC_EXCP_ISI; @@ -476,10 +383,10 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, "found PTE at offset %08" HWADDR_PRIx "\n", pte_offset); /* 7. Check access permissions */ + key = ppc_hash32_key(mmuidx_pr(mmu_idx), sr); + prot = ppc_hash32_prot(key, pte.pte1 & HPTE32_R_PP, sr & SR32_NX); - prot = ppc_hash32_pte_prot(mmu_idx, sr, pte); - - if (need_prot & ~prot) { + if (!check_prot_access_type(prot, access_type)) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); if (guest_visible) { @@ -517,11 +424,12 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, */ prot &= ~PAGE_WRITE; } - } + } + *protp = prot; /* 9. Determine the real address from the PTE */ - - *raddrp = ppc_hash32_pte_raddr(sr, pte, eaddr); - *protp = prot; + *raddrp = pte.pte1 & HPTE32_R_RPN; + *raddrp &= TARGET_PAGE_MASK; + *raddrp |= eaddr & ~TARGET_PAGE_MASK; return true; } diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 7119a63d97..2838de031c 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -3,7 +3,6 @@ #ifndef CONFIG_USER_ONLY -hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash); bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); @@ -102,6 +101,66 @@ static inline void ppc_hash32_store_hpte1(PowerPCCPU *cpu, stl_phys(CPU(cpu)->as, base + pte_offset + HASH_PTE_SIZE_32 / 2, pte1); } +static inline hwaddr get_pteg_offset32(PowerPCCPU *cpu, hwaddr hash) +{ + return (hash * HASH_PTEG_SIZE_32) & ppc_hash32_hpt_mask(cpu); +} + +static inline bool ppc_hash32_key(bool pr, target_ulong sr) +{ + return pr ? (sr & SR32_KP) : (sr & SR32_KS); +} + +static inline int ppc_hash32_prot(bool key, int pp, bool nx) +{ + int prot; + + if (key) { + switch (pp) { + case 0x0: + prot = 0; + break; + case 0x1: + case 0x3: + prot = PAGE_READ; + break; + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + default: + g_assert_not_reached(); + } + } else { + switch (pp) { + case 0x0: + case 0x1: + case 0x2: + prot = PAGE_READ | PAGE_WRITE; + break; + case 0x3: + prot = PAGE_READ; + break; + default: + g_assert_not_reached(); + } + } + return nx ? prot : prot | PAGE_EXEC; +} + +static inline int ppc_hash32_bat_prot(target_ulong batu, target_ulong batl) +{ + int prot = 0; + int pp = batl & BATL32_PP; + + if (pp) { + prot = PAGE_READ | PAGE_EXEC; + if (pp == 0x2) { + prot |= PAGE_WRITE; + } + } + return prot; +} + typedef struct { uint32_t pte0, pte1; } ppc_hash_pte32_t; diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index b9b31fd276..c8c2f8910a 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -21,6 +21,7 @@ #include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "sysemu/hw_accel.h" @@ -30,6 +31,7 @@ #include "hw/hw.h" #include "internal.h" #include "mmu-book3s-v3.h" +#include "mmu-books.h" #include "helper_regs.h" #ifdef CONFIG_TCG @@ -507,6 +509,46 @@ static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) return prot; } +static hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return 0; + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return base & SDR_64_HTABORG; +} + +static hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return cpu->vhyp_class->hpt_mask(cpu->vhyp); + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; +} + const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n) { @@ -516,9 +558,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->map_hptes(cpu->vhyp, ptex, n); + return cpu->vhyp_class->map_hptes(cpu->vhyp, ptex, n); } base = ppc_hash64_hpt_base(cpu); @@ -538,9 +578,7 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n) { if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->unmap_hptes(cpu->vhyp, hptes, ptex, n); + cpu->vhyp_class->unmap_hptes(cpu->vhyp, hptes, ptex, n); return; } @@ -548,6 +586,15 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, false, n * HASH_PTE_SIZE_64); } +bool ppc_hash64_valid_ptex(PowerPCCPU *cpu, target_ulong ptex) +{ + /* hash value/pteg group index is normalized by HPT mask */ + if (((ptex & ~7ULL) / HPTES_PER_GROUP) & ~ppc_hash64_hpt_mask(cpu)) { + return false; + } + return true; +} + static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps, uint64_t pte0, uint64_t pte1) { @@ -697,15 +744,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); @@ -714,9 +761,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); @@ -770,7 +817,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; @@ -782,13 +830,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; @@ -802,6 +852,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; @@ -816,9 +867,7 @@ static void ppc_hash64_set_r(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_R; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hpte_set_r(cpu->vhyp, ptex, pte1); + cpu->vhyp_class->hpte_set_r(cpu->vhyp, ptex, pte1); return; } base = ppc_hash64_hpt_base(cpu); @@ -833,9 +882,7 @@ static void ppc_hash64_set_c(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte1) hwaddr base, offset = ptex * HASH_PTE_SIZE_64 + HPTE64_DW1_C; if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hpte_set_c(cpu->vhyp, ptex, pte1); + cpu->vhyp_class->hpte_set_c(cpu->vhyp, ptex, pte1); return; } base = ppc_hash64_hpt_base(cpu); @@ -870,12 +917,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++) { @@ -893,8 +974,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; } @@ -913,6 +993,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, int exec_prot, pp_prot, amr_prot, prot; int need_prot; hwaddr raddr; + bool vrma = false; /* * Note on LPCR usage: 970 uses HID4, but our special variant of @@ -942,6 +1023,7 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, } } else if (ppc_hash64_use_vrma(env)) { /* Emulated VRMA mode */ + vrma = true; slb = &vrma_slbe; if (build_vrma_slbe(cpu, slb) != 0) { /* Invalid VRMA setup, machine check */ @@ -963,13 +1045,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: @@ -1022,7 +1104,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; } @@ -1035,13 +1117,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(); @@ -1055,10 +1138,15 @@ bool ppc_hash64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, exec_prot = ppc_hash64_pte_noexec_guard(cpu, pte); pp_prot = ppc_hash64_pte_prot(mmu_idx, slb, pte); - amr_prot = ppc_hash64_amr_prot(cpu, pte); + if (vrma) { + /* VRMA does not check keys */ + amr_prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + } else { + amr_prot = ppc_hash64_amr_prot(cpu, pte); + } prot = exec_prot & pp_prot & amr_prot; - need_prot = prot_for_access_type(access_type); + need_prot = check_prot_access_type(PAGE_RWX, access_type); if (need_prot & ~prot) { /* Access right violation */ qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); @@ -1075,7 +1163,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) { @@ -1087,7 +1175,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; } @@ -1149,7 +1237,7 @@ void ppc_hash64_init(PowerPCCPU *cpu) return; } - cpu->hash64_opts = g_memdup(pcc->hash64_opts, sizeof(*cpu->hash64_opts)); + cpu->hash64_opts = g_memdup2(pcc->hash64_opts, sizeof(*cpu->hash64_opts)); } void ppc_hash64_finalize(PowerPCCPU *cpu) diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index 1496955d38..ae8d4b37ae 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) @@ -115,6 +120,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n); void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n); +bool ppc_hash64_valid_ptex(PowerPCCPU *cpu, target_ulong ptex); static inline uint64_t ppc_hash64_hpte0(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, int i) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 031efda0df..be7a45f254 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" @@ -27,6 +28,38 @@ #include "internal.h" #include "mmu-radix64.h" #include "mmu-book3s-v3.h" +#include "mmu-books.h" + +/* Radix Partition Table Entry Fields */ +#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 +#define PATE1_R_PRTS 0x000000000000001F + +/* Radix Process Table Entry Fields */ +#define PRTBE_R_GET_RTS(rts) \ + ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31) +#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00 +#define PRTBE_R_RPDS 0x000000000000001F + +/* Radix Page Directory/Table Entry Fields */ +#define R_PTE_VALID 0x8000000000000000 +#define R_PTE_LEAF 0x4000000000000000 +#define R_PTE_SW0 0x2000000000000000 +#define R_PTE_RPN 0x01FFFFFFFFFFF000 +#define R_PTE_SW1 0x0000000000000E00 +#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7)) +#define R_PTE_R 0x0000000000000100 +#define R_PTE_C 0x0000000000000080 +#define R_PTE_ATT 0x0000000000000030 +#define R_PTE_ATT_NORMAL 0x0000000000000000 +#define R_PTE_ATT_SAO 0x0000000000000010 +#define R_PTE_ATT_NI_IO 0x0000000000000020 +#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030 +#define R_PTE_EAA_PRIV 0x0000000000000008 +#define R_PTE_EAA_R 0x0000000000000004 +#define R_PTE_EAA_RW 0x0000000000000002 +#define R_PTE_EAA_X 0x0000000000000001 +#define R_PDE_NLB PRTBE_R_RPDB +#define R_PDE_NLS PRTBE_R_RPDS static bool ppc_radix64_get_fully_qualified_addr(const CPUPPCState *env, vaddr eaddr, @@ -145,6 +178,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,19 +206,35 @@ 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(); } } +static int ppc_radix64_get_prot_eaa(uint64_t pte) +{ + return (pte & R_PTE_EAA_R ? PAGE_READ : 0) | + (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) | + (pte & R_PTE_EAA_X ? PAGE_EXEC : 0); +} + +static int ppc_radix64_get_prot_amr(const PowerPCCPU *cpu) +{ + const CPUPPCState *env = &cpu->env; + int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */ + int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */ + + return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */ + (amr & 0x1 ? 0 : PAGE_READ) | + (iamr & 0x1 ? 0 : PAGE_EXEC); +} + static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, uint64_t pte, int *fault_cause, int *prot, int mmu_idx, bool partition_scoped) { CPUPPCState *env = &cpu->env; - int need_prot; /* Check Page Attributes (pte58:59) */ if ((pte & R_PTE_ATT) == R_PTE_ATT_NI_IO && access_type == MMU_INST_FETCH) { @@ -203,8 +259,8 @@ static bool ppc_radix64_check_prot(PowerPCCPU *cpu, MMUAccessType access_type, } /* Check if requested access type is allowed */ - need_prot = prot_for_access_type(access_type); - if (need_prot & ~*prot) { /* Page Protected for that Access */ + if (!check_prot_access_type(*prot, access_type)) { + /* Page Protected for that Access */ *fault_cause |= access_type == MMU_INST_FETCH ? SRR1_NOEXEC_GUARD : DSISR_PROTFAULT; return true; @@ -213,27 +269,25 @@ 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) @@ -296,8 +350,8 @@ static int ppc_radix64_next_level(AddressSpace *as, vaddr eaddr, if (nlb & mask) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: misaligned page dir/table base: 0x"TARGET_FMT_lx - " page dir size: 0x"TARGET_FMT_lx"\n", + "%s: misaligned page dir/table base: 0x%" PRIx64 + " page dir size: 0x%" PRIx64 "\n", __func__, nlb, mask + 1); nlb &= ~mask; } @@ -320,8 +374,8 @@ static int ppc_radix64_walk_tree(AddressSpace *as, vaddr eaddr, if (base_addr & mask) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: misaligned page dir base: 0x"TARGET_FMT_lx - " page dir size: 0x"TARGET_FMT_lx"\n", + "%s: misaligned page dir base: 0x%" PRIx64 + " page dir size: 0x%" PRIx64 "\n", __func__, base_addr, mask + 1); base_addr &= ~mask; } @@ -369,17 +423,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), @@ -396,13 +460,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; @@ -431,7 +513,8 @@ 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; @@ -477,11 +560,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; } @@ -519,11 +602,12 @@ 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; } @@ -564,7 +648,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; @@ -639,9 +727,7 @@ static bool ppc_radix64_xlate_impl(PowerPCCPU *cpu, vaddr eaddr, /* Get Partition Table */ if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc; - vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - if (!vhc->get_pate(cpu->vhyp, cpu, lpid, &pate)) { + if (!cpu->vhyp_class->get_pate(cpu->vhyp, cpu, lpid, &pate)) { if (guest_visible) { ppc_radix64_raise_hsi(cpu, access_type, eaddr, eaddr, DSISR_R_BADCONFIG); @@ -679,7 +765,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; } @@ -703,7 +790,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-radix64.h b/target/ppc/mmu-radix64.h index 4c768aa5cc..6620b3d648 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -3,6 +3,8 @@ #ifndef CONFIG_USER_ONLY +#ifdef TARGET_PPC64 + /* Radix Quadrants */ #define R_EADDR_MASK 0x3FFFFFFFFFFFFFFF #define R_EADDR_VALID_MASK 0xC00FFFFFFFFFFFFF @@ -12,61 +14,10 @@ #define R_EADDR_QUADRANT2 0x8000000000000000 #define R_EADDR_QUADRANT3 0xC000000000000000 -/* Radix Partition Table Entry Fields */ -#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 -#define PATE1_R_PRTS 0x000000000000001F - -/* Radix Process Table Entry Fields */ -#define PRTBE_R_GET_RTS(rts) \ - ((((rts >> 58) & 0x18) | ((rts >> 5) & 0x7)) + 31) -#define PRTBE_R_RPDB 0x0FFFFFFFFFFFFF00 -#define PRTBE_R_RPDS 0x000000000000001F - -/* Radix Page Directory/Table Entry Fields */ -#define R_PTE_VALID 0x8000000000000000 -#define R_PTE_LEAF 0x4000000000000000 -#define R_PTE_SW0 0x2000000000000000 -#define R_PTE_RPN 0x01FFFFFFFFFFF000 -#define R_PTE_SW1 0x0000000000000E00 -#define R_GET_SW(sw) (((sw >> 58) & 0x8) | ((sw >> 9) & 0x7)) -#define R_PTE_R 0x0000000000000100 -#define R_PTE_C 0x0000000000000080 -#define R_PTE_ATT 0x0000000000000030 -#define R_PTE_ATT_NORMAL 0x0000000000000000 -#define R_PTE_ATT_SAO 0x0000000000000010 -#define R_PTE_ATT_NI_IO 0x0000000000000020 -#define R_PTE_ATT_TOLERANT_IO 0x0000000000000030 -#define R_PTE_EAA_PRIV 0x0000000000000008 -#define R_PTE_EAA_R 0x0000000000000004 -#define R_PTE_EAA_RW 0x0000000000000002 -#define R_PTE_EAA_X 0x0000000000000001 -#define R_PDE_NLB PRTBE_R_RPDB -#define R_PDE_NLS PRTBE_R_RPDS - -#ifdef TARGET_PPC64 - bool ppc_radix64_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddr, int *psizep, int *protp, int mmu_idx, bool guest_visible); -static inline int ppc_radix64_get_prot_eaa(uint64_t pte) -{ - return (pte & R_PTE_EAA_R ? PAGE_READ : 0) | - (pte & R_PTE_EAA_RW ? PAGE_READ | PAGE_WRITE : 0) | - (pte & R_PTE_EAA_X ? PAGE_EXEC : 0); -} - -static inline int ppc_radix64_get_prot_amr(const PowerPCCPU *cpu) -{ - const CPUPPCState *env = &cpu->env; - int amr = env->spr[SPR_AMR] >> 62; /* We only care about key0 AMR63:62 */ - int iamr = env->spr[SPR_IAMR] >> 62; /* We only care about key0 IAMR63:62 */ - - return (amr & 0x2 ? 0 : PAGE_WRITE) | /* Access denied if bit is set */ - (amr & 0x1 ? 0 : PAGE_READ) | - (iamr & 0x1 ? 0 : PAGE_EXEC); -} - #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 89107a6af2..60f8736210 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -25,14 +25,15 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #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" #include "mmu-radix64.h" +#include "mmu-booke.h" /* #define DUMP_PAGE_TABLES */ @@ -65,49 +66,6 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) /*****************************************************************************/ /* PowerPC MMU emulation */ -static int pp_check(int key, int pp, int nx) -{ - int access; - - /* Compute access rights */ - access = 0; - if (key == 0) { - switch (pp) { - case 0x0: - case 0x1: - case 0x2: - access |= PAGE_WRITE; - /* fall through */ - case 0x3: - access |= PAGE_READ; - break; - } - } else { - switch (pp) { - case 0x0: - access = 0; - break; - case 0x1: - case 0x3: - access = PAGE_READ; - break; - case 0x2: - access = PAGE_READ | PAGE_WRITE; - break; - } - } - if (nx == 0) { - access |= PAGE_EXEC; - } - - return access; -} - -static int check_prot(int prot, MMUAccessType access_type) -{ - return prot & prot_for_access_type(access_type) ? 0 : -2; -} - int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, int way, int is_code) { @@ -117,95 +75,31 @@ int ppc6xx_tlb_getnum(CPUPPCState *env, target_ulong eaddr, nr = (eaddr >> TARGET_PAGE_BITS) & (env->tlb_per_way - 1); /* Select TLB way */ nr += env->tlb_per_way * way; - /* 6xx have separate TLBs for instructions and data */ - if (is_code && env->id_tlbs == 1) { + /* 6xx has separate TLBs for instructions and data */ + if (is_code) { nr += env->nb_tlb; } return nr; } -static int ppc6xx_tlb_pte_check(mmu_ctx_t *ctx, target_ulong pte0, - target_ulong pte1, int h, - MMUAccessType access_type) -{ - target_ulong ptem, mmask; - int access, ret, pteh, ptev, pp; - - ret = -1; - /* Check validity and table match */ - ptev = pte_is_valid(pte0); - pteh = (pte0 >> 6) & 1; - if (ptev && h == pteh) { - /* Check vsid & api */ - ptem = pte0 & PTE_PTEM_MASK; - mmask = PTE_CHECK_MASK; - pp = pte1 & 0x00000003; - if (ptem == ctx->ptem) { - if (ctx->raddr != (hwaddr)-1ULL) { - /* all matches should have equal RPN, WIMG & PP */ - if ((ctx->raddr & mmask) != (pte1 & mmask)) { - qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); - return -3; - } - } - /* Compute access rights */ - access = pp_check(ctx->key, pp, ctx->nx); - /* Keep the matching PTE information */ - ctx->raddr = pte1; - ctx->prot = access; - ret = check_prot(ctx->prot, access_type); - if (ret == 0) { - /* Access granted */ - qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); - } else { - /* Access right violation */ - qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); - } - } - } - - return ret; -} - -static int pte_update_flags(mmu_ctx_t *ctx, target_ulong *pte1p, - int ret, MMUAccessType access_type) -{ - int store = 0; - - /* Update page flags */ - if (!(*pte1p & 0x00000100)) { - /* Update accessed flag */ - *pte1p |= 0x00000100; - store = 1; - } - if (!(*pte1p & 0x00000080)) { - if (access_type == MMU_DATA_STORE && ret == 0) { - /* Update changed flag */ - *pte1p |= 0x00000080; - store = 1; - } else { - /* Force page fault for first write access */ - ctx->prot &= ~PAGE_WRITE; - } - } - - return store; -} - /* Software driven TLB helpers */ -static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type) +static int ppc6xx_tlb_check(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong eaddr, MMUAccessType access_type, + target_ulong ptem, bool key, bool nx) { ppc6xx_tlb_t *tlb; - int nr, best, way; - int ret; + target_ulong *pte1p; + int nr, best, way, ret; + bool is_code = (access_type == MMU_INST_FETCH); + /* Initialize real address with an invalid value */ + *raddr = (hwaddr)-1ULL; best = -1; ret = -1; /* No TLB found */ for (way = 0; way < env->nb_ways; way++) { - nr = ppc6xx_tlb_getnum(env, eaddr, way, access_type == MMU_INST_FETCH); + nr = ppc6xx_tlb_getnum(env, eaddr, way, is_code); tlb = &env->tlb.tlb6[nr]; /* This test "emulates" the PTE index match for hardware TLBs */ if ((eaddr & TARGET_PAGE_MASK) != tlb->EPN) { @@ -223,83 +117,87 @@ static int ppc6xx_tlb_check(CPUPPCState *env, mmu_ctx_t *ctx, tlb->EPN, eaddr, tlb->pte1, access_type == MMU_DATA_STORE ? 'S' : 'L', access_type == MMU_INST_FETCH ? 'I' : 'D'); - switch (ppc6xx_tlb_pte_check(ctx, tlb->pte0, tlb->pte1, - 0, access_type)) { - case -3: + /* Check validity and table match */ + if (!pte_is_valid(tlb->pte0) || ((tlb->pte0 >> 6) & 1) != 0 || + (tlb->pte0 & PTE_PTEM_MASK) != ptem) { + continue; + } + /* all matches should have equal RPN, WIMG & PP */ + if (*raddr != (hwaddr)-1ULL && + (*raddr & PTE_CHECK_MASK) != (tlb->pte1 & PTE_CHECK_MASK)) { + qemu_log_mask(CPU_LOG_MMU, "Bad RPN/WIMG/PP\n"); /* TLB inconsistency */ - return -1; - case -2: - /* Access violation */ - ret = -2; - best = nr; - break; - case -1: - default: - /* No match */ - break; - case 0: - /* access granted */ - /* - * XXX: we should go on looping to check all TLBs - * consistency but we can speed-up the whole thing as - * the result would be undefined if TLBs are not - * consistent. - */ + continue; + } + /* Keep the matching PTE information */ + best = nr; + *raddr = tlb->pte1; + *prot = ppc_hash32_prot(key, tlb->pte1 & HPTE32_R_PP, nx); + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "PTE access granted !\n"); ret = 0; - best = nr; - goto done; + break; + } else { + qemu_log_mask(CPU_LOG_MMU, "PTE access rejected\n"); + ret = -2; } } 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); + *raddr & TARGET_PAGE_MASK, *prot, ret); /* Update page flags */ - pte_update_flags(ctx, &env->tlb.tlb6[best].pte1, ret, access_type); - } - - return ret; -} - -/* Perform BAT hit & translation */ -static inline void bat_size_prot(CPUPPCState *env, target_ulong *blp, - int *validp, int *protp, target_ulong *BATu, - target_ulong *BATl) -{ - target_ulong bl; - int pp, valid, prot; - - bl = (*BATu & 0x00001FFC) << 15; - valid = 0; - prot = 0; - if ((!FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000002)) || - (FIELD_EX64(env->msr, MSR, PR) && (*BATu & 0x00000001))) { - valid = 1; - pp = *BATl & 0x00000003; - if (pp != 0) { - prot = PAGE_READ | PAGE_EXEC; - if (pp == 0x2) { - prot |= PAGE_WRITE; + pte1p = &env->tlb.tlb6[best].pte1; + *pte1p |= 0x00000100; /* Update accessed flag */ + if (!(*pte1p & 0x00000080)) { + if (access_type == MMU_DATA_STORE && ret == 0) { + /* Update changed flag */ + *pte1p |= 0x00000080; + } else { + /* Force page fault for first write access */ + *prot &= ~PAGE_WRITE; } } } - *blp = bl; - *validp = valid; - *protp = prot; + if (ret == -1) { + int r = is_code ? SPR_ICMP : SPR_DCMP; + env->spr[r] = ptem; + } +#if defined(DUMP_PAGE_TABLES) + if (qemu_loglevel_mask(CPU_LOG_MMU)) { + CPUState *cs = env_cpu(env); + hwaddr base = ppc_hash32_hpt_base(env_archcpu(env)); + hwaddr len = ppc_hash32_hpt_mask(env_archcpu(env)) + 0x80; + uint32_t a0, a1, a2, a3; + + qemu_log("Page table: " HWADDR_FMT_plx " len " HWADDR_FMT_plx "\n", + base, len); + for (hwaddr curaddr = base; curaddr < base + len; curaddr += 16) { + a0 = ldl_phys(cs->as, curaddr); + a1 = ldl_phys(cs->as, curaddr + 4); + 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(HWADDR_FMT_plx ": %08x %08x %08x %08x\n", + curaddr, a0, a1, a2, a3); + } + } + } +#endif + return ret; } -static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong virtual, MMUAccessType access_type) +static int get_bat_6xx_tlb(CPUPPCState *env, hwaddr *raddr, int *prot, + target_ulong eaddr, MMUAccessType access_type, + bool pr) { target_ulong *BATlt, *BATut, *BATu, *BATl; target_ulong BEPIl, BEPIu, bl; - int i, valid, prot; - int ret = -1; + int i, ret = -1; bool ifetch = access_type == MMU_INST_FETCH; - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', virtual); + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT v " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', eaddr); if (ifetch) { BATlt = env->IBAT[1]; BATut = env->IBAT[0]; @@ -310,28 +208,29 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bat_size_prot(env, &bl, &valid, &prot, BATu, BATl); - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " - TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, - ifetch ? 'I' : 'D', i, virtual, *BATu, *BATl); - if ((virtual & 0xF0000000) == BEPIu && - ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { - /* BAT matches */ - if (valid != 0) { + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx " BATu " + TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, + ifetch ? 'I' : 'D', i, eaddr, *BATu, *BATl); + bl = (*BATu & BATU32_BL) << 15; + if ((!pr && (*BATu & BATU32_VS)) || (pr && (*BATu & BATU32_VP))) { + if ((eaddr & BATU32_BEPIU) == BEPIu && + ((eaddr & BATU32_BEPIL) & ~bl) == BEPIl) { /* Get physical address */ - ctx->raddr = (*BATl & 0xF0000000) | - ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | - (virtual & 0x0001F000); + *raddr = (*BATl & BATU32_BEPIU) | + ((eaddr & BATU32_BEPIL & bl) | (*BATl & BATU32_BEPIL)) | + (eaddr & 0x0001F000); /* Compute access rights */ - 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 - " prot=%c%c\n", i, ctx->raddr, - ctx->prot & PAGE_READ ? 'R' : '-', - ctx->prot & PAGE_WRITE ? 'W' : '-'); + *prot = ppc_hash32_bat_prot(*BATu, *BATl); + if (check_prot_access_type(*prot, access_type)) { + qemu_log_mask(CPU_LOG_MMU, "BAT %d match: r " HWADDR_FMT_plx + " prot=%c%c\n", i, *raddr, + *prot & PAGE_READ ? 'R' : '-', + *prot & PAGE_WRITE ? 'W' : '-'); + ret = 0; + } else { + ret = -2; } break; } @@ -340,19 +239,18 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, if (ret < 0) { if (qemu_log_enabled()) { qemu_log_mask(CPU_LOG_MMU, "no BAT match for " - TARGET_FMT_lx ":\n", virtual); + TARGET_FMT_lx ":\n", eaddr); for (i = 0; i < 4; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; - qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " - TARGET_FMT_lx " BATu " TARGET_FMT_lx - " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " - TARGET_FMT_lx " " TARGET_FMT_lx "\n", - __func__, ifetch ? 'I' : 'D', i, virtual, - *BATu, *BATl, BEPIu, BEPIl, bl); + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & BATU32_BL) << 15; + qemu_log_mask(CPU_LOG_MMU, "%s: %cBAT%d v " TARGET_FMT_lx + " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx + "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " + TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', + i, eaddr, *BATu, *BATl, BEPIu, BEPIl, bl); } } } @@ -360,29 +258,30 @@ static int get_bat_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -/* Perform segment based translation */ -static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, MMUAccessType access_type, - int type) +static int mmu6xx_get_physical_address(CPUPPCState *env, hwaddr *raddr, + int *prot, target_ulong eaddr, + hwaddr *hashp, bool *keyp, + MMUAccessType access_type, int type) { PowerPCCPU *cpu = env_archcpu(env); hwaddr hash; - target_ulong vsid; - int ds, target_page_bits; - bool pr; - int ret; - target_ulong sr, pgidx; + target_ulong vsid, sr, pgidx, ptem; + bool key, ds, nx; + bool pr = FIELD_EX64(env->msr, MSR, PR); - pr = FIELD_EX64(env->msr, MSR, PR); - ctx->eaddr = eaddr; + /* First try to find a BAT entry if there are any */ + if (env->nb_BATs && + get_bat_6xx_tlb(env, raddr, prot, eaddr, access_type, pr) == 0) { + return 0; + } + /* Perform segment based translation when no BATs matched */ sr = env->sr[eaddr >> 28]; - ctx->key = (((sr & 0x20000000) && pr) || - ((sr & 0x40000000) && !pr)) ? 1 : 0; - ds = sr & 0x80000000 ? 1 : 0; - ctx->nx = sr & 0x10000000 ? 1 : 0; - vsid = sr & 0x00FFFFFF; - target_page_bits = TARGET_PAGE_BITS; + key = ppc_hash32_key(pr, sr); + *keyp = key; + ds = sr & SR32_T; + nx = sr & SR32_NX; + vsid = sr & SR32_VSID; qemu_log_mask(CPU_LOG_MMU, "Check segment v=" TARGET_FMT_lx " %d " TARGET_FMT_lx " nip=" TARGET_FMT_lx " lr=" TARGET_FMT_lx @@ -391,517 +290,56 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, (int)FIELD_EX64(env->msr, MSR, IR), (int)FIELD_EX64(env->msr, MSR, DR), pr ? 1 : 0, access_type == MMU_DATA_STORE, type); - pgidx = (eaddr & ~SEGMENT_MASK_256M) >> target_page_bits; + pgidx = (eaddr & ~SEGMENT_MASK_256M) >> TARGET_PAGE_BITS; hash = vsid ^ pgidx; - ctx->ptem = (vsid << 7) | (pgidx >> 10); + ptem = (vsid << 7) | (pgidx >> 10); /* Virtual segment ID | API */ - qemu_log_mask(CPU_LOG_MMU, - "pte segment: key=%d ds %d nx %d vsid " TARGET_FMT_lx "\n", - ctx->key, ds, ctx->nx, vsid); - ret = -1; + qemu_log_mask(CPU_LOG_MMU, "pte segment: key=%d ds %d nx %d vsid " + TARGET_FMT_lx "\n", key, ds, nx, vsid); if (!ds) { /* 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", - ppc_hash32_hpt_base(cpu), ppc_hash32_hpt_mask(cpu), hash); - ctx->hash[0] = hash; - ctx->hash[1] = ~hash; - - /* Initialize real address with an invalid value */ - ctx->raddr = (hwaddr)-1ULL; - /* Software TLB search */ - ret = ppc6xx_tlb_check(env, ctx, eaddr, access_type); -#if defined(DUMP_PAGE_TABLES) - if (qemu_loglevel_mask(CPU_LOG_MMU)) { - CPUState *cs = env_cpu(env); - hwaddr curaddr; - uint32_t a0, a1, a2, a3; - - qemu_log("Page table: " TARGET_FMT_plx " len " TARGET_FMT_plx - "\n", ppc_hash32_hpt_base(cpu), - ppc_hash32_hpt_mask(cpu) + 0x80); - for (curaddr = ppc_hash32_hpt_base(cpu); - curaddr < (ppc_hash32_hpt_base(cpu) - + ppc_hash32_hpt_mask(cpu) + 0x80); - curaddr += 16) { - a0 = ldl_phys(cs->as, curaddr); - a1 = ldl_phys(cs->as, curaddr + 4); - 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", - curaddr, a0, a1, a2, a3); - } - } - } -#endif - } else { + if (type == ACCESS_CODE && nx) { qemu_log_mask(CPU_LOG_MMU, "No access allowed\n"); - ret = -3; + return -3; } - } else { - qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); - /* Direct-store segment : absolutely *BUGGY* for now */ + /* Page address translation */ + 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); + *hashp = hash; - switch (type) { - case ACCESS_INT: - /* Integer load/store : only access allowed */ - break; - case ACCESS_CODE: - /* No code fetch is allowed in direct-store areas */ - return -4; - case ACCESS_FLOAT: - /* Floating point load/store */ - return -4; - case ACCESS_RES: - /* lwarx, ldarx or srwcx. */ - return -4; - case ACCESS_CACHE: - /* - * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi - * - * Should make the instruction do no-op. As it already do - * no-op, it's quite easy :-) - */ - ctx->raddr = eaddr; - return 0; - case ACCESS_EXT: - /* eciwx or ecowx */ - return -4; - default: - qemu_log_mask(CPU_LOG_MMU, "ERROR: instruction should not need " - "address translation\n"); - return -4; - } - if ((access_type == MMU_DATA_STORE || ctx->key != 1) && - (access_type == MMU_DATA_LOAD || ctx->key != 0)) { - ctx->raddr = eaddr; - ret = 2; - } else { - ret = -2; - } + /* Software TLB search */ + return ppc6xx_tlb_check(env, raddr, prot, eaddr, + access_type, ptem, key, nx); } - return ret; -} - -/* 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) -{ - target_ulong mask; - - /* Check valid flag */ - if (!(tlb->prot & PAGE_VALID)) { - return -1; - } - mask = ~(tlb->size - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB %d address " TARGET_FMT_lx - " PID %u <=> " TARGET_FMT_lx " " TARGET_FMT_lx " %u %x\n", - __func__, i, address, pid, tlb->EPN, - mask, (uint32_t)tlb->PID, tlb->prot); - /* Check PID */ - if (tlb->PID != 0 && tlb->PID != pid) { - return -1; - } - /* Check effective address */ - if ((address & mask) != tlb->EPN) { - return -1; - } - *raddrp = (tlb->RPN & mask) | (address & ~mask); - if (ext) { - /* Extend the physical address to 36 bits */ - *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; - } - - return 0; -} - -static int mmu40x_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret, zsel, zpr, pr; - - ret = -1; - raddr = (hwaddr)-1ULL; - 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) { - continue; - } - zsel = (tlb->attr >> 4) & 0xF; - zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; - qemu_log_mask(CPU_LOG_MMU, - "%s: TLB %d zsel %d zpr %d ty %d attr %08x\n", - __func__, i, zsel, zpr, access_type, tlb->attr); - /* Check execute enable bit */ - switch (zpr) { - case 0x2: - if (pr != 0) { - goto check_perms; - } - /* fall through */ - case 0x3: - /* All accesses granted */ - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - ret = 0; - break; - case 0x0: - if (pr != 0) { - /* Raise Zone protection fault. */ - env->spr[SPR_40x_ESR] = 1 << 22; - ctx->prot = 0; - ret = -2; - break; - } - /* fall through */ - case 0x1: - check_perms: - /* Check from TLB entry */ - ctx->prot = tlb->prot; - ret = check_prot(ctx->prot, access_type); - if (ret == -2) { - env->spr[SPR_40x_ESR] = 0; - } - break; - } - 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, ctx->raddr, ctx->prot, - ret); - return 0; - } - } - qemu_log_mask(CPU_LOG_MMU, "%s: access refused " TARGET_FMT_lx - " => " TARGET_FMT_plx - " %d %d\n", __func__, address, raddr, ctx->prot, ret); - - return ret; -} - -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 (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 { - prot2 = (tlb->prot >> 4) & 0xF; - } - - /* Check the address space */ - if ((access_type == MMU_INST_FETCH ? - FIELD_EX64(env->msr, MSR, IR) : - FIELD_EX64(env->msr, MSR, DR)) != (tlb->attr & 1)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { - qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); + /* Direct-store segment : absolutely *BUGGY* for now */ + qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CACHE: + /* + * dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi + * + * Should make the instruction do no-op. As it already do + * no-op, it's quite easy :-) + */ + *raddr = eaddr; return 0; + case ACCESS_CODE: /* No code fetch is allowed in direct-store areas */ + case ACCESS_FLOAT: /* Floating point load/store */ + case ACCESS_RES: /* lwarx, ldarx or srwcx. */ + case ACCESS_EXT: /* eciwx or ecowx */ + return -4; } - - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, - MMUAccessType access_type) -{ - ppcemb_tlb_t *tlb; - hwaddr raddr; - int i, ret; - - ret = -1; - raddr = (hwaddr)-1ULL; - for (i = 0; i < env->nb_tlb; i++) { - tlb = &env->tlb.tlbe[i]; - ret = mmubooke_check_tlb(env, tlb, &raddr, &ctx->prot, address, - access_type, i); - if (ret != -1) { - break; - } + if ((access_type == MMU_DATA_STORE || !key) && + (access_type == MMU_DATA_LOAD || key)) { + *raddr = eaddr; + return 2; } - - 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, 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, raddr, ctx->prot, ret); - } - - return ret; -} - -hwaddr booke206_tlb_to_page_size(CPUPPCState *env, - ppcmas_tlb_t *tlb) -{ - int tlbm_size; - - tlbm_size = (tlb->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; - - return 1024ULL << tlbm_size; -} - -/* 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) -{ - hwaddr mask; - uint32_t tlb_pid; - - if (!FIELD_EX64(env->msr, MSR, CM)) { - /* In 32bit mode we can only address 32bit EAs */ - address = (uint32_t)address; - } - - /* Check valid flag */ - if (!(tlb->mas1 & MAS1_VALID)) { - return -1; - } - - mask = ~(booke206_tlb_to_page_size(env, tlb) - 1); - qemu_log_mask(CPU_LOG_MMU, "%s: TLB ADDR=0x" TARGET_FMT_lx - " PID=0x%x MAS1=0x%x MAS2=0x%" PRIx64 " mask=0x%" - HWADDR_PRIx " MAS7_3=0x%" PRIx64 " MAS8=0x%" PRIx32 "\n", - __func__, address, pid, tlb->mas1, tlb->mas2, mask, - tlb->mas7_3, tlb->mas8); - - /* Check PID */ - tlb_pid = (tlb->mas1 & MAS1_TID_MASK) >> MAS1_TID_SHIFT; - if (tlb_pid != 0 && tlb_pid != pid) { - return -1; - } - - /* Check effective address */ - if ((address & mask) != (tlb->mas2 & MAS2_EPN_MASK)) { - return -1; - } - - if (raddrp) { - *raddrp = (tlb->mas7_3 & mask) | (address & ~mask); - } - - return 0; -} - -static bool is_epid_mmu(int mmu_idx) -{ - return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; -} - -static uint32_t mmubooke206_esr(int mmu_idx, MMUAccessType access_type) -{ - uint32_t esr = 0; - if (access_type == MMU_DATA_STORE) { - esr |= ESR_ST; - } - if (is_epid_mmu(mmu_idx)) { - esr |= ESR_EPID; - } - return esr; -} - -/* - * Get EPID register given the mmu_idx. If this is regular load, - * construct the EPID access bits from current processor state - * - * Get the effective AS and PR bits and the PID. The PID is returned - * only if EPID load is requested, otherwise the caller must detect - * the correct EPID. Return true if valid EPID is returned. - */ -static bool mmubooke206_get_as(CPUPPCState *env, - int mmu_idx, uint32_t *epid_out, - bool *as_out, bool *pr_out) -{ - if (is_epid_mmu(mmu_idx)) { - uint32_t epidr; - if (mmu_idx == PPC_TLB_EPID_STORE) { - epidr = env->spr[SPR_BOOKE_EPSC]; - } else { - epidr = env->spr[SPR_BOOKE_EPLC]; - } - *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; - *as_out = !!(epidr & EPID_EAS); - *pr_out = !!(epidr & EPID_EPR); - return true; - } else { - *as_out = FIELD_EX64(env->msr, MSR, DS); - *pr_out = FIELD_EX64(env->msr, MSR, PR); - return false; - } -} - -/* Check if the tlb found by hashing really matches */ -static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, - hwaddr *raddr, int *prot, - target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int prot2 = 0; - uint32_t epid; - bool as, pr; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (!use_epid) { - if (ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID1] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1]) >= 0) { - goto found_tlb; - } - - if (env->spr[SPR_BOOKE_PID2] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2]) >= 0) { - goto found_tlb; - } - } else { - if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { - goto found_tlb; - } - } - - qemu_log_mask(CPU_LOG_MMU, "%s: TLB entry not found\n", __func__); - return -1; - -found_tlb: - - if (pr) { - if (tlb->mas7_3 & MAS3_UR) { - prot2 |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_UW) { - prot2 |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_UX) { - prot2 |= PAGE_EXEC; - } - } else { - if (tlb->mas7_3 & MAS3_SR) { - prot2 |= PAGE_READ; - } - if (tlb->mas7_3 & MAS3_SW) { - prot2 |= PAGE_WRITE; - } - if (tlb->mas7_3 & MAS3_SX) { - prot2 |= PAGE_EXEC; - } - } - - /* Check the address space and permissions */ - if (access_type == MMU_INST_FETCH) { - /* There is no way to fetch code using epid load */ - assert(!use_epid); - as = FIELD_EX64(env->msr, MSR, IR); - } - - if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { - qemu_log_mask(CPU_LOG_MMU, "%s: AS doesn't match\n", __func__); - return -1; - } - - *prot = prot2; - if (prot2 & prot_for_access_type(access_type)) { - qemu_log_mask(CPU_LOG_MMU, "%s: good TLB!\n", __func__); - return 0; - } - - qemu_log_mask(CPU_LOG_MMU, "%s: no prot match: %x\n", __func__, prot2); - return access_type == MMU_INST_FETCH ? -3 : -2; -} - -static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong address, - MMUAccessType access_type, - int mmu_idx) -{ - ppcmas_tlb_t *tlb; - hwaddr raddr; - int i, j, ret; - - ret = -1; - raddr = (hwaddr)-1ULL; - - for (i = 0; i < BOOKE206_MAX_TLBN; i++) { - int ways = booke206_tlb_ways(env, i); - - for (j = 0; j < ways; j++) { - tlb = booke206_get_tlbm(env, i, address, j); - if (!tlb) { - continue; - } - ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, - access_type, mmu_idx); - if (ret != -1) { - goto found_tlb; - } - } - } - -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, - 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, - raddr, ctx->prot, ret); - } - - return ret; + return -2; } static const char *book3e_tsize_to_str[32] = { @@ -916,10 +354,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 +419,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 +447,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); @@ -1044,9 +486,9 @@ static void mmu6xx_dump_BATs(CPUPPCState *env, int type) for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; - BEPIu = *BATu & 0xF0000000; - BEPIl = *BATu & 0x0FFE0000; - bl = (*BATu & 0x00001FFC) << 15; + BEPIu = *BATu & BATU32_BEPIU; + BEPIl = *BATu & BATU32_BEPIL; + bl = (*BATu & BATU32_BL) << 15; qemu_printf("%s BAT%d BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n\t" TARGET_FMT_lx " " TARGET_FMT_lx " " TARGET_FMT_lx "\n", @@ -1086,13 +528,7 @@ static void mmu6xx_dump_mmu(CPUPPCState *env) mmu6xx_dump_BATs(env, ACCESS_INT); mmu6xx_dump_BATs(env, ACCESS_CODE); - if (env->id_tlbs != 1) { - qemu_printf("ERROR: 6xx MMU should have separated TLB" - " for code and data\n"); - } - qemu_printf("\nTLBs [EPN EPN + SIZE]\n"); - for (type = 0; type < 2; type++) { for (way = 0; way < env->nb_ways; way++) { for (entry = env->nb_tlb * type + env->tlb_per_way * way; @@ -1144,154 +580,108 @@ void dump_mmu(CPUPPCState *env) } } -static int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong eaddr, - MMUAccessType access_type) + +static bool ppc_real_mode_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp) { - ctx->raddr = eaddr; - ctx->prot = PAGE_READ | PAGE_EXEC; + CPUPPCState *env = &cpu->env; - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - case POWERPC_MMU_SOFT_4xx: - case POWERPC_MMU_REAL: - case POWERPC_MMU_BOOKE: - ctx->prot |= PAGE_WRITE; - break; - - default: - /* Caller's checks mean we should never get here for other models */ - g_assert_not_reached(); + if (access_type == MMU_INST_FETCH ? !FIELD_EX64(env->msr, MSR, IR) + : !FIELD_EX64(env->msr, MSR, DR)) { + *raddrp = eaddr; + *protp = PAGE_RWX; + *psizep = TARGET_PAGE_BITS; + return true; + } else if (env->mmu_model == POWERPC_MMU_REAL) { + cpu_abort(CPU(cpu), "PowerPC in real mode shold not do translation\n"); } - - return 0; + return false; } -int get_physical_address_wtlb(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, - MMUAccessType access_type, int type, - int mmu_idx) -{ - int ret = -1; - bool real_mode = (type == ACCESS_CODE && !FIELD_EX64(env->msr, MSR, IR)) || - (type != ACCESS_CODE && !FIELD_EX64(env->msr, MSR, DR)); - - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - /* Try to find a BAT */ - if (env->nb_BATs != 0) { - ret = get_bat_6xx_tlb(env, ctx, eaddr, access_type); - } - if (ret < 0) { - /* We didn't match any BAT entry or don't have BATs */ - ret = get_segment_6xx_tlb(env, ctx, eaddr, access_type, type); - } - } - break; - - case POWERPC_MMU_SOFT_4xx: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - ret = mmu40x_get_physical_address(env, ctx, eaddr, access_type); - } - break; - case POWERPC_MMU_BOOKE: - ret = mmubooke_get_physical_address(env, ctx, eaddr, access_type); - break; - case POWERPC_MMU_BOOKE206: - ret = mmubooke206_get_physical_address(env, ctx, eaddr, access_type, - mmu_idx); - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(env_cpu(env), "MPC8xx MMU model is not implemented\n"); - break; - case POWERPC_MMU_REAL: - if (real_mode) { - ret = check_physical(env, ctx, eaddr, access_type); - } else { - cpu_abort(env_cpu(env), - "PowerPC in real mode do not do any translation\n"); - } - return -1; - default: - cpu_abort(env_cpu(env), "Unknown or invalid MMU model\n"); - return -1; - } - - return ret; -} - -static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - uint32_t epid; - bool as, pr; - uint32_t missed_tid = 0; - bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - - if (access_type == MMU_INST_FETCH) { - as = FIELD_EX64(env->msr, MSR, IR); - } - env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; - env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; - env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; - env->spr[SPR_BOOKE_MAS3] = 0; - env->spr[SPR_BOOKE_MAS6] = 0; - env->spr[SPR_BOOKE_MAS7] = 0; - - /* AS */ - if (as) { - env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; - env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; - } - - env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; - env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; - - if (!use_epid) { - switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { - case MAS4_TIDSELD_PID0: - missed_tid = env->spr[SPR_BOOKE_PID]; - break; - case MAS4_TIDSELD_PID1: - missed_tid = env->spr[SPR_BOOKE_PID1]; - break; - case MAS4_TIDSELD_PID2: - missed_tid = env->spr[SPR_BOOKE_PID2]; - break; - } - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; - } else { - missed_tid = epid; - env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; - } - env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); - - - /* next victim logic */ - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; - env->last_way++; - env->last_way &= booke206_tlb_ways(env, 0) - 1; - env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_NV_SHIFT; -} - -/* Perform address translation */ -/* TODO: Split this by mmu_model. */ -static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, - MMUAccessType access_type, - hwaddr *raddrp, int *psizep, int *protp, - int mmu_idx, bool guest_visible) +static bool ppc_40x_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - mmu_ctx_t ctx; - int type; int ret; + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; + } + + ret = mmu40x_get_physical_address(env, raddrp, protp, eaddr, access_type); + if (ret == 0) { + *psizep = TARGET_PAGE_BITS; + return true; + } else if (!guest_visible) { + return false; + } + + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (access_type == MMU_INST_FETCH) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + env->spr[SPR_40x_ESR] = 0x00000000; + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + default: + g_assert_not_reached(); + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] = 0x00800000; + } else { + env->spr[SPR_40x_ESR] = 0x00000000; + } + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_40x_DEAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + break; + default: + g_assert_not_reached(); + } + } + return false; +} + +static bool ppc_6xx_xlate(PowerPCCPU *cpu, vaddr eaddr, + MMUAccessType access_type, + hwaddr *raddrp, int *psizep, int *protp, + int mmu_idx, bool guest_visible) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + hwaddr hash = 0; /* init to 0 to avoid used uninit warning */ + bool key; + int type, ret; + + if (ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, protp)) { + return true; + } + if (access_type == MMU_INST_FETCH) { /* code access */ type = ACCESS_CODE; @@ -1302,199 +692,112 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, type = ACCESS_INT; } - ret = get_physical_address_wtlb(env, &ctx, eaddr, access_type, - type, mmu_idx); + ret = mmu6xx_get_physical_address(env, raddrp, protp, eaddr, &hash, &key, + access_type, type); if (ret == 0) { - *raddrp = ctx.raddr; - *protp = ctx.prot; *psizep = TARGET_PAGE_BITS; return true; + } else if (!guest_visible) { + return false; } - if (guest_visible) { - log_cpu_state_mask(CPU_LOG_MMU, cs, 0); - if (type == ACCESS_CODE) { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - cs->exception_index = POWERPC_EXCP_IFTLB; - env->error_code = 1 << 18; - env->spr[SPR_IMISS] = eaddr; - env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; - goto tlb_miss; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - env->spr[SPR_40x_ESR] = 0x00000000; - break; - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, 2, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_ITLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, MMU_DATA_LOAD); - break; - case POWERPC_MMU_MPC8xx: - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); - } - break; - case -2: - /* Access rights violation */ - cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x08000000; - } - break; - case -3: - /* No execute protection violation */ - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_ESR] = 0x00000000; - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } - cs->exception_index = POWERPC_EXCP_ISI; - break; - case -4: - /* Direct store exception */ - /* No code fetch is allowed in direct-store areas */ - cs->exception_index = POWERPC_EXCP_ISI; - if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->error_code = 0; - } else { - env->error_code = 0x10000000; - } - break; + log_cpu_state_mask(CPU_LOG_MMU, cs, 0); + if (type == ACCESS_CODE) { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + cs->exception_index = POWERPC_EXCP_IFTLB; + env->error_code = 1 << 18; + env->spr[SPR_IMISS] = eaddr; + env->spr[SPR_ICMP] |= 0x80000000; + goto tlb_miss; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x08000000; + break; + case -3: + /* No execute protection violation */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + cs->exception_index = POWERPC_EXCP_ISI; + env->error_code = 0x10000000; + break; + } + } else { + switch (ret) { + case -1: + /* No matches in page tables or TLB */ + if (access_type == MMU_DATA_STORE) { + cs->exception_index = POWERPC_EXCP_DSTLB; + env->error_code = 1 << 16; + } else { + cs->exception_index = POWERPC_EXCP_DLTLB; + env->error_code = 0; } - } else { - switch (ret) { - case -1: - /* No matches in page tables or TLB */ - switch (env->mmu_model) { - case POWERPC_MMU_SOFT_6xx: - if (access_type == MMU_DATA_STORE) { - cs->exception_index = POWERPC_EXCP_DSTLB; - env->error_code = 1 << 16; - } else { - cs->exception_index = POWERPC_EXCP_DLTLB; - env->error_code = 0; - } - env->spr[SPR_DMISS] = eaddr; - env->spr[SPR_DCMP] = 0x80000000 | ctx.ptem; - tlb_miss: - env->error_code |= ctx.key << 19; - env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[0]); - env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + - get_pteg_offset32(cpu, ctx.hash[1]); - break; - case POWERPC_MMU_SOFT_4xx: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] = 0x00800000; - } else { - env->spr[SPR_40x_ESR] = 0x00000000; - } - break; - case POWERPC_MMU_MPC8xx: - /* XXX: TODO */ - cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); - case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, eaddr, access_type, mmu_idx); - /* fall through */ - case POWERPC_MMU_BOOKE: - cs->exception_index = POWERPC_EXCP_DTLB; - env->error_code = 0; - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); - break; - case POWERPC_MMU_REAL: - cpu_abort(cs, "PowerPC in real mode should never raise " - "any MMU exceptions\n"); - default: - cpu_abort(cs, "Unknown or invalid MMU model\n"); - } + env->spr[SPR_DMISS] = eaddr; + env->spr[SPR_DCMP] |= 0x80000000; +tlb_miss: + env->error_code |= key << 19; + env->spr[SPR_HASH1] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, hash); + env->spr[SPR_HASH2] = ppc_hash32_hpt_base(cpu) + + get_pteg_offset32(cpu, ~hash); + break; + case -2: + /* Access rights violation */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + break; + case -4: + /* Direct store exception */ + switch (type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = POWERPC_EXCP_ALIGN_FP; + env->spr[SPR_DAR] = eaddr; break; - case -2: - /* Access rights violation */ + case ACCESS_RES: + /* lwarx, ldarx or stwcx. */ cs->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - if (env->mmu_model == POWERPC_MMU_SOFT_4xx) { - env->spr[SPR_40x_DEAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_40x_ESR] |= 0x00800000; - } - } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || - (env->mmu_model == POWERPC_MMU_BOOKE206)) { - env->spr[SPR_BOOKE_DEAR] = eaddr; - env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, access_type); + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06000000; } else { - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x0A000000; - } else { - env->spr[SPR_DSISR] = 0x08000000; - } + env->spr[SPR_DSISR] = 0x04000000; } break; - case -4: - /* Direct store exception */ - switch (type) { - case ACCESS_FLOAT: - /* Floating point load/store */ - cs->exception_index = POWERPC_EXCP_ALIGN; - env->error_code = POWERPC_EXCP_ALIGN_FP; - env->spr[SPR_DAR] = eaddr; - break; - case ACCESS_RES: - /* lwarx, ldarx or stwcx. */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06000000; - } else { - env->spr[SPR_DSISR] = 0x04000000; - } - break; - case ACCESS_EXT: - /* eciwx or ecowx */ - cs->exception_index = POWERPC_EXCP_DSI; - env->error_code = 0; - env->spr[SPR_DAR] = eaddr; - if (access_type == MMU_DATA_STORE) { - env->spr[SPR_DSISR] = 0x06100000; - } else { - env->spr[SPR_DSISR] = 0x04100000; - } - break; - default: - printf("DSI: invalid exception (%d)\n", ret); - cs->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = - POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; - env->spr[SPR_DAR] = eaddr; - break; + case ACCESS_EXT: + /* eciwx or ecowx */ + cs->exception_index = POWERPC_EXCP_DSI; + env->error_code = 0; + env->spr[SPR_DAR] = eaddr; + if (access_type == MMU_DATA_STORE) { + env->spr[SPR_DSISR] = 0x06100000; + } else { + env->spr[SPR_DSISR] = 0x04100000; } break; + default: + printf("DSI: invalid exception (%d)\n", ret); + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; + env->spr[SPR_DAR] = eaddr; + break; } + break; } } return false; @@ -1525,10 +828,23 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, case POWERPC_MMU_32B: return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); - - default: - return ppc_jumbo_xlate(cpu, eaddr, access_type, raddrp, + case POWERPC_MMU_BOOKE: + case POWERPC_MMU_BOOKE206: + return ppc_booke_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_SOFT_4xx: + return ppc_40x_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_SOFT_6xx: + return ppc_6xx_xlate(cpu, eaddr, access_type, raddrp, + psizep, protp, mmu_idx, guest_visible); + case POWERPC_MMU_REAL: + return ppc_real_mode_xlate(cpu, eaddr, access_type, raddrp, psizep, + protp); + case POWERPC_MMU_MPC8xx: + cpu_abort(env_cpu(&cpu->env), "MPC8xx MMU model is not implemented\n"); + default: + cpu_abort(CPU(cpu), "Unknown or invalid MMU model\n"); } } @@ -1544,9 +860,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 2a91f3f46a..b167b37e0a 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -25,14 +25,15 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #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" #include "mmu-radix64.h" +#include "mmu-booke.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" @@ -45,14 +46,8 @@ static inline void ppc6xx_tlb_invalidate_all(CPUPPCState *env) { ppc6xx_tlb_t *tlb; - int nr, max; + int nr, max = 2 * env->nb_tlb; - /* LOG_SWTLB("Invalidate all TLBs\n"); */ - /* Invalidate all defined software TLB */ - max = env->nb_tlb; - if (env->id_tlbs == 1) { - max *= 2; - } for (nr = 0; nr < max; nr++) { tlb = &env->tlb.tlb6[nr]; pte_invalidate(&tlb->pte0); @@ -112,27 +107,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 +142,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) @@ -338,9 +303,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: ppc6xx_tlb_invalidate_virt(env, addr, 0); - if (env->id_tlbs == 1) { - ppc6xx_tlb_invalidate_virt(env, addr, 1); - } + ppc6xx_tlb_invalidate_virt(env, addr, 1); break; case POWERPC_MMU_32B: /* @@ -353,7 +316,7 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) break; default: /* Should never reach here with other MMU models */ - assert(0); + g_assert_not_reached(); } #else ppc_tlb_invalidate_all(env); @@ -564,7 +527,7 @@ void helper_tlbie_isa300(CPUPPCState *env, target_ulong rb, target_ulong rs, if (local) { tlb_flush_page(env_cpu(env), addr); } else { - tlb_flush_page_all_cpus(env_cpu(env), addr); + tlb_flush_page_all_cpus_synced(env_cpu(env), addr); } return; @@ -626,30 +589,6 @@ void helper_6xx_tlbi(CPUPPCState *env, target_ulong EPN) do_6xx_tlb(env, EPN, 1); } -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ - -target_ulong helper_rac(CPUPPCState *env, target_ulong addr) -{ - mmu_ctx_t ctx; - int nb_BATs; - target_ulong ret = 0; - - /* - * We don't have to generate many instances of this instruction, - * as rac is supervisor only. - * - * XXX: FIX THIS: Pretend we have no BAT - */ - nb_BATs = env->nb_BATs; - env->nb_BATs = 0; - if (get_physical_address(env, &ctx, addr, 0, ACCESS_INT) == 0) { - ret = ctx.raddr; - } - env->nb_BATs = nb_BATs; - return ret; -} - static inline target_ulong booke_tlb_to_page_size(int size) { return 1024 << (2 * size); @@ -780,12 +719,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, @@ -793,14 +749,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); @@ -826,7 +779,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, @@ -834,27 +787,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; @@ -864,7 +815,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, @@ -872,8 +823,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) @@ -881,54 +830,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/monitor.c b/target/ppc/monitor.c deleted file mode 100644 index 8250b1304e..0000000000 --- a/target/ppc/monitor.c +++ /dev/null @@ -1,174 +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 "cpu.h" -#include "monitor/monitor.h" -#include "qemu/ctype.h" -#include "monitor/hmp-target.h" -#include "monitor/hmp.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))); - } - - return u; -} - -static target_long monitor_get_xer(Monitor *mon, const struct MonitorDef *md, - int val) -{ - CPUArchState *env = mon_get_cpu_env(mon); - return cpu_read_xer(env); -} - -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); -} - -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); -} - -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); -} - -void hmp_info_tlb(Monitor *mon, const QDict *qdict) -{ - CPUArchState *env1 = mon_get_cpu_env(mon); - - if (!env1) { - monitor_printf(mon, "No CPU available\n"); - return; - } - dump_mmu(env1); -} - -const MonitorDef monitor_defs[] = { - { "fpscr", offsetof(CPUPPCState, fpscr) }, - /* Next instruction pointer */ - { "nip|pc", offsetof(CPUPPCState, nip) }, - { "lr", offsetof(CPUPPCState, lr) }, - { "ctr", offsetof(CPUPPCState, ctr) }, - { "decr", 0, &monitor_get_decr, }, - { "ccr|cr", 0, &monitor_get_ccr, }, - /* Machine state register */ - { "xer", 0, &monitor_get_xer }, - { "msr", offsetof(CPUPPCState, msr) }, - { "tbu", 0, &monitor_get_tbu, }, - { "tbl", 0, &monitor_get_tbl, }, - { NULL }, -}; - -const MonitorDef *target_monitor_defs(void) -{ - return monitor_defs; -} - -static int ppc_cpu_get_reg_num(const char *numstr, int maxnum, int *pregnum) -{ - int regnum; - char *endptr = NULL; - - if (!*numstr) { - return false; - } - - regnum = strtoul(numstr, &endptr, 10); - if (*endptr || (regnum >= maxnum)) { - return false; - } - *pregnum = regnum; - - return true; -} - -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; - - /* General purpose registers */ - if ((qemu_tolower(name[0]) == 'r') && - ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->gpr), ®num)) { - *pval = env->gpr[regnum]; - return 0; - } - - /* Floating point registers */ - if ((qemu_tolower(name[0]) == 'f') && - ppc_cpu_get_reg_num(name + 1, 32, ®num)) { - *pval = *cpu_fpr_ptr(env, regnum); - return 0; - } - - /* Special purpose registers */ - for (i = 0; i < ARRAY_SIZE(env->spr_cb); ++i) { - ppc_spr_t *spr = &env->spr_cb[i]; - - if (spr->name && (strcasecmp(name, spr->name) == 0)) { - *pval = env->spr[i]; - return 0; - } - } - - /* Segment registers */ -#if !defined(CONFIG_USER_ONLY) - if ((strncasecmp(name, "sr", 2) == 0) && - ppc_cpu_get_reg_num(name + 2, ARRAY_SIZE(env->sr), ®num)) { - *pval = env->sr[regnum]; - return 0; - } -#endif - - return -EINVAL; -} diff --git a/target/ppc/power8-pmu-regs.c.inc b/target/ppc/power8-pmu-regs.c.inc index c3cc919ee4..652cf20704 100644 --- a/target/ppc/power8-pmu-regs.c.inc +++ b/target/ppc/power8-pmu-regs.c.inc @@ -16,7 +16,7 @@ * 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) @@ -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. @@ -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,19 @@ 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_write_MMCRA(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_mmcrA(tcg_env, cpu_gpr[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) @@ -224,12 +215,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) @@ -264,8 +253,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 fccd011088..db9ee8e96b 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -16,7 +16,7 @@ #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" @@ -82,10 +82,52 @@ static void pmu_update_summaries(CPUPPCState *env) env->pmc_cyc_cnt = cyc_cnt; } -void pmu_mmcr01_updated(CPUPPCState *env) +static void hreg_bhrb_filter_update(CPUPPCState *env) { + target_long ifm; + + if (!(env->spr[SPR_POWER_MMCR0] & MMCR0_PMAE)) { + /* disable recording to BHRB */ + env->bhrb_filter = BHRB_TYPE_NORECORD; + return; + } + + ifm = (env->spr[SPR_POWER_MMCRA] & MMCRA_IFM_MASK) >> MMCRA_IFM_SHIFT; + switch (ifm) { + case 0: + /* record all branches */ + env->bhrb_filter = -1; + break; + case 1: + /* only record calls (LK = 1) */ + env->bhrb_filter = BHRB_TYPE_CALL; + break; + case 2: + /* only record indirect branches */ + env->bhrb_filter = BHRB_TYPE_INDIRECT; + break; + case 3: + /* only record conditional branches */ + env->bhrb_filter = BHRB_TYPE_COND; + break; + } +} + +void pmu_mmcr01a_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); + } + + hreg_bhrb_filter_update(env); + /* * Should this update overflow timers (if mmcr0 is updated) so they * get set in cpu_post_load? @@ -251,7 +293,7 @@ void helper_store_mmcr0(CPUPPCState *env, target_ulong value) env->spr[SPR_POWER_MMCR0] = value; - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); /* Update cycle overflow timers with the current MMCR0 state */ pmu_update_overflow_timers(env); @@ -263,7 +305,14 @@ void helper_store_mmcr1(CPUPPCState *env, uint64_t value) env->spr[SPR_POWER_MMCR1] = value; - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); +} + +void helper_store_mmcrA(CPUPPCState *env, uint64_t value) +{ + env->spr[SPR_POWER_MMCRA] = value; + + pmu_mmcr01a_updated(env); } target_ulong helper_read_pmc(CPUPPCState *env, uint32_t sprn) @@ -277,23 +326,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 summaries and hflags update */ - pmu_mmcr01_updated(env); + pmu_mmcr01a_updated(env); /* * Delete all pending timers if we need to freeze @@ -307,6 +355,7 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) /* 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); @@ -315,20 +364,17 @@ static void fire_PMC_interrupt(PowerPCCPU *cpu) void helper_handle_pmc5_overflow(CPUPPCState *env) { env->spr[SPR_POWER_PMC5] = PMC_COUNTER_NEGATIVE_VAL; - fire_PMC_interrupt(env_archcpu(env)); + 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)); } } @@ -336,7 +382,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 775e640053..3f79cfc45b 100644 --- a/target/ppc/power8-pmu.h +++ b/target/ppc/power8-pmu.h @@ -13,15 +13,22 @@ #ifndef POWER8_PMU_H #define POWER8_PMU_H +#define BHRB_TYPE_NORECORD 0x00 +#define BHRB_TYPE_CALL 0x01 +#define BHRB_TYPE_INDIRECT 0x02 +#define BHRB_TYPE_COND 0x04 +#define BHRB_TYPE_OTHER 0x08 +#define BHRB_TYPE_XL_FORM 0x10 + #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) #define PMC_COUNTER_NEGATIVE_VAL 0x80000000UL void cpu_ppc_pmu_init(CPUPPCState *env); -void pmu_mmcr01_updated(CPUPPCState *env); +void pmu_mmcr01a_updated(CPUPPCState *env); #else static inline void cpu_ppc_pmu_init(CPUPPCState *env) { } -static inline void pmu_mmcr01_updated(CPUPPCState *env) { } +static inline void pmu_mmcr01a_updated(CPUPPCState *env) { } #endif #endif diff --git a/target/ppc/ppc-qmp-cmds.c b/target/ppc/ppc-qmp-cmds.c new file mode 100644 index 0000000000..a25d86a8d1 --- /dev/null +++ b/target/ppc/ppc-qmp-cmds.c @@ -0,0 +1,220 @@ +/* + * QEMU PPC (monitor definitions) + * + * 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 "cpu.h" +#include "monitor/monitor.h" +#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; + + u = ppc_get_cr(env); + + return u; +} + +static target_long monitor_get_xer(Monitor *mon, const struct MonitorDef *md, + int val) +{ + CPUArchState *env = mon_get_cpu_env(mon); + return cpu_read_xer(env); +} + +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); +} + +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); +} + +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); +} + +void hmp_info_tlb(Monitor *mon, const QDict *qdict) +{ + CPUArchState *env1 = mon_get_cpu_env(mon); + + if (!env1) { + monitor_printf(mon, "No CPU available\n"); + return; + } + dump_mmu(env1); +} + +const MonitorDef monitor_defs[] = { + { "fpscr", offsetof(CPUPPCState, fpscr) }, + /* Next instruction pointer */ + { "nip|pc", offsetof(CPUPPCState, nip) }, + { "lr", offsetof(CPUPPCState, lr) }, + { "ctr", offsetof(CPUPPCState, ctr) }, + { "decr", 0, &monitor_get_decr, }, + { "ccr|cr", 0, &monitor_get_ccr, }, + /* Machine state register */ + { "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 }, +}; + +const MonitorDef *target_monitor_defs(void) +{ + return monitor_defs; +} + +static int ppc_cpu_get_reg_num(const char *numstr, int maxnum, int *pregnum) +{ + int regnum; + char *endptr = NULL; + + if (!*numstr) { + return false; + } + + regnum = strtoul(numstr, &endptr, 10); + if (*endptr || (regnum >= maxnum)) { + return false; + } + *pregnum = regnum; + + return true; +} + +int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) +{ + int i, regnum; + CPUPPCState *env = cpu_env(cs); + + /* General purpose registers */ + if ((qemu_tolower(name[0]) == 'r') && + ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->gpr), ®num)) { + *pval = env->gpr[regnum]; + return 0; + } + + /* Floating point registers */ + if ((qemu_tolower(name[0]) == 'f') && + ppc_cpu_get_reg_num(name + 1, 32, ®num)) { + *pval = *cpu_fpr_ptr(env, regnum); + return 0; + } + + /* Special purpose registers */ + for (i = 0; i < ARRAY_SIZE(env->spr_cb); ++i) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (spr->name && (strcasecmp(name, spr->name) == 0)) { + *pval = env->spr[i]; + return 0; + } + } + + /* Segment registers */ +#if !defined(CONFIG_USER_ONLY) + if ((strncasecmp(name, "sr", 2) == 0) && + ppc_cpu_get_reg_num(name + 2, ARRAY_SIZE(env->sr), ®num)) { + *pval = env->sr[regnum]; + return 0; + } +#endif + + 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..01aff449bc 100644 --- a/target/ppc/spr_common.h +++ b/target/ppc/spr_common.h @@ -81,8 +81,13 @@ 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_core_write_generic32(DisasContext *ctx, int sprn, int gprn); +void spr_core_lpar_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_MMCRA(DisasContext *ctx, int sprn, int gprn); void spr_write_PMC(DisasContext *ctx, int sprn, int gprn); void spr_write_CTRL(DisasContext *ctx, int sprn, int gprn); void spr_read_xer(DisasContext *ctx, int gprn, int sprn); @@ -109,7 +114,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 +162,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 +201,15 @@ 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); +void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); +void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn); +void spr_read_sprd(DisasContext *ctx, int sprn, int gprn); +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn); #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 b80f56af7e..73120323b4 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,52 @@ 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; + + if (ppc_cpu_lpar_single_threaded(cs)) { + 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; + + if (ppc_cpu_lpar_single_threaded(cs)) { + 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; + + if (ppc_cpu_lpar_single_threaded(cs)) { + 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 +135,50 @@ 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; + + if (ppc_cpu_lpar_single_threaded(cs)) { + 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; + + if (ppc_cpu_lpar_single_threaded(cs)) { + 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; + + if (ppc_cpu_lpar_single_threaded(cs)) { + 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) @@ -144,6 +211,256 @@ void helper_store_booke_tsr(CPUPPCState *env, target_ulong val) store_booke_tsr(env, val); } +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) +/* + * qemu-user breaks with pnv headers, so they go under ifdefs for now. + * A clean up may be to move powernv specific registers and helpers into + * target/ppc/pnv_helper.c + */ +#include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_chip.h" +/* + * 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 (ppc_cpu_core_single_threaded(cs)) { + 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 PnvCoreTODState *cpu_get_tbst(PowerPCCPU *cpu) +{ + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + + if (pc->big_core && pc->tod_state.big_core_quirk) { + /* Must operate on the even small core */ + int core_id = CPU_CORE(pc)->core_id; + if (core_id & 1) { + pc = pc->chip->cores[core_id & ~1]; + } + } + + return &pc->tod_state; +} + +static void tb_state_machine_step(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + PnvCoreTODState *tod_state = cpu_get_tbst(cpu); + 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 (tod_state->tb_sync_pulse_timer) { + tod_state->tb_sync_pulse_timer--; + } else { + tfmr |= TFMR_TB_SYNC_OCCURED; + write_tfmr(env, tfmr); + } + + if (tod_state->tb_state_timer) { + tod_state->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); + tod_state->tb_state_timer = 3; + } else if (tbst == TBST_GET_TOD) { + if (tod_state->tod_sent_to_tb) { + tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING); + tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB; + tod_state->tb_ready_for_tod = 0; + tod_state->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; + tod_state->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) +{ + PowerPCCPU *cpu = env_archcpu(env); + PnvCoreTODState *tod_state = cpu_get_tbst(cpu); + 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; + tod_state->tb_sync_pulse_timer = 1; + + 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; + tod_state->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? */ + tod_state->tb_ready_for_tod = 0; + tod_state->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. */ + tod_state->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); + tod_state->tb_ready_for_tod = 1; + tod_state->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; + tod_state->tb_ready_for_tod = 0; + } + } + +out: + write_tfmr(env, tfmr); +} +#endif + /*****************************************************************************/ /* Embedded PowerPC specific helpers */ @@ -160,9 +477,9 @@ 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); @@ -183,9 +500,9 @@ 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); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 1de7eca9c4..47ca50a064 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -21,13 +21,10 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "qemu/host-utils.h" -#include "qemu/main-loop.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -41,6 +38,10 @@ #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 @@ -71,12 +72,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; @@ -88,7 +91,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; @@ -96,58 +99,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"); } @@ -166,8 +178,10 @@ struct DisasContext { /* Translation flags */ MemOp default_tcg_memop_mask; #if defined(TARGET_PPC64) + powerpc_excp_t excp_model; bool sf_mode; bool has_cfar; + bool has_bhrb; #endif bool fpu_enabled; bool altivec_enabled; @@ -181,6 +195,7 @@ struct DisasContext { bool mmcr0_pmcjce; bool pmc_other; bool pmu_insn_cnt; + bool bhrb_enable; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; uint32_t flags; @@ -223,15 +238,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) @@ -250,36 +298,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, @@ -288,31 +326,25 @@ 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) { - gen_icount_io_start(ctx); - gen_helper_ppc_maybe_interrupt(cpu_env); + translator_io_start(&ctx->base); + gen_helper_ppc_maybe_interrupt(tcg_env); } #endif @@ -323,8 +355,9 @@ static void gen_ppc_maybe_interrupt(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) { @@ -337,17 +370,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) @@ -387,9 +421,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 } @@ -402,9 +435,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 } @@ -414,9 +446,89 @@ 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); +} + +void spr_core_write_generic32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0; + + if (!(ctx->flags & POWERPC_FLAG_SMT)) { + spr_write_generic32(ctx, sprn, gprn); + return; + } + + if (!gen_serialize(ctx)) { + return; + } + + t0 = tcg_temp_new(); + tcg_gen_ext32u_tl(t0, cpu_gpr[gprn]); + gen_helper_spr_core_write_generic(tcg_env, tcg_constant_i32(sprn), t0); + spr_store_dump_spr(sprn); +} + +void spr_core_lpar_write_generic(DisasContext *ctx, int sprn, int gprn) +{ + if (ctx->flags & POWERPC_FLAG_SMT_1LPAR) { + spr_core_write_generic(ctx, sprn, gprn); + } else { + spr_write_generic(ctx, sprn, gprn); + } +} + +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, @@ -427,19 +539,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(); @@ -448,8 +547,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) @@ -479,9 +576,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) @@ -510,8 +604,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); @@ -521,6 +616,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 */ @@ -557,14 +672,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 @@ -572,91 +687,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 @@ -667,94 +802,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) @@ -762,45 +889,56 @@ 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]); + if (!gen_serialize_core(ctx)) { + return; + } + + 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 @@ -809,61 +947,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 @@ -874,7 +1011,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 @@ -882,17 +1018,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) @@ -900,11 +1034,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) @@ -925,11 +1058,10 @@ void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) } 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 @@ -964,10 +1096,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) @@ -995,10 +1123,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) @@ -1026,10 +1150,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 @@ -1037,7 +1157,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); } @@ -1050,7 +1170,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) @@ -1059,7 +1178,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) @@ -1069,27 +1187,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 @@ -1102,7 +1220,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) @@ -1113,8 +1230,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 @@ -1123,29 +1238,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) @@ -1156,9 +1263,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) @@ -1168,8 +1272,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) @@ -1181,12 +1283,47 @@ 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_sprc(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_sprc(tcg_env, cpu_gpr[gprn]); +} + +void spr_read_sprd(DisasContext *ctx, int gprn, int sprn) +{ + gen_helper_load_sprd(cpu_gpr[gprn], tcg_env); +} + +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn) +{ + if (!gen_serialize_core(ctx)) { + return; + } + gen_helper_store_sprd(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) */ @@ -1249,6 +1386,47 @@ 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); +} + +/* The PPR32 SPR accesses the upper 32-bits of PPR */ +void spr_read_ppr32(DisasContext *ctx, int gprn, int sprn) +{ + gen_load_spr(cpu_gpr[gprn], SPR_PPR); + tcg_gen_shri_tl(cpu_gpr[gprn], cpu_gpr[gprn], 32); + spr_load_dump_spr(SPR_PPR); +} + +void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn) +{ + TCGv t0 = tcg_temp_new(); + + /* + * Don't clobber the low 32-bits of the PPR. These are all reserved bits + * but TCG does implement them, so it would be surprising to zero them + * here. "Priority nops" are similarly careful not to clobber reserved + * bits. + */ + gen_load_spr(t0, SPR_PPR); + tcg_gen_deposit_tl(t0, t0, cpu_gpr[gprn], 32, 32); + gen_store_spr(SPR_PPR, t0); + spr_store_dump_spr(SPR_PPR); +} #endif #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ @@ -1410,31 +1588,23 @@ static opc_handler_t invalid_handler = { static inline void gen_op_cmp(TCGv arg0, TCGv arg1, int s, int crf) { TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_movi_tl(t0, CRF_EQ); - tcg_gen_movi_tl(t1, CRF_LT); tcg_gen_movcond_tl((s ? TCG_COND_LT : TCG_COND_LTU), - t0, arg0, arg1, t1, t0); - tcg_gen_movi_tl(t1, CRF_GT); + t0, arg0, arg1, + tcg_constant_tl(CRF_LT), tcg_constant_tl(CRF_EQ)); tcg_gen_movcond_tl((s ? TCG_COND_GT : TCG_COND_GTU), - t0, arg0, arg1, t1, t0); + t0, arg0, arg1, tcg_constant_tl(CRF_GT), t0); 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) @@ -1450,15 +1620,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) @@ -1470,79 +1637,6 @@ static inline void gen_set_Rc0(DisasContext *ctx, TCGv reg) } } -/* cmprb - range comparison: isupper, isaplha, islower*/ -static void gen_cmprb(DisasContext *ctx) -{ - TCGv_i32 src1 = tcg_temp_new_i32(); - TCGv_i32 src2 = tcg_temp_new_i32(); - TCGv_i32 src2lo = tcg_temp_new_i32(); - TCGv_i32 src2hi = tcg_temp_new_i32(); - TCGv_i32 crf = cpu_crf[crfD(ctx->opcode)]; - - tcg_gen_trunc_tl_i32(src1, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_trunc_tl_i32(src2, cpu_gpr[rB(ctx->opcode)]); - - tcg_gen_andi_i32(src1, src1, 0xFF); - tcg_gen_ext8u_i32(src2lo, src2); - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2hi, src2); - - tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); - tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); - tcg_gen_and_i32(crf, src2lo, src2hi); - - if (ctx->opcode & 0x00200000) { - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2lo, src2); - tcg_gen_shri_i32(src2, src2, 8); - tcg_gen_ext8u_i32(src2hi, src2); - tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); - tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); - tcg_gen_and_i32(src2lo, src2lo, src2hi); - 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) -/* cmpeqb */ -static void gen_cmpeqb(DisasContext *ctx) -{ - gen_helper_cmpeqb(cpu_crf[crfD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -} -#endif - -/* isel (PowerPC 2.03 specification) */ -static void gen_isel(DisasContext *ctx) -{ - uint32_t bi = rC(ctx->opcode); - uint32_t mask = 0x08 >> (bi & 0x03); - TCGv t0 = tcg_temp_new(); - TCGv zr; - - tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); - tcg_gen_andi_tl(t0, t0, mask); - - zr = tcg_const_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 */ -static void gen_cmpb(DisasContext *ctx) -{ - gen_helper_cmpb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); -} - /*** Integer arithmetic ***/ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, @@ -1557,7 +1651,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)) { @@ -1590,7 +1683,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 */ @@ -1619,13 +1711,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); @@ -1633,7 +1724,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); @@ -1651,69 +1741,12 @@ 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) +static inline void gen_op_arith_divw(DisasContext *ctx, TCGv ret, + TCGv arg1, TCGv arg2, bool sign, + bool compute_ov, bool compute_rc0) { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -1746,51 +1779,16 @@ 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)) { + if (unlikely(compute_rc0)) { gen_set_Rc0(ctx, ret); } } -/* Div functions */ -#define GEN_INT_ARITH_DIVW(name, opc3, sign, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_divw(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov); \ -} -/* divwu divwu. divwuo divwuo. */ -GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0); -GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1); -/* divw divw. divwo divwo. */ -GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0); -GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1); - -/* div[wd]eu[o][.] */ -#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, \ - 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)]); \ - } \ -} - -GEN_DIVE(divweu, divweu, 0); -GEN_DIVE(divweuo, divweu, 1); -GEN_DIVE(divwe, divwe, 0); -GEN_DIVE(divweo, divwe, 1); #if defined(TARGET_PPC64) -static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, int sign, int compute_ov) +static inline void gen_op_arith_divd(DisasContext *ctx, TCGv ret, + TCGv arg1, TCGv arg2, bool sign, + bool compute_ov, bool compute_rc0) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -1821,34 +1819,11 @@ 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)) { + if (unlikely(compute_rc0)) { gen_set_Rc0(ctx, ret); } } - -#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_divd(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign, compute_ov); \ -} -/* divdu divdu. divduo divduo. */ -GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0); -GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1); -/* divd divd. divdo divdo. */ -GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0); -GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1); - -GEN_DIVE(divdeu, divdeu, 0); -GEN_DIVE(divdeuo, divdeu, 1); -GEN_DIVE(divde, divde, 0); -GEN_DIVE(divdeo, divde, 1); #endif static inline void gen_op_arith_modw(DisasContext *ctx, TCGv ret, TCGv arg1, @@ -1871,32 +1846,15 @@ 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) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_modw(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign); \ -} - -GEN_INT_ARITH_MODW(moduw, 0x08, 0); -GEN_INT_ARITH_MODW(modsw, 0x18, 1); - #if defined(TARGET_PPC64) static inline void gen_op_arith_modd(DisasContext *ctx, TCGv ret, TCGv arg1, TCGv arg2, int sign) @@ -1917,181 +1875,11 @@ 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) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - gen_op_arith_modd(ctx, cpu_gpr[rD(ctx->opcode)], \ - cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ - sign); \ -} - -GEN_INT_ARITH_MODD(modud, 0x08, 0); -GEN_INT_ARITH_MODD(modsd, 0x18, 1); -#endif - -/* mulhw mulhw. */ -static void gen_mulhw(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - 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)]); - 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)]); - } -} - -/* mulhwu mulhwu. */ -static void gen_mulhwu(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - 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)]); - 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)]); - } -} - -/* mullw mullw. */ -static void gen_mullw(DisasContext *ctx) -{ -#if defined(TARGET_PPC64) - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); - 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)]); -#endif - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mullwo mullwo. */ -static void gen_mullwo(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - 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)]); - tcg_gen_muls2_i32(t0, t1, t0, t1); -#if defined(TARGET_PPC64) - tcg_gen_concat_i32_i64(cpu_gpr[rD(ctx->opcode)], t0, t1); -#else - tcg_gen_mov_i32(cpu_gpr[rD(ctx->opcode)], t0); -#endif - - tcg_gen_sari_i32(t0, t0, 31); - tcg_gen_setcond_i32(TCG_COND_NE, t0, t0, t1); - tcg_gen_extu_i32_tl(cpu_ov, t0); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ov32, cpu_ov); - } - 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)]); - } -} - -/* mulli */ -static void gen_mulli(DisasContext *ctx) -{ - tcg_gen_muli_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - SIMM(ctx->opcode)); -} - -#if defined(TARGET_PPC64) -/* mulhd mulhd. */ -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)]); - } -} - -/* mulhdu mulhdu. */ -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)]); - } -} - -/* mulld mulld. */ -static void gen_mulld(DisasContext *ctx) -{ - tcg_gen_mul_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulldo mulldo. */ -static void gen_mulldo(DisasContext *ctx) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - tcg_gen_muls2_i64(t0, t1, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mov_i64(cpu_gpr[rD(ctx->opcode)], t0); - - tcg_gen_sari_i64(t0, t0, 63); - tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); - if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ov32, cpu_ov); - } - 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)]); } } #endif @@ -2125,9 +1913,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); @@ -2135,12 +1921,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); @@ -2167,426 +1951,23 @@ 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); - 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) -{ - tcg_gen_neg_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); - if (unlikely(Rc(ctx->opcode))) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -static void gen_nego(DisasContext *ctx) -{ - gen_op_arith_neg(ctx, 1); -} /*** Integer logical ***/ -#define GEN_LOGICAL2(name, tcg_op, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - tcg_op(cpu_gpr[rA(ctx->opcode)], 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)]); \ -} - -#define GEN_LOGICAL1(name, tcg_op, opc, type) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - tcg_op(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); \ -} - -/* and & and. */ -GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER); -/* andc & andc. */ -GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER); - -/* andi. */ -static void gen_andi_(DisasContext *ctx) -{ - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - UIMM(ctx->opcode)); - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); -} - -/* andis. */ -static void gen_andis_(DisasContext *ctx) -{ - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - UIMM(ctx->opcode) << 16); - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); -} - -/* cntlzw */ -static void gen_cntlzw(DisasContext *ctx) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - 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)]); - } -} - -/* cnttzw */ -static void gen_cnttzw(DisasContext *ctx) -{ - TCGv_i32 t = tcg_temp_new_i32(); - - 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)]); - } -} - -/* eqv & eqv. */ -GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER); -/* extsb & extsb. */ -GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER); -/* extsh & extsh. */ -GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER); -/* nand & nand. */ -GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER); -/* nor & nor. */ -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); } #endif /* defined(TARGET_PPC64) */ -/* or & or. */ -static void gen_or(DisasContext *ctx) -{ - int rs, ra, rb; - - rs = rS(ctx->opcode); - ra = rA(ctx->opcode); - rb = rB(ctx->opcode); - /* Optimisation for mr. ri case */ - if (rs != ra || rs != rb) { - if (rs != rb) { - tcg_gen_or_tl(cpu_gpr[ra], cpu_gpr[rs], cpu_gpr[rb]); - } else { - tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rs]); - } - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[ra]); - } - } else if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rs]); -#if defined(TARGET_PPC64) - } else if (rs != 0) { /* 0 is nop */ - int prio = 0; - - switch (rs) { - case 1: - /* Set process priority to low */ - prio = 2; - break; - case 6: - /* Set process priority to medium-low */ - prio = 3; - break; - case 2: - /* Set process priority to normal */ - prio = 4; - break; -#if !defined(CONFIG_USER_ONLY) - case 31: - if (!ctx->pr) { - /* Set process priority to very low */ - prio = 1; - } - break; - case 5: - if (!ctx->pr) { - /* Set process priority to medium-hight */ - prio = 5; - } - break; - case 3: - if (!ctx->pr) { - /* Set process priority to high */ - prio = 6; - } - break; - case 7: - if (ctx->hv && !ctx->pr) { - /* Set process priority to very high */ - prio = 7; - } - break; -#endif - default: - break; - } - if (prio) { - TCGv t0 = tcg_temp_new(); - gen_load_spr(t0, SPR_PPR); - 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) - /* - * Pause out of TCG otherwise spin loops with smt_low eat too - * much CPU and the kernel hangs. This applies to all - * encodings other than no-op, e.g., miso(rs=26), yield(27), - * mdoio(29), mdoom(30), and all currently undefined. - */ - gen_pause(ctx); -#endif -#endif - } -} -/* orc & orc. */ -GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER); - -/* xor & xor. */ -static void gen_xor(DisasContext *ctx) -{ - /* Optimisation for "set to zero" case */ - if (rS(ctx->opcode) != rB(ctx->opcode)) { - tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - } else { - tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); - } - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* ori */ -static void gen_ori(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - return; - } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); -} - -/* oris */ -static void gen_oris(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - uimm << 16); -} - -/* xori */ -static void gen_xori(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], uimm); -} - -/* xoris */ -static void gen_xoris(DisasContext *ctx) -{ - target_ulong uimm = UIMM(ctx->opcode); - - if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) { - /* NOP */ - return; - } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], - uimm << 16); -} - -/* popcntb : PowerPC 2.03 specification */ -static void gen_popcntb(DisasContext *ctx) -{ - gen_helper_popcntb(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -} - -static void gen_popcntw(DisasContext *ctx) -{ -#if defined(TARGET_PPC64) - gen_helper_popcntw(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#else - tcg_gen_ctpop_i32(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -#endif -} - -#if defined(TARGET_PPC64) -/* popcntd: PowerPC 2.06 specification */ -static void gen_popcntd(DisasContext *ctx) -{ - tcg_gen_ctpop_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)]); -} -#endif - -/* prtyw: PowerPC 2.05 specification */ -static void gen_prtyw(DisasContext *ctx) -{ - TCGv ra = cpu_gpr[rA(ctx->opcode)]; - TCGv rs = cpu_gpr[rS(ctx->opcode)]; - TCGv t0 = tcg_temp_new(); - tcg_gen_shri_tl(t0, rs, 16); - tcg_gen_xor_tl(ra, rs, t0); - 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) -/* prtyd: PowerPC 2.05 specification */ -static void gen_prtyd(DisasContext *ctx) -{ - TCGv ra = cpu_gpr[rA(ctx->opcode)]; - TCGv rs = cpu_gpr[rS(ctx->opcode)]; - TCGv t0 = tcg_temp_new(); - tcg_gen_shri_tl(t0, rs, 32); - tcg_gen_xor_tl(ra, rs, t0); - tcg_gen_shri_tl(t0, ra, 16); - tcg_gen_xor_tl(ra, ra, t0); - 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 - -#if defined(TARGET_PPC64) -/* bpermd */ -static void gen_bpermd(DisasContext *ctx) -{ - gen_helper_bpermd(cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); -} -#endif - -#if defined(TARGET_PPC64) -/* extsw & extsw. */ -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B); - -/* cntlzd */ -static void gen_cntlzd(DisasContext *ctx) -{ - tcg_gen_clzi_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], 64); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* cnttzd */ -static void gen_cnttzd(DisasContext *ctx) -{ - tcg_gen_ctzi_i64(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], 64); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* darn */ -static void gen_darn(DisasContext *ctx) -{ - int l = L(ctx->opcode); - - if (l > 2) { - tcg_gen_movi_i64(cpu_gpr[rD(ctx->opcode)], -1); - } else { - gen_icount_io_start(ctx); - if (l == 0) { - gen_helper_darn32(cpu_gpr[rD(ctx->opcode)]); - } else { - /* Return 64-bit random for both CRN and RRN */ - gen_helper_darn64(cpu_gpr[rD(ctx->opcode)]); - } - } -} -#endif - /*** Integer rotate ***/ /* rlwimi & rlwimi. */ @@ -2622,7 +2003,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); @@ -2635,7 +2015,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); @@ -2679,7 +2058,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) @@ -2726,15 +2104,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 @@ -2842,7 +2217,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)) { @@ -2889,7 +2263,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); @@ -2918,8 +2291,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)]); @@ -2929,7 +2300,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)]); @@ -2955,7 +2326,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); @@ -2986,8 +2356,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)]); } @@ -3007,8 +2375,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)]); } @@ -3017,7 +2383,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)]); @@ -3041,7 +2407,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); @@ -3100,8 +2465,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)]); } @@ -3177,6 +2540,7 @@ static inline void gen_align_no_le(DisasContext *ctx) (ctx->opcode & 0x03FF0000) | POWERPC_EXCP_ALIGN_LE); } +/* EA <- {(ra == 0) ? 0 : GPR[ra]} + displ */ static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) { TCGv ea = tcg_temp_new(); @@ -3191,6 +2555,22 @@ static TCGv do_ea_calc(DisasContext *ctx, int ra, TCGv displ) return ea; } +#if defined(TARGET_PPC64) +/* EA <- (ra == 0) ? 0 : GPR[ra] */ +static TCGv do_ea_calc_ra(DisasContext *ctx, int ra) +{ + TCGv EA = tcg_temp_new(); + if (!ra) { + tcg_gen_movi_tl(EA, 0); + } else if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(EA, cpu_gpr[ra]); + } else { + tcg_gen_mov_tl(EA, cpu_gpr[ra]); + } + return EA; +} +#endif + /*** Integer load ***/ #define DEF_MEMOP(op) ((op) | ctx->default_tcg_memop_mask) #define BSWAP_MEMOP(op) ((op) | (ctx->default_tcg_memop_mask ^ MO_BSWAP)) @@ -3273,7 +2653,6 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ 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) \ @@ -3291,7 +2670,6 @@ static void glue(gen_, name##epx)(DisasContext *ctx) \ 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) @@ -3319,7 +2697,6 @@ static void glue(gen_, name##x)(DisasContext *ctx) \ 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) @@ -3337,7 +2714,6 @@ static void glue(gen_, name##epx)(DisasContext *ctx) \ 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) @@ -3387,11 +2763,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 */ @@ -3406,11 +2780,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 ***/ @@ -3446,12 +2818,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 */ @@ -3467,14 +2836,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 */ @@ -3494,12 +2859,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 */ @@ -3518,64 +2880,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); -} - -/*** Memory synchronisation ***/ -/* eieio */ -static void gen_eieio(DisasContext *ctx) -{ - TCGBar bar = TCG_MO_ALL; - - /* - * eieio has complex semanitcs. It provides memory ordering between - * operations in the set: - * - loads from CI memory. - * - stores to CI memory. - * - stores to WT memory. - * - * It separately also orders memory for operations in the set: - * - stores to cacheble memory. - * - * It also serializes instructions: - * - dcbt and dcbst. - * - * It separately serializes: - * - tlbie and tlbsync. - * - * And separately serializes: - * - slbieg, slbiag, and slbsync. - * - * The end result is that CI memory ordering requires TCG_MO_ALL - * and it is not possible to special-case more relaxed ordering for - * cacheable accesses. TCG_BAR_SC is required to provide this - * serialization. - */ - - /* - * POWER9 has a eieio instruction variant using bit 6 as a hint to - * tell the CPU it is a store-forwarding barrier. - */ - if (ctx->opcode & 0x2000000) { - /* - * ISA says that "Reserved fields in instructions are ignored - * by the processor". So ignore the bit 6 on non-POWER9 CPU but - * as this is not an instruction software should be using, - * complain to the user. - */ - if (!(ctx->insns_flags2 & PPC2_ISA300)) { - qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" - TARGET_FMT_lx "\n", ctx->cia); - } else { - bar = TCG_MO_ST_LD; - } - } - - tcg_gen_mb(bar | TCG_BAR_SC); + t2 = tcg_constant_i32(rS(ctx->opcode)); + gen_helper_stsw(tcg_env, t0, t1, t2); } #if !defined(CONFIG_USER_ONLY) @@ -3589,15 +2895,21 @@ 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); + if (global) { + /* + * Global TLB flush uses async-work which must run before the + * next instruction, so this must be the last in the TB. + */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } } #else static inline void gen_check_tlb_flush(DisasContext *ctx, bool global) { } @@ -3617,8 +2929,6 @@ static void gen_isync(DisasContext *ctx) ctx->base.is_jmp = DISAS_EXIT_UPDATE; } -#define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) - static void gen_load_locked(DisasContext *ctx, MemOp memop) { TCGv gpr = cpu_gpr[rD(ctx->opcode)]; @@ -3626,11 +2936,10 @@ static void gen_load_locked(DisasContext *ctx, MemOp memop) gen_set_access_type(ctx, ACCESS_RES); gen_addr_reg_index(ctx, t0); - tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); + tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, DEF_MEMOP(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) \ @@ -3640,9 +2949,9 @@ static void gen_##name(DisasContext *ctx) \ } /* lwarx */ -LARX(lbarx, DEF_MEMOP(MO_UB)) -LARX(lharx, DEF_MEMOP(MO_UW)) -LARX(lwarx, DEF_MEMOP(MO_UL)) +LARX(lbarx, MO_UB) +LARX(lharx, MO_UW) +LARX(lwarx, MO_UL) static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, TCGv EA, TCGCond cond, int addend) @@ -3652,7 +2961,7 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, TCGv u = tcg_temp_new(); tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); - tcg_gen_addi_tl(t2, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_addi_tl(t2, EA, memop_size(memop)); tcg_gen_qemu_ld_tl(t2, t2, ctx->mem_idx, memop); tcg_gen_addi_tl(u, t, addend); @@ -3662,12 +2971,8 @@ static void gen_fetch_inc_conditional(DisasContext *ctx, MemOp memop, tcg_gen_qemu_st_tl(u, EA, ctx->mem_idx, 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); + tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, + tcg_constant_tl(1 << (memop_size(memop) * 8 - 1))); } static void gen_ld_atomic(DisasContext *ctx, MemOp memop) @@ -3730,9 +3035,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; @@ -3762,11 +3064,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; } } @@ -3822,7 +3123,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(); @@ -3832,26 +3133,18 @@ static void gen_st_atomic(DisasContext *ctx, MemOp memop) TCGv ea_plus_s = tcg_temp_new(); tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); - tcg_gen_addi_tl(ea_plus_s, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_addi_tl(ea_plus_s, EA, memop_size(memop)); tcg_gen_qemu_ld_tl(t2, ea_plus_s, ctx->mem_idx, memop); tcg_gen_movcond_tl(TCG_COND_EQ, s, t, t2, src, t); 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) @@ -3868,37 +3161,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); } @@ -3908,21 +3196,22 @@ static void gen_##name(DisasContext *ctx) \ gen_conditional_store(ctx, memop); \ } -STCX(stbcx_, DEF_MEMOP(MO_UB)) -STCX(sthcx_, DEF_MEMOP(MO_UW)) -STCX(stwcx_, DEF_MEMOP(MO_UL)) +STCX(stbcx_, MO_UB) +STCX(sthcx_, MO_UW) +STCX(stwcx_, MO_UL) #if defined(TARGET_PPC64) /* ldarx */ -LARX(ldarx, DEF_MEMOP(MO_UQ)) +LARX(ldarx, MO_UQ) /* stdcx. */ -STCX(stdcx_, DEF_MEMOP(MO_UQ)) +STCX(stdcx_, MO_UQ) /* lqarx */ 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)))) { @@ -3938,148 +3227,69 @@ 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_mov_tl(cpu_reserve, EA); - tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); + 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) */ -/* sync */ -static void gen_sync(DisasContext *ctx) -{ - TCGBar bar = TCG_MO_ALL; - uint32_t l = (ctx->opcode >> 21) & 3; - - if ((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) { - bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; - } - - /* - * We may need to check for a pending TLB flush. - * - * We do this on ptesync (l == 2) on ppc64 and any sync pn ppc32. - * - * Additionally, this can only happen in kernel mode however so - * check MSR_PR as well. - */ - if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { - gen_check_tlb_flush(ctx, true); - } - - tcg_gen_mb(bar | TCG_BAR_SC); -} - /* wait */ static void gen_wait(DisasContext *ctx) { @@ -4132,10 +3342,9 @@ static void gen_wait(DisasContext *ctx) * to occur. */ if (wc == 0) { - TCGv_i32 t0 = tcg_const_i32(1); - tcg_gen_st_i32(t0, cpu_env, + TCGv_i32 t0 = tcg_constant_i32(1); + tcg_gen_st_i32(t0, tcg_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); } @@ -4179,9 +3388,9 @@ static void gen_doze(DisasContext *ctx) TCGv_i32 t; CHK_HV(ctx); - t = tcg_const_i32(PPC_PM_DOZE); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + 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) */ @@ -4195,9 +3404,9 @@ static void gen_nap(DisasContext *ctx) TCGv_i32 t; CHK_HV(ctx); - t = tcg_const_i32(PPC_PM_NAP); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + 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) */ @@ -4211,9 +3420,9 @@ static void gen_stop(DisasContext *ctx) TCGv_i32 t; CHK_HV(ctx); - t = tcg_const_i32(PPC_PM_STOP); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + 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) */ @@ -4227,9 +3436,9 @@ static void gen_sleep(DisasContext *ctx) TCGv_i32 t; CHK_HV(ctx); - t = tcg_const_i32(PPC_PM_SLEEP); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + 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) */ @@ -4243,21 +3452,92 @@ static void gen_rvwinkle(DisasContext *ctx) TCGv_i32 t; CHK_HV(ctx); - t = tcg_const_i32(PPC_PM_RVWINKLE); - gen_helper_pminsn(cpu_env, t); - tcg_temp_free_i32(t); + 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) */ } + +static inline TCGv gen_write_bhrb(TCGv_ptr base, TCGv offset, TCGv mask, TCGv value) +{ + TCGv_ptr tmp = tcg_temp_new_ptr(); + + /* add base and offset to get address of bhrb entry */ + tcg_gen_add_ptr(tmp, base, (TCGv_ptr)offset); + + /* store value into bhrb at bhrb_offset */ + tcg_gen_st_i64(value, tmp, 0); + + /* add 8 to current bhrb_offset */ + tcg_gen_addi_tl(offset, offset, 8); + + /* apply offset mask */ + tcg_gen_and_tl(offset, offset, mask); + + return offset; +} #endif /* #if defined(TARGET_PPC64) */ -static inline void gen_update_cfar(DisasContext *ctx, target_ulong nip) +static inline void gen_update_branch_history(DisasContext *ctx, + target_ulong nip, + TCGv target, + target_long inst_type) { #if defined(TARGET_PPC64) + TCGv_ptr base; + TCGv tmp; + TCGv offset; + TCGv mask; + TCGLabel *no_update; + if (ctx->has_cfar) { tcg_gen_movi_tl(cpu_cfar, nip); } + + if (!ctx->has_bhrb || + !ctx->bhrb_enable || + inst_type == BHRB_TYPE_NORECORD) { + return; + } + + tmp = tcg_temp_new(); + no_update = gen_new_label(); + + /* check for bhrb filtering */ + tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPUPPCState, bhrb_filter)); + tcg_gen_andi_tl(tmp, tmp, inst_type); + tcg_gen_brcondi_tl(TCG_COND_EQ, tmp, 0, no_update); + + base = tcg_temp_new_ptr(); + offset = tcg_temp_new(); + mask = tcg_temp_new(); + + /* load bhrb base address */ + tcg_gen_ld_ptr(base, tcg_env, offsetof(CPUPPCState, bhrb_base)); + + /* load current bhrb_offset */ + tcg_gen_ld_tl(offset, tcg_env, offsetof(CPUPPCState, bhrb_offset)); + + /* load a BHRB offset mask */ + tcg_gen_ld_tl(mask, tcg_env, offsetof(CPUPPCState, bhrb_offset_mask)); + + offset = gen_write_bhrb(base, offset, mask, tcg_constant_i64(nip)); + + /* Also record the target address for XL-Form branches */ + if (inst_type & BHRB_TYPE_XL_FORM) { + + /* Set the 'T' bit for target entries */ + tcg_gen_ori_tl(tmp, target, 0x2); + + offset = gen_write_bhrb(base, offset, mask, tmp); + } + + /* save updated bhrb_offset for next time */ + tcg_gen_st_tl(offset, tcg_env, offsetof(CPUPPCState, bhrb_offset)); + + gen_set_label(no_update); #endif } @@ -4282,7 +3562,7 @@ static void pmu_count_insns(DisasContext *ctx) * 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); /* Avoid helper calls when only PMC5-6 are enabled. */ if (!ctx->pmc_other) { @@ -4295,13 +3575,12 @@ static void pmu_count_insns(DisasContext *ctx) /* 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(cpu_env); + gen_helper_handle_pmc5_overflow(tcg_env); } gen_set_label(l); - tcg_temp_free(t0); } else { - gen_helper_insns_inc(cpu_env, tcg_constant_i32(ctx->base.num_insns)); + gen_helper_insns_inc(tcg_env, tcg_constant_i32(ctx->base.num_insns)); } #else /* @@ -4314,8 +3593,6 @@ 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) */ } #else @@ -4327,13 +3604,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 @@ -4387,8 +3667,10 @@ static void gen_b(DisasContext *ctx) } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_CALL); + } else { + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_OTHER); } - gen_update_cfar(ctx, ctx->cia); gen_goto_tb(ctx, 0, target); ctx->base.is_jmp = DISAS_NORETURN; } @@ -4403,9 +3685,10 @@ static void gen_bcond(DisasContext *ctx, int type) uint32_t bo = BO(ctx->opcode); TCGLabel *l1; TCGv target; + target_long bhrb_type = BHRB_TYPE_OTHER; 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) { @@ -4413,11 +3696,16 @@ static void gen_bcond(DisasContext *ctx, int type) } else { tcg_gen_mov_tl(target, cpu_lr); } + if (!LK(ctx->opcode)) { + bhrb_type |= BHRB_TYPE_INDIRECT; + } + bhrb_type |= BHRB_TYPE_XL_FORM; } else { target = NULL; } if (LK(ctx->opcode)) { gen_setlr(ctx, ctx->base.pc_next); + bhrb_type |= BHRB_TYPE_CALL; } l1 = gen_new_label(); if ((bo & 0x4) == 0) { @@ -4441,8 +3729,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; } @@ -4470,7 +3756,7 @@ static void gen_bcond(DisasContext *ctx, int type) tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); } } - tcg_temp_free(temp); + bhrb_type |= BHRB_TYPE_COND; } if ((bo & 0x10) == 0) { /* Test CR */ @@ -4485,9 +3771,11 @@ 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); + bhrb_type |= BHRB_TYPE_COND; } - gen_update_cfar(ctx, ctx->cia); + + gen_update_branch_history(ctx, ctx->cia, target, bhrb_type); + if (type == BCOND_IM) { target_ulong li = (target_long)((int16_t)(BD(ctx->opcode))); if (likely(AA(ctx->opcode) == 0)) { @@ -4502,7 +3790,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 */ @@ -4560,8 +3847,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 */ @@ -4605,9 +3890,9 @@ static void gen_rfi(DisasContext *ctx) } /* Restore CPU state */ CHK_SV(ctx); - gen_icount_io_start(ctx); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfi(cpu_env); + translator_io_start(&ctx->base); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + gen_helper_rfi(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4620,9 +3905,9 @@ static void gen_rfid(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV(ctx); - gen_icount_io_start(ctx); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfid(cpu_env); + translator_io_start(&ctx->base); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + gen_helper_rfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4635,9 +3920,9 @@ static void gen_rfscv(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV(ctx); - gen_icount_io_start(ctx); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfscv(cpu_env); + translator_io_start(&ctx->base); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + gen_helper_rfscv(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4650,7 +3935,8 @@ static void gen_hrfid(DisasContext *ctx) #else /* Restore CPU state */ CHK_HV(ctx); - gen_helper_hrfid(cpu_env); + translator_io_start(&ctx->base); + gen_helper_hrfid(tcg_env); ctx->base.is_jmp = DISAS_EXIT; #endif } @@ -4661,13 +3947,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); } @@ -4679,7 +3969,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; } @@ -4689,82 +3979,20 @@ static void gen_scv(DisasContext *ctx) /*** Trap ***/ /* Check for unconditional traps (always or never) */ -static bool check_unconditional_trap(DisasContext *ctx) +static bool check_unconditional_trap(DisasContext *ctx, int to) { /* Trap never */ - if (TO(ctx->opcode) == 0) { + if (to == 0) { return true; } /* Trap always */ - if (TO(ctx->opcode) == 31) { + if (to == 31) { gen_exception_err(ctx, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_TRAP); return true; } return false; } -/* tw */ -static void gen_tw(DisasContext *ctx) -{ - TCGv_i32 t0; - - 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_temp_free_i32(t0); -} - -/* twi */ -static void gen_twi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - 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); -} - -#if defined(TARGET_PPC64) -/* td */ -static void gen_td(DisasContext *ctx) -{ - TCGv_i32 t0; - - 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_temp_free_i32(t0); -} - -/* tdi */ -static void gen_tdi(DisasContext *ctx) -{ - TCGv t0; - TCGv_i32 t1; - - 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); -} -#endif - /*** Processor control ***/ /* mcrxr */ @@ -4782,8 +4010,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); @@ -4807,8 +4033,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 @@ -4843,7 +4067,6 @@ 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); } } @@ -4940,7 +4163,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(); @@ -4951,7 +4173,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); } } @@ -4973,7 +4194,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 */ @@ -4994,13 +4215,10 @@ 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) */ @@ -5016,7 +4234,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); @@ -5036,13 +4254,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 } @@ -5115,8 +4330,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 @@ -5131,7 +4344,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) */ @@ -5144,7 +4356,6 @@ static void gen_dcbfep(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); } /* dcbi (Supervisor only) */ @@ -5163,8 +4374,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) */ } @@ -5177,7 +4386,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) */ @@ -5189,7 +4397,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 */ @@ -5240,37 +4447,42 @@ 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 */ static void gen_dcbz(DisasContext *ctx) { - TCGv tcgv_addr; - TCGv_i32 tcgv_op; + TCGv tcgv_addr = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); - tcgv_addr = tcg_temp_new(); - tcgv_op = tcg_const_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); + +#ifdef TARGET_PPC64 + if (ctx->excp_model == POWERPC_EXCP_970 && !(ctx->opcode & 0x00200000)) { + gen_helper_dcbzl(tcg_env, tcgv_addr); + return; + } +#endif + + gen_helper_dcbz(tcg_env, tcgv_addr, tcg_constant_i32(ctx->mem_idx)); } /* dcbzep */ static void gen_dcbzep(DisasContext *ctx) { - TCGv tcgv_addr; - TCGv_i32 tcgv_op; + TCGv tcgv_addr = tcg_temp_new(); gen_set_access_type(ctx, ACCESS_CACHE); - tcgv_addr = tcg_temp_new(); - tcgv_op = tcg_const_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_dcbz(tcg_env, tcgv_addr, tcg_constant_i32(PPC_TLB_EPID_STORE)); } /* dst / dstt */ @@ -5307,8 +4519,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 */ @@ -5318,8 +4529,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: */ @@ -5345,9 +4555,8 @@ static void gen_mfsr(DisasContext *ctx) TCGv t0; CHK_SV(ctx); - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5362,8 +4571,7 @@ static void gen_mfsrin(DisasContext *ctx) 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) */ } @@ -5376,9 +4584,8 @@ static void gen_mtsr(DisasContext *ctx) TCGv t0; CHK_SV(ctx); - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(t0); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5393,8 +4600,7 @@ static void gen_mtsrin(DisasContext *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) */ } @@ -5410,9 +4616,8 @@ static void gen_mfsr_64b(DisasContext *ctx) TCGv t0; CHK_SV(ctx); - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_load_sr(cpu_gpr[rD(ctx->opcode)], tcg_env, t0); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5427,8 +4632,7 @@ static void gen_mfsrin_64b(DisasContext *ctx) 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) */ } @@ -5441,9 +4645,8 @@ static void gen_mtsr_64b(DisasContext *ctx) TCGv t0; CHK_SV(ctx); - t0 = tcg_const_tl(SR(ctx->opcode)); - gen_helper_store_sr(cpu_env, t0, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(t0); + t0 = tcg_constant_tl(SR(ctx->opcode)); + gen_helper_store_sr(tcg_env, t0, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5458,8 +4661,7 @@ static void gen_mtsrin_64b(DisasContext *ctx) 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) */ } @@ -5476,7 +4678,7 @@ static void gen_tlbia(DisasContext *ctx) #else CHK_HV(ctx); - gen_helper_tlbia(cpu_env); + gen_helper_tlbia(tcg_env); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5513,7 +4715,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 */ @@ -5526,7 +4727,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 */ @@ -5538,7 +4738,7 @@ static void gen_tlbld_6xx(DisasContext *ctx) GEN_PRIV(ctx); #else CHK_SV(ctx); - gen_helper_6xx_tlbd(cpu_env, cpu_gpr[rB(ctx->opcode)]); + gen_helper_6xx_tlbd(tcg_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5549,7 +4749,7 @@ static void gen_tlbli_6xx(DisasContext *ctx) GEN_PRIV(ctx); #else CHK_SV(ctx); - gen_helper_6xx_tlbi(cpu_env, cpu_gpr[rB(ctx->opcode)]); + gen_helper_6xx_tlbi(tcg_env, cpu_gpr[rB(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5573,8 +4773,7 @@ static void gen_tlbiva(DisasContext *ctx) 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) */ } @@ -5584,8 +4783,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: @@ -5692,8 +4891,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]); @@ -5802,9 +4999,8 @@ static void gen_mfdcr(DisasContext *ctx) TCGv dcrn; CHK_SV(ctx); - dcrn = tcg_const_tl(SPR(ctx->opcode)); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, dcrn); - tcg_temp_free(dcrn); + dcrn = tcg_constant_tl(SPR(ctx->opcode)); + gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], tcg_env, dcrn); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5817,9 +5013,8 @@ static void gen_mtdcr(DisasContext *ctx) TCGv dcrn; CHK_SV(ctx); - dcrn = tcg_const_tl(SPR(ctx->opcode)); - gen_helper_store_dcr(cpu_env, dcrn, cpu_gpr[rS(ctx->opcode)]); - tcg_temp_free(dcrn); + dcrn = tcg_constant_tl(SPR(ctx->opcode)); + gen_helper_store_dcr(tcg_env, dcrn, cpu_gpr[rS(ctx->opcode)]); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -5831,7 +5026,7 @@ static void gen_mfdcrx(DisasContext *ctx) GEN_PRIV(ctx); #else CHK_SV(ctx); - gen_helper_load_dcr(cpu_gpr[rD(ctx->opcode)], cpu_env, + 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) */ @@ -5845,7 +5040,7 @@ static void gen_mtdcrx(DisasContext *ctx) GEN_PRIV(ctx); #else CHK_SV(ctx); - gen_helper_store_dcr(cpu_env, cpu_gpr[rA(ctx->opcode)], + 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) */ @@ -5872,9 +5067,7 @@ static void gen_dcread(DisasContext *ctx) 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) */ } @@ -5910,7 +5103,7 @@ static void gen_rfci_40x(DisasContext *ctx) #else 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) */ } @@ -5922,7 +5115,7 @@ static void gen_rfci(DisasContext *ctx) #else 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) */ } @@ -5937,7 +5130,7 @@ static void gen_rfdi(DisasContext *ctx) #else 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) */ } @@ -5950,7 +5143,7 @@ static void gen_rfmci(DisasContext *ctx) #else 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) */ } @@ -5966,11 +5159,11 @@ static void gen_tlbre_40x(DisasContext *ctx) 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: @@ -5991,8 +5184,7 @@ static void gen_tlbsx_40x(DisasContext *ctx) 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); @@ -6013,11 +5205,11 @@ static void gen_tlbwe_40x(DisasContext *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: @@ -6042,10 +5234,9 @@ static void gen_tlbre_440(DisasContext *ctx) 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: @@ -6066,8 +5257,7 @@ static void gen_tlbsx_440(DisasContext *ctx) 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); @@ -6090,10 +5280,9 @@ static void gen_tlbwe_440(DisasContext *ctx) 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: @@ -6112,7 +5301,7 @@ static void gen_tlbre_booke206(DisasContext *ctx) GEN_PRIV(ctx); #else CHK_SV(ctx); - gen_helper_booke206_tlbre(cpu_env); + gen_helper_booke206_tlbre(tcg_env); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6127,14 +5316,11 @@ static void gen_tlbsx_booke206(DisasContext *ctx) 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) */ } @@ -6145,7 +5331,7 @@ static void gen_tlbwe_booke206(DisasContext *ctx) GEN_PRIV(ctx); #else CHK_SV(ctx); - gen_helper_booke206_tlbwe(cpu_env); + gen_helper_booke206_tlbwe(tcg_env); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -6159,8 +5345,7 @@ static void gen_tlbivax_booke206(DisasContext *ctx) 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) */ } @@ -6177,20 +5362,18 @@ static void gen_tlbilx_booke206(DisasContext *ctx) 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) */ } @@ -6208,7 +5391,6 @@ static void gen_wrtee(DisasContext *ctx) tcg_gen_andi_tl(cpu_msr, cpu_msr, ~(1 << MSR_EE)); tcg_gen_or_tl(cpu_msr, cpu_msr, t0); gen_ppc_maybe_interrupt(ctx); - tcg_temp_free(t0); /* * Stop translation to have a chance to raise an exception if we * just set msr_ee to 1 @@ -6240,27 +5422,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 */ -static void gen_mbar(DisasContext *ctx) -{ - /* interpreted as no-op */ -} - -/* msync replaces sync on 440 */ -static void gen_msync_4xx(DisasContext *ctx) -{ - /* Only e500 seems to treat reserved bits as invalid */ - if ((ctx->insns_flags2 & PPC2_BOOKE206) && - (ctx->opcode & 0x03FFF801)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - } - /* otherwise interpreted as no-op */ } /* icbt */ @@ -6273,47 +5437,13 @@ static void gen_icbt_440(DisasContext *ctx) */ } -#if defined(TARGET_PPC64) -static void gen_maddld(DisasContext *ctx) -{ - TCGv_i64 t1 = tcg_temp_new_i64(); - - 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 */ -static void gen_maddhd_maddhdu(DisasContext *ctx) -{ - TCGv_i64 lo = tcg_temp_new_i64(); - TCGv_i64 hi = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - - if (Rc(ctx->opcode)) { - tcg_gen_mulu2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_movi_i64(t1, 0); - } else { - tcg_gen_muls2_i64(lo, hi, cpu_gpr[rA(ctx->opcode)], - cpu_gpr[rB(ctx->opcode)]); - tcg_gen_sari_i64(t1, cpu_gpr[rC(ctx->opcode)], 63); - } - 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) */ - static void gen_tbegin(DisasContext *ctx) { if (unlikely(!ctx->tm_enabled)) { 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) \ @@ -6409,12 +5539,12 @@ 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, @@ -6422,17 +5552,7 @@ static inline void set_fpr(int regno, TCGv_i64 src) * Starting at ISA v3.1, the result for doubleword 1 is now defined * to be 0. */ - tcg_gen_st_i64(tcg_constant_i64(0), cpu_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)); -} - -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(tcg_constant_i64(0), tcg_env, vsr64_offset(regno, false)); } /* @@ -6604,6 +5724,10 @@ static bool resolve_PLS_D(DisasContext *ctx, arg_D *d, arg_PLS_D *a) #include "translate/storage-ctrl-impl.c.inc" +#include "translate/misc-impl.c.inc" + +#include "translate/bhrb-impl.c.inc" + /* Handles lfdp */ static void gen_dform39(DisasContext *ctx) { @@ -6654,9 +5778,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 @@ -6667,49 +5788,9 @@ GEN_HANDLER_E(brw, 0x1F, 0x1B, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA310), GEN_HANDLER_E(brh, 0x1F, 0x1B, 0x06, 0x0000F801, PPC_NONE, PPC2_ISA310), #endif GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(cmpeqb, 0x1F, 0x00, 0x07, 0x00600000, PPC_NONE, PPC2_ISA300), -#endif -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), -GEN_HANDLER(mullwo, 0x1F, 0x0B, 0x17, 0x00000000, PPC_INTEGER), -GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -#if defined(TARGET_PPC64) -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), -GEN_HANDLER_E(cnttzw, 0x1F, 0x1A, 0x10, 0x00000000, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(copy, 0x1F, 0x06, 0x18, 0x03C00001, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(cp_abort, 0x1F, 0x06, 0x1A, 0x03FFF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER_E(paste, 0x1F, 0x06, 0x1C, 0x03C00000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER), -GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), -GEN_HANDLER(popcntb, 0x1F, 0x1A, 0x03, 0x0000F801, PPC_POPCNTB), -GEN_HANDLER(popcntw, 0x1F, 0x1A, 0x0b, 0x0000F801, PPC_POPCNTWD), -GEN_HANDLER_E(prtyw, 0x1F, 0x1A, 0x04, 0x0000F801, PPC_NONE, PPC2_ISA205), -#if defined(TARGET_PPC64) -GEN_HANDLER(popcntd, 0x1F, 0x1A, 0x0F, 0x0000F801, PPC_POPCNTWD), -GEN_HANDLER(cntlzd, 0x1F, 0x1A, 0x01, 0x00000000, PPC_64B), -GEN_HANDLER_E(cnttzd, 0x1F, 0x1A, 0x11, 0x00000000, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(darn, 0x1F, 0x13, 0x17, 0x001CF801, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(prtyd, 0x1F, 0x1A, 0x05, 0x0000F801, PPC_NONE, PPC2_ISA205), -GEN_HANDLER_E(bpermd, 0x1F, 0x1C, 0x07, 0x00000001, PPC_NONE, PPC2_PERM_ISA206), -#endif GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER), @@ -6738,7 +5819,6 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING), GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), -GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x01FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), @@ -6756,7 +5836,6 @@ GEN_HANDLER_E(lqarx, 0x1F, 0x14, 0x08, 0, PPC_NONE, PPC2_LSQ_ISA207), 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), /* 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), @@ -6785,12 +5864,6 @@ GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H), /* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */ GEN_HANDLER(sc, 0x11, 0x11, 0xFF, 0x03FFF01D, PPC_FLOW), GEN_HANDLER(sc, 0x11, 0x01, 0xFF, 0x03FFF01D, PPC_FLOW), -GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW), -GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW), -#if defined(TARGET_PPC64) -GEN_HANDLER(td, 0x1F, 0x04, 0x02, 0x00000001, PPC_64B), -GEN_HANDLER(tdi, 0x02, 0xFF, 0xFF, 0x00000000, PPC_64B), -#endif GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC), GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x00000801, PPC_MISC), GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC), @@ -6814,6 +5887,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), @@ -6878,115 +5952,12 @@ GEN_HANDLER2_E(tlbilx_booke206, "tlbilx", 0x1F, 0x12, 0x00, 0x03800001, 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), -GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, - PPC_BOOKE, PPC2_BOOKE206), -GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x039FF801, PPC_BOOKE), GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, PPC_440_SPEC), -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), -#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), -#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) -GEN_INT_ARITH_DIVW(divwu, 0x0E, 0, 0), -GEN_INT_ARITH_DIVW(divwuo, 0x1E, 0, 1), -GEN_INT_ARITH_DIVW(divw, 0x0F, 1, 0), -GEN_INT_ARITH_DIVW(divwo, 0x1F, 1, 1), -GEN_HANDLER_E(divwe, 0x1F, 0x0B, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweo, 0x1F, 0x0B, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweu, 0x1F, 0x0B, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divweuo, 0x1F, 0x0B, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(modsw, 0x1F, 0x0B, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(moduw, 0x1F, 0x0B, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), - -#if defined(TARGET_PPC64) -#undef GEN_INT_ARITH_DIVD -#define GEN_INT_ARITH_DIVD(name, opc3, sign, compute_ov) \ -GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) -GEN_INT_ARITH_DIVD(divdu, 0x0E, 0, 0), -GEN_INT_ARITH_DIVD(divduo, 0x1E, 0, 1), -GEN_INT_ARITH_DIVD(divd, 0x0F, 1, 0), -GEN_INT_ARITH_DIVD(divdo, 0x1F, 1, 1), - -GEN_HANDLER_E(divdeu, 0x1F, 0x09, 0x0C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divdeuo, 0x1F, 0x09, 0x1C, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divde, 0x1F, 0x09, 0x0D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(divdeo, 0x1F, 0x09, 0x1D, 0, PPC_NONE, PPC2_DIVE_ISA206), -GEN_HANDLER_E(modsd, 0x1F, 0x09, 0x18, 0x00000001, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(modud, 0x1F, 0x09, 0x08, 0x00000001, PPC_NONE, PPC2_ISA300), - -#undef GEN_INT_ARITH_MUL_HELPER -#define GEN_INT_ARITH_MUL_HELPER(name, opc3) \ -GEN_HANDLER(name, 0x1F, 0x09, opc3, 0x00000000, PPC_64B) -GEN_INT_ARITH_MUL_HELPER(mulhdu, 0x00), -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) \ -GEN_HANDLER(name, 0x1F, 0x1C, opc, 0x00000000, type) -#define GEN_LOGICAL1(name, tcg_op, opc, type) \ -GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, type) -GEN_LOGICAL2(and, tcg_gen_and_tl, 0x00, PPC_INTEGER), -GEN_LOGICAL2(andc, tcg_gen_andc_tl, 0x01, PPC_INTEGER), -GEN_LOGICAL2(eqv, tcg_gen_eqv_tl, 0x08, PPC_INTEGER), -GEN_LOGICAL1(extsb, tcg_gen_ext8s_tl, 0x1D, PPC_INTEGER), -GEN_LOGICAL1(extsh, tcg_gen_ext16s_tl, 0x1C, PPC_INTEGER), -GEN_LOGICAL2(nand, tcg_gen_nand_tl, 0x0E, PPC_INTEGER), -GEN_LOGICAL2(nor, tcg_gen_nor_tl, 0x03, PPC_INTEGER), -GEN_LOGICAL2(orc, tcg_gen_orc_tl, 0x0C, PPC_INTEGER), -#if defined(TARGET_PPC64) -GEN_LOGICAL1(extsw, tcg_gen_ext32s_tl, 0x1E, PPC_64B), -#endif #if defined(TARGET_PPC64) #undef GEN_PPC64_R2 @@ -7344,7 +6315,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++; @@ -7452,8 +6423,6 @@ static bool decode_legacy(PowerPCCPU *cpu, DisasContext *ctx, uint32_t insn) opc_handler_t **table, *handler; uint32_t inval; - ctx->opcode = insn; - LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n", insn, opc1(insn), opc2(insn), opc3(insn), opc4(insn), ctx->le_mode ? "little" : "big"); @@ -7506,7 +6475,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; @@ -7522,8 +6491,10 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE; ctx->flags = env->flags; #if defined(TARGET_PPC64) + ctx->excp_model = env->excp_model; ctx->sf_mode = (hflags >> HFLAGS_64) & 1; ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); + ctx->has_bhrb = !!(env->flags & POWERPC_FLAG_BHRB); #endif ctx->lazy_tlb_flush = env->mmu_model == POWERPC_MMU_32B || env->mmu_model & POWERPC_MMU_64; @@ -7540,6 +6511,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) 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->bhrb_enable = (hflags >> HFLAGS_BHRB_ENABLE) & 1; ctx->singlestep_enabled = 0; if ((hflags >> HFLAGS_SE) & 1) { @@ -7570,7 +6542,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; @@ -7584,6 +6556,7 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ctx->base.pc_next = pc += 4; if (!is_prefix_insn(ctx, insn)) { + ctx->opcode = insn; ok = (decode_insn32(ctx, insn) || decode_legacy(cpu, ctx, insn)); } else if ((pc & 63) == 0) { @@ -7608,8 +6581,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) @@ -7624,8 +6595,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: @@ -7634,12 +6606,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; } @@ -7681,24 +6660,16 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void ppc_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps ppc_tr_ops = { .init_disas_context = ppc_tr_init_disas_context, .tb_start = ppc_tr_tb_start, .insn_start = ppc_tr_insn_start, .translate_insn = ppc_tr_translate_insn, .tb_stop = ppc_tr_tb_stop, - .disas_log = ppc_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/ppc/translate/bhrb-impl.c.inc b/target/ppc/translate/bhrb-impl.c.inc new file mode 100644 index 0000000000..3a19bc4555 --- /dev/null +++ b/target/ppc/translate/bhrb-impl.c.inc @@ -0,0 +1,43 @@ +/* + * Power ISA Decode For BHRB Instructions + * + * Copyright IBM Corp. 2023 + * + * Authors: + * Glenn Miles + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + +static bool trans_MFBHRBE(DisasContext *ctx, arg_XFX_bhrbe *arg) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + TCGv_i32 bhrbe = tcg_constant_i32(arg->bhrbe); + gen_helper_mfbhrbe(cpu_gpr[arg->rt], tcg_env, bhrbe); + return true; +} + +static bool trans_CLRBHRB(DisasContext *ctx, arg_CLRBHRB *arg) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA207S); + gen_helper_clrbhrb(tcg_env); + return true; +} + +#else + +static bool trans_MFBHRBE(DisasContext *ctx, arg_XFX_bhrbe *arg) +{ + gen_invalid(ctx); + return true; +} + +static bool trans_CLRBHRB(DisasContext *ctx, arg_CLRBHRB *arg) +{ + gen_invalid(ctx); + return true; +} +#endif diff --git a/target/ppc/translate/branch-impl.c.inc b/target/ppc/translate/branch-impl.c.inc index 29cfa11854..9ade0c659a 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); - gen_update_cfar(ctx, ctx->cia); - gen_helper_rfebb(cpu_env, cpu_gpr[arg->s]); + translator_io_start(&ctx->base); + gen_update_branch_history(ctx, ctx->cia, NULL, BHRB_TYPE_NORECORD); + 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 1ba56cbed5..fa0191e866 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,8 +71,8 @@ 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); @@ -96,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 @@ -329,6 +289,50 @@ TRANS(CMPL, do_cmp_X, false); TRANS(CMPI, do_cmp_D, true); TRANS(CMPLI, do_cmp_D, false); +static bool trans_CMPRB(DisasContext *ctx, arg_CMPRB *a) +{ + TCGv_i32 src1 = tcg_temp_new_i32(); + TCGv_i32 src2 = tcg_temp_new_i32(); + TCGv_i32 src2lo = tcg_temp_new_i32(); + TCGv_i32 src2hi = tcg_temp_new_i32(); + TCGv_i32 crf = cpu_crf[a->bf]; + + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + tcg_gen_trunc_tl_i32(src1, cpu_gpr[a->ra]); + tcg_gen_trunc_tl_i32(src2, cpu_gpr[a->rb]); + + tcg_gen_andi_i32(src1, src1, 0xFF); + tcg_gen_ext8u_i32(src2lo, src2); + tcg_gen_extract_i32(src2hi, src2, 8, 8); + + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(crf, src2lo, src2hi); + + if (a->l) { + tcg_gen_extract_i32(src2lo, src2, 16, 8); + tcg_gen_extract_i32(src2hi, src2, 24, 8); + tcg_gen_setcond_i32(TCG_COND_LEU, src2lo, src2lo, src1); + tcg_gen_setcond_i32(TCG_COND_LEU, src2hi, src1, src2hi); + tcg_gen_and_i32(src2lo, src2lo, src2hi); + tcg_gen_or_i32(crf, crf, src2lo); + } + tcg_gen_shli_i32(crf, crf, CRF_GT_BIT); + return true; +} + +static bool trans_CMPEQB(DisasContext *ctx, arg_CMPEQB *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + gen_helper_CMPEQB(cpu_crf[a->bf], cpu_gpr[a->ra], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + /* * Fixed-Point Arithmetic Instructions */ @@ -365,6 +369,459 @@ 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_MULLI(DisasContext *ctx, arg_MULLI *a) +{ + tcg_gen_muli_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], a->si); + return true; +} + +static bool trans_MULLW(DisasContext *ctx, arg_MULLW *a) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_ext32s_tl(t0, cpu_gpr[a->ra]); + tcg_gen_ext32s_tl(t1, cpu_gpr[a->rb]); + tcg_gen_mul_tl(cpu_gpr[a->rt], t0, t1); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +static bool trans_MULLWO(DisasContext *ctx, arg_MULLWO *a) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + +#if defined(TARGET_PPC64) + tcg_gen_ext32s_i64(t0, cpu_gpr[a->ra]); + tcg_gen_ext32s_i64(t1, cpu_gpr[a->rb]); + tcg_gen_mul_i64(cpu_gpr[a->rt], t0, t1); + tcg_gen_sextract_i64(t0, cpu_gpr[a->rt], 31, 1); + tcg_gen_sari_i64(t1, cpu_gpr[a->rt], 32); +#else + tcg_gen_muls2_i32(cpu_gpr[a->rt], t1, cpu_gpr[a->ra], cpu_gpr[a->rb]); + tcg_gen_sari_i32(t0, cpu_gpr[a->rt], 31); +#endif + tcg_gen_setcond_tl(TCG_COND_NE, cpu_ov, t0, t1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ov32, cpu_ov); + } + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +static bool do_mulhw(DisasContext *ctx, arg_XO_tab_rc *a, + void (*helper)(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, + TCGv_i32 arg2)) +{ + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t0, cpu_gpr[a->ra]); + tcg_gen_trunc_tl_i32(t1, cpu_gpr[a->rb]); + helper(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_gpr[a->rt], t1); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS(MULHW, do_mulhw, tcg_gen_muls2_i32) +TRANS(MULHWU, do_mulhw, tcg_gen_mulu2_i32) + +static bool do_divw(DisasContext *ctx, arg_XO *a, int sign) +{ + gen_op_arith_divw(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign, a->oe, a->rc); + return true; +} + +static bool do_dive(DisasContext *ctx, arg_XO *a, + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv, TCGv_i32)) +{ + REQUIRE_INSNS_FLAGS2(ctx, DIVE_ISA206); + helper(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], + tcg_constant_i32(a->oe)); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS(DIVW, do_divw, 1); +TRANS(DIVWU, do_divw, 0); +TRANS(DIVWE, do_dive, gen_helper_DIVWE); +TRANS(DIVWEU, do_dive, gen_helper_DIVWEU); + +static bool do_modw(DisasContext *ctx, arg_X *a, bool sign) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + gen_op_arith_modw(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign); + return true; +} + +TRANS(MODUW, do_modw, false); +TRANS(MODSW, do_modw, true); + +static bool trans_NEG(DisasContext *ctx, arg_NEG *a) +{ + if (a->oe) { + TCGv zero = tcg_constant_tl(0); + gen_op_arith_subf(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], zero, + false, false, true, a->rc); + } else { + tcg_gen_neg_tl(cpu_gpr[a->rt], cpu_gpr[a->ra]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + } + return true; +} + +static bool trans_DARN(DisasContext *ctx, arg_DARN *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + if (a->l > 2) { + tcg_gen_movi_i64(cpu_gpr[a->rt], -1); + } else { + translator_io_start(&ctx->base); + if (a->l == 0) { + gen_helper_DARN32(cpu_gpr[a->rt]); + } else { + /* Return 64-bit random for both CRN and RRN */ + gen_helper_DARN64(cpu_gpr[a->rt]); + } + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MULLD(DisasContext *ctx, arg_MULLD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + tcg_gen_mul_tl(cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MULLDO(DisasContext *ctx, arg_MULLD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(t0, t1, cpu_gpr[a->ra], cpu_gpr[a->rb]); + tcg_gen_mov_i64(cpu_gpr[a->rt], t0); + + tcg_gen_sari_i64(t0, t0, 63); + tcg_gen_setcond_i64(TCG_COND_NE, cpu_ov, t0, t1); + if (is_isa300(ctx)) { + tcg_gen_mov_tl(cpu_ov32, cpu_ov); + } + tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_mulhd(DisasContext *ctx, arg_XO_tab_rc *a, + void (*helper)(TCGv, TCGv, TCGv, TCGv)) +{ + TCGv lo = tcg_temp_new(); + helper(lo, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); + } + return true; +} + +TRANS64(MULHD, do_mulhd, tcg_gen_muls2_tl); +TRANS64(MULHDU, do_mulhd, tcg_gen_mulu2_tl); + +static bool trans_MADDLD(DisasContext *ctx, arg_MADDLD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t1, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_add_i64(cpu_gpr[a->vrt], t1, cpu_gpr[a->rc]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MADDHD(DisasContext *ctx, arg_MADDHD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_muls2_i64(lo, hi, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_sari_i64(t1, cpu_gpr[a->rc], 63); + tcg_gen_add2_i64(t1, cpu_gpr[a->vrt], lo, hi, cpu_gpr[a->rc], t1); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_MADDHDU(DisasContext *ctx, arg_MADDHDU *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + TCGv_i64 lo = tcg_temp_new_i64(); + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_mulu2_i64(lo, hi, cpu_gpr[a->vra], cpu_gpr[a->vrb]); + tcg_gen_add2_i64(t1, cpu_gpr[a->vrt], lo, hi, cpu_gpr[a->rc], + tcg_constant_i64(0)); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_divd(DisasContext *ctx, arg_XO *a, bool sign) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + gen_op_arith_divd(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign, a->oe, a->rc); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_modd(DisasContext *ctx, arg_X *a, bool sign) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + gen_op_arith_modd(ctx, cpu_gpr[a->rt], cpu_gpr[a->ra], cpu_gpr[a->rb], + sign); +#else + qemu_build_not_reached(); +#endif + return true; +} + +TRANS64(DIVD, do_divd, true); +TRANS64(DIVDU, do_divd, false); + +static bool trans_DIVDE(DisasContext *ctx, arg_DIVDE *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + return do_dive(ctx, a, gen_helper_DIVDE); +#else + qemu_build_not_reached(); +#endif +} + +static bool trans_DIVDEU(DisasContext *ctx, arg_DIVDEU *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + return do_dive(ctx, a, gen_helper_DIVDEU); +#else + qemu_build_not_reached(); +#endif + return true; +} + +TRANS64(MODSD, do_modd, true); +TRANS64(MODUD, do_modd, false); + +/* + * Fixed-Point Select Instructions + */ + +static bool trans_ISEL(DisasContext *ctx, arg_ISEL *a) +{ + REQUIRE_INSNS_FLAGS(ctx, ISEL); + uint32_t bi = a->bc; + uint32_t mask = 0x08 >> (bi & 0x03); + TCGv t0 = tcg_temp_new(); + TCGv zr; + + tcg_gen_extu_i32_tl(t0, cpu_crf[bi >> 2]); + tcg_gen_andi_tl(t0, t0, mask); + + zr = tcg_constant_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[a->rt], t0, zr, + a->ra ? cpu_gpr[a->ra] : zr, + cpu_gpr[a->rb]); + return true; +} + +/* + * Fixed-Point Trap Instructions + */ + +static bool trans_TW(DisasContext *ctx, arg_TW *a) +{ + TCGv_i32 t0; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_i32(a->rt); + gen_helper_TW(tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], t0); + return true; +} + +static bool trans_TWI(DisasContext *ctx, arg_TWI *a) +{ + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_tl(a->si); + t1 = tcg_constant_i32(a->rt); + gen_helper_TW(tcg_env, cpu_gpr[a->ra], t0, t1); + return true; +} + +static bool trans_TD(DisasContext *ctx, arg_TD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv_i32 t0; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_i32(a->rt); + gen_helper_TD(tcg_env, cpu_gpr[a->ra], cpu_gpr[a->rb], t0); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_TDI(DisasContext *ctx, arg_TDI *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + TCGv t0; + TCGv_i32 t1; + + if (check_unconditional_trap(ctx, a->rt)) { + return true; + } + t0 = tcg_constant_tl(a->si); + t1 = tcg_constant_i32(a->rt); + gen_helper_TD(tcg_env, cpu_gpr[a->ra], t0, t1); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_INVALID(DisasContext *ctx, arg_INVALID *a) { gen_invalid(ctx); @@ -382,15 +839,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; } @@ -399,6 +856,285 @@ TRANS(SETBCR, do_set_bool_cond, false, true) TRANS(SETNBC, do_set_bool_cond, true, false) TRANS(SETNBCR, do_set_bool_cond, true, true) +/* + * Fixed-Point Logical Instructions + */ + +static bool do_addi_(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + tcg_gen_andi_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + return true; +} + +static bool do_ori(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + if (a->rt == a->ra && a->ui == 0) { + /* NOP */ + return true; + } + tcg_gen_ori_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + return true; +} + +static bool do_xori(DisasContext *ctx, arg_D_ui *a, bool shift) +{ + if (a->rt == a->ra && a->ui == 0) { + /* NOP */ + return true; + } + tcg_gen_xori_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], shift ? a->ui << 16 : a->ui); + return true; +} + +static bool do_logical1(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv, TCGv)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rs]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool do_logical2(DisasContext *ctx, arg_X_rc *a, + void (*helper)(TCGv, TCGv, TCGv)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool trans_OR(DisasContext *ctx, arg_OR *a) +{ + /* Optimisation for mr. ri case */ + if (a->rt != a->ra || a->rt != a->rb) { + if (a->rt != a->rb) { + tcg_gen_or_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + } else { + tcg_gen_mov_tl(cpu_gpr[a->ra], cpu_gpr[a->rt]); + } + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + } else if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->rt]); +#if defined(TARGET_PPC64) + } else if (a->rt != 0) { /* 0 is nop */ + int prio = 0; + + switch (a->rt) { + case 1: + /* Set process priority to low */ + prio = 2; + break; + case 6: + /* Set process priority to medium-low */ + prio = 3; + break; + case 2: + /* Set process priority to normal */ + prio = 4; + break; +#if !defined(CONFIG_USER_ONLY) + case 31: + if (!ctx->pr) { + /* Set process priority to very low */ + prio = 1; + } + break; + case 5: + if (!ctx->pr) { + /* Set process priority to medium-hight */ + prio = 5; + } + break; + case 3: + if (!ctx->pr) { + /* Set process priority to high */ + prio = 6; + } + break; + case 7: + if (ctx->hv && !ctx->pr) { + /* Set process priority to very high */ + prio = 7; + } + break; +#endif + default: + break; + } + if (prio) { + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_PPR); + tcg_gen_andi_tl(t0, t0, ~0x001C000000000000ULL); + tcg_gen_ori_tl(t0, t0, ((uint64_t)prio) << 50); + gen_store_spr(SPR_PPR, t0); + } +#if !defined(CONFIG_USER_ONLY) + /* + * Pause out of TCG otherwise spin loops with smt_low eat too + * much CPU and the kernel hangs. This applies to all + * encodings other than no-op, e.g., miso(rs=26), yield(27), + * mdoio(29), mdoom(30), and all currently undefined. + */ + gen_pause(ctx); +#endif +#endif + } + + return true; +} + +static bool trans_XOR(DisasContext *ctx, arg_XOR *a) +{ + /* Optimisation for "set to zero" case */ + if (a->rt != a->rb) { + tcg_gen_xor_tl(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + } else { + tcg_gen_movi_tl(cpu_gpr[a->ra], 0); + } + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +static bool trans_CMPB(DisasContext *ctx, arg_CMPB *a) +{ + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + gen_helper_CMPB(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); + return true; +} + +static bool do_cntzw(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv_i32, TCGv_i32, uint32_t)) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t, cpu_gpr[a->rs]); + helper(t, t, 32); + tcg_gen_extu_i32_tl(cpu_gpr[a->ra], t); + + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} + +#if defined(TARGET_PPC64) +static bool do_cntzd(DisasContext *ctx, arg_X_sa_rc *a, + void (*helper)(TCGv_i64, TCGv_i64, uint64_t)) +{ + helper(cpu_gpr[a->ra], cpu_gpr[a->rs], 64); + if (unlikely(a->rc)) { + gen_set_Rc0(ctx, cpu_gpr[a->ra]); + } + return true; +} +#endif + +static bool trans_CNTLZD(DisasContext *ctx, arg_CNTLZD *a) +{ + REQUIRE_64BIT(ctx); +#if defined(TARGET_PPC64) + do_cntzd(ctx, a, tcg_gen_clzi_i64); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_CNTTZD(DisasContext *ctx, arg_CNTTZD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + do_cntzd(ctx, a, tcg_gen_ctzi_i64); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_POPCNTB(DisasContext *ctx, arg_POPCNTB *a) +{ + REQUIRE_INSNS_FLAGS(ctx, POPCNTB); + gen_helper_POPCNTB(cpu_gpr[a->ra], cpu_gpr[a->rs]); + return true; +} + +static bool trans_POPCNTW(DisasContext *ctx, arg_POPCNTW *a) +{ + REQUIRE_INSNS_FLAGS(ctx, POPCNTWD); +#if defined(TARGET_PPC64) + gen_helper_POPCNTW(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#else + tcg_gen_ctpop_i32(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#endif + return true; +} + +static bool trans_POPCNTD(DisasContext *ctx, arg_POPCNTD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS(ctx, POPCNTWD); +#if defined(TARGET_PPC64) + tcg_gen_ctpop_i64(cpu_gpr[a->ra], cpu_gpr[a->rs]); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool trans_PRTYW(DisasContext *ctx, arg_PRTYW *a) +{ + TCGv ra = cpu_gpr[a->ra]; + TCGv rs = cpu_gpr[a->rs]; + TCGv t0 = tcg_temp_new(); + + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + tcg_gen_shri_tl(t0, rs, 16); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, (target_ulong)0x100000001ULL); + return true; +} + +static bool trans_PRTYD(DisasContext *ctx, arg_PRTYD *a) +{ + TCGv ra = cpu_gpr[a->ra]; + TCGv rs = cpu_gpr[a->rs]; + TCGv t0 = tcg_temp_new(); + + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA205); + tcg_gen_shri_tl(t0, rs, 32); + tcg_gen_xor_tl(ra, rs, t0); + tcg_gen_shri_tl(t0, ra, 16); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_shri_tl(t0, ra, 8); + tcg_gen_xor_tl(ra, ra, t0); + tcg_gen_andi_tl(ra, ra, 1); + return true; +} + +static bool trans_BPERMD(DisasContext *ctx, arg_BPERMD *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, PERM_ISA206); +#if defined(TARGET_PPC64) + gen_helper_BPERMD(cpu_gpr[a->ra], cpu_gpr[a->rt], cpu_gpr[a->rb]); +#else + qemu_build_not_reached(); +#endif + return true; +} + static bool trans_CFUGED(DisasContext *ctx, arg_X *a) { REQUIRE_64BIT(ctx); @@ -437,9 +1173,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) @@ -490,40 +1223,58 @@ static bool trans_PEXTD(DisasContext *ctx, arg_X *a) return true; } +TRANS(ANDI_, do_addi_, false); +TRANS(ANDIS_, do_addi_, true); +TRANS(ORI, do_ori, false); +TRANS(ORIS, do_ori, true); +TRANS(XORI, do_xori, false); +TRANS(XORIS, do_xori, true); + +TRANS(AND, do_logical2, tcg_gen_and_tl); +TRANS(ANDC, do_logical2, tcg_gen_andc_tl); +TRANS(NAND, do_logical2, tcg_gen_nand_tl); +TRANS(ORC, do_logical2, tcg_gen_orc_tl); +TRANS(NOR, do_logical2, tcg_gen_nor_tl); +TRANS(EQV, do_logical2, tcg_gen_eqv_tl); +TRANS(EXTSB, do_logical1, tcg_gen_ext8s_tl); +TRANS(EXTSH, do_logical1, tcg_gen_ext16s_tl); + +TRANS(CNTLZW, do_cntzw, tcg_gen_clzi_i32); +TRANS_FLAGS2(ISA300, CNTTZW, do_cntzw, tcg_gen_ctzi_i32); + +TRANS64(EXTSW, do_logical1, tcg_gen_ext32s_tl); + static bool trans_ADDG6S(DisasContext *ctx, arg_X *a) { - const uint64_t carry_bits = 0x1111111111111111ULL; - TCGv t0, t1, carry, zero = tcg_constant_tl(0); + 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); - t0 = tcg_temp_new(); - t1 = tcg_const_tl(0); - carry = tcg_const_tl(0); + in1 = cpu_gpr[a->ra]; + in2 = cpu_gpr[a->rb]; + tmp = tcg_temp_new(); + carryl = tcg_temp_new(); + carryh = tcg_temp_new(); - for (int i = 0; i < 16; i++) { - tcg_gen_shri_tl(t0, cpu_gpr[a->ra], i * 4); - tcg_gen_andi_tl(t0, t0, 0xf); - tcg_gen_add_tl(t1, t1, t0); + /* 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); - tcg_gen_shri_tl(t0, cpu_gpr[a->rb], i * 4); - tcg_gen_andi_tl(t0, t0, 0xf); - tcg_gen_add_tl(t1, t1, t0); - - tcg_gen_andi_tl(t1, t1, 0x10); - tcg_gen_setcond_tl(TCG_COND_NE, t1, t1, zero); - - tcg_gen_shli_tl(t0, t1, i * 4); - tcg_gen_or_tl(carry, carry, t0); - } - - tcg_gen_xori_tl(carry, carry, (target_long)carry_bits); - tcg_gen_muli_tl(cpu_gpr[a->rt], carry, 6); - - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(carry); + /* + * 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; } @@ -563,10 +1314,7 @@ static bool do_hash(DisasContext *ctx, arg_X *a, bool priv, } ea = do_ea_calc(ctx, a->ra, tcg_constant_tl(a->rt)); - helper(cpu_env, ea, cpu_gpr[a->ra], cpu_gpr[a->rb]); - - tcg_temp_free(ea); - + helper(tcg_env, ea, cpu_gpr[a->ra], cpu_gpr[a->rb]); return true; } diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index 8d5cf0f982..a66b83398b 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) @@ -31,106 +30,73 @@ static void gen_set_cr1_from_fpscr(DisasContext *ctx) #endif /*** Floating-Point arithmetic ***/ -#define _GEN_FLOAT_ACB(name, op1, op2, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - TCGv_i64 t3; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - t3 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - 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); \ - set_fpr(rD(ctx->opcode), t3); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t3); \ - } \ - 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); \ +static bool do_helper_acb(DisasContext *ctx, arg_A *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64, TCGv_i64)) +{ + TCGv_i64 t0, t1, t2, t3; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + t3 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frc); + get_fpr(t2, a->frb); + helper(t3, tcg_env, t0, t1, t2); + set_fpr(a->frt, t3); + gen_compute_fprf_float64(t3); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ -_GEN_FLOAT_ACB(name, 0x3F, op2, set_fprf, type); \ -_GEN_FLOAT_ACB(name##s, 0x3B, op2, set_fprf, type); - -#define _GEN_FLOAT_AB(name, op1, op2, inval, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rB(ctx->opcode)); \ - gen_helper_f##name(t2, cpu_env, t0, t1); \ - set_fpr(rD(ctx->opcode), t2); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t2); \ - } \ - 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); \ +static bool do_helper_ab(DisasContext *ctx, arg_A_tab *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64)) +{ + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frb); + helper(t2, tcg_env, t0, t1); + set_fpr(a->frt, t2); + gen_compute_fprf_float64(t2); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AB(name, 0x3F, op2, inval, set_fprf, type); \ -_GEN_FLOAT_AB(name##s, 0x3B, op2, inval, set_fprf, type); -#define _GEN_FLOAT_AC(name, op1, op2, inval, set_fprf, type) \ -static void gen_f##name(DisasContext *ctx) \ -{ \ - TCGv_i64 t0; \ - TCGv_i64 t1; \ - TCGv_i64 t2; \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - t1 = tcg_temp_new_i64(); \ - t2 = tcg_temp_new_i64(); \ - gen_reset_fpstatus(); \ - get_fpr(t0, rA(ctx->opcode)); \ - get_fpr(t1, rC(ctx->opcode)); \ - gen_helper_f##name(t2, cpu_env, t0, t1); \ - set_fpr(rD(ctx->opcode), t2); \ - if (set_fprf) { \ - gen_compute_fprf_float64(t2); \ - } \ - 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); \ +static bool do_helper_ac(DisasContext *ctx, arg_A_tac *a, + void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64, + TCGv_i64)) +{ + TCGv_i64 t0, t1, t2; + REQUIRE_INSNS_FLAGS(ctx, FLOAT); + REQUIRE_FPU(ctx); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + gen_reset_fpstatus(); + get_fpr(t0, a->fra); + get_fpr(t1, a->frc); + helper(t2, tcg_env, t0, t1); + set_fpr(a->frt, t2); + gen_compute_fprf_float64(t2); + if (unlikely(a->rc)) { + gen_set_cr1_from_fpscr(ctx); + } + return true; } -#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AC(name, 0x3F, op2, inval, set_fprf, type); \ -_GEN_FLOAT_AC(name##s, 0x3B, op2, inval, set_fprf, type); #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ @@ -145,81 +111,33 @@ 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) \ -static void gen_f##name(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_f##name(t1, cpu_env, t0); \ - set_fpr(rD(ctx->opcode), t1); \ - if (set_fprf) { \ - 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); \ -} - -/* fadd - fadds */ -GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT); -/* fdiv - fdivs */ -GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT); -/* fmul - fmuls */ -GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT); - -/* fre */ -GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT); - -/* fres */ -GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES); - -/* frsqrte */ -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE); - -/* frsqrtes */ -static void gen_frsqrtes(DisasContext *ctx) +static bool do_helper_bs(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_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - get_fpr(t0, rB(ctx->opcode)); - gen_helper_frsqrtes(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)) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + return true; } static bool trans_FSEL(DisasContext *ctx, arg_A *a) @@ -242,18 +160,9 @@ 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; } -/* fsub - fsubs */ -GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); -/* Optional: */ - static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64)) { @@ -267,32 +176,42 @@ static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a, gen_reset_fpstatus(); get_fpr(t0, a->frb); - helper(t1, cpu_env, t0); + helper(t1, tcg_env, t0); set_fpr(a->frt, t1); gen_compute_fprf_float64(t1); if (unlikely(a->rc != 0)) { gen_set_cr1_from_fpscr(ctx); } - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - return true; } +TRANS(FADD, do_helper_ab, gen_helper_FADD); +TRANS(FADDS, do_helper_ab, gen_helper_FADDS); +TRANS(FSUB, do_helper_ab, gen_helper_FSUB); +TRANS(FSUBS, do_helper_ab, gen_helper_FSUBS); +TRANS(FDIV, do_helper_ab, gen_helper_FDIV); +TRANS(FDIVS, do_helper_ab, gen_helper_FDIVS); +TRANS(FMUL, do_helper_ac, gen_helper_FMUL); +TRANS(FMULS, do_helper_ac, gen_helper_FMULS); + +TRANS(FMADD, do_helper_acb, gen_helper_FMADD); +TRANS(FMADDS, do_helper_acb, gen_helper_FMADDS); +TRANS(FMSUB, do_helper_acb, gen_helper_FMSUB); +TRANS(FMSUBS, do_helper_acb, gen_helper_FMSUBS); + +TRANS(FNMADD, do_helper_acb, gen_helper_FNMADD); +TRANS(FNMADDS, do_helper_acb, gen_helper_FNMADDS); +TRANS(FNMSUB, do_helper_acb, gen_helper_FNMSUB); +TRANS(FNMSUBS, do_helper_acb, gen_helper_FNMSUBS); + +TRANS_FLAGS(FLOAT_EXT, FRE, do_helper_bs, gen_helper_FRE); +TRANS_FLAGS(FLOAT_FRES, FRES, do_helper_bs, gen_helper_FRES); +TRANS_FLAGS(FLOAT_FRSQRTE, FRSQRTE, do_helper_bs, gen_helper_FRSQRTE); +TRANS_FLAGS(FLOAT_FRSQRTES, FRSQRTES, do_helper_bs, gen_helper_FRSQRTES); + TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT); TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS); -/*** Floating-Point multiply-and-add ***/ -/* fmadd - fmadds */ -GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT); -/* fmsub - fmsubs */ -GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT); -/* fnmadd - fnmadds */ -GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT); -/* fnmsub - fnmsubs */ -GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); - /*** Floating-Point round & convert ***/ /* fctiw */ GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); @@ -330,38 +249,30 @@ GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); /* frim */ GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); -static void gen_ftdiv(DisasContext *ctx) +static bool trans_FTDIV(DisasContext *ctx, arg_X_bf *a) { - TCGv_i64 t0; - TCGv_i64 t1; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + TCGv_i64 t0, t1; + REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206); + REQUIRE_FPU(ctx); t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - 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); + get_fpr(t0, a->ra); + get_fpr(t1, a->rb); + gen_helper_FTDIV(cpu_crf[a->bf], t0, t1); + return true; } -static void gen_ftsqrt(DisasContext *ctx) +static bool trans_FTSQRT(DisasContext *ctx, arg_X_bf_b *a) { TCGv_i64 t0; - if (unlikely(!ctx->fpu_enabled)) { - gen_exception(ctx, POWERPC_EXCP_FPU); - return; - } + REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206); + REQUIRE_FPU(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); + get_fpr(t0, a->rb); + gen_helper_FTSQRT(cpu_crf[a->bf], t0); + return true; } - - /*** Floating-Point compare ***/ /* fcmpo */ @@ -377,14 +288,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 */ @@ -400,14 +308,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 ***/ @@ -429,8 +334,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. */ @@ -448,7 +351,6 @@ static void gen_fmr(DisasContext *ctx) if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } - tcg_temp_free_i64(t0); } /* fnabs */ @@ -469,8 +371,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 */ @@ -491,8 +391,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 */ @@ -516,9 +414,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) @@ -538,9 +433,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) @@ -559,9 +451,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 ***/ @@ -587,16 +476,13 @@ 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); } static TCGv_i64 place_from_fpscr(int rt, uint64_t mask) @@ -608,8 +494,6 @@ static TCGv_i64 place_from_fpscr(int rt, uint64_t mask) tcg_gen_andi_i64(fpscr_masked, fpscr, mask); set_fpr(rt, fpscr_masked); - tcg_temp_free_i64(fpscr_masked); - return fpscr; } @@ -621,25 +505,34 @@ static void store_fpscr_masked(TCGv_i64 fpscr, uint64_t clear_mask, tcg_gen_andi_i64(fpscr_masked, fpscr, ~clear_mask); tcg_gen_or_i64(fpscr_masked, fpscr_masked, set_mask); - gen_helper_store_fpscr(cpu_env, fpscr_masked, st_mask); + gen_helper_store_fpscr(tcg_env, fpscr_masked, st_mask); +} - tcg_temp_free_i64(fpscr_masked); +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); + } + /* + * 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) { - TCGv_i64 fpscr; - REQUIRE_FPU(ctx); gen_reset_fpstatus(); - fpscr = place_from_fpscr(a->rt, UINT64_MAX); + place_from_fpscr(a->rt, UINT64_MAX); if (a->rc) { gen_set_cr1_from_fpscr(ctx); } - - tcg_temp_free_i64(fpscr); - return true; } @@ -647,15 +540,11 @@ static bool trans_MFFSCE(DisasContext *ctx, arg_X_t *a) { TCGv_i64 fpscr; - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_FPU(ctx); gen_reset_fpstatus(); fpscr = place_from_fpscr(a->rt, UINT64_MAX); store_fpscr_masked(fpscr, FP_ENABLES, tcg_constant_i64(0), 0x0003); - - tcg_temp_free_i64(fpscr); - return true; } @@ -663,7 +552,6 @@ static bool trans_MFFSCRN(DisasContext *ctx, arg_X_tb *a) { TCGv_i64 t1, fpscr; - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_FPU(ctx); t1 = tcg_temp_new_i64(); @@ -673,10 +561,6 @@ static bool trans_MFFSCRN(DisasContext *ctx, arg_X_tb *a) 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); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(fpscr); - return true; } @@ -684,7 +568,6 @@ static bool trans_MFFSCDRN(DisasContext *ctx, arg_X_tb *a) { TCGv_i64 t1, fpscr; - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_FPU(ctx); t1 = tcg_temp_new_i64(); @@ -694,10 +577,6 @@ static bool trans_MFFSCDRN(DisasContext *ctx, arg_X_tb *a) 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); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(fpscr); - return true; } @@ -705,7 +584,6 @@ static bool trans_MFFSCRNI(DisasContext *ctx, arg_X_imm2 *a) { TCGv_i64 t1, fpscr; - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_FPU(ctx); t1 = tcg_temp_new_i64(); @@ -714,10 +592,6 @@ static bool trans_MFFSCRNI(DisasContext *ctx, arg_X_imm2 *a) 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); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(fpscr); - return true; } @@ -725,7 +599,6 @@ static bool trans_MFFSCDRNI(DisasContext *ctx, arg_X_imm3 *a) { TCGv_i64 t1, fpscr; - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_FPU(ctx); t1 = tcg_temp_new_i64(); @@ -734,26 +607,15 @@ static bool trans_MFFSCDRNI(DisasContext *ctx, arg_X_imm3 *a) 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); - - tcg_temp_free_i64(t1); - tcg_temp_free_i64(fpscr); - return true; } static bool trans_MFFSL(DisasContext *ctx, arg_X_t *a) { - TCGv_i64 fpscr; - - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_FPU(ctx); gen_reset_fpstatus(); - fpscr = place_from_fpscr(a->rt, - FP_DRN | FP_STATUS | FP_ENABLES | FP_NI | FP_RN); - - tcg_temp_free_i64(fpscr); - + place_from_fpscr(a->rt, FP_DRN | FP_STATUS | FP_ENABLES | FP_NI | FP_RN); return true; } @@ -769,10 +631,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); @@ -792,17 +651,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 */ @@ -823,22 +679,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 */ @@ -859,17 +715,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) @@ -877,7 +731,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) */ @@ -896,8 +749,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 */ @@ -930,8 +781,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 */ @@ -964,8 +813,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 */ @@ -986,9 +833,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 */ @@ -1006,8 +850,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) \ @@ -1025,8 +867,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) @@ -1034,7 +874,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) */ @@ -1053,8 +892,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 */ @@ -1087,8 +924,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 */ @@ -1121,8 +956,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: */ @@ -1131,7 +964,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); @@ -1169,8 +1001,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; } @@ -1221,14 +1051,7 @@ TRANS(STFDX, do_lsfp_X, false, true, false) TRANS(STFDUX, do_lsfp_X, true, true, false) TRANS(PSTFD, do_lsfp_PLS_D, false, true, false) -#undef _GEN_FLOAT_ACB -#undef GEN_FLOAT_ACB -#undef _GEN_FLOAT_AB -#undef GEN_FLOAT_AB -#undef _GEN_FLOAT_AC -#undef GEN_FLOAT_AC #undef GEN_FLOAT_B -#undef GEN_FLOAT_BS #undef GEN_LDF #undef GEN_LDUF diff --git a/target/ppc/translate/fp-ops.c.inc b/target/ppc/translate/fp-ops.c.inc index d4c6c4bed1..cef4b5dfcb 100644 --- a/target/ppc/translate/fp-ops.c.inc +++ b/target/ppc/translate/fp-ops.c.inc @@ -1,36 +1,6 @@ -#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type) -#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ -_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type), \ -_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type) -#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) -#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ -_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) -#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) -#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ -_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type), \ -_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type) #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type) -#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type) -GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT), -GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT), -GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES), -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE), -GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT), -GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT), -GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT), -GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT), -GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT), -GEN_HANDLER_E(ftdiv, 0x3F, 0x00, 0x04, 1, PPC_NONE, PPC2_FP_TST_ISA206), -GEN_HANDLER_E(ftsqrt, 0x3F, 0x00, 0x05, 1, PPC_NONE, PPC2_FP_TST_ISA206), GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT), GEN_HANDLER_E(fctiwu, 0x3F, 0x0E, 0x04, 0, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT), @@ -61,7 +31,6 @@ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) 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(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), diff --git a/target/ppc/translate/misc-impl.c.inc b/target/ppc/translate/misc-impl.c.inc new file mode 100644 index 0000000000..cbf82b1ea0 --- /dev/null +++ b/target/ppc/translate/misc-impl.c.inc @@ -0,0 +1,157 @@ +/* + * Power ISA decode for misc instructions + * + * Copyright (c) 2024, 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 . + */ + +/* + * Memory Barrier Instructions + */ + +static bool trans_SYNC(DisasContext *ctx, arg_X_sync *a) +{ + TCGBar bar = TCG_MO_ALL; + uint32_t l = a->l; + uint32_t sc = a->sc; + + /* + * BookE uses the msync mnemonic. This means hwsync, except in the + * 440, where it an execution serialisation point that requires all + * previous storage accesses to have been performed to memory (which + * doesn't matter for TCG). + */ + if (!(ctx->insns_flags & PPC_MEM_SYNC)) { + if (ctx->insns_flags & PPC_BOOKE) { + tcg_gen_mb(bar | TCG_BAR_SC); + return true; + } + + return false; + } + + /* + * In ISA v3.1, the L field grew one bit. Mask that out to ignore it in + * older processors. It also added the SC field, zero this to ignore + * it too. + */ + if (!(ctx->insns_flags2 & PPC2_ISA310)) { + l &= 0x3; + sc = 0; + } + + if (sc) { + /* Store syncs [stsync, stcisync, stncisync]. These ignore L. */ + bar = TCG_MO_ST_ST; + } else { + if (((l == 1) && (ctx->insns_flags2 & PPC2_MEM_LWSYNC)) || (l == 5)) { + /* lwsync, or plwsync on POWER10 and later */ + bar = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST; + } + + /* + * We may need to check for a pending TLB flush. + * + * We do this on ptesync (l == 2) on ppc64 and any sync on ppc32. + * + * Additionally, this can only happen in kernel mode however so + * check MSR_PR as well. + */ + if (((l == 2) || !(ctx->insns_flags & PPC_64B)) && !ctx->pr) { + gen_check_tlb_flush(ctx, true); + } + } + + tcg_gen_mb(bar | TCG_BAR_SC); + + return true; +} + +static bool trans_EIEIO(DisasContext *ctx, arg_EIEIO *a) +{ + TCGBar bar = TCG_MO_ALL; + + /* + * BookE uses the mbar instruction instead of eieio, which is basically + * full hwsync memory barrier, but is not execution synchronising. For + * the purpose of TCG the distinction is not relevant. + */ + if (!(ctx->insns_flags & PPC_MEM_EIEIO)) { + if ((ctx->insns_flags & PPC_BOOKE) || + (ctx->insns_flags2 & PPC2_BOOKE206)) { + tcg_gen_mb(bar | TCG_BAR_SC); + return true; + } + return false; + } + + /* + * eieio has complex semanitcs. It provides memory ordering between + * operations in the set: + * - loads from CI memory. + * - stores to CI memory. + * - stores to WT memory. + * + * It separately also orders memory for operations in the set: + * - stores to cacheble memory. + * + * It also serializes instructions: + * - dcbt and dcbst. + * + * It separately serializes: + * - tlbie and tlbsync. + * + * And separately serializes: + * - slbieg, slbiag, and slbsync. + * + * The end result is that CI memory ordering requires TCG_MO_ALL + * and it is not possible to special-case more relaxed ordering for + * cacheable accesses. TCG_BAR_SC is required to provide this + * serialization. + */ + + /* + * POWER9 has a eieio instruction variant using bit 6 as a hint to + * tell the CPU it is a store-forwarding barrier. + */ + if (ctx->opcode & 0x2000000) { + /* + * ISA says that "Reserved fields in instructions are ignored + * by the processor". So ignore the bit 6 on non-POWER9 CPU but + * as this is not an instruction software should be using, + * complain to the user. + */ + if (!(ctx->insns_flags2 & PPC2_ISA300)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" + TARGET_FMT_lx "\n", ctx->cia); + } else { + bar = TCG_MO_ST_LD; + } + } + + tcg_gen_mb(bar | TCG_BAR_SC); + + return true; +} + +static bool trans_ATTN(DisasContext *ctx, arg_ATTN *a) +{ +#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) + gen_helper_attn(tcg_env); + return true; +#else + return false; +#endif +} diff --git a/target/ppc/translate/processor-ctrl-impl.c.inc b/target/ppc/translate/processor-ctrl-impl.c.inc index cc7a50d579..8abbb89630 100644 --- a/target/ppc/translate/processor-ctrl-impl.c.inc +++ b/target/ppc/translate/processor-ctrl-impl.c.inc @@ -35,9 +35,9 @@ static bool trans_MSGCLR(DisasContext *ctx, arg_X_rb *a) #if !defined(CONFIG_USER_ONLY) if (is_book3s_arch2x(ctx)) { - gen_helper_book3s_msgclr(cpu_env, cpu_gpr[a->rb]); + gen_helper_book3s_msgclr(tcg_env, cpu_gpr[a->rb]); } else { - gen_helper_msgclr(cpu_env, cpu_gpr[a->rb]); + gen_helper_msgclr(tcg_env, cpu_gpr[a->rb]); } #else qemu_build_not_reached(); @@ -59,7 +59,7 @@ static bool trans_MSGSND(DisasContext *ctx, arg_X_rb *a) #if !defined(CONFIG_USER_ONLY) if (is_book3s_arch2x(ctx)) { - gen_helper_book3s_msgsnd(cpu_gpr[a->rb]); + gen_helper_book3s_msgsnd(tcg_env, cpu_gpr[a->rb]); } else { gen_helper_msgsnd(cpu_gpr[a->rb]); } @@ -75,7 +75,7 @@ static bool trans_MSGCLRP(DisasContext *ctx, arg_X_rb *a) REQUIRE_INSNS_FLAGS2(ctx, ISA207S); REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_book3s_msgclrp(cpu_env, cpu_gpr[a->rb]); + gen_helper_book3s_msgclrp(tcg_env, cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif @@ -88,7 +88,7 @@ static bool trans_MSGSNDP(DisasContext *ctx, arg_X_rb *a) REQUIRE_INSNS_FLAGS2(ctx, ISA207S); REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_book3s_msgsndp(cpu_env, cpu_gpr[a->rb]); + gen_helper_book3s_msgsndp(tcg_env, cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif 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 index 6ea1d22ef9..b8b4454663 100644 --- a/target/ppc/translate/storage-ctrl-impl.c.inc +++ b/target/ppc/translate/storage-ctrl-impl.c.inc @@ -30,7 +30,7 @@ static bool trans_SLBIE(DisasContext *ctx, arg_SLBIE *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBIE(cpu_env, cpu_gpr[a->rb]); + gen_helper_SLBIE(tcg_env, cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif @@ -44,7 +44,7 @@ static bool trans_SLBIEG(DisasContext *ctx, arg_SLBIEG *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBIEG(cpu_env, cpu_gpr[a->rb]); + gen_helper_SLBIEG(tcg_env, cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif @@ -58,7 +58,7 @@ static bool trans_SLBIA(DisasContext *ctx, arg_SLBIA *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBIA(cpu_env, tcg_constant_i32(a->ih)); + gen_helper_SLBIA(tcg_env, tcg_constant_i32(a->ih)); #else qemu_build_not_reached(); #endif @@ -72,7 +72,7 @@ static bool trans_SLBIAG(DisasContext *ctx, arg_SLBIAG *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBIAG(cpu_env, cpu_gpr[a->rs], tcg_constant_i32(a->l)); + gen_helper_SLBIAG(tcg_env, cpu_gpr[a->rs], tcg_constant_i32(a->l)); #else qemu_build_not_reached(); #endif @@ -86,7 +86,7 @@ static bool trans_SLBMTE(DisasContext *ctx, arg_SLBMTE *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBMTE(cpu_env, cpu_gpr[a->rb], cpu_gpr[a->rt]); + gen_helper_SLBMTE(tcg_env, cpu_gpr[a->rb], cpu_gpr[a->rt]); #else qemu_build_not_reached(); #endif @@ -100,7 +100,7 @@ static bool trans_SLBMFEV(DisasContext *ctx, arg_SLBMFEV *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBMFEV(cpu_gpr[a->rt], cpu_env, cpu_gpr[a->rb]); + gen_helper_SLBMFEV(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif @@ -114,7 +114,7 @@ static bool trans_SLBMFEE(DisasContext *ctx, arg_SLBMFEE *a) REQUIRE_SV(ctx); #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) - gen_helper_SLBMFEE(cpu_gpr[a->rt], cpu_env, cpu_gpr[a->rb]); + gen_helper_SLBMFEE(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]); #else qemu_build_not_reached(); #endif @@ -137,7 +137,7 @@ static bool trans_SLBFEE(DisasContext *ctx, arg_SLBFEE *a) gen_inval_exception(ctx, POWERPC_EXCP_PRIV_REG); return true; } - gen_helper_SLBFEE(cpu_gpr[a->rt], cpu_env, + gen_helper_SLBFEE(cpu_gpr[a->rt], tcg_env, cpu_gpr[a->rb]); l1 = gen_new_label(); l2 = gen_new_label(); @@ -211,8 +211,7 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) if (!local && NARROW_MODE(ctx)) { TCGv t0 = tcg_temp_new(); tcg_gen_ext32u_tl(t0, cpu_gpr[rb]); - gen_helper_tlbie(cpu_env, t0); - tcg_temp_free(t0); + gen_helper_tlbie(tcg_env, t0); #if defined(TARGET_PPC64) /* @@ -220,16 +219,23 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) * otherwise the results are undefined. */ } else if (a->r) { - gen_helper_tlbie_isa300(cpu_env, cpu_gpr[rb], cpu_gpr[a->rs], + 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)); + if (!local) { + /* + * Global TLB flush uses async-work which must run before the + * next instruction, so this must be the last in the TB. + */ + ctx->base.is_jmp = DISAS_EXIT_UPDATE; + } return true; #endif } else { - gen_helper_tlbie(cpu_env, cpu_gpr[rb]); + gen_helper_tlbie(tcg_env, cpu_gpr[rb]); } if (local) { @@ -237,10 +243,9 @@ static bool do_tlbie(DisasContext *ctx, arg_X_tlbie *a, bool local) } t1 = tcg_temp_new_i32(); - tcg_gen_ld_i32(t1, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); + 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, cpu_env, offsetof(CPUPPCState, tlb_need_flush)); - tcg_temp_free_i32(t1); + tcg_gen_st_i32(t1, tcg_env, offsetof(CPUPPCState, tlb_need_flush)); return true; #endif diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 764b76dcc6..70d0ad2e71 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -10,137 +10,100 @@ 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; } -#define GEN_VR_LDX(name, opc2, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 avr; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - avr = tcg_temp_new_i64(); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - tcg_gen_andi_tl(EA, EA, ~0xf); \ - /* \ - * We only need to swap high and low halves. gen_qemu_ld64_i64 \ - * does necessary 64-bit byteswap already. \ - */ \ - if (ctx->le_mode) { \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, false); \ - tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, true); \ - } else { \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, true); \ - tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, avr, EA); \ - set_avr64(rD(ctx->opcode), avr, false); \ - } \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(avr); \ +static inline void get_avr64(TCGv_i64 dst, int regno, bool high) +{ + tcg_gen_ld_i64(dst, tcg_env, avr64_offset(regno, high)); } -#define GEN_VR_STX(name, opc2, opc3) \ -static void gen_st##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 avr; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - avr = tcg_temp_new_i64(); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - tcg_gen_andi_tl(EA, EA, ~0xf); \ - /* \ - * We only need to swap high and low halves. gen_qemu_st64_i64 \ - * does necessary 64-bit byteswap already. \ - */ \ - if (ctx->le_mode) { \ - get_avr64(avr, rD(ctx->opcode), false); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - tcg_gen_addi_tl(EA, EA, 8); \ - get_avr64(avr, rD(ctx->opcode), true); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - } else { \ - get_avr64(avr, rD(ctx->opcode), true); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - tcg_gen_addi_tl(EA, EA, 8); \ - get_avr64(avr, rD(ctx->opcode), false); \ - gen_qemu_st64_i64(ctx, avr, EA); \ - } \ - tcg_temp_free(EA); \ - tcg_temp_free_i64(avr); \ +static inline void set_avr64(int regno, TCGv_i64 src, bool high) +{ + tcg_gen_st_i64(src, tcg_env, avr64_offset(regno, high)); } -#define GEN_VR_LVE(name, opc2, opc3, size) \ -static void gen_lve##name(DisasContext *ctx) \ - { \ - TCGv EA; \ - TCGv_ptr rs; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - if (size > 1) { \ - 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); \ +static inline void get_avr_full(TCGv_i128 dst, int regno) +{ + tcg_gen_ld_i128(dst, tcg_env, avr_full_offset(regno)); +} + +static inline void set_avr_full(int regno, TCGv_i128 src) +{ + tcg_gen_st_i128(src, tcg_env, avr_full_offset(regno)); +} + +static bool trans_LVX(DisasContext *ctx, arg_X *a) +{ + TCGv EA; + TCGv_i128 avr; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + avr = tcg_temp_new_i128(); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_andi_tl(EA, EA, ~0xf); + tcg_gen_qemu_ld_i128(avr, EA, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR)); + set_avr_full(a->rt, avr); + return true; +} + +/* As we don't emulate the cache, lvxl is strictly equivalent to lvx */ +QEMU_FLATTEN +static bool trans_LVXL(DisasContext *ctx, arg_LVXL *a) +{ + return trans_LVX(ctx, a); +} + +static bool trans_STVX(DisasContext *ctx, arg_STVX *a) +{ + TCGv EA; + TCGv_i128 avr; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + avr = tcg_temp_new_i128(); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_andi_tl(EA, EA, ~0xf); + get_avr_full(avr, a->rt); + tcg_gen_qemu_st_i128(avr, EA, ctx->mem_idx, + DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR)); + return true; +} + +/* As we don't emulate the cache, stvxl is strictly equivalent to stvx */ +QEMU_FLATTEN +static bool trans_STVXL(DisasContext *ctx, arg_STVXL *a) +{ + return trans_STVX(ctx, a); +} + +static bool do_ldst_ve_X(DisasContext *ctx, arg_X *a, int size, + void (*helper)(TCGv_env, TCGv_ptr, TCGv)) +{ + TCGv EA; + TCGv_ptr vrt; + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + if (size > 1) { + tcg_gen_andi_tl(EA, EA, ~(size - 1)); } + vrt = gen_avr_ptr(a->rt); + helper(tcg_env, vrt, EA); + return true; +} -#define GEN_VR_STVE(name, opc2, opc3, size) \ -static void gen_stve##name(DisasContext *ctx) \ - { \ - TCGv EA; \ - TCGv_ptr rs; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - if (size > 1) { \ - 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); \ - } +TRANS(LVEBX, do_ldst_ve_X, 1, gen_helper_LVEBX); +TRANS(LVEHX, do_ldst_ve_X, 2, gen_helper_LVEHX); +TRANS(LVEWX, do_ldst_ve_X, 4, gen_helper_LVEWX); -GEN_VR_LDX(lvx, 0x07, 0x03); -/* As we don't emulate the cache, lvxl is stricly equivalent to lvx */ -GEN_VR_LDX(lvxl, 0x07, 0x0B); - -GEN_VR_LVE(bx, 0x07, 0x00, 1); -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 */ -GEN_VR_STX(svxl, 0x07, 0x0F); - -GEN_VR_STVE(bx, 0x07, 0x04, 1); -GEN_VR_STVE(hx, 0x07, 0x05, 2); -GEN_VR_STVE(wx, 0x07, 0x06, 4); +TRANS(STVEBX, do_ldst_ve_X, 1, gen_helper_STVEBX); +TRANS(STVEHX, do_ldst_ve_X, 2, gen_helper_STVEHX); +TRANS(STVEWX, do_ldst_ve_X, 4, gen_helper_STVEWX); static void gen_mfvscr(DisasContext *ctx) { @@ -154,11 +117,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 +138,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); @@ -257,16 +213,6 @@ static void glue(gen_, name)(DisasContext *ctx) \ 16, 16); \ } -/* Logical operations */ -GEN_VXFORM_V(vand, MO_64, tcg_gen_gvec_and, 2, 16); -GEN_VXFORM_V(vandc, MO_64, tcg_gen_gvec_andc, 2, 17); -GEN_VXFORM_V(vor, MO_64, tcg_gen_gvec_or, 2, 18); -GEN_VXFORM_V(vxor, MO_64, tcg_gen_gvec_xor, 2, 19); -GEN_VXFORM_V(vnor, MO_64, tcg_gen_gvec_nor, 2, 20); -GEN_VXFORM_V(veqv, MO_64, tcg_gen_gvec_eqv, 2, 26); -GEN_VXFORM_V(vnand, MO_64, tcg_gen_gvec_nand, 2, 22); -GEN_VXFORM_V(vorc, MO_64, tcg_gen_gvec_orc, 2, 21); - #define GEN_VXFORM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ @@ -279,9 +225,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 +248,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 +264,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 +336,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); @@ -415,22 +350,6 @@ GEN_VXFORM_V(vsububm, MO_8, tcg_gen_gvec_sub, 0, 16); GEN_VXFORM_V(vsubuhm, MO_16, tcg_gen_gvec_sub, 0, 17); GEN_VXFORM_V(vsubuwm, MO_32, tcg_gen_gvec_sub, 0, 18); GEN_VXFORM_V(vsubudm, MO_64, tcg_gen_gvec_sub, 0, 19); -GEN_VXFORM_V(vmaxub, MO_8, tcg_gen_gvec_umax, 1, 0); -GEN_VXFORM_V(vmaxuh, MO_16, tcg_gen_gvec_umax, 1, 1); -GEN_VXFORM_V(vmaxuw, MO_32, tcg_gen_gvec_umax, 1, 2); -GEN_VXFORM_V(vmaxud, MO_64, tcg_gen_gvec_umax, 1, 3); -GEN_VXFORM_V(vmaxsb, MO_8, tcg_gen_gvec_smax, 1, 4); -GEN_VXFORM_V(vmaxsh, MO_16, tcg_gen_gvec_smax, 1, 5); -GEN_VXFORM_V(vmaxsw, MO_32, tcg_gen_gvec_smax, 1, 6); -GEN_VXFORM_V(vmaxsd, MO_64, tcg_gen_gvec_smax, 1, 7); -GEN_VXFORM_V(vminub, MO_8, tcg_gen_gvec_umin, 1, 8); -GEN_VXFORM_V(vminuh, MO_16, tcg_gen_gvec_umin, 1, 9); -GEN_VXFORM_V(vminuw, MO_32, tcg_gen_gvec_umin, 1, 10); -GEN_VXFORM_V(vminud, MO_64, tcg_gen_gvec_umin, 1, 11); -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(vmrghb, 6, 0); GEN_VXFORM(vmrghh, 6, 1); GEN_VXFORM(vmrghw, 6, 2); @@ -457,9 +376,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) @@ -480,10 +396,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); } /* @@ -493,15 +405,17 @@ static void trans_vmrgow(DisasContext *ctx) * Let X be the 32-byte value 0x00 || 0x01 || 0x02 || ... || 0x1E || 0x1F. * Bytes sh:sh+15 of X are placed into vD. */ -static void trans_lvsl(DisasContext *ctx) +static bool trans_LVSL(DisasContext *ctx, arg_LVSL *a) { - int VT = rD(ctx->opcode); TCGv_i64 result = tcg_temp_new_i64(); TCGv_i64 sh = tcg_temp_new_i64(); TCGv EA = tcg_temp_new(); + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); + /* Get sh(from description) by anding EA with 0xf. */ - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_extu_tl_i64(sh, EA); tcg_gen_andi_i64(sh, sh, 0xfULL); @@ -511,17 +425,14 @@ static void trans_lvsl(DisasContext *ctx) */ tcg_gen_muli_i64(sh, sh, 0x0101010101010101ULL); tcg_gen_addi_i64(result, sh, 0x0001020304050607ull); - set_avr64(VT, result, true); + set_avr64(a->rt, result, true); /* * Create bytes sh+8:sh+15 of X(from description) and place them in * lower doubleword of vD. */ 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); + set_avr64(a->rt, result, false); + return true; } /* @@ -531,16 +442,17 @@ static void trans_lvsl(DisasContext *ctx) * Let X be the 32-byte value 0x00 || 0x01 || 0x02 || ... || 0x1E || 0x1F. * Bytes (16-sh):(31-sh) of X are placed into vD. */ -static void trans_lvsr(DisasContext *ctx) +static bool trans_LVSR(DisasContext *ctx, arg_LVSR *a) { - int VT = rD(ctx->opcode); TCGv_i64 result = tcg_temp_new_i64(); TCGv_i64 sh = tcg_temp_new_i64(); TCGv EA = tcg_temp_new(); + REQUIRE_INSNS_FLAGS(ctx, ALTIVEC); + REQUIRE_VECTOR(ctx); /* Get sh(from description) by anding EA with 0xf. */ - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_extu_tl_i64(sh, EA); tcg_gen_andi_i64(sh, sh, 0xfULL); @@ -550,17 +462,14 @@ static void trans_lvsr(DisasContext *ctx) */ tcg_gen_muli_i64(sh, sh, 0x0101010101010101ULL); tcg_gen_subfi_i64(result, 0x1011121314151617ULL, sh); - set_avr64(VT, result, true); + set_avr64(a->rt, result, true); /* * Create bytes (24-sh):(32-sh) of X(from description) and place them in * lower doubleword of vD. */ 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); + set_avr64(a->rt, result, false); + return true; } /* @@ -603,11 +512,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); } /* @@ -649,11 +553,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); } /* @@ -722,13 +621,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]); } /* @@ -747,14 +639,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); } /* @@ -779,8 +669,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); @@ -821,6 +709,37 @@ TRANS_FLAGS(ALTIVEC, VRLH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_rotlv) TRANS_FLAGS(ALTIVEC, VRLW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_rotlv) TRANS_FLAGS2(ALTIVEC_207, VRLD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_rotlv) +/* Logical operations */ +TRANS_FLAGS(ALTIVEC, VAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_and); +TRANS_FLAGS(ALTIVEC, VANDC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_andc); +TRANS_FLAGS(ALTIVEC, VOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_or); +TRANS_FLAGS(ALTIVEC, VXOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_xor); +TRANS_FLAGS(ALTIVEC, VNOR, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nor); +TRANS_FLAGS2(ALTIVEC_207, VEQV, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_eqv); +TRANS_FLAGS2(ALTIVEC_207, VNAND, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_nand); +TRANS_FLAGS2(ALTIVEC_207, VORC, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_orc); + +/* Integer Max/Min operations */ +TRANS_FLAGS(ALTIVEC, VMAXUB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_umax); +TRANS_FLAGS(ALTIVEC, VMAXUH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_umax); +TRANS_FLAGS(ALTIVEC, VMAXUW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_umax); +TRANS_FLAGS2(ALTIVEC_207, VMAXUD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_umax); + +TRANS_FLAGS(ALTIVEC, VMAXSB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_smax); +TRANS_FLAGS(ALTIVEC, VMAXSH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_smax); +TRANS_FLAGS(ALTIVEC, VMAXSW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_smax); +TRANS_FLAGS2(ALTIVEC_207, VMAXSD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_smax); + +TRANS_FLAGS(ALTIVEC, VMINUB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_umin); +TRANS_FLAGS(ALTIVEC, VMINUH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_umin); +TRANS_FLAGS(ALTIVEC, VMINUW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_umin); +TRANS_FLAGS2(ALTIVEC_207, VMINUD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_umin); + +TRANS_FLAGS(ALTIVEC, VMINSB, do_vector_gvec3_VX, MO_8, tcg_gen_gvec_smin); +TRANS_FLAGS(ALTIVEC, VMINSH, do_vector_gvec3_VX, MO_16, tcg_gen_gvec_smin); +TRANS_FLAGS(ALTIVEC, VMINSW, do_vector_gvec3_VX, MO_32, tcg_gen_gvec_smin); +TRANS_FLAGS2(ALTIVEC_207, VMINSD, do_vector_gvec3_VX, MO_64, tcg_gen_gvec_smin); + static TCGv_vec do_vrl_mask_vec(unsigned vece, TCGv_vec vrb) { TCGv_vec t0 = tcg_temp_new_vec_matching(vrb), @@ -849,9 +768,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; } @@ -870,9 +786,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) @@ -926,10 +839,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) @@ -978,7 +887,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); @@ -989,7 +897,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 { @@ -1024,13 +935,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; } @@ -1083,11 +987,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, @@ -1149,14 +1048,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; } @@ -1164,59 +1055,6 @@ TRANS(VRLQ, do_vector_rotl_quad, false, false) TRANS(VRLQNM, do_vector_rotl_quad, true, false) TRANS(VRLQMI, do_vector_rotl_quad, false, true) -#define GEN_VXFORM_SAT(NAME, VECE, NORM, SAT, OPC2, OPC3) \ -static void glue(glue(gen_, NAME), _vec)(unsigned vece, TCGv_vec t, \ - TCGv_vec sat, TCGv_vec a, \ - TCGv_vec b) \ -{ \ - TCGv_vec x = tcg_temp_new_vec_matching(t); \ - glue(glue(tcg_gen_, NORM), _vec)(VECE, x, a, b); \ - 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) \ -{ \ - static const TCGOpcode vecop_list[] = { \ - glue(glue(INDEX_op_, NORM), _vec), \ - glue(glue(INDEX_op_, SAT), _vec), \ - INDEX_op_cmp_vec, 0 \ - }; \ - static const GVecGen4 g = { \ - .fniv = glue(glue(gen_, NAME), _vec), \ - .fno = glue(gen_helper_, NAME), \ - .opt_opc = vecop_list, \ - .write_aofs = true, \ - .vece = VECE, \ - }; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - tcg_gen_gvec_4(avr_full_offset(rD(ctx->opcode)), \ - offsetof(CPUPPCState, vscr_sat), \ - avr_full_offset(rA(ctx->opcode)), \ - avr_full_offset(rB(ctx->opcode)), \ - 16, 16, &g); \ -} - -GEN_VXFORM_SAT(vaddubs, MO_8, add, usadd, 0, 8); -GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ - vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) -GEN_VXFORM_SAT(vadduhs, MO_16, add, usadd, 0, 9); -GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ - vmul10euq, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_SAT(vadduws, MO_32, add, usadd, 0, 10); -GEN_VXFORM_SAT(vaddsbs, MO_8, add, ssadd, 0, 12); -GEN_VXFORM_SAT(vaddshs, MO_16, add, ssadd, 0, 13); -GEN_VXFORM_SAT(vaddsws, MO_32, add, ssadd, 0, 14); -GEN_VXFORM_SAT(vsububs, MO_8, sub, ussub, 0, 24); -GEN_VXFORM_SAT(vsubuhs, MO_16, sub, ussub, 0, 25); -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_TRANS(vsl, 2, 7); GEN_VXFORM_TRANS(vsr, 2, 11); GEN_VXFORM_ENV(vpkuhum, 7, 0); @@ -1249,8 +1087,6 @@ GEN_VXFORM_TRANS_DUAL(vmrgow, PPC_NONE, PPC2_ALTIVEC_207, GEN_VXFORM_HETRO(vextubrx, 6, 28) GEN_VXFORM_HETRO(vextuhrx, 6, 29) GEN_VXFORM_HETRO(vextuwrx, 6, 30) -GEN_VXFORM_TRANS(lvsl, 6, 31) -GEN_VXFORM_TRANS(lvsr, 6, 32) GEN_VXFORM_TRANS_DUAL(vmrgew, PPC_NONE, PPC2_ALTIVEC_207, vextuwrx, PPC_NONE, PPC2_ISA300) @@ -1265,10 +1101,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) \ @@ -1277,7 +1110,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. */ @@ -1325,10 +1158,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) @@ -1377,9 +1206,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) @@ -1442,8 +1268,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); @@ -1453,11 +1278,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; } @@ -1471,15 +1291,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); @@ -1489,11 +1308,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; } @@ -1508,8 +1322,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(); @@ -1536,9 +1350,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; } @@ -1581,8 +1392,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) \ @@ -1596,9 +1405,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) \ @@ -1612,8 +1419,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) \ @@ -1626,7 +1431,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); @@ -1649,13 +1453,12 @@ 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 iteractions for MO_32 and 3 for MO_64 */ + /* 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)); - tcg_temp_free_vec(tmp); } /* vprtybw */ @@ -1746,13 +1549,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) \ @@ -1773,9 +1573,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); @@ -1922,12 +1719,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; } @@ -1949,12 +1740,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; } @@ -1982,32 +1768,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); @@ -2016,10 +1793,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, @@ -2035,7 +1809,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); @@ -2059,11 +1832,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, @@ -2120,12 +1890,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) @@ -2148,16 +1914,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; } @@ -2181,16 +1941,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; } @@ -2223,8 +1977,6 @@ 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; } @@ -2278,12 +2030,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; } @@ -2304,9 +2050,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; } @@ -2367,12 +2110,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; } @@ -2394,9 +2131,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; } @@ -2427,28 +2161,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; } @@ -2473,12 +2204,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; } @@ -2531,12 +2257,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; } @@ -2556,14 +2276,10 @@ 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(vmaddfp, vnmsubfp, 23) @@ -2579,11 +2295,6 @@ 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; } @@ -2653,12 +2364,7 @@ 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; } @@ -2751,8 +2457,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; } @@ -2796,14 +2500,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) \ @@ -2820,13 +2519,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) @@ -2902,26 +2597,14 @@ static void gen_xpnd04_2(DisasContext *ctx) } } - -GEN_VXFORM_DUAL(vsubsws, PPC_ALTIVEC, PPC_NONE, \ - xpnd04_2, PPC_NONE, PPC2_ISA300) - GEN_VXFORM_DUAL(vsububm, PPC_ALTIVEC, PPC_NONE, \ bcdadd, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vsububs, PPC_ALTIVEC, PPC_NONE, \ - bcdadd, PPC_NONE, PPC2_ALTIVEC_207) GEN_VXFORM_DUAL(vsubuhm, PPC_ALTIVEC, PPC_NONE, \ bcdsub, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vsubuhs, PPC_ALTIVEC, PPC_NONE, \ - bcdsub, PPC_NONE, PPC2_ALTIVEC_207) -GEN_VXFORM_DUAL(vaddshs, PPC_ALTIVEC, PPC_NONE, \ - bcdcpsgn, PPC_NONE, PPC2_ISA300) GEN_VXFORM_DUAL(vsubudm, PPC2_ALTIVEC_207, PPC_NONE, \ bcds, PPC_NONE, PPC2_ISA300) 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) static void gen_vsbox(DisasContext *ctx) { @@ -2933,8 +2616,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) @@ -2958,11 +2639,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) @@ -3077,12 +2755,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; } @@ -3128,14 +2800,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; } @@ -3149,10 +2813,6 @@ 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; } @@ -3221,6 +2881,180 @@ static bool do_vx_vaddsubcuw(DisasContext *ctx, arg_VX *a, int add) TRANS(VSUBCUW, do_vx_vaddsubcuw, 0) TRANS(VADDCUW, do_vx_vaddsubcuw, 1) +/* Integer Add/Sub Saturate Instructions */ +static inline void do_vadd_vsub_sat +( + unsigned vece, TCGv_vec t, TCGv_vec qc, TCGv_vec a, TCGv_vec b, + void (*norm_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec), + void (*sat_op)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + norm_op(vece, x, a, b); + sat_op(vece, t, a, b); + tcg_gen_xor_vec(vece, x, x, t); + tcg_gen_or_vec(vece, qc, qc, x); +} + +static void gen_vadd_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_add_vec, tcg_gen_usadd_vec); +} + +static void gen_vadd_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_add_vec, tcg_gen_ssadd_vec); +} + +static void gen_vsub_sat_u(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_sub_vec, tcg_gen_ussub_vec); +} + +static void gen_vsub_sat_s(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + do_vadd_vsub_sat(vece, t, sat, a, b, tcg_gen_sub_vec, tcg_gen_sssub_vec); +} + +/* + * Signed/Unsigned add/sub helper ops for byte/halfword/word + * GVecGen4 struct variants. + */ +static const TCGOpcode vecop_list_sub_u[] = { + INDEX_op_sub_vec, INDEX_op_ussub_vec, 0 +}; +static const TCGOpcode vecop_list_sub_s[] = { + INDEX_op_sub_vec, INDEX_op_sssub_vec, 0 +}; +static const TCGOpcode vecop_list_add_u[] = { + INDEX_op_add_vec, INDEX_op_usadd_vec, 0 +}; +static const TCGOpcode vecop_list_add_s[] = { + INDEX_op_add_vec, INDEX_op_ssadd_vec, 0 +}; + +static const GVecGen4 op_vsububs = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUBS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vaddubs = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUBS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vsubuhs = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUHS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vadduhs = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUHS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vsubuws = { + .fniv = gen_vsub_sat_u, + .fno = gen_helper_VSUBUWS, + .opt_opc = vecop_list_sub_u, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vadduws = { + .fniv = gen_vadd_sat_u, + .fno = gen_helper_VADDUWS, + .opt_opc = vecop_list_add_u, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vsubsbs = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSBS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vaddsbs = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSBS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_8 +}; + +static const GVecGen4 op_vsubshs = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSHS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vaddshs = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSHS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_16 +}; + +static const GVecGen4 op_vsubsws = { + .fniv = gen_vsub_sat_s, + .fno = gen_helper_VSUBSWS, + .opt_opc = vecop_list_sub_s, + .write_aofs = true, + .vece = MO_32 +}; + +static const GVecGen4 op_vaddsws = { + .fniv = gen_vadd_sat_s, + .fno = gen_helper_VADDSWS, + .opt_opc = vecop_list_add_s, + .write_aofs = true, + .vece = MO_32 +}; + +static bool do_vx_vadd_vsub_sat(DisasContext *ctx, arg_VX *a, const GVecGen4 *op) +{ + REQUIRE_VECTOR(ctx); + tcg_gen_gvec_4(avr_full_offset(a->vrt), offsetof(CPUPPCState, vscr_sat), + avr_full_offset(a->vra), avr_full_offset(a->vrb), + 16, 16, op); + + return true; +} + +TRANS_FLAGS(ALTIVEC, VSUBUBS, do_vx_vadd_vsub_sat, &op_vsububs) +TRANS_FLAGS(ALTIVEC, VSUBUHS, do_vx_vadd_vsub_sat, &op_vsubuhs) +TRANS_FLAGS(ALTIVEC, VSUBUWS, do_vx_vadd_vsub_sat, &op_vsubuws) +TRANS_FLAGS(ALTIVEC, VSUBSBS, do_vx_vadd_vsub_sat, &op_vsubsbs) +TRANS_FLAGS(ALTIVEC, VSUBSHS, do_vx_vadd_vsub_sat, &op_vsubshs) +TRANS_FLAGS(ALTIVEC, VSUBSWS, do_vx_vadd_vsub_sat, &op_vsubsws) +TRANS_FLAGS(ALTIVEC, VADDUBS, do_vx_vadd_vsub_sat, &op_vaddubs) +TRANS_FLAGS(ALTIVEC, VADDUHS, do_vx_vadd_vsub_sat, &op_vadduhs) +TRANS_FLAGS(ALTIVEC, VADDUWS, do_vx_vadd_vsub_sat, &op_vadduws) +TRANS_FLAGS(ALTIVEC, VADDSBS, do_vx_vadd_vsub_sat, &op_vaddsbs) +TRANS_FLAGS(ALTIVEC, VADDSHS, do_vx_vadd_vsub_sat, &op_vaddshs) +TRANS_FLAGS(ALTIVEC, VADDSWS, do_vx_vadd_vsub_sat, &op_vaddsws) + static bool do_vx_vmuleo(DisasContext *ctx, arg_VX *a, bool even, void (*gen_mul)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { @@ -3237,12 +3071,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; } @@ -3302,10 +3130,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) @@ -3318,8 +3142,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, @@ -3344,13 +3166,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) @@ -3368,7 +3184,6 @@ static void do_vavg(unsigned vece, TCGv_vec t, TCGv_vec a, TCGv_vec b, gen_shr_vec(vece, b, b, 1); tcg_gen_add_vec(vece, t, a, b); tcg_gen_add_vec(vece, t, t, tmp); - tcg_temp_free_vec(tmp); } QEMU_FLATTEN @@ -3538,8 +3353,6 @@ static void NAME(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) \ tcg_gen_movi_i32(t1, 0); \ tcg_gen_movcond_i32(TCG_COND_NE, b, t0, t1, t0, b); \ DIV(t, a, b); \ - tcg_temp_free_i32(t0); \ - tcg_temp_free_i32(t1); \ } #define DIVU64(NAME, DIV) \ @@ -3564,8 +3377,6 @@ static void NAME(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b) \ tcg_gen_movi_i64(t1, 0); \ tcg_gen_movcond_i64(TCG_COND_NE, b, t0, t1, t0, b); \ DIV(t, a, b); \ - tcg_temp_free_i64(t0); \ - tcg_temp_free_i64(t1); \ } DIVS32(do_divsw, tcg_gen_div_i32) @@ -3596,9 +3407,6 @@ static void do_dives_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) /* if quotient doesn't fit in 32 bits the result is undefined */ tcg_gen_extrl_i64_i32(t, val1); - - tcg_temp_free_i64(val1); - tcg_temp_free_i64(val2); } static void do_diveu_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) @@ -3617,9 +3425,6 @@ static void do_diveu_i32(TCGv_i32 t, TCGv_i32 a, TCGv_i32 b) /* if quotient doesn't fit in 32 bits the result is undefined */ tcg_gen_extrl_i64_i32(t, val1); - - tcg_temp_free_i64(val1); - tcg_temp_free_i64(val2); } DIVS32(do_divesw, do_dives_i32) @@ -3649,13 +3454,6 @@ TRANS_FLAGS2(ISA310, VMODUQ, do_vx_helper, gen_helper_VMODUQ) #undef DIVS64 #undef DIVU64 -#undef GEN_VR_LDX -#undef GEN_VR_STX -#undef GEN_VR_LVE -#undef GEN_VR_STVE - -#undef GEN_VX_LOGICAL -#undef GEN_VX_LOGICAL_207 #undef GEN_VXFORM #undef GEN_VXFORM_207 #undef GEN_VXFORM_DUAL diff --git a/target/ppc/translate/vmx-ops.c.inc b/target/ppc/translate/vmx-ops.c.inc index 33fec8aca4..e28958a126 100644 --- a/target/ppc/translate/vmx-ops.c.inc +++ b/target/ppc/translate/vmx-ops.c.inc @@ -1,37 +1,3 @@ -#define GEN_VR_LDX(name, opc2, opc3) \ -GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_STX(name, opc2, opc3) \ -GEN_HANDLER(st##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_LVE(name, opc2, opc3) \ - GEN_HANDLER(lve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -#define GEN_VR_STVE(name, opc2, opc3) \ - GEN_HANDLER(stve##name, 0x1F, opc2, opc3, 0x00000001, PPC_ALTIVEC) -GEN_VR_LDX(lvx, 0x07, 0x03), -GEN_VR_LDX(lvxl, 0x07, 0x0B), -GEN_VR_LVE(bx, 0x07, 0x00), -GEN_VR_LVE(hx, 0x07, 0x01), -GEN_VR_LVE(wx, 0x07, 0x02), -GEN_VR_STX(svx, 0x07, 0x07), -GEN_VR_STX(svxl, 0x07, 0x0F), -GEN_VR_STVE(bx, 0x07, 0x04), -GEN_VR_STVE(hx, 0x07, 0x05), -GEN_VR_STVE(wx, 0x07, 0x06), - -#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ -GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) - -#define GEN_VX_LOGICAL_207(name, tcg_op, opc2, opc3) \ -GEN_HANDLER_E(name, 0x04, opc2, opc3, 0x00000000, PPC_NONE, PPC2_ALTIVEC_207) - -GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16), -GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17), -GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18), -GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19), -GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20), -GEN_VX_LOGICAL_207(veqv, tcg_gen_eqv_i64, 2, 26), -GEN_VX_LOGICAL_207(vnand, tcg_gen_nand_i64, 2, 22), -GEN_VX_LOGICAL_207(vorc, tcg_gen_orc_i64, 2, 21), - #define GEN_VXFORM(name, opc2, opc3) \ GEN_HANDLER(name, 0x04, opc2, opc3, 0x00000000, PPC_ALTIVEC) @@ -67,22 +33,6 @@ GEN_VXFORM_DUAL(vsubuhm, bcdsub, 0, 17, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vsubuwm, bcdus, 0, 18, PPC_ALTIVEC, PPC2_ISA300), GEN_VXFORM_DUAL(vsubudm, bcds, 0, 19, PPC2_ALTIVEC_207, PPC2_ISA300), GEN_VXFORM_300(bcds, 0, 27), -GEN_VXFORM(vmaxub, 1, 0), -GEN_VXFORM(vmaxuh, 1, 1), -GEN_VXFORM(vmaxuw, 1, 2), -GEN_VXFORM_207(vmaxud, 1, 3), -GEN_VXFORM(vmaxsb, 1, 4), -GEN_VXFORM(vmaxsh, 1, 5), -GEN_VXFORM(vmaxsw, 1, 6), -GEN_VXFORM_207(vmaxsd, 1, 7), -GEN_VXFORM(vminub, 1, 8), -GEN_VXFORM(vminuh, 1, 9), -GEN_VXFORM(vminuw, 1, 10), -GEN_VXFORM_207(vminud, 1, 11), -GEN_VXFORM(vminsb, 1, 12), -GEN_VXFORM(vminsh, 1, 13), -GEN_VXFORM(vminsw, 1, 14), -GEN_VXFORM_207(vminsd, 1, 15), GEN_VXFORM(vmrghb, 6, 0), GEN_VXFORM(vmrghh, 6, 1), GEN_VXFORM(vmrghw, 6, 2), @@ -104,18 +54,13 @@ GEN_VXFORM(vsro, 6, 17), 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), -GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vadduws, 0, 10), -GEN_VXFORM(vaddsbs, 0, 12), -GEN_VXFORM_DUAL(vaddshs, bcdcpsgn, 0, 13, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM(vaddsws, 0, 14), -GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), -GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), -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_300_EXT(vmul10uq, 0, 8, 0x0000F800), +GEN_VXFORM_300(vmul10euq, 0, 9), +GEN_VXFORM_300(bcdcpsgn, 0, 13), +GEN_VXFORM_207(bcdadd, 0, 24), +GEN_VXFORM_207(bcdsub, 0, 25), +GEN_VXFORM_300(bcdtrunc, 0, 28), +GEN_VXFORM_300(xpnd04_2, 0, 30), GEN_VXFORM_300(bcdtrunc, 0, 20), GEN_VXFORM_300(bcdutrunc, 0, 21), GEN_VXFORM(vsl, 2, 7), diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index 4deb29ee42..a869f30e86 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -2,92 +2,91 @@ 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 void get_vsr_full(TCGv_i128 dst, int reg) +{ + tcg_gen_ld_i128(dst, tcg_env, vsr_full_offset(reg)); +} + +static inline void set_vsr_full(int reg, TCGv_i128 src) +{ + tcg_gen_st_i128(src, tcg_env, vsr_full_offset(reg)); } 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; } -#define VSX_LOAD_SCALAR(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - gen_addr_reg_index(ctx, EA); \ - 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) -VSX_LOAD_SCALAR(lxsiwax, ld32s_i64) -VSX_LOAD_SCALAR(lxsibzx, ld8u_i64) -VSX_LOAD_SCALAR(lxsihzx, ld16u_i64) -VSX_LOAD_SCALAR(lxsiwzx, ld32u_i64) -VSX_LOAD_SCALAR(lxsspx, ld32fs) - -static void gen_lxvd2x(DisasContext *ctx) +static bool do_lxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - gen_qemu_ld64_i64(ctx, t0, EA); - set_cpu_vsr(xT(ctx->opcode), t0, true); - 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); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + op(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, true); + /* NOTE: cpu_vsrl is undefined */ + return true; } -static void gen_lxvw4x(DisasContext *ctx) +TRANS_FLAGS2(VSX, LXSDX, do_lxs, gen_qemu_ld64_i64); +TRANS_FLAGS2(VSX207, LXSIWAX, do_lxs, gen_qemu_ld32s_i64); +TRANS_FLAGS2(ISA300, LXSIBZX, do_lxs, gen_qemu_ld8u_i64); +TRANS_FLAGS2(ISA300, LXSIHZX, do_lxs, gen_qemu_ld16u_i64); +TRANS_FLAGS2(VSX207, LXSIWZX, do_lxs, gen_qemu_ld32u_i64); +TRANS_FLAGS2(VSX207, LXSSPX, do_lxs, gen_qemu_ld32fs); + +static bool trans_LXVD2X(DisasContext *ctx, arg_LXVD2X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + TCGv_i64 t0; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, true); + tcg_gen_addi_tl(EA, EA, 8); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsr(a->rt, t0, false); + return true; +} + +static bool trans_LXVW4X(DisasContext *ctx, arg_LXVW4X *a) +{ + TCGv EA; + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); - gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -99,77 +98,56 @@ 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); 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); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } -static void gen_lxvwsx(DisasContext *ctx) +static bool trans_LXVWSX(DisasContext *ctx, arg_LXVWSX *a) { TCGv EA; TCGv_i32 data; - if (xT(ctx->opcode) < 32) { - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + if (a->rt < 32) { + REQUIRE_VSX(ctx); } else { - if (unlikely(!ctx->altivec_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VPU); - return; - } + REQUIRE_VECTOR(ctx); } + REQUIRE_INSNS_FLAGS2(ctx, ISA300); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); - + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); 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); + tcg_gen_gvec_dup_i32(MO_UL, vsr_full_offset(a->rt), 16, 16, data); + return true; } -static void gen_lxvdsx(DisasContext *ctx) +static bool trans_LXVDSX(DisasContext *ctx, arg_LXVDSX *a) { TCGv EA; TCGv_i64 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - - gen_addr_reg_index(ctx, EA); - + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); 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); + tcg_gen_gvec_dup_i64(MO_UQ, vsr_full_offset(a->rt), 16, 16, data); + return true; } 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 +164,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,161 +178,168 @@ 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) + +static bool trans_LXVH8X(DisasContext *ctx, arg_LXVH8X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; + TCGv_i64 xth, xtl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEUQ); if (ctx->le_mode) { gen_bswap16x8(xth, xtl, xth, xtl); } - 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); + set_cpu_vsr(a->rt, xth, true); + set_cpu_vsr(a->rt, xtl, false); + return true; } -static void gen_lxvb16x(DisasContext *ctx) +static bool trans_LXVB16X(DisasContext *ctx, arg_LXVB16X *a) { TCGv EA; - TCGv_i64 xth; - TCGv_i64 xtl; + TCGv_i128 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - xth = tcg_temp_new_i64(); - xtl = tcg_temp_new_i64(); + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + + data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - tcg_gen_qemu_ld_i64(xth, EA, ctx->mem_idx, MO_BEUQ); - tcg_gen_addi_tl(EA, EA, 8); - 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); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + tcg_gen_qemu_ld_i128(data, EA, ctx->mem_idx, + MO_BE | MO_128 | MO_ATOM_IFALIGN_PAIR); + set_vsr_full(a->rt, data); + return true; } -#ifdef TARGET_PPC64 -#define VSX_VECTOR_LOAD_STORE_LENGTH(name) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_ptr xt; \ - \ - if (xT(ctx->opcode) < 32) { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - EA = tcg_temp_new(); \ - 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); \ +#if defined(TARGET_PPC64) +static bool do_ld_st_vl(DisasContext *ctx, arg_X *a, + void (*helper)(TCGv_ptr, TCGv, TCGv_ptr, TCGv)) +{ + TCGv EA; + TCGv_ptr xt; + if (a->rt < 32) { + REQUIRE_VSX(ctx); + } else { + REQUIRE_VECTOR(ctx); + } + xt = gen_vsr_ptr(a->rt); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc_ra(ctx, a->ra); + helper(tcg_env, EA, xt, cpu_gpr[a->rb]); + return true; } - -VSX_VECTOR_LOAD_STORE_LENGTH(lxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(lxvll) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvl) -VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) #endif -#define VSX_STORE_SCALAR(name, operation) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv EA; \ - TCGv_i64 t0; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - t0 = tcg_temp_new_i64(); \ - gen_set_access_type(ctx, ACCESS_INT); \ - EA = tcg_temp_new(); \ - 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); \ +static bool trans_LXVL(DisasContext *ctx, arg_LXVL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_LXVL); +#else + qemu_build_not_reached(); +#endif + return true; } -VSX_STORE_SCALAR(stxsdx, st64_i64) +static bool trans_LXVLL(DisasContext *ctx, arg_LXVLL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_LXVLL); +#else + qemu_build_not_reached(); +#endif + return true; +} -VSX_STORE_SCALAR(stxsibx, st8_i64) -VSX_STORE_SCALAR(stxsihx, st16_i64) -VSX_STORE_SCALAR(stxsiwx, st32_i64) -VSX_STORE_SCALAR(stxsspx, st32fs) +static bool trans_STXVL(DisasContext *ctx, arg_STXVL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_STXVL); +#else + qemu_build_not_reached(); +#endif + return true; +} -static void gen_stxvd2x(DisasContext *ctx) +static bool trans_STXVLL(DisasContext *ctx, arg_STXVLL *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); +#if defined(TARGET_PPC64) + return do_ld_st_vl(ctx, a, gen_helper_STXVLL); +#else + qemu_build_not_reached(); +#endif + return true; +} + +static bool do_stxs(DisasContext *ctx, arg_X *a, + void (*op)(DisasContext *, TCGv_i64, TCGv)) { TCGv EA; TCGv_i64 t0; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - get_cpu_vsr(t0, xS(ctx->opcode), true); - gen_qemu_st64_i64(ctx, t0, EA); - 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); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); + op(ctx, t0, EA); + return true; } -static void gen_stxvw4x(DisasContext *ctx) +TRANS_FLAGS2(VSX, STXSDX, do_stxs, gen_qemu_st64_i64); +TRANS_FLAGS2(ISA300, STXSIBX, do_stxs, gen_qemu_st8_i64); +TRANS_FLAGS2(ISA300, STXSIHX, do_stxs, gen_qemu_st16_i64); +TRANS_FLAGS2(VSX207, STXSIWX, do_stxs, gen_qemu_st32_i64); +TRANS_FLAGS2(VSX207, STXSSPX, do_stxs, gen_qemu_st32fs); + +static bool trans_STXVD2X(DisasContext *ctx, arg_STXVD2X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 t0; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); + + t0 = tcg_temp_new_i64(); + gen_set_access_type(ctx, ACCESS_INT); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_cpu_vsr(t0, a->rt, true); + gen_qemu_st64_i64(ctx, t0, EA); + tcg_gen_addi_tl(EA, EA, 8); + get_cpu_vsr(t0, a->rt, false); + gen_qemu_st64_i64(ctx, t0, EA); + return true; +} + +static bool trans_STXVW4X(DisasContext *ctx, arg_STXVW4X *a) +{ + TCGv EA; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, VSX); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); @@ -370,35 +351,28 @@ 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); + return true; } -static void gen_stxvh8x(DisasContext *ctx) +static bool trans_STXVH8X(DisasContext *ctx, arg_STXVH8X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i64 xsh, xsl; + + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + get_cpu_vsr(xsh, a->rt, true); + get_cpu_vsr(xsl, a->rt, false); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); if (ctx->le_mode) { TCGv_i64 outh = tcg_temp_new_i64(); TCGv_i64 outl = tcg_temp_new_i64(); @@ -407,41 +381,29 @@ 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); + return true; } -static void gen_stxvb16x(DisasContext *ctx) +static bool trans_STXVB16X(DisasContext *ctx, arg_STXVB16X *a) { TCGv EA; - TCGv_i64 xsh; - TCGv_i64 xsl; + TCGv_i128 data; - if (unlikely(!ctx->vsx_enabled)) { - gen_exception(ctx, POWERPC_EXCP_VSXU); - return; - } - xsh = tcg_temp_new_i64(); - xsl = tcg_temp_new_i64(); - get_cpu_vsr(xsh, xS(ctx->opcode), true); - get_cpu_vsr(xsl, xS(ctx->opcode), false); + REQUIRE_VSX(ctx); + REQUIRE_INSNS_FLAGS2(ctx, ISA300); + + data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); - EA = tcg_temp_new(); - gen_addr_reg_index(ctx, EA); - 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); + EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); + get_vsr_full(data, a->rt); + tcg_gen_qemu_st_i128(data, EA, ctx->mem_idx, + MO_BE | MO_128 | MO_ATOM_IFALIGN_PAIR); + return true; } static void gen_mfvsrwz(DisasContext *ctx) @@ -462,8 +424,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 +444,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 +464,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 +484,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 +503,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 +522,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 +549,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 +571,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 @@ -666,14 +617,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) @@ -717,15 +665,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) @@ -853,38 +796,28 @@ static bool do_xvcpsgn(DisasContext *ctx, arg_XX3 *a, unsigned vece) 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) \ -{ \ - TCGv_i32 ignored; \ - TCGv_ptr xt, xa, xb; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ - xb = gen_vsr_ptr(xB(ctx->opcode)); \ - if ((ctx->opcode >> (31 - 21)) & 1) { \ - gen_helper_##name(cpu_crf[6], cpu_env, xt, xa, xb); \ - } else { \ - ignored = tcg_temp_new_i32(); \ - gen_helper_##name(ignored, cpu_env, xt, xa, xb); \ - tcg_temp_free_i32(ignored); \ - } \ - tcg_temp_free_ptr(xt); \ - tcg_temp_free_ptr(xa); \ - tcg_temp_free_ptr(xb); \ +static bool do_cmp(DisasContext *ctx, arg_XX3_rc *a, + void (*helper)(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) +{ + TCGv_i32 dest; + TCGv_ptr xt, xa, xb; + REQUIRE_VSX(ctx); + xt = gen_vsr_ptr(a->xt); + xa = gen_vsr_ptr(a->xa); + xb = gen_vsr_ptr(a->xb); + dest = a->rc ? cpu_crf[6] : tcg_temp_new_i32(); + helper(dest, tcg_env, xt, xa, xb); + return true; } -VSX_CMP(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) -VSX_CMP(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) -VSX_CMP(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) -VSX_CMP(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300) -VSX_CMP(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) -VSX_CMP(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) -VSX_CMP(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) -VSX_CMP(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) +TRANS_FLAGS2(VSX, XVCMPEQSP, do_cmp, gen_helper_XVCMPEQSP); +TRANS_FLAGS2(VSX, XVCMPGTSP, do_cmp, gen_helper_XVCMPGTSP); +TRANS_FLAGS2(VSX, XVCMPGESP, do_cmp, gen_helper_XVCMPGESP); +TRANS_FLAGS2(ISA300, XVCMPNESP, do_cmp, gen_helper_XVCMPNESP); +TRANS_FLAGS2(VSX, XVCMPEQDP, do_cmp, gen_helper_XVCMPEQDP); +TRANS_FLAGS2(VSX, XVCMPGTDP, do_cmp, gen_helper_XVCMPGTDP); +TRANS_FLAGS2(VSX, XVCMPGEDP, do_cmp, gen_helper_XVCMPGEDP); +TRANS_FLAGS2(ISA300, XVCMPNEDP, do_cmp, gen_helper_XVCMPNEDP); static bool trans_XSCVQPDP(DisasContext *ctx, arg_X_tb_rc *a) { @@ -894,15 +827,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; } @@ -916,10 +845,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; } @@ -936,26 +862,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); \ -} - -#define GEN_VSX_HELPER_X3(name, op1, op2, inval, type) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv_ptr xt, xa, xb; \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - xt = gen_vsr_ptr(xT(ctx->opcode)); \ - xa = gen_vsr_ptr(xA(ctx->opcode)); \ - 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); \ + opc = tcg_constant_i32(ctx->opcode); \ + gen_helper_##name(tcg_env, opc); \ } #define GEN_VSX_HELPER_X2(name, op1, op2, inval, type) \ @@ -968,9 +876,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) \ @@ -982,13 +888,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) \ @@ -1000,11 +903,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) \ @@ -1016,15 +917,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) \ @@ -1036,13 +933,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) \ @@ -1054,13 +948,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) \ @@ -1075,19 +966,13 @@ 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) GEN_VSX_HELPER_R3(xsaddqp, 0x04, 0x00, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xssubdp, 0x00, 0x05, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmuldp, 0x00, 0x06, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsmulqp, 0x04, 0x01, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xsdivdp, 0x00, 0x07, 0, PPC2_VSX) GEN_VSX_HELPER_R3(xsdivqp, 0x04, 0x11, 0, PPC2_ISA300) GEN_VSX_HELPER_X2(xsredp, 0x14, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xssqrtdp, 0x16, 0x04, 0, PPC2_VSX) @@ -1100,8 +985,6 @@ GEN_VSX_HELPER_X2_AB(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R2_AB(xscmpoqp, 0x04, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_R2_AB(xscmpuqp, 0x04, 0x14, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xsmindp, 0x00, 0x15, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300) GEN_VSX_HELPER_X2(xscvdpsp, 0x12, 0x10, 0, PPC2_VSX) GEN_VSX_HELPER_R2(xscvdpqp, 0x04, 0x1A, 0x16, PPC2_ISA300) @@ -1290,9 +1173,7 @@ static bool do_XX2_bf_uim(DisasContext *ctx, arg_XX2_bf_uim *a, bool vsr, REQUIRE_VSX(ctx); xb = vsr ? gen_vsr_ptr(a->xb) : gen_avr_ptr(a->xb); - gen_helper(cpu_env, tcg_constant_i32(a->bf), tcg_constant_i32(a->uim), xb); - tcg_temp_free_ptr(xb); - + gen_helper(tcg_env, tcg_constant_i32(a->bf), tcg_constant_i32(a->uim), xb); return true; } @@ -1314,9 +1195,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; } @@ -1337,27 +1215,17 @@ GEN_VSX_HELPER_R2(xsrqpi, 0x05, 0x00, 0, PPC2_ISA300) GEN_VSX_HELPER_R2(xsrqpxp, 0x05, 0x01, 0, PPC2_ISA300) GEN_VSX_HELPER_R2(xssqrtqp, 0x04, 0x19, 0x1B, PPC2_ISA300) GEN_VSX_HELPER_R3(xssubqp, 0x04, 0x10, 0, PPC2_ISA300) -GEN_VSX_HELPER_X3(xsaddsp, 0x00, 0x00, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xssubsp, 0x00, 0x01, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xsmulsp, 0x00, 0x02, 0, PPC2_VSX207) -GEN_VSX_HELPER_X3(xsdivsp, 0x00, 0x03, 0, PPC2_VSX207) GEN_VSX_HELPER_X2(xsresp, 0x14, 0x01, 0, PPC2_VSX207) 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_X3(xvadddp, 0x00, 0x0C, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvsubdp, 0x00, 0x0D, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmuldp, 0x00, 0x0E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvdivdp, 0x00, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvredp, 0x14, 0x0D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvsqrtdp, 0x16, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrsqrtedp, 0x14, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xvtdivdp, 0x14, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xvtsqrtdp, 0x14, 0x0E, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmaxdp, 0x00, 0x1C, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) @@ -1373,17 +1241,11 @@ GEN_VSX_HELPER_X2(xvrdpim, 0x12, 0x0F, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrdpip, 0x12, 0x0E, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrdpiz, 0x12, 0x0D, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvaddsp, 0x00, 0x08, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvsubsp, 0x00, 0x09, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmulsp, 0x00, 0x0A, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvdivsp, 0x00, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvresp, 0x14, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvsqrtsp, 0x16, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvrsqrtesp, 0x14, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_X2_AB(xvtdivsp, 0x14, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_X1(xvtsqrtsp, 0x14, 0x0A, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvmaxsp, 0x00, 0x18, 0, PPC2_VSX) -GEN_VSX_HELPER_X3(xvminsp, 0x00, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) GEN_VSX_HELPER_X2(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300) GEN_VSX_HELPER_X2(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300) @@ -1413,11 +1275,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; } @@ -1433,11 +1290,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; } @@ -1458,8 +1310,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); @@ -1467,9 +1317,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; } @@ -1486,12 +1333,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; } @@ -1514,10 +1355,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; } @@ -1549,13 +1386,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; } @@ -1635,11 +1466,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) @@ -1673,11 +1500,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) @@ -1701,11 +1523,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) @@ -1733,12 +1550,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) @@ -1762,33 +1573,26 @@ 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) \ -static void glue(gen_, name)(DisasContext *ctx) \ - { \ - if (unlikely(!ctx->vsx_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VSXU); \ - return; \ - } \ - tcg_op(vece, vsr_full_offset(xT(ctx->opcode)), \ - vsr_full_offset(xA(ctx->opcode)), \ - vsr_full_offset(xB(ctx->opcode)), 16, 16); \ - } +static bool do_logical_op(DisasContext *ctx, arg_XX3 *a, unsigned vece, + void (*helper)(unsigned, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)) +{ + REQUIRE_VSX(ctx); + helper(vece, vsr_full_offset(a->xt), + vsr_full_offset(a->xa), + vsr_full_offset(a->xb), 16, 16); + return true; +} -VSX_LOGICAL(xxland, MO_64, tcg_gen_gvec_and) -VSX_LOGICAL(xxlandc, MO_64, tcg_gen_gvec_andc) -VSX_LOGICAL(xxlor, MO_64, tcg_gen_gvec_or) -VSX_LOGICAL(xxlxor, MO_64, tcg_gen_gvec_xor) -VSX_LOGICAL(xxlnor, MO_64, tcg_gen_gvec_nor) -VSX_LOGICAL(xxleqv, MO_64, tcg_gen_gvec_eqv) -VSX_LOGICAL(xxlnand, MO_64, tcg_gen_gvec_nand) -VSX_LOGICAL(xxlorc, MO_64, tcg_gen_gvec_orc) +TRANS_FLAGS2(VSX, XXLAND, do_logical_op, MO_64, tcg_gen_gvec_and); +TRANS_FLAGS2(VSX, XXLANDC, do_logical_op, MO_64, tcg_gen_gvec_andc); +TRANS_FLAGS2(VSX, XXLOR, do_logical_op, MO_64, tcg_gen_gvec_or); +TRANS_FLAGS2(VSX, XXLXOR, do_logical_op, MO_64, tcg_gen_gvec_xor); +TRANS_FLAGS2(VSX, XXLNOR, do_logical_op, MO_64, tcg_gen_gvec_nor); +TRANS_FLAGS2(VSX207, XXLEQV, do_logical_op, MO_64, tcg_gen_gvec_eqv); +TRANS_FLAGS2(VSX207, XXLNAND, do_logical_op, MO_64, tcg_gen_gvec_nand); +TRANS_FLAGS2(VSX207, XXLORC, do_logical_op, MO_64, tcg_gen_gvec_orc); #define VSX_XXMRG(name, high) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -1813,11 +1617,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) @@ -1893,9 +1692,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; @@ -1974,13 +1773,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; } @@ -2012,7 +1804,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: { @@ -2032,16 +1823,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, @@ -2064,10 +1851,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; } @@ -2086,7 +1870,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) @@ -2108,10 +1891,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) @@ -2133,8 +1912,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) @@ -2167,13 +1944,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) @@ -2188,8 +1958,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); @@ -2198,12 +1968,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) @@ -2226,8 +1990,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); @@ -2237,15 +2001,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 @@ -2285,14 +2040,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) @@ -2324,13 +2071,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) @@ -2357,11 +2097,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) @@ -2386,11 +2121,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) @@ -2404,10 +2134,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; } @@ -2431,8 +2157,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); @@ -2447,28 +2173,19 @@ 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, int rt, bool store, bool paired) { TCGv ea; - TCGv_i64 xt; + TCGv_i128 data; MemOp mop; int rt1, rt2; - xt = tcg_temp_new_i64(); + data = tcg_temp_new_i128(); - mop = DEF_MEMOP(MO_UQ); + mop = DEF_MEMOP(MO_128 | MO_ATOM_IFALIGN_PAIR); gen_set_access_type(ctx, ACCESS_INT); ea = do_ea_calc(ctx, ra, displ); @@ -2482,43 +2199,28 @@ static bool do_lstxv(DisasContext *ctx, int ra, TCGv displ, } if (store) { - get_cpu_vsr(xt, rt1, !ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt1, ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + get_vsr_full(data, rt1); + tcg_gen_qemu_st_i128(data, ea, ctx->mem_idx, mop); if (paired) { - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt2, !ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); - gen_addr_add(ctx, ea, ea, 8); - get_cpu_vsr(xt, rt2, ctx->le_mode); - tcg_gen_qemu_st_i64(xt, ea, ctx->mem_idx, mop); + gen_addr_add(ctx, ea, ea, 16); + get_vsr_full(data, rt2); + tcg_gen_qemu_st_i128(data, ea, ctx->mem_idx, mop); } } else { - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt1, xt, !ctx->le_mode); - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt1, xt, ctx->le_mode); + tcg_gen_qemu_ld_i128(data, ea, ctx->mem_idx, mop); + set_vsr_full(rt1, data); if (paired) { - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt2, xt, !ctx->le_mode); - gen_addr_add(ctx, ea, ea, 8); - tcg_gen_qemu_ld_i64(xt, ea, ctx->mem_idx, mop); - set_cpu_vsr(rt2, xt, ctx->le_mode); + gen_addr_add(ctx, ea, ea, 16); + tcg_gen_qemu_ld_i128(data, ea, ctx->mem_idx, mop); + set_vsr_full(rt2, data); } } - - 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); @@ -2542,7 +2244,7 @@ static bool do_lstxv_PLS_D(DisasContext *ctx, arg_PLS_D *a, static bool do_lstxv_X(DisasContext *ctx, arg_X *a, bool store, bool paired) { - if (paired || a->rt >= 32) { + if (paired || a->rt < 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); @@ -2577,10 +2279,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; } @@ -2620,10 +2318,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; } @@ -2684,9 +2378,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; } @@ -2710,7 +2401,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) { @@ -2741,9 +2433,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, @@ -2756,8 +2445,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) { @@ -2788,9 +2478,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) @@ -2813,7 +2500,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; @@ -2925,7 +2612,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) @@ -2978,30 +2664,50 @@ static bool do_helper_XX3(DisasContext *ctx, arg_XX3 *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) { TCGv_ptr xt, xa, xb; - - REQUIRE_INSNS_FLAGS2(ctx, ISA300); REQUIRE_VSX(ctx); xt = gen_vsr_ptr(a->xt); xa = gen_vsr_ptr(a->xa); xb = gen_vsr_ptr(a->xb); - 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; } -TRANS(XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) -TRANS(XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) -TRANS(XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) -TRANS(XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) -TRANS(XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) -TRANS(XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) -TRANS(XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) +TRANS_FLAGS2(ISA300, XSCMPEQDP, do_helper_XX3, gen_helper_XSCMPEQDP) +TRANS_FLAGS2(ISA300, XSCMPGEDP, do_helper_XX3, gen_helper_XSCMPGEDP) +TRANS_FLAGS2(ISA300, XSCMPGTDP, do_helper_XX3, gen_helper_XSCMPGTDP) +TRANS_FLAGS2(ISA300, XSMAXCDP, do_helper_XX3, gen_helper_XSMAXCDP) +TRANS_FLAGS2(ISA300, XSMINCDP, do_helper_XX3, gen_helper_XSMINCDP) +TRANS_FLAGS2(ISA300, XSMAXJDP, do_helper_XX3, gen_helper_XSMAXJDP) +TRANS_FLAGS2(ISA300, XSMINJDP, do_helper_XX3, gen_helper_XSMINJDP) + +TRANS_FLAGS2(VSX207, XSADDSP, do_helper_XX3, gen_helper_XSADDSP) +TRANS_FLAGS2(VSX207, XSSUBSP, do_helper_XX3, gen_helper_XSSUBSP) +TRANS_FLAGS2(VSX207, XSMULSP, do_helper_XX3, gen_helper_XSMULSP) +TRANS_FLAGS2(VSX207, XSDIVSP, do_helper_XX3, gen_helper_XSDIVSP) + +TRANS_FLAGS2(VSX, XSADDDP, do_helper_XX3, gen_helper_XSADDDP) +TRANS_FLAGS2(VSX, XSSUBDP, do_helper_XX3, gen_helper_XSSUBDP) +TRANS_FLAGS2(VSX, XSMULDP, do_helper_XX3, gen_helper_XSMULDP) +TRANS_FLAGS2(VSX, XSDIVDP, do_helper_XX3, gen_helper_XSDIVDP) + +TRANS_FLAGS2(VSX, XVADDSP, do_helper_XX3, gen_helper_XVADDSP) +TRANS_FLAGS2(VSX, XVSUBSP, do_helper_XX3, gen_helper_XVSUBSP) +TRANS_FLAGS2(VSX, XVMULSP, do_helper_XX3, gen_helper_XVMULSP) +TRANS_FLAGS2(VSX, XVDIVSP, do_helper_XX3, gen_helper_XVDIVSP) + +TRANS_FLAGS2(VSX, XVADDDP, do_helper_XX3, gen_helper_XVADDDP) +TRANS_FLAGS2(VSX, XVSUBDP, do_helper_XX3, gen_helper_XVSUBDP) +TRANS_FLAGS2(VSX, XVMULDP, do_helper_XX3, gen_helper_XVMULDP) +TRANS_FLAGS2(VSX, XVDIVDP, do_helper_XX3, gen_helper_XVDIVDP) + +TRANS_FLAGS2(VSX, XSMAXDP, do_helper_XX3, gen_helper_XSMAXDP) +TRANS_FLAGS2(VSX, XSMINDP, do_helper_XX3, gen_helper_XSMINDP) +TRANS_FLAGS2(VSX, XVMAXSP, do_helper_XX3, gen_helper_XVMAXSP) +TRANS_FLAGS2(VSX, XVMINSP, do_helper_XX3, gen_helper_XVMINSP) +TRANS_FLAGS2(VSX, XVMAXDP, do_helper_XX3, gen_helper_XVMAXDP) +TRANS_FLAGS2(VSX, XVMINDP, do_helper_XX3, gen_helper_XVMINDP) static bool do_helper_X(arg_X *a, void (*helper)(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr)) @@ -3012,12 +2718,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; } @@ -3046,11 +2747,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; } @@ -3113,10 +2810,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; } @@ -3193,4 +2887,3 @@ TRANS64(PMXVF64GERNN, do_ger, gen_helper_XVF64GERNN) #undef GEN_XX2IFORM #undef GEN_XX3_RC_FORM #undef GEN_XX3FORM_DM -#undef VSX_LOGICAL diff --git a/target/ppc/translate/vsx-ops.c.inc b/target/ppc/translate/vsx-ops.c.inc index a3ba094d62..e553b5b8fa 100644 --- a/target/ppc/translate/vsx-ops.c.inc +++ b/target/ppc/translate/vsx-ops.c.inc @@ -1,34 +1,3 @@ -GEN_HANDLER_E(lxsdx, 0x1F, 0x0C, 0x12, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxsiwax, 0x1F, 0x0C, 0x02, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxsiwzx, 0x1F, 0x0C, 0x00, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxsibzx, 0x1F, 0x0D, 0x18, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxsihzx, 0x1F, 0x0D, 0x19, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxsspx, 0x1F, 0x0C, 0x10, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(lxvd2x, 0x1F, 0x0C, 0x1A, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvwsx, 0x1F, 0x0C, 0x0B, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvdsx, 0x1F, 0x0C, 0x0A, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvw4x, 0x1F, 0x0C, 0x18, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(lxvh8x, 0x1F, 0x0C, 0x19, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvb16x, 0x1F, 0x0C, 0x1B, 0, PPC_NONE, PPC2_ISA300), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(lxvl, 0x1F, 0x0D, 0x08, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(lxvll, 0x1F, 0x0D, 0x09, 0, PPC_NONE, PPC2_ISA300), -#endif - -GEN_HANDLER_E(stxsdx, 0x1F, 0xC, 0x16, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxsibx, 0x1F, 0xD, 0x1C, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxsihx, 0x1F, 0xD, 0x1D, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxsiwx, 0x1F, 0xC, 0x04, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(stxsspx, 0x1F, 0xC, 0x14, 0, PPC_NONE, PPC2_VSX207), -GEN_HANDLER_E(stxvd2x, 0x1F, 0xC, 0x1E, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxvw4x, 0x1F, 0xC, 0x1C, 0, PPC_NONE, PPC2_VSX), -GEN_HANDLER_E(stxvh8x, 0x1F, 0x0C, 0x1D, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvb16x, 0x1F, 0x0C, 0x1F, 0, PPC_NONE, PPC2_ISA300), -#if defined(TARGET_PPC64) -GEN_HANDLER_E(stxvl, 0x1F, 0x0D, 0x0C, 0, PPC_NONE, PPC2_ISA300), -GEN_HANDLER_E(stxvll, 0x1F, 0x0D, 0x0D, 0, PPC_NONE, PPC2_ISA300), -#endif - GEN_HANDLER_E(mfvsrwz, 0x1F, 0x13, 0x03, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwa, 0x1F, 0x13, 0x06, 0x0000F800, PPC_NONE, PPC2_VSX207), GEN_HANDLER_E(mtvsrwz, 0x1F, 0x13, 0x07, 0x0000F800, PPC_NONE, PPC2_VSX207), @@ -74,16 +43,6 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 1, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 2, opc3, 1, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 3, opc3, 1, PPC_NONE, fl2) -#define GEN_XX3_RC_FORM(name, opc2, opc3, fl2) \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x00, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x00, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x01, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x02, opc3 | 0x10, 0, PPC_NONE, fl2), \ -GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0x03, opc3 | 0x10, 0, PPC_NONE, fl2) - #define GEN_XX3FORM_DM(name, opc2, opc3) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x00, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ GEN_HANDLER2_E(name, #name, 0x3C, opc2|0x01, opc3|0x00, 0, PPC_NONE, PPC2_VSX),\ @@ -153,12 +112,8 @@ 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), -GEN_XX3FORM(xsadddp, 0x00, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xsaddqp, 0x04, 0x00, 0x0), -GEN_XX3FORM(xssubdp, 0x00, 0x05, PPC2_VSX), -GEN_XX3FORM(xsmuldp, 0x00, 0x06, PPC2_VSX), GEN_VSX_XFORM_300(xsmulqp, 0x04, 0x01, 0x0), -GEN_XX3FORM(xsdivdp, 0x00, 0x07, PPC2_VSX), GEN_XX2FORM(xsredp, 0x14, 0x05, PPC2_VSX), GEN_XX2FORM(xssqrtdp, 0x16, 0x04, PPC2_VSX), GEN_XX2FORM(xsrsqrtedp, 0x14, 0x04, PPC2_VSX), @@ -170,8 +125,6 @@ GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), GEN_VSX_XFORM_300(xscmpoqp, 0x04, 0x04, 0x00600001), GEN_VSX_XFORM_300(xscmpuqp, 0x04, 0x14, 0x00600001), -GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), -GEN_XX3FORM(xsmindp, 0x00, 0x15, PPC2_VSX), GEN_XX2FORM_EO(xscvdphp, 0x16, 0x15, 0x11, PPC2_ISA300), GEN_XX2FORM(xscvdpsp, 0x12, 0x10, PPC2_VSX), GEN_XX2FORM(xscvdpspn, 0x16, 0x10, PPC2_VSX207), @@ -191,10 +144,6 @@ GEN_XX2FORM(xsrdpim, 0x12, 0x07, PPC2_VSX), GEN_XX2FORM(xsrdpip, 0x12, 0x06, PPC2_VSX), GEN_XX2FORM(xsrdpiz, 0x12, 0x05, PPC2_VSX), -GEN_XX3FORM(xsaddsp, 0x00, 0x00, PPC2_VSX207), -GEN_XX3FORM(xssubsp, 0x00, 0x01, PPC2_VSX207), -GEN_XX3FORM(xsmulsp, 0x00, 0x02, PPC2_VSX207), -GEN_XX3FORM(xsdivsp, 0x00, 0x03, PPC2_VSX207), GEN_VSX_XFORM_300(xsdivqp, 0x04, 0x11, 0x0), GEN_XX2FORM(xsresp, 0x14, 0x01, PPC2_VSX207), GEN_XX2FORM(xsrsp, 0x12, 0x11, PPC2_VSX207), @@ -203,10 +152,6 @@ GEN_XX2FORM(xsrsqrtesp, 0x14, 0x00, PPC2_VSX207), GEN_XX2FORM(xscvsxdsp, 0x10, 0x13, PPC2_VSX207), GEN_XX2FORM(xscvuxdsp, 0x10, 0x12, PPC2_VSX207), -GEN_XX3FORM(xvadddp, 0x00, 0x0C, PPC2_VSX), -GEN_XX3FORM(xvsubdp, 0x00, 0x0D, PPC2_VSX), -GEN_XX3FORM(xvmuldp, 0x00, 0x0E, PPC2_VSX), -GEN_XX3FORM(xvdivdp, 0x00, 0x0F, PPC2_VSX), GEN_XX2FORM(xvredp, 0x14, 0x0D, PPC2_VSX), GEN_XX2FORM(xvsqrtdp, 0x16, 0x0C, PPC2_VSX), GEN_XX2FORM(xvrsqrtedp, 0x14, 0x0C, PPC2_VSX), @@ -220,12 +165,6 @@ GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddadp", 0x04, 0x1C, PPC2_VSX), GEN_XX3FORM_NAME(xvnmadddp, "xvnmaddmdp", 0x04, 0x1D, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubadp", 0x04, 0x1E, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubdp, "xvnmsubmdp", 0x04, 0x1F, PPC2_VSX), -GEN_XX3FORM(xvmaxdp, 0x00, 0x1C, PPC2_VSX), -GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300), GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), @@ -241,10 +180,6 @@ GEN_XX2FORM(xvrdpim, 0x12, 0x0F, PPC2_VSX), GEN_XX2FORM(xvrdpip, 0x12, 0x0E, PPC2_VSX), GEN_XX2FORM(xvrdpiz, 0x12, 0x0D, PPC2_VSX), -GEN_XX3FORM(xvaddsp, 0x00, 0x08, PPC2_VSX), -GEN_XX3FORM(xvsubsp, 0x00, 0x09, PPC2_VSX), -GEN_XX3FORM(xvmulsp, 0x00, 0x0A, PPC2_VSX), -GEN_XX3FORM(xvdivsp, 0x00, 0x0B, PPC2_VSX), GEN_XX2FORM(xvresp, 0x14, 0x09, PPC2_VSX), GEN_XX2FORM(xvsqrtsp, 0x16, 0x08, PPC2_VSX), GEN_XX2FORM(xvrsqrtesp, 0x14, 0x08, PPC2_VSX), @@ -258,12 +193,6 @@ GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddasp", 0x04, 0x18, PPC2_VSX), GEN_XX3FORM_NAME(xvnmaddsp, "xvnmaddmsp", 0x04, 0x19, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubasp", 0x04, 0x1A, PPC2_VSX), GEN_XX3FORM_NAME(xvnmsubsp, "xvnmsubmsp", 0x04, 0x1B, PPC2_VSX), -GEN_XX3FORM(xvmaxsp, 0x00, 0x18, PPC2_VSX), -GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), -GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300), GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), @@ -285,17 +214,6 @@ GEN_XX2FORM_EO(xvcvhpsp, 0x16, 0x1D, 0x18, PPC2_ISA300), GEN_XX2FORM_EO(xvcvsphp, 0x16, 0x1D, 0x19, PPC2_ISA300), GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), -#define VSX_LOGICAL(name, opc2, opc3, fl2) \ -GEN_XX3FORM(name, opc2, opc3, fl2) - -VSX_LOGICAL(xxland, 0x8, 0x10, PPC2_VSX), -VSX_LOGICAL(xxlandc, 0x8, 0x11, PPC2_VSX), -VSX_LOGICAL(xxlor, 0x8, 0x12, PPC2_VSX), -VSX_LOGICAL(xxlxor, 0x8, 0x13, PPC2_VSX), -VSX_LOGICAL(xxlnor, 0x8, 0x14, PPC2_VSX), -VSX_LOGICAL(xxleqv, 0x8, 0x17, PPC2_VSX207), -VSX_LOGICAL(xxlnand, 0x8, 0x16, PPC2_VSX207), -VSX_LOGICAL(xxlorc, 0x8, 0x15, PPC2_VSX207), GEN_XX3FORM(xxmrghw, 0x08, 0x02, PPC2_VSX), GEN_XX3FORM(xxmrglw, 0x08, 0x06, PPC2_VSX), GEN_XX3FORM_DM(xxsldwi, 0x08, 0x00), 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..11bc09b414 100644 --- a/target/riscv/Kconfig +++ b/target/riscv/Kconfig @@ -1,5 +1,9 @@ config RISCV32 bool + select ARM_COMPATIBLE_SEMIHOSTING if TCG + select DEVICE_TREE # needed by boot.c config RISCV64 bool + select ARM_COMPATIBLE_SEMIHOSTING if TCG + select DEVICE_TREE # needed by boot.c diff --git a/target/riscv/arch_dump.c b/target/riscv/arch_dump.c index 736a232956..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 * @@ -180,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/cpu-param.h b/target/riscv/cpu-param.h index ebaf26d26d..25686192c0 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -2,7 +2,7 @@ * RISC-V cpu parameters for qemu. * * Copyright (c) 2017-2018 SiFive, Inc. - * SPDX-License-Identifier: GPL-2.0+ + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef RISCV_CPU_PARAM_H @@ -27,6 +27,7 @@ * - S mode HLV/HLVX/HSV 0b101 * - M mode HLV/HLVX/HSV 0b111 */ -#define NB_MMU_MODES 8 + +#define TCG_GUEST_DEFAULT_MO 0 #endif diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h new file mode 100644 index 0000000000..62115375cd --- /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_MAX RISCV_CPU_TYPE_NAME("max") +#define TYPE_RISCV_CPU_MAX32 RISCV_CPU_TYPE_NAME("max32") +#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 d14e95c9dc..f219f0c3b5 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -22,38 +22,62 @@ #include "qemu/ctype.h" #include "qemu/log.h" #include "cpu.h" -#include "pmu.h" +#include "cpu_vendorid.h" #include "internals.h" -#include "time_helper.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 multi_letter; - int min_version; - int ext_enable_offset; -}; +/* Hash that stores general user set numeric options */ +static GHashTable *general_user_opts; -#define ISA_EXT_DATA_ENTRY(_name, _m_letter, _min_ver, _prop) \ -{#_name, _m_letter, _min_ver, offsetof(struct RISCVCPUConfig, _prop)} +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 @@ -69,88 +93,180 @@ struct isa_ext_data { * 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. */ -static const struct isa_ext_data isa_edata_arr[] = { - ISA_EXT_DATA_ENTRY(h, false, PRIV_VERSION_1_12_0, ext_h), - ISA_EXT_DATA_ENTRY(v, false, PRIV_VERSION_1_12_0, ext_v), - ISA_EXT_DATA_ENTRY(zicsr, true, PRIV_VERSION_1_10_0, ext_icsr), - ISA_EXT_DATA_ENTRY(zifencei, true, PRIV_VERSION_1_10_0, ext_ifencei), - ISA_EXT_DATA_ENTRY(zihintpause, true, PRIV_VERSION_1_10_0, ext_zihintpause), - ISA_EXT_DATA_ENTRY(zfh, true, PRIV_VERSION_1_12_0, ext_zfh), - ISA_EXT_DATA_ENTRY(zfhmin, true, PRIV_VERSION_1_12_0, ext_zfhmin), - ISA_EXT_DATA_ENTRY(zfinx, true, PRIV_VERSION_1_12_0, ext_zfinx), - ISA_EXT_DATA_ENTRY(zdinx, true, PRIV_VERSION_1_12_0, ext_zdinx), - ISA_EXT_DATA_ENTRY(zba, true, PRIV_VERSION_1_12_0, ext_zba), - ISA_EXT_DATA_ENTRY(zbb, true, PRIV_VERSION_1_12_0, ext_zbb), - ISA_EXT_DATA_ENTRY(zbc, true, PRIV_VERSION_1_12_0, ext_zbc), - ISA_EXT_DATA_ENTRY(zbkb, true, PRIV_VERSION_1_12_0, ext_zbkb), - ISA_EXT_DATA_ENTRY(zbkc, true, PRIV_VERSION_1_12_0, ext_zbkc), - ISA_EXT_DATA_ENTRY(zbkx, true, PRIV_VERSION_1_12_0, ext_zbkx), - ISA_EXT_DATA_ENTRY(zbs, true, PRIV_VERSION_1_12_0, ext_zbs), - ISA_EXT_DATA_ENTRY(zk, true, PRIV_VERSION_1_12_0, ext_zk), - ISA_EXT_DATA_ENTRY(zkn, true, PRIV_VERSION_1_12_0, ext_zkn), - ISA_EXT_DATA_ENTRY(zknd, true, PRIV_VERSION_1_12_0, ext_zknd), - ISA_EXT_DATA_ENTRY(zkne, true, PRIV_VERSION_1_12_0, ext_zkne), - ISA_EXT_DATA_ENTRY(zknh, true, PRIV_VERSION_1_12_0, ext_zknh), - ISA_EXT_DATA_ENTRY(zkr, true, PRIV_VERSION_1_12_0, ext_zkr), - ISA_EXT_DATA_ENTRY(zks, true, PRIV_VERSION_1_12_0, ext_zks), - ISA_EXT_DATA_ENTRY(zksed, true, PRIV_VERSION_1_12_0, ext_zksed), - ISA_EXT_DATA_ENTRY(zksh, true, PRIV_VERSION_1_12_0, ext_zksh), - ISA_EXT_DATA_ENTRY(zkt, true, PRIV_VERSION_1_12_0, ext_zkt), - ISA_EXT_DATA_ENTRY(zve32f, true, PRIV_VERSION_1_12_0, ext_zve32f), - ISA_EXT_DATA_ENTRY(zve64f, true, PRIV_VERSION_1_12_0, ext_zve64f), - ISA_EXT_DATA_ENTRY(zhinx, true, PRIV_VERSION_1_12_0, ext_zhinx), - ISA_EXT_DATA_ENTRY(zhinxmin, true, PRIV_VERSION_1_12_0, ext_zhinxmin), - ISA_EXT_DATA_ENTRY(smaia, true, PRIV_VERSION_1_12_0, ext_smaia), - ISA_EXT_DATA_ENTRY(ssaia, true, PRIV_VERSION_1_12_0, ext_ssaia), - ISA_EXT_DATA_ENTRY(sscofpmf, true, PRIV_VERSION_1_12_0, ext_sscofpmf), - ISA_EXT_DATA_ENTRY(sstc, true, PRIV_VERSION_1_12_0, ext_sstc), - ISA_EXT_DATA_ENTRY(svinval, true, PRIV_VERSION_1_12_0, ext_svinval), - ISA_EXT_DATA_ENTRY(svnapot, true, PRIV_VERSION_1_12_0, ext_svnapot), - ISA_EXT_DATA_ENTRY(svpbmt, true, PRIV_VERSION_1_12_0, ext_svpbmt), - ISA_EXT_DATA_ENTRY(xventanacondops, true, PRIV_VERSION_1_12_0, ext_XVentanaCondOps), +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(zicfilp, PRIV_VERSION_1_12_0, ext_zicfilp), + ISA_EXT_DATA_ENTRY(zicfiss, PRIV_VERSION_1_13_0, ext_zicfiss), + 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(zimop, PRIV_VERSION_1_13_0, ext_zimop), + 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_12), + ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), + ISA_EXT_DATA_ENTRY(zabha, PRIV_VERSION_1_13_0, ext_zabha), + ISA_EXT_DATA_ENTRY(zacas, PRIV_VERSION_1_12_0, ext_zacas), + ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), + 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(zcmop, PRIV_VERSION_1_13_0, ext_zcmop), + 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(zve32x, PRIV_VERSION_1_10_0, ext_zve32x), + 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(zve64x, PRIV_VERSION_1_10_0, ext_zve64x), + 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(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf), + 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(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), + 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(), }; -static bool isa_ext_is_enabled(RISCVCPU *cpu, - const struct isa_ext_data *edata) +bool isa_ext_is_enabled(RISCVCPU *cpu, uint32_t ext_offset) { - bool *ext_enabled = (void *)&cpu->cfg + edata->ext_enable_offset; + bool *ext_enabled = (void *)&cpu->cfg + ext_offset; return *ext_enabled; } -static void isa_ext_update_enabled(RISCVCPU *cpu, - const struct isa_ext_data *edata, bool en) +void isa_ext_update_enabled(RISCVCPU *cpu, uint32_t ext_offset, bool en) { - bool *ext_enabled = (void *)&cpu->cfg + edata->ext_enable_offset; + 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[] = { @@ -199,8 +315,6 @@ static const char * const riscv_intr_names[] = { "reserved" }; -static void register_cpu_props(DeviceState *dev); - const char *riscv_cpu_get_trap_name(target_ulong cause, bool async) { if (async) { @@ -212,50 +326,168 @@ 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) +#ifndef CONFIG_USER_ONLY +static uint8_t satp_mode_from_str(const char *satp_mode_str) { - env->vext_ver = vext_ver; + 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(); } -static void riscv_any_cpu_init(Object *obj) +uint8_t satp_mode_max_from_map(uint32_t map) { - 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); + /* + * '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_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 + 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 - set_priv_version(env, PRIV_VERSION_1_12_0); - register_cpu_props(DEVICE(obj)); } #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); - register_cpu_props(DEVICE(obj)); + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + 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) @@ -263,42 +495,161 @@ static void rv64_sifive_e_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - set_misa(env, MXL_RV64, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - cpu->cfg.mmu = false; + 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); + th_register_custom_csrs(cpu); +#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 +} + +#ifdef CONFIG_TCG 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); } - CPURISCVState *env = &RISCV_CPU(obj)->env; - /* We set this in the realise function */ - set_misa(env, MXL_RV128, 0); - register_cpu_props(DEVICE(obj)); + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + env->priv_ver = PRIV_VERSION_LATEST; +#ifndef CONFIG_USER_ONLY + set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); +#endif } -#else -static void rv32_base_cpu_init(Object *obj) +#endif /* CONFIG_TCG */ + +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_RV32, 0); - register_cpu_props(DEVICE(obj)); + 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); +} + +#endif /* !TARGET_RISCV64 */ + +#if defined(TARGET_RISCV32) || \ + (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) + +static void rv32_base_cpu_init(Object *obj) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + CPURISCVState *env = &cpu->env; + + cpu->cfg.mmu = true; + cpu->cfg.pmp = true; + /* Set latest version of privileged specification */ - set_priv_version(env, PRIV_VERSION_1_12_0); + 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) @@ -306,9 +657,16 @@ static void rv32_sifive_e_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - set_misa(env, MXL_RV32, RVI | RVM | RVA | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - cpu->cfg.mmu = false; + 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) @@ -316,10 +674,21 @@ static void rv32_ibex_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - set_misa(env, MXL_RV32, RVI | RVM | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_11_0); - cpu->cfg.mmu = false; - cpu->cfg.epmp = true; + 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; + + cpu->cfg.ext_zba = true; + cpu->cfg.ext_zbb = true; + cpu->cfg.ext_zbc = true; + cpu->cfg.ext_zbs = true; } static void rv32_imafcu_nommu_cpu_init(Object *obj) @@ -327,22 +696,28 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) CPURISCVState *env = &RISCV_CPU(obj)->env; RISCVCPU *cpu = RISCV_CPU(obj); - set_misa(env, MXL_RV32, RVI | RVM | RVA | RVF | RVC | RVU); - set_priv_version(env, PRIV_VERSION_1_10_0); - cpu->cfg.mmu = false; -} + 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 - register_cpu_props(DEVICE(obj)); + 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 @@ -357,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); @@ -382,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, @@ -415,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); @@ -440,6 +827,12 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } if (flags & CPU_DUMP_FPU) { + target_ulong val = 0; + RISCVException res = riscv_csrrw_debug(env, CSR_FCSR, &val, 0, 0); + if (res == RISCV_EXCP_NONE) { + qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n", + csr_ops[CSR_FCSR].name, val); + } for (i = 0; i < 32; i++) { qemu_fprintf(f, " %-8s %016" PRIx64, riscv_fpr_regnames[i], env->fpr[i]); @@ -448,6 +841,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) @@ -474,21 +902,7 @@ static vaddr riscv_cpu_get_pc(CPUState *cs) return env->pc; } -static void riscv_cpu_synchronize_from_tb(CPUState *cs, - const TranslationBlock *tb) -{ - 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(tb); - } else { - env->pc = tb_pc(tb); - } -} - -static bool riscv_cpu_has_work(CPUState *cs) +bool riscv_cpu_has_work(CPUState *cs) { #ifndef CONFIG_USER_ONLY RISCVCPU *cpu = RISCV_CPU(cs); @@ -497,42 +911,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 riscv_cpu_all_pending(env) != 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 } -static void riscv_restore_state_to_opc(CPUState *cs, - const TranslationBlock *tb, - const uint64_t *data) +static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) { - 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)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, ResetType type) { #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, type); + } #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) { @@ -559,6 +966,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); @@ -574,17 +986,47 @@ 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); +#else + env->priv = PRV_U; + env->senvcfg = 0; + env->menvcfg = 0; #endif + + /* on reset elp is clear */ + env->elp = false; + /* on reset ssp is set to 0 */ + env->ssp = 0; + env->xl = riscv_cpu_mxl(env); riscv_cpu_update_mask(env); cs->exception_index = RISCV_EXCP_NONE; env->load_res = -1; set_default_nan_mode(1, &env->fp_status); + env->vill = true; #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()) { @@ -596,8 +1038,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; @@ -612,14 +1056,114 @@ 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; + } + riscv_tcg_cpu_finalize_dynamic_decoder(cpu); + } 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 i, priv_version = -1; Error *local_err = NULL; cpu_exec_realizefn(cs, &local_err); @@ -628,294 +1172,82 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) 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; - } + riscv_cpu_finalize_features(cpu, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; } - if (priv_version >= PRIV_VERSION_1_10_0) { - set_priv_version(env, priv_version); - } - - /* Force disable extensions if priv spec version does not match */ - for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_ext_is_enabled(cpu, &isa_edata_arr[i]) && - (env->priv_ver < isa_edata_arr[i].min_version)) { - isa_ext_update_enabled(cpu, &isa_edata_arr[i], false); -#ifndef CONFIG_USER_ONLY - warn_report("disabling %s extension for hart 0x%lx because " - "privilege spec version does not match", - isa_edata_arr[i].name, (unsigned long)env->mhartid); -#else - warn_report("disabling %s extension because " - "privilege spec version does not match", - isa_edata_arr[i].name); -#endif - } - } - - 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.debug) { - riscv_set_feature(env, RISCV_FEATURE_DEBUG); - } - - -#ifndef CONFIG_USER_ONLY - if (cpu->cfg.ext_sstc) { - riscv_timer_init(cpu); - } -#endif /* CONFIG_USER_ONLY */ - - /* 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_s && !cpu->cfg.ext_u) { - error_setg(errp, - "Setting S extension without U extension is illegal"); - return; - } - - if (cpu->cfg.ext_h && !cpu->cfg.ext_i) { - error_setg(errp, - "H depends on an I base integer ISA with 32 x registers"); - return; - } - - if (cpu->cfg.ext_h && !cpu->cfg.ext_s) { - error_setg(errp, "H extension implicitly requires S-mode"); - 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); - } - -#ifndef CONFIG_USER_ONLY - if (cpu->cfg.pmu_num) { - if (!riscv_pmu_init(cpu, cpu->cfg.pmu_num) && cpu->cfg.ext_sscofpmf) { - cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - riscv_pmu_timer_cb, cpu); - } - } -#endif - 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); @@ -937,7 +1269,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: @@ -945,7 +1277,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; @@ -971,7 +1303,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(); @@ -979,112 +1311,1376 @@ 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->cfg.ext_ifencei = true; - cpu->cfg.ext_icsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; + /* + * 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; - cpu_set_cpustate_pointers(cpu); + /* 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_extensions[] = { +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, "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_UINT8("pmu-num", RISCVCPU, cfg.pmu_num, 16), - DEFINE_PROP_BOOL("sscofpmf", RISCVCPU, cfg.ext_sscofpmf, false), - DEFINE_PROP_BOOL("Zifencei", RISCVCPU, cfg.ext_ifencei, true), - DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true), - DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, 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("sstc", RISCVCPU, cfg.ext_sstc, true), + MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false), + MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false), + MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true), + MULTI_EXT_CFG_BOOL("zicfilp", ext_zicfilp, false), + MULTI_EXT_CFG_BOOL("zicfiss", ext_zicfiss, false), + 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("zimop", ext_zimop, false), + MULTI_EXT_CFG_BOOL("zcmop", ext_zcmop, false), + MULTI_EXT_CFG_BOOL("zacas", ext_zacas, false), + MULTI_EXT_CFG_BOOL("zama16b", ext_zama16b, false), + MULTI_EXT_CFG_BOOL("zabha", ext_zabha, 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("zve32x", ext_zve32x, false), + MULTI_EXT_CFG_BOOL("zve64f", ext_zve64f, false), + MULTI_EXT_CFG_BOOL("zve64d", ext_zve64d, false), + MULTI_EXT_CFG_BOOL("zve64x", ext_zve64x, 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), + MULTI_EXT_CFG_BOOL("svvptc", ext_svvptc, 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("zicntr", ext_zicntr, true), + MULTI_EXT_CFG_BOOL("zihpm", ext_zihpm, true), - 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("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("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("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("zmmul", RISCVCPU, cfg.ext_zmmul, 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-smaia", RISCVCPU, cfg.ext_smaia, false), - DEFINE_PROP_BOOL("x-ssaia", RISCVCPU, cfg.ext_ssaia, 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), + + /* 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_zvkb, 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_END_OF_LIST(), }; -static void register_cpu_props(DeviceState *dev) -{ - Property *prop; +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), - for (prop = riscv_cpu_extensions; prop && prop->name; prop++) { - qdev_property_add_static(dev, prop); + 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_13_0_STR)) { + priv_version = PRIV_VERSION_1_13_0; + } else 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; +} + +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; + case PRIV_VERSION_1_13_0: + return PRIV_VER_1_13_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 RISCVCPUImpliedExtsRule RVA_IMPLIED = { + .is_misa = true, + .ext = RVA, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zalrsc), CPU_CFG_OFFSET(ext_zaamo), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule RVD_IMPLIED = { + .is_misa = true, + .ext = RVD, + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule RVF_IMPLIED = { + .is_misa = true, + .ext = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule RVM_IMPLIED = { + .is_misa = true, + .ext = RVM, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zmmul), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule RVV_IMPLIED = { + .is_misa = true, + .ext = RVV, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64d), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCD_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcd), + .implied_misa_exts = RVD, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCE_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zce), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zcb), CPU_CFG_OFFSET(ext_zcmp), + CPU_CFG_OFFSET(ext_zcmt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCF_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcf), + .implied_misa_exts = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCMP_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcmp), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZCMT_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zcmt), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zca), CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZDINX_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zdinx), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zfinx), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZFA_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfa), + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule ZFBFMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfbfmin), + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule ZFH_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfh), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zfhmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZFHMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfhmin), + .implied_misa_exts = RVF, + .implied_multi_exts = { RISCV_IMPLIED_EXTS_RULE_END }, +}; + +static RISCVCPUImpliedExtsRule ZFINX_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zfinx), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZHINX_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zhinx), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zhinxmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZHINXMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zhinxmin), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zfinx), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZICNTR_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zicntr), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZIHPM_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zihpm), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZK_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zk), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zkn), CPU_CFG_OFFSET(ext_zkr), + CPU_CFG_OFFSET(ext_zkt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZKN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zkn), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zbkb), CPU_CFG_OFFSET(ext_zbkc), + CPU_CFG_OFFSET(ext_zbkx), CPU_CFG_OFFSET(ext_zkne), + CPU_CFG_OFFSET(ext_zknd), CPU_CFG_OFFSET(ext_zknh), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZKS_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zks), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zbkb), CPU_CFG_OFFSET(ext_zbkc), + CPU_CFG_OFFSET(ext_zbkx), CPU_CFG_OFFSET(ext_zksed), + CPU_CFG_OFFSET(ext_zksh), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVBB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvbb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkb), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE32F_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve32f), + .implied_misa_exts = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE32X_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve32x), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zicsr), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE64D_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve64d), + .implied_misa_exts = RVD, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64f), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE64F_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve64f), + .implied_misa_exts = RVF, + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32f), CPU_CFG_OFFSET(ext_zve64x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVE64X_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zve64x), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFBFMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfbfmin), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32f), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFBFWMA_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfbfwma), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvfbfmin), CPU_CFG_OFFSET(ext_zfbfmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFH_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfh), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvfhmin), CPU_CFG_OFFSET(ext_zfhmin), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVFHMIN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvfhmin), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve32f), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKN_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvkn), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkned), CPU_CFG_OFFSET(ext_zvknhb), + CPU_CFG_OFFSET(ext_zvkb), CPU_CFG_OFFSET(ext_zvkt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKNC_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvknc), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkn), CPU_CFG_OFFSET(ext_zvbc), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKNG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvkng), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvkn), CPU_CFG_OFFSET(ext_zvkg), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKNHB_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvknhb), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zve64x), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKS_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvks), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvksed), CPU_CFG_OFFSET(ext_zvksh), + CPU_CFG_OFFSET(ext_zvkb), CPU_CFG_OFFSET(ext_zvkt), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKSC_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvksc), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvks), CPU_CFG_OFFSET(ext_zvbc), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +static RISCVCPUImpliedExtsRule ZVKSG_IMPLIED = { + .ext = CPU_CFG_OFFSET(ext_zvksg), + .implied_multi_exts = { + CPU_CFG_OFFSET(ext_zvks), CPU_CFG_OFFSET(ext_zvkg), + + RISCV_IMPLIED_EXTS_RULE_END + }, +}; + +RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[] = { + &RVA_IMPLIED, &RVD_IMPLIED, &RVF_IMPLIED, + &RVM_IMPLIED, &RVV_IMPLIED, NULL +}; + +RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[] = { + &ZCB_IMPLIED, &ZCD_IMPLIED, &ZCE_IMPLIED, + &ZCF_IMPLIED, &ZCMP_IMPLIED, &ZCMT_IMPLIED, + &ZDINX_IMPLIED, &ZFA_IMPLIED, &ZFBFMIN_IMPLIED, + &ZFH_IMPLIED, &ZFHMIN_IMPLIED, &ZFINX_IMPLIED, + &ZHINX_IMPLIED, &ZHINXMIN_IMPLIED, &ZICNTR_IMPLIED, + &ZIHPM_IMPLIED, &ZK_IMPLIED, &ZKN_IMPLIED, + &ZKS_IMPLIED, &ZVBB_IMPLIED, &ZVE32F_IMPLIED, + &ZVE32X_IMPLIED, &ZVE64D_IMPLIED, &ZVE64F_IMPLIED, + &ZVE64X_IMPLIED, &ZVFBFMIN_IMPLIED, &ZVFBFWMA_IMPLIED, + &ZVFH_IMPLIED, &ZVFHMIN_IMPLIED, &ZVKN_IMPLIED, + &ZVKNC_IMPLIED, &ZVKNG_IMPLIED, &ZVKNHB_IMPLIED, + &ZVKS_IMPLIED, &ZVKSC_IMPLIED, &ZVKSG_IMPLIED, + NULL +}; + static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true), - 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), + {.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), @@ -1094,39 +2690,56 @@ static Property riscv_cpu_properties[] = { 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), + DEFINE_PROP_BOOL("rvv_vl_half_avl", RISCVCPU, cfg.rvv_vl_half_avl, 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(), }; -static gchar *riscv_gdb_arch_name(CPUState *cs) +#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 = { @@ -1137,66 +2750,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, - .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 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); } -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; - for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) { - if (isa_edata_arr[i].multi_letter && - isa_ext_is_enabled(cpu, &isa_edata_arr[i])) { - 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; } @@ -1207,10 +2810,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]); @@ -1223,40 +2829,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[] = { @@ -1264,29 +2932,65 @@ 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_MAX, MXL_RV32, riscv_max_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_MAX, MXL_RV64, riscv_max_cpu_init), #endif + +#if defined(TARGET_RISCV32) || \ + (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) + 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), +#endif + +#if (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX32, MXL_RV32, riscv_max_cpu_init), +#endif + +#if defined(TARGET_RISCV64) + 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), +#ifdef CONFIG_TCG + DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), +#endif /* CONFIG_TCG */ + 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 /* TARGET_RISCV64 */ }; DEFINE_TYPES(riscv_cpu_type_infos) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 3a9e25053f..284b112821 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -22,47 +22,44 @@ #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" -#define TCG_GUEST_DEFAULT_MO 0 +typedef struct CPUArchState CPURISCVState; -/* - * RISC-V-specific extra insn start words: - * 1: Original instruction opcode - */ -#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 +/* + * RISC-V-specific extra insn start words: + * 1: Original instruction opcode + * 2: more information about instruction + */ +#define TARGET_INSN_START_EXTRA_WORDS 2 +/* + * b0: Whether a instruction always raise a store AMO or not. + */ +#define RISCV_UW2_ALWAYS_STORE_AMO 1 + #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,27 +72,47 @@ #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_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" +#define PRIV_VER_1_13_0_STR "v1.13.0" enum { PRIV_VERSION_1_10_0 = 0, PRIV_VERSION_1_11_0, PRIV_VERSION_1_12_0, + PRIV_VERSION_1_13_0, + + PRIV_VERSION_LATEST = PRIV_VERSION_1_13_0, }; #define VEXT_VERSION_1_00_0 0x00010000 +#define VEXT_VER_1_00_0_STR "v1.0" enum { TRANSLATE_SUCCESS, @@ -104,12 +121,41 @@ enum { TRANSLATE_G_STAGE_FAIL }; +/* Extension context status */ +typedef enum { + EXT_STATUS_DISABLED = 0, + EXT_STATUS_INITIAL, + EXT_STATUS_CLEAN, + EXT_STATUS_DIRTY, +} RISCVExtStatus; + +typedef struct riscv_cpu_implied_exts_rule { +#ifndef CONFIG_USER_ONLY + /* + * Bitmask indicates the rule enabled status for the harts. + * This enhancement is only available in system-mode QEMU, + * as we don't have a good way (e.g. mhartid) to distinguish + * the SMP cores in user-mode QEMU. + */ + unsigned long *enabled; +#endif + /* True if this is a MISA implied rule. */ + bool is_misa; + /* ext is MISA bit if is_misa flag is true, else multi extension offset. */ + const uint32_t ext; + const uint32_t implied_misa_exts; + const uint32_t implied_multi_exts[]; +} RISCVCPUImpliedExtsRule; + +extern RISCVCPUImpliedExtsRule *riscv_misa_ext_implied_rules[]; +extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; + +#define RISCV_IMPLIED_EXTS_RULE_END -1 + #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" @@ -129,21 +175,28 @@ 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*/ + /* 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; +typedef struct PMUFixedCtrState { + /* Track cycle and icount for each privilege mode */ + uint64_t counter[4]; + uint64_t counter_prev[4]; + /* Track cycle and icount for each privilege mode when V = 1*/ + uint64_t counter_virt[2]; + uint64_t counter_virt_prev[2]; +} PMUFixedCtrState; + 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); @@ -158,7 +211,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; @@ -166,12 +222,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 */ @@ -179,16 +233,28 @@ struct CPUArchState { /* 128-bit helpers upper part return value */ target_ulong retxh; - uint32_t features; + target_ulong jvt; + /* elp state for zicfilp extension */ + bool elp; + /* shadow stack register for zicfiss extension */ + target_ulong ssp; + /* env place holder for extra word 2 during unwind */ + target_ulong excp_uw2; + /* sw check code for sw check exception */ + target_ulong sw_check_code; #ifdef CONFIG_USER_ONLY uint32_t elf_flags; #endif -#ifndef CONFIG_USER_ONLY target_ulong priv; + /* CSRs for execution environment configuration */ + uint64_t menvcfg; + target_ulong senvcfg; + +#ifndef CONFIG_USER_ONLY /* This contains QEMU specific information about the virt state. */ - target_ulong virt; + bool virt_enabled; target_ulong geilen; uint64_t resetvec; @@ -214,6 +280,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; @@ -234,18 +312,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; @@ -283,8 +371,10 @@ 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 @@ -292,27 +382,31 @@ struct CPUArchState { */ bool two_stage_indirect_lookup; - target_ulong scounteren; - target_ulong mcounteren; + uint32_t scounteren; + uint32_t mcounteren; - target_ulong mcountinhibit; + uint32_t mcountinhibit; + + /* PMU cycle & instret privilege mode filtering */ + target_ulong mcyclecfg; + target_ulong mcyclecfgh; + target_ulong minstretcfg; + target_ulong minstretcfgh; /* PMU counter state */ PMUCTRState pmu_ctrs[RV_MAX_MHPMCOUNTERS]; - /* PMU event selector configured values. First three are unused*/ + /* PMU event selector configured values. First three are unused */ target_ulong mhpmevent_val[RV_MAX_MHPMEVENTS]; - /* PMU event selector configured values for RV32*/ + /* PMU event selector configured values for RV32 */ target_ulong mhpmeventh_val[RV_MAX_MHPMEVENTS]; + PMUFixedCtrState pmu_fixed_ctrs[2]; + target_ulong sscratch; target_ulong mscratch; - /* temporary htif regs */ - uint64_t mfromhost; - uint64_t mtohost; - /* Sstc CSRs */ uint64_t stimecmp; @@ -327,8 +421,12 @@ struct CPUArchState { 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 *); @@ -364,16 +462,14 @@ struct CPUArchState { target_ulong upmmask; target_ulong upmbase; - /* CSRs for execution enviornment configuration */ - uint64_t menvcfg; - target_ulong senvcfg; + uint64_t mstateen[SMSTATEEN_MAX_COUNT]; + uint64_t hstateen[SMSTATEEN_MAX_COUNT]; + uint64_t sstateen[SMSTATEEN_MAX_COUNT]; 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 *stimer; /* Internal timer for S-mode interrupt */ QEMUTimer *vstimer; /* Internal timer for VS-mode interrupt */ @@ -382,123 +478,29 @@ struct CPUArchState { 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_ifencei; - bool ext_icsr; - bool ext_zihintpause; - bool ext_sstc; - 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; - bool ext_zmmul; - bool ext_smaia; - bool ext_ssaia; - bool ext_sscofpmf; - bool rvv_ta_all_1s; - bool rvv_ma_all_1s; - - uint32_t mvendorid; - uint64_t marchid; - uint64_t mimpid; - - /* Vendor-specific custom extensions */ - bool ext_XVentanaCondOps; - - uint8_t pmu_num; - 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 debug; - - 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; @@ -508,6 +510,22 @@ struct ArchCPU { uint32_t pmu_avail_ctrs; /* Mapping of events to counters */ GHashTable *pmu_event_ctr_map; + const GPtrArray *decoders; +}; + +/** + * 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) @@ -515,16 +533,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[]; @@ -532,7 +540,6 @@ extern const char * const riscv_int_regnamesh[]; 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, DumpState *s); int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, @@ -549,33 +556,35 @@ 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); +bool cpu_get_fcfien(CPURISCVState *env); +bool cpu_get_bcfien(CPURISCVState *env); 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_cpu_do_interrupt(CPUState *cpu); +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); @@ -586,8 +595,11 @@ 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); -#endif -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); + +RISCVException smstateen_acc_ok(CPURISCVState *env, int index, uint64_t bit); +#endif /* !CONFIG_USER_ONLY */ + +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en); void riscv_translate_init(void); G_NORETURN void riscv_raise_exception(CPURISCVState *env, @@ -596,31 +608,35 @@ 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, VTA, 24, 1) -FIELD(TB_FLAGS, VMA, 25, 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) +/* zicfilp needs a TB flag to track indirect branches */ +FIELD(TB_FLAGS, FCFI_ENABLED, 28, 1) +FIELD(TB_FLAGS, FCFI_LP_EXPECTED, 29, 1) +/* zicfiss needs a TB flag so that correct TB is located based on tb flags */ +FIELD(TB_FLAGS, BCFI_ENABLED, 30, 1) #ifdef TARGET_RISCV32 #define riscv_cpu_mxl(env) ((void)(env), MXL_RV32) @@ -632,13 +648,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, @@ -646,22 +674,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; @@ -675,8 +730,11 @@ static inline RISCVMXL riscv_cpu_sxl(CPURISCVState *env) #ifdef CONFIG_USER_ONLY return env->misa_mxl; #else - return get_field(env->mstatus, MSTATUS64_SXL); + if (env->misa_mxl != MXL_RV32) { + return get_field(env->mstatus, MSTATUS64_SXL); + } #endif + return MXL_RV32; } #endif @@ -698,18 +756,27 @@ 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_csrr(CPURISCVState *env, int csrno, + target_ulong *ret_value); RISCVException riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); @@ -742,6 +809,8 @@ typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, target_ulong new_value, target_ulong write_mask); +RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, + Int128 *ret_value); RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, Int128 *ret_value, Int128 new_value, Int128 write_mask); @@ -768,7 +837,7 @@ enum { CSR_TABLE_SIZE = 0x1000 }; -/** +/* * The event id are encoded based on the encoding specified in the * SBI specification v0.3 */ @@ -781,12 +850,54 @@ enum riscv_pmu_event_idx { 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); +target_ulong riscv_new_csr_seed(target_ulong new_value, + target_ulong write_mask); + +uint8_t satp_mode_max_from_map(uint32_t map); +const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); + +/* Implemented in th_csr.c */ +void th_register_custom_csrs(RISCVCPU *cpu); + +const char *priv_spec_to_str(int priv_version); #endif /* RISCV_CPU_H */ diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index d8f5f0abed..385a2c67c2 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,16 +32,11 @@ #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 */ +/* zicfiss user ssp csr */ +#define CSR_SSP 0x011 + /* User Trap Setup */ #define CSR_USTATUS 0x000 #define CSR_UIE 0x004 @@ -161,6 +159,8 @@ /* 32-bit only */ #define CSR_MSTATUSH 0x310 +#define CSR_MEDELEGH 0x312 +#define CSR_HEDELEGH 0x612 /* Machine Trap Handling */ #define CSR_MSCRATCH 0x340 @@ -197,6 +197,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 @@ -244,6 +250,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 @@ -289,6 +305,29 @@ #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_P1P13 (1ULL << 56) +#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 @@ -320,6 +359,7 @@ #define CSR_TDATA2 0x7a2 #define CSR_TDATA3 0x7a3 #define CSR_TINFO 0x7a4 +#define CSR_MCONTEXT 0x7a8 /* Debug Mode Registers */ #define CSR_DCSR 0x7b0 @@ -360,6 +400,10 @@ /* Machine counter-inhibit register */ #define CSR_MCOUNTINHIBIT 0x320 +/* Machine counter configuration registers */ +#define CSR_MCYCLECFG 0x321 +#define CSR_MINSTRETCFG 0x322 + #define CSR_MHPMEVENT3 0x323 #define CSR_MHPMEVENT4 0x324 #define CSR_MHPMEVENT5 0x325 @@ -390,6 +434,9 @@ #define CSR_MHPMEVENT30 0x33e #define CSR_MHPMEVENT31 0x33f +#define CSR_MCYCLECFGH 0x721 +#define CSR_MINSTRETCFGH 0x722 + #define CSR_MHPMEVENT3H 0x723 #define CSR_MHPMEVENT4H 0x724 #define CSR_MHPMEVENT5H 0x725 @@ -486,6 +533,9 @@ /* Crypto Extension */ #define CSR_SEED 0x015 +/* Zcmt Extension */ +#define CSR_JVT 0x017 + /* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 #define MSTATUS_SIE 0x00000002 @@ -505,6 +555,8 @@ #define MSTATUS_TVM 0x00100000 /* since: priv-1.10 */ #define MSTATUS_TW 0x00200000 /* since: priv-1.10 */ #define MSTATUS_TSR 0x00400000 /* since: priv-1.10 */ +#define MSTATUS_SPELP 0x00800000 /* zicfilp */ +#define MSTATUS_MPELP 0x020000000000 /* zicfilp */ #define MSTATUS_GVA 0x4000000000ULL #define MSTATUS_MPV 0x8000000000ULL @@ -535,6 +587,7 @@ typedef enum { #define SSTATUS_XS 0x00018000 #define SSTATUS_SUM 0x00040000 /* since: priv-1.10 */ #define SSTATUS_MXR 0x00080000 +#define SSTATUS_SPELP MSTATUS_SPELP /* zicfilp */ #define SSTATUS64_UXL 0x0000000300000000ULL @@ -567,12 +620,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 @@ -603,6 +653,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 */ @@ -614,7 +665,7 @@ typedef enum { /* Leaf page shift amount */ #define PGSHIFT 12 -/* Default Reset Vector adress */ +/* Default Reset Vector address */ #define DEFAULT_RSTVEC 0x1000 /* Exception causes */ @@ -635,13 +686,20 @@ typedef enum RISCVException { RISCV_EXCP_INST_PAGE_FAULT = 0xc, /* since: priv-1.10.0 */ RISCV_EXCP_LOAD_PAGE_FAULT = 0xd, /* since: priv-1.10.0 */ RISCV_EXCP_STORE_PAGE_FAULT = 0xf, /* since: priv-1.10.0 */ - RISCV_EXCP_SEMIHOST = 0x10, + RISCV_EXCP_SW_CHECK = 0x12, /* since: priv-1.13.0 */ + RISCV_EXCP_HW_ERR = 0x13, /* since: priv-1.13.0 */ RISCV_EXCP_INST_GUEST_PAGE_FAULT = 0x14, RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT = 0x15, RISCV_EXCP_VIRT_INSTRUCTION_FAULT = 0x16, RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT = 0x17, + RISCV_EXCP_SEMIHOST = 0x3f, } RISCVException; +/* zicfilp defines lp violation results in sw check with tval = 2*/ +#define RISCV_EXCP_SW_CHECK_FCFI_TVAL 2 +/* zicfiss defines ss violation results in sw check with tval = 3*/ +#define RISCV_EXCP_SW_CHECK_BCFI_TVAL 3 + #define RISCV_EXCP_INT_FLAG 0x80000000 #define RISCV_EXCP_INT_MASK 0x7fffffff @@ -660,7 +718,8 @@ typedef enum RISCVException { #define IRQ_M_EXT 11 #define IRQ_S_GEXT 12 #define IRQ_PMU_OVF 13 -#define IRQ_LOCAL_MAX 16 +#define IRQ_LOCAL_MAX 64 +/* -1 is due to bit zero of hgeip and hgeie being ROZ. */ #define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) /* mip masks */ @@ -693,43 +752,52 @@ 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_LPE BIT(2) /* zicfilp */ +#define MENVCFG_SSE BIT(3) /* zicfiss */ #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) #define SENVCFG_FIOM MENVCFG_FIOM +#define SENVCFG_LPE MENVCFG_LPE +#define SENVCFG_SSE MENVCFG_SSE #define SENVCFG_CBIE MENVCFG_CBIE #define SENVCFG_CBCFE MENVCFG_CBCFE #define SENVCFG_CBZE MENVCFG_CBZE #define HENVCFG_FIOM MENVCFG_FIOM +#define HENVCFG_LPE MENVCFG_LPE +#define HENVCFG_SSE MENVCFG_SSE #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 @@ -739,7 +807,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) @@ -840,6 +908,28 @@ typedef enum RISCVException { /* PMU related bits */ #define MIE_LCOFIE (1 << IRQ_PMU_OVF) +#define MCYCLECFG_BIT_MINH BIT_ULL(62) +#define MCYCLECFGH_BIT_MINH BIT(30) +#define MCYCLECFG_BIT_SINH BIT_ULL(61) +#define MCYCLECFGH_BIT_SINH BIT(29) +#define MCYCLECFG_BIT_UINH BIT_ULL(60) +#define MCYCLECFGH_BIT_UINH BIT(28) +#define MCYCLECFG_BIT_VSINH BIT_ULL(59) +#define MCYCLECFGH_BIT_VSINH BIT(27) +#define MCYCLECFG_BIT_VUINH BIT_ULL(58) +#define MCYCLECFGH_BIT_VUINH BIT(26) + +#define MINSTRETCFG_BIT_MINH BIT_ULL(62) +#define MINSTRETCFGH_BIT_MINH BIT(30) +#define MINSTRETCFG_BIT_SINH BIT_ULL(61) +#define MINSTRETCFGH_BIT_SINH BIT(29) +#define MINSTRETCFG_BIT_UINH BIT_ULL(60) +#define MINSTRETCFGH_BIT_UINH BIT(28) +#define MINSTRETCFG_BIT_VSINH BIT_ULL(59) +#define MINSTRETCFGH_BIT_VSINH BIT(27) +#define MINSTRETCFG_BIT_VUINH BIT_ULL(58) +#define MINSTRETCFGH_BIT_VUINH BIT(26) + #define MHPMEVENT_BIT_OF BIT_ULL(63) #define MHPMEVENTH_BIT_OF BIT(31) #define MHPMEVENT_BIT_MINH BIT_ULL(62) @@ -853,8 +943,39 @@ typedef enum RISCVException { #define MHPMEVENT_BIT_VUINH BIT_ULL(58) #define MHPMEVENTH_BIT_VUINH BIT(26) +#define MHPMEVENT_FILTER_MASK (MHPMEVENT_BIT_MINH | \ + MHPMEVENT_BIT_SINH | \ + MHPMEVENT_BIT_UINH | \ + MHPMEVENT_BIT_VSINH | \ + MHPMEVENT_BIT_VUINH) + +#define MHPMEVENTH_FILTER_MASK (MHPMEVENTH_BIT_MINH | \ + MHPMEVENTH_BIT_SINH | \ + MHPMEVENTH_BIT_UINH | \ + MHPMEVENTH_BIT_VSINH | \ + MHPMEVENTH_BIT_VUINH) + #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 TEXTRA32_MHVALUE 0xFC000000 +#define TEXTRA32_MHSELECT 0x03800000 +#define TEXTRA32_SBYTEMASK 0x000C0000 +#define TEXTRA32_SVALUE 0x0003FFFC +#define TEXTRA32_SSELECT 0x00000003 +#define TEXTRA64_MHVALUE 0xFFF8000000000000ULL +#define TEXTRA64_MHSELECT 0x0007000000000000ULL +#define TEXTRA64_SBYTEMASK 0x000000F000000000ULL +#define TEXTRA64_SVALUE 0x00000003FFFFFFFCULL +#define TEXTRA64_SSELECT 0x0000000000000003ULL +#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..59d6fc445d --- /dev/null +++ b/target/riscv/cpu_cfg.h @@ -0,0 +1,222 @@ +/* + * 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_zicfilp; + bool ext_zicfiss; + bool ext_zicond; + bool ext_zihintntl; + bool ext_zihintpause; + bool ext_zihpm; + bool ext_zimop; + bool ext_zcmop; + bool ext_ztso; + bool ext_smstateen; + bool ext_sstc; + bool ext_smcntrpmf; + bool ext_svadu; + bool ext_svinval; + bool ext_svnapot; + bool ext_svpbmt; + bool ext_svvptc; + bool ext_zdinx; + bool ext_zaamo; + bool ext_zacas; + bool ext_zama16b; + bool ext_zabha; + 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_zve32x; + bool ext_zve64f; + bool ext_zve64d; + bool ext_zve64x; + 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; + bool rvv_vl_half_avl; + + 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_13; + 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 278d163803..45806f5ab0 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -21,35 +21,116 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "cpu.h" +#include "internals.h" #include "pmu.h" #include "exec/exec-all.h" +#include "exec/page-protection.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" +#include "pmp.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) +bool cpu_get_fcfien(CPURISCVState *env) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); + /* no cfi extension, return false */ + if (!env_archcpu(env)->cfg.ext_zicfilp) { + return false; + } + switch (env->priv) { + case PRV_U: + if (riscv_has_ext(env, RVS)) { + return env->senvcfg & SENVCFG_LPE; + } + return env->menvcfg & MENVCFG_LPE; +#ifndef CONFIG_USER_ONLY + case PRV_S: + if (env->virt_enabled) { + return env->henvcfg & HENVCFG_LPE; + } + return env->menvcfg & MENVCFG_LPE; + case PRV_M: + return env->mseccfg & MSECCFG_MLPE; +#endif + default: + g_assert_not_reached(); + } +} + +bool cpu_get_bcfien(CPURISCVState *env) +{ + /* no cfi extension, return false */ + if (!env_archcpu(env)->cfg.ext_zicfiss) { + return false; + } + + switch (env->priv) { + case PRV_U: + /* + * If S is not implemented then shadow stack for U can't be turned on + * It is checked in `riscv_cpu_validate_set_extensions`, so no need to + * check here or assert here + */ + return env->senvcfg & SENVCFG_SSE; +#ifndef CONFIG_USER_ONLY + case PRV_S: + if (env->virt_enabled) { + return env->henvcfg & HENVCFG_SSE; + } + return env->menvcfg & MENVCFG_SSE; + case PRV_M: /* M-mode shadow stack is always off */ + return false; +#endif + default: + g_assert_not_reached(); + } +} + +void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + 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_zve32x) { /* * If env->vl equals to VLMAX, we can use generic vector operation * expanders (GVEC) to accerlate the vector operations. @@ -58,55 +139,78 @@ 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)); + FIELD_EX64(env->vtype, VTYPE, VTA)); flags = FIELD_DP32(flags, TB_FLAGS, VMA, - FIELD_EX64(env->vtype, VTYPE, 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); } + if (cpu_get_fcfien(env)) { + /* + * For Forward CFI, only the expectation of a lpad at + * the start of the block is tracked via env->elp. env->elp + * is turned on during jalr translation. + */ + flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED, env->elp); + flags = FIELD_DP32(flags, TB_FLAGS, FCFI_ENABLED, 1); + } + + if (cpu_get_bcfien(env)) { + flags = FIELD_DP32(flags, TB_FLAGS, BCFI_ENABLED, 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) { @@ -118,14 +222,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; @@ -149,7 +256,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 { @@ -225,75 +332,75 @@ int riscv_cpu_hviprio_index2irq(int index, int *out_irq, int *out_rdzero) * ---------------------------------------------------------------- */ static const uint8_t default_iprio[64] = { - /* 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, + /* 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, - /* 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, + /* 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, - [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, + [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, - [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, + [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, - [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, + [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, - [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, + [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) @@ -309,7 +416,6 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, int extirq, unsigned int extirq_def_prio, uint64_t pending, uint8_t *iprio) { - RISCVCPU *cpu = env_archcpu(env); int irq, best_irq = RISCV_EXCP_NONE; unsigned int prio, best_prio = UINT_MAX; @@ -318,7 +424,8 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, } irq = ctz64(pending); - if (!((extirq == IRQ_M_EXT) ? cpu->cfg.ext_smaia : cpu->cfg.ext_ssaia)) { + if (!((extirq == IRQ_M_EXT) ? riscv_cpu_cfg(env)->ext_smaia : + riscv_cpu_cfg(env)->ext_ssaia)) { return irq; } @@ -344,6 +451,11 @@ static int riscv_cpu_pending_to_irq(CPURISCVState *env, return best_irq; } +/* + * 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); @@ -366,27 +478,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) || @@ -409,19 +529,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 */ @@ -447,7 +584,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; @@ -460,7 +597,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; @@ -478,7 +615,16 @@ 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; + + /* + * If zicfilp extension available and henvcfg.LPE = 1, + * then apply SPELP mask on mstatus + */ + if (env_archcpu(env)->cfg.ext_zicfilp && + get_field(env->henvcfg, HENVCFG_LPE)) { + mstatus_mask |= SSTATUS_SPELP; + } g_assert(riscv_has_ext(env, RVH)); @@ -553,47 +699,6 @@ 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); -} - -void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) -{ - if (!riscv_has_ext(env, RVH)) { - return; - } - - /* Flush the TLB on all virt mode changes. */ - if (get_field(env->virt, VIRT_ONOFF) != enable) { - tlb_flush(env_cpu(env)); - } - - env->virt = set_field(env->virt, VIRT_ONOFF, enable); - - if (enable) { - /* - * The guest external interrupts from an interrupt controller are - * delivered only when the Guest/VM is running (i.e. V=1). This means - * any guest external interrupt which is triggered while the Guest/VM - * is not running (i.e. V=0) will be missed on QEMU resulting in guest - * with sluggish response to serial console input and other I/O events. - * - * To solve this, we check and inject interrupt after setting V=1. - */ - riscv_cpu_update_mip(env_archcpu(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; @@ -605,38 +710,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, vstip = 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; } - /* No need to update mip for VSTIP */ - mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask; vstip = env->vstime_irq ? MIP_VSTIP : 0; - if (!qemu_mutex_iothread_locked()) { - locked = true; - qemu_mutex_lock_iothread(); - } - - env->mip = (env->mip & ~mask) | (value & mask); - - if (env->mip | vsgein | vstip) { + 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; } @@ -662,14 +771,18 @@ void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, } } -void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv, bool virt_en) { - if (newpriv > PRV_M) { - g_assert_not_reached(); - } - if (newpriv == PRV_H) { - newpriv = PRV_U; + g_assert(newpriv <= PRV_M && newpriv != PRV_RESERVED); + + if (newpriv != env->priv || env->virt_enabled != virt_en) { + if (icount_enabled()) { + riscv_itrigger_update_priv(env); + } + + riscv_pmu_update_fixed_ctrs(env, newpriv, virt_en); } + /* tlb_flush is unnecessary as mode is contained in mmu_idx */ env->priv = newpriv; env->xl = cpu_recompute_xl(env); @@ -684,6 +797,28 @@ void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) * preemptive context switch. As a result, do both. */ env->load_res = -1; + + if (riscv_has_ext(env, RVH)) { + /* Flush the TLB on all virt mode changes. */ + if (env->virt_enabled != virt_en) { + tlb_flush(env_cpu(env)); + } + + env->virt_enabled = virt_en; + if (virt_en) { + /* + * The guest external interrupts from an interrupt controller are + * delivered only when the Guest/VM is running (i.e. V=1). This + * means any guest external interrupt which is triggered while the + * Guest/VM is not running (i.e. V=0) will be missed on QEMU + * resulting in guest with sluggish response to serial console + * input and other I/O events. + * + * To solve this, we check and inject interrupt after setting V=1. + */ + riscv_cpu_update_mip(env, 0, 0); + } + } } /* @@ -694,42 +829,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 @@ -739,7 +868,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. @@ -751,23 +880,26 @@ 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) + bool is_debug, bool is_probe) { - /* 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; + bool is_sstack_idx = ((mmu_idx & MMU_IDX_SS_WRITE) == MMU_IDX_SS_WRITE); + bool sstack_page = false; /* * Check if we should use the background registers for the two @@ -776,42 +908,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) { @@ -842,8 +952,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; @@ -855,7 +964,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(); @@ -863,20 +972,41 @@ 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; + int sxlen = 16 << riscv_cpu_sxl(env); + int sxlen_bytes = sxlen / 8; - 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 (sxlen > (va_bits - 1)) { + mask = (1L << (sxlen - (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 @@ -893,7 +1023,6 @@ restart: } /* check that physical address of PTE is legal */ - hwaddr pte_addr; if (two_stage && first_stage) { int vbase_prot; @@ -902,8 +1031,8 @@ 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, - is_debug); + MMUIdx_U, false, true, + is_debug, false); if (vbase_ret != TRANSLATE_SUCCESS) { if (fault_pte_addr) { @@ -918,14 +1047,13 @@ restart: } int pmp_prot; - int pmp_ret = get_physical_address_pmp(env, &pmp_prot, NULL, pte_addr, - sizeof(target_ulong), + int pmp_ret = get_physical_address_pmp(env, &pmp_prot, pte_addr, + sxlen_bytes, 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 { @@ -938,128 +1066,223 @@ 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; + } + + target_ulong rwx = pte & (PTE_R | PTE_W | PTE_X); + /* Check for reserved combinations of RWX flags. */ + switch (rwx) { + case PTE_W | PTE_X: + return TRANSLATE_FAIL; + case PTE_W: + /* if bcfi enabled, PTE_W is not reserved and shadow stack page */ + if (cpu_get_bcfien(env) && first_stage) { + sstack_page = true; + /* + * if ss index, read and write allowed. else if not a probe + * then only read allowed + */ + rwx = is_sstack_idx ? (PTE_R | PTE_W) : (is_probe ? 0 : PTE_R); + break; + } + return TRANSLATE_FAIL; + case PTE_R: + /* + * no matter what's the `access_type`, shadow stack access to readonly + * memory are always store page faults. During unwind, loads will be + * promoted as store fault. + */ + if (is_sstack_idx) { + return TRANSLATE_FAIL; + } + break; + } + + int prot = 0; + if (rwx & PTE_R) { + prot |= PAGE_READ; + } + if (rwx & PTE_W) { + prot |= PAGE_WRITE; + } + if (rwx & 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, access check failures for shadow stack are + * access faults. + */ + return sstack_page ? TRANSLATE_PMP_FAIL : 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 = sxlen_bytes, 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; + if (riscv_cpu_sxl(env) == MXL_RV32) { + old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, pte, updated_pte); + } else { + 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, @@ -1068,46 +1291,33 @@ static void raise_mmu_exception(CPURISCVState *env, target_ulong address, 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 (pmp_violation) { + cs->exception_index = RISCV_EXCP_INST_ACCESS_FAULT; + } else 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 = RISCV_EXCP_INST_PAGE_FAULT; } break; case MMU_DATA_LOAD: - if (two_stage && !first_stage) { + if (pmp_violation) { + cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; + } else 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 = RISCV_EXCP_LOAD_PAGE_FAULT; } break; case MMU_DATA_STORE: - if (two_stage && !first_stage) { + if (pmp_violation) { + cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } else 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 = RISCV_EXCP_STORE_PAGE_FAULT; } break; default: @@ -1124,16 +1334,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, false)) { 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, false)) { return -1; } } @@ -1159,8 +1369,7 @@ void riscv_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, } env->badaddr = addr; - env->two_stage_lookup = riscv_cpu_virt_enabled(env) || - riscv_cpu_two_stage_lookup(mmu_idx); + env->two_stage_lookup = mmuidx_2stage(mmu_idx); env->two_stage_indirect_lookup = false; cpu_loop_exit_restore(cs, retaddr); } @@ -1177,16 +1386,23 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, break; case MMU_DATA_LOAD: cs->exception_index = RISCV_EXCP_LOAD_ADDR_MIS; + /* shadow stack mis aligned accesses are access faults */ + if (mmu_idx & MMU_IDX_SS_WRITE) { + cs->exception_index = RISCV_EXCP_LOAD_ACCESS_FAULT; + } break; case MMU_DATA_STORE: cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS; + /* shadow stack mis aligned accesses are access faults */ + if (mmu_idx & MMU_IDX_SS_WRITE) { + cs->exception_index = RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + } break; default: 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); } @@ -1224,37 +1440,24 @@ 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; + hwaddr tlb_size = TARGET_PAGE_SIZE; env->guest_phys_fault_addr = 0; qemu_log_mask(CPU_LOG_MMU, "%s ad %" VADDR_PRIx " rw %d mmu_idx %d\n", __func__, address, access_type, mmu_idx); - /* 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, - mmu_idx, true, true, false); + mmu_idx, true, true, false, probe); /* * A G-stage exception may be triggered during two state lookup. @@ -1264,12 +1467,11 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size, if (ret == TRANSLATE_G_STAGE_FAIL) { first_stage_error = false; two_stage_indirect_error = true; - access_type = MMU_DATA_LOAD; } 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) { @@ -1277,57 +1479,60 @@ 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, - false); + access_type, MMUIdx_U, false, true, + false, probe); 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" - " %d tlb_size " TARGET_FMT_lu "\n", + "%s PMP address=" HWADDR_FMT_plx " ret %d prot" + " %d tlb_size %" HWADDR_PRIu "\n", __func__, pa, ret, prot_pmp, tlb_size); prot &= prot_pmp; - } - - if (ret != TRANSLATE_SUCCESS) { + } else { /* * Guest physical address translation failed, this is a HS * level exception */ first_stage_error = false; - env->guest_phys_fault_addr = (im_address | - (address & - (TARGET_PAGE_SIZE - 1))) >> 2; + if (ret != TRANSLATE_PMP_FAIL) { + env->guest_phys_fault_addr = (im_address | + (address & + (TARGET_PAGE_SIZE - 1))) >> 2; + } } } } else { - pmu_tlb_fill_incr_ctr(cpu, access_type); /* Single stage lookup */ ret = get_physical_address(env, &pa, &prot, address, NULL, - access_type, mmu_idx, true, false, false); + access_type, mmu_idx, true, false, false, + probe); 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" - " %d tlb_size " TARGET_FMT_lu "\n", + "%s PMP address=" HWADDR_FMT_plx " ret %d prot" + " %d tlb_size %" HWADDR_PRIu "\n", __func__, pa, ret, prot_pmp, tlb_size); prot &= prot_pmp; @@ -1346,9 +1551,7 @@ 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); } @@ -1560,8 +1763,23 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env, return xinsn; } -#endif /* !CONFIG_USER_ONLY */ +static target_ulong promote_load_fault(target_ulong orig_cause) +{ + switch (orig_cause) { + case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: + return RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT; + + case RISCV_EXCP_LOAD_ACCESS_FAULT: + return RISCV_EXCP_STORE_AMO_ACCESS_FAULT; + + case RISCV_EXCP_LOAD_PAGE_FAULT: + return RISCV_EXCP_STORE_PAGE_FAULT; + } + + /* if no promotion, return original cause */ + return orig_cause; +} /* * Handle Traps * @@ -1570,33 +1788,40 @@ static target_ulong riscv_transformed_insn(CPURISCVState *env, */ void riscv_cpu_do_interrupt(CPUState *cs) { -#if !defined(CONFIG_USER_ONLY) - RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + bool virt = env->virt_enabled; bool write_gva = false; + bool always_storeamo = (env->excp_uw2 & RISCV_UW2_ALWAYS_STORE_AMO); 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 & (1ULL << cause) & env->mvien && + !(env->mip & (1ULL << cause)); + bool vs_injected = env->hvip & (1ULL << cause) & env->hvien && + !(env->mip & (1ULL << cause)); target_ulong tval = 0; target_ulong tinst = 0; target_ulong htval = 0; target_ulong mtval2 = 0; - - if (cause == RISCV_EXCP_SEMIHOST) { - do_common_semihosting(cs); - env->pc += 4; - return; - } + int sxlen = 0; + int mxlen = 0; if (!async) { /* set tval to badaddr for traps with address information */ switch (cause) { +#ifdef CONFIG_TCG + case RISCV_EXCP_SEMIHOST: + do_common_semihosting(cs); + env->pc += 4; + return; +#endif case RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT: case RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT: case RISCV_EXCP_LOAD_ADDR_MIS: @@ -1605,6 +1830,9 @@ void riscv_cpu_do_interrupt(CPUState *cs) case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: case RISCV_EXCP_LOAD_PAGE_FAULT: case RISCV_EXCP_STORE_PAGE_FAULT: + if (always_storeamo) { + cause = promote_load_fault(cause); + } write_gva = env->two_stage_lookup; tval = env->badaddr; if (env->two_stage_indirect_lookup) { @@ -1639,6 +1867,16 @@ void riscv_cpu_do_interrupt(CPUState *cs) case RISCV_EXCP_VIRT_INSTRUCTION_FAULT: tval = env->bins; break; + case RISCV_EXCP_BREAKPOINT: + tval = env->badaddr; + if (cs->watchpoint_hit) { + tval = cs->watchpoint_hit->hitaddr; + cs->watchpoint_hit = NULL; + } + break; + case RISCV_EXCP_SW_CHECK: + tval = env->sw_check_code; + break; default: break; } @@ -1648,9 +1886,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; @@ -1667,35 +1905,39 @@ 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 */ + /* save elp status */ + if (cpu_get_fcfien(env)) { + env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, env->elp); + } + 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; - riscv_cpu_set_virt_enabled(env, 0); + virt = false; } else { /* Trap into HS mode */ env->hstatus = set_field(env->hstatus, HSTATUS_SPV, false); @@ -1709,30 +1951,36 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); env->mstatus = s; - env->scause = cause | ((target_ulong)async << (TARGET_LONG_BITS - 1)); + sxlen = 16 << riscv_cpu_sxl(env); + env->scause = cause | ((target_ulong)async << (sxlen - 1)); 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); - riscv_cpu_set_mode(env, PRV_S); + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); + riscv_cpu_set_mode(env, PRV_S, virt); } else { /* handle the trap in M-mode */ + /* save elp status */ + if (cpu_get_fcfien(env)) { + env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, env->elp); + } + 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); } mtval2 = env->guest_phys_fault_addr; /* Trapping to M mode, virt is disabled */ - riscv_cpu_set_virt_enabled(env, 0); + virt = false; } s = env->mstatus; @@ -1740,17 +1988,26 @@ void riscv_cpu_do_interrupt(CPUState *cs) s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); env->mstatus = s; - env->mcause = cause | ~(((target_ulong)-1) >> async); + mxlen = 16 << riscv_cpu_mxl(env); + env->mcause = cause | ((target_ulong)async << (mxlen - 1)); 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); - riscv_cpu_set_mode(env, PRV_M); + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); + riscv_cpu_set_mode(env, PRV_M, virt); } - /* NOTE: it is not necessary to yield load reservations here. It is only + /* + * Interrupt/exception/trap delivery is asynchronous event and as per + * zicfilp spec CPU should clear up the ELP state. No harm in clearing + * unconditionally. + */ + env->elp = false; + + /* + * 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. @@ -1758,6 +2015,6 @@ void riscv_cpu_do_interrupt(CPUState *cs) env->two_stage_lookup = false; env->two_stage_indirect_lookup = false; -#endif - cs->exception_index = RISCV_EXCP_NONE; /* mark handled to qemu */ } + +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h index 02afad608b..e6927ff847 100644 --- a/target/riscv/cpu_user.h +++ b/target/riscv/cpu_user.h @@ -15,5 +15,6 @@ #define xA6 16 #define xA7 17 /* syscall number for RVI ABI */ #define xT0 5 /* syscall number for RVE ABI */ +#define xT2 7 #endif 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 5c9a7ee287..9846770820 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -21,10 +21,11 @@ #include "qemu/log.h" #include "qemu/timer.h" #include "cpu.h" +#include "tcg/tcg-cpu.h" #include "pmu.h" #include "time_helper.h" -#include "qemu/main-loop.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" @@ -41,24 +42,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_zve32x) { #if !defined(CONFIG_USER_ONLY) if (!env->debugger && !riscv_cpu_vector_enabled(env)) { return RISCV_EXCP_ILLEGAL_INST; @@ -72,8 +106,7 @@ 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; @@ -88,6 +121,10 @@ static RISCVException ctr(CPURISCVState *env, int csrno) 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; } @@ -98,11 +135,15 @@ static RISCVException ctr(CPURISCVState *env, int csrno) 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 (riscv_cpu_virt_enabled(env)) { + 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; @@ -127,21 +168,59 @@ 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; +} + +static RISCVException cfi_ss(CPURISCVState *env, int csrno) +{ + if (!env_archcpu(env)->cfg.ext_zicfiss) { + return RISCV_EXCP_ILLEGAL_INST; + } + + /* if bcfi not active for current env, access to csr is illegal */ + if (!cpu_get_bcfien(env)) { +#if !defined(CONFIG_USER_ONLY) + if (env->debugger) { + return RISCV_EXCP_NONE; + } +#endif + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + #if !defined(CONFIG_USER_ONLY) static RISCVException mctr(CPURISCVState *env, int csrno) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); + 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 */ - base_csrno += 0x80; + csrno -= 0x80; } + + g_assert(csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31); + ctr_index = csrno - base_csrno; - if (!cpu->cfg.pmu_num || ctr_index >= cpu->cfg.pmu_num) { - /* The PMU is not enabled or counter is out of range*/ + 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; } @@ -159,16 +238,40 @@ static RISCVException mctr32(CPURISCVState *env, int csrno) static RISCVException sscofpmf(CPURISCVState *env, int csrno) { - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - - if (!cpu->cfg.ext_sscofpmf) { + if (!riscv_cpu_cfg(env)->ext_sscofpmf) { return RISCV_EXCP_ILLEGAL_INST; } return RISCV_EXCP_NONE; } +static RISCVException sscofpmf_32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return sscofpmf(env, csrno); +} + +static RISCVException smcntrpmf(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_cfg(env)->ext_smcntrpmf) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException smcntrpmf_32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smcntrpmf(env, csrno); +} + static RISCVException any(CPURISCVState *env, int csrno) { return RISCV_EXCP_NONE; @@ -184,22 +287,18 @@ static RISCVException any32(CPURISCVState *env, int csrno) } -static int aia_any(CPURISCVState *env, int csrno) +static RISCVException aia_any(CPURISCVState *env, int csrno) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_smaia) { + 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) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_smaia) { + if (!riscv_cpu_cfg(env)->ext_smaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -215,7 +314,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; @@ -224,22 +323,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) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_ssaia) { + 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) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_ssaia) { + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -283,6 +378,159 @@ static RISCVException umode32(CPURISCVState *env, int csrno) 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) { @@ -293,22 +541,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) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_ssaia) { + 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) { - RISCVCPU *cpu = env_archcpu(env); - - if (!cpu->cfg.ext_ssaia) { + if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -317,16 +561,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; } @@ -335,7 +591,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; } @@ -345,13 +601,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. @@ -363,7 +621,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 { @@ -383,6 +641,19 @@ static RISCVException seed(CPURISCVState *env, int csrno) #endif } +/* zicfiss CSR_SSP read and write */ +static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->ssp; + return RISCV_EXCP_NONE; +} + +static int write_ssp(CPURISCVState *env, int csrno, target_ulong val) +{ + env->ssp = val; + return RISCV_EXCP_NONE; +} + /* User Floating-Point CSRs */ static RISCVException read_fflags(CPURISCVState *env, int csrno, target_ulong *val) @@ -468,9 +739,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; } @@ -494,7 +766,7 @@ static RISCVException write_vxrm(CPURISCVState *env, int csrno, static RISCVException read_vxsat(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->vxsat; + *val = env->vxsat & BIT(0); return RISCV_EXCP_NONE; } @@ -504,7 +776,7 @@ static RISCVException write_vxsat(CPURISCVState *env, int csrno, #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; #endif - env->vxsat = val; + env->vxsat = val & BIT(0); return RISCV_EXCP_NONE; } @@ -525,17 +797,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; @@ -545,32 +819,16 @@ static int write_vcsr(CPURISCVState *env, int csrno, target_ulong val) return RISCV_EXCP_NONE; } +#if defined(CONFIG_USER_ONLY) /* User Timers and Counters */ static target_ulong get_ticks(bool shift) { - int64_t val; - target_ulong result; - -#if !defined(CONFIG_USER_ONLY) - if (icount_enabled()) { - val = icount_get(); - } else { - val = cpu_get_host_ticks(); - } -#else - val = cpu_get_host_ticks(); -#endif - - if (shift) { - result = val >> 32; - } else { - result = val; - } + int64_t val = cpu_get_host_ticks(); + target_ulong result = shift ? val >> 32 : val; return result; } -#if defined(CONFIG_USER_ONLY) static RISCVException read_time(CPURISCVState *env, int csrno, target_ulong *val) { @@ -585,13 +843,15 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static int read_hpmcounter(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hpmcounter(CPURISCVState *env, int csrno, + target_ulong *val) { *val = get_ticks(false); return RISCV_EXCP_NONE; } -static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, + target_ulong *val) { *val = get_ticks(true); return RISCV_EXCP_NONE; @@ -599,7 +859,113 @@ static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) #else /* CONFIG_USER_ONLY */ -static int read_mhpmevent(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_mcyclecfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mcyclecfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcyclecfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t inh_avail_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->mcyclecfg = val; + } else { + /* Set xINH fields if priv mode supported */ + inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MCYCLECFG_BIT_MINH; + inh_avail_mask |= riscv_has_ext(env, RVU) ? MCYCLECFG_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MCYCLECFG_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MCYCLECFG_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MCYCLECFG_BIT_VSINH : 0; + env->mcyclecfg = val & inh_avail_mask; + } + + return RISCV_EXCP_NONE; +} + +static RISCVException read_mcyclecfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->mcyclecfgh; + return RISCV_EXCP_NONE; +} + +static RISCVException write_mcyclecfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MCYCLECFGH_BIT_MINH); + + /* Set xINH fields if priv mode supported */ + inh_avail_mask |= riscv_has_ext(env, RVU) ? MCYCLECFGH_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MCYCLECFGH_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MCYCLECFGH_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MCYCLECFGH_BIT_VSINH : 0; + + env->mcyclecfgh = val & inh_avail_mask; + return RISCV_EXCP_NONE; +} + +static RISCVException read_minstretcfg(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->minstretcfg; + return RISCV_EXCP_NONE; +} + +static RISCVException write_minstretcfg(CPURISCVState *env, int csrno, + target_ulong val) +{ + uint64_t inh_avail_mask; + + if (riscv_cpu_mxl(env) == MXL_RV32) { + env->minstretcfg = val; + } else { + inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MINSTRETCFG_BIT_MINH; + inh_avail_mask |= riscv_has_ext(env, RVU) ? MINSTRETCFG_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MINSTRETCFG_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MINSTRETCFG_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MINSTRETCFG_BIT_VSINH : 0; + env->minstretcfg = val & inh_avail_mask; + } + return RISCV_EXCP_NONE; +} + +static RISCVException read_minstretcfgh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + *val = env->minstretcfgh; + return RISCV_EXCP_NONE; +} + +static RISCVException write_minstretcfgh(CPURISCVState *env, int csrno, + target_ulong val) +{ + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MINSTRETCFGH_BIT_MINH); + + inh_avail_mask |= riscv_has_ext(env, RVU) ? MINSTRETCFGH_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MINSTRETCFGH_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MINSTRETCFGH_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MINSTRETCFGH_BIT_VSINH : 0; + + env->minstretcfgh = val & inh_avail_mask; + return RISCV_EXCP_NONE; +} + +static RISCVException read_mhpmevent(CPURISCVState *env, int csrno, + target_ulong *val) { int evt_index = csrno - CSR_MCOUNTINHIBIT; @@ -608,23 +974,36 @@ static int read_mhpmevent(CPURISCVState *env, int csrno, target_ulong *val) return RISCV_EXCP_NONE; } -static int write_mhpmevent(CPURISCVState *env, int csrno, target_ulong val) +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; + uint64_t inh_avail_mask; if (riscv_cpu_mxl(env) == MXL_RV32) { + env->mhpmevent_val[evt_index] = val; mhpmevt_val = mhpmevt_val | ((uint64_t)env->mhpmeventh_val[evt_index] << 32); + } else { + inh_avail_mask = ~MHPMEVENT_FILTER_MASK | MHPMEVENT_BIT_MINH; + inh_avail_mask |= riscv_has_ext(env, RVU) ? MHPMEVENT_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MHPMEVENT_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MHPMEVENT_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MHPMEVENT_BIT_VSINH : 0; + mhpmevt_val = val & inh_avail_mask; + env->mhpmevent_val[evt_index] = mhpmevt_val; } + riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); return RISCV_EXCP_NONE; } -static int read_mhpmeventh(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_mhpmeventh(CPURISCVState *env, int csrno, + target_ulong *val) { int evt_index = csrno - CSR_MHPMEVENT3H + 3; @@ -633,30 +1012,111 @@ static int read_mhpmeventh(CPURISCVState *env, int csrno, target_ulong *val) return RISCV_EXCP_NONE; } -static int write_mhpmeventh(CPURISCVState *env, int csrno, target_ulong val) +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 mhpmevth_val; uint64_t mhpmevt_val = env->mhpmevent_val[evt_index]; + target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | + MHPMEVENTH_BIT_MINH); + inh_avail_mask |= riscv_has_ext(env, RVU) ? MHPMEVENTH_BIT_UINH : 0; + inh_avail_mask |= riscv_has_ext(env, RVS) ? MHPMEVENTH_BIT_SINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVU)) ? MHPMEVENTH_BIT_VUINH : 0; + inh_avail_mask |= (riscv_has_ext(env, RVH) && + riscv_has_ext(env, RVS)) ? MHPMEVENTH_BIT_VSINH : 0; + + mhpmevth_val = val & inh_avail_mask; mhpmevt_val = mhpmevt_val | (mhpmevth_val << 32); - env->mhpmeventh_val[evt_index] = val; + env->mhpmeventh_val[evt_index] = mhpmevth_val; riscv_pmu_update_event_map(env, mhpmevt_val, evt_index); return RISCV_EXCP_NONE; } -static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) +static target_ulong riscv_pmu_ctr_get_fixed_counters_val(CPURISCVState *env, + int counter_idx, + bool upper_half) +{ + int inst = riscv_pmu_ctr_monitor_instructions(env, counter_idx); + uint64_t *counter_arr_virt = env->pmu_fixed_ctrs[inst].counter_virt; + uint64_t *counter_arr = env->pmu_fixed_ctrs[inst].counter; + target_ulong result = 0; + uint64_t curr_val = 0; + uint64_t cfg_val = 0; + + if (counter_idx == 0) { + cfg_val = upper_half ? ((uint64_t)env->mcyclecfgh << 32) : + env->mcyclecfg; + } else if (counter_idx == 2) { + cfg_val = upper_half ? ((uint64_t)env->minstretcfgh << 32) : + env->minstretcfg; + } else { + cfg_val = upper_half ? + ((uint64_t)env->mhpmeventh_val[counter_idx] << 32) : + env->mhpmevent_val[counter_idx]; + cfg_val &= MHPMEVENT_FILTER_MASK; + } + + if (!cfg_val) { + if (icount_enabled()) { + curr_val = inst ? icount_get_raw() : icount_get(); + } else { + curr_val = cpu_get_host_ticks(); + } + + goto done; + } + + /* Update counter before reading. */ + riscv_pmu_update_fixed_ctrs(env, env->priv, env->virt_enabled); + + if (!(cfg_val & MCYCLECFG_BIT_MINH)) { + curr_val += counter_arr[PRV_M]; + } + + if (!(cfg_val & MCYCLECFG_BIT_SINH)) { + curr_val += counter_arr[PRV_S]; + } + + if (!(cfg_val & MCYCLECFG_BIT_UINH)) { + curr_val += counter_arr[PRV_U]; + } + + if (!(cfg_val & MCYCLECFG_BIT_VSINH)) { + curr_val += counter_arr_virt[PRV_S]; + } + + if (!(cfg_val & MCYCLECFG_BIT_VUINH)) { + curr_val += counter_arr_virt[PRV_U]; + } + +done: + if (riscv_cpu_mxl(env) == MXL_RV32) { + result = upper_half ? curr_val >> 32 : curr_val; + } else { + result = curr_val; + } + + return result; +} + +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 (!get_field(env->mcountinhibit, BIT(ctr_idx)) && + (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) { + counter->mhpmcounter_prev = riscv_pmu_ctr_get_fixed_counters_val(env, + ctr_idx, false); if (ctr_idx > 2) { if (riscv_cpu_mxl(env) == MXL_RV32) { mhpmctr_val = mhpmctr_val | @@ -672,7 +1132,8 @@ static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) return RISCV_EXCP_NONE; } -static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, + target_ulong val) { int ctr_idx = csrno - CSR_MCYCLEH; PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; @@ -681,9 +1142,11 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong 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 (!get_field(env->mcountinhibit, BIT(ctr_idx)) && + (riscv_pmu_ctr_monitor_cycles(env, ctr_idx) || + riscv_pmu_ctr_monitor_instructions(env, ctr_idx))) { + counter->mhpmcounterh_prev = riscv_pmu_ctr_get_fixed_counters_val(env, + ctr_idx, true); if (ctr_idx > 2) { riscv_pmu_setup_timer(env, mhpmctr_val, ctr_idx); } @@ -694,37 +1157,32 @@ static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) return RISCV_EXCP_NONE; } -static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, +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; + 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. + /* + * Counter should not increment if inhibit bit is set. Just return the + * current counter value. */ - if (!counter.started) { - *val = ctr_val; - return RISCV_EXCP_NONE; - } else { - /* Mark that the counter has been stopped */ - counter.started = false; - } + *val = ctr_val; + return RISCV_EXCP_NONE; } - /** + /* * 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; + *val = riscv_pmu_ctr_get_fixed_counters_val(env, ctr_idx, upper_half) - + ctr_prev + ctr_val; } else { *val = ctr_val; } @@ -732,7 +1190,8 @@ static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, return RISCV_EXCP_NONE; } -static int read_hpmcounter(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hpmcounter(CPURISCVState *env, int csrno, + target_ulong *val) { uint16_t ctr_index; @@ -747,7 +1206,8 @@ static int read_hpmcounter(CPURISCVState *env, int csrno, target_ulong *val) return riscv_pmu_read_ctr(env, val, false, ctr_index); } -static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_hpmcounterh(CPURISCVState *env, int csrno, + target_ulong *val) { uint16_t ctr_index; @@ -762,7 +1222,8 @@ static int read_hpmcounterh(CPURISCVState *env, int csrno, target_ulong *val) return riscv_pmu_read_ctr(env, val, true, ctr_index); } -static int read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_scountovf(CPURISCVState *env, int csrno, + target_ulong *val) { int mhpmevt_start = CSR_MHPMEVENT3 - CSR_MCOUNTINHIBIT; int i; @@ -791,7 +1252,7 @@ static int read_scountovf(CPURISCVState *env, int csrno, target_ulong *val) 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; @@ -804,7 +1265,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; @@ -814,54 +1275,8 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException sstc(CPURISCVState *env, int csrno) -{ - CPUState *cs = env_cpu(env); - RISCVCPU *cpu = RISCV_CPU(cs); - bool hmode_check = false; - - if (!cpu->cfg.ext_sstc || !env->rdtime_fn) { - return RISCV_EXCP_ILLEGAL_INST; - } - - 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 (riscv_cpu_virt_enabled(env)) { - if (!(get_field(env->hcounteren, COUNTEREN_TM) & - get_field(env->henvcfg, HENVCFG_STCE))) { - return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; - } - } - - if ((csrno == CSR_VSTIMECMP) || (csrno == CSR_VSTIMECMPH)) { - hmode_check = true; - } - - return hmode_check ? hmode(env, csrno) : smode(env, csrno); -} - -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 read_vstimecmp(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { *val = env->vstimecmp; @@ -869,7 +1284,7 @@ static RISCVException read_vstimecmp(CPURISCVState *env, int csrno, } static RISCVException read_vstimecmph(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { *val = env->vstimecmp >> 32; @@ -877,29 +1292,25 @@ static RISCVException read_vstimecmph(CPURISCVState *env, int csrno, } static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { - RISCVCPU *cpu = env_archcpu(env); - 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(cpu, env->vstimer, env->vstimecmp, + 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) + target_ulong val) { - RISCVCPU *cpu = env_archcpu(env); - env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val); - riscv_timer_write_timecmp(cpu, env->vstimer, env->vstimecmp, + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, env->htimedelta, MIP_VSTIP); return RISCV_EXCP_NONE; @@ -908,7 +1319,7 @@ static RISCVException write_vstimecmph(CPURISCVState *env, int csrno, static RISCVException read_stimecmp(CPURISCVState *env, int csrno, target_ulong *val) { - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { *val = env->vstimecmp; } else { *val = env->stimecmp; @@ -918,9 +1329,9 @@ static RISCVException read_stimecmp(CPURISCVState *env, int csrno, } static RISCVException read_stimecmph(CPURISCVState *env, int csrno, - target_ulong *val) + target_ulong *val) { - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { *val = env->vstimecmp >> 32; } else { *val = env->stimecmp >> 32; @@ -930,11 +1341,12 @@ static RISCVException read_stimecmph(CPURISCVState *env, int csrno, } static RISCVException write_stimecmp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val) { - RISCVCPU *cpu = env_archcpu(env); - - if (riscv_cpu_virt_enabled(env)) { + if (env->virt_enabled) { + if (env->hvictl & HVICTL_VTI) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } return write_vstimecmp(env, csrno, val); } @@ -944,41 +1356,44 @@ static RISCVException write_stimecmp(CPURISCVState *env, int csrno, env->stimecmp = val; } - riscv_timer_write_timecmp(cpu, env->stimer, env->stimecmp, 0, MIP_STIP); + 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) + target_ulong val) { - RISCVCPU *cpu = env_archcpu(env); - - if (riscv_cpu_virt_enabled(env)) { + 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(cpu, env->stimer, env->stimecmp, 0, MIP_STIP); + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); return RISCV_EXCP_NONE; } -/* 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 | \ - MIP_LCOFIP)) -#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) -#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) - #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; +/* + * All core local interrupts except the fixed ones 0:12. This macro is for + * virtual interrupts logic so please don't change this to avoid messing up + * the whole support, For reference see AIA spec: `5.3 Interrupt filtering and + * virtual interrupts for supervisor level` and `6.3.2 Virtual interrupts for + * VS level`. + */ +#define LOCAL_INTERRUPTS (~0x1FFFULL) + +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)) | \ @@ -994,6 +1409,7 @@ static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | \ (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | \ (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)) | \ + (1ULL << (RISCV_EXCP_SW_CHECK)) | \ (1ULL << (RISCV_EXCP_INST_GUEST_PAGE_FAULT)) | \ (1ULL << (RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT)) | \ (1ULL << (RISCV_EXCP_VIRT_INSTRUCTION_FAULT)) | \ @@ -1009,22 +1425,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 | - SIP_LCOFIP; -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 uint64_t mvip_writable_mask = MIP_SSIP | MIP_STIP | MIP_SEIP | + LOCAL_INTERRUPTS; +static const uint64_t mvien_writable_mask = MIP_SSIP | MIP_SEIP | + LOCAL_INTERRUPTS; + +static const uint64_t sip_writable_mask = SIP_SSIP | LOCAL_INTERRUPTS; +static const uint64_t hip_writable_mask = MIP_VSSIP; +static const uint64_t hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | + MIP_VSEIP | LOCAL_INTERRUPTS; +static const uint64_t hvien_writable_mask = LOCAL_INTERRUPTS; + +static const uint64_t 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 */ @@ -1044,30 +1481,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; } @@ -1107,13 +1535,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, @@ -1123,40 +1600,55 @@ 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; } } + /* If cfi lp extension is available, then apply cfi lp mask */ + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= (MSTATUS_MPELP | MSTATUS_SPELP); + } + 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; } @@ -1171,11 +1663,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); @@ -1185,7 +1673,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; } @@ -1221,60 +1710,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; } @@ -1357,7 +1842,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; @@ -1394,7 +1879,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; @@ -1418,7 +1950,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; } @@ -1434,8 +1966,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; @@ -1514,10 +2047,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; @@ -1527,6 +2061,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; @@ -1534,6 +2069,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; @@ -1571,18 +2111,21 @@ static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, riscv_cpu_mxl_bits(env)), val, new_val, wr_mask); } + } else { + 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; @@ -1598,6 +2141,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: @@ -1629,7 +2175,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; @@ -1666,14 +2212,62 @@ static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, { int cidx; PMUCTRState *counter; + RISCVCPU *cpu = env_archcpu(env); + uint32_t present_ctrs = cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_IR; + target_ulong updated_ctrs = (env->mcountinhibit ^ val) & present_ctrs; + uint64_t mhpmctr_val, prev_count, curr_count; - env->mcountinhibit = val; + /* WARL register - disable unavailable counters; TM bit is always 0 */ + env->mcountinhibit = val & present_ctrs; /* Check if any other counter is also monitoring cycles/instructions */ for (cidx = 0; cidx < RV_MAX_MHPMCOUNTERS; cidx++) { + if (!(updated_ctrs & BIT(cidx)) || + (!riscv_pmu_ctr_monitor_cycles(env, cidx) && + !riscv_pmu_ctr_monitor_instructions(env, cidx))) { + continue; + } + + counter = &env->pmu_ctrs[cidx]; + if (!get_field(env->mcountinhibit, BIT(cidx))) { - counter = &env->pmu_ctrs[cidx]; - counter->started = true; + counter->mhpmcounter_prev = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, false); + if (riscv_cpu_mxl(env) == MXL_RV32) { + counter->mhpmcounterh_prev = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, true); + } + + if (cidx > 2) { + mhpmctr_val = counter->mhpmcounter_val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + mhpmctr_val = mhpmctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + } + riscv_pmu_setup_timer(env, mhpmctr_val, cidx); + } + } else { + curr_count = riscv_pmu_ctr_get_fixed_counters_val(env, cidx, false); + + mhpmctr_val = counter->mhpmcounter_val; + prev_count = counter->mhpmcounter_prev; + if (riscv_cpu_mxl(env) == MXL_RV32) { + uint64_t tmp = + riscv_pmu_ctr_get_fixed_counters_val(env, cidx, true); + + curr_count = curr_count | (tmp << 32); + mhpmctr_val = mhpmctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + prev_count = prev_count | + ((uint64_t)counter->mhpmcounterh_prev << 32); + } + + /* Adjust the counter for later reads. */ + mhpmctr_val = curr_count - prev_count + mhpmctr_val; + counter->mhpmcounter_val = mhpmctr_val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + counter->mhpmcounterh_val = mhpmctr_val >> 32; + } } } @@ -1690,7 +2284,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; } @@ -1725,28 +2323,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; @@ -1768,19 +2366,30 @@ 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); + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= MENVCFG_LPE; + } + + if (env_archcpu(env)->cfg.ext_zicfiss) { + mask |= MENVCFG_SSE; + } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); @@ -1788,16 +2397,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); @@ -1806,36 +2418,88 @@ 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; + } + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= SENVCFG_LPE; + } + + /* Higher mode SSE must be ON for next-less mode SSE to be ON */ + if (env_archcpu(env)->cfg.ext_zicfiss && + get_field(env->menvcfg, MENVCFG_SSE) && + (env->virt_enabled ? get_field(env->henvcfg, HENVCFG_SSE) : true)) { + mask |= SENVCFG_SSE; + } 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); + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= HENVCFG_LPE; + } + + /* H can light up SSE for VS only if HS had it from menvcfg */ + if (env_archcpu(env)->cfg.ext_zicfiss && + get_field(env->menvcfg, MENVCFG_SSE)) { + mask |= HENVCFG_SSE; + } } env->henvcfg = (env->henvcfg & ~mask) | (val & mask); @@ -1844,28 +2508,251 @@ 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; + } + + if (env->priv_ver >= PRIV_VERSION_1_13_0) { + wr_mask |= SMSTATEEN0_P1P13; + } + + 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; + + if (env->priv_ver >= PRIV_VERSION_1_13_0) { + wr_mask |= SMSTATEEN0_P1P13; + } + + 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; @@ -1874,14 +2761,14 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, new_val |= env->external_seip * MIP_SEIP; } - if (cpu->cfg.ext_sstc && (env->priv == PRV_M) && + 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; } @@ -1930,6 +2817,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) @@ -1940,6 +2964,10 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, mask |= SSTATUS64_UXL; } + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= SSTATUS_SPELP; + } + *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus)); return RISCV_EXCP_NONE; } @@ -1951,6 +2979,11 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, if (env->xl != MXL_RV32 || env->debugger) { mask |= SSTATUS64_UXL; } + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= SSTATUS_SPELP; + } + /* TODO: Use SXL not MXL. */ *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; @@ -1966,6 +2999,11 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno, mask |= SSTATUS64_UXL; } } + + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= SSTATUS_SPELP; + } + target_ulong newval = (env->mstatus & ~mask) | (val & mask); return write_mstatus(env, CSR_MSTATUS, newval); } @@ -1974,23 +3012,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; @@ -2031,20 +3082,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; @@ -2110,7 +3178,11 @@ static RISCVException read_scounteren(CPURISCVState *env, int csrno, static RISCVException write_scounteren(CPURISCVState *env, int csrno, target_ulong val) { - env->scounteren = val; + RISCVCPU *cpu = env_archcpu(env); + + /* WARL register - disable unavailable counters */ + env->scounteren = val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_TM | + COUNTEREN_IR); return RISCV_EXCP_NONE; } @@ -2186,12 +3258,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); @@ -2201,7 +3281,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; @@ -2248,19 +3329,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; @@ -2301,55 +3383,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; @@ -2434,15 +3488,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); } @@ -2482,7 +3538,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."); @@ -2504,6 +3561,79 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_hedelegh(CPURISCVState *env, int csrno, + target_ulong *val) +{ + RISCVException ret; + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_P1P13); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Reserved, now read zero */ + *val = 0; + return RISCV_EXCP_NONE; +} + +static RISCVException write_hedelegh(CPURISCVState *env, int csrno, + target_ulong val) +{ + RISCVException ret; + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_P1P13); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + /* Reserved, now write ignore */ + 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) @@ -2549,16 +3679,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; @@ -2633,7 +3841,11 @@ static RISCVException read_hcounteren(CPURISCVState *env, int csrno, static RISCVException write_hcounteren(CPURISCVState *env, int csrno, target_ulong val) { - env->hcounteren = val; + RISCVCPU *cpu = env_archcpu(env); + + /* WARL register - disable unavailable counters */ + env->hcounteren = val & (cpu->pmu_avail_ctrs | COUNTEREN_CY | COUNTEREN_TM | + COUNTEREN_IR); return RISCV_EXCP_NONE; } @@ -2653,7 +3865,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; } @@ -2704,7 +3916,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; } @@ -2731,6 +3943,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; } @@ -2753,29 +3971,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; } @@ -2794,18 +4020,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; @@ -2820,42 +4046,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); } @@ -2879,7 +4113,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; @@ -2888,7 +4123,12 @@ static int read_vstvec(CPURISCVState *env, int csrno, target_ulong *val) static RISCVException write_vstvec(CPURISCVState *env, int csrno, target_ulong val) { - env->vstvec = val; + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val & 3) < 2) { + env->vstvec = val; + } else { + qemu_log_mask(LOG_UNIMP, "CSR_VSTVEC: reserved mode not supported\n"); + } return RISCV_EXCP_NONE; } @@ -2958,7 +4198,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; } @@ -2999,30 +4239,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; } @@ -3031,10 +4259,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; } @@ -3101,6 +4326,31 @@ static RISCVException read_tinfo(CPURISCVState *env, int csrno, 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 @@ -3152,16 +4402,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 */ @@ -3183,9 +4433,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 */ @@ -3211,9 +4461,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)) { @@ -3238,10 +4488,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; @@ -3266,10 +4516,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; @@ -3294,10 +4547,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; @@ -3318,10 +4574,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; @@ -3346,10 +4602,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; @@ -3374,10 +4633,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; @@ -3388,10 +4650,8 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno, #endif /* Crypto Extension */ -static RISCVException rmw_seed(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, - target_ulong write_mask) +target_ulong riscv_new_csr_seed(target_ulong new_value, + target_ulong write_mask) { uint16_t random_v; Error *random_e = NULL; @@ -3415,6 +4675,18 @@ static RISCVException rmw_seed(CPURISCVState *env, int csrno, rval = random_v | SEED_OPST_ES16; } + return rval; +} + +static RISCVException rmw_seed(CPURISCVState *env, int csrno, + target_ulong *ret_value, + target_ulong new_value, + target_ulong write_mask) +{ + target_ulong rval; + + rval = riscv_new_csr_seed(new_value, write_mask); + if (ret_value) { *ret_value = rval; } @@ -3433,31 +4705,39 @@ 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) { /* 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 (!cpu->cfg.ext_icsr) { + /* ensure the CSR extension is enabled */ + if (!riscv_cpu_cfg(env)->ext_zicsr) { return RISCV_EXCP_ILLEGAL_INST; } - if (env->priv_ver < csr_min_priv) { - return RISCV_EXCP_ILLEGAL_INST; - } - - /* check predicate */ + /* ensure CSR is implemented by checking predicate */ if (!csr_ops[csrno].predicate) { return RISCV_EXCP_ILLEGAL_INST; } - if (write_mask && read_only) { + /* privileged spec version check */ + if (env->priv_ver < csr_min_priv) { return RISCV_EXCP_ILLEGAL_INST; } + /* read / write check */ + if (write && 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; @@ -3467,9 +4747,9 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, int csr_priv, effective_priv = env->priv; if (riscv_has_ext(env, RVH) && env->priv == PRV_S && - !riscv_cpu_virt_enabled(env)) { + !env->virt_enabled) { /* - * We are in HS mode. Add 1 to the effective privledge level to + * We are in HS mode. Add 1 to the effective privilege level to * allow us to access the Hypervisor CSRs. */ effective_priv++; @@ -3477,7 +4757,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, 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; @@ -3492,21 +4772,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 */ @@ -3528,13 +4814,22 @@ static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +RISCVException riscv_csrr(CPURISCVState *env, int csrno, + target_ulong *ret_value) +{ + RISCVException ret = riscv_csrrw_check(env, csrno, false); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + return riscv_csrrw_do64(env, csrno, ret_value, 0, 0); +} + 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, true); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -3582,14 +4877,45 @@ static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, + Int128 *ret_value) +{ + RISCVException ret; + + ret = riscv_csrrw_check(env, csrno, false); + if (ret != RISCV_EXCP_NONE) { + return ret; + } + + if (csr_ops[csrno].read128) { + return riscv_csrrw_do128(env, csrno, ret_value, + int128_zero(), int128_zero()); + } + + /* + * 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 + */ + target_ulong old_value; + ret = riscv_csrrw_do64(env, csrno, &old_value, + (target_ulong)0, + (target_ulong)0); + if (ret == RISCV_EXCP_NONE && ret_value) { + *ret_value = int128_make64(old_value); + } + return ret; +} + RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, Int128 *ret_value, 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, true); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -3602,7 +4928,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, @@ -3627,34 +4954,48 @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, #if !defined(CONFIG_USER_ONLY) env->debugger = true; #endif - ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); + if (!write_mask) { + ret = riscv_csrr(env, csrno, ret_value); + } else { + ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); + } #if !defined(CONFIG_USER_ONLY) env->debugger = false; #endif 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_hpmcounter }, [CSR_INSTRET] = { "instret", ctr, read_hpmcounter }, @@ -3671,6 +5012,12 @@ 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}, + + /* zicfiss Extension, shadow stack register */ + [CSR_SSP] = { "ssp", cfi_ss, read_ssp, write_ssp }, + #if !defined(CONFIG_USER_ONLY) /* Machine Timers and Counters */ [CSR_MCYCLE] = { "mcycle", any, read_hpmcounter, @@ -3704,6 +5051,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush }, + [CSR_MEDELEGH] = { "medelegh", any32, read_zero, write_ignore, + .min_priv_ver = PRIV_VERSION_1_13_0 }, + [CSR_HEDELEGH] = { "hedelegh", hmode32, read_hedelegh, write_hedelegh, + .min_priv_ver = PRIV_VERSION_1_13_0 }, /* Machine Trap Handling */ [CSR_MSCRATCH] = { "mscratch", any, read_mscratch, write_mscratch, @@ -3722,14 +5073,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [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 */ @@ -3744,13 +5095,72 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HENVCFGH] = { "henvcfgh", hmode32, read_henvcfgh, write_henvcfgh, .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 }, + 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 }, + write_scounteren }, /* Supervisor Trap Handling */ [CSR_SSCRATCH] = { "sscratch", smode, read_sscratch, write_sscratch, @@ -3771,7 +5181,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { .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 }, @@ -3808,7 +5218,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { .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, + [CSR_HGATP] = { "hgatp", hgatp, read_hgatp, write_hgatp, .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta, @@ -3844,14 +5254,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { .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_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) */ @@ -3866,8 +5275,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* 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_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 }, @@ -3877,7 +5285,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, /* Physical Memory Protection */ - [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg, + [CSR_MSECCFG] = { "mseccfg", 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 }, @@ -3901,11 +5309,12 @@ 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_TINFO] = { "tinfo", debug, read_tinfo, write_ignore }, + [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 }, @@ -4020,6 +5429,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { write_mcountinhibit, .min_priv_ver = PRIV_VERSION_1_11_0 }, + [CSR_MCYCLECFG] = { "mcyclecfg", smcntrpmf, read_mcyclecfg, + write_mcyclecfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MINSTRETCFG] = { "minstretcfg", smcntrpmf, read_minstretcfg, + write_minstretcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MHPMEVENT3] = { "mhpmevent3", any, read_mhpmevent, write_mhpmevent }, [CSR_MHPMEVENT4] = { "mhpmevent4", any, read_mhpmevent, @@ -4079,91 +5495,98 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MHPMEVENT31] = { "mhpmevent31", any, read_mhpmevent, write_mhpmevent }, - [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf, read_mhpmeventh, + [CSR_MCYCLECFGH] = { "mcyclecfgh", smcntrpmf_32, read_mcyclecfgh, + write_mcyclecfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_MINSTRETCFGH] = { "minstretcfgh", smcntrpmf_32, read_minstretcfgh, + write_minstretcfgh, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + + [CSR_MHPMEVENT3H] = { "mhpmevent3h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT4H] = { "mhpmevent4h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT5H] = { "mhpmevent5h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT6H] = { "mhpmevent6h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT7H] = { "mhpmevent7h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT8H] = { "mhpmevent8h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT9H] = { "mhpmevent9h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT10H] = { "mhpmevent10h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT11H] = { "mhpmevent11h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT12H] = { "mhpmevent12h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT13H] = { "mhpmevent13h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT14H] = { "mhpmevent14h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT15H] = { "mhpmevent15h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT16H] = { "mhpmevent16h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT17H] = { "mhpmevent17h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT18H] = { "mhpmevent18h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT19H] = { "mhpmevent19h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT20H] = { "mhpmevent20h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT21H] = { "mhpmevent21h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT22H] = { "mhpmevent22h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT23H] = { "mhpmevent23h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT24H] = { "mhpmevent24h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT25H] = { "mhpmevent25h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT26H] = { "mhpmevent26h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT27H] = { "mhpmevent27h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT28H] = { "mhpmevent28h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT29H] = { "mhpmevent29h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT30H] = { "mhpmevent30h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, - [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf, read_mhpmeventh, + [CSR_MHPMEVENT31H] = { "mhpmevent31h", sscofpmf_32, read_mhpmeventh, write_mhpmeventh, .min_priv_ver = PRIV_VERSION_1_12_0 }, diff --git a/target/riscv/debug.c b/target/riscv/debug.c index 26ea764407..c79b51af30 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: @@ -215,6 +217,66 @@ static inline void warn_always_zero_bit(target_ulong val, target_ulong mask, } } +static target_ulong textra_validate(CPURISCVState *env, target_ulong tdata3) +{ + target_ulong mhvalue, mhselect; + target_ulong mhselect_new; + target_ulong textra; + const uint32_t mhselect_no_rvh[8] = { 0, 0, 0, 0, 4, 4, 4, 4 }; + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + mhvalue = get_field(tdata3, TEXTRA32_MHVALUE); + mhselect = get_field(tdata3, TEXTRA32_MHSELECT); + /* Validate unimplemented (always zero) bits */ + warn_always_zero_bit(tdata3, (target_ulong)TEXTRA32_SBYTEMASK, + "sbytemask"); + warn_always_zero_bit(tdata3, (target_ulong)TEXTRA32_SVALUE, + "svalue"); + warn_always_zero_bit(tdata3, (target_ulong)TEXTRA32_SSELECT, + "sselect"); + break; + case MXL_RV64: + case MXL_RV128: + mhvalue = get_field(tdata3, TEXTRA64_MHVALUE); + mhselect = get_field(tdata3, TEXTRA64_MHSELECT); + /* Validate unimplemented (always zero) bits */ + warn_always_zero_bit(tdata3, (target_ulong)TEXTRA64_SBYTEMASK, + "sbytemask"); + warn_always_zero_bit(tdata3, (target_ulong)TEXTRA64_SVALUE, + "svalue"); + warn_always_zero_bit(tdata3, (target_ulong)TEXTRA64_SSELECT, + "sselect"); + break; + default: + g_assert_not_reached(); + } + + /* Validate mhselect. */ + mhselect_new = mhselect_no_rvh[mhselect]; + if (mhselect != mhselect_new) { + qemu_log_mask(LOG_UNIMP, "mhselect only supports 0 or 4 for now\n"); + } + + /* Write legal values into textra */ + textra = 0; + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + textra = set_field(textra, TEXTRA32_MHVALUE, mhvalue); + textra = set_field(textra, TEXTRA32_MHSELECT, mhselect_new); + break; + case MXL_RV64: + case MXL_RV128: + textra = set_field(textra, TEXTRA64_MHVALUE, mhvalue); + textra = set_field(textra, TEXTRA64_MHSELECT, mhselect_new); + break; + default: + g_assert_not_reached(); + } + + return textra; +} + static void do_trigger_action(CPURISCVState *env, target_ulong trigger_index) { trigger_action_t action = get_trigger_action(env, trigger_index); @@ -239,19 +301,130 @@ static void do_trigger_action(CPURISCVState *env, target_ulong trigger_index) } } +/* + * Check the privilege level of specific trigger matches CPU's current privilege + * level. + */ +static bool trigger_priv_match(CPURISCVState *env, trigger_type_t type, + int trigger_index) +{ + target_ulong ctrl = env->tdata1[trigger_index]; + + switch (type) { + case TRIGGER_TYPE_AD_MATCH: + /* type 2 trigger cannot be fired in VU/VS mode */ + if (env->virt_enabled) { + return false; + } + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 3) & BIT(env->priv)) { + return true; + } + break; + case TRIGGER_TYPE_AD_MATCH6: + 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; + case TRIGGER_TYPE_INST_CNT: + if (env->virt_enabled) { + /* check VU/VS bit against current privilege level */ + if ((ctrl >> 25) & BIT(env->priv)) { + return true; + } + } else { + /* check U/S/M bit against current privilege level */ + if ((ctrl >> 6) & BIT(env->priv)) { + return true; + } + } + 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", type); + break; + case TRIGGER_TYPE_NO_EXIST: + case TRIGGER_TYPE_UNAVAIL: + qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exist\n", + type); + break; + default: + g_assert_not_reached(); + } + + return false; +} + +static bool trigger_textra_match(CPURISCVState *env, trigger_type_t type, + int trigger_index) +{ + target_ulong textra = env->tdata3[trigger_index]; + target_ulong mhvalue, mhselect; + + if (type < TRIGGER_TYPE_AD_MATCH || type > TRIGGER_TYPE_AD_MATCH6) { + /* textra checking is only applicable when type is 2, 3, 4, 5, or 6 */ + return true; + } + + switch (riscv_cpu_mxl(env)) { + case MXL_RV32: + mhvalue = get_field(textra, TEXTRA32_MHVALUE); + mhselect = get_field(textra, TEXTRA32_MHSELECT); + break; + case MXL_RV64: + case MXL_RV128: + mhvalue = get_field(textra, TEXTRA64_MHVALUE); + mhselect = get_field(textra, TEXTRA64_MHSELECT); + break; + default: + g_assert_not_reached(); + } + + /* Check mhvalue and mhselect. */ + switch (mhselect) { + case MHSELECT_IGNORE: + break; + case MHSELECT_MCONTEXT: + /* Match if the low bits of mcontext/hcontext equal mhvalue. */ + if (mhvalue != env->mcontext) { + return false; + } + break; + default: + break; + } + + return true; +} + +/* Common matching conditions for all types of the triggers. */ +static bool trigger_common_match(CPURISCVState *env, trigger_type_t type, + int trigger_index) +{ + return trigger_priv_match(env, type, trigger_index) && + trigger_textra_match(env, type, trigger_index); +} + /* 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) @@ -282,8 +455,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) { @@ -371,8 +544,7 @@ static void type2_reg_write(CPURISCVState *env, target_ulong index, } break; case TDATA3: - qemu_log_mask(LOG_UNIMP, - "tdata3 is not supported for type 2 trigger\n"); + env->tdata3[index] = textra_validate(env, val); break; default: g_assert_not_reached(); @@ -411,8 +583,8 @@ static target_ulong type6_mcontrol6_validate(CPURISCVState *env, /* 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); + qemu_log_mask(LOG_UNIMP, "access size %d is not supported, using " + "SIZE_ANY\n", size); } else { val |= (ctrl & TYPE6_SIZE); } @@ -488,8 +660,7 @@ static void type6_reg_write(CPURISCVState *env, target_ulong index, } break; case TDATA3: - qemu_log_mask(LOG_UNIMP, - "tdata3 is not supported for type 6 trigger\n"); + env->tdata3[index] = textra_validate(env, val); break; default: g_assert_not_reached(); @@ -498,10 +669,209 @@ static void type6_reg_write(CPURISCVState *env, target_ulong index, return; } -target_ulong tdata_csr_read(CPURISCVState *env, int tdata_index) +/* 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 (!trigger_common_match(env, TRIGGER_TYPE_INST_CNT, 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: + env->tdata3[index] = textra_validate(env, val); + 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) +{ + 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]; @@ -530,6 +900,8 @@ void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val) 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: @@ -560,7 +932,6 @@ void riscv_cpu_debug_excp_handler(CPUState *cs) if (cs->watchpoint_hit) { if (cs->watchpoint_hit->flags & BP_CPU) { - cs->watchpoint_hit = NULL; do_trigger_action(env, DBG_ACTION_BP); } } else { @@ -584,21 +955,18 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) for (i = 0; i < RV_MAX_TRIGGERS; i++) { trigger_type = get_trigger_type(env, i); + if (!trigger_common_match(env, trigger_type, i)) { + continue; + } + switch (trigger_type) { case TRIGGER_TYPE_AD_MATCH: - /* type 2 trigger cannot be fired in VU/VS mode */ - if (riscv_cpu_virt_enabled(env)) { - 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; - } + env->badaddr = pc; + return true; } break; case TRIGGER_TYPE_AD_MATCH6: @@ -606,17 +974,8 @@ bool riscv_cpu_debug_check_breakpoint(CPUState *cs) pc = env->tdata2[i]; if ((ctrl & TYPE6_EXEC) && (bp->pc == pc)) { - if (riscv_cpu_virt_enabled(env)) { - /* 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; - } - } + env->badaddr = pc; + return true; } break; default: @@ -642,13 +1001,12 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) for (i = 0; i < RV_MAX_TRIGGERS; i++) { trigger_type = get_trigger_type(env, i); + if (!trigger_common_match(env, trigger_type, i)) { + continue; + } + switch (trigger_type) { case TRIGGER_TYPE_AD_MATCH: - /* type 2 trigger cannot be fired in VU/VS mode */ - if (riscv_cpu_virt_enabled(env)) { - return false; - } - ctrl = env->tdata1[i]; addr = env->tdata2[i]; flags = 0; @@ -661,10 +1019,7 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) } if ((wp->flags & flags) && (wp->vaddr == addr)) { - /* check U/S/M bit against current privilege level */ - if ((ctrl >> 3) & BIT(env->priv)) { - return true; - } + return true; } break; case TRIGGER_TYPE_AD_MATCH6: @@ -680,17 +1035,7 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) } if ((wp->flags & flags) && (wp->vaddr == addr)) { - if (riscv_cpu_virt_enabled(env)) { - /* 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; - } - } + return true; } break; default: @@ -702,7 +1047,17 @@ bool riscv_cpu_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) return false; } -void riscv_trigger_init(CPURISCVState *env) +void riscv_trigger_realize(CPURISCVState *env) +{ + int 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; @@ -727,5 +1082,8 @@ void riscv_trigger_init(CPURISCVState *env) 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 a1226b4d29..f76b8f944a 100644 --- a/target/riscv/debug.h +++ b/target/riscv/debug.h @@ -22,6 +22,8 @@ #ifndef RISCV_DEBUG_H #define RISCV_DEBUG_H +#include "exec/breakpoint.h" + #define RV_MAX_TRIGGERS 2 /* register index of tdata CSRs */ @@ -118,6 +120,20 @@ 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) + +#define MHSELECT_IGNORE 0 +#define MHSELECT_MCONTEXT 4 + bool tdata_available(CPURISCVState *env, int tdata_index); target_ulong tselect_csr_read(CPURISCVState *env); @@ -132,6 +148,9 @@ 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..91b1a56d10 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_h(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 6e7bbdbd5e..c07df972f1 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]); @@ -118,8 +124,11 @@ static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n) 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); @@ -127,43 +136,11 @@ static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) 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; @@ -174,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 == RISCV_EXCP_NONE) { - 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) { @@ -201,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 == RISCV_EXCP_NONE) { - 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; @@ -231,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; @@ -245,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); @@ -271,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 bitsize = cpu->cfg.vlenb << 3; + 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); + int count = bitsize / vec_lanes[i].size; + 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), + bitsize, 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, - 32, "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, - 32, "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); + if (cpu->cfg.ext_zve32x) { + 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 a03014fe67..451261ce5a 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) @@ -108,15 +132,27 @@ DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl) DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) DEF_HELPER_1(wfi, void, env) +DEF_HELPER_1(wrs_nto, 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 */ @@ -1134,3 +1170,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 ccfe59f294..bf893d1c2e 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 @@ -43,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 @@ -53,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 @@ -89,6 +100,15 @@ @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. @@ -97,29 +117,34 @@ } { 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) *** addi 000 . ..... ..... 01 @ci addi 010 . ..... ..... 01 @c_li { + # c.sspush x1 carving out of zcmops + sspush 011 0 00001 00000 01 &r2_s rs2=1 rs1=0 + # c.sspopchk x5 carving out of zcmops + sspopchk 011 0 00101 00000 01 &r2 rs1=5 rd=0 + c_mop_n 011 0 0 n:3 1 00000 01 illegal 011 0 ----- 00000 01 # c.addi16sp and c.lui, RES nzimm=0 addi 011 . 00010 ..... 01 @c_addi16sp lui 011 . ..... ..... 01 @c_lui @@ -148,7 +173,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 @@ -166,7 +191,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 @@ -174,9 +211,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 d0253b8104..e9139ec1b9 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -37,6 +37,9 @@ %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 +%imm_mop5 30:1 26:2 20:2 +%imm_mop3 30:1 26:2 # Argument sets: &empty @@ -55,6 +58,8 @@ &r2nfvm vm rd rs1 nf &rnfvm vm rd rs1 rs2 nf &k_aes shamt rs2 rs1 rd +&mop5 imm rd rs1 +&mop3 imm rd rs1 rs2 # Formats 32: @r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd @@ -74,6 +79,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 +88,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 @@ -95,6 +102,9 @@ @k_aes .. ..... ..... ..... ... ..... ....... &k_aes shamt=%imm_bs %rs2 %rs1 %rd @i_aes .. ..... ..... ..... ... ..... ....... &i imm=%imm_rnum %rs1 %rd +@mop5 . . .. .. .... .. ..... ... ..... ....... &mop5 imm=%imm_mop5 %rd %rs1 +@mop3 . . .. .. . ..... ..... ... ..... ....... &mop3 imm=%imm_mop3 %rd %rs1 %rs2 + # Formats 64: @sh5 ....... ..... ..... ... ..... ....... &shift shamt=%sh5 %rs1 %rd @@ -113,7 +123,10 @@ sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm # *** RV32I Base Instruction Set *** lui .................... ..... 0110111 @u -auipc .................... ..... 0010111 @u +{ + lpad label:20 00000 0010111 + auipc .................... ..... 0010111 @u +} jal .................... ..... 1101111 @j jalr ............ ..... 000 ..... 1100111 @i beq ....... ..... ..... 000 ..... 1100011 @b @@ -134,6 +147,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 @@ -179,7 +193,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 @@ -219,6 +246,7 @@ remud 0000001 ..... ..... 111 ..... 1111011 @r lr_w 00010 . . 00000 ..... 010 ..... 0101111 @atom_ld sc_w 00011 . . ..... ..... 010 ..... 0101111 @atom_st amoswap_w 00001 . . ..... ..... 010 ..... 0101111 @atom_st +ssamoswap_w 01001 . . ..... ..... 010 ..... 0101111 @atom_st amoadd_w 00000 . . ..... ..... 010 ..... 0101111 @atom_st amoxor_w 00100 . . ..... ..... 010 ..... 0101111 @atom_st amoand_w 01100 . . ..... ..... 010 ..... 0101111 @atom_st @@ -232,6 +260,7 @@ amomaxu_w 11100 . . ..... ..... 010 ..... 0101111 @atom_st lr_d 00010 . . 00000 ..... 011 ..... 0101111 @atom_ld sc_d 00011 . . ..... ..... 011 ..... 0101111 @atom_st amoswap_d 00001 . . ..... ..... 011 ..... 0101111 @atom_st +ssamoswap_d 01001 . . ..... ..... 011 ..... 0101111 @atom_st amoadd_d 00000 . . ..... ..... 011 ..... 0101111 @atom_st amoxor_d 00100 . . ..... ..... 011 ..... 0101111 @atom_st amoand_d 01100 . . ..... ..... 011 ..... 0101111 @atom_st @@ -718,6 +747,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 @@ -803,6 +836,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 @@ -886,3 +945,121 @@ 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 + +# *** Zimop may-be-operation extension *** +{ + # zicfiss instructions carved out of mop.r + [ + ssrdp 1100110 11100 00000 100 rd:5 1110011 + sspopchk 1100110 11100 00001 100 00000 1110011 &r2 rs1=1 rd=0 + sspopchk 1100110 11100 00101 100 00000 1110011 &r2 rs1=5 rd=0 + ] + mop_r_n 1 . 00 .. 0111 .. ..... 100 ..... 1110011 @mop5 +} +{ + # zicfiss instruction carved out of mop.rr + [ + sspush 1100111 00001 00000 100 00000 1110011 &r2_s rs2=1 rs1=0 + sspush 1100111 00101 00000 100 00000 1110011 &r2_s rs2=5 rs1=0 + ] + mop_rr_n 1 . 00 .. 1 ..... ..... 100 ..... 1110011 @mop3 +} + +# *** Zabhb Standard Extension *** +amoswap_b 00001 . . ..... ..... 000 ..... 0101111 @atom_st +amoadd_b 00000 . . ..... ..... 000 ..... 0101111 @atom_st +amoxor_b 00100 . . ..... ..... 000 ..... 0101111 @atom_st +amoand_b 01100 . . ..... ..... 000 ..... 0101111 @atom_st +amoor_b 01000 . . ..... ..... 000 ..... 0101111 @atom_st +amomin_b 10000 . . ..... ..... 000 ..... 0101111 @atom_st +amomax_b 10100 . . ..... ..... 000 ..... 0101111 @atom_st +amominu_b 11000 . . ..... ..... 000 ..... 0101111 @atom_st +amomaxu_b 11100 . . ..... ..... 000 ..... 0101111 @atom_st +amoswap_h 00001 . . ..... ..... 001 ..... 0101111 @atom_st +amoadd_h 00000 . . ..... ..... 001 ..... 0101111 @atom_st +amoxor_h 00100 . . ..... ..... 001 ..... 0101111 @atom_st +amoand_h 01100 . . ..... ..... 001 ..... 0101111 @atom_st +amoor_h 01000 . . ..... ..... 001 ..... 0101111 @atom_st +amomin_h 10000 . . ..... ..... 001 ..... 0101111 @atom_st +amomax_h 10100 . . ..... ..... 001 ..... 0101111 @atom_st +amominu_h 11000 . . ..... ..... 001 ..... 0101111 @atom_st +amomaxu_h 11100 . . ..... ..... 001 ..... 0101111 @atom_st +amocas_b 00101 . . ..... ..... 000 ..... 0101111 @atom_st +amocas_h 00101 . . ..... ..... 001 ..... 0101111 @atom_st diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc index 74e2894462..ecd3b8b2c9 100644 --- a/target/riscv/insn_trans/trans_privileged.c.inc +++ b/target/riscv/insn_trans/trans_privileged.c.inc @@ -52,7 +52,7 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) * that no exception will be raised when fetching them. */ - if (semihosting_enabled(ctx->mem_idx < PRV_S) && + 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); @@ -62,6 +62,8 @@ static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) { generate_exception(ctx, RISCV_EXCP_SEMIHOST); } else { + tcg_gen_st_tl(tcg_constant_tl(ebreak_addr), tcg_env, + offsetof(CPURISCVState, badaddr)); generate_exception(ctx, RISCV_EXCP_BREAKPOINT); } return true; @@ -76,12 +78,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) { #ifndef CONFIG_USER_ONLY if (has_ext(ctx, RVS)) { - decode_save_opc(ctx); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_sret(cpu_pc, cpu_env); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ + decode_save_opc(ctx, 0); + 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; @@ -95,12 +95,10 @@ static bool trans_sret(DisasContext *ctx, arg_sret *a) static bool trans_mret(DisasContext *ctx, arg_mret *a) { #ifndef CONFIG_USER_ONLY - decode_save_opc(ctx); - if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_mret(cpu_pc, cpu_env); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ + decode_save_opc(ctx, 0); + 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 @@ -111,9 +109,9 @@ static bool trans_mret(DisasContext *ctx, arg_mret *a) static bool trans_wfi(DisasContext *ctx, arg_wfi *a) { #ifndef CONFIG_USER_ONLY - decode_save_opc(ctx); - gen_set_pc_imm(ctx, ctx->pc_succ_insn); - gen_helper_wfi(cpu_env); + decode_save_opc(ctx, 0); + gen_update_pc(ctx, ctx->cur_insn_len); + gen_helper_wfi(tcg_env); return true; #else return false; @@ -123,8 +121,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 - decode_save_opc(ctx); - gen_helper_tlb_flush(cpu_env); + decode_save_opc(ctx, 0); + 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..9cf3ae8019 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, 0); + 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, 0); 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); @@ -76,148 +96,145 @@ static bool gen_sc(DisasContext *ctx, arg_atomic *a, MemOp mop) return true; } -static bool gen_amo(DisasContext *ctx, arg_atomic *a, - void(*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), - 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); - - func(dest, src1, src2, ctx->mem_idx, mop); - - gen_set_gpr(ctx, a->rd, dest); - return true; -} - 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); - return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_TESL); } static bool trans_amoadd_w(DisasContext *ctx, arg_amoadd_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_TESL); } static bool trans_amoxor_w(DisasContext *ctx, arg_amoxor_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_TESL); } static bool trans_amoand_w(DisasContext *ctx, arg_amoand_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_TESL); } static bool trans_amoor_w(DisasContext *ctx, arg_amoor_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_TESL); } static bool trans_amomin_w(DisasContext *ctx, arg_amomin_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_TESL); } static bool trans_amomax_w(DisasContext *ctx, arg_amomax_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_TESL); } static bool trans_amominu_w(DisasContext *ctx, arg_amominu_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_TESL); } static bool trans_amomaxu_w(DisasContext *ctx, arg_amomaxu_w *a) { - REQUIRE_EXT(ctx, RVA); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TESL)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, 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); - return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_TEUQ); } static bool trans_amoadd_d(DisasContext *ctx, arg_amoadd_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_TEUQ); } static bool trans_amoxor_d(DisasContext *ctx, arg_amoxor_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_TEUQ); } static bool trans_amoand_d(DisasContext *ctx, arg_amoand_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_TEUQ); } static bool trans_amoor_d(DisasContext *ctx, arg_amoor_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_TEUQ); } static bool trans_amomin_d(DisasContext *ctx, arg_amomin_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_TEUQ); } static bool trans_amomax_d(DisasContext *ctx, arg_amomax_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_TEUQ); } static bool trans_amominu_d(DisasContext *ctx, arg_amominu_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_TEUQ); } static bool trans_amomaxu_d(DisasContext *ctx, arg_amomaxu_d *a) { REQUIRE_64BIT(ctx); - return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TEUQ)); + REQUIRE_A_OR_ZAAMO(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, 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..30883ea37c 100644 --- a/target/riscv/insn_trans/trans_rvd.c.inc +++ b/target/riscv/insn_trans/trans_rvd.c.inc @@ -31,15 +31,39 @@ } \ } 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; + MemOp memop = MO_TEUQ; REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); + /* + * FLD and FSD are only guaranteed to execute atomically if the effective + * address is naturally aligned and XLEN≥64. Also, zama16b applies to + * loads and stores of no more than MXLEN bits defined in the F, D, and + * Q extensions. + */ + if (get_xl_max(ctx) == MXL_RV32) { + memop |= MO_ATOM_NONE; + } else if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; + } else { + memop |= MO_ATOM_IFALIGN; + } + + decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); - tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, memop); mark_fs_dirty(ctx); return true; @@ -48,15 +72,37 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) static bool trans_fsd(DisasContext *ctx, arg_fsd *a) { TCGv addr; + MemOp memop = MO_TEUQ; REQUIRE_FPU; REQUIRE_EXT(ctx, RVD); + if (get_xl_max(ctx) == MXL_RV32) { + memop |= MO_ATOM_NONE; + } else if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; + } else { + memop |= MO_ATOM_IFALIGN; + } + + decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); - tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, memop); 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 +115,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 +133,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 +151,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 +169,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 +186,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 +203,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 +220,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 +237,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 +253,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 +294,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 +316,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 +332,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 +348,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 +364,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 +380,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 +396,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 +411,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 +426,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 +455,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 +470,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 +485,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 +502,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 +520,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 +536,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 +566,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 +584,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..ed73afe089 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,17 +31,31 @@ } \ } 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; TCGv addr; + MemOp memop = MO_TEUL; REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); + if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; + } + + decode_save_opc(ctx, 0); 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); + tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, memop); gen_nanbox_s(dest, dest); mark_fs_dirty(ctx); @@ -50,15 +65,33 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) static bool trans_fsw(DisasContext *ctx, arg_fsw *a) { TCGv addr; + MemOp memop = MO_TEUL; REQUIRE_FPU; REQUIRE_EXT(ctx, RVF); + if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; + } + + decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); - tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL); + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, memop); 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 +103,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 +120,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 +137,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 +154,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 +170,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 +186,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 +202,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 +218,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 +233,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 +264,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 +309,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 +354,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 +373,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 +388,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 +403,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 +417,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 +449,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 +463,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 +477,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 +490,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 +504,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 +519,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 +551,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 +566,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 +581,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 +597,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 4f8aecddc7..03c6694430 100644 --- a/target/riscv/insn_trans/trans_rvh.c.inc +++ b/target/riscv/insn_trans/trans_rvh.c.inc @@ -16,161 +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, 0); + 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, 0); + 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 - decode_save_opc(ctx); - gen_helper_hyp_gvma_tlb_flush(cpu_env); + decode_save_opc(ctx, 0); + gen_helper_hyp_gvma_tlb_flush(tcg_env); return true; #endif return false; @@ -180,8 +158,8 @@ static bool trans_hfence_vvma(DisasContext *ctx, arg_sfence_vma *a) { REQUIRE_EXT(ctx, RVH); #ifndef CONFIG_USER_ONLY - decode_save_opc(ctx); - gen_helper_hyp_tlb_flush(cpu_env); + decode_save_opc(ctx, 0); + 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 c49dbec0eb..96c218a9d7 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -36,9 +36,54 @@ static bool trans_lui(DisasContext *ctx, arg_lui *a) return true; } +static bool trans_lpad(DisasContext *ctx, arg_lpad *a) +{ + /* + * fcfi_lp_expected can set only if fcfi was eanbled. + * translate further only if fcfi_lp_expected set. + * lpad comes from NOP space anyways, so return true if + * fcfi_lp_expected is false. + */ + if (!ctx->fcfi_lp_expected) { + return true; + } + + ctx->fcfi_lp_expected = false; + if ((ctx->base.pc_next) & 0x3) { + /* + * misaligned, according to spec we should raise sw check exception + */ + tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); + gen_helper_raise_exception(tcg_env, + tcg_constant_i32(RISCV_EXCP_SW_CHECK)); + return true; + } + + /* per spec, label check performed only when embedded label non-zero */ + if (a->label != 0) { + TCGLabel *skip = gen_new_label(); + TCGv tmp = tcg_temp_new(); + tcg_gen_extract_tl(tmp, get_gpr(ctx, xT2, EXT_NONE), 12, 20); + tcg_gen_brcondi_tl(TCG_COND_EQ, tmp, a->label, skip); + tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); + gen_helper_raise_exception(tcg_env, + tcg_constant_i32(RISCV_EXCP_SW_CHECK)); + gen_set_label(skip); + } + + tcg_gen_st8_tl(tcg_constant_tl(0), tcg_env, + offsetof(CPURISCVState, elp)); + + return true; +} + static bool trans_auipc(DisasContext *ctx, arg_auipc *a) { - 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; } @@ -51,26 +96,45 @@ 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); + if (ctx->fcfi_enabled) { + /* + * return from functions (i.e. rs1 == xRA || rs1 == xT0) are not + * tracked. zicfilp introduces sw guarded branch as well. sw guarded + * branch are not tracked. rs1 == xT2 is a sw guarded branch. + */ + if (a->rs1 != xRA && a->rs1 != xT0 && a->rs1 != xT2) { + tcg_gen_st8_tl(tcg_constant_tl(1), + tcg_env, offsetof(CPURISCVState, elp)); + } + } + + 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; @@ -108,8 +172,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; @@ -128,8 +190,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; @@ -140,8 +200,6 @@ static TCGCond gen_compare_i128(bool bz, TCGv rl, if (invert) { cond = tcg_invert_cond(cond); } - - tcg_temp_free(rh); return cond; } @@ -160,6 +218,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); @@ -169,21 +228,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; @@ -254,18 +316,28 @@ 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) { - if (get_xl(ctx) == MXL_RV128) { - return gen_load_i128(ctx, a, memop); - } else { - return gen_load_tl(ctx, a, memop); + bool out; + + if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; } + decode_save_opc(ctx, 0); + if (get_xl(ctx) == MXL_RV128) { + out = gen_load_i128(ctx, a, memop); + } else { + 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) @@ -322,6 +394,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; } @@ -343,13 +419,15 @@ 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) { + if (ctx->cfg_ptr->ext_zama16b) { + memop |= MO_ATOM_WITHIN16; + } + decode_save_opc(ctx, 0); if (get_xl(ctx) == MXL_RV128) { return gen_store_i128(ctx, a, memop); } else { @@ -566,14 +644,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) @@ -616,14 +686,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) @@ -657,14 +719,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) @@ -802,8 +856,8 @@ static bool trans_pause(DisasContext *ctx, arg_pause *a) * PAUSE is a no-op in QEMU, * end the TB and return 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; @@ -818,7 +872,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; } @@ -826,8 +880,8 @@ 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; } @@ -835,10 +889,10 @@ static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) static bool do_csr_post(DisasContext *ctx) { /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ - decode_save_opc(ctx); + decode_save_opc(ctx, 0); /* 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; } @@ -848,10 +902,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); } @@ -860,10 +912,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); } @@ -872,10 +922,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); } @@ -886,11 +934,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); } @@ -899,10 +945,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); } @@ -913,11 +957,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 ec7f705aab..795f0ccf14 100644 --- a/target/riscv/insn_trans/trans_rvm.c.inc +++ b/target/riscv/insn_trans/trans_rvm.c.inc @@ -45,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, @@ -63,10 +60,6 @@ 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) @@ -92,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) @@ -104,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) @@ -132,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) @@ -147,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) @@ -160,8 +141,6 @@ 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); } @@ -177,7 +156,6 @@ 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) @@ -191,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) @@ -223,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) @@ -237,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) @@ -258,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) @@ -272,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) @@ -306,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) @@ -320,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) @@ -342,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) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 4dea4413ae..b9883a5d32 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -29,21 +29,38 @@ 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; + } +} + +static bool require_rvfmin(DisasContext *s) +{ + if (s->mstatus_fs == EXT_STATUS_DISABLED) { + return false; + } + + switch (s->sew) { + case MO_16: + return s->cfg_ptr->ext_zvfhmin; + case MO_32: + return s->cfg_ptr->ext_zve32f; default: return false; } @@ -51,63 +68,36 @@ 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_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 +163,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_zve32x) { return false; } @@ -191,18 +179,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 +193,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_zve32x) { 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 +223,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 +252,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 +296,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 +329,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 +359,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 +379,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 +410,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 +474,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 +499,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 +521,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 +549,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 +561,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,10 +630,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); @@ -662,25 +638,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; } @@ -779,6 +770,7 @@ static bool ld_us_mask_op(DisasContext *s, arg_vlm_v *a, uint8_t eew) /* 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); + data = FIELD_DP32(data, VDATA, VM, 1); return ldst_us_trans(a->rd, a->rs1, data, fn, s, false); } @@ -796,6 +788,7 @@ static bool st_us_mask_op(DisasContext *s, arg_vsm_v *a, uint8_t eew) /* EMUL = 1, NFIELDS = 1 */ data = FIELD_DP32(data, VDATA, LMUL, 0); data = FIELD_DP32(data, VDATA, NF, 1); + data = FIELD_DP32(data, VDATA, VM, 1); return ldst_us_trans(a->rd, a->rs1, data, fn, s, true); } @@ -816,36 +809,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; } @@ -869,7 +853,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) 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_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); + 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) @@ -903,7 +887,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) @@ -926,38 +910,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; } @@ -1000,7 +974,7 @@ static bool ld_index_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) 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_index_trans(a->rd, a->rs1, a->rs2, data, fn, s, false); + 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) @@ -1052,7 +1026,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) @@ -1077,25 +1051,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; } @@ -1133,34 +1100,27 @@ 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); + data = FIELD_DP32(data, VDATA, VM, 1); 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; } @@ -1168,42 +1128,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 @@ -1212,12 +1172,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) @@ -1234,14 +1194,6 @@ 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); - 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), @@ -1255,11 +1207,10 @@ do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn, 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; } @@ -1271,6 +1222,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]); \ } @@ -1288,10 +1242,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); @@ -1302,20 +1252,16 @@ static bool opivx_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t vm, 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->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)); - 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; } @@ -1333,10 +1279,6 @@ 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 && !(s->vta && s->lmul < 0)) { TCGv_i64 src1 = tcg_temp_new_i64(); @@ -1344,8 +1286,7 @@ do_opivx_gvec(DisasContext *s, arg_rmrr *a, GVecGen2sFn *gvec_fn, 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); @@ -1359,6 +1300,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]); \ } @@ -1456,10 +1400,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); - dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); @@ -1470,20 +1410,16 @@ static bool opivi_trans(uint32_t vd, uint32_t imm, uint32_t vs2, uint32_t vm, 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->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)); - 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; } @@ -1494,14 +1430,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 && !(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); @@ -1515,6 +1447,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); \ } @@ -1546,9 +1481,6 @@ 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); @@ -1557,11 +1489,10 @@ static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, 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; @@ -1591,30 +1522,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) @@ -1629,9 +1554,6 @@ 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); @@ -1640,10 +1562,9 @@ static bool do_opiwv_widen(DisasContext *s, arg_rmrr *a, 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; @@ -1698,38 +1619,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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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, \ - 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; \ } /* @@ -1838,10 +1757,6 @@ 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 && !(s->vta && s->lmul < 0)) { TCGv_i32 src1 = tcg_temp_new_i32(); @@ -1850,8 +1765,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); @@ -1864,7 +1778,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]); \ } @@ -1895,9 +1811,6 @@ 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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ @@ -1905,12 +1818,11 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ 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; \ @@ -2027,8 +1939,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) @@ -2041,8 +1952,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) @@ -2068,9 +1978,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) @@ -2086,10 +1996,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) @@ -2109,17 +2019,13 @@ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) 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_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; @@ -2133,9 +2039,6 @@ 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); s1 = get_gpr(s, a->rs1, EXT_SIGN); @@ -2145,7 +2048,6 @@ static bool trans_vmv_v_x(DisasContext *s, arg_vmv_v_x *a) 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); - tcg_temp_free_i64(s1_i64); } else { tcg_gen_gvec_dup_tl(s->sew, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), s1); @@ -2162,17 +2064,13 @@ static bool trans_vmv_v_x(DisasContext *s, arg_vmv_v_x *a) }; 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; @@ -2188,7 +2086,6 @@ static bool trans_vmv_v_i(DisasContext *s, arg_vmv_v_i *a) 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; @@ -2199,21 +2096,15 @@ static bool trans_vmv_v_i(DisasContext *s, arg_vmv_v_i *a) 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; @@ -2259,8 +2150,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) @@ -2271,8 +2161,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) @@ -2305,7 +2194,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) { @@ -2335,9 +2224,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 */ @@ -2351,10 +2238,7 @@ 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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ @@ -2364,12 +2248,11 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ 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; \ @@ -2387,32 +2270,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; } @@ -2425,9 +2299,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 */ @@ -2462,12 +2334,10 @@ GEN_OPFVF_TRANS(vfrsub_vf, opfvf_check) static bool opfvv_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && 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 */ @@ -2479,10 +2349,7 @@ 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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);\ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ @@ -2490,12 +2357,11 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ 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; \ @@ -2507,12 +2373,10 @@ GEN_OPFVV_WIDEN_TRANS(vfwsub_vv, opfvv_widen_check) static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && 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 */ @@ -2541,12 +2405,10 @@ GEN_OPFVF_WIDEN_TRANS(vfwsub_vf) static bool opfwv_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && 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 */ @@ -2558,10 +2420,7 @@ 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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); \ \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ @@ -2569,12 +2428,11 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ 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; \ @@ -2586,12 +2444,10 @@ GEN_OPFWV_WIDEN_TRANS(vfwsub_wv) static bool opfwf_widen_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && + require_rvf(s) && 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 */ @@ -2668,9 +2524,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, @@ -2679,26 +2533,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; @@ -2739,9 +2585,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) @@ -2754,9 +2598,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) @@ -2777,9 +2619,7 @@ 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; @@ -2791,7 +2631,6 @@ static bool trans_vfmv_v_f(DisasContext *s, arg_vfmv_v_f *a) 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; @@ -2803,26 +2642,19 @@ static bool trans_vfmv_v_f(DisasContext *s, arg_vfmv_v_f *a) 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; @@ -2864,49 +2696,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_rvfmin(s) && + require_scale_rvfmin(s); } #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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; \ @@ -2930,9 +2750,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) \ @@ -2945,22 +2763,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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; \ @@ -2987,49 +2801,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_rvf(s) && - (s->sew != MO_8) && - require_scale_zve32f(s) && - require_scale_zve64f(s); + require_rvfmin(s) && + require_scale_rvfmin(s); +} + +static bool opffv_rod_narrow_check(DisasContext *s, arg_rmr *a) +{ + return opfv_narrow_check(s, a) && + require_rvf(s) && + require_scale_rvf(s); } #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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; \ @@ -3042,7 +2851,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) @@ -3051,41 +2860,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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; \ @@ -3131,9 +2930,7 @@ 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(vfredusum_vs, freduction_check) @@ -3145,8 +2942,8 @@ GEN_OPFVV_TRANS(vfredmin_vs, freduction_check) static bool freduction_widen_check(DisasContext *s, arg_rmrr *a) { return reduction_widen_check(s, a) && - require_scale_rvf(s) && - (s->sew != MO_8); + require_rvf(s) && + require_scale_rvf(s); } GEN_OPFVV_WIDEN_TRANS(vfwredusum_vs, freduction_widen_check) @@ -3164,20 +2961,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); \ - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, 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; \ @@ -3197,7 +2990,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; @@ -3208,18 +3001,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; @@ -3230,7 +3019,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; @@ -3241,25 +3030,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) \ { \ @@ -3267,11 +3055,9 @@ 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); \ @@ -3280,11 +3066,10 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ 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; \ @@ -3308,10 +3093,8 @@ 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); @@ -3322,11 +3105,10 @@ static bool trans_viota_m(DisasContext *s, arg_viota_m *a) 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; @@ -3340,9 +3122,6 @@ 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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); @@ -3353,11 +3132,10 @@ static bool trans_vid_v(DisasContext *s, arg_vid_v *a) 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; @@ -3397,11 +3175,10 @@ static void load_element(TCGv_i64 dest, TCGv_ptr base, break; default: g_assert_not_reached(); - break; } } -/* 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 @@ -3441,13 +3218,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); @@ -3456,14 +3231,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 */ @@ -3486,7 +3259,6 @@ static void store_element(TCGv_i64 val, TCGv_ptr base, break; default: g_assert_not_reached(); - break; } } @@ -3497,7 +3269,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] */ @@ -3517,9 +3289,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; @@ -3535,7 +3306,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(); @@ -3547,9 +3317,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; @@ -3560,9 +3330,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); @@ -3578,6 +3346,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; @@ -3588,17 +3358,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] */ @@ -3606,9 +3373,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; @@ -3641,17 +3409,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) @@ -3707,8 +3471,7 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) } if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { - int scale = s->lmul - (s->sew + 3); - int vlmax = s->cfg_ptr->vlen >> -scale; + int vlmax = vext_get_vlmax(s->cfg_ptr->vlenb, s->sew, s->lmul); TCGv_i64 dest = tcg_temp_new_i64(); if (a->rs1 == 0) { @@ -3719,8 +3482,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, @@ -3739,8 +3501,7 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) } if (a->vm && s->vl_eq_vlmax && !(s->vta && s->lmul < 0)) { - int scale = s->lmul - (s->sew + 3); - int vlmax = s->cfg_ptr->vlen >> -scale; + 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); @@ -3749,7 +3510,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, @@ -3775,7 +3536,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) @@ -3786,47 +3547,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; \ @@ -3854,9 +3608,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); - tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over); static gen_helper_gvec_3_ptr * const fns[6][4] = { { @@ -3896,12 +3647,11 @@ static bool int_ext_op(DisasContext *s, arg_rmr *a, uint8_t seq) 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..27bf3f0b68 --- /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, 0); \ + 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, 0); \ + 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, 0); \ + 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, 0); + 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, 0); + 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_rvzabha.c.inc b/target/riscv/insn_trans/trans_rvzabha.c.inc new file mode 100644 index 0000000000..ce8edcba62 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzabha.c.inc @@ -0,0 +1,145 @@ +/* + * RISC-V translation routines for the Zabha Standard Extension. + * + * Copyright (c) 2024 Alibaba Group + * + * 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_ZABHA(ctx) do { \ + if (!ctx->cfg_ptr->ext_zabha) { \ + return false; \ + } \ +} while (0) + +static bool trans_amoswap_b(DisasContext *ctx, arg_amoswap_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_SB); +} + +static bool trans_amoadd_b(DisasContext *ctx, arg_amoadd_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_SB); +} + +static bool trans_amoxor_b(DisasContext *ctx, arg_amoxor_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_SB); +} + +static bool trans_amoand_b(DisasContext *ctx, arg_amoand_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_SB); +} + +static bool trans_amoor_b(DisasContext *ctx, arg_amoor_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_SB); +} + +static bool trans_amomin_b(DisasContext *ctx, arg_amomin_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_SB); +} + +static bool trans_amomax_b(DisasContext *ctx, arg_amomax_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_SB); +} + +static bool trans_amominu_b(DisasContext *ctx, arg_amominu_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_SB); +} + +static bool trans_amomaxu_b(DisasContext *ctx, arg_amomaxu_b *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_SB); +} + +static bool trans_amoswap_h(DisasContext *ctx, arg_amoswap_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, MO_TESW); +} + +static bool trans_amoadd_h(DisasContext *ctx, arg_amoadd_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, MO_TESW); +} + +static bool trans_amoxor_h(DisasContext *ctx, arg_amoxor_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, MO_TESW); +} + +static bool trans_amoand_h(DisasContext *ctx, arg_amoand_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, MO_TESW); +} + +static bool trans_amoor_h(DisasContext *ctx, arg_amoor_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, MO_TESW); +} + +static bool trans_amomin_h(DisasContext *ctx, arg_amomin_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, MO_TESW); +} + +static bool trans_amomax_h(DisasContext *ctx, arg_amomax_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, MO_TESW); +} + +static bool trans_amominu_h(DisasContext *ctx, arg_amominu_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, MO_TESW); +} + +static bool trans_amomaxu_h(DisasContext *ctx, arg_amomaxu_h *a) +{ + REQUIRE_ZABHA(ctx); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, MO_TESW); +} + +static bool trans_amocas_b(DisasContext *ctx, arg_amocas_b *a) +{ + REQUIRE_ZACAS(ctx); + REQUIRE_ZABHA(ctx); + return gen_cmpxchg(ctx, a, MO_SB); +} + +static bool trans_amocas_h(DisasContext *ctx, arg_amocas_h *a) +{ + REQUIRE_ZACAS(ctx); + REQUIRE_ZABHA(ctx); + return gen_cmpxchg(ctx, a, MO_ALIGN | MO_TESW); +} 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..15e688a033 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzacas.c.inc @@ -0,0 +1,137 @@ +/* + * 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 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, RISCV_UW2_ALWAYS_STORE_AMO); + 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, RISCV_UW2_ALWAYS_STORE_AMO); + 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..0eef033838 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzawrs.c.inc @@ -0,0 +1,62 @@ +/* + * 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_sto(DisasContext *ctx, arg_wrs_sto *a) +{ + 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; +} + +static bool trans_wrs_nto(DisasContext *ctx, arg_wrs_nto *a) +{ + if (!ctx->cfg_ptr->ext_zawrs) { + return false; + } + + /* + * Depending on the mode of execution, mstatus.TW and hstatus.VTW, wrs.nto + * should raise an exception when the implementation-specific bounded time + * limit has expired. Our time limit is zero, so we either return + * immediately, as does our implementation of wrs.sto, or raise an + * exception, as handled by the wrs.nto helper. + */ +#ifndef CONFIG_USER_ONLY + gen_helper_wrs_nto(tcg_env); +#endif + + /* We only get here when helper_wrs_nto() doesn't raise an exception. */ + return trans_wrs_sto(ctx, NULL); +} 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_rvzcmop.c.inc b/target/riscv/insn_trans/trans_rvzcmop.c.inc new file mode 100644 index 0000000000..7205586508 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzcmop.c.inc @@ -0,0 +1,29 @@ +/* + * RISC-V translation routines for compressed May-Be-Operation(zcmop). + * + * Copyright (c) 2024 Alibaba Group. + * + * 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_ZCMOP(ctx) do { \ + if (!ctx->cfg_ptr->ext_zcmop) { \ + return false; \ + } \ +} while (0) + +static bool trans_c_mop_n(DisasContext *ctx, arg_c_mop_n *a) +{ + REQUIRE_ZCMOP(ctx); + 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..bece48e600 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, 0); 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, 0); 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..15711c3140 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzicbo.c.inc @@ -0,0 +1,65 @@ +/* + * 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); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_clean_flush(tcg_env, src); + return true; +} + +static bool trans_cbo_flush(DisasContext *ctx, arg_cbo_flush *a) +{ + REQUIRE_ZICBOM(ctx); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_clean_flush(tcg_env, src); + return true; +} + +static bool trans_cbo_inval(DisasContext *ctx, arg_cbo_inval *a) +{ + REQUIRE_ZICBOM(ctx); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_inval(tcg_env, src); + return true; +} + +static bool trans_cbo_zero(DisasContext *ctx, arg_cbo_zero *a) +{ + REQUIRE_ZICBOZ(ctx); + TCGv src = get_address(ctx, a->rs1, 0); + + gen_helper_cbo_zero(tcg_env, src); + return true; +} diff --git a/target/riscv/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/insn_trans/trans_rvzicfiss.c.inc new file mode 100644 index 0000000000..e3ebc4977c --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzicfiss.c.inc @@ -0,0 +1,114 @@ +/* + * RISC-V translation routines for the Control-Flow Integrity Extension + * + * Copyright (c) 2024 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 . + */ +static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) +{ + if (!ctx->bcfi_enabled) { + return false; + } + + TCGv addr = tcg_temp_new(); + TCGLabel *skip = gen_new_label(); + uint32_t tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; + TCGv data = tcg_temp_new(); + tcg_gen_ld_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); + tcg_gen_qemu_ld_tl(data, addr, SS_MMU_INDEX(ctx), + mxl_memop(ctx) | MO_ALIGN); + TCGv rs1 = get_gpr(ctx, a->rs1, EXT_NONE); + tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); + tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_BCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); + gen_helper_raise_exception(tcg_env, + tcg_constant_i32(RISCV_EXCP_SW_CHECK)); + gen_set_label(skip); + tcg_gen_addi_tl(addr, addr, tmp); + tcg_gen_st_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + + return true; +} + +static bool trans_sspush(DisasContext *ctx, arg_sspush *a) +{ + if (!ctx->bcfi_enabled) { + return false; + } + + TCGv addr = tcg_temp_new(); + int tmp = (get_xl(ctx) == MXL_RV64) ? -8 : -4; + TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); + decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); + tcg_gen_ld_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + tcg_gen_addi_tl(addr, addr, tmp); + tcg_gen_qemu_st_tl(data, addr, SS_MMU_INDEX(ctx), + mxl_memop(ctx) | MO_ALIGN); + tcg_gen_st_tl(addr, tcg_env, offsetof(CPURISCVState, ssp)); + + return true; +} + +static bool trans_ssrdp(DisasContext *ctx, arg_ssrdp *a) +{ + if (!ctx->bcfi_enabled || a->rd == 0) { + return false; + } + + TCGv dest = dest_gpr(ctx, a->rd); + tcg_gen_ld_tl(dest, tcg_env, offsetof(CPURISCVState, ssp)); + gen_set_gpr(ctx, a->rd, dest); + + return true; +} + +static bool trans_ssamoswap_w(DisasContext *ctx, arg_amoswap_w *a) +{ + REQUIRE_A_OR_ZAAMO(ctx); + if (!ctx->bcfi_enabled) { + return false; + } + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); + src1 = get_address(ctx, a->rs1, 0); + + tcg_gen_atomic_xchg_tl(dest, src1, src2, SS_MMU_INDEX(ctx), + (MO_ALIGN | MO_TESL)); + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +static bool trans_ssamoswap_d(DisasContext *ctx, arg_amoswap_w *a) +{ + REQUIRE_64BIT(ctx); + REQUIRE_A_OR_ZAAMO(ctx); + if (!ctx->bcfi_enabled) { + return false; + } + + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); + src1 = get_address(ctx, a->rs1, 0); + + tcg_gen_atomic_xchg_tl(dest, src1, src2, SS_MMU_INDEX(ctx), + (MO_ALIGN | MO_TESQ)); + gen_set_gpr(ctx, a->rd, dest); + 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_rvzimop.c.inc b/target/riscv/insn_trans/trans_rvzimop.c.inc new file mode 100644 index 0000000000..165aacd2b6 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvzimop.c.inc @@ -0,0 +1,37 @@ +/* + * RISC-V translation routines for May-Be-Operation(zimop). + * + * Copyright (c) 2024 Alibaba Group. + * + * 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_ZIMOP(ctx) do { \ + if (!ctx->cfg_ptr->ext_zimop) { \ + return false; \ + } \ +} while (0) + +static bool trans_mop_r_n(DisasContext *ctx, arg_mop_r_n *a) +{ + REQUIRE_ZIMOP(ctx); + gen_set_gpr(ctx, a->rd, ctx->zero); + return true; +} + +static bool trans_mop_rr_n(DisasContext *ctx, arg_mop_rr_n *a) +{ + REQUIRE_ZIMOP(ctx); + gen_set_gpr(ctx, a->rd, ctx->zero); + return true; +} diff --git a/target/riscv/insn_trans/trans_svinval.c.inc b/target/riscv/insn_trans/trans_svinval.c.inc index 2682bd969f..a06c3b214f 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, 0); + 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, 0); + 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, 0); + 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/internals.h b/target/riscv/internals.h index 5620fbffb6..ddbdee885b 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -21,6 +21,44 @@ #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 + * - Shadow stack+U 0b1000 + * - Shadow stack+S 0b1001 + */ +#define MMUIdx_U 0 +#define MMUIdx_S 1 +#define MMUIdx_S_SUM 2 +#define MMUIdx_M 3 +#define MMU_2STAGE_BIT (1 << 2) +#define MMU_IDX_SS_WRITE (1 << 3) + +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) @@ -52,7 +90,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); @@ -62,7 +100,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; } @@ -78,7 +116,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); @@ -88,7 +126,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; } @@ -101,4 +139,7 @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) } } +/* Our implementation of CPUClass::has_work */ +bool riscv_cpu_has_work(CPUState *cs); + #endif diff --git a/target/riscv/kvm-stub.c b/target/riscv/kvm-stub.c deleted file mode 100644 index 4e8fc31a21..0000000000 --- a/target/riscv/kvm-stub.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * QEMU KVM RISC-V specific function stubs - * - * 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 "cpu.h" -#include "kvm_riscv.h" - -void kvm_riscv_reset_vcpu(RISCVCPU *cpu) -{ - abort(); -} - -void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level) -{ - abort(); -} diff --git a/target/riscv/kvm.c b/target/riscv/kvm.c deleted file mode 100644 index 30f21453d6..0000000000 --- a/target/riscv/kvm.c +++ /dev/null @@ -1,538 +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; -} - -void kvm_arch_accel_class_init(ObjectClass *oc) -{ -} diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c new file mode 100644 index 0000000000..c53ca1f76b --- /dev/null +++ b/target/riscv/kvm/kvm-cpu.c @@ -0,0 +1,2073 @@ +/* + * 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("zimop", ext_zimop, KVM_RISCV_ISA_EXT_ZIMOP), + KVM_EXT_CFG("zcmop", ext_zcmop, KVM_RISCV_ISA_EXT_ZCMOP), + KVM_EXT_CFG("zacas", ext_zacas, KVM_RISCV_ISA_EXT_ZACAS), + KVM_EXT_CFG("zawrs", ext_zawrs, KVM_RISCV_ISA_EXT_ZAWRS), + 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("zca", ext_zca, KVM_RISCV_ISA_EXT_ZCA), + KVM_EXT_CFG("zcb", ext_zcb, KVM_RISCV_ISA_EXT_ZCB), + KVM_EXT_CFG("zcd", ext_zcd, KVM_RISCV_ISA_EXT_ZCD), + KVM_EXT_CFG("zcf", ext_zcf, KVM_RISCV_ISA_EXT_ZCF), + 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("ztso", ext_ztso, KVM_RISCV_ISA_EXT_ZTSO), + 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 KVMCPUConfig kvm_sbi_dbcn = { + .name = "sbi_dbcn", + .kvm_reg_id = KVM_REG_RISCV | KVM_REG_SIZE_U64 | + KVM_REG_RISCV_SBI_EXT | KVM_RISCV_SBI_EXT_DBCN +}; + +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) { + if (!reg && ret == -EINVAL) { + warn_report("KVM cannot disable extension %s", + multi_ext_cfg->name); + } else { + error_report("Unable to enable extension %s in KVM, error %d", + 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_check_sbi_dbcn_support(RISCVCPU *cpu, + KVMScratchCPU *kvmcpu, + struct kvm_reg_list *reglist) +{ + struct kvm_reg_list *reg_search; + + reg_search = bsearch(&kvm_sbi_dbcn.kvm_reg_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + + if (reg_search) { + kvm_sbi_dbcn.supported = true; + } +} + +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: %d", + 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); + } + + kvm_riscv_check_sbi_dbcn_support(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, Error **errp) +{ + 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, Error **errp) +{ + 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; +} + +static int kvm_vcpu_enable_sbi_dbcn(RISCVCPU *cpu, CPUState *cs) +{ + target_ulong reg = 1; + + if (!kvm_sbi_dbcn.supported) { + return 0; + } + + return kvm_set_one_reg(cs, kvm_sbi_dbcn.kvm_reg_id, ®); +} + +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); + + ret = kvm_vcpu_enable_sbi_dbcn(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 void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) +{ + g_autofree uint8_t *buf = NULL; + RISCVCPU *cpu = RISCV_CPU(cs); + target_ulong num_bytes; + uint64_t addr; + unsigned char ch; + int ret; + + switch (run->riscv_sbi.function_id) { + case SBI_EXT_DBCN_CONSOLE_READ: + case SBI_EXT_DBCN_CONSOLE_WRITE: + num_bytes = run->riscv_sbi.args[0]; + + if (num_bytes == 0) { + run->riscv_sbi.ret[0] = SBI_SUCCESS; + run->riscv_sbi.ret[1] = 0; + break; + } + + addr = run->riscv_sbi.args[1]; + + /* + * Handle the case where a 32 bit CPU is running in a + * 64 bit addressing env. + */ + if (riscv_cpu_mxl(&cpu->env) == MXL_RV32) { + addr |= (uint64_t)run->riscv_sbi.args[2] << 32; + } + + buf = g_malloc0(num_bytes); + + if (run->riscv_sbi.function_id == SBI_EXT_DBCN_CONSOLE_READ) { + ret = qemu_chr_fe_read_all(serial_hd(0)->be, buf, num_bytes); + if (ret < 0) { + error_report("SBI_EXT_DBCN_CONSOLE_READ: error when " + "reading chardev"); + exit(1); + } + + cpu_physical_memory_write(addr, buf, ret); + } else { + cpu_physical_memory_read(addr, buf, num_bytes); + + ret = qemu_chr_fe_write_all(serial_hd(0)->be, buf, num_bytes); + if (ret < 0) { + error_report("SBI_EXT_DBCN_CONSOLE_WRITE: error when " + "writing chardev"); + exit(1); + } + } + + run->riscv_sbi.ret[0] = SBI_SUCCESS; + run->riscv_sbi.ret[1] = ret; + break; + case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: + ch = run->riscv_sbi.args[0]; + ret = qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); + + if (ret < 0) { + error_report("SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: error when " + "writing chardev"); + exit(1); + } + + run->riscv_sbi.ret[0] = SBI_SUCCESS; + run->riscv_sbi.ret[1] = 0; + break; + default: + run->riscv_sbi.ret[0] = SBI_ERR_NOT_SUPPORTED; + } +} + +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; + case SBI_EXT_DBCN: + kvm_riscv_handle_sbi_dbcn(cs, run); + 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; +} + +static int kvm_riscv_handle_csr(CPUState *cs, struct kvm_run *run) +{ + target_ulong csr_num = run->riscv_csr.csr_num; + target_ulong new_value = run->riscv_csr.new_value; + target_ulong write_mask = run->riscv_csr.write_mask; + int ret = 0; + + switch (csr_num) { + case CSR_SEED: + run->riscv_csr.ret_value = riscv_new_csr_seed(new_value, write_mask); + break; + default: + qemu_log_mask(LOG_UNIMP, + "%s: un-handled CSR EXIT for CSR %lx\n", + __func__, csr_num); + ret = -1; + break; + } + + return ret; +} + +static bool kvm_riscv_handle_debug(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; + + /* Ensure PC is synchronised */ + kvm_cpu_synchronize_state(cs); + + if (kvm_find_sw_breakpoint(cs, env->pc)) { + return true; + } + + return false; +} + +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; + case KVM_EXIT_RISCV_CSR: + ret = kvm_riscv_handle_csr(cs, run); + break; + case KVM_EXIT_DEBUG: + if (kvm_riscv_handle_debug(cs)) { + ret = EXCP_DEBUG; + } + 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(); + } +} + +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'. " + "Changing KVM AIA modes relies on host support. Defaults to 'auto' " + "if the host supports it"); + 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; + uint64_t max_group_id; + + 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); + } + + 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 '%s', using " + "default host mode '%s'", + kvm_aia_mode_str(aia_mode), + kvm_aia_mode_str(default_aia_mode)); + + /* failed to change AIA mode, use default */ + aia_mode = default_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) { + max_group_id = socket_count - 1; + socket_bits = find_last_bit(&max_group_id, 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); + } + } + } + + + if (max_hart_per_socket > 1) { + max_hart_per_socket--; + hart_bits = find_last_bit(&max_hart_per_socket, BITS_PER_LONG) + 1; + } else { + hart_bits = 0; + } + + 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) + +static const uint32_t ebreak_insn = 0x00100073; +static const uint16_t c_ebreak_insn = 0x9002; + +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, 2, 0)) { + return -EINVAL; + } + + if ((bp->saved_insn & 0x3) == 0x3) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 0) + || cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&ebreak_insn, 4, 1)) { + return -EINVAL; + } + } else { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&c_ebreak_insn, 2, 1)) { + return -EINVAL; + } + } + + return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ + uint32_t ebreak; + uint16_t c_ebreak; + + if ((bp->saved_insn & 0x3) == 0x3) { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&ebreak, 4, 0) || + ebreak != ebreak_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 4, 1)) { + return -EINVAL; + } + } else { + if (cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&c_ebreak, 2, 0) || + c_ebreak != c_ebreak_insn || + cpu_memory_rw_debug(cs, bp->pc, (uint8_t *)&bp->saved_insn, 2, 1)) { + return -EINVAL; + } + } + + return 0; +} + +int kvm_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + /* TODO; To be implemented later. */ + return -EINVAL; +} + +int kvm_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type) +{ + /* TODO; To be implemented later. */ + return -EINVAL; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ + /* TODO; To be implemented later. */ +} + +void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) +{ + if (kvm_sw_breakpoints_active(cs)) { + dbg->control |= KVM_GUESTDBG_ENABLE; + } +} diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h new file mode 100644 index 0000000000..5851898868 --- /dev/null +++ b/target/riscv/kvm/kvm_riscv.h @@ -0,0 +1,33 @@ +/* + * QEMU KVM support -- RISC-V specific functions. + * + * 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 . + */ + +#ifndef QEMU_KVM_RISCV_H +#define QEMU_KVM_RISCV_H + +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/kvm_riscv.h b/target/riscv/kvm_riscv.h deleted file mode 100644 index ed281bdce0..0000000000 --- a/target/riscv/kvm_riscv.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * QEMU KVM support -- RISC-V specific functions. - * - * 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 . - */ - -#ifndef QEMU_KVM_RISCV_H -#define QEMU_KVM_RISCV_H - -void kvm_riscv_reset_vcpu(RISCVCPU *cpu); -void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level); - -#endif 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 c2a94a82b3..99f0af5077 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,19 +79,21 @@ 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), @@ -105,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), @@ -134,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) @@ -159,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), @@ -174,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 = { @@ -185,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), @@ -193,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; @@ -212,21 +216,33 @@ 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; + + return cpu->cfg.debug; +} + +static int debug_post_load(void *opaque, int version_id) { RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; - return riscv_feature(env, RISCV_FEATURE_DEBUG); + if (icount_enabled()) { + env->itrigger_enabled = riscv_itrigger_enabled(env); + } + + return 0; } static const VMStateDescription vmstate_debug = { @@ -234,7 +250,8 @@ static const VMStateDescription vmstate_debug = { .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_UINTTL_ARRAY(env.tdata1, RISCVCPU, RV_MAX_TRIGGERS), VMSTATE_UINTTL_ARRAY(env.tdata2, RISCVCPU, RV_MAX_TRIGGERS), @@ -253,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; @@ -266,7 +303,7 @@ 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), @@ -278,30 +315,83 @@ static bool pmu_needed(void *opaque) { RISCVCPU *cpu = opaque; - return cpu->cfg.pmu_num; + return (cpu->cfg.pmu_mask > 0); } static const VMStateDescription vmstate_pmu_ctr_state = { .name = "cpu/pmu", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = pmu_needed, - .fields = (VMStateField[]) { + .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() + } +}; + +static bool elp_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_zicfilp; +} + +static const VMStateDescription vmstate_elp = { + .name = "cpu/elp", + .version_id = 1, + .minimum_version_id = 1, + .needed = elp_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(env.elp, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool ssp_needed(void *opaque) +{ + RISCVCPU *cpu = opaque; + + return cpu->cfg.ext_zicfiss; +} + +static const VMStateDescription vmstate_ssp = { + .name = "cpu/ssp", + .version_id = 1, + .minimum_version_id = 1, + .needed = ssp_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINTTL(env.ssp, RISCVCPU), VMSTATE_END_OF_LIST() } }; const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 5, - .minimum_version_id = 5, + .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), @@ -316,17 +406,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_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), @@ -340,30 +432,34 @@ 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_UINTTL(env.mcountinhibit, 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.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, + &vmstate_elp, + &vmstate_ssp, NULL } }; diff --git a/target/riscv/meson.build b/target/riscv/meson.build index ba25164d74..a4bd61e52a 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -2,6 +2,7 @@ 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'), ] @@ -15,23 +16,30 @@ 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', 'pmu.c', - 'time_helper.c' + 'th_csr.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..100005ea4e 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, @@ -184,7 +184,6 @@ static void mem_info_svxx(Monitor *mon, CPUArchState *env) break; default: g_assert_not_reached(); - break; } /* calculate virtual address bits */ @@ -218,7 +217,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..eddedacf4b 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 */ @@ -49,7 +51,7 @@ target_ulong helper_csrr(CPURISCVState *env, int csr) } target_ulong val = 0; - RISCVException ret = riscv_csrrw(env, csr, &val, 0, 0); + RISCVException ret = riscv_csrr(env, csr, &val); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -82,9 +84,7 @@ target_ulong helper_csrrw(CPURISCVState *env, int csr, target_ulong helper_csrr_i128(CPURISCVState *env, int csr) { Int128 rv = int128_zero(); - RISCVException ret = riscv_csrrw_i128(env, csr, &rv, - int128_zero(), - int128_zero()); + RISCVException ret = riscv_csrr_i128(env, csr, &rv); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -123,12 +123,146 @@ 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) { uint64_t mstatus; - target_ulong prev_priv, prev_virt; + target_ulong prev_priv, prev_virt = env->virt_enabled; if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); @@ -143,45 +277,46 @@ 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) { riscv_cpu_swap_hypervisor_regs(env); } - - riscv_cpu_set_virt_enabled(env, prev_virt); - } else { - prev_priv = get_field(mstatus, MSTATUS_SPP); - - mstatus = set_field(mstatus, 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); + riscv_cpu_set_mode(env, prev_priv, prev_virt); + + /* + * If forward cfi enabled for new priv, restore elp status + * and clear spelp in mstatus + */ + if (cpu_get_fcfien(env)) { + env->elp = get_field(env->mstatus, MSTATUS_SPELP); + } + env->mstatus = set_field(env->mstatus, MSTATUS_SPELP, 0); return retpc; } @@ -200,27 +335,37 @@ 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); - env->mstatus = mstatus; - riscv_cpu_set_mode(env, prev_priv); - - if (riscv_has_ext(env, RVH)) { - if (prev_virt) { - riscv_cpu_swap_hypervisor_regs(env); - } - - riscv_cpu_set_virt_enabled(env, prev_virt); + if ((env->priv_ver >= PRIV_VERSION_1_12_0) && (prev_priv != PRV_M)) { + mstatus = set_field(mstatus, MSTATUS_MPRV, 0); } + env->mstatus = mstatus; + + if (riscv_has_ext(env, RVH) && prev_virt) { + riscv_cpu_swap_hypervisor_regs(env); + } + + riscv_cpu_set_mode(env, prev_priv, prev_virt); + /* + * If forward cfi enabled for new priv, restore elp status + * and clear mpelp in mstatus + */ + if (cpu_get_fcfien(env)) { + env->elp = get_field(env->mstatus, MSTATUS_MPELP); + } + env->mstatus = set_field(env->mstatus, MSTATUS_MPELP, 0); return retpc; } @@ -233,10 +378,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; @@ -245,31 +390,48 @@ void helper_wfi(CPURISCVState *env) } } +void helper_wrs_nto(CPURISCVState *env) +{ + if (env->virt_enabled && (env->priv == PRV_S || env->priv == PRV_U) && + get_field(env->hstatus, HSTATUS_VTW) && + !get_field(env->mstatus, MSTATUS_TW)) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); + } else if (env->priv != PRV_M && get_field(env->mstatus, MSTATUS_TW)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } +} + 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 +441,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 +449,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 2b43e399b8..a1b36664fc 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -25,11 +25,11 @@ #include "cpu.h" #include "trace.h" #include "exec/exec-all.h" +#include "exec/page-protection.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 +45,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 +87,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 +123,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 +173,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; @@ -205,23 +227,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; @@ -233,39 +244,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. @@ -292,39 +301,41 @@ 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. */ pmp_size = -(addr | TARGET_PAGE_MASK); } else { - pmp_size = sizeof(target_ulong); + pmp_size = 2 << riscv_cpu_mxl(env); } } else { 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); @@ -333,8 +344,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 */ @@ -343,9 +354,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) | @@ -370,7 +381,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: @@ -401,7 +412,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: @@ -436,39 +447,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)); + } } @@ -496,9 +510,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) { /* @@ -507,9 +522,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; @@ -517,8 +532,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"); @@ -567,8 +588,20 @@ 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); + } + + /* M-mode forward cfi to be enabled if cfi extension is implemented */ + if (env_archcpu(env)->cfg.ext_zicfilp) { + val |= (val & MSECCFG_MLPE); + } env->mseccfg = val; } @@ -583,67 +616,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; } - } - if (*tlb_size != 0) { + pmp_sa = env->pmp_state.addr[i].sa; + pmp_ea = env->pmp_state.addr[i].ea; + /* - * At this point we have a tlb_size that is the smallest possible size - * That fits within a TARGET_PAGE_SIZE and the PMP region. - * - * If the size is less then TARGET_PAGE_SIZE we drop the size to 1. - * This means the result isn't cached in the TLB and is only used for - * a single translation. + * 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 (*tlb_size < TARGET_PAGE_SIZE) { - *tlb_size = 1; + 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; } - - 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..e0530a17a3 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; @@ -43,7 +44,8 @@ typedef enum { MSECCFG_MMWP = 1 << 1, MSECCFG_RLB = 1 << 2, MSECCFG_USEED = 1 << 8, - MSECCFG_SSEED = 1 << 9 + MSECCFG_SSEED = 1 << 9, + MSECCFG_MLPE = 1 << 10, } mseccfg_field_t; typedef struct { @@ -52,8 +54,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 +65,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 index b8e56d2b7b..e05ab067d2 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -17,14 +17,15 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" #include "cpu.h" #include "pmu.h" #include "sysemu/cpu-timers.h" #include "sysemu/device_tree.h" #define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ -#define MAKE_32BIT_MASK(shift, length) \ - (((uint32_t)(~0UL) >> (32 - (length))) << (shift)) /* * To keep it simple, any event can be mapped to any programmable counters in @@ -33,13 +34,9 @@ * 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, int num_ctrs, char *pmu_name) +void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name) { - uint32_t fdt_event_ctr_map[20] = {}; - uint32_t cmask; - - /* All the programmable counters can map to any event */ - cmask = MAKE_32BIT_MASK(3, num_ctrs); + uint32_t fdt_event_ctr_map[15] = {}; /* * The event encoding is specified in the SBI specification @@ -109,7 +106,7 @@ 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 = riscv_cpu_virt_enabled(env); + bool virt_on = env->virt_enabled; /* Privilege mode filtering */ if ((env->priv == PRV_M && @@ -133,7 +130,7 @@ static int riscv_pmu_incr_ctr_rv32(RISCVCPU *cpu, uint32_t ctr_idx) /* 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(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); } } else { counter->mhpmcounterh_val++; @@ -150,7 +147,7 @@ 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 = riscv_cpu_virt_enabled(env); + bool virt_on = env->virt_enabled; /* Privilege mode filtering */ if ((env->priv == PRV_M && @@ -172,7 +169,7 @@ static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) /* 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(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); } } else { counter->mhpmcounter_val++; @@ -180,6 +177,101 @@ static int riscv_pmu_incr_ctr_rv64(RISCVCPU *cpu, uint32_t ctr_idx) return 0; } +/* + * Information needed to update counters: + * new_priv, new_virt: To correctly save starting snapshot for the newly + * started mode. Look at array being indexed with newprv. + * old_priv, old_virt: To correctly select previous snapshot for old priv + * and compute delta. Also to select correct counter + * to inc. Look at arrays being indexed with env->priv. + * + * To avoid the complexity of calling this function, we assume that + * env->priv and env->virt_enabled contain old priv and old virt and + * new priv and new virt values are passed in as arguments. + */ +static void riscv_pmu_icount_update_priv(CPURISCVState *env, + target_ulong newpriv, bool new_virt) +{ + uint64_t *snapshot_prev, *snapshot_new; + uint64_t current_icount; + uint64_t *counter_arr; + uint64_t delta; + + if (icount_enabled()) { + current_icount = icount_get_raw(); + } else { + current_icount = cpu_get_host_ticks(); + } + + if (env->virt_enabled) { + g_assert(env->priv <= PRV_S); + counter_arr = env->pmu_fixed_ctrs[1].counter_virt; + snapshot_prev = env->pmu_fixed_ctrs[1].counter_virt_prev; + } else { + counter_arr = env->pmu_fixed_ctrs[1].counter; + snapshot_prev = env->pmu_fixed_ctrs[1].counter_prev; + } + + if (new_virt) { + g_assert(newpriv <= PRV_S); + snapshot_new = env->pmu_fixed_ctrs[1].counter_virt_prev; + } else { + snapshot_new = env->pmu_fixed_ctrs[1].counter_prev; + } + + /* + * new_priv can be same as env->priv. So we need to calculate + * delta first before updating snapshot_new[new_priv]. + */ + delta = current_icount - snapshot_prev[env->priv]; + snapshot_new[newpriv] = current_icount; + + counter_arr[env->priv] += delta; +} + +static void riscv_pmu_cycle_update_priv(CPURISCVState *env, + target_ulong newpriv, bool new_virt) +{ + uint64_t *snapshot_prev, *snapshot_new; + uint64_t current_ticks; + uint64_t *counter_arr; + uint64_t delta; + + if (icount_enabled()) { + current_ticks = icount_get(); + } else { + current_ticks = cpu_get_host_ticks(); + } + + if (env->virt_enabled) { + g_assert(env->priv <= PRV_S); + counter_arr = env->pmu_fixed_ctrs[0].counter_virt; + snapshot_prev = env->pmu_fixed_ctrs[0].counter_virt_prev; + } else { + counter_arr = env->pmu_fixed_ctrs[0].counter; + snapshot_prev = env->pmu_fixed_ctrs[0].counter_prev; + } + + if (new_virt) { + g_assert(newpriv <= PRV_S); + snapshot_new = env->pmu_fixed_ctrs[0].counter_virt_prev; + } else { + snapshot_new = env->pmu_fixed_ctrs[0].counter_prev; + } + + delta = current_ticks - snapshot_prev[env->priv]; + snapshot_new[newpriv] = current_ticks; + + counter_arr[env->priv] += delta; +} + +void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, + bool new_virt) +{ + riscv_pmu_cycle_update_priv(env, newpriv, new_virt); + riscv_pmu_icount_update_priv(env, newpriv, new_virt); +} + int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) { uint32_t ctr_idx; @@ -187,7 +279,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) CPURISCVState *env = &cpu->env; gpointer value; - if (!cpu->cfg.pmu_num) { + if (!cpu->cfg.pmu_mask) { return 0; } value = g_hash_table_lookup(cpu->pmu_event_ctr_map, @@ -197,8 +289,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) } ctr_idx = GPOINTER_TO_UINT(value); - if (!riscv_pmu_counter_enabled(cpu, ctr_idx) || - get_field(env->mcountinhibit, BIT(ctr_idx))) { + if (!riscv_pmu_counter_enabled(cpu, ctr_idx)) { return -1; } @@ -223,7 +314,7 @@ bool riscv_pmu_ctr_monitor_instructions(CPURISCVState *env, return true; } - cpu = RISCV_CPU(env_cpu(env)); + cpu = env_archcpu(env); if (!cpu->pmu_event_ctr_map) { return false; } @@ -249,7 +340,7 @@ bool riscv_pmu_ctr_monitor_cycles(CPURISCVState *env, uint32_t target_ctr) return true; } - cpu = RISCV_CPU(env_cpu(env)); + cpu = env_archcpu(env); if (!cpu->pmu_event_ctr_map) { return false; } @@ -289,7 +380,7 @@ int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) { uint32_t event_idx; - RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); + RISCVCPU *cpu = env_archcpu(env); if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->pmu_event_ctr_map) { return -1; @@ -329,15 +420,52 @@ int riscv_pmu_update_event_map(CPURISCVState *env, uint64_t value, return 0; } +static bool pmu_hpmevent_is_of_set(CPURISCVState *env, uint32_t ctr_idx) +{ + target_ulong mhpmevent_val; + uint64_t of_bit_mask; + + 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; + } + + return get_field(mhpmevent_val, of_bit_mask); +} + +static bool pmu_hpmevent_set_of_if_clear(CPURISCVState *env, uint32_t ctr_idx) +{ + target_ulong *mhpmevent_val; + uint64_t of_bit_mask; + + 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; + } + + if (!get_field(*mhpmevent_val, of_bit_mask)) { + *mhpmevent_val |= of_bit_mask; + return true; + } + + return false; +} + 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; + uint64_t curr_ctr_val, curr_ctrh_val; + uint64_t ctr_val; if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && evt_idx != RISCV_PMU_EVENT_HW_INSTRUCTIONS) { @@ -350,12 +478,9 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, 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; + /* Generate interrupt only if OF bit is clear */ + if (pmu_hpmevent_is_of_set(env, ctr_idx)) { + return; } counter = &env->pmu_ctrs[ctr_idx]; @@ -367,11 +492,29 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, return; } + riscv_pmu_read_ctr(env, (target_ulong *)&curr_ctr_val, false, ctr_idx); + ctr_val = counter->mhpmcounter_val; + if (riscv_cpu_mxl(env) == MXL_RV32) { + riscv_pmu_read_ctr(env, (target_ulong *)&curr_ctrh_val, true, ctr_idx); + curr_ctr_val = curr_ctr_val | (curr_ctrh_val << 32); + ctr_val = ctr_val | + ((uint64_t)counter->mhpmcounterh_val << 32); + } + + /* + * We can not accommodate for inhibited modes when setting up timer. Check + * if the counter has actually overflowed or not by comparing current + * counter value (accommodated for inhibited modes) with software written + * counter value. + */ + if (curr_ctr_val >= ctr_val) { + riscv_pmu_setup_timer(env, curr_ctr_val, ctr_idx); + 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(cpu, MIP_LCOFIP, BOOL_TO_MASK(1)); + if (pmu_hpmevent_set_of_if_clear(env, ctr_idx)) { + riscv_cpu_update_mip(env, MIP_LCOFIP, BOOL_TO_MASK(1)); } } } @@ -388,12 +531,14 @@ void riscv_pmu_timer_cb(void *priv) int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) { - uint64_t overflow_delta, overflow_at; + uint64_t overflow_delta, overflow_at, curr_ns; int64_t overflow_ns, overflow_left = 0; - RISCVCPU *cpu = RISCV_CPU(env_cpu(env)); + RISCVCPU *cpu = env_archcpu(env); PMUCTRState *counter = &env->pmu_ctrs[ctr_idx]; - if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf) { + /* No need to setup a timer if LCOFI is disabled when OF is set */ + if (!riscv_pmu_counter_valid(cpu, ctr_idx) || !cpu->cfg.ext_sscofpmf || + pmu_hpmevent_is_of_set(env, ctr_idx)) { return -1; } @@ -419,7 +564,10 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) } else { return -1; } - overflow_at = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + overflow_ns; + curr_ns = (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + overflow_at = curr_ns + overflow_ns; + if (overflow_at <= curr_ns) + overflow_at = UINT64_MAX; if (overflow_at > INT64_MAX) { overflow_left += overflow_at - INT64_MAX; @@ -432,22 +580,23 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx) } -int riscv_pmu_init(RISCVCPU *cpu, int num_counters) +void riscv_pmu_init(RISCVCPU *cpu, Error **errp) { - if (num_counters > (RV_MAX_MHPMCOUNTERS - 3)) { - return -1; + 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) { - /* PMU support can not be enabled */ - qemu_log_mask(LOG_UNIMP, "PMU events can't be supported\n"); - cpu->cfg.pmu_num = 0; - return -1; + error_setg(errp, "Unable to allocate PMU event hash table"); + return; } - /* Create a bitmask of available programmable counters */ - cpu->pmu_avail_ctrs = MAKE_32BIT_MASK(3, num_counters); - - return 0; + cpu->pmu_avail_ctrs = cpu->cfg.pmu_mask; } diff --git a/target/riscv/pmu.h b/target/riscv/pmu.h index 3004ce37b6..3853d0e262 100644 --- a/target/riscv/pmu.h +++ b/target/riscv/pmu.h @@ -16,21 +16,27 @@ * this program. If not, see . */ -#include "qemu/osdep.h" -#include "qemu/log.h" +#ifndef RISCV_PMU_H +#define RISCV_PMU_H + #include "cpu.h" -#include "qemu/main-loop.h" -#include "exec/exec-all.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); -int riscv_pmu_init(RISCVCPU *cpu, int num_counters); +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, int num_counters, char *pmu_name); +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); +void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, target_ulong newpriv, + bool new_virt); +RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, + bool upper_half, 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..7dfe5f72c6 100644 --- a/target/riscv/sbi_ecall_interface.h +++ b/target/riscv/sbi_ecall_interface.h @@ -12,6 +12,17 @@ /* clang-format off */ +#define SBI_SUCCESS 0 +#define SBI_ERR_FAILED -1 +#define SBI_ERR_NOT_SUPPORTED -2 +#define SBI_ERR_INVALID_PARAM -3 +#define SBI_ERR_DENIED -4 +#define SBI_ERR_INVALID_ADDRESS -5 +#define SBI_ERR_ALREADY_AVAILABLE -6 +#define SBI_ERR_ALREADY_STARTED -7 +#define SBI_ERR_ALREADY_STOPPED -8 +#define SBI_ERR_NO_SHMEM -9 + /* SBI Extension IDs */ #define SBI_EXT_0_1_SET_TIMER 0x0 #define SBI_EXT_0_1_CONSOLE_PUTCHAR 0x1 @@ -27,8 +38,9 @@ #define SBI_EXT_IPI 0x735049 #define SBI_EXT_RFENCE 0x52464E43 #define SBI_EXT_HSM 0x48534D +#define SBI_EXT_DBCN 0x4442434E -/* 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 +49,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 @@ -57,6 +69,11 @@ #define SBI_EXT_HSM_HART_STOP 0x1 #define SBI_EXT_HSM_HART_GET_STATUS 0x2 +/* SBI function IDs for DBCN extension */ +#define SBI_EXT_DBCN_CONSOLE_WRITE 0x0 +#define SBI_EXT_DBCN_CONSOLE_READ 0x1 +#define SBI_EXT_DBCN_CONSOLE_WRITE_BYTE 0x2 + #define SBI_HSM_HART_STATUS_STARTED 0x0 #define SBI_HSM_HART_STATUS_STOPPED 0x1 #define SBI_HSM_HART_STATUS_START_PENDING 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..c62c221696 --- /dev/null +++ b/target/riscv/tcg/tcg-cpu.c @@ -0,0 +1,1469 @@ +/* + * 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 "internals.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" +#ifndef CONFIG_USER_ONLY +#include "hw/boards.h" +#endif + +/* Hash that stores user set extensions */ +static GHashTable *multi_ext_user_opts; +static GHashTable *misa_ext_user_opts; + +static GHashTable *multi_ext_implied_rules; +static GHashTable *misa_ext_implied_rules; + +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) +{ + const char *priv_spec_str = priv_spec_to_str(priv_ver); + + g_assert(priv_spec_str); + + return priv_spec_str; +} + +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(!tcg_cflags_has(cs, 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]; + env->excp_uw2 = data[2]; +} + +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, + .cpu_exec_halt = riscv_cpu_has_work, + .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; + } + + if (cpu->env.priv_ver >= PRIV_VERSION_1_13_0) { + cpu->cfg.has_priv_1_13 = 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_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 Zve64d extension depends on the Zve64f extension */ + if (cpu->cfg.ext_zve64d) { + if (!riscv_has_ext(env, RVD)) { + error_setg(errp, "Zve64d/V extensions require D extension"); + return; + } + } + + /* The Zve32f extension depends on the Zve32x extension */ + if (cpu->cfg.ext_zve32f) { + if (!riscv_has_ext(env, RVF)) { + error_setg(errp, "Zve32f/Zve64f extensions require F extension"); + return; + } + } + + 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; + } + + 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_zcmop && !cpu->cfg.ext_zca) { + error_setg(errp, "Zcmop extensions require Zca"); + return; + } + + 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; + } + + 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_zve32x) { + error_setg(errp, + "Vector crypto extensions require V or Zve* extensions"); + return; + } + + if ((cpu->cfg.ext_zvbc || cpu->cfg.ext_zvknhb) && !cpu->cfg.ext_zve64x) { + error_setg( + errp, + "Zvbc and Zvknhb extensions require V or Zve64x extensions"); + return; + } + + 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_zicfiss) { + if (!cpu->cfg.ext_zicsr) { + error_setg(errp, "zicfiss extension requires zicsr extension"); + return; + } + if (!riscv_has_ext(env, RVA)) { + error_setg(errp, "zicfiss extension requires A extension"); + return; + } + if (!riscv_has_ext(env, RVS)) { + error_setg(errp, "zicfiss extension requires S"); + return; + } + if (!cpu->cfg.ext_zimop) { + error_setg(errp, "zicfiss extension requires zimop extension"); + return; + } + if (cpu->cfg.ext_zca && !cpu->cfg.ext_zcmop) { + error_setg(errp, "zicfiss with zca requires zcmop extension"); + return; + } + } + + if (!cpu->cfg.ext_zihpm) { + cpu->cfg.pmu_mask = 0; + cpu->pmu_avail_ctrs = 0; + } + + if (cpu->cfg.ext_zicfilp && !cpu->cfg.ext_zicsr) { + error_setg(errp, "zicfilp extension requires zicsr extension"); + return; + } + + /* + * 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]); + } +} + +static void riscv_cpu_init_implied_exts_rules(void) +{ + RISCVCPUImpliedExtsRule *rule; +#ifndef CONFIG_USER_ONLY + MachineState *ms = MACHINE(qdev_get_machine()); +#endif + static bool initialized; + int i; + + /* Implied rules only need to be initialized once. */ + if (initialized) { + return; + } + + for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) { +#ifndef CONFIG_USER_ONLY + rule->enabled = bitmap_new(ms->smp.cpus); +#endif + g_hash_table_insert(misa_ext_implied_rules, + GUINT_TO_POINTER(rule->ext), (gpointer)rule); + } + + for (i = 0; (rule = riscv_multi_ext_implied_rules[i]); i++) { +#ifndef CONFIG_USER_ONLY + rule->enabled = bitmap_new(ms->smp.cpus); +#endif + g_hash_table_insert(multi_ext_implied_rules, + GUINT_TO_POINTER(rule->ext), (gpointer)rule); + } + + initialized = true; +} + +static void cpu_enable_implied_rule(RISCVCPU *cpu, + RISCVCPUImpliedExtsRule *rule) +{ + CPURISCVState *env = &cpu->env; + RISCVCPUImpliedExtsRule *ir; + bool enabled = false; + int i; + +#ifndef CONFIG_USER_ONLY + enabled = test_bit(cpu->env.mhartid, rule->enabled); +#endif + + if (!enabled) { + /* Enable the implied MISAs. */ + if (rule->implied_misa_exts) { + for (i = 0; misa_bits[i] != 0; i++) { + if (rule->implied_misa_exts & misa_bits[i]) { + /* + * If the user disabled the misa_bit do not re-enable it + * and do not apply any implied rules related to it. + */ + if (cpu_misa_ext_is_user_set(misa_bits[i]) && + !(env->misa_ext & misa_bits[i])) { + continue; + } + + riscv_cpu_set_misa_ext(env, env->misa_ext | misa_bits[i]); + ir = g_hash_table_lookup(misa_ext_implied_rules, + GUINT_TO_POINTER(misa_bits[i])); + + if (ir) { + cpu_enable_implied_rule(cpu, ir); + } + } + } + } + + /* Enable the implied extensions. */ + for (i = 0; + rule->implied_multi_exts[i] != RISCV_IMPLIED_EXTS_RULE_END; i++) { + cpu_cfg_ext_auto_update(cpu, rule->implied_multi_exts[i], true); + + ir = g_hash_table_lookup(multi_ext_implied_rules, + GUINT_TO_POINTER( + rule->implied_multi_exts[i])); + + if (ir) { + cpu_enable_implied_rule(cpu, ir); + } + } + +#ifndef CONFIG_USER_ONLY + bitmap_set(rule->enabled, cpu->env.mhartid, 1); +#endif + } +} + +/* Zc extension has special implied rules that need to be handled separately. */ +static void cpu_enable_zc_implied_rules(RISCVCPU *cpu) +{ + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + CPURISCVState *env = &cpu->env; + + 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); + } + } +} + +static void riscv_cpu_enable_implied_rules(RISCVCPU *cpu) +{ + RISCVCPUImpliedExtsRule *rule; + int i; + + /* Enable the implied extensions for Zc. */ + cpu_enable_zc_implied_rules(cpu); + + /* Enable the implied MISAs. */ + for (i = 0; (rule = riscv_misa_ext_implied_rules[i]); i++) { + if (riscv_has_ext(&cpu->env, rule->ext)) { + cpu_enable_implied_rule(cpu, rule); + } + } + + /* Enable the implied extensions. */ + for (i = 0; (rule = riscv_multi_ext_implied_rules[i]); i++) { + if (isa_ext_is_enabled(cpu, rule->ext)) { + cpu_enable_implied_rule(cpu, rule); + } + } +} + +void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp) +{ + CPURISCVState *env = &cpu->env; + Error *local_err = NULL; + + riscv_cpu_init_implied_exts_rules(); + riscv_cpu_enable_implied_rules(cpu); + + 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; + } +} + +void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu) +{ + GPtrArray *dynamic_decoders; + dynamic_decoders = g_ptr_array_sized_new(decoder_table_size); + for (size_t i = 0; i < decoder_table_size; ++i) { + if (decoder_table[i].guard_func && + decoder_table[i].guard_func(&cpu->cfg)) { + g_ptr_array_add(dynamic_decoders, + (gpointer)decoder_table[i].riscv_cpu_decode_fn); + } + } + + cpu->decoders = dynamic_decoders; +} + +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; + + tcg_cflags_set(CPU(cs), 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 | RVB | 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); + + if (!misa_ext_implied_rules) { + misa_ext_implied_rules = g_hash_table_new(NULL, g_direct_equal); + } + + if (!multi_ext_implied_rules) { + multi_ext_implied_rules = 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..ce94253fe4 --- /dev/null +++ b/target/riscv/tcg/tcg-cpu.h @@ -0,0 +1,44 @@ +/* + * 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); + +struct DisasContext; +struct RISCVCPUConfig; +typedef struct RISCVDecoder { + bool (*guard_func)(const struct RISCVCPUConfig *); + bool (*riscv_cpu_decode_fn)(struct DisasContext *, uint32_t); +} RISCVDecoder; + +typedef bool (*riscv_cpu_decode_fn)(struct DisasContext *, uint32_t); + +extern const size_t decoder_table_size; + +extern const RISCVDecoder decoder_table[]; + +void riscv_tcg_cpu_finalize_dynamic_decoder(RISCVCPU *cpu); + +#endif diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c new file mode 100644 index 0000000000..6c970d4e81 --- /dev/null +++ b/target/riscv/th_csr.c @@ -0,0 +1,79 @@ +/* + * T-Head-specific CSRs. + * + * Copyright (c) 2024 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu_vendorid.h" + +#define CSR_TH_SXSTATUS 0x5c0 + +/* TH_SXSTATUS bits */ +#define TH_SXSTATUS_UCME BIT(16) +#define TH_SXSTATUS_MAEE BIT(21) +#define TH_SXSTATUS_THEADISAEE BIT(22) + +typedef struct { + int csrno; + int (*insertion_test)(RISCVCPU *cpu); + riscv_csr_operations csr_ops; +} riscv_csr; + +static RISCVException smode(CPURISCVState *env, int csrno) +{ + if (riscv_has_ext(env, RVS)) { + return RISCV_EXCP_NONE; + } + + return RISCV_EXCP_ILLEGAL_INST; +} + +static int test_thead_mvendorid(RISCVCPU *cpu) +{ + if (cpu->cfg.mvendorid != THEAD_VENDOR_ID) { + return -1; + } + + return 0; +} + +static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno, + target_ulong *val) +{ + /* We don't set MAEE here, because QEMU does not implement MAEE. */ + *val = TH_SXSTATUS_UCME | TH_SXSTATUS_THEADISAEE; + return RISCV_EXCP_NONE; +} + +static riscv_csr th_csr_list[] = { + { + .csrno = CSR_TH_SXSTATUS, + .insertion_test = test_thead_mvendorid, + .csr_ops = { "th.sxstatus", smode, read_th_sxstatus } + } +}; + +void th_register_custom_csrs(RISCVCPU *cpu) +{ + for (size_t i = 0; i < ARRAY_SIZE(th_csr_list); i++) { + int csrno = th_csr_list[i].csrno; + riscv_csr_operations *csr_ops = &th_csr_list[i].csr_ops; + if (!th_csr_list[i].insertion_test(cpu)) { + riscv_set_csr_ops(csrno, csr_ops); + } + } +} diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index 8cce667dfd..bc0d9a0c4c 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -27,25 +27,24 @@ static void riscv_vstimer_cb(void *opaque) RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; env->vstime_irq = 1; - riscv_cpu_update_mip(cpu, MIP_VSTIP, BOOL_TO_MASK(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, MIP_STIP, BOOL_TO_MASK(1)); + 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(RISCVCPU *cpu, QEMUTimer *timer, +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; - CPURISCVState *env = &cpu->env; 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; @@ -57,16 +56,45 @@ void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, */ 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)); } - riscv_cpu_update_mip(cpu, timer_irq, 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) { + timer_del(timer); + return; } - /* Clear the [V]STIP bit in mip */ - riscv_cpu_update_mip(cpu, timer_irq, BOOL_TO_MASK(0)); /* otherwise, set up the future timer interrupt */ diff = timecmp - rtc_r; diff --git a/target/riscv/time_helper.h b/target/riscv/time_helper.h index 7b3cdcc350..cacd79b80c 100644 --- a/target/riscv/time_helper.h +++ b/target/riscv/time_helper.h @@ -22,7 +22,7 @@ #include "cpu.h" #include "qemu/timer.h" -void riscv_timer_write_timecmp(RISCVCPU *cpu, QEMUTimer *timer, +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); diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 1ed4bb5ec3..bccaf8e89a 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -20,8 +20,6 @@ #include "qemu/log.h" #include "cpu.h" #include "tcg/tcg-op.h" -#include "disas/disas.h" -#include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -30,9 +28,14 @@ #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 + +#include "tcg/tcg-cpu.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 */ @@ -42,8 +45,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 @@ -57,28 +58,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; /* @@ -98,21 +101,26 @@ typedef struct DisasContext { uint8_t vta; uint8_t vma; bool cfg_vta_all_1s; - target_ulong vstart; + 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; + const GPtrArray *decoders; + /* zicfilp extension. fcfi_enabled, lp expected or not */ + bool fcfi_enabled; + bool fcfi_lp_expected; + /* zicfiss extension, if shadow stack was enabled during TB gen */ + bool bcfi_enabled; } DisasContext; static inline bool has_ext(DisasContext *ctx, uint32_t ext) @@ -120,19 +128,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) @@ -141,6 +136,16 @@ 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 + +#define mxl_memop(ctx) ((get_xl(ctx) + 1) | MO_TE) + /* The word size for this machine mode. */ static inline int __attribute__((unused)) get_xlen(DisasContext *ctx) { @@ -192,12 +197,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) @@ -208,59 +211,111 @@ 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 decode_save_opc(DisasContext *ctx) +static void decode_save_opc(DisasContext *ctx, target_ulong excp_uw2) { - assert(ctx->insn_start != NULL); - tcg_set_insn_start_param(ctx->insn_start, 1, ctx->opcode); - ctx->insn_start = NULL; + assert(!ctx->insn_start_updated); + ctx->insn_start_updated = true; + tcg_set_insn_start_param(ctx->base.insn_start, 1, ctx->opcode); + tcg_set_insn_start_param(ctx->base.insn_start, 2, excp_uw2); } -static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest) +static void gen_pc_plus_diff(TCGv target, DisasContext *ctx, + target_long diff) { - if (get_xl(ctx) == MXL_RV32) { - dest = (int32_t)dest; - } - tcg_gen_movi_tl(cpu_pc, dest); -} + target_ulong dest = ctx->base.pc_next + diff; -static void gen_set_pc(DisasContext *ctx, TCGv dest) -{ - if (get_xl(ctx) == MXL_RV32) { - tcg_gen_ext32s_tl(cpu_pc, dest); + 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)); + 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) { - tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), cpu_env, + tcg_gen_st_i32(tcg_constant_i32(ctx->opcode), tcg_env, offsetof(CPURISCVState, bins)); - generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST); + 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) { - tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); + 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); } } @@ -272,12 +327,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; @@ -292,11 +341,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: @@ -324,7 +373,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]; } @@ -332,7 +381,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]; } @@ -388,12 +437,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) { @@ -407,7 +450,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; } @@ -433,7 +476,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; } @@ -453,12 +496,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]; @@ -468,7 +511,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) { @@ -522,29 +565,51 @@ 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_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) { @@ -557,8 +622,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. */ @@ -570,26 +634,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 @@ -597,8 +655,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. */ @@ -606,32 +663,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) { @@ -639,14 +696,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; } /* The helper may raise ILLEGAL_INSN -- record binv for unwind. */ - decode_save_opc(ctx); - gen_helper_set_rounding_mode(cpu_env, tcg_constant_i32(rm)); + decode_save_opc(ctx, 0); + 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, 0); + gen_helper_set_rounding_mode_chkfrm(tcg_env, tcg_constant_i32(rm)); } static int ex_plus_1(DisasContext *ctx, int nf) @@ -696,8 +766,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) @@ -707,6 +777,11 @@ static int ex_rvc_register(DisasContext *ctx, int reg) return 8 + reg; } +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. */ @@ -963,7 +1038,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; } @@ -1011,15 +1085,52 @@ static bool gen_unary_per_ol(DisasContext *ctx, arg_r2 *a, DisasExtend ext, return gen_unary(ctx, a, ext, f_tl); } +static bool gen_amo(DisasContext *ctx, arg_atomic *a, + void(*func)(TCGv, TCGv, TCGv, TCGArg, MemOp), + MemOp mop) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1, src2 = get_gpr(ctx, a->rs2, EXT_NONE); + MemOp size = mop & MO_SIZE; + + if (ctx->cfg_ptr->ext_zama16b && size >= MO_32) { + mop |= MO_ATOM_WITHIN16; + } else { + mop |= MO_ALIGN; + } + + decode_save_opc(ctx, RISCV_UW2_ALWAYS_STORE_AMO); + src1 = get_address(ctx, a->rs1, 0); + func(dest, src1, src2, ctx->mem_idx, mop); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +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, RISCV_UW2_ALWAYS_STORE_AMO); + tcg_gen_atomic_cmpxchg_tl(dest, src1, dest, src2, ctx->mem_idx, mop); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + 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); + return translator_ldl(env, &ctx->base, pc); } +#define SS_MMU_INDEX(ctx) (ctx->mem_idx | MMU_IDX_SS_WRITE) + /* Include insn module translation function */ #include "insn_trans/trans_rvi.c.inc" #include "insn_trans/trans_rvm.c.inc" @@ -1029,14 +1140,29 @@ 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_rvzabha.c.inc" +#include "insn_trans/trans_rvzawrs.c.inc" +#include "insn_trans/trans_rvzicbo.c.inc" +#include "insn_trans/trans_rvzimop.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 "insn_trans/trans_rvzcmop.c.inc" +#include "insn_trans/trans_rvzicfiss.c.inc" + /* Include decoders for factored-out extensions */ #include "decode-XVentanaCondOps.c.inc" @@ -1048,25 +1174,27 @@ static inline int insn_len(uint16_t first_word) return (first_word & 3) == 3 ? 4 : 2; } +const RISCVDecoder decoder_table[] = { + { always_true_p, decode_insn32 }, + { has_xthead_p, decode_xthead}, + { has_XVentanaCondOps_p, decode_XVentanaCodeOps}, +}; + +const size_t decoder_table_size = ARRAY_SIZE(decoder_table); + static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) { - /* - * A table with predicate (i.e., guard) functions and decoder functions - * that are tested in-order until a decoder matches onto the opcode. - */ - static const struct { - bool (*guard_func)(DisasContext *); - bool (*decode_func)(DisasContext *, uint32_t); - } decoders[] = { - { always_true_p, decode_insn32 }, - { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, - }; - + ctx->virt_inst_excp = false; + ctx->cur_insn_len = insn_len(opcode); /* Check for compressed insn */ - if (insn_len(opcode) == 2) { + if (ctx->cur_insn_len == 2) { ctx->opcode = opcode; - ctx->pc_succ_insn = ctx->base.pc_next + 2; - if (has_ext(ctx, RVC) && decode_insn16(ctx, 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 { @@ -1075,11 +1203,10 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) translator_lduw(env, &ctx->base, ctx->base.pc_next + 2)); 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) && - decoders[i].decode_func(ctx, opcode32)) { + for (guint i = 0; i < ctx->decoders->len; ++i) { + riscv_cpu_decode_fn func = g_ptr_array_index(ctx->decoders, i); + if (func(ctx, opcode32)) { return; } } @@ -1091,48 +1218,43 @@ 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->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 = env->vstart; + 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->bcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, BCFI_ENABLED); + ctx->fcfi_lp_expected = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_LP_EXPECTED); + ctx->fcfi_enabled = FIELD_EX32(tb_flags, TB_FLAGS, FCFI_ENABLED); ctx->zero = tcg_constant_tl(0); + ctx->virt_inst_excp = false; + ctx->decoders = cpu->decoders; } static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -1142,45 +1264,57 @@ 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, 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; + ctx->base.pc_next += ctx->cur_insn_len; - for (i = ctx->ntemp - 1; i >= 0; --i) { - tcg_temp_free(ctx->temp[i]); - ctx->temp[i] = NULL; + /* + * If 'fcfi_lp_expected' is still true after processing the instruction, + * then we did not see an 'lpad' instruction, and must raise an exception. + * Insert code to raise the exception at the start of the insn; any other + * code the insn may have emitted will be deleted as dead code following + * the noreturn exception + */ + if (ctx->fcfi_lp_expected) { + /* Emit after insn_start, i.e. before the op following insn_start. */ + tcg_ctx->emit_before_op = QTAILQ_NEXT(ctx->base.insn_start, link); + tcg_gen_st_tl(tcg_constant_tl(RISCV_EXCP_SW_CHECK_FCFI_TVAL), + tcg_env, offsetof(CPURISCVState, sw_check_code)); + gen_helper_raise_exception(tcg_env, + tcg_constant_i32(RISCV_EXCP_SW_CHECK)); + tcg_ctx->emit_before_op = NULL; + ctx->base.is_jmp = DISAS_NORETURN; } - 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; /* Only the first insn within a TB is allowed to cross a page boundary. */ if (ctx->base.is_jmp == DISAS_NEXT) { - if (!is_same_page(&ctx->base, ctx->base.pc_next)) { + 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); + uint16_t next_insn = + translator_lduw(env, &ctx->base, ctx->base.pc_next); int len = insn_len(next_insn); - if (!is_same_page(&ctx->base, ctx->base.pc_next + len)) { + if (!is_same_page(&ctx->base, ctx->base.pc_next + len - 1)) { ctx->base.is_jmp = DISAS_TOO_MANY; } } @@ -1194,7 +1328,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; @@ -1203,33 +1337,16 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void riscv_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ -#ifndef CONFIG_USER_ONLY - RISCVCPU *rvcpu = RISCV_CPU(cpu); - CPURISCVState *env = &rvcpu->env; -#endif - - 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); -#endif - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps riscv_tr_ops = { .init_disas_context = riscv_tr_init_disas_context, .tb_start = riscv_tr_tb_start, .insn_start = riscv_tr_insn_start, .translate_insn = riscv_tr_translate_insn, .tb_stop = riscv_tr_tb_stop, - .disas_log = riscv_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; @@ -1249,28 +1366,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 0020b9a95d..a85dd1d200 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -22,10 +22,13 @@ #include "cpu.h" #include "exec/memop.h" #include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/page-protection.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 +36,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,9 +70,13 @@ 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 if (s1 < 2 * vlmax && cpu->cfg.rvv_vl_half_avl) { + vl = (s1 + 1) >> 1; } else { vl = vlmax; } @@ -75,68 +87,6 @@ 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); -} - -static inline uint32_t vext_vta(uint32_t desc) -{ - return FIELD_EX32(simd_data(desc), VDATA, VTA); -} - -static inline uint32_t vext_vma(uint32_t desc) -{ - return FIELD_EX32(simd_data(desc), VDATA, VMA); -} - -static inline uint32_t vext_vta_all_1s(uint32_t desc) -{ - return FIELD_EX32(simd_data(desc), VDATA, VTA_ALL_1S); -} - /* * Get the maximum number of elements can be operated. * @@ -155,30 +105,15 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) return scale < 0 ? vlenb >> -scale : vlenb << scale; } -/* - * 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; -} - 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 @@ -191,31 +126,18 @@ 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); } } -/* set agnostic elements to 1s */ -static 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); -} - static inline void vext_set_elem_mask(void *v0, int index, uint8_t value) { @@ -225,67 +147,127 @@ 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, - uint32_t idx, void *vd, uintptr_t retaddr); +typedef void vext_ldst_elem_fn_tlb(CPURISCVState *env, abi_ptr addr, + uint32_t idx, void *vd, uintptr_t retaddr); +typedef void vext_ldst_elem_fn_host(void *vd, uint32_t idx, void *host); -#define GEN_VEXT_LD_ELEM(NAME, ETYPE, H, LDSUF) \ -static void NAME(CPURISCVState *env, abi_ptr addr, \ - uint32_t idx, void *vd, uintptr_t retaddr)\ -{ \ - ETYPE *cur = ((ETYPE *)vd + H(idx)); \ - *cur = cpu_##LDSUF##_data_ra(env, addr, retaddr); \ -} \ - -GEN_VEXT_LD_ELEM(lde_b, int8_t, H1, ldsb) -GEN_VEXT_LD_ELEM(lde_h, int16_t, H2, ldsw) -GEN_VEXT_LD_ELEM(lde_w, int32_t, H4, ldl) -GEN_VEXT_LD_ELEM(lde_d, int64_t, H8, ldq) - -#define GEN_VEXT_ST_ELEM(NAME, ETYPE, H, STSUF) \ -static void NAME(CPURISCVState *env, abi_ptr addr, \ - uint32_t idx, void *vd, uintptr_t retaddr)\ -{ \ - ETYPE data = *((ETYPE *)vd + H(idx)); \ - cpu_##STSUF##_data_ra(env, addr, data, retaddr); \ +#define GEN_VEXT_LD_ELEM(NAME, ETYPE, H, LDSUF) \ +static inline QEMU_ALWAYS_INLINE \ +void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \ + uint32_t idx, void *vd, uintptr_t retaddr) \ +{ \ + ETYPE *cur = ((ETYPE *)vd + H(idx)); \ + *cur = cpu_##LDSUF##_data_ra(env, addr, retaddr); \ +} \ + \ +static inline QEMU_ALWAYS_INLINE \ +void NAME##_host(void *vd, uint32_t idx, void *host) \ +{ \ + ETYPE *cur = ((ETYPE *)vd + H(idx)); \ + *cur = (ETYPE)LDSUF##_p(host); \ } -GEN_VEXT_ST_ELEM(ste_b, int8_t, H1, stb) -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) +GEN_VEXT_LD_ELEM(lde_b, uint8_t, H1, ldub) +GEN_VEXT_LD_ELEM(lde_h, uint16_t, H2, lduw) +GEN_VEXT_LD_ELEM(lde_w, uint32_t, H4, ldl) +GEN_VEXT_LD_ELEM(lde_d, uint64_t, H8, ldq) + +#define GEN_VEXT_ST_ELEM(NAME, ETYPE, H, STSUF) \ +static inline QEMU_ALWAYS_INLINE \ +void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \ + uint32_t idx, void *vd, uintptr_t retaddr) \ +{ \ + ETYPE data = *((ETYPE *)vd + H(idx)); \ + cpu_##STSUF##_data_ra(env, addr, data, retaddr); \ +} \ + \ +static inline QEMU_ALWAYS_INLINE \ +void NAME##_host(void *vd, uint32_t idx, void *host) \ +{ \ + ETYPE data = *((ETYPE *)vd + H(idx)); \ + STSUF##_p(host, data); \ +} + +GEN_VEXT_ST_ELEM(ste_b, uint8_t, H1, stb) +GEN_VEXT_ST_ELEM(ste_h, uint16_t, H2, stw) +GEN_VEXT_ST_ELEM(ste_w, uint32_t, H4, stl) +GEN_VEXT_ST_ELEM(ste_d, uint64_t, H8, stq) + +static inline QEMU_ALWAYS_INLINE void +vext_continus_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb, + void *vd, uint32_t evl, target_ulong addr, + uint32_t reg_start, uintptr_t ra, uint32_t esz, + bool is_load) +{ + uint32_t i; + for (i = env->vstart; i < evl; env->vstart = ++i, addr += esz) { + ldst_tlb(env, adjust_addr(env, addr), i, vd, ra); + } +} + +static inline QEMU_ALWAYS_INLINE void +vext_continus_ldst_host(CPURISCVState *env, vext_ldst_elem_fn_host *ldst_host, + void *vd, uint32_t evl, uint32_t reg_start, void *host, + uint32_t esz, bool is_load) +{ +#if HOST_BIG_ENDIAN + for (; reg_start < evl; reg_start++, host += esz) { + ldst_host(vd, reg_start, host); + } +#else + if (esz == 1) { + uint32_t byte_offset = reg_start * esz; + uint32_t size = (evl - reg_start) * esz; + + if (is_load) { + memcpy(vd + byte_offset, host, size); + } else { + memcpy(host, vd + byte_offset, size); + } + } else { + for (; reg_start < evl; reg_start++, host += esz) { + ldst_host(vd, reg_start, host); + } + } +#endif +} + +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 log2_esz, uintptr_t ra) +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_tlb *ldst_elem, 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, log2_esz); uint32_t esz = 1 << log2_esz; - uint32_t total_elems = vext_get_total_elems(env, desc, esz); - uint32_t vta = vext_vta(desc); uint32_t vma = vext_vma(desc); - for (i = env->vstart; i < env->vl; i++, env->vstart++) { + VSTART_CHECK_EARLY_EXIT(env); + + for (i = env->vstart; i < env->vl; env->vstart = ++i) { k = 0; while (k < nf) { if (!vm && !vext_elem_mask(v0, i)) { @@ -301,18 +283,8 @@ vext_ldst_stride(void *vd, void *v0, target_ulong base, } } env->vstart = 0; - /* set tail elements to 1s */ - for (k = 0; k < nf; ++k) { - vext_set_elems_1s(vd, vta, (k * max_elems + env->vl) * esz, - (k * max_elems + max_elems) * esz); - } - if (nf * max_elems % total_elems != 0) { - uint32_t vlenb = env_archcpu(env)->cfg.vlen >> 3; - uint32_t registers_used = - ((nf * max_elems) * esz + (vlenb - 1)) / vlenb; - vext_set_elems_1s(vd, vta, (nf * max_elems) * esz, - registers_used * vlenb); - } + + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_STRIDE(NAME, ETYPE, LOAD_FN) \ @@ -325,10 +297,10 @@ void HELPER(NAME)(void *vd, void * v0, target_ulong base, \ ctzl(sizeof(ETYPE)), GETPC()); \ } -GEN_VEXT_LD_STRIDE(vlse8_v, int8_t, lde_b) -GEN_VEXT_LD_STRIDE(vlse16_v, int16_t, lde_h) -GEN_VEXT_LD_STRIDE(vlse32_v, int32_t, lde_w) -GEN_VEXT_LD_STRIDE(vlse64_v, int64_t, lde_d) +GEN_VEXT_LD_STRIDE(vlse8_v, int8_t, lde_b_tlb) +GEN_VEXT_LD_STRIDE(vlse16_v, int16_t, lde_h_tlb) +GEN_VEXT_LD_STRIDE(vlse32_v, int32_t, lde_w_tlb) +GEN_VEXT_LD_STRIDE(vlse64_v, int64_t, lde_d_tlb) #define GEN_VEXT_ST_STRIDE(NAME, ETYPE, STORE_FN) \ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ @@ -340,109 +312,184 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ ctzl(sizeof(ETYPE)), GETPC()); \ } -GEN_VEXT_ST_STRIDE(vsse8_v, int8_t, ste_b) -GEN_VEXT_ST_STRIDE(vsse16_v, int16_t, ste_h) -GEN_VEXT_ST_STRIDE(vsse32_v, int32_t, ste_w) -GEN_VEXT_ST_STRIDE(vsse64_v, int64_t, ste_d) +GEN_VEXT_ST_STRIDE(vsse8_v, int8_t, ste_b_tlb) +GEN_VEXT_ST_STRIDE(vsse16_v, int16_t, ste_h_tlb) +GEN_VEXT_ST_STRIDE(vsse32_v, int32_t, ste_w_tlb) +GEN_VEXT_ST_STRIDE(vsse64_v, int64_t, ste_d_tlb) /* - *** unit-stride: access elements stored contiguously in memory + * unit-stride: access elements stored contiguously in memory */ -/* 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 log2_esz, uint32_t evl, - uintptr_t ra) +/* unmasked unit-stride load and store operation */ +static inline QEMU_ALWAYS_INLINE void +vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, + uint32_t elems, uint32_t nf, uint32_t max_elems, + uint32_t log2_esz, bool is_load, int mmu_index, + vext_ldst_elem_fn_tlb *ldst_tlb, + vext_ldst_elem_fn_host *ldst_host, uintptr_t ra) { - uint32_t i, k; + void *host; + int i, k, flags; + uint32_t esz = 1 << log2_esz; + uint32_t size = (elems * nf) << log2_esz; + uint32_t evl = env->vstart + elems; + MMUAccessType access_type = is_load ? MMU_DATA_LOAD : MMU_DATA_STORE; + + /* Check page permission/pmp/watchpoint/etc. */ + flags = probe_access_flags(env, adjust_addr(env, addr), size, access_type, + mmu_index, true, &host, ra); + + if (flags == 0) { + if (nf == 1) { + vext_continus_ldst_host(env, ldst_host, vd, evl, env->vstart, host, + esz, is_load); + } else { + for (i = env->vstart; i < evl; ++i) { + k = 0; + while (k < nf) { + ldst_host(vd, i + k * max_elems, host); + host += esz; + k++; + } + } + } + env->vstart += elems; + } else { + if (nf == 1) { + vext_continus_ldst_tlb(env, ldst_tlb, vd, evl, addr, env->vstart, + ra, esz, is_load); + } else { + /* load bytes from guest memory */ + for (i = env->vstart; i < evl; env->vstart = ++i) { + k = 0; + while (k < nf) { + ldst_tlb(env, adjust_addr(env, addr), i + k * max_elems, + vd, ra); + addr += esz; + k++; + } + } + } + } +} + +static inline QEMU_ALWAYS_INLINE void +vext_ldst_us(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, + vext_ldst_elem_fn_tlb *ldst_tlb, + vext_ldst_elem_fn_host *ldst_host, uint32_t log2_esz, + uint32_t evl, uintptr_t ra, bool is_load) +{ + uint32_t k; + target_ulong page_split, elems, addr; uint32_t nf = vext_nf(desc); uint32_t max_elems = vext_max_elems(desc, log2_esz); uint32_t esz = 1 << log2_esz; - uint32_t total_elems = vext_get_total_elems(env, desc, esz); - uint32_t vta = vext_vta(desc); + uint32_t msize = nf * esz; + int mmu_index = riscv_env_mmu_index(env, false); - /* load bytes from guest memory */ - for (i = env->vstart; i < evl; i++, env->vstart++) { - k = 0; - while (k < nf) { - target_ulong addr = base + ((i * nf + k) << log2_esz); - ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); - k++; + if (env->vstart >= evl) { + env->vstart = 0; + return; + } + + /* Calculate the page range of first page */ + addr = base + ((env->vstart * nf) << log2_esz); + page_split = -(addr | TARGET_PAGE_MASK); + /* Get number of elements */ + elems = page_split / msize; + if (unlikely(env->vstart + elems >= evl)) { + elems = evl - env->vstart; + } + + /* Load/store elements in the first page */ + if (likely(elems)) { + vext_page_ldst_us(env, vd, addr, elems, nf, max_elems, log2_esz, + is_load, mmu_index, ldst_tlb, ldst_host, ra); + } + + /* Load/store elements in the second page */ + if (unlikely(env->vstart < evl)) { + /* Cross page element */ + if (unlikely(page_split % msize)) { + for (k = 0; k < nf; k++) { + addr = base + ((env->vstart * nf + k) << log2_esz); + ldst_tlb(env, adjust_addr(env, addr), + env->vstart + k * max_elems, vd, ra); + } + env->vstart++; } + + addr = base + ((env->vstart * nf) << log2_esz); + /* Get number of elements of second page */ + elems = evl - env->vstart; + + /* Load/store elements in the second page */ + vext_page_ldst_us(env, vd, addr, elems, nf, max_elems, log2_esz, + is_load, mmu_index, ldst_tlb, ldst_host, ra); } + env->vstart = 0; - /* set tail elements to 1s */ - for (k = 0; k < nf; ++k) { - vext_set_elems_1s(vd, vta, (k * max_elems + evl) * esz, - (k * max_elems + max_elems) * esz); - } - if (nf * max_elems % total_elems != 0) { - uint32_t vlenb = env_archcpu(env)->cfg.vlen >> 3; - uint32_t registers_used = - ((nf * max_elems) * esz + (vlenb - 1)) / vlenb; - vext_set_elems_1s(vd, vta, (nf * max_elems) * esz, - registers_used * vlenb); - } + 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) \ -void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - 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()); \ -} \ - \ -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()); \ +#define GEN_VEXT_LD_US(NAME, ETYPE, LOAD_FN_TLB, LOAD_FN_HOST) \ +void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + uint32_t stride = vext_nf(desc) << ctzl(sizeof(ETYPE)); \ + vext_ldst_stride(vd, v0, base, stride, env, desc, false, \ + LOAD_FN_TLB, 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_TLB, LOAD_FN_HOST, \ + ctzl(sizeof(ETYPE)), env->vl, GETPC(), true); \ } -GEN_VEXT_LD_US(vle8_v, int8_t, lde_b) -GEN_VEXT_LD_US(vle16_v, int16_t, lde_h) -GEN_VEXT_LD_US(vle32_v, int32_t, lde_w) -GEN_VEXT_LD_US(vle64_v, int64_t, lde_d) +GEN_VEXT_LD_US(vle8_v, int8_t, lde_b_tlb, lde_b_host) +GEN_VEXT_LD_US(vle16_v, int16_t, lde_h_tlb, lde_h_host) +GEN_VEXT_LD_US(vle32_v, int32_t, lde_w_tlb, lde_w_host) +GEN_VEXT_LD_US(vle64_v, int64_t, lde_d_tlb, lde_d_host) -#define GEN_VEXT_ST_US(NAME, ETYPE, STORE_FN) \ +#define GEN_VEXT_ST_US(NAME, ETYPE, STORE_FN_TLB, STORE_FN_HOST) \ void HELPER(NAME##_mask)(void *vd, void *v0, target_ulong base, \ CPURISCVState *env, uint32_t desc) \ { \ 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()); \ + vext_ldst_stride(vd, v0, base, stride, env, desc, false, \ + STORE_FN_TLB, 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()); \ + vext_ldst_us(vd, base, env, desc, STORE_FN_TLB, STORE_FN_HOST, \ + ctzl(sizeof(ETYPE)), env->vl, GETPC(), false); \ } -GEN_VEXT_ST_US(vse8_v, int8_t, ste_b) -GEN_VEXT_ST_US(vse16_v, int16_t, ste_h) -GEN_VEXT_ST_US(vse32_v, int32_t, ste_w) -GEN_VEXT_ST_US(vse64_v, int64_t, ste_d) +GEN_VEXT_ST_US(vse8_v, int8_t, ste_b_tlb, ste_b_host) +GEN_VEXT_ST_US(vse16_v, int16_t, ste_h_tlb, ste_h_host) +GEN_VEXT_ST_US(vse32_v, int32_t, ste_w_tlb, ste_w_host) +GEN_VEXT_ST_US(vse64_v, int64_t, ste_d_tlb, ste_d_host) /* - *** 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) { /* evl = ceil(vl/8) */ uint8_t evl = (env->vl + 7) >> 3; - vext_ldst_us(vd, base, env, desc, lde_b, - 0, evl, GETPC()); + vext_ldst_us(vd, base, env, desc, lde_b_tlb, lde_b_host, + 0, evl, GETPC(), true); } void HELPER(vsm_v)(void *vd, void *v0, target_ulong base, @@ -450,12 +497,12 @@ 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()); + vext_ldst_us(vd, base, env, desc, ste_b_tlb, ste_b_host, + 0, evl, GETPC(), false); } /* - *** 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); @@ -476,7 +523,7 @@ static inline void 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, + vext_ldst_elem_fn_tlb *ldst_elem, uint32_t log2_esz, uintptr_t ra) { uint32_t i, k; @@ -484,12 +531,12 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, uint32_t vm = vext_vm(desc); uint32_t max_elems = vext_max_elems(desc, log2_esz); uint32_t esz = 1 << log2_esz; - uint32_t total_elems = vext_get_total_elems(env, desc, esz); - uint32_t vta = vext_vta(desc); 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++) { + for (i = env->vstart; i < env->vl; env->vstart = ++i) { k = 0; while (k < nf) { if (!vm && !vext_elem_mask(v0, i)) { @@ -505,18 +552,8 @@ vext_ldst_index(void *vd, void *v0, target_ulong base, } } env->vstart = 0; - /* set tail elements to 1s */ - for (k = 0; k < nf; ++k) { - vext_set_elems_1s(vd, vta, (k * max_elems + env->vl) * esz, - (k * max_elems + max_elems) * esz); - } - if (nf * max_elems % total_elems != 0) { - uint32_t vlenb = env_archcpu(env)->cfg.vlen >> 3; - uint32_t registers_used = - ((nf * max_elems) * esz + (vlenb - 1)) / vlenb; - vext_set_elems_1s(vd, vta, (nf * max_elems) * esz, - registers_used * vlenb); - } + + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } #define GEN_VEXT_LD_INDEX(NAME, ETYPE, INDEX_FN, LOAD_FN) \ @@ -527,22 +564,22 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ LOAD_FN, ctzl(sizeof(ETYPE)), GETPC()); \ } -GEN_VEXT_LD_INDEX(vlxei8_8_v, int8_t, idx_b, lde_b) -GEN_VEXT_LD_INDEX(vlxei8_16_v, int16_t, idx_b, lde_h) -GEN_VEXT_LD_INDEX(vlxei8_32_v, int32_t, idx_b, lde_w) -GEN_VEXT_LD_INDEX(vlxei8_64_v, int64_t, idx_b, lde_d) -GEN_VEXT_LD_INDEX(vlxei16_8_v, int8_t, idx_h, lde_b) -GEN_VEXT_LD_INDEX(vlxei16_16_v, int16_t, idx_h, lde_h) -GEN_VEXT_LD_INDEX(vlxei16_32_v, int32_t, idx_h, lde_w) -GEN_VEXT_LD_INDEX(vlxei16_64_v, int64_t, idx_h, lde_d) -GEN_VEXT_LD_INDEX(vlxei32_8_v, int8_t, idx_w, lde_b) -GEN_VEXT_LD_INDEX(vlxei32_16_v, int16_t, idx_w, lde_h) -GEN_VEXT_LD_INDEX(vlxei32_32_v, int32_t, idx_w, lde_w) -GEN_VEXT_LD_INDEX(vlxei32_64_v, int64_t, idx_w, lde_d) -GEN_VEXT_LD_INDEX(vlxei64_8_v, int8_t, idx_d, lde_b) -GEN_VEXT_LD_INDEX(vlxei64_16_v, int16_t, idx_d, lde_h) -GEN_VEXT_LD_INDEX(vlxei64_32_v, int32_t, idx_d, lde_w) -GEN_VEXT_LD_INDEX(vlxei64_64_v, int64_t, idx_d, lde_d) +GEN_VEXT_LD_INDEX(vlxei8_8_v, int8_t, idx_b, lde_b_tlb) +GEN_VEXT_LD_INDEX(vlxei8_16_v, int16_t, idx_b, lde_h_tlb) +GEN_VEXT_LD_INDEX(vlxei8_32_v, int32_t, idx_b, lde_w_tlb) +GEN_VEXT_LD_INDEX(vlxei8_64_v, int64_t, idx_b, lde_d_tlb) +GEN_VEXT_LD_INDEX(vlxei16_8_v, int8_t, idx_h, lde_b_tlb) +GEN_VEXT_LD_INDEX(vlxei16_16_v, int16_t, idx_h, lde_h_tlb) +GEN_VEXT_LD_INDEX(vlxei16_32_v, int32_t, idx_h, lde_w_tlb) +GEN_VEXT_LD_INDEX(vlxei16_64_v, int64_t, idx_h, lde_d_tlb) +GEN_VEXT_LD_INDEX(vlxei32_8_v, int8_t, idx_w, lde_b_tlb) +GEN_VEXT_LD_INDEX(vlxei32_16_v, int16_t, idx_w, lde_h_tlb) +GEN_VEXT_LD_INDEX(vlxei32_32_v, int32_t, idx_w, lde_w_tlb) +GEN_VEXT_LD_INDEX(vlxei32_64_v, int64_t, idx_w, lde_d_tlb) +GEN_VEXT_LD_INDEX(vlxei64_8_v, int8_t, idx_d, lde_b_tlb) +GEN_VEXT_LD_INDEX(vlxei64_16_v, int16_t, idx_d, lde_h_tlb) +GEN_VEXT_LD_INDEX(vlxei64_32_v, int32_t, idx_d, lde_w_tlb) +GEN_VEXT_LD_INDEX(vlxei64_64_v, int64_t, idx_d, lde_d_tlb) #define GEN_VEXT_ST_INDEX(NAME, ETYPE, INDEX_FN, STORE_FN) \ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ @@ -553,72 +590,75 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ GETPC()); \ } -GEN_VEXT_ST_INDEX(vsxei8_8_v, int8_t, idx_b, ste_b) -GEN_VEXT_ST_INDEX(vsxei8_16_v, int16_t, idx_b, ste_h) -GEN_VEXT_ST_INDEX(vsxei8_32_v, int32_t, idx_b, ste_w) -GEN_VEXT_ST_INDEX(vsxei8_64_v, int64_t, idx_b, ste_d) -GEN_VEXT_ST_INDEX(vsxei16_8_v, int8_t, idx_h, ste_b) -GEN_VEXT_ST_INDEX(vsxei16_16_v, int16_t, idx_h, ste_h) -GEN_VEXT_ST_INDEX(vsxei16_32_v, int32_t, idx_h, ste_w) -GEN_VEXT_ST_INDEX(vsxei16_64_v, int64_t, idx_h, ste_d) -GEN_VEXT_ST_INDEX(vsxei32_8_v, int8_t, idx_w, ste_b) -GEN_VEXT_ST_INDEX(vsxei32_16_v, int16_t, idx_w, ste_h) -GEN_VEXT_ST_INDEX(vsxei32_32_v, int32_t, idx_w, ste_w) -GEN_VEXT_ST_INDEX(vsxei32_64_v, int64_t, idx_w, ste_d) -GEN_VEXT_ST_INDEX(vsxei64_8_v, int8_t, idx_d, ste_b) -GEN_VEXT_ST_INDEX(vsxei64_16_v, int16_t, idx_d, ste_h) -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) +GEN_VEXT_ST_INDEX(vsxei8_8_v, int8_t, idx_b, ste_b_tlb) +GEN_VEXT_ST_INDEX(vsxei8_16_v, int16_t, idx_b, ste_h_tlb) +GEN_VEXT_ST_INDEX(vsxei8_32_v, int32_t, idx_b, ste_w_tlb) +GEN_VEXT_ST_INDEX(vsxei8_64_v, int64_t, idx_b, ste_d_tlb) +GEN_VEXT_ST_INDEX(vsxei16_8_v, int8_t, idx_h, ste_b_tlb) +GEN_VEXT_ST_INDEX(vsxei16_16_v, int16_t, idx_h, ste_h_tlb) +GEN_VEXT_ST_INDEX(vsxei16_32_v, int32_t, idx_h, ste_w_tlb) +GEN_VEXT_ST_INDEX(vsxei16_64_v, int64_t, idx_h, ste_d_tlb) +GEN_VEXT_ST_INDEX(vsxei32_8_v, int8_t, idx_w, ste_b_tlb) +GEN_VEXT_ST_INDEX(vsxei32_16_v, int16_t, idx_w, ste_h_tlb) +GEN_VEXT_ST_INDEX(vsxei32_32_v, int32_t, idx_w, ste_w_tlb) +GEN_VEXT_ST_INDEX(vsxei32_64_v, int64_t, idx_w, ste_d_tlb) +GEN_VEXT_ST_INDEX(vsxei64_8_v, int8_t, idx_d, ste_b_tlb) +GEN_VEXT_ST_INDEX(vsxei64_16_v, int16_t, idx_d, ste_h_tlb) +GEN_VEXT_ST_INDEX(vsxei64_32_v, int32_t, idx_d, ste_w_tlb) +GEN_VEXT_ST_INDEX(vsxei64_64_v, int64_t, idx_d, ste_d_tlb) /* - *** 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 log2_esz, uintptr_t ra) +vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, + uint32_t desc, vext_ldst_elem_fn_tlb *ldst_tlb, + vext_ldst_elem_fn_host *ldst_host, 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, log2_esz); uint32_t esz = 1 << log2_esz; - uint32_t total_elems = vext_get_total_elems(env, desc, esz); - uint32_t vta = vext_vta(desc); + uint32_t msize = nf * esz; uint32_t vma = vext_vma(desc); - target_ulong addr, offset, remain; + target_ulong addr, offset, remain, page_split, elems; + 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 << log2_esz)); if (i == 0) { + /* Allow fault on first element. */ probe_pages(env, addr, nf << log2_esz, ra, MMU_DATA_LOAD); } else { - /* if it triggers an exception, no need to check watchpoint */ remain = nf << log2_esz; while (remain > 0) { + void *host; + int flags; + offset = -(addr | TARGET_PAGE_MASK); - host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, - cpu_mmu_index(env, false)); - if (host) { -#ifdef CONFIG_USER_ONLY - if (page_check_range(addr, offset, PAGE_READ) < 0) { - vl = i; - goto ProbeSuccess; - } -#else - probe_pages(env, addr, offset, ra, MMU_DATA_LOAD); -#endif - } else { + + /* Probe nonfault on subsequent elements. */ + flags = probe_access_flags(env, addr, offset, MMU_DATA_LOAD, + mmu_index, true, &host, 0); + + /* + * Stop if invalid (unmapped) or mmio (transaction may fail). + * Do not stop if watchpoint, as the spec says that + * first-fault should continue to access the same + * elements regardless of any watchpoint. + */ + if (flags & ~TLB_WATCHPOINT) { vl = i; goto ProbeSuccess; } - if (remain <= offset) { + if (remain <= offset) { break; } remain -= offset; @@ -631,48 +671,82 @@ ProbeSuccess: if (vl != 0) { env->vl = vl; } - for (i = env->vstart; i < env->vl; i++) { - k = 0; - while (k < nf) { - 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; + + if (env->vstart < env->vl) { + if (vm) { + /* Calculate the page range of first page */ + addr = base + ((env->vstart * nf) << log2_esz); + page_split = -(addr | TARGET_PAGE_MASK); + /* Get number of elements */ + elems = page_split / msize; + if (unlikely(env->vstart + elems >= env->vl)) { + elems = env->vl - env->vstart; + } + + /* Load/store elements in the first page */ + if (likely(elems)) { + vext_page_ldst_us(env, vd, addr, elems, nf, max_elems, + log2_esz, true, mmu_index, ldst_tlb, + ldst_host, ra); + } + + /* Load/store elements in the second page */ + if (unlikely(env->vstart < env->vl)) { + /* Cross page element */ + if (unlikely(page_split % msize)) { + for (k = 0; k < nf; k++) { + addr = base + ((env->vstart * nf + k) << log2_esz); + ldst_tlb(env, adjust_addr(env, addr), + env->vstart + k * max_elems, vd, ra); + } + env->vstart++; + } + + addr = base + ((env->vstart * nf) << log2_esz); + /* Get number of elements of second page */ + elems = env->vl - env->vstart; + + /* Load/store elements in the second page */ + vext_page_ldst_us(env, vd, addr, elems, nf, max_elems, + log2_esz, true, mmu_index, ldst_tlb, + ldst_host, ra); + } + } else { + for (i = env->vstart; i < env->vl; i++) { + k = 0; + while (k < nf) { + if (!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_tlb(env, adjust_addr(env, addr), i + k * max_elems, + vd, ra); + k++; + } } - 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; - /* set tail elements to 1s */ - for (k = 0; k < nf; ++k) { - vext_set_elems_1s(vd, vta, (k * max_elems + env->vl) * esz, - (k * max_elems + max_elems) * esz); - } - if (nf * max_elems % total_elems != 0) { - uint32_t vlenb = env_archcpu(env)->cfg.vlen >> 3; - uint32_t registers_used = - ((nf * max_elems) * esz + (vlenb - 1)) / vlenb; - vext_set_elems_1s(vd, vta, (nf * max_elems) * esz, - registers_used * vlenb); - } + + vext_set_tail_elems_1s(env->vl, vd, desc, nf, esz, max_elems); } -#define GEN_VEXT_LDFF(NAME, ETYPE, LOAD_FN) \ -void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ - CPURISCVState *env, uint32_t desc) \ -{ \ - vext_ldff(vd, v0, base, env, desc, LOAD_FN, \ - ctzl(sizeof(ETYPE)), GETPC()); \ +#define GEN_VEXT_LDFF(NAME, ETYPE, LOAD_FN_TLB, LOAD_FN_HOST) \ +void HELPER(NAME)(void *vd, void *v0, target_ulong base, \ + CPURISCVState *env, uint32_t desc) \ +{ \ + vext_ldff(vd, v0, base, env, desc, LOAD_FN_TLB, \ + LOAD_FN_HOST, ctzl(sizeof(ETYPE)), GETPC()); \ } -GEN_VEXT_LDFF(vle8ff_v, int8_t, lde_b) -GEN_VEXT_LDFF(vle16ff_v, int16_t, lde_h) -GEN_VEXT_LDFF(vle32ff_v, int32_t, lde_w) -GEN_VEXT_LDFF(vle64ff_v, int64_t, lde_d) +GEN_VEXT_LDFF(vle8ff_v, int8_t, lde_b_tlb, lde_b_host) +GEN_VEXT_LDFF(vle16ff_v, int16_t, lde_h_tlb, lde_h_host) +GEN_VEXT_LDFF(vle32ff_v, int32_t, lde_w_tlb, lde_w_host) +GEN_VEXT_LDFF(vle64ff_v, int64_t, lde_d_tlb, lde_d_host) #define DO_SWAP(N, M) (M) #define DO_AND(N, M) (N & M) @@ -684,106 +758,110 @@ 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 +static inline QEMU_ALWAYS_INLINE void vext_ldst_whole(void *vd, target_ulong base, CPURISCVState *env, uint32_t desc, - vext_ldst_elem_fn *ldst_elem, uint32_t log2_esz, uintptr_t ra) + vext_ldst_elem_fn_tlb *ldst_tlb, + vext_ldst_elem_fn_host *ldst_host, uint32_t log2_esz, + uintptr_t ra, bool is_load) { - uint32_t i, k, off, pos; + target_ulong page_split, elems, addr; uint32_t nf = vext_nf(desc); - uint32_t vlenb = env_archcpu(env)->cfg.vlen >> 3; + uint32_t vlenb = riscv_cpu_cfg(env)->vlenb; uint32_t max_elems = vlenb >> log2_esz; + uint32_t evl = nf * max_elems; + uint32_t esz = 1 << log2_esz; + int mmu_index = riscv_env_mmu_index(env, false); - k = env->vstart / max_elems; - off = env->vstart % max_elems; - - 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) << log2_esz); - ldst_elem(env, adjust_addr(env, addr), pos + k * max_elems, vd, ra); - } - k++; + /* Calculate the page range of first page */ + addr = base + (env->vstart << log2_esz); + page_split = -(addr | TARGET_PAGE_MASK); + /* Get number of elements */ + elems = page_split / esz; + if (unlikely(env->vstart + elems >= evl)) { + elems = evl - env->vstart; } - /* 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) << log2_esz); - ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); + /* Load/store elements in the first page */ + if (likely(elems)) { + vext_page_ldst_us(env, vd, addr, elems, 1, max_elems, log2_esz, + is_load, mmu_index, ldst_tlb, ldst_host, ra); + } + + /* Load/store elements in the second page */ + if (unlikely(env->vstart < evl)) { + /* Cross page element */ + if (unlikely(page_split % esz)) { + addr = base + (env->vstart << log2_esz); + ldst_tlb(env, adjust_addr(env, addr), env->vstart, vd, ra); + env->vstart++; } + + addr = base + (env->vstart << log2_esz); + /* Get number of elements of second page */ + elems = evl - env->vstart; + + /* Load/store elements in the second page */ + vext_page_ldst_us(env, vd, addr, elems, 1, max_elems, log2_esz, + is_load, mmu_index, ldst_tlb, ldst_host, ra); } env->vstart = 0; } -#define GEN_VEXT_LD_WHOLE(NAME, ETYPE, LOAD_FN) \ -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()); \ +#define GEN_VEXT_LD_WHOLE(NAME, ETYPE, LOAD_FN_TLB, LOAD_FN_HOST) \ +void HELPER(NAME)(void *vd, target_ulong base, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + vext_ldst_whole(vd, base, env, desc, LOAD_FN_TLB, LOAD_FN_HOST, \ + ctzl(sizeof(ETYPE)), GETPC(), true); \ } -GEN_VEXT_LD_WHOLE(vl1re8_v, int8_t, lde_b) -GEN_VEXT_LD_WHOLE(vl1re16_v, int16_t, lde_h) -GEN_VEXT_LD_WHOLE(vl1re32_v, int32_t, lde_w) -GEN_VEXT_LD_WHOLE(vl1re64_v, int64_t, lde_d) -GEN_VEXT_LD_WHOLE(vl2re8_v, int8_t, lde_b) -GEN_VEXT_LD_WHOLE(vl2re16_v, int16_t, lde_h) -GEN_VEXT_LD_WHOLE(vl2re32_v, int32_t, lde_w) -GEN_VEXT_LD_WHOLE(vl2re64_v, int64_t, lde_d) -GEN_VEXT_LD_WHOLE(vl4re8_v, int8_t, lde_b) -GEN_VEXT_LD_WHOLE(vl4re16_v, int16_t, lde_h) -GEN_VEXT_LD_WHOLE(vl4re32_v, int32_t, lde_w) -GEN_VEXT_LD_WHOLE(vl4re64_v, int64_t, lde_d) -GEN_VEXT_LD_WHOLE(vl8re8_v, int8_t, lde_b) -GEN_VEXT_LD_WHOLE(vl8re16_v, int16_t, lde_h) -GEN_VEXT_LD_WHOLE(vl8re32_v, int32_t, lde_w) -GEN_VEXT_LD_WHOLE(vl8re64_v, int64_t, lde_d) +GEN_VEXT_LD_WHOLE(vl1re8_v, int8_t, lde_b_tlb, lde_b_host) +GEN_VEXT_LD_WHOLE(vl1re16_v, int16_t, lde_h_tlb, lde_h_host) +GEN_VEXT_LD_WHOLE(vl1re32_v, int32_t, lde_w_tlb, lde_w_host) +GEN_VEXT_LD_WHOLE(vl1re64_v, int64_t, lde_d_tlb, lde_d_host) +GEN_VEXT_LD_WHOLE(vl2re8_v, int8_t, lde_b_tlb, lde_b_host) +GEN_VEXT_LD_WHOLE(vl2re16_v, int16_t, lde_h_tlb, lde_h_host) +GEN_VEXT_LD_WHOLE(vl2re32_v, int32_t, lde_w_tlb, lde_w_host) +GEN_VEXT_LD_WHOLE(vl2re64_v, int64_t, lde_d_tlb, lde_d_host) +GEN_VEXT_LD_WHOLE(vl4re8_v, int8_t, lde_b_tlb, lde_b_host) +GEN_VEXT_LD_WHOLE(vl4re16_v, int16_t, lde_h_tlb, lde_h_host) +GEN_VEXT_LD_WHOLE(vl4re32_v, int32_t, lde_w_tlb, lde_w_host) +GEN_VEXT_LD_WHOLE(vl4re64_v, int64_t, lde_d_tlb, lde_d_host) +GEN_VEXT_LD_WHOLE(vl8re8_v, int8_t, lde_b_tlb, lde_b_host) +GEN_VEXT_LD_WHOLE(vl8re16_v, int16_t, lde_h_tlb, lde_h_host) +GEN_VEXT_LD_WHOLE(vl8re32_v, int32_t, lde_w_tlb, lde_w_host) +GEN_VEXT_LD_WHOLE(vl8re64_v, int64_t, lde_d_tlb, lde_d_host) -#define GEN_VEXT_ST_WHOLE(NAME, ETYPE, STORE_FN) \ -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()); \ +#define GEN_VEXT_ST_WHOLE(NAME, ETYPE, STORE_FN_TLB, STORE_FN_HOST) \ +void HELPER(NAME)(void *vd, target_ulong base, CPURISCVState *env, \ + uint32_t desc) \ +{ \ + vext_ldst_whole(vd, base, env, desc, STORE_FN_TLB, STORE_FN_HOST, \ + ctzl(sizeof(ETYPE)), GETPC(), false); \ } -GEN_VEXT_ST_WHOLE(vs1r_v, int8_t, ste_b) -GEN_VEXT_ST_WHOLE(vs2r_v, int8_t, ste_b) -GEN_VEXT_ST_WHOLE(vs4r_v, int8_t, ste_b) -GEN_VEXT_ST_WHOLE(vs8r_v, int8_t, ste_b) +GEN_VEXT_ST_WHOLE(vs1r_v, int8_t, ste_b_tlb, ste_b_host) +GEN_VEXT_ST_WHOLE(vs2r_v, int8_t, ste_b_tlb, ste_b_host) +GEN_VEXT_ST_WHOLE(vs4r_v, int8_t, ste_b_tlb, ste_b_host) +GEN_VEXT_ST_WHOLE(vs8r_v, int8_t, ste_b_tlb, ste_b_host) /* - *** 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 @@ -800,16 +878,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) @@ -822,40 +890,6 @@ 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, - 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; - - 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); -} - -/* 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); \ -} - GEN_VEXT_VV(vadd_vv_b, 1) GEN_VEXT_VV(vadd_vv_h, 2) GEN_VEXT_VV(vadd_vv_w, 4) @@ -865,18 +899,6 @@ GEN_VEXT_VV(vsub_vv_h, 2) GEN_VEXT_VV(vsub_vv_w, 4) GEN_VEXT_VV(vsub_vv_d, 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) @@ -891,40 +913,6 @@ 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, - 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; - - 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); -} - -/* 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); \ -} - GEN_VEXT_VX(vadd_vx_b, 1) GEN_VEXT_VX(vadd_vx_h, 2) GEN_VEXT_VX(vadd_vx_w, 4) @@ -1104,6 +1092,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ 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)); \ @@ -1136,13 +1126,15 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ 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); \ } @@ -1167,10 +1159,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 = env_archcpu(env)->cfg.vlen; \ + 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)); \ @@ -1178,8 +1172,10 @@ 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 */ \ + /* + * 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); \ @@ -1203,10 +1199,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 = env_archcpu(env)->cfg.vlen; \ + 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); \ @@ -1214,8 +1212,10 @@ 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 */ \ + /* + * 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); \ @@ -1301,6 +1301,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ 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 */ \ @@ -1331,10 +1333,13 @@ 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; \ @@ -1345,6 +1350,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ 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 */ \ @@ -1402,11 +1409,13 @@ 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 = env_archcpu(env)->cfg.vlen; \ + 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)); \ @@ -1420,8 +1429,10 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ 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 */ \ + /* + * 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); \ @@ -1465,11 +1476,13 @@ 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 = env_archcpu(env)->cfg.vlen; \ + 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)) { \ @@ -1483,8 +1496,10 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ DO_OP(s2, (ETYPE)(target_long)s1)); \ } \ env->vstart = 0; \ - /* mask destination register are always tail-agnostic */ \ - /* set tail elements to 1s */ \ + /* + * 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); \ @@ -1761,9 +1776,9 @@ 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) @@ -1872,7 +1887,7 @@ 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)); \ @@ -2015,6 +2030,8 @@ void HELPER(NAME)(void *vd, void *vs1, CPURISCVState *env, \ 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; \ @@ -2039,6 +2056,8 @@ void HELPER(NAME)(void *vd, uint64_t s1, CPURISCVState *env, \ 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; \ } \ @@ -2062,6 +2081,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ 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)); \ @@ -2086,6 +2107,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, \ 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 : \ @@ -2103,7 +2126,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 */ @@ -2131,6 +2154,8 @@ vext_vv_rm_1(void *vd, void *v0, void *vs1, void *vs2, uint32_t vl, uint32_t vm, int vxrm, 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 */ @@ -2185,7 +2210,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ 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) { @@ -2255,6 +2281,8 @@ vext_vx_rm_1(void *vd, void *v0, target_long s1, void *vs2, uint32_t vl, uint32_t vm, int vxrm, 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 */ @@ -2303,7 +2331,8 @@ vext_vx_rm_2(void *vd, void *v0, target_long s1, void *vs2, /* generate helpers for fixed point instructions with OPIVX format */ #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, \ do_##NAME, ESZ); \ @@ -2328,7 +2357,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) { @@ -2338,7 +2368,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) { @@ -2348,7 +2379,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) { @@ -2376,7 +2408,8 @@ 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) { @@ -2447,7 +2480,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) { @@ -2457,7 +2491,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) { @@ -2467,7 +2502,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) { @@ -2523,7 +2559,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); @@ -2531,7 +2568,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); @@ -2596,7 +2634,8 @@ 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); @@ -2604,7 +2643,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); @@ -2677,7 +2717,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; @@ -2697,7 +2737,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; @@ -2717,7 +2757,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; @@ -2784,38 +2824,32 @@ 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) @@ -2839,41 +2873,33 @@ 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) @@ -2902,7 +2928,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; @@ -2921,7 +2947,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; @@ -2940,7 +2966,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; @@ -2973,7 +2999,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; @@ -2989,7 +3015,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; @@ -3005,7 +3031,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; @@ -3029,7 +3055,7 @@ 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) \ @@ -3054,6 +3080,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ 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 */ \ @@ -3092,11 +3120,13 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, \ uint32_t vm = vext_vm(desc); \ uint32_t vl = env->vl; \ uint32_t total_elems = \ - vext_get_total_elems(env, desc, ESZ); \ + 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 */ \ @@ -3158,13 +3188,13 @@ GEN_VEXT_VF(vfrsub_vf_d, 8) 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); } @@ -3180,13 +3210,13 @@ 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); } @@ -3290,13 +3320,13 @@ GEN_VEXT_VF(vfrdiv_vf_d, 8) 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) @@ -3311,7 +3341,7 @@ 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)); \ @@ -3343,7 +3373,7 @@ 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)); \ @@ -3359,20 +3389,20 @@ 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) @@ -3474,20 +3504,20 @@ 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) @@ -3563,13 +3593,13 @@ GEN_VEXT_VF(vfnmsub_vf_d, 8) 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) @@ -3581,18 +3611,30 @@ RVVCALL(OPFVF3, vfwmacc_vf_w, WOP_UUU_W, H8, H4, fwmacc32) 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) @@ -3607,15 +3649,15 @@ 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) @@ -3630,15 +3672,15 @@ 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) @@ -3651,14 +3693,9 @@ 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); \ @@ -3666,7 +3703,7 @@ static void do_##NAME(void *vd, void *vs2, int i, \ #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; \ @@ -3676,6 +3713,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ uint32_t vma = vext_vma(desc); \ uint32_t i; \ \ + VSTART_CHECK_EARLY_EXIT(env); \ + \ if (vl == 0) { \ return; \ } \ @@ -3743,9 +3782,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; @@ -3767,9 +3806,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); } @@ -3807,9 +3846,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); } @@ -3847,9 +3886,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); } @@ -3937,18 +3976,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) { @@ -4192,11 +4231,13 @@ 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 = env_archcpu(env)->cfg.vlen; \ + 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)); \ @@ -4211,8 +4252,10 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ DO_OP(s2, s1, &env->fp_status)); \ } \ env->vstart = 0; \ - /* mask destination register are always tail-agnostic */ \ - /* set tail elements to 1s */ \ + /* + * 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); \ @@ -4230,11 +4273,13 @@ 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 = env_archcpu(env)->cfg.vlen; \ + 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)) { \ @@ -4248,8 +4293,10 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ DO_OP(s2, (ETYPE)s1, &env->fp_status)); \ } \ env->vstart = 0; \ - /* mask destination register are always tail-agnostic */ \ - /* set tail elements to 1s */ \ + /* + * 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); \ @@ -4348,40 +4395,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) \ -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; \ - \ - 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); \ -} - target_ulong fclass_h(uint64_t frs1) { float16 f = frs1; @@ -4460,10 +4473,12 @@ void HELPER(NAME)(void *vd, void *v0, uint64_t s1, void *vs2, \ 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 */ \ @@ -4512,7 +4527,9 @@ GEN_VEXT_V_ENV(vfcvt_f_x_v_d, 8) #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, 4) @@ -4524,7 +4541,9 @@ RVVCALL(OPFVV1, vfwcvt_x_f_v_w, WOP_UU_W, H8, H4, float32_to_int64) 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) @@ -4541,8 +4560,7 @@ 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) { @@ -4554,6 +4572,9 @@ RVVCALL(OPFVV1, vfwcvt_f_f_v_w, WOP_UU_W, H8, H4, float32_to_float64) 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) */ #define NOP_UU_B uint8_t, uint16_t, uint32_t @@ -4575,7 +4596,9 @@ 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) @@ -4598,13 +4621,17 @@ 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) 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; \ @@ -4724,14 +4751,20 @@ 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) @@ -4752,7 +4785,7 @@ 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) \ @@ -4761,21 +4794,23 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, \ uint32_t desc) \ { \ uint32_t vl = env->vl; \ - uint32_t total_elems = env_archcpu(env)->cfg.vlen; \ + 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 \ + /* + * mask destination register are always tail-agnostic + * set tail elements to 1s */ \ - /* set tail elements to 1s */ \ if (vta_all_1s) { \ for (; i < total_elems; i++) { \ vext_set_elem_mask(vd, i, 1); \ @@ -4818,7 +4853,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) { @@ -4848,7 +4883,7 @@ 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 = env_archcpu(env)->cfg.vlen; + 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; @@ -4883,8 +4918,10 @@ 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 */ + /* + * 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); @@ -4957,6 +4994,8 @@ void HELPER(NAME)(void *vd, void *v0, CPURISCVState *env, uint32_t 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 */ \ @@ -4976,7 +5015,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 */ @@ -4992,6 +5031,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ 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)) { \ @@ -5001,6 +5042,7 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ *((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); \ } @@ -5022,9 +5064,12 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ 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; \ + 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)) { \ /* set masked-off elements to 1s */ \ @@ -5052,8 +5097,9 @@ GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_w, uint32_t, H4) GEN_VEXT_VSLIDEDOWN_VX(vslidedown_vx_d, uint64_t, H8) #define GEN_VEXT_VSLIE1UP(BITWIDTH, H) \ -static void vslide1up_##BITWIDTH(void *vd, void *v0, target_ulong s1, \ - void *vs2, CPURISCVState *env, uint32_t desc) \ +static void vslide1up_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ typedef uint##BITWIDTH##_t ETYPE; \ uint32_t vm = vext_vm(desc); \ @@ -5064,6 +5110,8 @@ static void vslide1up_##BITWIDTH(void *vd, void *v0, target_ulong s1, \ 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 */ \ @@ -5100,8 +5148,9 @@ GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_w, 32) GEN_VEXT_VSLIDE1UP_VX(vslide1up_vx_d, 64) #define GEN_VEXT_VSLIDE1DOWN(BITWIDTH, H) \ -static void vslide1down_##BITWIDTH(void *vd, void *v0, target_ulong s1, \ - void *vs2, CPURISCVState *env, uint32_t desc) \ +static void vslide1down_##BITWIDTH(void *vd, void *v0, uint64_t s1, \ + void *vs2, CPURISCVState *env, \ + uint32_t desc) \ { \ typedef uint##BITWIDTH##_t ETYPE; \ uint32_t vm = vext_vm(desc); \ @@ -5112,6 +5161,8 @@ static void vslide1down_##BITWIDTH(void *vd, void *v0, target_ulong s1, \ 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 */ \ @@ -5187,6 +5238,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs1, void *vs2, \ 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 */ \ @@ -5230,6 +5283,8 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ 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 */ \ @@ -5273,7 +5328,7 @@ 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); \ + vext_set_elems_1s(vd, vta, num * esz, total_elems * esz); \ } /* Compress into vd elements of vs2 where vs1 is enabled */ @@ -5291,9 +5346,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; } @@ -5311,6 +5379,8 @@ void HELPER(NAME)(void *vd, void *v0, void *vs2, \ 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 */ \ diff --git a/target/riscv/vector_internals.c b/target/riscv/vector_internals.c new file mode 100644 index 0000000000..05b2d01e58 --- /dev/null +++ b/target/riscv/vector_internals.c @@ -0,0 +1,108 @@ +/* + * 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 ; + } + + if (HOST_BIG_ENDIAN) { + /* + * Deal the situation when the elements are insdie + * only one uint64 block including setting the + * masked-off element. + */ + if (((tot - 1) ^ cnt) < 8) { + memset(base + H1(tot - 1), -1, tot - cnt); + return; + } + /* + * Otherwise, at least cross two uint64_t blocks. + * Set first unaligned block. + */ + if (cnt % 8 != 0) { + uint32_t j = ROUND_UP(cnt, 8); + memset(base + H1(j - 1), -1, j - cnt); + cnt = j; + } + /* Set other 64bit aligend blocks */ + } + 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 9003c6e9fe..65a74ce720 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -22,8 +22,10 @@ #include "cpu.h" #include "migration/vmstate.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "hw/loader.h" #include "fpu/softfloat.h" +#include "tcg/debug-assert.h" static void rx_cpu_set_pc(CPUState *cs, vaddr value) { @@ -44,7 +46,8 @@ static void rx_cpu_synchronize_from_tb(CPUState *cs, { RXCPU *cpu = RX_CPU(cs); - cpu->env.pc = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu->env.pc = tb->pc; } static void rx_restore_state_to_opc(CPUState *cs, @@ -62,14 +65,21 @@ static bool rx_cpu_has_work(CPUState *cs) (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, ResetType type) +{ + 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, type); + } memset(env, 0, offsetof(CPURXState, end_reset_fields)); @@ -83,22 +93,13 @@ static void rx_cpu_reset(DeviceState *dev) env->fpsw = 0; set_flush_to_zero(1, &env->fp_status); set_flush_inputs_to_zero(1, &env->fp_status); -} - -static void rx_cpu_list_entry(gpointer data, gpointer user_data) -{ - ObjectClass *oc = data; - - qemu_printf(" %s\n", object_class_get_name(oc)); -} - -void rx_cpu_list(void) -{ - GSList *list; - list = object_class_get_list_sorted(TYPE_RX_CPU, false); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, rx_cpu_list_entry, NULL); - g_slist_free(list); + /* + * TODO: this is not the correct NaN propagation rule for this + * architecture. The "RX Family User's Manual: Software" table 1.6 + * defines the propagation rules as "prefer SNaN over QNaN; + * then prefer dest over source", which is float_2nan_prop_s_ab. + */ + set_float_2nan_prop_rule(float_2nan_prop_x87, &env->fp_status); } static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) @@ -107,16 +108,12 @@ static ObjectClass *rx_cpu_class_by_name(const char *cpu_model) 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; } @@ -179,12 +176,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); } @@ -198,7 +191,7 @@ 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, @@ -206,6 +199,7 @@ static const struct TCGCPUOps rx_tcg_ops = { #ifndef CONFIG_USER_ONLY .cpu_exec_interrupt = rx_cpu_exec_interrupt, + .cpu_exec_halt = rx_cpu_has_work, .do_interrupt = rx_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; @@ -215,14 +209,16 @@ 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; @@ -234,7 +230,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; } @@ -243,6 +238,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 acce650185..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, diff --git a/target/rx/translate.c b/target/rx/translate.c index 87a3f54adb..9aade2b6e5 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -22,12 +22,16 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/translator.h" #include "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,14 +72,12 @@ 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) + int i, int n) { while (++i <= n) { - uint8_t b = cpu_ldub_code(ctx->env, ctx->base.pc_next++); + uint8_t b = translator_ldub(ctx->env, &ctx->base, ctx->base.pc_next++); insn |= b << (32 - i * 8); } return insn; @@ -83,26 +85,29 @@ static uint32_t decode_load_bytes(DisasContext *ctx, uint32_t insn, static uint32_t li(DisasContext *ctx, int sz) { - int32_t tmp, addr; + target_ulong addr; + uint32_t tmp; CPURXState *env = ctx->env; addr = ctx->base.pc_next; - tcg_debug_assert(sz < 4); switch (sz) { case 1: ctx->base.pc_next += 1; - return cpu_ldsb_code(env, addr); + return (int8_t)translator_ldub(env, &ctx->base, addr); case 2: ctx->base.pc_next += 2; - return cpu_ldsw_code(env, addr); + return (int16_t)translator_lduw(env, &ctx->base, addr); case 3: ctx->base.pc_next += 3; - tmp = cpu_ldsb_code(env, addr + 2) << 16; - tmp |= cpu_lduw_code(env, addr) & 0xffff; + tmp = (int8_t)translator_ldub(env, &ctx->base, addr + 2); + tmp <<= 16; + tmp |= translator_lduw(env, &ctx->base, addr); return tmp; case 0: ctx->base.pc_next += 4; - return cpu_ldl_code(env, addr); + return translator_ldl(env, &ctx->base, addr); + default: + g_assert_not_reached(); } return 0; } @@ -128,8 +133,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; @@ -188,22 +192,22 @@ static inline TCGv rx_index_addr(DisasContext *ctx, TCGv mem, { uint32_t dsp; - tcg_debug_assert(ld < 3); switch (ld) { case 0: return cpu_regs[reg]; case 1: - dsp = cpu_ldub_code(ctx->env, ctx->base.pc_next) << size; + dsp = translator_ldub(ctx->env, &ctx->base, ctx->base.pc_next) << size; tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); ctx->base.pc_next += 1; return mem; case 2: - dsp = cpu_lduw_code(ctx->env, ctx->base.pc_next) << size; + dsp = translator_lduw(ctx->env, &ctx->base, ctx->base.pc_next) << size; tcg_gen_addi_i32(mem, cpu_regs[reg], dsp); ctx->base.pc_next += 2; return mem; + default: + g_assert_not_reached(); } - return NULL; } static inline MemOp mi_to_mop(unsigned mi) @@ -234,7 +238,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 +319,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 +371,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 +386,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 +433,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 +443,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 +460,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 +474,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 +484,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 +493,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 +517,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 +535,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 +552,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 +563,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 +580,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 +597,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 +621,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 +648,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 +661,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 +671,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 +698,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 +721,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 +728,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 +762,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 +814,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 +825,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 +966,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 +1004,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 +1039,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 +1064,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 +1123,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 +1187,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 +1208,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 +1235,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 +1312,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 +1331,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 +1384,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 +1427,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 +1516,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 +1537,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 +1584,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 +1640,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 +1709,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 +1722,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 +1745,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 +1755,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 +1772,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 +1782,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 +1792,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 +1808,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 +1830,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 +1841,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 +1852,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 +1864,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 +1876,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 +1890,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 +1901,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 +1910,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 +1920,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 +1929,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 +1948,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 +1961,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 +2014,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 +2026,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 +2062,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 +2080,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 +2115,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 +2142,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 +2156,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 +2166,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 +2177,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 +2189,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 +2220,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); } } @@ -2347,24 +2249,16 @@ static void rx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void rx_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps rx_tr_ops = { .init_disas_context = rx_tr_init_disas_context, .tb_start = rx_tr_tb_start, .insn_start = rx_tr_insn_start, .translate_insn = rx_tr_translate_insn, .tb_stop = rx_tr_tb_stop, - .disas_log = rx_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; @@ -2372,7 +2266,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, } #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) @@ -2384,7 +2278,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]); } @@ -2404,6 +2298,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/Kconfig b/target/s390x/Kconfig index 72da48136c..8a95f2bc3f 100644 --- a/target/s390x/Kconfig +++ b/target/s390x/Kconfig @@ -1,2 +1,9 @@ config S390X bool + select PCI + select S390_FLIC + +config S390X_LEGACY_CPUS + bool + default y + depends on S390X diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index a7c44ba49d..029d91d93a 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -17,8 +17,8 @@ #include "s390x-internal.h" #include "elf.h" #include "sysemu/dump.h" -#include "hw/s390x/pv.h" #include "kvm/kvm_s390x.h" +#include "target/s390x/kvm/pv.h" struct S390xUserRegsStruct { uint64_t psw[2]; @@ -102,7 +102,7 @@ static void s390x_write_elf64_prstatus(Note *note, S390CPU *cpu, int id) regs->acrs[i] = cpu_to_be32(cpu->env.aregs[i]); regs->gprs[i] = cpu_to_be64(cpu->env.regs[i]); } - note->contents.prstatus.pid = id; + note->contents.prstatus.pid = cpu_to_be32(id); } static void s390x_write_elf64_fpregset(Note *note, S390CPU *cpu, int id) @@ -227,25 +227,25 @@ static int s390x_write_elf64_notes(const char *note_name, DumpState *s, const NoteFuncDesc *funcs) { - Note note, *notep; + g_autofree Note *notep = NULL; const NoteFuncDesc *nf; - int note_size, content_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++) { - notep = ¬e; if (nf->pvonly && !s390_is_pv()) { continue; } content_size = nf->note_size_func ? nf->note_size_func() : nf->contents_size; - note_size = sizeof(note) - sizeof(notep->contents) + content_size; + note_size = sizeof(Note) - sizeof(notep->contents) + content_size; - /* Notes with dynamic sizes need to allocate a note */ - if (nf->note_size_func) { + if (prev_size < note_size) { + g_free(notep); notep = g_malloc(note_size); + prev_size = note_size; } memset(notep, 0, note_size); @@ -258,15 +258,9 @@ static int s390x_write_elf64_notes(const char *note_name, /* Get contents and write them out */ (*nf->note_contents_func)(notep, cpu, id); ret = f(notep, note_size, s); - - if (nf->note_size_func) { - g_free(notep); - } - if (ret < 0) { return -1; } - } return 0; @@ -439,6 +433,22 @@ static int arch_sections_write(DumpState *s, uint8_t *buff) 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, const struct GuestPhysBlockList *guest_phys_blocks) { @@ -454,10 +464,7 @@ int cpu_get_dump_info(ArchDumpInfo *info, 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; - } else { - info->arch_sections_add_fn = NULL; - info->arch_sections_write_hdr_fn = NULL; - info->arch_sections_write_fn = NULL; + info->arch_cleanup_fn = *arch_cleanup; } return 0; } 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..a05ffcf78d 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -2,7 +2,7 @@ * S/390 cpu parameters for qemu. * * Copyright (c) 2009 Ulrich Hecht - * SPDX-License-Identifier: GPL-2.0+ + * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef S390_CPU_PARAM_H @@ -12,6 +12,11 @@ #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 -#define NB_MMU_MODES 4 + +/* + * 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) #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 96562c516d..514c70f301 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -26,19 +26,44 @@ #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 "hw/resettable.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 +80,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)); } @@ -112,23 +143,45 @@ static bool s390_cpu_has_work(CPUState *cs) return s390_cpu_has_int(cpu); } -/* S390CPUClass::reset() */ -static void s390_cpu_reset(CPUState *s, cpu_reset_type type) +static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) { - S390CPU *cpu = S390_CPU(s); + 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 Resettable reset_hold phase method */ +static void s390_cpu_reset_hold(Object *obj, ResetType type) +{ + S390CPU *cpu = S390_CPU(obj); S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); CPUS390XState *env = &cpu->env; - DeviceState *dev = DEVICE(s); - scc->parent_reset(dev); + if (scc->parent_phases.hold) { + scc->parent_phases.hold(obj, type); + } cpu->env.sigp_order = 0; s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); switch (type) { - case S390_CPU_RESET_CLEAR: + default: + /* RESET_TYPE_COLD: power on or "clear" reset */ memset(env, 0, offsetof(CPUS390XState, start_initial_reset_fields)); /* fall through */ - case S390_CPU_RESET_INITIAL: + case RESET_TYPE_S390_CPU_INITIAL: /* initial reset does not clear everything! */ memset(&env->start_initial_reset_fields, 0, offsetof(CPUS390XState, start_normal_reset_fields) - @@ -152,8 +205,9 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) /* tininess for underflow is detected before rounding */ set_float_detect_tininess(float_tininess_before_rounding, &env->fpu_status); + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fpu_status); /* fall through */ - case S390_CPU_RESET_NORMAL: + case RESET_TYPE_S390_CPU_NORMAL: env->psw.mask &= ~PSW_MASK_RI; memset(&env->start_normal_reset_fields, 0, offsetof(CPUS390XState, end_reset_fields) - @@ -162,20 +216,18 @@ static void s390_cpu_reset(CPUState *s, cpu_reset_type type) env->pfault_token = -1UL; env->bpbc = false; break; - default: - g_assert_not_reached(); } /* Reset state inside the kernel that we cannot access yet from QEMU. */ if (kvm_enabled()) { switch (type) { - case S390_CPU_RESET_CLEAR: + default: kvm_s390_reset_vcpu_clear(cpu); break; - case S390_CPU_RESET_INITIAL: + case RESET_TYPE_S390_CPU_INITIAL: kvm_s390_reset_vcpu_initial(cpu); break; - case S390_CPU_RESET_NORMAL: + case RESET_TYPE_S390_CPU_NORMAL: kvm_s390_reset_vcpu_normal(cpu); break; } @@ -239,9 +291,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) @@ -249,28 +299,64 @@ 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() }; -static void s390_cpu_reset_full(DeviceState *dev) -{ - CPUState *s = CPU(dev); - return s390_cpu_reset(s, S390_CPU_RESET_CLEAR); -} - #ifdef CONFIG_TCG #include "hw/core/tcg-cpu-ops.h" -static const struct TCGCPUOps s390_tcg_ops = { +void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + 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; + if (env->psw.mask & PSW_MASK_PER) { + flags |= env->cregs[9] & (FLAG_MASK_PER_BRANCH | + FLAG_MASK_PER_IFETCH | + FLAG_MASK_PER_IFETCH_NULLIFY); + if ((env->cregs[9] & PER_CR9_EVENT_STORE) && + (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { + flags |= FLAG_MASK_PER_STORE_REAL; + } + } + if (env->cregs[0] & CR0_AFP) { + flags |= FLAG_MASK_AFP; + } + if (env->cregs[0] & CR0_VECTOR) { + flags |= FLAG_MASK_VECTOR; + } + *pflags = flags; +} + +static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, .restore_state_to_opc = s390x_restore_state_to_opc, @@ -280,6 +366,7 @@ static const struct TCGCPUOps s390_tcg_ops = { #else .tlb_fill = s390_cpu_tlb_fill, .cpu_exec_interrupt = s390_cpu_exec_interrupt, + .cpu_exec_halt = s390_cpu_has_work, .do_interrupt = s390_cpu_do_interrupt, .debug_excp_handler = s390x_cpu_debug_excp_handler, .do_unaligned_access = s390x_cpu_do_unaligned_access, @@ -292,18 +379,21 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) S390CPUClass *scc = S390_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(scc); DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); device_class_set_parent_realize(dc, s390_cpu_realizefn, &scc->parent_realize); device_class_set_props(dc, s390x_cpu_properties); dc->user_creatable = true; - device_class_set_parent_reset(dc, s390_cpu_reset_full, &scc->parent_reset); + resettable_class_set_parent_phases(rc, NULL, s390_cpu_reset_hold, NULL, + &scc->parent_phases); - 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; @@ -312,7 +402,6 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) 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 8aaf8dd5a3..5ef61b1f75 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -29,11 +29,11 @@ #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 @@ -52,7 +52,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 +75,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; @@ -132,6 +129,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; + S390CpuEntitlement entitlement; /* Used only for vertical polarization */ uint64_t cpuid; #endif @@ -152,7 +154,7 @@ struct CPUArchState { /* currently processed sigp order */ uint8_t sigp_order; -}; +} CPUS390XState; static inline uint64_t *get_freg(CPUS390XState *cs, int nr) { @@ -166,11 +168,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 */ @@ -178,6 +177,27 @@ struct ArchCPU { uint32_t irqstate_saved_size; }; +/** + * S390CPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_phases: The parent class' reset phase handlers. + * @load_normal: Performs a load normal. + * + * 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; + ResettablePhases parent_phases; + void (*load_normal)(CPUState *cpu); +}; #ifndef CONFIG_USER_ONLY extern const VMStateDescription vmstate_s390_cpu; @@ -293,6 +313,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 @@ -312,19 +333,32 @@ extern const VMStateDescription vmstate_s390_cpu; /* tb flags */ -#define FLAG_MASK_PSW_SHIFT 31 -#define FLAG_MASK_PER (PSW_MASK_PER >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_DAT (PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSTATE (PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_ASC (PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_64 (PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_32 (PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \ - | FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) +#define FLAG_MASK_PSW_SHIFT 31 +#define FLAG_MASK_32 0x00000001u +#define FLAG_MASK_64 0x00000002u +#define FLAG_MASK_AFP 0x00000004u +#define FLAG_MASK_VECTOR 0x00000008u +#define FLAG_MASK_ASC 0x00018000u +#define FLAG_MASK_PSTATE 0x00020000u +#define FLAG_MASK_PER_IFETCH_NULLIFY 0x01000000u +#define FLAG_MASK_DAT 0x08000000u +#define FLAG_MASK_PER_STORE_REAL 0x20000000u +#define FLAG_MASK_PER_IFETCH 0x40000000u +#define FLAG_MASK_PER_BRANCH 0x80000000u -/* we'll use some unused PSW positions to store CR flags in tb flags */ -#define FLAG_MASK_AFP (PSW_MASK_UNUSED_2 >> FLAG_MASK_PSW_SHIFT) -#define FLAG_MASK_VECTOR (PSW_MASK_UNUSED_3 >> FLAG_MASK_PSW_SHIFT) +QEMU_BUILD_BUG_ON(FLAG_MASK_32 != PSW_MASK_32 >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_64 != PSW_MASK_64 >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_ASC != PSW_MASK_ASC >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_PSTATE != PSW_MASK_PSTATE >> FLAG_MASK_PSW_SHIFT); +QEMU_BUILD_BUG_ON(FLAG_MASK_DAT != PSW_MASK_DAT >> FLAG_MASK_PSW_SHIFT); + +#define FLAG_MASK_PSW (FLAG_MASK_DAT | FLAG_MASK_PSTATE | \ + FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) +#define FLAG_MASK_CR9 (FLAG_MASK_PER_BRANCH | FLAG_MASK_PER_IFETCH) +#define FLAG_MASK_PER (FLAG_MASK_PER_BRANCH | \ + FLAG_MASK_PER_IFETCH | \ + FLAG_MASK_PER_IFETCH_NULLIFY | \ + FLAG_MASK_PER_STORE_REAL) /* Control register 0 bits */ #define CR0_LOWPROT 0x0000000010000000ULL @@ -348,7 +382,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; @@ -379,28 +413,32 @@ 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) -{ - *pc = env->psw.addr; - *cs_base = env->ex_value; - *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; - if (env->cregs[0] & CR0_AFP) { - *flags |= FLAG_MASK_AFP; - } - if (env->cregs[0] & CR0_VECTOR) { - *flags |= FLAG_MASK_VECTOR; - } -} +#ifdef CONFIG_TCG + +#include "tcg/tcg_s390x.h" + +void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags); + +#endif /* CONFIG_TCG */ /* PER bits from control register 9 */ -#define PER_CR9_EVENT_BRANCH 0x80000000 -#define PER_CR9_EVENT_IFETCH 0x40000000 -#define PER_CR9_EVENT_STORE 0x20000000 -#define PER_CR9_EVENT_STORE_REAL 0x08000000 -#define PER_CR9_EVENT_NULLIFICATION 0x01000000 -#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000 -#define PER_CR9_CONTROL_ALTERATION 0x00200000 +#define PER_CR9_EVENT_BRANCH 0x80000000 +#define PER_CR9_EVENT_IFETCH 0x40000000 +#define PER_CR9_EVENT_STORE 0x20000000 +#define PER_CR9_EVENT_STORAGE_KEY_ALTERATION 0x10000000 +#define PER_CR9_EVENT_STORE_REAL 0x08000000 +#define PER_CR9_EVENT_ZERO_ADDRESS_DETECTION 0x04000000 +#define PER_CR9_EVENT_TRANSACTION_END 0x02000000 +#define PER_CR9_EVENT_IFETCH_NULLIFICATION 0x01000000 +#define PER_CR9_CONTROL_BRANCH_ADDRESS 0x00800000 +#define PER_CR9_CONTROL_TRANSACTION_SUPRESS 0x00400000 +#define PER_CR9_CONTROL_STORAGE_ALTERATION 0x00200000 + +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_BRANCH != PER_CR9_EVENT_BRANCH); +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_IFETCH != PER_CR9_EVENT_IFETCH); +QEMU_BUILD_BUG_ON(FLAG_MASK_PER_IFETCH_NULLIFY != + PER_CR9_EVENT_IFETCH_NULLIFICATION); /* PER bits from the PER CODE/ATMID/AI in lowcore */ #define PER_CODE_EVENT_BRANCH 0x8000 @@ -444,8 +482,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) { @@ -556,6 +592,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; @@ -563,9 +622,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 */ @@ -751,16 +863,12 @@ static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg) static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) { - S390CPUClass *scc = S390_CPU_GET_CLASS(cs); - - scc->reset(cs, S390_CPU_RESET_NORMAL); + resettable_reset(OBJECT(cs), RESET_TYPE_S390_CPU_NORMAL); } static inline void s390_do_cpu_initial_reset(CPUState *cs, run_on_cpu_data arg) { - S390CPUClass *scc = S390_CPU_GET_CLASS(cs); - - scc->reset(cs, S390_CPU_RESET_INITIAL); + resettable_reset(OBJECT(cs), RESET_TYPE_S390_CPU_INITIAL); } static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) @@ -802,8 +910,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..cb4e2b8920 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, @@ -208,6 +212,23 @@ void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque, }; } +void s390_get_deprecated_features(S390FeatBitmap features) +{ + static const int feats[] = { + /* CSSKE is deprecated on newer generations */ + S390_FEAT_CONDITIONAL_SSKE, + S390_FEAT_BPB, + /* Deprecated on z16 */ + S390_FEAT_CONSTRAINT_TRANSACTIONAL_EXE, + S390_FEAT_TRANSACTIONAL_EXE + }; + int i; + + for (i = 0; i < ARRAY_SIZE(feats); i++) { + set_bit(feats[i], features); + } +} + #define FEAT_GROUP_INIT(_name, _group, _desc) \ { \ .name = _name, \ @@ -245,7 +266,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..661a8cd6db 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 */ @@ -68,6 +69,7 @@ void s390_add_from_feat_block(S390FeatBitmap features, S390FeatType type, uint8_t *data); void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque, void (*fn)(const char *name, void *opaque)); +void s390_get_deprecated_features(S390FeatBitmap features); /* Definition of a CPU feature group */ typedef struct { diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index e3cfe63735..c53ac13352 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -142,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") @@ -379,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 a85c56b4ee..a27f4b6f79 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -17,14 +17,16 @@ #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" +#include CONFIG_DEVICES #endif -#include "hw/s390x/pv.h" #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ { \ @@ -46,6 +48,13 @@ * generation 15 one base feature and one optional feature have been deprecated. */ static S390CPUDef s390_cpu_defs[] = { + /* + * Linux requires at least z10 nowadays, and IBM only supports recent CPUs + * (see https://www.ibm.com/support/pages/ibm-mainframe-life-cycle-history), + * so we consider older CPUs as legacy that can optionally be disabled via + * the CONFIG_S390X_LEGACY_CPUS config switch. + */ +#if defined(CONFIG_S390X_LEGACY_CPUS) || defined(CONFIG_USER_ONLY) CPUDEF_INIT(0x2064, 7, 1, 38, 0x00000000U, "z900", "IBM zSeries 900 GA1"), CPUDEF_INIT(0x2064, 7, 2, 38, 0x00000000U, "z900.2", "IBM zSeries 900 GA2"), CPUDEF_INIT(0x2064, 7, 3, 38, 0x00000000U, "z900.3", "IBM zSeries 900 GA3"), @@ -63,6 +72,7 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x2096, 9, 2, 40, 0x00000000U, "z9BC", "IBM System z9 BC GA1"), CPUDEF_INIT(0x2094, 9, 3, 40, 0x00000000U, "z9EC.3", "IBM System z9 EC GA3"), CPUDEF_INIT(0x2096, 9, 3, 40, 0x00000000U, "z9BC.2", "IBM System z9 BC GA2"), +#endif CPUDEF_INIT(0x2097, 10, 1, 43, 0x00000000U, "z10EC", "IBM System z10 EC GA1"), CPUDEF_INIT(0x2097, 10, 2, 43, 0x00000000U, "z10EC.2", "IBM System z10 EC GA2"), CPUDEF_INIT(0x2098, 10, 2, 43, 0x00000000U, "z10BC", "IBM System z10 BC GA1"), @@ -195,11 +205,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 +242,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 +260,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); } @@ -355,9 +364,9 @@ static void s390_print_cpu_model_list_entry(gpointer data, gpointer user_data) /* strip off the -s390x-cpu */ g_strrstr(name, "-" TYPE_S390_CPU)[0] = 0; if (details->len) { - qemu_printf("s390 %-15s %-35s (%s)\n", name, scc->desc, details->str); + qemu_printf(" %-15s %-35s (%s)\n", name, scc->desc, details->str); } else { - qemu_printf("s390 %-15s %-35s\n", name, scc->desc); + qemu_printf(" %-15s %-35s\n", name, scc->desc); } g_free(name); } @@ -402,6 +411,7 @@ void s390_cpu_list(void) S390Feat feat; GSList *list; + qemu_printf("Available CPUs:\n"); list = object_class_get_list(TYPE_S390_CPU, false); list = g_slist_sort(list, s390_cpu_list_compare); g_slist_foreach(list, s390_print_cpu_model_list_entry, NULL); @@ -411,14 +421,14 @@ void s390_cpu_list(void) for (feat = 0; feat < S390_FEAT_MAX; feat++) { const S390FeatDef *def = s390_feat_def(feat); - qemu_printf("%-20s %s\n", def->name, def->desc); + qemu_printf(" %-20s %s\n", def->name, def->desc); } qemu_printf("\nRecognized feature groups:\n"); for (group = 0; group < S390_FEAT_GROUP_MAX; group++) { const S390FeatGroupDef *def = s390_feat_group_def(group); - qemu_printf("%-20s %s\n", def->name, def->desc); + qemu_printf(" %-20s %s\n", def->name, def->desc); } } @@ -480,6 +490,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; @@ -498,22 +510,29 @@ static void error_prepend_missing_feat(const char *name, void *opaque) error_prepend((Error **) opaque, "%s ", name); } -static void check_compatibility(const S390CPUModel *max_model, +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 bool 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); - return; + check_compat_model_failed(errp, max_model, "Selected CPU generation is too new"); + return false; } 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); - return; + check_compat_model_failed(errp, max_model, "Selected CPU GA level is too new"); + return false; } #ifndef CONFIG_USER_ONLY @@ -521,25 +540,27 @@ static void check_compatibility(const S390CPUModel *max_model, error_setg(errp, "The unpack facility is not compatible with " "the --only-migratable option. You must remove either " "the 'unpack' facility or the --only-migratable option"); - return; + return false; } #endif /* detect the missing features to properly report them */ bitmap_andnot(missing, model->features, max_model->features, S390_FEAT_MAX); if (bitmap_empty(missing, S390_FEAT_MAX)) { - return; + return true; } 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"); + return false; } S390CPUModel *get_max_cpu_model(Error **errp) { - Error *err = NULL; static S390CPUModel max_model; static bool cached; @@ -548,23 +569,21 @@ S390CPUModel *get_max_cpu_model(Error **errp) } if (kvm_enabled()) { - kvm_s390_get_host_cpu_model(&max_model, &err); + if (!kvm_s390_get_host_cpu_model(&max_model, errp)) { + return NULL; + } } else { max_model.def = s390_find_cpu_def(QEMU_MAX_CPU_TYPE, QEMU_MAX_CPU_GEN, QEMU_MAX_CPU_EC_GA, NULL); bitmap_copy(max_model.features, qemu_max_cpu_feat, S390_FEAT_MAX); } - if (err) { - error_propagate(errp, err); - return NULL; - } cached = true; return &max_model; } void s390_realize_cpu_model(CPUState *cs, Error **errp) { - Error *err = NULL; + ERRP_GUARD(); S390CPUClass *xcc = S390_CPU_GET_CLASS(cs); S390CPU *cpu = S390_CPU(cs); const S390CPUModel *max_model; @@ -593,9 +612,7 @@ void s390_realize_cpu_model(CPUState *cs, Error **errp) cpu->model->cpu_ver = max_model->cpu_ver; check_consistency(cpu->model); - check_compatibility(max_model, cpu->model, &err); - if (err) { - error_propagate(errp, err); + if (!check_compatibility(max_model, cpu->model, errp)) { return; } @@ -751,7 +768,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; @@ -972,7 +989,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 cc7305ec21..71d4bc2dd4 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -18,7 +18,7 @@ #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 */ @@ -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 @@ -114,23 +114,8 @@ static inline uint64_t s390_cpuid_from_cpu_model(const S390CPUModel *model) S390CPUDef const *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga, S390FeatBitmap features); -#ifdef CONFIG_KVM bool kvm_s390_cpu_models_supported(void); -void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); -void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp); -#else -static inline void kvm_s390_get_host_cpu_model(S390CPUModel *model, - Error **errp) -{ -} -static inline void kvm_s390_apply_cpu_model(const S390CPUModel *model, - Error **errp) -{ -} -static inline bool kvm_s390_cpu_models_supported(void) -{ - return false; -} -#endif +bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); +bool kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp); #endif /* TARGET_S390X_CPU_MODELS_H */ diff --git a/target/s390x/cpu_models_sysemu.c b/target/s390x/cpu_models_sysemu.c index d8a141a023..f6df691b66 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; } } @@ -222,9 +216,10 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, CpuModelExpansionInfo *expansion_info = NULL; S390CPUModel s390_model; bool delta_changes = false; + S390FeatBitmap deprecated_feats; /* 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; @@ -241,6 +236,22 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, expansion_info = g_new0(CpuModelExpansionInfo, 1); expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); cpu_info_from_model(expansion_info->model, &s390_model, delta_changes); + + /* populate list of deprecated features */ + bitmap_zero(deprecated_feats, S390_FEAT_MAX); + s390_get_deprecated_features(deprecated_feats); + + if (delta_changes) { + /* + * Only populate deprecated features that are a + * subset of the features enabled on the CPU model. + */ + bitmap_and(deprecated_feats, deprecated_feats, + s390_model.features, S390_FEAT_MAX); + } + + s390_feat_bitmap_to_ascii(deprecated_feats, + &expansion_info->deprecated_props, list_add_feat); return expansion_info; } @@ -262,12 +273,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 +350,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; @@ -395,7 +406,6 @@ CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, void apply_cpu_model(const S390CPUModel *model, Error **errp) { - Error *err = NULL; static S390CPUModel applied_model; static bool applied; @@ -411,9 +421,7 @@ void apply_cpu_model(const S390CPUModel *model, Error **errp) } if (kvm_enabled()) { - kvm_s390_apply_cpu_model(model, &err); - if (err) { - error_propagate(errp, err); + if (!kvm_s390_apply_cpu_model(model, errp)) { return; } } diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 76b01dcd68..a1fd54ddac 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; @@ -131,7 +133,14 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb); if (!valid) { - env->regs[r1 + 1] = DIAG_308_RC_INVALID; + if (subcode == DIAG308_SET && iplb->pbt == S390_IPL_TYPE_QEMU_SCSI) { + s390_rebuild_iplb(iplb->devno, iplb); + s390_ipl_update_diag308(iplb); + env->regs[r1 + 1] = DIAG_308_RC_OK; + } else { + env->regs[r1 + 1] = DIAG_308_RC_INVALID; + } + goto out; } diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index a5d69d0e0b..63373f02ce 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,9 +45,8 @@ 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; - target_ulong tmpl = ldtul_p(mem_buf); + CPUS390XState *env = cpu_env(cs); + target_ulong tmpl = ldq_be_p(mem_buf); switch (n) { case S390_PSWM_REGNUM: @@ -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,11 +81,14 @@ 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); + env->aregs[n] = ldl_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 4; default: @@ -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,14 +116,17 @@ 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); + env->fpc = ldl_be_p(mem_buf); return 4; case S390_F0_REGNUM ... S390_F15_REGNUM: - *get_freg(env, n - S390_F0_REGNUM) = ldtul_p(mem_buf); + *get_freg(env, n - S390_F0_REGNUM) = ldq_be_p(mem_buf); return 8; default: return 0; @@ -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,15 +160,18 @@ 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); + env->vregs[n][1] = ldq_be_p(mem_buf + 8); return 8; case S390_V16_REGNUM ... S390_V31_REGNUM: - env->vregs[n][0] = ldtul_p(mem_buf); - env->vregs[n][1] = ldtul_p(mem_buf + 8); + env->vregs[n][0] = ldq_be_p(mem_buf); + env->vregs[n][1] = ldq_be_p(mem_buf + 8); return 16; default: return 0; @@ -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,11 +196,14 @@ 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); + env->cregs[n] = ldq_be_p(mem_buf); if (tcg_enabled()) { tlb_flush(env_cpu(env)); } @@ -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,52 +234,83 @@ 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 = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + case S390_VIRT_CPUTM_REGNUM: + env->cputm = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + case S390_VIRT_BEA_REGNUM: + env->gbea = ldq_be_p(mem_buf); + cpu_synchronize_post_init(cs); + return 8; + case S390_VIRT_PREFIX_REGNUM: + env->psa = ldq_be_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); + case S390_VIRT_KVM_PP_REGNUM: + env->pp = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; - case S390_VIRT_CPUTM_REGNUM: - env->cputm = ldtul_p(mem_buf); + case S390_VIRT_KVM_PFT_REGNUM: + env->pfault_token = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; - case S390_VIRT_BEA_REGNUM: - env->gbea = ldtul_p(mem_buf); + case S390_VIRT_KVM_PFS_REGNUM: + env->pfault_select = ldq_be_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: - env->pp = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_PFT_REGNUM: - env->pfault_token = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_PFS_REGNUM: - env->pfault_select = ldtul_p(mem_buf); - cpu_synchronize_post_init(env_cpu(env)); - return 8; - case S390_VIRT_PFC_REGNUM: - env->pfault_compare = ldtul_p(mem_buf); + case S390_VIRT_KVM_PFC_REGNUM: + env->pfault_compare = ldq_be_p(mem_buf); cpu_synchronize_post_init(env_cpu(env)); return 8; default: @@ -282,17 +324,21 @@ 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) { - env->gscb[n] = ldtul_p(mem_buf); + S390CPU *cpu = S390_CPU(cs); + CPUS390XState *env = &cpu->env; + + env->gscb[n] = ldq_be_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 1e3b7c0dc9..2b2bfc3736 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -576,6 +576,8 @@ static uint16_t full_GEN16_GA1[] = { S390_FEAT_RDP, S390_FEAT_PAI, S390_FEAT_PAIE, + S390_FEAT_UV_FEAT_AP, + S390_FEAT_UV_FEAT_AP_INTR, }; 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..1a8a76abb9 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,19 +350,19 @@ 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_1(per_check_exception, void, env) -DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_RWG, void, env, i64, i64) -DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_1(per_store_real, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_3(lra, i64, env, i64, i64) +DEF_HELPER_FLAGS_1(per_check_exception, TCG_CALL_NO_WG, void, env) +DEF_HELPER_FLAGS_3(per_branch, TCG_CALL_NO_WG, void, env, i64, i32) +DEF_HELPER_FLAGS_2(per_ifetch, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(per_store_real, TCG_CALL_NO_WG, noreturn, env, i32) DEF_HELPER_FLAGS_1(stfl, TCG_CALL_NO_RWG, void, env) DEF_HELPER_2(xsch, void, env, i64) diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index 053aaabb5a..a944f16c25 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, @@ -603,7 +603,7 @@ static int chsc_sei_nt2_have_event(void) #define CHSC_SEI_NT2 (1ULL << 61) static void ioinst_handle_chsc_sei(ChscReq *req, ChscResp *res) { - uint64_t selection_mask = ldq_p(&req->param1); + uint64_t selection_mask = ldq_be_p(&req->param1); uint8_t *res_flags = (uint8_t *)res->data; int have_event = 0; int have_more = 0; diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 3ac7ec9acf..8ffe0159d8 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -40,7 +40,7 @@ #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "sysemu/device_tree.h" -#include "exec/gdbstub.h" +#include "gdbstub/enums.h" #include "exec/ram_addr.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" @@ -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; @@ -250,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, @@ -275,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); } } @@ -289,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); } } @@ -340,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); @@ -370,6 +372,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) 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; @@ -463,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; } -int kvm_arch_put_registers(CPUState *cs, int level) +#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, Error **errp) { - 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++) { @@ -526,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; @@ -572,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; @@ -612,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) +int kvm_arch_get_registers(CPUState *cs, Error **errp) { - 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; @@ -635,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)) { @@ -690,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; @@ -912,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); } } @@ -995,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: @@ -1014,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); @@ -1229,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; @@ -1307,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; } @@ -1413,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); } @@ -1464,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; @@ -1481,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; } @@ -1511,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; } @@ -1531,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; } @@ -1654,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; } @@ -1684,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); @@ -1765,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: @@ -1919,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; } @@ -1966,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); @@ -1990,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; @@ -2150,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; } @@ -2296,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 }, @@ -2373,7 +2375,7 @@ bool kvm_s390_cpu_models_supported(void) KVM_S390_VM_CPU_MACHINE_SUBFUNC); } -void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) +bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) { struct kvm_s390_vm_cpu_machine prop = {}; struct kvm_device_attr attr = { @@ -2388,14 +2390,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) if (!kvm_s390_cpu_models_supported()) { error_setg(errp, "KVM doesn't support CPU models"); - return; + return false; } /* query the basic cpu model properties */ rc = kvm_vm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr); if (rc) { error_setg(errp, "KVM: Error querying host CPU model: %d", rc); - return; + return false; } cpu_type = cpuid_type(prop.cpuid); @@ -2418,13 +2420,13 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) rc = query_cpu_feat(model->features); if (rc) { error_setg(errp, "KVM: Error querying CPU features: %d", rc); - return; + return false; } /* get supported cpu subfunctions indicated via query / test bit */ rc = query_cpu_subfunc(model->features); if (rc) { error_setg(errp, "KVM: Error querying CPU subfunctions: %d", rc); - return; + return false; } /* PTFF subfunctions might be indicated although kernel support missing */ @@ -2456,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); @@ -2472,11 +2482,10 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) } if (!model->def) { error_setg(errp, "KVM: host CPU model could not be identified"); - return; + return false; } /* 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); } @@ -2491,9 +2500,37 @@ 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); + return true; +} + +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) @@ -2502,11 +2539,11 @@ static void kvm_s390_configure_apie(bool interpret) 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); } } -void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) +bool kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) { struct kvm_s390_vm_cpu_processor prop = { .fac_list = { 0 }, @@ -2523,11 +2560,11 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) if (kvm_s390_cmma_available()) { kvm_s390_enable_cmma(); } - return; + return true; } if (!kvm_s390_cpu_models_supported()) { error_setg(errp, "KVM doesn't support CPU models"); - return; + return false; } prop.cpuid = s390_cpuid_from_cpu_model(model); prop.ibc = s390_ibc_from_cpu_model(model); @@ -2537,28 +2574,36 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); if (rc) { error_setg(errp, "KVM: Error configuring the CPU model: %d", rc); - return; + return false; } /* configure cpu features indicated e.g. via SCLP */ rc = configure_cpu_feat(model->features); if (rc) { error_setg(errp, "KVM: Error configuring CPU features: %d", rc); - return; + return false; } /* configure cpu subfunctions indicated via query / test bit */ rc = configure_cpu_subfunc(model->features); if (rc) { error_setg(errp, "KVM: Error configuring CPU subfunctions: %d", rc); - return; + return false; } /* enable CMM via CMMA */ if (test_bit(S390_FEAT_CMM, model->features)) { 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 false; + } + return true; } void kvm_s390_restart_interrupt(S390CPU *cpu) @@ -2579,16 +2624,28 @@ void kvm_s390_stop_interrupt(S390CPU *cpu) kvm_s390_vcpu_interrupt(cpu, &irq); } -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 f9785564d0..649dae5948 100644 --- a/target/s390x/kvm/kvm_s390x.h +++ b/target/s390x/kvm/kvm_s390x.h @@ -47,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 aef52b6686..588a9aa737 100644 --- a/target/s390x/kvm/meson.build +++ b/target/s390x/kvm/meson.build @@ -1,6 +1,8 @@ s390x_ss.add(when: 'CONFIG_KVM', if_true: files( - 'kvm.c' + 'pv.c', + 'kvm.c', + 'stsi-topology.c' ), if_false: files( 'stubs.c' )) @@ -14,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..dde836d21a --- /dev/null +++ b/target/s390x/kvm/pv.c @@ -0,0 +1,383 @@ +/* + * 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); +} + +static int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + if (!object_dynamic_cast(OBJECT(cgs), TYPE_S390_PV_GUEST)) { + return 0; + } + + if (!kvm_enabled()) { + error_setg(errp, "Protected Virtualization requires KVM"); + return -1; + } + + 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) +{ + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + + klass->kvm_init = s390_pv_kvm_init; +} + +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 86% rename from include/hw/s390x/pv.h rename to target/s390x/kvm/pv.h index 9360aa1091..4b40817439 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) { @@ -41,7 +41,8 @@ static inline bool s390_is_pv(void) 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); @@ -60,7 +61,9 @@ 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; } @@ -77,18 +80,4 @@ static inline int kvm_s390_dump_mem_state(uint64_t addr, size_t len, static inline int kvm_s390_dump_completion_data(void *buff) { return 0; } #endif /* CONFIG_KVM */ -int s390_pv_kvm_init(ConfidentialGuestSupport *cgs, Error **errp); -static inline int s390_pv_init(ConfidentialGuestSupport *cgs, Error **errp) -{ - if (!cgs) { - return 0; - } - if (kvm_enabled()) { - return s390_pv_kvm_init(cgs, errp); - } - - error_setg(errp, "Protected Virtualization requires KVM"); - return -1; -} - #endif /* HW_S390_PV_H */ 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/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..6c59d0d216 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -24,7 +24,7 @@ #include "sysemu/kvm.h" #include "sysemu/tcg.h" #include "exec/exec-all.h" -#include "trace.h" +#include "exec/page-protection.h" #include "hw/hw.h" #include "hw/s390x/storage-keys.h" #include "hw/boards.h" @@ -302,7 +302,6 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) static S390SKeysClass *skeyclass; static S390SKeysState *ss; uint8_t key, old_key; - int rc; /* * We expect to be called with an absolute address that has already been @@ -340,9 +339,7 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) * * TODO: we have races between getting and setting the key. */ - rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_get_skeys_nonzero(rc); + if (s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key)) { return; } old_key = key; @@ -370,10 +367,7 @@ static void mmu_handle_skey(target_ulong addr, int rw, int *flags) key |= SK_R; if (key != old_key) { - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_set_skeys_nonzero(rc); - } + s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key); } } @@ -417,7 +411,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/sigp.c b/target/s390x/sigp.c index 9dd977349a..08aaecf12b 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" +#include "hw/boards.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" #include "exec/address-spaces.h" @@ -250,24 +251,20 @@ static void sigp_restart(CPUState *cs, run_on_cpu_data arg) static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg) { - S390CPU *cpu = S390_CPU(cs); - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); SigpInfo *si = arg.host_ptr; cpu_synchronize_state(cs); - scc->reset(cs, S390_CPU_RESET_INITIAL); + resettable_reset(OBJECT(cs), RESET_TYPE_S390_CPU_INITIAL); cpu_synchronize_post_reset(cs); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg) { - S390CPU *cpu = S390_CPU(cs); - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); SigpInfo *si = arg.host_ptr; cpu_synchronize_state(cs); - scc->reset(cs, S390_CPU_RESET_NORMAL); + resettable_reset(OBJECT(cs), RESET_TYPE_S390_CPU_NORMAL); cpu_synchronize_post_reset(cs); si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; } @@ -435,6 +432,22 @@ static int sigp_set_architecture(S390CPU *cpu, uint32_t param, return SIGP_CC_STATUS_STORED; } +S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) +{ + static MachineState *ms; + + if (!ms) { + ms = MACHINE(qdev_get_machine()); + g_assert(ms->possible_cpus); + } + + /* CPU address corresponds to the core_id and the index */ + if (cpu_addr >= ms->possible_cpus->len) { + return NULL; + } + return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu); +} + int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3) { uint64_t *status_reg = &env->regs[r1]; diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 762b277884..93aabd236f 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -13,7 +13,6 @@ */ #include "qemu/osdep.h" -#include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "s390x-internal.h" #include "tcg_s390x.h" diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index fe02d82201..4c0b692c9e 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 @@ -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 already handled */ + 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)); @@ -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 be80b2373c..5041c13962 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! @@ -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); @@ -791,7 +780,7 @@ uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status); + float32 ret = float32_muladd(f3, f2, f1, 0, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; } @@ -800,7 +789,7 @@ uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status); + float64 ret = float64_muladd(f3, f2, f1, 0, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; } @@ -809,7 +798,7 @@ uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c, + float32 ret = float32_muladd(f3, f2, f1, float_muladd_negate_c, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; @@ -819,7 +808,7 @@ uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { - float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c, + float64 ret = float64_muladd(f3, f2, f1, float_muladd_negate_c, &env->fpu_status); handle_exceptions(env, false, GETPC()); return ret; @@ -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.h.inc b/target/s390x/tcg/insn-data.h.inc index 4249632af3..e7d61cdec2 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -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 */ @@ -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) @@ -293,37 +293,42 @@ 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,12 +461,12 @@ 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) @@ -483,7 +488,7 @@ 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, new, r1_32, lcbb, 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) @@ -570,14 +575,14 @@ 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(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 */ 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 7e7de5e2f1..0e12dae2aa 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -25,16 +25,23 @@ #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" +#include "hw/core/tcg-cpu-ops.h" #include "qemu/int128.h" #include "qemu/atomic128.h" -#include "trace.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/storage-keys.h" #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,23 +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); - 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, @@ -162,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, @@ -171,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. */ @@ -223,111 +225,66 @@ static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr, uint8_t byte, uint16_t size, int mmu_idx, 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)) { + if (user_or_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 } static void access_memset(CPUS390XState *env, S390Access *desta, uint8_t byte, uintptr_t ra) { - + set_helper_retaddr(ra); do_access_memset(env, desta->vaddr1, desta->haddr1, byte, desta->size1, desta->mmu_idx, ra); - if (likely(!desta->size2)) { - return; + if (unlikely(desta->size2)) { + do_access_memset(env, desta->vaddr2, desta->haddr2, byte, + desta->size2, desta->mmu_idx, ra); } - do_access_memset(env, desta->vaddr2, desta->haddr2, byte, desta->size2, - 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 + clear_helper_retaddr(); } 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); } } @@ -338,41 +295,40 @@ static void access_set_byte(CPUS390XState *env, S390Access *access, static void access_memmove(CPUS390XState *env, S390Access *desta, S390Access *srca, uintptr_t ra) { - int diff; + int len = desta->size1 + desta->size2; - 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; + if (user_or_likely(desta->haddr1 && + srca->haddr1 && + (!desta->size2 || desta->haddr2) && + (!srca->size2 || srca->haddr2))) { + int diff = desta->size1 - srca->size1; - for (i = 0; i < desta->size1 + desta->size2; i++) { - uint8_t byte = access_get_byte(env, srca, i, ra); - - access_set_byte(env, desta, i, byte, ra); - } - return; - } - - if (srca->size1 == desta->size1) { - 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; - 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); + if (likely(diff == 0)) { + memmove(desta->haddr1, srca->haddr1, srca->size1); + if (unlikely(srca->size2)) { + memmove(desta->haddr2, srca->haddr2, srca->size2); + } + } 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 = -diff; + memmove(desta->haddr1, srca->haddr1, desta->size1); + memmove(desta->haddr2, srca->haddr1 + desta->size1, diff); + if (likely(srca->size2)) { + memmove(desta->haddr2 + diff, srca->haddr2, srca->size2); + } } } else { - diff = srca->size1 - desta->size1; - memmove(desta->haddr1, srca->haddr1, desta->size1); - memmove(desta->haddr2, srca->haddr1 + desta->size1, diff); - if (likely(srca->size2)) { - memmove(desta->haddr2 + diff, srca->haddr2, srca->size2); + for (int i = 0; i < len; i++) { + uint8_t byte = access_get_byte(env, srca, i, ra); + access_set_byte(env, desta, i, byte, ra); } } } @@ -396,7 +352,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; @@ -407,9 +363,11 @@ 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); + set_helper_retaddr(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); @@ -417,6 +375,8 @@ static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest, c |= x; access_set_byte(env, &desta, i, x, ra); } + + clear_helper_retaddr(); return c != 0; } @@ -430,7 +390,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; @@ -441,9 +401,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) { @@ -451,6 +411,7 @@ static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, return 0; } + set_helper_retaddr(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); @@ -458,6 +419,7 @@ static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest, c |= x; access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); return c != 0; } @@ -471,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; @@ -482,9 +444,11 @@ 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); + set_helper_retaddr(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); @@ -492,6 +456,8 @@ static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest, c |= x; access_set_byte(env, &desta, i, x, ra); } + + clear_helper_retaddr(); return c != 0; } @@ -505,7 +471,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; @@ -515,8 +481,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 @@ -528,11 +494,13 @@ static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest, } else if (!is_destructive_overlap(env, dest, src, l)) { access_memmove(env, &desta, &srca, ra); } else { + set_helper_retaddr(ra); for (i = 0; i < l; i++) { uint8_t byte = access_get_byte(env, &srca, i, ra); access_set_byte(env, &desta, i, byte, ra); } + clear_helper_retaddr(); } return env->cc_op; @@ -546,27 +514,30 @@ 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); + set_helper_retaddr(ra); for (i = l - 1; i >= 0; i--) { uint8_t byte = access_get_byte(env, &srca, i, ra); access_set_byte(env, &desta, i, byte, ra); } + clear_helper_retaddr(); } /* 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; @@ -575,19 +546,21 @@ 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); + + set_helper_retaddr(ra); for (i = 0; i < l; i++) { const uint8_t x = access_get_byte(env, &srca, l - i - 1, ra); - access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); } /* 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; @@ -595,21 +568,24 @@ 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); + + set_helper_retaddr(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); access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); } /* 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; @@ -618,11 +594,13 @@ 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); + + set_helper_retaddr(ra); byte_src = access_get_byte(env, &srca, len_src - 1, ra); byte_dest = (byte_dest & 0x0f) | (byte_src << 4); access_set_byte(env, &desta, len_dest - 1, byte_dest, ra); @@ -638,12 +616,13 @@ void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) byte_dest |= byte_src << 4; access_set_byte(env, &desta, i, byte_dest, ra); } + clear_helper_retaddr(); } /* 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; @@ -651,15 +630,18 @@ 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); + + set_helper_retaddr(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); access_set_byte(env, &desta, i, x, ra); } + clear_helper_retaddr(); } /* compare unsigned byte arrays */ @@ -704,6 +686,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); @@ -886,7 +873,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; @@ -904,23 +891,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 */ @@ -928,7 +912,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); @@ -981,7 +965,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]; @@ -1000,17 +984,21 @@ 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); + + set_helper_retaddr(ra); for (i = 0; i < len; i++) { const uint8_t v = access_get_byte(env, &srca, i, ra); access_set_byte(env, &desta, i, v, ra); if (v == c) { + clear_helper_retaddr(); set_address_zero(env, r1, d + i); return 1; } } + clear_helper_retaddr(); set_address_zero(env, r1, d + len); set_address_zero(env, r2, s + len); return 3; @@ -1062,7 +1050,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; @@ -1088,19 +1076,20 @@ 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); + set_helper_retaddr(ra); /* The remaining length selects the padding byte. */ for (i = 0; i < len; (*destlen)--, i++) { @@ -1110,6 +1099,7 @@ static inline uint32_t do_mvcl(CPUS390XState *env, access_set_byte(env, &desta, i, pad >> 8, ra); } } + clear_helper_retaddr(); *dest = wrap_address(env, *dest + len); } @@ -1119,7 +1109,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); @@ -1156,16 +1146,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; @@ -1353,8 +1343,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; @@ -1395,8 +1385,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) @@ -1636,8 +1625,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; @@ -1672,8 +1661,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, @@ -1776,62 +1764,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); @@ -1870,34 +1811,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); @@ -1906,21 +1843,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; @@ -1929,27 +1865,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. */ @@ -1971,29 +1899,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(); @@ -2199,9 +2117,8 @@ uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) } } - rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + rc = s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (rc) { - trace_get_skeys_nonzero(rc); return 0; } return key; @@ -2214,7 +2131,6 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) static S390SKeysClass *skeyclass; uint64_t addr = wrap_address(env, r2); uint8_t key; - int rc; addr = mmu_real2abs(env, addr); if (!mmu_absolute_addr_valid(addr, false)) { @@ -2230,10 +2146,7 @@ void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) } key = r1 & 0xfe; - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); - if (rc) { - trace_set_skeys_nonzero(rc); - } + s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key); /* * As we can only flush by virtual address and not all the entries * that point to a physical address we have to flush the whole TLB. @@ -2263,18 +2176,16 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) } } - rc = skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + rc = s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (rc) { - trace_get_skeys_nonzero(rc); return 0; } re = key & (SK_R | SK_C); key &= ~SK_R; - rc = skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); + rc = s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key); if (rc) { - trace_set_skeys_nonzero(rc); return 0; } /* @@ -2295,7 +2206,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; @@ -2310,6 +2222,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 */ @@ -2319,14 +2235,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; @@ -2341,6 +2257,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 */ @@ -2349,10 +2269,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; } @@ -2461,7 +2379,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; @@ -2475,7 +2393,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; @@ -2486,67 +2404,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. @@ -2556,8 +2413,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; @@ -2694,10 +2559,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); } @@ -3018,12 +2885,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 71388a7119..303f86d363 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -20,10 +20,9 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "qemu/main-loop.h" +#include "qemu/log.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 +102,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 +118,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 +128,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: @@ -187,7 +186,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 */ @@ -209,16 +208,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 */ @@ -231,9 +228,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; } @@ -423,9 +420,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; } @@ -435,92 +432,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) @@ -535,10 +532,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; } @@ -556,7 +553,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; @@ -572,32 +569,46 @@ 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 #ifndef CONFIG_USER_ONLY +static G_NORETURN void per_raise_exception(CPUS390XState *env) +{ + trigger_pgm_exception(env, PGM_PER); + cpu_loop_exit(env_cpu(env)); +} + +static G_NORETURN void per_raise_exception_log(CPUS390XState *env) +{ + qemu_log_mask(CPU_LOG_INT, "PER interrupt after 0x%" PRIx64 "\n", + env->per_address); + per_raise_exception(env); +} + void HELPER(per_check_exception)(CPUS390XState *env) { - if (env->per_perc_atmid) { - tcg_s390_program_interrupt(env, PGM_PER, GETPC()); + /* psw_addr, per_address and int_pgm_ilen are already set. */ + if (unlikely(env->per_perc_atmid)) { + per_raise_exception_log(env); } } @@ -612,46 +623,45 @@ static inline bool get_per_in_range(CPUS390XState *env, uint64_t addr) } } -void HELPER(per_branch)(CPUS390XState *env, uint64_t from, uint64_t to) +void HELPER(per_branch)(CPUS390XState *env, uint64_t dest, uint32_t ilen) { - if ((env->cregs[9] & PER_CR9_EVENT_BRANCH)) { - if (!(env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) - || get_per_in_range(env, to)) { - env->per_address = from; - env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); - } + if ((env->cregs[9] & PER_CR9_CONTROL_BRANCH_ADDRESS) + && !get_per_in_range(env, dest)) { + return; } + + env->psw.addr = dest; + env->int_pgm_ilen = ilen; + env->per_address = env->gbea; + env->per_perc_atmid = PER_CODE_EVENT_BRANCH | get_per_atmid(env); + per_raise_exception_log(env); } -void HELPER(per_ifetch)(CPUS390XState *env, uint64_t addr) +void HELPER(per_ifetch)(CPUS390XState *env, uint32_t ilen) { - if ((env->cregs[9] & PER_CR9_EVENT_IFETCH) && get_per_in_range(env, addr)) { - env->per_address = addr; + if (get_per_in_range(env, env->psw.addr)) { + env->per_address = env->psw.addr; + env->int_pgm_ilen = ilen; env->per_perc_atmid = PER_CODE_EVENT_IFETCH | get_per_atmid(env); /* If the instruction has to be nullified, trigger the exception immediately. */ - if (env->cregs[9] & PER_CR9_EVENT_NULLIFICATION) { - CPUState *cs = env_cpu(env); - + if (env->cregs[9] & PER_CR9_EVENT_IFETCH_NULLIFICATION) { env->per_perc_atmid |= PER_CODE_EVENT_NULLIFICATION; - env->int_pgm_code = PGM_PER; - env->int_pgm_ilen = get_ilen(cpu_ldub_code(env, addr)); - - cs->exception_index = EXCP_PGM; - cpu_loop_exit(cs); + qemu_log_mask(CPU_LOG_INT, "PER interrupt before 0x%" PRIx64 "\n", + env->per_address); + per_raise_exception(env); } } } -void HELPER(per_store_real)(CPUS390XState *env) +void HELPER(per_store_real)(CPUS390XState *env, uint32_t ilen) { - if ((env->cregs[9] & PER_CR9_EVENT_STORE) && - (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { - /* PSW is saved just before calling the helper. */ - env->per_address = env->psw.addr; - env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); - } + /* PSW is saved just before calling the helper. */ + env->per_address = env->psw.addr; + env->int_pgm_ilen = ilen; + env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); + per_raise_exception_log(env); } #endif @@ -728,27 +738,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, @@ -756,18 +766,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()); @@ -778,9 +789,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, @@ -788,9 +799,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, @@ -798,8 +809,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 0885bf2641..bcfff40b25 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -31,14 +31,11 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "disas/disas.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "qemu/log.h" #include "qemu/host-utils.h" -#include "exec/cpu_ldst.h" -#include "exec/gen-icount.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -46,6 +43,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 +139,6 @@ struct DisasFields { struct DisasContext { DisasContextBase base; const DisasInsn *insn; - TCGOp *insn_start; DisasFields fields; uint64_t ex_value; /* @@ -156,8 +156,6 @@ struct DisasContext { 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; @@ -171,8 +169,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); @@ -181,9 +177,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; @@ -202,28 +196,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]); } @@ -293,7 +287,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; } @@ -301,7 +295,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; } @@ -312,7 +316,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) @@ -328,12 +332,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) @@ -342,37 +341,11 @@ static void update_psw_addr(DisasContext *s) tcg_gen_movi_i64(psw_addr, s->base.pc_next); } -static void per_branch(DisasContext *s, bool to_next) +static void per_branch(DisasContext *s, TCGv_i64 dest) { #ifndef CONFIG_USER_ONLY - 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); - } - } -#endif -} - -static void per_branch_cond(DisasContext *s, TCGCond cond, - TCGv_i64 arg1, TCGv_i64 arg2) -{ -#ifndef CONFIG_USER_ONLY - if (s->base.tb->flags & FLAG_MASK_PER) { - TCGLabel *lab = gen_new_label(); - 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_set_label(lab); - } else { - TCGv_i64 pc = tcg_const_i64(s->base.pc_next); - tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); - tcg_temp_free_i64(pc); + if (s->base.tb->flags & FLAG_MASK_PER_BRANCH) { + gen_helper_per_branch(tcg_env, dest, tcg_constant_i32(s->ilen)); } #endif } @@ -418,31 +391,24 @@ static int get_mem_index(DisasContext *s) case PSW_ASC_HOME >> FLAG_MASK_PSW_SHIFT: return MMU_HOME_IDX; default: - tcg_abort(); - break; + g_assert_not_reached(); } #endif } 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. */ - tmp = tcg_const_i32(code); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUS390XState, int_pgm_code)); - tcg_temp_free_i32(tmp); + tcg_gen_st_i32(tcg_constant_i32(code), tcg_env, + offsetof(CPUS390XState, int_pgm_code)); - 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); @@ -461,9 +427,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) @@ -584,13 +548,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: @@ -613,6 +577,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: @@ -627,7 +594,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: @@ -643,28 +610,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 */ @@ -673,9 +633,6 @@ static void gen_op_calc_cc(DisasContext *s) static bool use_goto_tb(DisasContext *s, uint64_t dest) { - if (unlikely(s->base.tb->flags & FLAG_MASK_PER)) { - return false; - } return translator_use_goto_tb(&s->base, dest); } @@ -730,7 +687,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; } @@ -769,10 +725,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; @@ -783,11 +739,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; @@ -847,13 +803,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: @@ -868,29 +823,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 */ @@ -908,71 +856,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; @@ -982,24 +904,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, @@ -1100,9 +1004,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 @@ -1170,153 +1074,105 @@ struct DisasInsn { static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { + update_cc_op(s); + per_breaking_event(s); + per_branch(s, tcg_constant_i64(dest)); + if (dest == s->pc_tmp) { - per_branch(s, true); return DISAS_NEXT; } if (use_goto_tb(s, dest)) { - update_cc_op(s); - per_breaking_event(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); tcg_gen_exit_tb(s->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(psw_addr, dest); - per_branch(s, false); - return DISAS_PC_UPDATED; + return DISAS_PC_CC_UPDATED; } } +static DisasJumpType help_goto_indirect(DisasContext *s, TCGv_i64 dest) +{ + update_cc_op(s); + per_breaking_event(s); + tcg_gen_mov_i64(psw_addr, dest); + per_branch(s, psw_addr); + return DISAS_PC_CC_UPDATED; +} + static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, bool is_imm, int imm, TCGv_i64 cdest) { - DisasJumpType ret; uint64_t dest = s->base.pc_next + (int64_t)imm * 2; TCGLabel *lab; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { - ret = DISAS_NEXT; - goto egress; + return DISAS_NEXT; } if (is_imm) { - if (dest == s->pc_tmp) { - /* Branch to next. */ - per_branch(s, true); - ret = DISAS_NEXT; - goto egress; - } - if (c->cond == TCG_COND_ALWAYS) { - ret = help_goto_direct(s, dest); - goto egress; + /* + * Do not optimize a conditional branch if PER enabled, because we + * still need a conditional call to helper_per_branch. + */ + if (c->cond == TCG_COND_ALWAYS + || (dest == s->pc_tmp && + !(s->base.tb->flags & FLAG_MASK_PER_BRANCH))) { + return help_goto_direct(s, dest); } } else { if (!cdest) { /* E.g. bcr %r0 -> no branch. */ - ret = DISAS_NEXT; - goto egress; + return DISAS_NEXT; } if (c->cond == TCG_COND_ALWAYS) { - tcg_gen_mov_i64(psw_addr, cdest); - per_branch(s, false); - ret = DISAS_PC_UPDATED; - goto egress; + return help_goto_indirect(s, cdest); } } - if (use_goto_tb(s, s->pc_tmp)) { - if (is_imm && use_goto_tb(s, dest)) { - /* Both exits can use goto_tb. */ - update_cc_op(s); + update_cc_op(s); - lab = gen_new_label(); - if (c->is_64) { - tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); - } else { - tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); - } - - /* Branch not taken. */ - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb(s->base.tb, 0); - - /* Branch taken. */ - gen_set_label(lab); - per_breaking_event(s); - tcg_gen_goto_tb(1); - tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb(s->base.tb, 1); - - ret = DISAS_NORETURN; - } else { - /* Fallthru can use goto_tb, but taken branch cannot. */ - /* Store taken branch destination before the brcond. This - avoids having to allocate a new local temp to hold it. - We'll overwrite this in the not taken case anyway. */ - if (!is_imm) { - tcg_gen_mov_i64(psw_addr, cdest); - } - - lab = gen_new_label(); - if (c->is_64) { - tcg_gen_brcond_i64(c->cond, c->u.s64.a, c->u.s64.b, lab); - } else { - tcg_gen_brcond_i32(c->cond, c->u.s32.a, c->u.s32.b, lab); - } - - /* Branch not taken. */ - update_cc_op(s); - tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->pc_tmp); - tcg_gen_exit_tb(s->base.tb, 0); - - gen_set_label(lab); - if (is_imm) { - tcg_gen_movi_i64(psw_addr, dest); - } - per_breaking_event(s); - ret = DISAS_PC_UPDATED; - } + /* + * Ensure the taken branch is fall-through of the tcg branch. + * This keeps @cdest usage within the extended basic block, + * which avoids an otherwise unnecessary spill to the stack. + */ + lab = gen_new_label(); + if (c->is_64) { + tcg_gen_brcond_i64(tcg_invert_cond(c->cond), + c->u.s64.a, c->u.s64.b, lab); } else { - /* Fallthru cannot use goto_tb. This by itself is vanishingly rare. - 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); - if (is_imm) { - cdest = tcg_const_i64(dest); - } - - if (c->is_64) { - tcg_gen_movcond_i64(c->cond, psw_addr, c->u.s64.a, c->u.s64.b, - cdest, next); - per_branch_cond(s, c->cond, c->u.s64.a, c->u.s64.b); - } else { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 z = tcg_const_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; + tcg_gen_brcond_i32(tcg_invert_cond(c->cond), + c->u.s32.a, c->u.s32.b, lab); } - egress: - free_compare(c); - return ret; + /* Branch taken. */ + per_breaking_event(s); + if (is_imm) { + tcg_gen_movi_i64(psw_addr, dest); + } else { + tcg_gen_mov_i64(psw_addr, cdest); + } + per_branch(s, psw_addr); + + if (is_imm && use_goto_tb(s, dest)) { + tcg_gen_goto_tb(0); + tcg_gen_exit_tb(s->base.tb, 0); + } else { + tcg_gen_lookup_and_goto_ptr(); + } + + gen_set_label(lab); + + /* Branch not taken. */ + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + if (use_goto_tb(s, s->pc_tmp)) { + tcg_gen_goto_tb(1); + tcg_gen_exit_tb(s->base.tb, 1); + return DISAS_NORETURN; + } + return DISAS_PC_CC_UPDATED; } /* ====================================================================== */ @@ -1394,10 +1250,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; } @@ -1449,20 +1304,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; } @@ -1477,11 +1331,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); @@ -1544,9 +1398,7 @@ static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { pc_to_link_info(o->out, s, s->pc_tmp); if (o->in2) { - tcg_gen_mov_i64(psw_addr, o->in2); - per_branch(s, false); - return DISAS_PC_UPDATED; + return help_goto_indirect(s, o->in2); } else { return DISAS_NEXT; } @@ -1570,16 +1422,13 @@ 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) { save_link_info(s, o); if (o->in2) { - tcg_gen_mov_i64(psw_addr, o->in2); - per_branch(s, false); - return DISAS_PC_UPDATED; + return help_goto_indirect(s, o->in2); } else { return DISAS_NEXT; } @@ -1599,7 +1448,7 @@ static DisasJumpType op_bal(DisasContext *s, DisasOps *o) if (have_field(s, ri)) { \ if (unlikely(s->ex_value)) { \ cdest = tcg_temp_new_i64(); \ - tcg_gen_ld_i64(cdest, cpu_env, offsetof(CPUS390XState, ex_target));\ + 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 { \ @@ -1661,16 +1510,13 @@ static DisasJumpType op_bct32(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_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); @@ -1685,17 +1531,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); } @@ -1709,12 +1552,10 @@ static DisasJumpType op_bct64(DisasContext *s, DisasOps *o) 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); @@ -1731,8 +1572,6 @@ static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) 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]); @@ -1741,7 +1580,6 @@ 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); @@ -1760,15 +1598,12 @@ static DisasJumpType op_bx64(DisasContext *s, DisasOps *o) 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); @@ -1784,7 +1619,7 @@ 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; @@ -1801,21 +1636,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; } @@ -1842,7 +1677,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) @@ -1852,8 +1687,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; } @@ -1865,8 +1699,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; } @@ -1878,8 +1711,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; } @@ -1891,8 +1723,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; } @@ -1904,8 +1735,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; } @@ -1917,8 +1747,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; } @@ -1930,8 +1759,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; } @@ -1943,8 +1771,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; } @@ -1956,8 +1783,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; } @@ -1969,8 +1795,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; } @@ -1982,8 +1807,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; } @@ -1995,8 +1819,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; } @@ -2008,8 +1831,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; } @@ -2020,8 +1842,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; } @@ -2032,9 +1853,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; } @@ -2045,8 +1864,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; } @@ -2057,8 +1875,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; } @@ -2069,24 +1886,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; } @@ -2094,34 +1909,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) @@ -2136,11 +1945,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; } @@ -2157,11 +1964,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; } @@ -2178,32 +1983,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; } @@ -2213,7 +2018,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; } @@ -2229,14 +2033,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; @@ -2245,44 +2047,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; @@ -2303,7 +2098,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(); @@ -2317,31 +2111,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; } @@ -2380,36 +2194,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; } @@ -2417,70 +2228,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; } @@ -2493,7 +2303,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; } @@ -2502,16 +2312,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; } @@ -2531,18 +2343,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; } @@ -2554,8 +2361,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; } @@ -2566,8 +2372,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; } @@ -2578,9 +2383,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; } @@ -2614,7 +2417,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; @@ -2622,7 +2425,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; @@ -2631,7 +2434,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; @@ -2641,13 +2444,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; @@ -2660,7 +2469,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; } @@ -2683,8 +2491,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; } @@ -2694,12 +2500,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; } @@ -2708,18 +2513,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 @@ -2769,51 +2573,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; @@ -2849,7 +2664,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; } @@ -2860,8 +2675,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; } @@ -2872,8 +2686,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; } @@ -2884,22 +2697,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; } @@ -2917,43 +2727,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; } @@ -2971,7 +2784,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); @@ -2993,7 +2806,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); @@ -3027,22 +2841,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; @@ -3051,11 +2860,10 @@ 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. */ s->exit_to_mainloop = true; return DISAS_TOO_MANY; @@ -3063,11 +2871,10 @@ static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) { - TCGv_i32 r1 = tcg_const_i32(get_field(s, 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. */ s->exit_to_mainloop = true; return DISAS_TOO_MANY; @@ -3075,34 +2882,34 @@ static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) 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; } @@ -3117,21 +2924,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; } @@ -3144,25 +2948,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; } @@ -3172,12 +2973,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; } @@ -3190,25 +2988,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; } @@ -3218,12 +3013,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; } @@ -3235,7 +3027,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; } @@ -3243,15 +3035,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; } @@ -3261,10 +3051,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; } @@ -3286,8 +3074,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); @@ -3296,15 +3082,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; } @@ -3341,20 +3121,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; @@ -3363,9 +3139,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; } @@ -3373,11 +3147,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: @@ -3388,7 +3161,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); } @@ -3398,9 +3171,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; } @@ -3408,33 +3179,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; } @@ -3450,11 +3218,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; } @@ -3471,11 +3237,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; } @@ -3492,11 +3256,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; } @@ -3504,7 +3266,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; } @@ -3513,7 +3275,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; } @@ -3521,7 +3284,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; } @@ -3529,49 +3293,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; } @@ -3595,77 +3355,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; } @@ -3690,9 +3442,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; } @@ -3724,9 +3476,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; } @@ -3742,10 +3494,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); @@ -3776,9 +3528,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; } @@ -3792,9 +3544,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; } @@ -3808,9 +3559,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; } @@ -3829,7 +3579,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 @@ -3927,12 +3677,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; @@ -4002,9 +3755,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; } @@ -4017,14 +3767,14 @@ 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_TOO_MANY; } @@ -4057,9 +3807,8 @@ 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_TOO_MANY; @@ -4068,64 +3817,61 @@ static DisasJumpType op_sam(DisasContext *s, DisasOps *o) 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 @@ -4149,27 +3895,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; @@ -4186,9 +3929,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. */ @@ -4218,13 +3958,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; } @@ -4232,7 +3972,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; } @@ -4240,7 +3980,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; } @@ -4252,11 +3992,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; } @@ -4289,16 +4027,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; } @@ -4312,13 +4048,27 @@ 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. */ s->exit_to_mainloop = true; return DISAS_TOO_MANY; @@ -4326,14 +4076,14 @@ static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) 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; @@ -4344,9 +4094,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 @@ -4355,12 +4105,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; @@ -4369,137 +4116,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; } @@ -4518,49 +4263,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; } @@ -4575,8 +4320,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, @@ -4585,6 +4329,8 @@ 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. */ s->exit_to_mainloop = true; return DISAS_TOO_MANY; @@ -4594,9 +4340,11 @@ static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st_tl(o->in1, o->in2, MMU_REAL_IDX, s->insn->data); - if (s->base.tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER_STORE_REAL) { + update_cc_op(s); update_psw_addr(s); - gen_helper_per_store_real(cpu_env); + gen_helper_per_store_real(tcg_env, tcg_constant_i32(s->ilen)); + return DISAS_NORETURN; } return DISAS_NEXT; } @@ -4604,42 +4352,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; } @@ -4654,7 +4403,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: @@ -4662,7 +4411,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: @@ -4671,7 +4420,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: @@ -4680,7 +4429,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; @@ -4688,7 +4437,6 @@ static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) } break; } - tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -4697,14 +4445,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; } @@ -4712,7 +4457,6 @@ static DisasJumpType op_stm(DisasContext *s, DisasOps *o) r1 = (r1 + 1) & 15; } - tcg_temp_free_i64(tsize); return DISAS_NEXT; } @@ -4721,60 +4465,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; } @@ -4832,10 +4563,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; } @@ -4847,13 +4577,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; @@ -4871,21 +4599,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; } @@ -4894,14 +4622,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; } @@ -4910,53 +4638,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); @@ -4973,31 +4703,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; } @@ -5011,9 +4738,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; } @@ -5028,9 +4754,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; } @@ -5049,32 +4774,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; @@ -5082,9 +4807,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; } @@ -5100,10 +4824,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); @@ -5134,105 +4858,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; } @@ -5415,10 +5124,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 @@ -5427,18 +5141,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 @@ -5498,10 +5203,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); @@ -5533,11 +5244,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)) { @@ -5556,13 +5282,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 @@ -5576,7 +5302,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 @@ -5590,7 +5316,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 @@ -5604,7 +5330,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 @@ -5632,7 +5358,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 @@ -5666,7 +5391,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 @@ -5721,7 +5445,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 @@ -5766,6 +5489,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) { @@ -5796,7 +5525,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 @@ -5804,7 +5533,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 @@ -5812,7 +5541,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 @@ -5820,7 +5549,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 @@ -5828,7 +5557,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 @@ -5836,7 +5565,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 @@ -5846,7 +5575,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 @@ -5881,7 +5609,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 @@ -5928,6 +5655,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(); @@ -5975,6 +5710,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) { @@ -5992,6 +5733,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; @@ -6007,7 +5754,7 @@ static TCGv gen_ri2(DisasContext *s) disas_jdest(s, i2, is_imm, imm, ri2); if (is_imm) { - ri2 = tcg_constant_i64(s->base.pc_next + imm * 2); + ri2 = tcg_constant_i64(s->base.pc_next + (int64_t)imm * 2); } return ri2; @@ -6025,7 +5772,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); @@ -6036,35 +5783,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 @@ -6080,14 +5827,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 @@ -6104,80 +5851,83 @@ static void in2_m2_64a(DisasContext *s, DisasOps *o) static void in2_mri2_16s(DisasContext *s, DisasOps *o) { o->in2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16s(o->in2, gen_ri2(s), get_mem_index(s)); + 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) { o->in2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld16u(o->in2, gen_ri2(s), get_mem_index(s)); + 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) { o->in2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32s(o->in2, gen_ri2(s), get_mem_index(s)); + 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) { o->in2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(o->in2, gen_ri2(s), get_mem_index(s)); + 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) { o->in2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(o->in2, gen_ri2(s), get_mem_index(s)); + 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 @@ -6373,21 +6123,20 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s) const DisasInsn *info; if (unlikely(s->ex_value)) { + uint64_t be_insn; + /* Drop the EX data now, so that it's clear on exception paths. */ - TCGv_i64 zero = tcg_const_i64(0); - int i; - 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 (i = 0; i < ilen; i++) { - uint8_t byte = extract64(insn, 56 - (i * 8), 8); - translator_fake_ldb(byte, pc + i); - } op = insn >> 56; + + /* Register insn bytes with translator so plugins work. */ + be_insn = cpu_to_be64(insn); + translator_fake_ld(&s->base, &be_insn, get_ilen(op)); } else { insn = ld_code2(env, s, pc); op = (insn >> 8) & 0xff; @@ -6495,7 +6244,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) { @@ -6507,10 +6256,9 @@ 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); + if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { + /* With ifetch set, psw_addr and cc_op are always up-to-date. */ + gen_helper_per_ifetch(tcg_env, tcg_constant_i32(s->ilen)); } #endif @@ -6563,10 +6311,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); } } @@ -6595,31 +6340,15 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } if (insn->help_op) { ret = insn->help_op(s, &o); - } - if (ret != DISAS_NORETURN) { - if (insn->help_wout) { - insn->help_wout(s, &o); - } - if (insn->help_cout) { - insn->help_cout(s, &o); + if (ret == DISAS_NORETURN) { + goto out; } } - - /* Free any temporaries created by the helpers. */ - if (o.out && !o.g_out) { - tcg_temp_free_i64(o.out); + if (insn->help_wout) { + insn->help_wout(s, &o); } - 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); + if (insn->help_cout) { + insn->help_cout(s, &o); } /* io should be the last instruction in tb when icount is enabled */ @@ -6628,14 +6357,19 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #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_TOO_MANY) { + if (s->base.tb->flags & FLAG_MASK_PER_IFETCH) { + switch (ret) { + case DISAS_TOO_MANY: + s->base.is_jmp = DISAS_PC_CC_UPDATED; + /* fall through */ + case DISAS_NEXT: tcg_gen_movi_i64(psw_addr, s->pc_tmp); + break; + default: + break; } - - /* Call the helper to check for a possible PER exception. */ - gen_helper_per_check_exception(cpu_env); + update_cc_op(s); + gen_helper_per_check_exception(tcg_env); } #endif @@ -6657,7 +6391,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; + dc->exit_to_mainloop = dc->ex_value; } static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -6670,20 +6404,19 @@ 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); + uint64_t insn = translator_lduw(env, &s->base, 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); @@ -6724,18 +6457,18 @@ static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void s390x_tr_disas_log(const DisasContextBase *dcbase, +static bool s390x_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs, FILE *logfile) { DisasContext *dc = container_of(dcbase, DisasContext, base); if (unlikely(dc->ex_value)) { - /* ??? Unfortunately target_disas can't use host memory. */ - fprintf(logfile, "IN: EXECUTE %016" PRIx64, dc->ex_value); - } else { - fprintf(logfile, "IN: %s\n", lookup_symbol(dc->base.pc_first)); - target_disas(logfile, cs, dc->base.pc_first, dc->base.tb->size); + /* The ex_value has been recorded with translator_fake_ld. */ + fprintf(logfile, "IN: EXECUTE\n"); + target_disas(logfile, cs, &dc->base); + return true; } + return false; } static const TranslatorOps s390x_tr_ops = { @@ -6747,8 +6480,8 @@ static const TranslatorOps s390x_tr_ops = { .disas_log = s390x_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc; @@ -6759,8 +6492,7 @@ void s390x_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - S390CPU *cpu = S390_CPU(cs); - CPUS390XState *env = &cpu->env; + 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 d39ee81cd6..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,7 +906,7 @@ 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]); @@ -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) @@ -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), @@ -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 75cf605b9f..1bbaa82fe8 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -621,8 +621,8 @@ static void vfma32(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, int i; for (i = 0; i < 4; i++) { - const float32 a = s390_vec_read_float32(v2, i); - const float32 b = s390_vec_read_float32(v3, i); + const float32 a = s390_vec_read_float32(v3, i); + const float32 b = s390_vec_read_float32(v2, i); const float32 c = s390_vec_read_float32(v4, i); float32 ret = float32_muladd(a, b, c, flags, &env->fpu_status); @@ -645,8 +645,8 @@ static void vfma64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, int i; for (i = 0; i < 2; i++) { - const float64 a = s390_vec_read_float64(v2, i); - const float64 b = s390_vec_read_float64(v3, i); + const float64 a = s390_vec_read_float64(v3, i); + const float64 b = s390_vec_read_float64(v2, i); const float64 c = s390_vec_read_float64(v4, i); const float64 ret = float64_muladd(a, b, c, flags, &env->fpu_status); @@ -664,8 +664,8 @@ static void vfma128(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, const S390Vector *v4, CPUS390XState *env, bool s, int flags, uintptr_t retaddr) { - const float128 a = s390_vec_read_float128(v2); - const float128 b = s390_vec_read_float128(v3); + const float128 a = s390_vec_read_float128(v3); + const float128 b = s390_vec_read_float128(v2); const float128 c = s390_vec_read_float128(v4); uint8_t vxc, vec_exc = 0; float128 ret; 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/s390x/trace-events b/target/s390x/trace-events index 729cb012b4..d371ef71b9 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -1,9 +1,5 @@ # See docs/devel/tracing.rst for syntax documentation. -# mmu_helper.c -get_skeys_nonzero(int rc) "SKEY: Call to get_skeys unexpectedly returned %d" -set_skeys_nonzero(int rc) "SKEY: Call to set_skeys unexpectedly returned %d" - # ioinst.c ioinst(const char *insn) "IOINST: %s" ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" diff --git a/target/sh4/Kconfig b/target/sh4/Kconfig index 2397c86028..93b92f1e48 100644 --- a/target/sh4/Kconfig +++ b/target/sh4/Kconfig @@ -1,2 +1,4 @@ config SH4 bool + # needed for sh_intc_get_pending_vector + select SH_INTC diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index 98a02509bb..a30ba992b3 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -2,7 +2,7 @@ * SH4 cpu parameters for qemu. * * Copyright (c) 2005 Samuel Tardieu - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef 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 827cee25af..8f07261dcf 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) { @@ -46,7 +47,8 @@ static void superh_cpu_synchronize_from_tb(CPUState *cs, { SuperHCPU *cpu = SUPERH_CPU(cs); - cpu->env.pc = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu->env.pc = tb->pc; cpu->env.flags = tb->flags & TB_FLAG_ENVFLAGS_MASK; } @@ -69,11 +71,10 @@ static void superh_restore_state_to_opc(CPUState *cs, 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 & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND)) - && env->pc != tb_pc(tb)) { + && !tcg_cflags_has(cs, CF_PCREL) && env->pc != tb->pc) { env->pc -= 2; env->flags &= ~(TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND); return true; @@ -87,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, ResetType type) +{ + 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, type); + } memset(env, 0, offsetof(CPUSH4State, end_reset_fields)); @@ -118,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; @@ -148,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); @@ -160,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; @@ -178,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; @@ -196,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; @@ -232,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); } @@ -255,7 +246,7 @@ 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, @@ -263,6 +254,7 @@ static const struct TCGCPUOps superh_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = superh_cpu_tlb_fill, .cpu_exec_interrupt = superh_cpu_exec_interrupt, + .cpu_exec_halt = superh_cpu_has_work, .do_interrupt = superh_cpu_do_interrupt, .do_unaligned_access = superh_cpu_do_unaligned_access, .io_recompile_replay_branch = superh_io_recompile_replay_branch, @@ -274,14 +266,17 @@ 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; @@ -309,6 +304,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 727b829598..d928bcf006 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -107,19 +107,19 @@ 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 @@ -139,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; @@ -186,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; @@ -204,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, @@ -222,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); @@ -253,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 & TB_FLAG_DELAY_SLOT_RTE)) { - return 0; - } else { - return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; - } -} #include "exec/cpu-all.h" @@ -368,8 +380,8 @@ 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. */ 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 e02e7af607..9659c69550 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/log.h" #if !defined(CONFIG_USER_ONLY) @@ -55,8 +56,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 +84,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); } @@ -149,8 +149,8 @@ void superh_cpu_do_interrupt(CPUState *cs) 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->spc -= 2; + /* Clear flags for exception/interrupt routine. */ env->flags &= ~TB_FLAG_DELAY_SLOT_MASK; } @@ -187,23 +187,23 @@ void superh_cpu_do_interrupt(CPUState *cs) static void update_itlb_use(CPUSH4State * env, int itlbnb) { - uint8_t or_mask = 0, and_mask = (uint8_t) - 1; + uint32_t or_mask = 0, and_mask = 0xff; 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 +213,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 +231,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 +241,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 +265,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 +297,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 +432,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 +517,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 +600,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 +611,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 +660,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 +781,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 & TB_FLAG_DELAY_SLOT_MASK) { + if (cpu_env(cs)->flags & TB_FLAG_DELAY_SLOT_MASK) { return false; } else { superh_cpu_do_interrupt(cs); @@ -800,8 +796,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 7db3468b01..53b092175d 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -17,20 +17,20 @@ * 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 +73,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 +95,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,16 +168,16 @@ 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 & TB_FLAG_DELAY_SLOT) { - qemu_printf("in delay slot (delayed_pc=0x%08x)\n", - env->delayed_pc); + qemu_fprintf(f, "in delay slot (delayed_pc=0x%08x)\n", + env->delayed_pc); } else if (env->flags & TB_FLAG_DELAY_SLOT_COND) { - qemu_printf("in conditional delay slot (delayed_pc=0x%08x)\n", - env->delayed_pc); + 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 +193,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) @@ -254,9 +250,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 +261,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); } } @@ -413,105 +409,103 @@ 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); + 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 */ + 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); + tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); ctx->envflags |= TB_FLAG_DELAY_SLOT_RTE; - ctx->delayed_pc = (uint32_t) - 1; + 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 (mov #-n, r15). @@ -525,260 +519,247 @@ static void _decode_opc(DisasContext * ctx) 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 |= TB_FLAG_DELAY_SLOT; - return; - case 0xb000: /* bsr disp */ - CHECK_NOT_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 |= TB_FLAG_DELAY_SLOT; - return; + 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(); - tcg_gen_add_i32(t0, REG(B7_4), REG(B11_8)); + TCGv Rn = REG(B11_8); + TCGv Rm = REG(B7_4); + TCGv result, t1, t2; + + result = tcg_temp_new(); t1 = tcg_temp_new(); - tcg_gen_xor_i32(t1, t0, REG(B11_8)); t2 = tcg_temp_new(); - tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); + tcg_gen_add_i32(result, Rm, Rn); + /* T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 */ + tcg_gen_xor_i32(t1, result, Rn); + tcg_gen_xor_i32(t2, Rm, Rn); 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); + tcg_gen_mov_i32(Rn, result); } - 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 */ @@ -801,108 +782,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(); @@ -921,14 +892,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(); @@ -947,621 +914,596 @@ 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(); - tcg_gen_sub_i32(t0, REG(B11_8), REG(B7_4)); + TCGv Rn = REG(B11_8); + TCGv Rm = REG(B7_4); + TCGv result, t1, t2; + + result = tcg_temp_new(); t1 = tcg_temp_new(); - tcg_gen_xor_i32(t1, t0, REG(B7_4)); t2 = tcg_temp_new(); - tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); + tcg_gen_sub_i32(result, Rn, Rm); + /* T = ((Rn ^ Rm) & (Result ^ Rn)) >> 31 */ + tcg_gen_xor_i32(t1, result, Rn); + tcg_gen_xor_i32(t2, Rn, Rm); 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); + tcg_gen_mov_i32(Rn, result); } - 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 |= TB_FLAG_DELAY_SLOT_COND; - return; - case 0x8900: /* bt label */ - CHECK_NOT_DELAY_SLOT + 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 |= TB_FLAG_DELAY_SLOT_COND; - return; - case 0x8800: /* cmp/eq #imm,R0 */ + 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 |= TB_FLAG_DELAY_SLOT; - ctx->delayed_pc = (uint32_t) - 1; - return; - case 0x0003: /* bsrf Rn */ - CHECK_NOT_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); + 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 */ + 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)); + 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 + 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)); + 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 + 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 @@ -1584,12 +1526,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); @@ -1614,211 +1557,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; @@ -1829,9 +1760,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; @@ -1845,10 +1775,10 @@ static void _decode_opc(DisasContext * ctx) 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; @@ -1856,9 +1786,9 @@ static void _decode_opc(DisasContext * ctx) do_fpu_disabled: gen_save_cpu_state(ctx, true); if (ctx->envflags & TB_FLAG_DELAY_SLOT_MASK) { - gen_helper_raise_slot_fpu_disable(cpu_env); + 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; @@ -1887,14 +1817,26 @@ static void decode_opc(DisasContext * ctx) tcg_gen_movi_i32(cpu_flags, ctx->envflags); if (old_flags & TB_FLAG_DELAY_SLOT_COND) { - gen_delayed_conditional_jump(ctx); + 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. @@ -2034,7 +1976,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 */ @@ -2042,7 +1984,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 */ @@ -2088,7 +2030,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 */ @@ -2220,41 +2162,40 @@ 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 &= ~TB_FLAG_GUSA_MASK; - ctx->base.pc_next = pc_end; - ctx->base.num_insns += max_insns - 1; - return; + 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 |= TB_FLAG_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); + ctx->base.insn_start = tcg_last_op(); + } } #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; @@ -2264,7 +2205,7 @@ 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; @@ -2311,18 +2252,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 & 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. */ - decode_gusa(ctx, env); + /* + * 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 @@ -2358,24 +2309,16 @@ static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) } } -static void sh4_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cs, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cs, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps sh4_tr_ops = { .init_disas_context = sh4_tr_init_disas_context, .tb_start = sh4_tr_tb_start, .insn_start = sh4_tr_insn_start, .translate_insn = sh4_tr_translate_insn, .tb_stop = sh4_tr_tb_stop, - .disas_log = sh4_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; diff --git a/target/sparc/asi.h b/target/sparc/asi.h index bb58735ddb..14ffaa3842 100644 --- a/target/sparc/asi.h +++ b/target/sparc/asi.h @@ -144,15 +144,17 @@ * 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_MON_AIUP 0x12 /* (VIS4) Primary, user, monitor */ +#define ASI_MON_AIUS 0x13 /* (VIS4) Secondary, user, monitor */ +#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 +165,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 +233,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 */ @@ -257,6 +259,8 @@ #define ASI_UDBL_CONTROL_R 0x7f /* External UDB control regs rd low*/ #define ASI_INTR_R 0x7f /* IRQ vector dispatch read */ #define ASI_INTR_DATAN_R 0x7f /* (III) In irq vector data reg N */ +#define ASI_MON_P 0x84 /* (VIS4) Primary, monitor */ +#define ASI_MON_S 0x85 /* (VIS4) Secondary, monitor */ #define ASI_PIC 0xb0 /* (NG4) PIC registers */ #define ASI_PST8_P 0xc0 /* Primary, 8 8-bit, partial */ #define ASI_PST8_S 0xc1 /* Secondary, 8 8-bit, partial */ 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..be81005237 --- /dev/null +++ b/target/sparc/cpu-feature.h.inc @@ -0,0 +1,18 @@ +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) +FEATURE(FMAF) +FEATURE(VIS3) +FEATURE(IMA) +FEATURE(VIS4) diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h index 72ddc4a34f..14105dc18b 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -1,7 +1,7 @@ /* * Sparc cpu parameters for qemu. * - * SPDX-License-Identifier: LGPL-2.0+ + * SPDX-License-Identifier: LGPL-2.0-or-later */ #ifndef SPARC_CPU_PARAM_H @@ -16,13 +16,34 @@ # 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 +/* + * 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) + #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 4c3d08a875..dd7af86de7 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -25,17 +25,20 @@ #include "exec/exec-all.h" #include "hw/qdev-properties.h" #include "qapi/visitor.h" +#include "tcg/tcg.h" +#include "fpu/softfloat.h" //#define DEBUG_FEATURES -static void sparc_cpu_reset(DeviceState *dev) +static void sparc_cpu_reset_hold(Object *obj, ResetType type) { - 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, type); + } memset(env, 0, offsetof(CPUSPARCState, end_reset_fields)); env->cwp = 0; @@ -43,7 +46,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; @@ -75,14 +77,14 @@ static void sparc_cpu_reset(DeviceState *dev) env->npc = env->pc + 4; #endif env->cache_control = 0; + cpu_put_fsr(env, 0); } #ifndef CONFIG_USER_ONLY 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; @@ -206,7 +208,7 @@ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu) static const sparc_def_t sparc_defs[] = { #ifdef TARGET_SPARC64 { - .name = "Fujitsu Sparc64", + .name = "Fujitsu-Sparc64", .iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -215,7 +217,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu Sparc64 III", + .name = "Fujitsu-Sparc64-III", .iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -224,7 +226,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu Sparc64 IV", + .name = "Fujitsu-Sparc64-IV", .iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -233,7 +235,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu Sparc64 V", + .name = "Fujitsu-Sparc64-V", .iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -242,7 +244,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc I", + .name = "TI-UltraSparc-I", .iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -251,7 +253,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc II", + .name = "TI-UltraSparc-II", .iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -260,7 +262,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc IIi", + .name = "TI-UltraSparc-IIi", .iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -269,7 +271,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI UltraSparc IIe", + .name = "TI-UltraSparc-IIe", .iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -278,7 +280,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc III", + .name = "Sun-UltraSparc-III", .iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -287,7 +289,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc III Cu", + .name = "Sun-UltraSparc-III-Cu", .iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_3, @@ -296,7 +298,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IIIi", + .name = "Sun-UltraSparc-IIIi", .iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -305,7 +307,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IV", + .name = "Sun-UltraSparc-IV", .iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_4, @@ -314,7 +316,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc IV+", + .name = "Sun-UltraSparc-IV-plus", .iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -323,7 +325,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT, }, { - .name = "Sun UltraSparc IIIi+", + .name = "Sun-UltraSparc-IIIi-plus", .iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_3, @@ -332,7 +334,7 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Sun UltraSparc T1", + .name = "Sun-UltraSparc-T1", /* defined in sparc_ifu_fdp.v and ctu.h */ .iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)), .fpu_version = 0x00000000, @@ -343,7 +345,7 @@ static const sparc_def_t sparc_defs[] = { | CPU_FEATURE_GL, }, { - .name = "Sun UltraSparc T2", + .name = "Sun-UltraSparc-T2", /* defined in tlu_asi_ctl.v and n2_revid_cust.v */ .iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)), .fpu_version = 0x00000000, @@ -354,7 +356,7 @@ static const sparc_def_t sparc_defs[] = { | CPU_FEATURE_GL, }, { - .name = "NEC UltraSparc I", + .name = "NEC-UltraSparc-I", .iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)), .fpu_version = 0x00000000, .mmu_version = mmu_us_12, @@ -364,9 +366,9 @@ static const sparc_def_t sparc_defs[] = { }, #else { - .name = "Fujitsu MB86904", + .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, @@ -377,9 +379,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "Fujitsu MB86907", + .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, @@ -390,9 +392,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI MicroSparc I", + .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 +402,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", + .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, @@ -418,9 +418,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI MicroSparc IIep", + .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, @@ -431,9 +431,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 40", /* STP1020NPGA */ + .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, @@ -444,9 +444,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 50", /* STP1020PGA */ + .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, @@ -457,9 +457,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 51", + .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, @@ -471,9 +471,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 60", /* STP1020APGA */ + .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, @@ -484,9 +484,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc 61", + .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, @@ -498,9 +498,9 @@ static const sparc_def_t sparc_defs[] = { .features = CPU_DEFAULT_FEATURES, }, { - .name = "TI SuperSparc II", + .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 +514,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 +527,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 +542,24 @@ 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", + [CPU_FEATURE_BIT_FMAF] = "fmaf", + [CPU_FEATURE_BIT_VIS3] = "vis3", + [CPU_FEATURE_BIT_IMA] = "ima", + [CPU_FEATURE_BIT_VIS4] = "vis4", +#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 +580,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 +618,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 +673,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 +683,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"); } @@ -705,17 +708,43 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs, { SPARCCPU *cpu = SPARC_CPU(cs); - cpu->env.pc = tb_pc(tb); + tcg_debug_assert(!tcg_cflags_has(cs, 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) @@ -739,6 +768,16 @@ static ObjectClass *sparc_cpu_class_by_name(const char *cpu_model) char *typename; typename = sparc_cpu_type_name(cpu_model); + + /* Fix up legacy names with '+' in it */ + if (g_str_equal(typename, SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IV+"))) { + g_free(typename); + typename = g_strdup(SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IV-plus")); + } else if (g_str_equal(typename, SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IIIi+"))) { + g_free(typename); + typename = g_strdup(SPARC_CPU_TYPE_NAME("Sun-UltraSparc-IIIi-plus")); + } + oc = object_class_by_name(typename); g_free(typename); return oc; @@ -749,17 +788,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; @@ -772,6 +808,13 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) env->version |= env->def.nwindows - 1; #endif + /* + * Prefer SNaN over QNaN, order B then A. It's OK to do this in realize + * rather than reset, because fp_status is after 'end_reset_fields' in + * the CPU state struct so it won't get zeroed on reset. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ba, &env->fp_status); + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -789,8 +832,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; } @@ -833,21 +874,37 @@ 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), + DEFINE_PROP_BIT("fmaf", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_FMAF, false), + DEFINE_PROP_BIT("vis3", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS3, false), + DEFINE_PROP_BIT("ima", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_IMA, false), + DEFINE_PROP_BIT("vis4", SPARCCPU, env.def.features, + CPU_FEATURE_BIT_VIS4, 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), @@ -869,7 +926,7 @@ 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, @@ -877,6 +934,7 @@ static const struct TCGCPUOps sparc_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = sparc_cpu_tlb_fill, .cpu_exec_interrupt = sparc_cpu_exec_interrupt, + .cpu_exec_halt = sparc_cpu_has_work, .do_interrupt = sparc_cpu_do_interrupt, .do_transaction_failed = sparc_cpu_do_transaction_failed, .do_unaligned_access = sparc_cpu_do_unaligned_access, @@ -889,16 +947,19 @@ 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; @@ -924,6 +985,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 e478c5eb16..f517e5a383 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -8,8 +8,10 @@ #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 +116,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 +155,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 +163,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 +172,24 @@ 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_QNE (1ULL << 13) + +#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 +223,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 +237,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 +406,60 @@ 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* */ + +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + /* + * Single-element FPU fault queue, with address and insn, + * packaged into the double-word with which it is stored. + */ + uint32_t fsr_qne; /* qne */ + union { + uint64_t d; + struct { +#if HOST_BIG_ENDIAN + uint32_t addr; + uint32_t insn; +#else + uint32_t insn; + uint32_t addr; +#endif + } s; + } fq; +#endif + CPU_DoubleU fpr[TARGET_DPREGS]; /* floating point registers */ uint32_t cwp; /* index of current register window (extracted from PSR) */ @@ -504,14 +507,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 +522,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 +544,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 +557,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 +592,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); @@ -604,11 +614,14 @@ 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); @@ -637,7 +650,6 @@ static inline int tlb_compare_context(const SparcTLBEntry *tlb, return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK); } -#endif #endif /* cpu-exec.c */ @@ -653,8 +665,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 @@ -695,34 +705,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) @@ -762,15 +744,16 @@ trap_state* cpu_tsptr(CPUSPARCState* env); #define TB_FLAG_AM_ENABLED (1 << 5) #define TB_FLAG_SUPER (1 << 6) #define TB_FLAG_HYPER (1 << 7) +#define TB_FLAG_FSR_QNE (1 << 8) #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; @@ -785,17 +768,20 @@ 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 +#ifndef CONFIG_USER_ONLY + if (env->fsr_qne) { + flags |= TB_FLAG_FSR_QNE; + } +#endif /* !CONFIG_USER_ONLY */ +#endif /* TARGET_SPARC64 */ *pflags = flags; } diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index f54fa9b959..6f9ccc008a 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,522 @@ 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; \ +float32 helper_fmadds(CPUSPARCState *env, float32 s1, + float32 s2, float32 s3, uint32_t op) +{ + float32 ret = float32_muladd(s1, s2, s3, op, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fmaddd(CPUSPARCState *env, float64 s1, + float64 s2, float64 s3, uint32_t op) +{ + float64 ret = float64_muladd(s1, s2, s3, op, &env->fp_status); + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float32 helper_fnadds(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_add(src1, src2, &env->fp_status); + + /* + * NaN inputs or result do not get a sign change. + * Nor, apparently, does zero: on hardware, -(x + -x) yields +0. + */ + if (!float32_is_any_nan(ret) && !float32_is_zero(ret)) { + ret = float32_chs(ret); } -#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; \ + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float32 helper_fnmuls(CPUSPARCState *env, float32 src1, float32 src2) +{ + float32 ret = float32_mul(src1, src2, &env->fp_status); + + /* NaN inputs or result do not get a sign change. */ + if (!float32_is_any_nan(ret)) { + ret = float32_chs(ret); } + check_ieee_exceptions(env, GETPC()); + return ret; +} -GEN_FCMP_T(fcmps, float32, 0, 0); -GEN_FCMP_T(fcmpd, float64, 0, 0); +float64 helper_fnaddd(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_add(src1, src2, &env->fp_status); -GEN_FCMP_T(fcmpes, float32, 0, 1); -GEN_FCMP_T(fcmped, float64, 0, 1); + /* + * NaN inputs or result do not get a sign change. + * Nor, apparently, does zero: on hardware, -(x + -x) yields +0. + */ + if (!float64_is_any_nan(ret) && !float64_is_zero(ret)) { + ret = float64_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} -GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0); -GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1); +float64 helper_fnmuld(CPUSPARCState *env, float64 src1, float64 src2) +{ + float64 ret = float64_mul(src1, src2, &env->fp_status); + /* NaN inputs or result do not get a sign change. */ + if (!float64_is_any_nan(ret)) { + ret = float64_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + +float64 helper_fnsmuld(CPUSPARCState *env, float32 src1, float32 src2) +{ + float64 ret = float64_mul(float32_to_float64(src1, &env->fp_status), + float32_to_float64(src2, &env->fp_status), + &env->fp_status); + + /* NaN inputs or result do not get a sign change. */ + if (!float64_is_any_nan(ret)) { + ret = float64_chs(ret); + } + check_ieee_exceptions(env, GETPC()); + return ret; +} + +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(); +} + +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()); +} + +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()); +} + +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()); +} + +uint32_t helper_flcmps(float32 src1, float32 src2) +{ + /* + * FLCMP never raises an exception nor modifies any FSR fields. + * Perform the comparison with a dummy fp environment. + */ + float_status discard = { }; + FloatRelation r; + + set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); + r = float32_compare_quiet(src1, src2, &discard); + + switch (r) { + case float_relation_equal: + if (src2 == float32_zero && src1 != float32_zero) { + return 1; /* -0.0 < +0.0 */ + } + return 0; + case float_relation_less: + return 1; + case float_relation_greater: + return 0; + case float_relation_unordered: + return float32_is_any_nan(src2) ? 3 : 2; + } + g_assert_not_reached(); +} + +uint32_t helper_flcmpd(float64 src1, float64 src2) +{ + float_status discard = { }; + FloatRelation r; + + set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard); + r = float64_compare_quiet(src1, src2, &discard); + + switch (r) { + case float_relation_equal: + if (src2 == float64_zero && src1 != float64_zero) { + return 1; /* -0.0 < +0.0 */ + } + return 0; + case float_relation_less: + return 1; + case float_relation_greater: + return 0; + case float_relation_unordered: + return float64_is_any_nan(src2) ? 3 : 2; + } + g_assert_not_reached(); +} + +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; +#elif !defined(CONFIG_USER_ONLY) + fsr |= env->fsr_qne; #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 +590,31 @@ 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); +#elif !defined(CONFIG_USER_ONLY) + env->fsr_qne = fsr & FSR_QNE; +#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); +} + +void helper_set_fsr_nofcc(CPUSPARCState *env, uint32_t fsr) +{ + env->fsr_cexc_ftt = fsr & (FSR_CEXC_MASK | FSR_FTT_MASK); + set_fsr_nonsplit(env, fsr); } -#endif diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c index 5d1e808e8c..ec0036e9ef 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: @@ -109,7 +108,7 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) SPARCCPU *cpu = SPARC_CPU(cs); CPUSPARCState *env = &cpu->env; #if defined(TARGET_ABI32) - abi_ulong tmp; + uint32_t tmp; tmp = ldl_p(mem_buf); #else @@ -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..7846ddd6f6 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 /= b32; + 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..134e519a37 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,145 +25,118 @@ 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, TCG_CALL_NO_RWG, void, env, i32) +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_FLAGS_2(flcmps, TCG_CALL_NO_RWG_SE, i32, f32, f32) +DEF_HELPER_FLAGS_2(flcmpd, TCG_CALL_NO_RWG_SE, i32, f64, f64) 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_5(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, i32) +DEF_HELPER_FLAGS_3(fnaddd, TCG_CALL_NO_WG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fnmuld, 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_5(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, i32) +DEF_HELPER_FLAGS_3(fnadds, TCG_CALL_NO_WG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fnmuls, 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(fnsmuld, 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) -DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i32, i32) +DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i32, i64) +DEF_HELPER_FLAGS_2(fmul8x16a, TCG_CALL_NO_RWG_SE, i64, i32, s32) DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) -DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(fexpand, TCG_CALL_NO_RWG_SE, i64, i32) DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) 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(cmask8, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmask16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmask32, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fchksm16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmean16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fslas16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fslas32, TCG_CALL_NO_RWG_SE, i64, i64, i64) +#define VIS_CMPHELPER(name) \ + DEF_HELPER_FLAGS_2(f##name##8, TCG_CALL_NO_RWG_SE, \ + i64, i64, i64) \ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \ - i64, i64, i64) \ + i64, i64, i64) \ DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_NO_RWG_SE, \ i64, i64, i64) VIS_CMPHELPER(cmpgt) VIS_CMPHELPER(cmpeq) VIS_CMPHELPER(cmple) VIS_CMPHELPER(cmpne) +VIS_CMPHELPER(cmpugt) +VIS_CMPHELPER(cmpule) +DEF_HELPER_FLAGS_2(xmulx, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(xmulxhi, TCG_CALL_NO_RWG_SE, i64, i64, i64) #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..989c20b44a --- /dev/null +++ b/target/sparc/insns.decode @@ -0,0 +1,708 @@ +# SPDX-License-Identifier: LGPL-2.0-or-later +# +# 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. +## + +%dfp_rd 25:5 !function=extract_dfpreg +%dfp_rs1 14:5 !function=extract_dfpreg +%dfp_rs2 0:5 !function=extract_dfpreg +%dfp_rs3 9:5 !function=extract_dfpreg + +%qfp_rd 25:5 !function=extract_qfpreg +%qfp_rs1 14:5 !function=extract_qfpreg +%qfp_rs2 0:5 !function=extract_qfpreg + +&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 +@d_r_r .. ..... ...... rs1:5 . ........ rs2:5 \ + &r_r_r rd=%dfp_rd +@r_d_d .. rd:5 ...... ..... . ........ ..... \ + &r_r_r rs1=%dfp_rs1 rs2=%dfp_rs2 +@d_r_d .. ..... ...... rs1:5 . ........ ..... \ + &r_r_r rd=%dfp_rd rs2=%dfp_rs2 +@d_d_d .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%dfp_rd rs1=%dfp_rs1 rs2=%dfp_rs2 +@q_q_q .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%qfp_rd rs1=%qfp_rs1 rs2=%qfp_rs2 +@q_d_d .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%qfp_rd rs1=%dfp_rs1 rs2=%dfp_rs2 + +@r_r_r_swap .. rd:5 ...... rs2:5 . ........ rs1:5 &r_r_r +@d_d_d_swap .. ..... ...... ..... . ........ ..... \ + &r_r_r rd=%dfp_rd rs1=%dfp_rs2 rs2=%dfp_rs1 + +&r_r rd rs +@r_r1 .. rd:5 ...... rs:5 . ........ ..... &r_r +@r_r2 .. rd:5 ...... ..... . ........ rs:5 &r_r +@r_d2 .. rd:5 ...... ..... . ........ ..... &r_r rs=%dfp_rs2 +@r_q2 .. rd:5 ...... ..... . ........ ..... &r_r rs=%qfp_rs2 +@d_r2 .. ..... ...... ..... . ........ rs:5 &r_r rd=%dfp_rd +@q_r2 .. ..... ...... ..... . ........ rs:5 &r_r rd=%qfp_rd +@d_d1 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%dfp_rd rs=%dfp_rs1 +@d_d2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%dfp_rd rs=%dfp_rs2 +@d_q2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%dfp_rd rs=%qfp_rs2 +@q_q2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%qfp_rd rs=%qfp_rs2 +@q_d2 .. ..... ...... ..... . ........ ..... \ + &r_r rd=%qfp_rd rs=%dfp_rs2 + +&r_r_r_r rd rs1 rs2 rs3 +@r_r_r_r .. rd:5 ...... rs1:5 rs3:5 .... rs2:5 &r_r_r_r +@d_d_d_d .. ..... ...... ..... ..... .... ..... \ + &r_r_r_r rd=%dfp_rd rs1=%dfp_rs1 rs2=%dfp_rs2 rs3=%dfp_rs3 + +{ + [ + 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 + WRMWAIT 10 11100 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 ..... @d_d2 +FMOVq 10 ..... 110100 00000 0 0000 0011 ..... @q_q2 +FNEGs 10 ..... 110100 00000 0 0000 0101 ..... @r_r2 +FNEGd 10 ..... 110100 00000 0 0000 0110 ..... @d_d2 +FNEGq 10 ..... 110100 00000 0 0000 0111 ..... @q_q2 +FABSs 10 ..... 110100 00000 0 0000 1001 ..... @r_r2 +FABSd 10 ..... 110100 00000 0 0000 1010 ..... @d_d2 +FABSq 10 ..... 110100 00000 0 0000 1011 ..... @q_q2 +FSQRTs 10 ..... 110100 00000 0 0010 1001 ..... @r_r2 +FSQRTd 10 ..... 110100 00000 0 0010 1010 ..... @d_d2 +FSQRTq 10 ..... 110100 00000 0 0010 1011 ..... @q_q2 +FADDs 10 ..... 110100 ..... 0 0100 0001 ..... @r_r_r +FADDd 10 ..... 110100 ..... 0 0100 0010 ..... @d_d_d +FADDq 10 ..... 110100 ..... 0 0100 0011 ..... @q_q_q +FSUBs 10 ..... 110100 ..... 0 0100 0101 ..... @r_r_r +FSUBd 10 ..... 110100 ..... 0 0100 0110 ..... @d_d_d +FSUBq 10 ..... 110100 ..... 0 0100 0111 ..... @q_q_q +FMULs 10 ..... 110100 ..... 0 0100 1001 ..... @r_r_r +FMULd 10 ..... 110100 ..... 0 0100 1010 ..... @d_d_d +FMULq 10 ..... 110100 ..... 0 0100 1011 ..... @q_q_q +FDIVs 10 ..... 110100 ..... 0 0100 1101 ..... @r_r_r +FDIVd 10 ..... 110100 ..... 0 0100 1110 ..... @d_d_d +FDIVq 10 ..... 110100 ..... 0 0100 1111 ..... @q_q_q +FNADDs 10 ..... 110100 ..... 0 0101 0001 ..... @r_r_r +FNADDd 10 ..... 110100 ..... 0 0101 0010 ..... @d_d_d +FNMULs 10 ..... 110100 ..... 0 0101 1001 ..... @r_r_r +FNMULd 10 ..... 110100 ..... 0 0101 1010 ..... @d_d_d +FHADDs 10 ..... 110100 ..... 0 0110 0001 ..... @r_r_r +FHADDd 10 ..... 110100 ..... 0 0110 0010 ..... @d_d_d +FHSUBs 10 ..... 110100 ..... 0 0110 0101 ..... @r_r_r +FHSUBd 10 ..... 110100 ..... 0 0110 0110 ..... @d_d_d +FsMULd 10 ..... 110100 ..... 0 0110 1001 ..... @d_r_r +FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @q_d_d +FNHADDs 10 ..... 110100 ..... 0 0111 0001 ..... @r_r_r +FNHADDd 10 ..... 110100 ..... 0 0111 0010 ..... @d_d_d +FNsMULd 10 ..... 110100 ..... 0 0111 1001 ..... @d_r_r +FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2 +FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_d2 +FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_q2 +FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2 +FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_r2 +FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_r2 +FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2 +FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_d2 +FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_q2 +FiTOd 10 ..... 110100 00000 0 1100 1000 ..... @d_r2 +FsTOd 10 ..... 110100 00000 0 1100 1001 ..... @d_r2 +FqTOd 10 ..... 110100 00000 0 1100 1011 ..... @d_q2 +FiTOq 10 ..... 110100 00000 0 1100 1100 ..... @q_r2 +FsTOq 10 ..... 110100 00000 0 1100 1101 ..... @q_r2 +FdTOq 10 ..... 110100 00000 0 1100 1110 ..... @q_d2 +FsTOi 10 ..... 110100 00000 0 1101 0001 ..... @r_r2 +FdTOi 10 ..... 110100 00000 0 1101 0010 ..... @r_d2 +FqTOi 10 ..... 110100 00000 0 1101 0011 ..... @r_q2 + +FMOVscc 10 rd:5 110101 0 cond:4 1 cc:1 0 000001 rs2:5 +FMOVdcc 10 ..... 110101 0 cond:4 1 cc:1 0 000010 ..... \ + rd=%dfp_rd rs2=%dfp_rs2 +FMOVqcc 10 ..... 110101 0 cond:4 1 cc:1 0 000011 ..... \ + rd=%qfp_rd rs2=%qfp_rs2 + +FMOVsfcc 10 rd:5 110101 0 cond:4 0 cc:2 000001 rs2:5 +FMOVdfcc 10 ..... 110101 0 cond:4 0 cc:2 000010 ..... \ + rd=%dfp_rd rs2=%dfp_rs2 +FMOVqfcc 10 ..... 110101 0 cond:4 0 cc:2 000011 ..... \ + rd=%qfp_rd rs2=%qfp_rs2 + +FMOVRs 10 rd:5 110101 rs1:5 0 cond:3 00101 rs2:5 +FMOVRd 10 ..... 110101 rs1:5 0 cond:3 00110 ..... \ + rd=%dfp_rd rs2=%dfp_rs2 +FMOVRq 10 ..... 110101 rs1:5 0 cond:3 00111 ..... \ + rd=%qfp_rd rs2=%qfp_rs2 + +FCMPs 10 000 cc:2 110101 rs1:5 0 0101 0001 rs2:5 +FCMPd 10 000 cc:2 110101 ..... 0 0101 0010 ..... \ + rs1=%dfp_rs1 rs2=%dfp_rs2 +FCMPq 10 000 cc:2 110101 ..... 0 0101 0011 ..... \ + rs1=%qfp_rs1 rs2=%qfp_rs2 +FCMPEs 10 000 cc:2 110101 rs1:5 0 0101 0101 rs2:5 +FCMPEd 10 000 cc:2 110101 ..... 0 0101 0110 ..... \ + rs1=%dfp_rs1 rs2=%dfp_rs2 +FCMPEq 10 000 cc:2 110101 ..... 0 0101 0111 ..... \ + rs1=%qfp_rs1 rs2=%qfp_rs2 + +{ + [ + 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 + + ADDXC 10 ..... 110110 ..... 0 0001 0001 ..... @r_r_r + ADDXCcc 10 ..... 110110 ..... 0 0001 0011 ..... @r_r_r + UMULXHI 10 ..... 110110 ..... 0 0001 0110 ..... @r_r_r + LZCNT 10 ..... 110110 00000 0 0001 0111 ..... @r_r2 + XMULX 10 ..... 110110 ..... 1 0001 0101 ..... @r_r_r + XMULXHI 10 ..... 110110 ..... 1 0001 0110 ..... @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 + + CMASK8 10 00000 110110 00000 0 0001 1011 rs2:5 + CMASK16 10 00000 110110 00000 0 0001 1101 rs2:5 + CMASK32 10 00000 110110 00000 0 0001 1111 rs2:5 + + FPCMPLE16 10 ..... 110110 ..... 0 0010 0000 ..... @r_d_d + FPCMPNE16 10 ..... 110110 ..... 0 0010 0010 ..... @r_d_d + FPCMPGT16 10 ..... 110110 ..... 0 0010 1000 ..... @r_d_d + FPCMPEQ16 10 ..... 110110 ..... 0 0010 1010 ..... @r_d_d + FPCMPLE32 10 ..... 110110 ..... 0 0010 0100 ..... @r_d_d + FPCMPNE32 10 ..... 110110 ..... 0 0010 0110 ..... @r_d_d + FPCMPGT32 10 ..... 110110 ..... 0 0010 1100 ..... @r_d_d + FPCMPEQ32 10 ..... 110110 ..... 0 0010 1110 ..... @r_d_d + + FSLL16 10 ..... 110110 ..... 0 0010 0001 ..... @d_d_d + FSRL16 10 ..... 110110 ..... 0 0010 0011 ..... @d_d_d + FSLAS16 10 ..... 110110 ..... 0 0010 1001 ..... @d_d_d + FSRA16 10 ..... 110110 ..... 0 0010 1011 ..... @d_d_d + FSLL32 10 ..... 110110 ..... 0 0010 0101 ..... @d_d_d + FSRL32 10 ..... 110110 ..... 0 0010 0111 ..... @d_d_d + FSLAS32 10 ..... 110110 ..... 0 0010 1101 ..... @d_d_d + FSRA32 10 ..... 110110 ..... 0 0010 1111 ..... @d_d_d + + FPCMPULE8 10 ..... 110110 ..... 1 0010 0000 ..... @r_d_d + FPCMPUGT8 10 ..... 110110 ..... 1 0010 1000 ..... @r_d_d + FPCMPNE8 10 ..... 110110 ..... 1 0010 0010 ..... @r_d_d + FPCMPEQ8 10 ..... 110110 ..... 1 0010 1010 ..... @r_d_d + FPCMPLE8 10 ..... 110110 ..... 0 0011 0100 ..... @r_d_d + FPCMPGT8 10 ..... 110110 ..... 0 0011 1100 ..... @r_d_d + FPCMPULE16 10 ..... 110110 ..... 1 0010 1110 ..... @r_d_d + FPCMPUGT16 10 ..... 110110 ..... 1 0010 1011 ..... @r_d_d + FPCMPULE32 10 ..... 110110 ..... 1 0010 1111 ..... @r_d_d + FPCMPUGT32 10 ..... 110110 ..... 1 0010 1100 ..... @r_d_d + + FMUL8x16 10 ..... 110110 ..... 0 0011 0001 ..... @d_r_d + FMUL8x16AU 10 ..... 110110 ..... 0 0011 0011 ..... @d_r_r + FMUL8x16AL 10 ..... 110110 ..... 0 0011 0101 ..... @d_r_r + FMUL8SUx16 10 ..... 110110 ..... 0 0011 0110 ..... @d_d_d + FMUL8ULx16 10 ..... 110110 ..... 0 0011 0111 ..... @d_d_d + FMULD8SUx16 10 ..... 110110 ..... 0 0011 1000 ..... @d_r_r + FMULD8ULx16 10 ..... 110110 ..... 0 0011 1001 ..... @d_r_r + FPACK32 10 ..... 110110 ..... 0 0011 1010 ..... @d_d_d + FPACK16 10 ..... 110110 00000 0 0011 1011 ..... @r_d2 + FPACKFIX 10 ..... 110110 00000 0 0011 1101 ..... @r_d2 + PDIST 10 ..... 110110 ..... 0 0011 1110 ..... \ + &r_r_r_r rd=%dfp_rd rs1=%dfp_rd rs2=%dfp_rs1 rs3=%dfp_rs2 + PDISTN 10 ..... 110110 ..... 0 0011 1111 ..... @r_d_d + + FMEAN16 10 ..... 110110 ..... 0 0100 0000 ..... @d_d_d + SUBXC 10 ..... 110110 ..... 0 0100 0001 ..... @r_r_r + SUBXCcc 10 ..... 110110 ..... 0 0100 0011 ..... @r_r_r + FCHKSM16 10 ..... 110110 ..... 0 0100 0100 ..... @d_d_d + FALIGNDATAg 10 ..... 110110 ..... 0 0100 1000 ..... @d_d_d + FPMERGE 10 ..... 110110 ..... 0 0100 1011 ..... @d_r_r + BSHUFFLE 10 ..... 110110 ..... 0 0100 1100 ..... @d_d_d + FEXPAND 10 ..... 110110 00000 0 0100 1101 ..... @d_r2 + FALIGNDATAi 10 ..... 110110 ..... 0 0100 1001 ..... @d_r_d + + FSRCd 10 ..... 110110 ..... 0 0111 0100 00000 @d_d1 # FSRC1d + FSRCs 10 ..... 110110 ..... 0 0111 0101 00000 @r_r1 # FSRC1s + FSRCd 10 ..... 110110 00000 0 0111 1000 ..... @d_d2 # FSRC2d + FSRCs 10 ..... 110110 00000 0 0111 1001 ..... @r_r2 # FSRC2s + FNOTd 10 ..... 110110 ..... 0 0110 1010 00000 @d_d1 # FNOT1d + FNOTs 10 ..... 110110 ..... 0 0110 1011 00000 @r_r1 # FNOT1s + FNOTd 10 ..... 110110 00000 0 0110 0110 ..... @d_d2 # FNOT2d + FNOTs 10 ..... 110110 00000 0 0110 0111 ..... @r_r2 # FNOT2s + + FPADD16 10 ..... 110110 ..... 0 0101 0000 ..... @d_d_d + FPADD16s 10 ..... 110110 ..... 0 0101 0001 ..... @r_r_r + FPADD32 10 ..... 110110 ..... 0 0101 0010 ..... @d_d_d + FPADD32s 10 ..... 110110 ..... 0 0101 0011 ..... @r_r_r + FPADD64 10 ..... 110110 ..... 0 0100 0010 ..... @d_d_d + FPSUB16 10 ..... 110110 ..... 0 0101 0100 ..... @d_d_d + FPSUB16s 10 ..... 110110 ..... 0 0101 0101 ..... @r_r_r + FPSUB32 10 ..... 110110 ..... 0 0101 0110 ..... @d_d_d + FPSUB32s 10 ..... 110110 ..... 0 0101 0111 ..... @r_r_r + FPSUB64 10 ..... 110110 ..... 0 0100 0110 ..... @d_d_d + + FPADDS16 10 ..... 110110 ..... 0 0101 1000 ..... @d_d_d + FPADDS16s 10 ..... 110110 ..... 0 0101 1001 ..... @r_r_r + FPADDS32 10 ..... 110110 ..... 0 0101 1010 ..... @d_d_d + FPADDS32s 10 ..... 110110 ..... 0 0101 1011 ..... @r_r_r + FPSUBS16 10 ..... 110110 ..... 0 0101 1100 ..... @d_d_d + FPSUBS16s 10 ..... 110110 ..... 0 0101 1101 ..... @r_r_r + FPSUBS32 10 ..... 110110 ..... 0 0101 1110 ..... @d_d_d + FPSUBS32s 10 ..... 110110 ..... 0 0101 1111 ..... @r_r_r + + FNORd 10 ..... 110110 ..... 0 0110 0010 ..... @d_d_d + FNORs 10 ..... 110110 ..... 0 0110 0011 ..... @r_r_r + FANDNOTd 10 ..... 110110 ..... 0 0110 0100 ..... @d_d_d # FANDNOT2d + FANDNOTs 10 ..... 110110 ..... 0 0110 0101 ..... @r_r_r # FANDNOT2s + FANDNOTd 10 ..... 110110 ..... 0 0110 1000 ..... @d_d_d_swap # ... 1d + FANDNOTs 10 ..... 110110 ..... 0 0110 1001 ..... @r_r_r_swap # ... 1s + FXORd 10 ..... 110110 ..... 0 0110 1100 ..... @d_d_d + FXORs 10 ..... 110110 ..... 0 0110 1101 ..... @r_r_r + FNANDd 10 ..... 110110 ..... 0 0110 1110 ..... @d_d_d + FNANDs 10 ..... 110110 ..... 0 0110 1111 ..... @r_r_r + FANDd 10 ..... 110110 ..... 0 0111 0000 ..... @d_d_d + FANDs 10 ..... 110110 ..... 0 0111 0001 ..... @r_r_r + FXNORd 10 ..... 110110 ..... 0 0111 0010 ..... @d_d_d + FXNORs 10 ..... 110110 ..... 0 0111 0011 ..... @r_r_r + FORNOTd 10 ..... 110110 ..... 0 0111 0110 ..... @d_d_d # FORNOT2d + FORNOTs 10 ..... 110110 ..... 0 0111 0111 ..... @r_r_r # FORNOT2s + FORNOTd 10 ..... 110110 ..... 0 0111 1010 ..... @d_d_d_swap # ... 1d + FORNOTs 10 ..... 110110 ..... 0 0111 1011 ..... @r_r_r_swap # ... 1s + FORd 10 ..... 110110 ..... 0 0111 1100 ..... @d_d_d + FORs 10 ..... 110110 ..... 0 0111 1101 ..... @r_r_r + + FZEROd 10 ..... 110110 00000 0 0110 0000 00000 rd=%dfp_rd + FZEROs 10 rd:5 110110 00000 0 0110 0001 00000 + FONEd 10 ..... 110110 00000 0 0111 1110 00000 rd=%dfp_rd + FONEs 10 rd:5 110110 00000 0 0111 1111 00000 + + MOVsTOuw 10 ..... 110110 00000 1 0001 0001 ..... @r_r2 + MOVsTOsw 10 ..... 110110 00000 1 0001 0011 ..... @r_r2 + MOVwTOs 10 ..... 110110 00000 1 0001 1001 ..... @r_r2 + MOVdTOx 10 ..... 110110 00000 1 0001 0000 ..... @r_d2 + MOVxTOd 10 ..... 110110 00000 1 0001 1000 ..... @d_r2 + + FPADD8 10 ..... 110110 ..... 1 0010 0100 ..... @d_d_d + FPADDS8 10 ..... 110110 ..... 1 0010 0110 ..... @d_d_d + FPADDUS8 10 ..... 110110 ..... 1 0010 0111 ..... @d_d_d + FPADDUS16 10 ..... 110110 ..... 1 0010 0011 ..... @d_d_d + FPSUB8 10 ..... 110110 ..... 1 0101 0100 ..... @d_d_d + FPSUBS8 10 ..... 110110 ..... 1 0101 0110 ..... @d_d_d + FPSUBUS8 10 ..... 110110 ..... 1 0101 0111 ..... @d_d_d + FPSUBUS16 10 ..... 110110 ..... 1 0101 0011 ..... @d_d_d + + FPMIN8 10 ..... 110110 ..... 1 0001 1010 ..... @d_d_d + FPMIN16 10 ..... 110110 ..... 1 0001 1011 ..... @d_d_d + FPMIN32 10 ..... 110110 ..... 1 0001 1100 ..... @d_d_d + FPMINU8 10 ..... 110110 ..... 1 0101 1010 ..... @d_d_d + FPMINU16 10 ..... 110110 ..... 1 0101 1011 ..... @d_d_d + FPMINU32 10 ..... 110110 ..... 1 0101 1100 ..... @d_d_d + + FPMAX8 10 ..... 110110 ..... 1 0001 1101 ..... @d_d_d + FPMAX16 10 ..... 110110 ..... 1 0001 1110 ..... @d_d_d + FPMAX32 10 ..... 110110 ..... 1 0001 1111 ..... @d_d_d + FPMAXU8 10 ..... 110110 ..... 1 0101 1101 ..... @d_d_d + FPMAXU16 10 ..... 110110 ..... 1 0101 1110 ..... @d_d_d + FPMAXU32 10 ..... 110110 ..... 1 0101 1111 ..... @d_d_d + + FLCMPs 10 000 cc:2 110110 rs1:5 1 0101 0001 rs2:5 + FLCMPd 10 000 cc:2 110110 ..... 1 0101 0010 ..... \ + rs1=%dfp_rs1 rs2=%dfp_rs2 + ] + NCP 10 ----- 110110 ----- --------- ----- # v8 CPop1 +} + +{ + [ + FMADDs 10 ..... 110111 ..... ..... 0001 ..... @r_r_r_r + FMADDd 10 ..... 110111 ..... ..... 0010 ..... @d_d_d_d + FMSUBs 10 ..... 110111 ..... ..... 0101 ..... @r_r_r_r + FMSUBd 10 ..... 110111 ..... ..... 0110 ..... @d_d_d_d + FNMSUBs 10 ..... 110111 ..... ..... 1001 ..... @r_r_r_r + FNMSUBd 10 ..... 110111 ..... ..... 1010 ..... @d_d_d_d + FNMADDs 10 ..... 110111 ..... ..... 1101 ..... @r_r_r_r + FNMADDd 10 ..... 110111 ..... ..... 1110 ..... @d_d_d_d + + FPMADDX 10 ..... 110111 ..... ..... 0000 ..... @d_d_d_d + FPMADDXHI 10 ..... 110111 ..... ..... 0100 ..... @d_d_d_d + ] + NCP 10 ----- 110111 ----- --------- ----- # v8 CPop2 +} + +## +## Major Opcode 11 -- load and store instructions +## + +&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 +LDXEFSR 11 00011 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 # v9 + STDFQ 11 ..... 100110 ..... . ............. @r_r_ri # v7,v8 +} +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..f2dd8bcb2e 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -21,10 +21,10 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "trace.h" +#include "exec/cpu_ldst.h" #include "exec/log.h" #include "sysemu/runstate.h" - static const char * const excp_names[0x80] = { [TT_TFAULT] = "Instruction Access Fault", [TT_ILL_INSN] = "Illegal Instruction", @@ -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; @@ -122,22 +116,9 @@ void sparc_cpu_do_interrupt(CPUState *cs) qemu_log("%6d: %s (v=%02x)\n", count, name, intno); log_cpu_state(cs, 0); -#if 0 - { - int i; - uint8_t *ptr; - - qemu_log(" code="); - ptr = (uint8_t *)env->pc; - for (i = 0; i < 16; i++) { - qemu_log(" %02x", ldub(ptr + i)); - } - qemu_log("\n"); - } -#endif count++; } -#if !defined(CONFIG_USER_ONLY) +#ifndef CONFIG_USER_ONLY if (env->psret == 0) { if (cs->exception_index == 0x80 && env->def.features & CPU_FEATURE_TA0_SHUTDOWN) { @@ -149,6 +130,29 @@ void sparc_cpu_do_interrupt(CPUState *cs) } return; } + if (intno == TT_FP_EXCP) { + /* + * The sparc32 fpu has three states related to exception handling. + * The FPop that signals an exception transitions from fp_execute + * to fp_exception_pending. A subsequent FPop transitions from + * fp_exception_pending to fp_exception, which forces the trap. + * + * If the queue is not empty, this trap is due to execution of an + * illegal FPop while in fp_exception state. Here we are to + * re-enter fp_exception_pending state without queuing the insn. + * + * We do not model the fp_exception_pending state, but instead + * skip directly to fp_exception state. We advance pc/npc to + * mimic delayed trap delivery as if by the subsequent insn. + */ + if (!env->fsr_qne) { + env->fsr_qne = FSR_QNE; + env->fq.s.addr = env->pc; + env->fq.s.insn = cpu_ldl_code(env, env->pc); + } + env->pc = env->npc; + env->npc = env->npc + 4; + } #endif env->psret = 0; cwp = cpu_cwp_dec(env, env->cwp - 1); @@ -165,7 +169,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..d92c9f1593 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -19,10 +19,12 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/range.h" #include "cpu.h" #include "tcg/tcg.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "exec/cpu_ldst.h" #include "asi.h" @@ -66,9 +68,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 @@ -242,9 +241,7 @@ static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, if (new_ctx == ctx) { uint64_t vaddr = tlb[i].tag & ~0x1fffULL; uint64_t size = 8192ULL << 3 * TTE_PGSIZE(tlb[i].tte); - if (new_vaddr == vaddr - || (new_vaddr < vaddr + size - && vaddr < new_vaddr + new_size)) { + if (ranges_overlap(new_vaddr, new_size, vaddr, size)) { DPRINTF_MMU("auto demap entry [%d] %lx->%lx\n", i, vaddr, new_vaddr); replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1); @@ -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(); @@ -1387,6 +1394,10 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, case ASI_TWINX_PL: /* Primary, twinx, LE */ case ASI_TWINX_S: /* Secondary, twinx */ case ASI_TWINX_SL: /* Secondary, twinx, LE */ + case ASI_MON_P: + case ASI_MON_S: + case ASI_MON_AIUP: + case ASI_MON_AIUS: /* These are always handled inline. */ g_assert_not_reached(); @@ -1663,7 +1674,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 +1686,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 +1703,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 +1715,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 +1944,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..222e5709c5 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,86 @@ 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, +}; +#else +static bool fq_needed(void *opaque) +{ + SPARCCPU *cpu = opaque; + return cpu->env.fsr_qne; +} + +static const VMStateDescription vmstate_fq = { + .name = "cpu/fq", + .version_id = 1, + .minimum_version_id = 1, + .needed = fq_needed, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(env.fq.s.addr, SPARCCPU), + VMSTATE_UINT32(env.fq.s.insn, SPARCCPU), + VMSTATE_END_OF_LIST() + }, +}; +#endif + static int cpu_pre_save(void *opaque) { SPARCCPU *cpu = opaque; @@ -111,7 +191,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 +201,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 +208,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 +241,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 +261,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), @@ -189,4 +283,11 @@ const VMStateDescription vmstate_sparc_cpu = { #endif VMSTATE_END_OF_LIST() }, +#ifndef TARGET_SPARC64 + .subsections = (const VMStateDescription * const []) { + &vmstate_fq, + NULL + }, +#endif + }; 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..9ff06026b8 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "qemu/qemu-print.h" #include "trace.h" @@ -64,10 +65,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 +80,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 +157,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 +189,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 +207,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 +221,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 +244,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 +353,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 +391,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 +541,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 +574,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 +612,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 +641,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 +676,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 +703,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 +717,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 +741,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 +758,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 +871,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 +896,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 +908,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 +924,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 34858eb95f..cdd0a95c03 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -21,86 +21,183 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "disas/disas.h" #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 "fpu/softfloat.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_fmul8x16a(D, S1, S2) 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_cmask8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_cmask16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_cmask32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpeq32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpgt8 ({ 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_fcmple8 ({ 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_fcmpne8 ({ 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_fcmpule8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpule16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpule32 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpugt8 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpugt16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fcmpugt32 ({ 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_fmul8x16 ({ 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_fslas16 ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_fslas32 ({ 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 gen_helper_xmulx ({ qemu_build_not_reached(); NULL; }) +# define gen_helper_xmulxhi ({ 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 -/* Floating point registers */ -static TCGv_i64 cpu_fpr[TARGET_DPREGS]; -#include "exec/gen-icount.h" +#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 comparison registers */ +static TCGv_i32 cpu_fcc[TARGET_FCCREGS]; + +#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 bool supervisor; #ifdef TARGET_SPARC64 bool hypervisor; +#else + bool fsr_qne; #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)) @@ -112,42 +209,12 @@ typedef struct { #define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1) #define GET_FIELD_SPs(x,a,b) sign_extend (GET_FIELD_SP(x,a,b), ((b) - (a) + 1)) -#ifdef TARGET_SPARC64 -#define DFPREG(r) (((r & 1) << 5) | (r & 0x1e)) -#define QFPREG(r) (((r & 1) << 5) | (r & 0x1c)) -#else -#define DFPREG(r) (r & 0x1e) -#define QFPREG(r) (r & 0x1c) -#endif - #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; @@ -161,166 +228,123 @@ 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) + +static int gen_offset_fpr_F(unsigned int reg) { -#if TCG_TARGET_REG_BITS == 32 - if (src & 1) { - return TCGV_LOW(cpu_fpr[src / 2]); + int ret; + + tcg_debug_assert(reg < 32); + ret= offsetof(CPUSPARCState, fpr[reg / 2]); + if (reg & 1) { + ret += offsetof(CPU_DoubleU, l.lower); } else { - return TCGV_HIGH(cpu_fpr[src / 2]); - } -#else - TCGv_i32 ret = get_temp_i32(dc); - if (src & 1) { - tcg_gen_extrl_i64_i32(ret, cpu_fpr[src / 2]); - } else { - tcg_gen_extrh_i64_i32(ret, cpu_fpr[src / 2]); + ret += offsetof(CPU_DoubleU, l.upper); } return ret; -#endif +} + +static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src) +{ + TCGv_i32 ret = tcg_temp_new_i32(); + tcg_gen_ld_i32(ret, tcg_env, gen_offset_fpr_F(src)); + return ret; } 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; - tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t, - (dst & 1 ? 0 : 32), 32); -#endif + tcg_gen_st_i32(v, tcg_env, gen_offset_fpr_F(dst)); gen_update_fprs_dirty(dc, dst); } -static TCGv_i32 gen_dest_fpr_F(DisasContext *dc) +static int gen_offset_fpr_D(unsigned int reg) { - return get_temp_i32(dc); + tcg_debug_assert(reg < 64); + tcg_debug_assert(reg % 2 == 0); + return offsetof(CPUSPARCState, fpr[reg / 2]); } static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) { - src = DFPREG(src); - return cpu_fpr[src / 2]; + TCGv_i64 ret = tcg_temp_new_i64(); + tcg_gen_ld_i64(ret, tcg_env, gen_offset_fpr_D(src)); + return ret; } static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) { - dst = DFPREG(dst); - tcg_gen_mov_i64(cpu_fpr[dst / 2], v); + tcg_gen_st_i64(v, tcg_env, gen_offset_fpr_D(dst)); gen_update_fprs_dirty(dc, dst); } -static TCGv_i64 gen_dest_fpr_D(DisasContext *dc, unsigned int dst) +static TCGv_i128 gen_load_fpr_Q(DisasContext *dc, unsigned int src) { - return cpu_fpr[DFPREG(dst) / 2]; + TCGv_i128 ret = tcg_temp_new_i128(); + TCGv_i64 h = gen_load_fpr_D(dc, src); + TCGv_i64 l = gen_load_fpr_D(dc, src + 2); + + tcg_gen_concat_i64_i128(ret, l, h); + return ret; } -static void gen_op_load_fpr_QT0(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, 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_i64 h = tcg_temp_new_i64(); + TCGv_i64 l = tcg_temp_new_i64(); + + tcg_gen_extr_i128_i64(l, h, v); + gen_store_fpr_D(dc, dst, h); + gen_store_fpr_D(dc, dst + 2, l); } -static void gen_op_load_fpr_QT1(unsigned int src) -{ - 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); - 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 +352,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 +378,194 @@ 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_addxc(TCGv dst, TCGv src1, TCGv src2) { - TCGv_i32 carry_32; - TCGv carry; + tcg_gen_add_tl(dst, src1, src2); + tcg_gen_add_tl(dst, dst, cpu_cc_C); +} - 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; +static void gen_op_addxccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_addcc_int(dst, src1, src2, cpu_cc_C); +} - case CC_OP_ADD: - case CC_OP_TADD: - case CC_OP_TADDTV: - carry_32 = gen_add32_carry32(); - break; +static void gen_op_subcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) +{ + TCGv z = tcg_constant_tl(0); - 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_subxc(TCGv dst, TCGv src1, TCGv src2) +{ + tcg_gen_sub_tl(dst, src1, src2); + tcg_gen_sub_tl(dst, dst, cpu_cc_C); +} - /* 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); +static void gen_op_subxccc(TCGv dst, TCGv src1, TCGv src2) +{ + gen_op_subcc_int(dst, src1, src2, cpu_cc_C); +} - // b2 = T0 & 1; - // env->y = (b2 << 31) | (env->y >> 1); +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(); + + tcg_gen_ext32u_tl(t_src1, src1); + tcg_gen_ext32u_tl(t_src2, src2); + + /* + * 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 +586,465 @@ 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_umulxhi(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_movi_tl(dst, 1); + TCGv discard = tcg_temp_new(); + tcg_gen_mulu2_tl(discard, dst, src1, src2); } -// Z -static inline void gen_op_eval_be(TCGv dst, TCGv_i32 src) +static void gen_op_fpmaddx(TCGv_i64 dst, TCGv_i64 src1, + TCGv_i64 src2, TCGv_i64 src3) { - gen_mov_reg_Z(dst, src); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_mul_i64(t, src1, src2); + tcg_gen_add_i64(dst, src3, t); } -// Z | (N ^ V) -static inline void gen_op_eval_ble(TCGv dst, TCGv_i32 src) +static void gen_op_fpmaddxhi(TCGv_i64 dst, TCGv_i64 src1, + TCGv_i64 src2, TCGv_i64 src3) { - 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 l = tcg_temp_new_i64(); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + + tcg_gen_mulu2_i64(l, h, src1, src2); + tcg_gen_add2_i64(l, dst, l, h, src3, z); } -// N ^ V -static inline void gen_op_eval_bl(TCGv dst, TCGv_i32 src) +static void gen_op_sdiv(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); +#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 } -// C | Z -static inline void gen_op_eval_bleu(TCGv dst, TCGv_i32 src) +static void gen_op_udivcc(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); + 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); } -// C -static inline void gen_op_eval_bcs(TCGv dst, TCGv_i32 src) +static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_C(dst, src); + 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); } -// V -static inline void gen_op_eval_bvs(TCGv dst, TCGv_i32 src) +static void gen_op_taddcctv(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_V(dst, src); + gen_helper_taddcctv(dst, tcg_env, src1, src2); } -// 0 -static inline void gen_op_eval_bn(TCGv dst) +static void gen_op_tsubcctv(TCGv dst, TCGv src1, TCGv src2) { - tcg_gen_movi_tl(dst, 0); + gen_helper_tsubcctv(dst, tcg_env, src1, src2); } -// N -static inline void gen_op_eval_bneg(TCGv dst, TCGv_i32 src) +static void gen_op_popc(TCGv dst, TCGv src1, TCGv src2) { - gen_mov_reg_N(dst, src); + tcg_gen_ctpop_tl(dst, src2); } -// !Z -static inline void gen_op_eval_bne(TCGv dst, TCGv_i32 src) +static void gen_op_lzcnt(TCGv dst, TCGv src) { - gen_mov_reg_Z(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + tcg_gen_clzi_tl(dst, src, TARGET_LONG_BITS); } -// !(Z | (N ^ V)) -static inline void gen_op_eval_bg(TCGv dst, TCGv_i32 src) +#ifndef TARGET_SPARC64 +static void gen_helper_array8(TCGv dst, TCGv src1, TCGv src2) { - gen_op_eval_ble(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + 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); } -// !(N ^ V) -static inline void gen_op_eval_bge(TCGv dst, TCGv_i32 src) +static void gen_op_array32(TCGv dst, TCGv src1, TCGv src2) { - gen_op_eval_bl(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + gen_helper_array8(dst, src1, src2); + tcg_gen_shli_tl(dst, dst, 2); } -// !(C | Z) -static inline void gen_op_eval_bgu(TCGv dst, TCGv_i32 src) +static void gen_op_fpack16(TCGv_i32 dst, TCGv_i64 src) { - gen_op_eval_bleu(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); +#ifdef TARGET_SPARC64 + gen_helper_fpack16(dst, cpu_gsr, src); +#else + g_assert_not_reached(); +#endif } -// !C -static inline void gen_op_eval_bcc(TCGv dst, TCGv_i32 src) +static void gen_op_fpackfix(TCGv_i32 dst, TCGv_i64 src) { - gen_mov_reg_C(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 } -// !N -static inline void gen_op_eval_bpos(TCGv dst, TCGv_i32 src) +static void gen_op_fpack32(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) { - gen_mov_reg_N(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 } -// !V -static inline void gen_op_eval_bvc(TCGv dst, TCGv_i32 src) +static void gen_op_fpadds16s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) { - gen_mov_reg_V(dst, src); - tcg_gen_xori_tl(dst, dst, 0x1); + TCGv_i32 t[2]; + + for (int i = 0; i < 2; i++) { + TCGv_i32 u = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_sextract_i32(u, src1, i * 16, 16); + tcg_gen_sextract_i32(v, src2, i * 16, 16); + tcg_gen_add_i32(u, u, v); + tcg_gen_smax_i32(u, u, tcg_constant_i32(INT16_MIN)); + tcg_gen_smin_i32(u, u, tcg_constant_i32(INT16_MAX)); + t[i] = u; + } + tcg_gen_deposit_i32(d, t[0], t[1], 16, 16); } -/* - 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) +static void gen_op_fpsubs16s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) { - tcg_gen_shri_tl(reg, src, FSR_FCC0_SHIFT + fcc_offset); - tcg_gen_andi_tl(reg, reg, 0x1); + TCGv_i32 t[2]; + + for (int i = 0; i < 2; i++) { + TCGv_i32 u = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_sextract_i32(u, src1, i * 16, 16); + tcg_gen_sextract_i32(v, src2, i * 16, 16); + tcg_gen_sub_i32(u, u, v); + tcg_gen_smax_i32(u, u, tcg_constant_i32(INT16_MIN)); + tcg_gen_smin_i32(u, u, tcg_constant_i32(INT16_MAX)); + t[i] = u; + } + tcg_gen_deposit_i32(d, t[0], t[1], 16, 16); } -static inline void gen_mov_reg_FCC1(TCGv reg, TCGv src, - unsigned int fcc_offset) +static void gen_op_fpadds32s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) { - tcg_gen_shri_tl(reg, src, FSR_FCC1_SHIFT + fcc_offset); - tcg_gen_andi_tl(reg, reg, 0x1); + TCGv_i32 r = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_add_i32(r, src1, src2); + tcg_gen_xor_i32(t, src1, src2); + tcg_gen_xor_i32(v, r, src2); + tcg_gen_andc_i32(v, v, t); + + tcg_gen_setcond_i32(TCG_COND_GE, t, r, z); + tcg_gen_addi_i32(t, t, INT32_MAX); + + tcg_gen_movcond_i32(TCG_COND_LT, d, v, z, t, r); } -// !0: FCC0 | FCC1 -static inline void gen_op_eval_fbne(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fpsubs32s(TCGv_i32 d, TCGv_i32 src1, TCGv_i32 src2) { - 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); + TCGv_i32 r = tcg_temp_new_i32(); + TCGv_i32 t = tcg_temp_new_i32(); + TCGv_i32 v = tcg_temp_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_sub_i32(r, src1, src2); + tcg_gen_xor_i32(t, src1, src2); + tcg_gen_xor_i32(v, r, src1); + tcg_gen_and_i32(v, v, t); + + tcg_gen_setcond_i32(TCG_COND_GE, t, r, z); + tcg_gen_addi_i32(t, t, INT32_MAX); + + tcg_gen_movcond_i32(TCG_COND_LT, d, v, z, t, r); } -// 1 or 2: FCC0 ^ FCC1 -static inline void gen_op_eval_fblg(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_faligndata_i(TCGv_i64 dst, TCGv_i64 s1, + TCGv_i64 s2, TCGv gsr) { - 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); +#ifdef TARGET_SPARC64 + TCGv t1, t2, shift; + + 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); +#else + g_assert_not_reached(); +#endif } -// 1 or 3: FCC0 -static inline void gen_op_eval_fbul(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_faligndata_g(TCGv_i64 dst, TCGv_i64 s1, TCGv_i64 s2) { - gen_mov_reg_FCC0(dst, src, fcc_offset); + gen_op_faligndata_i(dst, s1, s2, cpu_gsr); } -// 1: FCC0 & !FCC1 -static inline void gen_op_eval_fbl(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_bshuffle(TCGv_i64 dst, TCGv_i64 src1, TCGv_i64 src2) { - 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); +#ifdef TARGET_SPARC64 + gen_helper_bshuffle(dst, cpu_gsr, src1, src2); +#else + g_assert_not_reached(); +#endif } -// 2 or 3: FCC1 -static inline void gen_op_eval_fbug(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_pdistn(TCGv dst, TCGv_i64 src1, TCGv_i64 src2) { - gen_mov_reg_FCC1(dst, src, fcc_offset); +#ifdef TARGET_SPARC64 + gen_helper_pdist(dst, tcg_constant_i64(0), src1, src2); +#else + g_assert_not_reached(); +#endif } -// 2: !FCC0 & FCC1 -static inline void gen_op_eval_fbg(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fmul8x16al(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { - 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); + tcg_gen_ext16s_i32(src2, src2); + gen_helper_fmul8x16a(dst, src1, src2); } -// 3: FCC0 & FCC1 -static inline void gen_op_eval_fbu(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fmul8x16au(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { - 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); + tcg_gen_sari_i32(src2, src2, 16); + gen_helper_fmul8x16a(dst, src1, src2); } -// 0: !(FCC0 | FCC1) -static inline void gen_op_eval_fbe(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fmuld8ulx16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { - 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); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_ext8u_i32(t0, src1); + tcg_gen_ext16s_i32(t1, src2); + tcg_gen_mul_i32(t0, t0, t1); + + tcg_gen_extract_i32(t1, src1, 16, 8); + tcg_gen_sextract_i32(t2, src2, 16, 16); + tcg_gen_mul_i32(t1, t1, t2); + + tcg_gen_concat_i32_i64(dst, t0, t1); } -// 0 or 3: !(FCC0 ^ FCC1) -static inline void gen_op_eval_fbue(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fmuld8sux16(TCGv_i64 dst, TCGv_i32 src1, TCGv_i32 src2) { - 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); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + /* + * The insn description talks about extracting the upper 8 bits + * of the signed 16-bit input rs1, performing the multiply, then + * shifting left by 8 bits. Instead, zap the lower 8 bits of + * the rs1 input, which avoids the need for two shifts. + */ + tcg_gen_ext16s_i32(t0, src1); + tcg_gen_andi_i32(t0, t0, ~0xff); + tcg_gen_ext16s_i32(t1, src2); + tcg_gen_mul_i32(t0, t0, t1); + + tcg_gen_sextract_i32(t1, src1, 16, 16); + tcg_gen_andi_i32(t1, t1, ~0xff); + tcg_gen_sextract_i32(t2, src2, 16, 16); + tcg_gen_mul_i32(t1, t1, t2); + + tcg_gen_concat_i32_i64(dst, t0, t1); } -// 0 or 2: !FCC0 -static inline void gen_op_eval_fbge(TCGv dst, TCGv src, - unsigned int fcc_offset) +#ifdef TARGET_SPARC64 +static void gen_vec_fchksm16(unsigned vece, TCGv_vec dst, + TCGv_vec src1, TCGv_vec src2) { - gen_mov_reg_FCC0(dst, src, fcc_offset); - tcg_gen_xori_tl(dst, dst, 0x1); + TCGv_vec a = tcg_temp_new_vec_matching(dst); + TCGv_vec c = tcg_temp_new_vec_matching(dst); + + tcg_gen_add_vec(vece, a, src1, src2); + tcg_gen_cmp_vec(TCG_COND_LTU, vece, c, a, src1); + /* Vector cmp produces -1 for true, so subtract to add carry. */ + tcg_gen_sub_vec(vece, dst, a, c); } -// !1: !(FCC0 & !FCC1) -static inline void gen_op_eval_fbuge(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fchksm16(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { - 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); + static const TCGOpcode vecop_list[] = { + INDEX_op_cmp_vec, INDEX_op_add_vec, INDEX_op_sub_vec, + }; + static const GVecGen3 op = { + .fni8 = gen_helper_fchksm16, + .fniv = gen_vec_fchksm16, + .opt_opc = vecop_list, + .vece = MO_16, + }; + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &op); } -// 0 or 1: !FCC1 -static inline void gen_op_eval_fble(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_vec_fmean16(unsigned vece, TCGv_vec dst, + TCGv_vec src1, TCGv_vec src2) { - gen_mov_reg_FCC1(dst, src, fcc_offset); - tcg_gen_xori_tl(dst, dst, 0x1); + TCGv_vec t = tcg_temp_new_vec_matching(dst); + + tcg_gen_or_vec(vece, t, src1, src2); + tcg_gen_and_vec(vece, t, t, tcg_constant_vec_matching(dst, vece, 1)); + tcg_gen_sari_vec(vece, src1, src1, 1); + tcg_gen_sari_vec(vece, src2, src2, 1); + tcg_gen_add_vec(vece, dst, src1, src2); + tcg_gen_add_vec(vece, dst, dst, t); } -// !2: !(!FCC0 & FCC1) -static inline void gen_op_eval_fbule(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void gen_op_fmean16(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { - 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); + static const TCGOpcode vecop_list[] = { + INDEX_op_add_vec, INDEX_op_sari_vec, + }; + static const GVecGen3 op = { + .fni8 = gen_helper_fmean16, + .fniv = gen_vec_fmean16, + .opt_opc = vecop_list, + .vece = MO_16, + }; + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &op); } +#else +#define gen_op_fchksm16 ({ qemu_build_not_reached(); NULL; }) +#define gen_op_fmean16 ({ qemu_build_not_reached(); NULL; }) +#endif -// !3: !(FCC0 & FCC1) -static inline void gen_op_eval_fbo(TCGv dst, TCGv src, - unsigned int fcc_offset) +static void finishing_insn(DisasContext *dc) { - 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,973 +1052,470 @@ 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_fmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - 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; - } + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(0)); } -#else - -static inline void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2) +static void gen_op_fmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - gen_helper_fcmps(cpu_fsr, cpu_env, r_rs1, r_rs2); + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(0)); } -static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - gen_helper_fcmpd(cpu_fsr, cpu_env, r_rs1, r_rs2); + int op = float_muladd_negate_c; + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); } -static inline void gen_op_fcmpq(int fccno) +static void gen_op_fmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - gen_helper_fcmpq(cpu_fsr, cpu_env); + int op = float_muladd_negate_c; + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); } -static inline void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2) +static void gen_op_fnmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - gen_helper_fcmpes(cpu_fsr, cpu_env, r_rs1, r_rs2); + int op = float_muladd_negate_c | float_muladd_negate_result; + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); } -static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +static void gen_op_fnmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - gen_helper_fcmped(cpu_fsr, cpu_env, r_rs1, r_rs2); + int op = float_muladd_negate_c | float_muladd_negate_result; + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); } -static inline void gen_op_fcmpeq(int fccno) +static void gen_op_fnmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3) { - gen_helper_fcmpeq(cpu_fsr, cpu_env); + int op = float_muladd_negate_result; + gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); } -#endif -static void gen_op_fpexception_im(DisasContext *dc, int fsr_flags) +static void gen_op_fnmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3) { - tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_NMASK); - tcg_gen_ori_tl(cpu_fsr, cpu_fsr, fsr_flags); + int op = float_muladd_negate_result; + gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op)); +} + +/* Use muladd to compute (1 * src1) + src2 / 2 with one rounding. */ +static void gen_op_fhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) +{ + TCGv_i32 one = tcg_constant_i32(float32_one); + int op = float_muladd_halve_result; + gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 one = tcg_constant_i64(float64_one); + int op = float_muladd_halve_result; + gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +/* Use muladd to compute (1 * src1) - src2 / 2 with one rounding. */ +static void gen_op_fhsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) +{ + TCGv_i32 one = tcg_constant_i32(float32_one); + int op = float_muladd_negate_c | float_muladd_halve_result; + gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fhsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 one = tcg_constant_i64(float64_one); + int op = float_muladd_negate_c | float_muladd_halve_result; + gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +/* Use muladd to compute -((1 * src1) + src2 / 2) with one rounding. */ +static void gen_op_fnhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2) +{ + TCGv_i32 one = tcg_constant_i32(float32_one); + int op = float_muladd_negate_result | float_muladd_halve_result; + gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fnhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2) +{ + TCGv_i64 one = tcg_constant_i64(float64_one); + int op = float_muladd_negate_result | float_muladd_halve_result; + gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op)); +} + +static void gen_op_fpexception_im(DisasContext *dc, int ftt) +{ + /* + * 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); } -static int gen_trap_ifnofpu(DisasContext *dc) +static bool gen_trap_ifnofpu(DisasContext *dc) { #if !defined(CONFIG_USER_ONLY) if (!dc->fpu_enabled) { gen_exception(dc, TT_NFPU_INSN); - return 1; + return true; } #endif - return 0; + return false; } -static inline void gen_op_clear_ieee_excp_and_FTT(void) +static bool gen_trap_iffpexception(DisasContext *dc) { - 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); -} +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + /* + * There are 3 states for the sparc32 fpu: + * Normally the fpu is in fp_execute, and all insns are allowed. + * When an exception is signaled, it moves to fp_exception_pending state. + * Upon seeing the next FPop, the fpu moves to fp_exception state, + * populates the FQ, and generates an fp_exception trap. + * The fpu remains in fp_exception state until FQ becomes empty + * after execution of a STDFQ instruction. While the fpu is in + * fp_exception state, and FPop, fp load or fp branch insn will + * return to fp_exception_pending state, set FSR.FTT to sequence_error, + * and the insn will not be entered into the FQ. + * + * In QEMU, we do not model the fp_exception_pending state and + * instead populate FQ and raise the exception immediately. + * But we can still honor fp_exception state by noticing when + * the FQ is not empty. + */ + if (dc->fsr_qne) { + gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); + return true; + } #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); + return false; } -#ifdef TARGET_SPARC64 -static inline void gen_ne_fop_DD(DisasContext *dc, int rd, int rs, - void (*gen)(TCGv_i64, TCGv_i64)) +static bool gen_trap_if_nofpu_fpexception(DisasContext *dc) { - 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); + return gen_trap_ifnofpu(dc) || gen_trap_iffpexception(dc); } /* 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 +1529,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 +1558,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 +1598,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. */ @@ -2138,6 +1642,7 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) case ASI_BLK_AIUP_L_4V: case ASI_BLK_AIUP: case ASI_BLK_AIUPL: + case ASI_MON_AIUP: mem_idx = MMU_USER_IDX; break; case ASI_AIUS: /* As if user secondary */ @@ -2148,6 +1653,7 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) case ASI_BLK_AIUS_L_4V: case ASI_BLK_AIUS: case ASI_BLK_AIUSL: + case ASI_MON_AIUS: mem_idx = MMU_USER_SECONDARY_IDX; break; case ASI_S: /* Secondary */ @@ -2161,6 +1667,7 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) case ASI_FL8_SL: case ASI_FL16_S: case ASI_FL16_SL: + case ASI_MON_S: if (mem_idx == MMU_USER_IDX) { mem_idx = MMU_USER_SECONDARY_IDX; } else if (mem_idx == MMU_KERNEL_IDX) { @@ -2178,6 +1685,7 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) case ASI_FL8_PL: case ASI_FL16_P: case ASI_FL16_PL: + case ASI_MON_P: break; } switch (asi) { @@ -2195,6 +1703,10 @@ static DisasASI get_asi(DisasContext *dc, int insn, MemOp memop) case ASI_SL: case ASI_P: case ASI_PL: + case ASI_MON_P: + case ASI_MON_S: + case ASI_MON_AIUP: + case ASI_MON_AIUS: type = GET_ASI_DIRECT; break; case ASI_TWINX_REAL: @@ -2253,123 +1765,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 +1908,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 +1925,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 +1942,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 +1976,49 @@ 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_i64 d64, l64; + 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); - break; - case 16: + + case MO_64: 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_mov_i64(cpu_fpr[rd / 2], d64); - tcg_temp_free_i64(d64); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop); + gen_store_fpr_D(dc, rd, d64); + break; + + case MO_128: + d64 = tcg_temp_new_i64(); + l64 = tcg_temp_new_i64(); + 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(l64, addr_tmp, da->mem_idx, memop); + gen_store_fpr_D(dc, rd, d64); + gen_store_fpr_D(dc, rd + 2, l64); break; default: g_assert_not_reached(); @@ -2500,26 +2027,20 @@ 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(); + d64 = tcg_temp_new_i64(); + for (int i = 0; ; ++i) { + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, + memop | (i == 0 ? MO_ALIGN_64 : 0)); + gen_store_fpr_D(dc, rd + 2 * i, d64); 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 +2048,10 @@ 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) { + d64 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(d64, addr, da->mem_idx, memop | MO_ALIGN); + gen_store_fpr_D(dc, rd, d64); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2537,8 +2059,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 +2068,77 @@ 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); - break; - case 16: + case MO_64: 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); - tcg_gen_mov_i64(cpu_fpr[rd / 2], d64); - tcg_temp_free_i64(d64); + gen_helper_ld_asi(d64, tcg_env, addr, r_asi, r_mop); + gen_store_fpr_D(dc, rd, d64); + break; + case MO_128: + d64 = tcg_temp_new_i64(); + l64 = tcg_temp_new_i64(); + 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(l64, tcg_env, addr_tmp, r_asi, r_mop); + gen_store_fpr_D(dc, rd, d64); + gen_store_fpr_D(dc, rd + 2, l64); 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_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: + 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: + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, 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); + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | MO_ALIGN_16); + addr_tmp = tcg_temp_new(); + tcg_gen_addi_tl(addr_tmp, addr, 8); + d64 = gen_load_fpr_D(dc, rd + 2); + tcg_gen_qemu_st_i64(d64, addr_tmp, da->mem_idx, memop); break; default: g_assert_not_reached(); @@ -2614,26 +2147,19 @@ 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) { + d64 = gen_load_fpr_D(dc, rd + 2 * i); + tcg_gen_qemu_st_i64(d64, 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 +2167,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) { + d64 = gen_load_fpr_D(dc, rd); + tcg_gen_qemu_st_i64(d64, addr, da->mem_idx, memop | MO_ALIGN); } else { gen_exception(dc, TT_ILL_INSN); } @@ -2658,64 +2184,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 +2280,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 +2319,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,3067 +2353,3316 @@ 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) { - TCGv_i64 dst = gen_dest_fpr_D(dc, rd); - tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, cmp->c2, +#ifdef TARGET_SPARC64 + TCGv_i64 dst = tcg_temp_new_i64(); + 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) { - int qd = QFPREG(rd); - int qs = QFPREG(rs); +#ifdef TARGET_SPARC64 + TCGv c2 = tcg_constant_tl(cmp->c2); + TCGv_i64 h = tcg_temp_new_i64(); + TCGv_i64 l = tcg_temp_new_i64(); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, cmp->c2, - cpu_fpr[qs / 2], cpu_fpr[qd / 2]); - tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, cmp->c2, - cpu_fpr[qs / 2 + 1], cpu_fpr[qd / 2 + 1]); - - gen_update_fprs_dirty(dc, qd); + tcg_gen_movcond_i64(cmp->cond, h, cmp->c1, c2, + gen_load_fpr_D(dc, rs), + gen_load_fpr_D(dc, rd)); + tcg_gen_movcond_i64(cmp->cond, l, cmp->c1, c2, + gen_load_fpr_D(dc, rs + 2), + gen_load_fpr_D(dc, rd + 2)); + gen_store_fpr_D(dc, rd, h); + gen_store_fpr_D(dc, rd + 2, l); +#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, - int width, bool cc, bool left) +static int extract_dfpreg(DisasContext *dc, int x) { - TCGv lo1, lo2, t1, t2; - uint64_t amask, tabl, tabr; - int shift, imask, omask; + int r = x & 0x1e; +#ifdef TARGET_SPARC64 + r |= (x & 1) << 5; +#endif + return r; +} - 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; +static int extract_qfpreg(DisasContext *dc, int x) +{ + int r = x & 0x1c; +#ifdef TARGET_SPARC64 + r |= (x & 1) << 5; +#endif + return r; +} + +/* 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_FMAF(C) ((C)->def->features & CPU_FEATURE_FMAF) +# define avail_GL(C) ((C)->def->features & CPU_FEATURE_GL) +# define avail_HYPV(C) ((C)->def->features & CPU_FEATURE_HYPV) +# define avail_IMA(C) ((C)->def->features & CPU_FEATURE_IMA) +# define avail_VIS1(C) ((C)->def->features & CPU_FEATURE_VIS1) +# define avail_VIS2(C) ((C)->def->features & CPU_FEATURE_VIS2) +# define avail_VIS3(C) ((C)->def->features & CPU_FEATURE_VIS3) +# define avail_VIS3B(C) avail_VIS3(C) +# define avail_VIS4(C) ((C)->def->features & CPU_FEATURE_VIS4) +#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_FMAF(C) false +# define avail_GL(C) false +# define avail_HYPV(C) false +# define avail_IMA(C) false +# define avail_VIS1(C) false +# define avail_VIS2(C) false +# define avail_VIS3(C) false +# define avail_VIS3B(C) false +# define avail_VIS4(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; } - /* 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. */ + 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_if_nofpu_fpexception(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_wrmwait(DisasContext *dc, TCGv src) +{ + /* + * TODO: This is a stub version of mwait, which merely recognizes + * interrupts immediately and does not wait. + */ + dc->base.is_jmp = DISAS_EXIT; +} + +TRANS(WRMWAIT, VIS4, do_wr_special, a, true, do_wrmwait) + +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 little_endian) +{ + TCGv dst, s1, s2, l, r, t, m; + uint64_t amask = address_mask_i(dc, -8); + + dst = gen_dest_gpr(dc, a->rd); + s1 = gen_load_gpr(dc, a->rs1); + s2 = gen_load_gpr(dc, a->rs2); + + if (cc) { + gen_op_subcc(cpu_cc_N, s1, s2); + } + + l = tcg_temp_new(); + r = tcg_temp_new(); + t = tcg_temp_new(); + switch (width) { case 8: - imask = 0x7; - shift = 3; - omask = 0xff; - if (left) { - tabl = 0x80c0e0f0f8fcfeffULL; - tabr = 0xff7f3f1f0f070301ULL; - } else { - tabl = 0x0103070f1f3f7fffULL; - tabr = 0xfffefcf8f0e0c080ULL; - } + tcg_gen_andi_tl(l, s1, 7); + tcg_gen_andi_tl(r, s2, 7); + tcg_gen_xori_tl(r, r, 7); + m = tcg_constant_tl(0xff); break; case 16: - imask = 0x6; - shift = 1; - omask = 0xf; - if (left) { - tabl = 0x8cef; - tabr = 0xf731; - } else { - tabl = 0x137f; - tabr = 0xfec8; - } + tcg_gen_extract_tl(l, s1, 1, 2); + tcg_gen_extract_tl(r, s2, 1, 2); + tcg_gen_xori_tl(r, r, 3); + m = tcg_constant_tl(0xf); break; case 32: - imask = 0x4; - shift = 0; - omask = 0x3; - if (left) { - tabl = (2 << 2) | 3; - tabr = (3 << 2) | 1; - } else { - tabl = (1 << 2) | 3; - tabr = (3 << 2) | 2; - } + tcg_gen_extract_tl(l, s1, 2, 1); + tcg_gen_extract_tl(r, s2, 2, 1); + tcg_gen_xori_tl(r, r, 1); + m = tcg_constant_tl(0x3); break; default: abort(); } - lo1 = tcg_temp_new(); - lo2 = tcg_temp_new(); - tcg_gen_andi_tl(lo1, s1, imask); - tcg_gen_andi_tl(lo2, s2, imask); - 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_andi_tl(lo2, lo2, omask); - - amask = -8; - if (AM_CHECK(dc)) { - amask &= 0xffffffffULL; + /* Compute Left Edge */ + if (little_endian) { + tcg_gen_shl_tl(l, m, l); + tcg_gen_and_tl(l, l, m); + } else { + tcg_gen_shr_tl(l, m, l); + } + /* Compute Right Edge */ + if (little_endian) { + tcg_gen_shr_tl(r, m, r); + } else { + tcg_gen_shl_tl(r, m, r); + tcg_gen_and_tl(r, r, m); } - 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 under amask ? l : l & r) */ + tcg_gen_xor_tl(t, s1, s2); + tcg_gen_and_tl(r, r, l); + tcg_gen_movcond_tl(TCG_COND_TSTEQ, dst, t, tcg_constant_tl(amask), r, l); - 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_rr(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv, TCGv)) { + TCGv dst = gen_dest_gpr(dc, a->rd); + TCGv src = gen_load_gpr(dc, a->rs); + + func(dst, src); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(LZCNT, VIS3, do_rr, a, gen_op_lzcnt) + +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) + +TRANS(ADDXC, VIS3, do_rrr, a, gen_op_addxc) +TRANS(ADDXCcc, VIS3, do_rrr, a, gen_op_addxccc) + +TRANS(SUBXC, VIS4, do_rrr, a, gen_op_subxc) +TRANS(SUBXCcc, VIS4, do_rrr, a, gen_op_subxccc) + +TRANS(UMULXHI, VIS3, do_rrr, a, gen_op_umulxhi) + +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_cmask(DisasContext *dc, int rs2, void (*func)(TCGv, TCGv, TCGv)) +{ + func(cpu_gsr, cpu_gsr, gen_load_gpr(dc, rs2)); + return true; +} - if (cond == 0) { - /* Trap never. */ - break; - } +TRANS(CMASK8, VIS3, do_cmask, a->rs2, gen_helper_cmask8) +TRANS(CMASK16, VIS3, do_cmask, a->rs2, gen_helper_cmask16) +TRANS(CMASK32, VIS3, do_cmask, a->rs2, gen_helper_cmask32) - save_state(dc); +static bool do_shift_r(DisasContext *dc, arg_shiftr *a, bool l, bool u) +{ + TCGv dst, src1, src2; - 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_if_nofpu_fpexception(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; + } + /* Store insns are ok in fp_exception_pending state. */ + 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, 64, 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) +{ + TCGv addr; + + if (!avail_32(dc)) { + return false; + } + addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm); + if (addr == NULL) { + return false; + } + if (!supervisor(dc)) { + return raise_priv(dc); + } +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + if (gen_trap_ifnofpu(dc)) { + return true; + } + if (!dc->fsr_qne) { + gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR); + return true; + } + + /* Store the single element from the queue. */ + TCGv_i64 fq = tcg_temp_new_i64(); + tcg_gen_ld_i64(fq, tcg_env, offsetof(CPUSPARCState, fq.d)); + tcg_gen_qemu_st_i64(fq, addr, dc->mem_idx, MO_TEUQ | MO_ALIGN_4); + + /* Mark the queue empty, transitioning to fp_execute state. */ + tcg_gen_st_i32(tcg_constant_i32(0), tcg_env, + offsetof(CPUSPARCState, fsr_qne)); + dc->fsr_qne = 0; + + return advance_pc(dc); +#else + qemu_build_not_reached(); +#endif +} + +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_if_nofpu_fpexception(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 do_ldxfsr(DisasContext *dc, arg_r_r_ri *a, bool entire) +{ +#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_if_nofpu_fpexception(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); + + if (entire) { + gen_helper_set_fsr_nofcc(tcg_env, lo); + } else { + gen_helper_set_fsr_nofcc_noftt(tcg_env, lo); + } + return advance_pc(dc); +#else + return false; +#endif +} + +TRANS(LDXFSR, 64, do_ldxfsr, a, false) +TRANS(LDXEFSR, VIS3B, do_ldxfsr, a, true) + +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; + } + /* Store insns are ok in fp_exception_pending state. */ + 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, int32_t c) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + gen_store_fpr_F(dc, rd, tcg_constant_i32(c)); + 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; + } + gen_store_fpr_D(dc, rd, tcg_constant_i64(c)); + 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_if_nofpu_fpexception(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_if_nofpu_fpexception(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_if_nofpu_fpexception(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_if_nofpu_fpexception(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + 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_if_nofpu_fpexception(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + 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_df(DisasContext *dc, arg_r_r *a, + void (*func)(TCGv_i64, TCGv_i32)) +{ + TCGv_i64 dst; + TCGv_i32 src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src = gen_load_fpr_F(dc, a->rs); + func(dst, src); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FEXPAND, VIS1, do_df, a, gen_helper_fexpand) + +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_if_nofpu_fpexception(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + 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_if_nofpu_fpexception(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_if_nofpu_fpexception(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_if_nofpu_fpexception(dc)) { + return true; + } + if (gen_trap_float128(dc)) { + return true; + } + + src = gen_load_fpr_Q(dc, a->rs); + dst = tcg_temp_new_i64(); + 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_if_nofpu_fpexception(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_if_nofpu_fpexception(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) + +TRANS(FHADDs, VIS3, do_fff, a, gen_op_fhadds) +TRANS(FHSUBs, VIS3, do_fff, a, gen_op_fhsubs) +TRANS(FNHADDs, VIS3, do_fff, a, gen_op_fnhadds) + +TRANS(FPADDS16s, VIS3, do_fff, a, gen_op_fpadds16s) +TRANS(FPSUBS16s, VIS3, do_fff, a, gen_op_fpsubs16s) +TRANS(FPADDS32s, VIS3, do_fff, a, gen_op_fpadds32s) +TRANS(FPSUBS32s, VIS3, do_fff, a, gen_op_fpsubs32s) + +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_if_nofpu_fpexception(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) +TRANS(FNADDs, VIS3, do_env_fff, a, gen_helper_fnadds) +TRANS(FNMULs, VIS3, do_env_fff, a, gen_helper_fnmuls) + +static bool do_dff(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i32, TCGv_i32)) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + func(dst, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMUL8x16AU, VIS1, do_dff, a, gen_op_fmul8x16au) +TRANS(FMUL8x16AL, VIS1, do_dff, a, gen_op_fmul8x16al) +TRANS(FMULD8SUx16, VIS1, do_dff, a, gen_op_fmuld8sux16) +TRANS(FMULD8ULx16, VIS1, do_dff, a, gen_op_fmuld8ulx16) +TRANS(FPMERGE, VIS1, do_dff, a, gen_helper_fpmerge) + +static bool do_dfd(DisasContext *dc, arg_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i32, TCGv_i64)) +{ + TCGv_i64 dst, src2; + TCGv_i32 src1; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_F(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_dfd, a, gen_helper_fmul8x16) + +static bool do_gvec_ddd(DisasContext *dc, arg_r_r_r *a, MemOp vece, + void (*func)(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t)) +{ + if (gen_trap_ifnofpu(dc)) { + return true; + } + + func(vece, gen_offset_fpr_D(a->rd), gen_offset_fpr_D(a->rs1), + gen_offset_fpr_D(a->rs2), 8, 8); + return advance_pc(dc); +} + +TRANS(FPADD8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_add) +TRANS(FPADD16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_add) +TRANS(FPADD32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_add) + +TRANS(FPSUB8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_sub) +TRANS(FPSUB16, VIS1, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sub) +TRANS(FPSUB32, VIS1, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sub) + +TRANS(FCHKSM16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fchksm16) +TRANS(FMEAN16, VIS3, do_gvec_ddd, a, MO_16, gen_op_fmean16) + +TRANS(FPADDS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_ssadd) +TRANS(FPADDS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_ssadd) +TRANS(FPADDS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_ssadd) +TRANS(FPADDUS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_usadd) +TRANS(FPADDUS16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_usadd) + +TRANS(FPSUBS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_sssub) +TRANS(FPSUBS16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sssub) +TRANS(FPSUBS32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sssub) +TRANS(FPSUBUS8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_ussub) +TRANS(FPSUBUS16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_ussub) + +TRANS(FSLL16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_shlv) +TRANS(FSLL32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_shlv) +TRANS(FSRL16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_shrv) +TRANS(FSRL32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_shrv) +TRANS(FSRA16, VIS3, do_gvec_ddd, a, MO_16, tcg_gen_gvec_sarv) +TRANS(FSRA32, VIS3, do_gvec_ddd, a, MO_32, tcg_gen_gvec_sarv) + +TRANS(FPMIN8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_smin) +TRANS(FPMIN16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_smin) +TRANS(FPMIN32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_smin) +TRANS(FPMINU8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_umin) +TRANS(FPMINU16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_umin) +TRANS(FPMINU32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_umin) + +TRANS(FPMAX8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_smax) +TRANS(FPMAX16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_smax) +TRANS(FPMAX32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_smax) +TRANS(FPMAXU8, VIS4, do_gvec_ddd, a, MO_8, tcg_gen_gvec_umax) +TRANS(FPMAXU16, VIS4, do_gvec_ddd, a, MO_16, tcg_gen_gvec_umax) +TRANS(FPMAXU32, VIS4, do_gvec_ddd, a, MO_32, tcg_gen_gvec_umax) + +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 = tcg_temp_new_i64(); + 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(FMUL8SUx16, VIS1, do_ddd, a, gen_helper_fmul8sux16) +TRANS(FMUL8ULx16, VIS1, do_ddd, a, gen_helper_fmul8ulx16) + +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_g) +TRANS(BSHUFFLE, VIS2, do_ddd, a, gen_op_bshuffle) + +TRANS(FHADDd, VIS3, do_ddd, a, gen_op_fhaddd) +TRANS(FHSUBd, VIS3, do_ddd, a, gen_op_fhsubd) +TRANS(FNHADDd, VIS3, do_ddd, a, gen_op_fnhaddd) + +TRANS(FPADD64, VIS3B, do_ddd, a, tcg_gen_add_i64) +TRANS(FPSUB64, VIS3B, do_ddd, a, tcg_gen_sub_i64) +TRANS(FSLAS16, VIS3, do_ddd, a, gen_helper_fslas16) +TRANS(FSLAS32, VIS3, do_ddd, a, gen_helper_fslas32) + +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(FPCMPULE16, VIS4, do_rdd, a, gen_helper_fcmpule16) +TRANS(FPCMPUGT16, VIS4, do_rdd, a, gen_helper_fcmpugt16) + +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) +TRANS(FPCMPULE32, VIS4, do_rdd, a, gen_helper_fcmpule32) +TRANS(FPCMPUGT32, VIS4, do_rdd, a, gen_helper_fcmpugt32) + +TRANS(FPCMPEQ8, VIS3B, do_rdd, a, gen_helper_fcmpeq8) +TRANS(FPCMPNE8, VIS3B, do_rdd, a, gen_helper_fcmpne8) +TRANS(FPCMPULE8, VIS3B, do_rdd, a, gen_helper_fcmpule8) +TRANS(FPCMPUGT8, VIS3B, do_rdd, a, gen_helper_fcmpugt8) +TRANS(FPCMPLE8, VIS4, do_rdd, a, gen_helper_fcmple8) +TRANS(FPCMPGT8, VIS4, do_rdd, a, gen_helper_fcmpgt8) + +TRANS(PDISTN, VIS3, do_rdd, a, gen_op_pdistn) +TRANS(XMULX, VIS3, do_rrr, a, gen_helper_xmulx) +TRANS(XMULXHI, VIS3, do_rrr, a, gen_helper_xmulxhi) + +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_if_nofpu_fpexception(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + 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) +TRANS(FNADDd, VIS3, do_env_ddd, a, gen_helper_fnaddd) +TRANS(FNMULd, VIS3, do_env_ddd, a, gen_helper_fnmuld) + +static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (gen_trap_if_nofpu_fpexception(dc)) { + return true; + } + if (!(dc->def->features & CPU_FEATURE_FSMULD)) { + return raise_unimpfpop(dc); + } + + dst = tcg_temp_new_i64(); + 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 trans_FNsMULd(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst; + TCGv_i32 src1, src2; + + if (!avail_VIS3(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + gen_helper_fnsmuld(dst, tcg_env, src1, src2); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +static bool do_ffff(DisasContext *dc, arg_r_r_r_r *a, + void (*func)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 dst, src1, src2, src3; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + src1 = gen_load_fpr_F(dc, a->rs1); + src2 = gen_load_fpr_F(dc, a->rs2); + src3 = gen_load_fpr_F(dc, a->rs3); + dst = tcg_temp_new_i32(); + func(dst, src1, src2, src3); + gen_store_fpr_F(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(FMADDs, FMAF, do_ffff, a, gen_op_fmadds) +TRANS(FMSUBs, FMAF, do_ffff, a, gen_op_fmsubs) +TRANS(FNMSUBs, FMAF, do_ffff, a, gen_op_fnmsubs) +TRANS(FNMADDs, FMAF, do_ffff, a, gen_op_fnmadds) + +static bool do_dddd(DisasContext *dc, arg_r_r_r_r *a, + void (*func)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i64 dst, src1, src2, src3; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_D(dc, a->rs1); + src2 = gen_load_fpr_D(dc, a->rs2); + src3 = gen_load_fpr_D(dc, a->rs3); + func(dst, src1, src2, src3); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(PDIST, VIS1, do_dddd, a, gen_helper_pdist) +TRANS(FMADDd, FMAF, do_dddd, a, gen_op_fmaddd) +TRANS(FMSUBd, FMAF, do_dddd, a, gen_op_fmsubd) +TRANS(FNMSUBd, FMAF, do_dddd, a, gen_op_fnmsubd) +TRANS(FNMADDd, FMAF, do_dddd, a, gen_op_fnmaddd) +TRANS(FPMADDX, IMA, do_dddd, a, gen_op_fpmaddx) +TRANS(FPMADDXHI, IMA, do_dddd, a, gen_op_fpmaddxhi) + +static bool trans_FALIGNDATAi(DisasContext *dc, arg_r_r_r *a) +{ + TCGv_i64 dst, src1, src2; + TCGv src3; + + if (!avail_VIS4(dc)) { + return false; + } + if (gen_trap_ifnofpu(dc)) { + return true; + } + + dst = tcg_temp_new_i64(); + src1 = gen_load_fpr_D(dc, a->rd); + src2 = gen_load_fpr_D(dc, a->rs2); + src3 = gen_load_gpr(dc, a->rs1); + gen_op_faligndata_i(dst, src1, src2, src3); + gen_store_fpr_D(dc, a->rd, dst); + return advance_pc(dc); +} + +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_if_nofpu_fpexception(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_if_nofpu_fpexception(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_if_nofpu_fpexception(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_if_nofpu_fpexception(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_if_nofpu_fpexception(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 bool trans_FLCMPs(DisasContext *dc, arg_FLCMPs *a) +{ + TCGv_i32 src1, src2; + + if (!avail_VIS3(dc)) { + 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); + gen_helper_flcmps(cpu_fcc[a->cc], src1, src2); + return advance_pc(dc); +} + +static bool trans_FLCMPd(DisasContext *dc, arg_FLCMPd *a) +{ + TCGv_i64 src1, src2; + + if (!avail_VIS3(dc)) { + 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); + gen_helper_flcmpd(cpu_fcc[a->cc], src1, src2); + return advance_pc(dc); +} + +static bool do_movf2r(DisasContext *dc, arg_r_r *a, + int (*offset)(unsigned int), + void (*load)(TCGv, TCGv_ptr, tcg_target_long)) +{ + TCGv dst; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + dst = gen_dest_gpr(dc, a->rd); + load(dst, tcg_env, offset(a->rs)); + gen_store_gpr(dc, a->rd, dst); + return advance_pc(dc); +} + +TRANS(MOVsTOsw, VIS3B, do_movf2r, a, gen_offset_fpr_F, tcg_gen_ld32s_tl) +TRANS(MOVsTOuw, VIS3B, do_movf2r, a, gen_offset_fpr_F, tcg_gen_ld32u_tl) +TRANS(MOVdTOx, VIS3B, do_movf2r, a, gen_offset_fpr_D, tcg_gen_ld_tl) + +static bool do_movr2f(DisasContext *dc, arg_r_r *a, + int (*offset)(unsigned int), + void (*store)(TCGv, TCGv_ptr, tcg_target_long)) +{ + TCGv src; + + if (gen_trap_ifnofpu(dc)) { + return true; + } + src = gen_load_gpr(dc, a->rs); + store(src, tcg_env, offset(a->rd)); + return advance_pc(dc); +} + +TRANS(MOVwTOs, VIS3B, do_movr2f, a, gen_offset_fpr_F, tcg_gen_st32_tl) +TRANS(MOVxTOd, VIS3B, do_movr2f, a, gen_offset_fpr_D, tcg_gen_st_tl) + 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 dc->supervisor = (dc->base.tb->flags & TB_FLAG_SUPER) != 0; +# ifdef TARGET_SPARC64 + dc->hypervisor = (dc->base.tb->flags & TB_FLAG_HYPER) != 0; +# else + dc->fsr_qne = (dc->base.tb->flags & TB_FLAG_FSR_QNE) != 0; +# endif #endif #ifdef TARGET_SPARC64 dc->fprs_dirty = 0; dc->asi = (dc->base.tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff; -#ifndef CONFIG_USER_ONLY - dc->hypervisor = (dc->base.tb->flags & TB_FLAG_HYPER) != 0; -#endif #endif /* * if we reach a page boundary, we stop generation so that the @@ -5840,24 +5679,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 +5721,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,13 +5785,19 @@ static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) default: g_assert_not_reached(); } -} -static void sparc_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); + 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 const TranslatorOps sparc_tr_ops = { @@ -5914,11 +5806,10 @@ static const TranslatorOps sparc_tr_ops = { .insn_start = sparc_tr_insn_start, .translate_insn = sparc_tr_translate_insn, .tb_stop = sparc_tr_tb_stop, - .disas_log = sparc_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = {}; @@ -5933,67 +5824,53 @@ void sparc_tcg_init(void) "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7", }; - static const char fregnames[32][4] = { - "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14", - "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30", - "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46", - "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62", - }; 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]); } @@ -6003,20 +5880,13 @@ void sparc_tcg_init(void) (i - 8) * sizeof(target_ulong), gregnames[i]); } - - for (i = 0; i < TARGET_DPREGS; i++) { - cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUSPARCState, fpr[i]), - fregnames[i]); - } } void sparc_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - SPARCCPU *cpu = SPARC_CPU(cs); - CPUSPARCState *env = &cpu->env; + 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..371f5445a1 100644 --- a/target/sparc/vis_helper.c +++ b/target/sparc/vis_helper.c @@ -20,49 +20,73 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "crypto/clmul.h" -/* This function uses non-native bit order */ -#define GET_FIELD(X, FROM, TO) \ - ((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1)) - -/* This function uses the order in the manuals, i.e. bit 0 is 2^0 */ -#define GET_FIELD_SP(X, FROM, TO) \ - GET_FIELD(X, 63 - (TO), 63 - (FROM)) - -target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) +target_ulong helper_array8(target_ulong rs1, target_ulong rs2) { - return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) | - (GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) | - (GET_FIELD_SP(pixel_addr, 17 + cubesize - 1, 17) << 17) | - (GET_FIELD_SP(pixel_addr, 56, 59) << 13) | - (GET_FIELD_SP(pixel_addr, 35, 38) << 9) | - (GET_FIELD_SP(pixel_addr, 13, 16) << 5) | - (((pixel_addr >> 55) & 1) << 4) | - (GET_FIELD_SP(pixel_addr, 33, 34) << 2) | - GET_FIELD_SP(pixel_addr, 11, 12); + /* + * From Oracle SPARC Architecture 2015: + * Architecturally, an illegal R[rs2] value (>5) causes the array + * instructions to produce undefined results. For historic reference, + * past implementations of these instructions have ignored R[rs2]{63:3} + * and have treated R[rs2] values of 6 and 7 as if they were 5. + */ + target_ulong n = MIN(rs2 & 7, 5); + + target_ulong x_int = (rs1 >> 11) & 0x7ff; + target_ulong y_int = (rs1 >> 33) & 0x7ff; + target_ulong z_int = rs1 >> 55; + + target_ulong lower_x = x_int & 3; + target_ulong lower_y = y_int & 3; + target_ulong lower_z = z_int & 1; + + target_ulong middle_x = (x_int >> 2) & 15; + target_ulong middle_y = (y_int >> 2) & 15; + target_ulong middle_z = (z_int >> 1) & 15; + + target_ulong upper_x = (x_int >> 6) & ((1 << n) - 1); + target_ulong upper_y = (y_int >> 6) & ((1 << n) - 1); + target_ulong upper_z = z_int >> 5; + + return (upper_z << (17 + 2 * n)) + | (upper_y << (17 + n)) + | (upper_x << 17) + | (middle_z << 13) + | (middle_y << 9) + | (middle_x << 5) + | (lower_z << 4) + | (lower_y << 2) + | lower_x; } #if HOST_BIG_ENDIAN #define VIS_B64(n) b[7 - (n)] +#define VIS_SB64(n) sb[7 - (n)] #define VIS_W64(n) w[3 - (n)] #define VIS_SW64(n) sw[3 - (n)] #define VIS_L64(n) l[1 - (n)] +#define VIS_SL64(n) sl[1 - (n)] #define VIS_B32(n) b[3 - (n)] #define VIS_W32(n) w[1 - (n)] #else #define VIS_B64(n) b[n] +#define VIS_SB64(n) sb[n] #define VIS_W64(n) w[n] #define VIS_SW64(n) sw[n] #define VIS_L64(n) l[n] +#define VIS_SL64(n) sl[n] #define VIS_B32(n) b[n] #define VIS_W32(n) w[n] #endif typedef union { uint8_t b[8]; + int8_t sb[8]; uint16_t w[4]; int16_t sw[4]; uint32_t l[2]; + int32_t sl[2]; uint64_t ll; float64 d; } VIS64; @@ -74,94 +98,60 @@ typedef union { float32 f; } VIS32; -uint64_t helper_fpmerge(uint64_t src1, uint64_t src2) +uint64_t helper_fpmerge(uint32_t src1, uint32_t src2) { - VIS64 s, d; + VIS32 s1, s2; + VIS64 d; - s.ll = src1; - d.ll = src2; + s1.l = src1; + s2.l = src2; + d.ll = 0; - /* Reverse calculation order to handle overlap */ - d.VIS_B64(7) = s.VIS_B64(3); - d.VIS_B64(6) = d.VIS_B64(3); - d.VIS_B64(5) = s.VIS_B64(2); - d.VIS_B64(4) = d.VIS_B64(2); - d.VIS_B64(3) = s.VIS_B64(1); - d.VIS_B64(2) = d.VIS_B64(1); - d.VIS_B64(1) = s.VIS_B64(0); - /* d.VIS_B64(0) = d.VIS_B64(0); */ + d.VIS_B64(7) = s1.VIS_B32(3); + d.VIS_B64(6) = s2.VIS_B32(3); + d.VIS_B64(5) = s1.VIS_B32(2); + d.VIS_B64(4) = s2.VIS_B32(2); + d.VIS_B64(3) = s1.VIS_B32(1); + d.VIS_B64(2) = s2.VIS_B32(1); + d.VIS_B64(1) = s1.VIS_B32(0); + d.VIS_B64(0) = s2.VIS_B32(0); return d.ll; } -uint64_t helper_fmul8x16(uint64_t src1, uint64_t src2) +static inline int do_ms16b(int x, int y) { - VIS64 s, d; - uint32_t tmp; + return ((x * y) + 0x80) >> 8; +} - s.ll = src1; +uint64_t helper_fmul8x16(uint32_t src1, uint64_t src2) +{ + VIS64 d; + VIS32 s; + + s.l = src1; d.ll = src2; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_B32(0), d.VIS_SW64(0)); + d.VIS_W64(1) = do_ms16b(s.VIS_B32(1), d.VIS_SW64(1)); + d.VIS_W64(2) = do_ms16b(s.VIS_B32(2), d.VIS_SW64(2)); + d.VIS_W64(3) = do_ms16b(s.VIS_B32(3), d.VIS_SW64(3)); return d.ll; } -uint64_t helper_fmul8x16al(uint64_t src1, uint64_t src2) +uint64_t helper_fmul8x16a(uint32_t src1, int32_t src2) { - VIS64 s, d; - uint32_t tmp; + VIS32 s; + VIS64 d; - s.ll = src1; - d.ll = src2; + s.l = src1; + d.ll = 0; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL - - return d.ll; -} - -uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2) -{ - VIS64 s, d; - uint32_t tmp; - - s.ll = src1; - d.ll = src2; - -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_B32(0), src2); + d.VIS_W64(1) = do_ms16b(s.VIS_B32(1), src2); + d.VIS_W64(2) = do_ms16b(s.VIS_B32(2), src2); + d.VIS_W64(3) = do_ms16b(s.VIS_B32(3), src2); return d.ll; } @@ -169,23 +159,14 @@ uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2) uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2) { VIS64 s, d; - uint32_t tmp; s.ll = src1; d.ll = src2; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = do_ms16b(s.VIS_SB64(1), d.VIS_SW64(0)); + d.VIS_W64(1) = do_ms16b(s.VIS_SB64(3), d.VIS_SW64(1)); + d.VIS_W64(2) = do_ms16b(s.VIS_SB64(5), d.VIS_SW64(2)); + d.VIS_W64(3) = do_ms16b(s.VIS_SB64(7), d.VIS_SW64(3)); return d.ll; } @@ -193,80 +174,25 @@ uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2) uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2) { VIS64 s, d; - uint32_t tmp; s.ll = src1; d.ll = src2; -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_W64(r) = tmp >> 8; - - PMUL(0); - PMUL(1); - PMUL(2); - PMUL(3); -#undef PMUL + d.VIS_W64(0) = (s.VIS_B64(0) * d.VIS_SW64(0) + 0x8000) >> 16; + d.VIS_W64(1) = (s.VIS_B64(2) * d.VIS_SW64(1) + 0x8000) >> 16; + d.VIS_W64(2) = (s.VIS_B64(4) * d.VIS_SW64(2) + 0x8000) >> 16; + d.VIS_W64(3) = (s.VIS_B64(6) * d.VIS_SW64(3) + 0x8000) >> 16; return d.ll; } -uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2) -{ - VIS64 s, d; - uint32_t tmp; - - s.ll = src1; - d.ll = src2; - -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_L64(r) = tmp; - - /* Reverse calculation order to handle overlap */ - PMUL(1); - PMUL(0); -#undef PMUL - - return d.ll; -} - -uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2) -{ - VIS64 s, d; - uint32_t tmp; - - s.ll = src1; - d.ll = src2; - -#define PMUL(r) \ - tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \ - if ((tmp & 0xff) > 0x7f) { \ - tmp += 0x100; \ - } \ - d.VIS_L64(r) = tmp; - - /* Reverse calculation order to handle overlap */ - PMUL(1); - PMUL(0); -#undef PMUL - - return d.ll; -} - -uint64_t helper_fexpand(uint64_t src1, uint64_t src2) +uint64_t helper_fexpand(uint32_t src2) { VIS32 s; VIS64 d; - s.l = (uint32_t)src1; - d.ll = src2; + s.l = src2; + d.ll = 0; d.VIS_W64(0) = s.VIS_B32(0) << 4; d.VIS_W64(1) = s.VIS_B32(1) << 4; d.VIS_W64(2) = s.VIS_B32(2) << 4; @@ -275,105 +201,171 @@ 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; \ +uint64_t helper_fcmpeq8(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + uint64_t m = 0x7f7f7f7f7f7f7f7fULL; + uint64_t c = ~(((a & m) + m) | a | m); + + /* a.......b.......c.......d.......e.......f.......g.......h....... */ + c |= c << 7; + /* ab......bc......cd......de......ef......fg......gh......h....... */ + c |= c << 14; + /* abcd....bcde....cdef....defg....efgh....fgh.....gh......h....... */ + c |= c << 28; + /* abcdefghbcdefgh.cdefgh..defgh...efgh....fgh.....gh......h....... */ + return c >> 56; +} + +uint64_t helper_fcmpne8(uint64_t src1, uint64_t src2) +{ + return helper_fcmpeq8(src1, src2) ^ 0xff; +} + +uint64_t helper_fcmple8(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 8; ++i) { + r |= (s1.VIS_SB64(i) <= s2.VIS_SB64(i)) << i; } + return r; +} -#define FADD(a, b) ((a) + (b)) -#define FSUB(a, b) ((a) - (b)) -VIS_HELPER(helper_fpadd, FADD) -VIS_HELPER(helper_fpsub, FSUB) +uint64_t helper_fcmpgt8(uint64_t src1, uint64_t src2) +{ + return helper_fcmple8(src1, src2) ^ 0xff; +} -#define VIS_CMPHELPER(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(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \ - d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \ - d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \ - d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \ - d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \ - \ - return d.ll; \ - } \ - \ - uint64_t name##32(uint64_t src1, uint64_t src2) \ - { \ - VIS64 s, d; \ - \ - s.ll = src1; \ - d.ll = src2; \ - \ - d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \ - d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \ - d.VIS_L64(1) = 0; \ - \ - return d.ll; \ +uint64_t helper_fcmpule8(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 8; ++i) { + r |= (s1.VIS_B64(i) <= s2.VIS_B64(i)) << i; } + return r; +} -#define FCMPGT(a, b) ((a) > (b)) -#define FCMPEQ(a, b) ((a) == (b)) -#define FCMPLE(a, b) ((a) <= (b)) -#define FCMPNE(a, b) ((a) != (b)) +uint64_t helper_fcmpugt8(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule8(src1, src2) ^ 0xff; +} -VIS_CMPHELPER(helper_fcmpgt, FCMPGT) -VIS_CMPHELPER(helper_fcmpeq, FCMPEQ) -VIS_CMPHELPER(helper_fcmple, FCMPLE) -VIS_CMPHELPER(helper_fcmpne, FCMPNE) +uint64_t helper_fcmpeq16(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + uint64_t m = 0x7fff7fff7fff7fffULL; + uint64_t c = ~(((a & m) + m) | a | m); + + /* a...............b...............c...............d............... */ + c |= c << 15; + /* ab..............bc..............cd..............d............... */ + c |= c << 30; + /* abcd............bcd.............cd..............d............... */ + return c >> 60; +} + +uint64_t helper_fcmpne16(uint64_t src1, uint64_t src2) +{ + return helper_fcmpeq16(src1, src2) ^ 0xf; +} + +uint64_t helper_fcmple16(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 4; ++i) { + r |= (s1.VIS_SW64(i) <= s2.VIS_SW64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpgt16(uint64_t src1, uint64_t src2) +{ + return helper_fcmple16(src1, src2) ^ 0xf; +} + +uint64_t helper_fcmpule16(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 4; ++i) { + r |= (s1.VIS_W64(i) <= s2.VIS_W64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpugt16(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule16(src1, src2) ^ 0xf; +} + +uint64_t helper_fcmpeq32(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + return ((uint32_t)a == 0) | (a >> 32 ? 0 : 2); +} + +uint64_t helper_fcmpne32(uint64_t src1, uint64_t src2) +{ + uint64_t a = src1 ^ src2; + return ((uint32_t)a != 0) | (a >> 32 ? 2 : 0); +} + +uint64_t helper_fcmple32(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 2; ++i) { + r |= (s1.VIS_SL64(i) <= s2.VIS_SL64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpgt32(uint64_t src1, uint64_t src2) +{ + return helper_fcmple32(src1, src2) ^ 3; +} + +uint64_t helper_fcmpule32(uint64_t src1, uint64_t src2) +{ + VIS64 s1, s2; + uint64_t r = 0; + + s1.ll = src1; + s2.ll = src2; + + for (int i = 0; i < 2; ++i) { + r |= (s1.VIS_L64(i) <= s2.VIS_L64(i)) << i; + } + return r; +} + +uint64_t helper_fcmpugt32(uint64_t src1, uint64_t src2) +{ + return helper_fcmpule32(src1, src2) ^ 3; +} uint64_t helper_pdist(uint64_t sum, uint64_t src1, uint64_t src2) { @@ -488,3 +480,131 @@ uint64_t helper_bshuffle(uint64_t gsr, uint64_t src1, uint64_t src2) return r.ll; } + +uint64_t helper_cmask8(uint64_t gsr, uint64_t src) +{ + uint32_t mask = 0; + + mask |= (src & 0x01 ? 0x00000007 : 0x0000000f); + mask |= (src & 0x02 ? 0x00000060 : 0x000000e0); + mask |= (src & 0x04 ? 0x00000500 : 0x00000d00); + mask |= (src & 0x08 ? 0x00004000 : 0x0000c000); + mask |= (src & 0x10 ? 0x00030000 : 0x000b0000); + mask |= (src & 0x20 ? 0x00200000 : 0x00a00000); + mask |= (src & 0x40 ? 0x01000000 : 0x09000000); + mask |= (src & 0x80 ? 0x00000000 : 0x80000000); + + return deposit64(gsr, 32, 32, mask); +} + +uint64_t helper_cmask16(uint64_t gsr, uint64_t src) +{ + uint32_t mask = 0; + + mask |= (src & 0x1 ? 0x00000067 : 0x000000ef); + mask |= (src & 0x2 ? 0x00004500 : 0x0000cd00); + mask |= (src & 0x4 ? 0x00230000 : 0x00ab0000); + mask |= (src & 0x8 ? 0x01000000 : 0x89000000); + + return deposit64(gsr, 32, 32, mask); +} + +uint64_t helper_cmask32(uint64_t gsr, uint64_t src) +{ + uint32_t mask = 0; + + mask |= (src & 0x1 ? 0x00004567 : 0x0000cdef); + mask |= (src & 0x2 ? 0x01230000 : 0x89ab0000); + + return deposit64(gsr, 32, 32, mask); +} + +static inline uint16_t do_fchksm16(uint16_t src1, uint16_t src2) +{ + uint16_t a = src1 + src2; + uint16_t c = a < src1; + return a + c; +} + +uint64_t helper_fchksm16(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + r.VIS_W64(0) = do_fchksm16(s1.VIS_W64(0), s2.VIS_W64(0)); + r.VIS_W64(1) = do_fchksm16(s1.VIS_W64(1), s2.VIS_W64(1)); + r.VIS_W64(2) = do_fchksm16(s1.VIS_W64(2), s2.VIS_W64(2)); + r.VIS_W64(3) = do_fchksm16(s1.VIS_W64(3), s2.VIS_W64(3)); + + return r.ll; +} + +static inline int16_t do_fmean16(int16_t src1, int16_t src2) +{ + return (src1 + src2 + 1) / 2; +} + +uint64_t helper_fmean16(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + r.VIS_SW64(0) = do_fmean16(s1.VIS_SW64(0), s2.VIS_SW64(0)); + r.VIS_SW64(1) = do_fmean16(s1.VIS_SW64(1), s2.VIS_SW64(1)); + r.VIS_SW64(2) = do_fmean16(s1.VIS_SW64(2), s2.VIS_SW64(2)); + r.VIS_SW64(3) = do_fmean16(s1.VIS_SW64(3), s2.VIS_SW64(3)); + + return r.ll; +} + +uint64_t helper_fslas16(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + for (int i = 0; i < 4; ++i) { + int t = s1.VIS_SW64(i) << (s2.VIS_W64(i) % 16); + t = MIN(t, INT16_MAX); + t = MAX(t, INT16_MIN); + r.VIS_SW64(i) = t; + } + + return r.ll; +} + +uint64_t helper_fslas32(uint64_t src1, uint64_t src2) +{ + VIS64 r, s1, s2; + + s1.ll = src1; + s2.ll = src2; + r.ll = 0; + + for (int i = 0; i < 2; ++i) { + int64_t t = (int64_t)(int32_t)s1.VIS_L64(i) << (s2.VIS_L64(i) % 32); + t = MIN(t, INT32_MAX); + t = MAX(t, INT32_MIN); + r.VIS_L64(i) = t; + } + + return r.ll; +} + +uint64_t helper_xmulx(uint64_t src1, uint64_t src2) +{ + return int128_getlo(clmul_64(src1, src2)); +} + +uint64_t helper_xmulxhi(uint64_t src1, uint64_t src2) +{ + return int128_gethi(clmul_64(src1, 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/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 2c54a2825f..1a26171590 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -22,62 +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; - - env->PC = value & ~(target_ulong)1; + cpu_env(cs)->PC = value & ~(target_ulong)1; } static vaddr tricore_cpu_get_pc(CPUState *cs) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; - - return env->PC; + 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(tb); + tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL)); + cpu_env(cs)->PC = tb->pc; } static void tricore_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) { - TriCoreCPU *cpu = TRICORE_CPU(cs); - CPUTriCoreState *env = &cpu->env; - - env->PC = data[0]; + cpu_env(cs)->PC = data[0]; } -static void tricore_cpu_reset(DeviceState *dev) +static void tricore_cpu_reset_hold(Object *obj, ResetType type) { - CPUState *s = CPU(dev); - TriCoreCPU *cpu = TRICORE_CPU(s); - TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(cpu); - CPUTriCoreState *env = &cpu->env; + CPUState *cs = CPU(obj); + TriCoreCPUClass *tcc = TRICORE_CPU_GET_CLASS(obj); - tcc->parent_reset(dev); + if (tcc->parent_phases.hold) { + tcc->parent_phases.hold(obj, type); + } - cpu_state_reset(env); + cpu_state_reset(cpu_env(cs)); } static bool tricore_cpu_has_work(CPUState *cs) @@ -85,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); @@ -100,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); @@ -116,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; @@ -132,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; } @@ -160,6 +148,19 @@ 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); +} + +static bool tricore_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + /* Interrupts are not implemented */ + return false; +} + #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps tricore_sysemu_ops = { @@ -168,11 +169,13 @@ 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, + .cpu_exec_interrupt = tricore_cpu_exec_interrupt, + .cpu_exec_halt = tricore_cpu_has_work, }; static void tricore_cpu_class_init(ObjectClass *c, void *data) @@ -180,13 +183,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; @@ -212,7 +218,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, @@ -220,6 +226,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.h.inc b/target/tricore/csfr.h.inc index ff004cbddc..cdfaf1d662 100644 --- a/target/tricore/csfr.h.inc +++ 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..0b73b1280e 100644 --- a/target/tricore/gdbstub.c +++ b/target/tricore/gdbstub.c @@ -18,7 +18,8 @@ */ #include "qemu/osdep.h" -#include "exec/gdbstub.h" +#include "gdbstub/helpers.h" +#include "cpu.h" #define LCX_REGNUM 32 @@ -106,8 +107,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,16 +121,15 @@ 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); + tmp = ldl_le_p(mem_buf); 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..7014255f77 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -17,8 +17,10 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "hw/registerfields.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/page-protection.h" #include "fpu/softfloat-helpers.h" #include "qemu/qemu-print.h" @@ -30,7 +32,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 +49,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 +57,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 +68,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 +77,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 +96,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 +144,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 532ae6b74c..a0d5a0da1d 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -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); @@ -1506,8 +1505,8 @@ uint32_t helper_sub_h(CPUTriCoreState *env, target_ulong r1, target_ulong r2) uint32_t helper_eq_b(target_ulong r1, target_ulong r2) { - int32_t ret; - int32_t i, msk; + uint32_t ret, msk; + int32_t i; ret = 0; msk = 0xff; @@ -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 df9e46c649..4a12d2ca19 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -20,7 +20,6 @@ #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" @@ -33,6 +32,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 +57,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 +75,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 +94,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 +126,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 +141,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 +153,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 +165,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 +175,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 +184,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 +214,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 +222,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 +230,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 +238,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 +247,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 +255,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 +264,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 +273,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 +292,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 +304,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 +323,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,7 +348,7 @@ 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.h.inc" @@ -397,11 +360,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 +375,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.h.inc" } } } else { - /* generate privilege trap */ + generate_trap(ctx, TRAPC_PROT, TIN1_PRIV); } } @@ -447,9 +411,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 +437,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 +483,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 +515,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 +549,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 +579,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 +682,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 +690,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 +719,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 +727,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 +756,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 +786,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 +795,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 +877,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 +910,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 +938,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 +1028,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 +1045,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 +1062,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 +1089,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 +1113,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 +1153,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 +1163,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 +1180,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 +1188,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 +1226,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 +1260,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 +1296,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 +1334,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 +1366,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 +1381,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 +1405,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 +1433,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 +1459,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 +1482,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 +1489,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 +1498,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 +1522,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 +1584,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 +1672,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 +1706,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 +1761,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 +1778,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 +1795,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 +1822,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 +1846,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 +1886,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 +1896,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 +1918,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 +1926,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 +1991,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 +2052,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 +2082,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 +2151,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 +2183,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 +2208,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 +2229,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 +2313,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 +2335,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 +2361,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 +2369,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 +2387,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 +2417,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 +2435,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 +2484,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 +2492,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 +2506,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 +2522,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 +2536,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 +2563,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 +2571,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 +2583,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 +2635,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 +2653,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 +2668,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 +2705,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 +2722,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) @@ -3123,8 +2732,7 @@ static inline void gen_insert(TCGv ret, TCGv r1, TCGv r2, TCGv width, TCGv pos) TCGv temp = tcg_temp_new(); TCGv temp2 = tcg_temp_new(); - tcg_gen_movi_tl(mask, 1); - tcg_gen_shl_tl(mask, mask, width); + tcg_gen_shl_tl(mask, tcg_constant_tl(1), width); tcg_gen_subi_tl(mask, mask, 1); tcg_gen_shl_tl(mask, mask, pos); @@ -3132,10 +2740,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 +2748,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 +2756,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 +2764,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 +2777,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 +2796,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 +2810,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 +2829,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 +2857,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 +2880,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 +2890,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 +2936,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 +2989,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 +3058,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 +3071,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 +3135,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 +3146,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 +3184,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 +3212,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 +3258,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 +3365,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 +3456,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 +3490,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 +3508,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 +3526,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 +3620,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 +3791,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 +3811,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 +3824,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 +3842,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 +3855,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 +3867,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 +3906,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 +3926,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 +3939,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 +3951,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 +4050,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 +4167,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 +4207,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 +4304,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 +4321,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 +4337,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 +4344,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 +4371,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 +4380,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 +4394,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 +4402,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 +4410,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 +4425,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 +4440,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 +4448,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 +4458,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 +4466,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 +4517,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 +4535,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 +4552,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 +4610,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 +4619,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 +4632,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 +4640,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 +4648,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 +4663,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 +4678,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 +4686,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 +4694,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 +4704,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 +4712,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 +4725,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 +4734,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 +4754,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 +4806,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 +4813,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 +4822,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 +4834,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 +4842,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 +4850,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 +4858,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 +4880,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 +4959,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 +4967,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 +5008,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 +5218,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 +5230,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 +5307,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 +5343,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 +5389,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 +5590,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 +5636,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 +5685,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 +5723,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 +5781,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 +5862,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 +5877,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 +5920,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 +5971,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 +5988,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 +6010,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 +6069,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 +6082,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 +6135,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 +6164,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 +6185,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 +6207,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 +6247,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 +6255,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 +6322,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 +6332,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 +6339,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 +6346,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 +6353,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 +6363,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 +6373,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 +6383,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 +6393,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 +6413,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 +6482,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 +6504,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 +6513,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 +6539,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 +6559,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 +6607,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 +6681,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 +6731,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 +6747,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 +6784,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 +6794,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 +6974,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 +7137,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 +7456,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 +7619,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 +7801,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 +7840,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 +7879,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 +7892,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 +7914,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 +7953,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 +8009,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 +8046,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 +8081,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 +8208,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 +8282,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 +8351,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 +8387,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 +8396,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 +8435,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: @@ -8861,25 +8451,17 @@ static void tricore_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void tricore_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps tricore_tr_ops = { .init_disas_context = tricore_tr_init_disas_context, .tb_start = tricore_tr_tb_start, .insn_start = tricore_tr_insn_start, .translate_insn = tricore_tr_translate_insn, .tb_stop = tricore_tr_tb_stop, - .disas_log = tricore_tr_disas_log, }; -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext ctx; translator_loop(cs, tb, max_insns, pc, host_pc, @@ -8901,13 +8483,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"); } @@ -8917,30 +8499,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..e8c2598c4d 100644 --- a/target/xtensa/Kconfig +++ b/target/xtensa/Kconfig @@ -1,2 +1,3 @@ config XTENSA bool + imply SEMIHOSTING if TCG 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..0000725f2f 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -16,6 +16,8 @@ #else #define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif -#define NB_MMU_MODES 4 + +/* Xtensa processors have a weak memory model */ +#define TCG_GUEST_DEFAULT_MO (0) #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 09923301c4..6f9039abae 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) @@ -71,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; @@ -85,16 +93,17 @@ bool xtensa_abi_call0(void) } #endif -static void xtensa_cpu_reset(DeviceState *dev) +static void xtensa_cpu_reset_hold(Object *obj, ResetType type) { - 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, type); + } env->pc = env->config->exception_vector[EXC_RESET0 + env->static_vectors]; env->sregs[LITBASE] &= ~1; @@ -122,10 +131,10 @@ 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); + xtensa_use_first_nan(env, !dfpu); } static ObjectClass *xtensa_cpu_class_by_name(const char *cpu_model) @@ -136,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; } @@ -180,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 @@ -221,7 +226,7 @@ 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, @@ -229,9 +234,11 @@ static const struct TCGCPUOps xtensa_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = xtensa_cpu_tlb_fill, .cpu_exec_interrupt = xtensa_cpu_exec_interrupt, + .cpu_exec_halt = xtensa_cpu_has_work, .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 */ }; @@ -240,14 +247,17 @@ 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; @@ -266,6 +276,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..77e48eef19 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -34,9 +34,6 @@ #include "hw/clock.h" #include "xtensa-isa.h" -/* Xtensa processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - enum { /* Additional instructions */ XTENSA_OPTION_CODE_DENSITY, @@ -229,6 +226,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 +424,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 +487,7 @@ struct XtensaConfig { const xtensa_mpu_entry *mpu_bg; bool use_first_nan; -}; +} XtensaConfig; typedef struct XtensaConfigList { const XtensaConfig *config; @@ -547,6 +545,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 +556,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 +589,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 +601,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 +625,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 +710,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 +731,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; @@ -798,4 +802,10 @@ static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, XtensaCPU *xtensa_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk); +/* + * Set the NaN propagation rule for future FPU operations: + * use_first is true to pick the first NaN as the result if both + * inputs are NaNs, false to pick the second. + */ +void xtensa_use_first_nan(CPUXtensaState *env, bool use_first); #endif 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..ca629f071d 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 < ARRAY_SIZE(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..f2d212d05d 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" @@ -58,6 +57,13 @@ static const struct { { XTENSA_FP_V, float_flag_invalid, }, }; +void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) +{ + set_use_first_nan(use_first, &env->fp_status); + set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, + &env->fp_status); +} + void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) { static const int rounding_mode[] = { @@ -172,87 +178,87 @@ float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env, float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b) { - set_use_first_nan(true, &env->fp_status); + xtensa_use_first_nan(env, true); return float64_add(a, b, &env->fp_status); } float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_add(a, b, &env->fp_status); } float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b) { - set_use_first_nan(true, &env->fp_status); + xtensa_use_first_nan(env, true); return float64_sub(a, b, &env->fp_status); } float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_sub(a, b, &env->fp_status); } float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b) { - set_use_first_nan(true, &env->fp_status); + xtensa_use_first_nan(env, true); return float64_mul(a, b, &env->fp_status); } float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_mul(a, b, &env->fp_status); } float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float64_muladd(b, c, a, 0, &env->fp_status); } float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_muladd(b, c, a, 0, &env->fp_status); } float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float64_muladd(b, c, a, float_muladd_negate_product, &env->fp_status); } float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_muladd(b, c, a, float_muladd_negate_product, &env->fp_status); } float64 HELPER(mkdadj_d)(CPUXtensaState *env, float64 a, float64 b) { - set_use_first_nan(true, &env->fp_status); + xtensa_use_first_nan(env, true); return float64_div(b, a, &env->fp_status); } float32 HELPER(mkdadj_s)(CPUXtensaState *env, float32 a, float32 b) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_div(b, a, &env->fp_status); } float64 HELPER(mksadj_d)(CPUXtensaState *env, float64 v) { - set_use_first_nan(true, &env->fp_status); + xtensa_use_first_nan(env, true); return float64_sqrt(v, &env->fp_status); } float32 HELPER(mksadj_s)(CPUXtensaState *env, float32 v) { - set_use_first_nan(env->config->use_first_nan, &env->fp_status); + xtensa_use_first_nan(env, env->config->use_first_nan); return float32_sqrt(v, &env->fp_status); } 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 2aa9777a8e..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); } } @@ -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; @@ -294,8 +295,7 @@ 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); HELPER(exception_cause_vaddr)(env, env->pc, 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..29b84d5dbf 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -27,14 +27,13 @@ #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" +#include "exec/page-protection.h" #define XTENSA_MPU_SEGMENT_MASK 0x0000001f #define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00 @@ -68,7 +67,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 +225,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 +262,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 +495,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 +509,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 +522,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 +560,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); + } } /*! @@ -969,7 +991,7 @@ uint32_t HELPER(rptlb1)(CPUXtensaState *env, uint32_t s) uint32_t HELPER(pptlb)(CPUXtensaState *env, uint32_t v) { unsigned nhits; - unsigned segment = XTENSA_MPU_PROBE_B; + unsigned segment; unsigned bg_segment; nhits = xtensa_mpu_lookup(env->mpu_fg, env->config->n_mpu_fg_segments, @@ -983,7 +1005,7 @@ uint32_t HELPER(pptlb)(CPUXtensaState *env, uint32_t v) xtensa_mpu_lookup(env->config->mpu_bg, env->config->n_mpu_bg_segments, v, &bg_segment); - return env->config->mpu_bg[bg_segment].attr | segment; + return env->config->mpu_bg[bg_segment].attr | XTENSA_MPU_PROBE_B; } } diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index 1af7becc54..028d4e0a1c 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -26,19 +26,19 @@ */ #include "qemu/osdep.h" -#include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/page-protection.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 +59,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 77bcd71030..f4da4a40f9 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -32,11 +32,9 @@ #include "cpu.h" #include "exec/exec-all.h" -#include "disas/disas.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" -#include "exec/cpu_ldst.h" #include "semihosting/semihost.h" #include "exec/translator.h" @@ -45,6 +43,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 +59,6 @@ struct DisasContext { bool sar_5bit; bool sar_m32_5bit; - bool sar_m32_allocated; TCGv_i32 sar_m32; unsigned window; @@ -91,8 +92,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 +152,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 +203,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 +212,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 +220,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 +283,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 +298,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 +309,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 +325,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; } @@ -530,7 +521,7 @@ static MemOp gen_load_store_alignment(DisasContext *dc, MemOp mop, mop |= MO_ALIGN; } if (!option_enabled(dc, XTENSA_OPTION_UNALIGNED_EXCEPTION)) { - tcg_gen_andi_i32(addr, addr, ~0 << get_alignment_bits(mop)); + tcg_gen_andi_i32(addr, addr, ~0 << memop_alignment_bits(mop)); } return mop; } @@ -543,7 +534,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 +573,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 +1040,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 +1063,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 +1100,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; @@ -1138,31 +1117,17 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) { - uint8_t b0 = cpu_ldub_code(env, dc->pc); + uint8_t b0 = translator_ldub(env, &dc->base, dc->pc); 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 +1152,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 +1164,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 +1189,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 +1208,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; @@ -1263,24 +1219,16 @@ static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) } } -static void xtensa_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu, FILE *logfile) -{ - fprintf(logfile, "IN: %s\n", lookup_symbol(dcbase->pc_first)); - target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); -} - static const TranslatorOps xtensa_translator_ops = { .init_disas_context = xtensa_tr_init_disas_context, .tb_start = xtensa_tr_tb_start, .insn_start = xtensa_tr_insn_start, .translate_insn = xtensa_tr_translate_insn, .tb_stop = xtensa_tr_tb_stop, - .disas_log = xtensa_tr_disas_log, }; -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, - target_ulong pc, void *host_pc) +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int *max_insns, + vaddr pc, void *host_pc) { DisasContext dc = {}; translator_loop(cpu, tb, max_insns, pc, host_pc, @@ -1289,8 +1237,7 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns, 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; @@ -1379,14 +1326,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); @@ -1398,8 +1344,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[], @@ -1414,7 +1358,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[], @@ -1423,7 +1366,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[], @@ -1435,22 +1377,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[], @@ -1463,7 +1399,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[], @@ -1504,8 +1439,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[], @@ -1515,7 +1448,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[], @@ -1528,9 +1460,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[], @@ -1540,7 +1471,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[], @@ -1550,19 +1480,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[], @@ -1581,10 +1508,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[], @@ -1594,9 +1520,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[], @@ -1636,7 +1560,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[], @@ -1647,7 +1571,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[], @@ -1658,7 +1581,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[], @@ -1669,8 +1591,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 } @@ -1680,7 +1601,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 } @@ -1705,7 +1626,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 @@ -1718,7 +1638,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)); } } @@ -1736,7 +1656,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[], @@ -1759,7 +1678,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[], @@ -1774,13 +1692,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[], @@ -1866,19 +1783,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[], @@ -1942,7 +1852,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[], @@ -1965,8 +1874,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[], @@ -1985,7 +1892,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[], @@ -2024,7 +1930,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 } @@ -2033,7 +1939,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 } @@ -2085,7 +1991,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[], @@ -2104,7 +2010,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; } } @@ -2112,15 +2018,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[], @@ -2146,10 +2051,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], @@ -2159,8 +2064,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]); } @@ -2192,10 +2096,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 } @@ -2209,7 +2111,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 } @@ -2224,7 +2125,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 } @@ -2232,7 +2133,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 } @@ -2240,7 +2141,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 } @@ -2266,15 +2167,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); @@ -2283,8 +2184,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[], @@ -2296,15 +2195,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; @@ -2322,9 +2220,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[], @@ -2338,18 +2233,7 @@ 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, @@ -2374,7 +2258,7 @@ static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], { #ifndef CONFIG_USER_ONLY if (semihosting_enabled(dc->cring != 0)) { - gen_helper_simcall(cpu_env); + gen_helper_simcall(tcg_env); } #endif } @@ -2388,8 +2272,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]) @@ -2401,12 +2283,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); } } @@ -2473,7 +2354,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[], @@ -2482,7 +2362,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[], @@ -2515,7 +2394,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[], @@ -2524,10 +2402,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 } @@ -2537,7 +2413,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 } @@ -2545,14 +2421,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[], @@ -2591,11 +2467,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 } @@ -2603,10 +2477,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 } @@ -2617,7 +2489,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 } @@ -2628,7 +2500,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 } @@ -2639,7 +2511,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 } @@ -2647,7 +2519,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 } @@ -2667,7 +2539,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 } @@ -2675,7 +2547,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 } @@ -2683,7 +2555,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 } @@ -2691,7 +2563,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 } @@ -2714,7 +2586,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 } @@ -2767,7 +2639,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); } @@ -2782,7 +2653,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); } @@ -2794,15 +2664,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 } @@ -2820,7 +2686,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) @@ -6307,16 +6172,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); } } } @@ -6401,7 +6256,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); } @@ -6436,13 +6291,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[], @@ -6468,14 +6320,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[], @@ -6524,9 +6373,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); } } @@ -6538,9 +6387,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); } @@ -6552,10 +6401,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); } } @@ -6569,10 +6418,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); @@ -6594,7 +6443,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[], @@ -6613,13 +6461,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); } @@ -6649,7 +6496,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[], @@ -6678,8 +6524,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[], @@ -6693,7 +6537,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); } @@ -6702,14 +6545,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); } @@ -6748,7 +6591,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); } @@ -6771,7 +6614,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[], @@ -7000,20 +6843,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); } } @@ -7024,8 +6867,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[], @@ -7033,9 +6875,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[], @@ -7063,9 +6904,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[], @@ -7098,9 +6936,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[], @@ -7128,9 +6963,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[], @@ -7163,15 +6995,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); } @@ -7179,13 +7008,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); } @@ -7194,20 +7023,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); } } @@ -7215,7 +7044,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); } @@ -7223,13 +7052,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); } @@ -7238,20 +7067,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); } } @@ -7259,7 +7088,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[], @@ -7268,14 +7097,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[], @@ -7284,26 +7113,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 344b63e20f..ffa8a3e519 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,36 +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: - tcg_debug_assert(s->tb_jmp_insn_offset != NULL); - /* - * 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); - 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; @@ -2104,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: @@ -2154,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; @@ -2179,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: @@ -2208,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: @@ -2219,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]); @@ -2304,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(); } @@ -2537,7 +2737,8 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, TCGCond cond = args[3]; AArch64Insn insn; - if (cond == TCG_COND_NE) { + switch (cond) { + case TCG_COND_NE: if (const_args[2]) { if (is_scalar) { tcg_out_insn(s, 3611, CMTST, vece, a0, a1, a1); @@ -2552,7 +2753,27 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } tcg_out_insn(s, 3617, NOT, is_q, 0, a0, a0); } - } else { + break; + + case TCG_COND_TSTNE: + case TCG_COND_TSTEQ: + if (const_args[2]) { + /* (x & 0) == 0 */ + tcg_out_dupi_vec(s, type, MO_8, a0, + -(cond == TCG_COND_TSTEQ)); + break; + } + if (is_scalar) { + tcg_out_insn(s, 3611, CMTST, vece, a0, a1, a2); + } else { + tcg_out_insn(s, 3616, CMTST, is_q, vece, a0, a1, a2); + } + if (cond == TCG_COND_TSTEQ) { + tcg_out_insn(s, 3617, NOT, is_q, 0, a0, a0); + } + break; + + default: if (const_args[2]) { if (is_scalar) { insn = cmp0_scalar_insn[cond]; @@ -2567,8 +2788,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]; @@ -2591,6 +2812,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, } tcg_out_insn_3616(s, insn, is_q, vece, a0, a1, a2); } + break; } } break; @@ -2787,9 +3009,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: @@ -2839,18 +3065,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: @@ -2946,9 +3182,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)). */ @@ -2971,6 +3209,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); @@ -2992,12 +3232,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]); @@ -3007,10 +3251,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, @@ -3028,6 +3274,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..8bd9e6a5eb 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 @@ -148,12 +167,9 @@ typedef enum { #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 #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..56072d89a2 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 (memop_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 (memop_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) { @@ -2843,17 +2740,33 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_cmp_vec: { TCGCond cond = args[3]; + ARMInsn insn; - if (cond == TCG_COND_NE) { + switch (cond) { + case TCG_COND_NE: if (const_args[2]) { tcg_out_vreg3(s, INSN_VTST, q, vece, a0, a1, a1); } else { tcg_out_vreg3(s, INSN_VCEQ, q, vece, a0, a1, a2); tcg_out_vreg2(s, INSN_VMVN, q, 0, a0, a0); } - } else { - ARMInsn insn; + break; + case TCG_COND_TSTNE: + case TCG_COND_TSTEQ: + if (const_args[2]) { + /* (x & 0) == 0 */ + tcg_out_dupi_vec(s, type, MO_8, a0, + -(cond == TCG_COND_TSTEQ)); + break; + } + tcg_out_vreg3(s, INSN_VTST, q, vece, a0, a1, a2); + if (cond == TCG_COND_TSTEQ) { + tcg_out_vreg2(s, INSN_VMVN, q, 0, a0, a0); + } + break; + + default: if (const_args[2]) { insn = vec_cmp0_insn[cond]; if (insn) { @@ -2872,6 +2785,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, tcg_debug_assert(insn != 0); } tcg_out_vreg3(s, insn, q, vece, a0, a1, a2); + break; } } return; @@ -3060,12 +2974,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 +3003,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..fb7261499b 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 @@ -147,13 +150,9 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 #define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_tst_vec 1 #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 d121ace0c4..f61cebdaeb 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_I2(L, x) @@ -35,6 +38,8 @@ C_O1_I1(x, L) C_O1_I1(r, 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) @@ -51,10 +56,11 @@ 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(x, x, x, xO, x) +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..52142ab121 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,7 @@ 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('O', TCG_CT_CONST_ZERO) +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 a7ede4c2b6..79d30223cd 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,29 @@ 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 +#define TCG_CT_CONST_ZERO 0x2000 /* Registers used with L constraint, which are the first argument registers on x86_64, and two random call clobbered registers on @@ -133,7 +146,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 +153,13 @@ 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 +# define ALL_BYTEL_REGS 0x0000000fu #endif +#define SOFTMMU_RESERVE_REGS \ + (tcg_use_softmmu ? (1 << TCG_REG_L0) | (1 << TCG_REG_L1) : 0) -/* 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" -#endif - -/* 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; -#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 -#endif +#define have_bmi2 (cpuinfo & CPUINFO_BMI2) +#define have_lzcnt (cpuinfo & CPUINFO_LZCNT) static const tcg_insn_unit *tb_ret_addr; @@ -209,19 +185,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,10 +212,24 @@ 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; } + if ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } return 0; } @@ -264,6 +256,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) */ @@ -312,6 +305,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) @@ -358,6 +352,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) @@ -437,11 +433,24 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_SQRTSS (0x51 | P_EXT | P_SIMDF3) #define OPC_SUBSD (0x5c | P_EXT | P_SIMDF2) #define OPC_SUBSS (0x5c | P_EXT | P_SIMDF3) +#define OPC_TESTB (0x84) #define OPC_TESTL (0x85) #define OPC_TZCNT (0xbc | P_EXT | P_SIMDF3) #define OPC_UD2 (0x0b | P_EXT) #define OPC_VPBLENDD (0x02 | P_EXT3A | P_DATA16) #define OPC_VPBLENDVB (0x4c | P_EXT3A | P_DATA16) +#define OPC_VPBLENDMB (0x66 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPBLENDMW (0x66 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPBLENDMD (0x64 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPBLENDMQ (0x64 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPCMPB (0x3f | P_EXT3A | P_DATA16 | P_EVEX) +#define OPC_VPCMPUB (0x3e | P_EXT3A | P_DATA16 | P_EVEX) +#define OPC_VPCMPW (0x3f | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPCMPUW (0x3e | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPCMPD (0x1f | P_EXT3A | P_DATA16 | P_EVEX) +#define OPC_VPCMPUD (0x1e | P_EXT3A | P_DATA16 | P_EVEX) +#define OPC_VPCMPQ (0x1f | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPCMPUQ (0x1e | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) #define OPC_VPINSRB (0x20 | P_EXT3A | P_DATA16) #define OPC_VPINSRW (0xc4 | P_EXT | P_DATA16) #define OPC_VBROADCASTSS (0x18 | P_EXT38 | P_DATA16) @@ -450,6 +459,10 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_VPBROADCASTW (0x79 | P_EXT38 | P_DATA16) #define OPC_VPBROADCASTD (0x58 | P_EXT38 | P_DATA16) #define OPC_VPBROADCASTQ (0x59 | P_EXT38 | P_DATA16) +#define OPC_VPMOVM2B (0x28 | P_EXT38 | P_SIMDF3 | P_EVEX) +#define OPC_VPMOVM2W (0x28 | P_EXT38 | P_SIMDF3 | P_VEXW | P_EVEX) +#define OPC_VPMOVM2D (0x38 | P_EXT38 | P_SIMDF3 | P_EVEX) +#define OPC_VPMOVM2Q (0x38 | P_EXT38 | P_SIMDF3 | P_VEXW | P_EVEX) #define OPC_VPERMQ (0x00 | P_EXT3A | P_DATA16 | P_VEXW) #define OPC_VPERM2I128 (0x46 | P_EXT3A | P_DATA16 | P_VEXL) #define OPC_VPROLVD (0x15 | P_EXT38 | P_DATA16 | P_EVEX) @@ -475,13 +488,28 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_VPSRLVD (0x45 | P_EXT38 | P_DATA16) #define OPC_VPSRLVQ (0x45 | P_EXT38 | P_DATA16 | P_VEXW) #define OPC_VPTERNLOGQ (0x25 | P_EXT3A | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPTESTMB (0x26 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPTESTMW (0x26 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPTESTMD (0x27 | P_EXT38 | P_DATA16 | P_EVEX) +#define OPC_VPTESTMQ (0x27 | P_EXT38 | P_DATA16 | P_VEXW | P_EVEX) +#define OPC_VPTESTNMB (0x26 | P_EXT38 | P_SIMDF3 | P_EVEX) +#define OPC_VPTESTNMW (0x26 | P_EXT38 | P_SIMDF3 | P_VEXW | P_EVEX) +#define OPC_VPTESTNMD (0x27 | P_EXT38 | P_SIMDF3 | P_EVEX) +#define OPC_VPTESTNMQ (0x27 | P_EXT38 | P_SIMDF3 | 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. */ @@ -546,6 +574,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 @@ -632,6 +662,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 @@ -677,7 +710,7 @@ static void tcg_out_vex_opc(TCGContext *s, int opc, int r, int v, } static void tcg_out_evex_opc(TCGContext *s, int opc, int r, int v, - int rm, int index) + int rm, int index, int aaa, bool z) { /* The entire 4-byte evex prefix; with R' and V' set. */ uint32_t p = 0x08041062; @@ -714,7 +747,9 @@ static void tcg_out_evex_opc(TCGContext *s, int opc, int r, int v, p = deposit32(p, 16, 2, pp); p = deposit32(p, 19, 4, ~v); p = deposit32(p, 23, 1, (opc & P_VEXW) != 0); + p = deposit32(p, 24, 3, aaa); p = deposit32(p, 29, 2, (opc & P_VEXL) != 0); + p = deposit32(p, 31, 1, z); tcg_out32(s, p); tcg_out8(s, opc); @@ -723,13 +758,32 @@ static void tcg_out_evex_opc(TCGContext *s, int opc, int r, int v, static void tcg_out_vex_modrm(TCGContext *s, int opc, int r, int v, int rm) { if (opc & P_EVEX) { - tcg_out_evex_opc(s, opc, r, v, rm, 0); + tcg_out_evex_opc(s, opc, r, v, rm, 0, 0, false); } else { tcg_out_vex_opc(s, opc, r, v, rm, 0); } tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); } +static void tcg_out_vex_modrm_type(TCGContext *s, int opc, + int r, int v, int rm, TCGType type) +{ + if (type == TCG_TYPE_V256) { + opc |= P_VEXL; + } + tcg_out_vex_modrm(s, opc, r, v, rm); +} + +static void tcg_out_evex_modrm_type(TCGContext *s, int opc, int r, int v, + int rm, int aaa, bool z, TCGType type) +{ + if (type == TCG_TYPE_V256) { + opc |= P_VEXL; + } + tcg_out_evex_opc(s, opc, r, v, rm, 0, aaa, z); + tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); +} + /* Output an opcode with a full "rm + (index<= 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: /* @@ -1291,43 +1366,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); @@ -1343,23 +1438,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); @@ -1378,6 +1491,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) { @@ -1391,7 +1515,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) @@ -1401,8 +1525,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; @@ -1417,9 +1541,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); @@ -1447,139 +1569,304 @@ 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); + int cmp_rexw = rexw; + 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_TSTNE: + inv = true; + /* fall through */ + case TCG_COND_TSTEQ: + /* If arg2 is -1, convert to LTU/GEU vs 1. */ + if (const_arg2 && arg2 == 0xffffffffu) { + arg2 = 1; + cmp_rexw = 0; + 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, cmp_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, cmp_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) { @@ -1623,37 +1910,20 @@ 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); - } else { - TCGLabel *over = gen_new_label(); - tcg_out_jxx(s, tcg_cond_to_jcc[tcg_invert_cond(cond)], over, 1); - tcg_out_mov(s, TCG_TYPE_I32, dest, v1); - tcg_out_label(s, over); - } + tcg_out_modrm(s, OPC_CMOVCC | jcc | rexw, dest, v1); } -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) { @@ -1663,12 +1933,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); } } @@ -1681,7 +1951,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); @@ -1693,8 +1963,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); } } @@ -1717,9 +1987,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) @@ -1742,160 +2029,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); } /* @@ -1903,82 +2110,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; } @@ -1988,157 +2132,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) { @@ -2147,8 +2164,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) { @@ -2156,21 +2175,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. */ @@ -2182,140 +2323,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) { @@ -2328,78 +2507,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_stash_xmm(TCGContext *s, TCGArg a0) @@ -2483,36 +2732,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); @@ -2836,14 +3055,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): @@ -2878,31 +3101,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): @@ -2959,28 +3215,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; @@ -2989,15 +3226,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; @@ -3030,7 +3284,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; @@ -3047,13 +3301,236 @@ 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 } +static int const umin_insn[4] = { + OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_VPMINUQ +}; + +static int const umax_insn[4] = { + OPC_PMAXUB, OPC_PMAXUW, OPC_PMAXUD, OPC_VPMAXUQ +}; + +static bool tcg_out_cmp_vec_noinv(TCGContext *s, TCGType type, unsigned vece, + TCGReg v0, TCGReg v1, TCGReg v2, TCGCond cond) +{ + static int const cmpeq_insn[4] = { + OPC_PCMPEQB, OPC_PCMPEQW, OPC_PCMPEQD, OPC_PCMPEQQ + }; + static int const cmpgt_insn[4] = { + OPC_PCMPGTB, OPC_PCMPGTW, OPC_PCMPGTD, OPC_PCMPGTQ + }; + + enum { + NEED_INV = 1, + NEED_SWAP = 2, + NEED_UMIN = 4, + NEED_UMAX = 8, + INVALID = 16, + }; + static const uint8_t cond_fixup[16] = { + [0 ... 15] = INVALID, + [TCG_COND_EQ] = 0, + [TCG_COND_GT] = 0, + [TCG_COND_NE] = NEED_INV, + [TCG_COND_LE] = NEED_INV, + [TCG_COND_LT] = NEED_SWAP, + [TCG_COND_GE] = NEED_SWAP | NEED_INV, + [TCG_COND_LEU] = NEED_UMIN, + [TCG_COND_GTU] = NEED_UMIN | NEED_INV, + [TCG_COND_GEU] = NEED_UMAX, + [TCG_COND_LTU] = NEED_UMAX | NEED_INV, + }; + int fixup = cond_fixup[cond]; + + assert(!(fixup & INVALID)); + + if (fixup & NEED_INV) { + cond = tcg_invert_cond(cond); + } + + if (fixup & NEED_SWAP) { + TCGReg swap = v1; + v1 = v2; + v2 = swap; + cond = tcg_swap_cond(cond); + } + + if (fixup & (NEED_UMIN | NEED_UMAX)) { + int op = (fixup & NEED_UMIN ? umin_insn[vece] : umax_insn[vece]); + + /* avx2 does not have 64-bit min/max; adjusted during expand. */ + assert(vece <= MO_32); + + tcg_out_vex_modrm_type(s, op, TCG_TMP_VEC, v1, v2, type); + v2 = TCG_TMP_VEC; + cond = TCG_COND_EQ; + } + + switch (cond) { + case TCG_COND_EQ: + tcg_out_vex_modrm_type(s, cmpeq_insn[vece], v0, v1, v2, type); + break; + case TCG_COND_GT: + tcg_out_vex_modrm_type(s, cmpgt_insn[vece], v0, v1, v2, type); + break; + default: + g_assert_not_reached(); + } + return fixup & NEED_INV; +} + +static void tcg_out_cmp_vec_k1(TCGContext *s, TCGType type, unsigned vece, + TCGReg v1, TCGReg v2, TCGCond cond) +{ + static const int cmpm_insn[2][4] = { + { OPC_VPCMPB, OPC_VPCMPW, OPC_VPCMPD, OPC_VPCMPQ }, + { OPC_VPCMPUB, OPC_VPCMPUW, OPC_VPCMPUD, OPC_VPCMPUQ } + }; + static const int testm_insn[4] = { + OPC_VPTESTMB, OPC_VPTESTMW, OPC_VPTESTMD, OPC_VPTESTMQ + }; + static const int testnm_insn[4] = { + OPC_VPTESTNMB, OPC_VPTESTNMW, OPC_VPTESTNMD, OPC_VPTESTNMQ + }; + + static const int cond_ext[16] = { + [TCG_COND_EQ] = 0, + [TCG_COND_NE] = 4, + [TCG_COND_LT] = 1, + [TCG_COND_LTU] = 1, + [TCG_COND_LE] = 2, + [TCG_COND_LEU] = 2, + [TCG_COND_NEVER] = 3, + [TCG_COND_GE] = 5, + [TCG_COND_GEU] = 5, + [TCG_COND_GT] = 6, + [TCG_COND_GTU] = 6, + [TCG_COND_ALWAYS] = 7, + }; + + switch (cond) { + case TCG_COND_TSTNE: + tcg_out_vex_modrm_type(s, testm_insn[vece], /* k1 */ 1, v1, v2, type); + break; + case TCG_COND_TSTEQ: + tcg_out_vex_modrm_type(s, testnm_insn[vece], /* k1 */ 1, v1, v2, type); + break; + default: + tcg_out_vex_modrm_type(s, cmpm_insn[is_unsigned_cond(cond)][vece], + /* k1 */ 1, v1, v2, type); + tcg_out8(s, cond_ext[cond]); + break; + } +} + +static void tcg_out_k1_to_vec(TCGContext *s, TCGType type, + unsigned vece, TCGReg dest) +{ + static const int movm_insn[] = { + OPC_VPMOVM2B, OPC_VPMOVM2W, OPC_VPMOVM2D, OPC_VPMOVM2Q + }; + tcg_out_vex_modrm_type(s, movm_insn[vece], dest, 0, /* k1 */ 1, type); +} + +static void tcg_out_cmp_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg v0, TCGReg v1, TCGReg v2, TCGCond cond) +{ + /* + * With avx512, we have a complete set of comparisons into mask. + * Unless there's a single insn expansion for the comparision, + * expand via a mask in k1. + */ + if ((vece <= MO_16 ? have_avx512bw : have_avx512dq) + && cond != TCG_COND_EQ + && cond != TCG_COND_LT + && cond != TCG_COND_GT) { + tcg_out_cmp_vec_k1(s, type, vece, v1, v2, cond); + tcg_out_k1_to_vec(s, type, vece, v0); + return; + } + + if (tcg_out_cmp_vec_noinv(s, type, vece, v0, v1, v2, cond)) { + tcg_out_dupi_vec(s, type, vece, TCG_TMP_VEC, -1); + tcg_out_vex_modrm_type(s, OPC_PXOR, v0, v0, TCG_TMP_VEC, type); + } +} + +static void tcg_out_cmpsel_vec_k1(TCGContext *s, TCGType type, unsigned vece, + TCGReg v0, TCGReg c1, TCGReg c2, + TCGReg v3, TCGReg v4, TCGCond cond) +{ + static const int vpblendm_insn[] = { + OPC_VPBLENDMB, OPC_VPBLENDMW, OPC_VPBLENDMD, OPC_VPBLENDMQ + }; + bool z = false; + + /* Swap to place constant in V4 to take advantage of zero-masking. */ + if (!v3) { + z = true; + v3 = v4; + cond = tcg_invert_cond(cond); + } + + tcg_out_cmp_vec_k1(s, type, vece, c1, c2, cond); + tcg_out_evex_modrm_type(s, vpblendm_insn[vece], v0, v4, v3, + /* k1 */1, z, type); +} + +static void tcg_out_cmpsel_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg v0, TCGReg c1, TCGReg c2, + TCGReg v3, TCGReg v4, TCGCond cond) +{ + bool inv; + + if (vece <= MO_16 ? have_avx512bw : have_avx512vl) { + tcg_out_cmpsel_vec_k1(s, type, vece, v0, c1, c2, v3, v4, cond); + return; + } + + inv = tcg_out_cmp_vec_noinv(s, type, vece, TCG_TMP_VEC, c1, c2, cond); + + /* + * Since XMM0 is 16, the only way we get 0 into V3 + * is via the constant zero constraint. + */ + if (!v3) { + if (inv) { + tcg_out_vex_modrm_type(s, OPC_PAND, v0, TCG_TMP_VEC, v4, type); + } else { + tcg_out_vex_modrm_type(s, OPC_PANDN, v0, TCG_TMP_VEC, v4, type); + } + } else { + if (inv) { + TCGReg swap = v3; + v3 = v4; + v4 = swap; + } + tcg_out_vex_modrm_type(s, OPC_VPBLENDVB, v0, v4, v3, type); + tcg_out8(s, (TCG_TMP_VEC - TCG_REG_XMM0) << 4); + } +} + static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3083,12 +3560,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, static int const shift_imm_insn[4] = { OPC_UD2, OPC_PSHIFTW_Ib, OPC_PSHIFTD_Ib, OPC_PSHIFTQ_Ib }; - static int const cmpeq_insn[4] = { - OPC_PCMPEQB, OPC_PCMPEQW, OPC_PCMPEQD, OPC_PCMPEQQ - }; - static int const cmpgt_insn[4] = { - OPC_PCMPGTB, OPC_PCMPGTW, OPC_PCMPGTD, OPC_PCMPGTQ - }; static int const punpckl_insn[4] = { OPC_PUNPCKLBW, OPC_PUNPCKLWD, OPC_PUNPCKLDQ, OPC_PUNPCKLQDQ }; @@ -3107,12 +3578,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, static int const smax_insn[4] = { OPC_PMAXSB, OPC_PMAXSW, OPC_PMAXSD, OPC_VPMAXSQ }; - static int const umin_insn[4] = { - OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_VPMINUQ - }; - static int const umax_insn[4] = { - OPC_PMAXUB, OPC_PMAXUW, OPC_PMAXUD, OPC_VPMAXUQ - }; static int const rotlv_insn[4] = { OPC_UD2, OPC_UD2, OPC_VPROLVD, OPC_VPROLVQ }; @@ -3264,29 +3729,21 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, goto gen_simd; gen_simd: tcg_debug_assert(insn != OPC_UD2); - if (type == TCG_TYPE_V256) { - insn |= P_VEXL; - } - tcg_out_vex_modrm(s, insn, a0, a1, a2); + tcg_out_vex_modrm_type(s, insn, a0, a1, a2, type); break; case INDEX_op_cmp_vec: - sub = args[3]; - if (sub == TCG_COND_EQ) { - insn = cmpeq_insn[vece]; - } else if (sub == TCG_COND_GT) { - insn = cmpgt_insn[vece]; - } else { - g_assert_not_reached(); - } - goto gen_simd; + tcg_out_cmp_vec(s, type, vece, a0, a1, a2, args[3]); + break; + + case INDEX_op_cmpsel_vec: + tcg_out_cmpsel_vec(s, type, vece, a0, a1, a2, + args[3], args[4], args[5]); + break; case INDEX_op_andc_vec: insn = OPC_PANDN; - if (type == TCG_TYPE_V256) { - insn |= P_VEXL; - } - tcg_out_vex_modrm(s, insn, a0, a2, a1); + tcg_out_vex_modrm_type(s, insn, a0, a2, a1, type); break; case INDEX_op_shli_vec: @@ -3314,10 +3771,7 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, goto gen_shift; gen_shift: tcg_debug_assert(vece != MO_8); - if (type == TCG_TYPE_V256) { - insn |= P_VEXL; - } - tcg_out_vex_modrm(s, insn, sub, a0, a1); + tcg_out_vex_modrm_type(s, insn, sub, a0, a1, type); tcg_out8(s, a2); break; @@ -3394,22 +3848,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, gen_simd_imm8: tcg_debug_assert(insn != OPC_UD2); - if (type == TCG_TYPE_V256) { - insn |= P_VEXL; - } - tcg_out_vex_modrm(s, insn, a0, a1, a2); + tcg_out_vex_modrm_type(s, insn, a0, a1, a2, type); tcg_out8(s, sub); break; - case INDEX_op_x86_vpblendvb_vec: - insn = OPC_VPBLENDVB; - if (type == TCG_TYPE_V256) { - insn |= P_VEXL; - } - tcg_out_vex_modrm(s, insn, a0, a1, a2); - tcg_out8(s, args[3] << 4); - break; - case INDEX_op_x86_psrldq_vec: tcg_out_vex_modrm(s, OPC_GRP14, 3, a0, a1); tcg_out8(s, a2); @@ -3542,7 +3984,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: @@ -3584,15 +4026,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: @@ -3610,7 +4054,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: @@ -3620,26 +4064,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); @@ -3710,8 +4166,9 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I3(x, 0, x, x); case INDEX_op_bitsel_vec: - case INDEX_op_x86_vpblendvb_vec: return C_O1_I3(x, x, x, x); + case INDEX_op_cmpsel_vec: + return C_O1_I4(x, x, x, xO, x); default: g_assert_not_reached(); @@ -3837,49 +4294,20 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } } -static void expand_vec_shi(TCGType type, unsigned vece, TCGOpcode opc, +static void expand_vec_shi(TCGType type, unsigned vece, bool right, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { - TCGv_vec t1, t2; + uint8_t mask; tcg_debug_assert(vece == MO_8); - - t1 = tcg_temp_new_vec(type); - t2 = tcg_temp_new_vec(type); - - /* - * Unpack to W, shift, and repack. Tricky bits: - * (1) Use punpck*bw x,x to produce DDCCBBAA, - * i.e. duplicate in other half of the 16-bit lane. - * (2) For right-shift, add 8 so that the high half of the lane - * becomes zero. For left-shift, and left-rotate, we must - * shift up and down again. - * (3) Step 2 leaves high half zero such that PACKUSWB - * (pack with unsigned saturation) does not modify - * the quantity. - */ - vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, - tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); - vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, - tcgv_vec_arg(t2), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); - - if (opc != INDEX_op_rotli_vec) { - imm += 8; - } - if (opc == INDEX_op_shri_vec) { - tcg_gen_shri_vec(MO_16, t1, t1, imm); - tcg_gen_shri_vec(MO_16, t2, t2, imm); + if (right) { + mask = 0xff >> imm; + tcg_gen_shri_vec(MO_16, v0, v1, imm); } else { - tcg_gen_shli_vec(MO_16, t1, t1, imm); - tcg_gen_shli_vec(MO_16, t2, t2, imm); - tcg_gen_shri_vec(MO_16, t1, t1, 8); - tcg_gen_shri_vec(MO_16, t2, t2, 8); + mask = 0xff << imm; + tcg_gen_shli_vec(MO_16, v0, v1, imm); } - - vec_gen_3(INDEX_op_x86_packus_vec, type, MO_8, - tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t2)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); + tcg_gen_and_vec(MO_8, v0, v0, tcg_constant_vec(type, MO_8, mask)); } static void expand_vec_sari(TCGType type, unsigned vece, @@ -3889,7 +4317,7 @@ static void expand_vec_sari(TCGType type, unsigned vece, switch (vece) { case MO_8: - /* Unpack to W, shift, and repack, as in expand_vec_shi. */ + /* Unpack to 16-bit, shift, and repack. */ t1 = tcg_temp_new_vec(type); t2 = tcg_temp_new_vec(type); vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, @@ -3905,6 +4333,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 @@ -3913,24 +4342,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: @@ -3943,12 +4370,7 @@ static void expand_vec_rotli(TCGType type, unsigned vece, { TCGv_vec t; - if (vece == MO_8) { - expand_vec_shi(type, vece, INDEX_op_rotli_vec, v0, v1, imm); - return; - } - - if (have_avx512vbmi2) { + if (vece != MO_8 && have_avx512vbmi2) { vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v1), imm); return; @@ -4082,152 +4504,67 @@ static void expand_vec_mul(TCGType type, unsigned vece, } } -static bool expand_vec_cmp_noinv(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec v1, TCGv_vec v2, TCGCond cond) +static TCGCond expand_vec_cond(TCGType type, unsigned vece, + TCGArg *a1, TCGArg *a2, TCGCond cond) { - enum { - NEED_INV = 1, - NEED_SWAP = 2, - NEED_BIAS = 4, - NEED_UMIN = 8, - NEED_UMAX = 16, - }; - TCGv_vec t1, t2, t3; - uint8_t fixup; + /* + * Without AVX512, there are no 64-bit unsigned comparisons. + * We must bias the inputs so that they become signed. + * All other swapping and inversion are handled during code generation. + */ + if (vece == MO_64 && !have_avx512dq && is_unsigned_cond(cond)) { + TCGv_vec v1 = temp_tcgv_vec(arg_temp(*a1)); + TCGv_vec v2 = temp_tcgv_vec(arg_temp(*a2)); + TCGv_vec t1 = tcg_temp_new_vec(type); + TCGv_vec t2 = tcg_temp_new_vec(type); + TCGv_vec t3 = tcg_constant_vec(type, vece, 1ull << ((8 << vece) - 1)); - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_GT: - fixup = 0; - break; - case TCG_COND_NE: - case TCG_COND_LE: - fixup = NEED_INV; - break; - case TCG_COND_LT: - fixup = NEED_SWAP; - break; - case TCG_COND_GE: - fixup = NEED_SWAP | NEED_INV; - break; - case TCG_COND_LEU: - if (tcg_can_emit_vec_op(INDEX_op_umin_vec, type, vece)) { - fixup = NEED_UMIN; - } else { - fixup = NEED_BIAS | NEED_INV; - } - break; - case TCG_COND_GTU: - if (tcg_can_emit_vec_op(INDEX_op_umin_vec, type, vece)) { - fixup = NEED_UMIN | NEED_INV; - } else { - fixup = NEED_BIAS; - } - break; - case TCG_COND_GEU: - if (tcg_can_emit_vec_op(INDEX_op_umax_vec, type, vece)) { - fixup = NEED_UMAX; - } else { - fixup = NEED_BIAS | NEED_SWAP | NEED_INV; - } - break; - case TCG_COND_LTU: - if (tcg_can_emit_vec_op(INDEX_op_umax_vec, type, vece)) { - fixup = NEED_UMAX | NEED_INV; - } else { - fixup = NEED_BIAS | NEED_SWAP; - } - break; - default: - g_assert_not_reached(); - } - - if (fixup & NEED_INV) { - cond = tcg_invert_cond(cond); - } - if (fixup & NEED_SWAP) { - t1 = v1, v1 = v2, v2 = t1; - cond = tcg_swap_cond(cond); - } - - t1 = t2 = NULL; - if (fixup & (NEED_UMIN | NEED_UMAX)) { - t1 = tcg_temp_new_vec(type); - if (fixup & NEED_UMIN) { - tcg_gen_umin_vec(vece, t1, v1, v2); - } else { - tcg_gen_umax_vec(vece, t1, v1, v2); - } - v2 = t1; - cond = TCG_COND_EQ; - } else if (fixup & NEED_BIAS) { - t1 = tcg_temp_new_vec(type); - t2 = tcg_temp_new_vec(type); - t3 = tcg_constant_vec(type, vece, 1ull << ((8 << vece) - 1)); tcg_gen_sub_vec(vece, t1, v1, t3); tcg_gen_sub_vec(vece, t2, v2, t3); - v1 = t1; - v2 = t2; + *a1 = tcgv_vec_arg(t1); + *a2 = tcgv_vec_arg(t2); cond = tcg_signed_cond(cond); } + return cond; +} - tcg_debug_assert(cond == TCG_COND_EQ || cond == TCG_COND_GT); +static void expand_vec_cmp(TCGType type, unsigned vece, TCGArg a0, + TCGArg a1, TCGArg a2, TCGCond cond) +{ + cond = expand_vec_cond(type, vece, &a1, &a2, cond); /* Expand directly; do not recurse. */ - vec_gen_4(INDEX_op_cmp_vec, type, vece, - tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v2), cond); - - if (t1) { - tcg_temp_free_vec(t1); - if (t2) { - tcg_temp_free_vec(t2); - } - } - return fixup & NEED_INV; + vec_gen_4(INDEX_op_cmp_vec, type, vece, a0, a1, a2, cond); } -static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec v1, TCGv_vec v2, TCGCond cond) +static void expand_vec_cmpsel(TCGType type, unsigned vece, TCGArg a0, + TCGArg a1, TCGArg a2, + TCGArg a3, TCGArg a4, TCGCond cond) { - if (expand_vec_cmp_noinv(type, vece, v0, v1, v2, cond)) { - tcg_gen_not_vec(vece, v0, v0); - } -} - -static void expand_vec_cmpsel(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec c1, TCGv_vec c2, - TCGv_vec v3, TCGv_vec v4, TCGCond cond) -{ - TCGv_vec t = tcg_temp_new_vec(type); - - if (expand_vec_cmp_noinv(type, vece, t, c1, c2, cond)) { - /* Invert the sense of the compare by swapping arguments. */ - TCGv_vec x; - x = v3, v3 = v4, v4 = x; - } - vec_gen_4(INDEX_op_x86_vpblendvb_vec, type, vece, - tcgv_vec_arg(v0), tcgv_vec_arg(v4), - tcgv_vec_arg(v3), tcgv_vec_arg(t)); - tcg_temp_free_vec(t); + cond = expand_vec_cond(type, vece, &a1, &a2, cond); + /* Expand directly; do not recurse. */ + vec_gen_6(INDEX_op_cmpsel_vec, type, vece, a0, a1, a2, a3, a4, cond); } void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { va_list va; - TCGArg a2; - TCGv_vec v0, v1, v2, v3, v4; + TCGArg a1, a2, a3, a4, a5; + TCGv_vec v0, v1, v2; va_start(va, a0); - v0 = temp_tcgv_vec(arg_temp(a0)); - v1 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); + a1 = va_arg(va, TCGArg); a2 = va_arg(va, TCGArg); + v0 = temp_tcgv_vec(arg_temp(a0)); + v1 = temp_tcgv_vec(arg_temp(a1)); switch (opc) { case INDEX_op_shli_vec: - case INDEX_op_shri_vec: - expand_vec_shi(type, vece, opc, v0, v1, a2); + expand_vec_shi(type, vece, false, v0, v1, a2); + break; + case INDEX_op_shri_vec: + expand_vec_shi(type, vece, true, v0, v1, a2); break; - case INDEX_op_sari_vec: expand_vec_sari(type, vece, v0, v1, a2); break; @@ -4255,15 +4592,15 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, break; case INDEX_op_cmp_vec: - v2 = temp_tcgv_vec(arg_temp(a2)); - expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); + a3 = va_arg(va, TCGArg); + expand_vec_cmp(type, vece, a0, a1, a2, a3); break; case INDEX_op_cmpsel_vec: - v2 = temp_tcgv_vec(arg_temp(a2)); - v3 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); - v4 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); - expand_vec_cmpsel(type, vece, v0, v1, v2, v3, v4, va_arg(va, TCGArg)); + a3 = va_arg(va, TCGArg); + a4 = va_arg(va, TCGArg); + a5 = va_arg(va, TCGArg); + expand_vec_cmpsel(type, vece, a0, a1, a2, a3, a4, a5); break; default: @@ -4324,35 +4661,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, @@ -4375,6 +4712,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); @@ -4382,70 +4724,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; @@ -4477,6 +4755,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 d23ab76458..7ae7828485 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 @@ -184,6 +197,11 @@ extern bool have_movbe; #define TCG_TARGET_HAS_fpu (TCG_TARGET_REG_BITS == 64) +#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 @@ -207,11 +225,12 @@ extern bool have_movbe; #define TCG_TARGET_HAS_sat_vec 1 #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec have_avx512vl -#define TCG_TARGET_HAS_cmpsel_vec -1 +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec have_avx512bw #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, @@ -220,14 +239,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. @@ -238,9 +249,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/i386/tcg-target.opc.h b/tcg/i386/tcg-target.opc.h index b5f403e35e..4ffc084bda 100644 --- a/tcg/i386/tcg-target.opc.h +++ b/tcg/i386/tcg-target.opc.h @@ -25,7 +25,6 @@ */ DEF(x86_shufps_vec, 1, 2, 1, IMPLVEC) -DEF(x86_vpblendvb_vec, 1, 3, 0, IMPLVEC) DEF(x86_blend_vec, 1, 2, 1, IMPLVEC) DEF(x86_packss_vec, 1, 2, 0, IMPLVEC) DEF(x86_packus_vec, 1, 2, 0, IMPLVEC) diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index d162571856..6bb8656fd8 100644 --- a/tcg/loongarch64/tcg-insn-defs.c.inc +++ b/tcg/loongarch64/tcg-insn-defs.c.inc @@ -4,11 +4,13 @@ * * This file is auto-generated by genqemutcgdefs from * https://github.com/loongson-community/loongarch-opcodes, - * from commit 961f0c60f5b63e574d785995600c71ad5413fdc4. + * from commit 7f353fb69bd99ce6edfad7ad63948c4bb526f0bf. * DO NOT EDIT. */ typedef enum { + OPC_MOVGR2SCR = 0x00000800, + OPC_MOVSCR2GR = 0x00000c00, OPC_CLZ_W = 0x00001400, OPC_CTZ_W = 0x00001c00, OPC_CLZ_D = 0x00002400, @@ -38,6 +40,8 @@ typedef enum { OPC_SLL_D = 0x00188000, OPC_SRL_D = 0x00190000, OPC_SRA_D = 0x00198000, + OPC_ROTR_B = 0x001a0000, + OPC_ROTR_H = 0x001a8000, OPC_ROTR_W = 0x001b0000, OPC_ROTR_D = 0x001b8000, OPC_MUL_W = 0x001c0000, @@ -60,12 +64,17 @@ typedef enum { OPC_SRLI_D = 0x00450000, OPC_SRAI_W = 0x00488000, OPC_SRAI_D = 0x00490000, + OPC_ROTRI_B = 0x004c2000, + OPC_ROTRI_H = 0x004c4000, OPC_ROTRI_W = 0x004c8000, OPC_ROTRI_D = 0x004d0000, OPC_BSTRINS_W = 0x00600000, OPC_BSTRPICK_W = 0x00608000, OPC_BSTRINS_D = 0x00800000, OPC_BSTRPICK_D = 0x00c00000, + OPC_FMOV_D = 0x01149800, + OPC_MOVGR2FR_D = 0x0114a800, + OPC_MOVFR2GR_D = 0x0114b800, OPC_SLTI = 0x02000000, OPC_SLTUI = 0x02400000, OPC_ADDI_W = 0x02800000, @@ -74,6 +83,11 @@ typedef enum { OPC_ANDI = 0x03400000, OPC_ORI = 0x03800000, OPC_XORI = 0x03c00000, + OPC_VBITSEL_V = 0x0d100000, + OPC_XVBITSEL_V = 0x0d200000, + OPC_VSHUF_B = 0x0d500000, + OPC_XVSHUF_B = 0x0d600000, + OPC_ADDU16I_D = 0x10000000, OPC_LU12I_W = 0x14000000, OPC_CU32I_D = 0x16000000, OPC_PCADDU2I = 0x18000000, @@ -91,6 +105,30 @@ typedef enum { OPC_LD_BU = 0x2a000000, OPC_LD_HU = 0x2a400000, OPC_LD_WU = 0x2a800000, + OPC_FLD_S = 0x2b000000, + OPC_FST_S = 0x2b400000, + OPC_FLD_D = 0x2b800000, + OPC_FST_D = 0x2bc00000, + OPC_VLD = 0x2c000000, + OPC_VST = 0x2c400000, + OPC_XVLD = 0x2c800000, + OPC_XVST = 0x2cc00000, + 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_XVLDREPL_D = 0x32100000, + OPC_XVLDREPL_W = 0x32200000, + OPC_XVLDREPL_H = 0x32400000, + OPC_XVLDREPL_B = 0x32800000, + OPC_XVSTELM_D = 0x33100000, + OPC_XVSTELM_W = 0x33200000, + OPC_XVSTELM_H = 0x33400000, + OPC_XVSTELM_B = 0x33800000, OPC_LDX_B = 0x38000000, OPC_LDX_H = 0x38040000, OPC_LDX_W = 0x38080000, @@ -102,7 +140,17 @@ typedef enum { OPC_LDX_BU = 0x38200000, OPC_LDX_HU = 0x38240000, OPC_LDX_WU = 0x38280000, + OPC_FLDX_S = 0x38300000, + OPC_FLDX_D = 0x38340000, + OPC_FSTX_S = 0x38380000, + OPC_FSTX_D = 0x383c0000, + OPC_VLDX = 0x38400000, + OPC_VSTX = 0x38440000, + OPC_XVLDX = 0x38480000, + OPC_XVSTX = 0x384c0000, OPC_DBAR = 0x38720000, + OPC_JISCR0 = 0x48000200, + OPC_JISCR1 = 0x48000300, OPC_JIRL = 0x4c000000, OPC_B = 0x50000000, OPC_BL = 0x54000000, @@ -112,6 +160,389 @@ 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_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_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_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_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_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_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_VNEG_B = 0x729c3000, + OPC_VNEG_H = 0x729c3400, + OPC_VNEG_W = 0x729c3800, + OPC_VNEG_D = 0x729c3c00, + 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_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_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_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_VBITSELI_B = 0x73c40000, + OPC_VANDI_B = 0x73d00000, + OPC_VORI_B = 0x73d40000, + OPC_VXORI_B = 0x73d80000, + OPC_VNORI_B = 0x73dc0000, + OPC_VLDI = 0x73e00000, + OPC_XVSEQ_B = 0x74000000, + OPC_XVSEQ_H = 0x74008000, + OPC_XVSEQ_W = 0x74010000, + OPC_XVSEQ_D = 0x74018000, + OPC_XVSLE_B = 0x74020000, + OPC_XVSLE_H = 0x74028000, + OPC_XVSLE_W = 0x74030000, + OPC_XVSLE_D = 0x74038000, + OPC_XVSLE_BU = 0x74040000, + OPC_XVSLE_HU = 0x74048000, + OPC_XVSLE_WU = 0x74050000, + OPC_XVSLE_DU = 0x74058000, + OPC_XVSLT_B = 0x74060000, + OPC_XVSLT_H = 0x74068000, + OPC_XVSLT_W = 0x74070000, + OPC_XVSLT_D = 0x74078000, + OPC_XVSLT_BU = 0x74080000, + OPC_XVSLT_HU = 0x74088000, + OPC_XVSLT_WU = 0x74090000, + OPC_XVSLT_DU = 0x74098000, + OPC_XVADD_B = 0x740a0000, + OPC_XVADD_H = 0x740a8000, + OPC_XVADD_W = 0x740b0000, + OPC_XVADD_D = 0x740b8000, + OPC_XVSUB_B = 0x740c0000, + OPC_XVSUB_H = 0x740c8000, + OPC_XVSUB_W = 0x740d0000, + OPC_XVSUB_D = 0x740d8000, + OPC_XVSADD_B = 0x74460000, + OPC_XVSADD_H = 0x74468000, + OPC_XVSADD_W = 0x74470000, + OPC_XVSADD_D = 0x74478000, + OPC_XVSSUB_B = 0x74480000, + OPC_XVSSUB_H = 0x74488000, + OPC_XVSSUB_W = 0x74490000, + OPC_XVSSUB_D = 0x74498000, + OPC_XVSADD_BU = 0x744a0000, + OPC_XVSADD_HU = 0x744a8000, + OPC_XVSADD_WU = 0x744b0000, + OPC_XVSADD_DU = 0x744b8000, + OPC_XVSSUB_BU = 0x744c0000, + OPC_XVSSUB_HU = 0x744c8000, + OPC_XVSSUB_WU = 0x744d0000, + OPC_XVSSUB_DU = 0x744d8000, + OPC_XVMAX_B = 0x74700000, + OPC_XVMAX_H = 0x74708000, + OPC_XVMAX_W = 0x74710000, + OPC_XVMAX_D = 0x74718000, + OPC_XVMIN_B = 0x74720000, + OPC_XVMIN_H = 0x74728000, + OPC_XVMIN_W = 0x74730000, + OPC_XVMIN_D = 0x74738000, + OPC_XVMAX_BU = 0x74740000, + OPC_XVMAX_HU = 0x74748000, + OPC_XVMAX_WU = 0x74750000, + OPC_XVMAX_DU = 0x74758000, + OPC_XVMIN_BU = 0x74760000, + OPC_XVMIN_HU = 0x74768000, + OPC_XVMIN_WU = 0x74770000, + OPC_XVMIN_DU = 0x74778000, + OPC_XVMUL_B = 0x74840000, + OPC_XVMUL_H = 0x74848000, + OPC_XVMUL_W = 0x74850000, + OPC_XVMUL_D = 0x74858000, + OPC_XVSLL_B = 0x74e80000, + OPC_XVSLL_H = 0x74e88000, + OPC_XVSLL_W = 0x74e90000, + OPC_XVSLL_D = 0x74e98000, + OPC_XVSRL_B = 0x74ea0000, + OPC_XVSRL_H = 0x74ea8000, + OPC_XVSRL_W = 0x74eb0000, + OPC_XVSRL_D = 0x74eb8000, + OPC_XVSRA_B = 0x74ec0000, + OPC_XVSRA_H = 0x74ec8000, + OPC_XVSRA_W = 0x74ed0000, + OPC_XVSRA_D = 0x74ed8000, + OPC_XVROTR_B = 0x74ee0000, + OPC_XVROTR_H = 0x74ee8000, + OPC_XVROTR_W = 0x74ef0000, + OPC_XVROTR_D = 0x74ef8000, + OPC_XVREPLVE_B = 0x75220000, + OPC_XVREPLVE_H = 0x75228000, + OPC_XVREPLVE_W = 0x75230000, + OPC_XVREPLVE_D = 0x75238000, + OPC_XVAND_V = 0x75260000, + OPC_XVOR_V = 0x75268000, + OPC_XVXOR_V = 0x75270000, + OPC_XVNOR_V = 0x75278000, + OPC_XVANDN_V = 0x75280000, + OPC_XVORN_V = 0x75288000, + OPC_XVSEQI_B = 0x76800000, + OPC_XVSEQI_H = 0x76808000, + OPC_XVSEQI_W = 0x76810000, + OPC_XVSEQI_D = 0x76818000, + OPC_XVSLEI_B = 0x76820000, + OPC_XVSLEI_H = 0x76828000, + OPC_XVSLEI_W = 0x76830000, + OPC_XVSLEI_D = 0x76838000, + OPC_XVSLEI_BU = 0x76840000, + OPC_XVSLEI_HU = 0x76848000, + OPC_XVSLEI_WU = 0x76850000, + OPC_XVSLEI_DU = 0x76858000, + OPC_XVSLTI_B = 0x76860000, + OPC_XVSLTI_H = 0x76868000, + OPC_XVSLTI_W = 0x76870000, + OPC_XVSLTI_D = 0x76878000, + OPC_XVSLTI_BU = 0x76880000, + OPC_XVSLTI_HU = 0x76888000, + OPC_XVSLTI_WU = 0x76890000, + OPC_XVSLTI_DU = 0x76898000, + OPC_XVADDI_BU = 0x768a0000, + OPC_XVADDI_HU = 0x768a8000, + OPC_XVADDI_WU = 0x768b0000, + OPC_XVADDI_DU = 0x768b8000, + OPC_XVSUBI_BU = 0x768c0000, + OPC_XVSUBI_HU = 0x768c8000, + OPC_XVSUBI_WU = 0x768d0000, + OPC_XVSUBI_DU = 0x768d8000, + OPC_XVMAXI_B = 0x76900000, + OPC_XVMAXI_H = 0x76908000, + OPC_XVMAXI_W = 0x76910000, + OPC_XVMAXI_D = 0x76918000, + OPC_XVMINI_B = 0x76920000, + OPC_XVMINI_H = 0x76928000, + OPC_XVMINI_W = 0x76930000, + OPC_XVMINI_D = 0x76938000, + OPC_XVMAXI_BU = 0x76940000, + OPC_XVMAXI_HU = 0x76948000, + OPC_XVMAXI_WU = 0x76950000, + OPC_XVMAXI_DU = 0x76958000, + OPC_XVMINI_BU = 0x76960000, + OPC_XVMINI_HU = 0x76968000, + OPC_XVMINI_WU = 0x76970000, + OPC_XVMINI_DU = 0x76978000, + OPC_XVNEG_B = 0x769c3000, + OPC_XVNEG_H = 0x769c3400, + OPC_XVNEG_W = 0x769c3800, + OPC_XVNEG_D = 0x769c3c00, + OPC_XVREPLGR2VR_B = 0x769f0000, + OPC_XVREPLGR2VR_H = 0x769f0400, + OPC_XVREPLGR2VR_W = 0x769f0800, + OPC_XVREPLGR2VR_D = 0x769f0c00, + OPC_XVROTRI_B = 0x76a02000, + OPC_XVROTRI_H = 0x76a04000, + OPC_XVROTRI_W = 0x76a08000, + OPC_XVROTRI_D = 0x76a10000, + OPC_XVINSGR2VR_W = 0x76ebc000, + OPC_XVINSGR2VR_D = 0x76ebe000, + OPC_XVPICKVE2GR_W = 0x76efc000, + OPC_XVPICKVE2GR_D = 0x76efe000, + OPC_XVPICKVE2GR_WU = 0x76f3c000, + OPC_XVPICKVE2GR_DU = 0x76f3e000, + OPC_XVREPL128VEI_B = 0x76f78000, + OPC_XVREPL128VEI_H = 0x76f7c000, + OPC_XVREPL128VEI_W = 0x76f7e000, + OPC_XVREPL128VEI_D = 0x76f7f000, + OPC_XVREPLVE0_B = 0x77070000, + OPC_XVREPLVE0_H = 0x77078000, + OPC_XVREPLVE0_W = 0x7707c000, + OPC_XVREPLVE0_D = 0x7707e000, + OPC_XVREPLVE0_Q = 0x7707f000, + OPC_XVBITCLRI_B = 0x77102000, + OPC_XVBITCLRI_H = 0x77104000, + OPC_XVBITCLRI_W = 0x77108000, + OPC_XVBITCLRI_D = 0x77110000, + OPC_XVBITSETI_B = 0x77142000, + OPC_XVBITSETI_H = 0x77144000, + OPC_XVBITSETI_W = 0x77148000, + OPC_XVBITSETI_D = 0x77150000, + OPC_XVBITREVI_B = 0x77182000, + OPC_XVBITREVI_H = 0x77184000, + OPC_XVBITREVI_W = 0x77188000, + OPC_XVBITREVI_D = 0x77190000, + OPC_XVSLLI_B = 0x772c2000, + OPC_XVSLLI_H = 0x772c4000, + OPC_XVSLLI_W = 0x772c8000, + OPC_XVSLLI_D = 0x772d0000, + OPC_XVSRLI_B = 0x77302000, + OPC_XVSRLI_H = 0x77304000, + OPC_XVSRLI_W = 0x77308000, + OPC_XVSRLI_D = 0x77310000, + OPC_XVSRAI_B = 0x77342000, + OPC_XVSRAI_H = 0x77344000, + OPC_XVSRAI_W = 0x77348000, + OPC_XVSRAI_D = 0x77350000, + OPC_XVBITSELI_B = 0x77c40000, + OPC_XVANDI_B = 0x77d00000, + OPC_XVORI_B = 0x77d40000, + OPC_XVXORI_B = 0x77d80000, + OPC_XVNORI_B = 0x77dc0000, + OPC_XVLDI = 0x77e00000, } LoongArchInsn; static int32_t __attribute__((unused)) @@ -132,6 +563,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 +577,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_dfj_insn(LoongArchInsn opc, TCGReg d, TCGReg fj) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); + return encode_dj_slots(opc, d, fj & 0x1f); +} + static int32_t __attribute__((unused)) encode_dj_insn(LoongArchInsn opc, TCGReg d, TCGReg j) { @@ -189,6 +642,24 @@ encode_djuk12_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk12) return encode_djk_slots(opc, d, j, uk12); } +static int32_t __attribute__((unused)) +encode_djuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk3) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, d, j, uk3); +} + +static int32_t __attribute__((unused)) +encode_djuk4_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk4) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, d, j, uk4); +} + static int32_t __attribute__((unused)) encode_djuk5_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk5) { @@ -237,6 +708,102 @@ encode_dsj20_insn(LoongArchInsn opc, TCGReg d, int32_t sj20) return encode_dj_slots(opc, d, sj20 & 0xfffff); } +static int32_t __attribute__((unused)) +encode_dtj_insn(LoongArchInsn opc, TCGReg d, TCGReg tj) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(tj >= 0 && tj <= 0x3); + return encode_dj_slots(opc, d, tj); +} + +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_dxjuk2_insn(LoongArchInsn opc, TCGReg d, TCGReg xj, uint32_t uk2) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, d, xj & 0x1f, uk2); +} + +static int32_t __attribute__((unused)) +encode_dxjuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg xj, uint32_t uk3) +{ + tcg_debug_assert(d >= 0 && d <= 0x1f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, d, xj & 0x1f, uk3); +} + +static int32_t __attribute__((unused)) +encode_fdfj_insn(LoongArchInsn opc, TCGReg fd, TCGReg fj) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); + return encode_dj_slots(opc, fd & 0x1f, fj & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_fdj_insn(LoongArchInsn opc, TCGReg fd, TCGReg j) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, fd & 0x1f, j); +} + +static int32_t __attribute__((unused)) +encode_fdjk_insn(LoongArchInsn opc, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, fd & 0x1f, j, k); +} + +static int32_t __attribute__((unused)) +encode_fdjsk12_insn(LoongArchInsn opc, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk12 >= -0x800 && sk12 <= 0x7ff); + return encode_djk_slots(opc, fd & 0x1f, j, sk12 & 0xfff); +} + static int32_t __attribute__((unused)) encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) { @@ -244,6 +811,21 @@ encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) return encode_dk_slots(opc, (sd10k16 >> 16) & 0x3ff, sd10k16 & 0xffff); } +static int32_t __attribute__((unused)) +encode_sd5k16_insn(LoongArchInsn opc, int32_t sd5k16) +{ + tcg_debug_assert(sd5k16 >= -0x100000 && sd5k16 <= 0xfffff); + return encode_dk_slots(opc, (sd5k16 >> 16) & 0x1f, sd5k16 & 0xffff); +} + +static int32_t __attribute__((unused)) +encode_tdj_insn(LoongArchInsn opc, TCGReg td, TCGReg j) +{ + tcg_debug_assert(td >= 0 && td <= 0x3); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, td, j); +} + static int32_t __attribute__((unused)) encode_ud15_insn(LoongArchInsn opc, uint32_t ud15) { @@ -251,6 +833,502 @@ 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_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); +} + +static int32_t __attribute__((unused)) +encode_xdj_insn(LoongArchInsn opc, TCGReg xd, TCGReg j) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + return encode_dj_slots(opc, xd & 0x1f, j); +} + +static int32_t __attribute__((unused)) +encode_xdjk_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, TCGReg k) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, xd & 0x1f, j, k); +} + +static int32_t __attribute__((unused)) +encode_xdjsk10_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk10) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk10 >= -0x200 && sk10 <= 0x1ff); + return encode_djk_slots(opc, xd & 0x1f, j, sk10 & 0x3ff); +} + +static int32_t __attribute__((unused)) +encode_xdjsk11_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk11) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk11 >= -0x400 && sk11 <= 0x3ff); + return encode_djk_slots(opc, xd & 0x1f, j, sk11 & 0x7ff); +} + +static int32_t __attribute__((unused)) +encode_xdjsk12_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk12 >= -0x800 && sk12 <= 0x7ff); + return encode_djk_slots(opc, xd & 0x1f, j, sk12 & 0xfff); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un2_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un2) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 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, xd & 0x1f, j, sk8 & 0xff, un2); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un3_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un3) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 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, xd & 0x1f, j, sk8 & 0xff, un3); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un4_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un4) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 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, xd & 0x1f, j, sk8 & 0xff, un4); +} + +static int32_t __attribute__((unused)) +encode_xdjsk8un5_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un5) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk8 >= -0x80 && sk8 <= 0x7f); + tcg_debug_assert(un5 <= 0x1f); + return encode_djkn_slots(opc, xd & 0x1f, j, sk8 & 0xff, un5); +} + +static int32_t __attribute__((unused)) +encode_xdjsk9_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, int32_t sk9) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(sk9 >= -0x100 && sk9 <= 0xff); + return encode_djk_slots(opc, xd & 0x1f, j, sk9 & 0x1ff); +} + +static int32_t __attribute__((unused)) +encode_xdjuk2_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, uint32_t uk2) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, xd & 0x1f, j, uk2); +} + +static int32_t __attribute__((unused)) +encode_xdjuk3_insn(LoongArchInsn opc, TCGReg xd, TCGReg j, uint32_t uk3) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(j >= 0 && j <= 0x1f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, xd & 0x1f, j, uk3); +} + +static int32_t __attribute__((unused)) +encode_xdsj13_insn(LoongArchInsn opc, TCGReg xd, int32_t sj13) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(sj13 >= -0x1000 && sj13 <= 0xfff); + return encode_dj_slots(opc, xd & 0x1f, sj13 & 0x1fff); +} + +static int32_t __attribute__((unused)) +encode_xdxj_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + return encode_dj_slots(opc, xd & 0x1f, xj & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_xdxjk_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(k >= 0 && k <= 0x1f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, k); +} + +static int32_t __attribute__((unused)) +encode_xdxjsk5_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(sk5 >= -0x10 && sk5 <= 0xf); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, sk5 & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk1_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk1) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk1 <= 0x1); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk1); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk2_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk2) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk2 <= 0x3); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk2); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk3_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk3 <= 0x7); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk3); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk4_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk4 <= 0xf); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk4); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk5_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk5 <= 0x1f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk5); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk6_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk6 <= 0x3f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk6); +} + +static int32_t __attribute__((unused)) +encode_xdxjuk8_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(uk8 <= 0xff); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, uk8); +} + +static int32_t __attribute__((unused)) +encode_xdxjxk_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(xk >= 0x20 && xk <= 0x3f); + return encode_djk_slots(opc, xd & 0x1f, xj & 0x1f, xk & 0x1f); +} + +static int32_t __attribute__((unused)) +encode_xdxjxkxa_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg xk, + TCGReg xa) +{ + tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); + tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); + tcg_debug_assert(xk >= 0x20 && xk <= 0x3f); + tcg_debug_assert(xa >= 0x20 && xa <= 0x3f); + return encode_djka_slots(opc, xd & 0x1f, xj & 0x1f, xk & 0x1f, xa & 0x1f); +} + +/* Emits the `movgr2scr td, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movgr2scr(TCGContext *s, TCGReg td, TCGReg j) +{ + tcg_out32(s, encode_tdj_insn(OPC_MOVGR2SCR, td, j)); +} + +/* Emits the `movscr2gr d, tj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movscr2gr(TCGContext *s, TCGReg d, TCGReg tj) +{ + tcg_out32(s, encode_dtj_insn(OPC_MOVSCR2GR, d, tj)); +} + /* Emits the `clz.w d, j` instruction. */ static void __attribute__((unused)) tcg_out_opc_clz_w(TCGContext *s, TCGReg d, TCGReg j) @@ -454,6 +1532,20 @@ tcg_out_opc_sra_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) tcg_out32(s, encode_djk_insn(OPC_SRA_D, d, j, k)); } +/* Emits the `rotr.b d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotr_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ROTR_B, d, j, k)); +} + +/* Emits the `rotr.h d, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotr_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_djk_insn(OPC_ROTR_H, d, j, k)); +} + /* Emits the `rotr.w d, j, k` instruction. */ static void __attribute__((unused)) tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) @@ -608,6 +1700,20 @@ tcg_out_opc_srai_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) tcg_out32(s, encode_djuk6_insn(OPC_SRAI_D, d, j, uk6)); } +/* Emits the `rotri.b d, j, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotri_b(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk3) +{ + tcg_out32(s, encode_djuk3_insn(OPC_ROTRI_B, d, j, uk3)); +} + +/* Emits the `rotri.h d, j, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_rotri_h(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk4) +{ + tcg_out32(s, encode_djuk4_insn(OPC_ROTRI_H, d, j, uk4)); +} + /* Emits the `rotri.w d, j, uk5` instruction. */ static void __attribute__((unused)) tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) @@ -654,6 +1760,27 @@ tcg_out_opc_bstrpick_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRPICK_D, d, j, uk6, um6)); } +/* Emits the `fmov.d fd, fj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fmov_d(TCGContext *s, TCGReg fd, TCGReg fj) +{ + tcg_out32(s, encode_fdfj_insn(OPC_FMOV_D, fd, fj)); +} + +/* Emits the `movgr2fr.d fd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movgr2fr_d(TCGContext *s, TCGReg fd, TCGReg j) +{ + tcg_out32(s, encode_fdj_insn(OPC_MOVGR2FR_D, fd, j)); +} + +/* Emits the `movfr2gr.d d, fj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_movfr2gr_d(TCGContext *s, TCGReg d, TCGReg fj) +{ + tcg_out32(s, encode_dfj_insn(OPC_MOVFR2GR_D, d, fj)); +} + /* Emits the `slti d, j, sk12` instruction. */ static void __attribute__((unused)) tcg_out_opc_slti(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) @@ -710,6 +1837,42 @@ 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 `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 `xvbitsel.v xd, xj, xk, xa` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitsel_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk, + TCGReg xa) +{ + tcg_out32(s, encode_xdxjxkxa_insn(OPC_XVBITSEL_V, xd, xj, xk, xa)); +} + +/* 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 `xvshuf.b xd, xj, xk, xa` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvshuf_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk, TCGReg xa) +{ + tcg_out32(s, encode_xdxjxkxa_insn(OPC_XVSHUF_B, xd, xj, xk, xa)); +} + +/* 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 +1992,182 @@ 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 `fld.s fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fld_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_S, fd, j, sk12)); +} + +/* Emits the `fst.s fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fst_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FST_S, fd, j, sk12)); +} + +/* Emits the `fld.d fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fld_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_D, fd, j, sk12)); +} + +/* Emits the `fst.d fd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fst_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_fdjsk12_insn(OPC_FST_D, fd, 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 `xvld xd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvld(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_xdjsk12_insn(OPC_XVLD, xd, j, sk12)); +} + +/* Emits the `xvst xd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvst(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_xdjsk12_insn(OPC_XVST, xd, 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 `xvldrepl.d xd, j, sk9` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_d(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk9) +{ + tcg_out32(s, encode_xdjsk9_insn(OPC_XVLDREPL_D, xd, j, sk9)); +} + +/* Emits the `xvldrepl.w xd, j, sk10` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_w(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk10) +{ + tcg_out32(s, encode_xdjsk10_insn(OPC_XVLDREPL_W, xd, j, sk10)); +} + +/* Emits the `xvldrepl.h xd, j, sk11` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_h(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk11) +{ + tcg_out32(s, encode_xdjsk11_insn(OPC_XVLDREPL_H, xd, j, sk11)); +} + +/* Emits the `xvldrepl.b xd, j, sk12` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldrepl_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +{ + tcg_out32(s, encode_xdjsk12_insn(OPC_XVLDREPL_B, xd, j, sk12)); +} + +/* Emits the `xvstelm.d xd, j, sk8, un2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_d(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un2) +{ + tcg_out32(s, encode_xdjsk8un2_insn(OPC_XVSTELM_D, xd, j, sk8, un2)); +} + +/* Emits the `xvstelm.w xd, j, sk8, un3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_w(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un3) +{ + tcg_out32(s, encode_xdjsk8un3_insn(OPC_XVSTELM_W, xd, j, sk8, un3)); +} + +/* Emits the `xvstelm.h xd, j, sk8, un4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_h(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un4) +{ + tcg_out32(s, encode_xdjsk8un4_insn(OPC_XVSTELM_H, xd, j, sk8, un4)); +} + +/* Emits the `xvstelm.b xd, j, sk8, un5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstelm_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, + uint32_t un5) +{ + tcg_out32(s, encode_xdjsk8un5_insn(OPC_XVSTELM_B, xd, j, sk8, un5)); +} + /* 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 +2245,62 @@ 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 `fldx.s fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fldx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FLDX_S, fd, j, k)); +} + +/* Emits the `fldx.d fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fldx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FLDX_D, fd, j, k)); +} + +/* Emits the `fstx.s fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fstx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FSTX_S, fd, j, k)); +} + +/* Emits the `fstx.d fd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_fstx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_fdjk_insn(OPC_FSTX_D, fd, 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 `xvldx xd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_xdjk_insn(OPC_XVLDX, xd, j, k)); +} + +/* Emits the `xvstx xd, j, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvstx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +{ + tcg_out32(s, encode_xdjk_insn(OPC_XVSTX, xd, j, k)); +} + /* Emits the `dbar ud15` instruction. */ static void __attribute__((unused)) tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) @@ -913,6 +2308,20 @@ tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) tcg_out32(s, encode_ud15_insn(OPC_DBAR, ud15)); } +/* Emits the `jiscr0 sd5k16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_jiscr0(TCGContext *s, int32_t sd5k16) +{ + tcg_out32(s, encode_sd5k16_insn(OPC_JISCR0, sd5k16)); +} + +/* Emits the `jiscr1 sd5k16` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_jiscr1(TCGContext *s, int32_t sd5k16) +{ + tcg_out32(s, encode_sd5k16_insn(OPC_JISCR1, sd5k16)); +} + /* Emits the `jirl d, j, sk16` instruction. */ static void __attribute__((unused)) tcg_out_opc_jirl(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) @@ -976,4 +2385,2685 @@ 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 `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 `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 `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 `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 `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 `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 `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 `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 `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 `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 `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 `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 `xvseq.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_B, xd, xj, xk)); +} + +/* Emits the `xvseq.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_H, xd, xj, xk)); +} + +/* Emits the `xvseq.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_W, xd, xj, xk)); +} + +/* Emits the `xvseq.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseq_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSEQ_D, xd, xj, xk)); +} + +/* Emits the `xvsle.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_B, xd, xj, xk)); +} + +/* Emits the `xvsle.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_H, xd, xj, xk)); +} + +/* Emits the `xvsle.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_W, xd, xj, xk)); +} + +/* Emits the `xvsle.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_D, xd, xj, xk)); +} + +/* Emits the `xvsle.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_BU, xd, xj, xk)); +} + +/* Emits the `xvsle.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_HU, xd, xj, xk)); +} + +/* Emits the `xvsle.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_WU, xd, xj, xk)); +} + +/* Emits the `xvsle.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsle_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLE_DU, xd, xj, xk)); +} + +/* Emits the `xvslt.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_B, xd, xj, xk)); +} + +/* Emits the `xvslt.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_H, xd, xj, xk)); +} + +/* Emits the `xvslt.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_W, xd, xj, xk)); +} + +/* Emits the `xvslt.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_D, xd, xj, xk)); +} + +/* Emits the `xvslt.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_BU, xd, xj, xk)); +} + +/* Emits the `xvslt.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_HU, xd, xj, xk)); +} + +/* Emits the `xvslt.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_WU, xd, xj, xk)); +} + +/* Emits the `xvslt.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslt_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLT_DU, xd, xj, xk)); +} + +/* Emits the `xvadd.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_B, xd, xj, xk)); +} + +/* Emits the `xvadd.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_H, xd, xj, xk)); +} + +/* Emits the `xvadd.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_W, xd, xj, xk)); +} + +/* Emits the `xvadd.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvadd_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVADD_D, xd, xj, xk)); +} + +/* Emits the `xvsub.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_B, xd, xj, xk)); +} + +/* Emits the `xvsub.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_H, xd, xj, xk)); +} + +/* Emits the `xvsub.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_W, xd, xj, xk)); +} + +/* Emits the `xvsub.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsub_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSUB_D, xd, xj, xk)); +} + +/* Emits the `xvsadd.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_B, xd, xj, xk)); +} + +/* Emits the `xvsadd.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_H, xd, xj, xk)); +} + +/* Emits the `xvsadd.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_W, xd, xj, xk)); +} + +/* Emits the `xvsadd.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_D, xd, xj, xk)); +} + +/* Emits the `xvssub.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_B, xd, xj, xk)); +} + +/* Emits the `xvssub.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_H, xd, xj, xk)); +} + +/* Emits the `xvssub.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_W, xd, xj, xk)); +} + +/* Emits the `xvssub.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_D, xd, xj, xk)); +} + +/* Emits the `xvsadd.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_BU, xd, xj, xk)); +} + +/* Emits the `xvsadd.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_HU, xd, xj, xk)); +} + +/* Emits the `xvsadd.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_WU, xd, xj, xk)); +} + +/* Emits the `xvsadd.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsadd_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSADD_DU, xd, xj, xk)); +} + +/* Emits the `xvssub.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_BU, xd, xj, xk)); +} + +/* Emits the `xvssub.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_HU, xd, xj, xk)); +} + +/* Emits the `xvssub.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_WU, xd, xj, xk)); +} + +/* Emits the `xvssub.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvssub_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSSUB_DU, xd, xj, xk)); +} + +/* Emits the `xvmax.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_B, xd, xj, xk)); +} + +/* Emits the `xvmax.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_H, xd, xj, xk)); +} + +/* Emits the `xvmax.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_W, xd, xj, xk)); +} + +/* Emits the `xvmax.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_D, xd, xj, xk)); +} + +/* Emits the `xvmin.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_B, xd, xj, xk)); +} + +/* Emits the `xvmin.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_H, xd, xj, xk)); +} + +/* Emits the `xvmin.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_W, xd, xj, xk)); +} + +/* Emits the `xvmin.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_D, xd, xj, xk)); +} + +/* Emits the `xvmax.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_BU, xd, xj, xk)); +} + +/* Emits the `xvmax.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_HU, xd, xj, xk)); +} + +/* Emits the `xvmax.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_WU, xd, xj, xk)); +} + +/* Emits the `xvmax.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmax_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMAX_DU, xd, xj, xk)); +} + +/* Emits the `xvmin.bu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_bu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_BU, xd, xj, xk)); +} + +/* Emits the `xvmin.hu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_hu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_HU, xd, xj, xk)); +} + +/* Emits the `xvmin.wu xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_wu(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_WU, xd, xj, xk)); +} + +/* Emits the `xvmin.du xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmin_du(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMIN_DU, xd, xj, xk)); +} + +/* Emits the `xvmul.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_B, xd, xj, xk)); +} + +/* Emits the `xvmul.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_H, xd, xj, xk)); +} + +/* Emits the `xvmul.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_W, xd, xj, xk)); +} + +/* Emits the `xvmul.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmul_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVMUL_D, xd, xj, xk)); +} + +/* Emits the `xvsll.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_B, xd, xj, xk)); +} + +/* Emits the `xvsll.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_H, xd, xj, xk)); +} + +/* Emits the `xvsll.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_W, xd, xj, xk)); +} + +/* Emits the `xvsll.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsll_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSLL_D, xd, xj, xk)); +} + +/* Emits the `xvsrl.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_B, xd, xj, xk)); +} + +/* Emits the `xvsrl.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_H, xd, xj, xk)); +} + +/* Emits the `xvsrl.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_W, xd, xj, xk)); +} + +/* Emits the `xvsrl.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrl_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRL_D, xd, xj, xk)); +} + +/* Emits the `xvsra.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_B, xd, xj, xk)); +} + +/* Emits the `xvsra.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_H, xd, xj, xk)); +} + +/* Emits the `xvsra.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_W, xd, xj, xk)); +} + +/* Emits the `xvsra.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsra_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVSRA_D, xd, xj, xk)); +} + +/* Emits the `xvrotr.b xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_B, xd, xj, xk)); +} + +/* Emits the `xvrotr.h xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_H, xd, xj, xk)); +} + +/* Emits the `xvrotr.w xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_W, xd, xj, xk)); +} + +/* Emits the `xvrotr.d xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotr_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVROTR_D, xd, xj, xk)); +} + +/* Emits the `xvreplve.b xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_b(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_B, xd, xj, k)); +} + +/* Emits the `xvreplve.h xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_h(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_H, xd, xj, k)); +} + +/* Emits the `xvreplve.w xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_w(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_W, xd, xj, k)); +} + +/* Emits the `xvreplve.d xd, xj, k` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve_d(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg k) +{ + tcg_out32(s, encode_xdxjk_insn(OPC_XVREPLVE_D, xd, xj, k)); +} + +/* Emits the `xvand.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvand_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVAND_V, xd, xj, xk)); +} + +/* Emits the `xvor.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVOR_V, xd, xj, xk)); +} + +/* Emits the `xvxor.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvxor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVXOR_V, xd, xj, xk)); +} + +/* Emits the `xvnor.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvnor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVNOR_V, xd, xj, xk)); +} + +/* Emits the `xvandn.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvandn_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVANDN_V, xd, xj, xk)); +} + +/* Emits the `xvorn.v xd, xj, xk` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvorn_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +{ + tcg_out32(s, encode_xdxjxk_insn(OPC_XVORN_V, xd, xj, xk)); +} + +/* Emits the `xvseqi.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_B, xd, xj, sk5)); +} + +/* Emits the `xvseqi.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_H, xd, xj, sk5)); +} + +/* Emits the `xvseqi.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_W, xd, xj, sk5)); +} + +/* Emits the `xvseqi.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvseqi_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSEQI_D, xd, xj, sk5)); +} + +/* Emits the `xvslei.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_B, xd, xj, sk5)); +} + +/* Emits the `xvslei.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_H, xd, xj, sk5)); +} + +/* Emits the `xvslei.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_W, xd, xj, sk5)); +} + +/* Emits the `xvslei.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLEI_D, xd, xj, sk5)); +} + +/* Emits the `xvslei.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_BU, xd, xj, uk5)); +} + +/* Emits the `xvslei.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_HU, xd, xj, uk5)); +} + +/* Emits the `xvslei.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_WU, xd, xj, uk5)); +} + +/* Emits the `xvslei.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslei_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLEI_DU, xd, xj, uk5)); +} + +/* Emits the `xvslti.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_B, xd, xj, sk5)); +} + +/* Emits the `xvslti.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_H, xd, xj, sk5)); +} + +/* Emits the `xvslti.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_W, xd, xj, sk5)); +} + +/* Emits the `xvslti.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVSLTI_D, xd, xj, sk5)); +} + +/* Emits the `xvslti.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_BU, xd, xj, uk5)); +} + +/* Emits the `xvslti.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_HU, xd, xj, uk5)); +} + +/* Emits the `xvslti.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_WU, xd, xj, uk5)); +} + +/* Emits the `xvslti.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslti_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLTI_DU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_BU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_HU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_WU, xd, xj, uk5)); +} + +/* Emits the `xvaddi.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvaddi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVADDI_DU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_BU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_HU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_WU, xd, xj, uk5)); +} + +/* Emits the `xvsubi.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsubi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSUBI_DU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_B, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_H, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_W, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMAXI_D, xd, xj, sk5)); +} + +/* Emits the `xvmini.b xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_b(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_B, xd, xj, sk5)); +} + +/* Emits the `xvmini.h xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_h(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_H, xd, xj, sk5)); +} + +/* Emits the `xvmini.w xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_w(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_W, xd, xj, sk5)); +} + +/* Emits the `xvmini.d xd, xj, sk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_d(TCGContext *s, TCGReg xd, TCGReg xj, int32_t sk5) +{ + tcg_out32(s, encode_xdxjsk5_insn(OPC_XVMINI_D, xd, xj, sk5)); +} + +/* Emits the `xvmaxi.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_BU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_HU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_WU, xd, xj, uk5)); +} + +/* Emits the `xvmaxi.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmaxi_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMAXI_DU, xd, xj, uk5)); +} + +/* Emits the `xvmini.bu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_bu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_BU, xd, xj, uk5)); +} + +/* Emits the `xvmini.hu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_hu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_HU, xd, xj, uk5)); +} + +/* Emits the `xvmini.wu xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_wu(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_WU, xd, xj, uk5)); +} + +/* Emits the `xvmini.du xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvmini_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVMINI_DU, xd, xj, uk5)); +} + +/* Emits the `xvneg.b xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_b(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_B, xd, xj)); +} + +/* Emits the `xvneg.h xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_h(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_H, xd, xj)); +} + +/* Emits the `xvneg.w xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_w(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_W, xd, xj)); +} + +/* Emits the `xvneg.d xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvneg_d(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_D, xd, xj)); +} + +/* Emits the `xvreplgr2vr.b xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_b(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_B, xd, j)); +} + +/* Emits the `xvreplgr2vr.h xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_h(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_H, xd, j)); +} + +/* Emits the `xvreplgr2vr.w xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_w(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_W, xd, j)); +} + +/* Emits the `xvreplgr2vr.d xd, j` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplgr2vr_d(TCGContext *s, TCGReg xd, TCGReg j) +{ + tcg_out32(s, encode_xdj_insn(OPC_XVREPLGR2VR_D, xd, j)); +} + +/* Emits the `xvrotri.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVROTRI_B, xd, xj, uk3)); +} + +/* Emits the `xvrotri.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVROTRI_H, xd, xj, uk4)); +} + +/* Emits the `xvrotri.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVROTRI_W, xd, xj, uk5)); +} + +/* Emits the `xvrotri.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrotri_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVROTRI_D, xd, xj, uk6)); +} + +/* Emits the `xvinsgr2vr.w xd, j, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvinsgr2vr_w(TCGContext *s, TCGReg xd, TCGReg j, uint32_t uk3) +{ + tcg_out32(s, encode_xdjuk3_insn(OPC_XVINSGR2VR_W, xd, j, uk3)); +} + +/* Emits the `xvinsgr2vr.d xd, j, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvinsgr2vr_d(TCGContext *s, TCGReg xd, TCGReg j, uint32_t uk2) +{ + tcg_out32(s, encode_xdjuk2_insn(OPC_XVINSGR2VR_D, xd, j, uk2)); +} + +/* Emits the `xvpickve2gr.w d, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_w(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_dxjuk3_insn(OPC_XVPICKVE2GR_W, d, xj, uk3)); +} + +/* Emits the `xvpickve2gr.d d, xj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_d(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk2) +{ + tcg_out32(s, encode_dxjuk2_insn(OPC_XVPICKVE2GR_D, d, xj, uk2)); +} + +/* Emits the `xvpickve2gr.wu d, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_wu(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_dxjuk3_insn(OPC_XVPICKVE2GR_WU, d, xj, uk3)); +} + +/* Emits the `xvpickve2gr.du d, xj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvpickve2gr_du(TCGContext *s, TCGReg d, TCGReg xj, uint32_t uk2) +{ + tcg_out32(s, encode_dxjuk2_insn(OPC_XVPICKVE2GR_DU, d, xj, uk2)); +} + +/* Emits the `xvrepl128vei.b xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVREPL128VEI_B, xd, xj, uk4)); +} + +/* Emits the `xvrepl128vei.h xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVREPL128VEI_H, xd, xj, uk3)); +} + +/* Emits the `xvrepl128vei.w xd, xj, uk2` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk2) +{ + tcg_out32(s, encode_xdxjuk2_insn(OPC_XVREPL128VEI_W, xd, xj, uk2)); +} + +/* Emits the `xvrepl128vei.d xd, xj, uk1` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvrepl128vei_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk1) +{ + tcg_out32(s, encode_xdxjuk1_insn(OPC_XVREPL128VEI_D, xd, xj, uk1)); +} + +/* Emits the `xvreplve0.b xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_b(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_B, xd, xj)); +} + +/* Emits the `xvreplve0.h xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_h(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_H, xd, xj)); +} + +/* Emits the `xvreplve0.w xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_w(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_W, xd, xj)); +} + +/* Emits the `xvreplve0.d xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_d(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_D, xd, xj)); +} + +/* Emits the `xvreplve0.q xd, xj` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvreplve0_q(TCGContext *s, TCGReg xd, TCGReg xj) +{ + tcg_out32(s, encode_xdxj_insn(OPC_XVREPLVE0_Q, xd, xj)); +} + +/* Emits the `xvbitclri.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITCLRI_B, xd, xj, uk3)); +} + +/* Emits the `xvbitclri.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITCLRI_H, xd, xj, uk4)); +} + +/* Emits the `xvbitclri.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITCLRI_W, xd, xj, uk5)); +} + +/* Emits the `xvbitclri.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitclri_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITCLRI_D, xd, xj, uk6)); +} + +/* Emits the `xvbitseti.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITSETI_B, xd, xj, uk3)); +} + +/* Emits the `xvbitseti.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITSETI_H, xd, xj, uk4)); +} + +/* Emits the `xvbitseti.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITSETI_W, xd, xj, uk5)); +} + +/* Emits the `xvbitseti.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseti_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITSETI_D, xd, xj, uk6)); +} + +/* Emits the `xvbitrevi.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVBITREVI_B, xd, xj, uk3)); +} + +/* Emits the `xvbitrevi.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVBITREVI_H, xd, xj, uk4)); +} + +/* Emits the `xvbitrevi.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVBITREVI_W, xd, xj, uk5)); +} + +/* Emits the `xvbitrevi.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitrevi_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVBITREVI_D, xd, xj, uk6)); +} + +/* Emits the `xvslli.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSLLI_B, xd, xj, uk3)); +} + +/* Emits the `xvslli.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSLLI_H, xd, xj, uk4)); +} + +/* Emits the `xvslli.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSLLI_W, xd, xj, uk5)); +} + +/* Emits the `xvslli.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvslli_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSLLI_D, xd, xj, uk6)); +} + +/* Emits the `xvsrli.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSRLI_B, xd, xj, uk3)); +} + +/* Emits the `xvsrli.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSRLI_H, xd, xj, uk4)); +} + +/* Emits the `xvsrli.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSRLI_W, xd, xj, uk5)); +} + +/* Emits the `xvsrli.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrli_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSRLI_D, xd, xj, uk6)); +} + +/* Emits the `xvsrai.b xd, xj, uk3` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk3) +{ + tcg_out32(s, encode_xdxjuk3_insn(OPC_XVSRAI_B, xd, xj, uk3)); +} + +/* Emits the `xvsrai.h xd, xj, uk4` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_h(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk4) +{ + tcg_out32(s, encode_xdxjuk4_insn(OPC_XVSRAI_H, xd, xj, uk4)); +} + +/* Emits the `xvsrai.w xd, xj, uk5` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_w(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) +{ + tcg_out32(s, encode_xdxjuk5_insn(OPC_XVSRAI_W, xd, xj, uk5)); +} + +/* Emits the `xvsrai.d xd, xj, uk6` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvsrai_d(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk6) +{ + tcg_out32(s, encode_xdxjuk6_insn(OPC_XVSRAI_D, xd, xj, uk6)); +} + +/* Emits the `xvbitseli.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvbitseli_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVBITSELI_B, xd, xj, uk8)); +} + +/* Emits the `xvandi.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvandi_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVANDI_B, xd, xj, uk8)); +} + +/* Emits the `xvori.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVORI_B, xd, xj, uk8)); +} + +/* Emits the `xvxori.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvxori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVXORI_B, xd, xj, uk8)); +} + +/* Emits the `xvnori.b xd, xj, uk8` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvnori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) +{ + tcg_out32(s, encode_xdxjuk8_insn(OPC_XVNORI_B, xd, xj, uk8)); +} + +/* Emits the `xvldi xd, sj13` instruction. */ +static void __attribute__((unused)) +tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, int32_t sj13) +{ + tcg_out32(s, encode_xdsj13_insn(OPC_XVLDI, xd, sj13)); +} + /* 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 d326e28740..973601aec3 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; } @@ -262,11 +303,30 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) switch (type) { case TCG_TYPE_I32: case TCG_TYPE_I64: - /* - * Conventional register-register move used in LoongArch is - * `or dst, src, zero`. - */ - tcg_out_opc_or(s, ret, arg, TCG_REG_ZERO); + if (ret < TCG_REG_V0) { + if (arg < TCG_REG_V0) { + /* + * Conventional register-register move used in LoongArch is + * `or dst, src, zero`. + */ + tcg_out_opc_or(s, ret, arg, TCG_REG_ZERO); + } else { + tcg_out_opc_movfr2gr_d(s, ret, arg); + } + } else { + if (arg < TCG_REG_V0) { + tcg_out_opc_movgr2fr_d(s, ret, arg); + } else { + tcg_out_opc_fmov_d(s, ret, arg); + } + } + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + tcg_out_opc_vori_b(s, ret, arg, 0); + break; + case TCG_TYPE_V256: + tcg_out_opc_xvori_b(s, ret, arg, 0); break; default: g_assert_not_reached(); @@ -274,16 +334,6 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) return true; } -static bool imm_part_needs_loading(bool high_bits_are_ones, - tcg_target_long part) -{ - if (high_bits_are_ones) { - return part != -1; - } else { - return part != 0; - } -} - /* Loads a 32-bit immediate into rd, sign-extended. */ static void tcg_out_movi_i32(TCGContext *s, TCGReg rd, int32_t val) { @@ -291,16 +341,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); @@ -332,10 +382,8 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, * back to the slow path. */ - 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; + intptr_t src_rx, pc_offset; + tcg_target_long hi12, hi32, hi52; /* Value fits in signed i32. */ if (type == TCG_TYPE_I32 || val == (int32_t)val) { @@ -344,51 +392,101 @@ static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, } /* PC-relative cases. */ - pc_offset = tcg_pcrel_diff(s, (void *)val); - if (pc_offset == sextreg(pc_offset, 0, 22) && (pc_offset & 3) == 0) { - /* Single pcaddu2i. */ - tcg_out_opc_pcaddu2i(s, rd, pc_offset >> 2); - return; + src_rx = (intptr_t)tcg_splitwx_to_rx(s->code_ptr); + if ((val & 3) == 0) { + pc_offset = val - src_rx; + if (pc_offset == sextreg(pc_offset, 0, 22)) { + /* Single pcaddu2i. */ + tcg_out_opc_pcaddu2i(s, rd, pc_offset >> 2); + return; + } } - if (pc_offset == (int32_t)pc_offset) { - /* Offset within 32 bits; load with pcalau12i + ori. */ - val_lo = sextreg(val, 0, 12); - val_hi = val >> 12; - pc_hi = (val - pc_offset) >> 12; - offset_hi = val_hi - pc_hi; - - tcg_debug_assert(offset_hi == sextreg(offset_hi, 0, 20)); - tcg_out_opc_pcalau12i(s, rd, offset_hi); + pc_offset = (val >> 12) - (src_rx >> 12); + if (pc_offset == sextreg(pc_offset, 0, 20)) { + /* Load with pcalau12i + ori. */ + tcg_target_long val_lo = val & 0xfff; + tcg_out_opc_pcalau12i(s, rd, pc_offset); if (val_lo != 0) { - tcg_out_opc_ori(s, rd, rd, val_lo & 0xfff); + tcg_out_opc_ori(s, rd, rd, val_lo); } 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 +502,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 +517,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 +556,154 @@ 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); - break; - case TCG_COND_GTU: - tcg_out_setcond(s, TCG_COND_LTU, 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; default: - g_assert_not_reached(); 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(); + } + + 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 +772,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 +788,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); @@ -610,23 +816,95 @@ static void tcg_out_ldst(TCGContext *s, LoongArchInsn opc, TCGReg data, case OPC_ST_D: tcg_out32(s, encode_djsk12_insn(opc, data, addr, imm12)); break; + case OPC_FLD_S: + case OPC_FLD_D: + case OPC_FST_S: + case OPC_FST_D: + tcg_out32(s, encode_fdjsk12_insn(opc, data, addr, imm12)); + break; default: g_assert_not_reached(); } } -static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, intptr_t offset) { - bool is_32bit = type == TCG_TYPE_I32; - tcg_out_ldst(s, is_32bit ? OPC_LD_W : OPC_LD_D, arg, arg1, arg2); + switch (type) { + case TCG_TYPE_I32: + if (dest < TCG_REG_V0) { + tcg_out_ldst(s, OPC_LD_W, dest, base, offset); + } else { + tcg_out_ldst(s, OPC_FLD_S, dest, base, offset); + } + break; + case TCG_TYPE_I64: + case TCG_TYPE_V64: + if (dest < TCG_REG_V0) { + tcg_out_ldst(s, OPC_LD_D, dest, base, offset); + } else { + tcg_out_ldst(s, OPC_FLD_D, dest, base, offset); + } + break; + case TCG_TYPE_V128: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_vld(s, dest, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_vldx(s, dest, base, TCG_REG_TMP0); + } + break; + case TCG_TYPE_V256: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_xvld(s, dest, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_xvldx(s, dest, base, TCG_REG_TMP0); + } + break; + default: + g_assert_not_reached(); + } } -static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, - TCGReg arg1, intptr_t arg2) +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg src, + TCGReg base, intptr_t offset) { - bool is_32bit = type == TCG_TYPE_I32; - tcg_out_ldst(s, is_32bit ? OPC_ST_W : OPC_ST_D, arg, arg1, arg2); + switch (type) { + case TCG_TYPE_I32: + if (src < TCG_REG_V0) { + tcg_out_ldst(s, OPC_ST_W, src, base, offset); + } else { + tcg_out_ldst(s, OPC_FST_S, src, base, offset); + } + break; + case TCG_TYPE_I64: + case TCG_TYPE_V64: + if (src < TCG_REG_V0) { + tcg_out_ldst(s, OPC_ST_D, src, base, offset); + } else { + tcg_out_ldst(s, OPC_FST_D, src, base, offset); + } + break; + case TCG_TYPE_V128: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_vst(s, src, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_vstx(s, src, base, TCG_REG_TMP0); + } + break; + case TCG_TYPE_V256: + if (-0x800 <= offset && offset <= 0x7ff) { + tcg_out_opc_xvst(s, src, base, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_xvstx(s, src, base, TCG_REG_TMP0); + } + break; + default: + g_assert_not_reached(); + } } static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, @@ -643,422 +921,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); } - 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 } -/* LoongArch uses `andi zero, zero, 0` as NOP. */ -#define NOP OPC_ANDI -static void tcg_out_nop(TCGContext *s) +static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg data_lo, TCGReg data_hi, + TCGReg addr_reg, MemOpIdx oi, bool is_ld) { - tcg_out32(s, NOP); -} + TCGLabelQemuLdst *ldst; + HostAddress h; -void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) -{ - tcg_insn_unit i1, i2; - ptrdiff_t upper, lower; - ptrdiff_t offset = (ptrdiff_t)(addr - jmp_rx) >> 2; + ldst = prepare_host_addr(s, &h, addr_reg, oi, is_ld); - if (offset == sextreg(offset, 0, 26)) { - i1 = encode_sd10k16_insn(OPC_B, offset); - i2 = NOP; + 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 { - tcg_debug_assert(offset == sextreg(offset, 0, 36)); - lower = (int16_t)offset; - upper = (offset - lower) >> 16; - - i1 = encode_dsj20_insn(OPC_PCADDU18I, TCG_REG_TMP0, upper); - i2 = encode_djsk16_insn(OPC_JIRL, TCG_REG_ZERO, TCG_REG_TMP0, lower); + /* 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); } - uint64_t pair = ((uint64_t)i2 << 32) | i1; - qatomic_set((uint64_t *)jmp_rw, pair); - flush_idcache_range(jmp_rx, jmp_rw, 8); } /* @@ -1067,6 +1219,57 @@ void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, 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]) @@ -1074,38 +1277,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: - tcg_debug_assert(s->tb_jmp_insn_offset != NULL); - /* - * Ensure that patch area is 8-byte aligned so that an - * atomic write can be used to patch the target address. - */ - if ((uintptr_t)s->code_ptr & 7) { - tcg_out_nop(s); - } - 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_opc_pcaddu18i(s, TCG_REG_TMP0, 0); - 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; @@ -1125,37 +1300,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; @@ -1240,7 +1384,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); } @@ -1358,14 +1502,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); } @@ -1373,19 +1517,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; @@ -1440,6 +1591,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); @@ -1483,27 +1639,550 @@ 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) +{ + static const LoongArchInsn repl_insn[2][4] = { + { OPC_VREPLGR2VR_B, OPC_VREPLGR2VR_H, + OPC_VREPLGR2VR_W, OPC_VREPLGR2VR_D }, + { OPC_XVREPLGR2VR_B, OPC_XVREPLGR2VR_H, + OPC_XVREPLGR2VR_W, OPC_XVREPLGR2VR_D }, + }; + bool lasx = type == TCG_TYPE_V256; + + tcg_debug_assert(vece <= MO_64); + tcg_out32(s, encode_vdj_insn(repl_insn[lasx][vece], rd, rs)); + return true; +} + +static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg r, TCGReg base, intptr_t offset) +{ + bool lasx = type == TCG_TYPE_V256; + + /* 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: + if (lasx) { + tcg_out_opc_xvldrepl_b(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_b(s, r, base, offset); + } + break; + case MO_16: + if (lasx) { + tcg_out_opc_xvldrepl_h(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_h(s, r, base, offset); + } + break; + case MO_32: + if (lasx) { + tcg_out_opc_xvldrepl_w(s, r, base, offset); + } else { + tcg_out_opc_vldrepl_w(s, r, base, offset); + } + break; + case MO_64: + if (lasx) { + tcg_out_opc_xvldrepl_d(s, r, base, offset); + } else { + 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); + + if (type == TCG_TYPE_V256) { + tcg_out_opc_xvldi(s, rd, imm); + } else { + tcg_out_opc_vldi(s, rd, imm); + } + return; + } + + /* TODO: vldi patterns when imm 12 is set */ + + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, value); + tcg_out_dup_vec(s, type, vece, rd, TCG_REG_TMP0); +} + +static void tcg_out_addsub_vec(TCGContext *s, bool lasx, unsigned vece, + TCGArg a0, TCGArg a1, TCGArg a2, + bool a2_is_const, bool is_add) +{ + static const LoongArchInsn add_vec_insn[2][4] = { + { OPC_VADD_B, OPC_VADD_H, OPC_VADD_W, OPC_VADD_D }, + { OPC_XVADD_B, OPC_XVADD_H, OPC_XVADD_W, OPC_XVADD_D }, + }; + static const LoongArchInsn add_vec_imm_insn[2][4] = { + { OPC_VADDI_BU, OPC_VADDI_HU, OPC_VADDI_WU, OPC_VADDI_DU }, + { OPC_XVADDI_BU, OPC_XVADDI_HU, OPC_XVADDI_WU, OPC_XVADDI_DU }, + }; + static const LoongArchInsn sub_vec_insn[2][4] = { + { OPC_VSUB_B, OPC_VSUB_H, OPC_VSUB_W, OPC_VSUB_D }, + { OPC_XVSUB_B, OPC_XVSUB_H, OPC_XVSUB_W, OPC_XVSUB_D }, + }; + static const LoongArchInsn sub_vec_imm_insn[2][4] = { + { OPC_VSUBI_BU, OPC_VSUBI_HU, OPC_VSUBI_WU, OPC_VSUBI_DU }, + { OPC_XVSUBI_BU, OPC_XVSUBI_HU, OPC_XVSUBI_WU, OPC_XVSUBI_DU }, + }; + LoongArchInsn insn; + + if (a2_is_const) { + int64_t value = sextract64(a2, 0, 8 << vece); + + if (!is_add) { + value = -value; + } + if (value < 0) { + insn = sub_vec_imm_insn[lasx][vece]; + value = -value; + } else { + insn = add_vec_imm_insn[lasx][vece]; + } + + /* Constraint TCG_CT_CONST_VADD ensures validity. */ + tcg_debug_assert(0 <= value && value <= 0x1f); + + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); + return; + } + + if (is_add) { + insn = add_vec_insn[lasx][vece]; + } else { + insn = sub_vec_insn[lasx][vece]; + } + tcg_out32(s, encode_vdvjvk_insn(insn, 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; + bool lasx = type == TCG_TYPE_V256; + TCGArg a0, a1, a2, a3; + LoongArchInsn insn; + + static const LoongArchInsn cmp_vec_insn[16][2][4] = { + [TCG_COND_EQ] = { + { OPC_VSEQ_B, OPC_VSEQ_H, OPC_VSEQ_W, OPC_VSEQ_D }, + { OPC_XVSEQ_B, OPC_XVSEQ_H, OPC_XVSEQ_W, OPC_XVSEQ_D }, + }, + [TCG_COND_LE] = { + { OPC_VSLE_B, OPC_VSLE_H, OPC_VSLE_W, OPC_VSLE_D }, + { OPC_XVSLE_B, OPC_XVSLE_H, OPC_XVSLE_W, OPC_XVSLE_D }, + }, + [TCG_COND_LEU] = { + { OPC_VSLE_BU, OPC_VSLE_HU, OPC_VSLE_WU, OPC_VSLE_DU }, + { OPC_XVSLE_BU, OPC_XVSLE_HU, OPC_XVSLE_WU, OPC_XVSLE_DU }, + }, + [TCG_COND_LT] = { + { OPC_VSLT_B, OPC_VSLT_H, OPC_VSLT_W, OPC_VSLT_D }, + { OPC_XVSLT_B, OPC_XVSLT_H, OPC_XVSLT_W, OPC_XVSLT_D }, + }, + [TCG_COND_LTU] = { + { OPC_VSLT_BU, OPC_VSLT_HU, OPC_VSLT_WU, OPC_VSLT_DU }, + { OPC_XVSLT_BU, OPC_XVSLT_HU, OPC_XVSLT_WU, OPC_XVSLT_DU }, + } + }; + static const LoongArchInsn cmp_vec_imm_insn[16][2][4] = { + [TCG_COND_EQ] = { + { OPC_VSEQI_B, OPC_VSEQI_H, OPC_VSEQI_W, OPC_VSEQI_D }, + { OPC_XVSEQI_B, OPC_XVSEQI_H, OPC_XVSEQI_W, OPC_XVSEQI_D }, + }, + [TCG_COND_LE] = { + { OPC_VSLEI_B, OPC_VSLEI_H, OPC_VSLEI_W, OPC_VSLEI_D }, + { OPC_XVSLEI_B, OPC_XVSLEI_H, OPC_XVSLEI_W, OPC_XVSLEI_D }, + }, + [TCG_COND_LEU] = { + { OPC_VSLEI_BU, OPC_VSLEI_HU, OPC_VSLEI_WU, OPC_VSLEI_DU }, + { OPC_XVSLEI_BU, OPC_XVSLEI_HU, OPC_XVSLEI_WU, OPC_XVSLEI_DU }, + }, + [TCG_COND_LT] = { + { OPC_VSLTI_B, OPC_VSLTI_H, OPC_VSLTI_W, OPC_VSLTI_D }, + { OPC_XVSLTI_B, OPC_XVSLTI_H, OPC_XVSLTI_W, OPC_XVSLTI_D }, + }, + [TCG_COND_LTU] = { + { OPC_VSLTI_BU, OPC_VSLTI_HU, OPC_VSLTI_WU, OPC_VSLTI_DU }, + { OPC_XVSLTI_BU, OPC_XVSLTI_HU, OPC_XVSLTI_WU, OPC_XVSLTI_DU }, + } + }; + static const LoongArchInsn neg_vec_insn[2][4] = { + { OPC_VNEG_B, OPC_VNEG_H, OPC_VNEG_W, OPC_VNEG_D }, + { OPC_XVNEG_B, OPC_XVNEG_H, OPC_XVNEG_W, OPC_XVNEG_D }, + }; + static const LoongArchInsn mul_vec_insn[2][4] = { + { OPC_VMUL_B, OPC_VMUL_H, OPC_VMUL_W, OPC_VMUL_D }, + { OPC_XVMUL_B, OPC_XVMUL_H, OPC_XVMUL_W, OPC_XVMUL_D }, + }; + static const LoongArchInsn smin_vec_insn[2][4] = { + { OPC_VMIN_B, OPC_VMIN_H, OPC_VMIN_W, OPC_VMIN_D }, + { OPC_XVMIN_B, OPC_XVMIN_H, OPC_XVMIN_W, OPC_XVMIN_D }, + }; + static const LoongArchInsn umin_vec_insn[2][4] = { + { OPC_VMIN_BU, OPC_VMIN_HU, OPC_VMIN_WU, OPC_VMIN_DU }, + { OPC_XVMIN_BU, OPC_XVMIN_HU, OPC_XVMIN_WU, OPC_XVMIN_DU }, + }; + static const LoongArchInsn smax_vec_insn[2][4] = { + { OPC_VMAX_B, OPC_VMAX_H, OPC_VMAX_W, OPC_VMAX_D }, + { OPC_XVMAX_B, OPC_XVMAX_H, OPC_XVMAX_W, OPC_XVMAX_D }, + }; + static const LoongArchInsn umax_vec_insn[2][4] = { + { OPC_VMAX_BU, OPC_VMAX_HU, OPC_VMAX_WU, OPC_VMAX_DU }, + { OPC_XVMAX_BU, OPC_XVMAX_HU, OPC_XVMAX_WU, OPC_XVMAX_DU }, + }; + static const LoongArchInsn ssadd_vec_insn[2][4] = { + { OPC_VSADD_B, OPC_VSADD_H, OPC_VSADD_W, OPC_VSADD_D }, + { OPC_XVSADD_B, OPC_XVSADD_H, OPC_XVSADD_W, OPC_XVSADD_D }, + }; + static const LoongArchInsn usadd_vec_insn[2][4] = { + { OPC_VSADD_BU, OPC_VSADD_HU, OPC_VSADD_WU, OPC_VSADD_DU }, + { OPC_XVSADD_BU, OPC_XVSADD_HU, OPC_XVSADD_WU, OPC_XVSADD_DU }, + }; + static const LoongArchInsn sssub_vec_insn[2][4] = { + { OPC_VSSUB_B, OPC_VSSUB_H, OPC_VSSUB_W, OPC_VSSUB_D }, + { OPC_XVSSUB_B, OPC_XVSSUB_H, OPC_XVSSUB_W, OPC_XVSSUB_D }, + }; + static const LoongArchInsn ussub_vec_insn[2][4] = { + { OPC_VSSUB_BU, OPC_VSSUB_HU, OPC_VSSUB_WU, OPC_VSSUB_DU }, + { OPC_XVSSUB_BU, OPC_XVSSUB_HU, OPC_XVSSUB_WU, OPC_XVSSUB_DU }, + }; + static const LoongArchInsn shlv_vec_insn[2][4] = { + { OPC_VSLL_B, OPC_VSLL_H, OPC_VSLL_W, OPC_VSLL_D }, + { OPC_XVSLL_B, OPC_XVSLL_H, OPC_XVSLL_W, OPC_XVSLL_D }, + }; + static const LoongArchInsn shrv_vec_insn[2][4] = { + { OPC_VSRL_B, OPC_VSRL_H, OPC_VSRL_W, OPC_VSRL_D }, + { OPC_XVSRL_B, OPC_XVSRL_H, OPC_XVSRL_W, OPC_XVSRL_D }, + }; + static const LoongArchInsn sarv_vec_insn[2][4] = { + { OPC_VSRA_B, OPC_VSRA_H, OPC_VSRA_W, OPC_VSRA_D }, + { OPC_XVSRA_B, OPC_XVSRA_H, OPC_XVSRA_W, OPC_XVSRA_D }, + }; + static const LoongArchInsn shli_vec_insn[2][4] = { + { OPC_VSLLI_B, OPC_VSLLI_H, OPC_VSLLI_W, OPC_VSLLI_D }, + { OPC_XVSLLI_B, OPC_XVSLLI_H, OPC_XVSLLI_W, OPC_XVSLLI_D }, + }; + static const LoongArchInsn shri_vec_insn[2][4] = { + { OPC_VSRLI_B, OPC_VSRLI_H, OPC_VSRLI_W, OPC_VSRLI_D }, + { OPC_XVSRLI_B, OPC_XVSRLI_H, OPC_XVSRLI_W, OPC_XVSRLI_D }, + }; + static const LoongArchInsn sari_vec_insn[2][4] = { + { OPC_VSRAI_B, OPC_VSRAI_H, OPC_VSRAI_W, OPC_VSRAI_D }, + { OPC_XVSRAI_B, OPC_XVSRAI_H, OPC_XVSRAI_W, OPC_XVSRAI_D }, + }; + static const LoongArchInsn rotrv_vec_insn[2][4] = { + { OPC_VROTR_B, OPC_VROTR_H, OPC_VROTR_W, OPC_VROTR_D }, + { OPC_XVROTR_B, OPC_XVROTR_H, OPC_XVROTR_W, OPC_XVROTR_D }, + }; + static const LoongArchInsn rotri_vec_insn[2][4] = { + { OPC_VROTRI_B, OPC_VROTRI_H, OPC_VROTRI_W, OPC_VROTRI_D }, + { OPC_XVROTRI_B, OPC_XVROTRI_H, OPC_XVROTRI_W, OPC_XVROTRI_D }, + }; + + a0 = args[0]; + a1 = args[1]; + a2 = args[2]; + a3 = args[3]; + + switch (opc) { + case INDEX_op_st_vec: + tcg_out_st(s, type, a0, a1, a2); + break; + case INDEX_op_ld_vec: + tcg_out_ld(s, type, a0, a1, a2); + break; + case INDEX_op_and_vec: + insn = lasx ? OPC_XVAND_V : OPC_VAND_V; + goto vdvjvk; + case INDEX_op_andc_vec: + /* + * vandn vd, vj, vk: vd = vk & ~vj + * andc_vec vd, vj, vk: vd = vj & ~vk + * vj and vk are swapped + */ + a1 = a2; + a2 = args[1]; + insn = lasx ? OPC_XVANDN_V : OPC_VANDN_V; + goto vdvjvk; + case INDEX_op_or_vec: + insn = lasx ? OPC_XVOR_V : OPC_VOR_V; + goto vdvjvk; + case INDEX_op_orc_vec: + insn = lasx ? OPC_XVORN_V : OPC_VORN_V; + goto vdvjvk; + case INDEX_op_xor_vec: + insn = lasx ? OPC_XVXOR_V : OPC_VXOR_V; + goto vdvjvk; + case INDEX_op_not_vec: + a2 = a1; + /* fall through */ + case INDEX_op_nor_vec: + insn = lasx ? OPC_XVNOR_V : OPC_VNOR_V; + goto vdvjvk; + 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)) { + insn = cmp_vec_imm_insn[cond][lasx][vece]; + tcg_out32(s, encode_vdvjsk5_insn(insn, a0, a1, value)); + break; + } else if ((cond == TCG_COND_LEU || + cond == TCG_COND_LTU) && + (0x00 <= value && value <= 0x1f)) { + insn = cmp_vec_imm_insn[cond][lasx][vece]; + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); + break; + } + + /* + * Fallback to: + * dupi_vec temp, a2 + * cmp_vec a0, a1, temp, cond + */ + tcg_out_dupi_vec(s, type, vece, TCG_VEC_TMP0, a2); + a2 = TCG_VEC_TMP0; + } + + insn = cmp_vec_insn[cond][lasx][vece]; + if (insn == 0) { + TCGArg t; + t = a1, a1 = a2, a2 = t; + cond = tcg_swap_cond(cond); + insn = cmp_vec_insn[cond][lasx][vece]; + tcg_debug_assert(insn != 0); + } + } + goto vdvjvk; + case INDEX_op_add_vec: + tcg_out_addsub_vec(s, lasx, vece, a0, a1, a2, const_args[2], true); + break; + case INDEX_op_sub_vec: + tcg_out_addsub_vec(s, lasx, vece, a0, a1, a2, const_args[2], false); + break; + case INDEX_op_neg_vec: + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[lasx][vece], a0, a1)); + break; + case INDEX_op_mul_vec: + insn = mul_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_smin_vec: + insn = smin_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_smax_vec: + insn = smax_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_umin_vec: + insn = umin_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_umax_vec: + insn = umax_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_ssadd_vec: + insn = ssadd_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_usadd_vec: + insn = usadd_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_sssub_vec: + insn = sssub_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_ussub_vec: + insn = ussub_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_shlv_vec: + insn = shlv_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_shrv_vec: + insn = shrv_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_sarv_vec: + insn = sarv_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_rotlv_vec: + /* rotlv_vec a1, a2 = rotrv_vec a1, -a2 */ + tcg_out32(s, encode_vdvj_insn(neg_vec_insn[lasx][vece], + TCG_VEC_TMP0, a2)); + a2 = TCG_VEC_TMP0; + /* fall through */ + case INDEX_op_rotrv_vec: + insn = rotrv_vec_insn[lasx][vece]; + goto vdvjvk; + case INDEX_op_shli_vec: + insn = shli_vec_insn[lasx][vece]; + goto vdvjukN; + case INDEX_op_shri_vec: + insn = shri_vec_insn[lasx][vece]; + goto vdvjukN; + case INDEX_op_sari_vec: + insn = sari_vec_insn[lasx][vece]; + goto vdvjukN; + case INDEX_op_rotli_vec: + /* rotli_vec a1, a2 = rotri_vec a1, -a2 */ + a2 = extract32(-a2, 0, 3 + vece); + insn = rotri_vec_insn[lasx][vece]; + goto vdvjukN; + case INDEX_op_bitsel_vec: + /* vbitsel vd, vj, vk, va = bitsel_vec vd, va, vk, vj */ + if (lasx) { + tcg_out_opc_xvbitsel_v(s, a0, a3, a2, a1); + } else { + 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(); + vdvjvk: + tcg_out32(s, encode_vdvjvk_insn(insn, a0, a1, a2)); + break; + vdvjukN: + switch (vece) { + case MO_8: + tcg_out32(s, encode_vdvjuk3_insn(insn, a0, a1, a2)); + break; + case MO_16: + tcg_out32(s, encode_vdvjuk4_insn(insn, a0, a1, a2)); + break; + case MO_32: + tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, a2)); + break; + case MO_64: + tcg_out32(s, encode_vdvjuk6_insn(insn, a0, a1, a2)); + break; + default: + g_assert_not_reached(); + } + break; + } +} + +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) { @@ -1517,16 +2196,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: @@ -1541,6 +2228,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: @@ -1562,12 +2251,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: @@ -1592,8 +2281,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: @@ -1612,18 +2302,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: @@ -1641,6 +2330,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(); } @@ -1686,12 +2427,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]); @@ -1712,12 +2451,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); @@ -1729,6 +2481,22 @@ 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_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + if (cpuinfo & CPUINFO_LASX) { + tcg_target_available_regs[TCG_TYPE_V256] = 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); @@ -1737,6 +2505,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 a659c8d6fd..58bd7d258e 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -29,24 +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 -/* - * PCADDU18I + JIRL sequence can give 20 + 16 + 2 = 38 bits - * signed offset, which is +/- 128 GiB. - */ -#define MAX_CODE_GEN_BUFFER_SIZE (128 * GiB) +#define TCG_TARGET_NB_REGS 64 + +#define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) typedef enum { TCG_REG_ZERO, @@ -82,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 @@ -118,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 @@ -127,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 1 #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 @@ -142,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 @@ -154,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 @@ -170,12 +167,37 @@ typedef enum { #define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 -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 (cpuinfo & CPUINFO_LSX) +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_LSX) +#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_LASX) + +#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_HAS_tst_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..79e72223a8 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,42 @@ tcg_ss.add(files( 'tcg.c', 'tcg-common.c', 'tcg-op.c', + 'tcg-op-ldst.c', 'tcg-op-gvec.c', 'tcg-op-vec.c', + 'tcg-op-fp.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, + dependencies: tcg_ss.dependencies(), + c_args: '-DCONFIG_USER_ONLY', + build_by_default: false) + +tcg_user = declare_dependency(objects: libtcg_user.extract_all_objects(recursive: false), + dependencies: tcg_ss.dependencies()) +user_ss.add(tcg_user) + +libtcg_system = static_library('tcg_system', + tcg_ss.sources() + genh, + dependencies: tcg_ss.dependencies(), + c_args: '-DCONFIG_SOFTMMU', + build_by_default: false) + +tcg_system = declare_dependency(objects: libtcg_system.extract_all_objects(recursive: false), + 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..e9ef16b3c6 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,23 @@ 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); + /* If true and false values are the same, eliminate the cmp. */ + if (args_are_copies(op->args[3], op->args[4])) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[3]); } + /* * 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 +1878,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 +1906,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 +1974,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 +1995,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 +2009,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 +2104,245 @@ static bool fold_remainder(OptContext *ctx, TCGOp *op) return false; } -static bool fold_setcond(OptContext *ctx, TCGOp *op) +static bool fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) { - TCGCond cond = op->args[3]; - int i; + uint64_t a_zmask, b_val; + TCGCond cond; - if (swap_commutative(op->args[0], &op->args[1], &op->args[2])) { - op->args[3] = cond = tcg_swap_cond(cond); + if (!arg_is_const(op->args[2])) { + return false; } - i = do_constant_folding_cond(ctx->type, op->args[1], op->args[2], cond); + a_zmask = arg_info(op->args[1])->z_mask; + b_val = arg_info(op->args[2])->val; + cond = op->args[3]; + + if (ctx->type == TCG_TYPE_I32) { + a_zmask = (uint32_t)a_zmask; + b_val = (uint32_t)b_val; + } + + /* + * A with only low bits set vs B with high bits set means that A < B. + */ + if (a_zmask < b_val) { + bool inv = false; + + switch (cond) { + case TCG_COND_NE: + case TCG_COND_LEU: + case TCG_COND_LTU: + inv = true; + /* fall through */ + case TCG_COND_GTU: + case TCG_COND_GEU: + case TCG_COND_EQ: + return tcg_opt_gen_movi(ctx, op, op->args[0], neg ? -inv : inv); + default: + break; + } + } + + /* + * A with only lsb set is already boolean. + */ + if (a_zmask <= 1) { + bool convert = false; + bool inv = false; + + switch (cond) { + case TCG_COND_EQ: + inv = true; + /* fall through */ + case TCG_COND_NE: + convert = (b_val == 0); + break; + case TCG_COND_LTU: + case TCG_COND_TSTEQ: + inv = true; + /* fall through */ + case TCG_COND_GEU: + case TCG_COND_TSTNE: + convert = (b_val == 1); + break; + default: + break; + } + if (convert) { + TCGOpcode add_opc, xor_opc, neg_opc; + + if (!inv && !neg) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + + switch (ctx->type) { + case TCG_TYPE_I32: + add_opc = INDEX_op_add_i32; + neg_opc = INDEX_op_neg_i32; + xor_opc = INDEX_op_xor_i32; + break; + case TCG_TYPE_I64: + add_opc = INDEX_op_add_i64; + neg_opc = INDEX_op_neg_i64; + xor_opc = INDEX_op_xor_i64; + break; + default: + g_assert_not_reached(); + } + + if (!inv) { + op->opc = neg_opc; + } else if (neg) { + op->opc = add_opc; + op->args[2] = arg_new_constant(ctx, -1); + } else { + op->opc = xor_opc; + op->args[2] = arg_new_constant(ctx, 1); + } + return false; + } + } + + return false; +} + +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]; + TCGArg ret, src1, src2; + TCGOp *op2; + uint64_t val; + int sh; + bool inv; + + if (!is_tst_cond(cond) || !arg_is_const(op->args[2])) { + return; + } + + 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); } + if (fold_setcond_zmask(ctx, op, false)) { + return true; + } + fold_setcond_tst_pow2(ctx, op, false); + ctx->z_mask = 1; ctx->s_mask = smask_from_zmask(1); return false; } -static bool fold_setcond2(OptContext *ctx, TCGOp *op) +static bool fold_negsetcond(OptContext *ctx, TCGOp *op) { - TCGCond cond = op->args[5]; - int i, inv = 0; - - if (swap_commutative2(&op->args[1], &op->args[3])) { - op->args[5] = cond = tcg_swap_cond(cond); + 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); } - i = do_constant_folding_cond2(&op->args[1], &op->args[3], cond); + if (fold_setcond_zmask(ctx, op, true)) { + return true; + } + 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; + int i, inv = 0; + + i = do_constant_folding_cond2(ctx, op, &op->args[1]); + cond = op->args[5]; if (i >= 0) { goto do_setcond_const; } @@ -1784,8 +2354,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 +2383,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[3], 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; @@ -1839,6 +2422,36 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) return tcg_opt_gen_movi(ctx, op, op->args[0], i); } +static bool fold_cmp_vec(OptContext *ctx, TCGOp *op) +{ + /* Canonicalize the comparison to put immediate second. */ + if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { + op->args[3] = tcg_swap_cond(op->args[3]); + } + return false; +} + +static bool fold_cmpsel_vec(OptContext *ctx, TCGOp *op) +{ + /* If true and false values are the same, eliminate the cmp. */ + if (args_are_copies(op->args[3], op->args[4])) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[3]); + } + + /* Canonicalize the comparison to put immediate second. */ + if (swap_commutative(NO_DEST, &op->args[1], &op->args[2])) { + op->args[5] = tcg_swap_cond(op->args[5]); + } + /* + * Canonicalize the "false" input reg to match the destination, + * so that the tcg backend can implement "move if true". + */ + if (swap_commutative(op->args[0], &op->args[4], &op->args[3])) { + op->args[5] = tcg_invert_cond(op->args[5]); + } + return false; +} + static bool fold_sextract(OptContext *ctx, TCGOp *op) { uint64_t z_mask, s_mask, s_mask_old; @@ -1907,7 +2520,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 +2543,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 +2562,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 +2580,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 +2631,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) || @@ -2022,6 +2737,61 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) return fold_masks(ctx, op); } +static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) +{ + /* If true and false values are the same, eliminate the cmp. */ + if (args_are_copies(op->args[2], op->args[3])) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[2]); + } + + if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { + uint64_t tv = arg_info(op->args[2])->val; + uint64_t fv = arg_info(op->args[3])->val; + + if (tv == -1 && fv == 0) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + if (tv == 0 && fv == -1) { + if (TCG_TARGET_HAS_not_vec) { + op->opc = INDEX_op_not_vec; + return fold_not(ctx, op); + } else { + op->opc = INDEX_op_xor_vec; + op->args[2] = arg_new_constant(ctx, -1); + return fold_xor(ctx, op); + } + } + } + if (arg_is_const(op->args[2])) { + uint64_t tv = arg_info(op->args[2])->val; + if (tv == -1) { + op->opc = INDEX_op_or_vec; + op->args[2] = op->args[3]; + return fold_or(ctx, op); + } + if (tv == 0 && TCG_TARGET_HAS_andc_vec) { + op->opc = INDEX_op_andc_vec; + op->args[2] = op->args[1]; + op->args[1] = op->args[3]; + return fold_andc(ctx, op); + } + } + if (arg_is_const(op->args[3])) { + uint64_t fv = arg_info(op->args[3])->val; + if (fv == 0) { + op->opc = INDEX_op_and_vec; + return fold_and(ctx, op); + } + if (fv == -1 && TCG_TARGET_HAS_orc_vec) { + op->opc = INDEX_op_orc_vec; + op->args[2] = op->args[1]; + op->args[1] = op->args[3]; + return fold_orc(ctx, op); + } + } + return false; +} + /* Propagate constants and copies, fold constant expressions. */ void tcg_optimize(TCGContext *s) { @@ -2029,6 +2799,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 +2922,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 +2975,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,9 +3007,21 @@ 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; + case INDEX_op_cmp_vec: + done = fold_cmp_vec(&ctx, op); + break; + case INDEX_op_cmpsel_vec: + done = fold_cmpsel_vec(&ctx, op); + break; + case INDEX_op_bitsel_vec: + done = fold_bitsel_vec(&ctx, op); + break; CASE_OP_32_64(sextract): done = fold_sextract(&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..453abde6c1 100644 --- a/tcg/ppc/tcg-target-con-set.h +++ b/tcg/ppc/tcg-target-con-set.h @@ -11,32 +11,33 @@ */ 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(v, v, v, vZM, v) +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 e3dba47697..9a11c26fd3 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,93 @@ 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 (uval == (uint32_t)uval && mask_operand(uval, &mb, &me)) { + return 1; + } + if (TCG_TARGET_REG_BITS == 64 && + 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 +373,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 +437,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) @@ -440,6 +529,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) @@ -626,31 +720,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, @@ -668,6 +766,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, @@ -700,6 +800,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); @@ -747,46 +893,83 @@ 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_debug_assert((mb & 0x1f) == mb); + tcg_debug_assert((me & 0x1f) == me); + tcg_out32(s, op | RA(ra) | RS(rs) | SH(sh & 0x1f) | 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); @@ -818,6 +1001,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; @@ -825,7 +1021,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); } @@ -844,7 +1040,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); } @@ -956,12 +1152,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) { @@ -1000,6 +1215,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); @@ -1009,10 +1237,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); @@ -1069,7 +1308,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; @@ -1096,6 +1348,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)); @@ -1118,6 +1372,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; @@ -1275,6 +1541,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) { @@ -1428,6 +1737,47 @@ 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; + } + + 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 (arg2 == (uint32_t)arg2 && mask_operand(arg2, &mb, &me)) { + tcg_out_rlw_rc(s, RLWINM, dest, arg1, 0, mb, me, rc); + return; + } + if (TCG_TARGET_REG_BITS == 64) { + 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) { @@ -1436,7 +1786,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; } @@ -1459,6 +1812,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: @@ -1490,7 +1849,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); @@ -1506,8 +1865,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); @@ -1515,18 +1886,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); } } @@ -1548,9 +1929,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); @@ -1559,18 +1941,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)); @@ -1579,9 +1974,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: @@ -1595,11 +1998,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); @@ -1613,74 +2016,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, @@ -1693,7 +2108,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]; @@ -1722,7 +2137,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 { @@ -1737,17 +2152,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 { @@ -1792,7 +2207,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: @@ -1810,12 +2240,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(); } } @@ -1823,15 +2253,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) @@ -1847,103 +2277,6 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, insn); } -static inline uint64_t make_pair(tcg_insn_unit i1, tcg_insn_unit i2) -{ - if (HOST_BIG_ENDIAN) { - return (uint64_t)i1 << 32 | i2; - } - return (uint64_t)i2 << 32 | i1; -} - -static inline void ppc64_replace2(uintptr_t rx, uintptr_t rw, - tcg_insn_unit i0, tcg_insn_unit i1) -{ -#if TCG_TARGET_REG_BITS == 64 - qatomic_set((uint64_t *)rw, make_pair(i0, i1)); - flush_idcache_range(rx, rw, 8); -#else - qemu_build_not_reached(); -#endif -} - -static inline void ppc64_replace4(uintptr_t rx, uintptr_t rw, - tcg_insn_unit i0, tcg_insn_unit i1, - tcg_insn_unit i2, tcg_insn_unit i3) -{ - uint64_t p[2]; - - p[!HOST_BIG_ENDIAN] = make_pair(i0, i1); - p[HOST_BIG_ENDIAN] = make_pair(i2, i3); - - /* - * There's no convenient way to get the compiler to allocate a pair - * of registers at an even index, so copy into r6/r7 and clobber. - */ - asm("mr %%r6, %1\n\t" - "mr %%r7, %2\n\t" - "stq %%r6, %0" - : "=Q"(*(__int128 *)rw) : "r"(p[0]), "r"(p[1]) : "r6", "r7"); - flush_idcache_range(rx, rw, 16); -} - -void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, - uintptr_t jmp_rw, uintptr_t addr) -{ - tcg_insn_unit i0, i1, i2, i3; - intptr_t tb_diff = addr - tc_ptr; - intptr_t br_diff = addr - (jmp_rx + 4); - intptr_t lo, hi; - - if (TCG_TARGET_REG_BITS == 32) { - 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); - return; - } - - /* - * For 16-bit displacements, we can use a single add + branch. - * This happens quite often. - */ - if (tb_diff == (int16_t)tb_diff) { - i0 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, tb_diff); - i1 = B | (br_diff & 0x3fffffc); - ppc64_replace2(jmp_rx, jmp_rw, i0, i1); - return; - } - - lo = (int16_t)tb_diff; - hi = (int32_t)(tb_diff - lo); - assert(tb_diff == hi + lo); - i0 = ADDIS | TAI(TCG_REG_TB, TCG_REG_TB, hi >> 16); - i1 = ADDI | TAI(TCG_REG_TB, TCG_REG_TB, lo); - - /* - * Without stq from 2.07, we can only update two insns, - * and those must be the ones that load the target address. - */ - if (!have_isa_2_07) { - ppc64_replace2(jmp_rx, jmp_rw, i0, i1); - return; - } - - /* - * For 26-bit displacements, we can use a direct branch. - * Otherwise we still need the indirect branch, which we - * must restore after a potential direct branch write. - */ - br_diff -= 4; - if (in_range_b(br_diff)) { - i2 = B | (br_diff & 0x3fffffc); - i3 = NOP; - } else { - i2 = MTSPR | RS(TCG_REG_TB) | CTR; - i3 = BCCTR | BO_ALWAYS; - } - ppc64_replace4(jmp_rx, jmp_rw, i0, i1, i2, i3); -} - static void tcg_out_call_int(TCGContext *s, int lk, const tcg_insn_unit *target) { @@ -1995,7 +2328,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); } @@ -2024,211 +2358,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; @@ -2236,283 +2395,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_TMP2, addrlo); + h->index = TCG_REG_TMP2; + } 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_TMP2, 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_TMP2)); } 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) @@ -2525,7 +2787,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) @@ -2589,18 +2850,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 */ @@ -2616,6 +2872,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]) @@ -2623,47 +2949,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 or 16-byte aligned. */ - while ((uintptr_t)s->code_ptr & (have_isa_2_07 ? 15 : 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; @@ -2688,7 +2975,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: @@ -3023,42 +3310,89 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out32(s, MODUD | TAB(args[0], args[1], args[2])); 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, 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_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, 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_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, 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_qemu_st_i64: - tcg_out_qemu_st(s, args, true); + 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_ext8s_i32: - case INDEX_op_ext8s_i64: - tcg_out_ext8s(s, args[0], args[1]); + 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_ext16s_i32: - case INDEX_op_ext16s_i64: - tcg_out_ext16s(s, args[0], args[1]); + 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_ext_i32_i64: - case INDEX_op_ext32s_i64: - tcg_out_ext32s(s, args[0], args[1]); + 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_extu_i32_i64: - tcg_out_ext32u(s, args[0], 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_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); @@ -3185,8 +3519,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(); } } @@ -3220,12 +3569,14 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_usadd_vec: case INDEX_op_ussub_vec: return vece <= MO_32; - case INDEX_op_cmp_vec: case INDEX_op_shli_vec: case INDEX_op_shri_vec: case INDEX_op_sari_vec: case INDEX_op_rotli_vec: return vece <= MO_32 || have_isa_2_07 ? -1 : 0; + case INDEX_op_cmp_vec: + case INDEX_op_cmpsel_vec: + return vece <= MO_32 || have_isa_2_07 ? 1 : 0; case INDEX_op_neg_vec: return vece >= MO_32 && have_isa_3_00; case INDEX_op_mul_vec: @@ -3366,6 +3717,149 @@ static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, return true; } +static void tcg_out_not_vec(TCGContext *s, TCGReg a0, TCGReg a1) +{ + tcg_out32(s, VNOR | VRT(a0) | VRA(a1) | VRB(a1)); +} + +static void tcg_out_or_vec(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, VOR | VRT(a0) | VRA(a1) | VRB(a2)); +} + +static void tcg_out_orc_vec(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, VORC | VRT(a0) | VRA(a1) | VRB(a2)); +} + +static void tcg_out_and_vec(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, VAND | VRT(a0) | VRA(a1) | VRB(a2)); +} + +static void tcg_out_andc_vec(TCGContext *s, TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, VANDC | VRT(a0) | VRA(a1) | VRB(a2)); +} + +static void tcg_out_bitsel_vec(TCGContext *s, TCGReg d, + TCGReg c, TCGReg t, TCGReg f) +{ + if (TCG_TARGET_HAS_bitsel_vec) { + tcg_out32(s, XXSEL | VRT(d) | VRC(c) | VRB(t) | VRA(f)); + } else { + tcg_out_and_vec(s, TCG_VEC_TMP2, t, c); + tcg_out_andc_vec(s, d, f, c); + tcg_out_or_vec(s, d, d, TCG_VEC_TMP2); + } +} + +static bool tcg_out_cmp_vec_noinv(TCGContext *s, unsigned vece, TCGReg a0, + TCGReg a1, TCGReg a2, TCGCond cond) +{ + static const uint32_t + eq_op[4] = { VCMPEQUB, VCMPEQUH, VCMPEQUW, VCMPEQUD }, + ne_op[4] = { VCMPNEB, VCMPNEH, VCMPNEW, 0 }, + gts_op[4] = { VCMPGTSB, VCMPGTSH, VCMPGTSW, VCMPGTSD }, + gtu_op[4] = { VCMPGTUB, VCMPGTUH, VCMPGTUW, VCMPGTUD }; + uint32_t insn; + + bool need_swap = false, need_inv = false; + + tcg_debug_assert(vece <= MO_32 || have_isa_2_07); + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_GT: + case TCG_COND_GTU: + break; + case TCG_COND_NE: + if (have_isa_3_00 && vece <= MO_32) { + break; + } + /* fall through */ + case TCG_COND_LE: + case TCG_COND_LEU: + need_inv = true; + break; + case TCG_COND_LT: + case TCG_COND_LTU: + need_swap = true; + break; + case TCG_COND_GE: + case TCG_COND_GEU: + need_swap = need_inv = true; + break; + default: + g_assert_not_reached(); + } + + if (need_inv) { + cond = tcg_invert_cond(cond); + } + if (need_swap) { + TCGReg swap = a1; + a1 = a2; + a2 = swap; + cond = tcg_swap_cond(cond); + } + + switch (cond) { + case TCG_COND_EQ: + insn = eq_op[vece]; + break; + case TCG_COND_NE: + insn = ne_op[vece]; + break; + case TCG_COND_GT: + insn = gts_op[vece]; + break; + case TCG_COND_GTU: + insn = gtu_op[vece]; + break; + default: + g_assert_not_reached(); + } + tcg_out32(s, insn | VRT(a0) | VRA(a1) | VRB(a2)); + + return need_inv; +} + +static void tcg_out_cmp_vec(TCGContext *s, unsigned vece, TCGReg a0, + TCGReg a1, TCGReg a2, TCGCond cond) +{ + if (tcg_out_cmp_vec_noinv(s, vece, a0, a1, a2, cond)) { + tcg_out_not_vec(s, a0, a0); + } +} + +static void tcg_out_cmpsel_vec(TCGContext *s, unsigned vece, TCGReg a0, + TCGReg c1, TCGReg c2, TCGArg v3, int const_v3, + TCGReg v4, TCGCond cond) +{ + bool inv = tcg_out_cmp_vec_noinv(s, vece, TCG_VEC_TMP1, c1, c2, cond); + + if (!const_v3) { + if (inv) { + tcg_out_bitsel_vec(s, a0, TCG_VEC_TMP1, v4, v3); + } else { + tcg_out_bitsel_vec(s, a0, TCG_VEC_TMP1, v3, v4); + } + } else if (v3) { + if (inv) { + tcg_out_orc_vec(s, a0, v4, TCG_VEC_TMP1); + } else { + tcg_out_or_vec(s, a0, v4, TCG_VEC_TMP1); + } + } else { + if (inv) { + tcg_out_and_vec(s, a0, v4, TCG_VEC_TMP1); + } else { + tcg_out_andc_vec(s, a0, v4, TCG_VEC_TMP1); + } + } +} + static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3376,10 +3870,6 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, sub_op[4] = { VSUBUBM, VSUBUHM, VSUBUWM, VSUBUDM }, mul_op[4] = { 0, 0, VMULUWM, VMULLD }, neg_op[4] = { 0, 0, VNEGW, VNEGD }, - eq_op[4] = { VCMPEQUB, VCMPEQUH, VCMPEQUW, VCMPEQUD }, - ne_op[4] = { VCMPNEB, VCMPNEH, VCMPNEW, 0 }, - gts_op[4] = { VCMPGTSB, VCMPGTSH, VCMPGTSW, VCMPGTSD }, - gtu_op[4] = { VCMPGTUB, VCMPGTUH, VCMPGTUW, VCMPGTUD }, ssadd_op[4] = { VADDSBS, VADDSHS, VADDSWS, 0 }, usadd_op[4] = { VADDUBS, VADDUHS, VADDUWS, 0 }, sssub_op[4] = { VSUBSBS, VSUBSHS, VSUBSWS, 0 }, @@ -3461,24 +3951,23 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, insn = sarv_op[vece]; break; case INDEX_op_and_vec: - insn = VAND; - break; + tcg_out_and_vec(s, a0, a1, a2); + return; case INDEX_op_or_vec: - insn = VOR; - break; + tcg_out_or_vec(s, a0, a1, a2); + return; case INDEX_op_xor_vec: insn = VXOR; break; case INDEX_op_andc_vec: - insn = VANDC; - break; + tcg_out_andc_vec(s, a0, a1, a2); + return; case INDEX_op_not_vec: - insn = VNOR; - a2 = a1; - break; + tcg_out_not_vec(s, a0, a1); + return; case INDEX_op_orc_vec: - insn = VORC; - break; + tcg_out_orc_vec(s, a0, a1, a2); + return; case INDEX_op_nand_vec: insn = VNAND; break; @@ -3490,26 +3979,14 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_cmp_vec: - switch (args[3]) { - case TCG_COND_EQ: - insn = eq_op[vece]; - break; - case TCG_COND_NE: - insn = ne_op[vece]; - break; - case TCG_COND_GT: - insn = gts_op[vece]; - break; - case TCG_COND_GTU: - insn = gtu_op[vece]; - break; - default: - g_assert_not_reached(); - } - break; - + tcg_out_cmp_vec(s, vece, a0, a1, a2, args[3]); + return; + case INDEX_op_cmpsel_vec: + tcg_out_cmpsel_vec(s, vece, a0, a1, a2, + args[3], const_args[3], args[4], args[5]); + return; case INDEX_op_bitsel_vec: - tcg_out32(s, XXSEL | VRT(a0) | VRC(a1) | VRB(a2) | VRA(args[3])); + tcg_out_bitsel_vec(s, a0, a1, a2, args[3]); return; case INDEX_op_dup2_vec: @@ -3574,56 +4051,6 @@ static void expand_vec_shi(TCGType type, unsigned vece, TCGv_vec v0, tcgv_vec_arg(v1), tcgv_vec_arg(t1)); } -static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec v1, TCGv_vec v2, TCGCond cond) -{ - bool need_swap = false, need_inv = false; - - tcg_debug_assert(vece <= MO_32 || have_isa_2_07); - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_GT: - case TCG_COND_GTU: - break; - case TCG_COND_NE: - if (have_isa_3_00 && vece <= MO_32) { - break; - } - /* fall through */ - case TCG_COND_LE: - case TCG_COND_LEU: - need_inv = true; - break; - case TCG_COND_LT: - case TCG_COND_LTU: - need_swap = true; - break; - case TCG_COND_GE: - case TCG_COND_GEU: - need_swap = need_inv = true; - break; - default: - g_assert_not_reached(); - } - - if (need_inv) { - cond = tcg_invert_cond(cond); - } - if (need_swap) { - TCGv_vec t1; - t1 = v1, v1 = v2, v2 = t1; - cond = tcg_swap_cond(cond); - } - - vec_gen_4(INDEX_op_cmp_vec, type, vece, tcgv_vec_arg(v0), - tcgv_vec_arg(v1), tcgv_vec_arg(v2), cond); - - if (need_inv) { - tcg_gen_not_vec(vece, v0, v0); - } -} - static void expand_vec_mul(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGv_vec v2) { @@ -3644,7 +4071,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); @@ -3698,10 +4125,6 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, case INDEX_op_rotli_vec: expand_vec_shi(type, vece, v0, v1, a2, INDEX_op_rotlv_vec); break; - case INDEX_op_cmp_vec: - v2 = temp_tcgv_vec(arg_temp(a2)); - expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); - break; case INDEX_op_mul_vec: v2 = temp_tcgv_vec(arg_temp(a2)); expand_vec_mul(type, vece, v0, v1, v2); @@ -3780,7 +4203,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: @@ -3788,7 +4210,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: @@ -3832,11 +4253,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); @@ -3851,25 +4277,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: @@ -3921,6 +4352,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_bitsel_vec: case INDEX_op_ppc_msum_vec: return C_O1_I3(v, v, v, v); + case INDEX_op_cmpsel_vec: + return C_O1_I4(v, v, v, vZM, v); default: g_assert_not_reached(); @@ -3929,45 +4362,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) { @@ -4019,7 +4413,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 b5cd225cfa..0b2171d38c 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 */ @@ -90,7 +83,6 @@ extern bool have_vsx; #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,19 +95,17 @@ 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 have_isa_3_00 #define TCG_TARGET_HAS_rot_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 @@ -178,13 +172,10 @@ extern bool have_vsx; #define TCG_TARGET_HAS_sat_vec 1 #define TCG_TARGET_HAS_minmax_vec 1 #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_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec 0 #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 88d6bb273f..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, @@ -558,9 +582,11 @@ static int 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 int 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..3c4ef44eb0 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -10,21 +10,23 @@ * 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) +C_O0_I2(v, r) +C_O1_I1(v, r) +C_O1_I1(v, v) +C_O1_I2(v, v, r) +C_O1_I2(v, v, v) +C_O1_I2(v, vK, v) +C_O1_I2(v, v, vK) +C_O1_I2(v, v, vL) +C_O1_I4(v, v, vL, vK, vK) diff --git a/tcg/riscv/tcg-target-con-str.h b/tcg/riscv/tcg-target-con-str.h index 8d8afaee53..089efe96ca 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -9,13 +9,16 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) +REGS('v', ALL_VECTOR_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('K', TCG_CT_CONST_S5) +CONST('L', TCG_CT_CONST_CMP_VI) 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..f8331e4688 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -32,44 +32,20 @@ #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "zero", - "ra", - "sp", - "gp", - "tp", - "t0", - "t1", - "t2", - "s0", - "s1", - "a0", - "a1", - "a2", - "a3", - "a4", - "a5", - "a6", - "a7", - "s2", - "s3", - "s4", - "s5", - "s6", - "s7", - "s8", - "s9", - "s10", - "s11", - "t3", - "t4", - "t5", - "t6" + "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", + "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", }; #endif 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, @@ -100,6 +76,16 @@ static const int tcg_target_reg_alloc_order[] = { TCG_REG_A5, TCG_REG_A6, TCG_REG_A7, + + /* Vector registers and TCG_REG_V0 reserved for mask. */ + 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, }; static const int tcg_target_call_iarg_regs[] = { @@ -113,63 +99,57 @@ 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, -}; - -#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 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 - - -static inline tcg_target_long sextreg(tcg_target_long val, int pos, int len) +static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) { - if (TCG_TARGET_REG_BITS == 32) { - return sextract32(val, pos, len); - } else { - return sextract64(val, pos, len); - } + tcg_debug_assert(kind == TCG_CALL_RET_NORMAL); + tcg_debug_assert(slot >= 0 && slot <= 1); + return TCG_REG_A0 + slot; } -/* test if a constant matches the constraint */ -static bool tcg_target_const_match(int64_t val, TCGType type, int ct) -{ - if (ct & TCG_CT_CONST) { - return 1; - } - if ((ct & TCG_CT_CONST_ZERO) && val == 0) { - return 1; - } - if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { - return 1; - } - if ((ct & TCG_CT_CONST_N12) && -val == sextreg(-val, 0, 12)) { - return 1; - } - if ((ct & TCG_CT_CONST_M12) && val >= -0xfff && val <= 0xfff) { - return 1; - } - return 0; -} +#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 TCG_CT_CONST_S5 0x2000 +#define TCG_CT_CONST_CMP_VI 0x4000 + +#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) +#define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) +#define ALL_DVECTOR_REG_GROUPS 0x5555555500000000 +#define ALL_QVECTOR_REG_GROUPS 0x1111111100000000 + +#define sextreg sextract64 /* * RISC-V Base ISA opcodes (IM) */ +#define V_OPIVV (0x0 << 12) +#define V_OPFVV (0x1 << 12) +#define V_OPMVV (0x2 << 12) +#define V_OPIVI (0x3 << 12) +#define V_OPIVX (0x4 << 12) +#define V_OPFVF (0x5 << 12) +#define V_OPMVX (0x6 << 12) +#define V_OPCFG (0x7 << 12) + +/* NF <= 7 && NF >= 0 */ +#define V_NF(x) (x << 29) +#define V_UNIT_STRIDE (0x0 << 20) +#define V_UNIT_STRIDE_WHOLE_REG (0x8 << 20) + +typedef enum { + VLMUL_M1 = 0, /* LMUL=1 */ + VLMUL_M2, /* LMUL=2 */ + VLMUL_M4, /* LMUL=4 */ + VLMUL_M8, /* LMUL=8 */ + VLMUL_RESERVED, + VLMUL_MF8, /* LMUL=1/8 */ + VLMUL_MF4, /* LMUL=1/4 */ + VLMUL_MF2, /* LMUL=1/2 */ +} RISCVVlmul; + typedef enum { OPC_ADD = 0x33, OPC_ADDI = 0x13, @@ -220,7 +200,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,27 +214,230 @@ 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, + + /* V: Vector extension 1.0 */ + OPC_VSETVLI = 0x57 | V_OPCFG, + OPC_VSETIVLI = 0xc0000057 | V_OPCFG, + OPC_VSETVL = 0x80000057 | V_OPCFG, + + OPC_VLE8_V = 0x7 | V_UNIT_STRIDE, + OPC_VLE16_V = 0x5007 | V_UNIT_STRIDE, + OPC_VLE32_V = 0x6007 | V_UNIT_STRIDE, + OPC_VLE64_V = 0x7007 | V_UNIT_STRIDE, + OPC_VSE8_V = 0x27 | V_UNIT_STRIDE, + OPC_VSE16_V = 0x5027 | V_UNIT_STRIDE, + OPC_VSE32_V = 0x6027 | V_UNIT_STRIDE, + OPC_VSE64_V = 0x7027 | V_UNIT_STRIDE, + + OPC_VL1RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(0), + OPC_VL2RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(1), + OPC_VL4RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(3), + OPC_VL8RE64_V = 0x2007007 | V_UNIT_STRIDE_WHOLE_REG | V_NF(7), + + OPC_VS1R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(0), + OPC_VS2R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(1), + OPC_VS4R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(3), + OPC_VS8R_V = 0x2000027 | V_UNIT_STRIDE_WHOLE_REG | V_NF(7), + + OPC_VMERGE_VIM = 0x5c000057 | V_OPIVI, + OPC_VMERGE_VVM = 0x5c000057 | V_OPIVV, + + OPC_VADD_VV = 0x57 | V_OPIVV, + OPC_VADD_VI = 0x57 | V_OPIVI, + OPC_VSUB_VV = 0x8000057 | V_OPIVV, + OPC_VRSUB_VI = 0xc000057 | V_OPIVI, + OPC_VAND_VV = 0x24000057 | V_OPIVV, + OPC_VAND_VI = 0x24000057 | V_OPIVI, + OPC_VOR_VV = 0x28000057 | V_OPIVV, + OPC_VOR_VI = 0x28000057 | V_OPIVI, + OPC_VXOR_VV = 0x2c000057 | V_OPIVV, + OPC_VXOR_VI = 0x2c000057 | V_OPIVI, + + OPC_VMUL_VV = 0x94000057 | V_OPMVV, + OPC_VSADD_VV = 0x84000057 | V_OPIVV, + OPC_VSADD_VI = 0x84000057 | V_OPIVI, + OPC_VSSUB_VV = 0x8c000057 | V_OPIVV, + OPC_VSSUB_VI = 0x8c000057 | V_OPIVI, + OPC_VSADDU_VV = 0x80000057 | V_OPIVV, + OPC_VSADDU_VI = 0x80000057 | V_OPIVI, + OPC_VSSUBU_VV = 0x88000057 | V_OPIVV, + OPC_VSSUBU_VI = 0x88000057 | V_OPIVI, + + OPC_VMAX_VV = 0x1c000057 | V_OPIVV, + OPC_VMAX_VI = 0x1c000057 | V_OPIVI, + OPC_VMAXU_VV = 0x18000057 | V_OPIVV, + OPC_VMAXU_VI = 0x18000057 | V_OPIVI, + OPC_VMIN_VV = 0x14000057 | V_OPIVV, + OPC_VMIN_VI = 0x14000057 | V_OPIVI, + OPC_VMINU_VV = 0x10000057 | V_OPIVV, + OPC_VMINU_VI = 0x10000057 | V_OPIVI, + + OPC_VMSEQ_VV = 0x60000057 | V_OPIVV, + OPC_VMSEQ_VI = 0x60000057 | V_OPIVI, + OPC_VMSEQ_VX = 0x60000057 | V_OPIVX, + OPC_VMSNE_VV = 0x64000057 | V_OPIVV, + OPC_VMSNE_VI = 0x64000057 | V_OPIVI, + OPC_VMSNE_VX = 0x64000057 | V_OPIVX, + + OPC_VMSLTU_VV = 0x68000057 | V_OPIVV, + OPC_VMSLTU_VX = 0x68000057 | V_OPIVX, + OPC_VMSLT_VV = 0x6c000057 | V_OPIVV, + OPC_VMSLT_VX = 0x6c000057 | V_OPIVX, + OPC_VMSLEU_VV = 0x70000057 | V_OPIVV, + OPC_VMSLEU_VX = 0x70000057 | V_OPIVX, + OPC_VMSLE_VV = 0x74000057 | V_OPIVV, + OPC_VMSLE_VX = 0x74000057 | V_OPIVX, + + OPC_VMSLEU_VI = 0x70000057 | V_OPIVI, + OPC_VMSLE_VI = 0x74000057 | V_OPIVI, + OPC_VMSGTU_VI = 0x78000057 | V_OPIVI, + OPC_VMSGTU_VX = 0x78000057 | V_OPIVX, + OPC_VMSGT_VI = 0x7c000057 | V_OPIVI, + OPC_VMSGT_VX = 0x7c000057 | V_OPIVX, + + OPC_VSLL_VV = 0x94000057 | V_OPIVV, + OPC_VSLL_VI = 0x94000057 | V_OPIVI, + OPC_VSLL_VX = 0x94000057 | V_OPIVX, + OPC_VSRL_VV = 0xa0000057 | V_OPIVV, + OPC_VSRL_VI = 0xa0000057 | V_OPIVI, + OPC_VSRL_VX = 0xa0000057 | V_OPIVX, + OPC_VSRA_VV = 0xa4000057 | V_OPIVV, + OPC_VSRA_VI = 0xa4000057 | V_OPIVI, + OPC_VSRA_VX = 0xa4000057 | V_OPIVX, + + OPC_VMV_V_V = 0x5e000057 | V_OPIVV, + OPC_VMV_V_I = 0x5e000057 | V_OPIVI, + OPC_VMV_V_X = 0x5e000057 | V_OPIVX, + + OPC_VMVNR_V = 0x9e000057 | V_OPIVI, } RISCVInsn; +static const struct { + RISCVInsn op; + bool swap; +} tcg_cmpcond_to_rvv_vv[] = { + [TCG_COND_EQ] = { OPC_VMSEQ_VV, false }, + [TCG_COND_NE] = { OPC_VMSNE_VV, false }, + [TCG_COND_LT] = { OPC_VMSLT_VV, false }, + [TCG_COND_GE] = { OPC_VMSLE_VV, true }, + [TCG_COND_GT] = { OPC_VMSLT_VV, true }, + [TCG_COND_LE] = { OPC_VMSLE_VV, false }, + [TCG_COND_LTU] = { OPC_VMSLTU_VV, false }, + [TCG_COND_GEU] = { OPC_VMSLEU_VV, true }, + [TCG_COND_GTU] = { OPC_VMSLTU_VV, true }, + [TCG_COND_LEU] = { OPC_VMSLEU_VV, false } +}; + +static const struct { + RISCVInsn op; + int min; + int max; + bool adjust; +} tcg_cmpcond_to_rvv_vi[] = { + [TCG_COND_EQ] = { OPC_VMSEQ_VI, -16, 15, false }, + [TCG_COND_NE] = { OPC_VMSNE_VI, -16, 15, false }, + [TCG_COND_GT] = { OPC_VMSGT_VI, -16, 15, false }, + [TCG_COND_LE] = { OPC_VMSLE_VI, -16, 15, false }, + [TCG_COND_LT] = { OPC_VMSLE_VI, -15, 16, true }, + [TCG_COND_GE] = { OPC_VMSGT_VI, -15, 16, true }, + [TCG_COND_LEU] = { OPC_VMSLEU_VI, 0, 15, false }, + [TCG_COND_GTU] = { OPC_VMSGTU_VI, 0, 15, false }, + [TCG_COND_LTU] = { OPC_VMSLEU_VI, 1, 16, true }, + [TCG_COND_GEU] = { OPC_VMSGTU_VI, 1, 16, true }, +}; + +/* test if a constant matches the constraint */ +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 ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } + if (type >= TCG_TYPE_V64) { + /* Val is replicated by VECE; extract the highest element. */ + val >>= (-8 << vece) & 63; + } + /* + * 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; + } + /* + * 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; + } + /* + * 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; + } + /* + * Sign extended from 5 bits: [-0x10, 0x0f]. + * Used for vector-immediate. + */ + if ((ct & TCG_CT_CONST_S5) && val >= -0x10 && val <= 0x0f) { + return 1; + } + /* + * Used for vector compare OPIVI instructions. + */ + if ((ct & TCG_CT_CONST_CMP_VI) && + val >= tcg_cmpcond_to_rvv_vi[cond].min && + val <= tcg_cmpcond_to_rvv_vi[cond].max) { + return true; + } + return 0; +} + /* * RISC-V immediate and instruction encoders (excludes 16-bit RVC) */ @@ -346,6 +528,45 @@ static int32_t encode_uj(RISCVInsn opc, TCGReg rd, uint32_t imm) return opc | (rd & 0x1f) << 7 | encode_ujimm20(imm); } + +/* Type-OPIVI */ + +static int32_t encode_vi(RISCVInsn opc, TCGReg rd, int32_t imm, + TCGReg vs2, bool vm) +{ + return opc | (rd & 0x1f) << 7 | (imm & 0x1f) << 15 | + (vs2 & 0x1f) << 20 | (vm << 25); +} + +/* Type-OPIVV/OPMVV/OPIVX/OPMVX, Vector load and store */ + +static int32_t encode_v(RISCVInsn opc, TCGReg d, TCGReg s1, + TCGReg s2, bool vm) +{ + return opc | (d & 0x1f) << 7 | (s1 & 0x1f) << 15 | + (s2 & 0x1f) << 20 | (vm << 25); +} + +/* Vector vtype */ + +static uint32_t encode_vtype(bool vta, bool vma, + MemOp vsew, RISCVVlmul vlmul) +{ + return vma << 7 | vta << 6 | vsew << 3 | vlmul; +} + +static int32_t encode_vset(RISCVInsn opc, TCGReg rd, + TCGArg rs1, uint32_t vtype) +{ + return opc | (rd & 0x1f) << 7 | (rs1 & 0x1f) << 15 | (vtype & 0x7ff) << 20; +} + +static int32_t encode_vseti(RISCVInsn opc, TCGReg rd, + uint32_t uimm, uint32_t vtype) +{ + return opc | (rd & 0x1f) << 7 | (uimm & 0x1f) << 15 | (vtype & 0x3ff) << 20; +} + /* * RISC-V instruction emitters */ @@ -390,7 +611,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; } } @@ -458,6 +679,91 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, } } +/* + * RISC-V vector instruction emitters + */ + +/* + * Vector registers uses the same 5 lower bits as GPR registers, + * and vm=0 (vm = false) means vector masking ENABLED. + * With RVV 1.0, vs2 is the first operand, while rs1/imm is the + * second operand. + */ +static void tcg_out_opc_vv(TCGContext *s, RISCVInsn opc, + TCGReg vd, TCGReg vs2, TCGReg vs1) +{ + tcg_out32(s, encode_v(opc, vd, vs1, vs2, true)); +} + +static void tcg_out_opc_vx(TCGContext *s, RISCVInsn opc, + TCGReg vd, TCGReg vs2, TCGReg rs1) +{ + tcg_out32(s, encode_v(opc, vd, rs1, vs2, true)); +} + +static void tcg_out_opc_vi(TCGContext *s, RISCVInsn opc, + TCGReg vd, TCGReg vs2, int32_t imm) +{ + tcg_out32(s, encode_vi(opc, vd, imm, vs2, true)); +} + +static void tcg_out_opc_vv_vi(TCGContext *s, RISCVInsn o_vv, RISCVInsn o_vi, + TCGReg vd, TCGReg vs2, TCGArg vi1, int c_vi1) +{ + if (c_vi1) { + tcg_out_opc_vi(s, o_vi, vd, vs2, vi1); + } else { + tcg_out_opc_vv(s, o_vv, vd, vs2, vi1); + } +} + +static void tcg_out_opc_vim_mask(TCGContext *s, RISCVInsn opc, TCGReg vd, + TCGReg vs2, int32_t imm) +{ + tcg_out32(s, encode_vi(opc, vd, imm, vs2, false)); +} + +static void tcg_out_opc_vvm_mask(TCGContext *s, RISCVInsn opc, TCGReg vd, + TCGReg vs2, TCGReg vs1) +{ + tcg_out32(s, encode_v(opc, vd, vs1, vs2, false)); +} + +typedef struct VsetCache { + uint32_t movi_insn; + uint32_t vset_insn; +} VsetCache; + +static VsetCache riscv_vset_cache[3][4]; + +static void set_vtype(TCGContext *s, TCGType type, MemOp vsew) +{ + const VsetCache *p = &riscv_vset_cache[type - TCG_TYPE_V64][vsew]; + + s->riscv_cur_type = type; + s->riscv_cur_vsew = vsew; + + if (p->movi_insn) { + tcg_out32(s, p->movi_insn); + } + tcg_out32(s, p->vset_insn); +} + +static MemOp set_vtype_len(TCGContext *s, TCGType type) +{ + if (type != s->riscv_cur_type) { + set_vtype(s, type, MO_64); + } + return s->riscv_cur_vsew; +} + +static void set_vtype_len_sew(TCGContext *s, TCGType type, MemOp vsew) +{ + if (type != s->riscv_cur_type || vsew != s->riscv_cur_vsew) { + set_vtype(s, type, vsew); + } +} + /* * TCG intrinsics */ @@ -472,6 +778,15 @@ static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) case TCG_TYPE_I64: tcg_out_opc_imm(s, OPC_ADDI, ret, arg, 0); break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + { + int lmul = type - riscv_lg2_vlenb; + int nf = 1 << MAX(lmul, 0); + tcg_out_opc_vi(s, OPC_VMVNR_V, ret, arg, nf - 1); + } + break; default: g_assert_not_reached(); } @@ -484,7 +799,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 +810,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 +818,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 +859,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 +878,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 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_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 +921,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); @@ -620,18 +979,101 @@ static void tcg_out_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, } } +static void tcg_out_vec_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, + TCGReg addr, intptr_t offset) +{ + tcg_debug_assert(data >= TCG_REG_V0); + tcg_debug_assert(addr < TCG_REG_V0); + + if (offset) { + tcg_debug_assert(addr != TCG_REG_ZERO); + if (offset == sextreg(offset, 0, 12)) { + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_TMP0, addr, offset); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, offset); + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, TCG_REG_TMP0, addr); + } + addr = TCG_REG_TMP0; + } + tcg_out32(s, encode_v(opc, data, addr, 0, true)); +} + 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; + + switch (type) { + case TCG_TYPE_I32: + tcg_out_ldst(s, OPC_LW, arg, arg1, arg2); + break; + case TCG_TYPE_I64: + tcg_out_ldst(s, OPC_LD, arg, arg1, arg2); + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + if (type >= riscv_lg2_vlenb) { + static const RISCVInsn whole_reg_ld[] = { + OPC_VL1RE64_V, OPC_VL2RE64_V, OPC_VL4RE64_V, OPC_VL8RE64_V + }; + unsigned idx = type - riscv_lg2_vlenb; + + tcg_debug_assert(idx < ARRAY_SIZE(whole_reg_ld)); + insn = whole_reg_ld[idx]; + } else { + static const RISCVInsn unit_stride_ld[] = { + OPC_VLE8_V, OPC_VLE16_V, OPC_VLE32_V, OPC_VLE64_V + }; + MemOp prev_vsew = set_vtype_len(s, type); + + tcg_debug_assert(prev_vsew < ARRAY_SIZE(unit_stride_ld)); + insn = unit_stride_ld[prev_vsew]; + } + tcg_out_vec_ldst(s, insn, arg, arg1, arg2); + break; + default: + g_assert_not_reached(); + } } 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; + + switch (type) { + case TCG_TYPE_I32: + tcg_out_ldst(s, OPC_SW, arg, arg1, arg2); + break; + case TCG_TYPE_I64: + tcg_out_ldst(s, OPC_SD, arg, arg1, arg2); + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + if (type >= riscv_lg2_vlenb) { + static const RISCVInsn whole_reg_st[] = { + OPC_VS1R_V, OPC_VS2R_V, OPC_VS4R_V, OPC_VS8R_V + }; + unsigned idx = type - riscv_lg2_vlenb; + + tcg_debug_assert(idx < ARRAY_SIZE(whole_reg_st)); + insn = whole_reg_st[idx]; + } else { + static const RISCVInsn unit_stride_st[] = { + OPC_VSE8_V, OPC_VSE16_V, OPC_VSE32_V, OPC_VSE64_V + }; + MemOp prev_vsew = set_vtype_len(s, type); + + tcg_debug_assert(prev_vsew < ARRAY_SIZE(unit_stride_st)); + insn = unit_stride_st[prev_vsew]; + } + tcg_out_vec_ldst(s, insn, arg, arg1, arg2); + break; + default: + g_assert_not_reached(); + } } static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, @@ -687,9 +1129,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, @@ -699,6 +1147,40 @@ static void tcg_out_addsub2(TCGContext *s, } } +static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg src) +{ + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VMV_V_X, dst, 0, src); + return true; +} + +static bool tcg_out_dupm_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, TCGReg base, intptr_t offset) +{ + tcg_out_ld(s, TCG_TYPE_REG, TCG_REG_TMP0, base, offset); + return tcg_out_dup_vec(s, type, vece, dst, TCG_REG_TMP0); +} + +static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, + TCGReg dst, int64_t arg) +{ + /* Arg is replicated by VECE; extract the highest element. */ + arg >>= (-8 << vece) & 63; + + if (arg >= -16 && arg < 16) { + if (arg == 0 || arg == -1) { + set_vtype_len(s, type); + } else { + set_vtype_len_sew(s, type, vece); + } + tcg_out_opc_vi(s, OPC_VMV_V_I, dst, 0, arg); + return; + } + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP0, arg); + tcg_out_dup_vec(s, type, vece, dst, TCG_REG_TMP0); +} + static const struct { RISCVInsn op; bool swap; @@ -732,64 +1214,367 @@ 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 (!(cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_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_cmpsel(TCGContext *s, TCGType type, unsigned vece, + TCGCond cond, TCGReg ret, + TCGReg cmp1, TCGReg cmp2, bool c_cmp2, + TCGReg val1, bool c_val1, + TCGReg val2, bool c_val2) +{ + set_vtype_len_sew(s, type, vece); + + /* Use only vmerge_vim if possible, by inverting the test. */ + if (c_val2 && !c_val1) { + TCGArg temp = val1; + cond = tcg_invert_cond(cond); + val1 = val2; + val2 = temp; + c_val1 = true; + c_val2 = false; + } + + /* Perform the comparison into V0 mask. */ + if (c_cmp2) { + tcg_out_opc_vi(s, tcg_cmpcond_to_rvv_vi[cond].op, TCG_REG_V0, cmp1, + cmp2 - tcg_cmpcond_to_rvv_vi[cond].adjust); + } else if (tcg_cmpcond_to_rvv_vv[cond].swap) { + tcg_out_opc_vv(s, tcg_cmpcond_to_rvv_vv[cond].op, + TCG_REG_V0, cmp2, cmp1); + } else { + tcg_out_opc_vv(s, tcg_cmpcond_to_rvv_vv[cond].op, + TCG_REG_V0, cmp1, cmp2); + } + if (c_val1) { + if (c_val2) { + tcg_out_opc_vi(s, OPC_VMV_V_I, ret, 0, val2); + val2 = ret; + } + /* vd[i] == v0.mask[i] ? imm : vs2[i] */ + tcg_out_opc_vim_mask(s, OPC_VMERGE_VIM, ret, val2, val1); + } else { + /* vd[i] == v0.mask[i] ? vs1[i] : vs2[i] */ + tcg_out_opc_vvm_mask(s, OPC_VMERGE_VVM, ret, val2, val1); + } +} + +static void tcg_out_vshifti(TCGContext *s, RISCVInsn opc_vi, RISCVInsn opc_vx, + TCGReg dst, TCGReg src, unsigned imm) +{ + if (imm < 32) { + tcg_out_opc_vi(s, opc_vi, dst, src, imm); + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP0, imm); + tcg_out_opc_vx(s, opc_vx, dst, src, TCG_REG_TMP0); + } +} + +static void init_setting_vtype(TCGContext *s) +{ + s->riscv_cur_type = TCG_TYPE_COUNT; } static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) @@ -798,28 +1583,29 @@ static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) ptrdiff_t offset = tcg_pcrel_diff(s, arg); int ret; + init_setting_vtype(s); + tcg_debug_assert((offset & 1) == 0); 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 +1633,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 +1640,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 +1660,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 +1670,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 +1678,200 @@ 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); -} + init_setting_vtype(s); -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_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_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 (cpuinfo & CPUINFO_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; + + init_setting_vtype(s); + + /* 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 (cpuinfo & CPUINFO_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 +1879,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 +1964,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 +2073,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 +2190,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 +2285,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,11 +2340,243 @@ 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(); } } +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; + int c2; + + a0 = args[0]; + a1 = args[1]; + a2 = args[2]; + c2 = const_args[2]; + + switch (opc) { + case INDEX_op_dupm_vec: + tcg_out_dupm_vec(s, type, vece, a0, a1, a2); + break; + case INDEX_op_ld_vec: + tcg_out_ld(s, type, a0, a1, a2); + break; + case INDEX_op_st_vec: + tcg_out_st(s, type, a0, a1, a2); + break; + case INDEX_op_add_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VADD_VV, OPC_VADD_VI, a0, a1, a2, c2); + break; + case INDEX_op_sub_vec: + set_vtype_len_sew(s, type, vece); + if (const_args[1]) { + tcg_out_opc_vi(s, OPC_VRSUB_VI, a0, a2, a1); + } else { + tcg_out_opc_vv(s, OPC_VSUB_VV, a0, a1, a2); + } + break; + case INDEX_op_and_vec: + set_vtype_len(s, type); + tcg_out_opc_vv_vi(s, OPC_VAND_VV, OPC_VAND_VI, a0, a1, a2, c2); + break; + case INDEX_op_or_vec: + set_vtype_len(s, type); + tcg_out_opc_vv_vi(s, OPC_VOR_VV, OPC_VOR_VI, a0, a1, a2, c2); + break; + case INDEX_op_xor_vec: + set_vtype_len(s, type); + tcg_out_opc_vv_vi(s, OPC_VXOR_VV, OPC_VXOR_VI, a0, a1, a2, c2); + break; + case INDEX_op_not_vec: + set_vtype_len(s, type); + tcg_out_opc_vi(s, OPC_VXOR_VI, a0, a1, -1); + break; + case INDEX_op_neg_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vi(s, OPC_VRSUB_VI, a0, a1, 0); + break; + case INDEX_op_mul_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VMUL_VV, a0, a1, a2); + break; + case INDEX_op_ssadd_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSADD_VV, OPC_VSADD_VI, a0, a1, a2, c2); + break; + case INDEX_op_sssub_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSSUB_VV, OPC_VSSUB_VI, a0, a1, a2, c2); + break; + case INDEX_op_usadd_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSADDU_VV, OPC_VSADDU_VI, a0, a1, a2, c2); + break; + case INDEX_op_ussub_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VSSUBU_VV, OPC_VSSUBU_VI, a0, a1, a2, c2); + break; + case INDEX_op_smax_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMAX_VV, OPC_VMAX_VI, a0, a1, a2, c2); + break; + case INDEX_op_smin_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMIN_VV, OPC_VMIN_VI, a0, a1, a2, c2); + break; + case INDEX_op_umax_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMAXU_VV, OPC_VMAXU_VI, a0, a1, a2, c2); + break; + case INDEX_op_umin_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv_vi(s, OPC_VMINU_VV, OPC_VMINU_VI, a0, a1, a2, c2); + break; + case INDEX_op_shls_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSLL_VX, a0, a1, a2); + break; + case INDEX_op_shrs_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSRL_VX, a0, a1, a2); + break; + case INDEX_op_sars_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSRA_VX, a0, a1, a2); + break; + case INDEX_op_shlv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VSLL_VV, a0, a1, a2); + break; + case INDEX_op_shrv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VSRL_VV, a0, a1, a2); + break; + case INDEX_op_sarv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vv(s, OPC_VSRA_VV, a0, a1, a2); + break; + case INDEX_op_shli_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSLL_VI, OPC_VSLL_VX, a0, a1, a2); + break; + case INDEX_op_shri_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSRL_VI, OPC_VSRL_VX, a0, a1, a2); + break; + case INDEX_op_sari_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSRA_VI, OPC_VSRA_VX, a0, a1, a2); + break; + case INDEX_op_rotli_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_vshifti(s, OPC_VSLL_VI, OPC_VSLL_VX, TCG_REG_V0, a1, a2); + tcg_out_vshifti(s, OPC_VSRL_VI, OPC_VSRL_VX, a0, a1, + -a2 & ((8 << vece) - 1)); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_rotls_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vx(s, OPC_VSLL_VX, TCG_REG_V0, a1, a2); + tcg_out_opc_reg(s, OPC_SUBW, TCG_REG_TMP0, TCG_REG_ZERO, a2); + tcg_out_opc_vx(s, OPC_VSRL_VX, a0, a1, TCG_REG_TMP0); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_rotlv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vi(s, OPC_VRSUB_VI, TCG_REG_V0, a2, 0); + tcg_out_opc_vv(s, OPC_VSRL_VV, TCG_REG_V0, a1, TCG_REG_V0); + tcg_out_opc_vv(s, OPC_VSLL_VV, a0, a1, a2); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_rotrv_vec: + set_vtype_len_sew(s, type, vece); + tcg_out_opc_vi(s, OPC_VRSUB_VI, TCG_REG_V0, a2, 0); + tcg_out_opc_vv(s, OPC_VSLL_VV, TCG_REG_V0, a1, TCG_REG_V0); + tcg_out_opc_vv(s, OPC_VSRL_VV, a0, a1, a2); + tcg_out_opc_vv(s, OPC_VOR_VV, a0, a0, TCG_REG_V0); + break; + case INDEX_op_cmp_vec: + tcg_out_cmpsel(s, type, vece, args[3], a0, a1, a2, c2, + -1, true, 0, true); + break; + case INDEX_op_cmpsel_vec: + tcg_out_cmpsel(s, type, vece, args[5], a0, a1, a2, c2, + args[3], const_args[3], args[4], const_args[4]); + break; + case INDEX_op_mov_vec: /* Always emitted via tcg_out_mov. */ + case INDEX_op_dup_vec: /* Always emitted via tcg_out_dup_vec. */ + default: + g_assert_not_reached(); + } +} + +void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, + TCGArg a0, ...) +{ + g_assert_not_reached(); +} + +int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) +{ + switch (opc) { + case INDEX_op_add_vec: + case INDEX_op_sub_vec: + case INDEX_op_and_vec: + case INDEX_op_or_vec: + case INDEX_op_xor_vec: + case INDEX_op_not_vec: + case INDEX_op_neg_vec: + case INDEX_op_mul_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_usadd_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: + case INDEX_op_shls_vec: + case INDEX_op_shrs_vec: + case INDEX_op_sars_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: + case INDEX_op_shri_vec: + case INDEX_op_shli_vec: + case INDEX_op_sari_vec: + case INDEX_op_rotls_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_rotrv_vec: + case INDEX_op_rotli_vec: + case INDEX_op_cmp_vec: + case INDEX_op_cmpsel_vec: + return 1; + default: + return 0; + } +} + static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) { switch (op) { @@ -1633,6 +2613,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 +2639,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 +2664,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,48 +2671,95 @@ 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); + case INDEX_op_st_vec: + return C_O0_I2(v, r); + case INDEX_op_dup_vec: + case INDEX_op_dupm_vec: + case INDEX_op_ld_vec: + return C_O1_I1(v, r); + case INDEX_op_neg_vec: + case INDEX_op_not_vec: + case INDEX_op_shli_vec: + case INDEX_op_shri_vec: + case INDEX_op_sari_vec: + case INDEX_op_rotli_vec: + return C_O1_I1(v, v); + case INDEX_op_add_vec: + case INDEX_op_and_vec: + case INDEX_op_or_vec: + case INDEX_op_xor_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_usadd_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: + return C_O1_I2(v, v, vK); + case INDEX_op_sub_vec: + return C_O1_I2(v, vK, v); + case INDEX_op_mul_vec: + case INDEX_op_shlv_vec: + case INDEX_op_shrv_vec: + case INDEX_op_sarv_vec: + case INDEX_op_rotlv_vec: + case INDEX_op_rotrv_vec: + return C_O1_I2(v, v, v); + case INDEX_op_shls_vec: + case INDEX_op_shrs_vec: + case INDEX_op_sars_vec: + case INDEX_op_rotls_vec: + return C_O1_I2(v, v, r); + case INDEX_op_cmp_vec: + return C_O1_I2(v, v, vL); + case INDEX_op_cmpsel_vec: + return C_O1_I4(v, v, vL, vK, vK); default: g_assert_not_reached(); } @@ -1762,10 +2807,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,14 +2831,75 @@ 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) +{ + init_setting_vtype(s); +} + +static bool vtype_check(unsigned vtype) +{ + unsigned long tmp; + + /* vsetvl tmp, zero, vtype */ + asm(".insn r 0x57, 7, 0x40, %0, zero, %1" : "=r"(tmp) : "r"(vtype)); + return tmp != 0; +} + +static void probe_frac_lmul_1(TCGType type, MemOp vsew) +{ + VsetCache *p = &riscv_vset_cache[type - TCG_TYPE_V64][vsew]; + unsigned avl = tcg_type_size(type) >> vsew; + int lmul = type - riscv_lg2_vlenb; + unsigned vtype = encode_vtype(true, true, vsew, lmul & 7); + bool lmul_eq_avl = true; + + /* Guaranteed by Zve64x. */ + assert(lmul < 3); + + /* + * For LMUL < -3, the host vector size is so large that TYPE + * is smaller than the minimum 1/8 fraction. + * + * For other fractional LMUL settings, implementations must + * support SEW settings between SEW_MIN and LMUL * ELEN, inclusive. + * So if ELEN = 64, LMUL = 1/2, then SEW will support e8, e16, e32, + * but e64 may not be supported. In other words, the hardware only + * guarantees SEW_MIN <= SEW <= LMUL * ELEN. Check. + */ + if (lmul < 0 && (lmul < -3 || !vtype_check(vtype))) { + vtype = encode_vtype(true, true, vsew, VLMUL_M1); + lmul_eq_avl = false; + } + + if (avl < 32) { + p->vset_insn = encode_vseti(OPC_VSETIVLI, TCG_REG_ZERO, avl, vtype); + } else if (lmul_eq_avl) { + /* rd != 0 and rs1 == 0 uses vlmax */ + p->vset_insn = encode_vset(OPC_VSETVLI, TCG_REG_TMP0, TCG_REG_ZERO, vtype); + } else { + p->movi_insn = encode_i(OPC_ADDI, TCG_REG_TMP0, TCG_REG_ZERO, avl); + p->vset_insn = encode_vset(OPC_VSETVLI, TCG_REG_ZERO, TCG_REG_TMP0, vtype); + } +} + +static void probe_frac_lmul(void) +{ + /* Match riscv_lg2_vlenb to TCG_TYPE_V64. */ + QEMU_BUILD_BUG_ON(TCG_TYPE_V64 != 3); + + for (TCGType t = TCG_TYPE_V64; t <= TCG_TYPE_V256; t++) { + for (MemOp e = MO_8; e <= MO_64; e++) { + probe_frac_lmul_1(t, e); + } + } +} + static void tcg_target_init(TCGContext *s) { 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_target_call_clobber_regs = -1; 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); @@ -1815,6 +2921,32 @@ 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_GP); tcg_regset_set_reg(s->reserved_regs, TCG_REG_TP); + + if (cpuinfo & CPUINFO_ZVE64X) { + switch (riscv_lg2_vlenb) { + case TCG_TYPE_V64: + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_DVECTOR_REG_GROUPS; + tcg_target_available_regs[TCG_TYPE_V256] = ALL_QVECTOR_REG_GROUPS; + s->reserved_regs |= (~ALL_QVECTOR_REG_GROUPS & ALL_VECTOR_REGS); + break; + case TCG_TYPE_V128: + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V256] = ALL_DVECTOR_REG_GROUPS; + s->reserved_regs |= (~ALL_DVECTOR_REG_GROUPS & ALL_VECTOR_REGS); + break; + default: + /* Guaranteed by Zve64x. */ + tcg_debug_assert(riscv_lg2_vlenb >= TCG_TYPE_V256); + tcg_target_available_regs[TCG_TYPE_V64] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V128] = ALL_VECTOR_REGS; + tcg_target_available_regs[TCG_TYPE_V256] = ALL_VECTOR_REGS; + break; + } + tcg_regset_set_reg(s->reserved_regs, TCG_REG_V0); + probe_frac_lmul(); + } } typedef struct { diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index 11c9b3e4f4..334c37cbe6 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -25,50 +25,31 @@ #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 +#include "host/cpuinfo.h" #define TCG_TARGET_INSN_UNIT_SIZE 4 -#define TCG_TARGET_TLB_DISPLACEMENT_BITS 20 -#define TCG_TARGET_NB_REGS 32 +#define TCG_TARGET_NB_REGS 64 #define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1) typedef enum { - TCG_REG_ZERO, - TCG_REG_RA, - TCG_REG_SP, - TCG_REG_GP, - TCG_REG_TP, - TCG_REG_T0, - TCG_REG_T1, - TCG_REG_T2, - TCG_REG_S0, - TCG_REG_S1, - TCG_REG_A0, - TCG_REG_A1, - TCG_REG_A2, - TCG_REG_A3, - TCG_REG_A4, - TCG_REG_A5, - TCG_REG_A6, - TCG_REG_A7, - TCG_REG_S2, - TCG_REG_S3, - TCG_REG_S4, - TCG_REG_S5, - TCG_REG_S6, - TCG_REG_S7, - TCG_REG_S8, - TCG_REG_S9, - TCG_REG_S10, - TCG_REG_S11, - TCG_REG_T3, - TCG_REG_T4, - TCG_REG_T5, - TCG_REG_T6, + TCG_REG_ZERO, TCG_REG_RA, TCG_REG_SP, TCG_REG_GP, + TCG_REG_TP, TCG_REG_T0, TCG_REG_T1, TCG_REG_T2, + TCG_REG_S0, TCG_REG_S1, TCG_REG_A0, TCG_REG_A1, + TCG_REG_A2, TCG_REG_A3, TCG_REG_A4, TCG_REG_A5, + TCG_REG_A6, TCG_REG_A7, TCG_REG_S2, TCG_REG_S3, + TCG_REG_S4, TCG_REG_S5, TCG_REG_S6, TCG_REG_S7, + TCG_REG_S8, TCG_REG_S9, TCG_REG_S10, TCG_REG_S11, + TCG_REG_T3, TCG_REG_T4, TCG_REG_T5, TCG_REG_T6, + + /* RISC-V V Extension 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, + 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, @@ -81,15 +62,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 +#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 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 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_deposit_i32 0 #define TCG_TARGET_HAS_extract_i32 0 #define TCG_TARGET_HAS_sextract_i32 0 @@ -98,76 +82,95 @@ 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 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_orc_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_orc_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_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 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) +#define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_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 + +/* vector instructions */ +#define TCG_TARGET_HAS_v64 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_v128 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_v256 (cpuinfo & CPUINFO_ZVE64X) +#define TCG_TARGET_HAS_andc_vec 0 +#define TCG_TARGET_HAS_orc_vec 0 +#define TCG_TARGET_HAS_nand_vec 0 +#define TCG_TARGET_HAS_nor_vec 0 +#define TCG_TARGET_HAS_eqv_vec 0 +#define TCG_TARGET_HAS_not_vec 1 +#define TCG_TARGET_HAS_neg_vec 1 +#define TCG_TARGET_HAS_abs_vec 0 +#define TCG_TARGET_HAS_roti_vec 1 +#define TCG_TARGET_HAS_rots_vec 1 +#define TCG_TARGET_HAS_rotv_vec 1 +#define TCG_TARGET_HAS_shi_vec 1 +#define TCG_TARGET_HAS_shs_vec 1 +#define TCG_TARGET_HAS_shv_vec 1 +#define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 +#define TCG_TARGET_HAS_bitsel_vec 0 +#define TCG_TARGET_HAS_cmpsel_vec 1 + +#define TCG_TARGET_HAS_tst_vec 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/riscv/tcg-target.opc.h b/tcg/riscv/tcg-target.opc.h new file mode 100644 index 0000000000..b80b39e1e5 --- /dev/null +++ b/tcg/riscv/tcg-target.opc.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) C-SKY Microsystems Co., Ltd. + * + * 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/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 426dd92e51..370e4b1295 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,26 @@ 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(v, v, v, vZ, v) +C_O1_I4(v, v, v, vZM, v) +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..3e574e0662 100644 --- a/tcg/s390x/tcg-target-con-str.h +++ b/tcg/s390x/tcg-target-con-str.h @@ -9,21 +9,19 @@ * 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('M', TCG_CT_CONST_M1) +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..27bccc14e5 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -24,56 +24,32 @@ * 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 TCG_CT_CONST_M1 (1 << 16) #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 /* A scratch register that may be be used throughout the backend. */ #define TCG_TMP0 TCG_REG_R1 +#define TCG_VEC_TMP0 TCG_REG_V31 -/* 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 +114,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 +166,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 +242,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 +253,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 +392,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 +409,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 +431,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 +446,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 +496,140 @@ 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) { + if (is_tst_cond(cond)) { + if (is_const_p16(uval) >= 0) { + return true; /* TMxx */ + } + if (risbg_mask(uval)) { + return true; /* RISBG */ + } + return false; + } + + if (type == TCG_TYPE_I32) { + return true; + } + + 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: + /* checked above, fallthru */ + 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_M1) && val == -1) { + 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 +645,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 +670,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 +739,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 +794,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 +818,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 +890,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 +919,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 +1092,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 +1185,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 +1300,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 +1311,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 +1353,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 +1408,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 +1435,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 +1518,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 +1586,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 +1597,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 +1607,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 +1618,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 +1627,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 +1666,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 +1677,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 +1834,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 +2119,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 +2172,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 +2190,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 +2202,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 +2347,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 +2373,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 +2406,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 +2471,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 +2496,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 +2506,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 +2657,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 +2688,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 +2726,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(); } } @@ -2755,6 +2854,94 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out_insn(s, VRX, VLREP, dst, TCG_TMP0, TCG_REG_NONE, 0, MO_64); } +static bool tcg_out_cmp_vec_noinv(TCGContext *s, unsigned vece, TCGReg a0, + TCGReg a1, TCGReg a2, TCGCond cond) +{ + bool need_swap = false, need_inv = false; + + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_GT: + case TCG_COND_GTU: + break; + case TCG_COND_NE: + case TCG_COND_LE: + case TCG_COND_LEU: + need_inv = true; + break; + case TCG_COND_LT: + case TCG_COND_LTU: + need_swap = true; + break; + case TCG_COND_GE: + case TCG_COND_GEU: + need_swap = need_inv = true; + break; + default: + g_assert_not_reached(); + } + + if (need_inv) { + cond = tcg_invert_cond(cond); + } + if (need_swap) { + TCGReg swap = a1; + a1 = a2; + a2 = swap; + cond = tcg_swap_cond(cond); + } + + switch (cond) { + case TCG_COND_EQ: + tcg_out_insn(s, VRRc, VCEQ, a0, a1, a2, vece); + break; + case TCG_COND_GT: + tcg_out_insn(s, VRRc, VCH, a0, a1, a2, vece); + break; + case TCG_COND_GTU: + tcg_out_insn(s, VRRc, VCHL, a0, a1, a2, vece); + break; + default: + g_assert_not_reached(); + } + return need_inv; +} + +static void tcg_out_cmp_vec(TCGContext *s, unsigned vece, TCGReg a0, + TCGReg a1, TCGReg a2, TCGCond cond) +{ + if (tcg_out_cmp_vec_noinv(s, vece, a0, a1, a2, cond)) { + tcg_out_insn(s, VRRc, VNO, a0, a0, a0, 0); + } +} + +static void tcg_out_cmpsel_vec(TCGContext *s, unsigned vece, TCGReg a0, + TCGReg c1, TCGReg c2, TCGArg v3, + int const_v3, TCGReg v4, TCGCond cond) +{ + bool inv = tcg_out_cmp_vec_noinv(s, vece, TCG_VEC_TMP0, c1, c2, cond); + + if (!const_v3) { + if (inv) { + tcg_out_insn(s, VRRe, VSEL, a0, v4, v3, TCG_VEC_TMP0); + } else { + tcg_out_insn(s, VRRe, VSEL, a0, v3, v4, TCG_VEC_TMP0); + } + } else if (v3) { + if (inv) { + tcg_out_insn(s, VRRc, VOC, a0, v4, TCG_VEC_TMP0, 0); + } else { + tcg_out_insn(s, VRRc, VO, a0, v4, TCG_VEC_TMP0, 0); + } + } else { + if (inv) { + tcg_out_insn(s, VRRc, VN, a0, v4, TCG_VEC_TMP0, 0); + } else { + tcg_out_insn(s, VRRc, VNC, a0, v4, TCG_VEC_TMP0, 0); + } + } +} + static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2873,19 +3060,11 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_cmp_vec: - switch ((TCGCond)args[3]) { - case TCG_COND_EQ: - tcg_out_insn(s, VRRc, VCEQ, a0, a1, a2, vece); - break; - case TCG_COND_GT: - tcg_out_insn(s, VRRc, VCH, a0, a1, a2, vece); - break; - case TCG_COND_GTU: - tcg_out_insn(s, VRRc, VCHL, a0, a1, a2, vece); - break; - default: - g_assert_not_reached(); - } + tcg_out_cmp_vec(s, vece, a0, a1, a2, args[3]); + break; + case INDEX_op_cmpsel_vec: + tcg_out_cmpsel_vec(s, vece, a0, a1, a2, args[3], const_args[3], + args[4], args[5]); break; case INDEX_op_s390_vuph_vec: @@ -2938,9 +3117,9 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_umax_vec: case INDEX_op_umin_vec: case INDEX_op_xor_vec: - return 1; case INDEX_op_cmp_vec: case INDEX_op_cmpsel_vec: + return 1; case INDEX_op_rotrv_vec: return -1; case INDEX_op_mul_vec: @@ -2953,71 +3132,6 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } } -static bool expand_vec_cmp_noinv(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec v1, TCGv_vec v2, TCGCond cond) -{ - bool need_swap = false, need_inv = false; - - switch (cond) { - case TCG_COND_EQ: - case TCG_COND_GT: - case TCG_COND_GTU: - break; - case TCG_COND_NE: - case TCG_COND_LE: - case TCG_COND_LEU: - need_inv = true; - break; - case TCG_COND_LT: - case TCG_COND_LTU: - need_swap = true; - break; - case TCG_COND_GE: - case TCG_COND_GEU: - need_swap = need_inv = true; - break; - default: - g_assert_not_reached(); - } - - if (need_inv) { - cond = tcg_invert_cond(cond); - } - if (need_swap) { - TCGv_vec t1; - t1 = v1, v1 = v2, v2 = t1; - cond = tcg_swap_cond(cond); - } - - vec_gen_4(INDEX_op_cmp_vec, type, vece, tcgv_vec_arg(v0), - tcgv_vec_arg(v1), tcgv_vec_arg(v2), cond); - - return need_inv; -} - -static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec v1, TCGv_vec v2, TCGCond cond) -{ - if (expand_vec_cmp_noinv(type, vece, v0, v1, v2, cond)) { - tcg_gen_not_vec(vece, v0, v0); - } -} - -static void expand_vec_cmpsel(TCGType type, unsigned vece, TCGv_vec v0, - TCGv_vec c1, TCGv_vec c2, - TCGv_vec v3, TCGv_vec v4, TCGCond cond) -{ - TCGv_vec t = tcg_temp_new_vec(type); - - if (expand_vec_cmp_noinv(type, vece, t, c1, c2, cond)) { - /* Invert the sense of the compare by swapping arguments. */ - tcg_gen_bitsel_vec(vece, v0, t, v4, v3); - } else { - tcg_gen_bitsel_vec(vece, v0, t, v3, v4); - } - tcg_temp_free_vec(t); -} - static void expand_vec_sat(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGv_vec v2, TCGOpcode add_sub_opc) { @@ -3059,7 +3173,7 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { va_list va; - TCGv_vec v0, v1, v2, v3, v4, t0; + TCGv_vec v0, v1, v2, t0; va_start(va, a0); v0 = temp_tcgv_vec(arg_temp(a0)); @@ -3067,16 +3181,6 @@ void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, v2 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); switch (opc) { - case INDEX_op_cmp_vec: - expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); - break; - - case INDEX_op_cmpsel_vec: - v3 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); - v4 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); - expand_vec_cmpsel(type, vece, v0, v1, v2, v3, v4, va_arg(va, TCGArg)); - break; - case INDEX_op_rotrv_vec: t0 = tcg_temp_new_vec(type); tcg_gen_neg_vec(vece, t0, v2); @@ -3135,46 +3239,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: return C_O1_I2(r, r, ri); + case INDEX_op_setcond_i32: + case INDEX_op_negsetcond_i32: + 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 +3303,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 +3319,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); @@ -3284,6 +3415,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) return C_O1_I2(v, v, r); case INDEX_op_bitsel_vec: return C_O1_I3(v, v, v, v); + case INDEX_op_cmpsel_vec: + return (TCG_TARGET_HAS_orc_vec + ? C_O1_I4(v, v, v, vZM, v) + : C_O1_I4(v, v, v, vZ, v)); default: g_assert_not_reached(); @@ -3301,6 +3436,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 +3458,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) @@ -3375,12 +3543,10 @@ static void tcg_target_init(TCGContext *s) s->reserved_regs = 0; tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); + tcg_regset_set_reg(s->reserved_regs, TCG_VEC_TMP0); /* 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 +3565,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 +3593,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..86aeca166f 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 @@ -161,26 +162,18 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_sat_vec 0 #define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_HAS_bitsel_vec 1 -#define TCG_TARGET_HAS_cmpsel_vec 0 +#define TCG_TARGET_HAS_cmpsel_vec 1 +#define TCG_TARGET_HAS_tst_vec 0 /* 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/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 31e6fea1fc..434bf25072 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -12,8 +12,6 @@ C_O0_I1(r) C_O0_I2(rZ, r) C_O0_I2(rZ, rJ) -C_O0_I2(sZ, s) -C_O1_I1(r, s) C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, rZ, rJ) diff --git a/tcg/sparc64/tcg-target-con-str.h b/tcg/sparc64/tcg-target-con-str.h index 8f5c7aef97..0577ec4942 100644 --- a/tcg/sparc64/tcg-target-con-str.h +++ b/tcg/sparc64/tcg-target-con-str.h @@ -9,7 +9,6 @@ * REGS(letter, register_mask) */ REGS('r', ALL_GENERAL_REGS) -REGS('s', 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/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index cb9453efdd..32f9ec24b5 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -27,6 +27,7 @@ #error "unsupported code generation mode" #endif +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #ifdef CONFIG_DEBUG_TCG @@ -70,29 +71,18 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { #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) -#define ALL_QLDST_REGS (ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) +#define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) -/* 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, @@ -111,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, @@ -133,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) @@ -333,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; @@ -400,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); } } @@ -426,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); @@ -449,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; } @@ -468,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)); @@ -478,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); } @@ -493,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, @@ -537,17 +587,6 @@ 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)); @@ -568,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, @@ -581,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, @@ -607,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); } @@ -632,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); } @@ -640,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) { @@ -649,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))); } @@ -672,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) { @@ -704,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 @@ -720,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) { @@ -746,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: @@ -756,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); } } @@ -798,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); @@ -820,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); @@ -839,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); } @@ -859,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); @@ -871,127 +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: - 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; - - 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); - - /* Set the retaddr operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O3, 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); - - emit_extend(s, TCG_REG_O2, i); - - /* Set the retaddr operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O4, 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); - } - - /* 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) { @@ -1025,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 */ @@ -1038,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) @@ -1051,380 +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 (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; - 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; - tcg_out_mov(s, TCG_TYPE_REG, TCG_REG_O1, 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, TCG_REG_O2, oi); + tcg_out_cmp(s, TCG_COND_NE, TCG_REG_T2, TCG_REG_T3, 0); - /* 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); - } + 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 (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 != memop_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. - */ - tcg_out_call_nodelay(s, qemu_unalign_ld_trampoline, false); - /* delay slot -- move to low part of argument reg */ - tcg_out_mov_delay(s, TCG_REG_O1, 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; - 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); - tcg_out_mov(s, TCG_TYPE_REG, TCG_REG_O1, addrz); - tcg_out_mov(s, TCG_TYPE_REG, TCG_REG_O2, data); + tcg_out_ldst_rr(s, data, h.base, h.index, + st_opc[get_memop(oi) & (MO_BSWAP | MO_SIZE)]); - 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, TCG_REG_O3, 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 (TARGET_LONG_BITS == 32) { - tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); - addr = TCG_REG_T1; + if (ldst) { + ldst->type = data_type; + ldst->datalo_reg = data; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } +} - /* - * 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. - */ - tcg_out_call_nodelay(s, qemu_unalign_st_trampoline, false); - /* delay slot -- move to low part of argument reg */ - tcg_out_mov_delay(s, TCG_REG_O1, 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, @@ -1441,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)); @@ -1596,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]); @@ -1623,15 +1419,21 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, 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: @@ -1664,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]); @@ -1715,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(); } } @@ -1746,8 +1551,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extrl_i64_i32: - case INDEX_op_extrh_i64_i32: + 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: @@ -1757,6 +1564,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) 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: @@ -1787,6 +1598,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) 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: @@ -1806,13 +1619,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op) case INDEX_op_muluh_i64: return C_O1_I2(r, r, r); - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: - return C_O1_I1(r, s); - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - return C_O0_I2(sZ, s); - default: g_assert_not_reached(); } @@ -1860,6 +1666,7 @@ 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 */ } #define ELF_HOST_MACHINE EM_SPARCV9 @@ -1894,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/sparc64/tcg-target.h b/tcg/sparc64/tcg-target.h index 8655acdbe5..a18906a14e 100644 --- a/tcg/sparc64/tcg-target.h +++ b/tcg/sparc64/tcg-target.h @@ -26,7 +26,6 @@ #define SPARC_TCG_TARGET_H #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) @@ -71,7 +70,10 @@ typedef enum { #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) -#define TCG_TARGET_EXTEND_ARGS 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_NORMAL +#define TCG_TARGET_CALL_RET_I128 TCG_CALL_RET_NORMAL #if defined(__VIS__) && __VIS__ >= 0x300 #define use_vis3_instructions 1 @@ -89,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 @@ -103,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 @@ -127,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 @@ -141,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 @@ -149,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..8099248076 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,55 @@ 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); + +TCGOp *tcg_gen_op1(TCGOpcode, TCGArg); +TCGOp *tcg_gen_op2(TCGOpcode, TCGArg, TCGArg); +TCGOp *tcg_gen_op3(TCGOpcode, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op4(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *tcg_gen_op5(TCGOpcode, TCGArg, TCGArg, TCGArg, TCGArg, TCGArg); +TCGOp *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); +void vec_gen_6(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, + TCGArg a, TCGArg b, TCGArg c, TCGArg d, TCGArg e); + #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-fp.c b/tcg/tcg-op-fp.c new file mode 100644 index 0000000000..2abbbd295d --- /dev/null +++ b/tcg/tcg-op-fp.c @@ -0,0 +1,227 @@ +#include "qemu/osdep.h" +#include "tcg/tcg.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" + +static void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 a1) +{ + tcg_gen_op1(opc, tcgv_i32_arg(a1)); +} + +void tcg_gen_flcr(TCGv_i32 arg) +{ + tcg_gen_op1_i32(INDEX_op_flcr, arg); +} + +void tcg_gen_st80f_f32(TCGv_f32 arg, TCGv_ptr dst) +{ + tcg_gen_op2(INDEX_op_st80f_f32, tcgv_f32_arg(arg), tcgv_ptr_arg(dst)); +} + +void tcg_gen_st80f_f64(TCGv_f64 arg, TCGv_ptr dst) +{ + tcg_gen_op2(INDEX_op_st80f_f64, tcgv_f64_arg(arg), tcgv_ptr_arg(dst)); +} + +void tcg_gen_ld80f_f32(TCGv_f32 ret, TCGv_ptr src) +{ + tcg_gen_op2(INDEX_op_ld80f_f32, tcgv_f32_arg(ret), tcgv_ptr_arg(src)); +} + +void tcg_gen_ld80f_f64(TCGv_f64 ret, TCGv_ptr src) +{ + tcg_gen_op2(INDEX_op_ld80f_f64, tcgv_f64_arg(ret), tcgv_ptr_arg(src)); +} + +void tcg_gen_abs_f32(TCGv_f32 ret, TCGv_f32 src) +{ + tcg_gen_op2(INDEX_op_abs_f32, tcgv_f32_arg(ret), tcgv_f32_arg(src)); +} + +void tcg_gen_abs_f64(TCGv_f64 ret, TCGv_f64 src) +{ + tcg_gen_op2(INDEX_op_abs_f64, tcgv_f64_arg(ret), tcgv_f64_arg(src)); +} + +void tcg_gen_add_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) +{ + tcg_gen_op3(INDEX_op_add_f32, + tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); +} + +void tcg_gen_add_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) +{ + tcg_gen_op3(INDEX_op_add_f64, + tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); +} + +void tcg_gen_chs_f32(TCGv_f32 ret, TCGv_f32 src) +{ + tcg_gen_op2(INDEX_op_chs_f32, tcgv_f32_arg(ret), tcgv_f32_arg(src)); +} + +void tcg_gen_chs_f64(TCGv_f64 ret, TCGv_f64 src) +{ + tcg_gen_op2(INDEX_op_chs_f64, tcgv_f64_arg(ret), tcgv_f64_arg(src)); +} + +void tcg_gen_com_f32(TCGv_i64 ret, TCGv_f32 arg1, TCGv_f32 arg2) +{ + tcg_gen_op3(INDEX_op_com_f32, + tcgv_i64_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); +} + +void tcg_gen_com_f64(TCGv_i64 ret, TCGv_f64 arg1, TCGv_f64 arg2) +{ + tcg_gen_op3(INDEX_op_com_f64, + tcgv_i64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); +} + +void tcg_gen_cos_f32(TCGv_f32 ret, TCGv_f32 arg) +{ + tcg_gen_op2(INDEX_op_cos_f32, tcgv_f32_arg(ret), tcgv_f32_arg(arg)); +} + +void tcg_gen_cos_f64(TCGv_f64 ret, TCGv_f64 arg) +{ + tcg_gen_op2(INDEX_op_cos_f64, tcgv_f64_arg(ret), tcgv_f64_arg(arg)); +} + +void tcg_gen_cvt32f_f64(TCGv_f64 ret, TCGv_f32 arg) +{ + tcg_gen_op2(INDEX_op_cvt32f_f64, tcgv_f64_arg(ret), tcgv_f32_arg(arg)); +} + +void tcg_gen_cvt32f_i32(TCGv_i32 ret, TCGv_f32 arg) +{ + tcg_gen_op2(INDEX_op_cvt32f_i32, tcgv_i32_arg(ret), tcgv_f32_arg(arg)); +} + +void tcg_gen_cvt32f_i64(TCGv_i64 ret, TCGv_f32 arg) +{ + tcg_gen_op2(INDEX_op_cvt32f_i64, tcgv_i64_arg(ret), tcgv_f32_arg(arg)); +} + +void tcg_gen_cvt32i_f32(TCGv_f32 ret, TCGv_i32 arg) +{ + tcg_gen_op2(INDEX_op_cvt32i_f32, tcgv_f32_arg(ret), tcgv_i32_arg(arg)); +} + +void tcg_gen_cvt32i_f64(TCGv_f64 ret, TCGv_i32 arg) +{ + tcg_gen_op2(INDEX_op_cvt32i_f64, tcgv_f64_arg(ret), tcgv_i32_arg(arg)); +} + +void tcg_gen_cvt64f_f32(TCGv_f32 ret, TCGv_f64 arg) +{ + tcg_gen_op2(INDEX_op_cvt64f_f32, tcgv_f32_arg(ret), tcgv_f64_arg(arg)); +} + +void tcg_gen_cvt64f_i32(TCGv_i32 ret, TCGv_f64 src) +{ + tcg_gen_op2(INDEX_op_cvt64f_i32, tcgv_i32_arg(ret), tcgv_f64_arg(src)); +} + +void tcg_gen_cvt64f_i64(TCGv_i64 ret, TCGv_f64 src) +{ + tcg_gen_op2(INDEX_op_cvt64f_i64, tcgv_i64_arg(ret), tcgv_f64_arg(src)); +} + +void tcg_gen_cvt64i_f32(TCGv_f32 ret, TCGv_i64 arg) +{ + tcg_gen_op2(INDEX_op_cvt64i_f32, tcgv_f32_arg(ret), tcgv_i64_arg(arg)); +} + +void tcg_gen_cvt64i_f64(TCGv_f64 ret, TCGv_i64 arg) +{ + tcg_gen_op2(INDEX_op_cvt64i_f64, tcgv_f64_arg(ret), tcgv_i64_arg(arg)); +} + +void tcg_gen_div_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) +{ + tcg_gen_op3(INDEX_op_div_f32, + tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); +} + +void tcg_gen_div_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) +{ + tcg_gen_op3(INDEX_op_div_f64, + tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); +} + +void tcg_gen_mov32f_i32(TCGv_i32 ret, TCGv_f32 src) +{ + tcg_gen_op2(INDEX_op_mov32f_i32, tcgv_i32_arg(ret), tcgv_f32_arg(src)); +} + +void tcg_gen_mov32i_f32(TCGv_f32 ret, TCGv_i32 arg) +{ + tcg_gen_op2(INDEX_op_mov32i_f32, tcgv_f32_arg(ret), tcgv_i32_arg(arg)); +} + +void tcg_gen_mov64f_i64(TCGv_i64 ret, TCGv_f64 src) +{ + tcg_gen_op2(INDEX_op_mov64f_i64, tcgv_i64_arg(ret), tcgv_f64_arg(src)); +} + +void tcg_gen_mov64i_f64(TCGv_f64 ret, TCGv_i64 arg) +{ + tcg_gen_op2(INDEX_op_mov64i_f64, tcgv_f64_arg(ret), tcgv_i64_arg(arg)); +} + +void tcg_gen_mov_f32(TCGv_f32 ret, TCGv_f32 src) +{ + tcg_gen_op2(INDEX_op_mov_f32, tcgv_f32_arg(ret), tcgv_f32_arg(src)); +} + +void tcg_gen_mov_f64(TCGv_f64 ret, TCGv_f64 src) +{ + tcg_gen_op2(INDEX_op_mov_f64, tcgv_f64_arg(ret), tcgv_f64_arg(src)); +} + +void tcg_gen_mul_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) +{ + tcg_gen_op3(INDEX_op_mul_f32, + tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); +} + +void tcg_gen_mul_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) +{ + tcg_gen_op3(INDEX_op_mul_f64, + tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); +} + +void tcg_gen_sin_f32(TCGv_f32 ret, TCGv_f32 arg) +{ + tcg_gen_op2(INDEX_op_sin_f32, tcgv_f32_arg(ret), tcgv_f32_arg(arg)); +} + +void tcg_gen_sin_f64(TCGv_f64 ret, TCGv_f64 arg) +{ + tcg_gen_op2(INDEX_op_sin_f64, tcgv_f64_arg(ret), tcgv_f64_arg(arg)); +} + +void tcg_gen_sqrt_f32(TCGv_f32 ret, TCGv_f32 arg) +{ + tcg_gen_op2(INDEX_op_sqrt_f32, tcgv_f32_arg(ret), tcgv_f32_arg(arg)); +} + +void tcg_gen_sqrt_f64(TCGv_f64 ret, TCGv_f64 arg) +{ + tcg_gen_op2(INDEX_op_sqrt_f64, tcgv_f64_arg(ret), tcgv_f64_arg(arg)); +} + +void tcg_gen_sub_f32(TCGv_f32 ret, TCGv_f32 arg1, TCGv_f32 arg2) +{ + tcg_gen_op3(INDEX_op_sub_f32, + tcgv_f32_arg(ret), tcgv_f32_arg(arg1), tcgv_f32_arg(arg2)); +} + +void tcg_gen_sub_f64(TCGv_f64 ret, TCGv_f64 arg1, TCGv_f64 arg2) +{ + tcg_gen_op3(INDEX_op_sub_f64, + tcgv_f64_arg(ret), tcgv_f64_arg(arg1), tcgv_f64_arg(arg2)); +} diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 079a761b04..97e4df221a 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 @@ -88,7 +88,20 @@ uint32_t simd_desc(uint32_t oprsz, uint32_t maxsz, int32_t data) uint32_t desc = 0; check_size_align(oprsz, maxsz, 0); - tcg_debug_assert(data == sextract32(data, 0, SIMD_DATA_BITS)); + + /* + * We want to check that 'data' will fit into SIMD_DATA_BITS. + * However, some callers want to treat the data as a signed + * value (which they can later get back with simd_data()) + * and some want to treat it as an unsigned value. + * So here we assert only that the data will fit into the + * field in at least one way. This means that some invalid + * values from the caller will not be detected, e.g. if the + * caller wants to handle the value as a signed integer but + * incorrectly passes us 1 << (SIMD_DATA_BITS - 1). + */ + tcg_debug_assert(data == sextract32(data, 0, SIMD_DATA_BITS) || + data == extract32(data, 0, SIMD_DATA_BITS)); oprsz = (oprsz / 8) - 1; maxsz = (maxsz / 8) - 1; @@ -117,11 +130,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 +151,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 +171,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 +194,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 +220,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 +250,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 +271,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 +296,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 +324,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 +495,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 +507,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 +574,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 +588,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 +617,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 +632,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 +642,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 +658,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 +682,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 +721,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 +741,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 +761,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 +784,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); @@ -787,7 +798,8 @@ static void expand_3_i32(uint32_t dofs, uint32_t aofs, } static void expand_3i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, int32_t c, bool load_dest, + uint32_t oprsz, int32_t c, + bool load_dest, bool write_aofs, void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32, int32_t)) { TCGv_i32 t0 = tcg_temp_new_i32(); @@ -796,13 +808,16 @@ 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); + if (write_aofs) { + tcg_gen_st_i32(t0, tcg_env, aofs + i); + } } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -821,13 +836,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 +863,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 +884,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 +904,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 +924,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 +947,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); @@ -946,7 +961,8 @@ static void expand_3_i64(uint32_t dofs, uint32_t aofs, } static void expand_3i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, int64_t c, bool load_dest, + uint32_t oprsz, int64_t c, + bool load_dest, bool write_aofs, void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64, int64_t)) { TCGv_i64 t0 = tcg_temp_new_i64(); @@ -955,13 +971,16 @@ 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); + if (write_aofs) { + tcg_gen_st_i64(t0, tcg_env, aofs + i); + } } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -980,13 +999,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 +1026,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 +1044,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 +1064,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 +1082,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 +1102,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); } /* @@ -1117,27 +1123,27 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, */ static void expand_3i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t tysz, - TCGType type, int64_t c, bool load_dest, + TCGType type, int64_t c, + bool load_dest, bool write_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); + if (write_aofs) { + tcg_gen_st_vec(t0, tcg_env, aofs + 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 +1153,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 +1180,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. */ @@ -1500,7 +1496,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, */ some = QEMU_ALIGN_DOWN(oprsz, 32); expand_3i_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, - c, g->load_dest, g->fniv); + c, g->load_dest, g->write_aofs, g->fniv); if (some == oprsz) { break; } @@ -1512,18 +1508,20 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, /* fallthru */ case TCG_TYPE_V128: expand_3i_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, - c, g->load_dest, g->fniv); + c, g->load_dest, g->write_aofs, g->fniv); break; case TCG_TYPE_V64: expand_3i_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, - c, g->load_dest, g->fniv); + c, g->load_dest, g->write_aofs, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_3i_i64(dofs, aofs, bofs, oprsz, c, g->load_dest, g->fni8); + expand_3i_i64(dofs, aofs, bofs, oprsz, c, + g->load_dest, g->write_aofs, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_3i_i32(dofs, aofs, bofs, oprsz, c, g->load_dest, g->fni4); + expand_3i_i32(dofs, aofs, bofs, oprsz, c, + g->load_dest, g->write_aofs, g->fni4); } else { assert(g->fno != NULL); tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, maxsz, c, g->fno); @@ -1731,27 +1729,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 +1760,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 +1789,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 +1853,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 +1878,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 +1902,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 +1916,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 +2036,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 +2061,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 +2085,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 +2099,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 +2461,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 +2487,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 +2533,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 +2742,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 +2755,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 +2783,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 +2807,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 +2954,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 +2968,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 +2982,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 +2996,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 +3130,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 +3189,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 +3230,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 +3346,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 +3380,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 +3389,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 +3443,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 +3452,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 +3506,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 +3515,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 +3569,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 +3578,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 +3628,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 +3637,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 +3678,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 +3695,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 +3713,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 +3837,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 += 4) { + 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..a318011229 --- /dev/null +++ b/tcg/tcg-op-ldst.c @@ -0,0 +1,1320 @@ +/* + * 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 = memop_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; +} + +#ifdef CONFIG_PLUGIN +static void +plugin_gen_mem_callbacks(TCGv_i64 copy_addr, TCGTemp *orig_addr, MemOpIdx oi, + enum qemu_plugin_mem_rw rw) +{ + 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)); + } + tcg_gen_plugin_mem_cb(copy_addr, info); + tcg_temp_free_i64(copy_addr); + } else { + if (copy_addr) { + tcg_gen_plugin_mem_cb(copy_addr, info); + tcg_temp_free_i64(copy_addr); + } else { + tcg_gen_plugin_mem_cb(temp_tcgv_i64(orig_addr), info); + } + } + } +} +#endif + +static void +plugin_gen_mem_callbacks_i32(TCGv_i32 val, + TCGv_i64 copy_addr, TCGTemp *orig_addr, + MemOpIdx oi, enum qemu_plugin_mem_rw rw) +{ +#ifdef CONFIG_PLUGIN + if (tcg_ctx->plugin_insn != NULL) { + tcg_gen_st_i32(val, tcg_env, + offsetof(CPUState, neg.plugin_mem_value_low) - + sizeof(CPUState) + (HOST_BIG_ENDIAN * 4)); + plugin_gen_mem_callbacks(copy_addr, orig_addr, oi, rw); + } +#endif +} + +static void +plugin_gen_mem_callbacks_i64(TCGv_i64 val, + TCGv_i64 copy_addr, TCGTemp *orig_addr, + MemOpIdx oi, enum qemu_plugin_mem_rw rw) +{ +#ifdef CONFIG_PLUGIN + if (tcg_ctx->plugin_insn != NULL) { + tcg_gen_st_i64(val, tcg_env, + offsetof(CPUState, neg.plugin_mem_value_low) - + sizeof(CPUState)); + plugin_gen_mem_callbacks(copy_addr, orig_addr, oi, rw); + } +#endif +} + +static void +plugin_gen_mem_callbacks_i128(TCGv_i128 val, + TCGv_i64 copy_addr, TCGTemp *orig_addr, + MemOpIdx oi, enum qemu_plugin_mem_rw rw) +{ +#ifdef CONFIG_PLUGIN + if (tcg_ctx->plugin_insn != NULL) { + tcg_gen_st_i64(TCGV128_LOW(val), tcg_env, + offsetof(CPUState, neg.plugin_mem_value_low) - + sizeof(CPUState)); + tcg_gen_st_i64(TCGV128_HIGH(val), tcg_env, + offsetof(CPUState, neg.plugin_mem_value_high) - + sizeof(CPUState)); + plugin_gen_mem_callbacks(copy_addr, orig_addr, oi, rw); + } +#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_i32(val, 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_i32(val, 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_i64(val, 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_i64(val, 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(memop_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_i128(val, 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(memop_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_i128(val, 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..d4bb4aee74 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; @@ -180,10 +172,10 @@ void vec_gen_4(TCGOpcode opc, TCGType type, unsigned vece, op->args[3] = c; } -static void vec_gen_6(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, - TCGArg a, TCGArg b, TCGArg c, TCGArg d, TCGArg e) +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); } @@ -547,9 +508,11 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGTemp *rt = tcgv_vec_temp(r); TCGTemp *at = tcgv_vec_temp(a); TCGTemp *bt = tcgv_vec_temp(b); + TCGTemp *tt = NULL; TCGArg ri = temp_arg(rt); TCGArg ai = temp_arg(at); TCGArg bi = temp_arg(bt); + TCGArg ti; TCGType type = rt->base_type; int can; @@ -557,6 +520,18 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, tcg_debug_assert(bt->base_type >= type); tcg_assert_listed_vecop(INDEX_op_cmp_vec); can = tcg_can_emit_vec_op(INDEX_op_cmp_vec, type, vece); + + if (!TCG_TARGET_HAS_tst_vec && is_tst_cond(cond)) { + tt = tcg_temp_new_internal(type, TEMP_EBB); + ti = temp_arg(tt); + vec_gen_3(INDEX_op_and_vec, type, 0, ti, ai, bi); + at = tt; + ai = ti; + bt = tcg_constant_internal(type, 0); + bi = temp_arg(bt); + cond = tcg_tst_eqne_cond(cond); + } + if (can > 0) { vec_gen_4(INDEX_op_cmp_vec, type, vece, ri, ai, bi, cond); } else { @@ -565,6 +540,10 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, tcg_expand_vec_op(INDEX_op_cmp_vec, type, vece, ri, ai, bi, cond); tcg_swap_vecop_list(hold_list); } + + if (tt) { + tcg_temp_free_internal(tt); + } } static bool do_op3(unsigned vece, TCGv_vec r, TCGv_vec a, diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e02f3afb96..4a7e705367 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -23,89 +23,335 @@ */ #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)) + +TCGOp * 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; + return op; } -void tcg_gen_op2(TCGOpcode opc, TCGArg a1, TCGArg a2) +TCGOp * 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; + return op; } -void tcg_gen_op3(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3) +TCGOp * 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; + return op; } -void tcg_gen_op4(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, TCGArg a4) +TCGOp * 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; + return op; } -void tcg_gen_op5(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5) +TCGOp * 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; op->args[3] = a4; op->args[4] = a5; + return op; } -void tcg_gen_op6(TCGOpcode opc, TCGArg a1, TCGArg a2, TCGArg a3, - TCGArg a4, TCGArg a5, TCGArg a6) +TCGOp * 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; op->args[3] = a4; op->args[4] = a5; op->args[5] = a6; + return op; +} + +/* + * 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 TCGOp * DNI tcg_gen_op1i(TCGOpcode opc, TCGArg a1) +{ + return 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 TCGOp * DNI tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGArg a3, TCGArg a4) +{ + return tcg_gen_op4(opc, tcgv_i32_arg(a1), tcgv_i32_arg(a2), a3, a4); +} + +static TCGOp * DNI tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, + TCGArg a3, TCGArg a4) +{ + return 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 TCGOp * DNI tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, + TCGv_i32 a3, TCGv_i32 a4, + TCGArg a5, TCGArg a6) +{ + return 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_as_label_use(TCGLabel *l, TCGOp *op) +{ + TCGLabelUse *u = tcg_malloc(sizeof(TCGLabelUse)); + + u->op = op; + QSIMPLEQ_INSERT_TAIL(&l->branches, u, next); +} + +void tcg_gen_br(TCGLabel *l) +{ + add_as_label_use(l, tcg_gen_op1(INDEX_op_br, label_arg(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(unsigned from) +{ + tcg_gen_op1(INDEX_op_plugin_cb, from); +} + +void tcg_gen_plugin_mem_cb(TCGv_i64 addr, unsigned meminfo) +{ + tcg_gen_op2(INDEX_op_plugin_mem_cb, tcgv_i64_arg(addr), meminfo); +} + /* 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 +362,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 +378,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 +419,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 +436,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 +454,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 +478,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 +493,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 +513,9 @@ 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)); + TCGOp *op = tcg_gen_op4ii_i32(INDEX_op_brcond_i32, + arg1, arg2, cond, label_arg(l)); + add_as_label_use(l, op); } } @@ -256,6 +546,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 +588,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 +602,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 +622,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 +636,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 +656,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 +698,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 +710,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 +735,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 +747,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 +772,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 +785,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 +801,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 +818,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 +839,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 +856,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 +898,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 +1125,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 +1139,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 +1150,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 +1167,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 +1183,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 +1207,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 +1231,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 +1245,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 +1258,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 +1307,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 +1323,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 +1380,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 +1413,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 +1421,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 +1703,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 +1725,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 +1741,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 +1754,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 +1883,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,15 +1934,16 @@ 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++; + TCGOp *op; if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), - TCGV_HIGH(arg1), TCGV_LOW(arg2), - TCGV_HIGH(arg2), cond, label_arg(l)); + op = tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), + TCGV_HIGH(arg1), TCGV_LOW(arg2), + TCGV_HIGH(arg2), cond, label_arg(l)); } else { - tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, - label_arg(l)); + op = tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, + label_arg(l)); } + add_as_label_use(l, op); } } @@ -1472,12 +1954,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)); + TCGOp *op = 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_as_label_use(l, op); } } @@ -1518,6 +2000,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 +2034,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 +2043,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 +2057,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 +2077,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 +2091,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 +2181,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 +2204,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 +2254,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 +2272,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 +2301,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 +2330,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 +2385,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 +2439,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 +2460,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 +2476,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 +2503,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 +2513,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 +2557,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 +2578,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 +2594,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 +2651,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 +2883,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 +2978,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 +2992,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 +3017,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 +3035,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 +3052,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 +3071,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 +3095,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 +3105,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 +3141,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 +3155,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 +3167,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 +3210,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 +3243,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) @@ -2741,7 +3305,6 @@ void tcg_gen_exit_tb(const TranslationBlock *tb, unsigned idx) * This requires coordination with targets that do not use * the translator_loop. */ - uintptr_t val = (uintptr_t)tcg_splitwx_to_rx((void *)tb) + idx; if (tb == NULL) { @@ -2757,14 +3320,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 @@ -2780,618 +3342,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 0d2854550a..3198600476 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 "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,22 +743,23 @@ 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" -static void alloc_tcg_plugin_context(TCGContext *s) -{ -#ifdef CONFIG_PLUGIN - s->plugin_tb = g_new0(struct qemu_plugin_tb, 1); - s->plugin_tb->insns = - g_ptr_array_new_with_free_func(qemu_plugin_insn_cleanup_fn); +#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 -} /* * All TCG threads except the parent (i.e. the one that called tcg_context_init @@ -448,12 +769,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) @@ -498,7 +820,6 @@ void tcg_register_thread(void) qatomic_set(&tcg_ctxs[n], s); if (n > 0) { - alloc_tcg_plugin_context(s); tcg_region_initial_alloc(s); } @@ -511,7 +832,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); @@ -532,10 +853,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; } @@ -559,25 +881,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); @@ -613,55 +1341,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 (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); - - 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); @@ -681,14 +1366,12 @@ static void tcg_context_init(unsigned max_cpus) indirect_reg_alloc_order[i] = tcg_target_reg_alloc_order[i]; } - alloc_tcg_plugin_context(s); - tcg_ctx = s; /* * 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; @@ -701,7 +1384,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) @@ -731,12 +1414,12 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) goto retry; } qatomic_set(&s->code_gen_ptr, next); - s->data_gen_ptr = NULL; 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; @@ -764,13 +1447,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) { @@ -802,7 +1485,6 @@ void tcg_prologue_init(TCGContext *s) qemu_log_unlock(logfile); } } -#endif #ifndef CONFIG_TCG_INTERPRETER /* @@ -822,7 +1504,7 @@ void tcg_func_start(TCGContext *s) s->nb_temps = s->nb_globals; /* No temps have been previously allocated for size or locality. */ - memset(s->free_temps, 0, sizeof(s->free_temps)); + tcg_temp_ebb_reset_freed(s); /* No constant temps have been previously allocated. */ for (int i = 0; i < TCG_TYPE_COUNT; ++i) { @@ -841,7 +1523,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) @@ -872,9 +1560,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; @@ -895,16 +1581,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: @@ -930,7 +1613,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); @@ -941,7 +1624,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); @@ -957,52 +1641,154 @@ 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_f32 tcg_global_mem_new_f32(TCGv_ptr reg, intptr_t offset, const char *name) +{ + TCGTemp *t = tcg_global_mem_new_internal(reg, offset, name, TCG_TYPE_F32); + return temp_tcgv_f32(t); +} + +TCGv_f64 tcg_global_mem_new_f64(TCGv_ptr reg, intptr_t offset, const char *name) +{ + TCGTemp *t = tcg_global_mem_new_internal(reg, offset, name, TCG_TYPE_F64); + return temp_tcgv_f64(t); +} + +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_F32: + case TCG_TYPE_F64: + 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_f32 tcg_temp_new_f32(void) +{ + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_F32, false); + return temp_tcgv_f32(t); +} + +TCGv_f64 tcg_temp_new_f64(void) +{ + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_F64, false); + return temp_tcgv_f64(t); +} + +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; @@ -1023,7 +1809,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); } @@ -1034,42 +1820,63 @@ 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_f32(TCGv_f32 arg) +{ + tcg_temp_free_internal(tcgv_f32_temp(arg)); +} + +void tcg_temp_free_f64(TCGv_f64 arg) +{ + tcg_temp_free_internal(tcgv_f64_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) @@ -1085,41 +1892,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); @@ -1134,58 +1963,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. */ @@ -1204,18 +1999,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: @@ -1226,6 +2033,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: @@ -1235,8 +2043,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; @@ -1283,8 +2091,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: @@ -1309,6 +2115,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: @@ -1322,6 +2129,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: @@ -1333,8 +2141,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; @@ -1356,9 +2164,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: @@ -1379,8 +2186,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: @@ -1525,136 +2330,155 @@ 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) +static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs); + +static void tcg_gen_callN(void *func, TCGHelperInfo *info, + TCGTemp *ret, TCGTemp **args) { - int i, real_args, nb_rets, pi; - unsigned typemask; - const TCGHelperInfo *info; + 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)); + } gen_bb_epilogue(); - info = g_hash_table_lookup(helper_table, (gpointer)func); - typemask = info->typemask; + 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_NO_SIDE_EFFECTS)) { tcg_ctx->plugin_insn->calls_helpers = true; } #endif -#if 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; + 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(); + } - 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 (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; - } - } 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; - 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(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]); - } + 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); } -#endif /* TCG_TARGET_EXTEND_ARGS */ + + tcg_debug_assert(n_extend < ARRAY_SIZE(extend_free)); + for (i = 0; i < n_extend; ++i) { + tcg_temp_free_i64(extend_free[i]); + } +} + +void tcg_gen_call0(void *func, TCGHelperInfo *info, TCGTemp *ret) +{ + tcg_gen_callN(func, info, ret, NULL); +} + +void tcg_gen_call1(void *func, TCGHelperInfo *info, TCGTemp *ret, TCGTemp *t1) +{ + tcg_gen_callN(func, info, ret, &t1); +} + +void tcg_gen_call2(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2) +{ + TCGTemp *args[2] = { t1, t2 }; + tcg_gen_callN(func, info, ret, args); +} + +void tcg_gen_call3(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2, TCGTemp *t3) +{ + TCGTemp *args[3] = { t1, t2, t3 }; + tcg_gen_callN(func, info, ret, args); +} + +void tcg_gen_call4(void *func, TCGHelperInfo *info, TCGTemp *ret, + TCGTemp *t1, TCGTemp *t2, TCGTemp *t3, TCGTemp *t4) +{ + TCGTemp *args[4] = { t1, t2, t3, t4 }; + tcg_gen_callN(func, info, ret, args); +} + +void tcg_gen_call5(void *func, 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(func, info, ret, args); +} + +void tcg_gen_call6(void *func, 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(func, info, ret, args); +} + +void tcg_gen_call7(void *func, 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(func, info, ret, args); } static void tcg_reg_alloc_start(TCGContext *s) @@ -1674,11 +2498,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: @@ -1700,13 +2523,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: @@ -1758,10 +2578,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", @@ -1775,16 +2597,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+", @@ -1793,6 +2612,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", @@ -1801,6 +2629,15 @@ static const char bswap_flag_name[][6] = { [TCG_BSWAP_IZ | TCG_BSWAP_OS] = "iz,os", }; +#ifdef CONFIG_PLUGIN +static const char * const plugin_from_name[] = { + "from-tb", + "from-insn", + "after-insn", + "after-tb", +}; +#endif + static inline bool tcg_regset_single(TCGRegSet d) { return (d & (d - 1)) == 0; @@ -1819,7 +2656,7 @@ static inline TCGReg tcg_regset_first(TCGRegSet d) #define ne_fprintf(...) \ ({ int ret_ = fprintf(__VA_ARGS__); ret_ >= 0 ? ret_ : 0; }) -static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) +void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) { char buf[128]; TCGOp *op; @@ -1837,14 +2674,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); @@ -1875,10 +2707,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 { @@ -1909,11 +2738,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: @@ -1925,23 +2756,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; } @@ -1966,6 +2812,24 @@ static void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) i = k = 1; } break; +#ifdef CONFIG_PLUGIN + case INDEX_op_plugin_cb: + { + TCGArg from = op->args[k++]; + const char *name = NULL; + + if (from < ARRAY_SIZE(plugin_from_name)) { + name = plugin_from_name[from]; + } + if (name) { + col += ne_fprintf(f, "%s", name); + } else { + col += ne_fprintf(f, "$0x%" TCG_PRIlx, from); + } + i = 1; + } + break; +#endif default: i = 0; break; @@ -1980,6 +2844,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; } @@ -2019,7 +2962,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="); @@ -2051,15 +2994,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 */ @@ -2094,7 +3054,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; @@ -2116,81 +3077,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; @@ -2199,10 +3288,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) @@ -2218,54 +3303,100 @@ 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; - s->nb_ops++; + 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) { /* FIXME: Ugly opcode hook should be moved elsewhere */ if (tcg_op_defs[opc].flags & TCG_OPF_BB_END) { gen_bb_epilogue(); } - 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 +3406,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 +3452,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 +3534,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 +3578,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 +3628,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 +3729,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 +3758,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 +3773,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 +3781,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 +3952,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 +4024,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 +4041,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 +4056,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 +4106,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 +4127,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 +4174,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 +4210,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,99 +4229,32 @@ 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: case TCG_TYPE_F32: - size = align = 4; + align = 4; break; case TCG_TYPE_I64: case TCG_TYPE_F64: 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(); @@ -3099,13 +4274,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); @@ -3120,10 +4341,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; @@ -3133,10 +4353,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. */ @@ -3180,7 +4397,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; } @@ -3267,7 +4484,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 @@ -3316,11 +4579,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 @@ -3369,10 +4630,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. */ @@ -3406,12 +4666,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; @@ -3432,10 +4689,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)) { @@ -3454,9 +4708,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]); @@ -3485,8 +4740,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). */ @@ -3494,52 +4750,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); + } } /* @@ -3551,8 +4808,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]); @@ -3571,7 +4828,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; } @@ -3581,16 +4838,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) { @@ -3621,16 +4877,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; @@ -3643,6 +4898,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); } @@ -3667,21 +4923,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]; @@ -3689,56 +4976,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 @@ -3753,7 +5159,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)) { @@ -3767,7 +5173,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); @@ -3779,7 +5185,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; @@ -3790,38 +5196,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 */ @@ -3861,6 +5332,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) { @@ -3870,11 +5342,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. */ @@ -3895,18 +5365,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; @@ -3917,6 +5383,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); } @@ -3931,296 +5398,811 @@ 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 = memop_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, target_ulong pc_start) +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(pc_start))) { FILE *logfile = qemu_log_trylock(); @@ -4231,7 +6213,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) qemu_log_unlock(logfile); } } -#endif #ifdef CONFIG_DEBUG_TCG /* Ensure all labels referenced have been emitted. */ @@ -4240,7 +6221,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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; @@ -4250,24 +6231,16 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) } #endif -#ifdef CONFIG_PROFILER - qatomic_set(&prof->opt_time, prof->opt_time - profile_getclock()); -#endif + /* Do not reuse any EBB that may be allocated within the TB. */ + tcg_temp_ebb_reset_freed(s); -#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(pc_start))) { FILE *logfile = qemu_log_trylock(); @@ -4278,7 +6251,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) qemu_log_unlock(logfile); } } -#endif + /* Replace indirect temps with direct temps. */ if (liveness_pass_2(s)) { /* If changes were made, re-run liveness. */ @@ -4286,11 +6259,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) } } -#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(pc_start))) { FILE *logfile = qemu_log_trylock(); @@ -4301,19 +6269,12 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) qemu_log_unlock(logfile); } } -#endif /* Initialize goto_tb jump offsets. */ - 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; - } + 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); @@ -4324,6 +6285,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) */ s->code_buf = tcg_splitwx_to_rw(tb->tc.ptr); s->code_ptr = s->code_buf; + s->data_gen_ptr = NULL; #ifdef TCG_TARGET_NEED_LDST_LABELS QSIMPLEQ_INIT(&s->ldst_labels); @@ -4332,14 +6294,16 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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: @@ -4357,14 +6321,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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: @@ -4377,6 +6336,12 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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; @@ -4391,9 +6356,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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 @@ -4406,7 +6368,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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 */ @@ -4436,77 +6398,6 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, target_ulong pc_start) 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: diff --git a/tcg/tci.c b/tcg/tci.c index bdfac83492..3afb223528 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -18,11 +18,9 @@ */ #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/helper-info.h" #include "tcg/tcg-ldst.h" -#include "qemu/compiler.h" #include @@ -109,7 +107,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 +142,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 +229,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 +277,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 +364,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 +375,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 +388,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 +746,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 +922,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 +967,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 +1054,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 +1279,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 +1301,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 f3d7441e06..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; } @@ -833,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); @@ -852,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 ceb36c4f7a..a076f401d2 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -42,17 +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 - /* Optional instructions. */ #define TCG_TARGET_HAS_bswap16_i32 1 @@ -74,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 @@ -110,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 @@ -128,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 @@ -158,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 @@ -167,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 9422ddaece..010369bd3a 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -3,28 +3,30 @@ .PHONY: check-help check-help: @echo "Regression testing targets:" - @echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests" - @echo " $(MAKE) bench Run speed tests" + @echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests" + @echo " $(MAKE) bench Run speed tests" @echo @echo "Individual test suites:" - @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target" - @echo " $(MAKE) check-qtest Run qtest tests" - @echo " $(MAKE) check-unit Run qobject tests" - @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" - @echo " $(MAKE) check-block Run block tests" + @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target" + @echo " $(MAKE) check-qtest Run qtest tests" + @echo " $(MAKE) check-functional Run python-based functional tests" + @echo " $(MAKE) check-functional-TARGET Run functional tests for a given target" + @echo " $(MAKE) check-unit Run qobject tests" + @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" + @echo " $(MAKE) check-block Run block tests" ifneq ($(filter $(all-check-targets), check-softfloat),) - @echo " $(MAKE) check-tcg Run TCG tests" - @echo " $(MAKE) check-softfloat Run FPU emulation tests" + @echo " $(MAKE) check-tcg Run TCG tests" + @echo " $(MAKE) check-softfloat Run FPU emulation tests" endif - @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" + @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" @echo - @echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report" - @echo " $(MAKE) check-venv Creates a Python venv for tests" - @echo " $(MAKE) check-clean Clean the tests and related data" + @echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report" + @echo " $(MAKE) check-venv Creates a Python venv for tests" + @echo " $(MAKE) check-clean Clean the tests and related data" @echo @echo "The following are useful for CI builds" - @echo " $(MAKE) check-build Build most test binaries" - @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" + @echo " $(MAKE) check-build Build most test binaries" + @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @@ -73,7 +75,7 @@ $(TCG_TESTS_TARGETS:%=distclean-tcg-tests-%): distclean-tcg-tests-%: 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 @@ -87,19 +89,17 @@ distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) .PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning # Build up our target list from the filtered list of ninja targets -TARGETS=$(patsubst libqemu-%.fa, %, $(filter libqemu-%.fa, $(ninja-targets))) +TARGETS=$(patsubst libqemu-%.a, %, $(filter libqemu-%.a, $(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 # Controls the output generated by Avocado when running tests. # Any number of command separated loggers are accepted. For more # information please refer to "avocado --help". -AVOCADO_SHOW=app +AVOCADO_SHOW?=app ifndef AVOCADO_TAGS AVOCADO_CMDLINE_TAGS=$(patsubst %-softmmu,-t arch:%, \ $(filter %-softmmu,$(TARGETS))) @@ -108,20 +108,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)) @@ -131,7 +130,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 $*") @@ -140,11 +139,11 @@ 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) \ - $(AVOCADO_CMDLINE_TAGS) \ + $(AVOCADO_CMDLINE_TAGS) --max-parallel-tasks=1 \ $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ "AVOCADO", "tests/avocado") @@ -155,6 +154,16 @@ check-acceptance-deprecated-warning: check-acceptance: check-acceptance-deprecated-warning | check-avocado +FUNCTIONAL_TARGETS=$(patsubst %-softmmu,check-functional-%, $(filter %-softmmu,$(TARGETS))) +.PHONY: $(FUNCTIONAL_TARGETS) +$(FUNCTIONAL_TARGETS): + @$(MAKE) SPEED=thorough $(subst -functional,-func,$@) + +.PHONY: check-functional +check-functional: + @$(NINJA) precache-functional + @QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick + # Consolidated targets .PHONY: check check-clean get-vm-images @@ -163,7 +172,7 @@ 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 diff --git a/tests/avocado/acpi-bits.py b/tests/avocado/acpi-bits.py deleted file mode 100644 index 898c837f26..0000000000 --- a/tests/avocado/acpi-bits.py +++ /dev/null @@ -1,398 +0,0 @@ -#!/usr/bin/env python3 -# group: rw quick -# Exercize 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_qemu import QemuBaseTest - -deps = ["xorriso"] # dependent tools needed in the test setup/box. -supported_platforms = ['x86_64'] # supported test platforms. - - -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", - sock_dir: Optional[str] = None, - qmp_timer: Optional[float] = None): - # pylint: disable=too-many-arguments - - if name is None: - name = "qemu-bits-%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) - 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() or os.getenv('GITLAB_CI'), - 'incorrect platform or dependencies (%s) not installed ' \ - 'or running on GitLab' % ','.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 = 200 - - 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 - self._bitsCommitHash = 'b48b88ff' # commit hash must match - # the artifact tag below - self._bitsTag = "qemu-bits-10182022" # this is the latest bits - # release as of today. - self._bitsArtSHA1Hash = 'b04790ac9b99b5662d0416392c73b97580641fe5' - 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'): - 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 implementaion.""" - - 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') - - args = " ".join(str(arg) for arg in self._vm.base_args()) + \ - " " + " ".join(str(arg) for arg in self._vm.args) - - self.logger.info("launching QEMU vm with the following arguments: %s", - args) - - self._vm.launch() - # biosbits has been configured to run all the specified test suites - # in batch mode and then automatically initiate a vm shutdown. - # Rely on avocado's unit test timeout. - self._vm.wait(timeout=None) - self.parse_log() diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index 910f3ba1ea..93c3460242 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -10,7 +10,6 @@ import logging import os -import shutil import subprocess import sys import tempfile @@ -18,7 +17,7 @@ import time import uuid import avocado -from avocado.utils import cloudinit, datadrainer, process, ssh, vmimage +from avocado.utils import ssh from avocado.utils.path import find_command from qemu.machine import QEMUMachine @@ -32,14 +31,6 @@ from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available, #: and build tree, it will not be accurate. BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) -if os.path.islink(os.path.dirname(os.path.dirname(__file__))): - # The link to the avocado tests dir in the source code directory - lnk = os.path.dirname(os.path.dirname(__file__)) - #: The QEMU root source directory - SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) -else: - SOURCE_DIR = BUILD_DIR - def has_cmd(name, args=None): """ @@ -137,13 +128,20 @@ 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: vm.console_socket.sendall(send_string.encode()) if not keep_sending: send_string = None # send only once + + # Only consume console output if waiting for something + if success_message is None and failure_message is None: + if send_string is None: + break + continue + try: msg = console.readline().decode().strip() except UnicodeDecodeError: @@ -254,7 +252,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, @@ -274,6 +272,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')) @@ -306,9 +308,9 @@ class QemuSystemTest(QemuBaseTest): self.cancel('no support for user networking') def _new_vm(self, name, *args): - self._sd = tempfile.TemporaryDirectory(prefix="avo_qemu_sock_") + self._sd = tempfile.TemporaryDirectory(prefix="qemu_") vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, - sock_dir=self._sd.name, log_dir=self.logdir) + 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) @@ -316,6 +318,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') @@ -359,30 +374,13 @@ class QemuSystemTest(QemuBaseTest): super().tearDown() -class QemuUserTest(QemuBaseTest): - """Facilitates user-mode emulation tests.""" - - def setUp(self): - self._ldpath = [] - super().setUp('qemu-') - - def add_ldpath(self, ldpath): - self._ldpath.append(os.path.abspath(ldpath)) - - def run(self, bin_path, args=[]): - qemu_args = " ".join(["-L %s" % ldpath for ldpath in self._ldpath]) - bin_args = " ".join(args) - return process.run("%s %s %s %s" % (self.qemu_bin, qemu_args, - bin_path, bin_args)) - - class LinuxSSHMixIn: """Contains utility methods for interacting with a guest via SSH.""" 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) @@ -417,238 +415,10 @@ class LinuxSSHMixIn: f'Guest command failed: {command}') return stdout_lines, stderr_lines -class LinuxDistro: - """Represents a Linux distribution - - Holds information of known distros. - """ - #: A collection of known distros and their respective image checksum - KNOWN_DISTROS = { - 'fedora': { - '31': { - 'x86_64': - {'checksum': ('e3c1b309d9203604922d6e255c2c5d09' - '8a309c2d46215d8fc026954f3c5c27a0'), - 'pxeboot_url': ('https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/x86_64/os/images/pxeboot/'), - 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-' - '08a96687f73c ro no_timer_check ' - 'net.ifnames=0 console=tty1 ' - 'console=ttyS0,115200n8'), - }, - 'aarch64': - {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae' - 'd2af0ad0329383d5639c997fdf16fe49'), - 'pxeboot_url': 'https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/aarch64/os/images/pxeboot/', - 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-' - '355e8475b0a7 ro earlyprintk=pl011,0x9000000' - ' ignore_loglevel no_timer_check' - ' printk.time=1 rd_NO_PLYMOUTH' - ' console=ttyAMA0'), - }, - 'ppc64': - {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4' - '3f991c506f2cc390dc4efa2026ad2f58')}, - 's390x': - {'checksum': ('4caaab5a434fd4d1079149a072fdc789' - '1e354f834d355069ca982fdcaf5a122d')}, - }, - '32': { - 'aarch64': - {'checksum': ('b367755c664a2d7a26955bbfff985855' - 'adfa2ca15e908baf15b4b176d68d3967'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/32/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-' - '14d95c0e90c5 ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8'), - }, - }, - '33': { - 'aarch64': - {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1' - 'a81f386a17f969c1d1c7c87031008a6b'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/33/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-' - '1126a0208f8a ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8' - ' console=tty0'), - }, - }, - } - } - - def __init__(self, name, version, arch): - self.name = name - self.version = version - self.arch = arch - try: - info = self.KNOWN_DISTROS.get(name).get(version).get(arch) - except AttributeError: - # Unknown distro - info = None - self._info = info or {} - - @property - def checksum(self): - """Gets the cloud-image file checksum""" - return self._info.get('checksum', None) - - @checksum.setter - def checksum(self, value): - self._info['checksum'] = value - - @property - def pxeboot_url(self): - """Gets the repository url where pxeboot files can be found""" - return self._info.get('pxeboot_url', None) - - @property - def default_kernel_params(self): - """Gets the default kernel parameters""" - return self._info.get('kernel_params', None) - - -class LinuxTest(LinuxSSHMixIn, QemuSystemTest): - """Facilitates having a cloud-image Linux based available. - - For tests that intend to interact with guests, this is a better choice - to start with than the more vanilla `QemuSystemTest` class. - """ - - distro = None - username = 'root' - password = 'password' - smp = '2' - memory = '1024' - - def _set_distro(self): - distro_name = self.params.get( - 'distro', - default=self._get_unique_tag_val('distro')) - if not distro_name: - distro_name = 'fedora' - - distro_version = self.params.get( - 'distro_version', - default=self._get_unique_tag_val('distro_version')) - if not distro_version: - distro_version = '31' - - self.distro = LinuxDistro(distro_name, distro_version, self.arch) - - # The distro checksum behaves differently than distro name and - # version. First, it does not respect a tag with the same - # name, given that it's not expected to be used for filtering - # (distro name versions are the natural choice). Second, the - # order of precedence is: parameter, attribute and then value - # from KNOWN_DISTROS. - distro_checksum = self.params.get('distro_checksum', - default=None) - if distro_checksum: - self.distro.checksum = distro_checksum - - 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) - # The following network device allows for SSH connections - self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', '%s,netdev=vnet' % network_device_type) - self.set_up_boot() - if ssh_pubkey is None: - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.set_up_cloudinit(ssh_pubkey) - - def set_up_existing_ssh_keys(self): - ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub') - source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa') - ssh_dir = os.path.join(self.workdir, '.ssh') - os.mkdir(ssh_dir, mode=0o700) - ssh_private_key = os.path.join(ssh_dir, - os.path.basename(source_private_key)) - shutil.copyfile(source_private_key, ssh_private_key) - os.chmod(ssh_private_key, 0o600) - 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 - - self.log.info('Downloading/preparing boot image') - # Fedora 31 only provides ppc64le images - image_arch = self.arch - if self.distro.name == 'fedora': - if image_arch == 'ppc64': - image_arch = 'ppc64le' - - try: - boot = vmimage.get( - self.distro.name, arch=image_arch, version=self.distro.version, - checksum=self.distro.checksum, - algorithm='sha256', - cache_dir=self.cache_dirs[0], - snapshot_dir=self.workdir) - except: - self.cancel('Failed to download/prepare boot image') - return boot.path - - def prepare_cloudinit(self, ssh_pubkey=None): - self.log.info('Preparing cloudinit image') - try: - cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') - pubkey_content = None - if ssh_pubkey: - with open(ssh_pubkey) as pubkey: - pubkey_content = pubkey.read() - cloudinit.iso(cloudinit_iso, self.name, - username=self.username, - password=self.password, - # QEMU's hard coded usermode router address - phone_home_host='10.0.2.2', - phone_home_port=self.phone_server.server_port, - authorized_key=pubkey_content) - except Exception: - self.cancel('Failed to prepare the cloudinit image') - return cloudinit_iso - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-drive', 'file=%s' % path) - - def set_up_cloudinit(self, ssh_pubkey=None): - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) - self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) - - def launch_and_wait(self, set_up_ssh_connection=True): - self.vm.set_console() - self.vm.launch() - console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), - logger=self.log.getChild('console')) - console_drainer.start() - self.log.info('VM launched, waiting for boot confirmation from guest') - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - - if set_up_ssh_connection: - self.log.info('Setting up the SSH connection') - self.ssh_connect(self.username, self.ssh_key) + 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)) diff --git a/tests/avocado/avocado_qemu/linuxtest.py b/tests/avocado/avocado_qemu/linuxtest.py new file mode 100644 index 0000000000..66fb9f1507 --- /dev/null +++ b/tests/avocado/avocado_qemu/linuxtest.py @@ -0,0 +1,253 @@ +# Test class and utilities for functional Linux-based tests +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import shutil + +from avocado.utils import cloudinit, datadrainer, process, vmimage + +from avocado_qemu import LinuxSSHMixIn +from avocado_qemu import QemuSystemTest + +if os.path.islink(os.path.dirname(os.path.dirname(__file__))): + # The link to the avocado tests dir in the source code directory + lnk = os.path.dirname(os.path.dirname(__file__)) + #: The QEMU root source directory + SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) +else: + SOURCE_DIR = BUILD_DIR + +class LinuxDistro: + """Represents a Linux distribution + + Holds information of known distros. + """ + #: A collection of known distros and their respective image checksum + KNOWN_DISTROS = { + 'fedora': { + '31': { + 'x86_64': + {'checksum': ('e3c1b309d9203604922d6e255c2c5d09' + '8a309c2d46215d8fc026954f3c5c27a0'), + 'pxeboot_url': ('https://archives.fedoraproject.org/' + 'pub/archive/fedora/linux/releases/31/' + 'Everything/x86_64/os/images/pxeboot/'), + 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-' + '08a96687f73c ro no_timer_check ' + 'net.ifnames=0 console=tty1 ' + 'console=ttyS0,115200n8'), + }, + 'aarch64': + {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae' + 'd2af0ad0329383d5639c997fdf16fe49'), + 'pxeboot_url': 'https://archives.fedoraproject.org/' + 'pub/archive/fedora/linux/releases/31/' + 'Everything/aarch64/os/images/pxeboot/', + 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-' + '355e8475b0a7 ro earlyprintk=pl011,0x9000000' + ' ignore_loglevel no_timer_check' + ' printk.time=1 rd_NO_PLYMOUTH' + ' console=ttyAMA0'), + }, + 'ppc64': + {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4' + '3f991c506f2cc390dc4efa2026ad2f58')}, + 's390x': + {'checksum': ('4caaab5a434fd4d1079149a072fdc789' + '1e354f834d355069ca982fdcaf5a122d')}, + }, + '32': { + 'aarch64': + {'checksum': ('b367755c664a2d7a26955bbfff985855' + 'adfa2ca15e908baf15b4b176d68d3967'), + 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' + 'releases/32/Server/aarch64/os/images/' + 'pxeboot/'), + 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-' + '14d95c0e90c5 ro no_timer_check net.ifnames=0' + ' console=tty1 console=ttyS0,115200n8'), + }, + }, + '33': { + 'aarch64': + {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1' + 'a81f386a17f969c1d1c7c87031008a6b'), + 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' + 'releases/33/Server/aarch64/os/images/' + 'pxeboot/'), + 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-' + '1126a0208f8a ro no_timer_check net.ifnames=0' + ' console=tty1 console=ttyS0,115200n8' + ' console=tty0'), + }, + }, + } + } + + def __init__(self, name, version, arch): + self.name = name + self.version = version + self.arch = arch + try: + info = self.KNOWN_DISTROS.get(name).get(version).get(arch) + except AttributeError: + # Unknown distro + info = None + self._info = info or {} + + @property + def checksum(self): + """Gets the cloud-image file checksum""" + return self._info.get('checksum', None) + + @checksum.setter + def checksum(self, value): + self._info['checksum'] = value + + @property + def pxeboot_url(self): + """Gets the repository url where pxeboot files can be found""" + return self._info.get('pxeboot_url', None) + + @property + def default_kernel_params(self): + """Gets the default kernel parameters""" + return self._info.get('kernel_params', None) + + +class LinuxTest(LinuxSSHMixIn, QemuSystemTest): + """Facilitates having a cloud-image Linux based available. + + For tests that intend to interact with guests, this is a better choice + to start with than the more vanilla `QemuSystemTest` class. + """ + + distro = None + username = 'root' + password = 'password' + smp = '2' + memory = '1024' + + def _set_distro(self): + distro_name = self.params.get( + 'distro', + default=self._get_unique_tag_val('distro')) + if not distro_name: + distro_name = 'fedora' + + distro_version = self.params.get( + 'distro_version', + default=self._get_unique_tag_val('distro_version')) + if not distro_version: + distro_version = '31' + + self.distro = LinuxDistro(distro_name, distro_version, self.arch) + + # The distro checksum behaves differently than distro name and + # version. First, it does not respect a tag with the same + # name, given that it's not expected to be used for filtering + # (distro name versions are the natural choice). Second, the + # order of precedence is: parameter, attribute and then value + # from KNOWN_DISTROS. + distro_checksum = self.params.get('distro_checksum', + default=None) + if distro_checksum: + self.distro.checksum = distro_checksum + + 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) + # The following network device allows for SSH connections + self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', '%s,netdev=vnet' % network_device_type) + self.set_up_boot() + if ssh_pubkey is None: + ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() + self.set_up_cloudinit(ssh_pubkey) + + def set_up_existing_ssh_keys(self): + ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub') + source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa') + ssh_dir = os.path.join(self.workdir, '.ssh') + os.mkdir(ssh_dir, mode=0o700) + ssh_private_key = os.path.join(ssh_dir, + os.path.basename(source_private_key)) + shutil.copyfile(source_private_key, ssh_private_key) + os.chmod(ssh_private_key, 0o600) + return (ssh_public_key, ssh_private_key) + + def download_boot(self): + # 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 + image_arch = self.arch + if self.distro.name == 'fedora': + if image_arch == 'ppc64': + image_arch = 'ppc64le' + + try: + boot = vmimage.get( + self.distro.name, arch=image_arch, version=self.distro.version, + checksum=self.distro.checksum, + algorithm='sha256', + cache_dir=self.cache_dirs[0], + snapshot_dir=self.workdir) + except: + self.cancel('Failed to download/prepare boot image') + return boot.path + + def prepare_cloudinit(self, ssh_pubkey=None): + self.log.info('Preparing cloudinit image') + try: + cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') + pubkey_content = None + if ssh_pubkey: + with open(ssh_pubkey) as pubkey: + pubkey_content = pubkey.read() + cloudinit.iso(cloudinit_iso, self.name, + username=self.username, + password=self.password, + # QEMU's hard coded usermode router address + phone_home_host='10.0.2.2', + phone_home_port=self.phone_server.server_port, + authorized_key=pubkey_content) + except Exception: + self.cancel('Failed to prepare the cloudinit image') + return cloudinit_iso + + def set_up_boot(self): + path = self.download_boot() + self.vm.add_args('-drive', 'file=%s' % path) + + def set_up_cloudinit(self, ssh_pubkey=None): + self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), + self.name) + cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) + self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) + + def launch_and_wait(self, set_up_ssh_connection=True): + self.vm.set_console() + self.vm.launch() + console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), + logger=self.log.getChild('console')) + console_drainer.start() + self.log.info('VM launched, waiting for boot confirmation from guest') + while not self.phone_server.instance_phoned_back: + self.phone_server.handle_request() + + if set_up_ssh_connection: + self.log.info('Setting up the SSH connection') + self.ssh_connect(self.username, self.ssh_key) diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py index b3e58fa309..a029ef4ad1 100644 --- a/tests/avocado/boot_linux.py +++ b/tests/avocado/boot_linux.py @@ -10,9 +10,10 @@ import os -from avocado_qemu import LinuxTest, BUILD_DIR +from avocado_qemu.linuxtest import LinuxTest +from avocado_qemu import BUILD_DIR -from avocado import skipIf +from avocado import skipUnless class BootLinuxX8664(LinuxTest): @@ -58,52 +59,16 @@ class BootLinuxX8664(LinuxTest): self.launch_and_wait(set_up_ssh_connection=False) -# For Aarch64 we only boot KVM tests in CI as the TCG tests are very -# heavyweight. There are lighter weight distros which we use in the -# machine_aarch64_virt.py tests. +# 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 """ timeout = 720 - 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') - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_fedora_cloud_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) - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_fedora_cloud_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) - def test_virt_kvm(self): """ :avocado: tags=accel:kvm @@ -112,10 +77,16 @@ 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 @@ -123,6 +94,7 @@ class BootLinuxPPC64(LinuxTest): timeout = 360 + @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') def test_pseries_tcg(self): """ :avocado: tags=machine:pseries @@ -132,6 +104,15 @@ 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): """ @@ -140,7 +121,7 @@ class BootLinuxS390X(LinuxTest): timeout = 240 - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') + @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 ec07c64291..12e24bb05a 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -15,7 +15,7 @@ import shutil from avocado import skip from avocado import skipUnless -from avocado import skipIf +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 @@ -30,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 """ @@ -111,221 +116,6 @@ class BootLinuxConsole(LinuxKernelTest): console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) - def test_mips_malta(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20130217T032700Z/pool/main/l/linux-2.6/' - 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb') - deb_hash = 'a8cfc28ad8f45f54811fc6cf74fc43ffcfe0ba04' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-4kc-malta') - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_mips64el_malta(self): - """ - This test requires the ar tool to extract "data.tar.gz" from - the Debian package. - - The kernel can be rebuilt using this Debian kernel source [1] and - following the instructions on [2]. - - [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/ - #linux-source-2.6.32_2.6.32-48 - [2] https://kernel-team.pages.debian.net/kernel-handbook/ - ch-common-tasks.html#s-common-official - - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20130217T032700Z/pool/main/l/linux-2.6/' - 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb') - deb_hash = '1aaec92083bf22fda31e0d27fa8d9a388e5fc3d5' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-5kc-malta') - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_mips64el_fuloong2e(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:fuloong2e - :avocado: tags=endian:little - """ - deb_url = ('http://archive.debian.org/debian/pool/main/l/linux/' - 'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb') - deb_hash = 'd04d446045deecf7b755ef576551de0c4184dd44' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-3.16.0-6-loongson-2e') - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_mips_malta_cpio(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20160601T041800Z/pool/main/l/linux/' - 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb') - deb_hash = 'a3c84f3e88b54e06107d65a410d1d1e8e0f340f8' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-4.5.0-2-4kc-malta') - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/' - 'mips/rootfs.cpio.gz') - initrd_hash = 'bf806e17009360a866bf537f6de66590de349a99' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = 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 console=tty ' - + 'rdinit=/sbin/init noreboot') - self.vm.add_args('-kernel', kernel_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', - 'BogoMIPS') - exec_command_and_wait_for_pattern(self, 'uname -a', - 'Debian') - 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_UNTRUSTED_CODE'), 'untrusted code') - def test_mips64el_malta_5KEc_cpio(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:5KEc - """ - kernel_url = ('https://github.com/philmd/qemu-testing-blob/' - 'raw/9ad2df38/mips/malta/mips64el/' - 'vmlinux-3.19.3.mtoman.20150408') - kernel_hash = '00d1d268fb9f7d8beda1de6bebcc46e884d71754' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - initrd_url = ('https://github.com/groeck/linux-build-test/' - 'raw/8584a59e/rootfs/' - 'mipsel64/rootfs.mipsel64r1.cpio.gz') - initrd_hash = '1dbb8a396e916847325284dbe2151167' - initrd_path_gz = self.fetch_asset(initrd_url, algorithm='md5', - asset_hash=initrd_hash) - initrd_path = 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 console=tty ' - + 'rdinit=/sbin/init noreboot') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - wait_for_console_pattern(self, 'Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'MIPS 5KE') - exec_command_and_wait_for_pattern(self, 'uname -a', - '3.19.3.mtoman.20150408') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def do_test_mips_malta32el_nanomips(self, kernel_url, kernel_hash): - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - kernel_path = self.workdir + "kernel" - with lzma.open(kernel_path_xz, 'rb') as f_in: - with open(kernel_path, 'wb') as f_out: - shutil.copyfileobj(f_in, f_out) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'mem=256m@@0x0 ' - + 'console=ttyS0') - self.vm.add_args('-no-reboot', - '-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_mips_malta32el_nanomips_4k(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page4k.xz') - kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' - self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) - - def test_mips_malta32el_nanomips_16k_up(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page16k_up.xz') - kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' - self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) - - def test_mips_malta32el_nanomips_64k_dbg(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('https://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page64k_dbg.xz') - kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' - self.do_test_mips_malta32el_nanomips(kernel_url, kernel_hash) - def test_aarch64_xlnx_versal_virt(self): """ :avocado: tags=arch:aarch64 @@ -394,12 +184,16 @@ class BootLinuxConsole(LinuxKernelTest): 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin') spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501' spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) + spi_path_rw = os.path.join(self.workdir, os.path.basename(spi_path)) + shutil.copy(spi_path, spi_path_rw) + + file_truncate(spi_path_rw, 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, '-append', kernel_command_line, - '-drive', 'file=' + spi_path + ',if=mtd,format=raw', + '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw', '-no-reboot') self.vm.launch() self.wait_for_console_pattern('Enter \'help\' for a list') @@ -409,91 +203,6 @@ class BootLinuxConsole(LinuxKernelTest): exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', '3 packets transmitted, 3 packets received, 0% packet loss') - def do_test_arm_raspi2(self, uart_id): - """ - :avocado: tags=accel:tcg - - 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 - """ - serial_kernel_cmdline = { - 0: 'earlycon=pl011,0x3f201000 console=ttyAMA0', - } - deb_url = ('http://archive.raspberrypi.org/debian/' - 'pool/main/r/raspberrypi-firmware/' - 'raspberrypi-kernel_1.20190215-1_armhf.deb') - deb_hash = 'cd284220b32128c5084037553db3c482426f3972' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - serial_kernel_cmdline[uart_id] + - ' root=/dev/mmcblk0p2 rootwait ' + - 'dwc_otg.fiq_fsm_enable=0') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line, - '-device', 'usb-kbd') - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - console_pattern = 'Product: QEMU USB Keyboard' - self.wait_for_console_pattern(console_pattern) - - def test_arm_raspi2_uart0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:raspi2b - :avocado: tags=device:pl011 - :avocado: tags=accel:tcg - """ - self.do_test_arm_raspi2(0) - - def test_arm_raspi2_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:raspi2b - """ - deb_url = ('http://archive.raspberrypi.org/debian/' - 'pool/main/r/raspberrypi-firmware/' - 'raspberrypi-kernel_1.20190215-1_armhf.deb') - deb_hash = 'cd284220b32128c5084037553db3c482426f3972' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') - - 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 + - 'earlycon=pl011,0x3f201000 console=ttyAMA0 ' - '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') - 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', - '/soc/cprman@7e101000') - exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') - # Wait for VM to shut down gracefully - self.vm.wait() - def test_arm_exynos4210_initrd(self): """ :avocado: tags=arch:arm @@ -542,12 +251,12 @@ class BootLinuxConsole(LinuxKernelTest): :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' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' 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/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -574,7 +283,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): """ @@ -583,12 +295,12 @@ class BootLinuxConsole(LinuxKernelTest): :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' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' 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/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -618,7 +330,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): @@ -706,363 +471,21 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern( 'Give root password for system maintenance') - def test_arm_orangepi(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :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-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart,mmio32,0x1c28000') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_arm_orangepi_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:orangepi-pc - """ - 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-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv7a.cpio.gz') - initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_orangepi_sd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :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' - 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-h3-orangepi-pc.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=/dev/mmcblk0 rootwait rw ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - shell_ready = "/bin/sh: can't access tty; job control turned off" - self.wait_for_console_pattern(shell_ready) - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'mmcblk0') - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', - 'eth0: Link is Up') - exec_command_and_wait_for_pattern(self, 'udhcpc eth0', - 'udhcpc: lease of 10.0.2.15 obtained') - exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', - '3 packets transmitted, 3 packets received, 0% packet loss') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_orangepi_bionic_20_08(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - """ - - # This test download a 275 MiB compressed image and expand it - # to 1036 MiB, but the underlying filesystem is 1552 MiB... - # As we expand it to 2 GiB we are safe. - - image_url = ('https://archive.armbian.com/orangepipc/archive/' - 'Armbian_20.08.1_Orangepipc_bionic_current_5.8.5.img.xz') - image_hash = ('b4d6775f5673486329e45a0586bf06b6' - 'dbe792199fd182ac6b9c7bb6c7d3e6dd') - image_path_xz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_xz, 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 + - 'console=ttyS0,115200 ' - 'loglevel=7 ' - 'nosmp ' - 'systemd.default_timeout_start_sec=9000 ' - 'systemd.mask=armbian-zram-config.service ' - 'systemd.mask=armbian-ramlog.service') - - self.wait_for_console_pattern('U-Boot SPL') - self.wait_for_console_pattern('Autoboot in ') - exec_command_and_wait_for_pattern(self, ' ', '=>') - exec_command_and_wait_for_pattern(self, "setenv extraargs '" + - kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); - - self.wait_for_console_pattern('systemd[1]: Set hostname ' + - 'to ') - self.wait_for_console_pattern('Starting Load Kernel Modules...') - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_orangepi_uboot_netbsd9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - :avocado: tags=os:netbsd - """ - # This test download a 304MB compressed image and expand it to 2GB - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20200108T145233Z/pool/main/u/u-boot/' - 'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb') - deb_hash = 'f67f404a80753ca3d1258f13e38f2b060e13db99' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - # We use the common OrangePi PC 'plus' build of U-Boot for our secondary - # program loader (SPL). We will then set the path to the more specific - # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, - # before to boot NetBSD. - uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' - uboot_path = self.extract_from_deb(deb_path, uboot_path) - image_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/' - 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz') - image_hash = '2babb29d36d8360adcb39c09e31060945259917a' - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) - image_path = os.path.join(self.workdir, 'armv7.img') - archive.gzip_uncompress(image_path_gz, image_path) - image_pow2ceil_expand(image_path) - image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path - - # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc - with open(uboot_path, 'rb') as f_in: - with open(image_path, 'r+b') as f_out: - f_out.seek(8 * 1024) - shutil.copyfileobj(f_in, f_out) - - self.vm.set_console() - self.vm.add_args('-nic', 'user', - '-drive', image_drive_args, - '-global', 'allwinner-rtc.base-year=2000', - '-no-reboot') - self.vm.launch() - wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1') - interrupt_interactive_console_until_pattern(self, - 'Hit any key to stop autoboot:', - 'switch to partitions #0, OK') - - exec_command_and_wait_for_pattern(self, '', '=>') - cmd = 'setenv bootargs root=ld0a' - exec_command_and_wait_for_pattern(self, cmd, '=>') - cmd = 'setenv kernel netbsd-GENERIC.ub' - exec_command_and_wait_for_pattern(self, cmd, '=>') - cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb' - exec_command_and_wait_for_pattern(self, cmd, '=>') - cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; " - "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; " - "fdt addr ${fdt_addr_r}; " - "bootm ${kernel_addr_r} - ${fdt_addr_r}'") - exec_command_and_wait_for_pattern(self, cmd, '=>') - - exec_command_and_wait_for_pattern(self, 'boot', - 'Booting kernel from Legacy Image') - wait_for_console_pattern(self, 'Starting kernel ...') - wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)') - # Wait for user-space - wait_for_console_pattern(self, 'Starting root file system check') - - def test_aarch64_raspi3_atf(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:raspi3b - :avocado: tags=cpu:cortex-a53 - :avocado: tags=device:pl011 - :avocado: tags=atf - """ - zip_url = ('https://github.com/pbatard/RPi3/releases/download/' - 'v1.15/RPi3_UEFI_Firmware_v1.15.zip') - zip_hash = '74b3bd0de92683cadb14e008a7575e1d0c3cafb9' - zip_path = self.fetch_asset(zip_url, asset_hash=zip_hash) - - archive.extract(zip_path, self.workdir) - efi_fd = os.path.join(self.workdir, 'RPI_EFI.fd') - - self.vm.set_console(console_index=1) - self.vm.add_args('-nodefaults', - '-device', 'loader,file=%s,force-raw=true' % efi_fd) - self.vm.launch() - self.wait_for_console_pattern('version UEFI Firmware v1.15') - - def test_s390x_s390_ccw_virtio(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/s390x/os/images' - '/kernel.img') - kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' - self.vm.add_args('-nodefaults', - '-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_alpha_clipper(self): - """ - :avocado: tags=arch:alpha - :avocado: tags=machine:clipper - """ - kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/' - 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz') - kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - uncompressed_kernel = archive.uncompress(kernel_path, self.workdir) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-nodefaults', - '-kernel', uncompressed_kernel, - '-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_m68k_q800(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:q800 - """ - deb_url = ('https://snapshot.debian.org/archive/debian-ports' - '/20191021T083923Z/pool-m68k/main' - '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') - deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-5.3.0-1-m68k') - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 vga=off') - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - console_pattern = 'No filesystem could mount root' - self.wait_for_console_pattern(console_pattern) - - def do_test_advcal_2018(self, day, tar_hash, kernel_name, console=0): - 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) - self.vm.add_args('-kernel', - self.workdir + '/day' + day + '/' + kernel_name) - self.vm.launch() - self.wait_for_console_pattern('QEMU advent calendar') - - def test_arm_vexpressa9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:vexpress-a9 - """ - tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - self.vm.add_args('-dtb', self.workdir + '/day16/vexpress-v2p-ca9.dtb') - self.do_test_advcal_2018('16', tar_hash, 'winter.zImage') - def test_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, @@ -1073,135 +496,3 @@ class BootLinuxConsole(LinuxKernelTest): self.wait_for_console_pattern("SMP: Total of 2 processors activated") self.wait_for_console_pattern("No filesystem could mount root") - def test_m68k_mcf5208evb(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:mcf5208evb - """ - tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - self.do_test_advcal_2018('07', tar_hash, 'sanity-clause.elf') - - def test_or1k_sim(self): - """ - :avocado: tags=arch:or1k - :avocado: tags=machine:or1k-sim - """ - tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - self.do_test_advcal_2018('20', tar_hash, 'vmlinux') - - def test_nios2_10m50(self): - """ - :avocado: tags=arch:nios2 - :avocado: tags=machine:10m50-ghrd - """ - tar_hash = 'e4251141726c412ac0407c5a6bceefbbff018918' - self.do_test_advcal_2018('14', tar_hash, 'vmlinux.elf') - - def test_ppc64_e500(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e5500 - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - self.do_test_advcal_2018('19', tar_hash, 'uImage') - - def do_test_ppc64_powernv(self, proc): - self.require_accelerator("tcg") - images_url = ('https://github.com/open-power/op-build/releases/download/v2.7/') - - kernel_url = images_url + 'zImage.epapr' - kernel_hash = '0ab237df661727e5392cee97460e8674057a883c5f74381a128fa772588d45cd' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash, - algorithm='sha256') - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path, - '-append', 'console=tty0 console=hvc0', - '-device', 'pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0', - '-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234', - '-device', 'e1000e,bus=bridge1,addr=0x3', - '-device', 'nec-usb-xhci,bus=bridge1,addr=0x2') - self.vm.launch() - - 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") - - def test_ppc_powernv8(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv8 - :avocado: tags=accel:tcg - """ - self.do_test_ppc64_powernv('P8') - - def test_ppc_powernv9(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv9 - :avocado: tags=accel:tcg - """ - self.do_test_ppc64_powernv('P9') - - def test_ppc_g3beige(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:g3beige - :avocado: tags=accel:tcg - """ - # TODO: g3beige works with kvm_pr but we don't have a - # reliable way ATM (e.g. looking at /proc/modules) to detect - # whether we're running kvm_hv or kvm_pr. For now let's - # disable this test if we don't have TCG support. - self.require_accelerator("tcg") - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - self.vm.add_args('-M', 'graphics=off') - self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') - - def test_ppc_mac99(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mac99 - :avocado: tags=accel:tcg - """ - # TODO: mac99 works with kvm_pr but we don't have a - # reliable way ATM (e.g. looking at /proc/modules) to detect - # whether we're running kvm_hv or kvm_pr. For now let's - # disable this test if we don't have TCG support. - self.require_accelerator("tcg") - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - self.vm.add_args('-M', 'graphics=off') - self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') - - # 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. - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_sh4_r2d(self): - """ - :avocado: tags=arch:sh4 - :avocado: tags=machine:r2d - """ - tar_hash = 'fe06a4fd8ccbf2e27928d64472939d47829d4c7e' - self.vm.add_args('-append', 'console=ttySC1') - self.do_test_advcal_2018('09', tar_hash, 'zImage', console=1) - - def test_sparc_ss20(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:SS-20 - """ - tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - self.do_test_advcal_2018('11', tar_hash, 'zImage.elf') - - def test_xtensa_lx60(self): - """ - :avocado: tags=arch:xtensa - :avocado: tags=machine:lx60 - :avocado: tags=cpu:dc233c - """ - tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - self.do_test_advcal_2018('02', tar_hash, 'santas-sleigh-ride.elf') diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py index fc2faeedb5..490a127a3e 100644 --- a/tests/avocado/boot_xen.py +++ b/tests/avocado/boot_xen.py @@ -17,59 +17,52 @@ from avocado_qemu import wait_for_console_pattern from boot_linux_console import LinuxKernelTest -class BootXenBase(LinuxKernelTest): +class BootXen(LinuxKernelTest): """ Boots a Xen hypervisor with a Linux DomU kernel. + + :avocado: tags=arch:aarch64 + :avocado: tags=accel:tcg + :avocado: tags=cpu:cortex-a57 + :avocado: tags=machine:virt """ timeout = 90 XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' - def fetch_guest_kernel(self): + def setUp(self): + super(BootXen, self).setUp() + # Using my own built kernel - which works kernel_url = ('https://fileserver.linaro.org/' 's/JSsewXGZ6mqxPr5/download?path=%2F&files=' 'linux-5.9.9-arm64-ajb') kernel_sha1 = '4f92bc4b9f88d5ab792fa7a43a68555d344e1b83' - kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_sha1) - - return kernel_path + self.kernel_path = self.fetch_asset(kernel_url, + asset_hash=kernel_sha1) def launch_xen(self, xen_path): """ Launch Xen with a dom0 guest kernel """ self.log.info("launch with xen_path: %s", xen_path) - kernel_path = self.fetch_guest_kernel() self.vm.set_console() - xen_command_line = self.XEN_COMMON_COMMAND_LINE self.vm.add_args('-machine', 'virtualization=on', '-m', '768', '-kernel', xen_path, - '-append', xen_command_line, + '-append', self.XEN_COMMON_COMMAND_LINE, '-device', 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' - % (kernel_path)) + % (self.kernel_path)) self.vm.launch() console_pattern = 'VFS: Cannot open root device' wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") - -class BootXen(BootXenBase): - def test_arm64_xen_411_and_dom0(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' 'download?path=%2F&files=' @@ -81,13 +74,6 @@ class BootXen(BootXenBase): self.launch_xen(xen_path) def test_arm64_xen_414_and_dom0(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' 'download?path=%2F&files=' @@ -99,13 +85,6 @@ class BootXen(BootXenBase): self.launch_xen(xen_path) def test_arm64_xen_415_and_dom0(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - xen_url = ('https://fileserver.linaro.org/' 's/JSsewXGZ6mqxPr5/download' '?path=%2F&files=xen-upstream-4.15-unstable.deb') diff --git a/tests/avocado/cpu_queries.py b/tests/avocado/cpu_queries.py deleted file mode 100644 index cf69f69b11..0000000000 --- a/tests/avocado/cpu_queries.py +++ /dev/null @@ -1,34 +0,0 @@ -# Sanity check of query-cpu-* results -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Author: -# Eduardo Habkost -# -# 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 - -class QueryCPUModelExpansion(QemuSystemTest): - """ - Run query-cpu-model-expansion for each CPU model, and validate results - """ - - def test(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:none - """ - self.vm.add_args('-S') - self.vm.launch() - - cpus = self.vm.command('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']) diff --git a/tests/avocado/empty_cpu_model.py b/tests/avocado/empty_cpu_model.py deleted file mode 100644 index 22f504418d..0000000000 --- a/tests/avocado/empty_cpu_model.py +++ /dev/null @@ -1,19 +0,0 @@ -# Check for crash when using empty -cpu option -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Author: -# Eduardo Habkost -# -# 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 - -class EmptyCPUModel(QemuSystemTest): - def test(self): - self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEquals(self.vm.exitcode(), 1, "QEMU exit code should be 1") - self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty') diff --git a/tests/avocado/hotplug_blk.py b/tests/avocado/hotplug_blk.py new file mode 100644 index 0000000000..b36bca02ec --- /dev/null +++ b/tests/avocado/hotplug_blk.py @@ -0,0 +1,69 @@ +# Functional test that hotplugs a virtio blk disk and checks it on a Linux +# guest +# +# Copyright (c) 2021 Red Hat, Inc. +# Copyright (c) Yandex +# +# 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.linuxtest import LinuxTest + + +class HotPlug(LinuxTest): + def blockdev_add(self) -> None: + self.vm.cmd('blockdev-add', **{ + 'driver': 'null-co', + 'size': 1073741824, + 'node-name': 'disk' + }) + + def assert_vda(self) -> None: + self.ssh_command('test -e /sys/block/vda') + + def assert_no_vda(self) -> None: + with self.assertRaises(AssertionError): + self.assert_vda() + + def plug(self) -> None: + args = { + 'driver': 'virtio-blk-pci', + 'drive': 'disk', + 'id': 'virtio-disk0', + 'bus': 'pci.1', + 'addr': '1', + } + + self.assert_no_vda() + self.vm.cmd('device_add', args) + try: + self.assert_vda() + except AssertionError: + time.sleep(1) + self.assert_vda() + + def unplug(self) -> None: + self.vm.cmd('device_del', id='virtio-disk0') + + self.vm.event_wait('DEVICE_DELETED', 1.0, + match={'data': {'device': 'virtio-disk0'}}) + + self.assert_no_vda() + + def test(self) -> None: + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 + :avocado: tags=accel:kvm + """ + self.require_accelerator('kvm') + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') + + self.launch_and_wait() + self.blockdev_add() + + self.plug() + self.unplug() diff --git a/tests/avocado/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py index 6374bf1b54..342c838539 100644 --- a/tests/avocado/hotplug_cpu.py +++ b/tests/avocado/hotplug_cpu.py @@ -8,7 +8,7 @@ # 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 LinuxTest +from avocado_qemu.linuxtest import LinuxTest class HotPlugCPU(LinuxTest): @@ -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 deleted file mode 100644 index fdc4d90c42..0000000000 --- a/tests/avocado/info_usernet.py +++ /dev/null @@ -1,33 +0,0 @@ -# Test for the hmp command "info usernet" -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu import QemuSystemTest - -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') - port = get_info_usernet_hostfwd_port(res) - self.assertIsNotNone(port, - ('"info usernet" output content does not seem to ' - 'contain the redirected port')) - self.assertGreater(port, 0, - ('Found a redirected port that is not greater than' - ' zero')) diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py index 474d62f6bf..992583fa7d 100644 --- a/tests/avocado/intel_iommu.py +++ b/tests/avocado/intel_iommu.py @@ -9,10 +9,10 @@ # later. See the COPYING file in the top-level directory. import os -from avocado import skipIf -from avocado_qemu import LinuxTest +from avocado import skipUnless +from avocado_qemu.linuxtest 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 +21,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' @@ -30,7 +31,7 @@ class IntelIOMMU(LinuxTest): def set_up_boot(self): path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' + + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + 'drive=drv0,id=virtio-disk0,bootindex=1,' 'werror=stop,rerror=stop' + self.IOMMU_ADDON) self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON) @@ -54,9 +55,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 deleted file mode 100644 index ba02e5a563..0000000000 --- a/tests/avocado/linux_initrd.py +++ /dev/null @@ -1,89 +0,0 @@ -# Linux initrd integration test. -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Wainer dos Santos Moschetta -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import logging -import tempfile - -from avocado_qemu import QemuSystemTest -from avocado import skipIf - - -class LinuxInitrd(QemuSystemTest): - """ - Checks QEMU evaluates correctly the initrd file passed as -initrd option. - - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - - timeout = 300 - - def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self): - """ - Pretends to boot QEMU with an initrd file with size of 2GiB - and expect it exits with error message. - Fedora-18 shipped with linux-3.6 which have not supported xloadflags - cannot support more than 2GiB initrd. - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora/li' - 'nux/releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz') - kernel_hash = '41464f68efe42b9991250bed86c7081d2ccdbb21' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - max_size = 2 * (1024 ** 3) - 1 - - with tempfile.NamedTemporaryFile() as initrd: - initrd.seek(max_size) - initrd.write(b'\0') - initrd.flush() - self.vm.add_args('-kernel', kernel_path, '-initrd', initrd.name, - '-m', '4096') - self.vm.set_qmp_monitor(enabled=False) - self.vm.launch() - self.vm.wait() - self.assertEqual(self.vm.exitcode(), 1) - expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % ( - max_size + 1) - self.assertRegex(self.vm.get_log(), expected_msg) - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_with_2gib_file_should_work_with_linux_v4_16(self): - """ - QEMU has supported up to 4 GiB initrd for recent kernel - Expect guest can reach 'Unpacking initramfs...' - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/28/Everything/x86_64/os/images/pxeboot/' - 'vmlinuz') - kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - max_size = 2 * (1024 ** 3) + 1 - - with tempfile.NamedTemporaryFile() as initrd: - initrd.seek(max_size) - initrd.write(b'\0') - initrd.flush() - - self.vm.set_console() - kernel_command_line = 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line, - '-initrd', initrd.name, - '-m', '5120') - self.vm.launch() - console = self.vm.console_socket.makefile() - console_logger = logging.getLogger('console') - while True: - msg = console.readline() - console_logger.debug(msg.strip()) - if 'Unpacking initramfs...' in msg: - break - if 'Kernel panic - not syncing' in msg: - self.fail("Kernel panic reached") 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/load_bflt.py b/tests/avocado/load_bflt.py deleted file mode 100644 index bb50cec1ee..0000000000 --- a/tests/avocado/load_bflt.py +++ /dev/null @@ -1,54 +0,0 @@ -# Test the bFLT loader format -# -# Copyright (C) 2019 Philippe Mathieu-Daudé -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import bz2 -import subprocess - -from avocado import skipUnless -from avocado_qemu import QemuUserTest -from avocado_qemu import has_cmd - - -class LoadBFLT(QemuUserTest): - - def extract_cpio(self, cpio_path): - """ - Extracts a cpio archive into the test workdir - - :param cpio_path: path to the cpio archive - """ - cwd = os.getcwd() - os.chdir(self.workdir) - with bz2.open(cpio_path, 'rb') as archive_cpio: - subprocess.run(['cpio', '-i'], input=archive_cpio.read(), - stderr=subprocess.DEVNULL) - os.chdir(cwd) - - @skipUnless(*has_cmd('cpio')) - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_stm32(self): - """ - :avocado: tags=arch:arm - :avocado: tags=linux_user - :avocado: tags=quick - """ - # See https://elinux.org/STM32#User_Space - rootfs_url = ('https://elinux.org/images/5/51/' - 'Stm32_mini_rootfs.cpio.bz2') - rootfs_hash = '9f065e6ba40cce7411ba757f924f30fcc57951e6' - rootfs_path_bz2 = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - busybox_path = os.path.join(self.workdir, "/bin/busybox") - - self.extract_cpio(rootfs_path_bz2) - - res = self.run(busybox_path) - ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' - self.assertIn(ver, res.stdout_text) - - res = self.run(busybox_path, ['uname', '-a']) - unm = 'armv7l GNU/Linux' - self.assertIn(unm, res.stdout_text) diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/avocado/machine_aarch64_virt.py deleted file mode 100644 index c2b2ba2cf8..0000000000 --- a/tests/avocado/machine_aarch64_virt.py +++ /dev/null @@ -1,95 +0,0 @@ -# Functional test that boots a various Linux systems and checks the -# console output. -# -# Copyright (c) 2022 Linaro Ltd. -# -# Author: -# Alex Bennée -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import time -import os - -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 - -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) - - # 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 - """ - iso_url = ('https://dl-cdn.alpinelinux.org/' - 'alpine/v3.16/releases/aarch64/' - 'alpine-virt-3.16.3-aarch64.iso') - - # Alpine use sha256 so I recalculated this myself - iso_sha1 = '0683bc089486d55c91bf6607d5ecb93925769bc0' - 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.16') - - - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - """ - kernel_url = ('https://fileserver.linaro.org/s/' - 'z6B2ARM7DQT3HWN/download') - - kernel_hash = 'ed11daab50c151dde0e1e9c9cb8b2d9bd3215347' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - self.require_accelerator("tcg") - self.vm.add_args('-cpu', 'max,pauth-impdef=on', - '-accel', 'tcg', - '-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - self.wait_for_console_pattern('Welcome to Buildroot') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command(self, 'cat /proc/self/maps') - time.sleep(0.1) diff --git a/tests/avocado/machine_arm_canona1100.py b/tests/avocado/machine_arm_canona1100.py deleted file mode 100644 index a42d8b0f2b..0000000000 --- a/tests/avocado/machine_arm_canona1100.py +++ /dev/null @@ -1,35 +0,0 @@ -# Functional test that boots the canon-a1100 machine with firmware -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Thomas Huth -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive - -class CanonA1100Machine(QemuSystemTest): - """Boots the barebox firmware and checks that the console is operational""" - - timeout = 90 - - def test_arm_canona1100(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:canon-a1100 - :avocado: tags=device:pflash_cfi02 - """ - 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) - self.vm.set_console() - self.vm.add_args('-bios', - self.workdir + '/day18/barebox.canon-a1100.bin') - self.vm.launch() - wait_for_console_pattern(self, 'running /env/bin/init') diff --git a/tests/avocado/machine_arm_integratorcp.py b/tests/avocado/machine_arm_integratorcp.py deleted file mode 100644 index 1ffe1073ef..0000000000 --- a/tests/avocado/machine_arm_integratorcp.py +++ /dev/null @@ -1,99 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Thomas Huth -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import logging - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - - -NUMPY_AVAILABLE = True -try: - import numpy as np -except ImportError: - NUMPY_AVAILABLE = False - -CV2_AVAILABLE = True -try: - import cv2 -except ImportError: - CV2_AVAILABLE = False - - -class IntegratorMachine(QemuSystemTest): - - timeout = 90 - - def boot_integratorcp(self): - kernel_url = ('https://github.com/zayac/qemu-arm/raw/master/' - 'arm-test/kernel/zImage.integrator') - kernel_hash = '0d7adba893c503267c946a3cbdc63b4b54f25468' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = ('https://github.com/zayac/qemu-arm/raw/master/' - 'arm-test/kernel/arm_root.img') - initrd_hash = 'b51e4154285bf784e017a37586428332d8c7bd8b' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-append', 'printk.time=0 console=ttyAMA0') - self.vm.launch() - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_integratorcp_console(self): - """ - Boots the Linux kernel and checks that the console is operational - :avocado: tags=arch:arm - :avocado: tags=machine:integratorcp - :avocado: tags=device:pl011 - """ - self.boot_integratorcp() - wait_for_console_pattern(self, 'Log in as root') - - @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') - @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_framebuffer_tux_logo(self): - """ - Boot Linux and verify the Tux logo is displayed on the framebuffer. - :avocado: tags=arch:arm - :avocado: tags=machine:integratorcp - :avocado: tags=device:pl110 - :avocado: tags=device:framebuffer - """ - screendump_path = os.path.join(self.workdir, "screendump.pbm") - tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/' - 'drivers/video/logo/logo_linux_vga16.ppm') - tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af' - tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash) - - self.boot_integratorcp() - framebuffer_ready = 'Console: switching to colour frame buffer device' - wait_for_console_pattern(self, framebuffer_ready) - self.vm.command('human-monitor-command', command_line='stop') - self.vm.command('human-monitor-command', - command_line='screendump %s' % screendump_path) - logger = logging.getLogger('framebuffer') - - cpu_count = 1 - match_threshold = 0.92 - screendump_bgr = cv2.imread(screendump_path) - screendump_gray = cv2.cvtColor(screendump_bgr, cv2.COLOR_BGR2GRAY) - result = cv2.matchTemplate(screendump_gray, cv2.imread(tuxlogo_path, 0), - cv2.TM_CCOEFF_NORMED) - loc = np.where(result >= match_threshold) - tux_count = 0 - for tux_count, pt in enumerate(zip(*loc[::-1]), start=1): - logger.debug('found Tux at position [x, y] = %s', pt) - self.assertGreaterEqual(tux_count, cpu_count) diff --git a/tests/avocado/machine_arm_n8x0.py b/tests/avocado/machine_arm_n8x0.py deleted file mode 100644 index 12e9a6803b..0000000000 --- a/tests/avocado/machine_arm_n8x0.py +++ /dev/null @@ -1,49 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Thomas Huth -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class N8x0Machine(QemuSystemTest): - """Boots the Linux kernel and checks that the console is operational""" - - timeout = 90 - - def __do_test_n8x0(self): - kernel_url = ('http://stskeeps.subnetmask.net/meego-n8x0/' - 'meego-arm-n8x0-1.0.80.20100712.1431-' - 'vmlinuz-2.6.35~rc4-129.1-n8x0') - kernel_hash = 'e9d5ab8d7548923a0061b6fbf601465e479ed269' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console(console_index=1) - self.vm.add_args('-kernel', kernel_path, - '-append', 'printk.time=0 console=ttyS1') - self.vm.launch() - wait_for_console_pattern(self, 'TSC2005 driver initializing') - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_n800(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:n800 - """ - self.__do_test_n8x0() - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_n810(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:n810 - """ - self.__do_test_n8x0() diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py deleted file mode 100644 index 1fc385e1c8..0000000000 --- a/tests/avocado/machine_aspeed.py +++ /dev/null @@ -1,272 +0,0 @@ -# Functional test that boots the ASPEED SoCs with firmware -# -# Copyright (C) 2022 ASPEED Technology Inc -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import time -import os - -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.utils import archive -from avocado import skipIf - - -class AST1030Machine(QemuSystemTest): - """Boots the zephyr os and checks that the console is operational""" - - timeout = 10 - - def test_ast1030_zephyros(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast1030-evb - """ - tar_url = ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip') - tar_hash = '4c6a8ce3a8ba76ef1a65dae419ae3409343c4b20' - tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(tar_path, self.workdir) - kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.elf" - self.vm.set_console() - self.vm.add_args('-kernel', kernel_file, - '-nographic') - self.vm.launch() - wait_for_console_pattern(self, "Booting Zephyr OS") - exec_command_and_wait_for_pattern(self, "help", - "Available commands") - -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): - 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('Aspeed EVB') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - - 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-2022.05/flash.img') - image_hash = ('549db6e9d8cdaf4367af21c36385a68bb465779c18b5e37094fc7343decccd3f') - 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') - - 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.command('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-2022.05/flash.img') - image_hash = ('6cc9e7d128fd4fa1fd01c883af67593cae8072c3239a0b8b6ace857f3538a92d') - 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.do_test_arm_aspeed_buildroot_start(image_path, '0xf00') - - 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/hwmon0/temp1_input', '0') - self.vm.command('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon0/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); - - self.do_test_arm_aspeed_buildroot_poweroff() - - -class AST2x00MachineSDK(QemuSystemTest): - - EXTRA_BOOTARGS = ' quiet' - - # 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') - 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 ...') - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_arm_ast2500_evb_sdk(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2500-evb - """ - - image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' - 'download/v08.01/ast2500-default-obmc.tar.gz') - image_hash = ('5375f82b4c43a79427909342a1e18b4e48bd663e38466862145d27bb358796fd') - 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('ast2500-default login:') - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_arm_ast2600_evb_sdk(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2600-evb - """ - - image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' - 'download/v08.01/ast2600-default-obmc.tar.gz') - image_hash = ('f12ef15e8c1f03a214df3b91c814515c5e2b2f56119021398c1dbdd626817d15') - 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-default/image-bmc') - self.wait_for_console_pattern('ast2600-default login:') - exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, '0penBmc', 'root@ast2600-default:~#') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') - self.vm.command('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') - - exec_command_and_wait_for_pattern(self, - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); - year = time.strftime("%Y") - exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); diff --git a/tests/avocado/machine_avr6.py b/tests/avocado/machine_avr6.py deleted file mode 100644 index 5485db79c6..0000000000 --- a/tests/avocado/machine_avr6.py +++ /dev/null @@ -1,50 +0,0 @@ -# -# QEMU AVR integration tests -# -# Copyright (c) 2019-2020 Michael Rolnik -# -# 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 time - -from avocado_qemu import QemuSystemTest - -class AVR6Machine(QemuSystemTest): - timeout = 5 - - def test_freertos(self): - """ - :avocado: tags=arch:avr - :avocado: tags=machine:arduino-mega-2560-v3 - """ - """ - https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf - constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX' - """ - rom_url = ('https://github.com/seharris/qemu-avr-tests' - '/raw/36c3e67b8755dcf/free-rtos/Demo' - '/AVR_ATMega2560_GCC/demo.elf') - rom_hash = '7eb521f511ca8f2622e0a3c5e8dd686efbb911d4' - rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) - - self.vm.add_args('-bios', rom_path) - self.vm.add_args('-nographic') - self.vm.launch() - - time.sleep(2) - self.vm.shutdown() - - self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX', - self.vm.get_log()) diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py deleted file mode 100644 index 6790e7d9cd..0000000000 --- a/tests/avocado/machine_m68k_nextcube.py +++ /dev/null @@ -1,79 +0,0 @@ -# Functional test that boots a VM and run OCR on the framebuffer -# -# Copyright (c) 2019 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. - -import os -import time - -from avocado_qemu import QemuSystemTest -from avocado import skipUnless - -from tesseract_utils import tesseract_available, tesseract_ocr - -PIL_AVAILABLE = True -try: - from PIL import Image -except ImportError: - PIL_AVAILABLE = False - - -class NextCubeMachine(QemuSystemTest): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:next-cube - :avocado: tags=device:framebuffer - """ - - 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_hash = 'b3534796abae238a0111299fc406a9349f7fee24' - rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) - - self.vm.add_args('-bios', rom_path) - self.vm.launch() - - self.log.info('VM launched, waiting for display') - # TODO: Use avocado.utils.wait.wait_for to catch the - # 'displaysurface_create 1120x832' trace-event. - time.sleep(2) - - self.vm.command('human-monitor-command', - command_line='screendump %s' % screenshot_path) - - @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') - def test_bootrom_framebuffer_size(self): - screenshot_path = os.path.join(self.workdir, "dump.ppm") - self.check_bootrom_framebuffer(screenshot_path) - - width, height = Image.open(screenshot_path).size - 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): - 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('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 deleted file mode 100644 index 8d0efff30d..0000000000 --- a/tests/avocado/machine_microblaze.py +++ /dev/null @@ -1,35 +0,0 @@ -# Functional test that boots a microblaze Linux kernel and checks the console -# -# Copyright (c) 2018, 2021 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_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive - -class MicroblazeMachine(QemuSystemTest): - - timeout = 90 - - def test_microblaze_s3adsp1800(self): - """ - :avocado: tags=arch:microblaze - :avocado: tags=machine:petalogix-s3adsp1800 - """ - - 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) - self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin') - self.vm.launch() - wait_for_console_pattern(self, 'This architecture does not have ' - 'kernel memory protection') - # Note: - # 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... diff --git a/tests/avocado/machine_mips_fuloong2e.py b/tests/avocado/machine_mips_fuloong2e.py deleted file mode 100644 index 89291f47b2..0000000000 --- a/tests/avocado/machine_mips_fuloong2e.py +++ /dev/null @@ -1,42 +0,0 @@ -# Functional tests for the Lemote Fuloong-2E machine. -# -# Copyright (c) 2019 Philippe Mathieu-Daudé -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class MipsFuloong2e(QemuSystemTest): - - timeout = 60 - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - @skipUnless(os.getenv('RESCUE_YL_PATH'), 'RESCUE_YL_PATH not available') - def test_linux_kernel_isa_serial(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:fuloong2e - :avocado: tags=endian:little - :avocado: tags=device:bonito64 - :avocado: tags=device:via686b - """ - # Recovery system for the Yeeloong laptop - # (enough to test the fuloong2e southbridge, accessing its ISA bus) - # http://dev.lemote.com/files/resource/download/rescue/rescue-yl - kernel_hash = 'ec4d1bd89a8439c41033ca63db60160cc6d6f09a' - kernel_path = self.fetch_asset('file://' + os.getenv('RESCUE_YL_PATH'), - asset_hash=kernel_hash) - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path) - self.vm.launch() - wait_for_console_pattern(self, 'Linux version 2.6.27.7lemote') - cpu_revision = 'CPU revision is: 00006302 (ICT Loongson-2)' - wait_for_console_pattern(self, cpu_revision) diff --git a/tests/avocado/machine_mips_loongson3v.py b/tests/avocado/machine_mips_loongson3v.py deleted file mode 100644 index 5194cf18c9..0000000000 --- a/tests/avocado/machine_mips_loongson3v.py +++ /dev/null @@ -1,39 +0,0 @@ -# Functional tests for the Generic Loongson-3 Platform. -# -# Copyright (c) 2021 Jiaxun Yang -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import time - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class MipsLoongson3v(QemuSystemTest): - timeout = 60 - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_pmon_serial_console(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=endian:little - :avocado: tags=machine:loongson3-virt - :avocado: tags=cpu:Loongson-3A1000 - :avocado: tags=device:liointc - :avocado: tags=device:goldfish_rtc - """ - - pmon_hash = '7c8b45dd81ccfc55ff28f5aa267a41c3' - pmon_path = self.fetch_asset('https://github.com/loongson-community/pmon/' - 'releases/download/20210112/pmon-3avirt.bin', - asset_hash=pmon_hash, algorithm='md5') - - self.vm.set_console() - self.vm.add_args('-bios', pmon_path) - self.vm.launch() - wait_for_console_pattern(self, 'CPU GODSON3 BogoMIPS:') diff --git a/tests/avocado/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py deleted file mode 100644 index f1895d59f3..0000000000 --- a/tests/avocado/machine_mips_malta.py +++ /dev/null @@ -1,120 +0,0 @@ -# Functional tests for the MIPS Malta board -# -# Copyright (c) Philippe Mathieu-Daudé -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import gzip -import logging - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive -from avocado import skipIf - - -NUMPY_AVAILABLE = True -try: - import numpy as np -except ImportError: - NUMPY_AVAILABLE = False - -CV2_AVAILABLE = True -try: - import cv2 -except ImportError: - CV2_AVAILABLE = False - - -@skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') -@skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') -class MaltaMachineFramebuffer(QemuSystemTest): - - timeout = 30 - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - def do_test_i6400_framebuffer_logo(self, cpu_cores_count): - """ - Boot Linux kernel and check Tux logo is displayed on the framebuffer. - """ - screendump_path = os.path.join(self.workdir, 'screendump.pbm') - - kernel_url = ('https://github.com/philmd/qemu-testing-blob/raw/' - 'a5966ca4b5/mips/malta/mips64el/' - 'vmlinux-4.7.0-rc1.I6400.gz') - kernel_hash = '096f50c377ec5072e6a366943324622c312045f6' - kernel_path_gz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - kernel_path = self.workdir + "vmlinux" - archive.gzip_uncompress(kernel_path_gz, kernel_path) - - tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/' - 'drivers/video/logo/logo_linux_vga16.ppm') - tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af' - tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'clocksource=GIC console=tty0 console=ttyS0') - self.vm.add_args('-kernel', kernel_path, - '-smp', '%u' % cpu_cores_count, - '-vga', 'std', - '-append', kernel_command_line) - self.vm.launch() - framebuffer_ready = 'Console: switching to colour frame buffer device' - wait_for_console_pattern(self, framebuffer_ready, - failure_message='Kernel panic - not syncing') - self.vm.command('human-monitor-command', command_line='stop') - self.vm.command('human-monitor-command', - command_line='screendump %s' % screendump_path) - logger = logging.getLogger('framebuffer') - - match_threshold = 0.95 - screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR) - tuxlogo_bgr = cv2.imread(tuxlogo_path, cv2.IMREAD_COLOR) - result = cv2.matchTemplate(screendump_bgr, tuxlogo_bgr, - cv2.TM_CCOEFF_NORMED) - loc = np.where(result >= match_threshold) - tuxlogo_count = 0 - h, w = tuxlogo_bgr.shape[:2] - debug_png = os.getenv('AVOCADO_CV2_SCREENDUMP_PNG_PATH') - for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1): - logger.debug('found Tux at position (x, y) = %s', pt) - cv2.rectangle(screendump_bgr, pt, - (pt[0] + w, pt[1] + h), (0, 0, 255), 2) - if debug_png: - cv2.imwrite(debug_png, screendump_bgr) - self.assertGreaterEqual(tuxlogo_count, cpu_cores_count) - - def test_mips_malta_i6400_framebuffer_logo_1core(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=cpu:I6400 - """ - self.do_test_i6400_framebuffer_logo(1) - - @skipIf(os.getenv('GITLAB_CI'), 'Running 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 - """ - self.do_test_i6400_framebuffer_logo(7) - - @skipIf(os.getenv('GITLAB_CI'), 'Running 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 - """ - self.do_test_i6400_framebuffer_logo(8) diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/avocado/machine_rx_gdbsim.py deleted file mode 100644 index 6cd8704b01..0000000000 --- a/tests/avocado/machine_rx_gdbsim.py +++ /dev/null @@ -1,73 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado import skipIf -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive - - -class RxGdbSimMachine(QemuSystemTest): - - timeout = 30 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - @skipIf(os.getenv('GITLAB_CI'), 'Running on GitLab') - def test_uboot(self): - """ - U-Boot and checks that the console is operational. - - :avocado: tags=arch:rx - :avocado: tags=machine:gdbsim-r5f562n8 - :avocado: tags=endian:little - """ - uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz') - uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb' - uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) - uboot_path = archive.uncompress(uboot_path, self.workdir) - - self.vm.set_console() - self.vm.add_args('-bios', uboot_path, - '-no-reboot') - self.vm.launch() - uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty' - wait_for_console_pattern(self, uboot_version) - gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)' - # 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') - def test_linux_sash(self): - """ - Boots a Linux kernel and checks that the console is operational. - - :avocado: tags=arch:rx - :avocado: tags=machine:gdbsim-r5f562n7 - :avocado: tags=endian:little - """ - dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb') - dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18' - dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash) - kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage') - kernel_hash = '39a81067f8d72faad90866ddfefa19165d68fc99' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon' - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-no-reboot') - self.vm.launch() - wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)', - failure_message='Kernel panic - not syncing') - exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux') diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/avocado/machine_s390_ccw_virtio.py deleted file mode 100644 index 78152f2ad1..0000000000 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ /dev/null @@ -1,273 +0,0 @@ -# Functional test that boots an s390x Linux guest with ccw and PCI devices -# attached and checks whether the devices are recognized by Linux -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Cornelia Huck -# -# 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 tempfile - -from avocado import skipIf -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive - -class S390CCWVirtioMachine(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 wait_for_crw_reports(self): - exec_command_and_wait_for_pattern(self, - 'while ! (dmesg -c | grep CRW) ; do sleep 1 ; done', - 'CRW reports') - - 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)) - self.dmesg_clear_count += 1 - - def test_s390x_devices(self): - - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - - kernel_url = ('https://snapshot.debian.org/archive/debian/' - '20201126T092837Z/dists/buster/main/installer-s390x/' - '20190702+deb10u6/images/generic/kernel.debian') - kernel_hash = '5821fbee57d6220a067a8b967d24595621aa1eb6' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = ('https://snapshot.debian.org/archive/debian/' - '20201126T092837Z/dists/buster/main/installer-s390x/' - '20190702+deb10u6/images/generic/initrd.debian') - initrd_hash = '81ba09c97bef46e8f4660ac25b4ac0a5be3a94d6' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=sclp0 root=/dev/ram0 BOOT_DEBUG=3') - self.vm.add_args('-nographic', - '-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', - '-device', - 'virtio-rng-ccw,devno=fe.3.1234,max_revision=2,id=rn2', - '-device', 'zpci,uid=5,target=zzz', - '-device', 'virtio-net-pci,id=zzz', - '-device', 'zpci,uid=0xa,fid=12,target=serial', - '-device', 'virtio-serial-pci,id=serial', - '-device', 'virtio-balloon-ccw') - self.vm.launch() - - shell_ready = "sh: can't access tty; job control turned off" - self.wait_for_console_pattern(shell_ready) - # first debug shell is too early, we need to wait for device detection - exec_command_and_wait_for_pattern(self, 'exit', shell_ready) - - ccw_bus_ids="0.1.1111 0.2.0000 0.3.1234" - pci_bus_ids="0005:00:00.0 000a:00:00.0" - exec_command_and_wait_for_pattern(self, 'ls /sys/bus/ccw/devices/', - ccw_bus_ids) - exec_command_and_wait_for_pattern(self, 'ls /sys/bus/pci/devices/', - pci_bus_ids) - # check that the device at 0.2.0000 is in legacy mode, while the - # device at 0.3.1234 has the virtio-1 feature bit set - virtio_rng_features="00000000000000000000000000001100" + \ - "10000000000000000000000000000000" - virtio_rng_features_legacy="00000000000000000000000000001100" + \ - "00000000000000000000000000000000" - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/ccw/devices/0.2.0000/virtio?/features', - virtio_rng_features_legacy) - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/ccw/devices/0.3.1234/virtio?/features', - virtio_rng_features) - # check that /dev/hwrng works - and that it's gone after ejecting - exec_command_and_wait_for_pattern(self, - '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.wait_for_crw_reports() - self.clear_guest_dmesg() - self.vm.command('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', - 'dd: /dev/hwrng: No such device') - # verify that we indeed have virtio-net devices (without having the - # virtio-net driver handy) - exec_command_and_wait_for_pattern(self, - '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') - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device', - '0x0001') - # check fid propagation - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id', - '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.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 ;' - 'sleep 1 ; done ; ls /sys/bus/ccw/devices/', - '0.0.4711') - # and detach it again - self.clear_guest_dmesg() - self.vm.command('device_del', id='net_4711') - self.vm.event_wait(name='DEVICE_DELETED', - match={'data': {'device': 'net_4711'}}) - self.wait_for_crw_reports() - exec_command_and_wait_for_pattern(self, - 'ls /sys/bus/ccw/devices/0.0.4711', - 'No such file or directory') - # 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') - 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') - 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): - - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - :avocado: tags=device:virtio-gpu - :avocado: tags=device:virtio-crypto - :avocado: tags=device:virtio-net - """ - - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/31/Server/s390x/os' - '/images/kernel.img') - kernel_hash = 'b93d1efcafcf29c1673a4ce371a1f8b43941cfeb' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/31/Server/s390x/os' - '/images/initrd.img') - initrd_hash = '3de45d411df5624b8d8ef21cd0b44419ab59b12f' - initrd_path_xz = self.fetch_asset(initrd_url, 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 + ' audit=0 ' - 'rd.plymouth=0 plymouth.enable=0 rd.rescue') - self.vm.add_args('-nographic', - '-smp', '4', - '-m', '512', - '-name', 'Some Guest Name', - '-uuid', '30de4fd9-b4d5-409e-86a5-09b387f70bfa', - '-kernel', kernel_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-device', 'zpci,uid=7,target=n', - '-device', 'virtio-net-pci,id=n,mac=02:ca:fe:fa:ce:12', - '-device', 'virtio-rng-ccw,devno=fe.1.9876', - '-device', 'virtio-gpu-ccw,devno=fe.2.5432') - self.vm.launch() - self.wait_for_console_pattern('Entering emergency mode') - - # Some tests to see whether the CLI options have been considered: - self.log.info("Test whether QEMU CLI options have been considered") - exec_command_and_wait_for_pattern(self, - 'while ! (dmesg | grep enP7p0s0) ; do sleep 1 ; done', - 'virtio_net virtio0 enP7p0s0: renamed') - exec_command_and_wait_for_pattern(self, 'lspci', - '0007:00:00.0 Class 0200: Device 1af4:1000') - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/net/enP7p0s0/address', - '02:ca:fe:fa:ce:12') - exec_command_and_wait_for_pattern(self, 'lscss', '0.1.9876') - exec_command_and_wait_for_pattern(self, 'lscss', '0.2.5432') - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'processors : 4') - exec_command_and_wait_for_pattern(self, 'grep MemTotal /proc/meminfo', - 'MemTotal: 499848 kB') - exec_command_and_wait_for_pattern(self, 'grep Name /proc/sysinfo', - 'Extended Name: Some Guest Name') - exec_command_and_wait_for_pattern(self, 'grep UUID /proc/sysinfo', - '30de4fd9-b4d5-409e-86a5-09b387f70bfa') - - # Disable blinking cursor, then write some stuff into the framebuffer. - # QEMU's PPM screendumps contain uncompressed 24-bit values, while the - # framebuffer uses 32-bit, so we pad our text with some spaces when - # 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, - '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") - - # 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') - 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') - 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 deleted file mode 100644 index d333c0ae91..0000000000 --- a/tests/avocado/machine_sparc64_sun4u.py +++ /dev/null @@ -1,36 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Thomas Huth -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive -from boot_linux_console import LinuxKernelTest - -class Sun4uMachine(LinuxKernelTest): - """Boots the Linux kernel and checks that the console is operational""" - - timeout = 90 - - def test_sparc64_sun4u(self): - """ - :avocado: tags=arch:sparc64 - :avocado: tags=machine:sun4u - """ - 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) - self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day23/vmlinux', - '-append', self.KERNEL_COMMON_COMMAND_LINE) - self.vm.launch() - wait_for_console_pattern(self, 'Starting logging: OK') diff --git a/tests/avocado/machine_sparc_leon3.py b/tests/avocado/machine_sparc_leon3.py deleted file mode 100644 index e61b223185..0000000000 --- a/tests/avocado/machine_sparc_leon3.py +++ /dev/null @@ -1,37 +0,0 @@ -# Functional test that boots a Leon3 machine and checks its serial console. -# -# Copyright (c) Philippe Mathieu-Daudé -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado import skip - - -class Leon3Machine(QemuSystemTest): - - timeout = 60 - - @skip("Test currently broken") - # A Window Underflow exception occurs before booting the kernel, - # and QEMU exit calling cpu_abort(), which makes this test to fail. - def test_leon3_helenos_uimage(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:leon3_generic - :avocado: tags=binfmt:uimage - """ - kernel_url = ('http://www.helenos.org/releases/' - 'HelenOS-0.6.0-sparc32-leon3.bin') - kernel_hash = 'a88c9cfdb8430c66650e5290a08765f9bf049a30' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path) - - self.vm.launch() - - wait_for_console_pattern(self, 'Copyright (c) 2001-2014 HelenOS project') - wait_for_console_pattern(self, 'Booting the kernel ...') diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py index 4b25680c50..be6234b3c2 100644 --- a/tests/avocado/migration.py +++ b/tests/avocado/migration.py @@ -11,6 +11,8 @@ import tempfile +import os + from avocado_qemu import QemuSystemTest from avocado import skipUnless @@ -19,7 +21,7 @@ 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) @@ -62,20 +64,72 @@ class Migration(QemuSystemTest): 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 deleted file mode 100644 index 80a3b8f442..0000000000 --- a/tests/avocado/multiprocess.py +++ /dev/null @@ -1,95 +0,0 @@ -# Test for multiprocess qemu -# -# 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 socket - -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 - -class Multiprocess(QemuSystemTest): - """ - :avocado: tags=multiprocess - """ - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - def do_test(self, kernel_url, initrd_url, kernel_command_line, - machine_type): - """Main test method""" - self.require_accelerator('kvm') - - # Create socketpair to connect proxy and remote processes - proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, - socket.SOCK_STREAM) - 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) - - # Create remote process - remote_vm = self.get_vm() - remote_vm.add_args('-machine', 'x-remote') - remote_vm.add_args('-nodefaults') - remote_vm.add_args('-device', 'lsi53c895a,id=lsi1') - remote_vm.add_args('-object', 'x-remote-object,id=robj1,' - 'devid=lsi1,fd='+str(remote_sock.fileno())) - remote_vm.launch() - - # Create proxy process - self.vm.set_console() - self.vm.add_args('-machine', machine_type) - self.vm.add_args('-accel', 'kvm') - self.vm.add_args('-cpu', 'host') - self.vm.add_args('-object', - 'memory-backend-memfd,id=sysmem-file,size=2G') - self.vm.add_args('--numa', 'node,memdev=sysmem-file') - self.vm.add_args('-m', '2048') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-append', kernel_command_line) - self.vm.add_args('-device', - 'x-pci-proxy-dev,' - 'id=lsi1,fd='+str(proxy_sock.fileno())) - self.vm.launch() - wait_for_console_pattern(self, 'as init process', - 'Kernel panic - not syncing') - exec_command(self, 'mount -t sysfs sysfs /sys') - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/*/uevent', - 'PCI_ID=1000:0012') - - def test_multiprocess_x86_64(self): - """ - :avocado: tags=arch:x86_64 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/x86_64/os/images' - '/pxeboot/vmlinuz') - initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/x86_64/os/images' - '/pxeboot/initrd.img') - 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) - - def test_multiprocess_aarch64(self): - """ - :avocado: tags=arch:aarch64 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/aarch64/os/images' - '/pxeboot/vmlinuz') - initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/aarch64/os/images' - '/pxeboot/initrd.img') - 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) diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/avocado/pc_cpu_hotplug_props.py deleted file mode 100644 index 52b878188e..0000000000 --- a/tests/avocado/pc_cpu_hotplug_props.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# Ensure CPU die-id can be omitted on -device -# -# Copyright (c) 2019 Red Hat Inc -# -# Author: -# Eduardo Habkost -# -# 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 . -# - -from avocado_qemu import QemuSystemTest - -class OmittedCPUProps(QemuSystemTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=cpu:qemu64 - """ - def test_no_die_id(self): - self.vm.add_args('-nodefaults', '-S') - 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) diff --git a/tests/avocado/ppc_405.py b/tests/avocado/ppc_405.py deleted file mode 100644 index 4e7e01aa76..0000000000 --- a/tests/avocado/ppc_405.py +++ /dev/null @@ -1,36 +0,0 @@ -# Test that the U-Boot firmware boots on ppc 405 machines and check the console -# -# Copyright (c) 2021 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 -from avocado_qemu import exec_command_and_wait_for_pattern - -class Ppc405Machine(QemuSystemTest): - - timeout = 90 - - def do_test_ppc405(self): - uboot_url = ('https://gitlab.com/huth/u-boot/-/raw/' - 'taihu-2021-10-09/u-boot-taihu.bin') - uboot_hash = ('3208940e908a5edc7c03eab072c60f0dcfadc2ab'); - file_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) - self.vm.set_console(console_index=1) - self.vm.add_args('-bios', file_path) - self.vm.launch() - wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') - exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') - - def test_ppc_ref405ep(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:ref405ep - :avocado: tags=cpu:405ep - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.do_test_ppc405() diff --git a/tests/avocado/ppc_74xx.py b/tests/avocado/ppc_74xx.py deleted file mode 100644 index f54757c243..0000000000 --- a/tests/avocado/ppc_74xx.py +++ /dev/null @@ -1,136 +0,0 @@ -# Smoke tests for 74xx cpus (aka G4). -# -# Copyright (c) 2021, 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. - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class ppc74xxCpu(QemuSystemTest): - """ - :avocado: tags=arch:ppc - :avocado: tags=accel:tcg - """ - timeout = 5 - - def test_ppc_7400(self): - """ - :avocado: tags=cpu:7400 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7410(self): - """ - :avocado: tags=cpu:7410 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,74xx') - - def test_ppc_7441(self): - """ - :avocado: tags=cpu:7441 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7445(self): - """ - :avocado: tags=cpu:7445 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7447(self): - """ - :avocado: tags=cpu:7447 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7447a(self): - """ - :avocado: tags=cpu:7447a - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7448(self): - """ - :avocado: tags=cpu:7448 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,MPC86xx') - - def test_ppc_7450(self): - """ - :avocado: tags=cpu:7450 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7451(self): - """ - :avocado: tags=cpu:7451 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7455(self): - """ - :avocado: tags=cpu:7455 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7457(self): - """ - :avocado: tags=cpu:7457 - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') - - def test_ppc_7457a(self): - """ - :avocado: tags=cpu:7457a - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> CPU type PowerPC,G4') diff --git a/tests/avocado/ppc_bamboo.py b/tests/avocado/ppc_bamboo.py deleted file mode 100644 index a81be3d608..0000000000 --- a/tests/avocado/ppc_bamboo.py +++ /dev/null @@ -1,42 +0,0 @@ -# Test that Linux kernel boots on the ppc bamboo board and check the console -# -# Copyright (c) 2021 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. - -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command_and_wait_for_pattern - -class BambooMachine(QemuSystemTest): - - timeout = 90 - - def test_ppc_bamboo(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:bamboo - :avocado: tags=cpu:440epb - :avocado: tags=device:rtl8139 - :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' - 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 + - '/system-image-powerpc-440fp/linux', - '-initrd', self.workdir + - '/system-image-powerpc-440fp/rootfs.cpio.gz', - '-nic', 'user,model=rtl8139,restrict=on') - self.vm.launch() - wait_for_console_pattern(self, 'Type exit when done') - exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', - '10.0.2.2 is alive!') - exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') diff --git a/tests/avocado/ppc_mpc8544ds.py b/tests/avocado/ppc_mpc8544ds.py deleted file mode 100644 index b599fb1cc9..0000000000 --- a/tests/avocado/ppc_mpc8544ds.py +++ /dev/null @@ -1,34 +0,0 @@ -# Test that Linux kernel boots on ppc 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 Mpc8544dsMachine(QemuSystemTest): - - timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - panic_message = 'Kernel panic - not syncing' - - def test_ppc_mpc8544ds(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mpc8544ds - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - 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() - self.vm.add_args('-kernel', self.workdir + '/creek/creek.bin') - self.vm.launch() - wait_for_console_pattern(self, 'QEMU advent calendar 2020', - self.panic_message) diff --git a/tests/avocado/ppc_prep_40p.py b/tests/avocado/ppc_prep_40p.py deleted file mode 100644 index d4f1eb7e1d..0000000000 --- a/tests/avocado/ppc_prep_40p.py +++ /dev/null @@ -1,85 +0,0 @@ -# Functional test that boots a PReP/40p machine and checks its serial console. -# -# Copyright (c) Philippe Mathieu-Daudé -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - - -class IbmPrep40pMachine(QemuSystemTest): - - timeout = 60 - - # 12H0455 PPS Firmware Licensed Materials - # Property of IBM (C) Copyright IBM Corp. 1994. - # All rights reserved. - # U.S. Government Users Restricted Rights - Use, duplication or disclosure - # restricted by GSA ADP Schedule Contract with IBM Corp. - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_factory_firmware_and_netbsd(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:40p - :avocado: tags=os:netbsd - :avocado: tags=slowness:high - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - bios_url = ('http://ftpmirror.your.org/pub/misc/' - 'ftp.software.ibm.com/rs6000/firmware/' - '7020-40p/P12H0456.IMG') - bios_hash = '1775face4e6dc27f3a6ed955ef6eb331bf817f03' - bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) - drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/' - 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs') - drive_hash = 'dbcfc09912e71bd5f0d82c7c1ee43082fb596ceb' - drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash) - - self.vm.set_console() - self.vm.add_args('-bios', bios_path, - '-fda', drive_path) - self.vm.launch() - os_banner = 'NetBSD 4.0 (GENERIC) #0: Sun Dec 16 00:49:40 PST 2007' - wait_for_console_pattern(self, os_banner) - wait_for_console_pattern(self, 'Model: IBM PPS Model 6015') - - def test_openbios_192m(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:40p - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.set_console() - self.vm.add_args('-m', '192') # test fw_cfg - - self.vm.launch() - wait_for_console_pattern(self, '>> OpenBIOS') - wait_for_console_pattern(self, '>> Memory: 192M') - wait_for_console_pattern(self, '>> CPU type PowerPC,604') - - def test_openbios_and_netbsd(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:40p - :avocado: tags=os:netbsd - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/' - 'NetBSD-7.1.2/iso/NetBSD-7.1.2-prep.iso') - drive_hash = 'ac6fa2707d888b36d6fa64de6e7fe48e' - drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash, - algorithm='md5') - self.vm.set_console() - self.vm.add_args('-cdrom', drive_path, - '-boot', 'd') - - self.vm.launch() - wait_for_console_pattern(self, 'NetBSD/prep BOOT, Revision 1.9') diff --git a/tests/avocado/ppc_pseries.py b/tests/avocado/ppc_pseries.py deleted file mode 100644 index d8b04dc3ea..0000000000 --- a/tests/avocado/ppc_pseries.py +++ /dev/null @@ -1,35 +0,0 @@ -# Test that Linux kernel boots on ppc 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 pseriesMachine(QemuSystemTest): - - timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - panic_message = 'Kernel panic - not syncing' - - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - 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() - 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 - wait_for_console_pattern(self, console_pattern, self.panic_message) diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/avocado/ppc_virtex_ml507.py deleted file mode 100644 index a73f8ae396..0000000000 --- a/tests/avocado/ppc_virtex_ml507.py +++ /dev/null @@ -1,36 +0,0 @@ -# Test that Linux kernel boots on ppc 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 VirtexMl507Machine(QemuSystemTest): - - timeout = 90 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - panic_message = 'Kernel panic - not syncing' - - def test_ppc_virtex_ml507(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:virtex-ml507 - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - 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() - self.vm.add_args('-kernel', self.workdir + '/hippo/hippo.linux', - '-dtb', self.workdir + '/hippo/virtex440-ml507.dtb', - '-m', '512') - self.vm.launch() - wait_for_console_pattern(self, 'QEMU advent calendar 2020', - self.panic_message) diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index 00a26e4a0c..e22c200a36 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -13,9 +13,10 @@ import lzma import shutil import logging import time +import subprocess 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 @@ -31,7 +32,7 @@ class ReplayKernelBase(LinuxKernelTest): terminates. """ - timeout = 120 + timeout = 180 KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' def run_vm(self, kernel_path, kernel_command_line, console_pattern, @@ -63,6 +64,8 @@ class ReplayKernelBase(LinuxKernelTest): vm.shutdown() logger.info('finished the recording with log size %s bytes' % os.path.getsize(replay_path)) + self.run_replay_dump(replay_path) + logger.info('successfully tested replay-dump.py') else: vm.wait() logger.info('successfully finished the replay') @@ -70,6 +73,14 @@ class ReplayKernelBase(LinuxKernelTest): logger.info('elapsed time %.2f sec' % elapsed) return elapsed + def run_replay_dump(self, replay_path): + try: + subprocess.check_call(["./scripts/replay-dump.py", + "-f", replay_path], + stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.fail('replay-dump.py failed') + def run_rr(self, kernel_path, kernel_command_line, console_pattern, shift=7, args=None): replay_path = os.path.join(self.workdir, 'replay.bin') @@ -81,11 +92,46 @@ 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'), 'pc machine is unstable with replay') 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' + '/vmlinuz') + kernel_hash = '23bebd2680757891cf7adedb033532163a792495' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + 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) + + def test_x86_64_q35(self): + """ + :avocado: tags=arch:x86_64 + :avocado: tags=machine:q35 """ kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' '/linux/releases/29/Everything/x86_64/os/images/pxeboot' @@ -178,19 +224,18 @@ 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 :avocado: tags=machine:cubieboard """ 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' + 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') + deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' 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/sun4i-a10-cubieboard.dtb' + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' dtb_path = self.extract_from_deb(deb_path, dtb_path) initrd_url = ('https://github.com/groeck/linux-build-test/raw/' '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' @@ -255,8 +300,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): @@ -314,7 +375,6 @@ class ReplayKernelNormal(ReplayKernelBase): 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 @@ -349,17 +409,6 @@ class ReplayKernelNormal(ReplayKernelBase): file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'vmlinux') - def test_nios2_10m50(self): - """ - :avocado: tags=arch:nios2 - :avocado: tags=machine:10m50-ghrd - """ - tar_hash = 'e4251141726c412ac0407c5a6bceefbbff018918' - 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') - def test_ppc_g3beige(self): """ :avocado: tags=arch:ppc @@ -492,7 +541,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 +555,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 +569,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 a76dd507fc..5916922435 100644 --- a/tests/avocado/replay_linux.py +++ b/tests/avocado/replay_linux.py @@ -19,7 +19,7 @@ from avocado.utils import network from avocado.utils import vmimage from avocado.utils import datadrainer from avocado.utils.path import find_command -from avocado_qemu import LinuxTest +from avocado_qemu.linuxtest import LinuxTest class ReplayLinux(LinuxTest): """ @@ -48,12 +48,15 @@ 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() @@ -65,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...') @@ -91,10 +94,12 @@ class ReplayLinux(LinuxTest): vm.shutdown() logger.info('finished the recording with log size %s bytes' % os.path.getsize(replay_path)) + self.run_replay_dump(replay_path) + logger.info('successfully tested replay-dump.py') 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 @@ -105,6 +110,14 @@ class ReplayLinux(LinuxTest): logger = logging.getLogger('replay') logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) + def run_replay_dump(self, replay_path): + try: + subprocess.check_call(["./scripts/replay-dump.py", + "-f", replay_path], + stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.fail('replay-dump.py failed') + @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') class ReplayLinuxX8664(ReplayLinux): """ diff --git a/tests/avocado/reverse_debugging.py b/tests/avocado/reverse_debugging.py index d2921e70c3..f24287cd0a 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,7 @@ 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 +216,14 @@ 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 +238,35 @@ 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/smmu.py b/tests/avocado/smmu.py index b3c4de6bf4..83fd79e922 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_qemu import LinuxTest, BUILD_DIR +from avocado import skipUnless +from avocado_qemu import BUILD_DIR +from avocado_qemu.linuxtest import LinuxTest -@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' @@ -30,7 +32,7 @@ class SMMU(LinuxTest): def set_up_boot(self): path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,scsi=off,' + + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + 'drive=drv0,id=virtio-disk0,bootindex=1,' 'werror=stop,rerror=stop' + self.IOMMU_ADDON) self.vm.add_args('-drive', diff --git a/tests/avocado/tcg_plugins.py b/tests/avocado/tcg_plugins.py deleted file mode 100644 index 642d2e49e3..0000000000 --- a/tests/avocado/tcg_plugins.py +++ /dev/null @@ -1,147 +0,0 @@ -# TCG Plugins tests -# -# These are a little more involved than the basic tests run by check-tcg. -# -# Copyright (c) 2021 Linaro -# -# Author: -# Alex Bennée -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import tempfile -import mmap -import re - -from boot_linux_console import LinuxKernelTest - - -class PluginKernelBase(LinuxKernelTest): - """ - Boots a Linux kernel with a TCG plugin enabled. - """ - - timeout = 120 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' - - def run_vm(self, kernel_path, kernel_command_line, - plugin, plugin_log, console_pattern, args=None): - - vm = self.get_vm() - vm.set_console() - vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line, - '-plugin', plugin, - '-d', 'plugin', - '-D', plugin_log, - '-net', 'none', - '-no-reboot') - if args: - vm.add_args(*args) - - try: - vm.launch() - except: - # TODO: probably fails because plugins not enabled but we - # can't currently probe for the feature. - self.cancel("TCG Plugins not enabled?") - - self.wait_for_console_pattern(console_pattern, vm) - # ensure logs are flushed - vm.shutdown() - - -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") - return kernel_path - - def test_aarch64_virt_insn(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_path = self._grab_aarch64_kernel() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' - - plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", - suffix=".log") - - self.run_vm(kernel_path, kernel_command_line, - "tests/plugin/libinsn.so", plugin_log.name, - console_pattern) - - with plugin_log as lf, \ - mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: - - m = re.search(br"insns: (?P\d+)", s) - if "count" not in m.groupdict(): - self.fail("Failed to find instruction count") - - def test_aarch64_virt_insn_icount(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_path = self._grab_aarch64_kernel() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' - - plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", - suffix=".log") - - self.run_vm(kernel_path, kernel_command_line, - "tests/plugin/libinsn.so", plugin_log.name, - console_pattern, - args=('-icount', 'shift=1')) - - 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") - - def test_aarch64_virt_mem_icount(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_path = self._grab_aarch64_kernel() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' - - plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", - suffix=".log") - - self.run_vm(kernel_path, kernel_command_line, - "tests/plugin/libmem.so,inline=true,callback=true", plugin_log.name, - console_pattern, - args=('-icount', 'shift=1')) - - with plugin_log as lf, \ - mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: - m = re.findall(br"mem accesses: (?P\d+)", s) - if m is None or len(m) != 2: - self.fail("no memory access counts found") - else: - inline = int(m[0]) - callback = int(m[1]) - if inline != callback: - self.fail("mismatched access counts") diff --git a/tests/avocado/tesseract_utils.py b/tests/avocado/tesseract_utils.py deleted file mode 100644 index 72cd9ab798..0000000000 --- a/tests/avocado/tesseract_utils.py +++ /dev/null @@ -1,46 +0,0 @@ -# ... -# -# Copyright (c) 2019 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. - -import re -import logging - -from avocado.utils import process -from avocado.utils.path import find_command, CmdNotFoundError - -def tesseract_available(expected_version): - try: - find_command('tesseract') - except CmdNotFoundError: - return False - res = process.run('tesseract --version') - try: - version = res.stdout_text.split()[1] - except IndexError: - version = res.stderr_text.split()[1] - 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 - - -def tesseract_ocr(image_path, tesseract_args='', tesseract_version=3): - console_logger = logging.getLogger('tesseract') - console_logger.debug(image_path) - if tesseract_version == 4: - tesseract_args += ' --oem 1' - proc = process.run("tesseract {} {} stdout".format(tesseract_args, - image_path)) - lines = [] - for line in proc.stdout_text.split('\n'): - sline = line.strip() - if len(sline): - console_logger.debug(sline) - lines += [sline] - return lines diff --git a/tests/avocado/version.py b/tests/avocado/version.py deleted file mode 100644 index ded7f039c1..0000000000 --- a/tests/avocado/version.py +++ /dev/null @@ -1,24 +0,0 @@ -# Version check example test -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - - -from avocado_qemu import QemuSystemTest - - -class Version(QemuSystemTest): - """ - :avocado: tags=quick - """ - def test_qmp_human_info_version(self): - self.vm.add_args('-nodefaults') - self.vm.launch() - res = self.vm.command('human-monitor-command', - command_line='info version') - self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') diff --git a/tests/avocado/virtio-gpu.py b/tests/avocado/virtio-gpu.py deleted file mode 100644 index 2a249a3a2c..0000000000 --- a/tests/avocado/virtio-gpu.py +++ /dev/null @@ -1,155 +0,0 @@ -# virtio-gpu tests -# -# 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 BUILD_DIR -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import is_readable_executable_file - -from qemu.utils import kvm_available - -import os -import socket -import subprocess - - -def pick_default_vug_bin(): - relative_path = "./contrib/vhost-user-gpu/vhost-user-gpu" - if is_readable_executable_file(relative_path): - return relative_path - - bld_dir_path = os.path.join(BUILD_DIR, relative_path) - if is_readable_executable_file(bld_dir_path): - return bld_dir_path - - -class VirtioGPUx86(QemuSystemTest): - """ - :avocado: tags=virtio-gpu - :avocado: tags=arch:x86_64 - :avocado: tags=cpu:host - """ - - KERNEL_COMMAND_LINE = "printk.time=0 console=ttyS0 rdinit=/bin/bash" - KERNEL_URL = ( - "https://archives.fedoraproject.org/pub/fedora" - "/linux/releases/33/Everything/x86_64/os/images" - "/pxeboot/vmlinuz" - ) - KERNEL_HASH = '1433cfe3f2ffaa44de4ecfb57ec25dc2399cdecf' - INITRD_URL = ( - "https://archives.fedoraproject.org/pub/fedora" - "/linux/releases/33/Everything/x86_64/os/images" - "/pxeboot/initrd.img" - ) - INITRD_HASH = 'c828d68a027b53e5220536585efe03412332c2d9' - - 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_virtio_vga_virgl(self): - """ - :avocado: tags=device:virtio-vga-gl - """ - # FIXME: should check presence of virtio, virgl etc - self.require_accelerator('kvm') - - kernel_path = self.fetch_asset(self.KERNEL_URL, self.KERNEL_HASH) - initrd_path = self.fetch_asset(self.INITRD_URL, self.INITRD_HASH) - - self.vm.set_console() - self.vm.add_args("-m", "2G") - self.vm.add_args("-machine", "pc,accel=kvm") - self.vm.add_args("-device", "virtio-vga-gl") - self.vm.add_args("-display", "egl-headless") - self.vm.add_args( - "-kernel", - kernel_path, - "-initrd", - initrd_path, - "-append", - self.KERNEL_COMMAND_LINE, - ) - 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") - - def test_vhost_user_vga_virgl(self): - """ - :avocado: tags=device:vhost-user-vga - """ - # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc - self.require_accelerator('kvm') - - vug = pick_default_vug_bin() - if not vug: - self.cancel("Could not find vhost-user-gpu") - - kernel_path = self.fetch_asset(self.KERNEL_URL, self.KERNEL_HASH) - initrd_path = self.fetch_asset(self.INITRD_URL, self.INITRD_HASH) - - # Create socketpair to connect proxy and remote processes - qemu_sock, vug_sock = socket.socketpair( - socket.AF_UNIX, socket.SOCK_STREAM - ) - os.set_inheritable(qemu_sock.fileno(), True) - os.set_inheritable(vug_sock.fileno(), True) - - self._vug_log_path = os.path.join( - self.logdir, "vhost-user-gpu.log" - ) - self._vug_log_file = open(self._vug_log_path, "wb") - self.log.info('Complete vhost-user-gpu.log file can be ' - 'found at %s', self._vug_log_path) - - vugp = subprocess.Popen( - [vug, "--virgl", "--fd=%d" % vug_sock.fileno()], - stdin=subprocess.DEVNULL, - stdout=self._vug_log_file, - stderr=subprocess.STDOUT, - shell=False, - close_fds=False, - ) - - self.vm.set_console() - self.vm.add_args("-m", "2G") - self.vm.add_args("-object", "memory-backend-memfd,id=mem,size=2G") - self.vm.add_args("-machine", "pc,memory-backend=mem,accel=kvm") - self.vm.add_args("-chardev", "socket,id=vug,fd=%d" % qemu_sock.fileno()) - self.vm.add_args("-device", "vhost-user-vga,chardev=vug") - self.vm.add_args("-display", "egl-headless") - self.vm.add_args( - "-kernel", - kernel_path, - "-initrd", - initrd_path, - "-append", - self.KERNEL_COMMAND_LINE, - ) - self.vm.launch() - 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") - self.vm.shutdown() - qemu_sock.close() - vugp.terminate() - vugp.wait() diff --git a/tests/avocado/virtio_check_params.py b/tests/avocado/virtio_check_params.py deleted file mode 100644 index 4093da8a67..0000000000 --- a/tests/avocado/virtio_check_params.py +++ /dev/null @@ -1,143 +0,0 @@ -# -# Test virtio-scsi and virtio-blk queue settings for all machine types -# -# Copyright (c) 2019 Virtuozzo International GmbH -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -import sys -import os -import re -import logging - -from qemu.machine import QEMUMachine -from avocado_qemu import QemuSystemTest -from avocado import skip - -#list of machine types and virtqueue properties to test -VIRTIO_SCSI_PROPS = {'seg_max_adjust': 'seg_max_adjust'} -VIRTIO_BLK_PROPS = {'seg_max_adjust': 'seg-max-adjust'} - -DEV_TYPES = {'virtio-scsi-pci': VIRTIO_SCSI_PROPS, - 'virtio-blk-pci': VIRTIO_BLK_PROPS} - -VM_DEV_PARAMS = {'virtio-scsi-pci': ['-device', 'virtio-scsi-pci,id=scsi0'], - 'virtio-blk-pci': ['-device', - 'virtio-blk-pci,id=scsi0,drive=drive0', - '-drive', - 'driver=null-co,id=drive0,if=none']} - - -class VirtioMaxSegSettingsCheck(QemuSystemTest): - @staticmethod - def make_pattern(props): - pattern_items = ['{0} = \w+'.format(prop) for prop in props] - return '|'.join(pattern_items) - - def query_virtqueue(self, vm, dev_type_name): - query_ok = False - error = None - props = None - - output = vm.command('human-monitor-command', - command_line = 'info qtree') - props_list = DEV_TYPES[dev_type_name].values(); - pattern = self.make_pattern(props_list) - res = re.findall(pattern, output) - - if len(res) != len(props_list): - props_list = set(props_list) - res = set(res) - not_found = props_list.difference(res) - not_found = ', '.join(not_found) - error = '({0}): The following properties not found: {1}'\ - .format(dev_type_name, not_found) - else: - query_ok = True - props = dict() - for prop in res: - p = prop.split(' = ') - props[p[0]] = p[1] - return query_ok, props, error - - def check_mt(self, mt, dev_type_name): - mt['device'] = dev_type_name # Only for the debug() call. - logger = logging.getLogger('machine') - logger.debug(mt) - with QEMUMachine(self.qemu_bin) as vm: - vm.set_machine(mt["name"]) - vm.add_args('-nodefaults') - for s in VM_DEV_PARAMS[dev_type_name]: - vm.add_args(s) - try: - vm.launch() - query_ok, props, error = self.query_virtqueue(vm, dev_type_name) - except: - query_ok = False - error = sys.exc_info()[0] - - if not query_ok: - self.fail('machine type {0}: {1}'.format(mt['name'], error)) - - for prop_name, prop_val in props.items(): - expected_val = mt[prop_name] - self.assertEqual(expected_val, prop_val) - - @staticmethod - def seg_max_adjust_enabled(mt): - # machine types >= 5.0 should have seg_max_adjust = true - # others seg_max_adjust = false - mt = mt.split("-") - - # machine types with one line name and name like pc-x.x - if len(mt) <= 2: - return False - - # machine types like pc--x.x[.x] - ver = mt[2] - ver = ver.split("."); - - # versions >= 5.0 goes with seg_max_adjust enabled - major = int(ver[0]) - - if major >= 5: - return True - return False - - @skip("break multi-arch CI") - def test_machine_types(self): - # collect all machine types except 'none', 'isapc', 'microvm' - with QEMUMachine(self.qemu_bin) as vm: - vm.launch() - machines = [m['name'] for m in vm.command('query-machines')] - vm.shutdown() - machines.remove('none') - machines.remove('isapc') - machines.remove('microvm') - - for dev_type in DEV_TYPES: - # create the list of machine types and their parameters. - mtypes = list() - for m in machines: - if self.seg_max_adjust_enabled(m): - enabled = 'true' - else: - enabled = 'false' - mtypes.append({'name': m, - DEV_TYPES[dev_type]['seg_max_adjust']: enabled}) - - # test each machine type for a device type - for mt in mtypes: - self.check_mt(mt, dev_type) diff --git a/tests/avocado/virtio_version.py b/tests/avocado/virtio_version.py deleted file mode 100644 index c84e48813a..0000000000 --- a/tests/avocado/virtio_version.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -Check compatibility of virtio device types -""" -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Eduardo Habkost -# -# 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 sys -import os - -from qemu.machine import QEMUMachine -from avocado_qemu import QemuSystemTest - -# Virtio Device IDs: -VIRTIO_NET = 1 -VIRTIO_BLOCK = 2 -VIRTIO_CONSOLE = 3 -VIRTIO_RNG = 4 -VIRTIO_BALLOON = 5 -VIRTIO_RPMSG = 7 -VIRTIO_SCSI = 8 -VIRTIO_9P = 9 -VIRTIO_RPROC_SERIAL = 11 -VIRTIO_CAIF = 12 -VIRTIO_GPU = 16 -VIRTIO_INPUT = 18 -VIRTIO_VSOCK = 19 -VIRTIO_CRYPTO = 20 - -PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 - -# Device IDs for legacy/transitional devices: -PCI_LEGACY_DEVICE_IDS = { - VIRTIO_NET: 0x1000, - VIRTIO_BLOCK: 0x1001, - VIRTIO_BALLOON: 0x1002, - VIRTIO_CONSOLE: 0x1003, - VIRTIO_SCSI: 0x1004, - VIRTIO_RNG: 0x1005, - VIRTIO_9P: 0x1009, - VIRTIO_VSOCK: 0x1012, -} - -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)] - -def get_pci_interfaces(vm, devtype): - interfaces = ('pci-express-device', 'conventional-pci-device') - return [i for i in interfaces if devtype_implements(vm, devtype, i)] - -class VirtioVersionCheck(QemuSystemTest): - """ - Check if virtio-version-specific device types result in the - same device tree created by `disable-modern` and - `disable-legacy`. - - :avocado: tags=arch:x86_64 - """ - - # just in case there are failures, show larger diff: - maxDiff = 4096 - - def run_device(self, devtype, opts=None, machine='pc'): - """ - Run QEMU with `-device DEVTYPE`, return device info from `query-pci` - """ - with QEMUMachine(self.qemu_bin) as vm: - vm.set_machine(machine) - if opts: - devtype += ',' + opts - vm.add_args('-device', '%s,id=devfortest' % (devtype)) - vm.add_args('-S') - vm.launch() - - pcibuses = vm.command('query-pci') - alldevs = [dev for bus in pcibuses for dev in bus['devices']] - devfortest = [dev for dev in alldevs - if dev['qdev_id'] == 'devfortest'] - return devfortest[0], get_pci_interfaces(vm, devtype) - - - def assert_devids(self, dev, devid, non_transitional=False): - self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) - self.assertEqual(dev['id']['device'], devid) - if non_transitional: - self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) - self.assertGreaterEqual(dev['id']['subsystem'], 0x40) - - def check_all_variants(self, qemu_devtype, virtio_devid): - """Check if a virtio device type and its variants behave as expected""" - # Force modern mode: - dev_modern, _ = self.run_device(qemu_devtype, - 'disable-modern=off,disable-legacy=on') - self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), - non_transitional=True) - - # -non-transitional device types should be 100% equivalent to - # ,disable-modern=off,disable-legacy=on - dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) - self.assertEqual(dev_modern, dev_1_0) - - # Force transitional mode: - dev_trans, _ = self.run_device(qemu_devtype, - 'disable-modern=off,disable-legacy=off') - self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) - - # Force legacy mode: - dev_legacy, _ = self.run_device(qemu_devtype, - 'disable-modern=on,disable-legacy=off') - self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) - - # No options: default to transitional on PC machine-type: - no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) - self.assertEqual(dev_trans, no_opts_pc) - - #TODO: check if plugging on a PCI Express bus will make the - # device non-transitional - #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') - #self.assertEqual(dev_modern, no_opts_q35) - - # -transitional device types should be 100% equivalent to - # ,disable-modern=off,disable-legacy=off - dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) - self.assertEqual(dev_trans, dev_trans) - - # ensure the interface information is correct: - self.assertIn('conventional-pci-device', generic_ifaces) - self.assertIn('pci-express-device', generic_ifaces) - - self.assertIn('conventional-pci-device', nt_ifaces) - self.assertIn('pci-express-device', nt_ifaces) - - self.assertIn('conventional-pci-device', trans_ifaces) - self.assertNotIn('pci-express-device', trans_ifaces) - - - def test_conventional_devs(self): - self.check_all_variants('virtio-net-pci', VIRTIO_NET) - # virtio-blk requires 'driver' parameter - #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) - self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) - self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) - self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) - self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) - # virtio-9p requires 'fsdev' parameter - #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) - - def check_modern_only(self, qemu_devtype, virtio_devid): - """Check if a modern-only virtio device type behaves as expected""" - # Force modern mode: - dev_modern, _ = self.run_device(qemu_devtype, - 'disable-modern=off,disable-legacy=on') - self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), - non_transitional=True) - - # No options: should be modern anyway - dev_no_opts, ifaces = self.run_device(qemu_devtype) - self.assertEqual(dev_modern, dev_no_opts) - - self.assertIn('conventional-pci-device', ifaces) - self.assertIn('pci-express-device', ifaces) - - def test_modern_only_devs(self): - self.check_modern_only('virtio-vga', VIRTIO_GPU) - self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) - self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) - self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) - self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) 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/virtiofs_submounts.py.data/cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/cleanup.sh deleted file mode 100644 index 2a6579a0fe..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/cleanup.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 " -} - -scratch_dir=$1 -if [ -z "$scratch_dir" ]; then - print_usage "$0" 'Scratch dir not given' >&2 - exit 1 -fi - -cd "$scratch_dir/share" || exit 1 -mps=(mnt*) -mp_i=0 -for mp in "${mps[@]}"; do - mp_i=$((mp_i + 1)) - printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}" - - sudo umount -R "$mp" - rm -rf "$mp" -done -echo - -rm some-file -cd .. -rmdir share - -imgs=(fs*.img) -img_i=0 -for img in "${imgs[@]}"; do - img_i=$((img_i + 1)) - printf "Detaching and deleting %i/%i...\r" "$img_i" "${#imgs[@]}" - - dev=$(losetup -j "$img" | sed -e 's/:.*//') - sudo losetup -d "$dev" - rm -f "$img" -done -echo - -echo 'Done.' diff --git a/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh b/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh deleted file mode 100644 index 729cb2d1a5..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/guest-cleanup.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 " -} - -scratch_dir=$1 -if [ -z "$scratch_dir" ]; then - print_usage "$0" 'Scratch dir not given' >&2 - exit 1 -fi - -cd "$scratch_dir/share" || exit 1 - -mps=(mnt*) -mp_i=0 -for mp in "${mps[@]}"; do - mp_i=$((mp_i + 1)) - printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}" - - sudo umount -R "$mp" -done -echo - -echo 'Done.' diff --git a/tests/avocado/virtiofs_submounts.py.data/guest.sh b/tests/avocado/virtiofs_submounts.py.data/guest.sh deleted file mode 100644 index 59ba40fde1..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/guest.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 " - echo '(The shared directory is the "share" directory in the scratch' \ - 'directory)' -} - -shared_dir=$1 -if [ -z "$shared_dir" ]; then - print_usage "$0" 'Shared dir not given' >&2 - exit 1 -fi - -cd "$shared_dir" - -# FIXME: This should not be necessary, but it is. In order for all -# submounts to be proper mount points, we need to visit them. -# (Before we visit them, they will not be auto-mounted, and so just -# appear as normal directories, with the catch that their st_ino will -# be the st_ino of the filesystem they host, while the st_dev will -# still be the st_dev of the parent.) -# `find` does not work, because it will refuse to touch the mount -# points as long as they are not mounted; their st_dev being shared -# with the parent and st_ino just being the root node's inode ID -# will practically ensure that this node exists elsewhere on the -# filesystem, and `find` is required to recognize loops and not to -# follow them. -# Thus, we have to manually visit all nodes first. - -mnt_i=0 - -function recursively_visit() -{ - pushd "$1" >/dev/null - for entry in *; do - if [[ "$entry" == mnt* ]]; then - mnt_i=$((mnt_i + 1)) - printf "Triggering auto-mount $mnt_i...\r" - fi - - if [ -d "$entry" ]; then - recursively_visit "$entry" - fi - done - popd >/dev/null -} - -recursively_visit . -echo - - -if [ -n "$(find -name not-mounted)" ]; then - echo "Error: not-mounted files visible on mount points:" >&2 - find -name not-mounted >&2 - exit 1 -fi - -if [ ! -f some-file -o "$(cat some-file)" != 'root' ]; then - echo "Error: Bad file in the share root" >&2 - exit 1 -fi - -shopt -s nullglob - -function check_submounts() -{ - local base_path=$1 - - for mp in mnt*; do - printf "Checking submount %i...\r" "$((${#devs[@]} + 1))" - - mp_i=$(echo "$mp" | sed -e 's/mnt//') - dev=$(stat -c '%D' "$mp") - - if [ -n "${devs[mp_i]}" ]; then - echo "Error: $mp encountered twice" >&2 - exit 1 - fi - devs[mp_i]=$dev - - pushd "$mp" >/dev/null - path="$base_path$mp" - while true; do - expected_content="$(printf '%s\n%s\n' "$mp_i" "$path")" - if [ ! -f some-file ]; then - echo "Error: $PWD/some-file does not exist" >&2 - exit 1 - fi - - if [ "$(cat some-file)" != "$expected_content" ]; then - echo "Error: Bad content in $PWD/some-file:" >&2 - echo '--- found ---' - cat some-file - echo '--- expected ---' - echo "$expected_content" - exit 1 - fi - if [ "$(stat -c '%D' some-file)" != "$dev" ]; then - echo "Error: $PWD/some-file has the wrong device ID" >&2 - exit 1 - fi - - if [ -d sub ]; then - if [ "$(stat -c '%D' sub)" != "$dev" ]; then - echo "Error: $PWD/some-file has the wrong device ID" >&2 - exit 1 - fi - cd sub - path="$path/sub" - else - if [ -n "$(echo mnt*)" ]; then - check_submounts "$path/" - fi - break - fi - done - popd >/dev/null - done -} - -root_dev=$(stat -c '%D' some-file) -devs=() -check_submounts '' -echo - -reused_devs=$(echo "$root_dev ${devs[@]}" | tr ' ' '\n' | sort | uniq -d) -if [ -n "$reused_devs" ]; then - echo "Error: Reused device IDs: $reused_devs" >&2 - exit 1 -fi - -echo "Test passed for ${#devs[@]} submounts." diff --git a/tests/avocado/virtiofs_submounts.py.data/host.sh b/tests/avocado/virtiofs_submounts.py.data/host.sh deleted file mode 100644 index d8a9afebdb..0000000000 --- a/tests/avocado/virtiofs_submounts.py.data/host.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -mount_count=128 - -function print_usage() -{ - if [ -n "$2" ]; then - echo "Error: $2" - echo - fi - echo "Usage: $1 [seed]" - echo "(If no seed is given, it will be randomly generated.)" -} - -scratch_dir=$1 -if [ -z "$scratch_dir" ]; then - print_usage "$0" 'No scratch dir given' >&2 - exit 1 -fi - -if [ ! -d "$scratch_dir" ]; then - print_usage "$0" "$scratch_dir is not a directory" >&2 - exit 1 -fi - -seed=$2 -if [ -z "$seed" ]; then - seed=$RANDOM -fi -RANDOM=$seed - -echo "Seed: $seed" - -set -e -shopt -s nullglob - -cd "$scratch_dir" -if [ -d share ]; then - echo 'Error: This directory seems to be in use already' >&2 - exit 1 -fi - -for ((i = 0; i < $mount_count; i++)); do - printf "Setting up fs %i/%i...\r" "$((i + 1))" "$mount_count" - - rm -f fs$i.img - truncate -s 512M fs$i.img - mkfs.xfs -q fs$i.img - devs[i]=$(sudo losetup -f --show fs$i.img) -done -echo - -top_level_mounts=$((RANDOM % mount_count + 1)) - -mkdir -p share -echo 'root' > share/some-file - -for ((i = 0; i < $top_level_mounts; i++)); do - printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count" - - mkdir -p share/mnt$i - touch share/mnt$i/not-mounted - sudo mount "${devs[i]}" share/mnt$i - sudo chown "$(id -u):$(id -g)" share/mnt$i - - pushd share/mnt$i >/dev/null - path=mnt$i - nesting=$((RANDOM % 4)) - for ((j = 0; j < $nesting; j++)); do - cat > some-file < some-file </dev/null -done - -for ((; i < $mount_count; i++)); do - printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count" - - mp_i=$((i % top_level_mounts)) - - pushd share/mnt$mp_i >/dev/null - path=mnt$mp_i - while true; do - sub_mp="$(echo mnt*)" - if cd sub 2>/dev/null; then - path="$path/sub" - elif [ -n "$sub_mp" ] && cd "$sub_mp" 2>/dev/null; then - path="$path/$sub_mp" - else - break - fi - done - mkdir mnt$i - touch mnt$i/not-mounted - sudo mount "${devs[i]}" mnt$i - sudo chown "$(id -u):$(id -g)" mnt$i - - cd mnt$i - path="$path/mnt$i" - nesting=$((RANDOM % 4)) - for ((j = 0; j < $nesting; j++)); do - cat > some-file < some-file </dev/null -done -echo - -echo 'Done.' diff --git a/tests/avocado/vnc.py b/tests/avocado/vnc.py index aeeefc70be..862c8996a8 100644 --- a/tests/avocado/vnc.py +++ b/tests/avocado/vnc.py @@ -88,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) @@ -105,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 deleted file mode 100644 index a6edf74c1c..0000000000 --- a/tests/avocado/x86_cpu_model_versions.py +++ /dev/null @@ -1,358 +0,0 @@ -# -# Basic validation of x86 versioned CPU models and CPU model aliases -# -# Copyright (c) 2019 Red Hat Inc -# -# Author: -# Eduardo Habkost -# -# 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 . -# - - -import avocado_qemu -import re - -class X86CPUModelAliases(avocado_qemu.QemuSystemTest): - """ - Validation of PC CPU model versions and CPU model aliases - - :avocado: tags=arch:x86_64 - """ - def validate_aliases(self, cpus): - for c in cpus.values(): - if 'alias-of' in c: - # all aliases must point to a valid CPU model name: - self.assertIn(c['alias-of'], cpus, - '%s.alias-of (%s) is not a valid CPU model name' % (c['name'], c['alias-of'])) - # aliases must not point to aliases - self.assertNotIn('alias-of', cpus[c['alias-of']], - '%s.alias-of (%s) points to another alias' % (c['name'], c['alias-of'])) - - # aliases must not be static - self.assertFalse(c['static']) - - def validate_variant_aliases(self, cpus): - # -noTSX, -IBRS and -IBPB variants of CPU models are special: - # they shouldn't have their own versions: - self.assertNotIn("Haswell-noTSX-v1", cpus, - "Haswell-noTSX shouldn't be versioned") - self.assertNotIn("Broadwell-noTSX-v1", cpus, - "Broadwell-noTSX shouldn't be versioned") - self.assertNotIn("Nehalem-IBRS-v1", cpus, - "Nehalem-IBRS shouldn't be versioned") - self.assertNotIn("Westmere-IBRS-v1", cpus, - "Westmere-IBRS shouldn't be versioned") - self.assertNotIn("SandyBridge-IBRS-v1", cpus, - "SandyBridge-IBRS shouldn't be versioned") - self.assertNotIn("IvyBridge-IBRS-v1", cpus, - "IvyBridge-IBRS shouldn't be versioned") - self.assertNotIn("Haswell-noTSX-IBRS-v1", cpus, - "Haswell-noTSX-IBRS shouldn't be versioned") - self.assertNotIn("Haswell-IBRS-v1", cpus, - "Haswell-IBRS shouldn't be versioned") - self.assertNotIn("Broadwell-noTSX-IBRS-v1", cpus, - "Broadwell-noTSX-IBRS shouldn't be versioned") - self.assertNotIn("Broadwell-IBRS-v1", cpus, - "Broadwell-IBRS shouldn't be versioned") - self.assertNotIn("Skylake-Client-IBRS-v1", cpus, - "Skylake-Client-IBRS shouldn't be versioned") - self.assertNotIn("Skylake-Server-IBRS-v1", cpus, - "Skylake-Server-IBRS shouldn't be versioned") - self.assertNotIn("EPYC-IBPB-v1", cpus, - "EPYC-IBPB shouldn't be versioned") - - def test_4_0_alias_compatibility(self): - """ - Check if pc-*-4.0 unversioned CPU model won't be reported as aliases - - :avocado: tags=machine:pc-i440fx-4.0 - """ - # pc-*-4.0 won't expose non-versioned CPU models as aliases - # We do this to help management software to keep compatibility - # with older QEMU versions that didn't have the versioned CPU model - self.vm.add_args('-S') - self.vm.launch() - cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) - - self.assertFalse(cpus['Cascadelake-Server']['static'], - 'unversioned Cascadelake-Server CPU model must not be static') - self.assertNotIn('alias-of', cpus['Cascadelake-Server'], - 'Cascadelake-Server must not be an alias') - 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.assertNotIn('alias-of', cpus['qemu64'], - 'qemu64 must not be an alias') - self.assertNotIn('alias-of', cpus['qemu64-v1'], - 'qemu64-v1 must not be an alias') - - self.validate_variant_aliases(cpus) - - # On pc-*-4.0, no CPU model should be reported as an alias: - for name,c in cpus.items(): - self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name)) - - def test_4_1_alias(self): - """ - Check if unversioned CPU model is an alias pointing to right version - - :avocado: tags=machine:pc-i440fx-4.1 - """ - self.vm.add_args('-S') - self.vm.launch() - - cpus = dict((m['name'], m) for m in self.vm.command('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.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.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", - "Haswell must be an alias") - self.assertEquals(cpus["Haswell-noTSX"].get('alias-of'), - "Haswell-v2", - "Haswell-noTSX must be an alias") - self.assertEquals(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", - "Haswell-noTSX-IBRS must be an alias") - - self.assertEquals(cpus["Broadwell"].get('alias-of'), - "Broadwell-v1", - "Broadwell must be an alias") - self.assertEquals(cpus["Broadwell-noTSX"].get('alias-of'), - "Broadwell-v2", - "Broadwell-noTSX must be an alias") - self.assertEquals(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", - "Broadwell-noTSX-IBRS must be an alias") - - self.assertEquals(cpus["Nehalem"].get('alias-of'), - "Nehalem-v1", - "Nehalem must be an alias") - self.assertEquals(cpus["Nehalem-IBRS"].get('alias-of'), - "Nehalem-v2", - "Nehalem-IBRS must be an alias") - - self.assertEquals(cpus["Westmere"].get('alias-of'), - "Westmere-v1", - "Westmere must be an alias") - self.assertEquals(cpus["Westmere-IBRS"].get('alias-of'), - "Westmere-v2", - "Westmere-IBRS must be an alias") - - self.assertEquals(cpus["SandyBridge"].get('alias-of'), - "SandyBridge-v1", - "SandyBridge must be an alias") - self.assertEquals(cpus["SandyBridge-IBRS"].get('alias-of'), - "SandyBridge-v2", - "SandyBridge-IBRS must be an alias") - - self.assertEquals(cpus["IvyBridge"].get('alias-of'), - "IvyBridge-v1", - "IvyBridge must be an alias") - self.assertEquals(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", - "Skylake-Client must be an alias") - self.assertEquals(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", - "Skylake-Server must be an alias") - self.assertEquals(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", - "EPYC must be an alias") - self.assertEquals(cpus["EPYC-IBPB"].get('alias-of'), - "EPYC-v2", - "EPYC-IBPB must be an alias") - - self.validate_aliases(cpus) - - def test_none_alias(self): - """ - Check if unversioned CPU model is an alias pointing to some version - - :avocado: tags=machine:none - """ - self.vm.add_args('-S') - self.vm.launch() - - cpus = dict((m['name'], m) for m in self.vm.command('query-cpu-definitions')) - - self.assertFalse(cpus['Cascadelake-Server']['static'], - 'unversioned Cascadelake-Server CPU model must not be static') - self.assertTrue(re.match('Cascadelake-Server-v[0-9]+', cpus['Cascadelake-Server']['alias-of']), - 'Cascadelake-Server must be an alias of versioned CPU model') - 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.assertTrue(re.match('qemu64-v[0-9]+', cpus['qemu64']['alias-of']), - 'qemu64 must be an alias of versioned CPU model') - self.assertNotIn('alias-of', cpus['qemu64-v1'], - 'qemu64-v1 must not be an alias') - - self.validate_aliases(cpus) - - -class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): - """ - Validation of Cascadelake arch-capabilities - - :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) - - def test_4_1(self): - """ - :avocado: tags=machine:pc-i440fx-4.1 - :avocado: tags=cpu:Cascadelake-Server - """ - # machine-type only: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities') - - def test_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities') - - def test_set_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ - # command line must override machine-type if CPU model is not versioned: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off,+arch-capabilities') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities') - - def test_unset_4_1(self): - """ - :avocado: tags=machine:pc-i440fx-4.1 - :avocado: tags=cpu:Cascadelake-Server - """ - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off,-arch-capabilities') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') - - def test_v1_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ - # versioned CPU model overrides machine-type: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v1,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities') - - def test_v2_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v2,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') - - def test_v1_set_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ - # command line must override machine-type and versioned CPU model: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v1,x-force-features=on,check=off,' - 'enforce=off,+arch-capabilities') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') - - def test_v2_unset_4_1(self): - """ - :avocado: tags=machine:pc-i440fx-4.1 - :avocado: tags=cpu:Cascadelake-Server - """ - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v2,x-force-features=on,check=off,' - 'enforce=off,-arch-capabilities') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') diff --git a/tests/bench/benchmark-crypto-akcipher.c b/tests/bench/benchmark-crypto-akcipher.c index 15e69557ed..0a6e5db1d6 100644 --- a/tests/bench/benchmark-crypto-akcipher.c +++ b/tests/bench/benchmark-crypto-akcipher.c @@ -16,22 +16,20 @@ #include "crypto/akcipher.h" #include "standard-headers/linux/virtio_crypto.h" -#include "test_akcipher_keys.inc" +#include "test_akcipher_keys.c.inc" static QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key, size_t keylen, - QCryptoRSAPaddingAlgorithm padding, - QCryptoHashAlgorithm hash) + QCryptoRSAPaddingAlgo padding, + QCryptoHashAlgo hash) { QCryptoAkCipherOptions opt; - QCryptoAkCipher *rsa; - opt.alg = QCRYPTO_AKCIPHER_ALG_RSA; + opt.alg = QCRYPTO_AK_CIPHER_ALGO_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_AK_CIPHER_KEY_TYPE_PRIVATE, + priv_key, keylen, &error_abort); } static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, @@ -41,8 +39,8 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, #define SHA1_DGST_LEN 20 #define SIGN_TIMES 10000 #define VERIFY_TIMES 100000 -#define PADDING QCRYPTO_RSA_PADDING_ALG_PKCS1 -#define HASH QCRYPTO_HASH_ALG_SHA1 +#define PADDING QCRYPTO_RSA_PADDING_ALGO_PKCS1 +#define HASH QCRYPTO_HASH_ALGO_SHA1 g_autoptr(QCryptoAkCipher) rsa = create_rsa_akcipher(priv_key, keylen, PADDING, HASH); @@ -55,8 +53,8 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, signature = g_new0(uint8_t, key_size / BYTE); g_test_message("benchmark rsa%zu (%s-%s) sign...", key_size, - QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH)); + QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH)); g_test_timer_start(); for (count = 0; count < SIGN_TIMES; ++count) { g_assert(qcrypto_akcipher_sign(rsa, dgst, SHA1_DGST_LEN, @@ -66,14 +64,14 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, g_test_timer_elapsed(); g_test_message("rsa%zu (%s-%s) sign %zu times in %.2f seconds," " %.2f times/sec ", - key_size, QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH), + key_size, QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH), count, g_test_timer_last(), (double)count / g_test_timer_last()); g_test_message("benchmark rsa%zu (%s-%s) verification...", key_size, - QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH)); + QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH)); g_test_timer_start(); for (count = 0; count < VERIFY_TIMES; ++count) { g_assert(qcrypto_akcipher_verify(rsa, signature, key_size / BYTE, @@ -83,8 +81,8 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, g_test_timer_elapsed(); g_test_message("rsa%zu (%s-%s) verify %zu times in %.2f seconds," " %.2f times/sec ", - key_size, QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH), + key_size, QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH), count, g_test_timer_last(), (double)count / g_test_timer_last()); } diff --git a/tests/bench/benchmark-crypto-cipher.c b/tests/bench/benchmark-crypto-cipher.c index c04f0a0fba..889a29ba5c 100644 --- a/tests/bench/benchmark-crypto-cipher.c +++ b/tests/bench/benchmark-crypto-cipher.c @@ -17,7 +17,7 @@ static void test_cipher_speed(size_t chunk_size, QCryptoCipherMode mode, - QCryptoCipherAlgorithm alg) + QCryptoCipherAlgo alg) { QCryptoCipher *cipher; Error *err = NULL; @@ -71,7 +71,7 @@ static void test_cipher_speed(size_t chunk_size, g_test_timer_elapsed(); g_test_message("enc(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), + QCryptoCipherAlgo_str(alg), QCryptoCipherMode_str(mode), chunk_size, (double)total / MiB / g_test_timer_last()); @@ -88,7 +88,7 @@ static void test_cipher_speed(size_t chunk_size, g_test_timer_elapsed(); g_test_message("dec(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), + QCryptoCipherAlgo_str(alg), QCryptoCipherMode_str(mode), chunk_size, (double)total / MiB / g_test_timer_last()); @@ -105,7 +105,7 @@ static void test_cipher_speed_ecb_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_ecb_aes_256(const void *opaque) @@ -113,7 +113,7 @@ static void test_cipher_speed_ecb_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } static void test_cipher_speed_cbc_aes_128(const void *opaque) @@ -121,7 +121,7 @@ static void test_cipher_speed_cbc_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_cbc_aes_256(const void *opaque) @@ -129,7 +129,7 @@ static void test_cipher_speed_cbc_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } static void test_cipher_speed_ctr_aes_128(const void *opaque) @@ -137,7 +137,7 @@ static void test_cipher_speed_ctr_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_ctr_aes_256(const void *opaque) @@ -145,7 +145,7 @@ static void test_cipher_speed_ctr_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } static void test_cipher_speed_xts_aes_128(const void *opaque) @@ -153,7 +153,7 @@ static void test_cipher_speed_xts_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_xts_aes_256(const void *opaque) @@ -161,7 +161,7 @@ static void test_cipher_speed_xts_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } diff --git a/tests/bench/benchmark-crypto-hash.c b/tests/bench/benchmark-crypto-hash.c index 927b00bb4d..252098a69d 100644 --- a/tests/bench/benchmark-crypto-hash.c +++ b/tests/bench/benchmark-crypto-hash.c @@ -17,7 +17,7 @@ typedef struct QCryptoHashOpts { size_t chunk_size; - QCryptoHashAlgorithm alg; + QCryptoHashAlgo alg; } QCryptoHashOpts; static void test_hash_speed(const void *opaque) @@ -49,7 +49,7 @@ static void test_hash_speed(const void *opaque) g_test_timer_elapsed(); g_test_message("hash(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(opts->alg), + QCryptoHashAlgo_str(opts->alg), opts->chunk_size, total / g_test_timer_last()); g_free(out); @@ -65,14 +65,14 @@ int main(int argc, char **argv) #define TEST_ONE(a, c) \ QCryptoHashOpts opts ## a ## c = { \ - .alg = QCRYPTO_HASH_ALG_ ## a, .chunk_size = c, \ + .alg = QCRYPTO_HASH_ALGO_ ## a, .chunk_size = c, \ }; \ memset(name, 0 , sizeof(name)); \ snprintf(name, sizeof(name), \ "/crypto/benchmark/hash/%s/bufsize-%d", \ - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_ ## a), \ + QCryptoHashAlgo_str(QCRYPTO_HASH_ALGO_ ## a), \ c); \ - if (qcrypto_hash_supports(QCRYPTO_HASH_ALG_ ## a)) \ + if (qcrypto_hash_supports(QCRYPTO_HASH_ALGO_ ## a)) \ g_test_add_data_func(name, \ &opts ## a ## c, \ test_hash_speed); diff --git a/tests/bench/benchmark-crypto-hmac.c b/tests/bench/benchmark-crypto-hmac.c index 5cca636789..d51de98f47 100644 --- a/tests/bench/benchmark-crypto-hmac.c +++ b/tests/bench/benchmark-crypto-hmac.c @@ -28,7 +28,7 @@ static void test_hmac_speed(const void *opaque) Error *err = NULL; int ret; - if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALG_SHA256)) { + if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALGO_SHA256)) { return; } @@ -40,7 +40,7 @@ static void test_hmac_speed(const void *opaque) g_test_timer_start(); do { - hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALG_SHA256, + hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALGO_SHA256, (const uint8_t *)KEY, strlen(KEY), &err); g_assert(err == NULL); g_assert(hmac != NULL); @@ -56,7 +56,7 @@ static void test_hmac_speed(const void *opaque) total /= MiB; g_test_message("hmac(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_SHA256), + QCryptoHashAlgo_str(QCRYPTO_HASH_ALGO_SHA256), chunk_size, total / g_test_timer_last()); g_free(out); diff --git a/tests/bench/bufferiszero-bench.c b/tests/bench/bufferiszero-bench.c new file mode 100644 index 0000000000..222695c1fa --- /dev/null +++ b/tests/bench/bufferiszero-bench.c @@ -0,0 +1,47 @@ +/* + * QEMU buffer_is_zero speed benchmark + * + * 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/cutils.h" +#include "qemu/units.h" + +static void test(const void *opaque) +{ + size_t max = 64 * KiB; + void *buf = g_malloc0(max); + int accel_index = 0; + + do { + if (accel_index != 0) { + g_test_message("%s", ""); /* gnu_printf Werror for simple "" */ + } + for (size_t len = 1 * KiB; len <= max; len *= 4) { + double total = 0.0; + + g_test_timer_start(); + do { + buffer_is_zero_ge256(buf, len); + total += len; + } while (g_test_timer_elapsed() < 0.5); + + total /= MiB; + g_test_message("buffer_is_zero #%d: %2zuKB %8.0f MB/sec", + accel_index, len / (size_t)KiB, + total / g_test_timer_last()); + } + accel_index++; + } while (test_buffer_is_zero_next_accel()); + + g_free(buf); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_data_func("/cutils/bufferiszero/speed", NULL, test); + return g_test_run(); +} diff --git a/tests/bench/meson.build b/tests/bench/meson.build index 279a8fcc33..4cd7a2f6b5 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], @@ -17,6 +21,7 @@ benchs = {} if have_block benchs += { + 'bufferiszero-bench': [], 'benchmark-crypto-hash': [crypto], 'benchmark-crypto-hmac': [crypto], 'benchmark-crypto-cipher': [crypto], 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/bench/test_akcipher_keys.inc b/tests/bench/test_akcipher_keys.c.inc similarity index 100% rename from tests/bench/test_akcipher_keys.inc rename to tests/bench/test_akcipher_keys.c.inc 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/virt/APIC b/tests/data/acpi/aarch64/virt/APIC similarity index 100% rename from tests/data/acpi/virt/APIC rename to tests/data/acpi/aarch64/virt/APIC diff --git a/tests/data/acpi/virt/APIC.acpihmatvirt b/tests/data/acpi/aarch64/virt/APIC.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/APIC.acpihmatvirt rename to tests/data/acpi/aarch64/virt/APIC.acpihmatvirt diff --git a/tests/data/acpi/aarch64/virt/APIC.topology b/tests/data/acpi/aarch64/virt/APIC.topology new file mode 100644 index 0000000000..3a6ac525e7 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/APIC.topology differ diff --git a/tests/data/acpi/aarch64/virt/DBG2 b/tests/data/acpi/aarch64/virt/DBG2 new file mode 100644 index 0000000000..0a05e1a47f Binary files /dev/null and b/tests/data/acpi/aarch64/virt/DBG2 differ diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT new file mode 100644 index 0000000000..36d3e5d5a5 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/DSDT differ diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt new file mode 100644 index 0000000000..e6154d0355 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt differ diff --git a/tests/data/acpi/aarch64/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp new file mode 100644 index 0000000000..33f011d6b6 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/DSDT.memhp differ diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb new file mode 100644 index 0000000000..c0fdc6e9c1 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/DSDT.pxb differ diff --git a/tests/data/acpi/aarch64/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology new file mode 100644 index 0000000000..029d03eecc Binary files /dev/null and b/tests/data/acpi/aarch64/virt/DSDT.topology differ diff --git a/tests/data/acpi/aarch64/virt/FACP b/tests/data/acpi/aarch64/virt/FACP new file mode 100644 index 0000000000..da0c3644cc Binary files /dev/null and b/tests/data/acpi/aarch64/virt/FACP differ diff --git a/tests/data/acpi/aarch64/virt/GTDT b/tests/data/acpi/aarch64/virt/GTDT new file mode 100644 index 0000000000..7f330e04d1 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/GTDT differ diff --git a/tests/data/acpi/virt/HMAT.acpihmatvirt b/tests/data/acpi/aarch64/virt/HMAT.acpihmatvirt similarity index 100% rename from tests/data/acpi/virt/HMAT.acpihmatvirt rename to tests/data/acpi/aarch64/virt/HMAT.acpihmatvirt diff --git a/tests/data/acpi/virt/IORT b/tests/data/acpi/aarch64/virt/IORT similarity index 100% rename from tests/data/acpi/virt/IORT rename to tests/data/acpi/aarch64/virt/IORT diff --git a/tests/data/acpi/virt/MCFG b/tests/data/acpi/aarch64/virt/MCFG similarity index 100% rename from tests/data/acpi/virt/MCFG rename to tests/data/acpi/aarch64/virt/MCFG diff --git a/tests/data/acpi/virt/NFIT.memhp b/tests/data/acpi/aarch64/virt/NFIT.memhp similarity index 100% rename from tests/data/acpi/virt/NFIT.memhp rename to tests/data/acpi/aarch64/virt/NFIT.memhp diff --git a/tests/data/acpi/aarch64/virt/PPTT b/tests/data/acpi/aarch64/virt/PPTT new file mode 100644 index 0000000000..7a1258ecf1 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/PPTT differ diff --git a/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt new file mode 100644 index 0000000000..4eef303a5b Binary files /dev/null and b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt differ diff --git a/tests/data/acpi/aarch64/virt/PPTT.topology b/tests/data/acpi/aarch64/virt/PPTT.topology new file mode 100644 index 0000000000..3fbcae5ff0 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/PPTT.topology differ diff --git a/tests/data/acpi/pc/SLIT.memhp b/tests/data/acpi/aarch64/virt/SLIT.memhp similarity index 100% rename from tests/data/acpi/pc/SLIT.memhp rename to tests/data/acpi/aarch64/virt/SLIT.memhp diff --git a/tests/data/acpi/aarch64/virt/SPCR b/tests/data/acpi/aarch64/virt/SPCR new file mode 100644 index 0000000000..cf0f2b7522 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/SPCR differ diff --git a/tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt b/tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt new file mode 100644 index 0000000000..6fe55dd7d0 Binary files /dev/null and b/tests/data/acpi/aarch64/virt/SRAT.acpihmatvirt differ diff --git a/tests/data/acpi/virt/SRAT.memhp b/tests/data/acpi/aarch64/virt/SRAT.memhp similarity index 100% rename from tests/data/acpi/virt/SRAT.memhp rename to tests/data/acpi/aarch64/virt/SRAT.memhp diff --git a/tests/data/acpi/virt/SRAT.numamem b/tests/data/acpi/aarch64/virt/SRAT.numamem similarity index 100% rename from tests/data/acpi/virt/SRAT.numamem rename to tests/data/acpi/aarch64/virt/SRAT.numamem diff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/aarch64/virt/SSDT.memhp similarity index 95% rename from tests/data/acpi/virt/SSDT.memhp rename to tests/data/acpi/aarch64/virt/SSDT.memhp index 2fcfc5fda9..1deb1d2832 100644 Binary files a/tests/data/acpi/virt/SSDT.memhp and b/tests/data/acpi/aarch64/virt/SSDT.memhp differ diff --git a/tests/data/acpi/virt/VIOT b/tests/data/acpi/aarch64/virt/VIOT similarity index 100% rename from tests/data/acpi/virt/VIOT rename to tests/data/acpi/aarch64/virt/VIOT diff --git a/tests/data/acpi/disassemle-aml.sh b/tests/data/acpi/disassemle-aml.sh index 253b7620a0..89561d233d 100755 --- a/tests/data/acpi/disassemle-aml.sh +++ b/tests/data/acpi/disassemle-aml.sh @@ -14,7 +14,7 @@ while getopts "o:" arg; do esac done -for machine in tests/data/acpi/* +for machine in tests/data/acpi/*/* do if [[ ! -d "$machine" ]]; then diff --git a/tests/data/acpi/microvm/APIC b/tests/data/acpi/microvm/APIC deleted file mode 100644 index 68dbd44a7e..0000000000 Binary files a/tests/data/acpi/microvm/APIC and /dev/null differ diff --git a/tests/data/acpi/microvm/APIC.ioapic2 b/tests/data/acpi/microvm/APIC.ioapic2 deleted file mode 100644 index 3063c52cd3..0000000000 Binary files a/tests/data/acpi/microvm/APIC.ioapic2 and /dev/null differ diff --git a/tests/data/acpi/microvm/APIC.pcie b/tests/data/acpi/microvm/APIC.pcie deleted file mode 100644 index 4e8f6ed8d6..0000000000 Binary files a/tests/data/acpi/microvm/APIC.pcie and /dev/null differ diff --git a/tests/data/acpi/microvm/DSDT.pcie b/tests/data/acpi/microvm/DSDT.pcie deleted file mode 100644 index 765f14ef3d..0000000000 Binary files a/tests/data/acpi/microvm/DSDT.pcie and /dev/null differ diff --git a/tests/data/acpi/pc/APIC b/tests/data/acpi/pc/APIC deleted file mode 100644 index 208331db53..0000000000 Binary files a/tests/data/acpi/pc/APIC and /dev/null differ diff --git a/tests/data/acpi/pc/APIC.acpihmat b/tests/data/acpi/pc/APIC.acpihmat deleted file mode 100644 index 812c4603f2..0000000000 Binary files a/tests/data/acpi/pc/APIC.acpihmat and /dev/null differ diff --git a/tests/data/acpi/pc/APIC.cphp b/tests/data/acpi/pc/APIC.cphp deleted file mode 100644 index 65cc4f4a9a..0000000000 Binary files a/tests/data/acpi/pc/APIC.cphp and /dev/null differ diff --git a/tests/data/acpi/pc/APIC.dimmpxm b/tests/data/acpi/pc/APIC.dimmpxm deleted file mode 100644 index d904d4a70d..0000000000 Binary files a/tests/data/acpi/pc/APIC.dimmpxm and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT b/tests/data/acpi/pc/DSDT deleted file mode 100644 index b688686dc3..0000000000 Binary files a/tests/data/acpi/pc/DSDT and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/pc/DSDT.acpierst deleted file mode 100644 index 86259be9d1..0000000000 Binary files a/tests/data/acpi/pc/DSDT.acpierst and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.acpihmat b/tests/data/acpi/pc/DSDT.acpihmat deleted file mode 100644 index e2cc2a6fc9..0000000000 Binary files a/tests/data/acpi/pc/DSDT.acpihmat and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge deleted file mode 100644 index 75016fd4b7..0000000000 Binary files a/tests/data/acpi/pc/DSDT.bridge and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.cphp b/tests/data/acpi/pc/DSDT.cphp deleted file mode 100644 index 53eb0dd7d4..0000000000 Binary files a/tests/data/acpi/pc/DSDT.cphp and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm deleted file mode 100644 index 9089d994e0..0000000000 Binary files a/tests/data/acpi/pc/DSDT.dimmpxm and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.hpbridge b/tests/data/acpi/pc/DSDT.hpbridge deleted file mode 100644 index 86259be9d1..0000000000 Binary files a/tests/data/acpi/pc/DSDT.hpbridge and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.hpbrroot b/tests/data/acpi/pc/DSDT.hpbrroot deleted file mode 100644 index 578468f4f0..0000000000 Binary files a/tests/data/acpi/pc/DSDT.hpbrroot and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs deleted file mode 100644 index 39427103aa..0000000000 Binary files a/tests/data/acpi/pc/DSDT.ipmikcs and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp deleted file mode 100644 index 987a263339..0000000000 Binary files a/tests/data/acpi/pc/DSDT.memhp and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.nohpet b/tests/data/acpi/pc/DSDT.nohpet deleted file mode 100644 index fc7598b762..0000000000 Binary files a/tests/data/acpi/pc/DSDT.nohpet and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.numamem b/tests/data/acpi/pc/DSDT.numamem deleted file mode 100644 index 85af400cdb..0000000000 Binary files a/tests/data/acpi/pc/DSDT.numamem and /dev/null differ diff --git a/tests/data/acpi/pc/DSDT.roothp b/tests/data/acpi/pc/DSDT.roothp deleted file mode 100644 index 545512adfa..0000000000 Binary files a/tests/data/acpi/pc/DSDT.roothp and /dev/null differ diff --git a/tests/data/acpi/q35/APIC b/tests/data/acpi/q35/APIC deleted file mode 100644 index 208331db53..0000000000 Binary files a/tests/data/acpi/q35/APIC and /dev/null differ diff --git a/tests/data/acpi/q35/APIC.acpihmat b/tests/data/acpi/q35/APIC.acpihmat deleted file mode 100644 index 812c4603f2..0000000000 Binary files a/tests/data/acpi/q35/APIC.acpihmat and /dev/null differ diff --git a/tests/data/acpi/q35/APIC.acpihmat-noinitiator b/tests/data/acpi/q35/APIC.acpihmat-noinitiator deleted file mode 100644 index d904d4a70d..0000000000 Binary files a/tests/data/acpi/q35/APIC.acpihmat-noinitiator and /dev/null differ diff --git a/tests/data/acpi/q35/APIC.core-count2 b/tests/data/acpi/q35/APIC.core-count2 deleted file mode 100644 index a255082ef5..0000000000 Binary files a/tests/data/acpi/q35/APIC.core-count2 and /dev/null differ diff --git a/tests/data/acpi/q35/APIC.cphp b/tests/data/acpi/q35/APIC.cphp deleted file mode 100644 index 65cc4f4a9a..0000000000 Binary files a/tests/data/acpi/q35/APIC.cphp and /dev/null differ diff --git a/tests/data/acpi/q35/APIC.dimmpxm b/tests/data/acpi/q35/APIC.dimmpxm deleted file mode 100644 index d904d4a70d..0000000000 Binary files a/tests/data/acpi/q35/APIC.dimmpxm and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT deleted file mode 100644 index 2771bcea89..0000000000 Binary files a/tests/data/acpi/q35/DSDT and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/q35/DSDT.acpierst deleted file mode 100644 index b45abca7c2..0000000000 Binary files a/tests/data/acpi/q35/DSDT.acpierst and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat deleted file mode 100644 index d90fd4723a..0000000000 Binary files a/tests/data/acpi/q35/DSDT.acpihmat and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge deleted file mode 100644 index b41a4dddc0..0000000000 Binary files a/tests/data/acpi/q35/DSDT.bridge and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.core-count2 b/tests/data/acpi/q35/DSDT.core-count2 deleted file mode 100644 index 375aceed6b..0000000000 Binary files a/tests/data/acpi/q35/DSDT.core-count2 and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp deleted file mode 100644 index a0ecafc36c..0000000000 Binary files a/tests/data/acpi/q35/DSDT.cphp and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.cxl b/tests/data/acpi/q35/DSDT.cxl deleted file mode 100644 index f9c6dd4ee0..0000000000 Binary files a/tests/data/acpi/q35/DSDT.cxl and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm deleted file mode 100644 index f0659716e3..0000000000 Binary files a/tests/data/acpi/q35/DSDT.dimmpxm and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp deleted file mode 100644 index 28a192c69a..0000000000 Binary files a/tests/data/acpi/q35/DSDT.memhp and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.multi-bridge b/tests/data/acpi/q35/DSDT.multi-bridge deleted file mode 100644 index 3dba4d8436..0000000000 Binary files a/tests/data/acpi/q35/DSDT.multi-bridge and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.nohpet b/tests/data/acpi/q35/DSDT.nohpet deleted file mode 100644 index b116947dac..0000000000 Binary files a/tests/data/acpi/q35/DSDT.nohpet and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem deleted file mode 100644 index 5eb6159d5f..0000000000 Binary files a/tests/data/acpi/q35/DSDT.numamem and /dev/null differ diff --git a/tests/data/acpi/q35/DSDT.viot b/tests/data/acpi/q35/DSDT.viot deleted file mode 100644 index 6b436f9cd9..0000000000 Binary files a/tests/data/acpi/q35/DSDT.viot and /dev/null differ diff --git a/tests/data/acpi/q35/IVRS.ivrs b/tests/data/acpi/q35/IVRS.ivrs deleted file mode 100644 index 17611202e5..0000000000 Binary files a/tests/data/acpi/q35/IVRS.ivrs and /dev/null differ diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index dcf2e2f221..c1092fb8ba 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -12,7 +12,7 @@ # This work is licensed under the terms of the GNU GPLv2. # See the COPYING.LIB file in the top-level directory. -qemu_arches="x86_64 aarch64" +qemu_arches="x86_64 aarch64 riscv64" if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." @@ -36,7 +36,8 @@ fi if [ -z "$qemu_bins" ]; then echo "Only the following architectures are currently supported: $qemu_arches" echo "None of these configured!" - echo "To fix, run configure --target-list=x86_64-softmmu,aarch64-softmmu" + echo "To fix, run configure \ + --target-list=x86_64-softmmu,aarch64-softmmu,riscv64-softmmu" exit 1; fi diff --git a/tests/data/acpi/riscv64/virt/APIC b/tests/data/acpi/riscv64/virt/APIC new file mode 100644 index 0000000000..66a25dfd2d Binary files /dev/null and b/tests/data/acpi/riscv64/virt/APIC differ diff --git a/tests/data/acpi/riscv64/virt/DSDT b/tests/data/acpi/riscv64/virt/DSDT new file mode 100644 index 0000000000..6a33f5647d Binary files /dev/null and b/tests/data/acpi/riscv64/virt/DSDT differ diff --git a/tests/data/acpi/riscv64/virt/FACP b/tests/data/acpi/riscv64/virt/FACP new file mode 100644 index 0000000000..a5276b65ea Binary files /dev/null and b/tests/data/acpi/riscv64/virt/FACP differ diff --git a/tests/data/acpi/riscv64/virt/MCFG b/tests/data/acpi/riscv64/virt/MCFG new file mode 100644 index 0000000000..37eb923a93 Binary files /dev/null and b/tests/data/acpi/riscv64/virt/MCFG differ diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT new file mode 100644 index 0000000000..4f231735ab Binary files /dev/null and b/tests/data/acpi/riscv64/virt/RHCT differ diff --git a/tests/data/acpi/riscv64/virt/SPCR b/tests/data/acpi/riscv64/virt/SPCR new file mode 100644 index 0000000000..4da9daf65f Binary files /dev/null and b/tests/data/acpi/riscv64/virt/SPCR differ diff --git a/tests/data/acpi/riscv64/virt/SRAT.numamem b/tests/data/acpi/riscv64/virt/SRAT.numamem new file mode 100644 index 0000000000..2b6467364b Binary files /dev/null and b/tests/data/acpi/riscv64/virt/SRAT.numamem differ diff --git a/tests/data/acpi/virt/APIC.memhp b/tests/data/acpi/virt/APIC.memhp deleted file mode 100644 index 179d274770..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 179d274770..0000000000 Binary files a/tests/data/acpi/virt/APIC.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/DBG2 b/tests/data/acpi/virt/DBG2 deleted file mode 100644 index 86e6314f7b..0000000000 Binary files a/tests/data/acpi/virt/DBG2 and /dev/null differ diff --git a/tests/data/acpi/virt/DSDT b/tests/data/acpi/virt/DSDT deleted file mode 100644 index c475039907..0000000000 Binary files a/tests/data/acpi/virt/DSDT and /dev/null differ diff --git a/tests/data/acpi/virt/DSDT.acpihmatvirt b/tests/data/acpi/virt/DSDT.acpihmatvirt deleted file mode 100644 index aee6ba017c..0000000000 Binary files a/tests/data/acpi/virt/DSDT.acpihmatvirt and /dev/null differ diff --git a/tests/data/acpi/virt/DSDT.memhp b/tests/data/acpi/virt/DSDT.memhp deleted file mode 100644 index bae36cdd39..0000000000 Binary files a/tests/data/acpi/virt/DSDT.memhp and /dev/null differ diff --git a/tests/data/acpi/virt/DSDT.numamem b/tests/data/acpi/virt/DSDT.numamem deleted file mode 100644 index c475039907..0000000000 Binary files a/tests/data/acpi/virt/DSDT.numamem and /dev/null differ diff --git a/tests/data/acpi/virt/DSDT.pxb b/tests/data/acpi/virt/DSDT.pxb deleted file mode 100644 index fbd78f44c4..0000000000 Binary files a/tests/data/acpi/virt/DSDT.pxb and /dev/null differ diff --git a/tests/data/acpi/virt/FACP b/tests/data/acpi/virt/FACP deleted file mode 100644 index ac05c35a69..0000000000 Binary files a/tests/data/acpi/virt/FACP and /dev/null differ diff --git a/tests/data/acpi/virt/FACP.memhp b/tests/data/acpi/virt/FACP.memhp deleted file mode 100644 index ac05c35a69..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 ac05c35a69..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 deleted file mode 100644 index 6f8cb9b8f3..0000000000 Binary files a/tests/data/acpi/virt/GTDT and /dev/null differ diff --git a/tests/data/acpi/virt/GTDT.memhp b/tests/data/acpi/virt/GTDT.memhp deleted file mode 100644 index 6f8cb9b8f3..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 6f8cb9b8f3..0000000000 Binary files a/tests/data/acpi/virt/GTDT.numamem and /dev/null 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 deleted file mode 100644 index f56ea63b36..0000000000 Binary files a/tests/data/acpi/virt/PPTT and /dev/null differ diff --git a/tests/data/acpi/virt/PPTT.acpihmatvirt b/tests/data/acpi/virt/PPTT.acpihmatvirt deleted file mode 100644 index 710dba5e79..0000000000 Binary files a/tests/data/acpi/virt/PPTT.acpihmatvirt and /dev/null differ diff --git a/tests/data/acpi/virt/SPCR b/tests/data/acpi/virt/SPCR deleted file mode 100644 index 24e0a579e7..0000000000 Binary files a/tests/data/acpi/virt/SPCR and /dev/null 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 deleted file mode 100644 index 691ef56e34..0000000000 Binary files a/tests/data/acpi/virt/SRAT.acpihmatvirt and /dev/null differ diff --git a/tests/data/acpi/x86/microvm/APIC b/tests/data/acpi/x86/microvm/APIC new file mode 100644 index 0000000000..672764e711 Binary files /dev/null and b/tests/data/acpi/x86/microvm/APIC differ diff --git a/tests/data/acpi/x86/microvm/APIC.ioapic2 b/tests/data/acpi/x86/microvm/APIC.ioapic2 new file mode 100644 index 0000000000..6f24fdb12c Binary files /dev/null and b/tests/data/acpi/x86/microvm/APIC.ioapic2 differ diff --git a/tests/data/acpi/x86/microvm/APIC.pcie b/tests/data/acpi/x86/microvm/APIC.pcie new file mode 100644 index 0000000000..2239ca76a6 Binary files /dev/null and b/tests/data/acpi/x86/microvm/APIC.pcie differ diff --git a/tests/data/acpi/microvm/DSDT b/tests/data/acpi/x86/microvm/DSDT similarity index 100% rename from tests/data/acpi/microvm/DSDT rename to tests/data/acpi/x86/microvm/DSDT diff --git a/tests/data/acpi/microvm/DSDT.ioapic2 b/tests/data/acpi/x86/microvm/DSDT.ioapic2 similarity index 100% rename from tests/data/acpi/microvm/DSDT.ioapic2 rename to tests/data/acpi/x86/microvm/DSDT.ioapic2 diff --git a/tests/data/acpi/x86/microvm/DSDT.pcie b/tests/data/acpi/x86/microvm/DSDT.pcie new file mode 100644 index 0000000000..8eacd21d6e Binary files /dev/null and b/tests/data/acpi/x86/microvm/DSDT.pcie differ diff --git a/tests/data/acpi/microvm/DSDT.rtc b/tests/data/acpi/x86/microvm/DSDT.rtc similarity index 100% rename from tests/data/acpi/microvm/DSDT.rtc rename to tests/data/acpi/x86/microvm/DSDT.rtc diff --git a/tests/data/acpi/microvm/DSDT.usb b/tests/data/acpi/x86/microvm/DSDT.usb similarity index 100% rename from tests/data/acpi/microvm/DSDT.usb rename to tests/data/acpi/x86/microvm/DSDT.usb diff --git a/tests/data/acpi/microvm/ERST.pcie b/tests/data/acpi/x86/microvm/ERST.pcie similarity index 100% rename from tests/data/acpi/microvm/ERST.pcie rename to tests/data/acpi/x86/microvm/ERST.pcie diff --git a/tests/data/acpi/microvm/FACP b/tests/data/acpi/x86/microvm/FACP similarity index 100% rename from tests/data/acpi/microvm/FACP rename to tests/data/acpi/x86/microvm/FACP diff --git a/tests/data/acpi/x86/pc/APIC b/tests/data/acpi/x86/pc/APIC new file mode 100644 index 0000000000..868a3432f0 Binary files /dev/null and b/tests/data/acpi/x86/pc/APIC differ diff --git a/tests/data/acpi/x86/pc/APIC.acpihmat b/tests/data/acpi/x86/pc/APIC.acpihmat new file mode 100644 index 0000000000..125d1ff087 Binary files /dev/null and b/tests/data/acpi/x86/pc/APIC.acpihmat differ diff --git a/tests/data/acpi/x86/pc/APIC.cphp b/tests/data/acpi/x86/pc/APIC.cphp new file mode 100644 index 0000000000..a2c2a24e5e Binary files /dev/null and b/tests/data/acpi/x86/pc/APIC.cphp differ diff --git a/tests/data/acpi/x86/pc/APIC.dimmpxm b/tests/data/acpi/x86/pc/APIC.dimmpxm new file mode 100644 index 0000000000..9b5922bc72 Binary files /dev/null and b/tests/data/acpi/x86/pc/APIC.dimmpxm differ diff --git a/tests/data/acpi/x86/pc/DSDT b/tests/data/acpi/x86/pc/DSDT new file mode 100644 index 0000000000..8b8235fe79 Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT differ diff --git a/tests/data/acpi/x86/pc/DSDT.acpierst b/tests/data/acpi/x86/pc/DSDT.acpierst new file mode 100644 index 0000000000..06829b9c6c Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.acpierst differ diff --git a/tests/data/acpi/x86/pc/DSDT.acpihmat b/tests/data/acpi/x86/pc/DSDT.acpihmat new file mode 100644 index 0000000000..2fe355ebdb Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.acpihmat differ diff --git a/tests/data/acpi/x86/pc/DSDT.bridge b/tests/data/acpi/x86/pc/DSDT.bridge new file mode 100644 index 0000000000..4d4067c182 Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.bridge differ diff --git a/tests/data/acpi/x86/pc/DSDT.cphp b/tests/data/acpi/x86/pc/DSDT.cphp new file mode 100644 index 0000000000..045a52e75b Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.cphp differ diff --git a/tests/data/acpi/x86/pc/DSDT.dimmpxm b/tests/data/acpi/x86/pc/DSDT.dimmpxm new file mode 100644 index 0000000000..205219b99d Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.dimmpxm differ diff --git a/tests/data/acpi/x86/pc/DSDT.hpbridge b/tests/data/acpi/x86/pc/DSDT.hpbridge new file mode 100644 index 0000000000..8fa8b519ec Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.hpbridge differ diff --git a/tests/data/acpi/x86/pc/DSDT.hpbrroot b/tests/data/acpi/x86/pc/DSDT.hpbrroot new file mode 100644 index 0000000000..01719462a7 Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.hpbrroot differ diff --git a/tests/data/acpi/x86/pc/DSDT.ipmikcs b/tests/data/acpi/x86/pc/DSDT.ipmikcs new file mode 100644 index 0000000000..0ca664688b Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.ipmikcs differ diff --git a/tests/data/acpi/x86/pc/DSDT.memhp b/tests/data/acpi/x86/pc/DSDT.memhp new file mode 100644 index 0000000000..03ff464ba4 Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.memhp differ diff --git a/tests/data/acpi/x86/pc/DSDT.nohpet b/tests/data/acpi/x86/pc/DSDT.nohpet new file mode 100644 index 0000000000..b081030f0e Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.nohpet differ diff --git a/tests/data/acpi/x86/pc/DSDT.numamem b/tests/data/acpi/x86/pc/DSDT.numamem new file mode 100644 index 0000000000..2c98cafbff Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.numamem differ diff --git a/tests/data/acpi/x86/pc/DSDT.roothp b/tests/data/acpi/x86/pc/DSDT.roothp new file mode 100644 index 0000000000..da018dca9e Binary files /dev/null and b/tests/data/acpi/x86/pc/DSDT.roothp differ diff --git a/tests/data/acpi/pc/ERST.acpierst b/tests/data/acpi/x86/pc/ERST.acpierst similarity index 100% rename from tests/data/acpi/pc/ERST.acpierst rename to tests/data/acpi/x86/pc/ERST.acpierst diff --git a/tests/data/acpi/pc/FACP b/tests/data/acpi/x86/pc/FACP similarity index 100% rename from tests/data/acpi/pc/FACP rename to tests/data/acpi/x86/pc/FACP diff --git a/tests/data/acpi/pc/FACP.nosmm b/tests/data/acpi/x86/pc/FACP.nosmm similarity index 100% rename from tests/data/acpi/pc/FACP.nosmm rename to tests/data/acpi/x86/pc/FACP.nosmm diff --git a/tests/data/acpi/pc/FACS b/tests/data/acpi/x86/pc/FACS similarity index 100% rename from tests/data/acpi/pc/FACS rename to tests/data/acpi/x86/pc/FACS diff --git a/tests/data/acpi/pc/HMAT.acpihmat b/tests/data/acpi/x86/pc/HMAT.acpihmat similarity index 100% rename from tests/data/acpi/pc/HMAT.acpihmat rename to tests/data/acpi/x86/pc/HMAT.acpihmat diff --git a/tests/data/acpi/pc/HPET b/tests/data/acpi/x86/pc/HPET similarity index 100% rename from tests/data/acpi/pc/HPET rename to tests/data/acpi/x86/pc/HPET diff --git a/tests/data/acpi/pc/NFIT.dimmpxm b/tests/data/acpi/x86/pc/NFIT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/NFIT.dimmpxm rename to tests/data/acpi/x86/pc/NFIT.dimmpxm diff --git a/tests/data/acpi/pc/SLIT.cphp b/tests/data/acpi/x86/pc/SLIT.cphp similarity index 100% rename from tests/data/acpi/pc/SLIT.cphp rename to tests/data/acpi/x86/pc/SLIT.cphp diff --git a/tests/data/acpi/q35/SLIT.memhp b/tests/data/acpi/x86/pc/SLIT.memhp similarity index 100% rename from tests/data/acpi/q35/SLIT.memhp rename to tests/data/acpi/x86/pc/SLIT.memhp diff --git a/tests/data/acpi/pc/SRAT.acpihmat b/tests/data/acpi/x86/pc/SRAT.acpihmat similarity index 100% rename from tests/data/acpi/pc/SRAT.acpihmat rename to tests/data/acpi/x86/pc/SRAT.acpihmat diff --git a/tests/data/acpi/pc/SRAT.cphp b/tests/data/acpi/x86/pc/SRAT.cphp similarity index 100% rename from tests/data/acpi/pc/SRAT.cphp rename to tests/data/acpi/x86/pc/SRAT.cphp diff --git a/tests/data/acpi/pc/SRAT.dimmpxm b/tests/data/acpi/x86/pc/SRAT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/SRAT.dimmpxm rename to tests/data/acpi/x86/pc/SRAT.dimmpxm diff --git a/tests/data/acpi/pc/SRAT.memhp b/tests/data/acpi/x86/pc/SRAT.memhp similarity index 100% rename from tests/data/acpi/pc/SRAT.memhp rename to tests/data/acpi/x86/pc/SRAT.memhp diff --git a/tests/data/acpi/pc/SRAT.numamem b/tests/data/acpi/x86/pc/SRAT.numamem similarity index 100% rename from tests/data/acpi/pc/SRAT.numamem rename to tests/data/acpi/x86/pc/SRAT.numamem diff --git a/tests/data/acpi/pc/SSDT.dimmpxm b/tests/data/acpi/x86/pc/SSDT.dimmpxm similarity index 100% rename from tests/data/acpi/pc/SSDT.dimmpxm rename to tests/data/acpi/x86/pc/SSDT.dimmpxm diff --git a/tests/data/acpi/pc/WAET b/tests/data/acpi/x86/pc/WAET similarity index 100% rename from tests/data/acpi/pc/WAET rename to tests/data/acpi/x86/pc/WAET diff --git a/tests/data/acpi/x86/q35/APIC b/tests/data/acpi/x86/q35/APIC new file mode 100644 index 0000000000..868a3432f0 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC differ diff --git a/tests/data/acpi/x86/q35/APIC.acpihmat b/tests/data/acpi/x86/q35/APIC.acpihmat new file mode 100644 index 0000000000..125d1ff087 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.acpihmat differ diff --git a/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x b/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x new file mode 100644 index 0000000000..317ddb3fbe Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x differ diff --git a/tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator b/tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator new file mode 100644 index 0000000000..9b5922bc72 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.acpihmat-noinitiator differ diff --git a/tests/data/acpi/x86/q35/APIC.core-count b/tests/data/acpi/x86/q35/APIC.core-count new file mode 100644 index 0000000000..d9d7ca9a89 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.core-count differ diff --git a/tests/data/acpi/x86/q35/APIC.core-count2 b/tests/data/acpi/x86/q35/APIC.core-count2 new file mode 100644 index 0000000000..4f24284434 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.core-count2 differ diff --git a/tests/data/acpi/x86/q35/APIC.cphp b/tests/data/acpi/x86/q35/APIC.cphp new file mode 100644 index 0000000000..a2c2a24e5e Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.cphp differ diff --git a/tests/data/acpi/x86/q35/APIC.dimmpxm b/tests/data/acpi/x86/q35/APIC.dimmpxm new file mode 100644 index 0000000000..9b5922bc72 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.dimmpxm differ diff --git a/tests/data/acpi/x86/q35/APIC.thread-count b/tests/data/acpi/x86/q35/APIC.thread-count new file mode 100644 index 0000000000..c27e87fcf1 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.thread-count differ diff --git a/tests/data/acpi/x86/q35/APIC.thread-count2 b/tests/data/acpi/x86/q35/APIC.thread-count2 new file mode 100644 index 0000000000..ac200ab7aa Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.thread-count2 differ diff --git a/tests/data/acpi/x86/q35/APIC.type4-count b/tests/data/acpi/x86/q35/APIC.type4-count new file mode 100644 index 0000000000..ab60a6ef06 Binary files /dev/null and b/tests/data/acpi/x86/q35/APIC.type4-count differ diff --git a/tests/data/acpi/q35/APIC.xapic b/tests/data/acpi/x86/q35/APIC.xapic similarity index 97% rename from tests/data/acpi/q35/APIC.xapic rename to tests/data/acpi/x86/q35/APIC.xapic index c1969c35aa..83bd28325a 100644 Binary files a/tests/data/acpi/q35/APIC.xapic and b/tests/data/acpi/x86/q35/APIC.xapic differ diff --git a/tests/data/acpi/x86/q35/CEDT.acpihmat-generic-x b/tests/data/acpi/x86/q35/CEDT.acpihmat-generic-x new file mode 100644 index 0000000000..31c9011663 Binary files /dev/null and b/tests/data/acpi/x86/q35/CEDT.acpihmat-generic-x differ diff --git a/tests/data/acpi/q35/CEDT.cxl b/tests/data/acpi/x86/q35/CEDT.cxl similarity index 100% rename from tests/data/acpi/q35/CEDT.cxl rename to tests/data/acpi/x86/q35/CEDT.cxl diff --git a/tests/data/acpi/q35/DMAR.dmar b/tests/data/acpi/x86/q35/DMAR.dmar similarity index 100% rename from tests/data/acpi/q35/DMAR.dmar rename to tests/data/acpi/x86/q35/DMAR.dmar diff --git a/tests/data/acpi/x86/q35/DSDT b/tests/data/acpi/x86/q35/DSDT new file mode 100644 index 0000000000..fb89ae0ac6 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT differ diff --git a/tests/data/acpi/x86/q35/DSDT.acpierst b/tests/data/acpi/x86/q35/DSDT.acpierst new file mode 100644 index 0000000000..46fd25400b Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.acpierst differ diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat b/tests/data/acpi/x86/q35/DSDT.acpihmat new file mode 100644 index 0000000000..61c5bd52a4 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.acpihmat differ diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x b/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x new file mode 100644 index 0000000000..497706c974 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x differ diff --git a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator similarity index 95% rename from tests/data/acpi/q35/DSDT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator index 279fafa821..3aaa2bbdf5 100644 Binary files a/tests/data/acpi/q35/DSDT.acpihmat-noinitiator and b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator differ diff --git a/tests/data/acpi/q35/DSDT.applesmc b/tests/data/acpi/x86/q35/DSDT.applesmc similarity index 94% rename from tests/data/acpi/q35/DSDT.applesmc rename to tests/data/acpi/x86/q35/DSDT.applesmc index fdf6d14428..944209adea 100644 Binary files a/tests/data/acpi/q35/DSDT.applesmc and b/tests/data/acpi/x86/q35/DSDT.applesmc differ diff --git a/tests/data/acpi/x86/q35/DSDT.bridge b/tests/data/acpi/x86/q35/DSDT.bridge new file mode 100644 index 0000000000..d9938dba8f Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.bridge differ diff --git a/tests/data/acpi/x86/q35/DSDT.core-count b/tests/data/acpi/x86/q35/DSDT.core-count new file mode 100644 index 0000000000..a24b04cbdb Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.core-count differ diff --git a/tests/data/acpi/x86/q35/DSDT.core-count2 b/tests/data/acpi/x86/q35/DSDT.core-count2 new file mode 100644 index 0000000000..3a0cb8c581 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.core-count2 differ diff --git a/tests/data/acpi/x86/q35/DSDT.cphp b/tests/data/acpi/x86/q35/DSDT.cphp new file mode 100644 index 0000000000..20955d0aa3 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.cphp differ diff --git a/tests/data/acpi/x86/q35/DSDT.cxl b/tests/data/acpi/x86/q35/DSDT.cxl new file mode 100644 index 0000000000..3c34d4dcab Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.cxl differ diff --git a/tests/data/acpi/x86/q35/DSDT.dimmpxm b/tests/data/acpi/x86/q35/DSDT.dimmpxm new file mode 100644 index 0000000000..228374b55b Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.dimmpxm differ diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/x86/q35/DSDT.ipmibt similarity index 94% rename from tests/data/acpi/q35/DSDT.ipmibt rename to tests/data/acpi/x86/q35/DSDT.ipmibt index 9c52529919..45f911ada5 100644 Binary files a/tests/data/acpi/q35/DSDT.ipmibt and b/tests/data/acpi/x86/q35/DSDT.ipmibt differ diff --git a/tests/data/acpi/q35/DSDT.ipmismbus b/tests/data/acpi/x86/q35/DSDT.ipmismbus similarity index 94% rename from tests/data/acpi/q35/DSDT.ipmismbus rename to tests/data/acpi/x86/q35/DSDT.ipmismbus index 3f32dffdbf..e5d6811bee 100644 Binary files a/tests/data/acpi/q35/DSDT.ipmismbus and b/tests/data/acpi/x86/q35/DSDT.ipmismbus differ diff --git a/tests/data/acpi/q35/DSDT.ivrs b/tests/data/acpi/x86/q35/DSDT.ivrs similarity index 94% rename from tests/data/acpi/q35/DSDT.ivrs rename to tests/data/acpi/x86/q35/DSDT.ivrs index b45abca7c2..46fd25400b 100644 Binary files a/tests/data/acpi/q35/DSDT.ivrs and b/tests/data/acpi/x86/q35/DSDT.ivrs differ diff --git a/tests/data/acpi/x86/q35/DSDT.memhp b/tests/data/acpi/x86/q35/DSDT.memhp new file mode 100644 index 0000000000..5ce081187a Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.memhp differ diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/x86/q35/DSDT.mmio64 similarity index 94% rename from tests/data/acpi/q35/DSDT.mmio64 rename to tests/data/acpi/x86/q35/DSDT.mmio64 index 8fda921296..bdf36c4d57 100644 Binary files a/tests/data/acpi/q35/DSDT.mmio64 and b/tests/data/acpi/x86/q35/DSDT.mmio64 differ diff --git a/tests/data/acpi/x86/q35/DSDT.multi-bridge b/tests/data/acpi/x86/q35/DSDT.multi-bridge new file mode 100644 index 0000000000..1db43a69e4 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.multi-bridge differ diff --git a/tests/data/acpi/x86/q35/DSDT.noacpihp b/tests/data/acpi/x86/q35/DSDT.noacpihp new file mode 100644 index 0000000000..8bc16887e1 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.noacpihp differ diff --git a/tests/data/acpi/x86/q35/DSDT.nohpet b/tests/data/acpi/x86/q35/DSDT.nohpet new file mode 100644 index 0000000000..c13e45e361 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.nohpet differ diff --git a/tests/data/acpi/x86/q35/DSDT.numamem b/tests/data/acpi/x86/q35/DSDT.numamem new file mode 100644 index 0000000000..ba6669437e Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.numamem differ diff --git a/tests/data/acpi/q35/DSDT.pvpanic-isa b/tests/data/acpi/x86/q35/DSDT.pvpanic-isa similarity index 94% rename from tests/data/acpi/q35/DSDT.pvpanic-isa rename to tests/data/acpi/x86/q35/DSDT.pvpanic-isa index 908e7b6606..6ad42873e9 100644 Binary files a/tests/data/acpi/q35/DSDT.pvpanic-isa and b/tests/data/acpi/x86/q35/DSDT.pvpanic-isa differ diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count b/tests/data/acpi/x86/q35/DSDT.thread-count new file mode 100644 index 0000000000..a24b04cbdb Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.thread-count differ diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count2 b/tests/data/acpi/x86/q35/DSDT.thread-count2 new file mode 100644 index 0000000000..3a0cb8c581 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.thread-count2 differ diff --git a/tests/data/acpi/q35/DSDT.tis.tpm12 b/tests/data/acpi/x86/q35/DSDT.tis.tpm12 similarity index 95% rename from tests/data/acpi/q35/DSDT.tis.tpm12 rename to tests/data/acpi/x86/q35/DSDT.tis.tpm12 index ce2c2c29c2..e381ce4cbf 100644 Binary files a/tests/data/acpi/q35/DSDT.tis.tpm12 and b/tests/data/acpi/x86/q35/DSDT.tis.tpm12 differ diff --git a/tests/data/acpi/q35/DSDT.tis.tpm2 b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 similarity index 95% rename from tests/data/acpi/q35/DSDT.tis.tpm2 rename to tests/data/acpi/x86/q35/DSDT.tis.tpm2 index e9e4b7f6ed..a09253042c 100644 Binary files a/tests/data/acpi/q35/DSDT.tis.tpm2 and b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 differ diff --git a/tests/data/acpi/x86/q35/DSDT.type4-count b/tests/data/acpi/x86/q35/DSDT.type4-count new file mode 100644 index 0000000000..edc23198cd Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.type4-count differ diff --git a/tests/data/acpi/x86/q35/DSDT.viot b/tests/data/acpi/x86/q35/DSDT.viot new file mode 100644 index 0000000000..4c93dfd5c4 Binary files /dev/null and b/tests/data/acpi/x86/q35/DSDT.viot differ diff --git a/tests/data/acpi/q35/DSDT.xapic b/tests/data/acpi/x86/q35/DSDT.xapic similarity index 98% rename from tests/data/acpi/q35/DSDT.xapic rename to tests/data/acpi/x86/q35/DSDT.xapic index f47f091222..d4acd851c6 100644 Binary files a/tests/data/acpi/q35/DSDT.xapic and b/tests/data/acpi/x86/q35/DSDT.xapic differ diff --git a/tests/data/acpi/q35/ERST.acpierst b/tests/data/acpi/x86/q35/ERST.acpierst similarity index 100% rename from tests/data/acpi/q35/ERST.acpierst rename to tests/data/acpi/x86/q35/ERST.acpierst diff --git a/tests/data/acpi/q35/FACP b/tests/data/acpi/x86/q35/FACP similarity index 100% rename from tests/data/acpi/q35/FACP rename to tests/data/acpi/x86/q35/FACP diff --git a/tests/data/acpi/q35/FACP.core-count2 b/tests/data/acpi/x86/q35/FACP.core-count similarity index 100% rename from tests/data/acpi/q35/FACP.core-count2 rename to tests/data/acpi/x86/q35/FACP.core-count diff --git a/tests/data/acpi/q35/FACP.xapic b/tests/data/acpi/x86/q35/FACP.core-count2 similarity index 100% rename from tests/data/acpi/q35/FACP.xapic rename to tests/data/acpi/x86/q35/FACP.core-count2 diff --git a/tests/data/acpi/q35/FACP.nosmm b/tests/data/acpi/x86/q35/FACP.nosmm similarity index 100% rename from tests/data/acpi/q35/FACP.nosmm rename to tests/data/acpi/x86/q35/FACP.nosmm diff --git a/tests/data/acpi/q35/FACP.slic b/tests/data/acpi/x86/q35/FACP.slic similarity index 100% rename from tests/data/acpi/q35/FACP.slic rename to tests/data/acpi/x86/q35/FACP.slic diff --git a/tests/data/acpi/x86/q35/FACP.thread-count b/tests/data/acpi/x86/q35/FACP.thread-count new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/x86/q35/FACP.thread-count differ diff --git a/tests/data/acpi/x86/q35/FACP.thread-count2 b/tests/data/acpi/x86/q35/FACP.thread-count2 new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/x86/q35/FACP.thread-count2 differ diff --git a/tests/data/acpi/x86/q35/FACP.type4-count b/tests/data/acpi/x86/q35/FACP.type4-count new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/x86/q35/FACP.type4-count differ diff --git a/tests/data/acpi/x86/q35/FACP.xapic b/tests/data/acpi/x86/q35/FACP.xapic new file mode 100644 index 0000000000..31fa5dd19c Binary files /dev/null and b/tests/data/acpi/x86/q35/FACP.xapic differ diff --git a/tests/data/acpi/q35/FACS b/tests/data/acpi/x86/q35/FACS similarity index 100% rename from tests/data/acpi/q35/FACS rename to tests/data/acpi/x86/q35/FACS diff --git a/tests/data/acpi/q35/HMAT.acpihmat b/tests/data/acpi/x86/q35/HMAT.acpihmat similarity index 100% rename from tests/data/acpi/q35/HMAT.acpihmat rename to tests/data/acpi/x86/q35/HMAT.acpihmat diff --git a/tests/data/acpi/x86/q35/HMAT.acpihmat-generic-x b/tests/data/acpi/x86/q35/HMAT.acpihmat-generic-x new file mode 100644 index 0000000000..0e5765f6ee Binary files /dev/null and b/tests/data/acpi/x86/q35/HMAT.acpihmat-generic-x differ diff --git a/tests/data/acpi/q35/HMAT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/HMAT.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/HMAT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/HMAT.acpihmat-noinitiator diff --git a/tests/data/acpi/q35/HPET b/tests/data/acpi/x86/q35/HPET similarity index 100% rename from tests/data/acpi/q35/HPET rename to tests/data/acpi/x86/q35/HPET diff --git a/tests/data/acpi/x86/q35/IVRS.ivrs b/tests/data/acpi/x86/q35/IVRS.ivrs new file mode 100644 index 0000000000..7f9e91aabc Binary files /dev/null and b/tests/data/acpi/x86/q35/IVRS.ivrs differ diff --git a/tests/data/acpi/q35/MCFG b/tests/data/acpi/x86/q35/MCFG similarity index 100% rename from tests/data/acpi/q35/MCFG rename to tests/data/acpi/x86/q35/MCFG diff --git a/tests/data/acpi/q35/NFIT.dimmpxm b/tests/data/acpi/x86/q35/NFIT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/NFIT.dimmpxm rename to tests/data/acpi/x86/q35/NFIT.dimmpxm diff --git a/tests/data/acpi/q35/SLIC.slic b/tests/data/acpi/x86/q35/SLIC.slic similarity index 100% rename from tests/data/acpi/q35/SLIC.slic rename to tests/data/acpi/x86/q35/SLIC.slic diff --git a/tests/data/acpi/q35/SLIT.cphp b/tests/data/acpi/x86/q35/SLIT.cphp similarity index 100% rename from tests/data/acpi/q35/SLIT.cphp rename to tests/data/acpi/x86/q35/SLIT.cphp diff --git a/tests/data/acpi/virt/SLIT.memhp b/tests/data/acpi/x86/q35/SLIT.memhp similarity index 100% rename from tests/data/acpi/virt/SLIT.memhp rename to tests/data/acpi/x86/q35/SLIT.memhp diff --git a/tests/data/acpi/q35/SRAT.acpihmat b/tests/data/acpi/x86/q35/SRAT.acpihmat similarity index 100% rename from tests/data/acpi/q35/SRAT.acpihmat rename to tests/data/acpi/x86/q35/SRAT.acpihmat diff --git a/tests/data/acpi/x86/q35/SRAT.acpihmat-generic-x b/tests/data/acpi/x86/q35/SRAT.acpihmat-generic-x new file mode 100644 index 0000000000..b45838adb7 Binary files /dev/null and b/tests/data/acpi/x86/q35/SRAT.acpihmat-generic-x differ diff --git a/tests/data/acpi/q35/SRAT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/SRAT.acpihmat-noinitiator similarity index 100% rename from tests/data/acpi/q35/SRAT.acpihmat-noinitiator rename to tests/data/acpi/x86/q35/SRAT.acpihmat-noinitiator diff --git a/tests/data/acpi/q35/SRAT.cphp b/tests/data/acpi/x86/q35/SRAT.cphp similarity index 100% rename from tests/data/acpi/q35/SRAT.cphp rename to tests/data/acpi/x86/q35/SRAT.cphp diff --git a/tests/data/acpi/q35/SRAT.dimmpxm b/tests/data/acpi/x86/q35/SRAT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/SRAT.dimmpxm rename to tests/data/acpi/x86/q35/SRAT.dimmpxm diff --git a/tests/data/acpi/q35/SRAT.memhp b/tests/data/acpi/x86/q35/SRAT.memhp similarity index 100% rename from tests/data/acpi/q35/SRAT.memhp rename to tests/data/acpi/x86/q35/SRAT.memhp diff --git a/tests/data/acpi/q35/SRAT.mmio64 b/tests/data/acpi/x86/q35/SRAT.mmio64 similarity index 100% rename from tests/data/acpi/q35/SRAT.mmio64 rename to tests/data/acpi/x86/q35/SRAT.mmio64 diff --git a/tests/data/acpi/q35/SRAT.numamem b/tests/data/acpi/x86/q35/SRAT.numamem similarity index 100% rename from tests/data/acpi/q35/SRAT.numamem rename to tests/data/acpi/x86/q35/SRAT.numamem diff --git a/tests/data/acpi/q35/SRAT.xapic b/tests/data/acpi/x86/q35/SRAT.xapic similarity index 100% rename from tests/data/acpi/q35/SRAT.xapic rename to tests/data/acpi/x86/q35/SRAT.xapic diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/x86/q35/SSDT.dimmpxm similarity index 100% rename from tests/data/acpi/q35/SSDT.dimmpxm rename to tests/data/acpi/x86/q35/SSDT.dimmpxm diff --git a/tests/data/acpi/q35/TCPA.tis.tpm12 b/tests/data/acpi/x86/q35/TCPA.tis.tpm12 similarity index 100% rename from tests/data/acpi/q35/TCPA.tis.tpm12 rename to tests/data/acpi/x86/q35/TCPA.tis.tpm12 diff --git a/tests/data/acpi/q35/TPM2.tis.tpm2 b/tests/data/acpi/x86/q35/TPM2.tis.tpm2 similarity index 100% rename from tests/data/acpi/q35/TPM2.tis.tpm2 rename to tests/data/acpi/x86/q35/TPM2.tis.tpm2 diff --git a/tests/data/acpi/q35/VIOT.viot b/tests/data/acpi/x86/q35/VIOT.viot similarity index 100% rename from tests/data/acpi/q35/VIOT.viot rename to tests/data/acpi/x86/q35/VIOT.viot diff --git a/tests/data/acpi/q35/WAET b/tests/data/acpi/x86/q35/WAET similarity index 100% rename from tests/data/acpi/q35/WAET rename to tests/data/acpi/x86/q35/WAET diff --git a/tests/data/qobject/qdict.txt b/tests/data/qobject/qdict.txt index 122fda4524..888f3431b5 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 @@ -3491,12 +3487,6 @@ cred-internals.h: 559 CREDITS: 603 crime.c: 2833 crime.h: 5271 -cris: 4096 -cris_defs_asm.h: 3805 -crisksyms.c: 472 -cris_supp_reg.h: 198 -crisv10.c: 129158 -crisv10.h: 4289 crm_regs.h: 1700 cr_pll.c: 4842 crt0_ram.S: 2152 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/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 new file mode 100644 index 0000000000..c720bf99a4 Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2 differ 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 fc7a3b7e71..fead7d3abe 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 @@ -84,17 +88,14 @@ DOCKER_PARTIAL_IMAGES += debian-s390x-cross DOCKER_PARTIAL_IMAGES += fedora endif -# The native build should never use the registry -docker-image-debian-native: DOCKER_REGISTRY= - # alpine has no adduser docker-image-alpine: NOUSER=1 debian-toolchain-run = \ - $(if $(NOCACHE), \ + $(if $(NOCACHE)$(NOFETCH), \ $(call quiet-command, \ $(DOCKER_SCRIPT) build -t qemu/$1 -f $< \ - $(if $V,,--quiet) --no-cache \ + $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ --registry $(DOCKER_REGISTRY) --extra-files \ $(DOCKER_FILES_DIR)/$1.d/build-toolchain.sh, \ "BUILD", $1), \ @@ -109,32 +110,13 @@ 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, $@) -docker-image-debian-nios2-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docker \ - $(DOCKER_FILES_DIR)/debian-nios2-cross.d/build-toolchain.sh - $(call debian-toolchain, $@) - # 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-loongarch-cross -DOCKER_PARTIAL_IMAGES += debian-m68k-cross debian-mips64-cross DOCKER_PARTIAL_IMAGES += debian-microblaze-cross -DOCKER_PARTIAL_IMAGES += debian-mips-cross -DOCKER_PARTIAL_IMAGES += debian-nios2-cross -DOCKER_PARTIAL_IMAGES += debian-riscv64-test-cross -DOCKER_PARTIAL_IMAGES += debian-sh4-cross debian-sparc64-cross -DOCKER_PARTIAL_IMAGES += debian-tricore-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross -DOCKER_PARTIAL_IMAGES += fedora-cris-cross # images that are only used to build other images DOCKER_VIRTUAL_IMAGES := debian-bootstrap debian-toolchain @@ -158,7 +140,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 @@ -195,11 +177,10 @@ docker: @echo ' NETWORK=$$BACKEND Enable virtual network interface with $$BACKEND.' @echo ' NOUSER=1 Define to disable adding current user to containers passwd.' @echo ' NOCACHE=1 Ignore cache when build images.' + @echo ' NOFETCH=1 Do not fetch from the registry.' @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 @@ -224,8 +205,14 @@ 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),, \ + $(if $(filter docker,$(RUNC)), \ + -u $(UID), \ + --userns keep-id \ + ) \ + ) \ --security-opt seccomp=unconfined \ $(if $(DEBUG),-ti,) \ $(if $(NETWORK),$(if $(subst $(NETWORK),,1),--net=$(NETWORK)),--net=none) \ diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 9a33df2832..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 diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 3a1ed7cb18..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" % @@ -316,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)) @@ -570,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 094f66f4eb..f87c40fbfe 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-316 qemu +# $ lcitool dockerfile --layers all alpine-319 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/alpine:3.16 +FROM docker.io/library/alpine:3.19 RUN apk update && \ apk upgrade && \ @@ -19,7 +19,6 @@ RUN apk update && \ ca-certificates \ capstone-dev \ ccache \ - cdrkit \ ceph-dev \ clang \ cmocka-dev \ @@ -33,7 +32,6 @@ RUN apk update && \ findutils \ flex \ fuse3-dev \ - g++ \ gcc \ gcovr \ gettext \ @@ -42,10 +40,12 @@ RUN apk update && \ glib-static \ gnutls-dev \ gtk+3.0-dev \ + gtk-vnc-dev \ json-c-dev \ libaio-dev \ libbpf-dev \ libcap-ng-dev \ + libcbor-dev \ libdrm-dev \ libepoxy-dev \ libffi-dev \ @@ -60,13 +60,15 @@ 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 \ @@ -77,7 +79,7 @@ RUN apk update && \ numactl-dev \ openssh-client \ pcre-dev \ - perl \ + pipewire-dev \ pixman-dev \ pkgconf \ pulseaudio-dev \ @@ -89,18 +91,21 @@ RUN apk update && \ py3-yaml \ python3 \ rpm2cpio \ + rust \ + rust-bindgen \ samurai \ sdl2-dev \ 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 \ @@ -108,16 +113,16 @@ RUN apk update && \ vte3-dev \ which \ xen-dev \ - xfsprogs-dev \ + xorriso \ zlib-dev \ zlib-static \ + zstd \ zstd-dev && \ - apk list | sort > /packages.txt && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + apk list --installed | 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" @@ -125,3 +130,8 @@ 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/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker deleted file mode 100644 index 1f70d41aeb..0000000000 --- a/tests/docker/dockerfiles/centos8.docker +++ /dev/null @@ -1,137 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all centos-stream-8 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM quay.io/centos/centos:stream8 - -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 \ - 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++ \ - 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 \ - perl \ - pixman-devel \ - pkgconfig \ - pulseaudio-libs-devel \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx_rtd_theme \ - rdma-core-devel \ - rpm \ - sed \ - snappy-devel \ - spice-protocol \ - spice-server-devel \ - systemd-devel \ - systemtap-sdt-devel \ - tar \ - texinfo \ - usbredir-devel \ - util-linux \ - virglrenderer-devel \ - vte291-devel \ - which \ - xfsprogs-devel \ - zlib-devel \ - zlib-static && \ - dnf autoremove -y && \ - 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" diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker new file mode 100644 index 0000000000..a9681c8a96 --- /dev/null +++ b/tests/docker/dockerfiles/centos9.docker @@ -0,0 +1,141 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all centos-stream-9 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +FROM quay.io/centos/centos:stream9 + +RUN dnf distro-sync -y && \ + dnf install 'dnf-command(config-manager)' -y && \ + dnf config-manager --set-enabled -y crb && \ + dnf install -y epel-release && \ + dnf install -y epel-next-release && \ + dnf install -y \ + SDL2-devel \ + alsa-lib-devel \ + bash \ + bc \ + bindgen-cli \ + 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 \ + gettext \ + git \ + glib2-devel \ + glib2-static \ + glibc-langpack-en \ + glibc-static \ + 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-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx_rtd_theme \ + python3-tomli \ + rdma-core-devel \ + rust \ + sed \ + snappy-devel \ + socat \ + spice-protocol \ + swtpm \ + systemd-devel \ + systemtap-sdt-devel \ + tar \ + usbredir-devel \ + util-linux \ + vte291-devel \ + which \ + xorriso \ + zlib-devel \ + zlib-static \ + zstd && \ + dnf autoremove -y && \ + dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + rpm -qa | sort > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + 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/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/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 8dc5e1b5de..8ab244e018 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -6,10 +6,10 @@ # basic compilers for as many targets as possible. We shall use this # to build and run linux-user tests on GitLab # -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim # Duplicate deb line as deb-src -RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list +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 && \ @@ -25,11 +25,18 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ 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 \ @@ -54,10 +61,16 @@ 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 + libc6-dev-sparc64-cross && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt + 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) +ENV MAKE /usr/bin/make +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 4eeb43c78a..0000000000 --- a/tests/docker/dockerfiles/debian-alpha-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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 5e57309361..644fd3734d 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross x86_64 debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch x86_64 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,10 +78,9 @@ 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++-x86-64-linux-gnu \ gcc-x86-64-linux-gnu \ libaio-dev:amd64 \ - libasan5:amd64 \ + libasan6:amd64 \ libasound2-dev:amd64 \ libattr1-dev:amd64 \ libbpf-dev:amd64 \ @@ -88,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:amd64 \ libcap-ng-dev:amd64 \ libcapstone-dev:amd64 \ + libcbor-dev:amd64 \ libcmocka-dev:amd64 \ libcurl4-gnutls-dev:amd64 \ libdaxctl-dev:amd64 \ @@ -102,7 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:amd64 \ libgnutls28-dev:amd64 \ libgtk-3-dev:amd64 \ - libibumad-dev:amd64 \ + libgtk-vnc-2.0-dev:amd64 \ libibverbs-dev:amd64 \ libiscsi-dev:amd64 \ libjemalloc-dev:amd64 \ @@ -114,6 +117,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:amd64 \ libnuma-dev:amd64 \ libpam0g-dev:amd64 \ + libpcre2-dev:amd64 \ + libpipewire-0.3-dev:amd64 \ libpixman-1-dev:amd64 \ libpmem-dev:amd64 \ libpng-dev:amd64 \ @@ -127,6 +132,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:amd64 \ libslirp-dev:amd64 \ libsnappy-dev:amd64 \ + libsndio-dev:amd64 \ + libspice-protocol-dev:amd64 \ libspice-server-dev:amd64 \ libssh-gcrypt-dev:amd64 \ libsystemd-dev:amd64 \ @@ -139,16 +146,16 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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 && \ - echo "[binaries]\n\ + 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\ @@ -158,15 +165,19 @@ pkgconfig = '/usr/bin/x86_64-linux-gnu-pkg-config'\n\ system = 'linux'\n\ cpu_family = 'x86_64'\n\ cpu = 'x86_64'\n\ -endian = 'little'" > /usr/local/share/meson/cross/x86_64-linux-gnu && \ +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 RUST_TARGET "x86_64-unknown-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 bfeab01ee3..0000000000 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ /dev/null @@ -1,158 +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 \ - bison \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - clang \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - flex \ - 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 \ - 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 \ - 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 \ - 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 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-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 98885bd0ee..060da53796 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -1,10 +1,10 @@ # 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,10 +78,9 @@ 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 \ + libasan6:arm64 \ libasound2-dev:arm64 \ libattr1-dev:arm64 \ libbpf-dev:arm64 \ @@ -88,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:arm64 \ libcap-ng-dev:arm64 \ libcapstone-dev:arm64 \ + libcbor-dev:arm64 \ libcmocka-dev:arm64 \ libcurl4-gnutls-dev:arm64 \ libdaxctl-dev:arm64 \ @@ -102,7 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:arm64 \ libgnutls28-dev:arm64 \ libgtk-3-dev:arm64 \ - libibumad-dev:arm64 \ + libgtk-vnc-2.0-dev:arm64 \ libibverbs-dev:arm64 \ libiscsi-dev:arm64 \ libjemalloc-dev:arm64 \ @@ -114,6 +117,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:arm64 \ libnuma-dev:arm64 \ libpam0g-dev:arm64 \ + libpcre2-dev:arm64 \ + libpipewire-0.3-dev:arm64 \ libpixman-1-dev:arm64 \ libpng-dev:arm64 \ libpulse-dev:arm64 \ @@ -126,6 +131,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:arm64 \ libslirp-dev:arm64 \ libsnappy-dev:arm64 \ + libsndio-dev:arm64 \ + libspice-protocol-dev:arm64 \ libspice-server-dev:arm64 \ libssh-gcrypt-dev:arm64 \ libsystemd-dev:arm64 \ @@ -138,16 +145,16 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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\ @@ -157,15 +164,19 @@ 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++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/aarch64-linux-gnu-gcc ENV ABI "aarch64-linux-gnu" ENV MESON_OPTS "--cross-file=aarch64-linux-gnu" +ENV RUST_TARGET "aarch64-unknown-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 deleted file mode 100644 index d5c08714e4..0000000000 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ /dev/null @@ -1,170 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all --cross armv6l 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 \ - gcovr \ - genisoimage \ - gettext \ - git \ - hostname \ - libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - ncat \ - ninja-build \ - openssh-client \ - perl-base \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-yaml \ - rpm2cpio \ - sed \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - texinfo && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales - -ENV 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 armel && \ - 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++-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 \ - 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 \ - 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\ -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\ -pkgconfig = '/usr/bin/arm-linux-gnueabi-pkg-config'\n\ -\n\ -[host_machine]\n\ -system = 'linux'\n\ -cpu_family = 'arm'\n\ -cpu = 'arm'\n\ -endian = 'little'" > /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++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-g++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-gcc - -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 diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 471444fcf4..a481fc9695 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -1,10 +1,10 @@ # 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,10 +78,9 @@ 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 \ + libasan6:armhf \ libasound2-dev:armhf \ libattr1-dev:armhf \ libbpf-dev:armhf \ @@ -88,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:armhf \ libcap-ng-dev:armhf \ libcapstone-dev:armhf \ + libcbor-dev:armhf \ libcmocka-dev:armhf \ libcurl4-gnutls-dev:armhf \ libdaxctl-dev:armhf \ @@ -102,7 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:armhf \ libgnutls28-dev:armhf \ libgtk-3-dev:armhf \ - libibumad-dev:armhf \ + libgtk-vnc-2.0-dev:armhf \ libibverbs-dev:armhf \ libiscsi-dev:armhf \ libjemalloc-dev:armhf \ @@ -114,6 +117,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:armhf \ libnuma-dev:armhf \ libpam0g-dev:armhf \ + libpcre2-dev:armhf \ + libpipewire-0.3-dev:armhf \ libpixman-1-dev:armhf \ libpng-dev:armhf \ libpulse-dev:armhf \ @@ -126,6 +131,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:armhf \ libslirp-dev:armhf \ libsnappy-dev:armhf \ + libsndio-dev:armhf \ + libspice-protocol-dev:armhf \ libspice-server-dev:armhf \ libssh-gcrypt-dev:armhf \ libsystemd-dev:armhf \ @@ -138,16 +145,16 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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\ @@ -157,15 +164,19 @@ 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++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabihf-gcc ENV ABI "arm-linux-gnueabihf" ENV MESON_OPTS "--cross-file=arm-linux-gnueabihf" +ENV RUST_TARGET "armv7-unknown-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 c4238e893f..23152b4918 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -7,44 +7,48 @@ # 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 \ - ca-certificates \ - clang \ - cmake \ - flex \ - gcc \ - lld \ - make \ - ninja-build \ - python3 \ - rsync \ - wget \ - xz-utils - -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 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 -# 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 bison flex 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 && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt + +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 +ENV MAKE /usr/bin/make +# 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 af1c8403d8..0000000000 --- a/tests/docker/dockerfiles/debian-hppa-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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..61bc361e85 --- /dev/null +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -0,0 +1,181 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all --cross-arch i686 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 \ + bindgen \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + gcc \ + gcovr \ + gettext \ + git \ + hostname \ + libglib2.0-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 \ + rustc \ + 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 && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED + +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 \ + 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 \ + libcbor-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 \ + libgtk-vnc-2.0-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 \ + libpcre2-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 \ + libsndio-dev:i386 \ + libspice-protocol-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 \ + libxdp-dev:i386 \ + libzstd-dev:i386 \ + nettle-dev:i386 \ + systemtap-sdt-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-cc && \ + 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 RUST_TARGET "i686-unknown-linux-gnu" +ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-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..5a6616b7d3 --- /dev/null +++ b/tests/docker/dockerfiles/debian-legacy-test-cross.docker @@ -0,0 +1,51 @@ +# 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 && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt + +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 +ENV MAKE /usr/bin/make +# 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 index a8e8e98909..538ab53490 100644 --- a/tests/docker/dockerfiles/debian-loongarch-cross.docker +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -9,19 +9,46 @@ 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 \ - python3-minimal + ninja-build \ + python3-pip \ + python3-setuptools \ + python3-venv \ + python3-wheel && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -RUN curl -#SL https://github.com/loongson/build-tools/releases/download/2022.05.29/loongarch64-clfs-5.0-cross-tools-gcc-glibc.tar.xz \ +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-docs --disable-tools +ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch64-softmmu +ENV MAKE /usr/bin/make + +# 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 dded71c5d2..0000000000 --- a/tests/docker/dockerfiles/debian-m68k-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - gcc-m68k-linux-gnu \ - libc6-dev-m68k-cross diff --git a/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh index 23ec0aa9a7..c5cd0aa931 100755 --- a/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh +++ b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh @@ -10,6 +10,8 @@ TOOLCHAIN_INSTALL=/usr/local TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin CROSS_SYSROOT=${TOOLCHAIN_INSTALL}/$TARGET/sys-root +GCC_PATCH0_URL=https://raw.githubusercontent.com/Xilinx/meta-xilinx/refs/tags/xlnx-rel-v2024.1/meta-microblaze/recipes-devtools/gcc/gcc-12/0009-Patch-microblaze-Fix-atomic-boolean-return-value.patch + export PATH=${TOOLCHAIN_BIN}:$PATH # @@ -31,6 +33,12 @@ mv gcc-11.2.0 src-gcc mv musl-1.2.2 src-musl mv linux-5.10.70 src-linux +# +# Patch gcc +# + +wget -O - ${GCC_PATCH0_URL} | patch -d src-gcc -p1 + mkdir -p bld-hdr bld-binu bld-gcc bld-musl mkdir -p ${CROSS_SYSROOT}/usr/include diff --git a/tests/docker/dockerfiles/debian-mips-cross.docker b/tests/docker/dockerfiles/debian-mips-cross.docker deleted file mode 100644 index 7b55f0f3b2..0000000000 --- a/tests/docker/dockerfiles/debian-mips-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker mips cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - gcc-mips-linux-gnu \ - libc6-dev-mips-cross diff --git a/tests/docker/dockerfiles/debian-mips64-cross.docker b/tests/docker/dockerfiles/debian-mips64-cross.docker deleted file mode 100644 index afcff9726f..0000000000 --- a/tests/docker/dockerfiles/debian-mips64-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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 15b0224b76..c09a8da890 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mips64el debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mips64el 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,7 +78,6 @@ 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 \ @@ -87,21 +89,17 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:mips64el \ libcap-ng-dev:mips64el \ libcapstone-dev:mips64el \ + libcbor-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 \ @@ -113,18 +111,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:mips64el \ libnuma-dev:mips64el \ libpam0g-dev:mips64el \ + libpcre2-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 \ + libsndio-dev:mips64el \ + libspice-protocol-dev:mips64el \ libspice-server-dev:mips64el \ libssh-gcrypt-dev:mips64el \ libsystemd-dev:mips64el \ @@ -134,17 +134,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libusb-1.0-0-dev:mips64el \ libusbredirhost-dev:mips64el \ libvdeplug-dev:mips64el \ - libvirglrenderer-dev:mips64el \ - libvte-2.91-dev:mips64el \ + libxdp-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\ @@ -154,15 +152,19 @@ 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++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mips64el-linux-gnuabi64-gcc ENV ABI "mips64el-linux-gnuabi64" ENV MESON_OPTS "--cross-file=mips64el-linux-gnuabi64" +ENV RUST_TARGET "mips64el-unknown-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 a5d3ca6e2f..2e979111e0 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mipsel debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mipsel 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,7 +78,6 @@ 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 \ @@ -87,6 +89,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:mipsel \ libcap-ng-dev:mipsel \ libcapstone-dev:mipsel \ + libcbor-dev:mipsel \ libcmocka-dev:mipsel \ libcurl4-gnutls-dev:mipsel \ libdaxctl-dev:mipsel \ @@ -101,7 +104,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:mipsel \ libgnutls28-dev:mipsel \ libgtk-3-dev:mipsel \ - libibumad-dev:mipsel \ + libgtk-vnc-2.0-dev:mipsel \ libibverbs-dev:mipsel \ libiscsi-dev:mipsel \ libjemalloc-dev:mipsel \ @@ -113,6 +116,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:mipsel \ libnuma-dev:mipsel \ libpam0g-dev:mipsel \ + libpcre2-dev:mipsel \ + libpipewire-0.3-dev:mipsel \ libpixman-1-dev:mipsel \ libpng-dev:mipsel \ libpulse-dev:mipsel \ @@ -125,6 +130,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:mipsel \ libslirp-dev:mipsel \ libsnappy-dev:mipsel \ + libsndio-dev:mipsel \ + libspice-protocol-dev:mipsel \ libspice-server-dev:mipsel \ libssh-gcrypt-dev:mipsel \ libsystemd-dev:mipsel \ @@ -136,15 +143,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvdeplug-dev:mipsel \ libvirglrenderer-dev:mipsel \ libvte-2.91-dev:mipsel \ + libxdp-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\ @@ -154,15 +161,19 @@ 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++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/mipsel-linux-gnu-gcc ENV ABI "mipsel-linux-gnu" ENV MESON_OPTS "--cross-file=mipsel-linux-gnu" +ENV RUST_TARGET "mipsel-unknown-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 8dd033097c..0000000000 --- a/tests/docker/dockerfiles/debian-native.docker +++ /dev/null @@ -1,49 +0,0 @@ -# -# Docker Debian Native -# -# This is 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-nios2-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh deleted file mode 100755 index ba3c9d8aff..0000000000 --- a/tests/docker/dockerfiles/debian-nios2-cross.d/build-toolchain.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash - -set -e - -TARGET=nios2-linux-gnu -LINUX_ARCH=nios2 - -J=$(expr $(nproc) / 2) -TOOLCHAIN_INSTALL=/usr/local -TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin -CROSS_SYSROOT=${TOOLCHAIN_INSTALL}/$TARGET/sys-root - -export PATH=${TOOLCHAIN_BIN}:$PATH - -# -# Grab all of the source for the toolchain bootstrap. -# - -wget https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz -wget https://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.xz -wget https://ftp.gnu.org/gnu/glibc/glibc-2.34.tar.xz -wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.70.tar.xz - -tar axf binutils-2.37.tar.xz -tar axf gcc-11.2.0.tar.xz -tar axf glibc-2.34.tar.xz -tar axf linux-5.10.70.tar.xz - -mv binutils-2.37 src-binu -mv gcc-11.2.0 src-gcc -mv glibc-2.34 src-glibc -mv linux-5.10.70 src-linux - -mkdir -p bld-hdr bld-binu bld-gcc bld-glibc -mkdir -p ${CROSS_SYSROOT}/usr/include - -# -# Install kernel and glibc headers -# - -cd src-linux -make headers_install ARCH=${LINUX_ARCH} INSTALL_HDR_PATH=${CROSS_SYSROOT}/usr -cd .. - -cd bld-hdr -../src-glibc/configure --prefix=/usr --host=${TARGET} -make install-headers DESTDIR=${CROSS_SYSROOT} -touch ${CROSS_SYSROOT}/usr/include/gnu/stubs.h -cd .. - -# -# Build binutils -# - -cd bld-binu -../src-binu/configure --disable-werror \ - --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} -make -j${J} -make install -cd .. - -# -# Build gcc, without shared libraries, because we do not yet -# have a shared libc against which to link. -# - -cd bld-gcc -../src-gcc/configure --disable-werror --disable-shared \ - --prefix=${TOOLCHAIN_INSTALL} --with-sysroot --target=${TARGET} \ - --enable-languages=c --disable-libssp --disable-libsanitizer \ - --disable-libatomic --disable-libgomp --disable-libquadmath -make -j${J} -make install -cd .. - -# -# Build glibc -# There are a few random things that use c++ but we didn't build that -# cross-compiler. We can get away without them. Disable CXX so that -# glibc doesn't try to use the host c++ compiler. -# - -cd bld-glibc -CXX=false ../src-glibc/configure --prefix=/usr --host=${TARGET} -make -j${j} -make install DESTDIR=${CROSS_SYSROOT} -cd .. 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 d6b2909cc4..0000000000 --- a/tests/docker/dockerfiles/debian-powerpc-test-cross.docker +++ /dev/null @@ -1,19 +0,0 @@ -# -# Docker powerpc/ppc64/ppc64le cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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 d2954e61f6..8ee450dba0 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -1,10 +1,10 @@ # 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,10 +78,9 @@ 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 \ + libasan6:ppc64el \ libasound2-dev:ppc64el \ libattr1-dev:ppc64el \ libbpf-dev:ppc64el \ @@ -88,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:ppc64el \ libcap-ng-dev:ppc64el \ libcapstone-dev:ppc64el \ + libcbor-dev:ppc64el \ libcmocka-dev:ppc64el \ libcurl4-gnutls-dev:ppc64el \ libdaxctl-dev:ppc64el \ @@ -102,7 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:ppc64el \ libgnutls28-dev:ppc64el \ libgtk-3-dev:ppc64el \ - libibumad-dev:ppc64el \ + libgtk-vnc-2.0-dev:ppc64el \ libibverbs-dev:ppc64el \ libiscsi-dev:ppc64el \ libjemalloc-dev:ppc64el \ @@ -114,6 +117,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:ppc64el \ libnuma-dev:ppc64el \ libpam0g-dev:ppc64el \ + libpcre2-dev:ppc64el \ + libpipewire-0.3-dev:ppc64el \ libpixman-1-dev:ppc64el \ libpng-dev:ppc64el \ libpulse-dev:ppc64el \ @@ -126,6 +131,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:ppc64el \ libslirp-dev:ppc64el \ libsnappy-dev:ppc64el \ + libsndio-dev:ppc64el \ + libspice-protocol-dev:ppc64el \ libspice-server-dev:ppc64el \ libssh-gcrypt-dev:ppc64el \ libsystemd-dev:ppc64el \ @@ -138,15 +145,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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\ @@ -156,15 +163,19 @@ 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++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/powerpc64le-linux-gnu-gcc ENV ABI "powerpc64le-linux-gnu" ENV MESON_OPTS "--cross-file=powerpc64le-linux-gnu" +ENV RUST_TARGET "powerpc64le-unknown-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 9715791e0b..4d8ca83cb3 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,51 +1,87 @@ +# 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 && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -# Install common build utilities -RUN DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \ - bison \ - bc \ - build-essential \ - ca-certificates \ - debian-ports-archive-keyring \ - dpkg-dev \ - flex \ - gettext \ - git \ - libglib2.0-dev \ - 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 \ + 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-cc && \ + 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 e5f83a5aeb..0000000000 --- a/tests/docker/dockerfiles/debian-riscv64-test-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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 d43ce16317..f451a07c4c 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -1,10 +1,10 @@ # 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 && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -24,23 +25,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ exuberant-ctags \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ gettext \ git \ hostname \ libglib2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ meson \ + mtools \ ncat \ ninja-build \ openssh-client \ - perl-base \ pkgconf \ python3 \ python3-numpy \ @@ -52,16 +50,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc \ sed \ + socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ - texinfo && \ + 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 + dpkg-reconfigure locales && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -75,10 +78,9 @@ 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 \ + libasan6:s390x \ libasound2-dev:s390x \ libattr1-dev:s390x \ libbpf-dev:s390x \ @@ -88,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:s390x \ libcap-ng-dev:s390x \ libcapstone-dev:s390x \ + libcbor-dev:s390x \ libcmocka-dev:s390x \ libcurl4-gnutls-dev:s390x \ libdaxctl-dev:s390x \ @@ -102,7 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:s390x \ libgnutls28-dev:s390x \ libgtk-3-dev:s390x \ - libibumad-dev:s390x \ + libgtk-vnc-2.0-dev:s390x \ libibverbs-dev:s390x \ libiscsi-dev:s390x \ libjemalloc-dev:s390x \ @@ -114,6 +117,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:s390x \ libnuma-dev:s390x \ libpam0g-dev:s390x \ + libpcre2-dev:s390x \ + libpipewire-0.3-dev:s390x \ libpixman-1-dev:s390x \ libpng-dev:s390x \ libpulse-dev:s390x \ @@ -126,6 +131,8 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:s390x \ libslirp-dev:s390x \ libsnappy-dev:s390x \ + libsndio-dev:s390x \ + libspice-protocol-dev:s390x \ libssh-gcrypt-dev:s390x \ libsystemd-dev:s390x \ libtasn1-6-dev:s390x \ @@ -137,15 +144,15 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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\ @@ -155,15 +162,19 @@ 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++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-g++ && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/s390x-linux-gnu-gcc ENV ABI "s390x-linux-gnu" ENV MESON_OPTS "--cross-file=s390x-linux-gnu" +ENV RUST_TARGET "s390x-unknown-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 d48ed9065f..0000000000 --- a/tests/docker/dockerfiles/debian-sh4-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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 8d3d306bc1..0000000000 --- a/tests/docker/dockerfiles/debian-sparc64-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Docker cross-compiler target -# -# This docker target builds on the Debian Bullseye base image. -# -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 \ - 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 d3d4d3344e..ab4ce29533 100644 --- a/tests/docker/dockerfiles/debian-toolchain.docker +++ b/tests/docker/dockerfiles/debian-toolchain.docker @@ -10,6 +10,8 @@ FROM docker.io/library/debian:11-slim # ??? The build-dep isn't working, missing a number of # minimal build dependiencies, e.g. libmpc. +RUN sed 's/^deb /deb-src /' /etc/apt/sources.list.d/deb-src.list + RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ @@ -21,7 +23,6 @@ RUN apt update && \ libmpc-dev \ libmpfr-dev \ rsync \ - texinfo \ wget && \ DEBIAN_FRONTEND=noninteractive eatmydata \ apt build-dep -yy --arch-only gcc glibc @@ -31,7 +32,17 @@ 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 docker.io/library/debian:bullseye-slim +FROM docker.io/library/debian:11-slim +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + libmpc3 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..479b4d6eba 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,24 @@ RUN apt update && \ locales \ make \ ninja-build \ - perl-base \ pkgconf \ python3-pip \ python3-setuptools \ - python3-wheel + python3-wheel \ + python3-venv && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -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 +ENV MAKE /usr/bin/make +# 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 aebfabdd6e..d011eee2ad 100644 --- a/tests/docker/dockerfiles/debian-xtensa-cross.docker +++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker @@ -16,7 +16,8 @@ RUN apt-get update && \ curl \ gettext \ git \ - python3-minimal + python3-minimal && \ + dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt ENV CPU_LIST dc232b dc233c de233_fpu dsp3400 ENV TOOLCHAIN_RELEASE 2020.07 @@ -27,3 +28,9 @@ 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 +ENV MAKE /usr/bin/make +# 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..505330a9e2 --- /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 \ + bindgen \ + bison \ + bsdextrautils \ + bzip2 \ + ca-certificates \ + ccache \ + clang \ + dbus \ + debianutils \ + diffutils \ + exuberant-ctags \ + findutils \ + flex \ + 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 \ + libcbor-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 \ + libgtk-vnc-2.0-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 \ + rustc \ + sed \ + socat \ + sparse \ + swtpm \ + systemtap-sdt-dev \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + 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 && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + 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/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ + 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/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker deleted file mode 100644 index 91c373fdd3..0000000000 --- a/tests/docker/dockerfiles/fedora-cris-cross.docker +++ /dev/null @@ -1,8 +0,0 @@ -# -# Cross compiler for cris system tests -# - -FROM registry.fedoraproject.org/fedora:33 -ENV PACKAGES gcc-cris-linux-gnu -RUN dnf install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker deleted file mode 100644 index 7eec648d2d..0000000000 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ /dev/null @@ -1,33 +0,0 @@ -FROM registry.fedoraproject.org/fedora:34 - -ENV PACKAGES \ - bison \ - bzip2 \ - ccache \ - diffutils \ - flex \ - 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-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker new file mode 100644 index 0000000000..9180c8b522 --- /dev/null +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -0,0 +1,176 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all fedora-40 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +FROM registry.fedoraproject.org/fedora:40 + +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 \ + SDL2-devel \ + SDL2_image-devel \ + alsa-lib-devel \ + bash \ + bc \ + bindgen-cli \ + 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 \ + gcovr \ + gettext \ + git \ + glib2-devel \ + glib2-static \ + glibc-langpack-en \ + glibc-static \ + glusterfs-api-devel \ + gnutls-devel \ + gtk-vnc2-devel \ + gtk3-devel \ + hostname \ + jemalloc-devel \ + json-c-devel \ + libaio-devel \ + libasan \ + libattr-devel \ + libbpf-devel \ + libcacard-devel \ + libcap-ng-devel \ + libcbor-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 \ + python3-zombie-imp \ + rdma-core-devel \ + rust \ + 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 \ + xorriso \ + zlib-devel \ + zlib-static \ + zstd && \ + nosync dnf autoremove -y && \ + nosync dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + rpm -qa | sort > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + 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/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" +RUN dnf install -y wget +ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo +ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc +RUN set -eux && \ + rustArch='x86_64-unknown-linux-gnu' && \ + rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \ + url="https://static.rust-lang.org/rustup/archive/1.27.1/${rustArch}/rustup-init" && \ + wget "$url" && \ + echo "${rustupSha256} *rustup-init" | sha256sum -c - && \ + chmod +x rustup-init && \ + ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \ + /usr/local/cargo/bin/rustup --version && \ + /usr/local/cargo/bin/rustup run nightly rustc --version && \ + test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)" +ENV PATH=$CARGO_HOME/bin:$PATH +RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli +# 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-win32-cross.docker b/tests/docker/dockerfiles/fedora-win32-cross.docker deleted file mode 100644 index 75383ba185..0000000000 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ /dev/null @@ -1,103 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all --cross mingw32 fedora-35 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM registry.fedoraproject.org/fedora:35 - -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 \ - bash \ - bc \ - bison \ - bzip2 \ - ca-certificates \ - ccache \ - ctags \ - dbus-daemon \ - diffutils \ - findutils \ - flex \ - gcovr \ - genisoimage \ - git \ - glib2-devel \ - glibc-langpack-en \ - hostname \ - llvm \ - make \ - meson \ - ninja-build \ - nmap-ncat \ - openssh-clients \ - pcre-static \ - perl-base \ - python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-sphinx \ - python3-sphinx_rtd_theme \ - rpm \ - sed \ - sparse \ - spice-protocol \ - tar \ - tesseract \ - tesseract-langpack-eng \ - texinfo \ - util-linux \ - which && \ - nosync dnf autoremove -y && \ - nosync dnf clean all -y - -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-SDL2 \ - mingw32-SDL2_image \ - mingw32-bzip2 \ - mingw32-curl \ - mingw32-gcc \ - mingw32-gcc-c++ \ - mingw32-gettext \ - mingw32-glib2 \ - mingw32-gnutls \ - mingw32-gtk3 \ - mingw32-libgcrypt \ - mingw32-libjpeg-turbo \ - mingw32-libpng \ - mingw32-libtasn1 \ - mingw32-nettle \ - mingw32-nsis \ - mingw32-pixman \ - mingw32-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/i686-w64-mingw32-c++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-w64-mingw32-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-w64-mingw32-g++ && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/i686-w64-mingw32-gcc - -ENV ABI "i686-w64-mingw32" -ENV MESON_OPTS "--cross-file=/usr/share/mingw/toolchain-mingw32.meson" -ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-w64-mingw32- -ENV DEF_TARGET_LIST i386-softmmu diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 98c03dc13b..7dc3eb03f5 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -1,25 +1,26 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross mingw64 fedora-35 qemu +# $ lcitool dockerfile --layers all --cross-arch mingw64 fedora-40 qemu,qemu-win-installer # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:35 +FROM registry.fedoraproject.org/fedora:40 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 \ bash \ bc \ + bindgen-cli \ bison \ bzip2 \ ca-certificates \ @@ -29,8 +30,8 @@ exec "$@"' > /usr/bin/nosync && \ diffutils \ findutils \ flex \ + gcc \ gcovr \ - genisoimage \ git \ glib2-devel \ glibc-langpack-en \ @@ -38,11 +39,10 @@ exec "$@"' > /usr/bin/nosync && \ llvm \ make \ meson \ + mtools \ ninja-build \ nmap-ncat \ openssh-clients \ - pcre-static \ - perl-base \ python3 \ python3-PyYAML \ python3-numpy \ @@ -51,18 +51,22 @@ exec "$@"' > /usr/bin/nosync && \ python3-pip \ python3-sphinx \ python3-sphinx_rtd_theme \ - rpm \ + python3-zombie-imp \ + rust \ sed \ + socat \ sparse \ - spice-protocol \ + swtpm \ tar \ tesseract \ tesseract-langpack-eng \ - texinfo \ util-linux \ - which && \ + which \ + xorriso \ + zstd && \ nosync dnf autoremove -y && \ - nosync dnf clean all -y + nosync dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" @@ -71,6 +75,7 @@ ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" RUN nosync dnf install -y \ + mingw-w64-tools \ mingw32-nsis \ mingw64-SDL2 \ mingw64-SDL2_image \ @@ -81,7 +86,9 @@ RUN nosync dnf install -y \ mingw64-gettext \ mingw64-glib2 \ mingw64-gnutls \ + mingw64-gtk-vnc2 \ mingw64-gtk3 \ + mingw64-libepoxy \ mingw64-libgcrypt \ mingw64-libjpeg-turbo \ mingw64-libpng \ @@ -101,3 +108,8 @@ 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 d200c7fc10..b64399af66 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,20 +1,20 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all fedora-35 qemu +# $ lcitool dockerfile --layers all fedora-40 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.fedoraproject.org/fedora:35 +FROM registry.fedoraproject.org/fedora:40 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 \ @@ -23,6 +23,7 @@ exec "$@"' > /usr/bin/nosync && \ alsa-lib-devel \ bash \ bc \ + bindgen-cli \ bison \ brlapi-devel \ bzip2 \ @@ -41,9 +42,7 @@ exec "$@"' > /usr/bin/nosync && \ flex \ fuse3-devel \ gcc \ - gcc-c++ \ gcovr \ - genisoimage \ gettext \ git \ glib2-devel \ @@ -52,6 +51,7 @@ exec "$@"' > /usr/bin/nosync && \ glibc-static \ glusterfs-api-devel \ gnutls-devel \ + gtk-vnc2-devel \ gtk3-devel \ hostname \ jemalloc-devel \ @@ -62,6 +62,7 @@ exec "$@"' > /usr/bin/nosync && \ libbpf-devel \ libcacard-devel \ libcap-ng-devel \ + libcbor-devel \ libcmocka-devel \ libcurl-devel \ libdrm-devel \ @@ -83,6 +84,7 @@ exec "$@"' > /usr/bin/nosync && \ libubsan \ liburing-devel \ libusbx-devel \ + libxdp-devel \ libzstd-devel \ llvm \ lttng-ust-devel \ @@ -90,6 +92,7 @@ exec "$@"' > /usr/bin/nosync && \ make \ mesa-libgbm-devel \ meson \ + mtools \ ncurses-devel \ nettle-devel \ ninja-build \ @@ -98,7 +101,7 @@ exec "$@"' > /usr/bin/nosync && \ openssh-clients \ pam-devel \ pcre-static \ - perl-base \ + pipewire-devel \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ @@ -110,36 +113,38 @@ exec "$@"' > /usr/bin/nosync && \ python3-pip \ python3-sphinx \ python3-sphinx_rtd_theme \ + python3-zombie-imp \ rdma-core-devel \ - rpm \ + rust \ sed \ snappy-devel \ + socat \ sparse \ spice-protocol \ spice-server-devel \ + swtpm \ systemd-devel \ systemtap-sdt-devel \ tar \ tesseract \ tesseract-langpack-eng \ - texinfo \ usbredir-devel \ util-linux \ virglrenderer-devel \ vte291-devel \ which \ xen-devel \ - xfsprogs-devel \ + xorriso \ zlib-devel \ - zlib-static && \ + zlib-static \ + zstd && \ nosync dnf autoremove -y && \ nosync dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ 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" @@ -147,3 +152,8 @@ 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/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 4361b01464..4d5fb3e3a1 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -1,12 +1,13 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all opensuse-leap-153 qemu +# $ lcitool dockerfile --layers all opensuse-leap-15 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.opensuse.org/opensuse/leap:15.3 +FROM registry.opensuse.org/opensuse/leap:15.6 RUN zypper update -y && \ + zypper addrepo -fc https://download.opensuse.org/update/leap/15.6/backports/openSUSE:Backports:SLE-15-SP6:Update.repo && \ zypper install -y \ Mesa-devel \ alsa-lib-devel \ @@ -26,7 +27,6 @@ RUN zypper update -y && \ flex \ fuse3-devel \ gcc \ - gcc-c++ \ gcovr \ gettext-runtime \ git \ @@ -34,18 +34,20 @@ RUN zypper update -y && \ glibc-locale \ glibc-static \ glusterfs-devel \ + gtk-vnc-devel \ gtk3-devel \ hostname \ jemalloc-devel \ libSDL2-devel \ libSDL2_image-devel \ libaio-devel \ - libasan6 \ + libasan8 \ libattr-devel \ libbpf-devel \ libbz2-devel \ libcacard-devel \ libcap-ng-devel \ + libcbor-devel \ libcmocka-devel \ libcurl-devel \ libdrm-devel \ @@ -81,60 +83,66 @@ 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-wheel \ + python311 \ + python311-base \ + python311-pip \ + python311-setuptools \ rdma-core-devel \ - rpm \ + rust \ + rust-bindgen \ 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 \ vte-devel \ which \ xen-devel \ - xfsprogs-devel \ + xorriso \ zlib-devel \ - zlib-devel-static && \ + zlib-devel-static \ + zstd && \ zypper clean --all && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ 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 -RUN /usr/bin/pip3 install meson==0.56.0 +RUN /usr/bin/pip3.11 install \ + PyYAML \ + meson==1.5.0 \ + 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 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..8f0af9ef25 100644 --- a/tests/docker/dockerfiles/python.docker +++ b/tests/docker/dockerfiles/python.docker @@ -7,12 +7,21 @@ MAINTAINER John Snow ENV PACKAGES \ gcc \ make \ - pipenv \ python3 \ python3-pip \ python3-tox \ python3-virtualenv \ - python3.10 + python3.10 \ + python3.11 \ + python3.12 \ + python3.13 \ + 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 deleted file mode 100644 index 9417bca2fa..0000000000 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ /dev/null @@ -1,152 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all ubuntu-2004 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM docker.io/library/ubuntu:20.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 \ - bsdmainutils \ - bzip2 \ - ca-certificates \ - ccache \ - clang \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - flex \ - 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 \ - 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 \ - 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 && \ - 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 - -RUN /usr/bin/pip3 install meson==0.56.0 - -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" -# 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 diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker new file mode 100644 index 0000000000..e1b70b536d --- /dev/null +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -0,0 +1,161 @@ +# 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 \ + 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 \ + libcbor-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 \ + libgtk-vnc-2.0-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 \ + rustc \ + sed \ + socat \ + sparse \ + swtpm \ + systemtap-sdt-dev \ + tar \ + tesseract-ocr \ + tesseract-ocr-eng \ + 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 && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + 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/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ + 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 CARGO_HOME=/usr/local/cargo +ENV PATH=$CARGO_HOME/bin:$PATH +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends cargo +RUN cargo install bindgen-cli +# 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/test-debug b/tests/docker/test-debug index f52f16328c..678ceccc27 100755 --- a/tests/docker/test-debug +++ b/tests/docker/test-debug @@ -1,6 +1,6 @@ #!/bin/bash -e # -# Compile and check with clang & --enable-debug --enable-sanitizers. +# Compile and check with clang & debug & sanitizers # # Copyright (c) 2016-2018 Red Hat Inc. # @@ -19,7 +19,7 @@ requires_binary clang cd "$BUILD_DIR" OPTS="--cxx=clang++ --cc=clang --host-cc=clang" -OPTS="--enable-debug --enable-sanitizers $OPTS" +OPTS="--enable-debug --enable-asan --enable-ubsan $OPTS" export ASAN_OPTIONS=detect_leaks=0 build_qemu $OPTS 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-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-bench.c b/tests/fp/fp-bench.c index 8ce0ca1545..75c07d5d1f 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -488,6 +488,8 @@ static void run_bench(void) { bench_func_t f; + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); + f = bench_funcs[operation][precision]; g_assert(f); f(); diff --git a/tests/fp/fp-test-log2.c b/tests/fp/fp-test-log2.c index 4eae93eb7c..de702c4c80 100644 --- a/tests/fp/fp-test-log2.c +++ b/tests/fp/fp-test-log2.c @@ -70,6 +70,7 @@ int main(int ac, char **av) float_status qsf = {0}; int i; + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); set_float_rounding_mode(float_round_nearest_even, &qsf); test.d = 0.0; diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 35829ad5f7..5f6f25c882 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); @@ -926,6 +935,8 @@ void run_test(void) { unsigned int i; + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + genCases_setLevel(test_level); verCases_maxErrorCount = n_max_errors; diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 6258e2bd7d..9059a24752 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -1,19 +1,31 @@ -if 'CONFIG_TCG' not in config_all +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 = [ +# By default tests run with the usual 30s timeout; particularly +# slow tests can have that overridden here. The keys here are +# the testnames without their fp-test- prefix. +slow_fp_tests = { + 'rem': 60, + 'div': 60, + 'mul': 60, + 'mulAdd': 180, +} + +sfcflags = [ # softfloat defines '-DSOFTFLOAT_ROUND_ODD', '-DINLINE_LEVEL=5', '-DSOFTFLOAT_FAST_DIV32TO16', '-DSOFTFLOAT_FAST_DIV64TO32', '-DSOFTFLOAT_FAST_INT64', +] +tfcflags = [ # testfloat defines '-DFLOAT16', '-DFLOAT64', @@ -23,522 +35,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 @@ -550,10 +56,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 = { @@ -608,13 +112,14 @@ 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: foreach k, v : softfloat_conv_tests test('fp-test-' + k, fptest, args: fptest_args + fptest_rounding_args + v.split(), + timeout: slow_fp_tests.get(k, 30), suite: ['softfloat', 'softfloat-conv']) endforeach @@ -622,6 +127,7 @@ foreach k, v : softfloat_tests test('fp-test-' + k, fptest, args: fptest_args + fptest_rounding_args + ['f16_' + k, 'f32_' + k, 'f64_' + k, 'f128_' + k, 'extF80_' + k], + timeout: slow_fp_tests.get(k, 30), suite: ['softfloat', 'softfloat-' + v]) endforeach @@ -630,24 +136,22 @@ 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) + timeout: slow_fp_tests.get('mulAdd', 30), + suite: ['softfloat-slow', 'softfloat-ops-slow', 'slow']) 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, + timeout: slow_fp_tests.get('log2', 30), suite: ['softfloat', 'softfloat-ops']) diff --git a/tests/avocado/acpi-bits/bits-config/bits-cfg.txt b/tests/functional/acpi-bits/bits-config/bits-cfg.txt similarity index 100% rename from tests/avocado/acpi-bits/bits-config/bits-cfg.txt rename to tests/functional/acpi-bits/bits-config/bits-cfg.txt diff --git a/tests/avocado/acpi-bits/bits-tests/smbios.py2 b/tests/functional/acpi-bits/bits-tests/smbios.py2 similarity index 99% rename from tests/avocado/acpi-bits/bits-tests/smbios.py2 rename to tests/functional/acpi-bits/bits-tests/smbios.py2 index 9667d0542c..5868a7137a 100644 --- a/tests/avocado/acpi-bits/bits-tests/smbios.py2 +++ b/tests/functional/acpi-bits/bits-tests/smbios.py2 @@ -1,6 +1,8 @@ # 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: # @@ -24,6 +26,8 @@ # (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 @@ -1056,7 +1060,7 @@ class EventLogDescriptor(unpack.Struct): 0x16: 'Log Area Reset/Cleared', 0x17: 'System boot', xrange(0x18, 0x7F): 'Unused, available for assignment', - xrange(0x80, 0xFE): 'Availalbe for system- and OEM-specific assignments', + 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) diff --git a/tests/functional/acpi-bits/bits-tests/smilatency.py2 b/tests/functional/acpi-bits/bits-tests/smilatency.py2 new file mode 100644 index 0000000000..405af67e19 --- /dev/null +++ b/tests/functional/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/functional/acpi-bits/bits-tests/testacpi.py2 similarity index 98% rename from tests/avocado/acpi-bits/bits-tests/testacpi.py2 rename to tests/functional/acpi-bits/bits-tests/testacpi.py2 index dbc150076e..7bf9075c1b 100644 --- a/tests/avocado/acpi-bits/bits-tests/testacpi.py2 +++ b/tests/functional/acpi-bits/bits-tests/testacpi.py2 @@ -1,6 +1,8 @@ # 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: # @@ -24,6 +26,8 @@ # (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 @@ -269,8 +273,8 @@ def test_rsdp(): # 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 cummulative checksum must equal 0', csum == 0) - testsuite.print_detail("Cummulative checksum = {} (Expected 0)".format(csum)) + 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() diff --git a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 b/tests/functional/acpi-bits/bits-tests/testcpuid.py2 similarity index 97% rename from tests/avocado/acpi-bits/bits-tests/testcpuid.py2 rename to tests/functional/acpi-bits/bits-tests/testcpuid.py2 index ac55d912e1..7adefbe355 100644 --- a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 +++ b/tests/functional/acpi-bits/bits-tests/testcpuid.py2 @@ -1,6 +1,8 @@ # 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: # @@ -24,6 +26,8 @@ # (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 diff --git a/tests/functional/meson.build b/tests/functional/meson.build new file mode 100644 index 0000000000..d6d2c0196c --- /dev/null +++ b/tests/functional/meson.build @@ -0,0 +1,302 @@ +# QEMU functional tests: +# Tests that are put in the 'quick' category are run by default during +# 'make check'. Everything that should not be run during 'make check' +# (e.g. tests that fetch assets from the internet) should be put into +# the 'thorough' category instead. + +# Most tests run too slow with TCI enabled, so skip the functional tests there +if get_option('tcg_interpreter') + subdir_done() +endif + +# Timeouts for individual tests that can be slow e.g. with debugging enabled +test_timeouts = { + 'aarch64_aspeed' : 600, + 'aarch64_raspi4' : 480, + 'aarch64_sbsaref_alpine' : 720, + 'aarch64_sbsaref_freebsd' : 720, + 'aarch64_tuxrun' : 240, + 'aarch64_virt' : 720, + 'acpi_bits' : 420, + 'arm_aspeed' : 600, + 'arm_bpim2u' : 500, + 'arm_collie' : 180, + 'arm_orangepi' : 540, + 'arm_raspi2' : 120, + 'arm_tuxrun' : 240, + 'arm_sx1' : 360, + 'mips_malta' : 120, + 'netdev_ethtool' : 180, + 'ppc_40p' : 240, + 'ppc64_hv' : 1000, + 'ppc64_powernv' : 480, + 'ppc64_pseries' : 480, + 'ppc64_tuxrun' : 420, + 'riscv64_tuxrun' : 120, + 's390x_ccw_virtio' : 420, +} + +tests_generic_system = [ + 'empty_cpu_model', + 'info_usernet', + 'version', +] + +tests_generic_linuxuser = [ +] + +tests_generic_bsduser = [ +] + +tests_aarch64_system_thorough = [ + 'aarch64_aspeed', + 'aarch64_raspi3', + 'aarch64_raspi4', + 'aarch64_sbsaref', + 'aarch64_sbsaref_alpine', + 'aarch64_sbsaref_freebsd', + 'aarch64_tuxrun', + 'aarch64_virt', + 'multiprocess', +] + +tests_alpha_system_thorough = [ + 'alpha_clipper', +] + +tests_arm_system_thorough = [ + 'arm_aspeed', + 'arm_bpim2u', + 'arm_canona1100', + 'arm_collie', + 'arm_integratorcp', + 'arm_orangepi', + 'arm_raspi2', + 'arm_sx1', + 'arm_vexpress', + 'arm_tuxrun', +] + +tests_arm_linuxuser_thorough = [ + 'arm_bflt', +] + +tests_avr_system_thorough = [ + 'avr_mega2560', +] + +tests_i386_system_thorough = [ + 'i386_tuxrun', +] + +tests_loongarch64_system_thorough = [ + 'loongarch64_virt', +] + +tests_m68k_system_thorough = [ + 'm68k_mcf5208evb', + 'm68k_nextcube', + 'm68k_q800', +] + +tests_microblaze_system_thorough = [ + 'microblaze_s3adsp1800' +] + +tests_microblazeel_system_thorough = [ + 'microblazeel_s3adsp1800' +] + +tests_mips_system_thorough = [ + 'mips_malta', + 'mips_tuxrun', +] + +tests_mipsel_system_thorough = [ + 'mipsel_malta', + 'mipsel_tuxrun', +] + +tests_mips64_system_thorough = [ + 'mips64_tuxrun', +] + +tests_mips64el_system_thorough = [ + 'mips64el_fuloong2e', + 'mips64el_loongson3v', + 'mips64el_malta', + 'mips64el_tuxrun', +] + +tests_or1k_system_thorough = [ + 'or1k_sim', +] + +tests_ppc_system_quick = [ + 'ppc_74xx', +] + +tests_ppc_system_thorough = [ + 'ppc_405', + 'ppc_40p', + 'ppc_amiga', + 'ppc_bamboo', + 'ppc_mac', + 'ppc_mpc8544ds', + 'ppc_tuxrun', + 'ppc_virtex_ml507', +] + +tests_ppc64_system_thorough = [ + 'ppc64_e500', + 'ppc64_hv', + 'ppc64_powernv', + 'ppc64_pseries', + 'ppc64_tuxrun', +] + +tests_riscv32_system_quick = [ + 'riscv_opensbi', +] + +tests_riscv32_system_thorough = [ + 'riscv32_tuxrun', +] + +tests_riscv64_system_quick = [ + 'riscv_opensbi', +] + +tests_riscv64_system_thorough = [ + 'riscv64_tuxrun', +] + +tests_rx_system_thorough = [ + 'rx_gdbsim', +] + +tests_s390x_system_thorough = [ + 's390x_ccw_virtio', + 's390x_topology', + 's390x_tuxrun', +] + +tests_sh4_system_thorough = [ + 'sh4_r2d', + 'sh4_tuxrun', +] + +tests_sh4eb_system_thorough = [ + 'sh4eb_r2d', +] + +tests_sparc_system_thorough = [ + 'sparc_sun4m', +] + +tests_sparc64_system_thorough = [ + 'sparc64_sun4u', + 'sparc64_tuxrun', +] + +tests_x86_64_system_quick = [ + 'cpu_queries', + 'mem_addr_space', + 'pc_cpu_hotplug_props', + 'virtio_version', + 'x86_cpu_model_versions', +] + +tests_x86_64_system_thorough = [ + 'acpi_bits', + 'x86_64_tuxrun', + 'linux_initrd', + 'multiprocess', + 'netdev_ethtool', + 'virtio_gpu', +] + +tests_xtensa_system_thorough = [ + 'xtensa_lx60', +] + +precache_all = [] +foreach speed : ['quick', 'thorough'] + foreach dir : target_dirs + + target_base = dir.split('-')[0] + + if dir.endswith('-softmmu') + sysmode = 'system' + test_emulator = emulators['qemu-system-' + target_base] + elif dir.endswith('-linux-user') + sysmode = 'linuxuser' + test_emulator = emulators['qemu-' + target_base] + elif dir.endswith('-bsd-user') + sysmode = 'bsduser' + test_emulator = emulators['qemu-' + target_base] + else + continue + endif + + if speed == 'quick' + suites = ['func-quick', 'func-' + target_base] + target_tests = get_variable('tests_' + target_base + '_' + sysmode + '_quick', []) \ + + get_variable('tests_generic_' + sysmode) + else + suites = ['func-' + speed, 'func-' + target_base + '-' + speed, speed] + target_tests = get_variable('tests_' + target_base + '_' + sysmode + '_' + speed, []) + endif + + test_deps = roms + test_env = environment() + if have_tools + test_env.set('QEMU_TEST_QEMU_IMG', meson.global_build_root() / 'qemu-img') + test_deps += [qemu_img] + endif + test_env.set('QEMU_TEST_QEMU_BINARY', test_emulator.full_path()) + test_env.set('QEMU_BUILD_ROOT', meson.project_build_root()) + test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' + + meson.current_source_dir()) + + foreach test : target_tests + testname = '@0@-@1@'.format(target_base, test) + testfile = 'test_' + test + '.py' + testpath = meson.current_source_dir() / testfile + teststamp = testname + '.tstamp' + test_precache_env = environment() + test_precache_env.set('QEMU_TEST_PRECACHE', meson.current_build_dir() / teststamp) + test_precache_env.set('PYTHONPATH', meson.project_source_root() / 'python:' + + meson.current_source_dir()) + precache = custom_target('func-precache-' + testname, + output: teststamp, + command: [python, testpath], + depend_files: files(testpath), + build_by_default: false, + env: test_precache_env) + precache_all += precache + + # Ideally we would add 'precache' to 'depends' here, such that + # 'build_by_default: false' lets the pre-caching automatically + # run immediately before the test runs. In practice this is + # broken in meson, with it running the pre-caching in the normal + # compile phase https://github.com/mesonbuild/meson/issues/2518 + # If the above bug ever gets fixed, when QEMU changes the min + # meson version, add the 'depends' and remove the custom + # 'run_target' logic below & in Makefile.include + test('func-' + testname, + python, + depends: [test_deps, test_emulator, emulator_modules], + env: test_env, + args: [testpath], + protocol: 'tap', + timeout: test_timeouts.get(test, 90), + priority: test_timeouts.get(test, 90), + suite: suites) + endforeach + endforeach +endforeach + +run_target('precache-functional', + depends: precache_all, + command: ['true']) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py new file mode 100644 index 0000000000..67f87be9c4 --- /dev/null +++ b/tests/functional/qemu_test/__init__.py @@ -0,0 +1,15 @@ +# Test class and utilities for functional tests +# +# Copyright 2024 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 .asset import Asset +from .config import BUILD_DIR +from .cmd import has_cmd, has_cmds, run_cmd, is_readable_executable_file, \ + interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ + exec_command, exec_command_and_wait_for_pattern, get_qemu_img +from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest +from .linuxkernel import LinuxKernelTest diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py new file mode 100644 index 0000000000..f126cd5863 --- /dev/null +++ b/tests/functional/qemu_test/asset.py @@ -0,0 +1,180 @@ +# Test utilities for fetching & caching assets +# +# Copyright 2024 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. + +import hashlib +import logging +import os +import stat +import subprocess +import sys +import unittest +import urllib.request +from time import sleep +from pathlib import Path +from shutil import copyfileobj + + +# Instances of this class must be declared as class level variables +# starting with a name "ASSET_". This enables the pre-caching logic +# to easily find all referenced assets and download them prior to +# execution of the tests. +class Asset: + + def __init__(self, url, hashsum): + self.url = url + self.hash = hashsum + cache_dir_env = os.getenv('QEMU_TEST_CACHE_DIR') + if cache_dir_env: + self.cache_dir = Path(cache_dir_env, "download") + else: + self.cache_dir = Path(Path("~").expanduser(), + ".cache", "qemu", "download") + self.cache_file = Path(self.cache_dir, hashsum) + self.log = logging.getLogger('qemu-test') + + def __repr__(self): + return "Asset: url=%s hash=%s cache=%s" % ( + self.url, self.hash, self.cache_file) + + def _check(self, cache_file): + if self.hash is None: + return True + if len(self.hash) == 64: + hl = hashlib.sha256() + elif len(self.hash) == 128: + hl = hashlib.sha512() + else: + raise Exception("unknown hash type") + + # Calculate the hash of the file: + with open(cache_file, 'rb') as file: + while True: + chunk = file.read(1 << 20) + if not chunk: + break + hl.update(chunk) + + return self.hash == hl.hexdigest() + + def valid(self): + return self.cache_file.exists() and self._check(self.cache_file) + + def _wait_for_other_download(self, tmp_cache_file): + # Another thread already seems to download the asset, so wait until + # it is done, while also checking the size to see whether it is stuck + try: + current_size = tmp_cache_file.stat().st_size + new_size = current_size + except: + if os.path.exists(self.cache_file): + return True + raise + waittime = lastchange = 600 + while waittime > 0: + sleep(1) + waittime -= 1 + try: + new_size = tmp_cache_file.stat().st_size + except: + if os.path.exists(self.cache_file): + return True + raise + if new_size != current_size: + lastchange = waittime + current_size = new_size + elif lastchange - waittime > 90: + return False + + self.log.debug("Time out while waiting for %s!", tmp_cache_file) + raise + + def fetch(self): + if not self.cache_dir.exists(): + self.cache_dir.mkdir(parents=True, exist_ok=True) + + if self.valid(): + self.log.debug("Using cached asset %s for %s", + self.cache_file, self.url) + return str(self.cache_file) + + if os.environ.get("QEMU_TEST_NO_DOWNLOAD", False): + raise Exception("Asset cache is invalid and downloads disabled") + + self.log.info("Downloading %s to %s...", self.url, self.cache_file) + tmp_cache_file = self.cache_file.with_suffix(".download") + + for retries in range(3): + try: + with tmp_cache_file.open("xb") as dst: + with urllib.request.urlopen(self.url) as resp: + copyfileobj(resp, dst) + break + except FileExistsError: + self.log.debug("%s already exists, " + "waiting for other thread to finish...", + tmp_cache_file) + if self._wait_for_other_download(tmp_cache_file): + return str(self.cache_file) + self.log.debug("%s seems to be stale, " + "deleting and retrying download...", + tmp_cache_file) + tmp_cache_file.unlink() + continue + except Exception as e: + self.log.error("Unable to download %s: %s", self.url, e) + tmp_cache_file.unlink() + raise + + try: + # Set these just for informational purposes + os.setxattr(str(tmp_cache_file), "user.qemu-asset-url", + self.url.encode('utf8')) + os.setxattr(str(tmp_cache_file), "user.qemu-asset-hash", + self.hash.encode('utf8')) + except Exception as e: + self.log.debug("Unable to set xattr on %s: %s", tmp_cache_file, e) + pass + + if not self._check(tmp_cache_file): + tmp_cache_file.unlink() + raise Exception("Hash of %s does not match %s" % + (self.url, self.hash)) + tmp_cache_file.replace(self.cache_file) + # Remove write perms to stop tests accidentally modifying them + os.chmod(self.cache_file, stat.S_IRUSR | stat.S_IRGRP) + + self.log.info("Cached %s at %s" % (self.url, self.cache_file)) + return str(self.cache_file) + + def precache_test(test): + log = logging.getLogger('qemu-test') + log.setLevel(logging.DEBUG) + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(logging.DEBUG) + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + log.addHandler(handler) + for name, asset in vars(test.__class__).items(): + if name.startswith("ASSET_") and type(asset) == Asset: + log.info("Attempting to cache '%s'" % asset) + asset.fetch() + log.removeHandler(handler) + + def precache_suite(suite): + for test in suite: + if isinstance(test, unittest.TestSuite): + Asset.precache_suite(test) + elif isinstance(test, unittest.TestCase): + Asset.precache_test(test) + + def precache_suites(path, cacheTstamp): + loader = unittest.loader.defaultTestLoader + tests = loader.loadTestsFromNames([path], None) + + with open(cacheTstamp, "w") as fh: + Asset.precache_suite(tests) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py new file mode 100644 index 0000000000..11c8334a7c --- /dev/null +++ b/tests/functional/qemu_test/cmd.py @@ -0,0 +1,250 @@ +# Test class and utilities for functional tests +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# Original Author (Avocado-based tests): +# Cleber Rosa +# +# Adaption for standalone version: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import logging +import os +import os.path +import subprocess + +from .config import BUILD_DIR + + +def has_cmd(name, args=None): + """ + This function is for use in a @skipUnless decorator, e.g.: + + @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) + def test_something_that_needs_sudo(self): + ... + """ + + if args is None: + args = ('which', name) + + try: + _, stderr, exitcode = run_cmd(args) + except Exception as e: + exitcode = -1 + stderr = str(e) + + if exitcode != 0: + cmd_line = ' '.join(args) + err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' + return (False, err) + else: + return (True, '') + +def has_cmds(*cmds): + """ + This function is for use in a @skipUnless decorator and + allows checking for the availability of multiple commands, e.g.: + + @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), + 'cmd2', 'cmd3')) + def test_something_that_needs_cmd1_and_cmd2(self): + ... + """ + + for cmd in cmds: + if isinstance(cmd, str): + cmd = (cmd,) + + ok, errstr = has_cmd(*cmd) + if not ok: + return (False, errstr) + + return (True, '') + +def run_cmd(args): + subp = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = subp.communicate() + ret = subp.returncode + + return (stdout, stderr, ret) + +def is_readable_executable_file(path): + return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) + +# @test: functional test to fail if @failure is seen +# @vm: the VM whose console to process +# @success: a non-None string to look for +# @failure: a string to look for that triggers test failure, or None +# +# Read up to 1 line of text from @vm, looking for @success +# and optionally @failure. +# +# If @success or @failure are seen, immediately return True, +# even if end of line is not yet seen. ie remainder of the +# line is left unread. +# +# If end of line is seen, with neither @success or @failure +# return False +# +# If @failure is seen, then mark @test as failed +def _console_read_line_until_match(test, vm, success, failure): + msg = bytes([]) + done = False + while True: + c = vm.console_socket.recv(1) + if c is None: + done = True + test.fail( + f"EOF in console, expected '{success}'") + break + msg += c + + if success in msg: + done = True + break + if failure and failure in msg: + done = True + vm.console_socket.close() + test.fail( + f"'{failure}' found in console, expected '{success}'") + + if c == b'\n': + break + + console_logger = logging.getLogger('console') + try: + console_logger.debug(msg.decode().strip()) + except: + console_logger.debug(msg) + + return done + +def _console_interaction(test, success_message, failure_message, + send_string, keep_sending=False, vm=None): + assert not keep_sending or send_string + assert success_message or send_string + + if vm is None: + vm = test.vm + + test.log.debug( + f"Console interaction: success_msg='{success_message}' " + + f"failure_msg='{failure_message}' send_string='{send_string}'") + + # We'll process console in bytes, to avoid having to + # deal with unicode decode errors from receiving + # partial utf8 byte sequences + success_message_b = None + if success_message is not None: + success_message_b = success_message.encode() + + failure_message_b = None + if failure_message is not None: + failure_message_b = failure_message.encode() + + while True: + if send_string: + vm.console_socket.sendall(send_string.encode()) + if not keep_sending: + send_string = None # send only once + + # Only consume console output if waiting for something + if success_message is None: + if send_string is None: + break + continue + + if _console_read_line_until_match(test, vm, + success_message_b, + failure_message_b): + break + +def interrupt_interactive_console_until_pattern(test, success_message, + failure_message=None, + interrupt_string='\r'): + """ + Keep sending a string to interrupt a console prompt, while logging the + console output. Typical use case is to break a boot loader prompt, such: + + Press a key within 5 seconds to interrupt boot process. + 5 + 4 + 3 + 2 + 1 + Booting default image... + + :param test: a test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`qemu_test.QemuSystemTest` + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + :param interrupt_string: a string to send to the console before trying + to read a new line + """ + assert success_message + _console_interaction(test, success_message, failure_message, + interrupt_string, True) + +def wait_for_console_pattern(test, success_message, failure_message=None, + vm=None): + """ + Waits for messages to appear on the console, while logging the content + + :param test: a test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`qemu_test.QemuSystemTest` + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + """ + assert success_message + _console_interaction(test, success_message, failure_message, None, vm=vm) + +def exec_command(test, command): + """ + Send a command to a console (appending CRLF characters), while logging + the content. + + :param test: a test containing a VM. + :type test: :class:`qemu_test.QemuSystemTest` + :param command: the command to send + :type command: str + """ + _console_interaction(test, None, None, command + '\r') + +def exec_command_and_wait_for_pattern(test, command, + success_message, failure_message=None): + """ + Send a command to a console (appending CRLF characters), then wait + for success_message to appear on the console, while logging the. + content. Mark the test as failed if failure_message is found instead. + + :param test: a test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`qemu_test.QemuSystemTest` + :param command: the command to send + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + """ + assert success_message + _console_interaction(test, success_message, failure_message, command + '\r') + +def get_qemu_img(test): + test.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 os.path.exists(qemu_img): + return qemu_img + (has_system_qemu_img, errmsg) = has_cmd('qemu-img') + if has_system_qemu_img: + return 'qemu-img' + test.skipTest(errmsg) diff --git a/tests/functional/qemu_test/config.py b/tests/functional/qemu_test/config.py new file mode 100644 index 0000000000..edd75b7fd0 --- /dev/null +++ b/tests/functional/qemu_test/config.py @@ -0,0 +1,36 @@ +# Test class and utilities for functional tests +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# Original Author (Avocado-based tests): +# Cleber Rosa +# +# Adaption for standalone version: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +from pathlib import Path + + +def _source_dir(): + # Determine top-level directory of the QEMU sources + return Path(__file__).parent.parent.parent.parent + +def _build_dir(): + root = os.getenv('QEMU_BUILD_ROOT') + if root is not None: + return Path(root) + # Makefile.mtest only exists in build dir, so if it is available, use CWD + if os.path.exists('Makefile.mtest'): + return Path(os.getcwd()) + + root = os.path.join(_source_dir(), 'build') + if os.path.exists(root): + return Path(root) + + raise Exception("Cannot identify build dir, set QEMU_BUILD_ROOT") + +BUILD_DIR = _build_dir() diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py new file mode 100644 index 0000000000..2b5b9a5fda --- /dev/null +++ b/tests/functional/qemu_test/linuxkernel.py @@ -0,0 +1,53 @@ +# Test class for testing the boot process of a Linux kernel +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from .testcase import QemuSystemTest +from .cmd import run_cmd, wait_for_console_pattern +from .utils import archive_extract + +class LinuxKernelTest(QemuSystemTest): + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def launch_kernel(self, kernel, initrd=None, dtb=None, console_index=0, + wait_for=None): + self.vm.set_console(console_index=console_index) + self.vm.add_args('-kernel', kernel) + if initrd: + self.vm.add_args('-initrd', initrd) + if dtb: + self.vm.add_args('-dtb', dtb) + self.vm.launch() + if wait_for: + self.wait_for_console_pattern(wait_for) + + def extract_from_deb(self, deb_path, path): + """ + Extracts a file from a deb package into the test workdir + + :param deb_path: path to the deb archive + :param path: path within the deb archive of the file to be extracted + :returns: path of the extracted file + """ + cwd = os.getcwd() + os.chdir(self.workdir) + (stdout, stderr, ret) = run_cmd(['ar', 't', deb_path]) + file_path = stdout.split()[2] + run_cmd(['ar', 'x', deb_path, file_path]) + archive_extract(file_path, self.workdir) + os.chdir(cwd) + # Return complete path to extracted file. Because callers to + # extract_from_deb() specify 'path' with a leading slash, it is + # necessary to use os.path.relpath() as otherwise os.path.join() + # interprets it as an absolute path and drops the self.workdir part. + return os.path.normpath(os.path.join(self.workdir, + os.path.relpath(path, '/'))) + diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py new file mode 100644 index 0000000000..db441027b9 --- /dev/null +++ b/tests/functional/qemu_test/tesseract.py @@ -0,0 +1,36 @@ +# ... +# +# Copyright (c) 2019 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. + +import re +import logging + +from . import has_cmd, run_cmd + +def tesseract_available(expected_version): + (has_tesseract, _) = has_cmd('tesseract') + if not has_tesseract: + return False + (stdout, stderr, ret) = run_cmd([ 'tesseract', '--version']) + if ret: + return False + version = stdout.split()[1] + return int(version.split('.')[0]) >= expected_version + +def tesseract_ocr(image_path, tesseract_args=''): + console_logger = logging.getLogger('console') + console_logger.debug(image_path) + (stdout, stderr, ret) = run_cmd(['tesseract', image_path, + 'stdout']) + if ret: + return None + lines = [] + for line in stdout.split('\n'): + sline = line.strip() + if len(sline): + console_logger.debug(sline) + lines += [sline] + return lines diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py new file mode 100644 index 0000000000..90ae59eb54 --- /dev/null +++ b/tests/functional/qemu_test/testcase.py @@ -0,0 +1,231 @@ +# Test class and utilities for functional tests +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# Original Author (Avocado-based tests): +# Cleber Rosa +# +# Adaption for standalone version: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import logging +import os +import pycotap +import shutil +import subprocess +import sys +import unittest +import uuid + +from qemu.machine import QEMUMachine +from qemu.utils import kvm_available, tcg_available + +from .asset import Asset +from .cmd import run_cmd +from .config import BUILD_DIR + + +class QemuBaseTest(unittest.TestCase): + + qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') + arch = None + + workdir = None + log = None + logdir = None + + def setUp(self, bin_prefix): + self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') + self.arch = self.qemu_bin.split('-')[-1] + + self.outputdir = os.path.join(BUILD_DIR, 'tests', 'functional', + self.arch, self.id()) + self.workdir = os.path.join(self.outputdir, 'scratch') + os.makedirs(self.workdir, exist_ok=True) + + self.logdir = self.outputdir + self.log_filename = os.path.join(self.logdir, 'base.log') + self.log = logging.getLogger('qemu-test') + self.log.setLevel(logging.DEBUG) + self._log_fh = logging.FileHandler(self.log_filename, mode='w') + self._log_fh.setLevel(logging.DEBUG) + fileFormatter = logging.Formatter( + '%(asctime)s - %(levelname)s: %(message)s') + self._log_fh.setFormatter(fileFormatter) + self.log.addHandler(self._log_fh) + + # Capture QEMUMachine logging + self.machinelog = logging.getLogger('qemu.machine') + self.machinelog.setLevel(logging.DEBUG) + self.machinelog.addHandler(self._log_fh) + + def tearDown(self): + if "QEMU_TEST_KEEP_SCRATCH" not in os.environ: + shutil.rmtree(self.workdir) + self.machinelog.removeHandler(self._log_fh) + self.log.removeHandler(self._log_fh) + + def main(): + path = os.path.basename(sys.argv[0])[:-3] + + cache = os.environ.get("QEMU_TEST_PRECACHE", None) + if cache is not None: + Asset.precache_suites(path, cache) + return + + tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError, + test_output_log = pycotap.LogMode.LogToError) + res = unittest.main(module = None, testRunner = tr, exit = False, + argv=["__dummy__", path]) + for (test, message) in res.result.errors + res.result.failures: + + if hasattr(test, "log_filename"): + print('More information on ' + test.id() + ' could be found here:' + '\n %s' % test.log_filename, file=sys.stderr) + if hasattr(test, 'console_log_name'): + print(' %s' % test.console_log_name, file=sys.stderr) + sys.exit(not res.result.wasSuccessful()) + + +class QemuUserTest(QemuBaseTest): + + def setUp(self): + super().setUp('qemu-') + self._ldpath = [] + + def add_ldpath(self, ldpath): + self._ldpath.append(os.path.abspath(ldpath)) + + def run_cmd(self, bin_path, args=[]): + return subprocess.run([self.qemu_bin] + + ["-L %s" % ldpath for ldpath in self._ldpath] + + [bin_path] + + args, + text=True, capture_output=True) + +class QemuSystemTest(QemuBaseTest): + """Facilitates system emulation tests.""" + + cpu = None + machine = None + _machinehelp = None + + def setUp(self): + self._vms = {} + + super().setUp('qemu-system-') + + console_log = logging.getLogger('console') + console_log.setLevel(logging.DEBUG) + self.console_log_name = os.path.join(self.logdir, 'console.log') + self._console_log_fh = logging.FileHandler(self.console_log_name, + mode='w') + self._console_log_fh.setLevel(logging.DEBUG) + fileFormatter = logging.Formatter('%(asctime)s: %(message)s') + self._console_log_fh.setFormatter(fileFormatter) + console_log.addHandler(self._console_log_fh) + + def set_machine(self, machinename): + # TODO: We should use QMP to get the list of available machines + if not self._machinehelp: + self._machinehelp = run_cmd([self.qemu_bin, '-M', 'help'])[0]; + if self._machinehelp.find(machinename) < 0: + self.skipTest('no support for machine ' + machinename) + self.machine = machinename + + def require_accelerator(self, accelerator): + """ + Requires an accelerator to be available for the test to continue + + It takes into account the currently set qemu binary. + + If the check fails, the test is canceled. If the check itself + for the given accelerator is not available, the test is also + canceled. + + :param accelerator: name of the accelerator, such as "kvm" or "tcg" + :type accelerator: str + """ + checker = {'tcg': tcg_available, + 'kvm': kvm_available}.get(accelerator) + if checker is None: + self.skipTest("Don't know how to check for the presence " + "of accelerator %s" % accelerator) + if not checker(qemu_bin=self.qemu_bin): + self.skipTest("%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.skipTest('no support for " + netdevname + " networking') + + def require_device(self, devicename): + devhelp = run_cmd([self.qemu_bin, + '-M', 'none', '-device', 'help'])[0]; + if devhelp.find(devicename) < 0: + self.skipTest('no support for device ' + devicename) + + def _new_vm(self, name, *args): + vm = QEMUMachine(self.qemu_bin, + name=name, + base_temp_dir=self.workdir, + log_dir=self.logdir) + self.log.debug('QEMUMachine "%s" created', name) + self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) + + sockpath = os.environ.get("QEMU_TEST_QMP_BACKDOOR", None) + if sockpath is not None: + vm.add_args("-chardev", + f"socket,id=backdoor,path={sockpath},server=on,wait=off", + "-mon", "chardev=backdoor,mode=control") + + if args: + vm.add_args(*args) + return vm + + @property + def vm(self): + return self.get_vm(name='default') + + def get_vm(self, *args, name=None): + if not name: + name = str(uuid.uuid4()) + if self._vms.get(name) is None: + self._vms[name] = self._new_vm(name, *args) + if self.cpu is not None: + self._vms[name].add_args('-cpu', self.cpu) + if self.machine is not None: + self._vms[name].set_machine(self.machine) + return self._vms[name] + + def set_vm_arg(self, arg, value): + """ + Set an argument to list of extra arguments to be given to the QEMU + binary. If the argument already exists then its value is replaced. + + :param arg: the QEMU argument, such as "-cpu" in "-cpu host" + :type arg: str + :param value: the argument value, such as "host" in "-cpu host" + :type value: str + """ + if not arg or not value: + return + if arg not in self.vm.args: + self.vm.args.extend([arg, value]) + else: + idx = self.vm.args.index(arg) + 1 + if idx < len(self.vm.args): + self.vm.args[idx] = value + else: + self.vm.args.append(value) + + def tearDown(self): + for vm in self._vms.values(): + vm.shutdown() + logging.getLogger('console').removeHandler(self._console_log_fh) + super().tearDown() diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py new file mode 100644 index 0000000000..ab3b27da43 --- /dev/null +++ b/tests/functional/qemu_test/tuxruntest.py @@ -0,0 +1,159 @@ +# 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 stat +import time + +from qemu_test import QemuSystemTest +from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test import has_cmd, run_cmd, get_qemu_img + +class TuxRunBaselineTest(QemuSystemTest): + + 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 + (has_zstd, msg) = has_cmd('zstd') + if has_zstd is False: + self.skipTest(msg) + self.zstd = 'zstd' + + # Pre-init TuxRun specific settings: 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.arch + + # Most Linux's use ttyS0 for their serial port + self.console = "ttyS0" + + # Does the machine shutdown QEMU nicely on "halt" + self.wait_for_shutdown = True + + self.root = "vda" + + # Occasionally we need extra devices to hook things up + self.extradev = None + + self.qemu_img = get_qemu_img(self) + + 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, kernel_asset, rootfs_asset, dtb_asset=None): + """ + Fetch the TuxBoot assets. + """ + kernel_image = kernel_asset.fetch() + disk_image_zst = rootfs_asset.fetch() + + disk_image = self.workdir + "/rootfs.ext4" + + run_cmd([self.zstd, "-f", "-d", disk_image_zst, + "-o", disk_image]) + # zstd copies source archive permissions for the output + # file, so must make this writable for QEMU + os.chmod(disk_image, stat.S_IRUSR | stat.S_IWUSR) + + dtb = dtb_asset.fetch() if dtb_asset is not None else None + + return (kernel_image, disk_image, 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. + """ + ps1='root@tuxtest:~#' + self.wait_for_console_pattern('tuxtest login:') + exec_command_and_wait_for_pattern(self, 'root', ps1) + exec_command_and_wait_for_pattern(self, 'cat /proc/interrupts', ps1) + exec_command_and_wait_for_pattern(self, 'cat /proc/self/maps', ps1) + exec_command_and_wait_for_pattern(self, 'uname -a', ps1) + exec_command_and_wait_for_pattern(self, 'halt', haltmsg) + + # Wait for VM to shut down gracefully if it can + if self.wait_for_shutdown: + self.vm.wait() + else: + self.vm.shutdown() + + def common_tuxrun(self, + kernel_asset, + rootfs_asset, + dtb_asset=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(kernel_asset, rootfs_asset, + dtb_asset) + + self.prepare_run(kernel, disk, drive, dtb, console_index) + self.vm.launch() + self.run_tuxtest_tests(haltmsg) + os.remove(disk) diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py new file mode 100644 index 0000000000..1bf1c410d5 --- /dev/null +++ b/tests/functional/qemu_test/utils.py @@ -0,0 +1,77 @@ +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import gzip +import lzma +import os +import shutil +import subprocess +import tarfile + +""" +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 +""" +def image_pow2ceil_expand(path): + size = os.path.getsize(path) + size_aligned = pow2ceil(size) + if size != size_aligned: + with open(path, 'ab+') as fd: + fd.truncate(size_aligned) + +def archive_extract(archive, dest_dir, member=None): + with tarfile.open(archive) as tf: + if hasattr(tarfile, 'data_filter'): + tf.extraction_filter = getattr(tarfile, 'data_filter', + (lambda member, path: member)) + if member: + tf.extract(member=member, path=dest_dir) + else: + tf.extractall(path=dest_dir) + +def gzip_uncompress(gz_path, output_path): + if os.path.exists(output_path): + return + with gzip.open(gz_path, 'rb') as gz_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(gz_in, raw_out) + except: + os.remove(output_path) + raise + +def lzma_uncompress(xz_path, output_path): + if os.path.exists(output_path): + return + with lzma.open(xz_path, 'rb') as lzma_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(lzma_in, raw_out) + except: + os.remove(output_path) + raise + +def cpio_extract(cpio_handle, output_path): + cwd = os.getcwd() + os.chdir(output_path) + subprocess.run(['cpio', '-i'], + input=cpio_handle.read(), + stderr=subprocess.DEVNULL) + os.chdir(cwd) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py new file mode 100644 index 0000000000..59916efd71 --- /dev/null +++ b/tests/functional/test_aarch64_aspeed.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import archive_extract + +class AST2x00MachineSDK(QemuSystemTest): + + def do_test_aarch64_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', '-snapshot') + + self.vm.launch() + + wait_for_console_pattern(self, 'U-Boot 2023.10') + wait_for_console_pattern(self, '## Loading kernel from FIT Image') + wait_for_console_pattern(self, 'Starting kernel ...') + + ASSET_SDK_V902_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.02/ast2700-default-obmc.tar.gz', + 'ac969c2602f4e6bdb69562ff466b89ae3fe1d86e1f6797bb7969d787f82116a7') + + def test_aarch64_ast2700_evb_sdk_v09_02(self): + self.set_machine('ast2700-evb') + + image_path = self.ASSET_SDK_V902_AST2700.fetch() + archive_extract(image_path, self.workdir) + + num_cpu = 4 + image_dir = self.workdir + '/ast2700-default/' + uboot_size = os.path.getsize(image_dir + 'u-boot-nodtb.bin') + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': image_dir + 'u-boot-nodtb.bin' + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': image_dir + 'u-boot.dtb' + }, + { + 'addr': '0x430000000', + 'file': image_dir + 'bl31.bin' + }, + { + 'addr': '0x430080000', + 'file': image_dir + 'optee/tee-raw.bin' + } + ] + + for load_image in load_images_list: + addr = load_image['addr'] + file = load_image['file'] + self.vm.add_args('-device', + f'loader,force-raw=on,addr={addr},file={file}') + + for i in range(num_cpu): + self.vm.add_args('-device', + f'loader,addr=0x430000000,cpu-num={i}') + + self.vm.add_args('-smp', str(num_cpu)) + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.do_test_aarch64_aspeed_sdk_start(image_dir + 'image-bmc') + + wait_for_console_pattern(self, 'ast2700-default login:') + + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, + '0penBmc', 'root@ast2700-default:~#') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', + 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon20/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/hwmon20/temp1_input', '18000') + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/test_aarch64_raspi3.py new file mode 100755 index 0000000000..369f95a3d9 --- /dev/null +++ b/tests/functional/test_aarch64_raspi3.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Raspberry Pi machine +# and checks the console +# +# Copyright (c) 2020 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +from zipfile import ZipFile + +from qemu_test import LinuxKernelTest, Asset + + +class Aarch64Raspi3Machine(LinuxKernelTest): + + ASSET_RPI3_UEFI = Asset( + ('https://github.com/pbatard/RPi3/releases/download/' + 'v1.15/RPi3_UEFI_Firmware_v1.15.zip'), + '8cff2e979560048b4c84921f41a91893240b9fb71a88f0b5c5d6c8edd994bd5b') + + def test_aarch64_raspi3_atf(self): + efi_name = 'RPI_EFI.fd' + zip_path = self.ASSET_RPI3_UEFI.fetch() + + with ZipFile(zip_path, 'r') as zf: + zf.extract(efi_name, path=self.workdir) + efi_fd = os.path.join(self.workdir, efi_name) + + self.set_machine('raspi3b') + self.vm.set_console(console_index=1) + self.vm.add_args('-cpu', 'cortex-a53', + '-nodefaults', + '-device', f'loader,file={efi_fd},force-raw=true') + self.vm.launch() + self.wait_for_console_pattern('version UEFI Firmware v1.15') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/test_aarch64_raspi4.py new file mode 100755 index 0000000000..e5c9f77479 --- /dev/null +++ b/tests/functional/test_aarch64_raspi4.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Raspberry Pi machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import gzip_uncompress + + +class Aarch64Raspi4Machine(LinuxKernelTest): + + """ + 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 + """ + ASSET_KERNEL_20190215 = Asset( + ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20230106-1_arm64.deb'), + '56d5713c8f6eee8a0d3f0e73600ec11391144fef318b08943e9abd94c0a9baf7') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/' + 'arm64/rootfs.cpio.gz'), + '7c0b16d1853772f6f4c3ca63e789b3b9ff4936efac9c8a01fb0c98c05c7a7648') + + def test_arm_raspi4(self): + deb_path = self.ASSET_KERNEL_20190215.fetch() + 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.set_machine('raspi4b') + 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): + deb_path = self.ASSET_KERNEL_20190215.fetch() + 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_path_gz = self.ASSET_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(initrd_path_gz, initrd_path) + + self.set_machine('raspi4b') + 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() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py new file mode 100755 index 0000000000..9fda396b3a --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a 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 qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test.utils import lzma_uncompress +from unittest import skipUnless + +def fetch_firmware(test): + """ + Flash volumes generated using: + + Toolchain from Debian: + aarch64-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0 + + Used components: + + - Trusted Firmware v2.11.0 + - Tianocore EDK2 4d4f569924 + - Tianocore EDK2-platforms 3f08401 + + """ + + # Secure BootRom (TF-A code) + fs0_xz_path = Aarch64SbsarefMachine.ASSET_FLASH0.fetch() + fs0_path = os.path.join(test.workdir, "SBSA_FLASH0.fd") + lzma_uncompress(fs0_xz_path, fs0_path) + + # Non-secure rom (UEFI and EFI variables) + fs1_xz_path = Aarch64SbsarefMachine.ASSET_FLASH1.fetch() + fs1_path = os.path.join(test.workdir, "SBSA_FLASH1.fd") + lzma_uncompress(fs1_xz_path, fs1_path) + + for path in [fs0_path, fs1_path]: + with open(path, "ab+") as fd: + fd.truncate(256 << 20) # Expand volumes to 256MiB + + test.set_machine('sbsa-ref') + test.vm.set_console() + test.vm.add_args( + "-drive", f"if=pflash,file={fs0_path},format=raw", + "-drive", f"if=pflash,file={fs1_path},format=raw", + ) + + +class Aarch64SbsarefMachine(QemuSystemTest): + """ + As firmware runs at a higher privilege level than the hypervisor we + can only run these tests under TCG emulation. + """ + + timeout = 180 + + ASSET_FLASH0 = Asset( + ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' + '20240619-148232/edk2/SBSA_FLASH0.fd.xz'), + '0c954842a590988f526984de22e21ae0ab9cb351a0c99a8a58e928f0c7359cf7') + + ASSET_FLASH1 = Asset( + ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' + '20240619-148232/edk2/SBSA_FLASH1.fd.xz'), + 'c6ec39374c4d79bb9e9cdeeb6db44732d90bb4a334cec92002b3f4b9cac4b5ee') + + def test_sbsaref_edk2_firmware(self): + + fetch_firmware(self) + + self.vm.add_args('-cpu', 'cortex-a57') + 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.11.0(release):") + wait_for_console_pattern(self, "BL1: Booting BL2") + + # Trusted Boot Firmware + wait_for_console_pattern(self, "BL2: v2.11.0(release)") + wait_for_console_pattern(self, "Booting BL31") + + # EL3 Runtime Software + wait_for_console_pattern(self, "BL31: v2.11.0(release)") + + # Non-trusted Firmware + wait_for_console_pattern(self, "UEFI firmware (version 1.0") + interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine") + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py new file mode 100755 index 0000000000..ebc29b2fb5 --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a 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 qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from unittest import skipUnless +from test_aarch64_sbsaref import fetch_firmware + + +class Aarch64SbsarefAlpine(QemuSystemTest): + + ASSET_ALPINE_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/' + 'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'), + '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027') + + # 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=None): + fetch_firmware(self) + + iso_path = self.ASSET_ALPINE_ISO.fetch() + + self.vm.set_console() + self.vm.add_args( + "-drive", f"file={iso_path},media=cdrom,format=raw", + ) + if cpu: + self.vm.add_args("-cpu", cpu) + + self.vm.launch() + wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") + + def test_sbsaref_alpine_linux_cortex_a57(self): + self.boot_alpine_linux("cortex-a57") + + def test_sbsaref_alpine_linux_default_cpu(self): + self.boot_alpine_linux() + + def test_sbsaref_alpine_linux_max_pauth_off(self): + self.boot_alpine_linux("max,pauth=off") + + def test_sbsaref_alpine_linux_max_pauth_impdef(self): + self.boot_alpine_linux("max,pauth-impdef=on") + + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), + 'Test might timeout due to PAuth emulation') + def test_sbsaref_alpine_linux_max(self): + self.boot_alpine_linux("max") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py new file mode 100755 index 0000000000..80298dd190 --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a 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 qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from unittest import skipUnless +from test_aarch64_sbsaref import fetch_firmware + + +class Aarch64SbsarefFreeBSD(QemuSystemTest): + + ASSET_FREEBSD_ISO = Asset( + ('https://download.freebsd.org/releases/arm64/aarch64/ISO-IMAGES/' + '14.1/FreeBSD-14.1-RELEASE-arm64-aarch64-bootonly.iso'), + '44cdbae275ef1bb6dab1d5fbb59473d4f741e1c8ea8a80fd9e906b531d6ad461') + + # 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_freebsd14(self, cpu=None): + fetch_firmware(self) + + img_path = self.ASSET_FREEBSD_ISO.fetch() + + self.vm.set_console() + self.vm.add_args( + "-drive", f"file={img_path},format=raw,snapshot=on", + ) + if cpu: + self.vm.add_args("-cpu", cpu) + + self.vm.launch() + wait_for_console_pattern(self, 'Welcome to FreeBSD!') + + def test_sbsaref_freebsd14_cortex_a57(self): + self.boot_freebsd14("cortex-a57") + + def test_sbsaref_freebsd14_default_cpu(self): + self.boot_freebsd14() + + def test_sbsaref_freebsd14_max_pauth_off(self): + self.boot_freebsd14("max,pauth=off") + + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), + 'Test might timeout due to PAuth emulation') + def test_sbsaref_freebsd14_max_pauth_impdef(self): + self.boot_freebsd14("max,pauth-impdef=on") + + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), + 'Test might timeout due to PAuth emulation') + def test_sbsaref_freebsd14_max(self): + self.boot_freebsd14("max") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py new file mode 100755 index 0000000000..01660eb090 --- /dev/null +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# +# TCG Plugins tests +# +# These are a little more involved than the basic tests run by check-tcg. +# +# Copyright (c) 2021 Linaro +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import tempfile +import mmap +import re + +from qemu_test import LinuxKernelTest, Asset + + +class PluginKernelBase(LinuxKernelTest): + """ + Boots a Linux kernel with a TCG plugin enabled. + """ + + timeout = 120 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' + + def run_vm(self, kernel_path, kernel_command_line, + plugin, plugin_log, console_pattern, args=None): + + vm = self.get_vm() + vm.set_console() + vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-plugin', plugin, + '-d', 'plugin', + '-D', plugin_log, + '-net', 'none', + '-no-reboot') + if args: + vm.add_args(*args) + + try: + vm.launch() + except: + # TODO: probably fails because plugins not enabled but we + # can't currently probe for the feature. + self.cancel("TCG Plugins not enabled?") + + self.wait_for_console_pattern(console_pattern, vm) + # ensure logs are flushed + vm.shutdown() + + +class PluginKernelNormal(PluginKernelBase): + + ASSET_KERNEL = Asset( + ('https://storage.tuxboot.com/20230331/arm64/Image'), + 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7') + + def test_aarch64_virt_insn(self): + self.set_machine('virt') + self.cpu='cortex-a53' + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'Kernel panic - not syncing: VFS:' + + plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", + suffix=".log") + + self.run_vm(kernel_path, kernel_command_line, + "tests/tcg/plugins/libinsn.so", plugin_log.name, + console_pattern) + + with plugin_log as lf, \ + mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: + + 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): + self.set_machine('virt') + self.cpu='cortex-a53' + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'Kernel panic - not syncing: VFS:' + + plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", + suffix=".log") + + self.run_vm(kernel_path, kernel_command_line, + "tests/tcg/plugins/libinsn.so", plugin_log.name, + console_pattern, + args=('-icount', 'shift=1')) + + with plugin_log as lf, \ + mmap.mmap(lf.fileno(), 0, access=mmap.ACCESS_READ) as s: + + 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") + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_tuxrun.py b/tests/functional/test_aarch64_tuxrun.py new file mode 100755 index 0000000000..75adc8acb8 --- /dev/null +++ b/tests/functional/test_aarch64_tuxrun.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunAarch64Test(TuxRunBaselineTest): + + ASSET_ARM64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/Image', + 'b74743c5e89e1cea0f73368d24ae0ae85c5204ff84be3b5e9610417417d2f235') + ASSET_ARM64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/rootfs.ext4.zst', + 'a1acaaae2068df4648d04ff75f532aaa8c5edcd6b936122b6f0db4848a07b465') + + def test_arm64(self): + self.set_machine('virt') + self.cpu='cortex-a57' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARM64_KERNEL, + rootfs_asset=self.ASSET_ARM64_ROOTFS) + + ASSET_ARM64BE_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64be/Image', + 'fd6af4f16689d17a2c24fe0053cc212edcdf77abdcaf301800b8d38fa9f6e109') + ASSET_ARM64BE_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64be/rootfs.ext4.zst', + 'f5e9371b62701aab8dead52592ca7488c8a9e255c9be8d7635c7f30f477c2c21') + + def test_arm64be(self): + self.set_machine('virt') + self.cpu='cortex-a57' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARM64BE_KERNEL, + rootfs_asset=self.ASSET_ARM64BE_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py new file mode 100755 index 0000000000..c967da41b4 --- /dev/null +++ b/tests/functional/test_aarch64_virt.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a various Linux systems and checks the +# console output. +# +# Copyright (c) 2022 Linaro Ltd. +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import time +import os +import logging + +from qemu_test import BUILD_DIR +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import get_qemu_img, run_cmd + + +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) + + ASSET_ALPINE_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/' + 'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'), + '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027') + + # 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): + iso_path = self.ASSET_ALPINE_ISO.fetch() + + self.set_machine('virt') + 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},media=cdrom,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') + + + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/' + 'z6B2ARM7DQT3HWN/download'), + '12a54d4805cda6ab647cb7c7bbdb16fafb3df400e0d6f16445c1a0436100ef8d') + + 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_path = self.ASSET_KERNEL.fetch() + + self.set_machine('virt') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.require_accelerator("tcg") + self.vm.add_args('-cpu', 'max,pauth-impdef=on', + '-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 = get_qemu_img(self) + run_cmd([qemu_img, 'create', '-f', 'qcow2', image_path, '8M']) + + # 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): + self.common_aarch64_virt("virt,gic_version=3") + + def test_aarch64_virt_gicv2(self): + self.common_aarch64_virt("virt,gic-version=2") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py new file mode 100755 index 0000000000..e2f84414d7 --- /dev/null +++ b/tests/functional/test_acpi_bits.py @@ -0,0 +1,392 @@ +#!/usr/bin/env python3 +# +# 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 functional 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 pathlib import Path +from typing import ( + List, + Optional, + Sequence, +) +from qemu.machine import QEMUMachine +from unittest import skipIf +from qemu_test import QemuSystemTest, Asset + +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(QemuSystemTest): #pylint: disable=too-many-instance-attributes + """ + ACPI and SMBIOS tests using biosbits. + """ + # in slower systems the test can take as long as 3 minutes to complete. + timeout = BITS_TIMEOUT + + # following are some standard configuration constants + # gitlab CI does shallow clones of depth 20 + BITS_INTERNAL_VER = 2020 + # commit hash must match the artifact tag below + BITS_COMMIT_HASH = 'c7920d2b' + # this is the latest bits release as of today. + BITS_TAG = "qemu-bits-10262023" + + ASSET_BITS = Asset(("https://gitlab.com/qemu-project/" + "biosbits-bits/-/jobs/artifacts/%s/" + "download?job=qemu-bits-build" % BITS_TAG), + '1b8dd612c6831a6b491716a77acc486666aaa867051cdc34f7ce169c2e25f487') + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._vm = None + self._baseDir = None + + self._debugcon_addr = '0x403' + self._debugcon_log = 'debugcon-log.txt' + self.logger = self.log + + 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.BITS_INTERNAL_VER, + '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.BITS_INTERNAL_VER, + '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 + # they are not run by accident. + # + # These scripts are intended to run inside the test VM + # and are written for python 2.7 not python 3, hence + # would cause syntax errors if loaded ouside the VM. + 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.BITS_INTERNAL_VER) + iso_file = os.path.join(self.workdir, + 'bits-%d.iso' %self.BITS_INTERNAL_VER) + 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'): + proc = subprocess.run([mkrescue_script, '-o', iso_file, + bits_dir], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=True) + self.logger.info("grub-mkrescue output %s" % proc.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() + self.logger = self.log + + self._baseDir = Path(__file__).parent + + 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.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) + grub_tar_file = os.path.join(prebuiltDir, + 'bits-%d-%s-grub.tar.gz' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) + + bitsLocalArtLoc = self.ASSET_BITS.fetch() + 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) + super().tearDown() + + def test_acpi_smbios_bits(self): + """The main test case implementation.""" + + self.set_machine('pc') + iso_file = os.path.join(self.workdir, + 'bits-%d.iso' %self.BITS_INTERNAL_VER) + + 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() + + + # biosbits has been configured to run all the specified test suites + # in batch mode and then automatically initiate a vm shutdown. + self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT) + self._vm.wait(timeout=None) + self.logger.debug("Checking console output ...") + self.parse_log() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/test_alpha_clipper.py new file mode 100755 index 0000000000..c1fbf0e395 --- /dev/null +++ b/tests/functional/test_alpha_clipper.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an Alpha Clipper machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import gzip_uncompress + + +class AlphaClipperTest(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/dists/lenny/main/' + 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz'), + '34f53da3fa32212e4f00b03cb944b2ad81c06bc8faaf9b7193b2e544ceeca576') + + def test_alpha_clipper(self): + self.set_machine('clipper') + kernel_path = self.ASSET_KERNEL.fetch() + + uncompressed_kernel = os.path.join(self.workdir, 'vmlinux') + gzip_uncompress(kernel_path, uncompressed_kernel) + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-nodefaults', + '-kernel', uncompressed_kernel, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_aspeed.py b/tests/functional/test_arm_aspeed.py new file mode 100755 index 0000000000..d88170ac24 --- /dev/null +++ b/tests/functional/test_arm_aspeed.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time +import subprocess +import tempfile + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test import has_cmd +from qemu_test.utils import archive_extract +from zipfile import ZipFile +from unittest import skipUnless + +class AST1030Machine(LinuxKernelTest): + + ASSET_ZEPHYR_1_04 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip'), + '4ac6210adcbc61294927918707c6762483fd844dde5e07f3ba834ad1f91434d3') + + def test_ast1030_zephyros_1_04(self): + self.set_machine('ast1030-evb') + + zip_file = self.ASSET_ZEPHYR_1_04.fetch() + + kernel_name = "ast1030-evb-demo/zephyr.elf" + with ZipFile(zip_file, 'r') as zf: + zf.extract(kernel_name, path=self.workdir) + kernel_file = os.path.join(self.workdir, kernel_name) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, '-nographic') + self.vm.launch() + self.wait_for_console_pattern("Booting Zephyr OS") + exec_command_and_wait_for_pattern(self, "help", + "Available commands") + + ASSET_ZEPHYR_1_07 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip'), + 'ad52e27959746988afaed8429bf4e12ab988c05c4d07c9d90e13ec6f7be4574c') + + def test_ast1030_zephyros_1_07(self): + self.set_machine('ast1030-evb') + + zip_file = self.ASSET_ZEPHYR_1_07.fetch() + + kernel_name = "ast1030-evb-demo/zephyr.bin" + with ZipFile(zip_file, 'r') as zf: + zf.extract(kernel_name, path=self.workdir) + kernel_file = os.path.join(self.workdir, kernel_name) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, '-nographic') + self.vm.launch() + self.wait_for_console_pattern("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(LinuxKernelTest): + + def do_test_arm_aspeed(self, machine, image): + self.set_machine(machine) + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-snapshot') + 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") + + ASSET_PALMETTO_FLASH = Asset( + ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' + 'obmc-phosphor-image-palmetto.static.mtd'), + '3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d'); + + def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): + image_path = self.ASSET_PALMETTO_FLASH.fetch() + + self.do_test_arm_aspeed('palmetto-bmc', image_path) + + ASSET_ROMULUS_FLASH = Asset( + ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' + 'obmc-phosphor-image-romulus.static.mtd'), + '820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') + + def test_arm_ast2500_romulus_openbmc_v2_9_0(self): + image_path = self.ASSET_ROMULUS_FLASH.fetch() + + self.do_test_arm_aspeed('romulus-bmc', 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,read-only=true', + '-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) + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, 'passw0rd', '#') + + def do_test_arm_aspeed_buildroot_poweroff(self): + exec_command_and_wait_for_pattern(self, 'poweroff', + 'reboot: System halted'); + + ASSET_BR2_202311_AST2500_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2500-evb/buildroot-2023.11/flash.img'), + 'c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f') + + def test_arm_ast2500_evb_buildroot(self): + self.set_machine('ast2500-evb') + + image_path = self.ASSET_BR2_202311_AST2500_FLASH.fetch() + + 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', + 'ast2500-evb login:') + + 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() + + ASSET_BR2_202311_AST2600_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.11/flash.img'), + 'b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68') + + def test_arm_ast2600_evb_buildroot(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202311_AST2600_FLASH.fetch() + + 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', + 'ast2600-evb login:') + + 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_and_wait_for_pattern(self, + 'i2cset -y 3 0x42 0x64 0x00 0xaa i', '#'); + 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() + + ASSET_BR2_202302_AST2600_TPM_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img'), + 'a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') + + @skipUnless(*has_cmd('swtpm')) + def test_arm_ast2600_evb_buildroot_tpm(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch() + + tpmstate_dir = tempfile.TemporaryDirectory(prefix="qemu_") + socket = os.path.join(tpmstate_dir.name, 'swtpm-socket') + + # We must put the TPM state dir in /tmp/, not the build dir, + # because some distros use AppArmor to lock down swtpm and + # restrict the set of locations it can access files in. + subprocess.run(['swtpm', 'socket', '-d', '--tpm2', + '--tpmstate', f'dir={tpmstate_dir.name}', + '--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() + + 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', '-snapshot') + 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 ...') + + ASSET_SDK_V806_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2500-default-obmc.tar.gz', + 'e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') + + def test_arm_ast2500_evb_sdk(self): + self.set_machine('ast2500-evb') + + image_path = self.ASSET_SDK_V806_AST2500.fetch() + + archive_extract(image_path, self.workdir) + + self.do_test_arm_aspeed_sdk_start( + self.workdir + '/ast2500-default/image-bmc') + + self.wait_for_console_pattern('ast2500-default login:') + + ASSET_SDK_V806_AST2600_A2 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2600-a2-obmc.tar.gz', + '9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') + + def test_arm_ast2600_evb_sdk(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_SDK_V806_AST2600_A2.fetch() + + 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('ast2600-a2 login:') + + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', 'root@ast2600-a2:~#') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/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/hwmon19/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, + '/sbin/hwclock -f /dev/rtc1', year); + + +class AST2x00MachineMMC(LinuxKernelTest): + + ASSET_RAINIER_EMMC = Asset( + ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/' + 'mmc-p10bmc-20240617.qcow2'), + 'd523fb478d2b84d5adc5658d08502bc64b1486955683814f89c6137518acd90b') + + def test_arm_aspeed_emmc_boot(self): + self.set_machine('rainier-bmc') + self.require_netdev('user') + + image_path = self.ASSET_RAINIER_EMMC.fetch() + + self.vm.set_console() + self.vm.add_args('-drive', + 'file=' + image_path + ',if=sd,id=sd2,index=2', + '-net', 'nic', '-net', 'user', '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot SPL 2019.04') + self.wait_for_console_pattern('Trying to boot from MMC1') + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('eMMC 2nd Boot') + 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 0xf00') + self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7') + self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py new file mode 100755 index 0000000000..281925d11a --- /dev/null +++ b/tests/functional/test_arm_bflt.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# +# Test the bFLT loader format +# +# Copyright (C) 2019 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import bz2 + +from qemu_test import QemuUserTest, Asset +from qemu_test import has_cmd +from qemu_test.utils import cpio_extract +from unittest import skipUnless + + +class LoadBFLT(QemuUserTest): + + ASSET_ROOTFS = Asset( + ('https://elinux.org/images/5/51/Stm32_mini_rootfs.cpio.bz2'), + 'eefb788e4980c9e8d6c9d60ce7d15d4da6bf4fbc6a80f487673824600d5ba9cc') + + @skipUnless(*has_cmd('cpio')) + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_stm32(self): + # See https://elinux.org/STM32#User_Space + rootfs_path_bz2 = self.ASSET_ROOTFS.fetch() + busybox_path = os.path.join(self.workdir, "bin/busybox") + + with bz2.open(rootfs_path_bz2, 'rb') as cpio_handle: + cpio_extract(cpio_handle, self.workdir) + + res = self.run_cmd(busybox_path) + ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' + self.assertIn(ver, res.stdout) + + res = self.run_cmd(busybox_path, ['uname', '-a']) + unm = 'armv7l GNU/Linux' + self.assertIn(unm, res.stdout) + + +if __name__ == '__main__': + QemuUserTest.main() diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py new file mode 100755 index 0000000000..35ea58d46c --- /dev/null +++ b/tests/functional/test_arm_bpim2u.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Banana Pi machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset, interrupt_interactive_console_until_pattern +from qemu_test.utils import archive_extract, gzip_uncompress, lzma_uncompress +from qemu_test.utils import image_pow2ceil_expand +from unittest import skipUnless + +class BananaPiMachine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz'), + '2c8dbdb16ea7af2dfbcbea96044dde639fb07d09fd3c4fb31f2027ef71e55ddd') + + ASSET_ROOTFS = Asset( + ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'buildroot-baseline/20230703.0/armel/rootfs.ext2.xz'), + '42b44a12965ac0afe9a88378527fb698a7dc76af50495efc2361ee1595b4e5c6') + + ASSET_SD_IMAGE = Asset( + ('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'), + '5b41b4e11423e562c6011640f9a7cd3bdd0a3d42b83430f7caa70a432e6cd82c') + + def test_arm_bpim2u(self): + self.set_machine('bpim2u') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('/usr/lib/linux-image-6.6.16-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) + os.remove(kernel_path) + os.remove(dtb_path) + + def test_arm_bpim2u_initrd(self): + self.set_machine('bpim2u') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_path_gz = self.ASSET_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + 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() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(initrd_path) + + def test_arm_bpim2u_gmac(self): + self.set_machine('bpim2u') + self.require_netdev('user') + + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_path_xz = self.ASSET_ROOTFS.fetch() + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + 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() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(rootfs_path) + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_bpim2u_openwrt_22_03_3(self): + self.set_machine('bpim2u') + # This test download a 8.9 MiB compressed image and expand it + # to 127 MiB. + image_path_gz = self.ASSET_SD_IMAGE.fetch() + image_path = os.path.join(self.workdir, 'sdcard.img') + gzip_uncompress(image_path_gz, image_path) + 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') + os.remove(image_path) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/test_arm_canona1100.py new file mode 100755 index 0000000000..65f1228296 --- /dev/null +++ b/tests/functional/test_arm_canona1100.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the canon-a1100 machine with firmware +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class CanonA1100Machine(QemuSystemTest): + """Boots the barebox firmware and checks that the console is operational""" + + timeout = 90 + + ASSET_BIOS = Asset(('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day18.tar.xz'), + '28e71874ce985be66b7fd1345ed88cb2523b982f899c8d2900d6353054a1be49') + + def test_arm_canona1100(self): + self.set_machine('canon-a1100') + + file_path = self.ASSET_BIOS.fetch() + archive_extract(file_path, dest_dir=self.workdir, + member="day18/barebox.canon-a1100.bin") + self.vm.set_console() + self.vm.add_args('-bios', + self.workdir + '/day18/barebox.canon-a1100.bin') + self.vm.launch() + wait_for_console_pattern(self, 'running /env/bin/init') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_arm_collie.py b/tests/functional/test_arm_collie.py new file mode 100755 index 0000000000..7e144a0a8f --- /dev/null +++ b/tests/functional/test_arm_collie.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a collie machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class CollieTest(LinuxKernelTest): + + ASSET_ZIMAGE = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/collie/zImage', + '10ace8abf9e0875ef8a83b8829cc3b5b50bc6d7bc3ca29f19f49f5673a43c13b') + + ASSET_ROOTFS = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/collie/rootfs-sa110.cpio', + '89ccaaa5c6b33331887047e1618ffe81b0f55909173944347d5d2426f3bcc1f2') + + def test_arm_collie(self): + self.set_machine('collie') + zimage_path = self.ASSET_ZIMAGE.fetch() + rootfs_path = self.ASSET_ROOTFS.fetch() + self.vm.add_args('-append', 'rdinit=/sbin/init console=ttySA1') + self.launch_kernel(zimage_path, + initrd=rootfs_path, + wait_for='reboot: Restarting system') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/test_arm_integratorcp.py new file mode 100755 index 0000000000..0fe083f661 --- /dev/null +++ b/tests/functional/test_arm_integratorcp.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import logging + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from unittest import skipUnless + + +NUMPY_AVAILABLE = True +try: + import numpy as np +except ImportError: + NUMPY_AVAILABLE = False + +CV2_AVAILABLE = True +try: + import cv2 +except ImportError: + CV2_AVAILABLE = False + + +class IntegratorMachine(QemuSystemTest): + + timeout = 90 + + ASSET_KERNEL = Asset( + ('https://github.com/zayac/qemu-arm/raw/master/' + 'arm-test/kernel/zImage.integrator'), + '26e7c7e8f943de785d95bd3c74d66451604a9b6a7a3d25dceb279e7548fd8e78') + + ASSET_INITRD = Asset( + ('https://github.com/zayac/qemu-arm/raw/master/' + 'arm-test/kernel/arm_root.img'), + 'e187c27fb342ad148c7f33475fbed124933e0b3f4be8c74bc4f3426a4793373a') + + ASSET_TUXLOGO = Asset( + ('https://github.com/torvalds/linux/raw/v2.6.12/' + 'drivers/video/logo/logo_linux_vga16.ppm'), + 'b762f0d91ec018887ad1b334543c2fdf9be9fdfc87672b409211efaa3ea0ef79') + + def boot_integratorcp(self): + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + self.set_machine('integratorcp') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', 'printk.time=0 console=ttyAMA0') + self.vm.launch() + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_integratorcp_console(self): + """ + Boots the Linux kernel and checks that the console is operational + """ + self.boot_integratorcp() + wait_for_console_pattern(self, 'Log in as root') + + @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') + @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_framebuffer_tux_logo(self): + """ + Boot Linux and verify the Tux logo is displayed on the framebuffer. + """ + screendump_path = os.path.join(self.workdir, "screendump.pbm") + tuxlogo_path = self.ASSET_TUXLOGO.fetch() + + self.boot_integratorcp() + framebuffer_ready = 'Console: switching to colour frame buffer device' + wait_for_console_pattern(self, framebuffer_ready) + 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 + match_threshold = 0.92 + screendump_bgr = cv2.imread(screendump_path) + screendump_gray = cv2.cvtColor(screendump_bgr, cv2.COLOR_BGR2GRAY) + result = cv2.matchTemplate(screendump_gray, cv2.imread(tuxlogo_path, 0), + cv2.TM_CCOEFF_NORMED) + loc = np.where(result >= match_threshold) + tux_count = 0 + for tux_count, pt in enumerate(zip(*loc[::-1]), start=1): + logger.debug('found Tux at position [x, y] = %s', pt) + self.assertGreaterEqual(tux_count, cpu_count) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py new file mode 100755 index 0000000000..6d57223a03 --- /dev/null +++ b/tests/functional/test_arm_orangepi.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an Orange Pi machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset, interrupt_interactive_console_until_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract, gzip_uncompress, lzma_uncompress +from qemu_test.utils import image_pow2ceil_expand +from unittest import skipUnless + +class BananaPiMachine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz'), + '2c8dbdb16ea7af2dfbcbea96044dde639fb07d09fd3c4fb31f2027ef71e55ddd') + + ASSET_ROOTFS = Asset( + ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'buildroot-baseline/20230703.0/armel/rootfs.ext2.xz'), + '42b44a12965ac0afe9a88378527fb698a7dc76af50495efc2361ee1595b4e5c6') + + ASSET_ARMBIAN = Asset( + ('https://k-space.ee.armbian.com/archive/orangepipc/archive/' + 'Armbian_23.8.1_Orangepipc_jammy_current_6.1.47.img.xz'), + 'b386dff6552513b5f164ea00f94814a6b0f1da9fb90b83725e949cf797e11afb') + + ASSET_UBOOT = Asset( + ('http://snapshot.debian.org/archive/debian/20200108T145233Z/pool/' + 'main/u/u-boot/u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb'), + '9223d94dc283ab54df41ce9d6f69025a5b47fece29fb67a714e23aa0cdf3bdfa') + + ASSET_NETBSD = Asset( + ('https://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/' + 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz'), + '20d3e07dc057e15c12452620e90ecab2047f0f7940d9cba8182ebc795927177f') + + def test_arm_orangepi(self): + self.set_machine('orangepi-pc') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart,mmio32,0x1c28000') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + os.remove(kernel_path) + os.remove(dtb_path) + + def test_arm_orangepi_initrd(self): + self.set_machine('orangepi-pc') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + initrd_path_gz = self.ASSET_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + 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() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(initrd_path) + + def test_arm_orangepi_sd(self): + self.set_machine('orangepi-pc') + self.require_netdev('user') + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' + dtb_path = self.extract_from_deb(deb_path, dtb_path) + rootfs_path_xz = self.ASSET_ROOTFS.fetch() + rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') + 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=/dev/mmcblk0 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + shell_ready = "/bin/sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'mmcblk0') + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', + 'eth0: Link is Up') + exec_command_and_wait_for_pattern(self, 'udhcpc eth0', + 'udhcpc: lease of 10.0.2.15 obtained') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(rootfs_path) + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_orangepi_armbian(self): + self.set_machine('orangepi-pc') + # This test download a 275 MiB compressed image and expand it + # to 1036 MiB, but the underlying filesystem is 1552 MiB... + # As we expand it to 2 GiB we are safe. + image_path_xz = self.ASSET_ARMBIAN.fetch() + image_path = os.path.join(self.workdir, 'armbian.img') + lzma_uncompress(image_path_xz, image_path) + 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 + + 'console=ttyS0,115200 ' + 'loglevel=7 ' + 'nosmp ' + 'systemd.default_timeout_start_sec=9000 ' + 'systemd.mask=armbian-zram-config.service ' + 'systemd.mask=armbian-ramlog.service') + + self.wait_for_console_pattern('U-Boot SPL') + self.wait_for_console_pattern('Autoboot in ') + exec_command_and_wait_for_pattern(self, ' ', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + + self.wait_for_console_pattern('systemd[1]: Hostname set ' + + 'to ') + self.wait_for_console_pattern('Starting Load Kernel Modules...') + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') + def test_arm_orangepi_uboot_netbsd9(self): + self.set_machine('orangepi-pc') + # This test download a 304MB compressed image and expand it to 2GB + deb_path = self.ASSET_UBOOT.fetch() + # We use the common OrangePi PC 'plus' build of U-Boot for our secondary + # program loader (SPL). We will then set the path to the more specific + # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, + # before to boot NetBSD. + uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' + uboot_path = self.extract_from_deb(deb_path, uboot_path) + image_path_gz = self.ASSET_NETBSD.fetch() + image_path = os.path.join(self.workdir, 'armv7.img') + gzip_uncompress(image_path_gz, image_path) + image_pow2ceil_expand(image_path) + image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path + + # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc + with open(uboot_path, 'rb') as f_in: + with open(image_path, 'r+b') as f_out: + f_out.seek(8 * 1024) + shutil.copyfileobj(f_in, f_out) + + self.vm.set_console() + self.vm.add_args('-nic', 'user', + '-drive', image_drive_args, + '-global', 'allwinner-rtc.base-year=2000', + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1') + interrupt_interactive_console_until_pattern(self, + 'Hit any key to stop autoboot:', + 'switch to partitions #0, OK') + + exec_command_and_wait_for_pattern(self, '', '=>') + cmd = 'setenv bootargs root=ld0a' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv kernel netbsd-GENERIC.ub' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; " + "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; " + "fdt addr ${fdt_addr_r}; " + "bootm ${kernel_addr_r} - ${fdt_addr_r}'") + exec_command_and_wait_for_pattern(self, cmd, '=>') + + exec_command_and_wait_for_pattern(self, 'boot', + 'Booting kernel from Legacy Image') + wait_for_console_pattern(self, 'Starting kernel ...') + wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)') + # Wait for user-space + wait_for_console_pattern(self, 'Starting root file system check') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/test_arm_raspi2.py new file mode 100755 index 0000000000..3bf079dc4d --- /dev/null +++ b/tests/functional/test_arm_raspi2.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Raspberry Pi machine +# and checks the console +# +# Copyright (c) 2019 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import gzip_uncompress + + +class ArmRaspi2Machine(LinuxKernelTest): + + ASSET_KERNEL_20190215 = Asset( + ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20190215-1_armhf.deb'), + '9f1759f7228113da24f5ee2aa6312946ec09a83e076aba9406c46ff776dfb291') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz'), + '2c8dbdb16ea7af2dfbcbea96044dde639fb07d09fd3c4fb31f2027ef71e55ddd') + + def do_test_arm_raspi2(self, uart_id): + """ + 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 + """ + serial_kernel_cmdline = { + 0: 'earlycon=pl011,0x3f201000 console=ttyAMA0', + } + deb_path = self.ASSET_KERNEL_20190215.fetch() + kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') + dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') + + self.set_machine('raspi2b') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + serial_kernel_cmdline[uart_id] + + ' root=/dev/mmcblk0p2 rootwait ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line, + '-device', 'usb-kbd') + self.vm.launch() + + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + self.wait_for_console_pattern('Product: QEMU USB Keyboard') + + def test_arm_raspi2_uart0(self): + self.do_test_arm_raspi2(0) + + def test_arm_raspi2_initrd(self): + deb_path = self.ASSET_KERNEL_20190215.fetch() + kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') + dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') + initrd_path_gz = self.ASSET_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(initrd_path_gz, initrd_path) + + self.set_machine('raspi2b') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,0x3f201000 console=ttyAMA0 ' + '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') + 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', + '/soc/cprman@7e101000') + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') + # Wait for VM to shut down gracefully + self.vm.wait() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py new file mode 100755 index 0000000000..2292317946 --- /dev/null +++ b/tests/functional/test_arm_sx1.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024 Linaro Ltd. +# +# Functional test that boots a Linux kernel on an sx1 machine +# and checks the console. We have three variants: +# * just boot initrd +# * boot with filesystem on SD card +# * boot from flash +# In all cases these images have a userspace that is configured +# to immediately reboot the system on successful boot, so we +# only need to wait for QEMU to exit (via -no-reboot). +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class SX1Test(LinuxKernelTest): + + ASSET_ZIMAGE = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/zImage', + 'a0271899a8dc2165f9e0adb2d0a57fc839ae3a469722ffc56c77e108a8887615') + + ASSET_INITRD = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/rootfs-armv4.cpio', + '35b0721249821aa544cd85b85d3cb8901db4c6d128eed86ab261e5d9e37d58f8') + + ASSET_SD_FS = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/rootfs-armv4.ext2', + 'c1db7f43ef92469ebc8605013728c8950e7608439f01d13678994f0ce101c3a8') + + ASSET_FLASH = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/flash', + '17e6a2758fa38efd2666be0879d4751fd37d194f25168a8deede420df519b676') + + CONSOLE_ARGS = 'console=ttyS0,115200 earlycon=uart8250,mmio32,0xfffb0000,115200n8' + + def test_arm_sx1_initrd(self): + self.set_machine('sx1') + zimage_path = self.ASSET_ZIMAGE.fetch() + initrd_path = self.ASSET_INITRD.fetch() + self.vm.add_args('-append', f'kunit.enable=0 rdinit=/sbin/init {self.CONSOLE_ARGS}') + self.vm.add_args('-no-reboot') + self.launch_kernel(zimage_path, + initrd=initrd_path) + self.vm.wait(timeout=60) + + def test_arm_sx1_sd(self): + self.set_machine('sx1') + zimage_path = self.ASSET_ZIMAGE.fetch() + sd_fs_path = self.ASSET_SD_FS.fetch() + self.vm.add_args('-append', f'kunit.enable=0 root=/dev/mmcblk0 rootwait {self.CONSOLE_ARGS}') + self.vm.add_args('-no-reboot') + self.vm.add_args('-snapshot') + self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}') + self.launch_kernel(zimage_path) + self.vm.wait(timeout=60) + + def test_arm_sx1_flash(self): + self.set_machine('sx1') + zimage_path = self.ASSET_ZIMAGE.fetch() + flash_path = self.ASSET_FLASH.fetch() + self.vm.add_args('-append', f'kunit.enable=0 root=/dev/mtdblock3 rootwait {self.CONSOLE_ARGS}') + self.vm.add_args('-no-reboot') + self.vm.add_args('-snapshot') + self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}') + self.launch_kernel(zimage_path) + self.vm.wait(timeout=60) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_tuxrun.py b/tests/functional/test_arm_tuxrun.py new file mode 100755 index 0000000000..944f0756e1 --- /dev/null +++ b/tests/functional/test_arm_tuxrun.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunArmTest(TuxRunBaselineTest): + + ASSET_ARMV5_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/armv5/zImage', + 'c95af2f27647c12265d75e9df44c22ff5228c59855f54aaa70f41ec2842e3a4d') + ASSET_ARMV5_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/armv5/rootfs.ext4.zst', + '17177afa74e7294da0642861f08c88ca3c836764299a54bf6d1ce276cb9712a5') + ASSET_ARMV5_DTB = Asset( + 'https://storage.tuxboot.com/20230331/armv5/versatile-pb.dtb', + '0bc0c0b0858cefd3c32b385c0d66d97142ded29472a496f4f490e42fc7615b25') + + def test_armv5(self): + self.set_machine('versatilepb') + self.cpu='arm926' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARMV5_KERNEL, + rootfs_asset=self.ASSET_ARMV5_ROOTFS, + dtb_asset=self.ASSET_ARMV5_DTB, + drive="virtio-blk-pci") + + ASSET_ARMV7_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/armv7/zImage', + '4c7a22e9f15875bec06bd2a29d822496571eb297d4f22694099ffcdb19077572') + ASSET_ARMV7_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/armv7/rootfs.ext4.zst', + 'ab1fbbeaddda1ffdd45c9405a28cd5370c20f23a7cbc809cc90dc9f243a8eb5a') + + def test_armv7(self): + self.set_machine('virt') + self.cpu='cortex-a15' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARMV7_KERNEL, + rootfs_asset=self.ASSET_ARMV7_ROOTFS) + + ASSET_ARMV7BE_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/armv7be/zImage', + '7facc62082b57af12015b08f7fdbaf2f123ba07a478367853ae12b219afc9f2f') + ASSET_ARMV7BE_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/armv7be/rootfs.ext4.zst', + '42ed46dd2d59986206c5b1f6cf35eab58fe3fd20c96b41aaa16b32f3f90a9835') + + def test_armv7be(self): + self.set_machine('virt') + self.cpu='cortex-a15' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARMV7BE_KERNEL, + rootfs_asset=self.ASSET_ARMV7BE_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/test_arm_vexpress.py new file mode 100755 index 0000000000..6bd6290030 --- /dev/null +++ b/tests/functional/test_arm_vexpress.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an versatile express machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class VExpressTest(LinuxKernelTest): + + ASSET_DAY16 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day16.tar.xz', + '63311adb2d4c4e7a73214a86d29988add87266a909719c56acfadd026b4110a7') + + def test_arm_vexpressa9(self): + self.set_machine('vexpress-a9') + file_path = self.ASSET_DAY16.fetch() + archive_extract(file_path, self.workdir) + self.launch_kernel(self.workdir + '/day16/winter.zImage', + dtb=self.workdir + '/day16/vexpress-v2p-ca9.dtb', + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_avr_mega2560.py b/tests/functional/test_avr_mega2560.py new file mode 100755 index 0000000000..8e47b4200b --- /dev/null +++ b/tests/functional/test_avr_mega2560.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# QEMU AVR integration tests +# +# Copyright (c) 2019-2020 Michael Rolnik +# +# 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 time + +from qemu_test import QemuSystemTest, Asset + +class AVR6Machine(QemuSystemTest): + timeout = 5 + + ASSET_ROM = Asset(('https://github.com/seharris/qemu-avr-tests' + '/raw/36c3e67b8755dcf/free-rtos/Demo' + '/AVR_ATMega2560_GCC/demo.elf'), + 'ee4833bd65fc69e84a79ed1c608affddbd499a60e63acf87d9113618401904e4') + + def test_freertos(self): + """ + https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf + constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX' + """ + rom_path = self.ASSET_ROM.fetch() + + self.set_machine('arduino-mega-2560-v3') + self.vm.add_args('-bios', rom_path) + self.vm.add_args('-nographic') + self.vm.launch() + + time.sleep(2) + self.vm.shutdown() + + self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX', + self.vm.get_log()) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_cpu_queries.py b/tests/functional/test_cpu_queries.py new file mode 100755 index 0000000000..b1122a0e8f --- /dev/null +++ b/tests/functional/test_cpu_queries.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Sanity check of query-cpu-* results +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# 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 qemu_test import QemuSystemTest + +class QueryCPUModelExpansion(QemuSystemTest): + """ + Run query-cpu-model-expansion for each CPU model, and validate results + """ + + def test(self): + self.set_machine('none') + self.vm.add_args('-S') + self.vm.launch() + + 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.cmd('query-cpu-model-expansion', model=model, + type='full') + self.assertEqual(e['model']['name'], c['name']) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_empty_cpu_model.py b/tests/functional/test_empty_cpu_model.py new file mode 100755 index 0000000000..0081b06d85 --- /dev/null +++ b/tests/functional/test_empty_cpu_model.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# +# Check for crash when using empty -cpu option +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# 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 qemu_test import QemuSystemTest + +class EmptyCPUModel(QemuSystemTest): + def test(self): + self.vm.add_args('-S', '-display', 'none', '-machine', 'none', '-cpu', '') + 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'-cpu option cannot be empty') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/test_i386_tuxrun.py new file mode 100755 index 0000000000..c593ffbe8c --- /dev/null +++ b/tests/functional/test_i386_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunI386Test(TuxRunBaselineTest): + + ASSET_I386_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/i386/bzImage', + 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956') + ASSET_I386_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/i386/rootfs.ext4.zst', + 'f15e66b2bf673a210ec2a4b2e744a80530b36289e04f5388aab812b97f69754a') + + def test_i386(self): + self.set_machine('q35') + self.cpu="coreduo" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_I386_KERNEL, + rootfs_asset=self.ASSET_I386_ROOTFS, + drive="virtio-blk-pci") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_info_usernet.py b/tests/functional/test_info_usernet.py new file mode 100755 index 0000000000..cd37524d94 --- /dev/null +++ b/tests/functional/test_info_usernet.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Test for the hmp command "info usernet" +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import QemuSystemTest + +from qemu.utils import get_info_usernet_hostfwd_port + + +class InfoUsernet(QemuSystemTest): + + def test_hostfwd(self): + self.require_netdev('user') + self.set_machine('none') + self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22') + self.vm.launch() + 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 ' + 'contain the redirected port')) + self.assertGreater(port, 0, + ('Found a redirected port that is not greater than' + ' zero')) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/test_linux_initrd.py new file mode 100755 index 0000000000..c71a59d4c9 --- /dev/null +++ b/tests/functional/test_linux_initrd.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# +# Linux initrd integration test. +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Wainer dos Santos Moschetta +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import logging +import tempfile + +from qemu_test import QemuSystemTest, Asset +from unittest import skipUnless + + +class LinuxInitrd(QemuSystemTest): + """ + Checks QEMU evaluates correctly the initrd file passed as -initrd option. + """ + + timeout = 300 + + ASSET_F18_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz'), + '1a27cb42559ce29237ac186699d063556ad69c8349d732bb1bd8d614e5a8cc2e') + + ASSET_F28_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/28/Everything/x86_64/os/images/pxeboot/vmlinuz'), + 'd05909c9d4a742a6fcc84dcc0361009e4611769619cc187a07107579a035f24e') + + def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self): + """ + Pretends to boot QEMU with an initrd file with size of 2GiB + and expect it exits with error message. + Fedora-18 shipped with linux-3.6 which have not supported xloadflags + cannot support more than 2GiB initrd. + """ + self.set_machine('pc') + kernel_path = self.ASSET_F18_KERNEL.fetch() + max_size = 2 * (1024 ** 3) - 1 + + with tempfile.NamedTemporaryFile() as initrd: + initrd.seek(max_size) + initrd.write(b'\0') + initrd.flush() + self.vm.add_args('-kernel', kernel_path, '-initrd', initrd.name, + '-m', '4096') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1) + expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % ( + max_size + 1) + self.assertRegex(self.vm.get_log(), expected_msg) + + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_with_2gib_file_should_work_with_linux_v4_16(self): + """ + QEMU has supported up to 4 GiB initrd for recent kernel + Expect guest can reach 'Unpacking initramfs...' + """ + self.set_machine('pc') + kernel_path = self.ASSET_F28_KERNEL.fetch() + max_size = 2 * (1024 ** 3) + 1 + + with tempfile.NamedTemporaryFile() as initrd: + initrd.seek(max_size) + initrd.write(b'\0') + initrd.flush() + + self.vm.set_console() + kernel_command_line = 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-initrd', initrd.name, + '-m', '5120') + self.vm.launch() + console = self.vm.console_socket.makefile() + console_logger = logging.getLogger('console') + while True: + msg = console.readline() + console_logger.debug(msg.strip()) + if 'Unpacking initramfs...' in msg: + break + if 'Kernel panic - not syncing' in msg: + self.fail("Kernel panic reached") + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_loongarch64_virt.py b/tests/functional/test_loongarch64_virt.py new file mode 100755 index 0000000000..b7d9abf933 --- /dev/null +++ b/tests/functional/test_loongarch64_virt.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# LoongArch virt test. +# +# Copyright (c) 2023 Loongson Technology Corporation Limited +# + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + +class LoongArchMachine(QemuSystemTest): + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + timeout = 120 + + ASSET_KERNEL = Asset( + ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/2024-11-26/vmlinuz.efi'), + '08b88a45f48a5fd92260bae895be4e5175be2397481a6f7821b9f39b2965b79e') + ASSET_INITRD = Asset( + ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/2024-11-26/ramdisk'), + '03d6fb6f8ee64ecac961120a0bdacf741f17b3bee2141f17fa01908c8baf176a') + ASSET_BIOS = Asset( + ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/2024-11-26/QEMU_EFI.fd'), + 'f55fbf5d92e885844631ae9bfa8887f659bbb4f6ef2beea9e9ff8bc0603b6697') + + 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): + + self.set_machine('virt') + + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + bios_path = self.ASSET_BIOS.fetch() + + 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') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/test_m68k_mcf5208evb.py new file mode 100755 index 0000000000..00c59590c3 --- /dev/null +++ b/tests/functional/test_m68k_mcf5208evb.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an MCF5208EVB machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class Mcf5208EvbTest(LinuxKernelTest): + + ASSET_DAY07 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', + '753c2f3837126b7c6ba92d0b1e0b156e8a2c5131d2d576bb0b9a763fae73c08a') + + def test_m68k_mcf5208evb(self): + self.set_machine('mcf5208evb') + file_path = self.ASSET_DAY07.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day07/sanity-clause.elf') + self.vm.launch() + self.wait_for_console_pattern('QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py new file mode 100755 index 0000000000..0124622c40 --- /dev/null +++ b/tests/functional/test_m68k_nextcube.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a VM and run OCR on the framebuffer +# +# Copyright (c) 2019 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. + +import os +import time + +from qemu_test import QemuSystemTest, Asset +from unittest import skipUnless + +from qemu_test.tesseract import tesseract_available, tesseract_ocr + +PIL_AVAILABLE = True +try: + from PIL import Image +except ImportError: + PIL_AVAILABLE = False + + +class NextCubeMachine(QemuSystemTest): + + timeout = 15 + + ASSET_ROM = Asset(('https://sourceforge.net/p/previous/code/1350/tree/' + 'trunk/src/Rev_2.5_v66.BIN?format=raw'), + '1b753890b67095b73e104c939ddf62eca9e7d0aedde5108e3893b0ed9d8000a4') + + def check_bootrom_framebuffer(self, screenshot_path): + rom_path = self.ASSET_ROM.fetch() + + self.vm.add_args('-bios', rom_path) + self.vm.launch() + + self.log.info('VM launched, waiting for display') + # TODO: wait for the 'displaysurface_create 1120x832' trace-event. + time.sleep(2) + + 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): + self.set_machine('next-cube') + screenshot_path = os.path.join(self.workdir, "dump.ppm") + self.check_bootrom_framebuffer(screenshot_path) + + width, height = Image.open(screenshot_path).size + self.assertEqual(width, 1120) + self.assertEqual(height, 832) + + # 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 OCR tool not available') + def test_bootrom_framebuffer_ocr_with_tesseract(self): + self.set_machine('next-cube') + screenshot_path = os.path.join(self.workdir, "dump.ppm") + self.check_bootrom_framebuffer(screenshot_path) + lines = tesseract_ocr(screenshot_path) + text = '\n'.join(lines) + self.assertIn('Testing the FPU', text) + self.assertIn('System test failed. Error code', text) + self.assertIn('Boot command', text) + self.assertIn('Next>', text) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/test_m68k_q800.py new file mode 100755 index 0000000000..3b17244b98 --- /dev/null +++ b/tests/functional/test_m68k_q800.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Functional test for testing the q800 m68k machine +# +# 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 qemu_test import LinuxKernelTest, Asset + +class Q800MachineTest(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://snapshot.debian.org/' + 'archive/debian-ports/20191021T083923Z/pool-m68k/main/l/linux/' + 'kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb'), + '949e50d74d4b9bc15d26c06d402717b7a4c0e32ff8100014f5930d8024de7b73') + + def test_m68k_q800(self): + self.set_machine('q800') + + deb_path = self.ASSET_KERNEL.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinux-5.3.0-1-m68k') + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + console_pattern = 'No filesystem could mount root' + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/test_mem_addr_space.py new file mode 100755 index 0000000000..bb0cf062ca --- /dev/null +++ b/tests/functional/test_mem_addr_space.py @@ -0,0 +1,314 @@ +#!/usr/bin/env python3 +# +# 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 qemu_test import QemuSystemTest +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): + """ + 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 machines. 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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): + """ + 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') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py new file mode 100755 index 0000000000..4f692ffdb1 --- /dev/null +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a microblaze Linux kernel and checks the console +# +# Copyright (c) 2018, 2021 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. + +import time +from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class MicroblazeMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day17.tar.xz'), + '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + + def test_microblaze_s3adsp1800(self): + self.set_machine('petalogix-s3adsp1800') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin') + self.vm.launch() + wait_for_console_pattern(self, 'This architecture does not have ' + 'kernel memory protection') + # Note: + # 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... + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py new file mode 100755 index 0000000000..faa3927f2e --- /dev/null +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a microblaze Linux kernel and checks the console +# +# Copyright (c) 2018, 2021 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. + +import time +from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class MicroblazeelMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), + 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') + + def test_microblazeel_s3adsp1800(self): + self.require_netdev('user') + self.set_machine('petalogix-s3adsp1800') + file_path = self.ASSET_IMAGE.fetch() + 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') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/test_mips64_tuxrun.py new file mode 100755 index 0000000000..54af1ae794 --- /dev/null +++ b/tests/functional/test_mips64_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMips64Test(TuxRunBaselineTest): + + ASSET_MIPS64_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/mips64/vmlinux', + '09010e51e4b8bcbbd2494786ffb48eca78f228e96e5c5438344b0eac4029dc61') + ASSET_MIPS64_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/mips64/rootfs.ext4.zst', + '69d91eeb04df3d8d172922c6993bb37d4deeb6496def75d8580f6f9de3e431da') + + def test_mips64(self): + self.set_machine('malta') + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPS64_KERNEL, + rootfs_asset=self.ASSET_MIPS64_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/test_mips64el_fuloong2e.py new file mode 100755 index 0000000000..a32d5f9d08 --- /dev/null +++ b/tests/functional/test_mips64el_fuloong2e.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Functional tests for the Lemote Fuloong-2E machine. +# +# Copyright (c) 2019 Philippe Mathieu-Daudé +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import subprocess + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import wait_for_console_pattern +from unittest import skipUnless + +class MipsFuloong2e(LinuxKernelTest): + + timeout = 60 + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/pool/main/l/linux/' + 'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb'), + '2a70f15b397f4ced632b0c15cb22660394190644146d804d60a4796eefbe1f50') + + def test_linux_kernel_3_16(self): + deb_path = self.ASSET_KERNEL.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinux-3.16.0-6-loongson-2e') + + self.set_machine('fuloong2e') + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUnless(os.getenv('RESCUE_YL_PATH'), 'RESCUE_YL_PATH not available') + def test_linux_kernel_2_6_27_isa_serial(self): + # Recovery system for the Yeeloong laptop + # (enough to test the fuloong2e southbridge, accessing its ISA bus) + # http://dev.lemote.com/files/resource/download/rescue/rescue-yl + sha = 'ab588d3316777c62cc81baa20ac92e98b01955c244dff3794b711bc34e26e51d' + kernel_path = os.getenv('RESCUE_YL_PATH') + output = subprocess.check_output(['sha256sum', kernel_path]) + checksum = output.split()[0] + assert checksum.decode("utf-8") == sha + + self.set_machine('fuloong2e') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + wait_for_console_pattern(self, 'Linux version 2.6.27.7lemote') + cpu_revision = 'CPU revision is: 00006302 (ICT Loongson-2)' + wait_for_console_pattern(self, cpu_revision) + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/test_mips64el_loongson3v.py new file mode 100755 index 0000000000..55d62928c7 --- /dev/null +++ b/tests/functional/test_mips64el_loongson3v.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# Functional tests for the Generic Loongson-3 Platform. +# +# Copyright (c) 2021 Jiaxun Yang +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time + +from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class MipsLoongson3v(QemuSystemTest): + timeout = 60 + + ASSET_PMON = Asset( + ('https://github.com/loongson-community/pmon/' + 'releases/download/20210112/pmon-3avirt.bin'), + 'fcdf6bb2cb7885a4a62f31fcb0d5e368bac7b6cea28f40c6dfa678af22fea20a') + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_pmon_serial_console(self): + self.set_machine('loongson3-virt') + + pmon_path = self.ASSET_PMON.fetch() + + self.vm.set_console() + self.vm.add_args('-bios', pmon_path) + self.vm.launch() + wait_for_console_pattern(self, 'CPU GODSON3 BogoMIPS:') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py new file mode 100755 index 0000000000..6d1195d362 --- /dev/null +++ b/tests/functional/test_mips64el_malta.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# +# Functional tests for the little-endian 64-bit MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import logging + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import gzip_uncompress +from unittest import skipUnless + +NUMPY_AVAILABLE = True +try: + import numpy as np +except ImportError: + NUMPY_AVAILABLE = False + +CV2_AVAILABLE = True +try: + import cv2 +except ImportError: + CV2_AVAILABLE = False + + +class MaltaMachineConsole(LinuxKernelTest): + + ASSET_KERNEL_2_63_2 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20130217T032700Z/pool/main/l/linux-2.6/' + 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb'), + '35eb476f03be589824b0310358f1c447d85e645b88cbcd2ac02b97ef560f9f8d') + + def test_mips64el_malta(self): + """ + This test requires the ar tool to extract "data.tar.gz" from + the Debian package. + + The kernel can be rebuilt using this Debian kernel source [1] and + following the instructions on [2]. + + [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/ + #linux-source-2.6.32_2.6.32-48 + [2] https://kernel-team.pages.debian.net/kernel-handbook/ + ch-common-tasks.html#s-common-official + """ + deb_path = self.ASSET_KERNEL_2_63_2.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinux-2.6.32-5-5kc-malta') + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + ASSET_KERNEL_3_19_3 = Asset( + ('https://github.com/philmd/qemu-testing-blob/' + 'raw/9ad2df38/mips/malta/mips64el/' + 'vmlinux-3.19.3.mtoman.20150408'), + '8d3beb003bc66051ead98e7172139017fcf9ce2172576541c57e86418dfa5ab8') + + ASSET_CPIO_R1 = Asset( + ('https://github.com/groeck/linux-build-test/' + 'raw/8584a59e/rootfs/mipsel64/' + 'rootfs.mipsel64r1.cpio.gz'), + '75ba10cd35fb44e32948eeb26974f061b703c81c4ba2fab1ebcacf1d1bec3b61') + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_mips64el_malta_5KEc_cpio(self): + kernel_path = self.ASSET_KERNEL_3_19_3.fetch() + initrd_path_gz = self.ASSET_CPIO_R1.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(initrd_path_gz, initrd_path) + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + + 'rdinit=/sbin/init noreboot') + self.vm.add_args('-cpu', '5KEc', + '-kernel', kernel_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', + 'MIPS 5KE') + exec_command_and_wait_for_pattern(self, 'uname -a', + '3.19.3.mtoman.20150408') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + +@skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') +@skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') +class MaltaMachineFramebuffer(LinuxKernelTest): + + timeout = 30 + + ASSET_KERNEL_4_7_0 = Asset( + ('https://github.com/philmd/qemu-testing-blob/raw/a5966ca4b5/' + 'mips/malta/mips64el/vmlinux-4.7.0-rc1.I6400.gz'), + '1f64efc59968a3c328672e6b10213fe574bb2308d9d2ed44e75e40be59e9fbc2') + + ASSET_TUXLOGO = Asset( + ('https://github.com/torvalds/linux/raw/v2.6.12/' + 'drivers/video/logo/logo_linux_vga16.ppm'), + 'b762f0d91ec018887ad1b334543c2fdf9be9fdfc87672b409211efaa3ea0ef79') + + def do_test_i6400_framebuffer_logo(self, cpu_cores_count): + """ + Boot Linux kernel and check Tux logo is displayed on the framebuffer. + """ + screendump_path = os.path.join(self.workdir, 'screendump.pbm') + + kernel_path_gz = self.ASSET_KERNEL_4_7_0.fetch() + kernel_path = self.workdir + "/vmlinux" + gzip_uncompress(kernel_path_gz, kernel_path) + + tuxlogo_path = self.ASSET_TUXLOGO.fetch() + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'clocksource=GIC console=tty0 console=ttyS0') + self.vm.add_args('-kernel', kernel_path, + '-cpu', 'I6400', + '-smp', '%u' % cpu_cores_count, + '-vga', 'std', + '-append', kernel_command_line) + self.vm.launch() + framebuffer_ready = 'Console: switching to colour frame buffer device' + self.wait_for_console_pattern(framebuffer_ready) + 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 + screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR) + tuxlogo_bgr = cv2.imread(tuxlogo_path, cv2.IMREAD_COLOR) + result = cv2.matchTemplate(screendump_bgr, tuxlogo_bgr, + cv2.TM_CCOEFF_NORMED) + loc = np.where(result >= match_threshold) + tuxlogo_count = 0 + h, w = tuxlogo_bgr.shape[:2] + debug_png = os.getenv('QEMU_TEST_CV2_SCREENDUMP_PNG_PATH') + for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1): + logger.debug('found Tux at position (x, y) = %s', pt) + cv2.rectangle(screendump_bgr, pt, + (pt[0] + w, pt[1] + h), (0, 0, 255), 2) + if debug_png: + cv2.imwrite(debug_png, screendump_bgr) + self.assertGreaterEqual(tuxlogo_count, cpu_cores_count) + + def test_mips_malta_i6400_framebuffer_logo_1core(self): + self.do_test_i6400_framebuffer_logo(1) + + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_mips_malta_i6400_framebuffer_logo_7cores(self): + self.do_test_i6400_framebuffer_logo(7) + + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test_mips_malta_i6400_framebuffer_logo_8cores(self): + self.do_test_i6400_framebuffer_logo(8) + + +from test_mipsel_malta import MaltaMachineYAMON + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_tuxrun.py b/tests/functional/test_mips64el_tuxrun.py new file mode 100755 index 0000000000..819549a27b --- /dev/null +++ b/tests/functional/test_mips64el_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMips64ELTest(TuxRunBaselineTest): + + ASSET_MIPS64EL_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/mips64el/vmlinux', + 'd4e08965e2155c4cccce7c5f34d18fe34c636cda2f2c9844387d614950155266') + ASSET_MIPS64EL_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/mips64el/rootfs.ext4.zst', + 'fba585368f5915b1498ed081863474b2d7ec4e97cdd46d21bdcb2f9698f83de4') + + def test_mips64el(self): + self.set_machine('malta') + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPS64EL_KERNEL, + rootfs_asset=self.ASSET_MIPS64EL_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py new file mode 100755 index 0000000000..a012081382 --- /dev/null +++ b/tests/functional/test_mips_malta.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# +# Functional tests for the little-endian 32-bit MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import gzip_uncompress + + +class MaltaMachineConsole(LinuxKernelTest): + + ASSET_KERNEL_2_63_2 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20130217T032700Z/pool/main/l/linux-2.6/' + 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb'), + '16ca524148afb0626f483163e5edf352bc1ab0e4fc7b9f9d473252762f2c7a43') + + def test_mips_malta(self): + deb_path = self.ASSET_KERNEL_2_63_2.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinux-2.6.32-5-4kc-malta') + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + ASSET_KERNEL_4_5_0 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20160601T041800Z/pool/main/l/linux/' + 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb'), + '526b17d5889840888b76fc2c36a0ebde182c9b1410a3a1e68203c3b160eb2027') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/' + 'mips/rootfs.cpio.gz'), + 'dcfe3a7fe3200da3a00d176b95caaa086495eb158f2bff64afc67d7e1eb2cddc') + + def test_mips_malta_cpio(self): + deb_path = self.ASSET_KERNEL_4_5_0.fetch() + kernel_path = self.extract_from_deb(deb_path, + '/boot/vmlinux-4.5.0-2-4kc-malta') + initrd_path_gz = self.ASSET_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'rootfs.cpio') + gzip_uncompress(initrd_path_gz, initrd_path) + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + + 'rdinit=/sbin/init noreboot') + self.vm.add_args('-kernel', kernel_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', + 'BogoMIPS') + exec_command_and_wait_for_pattern(self, 'uname -a', + 'Debian') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips_tuxrun.py b/tests/functional/test_mips_tuxrun.py new file mode 100755 index 0000000000..6fec44c2bf --- /dev/null +++ b/tests/functional/test_mips_tuxrun.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMipsTest(TuxRunBaselineTest): + + ASSET_MIPS_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/mips32/vmlinux', + 'bfd2172f8b17fb32970ca0c8c58f59c5a4ca38aa5855d920be3a69b5d16e52f0') + ASSET_MIPS_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/mips32/rootfs.ext4.zst', + 'fc3da0b4c2f38d74c6d705123bb0f633c76ed953128f9d0859378c328a6d11a0') + + def test_mips32(self): + self.set_machine('malta') + self.cpu="mips32r6-generic" + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPS_KERNEL, + rootfs_asset=self.ASSET_MIPS_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/test_mipsel_malta.py new file mode 100755 index 0000000000..b8dfddd856 --- /dev/null +++ b/tests/functional/test_mipsel_malta.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# +# Functional tests for the little-endian 32-bit MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, LinuxKernelTest, Asset +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import lzma_uncompress +from zipfile import ZipFile + + +class MaltaMachineConsole(LinuxKernelTest): + + ASSET_KERNEL_4K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page4k.xz'), + '019e034094ac6cf3aa77df5e130fb023ce4dbc804b04bfcc560c6403e1ae6bdb') + ASSET_KERNEL_16K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page16k_up.xz'), + '3a54a10b3108c16a448dca9ea3db378733a27423befc2a45a5bdf990bd85e12c') + ASSET_KERNEL_64K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page64k_dbg.xz'), + 'ce21ff4b07a981ecb8a39db2876616f5a2473eb2ab459c6f67465b9914b0c6b6') + + def do_test_mips_malta32el_nanomips(self, kernel_path_xz): + kernel_path = os.path.join(self.workdir, 'kernel') + lzma_uncompress(kernel_path_xz, kernel_path) + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'mem=256m@@0x0 ' + + 'console=ttyS0') + self.vm.add_args('-cpu', 'I7200', + '-no-reboot', + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + def test_mips_malta32el_nanomips_4k(self): + kernel_path_xz = self.ASSET_KERNEL_4K.fetch() + self.do_test_mips_malta32el_nanomips(kernel_path_xz) + + def test_mips_malta32el_nanomips_16k_up(self): + kernel_path_xz = self.ASSET_KERNEL_16K.fetch() + self.do_test_mips_malta32el_nanomips(kernel_path_xz) + + def test_mips_malta32el_nanomips_64k_dbg(self): + kernel_path_xz = self.ASSET_KERNEL_16K.fetch() + self.do_test_mips_malta32el_nanomips(kernel_path_xz) + + +class MaltaMachineYAMON(QemuSystemTest): + + ASSET_YAMON_ROM = Asset( + ('https://s3-eu-west-1.amazonaws.com/downloads-mips/mips-downloads/' + 'YAMON/yamon-bin-02.22.zip'), + 'eef86f0eed0ef554f041dcd47b87eebea0e6f9f1184ed31f7e9e8b4a803860ab') + + def test_mipsel_malta_yamon(self): + yamon_bin = 'yamon-02.22.bin' + zip_path = self.ASSET_YAMON_ROM.fetch() + with ZipFile(zip_path, 'r') as zf: + zf.extract(yamon_bin, path=self.workdir) + yamon_path = os.path.join(self.workdir, yamon_bin) + + self.set_machine('malta') + 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() + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mipsel_tuxrun.py b/tests/functional/test_mipsel_tuxrun.py new file mode 100755 index 0000000000..2965bbd913 --- /dev/null +++ b/tests/functional/test_mipsel_tuxrun.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMipsELTest(TuxRunBaselineTest): + + ASSET_MIPSEL_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/mips32el/vmlinux', + '8573867c68a8443db8de6d08bb33fb291c189ca2ca671471d3973a3e712096a3') + ASSET_MIPSEL_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/mips32el/rootfs.ext4.zst', + 'e799768e289fd69209c21f4dacffa11baea7543d5db101e8ce27e3bc2c41d90e') + + def test_mips32el(self): + self.set_machine('malta') + self.cpu="mips32r6-generic" + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPSEL_KERNEL, + rootfs_asset=self.ASSET_MIPSEL_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_multiprocess.py b/tests/functional/test_multiprocess.py new file mode 100755 index 0000000000..751cf10e63 --- /dev/null +++ b/tests/functional/test_multiprocess.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# +# Test for multiprocess qemu +# +# 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 socket + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import exec_command, exec_command_and_wait_for_pattern + +class Multiprocess(QemuSystemTest): + + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + ASSET_KERNEL_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/initrd.img'), + '3b6cb5c91a14c42e2f61520f1689264d865e772a1f0069e660a800d31dd61fb9') + + ASSET_KERNEL_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/initrd.img'), + '9fd230cab10b1dafea41cf00150e6669d37051fad133bd618d2130284e16d526') + + def do_test(self, kernel_asset, initrd_asset, + kernel_command_line, machine_type): + """Main test method""" + self.require_accelerator('kvm') + self.require_device('x-pci-proxy-dev') + + # Create socketpair to connect proxy and remote processes + proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, + socket.SOCK_STREAM) + os.set_inheritable(proxy_sock.fileno(), True) + os.set_inheritable(remote_sock.fileno(), True) + + kernel_path = kernel_asset.fetch() + initrd_path = initrd_asset.fetch() + + # Create remote process + remote_vm = self.get_vm() + remote_vm.add_args('-machine', 'x-remote') + remote_vm.add_args('-nodefaults') + remote_vm.add_args('-device', 'lsi53c895a,id=lsi1') + remote_vm.add_args('-object', 'x-remote-object,id=robj1,' + 'devid=lsi1,fd='+str(remote_sock.fileno())) + remote_vm.launch() + + # Create proxy process + self.vm.set_console() + self.vm.add_args('-machine', machine_type) + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-cpu', 'host') + self.vm.add_args('-object', + 'memory-backend-memfd,id=sysmem-file,size=2G') + self.vm.add_args('--numa', 'node,memdev=sysmem-file') + self.vm.add_args('-m', '2048') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line) + self.vm.add_args('-device', + 'x-pci-proxy-dev,' + 'id=lsi1,fd='+str(proxy_sock.fileno())) + self.vm.launch() + wait_for_console_pattern(self, 'as init process', + 'Kernel panic - not syncing') + exec_command(self, 'mount -t sysfs sysfs /sys') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/pci/devices/*/uevent', + 'PCI_ID=1000:0012') + + def test_multiprocess(self): + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + if self.arch == 'x86_64': + kernel_command_line += 'console=ttyS0 rdinit=/bin/bash' + self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, + kernel_command_line, 'pc') + elif self.arch == 'aarch64': + kernel_command_line += 'rdinit=/bin/bash console=ttyAMA0' + self.do_test(self.ASSET_KERNEL_AARCH64, self.ASSET_INITRD_AARCH64, + kernel_command_line, 'virt,gic-version=3') + else: + assert False + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_netdev_ethtool.py b/tests/functional/test_netdev_ethtool.py new file mode 100755 index 0000000000..ee1a397bd2 --- /dev/null +++ b/tests/functional/test_netdev_ethtool.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# +# 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-later + +from unittest import skip +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class NetDevEthtool(QemuSystemTest): + + # 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. + ASSET_BASEURL = ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/' + 'download?path=%2Fnetdev-ethtool&files=') + ASSET_BZIMAGE = Asset( + ASSET_BASEURL + "bzImage", + "ed62ee06ea620b1035747f3f66a5e9fc5d3096b29f75562ada888b04cd1c4baf") + ASSET_ROOTFS = Asset( + ASSET_BASEURL + "rootfs.squashfs", + "8f0207e3c4d40832ae73c1a927e42ca30ccb1e71f047acb6ddb161ba422934e6") + + def common_test_code(self, netdev, extra_args=None): + self.set_machine('q35') + + # This custom kernel has drivers for all the supported network + # devices we can emulate in QEMU + kernel = self.ASSET_BZIMAGE.fetch() + rootfs = self.ASSET_ROOTFS.fetch() + + 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): + self.common_test_code("igb") + + def test_igb_nomsi(self): + 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): + self.common_test_code("e1000") + + @skip("Incomplete reg 0x00178 support") + def test_i82550(self): + self.common_test_code("i82550") + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/test_or1k_sim.py new file mode 100755 index 0000000000..10e0437c50 --- /dev/null +++ b/tests/functional/test_or1k_sim.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an OpenRISC-1000 SIM machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class OpenRISC1kSimTest(LinuxKernelTest): + + ASSET_DAY20 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day20.tar.xz', + 'ff9d7dd7c6bdba325bd85ee85c02db61ff653e129558aeffe6aff55bffb6763a') + + def test_or1k_sim(self): + self.set_machine('or1k-sim') + file_path = self.ASSET_DAY20.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day20/vmlinux') + self.vm.launch() + self.wait_for_console_pattern('QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/test_pc_cpu_hotplug_props.py new file mode 100755 index 0000000000..9d5a37cb17 --- /dev/null +++ b/tests/functional/test_pc_cpu_hotplug_props.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Ensure CPU die-id can be omitted on -device +# +# Copyright (c) 2019 Red Hat Inc +# +# Author: +# Eduardo Habkost +# +# 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 . +# + +from qemu_test import QemuSystemTest + +class OmittedCPUProps(QemuSystemTest): + + def test_no_die_id(self): + self.vm.add_args('-nodefaults', '-S') + 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.assertEqual(len(self.vm.cmd('query-cpus-fast')), 2) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py new file mode 100755 index 0000000000..f1af92373e --- /dev/null +++ b/tests/functional/test_ppc64_e500.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Boot a Linux kernel on a e500 ppc64 machine and check the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class E500Test(LinuxKernelTest): + + ASSET_DAY19 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day19.tar.xz', + '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + + def test_ppc64_e500(self): + self.set_machine('ppce500') + self.cpu = 'e5500' + file_path = self.ASSET_DAY19.fetch() + archive_extract(file_path, self.workdir) + self.launch_kernel(self.workdir + '/day19/uImage', + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py new file mode 100755 index 0000000000..312248bbfe --- /dev/null +++ b/tests/functional/test_ppc64_hv.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# +# 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 unittest import skipIf, skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test 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_ALLOW_LARGE_STORAGE'), 'storage 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' + + ASSET_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/alpine/v3.18/' + 'releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso'), + 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff') + + 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.chmod(filename, 0o600) + 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() + + self.iso_path = self.ASSET_ISO.fetch() + self.vmlinuz = self.extract_from_iso(self.iso_path, '/boot/vmlinuz-lts') + self.initramfs = self.extract_from_iso(self.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,read-only=true") + + 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): + self.require_accelerator("tcg") + self.set_machine('pseries') + 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): + self.require_accelerator("kvm") + self.set_machine('pseries') + 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): + self.require_accelerator("tcg") + self.set_machine('powernv') + 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() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc64_powernv.py b/tests/functional/test_ppc64_powernv.py new file mode 100755 index 0000000000..685e2178ed --- /dev/null +++ b/tests/functional/test_ppc64_powernv.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +# +# 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 qemu_test import LinuxKernelTest, Asset +from qemu_test import wait_for_console_pattern + +class powernvMachine(LinuxKernelTest): + + timeout = 90 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' + panic_message = 'Kernel panic - not syncing' + good_message = 'VFS: Cannot open root device' + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + + def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE): + self.require_accelerator("tcg") + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-append', command_line) + self.vm.launch() + + def test_linux_boot(self): + self.set_machine('powernv') + 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): + self.set_machine('powernv') + 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): + self.set_machine('powernv') + 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): + self.set_machine('powernv') + 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): + self.set_machine('powernv') + 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) + + + ASSET_EPAPR_KERNEL = Asset( + ('https://github.com/open-power/op-build/releases/download/v2.7/' + 'zImage.epapr'), + '0ab237df661727e5392cee97460e8674057a883c5f74381a128fa772588d45cd') + + def do_test_ppc64_powernv(self, proc): + self.require_accelerator("tcg") + kernel_path = self.ASSET_EPAPR_KERNEL.fetch() + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-append', 'console=tty0 console=hvc0', + '-device', 'pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0', + '-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234', + '-device', 'e1000e,bus=bridge1,addr=0x3', + '-device', 'nec-usb-xhci,bus=bridge1,addr=0x2') + self.vm.launch() + + 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") + # Device detection output driven by udev probing is sometimes cut off + # from console output, suspect S14silence-console init script. + + def test_powernv8(self): + self.set_machine('powernv8') + self.do_test_ppc64_powernv('P8') + + def test_powernv9(self): + self.set_machine('powernv9') + self.do_test_ppc64_powernv('P9') + + def test_powernv10(self): + self.set_machine('powernv10') + self.do_test_ppc64_powernv('P10') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc64_pseries.py b/tests/functional/test_ppc64_pseries.py new file mode 100755 index 0000000000..fdc404ed03 --- /dev/null +++ b/tests/functional/test_ppc64_pseries.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# +# Test that Linux kernel boots on ppc 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 qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class pseriesMachine(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' + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + + def do_test_ppc64_linux_boot(self, kernel_command_line = KERNEL_COMMON_COMMAND_LINE): + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + + def test_ppc64_vof_linux_boot(self): + self.set_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): + self.set_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): + self.set_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): + self.set_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): + 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): + self.set_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) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/test_ppc64_tuxrun.py new file mode 100755 index 0000000000..03b47e07f2 --- /dev/null +++ b/tests/functional/test_ppc64_tuxrun.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# +# 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 tempfile + +from qemu_test import run_cmd, Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunPPC64Test(TuxRunBaselineTest): + + def ppc64_common_tuxrun(self, kernel_asset, rootfs_asset, prefix): + self.set_machine('pseries') + self.cpu='POWER10' + self.console='hvc0' + self.root='sda' + self.extradev='spapr-vscsi' + # 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: + run_cmd([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,bus=pci.0,' + 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1' + ',bootindex=2') + self.common_tuxrun(kernel_asset, rootfs_asset=rootfs_asset, + drive="scsi-hd") + + ASSET_PPC64_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/ppc64/vmlinux', + 'f22a9b9e924174a4c199f4c7e5d91a2339fcfe51c6eafd0907dc3e09b64ab728') + ASSET_PPC64_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/ppc64/rootfs.ext4.zst', + '1d953e81a4379e537fc8e41e05a0a59d9b453eef97aa03d47866c6c45b00bdff') + + def test_ppc64(self): + self.ppc64_common_tuxrun(kernel_asset=self.ASSET_PPC64_KERNEL, + rootfs_asset=self.ASSET_PPC64_ROOTFS, + prefix='tuxrun_ppc64_') + + ASSET_PPC64LE_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/ppc64le/vmlinux', + '979eb61b445a010fb13e2b927126991f8ceef9c590fa2be0996c00e293e80cf2') + ASSET_PPC64LE_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/ppc64le/rootfs.ext4.zst', + 'b442678c93fb8abe1f7d3bfa20556488de6b475c22c8fed363f42cf81a0a3906') + + def test_ppc64le(self): + self.ppc64_common_tuxrun(kernel_asset=self.ASSET_PPC64LE_KERNEL, + rootfs_asset=self.ASSET_PPC64LE_ROOTFS, + prefix='tuxrun_ppc64le_') + + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_ppc_405.py b/tests/functional/test_ppc_405.py new file mode 100755 index 0000000000..9851c03ee9 --- /dev/null +++ b/tests/functional/test_ppc_405.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Test that the U-Boot firmware boots on ppc 405 machines and check the console +# +# Copyright (c) 2021 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 qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +class Ppc405Machine(QemuSystemTest): + + timeout = 90 + + ASSET_UBOOT = Asset( + ('https://gitlab.com/huth/u-boot/-/raw/taihu-2021-10-09/' + 'u-boot-taihu.bin'), + 'a076bb6cdeaafa406330e51e074b66d8878d9036d67d4caa0137be03ee4c112c') + + def do_test_ppc405(self): + file_path = self.ASSET_UBOOT.fetch() + self.vm.set_console(console_index=1) + self.vm.add_args('-bios', file_path) + self.vm.launch() + wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') + exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') + + def test_ppc_ref405ep(self): + self.require_accelerator("tcg") + self.set_machine('ref405ep') + self.do_test_ppc405() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/test_ppc_40p.py new file mode 100755 index 0000000000..67bcdae53a --- /dev/null +++ b/tests/functional/test_ppc_40p.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a PReP/40p machine and checks its serial console. +# +# Copyright (c) Philippe Mathieu-Daudé +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + + +class IbmPrep40pMachine(QemuSystemTest): + + timeout = 60 + + ASSET_BIOS = Asset( + ('http://ftpmirror.your.org/pub/misc/' + 'ftp.software.ibm.com/rs6000/firmware/' + '7020-40p/P12H0456.IMG'), + 'd957f79c73f760d1455d2286fcd901ed6d06167320eb73511b478a939be25b3f') + ASSET_NETBSD40 = Asset( + ('https://archive.netbsd.org/pub/NetBSD-archive/' + 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs'), + 'f86236e9d01b3f0dd0f5d3b8d5bbd40c68e78b4db560a108358f5ad58e636619') + ASSET_NETBSD71 = Asset( + ('https://archive.netbsd.org/pub/NetBSD-archive/' + 'NetBSD-7.1.2/iso/NetBSD-7.1.2-prep.iso'), + 'cc7cb290b06aaa839362deb7bd9f417ac5015557db24088508330f76c3f825ec') + + # 12H0455 PPS Firmware Licensed Materials + # Property of IBM (C) Copyright IBM Corp. 1994. + # All rights reserved. + # U.S. Government Users Restricted Rights - Use, duplication or disclosure + # restricted by GSA ADP Schedule Contract with IBM Corp. + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_factory_firmware_and_netbsd(self): + self.set_machine('40p') + self.require_accelerator("tcg") + bios_path = self.ASSET_BIOS.fetch() + drive_path = self.ASSET_NETBSD40.fetch() + + self.vm.set_console() + self.vm.add_args('-bios', bios_path, + '-drive', + f"file={drive_path},format=raw,if=floppy,read-only=true") + self.vm.launch() + os_banner = 'NetBSD 4.0 (GENERIC) #0: Sun Dec 16 00:49:40 PST 2007' + wait_for_console_pattern(self, os_banner) + wait_for_console_pattern(self, 'Model: IBM PPS Model 6015') + + def test_openbios_192m(self): + self.set_machine('40p') + self.require_accelerator("tcg") + self.vm.set_console() + self.vm.add_args('-m', '192') # test fw_cfg + + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> Memory: 192M') + wait_for_console_pattern(self, '>> CPU type PowerPC,604') + + def test_openbios_and_netbsd(self): + self.set_machine('40p') + self.require_accelerator("tcg") + drive_path = self.ASSET_NETBSD71.fetch() + self.vm.set_console() + self.vm.add_args('-cdrom', drive_path, + '-boot', 'd') + + self.vm.launch() + wait_for_console_pattern(self, 'NetBSD/prep BOOT, Revision 1.9') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_74xx.py b/tests/functional/test_ppc_74xx.py new file mode 100755 index 0000000000..5386016f26 --- /dev/null +++ b/tests/functional/test_ppc_74xx.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# +# Smoke tests for 74xx cpus (aka G4). +# +# Copyright (c) 2021, 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. + +from qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern + +class ppc74xxCpu(QemuSystemTest): + + timeout = 5 + + def test_ppc_7400(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7400') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7410(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7410') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,74xx') + + def test_ppc_7441(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7441') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7445(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7445') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7447(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7447') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7447a(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7447a') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7448(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7448') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,MPC86xx') + + def test_ppc_7450(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7450') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7451(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7451') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7455(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7455') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7457(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7457') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + + def test_ppc_7457a(self): + self.require_accelerator("tcg") + self.set_machine('g3beige') + self.vm.set_console() + self.vm.add_args('-cpu', '7457a') + self.vm.launch() + wait_for_console_pattern(self, '>> OpenBIOS') + wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/test_ppc_amiga.py new file mode 100755 index 0000000000..b793b5c432 --- /dev/null +++ b/tests/functional/test_ppc_amiga.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# +# 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. + +import subprocess + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, run_cmd +from zipfile import ZipFile + +class AmigaOneMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('https://www.hyperion-entertainment.com/index.php/' + 'downloads?view=download&format=raw&file=25'), + '8ff39330ba47d4f64de4ee8fd6809e9c010a9ef17fe51e95c3c1d53437cb481f') + + def test_ppc_amigaone(self): + self.require_accelerator("tcg") + self.set_machine('amigaone') + tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' + zip_file = self.ASSET_IMAGE.fetch() + with ZipFile(zip_file, 'r') as zf: + zf.extractall(path=self.workdir) + bios_fh = open(self.workdir + "/u-boot-amigaone.bin", "wb") + subprocess.run(['tail', '-c', '524288', + self.workdir + "/floppy_edition/updater.image"], + stdout=bios_fh) + + self.vm.set_console() + self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.launch() + wait_for_console_pattern(self, 'FLASH:') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py new file mode 100755 index 0000000000..e72cbdee12 --- /dev/null +++ b/tests/functional/test_ppc_bamboo.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# +# Test that Linux kernel boots on the ppc bamboo board and check the console +# +# Copyright (c) 2021 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. + +from qemu_test.utils import archive_extract +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +class BambooMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('http://landley.net/aboriginal/downloads/binaries/' + 'system-image-powerpc-440fp.tar.gz'), + 'c12b58f841c775a0e6df4832a55afe6b74814d1565d08ddeafc1fb949a075c5e') + + def test_ppc_bamboo(self): + self.set_machine('bamboo') + self.require_accelerator("tcg") + self.require_netdev('user') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + + '/system-image-powerpc-440fp/linux', + '-initrd', self.workdir + + '/system-image-powerpc-440fp/rootfs.cpio.gz', + '-nic', 'user,model=rtl8139,restrict=on') + self.vm.launch() + wait_for_console_pattern(self, 'Type exit when done') + exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', + '10.0.2.2 is alive!') + exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/test_ppc_mac.py new file mode 100755 index 0000000000..3f45e37a45 --- /dev/null +++ b/tests/functional/test_ppc_mac.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Boot Linux kernel on a mac99 and g3beige ppc machine and check the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class MacTest(LinuxKernelTest): + + ASSET_DAY15 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day15.tar.xz', + '03e0757c131d2959decf293a3572d3b96c5a53587165bf05ce41b2818a2bccd5') + + def do_day15_test(self): + # mac99 also works with kvm_pr but we don't have a reliable way at + # the moment (e.g. by looking at /proc/modules) to detect whether + # we're running kvm_hv or kvm_pr. For now let's disable this test + # if we don't have TCG support. + self.require_accelerator("tcg") + + file_path = self.ASSET_DAY15.fetch() + archive_extract(file_path, self.workdir) + self.vm.add_args('-M', 'graphics=off') + self.launch_kernel(self.workdir + '/day15/invaders.elf', + wait_for='QEMU advent calendar') + + def test_ppc_g3beige(self): + self.set_machine('g3beige') + self.do_day15_test() + + def test_ppc_mac99(self): + self.set_machine('mac99') + self.do_day15_test() + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/test_ppc_mpc8544ds.py new file mode 100755 index 0000000000..2b3f0894ae --- /dev/null +++ b/tests/functional/test_ppc_mpc8544ds.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Test that Linux kernel boots on ppc 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 qemu_test.utils import archive_extract +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class Mpc8544dsMachine(QemuSystemTest): + + timeout = 90 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + panic_message = 'Kernel panic - not syncing' + + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day04.tar.xz'), + '88bc83f3c9f3d633bcfc108a6342d677abca247066a2fb8d4636744a0d319f94') + + def test_ppc_mpc8544ds(self): + self.require_accelerator("tcg") + self.set_machine('mpc8544ds') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir, member='creek/creek.bin') + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/creek/creek.bin') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU advent calendar 2020', + self.panic_message) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/test_ppc_tuxrun.py new file mode 100755 index 0000000000..50b76946c4 --- /dev/null +++ b/tests/functional/test_ppc_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunPPC32Test(TuxRunBaselineTest): + + ASSET_PPC32_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/ppc32/uImage', + '1a68f74b860fda022fb12e03c5efece8c2b8b590d96cca37a8481a3ae0b3f81f') + ASSET_PPC32_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/ppc32/rootfs.ext4.zst', + '8885b9d999cc24d679542a02e9b6aaf48f718f2050ece6b8347074b6ee41dd09') + + def test_ppc32(self): + self.set_machine('ppce500') + self.cpu='e500mc' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_PPC32_KERNEL, + rootfs_asset=self.ASSET_PPC32_ROOTFS, + drive="virtio-blk-pci") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/test_ppc_virtex_ml507.py new file mode 100755 index 0000000000..ffa9a0633e --- /dev/null +++ b/tests/functional/test_ppc_virtex_ml507.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# Test that Linux kernel boots on ppc 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 qemu_test.utils import archive_extract +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class VirtexMl507Machine(QemuSystemTest): + + timeout = 90 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + panic_message = 'Kernel panic - not syncing' + + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day08.tar.xz'), + 'cefe5b8aeb5e9d2d1d4fd22dcf48d917d68d5a765132bf2ddd6332dc393b824c') + + def test_ppc_virtex_ml507(self): + self.require_accelerator("tcg") + self.set_machine('virtex-ml507') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/hippo/hippo.linux', + '-dtb', self.workdir + '/hippo/virtex440-ml507.dtb', + '-m', '512') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU advent calendar 2020', + self.panic_message) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_riscv32_tuxrun.py b/tests/functional/test_riscv32_tuxrun.py new file mode 100755 index 0000000000..49b57cd428 --- /dev/null +++ b/tests/functional/test_riscv32_tuxrun.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunRiscV32Test(TuxRunBaselineTest): + + ASSET_RISCV32_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/riscv32/Image', + '89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5') + ASSET_RISCV32_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/riscv32/rootfs.ext4.zst', + '7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba') + + def test_riscv32(self): + self.set_machine('virt') + self.common_tuxrun(kernel_asset=self.ASSET_RISCV32_KERNEL, + rootfs_asset=self.ASSET_RISCV32_ROOTFS) + + def test_riscv32_maxcpu(self): + self.set_machine('virt') + self.cpu='max' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV32_KERNEL, + rootfs_asset=self.ASSET_RISCV32_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_riscv64_tuxrun.py b/tests/functional/test_riscv64_tuxrun.py new file mode 100755 index 0000000000..4e2449539c --- /dev/null +++ b/tests/functional/test_riscv64_tuxrun.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunRiscV64Test(TuxRunBaselineTest): + + ASSET_RISCV64_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/riscv64/Image', + 'cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e') + ASSET_RISCV64_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/riscv64/rootfs.ext4.zst', + 'b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb') + + ASSET_RISCV32_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/riscv32/Image', + '89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5') + ASSET_RISCV32_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/riscv32/rootfs.ext4.zst', + '7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba') + + def test_riscv64(self): + self.set_machine('virt') + self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL, + rootfs_asset=self.ASSET_RISCV64_ROOTFS) + + def test_riscv64_maxcpu(self): + self.set_machine('virt') + self.cpu='max' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL, + rootfs_asset=self.ASSET_RISCV64_ROOTFS) + + def test_riscv64_rv32(self): + self.set_machine('virt') + self.cpu='rv32' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV32_KERNEL, + rootfs_asset=self.ASSET_RISCV32_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_riscv_opensbi.py b/tests/functional/test_riscv_opensbi.py new file mode 100755 index 0000000000..d077e40f42 --- /dev/null +++ b/tests/functional/test_riscv_opensbi.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# 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 qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern + +class RiscvOpenSBI(QemuSystemTest): + + 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_riscv_spike(self): + self.set_machine('spike') + self.boot_opensbi() + + def test_riscv_sifive_u(self): + self.set_machine('sifive_u') + self.boot_opensbi() + + def test_riscv_virt(self): + self.set_machine('virt') + self.boot_opensbi() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py new file mode 100755 index 0000000000..5687f756bb --- /dev/null +++ b/tests/functional/test_rx_gdbsim.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import gzip_uncompress + + +class RxGdbSimMachine(QemuSystemTest): + + timeout = 30 + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + ASSET_UBOOT = Asset( + 'https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz', + '7146567d669e91dbac166384b29aeba1715beb844c8551e904b86831bfd9d046') + ASSET_DTB = Asset( + 'https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb', + 'aa278d9c1907a4501741d7ee57e7f65c02dd1b3e0323b33c6d4247f1b32cf29a') + ASSET_KERNEL = Asset( + 'http://acc.dl.osdn.jp/users/23/23845/zImage', + 'baa43205e74a7220ed8482188c5e9ce497226712abb7f4e7e4f825ce19ff9656') + + def test_uboot(self): + """ + U-Boot and checks that the console is operational. + """ + self.set_machine('gdbsim-r5f562n8') + + uboot_path_gz = self.ASSET_UBOOT.fetch() + uboot_path = os.path.join(self.workdir, 'u-boot.bin') + gzip_uncompress(uboot_path_gz, uboot_path) + + self.vm.set_console() + self.vm.add_args('-bios', uboot_path, + '-no-reboot') + self.vm.launch() + uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty' + wait_for_console_pattern(self, uboot_version) + gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)' + # FIXME limit baudrate on chardev, else we type too fast + #exec_command_and_wait_for_pattern(self, 'version', gcc_version) + + @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. + """ + self.set_machine('gdbsim-r5f562n7') + + dtb_path = self.ASSET_DTB.fetch() + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon' + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)', + failure_message='Kernel panic - not syncing') + exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/test_s390x_ccw_virtio.py new file mode 100755 index 0000000000..f7acd90a89 --- /dev/null +++ b/tests/functional/test_s390x_ccw_virtio.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +# +# Functional test that boots an s390x Linux guest with ccw and PCI devices +# attached and checks whether the devices are recognized by Linux +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Cornelia Huck +# +# 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 tempfile + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import lzma_uncompress + +class S390CCWVirtioMachine(QemuSystemTest): + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + timeout = 120 + + ASSET_BUSTER_KERNEL = Asset( + ('https://snapshot.debian.org/archive/debian/' + '20201126T092837Z/dists/buster/main/installer-s390x/' + '20190702+deb10u6/images/generic/kernel.debian'), + 'd411d17c39ae7ad38d27534376cbe88b68b403c325739364122c2e6f1537e818') + ASSET_BUSTER_INITRD = Asset( + ('https://snapshot.debian.org/archive/debian/' + '20201126T092837Z/dists/buster/main/installer-s390x/' + '20190702+deb10u6/images/generic/initrd.debian'), + '836bbd0fe6a5ca81274c28c2b063ea315ce1868660866e9b60180c575fef9fd5') + + ASSET_F31_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/31/Server/s390x/os' + '/images/kernel.img'), + '480859574f3f44caa6cd35c62d70e1ac0609134e22ce2a954bbed9b110c06e0b') + ASSET_F31_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/31/Server/s390x/os' + '/images/initrd.img'), + '04c46095b2c49020b1c2327158898b7db747e4892ae319726192fb949716aa9c') + + 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 wait_for_crw_reports(self): + exec_command_and_wait_for_pattern(self, + 'while ! (dmesg -c | grep CRW) ; do sleep 1 ; done', + 'CRW reports') + + dmesg_clear_count = 1 + def clear_guest_dmesg(self): + exec_command_and_wait_for_pattern(self, 'dmesg -c > /dev/null; ' + 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): + self.set_machine('s390-ccw-virtio') + + kernel_path = self.ASSET_BUSTER_KERNEL.fetch() + initrd_path = self.ASSET_BUSTER_INITRD.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=sclp0 root=/dev/ram0 BOOT_DEBUG=3') + self.vm.add_args('-nographic', + '-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', + '-device', + 'virtio-rng-ccw,devno=fe.3.1234,max_revision=2,id=rn2', + '-device', 'zpci,uid=5,target=zzz', + '-device', 'virtio-net-pci,id=zzz', + '-device', 'zpci,uid=0xa,fid=12,target=serial', + '-device', 'virtio-serial-pci,id=serial', + '-device', 'virtio-balloon-ccw') + self.vm.launch() + + shell_ready = "sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + # first debug shell is too early, we need to wait for device detection + exec_command_and_wait_for_pattern(self, 'exit', shell_ready) + + ccw_bus_ids="0.1.1111 0.2.0000 0.3.1234" + pci_bus_ids="0005:00:00.0 000a:00:00.0" + exec_command_and_wait_for_pattern(self, 'ls /sys/bus/ccw/devices/', + ccw_bus_ids) + exec_command_and_wait_for_pattern(self, 'ls /sys/bus/pci/devices/', + pci_bus_ids) + # check that the device at 0.2.0000 is in legacy mode, while the + # device at 0.3.1234 has the virtio-1 feature bit set + virtio_rng_features="00000000000000000000000000001100" + \ + "10000000000000000000000000000000" + virtio_rng_features_legacy="00000000000000000000000000001100" + \ + "00000000000000000000000000000000" + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/ccw/devices/0.2.0000/virtio?/features', + virtio_rng_features_legacy) + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/ccw/devices/0.3.1234/virtio?/features', + virtio_rng_features) + # check that /dev/hwrng works - and that it's gone after ejecting + exec_command_and_wait_for_pattern(self, + 'dd if=/dev/hwrng of=/dev/null bs=1k count=10', + '10+0 records out') + self.clear_guest_dmesg() + self.vm.cmd('device_del', id='rn1') + self.wait_for_crw_reports() + self.clear_guest_dmesg() + 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', + 'dd: /dev/hwrng: No such device') + # verify that we indeed have virtio-net devices (without having the + # virtio-net driver handy) + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/ccw/devices/0.1.1111/cutype', + '3832/01') + exec_command_and_wait_for_pattern(self, + r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_vendor', + r'0x1af4') + exec_command_and_wait_for_pattern(self, + r'cat /sys/bus/pci/devices/0005\:00\:00.0/subsystem_device', + r'0x0001') + # check fid propagation + exec_command_and_wait_for_pattern(self, + r'cat /sys/bus/pci/devices/000a\:00\:00.0/function_id', + r'0x0000000c') + # add another device + self.clear_guest_dmesg() + 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 ;' + 'sleep 1 ; done ; ls /sys/bus/ccw/devices/', + '0.0.4711') + # and detach it again + self.clear_guest_dmesg() + 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() + exec_command_and_wait_for_pattern(self, + 'ls /sys/bus/ccw/devices/0.0.4711', + 'No such file or directory') + # test the virtio-balloon device + exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo', + 'MemTotal: 115640 kB') + 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.cmd('human-monitor-command', command_line='balloon 128') + exec_command_and_wait_for_pattern(self, 'head -n 1 /proc/meminfo', + 'MemTotal: 115640 kB') + + + def test_s390x_fedora(self): + self.set_machine('s390-ccw-virtio') + + kernel_path = self.ASSET_F31_KERNEL.fetch() + + initrd_path_xz = self.ASSET_F31_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'initrd-raw.img') + lzma_uncompress(initrd_path_xz, initrd_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 ' + 'rd.plymouth=0 plymouth.enable=0 rd.rescue') + self.vm.add_args('-nographic', + '-smp', '4', + '-m', '512', + '-name', 'Some Guest Name', + '-uuid', '30de4fd9-b4d5-409e-86a5-09b387f70bfa', + '-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-device', 'zpci,uid=7,target=n', + '-device', 'virtio-net-pci,id=n,mac=02:ca:fe:fa:ce:12', + '-device', 'virtio-rng-ccw,devno=fe.1.9876', + '-device', 'virtio-gpu-ccw,devno=fe.2.5432') + self.vm.launch() + self.wait_for_console_pattern('Kernel command line: %s' + % kernel_command_line) + self.wait_for_console_pattern('Entering emergency mode') + + # Some tests to see whether the CLI options have been considered: + self.log.info("Test whether QEMU CLI options have been considered") + exec_command_and_wait_for_pattern(self, + 'while ! (dmesg | grep enP7p0s0) ; do sleep 1 ; done', + 'virtio_net virtio0 enP7p0s0: renamed') + exec_command_and_wait_for_pattern(self, 'lspci', + '0007:00:00.0 Class 0200: Device 1af4:1000') + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/net/enP7p0s0/address', + '02:ca:fe:fa:ce:12') + exec_command_and_wait_for_pattern(self, 'lscss', '0.1.9876') + exec_command_and_wait_for_pattern(self, 'lscss', '0.2.5432') + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'processors : 4') + exec_command_and_wait_for_pattern(self, 'grep MemTotal /proc/meminfo', + 'MemTotal: 499848 kB') + exec_command_and_wait_for_pattern(self, 'grep Name /proc/sysinfo', + 'Extended Name: Some Guest Name') + exec_command_and_wait_for_pattern(self, 'grep UUID /proc/sysinfo', + '30de4fd9-b4d5-409e-86a5-09b387f70bfa') + + # Disable blinking cursor, then write some stuff into the framebuffer. + # QEMU's PPM screendumps contain uncompressed 24-bit values, while the + # framebuffer uses 32-bit, so we pad our text with some spaces when + # 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. + # 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, + 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.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.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.') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py new file mode 100755 index 0000000000..20727f6bdf --- /dev/null +++ b/tests/functional/test_s390x_topology.py @@ -0,0 +1,421 @@ +#!/usr/bin/env python3 +# +# 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 time + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import lzma_uncompress + + +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') + ASSET_F35_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/35/Server/s390x/os' + '/images/kernel.img'), + '1f2dddfd11bb1393dd2eb2e784036fbf6fc11057a6d7d27f9eb12d3edc67ef73') + + ASSET_F35_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/35/Server/s390x/os' + '/images/initrd.img'), + '1100145fbca00240c8c372ae4b89b48c99844bc189b3dfbc3f481dc60055ca46') + + 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_path = self.ASSET_F35_KERNEL.fetch() + initrd_path_xz = self.ASSET_F35_INITRD.fetch() + initrd_path = os.path.join(self.workdir, 'initrd-raw.img') + 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. + """ + self.set_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. + """ + self.set_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. + """ + self.set_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. + """ + self.set_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. + """ + self.set_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. + """ + self.set_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. + """ + self.set_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. + """ + self.set_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 + """ + self.set_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 + """ + self.set_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) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/test_s390x_tuxrun.py new file mode 100755 index 0000000000..dcab17c68b --- /dev/null +++ b/tests/functional/test_s390x_tuxrun.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunS390xTest(TuxRunBaselineTest): + + ASSET_S390X_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/s390/bzImage', + '0414e98dd1c3dafff8496c9cd9c28a5f8d04553bb5ba37e906a812b48d442ef0') + ASSET_S390X_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/s390/rootfs.ext4.zst', + '88c37c32276677f873a25ab9ec6247895b8e3e6f8259134de2a616080b8ab3fc') + + def test_s390(self): + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_S390X_KERNEL, + rootfs_asset=self.ASSET_S390X_ROOTFS, + drive="virtio-blk-ccw", + haltmsg="Requesting system halt") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/test_sh4_r2d.py new file mode 100755 index 0000000000..c3cfff79ad --- /dev/null +++ b/tests/functional/test_sh4_r2d.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Boot a Linux kernel on a r2d sh4 machine and check the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract +from unittest import skipUnless + +class R2dTest(LinuxKernelTest): + + ASSET_DAY09 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day09.tar.xz', + 'a61b44d2630a739d1380cc4ff4b80981d47ccfd5992f1484ccf48322c35f09ac') + + # This test has a 6-10% failure rate on various hosts that look + # like issues with a buggy kernel. + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable') + def test_r2d(self): + self.set_machine('r2d') + file_path = self.ASSET_DAY09.fetch() + archive_extract(file_path, self.workdir) + self.vm.add_args('-append', 'console=ttySC1') + self.launch_kernel(self.workdir + '/day09/zImage', console_index=1, + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_sh4_tuxrun.py b/tests/functional/test_sh4_tuxrun.py new file mode 100755 index 0000000000..b33533fc7e --- /dev/null +++ b/tests/functional/test_sh4_tuxrun.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# +# 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 + +from unittest import skipUnless +from qemu_test import Asset, exec_command_and_wait_for_pattern +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunSh4Test(TuxRunBaselineTest): + + ASSET_SH4_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/sh4/zImage', + '29d9b2aba604a0f53a5dc3b5d0f2b8e35d497de1129f8ee5139eb6fdf0db692f') + ASSET_SH4_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/sh4/rootfs.ext4.zst', + '3592a7a3d5a641e8b9821449e77bc43c9904a56c30d45da0694349cfd86743fd') + + def test_sh4(self): + self.set_machine('r2d') + self.cpu='sh7785' + self.root='sda' + self.console='ttySC1' + + # 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(self.ASSET_SH4_KERNEL, + self.ASSET_SH4_ROOTFS) + + # 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("tuxtest login:") + exec_command_and_wait_for_pattern(self, 'root', 'root@tuxtest:~#') + exec_command_and_wait_for_pattern(self, 'halt', + "reboot: System halted") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/test_sh4eb_r2d.py new file mode 100755 index 0000000000..d9c022c8b8 --- /dev/null +++ b/tests/functional/test_sh4eb_r2d.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# +# Boot a Linux kernel on a r2d sh4eb machine and check the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test.utils import archive_extract +from unittest import skipUnless + +class R2dEBTest(LinuxKernelTest): + + ASSET_TGZ = Asset( + 'https://landley.net/bin/mkroot/0.8.11/sh4eb.tgz', + 'be8c6cb5aef8406899dc5aa5e22b6aa45840eb886cdd3ced51555c10577ada2c') + + def test_sh4eb_r2d(self): + self.set_machine('r2d') + file_path = self.ASSET_TGZ.fetch() + archive_extract(file_path, self.workdir) + self.vm.add_args('-append', 'console=ttySC1 noiotrap') + self.launch_kernel(os.path.join(self.workdir, 'sh4eb/linux-kernel'), + initrd=os.path.join(self.workdir, 'sh4eb/initramfs.cpio.gz'), + console_index=1, wait_for='Type exit when done') + exec_command_and_wait_for_pattern(self, 'exit', 'Restarting system') + shutil.rmtree(os.path.join(self.workdir, 'sh4eb')) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/test_sparc64_sun4u.py new file mode 100755 index 0000000000..32e245f4ad --- /dev/null +++ b/tests/functional/test_sparc64_sun4u.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class Sun4uMachine(QemuSystemTest): + """Boots the Linux kernel and checks that the console is operational""" + + timeout = 90 + + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day23.tar.xz'), + 'a3ed92450704af244178351afd0e769776e7decb298e95a63abfd9a6e3f6c854') + + def test_sparc64_sun4u(self): + self.set_machine('sun4u') + file_path = self.ASSET_IMAGE.fetch() + kernel_name = 'day23/vmlinux' + archive_extract(file_path, self.workdir, kernel_name) + self.vm.set_console() + self.vm.add_args('-kernel', os.path.join(self.workdir, kernel_name), + '-append', 'printk.time=0') + self.vm.launch() + wait_for_console_pattern(self, 'Starting logging: OK') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/test_sparc64_tuxrun.py new file mode 100755 index 0000000000..1c2c005630 --- /dev/null +++ b/tests/functional/test_sparc64_tuxrun.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunSparc64Test(TuxRunBaselineTest): + + ASSET_SPARC64_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/sparc64/vmlinux', + 'e34313e4325ff21deaa3d38a502aa09a373ef62b9bd4d7f8f29388b688225c55') + ASSET_SPARC64_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/sparc64/rootfs.ext4.zst', + 'ad2f1dc436ab51583543d25d2c210cab478645d47078d30d129a66ab0e281d76') + + def test_sparc64(self): + self.root='sda' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_SPARC64_KERNEL, + rootfs_asset=self.ASSET_SPARC64_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/test_sparc_sun4m.py new file mode 100755 index 0000000000..573f85222a --- /dev/null +++ b/tests/functional/test_sparc_sun4m.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a sparc sun4m machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class Sun4mTest(LinuxKernelTest): + + ASSET_DAY11 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day11.tar.xz', + 'c776533ba756bf4dd3f1fc4c024fb50ef0d853e05c5f5ddf0900a32d1eaa49e0') + + def test_sparc_ss20(self): + self.set_machine('SS-20') + file_path = self.ASSET_DAY11.fetch() + archive_extract(file_path, self.workdir) + self.launch_kernel(self.workdir + '/day11/zImage.elf', + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_version.py b/tests/functional/test_version.py new file mode 100755 index 0000000000..3ab3b67f7e --- /dev/null +++ b/tests/functional/test_version.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Version check example test +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from qemu_test import QemuSystemTest + + +class Version(QemuSystemTest): + + def test_qmp_human_info_version(self): + self.set_machine('none') + self.vm.add_args('-nodefaults') + self.vm.launch() + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py new file mode 100755 index 0000000000..d5027487ac --- /dev/null +++ b/tests/functional/test_virtio_gpu.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +# +# virtio-gpu tests +# +# 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 qemu_test import BUILD_DIR +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import is_readable_executable_file + +from qemu.utils import kvm_available + +import os +import socket +import subprocess + + +def pick_default_vug_bin(): + relative_path = "./contrib/vhost-user-gpu/vhost-user-gpu" + if is_readable_executable_file(relative_path): + return relative_path + + bld_dir_path = os.path.join(BUILD_DIR, relative_path) + if is_readable_executable_file(bld_dir_path): + return bld_dir_path + + +class VirtioGPUx86(QemuSystemTest): + + KERNEL_COMMAND_LINE = "printk.time=0 console=ttyS0 rdinit=/bin/bash" + ASSET_KERNEL = Asset( + ("https://archives.fedoraproject.org/pub/archive/fedora" + "/linux/releases/33/Everything/x86_64/os/images" + "/pxeboot/vmlinuz"), + '2dc5fb5cfe9ac278fa45640f3602d9b7a08cc189ed63fd9b162b07073e4df397') + ASSET_INITRD = Asset( + ("https://archives.fedoraproject.org/pub/archive/fedora" + "/linux/releases/33/Everything/x86_64/os/images" + "/pxeboot/initrd.img"), + 'c49b97f893a5349e4883452178763e402bdc5caa8845b226a2d1329b5f356045') + + 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_virtio_vga_virgl(self): + # FIXME: should check presence of virtio, virgl etc + self.require_accelerator('kvm') + + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + self.vm.set_console() + self.vm.add_args("-cpu", "host") + self.vm.add_args("-m", "2G") + self.vm.add_args("-machine", "pc,accel=kvm") + self.vm.add_args("-device", "virtio-vga-gl") + self.vm.add_args("-display", "egl-headless") + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + self.KERNEL_COMMAND_LINE, + ) + try: + self.vm.launch() + except: + # TODO: probably fails because we are missing the VirGL features + self.skipTest("VirGL not enabled?") + + self.wait_for_console_pattern("as init process") + exec_command_and_wait_for_pattern( + self, "/usr/sbin/modprobe virtio_gpu", "features: +virgl +edid" + ) + + def test_vhost_user_vga_virgl(self): + # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc + self.require_accelerator('kvm') + + vug = pick_default_vug_bin() + if not vug: + self.skipTest("Could not find vhost-user-gpu") + + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + # Create socketpair to connect proxy and remote processes + qemu_sock, vug_sock = socket.socketpair( + socket.AF_UNIX, socket.SOCK_STREAM + ) + os.set_inheritable(qemu_sock.fileno(), True) + os.set_inheritable(vug_sock.fileno(), True) + + self._vug_log_path = os.path.join( + self.logdir, "vhost-user-gpu.log" + ) + self._vug_log_file = open(self._vug_log_path, "wb") + self.log.info('Complete vhost-user-gpu.log file can be ' + 'found at %s', self._vug_log_path) + + vugp = subprocess.Popen( + [vug, "--virgl", "--fd=%d" % vug_sock.fileno()], + stdin=subprocess.DEVNULL, + stdout=self._vug_log_file, + stderr=subprocess.STDOUT, + shell=False, + close_fds=False, + ) + + self.vm.set_console() + self.vm.add_args("-cpu", "host") + self.vm.add_args("-m", "2G") + self.vm.add_args("-object", "memory-backend-memfd,id=mem,size=2G") + self.vm.add_args("-machine", "pc,memory-backend=mem,accel=kvm") + self.vm.add_args("-chardev", "socket,id=vug,fd=%d" % qemu_sock.fileno()) + self.vm.add_args("-device", "vhost-user-vga,chardev=vug") + self.vm.add_args("-display", "egl-headless") + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + self.KERNEL_COMMAND_LINE, + ) + try: + self.vm.launch() + except: + # TODO: probably fails because we are missing the VirGL features + self.skipTest("VirGL not enabled?") + self.wait_for_console_pattern("as init process") + exec_command_and_wait_for_pattern(self, "/usr/sbin/modprobe virtio_gpu", + "features: +virgl +edid") + self.vm.shutdown() + qemu_sock.close() + vugp.terminate() + vugp.wait() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_virtio_version.py b/tests/functional/test_virtio_version.py new file mode 100755 index 0000000000..92e3f5caf0 --- /dev/null +++ b/tests/functional/test_virtio_version.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +""" +Check compatibility of virtio device types +""" +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# 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 sys +import os + +from qemu.machine import QEMUMachine +from qemu_test import QemuSystemTest + +# Virtio Device IDs: +VIRTIO_NET = 1 +VIRTIO_BLOCK = 2 +VIRTIO_CONSOLE = 3 +VIRTIO_RNG = 4 +VIRTIO_BALLOON = 5 +VIRTIO_RPMSG = 7 +VIRTIO_SCSI = 8 +VIRTIO_9P = 9 +VIRTIO_RPROC_SERIAL = 11 +VIRTIO_CAIF = 12 +VIRTIO_GPU = 16 +VIRTIO_INPUT = 18 +VIRTIO_VSOCK = 19 +VIRTIO_CRYPTO = 20 + +PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 + +# Device IDs for legacy/transitional devices: +PCI_LEGACY_DEVICE_IDS = { + VIRTIO_NET: 0x1000, + VIRTIO_BLOCK: 0x1001, + VIRTIO_BALLOON: 0x1002, + VIRTIO_CONSOLE: 0x1003, + VIRTIO_SCSI: 0x1004, + VIRTIO_RNG: 0x1005, + VIRTIO_9P: 0x1009, + VIRTIO_VSOCK: 0x1012, +} + +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.cmd('qom-list-types', implements=implements)] + +def get_pci_interfaces(vm, devtype): + interfaces = ('pci-express-device', 'conventional-pci-device') + return [i for i in interfaces if devtype_implements(vm, devtype, i)] + +class VirtioVersionCheck(QemuSystemTest): + """ + Check if virtio-version-specific device types result in the + same device tree created by `disable-modern` and + `disable-legacy`. + """ + + # just in case there are failures, show larger diff: + maxDiff = 4096 + + def run_device(self, devtype, opts=None, machine='pc'): + """ + Run QEMU with `-device DEVTYPE`, return device info from `query-pci` + """ + with QEMUMachine(self.qemu_bin) as vm: + vm.set_machine(machine) + if opts: + devtype += ',' + opts + vm.add_args('-device', '%s,id=devfortest' % (devtype)) + vm.add_args('-S') + vm.launch() + + 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'] + return devfortest[0], get_pci_interfaces(vm, devtype) + + + def assert_devids(self, dev, devid, non_transitional=False): + self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) + self.assertEqual(dev['id']['device'], devid) + if non_transitional: + self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) + self.assertGreaterEqual(dev['id']['subsystem'], 0x40) + + def check_all_variants(self, qemu_devtype, virtio_devid): + """Check if a virtio device type and its variants behave as expected""" + # Force modern mode: + dev_modern, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=on') + self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), + non_transitional=True) + + # -non-transitional device types should be 100% equivalent to + # ,disable-modern=off,disable-legacy=on + dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) + self.assertEqual(dev_modern, dev_1_0) + + # Force transitional mode: + dev_trans, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=off') + self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) + + # Force legacy mode: + dev_legacy, _ = self.run_device(qemu_devtype, + 'disable-modern=on,disable-legacy=off') + self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) + + # No options: default to transitional on PC machine-type: + no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) + self.assertEqual(dev_trans, no_opts_pc) + + #TODO: check if plugging on a PCI Express bus will make the + # device non-transitional + #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') + #self.assertEqual(dev_modern, no_opts_q35) + + # -transitional device types should be 100% equivalent to + # ,disable-modern=off,disable-legacy=off + dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) + self.assertEqual(dev_trans, dev_trans) + + # ensure the interface information is correct: + self.assertIn('conventional-pci-device', generic_ifaces) + self.assertIn('pci-express-device', generic_ifaces) + + self.assertIn('conventional-pci-device', nt_ifaces) + self.assertIn('pci-express-device', nt_ifaces) + + self.assertIn('conventional-pci-device', trans_ifaces) + self.assertNotIn('pci-express-device', trans_ifaces) + + + def test_conventional_devs(self): + self.set_machine('pc') + self.check_all_variants('virtio-net-pci', VIRTIO_NET) + # virtio-blk requires 'driver' parameter + #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) + self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) + self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) + self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) + self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) + # virtio-9p requires 'fsdev' parameter + #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) + + def check_modern_only(self, qemu_devtype, virtio_devid): + """Check if a modern-only virtio device type behaves as expected""" + # Force modern mode: + dev_modern, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=on') + self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), + non_transitional=True) + + # No options: should be modern anyway + dev_no_opts, ifaces = self.run_device(qemu_devtype) + self.assertEqual(dev_modern, dev_no_opts) + + self.assertIn('conventional-pci-device', ifaces) + self.assertIn('pci-express-device', ifaces) + + def test_modern_only_devs(self): + self.set_machine('pc') + self.check_modern_only('virtio-vga', VIRTIO_GPU) + self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) + self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) + self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) + self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/test_x86_64_tuxrun.py new file mode 100755 index 0000000000..4f96139871 --- /dev/null +++ b/tests/functional/test_x86_64_tuxrun.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# 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 + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunX86Test(TuxRunBaselineTest): + + ASSET_X86_64_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/x86_64/bzImage', + '2bc7480a669ee9b6b82500a236aba0c54233debe98cb968268fa230f52f03461') + ASSET_X86_64_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/x86_64/rootfs.ext4.zst', + 'b72ac729769b8f51c6dffb221113c9a063c774dbe1d66af30eb593c4e9999b4b') + + def test_x86_64(self): + self.set_machine('q35') + self.cpu="Nehalem" + self.root='sda' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_X86_64_KERNEL, + rootfs_asset=self.ASSET_X86_64_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_x86_cpu_model_versions.py b/tests/functional/test_x86_cpu_model_versions.py new file mode 100755 index 0000000000..bd18acd44f --- /dev/null +++ b/tests/functional/test_x86_cpu_model_versions.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python3 +# +# Basic validation of x86 versioned CPU models and CPU model aliases +# +# Copyright (c) 2019 Red Hat Inc +# +# Author: +# Eduardo Habkost +# +# 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 . +# + +import re + +from qemu_test import QemuSystemTest + +class X86CPUModelAliases(QemuSystemTest): + """ + Validation of PC CPU model versions and CPU model aliases + """ + def validate_aliases(self, cpus): + for c in cpus.values(): + if 'alias-of' in c: + # all aliases must point to a valid CPU model name: + self.assertIn(c['alias-of'], cpus, + '%s.alias-of (%s) is not a valid CPU model name' % (c['name'], c['alias-of'])) + # aliases must not point to aliases + self.assertNotIn('alias-of', cpus[c['alias-of']], + '%s.alias-of (%s) points to another alias' % (c['name'], c['alias-of'])) + + # aliases must not be static + self.assertFalse(c['static']) + + def validate_variant_aliases(self, cpus): + # -noTSX, -IBRS and -IBPB variants of CPU models are special: + # they shouldn't have their own versions: + self.assertNotIn("Haswell-noTSX-v1", cpus, + "Haswell-noTSX shouldn't be versioned") + self.assertNotIn("Broadwell-noTSX-v1", cpus, + "Broadwell-noTSX shouldn't be versioned") + self.assertNotIn("Nehalem-IBRS-v1", cpus, + "Nehalem-IBRS shouldn't be versioned") + self.assertNotIn("Westmere-IBRS-v1", cpus, + "Westmere-IBRS shouldn't be versioned") + self.assertNotIn("SandyBridge-IBRS-v1", cpus, + "SandyBridge-IBRS shouldn't be versioned") + self.assertNotIn("IvyBridge-IBRS-v1", cpus, + "IvyBridge-IBRS shouldn't be versioned") + self.assertNotIn("Haswell-noTSX-IBRS-v1", cpus, + "Haswell-noTSX-IBRS shouldn't be versioned") + self.assertNotIn("Haswell-IBRS-v1", cpus, + "Haswell-IBRS shouldn't be versioned") + self.assertNotIn("Broadwell-noTSX-IBRS-v1", cpus, + "Broadwell-noTSX-IBRS shouldn't be versioned") + self.assertNotIn("Broadwell-IBRS-v1", cpus, + "Broadwell-IBRS shouldn't be versioned") + self.assertNotIn("Skylake-Client-IBRS-v1", cpus, + "Skylake-Client-IBRS shouldn't be versioned") + self.assertNotIn("Skylake-Server-IBRS-v1", cpus, + "Skylake-Server-IBRS shouldn't be versioned") + self.assertNotIn("EPYC-IBPB-v1", cpus, + "EPYC-IBPB shouldn't be versioned") + + def test_4_0_alias_compatibility(self): + """ + Check if pc-*-4.0 unversioned CPU model won't be reported as aliases + """ + self.set_machine('pc-i440fx-4.0') + # pc-*-4.0 won't expose non-versioned CPU models as aliases + # We do this to help management software to keep compatibility + # with older QEMU versions that didn't have the versioned CPU model + self.vm.add_args('-S') + self.vm.launch() + 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.assertNotIn('alias-of', cpus['Cascadelake-Server'], + 'Cascadelake-Server must not be an alias') + 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.assertNotIn('alias-of', cpus['qemu64'], + 'qemu64 must not be an alias') + self.assertNotIn('alias-of', cpus['qemu64-v1'], + 'qemu64-v1 must not be an alias') + + self.validate_variant_aliases(cpus) + + # On pc-*-4.0, no CPU model should be reported as an alias: + for name,c in cpus.items(): + self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name)) + + def test_4_1_alias(self): + """ + Check if unversioned CPU model is an alias pointing to right version + """ + self.set_machine('pc-i440fx-4.1') + self.vm.add_args('-S') + self.vm.launch() + + 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.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.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.assertEqual(cpus["Haswell"].get('alias-of'), + "Haswell-v1", + "Haswell must be an alias") + self.assertEqual(cpus["Haswell-noTSX"].get('alias-of'), + "Haswell-v2", + "Haswell-noTSX must be an alias") + self.assertEqual(cpus["Haswell-IBRS"].get('alias-of'), + "Haswell-v3", + "Haswell-IBRS must be an alias") + self.assertEqual(cpus["Haswell-noTSX-IBRS"].get('alias-of'), + "Haswell-v4", + "Haswell-noTSX-IBRS must be an alias") + + self.assertEqual(cpus["Broadwell"].get('alias-of'), + "Broadwell-v1", + "Broadwell must be an alias") + self.assertEqual(cpus["Broadwell-noTSX"].get('alias-of'), + "Broadwell-v2", + "Broadwell-noTSX must be an alias") + self.assertEqual(cpus["Broadwell-IBRS"].get('alias-of'), + "Broadwell-v3", + "Broadwell-IBRS must be an alias") + self.assertEqual(cpus["Broadwell-noTSX-IBRS"].get('alias-of'), + "Broadwell-v4", + "Broadwell-noTSX-IBRS must be an alias") + + self.assertEqual(cpus["Nehalem"].get('alias-of'), + "Nehalem-v1", + "Nehalem must be an alias") + self.assertEqual(cpus["Nehalem-IBRS"].get('alias-of'), + "Nehalem-v2", + "Nehalem-IBRS must be an alias") + + self.assertEqual(cpus["Westmere"].get('alias-of'), + "Westmere-v1", + "Westmere must be an alias") + self.assertEqual(cpus["Westmere-IBRS"].get('alias-of'), + "Westmere-v2", + "Westmere-IBRS must be an alias") + + self.assertEqual(cpus["SandyBridge"].get('alias-of'), + "SandyBridge-v1", + "SandyBridge must be an alias") + self.assertEqual(cpus["SandyBridge-IBRS"].get('alias-of'), + "SandyBridge-v2", + "SandyBridge-IBRS must be an alias") + + self.assertEqual(cpus["IvyBridge"].get('alias-of'), + "IvyBridge-v1", + "IvyBridge must be an alias") + self.assertEqual(cpus["IvyBridge-IBRS"].get('alias-of'), + "IvyBridge-v2", + "IvyBridge-IBRS must be an alias") + + self.assertEqual(cpus["Skylake-Client"].get('alias-of'), + "Skylake-Client-v1", + "Skylake-Client must be an alias") + self.assertEqual(cpus["Skylake-Client-IBRS"].get('alias-of'), + "Skylake-Client-v2", + "Skylake-Client-IBRS must be an alias") + + self.assertEqual(cpus["Skylake-Server"].get('alias-of'), + "Skylake-Server-v1", + "Skylake-Server must be an alias") + self.assertEqual(cpus["Skylake-Server-IBRS"].get('alias-of'), + "Skylake-Server-v2", + "Skylake-Server-IBRS must be an alias") + + self.assertEqual(cpus["EPYC"].get('alias-of'), + "EPYC-v1", + "EPYC must be an alias") + self.assertEqual(cpus["EPYC-IBPB"].get('alias-of'), + "EPYC-v2", + "EPYC-IBPB must be an alias") + + self.validate_aliases(cpus) + + def test_none_alias(self): + """ + Check if unversioned CPU model is an alias pointing to some version + """ + self.set_machine('none') + self.vm.add_args('-S') + self.vm.launch() + + 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.assertTrue(re.match('Cascadelake-Server-v[0-9]+', cpus['Cascadelake-Server']['alias-of']), + 'Cascadelake-Server must be an alias of versioned CPU model') + 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.assertTrue(re.match('qemu64-v[0-9]+', cpus['qemu64']['alias-of']), + 'qemu64 must be an alias of versioned CPU model') + self.assertNotIn('alias-of', cpus['qemu64-v1'], + 'qemu64-v1 must not be an alias') + + self.validate_aliases(cpus) + + +class CascadelakeArchCapabilities(QemuSystemTest): + """ + Validation of Cascadelake arch-capabilities + """ + def get_cpu_prop(self, 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): + self.set_machine('pc-i440fx-4.1') + # machine-type only: + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server,x-force-features=on,check=off,' + 'enforce=off') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities') + + def test_4_0(self): + self.set_machine('pc-i440fx-4.0') + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server,x-force-features=on,check=off,' + 'enforce=off') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities') + + def test_set_4_0(self): + self.set_machine('pc-i440fx-4.0') + # command line must override machine-type if CPU model is not versioned: + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server,x-force-features=on,check=off,' + 'enforce=off,+arch-capabilities') + self.vm.launch() + self.assertTrue(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities') + + def test_unset_4_1(self): + self.set_machine('pc-i440fx-4.1') + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server,x-force-features=on,check=off,' + 'enforce=off,-arch-capabilities') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') + + def test_v1_4_0(self): + self.set_machine('pc-i440fx-4.0') + # versioned CPU model overrides machine-type: + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server-v1,x-force-features=on,check=off,' + 'enforce=off') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities') + + def test_v2_4_0(self): + self.set_machine('pc-i440fx-4.0') + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server-v2,x-force-features=on,check=off,' + 'enforce=off') + self.vm.launch() + self.assertTrue(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') + + def test_v1_set_4_0(self): + self.set_machine('pc-i440fx-4.0') + # command line must override machine-type and versioned CPU model: + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server-v1,x-force-features=on,check=off,' + 'enforce=off,+arch-capabilities') + self.vm.launch() + self.assertTrue(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') + + def test_v2_unset_4_1(self): + self.set_machine('pc-i440fx-4.1') + self.vm.add_args('-S') + self.set_vm_arg('-cpu', + 'Cascadelake-Server-v2,x-force-features=on,check=off,' + 'enforce=off,-arch-capabilities') + self.vm.launch() + self.assertFalse(self.get_cpu_prop('arch-capabilities'), + 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/test_xtensa_lx60.py new file mode 100755 index 0000000000..d4ad92dc6c --- /dev/null +++ b/tests/functional/test_xtensa_lx60.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an xtensa lx650 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test.utils import archive_extract + +class XTensaLX60Test(LinuxKernelTest): + + ASSET_DAY02 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day02.tar.xz', + '68ff07f9b3fd3df36d015eb46299ba44748e94bfbb2d5295fddc1a8d4a9fd324') + + def test_xtensa_lx60(self): + self.set_machine('lx60') + self.cpu = 'dc233c' + file_path = self.ASSET_DAY02.fetch() + archive_extract(file_path, self.workdir) + self.launch_kernel(self.workdir + '/day02/santas-sleigh-ride.elf', + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index d865e46ecd..5a091db8be 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -26,11 +26,16 @@ 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('test_args', nargs='*', + help="Additional args for GDB test script. " + "The args should be preceded by -- to avoid confusion " + "with flags for runner 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 +63,52 @@ 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: + if args.test_args: + gdb_cmd += f" -ex \"py sys.argv={args.test_args}\"" + 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..4f08089e6a --- /dev/null +++ b/tests/guest-debug/test_gdbstub.py @@ -0,0 +1,71 @@ +"""Helper functions for gdbstub testing + +""" +from __future__ import print_function +import argparse +import gdb +import os +import sys +import traceback + +fail_count = 0 + + +def gdb_exit(status): + gdb.execute(f"exit {status}") + + +class arg_parser(argparse.ArgumentParser): + def exit(self, status=None, message=""): + print("Wrong GDB script test argument! " + message) + gdb_exit(1) + + +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") + gdb_exit(0) + + if gdb.parse_and_eval("$pc") == 0: + print("SKIP: PC not set") + gdb_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)) + gdb_exit(fail_count) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index e3eb28cf2e..9ad3f70bde 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit e3eb28cf2e17fbcf7fe7e19505ee432b8ec5bbb5 +Subproject commit 9ad3f70bde9865d5ad18f36d256d472e72b5cbf3 diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml new file mode 100644 index 0000000000..f8186b0e69 --- /dev/null +++ b/tests/lcitool/mappings.yml @@ -0,0 +1,110 @@ +mappings: + # Too old on Ubuntu 22.04; we install it from cargo instead + bindgen: + Ubuntu2204: + + flake8: + OpenSUSELeap15: + + # Due to https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1081535 we + # have to disable all packages that depend on libgl1-mesa-dri:mips64el + gtk3: + mips64el-deb: + + libdrm: + mips64el-deb: + + libepoxy: + mips64el-deb: + + gtk-vnc: + mips64el-deb: + + mesa-libgbm: + mips64el-deb: + + meson: + OpenSUSELeap15: + + python3: + OpenSUSELeap15: python311-base + + python3-PyYAML: + OpenSUSELeap15: + + python3-devel: + OpenSUSELeap15: python311-devel + + python3-docutils: + OpenSUSELeap15: + + python3-numpy: + OpenSUSELeap15: + + python3-opencv: + OpenSUSELeap15: + + python3-pillow: + OpenSUSELeap15: + + python3-pip: + OpenSUSELeap15: python311-pip + + python3-pillow: + OpenSUSELeap15: + + python3-selinux: + OpenSUSELeap15: + + python3-setuptools: + OpenSUSELeap15: python311-setuptools + + python3-sphinx: + OpenSUSELeap15: + + python3-sphinx-rtd-theme: + OpenSUSELeap15: + + python3-sqlite3: + OpenSUSELeap15: python311 + + python3-tomli: + # test using tomllib + apk: + Fedora: + Debian12: + OpenSUSELeap15: + + python3-venv: + OpenSUSELeap15: python311-base + + python3-wheel: + OpenSUSELeap15: python311-pip + + sdl2: + mips64el-deb: + + sdl2-image: + mips64el-deb: + + virglrenderer: + mips64el-deb: + + vte: + mips64el-deb: + +pypi_mappings: + # Request more recent version + meson: + default: meson==1.5.0 + + # 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..6bc232a1c3 --- /dev/null +++ b/tests/lcitool/projects/qemu-minimal.yml @@ -0,0 +1,26 @@ +# Very minimal set of qemu packages, used for minimal cross-compile sanity checks +--- +packages: + - bash + - bc + - bison + - ccache + - findutils + - flex + - 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-win-installer.yml b/tests/lcitool/projects/qemu-win-installer.yml new file mode 100644 index 0000000000..f3663ba030 --- /dev/null +++ b/tests/lcitool/projects/qemu-win-installer.yml @@ -0,0 +1,5 @@ +# Additional packages that are required to build the code in qga/vss-win32/ +--- +packages: + - g++ + - mingw-w64-tools diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index c62dbc00f9..80bcac0902 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -3,6 +3,7 @@ packages: - alsa - bash - bc + - bindgen - bison - brlapi - bzip2 @@ -22,39 +23,40 @@ packages: - findutils - flex - fuse3 - - g++ - gcc + - gcc-native - gcovr - gettext - - genisoimage - glib2 - glib2-native - glib2-static - glusterfs - gnutls - gtk3 + - gtk-vnc - hostname - json-c - libaio - - libattr - libasan + - libattr - libbpf - libc-static - libcacard - libcap-ng + - libcbor - libcurl - libdrm - libepoxy - libfdt - libffi - libgcrypt - - libibumad - libibverbs - libiscsi - libjemalloc - libjpeg - libnfs - libnuma + - libpipewire-dev - libpmem - libpng - librbd @@ -69,53 +71,60 @@ packages: - liburing - libusbx - libvdeplug + - libxdp - libzstd - llvm - lttng-ust - lzo + - make + - mesa-libgbm + - meson + - mtools + - ncursesw - netcat - nettle - ninja - nsis - - make - - mesa-libgbm - - meson - - ncursesw - pam - pcre-static - - perl - pixman - pkg-config - pulseaudio - python3 - - python3-PyYAML + - python3-imp - python3-numpy - python3-opencv - python3-pillow - python3-pip + - python3-PyYAML - python3-sphinx - python3-sphinx-rtd-theme + - python3-sqlite3 + - python3-tomli - python3-venv - rpm2cpio + - rust - 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 - zlib - zlib-static + - zstdtools diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index fa966e4009..51012783c0 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -43,55 +43,107 @@ def atomic_write(filename, content): def generate(filename, cmd, trailer): print("Generate %s" % filename) - lcitool = subprocess.run(cmd, capture_output=True) + lcitool = subprocess.run(cmd, capture_output=True, encoding='utf8') if lcitool.returncode != 0: raise Exception("Failed to generate %s: %s" % (filename, lcitool.stderr)) - content = lcitool.stdout.decode("utf8") + content = lcitool.stdout if trailer is not None: 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) def generate_cirrus(target, trailer=None): filename = Path(src_dir, ".gitlab-ci.d", "cirrus", target + ".vars") - cmd = lcitool_cmd + ["variables", target, "qemu"] + cmd = lcitool_cmd + ["variables", "--format", "shell", target, "qemu"] 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) + + +def generate_yaml(os, target, arch, trailer=None): + filename = Path(src_dir, "scripts", "ci", "setup", os, f"{target}-{arch}.yaml") + cmd = lcitool_cmd + ["variables", "--format", "yaml", "-a", + arch, target, "qemu"] + generate(filename, cmd, trailer) # 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" ] +# Based on the hub.docker.com/library/rust Dockerfiles +fedora_rustup_nightly_extras = [ + "RUN dnf install -y wget\n", + "ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n", + "ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n", + "RUN set -eux && \\\n", + " rustArch='x86_64-unknown-linux-gnu' && \\\n", + " rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \\\n", + ' url="https://static.rust-lang.org/rustup/archive/1.27.1/${rustArch}/rustup-init" && \\\n', + ' wget "$url" && \\\n', + ' echo "${rustupSha256} *rustup-init" | sha256sum -c - && \\\n', + " chmod +x rustup-init && \\\n", + " ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \\\n", + " chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \\\n", + " /usr/local/cargo/bin/rustup --version && \\\n", + " /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n", + ' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n', + 'ENV PATH=$CARGO_HOME/bin:$PATH\n', + 'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n', +] + +ubuntu2204_bindgen_extras = [ + "ENV CARGO_HOME=/usr/local/cargo\n", + 'ENV PATH=$CARGO_HOME/bin:$PATH\n', + "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", + " apt install -y --no-install-recommends cargo\n", + 'RUN cargo install bindgen-cli\n', +] def cross_build(prefix, targets): conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix) @@ -107,66 +159,76 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-316") - 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-153") - generate_dockerfile("ubuntu2004", "ubuntu-2004", - trailer="".join(ubuntu2004_tsanhack)) + generate_dockerfile("alpine", "alpine-319") + generate_dockerfile("centos9", "centos-stream-9") + generate_dockerfile("debian", "debian-12", + trailer="".join(debian12_extras)) + generate_dockerfile("fedora", "fedora-40") + generate_dockerfile("opensuse-leap", "opensuse-leap-15") + generate_dockerfile("ubuntu2204", "ubuntu-2204", + trailer="".join(ubuntu2204_bindgen_extras)) + + # + # Non-fatal Rust-enabled build + # + generate_dockerfile("fedora-rust-nightly", "fedora-40", + trailer="".join(fedora_rustup_nightly_extras)) # # Cross compiling builds # - generate_dockerfile("debian-amd64-cross", "debian-11", + 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-11", + generate_dockerfile("debian-arm64-cross", "debian-12", cross="aarch64", trailer=cross_build("aarch64-linux-gnu-", "aarch64-softmmu,aarch64-linux-user")) - generate_dockerfile("debian-armel-cross", "debian-11", - cross="armv6l", - 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=cross_build("arm-linux-gnueabihf-", "arm-softmmu,arm-linux-user")) - generate_dockerfile("debian-mips64el-cross", "debian-11", + generate_dockerfile("debian-i686-cross", "debian-12", + cross="i686", + trailer=cross_build("i686-linux-gnu-", + "x86_64-softmmu," + "x86_64-linux-user," + "i386-softmmu,i386-linux-user")) + + generate_dockerfile("debian-mips64el-cross", "debian-12", cross="mips64el", trailer=cross_build("mips64el-linux-gnuabi64-", "mips64el-softmmu,mips64el-linux-user")) - generate_dockerfile("debian-mipsel-cross", "debian-11", + generate_dockerfile("debian-mipsel-cross", "debian-12", cross="mipsel", 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=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=cross_build("s390x-linux-gnu-", "s390x-softmmu,s390x-linux-user")) - generate_dockerfile("fedora-win32-cross", "fedora-35", - cross="mingw32", - trailer=cross_build("i686-w64-mingw32-", - "i386-softmmu")) - - generate_dockerfile("fedora-win64-cross", "fedora-35", + generate_dockerfile("fedora-win64-cross", "fedora-40", + project='qemu,qemu-win-installer', cross="mingw64", trailer=cross_build("x86_64-w64-mingw32-", "x86_64-softmmu")) @@ -174,9 +236,20 @@ try: # # Cirrus packages lists for GitLab # - generate_cirrus("freebsd-12") - generate_cirrus("freebsd-13") - generate_cirrus("macos-12") + generate_cirrus("freebsd-14") + generate_cirrus("macos-14") + + # + # VM packages lists + # + generate_pkglist("freebsd", "freebsd-14") + + # + # Ansible package lists + # + generate_yaml("ubuntu", "ubuntu-2204", "aarch64") + generate_yaml("ubuntu", "ubuntu-2204", "s390x") + sys.exit(0) except Exception as ex: 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..907a4c1c98 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -68,28 +68,21 @@ 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 + subdir('tcg/plugins') endif subdir('unit') subdir('qapi-schema') subdir('qtest') subdir('migration') +subdir('functional') diff --git a/tests/migration/Makefile b/tests/migration/Makefile index 13e99b1692..2c5ee287ec 100644 --- a/tests/migration/Makefile +++ b/tests/migration/Makefile @@ -5,7 +5,7 @@ # See the COPYING file in the top-level directory. # -TARGET_LIST = i386 aarch64 s390x +TARGET_LIST = i386 aarch64 s390x ppc64 SRC_PATH = ../.. 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 59fca2c70b..608d7270f6 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -77,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"] = {} @@ -102,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): @@ -109,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"]) @@ -128,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 @@ -228,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) @@ -256,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 @@ -270,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 @@ -278,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", @@ -289,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: @@ -303,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 3d464c7568..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,19 +51,31 @@ 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 @@ -57,7 +86,30 @@ innerloop: 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 */ @@ -83,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 b7b0fce2ee..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, 0x78, 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, 0x80, 0xe3, 0x3f, 0x75, 0xe6, 0x66, 0xb8, - 0x42, 0x00, 0x66, 0xba, 0xf8, 0x03, 0xee, 0xeb, 0xdb, 0x8d, 0x76, 0x00, - 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, 0x60, 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, 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/migration-test.h b/tests/migration/migration-test.h index 68512c0b1b..194df7df6f 100644 --- a/tests/migration/migration-test.h +++ b/tests/migration/migration-test.h @@ -22,6 +22,7 @@ /* PPC */ #define PPC_TEST_MEM_START (1 * 1024 * 1024) #define PPC_TEST_MEM_END (100 * 1024 * 1024) +#define PPC_H_PUT_TERM_CHAR 0x58 /* ARM */ #define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) diff --git a/tests/migration/ppc64/Makefile b/tests/migration/ppc64/Makefile new file mode 100644 index 0000000000..a3a2d98ac8 --- /dev/null +++ b/tests/migration/ppc64/Makefile @@ -0,0 +1,15 @@ +.PHONY: all clean +all: a-b-kernel.h + +a-b-kernel.h: ppc64.kernel + echo "$$__note" > $@ + xxd -i $< | sed -e 's/.*int.*//' >> $@ + +ppc64.kernel: ppc64.elf + $(CROSS_PREFIX)objcopy -O binary -S $< $@ + +ppc64.elf: a-b-kernel.S + $(CROSS_PREFIX)gcc -static -o $@ -nostdlib -Wl,--build-id=none $< + +clean: + $(RM) *.kernel *.elf diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/migration/ppc64/a-b-kernel.S new file mode 100644 index 0000000000..0613a8d18e --- /dev/null +++ b/tests/migration/ppc64/a-b-kernel.S @@ -0,0 +1,66 @@ +# +# Copyright (c) 2024 IBM, 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 "../migration-test.h" + +.section .text + +.macro print ch + li %r3,PPC_H_PUT_TERM_CHAR + li %r4,0 + li %r5,1 + li %r6,\ch + sldi %r6,%r6,56 + sc 1 +.endm + + .globl _start +_start: +. = 0x100 + /* + * Enter 64-bit mode. Not necessary because the test uses 32-bit + * addresses, but those constants could easily be changed and break + * in 32-bit mode. + */ + mfmsr %r9 + li %r10,-1 + rldimi %r9,%r10,63,0 + mtmsrd %r9 + + /* + * Set up test memory region. Non-volatiles are used because the + * hcall can clobber regs. + * r20 - start address + * r21 - number of pages + */ + lis %r20,PPC_TEST_MEM_START@h + ori %r20,%r20,PPC_TEST_MEM_START@l + lis %r9,PPC_TEST_MEM_END@h + ori %r9,%r9,PPC_TEST_MEM_END@l + subf %r21,%r20,%r9 + li %r10,TEST_MEM_PAGE_SIZE + divd %r21,%r21,%r10 + + print 'A' + + li %r3,0 + mr %r9,%r20 + mtctr %r21 +1: stb %r3,0(%r9) + addi %r9,%r9,TEST_MEM_PAGE_SIZE + bdnz 1b + +loop: + mr %r9,%r20 + mtctr %r21 +1: lbz %r3,0(%r9) + addi %r3,%r3,1 + stb %r3,0(%r9) + addi %r9,%r9,TEST_MEM_PAGE_SIZE + bdnz 1b + + print 'B' + b loop diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/migration/ppc64/a-b-kernel.h new file mode 100644 index 0000000000..673317efdb --- /dev/null +++ b/tests/migration/ppc64/a-b-kernel.h @@ -0,0 +1,42 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/ppc64. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char ppc64_kernel[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x20, 0x00, 0xa6, 0x39, 0x40, 0xff, 0xff, + 0x79, 0x49, 0xf8, 0x0e, 0x7d, 0x20, 0x01, 0x64, 0x3e, 0x80, 0x00, 0x10, + 0x62, 0x94, 0x00, 0x00, 0x3d, 0x20, 0x06, 0x40, 0x61, 0x29, 0x00, 0x00, + 0x7e, 0xb4, 0x48, 0x50, 0x39, 0x40, 0x10, 0x00, 0x7e, 0xb5, 0x53, 0xd2, + 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, 0x38, 0xa0, 0x00, 0x01, + 0x38, 0xc0, 0x00, 0x41, 0x78, 0xc6, 0xc1, 0xc6, 0x44, 0x00, 0x00, 0x22, + 0x38, 0x60, 0x00, 0x00, 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, + 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, 0x42, 0x00, 0xff, 0xf8, + 0x7e, 0x89, 0xa3, 0x78, 0x7e, 0xa9, 0x03, 0xa6, 0x88, 0x69, 0x00, 0x00, + 0x38, 0x63, 0x00, 0x01, 0x98, 0x69, 0x00, 0x00, 0x39, 0x29, 0x10, 0x00, + 0x42, 0x00, 0xff, 0xf0, 0x38, 0x60, 0x00, 0x58, 0x38, 0x80, 0x00, 0x00, + 0x38, 0xa0, 0x00, 0x01, 0x38, 0xc0, 0x00, 0x42, 0x78, 0xc6, 0xc1, 0xc6, + 0x44, 0x00, 0x00, 0x22, 0x4b, 0xff, 0xff, 0xcc +}; + 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/plugin/bb.c b/tests/plugin/bb.c deleted file mode 100644 index 7d470a1011..0000000000 --- a/tests/plugin/bb.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2018, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -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; - -/* 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) -{ - if (count->bb_count) { - g_string_append_printf(report, "CPU%d: " - "bb's: %" PRIu64", insns: %" PRIu64 "\n", - count->index, - count->bb_count, count->insn_count); - } -} - -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); - } - qemu_plugin_outs(report->str); -} - -static void vcpu_idle(qemu_plugin_id_t id, unsigned int cpu_index) -{ - CPUCount *count = g_ptr_array_index(counts, cpu_index); - g_autoptr(GString) report = g_string_new(""); - gen_one_cpu_report(count, report); - - if (report->len > 0) { - g_string_prepend(report, "Idling "); - qemu_plugin_outs(report->str); - } -} - -static void vcpu_tb_exec(unsigned int cpu_index, void *udata) -{ - CPUCount *count = max_cpus ? - g_ptr_array_index(counts, cpu_index) : &inline_count; - - 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) -{ - 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); - } else { - qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, - QEMU_PLUGIN_CB_NO_REGS, - (void *)n_insns); - } -} - -QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, - const qemu_info_t *info, - int argc, char **argv) -{ - int i; - - for (i = 0; i < argc; i++) { - char *opt = argv[i]; - g_autofree char **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); - return -1; - } - } else if (g_strcmp0(tokens[0], "idle") == 0) { - if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &idle_report)) { - fprintf(stderr, "boolean argument parsing failed: %s\n", opt); - return -1; - } - } else { - fprintf(stderr, "option parsing failed: %s\n", opt); - return -1; - } - } - - 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); - } - - if (idle_report) { - qemu_plugin_register_vcpu_idle_cb(id, vcpu_idle); - } - - 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 deleted file mode 100644 index cd5ea5d4ae..0000000000 --- a/tests/plugin/insn.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2018, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -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 bool do_inline; -static bool do_size; -static GArray *sizes; - -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]; -} Match; - -static GArray *matches; - -typedef struct { - Match *match; - uint64_t vaddr; - uint64_t hits; - char *disas; -} Instruction; - -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++; -} - -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; - 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]; - - match->hits[i]++; - match->total_delta[i] += delta; - - g_string_append_printf(ts, - ", %"PRId64" match hits, " - "Δ+%"PRId64 " since last match," - " %"PRId64 " avg insns/match\n", - match->hits[i], delta, - match->total_delta[i] / match->hits[i]); - - match->last_hit[i] = 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) -{ - size_t n = qemu_plugin_tb_n_insns(tb); - size_t i; - - for (i = 0; i < n; i++) { - 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); - } else { - uint64_t vaddr = qemu_plugin_insn_vaddr(insn); - qemu_plugin_register_vcpu_insn_exec_cb( - insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, - GUINT_TO_POINTER(vaddr)); - } - - if (do_size) { - size_t sz = qemu_plugin_insn_size(insn); - if (sz > sizes->len) { - g_array_set_size(sizes, sz); - } - unsigned long *cnt = &g_array_index(sizes, unsigned long, sz); - (*cnt)++; - } - - /* - * If we are tracking certain instructions we will need more - * information about the instruction which we also need to - * save if there is a hit. - */ - if (matches) { - char *insn_disas = qemu_plugin_insn_disas(insn); - int j; - for (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); - rec->disas = g_strdup(insn_disas); - rec->vaddr = qemu_plugin_insn_vaddr(insn); - rec->match = m; - qemu_plugin_register_vcpu_insn_exec_cb( - insn, vcpu_insn_matched_exec_before, - QEMU_PLUGIN_CB_NO_REGS, rec); - } - } - g_free(insn_disas); - } - } -} - -static void plugin_exit(qemu_plugin_id_t id, void *p) -{ - g_autoptr(GString) out = g_string_new(NULL); - int i; - - if (do_size) { - for (i = 0; i <= sizes->len; i++) { - unsigned long *cnt = &g_array_index(sizes, unsigned long, i); - if (*cnt) { - g_string_append_printf(out, - "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; - } - } - g_string_append_printf(out, "total insns: %" PRIu64 "\n", - total_insns); - } - qemu_plugin_outs(out->str); -} - - -/* 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)); - } - g_array_append_val(matches, new_match); -} - -QEMU_PLUGIN_EXPORT 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++) { - char *opt = argv[i]; - g_autofree char **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); - return -1; - } - } else if (g_strcmp0(tokens[0], "sizes") == 0) { - if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_size)) { - fprintf(stderr, "boolean argument parsing failed: %s\n", opt); - return -1; - } - } else if (g_strcmp0(tokens[0], "match") == 0) { - parse_match(tokens[1]); - } else { - fprintf(stderr, "option parsing failed: %s\n", opt); - return -1; - } - } - - if (do_size) { - sizes = g_array_new(true, true, sizeof(unsigned long)); - } - - 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 deleted file mode 100644 index 4570f7d815..0000000000 --- a/tests/plugin/mem.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2018, Emilio G. Cota - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -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; -static bool do_inline, do_callback; -static bool do_haddr; -static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; - -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_haddr) { - g_string_append_printf(out, "io accesses: %" PRIu64 "\n", io_count); - } - qemu_plugin_outs(out->str); -} - -static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, - uint64_t vaddr, void *udata) -{ - if (do_haddr) { - struct qemu_plugin_hwaddr *hwaddr; - hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); - if (qemu_plugin_hwaddr_is_io(hwaddr)) { - io_count++; - } else { - cb_mem_count++; - } - } else { - cb_mem_count++; - } -} - -static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) -{ - size_t n = qemu_plugin_tb_n_insns(tb); - size_t i; - - for (i = 0; i < n; i++) { - 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); - } - if (do_callback) { - qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, - QEMU_PLUGIN_CB_NO_REGS, - rw, NULL); - } - } -} - -QEMU_PLUGIN_EXPORT 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++) { - char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); - - if (g_strcmp0(tokens[0], "haddr") == 0) { - if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) { - fprintf(stderr, "boolean argument parsing failed: %s\n", opt); - return -1; - } - } else if (g_strcmp0(tokens[0], "track") == 0) { - if (g_strcmp0(tokens[1], "r") == 0) { - rw = QEMU_PLUGIN_MEM_R; - } else if (g_strcmp0(tokens[1], "w") == 0) { - rw = QEMU_PLUGIN_MEM_W; - } else if (g_strcmp0(tokens[1], "rw") == 0) { - rw = QEMU_PLUGIN_MEM_RW; - } else { - fprintf(stderr, "invaild value for argument track: %s\n", opt); - return -1; - } - } else 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); - return -1; - } - } else if (g_strcmp0(tokens[0], "callback") == 0) { - if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) { - fprintf(stderr, "boolean argument parsing failed: %s\n", opt); - return -1; - } - } else { - fprintf(stderr, "option parsing failed: %s\n", opt); - return -1; - } - } - - 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 deleted file mode 100644 index 2bbfc4b19e..0000000000 --- a/tests/plugin/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -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) diff --git a/tests/plugin/syscall.c b/tests/plugin/syscall.c deleted file mode 100644 index 96040c578f..0000000000 --- a/tests/plugin/syscall.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2020, Matthias Weckbecker - * - * License: GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#include -#include -#include -#include -#include -#include -#include - -#include - -QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; - -typedef struct { - int64_t num; - int64_t calls; - int64_t errors; -} SyscallStats; - -static GMutex lock; -static GHashTable *statistics; - -static SyscallStats *get_or_create_entry(int64_t num) -{ - SyscallStats *entry = - (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num)); - - if (!entry) { - entry = g_new0(SyscallStats, 1); - entry->num = num; - g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry); - } - - return entry; -} - -static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, - int64_t num, uint64_t a1, uint64_t a2, - uint64_t a3, uint64_t a4, uint64_t a5, - uint64_t a6, uint64_t a7, uint64_t a8) -{ - if (statistics) { - SyscallStats *entry; - g_mutex_lock(&lock); - entry = get_or_create_entry(num); - entry->calls++; - g_mutex_unlock(&lock); - } else { - g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num); - qemu_plugin_outs(out); - } -} - -static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx, - int64_t num, int64_t ret) -{ - if (statistics) { - SyscallStats *entry; - - g_mutex_lock(&lock); - /* Should always return an existent entry. */ - entry = get_or_create_entry(num); - if (ret < 0) { - entry->errors++; - } - g_mutex_unlock(&lock); - } else { - g_autofree gchar *out = g_strdup_printf( - "syscall #%" PRIi64 " returned -> %" PRIi64 "\n", num, ret); - qemu_plugin_outs(out); - } -} - -static void print_entry(gpointer val, gpointer user_data) -{ - SyscallStats *entry = (SyscallStats *) val; - int64_t syscall_num = entry->num; - g_autofree gchar *out = g_strdup_printf( - "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n", - syscall_num, entry->calls, entry->errors); - qemu_plugin_outs(out); -} - -static gint comp_func(gconstpointer ea, gconstpointer eb) -{ - SyscallStats *ent_a = (SyscallStats *) ea; - SyscallStats *ent_b = (SyscallStats *) eb; - - return ent_a->calls > ent_b->calls ? -1 : 1; -} - -/* ************************************************************************* */ -static void plugin_exit(qemu_plugin_id_t id, void *p) -{ - if (!statistics) { - return; - } - - g_mutex_lock(&lock); - GList *entries = g_hash_table_get_values(statistics); - entries = g_list_sort(entries, comp_func); - qemu_plugin_outs("syscall no. calls errors\n"); - - g_list_foreach(entries, print_entry, NULL); - - g_list_free(entries); - g_hash_table_destroy(statistics); - g_mutex_unlock(&lock); -} - -QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, - const qemu_info_t *info, - int argc, char **argv) -{ - bool do_print = false; - - for (int i = 0; i < argc; i++) { - char *opt = argv[i]; - g_autofree char **tokens = g_strsplit(opt, "=", 2); - - if (g_strcmp0(tokens[0], "print") == 0) { - if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) { - fprintf(stderr, "boolean argument parsing failed: %s\n", opt); - } - } else { - fprintf(stderr, "unsupported argument: %s\n", argv[i]); - return -1; - } - } - - if (!do_print) { - statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); - } - - qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); - qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); - qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); - return 0; -} diff --git a/tests/qapi-schema/alternate-array.out b/tests/qapi-schema/alternate-array.out index a657d85738..2f30973ac3 100644 --- a/tests/qapi-schema/alternate-array.out +++ b/tests/qapi-schema/alternate-array.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum 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/comments.out b/tests/qapi-schema/comments.out index ce4f6a4f0f..937070c2c4 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum 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..711a0d629c 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 'Errors:' diff --git a/tests/qapi-schema/doc-empty-section.json b/tests/qapi-schema/doc-empty-section.json index f3384e9a3b..f179d3eff6 100644 --- a/tests/qapi-schema/doc-empty-section.json +++ b/tests/qapi-schema/doc-empty-section.json @@ -3,6 +3,6 @@ ## # @foo: # -# Note: +# Errors: ## { 'command': 'foo', 'data': {'a': 'int'} } diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 74745fb405..f64bf38d85 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* @@ -51,10 +55,12 @@ # - {braces} ## +# Not a doc comment + ## # @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 +79,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 +90,9 @@ # # A paragraph # -# Another paragraph (but no @var: line) +# Another paragraph +# +# @var1 is undocumented # # Features: # @variant1-feat: a feature @@ -118,7 +127,8 @@ ## # @Alternate: # -# @i: an integer +# @i: description starts on the same line +# remainder indented the same # @b is undocumented # # Features: @@ -136,30 +146,49 @@ ## # @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 +# +# .. note:: @arg3 is undocumented +# # Returns: @Object +# +# Errors: some +# # TODO: frobnicate -# Notes: # -# - Lorem ipsum dolor sit amet -# - Ut enim ad minim veniam +# .. admonition:: Notes # -# Duis aute irure dolor -# Example: +# - Lorem ipsum dolor sit amet +# - Ut enim ad minim veniam +# +# Duis aute irure dolor +# +# .. qmp-example:: +# :title: Ideal fast-food burger situation +# +# -> "in" +# <- "out" +# +# Examples:: +# +# - Not a QMP code block +# - Merely a preformatted code block literal +# It isn't even an rST list. +# - *verbatim* +# - {braces} +# +# Note:: +# Ceci n'est pas une note # -# -> in -# <- out -# Examples: -# - *verbatim* -# - {braces} # Since: 2.10 ## { 'command': 'cmd', @@ -170,14 +199,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 +# .. qmp-example:: # -# <- out +# -> "this example" +# +# <- "has no title" ## { '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..ec277be91e 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum @@ -104,7 +103,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 +116,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,8 +143,9 @@ doc symbol=Alternate body= arg=i -an integer -@b is undocumented +description starts on the same line + remainder indented the same + @b is undocumented arg=b feature=alt-feat @@ -154,33 +157,49 @@ 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 a feature feature=cmd-feat2 another feature - section=Note -@arg3 is undocumented + section=None +.. note:: @arg3 is undocumented section=Returns @Object + section=Errors +some section=TODO frobnicate - section=Notes -- Lorem ipsum dolor sit amet -- Ut enim ad minim veniam + section=None +.. admonition:: Notes -Duis aute irure dolor - section=Example --> in -<- out - section=Examples -- *verbatim* -- {braces} + - Lorem ipsum dolor sit amet + - Ut enim ad minim veniam + + Duis aute irure dolor + +.. qmp-example:: + :title: Ideal fast-food burger situation + + -> "in" + <- "out" + +Examples:: + + - Not a QMP code block + - Merely a preformatted code block literal + It isn't even an rST list. + - *verbatim* + - {braces} + +Note:: + Ceci n'est pas une note section=Since 2.10 doc symbol=cmd-boxed @@ -190,10 +209,12 @@ If you're bored enough to read this, go see a video of boxed cats a feature feature=cmd-feat2 another feature - section=Example --> in + section=None +.. qmp-example:: -<- out + -> "this example" + + <- "has no title" doc symbol=EVT_BOXED body= diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index b3b76bd43f..cb37db606a 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 @@ -190,11 +193,9 @@ Features "cmd-feat2" another feature +Note: -Note -~~~~ - -"arg3" is undocumented + "arg3" is undocumented Returns @@ -203,14 +204,12 @@ Returns "Object" -TODO -~~~~ +Errors +~~~~~~ -frobnicate +some - -Notes -~~~~~ +Notes: * Lorem ipsum dolor sit amet @@ -218,20 +217,22 @@ Notes Duis aute irure dolor +Example: Ideal fast-food burger situation: -Example -~~~~~~~ + -> "in" + <- "out" - -> in - <- out - - -Examples -~~~~~~~~ +Examples: + - Not a QMP code block + - Merely a preformatted code block literal + It isn't even an rST list. - *verbatim* - {braces} +Note:: + Ceci n'est pas une note + Since ~~~~~ @@ -259,13 +260,11 @@ Features "cmd-feat2" another feature +Example:: -Example -~~~~~~~ + -> "this example" - -> in - - <- out + <- "has no title" "EVT_BOXED" (Event) 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-interleaved-section.json b/tests/qapi-schema/doc-interleaved-section.json index adb29e98da..eec01ed565 100644 --- a/tests/qapi-schema/doc-interleaved-section.json +++ b/tests/qapi-schema/doc-interleaved-section.json @@ -10,7 +10,7 @@ # # bao # -# Note: a section. +# TODO: a section. # # @foobar: catch this # 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/empty.out b/tests/qapi-schema/empty.out index 3feb3f69d3..d1981f8586 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum 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/include-repetition.out b/tests/qapi-schema/include-repetition.out index 16dbd9b819..c564d27862 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 48e923bfbc..ec8200ab18 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 6a30ded3fa..a7c22c3eef 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 406bc7255d..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', @@ -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..4617eb4e98 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum @@ -105,6 +104,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 +274,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 +291,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 +303,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 +311,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..7e3f9f4aa1 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -48,7 +48,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) def visit_object_type(self, name, info, ifcond, features, - base, members, variants): + base, members, branches): print('object %s' % name) if base: print(' base %s' % base.name) @@ -57,13 +57,14 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): % (m.name, m.type.name, m.optional)) self._print_if(m.ifcond, 8) self._print_features(m.features, indent=8) - self._print_variants(variants) + self._print_variants(branches) self._print_if(ifcond) self._print_features(features) - def visit_alternate_type(self, name, info, ifcond, features, variants): + def visit_alternate_type(self, name, info, ifcond, features, + alternatives): print('alternate %s' % name) - self._print_variants(variants) + self._print_variants(alternatives) self._print_if(ifcond) self._print_features(features) @@ -130,18 +131,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 +206,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 +218,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..b29c76e161 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 | grep -v '^backing file format:' + +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..3d1e31927a 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -171,4 +171,76 @@ 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 + +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 <&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts echo $QEMU_IMG amend -o "data_file=" --image-opts "data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -_img_info --format-specific +$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt +$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts echo diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 139fc68177..24c33add7c 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -545,7 +545,9 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: data-file can only be set for images that use an external data file Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'foo': No such file or directory +qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'foo': No such file or directory +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) @@ -560,7 +562,9 @@ Format specific information: corrupt: false extended l2: false -qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'data-file' is required for this image +qemu-io: can't open device TEST_DIR/t.IMGFMT: 'data-file' is required for this image +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) image: TEST_DIR/t.IMGFMT file format: IMGFMT virtual size: 64 MiB (67108864 bytes) diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index b724c89c7c..b76701c71e 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -56,7 +56,7 @@ class TestQemuImgInfo(TestImageInfoSpecific): def test_human(self): data = qemu_img('info', '--output=human', test_img).stdout.split('\n') data = data[(data.index('Format specific information:') + 1) - :data.index('')] + :data.index("Child node '/file':")] for field in data: self.assertTrue(re.match('^ {4}[^ ]', field) is not None) data = [line.strip() for line in data] diff --git a/tests/qemu-iotests/066 b/tests/qemu-iotests/066 index cf63144cb9..336d8565dd 100755 --- a/tests/qemu-iotests/066 +++ b/tests/qemu-iotests/066 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qcow2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file # We need zero clusters and snapshots # (TODO: Consider splitting the snapshot part into a separate test # file, so this one runs with refcount_bits=1 and data_file) diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index 27bc7305bf..331f8cfddc 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -41,7 +41,7 @@ _supported_fmt qcow2 _supported_proto file fuse _require_drivers blkdebug blkverify # blkdebug can only inject errors on bs->file, not on the data_file, -# so thie test does not work with external data files +# 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.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 a847692b4c..3119100e78 100755 --- a/tests/qemu-iotests/131 +++ b/tests/qemu-iotests/131 @@ -44,31 +44,87 @@ _supported_os Linux inuse_offset=$((0x2c)) size=$((64 * 1024 * 1024)) -CLUSTER_SIZE=64k 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. @@ -83,7 +139,7 @@ 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 64k' "$TEST_IMG.base" | _filter_qemu_io +$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 @@ -99,8 +155,8 @@ QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT \ $QEMU_IO --image-opts "$imgopts" \ -c 'read -P 1 0 64' \ - -c "read -P 42 64 $((64 * 1024 - 64))" \ - -c "read -P 0 64k $((size - 64 * 1024))" \ + -c "read -P 42 64 $((CLUSTER_SIZE - 64))" \ + -c "read -P 0 $CLUSTER_SIZE $((size - CLUSTER_SIZE))" \ | _filter_qemu_io # success, all done diff --git a/tests/qemu-iotests/131.out b/tests/qemu-iotests/131.out index de5ef7a8f5..86a2d2a49b 100644 --- a/tests/qemu-iotests/131.out +++ b/tests/qemu-iotests/131.out @@ -1,53 +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 65536/65536 bytes at offset 0 -64 KiB, 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) 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 65472/65472 bytes at offset 64 -63.938 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 67043328/67043328 bytes at offset 65536 -63.938 MiB, 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 b4d1bc2553..f2ff9c5dac 100755 --- a/tests/qemu-iotests/151 +++ b/tests/qemu-iotests/151 @@ -79,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): @@ -125,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 @@ -151,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 @@ -189,8 +186,7 @@ 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 @@ -211,7 +207,7 @@ class TestThrottledWithNbdExportBase(iotests.QMPTestCase): self.vm = iotests.VM() self.vm.launch() - result = self.vm.qmp('object-add', **{ + self.vm.cmd('object-add', **{ 'qom-type': 'throttle-group', 'id': 'thrgr', 'limits': { @@ -219,9 +215,8 @@ class TestThrottledWithNbdExportBase(iotests.QMPTestCase): 'iops-total-max': self.iops } }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-add', **{ + self.vm.cmd('blockdev-add', **{ 'node-name': 'source-node', 'driver': 'throttle', 'throttle-group': 'thrgr', @@ -233,9 +228,8 @@ class TestThrottledWithNbdExportBase(iotests.QMPTestCase): } } }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-add', **{ + self.vm.cmd('blockdev-add', **{ 'node-name': 'target-node', 'driver': iotests.imgfmt, 'file': { @@ -243,23 +237,20 @@ class TestThrottledWithNbdExportBase(iotests.QMPTestCase): 'filename': target_img } }) - self.assert_qmp(result, 'return', {}) 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}' - result = self.vm.qmp('nbd-server-start', addr={ + self.vm.cmd('nbd-server-start', addr={ 'type': 'unix', 'data': { 'path': self.nbd_sock } }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-export-add', id='exp0', type='nbd', - node_name='source-node', writable=True) - self.assert_qmp(result, 'return', {}) + 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 @@ -312,15 +303,14 @@ class TestLowThrottledWithNbdExport(TestThrottledWithNbdExportBase): # Launch the mirror job mirror_buf_size = 65536 - 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=mirror_buf_size) - 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=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 @@ -408,13 +398,12 @@ class TestHighThrottledWithNbdExport(TestThrottledWithNbdExportBase): # start blockdev-mirror self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') - result = self.vm.qmp('blockdev-mirror', - job_id='mirror', - 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', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking') if __name__ == '__main__': 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/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 deleted file mode 100755 index ee62939e72..0000000000 --- a/tests/qemu-iotests/183 +++ /dev/null @@ -1,147 +0,0 @@ -#!/usr/bin/env bash -# group: rw migration quick -# -# Test old-style block migration (migrate -b) -# -# Copyright (C) 2017 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! - -MIG_SOCKET="${SOCK_DIR}/migrate" - -_cleanup() -{ - rm -f "${MIG_SOCKET}" - _rm_test_img "${TEST_IMG}.dest" - _cleanup_test_img - _cleanup_qemu -} -trap "_cleanup; exit \$status" 0 1 2 3 15 - -# get standard environment, filters and checks -. ./common.rc -. ./common.filter -. ./common.qemu - -_supported_os Linux FreeBSD NetBSD -_supported_fmt qcow2 raw qed quorum -_supported_proto file fuse - -size=64M -_make_test_img $size -TEST_IMG="${TEST_IMG}.dest" _make_test_img $size - -echo -echo === Starting VMs === -echo - -qemu_comm_method="qmp" - -_launch_qemu \ - -drive file="${TEST_IMG}",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk -src=$QEMU_HANDLE -_send_qemu_cmd $src "{ 'execute': 'qmp_capabilities' }" 'return' - -_launch_qemu \ - -drive file="${TEST_IMG}.dest",cache=$CACHEMODE,aio=$AIOMODE,driver=$IMGFMT,id=disk \ - -incoming "unix:${MIG_SOCKET}" -dest=$QEMU_HANDLE -_send_qemu_cmd $dest "{ 'execute': 'qmp_capabilities' }" 'return' - -echo -echo === Write something on the source === -echo - -_send_qemu_cmd $src \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"write -P 0x55 0 64k\"' } }" \ - 'return' -_send_qemu_cmd $src \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"read -P 0x55 0 64k\"' } }" \ - 'return' - -echo -echo === Do block migration to destination === -echo - -reply="$(_send_qemu_cmd $src \ - "{ 'execute': 'migrate', - 'arguments': { 'uri': 'unix:${MIG_SOCKET}', 'blk': true } }" \ - 'return\|error')" -echo "$reply" -if echo "$reply" | grep "compiled without old-style" > /dev/null; then - _notrun "migrate -b support not compiled in" -fi - -timeout_comm=$QEMU_COMM_TIMEOUT -if [ "${VALGRIND_QEMU}" == "y" ]; then - QEMU_COMM_TIMEOUT=4 -else - QEMU_COMM_TIMEOUT=0.1 -fi -qemu_cmd_repeat=50 silent=yes \ - _send_qemu_cmd $src "{ 'execute': 'query-migrate' }" '"status": "completed"' -QEMU_COMM_TIMEOUT=$timeout_comm -_send_qemu_cmd $src "{ 'execute': 'query-status' }" "return" - -echo -echo === Do some I/O on the destination === -echo - -# It is important that we use the BlockBackend of the guest device here instead -# of the node name, which would create a new BlockBackend and not test whether -# the guest has the necessary permissions to access the image now -silent=yes _send_qemu_cmd $dest "" "100 %" -_send_qemu_cmd $dest "{ 'execute': 'query-status' }" "return" -_send_qemu_cmd $dest \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"read -P 0x55 0 64k\"' } }" \ - 'return' -_send_qemu_cmd $dest \ - "{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk \"write -P 0x66 1M 64k\"' } }" \ - 'return' - -echo -echo === Shut down and check image === -echo - -_send_qemu_cmd $src '{"execute":"quit"}' 'return' -_send_qemu_cmd $dest '{"execute":"quit"}' 'return' -wait=1 _cleanup_qemu - -_check_test_img -TEST_IMG="${TEST_IMG}.dest" _check_test_img - -$QEMU_IO -c 'write -P 0x66 1M 64k' "$TEST_IMG" | _filter_qemu_io -$QEMU_IMG compare "$TEST_IMG.dest" "$TEST_IMG" - -# success, all done -echo "*** done" -rm -f $seq.full -status=0 diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out deleted file mode 100644 index fd9c2e52a5..0000000000 --- a/tests/qemu-iotests/183.out +++ /dev/null @@ -1,66 +0,0 @@ -QA output created by 183 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/t.IMGFMT.dest', fmt=IMGFMT size=67108864 - -=== Starting VMs === - -{ 'execute': 'qmp_capabilities' } -{"return": {}} -{ 'execute': 'qmp_capabilities' } -{"return": {}} - -=== Write something on the source === - -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "write -P 0x55 0 64k"' } } -wrote 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "read -P 0x55 0 64k"' } } -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} - -=== Do block migration to destination === - -{ 'execute': 'migrate', - 'arguments': { 'uri': 'unix:SOCK_DIR/migrate', 'blk': true } } -{"return": {}} -{ 'execute': 'query-status' } -{"return": {"status": "postmigrate", "singlestep": false, "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}} -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "read -P 0x55 0 64k"' } } -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} -{ 'execute': 'human-monitor-command', - 'arguments': { 'command-line': - 'qemu-io disk "write -P 0x66 1M 64k"' } } -wrote 65536/65536 bytes at offset 1048576 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -{"return": ""} - -=== Shut down and check image === - -{"execute":"quit"} -{"return": {}} -{"execute":"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 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -Images are identical. -*** done 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/211.out b/tests/qemu-iotests/211.out index f02c75409c..ff9f9a6913 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -17,7 +17,7 @@ file format: IMGFMT virtual size: 128 MiB (134217728 bytes) cluster_size: 1048576 -[{"data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] +[{"compressed": false, "data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] === Successful image creation (explicit defaults) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -35,7 +35,7 @@ file format: IMGFMT virtual size: 64 MiB (67108864 bytes) cluster_size: 1048576 -[{"data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] +[{"compressed": false, "data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] === Successful image creation (with non-default options) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -53,7 +53,7 @@ file format: IMGFMT virtual size: 32 MiB (33554432 bytes) cluster_size: 1048576 -[{"data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] +[{"compressed": false, "data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"compressed": false, "data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] === Invalid BlockdevRef === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}} diff --git a/tests/qemu-iotests/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 26fb347c5d..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: 0xded ( flush fua trim zeroes df multi 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: 0xded ( flush fua trim zeroes df multi 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 378c1b8fb1..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,6 +200,7 @@ 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": true, "rd_total_time_ns": 0, @@ -199,7 +213,11 @@ Testing: -blockdev driver=null-co,read-zeroes=on,node-name=null -device virtio-b "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 }, @@ -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..d498d55e0e 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 @@ -67,8 +69,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == check TLS with authorization == -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort -qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: The TLS connection was non-properly terminated. == check TLS fail over UNIX with no hostname == qemu-img: Could not open 'driver=nbd,path=SOCK_DIR/qemu-nbd.sock,tls-creds=tls0': No hostname for certificate validation @@ -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,20 +96,21 @@ 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. qemu-nbd: TLS handshake failed: The TLS connection was non-properly terminated. == final server log == -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: Verify failed: No certificate was found. qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied qemu-nbd: option negotiation failed: TLS x509 authz check for DISTINGUISHED-NAME is denied -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort -qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: Software caused connection abort +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. +qemu-nbd: option negotiation failed: Failed to read opts magic: Cannot read from TLS channel: The TLS connection was non-properly terminated. qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. qemu-nbd: option negotiation failed: TLS handshake failed: An illegal parameter has been received. *** done diff --git a/tests/qemu-iotests/234.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 b/tests/qemu-iotests/244 index 3e61fa25bb..bb9cc6512f 100755 --- a/tests/qemu-iotests/244 +++ b/tests/qemu-iotests/244 @@ -215,9 +215,22 @@ $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG" $QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG" # blkdebug doesn't support copy offloading, so this tests the error path -$QEMU_IMG amend -f $IMGFMT -o "data_file=blkdebug::$TEST_IMG.data" "$TEST_IMG" -$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG" -$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG" +test_img_with_blkdebug="json:{ + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }, + 'data-file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': '$TEST_IMG.data' + } + } +}" +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$test_img_with_blkdebug" +$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$test_img_with_blkdebug" echo echo "=== Flushing should flush the data file ===" 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..f96610f510 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', {}) + opts = {'driver': 'raw', 'node-name': 'hd', 'file': hd_opts(0), 'discard': 'unmap'} + 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/257.out b/tests/qemu-iotests/257.out index aa76131ca9..c33dd7f3a9 100644 --- a/tests/qemu-iotests/257.out +++ b/tests/qemu-iotests/257.out @@ -120,16 +120,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -596,16 +596,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -865,16 +865,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -1341,16 +1341,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -1610,16 +1610,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -2086,16 +2086,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -2355,16 +2355,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -2831,16 +2831,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -3100,16 +3100,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -3576,16 +3576,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -3845,16 +3845,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -4321,16 +4321,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -4590,16 +4590,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, @@ -5066,16 +5066,16 @@ write -P0x67 0x3fe0000 0x20000 "granularity": 65536, "persistent": false, "recording": false - } - ], - "drive0": [ + }, { "busy": false, "count": 0, "granularity": 65536, "persistent": false, "recording": false - }, + } + ], + "drive0": [ { "busy": false, "count": 458752, 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 289381e315..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,12 +89,10 @@ 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') 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/270 b/tests/qemu-iotests/270 index 74352342db..c37b674aa2 100755 --- a/tests/qemu-iotests/270 +++ b/tests/qemu-iotests/270 @@ -60,8 +60,16 @@ _make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \ # "write" 2G of data without using any space. # (qemu-img create does not like it, though, because null-co does not # support image creation.) -$QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \ - "$TEST_IMG" +test_img_with_null_data="json:{ + 'driver': '$IMGFMT', + 'file': { + 'filename': '$TEST_IMG' + }, + 'data-file': { + 'driver': 'null-co', + 'size':'4294967296' + } +}" # This gives us a range of: # 2^31 - 512 + 768 - 1 = 2^31 + 255 > 2^31 @@ -74,7 +82,7 @@ $QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \ # on L2 boundaries, we need large L2 tables; hence the cluster size of # 2 MB. (Anything from 256 kB should work, though, because then one L2 # table covers 8 GB.) -$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$test_img_with_null_data" | _filter_qemu_io _check_test_img 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 390f05d1b7..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: 0xded ( flush fua trim zeroes df multi 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: 0xded ( flush fua trim zeroes df multi 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..545f9ec7bd 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') @@ -70,7 +84,7 @@ def make_argparser() -> argparse.ArgumentParser: p.set_defaults(imgfmt='raw', imgproto='file') format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2', - 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg'] + 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg', 'vvfat'] g_fmt = p.add_argument_group( ' image format options', 'The following options set the IMGFMT environment variable. ' @@ -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..fc3c64bcb8 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 diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index db757025cb..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,10 +979,15 @@ _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 diff --git a/tests/qemu-iotests/fat16.py b/tests/qemu-iotests/fat16.py new file mode 100644 index 0000000000..7d2d052413 --- /dev/null +++ b/tests/qemu-iotests/fat16.py @@ -0,0 +1,690 @@ +# A simple FAT16 driver that is used to test the `vvfat` driver in QEMU. +# +# Copyright (C) 2024 Amjad Alsharafi +# +# 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 . + +from typing import Callable, List, Optional, Protocol, Set +import string + +SECTOR_SIZE = 512 +DIRENTRY_SIZE = 32 +ALLOWED_FILE_CHARS = set( + "!#$%&'()-@^_`{}~" + string.digits + string.ascii_uppercase +) + + +class MBR: + def __init__(self, data: bytes): + assert len(data) == 512 + self.partition_table = [] + for i in range(4): + partition = data[446 + i * 16 : 446 + (i + 1) * 16] + self.partition_table.append( + { + "status": partition[0], + "start_head": partition[1], + "start_sector": partition[2] & 0x3F, + "start_cylinder": ((partition[2] & 0xC0) << 2) + | partition[3], + "type": partition[4], + "end_head": partition[5], + "end_sector": partition[6] & 0x3F, + "end_cylinder": ((partition[6] & 0xC0) << 2) + | partition[7], + "start_lba": int.from_bytes(partition[8:12], "little"), + "size": int.from_bytes(partition[12:16], "little"), + } + ) + + def __str__(self): + return "\n".join( + [ + f"{i}: {partition}" + for i, partition in enumerate(self.partition_table) + ] + ) + + +class FatBootSector: + # pylint: disable=too-many-instance-attributes + def __init__(self, data: bytes): + assert len(data) == 512 + self.bytes_per_sector = int.from_bytes(data[11:13], "little") + self.sectors_per_cluster = data[13] + self.reserved_sectors = int.from_bytes(data[14:16], "little") + self.fat_count = data[16] + self.root_entries = int.from_bytes(data[17:19], "little") + total_sectors_16 = int.from_bytes(data[19:21], "little") + self.media_descriptor = data[21] + self.sectors_per_fat = int.from_bytes(data[22:24], "little") + self.sectors_per_track = int.from_bytes(data[24:26], "little") + self.heads = int.from_bytes(data[26:28], "little") + self.hidden_sectors = int.from_bytes(data[28:32], "little") + total_sectors_32 = int.from_bytes(data[32:36], "little") + assert ( + total_sectors_16 == 0 or total_sectors_32 == 0 + ), "Both total sectors (16 and 32) fields are non-zero" + self.total_sectors = total_sectors_16 or total_sectors_32 + self.drive_number = data[36] + self.volume_id = int.from_bytes(data[39:43], "little") + self.volume_label = data[43:54].decode("ascii").strip() + self.fs_type = data[54:62].decode("ascii").strip() + + def root_dir_start(self): + """ + Calculate the start sector of the root directory. + """ + return self.reserved_sectors + self.fat_count * self.sectors_per_fat + + def root_dir_size(self): + """ + Calculate the size of the root directory in sectors. + """ + return ( + self.root_entries * DIRENTRY_SIZE + self.bytes_per_sector - 1 + ) // self.bytes_per_sector + + def data_sector_start(self): + """ + Calculate the start sector of the data region. + """ + return self.root_dir_start() + self.root_dir_size() + + def first_sector_of_cluster(self, cluster: int) -> int: + """ + Calculate the first sector of the given cluster. + """ + return ( + self.data_sector_start() + (cluster - 2) * self.sectors_per_cluster + ) + + def cluster_bytes(self): + """ + Calculate the number of bytes in a cluster. + """ + return self.bytes_per_sector * self.sectors_per_cluster + + def __str__(self): + return ( + f"Bytes per sector: {self.bytes_per_sector}\n" + f"Sectors per cluster: {self.sectors_per_cluster}\n" + f"Reserved sectors: {self.reserved_sectors}\n" + f"FAT count: {self.fat_count}\n" + f"Root entries: {self.root_entries}\n" + f"Total sectors: {self.total_sectors}\n" + f"Media descriptor: {self.media_descriptor}\n" + f"Sectors per FAT: {self.sectors_per_fat}\n" + f"Sectors per track: {self.sectors_per_track}\n" + f"Heads: {self.heads}\n" + f"Hidden sectors: {self.hidden_sectors}\n" + f"Drive number: {self.drive_number}\n" + f"Volume ID: {self.volume_id}\n" + f"Volume label: {self.volume_label}\n" + f"FS type: {self.fs_type}\n" + ) + + +class FatDirectoryEntry: + # pylint: disable=too-many-instance-attributes + def __init__(self, data: bytes, sector: int, offset: int): + self.name = data[0:8].decode("ascii").strip() + self.ext = data[8:11].decode("ascii").strip() + self.attributes = data[11] + self.reserved = data[12] + self.create_time_tenth = data[13] + self.create_time = int.from_bytes(data[14:16], "little") + self.create_date = int.from_bytes(data[16:18], "little") + self.last_access_date = int.from_bytes(data[18:20], "little") + high_cluster = int.from_bytes(data[20:22], "little") + self.last_mod_time = int.from_bytes(data[22:24], "little") + self.last_mod_date = int.from_bytes(data[24:26], "little") + low_cluster = int.from_bytes(data[26:28], "little") + self.cluster = (high_cluster << 16) | low_cluster + self.size_bytes = int.from_bytes(data[28:32], "little") + + # extra (to help write back to disk) + self.sector = sector + self.offset = offset + + def as_bytes(self) -> bytes: + return ( + self.name.ljust(8, " ").encode("ascii") + + self.ext.ljust(3, " ").encode("ascii") + + self.attributes.to_bytes(1, "little") + + self.reserved.to_bytes(1, "little") + + self.create_time_tenth.to_bytes(1, "little") + + self.create_time.to_bytes(2, "little") + + self.create_date.to_bytes(2, "little") + + self.last_access_date.to_bytes(2, "little") + + (self.cluster >> 16).to_bytes(2, "little") + + self.last_mod_time.to_bytes(2, "little") + + self.last_mod_date.to_bytes(2, "little") + + (self.cluster & 0xFFFF).to_bytes(2, "little") + + self.size_bytes.to_bytes(4, "little") + ) + + def whole_name(self): + if self.ext: + return f"{self.name}.{self.ext}" + else: + return self.name + + def __str__(self): + return ( + f"Name: {self.name}\n" + f"Ext: {self.ext}\n" + f"Attributes: {self.attributes}\n" + f"Reserved: {self.reserved}\n" + f"Create time tenth: {self.create_time_tenth}\n" + f"Create time: {self.create_time}\n" + f"Create date: {self.create_date}\n" + f"Last access date: {self.last_access_date}\n" + f"Last mod time: {self.last_mod_time}\n" + f"Last mod date: {self.last_mod_date}\n" + f"Cluster: {self.cluster}\n" + f"Size: {self.size_bytes}\n" + ) + + def __repr__(self): + # convert to dict + return str(vars(self)) + + +class SectorReader(Protocol): + def __call__(self, start_sector: int, num_sectors: int = 1) -> bytes: ... + +# pylint: disable=broad-exception-raised +class Fat16: + def __init__( + self, + start_sector: int, + size: int, + sector_reader: SectorReader, + sector_writer: Callable[[int, bytes], None] + ): + self.start_sector = start_sector + self.size_in_sectors = size + self.sector_reader = sector_reader + self.sector_writer = sector_writer + + self.boot_sector = FatBootSector(self.sector_reader(start_sector, 1)) + + fat_size_in_sectors = ( + self.boot_sector.sectors_per_fat * self.boot_sector.fat_count + ) + self.fats = self.read_sectors( + self.boot_sector.reserved_sectors, fat_size_in_sectors + ) + self.fats_dirty_sectors: Set[int] = set() + + def read_sectors(self, start_sector: int, num_sectors: int) -> bytes: + return self.sector_reader(start_sector + self.start_sector, + num_sectors) + + def write_sectors(self, start_sector: int, data: bytes) -> None: + return self.sector_writer(start_sector + self.start_sector, data) + + def directory_from_bytes( + self, data: bytes, start_sector: int + ) -> List[FatDirectoryEntry]: + """ + Convert `bytes` into a list of `FatDirectoryEntry` objects. + Will ignore long file names. + Will stop when it encounters a 0x00 byte. + """ + + entries = [] + for i in range(0, len(data), DIRENTRY_SIZE): + entry = data[i : i + DIRENTRY_SIZE] + + current_sector = start_sector + (i // SECTOR_SIZE) + current_offset = i % SECTOR_SIZE + + if entry[0] == 0: + break + + if entry[0] == 0xE5: + # Deleted file + continue + + if entry[11] & 0xF == 0xF: + # Long file name + continue + + entries.append( + FatDirectoryEntry(entry, current_sector, current_offset) + ) + return entries + + def read_root_directory(self) -> List[FatDirectoryEntry]: + root_dir = self.read_sectors( + self.boot_sector.root_dir_start(), self.boot_sector.root_dir_size() + ) + return self.directory_from_bytes( + root_dir, self.boot_sector.root_dir_start() + ) + + def read_fat_entry(self, cluster: int) -> int: + """ + Read the FAT entry for the given cluster. + """ + fat_offset = cluster * 2 # FAT16 + return int.from_bytes(self.fats[fat_offset : fat_offset + 2], "little") + + def write_fat_entry(self, cluster: int, value: int) -> None: + """ + Write the FAT entry for the given cluster. + """ + fat_offset = cluster * 2 + self.fats = ( + self.fats[:fat_offset] + + value.to_bytes(2, "little") + + self.fats[fat_offset + 2 :] + ) + self.fats_dirty_sectors.add(fat_offset // SECTOR_SIZE) + + def flush_fats(self) -> None: + """ + Write the FATs back to the disk. + """ + for sector in self.fats_dirty_sectors: + data = self.fats[sector * SECTOR_SIZE : (sector + 1) * SECTOR_SIZE] + sector = self.boot_sector.reserved_sectors + sector + self.write_sectors(sector, data) + self.fats_dirty_sectors = set() + + def next_cluster(self, cluster: int) -> Optional[int]: + """ + Get the next cluster in the chain. + If its `None`, then its the last cluster. + The function will crash if the next cluster + is `FREE` (unexpected) or invalid entry. + """ + fat_entry = self.read_fat_entry(cluster) + if fat_entry == 0: + raise Exception("Unexpected: FREE cluster") + if fat_entry == 1: + raise Exception("Unexpected: RESERVED cluster") + if fat_entry >= 0xFFF8: + return None + if fat_entry >= 0xFFF7: + raise Exception("Invalid FAT entry") + + return fat_entry + + def next_free_cluster(self) -> int: + """ + Find the next free cluster. + """ + # simple linear search + for i in range(2, 0xFFFF): + if self.read_fat_entry(i) == 0: + return i + raise Exception("No free clusters") + + def next_free_cluster_non_continuous(self) -> int: + """ + Find the next free cluster, but makes sure + that the cluster before and after it are not allocated. + """ + # simple linear search + before = False + for i in range(2, 0xFFFF): + if self.read_fat_entry(i) == 0: + if before and self.read_fat_entry(i + 1) == 0: + return i + else: + before = True + else: + before = False + + raise Exception("No free clusters") + + def read_cluster(self, cluster: int) -> bytes: + """ + Read the cluster at the given cluster. + """ + return self.read_sectors( + self.boot_sector.first_sector_of_cluster(cluster), + self.boot_sector.sectors_per_cluster, + ) + + def write_cluster(self, cluster: int, data: bytes) -> None: + """ + Write the cluster at the given cluster. + """ + assert len(data) == self.boot_sector.cluster_bytes() + self.write_sectors( + self.boot_sector.first_sector_of_cluster(cluster), + data, + ) + + def read_directory( + self, cluster: Optional[int] + ) -> List[FatDirectoryEntry]: + """ + Read the directory at the given cluster. + """ + entries = [] + while cluster is not None: + data = self.read_cluster(cluster) + entries.extend( + self.directory_from_bytes( + data, self.boot_sector.first_sector_of_cluster(cluster) + ) + ) + cluster = self.next_cluster(cluster) + return entries + + def add_direntry( + self, cluster: Optional[int], name: str, ext: str, attributes: int + ) -> FatDirectoryEntry: + """ + Add a new directory entry to the given cluster. + If the cluster is `None`, then it will be added to the root directory. + """ + + def find_free_entry(data: bytes) -> Optional[int]: + for i in range(0, len(data), DIRENTRY_SIZE): + entry = data[i : i + DIRENTRY_SIZE] + if entry[0] == 0 or entry[0] == 0xE5: + return i + return None + + assert len(name) <= 8, "Name must be 8 characters or less" + assert len(ext) <= 3, "Ext must be 3 characters or less" + assert attributes % 0x15 != 0x15, "Invalid attributes" + + # initial dummy data + new_entry = FatDirectoryEntry(b"\0" * 32, 0, 0) + new_entry.name = name.ljust(8, " ") + new_entry.ext = ext.ljust(3, " ") + new_entry.attributes = attributes + new_entry.reserved = 0 + new_entry.create_time_tenth = 0 + new_entry.create_time = 0 + new_entry.create_date = 0 + new_entry.last_access_date = 0 + new_entry.last_mod_time = 0 + new_entry.last_mod_date = 0 + new_entry.cluster = self.next_free_cluster() + new_entry.size_bytes = 0 + + # mark as EOF + self.write_fat_entry(new_entry.cluster, 0xFFFF) + + if cluster is None: + for i in range(self.boot_sector.root_dir_size()): + sector_data = self.read_sectors( + self.boot_sector.root_dir_start() + i, 1 + ) + offset = find_free_entry(sector_data) + if offset is not None: + new_entry.sector = self.boot_sector.root_dir_start() + i + new_entry.offset = offset + self.update_direntry(new_entry) + return new_entry + else: + while cluster is not None: + data = self.read_cluster(cluster) + offset = find_free_entry(data) + if offset is not None: + new_entry.sector = ( + self.boot_sector.first_sector_of_cluster(cluster) + + (offset // SECTOR_SIZE)) + new_entry.offset = offset % SECTOR_SIZE + self.update_direntry(new_entry) + return new_entry + cluster = self.next_cluster(cluster) + + raise Exception("No free directory entries") + + def update_direntry(self, entry: FatDirectoryEntry) -> None: + """ + Write the directory entry back to the disk. + """ + sector = self.read_sectors(entry.sector, 1) + sector = ( + sector[: entry.offset] + + entry.as_bytes() + + sector[entry.offset + DIRENTRY_SIZE :] + ) + self.write_sectors(entry.sector, sector) + + def find_direntry(self, path: str) -> Optional[FatDirectoryEntry]: + """ + Find the directory entry for the given path. + """ + assert path[0] == "/", "Path must start with /" + + path = path[1:] # remove the leading / + parts = path.split("/") + directory = self.read_root_directory() + + current_entry = None + + for i, part in enumerate(parts): + is_last = i == len(parts) - 1 + + for entry in directory: + if entry.whole_name() == part: + current_entry = entry + break + if current_entry is None: + return None + + if is_last: + return current_entry + + if current_entry.attributes & 0x10 == 0: + raise Exception( + f"{current_entry.whole_name()} is not a directory" + ) + + directory = self.read_directory(current_entry.cluster) + + assert False, "Exited loop with is_last == False" + + def read_file(self, entry: Optional[FatDirectoryEntry]) -> Optional[bytes]: + """ + Read the content of the file at the given path. + """ + if entry is None: + return None + if entry.attributes & 0x10 != 0: + raise Exception(f"{entry.whole_name()} is a directory") + + data = b"" + cluster: Optional[int] = entry.cluster + while cluster is not None and len(data) <= entry.size_bytes: + data += self.read_cluster(cluster) + cluster = self.next_cluster(cluster) + return data[: entry.size_bytes] + + def truncate_file( + self, + entry: FatDirectoryEntry, + new_size: int, + allocate_non_continuous: bool = False, + ) -> None: + """ + Truncate the file at the given path to the new size. + """ + if entry is None: + raise Exception("entry is None") + if entry.attributes & 0x10 != 0: + raise Exception(f"{entry.whole_name()} is a directory") + + def clusters_from_size(size: int) -> int: + return ( + size + self.boot_sector.cluster_bytes() - 1 + ) // self.boot_sector.cluster_bytes() + + # First, allocate new FATs if we need to + required_clusters = clusters_from_size(new_size) + current_clusters = clusters_from_size(entry.size_bytes) + + affected_clusters = set() + + # Keep at least one cluster, easier to manage this way + if required_clusters == 0: + required_clusters = 1 + if current_clusters == 0: + current_clusters = 1 + + cluster: Optional[int] + + if required_clusters > current_clusters: + # Allocate new clusters + cluster = entry.cluster + to_add = required_clusters + for _ in range(current_clusters - 1): + to_add -= 1 + assert cluster is not None, "Cluster is None" + affected_clusters.add(cluster) + cluster = self.next_cluster(cluster) + assert required_clusters > 0, "No new clusters to allocate" + assert cluster is not None, "Cluster is None" + assert ( + self.next_cluster(cluster) is None + ), "Cluster is not the last cluster" + + # Allocate new clusters + for _ in range(to_add - 1): + if allocate_non_continuous: + new_cluster = self.next_free_cluster_non_continuous() + else: + new_cluster = self.next_free_cluster() + self.write_fat_entry(cluster, new_cluster) + self.write_fat_entry(new_cluster, 0xFFFF) + cluster = new_cluster + + elif required_clusters < current_clusters: + # Truncate the file + cluster = entry.cluster + for _ in range(required_clusters - 1): + assert cluster is not None, "Cluster is None" + cluster = self.next_cluster(cluster) + assert cluster is not None, "Cluster is None" + + next_cluster = self.next_cluster(cluster) + # mark last as EOF + self.write_fat_entry(cluster, 0xFFFF) + # free the rest + while next_cluster is not None: + cluster = next_cluster + next_cluster = self.next_cluster(next_cluster) + self.write_fat_entry(cluster, 0) + + self.flush_fats() + + # verify number of clusters + cluster = entry.cluster + count = 0 + while cluster is not None: + count += 1 + affected_clusters.add(cluster) + cluster = self.next_cluster(cluster) + assert ( + count == required_clusters + ), f"Expected {required_clusters} clusters, got {count}" + + # update the size + entry.size_bytes = new_size + self.update_direntry(entry) + + # trigger every affected cluster + for cluster in affected_clusters: + first_sector = self.boot_sector.first_sector_of_cluster(cluster) + first_sector_data = self.read_sectors(first_sector, 1) + self.write_sectors(first_sector, first_sector_data) + + def write_file(self, entry: FatDirectoryEntry, data: bytes) -> None: + """ + Write the content of the file at the given path. + """ + if entry is None: + raise Exception("entry is None") + if entry.attributes & 0x10 != 0: + raise Exception(f"{entry.whole_name()} is a directory") + + data_len = len(data) + + self.truncate_file(entry, data_len) + + cluster: Optional[int] = entry.cluster + while cluster is not None: + data_to_write = data[: self.boot_sector.cluster_bytes()] + if len(data_to_write) < self.boot_sector.cluster_bytes(): + old_data = self.read_cluster(cluster) + data_to_write += old_data[len(data_to_write) :] + + self.write_cluster(cluster, data_to_write) + data = data[self.boot_sector.cluster_bytes() :] + if len(data) == 0: + break + cluster = self.next_cluster(cluster) + + assert ( + len(data) == 0 + ), "Data was not written completely, clusters missing" + + def create_file(self, path: str) -> Optional[FatDirectoryEntry]: + """ + Create a new file at the given path. + """ + assert path[0] == "/", "Path must start with /" + + path = path[1:] # remove the leading / + + parts = path.split("/") + + directory_cluster = None + directory = self.read_root_directory() + + parts, filename = parts[:-1], parts[-1] + + for _, part in enumerate(parts): + current_entry = None + for entry in directory: + if entry.whole_name() == part: + current_entry = entry + break + if current_entry is None: + return None + + if current_entry.attributes & 0x10 == 0: + raise Exception( + f"{current_entry.whole_name()} is not a directory" + ) + + directory = self.read_directory(current_entry.cluster) + directory_cluster = current_entry.cluster + + # add new entry to the directory + + filename, ext = filename.split(".") + + if len(ext) > 3: + raise Exception("Ext must be 3 characters or less") + if len(filename) > 8: + raise Exception("Name must be 8 characters or less") + + for c in filename + ext: + + if c not in ALLOWED_FILE_CHARS: + raise Exception("Invalid character in filename") + + return self.add_direntry(directory_cluster, filename, ext, 0) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index da7d6637e1..19817c7353 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') @@ -1590,10 +1614,13 @@ class ReproducibleStreamWrapper: self.stream.write(arg) class ReproducibleTestRunner(unittest.TextTestRunner): - def __init__(self, stream: Optional[TextIO] = None, - resultclass: Type[unittest.TestResult] = - ReproducibleTestResult, - **kwargs: Any) -> None: + def __init__( + self, + stream: Optional[TextIO] = None, + resultclass: Type[unittest.TextTestResult] = + ReproducibleTestResult, + **kwargs: Any + ) -> None: rstream = ReproducibleStreamWrapper(stream or sys.stdout) super().__init__(stream=rstream, # type: ignore descriptions=True, 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/pylintrc b/tests/qemu-iotests/pylintrc index f4f823a991..c5f4833e45 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -13,13 +13,14 @@ disable=invalid-name, no-else-return, too-few-public-methods, too-many-arguments, + too-many-positional-arguments, too-many-branches, too-many-lines, too-many-locals, 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. @@ -55,4 +56,4 @@ max-line-length=79 [SIMILARITIES] -min-similarity-lines=6 +min-similarity-lines=10 diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index a864c74b12..6326e46b7b 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -25,7 +25,12 @@ import collections import random import subprocess import glob -from typing import List, Dict, Any, Optional, ContextManager +from typing import List, Dict, Any, Optional + +if sys.version_info >= (3, 9): + from contextlib import AbstractContextManager as ContextManager +else: + from typing import ContextManager DEF_GDB_OPTIONS = 'localhost:12345' @@ -40,7 +45,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 +131,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 +175,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 +218,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_', @@ -235,9 +240,12 @@ class TestEnv(ContextManager['TestEnv']): ('aarch64', 'virt'), ('avr', 'mega2560'), ('m68k', 'virt'), + ('or1k', 'virt'), ('riscv32', 'virt'), ('riscv64', 'virt'), ('rx', 'gdbsim-r5f562n8'), + ('sh4', 'r2d'), + ('sh4eb', 'r2d'), ('tricore', 'tricore_testboard') ) for suffix, machine in machine_map: @@ -250,7 +258,7 @@ class TestEnv(ContextManager['TestEnv']): self.qemu_img_options = os.getenv('QEMU_IMG_OPTIONS') self.qemu_nbd_options = os.getenv('QEMU_NBD_OPTIONS') - is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg'] + is_generic = self.imgfmt not in ['bochs', 'cloop', 'dmg', 'vvfat'] self.imgfmt_generic = 'true' if is_generic else 'false' self.qemu_io_options = f'--cache {self.cachemode} --aio {self.aiomode}' diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 5a771da86e..2e236c8fa3 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -24,16 +24,17 @@ 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, \ - ContextManager - +from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv +if sys.version_info >= (3, 9): + from contextlib import AbstractContextManager as ContextManager +else: + from typing import ContextManager + def silent_unlink(path: Path) -> None: try: @@ -56,22 +57,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 +154,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 +231,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 +258,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 +278,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 +350,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 +376,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 index 4eccc80153..b0e19839db 100755 --- a/tests/qemu-iotests/tests/backing-file-invalidation +++ b/tests/qemu-iotests/tests/backing-file-invalidation @@ -129,12 +129,11 @@ class TestPostMigrateFilename(iotests.QMPTestCase): # For good measure, try creating an overlay and check its backing # chain below. This is how the issue was originally found. - result = self.vm_d.qmp('blockdev-snapshot-sync', - format=iotests.imgfmt, - snapshot_file=imgs[3], - node_name='node0', - snapshot_node_name='node0-overlay') - self.assert_qmp(result, 'return', {}) + 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 diff --git a/tests/qemu-iotests/tests/backup-discard-source b/tests/qemu-iotests/tests/backup-discard-source new file mode 100755 index 0000000000..17fef9c6d3 --- /dev/null +++ b/tests/qemu-iotests/tests/backup-discard-source @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Test backup discard-source parameter +# +# Copyright (c) Virtuozzo International GmbH. +# Copyright (c) Yandex +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os + +import iotests +from iotests import qemu_img_create, qemu_img_map, qemu_io + + +temp_img = os.path.join(iotests.test_dir, 'temp') +source_img = os.path.join(iotests.test_dir, 'source') +target_img = os.path.join(iotests.test_dir, 'target') +size = 1024 * 1024 + + +class TestBackup(iotests.QMPTestCase): + def setUp(self): + qemu_img_create('-f', iotests.imgfmt, source_img, str(size)) + qemu_img_create('-f', iotests.imgfmt, temp_img, str(size)) + qemu_img_create('-f', iotests.imgfmt, target_img, str(size)) + qemu_io('-c', 'write 0 1M', source_img) + + self.vm = iotests.VM() + self.vm.launch() + + self.vm.cmd('blockdev-add', { + 'node-name': 'cbw', + 'driver': 'copy-before-write', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img, + } + }, + 'target': { + 'driver': iotests.imgfmt, + 'discard': 'unmap', + 'node-name': 'temp', + 'file': { + 'driver': 'file', + 'filename': temp_img + } + } + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'access', + 'discard': 'unmap', + 'driver': 'snapshot-access', + 'file': 'cbw' + }) + + self.vm.cmd('blockdev-add', { + 'driver': iotests.imgfmt, + 'node-name': 'target', + 'file': { + 'driver': 'file', + 'filename': target_img + } + }) + + self.bitmap = { + 'node': 'temp', + 'name': 'bitmap0' + } + + self.vm.cmd('block-dirty-bitmap-add', self.bitmap) + + def tearDown(self): + # That should fail, because region is discarded + self.vm.hmp_qemu_io('access', 'read 0 1M') + + self.vm.shutdown() + + self.assertTrue('read failed: Permission denied' in self.vm.get_log()) + + # Final check that temp image is empty + mapping = qemu_img_map(temp_img) + self.assertEqual(len(mapping), 1) + self.assertEqual(mapping[0]['start'], 0) + self.assertEqual(mapping[0]['length'], size) + self.assertEqual(mapping[0]['data'], False) + + os.remove(temp_img) + os.remove(source_img) + os.remove(target_img) + + def do_backup(self): + self.vm.cmd('blockdev-backup', device='access', + sync='full', target='target', + job_id='backup0', + discard_source=True) + + self.vm.event_wait(name='BLOCK_JOB_COMPLETED') + + def get_bitmap_count(self): + nodes = self.vm.cmd('query-named-block-nodes', flat=True) + temp = next(n for n in nodes if n['node-name'] == 'temp') + bitmap = temp['dirty-bitmaps'][0] + assert bitmap['name'] == self.bitmap['name'] + return bitmap['count'] + + def test_discard_written(self): + """ + 1. Guest writes + 2. copy-before-write operation, data is stored to temp + 3. start backup(discard_source=True), check that data is + removed from temp + """ + # Trigger copy-before-write operation + result = self.vm.hmp_qemu_io('cbw', 'write 0 1M') + self.assert_qmp(result, 'return', '') + + # Check that data is written to temporary image + self.assertEqual(self.get_bitmap_count(), size) + + self.do_backup() + + def test_discard_cbw(self): + """ + 1. do backup(discard_source=True), which should inform + copy-before-write that data is not needed anymore + 2. Guest writes + 3. Check that copy-before-write operation is not done + """ + self.do_backup() + + # backup job did discard operation and pollute the bitmap, + # we have to clean the bitmap, to check next write + self.assertEqual(self.get_bitmap_count(), size) + self.vm.cmd('block-dirty-bitmap-clear', self.bitmap) + + # Try trigger copy-before-write operation + result = self.vm.hmp_qemu_io('cbw', 'write 0 1M') + self.assert_qmp(result, 'return', '') + + # Check that data is not written to temporary image, as region + # is discarded from copy-before-write process + self.assertEqual(self.get_bitmap_count(), 0) + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/backup-discard-source.out b/tests/qemu-iotests/tests/backup-discard-source.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/tests/backup-discard-source.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write index 2ffe092b31..d33bea577d 100755 --- a/tests/qemu-iotests/tests/copy-before-write +++ b/tests/qemu-iotests/tests/copy-before-write @@ -44,12 +44,11 @@ class TestCbwError(iotests.QMPTestCase): opts = ['-nodefaults', '-display', 'none', '-machine', 'none'] self.vm = QEMUMachine(iotests.qemu_prog, opts, - base_temp_dir=iotests.test_dir, - sock_dir=iotests.sock_dir) + base_temp_dir=iotests.test_dir) self.vm.launch() def do_cbw_error(self, on_cbw_error): - result = self.vm.qmp('blockdev-add', { + self.vm.cmd('blockdev-add', { 'node-name': 'cbw', 'driver': 'copy-before-write', 'on-cbw-error': on_cbw_error, @@ -79,14 +78,12 @@ class TestCbwError(iotests.QMPTestCase): } } }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-add', { + self.vm.cmd('blockdev-add', { 'node-name': 'access', 'driver': 'snapshot-access', 'file': 'cbw' }) - self.assert_qmp(result, 'return', {}) result = self.vm.qmp('human-monitor-command', command_line='qemu-io cbw "write 0 1M"') @@ -130,14 +127,13 @@ read 1048576/1048576 bytes at offset 0 """) def do_cbw_timeout(self, on_cbw_error): - result = self.vm.qmp('object-add', { + self.vm.cmd('object-add', { 'qom-type': 'throttle-group', 'id': 'group0', 'limits': {'bps-write': 300 * 1024} }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-add', { + self.vm.cmd('blockdev-add', { 'node-name': 'cbw', 'driver': 'copy-before-write', 'on-cbw-error': on_cbw_error, @@ -161,14 +157,12 @@ read 1048576/1048576 bytes at offset 0 } } }) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('blockdev-add', { + self.vm.cmd('blockdev-add', { 'node-name': 'access', 'driver': 'snapshot-access', 'file': 'cbw' }) - self.assert_qmp(result, 'return', {}) result = self.vm.qmp('human-monitor-command', command_line='qemu-io cbw "write 0 512K"') 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/nbd-tls-iothread b/tests/qemu-iotests/tests/nbd-tls-iothread new file mode 100755 index 0000000000..a2fb07206e --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-tls-iothread @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test of NBD+TLS+iothread +# +# 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=eblake@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$dst_image" + tls_x509_cleanup +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu +. ./common.tls +. ./common.nbd + +_supported_fmt qcow2 # Hardcoded to qcow2 command line and QMP below +_supported_proto file + +# pick_unused_port +# +# Picks and returns an "unused" port, setting the global variable +# $port. +# +# This is inherently racy, but we need it because qemu does not currently +# permit NBD+TLS over a Unix domain socket +pick_unused_port () +{ + if ! (ss --version) >/dev/null 2>&1; then + _notrun "ss utility required, skipped this test" + fi + + # Start at a random port to make it less likely that two parallel + # tests will conflict. + port=$(( 50000 + (RANDOM%15000) )) + while ss -ltn | grep -sqE ":$port\b"; do + ((port++)) + if [ $port -eq 65000 ]; then port=50000; fi + done + echo picked unused port +} + +tls_x509_init + +size=1G +DST_IMG="$TEST_DIR/dst.qcow2" + +echo +echo "== preparing TLS creds and spare port ==" + +pick_unused_port +tls_x509_create_root_ca "ca1" +tls_x509_create_server "ca1" "server1" +tls_x509_create_client "ca1" "client1" +tls_obj_base=tls-creds-x509,id=tls0,verify-peer=true,dir="${tls_dir}" + +echo +echo "== preparing image ==" + +_make_test_img $size +$QEMU_IMG create -f qcow2 "$DST_IMG" $size | _filter_img_create + +echo +echo === Starting Src QEMU === +echo + +_launch_qemu -machine q35 \ + -object iothread,id=iothread0 \ + -object "${tls_obj_base}"/client1,endpoint=client \ + -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true, + "bus":"pcie.0"}' \ + -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0", + "bus":"root0", "iothread":"iothread0"}' \ + -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1", + "bus":"virtio_scsi_pci0.0"}' \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$TEST_IMG"'", "node-name":"drive_sys1"}' \ + -blockdev '{"driver":"qcow2", "node-name":"drive_image1", + "file":"drive_sys1"}' +h1=$QEMU_HANDLE +_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return' + +echo +echo === Starting Dst VM2 === +echo + +_launch_qemu -machine q35 \ + -object iothread,id=iothread0 \ + -object "${tls_obj_base}"/server1,endpoint=server \ + -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true, + "bus":"pcie.0"}' \ + -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0", + "bus":"root0", "iothread":"iothread0"}' \ + -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1", + "bus":"virtio_scsi_pci0.0"}' \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$DST_IMG"'", "node-name":"drive_sys1"}' \ + -blockdev '{"driver":"qcow2", "node-name":"drive_image1", + "file":"drive_sys1"}' \ + -incoming defer +h2=$QEMU_HANDLE +_send_qemu_cmd $h2 '{"execute": "qmp_capabilities"}' 'return' + +echo +echo === Dst VM: Enable NBD server for incoming storage migration === +echo + +_send_qemu_cmd $h2 '{"execute": "nbd-server-start", "arguments": + {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": "'$port'"}}, + "tls-creds": "tls0"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g" +_send_qemu_cmd $h2 '{"execute": "block-export-add", "arguments": + {"node-name": "drive_image1", "type": "nbd", "writable": true, + "id": "drive_image1"}}' '{"return": {}}' + +echo +echo === Src VM: Mirror to dst NBD for outgoing storage migration === +echo + +_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments": + {"node-name": "mirror", "driver": "nbd", + "server": {"type": "inet", "host": "127.0.0.1", "port": "'$port'"}, + "export": "drive_image1", "tls-creds": "tls0", + "tls-hostname": "127.0.0.1"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g" +_send_qemu_cmd $h1 '{"execute": "blockdev-mirror", "arguments": + {"sync": "full", "device": "drive_image1", "target": "mirror", + "job-id": "drive_image1_53"}}' '{"return": {}}' +_timed_wait_for $h1 '"ready"' + +echo +echo === Cleaning up === +echo + +_send_qemu_cmd $h1 '{"execute":"quit"}' '' +_send_qemu_cmd $h2 '{"execute":"quit"}' '' + +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread.out b/tests/qemu-iotests/tests/nbd-tls-iothread.out new file mode 100644 index 0000000000..1d83d4f903 --- /dev/null +++ b/tests/qemu-iotests/tests/nbd-tls-iothread.out @@ -0,0 +1,54 @@ +QA output created by nbd-tls-iothread + +== preparing TLS creds and spare port == +picked unused port +Generating a self signed certificate... +Generating a signed certificate... +Generating a signed certificate... + +== preparing image == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 +Formatting 'TEST_DIR/dst.IMGFMT', fmt=IMGFMT size=1073741824 + +=== Starting Src QEMU === + +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Starting Dst VM2 === + +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Dst VM: Enable NBD server for incoming storage migration === + +{"execute": "nbd-server-start", "arguments": + {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": PORT}}, + "tls-creds": "tls0"}} +{"return": {}} +{"execute": "block-export-add", "arguments": + {"node-name": "drive_image1", "type": "nbd", "writable": true, + "id": "drive_image1"}} +{"return": {}} + +=== Src VM: Mirror to dst NBD for outgoing storage migration === + +{"execute": "blockdev-add", "arguments": + {"node-name": "mirror", "driver": "nbd", + "server": {"type": "inet", "host": "127.0.0.1", "port": PORT}, + "export": "drive_image1", "tls-creds": "tls0", + "tls-hostname": "127.0.0.1"}} +{"return": {}} +{"execute": "blockdev-mirror", "arguments": + {"sync": "full", "device": "drive_image1", "target": "mirror", + "job-id": "drive_image1_53"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "drive_image1_53"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "drive_image1_53"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "drive_image1_53"}} + +=== Cleaning up === + +{"execute":"quit"} +{"execute":"quit"} +*** done 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 index 8d2d9e1684..1a50b682fc 100755 --- a/tests/qemu-iotests/tests/stream-under-throttle +++ b/tests/qemu-iotests/tests/stream-under-throttle @@ -88,6 +88,8 @@ class TestStreamWithThrottle(iotests.QMPTestCase): '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: @@ -100,10 +102,9 @@ class TestStreamWithThrottle(iotests.QMPTestCase): Do a simple stream beneath the two throttle nodes. Should complete with no problems. ''' - result = self.vm.qmp('block-stream', - job_id='stream', - device='unthrottled-node') - self.assert_qmp(result, 'return', {}) + self.vm.cmd('block-stream', + job_id='stream', + device='unthrottled-node') # Should succeed and not time out try: diff --git a/tests/qemu-iotests/tests/vvfat b/tests/qemu-iotests/tests/vvfat new file mode 100755 index 0000000000..acdc6ce8ff --- /dev/null +++ b/tests/qemu-iotests/tests/vvfat @@ -0,0 +1,485 @@ +#!/usr/bin/env python3 +# group: rw vvfat +# +# Test vvfat driver implementation +# Here, we use a simple FAT16 implementation and check the behavior of +# the vvfat driver. +# +# Copyright (C) 2024 Amjad Alsharafi +# +# 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 shutil +import iotests +from iotests import imgfmt, QMPTestCase +from fat16 import MBR, Fat16, DIRENTRY_SIZE + +filesystem = os.path.join(iotests.test_dir, "filesystem") + +nbd_sock = iotests.file_path("nbd.sock", base_dir=iotests.sock_dir) +nbd_uri = "nbd+unix:///disk?socket=" + nbd_sock + +SECTOR_SIZE = 512 + + +class TestVVFatDriver(QMPTestCase): + # pylint: disable=broad-exception-raised + def setUp(self) -> None: + if os.path.exists(filesystem): + if os.path.isdir(filesystem): + shutil.rmtree(filesystem) + else: + raise Exception(f"{filesystem} exists and is not a directory") + + os.mkdir(filesystem) + + # Add some text files to the filesystem + for i in range(10): + with open(os.path.join(filesystem, f"file{i}.txt"), + "w", encoding="ascii") as f: + f.write(f"Hello, world! {i}\n") + + # Add 2 large files, above the cluster size (8KB) + with open(os.path.join(filesystem, "large1.txt"), "wb") as f: + # write 'A' * 1KB, 'B' * 1KB, 'C' * 1KB, ... + for i in range(8 * 2): # two clusters + f.write(bytes([0x41 + i] * 1024)) + + with open(os.path.join(filesystem, "large2.txt"), "wb") as f: + # write 'A' * 1KB, 'B' * 1KB, 'C' * 1KB, ... + for i in range(8 * 3): # 3 clusters + f.write(bytes([0x41 + i] * 1024)) + + self.vm = iotests.VM() + + self.vm.add_blockdev( + self.vm.qmp_to_opts( + { + "driver": imgfmt, + "node-name": "disk", + "rw": "true", + "fat-type": "16", + "dir": filesystem, + } + ) + ) + + self.vm.launch() + + self.vm.qmp_log("block-dirty-bitmap-add", **{ + "node": "disk", + "name": "bitmap0", + }) + + # attach nbd server + self.vm.qmp_log( + "nbd-server-start", + **{"addr": {"type": "unix", "data": {"path": nbd_sock}}}, + filters=[], + ) + + self.vm.qmp_log( + "nbd-server-add", + **{"device": "disk", "writable": True, "bitmap": "bitmap0"}, + ) + + self.qio = iotests.QemuIoInteractive("-f", "raw", nbd_uri) + + def tearDown(self) -> None: + self.qio.close() + self.vm.shutdown() + # print(self.vm.get_log()) + shutil.rmtree(filesystem) + + def read_sectors(self, sector: int, num: int = 1) -> bytes: + """ + Read `num` sectors starting from `sector` from the `disk`. + This uses `QemuIoInteractive` to read the sectors into `stdout` and + then parse the output. + """ + self.assertGreater(num, 0) + + # The output contains the content of the sector in hex dump format + # We need to extract the content from it + output = self.qio.cmd( + f"read -v {sector * SECTOR_SIZE} {num * SECTOR_SIZE}") + + # Each row is 16 bytes long, and we are writing `num` sectors + rows = num * SECTOR_SIZE // 16 + output_rows = output.split("\n")[:rows] + + hex_content = "".join( + [(row.split(": ")[1]).split(" ")[0] for row in output_rows] + ) + bytes_content = bytes.fromhex(hex_content) + + self.assertEqual(len(bytes_content), num * SECTOR_SIZE) + + return bytes_content + + def write_sectors(self, sector: int, data: bytes) -> None: + """ + Write `data` to the `disk` starting from `sector`. + This uses `QemuIoInteractive` to write the data into the disk. + """ + + self.assertGreater(len(data), 0) + self.assertEqual(len(data) % SECTOR_SIZE, 0) + + temp_file = os.path.join(iotests.test_dir, "temp.bin") + with open(temp_file, "wb") as f: + f.write(data) + + self.qio.cmd( + f"write -s {temp_file} {sector * SECTOR_SIZE} {len(data)}" + ) + + os.remove(temp_file) + + def init_fat16(self): + mbr = MBR(self.read_sectors(0)) + return Fat16( + mbr.partition_table[0]["start_lba"], + mbr.partition_table[0]["size"], + self.read_sectors, + self.write_sectors, + ) + + # Tests + + def test_fat_filesystem(self): + """ + Test that vvfat produce a valid FAT16 and MBR sectors + """ + mbr = MBR(self.read_sectors(0)) + + self.assertEqual(mbr.partition_table[0]["status"], 0x80) + self.assertEqual(mbr.partition_table[0]["type"], 6) + + fat16 = Fat16( + mbr.partition_table[0]["start_lba"], + mbr.partition_table[0]["size"], + self.read_sectors, + self.write_sectors, + ) + self.assertEqual(fat16.boot_sector.bytes_per_sector, 512) + self.assertEqual(fat16.boot_sector.volume_label, "QEMU VVFAT") + + def test_read_root_directory(self): + """ + Test the content of the root directory + """ + fat16 = self.init_fat16() + + root_dir = fat16.read_root_directory() + + self.assertEqual(len(root_dir), 13) # 12 + 1 special file + + files = { + "QEMU VVF.AT": 0, # special empty file + "FILE0.TXT": 16, + "FILE1.TXT": 16, + "FILE2.TXT": 16, + "FILE3.TXT": 16, + "FILE4.TXT": 16, + "FILE5.TXT": 16, + "FILE6.TXT": 16, + "FILE7.TXT": 16, + "FILE8.TXT": 16, + "FILE9.TXT": 16, + "LARGE1.TXT": 0x2000 * 2, + "LARGE2.TXT": 0x2000 * 3, + } + + for entry in root_dir: + self.assertIn(entry.whole_name(), files) + self.assertEqual(entry.size_bytes, files[entry.whole_name()]) + + def test_direntry_as_bytes(self): + """ + Test if we can convert Direntry back to bytes, so that we can write it + back to the disk safely. + """ + fat16 = self.init_fat16() + + root_dir = fat16.read_root_directory() + first_entry_bytes = fat16.read_sectors( + fat16.boot_sector.root_dir_start(), 1) + + # The first entry won't be deleted, so we can compare it with the first + # entry in the root directory + self.assertEqual(root_dir[0].as_bytes(), + first_entry_bytes[:DIRENTRY_SIZE]) + + def test_read_files(self): + """ + Test reading the content of the files + """ + fat16 = self.init_fat16() + + for i in range(10): + file = fat16.find_direntry(f"/FILE{i}.TXT") + self.assertIsNotNone(file) + self.assertEqual( + fat16.read_file(file), f"Hello, world! {i}\n".encode("ascii") + ) + + # test large files + large1 = fat16.find_direntry("/LARGE1.TXT") + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(fat16.read_file(large1), f.read()) + + large2 = fat16.find_direntry("/LARGE2.TXT") + self.assertIsNotNone(large2) + with open(os.path.join(filesystem, "large2.txt"), "rb") as f: + self.assertEqual(fat16.read_file(large2), f.read()) + + def test_write_file_same_content_direct(self): + """ + Similar to `test_write_file_in_same_content`, but we write the file + directly clusters and thus we don't go through the modification of + direntry. + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + data = fat16.read_cluster(file.cluster) + fat16.write_cluster(file.cluster, data) + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(fat16.read_file(file), f.read()) + + def test_write_file_in_same_content(self): + """ + Test writing the same content to the file back to it + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.write_file(file, b"Hello, world! 0\n") + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), b"Hello, world! 0\n") + + def test_modify_content_same_clusters(self): + """ + Test modifying the content of the file without changing the number of + clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + new_content = b"Hello, world! Modified\n" + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_truncate_file_same_clusters_less(self): + """ + Test truncating the file without changing number of clusters + Test decreasing the file size + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.truncate_file(file, 5) + new_content = fat16.read_file(file) + self.assertEqual(new_content, b"Hello") + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_truncate_file_same_clusters_more(self): + """ + Test truncating the file without changing number of clusters + Test increase the file size + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/FILE0.TXT") + self.assertIsNotNone(file) + + self.assertEqual(fat16.read_file(file), b"Hello, world! 0\n") + + fat16.truncate_file(file, 20) + new_content = fat16.read_file(file) + self.assertIsNotNone(new_content) + + # random pattern will be appended to the file, and its not always the + # same + self.assertEqual(new_content[:16], b"Hello, world! 0\n") + self.assertEqual(len(new_content), 20) + + with open(os.path.join(filesystem, "file0.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_large_file(self): + """ + Test writing a large file + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + # The content of LARGE1 is A * 1KB, B * 1KB, C * 1KB, ..., P * 1KB + # Lets change it to be Z * 1KB, Y * 1KB, X * 1KB, ..., K * 1KB + # without changing the number of clusters or filesize + new_content = b"".join([bytes([0x5A - i] * 1024) for i in range(16)]) + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_truncate_file_change_clusters_less(self): + """ + Test truncating a file by reducing the number of clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + fat16.truncate_file(file, 1) + self.assertEqual(fat16.read_file(file), b"A") + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), b"A") + + def test_write_file_change_clusters_less(self): + """ + Test truncating a file by reducing the number of clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE2.TXT") + self.assertIsNotNone(file) + + new_content = b"X" * 8 * 1024 + b"Y" * 8 * 1024 + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large2.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_file_change_clusters_more(self): + """ + Test truncating a file by increasing the number of clusters + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE2.TXT") + self.assertIsNotNone(file) + + # from 3 clusters to 4 clusters + new_content = ( + b"W" * 8 * 1024 + + b"X" * 8 * 1024 + + b"Y" * 8 * 1024 + + b"Z" * 8 * 1024 + ) + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large2.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_file_change_clusters_more_non_contiguous_2_mappings(self): + """ + Test truncating a file by increasing the number of clusters Here we + allocate the new clusters in a way that makes them non-contiguous so + that we will get 2 cluster mappings for the file + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + # from 2 clusters to 3 clusters with non-contiguous allocation + fat16.truncate_file(file, 3 * 0x2000, allocate_non_continuous=True) + new_content = b"X" * 8 * 1024 + b"Y" * 8 * 1024 + b"Z" * 8 * 1024 + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_write_file_change_clusters_more_non_contiguous_3_mappings(self): + """ + Test truncating a file by increasing the number of clusters Here we + allocate the new clusters in a way that makes them non-contiguous so + that we will get 3 cluster mappings for the file + """ + fat16 = self.init_fat16() + + file = fat16.find_direntry("/LARGE1.TXT") + self.assertIsNotNone(file) + + # from 2 clusters to 4 clusters with non-contiguous allocation + fat16.truncate_file(file, 4 * 0x2000, allocate_non_continuous=True) + new_content = ( + b"W" * 8 * 1024 + + b"X" * 8 * 1024 + + b"Y" * 8 * 1024 + + b"Z" * 8 * 1024 + ) + fat16.write_file(file, new_content) + self.assertEqual(fat16.read_file(file), new_content) + + with open(os.path.join(filesystem, "large1.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + def test_create_file(self): + """ + Test creating a new file + """ + fat16 = self.init_fat16() + + new_file = fat16.create_file("/NEWFILE.TXT") + + self.assertIsNotNone(new_file) + self.assertEqual(new_file.size_bytes, 0) + + new_content = b"Hello, world! New file\n" + fat16.write_file(new_file, new_content) + self.assertEqual(fat16.read_file(new_file), new_content) + + with open(os.path.join(filesystem, "newfile.txt"), "rb") as f: + self.assertEqual(f.read(), new_content) + + # TODO: support deleting files + + +if __name__ == "__main__": + # This is a specific test for vvfat driver + iotests.main(supported_fmts=["vvfat"], supported_protocols=["file"]) diff --git a/tests/qemu-iotests/tests/vvfat.out b/tests/qemu-iotests/tests/vvfat.out new file mode 100755 index 0000000000..b6f257674e --- /dev/null +++ b/tests/qemu-iotests/tests/vvfat.out @@ -0,0 +1,5 @@ +................ +---------------------------------------------------------------------- +Ran 16 tests + +OK diff --git a/tests/qemu-iotests/tests/write-zeroes-unmap b/tests/qemu-iotests/tests/write-zeroes-unmap new file mode 100755 index 0000000000..7cfeeaf839 --- /dev/null +++ b/tests/qemu-iotests/tests/write-zeroes-unmap @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# group: quick +# +# Test write zeros unmap. +# +# Copyright (C) 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" + +trap _cleanup_test_img exit + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +create_test_image() { + _make_test_img -f $IMGFMT 1m +} + +filter_command() { + _filter_testdir | _filter_qemu_io | _filter_qemu | _filter_hmp +} + +print_disk_usage() { + du -sh $TEST_IMG | _filter_testdir +} + +echo +echo "=== defaults - write zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -z 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT \ + | filter_command +print_disk_usage + +echo +echo "=== defaults - write zeros unmap ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -zu 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT \ + | filter_command +print_disk_usage + + +echo +echo "=== defaults - write actual zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -P 0 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT \ + | filter_command +print_disk_usage + +echo +echo "=== discard=off - write zeroes unmap ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -zu 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,discard=off \ + | filter_command +print_disk_usage + +echo +echo "=== detect-zeroes=on - write actual zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -P 0 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,detect-zeroes=on \ + | filter_command +print_disk_usage + +echo +echo "=== detect-zeroes=on,discard=on - write actual zeros ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -P 0 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,detect-zeroes=on,discard=on \ + | filter_command +print_disk_usage + +echo +echo "=== discard=on - write zeroes ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -z 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,discard=on \ + | filter_command +print_disk_usage + +echo +echo "=== discard=on - write zeroes unmap ===" +echo + +create_test_image +echo -e 'qemu-io none0 "write -zu 0 1m"\nquit' \ + | $QEMU -monitor stdio -drive if=none,file=$TEST_IMG,format=$IMGFMT,discard=on \ + | filter_command +print_disk_usage diff --git a/tests/qemu-iotests/tests/write-zeroes-unmap.out b/tests/qemu-iotests/tests/write-zeroes-unmap.out new file mode 100644 index 0000000000..c931994897 --- /dev/null +++ b/tests/qemu-iotests/tests/write-zeroes-unmap.out @@ -0,0 +1,81 @@ +QA output created by write-zeroes-unmap + +=== defaults - write zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -z 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) quit +1.0M TEST_DIR/t.raw + +=== defaults - write zeros unmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -zu 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) quit +1.0M TEST_DIR/t.raw + +=== defaults - write actual zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -P 0 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) quit +1.0M TEST_DIR/t.raw + +=== discard=off - write zeroes unmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -zu 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) quit +1.0M TEST_DIR/t.raw + +=== detect-zeroes=on - write actual zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -P 0 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) quit +1.0M TEST_DIR/t.raw + +=== detect-zeroes=on,discard=on - write actual zeros === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -P 0 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) quit +1.0M TEST_DIR/t.raw + +=== discard=on - write zeroes === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -z 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) quit +1.0M TEST_DIR/t.raw + +=== discard=on - write zeroes unmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io none0 "write -zu 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) quit +0 TEST_DIR/t.raw 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 74103efdfa..b71bd60a8a 100644 --- a/tests/qtest/ac97-test.c +++ b/tests/qtest/ac97-test.c @@ -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/acpi-utils.c b/tests/qtest/acpi-utils.c index 673fc97586..9dc24fbe5a 100644 --- a/tests/qtest/acpi-utils.c +++ b/tests/qtest/acpi-utils.c @@ -156,5 +156,4 @@ uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start, g_usleep(TEST_DELAY); } g_assert_not_reached(); - return 0; } 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 66652fed04..5a1923f721 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -36,9 +36,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(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 @@ -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; @@ -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', " @@ -1943,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); diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index 5a14527386..cfd6f77353 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); @@ -497,6 +509,7 @@ static void test_query_cpu_model_expansion_kvm(const void *data) assert_set_feature(qts, "host", "kvm-no-adjvtime", false); if (g_str_equal(qtest_get_arch(), "aarch64")) { + bool kvm_supports_pmu; bool kvm_supports_steal_time; bool kvm_supports_sve; char max_name[8], name[8]; @@ -505,17 +518,26 @@ 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"); - /* Enabling and disabling pmu should always work. */ - assert_has_feature_enabled(qts, "host", "pmu"); - assert_set_feature(qts, "host", "pmu", false); - assert_set_feature(qts, "host", "pmu", true); - /* * Some features would be enabled by default, but they're disabled * because this instance of KVM doesn't support them. Test that the @@ -525,11 +547,18 @@ static void test_query_cpu_model_expansion_kvm(const void *data) assert_has_feature(qts, "host", "sve"); resp = do_query_no_props(qts, "host"); + kvm_supports_pmu = resp_get_feature(resp, "pmu"); kvm_supports_steal_time = resp_get_feature(resp, "kvm-steal-time"); kvm_supports_sve = resp_get_feature(resp, "sve"); vls = resp_get_sve_vls(resp); qobject_unref(resp); + if (kvm_supports_pmu) { + /* If we have pmu then we should be able to toggle it. */ + assert_set_feature(qts, "host", "pmu", false); + assert_set_feature(qts, "host", "pmu", true); + } + if (kvm_supports_steal_time) { /* If we have steal-time then we should be able to toggle it. */ assert_set_feature(qts, "host", "kvm-steal-time", false); @@ -606,31 +635,43 @@ 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_machine("virt")) { + goto out; + } + + 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..f5ab269972 --- /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_cmphex(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_cmphex(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_cmphex(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_cmphex(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_cmphex(curval, ==, 0x10000); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_BUS_STATUS); + g_assert_cmphex(curval, ==, 0x0); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB0_READ_DATA); + g_assert_cmphex(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_cmphex(curval, ==, 0x20000); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_BUS_STATUS); + g_assert_cmphex(curval, ==, 0x0); + curval = aspeed_fsi_readl(s, ASPEED_FSI_OPB1_READ_DATA); + g_assert_cmphex(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_smc-test.c b/tests/qtest/aspeed_smc-test.c index c713a3700b..4673371d95 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -353,7 +353,8 @@ static void test_read_page_mem(void) uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - /* Enable 4BYTE mode for controller. This is should be strapped by + /* + * Enable 4BYTE mode for controller. This is should be strapped by * HW for CE0 anyhow. */ spi_ce_ctrl(1 << CRTL_EXTENDED0); @@ -394,7 +395,8 @@ static void test_write_page_mem(void) uint32_t page[FLASH_PAGE_SIZE / 4]; int i; - /* Enable 4BYTE mode for controller. This is should be strapped by + /* + * Enable 4BYTE mode for controller. This is should be strapped by * HW for CE0 anyhow. */ spi_ce_ctrl(1 << CRTL_EXTENDED0); diff --git a/tests/qtest/ast2700-gpio-test.c b/tests/qtest/ast2700-gpio-test.c new file mode 100644 index 0000000000..9275845564 --- /dev/null +++ b/tests/qtest/ast2700-gpio-test.c @@ -0,0 +1,95 @@ +/* + * QTest testcase for the ASPEED AST2700 GPIO Controller. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "qapi/qmp/qdict.h" +#include "libqtest-single.h" + +#define AST2700_GPIO_BASE 0x14C0B000 +#define GPIOA0_CONTROL 0x180 + +static void test_output_pins(const char *machine, const uint32_t base) +{ + QTestState *s = qtest_init(machine); + uint32_t offset = 0; + uint32_t value = 0; + uint32_t pin = 0; + + for (char c = 'A'; c <= 'D'; c++) { + for (int i = 0; i < 8; i++) { + offset = base + (pin * 4); + + /* output direction and output hi */ + qtest_writel(s, offset, 0x00000003); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0x00000003); + + /* output direction and output low */ + qtest_writel(s, offset, 0x00000002); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0x00000002); + pin++; + } + } + + qtest_quit(s); +} + +static void test_input_pins(const char *machine, const uint32_t base) +{ + QTestState *s = qtest_init(machine); + char name[16]; + uint32_t offset = 0; + uint32_t value = 0; + uint32_t pin = 0; + + for (char c = 'A'; c <= 'D'; c++) { + for (int i = 0; i < 8; i++) { + sprintf(name, "gpio%c%d", c, i); + offset = base + (pin * 4); + /* input direction */ + qtest_writel(s, offset, 0); + + /* set input */ + qtest_qom_set_bool(s, "/machine/soc/gpio", name, true); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0x00002000); + + /* clear input */ + qtest_qom_set_bool(s, "/machine/soc/gpio", name, false); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0); + pin++; + } + } + + qtest_quit(s); +} + +static void test_2700_input_pins(void) +{ + test_input_pins("-machine ast2700-evb", + AST2700_GPIO_BASE + GPIOA0_CONTROL); +} + +static void test_2700_output_pins(void) +{ + test_output_pins("-machine ast2700-evb", + AST2700_GPIO_BASE + GPIOA0_CONTROL); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/ast2700/gpio/input_pins", test_2700_input_pins); + qtest_add_func("/ast2700/gpio/output_pins", test_2700_output_pins); + + return g_test_run(); +} diff --git a/tests/qtest/bcm2835-dma-test.c b/tests/qtest/bcm2835-dma-test.c index 8293d822b9..18901b76d2 100644 --- a/tests/qtest/bcm2835-dma-test.c +++ b/tests/qtest/bcm2835-dma-test.c @@ -25,7 +25,7 @@ #define BCM2708_DMA_INT_STATUS 0xfe0 -/* DMA Trasfer Info fields: */ +/* 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) diff --git a/tests/qtest/bcm2835-i2c-test.c b/tests/qtest/bcm2835-i2c-test.c new file mode 100644 index 0000000000..1599194926 --- /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, ==, 0xa0); + + /* 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 395d441212..16d0ffbdf6 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,8 @@ typedef struct { bool tcg_only; const char *machine; + const char *arch; + const char *machine_param; const char *variant; const char *uefi_fl1; const char *uefi_fl2; @@ -94,8 +96,11 @@ typedef struct { 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; @@ -107,6 +112,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); @@ -238,21 +246,34 @@ 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, + aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, + data->arch, 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) { @@ -367,7 +388,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,9 +401,9 @@ static GArray *load_expected_aml(test_data *data) memset(&exp_sdt, 0, sizeof(exp_sdt)); try_again: - aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - sdt->aml, ext); - if (getenv("V")) { + aml_file = g_strdup_printf("%s/%s/%s/%.4s%s", data_dir, data->arch, + data->machine, sdt->aml, ext); + if (verbosity_level >= 2) { fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { @@ -394,7 +415,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, @@ -435,10 +456,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) { @@ -502,7 +522,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, @@ -637,8 +657,10 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, SmbiosEntryPointType ep_type) { 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; @@ -660,6 +682,13 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, 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)); @@ -668,6 +697,24 @@ static void smbios_cpu_test(test_data *data, uint32_t addr, 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 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); } } @@ -676,7 +723,7 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) DECLARE_BITMAP(struct_bitmap, SMBIOS_MAX_TYPE+1) = { 0 }; SmbiosEntryPoint *ep_table = &data->smbios_ep_table; - int i = 0, len, max_len = 0; + int i = 0, len, max_len = 0, type4_count = 0; uint8_t type, prv, crt; uint64_t addr; @@ -702,6 +749,7 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) if (type == 4) { smbios_cpu_test(data, addr, ep_type); + type4_count++; } /* seek to end of unformatted string area of this struct ("\0\0") */ @@ -745,11 +793,13 @@ static void test_smbios_structs(test_data *data, SmbiosEntryPointType ep_type) 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); @@ -765,51 +815,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 " + 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); @@ -817,18 +892,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) { - SmbiosEntryPointType ep_type = test_smbios_entry_point(data); - test_smbios_structs(data, ep_type); - } + 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[] = { @@ -837,13 +913,13 @@ 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.arch = "x86"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one(NULL, &data); @@ -852,66 +928,104 @@ 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.arch = "x86"; 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.arch = "x86"; 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.arch = "x86"; 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.arch = "x86"; 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.arch = "x86"; data.required_struct_types = base_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(base_required_struct_types); test_acpi_one(NULL, &data); @@ -923,32 +1037,137 @@ static void test_acpi_q35_tcg(void) free_test_data(&data); } -static void test_acpi_q35_tcg_core_count2(void) +static void test_acpi_q35_kvm_type4_count(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", + .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, + .arch = "x86", + .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, + .arch = "x86", .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 = 275, + .smbios_core_count2 = 260, }; - test_acpi_one("-machine smbios-entry-point-type=64 -smp 275", &data); + 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, + .arch = "x86", + .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, + .arch = "x86", + .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.arch = "x86", 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.arch = "x86", + 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); } @@ -956,16 +1175,49 @@ static void test_acpi_q35_multif_bridge(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .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); } @@ -973,12 +1225,15 @@ static void test_acpi_q35_tcg_mmio64(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .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", @@ -988,10 +1243,10 @@ 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.arch = "x86"; data.variant = ".cphp"; test_acpi_one("-smp 2,cores=3,sockets=2,maxcpus=6" " -object memory-backend-ram,id=ram0,size=64M" @@ -1004,10 +1259,10 @@ 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.arch = "x86", data.variant = ".cphp"; test_acpi_one(" -smp 2,cores=3,sockets=2,maxcpus=6" " -object memory-backend-ram,id=ram0,size=64M" @@ -1024,10 +1279,10 @@ 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.arch = "x86", data.variant = ".ipmibt"; data.required_struct_types = ipmi_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); @@ -1039,10 +1294,10 @@ static void test_acpi_q35_tcg_ipmi(void) static void test_acpi_q35_tcg_smbus_ipmi(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; + data.arch = "x86", data.variant = ".ipmismbus"; data.required_struct_types = ipmi_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); @@ -1054,13 +1309,13 @@ static void test_acpi_q35_tcg_smbus_ipmi(void) 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.arch = "x86"; data.variant = ".ipmikcs"; data.required_struct_types = ipmi_required_struct_types; data.required_struct_types_len = ARRAY_SIZE(ipmi_required_struct_types); @@ -1072,10 +1327,10 @@ 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.arch = "x86", data.variant = ".memhp"; test_acpi_one(" -m 128,slots=3,maxmem=1G" " -object memory-backend-ram,id=ram0,size=64M" @@ -1088,10 +1343,10 @@ 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.arch = "x86"; data.variant = ".memhp"; test_acpi_one(" -m 128,slots=3,maxmem=1G" " -object memory-backend-ram,id=ram0,size=64M" @@ -1104,10 +1359,10 @@ 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.arch = "x86"; data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); free_test_data(&data); @@ -1115,10 +1370,10 @@ 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.arch = "x86"; data.variant = ".smm-compat"; test_acpi_one("-global PIIX4_PM.smm-compat=on", &data); free_test_data(&data); @@ -1126,10 +1381,10 @@ 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.arch = "x86"; data.variant = ".smm-compat-nosmm"; test_acpi_one("-global PIIX4_PM.smm-compat=on -machine smm=off", &data); free_test_data(&data); @@ -1137,21 +1392,22 @@ 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.arch = "x86"; + 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.arch = "x86", data.variant = ".numamem"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" " -numa node -numa node,memdev=ram0", &data); @@ -1160,10 +1416,10 @@ 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.arch = "x86", data.variant = ".xapic"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" " -numa node -numa node,memdev=ram0" @@ -1173,10 +1429,10 @@ 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.arch = "x86", data.variant = ".nosmm"; test_acpi_one("-machine smm=off", &data); free_test_data(&data); @@ -1184,10 +1440,10 @@ 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.arch = "x86", data.variant = ".smm-compat"; test_acpi_one("-global ICH9-LPC.smm-compat=on", &data); free_test_data(&data); @@ -1195,10 +1451,10 @@ 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.arch = "x86", data.variant = ".smm-compat-nosmm"; test_acpi_one("-global ICH9-LPC.smm-compat=on -machine smm=off", &data); free_test_data(&data); @@ -1206,21 +1462,22 @@ 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.arch = "x86", + 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.arch = "x86", data.variant = ".dmar"; test_acpi_one("-machine kernel-irqchip=split -accel kvm" " -device intel-iommu,intremap=on,device-iotlb=on", &data); @@ -1229,10 +1486,10 @@ 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.arch = "x86", data.variant = ".ivrs"; data.tcg_only = true, test_acpi_one(" -device amd-iommu", &data); @@ -1241,10 +1498,10 @@ 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.arch = "x86"; data.variant = ".numamem"; test_acpi_one(" -object memory-backend-ram,id=ram0,size=128M" " -numa node -numa node,memdev=ram0", &data); @@ -1253,14 +1510,15 @@ static void test_acpi_piix4_tcg_numamem(void) uint64_t tpm_tis_base_addr; -static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, - uint64_t base, enum TPMVersion tpm_version) +static void test_acpi_tcg_tpm(const char *machine, const char *arch, + const char *tpm_if, uint64_t base, + enum TPMVersion tpm_version) { gchar *tmp_dir_name = g_strdup_printf("qemu-test_acpi_%s_tcg_%s.XXXXXX", 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); @@ -1280,8 +1538,8 @@ 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.arch = arch; data.variant = variant; args = g_strdup_printf( @@ -1305,20 +1563,20 @@ static void test_acpi_tcg_tpm(const char *machine, const char *tpm_if, static void test_acpi_q35_tcg_tpm2_tis(void) { - test_acpi_tcg_tpm("q35", "tis", 0xFED40000, TPM_VERSION_2_0); + test_acpi_tcg_tpm("q35", "x86", "tis", 0xFED40000, TPM_VERSION_2_0); } static void test_acpi_q35_tcg_tpm12_tis(void) { - test_acpi_tcg_tpm("q35", "tis", 0xFED40000, TPM_VERSION_1_2); + test_acpi_tcg_tpm("q35", "x86", "tis", 0xFED40000, TPM_VERSION_1_2); } -static void test_acpi_tcg_dimm_pxm(const char *machine) +static void test_acpi_tcg_dimm_pxm(const char *machine, const char *arch) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = machine; + data.arch = arch; data.variant = ".dimmpxm"; test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" " -smp 4,sockets=4" @@ -1345,18 +1603,19 @@ static void test_acpi_tcg_dimm_pxm(const char *machine) static void test_acpi_q35_tcg_dimm_pxm(void) { - test_acpi_tcg_dimm_pxm(MACHINE_Q35); + test_acpi_tcg_dimm_pxm(MACHINE_Q35, "x86"); } static void test_acpi_piix4_tcg_dimm_pxm(void) { - test_acpi_tcg_dimm_pxm(MACHINE_PC); + test_acpi_tcg_dimm_pxm(MACHINE_PC, "x86"); } -static void test_acpi_virt_tcg_memhp(void) +static void test_acpi_aarch64_virt_tcg_memhp(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1385,8 +1644,8 @@ 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->arch = "x86"; data->required_struct_types = NULL; /* no smbios */ data->required_struct_types_len = 0; data->blkdev = "virtio-blk-device"; @@ -1394,7 +1653,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", @@ -1404,7 +1663,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"; @@ -1415,7 +1674,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"; @@ -1426,7 +1685,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"; @@ -1438,7 +1697,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"; @@ -1447,10 +1706,37 @@ static void test_acpi_microvm_ioapic2_tcg(void) free_test_data(&data); } -static void test_acpi_virt_tcg_numamem(void) +static void test_acpi_riscv64_virt_tcg_numamem(void) { test_data data = { .machine = "virt", + .arch = "riscv64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-riscv-code.fd", + .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", + .ram_start = 0x80000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + data.variant = ".numamem"; + /* + * RHCT will have ISA string encoded. To reduce the effort + * of updating expected AML file for any new default ISA extension, + * use the profile rva22s64. + */ + test_acpi_one(" -cpu rva22s64" + " -object memory-backend-ram,id=ram0,size=128M" + " -numa node,memdev=ram0", + &data); + free_test_data(&data); +} + +static void test_acpi_aarch64_virt_tcg_numamem(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1469,10 +1755,11 @@ static void test_acpi_virt_tcg_numamem(void) } -static void test_acpi_virt_tcg_pxb(void) +static void test_acpi_aarch64_virt_tcg_pxb(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1501,12 +1788,12 @@ static void test_acpi_virt_tcg_pxb(void) free_test_data(&data); } -static void test_acpi_tcg_acpi_hmat(const char *machine) +static void test_acpi_tcg_acpi_hmat(const char *machine, const char *arch) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = machine; + data.arch = arch; data.variant = ".acpihmat"; test_acpi_one(" -machine hmat=on" " -smp 2,sockets=2" @@ -1535,18 +1822,19 @@ static void test_acpi_tcg_acpi_hmat(const char *machine) static void test_acpi_q35_tcg_acpi_hmat(void) { - test_acpi_tcg_acpi_hmat(MACHINE_Q35); + test_acpi_tcg_acpi_hmat(MACHINE_Q35, "x86"); } static void test_acpi_piix4_tcg_acpi_hmat(void) { - test_acpi_tcg_acpi_hmat(MACHINE_PC); + test_acpi_tcg_acpi_hmat(MACHINE_PC, "x86"); } -static void test_acpi_virt_tcg_acpi_hmat(void) +static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1560,9 +1848,9 @@ static void test_acpi_virt_tcg_acpi_hmat(void) test_acpi_one(" -machine hmat=on" " -cpu cortex-a57" " -smp 4,sockets=2" - " -m 256M" - " -object memory-backend-ram,size=64M,id=ram0" - " -object memory-backend-ram,size=64M,id=ram1" + " -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" @@ -1602,10 +1890,10 @@ static void test_acpi_virt_tcg_acpi_hmat(void) static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) { - test_data data; + test_data data = {}; - memset(&data, 0, sizeof(data)); data.machine = MACHINE_Q35; + data.arch = "x86"; data.variant = ".acpihmat-noinitiator"; test_acpi_one(" -machine hmat=on" " -smp 4,sockets=2" @@ -1648,15 +1936,110 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) free_test_data(&data); } +/* Test intended to hit corner cases of SRAT and HMAT */ +static void test_acpi_q35_tcg_acpi_hmat_generic_x(void) +{ + test_data data = {}; + + data.machine = MACHINE_Q35; + data.arch = "x86"; + data.variant = ".acpihmat-generic-x"; + test_acpi_one(" -machine hmat=on,cxl=on" + " -smp 3,sockets=3" + " -m 128M,maxmem=384M,slots=2" + " -device pcie-root-port,chassis=1,id=pci.1" + " -device pci-testdev,bus=pci.1," + "multifunction=on,addr=00.0" + " -device pci-testdev,bus=pci.1,addr=00.1" + " -device pci-testdev,bus=pci.1,id=gidev,addr=00.2" + " -device pxb-cxl,bus_nr=64,bus=pcie.0,id=cxl.1" + " -object memory-backend-ram,size=64M,id=ram0" + " -object memory-backend-ram,size=64M,id=ram1" + " -numa node,nodeid=0,cpus=0,memdev=ram0" + " -numa node,nodeid=1" + " -object acpi-generic-initiator,id=gi0,pci-dev=gidev,node=1" + " -numa node,nodeid=2" + " -object acpi-generic-port,id=gp0,pci-bus=cxl.1,node=2" + " -numa node,nodeid=3,cpus=1" + " -numa node,nodeid=4,memdev=ram1" + " -numa node,nodeid=5,cpus=2" + " -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=800M" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-latency,latency=100" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=0,target=4,hierarchy=memory," + "data-type=access-latency,latency=100" + " -numa hmat-lb,initiator=0,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=0,target=5,hierarchy=memory," + "data-type=access-latency,latency=200" + " -numa hmat-lb,initiator=0,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-latency,latency=500" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=100M" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-latency,latency=50" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=1,target=4,hierarchy=memory," + "data-type=access-latency,latency=50" + " -numa hmat-lb,initiator=1,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=800M" + " -numa hmat-lb,initiator=1,target=5,hierarchy=memory," + "data-type=access-latency,latency=500" + " -numa hmat-lb,initiator=1,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=100M" + " -numa hmat-lb,initiator=3,target=0,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=3,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=3,target=2,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=3,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=3,target=4,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=3,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=3,target=5,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=3,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=5,target=0,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=5,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=5,target=2,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=5,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=5,target=4,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=5,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=5,target=5,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=5,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=800M", + &data); + free_test_data(&data); +} + #ifdef CONFIG_POSIX -static void test_acpi_erst(const char *machine) +static void test_acpi_erst(const char *machine, const char *arch) { 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.arch = arch; data.variant = ".acpierst"; params = g_strdup_printf( " -object memory-backend-file,id=erstnvram," @@ -1671,19 +2054,19 @@ static void test_acpi_erst(const char *machine) static void test_acpi_piix4_acpi_erst(void) { - test_acpi_erst(MACHINE_PC); + test_acpi_erst(MACHINE_PC, "x86"); } static void test_acpi_q35_acpi_erst(void) { - test_acpi_erst(MACHINE_Q35); + test_acpi_erst(MACHINE_Q35, "x86"); } 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"; @@ -1701,10 +2084,33 @@ static void test_acpi_microvm_acpi_erst(void) } #endif /* CONFIG_POSIX */ -static void test_acpi_virt_tcg(void) +static void test_acpi_riscv64_virt_tcg(void) { test_data data = { .machine = "virt", + .arch = "riscv64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-riscv-code.fd", + .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", + .ram_start = 0x80000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + /* + * RHCT will have ISA string encoded. To reduce the effort + * of updating expected AML file for any new default ISA extension, + * use the profile rva22s64. + */ + test_acpi_one("-cpu rva22s64 ", &data); + free_test_data(&data); +} + +static void test_acpi_aarch64_virt_tcg(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1720,10 +2126,30 @@ static void test_acpi_virt_tcg(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_tcg_topology(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .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 = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".viot", }; @@ -1748,6 +2174,7 @@ static void test_acpi_q35_cxl(void) test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".cxl", }; /* @@ -1765,13 +2192,13 @@ 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" + " -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, @@ -1785,10 +2212,11 @@ static void test_acpi_q35_cxl(void) } #endif /* CONFIG_POSIX */ -static void test_acpi_virt_viot(void) +static void test_acpi_aarch64_virt_viot(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1812,6 +2240,7 @@ static void test_acpi_q35_slic(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".slic", }; @@ -1826,6 +2255,7 @@ static void test_acpi_q35_applesmc(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".applesmc", }; @@ -1839,6 +2269,7 @@ static void test_acpi_q35_pvpanic_isa(void) { test_data data = { .machine = MACHINE_Q35, + .arch = "x86", .variant = ".pvpanic-isa", }; @@ -1846,6 +2277,52 @@ static void test_acpi_q35_pvpanic_isa(void) free_test_data(&data); } +static void test_acpi_pc_smbios_options(void) +{ + uint8_t req_type11[] = { 11 }; + test_data data = { + .machine = MACHINE_PC, + .arch = "x86", + .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, + .arch = "x86", + .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; @@ -1866,18 +2343,17 @@ static void test_oem_fields(test_data *data) 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.arch = "x86"; 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); @@ -1886,18 +2362,17 @@ static void test_acpi_piix4_oem_fields(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.arch = "x86"; 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); @@ -1906,25 +2381,26 @@ static void test_acpi_q35_oem_fields(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_virt_oem_fields(void) +static void test_acpi_aarch64_virt_oem_fields(void) { test_data data = { .machine = "virt", + .arch = "aarch64", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", @@ -1934,10 +2410,9 @@ static void test_acpi_virt_oem_fields(void) }; char *args; - args = test_acpi_create_args(&data, - "-cpu cortex-a57 "OEM_TEST_ARGS, true); + args = test_acpi_create_args(&data, "-cpu cortex-a57 "OEM_TEST_ARGS); data.qts = qtest_init(args); - test_acpi_load_tables(&data, true); + test_acpi_load_tables(&data); test_oem_fields(&data); qtest_quit(data.qts); free_test_data(&data); @@ -1948,12 +2423,24 @@ static void test_acpi_virt_oem_fields(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) { @@ -1971,7 +2458,6 @@ int main(int argc, char *argv[]) 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/memhp", test_acpi_piix4_tcg_memhp); 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", @@ -1979,12 +2465,24 @@ int main(int argc, char *argv[]) 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/piix4/dimmpxm", test_acpi_piix4_tcg_dimm_pxm); - qtest_add_func("acpi/piix4/acpihmat", - test_acpi_piix4_tcg_acpi_hmat); + + /* 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); } if (qtest_has_machine(MACHINE_Q35)) { qtest_add_func("acpi/q35", test_acpi_q35_tcg); @@ -1995,13 +2493,13 @@ int main(int argc, char *argv[]) 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/mmio64", test_acpi_q35_tcg_mmio64); 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/memhp", test_acpi_q35_tcg_memhp); 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", @@ -2009,10 +2507,19 @@ int main(int argc, char *argv[]) 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/dimmpxm", test_acpi_q35_tcg_dimm_pxm); - qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat); qtest_add_func("acpi/q35/acpihmat-noinitiator", test_acpi_q35_tcg_acpi_hmat_noinitiator); + qtest_add_func("acpi/q35/acpihmat-genericx", + test_acpi_q35_tcg_acpi_hmat_generic_x); + + /* 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 @@ -2024,10 +2531,20 @@ int main(int argc, char *argv[]) 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_tcg_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); } - qtest_add_func("acpi/q35/viot", test_acpi_q35_viot); #ifdef CONFIG_POSIX qtest_add_func("acpi/q35/cxl", test_acpi_q35_cxl); #endif @@ -2053,15 +2570,27 @@ int main(int argc, char *argv[]) } } } else if (strcmp(arch, "aarch64") == 0) { - if (has_tcg) { - qtest_add_func("acpi/virt", test_acpi_virt_tcg); + if (has_tcg && qtest_has_device("virtio-blk-pci")) { + qtest_add_func("acpi/virt", test_acpi_aarch64_virt_tcg); qtest_add_func("acpi/virt/acpihmatvirt", - test_acpi_virt_tcg_acpi_hmat); - 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_virt_oem_fields); - qtest_add_func("acpi/virt/viot", test_acpi_virt_viot); + test_acpi_aarch64_virt_tcg_acpi_hmat); + qtest_add_func("acpi/virt/topology", + test_acpi_aarch64_virt_tcg_topology); + qtest_add_func("acpi/virt/numamem", + test_acpi_aarch64_virt_tcg_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp); + qtest_add_func("acpi/virt/pxb", test_acpi_aarch64_virt_tcg_pxb); + qtest_add_func("acpi/virt/oem-fields", + test_acpi_aarch64_virt_oem_fields); + if (qtest_has_device("virtio-iommu-pci")) { + qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); + } + } + } else if (strcmp(arch, "riscv64") == 0) { + if (has_tcg && qtest_has_device("virtio-blk-pci")) { + qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg); + qtest_add_func("acpi/virt/numamem", + test_acpi_riscv64_virt_tcg_numamem); } } ret = g_test_run(); diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index 0680d79d6d..c67b8cfe16 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; @@ -34,7 +31,7 @@ static void test_a_boot_order(const char *machine, uint64_t actual; QTestState *qts; - if (machine && !qtest_has_machine(machine)) { + if (!qtest_has_machine(machine)) { g_test_skip("Machine is not available"); return; } @@ -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. @@ -110,7 +107,7 @@ static const boot_order_test test_cases_pc[] = { static void test_pc_boot_order(void) { - test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); + test_boot_orders("pc", read_boot_order_pc, test_cases_pc); } static uint64_t read_boot_order_pmac(QTestState *qts) diff --git a/tests/qtest/boot-sector.c b/tests/qtest/boot-sector.c index 44a109abd8..679ee17e2a 100644 --- a/tests/qtest/boot-sector.c +++ b/tests/qtest/boot-sector.c @@ -153,6 +153,8 @@ void boot_sector_test(QTestState *qts) signature_high = qtest_readb(qts, SIGNATURE_ADDR + 1); signature = (signature_high << 8) | signature_low; if (signature == SIGNATURE) { + /* wipe signature */ + qtest_writeb(qts, SIGNATURE_ADDR, 0x00); break; } @@ -160,7 +162,9 @@ void boot_sector_test(QTestState *qts) qrsp = qtest_qmp(qts, "{ 'execute': 'query-status' }"); qret = qdict_get_qdict(qrsp, "return"); g_assert_nonnull(qret); - g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + if (qdict_get_try_str(qret, "status")) { + g_assert_cmpstr(qdict_get_try_str(qret, "status"), ==, "running"); + } qobject_unref(qrsp); g_usleep(TEST_DELAY); diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index b216519b62..3b92fa5d50 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/libqos-spapr.h" +#include "ppc-util.h" static const uint8_t bios_avr[] = { 0x88, 0xe0, /* ldi r24, 0x08 */ @@ -26,6 +26,14 @@ static const uint8_t bios_avr[] = { 0x80, 0x93, 0xc6, 0x00, /* sts 0x00C6, r24 ; Output 'T' */ }; +static const uint8_t bios_loongarch64[] = { + 0x0c, 0xc0, 0x3f, 0x14, /* lu12i.w $t0, 0x1fe00 */ + 0x8c, 0x81, 0x87, 0x03, /* ori $t0, $t0, 0x1e0 */ + 0x0d, 0x50, 0x81, 0x03, /* li.w $t1, 'T' */ + 0x8d, 0x01, 0x00, 0x29, /* st.b $t1, $t0, 0 */ + 0xff, 0xf3, 0xff, 0x53, /* b -16 # loop */ +}; + static const uint8_t kernel_mcf5208[] = { 0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */ 0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */ @@ -139,7 +147,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 +164,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" }, @@ -167,6 +175,8 @@ static testdef_t tests[] = { { "sparc", "SS-600MP", "", "TMS390Z55" }, { "sparc64", "sun4u", "", "UltraSPARC" }, { "s390x", "s390-ccw-virtio", "", "device" }, + { "loongarch64", "virt", "-cpu max", "TT", sizeof(bios_loongarch64), + NULL, bios_loongarch64 }, { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, { "m68k", "next-cube", "", "TT", sizeof(bios_nextcube), 0, bios_nextcube }, { "microblaze", "petalogix-s3adsp1800", "", "TT", @@ -287,6 +297,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 26a2400181..c86725a511 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,17 +37,17 @@ 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; @@ -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,40 @@ static void test_cdboot(gconstpointer data) static void add_x86_tests(void) { - 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_accel("tcg") && !qtest_has_accel("kvm")) { + g_test_skip("No KVM or TCG accelerator available, skipping boot tests"); + return; + } + + if (qtest_has_machine("pc")) { + qtest_add_data_func("cdrom/boot/default", "-cdrom ", 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); + } + + if (qtest_has_device("am53c974")) { + qtest_add_data_func("cdrom/boot/am53c974", + "-device am53c974 -device scsi-cd,drive=cd1 " + "-drive if=none,id=cd1,format=raw,file=", + test_cdboot); + } + if (qtest_has_device("dc390")) { + qtest_add_data_func("cdrom/boot/dc390", + "-device dc390 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } + if (qtest_has_device("lsi53c895a")) { + qtest_add_data_func("cdrom/boot/lsi53c895a", + "-device lsi53c895a -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } + } + /* * Unstable CI test under load * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html @@ -142,41 +172,38 @@ static void add_x86_tests(void) qtest_add_data_func("cdrom/boot/isapc", "-M isapc " "-drive if=ide,media=cdrom,file=", test_cdboot); } - if (qtest_has_device("am53c974")) { - qtest_add_data_func("cdrom/boot/am53c974", - "-device am53c974 -device scsi-cd,drive=cd1 " - "-drive if=none,id=cd1,format=raw,file=", - test_cdboot); - } - if (qtest_has_device("dc390")) { - qtest_add_data_func("cdrom/boot/dc390", - "-device dc390 -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); - } - if (qtest_has_device("lsi53c895a")) { - qtest_add_data_func("cdrom/boot/lsi53c895a", - "-device lsi53c895a -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); - } - if (qtest_has_device("megasas")) { - qtest_add_data_func("cdrom/boot/megasas", "-M q35 " - "-device megasas -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); - } - if (qtest_has_device("megasas-gen2")) { - qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " - "-device megasas-gen2 -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); + + if (qtest_has_machine("q35")) { + if (qtest_has_device("megasas")) { + qtest_add_data_func("cdrom/boot/megasas", "-M q35 " + "-device megasas -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } + if (qtest_has_device("megasas-gen2")) { + qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " + "-device megasas-gen2 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } } } 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); @@ -186,6 +213,30 @@ static void add_s390x_tests(void) "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " "-device virtio-blk,drive=d2,bootindex=1 " "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/as-fallback-device", + "-device virtio-serial -device virtio-scsi " + "-device virtio-blk,drive=d1,bootindex=1 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " + "-device virtio-blk,drive=d2,bootindex=2 " + "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/as-last-option", + "-device virtio-serial -device virtio-scsi " + "-device virtio-blk,drive=d1,bootindex=1 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " + "-device virtio-blk,drive=d2,bootindex=2 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d2 " + "-device virtio-blk,drive=d3,bootindex=3 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d3 " + "-device scsi-hd,drive=d4,bootindex=4 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d4 " + "-device scsi-hd,drive=d5,bootindex=5 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d5 " + "-device virtio-blk,drive=d6,bootindex=6 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d6 " + "-device scsi-hd,drive=d7,bootindex=7 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d7 " + "-device scsi-cd,drive=d8,bootindex=8 " + "-drive if=none,id=d8,media=cdrom,file=", test_cdboot); if (qtest_has_device("x-terminal3270")) { qtest_add_data_func("cdrom/boot/without-bootindex", "-device virtio-scsi -device virtio-serial " @@ -201,12 +252,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 +295,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/cmsdk-apb-dualtimer-test.c b/tests/qtest/cmsdk-apb-dualtimer-test.c index ad6a758289..3b89bed97d 100644 --- a/tests/qtest/cmsdk-apb-dualtimer-test.c +++ b/tests/qtest/cmsdk-apb-dualtimer-test.c @@ -69,7 +69,7 @@ static void test_dualtimer(void) * tick VALUE should have wrapped round to 0xffff. */ clock_step(40); - g_assert_cmpuint(readl(TIMER_BASE + TIMER1VALUE), ==, 0xffff); + g_assert_cmphex(readl(TIMER_BASE + TIMER1VALUE), ==, 0xffff); /* Check that any write to INTCLR clears interrupt */ writel(TIMER_BASE + TIMER1INTCLR, 1); diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c index 2710cb17b8..53538f98c9 100644 --- a/tests/qtest/cmsdk-apb-watchdog-test.c +++ b/tests/qtest/cmsdk-apb-watchdog-test.c @@ -15,14 +15,12 @@ */ #include "qemu/osdep.h" +#include "exec/hwaddr.h" #include "qemu/bitops.h" #include "libqtest-single.h" -/* - * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz, - * which is 80ns per tick. - */ #define WDOG_BASE 0x40000000 +#define WDOG_BASE_MPS2 0x40008000 #define WDOGLOAD 0 #define WDOGVALUE 4 @@ -37,39 +35,97 @@ #define SYSDIV_SHIFT 23 #define SYSDIV_LENGTH 4 -static void test_watchdog(void) +#define WDOGLOAD_DEFAULT 0xFFFFFFFF +#define WDOGVALUE_DEFAULT 0xFFFFFFFF + +typedef struct CMSDKAPBWatchdogTestArgs { + int64_t tick; + hwaddr wdog_base; + const char *machine; +} CMSDKAPBWatchdogTestArgs; + +enum { + MACHINE_LM3S811EVB, + MACHINE_MPS2_AN385, +}; + +/* + * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz, + * which is 80ns per tick. + * + * IoTKit/ARMSSE dualtimer; driven at 25MHz in mps2-an385, so 40ns per tick + */ +static const CMSDKAPBWatchdogTestArgs machine_info[] = { + [MACHINE_LM3S811EVB] = { + .tick = 80, + .wdog_base = WDOG_BASE, + .machine = "lm3s811evb", + }, + [MACHINE_MPS2_AN385] = { + .tick = 40, + .wdog_base = WDOG_BASE_MPS2, + .machine = "mps2-an385", + }, +}; + +static void system_reset(QTestState *qtest) { - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); + QDict *resp; - writel(WDOG_BASE + WDOGCONTROL, 1); - writel(WDOG_BASE + WDOGLOAD, 1000); - - /* Step to just past the 500th tick */ - clock_step(500 * 80 + 1); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); - - /* Just past the 1000th tick: timer should have fired */ - clock_step(500 * 80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0); - - /* VALUE reloads at following tick */ - clock_step(80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); - - /* Writing any value to WDOGINTCLR clears the interrupt and reloads */ - clock_step(500 * 80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); - writel(WDOG_BASE + WDOGINTCLR, 0); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + qtest_qmp_eventwait(qtest, "RESET"); } -static void test_clock_change(void) +static void test_watchdog(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + writel(wdog_base + WDOGCONTROL, 1); + writel(wdog_base + WDOGLOAD, 1000); + + /* Step to just past the 500th tick */ + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500); + + /* Just past the 1000th tick: timer should have fired */ + clock_step(500 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 0); + + /* VALUE reloads at following tick */ + clock_step(tick); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000); + + /* Writing any value to WDOGINTCLR clears the interrupt and reloads */ + clock_step(500 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1); + writel(wdog_base + WDOGINTCLR, 0); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + qtest_end(); +} + +/* + * This test can only be executed in the stellaris board since it relies on a + * component of the board to change the clocking parameters of the watchdog. + */ +static void test_clock_change(const void *ptr) { uint32_t rcc; + const CMSDKAPBWatchdogTestArgs *args = ptr; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); /* * Test that writing to the stellaris board's RCC register to @@ -88,7 +144,7 @@ static void test_clock_change(void) /* Rewrite RCC.SYSDIV from 16 to 8, so the clock is now 40ns per tick */ rcc = readl(SSYS_BASE + RCC); - g_assert_cmpuint(extract32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH), ==, 0xf); + g_assert_cmphex(extract32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH), ==, 0xf); rcc = deposit32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH, 7); writel(SSYS_BASE + RCC, rcc); @@ -109,6 +165,201 @@ static void test_clock_change(void) writel(WDOG_BASE + WDOGINTCLR, 0); g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); + + qtest_end(); +} + +/* Tests the counter is not running after reset. */ +static void test_watchdog_reset(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0); + + /* + * The counter should not be running if WDOGCONTROL.INTEN has not been set, + * as it is the case after a cold reset. + */ + clock_step(15 * tick + 1); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* Let the counter run before reset */ + writel(wdog_base + WDOGLOAD, 3000); + writel(wdog_base + WDOGCONTROL, 1); + + /* Verify it is running */ + clock_step(1000 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 3000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 2000); + + system_reset(global_qtest); + + /* Check defaults after reset */ + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* The counter should not be running after reset. */ + clock_step(1000 * tick + 1); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + qtest_end(); +} + +/* + * Tests inten works as the counter enable based on this description: + * + * Enable the interrupt event, WDOGINT. Set HIGH to enable the counter and the + * interrupt, or LOW to disable the counter and interrupt. Reloads the counter + * from the value in WDOGLOAD when the interrupt is enabled, after previously + * being disabled. + */ +static void test_watchdog_inten(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* + * When WDOGLOAD is written to, the count is immediately restarted from the + * new value. + * + * Note: the counter should not be running as long as WDOGCONTROL.INTEN is + * not set + */ + writel(wdog_base + WDOGLOAD, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + + /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */ + writel(wdog_base + WDOGCONTROL, 1); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500); + + /* or LOW to disable the counter and interrupt. */ + writel(wdog_base + WDOGCONTROL, 0); + clock_step(100 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500); + + /* + * Reloads the counter from the value in WDOGLOAD when the interrupt is + * enabled, after previously being disabled. + */ + writel(wdog_base + WDOGCONTROL, 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + + /* Test counter is still on */ + clock_step(50 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3950); + + /* + * When WDOGLOAD is written to, the count is immediately restarted from the + * new value. + * + * Note: the counter should be running since WDOGCONTROL.INTEN is set + */ + writel(wdog_base + WDOGLOAD, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 5000); + clock_step(4999 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + /* Finally disable and check the conditions don't change */ + writel(wdog_base + WDOGCONTROL, 0); + clock_step(10 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + qtest_end(); +} + +/* + * Tests the following custom behavior: + * + * The Luminary version of this device ignores writes to this register after the + * guest has enabled interrupts (so they can only be disabled again via reset). + */ +static void test_watchdog_inten_luminary(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* + * When WDOGLOAD is written to, the count is immediately restarted from the + * new value. + * + * Note: the counter should not be running as long as WDOGCONTROL.INTEN is + * not set + */ + writel(wdog_base + WDOGLOAD, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + + /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */ + writel(wdog_base + WDOGCONTROL, 1); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500); + + /* + * The Luminary version of this device ignores writes to this register after + * the guest has enabled interrupts + */ + writel(wdog_base + WDOGCONTROL, 0); + clock_step(100 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3400); + g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0x1); + + /* They can only be disabled again via reset */ + system_reset(global_qtest); + + /* Check defaults after reset */ + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0); + + /* The counter should not be running after reset. */ + clock_step(1000 * tick + 1); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + qtest_end(); } int main(int argc, char **argv) @@ -116,16 +367,33 @@ int main(int argc, char **argv) int r; g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); - qtest_start("-machine lm3s811evb"); - - qtest_add_func("/cmsdk-apb-watchdog/watchdog", test_watchdog); - qtest_add_func("/cmsdk-apb-watchdog/watchdog_clock_change", - test_clock_change); + if (qtest_has_machine(machine_info[MACHINE_LM3S811EVB].machine)) { + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog", + &machine_info[MACHINE_LM3S811EVB], test_watchdog); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_clock_change", + &machine_info[MACHINE_LM3S811EVB], + test_clock_change); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset", + &machine_info[MACHINE_LM3S811EVB], + test_watchdog_reset); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten_luminary", + &machine_info[MACHINE_LM3S811EVB], + test_watchdog_inten_luminary); + } + if (qtest_has_machine(machine_info[MACHINE_MPS2_AN385].machine)) { + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_mps2", + &machine_info[MACHINE_MPS2_AN385], test_watchdog); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset_mps2", + &machine_info[MACHINE_MPS2_AN385], + test_watchdog_reset); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten", + &machine_info[MACHINE_MPS2_AN385], + test_watchdog_inten); + } r = g_test_run(); - qtest_end(); - return r; } diff --git a/tests/qtest/cxl-test.c b/tests/qtest/cxl-test.c index 61f25a72b6..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 " \ - "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.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 " \ - "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,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 " \ + "-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) { @@ -90,14 +112,55 @@ static void cxl_2root_port(void) } #ifdef CONFIG_POSIX -static void cxl_t3d(void) +static void cxl_t3d_deprecated(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, 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(); @@ -147,7 +210,10 @@ int main(int argc, char **argv) qtest_add_func("/pci/cxl/rp", cxl_root_port); qtest_add_func("/pci/cxl/rp_x2", cxl_2root_port); #ifdef CONFIG_POSIX - qtest_add_func("/pci/cxl/type3_device", cxl_t3d); + 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 diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c index cb1b62d1d1..f7fc873bfb 100644 --- a/tests/qtest/dbus-display-test.c +++ b/tests/qtest/dbus-display-test.c @@ -1,8 +1,15 @@ #include "qemu/osdep.h" +#include "qemu/sockets.h" #include "qemu/dbus.h" +#include "qemu/sockets.h" +#include "glib.h" +#include "glibconfig.h" #include #include #include "libqtest.h" +#ifndef WIN32 +#include +#endif #include "ui/dbus-display1.h" static GDBusConnection* @@ -13,7 +20,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 +47,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]); @@ -76,6 +87,7 @@ typedef struct TestDBusConsoleRegister { GThread *thread; GDBusConnection *listener_conn; GDBusObjectManagerServer *server; + bool with_map; } TestDBusConsoleRegister; static gboolean listener_handle_scanout( @@ -88,13 +100,49 @@ static gboolean listener_handle_scanout( GVariant *arg_data, TestDBusConsoleRegister *test) { - g_main_loop_quit(test->loop); + if (!test->with_map) { + g_main_loop_quit(test->loop); + } return DBUS_METHOD_INVOCATION_HANDLED; } +#ifndef WIN32 +static gboolean listener_handle_scanout_map( + QemuDBusDisplay1ListenerUnixMap *object, + GDBusMethodInvocation *invocation, + GUnixFDList *fd_list, + GVariant *arg_handle, + guint arg_offset, + guint arg_width, + guint arg_height, + guint arg_stride, + guint arg_pixman_format, + TestDBusConsoleRegister *test) +{ + int fd = -1; + gint32 handle = g_variant_get_handle(arg_handle); + g_autoptr(GError) error = NULL; + void *addr = NULL; + size_t len = arg_height * arg_stride; + + g_assert_cmpuint(g_unix_fd_list_get_length(fd_list), ==, 1); + fd = g_unix_fd_list_get(fd_list, handle, &error); + g_assert_no_error(error); + + addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, arg_offset); + g_assert_no_errno(addr == MAP_FAILED ? -1 : 0); + g_assert_no_errno(munmap(addr, len)); + + g_main_loop_quit(test->loop); + + close(fd); + return DBUS_METHOD_INVOCATION_HANDLED; +} +#endif + static void -test_dbus_console_setup_listener(TestDBusConsoleRegister *test) +test_dbus_console_setup_listener(TestDBusConsoleRegister *test, bool with_map) { g_autoptr(GDBusObjectSkeleton) listener = NULL; g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL; @@ -108,6 +156,25 @@ test_dbus_console_setup_listener(TestDBusConsoleRegister *test) NULL); g_dbus_object_skeleton_add_interface(listener, G_DBUS_INTERFACE_SKELETON(iface)); + if (with_map) { +#ifdef WIN32 + g_test_skip("map test lacking on win32"); + return; +#else + g_autoptr(QemuDBusDisplay1ListenerUnixMapSkeleton) iface_map = + QEMU_DBUS_DISPLAY1_LISTENER_UNIX_MAP_SKELETON( + qemu_dbus_display1_listener_unix_map_skeleton_new()); + + g_object_connect(iface_map, + "signal::handle-scanout-map", listener_handle_scanout_map, test, + NULL); + g_dbus_object_skeleton_add_interface(listener, + G_DBUS_INTERFACE_SKELETON(iface_map)); + g_object_set(iface, "interfaces", + (const gchar *[]) { "org.qemu.Display1.Listener.Unix.Map", NULL }, + NULL); +#endif + } g_dbus_object_manager_server_export(test->server, listener); g_dbus_object_manager_server_set_connection(test->server, test->listener_conn); @@ -125,11 +192,21 @@ 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); - test_dbus_console_setup_listener(test); + test_dbus_console_setup_listener(test, test->with_map); } static gpointer @@ -139,22 +216,30 @@ test_dbus_p2p_server_setup_thread(gpointer data) } static void -test_dbus_display_console(void) +test_dbus_display_console(const void* data) { 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, .with_map = GPOINTER_TO_INT(data) }; +#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 +255,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 +313,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 +323,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? */ @@ -249,7 +360,8 @@ main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/dbus-display/vm", test_dbus_display_vm); - qtest_add_func("/dbus-display/console", test_dbus_display_console); + qtest_add_data_func("/dbus-display/console", GINT_TO_POINTER(false), test_dbus_display_console); + qtest_add_data_func("/dbus-display/console/map", GINT_TO_POINTER(true), test_dbus_display_console); qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard); return g_test_run(); diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index 5b0ffe43f5..587da59623 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -266,7 +266,6 @@ static void test_device_intro_concrete(const void *args) qobject_unref(types); qtest_quit(qts); - g_free((void *)args); } static void test_abstract_interfaces(void) @@ -310,12 +309,12 @@ static void add_machine_test_case(const char *mname) path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); args = g_strdup_printf("-M %s", mname); - qtest_add_data_func(path, args, test_device_intro_concrete); + qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free); g_free(path); path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); args = g_strdup_printf("-nodefaults -M %s", mname); - qtest_add_data_func(path, args, test_device_intro_concrete); + qtest_add_data_func_full(path, args, test_device_intro_concrete, g_free); g_free(path); } @@ -330,7 +329,7 @@ int main(int argc, char **argv) qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); if (g_test_quick()) { qtest_add_data_func("device/introspect/concrete/defaults/none", - g_strdup(common_args), test_device_intro_concrete); + common_args, test_device_intro_concrete); } else { qtest_cb_for_every_machine(add_machine_test_case, true); } diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index 5a6afa2b57..c6f33153eb 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -64,15 +64,21 @@ static void process_device_remove(QTestState *qtest, const char *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"; } - QTestState *qtest = qtest_initf("%s -device virtio-mouse-pci,id=dev0", - machine_addition); + qtest = qtest_initf("%s -device virtio-mouse-pci,id=dev0", + machine_addition); process_device_remove(qtest, "dev0"); @@ -81,11 +87,17 @@ static void test_pci_unplug_request(void) static void test_q35_pci_unplug_request(void) { + QTestState *qtest; - QTestState *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"); + 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"); @@ -94,14 +106,20 @@ static void test_q35_pci_unplug_request(void) 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( + qtest = qtest_initf( "%s -device \"{'driver': 'virtio-mouse-pci', 'id': 'dev0'}\"", machine_addition); @@ -112,6 +130,7 @@ static void test_pci_unplug_json_request(void) static void test_q35_pci_unplug_json_request(void) { + QTestState *qtest; const char *port = "-device \"{'driver': 'pcie-root-port', " "'id': 'p1'}\""; @@ -123,8 +142,12 @@ static void test_q35_pci_unplug_json_request(void) "'bus': 'b1', " "'id': 'dev0'}\""; - QTestState *qtest = qtest_initf("-machine q35 %s %s %s", - port, bridge, device); + 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"); @@ -133,7 +156,14 @@ static void test_q35_pci_unplug_json_request(void) static void test_ccw_unplug(void) { - QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); + QTestState *qtest; + + 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"); @@ -145,8 +175,8 @@ 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 */ process_device_remove(qtest, "dev0"); 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/dm163-test.c b/tests/qtest/dm163-test.c new file mode 100644 index 0000000000..4c8e654af2 --- /dev/null +++ b/tests/qtest/dm163-test.c @@ -0,0 +1,196 @@ +/* + * QTest testcase for DM163 + * + * Copyright (C) 2024 Samuel Tardieu + * Copyright (C) 2024 Arnaud Minier + * Copyright (C) 2024 Inès Varhol + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +enum DM163_INPUTS { + SIN = 8, + DCK = 9, + RST_B = 10, + LAT_B = 11, + SELBK = 12, + EN_B = 13 +}; + +#define DEVICE_NAME "/machine/dm163" +#define GPIO_OUT(name, value) qtest_set_irq_in(qts, DEVICE_NAME, NULL, name, \ + value) +#define GPIO_PULSE(name) \ + do { \ + GPIO_OUT(name, 1); \ + GPIO_OUT(name, 0); \ + } while (0) + + +static void rise_gpio_pin_dck(QTestState *qts) +{ + /* Configure output mode for pin PB1 */ + qtest_writel(qts, 0x48000400, 0xFFFFFEB7); + /* Write 1 in ODR for PB1 */ + qtest_writel(qts, 0x48000414, 0x00000002); +} + +static void lower_gpio_pin_dck(QTestState *qts) +{ + /* Configure output mode for pin PB1 */ + qtest_writel(qts, 0x48000400, 0xFFFFFEB7); + /* Write 0 in ODR for PB1 */ + qtest_writel(qts, 0x48000414, 0x00000000); +} + +static void rise_gpio_pin_selbk(QTestState *qts) +{ + /* Configure output mode for pin PC5 */ + qtest_writel(qts, 0x48000800, 0xFFFFF7FF); + /* Write 1 in ODR for PC5 */ + qtest_writel(qts, 0x48000814, 0x00000020); +} + +static void lower_gpio_pin_selbk(QTestState *qts) +{ + /* Configure output mode for pin PC5 */ + qtest_writel(qts, 0x48000800, 0xFFFFF7FF); + /* Write 0 in ODR for PC5 */ + qtest_writel(qts, 0x48000814, 0x00000000); +} + +static void rise_gpio_pin_lat_b(QTestState *qts) +{ + /* Configure output mode for pin PC4 */ + qtest_writel(qts, 0x48000800, 0xFFFFFDFF); + /* Write 1 in ODR for PC4 */ + qtest_writel(qts, 0x48000814, 0x00000010); +} + +static void lower_gpio_pin_lat_b(QTestState *qts) +{ + /* Configure output mode for pin PC4 */ + qtest_writel(qts, 0x48000800, 0xFFFFFDFF); + /* Write 0 in ODR for PC4 */ + qtest_writel(qts, 0x48000814, 0x00000000); +} + +static void rise_gpio_pin_rst_b(QTestState *qts) +{ + /* Configure output mode for pin PC3 */ + qtest_writel(qts, 0x48000800, 0xFFFFFF7F); + /* Write 1 in ODR for PC3 */ + qtest_writel(qts, 0x48000814, 0x00000008); +} + +static void lower_gpio_pin_rst_b(QTestState *qts) +{ + /* Configure output mode for pin PC3 */ + qtest_writel(qts, 0x48000800, 0xFFFFFF7F); + /* Write 0 in ODR for PC3 */ + qtest_writel(qts, 0x48000814, 0x00000000); +} + +static void rise_gpio_pin_sin(QTestState *qts) +{ + /* Configure output mode for pin PA4 */ + qtest_writel(qts, 0x48000000, 0xFFFFFDFF); + /* Write 1 in ODR for PA4 */ + qtest_writel(qts, 0x48000014, 0x00000010); +} + +static void lower_gpio_pin_sin(QTestState *qts) +{ + /* Configure output mode for pin PA4 */ + qtest_writel(qts, 0x48000000, 0xFFFFFDFF); + /* Write 0 in ODR for PA4 */ + qtest_writel(qts, 0x48000014, 0x00000000); +} + +static void test_dm163_bank(const void *opaque) +{ + const unsigned bank = (uintptr_t) opaque; + const int width = bank ? 192 : 144; + + QTestState *qts = qtest_initf("-M b-l475e-iot01a"); + qtest_irq_intercept_out_named(qts, DEVICE_NAME, "sout"); + GPIO_OUT(RST_B, 1); + GPIO_OUT(EN_B, 0); + GPIO_OUT(DCK, 0); + GPIO_OUT(SELBK, bank); + GPIO_OUT(LAT_B, 1); + + /* Fill bank with zeroes */ + GPIO_OUT(SIN, 0); + for (int i = 0; i < width; i++) { + GPIO_PULSE(DCK); + } + /* Fill bank with ones, check that we get the previous zeroes */ + GPIO_OUT(SIN, 1); + for (int i = 0; i < width; i++) { + GPIO_PULSE(DCK); + g_assert(!qtest_get_irq(qts, 0)); + } + + /* Pulse one more bit in the bank, check that we get a one */ + GPIO_PULSE(DCK); + g_assert(qtest_get_irq(qts, 0)); + + qtest_quit(qts); +} + +static void test_dm163_gpio_connection(void) +{ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + qtest_irq_intercept_in(qts, DEVICE_NAME); + + g_assert_false(qtest_get_irq(qts, SIN)); + g_assert_false(qtest_get_irq(qts, DCK)); + g_assert_false(qtest_get_irq(qts, RST_B)); + g_assert_false(qtest_get_irq(qts, LAT_B)); + g_assert_false(qtest_get_irq(qts, SELBK)); + + rise_gpio_pin_dck(qts); + g_assert_true(qtest_get_irq(qts, DCK)); + lower_gpio_pin_dck(qts); + g_assert_false(qtest_get_irq(qts, DCK)); + + rise_gpio_pin_lat_b(qts); + g_assert_true(qtest_get_irq(qts, LAT_B)); + lower_gpio_pin_lat_b(qts); + g_assert_false(qtest_get_irq(qts, LAT_B)); + + rise_gpio_pin_selbk(qts); + g_assert_true(qtest_get_irq(qts, SELBK)); + lower_gpio_pin_selbk(qts); + g_assert_false(qtest_get_irq(qts, SELBK)); + + rise_gpio_pin_rst_b(qts); + g_assert_true(qtest_get_irq(qts, RST_B)); + lower_gpio_pin_rst_b(qts); + g_assert_false(qtest_get_irq(qts, RST_B)); + + rise_gpio_pin_sin(qts); + g_assert_true(qtest_get_irq(qts, SIN)); + lower_gpio_pin_sin(qts); + g_assert_false(qtest_get_irq(qts, SIN)); + + g_assert_false(qtest_get_irq(qts, DCK)); + g_assert_false(qtest_get_irq(qts, LAT_B)); + g_assert_false(qtest_get_irq(qts, SELBK)); + g_assert_false(qtest_get_irq(qts, RST_B)); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_data_func("/dm163/bank0", (void *)0, test_dm163_bank); + qtest_add_data_func("/dm163/bank1", (void *)1, test_dm163_bank); + qtest_add_func("/dm163/gpio_connection", test_dm163_gpio_connection); + return g_test_run(); +} diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 9a750395a9..7b67a4bbee 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"); @@ -158,7 +173,7 @@ static void test_drive_without_dev(void) QTestState *qts; /* Start with an empty drive */ - qts = qtest_init("-drive if=none,id=drive0"); + qts = qtest_init("-drive if=none,id=drive0 -M none"); /* Delete the drive */ drive_del(qts); @@ -177,6 +192,11 @@ static void test_after_failed_device_add(void) QDict *response; QTestState *qts; + if (!has_device_builtin("virtio-blk")) { + g_test_skip("Device virtio-blk is not available"); + return; + } + snprintf(driver, sizeof(driver), "virtio-blk-%s", qvirtio_get_dev_type()); @@ -208,6 +228,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" @@ -232,6 +257,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"; } @@ -256,6 +286,11 @@ 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. @@ -277,6 +312,11 @@ 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()); @@ -291,6 +331,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"; } @@ -330,6 +375,11 @@ 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. @@ -352,6 +402,11 @@ static void test_drive_add_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"; } @@ -374,6 +429,11 @@ 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"); @@ -395,6 +455,11 @@ static void test_blockdev_add_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"; } @@ -417,6 +482,11 @@ 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"); 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/e1000e-test.c b/tests/qtest/e1000e-test.c index 08adc5226d..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,6 +27,7 @@ #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" @@ -35,17 +36,21 @@ #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 e1000_tx_desc descr; - static const int data_len = 64; 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)); @@ -54,7 +59,7 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a E1000_TXD_CMD_EOP | E1000_TXD_CMD_DEXT | E1000_TXD_DTYP_D | - data_len); + sizeof(buffer)); /* Put descriptor to the ring */ e1000e_tx_ring_push(d, &descr); @@ -69,9 +74,9 @@ static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *a /* 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); @@ -81,28 +86,27 @@ static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator { union e1000_rx_desc_extended descr; - 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)); @@ -120,7 +124,7 @@ static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator /* 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..36bbe122ab 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); @@ -109,7 +109,7 @@ static void setup_vm_cmd(ERSTState *s, const char *cmd) g_assert_cmpuint(s->reg_barsize, ==, 16); s->mem_bar = qpci_iomap(s->dev, 1, &s->mem_barsize); - g_assert_cmpuint(s->mem_barsize, ==, 0x2000); + g_assert_cmphex(s->mem_barsize, ==, 0x2000); qpci_device_enable(s->dev); } @@ -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 97ab65c435..8387e74193 100644 --- a/tests/qtest/es1370-test.c +++ b/tests/qtest/es1370-test.c @@ -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 1f9b99ad6d..8645b080f7 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" @@ -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); @@ -553,7 +552,7 @@ static bool qtest_check_clang_sanitizer(void) #ifdef QEMU_SANITIZE_ADDRESS return true; #else - g_test_skip("QEMU not configured using --enable-sanitizers"); + g_test_skip("QEMU not configured using --enable-asan"); return false; #endif } diff --git a/tests/qtest/fuzz-lsi53c895a-test.c b/tests/qtest/fuzz-lsi53c895a-test.c index 9b007def26..1b55928b9f 100644 --- a/tests/qtest/fuzz-lsi53c895a-test.c +++ b/tests/qtest/fuzz-lsi53c895a-test.c @@ -144,6 +144,10 @@ 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); 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 eb7520544b..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); @@ -201,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"); 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 afc1d20355..d107a496da 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/range.h" #include @@ -18,16 +19,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 +49,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 +62,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; @@ -195,6 +197,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; } @@ -209,7 +212,7 @@ void fuzz_dma_read_cb(size_t addr, size_t len, MemoryRegion *mr) i < dma_regions->len && (avoid_double_fetches || qtest_log_enabled); ++i) { region = g_array_index(dma_regions, address_range, i); - if (addr < region.addr + region.size && addr + len > region.addr) { + if (ranges_overlap(addr, len, region.addr, region.size)) { double_fetch = true; if (addr < region.addr && avoid_double_fetches) { @@ -267,6 +270,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; @@ -588,30 +592,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 @@ -668,64 +648,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) @@ -737,8 +686,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); } @@ -824,7 +771,6 @@ static void generic_pre_fuzz(QTestState *s) { GHashTableIter iter; MemoryRegion *mr; - QPCIBus *pcibus; char **result; GString *name_pattern; @@ -837,9 +783,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; /* @@ -882,12 +825,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(); } /* @@ -910,9 +847,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, @@ -1018,17 +955,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 a825b78c14..ef0ad95712 100644 --- a/tests/qtest/fuzz/generic_fuzz_configs.h +++ b/tests/qtest/fuzz/generic_fuzz_configs.h @@ -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", @@ -143,7 +150,8 @@ const generic_fuzz_config predefined_configs[] = { "-chardev null,id=cd0 -chardev null,id=cd1 " "-device usb-braille,chardev=cd0 -device usb-ccid -device usb-ccid " "-device usb-kbd -device usb-mouse -device usb-serial,chardev=cd1 " - "-device usb-tablet -device usb-wacom-tablet -device usb-audio", + "-device usb-tablet -device usb-wacom-tablet " + "-device usb-audio,audiodev=snd0 -audiodev none,id=snd0", .objects = "*usb* *uhci* *xhci*", },{ .name = "pc-i440fx", 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 3a3d9c16dd..d3839bf999 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -46,12 +46,11 @@ static void qos_set_machines_devices_available(void) MachineInfoList *mach_info; ObjectTypeInfoList *type_info; - mach_info = qmp_query_machines(&error_abort); + mach_info = qmp_query_machines(false, false, &error_abort); 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); } @@ -181,6 +180,7 @@ static void walk_path(QOSGraphNode *orig_path, int len) fuzz_path_vec = path_vec; } else { + g_string_free(cmd_line, true); g_free(path_vec); } diff --git a/tests/qtest/fuzz/virtio_blk_fuzz.c b/tests/qtest/fuzz/virtio_blk_fuzz.c index a9fb9ecf6c..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) @@ -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 4a7628077b..85eb8d7668 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -784,14 +784,12 @@ static void test_override_scsi(void) test_override(args, "pc", expected); } -static void setup_pci_bridge(TestArgs *args, const char *id, const char *rootid) +static void setup_pci_bridge(TestArgs *args, const char *id) { - char *root, *br; - root = g_strdup_printf("-device pcie-root-port,id=%s", rootid); - br = g_strdup_printf("-device pcie-pci-bridge,bus=%s,id=%s", rootid, 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, root); args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, br); } @@ -811,8 +809,8 @@ static void test_override_scsi_q35(void) 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.0", "br"); - add_scsi_controller(args, "lsi53c895a", "br", 3); + 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); @@ -868,9 +866,9 @@ static void test_override_virtio_blk_q35(void) }; add_drive_with_mbr(args, empty_mbr, 1); add_drive_with_mbr(args, empty_mbr, 1); - setup_pci_bridge(args, "pcie.0", "br"); - add_virtio_disk(args, 0, "br", 3, 10000, 120, 30); - add_virtio_disk(args, 1, "br", 4, 9000, 120, 30); + 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); } @@ -1076,48 +1074,65 @@ int main(int argc, char **argv) } } - qtest_add_func("hd-geo/ide/none", test_ide_none); - qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); - qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); - qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); - qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); - qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); - qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); - qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); - qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); - qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); - if (have_qemu_img()) { + if (qtest_has_machine("pc")) { + qtest_add_func("hd-geo/ide/none", test_ide_none); + qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); + qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); + qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); + qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); + qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); + qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); + qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); + qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); + qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); + } + + if (!have_qemu_img()) { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping hd-geo/override/* tests"); + goto test_add_done; + } + + if (qtest_has_machine("pc")) { 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); - } - 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_machine("q35")) { - qtest_add_func("hd-geo/override/sata", test_override_sata); - qtest_add_func("hd-geo/override/virtio_blk_q35", - test_override_virtio_blk_q35); - 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_2_controllers", + test_override_scsi_2_controllers); } + } + qtest_add_func("hd-geo/override/zero_chs", test_override_zero_chs); + 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"); } test_add_done: 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/ide-test.c b/tests/qtest/ide-test.c index dbe1563b23..90ba6b298b 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -34,10 +34,8 @@ #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 +/* Specified by ATA (physical) CHS geometry for ~64 MiB device. */ +#define TEST_IMAGE_SIZE ((130 * 16 * 63) * 512) #define IDE_PCI_DEV 1 #define IDE_PCI_FUNC 1 @@ -91,11 +89,13 @@ enum { enum { CMD_DSM = 0x06, CMD_DIAGNOSE = 0x90, + CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_READ_DMA = 0xc8, CMD_WRITE_DMA = 0xca, CMD_FLUSH_CACHE = 0xe7, CMD_IDENTIFY = 0xec, CMD_PACKET = 0xa0, + CMD_READ_NATIVE = 0xf8, /* READ NATIVE MAX ADDRESS */ CMDF_ABORT = 0x100, CMDF_NO_BM = 0x200, @@ -125,6 +125,7 @@ static QGuestAllocator guest_malloc; 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; @@ -562,6 +563,46 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes) } } +static void test_specify(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t cyls; + uint8_t heads, spt; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Initialize drive with zero sectors per track and one head. */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 0); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + + /* READ NATIVE MAX ADDRESS (CHS mode). */ + qpci_io_writeb(dev, ide_bar, reg_device, 0xa0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ_NATIVE); + + heads = qpci_io_readb(dev, ide_bar, reg_device) & 0xf; + ++heads; + g_assert_cmpint(heads, ==, 16); + + cyls = qpci_io_readb(dev, ide_bar, reg_lba_high) << 8; + cyls |= qpci_io_readb(dev, ide_bar, reg_lba_middle); + ++cyls; + g_assert_cmpint(cyls, ==, 130); + + spt = qpci_io_readb(dev, ide_bar, reg_lba_low); + g_assert_cmpint(spt, ==, 63); + + ide_test_quit(qts); + free_pci_device(dev); +} + static void test_identify(void) { QTestState *qts; @@ -765,7 +806,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); @@ -788,7 +829,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); @@ -1079,6 +1120,8 @@ int main(int argc, char **argv) /* Run the tests */ g_test_init(&argc, &argv, NULL); + qtest_add_func("/ide/read_native", test_specify); + qtest_add_func("/ide/identify", test_identify); qtest_add_func("/ide/diagnostic", test_diagnostic); 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 d4a8db6fd6..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("-machine pc -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); diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index ed431e34e6..637732fd5a 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -251,7 +251,7 @@ static void emu_msg_handler(void) msg[msg_len++] = 0xa0; write_emu_msg(msg, msg_len); } else { - g_assert(0); + g_assert_not_reached(); } } @@ -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-ms=10000" " -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/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c index afc24dd3e4..3186c6ad64 100644 --- a/tests/qtest/ipmi-kcs-test.c +++ b/tests/qtest/ipmi-kcs-test.c @@ -145,7 +145,7 @@ static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, break; default: - g_assert(0); + g_assert_not_reached(); } *rsp_len = j; } @@ -184,7 +184,7 @@ static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, break; default: - g_assert(0); + g_assert_not_reached(); } /* Start the abort here */ diff --git a/tests/qtest/ivshmem-test.c b/tests/qtest/ivshmem-test.c index cd550c8935..fb45fdeb07 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); @@ -158,7 +158,7 @@ static void test_ivshmem_single(void) /* trigger interrupt via registers */ out_reg(s, INTRMASK, 0xffffffff); - g_assert_cmpuint(in_reg(s, INTRMASK), ==, 0xffffffff); + g_assert_cmphex(in_reg(s, INTRMASK), ==, 0xffffffff); out_reg(s, INTRSTATUS, 1); /* check interrupt status */ g_assert_cmpuint(in_reg(s, INTRSTATUS), ==, 1); @@ -211,11 +211,11 @@ static void test_ivshmem_pair(void) memset(tmpshmem, 0x42, TMPSHMSIZE); read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x42); + g_assert_cmphex(data[i], ==, 0x42); } read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x42); + g_assert_cmphex(data[i], ==, 0x42); } /* guest 1 write, guest 2 read */ @@ -224,7 +224,7 @@ static void test_ivshmem_pair(void) memset(data, 0, TMPSHMSIZE); read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x43); + g_assert_cmphex(data[i], ==, 0x43); } /* guest 2 write, guest 1 read */ @@ -233,7 +233,7 @@ static void test_ivshmem_pair(void) memset(data, 0, TMPSHMSIZE); read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { - g_assert_cmpuint(data[i], ==, 0x44); + g_assert_cmphex(data[i], ==, 0x44); } cleanup_vm(s1); diff --git a/tests/qtest/libqmp.c b/tests/qtest/libqmp.c index 2b08382e5d..a89cab03c3 100644 --- a/tests/qtest/libqmp.c +++ b/tests/qtest/libqmp.c @@ -134,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/ahci.c b/tests/qtest/libqos/ahci.c index f53f12aa99..34a75b7f43 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); @@ -993,7 +1046,7 @@ static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba) case CMD_ATAPI_REQUEST_SENSE: case CMD_ATAPI_TEST_UNIT_READY: case CMD_ATAPI_START_STOP_UNIT: - g_assert_cmpuint(lba, ==, 0x00); + g_assert_cmphex(lba, ==, 0x00); break; default: /* SCSI doesn't have uniform packet formats, @@ -1056,7 +1109,7 @@ static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) break; case CMD_ATAPI_READ_CD: /* 24bit BE store */ - g_assert_cmpuint(nsectors, <, 1ULL << 24); + g_assert_cmphex(nsectors, <, 1ULL << 24); tmp = nsectors; cbd[6] = (tmp & 0xFF0000) >> 16; cbd[7] = (tmp & 0xFF00) >> 8; @@ -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-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c deleted file mode 100644 index 4e5afe0164..0000000000 --- a/tests/qtest/libqos/arm-n800-machine.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * libqos driver framework - * - * Copyright (c) 2019 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 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 "libqos-malloc.h" -#include "qgraph.h" -#include "i2c.h" - -#define ARM_PAGE_SIZE 4096 -#define N800_RAM_START 0x80000000 -#define N800_RAM_END 0x88000000 - -typedef struct QN800Machine QN800Machine; - -struct QN800Machine { - QOSGraphObject obj; - QGuestAllocator alloc; - OMAPI2C i2c_1; -}; - -static void *n800_get_driver(void *object, const char *interface) -{ - QN800Machine *machine = object; - if (!g_strcmp0(interface, "memory")) { - return &machine->alloc; - } - - fprintf(stderr, "%s not present in arm/n800\n", interface); - g_assert_not_reached(); -} - -static QOSGraphObject *n800_get_device(void *obj, const char *device) -{ - QN800Machine *machine = obj; - if (!g_strcmp0(device, "omap_i2c")) { - return &machine->i2c_1.obj; - } - - fprintf(stderr, "%s not present in arm/n800\n", device); - g_assert_not_reached(); -} - -static void n800_destructor(QOSGraphObject *obj) -{ - QN800Machine *machine = (QN800Machine *) obj; - alloc_destroy(&machine->alloc); -} - -static void *qos_create_machine_arm_n800(QTestState *qts) -{ - QN800Machine *machine = g_new0(QN800Machine, 1); - - alloc_init(&machine->alloc, 0, - N800_RAM_START, - N800_RAM_END, - ARM_PAGE_SIZE); - machine->obj.get_device = n800_get_device; - machine->obj.get_driver = n800_get_driver; - machine->obj.destructor = n800_destructor; - - omap_i2c_init(&machine->i2c_1, qts, 0x48070000); - return &machine->obj; -} - -static void n800_register_nodes(void) -{ - QOSGraphEdgeOptions edge = { - .extra_device_opts = "bus=i2c-bus.0" - }; - qos_node_create_machine("arm/n800", qos_create_machine_arm_n800); - qos_node_contains("arm/n800", "omap_i2c", &edge, NULL); -} - -libqos_init(n800_register_nodes); diff --git a/tests/qtest/libqos/e1000e.c b/tests/qtest/libqos/e1000e.c index 80b3e3db90..925654c7fd 100644 --- a/tests/qtest/libqos/e1000e.c +++ b/tests/qtest/libqos/e1000e.c @@ -32,33 +32,20 @@ #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) | \ - ((E1000E_OTHER_MSG_ID | E1000_IVAR_INT_ALLOC_VALID) << E1000_IVAR_OTHER_SHIFT) | \ E1000_IVAR_TX_INT_EVERY_WB) #define E1000E_RING_LEN (0x1000) -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); -} - 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) / E1000_RING_DESC_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 * E1000_RING_DESC_LEN, descr, E1000_RING_DESC_LEN); - e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % 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, @@ -69,13 +56,13 @@ void e1000e_tx_ring_push(QE1000E *d, void *descr) 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) / E1000_RING_DESC_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 * E1000_RING_DESC_LEN, descr, E1000_RING_DESC_LEN); - e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % 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, @@ -146,18 +133,19 @@ static void e1000e_pci_start_hw(QOSGraphObject *obj) (uint32_t) d->e1000e.tx_ring); 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, 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, E1000_TCTL, E1000_TCTL_EN); + e1000e_macreg_write(&d->e1000e, E1000_RDBAL, (uint32_t)d->e1000e.rx_ring); 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, E1000_RDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000_RDT, 0); e1000e_macreg_write(&d->e1000e, E1000_RDH, 0); /* Enable receive */ @@ -222,8 +210,10 @@ static void e1000e_register_nodes(void) .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/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/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..a446276416 100644 --- a/tests/qtest/libqos/libqos-spapr.h +++ b/tests/qtest/libqos/libqos-spapr.h @@ -3,15 +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 */ -#define PSERIES_DEFAULT_CAPABILITIES \ - "cap-cfpc=broken," \ - "cap-sbbc=broken," \ - "cap-ibs=broken," \ - "cap-ccf-assist=off," - #endif diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 5ffda080ec..5c0fa1f7c5 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -137,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 9b4dd509f0..c04950e2b1 100644 --- a/tests/qtest/libqos/libqos.h +++ b/tests/qtest/libqos/libqos.h @@ -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/loongarch-virt-machine.c b/tests/qtest/libqos/loongarch-virt-machine.c new file mode 100644 index 0000000000..c12089c015 --- /dev/null +++ b/tests/qtest/libqos/loongarch-virt-machine.c @@ -0,0 +1,114 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.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 LOONGARCH_PAGE_SIZE 0x1000 +#define LOONGARCH_VIRT_RAM_ADDR 0x100000 +#define LOONGARCH_VIRT_RAM_SIZE 0xFF00000 + +#define LOONGARCH_VIRT_PIO_BASE 0x18000000 +#define LOONGARCH_VIRT_PCIE_PIO_OFFSET 0x4000 +#define LOONGARCH_VIRT_PCIE_PIO_LIMIT 0x10000 +#define LOONGARCH_VIRT_PCIE_ECAM_BASE 0x20000000 +#define LOONGARCH_VIRT_PCIE_MMIO32_BASE 0x40000000 +#define LOONGARCH_VIRT_PCIE_MMIO32_LIMIT 0x80000000 + +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 loongarch/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 loongarch/virt\n", device); + g_assert_not_reached(); +} + +static void loongarch_config_qpci_bus(QGenericPCIBus *qpci) +{ + qpci->gpex_pio_base = LOONGARCH_VIRT_PIO_BASE; + qpci->bus.pio_alloc_ptr = LOONGARCH_VIRT_PCIE_PIO_OFFSET; + qpci->bus.pio_limit = LOONGARCH_VIRT_PCIE_PIO_LIMIT; + qpci->bus.mmio_alloc_ptr = LOONGARCH_VIRT_PCIE_MMIO32_BASE; + qpci->bus.mmio_limit = LOONGARCH_VIRT_PCIE_MMIO32_LIMIT; + qpci->ecam_alloc_ptr = LOONGARCH_VIRT_PCIE_ECAM_BASE; +} + +static void *qos_create_machine_loongarch_virt(QTestState *qts) +{ + QVirtMachine *machine = g_new0(QVirtMachine, 1); + + alloc_init(&machine->alloc, 0, + LOONGARCH_VIRT_RAM_ADDR, + LOONGARCH_VIRT_RAM_ADDR + LOONGARCH_VIRT_RAM_SIZE, + LOONGARCH_PAGE_SIZE); + + qos_create_generic_pcihost(&machine->bridge, qts, &machine->alloc); + loongarch_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("loongarch64/virt", + qos_create_machine_loongarch_virt, + " -cpu la464"); + qos_node_contains("loongarch64/virt", "generic-pcihost", NULL); +} + +libqos_init(virt_machine_register_nodes); diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 32f028872c..46f130ccfd 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -30,6 +30,7 @@ libqos_srcs = files( 'i2c.c', 'i2c-imx.c', 'i2c-omap.c', + 'igb.c', 'sdhci.c', 'tpci200.c', 'virtio.c', @@ -45,12 +46,12 @@ libqos_srcs = files( 'virtio-serial.c', 'virtio-iommu.c', 'virtio-gpio.c', + 'virtio-scmi.c', 'generic-pcihost.c', # qgraph machines: 'aarch64-xlnx-zcu102-machine.c', 'arm-imx25-pdk-machine.c', - 'arm-n800-machine.c', 'arm-raspi2-machine.c', 'arm-sabrelite-machine.c', 'arm-smdkc210-machine.c', @@ -58,14 +59,19 @@ libqos_srcs = files( 'arm-xilinx-zynq-a9-machine.c', 'ppc64_pseries-machine.c', 'x86_64_pc-machine.c', + 'riscv-virt-machine.c', + 'loongarch-virt-machine.c', ) if have_virtfs libqos_srcs += files('virtio-9p.c', 'virtio-9p-client.c') endif +if config_all_devices.has_key('CONFIG_RISCV_IOMMU') + libqos_srcs += files('riscv-iommu.c') +endif + libqos = static_library('qos', libqos_srcs + genh, - name_suffix: 'fa', build_by_default: false) -qos = declare_dependency(link_whole: libqos) +qos = declare_dependency(objects: libqos.extract_all_objects(recursive: false)) 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 287022a67c..1b5de02e7b 100644 --- a/tests/qtest/libqos/qgraph.h +++ b/tests/qtest/libqos/qgraph.h @@ -24,7 +24,7 @@ #include "libqos-malloc.h" /* maximum path length */ -#define QOS_PATH_MAX_ELEMENT_SIZE 64 +#define QOS_PATH_MAX_ELEMENT_SIZE 128 typedef struct QOSGraphObject QOSGraphObject; typedef struct QOSGraphNode QOSGraphNode; 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/riscv-iommu.c b/tests/qtest/libqos/riscv-iommu.c new file mode 100644 index 0000000000..01e3b31c0b --- /dev/null +++ b/tests/qtest/libqos/riscv-iommu.c @@ -0,0 +1,76 @@ +/* + * libqos driver riscv-iommu-pci framework + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * 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 "../libqtest.h" +#include "qemu/module.h" +#include "qgraph.h" +#include "pci.h" +#include "riscv-iommu.h" + +static void *riscv_iommu_pci_get_driver(void *obj, const char *interface) +{ + QRISCVIOMMU *r_iommu_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &r_iommu_pci->dev; + } + + fprintf(stderr, "%s not present in riscv_iommu_pci\n", interface); + g_assert_not_reached(); +} + +static void riscv_iommu_pci_start_hw(QOSGraphObject *obj) +{ + QRISCVIOMMU *pci = (QRISCVIOMMU *)obj; + qpci_device_enable(&pci->dev); +} + +static void riscv_iommu_pci_destructor(QOSGraphObject *obj) +{ + QRISCVIOMMU *pci = (QRISCVIOMMU *)obj; + qpci_iounmap(&pci->dev, pci->reg_bar); +} + +static void *riscv_iommu_pci_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) +{ + QRISCVIOMMU *r_iommu_pci = g_new0(QRISCVIOMMU, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&r_iommu_pci->dev, bus, addr); + r_iommu_pci->reg_bar = qpci_iomap(&r_iommu_pci->dev, 0, NULL); + + r_iommu_pci->obj.get_driver = riscv_iommu_pci_get_driver; + r_iommu_pci->obj.start_hw = riscv_iommu_pci_start_hw; + r_iommu_pci->obj.destructor = riscv_iommu_pci_destructor; + return &r_iommu_pci->obj; +} + +static void riscv_iommu_pci_register_nodes(void) +{ + QPCIAddress addr = { + .vendor_id = RISCV_IOMMU_PCI_VENDOR_ID, + .device_id = RISCV_IOMMU_PCI_DEVICE_ID, + .devfn = QPCI_DEVFN(1, 0), + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=01.0", + }; + + add_qpci_address(&opts, &addr); + + qos_node_create_driver("riscv-iommu-pci", riscv_iommu_pci_create); + qos_node_produces("riscv-iommu-pci", "pci-device"); + qos_node_consumes("riscv-iommu-pci", "pci-bus", &opts); +} + +libqos_init(riscv_iommu_pci_register_nodes); diff --git a/tests/qtest/libqos/riscv-iommu.h b/tests/qtest/libqos/riscv-iommu.h new file mode 100644 index 0000000000..318db13799 --- /dev/null +++ b/tests/qtest/libqos/riscv-iommu.h @@ -0,0 +1,101 @@ +/* + * libqos driver riscv-iommu-pci framework + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * 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 TESTS_LIBQOS_RISCV_IOMMU_H +#define TESTS_LIBQOS_RISCV_IOMMU_H + +#include "qgraph.h" +#include "pci.h" +#include "qemu/bitops.h" + +#ifndef GENMASK_ULL +#define GENMASK_ULL(h, l) (((~0ULL) >> (63 - (h) + (l))) << (l)) +#endif + +/* + * RISC-V IOMMU uses PCI_VENDOR_ID_REDHAT 0x1b36 and + * PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014. + */ +#define RISCV_IOMMU_PCI_VENDOR_ID 0x1b36 +#define RISCV_IOMMU_PCI_DEVICE_ID 0x0014 +#define RISCV_IOMMU_PCI_DEVICE_CLASS 0x0806 + +/* Common field positions */ +#define RISCV_IOMMU_QUEUE_ENABLE BIT(0) +#define RISCV_IOMMU_QUEUE_INTR_ENABLE BIT(1) +#define RISCV_IOMMU_QUEUE_MEM_FAULT BIT(8) +#define RISCV_IOMMU_QUEUE_ACTIVE BIT(16) +#define RISCV_IOMMU_QUEUE_BUSY BIT(17) + +#define RISCV_IOMMU_REG_CAP 0x0000 +#define RISCV_IOMMU_CAP_VERSION GENMASK_ULL(7, 0) + +#define RISCV_IOMMU_REG_DDTP 0x0010 +#define RISCV_IOMMU_DDTP_BUSY BIT_ULL(4) +#define RISCV_IOMMU_DDTP_MODE GENMASK_ULL(3, 0) +#define RISCV_IOMMU_DDTP_MODE_OFF 0 + +#define RISCV_IOMMU_REG_CQCSR 0x0048 +#define RISCV_IOMMU_CQCSR_CQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_CQCSR_CIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_CQCSR_CQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_CQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +#define RISCV_IOMMU_REG_FQCSR 0x004C +#define RISCV_IOMMU_FQCSR_FQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_FQCSR_FIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_FQCSR_FQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_FQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +#define RISCV_IOMMU_REG_PQCSR 0x0050 +#define RISCV_IOMMU_PQCSR_PQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_PQCSR_PIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_PQCSR_PQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_PQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +#define RISCV_IOMMU_REG_IPSR 0x0054 + +#define RISCV_IOMMU_REG_IVEC 0x02F8 +#define RISCV_IOMMU_REG_IVEC_CIV GENMASK_ULL(3, 0) +#define RISCV_IOMMU_REG_IVEC_FIV GENMASK_ULL(7, 4) +#define RISCV_IOMMU_REG_IVEC_PMIV GENMASK_ULL(11, 8) +#define RISCV_IOMMU_REG_IVEC_PIV GENMASK_ULL(15, 12) + +#define RISCV_IOMMU_REG_CQB 0x0018 +#define RISCV_IOMMU_CQB_PPN_START 10 +#define RISCV_IOMMU_CQB_PPN_LEN 44 +#define RISCV_IOMMU_CQB_LOG2SZ_START 0 +#define RISCV_IOMMU_CQB_LOG2SZ_LEN 5 + +#define RISCV_IOMMU_REG_CQT 0x0024 + +#define RISCV_IOMMU_REG_FQB 0x0028 +#define RISCV_IOMMU_FQB_PPN_START 10 +#define RISCV_IOMMU_FQB_PPN_LEN 44 +#define RISCV_IOMMU_FQB_LOG2SZ_START 0 +#define RISCV_IOMMU_FQB_LOG2SZ_LEN 5 + +#define RISCV_IOMMU_REG_FQT 0x0034 + +#define RISCV_IOMMU_REG_PQB 0x0038 +#define RISCV_IOMMU_PQB_PPN_START 10 +#define RISCV_IOMMU_PQB_PPN_LEN 44 +#define RISCV_IOMMU_PQB_LOG2SZ_START 0 +#define RISCV_IOMMU_PQB_LOG2SZ_LEN 5 + +#define RISCV_IOMMU_REG_PQT 0x0044 + +typedef struct QRISCVIOMMU { + QOSGraphObject obj; + QPCIDevice dev; + QPCIBar reg_bar; +} QRISCVIOMMU; + +#endif 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/sdhci-cmd.h b/tests/qtest/libqos/sdhci-cmd.h index 9e61dd4944..90efa028ef 100644 --- a/tests/qtest/libqos/sdhci-cmd.h +++ b/tests/qtest/libqos/sdhci-cmd.h @@ -22,6 +22,7 @@ #define SDHC_ARGUMENT 0x08 #define SDHC_TRNMOD 0x0C #define SDHC_CMDREG 0x0E +#define SDHC_RSPREG0 0x10 #define SDHC_BDATA 0x20 #define SDHC_PRNSTS 0x24 #define SDHC_BLKGAP 0x2A @@ -38,6 +39,7 @@ #define SDHC_TRNS_MULTI 0x0020 /* CMD Reg */ +#define SDHC_CMD_RESPONSE (3 << 0) #define SDHC_CMD_DATA_PRESENT (1 << 5) #define SDHC_ALL_SEND_CID (2 << 8) #define SDHC_SEND_RELATIVE_ADDR (3 << 8) diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c index e4a368e036..98b77db51d 100644 --- a/tests/qtest/libqos/virtio-9p-client.c +++ b/tests/qtest/libqos/virtio-9p-client.c @@ -235,10 +235,11 @@ static const char *rmessage_name(uint8_t id) id == P9_RMKDIR ? "RMKDIR" : id == P9_RLCREATE ? "RLCREATE" : id == P9_RSYMLINK ? "RSYMLINK" : + id == P9_RGETATTR ? "RGETATTR" : id == P9_RLINK ? "RLINK" : id == P9_RUNLINKAT ? "RUNLINKAT" : id == P9_RFLUSH ? "RFLUSH" : - id == P9_RREADDIR ? "READDIR" : + id == P9_RREADDIR ? "RREADDIR" : ""; } @@ -594,6 +595,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, { 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; @@ -612,6 +615,8 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, e = g_new(struct V9fsDirent, 1); if (entries) { *entries = e; + } else { + unused_entries = e; } } else { e = e->next = g_new(struct V9fsDirent, 1); @@ -628,6 +633,7 @@ void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, *nentries = n; } + v9fs_free_dirents(unused_entries); v9fs_req_free(req); } diff --git a/tests/qtest/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c index 7f21028256..186fcc1141 100644 --- a/tests/qtest/libqos/virtio-9p.c +++ b/tests/qtest/libqos/virtio-9p.c @@ -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 index f22d7b5eb5..9220d287fe 100644 --- a/tests/qtest/libqos/virtio-gpio.c +++ b/tests/qtest/libqos/virtio-gpio.c @@ -28,7 +28,7 @@ static void virtio_gpio_cleanup(QVhostUserGPIO *gpio) /* * This handles the VirtIO setup from the point of view of the driver - * frontend and therefor doesn't present any vhost specific features + * 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) 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 410513225f..a21b6eee9c 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -265,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); } @@ -280,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; @@ -381,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/libqtest.c b/tests/qtest/libqtest.c index 2fbc3b88f3..817fd7aac5 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -24,6 +24,9 @@ #ifdef __linux__ #include #endif /* __linux__ */ +#ifdef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ #include "libqtest.h" #include "libqmp.h" @@ -34,6 +37,7 @@ #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" +#include "qapi/qmp/qbool.h" #define MAX_IRQ 256 @@ -49,6 +53,8 @@ # 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); typedef GString* (*QTestRecvFn)(QTestState *); @@ -80,10 +86,13 @@ struct QTestState GString *rx; QTestTransportOps ops; GList *pending_events; + QTestQMPEventCallback eventCB; + void *eventData; }; static GHookList abrt_hooks; static void (*sighandler_old)(int); +static bool silence_spawn_log; static int qtest_query_target_endianness(QTestState *s); @@ -108,7 +117,7 @@ static int socket_accept(int sock) socklen_t addrlen; int ret; /* - * timeout unit of blocking receive calls is different among platfoms. + * timeout unit of blocking receive calls is different among platforms. * It's in seconds on non-Windows platforms but milliseconds on Windows. */ #ifndef _WIN32 @@ -122,7 +131,7 @@ static int socket_accept(int sock) (void *)&timeout, sizeof(timeout))) { fprintf(stderr, "%s failed to set SO_RCVTIMEO: %s\n", __func__, strerror(errno)); - closesocket(sock); + close(sock); return -1; } @@ -133,11 +142,16 @@ static int socket_accept(int sock) if (ret == -1) { fprintf(stderr, "%s failed: %s\n", __func__, strerror(errno)); } - closesocket(sock); + close(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; @@ -156,6 +170,7 @@ bool qtest_probe_child(QTestState *s) CloseHandle((HANDLE)pid); #endif s->qemu_pid = -1; + qtest_remove_abrt_handler(s); } return false; } @@ -167,6 +182,8 @@ void qtest_set_expected_status(QTestState *s, int status) static void qtest_check_status(QTestState *s) { + 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. @@ -200,20 +217,40 @@ static void qtest_check_status(QTestState *s) void qtest_wait_qemu(QTestState *s) { + if (s->qemu_pid != -1) { #ifndef _WIN32 - pid_t pid; + pid_t pid; + uint64_t end; - TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); - assert(pid == s->qemu_pid); + /* 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; + 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); + 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); } @@ -227,7 +264,6 @@ void qtest_kill_qemu(QTestState *s) TerminateProcess((HANDLE)s->qemu_pid, s->expected_status); #endif qtest_wait_qemu(s); - s->qemu_pid = -1; return; } @@ -289,6 +325,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 */ @@ -297,10 +338,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"); @@ -342,60 +390,27 @@ static pid_t qtest_create_process(char *cmd) } #endif /* _WIN32 */ -QTestState *qtest_init_without_qmp_handshake(const char *extra_args) +static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, + const char *fmt, ...) { - QTestState *s; - int sock, qmpsock, i; - gchar *socket_path; - gchar *qmp_socket_path; - gchar *command; - const char *qemu_binary = qtest_qemu_binary(); + 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("%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); - - 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(CMD_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_STDERR : 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) { @@ -413,17 +428,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); + s->qemu_pid = qtest_create_process(command->str); #endif /* _WIN32 */ - g_free(command); + 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); + s->fd = socket_accept(sock); if (s->fd >= 0) { s->qmp_fd = socket_accept(qmpsock); @@ -458,12 +519,17 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) s->big_endian = qtest_query_target_endianness(s); - return s; + 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 */ @@ -474,6 +540,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); @@ -528,8 +599,8 @@ void qtest_quit(QTestState *s) qtest_remove_abrt_handler(s); qtest_kill_qemu(s); - closesocket(s->fd); - closesocket(s->qmp_fd); + close(s->fd); + close(s->qmp_fd); g_string_free(s->rx, true); for (GList *it = s->pending_events; it != NULL; it = it->next) { @@ -667,8 +738,15 @@ 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); + } else { + qobject_unref(response); + } } } @@ -689,9 +767,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); @@ -774,6 +850,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) { @@ -847,7 +930,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) { @@ -940,6 +1023,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); @@ -1195,25 +1284,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; @@ -1269,13 +1474,32 @@ struct MachInfo { char *alias; }; +struct CpuModel { + char *name; + char *alias_of; + bool deprecated; +}; + +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; @@ -1284,11 +1508,22 @@ static struct MachInfo *qtest_get_machines(void) QTestState *qts; int idx; + if (g_strcmp0(qemu_var, var)) { + g_free(qemu_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"); @@ -1319,22 +1554,102 @@ 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; } +static struct CpuModel *qtest_get_cpu_models(void) +{ + static struct CpuModel *cpus; + QDict *response, *minfo; + QList *list; + const QListEntry *p; + QObject *qobj; + QString *qstr; + QBool *qbool; + QTestState *qts; + int idx; + + if (cpus) { + return cpus; + } + + silence_spawn_log = !g_test_verbose(); + + qts = qtest_init_with_env(NULL, "-machine none"); + response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }"); + g_assert(response); + list = qdict_get_qlist(response, "return"); + g_assert(list); + + cpus = g_new0(struct CpuModel, qlist_size(list) + 1); + + for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + g_assert(minfo); + + qobj = qdict_get(minfo, "name"); + g_assert(qobj); + qstr = qobject_to(QString, qobj); + g_assert(qstr); + cpus[idx].name = g_strdup(qstring_get_str(qstr)); + + qobj = qdict_get(minfo, "alias_of"); + if (qobj) { /* old machines do not report aliases */ + qstr = qobject_to(QString, qobj); + g_assert(qstr); + cpus[idx].alias_of = g_strdup(qstring_get_str(qstr)); + } else { + cpus[idx].alias_of = NULL; + } + + qobj = qdict_get(minfo, "deprecated"); + qbool = qobject_to(QBool, qobj); + g_assert(qbool); + cpus[idx].deprecated = qbool_get_bool(qbool); + } + + qtest_quit(qts); + qobject_unref(response); + + silence_spawn_log = false; + + return cpus; +} + +bool qtest_has_cpu_model(const char *cpu) +{ + struct CpuModel *cpus; + int i; + + cpus = qtest_get_cpu_models(); + + for (i = 0; cpus[i].name != NULL; i++) { + if (g_str_equal(cpu, cpus[i].name) || + (cpus[i].alias_of && g_str_equal(cpu, cpus[i].alias_of))) { + return true; + } + } + + return false; +} + void qtest_cb_for_every_machine(void (*cb)(const char *machine), bool skip_old_versioned) { 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) || + g_str_equal("nitro-enclave", machines[i].name)) { continue; } if (!skip_old_versioned || @@ -1344,12 +1659,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) || @@ -1361,6 +1692,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; @@ -1419,6 +1755,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); } @@ -1440,13 +1780,28 @@ void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, qobject_unref(args); } -#ifndef _WIN32 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")); @@ -1460,7 +1815,6 @@ void qtest_qmp_add_client(QTestState *qts, const char *protocol, int fd) g_assert(!qdict_haskey(resp, "error")); qobject_unref(resp); } -#endif /* * Generic hot-unplugging test via the device_del QMP command. @@ -1536,7 +1890,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); @@ -1547,7 +1901,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); - g_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; @@ -1587,3 +1945,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 fcf1c3c3b3..beb96b18eb 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 @@ -238,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); @@ -258,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); @@ -332,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. @@ -693,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 @@ -706,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 @@ -716,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 @@ -724,6 +940,23 @@ 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_cpu_model: + * @cpu: The cpu to look for + * + * Returns: true if the cpu is available in the target binary. + */ +bool qtest_has_cpu_model(const char *cpu); + /** * qtest_has_device: * @device: The device to look for @@ -758,17 +991,16 @@ void qtest_qmp_device_add_qdict(QTestState *qts, const char *drv, void qtest_qmp_device_add(QTestState *qts, const char *driver, const char *id, const char *fmt, ...) G_GNUC_PRINTF(4, 5); -#ifndef _WIN32 /** * qtest_qmp_add_client: * @qts: QTestState instance to operate on * @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); -#endif /* _WIN32 */ /** * qtest_qmp_device_del_send: @@ -832,4 +1064,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 843d2ced8e..605797ab78 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -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 */ @@ -259,11 +262,12 @@ int main(int argc, char **argv) base_setup(); g_test_init(&argc, &argv, NULL); - - if (g_test_slow()) { - /* Do not run this in timing-sensitive environments */ - qtest_add_func("/rtc/bcd-check-time", bcd_check_time); + if (qtest_has_machine(base_machine)) { + if (g_test_slow()) { + /* Do not run this in timing-sensitive environments */ + qtest_add_func("/rtc/bcd-check-time", bcd_check_time); + } + qtest_add_func("/rtc/fuzz-registers", fuzz_registers); } - qtest_add_func("/rtc/fuzz-registers", fuzz_registers); return g_test_run(); } diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index 31cc0bfb01..159b2a705a 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -30,7 +30,6 @@ static struct arch2cpu cpus_map[] = { { "x86_64", "qemu64,apic-id=0" }, { "i386", "qemu32,apic-id=0" }, { "alpha", "ev67" }, - { "cris", "crisv32" }, { "m68k", "m5206" }, { "microblaze", "any" }, { "microblazeel", "any" }, @@ -38,7 +37,6 @@ static struct arch2cpu cpus_map[] = { { "mipsel", "I7200" }, { "mips64", "20Kc" }, { "mips64el", "I6500" }, - { "nios2", "FIXME" }, { "or1k", "or1200" }, { "ppc", "604" }, { "ppc64", "power8e_v2.1" }, 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 c07a5b1a5f..bd41c9da5f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -1,20 +1,22 @@ -# 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, + 'ahci-test': 150, + 'aspeed_smc-test': 360, + 'bios-tables-test' : 910, + 'cdrom-test' : 610, + 'device-introspect-test' : 720, + 'ide-test' : 120, + 'migration-test' : 480, + 'npcm7xx_pwm-test': 300, + 'npcm7xx_watchdog_timer-test': 120, + 'qmp-cmd-test' : 120, + 'qom-test' : 900, + 'stm32l4x5_usart-test' : 600, + 'test-hmp' : 240, + 'pxe-test': 610, + 'prom-env-test': 360, + 'boot-serial-test': 360, + 'qos-test': 120, + 'vmgenid-test': 610, } qtests_generic = [ @@ -27,8 +29,9 @@ qtests_generic = [ 'test-hmp', 'qos-test', 'readconfig-test', + 'netdev-socket', ] -if config_host.has_key('CONFIG_MODULES') +if enable_modules qtests_generic += [ 'modules-test' ] endif @@ -39,20 +42,31 @@ 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 = \ - (slirp.found() ? ['test-netfilter'] : []) + \ - (config_host.has_key('CONFIG_POSIX') ? ['test-filter-mirror'] : []) + \ - (config_host.has_key('CONFIG_POSIX') ? ['test-filter-redirector'] : []) + (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'] : []) + \ qtests_filter + \ - (have_tools ? ['ahci-test'] : []) + \ + (config_all_devices.has_key('CONFIG_ACPI_VMGENID') ? ['vmgenid-test'] : []) + \ + (config_all_devices.has_key('CONFIG_AHCI_ICH9') and have_tools ? ['ahci-test'] : []) + \ + (config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \ + (config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['test-x86-cpuid-compat'] : []) + \ (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'] : []) + \ @@ -60,6 +74,7 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : []) + \ (config_all_devices.has_key('CONFIG_IOH3420') ? ['ioh3420-test'] : []) + \ (config_all_devices.has_key('CONFIG_LPC_ICH9') ? ['lpc-ich9-test'] : []) + \ + (config_all_devices.has_key('CONFIG_MC146818RTC') ? ['rtc-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') and \ config_all_devices.has_key('CONFIG_USB_EHCI') ? ['usb-hcd-ehci-test'] : []) + \ @@ -73,37 +88,33 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \ (config_all_devices.has_key('CONFIG_LSI_SCSI_PCI') ? ['fuzz-lsi53c895a-test'] : []) + \ (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VIRTIO_BALLOON') ? ['virtio-balloon-test'] : []) + \ + (config_all_devices.has_key('CONFIG_Q35') ? ['q35-test'] : []) + \ (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ - (config_host.has_key('CONFIG_POSIX') and \ + (host_os != 'windows' and \ config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ - (config_all_devices.has_key('CONFIG_VIRTIO_NET') and \ + (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', - 'ide-test', + [ 'hd-geo-test', 'boot-order-test', - 'rtc-test', - 'i440fx-test', - 'fw_cfg-test', 'device-plug-test', 'drive_del-test', - 'tco-test', 'cpu-plug-test', - 'q35-test', - 'vmgenid-test', 'migration-test', - 'test-x86-cpuid-compat', - 'numa-test' ] -if dbus_display +if dbus_display and config_all_devices.has_key('CONFIG_VGA') qtests_i386 += ['dbus-display-test'] endif @@ -133,6 +144,10 @@ qtests_hppa = ['boot-serial-test'] + \ qtests_filter + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) +qtests_loongarch64 = qtests_filter + \ + (config_all_devices.has_key('CONFIG_LOONGARCH_VIRT') ? ['numa-test'] : []) + \ + ['boot-serial-test'] + qtests_m68k = ['boot-serial-test'] + \ qtests_filter @@ -154,19 +169,23 @@ qtests_ppc = \ 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-xive2-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-spi-seeprom-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \ + (config_all_devices.has_key('CONFIG_PSERIES') ? ['numa-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'] : []) + \ (config_all_devices.has_key('CONFIG_USB_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \ - qtests_pci + ['migration-test', 'numa-test', 'cpu-plug-test', 'drive_del-test'] + qtests_pci + ['migration-test', 'cpu-plug-test', 'drive_del-test'] qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) @@ -187,42 +206,62 @@ 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_aspeed64 = \ + ['ast2700-gpio-test'] + +qtests_stm32l4x5 = \ + ['stm32l4x5_exti-test', + 'stm32l4x5_syscfg-test', + 'stm32l4x5_rcc-test', + 'stm32l4x5_gpio-test', + 'stm32l4x5_usart-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_STELLARIS') or + config_all_devices.has_key('CONFIG_MPS2') ? ['cmsdk-apb-watchdog-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'] : []) + \ + (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') and + config_all_devices.has_key('CONFIG_DM163')? ['dm163-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'] : []) + \ + (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', - 'migration-test', - 'bcm2835-dma-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', @@ -230,10 +269,17 @@ qtests_s390x = \ 'cpu-plug-test', 'migration-test'] +qtests_riscv32 = \ + (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) + +qtests_riscv64 = \ + (unpack_edk2_blobs ? ['bios-tables-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', 'eepro100-test.c', @@ -259,11 +305,18 @@ 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', + 'riscv-iommu-test.c', ) -if config_host.has_key('CONFIG_POSIX') + +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 @@ -283,8 +336,7 @@ if gnutls.found() migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] if tasn1.found() - migration_files += [files('../unit/crypto-tls-x509-helpers.c', - '../unit/pkix_asn1_tab.c'), tasn1] + migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] endif endif @@ -296,20 +348,26 @@ 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()], + 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c'), + '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'), } -gvnc = dependency('gvnc-1.0', required: false) -if gvnc.found() - qtests += {'vnc-display-test': [gvnc]} - qtests_generic += [ 'vnc-display-test' ] +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 @@ -339,6 +397,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 @@ -362,8 +422,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 4bc267020b..505c831f13 100644 --- a/tests/qtest/microbit-test.c +++ b/tests/qtest/microbit-test.c @@ -107,7 +107,7 @@ static void test_nrf51_uart(void) g_assert_true(recv(sock_fd, s, 10, 0) == 5); g_assert_true(memcmp(s, "world", 5) == 0); - closesocket(sock_fd); + close(sock_fd); qtest_quit(qts); } @@ -143,14 +143,14 @@ static void test_microbit_i2c(void) /* MMA8653 magnetometer detection */ val = i2c_read_byte(qts, 0x3A, 0x0D); - g_assert_cmpuint(val, ==, 0x5A); + g_assert_cmphex(val, ==, 0x5A); val = i2c_read_byte(qts, 0x3A, 0x0D); - g_assert_cmpuint(val, ==, 0x5A); + g_assert_cmphex(val, ==, 0x5A); /* LSM303 accelerometer detection */ val = i2c_read_byte(qts, 0x3C, 0x4F); - g_assert_cmpuint(val, ==, 0x40); + g_assert_cmphex(val, ==, 0x40); qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0); @@ -171,7 +171,7 @@ static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size, /* Check memory */ for (i = 0; i < size / 4; i++) { - g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); + g_assert_cmphex(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); } /* Fill memory */ @@ -191,7 +191,7 @@ static void test_nrf51_nvmc(void) /* Test always ready */ value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY); - g_assert_cmpuint(value & 0x01, ==, 0x01); + g_assert_cmphex(value & 0x01, ==, 0x01); /* Test write-read config register */ qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03); @@ -302,19 +302,19 @@ static void test_nrf51_gpio(void) g_assert_cmpuint(actual, ==, expected); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); /* Check clear via DIRCLR */ qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); - g_assert_cmpuint(actual, ==, 0x00000000); + g_assert_cmphex(actual, ==, 0x00000000); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); /* Check set via DIR */ expected = 0x80000001; @@ -323,9 +323,9 @@ static void test_nrf51_gpio(void) g_assert_cmpuint(actual, ==, expected); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); /* Reset DIR */ qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); @@ -334,33 +334,33 @@ static void test_nrf51_gpio(void) qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); /* Check pull-up working */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); /* Check pull-down working */ qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); @@ -376,11 +376,11 @@ static void test_nrf51_gpio(void) qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x01); + g_assert_cmphex(actual, ==, 0x01); qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; - g_assert_cmpuint(actual, ==, 0x00); + g_assert_cmphex(actual, ==, 0x00); /* * Check short-circuit - generates an guest_error which must be checked @@ -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_cmphex(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_cmphex(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); @@ -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 f6f3c6680f..3f8ba7fa8e 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -11,7 +11,14 @@ */ #include "qemu/osdep.h" +#include "qemu/ctype.h" #include "qapi/qmp/qjson.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/error.h" +#include "qapi/qmp/qlist.h" +#include "qemu/cutils.h" +#include "qemu/memalign.h" #include "migration-helpers.h" @@ -23,88 +30,170 @@ */ #define MIGRATION_STATUS_WAIT_TIMEOUT 120 -bool got_stop; - -static void check_stop_event(QTestState *who) +static char *SocketAddress_to_str(SocketAddress *addr) { - QDict *event = qtest_qmp_event_ref(who, "STOP"); - if (event) { - got_stop = true; - qobject_unref(event); + 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"); } } -#ifndef _WIN32 -/* - * Events can get in the way of responses we are actually waiting for. - */ -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) +static QDict *SocketAddress_to_qdict(SocketAddress *addr) { - va_list ap; - QDict *resp, *ret; + QDict *dict = qdict_new(); - va_start(ap, command); - qtest_qmp_vsend_fds(who, &fd, 1, command, ap); - va_end(ap); + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + qdict_put_str(dict, "type", "inet"); + qdict_put_str(dict, "host", addr->u.inet.host); + qdict_put_str(dict, "port", addr->u.inet.port); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + qdict_put_str(dict, "type", "unix"); + qdict_put_str(dict, "path", addr->u.q_unix.path); + break; + case SOCKET_ADDRESS_TYPE_FD: + qdict_put_str(dict, "type", "fd"); + qdict_put_str(dict, "str", addr->u.fd.str); + break; + case SOCKET_ADDRESS_TYPE_VSOCK: + qdict_put_str(dict, "type", "vsock"); + qdict_put_str(dict, "cid", addr->u.vsock.cid); + qdict_put_str(dict, "port", addr->u.vsock.port); + break; + default: + g_assert_not_reached(); + } - resp = qtest_qmp_receive(who); - 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; -} -#endif - -/* - * 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; + return dict; } -/* - * Execute the qmp command only - */ -QDict *qmp_command(QTestState *who, const char *command, ...) +static SocketAddressList *migrate_get_socket_address(QTestState *who) +{ + QDict *rsp; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, "socket-address"); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); + visit_free(iv); + + qobject_unref(rsp); + return addrs; +} + +static char * +migrate_get_connect_uri(QTestState *who) +{ + SocketAddressList *addrs; + char *connect_uri; + + addrs = migrate_get_socket_address(who); + connect_uri = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_uri; +} + +static QDict * +migrate_get_connect_qdict(QTestState *who) +{ + SocketAddressList *addrs; + QDict *connect_qdict; + + addrs = migrate_get_socket_address(who); + connect_qdict = SocketAddress_to_qdict(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_qdict; +} + +static void migrate_set_ports(QTestState *to, QList *channel_list) +{ + QDict *addr; + QListEntry *entry; + const char *addr_port = NULL; + + addr = migrate_get_connect_qdict(to); + + QLIST_FOREACH_ENTRY(channel_list, entry) { + QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); + QDict *addrdict = qdict_get_qdict(channel, "addr"); + + if (qdict_haskey(addrdict, "port") && + qdict_haskey(addr, "port") && + (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { + addr_port = qdict_get_str(addr, "port"); + qdict_put_str(addrdict, "port", addr_port); + } + } + + qobject_unref(addr); +} + +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque) +{ + 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; +} + +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, const char *fmt, ...) { va_list ap; - QDict *resp, *ret; + QDict *args, *err; - va_start(ap, command); - resp = qtest_vqmp(who, command, ap); + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); va_end(ap); - g_assert(!qdict_haskey(resp, "error")); - g_assert(qdict_haskey(resp, "return")); + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } - ret = qdict_get_qdict(resp, "return"); - qobject_ref(ret); - qobject_unref(resp); + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + qdict_put_obj(args, "channels", channels_obj); + } - return ret; + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(err, "desc")); + + qobject_unref(err); } /* @@ -112,7 +201,49 @@ QDict *qmp_command(QTestState *who, const char *command, ...) * Arguments are built from @fmt... (formatted like * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. */ -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args; + g_autofree char *connect_uri = NULL; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } else if (!channels) { + connect_uri = migrate_get_connect_uri(to); + qdict_put_str(args, "uri", connect_uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QObject *channels_obj = qobject_from_json(channels, &error_abort); + QList *channel_list = qobject_to(QList, channels_obj); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels_obj); + } + + 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; @@ -124,10 +255,21 @@ 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); + /* This function relies on the event to work, make sure it's enabled */ + 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); + + migration_event_wait(to, "setup"); } /* @@ -136,7 +278,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) @@ -234,8 +376,155 @@ void wait_for_migration_fail(QTestState *from, bool allow_active) } 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); +} + +#ifdef O_DIRECT +/* + * Probe for O_DIRECT support on the filesystem. Since this is used + * for tests, be conservative, if anything fails, assume it's + * unsupported. + */ +bool probe_o_direct_support(const char *tmpfs) +{ + g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); + int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; + void *buf; + ssize_t ret, len; + uint64_t offset; + + fd = open(filename, flags, 0660); + if (fd < 0) { + unlink(filename); + return false; + } + + /* + * Using 1MB alignment as conservative choice to satisfy any + * plausible architecture default page size, and/or filesystem + * alignment restrictions. + */ + len = 0x100000; + offset = 0x100000; + + buf = qemu_try_memalign(len, len); + g_assert(buf); + + ret = pwrite(fd, buf, len, offset); + unlink(filename); + g_free(buf); + + if (ret < 0) { + return false; + } + + return true; +} +#endif + +/* + * Wait for a "MIGRATION" event. This is what Libvirt uses to track + * migration status changes. + */ +void migration_event_wait(QTestState *s, const char *target) +{ + QDict *response, *data; + const char *status; + bool found; + + do { + response = qtest_qmp_eventwait_ref(s, "MIGRATION"); + data = qdict_get_qdict(response, "data"); + g_assert(data); + status = qdict_get_str(data, "status"); + found = (strcmp(status, target) == 0); + qobject_unref(response); + } while (!found); +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h index db0684de48..72dba369fb 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration-helpers.h @@ -15,20 +15,30 @@ #include "libqtest.h" -extern bool got_stop; +typedef struct QTestMigrationState { + bool stop_seen; + bool resume_seen; + bool suspend_seen; + bool suspend_me; +} QTestMigrationState; -#ifndef _WIN32 -G_GNUC_PRINTF(3, 4) -QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...); -#endif +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque); -G_GNUC_PRINTF(2, 3) -QDict *wait_command(QTestState *who, const char *command, ...); - -QDict *qmp_command(QTestState *who, const char *command, ...); +G_GNUC_PRINTF(5, 6) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + const char *channels, const char *fmt, ...); G_GNUC_PRINTF(3, 4) -void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...); +void migrate_incoming_qmp(QTestState *who, const char *uri, + const char *fmt, ...); + +G_GNUC_PRINTF(4, 5) +void migrate_qmp_fail(QTestState *who, const char *uri, + const char *channels, 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); @@ -40,4 +50,19 @@ 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); +#ifdef O_DIRECT +bool probe_o_direct_support(const char *tmpfs); +#else +static inline bool probe_o_direct_support(const char *tmpfs) +{ + return false; +} +#endif +void migration_test_add(const char *path, void (*fn)(void)); +void migration_event_wait(QTestState *s, const char *target); + #endif /* MIGRATION_HELPERS_H */ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index dbde726adf..74d3000198 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -13,18 +13,15 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/range.h" #include "qemu/sockets.h" #include "chardev/char.h" -#include "qapi/qapi-visit-sockets.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/qobject-output-visitor.h" #include "crypto/tlscredspsk.h" #include "qapi/qmp/qlist.h" +#include "ppc-util.h" #include "migration-helpers.h" #include "tests/migration/migration-test.h" @@ -40,12 +37,25 @@ #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; + +/* + * 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 @@ -53,6 +63,26 @@ static bool uffd_feature_thread_id; */ #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 FILE_TEST_MARKER 'X' +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" + +typedef enum PostcopyRecoveryFailStage { + /* + * "no failure" must be 0 as it's the default. OTOH, real failure + * cases must be >0 to make sure they trigger by a "if" test. + */ + POSTCOPY_FAIL_NONE = 0, + POSTCOPY_FAIL_CHANNEL_ESTABLISH, + POSTCOPY_FAIL_RECOVERY, + POSTCOPY_FAIL_MAX +} PostcopyRecoveryFailStage; + #if defined(__linux__) #include #include @@ -61,14 +91,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"); @@ -83,8 +113,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; @@ -103,16 +133,54 @@ static bool ufd_version_check(void) #endif 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. */ #include "tests/migration/i386/a-b-bootblock.h" #include "tests/migration/aarch64/a-b-kernel.h" +#include "tests/migration/ppc64/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_delete(void) { + if (!bootpath) { + return; + } + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + +static void bootfile_create(char *dir, bool suspend_me) +{ + const char *arch = qtest_get_arch(); + unsigned char *content; + size_t len; + + bootfile_delete(); + 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) { + content = ppc64_kernel; + len = sizeof(ppc64_kernel); + } 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); @@ -122,35 +190,16 @@ static void init_bootfile(const char *bootpath, void *content, size_t len) /* * 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) { g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); FILE *serialfile = fopen(serialpath, "r"); - const char *arch = qtest_get_arch(); - int started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; do { int readvalue = fgetc(serialfile); - if (!started) { - /* SLOF prints its banner before starting test, - * to ignore it, mark the start of the test with '_', - * ignore all characters until this marker - */ - switch (readvalue) { - case '_': - started = 1; - break; - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - } - continue; - } switch (readvalue) { case 'A': /* Fine */ @@ -162,8 +211,6 @@ static void wait_for_serial(const char *side) return; case EOF: - started = (strcmp(side, "src_serial") == 0 && - strcmp(arch, "ppc64") == 0) ? 0 : 1; fseek(serialfile, 0, SEEK_SET); usleep(1000); break; @@ -175,6 +222,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. @@ -222,21 +290,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) @@ -294,57 +360,14 @@ static void cleanup(const char *filename) unlink(path); } -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"); - } -} - -static char *migrate_get_socket_address(QTestState *who, const char *parameter) -{ - QDict *rsp; - char *result; - SocketAddressList *addrs; - Visitor *iv = NULL; - QObject *object; - - rsp = migrate_query(who); - object = qdict_get(rsp, parameter); - - iv = qobject_input_visitor_new(object); - visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); - visit_free(iv); - - /* we are only using a single address */ - result = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); - qobject_unref(rsp); - return result; -} - static long long migrate_get_parameter_int(QTestState *who, const char *parameter) { 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; @@ -362,14 +385,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); } @@ -379,7 +398,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; @@ -395,21 +415,49 @@ 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 long long migrate_get_parameter_bool(QTestState *who, + const char *parameter) +{ + QDict *rsp; + int result; + + 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 + 30 mbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 30 * 1000 * 1000); + /* 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); } @@ -420,71 +468,129 @@ static void migrate_ensure_converge(QTestState *who) 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) { - QDict *rsp; - - rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); - qobject_unref(rsp); + 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"); } @@ -501,6 +607,8 @@ 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; /* @@ -542,6 +650,13 @@ typedef struct { */ const char *connect_uri; + /* + * Optional: JSON-formatted list of src QEMU URIs. If a port is + * defined as '0' in any QDict key a value of '0' will be + * automatically converted to the correct destination port. + */ + const char *connect_channels; + /* Optional: callback to run at start to set migration parameters */ TestMigrateStartHook start_hook; /* Optional: callback to run at finish to cleanup */ @@ -569,14 +684,30 @@ typedef struct { 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 */ + /* + * 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; + PostcopyRecoveryFailStage postcopy_recovery_fail_stage; } MigrateCommon; static int test_migrate_start(QTestState **from, QTestState **to, @@ -584,15 +715,18 @@ static int test_migrate_start(QTestState **from, QTestState **to, { 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)) { @@ -601,47 +735,46 @@ 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 " - "'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_printf( + "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " + "-bios %s", bootpath); } 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(); } @@ -667,52 +800,79 @@ 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"; + } + + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return -1; + } + + 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); } + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(*from, "events", true); + migrate_set_capability(*to, "events", true); + return 0; } @@ -731,7 +891,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); @@ -744,10 +904,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 @@ -765,7 +925,6 @@ 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, @@ -781,24 +940,22 @@ test_migrate_tls_psk_start_common(QTestState *from, 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"); @@ -869,7 +1026,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); @@ -905,47 +1061,48 @@ test_migrate_tls_x509_start_common(QTestState *from, QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : QCRYPTO_TLS_TEST_CLIENT_NAME, data->clientcert); + test_tls_deinit_cert(&servercertreq); } TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, data->servercert, args->certhostname, args->certipaddr); + test_tls_deinit_cert(&clientcertreq); + test_tls_deinit_cert(&cacertreq); - 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"); } @@ -1096,10 +1253,9 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, QTestState **to_ptr, MigrateCommon *args) { - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, &args->start)) { + if (test_migrate_start(&from, &to, "defer", &args->start)) { return -1; } @@ -1118,12 +1274,22 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, 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); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, NULL, NULL, "{}"); - wait_for_migration_pass(from); + migrate_wait_for_dirty_mem(from, to); *from_ptr = from; *to_ptr = to; @@ -1136,6 +1302,11 @@ static void migrate_postcopy_complete(QTestState *from, QTestState *to, { 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"); @@ -1169,6 +1340,15 @@ static void test_postcopy(void) test_postcopy_common(&args); } +static void test_postcopy_suspend(void) +{ + MigrateCommon args = { + .start.suspend_me = true, + }; + + test_postcopy_common(&args); +} + static void test_postcopy_preempt(void) { MigrateCommon args = { @@ -1201,6 +1381,125 @@ static void test_postcopy_preempt_tls_psk(void) } #endif +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { + "failed", "active", + "completed", NULL + }); +} + +static void postcopy_recover_fail(QTestState *from, QTestState *to, + PostcopyRecoveryFailStage stage) +{ +#ifndef _WIN32 + bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); + int ret, pair1[2], pair2[2]; + char c; + + g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); + + /* 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); + + if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + /* + * This will make src QEMU to fail at an early stage when trying to + * resume later, where it shouldn't reach RECOVER stage at all. + */ + close(pair1[1]); + } + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); + + /* + * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. + * Make sure it appears along the way. + */ + migration_event_wait(from, "postcopy-recover-setup"); + + if (fail_early) { + /* + * When fails at reconnection, src QEMU will automatically goes + * back to PAUSED state. Making sure there is an event in this + * case: Libvirt relies on this to detect early reconnection + * errors. + */ + migration_event_wait(from, "postcopy-paused"); + } else { + /* + * We want to test "fail later" at RECOVER stage here. Make sure + * both QEMU instances will go into RECOVER stage first, then test + * kicking them out using migrate-pause. + * + * Explicitly check the RECOVER event on src, that's what Libvirt + * relies on, rather than polling. + */ + migration_event_wait(from, "postcopy-recover"); + wait_for_postcopy_status(from, "postcopy-recover"); + + /* Need an explicit kick on src QEMU in this case */ + migrate_pause(from); + } + + /* + * For all failure cases, we'll reach such states on both sides now. + * Check them. + */ + wait_for_postcopy_status(from, "postcopy-paused"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * Kick dest QEMU out too. This is normally not needed in reality + * because when the channel is shutdown it should also happen on src. + * However here we used separate socket pairs so we need to do that + * explicitly. + */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair2[0]); + close(pair2[1]); + + if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + close(pair1[1]); + } +#endif +} + static void test_postcopy_recovery_common(MigrateCommon *args) { QTestState *from, *to; @@ -1236,9 +1535,17 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * 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"); + + if (args->postcopy_recovery_fail_stage) { + /* + * 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, args->postcopy_recovery_fail_stage); + /* continue with a good recovery */ + } /* * Create a new socket to emulate a new channel that is different @@ -1252,10 +1559,7 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * 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}"); + migrate_qmp(from, to, uri, NULL, "{'resume': true}"); /* Restore the postcopy bandwidth to unlimited */ migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); @@ -1270,6 +1574,24 @@ static void test_postcopy_recovery(void) test_postcopy_recovery_common(&args); } +static void test_postcopy_recovery_fail_handshake(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_reconnect(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, + }; + + test_postcopy_recovery_common(&args); +} + #ifdef CONFIG_GNUTLS static void test_postcopy_recovery_tls_psk(void) { @@ -1303,6 +1625,7 @@ static void test_postcopy_preempt_all(void) test_postcopy_recovery_common(&args); } + #endif static void test_baddest(void) @@ -1315,11 +1638,66 @@ static void test_baddest(void) if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { return; } - migrate_qmp(from, "tcp:127.0.0.1:0", "{}"); + migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); wait_for_migration_fail(from, false); test_migrate_end(from, to, false); } +#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"); + + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } + + /* dummy url */ + if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + + /* + * Setting these two capabilities causes the "configuration" + * vmstate to include subsections for them. The script needs to + * parse those subsections properly. + */ + migrate_set_capability(from, "validate-uuid", true); + migrate_set_capability(from, "x-ignore-shared", true); + + file = g_strdup_printf("%s/migfile", tmpfs); + uri = g_strdup_printf("exec:cat > %s", file); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + wait_for_migration_complete(from); + + 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; @@ -1329,8 +1707,6 @@ static void test_precopy_common(MigrateCommon *args) return; } - migrate_ensure_non_converge(from); - if (args->start_hook) { data_hook = args->start_hook(from, to); } @@ -1338,16 +1714,32 @@ static void test_precopy_common(MigrateCommon *args) /* Wait for the first serial output from the source */ if (args->result == MIG_TEST_SUCCEED) { wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); } - if (!args->connect_uri) { - g_autofree char *local_connect_uri = - migrate_get_socket_address(to, "socket-address"); - migrate_qmp(from, local_connect_uri, "{}"); + if (args->live) { + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); } else { - migrate_qmp(from, args->connect_uri, "{}"); + /* + * 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->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; @@ -1357,29 +1749,156 @@ static void test_precopy_common(MigrateCommon *args) 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_ensure_converge(from); + wait_for_resume(to, &dst_state); - /* We do this first, as it has a timeout to stop us - * hanging forever if migration didn't converge */ - wait_for_migration_complete(from); - - 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"); } +finish: + if (args->finish_hook) { + args->finish_hook(from, to, data_hook); + } + + test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +static void file_dirty_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *data = g_new0(char, size); + + memset(data, FILE_TEST_MARKER, size); + g_assert(g_file_set_contents(path, data, size, NULL)); +} + +static void file_check_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *expected = g_new0(char, size); + g_autofree char *actual = NULL; + uint64_t *stream_start; + + /* + * Ensure the skipped offset region's data has not been touched + * and the migration stream starts at the right place. + */ + + memset(expected, FILE_TEST_MARKER, size); + + g_assert(g_file_get_contents(path, &actual, NULL, NULL)); + g_assert(!memcmp(actual, expected, size)); + + stream_start = (uint64_t *)(actual + size); + g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); +} + +static void test_file_common(MigrateCommon *args, bool stop_src) +{ + QTestState *from, *to; + void *data_hook = NULL; + bool check_offset = false; + + 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 (g_strrstr(args->connect_uri, "offset=")) { + check_offset = true; + /* + * This comes before the start_hook because it's equivalent to + * a management application creating the file and writing to + * it so hooks should expect the file to be already present. + */ + file_dirty_offset_region(); + } + + 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, args->connect_uri, NULL, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); + wait_for_migration_complete(from); + + /* + * We need to wait for the source to finish before starting the + * destination. + */ + migrate_incoming_qmp(to, args->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"); + + if (check_offset) { + file_check_offset_region(); + } + +finish: if (args->finish_hook) { args->finish_hook(from, to, data_hook); } @@ -1393,11 +1912,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) { @@ -1408,6 +1960,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); @@ -1471,19 +2028,20 @@ 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); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); - 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"); @@ -1515,15 +2073,316 @@ 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_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); +} + +#ifndef _WIN32 +static void fdset_add_fds(QTestState *qts, const char *file, int flags, + int num_fds, bool direct_io) +{ + for (int i = 0; i < num_fds; i++) { + int fd; + +#ifdef O_DIRECT + /* only secondary channels can use direct-io */ + if (direct_io && i != 0) { + flags |= O_DIRECT; + } +#endif + + fd = open(file, flags, 0660); + assert(fd != -1); + + qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " + "'arguments': {'fdset-id': 1}}"); + close(fd); + } +} + +static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 1, false); + fdset_add_fds(to, file, O_RDONLY, 1, false); + + return NULL; +} + +static void test_precopy_file_offset_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = file_offset_fdset_start_hook, + }; + + test_file_common(&args, false); +} +#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", + }; + + 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 *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to) +{ + migrate_multifd_mapped_ram_start(from, to); + + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_dio(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 = multifd_mapped_ram_dio_start, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, + void *opaque) +{ + QDict *resp; + QList *fdsets; + + /* + * Remove the fdsets after migration, otherwise a second migration + * would fail due fdset reuse. + */ + qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " + "'arguments': { 'fdset-id': 1}}"); + + /* + * Make sure no fdsets are left after migration, otherwise a + * second migration would fail due fdset reuse. + */ + resp = qtest_qmp(from, "{'execute': 'query-fdsets', " + "'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + fdsets = qdict_get_qlist(resp, "return"); + g_assert(fdsets && qlist_empty(fdsets)); + qobject_unref(resp); +} + +static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, true); + fdset_add_fds(to, file, O_RDONLY, 2, true); + + migrate_multifd_mapped_ram_start(from, to); + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, false); + fdset_add_fds(to, file, O_RDONLY, 2, false); + + migrate_multifd_mapped_ram_start(from, to); + + return NULL; +} + +static void test_multifd_file_mapped_ram_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = multifd_mapped_ram_fdset, + .finish_hook = multifd_mapped_ram_fdset_end, + }; + + test_file_common(&args, true); +} + +static void test_multifd_file_mapped_ram_fdset_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = multifd_mapped_ram_fdset_dio, + .finish_hook = multifd_mapped_ram_fdset_end, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} +#endif /* !_WIN32 */ + static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -1533,6 +2392,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) { @@ -1656,31 +2542,26 @@ static void test_precopy_tcp_tls_x509_reject_anon_client(void) 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; @@ -1696,22 +2577,24 @@ static void test_migrate_fd_finish_hook(QTestState *from, /* Test closing fds */ /* We assume, that QEMU removes named fd from its list, * so this should fail */ - rsp = qtest_qmp(from, "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); + rsp = qtest_qmp(from, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); g_assert_true(qdict_haskey(rsp, "error")); error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); qobject_unref(rsp); - rsp = qtest_qmp(to, "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); + rsp = qtest_qmp(to, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); g_assert_true(qdict_haskey(rsp, "error")); error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); qobject_unref(rsp); } -static void test_migrate_fd_proto(void) +static void test_migrate_precopy_fd_socket(void) { MigrateCommon args = { .listen_uri = "defer", @@ -1721,6 +2604,45 @@ 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) @@ -1743,7 +2665,7 @@ static void do_test_validate_uuid(MigrateStart *args, bool should_fail) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); if (should_fail) { qtest_set_expected_status(to, EXIT_FAILURE); @@ -1796,6 +2718,70 @@ static void test_validate_uuid_dst_not_set(void) do_test_validate_uuid(&args, false); } +static void do_test_validate_uri_channel(MigrateCommon *args) +{ + QTestState *from, *to; + + if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + /* + * 'uri' and 'channels' validation is checked even before the migration + * starts. + */ + migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + test_migrate_end(from, to, false); +} + +static void test_validate_uri_channels_both_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .connect_uri = "tcp:127.0.0.1:0", + .connect_channels = ("[ { ""'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + + do_test_validate_uri_channel(&args); +} + +static void test_validate_uri_channels_none_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + }; + + do_test_validate_uri_channel(&args); +} + +/* + * 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); @@ -1805,10 +2791,12 @@ static void test_migrate_auto_converge(void) /* * 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 init_pct = 5, inc_pct = 25, max_pct = 95; + uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; + int max_try_count, hit = 0; if (test_migrate_start(&from, &to, uri, &args)) { return; @@ -1831,17 +2819,50 @@ static void test_migrate_auto_converge(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate_qmp(from, uri, "{}"); + migrate_qmp(from, to, uri, NULL, "{}"); /* 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); + 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); + + /* + * End the loop when the dirty sync count greater than 1. + */ + while ((dirty_sync_cnt = get_migration_pass(from)) < 2) { + usleep(1000 * 1000); } - /* The first percentage of throttling should be equal to init_pct */ - g_assert_cmpint(percentage, ==, init_pct); + + prev_dirty_sync_cnt = dirty_sync_cnt; + + /* + * The RAMBlock dirty sync count must changes in 5 seconds, here we set + * the timeout to 10 seconds to ensure it changes. + * + * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s, + * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3) + * to complete; this ensures that the RAMBlock dirty sync occurs. + */ + max_try_count = 10; + while (--max_try_count) { + dirty_sync_cnt = get_migration_pass(from); + if (dirty_sync_cnt != prev_dirty_sync_cnt) { + hit = 1; + break; + } + prev_dirty_sync_cnt = dirty_sync_cnt; + sleep(1); + } + g_assert_cmpint(hit, ==, 1); + /* Now, when we tested that throttling works, let it converge */ migrate_ensure_converge(from); @@ -1869,8 +2890,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); @@ -1881,9 +2900,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; } @@ -1895,10 +2912,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"); } @@ -1907,15 +2949,98 @@ 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 */ -static void test_multifd_tcp_none(void) +#ifdef CONFIG_QATZIP +static void * +test_migrate_precopy_tcp_multifd_qatzip_start(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-qatzip-level", 2); + migrate_set_parameter_int(to, "multifd-qatzip-level", 2); + + return test_migrate_precopy_tcp_multifd_start_common(from, to, "qatzip"); +} +#endif + +#ifdef CONFIG_QPL +static void * +test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from, + QTestState *to) +{ + return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl"); +} +#endif /* CONFIG_QPL */ +#ifdef CONFIG_UADK +static void * +test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from, + QTestState *to) +{ + return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk"); +} +#endif /* CONFIG_UADK */ + +static void test_multifd_tcp_uri_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); +} + +static void test_multifd_tcp_channels_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_start, + .live = true, + .connect_channels = ("[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), }; test_precopy_common(&args); } @@ -1940,6 +3065,39 @@ static void test_multifd_tcp_zstd(void) } #endif +#ifdef CONFIG_QATZIP +static void test_multifd_tcp_qatzip(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_qatzip_start, + }; + test_precopy_common(&args); +} +#endif + +#ifdef CONFIG_QPL +static void test_multifd_tcp_qpl(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_qpl_start, + }; + test_precopy_common(&args); +} +#endif + +#ifdef CONFIG_UADK +static void test_multifd_tcp_uadk(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = test_migrate_precopy_tcp_multifd_uadk_start, + }; + test_precopy_common(&args); +} +#endif + #ifdef CONFIG_GNUTLS static void * test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, @@ -2114,14 +3272,13 @@ 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; } 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); @@ -2130,24 +3287,31 @@ 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"); - uri = migrate_get_socket_address(to, "socket-address"); + migrate_qmp(from, to, NULL, NULL, "{}"); - 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); + qtest_quit(to); + + /* + * Ensure the source QEMU finishes its cancellation process before we + * proceed with the setup of the next migration. The test_migrate_start() + * function and others might want to interact with the source in a way that + * is not possible while the migration is not canceled properly. For + * example, setting migration capabilities when the migration is still + * running leads to an error. + */ + wait_for_migration_status(from, "cancelled", NULL); args = (MigrateStart){ .only_target = true, @@ -2162,24 +3326,17 @@ 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"); + migrate_ensure_non_converge(from); - wait_for_migration_status(from, "cancelled", NULL); + migrate_qmp(from, to2, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to2); migrate_ensure_converge(from); - migrate_qmp(from, uri, "{}"); - - wait_for_migration_pass(from); - - if (!got_stop) { - qtest_qmp_eventwait(from, "STOP"); - } + wait_for_stop(from, &src_state); qtest_qmp_eventwait(to2, "RESUME"); wait_for_serial("dest_serial"); @@ -2189,32 +3346,33 @@ static void test_multifd_tcp_cancel(void) static void calc_dirty_rate(QTestState *who, uint64_t calc_time) { - qobject_unref(qmp_command(who, - "{ 'execute': 'calc-dirty-rate'," - "'arguments': { " - "'calc-time': %" PRIu64 "," - "'mode': 'dirty-ring' }}", - 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 qmp_command(who, "{ 'execute': 'query-dirty-rate' }"); + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); } static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) { - qobject_unref(qmp_command(who, - "{ 'execute': 'set-vcpu-dirty-limit'," - "'arguments': { " - "'dirty-rate': %" PRIu64 " } }", - dirtyrate)); + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); } static void cancel_vcpu_dirty_limit(QTestState *who) { - qobject_unref(qmp_command(who, - "{ 'execute': 'cancel-vcpu-dirty-limit' }")); + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); } static QDict *query_vcpu_dirty_limit(QTestState *who) @@ -2231,15 +3389,18 @@ static QDict *query_vcpu_dirty_limit(QTestState *who) static bool calc_dirtyrate_ready(QTestState *who) { QDict *rsp_return; - gchar *status; + const char *status; + bool ready; rsp_return = query_dirty_rate(who); g_assert(rsp_return); - status = g_strdup(qdict_get_str(rsp_return, "status")); + status = qdict_get_str(rsp_return, "status"); g_assert(status); + ready = g_strcmp0(status, "measuring"); + qobject_unref(rsp_return); - return g_strcmp0(status, "measuring"); + return ready; } static void wait_for_calc_dirtyrate_complete(QTestState *who, @@ -2262,7 +3423,7 @@ static void wait_for_calc_dirtyrate_complete(QTestState *who, static int64_t get_dirty_rate(QTestState *who) { QDict *rsp_return; - gchar *status; + const char *status; QList *rates; const QListEntry *entry; QDict *rate; @@ -2271,7 +3432,7 @@ static int64_t get_dirty_rate(QTestState *who) rsp_return = query_dirty_rate(who); g_assert(rsp_return); - status = g_strdup(qdict_get_str(rsp_return, "status")); + status = qdict_get_str(rsp_return, "status"); g_assert(status); g_assert_cmpstr(status, ==, "measured"); @@ -2320,14 +3481,8 @@ static QTestState *dirtylimit_start_vm(void) { QTestState *vm = NULL; g_autofree gchar *cmd = NULL; - const char *arch = qtest_get_arch(); - g_autofree char *bootpath = NULL; - - assert((strcmp(arch, "x86_64") == 0)); - bootpath = g_strdup_printf("%s/bootsect", tmpfs); - assert(sizeof(x86_bootsect) == 512); - init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect)); + bootfile_create(tmpfs, false); cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " "-name dirtylimit-test,debug-threads=on " "-m 150M -smp 1 " @@ -2342,7 +3497,6 @@ static QTestState *dirtylimit_start_vm(void) static void dirtylimit_stop_vm(QTestState *vm) { qtest_quit(vm); - cleanup("bootsect"); cleanup("vm_serial"); } @@ -2434,6 +3588,169 @@ static void test_vcpu_dirty_limit(void) 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, to, args.connect_uri, NULL, "{}"); + + /* 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, to, args.connect_uri, NULL, "{}"); + + /* 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) @@ -2459,34 +3776,39 @@ static bool kvm_dirty_ring_supported(void) int main(int argc, char **argv) { - const bool has_kvm = qtest_has_accel("kvm"); - const bool has_uffd = ufd_version_check(); - const char *arch = qtest_get_arch(); + 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); /* - * 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) + * The default QTEST_QEMU_BINARY must always be provided because + * that is what helpers use to query the accel type and + * architecture. */ - 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(); + if (qemu_src && qemu_dst) { + g_test_message("Only one of %s, %s is allowed", + QEMU_ENV_SRC, QEMU_ENV_DST); + exit(1); } - /* - * Similar to ppc64, s390x seems to be touchy with TCG, so disable it - * there until the problems are resolved - */ - if (g_str_equal(arch, "s390x") && !has_kvm) { - g_test_message("Skipping test: s390x host with KVM is required"); - return g_test_run(); + 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"); + tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); if (!tmpfs) { g_test_message("Can't create temporary directory in %s: %s", @@ -2496,21 +3818,84 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); - if (has_uffd) { - qtest_add_func("/migration/postcopy/plain", test_postcopy); - qtest_add_func("/migration/postcopy/recovery/plain", - test_postcopy_recovery); - qtest_add_func("/migration/postcopy/preempt/plain", test_postcopy_preempt); - qtest_add_func("/migration/postcopy/preempt/recovery/plain", - test_postcopy_preempt_recovery); + migration_test_add("/migration/bad_dest", test_baddest); +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif + + 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); } - 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 (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); + migration_test_add("/migration/postcopy/recovery/double-failures/handshake", + test_postcopy_recovery_fail_handshake); + migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", + test_postcopy_recovery_fail_reconnect); + if (is_x86) { + migration_test_add("/migration/postcopy/suspend", + test_postcopy_suspend); + } + } + + migration_test_add("/migration/precopy/unix/plain", + test_precopy_unix_plain); + if (g_test_slow()) { + migration_test_add("/migration/precopy/unix/xbzrle", + test_precopy_unix_xbzrle); + } + migration_test_add("/migration/precopy/file", + test_precopy_file); + migration_test_add("/migration/precopy/file/offset", + test_precopy_file_offset); +#ifndef _WIN32 + migration_test_add("/migration/precopy/file/offset/fdset", + test_precopy_file_offset_fdset); +#endif + 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); + + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); + +#ifndef _WIN32 + migration_test_add("/migration/multifd/file/mapped-ram/fdset", + test_multifd_file_mapped_ram_fdset); + migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", + test_multifd_file_mapped_ram_fdset_dio); +#endif + #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) { /* @@ -2518,98 +3903,142 @@ int main(int argc, char **argv) * channels are tested under precopy. Here what we want to test is the * general postcopy path that has TLS channel enabled. */ - qtest_add_func("/migration/postcopy/tls/psk", test_postcopy_tls_psk); - qtest_add_func("/migration/postcopy/recovery/tls/psk", - test_postcopy_recovery_tls_psk); - qtest_add_func("/migration/postcopy/preempt/tls/psk", - test_postcopy_preempt_tls_psk); - qtest_add_func("/migration/postcopy/preempt/recovery/tls/psk", - test_postcopy_preempt_all); + 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); */ + /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ #ifndef _WIN32 - qtest_add_func("/migration/fd_proto", test_migrate_fd_proto); + 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 - 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/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); + migration_test_add("/migration/validate_uri/channels/both_set", + test_validate_uri_channels_both_set); + migration_test_add("/migration/validate_uri/channels/none_set", + test_validate_uri_channels_none_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/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/channels/plain/none", + test_multifd_tcp_channels_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_QATZIP + migration_test_add("/migration/multifd/tcp/plain/qatzip", + test_multifd_tcp_qatzip); +#endif +#ifdef CONFIG_QPL + migration_test_add("/migration/multifd/tcp/plain/qpl", + test_multifd_tcp_qpl); +#endif +#ifdef CONFIG_UADK + migration_test_add("/migration/multifd/tcp/plain/uadk", + test_multifd_tcp_uadk); #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 (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { - qtest_add_func("/migration/dirty_ring", - test_precopy_unix_dirty_ring); - qtest_add_func("/migration/vcpu_dirty_limit", - test_vcpu_dirty_limit); + migration_test_add("/migration/dirty_ring", + test_precopy_unix_dirty_ring); + if (qtest_has_machine("pc") && g_test_slow()) { + 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", diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c new file mode 100644 index 0000000000..317af03817 --- /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-ms=1000", 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 8048044d28..e751a72e36 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -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 b046f1d76a..2e1a1a6d70 100644 --- a/tests/qtest/npcm7xx_emc-test.c +++ b/tests/qtest/npcm7xx_emc-test.c @@ -225,21 +225,14 @@ 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; @@ -796,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); @@ -821,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); 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 5d68540e52..01f237a816 100644 --- a/tests/qtest/npcm7xx_sdhci-test.c +++ b/tests/qtest/npcm7xx_sdhci-test.c @@ -30,6 +30,8 @@ char *sd_path; static QTestState *setup_sd_card(void) { + uint16_t rca; + QTestState *qts = qtest_initf( "-machine kudo-bmc " "-device sd-card,drive=drive0 " @@ -43,8 +45,10 @@ static QTestState *setup_sd_card(void) sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_APP_CMD); sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x41200000, 0, (41 << 8)); sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_ALL_SEND_CID); - sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR); - sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0x45670000, 0, + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, 0, 0, SDHC_SEND_RELATIVE_ADDR + | SDHC_CMD_RESPONSE); + rca = qtest_readl(qts, NPCM7XX_MMC_BA + SDHC_RSPREG0) >> 16; + sdhci_cmd_regs(qts, NPCM7XX_MMC_BA, 0, 0, rca << 16, 0, SDHC_SELECT_DESELECT_CARD); return qts; 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..6d92baee86 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -125,7 +125,8 @@ static void pc_numa_cpu(const void *data) QTestState *qts; g_autofree char *cli = NULL; - cli = make_cli(data, "-cpu pentium -machine smp.cpus=8,smp.sockets=2,smp.cores=2,smp.threads=2 " + cli = make_cli(data, + "-cpu max -machine smp.cpus=8,smp.sockets=2,smp.cores=2,smp.threads=2 " "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " "-numa cpu,node-id=1,socket-id=0 " "-numa cpu,node-id=0,socket-id=1,core-id=0 " @@ -161,7 +162,7 @@ static void pc_numa_cpu(const void *data) } else if (socket == 1 && core == 1 && thread == 1) { g_assert_cmpint(node, ==, 1); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -206,7 +207,7 @@ static void spapr_numa_cpu(const void *data) } else if (core == 3) { g_assert_cmpint(node, ==, 1); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -256,7 +257,55 @@ static void aarch64_numa_cpu(const void *data) } else if (socket == 1 && cluster == 0 && core == 0 && thread == 0) { g_assert_cmpint(node, ==, 0); } else { - g_assert(false); + g_assert_not_reached(); + } + qobject_unref(e); + } + + qobject_unref(resp); + qtest_quit(qts); +} + +static void loongarch64_numa_cpu(const void *data) +{ + QDict *resp; + QList *cpus; + QObject *e; + QTestState *qts; + g_autofree char *cli = NULL; + + cli = make_cli(data, "-machine " + "smp.cpus=2,smp.sockets=2,smp.cores=1,smp.threads=1 " + "-numa node,nodeid=0,memdev=ram -numa node,nodeid=1 " + "-numa cpu,node-id=0,socket-id=1,core-id=0,thread-id=0 " + "-numa cpu,node-id=1,socket-id=0,core-id=0,thread-id=0"); + qts = qtest_init(cli); + cpus = get_cpus(qts, &resp); + g_assert(cpus); + + while ((e = qlist_pop(cpus))) { + QDict *cpu, *props; + int64_t socket, core, thread, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + g_assert(qdict_haskey(props, "core-id")); + core = qdict_get_int(props, "core-id"); + g_assert(qdict_haskey(props, "thread-id")); + thread = qdict_get_int(props, "thread-id"); + + if (socket == 0 && core == 0 && thread == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1 && core == 0 && thread == 0) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert_not_reached(); } qobject_unref(e); } @@ -318,7 +367,7 @@ static void pc_dynamic_cpu_cfg(const void *data) } else if (socket == 1) { g_assert_cmpint(node, ==, 0); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -558,6 +607,9 @@ int main(int argc, char **argv) } if (g_str_equal(arch, "aarch64")) { + if (!qtest_has_machine("virt")) { + goto out; + } g_string_append(args, " -machine virt"); } @@ -568,7 +620,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 +628,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); } @@ -585,5 +642,11 @@ int main(int argc, char **argv) aarch64_numa_cpu); } + if (!strcmp(arch, "loongarch64")) { + qtest_add_data_func("/numa/loongarch64/cpu/explicit", args, + loongarch64_numa_cpu); + } + +out: return g_test_run(); } diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c index 008d189b0f..5ad6821f7a 100644 --- a/tests/qtest/nvme-test.c +++ b/tests/qtest/nvme-test.c @@ -13,7 +13,7 @@ #include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" -#include "include/block/nvme.h" +#include "block/nvme.h" typedef struct QNvme QNvme; 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 0b52c2ca5c..8c073efcb4 100644 --- a/tests/qtest/pflash-cfi02-test.c +++ b/tests/qtest/pflash-cfi02-test.c @@ -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)); } } 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-spi-seeprom-test.c b/tests/qtest/pnv-spi-seeprom-test.c new file mode 100644 index 0000000000..57f20af76e --- /dev/null +++ b/tests/qtest/pnv-spi-seeprom-test.c @@ -0,0 +1,110 @@ +/* + * QTest testcase for PowerNV 10 Seeprom Communications + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bswap.h" +#include "hw/ssi/pnv_spi_regs.h" +#include "pnv-xscom.h" + +#define FLASH_SIZE (512 * 1024) +#define SPIC2_XSCOM_BASE 0xc0040 + +/* To transmit READ opcode and address */ +#define READ_OP_TDR_DATA 0x0300010000000000 +/* + * N1 shift - tx 4 bytes (transmit opcode and address) + * N2 shift - tx and rx 8 bytes. + */ +#define READ_OP_COUNTER_CONFIG 0x2040000000002b00 +/* SEQ_OP_SELECT_RESPONDER - N1 Shift - N2 Shift * 5 - SEQ_OP_STOP */ +#define READ_OP_SEQUENCER 0x1130404040404010 + +/* To transmit WREN(Set Write Enable Latch in status0 register) opcode */ +#define WRITE_OP_WREN 0x0600000000000000 +/* To transmit WRITE opcode, address and data */ +#define WRITE_OP_TDR_DATA 0x0300010012345678 +/* N1 shift - tx 8 bytes (transmit opcode, address and data) */ +#define WRITE_OP_COUNTER_CONFIG 0x4000000000002000 +/* SEQ_OP_SELECT_RESPONDER - N1 Shift - SEQ_OP_STOP */ +#define WRITE_OP_SEQUENCER 0x1130100000000000 + +static void pnv_spi_xscom_write(QTestState *qts, const PnvChip *chip, + uint32_t reg, uint64_t val) +{ + uint32_t pcba = SPIC2_XSCOM_BASE + reg; + qtest_writeq(qts, pnv_xscom_addr(chip, pcba), val); +} + +static uint64_t pnv_spi_xscom_read(QTestState *qts, const PnvChip *chip, + uint32_t reg) +{ + uint32_t pcba = SPIC2_XSCOM_BASE + reg; + return qtest_readq(qts, pnv_xscom_addr(chip, pcba)); +} + +static void spi_seeprom_transaction(QTestState *qts, const PnvChip *chip) +{ + /* SPI transactions to SEEPROM to read from SEEPROM image */ + pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, READ_OP_COUNTER_CONFIG); + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, READ_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, READ_OP_TDR_DATA); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, 0); + /* Read 5*8 bytes from SEEPROM at 0x100 */ + uint64_t rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + g_test_message("RDR READ = 0x%" PRIx64, rdr_val); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + rdr_val = pnv_spi_xscom_read(qts, chip, SPI_RCV_DATA_REG); + g_test_message("RDR READ = 0x%" PRIx64, rdr_val); + + /* SPI transactions to SEEPROM to write to SEEPROM image */ + pnv_spi_xscom_write(qts, chip, SPI_CTR_CFG_REG, WRITE_OP_COUNTER_CONFIG); + /* Set Write Enable Latch bit of status0 register */ + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_WREN); + /* write 8 bytes to SEEPROM at 0x100 */ + pnv_spi_xscom_write(qts, chip, SPI_SEQ_OP_REG, WRITE_OP_SEQUENCER); + pnv_spi_xscom_write(qts, chip, SPI_XMIT_DATA_REG, WRITE_OP_TDR_DATA); +} + +static void test_spi_seeprom(const void *data) +{ + const PnvChip *chip = data; + QTestState *qts = NULL; + g_autofree char *tmp_path = NULL; + int ret; + int fd; + + /* Create a temporary raw image */ + fd = g_file_open_tmp("qtest-seeprom-XXXXXX", &tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, FLASH_SIZE); + g_assert(ret == 0); + close(fd); + + qts = qtest_initf("-machine powernv10 -smp 2,cores=2," + "threads=1 -accel tcg,thread=single -nographic " + "-blockdev node-name=pib_spic2,driver=file," + "filename=%s -device 25csm04,bus=pnv-spi-bus.2,cs=0," + "drive=pib_spic2", tmp_path); + spi_seeprom_transaction(qts, chip); + qtest_quit(qts); + unlink(tmp_path); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + char *tname = g_strdup_printf("pnv-xscom/spi-seeprom/%s", + pnv_chips[3].cpu_model); + qtest_add_data_func(tname, &pnv_chips[3], test_spi_seeprom); + g_free(tname); + return g_test_run(); +} diff --git a/tests/qtest/pnv-xive2-common.c b/tests/qtest/pnv-xive2-common.c new file mode 100644 index 0000000000..bf2bce0056 --- /dev/null +++ b/tests/qtest/pnv-xive2-common.c @@ -0,0 +1,190 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Common functions for XIVE2 tests + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" + + +static uint64_t pnv_xscom_addr(uint32_t pcba) +{ + return P10_XSCOM_BASE | ((uint64_t) pcba << 3); +} + +static uint64_t pnv_xive_xscom_addr(uint32_t reg) +{ + return pnv_xscom_addr(XIVE_XSCOM + reg); +} + +uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg) +{ + return qtest_readq(qts, pnv_xive_xscom_addr(reg)); +} + +void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val) +{ + qtest_writeq(qts, pnv_xive_xscom_addr(reg), val); +} + +static void xive_get_struct(QTestState *qts, uint64_t src, void *dest, + size_t size) +{ + uint8_t *destination = (uint8_t *)dest; + size_t i; + + for (i = 0; i < size; i++) { + *(destination + i) = qtest_readb(qts, src + i); + } +} + +static void xive_copy_struct(QTestState *qts, void *src, uint64_t dest, + size_t size) +{ + uint8_t *source = (uint8_t *)src; + size_t i; + + for (i = 0; i < size; i++) { + qtest_writeb(qts, dest + i, *(source + i)); + } +} + +uint64_t xive_get_queue_addr(uint32_t end_index) +{ + return XIVE_QUEUE_MEM + (uint64_t)end_index * XIVE_QUEUE_SIZE; +} + +uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset) +{ + uint64_t addr; + + addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1)); + if (page == 1) { + addr += 1 << XIVE_PAGE_SHIFT; + } + return qtest_readb(qts, addr + offset); +} + +void set_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset, uint32_t val) +{ + uint64_t addr; + + addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1)); + if (page == 1) { + addr += 1 << XIVE_PAGE_SHIFT; + } + return qtest_writel(qts, addr + offset, cpu_to_be32(val)); +} + +void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp) +{ + uint64_t addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp); + xive_get_struct(qts, addr, nvp, sizeof(Xive2Nvp)); +} + +void set_nvp(QTestState *qts, uint32_t index, uint8_t first) +{ + uint64_t nvp_addr; + Xive2Nvp nvp; + uint64_t report_addr; + + nvp_addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp); + report_addr = (XIVE_REPORT_MEM + (uint64_t)index * XIVE_REPORT_SIZE) >> 8; + + memset(&nvp, 0, sizeof(nvp)); + nvp.w0 = xive_set_field32(NVP2_W0_VALID, 0, 1); + nvp.w0 = xive_set_field32(NVP2_W0_PGOFIRST, nvp.w0, first); + nvp.w6 = xive_set_field32(NVP2_W6_REPORTING_LINE, nvp.w6, + (report_addr >> 24) & 0xfffffff); + nvp.w7 = xive_set_field32(NVP2_W7_REPORTING_LINE, nvp.w7, + report_addr & 0xffffff); + xive_copy_struct(qts, &nvp, nvp_addr, sizeof(nvp)); +} + +static uint64_t get_cl_pair_addr(Xive2Nvp *nvp) +{ + uint64_t upper = xive_get_field32(0x0fffffff, nvp->w6); + uint64_t lower = xive_get_field32(0xffffff00, nvp->w7); + return (upper << 32) | (lower << 8); +} + +void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair) +{ + uint64_t addr = get_cl_pair_addr(nvp); + xive_get_struct(qts, addr, cl_pair, XIVE_REPORT_SIZE); +} + +void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair) +{ + uint64_t addr = get_cl_pair_addr(nvp); + xive_copy_struct(qts, cl_pair, addr, XIVE_REPORT_SIZE); +} + +void set_nvg(QTestState *qts, uint32_t index, uint8_t next) +{ + uint64_t nvg_addr; + Xive2Nvgc nvg; + + nvg_addr = XIVE_NVG_MEM + (uint64_t)index * sizeof(Xive2Nvgc); + + memset(&nvg, 0, sizeof(nvg)); + nvg.w0 = xive_set_field32(NVGC2_W0_VALID, 0, 1); + nvg.w0 = xive_set_field32(NVGC2_W0_PGONEXT, nvg.w0, next); + xive_copy_struct(qts, &nvg, nvg_addr, sizeof(nvg)); +} + +void set_eas(QTestState *qts, uint32_t index, uint32_t end_index, + uint32_t data) +{ + uint64_t eas_addr; + Xive2Eas eas; + + eas_addr = XIVE_EAS_MEM + (uint64_t)index * sizeof(Xive2Eas); + + memset(&eas, 0, sizeof(eas)); + eas.w = xive_set_field64(EAS2_VALID, 0, 1); + eas.w = xive_set_field64(EAS2_END_INDEX, eas.w, end_index); + eas.w = xive_set_field64(EAS2_END_DATA, eas.w, data); + xive_copy_struct(qts, &eas, eas_addr, sizeof(eas)); +} + +void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, + uint8_t priority, bool i) +{ + uint64_t end_addr, queue_addr, queue_hi, queue_lo; + uint8_t queue_size; + Xive2End end; + + end_addr = XIVE_END_MEM + (uint64_t)index * sizeof(Xive2End); + queue_addr = xive_get_queue_addr(index); + queue_hi = (queue_addr >> 32) & END2_W2_EQ_ADDR_HI; + queue_lo = queue_addr & END2_W3_EQ_ADDR_LO; + queue_size = ctz16(XIVE_QUEUE_SIZE) - 12; + + memset(&end, 0, sizeof(end)); + end.w0 = xive_set_field32(END2_W0_VALID, 0, 1); + end.w0 = xive_set_field32(END2_W0_ENQUEUE, end.w0, 1); + end.w0 = xive_set_field32(END2_W0_UCOND_NOTIFY, end.w0, 1); + end.w0 = xive_set_field32(END2_W0_BACKLOG, end.w0, 1); + + end.w1 = xive_set_field32(END2_W1_GENERATION, 0, 1); + + end.w2 = cpu_to_be32(queue_hi); + + end.w3 = cpu_to_be32(queue_lo); + end.w3 = xive_set_field32(END2_W3_QSIZE, end.w3, queue_size); + + end.w6 = xive_set_field32(END2_W6_IGNORE, 0, i); + end.w6 = xive_set_field32(END2_W6_VP_OFFSET, end.w6, nvp_index); + + end.w7 = xive_set_field32(END2_W7_F0_PRIORITY, 0, priority); + xive_copy_struct(qts, &end, end_addr, sizeof(end)); +} + diff --git a/tests/qtest/pnv-xive2-common.h b/tests/qtest/pnv-xive2-common.h new file mode 100644 index 0000000000..9ae34771aa --- /dev/null +++ b/tests/qtest/pnv-xive2-common.h @@ -0,0 +1,111 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TEST_PNV_XIVE2_COMMON_H +#define TEST_PNV_XIVE2_COMMON_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)) +#include "qemu/bswap.h" +#include "hw/intc/pnv_xive2_regs.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" + +/* + * sizing: + * 128 interrupts + * => ESB BAR range: 16M + * 256 ENDs + * => END BAR range: 16M + * 256 VPs + * => NVPG,NVC BAR range: 32M + */ +#define MAX_IRQS 128 +#define MAX_ENDS 256 +#define MAX_VPS 256 + +#define XIVE_PAGE_SHIFT 16 + +#define XIVE_TRIGGER_PAGE 0 +#define XIVE_EOI_PAGE 1 + +#define XIVE_IC_ADDR 0x0006030200000000ull +#define XIVE_IC_TM_INDIRECT (XIVE_IC_ADDR + (256 << XIVE_PAGE_SHIFT)) +#define XIVE_IC_BAR ((0x3ull << 62) | XIVE_IC_ADDR) +#define XIVE_TM_BAR 0xc006030203180000ull +#define XIVE_ESB_ADDR 0x0006050000000000ull +#define XIVE_ESB_BAR ((0x3ull << 62) | XIVE_ESB_ADDR) +#define XIVE_END_BAR 0xc006060000000000ull +#define XIVE_NVPG_ADDR 0x0006040000000000ull +#define XIVE_NVPG_BAR ((0x3ull << 62) | XIVE_NVPG_ADDR) +#define XIVE_NVC_ADDR 0x0006030208000000ull +#define XIVE_NVC_BAR ((0x3ull << 62) | XIVE_NVC_ADDR) + +/* + * Memory layout + * A check is done when a table is configured to ensure that the max + * size of the resource fits in the table. + */ +#define XIVE_VST_SIZE 0x10000ull /* must be at least 4k */ + +#define XIVE_MEM_START 0x10000000ull +#define XIVE_ESB_MEM XIVE_MEM_START +#define XIVE_EAS_MEM (XIVE_ESB_MEM + XIVE_VST_SIZE) +#define XIVE_END_MEM (XIVE_EAS_MEM + XIVE_VST_SIZE) +#define XIVE_NVP_MEM (XIVE_END_MEM + XIVE_VST_SIZE) +#define XIVE_NVG_MEM (XIVE_NVP_MEM + XIVE_VST_SIZE) +#define XIVE_NVC_MEM (XIVE_NVG_MEM + XIVE_VST_SIZE) +#define XIVE_SYNC_MEM (XIVE_NVC_MEM + XIVE_VST_SIZE) +#define XIVE_QUEUE_MEM (XIVE_SYNC_MEM + XIVE_VST_SIZE) +#define XIVE_QUEUE_SIZE 4096 /* per End */ +#define XIVE_REPORT_MEM (XIVE_QUEUE_MEM + XIVE_QUEUE_SIZE * MAX_VPS) +#define XIVE_REPORT_SIZE 256 /* two cache lines per NVP */ +#define XIVE_MEM_END (XIVE_REPORT_MEM + XIVE_REPORT_SIZE * MAX_VPS) + +#define P10_XSCOM_BASE 0x000603fc00000000ull +#define XIVE_XSCOM 0x2010800ull + +#define XIVE_ESB_RESET 0b00 +#define XIVE_ESB_OFF 0b01 +#define XIVE_ESB_PENDING 0b10 +#define XIVE_ESB_QUEUED 0b11 + +#define XIVE_ESB_GET 0x800 +#define XIVE_ESB_SET_PQ_00 0xc00 /* Load */ +#define XIVE_ESB_SET_PQ_01 0xd00 /* Load */ +#define XIVE_ESB_SET_PQ_10 0xe00 /* Load */ +#define XIVE_ESB_SET_PQ_11 0xf00 /* Load */ + +#define XIVE_ESB_STORE_EOI 0x400 /* Store */ + + +extern uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg); +extern void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val); +extern uint64_t xive_get_queue_addr(uint32_t end_index); +extern uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset); +extern void set_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset, uint32_t val); +extern void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp); +extern void set_nvp(QTestState *qts, uint32_t index, uint8_t first); +extern void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair); +extern void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair); +extern void set_nvg(QTestState *qts, uint32_t index, uint8_t next); +extern void set_eas(QTestState *qts, uint32_t index, uint32_t end_index, + uint32_t data); +extern void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, + uint8_t priority, bool i); + + +void test_flush_sync_inject(QTestState *qts); + +#endif /* TEST_PNV_XIVE2_COMMON_H */ diff --git a/tests/qtest/pnv-xive2-flush-sync.c b/tests/qtest/pnv-xive2-flush-sync.c new file mode 100644 index 0000000000..3b32446adb --- /dev/null +++ b/tests/qtest/pnv-xive2-flush-sync.c @@ -0,0 +1,205 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test cache flush/queue sync injection + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" +#include "hw/intc/pnv_xive2_regs.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" + +#define PNV_XIVE2_QUEUE_IPI 0x00 +#define PNV_XIVE2_QUEUE_HW 0x01 +#define PNV_XIVE2_QUEUE_NXC 0x02 +#define PNV_XIVE2_QUEUE_INT 0x03 +#define PNV_XIVE2_QUEUE_OS 0x04 +#define PNV_XIVE2_QUEUE_POOL 0x05 +#define PNV_XIVE2_QUEUE_HARD 0x06 +#define PNV_XIVE2_CACHE_ENDC 0x08 +#define PNV_XIVE2_CACHE_ESBC 0x09 +#define PNV_XIVE2_CACHE_EASC 0x0a +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10 +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15 +#define PNV_XIVE2_CACHE_NXC 0x18 + +#define PNV_XIVE2_SYNC_IPI 0x000 +#define PNV_XIVE2_SYNC_HW 0x080 +#define PNV_XIVE2_SYNC_NxC 0x100 +#define PNV_XIVE2_SYNC_INT 0x180 +#define PNV_XIVE2_SYNC_OS_ESC 0x200 +#define PNV_XIVE2_SYNC_POOL_ESC 0x280 +#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80 + + +static uint64_t get_sync_addr(uint32_t src_pir, int ic_topo_id, int type) +{ + int thread_nr = src_pir & 0x7f; + uint64_t addr = XIVE_SYNC_MEM + thread_nr * 512 + ic_topo_id * 32 + type; + return addr; +} + +static uint8_t get_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id, + int type) +{ + uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type); + return qtest_readb(qts, addr); +} + +static void clr_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id, + int type) +{ + uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type); + qtest_writeb(qts, addr, 0x0); +} + +static void inject_cache_flush(QTestState *qts, int ic_topo_id, + uint64_t scom_addr) +{ + (void)ic_topo_id; + pnv_xive_xscom_write(qts, scom_addr, 0); +} + +static void inject_queue_sync(QTestState *qts, int ic_topo_id, uint64_t offset) +{ + (void)ic_topo_id; + uint64_t addr = XIVE_IC_ADDR + (VST_SYNC << XIVE_PAGE_SHIFT) + offset; + qtest_writeq(qts, addr, 0); +} + +static void inject_op(QTestState *qts, int ic_topo_id, int type) +{ + switch (type) { + case PNV_XIVE2_QUEUE_IPI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_IPI); + break; + case PNV_XIVE2_QUEUE_HW: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HW); + break; + case PNV_XIVE2_QUEUE_NXC: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NxC); + break; + case PNV_XIVE2_QUEUE_INT: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_INT); + break; + case PNV_XIVE2_QUEUE_OS: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_OS_ESC); + break; + case PNV_XIVE2_QUEUE_POOL: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_POOL_ESC); + break; + case PNV_XIVE2_QUEUE_HARD: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HARD_ESC); + break; + case PNV_XIVE2_CACHE_ENDC: + inject_cache_flush(qts, ic_topo_id, X_VC_ENDC_FLUSH_INJECT); + break; + case PNV_XIVE2_CACHE_ESBC: + inject_cache_flush(qts, ic_topo_id, X_VC_ESBC_FLUSH_INJECT); + break; + case PNV_XIVE2_CACHE_EASC: + inject_cache_flush(qts, ic_topo_id, X_VC_EASC_FLUSH_INJECT); + break; + case PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_NCO); + break; + case PNV_XIVE2_QUEUE_NXC_LD_LCL_CO: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_CO); + break; + case PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_NCI); + break; + case PNV_XIVE2_QUEUE_NXC_ST_LCL_CI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_CI); + break; + case PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_NCI); + break; + case PNV_XIVE2_QUEUE_NXC_ST_RMT_CI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_CI); + break; + case PNV_XIVE2_CACHE_NXC: + inject_cache_flush(qts, ic_topo_id, X_PC_NXC_FLUSH_INJECT); + break; + default: + g_assert_not_reached(); + break; + } +} + +const uint8_t xive_inject_tests[] = { + PNV_XIVE2_QUEUE_IPI, + PNV_XIVE2_QUEUE_HW, + PNV_XIVE2_QUEUE_NXC, + PNV_XIVE2_QUEUE_INT, + PNV_XIVE2_QUEUE_OS, + PNV_XIVE2_QUEUE_POOL, + PNV_XIVE2_QUEUE_HARD, + PNV_XIVE2_CACHE_ENDC, + PNV_XIVE2_CACHE_ESBC, + PNV_XIVE2_CACHE_EASC, + PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO, + PNV_XIVE2_QUEUE_NXC_LD_LCL_CO, + PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI, + PNV_XIVE2_QUEUE_NXC_ST_LCL_CI, + PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI, + PNV_XIVE2_QUEUE_NXC_ST_RMT_CI, + PNV_XIVE2_CACHE_NXC, +}; + +void test_flush_sync_inject(QTestState *qts) +{ + int ic_topo_id = 0; + + /* + * Writes performed by qtest are not done in the context of a thread. + * This means that QEMU XIVE code doesn't have a way to determine what + * thread is originating the write. In order to allow for some testing, + * QEMU XIVE code will assume a PIR of 0 when unable to determine the + * source thread for cache flush and queue sync inject operations. + * See hw/intc/pnv_xive2.c: pnv_xive2_inject_notify() for details. + */ + int src_pir = 0; + int test_nr; + uint8_t byte; + + printf("# ============================================================\n"); + printf("# Starting cache flush/queue sync injection tests...\n"); + + for (test_nr = 0; test_nr < sizeof(xive_inject_tests); + test_nr++) { + int op_type = xive_inject_tests[test_nr]; + + printf("# Running test %d\n", test_nr); + + /* start with status byte set to 0 */ + clr_sync(qts, src_pir, ic_topo_id, op_type); + byte = get_sync(qts, src_pir, ic_topo_id, op_type); + g_assert_cmphex(byte, ==, 0); + + /* request cache flush or queue sync operation */ + inject_op(qts, ic_topo_id, op_type); + + /* verify that status byte was written to 0xff */ + byte = get_sync(qts, src_pir, ic_topo_id, op_type); + g_assert_cmphex(byte, ==, 0xff); + + clr_sync(qts, src_pir, ic_topo_id, op_type); + } +} + diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c new file mode 100644 index 0000000000..dd19e88861 --- /dev/null +++ b/tests/qtest/pnv-xive2-test.c @@ -0,0 +1,344 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test irq to hardware thread + * - Test 'Pull Thread Context to Odd Thread Reporting Line' + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" +#include "hw/intc/pnv_xive2_regs.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" + +#define SMT 4 /* some tests will break if less than 4 */ + + +static void set_table(QTestState *qts, uint64_t type, uint64_t addr) +{ + uint64_t vsd, size, log_size; + + /* + * First, let's make sure that all the resources used fit in the + * given table. + */ + switch (type) { + case VST_ESB: + size = MAX_IRQS / 4; + break; + case VST_EAS: + size = MAX_IRQS * 8; + break; + case VST_END: + size = MAX_ENDS * 32; + break; + case VST_NVP: + case VST_NVG: + case VST_NVC: + size = MAX_VPS * 32; + break; + case VST_SYNC: + size = 64 * 1024; + break; + default: + g_assert_not_reached(); + } + + g_assert_cmpuint(size, <=, XIVE_VST_SIZE); + log_size = ctzl(XIVE_VST_SIZE) - 12; + + vsd = ((uint64_t) VSD_MODE_EXCLUSIVE) << 62 | addr | log_size; + pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_ADDR, type << 48); + pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_DATA, vsd); + + if (type != VST_EAS && type != VST_IC && type != VST_ERQ) { + pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_ADDR, type << 48); + pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_DATA, vsd); + } +} + +static void set_tima8(QTestState *qts, uint32_t pir, uint32_t offset, + uint8_t b) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + qtest_writeb(qts, ic_addr + offset, b); +} + +static void set_tima32(QTestState *qts, uint32_t pir, uint32_t offset, + uint32_t l) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + qtest_writel(qts, ic_addr + offset, l); +} + +static uint8_t get_tima8(QTestState *qts, uint32_t pir, uint32_t offset) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + return qtest_readb(qts, ic_addr + offset); +} + +static uint16_t get_tima16(QTestState *qts, uint32_t pir, uint32_t offset) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + return qtest_readw(qts, ic_addr + offset); +} + +static uint32_t get_tima32(QTestState *qts, uint32_t pir, uint32_t offset) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + return qtest_readl(qts, ic_addr + offset); +} + +static void reset_pool_threads(QTestState *qts) +{ + uint8_t first_group = 0; + int i; + + for (i = 0; i < SMT; i++) { + uint32_t nvp_idx = 0x100 + i; + set_nvp(qts, nvp_idx, first_group); + set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD0, 0x000000ff); + set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD1, 0); + set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD2, TM_QW2W2_VP | nvp_idx); + } +} + +static void reset_hw_threads(QTestState *qts) +{ + uint8_t first_group = 0; + uint32_t w1 = 0x000000ff; + int i; + + if (SMT >= 4) { + /* define 2 groups of 2, part of a bigger group of size 4 */ + set_nvg(qts, 0x80, 0x02); + set_nvg(qts, 0x82, 0x02); + set_nvg(qts, 0x81, 0); + first_group = 0x01; + w1 = 0x000300ff; + } + + for (i = 0; i < SMT; i++) { + set_nvp(qts, 0x80 + i, first_group); + set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0, 0x00ff00ff); + set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD1, w1); + set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD2, 0x80000000); + } +} + +static void reset_state(QTestState *qts) +{ + size_t mem_used = XIVE_MEM_END - XIVE_MEM_START; + + qtest_memset(qts, XIVE_MEM_START, 0, mem_used); + reset_hw_threads(qts); + reset_pool_threads(qts); +} + +static void init_xive(QTestState *qts) +{ + uint64_t val1, val2, range; + + /* + * We can take a few shortcuts here, as we know the default values + * used for xive initialization + */ + + /* + * Set the BARs. + * We reuse the same values used by firmware to ease debug. + */ + pnv_xive_xscom_write(qts, X_CQ_IC_BAR, XIVE_IC_BAR); + pnv_xive_xscom_write(qts, X_CQ_TM_BAR, XIVE_TM_BAR); + + /* ESB and NVPG use 2 pages per resource. The others only one page */ + range = (MAX_IRQS << 17) >> 25; + val1 = XIVE_ESB_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_ESB_BAR, val1); + + range = (MAX_ENDS << 16) >> 25; + val1 = XIVE_END_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_END_BAR, val1); + + range = (MAX_VPS << 17) >> 25; + val1 = XIVE_NVPG_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_NVPG_BAR, val1); + + range = (MAX_VPS << 16) >> 25; + val1 = XIVE_NVC_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_NVC_BAR, val1); + + /* + * Enable hw threads. + * We check the value written. Useless with current + * implementation, but it validates the xscom read path and it's + * what the hardware procedure says + */ + val1 = 0xF000000000000000ull; /* core 0, 4 threads */ + pnv_xive_xscom_write(qts, X_TCTXT_EN0, val1); + val2 = pnv_xive_xscom_read(qts, X_TCTXT_EN0); + g_assert_cmphex(val1, ==, val2); + + /* Memory tables */ + set_table(qts, VST_ESB, XIVE_ESB_MEM); + set_table(qts, VST_EAS, XIVE_EAS_MEM); + set_table(qts, VST_END, XIVE_END_MEM); + set_table(qts, VST_NVP, XIVE_NVP_MEM); + set_table(qts, VST_NVG, XIVE_NVG_MEM); + set_table(qts, VST_NVC, XIVE_NVC_MEM); + set_table(qts, VST_SYNC, XIVE_SYNC_MEM); + + reset_hw_threads(qts); + reset_pool_threads(qts); +} + +static void test_hw_irq(QTestState *qts) +{ + uint32_t irq = 2; + uint32_t irq_data = 0x600df00d; + uint32_t end_index = 5; + uint32_t target_pir = 1; + uint32_t target_nvp = 0x80 + target_pir; + uint8_t priority = 5; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr; + + printf("# ============================================================\n"); + printf("# Testing irq %d to hardware thread %d\n", irq, target_pir); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, false /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x80); + g_assert_cmphex(cppr, ==, 0xFF); + + /* ack the irq */ + reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x80); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + +#define XIVE_ODD_CL 0x80 +static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) +{ + uint32_t target_pir = 1; + uint32_t target_nvp = 0x80 + target_pir; + Xive2Nvp nvp; + uint8_t cl_pair[XIVE_REPORT_SIZE]; + uint32_t qw1w0, qw3w0, qw1w2, qw2w2; + uint8_t qw3b8; + uint32_t cl_word; + uint32_t word2; + + printf("# ============================================================\n"); + printf("# Testing 'Pull Thread Context to Odd Thread Reporting Line'\n"); + + /* clear odd cache line prior to pull operation */ + memset(cl_pair, 0, sizeof(cl_pair)); + get_nvp(qts, target_nvp, &nvp); + set_cl_pair(qts, &nvp, cl_pair); + + /* Read some values from TIMA that we expect to see in cacheline */ + qw1w0 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD0); + qw3w0 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + qw1w2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); + qw2w2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); + qw3b8 = get_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); + + /* Execute the pull operation */ + set_tima8(qts, target_pir, TM_SPC_PULL_PHYS_CTX_OL, 0); + + /* Verify odd cache line values match TIMA after pull operation */ + get_cl_pair(qts, &nvp, cl_pair); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD0], 4); + g_assert_cmphex(qw1w0, ==, be32_to_cpu(cl_word)); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD0], 4); + g_assert_cmphex(qw3w0, ==, be32_to_cpu(cl_word)); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD2], 4); + g_assert_cmphex(qw1w2, ==, be32_to_cpu(cl_word)); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW2_HV_POOL + TM_WORD2], 4); + g_assert_cmphex(qw2w2, ==, be32_to_cpu(cl_word)); + g_assert_cmphex(qw3b8, ==, + cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD2]); + + /* Verify that all TIMA valid bits for target thread are cleared */ + word2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); + g_assert_cmphex(xive_get_field32(TM_QW1W2_VO, word2), ==, 0); + word2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); + g_assert_cmphex(xive_get_field32(TM_QW2W2_VP, word2), ==, 0); + word2 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); + g_assert_cmphex(xive_get_field32(TM_QW3W2_VT, word2), ==, 0); +} +static void test_xive(void) +{ + QTestState *qts; + + qts = qtest_initf("-M powernv10 -smp %d,cores=1,threads=%d -nographic " + "-nodefaults -serial mon:stdio -S " + "-d guest_errors -trace '*xive*'", + SMT, SMT); + init_xive(qts); + + test_hw_irq(qts); + + /* omit reset_state here and use settings from test_hw_irq */ + test_pull_thread_ctx_to_odd_thread_cl(qts); + + reset_state(qts); + test_flush_sync_inject(qts); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("xive2", test_xive); + 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..5aa1701ea7 --- /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 = 0x220da04980000000ull, + .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/ppc-util.h b/tests/qtest/ppc-util.h new file mode 100644 index 0000000000..f68ee93520 --- /dev/null +++ b/tests/qtest/ppc-util.h @@ -0,0 +1,19 @@ +/* + * PowerPC misc useful things + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_UTIL_H +#define PPC_UTIL_H + +/* List of capabilities needed to silence warnings with TCG */ +#define PSERIES_DEFAULT_CAPABILITIES \ + "cap-cfpc=broken," \ + "cap-sbbc=broken," \ + "cap-ibs=broken," \ + "cap-ccf-assist=off," + +#endif /* PPC_UTIL_H */ diff --git a/tests/qtest/prom-env-test.c b/tests/qtest/prom-env-test.c index 39ccb59797..14705105ad 100644 --- a/tests/qtest/prom-env-test.c +++ b/tests/qtest/prom-env-test.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/libqos-spapr.h" +#include "ppc-util.h" #define MAGIC 0xcafec0de #define ADDRESS 0x4000 diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index c82c365c26..dc021c2fdf 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -16,6 +16,7 @@ #include "qapi/qmp/qdict.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" +#include "hw/misc/pvpanic.h" #include "hw/pci/pci_regs.h" static void test_panic_nopause(void) @@ -34,7 +35,7 @@ static void test_panic_nopause(void) bar = qpci_iomap(dev, 0, NULL); qpci_memread(dev, bar, 0, &val, sizeof(val)); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); val = 1; qpci_memwrite(dev, bar, 0, &val, sizeof(val)); @@ -67,7 +68,7 @@ static void test_panic(void) bar = qpci_iomap(dev, 0, NULL); qpci_memread(dev, bar, 0, &val, sizeof(val)); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); val = 1; qpci_memwrite(dev, bar, 0, &val, sizeof(val)); @@ -84,15 +85,50 @@ static void test_panic(void) qtest_quit(qts); } +static void test_pvshutdown(void) +{ + uint8_t val; + QDict *response, *data; + QTestState *qts; + QPCIBus *pcibus; + QPCIDevice *dev; + QPCIBar bar; + + qts = qtest_init("-device pvpanic-pci,addr=04.0"); + pcibus = qpci_new_pc(qts, NULL); + dev = qpci_device_find(pcibus, QPCI_DEVFN(0x4, 0x0)); + qpci_device_enable(dev); + bar = qpci_iomap(dev, 0, NULL); + + qpci_memread(dev, bar, 0, &val, sizeof(val)); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); + + val = PVPANIC_SHUTDOWN; + qpci_memwrite(dev, bar, 0, &val, sizeof(val)); + + response = qtest_qmp_eventwait_ref(qts, "GUEST_PVSHUTDOWN"); + qobject_unref(response); + + response = qtest_qmp_eventwait_ref(qts, "SHUTDOWN"); + g_assert(qdict_haskey(response, "data")); + data = qdict_get_qdict(response, "data"); + g_assert(qdict_haskey(data, "guest")); + g_assert(qdict_get_bool(data, "guest")); + g_assert(qdict_haskey(data, "reason")); + g_assert_cmpstr(qdict_get_str(data, "reason"), ==, "guest-shutdown"); + qobject_unref(response); + + g_free(dev); + qpci_free_pc(pcibus); + qtest_quit(qts); +} + 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); + qtest_add_func("/pvpanic-pci/pvshutdown", test_pvshutdown); - 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..d49d2ba931 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qapi/qmp/qdict.h" +#include "hw/misc/pvpanic.h" static void test_panic_nopause(void) { @@ -20,7 +21,7 @@ static void test_panic_nopause(void) qts = qtest_init("-device pvpanic -action panic=none"); val = qtest_inb(qts, 0x505); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); qtest_outb(qts, 0x505, 0x1); @@ -43,7 +44,7 @@ static void test_panic(void) qts = qtest_init("-device pvpanic -action panic=pause"); val = qtest_inb(qts, 0x505); - g_assert_cmpuint(val, ==, 3); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); qtest_outb(qts, 0x505, 0x1); @@ -57,15 +58,40 @@ static void test_panic(void) qtest_quit(qts); } +static void test_pvshutdown(void) +{ + uint8_t val; + QDict *response, *data; + QTestState *qts; + + qts = qtest_init("-device pvpanic"); + + val = qtest_inb(qts, 0x505); + g_assert_cmpuint(val, ==, PVPANIC_EVENTS); + + qtest_outb(qts, 0x505, PVPANIC_SHUTDOWN); + + response = qtest_qmp_eventwait_ref(qts, "GUEST_PVSHUTDOWN"); + qobject_unref(response); + + response = qtest_qmp_eventwait_ref(qts, "SHUTDOWN"); + g_assert(qdict_haskey(response, "data")); + data = qdict_get_qdict(response, "data"); + g_assert(qdict_haskey(data, "guest")); + g_assert(qdict_get_bool(data, "guest")); + g_assert(qdict_haskey(data, "reason")); + g_assert_cmpstr(qdict_get_str(data, "reason"), ==, "guest-shutdown"); + qobject_unref(response); + + qtest_quit(qts); +} + 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); + qtest_add_func("/pvpanic/pvshutdown", test_pvshutdown); - 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..a3f900fbea 100644 --- a/tests/qtest/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -16,7 +16,7 @@ #include #include "libqtest.h" #include "boot-sector.h" -#include "libqos/libqos-spapr.h" +#include "ppc-util.h" #define NETNAME "net0" @@ -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 897e4e937b..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; @@ -174,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/qom-test.c b/tests/qtest/qom-test.c index 13510bc349..d677f87c8e 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -14,6 +14,8 @@ #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; @@ -22,7 +24,9 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) QListEntry *entry; GSList *children = NULL, *links = NULL; - g_test_message("Obtaining properties of %s", path); + if (verbosity_level >= 2) { + g_test_message("Obtaining properties of %s", path); + } response = qtest_qmp(qts, "{ 'execute': 'qom-list'," " 'arguments': { 'path': %s } }", path); g_assert(response); @@ -49,7 +53,9 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) } } else { const char *prop = qdict_get_str(tuple, "name"); - g_test_message("-> %s", prop); + if (verbosity_level >= 3) { + g_test_message("-> %s", prop); + } tmp = qtest_qmp(qts, "{ 'execute': 'qom-get'," " 'arguments': { 'path': %s, 'property': %s } }", @@ -103,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 5da4091ec3..114f6bef27 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -33,7 +33,6 @@ static char *old_path; - /** * qos_set_machines_devices_available(): sets availability of qgraph * machines and devices. @@ -191,6 +190,12 @@ static void subprocess_run_one_test(const void *arg) g_test_trap_assert_passed(); } +static void destroy_pathv(void *arg) +{ + g_free(((char **)arg)[0]); + g_free(arg); +} + /* * in this function, 2 path will be built: * path_str, a one-string path (ex "pc/i440FX-pcihost/...") @@ -295,10 +300,13 @@ static void walk_path(QOSGraphNode *orig_path, int len) if (path->u.test.subprocess) { gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", qtest_get_arch(), path_str); - qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test); - g_test_add_data_func(subprocess_path, path_vec, run_one_test); + qtest_add_data_func_full(path_str, subprocess_path, + subprocess_run_one_test, g_free); + g_test_add_data_func_full(subprocess_path, path_vec, + run_one_test, destroy_pathv); } else { - qtest_add_data_func(path_str, path_vec, run_one_test); + qtest_add_data_func_full(path_str, path_vec, + run_one_test, destroy_pathv); } g_free(path_str); 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 index c7a9b0c7dd..760f974e63 100644 --- a/tests/qtest/readconfig-test.c +++ b/tests/qtest/readconfig-test.c @@ -48,7 +48,7 @@ static QTestState *qtest_init_with_config(const char *cfgdata) return qts; } -static void test_x86_memdev_resp(QObject *res) +static void test_x86_memdev_resp(QObject *res, const char *mem_id, int size) { Visitor *v; g_autoptr(MemdevList) memdevs = NULL; @@ -63,8 +63,8 @@ static void test_x86_memdev_resp(QObject *res) g_assert(!memdevs->next); memdev = memdevs->value; - g_assert_cmpstr(memdev->id, ==, "ram"); - g_assert_cmpint(memdev->size, ==, 200 * MiB); + g_assert_cmpstr(memdev->id, ==, mem_id); + g_assert_cmpint(memdev->size, ==, size * MiB); visit_free(v); } @@ -80,14 +80,14 @@ static void test_x86_memdev(void) qts = qtest_init_with_config(cfgdata); /* Test valid command */ resp = qtest_qmp(qts, "{ 'execute': 'query-memdev' }"); - test_x86_memdev_resp(qdict_get(resp, "return")); + test_x86_memdev_resp(qdict_get(resp, "return"), "ram", 200); qobject_unref(resp); qtest_quit(qts); } - -#ifdef CONFIG_SPICE +/* FIXME: The test is currently broken on FreeBSD */ +#if defined(CONFIG_SPICE) && !defined(__FreeBSD__) static void test_spice_resp(QObject *res) { Visitor *v; @@ -109,8 +109,10 @@ static void test_spice(void) QTestState *qts; const char *cfgdata = "[spice]\n" - "disable-ticketing = \"on\"\n" - "unix = \"on\"\n"; +#ifndef WIN32 + "unix = \"on\"\n" +#endif + "disable-ticketing = \"on\"\n"; qts = qtest_init_with_config(cfgdata); /* Test valid command */ @@ -122,13 +124,15 @@ static void test_spice(void) } #endif -static void test_object_rng_resp(QObject *res) +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 seen_rng = false; + bool object_available = false; + g_autofree char *childtype = g_strdup_printf("child<%s>", type); g_assert(res); v = qobject_input_visitor_new(res); @@ -140,16 +144,15 @@ static void test_object_rng_resp(QObject *res) g_assert(tmp->value); obj = tmp->value; - if (g_str_equal(obj->name, "rng0") && - g_str_equal(obj->type, "child")) { - seen_rng = true; + if (g_str_equal(obj->name, name) && g_str_equal(obj->type, childtype)) { + object_available = true; break; } tmp = tmp->next; } - g_assert(seen_rng); + g_assert(object_available); visit_free(v); } @@ -168,12 +171,215 @@ static void test_object_rng(void) resp = qtest_qmp(qts, "{ 'execute': 'qom-list'," " 'arguments': {'path': '/objects' }}"); - test_object_rng_resp(qdict_get(resp, "return")); + 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; @@ -184,8 +390,25 @@ int main(int argc, char *argv[]) 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 } -#ifdef CONFIG_SPICE +#if defined(CONFIG_SPICE) && !defined(__FreeBSD__) qtest_add_func("readconfig/spice", test_spice); #endif diff --git a/tests/qtest/riscv-iommu-test.c b/tests/qtest/riscv-iommu-test.c new file mode 100644 index 0000000000..df0c7813ec --- /dev/null +++ b/tests/qtest/riscv-iommu-test.c @@ -0,0 +1,210 @@ +/* + * QTest testcase for RISC-V IOMMU + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * 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 "libqtest-single.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/riscv-iommu.h" +#include "hw/pci/pci_regs.h" + +static uint32_t riscv_iommu_read_reg32(QRISCVIOMMU *r_iommu, int reg_offset) +{ + return qpci_io_readl(&r_iommu->dev, r_iommu->reg_bar, reg_offset); +} + +static uint64_t riscv_iommu_read_reg64(QRISCVIOMMU *r_iommu, int reg_offset) +{ + return qpci_io_readq(&r_iommu->dev, r_iommu->reg_bar, reg_offset); +} + +static void riscv_iommu_write_reg32(QRISCVIOMMU *r_iommu, int reg_offset, + uint32_t val) +{ + qpci_io_writel(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val); +} + +static void riscv_iommu_write_reg64(QRISCVIOMMU *r_iommu, int reg_offset, + uint64_t val) +{ + qpci_io_writeq(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val); +} + +static void test_pci_config(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QRISCVIOMMU *r_iommu = obj; + QPCIDevice *dev = &r_iommu->dev; + uint16_t vendorid, deviceid, classid; + + vendorid = qpci_config_readw(dev, PCI_VENDOR_ID); + deviceid = qpci_config_readw(dev, PCI_DEVICE_ID); + classid = qpci_config_readw(dev, PCI_CLASS_DEVICE); + + g_assert_cmpuint(vendorid, ==, RISCV_IOMMU_PCI_VENDOR_ID); + g_assert_cmpuint(deviceid, ==, RISCV_IOMMU_PCI_DEVICE_ID); + g_assert_cmpuint(classid, ==, RISCV_IOMMU_PCI_DEVICE_CLASS); +} + +static void test_reg_reset(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QRISCVIOMMU *r_iommu = obj; + uint64_t cap; + uint32_t reg; + + cap = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP); + g_assert_cmpuint(cap & RISCV_IOMMU_CAP_VERSION, ==, 0x10); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQEN, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CIE, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQON, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_BUSY, ==, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQEN, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FIE, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQON, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_BUSY, ==, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQEN, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PIE, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQON, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_BUSY, ==, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_DDTP); + g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_BUSY, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_MODE, ==, + RISCV_IOMMU_DDTP_MODE_OFF); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IPSR); + g_assert_cmpuint(reg, ==, 0); +} + +/* + * Common timeout-based poll for CQCSR, FQCSR and PQCSR. All + * their ON bits are mapped as RISCV_IOMMU_QUEUE_ACTIVE (16), + */ +static void qtest_wait_for_queue_active(QRISCVIOMMU *r_iommu, + uint32_t queue_csr) +{ + QTestState *qts = global_qtest; + guint64 timeout_us = 2 * 1000 * 1000; + gint64 start_time = g_get_monotonic_time(); + uint32_t reg; + + for (;;) { + qtest_clock_step(qts, 100); + + reg = riscv_iommu_read_reg32(r_iommu, queue_csr); + if (reg & RISCV_IOMMU_QUEUE_ACTIVE) { + break; + } + g_assert(g_get_monotonic_time() - start_time <= timeout_us); + } +} + +/* + * Goes through the queue activation procedures of chapter 6.2, + * "Guidelines for initialization", of the RISCV-IOMMU spec. + */ +static void test_iommu_init_queues(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QRISCVIOMMU *r_iommu = obj; + uint64_t reg64, q_addr; + uint32_t reg; + int k = 2; + + reg64 = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP); + g_assert_cmpuint(reg64 & RISCV_IOMMU_CAP_VERSION, ==, 0x10); + + /* + * Program the command queue. Write 0xF to civ, fiv, pmiv and + * piv. With the current PCI device impl we expect 2 writable + * bits for each (k = 2) since we have N = 4 total vectors (2^k). + */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_IVEC, 0xFFFF); + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IVEC); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_CIV, ==, 0x3); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_FIV, ==, 0x30); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PMIV, ==, 0x300); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PIV, ==, 0x3000); + + /* Alloc a 4*16 bytes buffer and use it to set cqb */ + q_addr = guest_alloc(t_alloc, 4 * 16); + reg64 = 0; + deposit64(reg64, RISCV_IOMMU_CQB_PPN_START, + RISCV_IOMMU_CQB_PPN_LEN, q_addr); + deposit64(reg64, RISCV_IOMMU_CQB_LOG2SZ_START, + RISCV_IOMMU_CQB_LOG2SZ_LEN, k - 1); + riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_CQB, reg64); + + /* cqt = 0, cqcsr.cqen = 1, poll cqcsr.cqon until it reads 1 */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQT, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR); + reg |= RISCV_IOMMU_CQCSR_CQEN; + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR, reg); + + qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_CQCSR); + + /* + * Program the fault queue. Alloc a 4*32 bytes (instead of 4*16) + * buffer and use it to set fqb. + */ + q_addr = guest_alloc(t_alloc, 4 * 32); + reg64 = 0; + deposit64(reg64, RISCV_IOMMU_FQB_PPN_START, + RISCV_IOMMU_FQB_PPN_LEN, q_addr); + deposit64(reg64, RISCV_IOMMU_FQB_LOG2SZ_START, + RISCV_IOMMU_FQB_LOG2SZ_LEN, k - 1); + riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_FQB, reg64); + + /* fqt = 0, fqcsr.fqen = 1, poll fqcsr.fqon until it reads 1 */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQT, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR); + reg |= RISCV_IOMMU_FQCSR_FQEN; + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR, reg); + + qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_FQCSR); + + /* + * Program the page-request queue. Alloc a 4*16 bytes buffer + * and use it to set pqb. + */ + q_addr = guest_alloc(t_alloc, 4 * 16); + reg64 = 0; + deposit64(reg64, RISCV_IOMMU_PQB_PPN_START, + RISCV_IOMMU_PQB_PPN_LEN, q_addr); + deposit64(reg64, RISCV_IOMMU_PQB_LOG2SZ_START, + RISCV_IOMMU_PQB_LOG2SZ_LEN, k - 1); + riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_PQB, reg64); + + /* pqt = 0, pqcsr.pqen = 1, poll pqcsr.pqon until it reads 1 */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQT, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR); + reg |= RISCV_IOMMU_PQCSR_PQEN; + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR, reg); + + qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_PQCSR); +} + +static void register_riscv_iommu_test(void) +{ + qos_add_test("pci_config", "riscv-iommu-pci", test_pci_config, NULL); + qos_add_test("reg_reset", "riscv-iommu-pci", test_reg_reset, NULL); + qos_add_test("iommu_init_queues", "riscv-iommu-pci", + test_iommu_init_queues, NULL); +} + +libqos_init(register_riscv_iommu_test); 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/rtl8139-test.c b/tests/qtest/rtl8139-test.c index 8fa3313cc3..55f671f2f5 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) @@ -59,7 +65,7 @@ PORT(IntrMask, w, 0x3c) PORT(IntrStatus, w, 0x3E) PORT(TimerInt, l, 0x54) -#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) +#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert_not_reached(); } while (0) static void test_timer(void) { @@ -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/sse-timer-test.c b/tests/qtest/sse-timer-test.c index a65d7542d5..fd5635d4c9 100644 --- a/tests/qtest/sse-timer-test.c +++ b/tests/qtest/sse-timer-test.c @@ -181,12 +181,12 @@ static void test_timer(void) writel(TIMER_BASE + CNTP_AIVAL_CTL, 0); clock_step_ticks(0x42ULL << 32); g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_LO), ==, 4400); - g_assert_cmpuint(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42); + g_assert_cmphex(readl(TIMER_BASE + CNTPCT_HI), ==, 0x42); /* Turn on the autoinc again to check AIVAL_HI */ writel(TIMER_BASE + CNTP_AIVAL_CTL, 1); g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_LO), ==, 4600); - g_assert_cmpuint(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42); + g_assert_cmphex(readl(TIMER_BASE + CNTP_AIVAL_HI), ==, 0x42); } static void test_timer_scale_change(void) diff --git a/tests/qtest/stm32l4x5.h b/tests/qtest/stm32l4x5.h new file mode 100644 index 0000000000..2d21cc666c --- /dev/null +++ b/tests/qtest/stm32l4x5.h @@ -0,0 +1,42 @@ +/* + * QTest testcase header for STM32L4X5 : + * used for consolidating common objects in stm32l4x5_*-test.c + * + * 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 "libqtest.h" + +/* copied from clock.h */ +#define CLOCK_PERIOD_1SEC (1000000000llu << 32) +#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u) +/* + * MSI (4 MHz) is used as system clock source after startup + * from Reset. + * AHB, APB1 and APB2 prescalers are set to 1 at reset. + */ +#define SYSCLK_PERIOD CLOCK_PERIOD_FROM_HZ(4000000) +#define RCC_AHB2ENR 0x4002104C +#define RCC_APB1ENR1 0x40021058 +#define RCC_APB1ENR2 0x4002105C +#define RCC_APB2ENR 0x40021060 + + +static inline uint64_t get_clock_period(QTestState *qts, const char *path) +{ + uint64_t clock_period = 0; + QDict *r; + + r = qtest_qmp(qts, "{ 'execute': 'qom-get', 'arguments':" + " { 'path': %s, 'property': 'qtest-clock-period'} }", path); + g_assert_false(qdict_haskey(r, "error")); + clock_period = qdict_get_int(r, "return"); + qobject_unref(r); + return clock_period; +} + + diff --git a/tests/qtest/stm32l4x5_exti-test.c b/tests/qtest/stm32l4x5_exti-test.c new file mode 100644 index 0000000000..7e39c992fd --- /dev/null +++ b/tests/qtest/stm32l4x5_exti-test.c @@ -0,0 +1,569 @@ +/* + * 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_cmphex(exti_readl(EXTI_IMR1), ==, 0xFFFFFFFF); + exti_writel(EXTI_IMR1, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_IMR1), ==, 0x00000000); + + exti_writel(EXTI_EMR1, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_EMR1), ==, 0xFFFFFFFF); + exti_writel(EXTI_EMR1, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_EMR1), ==, 0x00000000); + + exti_writel(EXTI_RTSR1, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_RTSR1), ==, 0x007DFFFF); + exti_writel(EXTI_RTSR1, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR1), ==, 0x00000000); + + exti_writel(EXTI_FTSR1, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_FTSR1), ==, 0x007DFFFF); + exti_writel(EXTI_FTSR1, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_FTSR1), ==, 0x00000000); + + exti_writel(EXTI_IMR2, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_IMR2), ==, 0x000000FF); + exti_writel(EXTI_IMR2, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_IMR2), ==, 0x00000000); + + exti_writel(EXTI_EMR2, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_EMR2), ==, 0x000000FF); + exti_writel(EXTI_EMR2, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_EMR2), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000078); + exti_writel(EXTI_RTSR2, 0x00000000); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0xFFFFFFFF); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000078); + exti_writel(EXTI_FTSR2, 0x00000000); + g_assert_cmphex(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_cmphex(exti_readl(EXTI_RTSR1), ==, 0x00000000); + + exti_writel(EXTI_FTSR1, 0xFF820000); + g_assert_cmphex(exti_readl(EXTI_FTSR1), ==, 0x00000000); + + exti_writel(EXTI_SWIER1, 0xFF820000); + g_assert_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000000); + + exti_writel(EXTI_PR1, 0xFF820000); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0x00000087); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0x00000087); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000000); + + exti_writel(EXTI_SWIER2, 0x00000087); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000000); + + exti_writel(EXTI_PR2, 0x00000087); + g_assert_cmphex(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_cmphex(exti_readl(EXTI_IMR2), ==, 0x00000000); + + exti_writel(EXTI_EMR2, 0xFFFFFF00); + g_assert_cmphex(exti_readl(EXTI_EMR2), ==, 0x00000000); + + exti_writel(EXTI_RTSR2, 0xFFFFFF00); + g_assert_cmphex(exti_readl(EXTI_RTSR2), ==, 0x00000000); + + exti_writel(EXTI_FTSR2, 0xFFFFFF00); + g_assert_cmphex(exti_readl(EXTI_FTSR2), ==, 0x00000000); + + exti_writel(EXTI_SWIER2, 0xFFFFFF00); + g_assert_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000000); + + exti_writel(EXTI_PR2, 0xFFFFFF00); + g_assert_cmphex(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_cmphex(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_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000001); + /* Check that the corresponding pending bit in PR is set */ + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the corresponding bit in SWIER was cleared */ + g_assert_cmphex(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_cmphex(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_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000008); + /* Check that the corresponding pending bit in PR is set */ + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR2), ==, 0x00000000); + /* Check that the corresponding bit in SWIER was cleared */ + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 1); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 0); + g_assert_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000001); + g_assert_true(check_nvic_pending(EXTI0_IRQ)); + + /* Clean the test */ + exti_writel(EXTI_PR1, 0x00000001); + g_assert_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); + g_assert_false(check_nvic_pending(EXTI0_IRQ)); + + exti_set_irq(0, 0); + g_assert_cmphex(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_cmphex(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_cmphex(exti_readl(EXTI_SWIER1), ==, 0x00000001); + /* Check that the pending bit in PR wasn't set */ + g_assert_cmphex(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_cmphex(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_cmphex(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_cmphex(exti_readl(EXTI_SWIER2), ==, 0x00000008); + /* Check that the pending bit in PR wasn't set */ + g_assert_cmphex(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_cmphex(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_cmphex(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_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 0x00000000); + /* Check that the interrupt isn't pending in NVIC */ + g_assert_false(check_nvic_pending(EXTI1_IRQ)); + + /* Clean EXTI */ + exti_set_irq(1, 0); +} + +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_cmphex(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_cmphex(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_cmphex(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)); + + /* Clean EXTI */ + exti_set_irq(1, 0); +} + +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_cmphex(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_cmphex(exti_readl(EXTI_PR1), ==, 1 << i); + g_assert_true(check_nvic_pending(EXTI5_9_IRQ)); + + exti_writel(EXTI_PR1, 1 << i); + g_assert_cmphex(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)); + + exti_set_irq(i, 0); + } +} + +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..c0686c7b30 --- /dev/null +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -0,0 +1,588 @@ +/* + * 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" +#include "stm32l4x5.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 + +/* SoC forwards GPIOs to SysCfg */ +#define SYSCFG "/machine/soc" + +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, 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, 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, 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, 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, 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)); +} + +static void test_clock_enable(void) +{ + /* + * For each GPIO, enable its clock in RCC + * and check that its clock period changes to SYSCLK_PERIOD + */ + unsigned int gpio_id; + + for (uint32_t gpio = GPIO_A; gpio <= GPIO_H; gpio += GPIO_B - GPIO_A) { + gpio_id = get_gpio_id(gpio); + g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c/clk", + gpio_id + 'a'); + g_assert_cmpuint(get_clock_period(global_qtest, path), ==, 0); + /* Enable the gpio clock */ + writel(RCC_AHB2ENR, readl(RCC_AHB2ENR) | (0x1 << gpio_id)); + g_assert_cmpuint(get_clock_period(global_qtest, path), ==, + SYSCLK_PERIOD); + } +} + +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_add_func("stm32l4x5/gpio/test_clock_enable", + test_clock_enable); + + 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..d5c71e2c0e --- /dev/null +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -0,0 +1,350 @@ +/* + * QTest testcase for STM32L4x5_SYSCFG + * + * 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" +#include "stm32l4x5.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 + +/* SoC forwards GPIOs to SysCfg */ +#define SOC "/machine/soc" +#define SYSCFG "/machine/soc/syscfg" +#define SYSCFG_CLK "/machine/soc/syscfg/clk" +#define EXTI "/machine/soc/exti" + +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, SOC, 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_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_SCSR), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR2), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_SWPR), ==, 0x00000000); + + g_assert_cmphex(syscfg_readl(SYSCFG_SKR), ==, 0x00000000); + + g_assert_cmphex(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_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + syscfg_writel(SYSCFG_CFGR1, 0x7F00FEFF); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x7C000001); + + syscfg_writel(SYSCFG_EXTICR1, 0xFFFF0000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR2, 0xFFFF0000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR3, 0xFFFF0000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR4, 0xFFFF0000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + syscfg_writel(SYSCFG_SKR, 0xFFFFFF00); + g_assert_cmphex(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_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000107); + syscfg_writel(SYSCFG_MEMRMP, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_MEMRMP), ==, 0x00000000); + + /* cfgr1 bit 0 is clear only so we keep it set */ + syscfg_writel(SYSCFG_CFGR1, 0xFCFF0101); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0xFCFF0101); + syscfg_writel(SYSCFG_CFGR1, 0x00000001); + g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000001); + + syscfg_writel(SYSCFG_EXTICR1, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR1), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR2, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR2, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR2), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR3, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR3, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR3), ==, 0x00000000); + + syscfg_writel(SYSCFG_EXTICR4, 0x0000FFFF); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x0000FFFF); + syscfg_writel(SYSCFG_EXTICR4, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_EXTICR4), ==, 0x00000000); + + syscfg_writel(SYSCFG_SKR, 0x000000FF); + g_assert_cmphex(syscfg_readl(SYSCFG_SKR), ==, 0x000000FF); + syscfg_writel(SYSCFG_SKR, 0x00000000); + g_assert_cmphex(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_cmphex(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_cmphex(syscfg_readl(SYSCFG_CFGR2), ==, 0x0000000F); + + syscfg_writel(SYSCFG_SWPR, 0xFFFFFFFF); + syscfg_writel(SYSCFG_SWPR, 0x00000000); + g_assert_cmphex(syscfg_readl(SYSCFG_SWPR), ==, 0xFFFFFFFF); + + syscfg_writel(SYSCFG_SWPR2, 0xFFFFFFFF); + syscfg_writel(SYSCFG_SWPR2, 0x00000000); + g_assert_cmphex(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_cmphex(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, 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_set_irq(0, 0); + /* irq 15 is high at reset because GPIOA15 is high at reset */ + syscfg_set_irq(17, 0); + syscfg_writel(SYSCFG_EXTICR1, 0x00000000); +} + +static void test_irq_pin_multiplexer(void) +{ + /* + * Test that syscfg irq sets the right exti irq + */ + + qtest_irq_intercept_in(global_qtest, EXTI); + + syscfg_set_irq(0, 1); + + /* Check that irq 0 was set and irq 2 wasn't */ + g_assert_true(get_irq(0)); + g_assert_false(get_irq(2)); + + /* Clean the test */ + syscfg_set_irq(0, 0); + + syscfg_set_irq(2, 1); + + /* Check that irq 2 was set and irq 0 wasn't */ + g_assert_true(get_irq(2)); + g_assert_false(get_irq(0)); + + /* Clean the test */ + syscfg_set_irq(2, 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, 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); +} + +static void test_clock_enable(void) +{ + g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==, 0); + + /* Enable SYSCFG clock */ + writel(RCC_APB2ENR, readl(RCC_APB2ENR) | (0x1 << 0)); + + g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==, + SYSCLK_PERIOD); +} + +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_add_func("stm32l4x5/syscfg/test_clock_enable", + test_clock_enable); + + qtest_start("-machine b-l475e-iot01a"); + ret = g_test_run(); + qtest_end(); + + return ret; +} diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c new file mode 100644 index 0000000000..927bab6361 --- /dev/null +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -0,0 +1,379 @@ +/* + * QTest testcase for STML4X5_USART + * + * 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.h" +#include "hw/misc/stm32l4x5_rcc_internals.h" +#include "hw/registerfields.h" +#include "stm32l4x5.h" + +#define RCC_BASE_ADDR 0x40021000 +/* Use USART 1 ADDR, assume the others work the same */ +#define USART1_BASE_ADDR 0x40013800 + +/* See stm32l4x5_usart for definitions */ +REG32(CR1, 0x00) + FIELD(CR1, M1, 28, 1) + FIELD(CR1, OVER8, 15, 1) + FIELD(CR1, M0, 12, 1) + FIELD(CR1, PCE, 10, 1) + FIELD(CR1, TXEIE, 7, 1) + FIELD(CR1, RXNEIE, 5, 1) + FIELD(CR1, TE, 3, 1) + FIELD(CR1, RE, 2, 1) + FIELD(CR1, UE, 0, 1) +REG32(CR2, 0x04) +REG32(CR3, 0x08) + FIELD(CR3, OVRDIS, 12, 1) +REG32(BRR, 0x0C) +REG32(GTPR, 0x10) +REG32(RTOR, 0x14) +REG32(RQR, 0x18) +REG32(ISR, 0x1C) + FIELD(ISR, REACK, 22, 1) + FIELD(ISR, TEACK, 21, 1) + FIELD(ISR, TXE, 7, 1) + FIELD(ISR, RXNE, 5, 1) + FIELD(ISR, ORE, 3, 1) +REG32(ICR, 0x20) +REG32(RDR, 0x24) +REG32(TDR, 0x28) + +#define NVIC_ISPR1 0XE000E204 +#define NVIC_ICPR1 0xE000E284 +#define USART1_IRQ 37 + +static bool check_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + assert(n > 32); + n -= 32; + return qtest_readl(qts, NVIC_ISPR1) & (1 << n); +} + +static bool clear_nvic_pending(QTestState *qts, unsigned int n) +{ + /* No USART interrupts are less than 32 */ + assert(n > 32); + n -= 32; + qtest_writel(qts, NVIC_ICPR1, (1 << n)); + return true; +} + +/* + * Wait indefinitely for the flag to be updated. + * If this is run on a slow CI runner, + * the meson harness will timeout after 10 minutes for us. + */ +static bool usart_wait_for_flag(QTestState *qts, uint32_t event_addr, + uint32_t flag) +{ + while (true) { + if ((qtest_readl(qts, event_addr) & flag)) { + return true; + } + g_usleep(1000); + } + + return false; +} + +static void usart_receive_string(QTestState *qts, int sock_fd, const char *in, + char *out) +{ + int i, in_len = strlen(in); + + g_assert_true(send(sock_fd, in, in_len, 0) == in_len); + for (i = 0; i < in_len; i++) { + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK)); + out[i] = qtest_readl(qts, USART1_BASE_ADDR + A_RDR); + } + out[i] = '\0'; +} + +static void usart_send_string(QTestState *qts, const char *in) +{ + int i, in_len = strlen(in); + + for (i = 0; i < in_len; i++) { + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, in[i]); + g_assert_true(usart_wait_for_flag(qts, + USART1_BASE_ADDR + A_ISR, R_ISR_TXE_MASK)); + } +} + +/* Init the RCC clocks to run at 80 MHz */ +static void init_clocks(QTestState *qts) +{ + uint32_t value; + + /* MSIRANGE can be set only when MSI is OFF or READY */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), R_CR_MSION_MASK); + + /* Clocking from MSI, in case MSI was not the default source */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* + * 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 + */ + qtest_writel(qts, (RCC_BASE_ADDR + A_PLLCFGR), R_PLLCFGR_PLLREN_MASK | + (40 << R_PLLCFGR_PLLN_SHIFT) | + (0b01 << R_PLLCFGR_PLLSRC_SHIFT)); + + /* PLL activation */ + + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CR), value | R_CR_PLLON_MASK); + + /* RCC_CFGR is OK by defaut */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), 0); + + /* CCIPR : no periph clock by default */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Switches on the PLL clock source */ + value = qtest_readl(qts, (RCC_BASE_ADDR + A_CFGR)); + qtest_writel(qts, (RCC_BASE_ADDR + A_CFGR), (value & ~R_CFGR_SW_MASK) | + (0b11 << R_CFGR_SW_SHIFT)); + + /* Enable SYSCFG clock enabled */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), R_APB2ENR_SYSCFGEN_MASK); + + /* Enable the IO port B clock (See p.252) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_AHB2ENR), R_AHB2ENR_GPIOBEN_MASK); + + /* Enable the clock for USART1 (cf p.259) */ + /* We rewrite SYSCFGEN to not disable it */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2ENR), + R_APB2ENR_SYSCFGEN_MASK | R_APB2ENR_USART1EN_MASK); + + /* TODO: Enable usart via gpio */ + + /* Set PCLK as the clock for USART1(cf p.272) i.e. reset both bits */ + qtest_writel(qts, (RCC_BASE_ADDR + A_CCIPR), 0); + + /* Reset USART1 (see p.249) */ + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 1 << 14); + qtest_writel(qts, (RCC_BASE_ADDR + A_APB2RSTR), 0); +} + +static void init_uart(QTestState *qts) +{ + uint32_t cr1; + + init_clocks(qts); + + /* + * For 115200 bauds, see p.1349. + * The clock has a frequency of 80Mhz, + * for 115200, we have to put a divider of 695 = 0x2B7. + */ + qtest_writel(qts, (USART1_BASE_ADDR + A_BRR), 0x2B7); + + /* + * Set the oversampling by 16, + * disable the parity control and + * set the word length to 8. (cf p.1377) + */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 &= ~(R_CR1_M1_MASK | R_CR1_M0_MASK | R_CR1_OVER8_MASK | R_CR1_PCE_MASK); + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), cr1); + + /* Enable the transmitter, the receiver and the USART. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 | R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); +} + +static void test_write_read(void) +{ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + /* Test that we can write and retrieve a value from the device */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 0xFFFFFFFF); + const uint32_t tdr = qtest_readl(qts, USART1_BASE_ADDR + A_TDR); + g_assert_cmpuint(tdr, ==, 0x000001FF); + + qtest_quit(qts); +} + +static void test_receive_char(void) +{ + int sock_fd; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + g_assert_true(send(sock_fd, "a", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'a'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_RXNEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + g_assert_true(send(sock_fd, "b", 1, 0) == 1); + usart_wait_for_flag(qts, USART1_BASE_ADDR + A_ISR, R_ISR_RXNE_MASK); + g_assert_cmphex(qtest_readl(qts, USART1_BASE_ADDR + A_RDR), ==, 'b'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_char(void) +{ + int sock_fd; + char s[1]; + uint32_t cr1; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + /* Try without initializing IRQ */ + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'c'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'c'); + g_assert_false(check_nvic_pending(qts, USART1_IRQ)); + + /* Now with the IRQ */ + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + cr1 |= R_CR1_TXEIE_MASK; + qtest_writel(qts, USART1_BASE_ADDR + A_CR1, cr1); + qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 'd'); + g_assert_true(recv(sock_fd, s, 1, 0) == 1); + g_assert_cmphex(s[0], ==, 'd'); + g_assert_true(check_nvic_pending(qts, USART1_IRQ)); + clear_nvic_pending(qts, USART1_IRQ); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_receive_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_receive_string(qts, sock_fd, "hello", s); + g_assert_true(memcmp(s, "hello", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_send_str(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M b-l475e-iot01a", &sock_fd); + + init_uart(qts); + + usart_send_string(qts, "world"); + g_assert_true(recv(sock_fd, s, 10, 0) == 5); + g_assert_true(memcmp(s, "world", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +static void test_ack(void) +{ + uint32_t cr1; + uint32_t isr; + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + init_uart(qts); + + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + + /* Disable the transmitter and receiver. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 & ~(R_CR1_RE_MASK | R_CR1_TE_MASK)); + + /* Test ISR ACK for transmitter and receiver disabled */ + isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); + g_assert_false(isr & R_ISR_TEACK_MASK); + g_assert_false(isr & R_ISR_REACK_MASK); + + /* Enable the transmitter and receiver. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 | (R_CR1_RE_MASK | R_CR1_TE_MASK)); + + /* Test ISR ACK for transmitter and receiver disabled */ + isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); + g_assert_true(isr & R_ISR_TEACK_MASK); + g_assert_true(isr & R_ISR_REACK_MASK); + + qtest_quit(qts); +} + +static void check_clock(QTestState *qts, const char *path, uint32_t rcc_reg, + uint32_t reg_offset) +{ + g_assert_cmpuint(get_clock_period(qts, path), ==, 0); + qtest_writel(qts, rcc_reg, qtest_readl(qts, rcc_reg) | (0x1 << reg_offset)); + g_assert_cmpuint(get_clock_period(qts, path), ==, SYSCLK_PERIOD); +} + +static void test_clock_enable(void) +{ + /* + * For each USART device, enable its clock in RCC + * and check that its clock frequency is SYSCLK_PERIOD + */ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + check_clock(qts, "machine/soc/usart[0]/clk", RCC_APB2ENR, 14); + check_clock(qts, "machine/soc/usart[1]/clk", RCC_APB1ENR1, 17); + check_clock(qts, "machine/soc/usart[2]/clk", RCC_APB1ENR1, 18); + check_clock(qts, "machine/soc/uart[0]/clk", RCC_APB1ENR1, 19); + check_clock(qts, "machine/soc/uart[1]/clk", RCC_APB1ENR1, 20); + check_clock(qts, "machine/soc/lpuart1/clk", RCC_APB1ENR2, 0); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + qtest_add_func("stm32l4x5/usart/write_read", test_write_read); + qtest_add_func("stm32l4x5/usart/receive_char", test_receive_char); + qtest_add_func("stm32l4x5/usart/send_char", test_send_char); + qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); + qtest_add_func("stm32l4x5/usart/send_str", test_send_str); + qtest_add_func("stm32l4x5/usart/ack", test_ack); + qtest_add_func("stm32l4x5/usart/clock_enable", test_clock_enable); + ret = g_test_run(); + + 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..b9e7e5ef7b 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -67,10 +67,29 @@ static void test_cpuid_prop(const void *data) g_free(path); } -static void add_cpuid_test(const char *name, const char *cmdline, +static void add_cpuid_test(const char *name, const char *cpu, + const char *cpufeat, const char *machine, const char *property, int64_t expected_value) { CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); + char *cmdline; + char *save; + + if (!qtest_has_cpu_model(cpu)) { + return; + } + cmdline = g_strdup_printf("-cpu %s", cpu); + + if (cpufeat) { + save = cmdline; + cmdline = g_strdup_printf("%s,%s", cmdline, cpufeat); + g_free(save); + } + if (machine) { + save = cmdline; + cmdline = g_strdup_printf("-machine %s %s", machine, cmdline); + g_free(save); + } args->cmdline = cmdline; args->property = property; args->expected_value = expected_value; @@ -149,12 +168,24 @@ static void test_feature_flag(const void *data) * either "feature-words" or "filtered-features", when running QEMU * using cmdline */ -static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, - uint32_t eax, uint32_t ecx, - const char *reg, int bitnr, - bool expected_value) +static void add_feature_test(const char *name, const char *cpu, + const char *cpufeat, uint32_t eax, + uint32_t ecx, const char *reg, + int bitnr, bool expected_value) { FeatureTestArgs *args = g_new0(FeatureTestArgs, 1); + char *cmdline; + + if (!qtest_has_cpu_model(cpu)) { + return; + } + + if (cpufeat) { + cmdline = g_strdup_printf("-cpu %s,%s", cpu, cpufeat); + } else { + cmdline = g_strdup_printf("-cpu %s", cpu); + } + args->cmdline = cmdline; args->in_eax = eax; args->in_ecx = ecx; @@ -162,13 +193,17 @@ static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, args->bitnr = bitnr; args->expected_value = expected_value; qtest_add_data_func(name, args, test_feature_flag); - return args; + return; } static void test_plus_minus_subprocess(void) { char *path; + if (!qtest_has_cpu_model("pentium")) { + return; + } + /* Rules: * 1)"-foo" overrides "+foo" * 2) "[+-]foo" overrides "foo=..." @@ -198,6 +233,10 @@ static void test_plus_minus_subprocess(void) static void test_plus_minus(void) { + if (!qtest_has_cpu_model("pentium")) { + return; + } + g_test_trap_subprocess("/x86/cpuid/parsing-plus-minus/subprocess", 0, 0); g_test_trap_assert_passed(); g_test_trap_assert_stderr("*Ambiguous CPU model string. " @@ -217,128 +256,126 @@ int main(int argc, char **argv) /* Original level values for CPU models: */ add_cpuid_test("x86/cpuid/phenom/level", - "-cpu phenom", "level", 5); + "phenom", NULL, NULL, "level", 5); add_cpuid_test("x86/cpuid/Conroe/level", - "-cpu Conroe", "level", 10); + "Conroe", NULL, NULL, "level", 10); add_cpuid_test("x86/cpuid/SandyBridge/level", - "-cpu SandyBridge", "level", 0xd); + "SandyBridge", NULL, NULL, "level", 0xd); add_cpuid_test("x86/cpuid/486/xlevel", - "-cpu 486", "xlevel", 0); + "486", NULL, NULL, "xlevel", 0); add_cpuid_test("x86/cpuid/core2duo/xlevel", - "-cpu core2duo", "xlevel", 0x80000008); + "core2duo", NULL, NULL, "xlevel", 0x80000008); add_cpuid_test("x86/cpuid/phenom/xlevel", - "-cpu phenom", "xlevel", 0x8000001A); + "phenom", NULL, NULL, "xlevel", 0x8000001A); add_cpuid_test("x86/cpuid/athlon/xlevel", - "-cpu athlon", "xlevel", 0x80000008); + "athlon", NULL, NULL, "xlevel", 0x80000008); /* If level is not large enough, it should increase automatically: */ /* CPUID[6].EAX: */ - add_cpuid_test("x86/cpuid/auto-level/phenom/arat", - "-cpu 486,arat=on", "level", 6); + add_cpuid_test("x86/cpuid/auto-level/486/arat", + "486", "arat=on", NULL, "level", 6); /* CPUID[EAX=7,ECX=0].EBX: */ add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", - "-cpu phenom,fsgsbase=on", "level", 7); + "phenom", "fsgsbase=on", NULL, "level", 7); /* CPUID[EAX=7,ECX=0].ECX: */ add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", - "-cpu phenom,avx512vbmi=on", "level", 7); + "phenom", "avx512vbmi=on", NULL, "level", 7); /* CPUID[EAX=0xd,ECX=1].EAX: */ add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", - "-cpu phenom,xsaveopt=on", "level", 0xd); + "phenom", "xsaveopt=on", NULL, "level", 0xd); /* CPUID[8000_0001].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", - "-cpu 486,3dnow=on", "xlevel", 0x80000001); + "486", "3dnow=on", NULL, "xlevel", 0x80000001); /* CPUID[8000_0001].ECX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", - "-cpu 486,sse4a=on", "xlevel", 0x80000001); + "486", "sse4a=on", NULL, "xlevel", 0x80000001); /* CPUID[8000_0007].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", - "-cpu 486,invtsc=on", "xlevel", 0x80000007); + "486", "invtsc=on", NULL, "xlevel", 0x80000007); /* CPUID[8000_000A].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", - "-cpu 486,svm=on,npt=on", "xlevel", 0x8000000A); + "486", "svm=on,npt=on", NULL, "xlevel", 0x8000000A); /* CPUID[C000_0001].EDX: */ add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", - "-cpu phenom,xstore=on", "xlevel2", 0xC0000001); + "phenom", "xstore=on", NULL, "xlevel2", 0xC0000001); /* SVM needs CPUID[0x8000000A] */ add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", - "-cpu athlon,svm=on", "xlevel", 0x8000000A); + "athlon", "svm=on", NULL, "xlevel", 0x8000000A); /* If level is already large enough, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", - "-cpu SandyBridge,arat=on,fsgsbase=on,avx512vbmi=on", - "level", 0xd); + "SandyBridge", "arat=on,fsgsbase=on,avx512vbmi=on", + NULL, "level", 0xd); /* If level is explicitly set, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", - "-cpu 486,level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", - "level", 0xF); + "486", + "level=0xF,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", + NULL, "level", 0xF); add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", - "-cpu 486,level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", - "level", 2); + "486", + "level=2,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", + NULL, "level", 2); add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", - "-cpu 486,level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", - "level", 0); + "486", + "level=0,arat=on,fsgsbase=on,avx512vbmi=on,xsaveopt=on", + NULL, "level", 0); /* if xlevel is already large enough, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", - "-cpu phenom,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0x8000001A); + "phenom", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0x8000001A); /* If xlevel is explicitly set, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", - "-cpu 486,xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0x80000002); + "486", + "xlevel=0x80000002,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0x80000002); add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", - "-cpu 486,xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0x8000001A); + "486", + "xlevel=0x8000001A,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0x8000001A); add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", - "-cpu 486,xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0); + "486", + "xlevel=0,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + NULL, "xlevel", 0); /* if xlevel2 is already large enough, it shouldn't change: */ add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", - "-cpu 486,xlevel2=0xC0000002,xstore=on", - "xlevel2", 0xC0000002); + "486", "xlevel2=0xC0000002,xstore=on", + NULL, "xlevel2", 0xC0000002); /* Check compatibility of old machine-types that didn't * auto-increase level/xlevel/xlevel2: */ if (qtest_has_machine("pc-i440fx-2.7")) { add_cpuid_test("x86/cpuid/auto-level/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,arat=on,avx512vbmi=on,xsaveopt=on", - "level", 1); + "486", "arat=on,avx512vbmi=on,xsaveopt=on", + "pc-i440fx-2.7", "level", 1); add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", - "xlevel", 0); + "486", "3dnow=on,sse4a=on,invtsc=on,npt=on,svm=on", + "pc-i440fx-2.7", "xlevel", 0); add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", - "-machine pc-i440fx-2.7 -cpu 486,xstore=on", + "486", "xstore=on", "pc-i440fx-2.7", "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", + "Penryn", NULL, "pc-i440fx-2.3", "level", 4); add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", - "-machine pc-i440fx-2.3 -cpu Penryn,erms=on", + "Penryn", "erms=on", "pc-i440fx-2.3", "level", 7); } if (qtest_has_machine("pc-i440fx-2.9")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", - "-machine pc-i440fx-2.9 -cpu Conroe", + "Conroe", NULL, "pc-i440fx-2.9", "level", 10); add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/on", - "-machine pc-i440fx-2.9 -cpu Conroe,erms=on", + "Conroe", "erms=on", "pc-i440fx-2.9", "level", 10); } @@ -349,42 +386,43 @@ int main(int argc, char **argv) */ if (qtest_has_machine("pc-i440fx-2.3")) { add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", - "-machine pc-i440fx-2.3 -cpu SandyBridge", + "SandyBridge", NULL, "pc-i440fx-2.3", "xlevel", 0x8000000a); } if (qtest_has_machine("pc-i440fx-2.4")) { add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", - "-machine pc-i440fx-2.4 -cpu SandyBridge,", + "SandyBridge", NULL, "pc-i440fx-2.4", "xlevel", 0x80000008); add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", - "-machine pc-i440fx-2.4 -cpu SandyBridge,svm=on,npt=on", + "SandyBridge", "svm=on,npt=on", "pc-i440fx-2.4", "xlevel", 0x80000008); } /* Test feature parsing */ add_feature_test("x86/cpuid/features/plus", - "-cpu 486,+arat", + "486", "+arat", 6, 0, "EAX", 2, true); add_feature_test("x86/cpuid/features/minus", - "-cpu pentium,-mmx", + "pentium", "-mmx", 1, 0, "EDX", 23, false); add_feature_test("x86/cpuid/features/on", - "-cpu 486,arat=on", + "486", "arat=on", 6, 0, "EAX", 2, true); add_feature_test("x86/cpuid/features/off", - "-cpu pentium,mmx=off", + "pentium", "mmx=off", 1, 0, "EDX", 23, false); + add_feature_test("x86/cpuid/features/max-plus-invtsc", - "-cpu max,+invtsc", + "max" , "+invtsc", 0x80000007, 0, "EDX", 8, true); add_feature_test("x86/cpuid/features/max-invtsc-on", - "-cpu max,invtsc=on", + "max", "invtsc=on", 0x80000007, 0, "EDX", 8, true); add_feature_test("x86/cpuid/features/max-minus-mmx", - "-cpu max,-mmx", + "max", "-mmx", 1, 0, "EDX", 23, false); add_feature_test("x86/cpuid/features/max-invtsc-on,mmx=off", - "-cpu max,mmx=off", + "max", "mmx=off", 1, 0, "EDX", 23, false); return g_test_run(); diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c index 3678646df5..85ad4eed85 100644 --- a/tests/qtest/tmp105-test.c +++ b/tests/qtest/tmp105-test.c @@ -100,9 +100,9 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0x14f0); i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x1234); - g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1234); + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1230); i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231); - g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4231); + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230); } static void tmp105_register_nodes(void) diff --git a/tests/qtest/tpm-crb-swtpm-test.c b/tests/qtest/tpm-crb-swtpm-test.c index 40254f762f..ffeb1c396b 100644 --- a/tests/qtest/tpm-crb-swtpm-test.c +++ b/tests/qtest/tpm-crb-swtpm-test.c @@ -19,9 +19,6 @@ #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; 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..197714f8d9 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. @@ -114,7 +114,7 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path, sizeof(tpm_pcrread_resp)); tpm_util_migrate(src_qemu, uri); - tpm_util_wait_for_migration_complete(src_qemu); + tpm_util_wait_for_migration_complete(dst_qemu); tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp, sizeof(tpm_pcrread_resp)); 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 8c067fddd4..517a077005 100644 --- a/tests/qtest/tpm-tis-device-swtpm-test.c +++ b/tests/qtest/tpm-tis-device-swtpm-test.c @@ -18,6 +18,7 @@ #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; @@ -33,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); } @@ -42,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); } 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 11539c0a52..105e42e21d 100644 --- a/tests/qtest/tpm-tis-swtpm-test.c +++ b/tests/qtest/tpm-tis-swtpm-test.c @@ -17,6 +17,7 @@ #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; @@ -31,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); } @@ -40,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) 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 a7efe2d0d2..1c0319e6e7 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -51,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]; diff --git a/tests/qtest/tpm-util.h b/tests/qtest/tpm-util.h index 80720afac0..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); diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c new file mode 100644 index 0000000000..60199abbee --- /dev/null +++ b/tests/qtest/ufs-test.c @@ -0,0 +1,984 @@ +/* + * 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 "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, + uint8_t selector, uint32_t attr_value, + 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, + * 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; + req_upiu.qr.selector = selector; + req_upiu.qr.value = attr_value; + req_upiu.qr.length = UFS_QUERY_DESC_MAX_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)); +} + +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, 0, 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, 0, 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 ufstest_query_flag_request(void *obj, void *data, + QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + + UtpTransferReqDesc utrd; + UtpUpiuRsp rsp_upiu; + ufs_init(ufs, alloc); + + /* Read read-only flag */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_FLAG); + g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_FLAG_IDN_FDEVICEINIT); + g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); + + /* Flag Set, Clear, Toggle Test with fDeviceLifeSpanModeEn */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); + + /* Read Write-only Flag (Intended Failure) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_READABLE); + + /* Write Read-Only Flag (Intended Failure) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, UFS_QUERY_FLAG_IDN_BUSY_RTC, + 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ufs_exit(ufs, alloc); +} + +static void ufstest_query_attr_request(void *obj, void *data, + QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + + UtpTransferReqDesc utrd; + UtpUpiuRsp rsp_upiu; + ufs_init(ufs, alloc); + + /* Read Readable Attributes*/ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_ATTR); + g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_ATTR_IDN_BOOT_LU_EN); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + /* Write Writable Attributes & Read Again */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07)); + + /* Write Invalid Value (Intended Error) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_VALUE); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); + + /* Read Write-Only Attribute (Intended Error) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_READABLE); + + /* Write Read-Only Attribute (Intended Error) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_NOT_WRITEABLE); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + /* Reset Written Attributes */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, + &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ufs_exit(ufs, alloc); +} + +static void ufstest_query_desc_request(void *obj, void *data, + QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + + UtpTransferReqDesc utrd; + UtpUpiuRsp rsp_upiu; + ufs_init(ufs, alloc); + + /* Write Descriptor is not supported yet */ + + /* Read Device Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_DEVICE, + 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_DESC); + g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_DESC_IDN_DEVICE); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(DeviceDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_DEVICE); + + /* Read Configuration Descriptor is not supported yet*/ + + /* Read Unit Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 0, + 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); + g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 0); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 1, + 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); + g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 1); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, + UFS_UPIU_RPMB_WLUN, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(RpmbUnitDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); + g_assert_cmpuint(rsp_upiu.qr.data[2], ==, UFS_UPIU_RPMB_WLUN); + + /* Read Interconnect Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(InterconnectDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_INTERCONNECT); + + /* Read String Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, + 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x12); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, + 1, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x22); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, + 4, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x0a); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); + + /* Read Geometry Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_GEOMETRY, + 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(GeometryDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_GEOMETRY); + + /* Read Power Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_POWER, 0, + 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, + sizeof(PowerParametersDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_POWER); + + /* Read Health Descriptor */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_HEALTH, + 0, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(DeviceHealthDescriptor)); + g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_HEALTH); + + /* Invalid Index (Intended Failure) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 4, + 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_INDEX); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, + 5, 0, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_INDEX); + + /* Invalid Selector (Intended Failure) */ + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_DEVICE, + 0, 1, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_SELECTOR); + + ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, + 0, 1, 0, &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, + UFS_OCS_INVALID_CMD_TABLE_ATTR); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_QUERY_RESULT_INVALID_SELECTOR); + + 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); + qos_add_test("flag read-write", "ufs", + ufstest_query_flag_request, &io_test_opts); + qos_add_test("attr read-write", "ufs", + ufstest_query_attr_request, &io_test_opts); + qos_add_test("desc read-write", "ufs", + ufstest_query_desc_request, &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 07a4c2d500..ea90d41232 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -906,7 +906,7 @@ static void start_vhost_user_blk(GString *cmd_line, int vus_instances, vhost_user_blk_bin); g_string_append_printf(cmd_line, - " -object memory-backend-memfd,id=mem,size=256M,share=on " + " -object memory-backend-shm,id=mem,size=256M " " -M memory-backend=mem -m 256M "); for (i = 0; i < vus_instances; i++) { @@ -961,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) @@ -983,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 bf9f7c4248..8948fb81ef 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -33,6 +33,7 @@ #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 @@ -43,6 +44,8 @@ "mem-path=%s,share=on -numa node,memdev=mem" #define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ " -numa node,memdev=mem" +#define QEMU_CMD_SHM " -m %d -object memory-backend-shm,id=mem,size=%dM," \ + " -numa node,memdev=mem" #define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" #define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce=on" @@ -145,6 +148,7 @@ enum { enum { VHOST_USER_NET, VHOST_USER_GPIO, + VHOST_USER_SCMI, }; typedef struct TestServer { @@ -193,6 +197,7 @@ enum test_memfd { TEST_MEMFD_AUTO, TEST_MEMFD_YES, TEST_MEMFD_NO, + TEST_MEMFD_SHM, }; static void append_vhost_net_opts(TestServer *s, GString *cmd_line, @@ -226,6 +231,8 @@ static void append_mem_opts(TestServer *server, GString *cmd_line, if (memfd == TEST_MEMFD_YES) { g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); + } else if (memfd == TEST_MEMFD_SHM) { + g_string_append_printf(cmd_line, QEMU_CMD_SHM, size, size); } else { const char *root = init_hugepagefs() ? : server->tmpfs; @@ -281,7 +288,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; } @@ -351,7 +358,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) if (size != msg.size) { qos_printf("%s: Wrong message size received %d != %d\n", __func__, size, msg.size); - return; + goto out; } } @@ -456,7 +463,10 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_VRING_KICK: case VHOST_USER_SET_VRING_CALL: /* consume the fd */ - qemu_chr_fe_get_msgfds(chr, &fd, 1); + if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) { + qos_printf("call fd: %d, do not set non-blocking\n", fd); + break; + } /* * This is a non-blocking eventfd. * The receive function forces it to be blocking, @@ -509,6 +519,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) break; } +out: g_mutex_unlock(&s->data_mutex); } @@ -785,6 +796,19 @@ static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) return server; } +static void *vhost_user_test_setup_shm(GString *cmd_line, void *arg) +{ + TestServer *server = test_server_new("vhost-user-test", arg); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_SHM); + server->vu_ops->append_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) { TestServer *server = arg; @@ -896,7 +920,7 @@ static void wait_for_rings_started(TestServer *s, size_t count) static inline void test_server_connect(TestServer *server) { - test_server_create_chr(server, ",reconnect=1"); + test_server_create_chr(server, ",reconnect-ms=1000"); } static gboolean @@ -925,7 +949,7 @@ static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) { TestServer *s = test_server_new("reconnect", arg); - g_thread_new("connect", connect_thread, s); + g_thread_unref(g_thread_new("connect", connect_thread, s)); append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); s->vu_ops->append_opts(s, cmd_line, ",server=on"); @@ -962,7 +986,7 @@ static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) s->test_fail = true; - g_thread_new("connect", connect_thread, s); + g_thread_unref(g_thread_new("connect", connect_thread, s)); append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); s->vu_ops->append_opts(s, cmd_line, ",server=on"); @@ -977,7 +1001,7 @@ static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) s->test_flags = TEST_FLAGS_DISCONNECT; - g_thread_new("connect", connect_thread, s); + g_thread_unref(g_thread_new("connect", connect_thread, s)); append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); s->vu_ops->append_opts(s, cmd_line, ",server=on"); @@ -1078,6 +1102,11 @@ static void register_vhost_user_test(void) "virtio-net", test_read_guest_mem, &opts); + opts.before = vhost_user_test_setup_shm; + qos_add_test("vhost-user/read-guest-mem/shm", + "virtio-net", + test_read_guest_mem, &opts); + if (qemu_memfd_check(MFD_ALLOW_SEALING)) { opts.before = vhost_user_test_setup_memfd; qos_add_test("vhost-user/read-guest-mem/memfd", @@ -1156,3 +1185,45 @@ static void register_vhost_gpio_test(void) "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 65e69491e5..ab3a12c816 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -693,9 +693,64 @@ static void fs_unlinkat_hardlink(void *obj, void *data, g_assert(stat(real_file, &st_real) == 0); } +static void fs_use_after_unlink(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + v9fs_set_allocator(t_alloc); + static const uint32_t write_count = P9_MAX_SIZE / 2; + g_autofree char *real_file = virtio_9p_test_path("09/doa_file"); + g_autofree char *buf = g_malloc0(write_count); + struct stat st_file; + struct v9fs_attr attr; + uint32_t fid_file; + uint32_t count; + + tattach({ .client = v9p }); + + /* create a file "09/doa_file" and make sure it exists and is regular */ + tmkdir({ .client = v9p, .atPath = "/", .name = "09" }); + tlcreate({ .client = v9p, .atPath = "09", .name = "doa_file" }); + g_assert(stat(real_file, &st_file) == 0); + g_assert((st_file.st_mode & S_IFMT) == S_IFREG); + + /* request a FID for that regular file that we can work with next */ + fid_file = twalk({ + .client = v9p, .fid = 0, .path = "09/doa_file" + }).newfid; + g_assert(fid_file != 0); + + /* now first open the file in write mode before ... */ + tlopen({ .client = v9p, .fid = fid_file, .flags = O_WRONLY }); + /* ... removing the file from file system */ + tunlinkat({ .client = v9p, .atPath = "09", .name = "doa_file" }); + + /* file is removed, but we still have it open, so this should succeed */ + tgetattr({ + .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC, + .rgetattr.attr = &attr + }); + count = twrite({ + .client = v9p, .fid = fid_file, .offset = 0, .count = write_count, + .data = buf + }).count; + g_assert_cmpint(count, ==, write_count); +} + +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; } @@ -735,15 +790,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); @@ -756,18 +802,8 @@ static void register_virtio_9p_test(void) qos_add_test("local/hardlink_file", "virtio-9p", fs_hardlink_file, &opts); qos_add_test("local/unlinkat_hardlink", "virtio-9p", fs_unlinkat_hardlink, &opts); + qos_add_test("local/use_after_unlink", "virtio-9p", fs_use_after_unlink, + &opts); } 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-balloon-test.c b/tests/qtest/virtio-balloon-test.c new file mode 100644 index 0000000000..ecdd363b06 --- /dev/null +++ b/tests/qtest/virtio-balloon-test.c @@ -0,0 +1,57 @@ +/* + * QTest test cases for virtio balloon device + * + * Copyright (c) 2024 Gao Shiyuan + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_balloon.h" + +/* + * https://gitlab.com/qemu-project/qemu/-/issues/2576 + * Used to trigger: + * virtio_address_space_lookup: Assertion `mrs.mr' failed. + */ +static void oss_fuzz_71649(void) +{ + QTestState *s = qtest_init("-device virtio-balloon -machine q35" + " -nodefaults"); + + qtest_outl(s, 0xcf8, 0x80000890); + qtest_outl(s, 0xcfc, 0x2); + qtest_outl(s, 0xcf8, 0x80000891); + qtest_inl(s, 0xcfc); + qtest_quit(s); +} + +static void query_stats(void) +{ + QTestState *s = qtest_init("-device virtio-balloon,id=balloon" + " -nodefaults"); + QDict *ret = qtest_qmp_assert_success_ref( + s, + "{ 'execute': 'qom-get', 'arguments': " \ + "{ 'path': '/machine/peripheral/balloon', " \ + " 'property': 'guest-stats' } }"); + QDict *stats = qdict_get_qdict(ret, "stats"); + + /* We expect 1 entry in the dict for each known kernel stat */ + assert(qdict_size(stats) == VIRTIO_BALLOON_S_NR); + + qobject_unref(ret); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("virtio-balloon/oss_fuzz_71649", oss_fuzz_71649); + qtest_add_func("virtio-balloon/query-stats", query_stats); + + return g_test_run(); +} + diff --git a/tests/qtest/virtio-blk-test.c b/tests/qtest/virtio-blk-test.c index 19c01f808b..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 @@ -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 4a809590bf..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': ''," @@ -638,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"'}"); @@ -736,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 @@ -769,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"'}"); @@ -787,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"); @@ -834,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"'}"); @@ -888,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 @@ -902,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"'}"); @@ -920,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); @@ -964,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"'}"); @@ -1026,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 @@ -1040,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"'}"); @@ -1058,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); @@ -1102,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"'}"); @@ -1202,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"'}"); @@ -1291,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"'}"); @@ -1390,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"'}"); @@ -1482,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"'}"); @@ -1578,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"'}"); @@ -1609,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"'}"); @@ -1728,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 @@ -1748,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"'}"); @@ -1772,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"'}"); @@ -1794,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"); diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index dff43f0f60..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"); } @@ -212,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', " diff --git a/tests/qtest/virtio-scsi-test.c b/tests/qtest/virtio-scsi-test.c index ceaa7f2415..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; 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 index e2a9d682bb..f8933b0761 100644 --- a/tests/qtest/vnc-display-test.c +++ b/tests/qtest/vnc-display-test.c @@ -19,6 +19,8 @@ typedef struct Test { GMainLoop *loop; } Test; +#if !defined(CONFIG_DARWIN) + static void on_vnc_error(VncConnection* self, const char* msg) { @@ -31,16 +33,18 @@ static void on_vnc_auth_failure(VncConnection *self, g_error("vnc-auth-failure: %s", msg); } +#endif + static bool test_setup(Test *test) { -#ifdef WIN32 - g_test_skip("Not supported on Windows yet"); +#if defined(CONFIG_DARWIN) + g_test_skip("Broken on Darwin"); return false; #else int pair[2]; - test->qts = qtest_init("-vnc none -name vnc-test"); + test->qts = qtest_init("-M none -vnc none -name vnc-test"); g_assert_cmpint(qemu_socketpair(AF_UNIX, SOCK_STREAM, 0, pair), ==, 0); @@ -52,7 +56,12 @@ test_setup(Test *test) 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; 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 14bc013181..95ff76ea44 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -49,15 +49,15 @@ 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 detected" && echo "$(strip $2)=$(strip $1)" >&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, \ +run-test = $(call quiet-command, timeout -s KILL --foreground $(TIMEOUT) $2 > $1.out, \ TEST,$(or $3, $*, $<) on $(TARGET_NAME)) else -run-test = $(call quiet-command, timeout --foreground $(TIMEOUT) $2, \ +run-test = $(call quiet-command, timeout -s KILL --foreground $(TIMEOUT) $2, \ TEST,$(or $3, $*, $<) on $(TARGET_NAME)) endif @@ -90,24 +90,27 @@ CFLAGS= LDFLAGS= QEMU_OPTS= +CHECK_PLUGIN_OUTPUT_COMMAND= # 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 # then the target. If there are common tests shared between # sub-targets (e.g. ARM & AArch64) then it is up to # $(TARGET_NAME)/Makefile.target to include the common parent -# architecture in its VPATH. +# architecture in its VPATH. However some targets are so minimal we +# can't even build the multiarch tests. +ifneq ($(filter $(TARGET_NAME),aarch64_be),) +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.target +else -include $(SRC_PATH)/tests/tcg/multiarch/Makefile.target -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.target +endif # Add the common build options CFLAGS+=-Wall -Werror -O0 -g -fno-strict-aliasing @@ -118,12 +121,12 @@ endif %: %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) %: %.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wa,--noexecstack $< -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 +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 @@ -145,35 +148,48 @@ RUN_TESTS=$(patsubst %,run-%, $(TESTS)) # If plugins exist also include those in the tests ifeq ($(CONFIG_PLUGIN),y) -PLUGIN_SRC=$(SRC_PATH)/tests/plugin -PLUGIN_LIB=../../plugin +PLUGIN_SRC=$(SRC_PATH)/tests/tcg/plugins +PLUGIN_LIB=../plugins VPATH+=$(PLUGIN_LIB) 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. +# In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable. +ifneq ($(MULTIARCH_TESTS),) $(foreach p,$(PLUGINS), \ - $(foreach t,$(TESTS),\ + $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_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) $<) + $(call run-test, $<, env QEMU=$(QEMU) $(QEMU) $(QEMU_OPTS) $<) run-plugin-%: - $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ - -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \ + $(call run-test, $@, env QEMU=$(QEMU) $(QEMU) $(QEMU_OPTS) \ + -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \ -d plugin -D $*.pout \ $(call strip-plugin,$<)) + $(if $(CHECK_PLUGIN_OUTPUT_COMMAND), \ + $(call quiet-command, $(CHECK_PLUGIN_OUTPUT_COMMAND) $*.pout, \ + TEST, check plugin $(call extract-plugin,$@) output \ + with $(call strip-plugin,$<))) else run-%: % $(call run-test, $<, \ @@ -185,9 +201,13 @@ 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,$<)) + $(if $(CHECK_PLUGIN_OUTPUT_COMMAND), \ + $(call quiet-command, $(CHECK_PLUGIN_OUTPUT_COMMAND) $*.pout, \ + TEST, check plugin $(call extract-plugin,$@) output \ + with $(call strip-plugin,$<))) endif gdb-%: % @@ -201,3 +221,10 @@ clean: 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/README b/tests/tcg/README index 706bb185b4..6d08ca50dc 100644 --- a/tests/tcg/README +++ b/tests/tcg/README @@ -1,9 +1,14 @@ -This directory contains various interesting guest programs for -regression testing. Tests are either multi-arch, meaning they can be -built for all guest architectures that support linux-user executable, -or they are architecture specific. +This directory contains various interesting guest binaries for +regression testing the Tiny Code Generator doing system and user-mode +emulation. -CRIS -==== -The testsuite for CRIS is in tests/tcg/cris. You can run it -with "make test-cris". +The multiarch directory contains shared code for tests that can be +built for all guest architectures. Architecture specific code can be +found in their respective directories. + +System mode tests will be under the "system" subdirectories. + +GDB scripts for exercising the gdbstub on specific tests will be found +under the "gdbstb" subdirectories. + +See the developer guide for more instructions on "make check-tcg" diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index a1368905f5..d08d9b01de 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -2,14 +2,22 @@ # Aarch64 system tests # -AARCH64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/aarch64/system +AARCH64_SRC=$(SRC_PATH)/tests/tcg/aarch64 +AARCH64_SYSTEM_SRC=$(AARCH64_SRC)/system + VPATH+=$(AARCH64_SYSTEM_SRC) # These objects provide the basic boot code and helper functions for all tests CRT_OBJS=boot.o -AARCH64_TEST_SRCS=$(wildcard $(AARCH64_SYSTEM_SRC)/*.c) -AARCH64_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.c, %, $(AARCH64_TEST_SRCS)) +AARCH64_TEST_C_SRCS=$(wildcard $(AARCH64_SYSTEM_SRC)/*.c) +AARCH64_TEST_S_SRCS=$(AARCH64_SYSTEM_SRC)/mte.S + +AARCH64_C_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.c, %, $(AARCH64_TEST_C_SRCS)) +AARCH64_S_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.S, %, $(AARCH64_TEST_S_SRCS)) + +AARCH64_TESTS = $(AARCH64_C_TESTS) +AARCH64_TESTS += $(AARCH64_S_TESTS) CRT_PATH=$(AARCH64_SYSTEM_SRC) LINK_SCRIPT=$(AARCH64_SYSTEM_SRC)/kernel.ld @@ -21,14 +29,15 @@ 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 + $(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3); \ + $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE)) 3> config-cc.mak -include config-cc.mak # building head blobs .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) @@ -36,18 +45,31 @@ config-cc.mak: Makefile memory: CFLAGS+=-DCHECK_UNALIGNED=1 +memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3 + +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 @@ -68,12 +90,42 @@ run-memory-replay: memory-replay run-memory-record EXTRA_RUNS+=run-memory-replay ifneq ($(CROSS_CC_HAS_ARMV8_3),) -pauth-3: CFLAGS += -march=armv8.3-a +pauth-3: CFLAGS += $(CROSS_CC_HAS_ARMV8_3) else pauth-3: $(call skip-test, "BUILD of $@", "missing compiler support") run-pauth-3: $(call skip-test, "RUN of pauth-3", "not built") -run-plugin-pauth-3-with-%: - $(call skip-test, "RUN of pauth-3 ($*)", "not built") +endif + +ifneq ($(CROSS_CC_HAS_ARMV8_MTE),) +QEMU_MTE_ENABLED_MACHINE=-M virt,mte=on -cpu max -display none +QEMU_OPTS_WITH_MTE_ON = $(QEMU_MTE_ENABLED_MACHINE) $(QEMU_BASE_ARGS) -kernel +mte: CFLAGS+=-march=armv8.5-a+memtag +mte: mte.S $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-mte: QEMU_OPTS=$(QEMU_OPTS_WITH_MTE_ON) +run-mte: mte + +ifeq ($(GDB_SUPPORTS_MTE_IN_BAREMETAL),y) +run-gdbstub-mte: QEMU_OPTS=$(QEMU_OPTS_WITH_MTE_ON) +run-gdbstub-mte: mte + $(call run-test, $@, $(GDB_SCRIPT) \ + --output run-gdbstub-mte.out \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "-chardev null$(COMMA)id=output $(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py -- --mode=system, \ + gdbstub MTE support) + +EXTRA_RUNS += run-gdbstub-mte +else # !GDB_SUPPORTS_MTE_IN_BAREMETAL +run-gdbstub-mte: + $(call skip-test "RUN of gdbstub-mte", "GDB does not support MTE in baremetal!") +endif +else # !CROSS_CC_HAS_ARMV8_MTE +mte: + $(call skip-test, "BUILD of $@", "missing compiler support") +run-mte: + $(call skip-test, "RUN of mte", "not build") endif diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index fc8d90ed69..9efe2f81ad 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -9,36 +9,54 @@ 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-@)( \ + fnia=`$(call cc-test,-fno-integrated-as) && echo -fno-integrated-as`; \ $(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)) 3> config-cc.mak + $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE); \ + $(call cc-option,-Wa$(COMMA)-march=armv9-a+sme $$fnia, CROSS_AS_HAS_ARMV9_SME)) 3> config-cc.mak -include config-cc.mak +ifneq ($(CROSS_CC_HAS_ARMV8_2),) +AARCH64_TESTS += dcpop +dcpop: CFLAGS += $(CROSS_CC_HAS_ARMV8_2) +endif +ifneq ($(CROSS_CC_HAS_ARMV8_5),) +AARCH64_TESTS += dcpodp +dcpodp: CFLAGS += $(CROSS_CC_HAS_ARMV8_5) +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 +AARCH64_TESTS += pauth-1 pauth-2 pauth-4 pauth-5 test-2375 +pauth-%: CFLAGS += $(CROSS_CC_HAS_ARMV8_3) +test-2375: CFLAGS += -march=armv8.3-a +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 $(CROSS_CC_HAS_ARMV8_BTI) bti-1 bti-3: LDFLAGS += -nostdlib endif # bti-2 tests PROT_BTI, so no special compiler support required. @@ -46,18 +64,23 @@ AARCH64_TESTS += bti-2 # MTE Tests ifneq ($(CROSS_CC_HAS_ARMV8_MTE),) -AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 -mte-%: CFLAGS += -march=armv8.5-a+memtag +AARCH64_TESTS += mte-1 mte-2 mte-3 mte-4 mte-5 mte-6 mte-7 mte-8 +mte-%: CFLAGS += $(CROSS_CC_HAS_ARMV8_MTE) +endif + +# SME Tests +ifneq ($(CROSS_AS_HAS_ARMV9_SME),) +SME_TESTS = sme-outprod1 sme-smopa-1 sme-smopa-2 sme-fmopa-1 sme-fmopa-2 sme-fmopa-3 +AARCH64_TESTS += $(SME_TESTS) +$(SME_TESTS): CFLAGS += $(CROSS_AS_HAS_ARMV9_SME) endif -ifneq ($(CROSS_CC_HAS_SVE),) # System Registers Tests AARCH64_TESTS += sysregs -sysregs: CFLAGS+=-march=armv8.1-a+sve -# SVE ioctl test -AARCH64_TESTS += sve-ioctls -sve-ioctls: CFLAGS+=-march=armv8.1-a+sve +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 @@ -76,30 +99,58 @@ 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 += $(CROSS_CC_HAS_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) 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) EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls + +ifeq ($(GDB_HAS_MTE),y) +run-gdbstub-mte: mte-8 + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py \ + -- --mode=user, \ + gdbstub MTE support) + +EXTRA_RUNS += run-gdbstub-mte +endif + endif endif ifneq ($(CROSS_CC_HAS_SVE2),) AARCH64_TESTS += test-826 -test-826: CFLAGS+=-march=armv8.1-a+sve2 +test-826: CFLAGS += $(CROSS_CC_HAS_SVE2) endif TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/bti-1.c b/tests/tcg/aarch64/bti-1.c index 61924f0d7a..1fada8108d 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) { @@ -17,15 +17,15 @@ static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) #define BTI_JC "hint #38" #define BTYPE_1(DEST) \ - asm("mov %0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; br x16; 1: " DEST "; mov %w0,#0" \ : "=r"(skipped) : : "x16") #define BTYPE_2(DEST) \ - asm("mov %0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; blr x16; 1: " DEST "; mov %w0,#0" \ : "=r"(skipped) : : "x16", "x30") #define BTYPE_3(DEST) \ - asm("mov %0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %0,#0" \ + asm("mov %w0,#1; adr x15, 1f; br x15; 1: " DEST "; mov %w0,#0" \ : "=r"(skipped) : : "x15") #define TEST(WHICH, DEST, EXPECT) \ diff --git a/tests/tcg/aarch64/bti-3.c b/tests/tcg/aarch64/bti-3.c index a852856d9a..6a3bd037bc 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) { @@ -11,15 +11,15 @@ static void skip2_sigill(int sig, siginfo_t *info, ucontext_t *uc) } #define BTYPE_1() \ - asm("mov %0,#1; adr x16, 1f; br x16; 1: hint #25; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; br x16; 1: hint #25; mov %w0,#0" \ : "=r"(skipped) : : "x16", "x30") #define BTYPE_2() \ - asm("mov %0,#1; adr x16, 1f; blr x16; 1: hint #25; mov %0,#0" \ + asm("mov %w0,#1; adr x16, 1f; blr x16; 1: hint #25; mov %w0,#0" \ : "=r"(skipped) : : "x16", "x30") #define BTYPE_3() \ - asm("mov %0,#1; adr x15, 1f; br x15; 1: hint #25; mov %0,#0" \ + asm("mov %w0,#1; adr x15, 1f; br x15; 1: hint #25; mov %w0,#0" \ : "=r"(skipped) : : "x15", "x30") #define TEST(WHICH, EXPECT) \ 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/fcvt.ref b/tests/tcg/aarch64/fcvt.ref index e7af24dc58..2726b41063 100644 --- a/tests/tcg/aarch64/fcvt.ref +++ b/tests/tcg/aarch64/fcvt.ref @@ -211,45 +211,45 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -257,41 +257,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -574,87 +574,87 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -937,45 +937,45 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -983,41 +983,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1300,45 +1300,45 @@ Converting double-precision to half-precision 40 HALF: 0x7f00 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1346,41 +1346,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1845,45 +1845,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1891,41 +1891,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2208,87 +2208,87 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2571,45 +2571,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2617,41 +2617,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2934,45 +2934,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2980,41 +2980,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py b/tests/tcg/aarch64/gdbstub/test-mte.py new file mode 100644 index 0000000000..9ad98e7a54 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-mte.py @@ -0,0 +1,111 @@ +from __future__ import print_function +# +# Test GDB memory-tag commands that exercise the stubs for the qIsAddressTagged, +# qMemTag, and QMemTag packets, which are used for manipulating allocation tags. +# Logical tags-related commands rely on local operations, hence don't exercise +# any stub and so are not used in this test. +# +# The test consists in breaking just after a tag is set in a specific memory +# chunk, and then using the GDB 'memory-tagging' subcommands to set/get tags in +# different memory locations and ranges in the MTE-enabled memory chunk. +# +# This is launched via tests/guest-debug/run-test.py +# + + +try: + import gdb +except ModuleNotFoundError: + from sys import exit + exit("This script must be launched via tests/guest-debug/run-test.py!") +import re +from sys import argv +from test_gdbstub import arg_parser, main, report + + +PATTERN_0 = r"Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)." +PATTERN_1 = r".*(0x[0-9a-f]+)" + + +def run_test(): + p = arg_parser(prog="test-mte.py", description="TCG MTE tests.") + p.add_argument("--mode", help="Run test for QEMU system or user mode.", + required=True, choices=['system','user']) + + args = p.parse_args(args=argv) + + if args.mode == "system": + # Break address: where to break before performing the tests + # See mte.S for details about this label. + ba = "main_end" + # Tagged address: the start of the MTE-enabled memory chunk to be tested + # 'tagged_addr' (x1) is a pointer to the MTE-enabled page. See mte.S. + ta = "$x1" + else: # mode="user" + # Line 95 in mte-8.c + ba = "95" + # 'a' array. See mte-8.c + ta = "a" + + gdb.execute(f"break {ba}", False, True) + gdb.execute("continue", False, True) + + try: + # Test if we can check correctly that the allocation tag for the address + # in {ta} matches the logical tag in {ta}. + co = gdb.execute(f"memory-tag check {ta}", False, True) + tags_match = re.findall(PATTERN_0, co, re.MULTILINE) + if tags_match: + report(True, f"{tags_match[0]}") + else: + report(False, "Logical and allocation tags don't match!") + + # Test allocation tag 'set and print' commands. Commands on logical + # tags rely on local operation and so don't exercise any stub. + + # Set the allocation tag for the first granule (16 bytes) of + # address starting at {ta} address to a known value, i.e. 0x04. + gdb.execute(f"memory-tag set-allocation-tag {ta} 1 04", False, True) + + # Then set the allocation tag for the second granule to a known + # value, i.e. 0x06. This tests that contiguous tag granules are + # set correctly and don't run over each other. + gdb.execute(f"memory-tag set-allocation-tag {ta}+16 1 06", False, True) + + # Read the known values back and check if they remain the same. + + co = gdb.execute(f"memory-tag print-allocation-tag {ta}", False, True) + first_tag = re.match(PATTERN_1, co)[1] + + co = gdb.execute(f"memory-tag print-allocation-tag {ta}+16", False, True) + second_tag = re.match(PATTERN_1, co)[1] + + if first_tag == "0x4" and second_tag == "0x6": + report(True, "Allocation tags are correctly set/printed.") + else: + report(False, "Can't set/print allocation tags!") + + # Now test fill pattern by setting a whole page with a pattern. + gdb.execute(f"memory-tag set-allocation-tag {ta} 4096 0a0b", False, True) + + # And read back the tags of the last two granules in page so + # we also test if the pattern is set correctly up to the end of + # the page. + co = gdb.execute(f"memory-tag print-allocation-tag {ta}+4096-32", False, True) + tag = re.match(PATTERN_1, co)[1] + + co = gdb.execute(f"memory-tag print-allocation-tag {ta}+4096-16", False, True) + last_tag = re.match(PATTERN_1, co)[1] + + if tag == "0xa" and last_tag == "0xb": + report(True, "Fill pattern is ok.") + else: + report(False, "Fill pattern failed!") + + except gdb.error: + # This usually happens because a GDB version that does not support + # memory tagging was used to run the test. + report(False, "'memory-tag' command failed!") + + +main(run_test, expected_arch="aarch64") 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-1.c b/tests/tcg/aarch64/mte-1.c index 88dcd617ad..146cad4a04 100644 --- a/tests/tcg/aarch64/mte-1.c +++ b/tests/tcg/aarch64/mte-1.c @@ -15,7 +15,7 @@ int main(int ac, char **av) enable_mte(PR_MTE_TCF_NONE); p0 = alloc_mte_mem(sizeof(*p0)); - asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(1)); + asm("irg %0,%1,%2" : "=r"(p1) : "r"(p0), "r"(1l)); assert(p1 != p0); asm("subp %0,%1,%2" : "=r"(c) : "r"(p0), "r"(p1)); assert(c == 0); 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/mte-8.c b/tests/tcg/aarch64/mte-8.c new file mode 100644 index 0000000000..808135ba43 --- /dev/null +++ b/tests/tcg/aarch64/mte-8.c @@ -0,0 +1,99 @@ +/* + * To be compiled with -march=armv8.5-a+memtag + * + * This test is adapted from a Linux test. Please see: + * + * https://www.kernel.org/doc/html/next/arch/arm64/memory-tagging-extension.html#example-of-correct-usage + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * From arch/arm64/include/uapi/asm/hwcap.h + */ +#define HWCAP2_MTE (1 << 18) + +/* + * From arch/arm64/include/uapi/asm/mman.h + */ +#define PROT_MTE 0x20 + +/* + * Insert a random logical tag into the given pointer. + */ +#define insert_random_tag(ptr) ({ \ + uint64_t __val; \ + asm("irg %0, %1" : "=r" (__val) : "r" (ptr)); \ + __val; \ +}) + +/* + * Set the allocation tag on the destination address. + */ +#define set_tag(tagged_addr) do { \ + asm volatile("stg %0, [%0]" : : "r" (tagged_addr) : "memory"); \ +} while (0) + + +int main(int argc, char *argv[]) +{ + unsigned char *a; + unsigned long page_sz = sysconf(_SC_PAGESIZE); + unsigned long hwcap2 = getauxval(AT_HWCAP2); + + /* check if MTE is present */ + if (!(hwcap2 & HWCAP2_MTE)) { + return EXIT_FAILURE; + } + + /* + * Enable the tagged address ABI, synchronous or asynchronous MTE + * tag check faults (based on per-CPU preference) and allow all + * non-zero tags in the randomly generated set. + */ + if (prctl(PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC | + (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) { + perror("prctl() failed"); + return EXIT_FAILURE; + } + + a = mmap(0, page_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (a == MAP_FAILED) { + perror("mmap() failed"); + return EXIT_FAILURE; + } + + printf("a[] address is %p\n", a); + + /* + * Enable MTE on the above anonymous mmap. The flag could be passed + * directly to mmap() and skip this step. + */ + if (mprotect(a, page_sz, PROT_READ | PROT_WRITE | PROT_MTE)) { + perror("mprotect() failed"); + return EXIT_FAILURE; + } + + /* access with the default tag (0) */ + a[0] = 1; + a[1] = 2; + + printf("a[0] = %hhu a[1] = %hhu\n", a[0], a[1]); + + /* set the logical and allocation tags */ + a = (unsigned char *)insert_random_tag(a); + set_tag(a); + + printf("%p\n", a); + + return 0; +} 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-fmopa-1.c b/tests/tcg/aarch64/sme-fmopa-1.c new file mode 100644 index 0000000000..652c4ea090 --- /dev/null +++ b/tests/tcg/aarch64/sme-fmopa-1.c @@ -0,0 +1,63 @@ +/* + * SME outer product, 1 x 1. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +static void foo(float *dst) +{ + asm(".arch_extension sme\n\t" + "smstart\n\t" + "ptrue p0.s, vl4\n\t" + "fmov z0.s, #1.0\n\t" + /* + * 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\t" + "fmopa za1.s, p0/m, p0/m, z0.s, z0.s\n\t" + /* + * Read the first 4x4 sub-matrix of elements from tile 1: + * Note that za1h should be interchangeable here. + */ + "mov w12, #0\n\t" + "mova z0.s, p0/m, za1v.s[w12, #0]\n\t" + "mova z1.s, p0/m, za1v.s[w12, #1]\n\t" + "mova z2.s, p0/m, za1v.s[w12, #2]\n\t" + "mova z3.s, p0/m, za1v.s[w12, #3]\n\t" + /* + * And store them to the input pointer (dst in the C code): + */ + "st1w {z0.s}, p0, [%0]\n\t" + "add x0, x0, #16\n\t" + "st1w {z1.s}, p0, [x0]\n\t" + "add x0, x0, #16\n\t" + "st1w {z2.s}, p0, [x0]\n\t" + "add x0, x0, #16\n\t" + "st1w {z3.s}, p0, [x0]\n\t" + "smstop" + : : "r"(dst) + : "x12", "d0", "d1", "d2", "d3", "memory"); +} + +int main() +{ + float dst[16] = { }; + + foo(dst); + + for (int i = 0; i < 16; i++) { + if (dst[i] != 1.0f) { + goto failure; + } + } + /* success */ + return 0; + + failure: + for (int i = 0; i < 16; i++) { + printf("%f%c", dst[i], i % 4 == 3 ? '\n' : ' '); + } + return 1; +} diff --git a/tests/tcg/aarch64/sme-fmopa-2.c b/tests/tcg/aarch64/sme-fmopa-2.c new file mode 100644 index 0000000000..15f0972d83 --- /dev/null +++ b/tests/tcg/aarch64/sme-fmopa-2.c @@ -0,0 +1,56 @@ +/* + * SME outer product, FZ vs FZ16 + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +static void test_fmopa(uint32_t *result) +{ + asm(".arch_extension sme\n\t" + "smstart\n\t" /* Z*, P* and ZArray cleared */ + "ptrue p2.b, vl16\n\t" /* Limit vector length to 16 */ + "ptrue p5.b, vl16\n\t" + "movi d0, #0x00ff\n\t" /* fp16 denormal */ + "movi d16, #0x00ff\n\t" + "mov w15, #0x0001000000\n\t" /* FZ=1, FZ16=0 */ + "msr fpcr, x15\n\t" + "fmopa za3.s, p2/m, p5/m, z16.h, z0.h\n\t" + "mov w15, #0\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "mov w15, #2\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "smstop" + : "+r"(result) : + : "x15", "x16", "p2", "p5", "d0", "d16", "memory"); +} + +int main(void) +{ + uint32_t result[4 * 4] = { }; + + test_fmopa(result); + + if (result[0] != 0x2f7e0100) { + printf("Test failed: Incorrect output in first 4 bytes\n" + "Expected: %08x\n" + "Got: %08x\n", + 0x2f7e0100, result[0]); + return 1; + } + + for (int i = 1; i < 16; ++i) { + if (result[i] != 0) { + printf("Test failed: Non-zero word at position %d\n", i); + return 1; + } + } + + return 0; +} diff --git a/tests/tcg/aarch64/sme-fmopa-3.c b/tests/tcg/aarch64/sme-fmopa-3.c new file mode 100644 index 0000000000..3bfec34914 --- /dev/null +++ b/tests/tcg/aarch64/sme-fmopa-3.c @@ -0,0 +1,63 @@ +/* + * SME outer product, [ 1 2 3 4 ] squared + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include + +static const float i_1234[4] = { + 1.0f, 2.0f, 3.0f, 4.0f +}; + +static const float expected[4] = { + 4.515625f, 5.750000f, 6.984375f, 8.218750f +}; + +static void test_fmopa(float *result) +{ + asm(".arch_extension sme\n\t" + "smstart\n\t" /* ZArray cleared */ + "ptrue p2.b, vl16\n\t" /* Limit vector length to 16 */ + "ld1w {z0.s}, p2/z, [%1]\n\t" + "mov w15, #0\n\t" + "mov za3h.s[w15, 0], p2/m, z0.s\n\t" + "mov za3h.s[w15, 1], p2/m, z0.s\n\t" + "mov w15, #2\n\t" + "mov za3h.s[w15, 0], p2/m, z0.s\n\t" + "mov za3h.s[w15, 1], p2/m, z0.s\n\t" + "msr fpcr, xzr\n\t" + "fmopa za3.s, p2/m, p2/m, z0.h, z0.h\n\t" + "mov w15, #0\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "mov w15, #2\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 0]}, p2, [%0]\n\t" + "add %0, %0, #16\n\t" + "st1w {za3h.s[w15, 1]}, p2, [%0]\n\t" + "smstop" + : "+r"(result) : "r"(i_1234) + : "x15", "x16", "p2", "d0", "memory"); +} + +int main(void) +{ + float result[4 * 4] = { }; + int ret = 0; + + test_fmopa(result); + + for (int i = 0; i < 4; i++) { + float actual = result[i]; + if (fabsf(actual - expected[i]) > 0.001f) { + printf("Test failed at element %d: Expected %f, got %f\n", + i, expected[i], actual); + ret = 1; + } + } + return ret; +} 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..4eb1b35b88 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 */ @@ -135,6 +135,17 @@ __start: orr x1, x1, x3 str x1, [x2] /* 2nd 2mb (.data & .bss)*/ + /* Third block: at 'mte_page', set in kernel.ld */ + adrp x1, mte_page + add x1, x1, :lo12:mte_page + bic x1, x1, #(1 << 21) - 1 + and x4, x1, x5 + add x2, x0, x4, lsr #(21 - 3) + /* attr(AF, NX, block, AttrIndx=Attr1) */ + ldr x3, =(3 << 53) | 0x401 | (1 << 2) + orr x1, x1, x3 + str x1, [x2] + /* Setup/enable the MMU. */ /* @@ -147,7 +158,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 +190,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/kernel.ld b/tests/tcg/aarch64/system/kernel.ld index 7b3a76dcbf..aef043e31d 100644 --- a/tests/tcg/aarch64/system/kernel.ld +++ b/tests/tcg/aarch64/system/kernel.ld @@ -1,23 +1,32 @@ ENTRY(__start) -SECTIONS -{ - /* virt machine, RAM starts at 1gb */ - . = (1 << 30); +MEMORY { + /* On virt machine RAM starts at 1 GiB. */ + + /* Align text and rodata to the 1st 2 MiB chunk. */ + TXT (rx) : ORIGIN = 1 << 30, LENGTH = 2M + /* Align r/w data to the 2nd 2 MiB chunk. */ + DAT (rw) : ORIGIN = (1 << 30) + 2M, LENGTH = 2M + /* Align the MTE-enabled page to the 3rd 2 MiB chunk. */ + TAG (rw) : ORIGIN = (1 << 30) + 4M, LENGTH = 2M +} + +SECTIONS { .text : { *(.text) - } - .rodata : { *(.rodata) - } - /* align r/w section to next 2mb */ - . = ALIGN(1 << 21); + } >TXT .data : { *(.data) - } - .bss : { *(.bss) - } + } >DAT + .tag : { + /* + * Symbol 'mte_page' is used in boot.S to setup the PTE and in the mte.S + * test as the address that the MTE instructions operate on. + */ + mte_page = .; + } >TAG /DISCARD/ : { *(.ARM.attributes) } diff --git a/tests/tcg/aarch64/system/mte.S b/tests/tcg/aarch64/system/mte.S new file mode 100644 index 0000000000..b611240a95 --- /dev/null +++ b/tests/tcg/aarch64/system/mte.S @@ -0,0 +1,109 @@ +/* + * Code to help test the MTE gdbstubs in system mode. + * + * Copyright (c) 2024 Linaro Limited + * + * Author: Gustavo Romero + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define addr x0 /* Ptr to the start of the MTE-enabled page. */ +#define tagged_addr x1 /* 'addr' ptr with a random-generated tag added. */ +#define tmp0 x2 /* Scratch register. */ +#define tmp1 x3 /* Scratch register. */ +#define tmp2 x4 /* Scratch register. */ +#define tmp3 x5 /* Sctatch register. */ + + .file "mte.S" + + .text + .align 4 + + .globl main + .type main, @function + +main: + /* + * Set MAIR_EL1 (Memory Attribute Index Register). In boot.S, the + * attribute index for .mte_page is set to point to MAILR_EL field Attr1 + * (AttrIndx=Attr1), so set Attr1 as Tagged Normal (MTE) to enable MTE + * on this page. + * + * Attr1 = 0xF0 => Tagged Normal (MTE) + */ + mrs tmp0, mair_el1 + orr tmp0, tmp0, (0xF0 << 8) + msr mair_el1, tmp0 + + /* + * Set TCR_EL1 (Translation Control Registers) to ignore the top byte + * in the translated addresses so it can be used to keep the tags. + * + * TBI0[37] = 0b1 => Top Byte ignored and used for tagged addresses + */ + mrs tmp1, tcr_el1 + orr tmp1, tmp1, (1 << 37) + msr tcr_el1, tmp1 + + /* + * Set SCTLR_EL1 (System Control Register) to enable the use of MTE + * insns., like stg & friends, and to enable synchronous exception in + * case of a tag mismatch, i.e., when the logical tag in 'tagged_addr' + * is different from the allocation tag related to 'addr' address. + * + * ATA[43] = 0b1 => Enable access to allocation tags at EL1 + * TCF[41:40] = 0b01 => Tag Check Faults cause a synchronous exception + * + */ + mrs tmp2, sctlr_el1 + mov tmp3, (1 << 43) | (1 << 40) + orr tmp2, tmp2, tmp3 + msr sctlr_el1, tmp2 + + isb + + /* + * MTE-enabled page resides at the 3rd 2MB chunk in the second 1GB + * block, i.e., at 0x40400000 address. See .mte_page section in boot.S + * and kernel.ld (where the address is effectively computed). + * + * Load .mte_page address into 'addr' register. + */ + adrp addr, mte_page + add addr, addr, :lo12:mte_page + + /* + * Set GCR for random tag generation. 0xA5 is just a random value to set + * GCR != 0 so the tag generated by 'irg' insn. is not zero, which is + * more interesting for the tests than when tag is zero. + */ + mov tmp0, 0xA5 + msr gcr_el1, tmp0 + + /* + * Generate a logical tag, add it to 'addr' address and put it into + * 'tagged_addr'. + */ + irg tagged_addr, addr + + /* + * Store the generated tag to memory region pointed to by 'addr', i.e. + * set the allocation tag for granule at 'addr'. The tag is extracted + * by stg from tagged_addr pointer. + */ + stg tagged_addr, [addr] + + /* + * Store a random value (0xdeadbeef) to tagged_addr address. This must + * not cause any Tag Check Fault since logical tag in tagged_addr and + * allocation tag associated with the memory pointed by tagged_addr are + * set the same, otherwise something is off and the test fails -- an + * exception is generated. + */ + ldr tmp1, =0xdeadbeef + str tmp1, [tagged_addr] + + /* This label is used by GDB Python script test-mte.py. */ +main_end: + ret diff --git a/tests/tcg/aarch64/system/semiheap.c b/tests/tcg/aarch64/system/semiheap.c index a254bd8982..1a8c0f31a0 100644 --- a/tests/tcg/aarch64/system/semiheap.c +++ b/tests/tcg/aarch64/system/semiheap.c @@ -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-2375.c b/tests/tcg/aarch64/test-2375.c new file mode 100644 index 0000000000..84c7e7de71 --- /dev/null +++ b/tests/tcg/aarch64/test-2375.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Linaro Ltd */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2375 */ + +#include + +int main(void) +{ + int r, z; + + asm("msr fpcr, %2\n\t" + "fjcvtzs %w0, %d3\n\t" + "cset %1, eq" + : "=r"(r), "=r"(z) + : "r"(0x01000000L), /* FZ = 1 */ + "w"(0xfcff00L)); /* denormal */ + + assert(r == 0); + assert(z == 0); + 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/aarch64_be/Makefile.target b/tests/tcg/aarch64_be/Makefile.target new file mode 100644 index 0000000000..cbe5fa0b2d --- /dev/null +++ b/tests/tcg/aarch64_be/Makefile.target @@ -0,0 +1,17 @@ +# -*- Mode: makefile -*- +# +# A super basic AArch64 BE makefile. As we don't have any big-endian +# libc available the best we can do is a basic Hello World. + +AARCH64BE_SRC=$(SRC_PATH)/tests/tcg/aarch64_be +VPATH += $(AARCH64BE_SRC) + +AARCH64BE_TEST_SRCS=$(notdir $(wildcard $(AARCH64BE_SRC)/*.c)) +AARCH64BE_TESTS=$(AARCH64BE_TEST_SRCS:.c=) +#MULTIARCH_TESTS = $(MULTIARCH_SRCS:.c=) + +# We need to specify big-endian cflags +CFLAGS +=-mbig-endian -ffreestanding +LDFLAGS +=-nostdlib + +TESTS += $(AARCH64BE_TESTS) diff --git a/tests/tcg/aarch64_be/hello.c b/tests/tcg/aarch64_be/hello.c new file mode 100644 index 0000000000..a9b2ab45de --- /dev/null +++ b/tests/tcg/aarch64_be/hello.c @@ -0,0 +1,35 @@ +/* + * Non-libc syscall hello world for Aarch64 BE + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define __NR_write 64 +#define __NR_exit 93 + +int write(int fd, char *buf, int len) +{ + register int x0 __asm__("x0") = fd; + register char *x1 __asm__("x1") = buf; + register int x2 __asm__("x2") = len; + register int x8 __asm__("x8") = __NR_write; + + asm volatile("svc #0" : : "r"(x0), "r"(x1), "r"(x2), "r"(x8)); + + return len; +} + +void exit(int ret) +{ + register int x0 __asm__("x0") = ret; + register int x8 __asm__("x8") = __NR_exit; + + asm volatile("svc #0" : : "r"(x0), "r"(x8)); + __builtin_unreachable(); +} + +void _start(void) +{ + write(1, "Hello World\n", 12); + exit(0); +} diff --git a/tests/tcg/alpha/Makefile.softmmu-target b/tests/tcg/alpha/Makefile.softmmu-target index 09193a62d6..a944102a3c 100644 --- a/tests/tcg/alpha/Makefile.softmmu-target +++ b/tests/tcg/alpha/Makefile.softmmu-target @@ -22,13 +22,13 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -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 +memory: CFLAGS+=-DCHECK_UNALIGNED=0 -mbwx # Running QEMU_OPTS+=-serial chardev:output -kernel diff --git a/tests/tcg/alpha/Makefile.target b/tests/tcg/alpha/Makefile.target index a585080328..36d8ed1eae 100644 --- a/tests/tcg/alpha/Makefile.target +++ b/tests/tcg/alpha/Makefile.target @@ -5,14 +5,14 @@ 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 test-cmov: test-cond.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -run-test-cmov: test-cmov +# Force generation of byte read/write +test-plugin-mem-access: CFLAGS+=-mbwx -# On Alpha Linux only supports 8k pages -EXTRA_RUNS+=run-test-mmap-8192 +run-test-cmov: test-cmov 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 7df88ddea8..b66074b0b4 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 -mfloat-abi=soft +test-armv6m-undef: test-armv6m-undef.S + $(CC) -mcpu=cortex-m0 -mfloat-abi=soft \ + -Wl,--build-id=none -x assembler-with-cpp \ + $< -o $@ -nostdlib -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 +run-test-armv6m-undef: QEMU_OPTS=-semihosting-config enable=on,target=native,chardev=output -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 $(CRT_OBJS) $(MINILIB_OBJS) -lgcc + +# building head blobs +.PRECIOUS: $(CRT_OBJS) + +%.o: $(ARM_SRC)/%.S + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -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 b3b1504a1c..06ddf3e04f 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -8,23 +8,29 @@ ARM_SRC=$(SRC_PATH)/tests/tcg/arm # Set search path for all sources VPATH += $(ARM_SRC) +config-cc.mak: Makefile + $(quiet-@)( \ + $(call cc-option,-fno-integrated-as, CROSS_CC_HAS_FNIA)) 3> config-cc.mak +-include config-cc.mak + 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 ARM_TESTS += test-arm-iwmmxt -test-arm-iwmmxt: CFLAGS+=-marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 +# Clang assembler does not support IWMXT, so use the external assembler. +test-arm-iwmmxt: CFLAGS += -marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 $(CROSS_CC_HAS_FNIA) test-arm-iwmmxt: test-arm-iwmmxt.S - $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + $(CC) $(CFLAGS) -Wa,--noexecstack $< -o $@ $(LDFLAGS) # Float-convert Tests ARM_TESTS += fcvt -fcvt: LDFLAGS+=-lm -# fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 +fcvt: LDFLAGS += -lm +fcvt: CFLAGS += -march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 run-fcvt: fcvt $(call run-test,fcvt,$(QEMU) $<) $(call diff-out,fcvt,$(ARM_SRC)/fcvt.ref) @@ -46,11 +52,6 @@ semihosting-arm: semihosting.c run-semihosting-arm: semihosting-arm $(call run-test,$<,$(QEMU) $< 2> $<.err) -run-plugin-semihosting-arm-with-%: - $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ - -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@) \ - $(call strip-plugin,$<) 2> $<.err) - ARM_TESTS += semiconsole-arm semiconsole: CFLAGS += -mthumb @@ -62,9 +63,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 @@ -87,6 +85,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/fcvt.c b/tests/tcg/arm/fcvt.c index 7ac47b564e..ecebbb0247 100644 --- a/tests/tcg/arm/fcvt.c +++ b/tests/tcg/arm/fcvt.c @@ -126,7 +126,7 @@ static void convert_single_to_half(void) asm("vcvtb.f16.f32 %0, %1" : "=t" (output) : "x" (input)); #else uint16_t output; - asm("fcvt %h0, %s1" : "=w" (output) : "x" (input)); + asm("fcvt %h0, %s1" : "=w" (output) : "w" (input)); #endif print_half_number(i, output); } @@ -149,7 +149,7 @@ static void convert_single_to_double(void) #if defined(__arm__) asm("vcvt.f64.f32 %P0, %1" : "=w" (output) : "t" (input)); #else - asm("fcvt %d0, %s1" : "=w" (output) : "x" (input)); + asm("fcvt %d0, %s1" : "=w" (output) : "w" (input)); #endif print_double_number(i, output); } @@ -244,7 +244,7 @@ static void convert_double_to_half(void) /* asm("vcvtb.f16.f64 %0, %P1" : "=t" (output) : "x" (input)); */ output = input; #else - asm("fcvt %h0, %d1" : "=w" (output) : "x" (input)); + asm("fcvt %h0, %d1" : "=w" (output) : "w" (input)); #endif print_half_number(i, output); } @@ -258,7 +258,7 @@ static void convert_double_to_single(void) for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { double input = double_numbers[i].d; - uint32_t output; + float output; feclearexcept(FE_ALL_EXCEPT); @@ -267,7 +267,7 @@ static void convert_double_to_single(void) #if defined(__arm__) asm("vcvt.f32.f64 %0, %P1" : "=w" (output) : "x" (input)); #else - asm("fcvt %s0, %d1" : "=w" (output) : "x" (input)); + asm("fcvt %s0, %d1" : "=w" (output) : "w" (input)); #endif print_single_number(i, output); @@ -335,7 +335,7 @@ static void convert_half_to_double(void) /* asm("vcvtb.f64.f16 %P0, %1" : "=w" (output) : "t" (input)); */ output = input; #else - asm("fcvt %d0, %h1" : "=w" (output) : "x" (input)); + asm("fcvt %d0, %h1" : "=w" (output) : "w" (input)); #endif print_double_number(i, output); } @@ -355,9 +355,14 @@ static void convert_half_to_single(void) print_half_number(i, input); #if defined(__arm__) - asm("vcvtb.f32.f16 %0, %1" : "=w" (output) : "x" ((uint32_t)input)); + /* + * Clang refuses to allocate an integer to a fp register. + * Perform the move from a general register by hand. + */ + asm("vmov %0, %1\n\t" + "vcvtb.f32.f16 %0, %0" : "=w" (output) : "r" (input)); #else - asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); + asm("fcvt %s0, %h1" : "=w" (output) : "w" (input)); #endif print_single_number(i, output); } @@ -380,7 +385,7 @@ static void convert_half_to_integer(void) /* asm("vcvt.s32.f16 %0, %1" : "=t" (output) : "t" (input)); v8.2*/ output = input; #else - asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); + asm("fcvt %s0, %h1" : "=w" (output) : "w" (input)); #endif print_int64(i, output); } @@ -422,10 +427,9 @@ int main(int argc, char *argv[argc]) /* And now with ARM alternative FP16 */ #if defined(__arm__) - /* See glibc sysdeps/arm/fpu_control.h */ - asm("mrc p10, 7, r1, cr1, cr0, 0\n\t" + asm("vmrs r1, fpscr\n\t" "orr r1, r1, %[flags]\n\t" - "mcr p10, 7, r1, cr1, cr0, 0\n\t" + "vmsr fpscr, r1" : /* no output */ : [flags] "n" (1 << 26) : "r1" ); #else asm("mrs x1, fpcr\n\t" diff --git a/tests/tcg/arm/fcvt.ref b/tests/tcg/arm/fcvt.ref index f052b6d7e5..8e007c3345 100644 --- a/tests/tcg/arm/fcvt.ref +++ b/tests/tcg/arm/fcvt.ref @@ -211,45 +211,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -257,41 +257,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -574,87 +574,87 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -937,45 +937,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -983,41 +983,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1300,45 +1300,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1346,41 +1346,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -nan / 0xffffe000 (0 => OK) @@ -1845,45 +1845,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -1891,41 +1891,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2208,87 +2208,87 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +14 SINGLE: 2.98023259404089913006e-08 / 0x33000001 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +15 SINGLE: 5.96046021428264793940e-08 / 0x337ffff4 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +16 SINGLE: 6.09756025369279086590e-05 / 0x387fc00e (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +17 SINGLE: 6.10352071817032992840e-05 / 0x38800007 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) -20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +20 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) -21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) -22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 SINGLE: 1.40129846432481707093e-45 / 0x00000001 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +25 SINGLE: 2.71828198432922363282e+00 / 0x402df855 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +26 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +33 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +36 SINGLE: inf / 0x7f800000 (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2571,45 +2571,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -inf / 0xff800000 (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -1.40129846432481707093e-45 / 0x80000001 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2617,41 +2617,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) @@ -2934,45 +2934,45 @@ Converting double-precision to half-precision 40 HALF: 0000 (0x1 => INVALID) Converting double-precision to single-precision 00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +00 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) 01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) -01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +01 SINGLE: -nan / 0xffc00000 (0 => OK) 02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) -02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) 03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) -03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0x14 => OVERFLOW INEXACT ) 04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +04 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) -05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) 06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) -06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +06 SINGLE: -1.11099992680387713644e+31 / 0xf30c3a58 (0x10 => INEXACT ) 07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) -07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +07 SINGLE: -1.11099995702702262681e+30 / 0xf1605d5a (0x10 => INEXACT ) 08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) -08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +08 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) 09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) -09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +09 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) 10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) -10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +10 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0x18 => UNDERFLOW INEXACT ) 11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) -11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +11 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) 12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) 12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) 13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) -13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +13 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) 14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) -14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +14 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0x10 => INEXACT ) 15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) -15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +15 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0x10 => INEXACT ) 16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) -16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +16 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0x10 => INEXACT ) 17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) -17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +17 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0x10 => INEXACT ) 18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +18 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) -19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +19 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) 20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) 20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) @@ -2980,41 +2980,41 @@ Converting double-precision to single-precision 22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) 22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) 23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) -23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +23 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) 24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) -24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +24 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) 25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) -25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +25 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0x10 => INEXACT ) 26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) -26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +26 SINGLE: 3.14159250259399414062e+00 / 0x40490fda (0x10 => INEXACT ) 27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) -27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +27 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) 28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) -28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +28 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) 29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) -29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +29 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) 30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) -30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +30 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) 31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) -31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +31 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) 32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) -32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +32 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) 33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) -33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +33 SINGLE: 2.14748352000000000000e+09 / 0x4effffff (0x10 => INEXACT ) 34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +34 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) -35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) 36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) -36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +36 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0x14 => OVERFLOW INEXACT ) 37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) -37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +37 SINGLE: inf / 0x7f800000 (0 => OK) 38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) -38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +38 SINGLE: nan / 0x7fc00000 (0 => OK) 39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) -39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +39 SINGLE: nan / 0x7fc00000 (0x1 => INVALID) 40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) -40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +40 SINGLE: nan / 0x7fe00000 (0x1 => INVALID) Converting half-precision to single-precision 00 HALF: 0xffff (0 => OK) 00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) 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/.gdbinit b/tests/tcg/cris/.gdbinit deleted file mode 100644 index 5e8c1d32f3..0000000000 --- a/tests/tcg/cris/.gdbinit +++ /dev/null @@ -1,11 +0,0 @@ -b main -b _fail -b exit -display /i $pc -display /x $srp -display /x $r0 -display /x $r1 -display /x $r2 -display /x $r3 -display /x $r4 -display /t $ccs diff --git a/tests/tcg/cris/Makefile.target b/tests/tcg/cris/Makefile.target deleted file mode 100644 index 372287bd03..0000000000 --- a/tests/tcg/cris/Makefile.target +++ /dev/null @@ -1,59 +0,0 @@ -# -*- Mode: makefile -*- -# -# Cris tests -# -# Currently we can only build the "bare" tests with the docker -# supplied cross-compiler. -# - -CRIS_SRC = $(SRC_PATH)/tests/tcg/cris/bare -CRIS_ALL = $(wildcard $(CRIS_SRC)/*.s) -CRIS_TESTS = $(patsubst $(CRIS_SRC)/%.s, %, $(CRIS_ALL)) -# Filter out common blobs and broken tests -CRIS_BROKEN_TESTS = crt check_jsr -# upstream GCC doesn't support v32 -CRIS_BROKEN_TESTS += check_mcp check_mulv32 check_addiv32 check_movpmv32 -CRIS_BROKEN_TESTS += check_movprv32 check_clearfv32 check_movemrv32 check_bas -CRIS_BROKEN_TESTS += check_lapc check_movei -# no sure why -CRIS_BROKEN_TESTS += check_scc check_xarith - -CRIS_USABLE_TESTS = $(filter-out $(CRIS_BROKEN_TESTS), $(CRIS_TESTS)) -CRIS_RUNS = $(patsubst %, run-%, $(CRIS_USABLE_TESTS)) - -# override the list of tests, as we can't build the multiarch tests -TESTS = $(CRIS_USABLE_TESTS) -EXTRA_RUNS = -VPATH = $(CRIS_SRC) - -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 -NOSTDFLAGS = -nostartfiles -nostdlib -ASFLAGS += -mcpu=v10 -g -Wa,-I,$(SRC_PATH)/tests/tcg/cris/bare -CRT_FILES = crt.o sys.o - -# stop make deleting crt files if build fails -.PRECIOUS: $(CRT_FILES) - -%.o: %.c - $(CC) -c $< -o $@ - -%.o: %.s - $(AS) $(ASFLAGS) -c $< -o $@ - -%: %.s $(CRT_FILES) - $(CC) $(ASFLAGS) $< -o $@ $(LDFLAGS) $(NOSTDFLAGS) $(CRT_FILES) - -# The default CPU breaks (possibly as it's max?) so force crisv17 -QEMU_OPTS=-cpu crisv17 - -# Additional runners to run under GNU SIM -CRIS_RUNS_ON_SIM=$(patsubst %, %-on-sim, $(CRIS_RUNS)) -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) $<) diff --git a/tests/tcg/cris/README b/tests/tcg/cris/README deleted file mode 100644 index 2e65a76f10..0000000000 --- a/tests/tcg/cris/README +++ /dev/null @@ -1 +0,0 @@ -Test-suite for the cris port. Heavily based on the test-suite for the CRIS port of sim by Hans-Peter Nilsson. diff --git a/tests/tcg/cris/bare/check_addcv17.s b/tests/tcg/cris/bare/check_addcv17.s deleted file mode 100644 index 52ef7a9716..0000000000 --- a/tests/tcg/cris/bare/check_addcv17.s +++ /dev/null @@ -1,65 +0,0 @@ -# mach: crisv17 - - .include "testutils.inc" - - .macro addc Rs Rd inc=0 -# Create the instruction manually since there is no assembler support yet - .word (\Rd << 12) | \Rs | (\inc << 10) | 0x09a0 - .endm - - start - - .data -mem1: - .dword 0x0 -mem2: - .dword 0x12345678 - - .text - move.d mem1,r4 - clearf nzvc - addc 4 3 - test_cc 0 1 0 0 - checkr3 0 - - move.d mem1,r4 - clearf nzvc - ax - addc 4 3 - test_cc 0 0 0 0 - checkr3 0 - - move.d mem1,r4 - clearf nzvc - setf c - addc 4 3 - test_cc 0 0 0 0 - checkr3 1 - - move.d mem2,r4 - moveq 2, r3 - clearf nzvc - setf c - addc 4 3 - test_cc 0 0 0 0 - checkr3 1234567b - - move.d mem2,r5 - clearf nzvc - cmp.d r4,r5 - test_cc 0 1 0 0 - - move.d mem2,r4 - moveq 2, r3 - clearf nzvc - addc 4 3 inc=1 - test_cc 0 0 0 0 - checkr3 1234567a - - move.d mem2,r5 - clearf nzvc - addq 4,r5 - cmp.d r4,r5 - test_cc 0 1 0 0 - - quit diff --git a/tests/tcg/cris/bare/check_addi.s b/tests/tcg/cris/bare/check_addi.s deleted file mode 100644 index a00dec02af..0000000000 --- a/tests/tcg/cris/bare/check_addi.s +++ /dev/null @@ -1,57 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 0\n1\n2\n4\nbe02460f\n69d035a6\nc16c14d4\n - - .include "testutils.inc" - start - moveq 0,r3 - moveq 0,r4 - clearf zcvn - addi r4.b,r3 - test_cc 0 0 0 0 - checkr3 0 - - moveq 0,r3 - moveq 1,r4 - setf zcvn - addi r4.b,r3 - test_cc 1 1 1 1 - checkr3 1 - - moveq 0,r3 - moveq 1,r4 - setf cv - clearf zn - addi r4.w,r3 - test_cc 0 0 1 1 - checkr3 2 - - moveq 0,r3 - moveq 1,r4 - clearf cv - setf zn - addi r4.d,r3 - test_cc 1 1 0 0 - checkr3 4 - - move.d 0x12345678,r3 - move.d 0xabcdef97,r4 - clearf cn - setf zv - addi r4.b,r3 - test_cc 0 1 1 0 - checkr3 be02460f - - move.d 0x12345678,r3 - move.d 0xabcdef97,r4 - setf cn - clearf zv - addi r4.w,r3 - test_cc 1 0 0 1 - checkr3 69d035a6 - - move.d 0x12345678,r3 - move.d 0xabcdef97,r4 - addi r4.d,r3 - checkr3 c16c14d4 - - quit diff --git a/tests/tcg/cris/bare/check_addiv32.s b/tests/tcg/cris/bare/check_addiv32.s deleted file mode 100644 index 20ba25d219..0000000000 --- a/tests/tcg/cris/bare/check_addiv32.s +++ /dev/null @@ -1,62 +0,0 @@ -# mach: crisv32 -# output: 4455aa77\n4455aa77\nee19ccff\nff22\n4455aa77\nff224455\n55aa77ff\n - - .include "testutils.inc" - .data -x: - .dword 0x55aa77ff - .dword 0xccff2244 - .dword 0x88ccee19 - - start - setf cv - moveq -1,r0 - move.d x-32768,r5 - move.d 32769,r6 - addi r6.b,r5,acr - test_cc 0 0 1 1 - move.d [acr],r3 - checkr3 4455aa77 - - addu.w 32771,r5 - setf znvc - moveq -1,r8 - addi r8.w,r5,acr - test_cc 1 1 1 1 - move.d [acr],r3 - checkr3 4455aa77 - - moveq 5,r10 - clearf znvc - addi r10.b,acr,acr - test_cc 0 0 0 0 - move.d [acr],r3 - checkr3 ee19ccff - - subq 1,r5 - move.d r5,r8 - subq 1,r8 - moveq 1,r9 - addi r9.d,r8,acr - test_cc 0 0 0 0 - movu.w [acr],r3 - checkr3 ff22 - - moveq -2,r11 - addi r11.w,acr,acr - move.d [acr],r3 - checkr3 4455aa77 - - moveq 5,r9 - addi r9.d,acr,acr - subq 18,acr - move.d [acr],r3 - checkr3 ff224455 - - move.d -76789888/4,r12 - addi r12.d,r5,acr - add.d 76789886,acr - move.d [acr],r3 - checkr3 55aa77ff - - quit diff --git a/tests/tcg/cris/bare/check_addm.s b/tests/tcg/cris/bare/check_addm.s deleted file mode 100644 index efece9f538..0000000000 --- a/tests/tcg/cris/bare/check_addm.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n781344d0\n - - .include "testutils.inc" - .data -x: - .dword 2,-1,0xffff,-1,0x5432f789 - .word 2,-1,0xffff,0xf789 - .byte 2,0xff,0x89 - .byte 0x7e - - start - moveq -1,r3 - move.d x,r5 - add.d [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - add.d [r5],r3 - test_cc 0 0 0 1 - addq 4,r5 - checkr3 1 - - move.d 0xffff,r3 - add.d [r5+],r3 - test_cc 0 0 0 0 - checkr3 1fffe - - moveq -1,r3 - add.d [r5+],r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x78134452,r3 - add.d [r5+],r3 - test_cc 1 0 1 0 - checkr3 cc463bdb - - moveq -1,r3 - add.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 ffff0001 - - moveq 2,r3 - add.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r3 - add.w [r5],r3 - test_cc 1 0 0 1 - checkr3 fffe - - move.d 0xfedaffff,r3 - add.w [r5+],r3 - test_cc 1 0 0 1 - checkr3 fedafffe - - move.d 0x78134452,r3 - add.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - moveq -1,r3 - add.b [r5],r3 - test_cc 0 0 0 1 - addq 1,r5 - checkr3 ffffff01 - - moveq 2,r3 - add.b [r5],r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xff,r3 - add.b [r5],r3 - test_cc 1 0 0 1 - checkr3 fe - - move.d 0xfeda49ff,r3 - add.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 feda49fe - - move.d 0x78134452,r3 - add.b [r5+],r3 - test_cc 1 0 0 0 - checkr3 781344db - - move.d 0x78134452,r3 - add.b [r5],r3 - test_cc 1 0 1 0 - checkr3 781344d0 - - quit diff --git a/tests/tcg/cris/bare/check_addq.s b/tests/tcg/cris/bare/check_addq.s deleted file mode 100644 index e6f874f9b2..0000000000 --- a/tests/tcg/cris/bare/check_addq.s +++ /dev/null @@ -1,47 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n0\n1\n100\n10000\n47\n67\na6\n80000001\n - - .include "testutils.inc" - start - moveq -2,r3 - addq 1,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - addq 1,r3 - test_cc 0 1 0 1 - checkr3 0 - - addq 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xff,r3 - addq 1,r3 - test_cc 0 0 0 0 - checkr3 100 - - move.d 0xffff,r3 - addq 1,r3 - test_cc 0 0 0 0 - checkr3 10000 - - move.d 0x42,r3 - addq 5,r3 - test_cc 0 0 0 0 - checkr3 47 - - addq 32,r3 - test_cc 0 0 0 0 - checkr3 67 - - addq 63,r3 - test_cc 0 0 0 0 - checkr3 a6 - - move.d 0x7ffffffe,r3 - addq 3,r3 - test_cc 1 0 1 0 - checkr3 80000001 - - quit diff --git a/tests/tcg/cris/bare/check_addr.s b/tests/tcg/cris/bare/check_addr.s deleted file mode 100644 index 7f55cdc1b5..0000000000 --- a/tests/tcg/cris/bare/check_addr.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - moveq -1,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - - moveq -1,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.d r4,r3 - test_cc 1 0 1 0 - checkr3 cc463bdb - - moveq -1,r3 - moveq 2,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 ffff0001 - - moveq 2,r3 - moveq -1,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fffe - - move.d 0xfedaffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fedafffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - moveq -1,r3 - moveq 2,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 ffffff01 - - moveq 2,r3 - moveq -1,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 fe - - move.d 0xfeda49ff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 feda49fe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.b r4,r3 - test_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_addxc.s b/tests/tcg/cris/bare/check_addxc.s deleted file mode 100644 index 09c8355bf8..0000000000 --- a/tests/tcg/cris/bare/check_addxc.s +++ /dev/null @@ -1,91 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n101\n10001\n100fe\n1fffe\nfffe\nfffe\nfffffffe\nfe\nfffffffe\n781344db\n781343db\n78143bdb\n78133bdb\n800000ed\n0\n - - .include "testutils.inc" - start - moveq 2,r3 - adds.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - adds.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - addu.b 0xff,r3 - checkr3 101 - - moveq 2,r3 - move.d 0xffffffff,r4 - addu.w -1,r3 - test_cc 0 0 0 0 - checkr3 10001 - - move.d 0xffff,r3 - addu.b -1,r3 - test_cc 0 0 0 0 - checkr3 100fe - - move.d 0xffff,r3 - addu.w -1,r3 - test_cc 0 0 0 0 - checkr3 1fffe - - move.d 0xffff,r3 - adds.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 fffe - - move.d 0xffff,r3 - adds.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 fffe - - moveq -1,r3 - adds.b 0xff,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - moveq -1,r3 - adds.w 0xff,r3 - test_cc 0 0 0 1 - checkr3 fe - - moveq -1,r3 - adds.w 0xffff,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x78134452,r3 - addu.b 0x89,r3 - test_cc 0 0 0 0 - checkr3 781344db - - move.d 0x78134452,r3 - adds.b 0x89,r3 - test_cc 0 0 0 1 - checkr3 781343db - - move.d 0x78134452,r3 - addu.w 0xf789,r3 - test_cc 0 0 0 0 - checkr3 78143bdb - - move.d 0x78134452,r3 - adds.w 0xf789,r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - move.d 0x7fffffee,r3 - addu.b 0xff,r3 - test_cc 1 0 1 0 - checkr3 800000ed - - move.d 0x1,r3 - adds.w 0xffff,r3 - test_cc 0 1 0 1 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_addxm.s b/tests/tcg/cris/bare/check_addxm.s deleted file mode 100644 index 7563494b99..0000000000 --- a/tests/tcg/cris/bare/check_addxm.s +++ /dev/null @@ -1,106 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n101\n10001\n100fe\n1fffe\nfffe\nfffe\nfffffffe\nfe\nfffffffe\n781344db\n781343db\n78143bdb\n78133bdb\n800000ed\n0\n - - .include "testutils.inc" - .data -x: - .byte 0xff - .word 0xffff - .word 0xff - .word 0xffff - .byte 0x89 - .word 0xf789 - .byte 0xff - .word 0xffff - - start - moveq 2,r3 - move.d x,r5 - adds.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - adds.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - subq 3,r5 - addu.b [r5+],r3 - test_cc 0 0 0 0 - checkr3 101 - - moveq 2,r3 - addu.w [r5+],r3 - subq 3,r5 - test_cc 0 0 0 0 - checkr3 10001 - - move.d 0xffff,r3 - addu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 100fe - - move.d 0xffff,r3 - addu.w [r5],r3 - test_cc 0 0 0 0 - checkr3 1fffe - - move.d 0xffff,r3 - adds.b [r5],r3 - test_cc 0 0 0 1 - checkr3 fffe - - move.d 0xffff,r3 - adds.w [r5],r3 - test_cc 0 0 0 1 - checkr3 fffe - - moveq -1,r3 - adds.b [r5],r3 - test_cc 1 0 0 1 - addq 3,r5 - checkr3 fffffffe - - moveq -1,r3 - adds.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 fe - - moveq -1,r3 - adds.w [r5+],r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x78134452,r3 - addu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 781344db - - move.d 0x78134452,r3 - adds.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 781343db - - move.d 0x78134452,r3 - addu.w [r5],r3 - test_cc 0 0 0 0 - checkr3 78143bdb - - move.d 0x78134452,r3 - adds.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - move.d 0x7fffffee,r3 - addu.b [r5+],r3 - test_cc 1 0 1 0 - checkr3 800000ed - - move.d 0x1,r3 - adds.w [r5+],r3 - test_cc 0 1 0 1 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_addxr.s b/tests/tcg/cris/bare/check_addxr.s deleted file mode 100644 index 7f55cdc1b5..0000000000 --- a/tests/tcg/cris/bare/check_addxr.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - moveq -1,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - - moveq -1,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.d r4,r3 - test_cc 1 0 1 0 - checkr3 cc463bdb - - moveq -1,r3 - moveq 2,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 ffff0001 - - moveq 2,r3 - moveq -1,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fffe - - move.d 0xfedaffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fedafffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - moveq -1,r3 - moveq 2,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 ffffff01 - - moveq 2,r3 - moveq -1,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 fe - - move.d 0xfeda49ff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 feda49fe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.b r4,r3 - test_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_andc.s b/tests/tcg/cris/bare/check_andc.s deleted file mode 100644 index a947b773c9..0000000000 --- a/tests/tcg/cris/bare/check_andc.s +++ /dev/null @@ -1,80 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n - - .include "testutils.inc" - start - moveq -1,r3 - and.d 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - and.d -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - and.d 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - and.d -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - and.d 0x5432f789,r3 - test_move_cc 0 0 0 0 - checkr3 50124400 - - moveq -1,r3 - and.w 2,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0002 - - moveq 2,r3 - and.w -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfffff,r3 - and.w 0xffff,r3 - test_move_cc 1 0 0 0 - checkr3 fffff - - move.d 0xfedaffaf,r3 - and.w 0xff5f,r3 - test_move_cc 1 0 0 0 - checkr3 fedaff0f - - move.d 0x78134452,r3 - and.w 0xf789,r3 - test_move_cc 0 0 0 0 - checkr3 78134400 - - moveq -1,r3 - and.b 2,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff02 - - moveq 2,r3 - and.b -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfa7,r3 - and.b 0x5a,r3 - test_move_cc 0 0 0 0 - checkr3 f02 - - move.d 0x78134453,r3 - and.b 0x89,r3 - test_move_cc 0 0 0 0 - checkr3 78134401 - - and.b 0,r3 - test_move_cc 0 1 0 0 - checkr3 78134400 - - quit diff --git a/tests/tcg/cris/bare/check_andm.s b/tests/tcg/cris/bare/check_andm.s deleted file mode 100644 index 93858863fe..0000000000 --- a/tests/tcg/cris/bare/check_andm.s +++ /dev/null @@ -1,90 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n - - .include "testutils.inc" - .data -x: - .dword 2,-1,0xffff,-1,0x5432f789 - .word 2,-1,0xffff,0xff5f,0xf789 - .byte 2,-1,0x5a,0x89,0 - - start - moveq -1,r3 - move.d x,r5 - and.d [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - and.d [r5],r3 - test_move_cc 0 0 0 0 - addq 4,r5 - checkr3 2 - - move.d 0xffff,r3 - and.d [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - and.d [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - and.d [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 50124400 - - moveq -1,r3 - and.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 ffff0002 - - moveq 2,r3 - and.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfffff,r3 - and.w [r5],r3 - test_move_cc 1 0 0 0 - addq 2,r5 - checkr3 fffff - - move.d 0xfedaffaf,r3 - and.w [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 fedaff0f - - move.d 0x78134452,r3 - and.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 78134400 - - moveq -1,r3 - and.b [r5],r3 - test_move_cc 0 0 0 0 - addq 1,r5 - checkr3 ffffff02 - - moveq 2,r3 - and.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfa7,r3 - and.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 f02 - - move.d 0x78134453,r3 - and.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 78134401 - - and.b [r5],r3 - test_move_cc 0 1 0 0 - checkr3 78134400 - - quit diff --git a/tests/tcg/cris/bare/check_andq.s b/tests/tcg/cris/bare/check_andq.s deleted file mode 100644 index 55aa7b0607..0000000000 --- a/tests/tcg/cris/bare/check_andq.s +++ /dev/null @@ -1,46 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n1f\nffffffe0\n78134452\n0\n - - .include "testutils.inc" - start - moveq -1,r3 - andq 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - andq -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - andq -1,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - andq -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - andq 31,r3 - test_move_cc 0 0 0 0 - checkr3 1f - - moveq -1,r3 - andq -32,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffe0 - - move.d 0x78134457,r3 - andq -14,r3 - test_move_cc 0 0 0 0 - checkr3 78134452 - - moveq 0,r3 - andq -14,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_andr.s b/tests/tcg/cris/bare/check_andr.s deleted file mode 100644 index 61aa1dc32f..0000000000 --- a/tests/tcg/cris/bare/check_andr.s +++ /dev/null @@ -1,95 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r4 - move.d r4,r3 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - and.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 50124400 - - moveq -1,r3 - moveq 2,r4 - and.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0002 - - moveq 2,r3 - moveq -1,r4 - and.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfffff,r3 - move.d 0xffff,r4 - and.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffff - - move.d 0xfedaffaf,r3 - move.d 0xff5f,r4 - and.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fedaff0f - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - and.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 78134400 - - moveq -1,r3 - moveq 2,r4 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff02 - - moveq 2,r3 - moveq -1,r4 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0x5a,r4 - move.d 0xfa7,r3 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 f02 - - move.d 0x5432f789,r4 - move.d 0x78134453,r3 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 78134401 - - moveq 0,r7 - and.b r7,r3 - test_move_cc 0 1 0 0 - checkr3 78134400 - - quit diff --git a/tests/tcg/cris/bare/check_asr.s b/tests/tcg/cris/bare/check_asr.s deleted file mode 100644 index 0a02ae6f7e..0000000000 --- a/tests/tcg/cris/bare/check_asr.s +++ /dev/null @@ -1,230 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n1\nffffffff\nffffffff\n5a67f\nffffffff\nffffffff\nffffffff\nf699fc67\nffffffff\n1\nffffffff\nffffffff\n5a67f\nda67ffff\nda67ffff\nda67ffff\nda67fc67\nffffffff\nffffffff\n1\nffffffff\nffffffff\n5a670007\nda67f1ff\nda67f1ff\nda67f1ff\nda67f1e7\nffffffff\nffffffff\n1\nffffffff\nffffffff\nffffffff\n5a67f1ff\n5a67f1f9\n0\n5a670000\n - - .include "testutils.inc" - start - moveq -1,r3 - asrq 0,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - asrq 1,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - asrq 31,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - asrq 15,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5a67f19f,r3 - asrq 12,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xda67f19f,r3 - move.d 32,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xda67f19f,r3 - move.d 33,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xda67f19f,r3 - move.d 66,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 f699fc67 - - moveq -1,r3 - moveq 0,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - asr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 15,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5a67f19f,r3 - moveq 12,r4 - asr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67ffff - - move.d 0xda67f19f,r3 - move.d 32,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67ffff - - move.d 0xda67f19f,r3 - move.d 33,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67ffff - - move.d 0xda67f19f,r3 - move.d 66,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67fc67 - - moveq -1,r3 - moveq 0,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - asr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 15,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5a67719f,r3 - moveq 12,r4 - asr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a670007 - - move.d 0xda67f19f,r3 - move.d 31,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1ff - - move.d 0xda67f19f,r3 - move.d 32,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1ff - - move.d 0xda67f19f,r3 - move.d 33,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1ff - - move.d 0xda67f19f,r3 - move.d 66,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1e7 - - moveq -1,r3 - moveq 0,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - asr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 15,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 7,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - -; FIXME: was wrong. - move.d 0x5a67f19f,r3 - moveq 12,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f1ff - -; FIXME: was wrong. - move.d 0x5a67f19f,r3 - moveq 4,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f1f9 - - move.d 0x5a67f19f,r3 - asrq 31,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x5a67419f,r3 - moveq 16,r4 - asr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 5a670000 - - quit diff --git a/tests/tcg/cris/bare/check_ba.s b/tests/tcg/cris/bare/check_ba.s deleted file mode 100644 index 873a4086c5..0000000000 --- a/tests/tcg/cris/bare/check_ba.s +++ /dev/null @@ -1,93 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: a\n - - - .set smalloffset,0 - .set largeoffset,0 - - - .macro fail - jump _fail - .endm - - .global main -main: - moveq 0,$r3 - -; Short forward branch. - ba 0f - addq 1,$r3 - fail - -; Max short forward branch. -1: - ba 2f - addq 1,$r3 - fail - -; Short backward branch. -0: - ba 1b - addq 1,$r3 - fail - - .space 254-2+smalloffset+1b-.,0 - moveq 0,$r3 - -2: -; Transit branch (long). - ba 3f - addq 1,$r3 - fail - - moveq 0,$r3 -4: -; Long forward branch. - ba 5f - addq 1,$r3 - fail - - .space 256-2-smalloffset+4b-.,0 - - moveq 0,$r3 - -; Max short backward branch. -3: - ba 4b - addq 1,$r3 - fail - -5: -; Max long forward branch. - ba 6f - addq 1,$r3 - fail - - .space 32766+largeoffset-2+5b-.,0 - - moveq 0,$r3 -6: -; Transit branch. - ba 7f - addq 1,$r3 - fail - - moveq 0,$r3 -9: - jsr pass - nop - -; Transit branch. - moveq 0,$r3 -7: - ba 8f - addq 1,$r3 - fail - - .space 32768-largeoffset+9b-.,0 - -8: -; Max long backward branch. - ba 9b - addq 1,$r3 - fail diff --git a/tests/tcg/cris/bare/check_bas.s b/tests/tcg/cris/bare/check_bas.s deleted file mode 100644 index 11929d4202..0000000000 --- a/tests/tcg/cris/bare/check_bas.s +++ /dev/null @@ -1,102 +0,0 @@ -# mach: crisv32 -# output: 0\n0\n0\nfb349abc\n0\n12124243\n0\n0\neab5baad\n0\nefb37832\n - - .include "testutils.inc" - start -x: - setf zncv - bsr 0f - nop -0: - test_cc 1 1 1 1 - move srp,r3 - sub.d 0b,r3 - checkr3 0 - - bas 1f,mof - moveq 0,r0 -6: - nop - quit - -2: - move srp,r3 - sub.d 3f,r3 - checkr3 0 - move srp,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 fb349abc - - basc 4f,mof - nop - .dword 0x12124243 -7: - nop - quit - -8: - move mof,r3 - sub.d 7f,r3 - checkr3 0 - - move mof,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 eab5baad - - jasc 9f,mof - nop - .dword 0xefb37832 -0: - quit - - quit -9: - move mof,r3 - sub.d 0b,r3 - checkr3 0 - - move mof,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 efb37832 - - quit - -4: - move mof,r3 - sub.d 7b,r3 - checkr3 0 - move mof,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 12124243 - basc 5f,bz - moveq 0,r3 - .dword 0x7634aeba - quit - - .space 32770,0 -1: - move mof,r3 - sub.d 6b,r3 - checkr3 0 - - bsrc 2b - nop - .dword 0xfb349abc -3: - - quit - -5: - move mof,r3 - sub.d 7b,r3 - checkr3 0 - move.d 8b,r6 - jasc r6,mof - nop - .dword 0xeab5baad -7: - quit diff --git a/tests/tcg/cris/bare/check_bcc.s b/tests/tcg/cris/bare/check_bcc.s deleted file mode 100644 index c57ffa6fa3..0000000000 --- a/tests/tcg/cris/bare/check_bcc.s +++ /dev/null @@ -1,197 +0,0 @@ - .global main - .type main, @function -main: - clearf nzvc - setf nzv - bcc 0f - addq 1, $r3 - jump dofail - -0: - clearf nzvc - setf nzv - bcs dofail - addq 1,$r3 - - clearf nzvc - setf ncv - bne 1f - addq 1, $r3 - -fail: -dofail: - jump _fail - -1: - clearf nzvc - setf ncv - beq dofail - addq 1,$r3 - - clearf nzvc - setf ncz - bvc 2f - addq 1,$r3 - jump dofail - -2: - clearf nzvc - setf ncz - bvs dofail - addq 1,$r3 - - clearf nzvc - setf vcz - bpl 3f - addq 1,$r3 - jump fail -3: - clearf nzvc - setf vcz - bmi dofail - addq 1,$r3 - - clearf nzvc - setf nv - bls dofail - addq 1,$r3 - - clearf nzvc - setf nv - bhi 4f - addq 1,$r3 - jump dofail - -4: - clearf nzvc - setf zc - bge 5f - addq 1,$r3 - jump dofail - -5: - clearf nzvc - setf zc - blt dofail - addq 1,$r3 - - clearf nzvc - setf c - bgt 6f - addq 1,$r3 - jump fail - -6: - clearf nzvc - setf c - ble dofail - addq 1,$r3 - -;;;;;;;;;; - - setf nzvc - clearf nzv - bcc dofail - addq 1,$r3 - - setf nzvc - clearf nzv - bcs 0f - addq 1,$r3 - jump fail - -0: - setf nzvc - clearf ncv - bne dofail - addq 1,$r3 - - setf nzvc - clearf ncv - beq 1f - addq 1,$r3 - jump fail - -1: - setf nzvc - clearf ncz - bvc dofail - addq 1,$r3 - - setf nzvc - clearf ncz - bvs 2f - addq 1,$r3 - jump fail - -2: - setf nzvc - clearf vcz - bpl dofail - addq 1,$r3 - - setf nzvc - clearf vcz - bmi 3f - addq 1,$r3 - jump fail - -3: - setf nzvc - clearf nv - bls 4f - addq 1,$r3 - jump fail - -4: - setf nzvc - clearf nv - bhi dofail - addq 1,$r3 - - setf zvc - clearf nzc - bge dofail - addq 1,$r3 - - setf nzc - clearf vzc - blt 5f - addq 1,$r3 - jump fail - -5: - setf nzvc - clearf c - bgt dofail - addq 1,$r3 - - setf nzvc - clearf c - ble 6f - addq 1,$r3 - jump fail - -6: - ; do a forward branch. - ba 2f - nop - .fill 100 -1: - ba 3f - nop - .fill 800 -2: - ba 1b - nop - .fill 1024 -3: - - moveq 31, $r0 -1: bne 1b - subq 1, $r0 - - jsr pass - moveq 0, $r10 - ret - nop diff --git a/tests/tcg/cris/bare/check_boundc.s b/tests/tcg/cris/bare/check_boundc.s deleted file mode 100644 index fb9e5bc905..0000000000 --- a/tests/tcg/cris/bare/check_boundc.s +++ /dev/null @@ -1,101 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n5432f789\n2\nffff\n2\nffff\nffff\nf789\n2\n2\nff\nff\nff\n89\n0\nff\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - bound.d 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - bound.d 0xffffffff,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - bound.d 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - bound.d 0xffffffff,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - bound.d 0x5432f789,r3 - test_move_cc 0 0 0 0 - checkr3 5432f789 - - moveq -1,r3 - bound.w 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq -1,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq 2,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0x78134452,r3 - bound.w 0xf789,r3 - test_move_cc 0 0 0 0 - checkr3 f789 - - moveq -1,r3 - bound.b 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq -1,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0xff,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0x78134452,r3 - bound.b 0x89,r3 - test_move_cc 0 0 0 0 - checkr3 89 - - bound.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xffff,r3 - bound.b -1,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - quit diff --git a/tests/tcg/cris/bare/check_boundr.s b/tests/tcg/cris/bare/check_boundr.s deleted file mode 100644 index 5c50cc5f6a..0000000000 --- a/tests/tcg/cris/bare/check_boundr.s +++ /dev/null @@ -1,125 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n5432f789\n2\n2\nffff\nffff\nffff\nf789\n2\n2\nff\nff\n89\nfeda4953\nfeda4962\n0\n0\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r4 - move.d r4,r3 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - bound.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5432f789 - - moveq -1,r3 - moveq 2,r4 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq -1,r3 - bound.w r3,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0xffff,r4 - move.d r4,r3 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r4 - move.d r4,r3 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 f789 - - moveq -1,r3 - moveq 2,r4 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xff,r4 - move.d r4,r3 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r4 - move.d r4,r3 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 89 - - move.d 0xfeda4956,r3 - move.d 0xfeda4953,r4 - bound.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 feda4953 - - move.d 0xfeda4962,r3 - move.d 0xfeda4963,r4 - bound.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 feda4962 - - move.d 0xfeda4956,r3 - move.d 0,r4 - bound.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xfeda4956,r4 - move.d 0,r3 - bound.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_btst.s b/tests/tcg/cris/bare/check_btst.s deleted file mode 100644 index 485deb2006..0000000000 --- a/tests/tcg/cris/bare/check_btst.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1111\n - - .include "testutils.inc" - start - clearf nzvc - moveq -1,r3 - .if 1 ;..asm.arch.cris.v32 - .else - setf vc - .endif - btstq 0,r3 - test_cc 1 0 0 0 - - moveq 2,r3 - btstq 1,r3 - test_cc 1 0 0 0 - - moveq 4,r3 - btstq 1,r3 - test_cc 0 1 0 0 - - moveq -1,r3 - btstq 31,r3 - test_cc 1 0 0 0 - - move.d 0x5a67f19f,r3 - btstq 12,r3 - test_cc 1 0 0 0 - - move.d 0xda67f19f,r3 - move.d 29,r4 - btst r4,r3 - test_cc 0 0 0 0 - - move.d 0xda67f19f,r3 - move.d 32,r4 - btst r4,r3 - test_cc 1 0 0 0 - - move.d 0xda67f191,r3 - move.d 33,r4 - btst r4,r3 - test_cc 0 0 0 0 - - moveq -1,r3 - moveq 0,r4 - btst r4,r3 - test_cc 1 0 0 0 - - moveq 2,r3 - moveq 1,r4 - btst r4,r3 - test_cc 1 0 0 0 - - moveq -1,r3 - moveq 31,r4 - btst r4,r3 - test_cc 1 0 0 0 - - moveq 4,r3 - btstq 1,r3 - test_cc 0 1 0 0 - - moveq -1,r3 - moveq 15,r4 - btst r4,r3 - test_cc 1 0 0 0 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - btst r4,r3 - test_cc 1 0 0 0 - - move.d 0x5a678000,r3 - moveq 11,r4 - btst r4,r3 - test_cc 0 1 0 0 - - move.d 0x5a67f19f,r3 - btst r3,r3 - test_cc 0 0 0 0 - - move.d 0x1111,r3 - checkr3 1111 - - ; check that X gets cleared and that only the NZ flags are touched. - ;; move.d 0xff, $r0 - ;; move $r0, $ccs - ;; btst r3,r3 - ;; move $ccs, $r0 - ;; and.d 0xff, $r0 - ;; cmp.d 0xe3, $r0 - ;; test_cc 0 1 0 0 - - quit diff --git a/tests/tcg/cris/bare/check_clearfv32.s b/tests/tcg/cris/bare/check_clearfv32.s deleted file mode 100644 index 4e91360273..0000000000 --- a/tests/tcg/cris/bare/check_clearfv32.s +++ /dev/null @@ -1,19 +0,0 @@ -# mach: crisv32 -# output: ef\nef\n - -; Check that "clearf x" doesn't trivially fail. - - .include "testutils.inc" - start - setf puixnzvc - clearf x ; Actually, x would be cleared by almost-all other insns. - move ccs,r3 - and.d 0xff, $r3 - checkr3 ef - - setf puixnzvc - moveq 0, $r3 ; moveq should only clear the xflag. - move ccs,r3 - and.d 0xff, $r3 - checkr3 ef - quit diff --git a/tests/tcg/cris/bare/check_clrjmp1.s b/tests/tcg/cris/bare/check_clrjmp1.s deleted file mode 100644 index 45a7005e24..0000000000 --- a/tests/tcg/cris/bare/check_clrjmp1.s +++ /dev/null @@ -1,36 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff00\n - -; A bug resulting in a non-effectual clear.b discovered running the GCC -; testsuite; jump actually wrote to p0. - - .include "testutils.inc" - - start - jump 1f - nop - .p2align 8 -1: - move.d y,r4 - - .if 0 ;0 == ..asm.arch.cris.v32 -; There was a bug causing this insn to set special register p0 -; (byte-clear) to 8 (low 8 bits of location after insn). - jump [r4+] - .endif - -1: - move.d 0f,r4 - -; The corresponding bug would cause this insn too, to set p0. - jump r4 - nop - quit -0: - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - quit - -y: - .dword 1b diff --git a/tests/tcg/cris/bare/check_cmp-2.s b/tests/tcg/cris/bare/check_cmp-2.s deleted file mode 100644 index 414d370517..0000000000 --- a/tests/tcg/cris/bare/check_cmp-2.s +++ /dev/null @@ -1,15 +0,0 @@ - - -.include "testutils.inc" - - start - - move.d 4294967283, $r0 - move.d $r0, $r10 - cmp.d $r0, $r10 - beq 1f - move.d $r10, $r3 - fail -1: - pass - quit diff --git a/tests/tcg/cris/bare/check_cmpc.s b/tests/tcg/cris/bare/check_cmpc.s deleted file mode 100644 index 267c9ba8c0..0000000000 --- a/tests/tcg/cris/bare/check_cmpc.s +++ /dev/null @@ -1,86 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649282\n - - .include "testutils.inc" - start - moveq -1,r3 - cmp.d -2,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.d 1,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - cmp.d -0xffff,r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmp.d 1,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmp.d -0x5432f789,r3 - test_cc 1 0 1 1 - checkr3 78134452 - - moveq -1,r3 - cmp.w -2,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.w 1,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - cmp.w 1,r3 - test_cc 1 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - cmp.w 1,r3 - test_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - cmp.w 0x877,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - moveq -1,r3 - cmp.b -2,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.b 1,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xff,r3 - cmp.b 1,r3 - test_cc 1 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r3 - cmp.b 1,r3 - test_cc 1 0 0 0 - checkr3 feda49ff - - move.d 0x78134452,r3 - cmp.b 0x77,r3 - test_cc 1 0 0 1 - checkr3 78134452 - - move.d 0x85649282,r3 - cmp.b 0x82,r3 - test_cc 0 1 0 0 - checkr3 85649282 - - quit diff --git a/tests/tcg/cris/bare/check_cmpm.s b/tests/tcg/cris/bare/check_cmpm.s deleted file mode 100644 index e4dde15b32..0000000000 --- a/tests/tcg/cris/bare/check_cmpm.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649222\n - - .include "testutils.inc" - .data -x: - .dword -2,1,-0xffff,1,-0x5432f789 - .word -2,1,1,0x877 - .byte -2,1,0x77 - .byte 0x22 - - start - moveq -1,r3 - move.d x,r5 - cmp.d [r5+],r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.d [r5],r3 - test_cc 0 0 0 0 - addq 4,r5 - checkr3 2 - - move.d 0xffff,r3 - cmp.d [r5+],r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmp.d [r5+],r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmp.d [r5+],r3 - test_cc 1 0 1 1 - checkr3 78134452 - - moveq -1,r3 - cmp.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - cmp.w [r5],r3 - test_cc 1 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - cmp.w [r5+],r3 - test_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - cmp.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 78134452 - - moveq -1,r3 - cmp.b [r5],r3 - test_cc 0 0 0 0 - addq 1,r5 - checkr3 ffffffff - - moveq 2,r3 - cmp.b [r5],r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xff,r3 - cmp.b [r5],r3 - test_cc 1 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r3 - cmp.b [r5+],r3 - test_cc 1 0 0 0 - checkr3 feda49ff - - move.d 0x78134452,r3 - cmp.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 78134452 - - move.d 0x85649222,r3 - cmp.b [r5],r3 - test_cc 0 1 0 0 - checkr3 85649222 - - quit diff --git a/tests/tcg/cris/bare/check_cmpq.s b/tests/tcg/cris/bare/check_cmpq.s deleted file mode 100644 index 5469141c91..0000000000 --- a/tests/tcg/cris/bare/check_cmpq.s +++ /dev/null @@ -1,75 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1\n1f\n1f\nffffffe1\nffffffe1\nffffffe0\n0\n0\nffffffff\nffffffff\n10000\n100\n5678900\n - - .include "testutils.inc" - start - moveq 1,r3 - cmpq 1,r3 - test_cc 0 1 0 0 - checkr3 1 - - cmpq -1,r3 - test_cc 0 0 0 1 - checkr3 1 - - cmpq 31,r3 - test_cc 1 0 0 1 - checkr3 1 - - moveq 31,r3 - cmpq 31,r3 - test_cc 0 1 0 0 - checkr3 1f - - cmpq -31,r3 - test_cc 0 0 0 1 - checkr3 1f - - movs.b -31,r3 - cmpq -31,r3 - test_cc 0 1 0 0 - checkr3 ffffffe1 - - cmpq -32,r3 - test_cc 0 0 0 0 - checkr3 ffffffe1 - - movs.b -32,r3 - cmpq -32,r3 - test_cc 0 1 0 0 - checkr3 ffffffe0 - - moveq 0,r3 - cmpq 1,r3 - test_cc 1 0 0 1 - checkr3 0 - - cmpq -32,r3 - test_cc 0 0 0 1 - checkr3 0 - - moveq -1,r3 - cmpq 1,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - cmpq -1,r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - move.d 0x10000,r3 - cmpq 1,r3 - test_cc 0 0 0 0 - checkr3 10000 - - move.d 0x100,r3 - cmpq 1,r3 - test_cc 0 0 0 0 - checkr3 100 - - move.d 0x5678900,r3 - cmpq 7,r3 - test_cc 0 0 0 0 - checkr3 5678900 - - quit diff --git a/tests/tcg/cris/bare/check_cmpr.s b/tests/tcg/cris/bare/check_cmpr.s deleted file mode 100644 index b30af7a538..0000000000 --- a/tests/tcg/cris/bare/check_cmpr.s +++ /dev/null @@ -1,102 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649222\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq -2,r4 - cmp.d r4,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - cmp.d r4,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - move.d -0xffff,r4 - cmp.d r4,r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq 1,r4 - moveq -1,r3 - cmp.d r4,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - cmp.d r4,r3 - test_cc 1 0 1 1 - checkr3 78134452 - - moveq -1,r3 - moveq -2,r4 - cmp.w r4,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - cmp.w r4,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - move.d -0xffff,r4 - cmp.w r4,r3 - test_cc 1 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - move.d -0xfedaffff,r4 - cmp.w r4,r3 - test_cc 1 0 0 0 - checkr3 fedaffff - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - cmp.w r4,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - moveq -1,r3 - moveq -2,r4 - cmp.b r4,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - cmp.b r4,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d -0xff,r4 - move.d 0xff,r3 - cmp.b r4,r3 - test_cc 1 0 0 0 - checkr3 ff - - move.d -0xfeda49ff,r4 - move.d 0xfeda49ff,r3 - cmp.b r4,r3 - test_cc 1 0 0 0 - checkr3 feda49ff - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - cmp.b r4,r3 - test_cc 1 0 0 1 - checkr3 78134452 - - move.d 0x85649222,r3 - move.d 0x77445622,r4 - cmp.b r4,r3 - test_cc 0 1 0 0 - checkr3 85649222 - - quit diff --git a/tests/tcg/cris/bare/check_cmpxc.s b/tests/tcg/cris/bare/check_cmpxc.s deleted file mode 100644 index b237a93175..0000000000 --- a/tests/tcg/cris/bare/check_cmpxc.s +++ /dev/null @@ -1,92 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\n2\n2\nffff\nffff\nffff\nffff\nffffffff\nffffffff\nffffffff\n78134452\n78134452\n78134452\n78134452\n4452\n80000032\n - - .include "testutils.inc" - start - moveq 2,r3 - cmps.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - cmps.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - cmpu.b 0xff,r3 - test_cc 1 0 0 1 - checkr3 2 - - moveq 2,r3 - move.d 0xffffffff,r4 - cmpu.w -1,r3 - test_cc 1 0 0 1 - checkr3 2 - - move.d 0xffff,r3 - cmpu.b -1,r3 - test_cc 0 0 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmpu.w -1,r3 - test_cc 0 1 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmps.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 ffff - - move.d 0xffff,r3 - cmps.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmps.b 0xff,r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - moveq -1,r3 - cmps.w 0xff,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - cmps.w 0xffff,r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmpu.b 0x89,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.b 0x89,r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x78134452,r3 - cmpu.w 0xf789,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.w 0xf789,r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x4452,r3 - cmps.w 0x8002,r3 - test_cc 0 0 0 1 - checkr3 4452 - - move.d 0x80000032,r3 - cmpu.w 0x764,r3 - test_cc 0 0 1 0 - checkr3 80000032 - - quit diff --git a/tests/tcg/cris/bare/check_cmpxm.s b/tests/tcg/cris/bare/check_cmpxm.s deleted file mode 100644 index 87ea5bf8e3..0000000000 --- a/tests/tcg/cris/bare/check_cmpxm.s +++ /dev/null @@ -1,106 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\n2\n2\nffff\nffff\nffff\nffff\nffffffff\nffffffff\nffffffff\n78134452\n78134452\n78134452\n78134452\n4452\n80000032\n - - .include "testutils.inc" - .data -x: - .byte 0xff - .word 0xffff - .word 0xff - .word 0xffff - .byte 0x89 - .word 0xf789 - .word 0x8002 - .word 0x764 - - start - moveq 2,r3 - move.d x,r5 - cmps.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - cmps.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - subq 3,r5 - cmpu.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 2 - - moveq 2,r3 - cmpu.w [r5+],r3 - test_cc 1 0 0 1 - subq 3,r5 - checkr3 2 - - move.d 0xffff,r3 - cmpu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmpu.w [r5],r3 - test_cc 0 1 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmps.b [r5],r3 - test_cc 0 0 0 1 - checkr3 ffff - - move.d 0xffff,r3 - cmps.w [r5],r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmps.b [r5],r3 - test_cc 0 1 0 0 - addq 3,r5 - checkr3 ffffffff - - moveq -1,r3 - cmps.w [r5+],r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - cmps.w [r5+],r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmpu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x78134452,r3 - cmpu.w [r5],r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x4452,r3 - cmps.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 4452 - - move.d 0x80000032,r3 - cmpu.w [r5+],r3 - test_cc 0 0 1 0 - checkr3 80000032 - - quit diff --git a/tests/tcg/cris/bare/check_dstep.s b/tests/tcg/cris/bare/check_dstep.s deleted file mode 100644 index bd43b838ea..0000000000 --- a/tests/tcg/cris/bare/check_dstep.s +++ /dev/null @@ -1,42 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: fffffffc\n4\nffff\nfffffffe\n9bf3911b\n0\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - dstep r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffffffc - - moveq 2,r3 - moveq -1,r4 - dstep r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - move.d 0xffff,r4 - move.d r4,r3 - dstep r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - dstep r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - dstep r4,r3 - test_move_cc 1 0 0 0 - checkr3 9bf3911b - - move.d 0xffff,r3 - move.d 0x1fffe,r4 - dstep r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_jsr.s b/tests/tcg/cris/bare/check_jsr.s deleted file mode 100644 index 1060237873..0000000000 --- a/tests/tcg/cris/bare/check_jsr.s +++ /dev/null @@ -1,85 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 0\n0\n0\n0\n0\n0\n - -# Test that jsr Rn and jsr [PC+] work. - - .include "testutils.inc" - start -x: - move.d 0f,r6 - setf nzvc - jsr r6 - .if 1; ..asm.arch.cris.v32 - nop - .endif -0: - test_move_cc 1 1 1 1 - move srp,r3 - sub.d 0b,r3 - checkr3 0 - - move.d 1f,r0 - setf nzvc - jsr r0 - .if 1 ; ..asm.arch.cris.v32 - moveq 0,r0 - .endif -6: - nop - quit - -2: - test_move_cc 0 0 0 0 - move srp,r3 - sub.d 3f,r3 - checkr3 0 - jsr 4f - .if 1 ; ..asm.arch.cris.v32 - nop - .endif -7: - nop - quit - -8: - move srp,r3 - sub.d 7b,r3 - checkr3 0 - quit - -4: - move srp,r3 - sub.d 7b,r3 - checkr3 0 - move.d 5f,r3 - jump r3 - .if 1; ..asm.arch.cris.v32 - moveq 0,r3 - .endif - quit - - .space 32770,0 -1: - test_move_cc 1 1 1 1 - move srp,r3 - sub.d 6b,r3 - checkr3 0 - - clearf cznv - jsr 2b - .if 1; ..asm.arch.cris.v32 - nop - .endif -3: - - quit - -5: - move srp,r3 - sub.d 7b,r3 - checkr3 0 - jump 8b - .if 1 ; ..asm.arch.cris.v32 - nop - .endif - quit diff --git a/tests/tcg/cris/bare/check_lapc.s b/tests/tcg/cris/bare/check_lapc.s deleted file mode 100644 index 9a6150b749..0000000000 --- a/tests/tcg/cris/bare/check_lapc.s +++ /dev/null @@ -1,78 +0,0 @@ -# mach: crisv32 -# output: 0\n0\nfffffffa\nfffffffe\nffffffda\n1e\n1e\n0\n - -.include "testutils.inc" - -; To accommodate dumpr3 with more than one instruction, keep it -; out of lapc operand ranges and difference calculations. - - start - lapc.d 0f,r3 -0: - sub.d .,r3 - checkr3 0 - - lapcq 0f,r3 -0: - sub.d .,r3 - checkr3 0 - - lapc.d .,r3 - sub.d .,r3 - checkr3 fffffffa - - lapcq .,r3 - sub.d .,r3 - checkr3 fffffffe - -0: - .rept 16 - nop - .endr - lapc.d 0b,r3 - sub.d .,r3 - checkr3 ffffffda - - setf zcvn - lapc.d 0f,r3 - test_cc 1 1 1 1 - sub.d .,r3 - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop -0: - checkr3 1e -0: - lapcq 0f,r3 - sub.d 0b,r3 - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop -0: - checkr3 1e - clearf cn - setf zv -1: - lapcq .,r3 - test_cc 0 1 1 0 - sub.d 1b,r3 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_lsl.s b/tests/tcg/cris/bare/check_lsl.s deleted file mode 100644 index 9e2ddd7cd0..0000000000 --- a/tests/tcg/cris/bare/check_lsl.s +++ /dev/null @@ -1,217 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n4\n80000000\nffff8000\n7f19f000\n80000000\n0\n0\n699fc67c\nffffffff\n4\n80000000\nffff8000\n7f19f000\nda670000\nda670000\nda670000\nda67c67c\nffffffff\nfffafffe\n4\nffff0000\nffff8000\n5a67f000\nda67f100\nda67f100\nda67f100\nda67f17c\nfff3faff\nfff3fafe\n4\nffffff00\nffffff00\nffffff80\n5a67f100\n5a67f1f0\n - - .include "testutils.inc" - start - moveq -1,r3 - lslq 0,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - lslq 1,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - lslq 31,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - moveq -1,r3 - lslq 15,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8000 - - move.d 0x5a67f19f,r3 - lslq 12,r3 - test_move_cc 0 0 0 0 - checkr3 7f19f000 - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsl.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsl.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsl.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 699fc67c - - moveq -1,r3 - moveq 0,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - lsl.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - moveq 31,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - moveq -1,r3 - moveq 15,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8000 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsl.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 7f19f000 - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67c67c - - moveq -1,r3 - moveq 0,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xfffaffff,r3 - moveq 1,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffafffe - - moveq 2,r3 - moveq 1,r4 - lsl.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - moveq 31,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffff0000 - - moveq -1,r3 - moveq 15,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8000 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f000 - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsl.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 da67f17c - - move.d 0xfff3faff,r3 - moveq 0,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 fff3faff - - move.d 0xfff3faff,r3 - moveq 1,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 fff3fafe - - moveq 2,r3 - moveq 1,r4 - lsl.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - moveq 31,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 15,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 7,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffff80 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 5a67f100 - - move.d 0x5a67f19f,r3 - moveq 4,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f1f0 - - quit diff --git a/tests/tcg/cris/bare/check_lsr.s b/tests/tcg/cris/bare/check_lsr.s deleted file mode 100644 index 18fdbef9b2..0000000000 --- a/tests/tcg/cris/bare/check_lsr.s +++ /dev/null @@ -1,218 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n1\n1\n1ffff\n5a67f\n1\n0\n0\n3699fc67\nffffffff\n1\n1\n1ffff\n5a67f\nda670000\nda670000\nda670000\nda673c67\nffffffff\nffff7fff\n1\nffff0000\nffff0001\n5a67000f\nda67f100\nda67f100\nda67f100\nda67f127\nffffffff\nffffff7f\n1\nffffff00\nffffff00\nffffff01\n5a67f100\n5a67f109\n - - .include "testutils.inc" - start - moveq -1,r3 - lsrq 0,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - lsrq 1,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - lsrq 31,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - lsrq 15,r3 - test_move_cc 0 0 0 0 - checkr3 1ffff - - move.d 0x5a67f19f,r3 - lsrq 12,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsr.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsr.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 3699fc67 - - moveq -1,r3 - moveq 0,r4 - lsr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 15,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1ffff - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 da673c67 - - moveq -1,r3 - moveq 0,r4 - lsr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff7fff - - moveq 2,r3 - moveq 1,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - -;; FIXME: this was wrong. Z should be set. - moveq -1,r3 - moveq 31,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffff0000 - - moveq -1,r3 - moveq 15,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0001 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67000f - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 da67f127 - - moveq -1,r3 - moveq 0,r4 - lsr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff7f - - moveq 2,r3 - moveq 1,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 15,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 7,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff01 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 5a67f100 - - move.d 0x5a67f19f,r3 - moveq 4,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f109 - - quit diff --git a/tests/tcg/cris/bare/check_mcp.s b/tests/tcg/cris/bare/check_mcp.s deleted file mode 100644 index e65ccddfd4..0000000000 --- a/tests/tcg/cris/bare/check_mcp.s +++ /dev/null @@ -1,49 +0,0 @@ -# mach: crisv32 -# output: fffffffe\n1\n1ffff\nfffffffe\ncc463bdc\n4c463bdc\n0\n - - .include "testutils.inc" - start - -; Set R, clear C. - move 0x100,ccs - moveq -5,r3 - move 2,mof - mcp mof,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - moveq 2,r3 - move -1,srp - mcp srp,r3 - test_cc 0 0 0 0 - checkr3 1 - - move 0xffff,srp - move srp,r3 - mcp srp,r3 - test_cc 0 0 0 0 - checkr3 1ffff - - move -1,mof - move mof,r3 - mcp mof,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move 0x5432f789,mof - move.d 0x78134452,r3 - mcp mof,r3 - test_cc 1 0 1 0 - checkr3 cc463bdc - - move 0x80000000,srp - mcp srp,r3 - test_cc 0 0 1 0 - checkr3 4c463bdc - - move 0xb3b9c423,srp - mcp srp,r3 - test_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movdelsr1.s b/tests/tcg/cris/bare/check_movdelsr1.s deleted file mode 100644 index 300cc87742..0000000000 --- a/tests/tcg/cris/bare/check_movdelsr1.s +++ /dev/null @@ -1,33 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: aa117acd\n -# output: eeaabb42\n - -; Bug with move to special register in delay slot, due to -; special flush-insn-cache simulator use. Ordinary move worked; -; special register caused branch to fail. - - .include "testutils.inc" - start - move -1,srp - - move.d 0xaa117acd,r1 - moveq 3,r9 - cmpq 1,r9 - bhi 0f - move.d r1,r3 - - fail -0: - checkr3 aa117acd - - move.d 0xeeaabb42,r1 - moveq 3,r9 - cmpq 1,r9 - bhi 0f - move r1,srp - - fail -0: - move srp,r3 - checkr3 eeaabb42 - quit diff --git a/tests/tcg/cris/bare/check_movecr.s b/tests/tcg/cris/bare/check_movecr.s deleted file mode 100644 index da8ec26284..0000000000 --- a/tests/tcg/cris/bare/check_movecr.s +++ /dev/null @@ -1,37 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff42\n94\nffff4321\n9234\n76543210\n76540000\n - -; Move constant byte, word, dword to register. Check that no extension is -; performed, that only part of the register is set. - - .include "testutils.inc" - startnostack - moveq -1,r3 - move.b 0x42,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff42 - - moveq 0,r3 - move.b 0x94,r3 - test_move_cc 1 0 0 0 - checkr3 94 - - moveq -1,r3 - move.w 0x4321,r3 - test_move_cc 0 0 0 0 - checkr3 ffff4321 - - moveq 0,r3 - move.w 0x9234,r3 - test_move_cc 1 0 0 0 - checkr3 9234 - - move.d 0x76543210,r3 - test_move_cc 0 0 0 0 - checkr3 76543210 - - move.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 76540000 - - quit diff --git a/tests/tcg/cris/bare/check_movei.s b/tests/tcg/cris/bare/check_movei.s deleted file mode 100644 index bbfa633373..0000000000 --- a/tests/tcg/cris/bare/check_movei.s +++ /dev/null @@ -1,50 +0,0 @@ -# mach: crisv32 -# output: fffffffe\n -# output: fffffffe\n - -; Check basic integral-write semantics regarding flags. - - .include "testutils.inc" - start - - move.d 0, $r3 -; A write that works. Check that flags are set correspondingly. - move.d d,r4 - ;; store to bring it into the tlb with the right prot bits - move.d r3,[r4] - moveq -2,r5 - setf c - clearf p - move.d [r4],r3 - ax - move.d r5,[r4] - move.d [r4],r3 - - bcc 0f - nop - fail - -0: - checkr3 fffffffe - -; A write that fails; check flags too. - move.d d,r4 - moveq 23,r5 - setf p - clearf c - move.d [r4],r3 - ax - move.d r5,[r4] - move.d [r4],r3 - - bcs 0f - nop - fail - -0: - checkr3 fffffffe - quit - - .data -d: - .dword 42424242 diff --git a/tests/tcg/cris/bare/check_movemr.s b/tests/tcg/cris/bare/check_movemr.s deleted file mode 100644 index 88489dee31..0000000000 --- a/tests/tcg/cris/bare/check_movemr.s +++ /dev/null @@ -1,78 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 12345678\n10234567\n12345678\n12344567\n12344523\n76543210\nffffffaa\naa\n9911\nffff9911\n78\n56\n3456\n6712\n - - .include "testutils.inc" - start - - .data -mem1: - .dword 0x12345678 -mem2: - .word 0x4567 -mem3: - .byte 0x23 - .dword 0x76543210 - .byte 0xaa,0x11,0x99 - - .text - move.d mem1,r2 - move.d [r2],r3 - test_move_cc 0 0 0 0 - checkr3 12345678 - - move.d mem2,r3 - move.d [r3],r3 - test_move_cc 0 0 0 0 - checkr3 10234567 - - move.d mem1,r2 - move.d [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 12345678 - - move.w [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 12344567 - - move.b [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 12344523 - - move.d [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 76543210 - - movs.b [r2],r3 - test_move_cc 1 0 0 0 - checkr3 ffffffaa - - movu.b [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 aa - - movu.w [r2],r3 - test_move_cc 0 0 0 0 - checkr3 9911 - - movs.w [r2+],r3 - test_move_cc 1 0 0 0 - checkr3 ffff9911 - - move.d mem1,r13 - movs.b [r13+],r3 - test_move_cc 0 0 0 0 - checkr3 78 - - movu.b [r13],r3 - test_move_cc 0 0 0 0 - checkr3 56 - - movs.w [r13+],r3 - test_move_cc 0 0 0 0 - checkr3 3456 - - movu.w [r13+],r3 - test_move_cc 0 0 0 0 - checkr3 6712 - - quit diff --git a/tests/tcg/cris/bare/check_movemrv32.s b/tests/tcg/cris/bare/check_movemrv32.s deleted file mode 100644 index 53950abd5b..0000000000 --- a/tests/tcg/cris/bare/check_movemrv32.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv32 -# output: 15\n7\n2\nffff1234\nb\n16\nf\n2\nffffffef\nf\nffff1234\nf\nfffffff4\nd\nfffffff2\n10\nfffffff2\nd\n - - .include "testutils.inc" - .data -x: - .dword 8,9,10,11 -y: - .dword -12,13,-14,15,16 - - start - moveq 7,r0 - moveq 2,r1 - move.d 0xffff1234,r2 - moveq 21,r3 - move.d x,r4 - setf zcvn - movem r2,[r4+] - test_cc 1 1 1 1 - subq 12,r4 - - checkr3 15 - - move.d [r4+],r3 - checkr3 7 - - move.d [r4+],r3 - checkr3 2 - - move.d [r4+],r3 - checkr3 ffff1234 - - move.d [r4+],r3 - checkr3 b - - subq 16,r4 - moveq 22,r0 - moveq 15,r1 - clearf zcvn - movem r0,[r4] - test_cc 0 0 0 0 - move.d [r4+],r3 - checkr3 16 - - move.d r1,r3 - checkr3 f - - move.d [r4+],r3 - checkr3 2 - - subq 8,r4 - moveq 10,r2 - moveq -17,r0 - clearf zc - setf vn - movem r1,[r4] - test_cc 1 0 1 0 - move.d [r4+],r3 - checkr3 ffffffef - - move.d [r4+],r3 - checkr3 f - - move.d [r4+],r3 - checkr3 ffff1234 - - move.d y,r4 - setf zc - clearf vn - movem [r4+],r3 - test_cc 0 1 0 1 - checkr3 f - - move.d r0,r3 - checkr3 fffffff4 - - move.d r1,r3 - checkr3 d - - move.d r2,r3 - checkr3 fffffff2 - - move.d [r4],r3 - checkr3 10 - - subq 8,r4 - setf zcvn - movem [r4+],r0 - test_cc 1 1 1 1 - move.d r0,r3 - checkr3 fffffff2 - - move.d r1,r3 - checkr3 d - - quit diff --git a/tests/tcg/cris/bare/check_mover.s b/tests/tcg/cris/bare/check_mover.s deleted file mode 100644 index b4db595d64..0000000000 --- a/tests/tcg/cris/bare/check_mover.s +++ /dev/null @@ -1,28 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff05\nffff0005\n5\nffffff00\n - -; Move between registers. Check that just the subreg is copied. - - .include "testutils.inc" - startnostack - moveq -30,r3 - moveq 5,r4 - move.b r4,r3 - test_move_cc 0 0 0 0 ; FIXME - checkr3 ffffff05 - - move.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0005 - - move.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq -1,r3 - moveq 0,r4 - move.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - quit diff --git a/tests/tcg/cris/bare/check_moverm.s b/tests/tcg/cris/bare/check_moverm.s deleted file mode 100644 index eabc9588d4..0000000000 --- a/tests/tcg/cris/bare/check_moverm.s +++ /dev/null @@ -1,45 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 7823fec2\n10231879\n102318fe\n - - .include "testutils.inc" - start - - .data -mem1: - .dword 0x12345678 -mem2: - .word 0x4567 -mem3: - .byte 0x23 - .dword 0x76543210 - .byte 0xaa,0x11,0x99 - - .text - move.d mem1,r2 - move.d 0x7823fec2,r4 - setf nzvc - move.d r4,[r2+] - test_cc 1 1 1 1 - subq 4,r2 - move.d [r2],r3 - checkr3 7823fec2 - - move.d mem2,r3 - move.d 0x45231879,r4 - clearf nzvc - move.w r4,[r3] - test_cc 0 0 0 0 - move.d [r3],r3 - checkr3 10231879 - - move.d mem2,r2 - moveq -2,r4 - clearf nc - setf zv - move.b r4,[r2+] - test_cc 0 1 1 0 - subq 1,r2 - move.d [r2],r3 - checkr3 102318fe - - quit diff --git a/tests/tcg/cris/bare/check_movmp.s b/tests/tcg/cris/bare/check_movmp.s deleted file mode 100644 index 7fc11f064d..0000000000 --- a/tests/tcg/cris/bare/check_movmp.s +++ /dev/null @@ -1,131 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n - -# Test generic "move Ps,[]" and "move [],Pd" insns; the ones with -# functionality common to all models. - - .include "testutils.inc" - start - - .data -filler: - .byte 0xaa - .word 0x4433 - .dword 0x55778866 - .byte 0xcc - - .text -; Test that writing to zero-registers is a nop - .if 0 - ; We used to just ignore the writes, but now an error is emitted. We - ; keep the test-code but disabled, in case we need to change this again. - move 0xaa,p0 - move 0x4433,p4 - move 0x55774433,p8 - .endif - - moveq -1,r3 - setf zcvn - clear.b r3 - test_cc 1 1 1 1 - checkr3 ffffff00 - - moveq -1,r3 - clearf zcvn - clear.w r3 - test_cc 0 0 0 0 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - -; "Write" using ordinary memory references too. - .if 0 ; See ".if 0" above. - move.d filler,r6 - move [r6],p0 - move [r6],p4 - move [r6],p8 - .endif - -# ffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n - - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - - moveq -1,r3 - clear.w r3 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - -; And postincremented. - .if 0 ; See ".if 0" above. - move [r6+],p0 - move [r6+],p4 - move [r6+],p8 - .endif - -# ffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n - - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - - moveq -1,r3 - clear.w r3 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - -; Now see that we can write to the registers too. -# bb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n -; [PC+] - move.d filler,r9 - move 0xbb113344,srp - move srp,r3 - checkr3 bb113344 - -; [R+] - move [r9+],srp - move srp,r3 - checkr3 664433aa - -; [R] - move [r9],srp - move srp,r3 - checkr3 cc557788 - -; And check writing to memory, clear and srp. - - move.d filler,r9 - move 0xabcde012,srp - setf zcvn - move srp,[r9+] - test_cc 1 1 1 1 - subq 4,r9 - move.d [r9],r3 - checkr3 abcde012 - - clearf zcvn - clear.b [r9] - test_cc 0 0 0 0 - move.d [r9],r3 - checkr3 abcde000 - - addq 2,r9 - clear.w [r9+] - subq 2,r9 - move.d [r9],r3 - checkr3 77880000 - - clear.d [r9] - move.d [r9],r3 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movpmv32.s b/tests/tcg/cris/bare/check_movpmv32.s deleted file mode 100644 index daf0970e4a..0000000000 --- a/tests/tcg/cris/bare/check_movpmv32.s +++ /dev/null @@ -1,35 +0,0 @@ -# mach: crisv32 -# output: 11223320\nbb113344\naa557711\n - -# Test v32-specific special registers. FIXME: more registers. - - .include "testutils.inc" - start - .data -store: - .dword 0x11223344 - .dword 0x77665544 - - .text - moveq -1,r3 - move.d store,r4 - move vr,[r4] - move [r4+],mof - move mof,r3 - checkr3 11223320 - - moveq -1,r3 - clearf zcvn - move 0xbb113344,mof - test_cc 0 0 0 0 - move mof,r3 - checkr3 bb113344 - - setf zcvn - move 0xaa557711,mof - test_cc 1 1 1 1 - move mof,[r4] - move.d [r4],r3 - checkr3 aa557711 - - quit diff --git a/tests/tcg/cris/bare/check_movpr.s b/tests/tcg/cris/bare/check_movpr.s deleted file mode 100644 index eef9bdb4fb..0000000000 --- a/tests/tcg/cris/bare/check_movpr.s +++ /dev/null @@ -1,28 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff00\nffff0000\n0\nbb113344\n - -# Test generic "move Ps,Rd" and "move Rs,Pd" insns; the ones with -# functionality common to all models. - - .include "testutils.inc" - start - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - - moveq -1,r3 - clear.w r3 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - - moveq -1,r3 - move.d 0xbb113344,r4 - setf zcvn - move r4,srp - move srp,r3 - test_cc 1 1 1 1 - checkr3 bb113344 - quit diff --git a/tests/tcg/cris/bare/check_movprv32.s b/tests/tcg/cris/bare/check_movprv32.s deleted file mode 100644 index d0d90e1246..0000000000 --- a/tests/tcg/cris/bare/check_movprv32.s +++ /dev/null @@ -1,21 +0,0 @@ -# mach: crisv32 -# output: ffffff20\nbb113344\n - -# Test v32-specific special registers. FIXME: more registers. - - .include "testutils.inc" - start - moveq -1,r3 - setf zcvn - move vr,r3 - test_cc 1 1 1 1 - checkr3 ffffff20 - - moveq -1,r3 - move.d 0xbb113344,r4 - clearf cvnz - move r4,mof - test_cc 0 0 0 0 - move mof,r3 - checkr3 bb113344 - quit diff --git a/tests/tcg/cris/bare/check_movscr.s b/tests/tcg/cris/bare/check_movscr.s deleted file mode 100644 index 53c8ce6b50..0000000000 --- a/tests/tcg/cris/bare/check_movscr.s +++ /dev/null @@ -1,29 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 42\nffffff85\n7685\nffff8765\n0\n - -; Move constant byte, word, dword to register. Check that sign-extension -; is performed. - - .include "testutils.inc" - start - moveq -1,r3 - movs.b 0x42,r3 - checkr3 42 - - movs.b 0x85,r3 - test_move_cc 1 0 0 0 - checkr3 ffffff85 - - movs.w 0x7685,r3 - test_move_cc 0 0 0 0 - checkr3 7685 - - movs.w 0x8765,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8765 - - movs.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movsm.s b/tests/tcg/cris/bare/check_movsm.s deleted file mode 100644 index 7074336e78..0000000000 --- a/tests/tcg/cris/bare/check_movsm.s +++ /dev/null @@ -1,44 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nfffffff5\n5\nfffffff5\n0\n - -; Movs between registers. Check that sign-extension is performed and the -; full register is set. - - .include "testutils.inc" - - .data -x: - .byte 5,-11 - .word 5,-11 - .word 0 - - start - move.d x,r5 - - moveq -1,r3 - movs.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r3 - movs.b [r5],r3 - test_move_cc 1 0 0 0 - addq 1,r5 - checkr3 fffffff5 - - moveq -1,r3 - movs.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r3 - movs.w [r5],r3 - test_move_cc 1 0 0 0 - addq 2,r5 - checkr3 fffffff5 - - movs.w [r5],r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movsr.s b/tests/tcg/cris/bare/check_movsr.s deleted file mode 100644 index d1889a7a1b..0000000000 --- a/tests/tcg/cris/bare/check_movsr.s +++ /dev/null @@ -1,46 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nfffffff5\n5\nfffffff5\n0\n - -; Movs between registers. Check that sign-extension is performed and the -; full register is set. - - .include "testutils.inc" - start - moveq -1,r5 - moveq 5,r4 - move.b r4,r5 - moveq -1,r3 - movs.b r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.b r4,r5 - moveq 0,r3 - movs.b r5,r3 - test_move_cc 1 0 0 0 - checkr3 fffffff5 - - moveq -1,r5 - moveq 5,r4 - move.w r4,r5 - moveq -1,r3 - movs.w r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.w r4,r5 - moveq 0,r3 - movs.w r5,r3 - test_move_cc 1 0 0 0 - checkr3 fffffff5 - - moveq 0,r5 - movs.b r5,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movucr.s b/tests/tcg/cris/bare/check_movucr.s deleted file mode 100644 index 7c8487d1a2..0000000000 --- a/tests/tcg/cris/bare/check_movucr.s +++ /dev/null @@ -1,33 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 42\n85\n7685\n8765\n0\n - -; Move constant byte, word, dword to register. Check that zero-extension -; is performed. - - .include "testutils.inc" - start - moveq -1,r3 - movu.b 0x42,r3 - test_move_cc 0 0 0 0 - checkr3 42 - - moveq -1,r3 - movu.b 0x85,r3 - test_move_cc 0 0 0 0 - checkr3 85 - - moveq -1,r3 - movu.w 0x7685,r3 - test_move_cc 0 0 0 0 - checkr3 7685 - - moveq -1,r3 - movu.w 0x8765,r3 - test_move_cc 0 0 0 0 - checkr3 8765 - - movu.b 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movum.s b/tests/tcg/cris/bare/check_movum.s deleted file mode 100644 index 038e539463..0000000000 --- a/tests/tcg/cris/bare/check_movum.s +++ /dev/null @@ -1,40 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nf5\n5\nfff5\n0\n - -; Movu between registers. Check that zero-extension is performed and the -; full register is set. - - .include "testutils.inc" - - .data -x: - .byte 5,-11 - .word 5,-11 - .word 0 - - start - move.d x,r5 - - movu.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - movu.b [r5],r3 - test_move_cc 0 0 0 0 - addq 1,r5 - checkr3 f5 - - movu.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - movu.w [r5],r3 - test_move_cc 0 0 0 0 - addq 2,r5 - checkr3 fff5 - - movu.w [r5],r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movur.s b/tests/tcg/cris/bare/check_movur.s deleted file mode 100644 index 3ecf475f75..0000000000 --- a/tests/tcg/cris/bare/check_movur.s +++ /dev/null @@ -1,45 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nf5\n5\nfff5\n0\n - -; Movu between registers. Check that zero-extension is performed and the -; full register is set. - - .include "testutils.inc" - start - moveq -1,r5 - moveq 5,r4 - move.b r4,r5 - moveq -1,r3 - movu.b r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.b r4,r5 - moveq -1,r3 - movu.b r5,r3 - test_move_cc 0 0 0 0 - checkr3 f5 - - moveq -1,r5 - moveq 5,r4 - move.w r4,r5 - moveq -1,r3 - movu.w r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.w r4,r5 - moveq -1,r3 - movu.w r5,r3 - test_move_cc 0 0 0 0 - checkr3 fff5 - - movu.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_mulv32.s b/tests/tcg/cris/bare/check_mulv32.s deleted file mode 100644 index f379358765..0000000000 --- a/tests/tcg/cris/bare/check_mulv32.s +++ /dev/null @@ -1,51 +0,0 @@ -# mach: crisv32 -# output: fffffffe\n -# output: ffffffff\n -# output: fffffffe\n -# output: 1\n -# output: fffffffe\n -# output: ffffffff\n -# output: fffffffe\n -# output: 1\n - -; Check that carry is not modified on v32. - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - setf c - muls.d r4,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - setf c - mulu.d r4,r3 - test_cc 0 0 1 1 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - moveq -1,r3 - moveq 2,r4 - clearf c - muls.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - clearf c - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - quit diff --git a/tests/tcg/cris/bare/check_mulx.s b/tests/tcg/cris/bare/check_mulx.s deleted file mode 100644 index a7a1f82a82..0000000000 --- a/tests/tcg/cris/bare/check_mulx.s +++ /dev/null @@ -1,257 +0,0 @@ -# mach: crisv10 crisv32 -# output: fffffffe\nffffffff\nfffffffe\n1\nfffffffe\nffffffff\nfffffffe\n1\nfffe0001\n0\nfffe0001\n0\n1\n0\n1\nfffffffe\n193eade2\n277e3a49\n193eade2\n277e3a49\nfffffffe\nffffffff\n1fffe\n0\nfffffffe\nffffffff\n1fffe\n0\n1\n0\nfffe0001\n0\nfdbdade2\nffffffff\n420fade2\n0\nfffffffe\nffffffff\n1fe\n0\nfffffffe\nffffffff\n1fe\n0\n1\n0\nfe01\n0\n1\n0\nfe01\n0\nffffd9e2\nffffffff\n2be2\n0\n0\n0\n0\n0\n - - .include "testutils.inc" - start - - .align 4 - moveq -1,r3 - moveq 2,r4 - muls.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - .align 4 - moveq -1,r3 - moveq 2,r4 - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - .align 4 - moveq 2,r3 - moveq -1,r4 - muls.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - .align 4 - moveq 2,r3 - moveq -1,r4 - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - muls.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffe0001 - move mof,r3 - checkr3 0 - - move.d 0xffff,r4 - move.d r4,r3 - mulu.d r4,r3 - test_cc 0 0 0 0 - checkr3 fffe0001 - move mof,r3 - checkr3 0 - - moveq -1,r4 - move.d r4,r3 - muls.d r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - - moveq -1,r4 - move.d r4,r3 - mulu.d r4,r3 - test_cc 1 0 1 0 - checkr3 1 - move mof,r3 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - muls.d r4,r3 - test_cc 0 0 1 0 - checkr3 193eade2 - move mof,r3 - checkr3 277e3a49 - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 193eade2 - move mof,r3 - checkr3 277e3a49 - - move.d 0xffff,r3 - moveq 2,r4 - muls.w r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - move mof,r3 - checkr3 0 - nop - - moveq 2,r3 - move.d 0xffff,r4 - muls.w r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq 2,r3 - moveq -1,r4 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - move mof,r3 - checkr3 0 - - move.d 0xffff,r4 - move.d r4,r3 - muls.w r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - - moveq -1,r4 - move.d r4,r3 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 fffe0001 - move mof,r3 - checkr3 0 - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - muls.w r4,r3 - test_cc 1 0 0 0 - checkr3 fdbdade2 - move mof,r3 - checkr3 ffffffff - nop - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 420fade2 - move mof,r3 - checkr3 0 - nop - - move.d 0xff,r3 - moveq 2,r4 - muls.b r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 1fe - move mof,r3 - checkr3 0 - - moveq 2,r3 - moveq -1,r4 - muls.b r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq 2,r3 - moveq -1,r4 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 1fe - move mof,r3 - checkr3 0 - - move.d 0xff,r4 - move.d r4,r3 - muls.b r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - nop - - moveq -1,r4 - move.d r4,r3 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 fe01 - move mof,r3 - checkr3 0 - nop - - move.d 0xfeda49ff,r4 - move.d r4,r3 - muls.b r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - nop - - move.d 0xfeda49ff,r4 - move.d r4,r3 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 fe01 - move mof,r3 - checkr3 0 - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - muls.b r4,r3 - test_cc 1 0 0 0 - checkr3 ffffd9e2 - move mof,r3 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 2be2 - move mof,r3 - checkr3 0 - - moveq 0,r3 - move.d 0xf87f4aeb,r4 - muls.d r4,r3 - test_cc 0 1 0 0 - checkr3 0 - move mof,r3 - checkr3 0 - - move.d 0xf87f4aeb,r3 - moveq 0,r4 - mulu.d r4,r3 - test_cc 0 1 0 0 - checkr3 0 - move mof,r3 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_neg.s b/tests/tcg/cris/bare/check_neg.s deleted file mode 100644 index 963c4b6f5e..0000000000 --- a/tests/tcg/cris/bare/check_neg.s +++ /dev/null @@ -1,104 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\nffffffff\n0\n80000000\n1\nba987655\nffff\nffff\n0\n89ab8000\nffff0001\n45677655\nff\nff\n0\n89abae80\nffffff01\n45678955\n - - .include "testutils.inc" - start - moveq 0,r3 - moveq 1,r4 - neg.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 1,r3 - moveq 0,r4 - neg.d r3,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - -;; FIXME: this was wrong. - moveq 0,r3 - neg.d r3,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x80000000,r3 - neg.d r3,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - moveq -1,r3 - neg.d r3,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - move.d 0x456789ab,r3 - neg.d r3,r3 - test_move_cc 1 0 0 0 - checkr3 ba987655 - - moveq 0,r3 - moveq 1,r4 - neg.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffff - - moveq 1,r3 - moveq 0,r4 - neg.w r3,r3 - test_move_cc 1 0 0 0 - checkr3 ffff - - moveq 0,r3 - neg.w r3,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x89ab8000,r3 - neg.w r3,r3 - test_move_cc 1 0 0 0 - checkr3 89ab8000 - - moveq -1,r3 - neg.w r3,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0001 - - move.d 0x456789ab,r3 - neg.w r3,r3 - test_move_cc 0 0 0 0 - checkr3 45677655 - - moveq 0,r3 - moveq 1,r4 - neg.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ff - - moveq 1,r3 - moveq 0,r4 - neg.b r3,r3 - test_move_cc 1 0 0 0 - checkr3 ff - - moveq 0,r3 - neg.b r3,r3 - test_move_cc 0 1 0 0 - checkr3 0 - -;; FIXME: was wrong. - move.d 0x89abae80,r3 - neg.b r3,r3 - test_move_cc 1 0 0 1 - checkr3 89abae80 - - moveq -1,r3 - neg.b r3,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff01 - - move.d 0x456789ab,r3 - neg.b r3,r3 - test_move_cc 0 0 0 0 - checkr3 45678955 - - quit diff --git a/tests/tcg/cris/bare/check_not.s b/tests/tcg/cris/bare/check_not.s deleted file mode 100644 index 33bcf155e5..0000000000 --- a/tests/tcg/cris/bare/check_not.s +++ /dev/null @@ -1,31 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: fffffffe\nfffffffd\nffff0f00\n0\n87ecbbad\n - - .include "testutils.inc" - start - moveq 1,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 fffffffe - - moveq 2,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 fffffffd - - move.d 0xf0ff,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 ffff0f00 - - moveq -1,r3 - not r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x78134452,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 87ecbbad - - quit diff --git a/tests/tcg/cris/bare/check_orc.s b/tests/tcg/cris/bare/check_orc.s deleted file mode 100644 index c733f036a2..0000000000 --- a/tests/tcg/cris/bare/check_orc.s +++ /dev/null @@ -1,71 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n - - .include "testutils.inc" - start - moveq 1,r3 - or.d 2,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - or.d 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xf0ff,r3 - or.d 0xff0f,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - or.d -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - or.d 0x5432f789,r3 - test_move_cc 0 0 0 0 - checkr3 7c33f7db - - move.d 0xffff0001,r3 - or.w 2,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0003 - - moveq 2,r3 - or.w 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfedaffaf,r3 - or.w 0xff5f,r3 - test_move_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - or.w 0xf789,r3 - test_move_cc 1 0 0 0 - checkr3 7813f7db - - moveq 1,r3 - or.b 2,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - or.b 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfa3,r3 - or.b 0x4a,r3 - test_move_cc 1 0 0 0 - checkr3 feb - - move.d 0x78134453,r3 - or.b 0x89,r3 - test_move_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_orm.s b/tests/tcg/cris/bare/check_orm.s deleted file mode 100644 index ee723a6aa0..0000000000 --- a/tests/tcg/cris/bare/check_orm.s +++ /dev/null @@ -1,75 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n - - .include "testutils.inc" - .data -x: - .dword 2,1,0xff0f,-1,0x5432f789 - .word 2,1,0xff5f,0xf789 - .byte 2,1,0x4a,0x89 - - start - moveq 1,r3 - move.d x,r5 - or.d [r5+],r3 - checkr3 3 - - moveq 2,r3 - or.d [r5],r3 - addq 4,r5 - checkr3 3 - - move.d 0xf0ff,r3 - or.d [r5+],r3 - checkr3 ffff - - moveq -1,r3 - or.d [r5+],r3 - checkr3 ffffffff - - move.d 0x78134452,r3 - or.d [r5+],r3 - checkr3 7c33f7db - - move.d 0xffff0001,r3 - or.w [r5+],r3 - checkr3 ffff0003 - - moveq 2,r3 - or.w [r5],r3 - addq 2,r5 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfedaffaf,r3 - or.w [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - or.w [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 7813f7db - - moveq 1,r3 - or.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - or.b [r5],r3 - addq 1,r5 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfa3,r3 - or.b [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 feb - - move.d 0x78134453,r3 - or.b [r5],r3 - test_move_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_orq.s b/tests/tcg/cris/bare/check_orq.s deleted file mode 100644 index 5060edc72d..0000000000 --- a/tests/tcg/cris/bare/check_orq.s +++ /dev/null @@ -1,41 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffffffff\nffffffff\n1f\nffffffe0\n7813445e\n - - .include "testutils.inc" - start - moveq 1,r3 - orq 2,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - orq 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xf0ff,r3 - orq -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 0,r3 - orq -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 0,r3 - orq 31,r3 - test_move_cc 0 0 0 0 - checkr3 1f - - moveq 0,r3 - orq -32,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffe0 - - move.d 0x78134452,r3 - orq 12,r3 - test_move_cc 0 0 0 0 - checkr3 7813445e - - quit diff --git a/tests/tcg/cris/bare/check_orr.s b/tests/tcg/cris/bare/check_orr.s deleted file mode 100644 index a514c11bc9..0000000000 --- a/tests/tcg/cris/bare/check_orr.s +++ /dev/null @@ -1,84 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n - - .include "testutils.inc" - start - moveq 1,r3 - moveq 2,r4 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - moveq 1,r4 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xff0f,r4 - move.d 0xf0ff,r3 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - or.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 7c33f7db - - move.d 0xffff0001,r3 - moveq 2,r4 - or.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0003 - - moveq 2,r3 - move.d 0xffff0001,r4 - or.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfedaffaf,r3 - move.d 0xffffff5f,r4 - or.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - or.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 7813f7db - - moveq 1,r3 - move.d 0xffffff02,r4 - or.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - moveq 1,r4 - or.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0x4a,r4 - move.d 0xfa3,r3 - or.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 feb - - move.d 0x5432f789,r4 - move.d 0x78134453,r3 - or.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_ret.s b/tests/tcg/cris/bare/check_ret.s deleted file mode 100644 index b44fb25933..0000000000 --- a/tests/tcg/cris/bare/check_ret.s +++ /dev/null @@ -1,25 +0,0 @@ -# mach: crisv3 crisv8 crisv10 -# output: 3\n - -# Test that ret works. - - .include "testutils.inc" - start -x: - moveq 0,r3 - jsr z -w: - quit -y: - addq 1,r3 - checkr3 3 - quit - -z: - addq 1,r3 - move srp,r2 - add.d y-w,r2 - move r2,srp - ret - addq 1,r3 - quit diff --git a/tests/tcg/cris/bare/check_scc.s b/tests/tcg/cris/bare/check_scc.s deleted file mode 100644 index 4a8674cc1a..0000000000 --- a/tests/tcg/cris/bare/check_scc.s +++ /dev/null @@ -1,95 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n0\n1\n0\n1\n0\n1\n0\n0\n1\n1\n0\n1\n0\n1\n0\n1\n0\n0\n1\n0\n1\n1\n0\n1\n0\n0\n1\n1\n0\n1\n1\n0\n - - .include "testutils.inc" - - .macro lcheckr3 v - move $ccs, $r9 - checkr3 \v - move $r9, $ccs - .endm - - start - clearf nzvc - scc r3 - lcheckr3 1 - scs r3 - lcheckr3 0 - sne r3 - lcheckr3 1 - seq r3 - lcheckr3 0 - svc r3 - lcheckr3 1 - svs r3 - lcheckr3 0 - spl r3 - lcheckr3 1 - smi r3 - lcheckr3 0 - sls r3 - lcheckr3 0 - shi r3 - lcheckr3 1 - sge r3 - lcheckr3 1 - slt r3 - lcheckr3 0 - sgt r3 - lcheckr3 1 - sle r3 - lcheckr3 0 - sa r3 - lcheckr3 1 - setf nzvc - scc r3 - lcheckr3 0 - scs r3 - lcheckr3 1 - sne r3 - lcheckr3 0 - svc r3 - lcheckr3 0 - svs r3 - lcheckr3 1 - spl r3 - lcheckr3 0 - smi r3 - lcheckr3 1 - sls r3 - lcheckr3 1 - shi r3 - lcheckr3 0 - sge r3 - lcheckr3 1 - slt r3 - lcheckr3 0 - sgt r3 - lcheckr3 0 - sle r3 - lcheckr3 1 - sa r3 - lcheckr3 1 - clearf n - sge r3 - lcheckr3 0 - slt r3 - lcheckr3 1 - - .if 1 ;..asm.arch.cris.v32 - setf p - ssb r3 - .else - moveq 1,r3 - .endif - lcheckr3 1 - - .if 1 ;..asm.arch.cris.v32 - clearf p - ssb r3 - .else - moveq 0,r3 - .endif - lcheckr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_subc.s b/tests/tcg/cris/bare/check_subc.s deleted file mode 100644 index e34b5448e2..0000000000 --- a/tests/tcg/cris/bare/check_subc.s +++ /dev/null @@ -1,87 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n - - .include "testutils.inc" - start - - moveq -1,r3 - sub.d -2,r3 - test_cc 0 0 0 0 - checkr3 1 - - moveq 2,r3 - sub.d 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - sub.d -0xffff,r3 - test_cc 0 0 0 1 - checkr3 1fffe - - moveq -1,r3 - sub.d 1,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x78134452,r3 - sub.d -0x5432f789,r3 - test_cc 1 0 1 1 - checkr3 cc463bdb - - moveq -1,r3 - sub.w -2,r3 - test_cc 0 0 0 0 - checkr3 ffff0001 - - moveq 2,r3 - sub.w 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - sub.w 1,r3 - test_cc 1 0 0 0 - checkr3 fffe - - move.d 0xfedaffff,r3 - sub.w 1,r3 - test_cc 1 0 0 0 - checkr3 fedafffe - - move.d 0x78134452,r3 - sub.w 0x877,r3 - test_cc 0 0 0 0 - checkr3 78133bdb - - moveq -1,r3 - sub.b -2,r3 - test_cc 0 0 0 0 - checkr3 ffffff01 - - moveq 2,r3 - sub.b 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xff,r3 - sub.b 1,r3 - test_cc 1 0 0 0 - checkr3 fe - - move.d 0xfeda49ff,r3 - sub.b 1,r3 - test_cc 1 0 0 0 - checkr3 feda49fe - - move.d 0x78134452,r3 - sub.b 0x77,r3 - test_cc 1 0 0 1 - checkr3 781344db - - move.d 0x85649282,r3 - sub.b 0x82,r3 - test_cc 0 1 0 0 - checkr3 85649200 - - quit diff --git a/tests/tcg/cris/bare/check_subm.s b/tests/tcg/cris/bare/check_subm.s deleted file mode 100644 index e07ea02dd4..0000000000 --- a/tests/tcg/cris/bare/check_subm.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n - - .include "testutils.inc" - .data -x: - .dword -2,1,-0xffff,1,-0x5432f789 - .word -2,1,1,0x877 - .byte -2,1,0x77 - .byte 0x22 - - start - moveq -1,r3 - move.d x,r5 - sub.d [r5+],r3 - test_cc 0 0 0 0 - checkr3 1 - - moveq 2,r3 - sub.d [r5],r3 - test_cc 0 0 0 0 - addq 4,r5 - checkr3 1 - - move.d 0xffff,r3 - sub.d [r5+],r3 - test_cc 0 0 0 1 - checkr3 1fffe - - moveq -1,r3 - sub.d [r5+],r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x78134452,r3 - sub.d [r5+],r3 - test_cc 1 0 1 1 - checkr3 cc463bdb - - moveq -1,r3 - sub.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 ffff0001 - - moveq 2,r3 - sub.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - sub.w [r5],r3 - test_cc 1 0 0 0 - checkr3 fffe - - move.d 0xfedaffff,r3 - sub.w [r5+],r3 - test_cc 1 0 0 0 - checkr3 fedafffe - - move.d 0x78134452,r3 - sub.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 78133bdb - - moveq -1,r3 - sub.b [r5],r3 - test_cc 0 0 0 0 - addq 1,r5 - checkr3 ffffff01 - - moveq 2,r3 - sub.b [r5],r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xff,r3 - sub.b [r5],r3 - test_cc 1 0 0 0 - checkr3 fe - - move.d 0xfeda49ff,r3 - sub.b [r5+],r3 - test_cc 1 0 0 0 - checkr3 feda49fe - - move.d 0x78134452,r3 - sub.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 781344db - - move.d 0x85649222,r3 - sub.b [r5],r3 - test_cc 0 1 0 0 - checkr3 85649200 - - quit diff --git a/tests/tcg/cris/bare/check_subq.s b/tests/tcg/cris/bare/check_subq.s deleted file mode 100644 index 9e34fa31ab..0000000000 --- a/tests/tcg/cris/bare/check_subq.s +++ /dev/null @@ -1,52 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 0\nffffffff\nfffffffe\nffff\nff\n56788f9\n56788d9\n567889a\n0\n7ffffffc\n - - .include "testutils.inc" - start - moveq 1,r3 - subq 1,r3 - test_cc 0 1 0 0 - checkr3 0 - - subq 1,r3 - test_cc 1 0 0 1 - checkr3 ffffffff - - subq 1,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x10000,r3 - subq 1,r3 - test_cc 0 0 0 0 - checkr3 ffff - - move.d 0x100,r3 - subq 1,r3 - test_cc 0 0 0 0 - checkr3 ff - - move.d 0x5678900,r3 - subq 7,r3 - test_cc 0 0 0 0 - checkr3 56788f9 - - subq 32,r3 - test_cc 0 0 0 0 - checkr3 56788d9 - - subq 63,r3 - test_cc 0 0 0 0 - checkr3 567889a - - move.d 34,r3 - subq 34,r3 - test_cc 0 1 0 0 - checkr3 0 - - move.d 0x80000024,r3 - subq 40,r3 - test_cc 0 0 1 0 - checkr3 7ffffffc - - quit diff --git a/tests/tcg/cris/bare/check_subr.s b/tests/tcg/cris/bare/check_subr.s deleted file mode 100644 index 742fbc8915..0000000000 --- a/tests/tcg/cris/bare/check_subr.s +++ /dev/null @@ -1,102 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq -2,r4 - sub.d r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - moveq 2,r3 - moveq 1,r4 - sub.d r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - move.d -0xffff,r4 - sub.d r4,r3 - test_cc 0 0 0 1 - checkr3 1fffe - - moveq 1,r4 - moveq -1,r3 - sub.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - sub.d r4,r3 - test_cc 1 0 1 1 - checkr3 cc463bdb - - moveq -1,r3 - moveq -2,r4 - sub.w r4,r3 - test_cc 0 0 0 0 - checkr3 ffff0001 - - moveq 2,r3 - moveq 1,r4 - sub.w r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - move.d -0xffff,r4 - sub.w r4,r3 - test_cc 1 0 0 0 - checkr3 fffe - - move.d 0xfedaffff,r3 - move.d -0xfedaffff,r4 - sub.w r4,r3 - test_cc 1 0 0 0 - checkr3 fedafffe - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - sub.w r4,r3 - test_cc 0 0 0 0 - checkr3 78133bdb - - moveq -1,r3 - moveq -2,r4 - sub.b r4,r3 - test_cc 0 0 0 0 - checkr3 ffffff01 - - moveq 2,r3 - moveq 1,r4 - sub.b r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d -0xff,r4 - move.d 0xff,r3 - sub.b r4,r3 - test_cc 1 0 0 0 - checkr3 fe - - move.d -0xfeda49ff,r4 - move.d 0xfeda49ff,r3 - sub.b r4,r3 - test_cc 1 0 0 0 - checkr3 feda49fe - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - sub.b r4,r3 - test_cc 1 0 0 1 - checkr3 781344db - - move.d 0x85649222,r3 - move.d 0x77445622,r4 - sub.b r4,r3 - test_cc 0 1 0 0 - checkr3 85649200 - - quit diff --git a/tests/tcg/cris/bare/check_xarith.s b/tests/tcg/cris/bare/check_xarith.s deleted file mode 100644 index 80038b2ab9..0000000000 --- a/tests/tcg/cris/bare/check_xarith.s +++ /dev/null @@ -1,72 +0,0 @@ - -.include "testutils.inc" - - start - - moveq -1, $r0 - moveq 0, $r1 - addq 1, $r0 - ax - addq 0, $r1 - - move.d $r0, $r3 - checkr3 0 - move.d $r1, $r3 - checkr3 1 - - move.d 0, $r0 - moveq -1, $r1 - subq 1, $r0 - ax - subq 0, $r1 - - move.d $r0, $r3 - checkr3 ffffffff - move.d $r1, $r3 - checkr3 fffffffe - - - moveq -1, $r0 - moveq -1, $r1 - cmpq -1, $r0 - ax - cmpq -1, $r1 - beq 1f - nop - fail -1: - cmpq 0, $r0 - ax - cmpq -1, $r1 - bne 1f - nop - fail -1: - - ;; test for broken X sequence, run it several times. - moveq 8, $r0 -1: - moveq 0, $r3 - move.d $r0, $r1 - andq 1, $r1 - lslq 4, $r1 - moveq 1, $r2 - or.d $r1, $r2 - ba 2f - move $r2, $ccs -2: - addq 0, $r3 - move.d $r0, $r4 - move.d $r1, $r5 - move.d $r2, $r6 - move.d $r3, $r7 - lsrq 4, $r1 - move.d $r1, $r8 - xor $r1, $r3 - checkr3 0 - subq 1, $r0 - bne 1b - nop - - pass - quit diff --git a/tests/tcg/cris/bare/crt.s b/tests/tcg/cris/bare/crt.s deleted file mode 100644 index af027d7475..0000000000 --- a/tests/tcg/cris/bare/crt.s +++ /dev/null @@ -1,13 +0,0 @@ - .data -_stack_start: - .space 8192, 0 -_stack_end: - .text - .global _start -_start: - move.d _stack_end, $sp - jsr main - nop - moveq 0, $r10 - jump exit - nop diff --git a/tests/tcg/cris/bare/sys.c b/tests/tcg/cris/bare/sys.c deleted file mode 100644 index 1644eecc33..0000000000 --- a/tests/tcg/cris/bare/sys.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Helper functions for CRIS system tests - * - * There is no libc and only a limited set of headers. - */ - -#include - -void exit(int status) -{ - register unsigned int callno asm ("r9") = 1; /* NR_exit */ - - asm volatile ("break 13\n" - : /* no outputs */ - : "r" (callno) - : "memory"); - while (1) { - /* do nothing */ - }; -} - -size_t write(int fd, const void *buf, size_t count) -{ - register unsigned int callno asm ("r9") = 4; /* NR_write */ - register unsigned int r10 asm ("r10") = fd; - register const void *r11 asm ("r11") = buf; - register size_t r12 asm ("r12") = count; - register unsigned int r asm ("r10"); - - asm volatile ("break 13\n" - : "=r" (r) - : "r" (callno), "0" (r10), "r" (r11), "r" (r12) - : "memory"); - - return r; -} - -static inline int mystrlen(char *s) -{ - int i = 0; - while (s[i]) { - i++; - } - return i; -} - - -void pass(void) -{ - char s[] = "passed.\n"; - write(1, s, sizeof(s) - 1); - exit(0); -} - -void _fail(char *reason) -{ - char s[] = "\nfailed: "; - int len = mystrlen(reason); - write(1, s, sizeof(s) - 1); - write(1, reason, len); - write(1, "\n", 1); - exit(1); -} diff --git a/tests/tcg/cris/bare/testutils.inc b/tests/tcg/cris/bare/testutils.inc deleted file mode 100644 index aa1641b2e6..0000000000 --- a/tests/tcg/cris/bare/testutils.inc +++ /dev/null @@ -1,117 +0,0 @@ - .syntax no_register_prefix - - .macro start - .text - .global main -main: - .endm - - .macro quit - jump pass - nop - .endm - - .macro pass - jump pass - nop - .endm - - .macro startnostack - start - .endm - - .macro fail - .data -99: - .asciz " checkr3 failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop - .endm - - .macro checkr3 val - cmp.d 0x\val, $r3 - beq 100f - nop - .data -99: - .asciz "checkr3 failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop -100: - .endm - -; Test the condition codes - .macro test_cc N Z V C - .if \N - bpl 9f - nop - .else - bmi 9f - nop - .endif - .if \Z - bne 9f - nop - .else - beq 9f - nop - .endif - .if \V - bvc 9f - nop - .else - bvs 9f - nop - .endif - .if \C - bcc 9f - nop - .else - bcs 9f - nop - .endif - ba 8f - nop -9: - .data -99: - .asciz "test_move_cc failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop -8: - .endm - - - .macro test_move_cc N Z V C - .if \N - bpl 9f - nop - .else - bmi 9f - nop - .endif - .if \Z - bne 9f - nop - .else - beq 9f - nop - .endif - ba 8f - nop -9: - .data -99: - .asciz "test_move_cc failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop -8: - .endm diff --git a/tests/tcg/cris/libc/check_abs.c b/tests/tcg/cris/libc/check_abs.c deleted file mode 100644 index 08b67b6ef0..0000000000 --- a/tests/tcg/cris/libc/check_abs.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -static always_inline int cris_abs(int n) -{ - int r; - asm ("abs\t%1, %0\n" : "=r" (r) : "r" (n)); - return r; -} - -static always_inline void -verify_abs(int val, int res, - const int n, const int z, const int v, const int c) -{ - int r; - - cris_tst_cc_init(); - r = cris_abs(val); - cris_tst_cc(n, z, v, c); - if (r != res) - err(); -} - -int main(void) -{ - verify_abs(-1, 1, 0, 0, 0, 0); - verify_abs(0x80000000, 0x80000000, 1, 0, 0, 0); - verify_abs(0x7fffffff, 0x7fffffff, 0, 0, 0, 0); - verify_abs(42, 42, 0, 0, 0, 0); - verify_abs(1, 1, 0, 0, 0, 0); - verify_abs(0xffff, 0xffff, 0, 0, 0, 0); - verify_abs(0xffff, 0xffff, 0, 0, 0, 0); - verify_abs(-31, 0x1f, 0, 0, 0, 0); - verify_abs(0, 0, 0, 1, 0, 0); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addc.c b/tests/tcg/cris/libc/check_addc.c deleted file mode 100644 index fc3fb1faa8..0000000000 --- a/tests/tcg/cris/libc/check_addc.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -static always_inline int cris_addc(int a, const int b) -{ - asm ("addc\t%1, %0\n" : "+r" (a) : "r" (b)); - return a; -} - -#define verify_addc(a, b, res, n, z, v, c) \ -{ \ - int r; \ - r = cris_addc((a), (b)); \ - cris_tst_cc((n), (z), (v), (c)); \ - if (r != (res)) \ - err(); \ -} - -int main(void) -{ - cris_tst_cc_init(); - asm volatile ("clearf cz"); - verify_addc(0, 0, 0, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf z"); - verify_addc(0, 0, 0, 0, 1, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf cz"); - verify_addc(0, 0, 1, 0, 0, 0, 0); - cris_tst_cc_init(); - asm volatile ("clearf c"); - verify_addc(-1, 2, 1, 0, 0, 0, 1); - - cris_tst_cc_init(); - asm volatile ("clearf nzv"); - asm volatile ("setf c"); - verify_addc(-1, 2, 2, 0, 0, 0, 1); - - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc(0xffff, 0xffff, 0x1ffff, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("clearf nzvc"); - verify_addc(-1, -1, 0xfffffffe, 1, 0, 0, 1); - - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc(0x78134452, 0x5432f789, 0xcc463bdc, 1, 0, 1, 0); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addcm.c b/tests/tcg/cris/libc/check_addcm.c deleted file mode 100644 index b355ba164f..0000000000 --- a/tests/tcg/cris/libc/check_addcm.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -/* need to avoid acr as source here. */ -static always_inline int cris_addc_m(int a, const int *b) -{ - asm volatile ("addc [%1], %0\n" : "+r" (a) : "r" (b)); - return a; -} - -/* 'b' is a crisv32 constrain to avoid postinc with $acr. */ -static always_inline int cris_addc_pi_m(int a, int **b) -{ - asm volatile ("addc [%1+], %0\n" : "+r" (a), "+b" (*b)); - return a; -} - -#define verify_addc_m(a, b, res, n, z, v, c) \ -{ \ - int r; \ - r = cris_addc_m((a), (b)); \ - cris_tst_cc((n), (z), (v), (c)); \ - if (r != (res)) \ - err(); \ -} - -#define verify_addc_pi_m(a, b, res, n, z, v, c) \ -{ \ - int r; \ - r = cris_addc_pi_m((a), (b)); \ - cris_tst_cc((n), (z), (v), (c)); \ - if (r != (res)) \ - err(); \ -} - -int x[] = { 0, 0, 2, -1, 0xffff, -1, 0x5432f789}; - -int main(void) -{ - int *p = (void *)&x[0]; -#if 1 - cris_tst_cc_init(); - asm volatile ("clearf cz"); - verify_addc_m(0, p, 0, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf z"); - verify_addc_m(0, p, 0, 0, 1, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc_m(0, p, 1, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("clearf c"); - verify_addc_pi_m(0, &p, 0, 0, 1, 0, 0); - - p = &x[1]; - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc_pi_m(0, &p, 1, 0, 0, 0, 0); - - if (p != &x[2]) - err(); - - cris_tst_cc_init(); - asm volatile ("clearf c"); - verify_addc_pi_m(-1, &p, 1, 0, 0, 0, 1); - - if (p != &x[3]) - err(); -#endif - p = &x[3]; - /* TODO: investigate why this one fails. */ - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc_m(2, p, 2, 0, 0, 0, 1); - p += 4; - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addo.c b/tests/tcg/cris/libc/check_addo.c deleted file mode 100644 index 4235e5fc65..0000000000 --- a/tests/tcg/cris/libc/check_addo.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -/* this would be better to do in asm, it's an orgy in GCC inline asm now. */ - -#define cris_addo_b(o, v) \ - asm volatile ("addo.b\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr"); -#define cris_addo_w(o, v) \ - asm volatile ("addo.w\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr"); -#define cris_addo_d(o, v) \ - asm volatile ("addo.d\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr"); -#define cris_addo_pi_b(o, v) \ - asm volatile ("addo.b\t[%0+], %1, $acr\n" \ - : "+b" (o): "r" (v) : "acr"); -#define cris_addo_pi_w(o, v) \ - asm volatile ("addo.w\t[%0+], %1, $acr\n" \ - : "+b" (o): "r" (v) : "acr"); -#define cris_addo_pi_d(o, v) \ - asm volatile ("addo.d\t[%0+], %1, $acr\n" \ - : "+b" (o): "r" (v) : "acr"); - -struct { - uint32_t v1; - uint16_t v2; - uint32_t v3; - uint8_t v4; - uint8_t v5; - uint16_t v6; - uint32_t v7; -} y = { - 32769, - -1, - 5, - 3, -4, - 2, - -76789887 -}; - -static int x[3] = {0x55aa77ff, 0xccff2244, 0x88ccee19}; - -int main(void) -{ - int *r; - unsigned char *t, *p; - - /* Note, this test-case will trig an unaligned access, partly - to x[0] and to [x1]. */ - t = (unsigned char *)x; - t -= 32768; - p = (unsigned char *) &y.v1; - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_d(p, t); - cris_tst_cc(1, 1, 1, 1); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0x4455aa77) - err(); - - - t += 32770; - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_w(p, t); - cris_tst_cc(1, 1, 1, 1); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0x4455aa77) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_d(p, r); - cris_tst_cc(1, 1, 1, 1); - p += 4; - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0xee19ccff) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_b(p, t); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*(uint16_t*)r != 0xff22) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_b(p, r); - cris_tst_cc(1, 1, 1, 1); - p += 1; - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0x4455aa77) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_w(p, r); - cris_tst_cc(1, 1, 1, 1); - p += 2; - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0xff224455) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_d(p, t); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - r = (void*)(((char *)r) + 76789885); - if (*r != 0x55aa77ff) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addoq.c b/tests/tcg/cris/libc/check_addoq.c deleted file mode 100644 index ed509e27e0..0000000000 --- a/tests/tcg/cris/libc/check_addoq.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -/* this would be better to do in asm, it's an orgy in GCC inline asm now. */ - -/* ACR will be clobbered. */ -#define cris_addoq(o, v) \ - asm volatile ("addoq\t%1, %0, $acr\n" : : "r" (v), "i" (o) : "acr"); - - -int main(void) -{ - int x[3] = {0x55aa77ff, 0xccff2244, 0x88ccee19}; - int *p, *t = x + 1; - - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addoq(0, t); - cris_tst_cc(1, 1, 1, 1); - asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); - if (*p != 0xccff2244) - err(); - - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addoq(4, t); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); - if (*p != 0x88ccee19) - err(); - - cris_tst_cc_init(); - asm volatile ("clearf\tzvnc\n"); - cris_addoq(-8, t + 1); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); - if (*p != 0x55aa77ff) - err(); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_bound.c b/tests/tcg/cris/libc/check_bound.c deleted file mode 100644 index d956ab9ade..0000000000 --- a/tests/tcg/cris/libc/check_bound.c +++ /dev/null @@ -1,142 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -static always_inline int cris_bound_b(int v, int b) -{ - int r = v; - asm ("bound.b\t%1, %0\n" : "+r" (r) : "ri" (b)); - return r; -} - -static always_inline int cris_bound_w(int v, int b) -{ - int r = v; - asm ("bound.w\t%1, %0\n" : "+r" (r) : "ri" (b)); - return r; -} - -static always_inline int cris_bound_d(int v, int b) -{ - int r = v; - asm ("bound.d\t%1, %0\n" : "+r" (r) : "ri" (b)); - return r; -} - -int main(void) -{ - int r; - - cris_tst_cc_init(); - r = cris_bound_d(-1, 2); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(2, 0xffffffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(0xffff, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(-1, 0xffffffff); - cris_tst_cc(1, 0, 0, 0); - if (r != 0xffffffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(0x78134452, 0x5432f789); - cris_tst_cc(0, 0, 0, 0); - if (r != 0x5432f789) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(-1, 2); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(-1, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(2, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(0xfedaffff, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(0x78134452, 0xf789); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xf789) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(-1, 2); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(2, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(-1, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0xff, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0xfeda49ff, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0x78134452, 0x89); - cris_tst_cc(0, 0, 0, 0); - if (r != 0x89) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(0x78134452, 0); - cris_tst_cc(0, 1, 0, 0); - if (r != 0) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0xffff, -1); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_ftag.c b/tests/tcg/cris/libc/check_ftag.c deleted file mode 100644 index aaa5c97115..0000000000 --- a/tests/tcg/cris/libc/check_ftag.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -static always_inline void cris_ftag_i(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("ftagi\t[%0]\n" : : "r" (v) ); -} -static always_inline void cris_ftag_d(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("ftagd\t[%0]\n" : : "r" (v) ); -} -static always_inline void cris_fidx_i(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("fidxi\t[%0]\n" : : "r" (v) ); -} -static always_inline void cris_fidx_d(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("fidxd\t[%0]\n" : : "r" (v) ); -} - - -int main(void) -{ - cris_ftag_i(0); - cris_ftag_d(0); - cris_fidx_i(0); - cris_fidx_d(0); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c b/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c deleted file mode 100644 index 45ecd159b3..0000000000 --- a/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c +++ /dev/null @@ -1,15 +0,0 @@ -/* PR rtl-optimization/28634. On targets with delayed branches, - dbr_schedule could do the next iteration's addition in the - branch delay slot, then subtract the value again if the branch - wasn't taken. This can lead to rounding errors. */ -int x = -1; -int y = 1; -int -main (void) -{ - while (y > 0) - y += x; - if (y != x + 1) - abort (); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_gcctorture_pr28634.c b/tests/tcg/cris/libc/check_gcctorture_pr28634.c deleted file mode 100644 index a0c525497d..0000000000 --- a/tests/tcg/cris/libc/check_gcctorture_pr28634.c +++ /dev/null @@ -1,15 +0,0 @@ -/* PR rtl-optimization/28634. On targets with delayed branches, - dbr_schedule could do the next iteration's addition in the - branch delay slot, then subtract the value again if the branch - wasn't taken. This can lead to rounding errors. */ -double x = -0x1.0p53; -double y = 1; -int -main (void) -{ - while (y > 0) - y += x; - if (y != x + 1) - abort (); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_glibc_kernelversion.c b/tests/tcg/cris/libc/check_glibc_kernelversion.c deleted file mode 100644 index 7aada89911..0000000000 --- a/tests/tcg/cris/libc/check_glibc_kernelversion.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Check the lz insn. - */ - -#include -#include -#include -#include "sys.h" - -#define __LINUX_KERNEL_VERSION 131584 - -#define DL_SYSDEP_OSCHECK(FATAL) \ - do { \ - /* Test whether the kernel is new enough. This test is only \ - performed if the library is not compiled to run on all \ - kernels. */ \ - if (__LINUX_KERNEL_VERSION > 0) \ - { \ - char bufmem[64]; \ - char *buf = bufmem; \ - unsigned int version; \ - int parts; \ - char *cp; \ - struct utsname uts; \ - \ - /* Try the uname syscall */ \ - if (__uname (&uts)) \ - { \ - /* This was not successful. Now try reading the /proc \ - filesystem. */ \ - ssize_t reslen; \ - int fd = __open ("/proc/sys/kernel/osrelease", O_RDONLY); \ - if (fd == -1 \ - || (reslen = __read (fd, bufmem, sizeof (bufmem))) <= 0) \ - /* This also didn't work. We give up since we cannot \ - make sure the library can actually work. */ \ - FATAL ("FATAL: cannot determine library version\n"); \ - __close (fd); \ - buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0'; \ - } \ - else \ - buf = uts.release; \ - \ - /* Now convert it into a number. The string consists of at most \ - three parts. */ \ - version = 0; \ - parts = 0; \ - cp = buf; \ - while ((*cp >= '0') && (*cp <= '9')) \ - { \ - unsigned int here = *cp++ - '0'; \ - \ - while ((*cp >= '0') && (*cp <= '9')) \ - { \ - here *= 10; \ - here += *cp++ - '0'; \ - } \ - \ - ++parts; \ - version <<= 8; \ - version |= here; \ - \ - if (*cp++ != '.') \ - /* Another part following? */ \ - break; \ - } \ - \ - if (parts < 3) \ - version <<= 8 * (3 - parts); \ - \ - /* Now we can test with the required version. */ \ - if (version < __LINUX_KERNEL_VERSION) \ - /* Not sufficient. */ \ - FATAL ("FATAL: kernel too old\n"); \ - \ - _dl_osversion = version; \ - } \ - } while (0) - -int main(void) -{ - char bufmem[64] = "2.6.22"; - char *buf = bufmem; - unsigned int version; - int parts; - char *cp; - - version = 0; - parts = 0; - cp = buf; - while ((*cp >= '0') && (*cp <= '9')) - { - unsigned int here = *cp++ - '0'; - - while ((*cp >= '0') && (*cp <= '9')) - { - here *= 10; - here += *cp++ - '0'; - } - - ++parts; - version <<= 8; - version |= here; - - if (*cp++ != '.') - /* Another part following? */ - break; - } - - if (parts < 3) - version <<= 8 * (3 - parts); - if (version < __LINUX_KERNEL_VERSION) - err(); - pass(); - exit(0); -} diff --git a/tests/tcg/cris/libc/check_hello.c b/tests/tcg/cris/libc/check_hello.c deleted file mode 100644 index fb403ba996..0000000000 --- a/tests/tcg/cris/libc/check_hello.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -int main () -{ - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_int64.c b/tests/tcg/cris/libc/check_int64.c deleted file mode 100644 index 69caec1bb2..0000000000 --- a/tests/tcg/cris/libc/check_int64.c +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - - -static always_inline int64_t add64(const int64_t a, const int64_t b) -{ - return a + b; -} - -static always_inline int64_t sub64(const int64_t a, const int64_t b) -{ - return a - b; -} - -int main(void) -{ - int64_t a = 1; - int64_t b = 2; - - /* FIXME: add some tests. */ - a = add64(a, b); - if (a != 3) - err(); - - a = sub64(a, b); - if (a != 1) - err(); - - a = add64(a, -4); - if (a != -3) - err(); - - a = add64(a, 3); - if (a != 0) - err(); - - a = 0; - a = sub64(a, 1); - if (a != -1) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_lz.c b/tests/tcg/cris/libc/check_lz.c deleted file mode 100644 index bf051a6b55..0000000000 --- a/tests/tcg/cris/libc/check_lz.c +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include -#include "sys.h" - -static always_inline int cris_lz(int x) -{ - int r; - asm ("lz\t%1, %0\n" : "=r" (r) : "r" (x)); - return r; -} - -void check_lz(void) -{ - int i; - - if (cris_lz(0) != 32) - err(); - if (cris_lz(1) != 31) - err(); - if (cris_lz(2) != 30) - err(); - if (cris_lz(4) != 29) - err(); - if (cris_lz(8) != 28) - err(); - - /* try all positions with a single bit. */ - for (i = 1; i < 32; i++) { - if (cris_lz(1 << (i-1)) != (32 - i)) - err(); - } - - /* try all positions with all bits. */ - for (i = 1; i < 32; i++) { - /* split up this computation to clarify it. */ - uint32_t val; - val = (unsigned int)-1 >> (32 - i); - if (cris_lz(val) != (32 - i)) - err(); - } -} - -int main(void) -{ - check_lz(); - pass(); - exit(0); -} diff --git a/tests/tcg/cris/libc/check_mapbrk.c b/tests/tcg/cris/libc/check_mapbrk.c deleted file mode 100644 index 1aff7622bc..0000000000 --- a/tests/tcg/cris/libc/check_mapbrk.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include - -/* Basic sanity check that syscalls to implement malloc (brk, mmap2, - munmap) are trivially functional. */ - -int main () -{ - void *p1, *p2, *p3, *p4, *p5, *p6; - - if ((p1 = malloc (8100)) == NULL - || (p2 = malloc (16300)) == NULL - || (p3 = malloc (4000)) == NULL - || (p4 = malloc (500)) == NULL - || (p5 = malloc (1023*1024)) == NULL - || (p6 = malloc (8191*1024)) == NULL) - { - printf ("fail\n"); - exit (1); - } - - free (p1); - free (p2); - free (p3); - free (p4); - free (p5); - free (p6); - - p1 = malloc (64000); - if (p1 == NULL) - { - printf ("fail\n"); - exit (1); - } - free (p1); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_mmap1.c b/tests/tcg/cris/libc/check_mmap1.c deleted file mode 100644 index b803f0c431..0000000000 --- a/tests/tcg/cris/libc/check_mmap1.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - int fd = open (argv[0], O_RDONLY); - struct stat sb; - int size; - void *a; - const char *str = "a string you'll only find in the program"; - - if (fd == -1) - { - perror ("open"); - abort (); - } - - if (fstat (fd, &sb) < 0) - { - perror ("fstat"); - abort (); - } - - size = sb.st_size; - - /* We want to test mmapping a size that isn't exactly a page. */ - if ((size & 8191) == 0) - size--; - - a = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - - if (memmem (a, size, str, strlen (str) + 1) == NULL) - abort (); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_mmap2.c b/tests/tcg/cris/libc/check_mmap2.c deleted file mode 100644 index 35139a0ed9..0000000000 --- a/tests/tcg/cris/libc/check_mmap2.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - int fd = open (argv[0], O_RDONLY); - struct stat sb; - int size; - void *a; - const char *str = "a string you'll only find in the program"; - - if (fd == -1) - { - perror ("open"); - abort (); - } - - if (fstat (fd, &sb) < 0) - { - perror ("fstat"); - abort (); - } - - size = sb.st_size; - - /* We want to test mmapping a size that isn't exactly a page. */ - if ((size & 8191) == 0) - size--; - - a = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); - - if (memmem (a, size, str, strlen (str) + 1) == NULL) - abort (); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_mmap3.c b/tests/tcg/cris/libc/check_mmap3.c deleted file mode 100644 index cb890ef120..0000000000 --- a/tests/tcg/cris/libc/check_mmap3.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - volatile unsigned char *a; - - /* Check that we can map a non-multiple of a page and still get a full page. */ - a = mmap (NULL, 0x4c, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (a == NULL || a == (unsigned char *) -1) - abort (); - - a[0] = 0xbe; - a[8191] = 0xef; - memset ((char *) a + 1, 0, 8190); - - if (a[0] != 0xbe || a[8191] != 0xef) - abort (); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_moveq.c b/tests/tcg/cris/libc/check_moveq.c deleted file mode 100644 index 80f2dff6ab..0000000000 --- a/tests/tcg/cris/libc/check_moveq.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -#define cris_moveq(dst, src) \ - asm volatile ("moveq %1, %0\n" : "=r" (dst) : "i" (src)); - - - -int main(void) -{ - int t; - - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_moveq(t, 10); - cris_tst_cc(1, 1, 1, 1); - if (t != 10) - err(); - - /* make sure moveq doesn't clobber the zflag. */ - cris_tst_cc_init(); - asm volatile ("setf vnc\n"); - asm volatile ("clearf z\n"); - cris_moveq(t, 0); - cris_tst_cc(1, 0, 1, 1); - if (t != 0) - err(); - - /* make sure moveq doesn't clobber the nflag. - Also check large immediates */ - cris_tst_cc_init(); - asm volatile ("setf zvc\n"); - asm volatile ("clearf n\n"); - cris_moveq(t, -31); - cris_tst_cc(0, 1, 1, 1); - if (t != -31) - err(); - - cris_tst_cc_init(); - asm volatile ("setf nzvc\n"); - cris_moveq(t, 31); - cris_tst_cc(1, 1, 1, 1); - if (t != 31) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf1.c b/tests/tcg/cris/libc/check_openpf1.c deleted file mode 100644 index 251d26eec2..0000000000 --- a/tests/tcg/cris/libc/check_openpf1.c +++ /dev/null @@ -1,38 +0,0 @@ -/* Check that --sysroot is applied to open(2). -#sim: --sysroot=@exedir@ - - We assume, with EXE being the name of the executable: - - The simulator executes with cwd the same directory where the executable - is located (so argv[0] contains a plain filename without directory - components). - - There's no /EXE on the host file system. */ - -#include -#include -#include -#include -int main (int argc, char *argv[]) -{ - char *fnam = argv[0]; - FILE *f; - if (argv[0][0] != '/') - { - fnam = malloc (strlen (argv[0]) + 2); - if (fnam == NULL) - abort (); - strcpy (fnam, "/"); - strcat (fnam, argv[0]); - } - - f = fopen (fnam, "rb"); - if (f == NULL) - abort (); - fclose(f); - - /* Cover another execution path. */ - if (fopen ("/nonexistent", "rb") != NULL - || errno != ENOENT) - abort (); - printf ("pass\n"); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf2.c b/tests/tcg/cris/libc/check_openpf2.c deleted file mode 100644 index 5d56189f8e..0000000000 --- a/tests/tcg/cris/libc/check_openpf2.c +++ /dev/null @@ -1,16 +0,0 @@ -/* Check that the simulator has chdir:ed to the --sysroot argument -#sim: --sysroot=@srcdir@ - (or that --sysroot is applied to relative file paths). */ - -#include -#include -#include -int main (int argc, char *argv[]) -{ - FILE *f = fopen ("check_openpf2.c", "rb"); - if (f == NULL) - abort (); - fclose(f); - printf ("pass\n"); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf3.c b/tests/tcg/cris/libc/check_openpf3.c deleted file mode 100644 index 557adee92d..0000000000 --- a/tests/tcg/cris/libc/check_openpf3.c +++ /dev/null @@ -1,49 +0,0 @@ -/* Basic file operations (rename, unlink); once without sysroot. We - also test that the simulator has chdir:ed to PREFIX, when defined. */ - -#include -#include -#include -#include -#include -#include - -#ifndef PREFIX -#define PREFIX -#endif - -void err (const char *s) -{ - perror (s); - abort (); -} - -int main (int argc, char *argv[]) -{ - FILE *f; - struct stat buf; - - unlink (PREFIX "testfoo2.tmp"); - - f = fopen ("testfoo1.tmp", "w"); - if (f == NULL) - err ("open"); - fclose (f); - - if (rename (PREFIX "testfoo1.tmp", PREFIX "testfoo2.tmp") != 0) - err ("rename"); - - if (stat (PREFIX "testfoo2.tmp", &buf) != 0 - || !S_ISREG (buf.st_mode)) - err ("stat 1"); - - if (stat ("testfoo2.tmp", &buf) != 0 - || !S_ISREG (buf.st_mode)) - err ("stat 2"); - - if (unlink (PREFIX "testfoo2.tmp") != 0) - err ("unlink"); - - printf ("pass\n"); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf5.c b/tests/tcg/cris/libc/check_openpf5.c deleted file mode 100644 index 1f86ea283d..0000000000 --- a/tests/tcg/cris/libc/check_openpf5.c +++ /dev/null @@ -1,56 +0,0 @@ -/* Check that TRT happens when error on too many opened files. -#notarget: cris*-*-elf -#sim: --sysroot=@exedir@ -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - int i; - int filemax; - -#ifdef OPEN_MAX - filemax = OPEN_MAX; -#else - filemax = sysconf (_SC_OPEN_MAX); -#endif - - char *fn = malloc (strlen (argv[0]) + 2); - if (fn == NULL) - abort (); - strcpy (fn, "/"); - strcat (fn, argv[0]); - - for (i = 0; i < filemax + 1; i++) - { - if (open (fn, O_RDONLY) < 0) - { - /* Shouldn't happen too early. */ - if (i < filemax - 3 - 1) - { - fprintf (stderr, "i: %d\n", i); - abort (); - } - if (errno != EMFILE) - { - perror ("open"); - abort (); - } - goto ok; - } - } - abort (); - -ok: - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_settls1.c b/tests/tcg/cris/libc/check_settls1.c deleted file mode 100644 index 3abc3a9ea8..0000000000 --- a/tests/tcg/cris/libc/check_settls1.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include - -#include - -#ifndef SYS_set_thread_area -#define SYS_set_thread_area 243 -#endif - -int main (void) -{ - unsigned long tp, old_tp; - int ret; - - asm volatile ("move $pid,%0" : "=r" (old_tp)); - old_tp &= ~0xff; - - ret = syscall (SYS_set_thread_area, 0xf0); - if (ret != -1 || errno != EINVAL) { - syscall (SYS_set_thread_area, old_tp); - perror ("Invalid thread area accepted:"); - abort(); - } - - ret = syscall (SYS_set_thread_area, 0xeddeed00); - if (ret != 0) { - perror ("Valid thread area not accepted: "); - abort (); - } - - asm volatile ("move $pid,%0" : "=r" (tp)); - tp &= ~0xff; - syscall (SYS_set_thread_area, old_tp); - - if (tp != 0xeddeed00) { - * (volatile int *) 0 = 0; - perror ("tls2"); - abort (); - } - - printf ("pass\n"); - return EXIT_SUCCESS; -} diff --git a/tests/tcg/cris/libc/check_sigalrm.c b/tests/tcg/cris/libc/check_sigalrm.c deleted file mode 100644 index 39fa8d9bac..0000000000 --- a/tests/tcg/cris/libc/check_sigalrm.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include - -#define MAGIC (0xdeadbeef) - -int s = 0; -void sighandler(int sig) -{ - s = MAGIC; -} - -int main(int argc, char **argv) -{ - int p; - - p = getpid(); - signal(SIGALRM, sighandler); - kill(p, SIGALRM); - if (s != MAGIC) - return EXIT_FAILURE; - - printf ("passed\n"); - return EXIT_SUCCESS; -} diff --git a/tests/tcg/cris/libc/check_stat1.c b/tests/tcg/cris/libc/check_stat1.c deleted file mode 100644 index 2e2cae51df..0000000000 --- a/tests/tcg/cris/libc/check_stat1.c +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include -#include -#include -#include - -int main (void) -{ - struct stat buf; - - if (stat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_stat2.c b/tests/tcg/cris/libc/check_stat2.c deleted file mode 100644 index e36172ed25..0000000000 --- a/tests/tcg/cris/libc/check_stat2.c +++ /dev/null @@ -1,20 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#include -#include -#include -#include -#include - -int main (void) -{ - struct stat buf; - - if (lstat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_stat3.c b/tests/tcg/cris/libc/check_stat3.c deleted file mode 100644 index 36a9d5d274..0000000000 --- a/tests/tcg/cris/libc/check_stat3.c +++ /dev/null @@ -1,25 +0,0 @@ -/* Simulator options: -#sim: --sysroot=@exedir@ -*/ -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - char path[1024] = "/"; - struct stat buf; - - strncat(path, argv[0], sizeof(path) - 2); - if (stat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - if (stat (path, &buf) != 0 - || !S_ISREG (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_stat4.c b/tests/tcg/cris/libc/check_stat4.c deleted file mode 100644 index 04f21fe7c4..0000000000 --- a/tests/tcg/cris/libc/check_stat4.c +++ /dev/null @@ -1,27 +0,0 @@ -/* Simulator options: -#notarget: cris*-*-elf -#sim: --sysroot=@exedir@ -*/ - -#include -#include -#include -#include -#include -#include - -int main (int argc, char *argv[]) -{ - char path[1024] = "/"; - struct stat buf; - - strncat(path, argv[0], sizeof(path) - 2); - if (lstat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - if (lstat (path, &buf) != 0 - || !S_ISREG (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_swap.c b/tests/tcg/cris/libc/check_swap.c deleted file mode 100644 index 9a68c1e5d7..0000000000 --- a/tests/tcg/cris/libc/check_swap.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include -#include "sys.h" -#include "crisutils.h" - -#define N 8 -#define W 4 -#define B 2 -#define R 1 - -static always_inline int cris_swap(const int mode, int x) -{ - switch (mode) - { - case N: asm ("swapn\t%0\n" : "+r" (x) : "0" (x)); break; - case W: asm ("swapw\t%0\n" : "+r" (x) : "0" (x)); break; - case B: asm ("swapb\t%0\n" : "+r" (x) : "0" (x)); break; - case R: asm ("swapr\t%0\n" : "+r" (x) : "0" (x)); break; - case B|R: asm ("swapbr\t%0\n" : "+r" (x) : "0" (x)); break; - case W|R: asm ("swapwr\t%0\n" : "+r" (x) : "0" (x)); break; - case W|B: asm ("swapwb\t%0\n" : "+r" (x) : "0" (x)); break; - case W|B|R: asm ("swapwbr\t%0\n" : "+r" (x) : "0" (x)); break; - case N|R: asm ("swapnr\t%0\n" : "+r" (x) : "0" (x)); break; - case N|B: asm ("swapnb\t%0\n" : "+r" (x) : "0" (x)); break; - case N|B|R: asm ("swapnbr\t%0\n" : "+r" (x) : "0" (x)); break; - case N|W: asm ("swapnw\t%0\n" : "+r" (x) : "0" (x)); break; - default: - err(); - break; - } - return x; -} - -/* Made this a macro to be able to pick up the location of the errors. */ -#define verify_swap(mode, val, expected, n, z) \ -do { \ - int r; \ - cris_tst_cc_init(); \ - r = cris_swap(mode, val); \ - cris_tst_mov_cc(n, z); \ - if (r != expected) \ - err(); \ -} while(0) - -void check_swap(void) -{ - /* Some of these numbers are borrowed from GDB's cris sim - testsuite. */ - if (cris_swap(N, 0) != 0xffffffff) - err(); - if (cris_swap(W, 0x12345678) != 0x56781234) - err(); - if (cris_swap(B, 0x12345678) != 0x34127856) - err(); - - verify_swap(R, 0x78134452, 0x1ec8224a, 0, 0); - verify_swap(B, 0x78134452, 0x13785244, 0, 0); - verify_swap(B|R, 0x78134452, 0xc81e4a22, 1, 0); - verify_swap(W, 0x78134452, 0x44527813, 0, 0); - verify_swap(W|R, 0x78134452, 0x224a1ec8, 0, 0); - verify_swap(W|B|R, 0x78134452, 0x4a22c81e, 0, 0); - verify_swap(N, 0x78134452, 0x87ecbbad, 1, 0); - verify_swap(N|R, 0x78134452, 0xe137ddb5, 1, 0); - verify_swap(N|B, 0x78134452, 0xec87adbb, 1, 0); - verify_swap(N|B|R, 0x78134452, 0x37e1b5dd, 0, 0); - verify_swap(N|W, 0x78134452, 0xbbad87ec, 1, 0); - verify_swap(N|B|R, 0xffffffff, 0, 0, 1); -} - -int main(void) -{ - check_swap(); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_time2.c b/tests/tcg/cris/libc/check_time2.c deleted file mode 100644 index 20b69b4f60..0000000000 --- a/tests/tcg/cris/libc/check_time2.c +++ /dev/null @@ -1,18 +0,0 @@ -/* CB_SYS_time doesn't implement the Linux time syscall; the return - value isn't written to the argument. */ - -#include -#include -#include - -int -main (void) -{ - time_t x = (time_t) -1; - time_t t = time (&x); - - if (t == (time_t) -1 || t != x) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/crisutils.h b/tests/tcg/cris/libc/crisutils.h deleted file mode 100644 index bbbe6c5540..0000000000 --- a/tests/tcg/cris/libc/crisutils.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef CRISUTILS_H -#define CRISUTILS_H 1 - -static char *tst_cc_loc = NULL; - -#define cris_tst_cc_init() \ -do { tst_cc_loc = "test_cc failed at " CURRENT_LOCATION; } while(0) - -/* We need a real symbol to signal error. */ -void _err(void) { - if (!tst_cc_loc) - tst_cc_loc = "tst_cc_failed\n"; - _fail(tst_cc_loc); -} - -static always_inline void cris_tst_cc_n1(void) -{ - asm volatile ("bpl _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_n0(void) -{ - asm volatile ("bmi _err\n" - "nop\n"); -} - -static always_inline void cris_tst_cc_z1(void) -{ - asm volatile ("bne _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_z0(void) -{ - asm volatile ("beq _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_v1(void) -{ - asm volatile ("bvc _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_v0(void) -{ - asm volatile ("bvs _err\n" - "nop\n"); -} - -static always_inline void cris_tst_cc_c1(void) -{ - asm volatile ("bcc _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_c0(void) -{ - asm volatile ("bcs _err\n" - "nop\n"); -} - -static always_inline void cris_tst_mov_cc(int n, int z) -{ - if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); - if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); - asm volatile ("" : : "g" (_err)); -} - -static always_inline void cris_tst_cc(const int n, const int z, - const int v, const int c) -{ - if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); - if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); - if (v) cris_tst_cc_v1(); else cris_tst_cc_v0(); - if (c) cris_tst_cc_c1(); else cris_tst_cc_c0(); - asm volatile ("" : : "g" (_err)); -} - -#endif diff --git a/tests/tcg/cris/libc/sys.h b/tests/tcg/cris/libc/sys.h deleted file mode 100644 index 3dd47bb673..0000000000 --- a/tests/tcg/cris/libc/sys.h +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -#define always_inline inline __attribute__((always_inline)) - -#define CURRENT_LOCATION __FILE__ ":" TOSTRING(__LINE__) - -#define err() \ -{ \ - _fail("at " CURRENT_LOCATION " "); \ -} - -#define mb() asm volatile ("" : : : "memory") - -void pass(void); -void _fail(char *reason); diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 96a4d7a614..e5182c01d8 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 @@ -43,10 +43,90 @@ 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 +HEX_TESTS += unaligned_pc + +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 +unaligned_pc: unaligned_pc.c + # 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/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 6e2c9ab3cd..90c3733da0 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-2024 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; @@ -263,6 +231,7 @@ static void test_masked_store(bool invert) static void test_new_value_store(void) { void *p0 = buffer0; + void *p1 = buffer1; void *pout = output; asm("{\n\t" @@ -274,6 +243,19 @@ static void test_new_value_store(void) expect[0] = buffer0[0]; check_output_w(__LINE__, 1); + + /* Test the .new read from the high half of a pair */ + asm("v7 = vmem(%0 + #0)\n\t" + "v12 = vmem(%1 + #0)\n\t" + "{\n\t" + " v5:4 = vcombine(v12, v7)\n\t" + " vmem(%2 + #0) = v5.new\n\t" + "}\n\t" + : : "r"(p0), "r"(p1), "r"(pout) : "v4", "v5", "v7", "v12", "memory"); + + expect[0] = buffer1[0]; + + check_output_w(__LINE__, 1); } static void test_max_temps() @@ -322,100 +304,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,47 +386,113 @@ static void test_vsubuwsat_dv(void) check_output_w(__LINE__, 2); } -static void test_vshuff(void) +static void test_load_tmp_predicated(void) { - /* Test that vshuff works when the two operands are the same register */ - const uint32_t splat = 0x089be55c; - const uint32_t shuff = 0x454fa926; - MMVector v0, v1; + void *p0 = buffer0; + void *p1 = buffer1; + void *pout = output; + bool pred = true; - memset(expect, 0x12, sizeof(MMVector)); - memset(output, 0x34, sizeof(MMVector)); + 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); - asm volatile("v25 = vsplat(%0)\n\t" - "vshuff(v25, v25, %1)\n\t" - "vmem(%2 + #0) = v25\n\t" - : /* no outputs */ - : "r"(splat), "r"(shuff), "r"(output) - : "v25", "memory"); - - /* - * The semantics of Hexagon are the operands are pass-by-value, so create - * two copies of the vsplat result. - */ - for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) { - v0.uw[i] = splat; - v1.uw[i] = splat; + 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; } - /* Do the vshuff operation */ - for (int offset = 1; offset < MAX_VEC_SIZE_BYTES; offset <<= 1) { - if (shuff & offset) { - for (int k = 0; k < MAX_VEC_SIZE_BYTES; k++) { - if (!(k & offset)) { - uint8_t tmp = v0.ub[k]; - v0.ub[k] = v1.ub[k + offset]; - v1.ub[k + offset] = tmp; - } - } + + 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; } } - /* Put the result in the expect buffer for verification */ - expect[0] = v1; + check_output_w(__LINE__, BUFSIZE); +} - check_output_b(__LINE__, 1); +void test_store_new() +{ + asm volatile( + "r0 = #0x12345678\n" + "v0 = vsplat(r0)\n" + "r0 = #0xff00ff00\n" + "v1 = vsplat(r0)\n" + "{\n" + " vdeal(v1,v0,r0)\n" + " vmem(%0) = v0.new\n" + "}\n" + : + : "r"(&output[0]) + : "r0", "v0", "v1", "memory" + ); + for (int i = 0; i < MAX_VEC_SIZE_BYTES / 4; i++) { + expect[0].w[i] = 0x12345678; + } + check_output_w(__LINE__, 1); } int main() @@ -546,6 +500,7 @@ int main() init_buffers(); test_load_tmp(); + test_load_tmp2(); test_load_cur(); test_load_aligned(); test_load_unaligned(); @@ -576,7 +531,12 @@ int main() test_vadduwsat(); test_vsubuwsat_dv(); - test_vshuff(); + test_load_tmp_predicated(); + test_load_cur_predicated(); + + test_vcombine(); + + test_store_new(); 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 4aa26fc388..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) @@ -263,23 +244,23 @@ 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, +TEST_pbr(loadbzw2_pbr, int32_t, Z, 0x00000000, 0x00020081, 0x000a0089, 0x00060085, 0x000e008d) -TEST_pbr(loadbsw2_pbr, int, S, 0x0000ff00, +TEST_pbr(loadbsw2_pbr, int32_t, S, 0x0000ff00, 0x00020081, 0x000aff89, 0x0006ff85, 0x000eff8d) -TEST_pbr(loadbzw4_pbr, long long, Z, 0x0000000000000000LL, +TEST_pbr(loadbzw4_pbr, int64_t, Z, 0x0000000000000000LL, 0x0004008300020081LL, 0x000c008b000a0089LL, 0x0008008700060085LL, 0x0010008f000e008dLL) -TEST_pbr(loadbsw4_pbr, long long, S, 0x0000ff000000ff00LL, +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 0f4064e700..6263d5ef8e 100644 --- a/tests/tcg/hexagon/mem_noshuf.c +++ b/tests/tcg/hexagon/mem_noshuf.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,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,38 +59,39 @@ 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 int pred_lw_sw(int pred, int *p, int *q, int x, int y) +static inline int pred_lw_sw(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" @@ -99,7 +106,8 @@ static inline int pred_lw_sw(int pred, int *p, int *q, int x, int y) return ret; } -static inline int pred_lw_sw_pi(int pred, int *p, int *q, int x, int y) +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" @@ -115,10 +123,10 @@ static inline int pred_lw_sw_pi(int pred, int *p, int *q, int x, int y) return ret; } -static inline long long pred_ld_sd(int pred, long long *p, long long *q, - long long x, long long y) +static inline int64_t pred_ld_sd(bool pred, int64_t *p, int64_t *q, + int64_t x, int64_t y) { - unsigned long long ret; + int64_t ret; asm volatile("p0 = cmp.eq(%5, #0)\n\t" "%0 = %3\n\t" "{\n\t" @@ -131,10 +139,10 @@ static inline long long pred_ld_sd(int pred, long long *p, long long *q, return ret; } -static inline long long pred_ld_sd_pi(int pred, long long *p, long long *q, - long long x, long long y) +static inline int64_t pred_ld_sd_pi(bool pred, int64_t *p, int64_t *q, + int64_t x, int64_t y) { - long long ret; + int64_t ret; asm volatile("p0 = cmp.eq(%5, #0)\n\t" "%0 = %3\n\t" "r7 = %2\n\t" @@ -144,13 +152,13 @@ static inline long long pred_ld_sd_pi(int pred, long long *p, long long *q, "}:mem_noshuf\n" : "=&r"(ret) : "r"(p), "r"(q), "r"(x), "r"(y), "r"(pred) - : "p0", "memory"); + : "r7", "p0", "memory"); return ret; } -static inline unsigned int cancel_sw_lb(int pred, int *p, signed char *q, int x) +static inline int32_t cancel_sw_lb(bool pred, int32_t *p, int8_t *q, int32_t x) { - unsigned int ret; + int32_t ret; asm volatile("p0 = cmp.eq(%4, #0)\n\t" "{\n\t" " if (!p0) memw(%1) = %3\n\t" @@ -162,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" @@ -178,43 +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; - -#define check32(n, expect) check32_(n, expect, __LINE__) - -static void check32_(int n, int expect, int line) -{ - if (n != expect) { - printf("ERROR: 0x%08x != 0x%08x, line %d\n", n, expect, line); - err++; - } -} - -#define check64(n, expect) check64_(n, expect, __LINE__) - -static void check64_(long long n, long long expect, int line) -{ - if (n != expect) { - printf("ERROR: 0x%08llx != 0x%08llx, line %d\n", n, expect, line); - err++; - } -} - int main() { Memory n; - unsigned int res32; - unsigned long long res64; + uint32_t res32; + uint64_t res64; /* * Store byte combinations @@ -328,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); /* @@ -392,45 +377,45 @@ int main() check64(res64, 0xffffffffffffffffLL); n.w[0] = ~0; - res32 = pred_lw_sw(0, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + 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(1, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + 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(0, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + 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(1, &n.w[0], &n.w[0], 0x12345678, 0xc0ffeeda); + 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(0, &n.d[0], &n.d[0], + 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(1, &n.d[0], &n.d[0], + 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(0, &n.d[0], &n.d[0], + 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(1, &n.d[0], &n.d[0], + res64 = pred_ld_sd_pi(true, &n.d[0], &n.d[0], 0x1234567812345678LL, 0xc0ffeedac0ffeedaLL); check64(res64, 0xc0ffeedac0ffeedaLL); check64(n.d[0], 0xc0ffeedac0ffeedaLL); diff --git a/tests/tcg/hexagon/mem_noshuf_exception.c b/tests/tcg/hexagon/mem_noshuf_exception.c index 08660ea3e1..61108a99be 100644 --- a/tests/tcg/hexagon/mem_noshuf_exception.c +++ b/tests/tcg/hexagon/mem_noshuf_exception.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 @@ -34,6 +34,8 @@ #include #include +#include +#include #include #include #include @@ -41,49 +43,31 @@ #include 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 OK_TO_CHANGE_VAL 13 -int ok_to_change = OK_TO_CHANGE_VAL; - -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)) +int32_t ok_to_change = OK_TO_CHANGE_VAL; 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); } int main() { struct sigaction act; - int dummy32; - long long dummy64; + int32_t dummy32; + int64_t dummy64; void *p; /* SIGSEGV test */ @@ -106,8 +90,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); /* * Check that a predicated load where the predicate is false doesn't @@ -122,7 +106,7 @@ int main() "}:mem_noshuf\n\t" : "=r"(dummy32) : : "r18", "r19", "p0", "memory"); - check(ok_to_change, 7); + check32(ok_to_change, 7); /* * Also check that the post-increment doesn't happen when the @@ -138,8 +122,8 @@ int main() "}:mem_noshuf\n\t" : "+r"(p), "=r"(dummy64) : : "r18", "p0", "memory"); - check(ok_to_change, 9); - check((int)p, (int)NULL); + 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/unaligned_pc.c b/tests/tcg/hexagon/unaligned_pc.c new file mode 100644 index 0000000000..e9dc7cb8b5 --- /dev/null +++ b/tests/tcg/hexagon/unaligned_pc.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +/* will be changed in signal handler */ +volatile sig_atomic_t completed_tests; +static jmp_buf after_test; +static int nr_tests; + +void __attribute__((naked)) test_return(void) +{ + asm volatile( + "allocframe(#0x8)\n" + "r0 = #0xffffffff\n" + "framekey = r0\n" + "dealloc_return\n" + : + : + : "r0", "r29", "r30", "r31", "framekey"); +} + +void test_endloop(void) +{ + asm volatile( + "loop0(1f, #2)\n" + "1: r0 = #0x3\n" + "sa0 = r0\n" + "{ nop }:endloop0\n" + : + : + : "r0", "sa0", "lc0", "usr"); +} + +asm( + ".pushsection .text.unaligned\n" + ".org 0x3\n" + ".global test_multi_cof_unaligned\n" + "test_multi_cof_unaligned:\n" + " jumpr r31\n" + ".popsection\n" +); + +#define SYS_EXIT 94 + +void test_multi_cof(void) +{ + asm volatile( + "p0 = cmp.eq(r0, r0)\n" + "{\n" + " if (p0) jump test_multi_cof_unaligned\n" + " if (!p0) jump 1f\n" + "}\n" + "1:" + " r0 = #1\n" + " r6 = #%0\n" + " trap0(#1)\n" + : + : "i"(SYS_EXIT) + : "p0", "r0", "r6"); +} + +void sigbus_handler(int signum) +{ + /* retore framekey after test_return */ + asm volatile( + "r0 = #0\n" + "framekey = r0\n" + : + : + : "r0", "framekey"); + printf("Test %d complete\n", completed_tests); + completed_tests++; + siglongjmp(after_test, 1); +} + +void test_done(void) +{ + int err = (completed_tests != nr_tests); + puts(err ? "FAIL" : "PASS"); + exit(err); +} + +typedef void (*test_fn)(void); + +int main() +{ + test_fn tests[] = { test_return, test_endloop, test_multi_cof, test_done }; + nr_tests = (sizeof(tests) / sizeof(tests[0])) - 1; + + struct sigaction sa = { + .sa_sigaction = sigbus_handler, + .sa_flags = SA_SIGINFO + }; + + if (sigaction(SIGBUS, &sa, NULL) < 0) { + perror("sigaction"); + return EXIT_FAILURE; + } + + sigsetjmp(after_test, 1); + tests[completed_tests](); + + /* should never get here */ + puts("FAIL"); + return 1; +} diff --git a/tests/tcg/hexagon/usr.c b/tests/tcg/hexagon/usr.c index fb4514989c..f0b23d312b 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-2024 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,53 +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_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 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; - /* * Templates for functions to execute an instruction * @@ -122,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) \ @@ -429,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)") @@ -507,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) \ @@ -531,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) \ @@ -547,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) \ @@ -584,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) \ @@ -601,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) \ @@ -621,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) \ @@ -646,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) \ @@ -663,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) \ @@ -678,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) \ @@ -907,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, @@ -1066,6 +1007,11 @@ int main() TEST_P_OP_R(conv_sf2d_chop, SF_QNaN, 0xffffffffffffffffULL, USR_FPINVF); TEST_P_OP_R(conv_sf2d_chop, SF_SNaN, 0xffffffffffffffffULL, USR_FPINVF); + TEST_R_OP_R(conv_sf2uw, SF_zero_neg, 0, USR_CLEAR); + TEST_R_OP_R(conv_sf2uw_chop, SF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_R(conv_sf2ud, SF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_R(conv_sf2ud_chop, SF_zero_neg, 0, USR_CLEAR); + TEST_R_OP_P(conv_df2sf, DF_QNaN, SF_HEX_NaN, USR_CLEAR); TEST_R_OP_P(conv_df2sf, DF_SNaN, SF_HEX_NaN, USR_FPINVF); TEST_R_OP_P(conv_df2uw, DF_QNaN, 0xffffffff, USR_FPINVF); @@ -1079,6 +1025,11 @@ int main() TEST_R_OP_P(conv_df2uw_chop, DF_QNaN, 0xffffffff, USR_FPINVF); TEST_R_OP_P(conv_df2uw_chop, DF_SNaN, 0xffffffff, USR_FPINVF); + TEST_R_OP_P(conv_df2uw, DF_zero_neg, 0, USR_CLEAR); + TEST_R_OP_P(conv_df2uw_chop, DF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_P(conv_df2ud, DF_zero_neg, 0, USR_CLEAR); + TEST_P_OP_P(conv_df2ud_chop, DF_zero_neg, 0, USR_CLEAR); + /* Test for typo in HELPER(conv_df2uw_chop) */ TEST_R_OP_P(conv_df2uw_chop, 0xffffff7f00000001ULL, 0xffffffff, USR_FPINVF); 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 ed922d59c8..4096a1cf31 100644 --- a/tests/tcg/i386/Makefile.softmmu-target +++ b/tests/tcg/i386/Makefile.softmmu-target @@ -25,7 +25,7 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) @@ -33,14 +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) $*) - # 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 bafd8c2180..bbe2c44b2a 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -13,29 +13,29 @@ config-cc.mak: Makefile I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c)) ALL_X86_TESTS=$(I386_SRCS:.c=) -SKIP_I386_TESTS=test-i386-ssse3 test-avx test-3dnow test-mmx +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 -run-plugin-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 @@ -52,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) @@ -69,25 +67,15 @@ 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 $*) - # 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 @@ -103,15 +91,12 @@ test-avx.h: test-avx.py x86.csv test-3dnow: CFLAGS += -masm=intel -O -I. run-test-3dnow: QEMU_OPTS += -cpu max -run-plugin-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 -run-plugin-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 -run-plugin-test-avx: QEMU_OPTS += -cpu max test-avx: test-avx.h 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-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 index c39c0e5bce..230e6d84b8 100644 --- a/tests/tcg/i386/test-avx.c +++ b/tests/tcg/i386/test-avx.c @@ -236,12 +236,15 @@ v4di val_i64[] = { v4di deadbeef = {0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull, 0xa5a5a5a5deadbeefull}; -v4di indexq = {0x000000000000001full, 0x000000000000008full, - 0xffffffffffffffffull, 0xffffffffffffff5full}; -v4di indexd = {0x00000002000000efull, 0xfffffff500000010ull, - 0x0000000afffffff0ull, 0x000000000000000eull}; +/* &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) { @@ -316,6 +319,8 @@ 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]); @@ -324,6 +329,8 @@ int main(int argc, char *argv[]) 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]); @@ -333,6 +340,8 @@ int main(int argc, char *argv[]) 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]); @@ -342,6 +351,8 @@ int main(int argc, char *argv[]) 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]); diff --git a/tests/tcg/i386/test-avx.py b/tests/tcg/i386/test-avx.py index d9ca00a49e..6063fb2d11 100755 --- a/tests/tcg/i386/test-avx.py +++ b/tests/tcg/i386/test-avx.py @@ -9,7 +9,7 @@ from fnmatch import fnmatch archs = [ "SSE", "SSE2", "SSE3", "SSSE3", "SSE4_1", "SSE4_2", "AES", "AVX", "AVX2", "AES+AVX", "VAES+AVX", - "F16C", "FMA", + "F16C", "FMA", "SHA", ] ignore = set(["FISTTP", @@ -43,13 +43,14 @@ imask = { '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': 0x33, + 'VPERM2[FI]128': 0xbb, 'VPERMPD': 0xff, 'VPERMQ': 0xff, 'VPERMILPS': 0xff, 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.c b/tests/tcg/i386/test-i386.c index 864c4e620d..ce3bf74b5a 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -715,6 +715,30 @@ void test_mul(void) printf("%-10s A=" FMTLX " R=" FMTLX " %ld\n", #op, val, res, resz);\ } +void test_xcnt(void) +{ + TEST_BSX(tzcntw, "w", 0); + TEST_BSX(tzcntw, "w", 0x12340128); + TEST_BSX(lzcntw, "w", 0); + TEST_BSX(lzcntw, "w", 0x12340128); + TEST_BSX(popcntw, "w", 0); + TEST_BSX(popcntw, "w", 0x12340128); + TEST_BSX(tzcntl, "k", 0); + TEST_BSX(tzcntl, "k", 0x00340128); + TEST_BSX(lzcntl, "k", 0); + TEST_BSX(lzcntl, "k", 0x00340128); + TEST_BSX(popcntl, "k", 0); + TEST_BSX(popcntl, "k", 0x00340128); +#if defined(__x86_64__) + TEST_BSX(tzcntq, "", 0); + TEST_BSX(tzcntq, "", 0x003401281234); + TEST_BSX(lzcntq, "", 0); + TEST_BSX(lzcntq, "", 0x003401281234); + TEST_BSX(popcntq, "", 0); + TEST_BSX(popcntq, "", 0x003401281234); +#endif +} + void test_bsx(void) { TEST_BSX(bsrw, "w", 0); @@ -2162,6 +2186,7 @@ int main(int argc, char **argv) func(); } test_bsx(); + test_xcnt(); test_mul(); test_jcc(); test_loop(); diff --git a/tests/tcg/i386/x86.csv b/tests/tcg/i386/x86.csv index c43bf42dd3..5c0f628e35 100644 --- a/tests/tcg/i386/x86.csv +++ b/tests/tcg/i386/x86.csv @@ -19,7 +19,7 @@ # # 4. The instruction encoding. For example, "C1 /4 ib". # -# 5. The validity of the instruction in 32-bit (aka compatiblity, legacy) mode. +# 5. The validity of the instruction in 32-bit (aka compatibility, legacy) mode. # # 6. The validity of the instruction in 64-bit mode. # diff --git a/tests/tcg/loongarch64/Makefile.softmmu-target b/tests/tcg/loongarch64/Makefile.softmmu-target index 908f3a8c0f..6d4a20fde7 100644 --- a/tests/tcg/loongarch64/Makefile.softmmu-target +++ b/tests/tcg/loongarch64/Makefile.softmmu-target @@ -16,13 +16,13 @@ LINK_SCRIPT=$(LOONGARCH64_SYSTEM_SRC)/kernel.ld LDFLAGS=-Wl,-T$(LINK_SCRIPT) TESTS+=$(LOONGARCH64_TESTS) $(MULTIARCH_TESTS) CFLAGS+=-nostdlib -g -O1 -march=loongarch64 -mabi=lp64d $(MINILIB_INC) -LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc +LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc -Wl,--no-warn-rwx-segments # building head blobs .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) 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/system/regdef.h b/tests/tcg/loongarch64/system/regdef.h index faa09b2377..b586b4e86d 100644 --- a/tests/tcg/loongarch64/system/regdef.h +++ b/tests/tcg/loongarch64/system/regdef.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2021 Loongson Technology Corporation Limited */ 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/minilib/printf.c b/tests/tcg/minilib/printf.c index 10472b4f58..fb0189c2bb 100644 --- a/tests/tcg/minilib/printf.c +++ b/tests/tcg/minilib/printf.c @@ -27,7 +27,7 @@ static void print_str(char *s) static void print_num(unsigned long long value, int base) { - char digits[] = "0123456789abcdef"; + static const char digits[] = "0123456789abcdef"; char buf[32]; int i = sizeof(buf) - 2, j; 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 5f0fee1aad..18d3cf4ae0 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -39,48 +39,107 @@ signals: LDFLAGS+=-lrt -lpthread munmap-pthread: CFLAGS+=-pthread munmap-pthread: LDFLAGS+=-pthread -# 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. +vma-pthread: CFLAGS+=-pthread +vma-pthread: LDFLAGS+=-pthread + +sigreturn-sigmask: CFLAGS+=-pthread +sigreturn-sigmask: 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)) -# 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)) - -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) 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) +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) +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 # @@ -114,5 +173,16 @@ run-plugin-semiconsole-with-%: TESTS += semihosting semiconsole endif +# Test plugin memory access instrumentation +run-plugin-test-plugin-mem-access-with-libmem.so: \ + PLUGIN_ARGS=$(COMMA)print-accesses=true +run-plugin-test-plugin-mem-access-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND= \ + $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ + $(QEMU) $< + +test-plugin-mem-access: CFLAGS+=-pthread -O0 +test-plugin-mem-access: LDFLAGS+=-pthread -O0 + # Update TESTS TESTS += $(MULTIARCH_TESTS) 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/check-plugin-output.sh b/tests/tcg/multiarch/check-plugin-output.sh new file mode 100755 index 0000000000..80607f04b5 --- /dev/null +++ b/tests/tcg/multiarch/check-plugin-output.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# This script runs a given executable using qemu, and compare its standard +# output with an expected plugin output. +# Each line of output is searched (as a regexp) in the expected plugin output. + +set -euo pipefail + +die() +{ + echo "$@" 1>&2 + exit 1 +} + +check() +{ + file=$1 + pattern=$2 + grep "$pattern" "$file" > /dev/null || die "\"$pattern\" not found in $file" +} + +[ $# -eq 3 ] || die "usage: qemu_bin exe plugin_out_file" + +qemu_bin=$1; shift +exe=$1;shift +plugin_out=$1; shift + +expected() +{ + $qemu_bin $exe || + die "running $exe failed" +} + +expected | while read line; do + check "$plugin_out" "$line" +done 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..2d5654d154 --- /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 gdb_exit, 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") + gdb_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..51082a30e4 --- /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 gdb_exit, 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") + gdb_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..6eb6ebf7b1 --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -0,0 +1,22 @@ +"""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 gdb_exit, main, report + + +def run_test(): + """Run through the tests one by one""" + if gdb.selected_inferior().architecture().name() == "m68k": + # m68k GDB supports only GDB_OSABI_SVR4, but GDB_OSABI_LINUX is + # required for the info proc support (see set_gdbarch_info_proc()). + print("SKIP: m68k GDB does not support GDB_OSABI_LINUX") + gdb_exit(0) + mappings = gdb.execute("info proc mappings", False, True) + 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 index 29d0997e68..539fb3b772 100644 --- a/tests/tcg/multiarch/linux/linux-madvise.c +++ b/tests/tcg/multiarch/linux/linux-madvise.c @@ -42,6 +42,8 @@ static void test_file(void) 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); 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-sigrtminmax.c b/tests/tcg/multiarch/linux/linux-sigrtminmax.c new file mode 100644 index 0000000000..a7059aacd9 --- /dev/null +++ b/tests/tcg/multiarch/linux/linux-sigrtminmax.c @@ -0,0 +1,74 @@ +/* + * Test the lowest and the highest real-time signals. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include +#include + +/* For hexagon and microblaze. */ +#ifndef __SIGRTMIN +#define __SIGRTMIN 32 +#endif + +extern char **environ; + +static bool seen_sigrtmin, seen_sigrtmax; + +static void handle_signal(int sig) +{ + if (sig == SIGRTMIN) { + seen_sigrtmin = true; + } else if (sig == SIGRTMAX) { + seen_sigrtmax = true; + } else { + _exit(1); + } +} + +int main(int argc, char **argv) +{ + char *qemu = getenv("QEMU"); + struct sigaction act; + + assert(qemu); + + if (!getenv("QEMU_RTSIG_MAP")) { + char **new_argv = malloc((argc + 2) + sizeof(char *)); + int tsig1, hsig1, count1, tsig2, hsig2, count2; + char rt_sigmap[64]; + + /* Re-exec with a mapping that includes SIGRTMIN and SIGRTMAX. */ + new_argv[0] = qemu; + memcpy(&new_argv[1], argv, (argc + 1) * sizeof(char *)); + tsig1 = __SIGRTMIN; + /* The host must have a few signals starting from this one. */ + hsig1 = 36; + count1 = SIGRTMIN - __SIGRTMIN + 1; + tsig2 = SIGRTMAX; + hsig2 = hsig1 + count1; + count2 = 1; + snprintf(rt_sigmap, sizeof(rt_sigmap), "%d %d %d,%d %d %d", + tsig1, hsig1, count1, tsig2, hsig2, count2); + setenv("QEMU_RTSIG_MAP", rt_sigmap, 0); + assert(execve(new_argv[0], new_argv, environ) == 0); + return EXIT_FAILURE; + } + + memset(&act, 0, sizeof(act)); + act.sa_handler = handle_signal; + assert(sigaction(SIGRTMIN, &act, NULL) == 0); + assert(sigaction(SIGRTMAX, &act, NULL) == 0); + + assert(kill(getpid(), SIGRTMIN) == 0); + assert(seen_sigrtmin); + assert(kill(getpid(), SIGRTMAX) == 0); + assert(seen_sigrtmax); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/munmap-pthread.c b/tests/tcg/multiarch/munmap-pthread.c index d7143b00d5..1c79005846 100644 --- a/tests/tcg/multiarch/munmap-pthread.c +++ b/tests/tcg/multiarch/munmap-pthread.c @@ -7,21 +7,7 @@ #include #include -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 -}; +#include "nop_func.h" static void *thread_mmap_munmap(void *arg) { 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/sigreturn-sigmask.c b/tests/tcg/multiarch/sigreturn-sigmask.c new file mode 100644 index 0000000000..e6cc904898 --- /dev/null +++ b/tests/tcg/multiarch/sigreturn-sigmask.c @@ -0,0 +1,51 @@ +/* + * Test that sigreturn() does not corrupt the signal mask. + * Block SIGUSR2 and handle SIGUSR1. + * Then sigwait() SIGUSR2, which relies on it remaining blocked. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include + +int seen_sig = -1; + +static void signal_func(int sig) +{ + seen_sig = sig; +} + +static void *thread_func(void *arg) +{ + kill(getpid(), SIGUSR2); + return NULL; +} + +int main(void) +{ + struct sigaction act = { + .sa_handler = signal_func, + }; + pthread_t thread; + sigset_t set; + int sig; + + assert(sigaction(SIGUSR1, &act, NULL) == 0); + + assert(sigemptyset(&set) == 0); + assert(sigaddset(&set, SIGUSR2) == 0); + assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0); + + kill(getpid(), SIGUSR1); + assert(seen_sig == SIGUSR1); + + assert(pthread_create(&thread, NULL, thread_func, NULL) == 0); + assert(sigwait(&set, &sig) == 0); + assert(sig == SIGUSR2); + assert(pthread_join(thread, NULL) == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 368b64d531..07be001102 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,60 @@ 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) +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 + +# Test plugin memory access instrumentation +run-plugin-memory-with-libmem.so: \ + PLUGIN_ARGS=$(COMMA)region-summary=true +run-plugin-memory-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out 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 214f7d4f54..65a6038a24 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -1,57 +1,69 @@ /* * 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 #ifndef CHECK_UNALIGNED # error "Target does not specify CHECK_UNALIGNED" #endif +uint32_t test_read_count; +uint32_t test_write_count; + #define MEM_PAGE_SIZE 4096 /* nominal 4k "pages" */ #define TEST_SIZE (MEM_PAGE_SIZE * 4) /* 4 pages */ #define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0]))) -__attribute__((aligned(MEM_PAGE_SIZE))) +__attribute__((aligned(TEST_SIZE))) static uint8_t test_data[TEST_SIZE]; typedef void (*init_ufn) (int offset); typedef bool (*read_ufn) (int offset); typedef bool (*read_sfn) (int offset, bool nf); -static void pdot(int count) +static void pdot(int count, bool write) { + if (write) { + test_write_count++; + } else { + test_read_count++; + } if (count % 128 == 0) { ml_printf("."); } } /* - * 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) @@ -60,16 +72,18 @@ static void init_test_data_u8(int unused_offset) int i; (void)(unused_offset); - ml_printf("Filling test area with u8:"); + ml_printf("Filling test area with u8 (%p):", ptr); + for (i = 0; i < TEST_SIZE; i++) { - *ptr++ = count++; - pdot(i); + *ptr++ = BYTE_NEXT(count); + pdot(i, true); } - ml_printf("done\n"); + + ml_printf("done %d @ %p\n", i, ptr); } /* - * 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. */ @@ -88,10 +102,11 @@ static void init_test_data_s8(bool neg_first) neg_first ? "neg first" : "pos first"); for (i = 0; i < TEST_SIZE / 2; i++) { *ptr++ = get_byte(i, neg_first); + pdot(i, true); *ptr++ = get_byte(i, !neg_first); - pdot(i); + pdot(i, true); } - ml_printf("done\n"); + ml_printf("done %d @ %p\n", i * 2, ptr); } /* @@ -102,9 +117,19 @@ static void reset_start_data(int offset) { uint32_t *ptr = (uint32_t *) &test_data[0]; int i; + + if (!offset) { + return; + } + + ml_printf("Flushing %d bytes from %p: ", offset, ptr); + for (i = 0; i < offset; i++) { *ptr++ = 0; + pdot(i, true); } + + ml_printf("done %d @ %p\n", i, ptr); } static void init_test_data_u16(int offset) @@ -114,17 +139,17 @@ static void init_test_data_u16(int offset) const int max = (TEST_SIZE - offset) / sizeof(word); int i; - ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr); - reset_start_data(offset); + ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr); + 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); + pdot(i, true); } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); } static void init_test_data_u32(int offset) @@ -134,20 +159,22 @@ static void init_test_data_u32(int offset) const int max = (TEST_SIZE - offset) / sizeof(word); int i; - ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr); - reset_start_data(offset); + ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr); + 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); + pdot(i, true); } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); } +#if __SIZEOF_POINTER__ >= 8 static void init_test_data_u64(int offset) { uint8_t count = 0; @@ -155,23 +182,24 @@ static void init_test_data_u64(int offset) const int max = (TEST_SIZE - offset) / sizeof(word); int i; - ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr); - reset_start_data(offset); + ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr); + 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); + pdot(i, true); } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); } +#endif static bool read_test_data_u16(int offset) { @@ -190,11 +218,11 @@ static bool read_test_data_u16(int offset) ml_printf("Error %d < %d\n", high, low); return false; } else { - pdot(i); + pdot(i, false); } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } @@ -232,13 +260,14 @@ static bool read_test_data_u32(int offset) ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4); return false; } else { - pdot(i); + pdot(i, false); } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } +#if __SIZEOF_POINTER__ >= 8 static bool read_test_data_u64(int offset) { uint64_t word, *ptr = (uint64_t *)&test_data[offset]; @@ -286,17 +315,22 @@ static bool read_test_data_u64(int offset) b1, b2, b3, b4, b5, b6, b7, b8); return false; } else { - pdot(i); + pdot(i, false); } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } +#endif /* Read the test data and verify at various offsets */ -read_ufn read_ufns[] = { read_test_data_u16, - read_test_data_u32, - read_test_data_u64 }; +read_ufn read_ufns[] = { + read_test_data_u16, + read_test_data_u32, +#if __SIZEOF_POINTER__ >= 8 + read_test_data_u64 +#endif +}; bool do_unsigned_reads(int start_off) { @@ -353,15 +387,17 @@ static bool read_test_data_s8(int offset, bool neg_first) second = *ptr++; if (neg_first && first < 0 && second > 0) { - pdot(i); + pdot(i, false); + pdot(i, false); } else if (!neg_first && first > 0 && second < 0) { - pdot(i); + pdot(i, false); + pdot(i, false); } else { ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', second); return false; } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i * 2, ptr); return true; } @@ -374,19 +410,27 @@ 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) { - pdot(i); + pdot(i, false); + } else if (!neg_first && data > 0) { + pdot(i, false); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); return false; } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } @@ -399,19 +443,27 @@ 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) { - pdot(i); + pdot(i, false); + } else if (!neg_first && data > 0) { + pdot(i, false); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); return false; } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } @@ -419,8 +471,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, @@ -446,16 +497,23 @@ bool do_signed_reads(bool neg_first) return ok; } -init_ufn init_ufns[] = { init_test_data_u8, - init_test_data_u16, - init_test_data_u32, - init_test_data_u64 }; +init_ufn init_ufns[] = { + init_test_data_u8, + init_test_data_u16, + init_test_data_u32, +#if __SIZEOF_POINTER__ >= 8 + init_test_data_u64 +#endif +}; int main(void) { int i; bool ok = true; + ml_printf("Test data start: 0x%"PRIxPTR"\n", &test_data[0]); + ml_printf("Test data end: 0x%"PRIxPTR"\n", &test_data[TEST_SIZE]); + /* Run through the unsigned tests first */ for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) { ok = do_unsigned_test(init_ufns[i]); @@ -471,6 +529,8 @@ int main(void) ok = do_signed_reads(true); } + ml_printf("Test data read: %"PRId32"\n", test_read_count); + ml_printf("Test data write: %"PRId32"\n", test_write_count); ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED"); return ok ? 0 : -1; } diff --git a/tests/tcg/multiarch/system/validate-memory-counts.py b/tests/tcg/multiarch/system/validate-memory-counts.py new file mode 100755 index 0000000000..5b8bbf3ef3 --- /dev/null +++ b/tests/tcg/multiarch/system/validate-memory-counts.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# +# validate-memory-counts.py: check we instrumented memory properly +# +# This program takes two inputs: +# - the mem plugin output +# - the memory binary output +# +# Copyright (C) 2024 Linaro Ltd +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +from argparse import ArgumentParser + +def extract_counts(path): + """ + Load the output from path and extract the lines containing: + + Test data start: 0x40214000 + Test data end: 0x40218001 + Test data read: 2522280 + Test data write: 262111 + + From the stream of data. Extract the values for use in the + validation function. + """ + start_address = None + end_address = None + read_count = 0 + write_count = 0 + with open(path, 'r') as f: + for line in f: + if line.startswith("Test data start:"): + start_address = int(line.split(':')[1].strip(), 16) + elif line.startswith("Test data end:"): + end_address = int(line.split(':')[1].strip(), 16) + elif line.startswith("Test data read:"): + read_count = int(line.split(':')[1].strip()) + elif line.startswith("Test data write:"): + write_count = int(line.split(':')[1].strip()) + return start_address, end_address, read_count, write_count + + +def parse_plugin_output(path, start, end): + """ + Load the plugin output from path in the form of: + + Region Base, Reads, Writes, Seen all + 0x0000000040004000, 31093, 0, false + 0x0000000040214000, 2522280, 278579, true + 0x0000000040000000, 137398, 0, false + 0x0000000040210000, 54727397, 33721956, false + + And extract the ranges that match test data start and end and + return the results. + """ + total_reads = 0 + total_writes = 0 + seen_all = False + + with open(path, 'r') as f: + next(f) # Skip the header + for line in f: + + if line.startswith("Region Base"): + continue + + parts = line.strip().split(', ') + if len(parts) != 4: + continue + + region_base = int(parts[0], 16) + reads = int(parts[1]) + writes = int(parts[2]) + + if start <= region_base < end: # Checking if within range + total_reads += reads + total_writes += writes + seen_all = parts[3] == "true" + + return total_reads, total_writes, seen_all + +def main() -> None: + """ + Process the arguments, injest the program and plugin out and + verify they match up and report if they do not. + """ + parser = ArgumentParser(description="Validate memory instrumentation") + parser.add_argument('test_output', + help="The output from the test itself") + parser.add_argument('plugin_output', + help="The output from memory plugin") + parser.add_argument('--bss-cleared', + action='store_true', + help='Assume bss was cleared (and adjusts counts).') + + args = parser.parse_args() + + # Extract counts from memory binary + start, end, exp_reads, exp_writes = extract_counts(args.test_output) + + # Some targets clear BSS before running but the test doesn't know + # that so we adjust it by the size of the test region. + if args.bss_cleared: + exp_writes += 16384 + + if start is None or end is None: + print("Failed to test_data boundaries from output.") + sys.exit(1) + + # Parse plugin output + preads, pwrites, seen_all = parse_plugin_output(args.plugin_output, + start, end) + + if not seen_all: + print("Fail: didn't instrument all accesses to test_data.") + sys.exit(1) + + # Compare and report + if preads == exp_reads and pwrites == exp_writes: + sys.exit(0) + else: + print("Fail: The memory reads and writes count does not match.") + print(f"Expected Reads: {exp_reads}, Actual Reads: {preads}") + print(f"Expected Writes: {exp_writes}, Actual Writes: {pwrites}") + sys.exit(1) + +if __name__ == "__main__": + main() 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-plugin-mem-access.c b/tests/tcg/multiarch/test-plugin-mem-access.c new file mode 100644 index 0000000000..057b9aac9f --- /dev/null +++ b/tests/tcg/multiarch/test-plugin-mem-access.c @@ -0,0 +1,177 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Check if we detect all memory accesses expected using plugin API. + * Used in conjunction with ./check-plugin-mem-access.sh check script. + * Output of this program is the list of patterns expected in plugin output. + * + * 8,16,32 load/store are tested for all arch. + * 64,128 load/store are tested for aarch64/x64. + * atomic operations (8,16,32,64) are tested for x64 only. + */ + +#include +#include +#include +#include + +#if defined(__x86_64__) +#include +#elif defined(__aarch64__) +#include +#endif /* __x86_64__ */ + +static void *data; + +/* ,store_u8,.*,8,store,0xf1 */ +#define PRINT_EXPECTED(function, type, value, action) \ +do { \ + printf(",%s,.*,%d,%s,%s\n", \ + #function, (int) sizeof(type) * 8, action, value); \ +} \ +while (0) + +#define DEFINE_STORE(name, type, value) \ + \ +static void print_expected_store_##name(void) \ +{ \ + PRINT_EXPECTED(store_##name, type, #value, "store"); \ +} \ + \ +static void store_##name(void) \ +{ \ + *((type *)data) = value; \ + print_expected_store_##name(); \ +} + +#define DEFINE_ATOMIC_OP(name, type, value) \ + \ +static void print_expected_atomic_op_##name(void) \ +{ \ + PRINT_EXPECTED(atomic_op_##name, type, "0x0*42", "load"); \ + PRINT_EXPECTED(atomic_op_##name, type, #value, "store"); \ +} \ + \ +static void atomic_op_##name(void) \ +{ \ + *((type *)data) = 0x42; \ + __sync_val_compare_and_swap((type *)data, 0x42, value); \ + print_expected_atomic_op_##name(); \ +} + +#define DEFINE_LOAD(name, type, value) \ + \ +static void print_expected_load_##name(void) \ +{ \ + PRINT_EXPECTED(load_##name, type, #value, "load"); \ +} \ + \ +static void load_##name(void) \ +{ \ + \ + /* volatile forces load to be generated. */ \ + volatile type src = *((type *) data); \ + volatile type dest = src; \ + (void)src, (void)dest; \ + print_expected_load_##name(); \ +} + +DEFINE_STORE(u8, uint8_t, 0xf1) +DEFINE_LOAD(u8, uint8_t, 0xf1) +DEFINE_STORE(u16, uint16_t, 0xf123) +DEFINE_LOAD(u16, uint16_t, 0xf123) +DEFINE_STORE(u32, uint32_t, 0xff112233) +DEFINE_LOAD(u32, uint32_t, 0xff112233) + +#if defined(__x86_64__) || defined(__aarch64__) +DEFINE_STORE(u64, uint64_t, 0xf123456789abcdef) +DEFINE_LOAD(u64, uint64_t, 0xf123456789abcdef) + +static void print_expected_store_u128(void) +{ + PRINT_EXPECTED(store_u128, __int128, + "0xf122334455667788f123456789abcdef", "store"); +} + +static void store_u128(void) +{ +#ifdef __x86_64__ + _mm_store_si128(data, _mm_set_epi32(0xf1223344, 0x55667788, + 0xf1234567, 0x89abcdef)); +#else + const uint32_t init[4] = {0x89abcdef, 0xf1234567, 0x55667788, 0xf1223344}; + uint32x4_t vec = vld1q_u32(init); + vst1q_u32(data, vec); +#endif /* __x86_64__ */ + print_expected_store_u128(); +} + +static void print_expected_load_u128(void) +{ + PRINT_EXPECTED(load_u128, __int128, + "0xf122334455667788f123456789abcdef", "load"); +} + +static void load_u128(void) +{ +#ifdef __x86_64__ + __m128i var = _mm_load_si128(data); +#else + uint32x4_t var = vld1q_u32(data); +#endif + (void) var; + print_expected_load_u128(); +} +#endif /* __x86_64__ || __aarch64__ */ + +#if defined(__x86_64__) +DEFINE_ATOMIC_OP(u8, uint8_t, 0xf1) +DEFINE_ATOMIC_OP(u16, uint16_t, 0xf123) +DEFINE_ATOMIC_OP(u32, uint32_t, 0xff112233) +DEFINE_ATOMIC_OP(u64, uint64_t, 0xf123456789abcdef) +#endif /* __x86_64__ */ + +static void *f(void *p) +{ + return NULL; +} + +int main(void) +{ + /* + * We force creation of a second thread to enable cpu flag CF_PARALLEL. + * This will generate atomic operations when needed. + */ + pthread_t thread; + pthread_create(&thread, NULL, &f, NULL); + pthread_join(thread, NULL); + + /* allocate storage up to 128 bits */ + data = malloc(16); + + store_u8(); + load_u8(); + + store_u16(); + load_u16(); + + store_u32(); + load_u32(); + +#if defined(__x86_64__) || defined(__aarch64__) + store_u64(); + load_u64(); + + store_u128(); + load_u128(); +#endif /* __x86_64__ || __aarch64__ */ + +#if defined(__x86_64__) + atomic_op_u8(); + atomic_op_u16(); + atomic_op_u32(); + atomic_op_u64(); +#endif /* __x86_64__ */ + + free(data); +} 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 deleted file mode 100644 index 71cdda450c..0000000000 --- a/tests/tcg/nios2/10m50-ghrd.ld +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Link script for the Nios2 10m50-ghrd board. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -MEMORY -{ - tpf (rx) : ORIGIN = 0xc0000000, LENGTH = 1K - ram (rwx) : ORIGIN = 0xc8000000, LENGTH = 128M -} - -PHDRS -{ - RAM PT_LOAD; -} - -ENTRY(_start) -EXTERN(_start) -EXTERN(_interrupt) -EXTERN(_fast_tlb_miss) - -SECTIONS -{ - /* Begin at the (hardcoded) _interrupt entry point. */ - .text 0xc8000120 : { - *(.text.intr) - *(.text .text.* .gnu.linkonce.t.*) - } >ram :RAM - - .rodata : ALIGN(4) { - *(.rodata .rodata.* .gnu.linkonce.r.*) - } > ram :RAM - - .eh_frame_hdr : ALIGN (4) { - KEEP (*(.eh_frame_hdr)) - *(.eh_frame_entry .eh_frame_entry.*) - } >ram :RAM - .eh_frame : ALIGN (4) { - KEEP (*(.eh_frame)) *(.eh_frame.*) - } >ram :RAM - - .data : ALIGN(4) { - *(.shdata) - *(.data .data.* .gnu.linkonce.d.*) - } >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 - - .bss : ALIGN(4) { - __bss_start = ABSOLUTE(.); - *(.sbss .sbss.* .gnu.linkonce.sb.*) - *(.scommon) - *(.bss .bss.* .gnu.linkonce.b.*) - *(COMMON) - . = ALIGN(4); - __bss_end = ABSOLUTE(.); - } >ram :RAM - - __stack = ORIGIN(ram) + LENGTH(ram); -} diff --git a/tests/tcg/nios2/Makefile.softmmu-target b/tests/tcg/nios2/Makefile.softmmu-target deleted file mode 100644 index bc7fd55060..0000000000 --- a/tests/tcg/nios2/Makefile.softmmu-target +++ /dev/null @@ -1,32 +0,0 @@ -# -# Nios2 system tests -# -# Copyright Linaro Ltd 2022 -# SPDX-License-Identifier: GPL-2.0-or-later -# - -NIOS2_SYSTEM_SRC = $(SRC_PATH)/tests/tcg/nios2 -VPATH += $(NIOS2_SYSTEM_SRC) - -# These objects provide the basic boot code and helper functions for all tests -CRT_OBJS = boot.o intr.o $(MINILIB_OBJS) -LINK_SCRIPT = $(NIOS2_SYSTEM_SRC)/10m50-ghrd.ld - -CFLAGS += -nostdlib -g -O0 $(MINILIB_INC) -LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc - -%.o: %.S - $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@, AS, $@) - -%.o: %.c - $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@, CC, $@) - -# Build and link the tests -%: %.o $(LINK_SCRIPT) $(CRT_OBJS) - $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@) - -QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting-config enable=on,target=native,chardev=output -kernel - -memory: CFLAGS+=-DCHECK_UNALIGNED=0 -TESTS += $(MULTIARCH_TESTS) -TESTS += test-shadow-1 diff --git a/tests/tcg/nios2/boot.S b/tests/tcg/nios2/boot.S deleted file mode 100644 index f6771cbc81..0000000000 --- a/tests/tcg/nios2/boot.S +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Minimal Nios2 system boot code. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "semicall.h" - - .text - .set noat - -_start: - /* Linker script defines stack at end of ram. */ - movia sp, __stack - - /* Install trampoline to _fast_tlb_miss at hardcoded vector. */ - movia r4, 0xc0000100 - movia r5, _ftm_tramp - movi r6, .L__ftm_end - _ftm_tramp - call memcpy - - /* Zero the bss to satisfy C. */ - movia r4, __bss_start - movia r6, __bss_end - sub r6, r6, r4 - movi r5, 0 - call memset - - /* Test! */ - call main - - /* Exit with main's return value. */ - movi r4, HOSTED_EXIT - mov r5, r2 - semihosting_call - - .globl _start - .type _start, @function - .size _start, . - _start - -_ftm_tramp: - movia et, _fast_tlb_miss - jmp et -.L__ftm_end: - - .type _ftm_tramp, @function - .size _ftm_tramp, . - _ftm_tramp - -#define dst r4 -#define src r5 -#define len r6 - -memcpy: - /* Store return value right away, per API */ - mov r2, dst - - /* Check for both dst and src aligned. */ - or at, dst, src - andi at, at, 3 - bne at, zero, .L_mc_test1 - - /* Copy blocks of 8. */ - - movi at, 8 - bltu len, at, .L_mc_test4 - -.L_mc_loop8: - ldw r8, 0(src) - ldw r9, 4(src) - addi src, src, 8 - addi dst, dst, 8 - subi len, len, 8 - stw r8, -8(dst) - stw r9, -4(dst) - bgeu len, at, .L_mc_loop8 - - /* Copy final aligned block of 4. */ - -.L_mc_test4: - movi at, 4 - bltu len, at, .L_mc_test1 - - ldw r8, 0(src) - addi src, src, 4 - addi dst, dst, 4 - subi len, len, 4 - stw r8, -4(dst) - - /* Copy single bytes to finish. */ - -.L_mc_test1: - beq len, zero, .L_mc_done - -.L_mc_loop1: - ldb r8, 0(src) - addi src, src, 1 - addi dst, dst, 1 - subi len, len, 1 - stb r8, -1(dst) - bne len, zero, .L_mc_loop1 - -.L_mc_done: - ret - -#undef dst -#undef src -#undef len - - .global memcpy - .type memcpy, @function - .size memcpy, . - memcpy - -#define dst r4 -#define val r5 -#define len r6 - -memset: - /* Store return value right away, per API */ - mov r2, dst - - /* Check for small blocks; fall back to bytewise. */ - movi r3, 8 - bltu len, r3, .L_ms_test1 - - /* Replicate the byte across the word. */ - andi val, val, 0xff - slli at, val, 8 - or val, val, at - slli at, val, 16 - or val, val, at - - /* Check for destination alignment; realign if needed. */ - andi at, dst, 3 - bne at, zero, .L_ms_align - - /* Set blocks of 8. */ - -.L_ms_loop8: - stw val, 0(dst) - stw val, 4(dst) - addi dst, dst, 8 - subi len, len, 8 - bgeu len, r3, .L_ms_loop8 - - /* Set final aligned block of 4. */ - -.L_ms_test4: - movi at, 4 - bltu len, at, .L_ms_test1 - - stw r8, 0(dst) - addi dst, dst, 4 - subi len, len, 4 - stw r8, -4(dst) - - /* Set single bytes to finish. */ - -.L_ms_test1: - beq len, zero, .L_ms_done - -.L_ms_loop1: - stb r8, 0(dst) - addi dst, dst, 1 - subi len, len, 1 - bne len, zero, .L_ms_loop1 - -.L_ms_done: - ret - - /* Realign for a large block, len >= 8. */ -.L_ms_align: - andi at, dst, 1 - beq at, zero, 2f - - stb val, 0(dst) - addi dst, dst, 1 - subi len, len, 1 - -2: andi at, dst, 2 - beq at, zero, 4f - - sth val, 0(dst) - addi dst, dst, 2 - subi len, len, 2 - -4: bgeu len, r3, .L_ms_loop8 - br .L_ms_test4 - -#undef dst -#undef val -#undef len - - .global memset - .type memset, @function - .size memset, . - memset - -/* - * void __sys_outc(char c); - */ -__sys_outc: - subi sp, sp, 16 - stb r4, 0(sp) /* buffer[0] = c */ - movi at, 1 - stw at, 4(sp) /* STDOUT_FILENO */ - stw sp, 8(sp) /* buffer */ - stw at, 12(sp) /* len */ - - movi r4, HOSTED_WRITE - addi r5, sp, 4 - semihosting_call - - addi sp, sp, 16 - ret - - .global __sys_outc - .type __sys_outc, @function - .size __sys_outc, . - __sys_outc diff --git a/tests/tcg/nios2/intr.S b/tests/tcg/nios2/intr.S deleted file mode 100644 index c1730692ba..0000000000 --- a/tests/tcg/nios2/intr.S +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Minimal Nios2 system boot code -- exit on interrupt. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "semicall.h" - - .section .text.intr, "ax" - .global _interrupt - .type _interrupt, @function - -_interrupt: - rdctl r5, exception /* extract exception.CAUSE */ - srli r5, r5, 2 - movi r4, HOSTED_EXIT - semihosting_call - - .size _interrupt, . - _interrupt - - .text - .global _fast_tlb_miss - .type _fast_tlb_miss, @function - -_fast_tlb_miss: - movi r5, 32 - movi r4, HOSTED_EXIT - semihosting_call - - .size _fast_tlb_miss, . - _fast_tlb_miss diff --git a/tests/tcg/nios2/semicall.h b/tests/tcg/nios2/semicall.h deleted file mode 100644 index 6ad4978099..0000000000 --- a/tests/tcg/nios2/semicall.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Nios2 semihosting interface. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef SEMICALL_H -#define SEMICALL_H - -#define HOSTED_EXIT 0 -#define HOSTED_INIT_SIM 1 -#define HOSTED_OPEN 2 -#define HOSTED_CLOSE 3 -#define HOSTED_READ 4 -#define HOSTED_WRITE 5 -#define HOSTED_LSEEK 6 -#define HOSTED_RENAME 7 -#define HOSTED_UNLINK 8 -#define HOSTED_STAT 9 -#define HOSTED_FSTAT 10 -#define HOSTED_GETTIMEOFDAY 11 -#define HOSTED_ISATTY 12 -#define HOSTED_SYSTEM 13 - -#define semihosting_call break 1 - -#endif /* SEMICALL_H */ diff --git a/tests/tcg/nios2/test-shadow-1.S b/tests/tcg/nios2/test-shadow-1.S deleted file mode 100644 index 79ef69db12..0000000000 --- a/tests/tcg/nios2/test-shadow-1.S +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Regression test for TCG indirect global lowering. - * - * Copyright Linaro Ltd 2022 - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include "semicall.h" - - .text - .set noat - .align 2 - .globl main - .type main, @function - -main: - /* Initialize r0 in shadow register set 1. */ - movhi at, 1 /* PRS=1, CRS=0, RSIE=0, PIE=0 */ - wrctl status, at - wrprs zero, zero - - /* Change current register set to 1. */ - movi at, 1 << 10 /* PRS=0, CRS=1, RSIE=0, PIE=0 */ - wrctl estatus, at - movia ea, 1f - eret - - /* Load address for callr, then end TB. */ -1: movia at, 3f - br 2f - - /* Test case! TCG abort on indirect lowering across brcond. */ -2: callr at - - /* exit(0) */ -3: movi r4, HOSTED_EXIT - movi r5, 0 - semihosting_call - - .size main, . - main diff --git a/tests/tcg/plugins/bb.c b/tests/tcg/plugins/bb.c new file mode 100644 index 0000000000..36776dee1e --- /dev/null +++ b/tests/tcg/plugins/bb.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef struct { + uint64_t bb_count; + uint64_t insn_count; +} CPUCount; + +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 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", + cpu_index, + count->bb_count, count->insn_count); + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_autoptr(GString) report = g_string_new(""); + + 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 = qemu_plugin_scoreboard_find(counts, cpu_index); + g_autoptr(GString) report = g_string_new(""); + gen_one_cpu_report(count, report, cpu_index); + + if (report->len > 0) { + g_string_prepend(report, "Idling "); + qemu_plugin_outs(report->str); + } +} + +static void vcpu_tb_exec(unsigned int cpu_index, void *udata) +{ + CPUCount *count = qemu_plugin_scoreboard_find(counts, cpu_index); + + uintptr_t n_insns = (uintptr_t)udata; + count->insn_count += n_insns; + count->bb_count++; +} + +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_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, + (void *)n_insns); + } +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + int i; + + for (i = 0; i < argc; i++) { + char *opt = argv[i]; + 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); + return -1; + } + } else if (g_strcmp0(tokens[0], "idle") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &idle_report)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + 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); + } + + 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/empty.c b/tests/tcg/plugins/empty.c similarity index 100% rename from tests/plugin/empty.c rename to tests/tcg/plugins/empty.c diff --git a/tests/tcg/plugins/inline.c b/tests/tcg/plugins/inline.c new file mode 100644 index 0000000000..73dde99578 --- /dev/null +++ b/tests/tcg/plugins/inline.c @@ -0,0 +1,310 @@ +/* + * 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; + uint64_t tb_cond_num_trigger; + uint64_t tb_cond_track_count; + uint64_t insn_cond_num_trigger; + uint64_t insn_cond_track_count; +} CPUCount; + +static const uint64_t cond_trigger_limit = 100; + +typedef struct { + uint64_t data_insn; + uint64_t data_tb; + uint64_t data_mem; +} CPUData; + +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 qemu_plugin_u64 tb_cond_num_trigger; +static qemu_plugin_u64 tb_cond_track_count; +static qemu_plugin_u64 insn_cond_num_trigger; +static qemu_plugin_u64 insn_cond_track_count; +static struct qemu_plugin_scoreboard *data; +static qemu_plugin_u64 data_insn; +static qemu_plugin_u64 data_tb; +static qemu_plugin_u64 data_mem; + +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); + const uint64_t cond_num_trigger = + qemu_plugin_u64_sum(insn_cond_num_trigger); + const uint64_t cond_track_left = qemu_plugin_u64_sum(insn_cond_track_count); + const uint64_t conditional = + cond_num_trigger * cond_trigger_limit + cond_track_left; + g_autoptr(GString) stats = g_string_new(""); + g_string_append_printf(stats, "insn: %" PRIu64 "\n", expected); + g_string_append_printf(stats, "insn: %" PRIu64 " (per vcpu)\n", per_vcpu); + g_string_append_printf(stats, "insn: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_string_append_printf(stats, "insn: %" PRIu64 " (cond cb)\n", conditional); + qemu_plugin_outs(stats->str); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); + g_assert(conditional == 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); + const uint64_t cond_num_trigger = qemu_plugin_u64_sum(tb_cond_num_trigger); + const uint64_t cond_track_left = qemu_plugin_u64_sum(tb_cond_track_count); + const uint64_t conditional = + cond_num_trigger * cond_trigger_limit + cond_track_left; + g_autoptr(GString) stats = g_string_new(""); + g_string_append_printf(stats, "tb: %" PRIu64 "\n", expected); + g_string_append_printf(stats, "tb: %" PRIu64 " (per vcpu)\n", per_vcpu); + g_string_append_printf(stats, "tb: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + g_string_append_printf(stats, "tb: %" PRIu64 " (conditional cb)\n", conditional); + qemu_plugin_outs(stats->str); + g_assert(expected > 0); + g_assert(per_vcpu == expected); + g_assert(inl_per_vcpu == expected); + g_assert(conditional == 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); + g_autoptr(GString) stats = g_string_new(""); + g_string_append_printf(stats, "mem: %" PRIu64 "\n", expected); + g_string_append_printf(stats, "mem: %" PRIu64 " (per vcpu)\n", per_vcpu); + g_string_append_printf(stats, "mem: %" PRIu64 " (per vcpu inline)\n", inl_per_vcpu); + qemu_plugin_outs(stats->str); + 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_autoptr(GString) stats = g_string_new(""); + 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); + const uint64_t tb_cond_trigger = + qemu_plugin_u64_get(tb_cond_num_trigger, i); + const uint64_t tb_cond_left = + qemu_plugin_u64_get(tb_cond_track_count, i); + const uint64_t insn_cond_trigger = + qemu_plugin_u64_get(insn_cond_num_trigger, i); + const uint64_t insn_cond_left = + qemu_plugin_u64_get(insn_cond_track_count, i); + g_string_printf(stats, "cpu %d: tb (%" PRIu64 ", %" PRIu64 + ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 + ") | " + "insn (%" PRIu64 ", %" PRIu64 + ", %" PRIu64 " * %" PRIu64 " + %" PRIu64 + ") | " + "mem (%" PRIu64 ", %" PRIu64 ")" + "\n", + i, + tb, tb_inline, + tb_cond_trigger, cond_trigger_limit, tb_cond_left, + insn, insn_inline, + insn_cond_trigger, cond_trigger_limit, insn_cond_left, + mem, mem_inline); + qemu_plugin_outs(stats->str); + g_assert(tb == tb_inline); + g_assert(insn == insn_inline); + g_assert(mem == mem_inline); + g_assert(tb_cond_trigger == tb / cond_trigger_limit); + g_assert(tb_cond_left == tb % cond_trigger_limit); + g_assert(insn_cond_trigger == insn / cond_trigger_limit); + g_assert(insn_cond_left == insn % cond_trigger_limit); + } + + stats_tb(); + stats_insn(); + stats_mem(); + + qemu_plugin_scoreboard_free(counts); + qemu_plugin_scoreboard_free(data); +} + +static void vcpu_tb_exec(unsigned int cpu_index, void *udata) +{ + qemu_plugin_u64_add(count_tb, cpu_index, 1); + g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata); + 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_tb_cond_exec(unsigned int cpu_index, void *udata) +{ + g_assert(qemu_plugin_u64_get(tb_cond_track_count, cpu_index) == + cond_trigger_limit); + g_assert(qemu_plugin_u64_get(data_tb, cpu_index) == (uintptr_t) udata); + qemu_plugin_u64_set(tb_cond_track_count, cpu_index, 0); + qemu_plugin_u64_add(tb_cond_num_trigger, cpu_index, 1); +} + +static void vcpu_insn_cond_exec(unsigned int cpu_index, void *udata) +{ + g_assert(qemu_plugin_u64_get(insn_cond_track_count, cpu_index) == + cond_trigger_limit); + g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata); + qemu_plugin_u64_set(insn_cond_track_count, cpu_index, 0); + qemu_plugin_u64_add(insn_cond_num_trigger, cpu_index, 1); +} + +static void vcpu_insn_exec(unsigned int cpu_index, void *udata) +{ + qemu_plugin_u64_add(count_insn, cpu_index, 1); + g_assert(qemu_plugin_u64_get(data_insn, cpu_index) == (uintptr_t) udata); + 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 *udata) +{ + qemu_plugin_u64_add(count_mem, cpu_index, 1); + g_assert(qemu_plugin_u64_get(data_mem, cpu_index) == (uintptr_t) udata); + 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) +{ + void *tb_store = tb; + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_STORE_U64, data_tb, (uintptr_t) tb_store); + qemu_plugin_register_vcpu_tb_exec_cb( + tb, vcpu_tb_exec, QEMU_PLUGIN_CB_NO_REGS, tb_store); + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, count_tb_inline, 1); + + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, tb_cond_track_count, 1); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, vcpu_tb_cond_exec, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, tb_cond_track_count, cond_trigger_limit, tb_store); + + for (int idx = 0; idx < qemu_plugin_tb_n_insns(tb); ++idx) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, idx); + void *insn_store = insn; + void *mem_store = (char *)insn_store + 0xff; + + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_STORE_U64, data_insn, + (uintptr_t) insn_store); + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_exec, QEMU_PLUGIN_CB_NO_REGS, insn_store); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, count_insn_inline, 1); + + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( + insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_cond_track_count, 1); + qemu_plugin_register_vcpu_insn_exec_cond_cb( + insn, vcpu_insn_cond_exec, QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_COND_EQ, insn_cond_track_count, cond_trigger_limit, + insn_store); + + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, QEMU_PLUGIN_MEM_RW, + QEMU_PLUGIN_INLINE_STORE_U64, + data_mem, (uintptr_t) mem_store); + qemu_plugin_register_vcpu_mem_cb(insn, &vcpu_mem_access, + QEMU_PLUGIN_CB_NO_REGS, + QEMU_PLUGIN_MEM_RW, mem_store); + 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); + tb_cond_num_trigger = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, tb_cond_num_trigger); + tb_cond_track_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, tb_cond_track_count); + insn_cond_num_trigger = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, insn_cond_num_trigger); + insn_cond_track_count = qemu_plugin_scoreboard_u64_in_struct( + counts, CPUCount, insn_cond_track_count); + data = qemu_plugin_scoreboard_new(sizeof(CPUData)); + data_insn = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_insn); + data_tb = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_tb); + data_mem = qemu_plugin_scoreboard_u64_in_struct(data, CPUData, data_mem); + + 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/tcg/plugins/insn.c b/tests/tcg/plugins/insn.c new file mode 100644 index 0000000000..baf2d07205 --- /dev/null +++ b/tests/tcg/plugins/insn.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static qemu_plugin_u64 insn_count; + +static bool do_inline; +static bool do_size; +static bool do_trace; +static GArray *sizes; + +typedef struct { + uint64_t hits; + uint64_t last_hit; + uint64_t total_delta; +} MatchCount; + +typedef struct { + char *match_string; + struct qemu_plugin_scoreboard *counts; /* MatchCount */ +} Match; + +static GArray *matches; + +typedef struct { + Match *match; + uint64_t vaddr; + uint64_t hits; + char *disas; +} Instruction; + +/* A hash table to hold matched instructions */ +static GHashTable *match_insn_records; +static GMutex match_hash_lock; + + +static Instruction * get_insn_record(const char *disas, uint64_t vaddr, Match *m) +{ + g_autofree char *str_hash = g_strdup_printf("%"PRIx64" %s", vaddr, disas); + Instruction *record; + + g_mutex_lock(&match_hash_lock); + + if (!match_insn_records) { + match_insn_records = g_hash_table_new(g_str_hash, g_str_equal); + } + + record = g_hash_table_lookup(match_insn_records, str_hash); + + if (!record) { + g_autoptr(GString) ts = g_string_new(str_hash); + + record = g_new0(Instruction, 1); + record->disas = g_strdup(disas); + record->vaddr = vaddr; + record->match = m; + + g_hash_table_insert(match_insn_records, str_hash, record); + + g_string_prepend(ts, "Created record for: "); + g_string_append(ts, "\n"); + qemu_plugin_outs(ts->str); + } + + g_mutex_unlock(&match_hash_lock); + + return record; +} + +/* + * 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) +{ + qemu_plugin_u64_add(insn_count, cpu_index, 1); +} + +static void vcpu_insn_matched_exec_before(unsigned int cpu_index, void *udata) +{ + Instruction *insn = (Instruction *) udata; + Match *insn_match = insn->match; + MatchCount *match = qemu_plugin_scoreboard_find(insn_match->counts, + cpu_index); + + insn->hits++; + + uint64_t icount = qemu_plugin_u64_get(insn_count, cpu_index); + uint64_t delta = icount - match->last_hit; + + match->hits++; + match->total_delta += delta; + match->last_hit = icount; + + if (do_trace) { + g_autoptr(GString) ts = g_string_new(""); + g_string_append_printf(ts, "0x%" PRIx64 ", '%s', %"PRId64 " hits", + insn->vaddr, insn->disas, insn->hits); + g_string_append_printf(ts, + " , cpu %u," + " %"PRId64" match hits," + " Δ+%"PRId64 " since last match," + " %"PRId64 " avg insns/match\n", + cpu_index, + match->hits, delta, + match->total_delta / match->hits); + + qemu_plugin_outs(ts->str); + } +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n = qemu_plugin_tb_n_insns(tb); + size_t i; + + for (i = 0; i < n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + + if (do_inline) { + 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( + insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, + GUINT_TO_POINTER(vaddr)); + } + + if (do_size) { + size_t sz = qemu_plugin_insn_size(insn); + if (sz > sizes->len) { + g_array_set_size(sizes, sz); + } + unsigned long *cnt = &g_array_index(sizes, unsigned long, sz); + (*cnt)++; + } + + /* + * If we are tracking certain instructions we will need more + * information about the instruction which we also need to + * save if there is a hit. + * + * We only want one record for each occurrence of the matched + * instruction. + */ + if (matches->len) { + char *insn_disas = qemu_plugin_insn_disas(insn); + 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 = get_insn_record(insn_disas, + qemu_plugin_insn_vaddr(insn), + m); + + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_matched_exec_before, + QEMU_PLUGIN_CB_NO_REGS, rec); + } + } + g_free(insn_disas); + } + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_autoptr(GString) out = g_string_new(NULL); + int i; + + if (do_size) { + for (i = 0; i <= sizes->len; i++) { + unsigned long *cnt = &g_array_index(sizes, unsigned long, i); + if (*cnt) { + g_string_append_printf(out, + "len %d bytes: %ld insns\n", i, *cnt); + } + } + } else { + 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", + qemu_plugin_u64_sum(insn_count)); + } + qemu_plugin_outs(out->str); + qemu_plugin_scoreboard_free(insn_count.score); + + g_mutex_lock(&match_hash_lock); + + for (i = 0; i < matches->len; ++i) { + Match *m = &g_array_index(matches, Match, i); + GHashTableIter iter; + Instruction *record; + qemu_plugin_u64 hit_e = qemu_plugin_scoreboard_u64_in_struct(m->counts, MatchCount, hits); + uint64_t hits = qemu_plugin_u64_sum(hit_e); + + g_string_printf(out, "Match: %s, hits %"PRId64"\n", m->match_string, hits); + qemu_plugin_outs(out->str); + + g_hash_table_iter_init(&iter, match_insn_records); + while (g_hash_table_iter_next(&iter, NULL, (void **)&record)) { + if (record->match == m) { + g_string_printf(out, + " %"PRIx64": %s (hits %"PRId64")\n", + record->vaddr, + record->disas, + record->hits); + qemu_plugin_outs(out->str); + } + } + + g_free(m->match_string); + qemu_plugin_scoreboard_free(m->counts); + } + + g_mutex_unlock(&match_hash_lock); + + 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 = g_strdup(match), + .counts = qemu_plugin_scoreboard_new(sizeof(MatchCount)) }; + g_array_append_val(matches, new_match); +} + +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_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); + return -1; + } + } else if (g_strcmp0(tokens[0], "sizes") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_size)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "match") == 0) { + parse_match(tokens[1]); + } else if (g_strcmp0(tokens[0], "trace") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_trace)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + 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/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c new file mode 100644 index 0000000000..b0fa8a9f27 --- /dev/null +++ b/tests/tcg/plugins/mem.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include +#include + +/* + * plugins should not include anything from QEMU aside from the + * API header. However as this is a test plugin to exercise the + * internals of QEMU and we want to avoid needless code duplication we + * do so here. bswap.h is pretty self-contained although it needs a + * few things provided by compiler.h. + */ +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef struct { + uint64_t mem_count; + uint64_t io_count; +} CPUCount; + +typedef struct { + uint64_t vaddr; + const char *sym; +} InsnInfo; + +/* + * For the "memory" system test we need to track accesses to + * individual regions. We mirror the data written to the region and + * then check when it is read that it matches up. + * + * We do this as regions rather than pages to save on complications + * with page crossing and the fact the test only cares about the + * test_data region. + */ +static uint64_t region_size = 4096 * 4; +static uint64_t region_mask; + +typedef struct { + uint64_t region_address; + uint64_t reads; + uint64_t writes; + uint8_t *data; + /* Did we see every write and read with correct values? */ + bool seen_all; +} RegionInfo; + +static struct qemu_plugin_scoreboard *counts; +static qemu_plugin_u64 mem_count; +static qemu_plugin_u64 io_count; +static bool do_inline, do_callback, do_print_accesses, do_region_summary; +static bool do_haddr; +static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; + + +static GMutex lock; +static GHashTable *regions; + +static gint addr_order(gconstpointer a, gconstpointer b) +{ + RegionInfo *na = (RegionInfo *) a; + RegionInfo *nb = (RegionInfo *) b; + + return na->region_address > nb->region_address ? 1 : -1; +} + + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + g_autoptr(GString) out = g_string_new(""); + + 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", + qemu_plugin_u64_sum(io_count)); + } + qemu_plugin_outs(out->str); + + + if (do_region_summary) { + GList *counts = g_hash_table_get_values(regions); + + counts = g_list_sort(counts, addr_order); + + g_string_printf(out, "Region Base, Reads, Writes, Seen all\n"); + + if (counts && g_list_next(counts)) { + for (/* counts */; counts; counts = counts->next) { + RegionInfo *ri = (RegionInfo *) counts->data; + + g_string_append_printf(out, + "0x%016"PRIx64", " + "%"PRId64", %"PRId64", %s\n", + ri->region_address, + ri->reads, + ri->writes, + ri->seen_all ? "true" : "false"); + } + } + qemu_plugin_outs(out->str); + } + + qemu_plugin_scoreboard_free(counts); +} + +/* + * Update the region tracking info for the access. We split up accesses + * that span regions even though the plugin infrastructure will deliver + * it as a single access. + */ +static void update_region_info(uint64_t region, uint64_t offset, + qemu_plugin_meminfo_t meminfo, + qemu_plugin_mem_value value, + unsigned size) +{ + bool be = qemu_plugin_mem_is_big_endian(meminfo); + bool is_store = qemu_plugin_mem_is_store(meminfo); + RegionInfo *ri; + bool unseen_data = false; + + g_assert(offset + size <= region_size); + + g_mutex_lock(&lock); + ri = (RegionInfo *) g_hash_table_lookup(regions, GUINT_TO_POINTER(region)); + + if (!ri) { + ri = g_new0(RegionInfo, 1); + ri->region_address = region; + ri->data = g_malloc0(region_size); + ri->seen_all = true; + g_hash_table_insert(regions, GUINT_TO_POINTER(region), (gpointer) ri); + } + + if (is_store) { + ri->writes++; + } else { + ri->reads++; + } + + switch (value.type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + if (is_store) { + ri->data[offset] = value.data.u8; + } else if (ri->data[offset] != value.data.u8) { + unseen_data = true; + } + break; + case QEMU_PLUGIN_MEM_VALUE_U16: + { + uint16_t *p = (uint16_t *) &ri->data[offset]; + if (is_store) { + if (be) { + stw_be_p(p, value.data.u16); + } else { + stw_le_p(p, value.data.u16); + } + } else { + uint16_t val = be ? lduw_be_p(p) : lduw_le_p(p); + unseen_data = val != value.data.u16; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U32: + { + uint32_t *p = (uint32_t *) &ri->data[offset]; + if (is_store) { + if (be) { + stl_be_p(p, value.data.u32); + } else { + stl_le_p(p, value.data.u32); + } + } else { + uint32_t val = be ? ldl_be_p(p) : ldl_le_p(p); + unseen_data = val != value.data.u32; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U64: + { + uint64_t *p = (uint64_t *) &ri->data[offset]; + if (is_store) { + if (be) { + stq_be_p(p, value.data.u64); + } else { + stq_le_p(p, value.data.u64); + } + } else { + uint64_t val = be ? ldq_be_p(p) : ldq_le_p(p); + unseen_data = val != value.data.u64; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U128: + /* non in test so skip */ + break; + default: + g_assert_not_reached(); + } + + /* + * This is expected for regions initialised by QEMU (.text etc) but we + * expect to see all data read and written to the test_data region + * of the memory test. + */ + if (unseen_data && ri->seen_all) { + g_autoptr(GString) error = g_string_new("Warning: "); + g_string_append_printf(error, "0x%016"PRIx64":%"PRId64 + " read an un-instrumented value\n", + region, offset); + qemu_plugin_outs(error->str); + ri->seen_all = false; + } + + g_mutex_unlock(&lock); +} + +static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, + uint64_t vaddr, void *udata) +{ + if (do_haddr) { + struct qemu_plugin_hwaddr *hwaddr; + hwaddr = qemu_plugin_get_hwaddr(meminfo, vaddr); + if (qemu_plugin_hwaddr_is_io(hwaddr)) { + qemu_plugin_u64_add(io_count, cpu_index, 1); + } else { + qemu_plugin_u64_add(mem_count, cpu_index, 1); + } + } else { + qemu_plugin_u64_add(mem_count, cpu_index, 1); + } + + if (do_region_summary) { + uint64_t region = vaddr & ~region_mask; + uint64_t offset = vaddr & region_mask; + qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo); + unsigned size = 1 << qemu_plugin_mem_size_shift(meminfo); + + update_region_info(region, offset, meminfo, value, size); + } +} + +static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, + uint64_t vaddr, void *udata) +{ + InsnInfo *insn_info = udata; + unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo); + const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load"; + qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo); + uint64_t hwaddr = + qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr)); + g_autoptr(GString) out = g_string_new(""); + g_string_printf(out, + "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,", + insn_info->vaddr, insn_info->sym, + vaddr, hwaddr, size, type); + switch (value.type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + g_string_append_printf(out, "0x%02"PRIx8, value.data.u8); + break; + case QEMU_PLUGIN_MEM_VALUE_U16: + g_string_append_printf(out, "0x%04"PRIx16, value.data.u16); + break; + case QEMU_PLUGIN_MEM_VALUE_U32: + g_string_append_printf(out, "0x%08"PRIx32, value.data.u32); + break; + case QEMU_PLUGIN_MEM_VALUE_U64: + g_string_append_printf(out, "0x%016"PRIx64, value.data.u64); + break; + case QEMU_PLUGIN_MEM_VALUE_U128: + g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64, + value.data.u128.high, value.data.u128.low); + break; + default: + g_assert_not_reached(); + } + g_string_append_printf(out, "\n"); + qemu_plugin_outs(out->str); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n = qemu_plugin_tb_n_insns(tb); + size_t i; + + for (i = 0; i < n; i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + + if (do_inline) { + qemu_plugin_register_vcpu_mem_inline_per_vcpu( + insn, rw, + QEMU_PLUGIN_INLINE_ADD_U64, + mem_count, 1); + } + if (do_callback || do_region_summary) { + qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, + QEMU_PLUGIN_CB_NO_REGS, + rw, NULL); + } + if (do_print_accesses) { + /* we leak this pointer, to avoid locking to keep track of it */ + InsnInfo *insn_info = g_malloc(sizeof(InsnInfo)); + const char *sym = qemu_plugin_insn_symbol(insn); + insn_info->sym = sym ? sym : ""; + insn_info->vaddr = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_mem_cb(insn, print_access, + QEMU_PLUGIN_CB_NO_REGS, + rw, (void *) insn_info); + } + } +} + +QEMU_PLUGIN_EXPORT 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++) { + char *opt = argv[i]; + 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)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "track") == 0) { + if (g_strcmp0(tokens[1], "r") == 0) { + rw = QEMU_PLUGIN_MEM_R; + } else if (g_strcmp0(tokens[1], "w") == 0) { + rw = QEMU_PLUGIN_MEM_W; + } else if (g_strcmp0(tokens[1], "rw") == 0) { + rw = QEMU_PLUGIN_MEM_RW; + } else { + fprintf(stderr, "invalid value for argument track: %s\n", opt); + return -1; + } + } else 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); + return -1; + } + } else if (g_strcmp0(tokens[0], "callback") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "print-accesses") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &do_print_accesses)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "region-summary") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &do_region_summary)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + if (do_inline && do_callback) { + fprintf(stderr, + "can't enable inline and callback counting at the same time\n"); + return -1; + } + + if (do_print_accesses) { + g_autoptr(GString) out = g_string_new(""); + g_string_printf(out, + "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr," + "access_size,access_type,mem_value\n"); + qemu_plugin_outs(out->str); + } + + if (do_region_summary) { + region_mask = (region_size - 1); + regions = g_hash_table_new(NULL, g_direct_equal); + } + + 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/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build new file mode 100644 index 0000000000..f847849b1b --- /dev/null +++ b/tests/tcg/plugins/meson.build @@ -0,0 +1,22 @@ +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/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c new file mode 100644 index 0000000000..ff452178b1 --- /dev/null +++ b/tests/tcg/plugins/syscall.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2020, Matthias Weckbecker + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef struct { + int64_t num; + int64_t calls; + int64_t errors; +} SyscallStats; + +struct SyscallInfo { + const char *name; + int64_t write_sysno; +}; + +static const struct SyscallInfo arch_syscall_info[] = { + { "aarch64", 64 }, + { "aarch64_be", 64 }, + { "alpha", 4 }, + { "arm", 4 }, + { "armeb", 4 }, + { "avr", -1 }, + { "hexagon", 64 }, + { "hppa", -1 }, + { "i386", 4 }, + { "loongarch64", -1 }, + { "m68k", 4 }, + { "microblaze", 4 }, + { "microblazeel", 4 }, + { "mips", 1 }, + { "mips64", 1 }, + { "mips64el", 1 }, + { "mipsel", 1 }, + { "mipsn32", 1 }, + { "mipsn32el", 1 }, + { "or1k", -1 }, + { "ppc", 4 }, + { "ppc64", 4 }, + { "ppc64le", 4 }, + { "riscv32", 64 }, + { "riscv64", 64 }, + { "rx", -1 }, + { "s390x", -1 }, + { "sh4", -1 }, + { "sh4eb", -1 }, + { "sparc", 4 }, + { "sparc32plus", 4 }, + { "sparc64", 4 }, + { "tricore", -1 }, + { "x86_64", 1 }, + { "xtensa", 13 }, + { "xtensaeb", 13 }, + { NULL, -1 }, +}; + +static GMutex lock; +static GHashTable *statistics; +static GByteArray *memory_buffer; +static bool do_log_writes; +static int64_t write_sysno = -1; + +static SyscallStats *get_or_create_entry(int64_t num) +{ + SyscallStats *entry = + (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num)); + + if (!entry) { + entry = g_new0(SyscallStats, 1); + entry->num = num; + g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry); + } + + return entry; +} + +/* + * Hex-dump a GByteArray to the QEMU plugin output in the format: + * 61 63 63 65 6c 09 09 20 20 20 66 70 75 09 09 09 | accel.....fpu... + * 20 6d 6f 64 75 6c 65 2d 63 6f 6d 6d 6f 6e 2e 63 | .module-common.c + */ +static void hexdump(const GByteArray *data) +{ + g_autoptr(GString) out = g_string_new(""); + + for (guint index = 0; index < data->len; index += 16) { + for (guint col = 0; col < 16; col++) { + if (index + col < data->len) { + g_string_append_printf(out, "%02x ", data->data[index + col]); + } else { + g_string_append(out, " "); + } + } + + g_string_append(out, " | "); + + for (guint col = 0; col < 16; col++) { + if (index + col >= data->len) { + break; + } + + if (g_ascii_isgraph(data->data[index + col])) { + g_string_append_printf(out, "%c", data->data[index + col]); + } else { + g_string_append(out, "."); + } + } + + g_string_append(out, "\n"); + } + + qemu_plugin_outs(out->str); +} + +static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, + int64_t num, uint64_t a1, uint64_t a2, + uint64_t a3, uint64_t a4, uint64_t a5, + uint64_t a6, uint64_t a7, uint64_t a8) +{ + if (statistics) { + SyscallStats *entry; + g_mutex_lock(&lock); + entry = get_or_create_entry(num); + entry->calls++; + g_mutex_unlock(&lock); + } else { + g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num); + qemu_plugin_outs(out); + } + + if (do_log_writes && num == write_sysno) { + if (qemu_plugin_read_memory_vaddr(a2, memory_buffer, a3)) { + hexdump(memory_buffer); + } else { + fprintf(stderr, "Error reading memory from vaddr %"PRIu64"\n", a2); + } + } +} + +static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx, + int64_t num, int64_t ret) +{ + if (statistics) { + SyscallStats *entry; + + g_mutex_lock(&lock); + /* Should always return an existent entry. */ + entry = get_or_create_entry(num); + if (ret < 0) { + entry->errors++; + } + g_mutex_unlock(&lock); + } else { + g_autofree gchar *out = g_strdup_printf( + "syscall #%" PRIi64 " returned -> %" PRIi64 "\n", num, ret); + qemu_plugin_outs(out); + } +} + +static void print_entry(gpointer val, gpointer user_data) +{ + SyscallStats *entry = (SyscallStats *) val; + int64_t syscall_num = entry->num; + g_autofree gchar *out = g_strdup_printf( + "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n", + syscall_num, entry->calls, entry->errors); + qemu_plugin_outs(out); +} + +static gint comp_func(gconstpointer ea, gconstpointer eb) +{ + SyscallStats *ent_a = (SyscallStats *) ea; + SyscallStats *ent_b = (SyscallStats *) eb; + + return ent_a->calls > ent_b->calls ? -1 : 1; +} + +/* ************************************************************************* */ +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + if (!statistics) { + return; + } + + g_mutex_lock(&lock); + GList *entries = g_hash_table_get_values(statistics); + entries = g_list_sort(entries, comp_func); + qemu_plugin_outs("syscall no. calls errors\n"); + + g_list_foreach(entries, print_entry, NULL); + + g_list_free(entries); + g_hash_table_destroy(statistics); + g_mutex_unlock(&lock); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + bool do_print = false; + + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + 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)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + } + } else if (g_strcmp0(tokens[0], "log_writes") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_log_writes)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + } + } else { + fprintf(stderr, "unsupported argument: %s\n", argv[i]); + return -1; + } + } + + if (!do_print) { + statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); + } + + if (do_log_writes) { + for (const struct SyscallInfo *syscall_info = arch_syscall_info; + syscall_info->name != NULL; syscall_info++) { + + if (g_strcmp0(syscall_info->name, info->target_name) == 0) { + write_sysno = syscall_info->write_sysno; + break; + } + } + + if (write_sysno == -1) { + fprintf(stderr, "write syscall number not found\n"); + return -1; + } + + memory_buffer = g_byte_array_new(); + } + + qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); + qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + return 0; +} 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 f081f1c683..0d058b2600 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -6,34 +6,58 @@ VPATH += $(SRC_PATH)/tests/tcg/ppc64 config-cc.mak: Makefile $(quiet-@)( \ - $(call cc-option,-mpower8-vector, CROSS_CC_HAS_POWER8_VECTOR); \ + $(call cc-option,-mcpu=power8, CROSS_CC_HAS_CPU_POWER8); \ $(call cc-option,-mpower10, CROSS_CC_HAS_POWER10)) 3> config-cc.mak -include config-cc.mak -ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) +# multi-threaded tests are known to fail (e.g., clang-user CI job) +# See: https://gitlab.com/qemu-project/qemu/-/issues/2456 +run-signals: signals + $(call skip-test, $<, "BROKEN (flaky with clang) ") +run-plugin-signals-with-%: + $(call skip-test, $<, "BROKEN (flaky with clang) ") + +run-threadcount: threadcount + $(call skip-test, $<, "BROKEN (flaky with clang) ") +run-plugin-threadcount-with-%: + $(call skip-test, $<, "BROKEN (flaky with clang) ") + +ifneq ($(CROSS_CC_HAS_CPU_POWER8),) PPC64_TESTS=bcdsub non_signalling_xscv endif -$(PPC64_TESTS): CFLAGS += -mpower8-vector +$(PPC64_TESTS): CFLAGS += -mcpu=power8 + +ifneq ($(CROSS_CC_HAS_CPU_POWER8),) +PPC64_TESTS += vsx_f2i_nan +endif +vsx_f2i_nan: CFLAGS += -mcpu=power8 -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 + +# ppc64 ABI uses function descriptors, and thus, QEMU can't find symbol for a +# given instruction. Thus, we don't check output of mem-access plugin. +run-plugin-test-plugin-mem-access-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND= TESTS += $(PPC64_TESTS) 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/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index e22cdb34c5..7c1d44d3f4 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -10,7 +10,7 @@ LDFLAGS = -T $(LINK_SCRIPT) CFLAGS += -g -Og %.o: %.S - $(CC) $(CFLAGS) $< -c -o $@ + $(CC) $(CFLAGS) $< -Wa,--noexecstack -c -o $@ %: %.o $(LINK_SCRIPT) $(LD) $(LDFLAGS) $< -o $@ @@ -19,3 +19,6 @@ 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 cc3ed65ffd..4da5b9a3b3 100644 --- a/tests/tcg/riscv64/Makefile.target +++ b/tests/tcg/riscv64/Makefile.target @@ -9,4 +9,12 @@ TESTS += noexec TESTS += test-noc test-noc: LDFLAGS = -nostdlib -static run-test-noc: QEMU_OPTS += -cpu rv64,c=false -run-plugin-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/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/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/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index a34fa68473..969bc5728f 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -1,9 +1,54 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) -QEMU_OPTS=-action panic=exit-failure -kernel +# EXTFLAGS can be passed by the user, e.g. to override the --accel +QEMU_OPTS+=-action panic=exit-failure -nographic -serial chardev:output $(EXTFLAGS) -kernel +LINK_SCRIPT=$(S390X_SRC)/softmmu.ld +CFLAGS+=-ggdb -O0 -I$(SRC_PATH)/include/hw/s390x/ipl/ +LDFLAGS=-nostdlib -static -%: %.S - $(CC) -march=z13 -m64 -nostartfiles -static -Wl,-Ttext=0 \ - -Wl,--build-id=none $< -o $@ +%.o: %.S + $(CC) -march=z13 -m64 -Wa,--noexecstack -c $< -o $@ -TESTS += unaligned-lowcore +%.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 \ + per \ + 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 + +# s390x clears the BSS section so we need to account for that +run-plugin-memory-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py \ + --bss-cleared $@.out diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index cb90d4183d..da5fe71a40 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -2,6 +2,9 @@ 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); \ @@ -24,16 +27,58 @@ 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 +TESTS+=ex-smc + +cdsg: CFLAGS+=-pthread +cdsg: LDFLAGS+=-pthread + +rxsbg: CFLAGS+=-O2 + +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 +Z14_TESTS=fma vfminmax +fma: float.h +fma: LDFLAGS+=-lm vfminmax: LDFLAGS+=-lm +vfminmax: float.h $(Z14_TESTS): CFLAGS+=-march=z14 -O2 TESTS+=$(Z14_TESTS) endif @@ -42,16 +87,17 @@ 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 ($(HAVE_GDB_BIN),) +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) @@ -60,7 +106,7 @@ hello-s390x-asm: CFLAGS+=-nostdlib run-gdbstub-svc: hello-s390x-asm $(call run-test, $@, $(GDB_SCRIPT) \ - --gdb $(HAVE_GDB_BIN) \ + --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ --bin $< --test $(S390X_SRC)/gdbstub/test-svc.py, \ single-stepping svc) 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..6c26f04949 --- /dev/null +++ b/tests/tcg/s390x/console.c @@ -0,0 +1,15 @@ +/* + * 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" +#include "../../../roms/SLOF/lib/libc/string/memset.c" +#include "../../../roms/SLOF/lib/libc/string/memcpy.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/ex-smc.c b/tests/tcg/s390x/ex-smc.c new file mode 100644 index 0000000000..f403640d6b --- /dev/null +++ b/tests/tcg/s390x/ex-smc.c @@ -0,0 +1,57 @@ +/* + * Test modifying an EXECUTE target. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include + +/* Make sure we exercise the same EXECUTE instruction. */ +extern void execute(unsigned char *insn, unsigned char mask, + unsigned long *r1_r5); +asm(".globl execute\n" + "execute:\n" + "lg %r1,0(%r4)\n" + "lg %r5,8(%r4)\n" + "ex %r3,0(%r2)\n" + "stg %r5,8(%r4)\n" + "stg %r1,0(%r4)\n" + "br %r14\n"); + +/* Define an RWX EXECUTE target. */ +extern unsigned char lgfi[]; +asm(".pushsection .rwx,\"awx\",@progbits\n" + ".globl lgfi\n" + "lgfi: lgfi %r0,0\n" + ".popsection\n"); + +int main(void) +{ + unsigned long r1_r5[2]; + + /* Create an initial TB. */ + r1_r5[0] = -1; + r1_r5[1] = -1; + execute(lgfi, 1 << 4, r1_r5); + assert(r1_r5[0] == 0); + assert(r1_r5[1] == -1); + + /* Test changing the mask. */ + execute(lgfi, 5 << 4, r1_r5); + assert(r1_r5[0] == 0); + assert(r1_r5[1] == 0); + + /* Test changing the target. */ + lgfi[5] = 42; + execute(lgfi, 5 << 4, r1_r5); + assert(r1_r5[0] == 0); + assert(r1_r5[1] == 42); + + /* Test changing both the mask and the target. */ + lgfi[5] = 24; + execute(lgfi, 1 << 4, r1_r5); + assert(r1_r5[0] == 24); + assert(r1_r5[1] == 42); + + return EXIT_SUCCESS; +} 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/float.h b/tests/tcg/s390x/float.h new file mode 100644 index 0000000000..9d1682b8fc --- /dev/null +++ b/tests/tcg/s390x/float.h @@ -0,0 +1,104 @@ +/* + * Helpers for floating-point tests. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef FLOAT_H +#define FLOAT_H + +/* + * Floating-point value classes. + */ +#define N_FORMATS 3 +#define CLASS_MINUS_INF 0 +#define CLASS_MINUS_FN 1 +#define CLASS_MINUS_ZERO 2 +#define CLASS_PLUS_ZERO 3 +#define CLASS_PLUS_FN 4 +#define CLASS_PLUS_INF 5 +#define CLASS_QNAN 6 +#define CLASS_SNAN 7 +#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, +}; +struct float_class { + size_t n; + unsigned char v[2][16]; +}; +static const struct float_class signed_floats[N_FORMATS][N_SIGNED_CLASSES] = { + /* M4 == 2: short */ + { + /* -inf */ {1, {{0xff, 0x80, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc2, 0x28, 0x00, 0x00}, + {0xc2, 0x29, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x42, 0x28, 0x00, 0x00}, + {0x42, 0x2a, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0x80, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xbf, 0xff, 0xff}, + {0x7f, 0xbf, 0xff, 0xfd}}}, + }, + + /* M4 == 3: long */ + { + /* -inf */ {1, {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}}, + }, + + /* M4 == 4: extended */ + { + /* -inf */ {1, {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -Fn */ {2, {{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 */ {1, {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{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 */ {1, {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* QNaN */ {2, {{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 */ {2, {{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}}}, + }, +}; +static const unsigned char default_nans[N_FORMATS][16] = { + /* M4 == 2: short */ {0x7f, 0xc0, 0x00, 0x00}, + /* M4 == 3: long */ {0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + /* M4 == 4: extended */ {0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +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 void snan_to_qnan(char *v, int fmt) +{ + size_t bit = 1 + e_bits[fmt]; + v[bit / 8] |= 1 << (7 - (bit % 8)); +} + +#endif diff --git a/tests/tcg/s390x/fma.c b/tests/tcg/s390x/fma.c new file mode 100644 index 0000000000..6872f59a7a --- /dev/null +++ b/tests/tcg/s390x/fma.c @@ -0,0 +1,233 @@ +/* + * Test floating-point multiply-and-add instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include "float.h" + +union val { + float e; + double d; + long double x; + char buf[16]; +}; + +/* + * PoP tables as close to the original as possible. + */ +static const char *table1[N_SIGNED_CLASSES][N_SIGNED_CLASSES] = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "P(+inf)", "P(+inf)", "Xi: T(dNaN)", "Xi: T(dNaN)", "P(-inf)", "P(-inf)", "P(b)", "Xi: T(b*)"}, + {/* -Fn */ "P(+inf)", "P(a*b)", "P(+0)", "P(-0)", "P(a*b)", "P(-inf)", "P(b)", "Xi: T(b*)"}, + {/* -0 */ "Xi: T(dNaN)", "P(+0)", "P(+0)", "P(-0)", "P(-0)", "Xi: T(dNaN)", "P(b)", "Xi: T(b*)"}, + {/* +0 */ "Xi: T(dNaN)", "P(-0)", "P(-0)", "P(+0)", "P(+0)", "Xi: T(dNaN)", "P(b)", "Xi: T(b*)"}, + {/* +Fn */ "P(-inf)", "P(a*b)", "P(-0)", "P(+0)", "P(a*b)", "P(+inf)", "P(b)", "Xi: T(b*)"}, + {/* +inf */ "P(-inf)", "P(-inf)", "Xi: T(dNaN)", "Xi: T(dNaN)", "P(+inf)", "P(+inf)", "P(b)", "Xi: T(b*)"}, + {/* QNaN */ "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "P(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*)"}, +}; + +static const char *table2[N_SIGNED_CLASSES][N_SIGNED_CLASSES] = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(-inf)", "T(-inf)", "T(-inf)", "T(-inf)", "T(-inf)", "Xi: T(dNaN)", "T(c)", "Xi: T(c*)"}, + {/* -Fn */ "T(-inf)", "R(p+c)", "R(p)", "R(p)", "R(p+c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* -0 */ "T(-inf)", "R(c)", "T(-0)", "Rezd", "R(c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +0 */ "T(-inf)", "R(c)", "Rezd", "T(+0)", "R(c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +Fn */ "T(-inf)", "R(p+c)", "R(p)", "R(p)", "R(p+c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +inf */ "Xi: T(dNaN)", "T(+inf)", "T(+inf)", "T(+inf)", "T(+inf)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* QNaN */ "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "Xi: T(c*)"}, + /* SNaN: can't happen */ +}; + +static void interpret_tables(union val *r, bool *xi, int fmt, + int cls_a, const union val *a, + int cls_b, const union val *b, + int cls_c, const union val *c) +{ + const char *spec1 = table1[cls_a][cls_b]; + const char *spec2; + union val p; + int cls_p; + + *xi = false; + + if (strcmp(spec1, "P(-inf)") == 0) { + cls_p = CLASS_MINUS_INF; + } else if (strcmp(spec1, "P(+inf)") == 0) { + cls_p = CLASS_PLUS_INF; + } else if (strcmp(spec1, "P(-0)") == 0) { + cls_p = CLASS_MINUS_ZERO; + } else if (strcmp(spec1, "P(+0)") == 0) { + cls_p = CLASS_PLUS_ZERO; + } else if (strcmp(spec1, "P(a)") == 0) { + cls_p = cls_a; + memcpy(&p, a, sizeof(p)); + } else if (strcmp(spec1, "P(b)") == 0) { + cls_p = cls_b; + memcpy(&p, b, sizeof(p)); + } else if (strcmp(spec1, "P(a*b)") == 0) { + /* + * In the general case splitting fma into multiplication and addition + * doesn't work, but this is the case with our test inputs. + */ + cls_p = cls_a == cls_b ? CLASS_PLUS_FN : CLASS_MINUS_FN; + switch (fmt) { + case 0: + p.e = a->e * b->e; + break; + case 1: + p.d = a->d * b->d; + break; + case 2: + p.x = a->x * b->x; + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", fmt); + exit(1); + } + } else if (strcmp(spec1, "Xi: T(dNaN)") == 0) { + memcpy(r, default_nans[fmt], sizeof(*r)); + *xi = true; + return; + } else if (strcmp(spec1, "Xi: T(a*)") == 0) { + memcpy(r, a, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + return; + } else if (strcmp(spec1, "Xi: T(b*)") == 0) { + memcpy(r, b, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + return; + } else { + fprintf(stderr, "Unsupported spec1: %s\n", spec1); + exit(1); + } + + spec2 = table2[cls_p][cls_c]; + if (strcmp(spec2, "T(-inf)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_MINUS_INF].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(+inf)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_PLUS_INF].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(-0)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_MINUS_ZERO].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(+0)") == 0 || strcmp(spec2, "Rezd") == 0) { + memcpy(r, signed_floats[fmt][CLASS_PLUS_ZERO].v[0], sizeof(*r)); + } else if (strcmp(spec2, "R(c)") == 0 || strcmp(spec2, "T(c)") == 0) { + memcpy(r, c, sizeof(*r)); + } else if (strcmp(spec2, "R(p)") == 0 || strcmp(spec2, "T(p)") == 0) { + memcpy(r, &p, sizeof(*r)); + } else if (strcmp(spec2, "R(p+c)") == 0 || strcmp(spec2, "T(p+c)") == 0) { + switch (fmt) { + case 0: + r->e = p.e + c->e; + break; + case 1: + r->d = p.d + c->d; + break; + case 2: + r->x = p.x + c->x; + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", fmt); + exit(1); + } + } else if (strcmp(spec2, "Xi: T(dNaN)") == 0) { + memcpy(r, default_nans[fmt], sizeof(*r)); + *xi = true; + } else if (strcmp(spec2, "Xi: T(c*)") == 0) { + memcpy(r, c, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + } else { + fprintf(stderr, "Unsupported spec2: %s\n", spec2); + exit(1); + } +} + +struct iter { + int fmt; + int cls[3]; + int val[3]; +}; + +static bool iter_next(struct iter *it) +{ + int i; + + for (i = 2; i >= 0; i--) { + if (++it->val[i] != signed_floats[it->fmt][it->cls[i]].n) { + return true; + } + it->val[i] = 0; + + if (++it->cls[i] != N_SIGNED_CLASSES) { + return true; + } + it->cls[i] = 0; + } + + return ++it->fmt != N_FORMATS; +} + +int main(void) +{ + int ret = EXIT_SUCCESS; + struct iter it = {}; + + do { + size_t n = float_sizes[it.fmt]; + union val a, b, c, exp, res; + bool xi_exp, xi; + + memcpy(&a, signed_floats[it.fmt][it.cls[0]].v[it.val[0]], sizeof(a)); + memcpy(&b, signed_floats[it.fmt][it.cls[1]].v[it.val[1]], sizeof(b)); + memcpy(&c, signed_floats[it.fmt][it.cls[2]].v[it.val[2]], sizeof(c)); + + interpret_tables(&exp, &xi_exp, it.fmt, + it.cls[1], &b, it.cls[2], &c, it.cls[0], &a); + + memcpy(&res, &a, sizeof(res)); + feclearexcept(FE_ALL_EXCEPT); + switch (it.fmt) { + case 0: + asm("maebr %[a],%[b],%[c]" + : [a] "+f" (res.e) : [b] "f" (b.e), [c] "f" (c.e)); + break; + case 1: + asm("madbr %[a],%[b],%[c]" + : [a] "+f" (res.d) : [b] "f" (b.d), [c] "f" (c.d)); + break; + case 2: + asm("wfmaxb %[a],%[c],%[b],%[a]" + : [a] "+v" (res.x) : [b] "v" (b.x), [c] "v" (c.x)); + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", it.fmt); + exit(1); + } + xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID; + + if (memcmp(&res, &exp, n) != 0 || xi != xi_exp) { + fprintf(stderr, "[ FAILED ] "); + dump_v(stderr, &b, n); + fprintf(stderr, " * "); + dump_v(stderr, &c, n); + fprintf(stderr, " + "); + dump_v(stderr, &a, n); + fprintf(stderr, ": actual="); + dump_v(stderr, &res, n); + fprintf(stderr, "/%d, expected=", (int)xi); + dump_v(stderr, &exp, n); + fprintf(stderr, "/%d\n", (int)xi_exp); + ret = EXIT_FAILURE; + } + } while (iter_next(&it)); + + return ret; +} 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 index 7851ca7284..17210b4e02 100644 --- a/tests/tcg/s390x/gdbstub/test-svc.py +++ b/tests/tcg/s390x/gdbstub/test-svc.py @@ -3,20 +3,7 @@ This runs as a sourced script (via -x, via run-test.py).""" from __future__ import print_function import gdb -import sys - - -n_failures = 0 - - -def report(cond, msg): - """Report success/fail of a test""" - if cond: - print("PASS: {}".format(msg)) - else: - print("FAIL: {}".format(msg)) - global n_failures - n_failures += 1 +from test_gdbstub import main, report def run_test(): @@ -25,7 +12,7 @@ def run_test(): gdb.execute("si") report("larl\t" in gdb.execute("x/i $pc", False, True), "insn #2") gdb.execute("si") - report("lghi\t" in gdb.execute("x/i $pc", False, True), "insn #3") + 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") @@ -35,30 +22,4 @@ def run_test(): gdb.execute("si") -def main(): - """Prepare the environment and run through the tests""" - try: - inferior = gdb.selected_inferior() - print("ATTACHED: {}".format(inferior.architecture().name())) - except (gdb.error, AttributeError): - print("SKIPPING (not connected)") - 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: - report(False, "GDB Exception: {}".format(sys.exc_info()[0])) - print("All tests complete: %d failures" % n_failures) - exit(n_failures) - - -main() +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 index 2e9faa1604..4dbda12d35 100644 --- a/tests/tcg/s390x/hello-s390x-asm.S +++ b/tests/tcg/s390x/hello-s390x-asm.S @@ -8,7 +8,7 @@ _start: /* puts("Hello, World!"); */ lghi %r2,1 larl %r3,foo -lghi %r4,foo_end-foo +lgrl %r4,foo_len svc 4 /* exit(0); */ @@ -18,3 +18,5 @@ 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/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/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/per.S b/tests/tcg/s390x/per.S new file mode 100644 index 0000000000..79e704a6ff --- /dev/null +++ b/tests/tcg/s390x/per.S @@ -0,0 +1,82 @@ + .org 0x8d +ilc: + .org 0x8e +program_interruption_code: + .org 0x96 +per_code: + .org 0x98 +per_address: + .org 0x150 +program_old_psw: + .org 0x1d0 +program_new_psw: + .quad 0, pgm_handler + + .org 0x200 /* exit lowcore */ + +per_on_psw: + .quad 0x4000000000000000, start_per +per_on_regs: + .quad 0x80000000, 0, -1 /* successful-branching everywhere */ +per_off_regs: + .quad 0, 0 ,0 +success_psw: + .quad 0x2000000000000, 0xfff /* see is_special_wait_psw() */ +failure_psw: + .quad 0x2000000000000, 0 /* disabled wait */ + + .org 0x2000 /* exit lowcore pages */ + + .globl _start +_start: + lpswe per_on_psw +start_per: + lctlg %c9, %c11, per_on_regs + +/* Test unconditional relative branch. */ + larl %r0, j1 + larl %r1, d1 + lhi %r2, 0 +j1: j d1 + lpswe failure_psw +d1: + +/* Test unconditional indirect branch. */ + larl %r0, j2 + larl %r1, d2 +j2: br %r1 + lpswe failure_psw +d2: + +/* Test conditional relative branch. */ + larl %r0, j3 + larl %r1, d3 + clr %r1, %r2 /* d3 != 0 */ +j3: jne d3 + lpswe failure_psw +d3: + +/* Test conditional register branch. */ + larl %r0, j4 + larl %r1, d4 + clr %r1, %r2 /* d4 != 0 */ +j4: bner %r1 + lpswe failure_psw +d4: + +/* Success! */ + nop + lpswe success_psw + +pgm_handler: + chhsi program_interruption_code, 0x80 /* PER event? */ + jne fail + cli per_code, 0x80 /* successful-branching event? */ + jne fail + clg %r0, per_address /* per_address == jump insn? */ + jne fail + clg %r1, program_old_psw+8 /* psw.addr updated to dest? */ + jne fail + lpswe program_old_psw +fail: + lpswe failure_psw 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/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 index 22629df160..e66285f762 100644 --- a/tests/tcg/s390x/vfminmax.c +++ b/tests/tcg/s390x/vfminmax.c @@ -4,6 +4,8 @@ #include #include +#include "float.h" + /* * vfmin/vfmax instruction execution. */ @@ -21,98 +23,21 @@ 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; + 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) + : [v2] "m" (*(const char (*)[16])v2) + , [v3] "m" (*(const 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. */ @@ -285,13 +210,6 @@ struct signed_test { }, }; -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) @@ -320,10 +238,28 @@ static int signed_test(struct signed_test *test, int m4, int m5, return 0; } -static void snan_to_qnan(char *v, int m4) +struct iter { + int cls[2]; + int val[2]; +}; + +static bool iter_next(struct iter *it, int fmt) { - size_t bit = 1 + e_bits[m4 - 2]; - v[bit / 8] |= 1 << (7 - (bit % 8)); + int i; + + for (i = 1; i >= 0; i--) { + if (++it->val[i] != signed_floats[fmt][it->cls[i]].n) { + return true; + } + it->val[i] = 0; + + if (++it->cls[i] != N_SIGNED_CLASSES) { + return true; + } + it->cls[i] = 0; + } + + return false; } int main(void) @@ -333,72 +269,71 @@ int main(void) for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) { struct signed_test *test = &signed_tests[i]; - int m4; + int fmt; - for (m4 = 2; m4 <= 4; m4++) { - const unsigned char (*floats)[2][16] = signed_floats[m4 - 2]; - size_t float_size = float_sizes[m4 - 2]; + for (fmt = 0; fmt < N_FORMATS; fmt++) { + size_t float_size = float_sizes[fmt]; + int m4 = fmt + 2; int m5; for (m5 = 0; m5 <= 8; m5 += 8) { char v1_exp[16], v2[16], v3[16]; bool xi_exp = false; + struct iter it = {}; int pos = 0; - int i2; - for (i2 = 0; i2 < N_SIGNED_CLASSES * 2; i2++) { - int i3; + do { + const char *spec = test->table[it.cls[0]][it.cls[1]]; - for (i3 = 0; i3 < N_SIGNED_CLASSES * 2; i3++) { - const char *spec = test->table[i2 / 2][i3 / 2]; + memcpy(&v2[pos], + signed_floats[fmt][it.cls[0]].v[it.val[0]], + float_size); + memcpy(&v3[pos], + signed_floats[fmt][it.cls[1]].v[it.val[1]], + 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], fmt); + } else if (strcmp(spec, "Xi: T(b*)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + snan_to_qnan(&v1_exp[pos], fmt); + } 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]; - 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) { + if ((v2_int < v3_int) == + ((test->op == VFMIN) != (v2_int < 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; + 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; + } + } while (iter_next(&it, fmt)); if (pos != 0) { ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3); 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 47c39a44b6..7852fa62d8 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -3,12 +3,23 @@ # 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 $(call skip-test, $<, "BROKEN") run-plugin-signals-with-%: $(call skip-test, $<, "BROKEN") + +VPATH += $(SRC_PATH)/tests/tcg/sh4 + +test-macl: CFLAGS += -O -g +TESTS += test-macl + +test-macw: CFLAGS += -O -g +TESTS += test-macw + +test-addv: CFLAGS += -O -g +TESTS += test-addv + +test-subv: CFLAGS += -O -g +TESTS += test-subv diff --git a/tests/tcg/sh4/test-addv.c b/tests/tcg/sh4/test-addv.c new file mode 100644 index 0000000000..ca87fe746a --- /dev/null +++ b/tests/tcg/sh4/test-addv.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +static void addv(const int a, const int b, const int res, const int carry) +{ + int o = a, c; + + asm volatile("addv %2,%0\n" + "movt %1\n" + : "+r"(o), "=r"(c) : "r"(b) : ); + + if (c != carry || o != res) { + printf("ADDV %d, %d = %d/%d [T = %d/%d]\n", a, b, o, res, c, carry); + abort(); + } +} + +int main(void) +{ + addv(INT_MAX, 1, INT_MIN, 1); + addv(INT_MAX - 1, 1, INT_MAX, 0); + + return 0; +} 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/sh4/test-subv.c b/tests/tcg/sh4/test-subv.c new file mode 100644 index 0000000000..a3c2db96e4 --- /dev/null +++ b/tests/tcg/sh4/test-subv.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +static void subv(const int a, const int b, const int res, const int carry) +{ + int o = a, c; + + asm volatile("subv %2,%0\n" + "movt %1\n" + : "+r"(o), "=r"(c) : "r"(b) : ); + + if (c != carry || o != res) { + printf("SUBV %d, %d = %d/%d [T = %d/%d]\n", a, b, o, res, c, carry); + abort(); + } +} + +int main(void) +{ + subv(INT_MIN, 1, INT_MAX, 1); + subv(INT_MAX, -1, INT_MIN, 1); + subv(INT_MAX, 1, INT_MAX - 1, 0); + subv(0, 1, -1, 0); + subv(-1, -1, 0, 0); + + 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/asm/macros.h b/tests/tcg/tricore/asm/macros.h new file mode 100644 index 0000000000..e831f73721 --- /dev/null +++ b/tests/tcg/tricore/asm/macros.h @@ -0,0 +1,221 @@ +/* Helpers */ +#define LI(reg, val) \ + mov.u reg, lo:val; \ + 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 %d2 +#define DREG_RS3 %d4 +#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 %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 + +/* Test case wrappers */ +#define TEST_CASE(num, testreg, correct, code...) \ +test_ ## num: \ + code; \ + LI(DREG_CORRECT_RESULT, correct) \ + mov DREG_TEST_NUM, num; \ + jne testreg, DREG_CORRECT_RESULT, fail; \ + mov testreg, 0 + +#define TEST_CASE_E(num, correct_lo, correct_hi, code...) \ +test_ ## num: \ + code; \ + mov DREG_TEST_NUM, num; \ + LI(EREG_CORRECT_RESULT_LO, correct_lo) \ + jne EREG_CALC_RESULT_LO, EREG_CORRECT_RESULT_LO, fail; \ + LI(EREG_CORRECT_RESULT_HI, correct_hi) \ + jne EREG_CALC_RESULT_HI, EREG_CORRECT_RESULT_HI, fail; + +#define TEST_CASE_PSW(num, testreg, correct, correct_psw, code...) \ +test_ ## num: \ + code; \ + LI(DREG_CORRECT_RESULT, correct) \ + mov DREG_TEST_NUM, num; \ + jne testreg, DREG_CORRECT_RESULT, fail; \ + mfcr DREG_CALC_PSW, $psw; \ + LI(DREG_CORRECT_PSW, correct_psw) \ + 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); \ + insn DREG_CALC_RESULT, DREG_RS1; \ + ) + +#define TEST_D_D_PSW(insn, num, result, psw, rs1) \ + TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ + LI(DREG_RS1, rs1); \ + rstv; \ + 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); \ + LI(DREG_RS2, rs2); \ + rstv; \ + insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2; \ + ) + +#define TEST_D_DDD_PSW(insn, num, result, psw, rs1, rs2, rs3) \ + TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ + 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_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); \ + LI(DREG_RS2, rs2); \ + rstv; \ + 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); \ + LI(EREG_RS1_HI, rs1_hi); \ + LI(DREG_RS2, rs2); \ + 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; \ +fail: \ + LI(DREG_TEMP, TESTDEV_ADDR) \ + mov.a DREG_DEV_ADDR, DREG_TEMP; \ + st.w [DREG_DEV_ADDR], DREG_TEST_NUM;\ + debug; \ + j fail; \ +pass: \ + LI(DREG_TEMP, TESTDEV_ADDR) \ + mov.a DREG_DEV_ADDR, DREG_TEMP; \ + mov DREG_TEST_NUM, 0; \ + st.w [DREG_DEV_ADDR], DREG_TEST_NUM;\ + debug; \ + 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/tricore/macros.h b/tests/tcg/tricore/macros.h deleted file mode 100644 index 0d76fc403a..0000000000 --- a/tests/tcg/tricore/macros.h +++ /dev/null @@ -1,129 +0,0 @@ -/* Helpers */ -#define LI(reg, val) \ - mov.u reg, lo:val; \ - movh DREG_TEMP_LI, up:val; \ - or reg, reg, DREG_TEMP_LI; \ - -/* Address definitions */ -#define TESTDEV_ADDR 0xf0000000 -/* Register definitions */ -#define DREG_RS1 %d0 -#define DREG_RS2 %d1 -#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_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_CORRECT_RESULT_LO %d0 -#define EREG_CORRECT_RESULT_HI %d1 - -/* Test case wrappers */ -#define TEST_CASE(num, testreg, correct, code...) \ -test_ ## num: \ - code; \ - LI(DREG_CORRECT_RESULT, correct) \ - mov DREG_TEST_NUM, num; \ - jne testreg, DREG_CORRECT_RESULT, fail \ - -#define TEST_CASE_E(num, correct_lo, correct_hi, code...) \ -test_ ## num: \ - code; \ - mov DREG_TEST_NUM, num; \ - LI(EREG_CORRECT_RESULT_LO, correct_lo) \ - jne EREG_CALC_RESULT_LO, EREG_CORRECT_RESULT_LO, fail; \ - LI(EREG_CORRECT_RESULT_HI, correct_hi) \ - jne EREG_CALC_RESULT_HI, EREG_CORRECT_RESULT_HI, fail; - -#define TEST_CASE_PSW(num, testreg, correct, correct_psw, code...) \ -test_ ## num: \ - code; \ - LI(DREG_CORRECT_RESULT, correct) \ - mov DREG_TEST_NUM, num; \ - jne testreg, DREG_CORRECT_RESULT, fail; \ - mfcr DREG_CALC_PSW, $psw; \ - LI(DREG_CORRECT_PSW, correct_psw) \ - mov DREG_TEST_NUM, num; \ - jne DREG_CALC_PSW, DREG_CORRECT_PSW, 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); \ - insn DREG_CALC_RESULT, DREG_RS1; \ - ) - -#define TEST_D_D_PSW(insn, num, result, psw, rs1) \ - TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ - LI(DREG_RS1, rs1); \ - rstv; \ - insn DREG_CORRECT_RESULT, DREG_RS1; \ - ) - -#define TEST_D_DD_PSW(insn, num, result, psw, rs1, rs2) \ - TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ - LI(DREG_RS1, rs1); \ - LI(DREG_RS2, rs2); \ - rstv; \ - insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2; \ - ) - -#define TEST_D_DDD_PSW(insn, num, result, psw, rs1, rs2, rs3) \ - TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ - 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_DDI_PSW(insn, num, result, psw, rs1, rs2, imm) \ - TEST_CASE_PSW(num, DREG_CALC_RESULT, result, psw, \ - LI(DREG_RS1, rs1); \ - LI(DREG_RS2, rs2); \ - rstv; \ - insn DREG_CALC_RESULT, DREG_RS1, DREG_RS2, imm; \ - ) - -#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); \ - LI(EREG_RS1_HI, rs1_hi); \ - LI(DREG_RS2, rs2); \ - insn EREG_CALC_RESULT, EREG_RS1, DREG_RS2; \ - ) - -/* Pass/Fail handling part */ -#define TEST_PASSFAIL \ - j pass; \ -fail: \ - LI(DREG_TEMP, TESTDEV_ADDR) \ - mov.a DREG_DEV_ADDR, DREG_TEMP; \ - st.w [DREG_DEV_ADDR], DREG_TEST_NUM;\ - debug; \ - j fail; \ -pass: \ - LI(DREG_TEMP, TESTDEV_ADDR) \ - mov.a DREG_DEV_ADDR, DREG_TEMP; \ - mov DREG_TEST_NUM, 0; \ - st.w [DREG_DEV_ADDR], DREG_TEST_NUM;\ - debug; \ - j pass; diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 7207fee94c..ef6bcb4dc7 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -25,7 +25,7 @@ EXTRA_RUNS+=$(MULTIARCH_RUNS) .PRECIOUS: $(CRT_OBJS) %.o: $(CRT_PATH)/%.S - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -Wa,--noexecstack -c $< -o $@ # Build and link the tests %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) @@ -33,14 +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) $*) - # 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 4eac78293f..d6dff559c7 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -8,18 +8,29 @@ include $(SRC_PATH)/tests/tcg/i386/Makefile.target +X86_64_TESTS += test-2413 + ifeq ($(filter %-linux-user, $(TARGET)),$(TARGET)) X86_64_TESTS += vsyscall X86_64_TESTS += noexec X86_64_TESTS += cmpxchg +X86_64_TESTS += adox +X86_64_TESTS += test-1648 +X86_64_TESTS += test-2175 +X86_64_TESTS += cross-modifying-code TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else TESTS=$(MULTIARCH_TESTS) endif +adox: CFLAGS=-O2 + run-test-i386-ssse3: QEMU_OPTS += -cpu max run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max +cross-modifying-code: CFLAGS+=-pthread +cross-modifying-code: LDFLAGS+=-pthread + 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) 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/cross-modifying-code.c b/tests/tcg/x86_64/cross-modifying-code.c new file mode 100644 index 0000000000..2704df6061 --- /dev/null +++ b/tests/tcg/x86_64/cross-modifying-code.c @@ -0,0 +1,80 @@ +/* + * Test patching code, running in one thread, from another thread. + * + * Intel SDM calls this "cross-modifying code" and recommends a special + * sequence, which requires both threads to cooperate. + * + * Linux kernel uses a different sequence that does not require cooperation and + * involves patching the first byte with int3. + * + * Finally, there is user-mode software out there that simply uses atomics, and + * that seems to be good enough in practice. Test that QEMU has no problems + * with this as well. + */ + +#include +#include +#include +#include + +void add1_or_nop(long *x); +asm(".pushsection .rwx,\"awx\",@progbits\n" + ".globl add1_or_nop\n" + /* addq $0x1,(%rdi) */ + "add1_or_nop: .byte 0x48, 0x83, 0x07, 0x01\n" + "ret\n" + ".popsection\n"); + +#define THREAD_WAIT 0 +#define THREAD_PATCH 1 +#define THREAD_STOP 2 + +static void *thread_func(void *arg) +{ + int val = 0x0026748d; /* nop */ + + while (true) { + switch (__atomic_load_n((int *)arg, __ATOMIC_SEQ_CST)) { + case THREAD_WAIT: + break; + case THREAD_PATCH: + val = __atomic_exchange_n((int *)&add1_or_nop, val, + __ATOMIC_SEQ_CST); + break; + case THREAD_STOP: + return NULL; + default: + assert(false); + __builtin_unreachable(); + } + } +} + +#define INITIAL 42 +#define COUNT 1000000 + +int main(void) +{ + int command = THREAD_WAIT; + pthread_t thread; + long x = 0; + int err; + int i; + + err = pthread_create(&thread, NULL, &thread_func, &command); + assert(err == 0); + + __atomic_store_n(&command, THREAD_PATCH, __ATOMIC_SEQ_CST); + for (i = 0; i < COUNT; i++) { + add1_or_nop(&x); + } + __atomic_store_n(&command, THREAD_STOP, __ATOMIC_SEQ_CST); + + err = pthread_join(thread, NULL); + assert(err == 0); + + assert(x >= INITIAL); + assert(x <= INITIAL + COUNT); + + return EXIT_SUCCESS; +} 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/x86_64/test-1648.c b/tests/tcg/x86_64/test-1648.c new file mode 100644 index 0000000000..fd0644a8ce --- /dev/null +++ b/tests/tcg/x86_64/test-1648.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/1648 */ + +#include + +__attribute__((noinline)) +void bar(void) +{ + /* Success! Continue through sigreturn. */ +} + +/* + * Because of the change of ABI between foo and bar, the compiler is + * required to save XMM6-XMM15. The compiler will use MOVAPS or MOVDQA, + * which will trap if the stack frame is not 16 byte aligned. + */ +__attribute__((noinline, ms_abi)) +void foo(void) +{ + bar(); +} + +void sighandler(int num) +{ + foo(); +} + +int main(void) +{ + signal(SIGUSR1, sighandler); + raise(SIGUSR1); + return 0; +} diff --git a/tests/tcg/x86_64/test-2175.c b/tests/tcg/x86_64/test-2175.c new file mode 100644 index 0000000000..aafd037bce --- /dev/null +++ b/tests/tcg/x86_64/test-2175.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2185 */ + +#include + +int test_setc(unsigned int x, unsigned int y) +{ + asm("blsi %1, %0; setc %b0" : "+r"(x) : "r"(y)); + return (unsigned char)x; +} + +int test_pushf(unsigned int x, unsigned int y) +{ + asm("blsi %1, %0; pushf; pop %q0" : "+r"(x) : "r"(y)); + return x & 1; +} + +int main() +{ + assert(test_setc(1, 0xedbf530a)); + assert(test_pushf(1, 0xedbf530a)); + return 0; +} + diff --git a/tests/tcg/x86_64/test-2413.c b/tests/tcg/x86_64/test-2413.c new file mode 100644 index 0000000000..456e5332fc --- /dev/null +++ b/tests/tcg/x86_64/test-2413.c @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright 2024 Linaro, Ltd. */ +/* See https://gitlab.com/qemu-project/qemu/-/issues/2413 */ + +#include + +void test(unsigned long *a, unsigned long *d, unsigned long c) +{ + asm("xorl %%eax, %%eax\n\t" + "xorl %%edx, %%edx\n\t" + "testb $0x20, %%cl\n\t" + "sete %%al\n\t" + "setne %%dl\n\t" + "shll %%cl, %%eax\n\t" + "shll %%cl, %%edx\n\t" + : "=a"(*a), "=d"(*d) + : "c"(c)); +} + +int main(void) +{ + unsigned long a, c, d; + + for (c = 0; c < 64; c++) { + test(&a, &d, c); + assert(a == (c & 0x20 ? 0 : 1u << (c & 0x1f))); + assert(d == (c & 0x20 ? 1u << (c & 0x1f) : 0)); + } + return 0; +} 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/blacklist.tsan deleted file mode 100644 index 75e444f5dc..0000000000 --- a/tests/tsan/blacklist.tsan +++ /dev/null @@ -1,10 +0,0 @@ -# This is an example blacklist. -# To enable use of the blacklist add this to configure: -# "--extra-cflags=-fsanitize-blacklist=/tests/tsan/blacklist.tsan" -# The eventual goal would be to fix these warnings. - -# TSan is not happy about setting/getting of dirty bits, -# for example, cpu_physical_memory_set_dirty_range, -# and cpu_physical_memory_get_dirty. -src:bitops.c -src:bitmap.c diff --git a/tests/tsan/ignore.tsan b/tests/tsan/ignore.tsan new file mode 100644 index 0000000000..423e482d2f --- /dev/null +++ b/tests/tsan/ignore.tsan @@ -0,0 +1,10 @@ +# 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, +# for example, cpu_physical_memory_set_dirty_range, +# and cpu_physical_memory_get_dirty. +src:bitops.c +src:bitmap.c 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..f4eaebd8ff 100644 --- a/tests/uefi-test-tools/Makefile +++ b/tests/uefi-test-tools/Makefile @@ -12,7 +12,7 @@ edk2_dir := ../../roms/edk2 images_dir := ../data/uefi-boot-images -emulation_targets := arm aarch64 i386 x86_64 +emulation_targets := arm aarch64 i386 x86_64 riscv64 uefi_binaries := bios-tables-test intermediate_suffixes := .efi .fat .iso.raw @@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat # stripped from, the argument. map_arm_to_uefi = $(subst arm,ARM,$(1)) map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1))) -map_i386_to_uefi = $(subst i386,IA32,$(call map_aarch64_to_uefi,$(1))) +map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1))) +map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1))) map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1))) map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1))) @@ -70,7 +71,7 @@ Build/%.fat: Build/%.efi uefi_bin_b=$$(stat --format=%s -- $<) && \ uefi_fat_kb=$$(( (uefi_bin_b * 11 / 10 + 1023) / 1024 )) && \ uefi_fat_kb=$$(( uefi_fat_kb >= 64 ? uefi_fat_kb : 64 )) && \ - mkdosfs -C $@ -n $(basename $(@F)) -- $$uefi_fat_kb + mkdosfs -C $@ -n "bios-test" -- $$uefi_fat_kb MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI/BOOT MTOOLS_SKIP_CHECK=1 mcopy -i $@ -- $< \ @@ -87,7 +88,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 @@ -95,15 +96,9 @@ Build/%.fat: Build/%.efi # we must mark the recipe manually as recursive, by using the "+" indicator. # This way, when the inner "make" starts a parallel build of the target edk2 # module, it can communicate with the outer "make"'s job server. -Build/bios-tables-test.%.efi: build-edk2-tools - +./build.sh $(edk2_dir) BiosTablesTest $* $@ - -build-edk2-tools: - cd $(edk2_dir)/BaseTools && git submodule update --init --force - $(MAKE) -C $(edk2_dir)/BaseTools \ - PYTHON_COMMAND=$${EDK2_PYTHON_COMMAND:-python3} \ - EXTRA_OPTFLAGS='$(EDK2_BASETOOLS_OPTFLAGS)' \ - EXTRA_LDFLAGS='$(EDK2_BASETOOLS_LDFLAGS)' +Build/bios-tables-test.%.efi: + $(PYTHON) ../../roms/edk2-build.py --config uefi-test-build.config \ + --match $* clean: rm -rf Build Conf log diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc index c8511cd732..0902fd3c73 100644 --- a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc @@ -19,7 +19,7 @@ PLATFORM_VERSION = 0.1 PLATFORM_NAME = UefiTestTools SKUID_IDENTIFIER = DEFAULT - SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64 + SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64 BUILD_TARGETS = DEBUG [BuildOptions.IA32] @@ -60,6 +60,10 @@ [LibraryClasses.IA32, LibraryClasses.X64] BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf + RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf + +[LibraryClasses.RISCV64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F diff --git a/tests/uefi-test-tools/uefi-test-build.config b/tests/uefi-test-tools/uefi-test-build.config new file mode 100644 index 0000000000..a4c61fc97a --- /dev/null +++ b/tests/uefi-test-tools/uefi-test-build.config @@ -0,0 +1,52 @@ +[global] +core = ../../roms/edk2 + +#################################################################################### +# arm + +[build.arm] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = ARM +cpy1 = ARM/BiosTablesTest.efi bios-tables-test.arm.efi + +#################################################################################### +# aarch64 + +[build.aarch64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = AARCH64 +cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi + +#################################################################################### +# riscv64 + +[build.riscv64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = RISCV64 +cpy1 = RISCV64/BiosTablesTest.efi bios-tables-test.riscv64.efi + +#################################################################################### +# ia32 + +[build.ia32] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = IA32 +cpy1 = IA32/BiosTablesTest.efi bios-tables-test.i386.efi + +#################################################################################### +# x64 + +[build.x64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = X64 +cpy1 = X64/BiosTablesTest.efi bios-tables-test.x86_64.efi 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 c6cc740772..36527fd655 100644 --- a/tests/unit/crypto-tls-psk-helpers.c +++ b/tests/unit/crypto-tls-psk-helpers.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" -#include "crypto-tls-x509-helpers.h" #include "crypto-tls-psk-helpers.h" #include "qemu/sockets.h" diff --git a/tests/unit/crypto-tls-x509-helpers.c b/tests/unit/crypto-tls-x509-helpers.c index e9937f60d8..2daecc416c 100644 --- a/tests/unit/crypto-tls-x509-helpers.c +++ b/tests/unit/crypto-tls-x509-helpers.c @@ -20,15 +20,19 @@ #include "qemu/osdep.h" +#include + #include "crypto-tls-x509-helpers.h" #include "crypto/init.h" #include "qemu/sockets.h" +#include "pkix_asn1_tab.c.inc" + /* * This stores some static data that is needed when * encoding extensions in the x509 certs */ -asn1_node pkix_asn1; +static asn1_node pkix_asn1; /* * To avoid consuming random entropy to generate keys, @@ -131,6 +135,7 @@ void test_tls_init(const char *keyfile) void test_tls_cleanup(const char *keyfile) { asn1_delete_structure(&pkix_asn1); + gnutls_x509_privkey_deinit(privkey); unlink(keyfile); } @@ -498,8 +503,7 @@ void test_tls_write_cert_chain(const char *filename, g_free(buffer); } - -void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +void test_tls_deinit_cert(QCryptoTLSTestCertReq *req) { if (!req->crt) { return; @@ -507,6 +511,15 @@ void test_tls_discard_cert(QCryptoTLSTestCertReq *req) gnutls_x509_crt_deinit(req->crt); req->crt = NULL; +} + +void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +{ + if (!req->crt) { + return; + } + + test_tls_deinit_cert(req); if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { unlink(req->filename); diff --git a/tests/unit/crypto-tls-x509-helpers.h b/tests/unit/crypto-tls-x509-helpers.h index 247e7160eb..2a0f7c04fd 100644 --- a/tests/unit/crypto-tls-x509-helpers.h +++ b/tests/unit/crypto-tls-x509-helpers.h @@ -23,7 +23,6 @@ #include #include -#include #define QCRYPTO_TLS_TEST_CLIENT_NAME "ACME QEMU Client" @@ -74,6 +73,12 @@ void test_tls_generate_cert(QCryptoTLSTestCertReq *req, void test_tls_write_cert_chain(const char *filename, gnutls_x509_crt_t *certs, size_t ncerts); +/* + * Deinitialize the QCryptoTLSTestCertReq, but don't delete the certificate + * file on disk. (The caller is then responsible for doing that themselves. + */ +void test_tls_deinit_cert(QCryptoTLSTestCertReq *req); +/* Deinit the QCryptoTLSTestCertReq, and delete the certificate file */ void test_tls_discard_cert(QCryptoTLSTestCertReq *req); void test_tls_init(const char *keyfile); @@ -171,6 +176,4 @@ void test_tls_cleanup(const char *keyfile); }; \ test_tls_generate_cert(&varname, cavarname.crt) -extern const asn1_static_node pkix_asn1_tab[]; - #endif diff --git a/tests/unit/meson.build b/tests/unit/meson.build index b497a41378..d5248ae51d 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -11,17 +11,18 @@ tests = { 'check-qobject': [], 'check-qjson': [], 'check-qlit': [], + 'test-error-report': [], 'test-qobject-output-visitor': [testqapi], 'test-clone-visitor': [testqapi], 'test-qobject-input-visitor': [testqapi], 'test-forward-visitor': [testqapi], 'test-string-input-visitor': [testqapi], 'test-string-output-visitor': [testqapi], - '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 +36,7 @@ tests = { 'test-rcu-slist': [], 'test-qdist': [], 'test-qht': [], + 'test-qtree': [], 'test-bitops': [], 'test-bitcnt': [], 'test-qgraph': ['../qtest/libqos/qgraph.c'], @@ -43,10 +45,9 @@ tests = { 'test-qemu-opts': [], 'test-keyval': [testqapi], 'test-logging': [], - '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-fifo': [], } if have_system or have_tools @@ -55,7 +56,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 @@ -92,16 +93,18 @@ if have_block 'test-crypto-ivgen': [io], 'test-crypto-afsplit': [io], 'test-crypto-block': [io], + 'test-timed-average': [], + 'test-uuid': [], } 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', + 'test-crypto-tlscredsx509': ['crypto-tls-x509-helpers.c', tasn1, crypto, gnutls], - 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', 'crypto-tls-psk-helpers.c', + 'test-crypto-tlssession': ['crypto-tls-x509-helpers.c', 'crypto-tls-psk-helpers.c', tasn1, crypto, gnutls], - 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', 'pkix_asn1_tab.c', + 'test-io-channel-tls': ['io-channel-helpers.c', 'crypto-tls-x509-helpers.c', tasn1, io, crypto, gnutls]} endif if pam.found() @@ -110,29 +113,31 @@ 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': [], + } endif if config_host_data.get('CONFIG_REPLICATION') tests += {'test-replication': [testblock]} endif - 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 + tests += {'test-crypto-pbkdf': [io]} endif if have_system tests += { + 'ptimer-test': ['ptimer-test-stubs.c', meson.project_source_root() / 'hw/core/ptimer.c'], 'test-iov': [], + 'test-opts-visitor': [testqapi], + 'test-xs-node': [qom], + 'test-virtio-dmabuf': [meson.project_source_root() / 'hw/display/virtio-dmabuf.c'], 'test-qmp-cmds': [testqapi], 'test-xbzrle': [migration], - 'test-timed-average': [], '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] } @@ -143,8 +148,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] } @@ -156,7 +161,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 @@ -166,8 +171,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/pkix_asn1_tab.c b/tests/unit/pkix_asn1_tab.c deleted file mode 100644 index 89521408a1..0000000000 --- a/tests/unit/pkix_asn1_tab.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * This file is taken from gnutls 1.6.3 under the GPLv2+ - * and is under copyright of various GNUTLS contributors. - */ - -#include "qemu/osdep.h" -#include "crypto-tls-x509-helpers.h" - -const asn1_static_node pkix_asn1_tab[] = { - {"PKIX1", 536875024, 0}, - {0, 1073741836, 0}, - {"id-ce", 1879048204, 0}, - {"joint-iso-ccitt", 1073741825, "2"}, - {"ds", 1073741825, "5"}, - {0, 1, "29"}, - {"id-ce-authorityKeyIdentifier", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "35"}, - {"AuthorityKeyIdentifier", 1610612741, 0}, - {"keyIdentifier", 1610637314, "KeyIdentifier"}, - {0, 4104, "0"}, - {"authorityCertIssuer", 1610637314, "GeneralNames"}, - {0, 4104, "1"}, - {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, - {0, 4104, "2"}, - {"KeyIdentifier", 1073741831, 0}, - {"id-ce-subjectKeyIdentifier", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "14"}, - {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, - {"id-ce-keyUsage", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "15"}, - {"KeyUsage", 1610874886, 0}, - {"digitalSignature", 1073741825, "0"}, - {"nonRepudiation", 1073741825, "1"}, - {"keyEncipherment", 1073741825, "2"}, - {"dataEncipherment", 1073741825, "3"}, - {"keyAgreement", 1073741825, "4"}, - {"keyCertSign", 1073741825, "5"}, - {"cRLSign", 1073741825, "6"}, - {"encipherOnly", 1073741825, "7"}, - {"decipherOnly", 1, "8"}, - {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "16"}, - {"PrivateKeyUsagePeriod", 1610612741, 0}, - {"notBefore", 1619025937, 0}, - {0, 4104, "0"}, - {"notAfter", 545284113, 0}, - {0, 4104, "1"}, - {"id-ce-certificatePolicies", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "32"}, - {"CertificatePolicies", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "PolicyInformation"}, - {"PolicyInformation", 1610612741, 0}, - {"policyIdentifier", 1073741826, "CertPolicyId"}, - {"policyQualifiers", 538984459, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "PolicyQualifierInfo"}, - {"CertPolicyId", 1073741836, 0}, - {"PolicyQualifierInfo", 1610612741, 0}, - {"policyQualifierId", 1073741826, "PolicyQualifierId"}, - {"qualifier", 541065229, 0}, - {"policyQualifierId", 1, 0}, - {"PolicyQualifierId", 1073741836, 0}, - {"CPSuri", 1073741826, "IA5String"}, - {"UserNotice", 1610612741, 0}, - {"noticeRef", 1073758210, "NoticeReference"}, - {"explicitText", 16386, "DisplayText"}, - {"NoticeReference", 1610612741, 0}, - {"organization", 1073741826, "DisplayText"}, - {"noticeNumbers", 536870923, 0}, - {0, 3, 0}, - {"DisplayText", 1610612754, 0}, - {"visibleString", 1612709890, "VisibleString"}, - {"200", 524298, "1"}, - {"bmpString", 1612709890, "BMPString"}, - {"200", 524298, "1"}, - {"utf8String", 538968066, "UTF8String"}, - {"200", 524298, "1"}, - {"id-ce-policyMappings", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "33"}, - {"PolicyMappings", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 536870917, 0}, - {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, - {"subjectDomainPolicy", 2, "CertPolicyId"}, - {"DirectoryString", 1610612754, 0}, - {"teletexString", 1612709890, "TeletexString"}, - {"MAX", 524298, "1"}, - {"printableString", 1612709890, "PrintableString"}, - {"MAX", 524298, "1"}, - {"universalString", 1612709890, "UniversalString"}, - {"MAX", 524298, "1"}, - {"utf8String", 1612709890, "UTF8String"}, - {"MAX", 524298, "1"}, - {"bmpString", 1612709890, "BMPString"}, - {"MAX", 524298, "1"}, - {"ia5String", 538968066, "IA5String"}, - {"MAX", 524298, "1"}, - {"id-ce-subjectAltName", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "17"}, - {"SubjectAltName", 1073741826, "GeneralNames"}, - {"GeneralNames", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "GeneralName"}, - {"GeneralName", 1610612754, 0}, - {"otherName", 1610620930, "AnotherName"}, - {0, 4104, "0"}, - {"rfc822Name", 1610620930, "IA5String"}, - {0, 4104, "1"}, - {"dNSName", 1610620930, "IA5String"}, - {0, 4104, "2"}, - {"x400Address", 1610620930, "ORAddress"}, - {0, 4104, "3"}, - {"directoryName", 1610620930, "RDNSequence"}, - {0, 2056, "4"}, - {"ediPartyName", 1610620930, "EDIPartyName"}, - {0, 4104, "5"}, - {"uniformResourceIdentifier", 1610620930, "IA5String"}, - {0, 4104, "6"}, - {"iPAddress", 1610620935, 0}, - {0, 4104, "7"}, - {"registeredID", 536879116, 0}, - {0, 4104, "8"}, - {"AnotherName", 1610612741, 0}, - {"type-id", 1073741836, 0}, - {"value", 541073421, 0}, - {0, 1073743880, "0"}, - {"type-id", 1, 0}, - {"EDIPartyName", 1610612741, 0}, - {"nameAssigner", 1610637314, "DirectoryString"}, - {0, 4104, "0"}, - {"partyName", 536879106, "DirectoryString"}, - {0, 4104, "1"}, - {"id-ce-issuerAltName", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "18"}, - {"IssuerAltName", 1073741826, "GeneralNames"}, - {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "9"}, - {"SubjectDirectoryAttributes", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "Attribute"}, - {"id-ce-basicConstraints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "19"}, - {"BasicConstraints", 1610612741, 0}, - {"cA", 1610645508, 0}, - {0, 131081, 0}, - {"pathLenConstraint", 537411587, 0}, - {"0", 10, "MAX"}, - {"id-ce-nameConstraints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "30"}, - {"NameConstraints", 1610612741, 0}, - {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, - {0, 4104, "0"}, - {"excludedSubtrees", 536895490, "GeneralSubtrees"}, - {0, 4104, "1"}, - {"GeneralSubtrees", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "GeneralSubtree"}, - {"GeneralSubtree", 1610612741, 0}, - {"base", 1073741826, "GeneralName"}, - {"minimum", 1610653698, "BaseDistance"}, - {0, 1073741833, "0"}, - {0, 4104, "0"}, - {"maximum", 536895490, "BaseDistance"}, - {0, 4104, "1"}, - {"BaseDistance", 1611137027, 0}, - {"0", 10, "MAX"}, - {"id-ce-policyConstraints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "36"}, - {"PolicyConstraints", 1610612741, 0}, - {"requireExplicitPolicy", 1610637314, "SkipCerts"}, - {0, 4104, "0"}, - {"inhibitPolicyMapping", 536895490, "SkipCerts"}, - {0, 4104, "1"}, - {"SkipCerts", 1611137027, 0}, - {"0", 10, "MAX"}, - {"id-ce-cRLDistributionPoints", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "31"}, - {"CRLDistributionPoints", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "DistributionPoint"}, - {"DistributionPoint", 1610612741, 0}, - {"distributionPoint", 1610637314, "DistributionPointName"}, - {0, 2056, "0"}, - {"reasons", 1610637314, "ReasonFlags"}, - {0, 4104, "1"}, - {"cRLIssuer", 536895490, "GeneralNames"}, - {0, 4104, "2"}, - {"DistributionPointName", 1610612754, 0}, - {"fullName", 1610620930, "GeneralNames"}, - {0, 4104, "0"}, - {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, - {0, 4104, "1"}, - {"ReasonFlags", 1610874886, 0}, - {"unused", 1073741825, "0"}, - {"keyCompromise", 1073741825, "1"}, - {"cACompromise", 1073741825, "2"}, - {"affiliationChanged", 1073741825, "3"}, - {"superseded", 1073741825, "4"}, - {"cessationOfOperation", 1073741825, "5"}, - {"certificateHold", 1073741825, "6"}, - {"privilegeWithdrawn", 1073741825, "7"}, - {"aACompromise", 1, "8"}, - {"id-ce-extKeyUsage", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "37"}, - {"ExtKeyUsageSyntax", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "KeyPurposeId"}, - {"KeyPurposeId", 1073741836, 0}, - {"id-kp-serverAuth", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "1"}, - {"id-kp-clientAuth", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "2"}, - {"id-kp-codeSigning", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "3"}, - {"id-kp-emailProtection", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "4"}, - {"id-kp-ipsecEndSystem", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "5"}, - {"id-kp-ipsecTunnel", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "6"}, - {"id-kp-ipsecUser", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "7"}, - {"id-kp-timeStamping", 1879048204, 0}, - {0, 1073741825, "id-kp"}, - {0, 1, "8"}, - {"id-pe-authorityInfoAccess", 1879048204, 0}, - {0, 1073741825, "id-pe"}, - {0, 1, "1"}, - {"AuthorityInfoAccessSyntax", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "AccessDescription"}, - {"AccessDescription", 1610612741, 0}, - {"accessMethod", 1073741836, 0}, - {"accessLocation", 2, "GeneralName"}, - {"id-ce-cRLNumber", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "20"}, - {"CRLNumber", 1611137027, 0}, - {"0", 10, "MAX"}, - {"id-ce-issuingDistributionPoint", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "28"}, - {"IssuingDistributionPoint", 1610612741, 0}, - {"distributionPoint", 1610637314, "DistributionPointName"}, - {0, 4104, "0"}, - {"onlyContainsUserCerts", 1610653700, 0}, - {0, 1073872905, 0}, - {0, 4104, "1"}, - {"onlyContainsCACerts", 1610653700, 0}, - {0, 1073872905, 0}, - {0, 4104, "2"}, - {"onlySomeReasons", 1610637314, "ReasonFlags"}, - {0, 4104, "3"}, - {"indirectCRL", 536911876, 0}, - {0, 1073872905, 0}, - {0, 4104, "4"}, - {"id-ce-deltaCRLIndicator", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "27"}, - {"BaseCRLNumber", 1073741826, "CRLNumber"}, - {"id-ce-cRLReasons", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "21"}, - {"CRLReason", 1610874901, 0}, - {"unspecified", 1073741825, "0"}, - {"keyCompromise", 1073741825, "1"}, - {"cACompromise", 1073741825, "2"}, - {"affiliationChanged", 1073741825, "3"}, - {"superseded", 1073741825, "4"}, - {"cessationOfOperation", 1073741825, "5"}, - {"certificateHold", 1073741825, "6"}, - {"removeFromCRL", 1, "8"}, - {"id-ce-certificateIssuer", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "29"}, - {"CertificateIssuer", 1073741826, "GeneralNames"}, - {"id-ce-holdInstructionCode", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "23"}, - {"HoldInstructionCode", 1073741836, 0}, - {"holdInstruction", 1879048204, 0}, - {"joint-iso-itu-t", 1073741825, "2"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"x9cm", 1073741825, "10040"}, - {0, 1, "2"}, - {"id-holdinstruction-none", 1879048204, 0}, - {0, 1073741825, "holdInstruction"}, - {0, 1, "1"}, - {"id-holdinstruction-callissuer", 1879048204, 0}, - {0, 1073741825, "holdInstruction"}, - {0, 1, "2"}, - {"id-holdinstruction-reject", 1879048204, 0}, - {0, 1073741825, "holdInstruction"}, - {0, 1, "3"}, - {"id-ce-invalidityDate", 1879048204, 0}, - {0, 1073741825, "id-ce"}, - {0, 1, "24"}, - {"InvalidityDate", 1082130449, 0}, - {"VisibleString", 1610620935, 0}, - {0, 4360, "26"}, - {"NumericString", 1610620935, 0}, - {0, 4360, "18"}, - {"IA5String", 1610620935, 0}, - {0, 4360, "22"}, - {"TeletexString", 1610620935, 0}, - {0, 4360, "20"}, - {"PrintableString", 1610620935, 0}, - {0, 4360, "19"}, - {"UniversalString", 1610620935, 0}, - {0, 4360, "28"}, - {"BMPString", 1610620935, 0}, - {0, 4360, "30"}, - {"UTF8String", 1610620935, 0}, - {0, 4360, "12"}, - {"id-pkix", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"identified-organization", 1073741825, "3"}, - {"dod", 1073741825, "6"}, - {"internet", 1073741825, "1"}, - {"security", 1073741825, "5"}, - {"mechanisms", 1073741825, "5"}, - {"pkix", 1, "7"}, - {"id-pe", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "1"}, - {"id-qt", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "2"}, - {"id-kp", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "3"}, - {"id-ad", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "48"}, - {"id-qt-cps", 1879048204, 0}, - {0, 1073741825, "id-qt"}, - {0, 1, "1"}, - {"id-qt-unotice", 1879048204, 0}, - {0, 1073741825, "id-qt"}, - {0, 1, "2"}, - {"id-ad-ocsp", 1879048204, 0}, - {0, 1073741825, "id-ad"}, - {0, 1, "1"}, - {"id-ad-caIssuers", 1879048204, 0}, - {0, 1073741825, "id-ad"}, - {0, 1, "2"}, - {"Attribute", 1610612741, 0}, - {"type", 1073741826, "AttributeType"}, - {"values", 536870927, 0}, - {0, 2, "AttributeValue"}, - {"AttributeType", 1073741836, 0}, - {"AttributeValue", 1614807053, 0}, - {"type", 1, 0}, - {"AttributeTypeAndValue", 1610612741, 0}, - {"type", 1073741826, "AttributeType"}, - {"value", 2, "AttributeValue"}, - {"id-at", 1879048204, 0}, - {"joint-iso-ccitt", 1073741825, "2"}, - {"ds", 1073741825, "5"}, - {0, 1, "4"}, - {"id-at-initials", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "43"}, - {"X520initials", 1073741826, "DirectoryString"}, - {"id-at-generationQualifier", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "44"}, - {"X520generationQualifier", 1073741826, "DirectoryString"}, - {"id-at-surname", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "4"}, - {"X520surName", 1073741826, "DirectoryString"}, - {"id-at-givenName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "42"}, - {"X520givenName", 1073741826, "DirectoryString"}, - {"id-at-name", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "41"}, - {"X520name", 1073741826, "DirectoryString"}, - {"id-at-commonName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "3"}, - {"X520CommonName", 1073741826, "DirectoryString"}, - {"id-at-localityName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "7"}, - {"X520LocalityName", 1073741826, "DirectoryString"}, - {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "8"}, - {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, - {"id-at-organizationName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "10"}, - {"X520OrganizationName", 1073741826, "DirectoryString"}, - {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "11"}, - {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, - {"id-at-title", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "12"}, - {"X520Title", 1073741826, "DirectoryString"}, - {"id-at-description", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "13"}, - {"X520Description", 1073741826, "DirectoryString"}, - {"id-at-dnQualifier", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "46"}, - {"X520dnQualifier", 1073741826, "PrintableString"}, - {"id-at-countryName", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "6"}, - {"X520countryName", 1612709890, "PrintableString"}, - {0, 1048586, "2"}, - {"id-at-serialNumber", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "5"}, - {"X520serialNumber", 1073741826, "PrintableString"}, - {"id-at-telephoneNumber", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "20"}, - {"X520telephoneNumber", 1073741826, "PrintableString"}, - {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "23"}, - {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, - {"id-at-pseudonym", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "65"}, - {"X520pseudonym", 1073741826, "DirectoryString"}, - {"id-at-name", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "41"}, - {"X520name", 1073741826, "DirectoryString"}, - {"id-at-streetAddress", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "9"}, - {"X520streetAddress", 1073741826, "DirectoryString"}, - {"id-at-postalAddress", 1880096780, "AttributeType"}, - {0, 1073741825, "id-at"}, - {0, 1, "16"}, - {"X520postalAddress", 1073741826, "PostalAddress"}, - {"PostalAddress", 1610612747, 0}, - {0, 2, "DirectoryString"}, - {"pkcs", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {"pkcs", 1, "1"}, - {"pkcs-9", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "9"}, - {"emailAddress", 1880096780, "AttributeType"}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "1"}, - {"Pkcs9email", 1612709890, "IA5String"}, - {"ub-emailaddress-length", 524298, "1"}, - {"Name", 1610612754, 0}, - {"rdnSequence", 2, "RDNSequence"}, - {"RDNSequence", 1610612747, 0}, - {0, 2, "RelativeDistinguishedName"}, - {"DistinguishedName", 1073741826, "RDNSequence"}, - {"RelativeDistinguishedName", 1612709903, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "AttributeTypeAndValue"}, - {"Certificate", 1610612741, 0}, - {"tbsCertificate", 1073741826, "TBSCertificate"}, - {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"signature", 6, 0}, - {"TBSCertificate", 1610612741, 0}, - {"version", 1610653698, "Version"}, - {0, 1073741833, "v1"}, - {0, 2056, "0"}, - {"serialNumber", 1073741826, "CertificateSerialNumber"}, - {"signature", 1073741826, "AlgorithmIdentifier"}, - {"issuer", 1073741826, "Name"}, - {"validity", 1073741826, "Validity"}, - {"subject", 1073741826, "Name"}, - {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, - {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, - {0, 4104, "1"}, - {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, - {0, 4104, "2"}, - {"extensions", 536895490, "Extensions"}, - {0, 2056, "3"}, - {"Version", 1610874883, 0}, - {"v1", 1073741825, "0"}, - {"v2", 1073741825, "1"}, - {"v3", 1, "2"}, - {"CertificateSerialNumber", 1073741827, 0}, - {"Validity", 1610612741, 0}, - {"notBefore", 1073741826, "Time"}, - {"notAfter", 2, "Time"}, - {"Time", 1610612754, 0}, - {"utcTime", 1090519057, 0}, - {"generalTime", 8388625, 0}, - {"UniqueIdentifier", 1073741830, 0}, - {"SubjectPublicKeyInfo", 1610612741, 0}, - {"algorithm", 1073741826, "AlgorithmIdentifier"}, - {"subjectPublicKey", 6, 0}, - {"Extensions", 1612709899, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "Extension"}, - {"Extension", 1610612741, 0}, - {"extnID", 1073741836, 0}, - {"critical", 1610645508, 0}, - {0, 131081, 0}, - {"extnValue", 7, 0}, - {"CertificateList", 1610612741, 0}, - {"tbsCertList", 1073741826, "TBSCertList"}, - {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"signature", 6, 0}, - {"TBSCertList", 1610612741, 0}, - {"version", 1073758210, "Version"}, - {"signature", 1073741826, "AlgorithmIdentifier"}, - {"issuer", 1073741826, "Name"}, - {"thisUpdate", 1073741826, "Time"}, - {"nextUpdate", 1073758210, "Time"}, - {"revokedCertificates", 1610629131, 0}, - {0, 536870917, 0}, - {"userCertificate", 1073741826, "CertificateSerialNumber"}, - {"revocationDate", 1073741826, "Time"}, - {"crlEntryExtensions", 16386, "Extensions"}, - {"crlExtensions", 536895490, "Extensions"}, - {0, 2056, "0"}, - {"AlgorithmIdentifier", 1610612741, 0}, - {"algorithm", 1073741836, 0}, - {"parameters", 541081613, 0}, - {"algorithm", 1, 0}, - {"pkcs-1", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "1"}, - {"rsaEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "1"}, - {"md2WithRSAEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "2"}, - {"md5WithRSAEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "4"}, - {"sha1WithRSAEncryption", 1879048204, 0}, - {0, 1073741825, "pkcs-1"}, - {0, 1, "5"}, - {"id-dsa-with-sha1", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"x9-57", 1073741825, "10040"}, - {"x9algorithm", 1073741825, "4"}, - {0, 1, "3"}, - {"Dss-Sig-Value", 1610612741, 0}, - {"r", 1073741827, 0}, - {"s", 3, 0}, - {"dhpublicnumber", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"ansi-x942", 1073741825, "10046"}, - {"number-type", 1073741825, "2"}, - {0, 1, "1"}, - {"DomainParameters", 1610612741, 0}, - {"p", 1073741827, 0}, - {"g", 1073741827, 0}, - {"q", 1073741827, 0}, - {"j", 1073758211, 0}, - {"validationParms", 16386, "ValidationParms"}, - {"ValidationParms", 1610612741, 0}, - {"seed", 1073741830, 0}, - {"pgenCounter", 3, 0}, - {"id-dsa", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"x9-57", 1073741825, "10040"}, - {"x9algorithm", 1073741825, "4"}, - {0, 1, "1"}, - {"Dss-Parms", 1610612741, 0}, - {"p", 1073741827, 0}, - {"q", 1073741827, 0}, - {"g", 3, 0}, - {"ORAddress", 1610612741, 0}, - {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, - {"built-in-domain-defined-attributes", 1073758210, - "BuiltInDomainDefinedAttributes"}, - {"extension-attributes", 16386, "ExtensionAttributes"}, - {"BuiltInStandardAttributes", 1610612741, 0}, - {"country-name", 1073758210, "CountryName"}, - {"administration-domain-name", 1073758210, "AdministrationDomainName"}, - {"network-address", 1610637314, "NetworkAddress"}, - {0, 2056, "0"}, - {"terminal-identifier", 1610637314, "TerminalIdentifier"}, - {0, 2056, "1"}, - {"private-domain-name", 1610637314, "PrivateDomainName"}, - {0, 2056, "2"}, - {"organization-name", 1610637314, "OrganizationName"}, - {0, 2056, "3"}, - {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, - {0, 2056, "4"}, - {"personal-name", 1610637314, "PersonalName"}, - {0, 2056, "5"}, - {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, - {0, 2056, "6"}, - {"CountryName", 1610620946, 0}, - {0, 1073746952, "1"}, - {"x121-dcc-code", 1612709890, "NumericString"}, - {0, 1048586, "ub-country-name-numeric-length"}, - {"iso-3166-alpha2-code", 538968066, "PrintableString"}, - {0, 1048586, "ub-country-name-alpha-length"}, - {"AdministrationDomainName", 1610620946, 0}, - {0, 1073744904, "2"}, - {"numeric", 1612709890, "NumericString"}, - {"ub-domain-name-length", 524298, "0"}, - {"printable", 538968066, "PrintableString"}, - {"ub-domain-name-length", 524298, "0"}, - {"NetworkAddress", 1073741826, "X121Address"}, - {"X121Address", 1612709890, "NumericString"}, - {"ub-x121-address-length", 524298, "1"}, - {"TerminalIdentifier", 1612709890, "PrintableString"}, - {"ub-terminal-id-length", 524298, "1"}, - {"PrivateDomainName", 1610612754, 0}, - {"numeric", 1612709890, "NumericString"}, - {"ub-domain-name-length", 524298, "1"}, - {"printable", 538968066, "PrintableString"}, - {"ub-domain-name-length", 524298, "1"}, - {"OrganizationName", 1612709890, "PrintableString"}, - {"ub-organization-name-length", 524298, "1"}, - {"NumericUserIdentifier", 1612709890, "NumericString"}, - {"ub-numeric-user-id-length", 524298, "1"}, - {"PersonalName", 1610612750, 0}, - {"surname", 1814044674, "PrintableString"}, - {0, 1073745928, "0"}, - {"ub-surname-length", 524298, "1"}, - {"given-name", 1814061058, "PrintableString"}, - {0, 1073745928, "1"}, - {"ub-given-name-length", 524298, "1"}, - {"initials", 1814061058, "PrintableString"}, - {0, 1073745928, "2"}, - {"ub-initials-length", 524298, "1"}, - {"generation-qualifier", 740319234, "PrintableString"}, - {0, 1073745928, "3"}, - {"ub-generation-qualifier-length", 524298, "1"}, - {"OrganizationalUnitNames", 1612709899, 0}, - {"ub-organizational-units", 1074266122, "1"}, - {0, 2, "OrganizationalUnitName"}, - {"OrganizationalUnitName", 1612709890, "PrintableString"}, - {"ub-organizational-unit-name-length", 524298, "1"}, - {"BuiltInDomainDefinedAttributes", 1612709899, 0}, - {"ub-domain-defined-attributes", 1074266122, "1"}, - {0, 2, "BuiltInDomainDefinedAttribute"}, - {"BuiltInDomainDefinedAttribute", 1610612741, 0}, - {"type", 1612709890, "PrintableString"}, - {"ub-domain-defined-attribute-type-length", 524298, "1"}, - {"value", 538968066, "PrintableString"}, - {"ub-domain-defined-attribute-value-length", 524298, "1"}, - {"ExtensionAttributes", 1612709903, 0}, - {"ub-extension-attributes", 1074266122, "1"}, - {0, 2, "ExtensionAttribute"}, - {"ExtensionAttribute", 1610612741, 0}, - {"extension-attribute-type", 1611145219, 0}, - {0, 1073743880, "0"}, - {"0", 10, "ub-extension-attributes"}, - {"extension-attribute-value", 541073421, 0}, - {0, 1073743880, "1"}, - {"extension-attribute-type", 1, 0}, - {"common-name", 1342177283, "1"}, - {"CommonName", 1612709890, "PrintableString"}, - {"ub-common-name-length", 524298, "1"}, - {"teletex-common-name", 1342177283, "2"}, - {"TeletexCommonName", 1612709890, "TeletexString"}, - {"ub-common-name-length", 524298, "1"}, - {"teletex-organization-name", 1342177283, "3"}, - {"TeletexOrganizationName", 1612709890, "TeletexString"}, - {"ub-organization-name-length", 524298, "1"}, - {"teletex-personal-name", 1342177283, "4"}, - {"TeletexPersonalName", 1610612750, 0}, - {"surname", 1814044674, "TeletexString"}, - {0, 1073743880, "0"}, - {"ub-surname-length", 524298, "1"}, - {"given-name", 1814061058, "TeletexString"}, - {0, 1073743880, "1"}, - {"ub-given-name-length", 524298, "1"}, - {"initials", 1814061058, "TeletexString"}, - {0, 1073743880, "2"}, - {"ub-initials-length", 524298, "1"}, - {"generation-qualifier", 740319234, "TeletexString"}, - {0, 1073743880, "3"}, - {"ub-generation-qualifier-length", 524298, "1"}, - {"teletex-organizational-unit-names", 1342177283, "5"}, - {"TeletexOrganizationalUnitNames", 1612709899, 0}, - {"ub-organizational-units", 1074266122, "1"}, - {0, 2, "TeletexOrganizationalUnitName"}, - {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, - {"ub-organizational-unit-name-length", 524298, "1"}, - {"pds-name", 1342177283, "7"}, - {"PDSName", 1612709890, "PrintableString"}, - {"ub-pds-name-length", 524298, "1"}, - {"physical-delivery-country-name", 1342177283, "8"}, - {"PhysicalDeliveryCountryName", 1610612754, 0}, - {"x121-dcc-code", 1612709890, "NumericString"}, - {0, 1048586, "ub-country-name-numeric-length"}, - {"iso-3166-alpha2-code", 538968066, "PrintableString"}, - {0, 1048586, "ub-country-name-alpha-length"}, - {"postal-code", 1342177283, "9"}, - {"PostalCode", 1610612754, 0}, - {"numeric-code", 1612709890, "NumericString"}, - {"ub-postal-code-length", 524298, "1"}, - {"printable-code", 538968066, "PrintableString"}, - {"ub-postal-code-length", 524298, "1"}, - {"physical-delivery-office-name", 1342177283, "10"}, - {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, - {"physical-delivery-office-number", 1342177283, "11"}, - {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, - {"extension-OR-address-components", 1342177283, "12"}, - {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, - {"physical-delivery-personal-name", 1342177283, "13"}, - {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, - {"physical-delivery-organization-name", 1342177283, "14"}, - {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, - {"extension-physical-delivery-address-components", 1342177283, "15"}, - {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, - {"unformatted-postal-address", 1342177283, "16"}, - {"UnformattedPostalAddress", 1610612750, 0}, - {"printable-address", 1814052875, 0}, - {"ub-pds-physical-address-lines", 1074266122, "1"}, - {0, 538968066, "PrintableString"}, - {"ub-pds-parameter-length", 524298, "1"}, - {"teletex-string", 740311042, "TeletexString"}, - {"ub-unformatted-address-length", 524298, "1"}, - {"street-address", 1342177283, "17"}, - {"StreetAddress", 1073741826, "PDSParameter"}, - {"post-office-box-address", 1342177283, "18"}, - {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, - {"poste-restante-address", 1342177283, "19"}, - {"PosteRestanteAddress", 1073741826, "PDSParameter"}, - {"unique-postal-name", 1342177283, "20"}, - {"UniquePostalName", 1073741826, "PDSParameter"}, - {"local-postal-attributes", 1342177283, "21"}, - {"LocalPostalAttributes", 1073741826, "PDSParameter"}, - {"PDSParameter", 1610612750, 0}, - {"printable-string", 1814052866, "PrintableString"}, - {"ub-pds-parameter-length", 524298, "1"}, - {"teletex-string", 740311042, "TeletexString"}, - {"ub-pds-parameter-length", 524298, "1"}, - {"extended-network-address", 1342177283, "22"}, - {"ExtendedNetworkAddress", 1610612754, 0}, - {"e163-4-address", 1610612741, 0}, - {"number", 1612718082, "NumericString"}, - {0, 1073743880, "0"}, - {"ub-e163-4-number-length", 524298, "1"}, - {"sub-address", 538992642, "NumericString"}, - {0, 1073743880, "1"}, - {"ub-e163-4-sub-address-length", 524298, "1"}, - {"psap-address", 536879106, "PresentationAddress"}, - {0, 2056, "0"}, - {"PresentationAddress", 1610612741, 0}, - {"pSelector", 1610637319, 0}, - {0, 2056, "0"}, - {"sSelector", 1610637319, 0}, - {0, 2056, "1"}, - {"tSelector", 1610637319, 0}, - {0, 2056, "2"}, - {"nAddresses", 538976271, 0}, - {0, 1073743880, "3"}, - {"MAX", 1074266122, "1"}, - {0, 7, 0}, - {"terminal-type", 1342177283, "23"}, - {"TerminalType", 1610874883, 0}, - {"telex", 1073741825, "3"}, - {"teletex", 1073741825, "4"}, - {"g3-facsimile", 1073741825, "5"}, - {"g4-facsimile", 1073741825, "6"}, - {"ia5-terminal", 1073741825, "7"}, - {"videotex", 1, "8"}, - {"teletex-domain-defined-attributes", 1342177283, "6"}, - {"TeletexDomainDefinedAttributes", 1612709899, 0}, - {"ub-domain-defined-attributes", 1074266122, "1"}, - {0, 2, "TeletexDomainDefinedAttribute"}, - {"TeletexDomainDefinedAttribute", 1610612741, 0}, - {"type", 1612709890, "TeletexString"}, - {"ub-domain-defined-attribute-type-length", 524298, "1"}, - {"value", 538968066, "TeletexString"}, - {"ub-domain-defined-attribute-value-length", 524298, "1"}, - {"ub-name", 1342177283, "32768"}, - {"ub-common-name", 1342177283, "64"}, - {"ub-locality-name", 1342177283, "128"}, - {"ub-state-name", 1342177283, "128"}, - {"ub-organization-name", 1342177283, "64"}, - {"ub-organizational-unit-name", 1342177283, "64"}, - {"ub-title", 1342177283, "64"}, - {"ub-match", 1342177283, "128"}, - {"ub-emailaddress-length", 1342177283, "128"}, - {"ub-common-name-length", 1342177283, "64"}, - {"ub-country-name-alpha-length", 1342177283, "2"}, - {"ub-country-name-numeric-length", 1342177283, "3"}, - {"ub-domain-defined-attributes", 1342177283, "4"}, - {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, - {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, - {"ub-domain-name-length", 1342177283, "16"}, - {"ub-extension-attributes", 1342177283, "256"}, - {"ub-e163-4-number-length", 1342177283, "15"}, - {"ub-e163-4-sub-address-length", 1342177283, "40"}, - {"ub-generation-qualifier-length", 1342177283, "3"}, - {"ub-given-name-length", 1342177283, "16"}, - {"ub-initials-length", 1342177283, "5"}, - {"ub-integer-options", 1342177283, "256"}, - {"ub-numeric-user-id-length", 1342177283, "32"}, - {"ub-organization-name-length", 1342177283, "64"}, - {"ub-organizational-unit-name-length", 1342177283, "32"}, - {"ub-organizational-units", 1342177283, "4"}, - {"ub-pds-name-length", 1342177283, "16"}, - {"ub-pds-parameter-length", 1342177283, "30"}, - {"ub-pds-physical-address-lines", 1342177283, "6"}, - {"ub-postal-code-length", 1342177283, "16"}, - {"ub-surname-length", 1342177283, "40"}, - {"ub-terminal-id-length", 1342177283, "24"}, - {"ub-unformatted-address-length", 1342177283, "180"}, - {"ub-x121-address-length", 1342177283, "16"}, - {"pkcs-7-ContentInfo", 1610612741, 0}, - {"contentType", 1073741826, "pkcs-7-ContentType"}, - {"content", 541073421, 0}, - {0, 1073743880, "0"}, - {"contentType", 1, 0}, - {"pkcs-7-DigestInfo", 1610612741, 0}, - {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, - {"digest", 2, "pkcs-7-Digest"}, - {"pkcs-7-Digest", 1073741831, 0}, - {"pkcs-7-ContentType", 1073741836, 0}, - {"pkcs-7-SignedData", 1610612741, 0}, - {"version", 1073741826, "pkcs-7-CMSVersion"}, - {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, - {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, - {"certificates", 1610637314, "pkcs-7-CertificateSet"}, - {0, 4104, "0"}, - {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, - {0, 4104, "1"}, - {"signerInfos", 2, "pkcs-7-SignerInfos"}, - {"pkcs-7-CMSVersion", 1610874883, 0}, - {"v0", 1073741825, "0"}, - {"v1", 1073741825, "1"}, - {"v2", 1073741825, "2"}, - {"v3", 1073741825, "3"}, - {"v4", 1, "4"}, - {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, - {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, - {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, - {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, - {"eContentType", 1073741826, "pkcs-7-ContentType"}, - {"eContent", 536895495, 0}, - {0, 2056, "0"}, - {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, - {0, 13, 0}, - {"pkcs-7-CertificateChoices", 1610612754, 0}, - {"certificate", 13, 0}, - {"pkcs-7-CertificateSet", 1610612751, 0}, - {0, 2, "pkcs-7-CertificateChoices"}, - {"pkcs-7-SignerInfos", 1610612751, 0}, - {0, 13, 0}, - {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, - {"version", 1610874883, 0}, - {"v1", 1, "0"}, - {"subject", 1073741826, "Name"}, - {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, - {"attributes", 536879106, "Attributes"}, - {0, 4104, "0"}, - {"Attributes", 1610612751, 0}, - {0, 2, "Attribute"}, - {"pkcs-10-CertificationRequest", 1610612741, 0}, - {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, - {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"signature", 6, 0}, - {"pkcs-9-ub-challengePassword", 1342177283, "255"}, - {"pkcs-9-certTypes", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "22"}, - {"pkcs-9-crlTypes", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "23"}, - {"pkcs-9-at-challengePassword", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "7"}, - {"pkcs-9-challengePassword", 1610612754, 0}, - {"printableString", 1612709890, "PrintableString"}, - {"pkcs-9-ub-challengePassword", 524298, "1"}, - {"utf8String", 538968066, "UTF8String"}, - {"pkcs-9-ub-challengePassword", 524298, "1"}, - {"pkcs-9-at-localKeyId", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "21"}, - {"pkcs-9-localKeyId", 1073741831, 0}, - {"pkcs-9-at-friendlyName", 1879048204, 0}, - {0, 1073741825, "pkcs-9"}, - {0, 1, "20"}, - {"pkcs-9-friendlyName", 1612709890, "BMPString"}, - {"255", 524298, "1"}, - {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, - {"version", 1073741826, "pkcs-8-Version"}, - {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, - {"attributes", 536895490, "Attributes"}, - {0, 4104, "0"}, - {"pkcs-8-Version", 1610874883, 0}, - {"v1", 1, "0"}, - {"pkcs-8-PrivateKey", 1073741831, 0}, - {"pkcs-8-Attributes", 1610612751, 0}, - {0, 2, "Attribute"}, - {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, - {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, - {"encryptedData", 2, "pkcs-8-EncryptedData"}, - {"pkcs-8-EncryptedData", 1073741831, 0}, - {"pkcs-5", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "5"}, - {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {0, 1, "3"}, - {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, - {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, - {0, 1, "7"}, - {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, - {0, 1048586, "8"}, - {"pkcs-5-id-PBES2", 1879048204, 0}, - {0, 1073741825, "pkcs-5"}, - {0, 1, "13"}, - {"pkcs-5-PBES2-params", 1610612741, 0}, - {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, - {"encryptionScheme", 2, "AlgorithmIdentifier"}, - {"pkcs-5-id-PBKDF2", 1879048204, 0}, - {0, 1073741825, "pkcs-5"}, - {0, 1, "12"}, - {"pkcs-5-PBKDF2-params", 1610612741, 0}, - {"salt", 1610612754, 0}, - {"specified", 1073741831, 0}, - {"otherSource", 2, "AlgorithmIdentifier"}, - {"iterationCount", 1611137027, 0}, - {"1", 10, "MAX"}, - {"keyLength", 1611153411, 0}, - {"1", 10, "MAX"}, - {"prf", 16386, "AlgorithmIdentifier"}, - {"pkcs-12", 1879048204, 0}, - {0, 1073741825, "pkcs"}, - {0, 1, "12"}, - {"pkcs-12-PFX", 1610612741, 0}, - {"version", 1610874883, 0}, - {"v3", 1, "3"}, - {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, - {"macData", 16386, "pkcs-12-MacData"}, - {"pkcs-12-PbeParams", 1610612741, 0}, - {"salt", 1073741831, 0}, - {"iterations", 3, 0}, - {"pkcs-12-MacData", 1610612741, 0}, - {"mac", 1073741826, "pkcs-7-DigestInfo"}, - {"macSalt", 1073741831, 0}, - {"iterations", 536903683, 0}, - {0, 9, "1"}, - {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, - {0, 2, "pkcs-7-ContentInfo"}, - {"pkcs-12-SafeContents", 1610612747, 0}, - {0, 2, "pkcs-12-SafeBag"}, - {"pkcs-12-SafeBag", 1610612741, 0}, - {"bagId", 1073741836, 0}, - {"bagValue", 1614815245, 0}, - {0, 1073743880, "0"}, - {"badId", 1, 0}, - {"bagAttributes", 536887311, 0}, - {0, 2, "pkcs-12-PKCS12Attribute"}, - {"pkcs-12-bagtypes", 1879048204, 0}, - {0, 1073741825, "pkcs-12"}, - {0, 1073741825, "10"}, - {0, 1, "1"}, - {"pkcs-12-keyBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "1"}, - {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "2"}, - {"pkcs-12-certBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "3"}, - {"pkcs-12-crlBag", 1879048204, 0}, - {0, 1073741825, "pkcs-12-bagtypes"}, - {0, 1, "4"}, - {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, - {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, - {"pkcs-12-CertBag", 1610612741, 0}, - {"certId", 1073741836, 0}, - {"certValue", 541073421, 0}, - {0, 1073743880, "0"}, - {"certId", 1, 0}, - {"pkcs-12-CRLBag", 1610612741, 0}, - {"crlId", 1073741836, 0}, - {"crlValue", 541073421, 0}, - {0, 1073743880, "0"}, - {"crlId", 1, 0}, - {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, - {"pkcs-7-data", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {"pkcs", 1073741825, "1"}, - {"pkcs7", 1073741825, "7"}, - {0, 1, "1"}, - {"pkcs-7-encryptedData", 1879048204, 0}, - {"iso", 1073741825, "1"}, - {"member-body", 1073741825, "2"}, - {"us", 1073741825, "840"}, - {"rsadsi", 1073741825, "113549"}, - {"pkcs", 1073741825, "1"}, - {"pkcs7", 1073741825, "7"}, - {0, 1, "6"}, - {"pkcs-7-Data", 1073741831, 0}, - {"pkcs-7-EncryptedData", 1610612741, 0}, - {"version", 1073741826, "pkcs-7-CMSVersion"}, - {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, - {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, - {0, 4104, "1"}, - {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, - {"contentType", 1073741826, "pkcs-7-ContentType"}, - {"contentEncryptionAlgorithm", 1073741826, - "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, - {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, - {0, 4104, "0"}, - {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, - "AlgorithmIdentifier"}, - {"pkcs-7-EncryptedContent", 1073741831, 0}, - {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, - {"MAX", 1074266122, "1"}, - {0, 2, "Attribute"}, - {"id-at-ldap-DC", 1880096780, "AttributeType"}, - {0, 1073741825, "0"}, - {0, 1073741825, "9"}, - {0, 1073741825, "2342"}, - {0, 1073741825, "19200300"}, - {0, 1073741825, "100"}, - {0, 1073741825, "1"}, - {0, 1, "25"}, - {"ldap-DC", 1073741826, "IA5String"}, - {"id-at-ldap-UID", 1880096780, "AttributeType"}, - {0, 1073741825, "0"}, - {0, 1073741825, "9"}, - {0, 1073741825, "2342"}, - {0, 1073741825, "19200300"}, - {0, 1073741825, "100"}, - {0, 1073741825, "1"}, - {0, 1, "1"}, - {"ldap-UID", 1073741826, "DirectoryString"}, - {"id-pda", 1879048204, 0}, - {0, 1073741825, "id-pkix"}, - {0, 1, "9"}, - {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "1"}, - {"DateOfBirth", 1082130449, 0}, - {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "2"}, - {"PlaceOfBirth", 1073741826, "DirectoryString"}, - {"id-pda-gender", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "3"}, - {"Gender", 1612709890, "PrintableString"}, - {0, 1048586, "1"}, - {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "4"}, - {"CountryOfCitizenship", 1612709890, "PrintableString"}, - {0, 1048586, "2"}, - {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, - {0, 1073741825, "id-pda"}, - {0, 1, "5"}, - {"CountryOfResidence", 538968066, "PrintableString"}, - {0, 1048586, "2"}, - {0, 0, 0} -}; diff --git a/tests/unit/pkix_asn1_tab.c.inc b/tests/unit/pkix_asn1_tab.c.inc new file mode 100644 index 0000000000..fe29c4102a --- /dev/null +++ b/tests/unit/pkix_asn1_tab.c.inc @@ -0,0 +1,1102 @@ +/* + * This file is taken from gnutls 1.6.3 under the GPLv2+ + * and is under copyright of various GNUTLS contributors. + */ + +static const asn1_static_node pkix_asn1_tab[] = { + {"PKIX1", 536875024, 0}, + {0, 1073741836, 0}, + {"id-ce", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "29"}, + {"id-ce-authorityKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "35"}, + {"AuthorityKeyIdentifier", 1610612741, 0}, + {"keyIdentifier", 1610637314, "KeyIdentifier"}, + {0, 4104, "0"}, + {"authorityCertIssuer", 1610637314, "GeneralNames"}, + {0, 4104, "1"}, + {"authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + {0, 4104, "2"}, + {"KeyIdentifier", 1073741831, 0}, + {"id-ce-subjectKeyIdentifier", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "14"}, + {"SubjectKeyIdentifier", 1073741826, "KeyIdentifier"}, + {"id-ce-keyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "15"}, + {"KeyUsage", 1610874886, 0}, + {"digitalSignature", 1073741825, "0"}, + {"nonRepudiation", 1073741825, "1"}, + {"keyEncipherment", 1073741825, "2"}, + {"dataEncipherment", 1073741825, "3"}, + {"keyAgreement", 1073741825, "4"}, + {"keyCertSign", 1073741825, "5"}, + {"cRLSign", 1073741825, "6"}, + {"encipherOnly", 1073741825, "7"}, + {"decipherOnly", 1, "8"}, + {"id-ce-privateKeyUsagePeriod", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "16"}, + {"PrivateKeyUsagePeriod", 1610612741, 0}, + {"notBefore", 1619025937, 0}, + {0, 4104, "0"}, + {"notAfter", 545284113, 0}, + {0, 4104, "1"}, + {"id-ce-certificatePolicies", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "32"}, + {"CertificatePolicies", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyInformation"}, + {"PolicyInformation", 1610612741, 0}, + {"policyIdentifier", 1073741826, "CertPolicyId"}, + {"policyQualifiers", 538984459, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "PolicyQualifierInfo"}, + {"CertPolicyId", 1073741836, 0}, + {"PolicyQualifierInfo", 1610612741, 0}, + {"policyQualifierId", 1073741826, "PolicyQualifierId"}, + {"qualifier", 541065229, 0}, + {"policyQualifierId", 1, 0}, + {"PolicyQualifierId", 1073741836, 0}, + {"CPSuri", 1073741826, "IA5String"}, + {"UserNotice", 1610612741, 0}, + {"noticeRef", 1073758210, "NoticeReference"}, + {"explicitText", 16386, "DisplayText"}, + {"NoticeReference", 1610612741, 0}, + {"organization", 1073741826, "DisplayText"}, + {"noticeNumbers", 536870923, 0}, + {0, 3, 0}, + {"DisplayText", 1610612754, 0}, + {"visibleString", 1612709890, "VisibleString"}, + {"200", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"200", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"200", 524298, "1"}, + {"id-ce-policyMappings", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "33"}, + {"PolicyMappings", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 536870917, 0}, + {"issuerDomainPolicy", 1073741826, "CertPolicyId"}, + {"subjectDomainPolicy", 2, "CertPolicyId"}, + {"DirectoryString", 1610612754, 0}, + {"teletexString", 1612709890, "TeletexString"}, + {"MAX", 524298, "1"}, + {"printableString", 1612709890, "PrintableString"}, + {"MAX", 524298, "1"}, + {"universalString", 1612709890, "UniversalString"}, + {"MAX", 524298, "1"}, + {"utf8String", 1612709890, "UTF8String"}, + {"MAX", 524298, "1"}, + {"bmpString", 1612709890, "BMPString"}, + {"MAX", 524298, "1"}, + {"ia5String", 538968066, "IA5String"}, + {"MAX", 524298, "1"}, + {"id-ce-subjectAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "17"}, + {"SubjectAltName", 1073741826, "GeneralNames"}, + {"GeneralNames", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralName"}, + {"GeneralName", 1610612754, 0}, + {"otherName", 1610620930, "AnotherName"}, + {0, 4104, "0"}, + {"rfc822Name", 1610620930, "IA5String"}, + {0, 4104, "1"}, + {"dNSName", 1610620930, "IA5String"}, + {0, 4104, "2"}, + {"x400Address", 1610620930, "ORAddress"}, + {0, 4104, "3"}, + {"directoryName", 1610620930, "RDNSequence"}, + {0, 2056, "4"}, + {"ediPartyName", 1610620930, "EDIPartyName"}, + {0, 4104, "5"}, + {"uniformResourceIdentifier", 1610620930, "IA5String"}, + {0, 4104, "6"}, + {"iPAddress", 1610620935, 0}, + {0, 4104, "7"}, + {"registeredID", 536879116, 0}, + {0, 4104, "8"}, + {"AnotherName", 1610612741, 0}, + {"type-id", 1073741836, 0}, + {"value", 541073421, 0}, + {0, 1073743880, "0"}, + {"type-id", 1, 0}, + {"EDIPartyName", 1610612741, 0}, + {"nameAssigner", 1610637314, "DirectoryString"}, + {0, 4104, "0"}, + {"partyName", 536879106, "DirectoryString"}, + {0, 4104, "1"}, + {"id-ce-issuerAltName", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "18"}, + {"IssuerAltName", 1073741826, "GeneralNames"}, + {"id-ce-subjectDirectoryAttributes", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "9"}, + {"SubjectDirectoryAttributes", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-ce-basicConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "19"}, + {"BasicConstraints", 1610612741, 0}, + {"cA", 1610645508, 0}, + {0, 131081, 0}, + {"pathLenConstraint", 537411587, 0}, + {"0", 10, "MAX"}, + {"id-ce-nameConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "30"}, + {"NameConstraints", 1610612741, 0}, + {"permittedSubtrees", 1610637314, "GeneralSubtrees"}, + {0, 4104, "0"}, + {"excludedSubtrees", 536895490, "GeneralSubtrees"}, + {0, 4104, "1"}, + {"GeneralSubtrees", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "GeneralSubtree"}, + {"GeneralSubtree", 1610612741, 0}, + {"base", 1073741826, "GeneralName"}, + {"minimum", 1610653698, "BaseDistance"}, + {0, 1073741833, "0"}, + {0, 4104, "0"}, + {"maximum", 536895490, "BaseDistance"}, + {0, 4104, "1"}, + {"BaseDistance", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-policyConstraints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "36"}, + {"PolicyConstraints", 1610612741, 0}, + {"requireExplicitPolicy", 1610637314, "SkipCerts"}, + {0, 4104, "0"}, + {"inhibitPolicyMapping", 536895490, "SkipCerts"}, + {0, 4104, "1"}, + {"SkipCerts", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-cRLDistributionPoints", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "31"}, + {"CRLDistributionPoints", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "DistributionPoint"}, + {"DistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 2056, "0"}, + {"reasons", 1610637314, "ReasonFlags"}, + {0, 4104, "1"}, + {"cRLIssuer", 536895490, "GeneralNames"}, + {0, 4104, "2"}, + {"DistributionPointName", 1610612754, 0}, + {"fullName", 1610620930, "GeneralNames"}, + {0, 4104, "0"}, + {"nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + {0, 4104, "1"}, + {"ReasonFlags", 1610874886, 0}, + {"unused", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"privilegeWithdrawn", 1073741825, "7"}, + {"aACompromise", 1, "8"}, + {"id-ce-extKeyUsage", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "37"}, + {"ExtKeyUsageSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "KeyPurposeId"}, + {"KeyPurposeId", 1073741836, 0}, + {"id-kp-serverAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "1"}, + {"id-kp-clientAuth", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "2"}, + {"id-kp-codeSigning", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "3"}, + {"id-kp-emailProtection", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "4"}, + {"id-kp-ipsecEndSystem", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "5"}, + {"id-kp-ipsecTunnel", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "6"}, + {"id-kp-ipsecUser", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "7"}, + {"id-kp-timeStamping", 1879048204, 0}, + {0, 1073741825, "id-kp"}, + {0, 1, "8"}, + {"id-pe-authorityInfoAccess", 1879048204, 0}, + {0, 1073741825, "id-pe"}, + {0, 1, "1"}, + {"AuthorityInfoAccessSyntax", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AccessDescription"}, + {"AccessDescription", 1610612741, 0}, + {"accessMethod", 1073741836, 0}, + {"accessLocation", 2, "GeneralName"}, + {"id-ce-cRLNumber", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "20"}, + {"CRLNumber", 1611137027, 0}, + {"0", 10, "MAX"}, + {"id-ce-issuingDistributionPoint", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "28"}, + {"IssuingDistributionPoint", 1610612741, 0}, + {"distributionPoint", 1610637314, "DistributionPointName"}, + {0, 4104, "0"}, + {"onlyContainsUserCerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "1"}, + {"onlyContainsCACerts", 1610653700, 0}, + {0, 1073872905, 0}, + {0, 4104, "2"}, + {"onlySomeReasons", 1610637314, "ReasonFlags"}, + {0, 4104, "3"}, + {"indirectCRL", 536911876, 0}, + {0, 1073872905, 0}, + {0, 4104, "4"}, + {"id-ce-deltaCRLIndicator", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "27"}, + {"BaseCRLNumber", 1073741826, "CRLNumber"}, + {"id-ce-cRLReasons", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "21"}, + {"CRLReason", 1610874901, 0}, + {"unspecified", 1073741825, "0"}, + {"keyCompromise", 1073741825, "1"}, + {"cACompromise", 1073741825, "2"}, + {"affiliationChanged", 1073741825, "3"}, + {"superseded", 1073741825, "4"}, + {"cessationOfOperation", 1073741825, "5"}, + {"certificateHold", 1073741825, "6"}, + {"removeFromCRL", 1, "8"}, + {"id-ce-certificateIssuer", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "29"}, + {"CertificateIssuer", 1073741826, "GeneralNames"}, + {"id-ce-holdInstructionCode", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "23"}, + {"HoldInstructionCode", 1073741836, 0}, + {"holdInstruction", 1879048204, 0}, + {"joint-iso-itu-t", 1073741825, "2"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9cm", 1073741825, "10040"}, + {0, 1, "2"}, + {"id-holdinstruction-none", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "1"}, + {"id-holdinstruction-callissuer", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "2"}, + {"id-holdinstruction-reject", 1879048204, 0}, + {0, 1073741825, "holdInstruction"}, + {0, 1, "3"}, + {"id-ce-invalidityDate", 1879048204, 0}, + {0, 1073741825, "id-ce"}, + {0, 1, "24"}, + {"InvalidityDate", 1082130449, 0}, + {"VisibleString", 1610620935, 0}, + {0, 4360, "26"}, + {"NumericString", 1610620935, 0}, + {0, 4360, "18"}, + {"IA5String", 1610620935, 0}, + {0, 4360, "22"}, + {"TeletexString", 1610620935, 0}, + {0, 4360, "20"}, + {"PrintableString", 1610620935, 0}, + {0, 4360, "19"}, + {"UniversalString", 1610620935, 0}, + {0, 4360, "28"}, + {"BMPString", 1610620935, 0}, + {0, 4360, "30"}, + {"UTF8String", 1610620935, 0}, + {0, 4360, "12"}, + {"id-pkix", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"identified-organization", 1073741825, "3"}, + {"dod", 1073741825, "6"}, + {"internet", 1073741825, "1"}, + {"security", 1073741825, "5"}, + {"mechanisms", 1073741825, "5"}, + {"pkix", 1, "7"}, + {"id-pe", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "1"}, + {"id-qt", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "2"}, + {"id-kp", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "3"}, + {"id-ad", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "48"}, + {"id-qt-cps", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "1"}, + {"id-qt-unotice", 1879048204, 0}, + {0, 1073741825, "id-qt"}, + {0, 1, "2"}, + {"id-ad-ocsp", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "1"}, + {"id-ad-caIssuers", 1879048204, 0}, + {0, 1073741825, "id-ad"}, + {0, 1, "2"}, + {"Attribute", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"values", 536870927, 0}, + {0, 2, "AttributeValue"}, + {"AttributeType", 1073741836, 0}, + {"AttributeValue", 1614807053, 0}, + {"type", 1, 0}, + {"AttributeTypeAndValue", 1610612741, 0}, + {"type", 1073741826, "AttributeType"}, + {"value", 2, "AttributeValue"}, + {"id-at", 1879048204, 0}, + {"joint-iso-ccitt", 1073741825, "2"}, + {"ds", 1073741825, "5"}, + {0, 1, "4"}, + {"id-at-initials", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "43"}, + {"X520initials", 1073741826, "DirectoryString"}, + {"id-at-generationQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "44"}, + {"X520generationQualifier", 1073741826, "DirectoryString"}, + {"id-at-surname", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "4"}, + {"X520surName", 1073741826, "DirectoryString"}, + {"id-at-givenName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "42"}, + {"X520givenName", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-commonName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "3"}, + {"X520CommonName", 1073741826, "DirectoryString"}, + {"id-at-localityName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "7"}, + {"X520LocalityName", 1073741826, "DirectoryString"}, + {"id-at-stateOrProvinceName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "8"}, + {"X520StateOrProvinceName", 1073741826, "DirectoryString"}, + {"id-at-organizationName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "10"}, + {"X520OrganizationName", 1073741826, "DirectoryString"}, + {"id-at-organizationalUnitName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "11"}, + {"X520OrganizationalUnitName", 1073741826, "DirectoryString"}, + {"id-at-title", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "12"}, + {"X520Title", 1073741826, "DirectoryString"}, + {"id-at-description", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "13"}, + {"X520Description", 1073741826, "DirectoryString"}, + {"id-at-dnQualifier", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "46"}, + {"X520dnQualifier", 1073741826, "PrintableString"}, + {"id-at-countryName", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "6"}, + {"X520countryName", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-at-serialNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "5"}, + {"X520serialNumber", 1073741826, "PrintableString"}, + {"id-at-telephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "20"}, + {"X520telephoneNumber", 1073741826, "PrintableString"}, + {"id-at-facsimileTelephoneNumber", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "23"}, + {"X520facsimileTelephoneNumber", 1073741826, "PrintableString"}, + {"id-at-pseudonym", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "65"}, + {"X520pseudonym", 1073741826, "DirectoryString"}, + {"id-at-name", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "41"}, + {"X520name", 1073741826, "DirectoryString"}, + {"id-at-streetAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "9"}, + {"X520streetAddress", 1073741826, "DirectoryString"}, + {"id-at-postalAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "id-at"}, + {0, 1, "16"}, + {"X520postalAddress", 1073741826, "PostalAddress"}, + {"PostalAddress", 1610612747, 0}, + {0, 2, "DirectoryString"}, + {"pkcs", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1, "1"}, + {"pkcs-9", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "9"}, + {"emailAddress", 1880096780, "AttributeType"}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "1"}, + {"Pkcs9email", 1612709890, "IA5String"}, + {"ub-emailaddress-length", 524298, "1"}, + {"Name", 1610612754, 0}, + {"rdnSequence", 2, "RDNSequence"}, + {"RDNSequence", 1610612747, 0}, + {0, 2, "RelativeDistinguishedName"}, + {"DistinguishedName", 1073741826, "RDNSequence"}, + {"RelativeDistinguishedName", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "AttributeTypeAndValue"}, + {"Certificate", 1610612741, 0}, + {"tbsCertificate", 1073741826, "TBSCertificate"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertificate", 1610612741, 0}, + {"version", 1610653698, "Version"}, + {0, 1073741833, "v1"}, + {0, 2056, "0"}, + {"serialNumber", 1073741826, "CertificateSerialNumber"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"validity", 1073741826, "Validity"}, + {"subject", 1073741826, "Name"}, + {"subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"issuerUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "1"}, + {"subjectUniqueID", 1610637314, "UniqueIdentifier"}, + {0, 4104, "2"}, + {"extensions", 536895490, "Extensions"}, + {0, 2056, "3"}, + {"Version", 1610874883, 0}, + {"v1", 1073741825, "0"}, + {"v2", 1073741825, "1"}, + {"v3", 1, "2"}, + {"CertificateSerialNumber", 1073741827, 0}, + {"Validity", 1610612741, 0}, + {"notBefore", 1073741826, "Time"}, + {"notAfter", 2, "Time"}, + {"Time", 1610612754, 0}, + {"utcTime", 1090519057, 0}, + {"generalTime", 8388625, 0}, + {"UniqueIdentifier", 1073741830, 0}, + {"SubjectPublicKeyInfo", 1610612741, 0}, + {"algorithm", 1073741826, "AlgorithmIdentifier"}, + {"subjectPublicKey", 6, 0}, + {"Extensions", 1612709899, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Extension"}, + {"Extension", 1610612741, 0}, + {"extnID", 1073741836, 0}, + {"critical", 1610645508, 0}, + {0, 131081, 0}, + {"extnValue", 7, 0}, + {"CertificateList", 1610612741, 0}, + {"tbsCertList", 1073741826, "TBSCertList"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"TBSCertList", 1610612741, 0}, + {"version", 1073758210, "Version"}, + {"signature", 1073741826, "AlgorithmIdentifier"}, + {"issuer", 1073741826, "Name"}, + {"thisUpdate", 1073741826, "Time"}, + {"nextUpdate", 1073758210, "Time"}, + {"revokedCertificates", 1610629131, 0}, + {0, 536870917, 0}, + {"userCertificate", 1073741826, "CertificateSerialNumber"}, + {"revocationDate", 1073741826, "Time"}, + {"crlEntryExtensions", 16386, "Extensions"}, + {"crlExtensions", 536895490, "Extensions"}, + {0, 2056, "0"}, + {"AlgorithmIdentifier", 1610612741, 0}, + {"algorithm", 1073741836, 0}, + {"parameters", 541081613, 0}, + {"algorithm", 1, 0}, + {"pkcs-1", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "1"}, + {"rsaEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "1"}, + {"md2WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "2"}, + {"md5WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "4"}, + {"sha1WithRSAEncryption", 1879048204, 0}, + {0, 1073741825, "pkcs-1"}, + {0, 1, "5"}, + {"id-dsa-with-sha1", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "3"}, + {"Dss-Sig-Value", 1610612741, 0}, + {"r", 1073741827, 0}, + {"s", 3, 0}, + {"dhpublicnumber", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"ansi-x942", 1073741825, "10046"}, + {"number-type", 1073741825, "2"}, + {0, 1, "1"}, + {"DomainParameters", 1610612741, 0}, + {"p", 1073741827, 0}, + {"g", 1073741827, 0}, + {"q", 1073741827, 0}, + {"j", 1073758211, 0}, + {"validationParms", 16386, "ValidationParms"}, + {"ValidationParms", 1610612741, 0}, + {"seed", 1073741830, 0}, + {"pgenCounter", 3, 0}, + {"id-dsa", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"x9-57", 1073741825, "10040"}, + {"x9algorithm", 1073741825, "4"}, + {0, 1, "1"}, + {"Dss-Parms", 1610612741, 0}, + {"p", 1073741827, 0}, + {"q", 1073741827, 0}, + {"g", 3, 0}, + {"ORAddress", 1610612741, 0}, + {"built-in-standard-attributes", 1073741826, "BuiltInStandardAttributes"}, + {"built-in-domain-defined-attributes", 1073758210, + "BuiltInDomainDefinedAttributes"}, + {"extension-attributes", 16386, "ExtensionAttributes"}, + {"BuiltInStandardAttributes", 1610612741, 0}, + {"country-name", 1073758210, "CountryName"}, + {"administration-domain-name", 1073758210, "AdministrationDomainName"}, + {"network-address", 1610637314, "NetworkAddress"}, + {0, 2056, "0"}, + {"terminal-identifier", 1610637314, "TerminalIdentifier"}, + {0, 2056, "1"}, + {"private-domain-name", 1610637314, "PrivateDomainName"}, + {0, 2056, "2"}, + {"organization-name", 1610637314, "OrganizationName"}, + {0, 2056, "3"}, + {"numeric-user-identifier", 1610637314, "NumericUserIdentifier"}, + {0, 2056, "4"}, + {"personal-name", 1610637314, "PersonalName"}, + {0, 2056, "5"}, + {"organizational-unit-names", 536895490, "OrganizationalUnitNames"}, + {0, 2056, "6"}, + {"CountryName", 1610620946, 0}, + {0, 1073746952, "1"}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"AdministrationDomainName", 1610620946, 0}, + {0, 1073744904, "2"}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "0"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "0"}, + {"NetworkAddress", 1073741826, "X121Address"}, + {"X121Address", 1612709890, "NumericString"}, + {"ub-x121-address-length", 524298, "1"}, + {"TerminalIdentifier", 1612709890, "PrintableString"}, + {"ub-terminal-id-length", 524298, "1"}, + {"PrivateDomainName", 1610612754, 0}, + {"numeric", 1612709890, "NumericString"}, + {"ub-domain-name-length", 524298, "1"}, + {"printable", 538968066, "PrintableString"}, + {"ub-domain-name-length", 524298, "1"}, + {"OrganizationName", 1612709890, "PrintableString"}, + {"ub-organization-name-length", 524298, "1"}, + {"NumericUserIdentifier", 1612709890, "NumericString"}, + {"ub-numeric-user-id-length", 524298, "1"}, + {"PersonalName", 1610612750, 0}, + {"surname", 1814044674, "PrintableString"}, + {0, 1073745928, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "PrintableString"}, + {0, 1073745928, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "PrintableString"}, + {0, 1073745928, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "PrintableString"}, + {0, 1073745928, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"OrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "OrganizationalUnitName"}, + {"OrganizationalUnitName", 1612709890, "PrintableString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"BuiltInDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "BuiltInDomainDefinedAttribute"}, + {"BuiltInDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "PrintableString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "PrintableString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ExtensionAttributes", 1612709903, 0}, + {"ub-extension-attributes", 1074266122, "1"}, + {0, 2, "ExtensionAttribute"}, + {"ExtensionAttribute", 1610612741, 0}, + {"extension-attribute-type", 1611145219, 0}, + {0, 1073743880, "0"}, + {"0", 10, "ub-extension-attributes"}, + {"extension-attribute-value", 541073421, 0}, + {0, 1073743880, "1"}, + {"extension-attribute-type", 1, 0}, + {"common-name", 1342177283, "1"}, + {"CommonName", 1612709890, "PrintableString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-common-name", 1342177283, "2"}, + {"TeletexCommonName", 1612709890, "TeletexString"}, + {"ub-common-name-length", 524298, "1"}, + {"teletex-organization-name", 1342177283, "3"}, + {"TeletexOrganizationName", 1612709890, "TeletexString"}, + {"ub-organization-name-length", 524298, "1"}, + {"teletex-personal-name", 1342177283, "4"}, + {"TeletexPersonalName", 1610612750, 0}, + {"surname", 1814044674, "TeletexString"}, + {0, 1073743880, "0"}, + {"ub-surname-length", 524298, "1"}, + {"given-name", 1814061058, "TeletexString"}, + {0, 1073743880, "1"}, + {"ub-given-name-length", 524298, "1"}, + {"initials", 1814061058, "TeletexString"}, + {0, 1073743880, "2"}, + {"ub-initials-length", 524298, "1"}, + {"generation-qualifier", 740319234, "TeletexString"}, + {0, 1073743880, "3"}, + {"ub-generation-qualifier-length", 524298, "1"}, + {"teletex-organizational-unit-names", 1342177283, "5"}, + {"TeletexOrganizationalUnitNames", 1612709899, 0}, + {"ub-organizational-units", 1074266122, "1"}, + {0, 2, "TeletexOrganizationalUnitName"}, + {"TeletexOrganizationalUnitName", 1612709890, "TeletexString"}, + {"ub-organizational-unit-name-length", 524298, "1"}, + {"pds-name", 1342177283, "7"}, + {"PDSName", 1612709890, "PrintableString"}, + {"ub-pds-name-length", 524298, "1"}, + {"physical-delivery-country-name", 1342177283, "8"}, + {"PhysicalDeliveryCountryName", 1610612754, 0}, + {"x121-dcc-code", 1612709890, "NumericString"}, + {0, 1048586, "ub-country-name-numeric-length"}, + {"iso-3166-alpha2-code", 538968066, "PrintableString"}, + {0, 1048586, "ub-country-name-alpha-length"}, + {"postal-code", 1342177283, "9"}, + {"PostalCode", 1610612754, 0}, + {"numeric-code", 1612709890, "NumericString"}, + {"ub-postal-code-length", 524298, "1"}, + {"printable-code", 538968066, "PrintableString"}, + {"ub-postal-code-length", 524298, "1"}, + {"physical-delivery-office-name", 1342177283, "10"}, + {"PhysicalDeliveryOfficeName", 1073741826, "PDSParameter"}, + {"physical-delivery-office-number", 1342177283, "11"}, + {"PhysicalDeliveryOfficeNumber", 1073741826, "PDSParameter"}, + {"extension-OR-address-components", 1342177283, "12"}, + {"ExtensionORAddressComponents", 1073741826, "PDSParameter"}, + {"physical-delivery-personal-name", 1342177283, "13"}, + {"PhysicalDeliveryPersonalName", 1073741826, "PDSParameter"}, + {"physical-delivery-organization-name", 1342177283, "14"}, + {"PhysicalDeliveryOrganizationName", 1073741826, "PDSParameter"}, + {"extension-physical-delivery-address-components", 1342177283, "15"}, + {"ExtensionPhysicalDeliveryAddressComponents", 1073741826, "PDSParameter"}, + {"unformatted-postal-address", 1342177283, "16"}, + {"UnformattedPostalAddress", 1610612750, 0}, + {"printable-address", 1814052875, 0}, + {"ub-pds-physical-address-lines", 1074266122, "1"}, + {0, 538968066, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-unformatted-address-length", 524298, "1"}, + {"street-address", 1342177283, "17"}, + {"StreetAddress", 1073741826, "PDSParameter"}, + {"post-office-box-address", 1342177283, "18"}, + {"PostOfficeBoxAddress", 1073741826, "PDSParameter"}, + {"poste-restante-address", 1342177283, "19"}, + {"PosteRestanteAddress", 1073741826, "PDSParameter"}, + {"unique-postal-name", 1342177283, "20"}, + {"UniquePostalName", 1073741826, "PDSParameter"}, + {"local-postal-attributes", 1342177283, "21"}, + {"LocalPostalAttributes", 1073741826, "PDSParameter"}, + {"PDSParameter", 1610612750, 0}, + {"printable-string", 1814052866, "PrintableString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"teletex-string", 740311042, "TeletexString"}, + {"ub-pds-parameter-length", 524298, "1"}, + {"extended-network-address", 1342177283, "22"}, + {"ExtendedNetworkAddress", 1610612754, 0}, + {"e163-4-address", 1610612741, 0}, + {"number", 1612718082, "NumericString"}, + {0, 1073743880, "0"}, + {"ub-e163-4-number-length", 524298, "1"}, + {"sub-address", 538992642, "NumericString"}, + {0, 1073743880, "1"}, + {"ub-e163-4-sub-address-length", 524298, "1"}, + {"psap-address", 536879106, "PresentationAddress"}, + {0, 2056, "0"}, + {"PresentationAddress", 1610612741, 0}, + {"pSelector", 1610637319, 0}, + {0, 2056, "0"}, + {"sSelector", 1610637319, 0}, + {0, 2056, "1"}, + {"tSelector", 1610637319, 0}, + {0, 2056, "2"}, + {"nAddresses", 538976271, 0}, + {0, 1073743880, "3"}, + {"MAX", 1074266122, "1"}, + {0, 7, 0}, + {"terminal-type", 1342177283, "23"}, + {"TerminalType", 1610874883, 0}, + {"telex", 1073741825, "3"}, + {"teletex", 1073741825, "4"}, + {"g3-facsimile", 1073741825, "5"}, + {"g4-facsimile", 1073741825, "6"}, + {"ia5-terminal", 1073741825, "7"}, + {"videotex", 1, "8"}, + {"teletex-domain-defined-attributes", 1342177283, "6"}, + {"TeletexDomainDefinedAttributes", 1612709899, 0}, + {"ub-domain-defined-attributes", 1074266122, "1"}, + {0, 2, "TeletexDomainDefinedAttribute"}, + {"TeletexDomainDefinedAttribute", 1610612741, 0}, + {"type", 1612709890, "TeletexString"}, + {"ub-domain-defined-attribute-type-length", 524298, "1"}, + {"value", 538968066, "TeletexString"}, + {"ub-domain-defined-attribute-value-length", 524298, "1"}, + {"ub-name", 1342177283, "32768"}, + {"ub-common-name", 1342177283, "64"}, + {"ub-locality-name", 1342177283, "128"}, + {"ub-state-name", 1342177283, "128"}, + {"ub-organization-name", 1342177283, "64"}, + {"ub-organizational-unit-name", 1342177283, "64"}, + {"ub-title", 1342177283, "64"}, + {"ub-match", 1342177283, "128"}, + {"ub-emailaddress-length", 1342177283, "128"}, + {"ub-common-name-length", 1342177283, "64"}, + {"ub-country-name-alpha-length", 1342177283, "2"}, + {"ub-country-name-numeric-length", 1342177283, "3"}, + {"ub-domain-defined-attributes", 1342177283, "4"}, + {"ub-domain-defined-attribute-type-length", 1342177283, "8"}, + {"ub-domain-defined-attribute-value-length", 1342177283, "128"}, + {"ub-domain-name-length", 1342177283, "16"}, + {"ub-extension-attributes", 1342177283, "256"}, + {"ub-e163-4-number-length", 1342177283, "15"}, + {"ub-e163-4-sub-address-length", 1342177283, "40"}, + {"ub-generation-qualifier-length", 1342177283, "3"}, + {"ub-given-name-length", 1342177283, "16"}, + {"ub-initials-length", 1342177283, "5"}, + {"ub-integer-options", 1342177283, "256"}, + {"ub-numeric-user-id-length", 1342177283, "32"}, + {"ub-organization-name-length", 1342177283, "64"}, + {"ub-organizational-unit-name-length", 1342177283, "32"}, + {"ub-organizational-units", 1342177283, "4"}, + {"ub-pds-name-length", 1342177283, "16"}, + {"ub-pds-parameter-length", 1342177283, "30"}, + {"ub-pds-physical-address-lines", 1342177283, "6"}, + {"ub-postal-code-length", 1342177283, "16"}, + {"ub-surname-length", 1342177283, "40"}, + {"ub-terminal-id-length", 1342177283, "24"}, + {"ub-unformatted-address-length", 1342177283, "180"}, + {"ub-x121-address-length", 1342177283, "16"}, + {"pkcs-7-ContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"content", 541073421, 0}, + {0, 1073743880, "0"}, + {"contentType", 1, 0}, + {"pkcs-7-DigestInfo", 1610612741, 0}, + {"digestAlgorithm", 1073741826, "pkcs-7-DigestAlgorithmIdentifier"}, + {"digest", 2, "pkcs-7-Digest"}, + {"pkcs-7-Digest", 1073741831, 0}, + {"pkcs-7-ContentType", 1073741836, 0}, + {"pkcs-7-SignedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + {"encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + {"certificates", 1610637314, "pkcs-7-CertificateSet"}, + {0, 4104, "0"}, + {"crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + {0, 4104, "1"}, + {"signerInfos", 2, "pkcs-7-SignerInfos"}, + {"pkcs-7-CMSVersion", 1610874883, 0}, + {"v0", 1073741825, "0"}, + {"v1", 1073741825, "1"}, + {"v2", 1073741825, "2"}, + {"v3", 1073741825, "3"}, + {"v4", 1, "4"}, + {"pkcs-7-DigestAlgorithmIdentifiers", 1610612751, 0}, + {0, 2, "pkcs-7-DigestAlgorithmIdentifier"}, + {"pkcs-7-DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + {"pkcs-7-EncapsulatedContentInfo", 1610612741, 0}, + {"eContentType", 1073741826, "pkcs-7-ContentType"}, + {"eContent", 536895495, 0}, + {0, 2056, "0"}, + {"pkcs-7-CertificateRevocationLists", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-7-CertificateChoices", 1610612754, 0}, + {"certificate", 13, 0}, + {"pkcs-7-CertificateSet", 1610612751, 0}, + {0, 2, "pkcs-7-CertificateChoices"}, + {"pkcs-7-SignerInfos", 1610612751, 0}, + {0, 13, 0}, + {"pkcs-10-CertificationRequestInfo", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v1", 1, "0"}, + {"subject", 1073741826, "Name"}, + {"subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + {"attributes", 536879106, "Attributes"}, + {0, 4104, "0"}, + {"Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-10-CertificationRequest", 1610612741, 0}, + {"certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + {"signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"signature", 6, 0}, + {"pkcs-9-ub-challengePassword", 1342177283, "255"}, + {"pkcs-9-certTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "22"}, + {"pkcs-9-crlTypes", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "23"}, + {"pkcs-9-at-challengePassword", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "7"}, + {"pkcs-9-challengePassword", 1610612754, 0}, + {"printableString", 1612709890, "PrintableString"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"utf8String", 538968066, "UTF8String"}, + {"pkcs-9-ub-challengePassword", 524298, "1"}, + {"pkcs-9-at-localKeyId", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "21"}, + {"pkcs-9-localKeyId", 1073741831, 0}, + {"pkcs-9-at-friendlyName", 1879048204, 0}, + {0, 1073741825, "pkcs-9"}, + {0, 1, "20"}, + {"pkcs-9-friendlyName", 1612709890, "BMPString"}, + {"255", 524298, "1"}, + {"pkcs-8-PrivateKeyInfo", 1610612741, 0}, + {"version", 1073741826, "pkcs-8-Version"}, + {"privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"privateKey", 1073741826, "pkcs-8-PrivateKey"}, + {"attributes", 536895490, "Attributes"}, + {0, 4104, "0"}, + {"pkcs-8-Version", 1610874883, 0}, + {"v1", 1, "0"}, + {"pkcs-8-PrivateKey", 1073741831, 0}, + {"pkcs-8-Attributes", 1610612751, 0}, + {0, 2, "Attribute"}, + {"pkcs-8-EncryptedPrivateKeyInfo", 1610612741, 0}, + {"encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + {"encryptedData", 2, "pkcs-8-EncryptedData"}, + {"pkcs-8-EncryptedData", 1073741831, 0}, + {"pkcs-5", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "5"}, + {"pkcs-5-encryptionAlgorithm", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {0, 1, "3"}, + {"pkcs-5-des-EDE3-CBC", 1879048204, 0}, + {0, 1073741825, "pkcs-5-encryptionAlgorithm"}, + {0, 1, "7"}, + {"pkcs-5-des-EDE3-CBC-params", 1612709895, 0}, + {0, 1048586, "8"}, + {"pkcs-5-id-PBES2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "13"}, + {"pkcs-5-PBES2-params", 1610612741, 0}, + {"keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + {"encryptionScheme", 2, "AlgorithmIdentifier"}, + {"pkcs-5-id-PBKDF2", 1879048204, 0}, + {0, 1073741825, "pkcs-5"}, + {0, 1, "12"}, + {"pkcs-5-PBKDF2-params", 1610612741, 0}, + {"salt", 1610612754, 0}, + {"specified", 1073741831, 0}, + {"otherSource", 2, "AlgorithmIdentifier"}, + {"iterationCount", 1611137027, 0}, + {"1", 10, "MAX"}, + {"keyLength", 1611153411, 0}, + {"1", 10, "MAX"}, + {"prf", 16386, "AlgorithmIdentifier"}, + {"pkcs-12", 1879048204, 0}, + {0, 1073741825, "pkcs"}, + {0, 1, "12"}, + {"pkcs-12-PFX", 1610612741, 0}, + {"version", 1610874883, 0}, + {"v3", 1, "3"}, + {"authSafe", 1073741826, "pkcs-7-ContentInfo"}, + {"macData", 16386, "pkcs-12-MacData"}, + {"pkcs-12-PbeParams", 1610612741, 0}, + {"salt", 1073741831, 0}, + {"iterations", 3, 0}, + {"pkcs-12-MacData", 1610612741, 0}, + {"mac", 1073741826, "pkcs-7-DigestInfo"}, + {"macSalt", 1073741831, 0}, + {"iterations", 536903683, 0}, + {0, 9, "1"}, + {"pkcs-12-AuthenticatedSafe", 1610612747, 0}, + {0, 2, "pkcs-7-ContentInfo"}, + {"pkcs-12-SafeContents", 1610612747, 0}, + {0, 2, "pkcs-12-SafeBag"}, + {"pkcs-12-SafeBag", 1610612741, 0}, + {"bagId", 1073741836, 0}, + {"bagValue", 1614815245, 0}, + {0, 1073743880, "0"}, + {"badId", 1, 0}, + {"bagAttributes", 536887311, 0}, + {0, 2, "pkcs-12-PKCS12Attribute"}, + {"pkcs-12-bagtypes", 1879048204, 0}, + {0, 1073741825, "pkcs-12"}, + {0, 1073741825, "10"}, + {0, 1, "1"}, + {"pkcs-12-keyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "1"}, + {"pkcs-12-pkcs8ShroudedKeyBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "2"}, + {"pkcs-12-certBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "3"}, + {"pkcs-12-crlBag", 1879048204, 0}, + {0, 1073741825, "pkcs-12-bagtypes"}, + {0, 1, "4"}, + {"pkcs-12-KeyBag", 1073741826, "pkcs-8-PrivateKeyInfo"}, + {"pkcs-12-PKCS8ShroudedKeyBag", 1073741826, "pkcs-8-EncryptedPrivateKeyInfo"}, + {"pkcs-12-CertBag", 1610612741, 0}, + {"certId", 1073741836, 0}, + {"certValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"certId", 1, 0}, + {"pkcs-12-CRLBag", 1610612741, 0}, + {"crlId", 1073741836, 0}, + {"crlValue", 541073421, 0}, + {0, 1073743880, "0"}, + {"crlId", 1, 0}, + {"pkcs-12-PKCS12Attribute", 1073741826, "Attribute"}, + {"pkcs-7-data", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "1"}, + {"pkcs-7-encryptedData", 1879048204, 0}, + {"iso", 1073741825, "1"}, + {"member-body", 1073741825, "2"}, + {"us", 1073741825, "840"}, + {"rsadsi", 1073741825, "113549"}, + {"pkcs", 1073741825, "1"}, + {"pkcs7", 1073741825, "7"}, + {0, 1, "6"}, + {"pkcs-7-Data", 1073741831, 0}, + {"pkcs-7-EncryptedData", 1610612741, 0}, + {"version", 1073741826, "pkcs-7-CMSVersion"}, + {"encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + {"unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + {0, 4104, "1"}, + {"pkcs-7-EncryptedContentInfo", 1610612741, 0}, + {"contentType", 1073741826, "pkcs-7-ContentType"}, + {"contentEncryptionAlgorithm", 1073741826, + "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + {"encryptedContent", 536895490, "pkcs-7-EncryptedContent"}, + {0, 4104, "0"}, + {"pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, + "AlgorithmIdentifier"}, + {"pkcs-7-EncryptedContent", 1073741831, 0}, + {"pkcs-7-UnprotectedAttributes", 1612709903, 0}, + {"MAX", 1074266122, "1"}, + {0, 2, "Attribute"}, + {"id-at-ldap-DC", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "25"}, + {"ldap-DC", 1073741826, "IA5String"}, + {"id-at-ldap-UID", 1880096780, "AttributeType"}, + {0, 1073741825, "0"}, + {0, 1073741825, "9"}, + {0, 1073741825, "2342"}, + {0, 1073741825, "19200300"}, + {0, 1073741825, "100"}, + {0, 1073741825, "1"}, + {0, 1, "1"}, + {"ldap-UID", 1073741826, "DirectoryString"}, + {"id-pda", 1879048204, 0}, + {0, 1073741825, "id-pkix"}, + {0, 1, "9"}, + {"id-pda-dateOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "1"}, + {"DateOfBirth", 1082130449, 0}, + {"id-pda-placeOfBirth", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "2"}, + {"PlaceOfBirth", 1073741826, "DirectoryString"}, + {"id-pda-gender", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "3"}, + {"Gender", 1612709890, "PrintableString"}, + {0, 1048586, "1"}, + {"id-pda-countryOfCitizenship", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "4"}, + {"CountryOfCitizenship", 1612709890, "PrintableString"}, + {0, 1048586, "2"}, + {"id-pda-countryOfResidence", 1880096780, "AttributeType"}, + {0, 1073741825, "id-pda"}, + {0, 1, "5"}, + {"CountryOfResidence", 538968066, "PrintableString"}, + {0, 1048586, "2"}, + {0, 0, 0} +}; 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 04b5f4e3d0..08240594bb 100644 --- a/tests/unit/ptimer-test.c +++ b/tests/unit/ptimer-test.c @@ -763,6 +763,33 @@ static void check_oneshot_with_load_0(gconstpointer arg) ptimer_free(ptimer); } +static void check_freq_more_than_1000M(gconstpointer arg) +{ + const uint8_t *policy = arg; + ptimer_state *ptimer = ptimer_init(ptimer_trigger, NULL, *policy); + bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + triggered = false; + + ptimer_transaction_begin(ptimer); + ptimer_set_freq(ptimer, 2000000000); + ptimer_set_limit(ptimer, 8, 1); + ptimer_run(ptimer, 1); + ptimer_transaction_commit(ptimer); + + qemu_clock_step(3); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_round_down ? 3 : 2); + g_assert_false(triggered); + + qemu_clock_step(1); + + g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); + g_assert_true(triggered); + + ptimer_free(ptimer); +} + static void add_ptimer_tests(uint8_t policy) { char policy_name[256] = ""; @@ -857,6 +884,12 @@ static void add_ptimer_tests(uint8_t policy) policy_name), g_memdup2(&policy, 1), check_oneshot_with_load_0, g_free); g_free(tmp); + + g_test_add_data_func_full( + tmp = g_strdup_printf("/ptimer/freq_more_than_1000M policy=%s", + policy_name), + g_memdup2(&policy, 1), check_freq_more_than_1000M, g_free); + g_free(tmp); } static void add_all_ptimer_policies_comb_tests(void) 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 eecadf3a3c..f3439cc4e5 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -160,7 +160,6 @@ void socket_check_afunix_support(bool *has_afunix) int fd; fd = socket(PF_UNIX, SOCK_STREAM, 0); - closesocket(fd); #ifdef _WIN32 *has_afunix = (fd != (int)INVALID_SOCKET); @@ -168,5 +167,8 @@ void socket_check_afunix_support(bool *has_afunix) *has_afunix = (fd >= 0); #endif + if (*has_afunix) { + close(fd); + } return; } 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 09dc4a4891..c112d5b189 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,8 +722,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, BlockJob *job; TestBlockJob *tjob; IOThread *iothread = NULL; - AioContext *ctx; - int ret; + int ret = -1; src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, &error_abort); @@ -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: @@ -930,7 +784,6 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, tjob->prepare_ret = -EIO; break; } - aio_context_release(ctx); job_start(&job->job); @@ -1021,12 +874,10 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, } g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); - aio_context_acquire(ctx); 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); @@ -1045,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) @@ -1061,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); @@ -1078,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); @@ -1094,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); @@ -1111,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; @@ -1125,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); @@ -1172,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; @@ -1212,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 */ @@ -1221,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); @@ -1263,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(); @@ -1305,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; @@ -1317,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); @@ -1371,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; @@ -1384,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); @@ -1411,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, @@ -1420,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); @@ -1437,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); @@ -1457,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); @@ -1506,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); @@ -1539,16 +1396,11 @@ static void test_set_aio_context(void) bdrv_drained_begin(bs); bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort); - - aio_context_acquire(ctx_a); bdrv_drained_end(bs); bdrv_drained_begin(bs); bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort); - aio_context_release(ctx_a); - aio_context_acquire(ctx_b); bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort); - aio_context_release(ctx_b); bdrv_drained_end(bs); bdrv_unref(bs); @@ -1693,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]); @@ -1738,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) { /* @@ -1827,6 +1681,7 @@ 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] */ @@ -1834,6 +1689,7 @@ static void test_drop_intermediate_poll(void) &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); @@ -1847,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); @@ -1856,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; @@ -1881,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; @@ -1916,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); } } } @@ -1974,8 +1861,8 @@ static BlockDriver bdrv_replace_test = { .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, }; @@ -2049,8 +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); + 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); @@ -2078,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) { @@ -2172,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 c522591531..cafc023db4 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -98,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. @@ -114,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 @@ -137,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_DATA, &error_abort); + bdrv_graph_wrunlock(); ret = bdrv_append(filter, bs, NULL); g_assert_cmpint(ret, <, 0); @@ -202,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); @@ -226,12 +233,18 @@ 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_graph_wrlock(); bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); @@ -243,7 +256,12 @@ static void test_parallel_exclusive_write(void) &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); } @@ -345,6 +363,7 @@ static void test_parallel_perm_update(void) */ bdrv_ref(base); + bdrv_graph_wrlock(); bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, &error_abort); c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds, @@ -357,9 +376,13 @@ static void test_parallel_perm_update(void) 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 */ 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); @@ -379,6 +402,7 @@ static void test_parallel_perm_update(void) assert(c_fl1->perm & BLK_PERM_WRITE); assert(!(c_fl2->perm & BLK_PERM_WRITE)); + bdrv_graph_rdunlock_main_loop(); bdrv_unref(top); } @@ -406,9 +430,11 @@ static void test_append_greedy_filter(void) BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl = exclusive_writer_node("fl1"); + 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 8ca5adec5e..20ed54f570 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" @@ -311,7 +312,8 @@ static void test_sync_op_blk_truncate(BlockBackend *blk) g_assert_cmpint(ret, ==, -EINVAL); } -static void test_sync_op_block_status(BdrvChild *c) +/* 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; @@ -381,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); } @@ -466,14 +471,18 @@ 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); if (t->fn) { t->fn(c); } @@ -481,7 +490,6 @@ static void test_sync_op(const void *opaque) t->blkfn(blk); } blk_set_aio_context(blk, qemu_get_aio_context(), &error_abort); - aio_context_release(ctx); bdrv_unref(bs); blk_unref(blk); @@ -566,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) { @@ -585,9 +591,7 @@ static void test_attach_blockjob(void) WITH_JOB_LOCK_GUARD() { job_complete_sync_locked(&tjob->common.job, &error_abort); } - 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); @@ -644,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); @@ -722,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); @@ -745,7 +745,7 @@ static void test_propagate_mirror(void) AioContext *main_ctx = qemu_get_aio_context(); BlockDriverState *src, *target, *filter; BlockBackend *blk; - Job *job; + Job *job = NULL; Error *local_err = NULL; /* Create src and target*/ @@ -759,6 +759,7 @@ static void test_propagate_mirror(void) BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, &error_abort); + WITH_JOB_LOCK_GUARD() { job = job_get_locked("job0"); } @@ -772,9 +773,7 @@ static void test_propagate_mirror(void) g_assert(job->aio_context == ctx); /* Change the AioContext of target */ - aio_context_acquire(ctx); bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort); - aio_context_release(ctx); 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); @@ -792,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_change_aio_context(target, ctx, NULL, &error_abort); - aio_context_release(ctx); g_assert(blk_get_aio_context(blk) == ctx); g_assert(bdrv_get_aio_context(src) == ctx); @@ -804,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_change_aio_context(target, main_ctx, NULL, &error_abort); - aio_context_release(ctx); blk_unref(blk); bdrv_unref(src); @@ -832,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); @@ -865,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()); @@ -876,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.c b/tests/unit/test-blockjob.c index c0426bd10c..fe3e0d2d38 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -228,7 +228,6 @@ static void cancel_common(CancelJob *s) BlockJob *job = &s->common; BlockBackend *blk = s->blk; JobStatus sts = job->job.status; - AioContext *ctx = job->job.aio_context; job_cancel_sync(&job->job, true); WITH_JOB_LOCK_GUARD() { @@ -240,9 +239,7 @@ static void cancel_common(CancelJob *s) job_unref_locked(&job->job); } - aio_context_acquire(ctx); destroy_blk(blk); - aio_context_release(ctx); } @@ -391,132 +388,6 @@ static void test_cancel_concluded(void) 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); - } - - 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_locked() 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_locked() 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_is(job, JOB_STATUS_CREATED); - - /* Wait for the job to become READY */ - job_start(job); - /* - * Here we are waiting for the status to change, so don't bother - * protecting the read every time. - */ - AIO_WAIT_WHILE_UNLOCKED(ctx, job->status != JOB_STATUS_READY); - - /* Begin the drained section, pausing the job */ - bdrv_drain_all_begin(); - assert_job_status_is(job, 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(); - aio_context_release(ctx); - - WITH_JOB_LOCK_GUARD() { - /* 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_locked(job, &error_abort); - - /* The test is done now, clean up. */ - job_finish_sync_locked(job, NULL, &error_abort); - assert(job->status == JOB_STATUS_PENDING); - - job_finalize_locked(job, &error_abort); - assert(job->status == JOB_STATUS_CONCLUDED); - - job_dismiss_locked(&job, &error_abort); - } - - aio_context_acquire(ctx); - destroy_blk(blk); - aio_context_release(ctx); - iothread_join(iothread); -} - int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -531,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..a6e8753e1c 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -1,6 +1,7 @@ #include "qemu/osdep.h" #include +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/module.h" #include "qemu/option.h" @@ -184,6 +185,21 @@ static void char_mux_test(void) char *data; FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, }; CharBackend chr_be1, chr_be2; + Error *error = NULL; + + /* Create mux and chardev to be immediately removed */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", "128", &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr); + qemu_opts_del(opts); + + /* Remove just created mux and chardev */ + qmp_chardev_remove("mux-label", &error_abort); + qmp_chardev_remove("mux-label-base", &error_abort); opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", 1, &error_abort); @@ -334,7 +350,13 @@ static void char_mux_test(void) g_free(data); qemu_chr_fe_deinit(&chr_be1, false); - qemu_chr_fe_deinit(&chr_be2, true); + + qmp_chardev_remove("mux-label", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'mux-label' is busy"); + error_free(error); + + qemu_chr_fe_deinit(&chr_be2, false); + qmp_chardev_remove("mux-label", &error_abort); } @@ -556,7 +578,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 +1225,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 +1258,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 +1429,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); @@ -1500,18 +1545,18 @@ int main(int argc, char **argv) static CharSocketClientTestConfig client2 ## name = \ { addr, NULL, true, false, char_socket_event }; \ static CharSocketClientTestConfig client3 ## name = \ - { addr, ",reconnect=1", false, false, char_socket_event }; \ + { addr, ",reconnect-ms=1000", false, false, char_socket_event }; \ static CharSocketClientTestConfig client4 ## name = \ - { addr, ",reconnect=1", true, false, char_socket_event }; \ + { addr, ",reconnect-ms=1000", true, false, char_socket_event }; \ static CharSocketClientTestConfig client5 ## name = \ { addr, NULL, false, true, char_socket_event }; \ static CharSocketClientTestConfig client6 ## name = \ { addr, NULL, true, true, char_socket_event }; \ static CharSocketClientTestConfig client7 ## name = \ - { addr, ",reconnect=1", true, false, \ + { addr, ",reconnect-ms=1000", true, false, \ char_socket_event_with_error }; \ static CharSocketClientTestConfig client8 ## name = \ - { addr, ",reconnect=1", false, false, char_socket_event }; \ + { addr, ",reconnect-ms=1000", false, false, char_socket_event };\ g_test_add_data_func("/char/socket/client/mainloop/" # name, \ &client1 ##name, char_socket_client_test); \ g_test_add_data_func("/char/socket/client/wait-conn/" # name, \ @@ -1545,6 +1590,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 e16b80c245..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); } @@ -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-afsplit.c b/tests/unit/test-crypto-afsplit.c index 00a7c180fd..45e9046bf6 100644 --- a/tests/unit/test-crypto-afsplit.c +++ b/tests/unit/test-crypto-afsplit.c @@ -26,7 +26,7 @@ typedef struct QCryptoAFSplitTestData QCryptoAFSplitTestData; struct QCryptoAFSplitTestData { const char *path; - QCryptoHashAlgorithm hash; + QCryptoHashAlgo hash; uint32_t stripes; size_t blocklen; const uint8_t *key; @@ -36,7 +36,7 @@ struct QCryptoAFSplitTestData { static QCryptoAFSplitTestData test_data[] = { { .path = "/crypto/afsplit/sha256/5", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .stripes = 5, .blocklen = 32, .key = (const uint8_t *) @@ -68,7 +68,7 @@ static QCryptoAFSplitTestData test_data[] = { }, { .path = "/crypto/afsplit/sha256/5000", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .stripes = 5000, .blocklen = 16, .key = (const uint8_t *) @@ -77,7 +77,7 @@ static QCryptoAFSplitTestData test_data[] = { }, { .path = "/crypto/afsplit/sha1/1000", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .stripes = 1000, .blocklen = 32, .key = (const uint8_t *) @@ -88,7 +88,7 @@ static QCryptoAFSplitTestData test_data[] = { }, { .path = "/crypto/afsplit/sha256/big", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .stripes = 1000, .blocklen = 64, .key = (const uint8_t *) diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c index 4f1f4214dd..53c2211ba8 100644 --- a/tests/unit/test-crypto-akcipher.c +++ b/tests/unit/test-crypto-akcipher.c @@ -692,7 +692,7 @@ struct QCryptoAkCipherTestData { static QCryptoRSAKeyTestData rsakey_test_data[] = { { .path = "/crypto/akcipher/rsakey-1024-public", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa1024_public_key, .keylen = sizeof(rsa1024_public_key), .is_valid_key = true, @@ -700,7 +700,7 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-1024-private", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa1024_private_key, .keylen = sizeof(rsa1024_private_key), .is_valid_key = true, @@ -708,7 +708,7 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-2048-public", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa2048_public_key, .keylen = sizeof(rsa2048_public_key), .is_valid_key = true, @@ -716,7 +716,7 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-2048-private", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa2048_private_key, .keylen = sizeof(rsa2048_private_key), .is_valid_key = true, @@ -724,56 +724,56 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-public-lack-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_lack_element, .keylen = sizeof(rsa_public_key_lack_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-private-lack-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa_private_key_lack_element, .keylen = sizeof(rsa_private_key_lack_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-empty-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_empty_element, .keylen = sizeof(rsa_public_key_empty_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-private-empty-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa_private_key_empty_element, .keylen = sizeof(rsa_private_key_empty_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-empty-key", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = NULL, .keylen = 0, .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-private-empty-key", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = NULL, .keylen = 0, .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-invalid-length-val", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_invalid_length_val, .keylen = sizeof(rsa_public_key_invalid_length_val), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-extra-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_extra_elem, .keylen = sizeof(rsa_public_key_extra_elem), .is_valid_key = false, @@ -785,9 +785,9 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa1024-raw", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW, }, }, .pub_key = rsa1024_public_key, @@ -805,10 +805,10 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa1024-pkcs1", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, }, }, .pub_key = rsa1024_public_key, @@ -830,9 +830,9 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa2048-raw", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW, }, }, .pub_key = rsa2048_public_key, @@ -850,10 +850,10 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa2048-pkcs1", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, }, }, .pub_key = rsa2048_public_key, @@ -885,12 +885,12 @@ static void test_akcipher(const void *opaque) return; } pub_key = qcrypto_akcipher_new(&data->opt, - QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, data->pub_key, data->pub_key_len, &error_abort); g_assert(pub_key != NULL); priv_key = qcrypto_akcipher_new(&data->opt, - QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, data->priv_key, data->priv_key_len, &error_abort); g_assert(priv_key != NULL); @@ -944,10 +944,10 @@ static void test_rsakey(const void *opaque) { const QCryptoRSAKeyTestData *data = (const QCryptoRSAKeyTestData *)opaque; QCryptoAkCipherOptions opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, } }; g_autoptr(QCryptoAkCipher) key = qcrypto_akcipher_new( diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index b629e240a9..9217b9a2ef 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -39,17 +39,15 @@ #endif static QCryptoBlockCreateOptions qcow_create_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .format = QCRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; static QCryptoBlockOpenOptions qcow_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .format = QCRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -57,9 +55,8 @@ static QCryptoBlockOpenOptions qcow_open_opts = { #ifdef TEST_LUKS static QCryptoBlockOpenOptions luks_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -67,9 +64,8 @@ static QCryptoBlockOpenOptions luks_open_opts = { /* Creation with all default values */ static QCryptoBlockCreateOptions luks_create_opts_default = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", }, }; @@ -77,35 +73,33 @@ static QCryptoBlockCreateOptions luks_create_opts_default = { /* ...and with explicit values */ static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", .has_cipher_alg = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .has_cipher_mode = true, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, .has_ivgen_alg = true, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, }, }; static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { - .has_key_secret = true, .key_secret = (char *)"sec0", .has_cipher_alg = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .has_cipher_mode = true, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, .has_ivgen_alg = true, - .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_ESSIV, .has_ivgen_hash_alg = true, - .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256, + .ivgen_hash_alg = QCRYPTO_HASH_ALGO_SHA256, .has_hash_alg = true, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, }, }; #endif /* TEST_LUKS */ @@ -118,12 +112,12 @@ static struct QCryptoBlockTestData { bool expect_header; - QCryptoCipherAlgorithm cipher_alg; + QCryptoCipherAlgo cipher_alg; QCryptoCipherMode cipher_mode; - QCryptoHashAlgorithm hash_alg; + QCryptoHashAlgo hash_alg; - QCryptoIVGenAlgorithm ivgen_alg; - QCryptoHashAlgorithm ivgen_hash; + QCryptoIVGenAlgo ivgen_alg; + QCryptoHashAlgo ivgen_hash; bool slow; } test_data[] = { @@ -134,10 +128,10 @@ static struct QCryptoBlockTestData { .expect_header = false, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_128, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, }, #ifdef TEST_LUKS { @@ -147,11 +141,11 @@ static struct QCryptoBlockTestData { .expect_header = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .cipher_mode = QCRYPTO_CIPHER_MODE_XTS, - .hash_alg = QCRYPTO_HASH_ALG_SHA256, + .hash_alg = QCRYPTO_HASH_ALGO_SHA256, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .slow = true, }, @@ -162,11 +156,11 @@ static struct QCryptoBlockTestData { .expect_header = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .hash_alg = QCRYPTO_HASH_ALG_SHA256, + .hash_alg = QCRYPTO_HASH_ALGO_SHA256, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .slow = true, }, @@ -177,12 +171,12 @@ static struct QCryptoBlockTestData { .expect_header = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, - .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, - .ivgen_hash = QCRYPTO_HASH_ALG_SHA256, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .ivgen_hash = QCRYPTO_HASH_ALGO_SHA256, .slow = true, }, @@ -289,6 +283,7 @@ static void test_block(gconstpointer opaque) test_block_init_func, test_block_write_func, &header, + 0, &error_abort); g_assert(blk); @@ -308,7 +303,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, - 1, NULL); g_assert(blk == NULL); @@ -317,7 +311,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, QCRYPTO_BLOCK_OPEN_NO_IO, - 1, &error_abort); g_assert(qcrypto_block_get_cipher(blk) == NULL); @@ -332,7 +325,6 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, - 1, &error_abort); g_assert(blk); @@ -368,6 +360,7 @@ test_luks_bad_header(gconstpointer data) test_block_init_func, test_block_write_func, &buf, + 0, &error_abort); g_assert(blk); @@ -388,7 +381,6 @@ test_luks_bad_header(gconstpointer data) test_block_read_func, &buf, 0, - 1, &err); g_assert(!blk); g_assert(err); @@ -580,7 +572,7 @@ int main(int argc, char **argv) g_assert(qcrypto_init(NULL) == 0); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS && + if (test_data[i].open_opts->format == QCRYPTO_BLOCK_FORMAT_LUKS && !qcrypto_hash_supports(test_data[i].hash_alg)) { continue; } diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index d9d9d078ff..b328b482e1 100644 --- a/tests/unit/test-crypto-cipher.c +++ b/tests/unit/test-crypto-cipher.c @@ -27,7 +27,7 @@ typedef struct QCryptoCipherTestData QCryptoCipherTestData; struct QCryptoCipherTestData { const char *path; - QCryptoCipherAlgorithm alg; + QCryptoCipherAlgo alg; QCryptoCipherMode mode; const char *key; const char *plaintext; @@ -43,7 +43,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.1.1 ECB-AES128.Encrypt */ .path = "/crypto/cipher/aes-ecb-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "2b7e151628aed2a6abf7158809cf4f3c", .plaintext = @@ -60,7 +60,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.1.3 ECB-AES192.Encrypt */ .path = "/crypto/cipher/aes-ecb-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, + .alg = QCRYPTO_CIPHER_ALGO_AES_192, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", .plaintext = @@ -77,7 +77,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.1.5 ECB-AES256.Encrypt */ .path = "/crypto/cipher/aes-ecb-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "603deb1015ca71be2b73aef0857d7781" @@ -96,7 +96,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.2.1 CBC-AES128.Encrypt */ .path = "/crypto/cipher/aes-cbc-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "2b7e151628aed2a6abf7158809cf4f3c", .iv = "000102030405060708090a0b0c0d0e0f", @@ -114,7 +114,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.2.3 CBC-AES128.Encrypt */ .path = "/crypto/cipher/aes-cbc-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, + .alg = QCRYPTO_CIPHER_ALGO_AES_192, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", .iv = "000102030405060708090a0b0c0d0e0f", @@ -132,7 +132,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.2.5 CBC-AES128.Encrypt */ .path = "/crypto/cipher/aes-cbc-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "603deb1015ca71be2b73aef0857d7781" @@ -156,7 +156,7 @@ static QCryptoCipherTestData test_data[] = { * ciphertext in ECB and CBC modes */ .path = "/crypto/cipher/des-ecb-56-one-block", - .alg = QCRYPTO_CIPHER_ALG_DES, + .alg = QCRYPTO_CIPHER_ALGO_DES, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "80c4a2e691d5b3f7", .plaintext = "70617373776f7264", @@ -165,7 +165,7 @@ static QCryptoCipherTestData test_data[] = { { /* See previous comment */ .path = "/crypto/cipher/des-cbc-56-one-block", - .alg = QCRYPTO_CIPHER_ALG_DES, + .alg = QCRYPTO_CIPHER_ALGO_DES, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "80c4a2e691d5b3f7", .iv = "0000000000000000", @@ -174,7 +174,7 @@ static QCryptoCipherTestData test_data[] = { }, { .path = "/crypto/cipher/des-ecb-56", - .alg = QCRYPTO_CIPHER_ALG_DES, + .alg = QCRYPTO_CIPHER_ALGO_DES, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "80c4a2e691d5b3f7", .plaintext = @@ -191,7 +191,7 @@ static QCryptoCipherTestData test_data[] = { { /* Borrowed from linux-kernel crypto/testmgr.h */ .path = "/crypto/cipher/3des-cbc", - .alg = QCRYPTO_CIPHER_ALG_3DES, + .alg = QCRYPTO_CIPHER_ALGO_3DES, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "e9c0ff2e760b6424444d995a12d640c0" @@ -220,7 +220,7 @@ static QCryptoCipherTestData test_data[] = { { /* Borrowed from linux-kernel crypto/testmgr.h */ .path = "/crypto/cipher/3des-ecb", - .alg = QCRYPTO_CIPHER_ALG_3DES, + .alg = QCRYPTO_CIPHER_ALGO_3DES, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "0123456789abcdef5555555555555555" @@ -233,7 +233,7 @@ static QCryptoCipherTestData test_data[] = { { /* Borrowed from linux-kernel crypto/testmgr.h */ .path = "/crypto/cipher/3des-ctr", - .alg = QCRYPTO_CIPHER_ALG_3DES, + .alg = QCRYPTO_CIPHER_ALGO_3DES, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "9cd6f39cb95a67005a67002dceeb2dce" @@ -308,7 +308,7 @@ static QCryptoCipherTestData test_data[] = { { /* RFC 2144, Appendix B.1 */ .path = "/crypto/cipher/cast5-128", - .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .alg = QCRYPTO_CIPHER_ALGO_CAST5_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "0123456712345678234567893456789A", .plaintext = "0123456789abcdef", @@ -317,7 +317,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-128", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000", .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", @@ -326,7 +326,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-192", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_192, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000" "0000000000000000", @@ -336,7 +336,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-256a", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000" "00000000000000000000000000000000", @@ -346,7 +346,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-256b", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000" "00000000000000000000000000000000", @@ -356,7 +356,7 @@ static QCryptoCipherTestData test_data[] = { { /* Twofish paper "Known Answer Test" */ .path = "/crypto/cipher/twofish-128", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, + .alg = QCRYPTO_CIPHER_ALGO_TWOFISH_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "d491db16e7b1c39e86cb086b789f5419", .plaintext = "019f9809de1711858faac3a3ba20fbc3", @@ -365,7 +365,7 @@ static QCryptoCipherTestData test_data[] = { { /* Twofish paper "Known Answer Test", I=3 */ .path = "/crypto/cipher/twofish-192", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, + .alg = QCRYPTO_CIPHER_ALGO_TWOFISH_192, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "88b2b2706b105e36b446bb6d731a1e88" "efa71f788965bd44", @@ -375,17 +375,30 @@ static QCryptoCipherTestData test_data[] = { { /* Twofish paper "Known Answer Test", I=4 */ .path = "/crypto/cipher/twofish-256", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, + .alg = QCRYPTO_CIPHER_ALGO_TWOFISH_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "d43bb7556ea32e46f2a282b7d45b4e0d" "57ff739d4dc92c1bd7fc01700cc8216f", .plaintext = "90afe91bb288544f2c32dc239b2635e6", .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", }, +#ifdef CONFIG_CRYPTO_SM4 + { + /* SM4, GB/T 32907-2016, Appendix A.1 */ + .path = "/crypto/cipher/sm4", + .alg = QCRYPTO_CIPHER_ALGO_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", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "00000000000000000000000000000000" @@ -402,7 +415,7 @@ static QCryptoCipherTestData test_data[] = { { /* #2, 32 byte key, 32 byte PTX */ .path = "/crypto/cipher/aes-xts-128-2", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "11111111111111111111111111111111" @@ -419,7 +432,7 @@ static QCryptoCipherTestData test_data[] = { { /* #5 from xts.7, 32 byte key, 32 byte PTX */ .path = "/crypto/cipher/aes-xts-128-3", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0" @@ -436,7 +449,7 @@ static QCryptoCipherTestData test_data[] = { { /* #4, 32 byte key, 512 byte PTX */ .path = "/crypto/cipher/aes-xts-128-4", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "27182818284590452353602874713526" @@ -515,7 +528,7 @@ static QCryptoCipherTestData test_data[] = { * which is incompatible with XTS */ .path = "/crypto/cipher/cast5-xts-128", - .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .alg = QCRYPTO_CIPHER_ALGO_CAST5_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "27182818284590452353602874713526" @@ -524,7 +537,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.5.1 CTR-AES128.Encrypt */ .path = "/crypto/cipher/aes-ctr-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "2b7e151628aed2a6abf7158809cf4f3c", .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", @@ -542,7 +555,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.5.3 CTR-AES192.Encrypt */ .path = "/crypto/cipher/aes-ctr-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, + .alg = QCRYPTO_CIPHER_ALGO_AES_192, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", @@ -560,7 +573,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.5.5 CTR-AES256.Encrypt */ .path = "/crypto/cipher/aes-ctr-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "603deb1015ca71be2b73aef0857d7781" "1f352c073b6108d72d9810a30914dff4", @@ -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); @@ -738,7 +750,7 @@ static void test_cipher_null_iv(void) uint8_t ciphertext[32] = { 0 }; cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC, key, sizeof(key), &error_abort); @@ -767,7 +779,7 @@ static void test_cipher_short_plaintext(void) int ret; cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC, key, sizeof(key), &error_abort); @@ -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", + QCryptoCipherAlgo_str(test_data[i].alg), + QCryptoCipherMode_str(test_data[i].mode)); } } diff --git a/tests/unit/test-crypto-hash.c b/tests/unit/test-crypto-hash.c index 1f4abb822b..8fee1593f9 100644 --- a/tests/unit/test-crypto-hash.c +++ b/tests/unit/test-crypto-hash.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -42,6 +43,9 @@ "63b54e4cb2d2032b393994aa263c0dbb" \ "e00a9f2fe9ef6037352232a1eec55ee7" #define OUTPUT_RIPEMD160 "f3d658fad3fdfb2b52c9369cf0d441249ddfa8a0" +#ifdef CONFIG_CRYPTO_SM3 +#define OUTPUT_SM3 "d4a97db105b477b84c4f20ec9c31a6c814e2705a0b83a5a89748d75f0ef456a1" +#endif #define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" #define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" @@ -54,32 +58,45 @@ "7sVe5w==" #define OUTPUT_RIPEMD160_B64 "89ZY+tP9+ytSyTac8NRBJJ3fqKA=" +#ifdef CONFIG_CRYPTO_SM3 +#define OUTPUT_SM3_B64 "1Kl9sQW0d7hMTyDsnDGmyBTicFoLg6Wol0jXXw70VqE=" +#endif + static const char *expected_outputs[] = { - [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5, - [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160, + [QCRYPTO_HASH_ALGO_MD5] = OUTPUT_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = OUTPUT_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = OUTPUT_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = OUTPUT_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = OUTPUT_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = OUTPUT_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = OUTPUT_RIPEMD160, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = OUTPUT_SM3, +#endif }; static const char *expected_outputs_b64[] = { - [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64, - [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64, - [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224_B64, - [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64, - [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384_B64, - [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512_B64, - [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160_B64, + [QCRYPTO_HASH_ALGO_MD5] = OUTPUT_MD5_B64, + [QCRYPTO_HASH_ALGO_SHA1] = OUTPUT_SHA1_B64, + [QCRYPTO_HASH_ALGO_SHA224] = OUTPUT_SHA224_B64, + [QCRYPTO_HASH_ALGO_SHA256] = OUTPUT_SHA256_B64, + [QCRYPTO_HASH_ALGO_SHA384] = OUTPUT_SHA384_B64, + [QCRYPTO_HASH_ALGO_SHA512] = OUTPUT_SHA512_B64, + [QCRYPTO_HASH_ALGO_RIPEMD160] = OUTPUT_RIPEMD160_B64, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = OUTPUT_SM3_B64, +#endif }; static const int expected_lens[] = { - [QCRYPTO_HASH_ALG_MD5] = 16, - [QCRYPTO_HASH_ALG_SHA1] = 20, - [QCRYPTO_HASH_ALG_SHA224] = 28, - [QCRYPTO_HASH_ALG_SHA256] = 32, - [QCRYPTO_HASH_ALG_SHA384] = 48, - [QCRYPTO_HASH_ALG_SHA512] = 64, - [QCRYPTO_HASH_ALG_RIPEMD160] = 20, + [QCRYPTO_HASH_ALGO_MD5] = 16, + [QCRYPTO_HASH_ALGO_SHA1] = 20, + [QCRYPTO_HASH_ALGO_SHA224] = 28, + [QCRYPTO_HASH_ALGO_SHA256] = 32, + [QCRYPTO_HASH_ALGO_SHA384] = 48, + [QCRYPTO_HASH_ALGO_SHA512] = 64, + [QCRYPTO_HASH_ALGO_RIPEMD160] = 20, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = 32, +#endif }; static const char hex[] = "0123456789abcdef"; @@ -122,7 +139,7 @@ static void test_hash_prealloc(void) size_t i; for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - uint8_t *result; + uint8_t *result, *origresult; size_t resultlen; int ret; size_t j; @@ -132,7 +149,7 @@ static void test_hash_prealloc(void) } resultlen = expected_lens[i]; - result = g_new0(uint8_t, resultlen); + origresult = result = g_new0(uint8_t, resultlen); ret = qcrypto_hash_bytes(i, INPUT_TEXT, @@ -141,7 +158,8 @@ static void test_hash_prealloc(void) &resultlen, &error_fatal); g_assert(ret == 0); - + /* Validate that our pre-allocated pointer was not replaced */ + g_assert(result == origresult); g_assert(resultlen == expected_lens[i]); for (j = 0; j < resultlen; j++) { g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); @@ -241,6 +259,50 @@ static void test_hash_base64(void) } } +static void test_hash_accumulate(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + g_autoptr(QCryptoHash) hash = NULL; + struct iovec iov[] = { + { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, + { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, + { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, + }; + g_autofree uint8_t *result = NULL; + size_t resultlen = 0; + int ret; + size_t j; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + hash = qcrypto_hash_new(i, &error_fatal); + g_assert(hash != NULL); + + /* Add each iovec to the hash context separately */ + for (j = 0; j < G_N_ELEMENTS(iov); j++) { + ret = qcrypto_hash_updatev(hash, + &iov[j], 1, + &error_fatal); + + g_assert(ret == 0); + } + + ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, + &error_fatal); + + g_assert(ret == 0); + g_assert(resultlen == expected_lens[i]); + for (j = 0; j < resultlen; j++) { + g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); + } + } +} + int main(int argc, char **argv) { int ret = qcrypto_init(&error_fatal); @@ -252,5 +314,6 @@ int main(int argc, char **argv) g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc); g_test_add_func("/crypto/hash/digest", test_hash_digest); g_test_add_func("/crypto/hash/base64", test_hash_base64); + g_test_add_func("/crypto/hash/accumulate", test_hash_accumulate); return g_test_run(); } diff --git a/tests/unit/test-crypto-hmac.c b/tests/unit/test-crypto-hmac.c index 23eb724d94..20c60eb9d8 100644 --- a/tests/unit/test-crypto-hmac.c +++ b/tests/unit/test-crypto-hmac.c @@ -27,43 +27,43 @@ typedef struct QCryptoHmacTestData QCryptoHmacTestData; struct QCryptoHmacTestData { - QCryptoHashAlgorithm alg; + QCryptoHashAlgo alg; const char *hex_digest; }; static QCryptoHmacTestData test_data[] = { { - .alg = QCRYPTO_HASH_ALG_MD5, + .alg = QCRYPTO_HASH_ALGO_MD5, .hex_digest = "ede9cb83679ba82d88fbeae865b3f8fc", }, { - .alg = QCRYPTO_HASH_ALG_SHA1, + .alg = QCRYPTO_HASH_ALGO_SHA1, .hex_digest = "c7b5a631e3aac975c4ededfcd346e469" "dbc5f2d1", }, { - .alg = QCRYPTO_HASH_ALG_SHA224, + .alg = QCRYPTO_HASH_ALGO_SHA224, .hex_digest = "5f768179dbb29ca722875d0f461a2e2f" "597d0210340a84df1a8e9c63", }, { - .alg = QCRYPTO_HASH_ALG_SHA256, + .alg = QCRYPTO_HASH_ALGO_SHA256, .hex_digest = "3798f363c57afa6edaffe39016ca7bad" "efd1e670afb0e3987194307dec3197db", }, { - .alg = QCRYPTO_HASH_ALG_SHA384, + .alg = QCRYPTO_HASH_ALGO_SHA384, .hex_digest = "d218680a6032d33dccd9882d6a6a7164" "64f26623be257a9b2919b185294f4a49" "9e54b190bfd6bc5cedd2cd05c7e65e82", }, { - .alg = QCRYPTO_HASH_ALG_SHA512, + .alg = QCRYPTO_HASH_ALGO_SHA512, .hex_digest = "835a4f5b3750b4c1fccfa88da2f746a4" "900160c9f18964309bb736c13b59491b" @@ -71,11 +71,19 @@ static QCryptoHmacTestData test_data[] = { "94c4ba26862b2dadb59b7ede1d08d53e", }, { - .alg = QCRYPTO_HASH_ALG_RIPEMD160, + .alg = QCRYPTO_HASH_ALGO_RIPEMD160, .hex_digest = "94964ed4c1155b62b668c241d67279e5" "8a711676", }, +#ifdef CONFIG_CRYPTO_SM3 + { + .alg = QCRYPTO_HASH_ALGO_SM3, + .hex_digest = + "760e3799332bc913819b930085360ddb" + "c05529261313d5b15b75bab4fd7ae91e", + }, +#endif }; static const char hex[] = "0123456789abcdef"; @@ -126,7 +134,7 @@ static void test_hmac_prealloc(void) for (i = 0; i < G_N_ELEMENTS(test_data); i++) { QCryptoHmacTestData *data = &test_data[i]; QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; + uint8_t *result = NULL, *origresult = NULL; size_t resultlen = 0; const char *exp_output = NULL; int ret; @@ -139,7 +147,7 @@ static void test_hmac_prealloc(void) exp_output = data->hex_digest; resultlen = strlen(exp_output) / 2; - result = g_new0(uint8_t, resultlen); + origresult = result = g_new0(uint8_t, resultlen); hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, strlen(KEY), &error_fatal); @@ -149,6 +157,8 @@ static void test_hmac_prealloc(void) strlen(INPUT_TEXT), &result, &resultlen, &error_fatal); g_assert(ret == 0); + /* Validate that our pre-allocated pointer was not replaced */ + g_assert(result == origresult); exp_output = data->hex_digest; for (j = 0; j < resultlen; j++) { diff --git a/tests/unit/test-crypto-ivgen.c b/tests/unit/test-crypto-ivgen.c index 29630ed348..bc9ffe34e7 100644 --- a/tests/unit/test-crypto-ivgen.c +++ b/tests/unit/test-crypto-ivgen.c @@ -26,9 +26,9 @@ struct QCryptoIVGenTestData { const char *path; uint64_t sector; - QCryptoIVGenAlgorithm ivalg; - QCryptoHashAlgorithm hashalg; - QCryptoCipherAlgorithm cipheralg; + QCryptoIVGenAlgo ivalg; + QCryptoHashAlgo hashalg; + QCryptoCipherAlgo cipheralg; const uint8_t *key; size_t nkey; const uint8_t *iv; @@ -38,7 +38,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain/1", .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN, .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -47,7 +47,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain/1f2e3d4c", .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN, .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -56,7 +56,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain/1f2e3d4c5b6a7988", .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN, .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -65,7 +65,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain64/1", .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -74,7 +74,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain64/1f2e3d4c", .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -83,7 +83,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain64/1f2e3d4c5b6a7988", .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x4c\x3d\x2e\x1f" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -92,9 +92,9 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/essiv/1", .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, + .ivalg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALGO_AES_128, + .hashalg = QCRYPTO_HASH_ALGO_SHA256, .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", .nkey = 16, @@ -106,9 +106,9 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/essiv/1f2e3d4c", .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, + .ivalg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALGO_AES_128, + .hashalg = QCRYPTO_HASH_ALGO_SHA256, .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", .nkey = 16, @@ -120,9 +120,9 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/essiv/1f2e3d4c5b6a7988", .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, + .ivalg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALGO_AES_128, + .hashalg = QCRYPTO_HASH_ALGO_SHA256, .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", .nkey = 16, @@ -166,7 +166,7 @@ int main(int argc, char **argv) size_t i; g_test_init(&argc, &argv, NULL); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (test_data[i].ivalg == QCRYPTO_IVGEN_ALG_ESSIV && + if (test_data[i].ivalg == QCRYPTO_IV_GEN_ALGO_ESSIV && !qcrypto_hash_supports(test_data[i].hashalg)) { continue; } diff --git a/tests/unit/test-crypto-pbkdf.c b/tests/unit/test-crypto-pbkdf.c index 43c417f6b4..ddb7244e21 100644 --- a/tests/unit/test-crypto-pbkdf.c +++ b/tests/unit/test-crypto-pbkdf.c @@ -25,14 +25,13 @@ #include #endif -#if ((defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) && \ - (defined(_WIN32) || defined(RUSAGE_THREAD))) +#if defined(_WIN32) || defined(RUSAGE_THREAD) || defined(CONFIG_DARWIN) #include "crypto/pbkdf.h" typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; struct QCryptoPbkdfTestData { const char *path; - QCryptoHashAlgorithm hash; + QCryptoHashAlgo hash; unsigned int iterations; const char *key; size_t nkey; @@ -53,7 +52,7 @@ static QCryptoPbkdfTestData test_data[] = { /* RFC 3962 test data */ { .path = "/crypto/pbkdf/rfc3962/sha1/iter1", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1, .key = "password", .nkey = 8, @@ -67,7 +66,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 2, .key = "password", .nkey = 8, @@ -81,7 +80,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter1200a", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1200, .key = "password", .nkey = 8, @@ -95,7 +94,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter5", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 5, .key = "password", .nkey = 8, @@ -109,7 +108,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter1200b", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -124,7 +123,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter1200c", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -139,7 +138,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter50", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 50, .key = "\360\235\204\236", /* g-clef ("\xf09d849e) */ .nkey = 4, @@ -155,7 +154,7 @@ static QCryptoPbkdfTestData test_data[] = { /* RFC-6070 test data */ { .path = "/crypto/pbkdf/rfc6070/sha1/iter1", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1, .key = "password", .nkey = 8, @@ -167,7 +166,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 2, .key = "password", .nkey = 8, @@ -179,7 +178,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter4096", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 4096, .key = "password", .nkey = 8, @@ -191,7 +190,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter16777216", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 16777216, .key = "password", .nkey = 8, @@ -204,7 +203,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter4096a", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 4096, .key = "passwordPASSWORDpassword", .nkey = 24, @@ -217,7 +216,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter4096b", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 4096, .key = "pass\0word", .nkey = 9, @@ -232,7 +231,7 @@ static QCryptoPbkdfTestData test_data[] = { { /* empty password test. */ .path = "/crypto/pbkdf/nonrfc/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 2, .key = "", .nkey = 0, @@ -245,7 +244,7 @@ static QCryptoPbkdfTestData test_data[] = { { /* Password exceeds block size test */ .path = "/crypto/pbkdf/nonrfc/sha256/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -260,7 +259,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/sha512/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA512, + .hash = QCRYPTO_HASH_ALGO_SHA512, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -277,7 +276,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/sha224/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA224, + .hash = QCRYPTO_HASH_ALGO_SHA224, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -294,7 +293,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/sha384/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA384, + .hash = QCRYPTO_HASH_ALGO_SHA384, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -311,7 +310,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/ripemd160/iter1200", - .hash = QCRYPTO_HASH_ALG_RIPEMD160, + .hash = QCRYPTO_HASH_ALGO_RIPEMD160, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -326,10 +325,26 @@ static QCryptoPbkdfTestData test_data[] = { "\xce\xbf\x91\x14\x8b\x5c\x48\x41", .nout = 32 }, +#ifdef CONFIG_CRYPTO_SM3 + { + .path = "/crypto/pbkdf/nonrfc/sm3/iter2", + .hash = QCRYPTO_HASH_ALGO_SM3, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x48\x71\x1b\x58\xa3\xcb\xce\x06" + "\xba\xad\x77\xa8\xb5\xb9\xd8\x07" + "\x6a\xe2\xb3\x5b\x95\xce\xc8\xce" + "\xe7\xb1\xcb\xee\x61\xdf\x04\xea", + .nout = 32 + }, +#endif #if 0 { .path = "/crypto/pbkdf/nonrfc/whirlpool/iter1200", - .hash = QCRYPTO_HASH_ALG_WHIRLPOOL, + .hash = QCRYPTO_HASH_ALGO_WHIRLPOOL, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -394,7 +409,7 @@ static void test_pbkdf(const void *opaque) } -static void test_pbkdf_timing(void) +static void test_pbkdf_timing_sha256(void) { uint8_t key[32]; uint8_t salt[32]; @@ -403,7 +418,7 @@ static void test_pbkdf_timing(void) memset(key, 0x5d, sizeof(key)); memset(salt, 0x7c, sizeof(salt)); - iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALGO_SHA256, key, sizeof(key), salt, sizeof(salt), 32, @@ -422,14 +437,18 @@ int main(int argc, char **argv) g_assert(qcrypto_init(NULL) == 0); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { + if (!qcrypto_pbkdf2_supports(test_data[i].hash)) { + continue; + } + if (!test_data[i].slow || g_test_slow()) { g_test_add_data_func(test_data[i].path, &test_data[i], test_pbkdf); } } - if (g_test_slow()) { - g_test_add_func("/crypt0/pbkdf/timing", test_pbkdf_timing); + if (g_test_slow() && qcrypto_pbkdf2_supports(QCRYPTO_HASH_ALGO_SHA256)) { + g_test_add_func("/crypt0/pbkdf/timing/sha256", test_pbkdf_timing_sha256); } return g_test_run(); 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-tlssession.c b/tests/unit/test-crypto-tlssession.c index 615a1344b4..3395f73560 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -35,18 +35,40 @@ #define PSKFILE WORKDIR "keys.psk" #define KEYFILE WORKDIR "key-ctx.pem" -static ssize_t testWrite(const char *buf, size_t len, void *opaque) +static ssize_t +testWrite(const char *buf, size_t len, void *opaque, Error **errp) { int *fd = opaque; + int ret; - return write(*fd, buf, len); + ret = write(*fd, buf, len); + if (ret < 0) { + if (errno == EAGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else { + error_setg_errno(errp, errno, "unable to write"); + return -1; + } + } + return ret; } -static ssize_t testRead(char *buf, size_t len, void *opaque) +static ssize_t +testRead(char *buf, size_t len, void *opaque, Error **errp) { int *fd = opaque; + int ret; - return read(*fd, buf, len); + ret = read(*fd, buf, len); + if (ret < 0) { + if (errno == EAGAIN) { + return QCRYPTO_TLS_SESSION_ERR_BLOCK; + } else { + error_setg_errno(errp, errno, "unable to read"); + return -1; + } + } + return ret; } static QCryptoTLSCreds *test_tls_creds_psk_create( @@ -82,7 +104,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 +258,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); /* diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 86caddcf64..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,782 @@ 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); - - 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); + /* Fraction is affected by floating-point rounding */ + /* This would be 0xfffffffffffffbff with infinite precision */ + do_strtosz_metric("18.446744073709550591E", 0, 0xfffffffffffffc0cULL, 22); } static void test_freq_to_str(void) @@ -2534,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", @@ -2564,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", @@ -2576,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", @@ -2600,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", @@ -2608,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", @@ -2616,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", @@ -2644,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", @@ -2656,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", @@ -2680,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", @@ -2688,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", @@ -2696,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", @@ -2710,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); @@ -2725,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", @@ -2737,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", @@ -2761,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", @@ -2769,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", @@ -2777,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", 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-fifo.c b/tests/unit/test-fifo.c new file mode 100644 index 0000000000..14153c41fa --- /dev/null +++ b/tests/unit/test-fifo.c @@ -0,0 +1,449 @@ +/* + * Fifo8 tests + * + * Copyright 2024 Mark Cave-Ayland + * + * Authors: + * Mark Cave-Ayland + * + * 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 "migration/vmstate.h" +#include "qemu/fifo8.h" + +const VMStateInfo vmstate_info_uint32; +const VMStateInfo vmstate_info_buffer; + + +static void test_fifo8_pop_bufptr_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: [ . . . . . . . . ] + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: [ 1 2 3 4 . . . . ] + */ + buf = fifo8_pop_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: [ 1 2 3 4 . . . . ] + * buf --^ count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail --]v-- head used = 8 + * FIFO: [ 9 a 3 4 5 6 7 8 ] + */ + buf = fifo8_pop_bufptr(&fifo, 8, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: [ 9 a 3 4 5 6 7 8 ] + * buf --^ count = 6 + */ + g_assert(count == 6); + g_assert(buf[0] == 0x3 && buf[1] == 0x4 && buf[2] == 0x5 && + buf[3] == 0x6 && buf[4] == 0x7 && buf[5] == 0x8); + + g_assert(fifo8_num_used(&fifo) == 2); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pop_bufptr(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4 }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: [ . . . . . . . . ] + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: [ 1 2 3 4 . . . . ] + */ + buf = fifo8_pop_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: [ 1 2 3 4 . . . . ] + * buf --^ count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + g_assert(fifo8_num_used(&fifo) == 2); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_bufptr_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + buf = fifo8_peek_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 ] count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + buf = fifo8_pop_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 ] count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail ---]v-- head used = 8 + * FIFO: { 9 a 3 4 5 6 7 8 } + */ + + buf = fifo8_peek_bufptr(&fifo, 8, &count); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a 3 4 5 6 7 8 } + * buf: [ 3 4 5 6 7 8 ] count = 6 + */ + g_assert(count == 6); + g_assert(buf[0] == 0x3 && buf[1] == 0x4 && buf[2] == 0x5 && + buf[3] == 0x6 && buf[4] == 0x7 && buf[5] == 0x8); + + g_assert(fifo8_num_used(&fifo) == 8); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_bufptr(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4 }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + buf = fifo8_peek_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 ] count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + g_assert(fifo8_num_used(&fifo) == 4); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pop_buf_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc }; + uint8_t data_out[4]; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + fifo8_pop_buf(&fifo, NULL, 4); + /* + * tail --]v-- head used = 0 + * FIFO: [ 1 2 3 4 . . . . ] + */ + + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + */ + count = fifo8_pop_buf(&fifo, NULL, 4); + /* + * head --v ]-- tail used = 4 + * FIFO: { 9 a b c 5 6 7 8 } + */ + g_assert(count == 4); + count = fifo8_pop_buf(&fifo, data_out, 4); + /* + * tail --]v-- head used = 0 + * FIFO: { 9 a b c 5 6 7 8 } + */ + g_assert(count == 4); + g_assert(data_out[0] == 0x9 && data_out[1] == 0xa && + data_out[2] == 0xb && data_out[3] == 0xc); + + g_assert(fifo8_num_used(&fifo) == 0); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pop_buf(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 }; + uint8_t data_out[] = { 0xff, 0xff, 0xff, 0xff }; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + count = fifo8_pop_buf(&fifo, NULL, 4); + /* + * tail --]v-- head used = 0 + * FIFO: { 1 2 3 4 . . . . } + */ + g_assert(count == 4); + count = fifo8_pop_buf(&fifo, data_out, 4); + g_assert(data_out[0] == 0x5 && data_out[1] == 0x6 && + data_out[2] == 0x7 && data_out[3] == 0x8); + + g_assert(fifo8_num_used(&fifo) == 0); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_buf_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc }; + uint8_t data_out[8]; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + fifo8_pop_buf(&fifo, NULL, 4); + /* + * tail --]v-- head used = 0 + * FIFO: { 1 2 3 4 . . . . } + */ + + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + */ + count = fifo8_peek_buf(&fifo, NULL, 4); + g_assert(count == 4); + count = fifo8_peek_buf(&fifo, data_out, 4); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + * buf: [ 5 6 7 8 ] count = 4 + */ + g_assert(count == 4); + g_assert(data_out[0] == 0x5 && data_out[1] == 0x6 && + data_out[2] == 0x7 && data_out[3] == 0x8); + + count = fifo8_peek_buf(&fifo, data_out, 8); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + * buf: [ 5 6 7 8 9 a b c ] count = 8 + */ + g_assert(count == 8); + g_assert(data_out[0] == 0x5 && data_out[1] == 0x6 && + data_out[2] == 0x7 && data_out[3] == 0x8); + g_assert(data_out[4] == 0x9 && data_out[5] == 0xa && + data_out[6] == 0xb && data_out[7] == 0xc); + + g_assert(fifo8_num_used(&fifo) == 8); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_buf(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_out[] = { 0xff, 0xff, 0xff, 0xff }; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + count = fifo8_peek_buf(&fifo, NULL, 4); + g_assert(count == 4); + + g_assert(data_out[0] == 0xff && data_out[1] == 0xff && + data_out[2] == 0xff && data_out[3] == 0xff); + count = fifo8_peek_buf(&fifo, data_out, 4); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 3 4 ] count = 4 + */ + g_assert(count == 4); + g_assert(data_out[0] == 0x1 && data_out[1] == 0x2 && + data_out[2] == 0x3 && data_out[3] == 0x4); + + g_assert(fifo8_num_used(&fifo) == 4); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek(void) +{ + Fifo8 fifo; + uint8_t c; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + fifo8_push(&fifo, 0x1); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 . . . . . . . } + */ + fifo8_push(&fifo, 0x2); + /* + * head --v ]-- tail used = 2 + * FIFO: { 1 2 . . . . . . } + */ + + c = fifo8_peek(&fifo); + g_assert(c == 0x1); + fifo8_pop(&fifo); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 2 . . . . . . } + */ + c = fifo8_peek(&fifo); + g_assert(c == 0x2); + + g_assert(fifo8_num_used(&fifo) == 1); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pushpop(void) +{ + Fifo8 fifo; + uint8_t c; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + fifo8_push(&fifo, 0x1); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 . . . . . . . } + */ + fifo8_push(&fifo, 0x2); + /* + * head --v ]-- tail used = 2 + * FIFO: { 1 2 . . . . . . } + */ + + c = fifo8_pop(&fifo); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 2 . . . . . . } + */ + g_assert(c == 0x1); + c = fifo8_pop(&fifo); + /* + * tail --]v-- head used = 0 + * FIFO: { 1 2 . . . . . . } + */ + g_assert(c == 0x2); + + g_assert(fifo8_num_used(&fifo) == 0); + fifo8_destroy(&fifo); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/fifo8/pushpop", test_fifo8_pushpop); + g_test_add_func("/fifo8/peek", test_fifo8_peek); + g_test_add_func("/fifo8/peek_buf", test_fifo8_peek_buf); + g_test_add_func("/fifo8/peek_buf_wrap", test_fifo8_peek_buf_wrap); + g_test_add_func("/fifo8/pop_buf", test_fifo8_pop_buf); + g_test_add_func("/fifo8/pop_buf_wrap", test_fifo8_pop_buf_wrap); + g_test_add_func("/fifo8/peek_bufptr", test_fifo8_peek_bufptr); + g_test_add_func("/fifo8/peek_bufptr_wrap", test_fifo8_peek_bufptr_wrap); + g_test_add_func("/fifo8/pop_bufptr", test_fifo8_pop_bufptr); + g_test_add_func("/fifo8/pop_bufptr_wrap", test_fifo8_pop_bufptr_wrap); + return g_test_run(); +} 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 19f72eab96..4f022617df 100644 --- a/tests/unit/test-io-channel-command.c +++ b/tests/unit/test-io-channel-command.c @@ -20,6 +20,8 @@ #include "qemu/osdep.h" #include +#include +#include #include "io/channel-command.h" #include "io-channel-helpers.h" #include "qapi/error.h" @@ -29,23 +31,26 @@ static char *socat = NULL; +#if !defined(_WIN32) && !defined(CONFIG_DARWIN) static void test_io_channel_command_fifo(bool async) { g_autofree gchar *tmpdir = g_dir_make_tmp("qemu-test-io-channel.XXXXXX", NULL); - g_autofree gchar *fifo = g_strdup_printf("%s/%s", tmpdir, TEST_FIFO); + 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; + int err; + + if (mkfifo(fifo, 0600)) { + g_error("mkfifo: %s", strerror(errno)); + } src = QIO_CHANNEL(qio_channel_command_new_spawn((const char **) srcargv, O_WRONLY, &error_abort)); - /* try to avoid a race to create the socket */ - g_usleep(1000); - dst = QIO_CHANNEL(qio_channel_command_new_spawn((const char **) dstargv, O_RDONLY, &error_abort)); @@ -57,10 +62,12 @@ static void test_io_channel_command_fifo(bool async) object_unref(OBJECT(src)); object_unref(OBJECT(dst)); - g_rmdir(tmpdir); + 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) { @@ -80,6 +87,7 @@ static void test_io_channel_command_fifo_sync(void) test_io_channel_command_fifo(false); } +#endif static void test_io_channel_command_echo(bool async) @@ -124,10 +132,12 @@ int main(int argc, char **argv) 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", diff --git a/tests/unit/test-io-channel-socket.c b/tests/unit/test-io-channel-socket.c index b36a5d972a..dc7be96e9c 100644 --- a/tests/unit/test-io-channel-socket.c +++ b/tests/unit/test-io-channel-socket.c @@ -460,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)); @@ -505,7 +506,7 @@ static void test_io_channel_unix_listen_cleanup(void) { QIOChannelSocket *ioc; struct sockaddr_un un; - int sock; + int sock, ret = 0; #define TEST_SOCKET "test-io-channel-socket.sock" @@ -518,7 +519,9 @@ static void test_io_channel_unix_listen_cleanup(void) un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET); unlink(TEST_SOCKET); - bind(sock, (struct sockaddr *)&un, sizeof(un)); + ret = bind(sock, (struct sockaddr *)&un, sizeof(un)); + g_assert_cmpint(ret, ==, 0); + ioc->fd = sock; ioc->localAddrLen = sizeof(ioc->localAddr); getsockname(sock, (struct sockaddr *)&ioc->localAddr, diff --git a/tests/unit/test-io-channel-tls.c b/tests/unit/test-io-channel-tls.c index cc39247556..e036ac5df4 100644 --- a/tests/unit/test-io-channel-tls.c +++ b/tests/unit/test-io-channel-tls.c @@ -121,7 +121,7 @@ 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/" 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 6f7623d310..75bc3be005 100644 --- a/tests/unit/test-iov.c +++ b/tests/unit/test-iov.c @@ -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); } diff --git a/tests/unit/test-nested-aio-poll.c b/tests/unit/test-nested-aio-poll.c new file mode 100644 index 0000000000..d8fd92c43b --- /dev/null +++ b/tests/unit/test-nested-aio-poll.c @@ -0,0 +1,122 @@ +/* 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) +{ + event_notifier_test_and_clear(notifier); +} + +static bool io_poll_true(void *opaque) +{ + return true; +} + +static bool io_poll_false(void *opaque) +{ + return false; +} + +static void io_poll_ready(EventNotifier *notifier) +{ + TestData *td = container_of(notifier, TestData, poll_notifier); + + 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; +} + +/* 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 b4e0a14573..8cddf5dc37 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -665,6 +665,36 @@ static void test_qga_blockedrpcs(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; @@ -755,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); @@ -793,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"); @@ -802,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; @@ -970,8 +1120,13 @@ int main(int argc, char **argv) test_qga_fsfreeze_status); 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 5f614afdbf..5479e68237 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, @@ -706,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_ENUM_A_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_ENUM_A_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) { @@ -1216,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..3455f3b107 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_ENUM_A_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_ENUM_A_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..f9bccb56ab 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" @@ -48,17 +48,19 @@ } /* - * Currently a 4-level topology hierarchy is supported on PC machines - * -sockets/dies/cores/threads + * Currently a 5-level topology hierarchy is supported on PC machines + * -sockets/dies/modules/cores/threads */ -#define SMP_CONFIG_WITH_DIES(ha, a, hb, b, hc, c, hd, d, he, e, hf, f) \ +#define SMP_CONFIG_WITH_MODS_DIES(ha, a, hb, b, hc, c, hd, d, \ + he, e, hf, f, hg, g) \ { \ .has_cpus = ha, .cpus = a, \ .has_sockets = hb, .sockets = b, \ .has_dies = hc, .dies = c, \ - .has_cores = hd, .cores = d, \ - .has_threads = he, .threads = e, \ - .has_maxcpus = hf, .maxcpus = f, \ + .has_modules = hd, .modules = d, \ + .has_cores = he, .cores = e, \ + .has_threads = hf, .threads = f, \ + .has_maxcpus = hg, .maxcpus = g, \ } /* @@ -75,6 +77,41 @@ .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 8-level topology hierarchy, which is the + * QEMU's unified abstract representation of CPU topology. + * -drawers/books/sockets/dies/clusters/modules/cores/threads + */ +#define SMP_CONFIG_WITH_FULL_TOPO(a, b, c, d, e, f, g, h, i, j) \ + { \ + .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_modules = true, .modules = g, \ + .has_cores = true, .cores = h, \ + .has_threads = true, .threads = i, \ + .has_maxcpus = true, .maxcpus = j, \ + } + /** * @config - the given SMP configuration * @expect_prefer_sockets - the expected parsing result for the @@ -296,18 +333,44 @@ static const struct SMPTestData data_generic_valid[] = { .config = SMP_CONFIG_GENERIC(T, 8, T, 2, T, 4, T, 2, T, 16), .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 4, 2, 16), + }, { + /* + * Unsupported parameters are always allowed to be set to '1' + * config: + * -smp 8,drawers=1,books=1,sockets=2,dies=1,clusters=1,modules=1,\ + * cores=2,threads=2,maxcpus=8 + * expect: cpus=8,sockets=2,cores=2,threads=2,maxcpus=8 */ + .config = SMP_CONFIG_WITH_FULL_TOPO(8, 1, 1, 2, 1, 1, 1, 2, 2, 8), + .expect_prefer_sockets = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), + .expect_prefer_cores = CPU_TOPOLOGY_GENERIC(8, 2, 2, 2, 8), }, }; static const struct SMPTestData data_generic_invalid[] = { { + /* config: -smp 2,modules=2 */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 2, F, 0, F, 0, T, 2, + F, 0, F, 0, F, 0), + .expect_error = "modules > 1 not supported by this machine's CPU topology", + }, { /* config: -smp 2,dies=2 */ - .config = SMP_CONFIG_WITH_DIES(T, 2, F, 0, T, 2, F, 0, F, 0, F, 0), - .expect_error = "dies not supported by this machine's CPU topology", + .config = SMP_CONFIG_WITH_MODS_DIES(T, 2, F, 0, T, 2, F, 0, + F, 0, F, 0, F, 0), + .expect_error = "dies > 1 not supported by this machine's CPU topology", }, { /* 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", + .expect_error = "clusters > 1 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 > 1 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 > 1 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,31 +386,59 @@ 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", + }, +}; + +static const struct SMPTestData data_with_modules_invalid[] = { + { + /* config: -smp 16,sockets=2,modules=2,cores=4,threads=2,maxcpus=16 */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 16, T, 2, F, 0, T, 2, + T, 4, T, 2, T, 16), + .expect_error = "Invalid CPU topology: " + "product of the hierarchy must match maxcpus: " + "sockets (2) * modules (2) * cores (4) * threads (2) " + "!= maxcpus (16)", + }, { + /* config: -smp 34,sockets=2,modules=2,cores=4,threads=2,maxcpus=32 */ + .config = SMP_CONFIG_WITH_MODS_DIES(T, 34, T, 2, F, 0, T, 2, + T, 4, T, 2, T, 32), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "sockets (2) * modules (2) * cores (4) * threads (2) " + "== maxcpus (32) < smp_cpus (34)", }, }; static const struct SMPTestData data_with_dies_invalid[] = { { /* config: -smp 16,sockets=2,dies=2,cores=4,threads=2,maxcpus=16 */ - .config = SMP_CONFIG_WITH_DIES(T, 16, T, 2, T, 2, T, 4, T, 2, T, 16), + .config = SMP_CONFIG_WITH_MODS_DIES(T, 16, T, 2, T, 2, F, 0, + T, 4, T, 2, T, 16), .expect_error = "Invalid CPU topology: " "product of the hierarchy must match maxcpus: " "sockets (2) * dies (2) * cores (4) * threads (2) " "!= maxcpus (16)", }, { /* config: -smp 34,sockets=2,dies=2,cores=4,threads=2,maxcpus=32 */ - .config = SMP_CONFIG_WITH_DIES(T, 34, T, 2, T, 2, T, 4, T, 2, T, 32), + .config = SMP_CONFIG_WITH_MODS_DIES(T, 34, T, 2, T, 2, F, 0, + T, 4, T, 2, T, 32), .expect_error = "Invalid CPU topology: " "maxcpus must be equal to or greater than smp: " "sockets (2) * dies (2) * cores (4) * threads (2) " @@ -355,6 +446,33 @@ static const struct SMPTestData data_with_dies_invalid[] = { }, }; +static const struct SMPTestData data_with_modules_dies_invalid[] = { + { + /* + * config: -smp 200,sockets=3,dies=5,modules=2,cores=4,\ + * threads=2,maxcpus=200 + */ + .config = SMP_CONFIG_WITH_MODS_DIES(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: " + "sockets (3) * dies (5) * modules (2) * " + "cores (4) * threads (2) != maxcpus (200)", + }, { + /* + * config: -smp 242,sockets=3,dies=5,modules=2,cores=4,\ + * threads=2,maxcpus=240 + */ + .config = SMP_CONFIG_WITH_MODS_DIES(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: " + "sockets (3) * dies (5) * modules (2) * " + "cores (4) * threads (2) " + "== maxcpus (240) < smp_cpus (242)", + }, +}; + static const struct SMPTestData data_with_clusters_invalid[] = { { /* config: -smp 16,sockets=2,clusters=2,cores=4,threads=2,maxcpus=16 */ @@ -373,67 +491,339 @@ 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, 0, 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, 0, 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, 0, 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, 0, 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=3,books=5,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=3,books=5,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,modules=3,cores=7,threads=2,\ + * maxcpus=200 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(200, 3, 5, 2, 4, 2, 3, 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) * modules (3) * cores (7) * threads (2) " + "!= maxcpus (200)", + }, { + /* + * config: -smp 2881,drawers=3,books=5,sockets=2,dies=4,\ + * clusters=2,modules=3,cores=2,threads=2, + * maxcpus=2880 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(2881, 3, 5, 2, 4, + 2, 3, 2, 2, 2880), + .expect_error = "Invalid CPU topology: " + "maxcpus must be equal to or greater than smp: " + "drawers (3) * books (5) * sockets (2) * " + "dies (4) * clusters (2) * modules (3) * " + "cores (2) * threads (2) == maxcpus (2880) " + "< smp_cpus (2881)", + }, { + /* + * config: -smp 1,drawers=3,books=5,sockets=2,dies=4,\ + * clusters=2,modules=3,cores=3,threads=3,\ + * maxcpus=6480 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 3, 5, 2, 4, 2, 3, 3, 3, 6480), + .expect_error = "Invalid SMP CPUs 6480. 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,modules=1,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(0, 1, 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,modules=1,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 0, 1, 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,modules=1,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 0, 1, 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,modules=1,cores=1,threads=1, + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 0, 1, 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,modules=1,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 0, 1, 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,modules=1,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 1, 1, 1, 1, 0, 1, 1, 1, 1), + .expect_error = "Invalid CPU topology: CPU topology parameters must " + "be greater than zero", + }, { + /* + * Test "modules=0". + * config: -smp 1,drawers=1,books=1,sockets=1,dies=1,\ + * clusters=1,modules=0,cores=1,threads=1,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 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,modules=1,cores=0,threads=1, + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 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,modules=1,cores=1,threads=0,\ + * maxcpus=1 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 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,modules=1,cores=1,threads=1,\ + * maxcpus=0 + */ + .config = SMP_CONFIG_WITH_FULL_TOPO(1, 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" + " .has_modules = %5s, modules = %" PRId64 ",\n" " .has_cores = %5s, cores = %" PRId64 ",\n" " .has_threads = %5s, threads = %" PRId64 ",\n" " .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, + config->has_modules ? "true" : "false", config->modules, config->has_cores ? "true" : "false", config->cores, config->has_threads ? "true" : "false", config->threads, 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" + " .modules = %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->modules, 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.modules == expect_topo->modules) && (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; } @@ -508,6 +898,11 @@ static void smp_parse_test(MachineState *ms, SMPTestData *data, bool is_valid) /* The parsed results of the unsupported parameters should be 1 */ static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) { + if (!mc->smp_props.modules_supported) { + data->expect_prefer_sockets.modules = 1; + data->expect_prefer_cores.modules = 1; + } + if (!mc->smp_props.dies_supported) { data->expect_prefer_sockets.dies = 1; data->expect_prefer_cores.dies = 1; @@ -517,6 +912,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 +939,15 @@ 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_modules_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.modules_supported = true; } static void machine_with_dies_class_init(ObjectClass *oc, void *data) @@ -545,6 +957,14 @@ static void machine_with_dies_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; } +static void machine_with_modules_dies_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->smp_props.modules_supported = true; + mc->smp_props.dies_supported = true; +} + static void machine_with_clusters_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -552,6 +972,39 @@ 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; + mc->smp_props.modules_supported = true; +} + static void test_generic_valid(const void *opaque) { const char *machine_type = opaque; @@ -566,11 +1019,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); @@ -595,6 +1043,56 @@ static void test_generic_invalid(const void *opaque) object_unref(obj); } +static void test_with_modules(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_modules = 2; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* when modules parameter is omitted, it will be set as 1 */ + data.expect_prefer_sockets.modules = 1; + data.expect_prefer_cores.modules = 1; + + smp_parse_test(ms, &data, true); + + /* when modules parameter is specified */ + data.config.has_modules = true; + data.config.modules = num_modules; + if (data.config.has_cpus) { + data.config.cpus *= num_modules; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_modules; + } + + data.expect_prefer_sockets.modules = num_modules; + data.expect_prefer_sockets.cpus *= num_modules; + data.expect_prefer_sockets.max_cpus *= num_modules; + data.expect_prefer_cores.modules = num_modules; + data.expect_prefer_cores.cpus *= num_modules; + data.expect_prefer_cores.max_cpus *= num_modules; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_modules_invalid); i++) { + data = data_with_modules_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + static void test_with_dies(const void *opaque) { const char *machine_type = opaque; @@ -645,6 +1143,67 @@ static void test_with_dies(const void *opaque) object_unref(obj); } +static void test_with_modules_dies(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_modules = 5, num_dies = 3; + int i; + + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* + * when modules and dies parameters are omitted, they will + * be both set as 1. + */ + data.expect_prefer_sockets.modules = 1; + data.expect_prefer_sockets.dies = 1; + data.expect_prefer_cores.modules = 1; + data.expect_prefer_cores.dies = 1; + + smp_parse_test(ms, &data, true); + + /* when modules and dies parameters are both specified */ + data.config.has_modules = true; + data.config.modules = num_modules; + data.config.has_dies = true; + data.config.dies = num_dies; + + if (data.config.has_cpus) { + data.config.cpus *= num_modules * num_dies; + } + if (data.config.has_maxcpus) { + data.config.maxcpus *= num_modules * num_dies; + } + + data.expect_prefer_sockets.modules = num_modules; + data.expect_prefer_sockets.dies = num_dies; + data.expect_prefer_sockets.cpus *= num_modules * num_dies; + data.expect_prefer_sockets.max_cpus *= num_modules * num_dies; + + data.expect_prefer_cores.modules = num_modules; + data.expect_prefer_cores.dies = num_dies; + data.expect_prefer_cores.cpus *= num_modules * num_dies; + data.expect_prefer_cores.max_cpus *= num_modules * num_dies; + + smp_parse_test(ms, &data, true); + } + + for (i = 0; i < ARRAY_SIZE(data_with_modules_dies_invalid); i++) { + data = data_with_modules_dies_invalid[i]; + unsupported_params_init(mc, &data); + + smp_parse_test(ms, &data, false); + } + + object_unref(obj); +} + static void test_with_clusters(const void *opaque) { const char *machine_type = opaque; @@ -695,6 +1254,263 @@ 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, books, dies, clusters, modules, multiplier; + int i; + + drawers = 5; + books = 3; + dies = 2; + clusters = 3; + modules = 2; + + multiplier = drawers * books * dies * clusters * modules; + for (i = 0; i < ARRAY_SIZE(data_generic_valid); i++) { + data = data_generic_valid[i]; + unsupported_params_init(mc, &data); + + /* + * when drawers, books, dies, clusters and modules 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_sockets.modules = 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; + data.expect_prefer_cores.modules = 1; + + smp_parse_test(ms, &data, true); + + /* + * when drawers, books, dies, clusters and modules 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; + data.config.has_modules = true; + data.config.modules = modules; + + 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.modules = modules; + 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.modules = modules; + 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[] = { { @@ -711,14 +1527,38 @@ static const TypeInfo smp_machine_types[] = { .name = MACHINE_TYPE_NAME("smp-generic-invalid"), .parent = TYPE_MACHINE, .class_init = machine_generic_invalid_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-modules"), + .parent = TYPE_MACHINE, + .class_init = machine_with_modules_class_init, }, { .name = MACHINE_TYPE_NAME("smp-with-dies"), .parent = TYPE_MACHINE, .class_init = machine_with_dies_class_init, + }, { + .name = MACHINE_TYPE_NAME("smp-with-modules-dies"), + .parent = TYPE_MACHINE, + .class_init = machine_with_modules_dies_class_init, }, { .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, } }; @@ -736,12 +1576,30 @@ int main(int argc, char *argv[]) g_test_add_data_func("/test-smp-parse/generic/invalid", MACHINE_TYPE_NAME("smp-generic-invalid"), test_generic_invalid); + g_test_add_data_func("/test-smp-parse/with_modules", + MACHINE_TYPE_NAME("smp-with-modules"), + test_with_modules); g_test_add_data_func("/test-smp-parse/with_dies", MACHINE_TYPE_NAME("smp-with-dies"), test_with_dies); + g_test_add_data_func("/test-smp-parse/with_modules_dies", + MACHINE_TYPE_NAME("smp-with-modules-dies"), + test_with_modules_dies); 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 667e8fed82..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, ==, diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c index 541bb4f63e..63f28f26f4 100644 --- a/tests/unit/test-vmstate.c +++ b/tests/unit/test-vmstate.c @@ -24,12 +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/savevm.h" -#include "qemu/coroutine.h" #include "qemu/module.h" #include "io/channel-file.h" @@ -198,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), @@ -300,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() } @@ -342,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. @@ -413,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), @@ -525,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() } @@ -543,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() @@ -631,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() @@ -686,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() @@ -697,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), @@ -822,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() \ @@ -840,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() \ @@ -916,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, @@ -941,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() } @@ -952,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), @@ -964,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), @@ -1074,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; } @@ -1083,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) @@ -1416,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() } @@ -1426,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), @@ -1437,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-cpuid.c deleted file mode 100644 index bfabc0403a..0000000000 --- a/tests/unit/test-x86-cpuid.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Test code for x86 CPUID and Topology functions - * - * Copyright (c) 2012 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 "hw/i386/topology.h" - -static void test_topo_bits(void) -{ - X86CPUTopoInfo topo_info = {0}; - - /* simple tests for 1 thread per core, 1 core per die, 1 die per package */ - topo_info = (X86CPUTopoInfo) {1, 1, 1}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - - topo_info = (X86CPUTopoInfo) {1, 1, 1}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3), ==, 3); - - - /* Test field width calculation for multiple values - */ - topo_info = (X86CPUTopoInfo) {1, 1, 2}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {1, 1, 3}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {1, 1, 4}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - - topo_info = (X86CPUTopoInfo) {1, 1, 14}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 15}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 16}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); - topo_info = (X86CPUTopoInfo) {1, 1, 17}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); - - - topo_info = (X86CPUTopoInfo) {1, 30, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 31, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 32, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); - topo_info = (X86CPUTopoInfo) {1, 33, 2}; - g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); - - topo_info = (X86CPUTopoInfo) {1, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); - topo_info = (X86CPUTopoInfo) {2, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); - topo_info = (X86CPUTopoInfo) {3, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); - topo_info = (X86CPUTopoInfo) {4, 30, 2}; - g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); - - /* build a weird topology and see if IDs are calculated correctly - */ - - /* This will use 2 bits for thread ID and 3 bits for core ID - */ - topo_info = (X86CPUTopoInfo) {1, 6, 3}; - g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); - g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); - g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); - g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); - - topo_info = (X86CPUTopoInfo) {1, 6, 3}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); - - topo_info = (X86CPUTopoInfo) {1, 6, 3}; - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, - (1 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, - (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 2), ==, - (1 << 2) | 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 0), ==, - (2 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 1), ==, - (2 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 2), ==, - (2 << 2) | 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 0), ==, - (5 << 2) | 0); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 1), ==, - (5 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 2), ==, - (5 << 2) | 2); - - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, - 1 * 6 * 3 + 0 * 3 + 0), ==, (1 << 5)); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, - 1 * 6 * 3 + 1 * 3 + 1), ==, (1 << 5) | (1 << 2) | 1); - g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, - 3 * 6 * 3 + 5 * 3 + 2), ==, (3 << 5) | (5 << 2) | 2); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - - g_test_add_func("/cpuid/topology/basic", test_topo_bits); - - g_test_run(); - - return 0; -} diff --git a/tests/unit/test-x86-topo.c b/tests/unit/test-x86-topo.c new file mode 100644 index 0000000000..55b731ccae --- /dev/null +++ b/tests/unit/test-x86-topo.c @@ -0,0 +1,152 @@ +/* + * Test code for x86 APIC ID and Topology functions + * + * Copyright (c) 2012 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 "hw/i386/topology.h" + +static void test_topo_bits(void) +{ + X86CPUTopoInfo topo_info = {0}; + + /* + * simple tests for 1 thread per core, 1 core per module, + * 1 module per die, 1 die per package + */ + topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 0); + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + + topo_info = (X86CPUTopoInfo) {1, 1, 1, 1}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 3), ==, 3); + + + /* Test field width calculation for multiple values + */ + topo_info = (X86CPUTopoInfo) {1, 1, 1, 2}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {1, 1, 1, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {1, 1, 1, 4}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + + topo_info = (X86CPUTopoInfo) {1, 1, 1, 14}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {1, 1, 1, 15}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {1, 1, 1, 16}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 4); + topo_info = (X86CPUTopoInfo) {1, 1, 1, 17}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 5); + + + topo_info = (X86CPUTopoInfo) {1, 1, 30, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {1, 1, 31, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {1, 1, 32, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 5); + topo_info = (X86CPUTopoInfo) {1, 1, 33, 2}; + g_assert_cmpuint(apicid_core_width(&topo_info), ==, 6); + + topo_info = (X86CPUTopoInfo) {1, 6, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3); + topo_info = (X86CPUTopoInfo) {1, 7, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3); + topo_info = (X86CPUTopoInfo) {1, 8, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 3); + topo_info = (X86CPUTopoInfo) {1, 9, 30, 2}; + g_assert_cmpuint(apicid_module_width(&topo_info), ==, 4); + + topo_info = (X86CPUTopoInfo) {1, 6, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 0); + topo_info = (X86CPUTopoInfo) {2, 6, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 1); + topo_info = (X86CPUTopoInfo) {3, 6, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); + topo_info = (X86CPUTopoInfo) {4, 6, 30, 2}; + g_assert_cmpuint(apicid_die_width(&topo_info), ==, 2); + + /* build a weird topology and see if IDs are calculated correctly + */ + + /* This will use 2 bits for thread ID and 3 bits for core ID + */ + topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; + g_assert_cmpuint(apicid_smt_width(&topo_info), ==, 2); + g_assert_cmpuint(apicid_core_offset(&topo_info), ==, 2); + g_assert_cmpuint(apicid_module_offset(&topo_info), ==, 5); + g_assert_cmpuint(apicid_die_offset(&topo_info), ==, 5); + g_assert_cmpuint(apicid_pkg_offset(&topo_info), ==, 5); + + topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 0), ==, 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1), ==, 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2), ==, 2); + + topo_info = (X86CPUTopoInfo) {1, 1, 6, 3}; + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 0), ==, + (1 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 1), ==, + (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 1 * 3 + 2), ==, + (1 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 0), ==, + (2 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 1), ==, + (2 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 2 * 3 + 2), ==, + (2 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 0), ==, + (5 << 2) | 0); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 1), ==, + (5 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, 5 * 3 + 2), ==, + (5 << 2) | 2); + + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, + 1 * 6 * 3 + 0 * 3 + 0), ==, (1 << 5)); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, + 1 * 6 * 3 + 1 * 3 + 1), ==, (1 << 5) | (1 << 2) | 1); + g_assert_cmpuint(x86_apicid_from_cpu_idx(&topo_info, + 3 * 6 * 3 + 5 * 3 + 2), ==, (3 << 5) | (5 << 2) | 2); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/cpuid/topology/basic", test_topo_bits); + + g_test_run(); + + return 0; +} 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..2f447a73fb --- /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_not_reached(); + } + 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_not_reached(); + } + + 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 fecdf915e7..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); diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 2cc2203d09..13ed80f72d 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -1,14 +1,12 @@ # Makefile for VM tests # Hack to allow running in an unconfigured build tree -ifeq ($(wildcard $(SRC_PATH)/config-host.mak),) +ifeq ($(realpath $(SRC_PATH)),$(realpath .)) VM_PYTHON = PYTHONPATH=$(SRC_PATH)/python /usr/bin/env python3 VM_VENV = -HOST_ARCH := $(shell uname -m) else -VM_PYTHON = $(TESTS_PYTHON) +VM_PYTHON = $(PYTHON) VM_VENV = check-venv -HOST_ARCH = $(ARCH) endif .PHONY: vm-build-all vm-clean-all @@ -23,6 +21,7 @@ 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) @@ -46,7 +45,6 @@ vm-help vm-test: @echo " vm-build-netbsd - Build QEMU in NetBSD VM" @echo " vm-build-openbsd - Build QEMU in OpenBSD VM" ifneq ($(GENISOIMAGE),) - @echo " vm-build-centos - Build QEMU in CentOS VM, with Docker" 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" @@ -82,7 +80,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)) @@ -103,7 +101,7 @@ $(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 $*") diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 2276364c42..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 @@ -311,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" % \ @@ -330,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) @@ -422,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) @@ -501,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 @@ -519,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): @@ -569,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, @@ -594,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. ") @@ -634,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 deleted file mode 100755 index 097a9ca14d..0000000000 --- a/tests/vm/centos +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -# -# CentOS 8 Stream image -# -# Copyright 2018, 2022 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 os -import sys -import subprocess -import basevm -import time - -class CentosVM(basevm.BaseVM): - name = "centos" - arch = "x86_64" - BUILD_SCRIPT = """ - set -e; - cd $(mktemp -d); - export SRC_ARCHIVE=/dev/vdb; - sudo chmod a+r $SRC_ARCHIVE; - tar -xf $SRC_ARCHIVE; - make docker-test-block@centos8 {verbose} J={jobs} NETWORK=1; - make docker-test-quick@centos8 {verbose} J={jobs} NETWORK=1; - """ - - def build_image(self, img): - 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(['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() - self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") - self.ssh_root_check("dnf update -y") - self.ssh_root_check("dnf install -y dnf-plugins-core") - self.ssh_root_check("dnf config-manager --set-enabled powertools") - self.ssh_root_check("dnf install -y podman make ninja-build git python3") - self.ssh_root("poweroff") - self.wait() - os.rename(img_tmp, img) - return 0 - -if __name__ == "__main__": - sys.exit(basevm.main(CentosVM)) diff --git a/tests/vm/centos.aarch64 b/tests/vm/centos.aarch64 index 2de7ef6992..fcf9e08c87 100755 --- a/tests/vm/centos.aarch64 +++ b/tests/vm/centos.aarch64 @@ -25,10 +25,10 @@ DEFAULT_CONFIG = { 'cpu' : "max", 'machine' : "virt,gic-version=max", 'install_cmds' : ( - "dnf config-manager --set-enabled powertools, " + "dnf config-manager --enable crb, " "dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo, " - "dnf install -y make ninja-build git python38 gcc gcc-c++ flex bison "\ - "glib2-devel perl pixman-devel zlib-devel docker-ce.aarch64, " + "dnf install -y make ninja-build git python3 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 @@ -38,10 +38,10 @@ DEFAULT_CONFIG = { class CentosAarch64VM(basevm.BaseVM): - name = "centos8.aarch64" + name = "centos9.aarch64" arch = "aarch64" - image_name = "CentOS-Stream-GenericCloud-8-20220125.1.aarch64.qcow2" - image_link = "https://cloud.centos.org/centos/8-stream/aarch64/images/" + image_name = "CentOS-Stream-GenericCloud-9-20230501.0.aarch64.qcow2" + image_link = "https://cloud.centos.org/centos/9-stream/aarch64/images/" image_link += image_name BUILD_SCRIPT = """ set -e; diff --git a/tests/vm/freebsd b/tests/vm/freebsd index d6ff4461ba..74b3b1e520 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,48 +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/14.1-RELEASE/amd64/Latest/FreeBSD-14.1-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "202fe27a05427f0a86d3ebb97712745186f2776ccc4f70d95466dd99a0238ba5" 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", - - # libs: networking - "libslirp", - - # libs: sndio - "sndio", - ] BUILD_SCRIPT = """ set -e; @@ -77,73 +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 --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 = [ "-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") @@ -165,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 = "$" @@ -182,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..3cb7fb7060 --- /dev/null +++ b/tests/vm/generated/freebsd.json @@ -0,0 +1,81 @@ +{ + "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", + "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", + "gtk-vnc", + "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", + "py311-numpy", + "py311-pillow", + "py311-pip", + "py311-pyyaml", + "py311-sphinx", + "py311-sphinx_rtd_theme", + "py311-tomli", + "python3", + "rpm2cpio", + "rust", + "rust-bindgen-cli", + "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 29668bc272..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" @@ -80,13 +80,12 @@ 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 diff --git a/tests/vm/netbsd b/tests/vm/netbsd index aa54338dfa..a3f6dd6b3c 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -30,7 +30,7 @@ class NetBSDVM(basevm.BaseVM): "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", @@ -66,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" diff --git a/tests/vm/openbsd b/tests/vm/openbsd index eaeb201e91..5e4f76f398 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.2/amd64/install72.iso" - csum = "0369ef40a3329efcb978c578c7fdc7bda71e502aecec930a74b44160928c91d3" + link = "https://cdn.openbsd.org/pub/OpenBSD/7.6/amd64/install76.iso" + csum = "60cba8cb391b50bba8fa10fc768bd0529636f5345d82133c93e22c798d8e5269" size = "20G" pkgs = [ # tools + "dtc", "git", "pkgconf", "bzip2", "xz", @@ -67,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" @@ -97,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") @@ -123,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 (p)assphrase", "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 666947393b..eeda281f87 100755 --- a/tests/vm/ubuntu.aarch64 +++ b/tests/vm/ubuntu.aarch64 @@ -25,7 +25,7 @@ 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, } 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..572ca5987a 100755 --- a/tools/ebpf/Makefile.ebpf +++ b/tools/ebpf/Makefile.ebpf @@ -1,21 +1,24 @@ -OBJS = rss.bpf.o +SKELETONS = rss.bpf.skeleton.h -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) +all: $(SKELETONS) .PHONY: clean clean: - rm -f $(OBJS) + rm -f $(SKELETONS) $(SKELETONS:%.skeleton.h=%.o) -$(OBJS): %.o:%.c +%.o: %.c $(CLANG) $(INC_FLAGS) \ -D__KERNEL__ -D__ASM_SYSREG_H \ -I../include $(LINUXINCLUDE) \ - $(EXTRA_CFLAGS) -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ - bpftool gen skeleton rss.bpf.o > rss.bpf.skeleton.h - cp rss.bpf.skeleton.h ../../ebpf/ + $(EXTRA_CFLAGS) -c $< -o $@ + $(LLVM_STRIP) -g $@ + +%.skeleton.h: %.o + bpftool gen skeleton $< > $@ + cp $@ ../../ebpf/ diff --git a/tools/ebpf/rss.bpf.c b/tools/ebpf/rss.bpf.c index e85ec55f9b..c989cb3cd8 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; @@ -380,18 +380,19 @@ error: return err; } -static inline __u32 calculate_rss_hash(struct __sk_buff *skb, - struct rss_config_t *config, struct toeplitz_key_data_t *toe) +static inline bool calculate_rss_hash(struct __sk_buff *skb, + struct rss_config_t *config, + struct toeplitz_key_data_t *toe, + __u32 *result) { __u8 rss_input[HASH_CALCULATION_BUFFER_SIZE] = {}; size_t bytes_written = 0; - __u32 result = 0; int err = 0; struct packet_hash_info_t packet_info = {}; err = parse_packet(skb, &packet_info); if (err) { - return 0; + return false; } if (packet_info.is_ipv4) { @@ -524,14 +525,16 @@ static inline __u32 calculate_rss_hash(struct __sk_buff *skb, } } - if (bytes_written) { - net_toeplitz_add(&result, rss_input, bytes_written, toe); + if (!bytes_written) { + return false; } - return result; + net_toeplitz_add(result, rss_input, bytes_written, toe); + + return true; } -SEC("tun_rss_steering") +SEC("socket") int tun_rss_steering_prog(struct __sk_buff *skb) { @@ -544,28 +547,23 @@ int tun_rss_steering_prog(struct __sk_buff *skb) config = bpf_map_lookup_elem(&tap_rss_map_configurations, &key); toe = bpf_map_lookup_elem(&tap_rss_map_toeplitz_key, &key); - if (config && toe) { - if (!config->redirect) { - return config->default_queue; - } - - hash = calculate_rss_hash(skb, config, toe); - if (hash) { - __u32 table_idx = hash % config->indirections_len; - __u16 *queue = 0; - - queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table, - &table_idx); - - if (queue) { - return *queue; - } - } - - return config->default_queue; + if (!config || !toe) { + return 0; } - return -1; + if (config->redirect && calculate_rss_hash(skb, config, toe, &hash)) { + __u32 table_idx = hash % config->indirections_len; + __u16 *queue = 0; + + queue = bpf_map_lookup_elem(&tap_rss_map_indirection_table, + &table_idx); + + if (queue) { + return *queue; + } + } + + return config->default_queue; } char _license[] SEC("license") = "GPL v2"; diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c new file mode 100644 index 0000000000..a35dcb88a3 --- /dev/null +++ b/tools/i386/qemu-vmsr-helper.c @@ -0,0 +1,540 @@ +/* + * Privileged RAPL MSR helper commands for QEMU + * + * Copyright (C) 2024 Red Hat, Inc. + * + * Author: Anthony Harivel + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include +#include +#include +#ifdef CONFIG_LIBCAP_NG +#include +#endif +#include +#include + +#include "qemu/help-texts.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/config-file.h" +#include "qemu-version.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/systemd.h" +#include "io/channel.h" +#include "io/channel-socket.h" +#include "trace/control.h" +#include "qemu-version.h" +#include "rapl-msr-index.h" + +#define MSR_PATH_TEMPLATE "/dev/cpu/%u/msr" + +static char *socket_path; +static char *pidfile; +static enum { RUNNING, TERMINATE, TERMINATING } state; +static QIOChannelSocket *server_ioc; +static int server_watch; +static int num_active_sockets = 1; +static bool verbose; + +#ifdef CONFIG_LIBCAP_NG +static int uid = -1; +static int gid = -1; +#endif + +static void compute_default_paths(void) +{ + g_autofree char *state = qemu_get_local_state_dir(); + + socket_path = g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL); + pidfile = g_build_filename(state, "run", "qemu-vmsr-helper.pid", NULL); +} + +static int is_intel_processor(void) +{ + int result; + int ebx, ecx, edx; + + /* Execute CPUID instruction with eax=0 (basic identification) */ + asm volatile ( + "cpuid" + : "=b" (ebx), "=c" (ecx), "=d" (edx) + : "a" (0) + ); + + /* + * Check if processor is "GenuineIntel" + * 0x756e6547 = "Genu" + * 0x49656e69 = "ineI" + * 0x6c65746e = "ntel" + */ + result = (ebx == 0x756e6547) && (edx == 0x49656e69) && (ecx == 0x6c65746e); + + return result; +} + +static int is_rapl_enabled(void) +{ + const char *path = "/sys/class/powercap/intel-rapl/enabled"; + FILE *file = fopen(path, "r"); + int value = 0; + + if (file != NULL) { + if (fscanf(file, "%d", &value) != 1) { + error_report("INTEL RAPL not enabled"); + } + fclose(file); + } else { + error_report("Error opening %s", path); + } + + return value; +} + +/* + * Check if the TID that request the MSR read + * belongs to the peer. It be should a TID of a vCPU. + */ +static bool is_tid_present(pid_t pid, pid_t tid) +{ + g_autofree char *tidPath = g_strdup_printf("/proc/%d/task/%d", pid, tid); + + /* Check if the TID directory exists within the PID directory */ + if (access(tidPath, F_OK) == 0) { + return true; + } + + error_report("Failed to open /proc at %s", tidPath); + return false; +} + +/* + * Only the RAPL MSR in target/i386/cpu.h are allowed + */ +static bool is_msr_allowed(uint32_t reg) +{ + switch (reg) { + case MSR_RAPL_POWER_UNIT: + case MSR_PKG_POWER_LIMIT: + case MSR_PKG_ENERGY_STATUS: + case MSR_PKG_POWER_INFO: + return true; + default: + return false; + } +} + +static uint64_t vmsr_read_msr(uint32_t msr_register, unsigned int cpu_id) +{ + int fd; + uint64_t result = 0; + + g_autofree char *path = g_strdup_printf(MSR_PATH_TEMPLATE, cpu_id); + + fd = open(path, O_RDONLY); + if (fd < 0) { + error_report("Failed to open MSR file at %s", path); + return result; + } + + if (pread(fd, &result, sizeof(result), msr_register) != sizeof(result)) { + error_report("Failed to read MSR"); + result = 0; + } + + close(fd); + return result; +} + +static void usage(const char *name) +{ + (printf) ( +"Usage: %s [OPTIONS] FILE\n" +"Virtual RAPL MSR helper program for QEMU\n" +"\n" +" -h, --help display this help and exit\n" +" -V, --version output version information and exit\n" +"\n" +" -d, --daemon run in the background\n" +" -f, --pidfile=PATH PID file when running as a daemon\n" +" (default '%s')\n" +" -k, --socket=PATH path to the unix socket\n" +" (default '%s')\n" +" -T, --trace [[enable=]][,events=][,file=]\n" +" specify tracing options\n" +#ifdef CONFIG_LIBCAP_NG +" -u, --user=USER user to drop privileges to\n" +" -g, --group=GROUP group to drop privileges to\n" +#endif +"\n" +QEMU_HELP_BOTTOM "\n" + , name, pidfile, socket_path); +} + +static void version(const char *name) +{ + printf( +"%s " QEMU_FULL_VERSION "\n" +"Written by Anthony Harivel.\n" +"\n" +QEMU_COPYRIGHT "\n" +"This is free software; see the source for copying conditions. There is NO\n" +"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + , name); +} + +typedef struct VMSRHelperClient { + QIOChannelSocket *ioc; + Coroutine *co; +} VMSRHelperClient; + +static void coroutine_fn vh_co_entry(void *opaque) +{ + VMSRHelperClient *client = opaque; + Error *local_err = NULL; + unsigned int peer_pid; + uint32_t request[3]; + uint64_t vmsr; + int r; + + qio_channel_set_blocking(QIO_CHANNEL(client->ioc), + false, NULL); + + qio_channel_set_follow_coroutine_ctx(QIO_CHANNEL(client->ioc), true); + + /* + * Check peer credentials + */ + r = qio_channel_get_peerpid(QIO_CHANNEL(client->ioc), + &peer_pid, + &local_err); + if (r < 0) { + goto out; + } + + for (;;) { + /* + * Read the requested MSR + * Only RAPL MSR in rapl-msr-index.h is allowed + */ + r = qio_channel_read_all_eof(QIO_CHANNEL(client->ioc), + (char *) &request, sizeof(request), &local_err); + if (r <= 0) { + break; + } + + if (!is_msr_allowed(request[0])) { + error_report("Requested unallowed msr: %d", request[0]); + break; + } + + vmsr = vmsr_read_msr(request[0], request[1]); + + if (!is_tid_present(peer_pid, request[2])) { + error_report("Requested TID not in peer PID: %d %d", + peer_pid, request[2]); + vmsr = 0; + } + + r = qio_channel_write_all(QIO_CHANNEL(client->ioc), + (char *) &vmsr, + sizeof(vmsr), + &local_err); + if (r < 0) { + break; + } + } + +out: + if (local_err) { + if (!verbose) { + error_free(local_err); + } else { + error_report_err(local_err); + } + } + + object_unref(OBJECT(client->ioc)); + g_free(client); +} + +static gboolean accept_client(QIOChannel *ioc, + GIOCondition cond, + gpointer opaque) +{ + QIOChannelSocket *cioc; + VMSRHelperClient *vmsrh; + + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; + } + + vmsrh = g_new(VMSRHelperClient, 1); + vmsrh->ioc = cioc; + vmsrh->co = qemu_coroutine_create(vh_co_entry, vmsrh); + qemu_coroutine_enter(vmsrh->co); + + return TRUE; +} + +static void termsig_handler(int signum) +{ + qatomic_cmpxchg(&state, RUNNING, TERMINATE); + qemu_notify_event(); +} + +static void close_server_socket(void) +{ + assert(server_ioc); + + g_source_remove(server_watch); + server_watch = -1; + object_unref(OBJECT(server_ioc)); + num_active_sockets--; +} + +#ifdef CONFIG_LIBCAP_NG +static int drop_privileges(void) +{ + /* clear all capabilities */ + capng_clear(CAPNG_SELECT_BOTH); + + if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, + CAP_SYS_RAWIO) < 0) { + return -1; + } + + return 0; +} +#endif + +int main(int argc, char **argv) +{ + const char *sopt = "hVk:f:dT:u:g:vq"; + struct option lopt[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "socket", required_argument, NULL, 'k' }, + { "pidfile", required_argument, NULL, 'f' }, + { "daemon", no_argument, NULL, 'd' }, + { "trace", required_argument, NULL, 'T' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } + }; + int opt_ind = 0; + int ch; + Error *local_err = NULL; + bool daemonize = false; + bool pidfile_specified = false; + bool socket_path_specified = false; + unsigned socket_activation; + + struct sigaction sa_sigterm; + memset(&sa_sigterm, 0, sizeof(sa_sigterm)); + sa_sigterm.sa_handler = termsig_handler; + sigaction(SIGTERM, &sa_sigterm, NULL); + sigaction(SIGINT, &sa_sigterm, NULL); + sigaction(SIGHUP, &sa_sigterm, NULL); + + signal(SIGPIPE, SIG_IGN); + + error_init(argv[0]); + module_call_init(MODULE_INIT_TRACE); + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_trace_opts); + qemu_init_exec_dir(argv[0]); + + compute_default_paths(); + + /* + * Sanity check + * 1. cpu must be Intel cpu + * 2. RAPL must be enabled + */ + if (!is_intel_processor()) { + error_report("error: CPU is not INTEL cpu"); + exit(EXIT_FAILURE); + } + + if (!is_rapl_enabled()) { + error_report("error: RAPL driver not enable"); + exit(EXIT_FAILURE); + } + + while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { + switch (ch) { + case 'k': + g_free(socket_path); + socket_path = g_strdup(optarg); + socket_path_specified = true; + if (socket_path[0] != '/') { + error_report("socket path must be absolute"); + exit(EXIT_FAILURE); + } + break; + case 'f': + g_free(pidfile); + pidfile = g_strdup(optarg); + pidfile_specified = true; + break; +#ifdef CONFIG_LIBCAP_NG + case 'u': { + unsigned long res; + struct passwd *userinfo = getpwnam(optarg); + if (userinfo) { + uid = userinfo->pw_uid; + } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && + (uid_t)res == res) { + uid = res; + } else { + error_report("invalid user '%s'", optarg); + exit(EXIT_FAILURE); + } + break; + } + case 'g': { + unsigned long res; + struct group *groupinfo = getgrnam(optarg); + if (groupinfo) { + gid = groupinfo->gr_gid; + } else if (qemu_strtoul(optarg, NULL, 10, &res) == 0 && + (gid_t)res == res) { + gid = res; + } else { + error_report("invalid group '%s'", optarg); + exit(EXIT_FAILURE); + } + break; + } +#else + case 'u': + case 'g': + error_report("-%c not supported by this %s", ch, argv[0]); + exit(1); +#endif + case 'd': + daemonize = true; + break; + case 'v': + verbose = true; + break; + case 'T': + trace_opt_parse(optarg); + break; + case 'V': + version(argv[0]); + exit(EXIT_SUCCESS); + break; + case 'h': + usage(argv[0]); + exit(EXIT_SUCCESS); + break; + case '?': + error_report("Try `%s --help' for more information.", argv[0]); + exit(EXIT_FAILURE); + } + } + + if (!trace_init_backends()) { + exit(EXIT_FAILURE); + } + trace_init_file(); + qemu_set_log(LOG_TRACE, &error_fatal); + + socket_activation = check_socket_activation(); + if (socket_activation == 0) { + SocketAddress saddr; + saddr = (SocketAddress){ + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = socket_path, + }; + server_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, &saddr, + 1, &local_err) < 0) { + object_unref(OBJECT(server_ioc)); + error_report_err(local_err); + return 1; + } + } else { + /* Using socket activation - check user didn't use -p etc. */ + if (socket_path_specified) { + error_report("Unix socket can't be set when" + "using socket activation"); + exit(EXIT_FAILURE); + } + + /* Can only listen on a single socket. */ + if (socket_activation > 1) { + error_report("%s does not support socket activation" + "with LISTEN_FDS > 1", + argv[0]); + exit(EXIT_FAILURE); + } + server_ioc = qio_channel_socket_new_fd(FIRST_SOCKET_ACTIVATION_FD, + &local_err); + if (server_ioc == NULL) { + error_reportf_err(local_err, + "Failed to use socket activation: "); + exit(EXIT_FAILURE); + } + } + + qemu_init_main_loop(&error_fatal); + + server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), + G_IO_IN, + accept_client, + NULL, NULL); + + if (daemonize) { + if (daemon(0, 0) < 0) { + error_report("Failed to daemonize: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (daemonize || pidfile_specified) { + qemu_write_pidfile(pidfile, &error_fatal); + } + +#ifdef CONFIG_LIBCAP_NG + if (drop_privileges() < 0) { + error_report("Failed to drop privileges: %s", strerror(errno)); + exit(EXIT_FAILURE); + } +#endif + + info_report("Listening on %s", socket_path); + + state = RUNNING; + do { + main_loop_wait(false); + if (state == TERMINATE) { + state = TERMINATING; + close_server_socket(); + } + } while (num_active_sockets > 0); + + exit(EXIT_SUCCESS); +} diff --git a/tools/i386/rapl-msr-index.h b/tools/i386/rapl-msr-index.h new file mode 100644 index 0000000000..9a7118639a --- /dev/null +++ b/tools/i386/rapl-msr-index.h @@ -0,0 +1,28 @@ +/* + * Allowed list of MSR for Privileged RAPL MSR helper commands for QEMU + * + * Copyright (C) 2023 Red Hat, Inc. + * + * Author: Anthony Harivel + * + * 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; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * Should stay in sync with the RAPL MSR + * in target/i386/cpu.h + */ +#define MSR_RAPL_POWER_UNIT 0x00000606 +#define MSR_PKG_POWER_LIMIT 0x00000610 +#define MSR_PKG_ENERGY_STATUS 0x00000611 +#define MSR_PKG_POWER_INFO 0x00000614 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 2f08471627..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%016" PRIx64 "\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%" PRIx64 " 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 9368e292e4..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 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 20f0f41f99..0000000000 --- a/tools/virtiofsd/passthrough_ll.c +++ /dev/null @@ -1,4520 +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 { - /* - * Either user specified to disable killpriv_v2, or did not - * specify anything. 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 setting. - */ - 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; - char buf[64]; - - 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.%%06d%z"); - snprintf(buf, 64, nowstr, g_date_time_get_microsecond(now)); - localfmt = g_strdup_printf("[%s] [ID: %08ld] %s", - buf, 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 0033dab493..0000000000 --- a/tools/virtiofsd/passthrough_seccomp.c +++ /dev/null @@ -1,182 +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(sigreturn), - 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 035f3d570d..3ec8a6c720 100644 --- a/trace-events +++ b/trace-events @@ -30,22 +30,6 @@ breakpoint_insert(int cpu_index, uint64_t pc, int flags) "cpu=%d pc=0x%" PRIx64 breakpoint_remove(int cpu_index, uint64_t pc, int flags) "cpu=%d pc=0x%" PRIx64 " flags=0x%x" breakpoint_singlestep(int cpu_index, int enabled) "cpu=%d enable=%d" -# dma-helpers.c -dma_blk_io(void *dbs, void *bs, int64_t offset, bool to_dev) "dbs=%p bs=%p offset=%" PRId64 " to_dev=%d" -dma_aio_cancel(void *dbs) "dbs=%p" -dma_complete(void *dbs, int ret, void *cb) "dbs=%p ret=%d cb=%p" -dma_blk_cb(void *dbs, int ret) "dbs=%p ret=%d" -dma_map_wait(void *dbs) "dbs=%p" - -# exec.c -find_ram_offset(uint64_t size, uint64_t offset) "size: 0x%" PRIx64 " @ 0x%" PRIx64 -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 - # 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)" @@ -58,53 +42,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 232c97a4a1..d58e84f6dd 100644 --- a/trace/control-target.c +++ b/trace/control-target.c @@ -8,8 +8,8 @@ */ #include "qemu/osdep.h" +#include "qemu/lockable.h" #include "cpu.h" -#include "trace/trace-root.h" #include "trace/control.h" @@ -35,115 +35,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); - tcg_flush_jmp_cache(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..1c8c50064a 100644 --- a/trace/control.c +++ b/trace/control.c @@ -27,7 +27,6 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" #include "monitor/monitor.h" -#include "trace/trace-root.h" int trace_events_enabled_count; @@ -68,16 +67,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 +261,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 +284,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..074a27b204 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); @@ -67,25 +48,15 @@ static bool check_events(bool has_vcpu, bool ignore_unavailable, bool is_pattern } 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 +64,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); @@ -130,24 +85,14 @@ TraceEventInfoList *qmp_trace_event_get_state(const char *name, void qmp_trace_event_set_state(const char *name, bool enable, bool has_ignore_unavailable, bool ignore_unavailable, - 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 +100,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..d38dd600de --- /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, &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, &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 3d14bffaf8..132086eb13 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -65,12 +65,24 @@ bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client) 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) { @@ -132,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); } @@ -141,6 +155,8 @@ void qemu_clipboard_reset_serial(void) QemuClipboardNotify notify = { .type = QEMU_CLIPBOARD_RESET_SERIAL }; int i; + trace_clipboard_reset_serial(); + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { QemuClipboardInfo *info = qemu_clipboard_info(i); if (info) { @@ -163,9 +179,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 660d3e0935..dd88115dc6 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #import +#import #include #include "qemu/help-texts.h" @@ -46,20 +47,12 @@ #include "qemu/cutils.h" #include "qemu/main-loop.h" #include "qemu/module.h" +#include "qemu/error-report.h" #include #include "hw/core/cpu.h" -#ifndef MAC_OS_X_VERSION_10_13 -#define MAC_OS_X_VERSION_10_13 101300 -#endif - -/* 10.14 deprecates NSOnState and NSOffState in favor of - * NSControlStateValueOn/Off, which were introduced in 10.13. - * Define for older versions - */ -#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 -#define NSControlStateValueOn NSOnState -#define NSControlStateValueOff NSOffState +#ifndef MAC_OS_VERSION_14_0 +#define MAC_OS_VERSION_14_0 140000 #endif //#define DEBUG @@ -72,6 +65,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,23 +80,26 @@ static void cocoa_switch(DisplayChangeListener *dcl, DisplaySurface *surface); static void cocoa_refresh(DisplayChangeListener *dcl); +static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on); +static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor); -static NSWindow *normalWindow; static const DisplayChangeListenerOps dcl_ops = { .dpy_name = "cocoa", .dpy_gfx_update = cocoa_update, .dpy_gfx_switch = cocoa_switch, .dpy_refresh = cocoa_refresh, + .dpy_mouse_set = cocoa_mouse_set, + .dpy_cursor_define = cocoa_cursor_define, }; 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 bool stretch_video; +static CGInterpolationQuality zoom_interpolation = kCGInterpolationNone; static NSTextField *pauseLabel; static bool allow_events; @@ -109,33 +108,33 @@ 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; } @@ -301,36 +300,34 @@ 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; + /* The state surrounding mouse grabbing is potentially confusing. + * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated + * pointing device an absolute-position one?"], but is only updated on + * next refresh. + * isMouseGrabbed tracks whether GUI events are directed to the guest; + * it controls whether special keys like Cmd get sent to the guest, + * and whether we capture the mouse when in non-absolute mode. + */ BOOL isMouseGrabbed; - BOOL isFullscreen; BOOL isAbsoluteEnabled; CFMachPortRef eventsTap; + CGColorSpaceRef colorspace; + CALayer *cursorLayer; + QEMUCursor *cursor; + int mouseX; + int mouseY; + bool mouseOn; } - (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; - (void) ungrabMouse; -- (void) toggleFullScreen:(id)sender; - (void) setFullGrab:(id)sender; - (void) handleMonitorInput:(NSEvent *)event; - (bool) handleEvent:(NSEvent *)event; - (bool) handleEventLocked:(NSEvent *)event; -- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; -/* The state surrounding mouse grabbing is potentially confusing. - * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated - * pointing device an absolute-position one?"], but is only updated on - * next refresh. - * isMouseGrabbed tracks whether GUI events are directed to the guest; - * it controls whether special keys like Cmd get sent to the guest, - * and whether we capture the mouse when in non-absolute mode. - */ +- (void) notifyMouseModeChange; - (BOOL) isMouseGrabbed; -- (BOOL) isAbsoluteEnabled; -- (float) cdx; -- (float) cdy; - (QEMUScreen) gscreen; - (void) raiseAllKeys; @end @@ -339,9 +336,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; } @@ -358,9 +355,31 @@ 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); + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_14_0 + [self setClipsToBounds:YES]; +#endif + [self setWantsLayer:YES]; + cursorLayer = [[CALayer alloc] init]; + [cursorLayer setAnchorPoint:CGPointMake(0, 1)]; + [cursorLayer setAutoresizingMask:kCALayerMaxXMargin | + kCALayerMinYMargin]; + [[self layer] addSublayer:cursorLayer]; } return self; @@ -374,12 +393,13 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven pixman_image_unref(pixman_image); } - qkbd_state_free(kbd); - if (eventsTap) { CFRelease(eventsTap); } + CGColorSpaceRelease(colorspace); + [cursorLayer release]; + cursor_unref(cursor); [super dealloc]; } @@ -388,44 +408,24 @@ 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 notifyMouseModeChange]; + [self updateUIInfo]; } - (void) hideCursor @@ -444,6 +444,72 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven [NSCursor unhide]; } +- (void)setMouseX:(int)x y:(int)y on:(bool)on +{ + CGPoint position; + + mouseX = x; + mouseY = y; + mouseOn = on; + + position.x = mouseX; + position.y = screen.height - mouseY; + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [cursorLayer setPosition:position]; + [cursorLayer setHidden:!mouseOn]; + [CATransaction commit]; +} + +- (void)setCursor:(QEMUCursor *)given_cursor +{ + CGDataProviderRef provider; + CGImageRef image; + CGRect bounds = CGRectZero; + + cursor_unref(cursor); + cursor = given_cursor; + + if (!cursor) { + return; + } + + cursor_ref(cursor); + + bounds.size.width = cursor->width; + bounds.size.height = cursor->height; + + provider = CGDataProviderCreateWithData( + NULL, + cursor->data, + cursor->width * cursor->height * 4, + NULL + ); + + image = CGImageCreate( + cursor->width, //width + cursor->height, //height + 8, //bitsPerComponent + 32, //bitsPerPixel + cursor->width * 4, //bytesPerRow + colorspace, //colorspace + kCGBitmapByteOrder32Little | kCGImageAlphaFirst, //bitmapInfo + provider, //provider + NULL, //decode + 0, //interpolate + kCGRenderingIntentDefault //intent + ); + + CGDataProviderRelease(provider); + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [cursorLayer setBounds:bounds]; + [cursorLayer setContents:(id)image]; + [CATransaction commit]; + CGImageRelease(image); +} + - (void) drawRect:(NSRect) rect { COCOA_DEBUG("QemuCocoaView: drawRect\n"); @@ -451,7 +517,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 @@ -477,7 +543,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven DIV_ROUND_UP(bitsPerPixel, 8) * 2, //bitsPerComponent bitsPerPixel, //bitsPerPixel stride, //bytesPerRow - CGColorSpaceCreateWithName(kCGColorSpaceSRGB), //colorspace + colorspace, //colorspace kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, //bitmapInfo dataProviderRef, //provider NULL, //decode @@ -493,10 +559,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 @@ -509,42 +573,78 @@ 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)]; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + - (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; @@ -557,9 +657,10 @@ 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 ? screenSize : [self frame].size; + frameSize = isFullscreen ? [self screenSafeAreaSize] : [self frame].size; if (!CVDisplayLinkCreateWithCGDisplay(display, &displayLink)) { CVTime period = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink); @@ -587,6 +688,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven dpy_set_ui_info(dcl.con, &info, TRUE); } +#pragma clang diagnostic pop + - (void) updateUIInfo { if (!allow_events) { @@ -601,36 +704,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 @@ -639,51 +731,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 @@ -780,13 +827,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 { - return bool_with_iothread_lock(^{ + return bool_with_bql(^{ return [self handleEventLocked:event]; }); } @@ -795,11 +843,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; - // Location of event in virtual screen coordinates - NSPoint p = [self screenLocationOfEvent:event]; NSUInteger modifiers = [event modifierFlags]; /* @@ -943,7 +988,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven } break; } - break; + return true; case NSEventTypeKeyDown: keycode = cocoa_keycode_to_qemu([event keyCode]); @@ -963,7 +1008,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 @@ -974,12 +1019,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]); @@ -989,156 +1034,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:] @@ -1148,27 +1191,36 @@ 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 { +- (void) notifyMouseModeChange { + bool tIsAbsoluteEnabled = bool_with_bql(^{ + return qemu_input_is_absolute(dcl.con); + }); + + if (tIsAbsoluteEnabled == isAbsoluteEnabled) { + return; + } + isAbsoluteEnabled = tIsAbsoluteEnabled; + if (isMouseGrabbed) { - CGAssociateMouseAndMouseCursorPosition(isAbsoluteEnabled); + if (isAbsoluteEnabled) { + [self ungrabMouse]; + } else { + CGAssociateMouseAndMouseCursorPosition(isAbsoluteEnabled); + } } } - (BOOL) isMouseGrabbed {return isMouseGrabbed;} -- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} -- (float) cdx {return cdx;} -- (float) cdy {return cdy;} - (QEMUScreen) gscreen {return screen;} /* @@ -1178,10 +1230,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 @@ -1196,7 +1257,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; @@ -1217,6 +1277,8 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven @implementation QemuCocoaAppController - (id) init { + NSWindow *window; + COCOA_DEBUG("QemuCocoaAppController: init\n"); self = [super init]; @@ -1230,20 +1292,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]; @@ -1279,7 +1341,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven { COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); - with_iothread_lock(^{ + with_bql(^{ shutdown_action = SHUTDOWN_ACTION_POWEROFF; qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); }); @@ -1309,8 +1371,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]; } @@ -1327,10 +1401,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]; } @@ -1341,14 +1428,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 @@ -1395,10 +1475,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]; } } @@ -1406,13 +1496,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]; @@ -1423,7 +1515,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]; @@ -1436,8 +1528,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)]; @@ -1453,7 +1545,7 @@ static CGEventRef handleTapEvent(CGEventTapProxy proxy, CGEventType type, CGEven /* Restarts QEMU */ - (void)restartQEMU:(id)sender { - with_iothread_lock(^{ + with_bql(^{ qmp_system_reset(NULL); }); } @@ -1461,7 +1553,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); }); } @@ -1480,9 +1572,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); } @@ -1515,14 +1607,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); @@ -1598,7 +1689,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(), '%'); @@ -1663,7 +1754,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]; @@ -1799,6 +1895,17 @@ static void addRemovableDevicesMenuItems(void) qapi_free_BlockInfoList(pointerToFree); } +static void cocoa_mouse_mode_change_notify(Notifier *notifier, void *data) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView notifyMouseModeChange]; + }); +} + +static Notifier mouse_mode_change_notifier = { + .notify = cocoa_mouse_mode_change_notify +}; + @interface QemuCocoaPasteboardTypeOwner : NSObject @end @@ -1810,7 +1917,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); @@ -1818,9 +1925,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) { @@ -1918,21 +2025,21 @@ static void *call_qemu_main(void *opaque) int status; COCOA_DEBUG("Second thread: calling qemu_default_main()\n"); - qemu_mutex_lock_iothread(); + bql_lock(); status = qemu_default_main(); - qemu_mutex_unlock_iothread(); + bql_unlock(); COCOA_DEBUG("Second thread: qemu_default_main() returned, exiting\n"); [cbowner release]; exit(status); } -static int cocoa_main() +static int cocoa_main(void) { QemuThread thread; COCOA_DEBUG("Entered %s()\n", __func__); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_thread_create(&thread, "qemu_main", call_qemu_main, NULL, QEMU_THREAD_DETACHED); @@ -1953,16 +2060,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]; }); } @@ -1981,7 +2079,6 @@ static void cocoa_switch(DisplayChangeListener *dcl, pixman_image_ref(image); dispatch_async(dispatch_get_main_queue(), ^{ - [cocoaView updateUIInfo]; [cocoaView switchSurface:image]; }); } @@ -1991,18 +2088,7 @@ static void cocoa_refresh(DisplayChangeListener *dcl) NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); - graphic_hw_update(NULL); - - if (qemu_input_is_absolute()) { - dispatch_async(dispatch_get_main_queue(), ^{ - if (![cocoaView isAbsoluteEnabled]) { - if ([cocoaView isMouseGrabbed]) { - [cocoaView ungrabMouse]; - } - } - [cocoaView setAbsoluteEnabled:YES]; - }); - } + graphic_hw_update(dcl->con); if (cbchangecount != [[NSPasteboard generalPasteboard] changeCount]) { qemu_clipboard_info_unref(cbinfo); @@ -2018,6 +2104,21 @@ static void cocoa_refresh(DisplayChangeListener *dcl) [pool release]; } +static void cocoa_mouse_set(DisplayChangeListener *dcl, int x, int y, bool on) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView setMouseX:x y:y on:on]; + }); +} + +static void cocoa_cursor_define(DisplayChangeListener *dcl, QEMUCursor *cursor) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + BQL_LOCK_GUARD(); + [cocoaView setCursor:qemu_console_get_cursor(dcl->con)]; + }); +} + static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; @@ -2033,26 +2134,13 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) [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 *controller = [[QemuCocoaAppController alloc] init]; [NSApp setDelegate:controller]; /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { - [NSApp activateIgnoringOtherApps: YES]; - [controller toggleFullScreen: nil]; + [[cocoaView window] toggleFullScreen: nil]; } if (opts->u.cocoa.has_full_grab && opts->u.cocoa.full_grab) { [controller setFullGrab: nil]; @@ -2069,8 +2157,33 @@ 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); + qemu_add_mouse_mode_change_notifier(&mouse_mode_change_notifier); + [cocoaView notifyMouseModeChange]; + [cocoaView updateUIInfo]; qemu_event_init(&cbevent, false); cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init]; 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..53fcee88f4 --- /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_bufptr(&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; + g_autofree char *response = NULL; + + 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 */ + response = g_strdup_printf("\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 646202214a..5165f17125 100644 --- a/ui/console.c +++ b/ui/console.c @@ -27,132 +27,57 @@ #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 +#include "qemu/memfd.h" -#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; + bool 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) { @@ -184,19 +109,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) { @@ -207,9 +124,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) @@ -222,7 +136,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; } @@ -235,6 +148,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"); @@ -280,872 +209,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); - 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; -} - -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) @@ -1184,12 +259,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) { @@ -1219,142 +294,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, 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] = { @@ -1382,7 +329,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; @@ -1390,96 +337,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. @@ -1487,41 +369,110 @@ 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) +{ +} + +void qemu_displaysurface_set_share_handle(DisplaySurface *surface, + qemu_pixman_shareable handle, + uint32_t offset) +{ + assert(surface->share_handle == SHAREABLE_NONE); + + surface->share_handle = handle; + surface->share_handle_offset = offset; + } DisplaySurface *qemu_create_displaysurface(int width, int height) { - DisplaySurface *surface = g_new0(DisplaySurface, 1); + trace_displaysurface_create(width, height); - 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); - surface->flags = QEMU_ALLOCATED_FLAG; - - return surface; + return qemu_create_displaysurface_from( + width, height, + PIXMAN_x8r8g8b8, + width * 4, NULL + ); } DisplaySurface *qemu_create_displaysurface_from(int width, int height, @@ -1531,12 +482,25 @@ 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, - width, height, - (void *)data, linesize); - assert(surface->image != NULL); + surface->share_handle = SHAREABLE_NONE; + if (data) { + surface->image = pixman_image_create_bits(format, + width, height, + (void *)data, linesize); + } else { + qemu_pixman_image_new_shareable(&surface->image, + &surface->share_handle, + "displaysurface", + format, + width, + height, + linesize, + &error_abort); + surface->flags = QEMU_ALLOCATED_FLAG; + } + + assert(surface->image != NULL); return surface; } @@ -1545,7 +509,7 @@ 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->share_handle = SHAREABLE_NONE; surface->image = pixman_image_ref(image); return surface; @@ -1555,8 +519,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; @@ -1569,6 +534,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; } @@ -1631,6 +597,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 */ @@ -1642,10 +673,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); @@ -1654,12 +694,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, @@ -1688,14 +730,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; @@ -1703,19 +746,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; } @@ -1754,7 +791,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) { @@ -1777,6 +814,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; @@ -1790,19 +828,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); @@ -1853,7 +891,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) { @@ -1871,7 +909,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) { @@ -1889,7 +927,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) { @@ -1898,16 +936,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, bool 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) { @@ -1916,16 +958,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) { @@ -1934,19 +979,6 @@ void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor) } } -bool dpy_cursor_define_supported(QemuConsole *con) -{ - DisplayState *s = con->ds; - DisplayChangeListener *dcl; - - QLIST_FOREACH(dcl, &s->listeners, next) { - if (dcl->ops->dpy_cursor_define) { - return true; - } - } - return false; -} - QEMUGLContext dpy_gl_ctx_create(QemuConsole *con, struct QEMUGLParams *qparams) { @@ -1975,7 +1007,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) { @@ -1990,7 +1022,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; @@ -1998,17 +1031,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); } } } @@ -2022,7 +1056,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) { @@ -2038,7 +1072,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) { @@ -2055,7 +1089,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) { @@ -2071,7 +1105,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) { @@ -2090,7 +1124,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) { @@ -2108,8 +1142,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; } @@ -2123,14 +1155,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); @@ -2159,21 +1185,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), @@ -2210,6 +1233,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; @@ -2267,13 +1302,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), @@ -2286,25 +1321,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) @@ -2313,25 +1347,23 @@ bool qemu_console_is_gl_blocked(QemuConsole *con) return con->gl_block; } -bool qemu_console_is_multihead(DeviceState *dev) +static bool qemu_graphic_console_is_multihead(QemuGraphicConsole *c) { QemuConsole *con; - Object *obj; - uint32_t f = 0xffffffff; - uint32_t h; QTAILQ_FOREACH(con, &consoles, next) { - obj = object_property_get_link(OBJECT(con), - "device", &error_abort); - if (DEVICE(obj) != dev) { + QemuGraphicConsole *candidate; + + if (!QEMU_IS_GRAPHIC_CONSOLE(con)) { continue; } - h = object_property_get_uint(OBJECT(con), - "head", &error_abort); - if (f == 0xffffffff) { - f = h; - } else if (h != f) { + candidate = QEMU_GRAPHIC_CONSOLE(con); + if (candidate->device != c->device) { + continue; + } + + if (candidate->head != c->head) { return true; } } @@ -2340,60 +1372,60 @@ bool qemu_console_is_multihead(DeviceState *dev) char *qemu_console_get_label(QemuConsole *con) { - if (con->console_type == GRAPHIC_CONSOLE) { - if (con->device) { + if (QEMU_IS_GRAPHIC_CONSOLE(con)) { + QemuGraphicConsole *c = QEMU_GRAPHIC_CONSOLE(con); + if (c->device) { DeviceState *dev; bool multihead; - dev = DEVICE(con->device); - multihead = qemu_console_is_multihead(dev); + 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(con->device), - con->head); + object_get_typename(c->device), + c->head); } else { return g_strdup_printf("%s", dev->id ? dev->id : - object_get_typename(con->device)); + 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; } switch (con->scanout.kind) { case SCANOUT_DMABUF: - return con->scanout.dmabuf->width; + return qemu_dmabuf_get_width(con->scanout.dmabuf); case SCANOUT_TEXTURE: return con->scanout.texture.width; case SCANOUT_SURFACE: @@ -2405,15 +1437,12 @@ 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; } switch (con->scanout.kind) { case SCANOUT_DMABUF: - return con->scanout.dmabuf->height; + return qemu_dmabuf_get_height(con->scanout.dmabuf); case SCANOUT_TEXTURE: return con->scanout.texture.height; case SCANOUT_SURFACE: @@ -2423,35 +1452,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)) { @@ -2461,126 +1466,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 = qemu_console_surface(s); - assert(s->console_type == GRAPHIC_CONSOLE); + assert(QEMU_IS_GRAPHIC_CONSOLE(s)); if ((s->scanout.kind != SCANOUT_SURFACE || - (surface && surface->flags & QEMU_ALLOCATED_FLAG)) && + (surface && surface_is_allocated(surface) && + !surface_is_placeholder(surface))) && qemu_console_get_width(s, -1) == width && qemu_console_get_height(s, -1) == height) { return; @@ -2680,6 +1577,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; @@ -2698,78 +1610,9 @@ void qemu_display_help(void) printf("%s\n", DisplayType_str(dpys[idx]->type)); } } + printf("\n" + "Some display backends support suboptions, which can be set with\n" + " -display backend,option=value,option=value...\n" + "For a short list of the suboptions for each display, see the " + "top-level -help output; more detail is in the documentation.\n"); } - -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 de962faa7c..4d0be9b37d 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -38,7 +38,7 @@ #include "ui/input.h" #include "sysemu/sysemu.h" -#if defined(__APPLE__) || defined(__OpenBSD__) +#ifdef __APPLE__ #define _XOPEN_SOURCE_EXTENDED 1 #endif @@ -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..6e23244fbe 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; @@ -195,30 +197,6 @@ void cursor_set_mono(QEMUCursor *c, } } -void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image) -{ - uint32_t *data = c->data; - uint8_t bit; - int x,y,bpl; - - bpl = cursor_get_mono_bpl(c); - memset(image, 0, bpl * c->height); - for (y = 0; y < c->height; y++) { - bit = 0x80; - for (x = 0; x < c->width; x++, data++) { - if (((*data & 0xff000000) == 0xff000000) && - ((*data & 0x00ffffff) == foreground)) { - image[x/8] |= bit; - } - bit >>= 1; - if (bit == 0) { - bit = 0x80; - } - } - image += bpl; - } -} - void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask) { uint32_t *data = c->data; @@ -230,7 +208,7 @@ void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask) for (y = 0; y < c->height; y++) { bit = 0x80; for (x = 0; x < c->width; x++, data++) { - if ((*data & 0xff000000) != 0xff000000) { + if ((*data & 0x80000000) == 0x0) { /* Alpha < 0x80 (128) */ if (transparent != 0) { mask[x/8] |= bit; } 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..fbb043abca 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" @@ -140,6 +141,8 @@ dbus_clipboard_qemu_request(QemuClipboardInfo *info, const char *mimes[] = { MIME_TEXT_PLAIN_UTF8, NULL }; size_t n; + trace_dbus_clipboard_qemu_request(type); + if (type != QEMU_CLIPBOARD_TYPE_TEXT) { /* unsupported atm */ return; @@ -203,15 +206,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 +213,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 +226,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 +246,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(); @@ -308,6 +307,8 @@ dbus_clipboard_grab( return DBUS_METHOD_INVOCATION_HANDLED; } + trace_dbus_clipboard_grab(arg_selection, arg_serial); + if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) { g_dbus_method_invocation_return_error( invocation, diff --git a/ui/dbus-console.c b/ui/dbus-console.c index 898a4ac8a5..5eb1d40d16 100644 --- a/ui/dbus-console.c +++ b/ui/dbus-console.c @@ -22,27 +22,33 @@ * 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; DBusDisplay *display; - GHashTable *listeners; + GPtrArray *listeners; QemuDBusDisplay1Console *iface; QemuDBusDisplay1Keyboard *iface_kbd; 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); @@ -103,11 +110,14 @@ static void dbus_gl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { + uint32_t width, height; + DBusDisplayConsole *ddc = container_of(dcl, DBusDisplayConsole, dcl); - dbus_display_console_set_size(ddc, - dmabuf->width, - dmabuf->height); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + + dbus_display_console_set_size(ddc, width, height); } static void @@ -132,8 +142,7 @@ dbus_display_console_init(DBusDisplayConsole *object) { DBusDisplayConsole *ddc = DBUS_DISPLAY_CONSOLE(object); - ddc->listeners = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, g_object_unref); + ddc->listeners = g_ptr_array_new_with_free_func(g_object_unref); ddc->dcl.ops = &dbus_console_dcl_ops; } @@ -143,9 +152,11 @@ 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); + g_clear_pointer(&ddc->listeners, g_ptr_array_unref); g_clear_pointer(&ddc->kbd, qkbd_state_free); G_OBJECT_CLASS(dbus_display_console_parent_class)->dispose(object); @@ -167,7 +178,7 @@ listener_vanished_cb(DBusDisplayListener *listener) trace_dbus_listener_vanished(name); - g_hash_table_remove(ddc->listeners, name); + g_ptr_array_remove_fast(ddc->listeners, listener); qkbd_state_lift_all_keys(ddc->kbd); } @@ -203,10 +214,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); @@ -218,16 +266,11 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, DBusDisplayListener *listener; int fd; - if (sender && g_hash_table_contains(ddc->listeners, sender)) { - g_dbus_method_invocation_return_error( - invocation, - DBUS_DISPLAY_ERROR, - DBUS_DISPLAY_ERROR_INVALID, - "`%s` is already registered!", - sender); +#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 +280,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 +289,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), @@ -268,9 +320,7 @@ dbus_console_register_listener(DBusDisplayConsole *ddc, return DBUS_METHOD_INVOCATION_HANDLED; } - g_hash_table_insert(ddc->listeners, - (gpointer)dbus_display_listener_get_bus_name(listener), - listener); + g_ptr_array_add(ddc->listeners, listener); g_object_connect(listener_conn, "swapped-signal::closed", listener_vanished_cb, listener, NULL); @@ -326,7 +376,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 +394,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 +443,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 +500,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 +529,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 +560,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 +591,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..e70f2848b7 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..99738e769b 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -22,15 +22,36 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "dbus.h" +#include "glib.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,107 +61,486 @@ 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 +#else /* !WIN32 */ + QemuDBusDisplay1ListenerUnixMap *map_proxy; +#endif + + guint dbus_filter; + guint32 display_serial_to_discard; + guint32 cursor_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_display_messages(DBusDisplayListener *ddl) +{ + guint32 serial = g_dbus_connection_get_last_serial( + g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); + + g_atomic_int_set(&ddl->display_serial_to_discard, serial); +} + +static void ddl_discard_cursor_messages(DBusDisplayListener *ddl) +{ + guint32 serial = g_dbus_connection_get_last_serial( + g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy))); + + g_atomic_int_set(&ddl->cursor_serial_to_discard, serial); +} + +#ifdef CONFIG_OPENGL +static void dbus_scanout_disable(DisplayChangeListener *dcl) +{ + DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); + + ddl_discard_display_messages(ddl); + + 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) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); g_autoptr(GError) err = NULL; g_autoptr(GUnixFDList) fd_list = NULL; + int fd; + uint32_t width, height, stride, fourcc; + uint64_t modifier; + bool y0_top; + fd = qemu_dmabuf_get_fd(dmabuf); fd_list = g_unix_fd_list_new(); - if (g_unix_fd_list_append(fd_list, dmabuf->fd, &err) != 0) { + if (g_unix_fd_list_append(fd_list, fd, &err) != 0) { error_report("Failed to setup dmabuf fdlist: %s", err->message); return; } + ddl_discard_display_messages(ddl); + + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + stride = qemu_dmabuf_get_stride(dmabuf); + fourcc = qemu_dmabuf_get_fourcc(dmabuf); + modifier = qemu_dmabuf_get_modifier(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); + + /* FIXME: add missing x/y/w/h support */ qemu_dbus_display1_listener_call_scanout_dmabuf( - ddl->proxy, - g_variant_new_handle(0), - dmabuf->width, - dmabuf->height, - dmabuf->stride, - dmabuf->fourcc, - dmabuf->modifier, - dmabuf->y0_top, - G_DBUS_CALL_FLAGS_NONE, - -1, - fd_list, - NULL, NULL, NULL); + ddl->proxy, g_variant_new_handle(0), + width, height, stride, fourcc, modifier, + y0_top, G_DBUS_CALL_FLAGS_NONE, + -1, 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->share_handle) { + return false; + } + + success = DuplicateHandle( + GetCurrentProcess(), + ddl->ds->share_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_display_messages(ddl); + + if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync( + ddl->map_proxy, + GPOINTER_TO_UINT(target_handle), + ddl->ds->share_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_display_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 */ +#else /* !WIN32 */ +static bool dbus_scanout_map(DBusDisplayListener *ddl) +{ + g_autoptr(GError) err = NULL; + g_autoptr(GUnixFDList) fd_list = NULL; + + if (ddl->ds_share == SHARE_KIND_MAPPED) { + return true; + } + + if (!ddl->can_share_map || ddl->ds->share_handle == SHAREABLE_NONE) { + return false; + } + + ddl_discard_display_messages(ddl); + fd_list = g_unix_fd_list_new(); + if (g_unix_fd_list_append(fd_list, ddl->ds->share_handle, &err) != 0) { + g_debug("Failed to setup scanout map fdlist: %s", err->message); + ddl->can_share_map = false; + return false; + } + + if (!qemu_dbus_display1_listener_unix_map_call_scanout_map_sync( + ddl->map_proxy, + g_variant_new_handle(0), + ddl->ds->share_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, + fd_list, + NULL, + 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; +} +#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) { - QemuDmaBuf dmabuf = { - .width = backing_width, - .height = backing_height, - .y0_top = backing_y_0_top, - }; + trace_dbus_scanout_texture(tex_id, backing_y_0_top, + backing_width, backing_height, x, y, w, h); +#ifdef CONFIG_GBM + g_autoptr(QemuDmaBuf) dmabuf = NULL; + int fd; + uint32_t stride, fourcc; + uint64_t modifier; assert(tex_id); - dmabuf.fd = egl_get_fd_for_texture( - tex_id, (EGLint *)&dmabuf.stride, - (EGLint *)&dmabuf.fourcc, - &dmabuf.modifier); - if (dmabuf.fd < 0) { + fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc, + &modifier); + if (fd < 0) { error_report("%s: failed to get fd for texture", __func__); return; } + dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width, + backing_height, fourcc, modifier, fd, + false, backing_y_0_top); - dbus_scanout_dmabuf(dcl, &dmabuf); - close(dmabuf.fd); + dbus_scanout_dmabuf(dcl, dmabuf); + qemu_dmabuf_close(dmabuf); +#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 +548,8 @@ 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; + uint32_t width, height, texture; if (!dmabuf) { qemu_dbus_display1_listener_call_mouse_set( @@ -157,13 +558,19 @@ static void dbus_cursor_dmabuf(DisplayChangeListener *dcl, return; } + ddl_discard_cursor_messages(ddl); + egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); - ds = qemu_create_displaysurface(dmabuf->width, dmabuf->height); + + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + + egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false); + ds = qemu_create_displaysurface(width, height); egl_fb_read(ds, &cursor_fb); v_data = g_variant_new_from_data( @@ -187,7 +594,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 +611,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 +628,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 +713,77 @@ 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_display_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); + + if (dbus_scanout_map(ddl)) { +#ifdef WIN32 + 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); +#else + qemu_dbus_display1_listener_unix_map_call_update_map( + ddl->map_proxy, + x, y, w, h, + G_DBUS_CALL_FLAGS_NONE, + DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL); +#endif + return; + } + + 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,14 +791,11 @@ 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, - int x, int y, int on) + int x, int y, bool on) { DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); @@ -338,14 +809,15 @@ static void dbus_cursor_define(DisplayChangeListener *dcl, DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl); GVariant *v_data = NULL; - cursor_get(c); + ddl_discard_cursor_messages(ddl); + 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 +833,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 +845,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 +873,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 +893,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 +915,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 +932,201 @@ dbus_display_listener_get_console(DBusDisplayListener *ddl) return ddl->console; } +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; +} + +#ifdef WIN32 +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) +{ + g_autoptr(GError) err = NULL; + +#ifdef WIN32 + 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; +#else /* !WIN32 */ + if (!dbus_display_listener_implements( + ddl, "org.qemu.Display1.Listener.Unix.Map")) { + return; + } + ddl->map_proxy = qemu_dbus_display1_listener_unix_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 Unix 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, discard_serial; + + if (incoming) { + return message; + } + + serial = g_dbus_message_get_serial(message); + + discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard); + if (serial <= discard_serial) { + const char *member = g_dbus_message_get_member(message); + static const char *const display_messages[] = { + "Scanout", + "Update", +#ifdef CONFIG_GBM + "ScanoutDMABUF", + "UpdateDMABUF", +#endif + "ScanoutMap", + "UpdateMap", + "Disable", + NULL, + }; + + if (g_strv_contains(display_messages, member)) { + trace_dbus_filter(serial, discard_serial); + g_object_unref(message); + return NULL; + } + } + + discard_serial = g_atomic_int_get(&ddl->cursor_serial_to_discard); + if (serial <= discard_serial) { + const gchar *member = g_dbus_message_get_member(message); + static const char *const cursor_messages[] = { + "CursorDefine", + NULL + }; + + if (g_strv_contains(cursor_messages, member)) { + trace_dbus_filter(serial, discard_serial); + g_object_unref(message); + return NULL; + } + } + + return message; +} + DBusDisplayListener * dbus_display_listener_new(const char *bus_name, GDBusConnection *conn, @@ -465,10 +1151,15 @@ 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); + trace_dbus_can_share_map(ddl->can_share_map); + 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 32d88dc94a..7ecd39e784 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; } @@ -165,7 +176,7 @@ dbus_display_add_console(DBusDisplay *dd, int idx, Error **errp) assert(con); if (qemu_console_is_graphic(con) && - dd->gl_mode != DISPLAYGL_MODE_OFF) { + dd->gl_mode != DISPLAY_GL_MODE_OFF) { qemu_console_set_display_gl_ctx(con, &dd->glctx); } @@ -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)); @@ -289,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); @@ -447,15 +466,14 @@ static const TypeInfo dbus_vc_type_info = { static void early_dbus_init(DisplayOptions *opts) { - DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_OFF; + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAY_GL_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; + if (mode != DISPLAY_GL_MODE_OFF) { +#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); @@ -464,7 +482,7 @@ early_dbus_init(DisplayOptions *opts) static void dbus_init(DisplayState *ds, DisplayOptions *opts) { - DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_OFF; + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAY_GL_MODE_OFF; if (opts->u.dbus.addr && opts->u.dbus.p2p) { error_report("dbus: can't accept both addr=X and p2p=yes options"); @@ -500,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 9c149e7b41..1e8c24a48e 100644 --- a/ui/dbus.h +++ b/ui/dbus.h @@ -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/dmabuf.c b/ui/dmabuf.c new file mode 100644 index 0000000000..df7a09703f --- /dev/null +++ b/ui/dmabuf.c @@ -0,0 +1,229 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QemuDmaBuf struct and helpers used for accessing its data + * + * 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 "ui/dmabuf.h" + +struct QemuDmaBuf { + int fd; + uint32_t width; + uint32_t height; + uint32_t stride; + uint32_t fourcc; + uint64_t modifier; + uint32_t texture; + uint32_t x; + uint32_t y; + uint32_t backing_width; + uint32_t backing_height; + bool y0_top; + void *sync; + int fence_fd; + bool allow_fences; + bool draw_submitted; +}; + +QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, + uint32_t stride, uint32_t x, + uint32_t y, uint32_t backing_width, + uint32_t backing_height, uint32_t fourcc, + uint64_t modifier, int32_t dmabuf_fd, + bool allow_fences, bool y0_top) { + QemuDmaBuf *dmabuf; + + dmabuf = g_new0(QemuDmaBuf, 1); + + dmabuf->width = width; + dmabuf->height = height; + dmabuf->stride = stride; + dmabuf->x = x; + dmabuf->y = y; + dmabuf->backing_width = backing_width; + dmabuf->backing_height = backing_height; + dmabuf->fourcc = fourcc; + dmabuf->modifier = modifier; + dmabuf->fd = dmabuf_fd; + dmabuf->allow_fences = allow_fences; + dmabuf->y0_top = y0_top; + dmabuf->fence_fd = -1; + + return dmabuf; +} + +void qemu_dmabuf_free(QemuDmaBuf *dmabuf) +{ + if (dmabuf == NULL) { + return; + } + + g_free(dmabuf); +} + +int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->fd; +} + +int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + if (dmabuf->fd >= 0) { + return dup(dmabuf->fd); + } else { + return -1; + } +} + +void qemu_dmabuf_close(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + if (dmabuf->fd >= 0) { + close(dmabuf->fd); + dmabuf->fd = -1; + } +} + +uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->width; +} + +uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->height; +} + +uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->stride; +} + +uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->fourcc; +} + +uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->modifier; +} + +uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->texture; +} + +uint32_t qemu_dmabuf_get_x(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->x; +} + +uint32_t qemu_dmabuf_get_y(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->y; +} + +uint32_t qemu_dmabuf_get_backing_width(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->backing_width; +} + +uint32_t qemu_dmabuf_get_backing_height(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->backing_height; +} + +bool qemu_dmabuf_get_y0_top(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->y0_top; +} + +void *qemu_dmabuf_get_sync(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->sync; +} + +int32_t qemu_dmabuf_get_fence_fd(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->fence_fd; +} + +bool qemu_dmabuf_get_allow_fences(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->allow_fences; +} + +bool qemu_dmabuf_get_draw_submitted(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->draw_submitted; +} + +void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture) +{ + assert(dmabuf != NULL); + dmabuf->texture = texture; +} + +void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd) +{ + assert(dmabuf != NULL); + dmabuf->fence_fd = fence_fd; +} + +void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync) +{ + assert(dmabuf != NULL); + dmabuf->sync = sync; +} + +void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted) +{ + assert(dmabuf != NULL); + dmabuf->draw_submitted = draw_submitted; +} + +void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd) +{ + assert(dmabuf != NULL); + dmabuf->fd = fd; +} diff --git a/ui/egl-context.c b/ui/egl-context.c index eb5f520fc4..aed3e3ba1f 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, @@ -16,7 +17,7 @@ QEMUGLContext qemu_egl_create_context(DisplayGLCtx *dgc, EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver, EGL_NONE }; - bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); + bool gles = (qemu_egl_mode == DISPLAY_GL_MODE_ES); ctx = eglCreateContext(qemu_egl_display, qemu_egl_config, eglGetCurrentContext(), @@ -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..1f6b845500 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,37 +80,56 @@ static void egl_scanout_texture(DisplayChangeListener *dcl, } } +#ifdef CONFIG_GBM + static void egl_scanout_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf) { + uint32_t width, height, texture; + egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_scanout_texture(dcl, dmabuf->texture, - false, dmabuf->width, dmabuf->height, - 0, 0, dmabuf->width, dmabuf->height); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + + egl_scanout_texture(dcl, texture, false, width, height, 0, 0, + width, height, NULL); } static void egl_cursor_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf, bool have_hot, uint32_t hot_x, uint32_t hot_y) { + uint32_t width, height, texture; egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); if (dmabuf) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&edpy->cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); + + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + egl_fb_setup_for_tex(&edpy->cursor_fb, width, height, texture, false); } else { egl_fb_destroy(&edpy->cursor_fb); } } +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 +139,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 +174,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 +207,21 @@ static const DisplayGLCtxOps eglctx_ops = { static void early_egl_headless_init(DisplayOptions *opts) { - display_opengl = 1; + DisplayGLMode mode = DISPLAY_GL_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..81a57fa975 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) { @@ -101,10 +146,10 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) glViewport(0, 0, dst->width, dst->height); if (src->dmabuf) { - x1 = src->dmabuf->x; - y1 = src->dmabuf->y; - w = src->dmabuf->scanout_width; - h = src->dmabuf->scanout_height; + x1 = qemu_dmabuf_get_x(src->dmabuf); + y1 = qemu_dmabuf_get_y(src->dmabuf); + w = qemu_dmabuf_get_width(src->dmabuf); + h = qemu_dmabuf_get_height(src->dmabuf); } 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) { @@ -248,30 +308,33 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) EGLImageKHR image = EGL_NO_IMAGE_KHR; EGLint attrs[64]; int i = 0; + uint64_t modifier; + uint32_t texture = qemu_dmabuf_get_texture(dmabuf); - if (dmabuf->texture != 0) { + if (texture != 0) { return; } attrs[i++] = EGL_WIDTH; - attrs[i++] = dmabuf->width; + attrs[i++] = qemu_dmabuf_get_backing_width(dmabuf); attrs[i++] = EGL_HEIGHT; - attrs[i++] = dmabuf->height; + attrs[i++] = qemu_dmabuf_get_backing_height(dmabuf); attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; - attrs[i++] = dmabuf->fourcc; + attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attrs[i++] = dmabuf->fd; + attrs[i++] = qemu_dmabuf_get_fd(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attrs[i++] = dmabuf->stride; + attrs[i++] = qemu_dmabuf_get_stride(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attrs[i++] = 0; #ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT - if (dmabuf->modifier) { + modifier = qemu_dmabuf_get_modifier(dmabuf); + if (modifier) { attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attrs[i++] = (dmabuf->modifier >> 0) & 0xffffffff; + attrs[i++] = (modifier >> 0) & 0xffffffff; attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attrs[i++] = (dmabuf->modifier >> 32) & 0xffffffff; + attrs[i++] = (modifier >> 32) & 0xffffffff; } #endif attrs[i++] = EGL_NONE; @@ -285,8 +348,9 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) return; } - glGenTextures(1, &dmabuf->texture); - glBindTexture(GL_TEXTURE_2D, dmabuf->texture); + glGenTextures(1, &texture); + qemu_dmabuf_set_texture(dmabuf, texture); + glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -296,12 +360,15 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf) { - if (dmabuf->texture == 0) { + uint32_t texture; + + texture = qemu_dmabuf_get_texture(dmabuf); + if (texture == 0) { return; } - glDeleteTextures(1, &dmabuf->texture); - dmabuf->texture = 0; + glDeleteTextures(1, &texture); + qemu_dmabuf_set_texture(dmabuf, 0); } void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) @@ -315,18 +382,22 @@ void egl_dmabuf_create_sync(QemuDmaBuf *dmabuf) sync = eglCreateSyncKHR(qemu_egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL); if (sync != EGL_NO_SYNC_KHR) { - dmabuf->sync = sync; + qemu_dmabuf_set_sync(dmabuf, sync); } } } void egl_dmabuf_create_fence(QemuDmaBuf *dmabuf) { - if (dmabuf->sync) { - dmabuf->fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display, - dmabuf->sync); - eglDestroySyncKHR(qemu_egl_display, dmabuf->sync); - dmabuf->sync = NULL; + void *sync = qemu_dmabuf_get_sync(dmabuf); + int fence_fd; + + if (sync) { + fence_fd = eglDupNativeFenceFDANDROID(qemu_egl_display, + sync); + qemu_dmabuf_set_fence_fd(dmabuf, fence_fd); + eglDestroySyncKHR(qemu_egl_display, sync); + qemu_dmabuf_set_sync(dmabuf, NULL); } } @@ -358,7 +429,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 +466,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); } } @@ -434,24 +503,24 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, EGLint major, minor; EGLBoolean b; EGLint n; - bool gles = (mode == DISPLAYGL_MODE_ES); + bool gles = (mode == DISPLAY_GL_MODE_ES); 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,15 +528,18 @@ 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; } - qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE; + qemu_egl_mode = gles ? DISPLAY_GL_MODE_ES : DISPLAY_GL_MODE_CORE; 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 +557,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 == DISPLAY_GL_MODE_ON) { + mode = DISPLAY_GL_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) @@ -508,7 +618,7 @@ EGLContext qemu_egl_init_ctx(void) EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; - bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); + bool gles = (qemu_egl_mode == DISPLAY_GL_MODE_ES); EGLContext ectx; EGLBoolean b; @@ -527,3 +637,38 @@ EGLContext qemu_egl_init_ctx(void) return ectx; } + +bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp) +{ + ERRP_GUARD(); + + if (mode == DISPLAY_GL_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 e99e3b0d8c..9831c10e1b 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); @@ -65,24 +68,26 @@ void gd_egl_draw(VirtualConsole *vc) GdkWindow *window; #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; + int fence_fd; #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 if (dmabuf) { - if (!dmabuf->draw_submitted) { + if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { return; } else { - dmabuf->draw_submitted = false; + qemu_dmabuf_set_draw_submitted(dmabuf, false); } } #endif @@ -95,8 +100,9 @@ void gd_egl_draw(VirtualConsole *vc) #ifdef CONFIG_GBM if (dmabuf) { egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd > 0) { - qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + if (fence_fd >= 0) { + qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc); return; } graphic_hw_gl_block(vc->gfx.dcl.con, false); @@ -134,6 +140,8 @@ 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) @@ -143,6 +151,12 @@ void gd_egl_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate( vc, vc->window ? vc->window : vc->gfx.drawing_area); + if (vc->gfx.guest_fb.dmabuf && + qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { + gd_egl_draw(vc); + return; + } + if (!vc->gfx.esurface) { gd_egl_init(vc); if (!vc->gfx.esurface) { @@ -223,7 +237,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); @@ -233,6 +248,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); @@ -246,21 +268,30 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, { #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + uint32_t x, y, width, height, backing_width, backing_height, texture; + bool y0_top; eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - gd_egl_scanout_texture(dcl, dmabuf->texture, - dmabuf->y0_top, dmabuf->width, dmabuf->height, - dmabuf->x, dmabuf->y, dmabuf->scanout_width, - dmabuf->scanout_height); + x = qemu_dmabuf_get_x(dmabuf); + y = qemu_dmabuf_get_y(dmabuf); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); - if (dmabuf->allow_fences) { + gd_egl_scanout_texture(dcl, texture, y0_top, backing_width, backing_height, + x, y, width, height, NULL); + + if (qemu_dmabuf_get_allow_fences(dmabuf)) { vc->gfx.guest_fb.dmabuf = dmabuf; } #endif @@ -272,14 +303,19 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, { #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + uint32_t backing_width, backing_height, texture; if (dmabuf) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); + + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + egl_fb_setup_for_tex(&vc->gfx.cursor_fb, backing_width, backing_height, + texture, false); } else { egl_fb_destroy(&vc->gfx.cursor_fb); } @@ -300,7 +336,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; @@ -313,8 +349,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, @@ -342,9 +379,11 @@ 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 && !vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { graphic_hw_gl_block(vc->gfx.dcl.con, true); - vc->gfx.guest_fb.dmabuf->draw_submitted = true; + qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); + gtk_egl_set_scanout_mode(vc, true); gtk_widget_queue_draw_area(area, x, y, w, h); return; } @@ -369,6 +408,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 1605818bd1..b628b35451 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); @@ -59,10 +60,10 @@ void gd_gl_area_draw(VirtualConsole *vc) #ifdef CONFIG_GBM if (dmabuf) { - if (!dmabuf->draw_submitted) { + if (!qemu_dmabuf_get_draw_submitted(dmabuf)) { return; } else { - dmabuf->draw_submitted = false; + qemu_dmabuf_set_draw_submitted(dmabuf, false); } } #endif @@ -84,9 +85,11 @@ void gd_gl_area_draw(VirtualConsole *vc) glFlush(); #ifdef CONFIG_GBM if (dmabuf) { + int fence_fd; egl_dmabuf_create_fence(dmabuf); - if (dmabuf->fence_fd > 0) { - qemu_set_fd_handler(dmabuf->fence_fd, gd_hw_gl_flushed, NULL, vc); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + if (fence_fd >= 0) { + qemu_set_fd_handler(fence_fd, gd_hw_gl_flushed, NULL, vc); return; } graphic_hw_gl_block(vc->gfx.dcl.con, false); @@ -115,6 +118,7 @@ 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) @@ -123,6 +127,12 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); + if (vc->gfx.guest_fb.dmabuf && + qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { + gd_gl_area_draw(vc); + return; + } + if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -244,7 +254,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); @@ -278,9 +289,11 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { + if (vc->gfx.guest_fb.dmabuf && + !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { graphic_hw_gl_block(vc->gfx.dcl.con, true); - vc->gfx.guest_fb.dmabuf->draw_submitted = true; + qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); + gtk_gl_area_set_scanout_mode(vc, true); } gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); } @@ -290,19 +303,29 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, { #ifdef CONFIG_GBM VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); + uint32_t x, y, width, height, backing_width, backing_height, texture; + bool y0_top; gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - gd_gl_area_scanout_texture(dcl, dmabuf->texture, - dmabuf->y0_top, dmabuf->width, dmabuf->height, - dmabuf->x, dmabuf->y, dmabuf->scanout_width, - dmabuf->scanout_height); + x = qemu_dmabuf_get_x(dmabuf); + y = qemu_dmabuf_get_y(dmabuf); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + backing_width = qemu_dmabuf_get_backing_width(dmabuf); + backing_height = qemu_dmabuf_get_backing_height(dmabuf); + y0_top = qemu_dmabuf_get_y0_top(dmabuf); - if (dmabuf->allow_fences) { + gd_gl_area_scanout_texture(dcl, texture, y0_top, + backing_width, backing_height, + x, y, width, height, NULL); + + if (qemu_dmabuf_get_allow_fences(dmabuf)) { vc->gfx.guest_fb.dmabuf = dmabuf; } #endif diff --git a/ui/gtk.c b/ui/gtk.c index e681e8c319..bf9d3dd679 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); @@ -444,13 +446,14 @@ static GdkDevice *gd_get_pointer(GdkDisplay *dpy) } static void gd_mouse_set(DisplayChangeListener *dcl, - int x, int y, int visible) + int x, int y, bool visible) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.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 } @@ -588,11 +596,15 @@ void gd_hw_gl_flushed(void *vcon) { VirtualConsole *vc = vcon; QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; + int fence_fd; - qemu_set_fd_handler(dmabuf->fence_fd, NULL, NULL, NULL); - close(dmabuf->fence_fd); - dmabuf->fence_fd = -1; - graphic_hw_gl_block(vc->gfx.dcl.con, false); + fence_fd = qemu_dmabuf_get_fence_fd(dmabuf); + if (fence_fd >= 0) { + qemu_set_fd_handler(fence_fd, NULL, NULL, NULL); + close(fence_fd); + qemu_dmabuf_set_fence_fd(dmabuf, -1); + graphic_hw_gl_block(vc->gfx.dcl.con, false); + } } /** DisplayState Callbacks (opengl version) **/ @@ -681,7 +693,7 @@ 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() && s->ptr_owner) { + 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); @@ -718,6 +730,10 @@ static void gd_set_ui_refresh_rate(VirtualConsole *vc, int refresh_rate) { QemuUIInfo 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); @@ -727,6 +743,10 @@ 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; @@ -871,7 +891,7 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, int x, y; int mx, my; int fbh, fbw; - int ww, wh, ws; + int ww, wh; if (!vc->gfx.ds) { return TRUE; @@ -879,11 +899,15 @@ 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; - ww = gtk_widget_get_allocated_width(widget); wh = gtk_widget_get_allocated_height(widget); - ws = gtk_widget_get_scale_factor(widget); + /* + * `widget` may not have the same size with the frame buffer. + * In such cases, some paddings are needed around the `vc`. + * To achieve that, `vc` will be displayed at (mx, my) + * so that it is displayed at the center of the widget. + */ mx = my = 0; if (ww > fbw) { mx = (ww - fbw) / 2; @@ -892,10 +916,16 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, my = (wh - fbh) / 2; } - x = (motion->x - mx) / vc->gfx.scale_x * ws; - y = (motion->y - my) / vc->gfx.scale_y * ws; + /* + * `motion` is reported in `widget` coordinates + * so translating it to the coordinates in `vc`. + */ + x = (motion->x - mx) / vc->gfx.scale_x; + y = (motion->y - my) / vc->gfx.scale_y; - if (qemu_input_is_absolute()) { + trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y); + + 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)) { @@ -915,15 +945,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); @@ -934,13 +964,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; } @@ -957,7 +987,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); @@ -1056,6 +1086,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(); @@ -1149,15 +1209,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; } @@ -1354,7 +1414,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; } @@ -1760,7 +1820,7 @@ static void gd_vc_send_chars(VirtualConsole *vc) const uint8_t *buf; uint32_t size; - buf = fifo8_pop_buf(&vc->vte.out_fifo, MIN(len, avail), &size); + buf = fifo8_pop_bufptr(&vc->vte.out_fifo, MIN(len, avail), &size); qemu_chr_be_write(vc->vte.chr, buf, size); len = qemu_chr_be_can_write(vc->vte.chr); avail -= size; @@ -1822,7 +1882,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; @@ -1977,6 +2036,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); @@ -2086,6 +2147,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; @@ -2133,6 +2195,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 | @@ -2168,6 +2231,11 @@ 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; } @@ -2313,16 +2381,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(); @@ -2379,6 +2449,15 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) gtk_container_add(GTK_CONTAINER(s->window), s->vbox); 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); @@ -2435,17 +2514,23 @@ static void early_gtk_display_init(DisplayOptions *opts) } assert(opts->type == DISPLAY_TYPE_GTK); - if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) { + if (opts->has_gl && opts->gl != DISPLAY_GL_MODE_OFF) { #if defined(CONFIG_OPENGL) #if defined(GDK_WINDOWING_WAYLAND) if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { 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 - DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAY_GL_MODE_ON; gtk_egl_init(mode); #endif } @@ -2463,6 +2548,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..ca4bccb411 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -109,43 +109,6 @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, g_free(up); } -static void legacy_kbd_event(DeviceState *dev, QemuConsole *src, - InputEvent *evt) -{ - QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev; - int scancodes[3], i, count; - InputKeyEvent *key = evt->u.key.data; - - if (!entry || !entry->put_kbd) { - return; - } - count = qemu_input_key_value_to_scancode(key->key, - key->down, - scancodes); - for (i = 0; i < count; i++) { - entry->put_kbd(entry->opaque, scancodes[i]); - } -} - -static QemuInputHandler legacy_kbd_handler = { - .name = "legacy-kbd", - .mask = INPUT_EVENT_MASK_KEY, - .event = legacy_kbd_event, -}; - -QEMUPutKbdEntry *qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) -{ - QEMUPutKbdEntry *entry; - - entry = g_new0(QEMUPutKbdEntry, 1); - entry->put_kbd = func; - entry->opaque = opaque; - entry->s = qemu_input_handler_register((DeviceState *)entry, - &legacy_kbd_handler); - qemu_input_handler_activate(entry->s); - return entry; -} - static void legacy_mouse_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { diff --git a/ui/input.c b/ui/input.c index e2a90af889..7ddefebc43 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; } @@ -176,37 +174,6 @@ void qmp_input_send_event(bool has_device, const char *device, qemu_input_event_sync(); } -static int qemu_input_transform_invert_abs_value(int value) -{ - return (int64_t)INPUT_EVENT_ABS_MAX - value + INPUT_EVENT_ABS_MIN; -} - -static void qemu_input_transform_abs_rotate(InputEvent *evt) -{ - InputMoveEvent *move = evt->u.abs.data; - switch (graphic_rotate) { - case 90: - if (move->axis == INPUT_AXIS_X) { - move->axis = INPUT_AXIS_Y; - } else if (move->axis == INPUT_AXIS_Y) { - move->axis = INPUT_AXIS_X; - move->value = qemu_input_transform_invert_abs_value(move->value); - } - break; - case 180: - move->value = qemu_input_transform_invert_abs_value(move->value); - break; - case 270: - if (move->axis == INPUT_AXIS_X) { - move->axis = INPUT_AXIS_Y; - move->value = qemu_input_transform_invert_abs_value(move->value); - } else if (move->axis == INPUT_AXIS_Y) { - move->axis = INPUT_AXIS_X; - } - break; - } -} - static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) { const char *name; @@ -214,6 +181,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 +220,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; @@ -336,11 +309,6 @@ void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) qemu_input_event_trace(src, evt); - /* pre processing */ - if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) { - qemu_input_transform_abs_rotate(evt); - } - /* send event */ s = qemu_input_find_handler(1 << evt->type, src); if (!s) { @@ -490,12 +458,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 +511,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 +583,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 75b82df927..47d7540b6e 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -1,12 +1,13 @@ -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', + 'dmabuf.c', 'input-keymap.c', 'input-legacy.c', 'input-barrier.c', @@ -14,6 +15,8 @@ softmmu_ss.add(files( 'kbd-state.c', 'keymaps.c', 'qemu-pixman.c', + 'ui-hmp-cmds.c', + 'ui-qmp-cmds.c', 'util.c', )) @@ -35,29 +38,32 @@ xemu_ss.add(files( subdir('xui') -if 'CONFIG_DARWIN' in config_host -xemu_cocoa = dependency('appleframeworks', modules: 'Cocoa') -xemu_ss.add(xemu_cocoa) +if host_os == 'darwin' + xemu_cocoa = dependency('appleframeworks', modules: 'Cocoa') + xemu_ss.add(xemu_cocoa) endif -xemu_ss.add(when: 'CONFIG_LINUX', if_true: [gtk, files('xemu-os-utils-linux.c')]) -xemu_ss.add(when: 'CONFIG_WIN32', if_true: files('xemu-os-utils-windows.c')) -xemu_ss.add(when: 'CONFIG_DARWIN', if_true: files('xemu-os-utils-macos.m')) +if host_os == 'linux' + xemu_ss.add([gtk, files('xemu-os-utils-linux.c')]) +elif host_os == 'windows' + xemu_ss.add(files('xemu-os-utils-windows.c')) +elif host_os == 'darwin' + xemu_ss.add(files('xemu-os-utils-macos.m')) +endif xemu_ss.add(imgui, implot, stb_image, noc, sdl, opengl, openssl, fa, fpng, json, httplib, fatx) +system_ss.add_all(xemu_ss) -softmmu_ss.add_all(xemu_ss) - +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( @@ -72,10 +78,10 @@ vnc_ss.add(files( 'vnc-jobs.c', 'vnc-clipboard.c', )) -vnc_ss.add(zlib, jpeg, gnutls) +vnc_ss.add(zlib, jpeg) 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 = {} @@ -89,34 +95,41 @@ 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, gbm], + dbus_ss.add(when: gio, if_true: [files( 'dbus-chardev.c', 'dbus-clipboard.c', @@ -124,12 +137,14 @@ if dbus_display 'dbus-error.c', 'dbus-listener.c', 'dbus.c', - ), dbus_display1]) + ), opengl, gbm, pixman, dbus_display1]) 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')) @@ -143,7 +158,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( @@ -164,12 +181,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 endif # xemu_enable_extra_ui_modules @@ -195,15 +212,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..6ef4376f4e 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -4,8 +4,11 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "ui/console.h" +#include "qemu/memfd.h" #include "standard-headers/drm/drm_fourcc.h" +#include "trace.h" PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format) { @@ -48,7 +51,6 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format) break; default: g_assert_not_reached(); - break; } pf.amax = (1 << pf.abits) - 1; @@ -95,7 +97,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 +146,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 +160,7 @@ pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) } return format; } +#endif /* * Return true for known-good pixman conversions. @@ -183,6 +189,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 +206,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 +215,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 +225,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 +268,73 @@ void qemu_pixman_glyph_render(pixman_image_t *glyph, pixman_image_unref(ifg); pixman_image_unref(ibg); } +#endif /* CONFIG_PIXMAN */ + +static void * +qemu_pixman_shareable_alloc(const char *name, size_t size, + qemu_pixman_shareable *handle, + Error **errp) +{ +#ifdef WIN32 + return qemu_win32_map_alloc(size, handle, errp); +#else + return qemu_memfd_alloc(name, size, 0, handle, errp); +#endif +} + +static void +qemu_pixman_shareable_free(qemu_pixman_shareable handle, + void *ptr, size_t size) +{ +#ifdef WIN32 + qemu_win32_map_free(ptr, handle, &error_warn); +#else + qemu_memfd_free(ptr, size, handle); +#endif +} + +static void +qemu_pixman_shared_image_destroy(pixman_image_t *image, void *data) +{ + qemu_pixman_shareable handle = PTR_TO_SHAREABLE(data); + void *ptr = pixman_image_get_data(image); + size_t size = pixman_image_get_height(image) * pixman_image_get_stride(image); + + qemu_pixman_shareable_free(handle, ptr, size); +} + +bool +qemu_pixman_image_new_shareable(pixman_image_t **image, + qemu_pixman_shareable *handle, + const char *name, + pixman_format_code_t format, + int width, + int height, + int rowstride_bytes, + Error **errp) +{ + ERRP_GUARD(); + size_t size = height * rowstride_bytes; + void *bits = NULL; + + g_return_val_if_fail(image != NULL, false); + g_return_val_if_fail(handle != NULL, false); + + bits = qemu_pixman_shareable_alloc(name, size, handle, errp); + if (!bits) { + return false; + } + + *image = pixman_image_create_bits(format, width, height, bits, rowstride_bytes); + if (!*image) { + error_setg(errp, "Failed to allocate image"); + qemu_pixman_shareable_free(*handle, bits, size); + return false; + } + + pixman_image_set_destroy_function(*image, + qemu_pixman_shared_image_destroy, + SHAREABLE_TO_PTR(*handle)); + + return true; +} diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index bfebbdeaea..73052383c2 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -72,7 +72,7 @@ void sdl2_2d_switch(DisplayChangeListener *dcl, scon->texture = NULL; } - if (is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { + if (surface_is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { sdl2_window_destroy(scon); return; } @@ -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 bbfa70eac3..e01d9ab0c7 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -89,7 +89,7 @@ void sdl2_gl_switch(DisplayChangeListener *dcl, scon->surface = new_surface; - if (is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { + if (surface_is_placeholder(new_surface) && qemu_console_get_index(dcl->con)) { qemu_gl_fini_shader(scon->gls); scon->gls = NULL; sdl2_window_destroy(scon); @@ -147,11 +147,11 @@ QEMUGLContext sdl2_gl_create_context(DisplayGLCtx *dgc, SDL_GL_MakeCurrent(scon->real_window, scon->winctx); SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - if (scon->opts->gl == DISPLAYGL_MODE_ON || - scon->opts->gl == DISPLAYGL_MODE_CORE) { + if (scon->opts->gl == DISPLAY_GL_MODE_ON || + scon->opts->gl == DISPLAY_GL_MODE_CORE) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - } else if (scon->opts->gl == DISPLAYGL_MODE_ES) { + } else if (scon->opts->gl == DISPLAY_GL_MODE_ES) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); } @@ -163,7 +163,7 @@ QEMUGLContext sdl2_gl_create_context(DisplayGLCtx *dgc, /* If SDL fail to create a GL context and we use the "on" flag, * then try to fallback to GLES. */ - if (!ctx && scon->opts->gl == DISPLAYGL_MODE_ON) { + if (!ctx && scon->opts->gl == DISPLAY_GL_MODE_ON) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); ctx = SDL_GL_CreateContext(scon->real_window); @@ -205,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..2286df4223 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -43,17 +43,23 @@ 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; } } } } + +void sdl2_release_modifiers(struct sdl2_console *scon) +{ + qkbd_state_lift_all_keys(scon->kbd); +} diff --git a/ui/sdl2.c b/ui/sdl2.c index d630459b78..bd4f5a9da1 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -49,7 +49,7 @@ static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static SDL_Cursor *sdl_cursor_normal; static SDL_Cursor *sdl_cursor_hidden; static int absolute_enabled; -static int guest_cursor; +static bool guest_cursor; static int guest_x, guest_y; static SDL_Cursor *guest_sprite; static Notifier mouse_mode_notifier; @@ -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,21 @@ 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 == DISPLAY_GL_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); + SDL_GL_SetSwapInterval(0); + } 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 +129,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 +173,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 +212,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 +223,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 +254,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 +289,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 +320,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, @@ -358,12 +389,13 @@ static void handle_keydown(SDL_Event *ev) int win; struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); int gui_key_modifier_pressed = get_mod_state(); - int gui_keysym = 0; if (!scon) { return; } + scon->gui_keysym = false; + if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) { switch (ev->key.keysym.scancode) { case SDL_SCANCODE_2: @@ -388,15 +420,16 @@ static void handle_keydown(SDL_Event *ev) SDL_ShowWindow(sdl2_console[win].real_window); } } - gui_keysym = 1; + sdl2_release_modifiers(scon); + scon->gui_keysym = true; } break; case SDL_SCANCODE_F: toggle_full_screen(scon); - gui_keysym = 1; + scon->gui_keysym = true; break; case SDL_SCANCODE_G: - gui_keysym = 1; + scon->gui_keysym = true; if (!gui_grab) { sdl_grab_start(scon); } else if (!gui_fullscreen) { @@ -409,7 +442,7 @@ static void handle_keydown(SDL_Event *ev) /* re-create scon->texture */ sdl2_2d_switch(&scon->dcl, scon->surface); } - gui_keysym = 1; + scon->gui_keysym = true; break; #if 0 case SDL_SCANCODE_KP_PLUS: @@ -428,14 +461,14 @@ static void handle_keydown(SDL_Event *ev) __func__, width, height); sdl_scale(scon, width, height); sdl2_redraw(scon); - gui_keysym = 1; + scon->gui_keysym = true; } #endif default: break; } } - if (!gui_keysym) { + if (!scon->gui_keysym) { sdl2_process_key(scon, &ev->key); } } @@ -461,10 +494,9 @@ static void handle_textinput(SDL_Event *ev) return; } - if (qemu_console_is_graphic(con)) { - return; + if (!scon->gui_keysym && 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 +508,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 +524,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 +541,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 +614,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 @@ -700,7 +732,7 @@ void sdl2_poll_events(struct sdl2_console *scon) } static void sdl_mouse_warp(DisplayChangeListener *dcl, - int x, int y, int on) + int x, int y, bool on) { struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); @@ -712,9 +744,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 +784,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 +857,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", @@ -850,13 +870,14 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); #endif #ifndef CONFIG_WIN32 - /* QEMU uses its own low level keyboard hook procecure on Windows */ + /* 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"); + SDL_EnableScreenSaver(); memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); diff --git a/ui/shader/meson.build b/ui/shader/meson.build index ef29f31e0b..ea223ae8db 100644 --- a/ui/shader/meson.build +++ b/ui/shader/meson.build @@ -11,5 +11,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..bd9dbe03f1 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"), DISPLAY_GL_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..c794ae0649 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; } @@ -252,7 +254,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd) static SimpleSpiceCursor* qemu_spice_create_cursor_update(SimpleSpiceDisplay *ssd, QEMUCursor *c, - int on) + bool on) { size_t size = c ? c->width * c->height * 4 : 0; SimpleSpiceCursor *update; @@ -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); } @@ -446,7 +448,8 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, qemu_mutex_lock(&ssd->lock); if (ssd->cursor) { g_free(ssd->ptr_define); - ssd->ptr_define = qemu_spice_create_cursor_update(ssd, ssd->cursor, 0); + ssd->ptr_define = + qemu_spice_create_cursor_update(ssd, ssd->cursor, false); } qemu_mutex_unlock(&ssd->lock); } @@ -459,11 +462,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) { @@ -474,7 +477,7 @@ void qemu_spice_cursor_refresh_bh(void *opaque) ssd->mouse_x = -1; ssd->mouse_y = -1; qemu_mutex_unlock(&ssd->lock); - dpy_mouse_set(ssd->dcl.con, x, y, 1); + dpy_mouse_set(ssd->dcl.con, x, y, true); } else { qemu_mutex_unlock(&ssd->lock); } @@ -517,13 +520,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 +711,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 */ @@ -755,7 +748,7 @@ static void display_refresh(DisplayChangeListener *dcl) } static void display_mouse_set(DisplayChangeListener *dcl, - int x, int y, int on) + int x, int y, bool on) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); @@ -774,15 +767,15 @@ 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; g_free(ssd->ptr_move); ssd->ptr_move = NULL; g_free(ssd->ptr_define); - ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, 0); + ssd->ptr_define = qemu_spice_create_cursor_update(ssd, c, false); qemu_mutex_unlock(&ssd->lock); qemu_spice_wakeup(ssd); } @@ -944,7 +937,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; @@ -983,6 +977,7 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, uint32_t hot_x, uint32_t hot_y) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + uint32_t width, height, texture; ssd->have_hot = have_hot; ssd->hot_x = hot_x; @@ -991,11 +986,13 @@ static void qemu_spice_gl_cursor_dmabuf(DisplayChangeListener *dcl, trace_qemu_spice_gl_cursor(ssd->qxl.id, dmabuf != NULL, have_hot); if (dmabuf) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } - egl_fb_setup_for_tex(&ssd->cursor_fb, dmabuf->width, dmabuf->height, - dmabuf->texture, false); + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + egl_fb_setup_for_tex(&ssd->cursor_fb, width, height, texture, false); } else { egl_fb_destroy(&ssd->cursor_fb); } @@ -1033,6 +1030,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, bool y_0_top = false; /* FIXME */ uint64_t cookie; int fd; + uint32_t width, height, texture; if (!ssd->have_scanout) { return; @@ -1049,54 +1047,59 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, if (ssd->guest_dmabuf_refresh) { QemuDmaBuf *dmabuf = ssd->guest_dmabuf; + width = qemu_dmabuf_get_width(dmabuf); + height = qemu_dmabuf_get_height(dmabuf); + if (render_cursor) { egl_dmabuf_import_texture(dmabuf); - if (!dmabuf->texture) { + texture = qemu_dmabuf_get_texture(dmabuf); + if (!texture) { return; } /* source framebuffer */ - egl_fb_setup_for_tex(&ssd->guest_fb, - dmabuf->width, dmabuf->height, - dmabuf->texture, false); + egl_fb_setup_for_tex(&ssd->guest_fb, width, height, + texture, false); /* dest framebuffer */ - if (ssd->blit_fb.width != dmabuf->width || - ssd->blit_fb.height != dmabuf->height) { - trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, dmabuf->width, - dmabuf->height); + if (ssd->blit_fb.width != width || + ssd->blit_fb.height != height) { + trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width, + height); egl_fb_destroy(&ssd->blit_fb); egl_fb_setup_new_tex(&ssd->blit_fb, - dmabuf->width, dmabuf->height); + width, height); fd = egl_get_fd_for_texture(ssd->blit_fb.texture, &stride, &fourcc, NULL); - spice_qxl_gl_scanout(&ssd->qxl, fd, - dmabuf->width, dmabuf->height, + spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, stride, fourcc, false); } } else { - trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, - dmabuf->width, dmabuf->height); + stride = qemu_dmabuf_get_stride(dmabuf); + fourcc = qemu_dmabuf_get_fourcc(dmabuf); + y_0_top = qemu_dmabuf_get_y0_top(dmabuf); + fd = qemu_dmabuf_dup_fd(dmabuf); + + trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height); /* note: spice server will close the fd, so hand over a dup */ - spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd), - dmabuf->width, dmabuf->height, - dmabuf->stride, dmabuf->fourcc, - dmabuf->y0_top); + spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, + stride, fourcc, y_0_top); } - qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height); + qemu_spice_gl_monitor_config(ssd, 0, 0, width, height); ssd->guest_dmabuf_refresh = false; } 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/thirdparty/imgui b/ui/thirdparty/imgui deleted file mode 160000 index fceff3210b..0000000000 --- a/ui/thirdparty/imgui +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fceff3210b9ecfa8fc66710a00f4cabc2447460f diff --git a/ui/thirdparty/implot b/ui/thirdparty/implot deleted file mode 160000 index cc5e1daa5c..0000000000 --- a/ui/thirdparty/implot +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cc5e1daa5c7f2335a9460ae79c829011dc5cef2d diff --git a/ui/thirdparty/meson.build b/ui/thirdparty/meson.build index baa38cb771..2684979282 100644 --- a/ui/thirdparty/meson.build +++ b/ui/thirdparty/meson.build @@ -1,43 +1,12 @@ -imgui_files = files( - 'imgui/imgui.cpp', - 'imgui/imgui_draw.cpp', - 'imgui/imgui_tables.cpp', - 'imgui/imgui_widgets.cpp', - 'imgui/backends/imgui_impl_sdl2.cpp', - 'imgui/backends/imgui_impl_opengl3.cpp', - 'imgui/misc/cpp/imgui_stdlib.cpp', - #'imgui/imgui_demo.cpp', -) - -imgui_cppargs = ['-DIMGUI_IMPL_OPENGL_LOADER_CUSTOM', - '-include', 'imgui_impl_opengl3_loader_override.h'] - -libimgui = static_library('imgui', - sources: imgui_files, - cpp_args: imgui_cppargs, - include_directories: ['.', 'imgui'], - dependencies: [sdl, opengl]) -imgui = declare_dependency(link_with: libimgui, - include_directories: ['imgui', 'imgui/backends']) - -implot_files = files( - 'implot/implot.cpp', - 'implot/implot_items.cpp' - #'implot/implot_demo.cpp', -) - -libimplot = static_library('implot', - sources: implot_files, - include_directories: 'implot', - dependencies: [imgui]) -implot = declare_dependency(link_with: libimplot, - include_directories: 'implot') - noc_ss = ss.source_set() -noc_ss.add(when: 'CONFIG_LINUX', if_true: [gtk, files('noc_file_dialog/noc_file_dialog_gtk.c')]) -noc_ss.add(when: 'CONFIG_WIN32', if_true: files('noc_file_dialog/noc_file_dialog_win32.cc')) -noc_ss.add(when: 'CONFIG_DARWIN', if_true: files('noc_file_dialog/noc_file_dialog_macos.m')) -noc_ss = noc_ss.apply(config_all, strict: false) +if host_os == 'linux' + noc_ss.add([gtk, files('noc_file_dialog/noc_file_dialog_gtk.c')]) +elif host_os == 'windows' + noc_ss.add(files('noc_file_dialog/noc_file_dialog_win32.cc')) +elif host_os == 'darwin' + noc_ss.add(files('noc_file_dialog/noc_file_dialog_macos.m')) +endif +noc_ss = noc_ss.apply(config_target, strict: false) noclib = static_library('noc', sources: noc_ss.sources(), dependencies: noc_ss.dependencies(), @@ -64,7 +33,7 @@ json = declare_dependency(include_directories: 'json') httplib_proj = subproject('cpp-httplib', default_options: ['cpp-httplib_openssl=enabled']) httplib_deps = [httplib_proj.get_variable('cpp_httplib_dep')] -if targetos == 'windows' +if host_os == 'windows' httplib_deps += [crypt32] endif httplib = declare_dependency(dependencies: httplib_deps) diff --git a/ui/trace-events b/ui/trace-events index 977577fbba..3da0d5e280 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" @@ -28,6 +28,7 @@ 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" +gd_motion_event(int ww, int wh, int ws, int x, int y) "ww=%d, wh=%d, ws=%d, x=%d, y=%d" # vnc-auth-sasl.c # vnc-auth-vencrypt.c @@ -90,8 +91,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)" @@ -129,9 +130,10 @@ xkeymap_keymap(const char *name) "keymap '%s'" # clipboard.c clipboard_check_serial(int cur, int recv, bool ok) "cur:%d recv:%d %d" +clipboard_reset_serial(void) "" # vdagent.c -vdagent_open(void) "" +vdagent_fe_open(bool fe_open) "fe_open=%d" vdagent_close(void) "" vdagent_disconnect(void) "" vdagent_send(const char *name) "msg %s" @@ -153,7 +155,18 @@ 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(int selection, unsigned int serial) "selection=%d serial=%u" dbus_clipboard_grab_failed(void) "" +dbus_clipboard_qemu_request(int type) "type=%d" 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)" +dbus_can_share_map(bool share) "can_share_map: %d" + +# 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 4bf50f0c4d..724eff972f 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[] = { @@ -188,7 +185,7 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) vdagent_send_buf(vd); } -static void vdagent_send_caps(VDAgentChardev *vd) +static void vdagent_send_caps(VDAgentChardev *vd, bool request) { g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) + sizeof(VDAgentAnnounceCapabilities) + @@ -208,6 +205,7 @@ static void vdagent_send_caps(VDAgentChardev *vd) #endif } + caps->request = request; vdagent_send_msg(vd, msg); } @@ -300,7 +298,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, @@ -674,7 +672,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; } @@ -714,7 +712,7 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) vd->caps = caps->caps[0]; if (caps->request) { - vdagent_send_caps(vd); + vdagent_send_caps(vd, false); } if (have_mouse(vd) && vd->mouse_hs) { qemu_input_handler_activate(vd->mouse_hs); @@ -723,6 +721,8 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) memset(vd->last_serial, 0, sizeof(vd->last_serial)); if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) { + qemu_clipboard_reset_serial(); + vd->cbpeer.name = "vdagent"; vd->cbpeer.notifier.notify = vdagent_clipboard_notify; vd->cbpeer.request = vdagent_clipboard_request; @@ -873,15 +873,20 @@ 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); + + trace_vdagent_fe_open(fe_open); + 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; } - trace_vdagent_open(); + vdagent_send_caps(vd, true); } static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, @@ -924,10 +929,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/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 47fdae5b21..3f4cfc471d 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -263,8 +263,14 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = (char*)data; - clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */ - datalen--; /* Don't count NULL byte when passing to _start() */ + if (clientdata[datalen - 1] != '\0') { + trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data", + "Missing SASL NUL padding byte"); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + datalen--; /* Discard the extra NUL padding byte */ } err = sasl_server_step(vs->sasl.conn, @@ -289,9 +295,10 @@ static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t le goto authabort; } - if (serveroutlen) { + if (serverout) { vnc_write_u32(vs, serveroutlen + 1); - vnc_write(vs, serverout, serveroutlen + 1); + vnc_write(vs, serverout, serveroutlen); + vnc_write_u8(vs, '\0'); } else { vnc_write_u32(vs, 0); } @@ -384,8 +391,14 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l /* NB, distinction of NULL vs "" is *critical* in SASL */ if (datalen) { clientdata = (char*)data; - clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ - datalen--; /* Don't count NULL byte when passing to _start() */ + if (clientdata[datalen - 1] != '\0') { + trace_vnc_auth_fail(vs, vs->auth, "Malformed SASL client data", + "Missing SASL NUL padding byte"); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + datalen--; /* Discard the extra NUL padding byte */ } err = sasl_server_start(vs->sasl.conn, @@ -410,9 +423,10 @@ static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t l goto authabort; } - if (serveroutlen) { + if (serverout) { vnc_write_u32(vs, serveroutlen + 1); - vnc_write(vs, serverout, serveroutlen + 1); + vnc_write(vs, serverout, serveroutlen); + vnc_write_u8(vs, '\0'); } else { vnc_write_u32(vs, 0); } @@ -524,13 +538,13 @@ static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, s return 0; } -static char * +static int vnc_socket_ip_addr_string(QIOChannelSocket *ioc, bool local, + char **addrstr, Error **errp) { SocketAddress *addr; - char *ret; if (local) { addr = qio_channel_socket_get_local_address(ioc, errp); @@ -538,17 +552,24 @@ vnc_socket_ip_addr_string(QIOChannelSocket *ioc, addr = qio_channel_socket_get_remote_address(ioc, errp); } if (!addr) { - return NULL; + return -1; } if (addr->type != SOCKET_ADDRESS_TYPE_INET) { - error_setg(errp, "Not an inet socket type"); + *addrstr = NULL; qapi_free_SocketAddress(addr); - return NULL; + return 0; } - ret = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port); + *addrstr = g_strdup_printf("%s;%s", addr->u.inet.host, addr->u.inet.port); qapi_free_SocketAddress(addr); - return ret; + return 0; +} + +static bool +vnc_socket_is_unix(QIOChannelSocket *ioc) +{ + SocketAddress *addr = qio_channel_socket_get_local_address(ioc, NULL); + return addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX; } void start_auth_sasl(VncState *vs) @@ -561,15 +582,15 @@ void start_auth_sasl(VncState *vs) int mechlistlen; /* Get local & remote client addresses in form IPADDR;PORT */ - localAddr = vnc_socket_ip_addr_string(vs->sioc, true, &local_err); - if (!localAddr) { + if (vnc_socket_ip_addr_string(vs->sioc, true, + &localAddr, &local_err) < 0) { trace_vnc_auth_fail(vs, vs->auth, "Cannot format local IP", error_get_pretty(local_err)); goto authabort; } - remoteAddr = vnc_socket_ip_addr_string(vs->sioc, false, &local_err); - if (!remoteAddr) { + if (vnc_socket_ip_addr_string(vs->sioc, false, + &remoteAddr, &local_err) < 0) { trace_vnc_auth_fail(vs, vs->auth, "Cannot format remote IP", error_get_pretty(local_err)); g_free(localAddr); @@ -621,16 +642,17 @@ void start_auth_sasl(VncState *vs) goto authabort; } } else { - vs->sasl.wantSSF = 1; + vs->sasl.wantSSF = !vnc_socket_is_unix(vs->sioc); } memset (&secprops, 0, sizeof secprops); /* Inform SASL that we've got an external SSF layer from TLS. * - * Disable SSF, if using TLS+x509+SASL only. TLS without x509 - * is not sufficiently strong + * Disable SSF, if using TLS+x509+SASL only, or UNIX sockets. + * TLS without x509 is not sufficiently strong, nor is plain + * TCP */ - if (vs->vd->is_unix || + if (vnc_socket_is_unix(vs->sioc) || (vs->auth == VNC_AUTH_VENCRYPT && vs->subauth == VNC_AUTH_VENCRYPT_X509SASL)) { /* If we've got TLS or UNIX domain sock, we don't care about SSF */ @@ -674,6 +696,13 @@ void start_auth_sasl(VncState *vs) } trace_vnc_auth_sasl_mech_list(vs, mechlist); + if (g_str_equal(mechlist, "")) { + trace_vnc_auth_fail(vs, vs->auth, "no available SASL mechanisms", ""); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + vs->sasl.mechlist = g_strdup(mechlist); mechlistlen = strlen(mechlist); vnc_write_u32(vs, mechlistlen); 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-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.h b/ui/vnc-enc-zywrle.h index e661ec117d..64fbc90ee7 100644 --- a/ui/vnc-enc-zywrle.h +++ b/ui/vnc-enc-zywrle.h @@ -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.) 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 1856d57380..5fcb35bf25 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) { @@ -991,17 +981,17 @@ int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) } static void vnc_mouse_set(DisplayChangeListener *dcl, - int x, int y, int visible) + int x, int y, bool visible) { /* can we ask the client(s) to move the pointer ??? */ } 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_IS_TEXT_CONSOLE(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: @@ -2456,6 +2450,11 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) } if (read_s32(data, 4) < 0) { + 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."); @@ -2468,7 +2467,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) 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; @@ -2516,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; @@ -2565,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; } @@ -2778,7 +2783,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) vnc_munge_des_rfb_key(key, sizeof(key)); cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_DES, + QCRYPTO_CIPHER_ALGO_DES, QCRYPTO_CIPHER_MODE_ECB, key, G_N_ELEMENTS(key), &err); @@ -3132,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)); @@ -3425,7 +3430,6 @@ static void vnc_display_close(VncDisplay *vd) if (!vd) { return; } - vd->is_unix = false; if (vd->listener) { qio_net_listener_disconnect(vd->listener); @@ -3729,11 +3733,6 @@ static int vnc_display_get_address(const char *addrstr, addr->type = SOCKET_ADDRESS_TYPE_UNIX; addr->u.q_unix.path = g_strdup(addrstr + 5); - if (websocket) { - error_setg(errp, "UNIX sockets not supported with websock"); - goto cleanup; - } - if (to) { error_setg(errp, "Port range not support with UNIX socket"); goto cleanup; @@ -3742,7 +3741,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, ':'); @@ -3790,7 +3789,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; } @@ -3852,7 +3851,7 @@ static int vnc_display_get_addresses(QemuOpts *opts, return 0; } if (qemu_opt_get(opts, "websocket") && - !qcrypto_hash_supports(QCRYPTO_HASH_ALG_SHA1)) { + !qcrypto_hash_supports(QCRYPTO_HASH_ALGO_SHA1)) { error_setg(errp, "SHA1 hash support is required for websockets"); return -1; @@ -3932,8 +3931,6 @@ static int vnc_display_connect(VncDisplay *vd, error_setg(errp, "Expected a single address in reverse mode"); return -1; } - /* TODO SOCKET_ADDRESS_TYPE_FD when fd has AF_UNIX */ - vd->is_unix = saddr_list->value->type == SOCKET_ADDRESS_TYPE_UNIX; sioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(sioc), "vnc-reverse"); if (qio_channel_socket_connect_sync(sioc, saddr_list->value, errp) < 0) { @@ -4064,7 +4061,7 @@ void vnc_display_open(const char *id, Error **errp) } if (password) { if (!qcrypto_cipher_supports( - QCRYPTO_CIPHER_ALG_DES, QCRYPTO_CIPHER_MODE_ECB)) { + QCRYPTO_CIPHER_ALGO_DES, QCRYPTO_CIPHER_MODE_ECB)) { error_setg(errp, "Cipher backend does not support DES algorithm"); goto fail; @@ -4186,11 +4183,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"); @@ -4204,7 +4202,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..acc53a2cc1 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -81,8 +81,8 @@ typedef void VncSendHextileTile(VncState *vs, /* VNC_MAX_WIDTH must be a multiple of VNC_DIRTY_PIXELS_PER_BIT. */ -#define VNC_MAX_WIDTH ROUND_UP(2560, VNC_DIRTY_PIXELS_PER_BIT) -#define VNC_MAX_HEIGHT 2048 +#define VNC_MAX_WIDTH ROUND_UP(5120, VNC_DIRTY_PIXELS_PER_BIT) +#define VNC_MAX_HEIGHT 2160 /* VNC_DIRTY_BITS is the number of bits in the dirty bitmap. */ #define VNC_DIRTY_BITS (VNC_MAX_WIDTH / VNC_DIRTY_PIXELS_PER_BIT) @@ -159,7 +159,6 @@ struct VncDisplay QKbdState *kbd; QemuMutex mutex; - QEMUCursor *cursor; int cursor_msize; uint8_t *cursor_mask; @@ -169,7 +168,6 @@ struct VncDisplay const char *id; QTAILQ_ENTRY(VncDisplay) next; - bool is_unix; char *password; time_t expires; int auth; @@ -465,24 +463,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 +581,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/xemu-snapshots.c b/ui/xemu-snapshots.c index 25f227e6fb..2d7c290643 100644 --- a/ui/xemu-snapshots.c +++ b/ui/xemu-snapshots.c @@ -1,7 +1,7 @@ /* * xemu User Interface * - * Copyright (C) 2020-2022 Matt Borgerson + * Copyright (C) 2020-2025 Matt Borgerson * * 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,6 +28,7 @@ #include "block/block_int.h" #include "block/qapi.h" #include "block/qdict.h" +#include "block/block-io.h" #include "migration/qemu-file.h" #include "migration/snapshot.h" #include "qapi/error.h" @@ -178,7 +179,6 @@ int xemu_snapshots_list(QEMUSnapshotInfo **info, XemuSnapshotData **extra_data, Error **err) { BlockDriverState *bs; - AioContext *aio_context; int snapshots_len; assert(err); @@ -195,11 +195,7 @@ int xemu_snapshots_list(QEMUSnapshotInfo **info, XemuSnapshotData **extra_data, return -1; } - aio_context = bdrv_get_aio_context(bs); - - aio_context_acquire(aio_context); snapshots_len = bdrv_snapshot_list(bs, &xemu_snapshots_metadata); - aio_context_release(aio_context); xemu_snapshots_all_load_data(&xemu_snapshots_metadata, &xemu_snapshots_extra_data, snapshots_len, err); @@ -233,11 +229,8 @@ char *xemu_get_currently_loaded_disc_path(void) continue; } - if (info->value->has_inserted) { - BlockDeviceInfo *inserted = info->value->inserted; - if (inserted->has_node_name) { - file = g_strdup(inserted->file); - } + if (info->value->inserted && info->value->inserted->node_name) { + file = g_strdup(info->value->inserted->file); } } diff --git a/ui/xemu.c b/ui/xemu.c index c8030fc8d0..645081d7e8 100644 --- a/ui/xemu.c +++ b/ui/xemu.c @@ -1,7 +1,7 @@ /* * xemu SDL display driver * - * Copyright (c) 2020-2021 Matt Borgerson + * Copyright (c) 2020-2025 Matt Borgerson * * Based on sdl2.c, sdl2-gl.c * @@ -134,8 +134,6 @@ void xemu_toggle_fullscreen(void) #define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \ / SDL2_REFRESH_INTERVAL_BUSY + 1) -// static void sdl_update_caption(struct sdl2_console *scon); - static struct sdl2_console *get_scon_from_window(uint32_t window_id) { int i; @@ -147,50 +145,6 @@ static struct sdl2_console *get_scon_from_window(uint32_t window_id) return NULL; } -#if 0 -void sdl2_window_create(struct sdl2_console *scon) -{ - int flags = 0; - - if (!scon->surface) { - return; - } - assert(!scon->real_window); - - if (gui_fullscreen) { - flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } else { - flags |= SDL_WINDOW_RESIZABLE; - } - if (scon->hidden) { - flags |= SDL_WINDOW_HIDDEN; - } - - scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 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(); - } - sdl_update_caption(scon); -} - -void sdl2_window_destroy(struct sdl2_console *scon) -{ - if (!scon->real_window) { - return; - } - - SDL_DestroyRenderer(scon->real_renderer); - scon->real_renderer = NULL; - SDL_DestroyWindow(scon->real_window); - scon->real_window = NULL; -} -#endif - void sdl2_window_resize(struct sdl2_console *scon) { if (!scon->real_window) { @@ -211,36 +165,6 @@ static void sdl2_redraw(struct sdl2_console *scon) static void sdl_update_caption(struct sdl2_console *scon) { -#if 0 - char win_title[1024]; - char icon_title[1024]; - const char *status = ""; - - if (!runstate_is_running()) { - status = " [Stopped]"; - } else if (gui_grab) { - if (alt_grab) { - status = " - Press Ctrl-Alt-Shift-G to exit grab"; - } else if (ctrl_grab) { - status = " - Press Right-Ctrl-G to exit grab"; - } else { - status = " - Press Ctrl-Alt-G to exit grab"; - } - } - - if (qemu_name) { - snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, - scon->idx, status); - snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); - } else { - snprintf(win_title, sizeof(win_title), "QEMU%s", status); - snprintf(icon_title, sizeof(icon_title), "QEMU"); - } - - if (scon->real_window) { - SDL_SetWindowTitle(scon->real_window, win_title); - } -#endif } static void sdl_hide_cursor(struct sdl2_console *scon) @@ -252,7 +176,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); } } @@ -263,12 +187,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); @@ -279,32 +203,6 @@ static void sdl_show_cursor(struct sdl2_console *scon) static void sdl_grab_start(struct sdl2_console *scon) { -#if 0 - QemuConsole *con = scon ? scon->dcl.con : NULL; - - if (!con || !qemu_console_is_graphic(con)) { - return; - } - /* - * If the application is not active, do not try to enter grab state. This - * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the - * application (SDL bug). - */ - if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { - return; - } - if (guest_cursor) { - SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { - SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); - } - } else { - sdl_hide_cursor(scon); - } - SDL_SetWindowGrab(scon->real_window, SDL_TRUE); - gui_grab = 1; - sdl_update_caption(scon); -#endif } static void sdl_grab_end(struct sdl2_console *scon) @@ -329,7 +227,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); @@ -358,7 +256,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, @@ -395,12 +293,6 @@ static void set_full_screen(struct sdl2_console *scon, bool set) } SDL_SetWindowFullscreen(scon->real_window, 0); } - - // Note: If this gets called while rendering HUD, we will draw twice. Just - // wait for next refresh. -#if 0 - sdl2_redraw(scon); -#endif } static void toggle_full_screen(struct sdl2_console *scon) @@ -493,13 +385,6 @@ static void handle_keyup(SDL_Event *ev) static void handle_textinput(SDL_Event *ev) { - struct sdl2_console *scon = get_scon_from_window(ev->text.windowID); - QemuConsole *con = scon ? scon->dcl.con : NULL; - - if (qemu_console_is_graphic(con)) { - return; - } - kbd_put_string_console(con, ev->text.text, strlen(ev->text.text)); } static void handle_mousemotion(SDL_Event *ev) @@ -511,7 +396,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; @@ -527,7 +412,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); } @@ -544,7 +429,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); @@ -613,7 +498,7 @@ static void handle_windowevent(SDL_Event *ev) break; case SDL_WINDOWEVENT_FOCUS_GAINED: 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 @@ -724,7 +609,7 @@ void sdl2_poll_events(struct sdl2_console *scon) } static void sdl_mouse_warp(DisplayChangeListener *dcl, - int x, int y, int on) + int x, int y, bool on) { struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); @@ -736,9 +621,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); } } @@ -776,21 +661,11 @@ 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); } } -#if 0 -static void sdl_cleanup(void) -{ - if (guest_sprite) { - SDL_FreeCursor(guest_sprite); - } - SDL_QuitSubSystem(SDL_INIT_VIDEO); -} -#endif - static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_name = "sdl2-gl", .dpy_gfx_update = sdl2_gl_update, @@ -798,8 +673,6 @@ static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_gfx_check_format = xb_console_gl_check_format, .dpy_mouse_set = sdl_mouse_warp, .dpy_cursor_define = sdl_mouse_define, - .dpy_gl_scanout_disable = sdl2_gl_scanout_disable, - .dpy_gl_scanout_texture = sdl2_gl_scanout_texture, .dpy_gl_update = sdl2_gl_scanout_flush, }; @@ -845,7 +718,7 @@ static void sdl2_display_very_early_init(DisplayOptions *o) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); char *title = g_strdup_printf("xemu | v%s" -#if XEMU_DEBUG_BUILD +#ifdef XEMU_DEBUG_BUILD " Debug" #endif , xemu_version); @@ -1055,7 +928,7 @@ void xb_surface_gl_create_texture(DisplaySurface *surface) { assert(QEMU_IS_ALIGNED(surface_stride(surface), surface_bytes_per_pixel(surface))); - switch (surface->format) { + switch (surface_format(surface)) { case PIXMAN_BE_b8g8r8x8: case PIXMAN_BE_b8g8r8a8: surface->glformat = GL_BGRA_EXT; @@ -1100,24 +973,6 @@ void xb_surface_gl_destroy_texture(DisplaySurface *surface) surface->texture = 0; } -#if 0 -static void sdl2_set_scanout_mode(struct sdl2_console *scon, bool scanout) -{ - if (scon->scanout_mode == scanout) { - return; - } - - scon->scanout_mode = scanout; - if (!scon->scanout_mode) { - egl_fb_destroy(&scon->guest_fb); - if (scon->surface) { - surface_gl_destroy_texture(scon->surface); - surface_gl_create_texture(scon->surface); - } - } -} -#endif - void sdl2_gl_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { @@ -1195,7 +1050,7 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl) * possible lengthy blocking (for vsync). */ qemu_mutex_lock_main_loop(); - qemu_mutex_lock_iothread(); + bql_lock(); sdl2_poll_events(scon); glClearColor(0, 0, 0, 0); @@ -1205,7 +1060,7 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl) xemu_hud_render(); // Release BQL before swapping (which may sleep if swap interval is not immediate) - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_unlock_main_loop(); glFinish(); @@ -1214,12 +1069,12 @@ void sdl2_gl_refresh(DisplayChangeListener *dcl) /* VGA update (see note above) + vblank */ qemu_mutex_lock_main_loop(); - qemu_mutex_lock_iothread(); + bql_lock(); graphic_hw_update(scon->dcl.con); if (scon->updates && scon->surface) { scon->updates = 0; } - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_unlock_main_loop(); /* @@ -1294,11 +1149,11 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, SDL_GL_MakeCurrent(scon->real_window, scon->winctx); SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - if (scon->opts->gl == DISPLAYGL_MODE_ON || - scon->opts->gl == DISPLAYGL_MODE_CORE) { + if (scon->opts->gl == DISPLAY_GL_MODE_ON || + scon->opts->gl == DISPLAY_GL_MODE_CORE) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - } else if (scon->opts->gl == DISPLAYGL_MODE_ES) { + } else if (scon->opts->gl == DISPLAY_GL_MODE_ES) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); } @@ -1310,7 +1165,7 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, /* If SDL fail to create a GL context and we use the "on" flag, * then try to fallback to GLES. */ - if (!ctx && scon->opts->gl == DISPLAYGL_MODE_ON) { + if (!ctx && scon->opts->gl == DISPLAY_GL_MODE_ON) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); ctx = SDL_GL_CreateContext(scon->real_window); @@ -1347,14 +1202,6 @@ QEMUGLContext sdl2_gl_get_current_context(DisplayChangeListener *dcl) void sdl2_gl_scanout_disable(DisplayChangeListener *dcl) { assert(0); -#if 0 - struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); - - assert(scon->opengl); - scon->w = 0; - scon->h = 0; - sdl2_set_scanout_mode(scon, false); -#endif } void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, @@ -1366,48 +1213,12 @@ void sdl2_gl_scanout_texture(DisplayChangeListener *dcl, uint32_t w, uint32_t h) { assert(0); -#if 0 - struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); - - assert(scon->opengl); - scon->x = x; - scon->y = y; - scon->w = w; - scon->h = h; - scon->y0_top = backing_y_0_top; - - SDL_GL_MakeCurrent(scon->real_window, scon->winctx); - - sdl2_set_scanout_mode(scon, true); - egl_fb_setup_for_tex(&scon->guest_fb, backing_width, backing_height, - backing_id, false); -#endif } void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { assert(0); -#if 0 - struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); - int ww, wh; - - assert(scon->opengl); - if (!scon->scanout_mode) { - return; - } - if (!scon->guest_fb.framebuffer) { - return; - } - - SDL_GL_MakeCurrent(scon->real_window, scon->winctx); - - SDL_GetWindowSize(scon->real_window, &ww, &wh); - egl_fb_setup_default(&scon->win_fb, ww, wh); - egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top); - - SDL_GL_SwapWindow(scon->real_window); -#endif } // sdl2-input.c @@ -1415,27 +1226,12 @@ void sdl2_process_key(struct sdl2_console *scon, SDL_KeyboardEvent *ev) { int qcode; - QemuConsole *con = scon->dcl.con; if (ev->keysym.scancode >= qemu_input_map_usb_to_qcode_len) { return; } qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode]; qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); - - if (!qemu_console_is_graphic(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'); - break; - default: - kbd_put_qcode_console(con, qcode, ctrl); - break; - } - } - } } int gArgc; @@ -1550,9 +1346,9 @@ int main(int argc, char **argv) DPRINTF("Main thread: initializing app\n"); qemu_mutex_lock_main_loop(); - qemu_mutex_lock_iothread(); + bql_lock(); xemu_input_init(); - qemu_mutex_unlock_iothread(); + bql_unlock(); qemu_mutex_unlock_main_loop(); while (1) { @@ -1571,7 +1367,7 @@ void xemu_eject_disc(Error **errp) xemu_settings_set_string(&g_config.sys.files.dvd_path, ""); // Xbox software may request that the drive open, but do it now anyway - qmp_eject(true, "ide0-cd1", false, NULL, true, false, &error); + qmp_eject("ide0-cd1", NULL, true, false, &error); if (error) { error_propagate(errp, error); } @@ -1587,9 +1383,8 @@ void xemu_load_disc(const char *path, Error **errp) xbox_smc_eject_button(); xemu_settings_set_string(&g_config.sys.files.dvd_path, ""); - qmp_blockdev_change_medium(true, "ide0-cd1", false, NULL, path, - false, "", false, false, false, 0, - &error); + qmp_blockdev_change_medium("ide0-cd1", NULL, path, "raw", false, false, + false, 0, &error); if (error) { error_propagate(errp, error); } else { diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index a9a6c6ec85..2347ded770 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -387,9 +387,6 @@ void MainMenuInputView::Draw() ImGui::Image(id, xmu_display_size, ImVec2(0.5f * i, 1), ImVec2(0.5f * (i + 1), 0)); - ImVec2 pos = ImGui::GetCursorPos(); - - ImGui::SetCursorPos(pos); // Button to generate a new XMU ImGui::PushID(i); @@ -1099,11 +1096,11 @@ void MainMenuSnapshotsView::Draw() XemuSnapshotData *data = &g_snapshot_mgr.m_extra_data[i]; int current_snapshot_binding = -1; - for (int i = 0; i < 4; ++i) { - if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[i]), + for (int j = 0; j < 4; ++j) { + if (g_strcmp0(*(g_snapshot_shortcut_index_key_map[j]), snapshot->name) == 0) { assert(current_snapshot_binding == -1); - current_snapshot_binding = i; + current_snapshot_binding = j; } } diff --git a/ui/xui/meson.build b/ui/xui/meson.build index 2fe5b2c64a..1f2f8d3aed 100644 --- a/ui/xui/meson.build +++ b/ui/xui/meson.build @@ -22,4 +22,6 @@ xemu_ss.add(files( 'widgets.cc', )) -xemu_ss.add(when: 'CONFIG_WIN32', if_true: files('update.cc')) +if host_os == 'windows' + xemu_ss.add(files('update.cc')) +endif diff --git a/ui/xui/popup-menu.cc b/ui/xui/popup-menu.cc index 82ae785137..f62b75042a 100644 --- a/ui/xui/popup-menu.cc +++ b/ui/xui/popup-menu.cc @@ -56,13 +56,13 @@ bool PopupMenuCheck(std::string text, std::string icon = "", bool v = false) ImGui::PushFont(g_font_mgr.m_menu_font); const ImVec2 p0 = ImGui::GetItemRectMin(); const ImVec2 p1 = ImGui::GetItemRectMax(); - const char *icon = ICON_FA_CHECK; - ImVec2 ts_icon = ImGui::CalcTextSize(icon); + const char *check_icon = ICON_FA_CHECK; + ImVec2 ts_icon = ImGui::CalcTextSize(check_icon); ImDrawList *draw_list = ImGui::GetWindowDrawList(); ImGuiStyle &style = ImGui::GetStyle(); draw_list->AddText(ImVec2(p1.x - style.FramePadding.x - ts_icon.x, p0.y + (p1.y - p0.y - ts_icon.y) / 2), - ImGui::GetColorU32(ImGuiCol_Text), icon); + ImGui::GetColorU32(ImGuiCol_Text), check_icon); ImGui::PopFont(); } return status; diff --git a/ui/xui/xemu-hud.h b/ui/xui/xemu-hud.h index 81e82aff18..14e049da68 100644 --- a/ui/xui/xemu-hud.h +++ b/ui/xui/xemu-hud.h @@ -32,7 +32,6 @@ extern "C" { // Implemented in xemu.c int xemu_is_fullscreen(void); -void xemu_monitor_init(void); void xemu_toggle_fullscreen(void); void xemu_eject_disc(Error **errp); void xemu_load_disc(const char *path, Error **errp); diff --git a/util/aio-posix.c b/util/aio-posix.c index 731f3826c0..06bf9f456c 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -17,6 +17,7 @@ #include "block/block.h" #include "block/thread-pool.h" #include "qemu/main-loop.h" +#include "qemu/lockcnt.h" #include "qemu/rcu.h" #include "qemu/rcu_queue.h" #include "qemu/sockets.h" @@ -99,7 +100,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 +144,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 +179,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 +195,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 +283,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 +346,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 +369,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 +379,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 +439,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 +587,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 +657,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 +778,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 98c5accd29..b5336cf5fd 100644 --- a/util/aio-wait.c +++ b/util/aio-wait.c @@ -82,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 80cfe012ad..6583d5c5f3 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -18,10 +18,12 @@ #include "qemu/osdep.h" #include "block/block.h" #include "qemu/main-loop.h" +#include "qemu/lockcnt.h" #include "qemu/queue.h" #include "qemu/sockets.h" #include "qapi/error.h" #include "qemu/rcu_queue.h" +#include "qemu/error-report.h" struct AioHandler { EventNotifier *e; @@ -31,7 +33,6 @@ struct AioHandler { GPollFD pfd; int deleted; void *opaque; - bool is_external; QLIST_ENTRY(AioHandler) node; }; @@ -63,20 +64,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 +94,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 +110,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 +121,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 +131,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 +158,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); @@ -367,8 +364,7 @@ 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); } @@ -443,7 +439,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-teardown.c b/util/async-teardown.c deleted file mode 100644 index 62bfce1b3c..0000000000 --- a/util/async-teardown.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include "qemu/osdep.h" -#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); -#if !defined(HOST_HPPA) - /* The top is at the end of the area, except on HPPA. */ - stack_ptr += stack_size; -#endif - - 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/util/async.c b/util/async.c index f449c3444e..99db28389f 100644 --- a/util/async.c +++ b/util/async.c @@ -27,8 +27,10 @@ #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/lockcnt.h" #include "qemu/rcu_queue.h" #include "block/raw-aio.h" #include "qemu/coroutine_int.h" @@ -64,6 +66,7 @@ struct QEMUBH { void *opaque; QSLIST_ENTRY(QEMUBH) next; unsigned flags; + MemReentrancyGuard *reentrancy_guard; }; /* Called concurrently from any thread */ @@ -73,25 +76,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 +118,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 +141,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 +150,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,6 +183,7 @@ 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); /* @@ -385,11 +412,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); } @@ -460,15 +488,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)) { @@ -481,8 +509,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(); } @@ -505,6 +534,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); } @@ -531,12 +565,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); } } @@ -562,7 +594,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); @@ -588,6 +619,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); @@ -650,7 +683,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; @@ -663,7 +696,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); @@ -675,9 +708,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); } } @@ -691,16 +722,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) @@ -709,7 +730,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(); } @@ -726,7 +747,7 @@ void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, int64_t max, Error **errp) { - if (min > max || !max || min > INT_MAX || max > INT_MAX) { + if (min > max || max <= 0 || min < 0 || min > INT_MAX || max > INT_MAX) { error_setg(errp, "bad thread-pool-min/thread-pool-max values"); return; } 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/block-helpers.c b/util/block-helpers.c index c4851432f5..052b4e1476 100644 --- a/util/block-helpers.c +++ b/util/block-helpers.c @@ -10,12 +10,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qerror.h" #include "block-helpers.h" /** * check_block_size: - * @id: The unique ID of the object * @name: The name of the property being validated * @value: The block size in bytes * @errp: A pointer to an area to store an error @@ -24,23 +22,23 @@ * 1. At least MIN_BLOCK_SIZE * 2. No larger than MAX_BLOCK_SIZE * 3. A power of 2 + * + * Returns: true on success, false on failure */ -void check_block_size(const char *id, const char *name, int64_t value, - Error **errp) +bool check_block_size(const char *name, int64_t value, Error **errp) { - /* value of 0 means "unset" */ - if (value && (value < MIN_BLOCK_SIZE || value > MAX_BLOCK_SIZE)) { - error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, - id, name, value, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE); - return; + if (!value) { + /* unset */ + return true; } - /* We rely on power-of-2 blocksizes for bitmasks */ - if ((value & (value - 1)) != 0) { + if (value < MIN_BLOCK_SIZE || value > MAX_BLOCK_SIZE + || (value & (value - 1))) { error_setg(errp, - "Property %s.%s doesn't take value '%" PRId64 - "', it's not a power of 2", - id, name, value); - return; + "parameter %s must be a power of 2 between %" PRId64 + " and %" PRId64, + name, MIN_BLOCK_SIZE, MAX_BLOCK_SIZE); + return false; } + return true; } diff --git a/util/block-helpers.h b/util/block-helpers.h index b53295a529..838b0825f6 100644 --- a/util/block-helpers.h +++ b/util/block-helpers.h @@ -13,7 +13,6 @@ #define MAX_BLOCK_SIZE (2 * MiB) #define MAX_BLOCK_SIZE_STR "2 MiB" -void check_block_size(const char *id, const char *name, int64_t value, - Error **errp); +bool check_block_size(const char *name, int64_t value, Error **errp); #endif /* BLOCK_HELPERS_H */ diff --git a/util/bufferiszero.c b/util/bufferiszero.c index ec3cd4ca15..522146dab9 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -24,332 +24,103 @@ #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) +typedef bool (*biz_accel_fn)(const void *, size_t); + +static bool buffer_is_zero_int_lt256(const void *buf, size_t len) { - if (unlikely(len < 8)) { - /* For a very small buffer, simply accumulate all the bytes. */ - const unsigned char *p = buf; - const unsigned char *e = buf + len; - unsigned char t = 0; + uint64_t t; + const uint64_t *p, *e; - do { - t |= *p++; - } while (p < e); - - return t == 0; - } else { - /* Otherwise, use the unaligned memory access functions to - handle the beginning and end of the buffer, with a couple - of loops handling the middle aligned section. */ - uint64_t t = ldq_he_p(buf); - const uint64_t *p = (uint64_t *)(((uintptr_t)buf + 8) & -8); - const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8); - - for (; p + 8 <= e; p += 8) { - __builtin_prefetch(p + 8); - if (t) { - return false; - } - t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; - } - while (p < e) { - t |= *p++; - } - t |= ldq_he_p(buf + len - 8); - - return t == 0; + /* + * Use unaligned memory access functions to handle + * the beginning and end of the buffer. + */ + if (unlikely(len <= 8)) { + return (ldl_he_p(buf) | ldl_he_p(buf + len - 4)) == 0; } + + t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); + p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); + e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); + + /* Read 0 to 31 aligned words from the middle. */ + while (p < e) { + t |= *p++; + } + return t == 0; } -#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 - -/* Note that each of these vectorized functions require len >= 64. */ - -static bool -buffer_zero_sse2(const void *buf, size_t len) +static bool buffer_is_zero_int_ge256(const void *buf, size_t len) { - __m128i t = _mm_loadu_si128(buf); - __m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16); - __m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16); - __m128i zero = _mm_setzero_si128(); + /* + * Use unaligned memory access functions to handle + * the beginning and end of the buffer. + */ + uint64_t t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); + const uint64_t *p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); + const uint64_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); - /* Loop over 16-byte aligned blocks of 64. */ - while (likely(p <= e)) { - __builtin_prefetch(p); - t = _mm_cmpeq_epi8(t, zero); - if (unlikely(_mm_movemask_epi8(t) != 0xFFFF)) { + /* Collect a partial block at the tail end. */ + t |= e[-7] | e[-6] | e[-5] | e[-4] | e[-3] | e[-2] | e[-1]; + + /* + * Loop over 64 byte blocks. + * With the head and tail removed, e - p >= 30, + * so the loop must iterate at least 3 times. + */ + do { + if (t) { return false; } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } + t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; + p += 8; + } while (p < e - 7); - /* Finish the aligned tail. */ - t |= e[-3]; - t |= e[-2]; - t |= e[-1]; - - /* Finish the unaligned tail. */ - t |= _mm_loadu_si128(buf + len - 16); - - 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 -buffer_zero_sse4(const void *buf, size_t len) -{ - __m128i t = _mm_loadu_si128(buf); - __m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16); - __m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16); - - /* Loop over 16-byte aligned blocks of 64. */ - while (likely(p <= e)) { - __builtin_prefetch(p); - if (unlikely(!_mm_testz_si128(t, t))) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } - - /* Finish the aligned tail. */ - t |= e[-3]; - t |= e[-2]; - t |= e[-1]; - - /* Finish the unaligned tail. */ - t |= _mm_loadu_si128(buf + len - 16); - - return _mm_testz_si128(t, t); + return t == 0; } -#pragma GCC pop_options -#pragma GCC push_options -#pragma GCC target("avx2") -#include +#include "host/bufferiszero.c.inc" -static bool -buffer_zero_avx2(const void *buf, size_t len) -{ - /* Begin with an unaligned head of 32 bytes. */ - __m256i t = _mm256_loadu_si256(buf); - __m256i *p = (__m256i *)(((uintptr_t)buf + 5 * 32) & -32); - __m256i *e = (__m256i *)(((uintptr_t)buf + len) & -32); +static biz_accel_fn buffer_is_zero_accel; +static unsigned accel_index; - /* Loop over 32-byte aligned blocks of 128. */ - while (p <= e) { - __builtin_prefetch(p); - if (unlikely(!_mm256_testz_si256(t, t))) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } ; - - /* Finish the last block of 128 unaligned. */ - t |= _mm256_loadu_si256(buf + len - 4 * 32); - t |= _mm256_loadu_si256(buf + len - 3 * 32); - t |= _mm256_loadu_si256(buf + len - 2 * 32); - t |= _mm256_loadu_si256(buf + len - 1 * 32); - - 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 -buffer_zero_avx512(const void *buf, size_t len) -{ - /* Begin with an unaligned head of 64 bytes. */ - __m512i t = _mm512_loadu_si512(buf); - __m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64); - __m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64); - - /* Loop over 64-byte aligned blocks of 256. */ - while (p <= e) { - __builtin_prefetch(p); - if (unlikely(_mm512_test_epi64_mask(t, t))) { - return false; - } - t = p[-4] | p[-3] | p[-2] | p[-1]; - p += 4; - } - - t |= _mm512_loadu_si512(buf + len - 4 * 64); - t |= _mm512_loadu_si512(buf + len - 3 * 64); - t |= _mm512_loadu_si512(buf + len - 2 * 64); - t |= _mm512_loadu_si512(buf + len - 1 * 64); - - return !_mm512_test_epi64_mask(t, t); - -} -#pragma GCC pop_options -#endif - - -/* Note that for test_buffer_is_zero_next_accel, the most preferred - * ISA must have the least significant bit. - */ -#define CACHE_AVX512F 1 -#define CACHE_AVX2 2 -#define CACHE_SSE4 4 -#define CACHE_SSE2 8 - -/* Make sure that these variables are appropriately initialized when - * SSE2 is enabled on the compiler command-line, but the compiler is - * too old to support CONFIG_AVX2_OPT. - */ -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -# define INIT_CACHE 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 -#endif - -static unsigned cpuid_cache = INIT_CACHE; -static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL; -static int length_to_accel = 64; - -static void init_accel(unsigned cache) -{ - bool (*fn)(const void *, size_t) = buffer_zero_int; - if (cache & CACHE_SSE2) { - fn = buffer_zero_sse2; - length_to_accel = 64; - } -#ifdef CONFIG_AVX2_OPT - if (cache & CACHE_SSE4) { - fn = buffer_zero_sse4; - length_to_accel = 64; - } - if (cache & CACHE_AVX2) { - fn = buffer_zero_avx2; - length_to_accel = 128; - } -#endif -#ifdef CONFIG_AVX512F_OPT - if (cache & CACHE_AVX512F) { - fn = buffer_zero_avx512; - length_to_accel = 256; - } -#endif - buffer_accel = fn; -} - -#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) -#include "qemu/cpuid.h" - -static void __attribute__((constructor)) init_cpuid_cache(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); -} -#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; -} - -static bool select_accel_fn(const void *buf, size_t len) -{ - if (likely(len >= length_to_accel)) { - return buffer_accel(buf, len); - } - return buffer_zero_int(buf, len); -} - -#else -#define select_accel_fn buffer_zero_int -bool test_buffer_is_zero_next_accel(void) -{ - return false; -} -#endif - -/* - * Checks if a buffer is all zeroes - */ -bool buffer_is_zero(const void *buf, size_t len) +bool buffer_is_zero_ool(const void *buf, size_t len) { if (unlikely(len == 0)) { return true; } + if (!buffer_is_zero_sample3(buf, len)) { + return false; + } + /* All bytes are covered for any len <= 3. */ + if (unlikely(len <= 3)) { + return true; + } - /* Fetch the beginning of the buffer while we select the accelerator. */ - __builtin_prefetch(buf); - - /* Use an optimized zero check if possible. Note that this also - includes a check for an unrolled loop over 64-bit integers. */ - return select_accel_fn(buf, len); + if (likely(len >= 256)) { + return buffer_is_zero_accel(buf, len); + } + return buffer_is_zero_int_lt256(buf, len); +} + +bool buffer_is_zero_ge256(const void *buf, size_t len) +{ + return buffer_is_zero_accel(buf, len); +} + +bool test_buffer_is_zero_next_accel(void) +{ + if (accel_index != 0) { + buffer_is_zero_accel = accel_table[--accel_index]; + return true; + } + return false; +} + +static void __attribute__((constructor)) init_accel(void) +{ + accel_index = best_accel(); + buffer_is_zero_accel = accel_table[accel_index]; } diff --git a/util/cacheflush.c b/util/cacheflush.c index 2c2c73e085..a08906155a 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -121,8 +121,12 @@ static void sys_cache_info(int *isize, int *dsize) static bool have_coherent_icache; #endif -#if defined(__aarch64__) && !defined(CONFIG_DARWIN) -/* Apple does not expose CTR_EL0, so we must use system interfaces. */ +#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) { @@ -225,15 +229,26 @@ static void __attribute__((constructor)) init_cache_info(void) /* 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 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..57468890c3 --- /dev/null +++ b/util/cpuinfo-aarch64.c @@ -0,0 +1,117 @@ +/* + * 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_ELF_AUX_INFO +#include +#endif +#ifdef CONFIG_DARWIN +# include +#endif +#if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) +# include +# include +# include +# 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; + +#if defined(CONFIG_LINUX) || defined(CONFIG_ELF_AUX_INFO) + 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 +#if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) + int mib[2]; + uint64_t isar0; + uint64_t pfr1; + size_t len; + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_ID_AA64ISAR0; + len = sizeof(isar0); + if (sysctl(mib, 2, &isar0, &len, NULL, 0) != -1) { + if (ID_AA64ISAR0_ATOMIC(isar0) >= ID_AA64ISAR0_ATOMIC_IMPL) { + info |= CPUINFO_LSE; + } + if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_BASE) { + info |= CPUINFO_AES; + } + if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_PMULL) { + info |= CPUINFO_PMULL; + } + } + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_ID_AA64PFR1; + len = sizeof(pfr1); + if (sysctl(mib, 2, &pfr1, &len, NULL, 0) != -1) { + if (ID_AA64PFR1_BT(pfr1) >= ID_AA64PFR1_BT_IMPL) { + info |= 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..c8c8a1b370 --- /dev/null +++ b/util/cpuinfo-i386.c @@ -0,0 +1,102 @@ +/* + * 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_SSE2 ? CPUINFO_SSE2 : 0); + info |= (c & bit_OSXSAVE ? CPUINFO_OSXSAVE : 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..bb1f7f698b --- /dev/null +++ b/util/cpuinfo-loongarch.c @@ -0,0 +1,36 @@ +/* + * 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); + info |= (hwcap & HWCAP_LOONGARCH_LASX ? CPUINFO_LASX : 0); + + cpuinfo = info; + return info; +} diff --git a/util/cpuinfo-ppc.c b/util/cpuinfo-ppc.c new file mode 100644 index 0000000000..4d3d3aae0b --- /dev/null +++ b/util/cpuinfo-ppc.c @@ -0,0 +1,76 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for ppc. + */ + +#include "qemu/osdep.h" +#include "host/cpuinfo.h" + +#ifdef CONFIG_LINUX +# include +# ifdef CONFIG_GETAUXVAL +# include +# else +# include "elf.h" +# endif +#endif +#if defined(CONFIG_ELF_AUX_INFO) +# include +# include +# ifndef PPC_FEATURE2_ARCH_3_1 +# define PPC_FEATURE2_ARCH_3_1 0 +# endif +# define PPC_FEATURE2_VEC_CRYPTO PPC_FEATURE2_HAS_VEC_CRYPTO +#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; + } + + info = CPUINFO_ALWAYS; + +#if defined(CONFIG_LINUX) || defined(CONFIG_ELF_AUX_INFO) + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); + + /* 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; + } + } + } +#endif + + cpuinfo = info; + return info; +} diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c new file mode 100644 index 0000000000..971c924012 --- /dev/null +++ b/util/cpuinfo-riscv.c @@ -0,0 +1,151 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Host specific cpu identification for RISC-V. + */ + +#include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "host/cpuinfo.h" + +#ifdef CONFIG_ASM_HWPROBE_H +#include +#include +#include +#endif + +unsigned cpuinfo; +unsigned riscv_lg2_vlenb; +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; + +#ifdef __linux__ + uc->uc_mcontext.__gregs[REG_PC] += 4; +#elif defined(__OpenBSD__) + uc->sc_sepc += 4; +#else +# error Unsupported OS +#endif + + got_sigill = 1; +} + +/* Called both as constructor and (possibly) via other constructors. */ +unsigned __attribute__((constructor)) cpuinfo_init(void) +{ + unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND | CPUINFO_ZVE64X; + unsigned info = cpuinfo; + + if (info) { + return info; + } + + /* Test for compile-time settings. */ +#if defined(__riscv_arch_test) && defined(__riscv_zba) + info |= CPUINFO_ZBA; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zbb) + info |= CPUINFO_ZBB; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zicond) + info |= CPUINFO_ZICOND; +#endif +#if defined(__riscv_arch_test) && \ + (defined(__riscv_vector) || defined(__riscv_zve64x)) + info |= CPUINFO_ZVE64X; +#endif + left &= ~info; + +#ifdef CONFIG_ASM_HWPROBE_H + if (left) { + /* + * TODO: glibc 2.40 will introduce , which + * provides __riscv_hwprobe and __riscv_hwprobe_one, + * which is a slightly cleaner interface. + */ + struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 }; + if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0 + && pair.key >= 0) { + info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0; + info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0; + left &= ~(CPUINFO_ZBA | CPUINFO_ZBB); +#ifdef RISCV_HWPROBE_EXT_ZICOND + info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; + left &= ~CPUINFO_ZICOND; +#endif + /* For rv64, V is Zve64d, a superset of Zve64x. */ + info |= pair.value & RISCV_HWPROBE_IMA_V ? CPUINFO_ZVE64X : 0; +#ifdef RISCV_HWPROBE_EXT_ZVE64X + info |= pair.value & RISCV_HWPROBE_EXT_ZVE64X ? CPUINFO_ZVE64X : 0; +#endif + } + } +#endif /* CONFIG_ASM_HWPROBE_H */ + + /* + * We only detect support for vectors with hwprobe. All kernels with + * support for vectors in userspace also support the hwprobe syscall. + */ + left &= ~CPUINFO_ZVE64X; + + if (left) { + 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); + + if (left & CPUINFO_ZBA) { + /* Probe for Zba: add.uw zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBA; + left &= ~CPUINFO_ZBA; + } + + if (left & CPUINFO_ZBB) { + /* Probe for Zbb: andn zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBB; + left &= ~CPUINFO_ZBB; + } + + if (left & CPUINFO_ZICOND) { + /* Probe for Zicond: czero.eqz zero,zero,zero. */ + got_sigill = 0; + asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZICOND; + left &= ~CPUINFO_ZICOND; + } + + sigaction(SIGILL, &sa_old, NULL); + assert(left == 0); + } + + if (info & CPUINFO_ZVE64X) { + /* + * We are guaranteed by RVV-1.0 that VLEN is a power of 2. + * We are guaranteed by Zve64x that VLEN >= 64, and that + * EEW of {8,16,32,64} are supported. + */ + unsigned long vlenb; + /* csrr %0, vlenb */ + asm volatile(".insn i 0x73, 0x2, %0, zero, -990" : "=r"(vlenb)); + assert(vlenb >= 8); + assert(is_power_of_2(vlenb)); + /* Cache VLEN in a convenient form. */ + riscv_lg2_vlenb = ctz32(vlenb); + } + + info |= CPUINFO_ALWAYS; + 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 def9c746ce..9803f11a59 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -194,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); @@ -314,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; @@ -385,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. @@ -399,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) @@ -408,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; } @@ -437,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. @@ -451,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; } @@ -474,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); @@ -496,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. @@ -518,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; } @@ -538,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. @@ -561,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; } @@ -589,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; } @@ -606,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) @@ -614,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; } @@ -640,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. @@ -660,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; } @@ -674,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; } @@ -715,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; @@ -776,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; } @@ -784,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) @@ -949,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. */ @@ -1072,16 +1144,10 @@ void qemu_init_exec_dir(const char *argv0) #endif } -const char *qemu_get_exec_dir(void) -{ - return exec_dir; -} - 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; @@ -1092,24 +1158,30 @@ char *get_relocated_path(const char *dir) g_string_append(result, "/qemu-bundle"); if (access(result->str, R_OK) == 0) { #ifdef G_OS_WIN32 - size_t size = mbsrtowcs(NULL, &dir, 0, &(mbstate_t){0}) + 1; + const char *src = dir; + size_t size = mbsrtowcs(NULL, &src, 0, &(mbstate_t){0}) + 1; PWSTR wdir = g_new(WCHAR, size); - mbsrtowcs(wdir, &dir, size, &(mbstate_t){0}); + mbsrtowcs(wdir, &src, size, &(mbstate_t){0}); PCWSTR wdir_skipped_root; - PathCchSkipRoot(wdir, &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); + } - 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}); g_free(wdir); #else g_string_append(result, dir); #endif - } else if (!starts_with_prefix(dir) || !starts_with_prefix(bindir)) { - g_string_assign(result, dir); - } else { + goto out; + } + + if (IS_ENABLED(CONFIG_RELOCATABLE) && + starts_with_prefix(dir) && starts_with_prefix(bindir)) { g_string_assign(result, exec_dir); /* Advance over common components. */ @@ -1132,7 +1204,10 @@ char *get_relocated_path(const char *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..15fdbb109d 100644 --- a/util/envlist.c +++ b/util/envlist.c @@ -3,32 +3,29 @@ #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, - const char *env, int (*)(envlist_t *, const char *)); - /* * Allocates new envlist and returns pointer to it. */ 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,84 +34,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); -} - -/* - * Parses comma separated list of set/modify environment - * variable entries and updates given enlist accordingly. - * - * For example: - * envlist_parse(el, "HOME=foo,SHELL=/bin/sh"); - * - * inserts/sets environment variables HOME and SHELL. - * - * Returns 0 on success, errno otherwise. - */ -int -envlist_parse_set(envlist_t *envlist, const char *env) -{ - return (envlist_parse(envlist, env, &envlist_setenv)); -} - -/* - * Parses comma separated list of unset environment variable - * entries and removes given variables from given envlist. - * - * Returns 0 on success, errno otherwise. - */ -int -envlist_parse_unset(envlist_t *envlist, const char *env) -{ - return (envlist_parse(envlist, env, &envlist_unsetenv)); -} - -/* - * Parses comma separated list of set, modify or unset entries - * and calls given callback for each entry. - * - * Returns 0 in case of success, errno otherwise. - */ -static int -envlist_parse(envlist_t *envlist, const char *env, - int (*callback)(envlist_t *, const char *)) -{ - char *tmpenv, *envvar; - char *envsave = NULL; - int ret = 0; - assert(callback != NULL); - - if ((envlist == NULL) || (env == NULL)) - return (EINVAL); - - tmpenv = g_strdup(env); - envsave = tmpenv; - - do { - envvar = strchr(tmpenv, ','); - if (envvar != NULL) { - *envvar = '\0'; - } - if ((*callback)(envlist, tmpenv) != 0) { - ret = errno; - break; - } - tmpenv = envvar + 1; - } while (envvar != NULL); - - g_free(envsave); - return ret; + g_free((char *)entry->ev_var); + g_free(entry); + } + g_free(envlist); } /* @@ -126,42 +57,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 +102,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 +145,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..1b17c11de1 100644 --- a/util/error-report.c +++ b/util/error-report.c @@ -172,18 +172,8 @@ static void print_loc(void) static char * real_time_iso8601(void) { -#if GLIB_CHECK_VERSION(2,62,0) g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); - /* ignore deprecation warning, since GLIB_VERSION_MAX_ALLOWED is 2.56 */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" return g_date_time_format_iso8601(dt); -#pragma GCC diagnostic pop -#else - GTimeVal tv; - g_get_current_time(&tv); - return g_time_val_to_iso8601(&tv); -#endif } /* @@ -193,6 +183,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 1683aa1105..9fb8800dde 100644 --- a/util/fdmon-epoll.c +++ b/util/fdmon-epoll.c @@ -5,6 +5,7 @@ #include "qemu/osdep.h" #include +#include "qemu/lockcnt.h" #include "qemu/rcu_queue.h" #include "aio-posix.h" @@ -64,11 +65,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) { @@ -133,11 +129,6 @@ bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) return false; } - /* Do not upgrade while external clients are disabled */ - if (qatomic_read(&ctx->external_disable_cnt)) { - return false; - } - if (npfd < EPOLL_ENABLE_THRESHOLD) { return false; } 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..a26da66ad2 100644 --- a/util/fifo8.c +++ b/util/fifo8.c @@ -16,12 +16,17 @@ #include "migration/vmstate.h" #include "qemu/fifo8.h" +void fifo8_reset(Fifo8 *fifo) +{ + fifo->num = 0; + fifo->head = 0; +} + void fifo8_create(Fifo8 *fifo, uint32_t capacity) { fifo->data = g_new(uint8_t, capacity); fifo->capacity = capacity; - fifo->head = 0; - fifo->num = 0; + fifo8_reset(fifo); } void fifo8_destroy(Fifo8 *fifo) @@ -66,23 +71,90 @@ uint8_t fifo8_pop(Fifo8 *fifo) return ret; } -const uint8_t *fifo8_pop_buf(Fifo8 *fifo, uint32_t max, uint32_t *num) +uint8_t fifo8_peek(Fifo8 *fifo) +{ + assert(fifo->num > 0); + return fifo->data[fifo->head]; +} + +static const uint8_t *fifo8_peekpop_bufptr(Fifo8 *fifo, uint32_t max, + uint32_t skip, uint32_t *numptr, + bool do_pop) { uint8_t *ret; + uint32_t num, head; assert(max > 0 && max <= fifo->num); - *num = MIN(fifo->capacity - fifo->head, max); - ret = &fifo->data[fifo->head]; - fifo->head += *num; - fifo->head %= fifo->capacity; - fifo->num -= *num; + assert(skip <= fifo->num); + head = (fifo->head + skip) % fifo->capacity; + num = MIN(fifo->capacity - head, max); + ret = &fifo->data[head]; + + if (do_pop) { + fifo->head = head + num; + fifo->head %= fifo->capacity; + fifo->num -= num; + } + if (numptr) { + *numptr = num; + } return ret; } -void fifo8_reset(Fifo8 *fifo) +const uint8_t *fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) { - fifo->num = 0; - fifo->head = 0; + return fifo8_peekpop_bufptr(fifo, max, 0, numptr, false); +} + +const uint8_t *fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr) +{ + return fifo8_peekpop_bufptr(fifo, max, 0, numptr, true); +} + +static uint32_t fifo8_peekpop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen, + bool do_pop) +{ + const uint8_t *buf; + uint32_t n1, n2 = 0; + uint32_t len; + + if (destlen == 0) { + return 0; + } + + len = destlen; + buf = fifo8_peekpop_bufptr(fifo, len, 0, &n1, do_pop); + if (dest) { + memcpy(dest, buf, n1); + } + + /* Add FIFO wraparound if needed */ + len -= n1; + len = MIN(len, fifo8_num_used(fifo)); + if (len) { + buf = fifo8_peekpop_bufptr(fifo, len, do_pop ? 0 : n1, &n2, do_pop); + if (dest) { + memcpy(&dest[n1], buf, n2); + } + } + + return n1 + n2; +} + +uint32_t fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen) +{ + return fifo8_peekpop_buf(fifo, dest, destlen, true); +} + +uint32_t fifo8_peek_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen) +{ + return fifo8_peekpop_buf(fifo, dest, destlen, false); +} + +void fifo8_drop(Fifo8 *fifo, uint32_t len) +{ + len -= fifo8_pop_buf(fifo, NULL, len); + assert(len == 0); } bool fifo8_is_empty(Fifo8 *fifo) @@ -109,7 +181,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/getauxval.c b/util/getauxval.c index b124107d61..0735cd8271 100644 --- a/util/getauxval.c +++ b/util/getauxval.c @@ -95,16 +95,20 @@ unsigned long qemu_getauxval(unsigned long type) } } + errno = ENOENT; return 0; } -#elif defined(__FreeBSD__) +#elif defined(CONFIG_ELF_AUX_INFO) #include unsigned long qemu_getauxval(unsigned long type) { unsigned long aux = 0; - elf_aux_info(type, &aux, sizeof(aux)); + int ret = elf_aux_info(type, &aux, sizeof(aux)); + if (ret != 0) { + errno = ret; + } return aux; } @@ -112,6 +116,7 @@ unsigned long qemu_getauxval(unsigned long type) unsigned long qemu_getauxval(unsigned long type) { + errno = ENOSYS; return 0; } 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 297db35fb1..d9a1dabc63 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) @@ -949,7 +949,7 @@ char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); char *data = (char *)bitmap->levels[HBITMAP_LEVELS - 1]; char *hash = NULL; - qcrypto_hash_digest(QCRYPTO_HASH_ALG_SHA256, data, size, &hash, errp); + qcrypto_hash_digest(QCRYPTO_HASH_ALGO_SHA256, data, size, &hash, errp); return hash; } diff --git a/util/hexdump.c b/util/hexdump.c index 9921114b3c..ae0d4992dc 100644 --- a/util/hexdump.c +++ b/util/hexdump.c @@ -1,5 +1,5 @@ /* - * Helper to hexdump a buffer +* Helper to hexdump a buffer * * Copyright (c) 2013 Red Hat, Inc. * Copyright (c) 2013 Gerd Hoffmann @@ -16,50 +16,84 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -void qemu_hexdump_line(char *line, unsigned int b, const void *bufptr, - unsigned int len, bool ascii) +static inline char hexdump_nibble(unsigned x) +{ + return (x < 10 ? '0' : 'a' - 10) + x; +} + +GString *qemu_hexdump_line(GString *str, const void *vbuf, size_t len, + size_t unit_len, size_t block_len) +{ + const uint8_t *buf = vbuf; + size_t u, b; + + if (str == NULL) { + /* Estimate the length of the output to avoid reallocs. */ + size_t est = len * 2; + if (unit_len) { + est += len / unit_len; + } + if (block_len) { + est += len / block_len; + } + str = g_string_sized_new(est + 1); + } + + for (u = 0, b = 0; len; u++, b++, len--, buf++) { + uint8_t c; + + if (unit_len && u == unit_len) { + g_string_append_c(str, ' '); + u = 0; + } + if (block_len && b == block_len) { + g_string_append_c(str, ' '); + b = 0; + } + + c = *buf; + g_string_append_c(str, hexdump_nibble(c / 16)); + g_string_append_c(str, hexdump_nibble(c % 16)); + } + + return str; +} + +static void asciidump_line(char *line, const void *bufptr, size_t len) { const char *buf = bufptr; - int i, c; - if (len > QEMU_HEXDUMP_LINE_BYTES) { - len = QEMU_HEXDUMP_LINE_BYTES; - } + for (size_t i = 0; i < len; i++) { + char c = buf[i]; - line += snprintf(line, 6, "%04x:", b); - for (i = 0; i < QEMU_HEXDUMP_LINE_BYTES; i++) { - if ((i % 4) == 0) { - *line++ = ' '; - } - if (i < len) { - line += sprintf(line, " %02x", (unsigned char)buf[b + i]); - } else { - line += sprintf(line, " "); - } - } - if (ascii) { - *line++ = ' '; - for (i = 0; i < len; i++) { - c = buf[b + i]; - if (c < ' ' || c > '~') { - c = '.'; - } - *line++ = c; + if (c < ' ' || c > '~') { + c = '.'; } + *line++ = c; } *line = '\0'; } +#define QEMU_HEXDUMP_LINE_BYTES 16 +#define QEMU_HEXDUMP_LINE_WIDTH \ + (QEMU_HEXDUMP_LINE_BYTES * 2 + QEMU_HEXDUMP_LINE_BYTES / 4) + void qemu_hexdump(FILE *fp, const char *prefix, const void *bufptr, size_t size) { - unsigned int b, len; - char line[QEMU_HEXDUMP_LINE_LEN]; + g_autoptr(GString) str = g_string_sized_new(QEMU_HEXDUMP_LINE_WIDTH + 1); + char ascii[QEMU_HEXDUMP_LINE_BYTES + 1]; + size_t b, len; - for (b = 0; b < size; b += QEMU_HEXDUMP_LINE_BYTES) { - len = size - b; - qemu_hexdump_line(line, b, bufptr, len, true); - fprintf(fp, "%s: %s\n", prefix, line); + for (b = 0; b < size; b += len) { + len = MIN(size - b, QEMU_HEXDUMP_LINE_BYTES); + + g_string_truncate(str, 0); + qemu_hexdump_line(str, bufptr + b, len, 1, 4); + asciidump_line(ascii, bufptr + b, len); + + fprintf(fp, "%s: %04zx: %-*s %s\n", + prefix, b, QEMU_HEXDUMP_LINE_WIDTH, str->str, ascii); } } 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 b4be580022..7777116123 100644 --- a/util/iov.c +++ b/util/iov.c @@ -3,6 +3,7 @@ * * Copyright IBM, Corp. 2007, 2008 * Copyright (C) 2010 Red Hat, Inc. + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * * Author(s): * Anthony Liguori @@ -92,7 +93,8 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) /* helper function for iov_send_recv() */ static ssize_t -do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) +do_send_recv(int sockfd, int flags, struct iovec *iov, unsigned iov_cnt, + bool do_send) { #ifdef CONFIG_POSIX ssize_t ret; @@ -102,8 +104,8 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) msg.msg_iovlen = iov_cnt; do { ret = do_send - ? sendmsg(sockfd, &msg, 0) - : recvmsg(sockfd, &msg, 0); + ? sendmsg(sockfd, &msg, flags) + : recvmsg(sockfd, &msg, flags); } while (ret < 0 && errno == EINTR); return ret; #else @@ -114,8 +116,8 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) ssize_t off = 0; while (i < iov_cnt) { ssize_t r = do_send - ? 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); + ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, flags) + : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, flags); if (r > 0) { ret += r; off += r; @@ -144,6 +146,15 @@ do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt, size_t offset, size_t bytes, bool do_send) +{ + return iov_send_recv_with_flags(sockfd, 0, _iov, iov_cnt, offset, bytes, + do_send); +} + +ssize_t iov_send_recv_with_flags(int sockfd, int sockflags, + const struct iovec *_iov, + unsigned iov_cnt, size_t offset, + size_t bytes, bool do_send) { ssize_t total = 0; ssize_t ret; @@ -192,11 +203,11 @@ ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt, assert(iov[niov].iov_len > tail); orig_len = iov[niov].iov_len; iov[niov++].iov_len = tail; - ret = do_send_recv(sockfd, iov, niov, do_send); + ret = do_send_recv(sockfd, sockflags, iov, niov, do_send); /* Undo the changes above before checking for errors */ iov[niov-1].iov_len = orig_len; } else { - ret = do_send_recv(sockfd, iov, niov, do_send); + ret = do_send_recv(sockfd, sockflags, iov, niov, do_send); } if (offset) { iov[0].iov_base -= offset; @@ -378,15 +389,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; @@ -411,75 +422,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. */ @@ -511,14 +458,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) @@ -628,7 +582,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 536789797e..06295e2755 100644 --- a/util/iova-tree.c +++ b/util/iova-tree.c @@ -115,13 +115,6 @@ const DMAMap *iova_tree_find_iova(const IOVATree *tree, const DMAMap *map) return args.result; } -const DMAMap *iova_tree_find_address(const IOVATree *tree, hwaddr iova) -{ - const DMAMap map = { .iova = iova, .size = 0 }; - - return iova_tree_find(tree, &map); -} - static inline void iova_tree_insert_internal(GTree *gtree, DMAMap *range) { /* Key and value are sharing the same range data */ @@ -148,22 +141,6 @@ int iova_tree_insert(IOVATree *tree, const DMAMap *map) return IOVA_OK; } -static gboolean iova_tree_traverse(gpointer key, gpointer value, - gpointer data) -{ - iova_tree_iterator iterator = data; - DMAMap *map = key; - - g_assert(key == value); - - return iterator(map); -} - -void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) -{ - g_tree_foreach(tree->tree, iova_tree_traverse, iterator); -} - void iova_tree_remove(IOVATree *tree, DMAMap map) { const DMAMap *overlap; diff --git a/util/lockcnt.c b/util/lockcnt.c index 5da36946b1..d07c6cc5ce 100644 --- a/util/lockcnt.c +++ b/util/lockcnt.c @@ -7,6 +7,7 @@ * Paolo Bonzini */ #include "qemu/osdep.h" +#include "qemu/lockcnt.h" #include "qemu/thread.h" #include "qemu/atomic.h" #include "trace.h" diff --git a/util/log.c b/util/log.c index c2198badf2..6219819855 100644 --- a/util/log.c +++ b/util/log.c @@ -45,7 +45,6 @@ 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; @@ -80,13 +79,15 @@ static int log_thread_id(void) static void qemu_log_thread_cleanup(Notifier *n, void *unused) { - fclose(thread_file); - thread_file = NULL; + 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; @@ -97,6 +98,9 @@ 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; @@ -123,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) { @@ -266,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 { @@ -308,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; } @@ -432,6 +466,10 @@ const QEMULogItem qemu_log_items[] = { "show micro ops after optimization" }, { CPU_LOG_TB_OP_IND, "op_ind", "show micro ops before indirect lowering" }, +#ifdef CONFIG_PLUGIN + { LOG_TB_OP_PLUGIN, "op_plugin", + "show micro ops before plugin injection" }, +#endif { CPU_LOG_INT, "int", "show interrupts/exceptions in short format" }, { CPU_LOG_EXEC, "exec", @@ -457,12 +495,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 479ec2c9ba..035c7220e5 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" #ifdef XBOX @@ -70,7 +69,7 @@ QemuMutex qemu_main_loop_lock; */ /* * 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 @@ -86,9 +85,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; @@ -249,10 +246,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); @@ -309,10 +303,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) { #ifdef XBOX @@ -375,7 +365,7 @@ static int os_host_main_loop_wait(int64_t timeout) glib_pollfds_fill(&timeout); - qemu_mutex_unlock_iothread(); + bql_unlock(); replay_mutex_unlock(); #ifdef XBOX @@ -387,7 +377,7 @@ static int os_host_main_loop_wait(int64_t timeout) #endif replay_mutex_lock(); - qemu_mutex_lock_iothread(); + bql_lock(); glib_pollfds_poll(); @@ -489,13 +479,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) { @@ -597,7 +580,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(); @@ -611,7 +594,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; @@ -701,9 +684,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); } /* @@ -738,14 +723,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/memfd.c b/util/memfd.c index 4a3c07e0be..8a2e906962 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/memfd.h" #include "qemu/host-utils.h" @@ -149,11 +150,15 @@ err: void qemu_memfd_free(void *ptr, size_t size, int fd) { if (ptr) { - munmap(ptr, size); + if (munmap(ptr, size) != 0) { + error_report("memfd munmap() failed: %s", strerror(errno)); + } } if (fd != -1) { - close(fd); + if (close(fd) != 0) { + error_report("memfd close() failed: %s", strerror(errno)); + } } } diff --git a/util/meson.build b/util/meson.build index 72ef1db2b5..9b7056e8f9 100644 --- a/util/meson.build +++ b/util/meson.build @@ -3,29 +3,35 @@ 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_LINUX', if_true: files('async-teardown.c')) -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(when: 'CONFIG_WIN32', if_true: pathcch) +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')) @@ -49,15 +55,18 @@ 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(when: 'CONFIG_WIN32', if_true: files('miniz/miniz.c')) +util_ss.add(files('interval-tree.c')) +util_ss.add(files('lockcnt.c')) +if host_os == 'windows' + util_ss.add(files('miniz/miniz.c')) +endif util_ss.add(files('fast-hash.c')) util_ss.add(files('mstring.c')) @@ -68,16 +77,17 @@ 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 or have_ga util_ss.add(files('aiocb.c', 'async.c')) util_ss.add(files('base64.c')) - util_ss.add(files('lockcnt.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('coroutine-@0@.c'.format(config_host['CONFIG_COROUTINE_BACKEND']))) + 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 @@ -88,11 +98,8 @@ if have_block 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', 'uri.c')) + util_ss.add(files('iov.c')) util_ss.add(files('nvdimm-utils.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')) @@ -101,10 +108,31 @@ if have_block 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 + util_ss.add(files('yank.c')) +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')) +elif cpu in ['riscv32', 'riscv64'] + util_ss.add(files('cpuinfo-riscv.c')) endif util_ss.add(files('sha1.c','rc4.c')) diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 5ed7d29183..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 diff --git a/util/module.c b/util/module.c index 32e263163c..3eb0f06df1 100644 --- a/util/module.c +++ b/util/module.c @@ -354,13 +354,13 @@ int module_load_qom(const char *type, Error **errp) void module_load_qom_all(void) { const QemuModinfo *modinfo; - Error *local_err = NULL; if (module_loaded_qom_all) { return; } for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + Error *local_err = NULL; if (!modinfo->objs) { continue; } 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 046c099828..7b53d2211f 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -57,9 +57,14 @@ int qemu_madvise(void *addr, size_t len, int advice) #if defined(CONFIG_MADVISE) return madvise(addr, len, advice); #elif defined(CONFIG_POSIX_MADVISE) - return posix_madvise(addr, len, advice); + int rc = posix_madvise(addr, len, advice); + if (rc) { + errno = rc; + return -1; + } + return 0; #else - errno = EINVAL; + errno = ENOSYS; return -1; #endif } @@ -244,9 +249,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; } @@ -279,6 +282,15 @@ int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive) } #endif +bool qemu_has_direct_io(void) +{ +#ifdef O_DIRECT + return true; +#else + return false; +#endif +} + static int qemu_open_cloexec_internal(const char *name, int flags, mode_t mode) { int ret; @@ -323,7 +335,6 @@ qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) /* Attempt dup of fd from fd set */ if (strstart(name, "/dev/fdset/", &fdset_id_str)) { int64_t fdset_id; - int dupfd; fdset_id = qemu_parse_fdset(fdset_id_str); if (fdset_id == -1) { @@ -332,14 +343,7 @@ qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) return -1; } - dupfd = monitor_fdset_dup_fd_add(fdset_id, flags); - if (dupfd == -1) { - error_setg_errno(errp, errno, "Could not dup FD for %s flags %x", - name, flags); - return -1; - } - - return dupfd; + return monitor_fdset_dup_fd_add(fdset_id, flags, errp); } #endif @@ -411,21 +415,8 @@ int qemu_open_old(const char *name, int flags, ...) int qemu_close(int fd) { - int64_t fdset_id; - /* Close fd that was dup'd from an fdset */ - fdset_id = monitor_fdset_dup_fd_find(fd); - if (fdset_id != -1) { - int ret; - - ret = close(fd); - if (ret == 0) { - monitor_fdset_dup_fd_remove(fd); - } - - return ret; - } - + monitor_fdset_dup_fd_remove(fd); return close(fd); } diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 59a891b6a8..11b35e48fb 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -40,9 +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 @@ -50,7 +50,6 @@ #ifdef __FreeBSD__ #include -#include #include #include #endif @@ -61,19 +60,19 @@ #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 { @@ -264,7 +263,7 @@ int qemu_socketpair(int domain, int type, int protocol, int sv[2]) return ret; } #endif - ret = socketpair(domain, type, protocol, sv);; + ret = socketpair(domain, type, protocol, sv); if (ret == 0) { qemu_set_cloexec(sv[0]); qemu_set_cloexec(sv[1]); @@ -418,19 +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 max_threads, ThreadContext *tc, + 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, max_threads), - }; + 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); @@ -438,63 +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; + 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, + thread_context_create_thread(tc, &context->threads[i].pgthread, "touch_pages", - touch_fn, &context.threads[i], + 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_create(&context->threads[i].pgthread, "touch_pages", + touch_fn, &context->threads[i], QEMU_THREAD_JOINABLE); } - addr += context.threads[i].numpages * hpagesize; + 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) @@ -503,8 +568,8 @@ static bool madv_populate_write_possible(char *area, size_t pagesize) errno != EINVAL; } -void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, - ThreadContext *tc, 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; @@ -512,6 +577,7 @@ void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, 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 @@ -540,16 +606,17 @@ void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, qemu_mutex_unlock(&sigbus_mutex); error_setg_errno(errp, errno, "qemu_prealloc_mem: failed to install signal handler"); - return; + return false; } } /* touch pages simultaneously */ - ret = touch_all_pages(area, hpagesize, numpages, max_threads, tc, + ret = touch_all_pages(area, hpagesize, numpages, max_threads, tc, async, use_madv_populate_write); if (ret) { error_setg_errno(errp, -ret, "qemu_prealloc_mem: preallocating memory failed"); + rv = false; } if (!use_madv_populate_write) { @@ -561,6 +628,7 @@ void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, } qemu_mutex_unlock(&sigbus_mutex); } + return rv; } char *qemu_get_pid_name(pid_t pid) @@ -589,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; @@ -694,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(); } @@ -747,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 @@ -818,3 +807,127 @@ int qemu_msync(void *addr, size_t length, int fd) return msync(addr, length, MS_SYNC); } + +static bool qemu_close_all_open_fd_proc(const int *skip, unsigned int nskip) +{ + struct dirent *de; + int fd, dfd; + DIR *dir; + unsigned int skip_start = 0, skip_end = nskip; + + dir = opendir("/proc/self/fd"); + if (!dir) { + /* If /proc is not mounted, there is nothing that can be done. */ + return false; + } + /* Avoid closing the directory. */ + dfd = dirfd(dir); + + for (de = readdir(dir); de; de = readdir(dir)) { + bool close_fd = true; + + if (de->d_name[0] == '.') { + continue; + } + fd = atoi(de->d_name); + if (fd == dfd) { + continue; + } + + for (unsigned int i = skip_start; i < skip_end; i++) { + if (fd < skip[i]) { + /* We are below the next skipped fd, break */ + break; + } else if (fd == skip[i]) { + close_fd = false; + /* Restrict the range as we found fds matching start/end */ + if (i == skip_start) { + skip_start++; + } else if (i == skip_end) { + skip_end--; + } + break; + } + } + + if (close_fd) { + close(fd); + } + } + closedir(dir); + + return true; +} + +static bool qemu_close_all_open_fd_close_range(const int *skip, + unsigned int nskip, + int open_max) +{ +#ifdef CONFIG_CLOSE_RANGE + int max_fd = open_max - 1; + int first = 0, last; + unsigned int cur_skip = 0; + int ret; + + do { + /* Find the start boundary of the range to close */ + while (cur_skip < nskip && first == skip[cur_skip]) { + cur_skip++; + first++; + } + + /* Find the upper boundary of the range to close */ + last = max_fd; + if (cur_skip < nskip) { + last = skip[cur_skip] - 1; + last = MIN(last, max_fd); + } + + /* With the adjustments to the range, we might be done. */ + if (first > last) { + break; + } + + ret = close_range(first, last, 0); + if (ret < 0) { + return false; + } + + first = last + 1; + } while (last < max_fd); + + return true; +#else + return false; +#endif +} + +static void qemu_close_all_open_fd_fallback(const int *skip, unsigned int nskip, + int open_max) +{ + unsigned int cur_skip = 0; + + /* Fallback */ + for (int i = 0; i < open_max; i++) { + if (cur_skip < nskip && i == skip[cur_skip]) { + cur_skip++; + continue; + } + close(i); + } +} + +/* + * Close all open file descriptors. + */ +void qemu_close_all_open_fd(const int *skip, unsigned int nskip) +{ + int open_max = sysconf(_SC_OPEN_MAX); + + assert(skip != NULL || nskip == 0); + + if (!qemu_close_all_open_fd_close_range(skip, nskip, open_max) && + !qemu_close_all_open_fd_proc(skip, nskip)) { + qemu_close_all_open_fd_fallback(skip, nskip, open_max); + } +} diff --git a/util/oslib-win32.c b/util/oslib-win32.c index e5c7ad5b59..cb45346911 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,8 +264,8 @@ int getpagesize(void) return system_info.dwPageSize; } -void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, - ThreadContext *tc, 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(); @@ -278,6 +274,14 @@ void qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, 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(); } @@ -596,3 +893,36 @@ done: g_free(wpathname); return ret; } + +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-shared-resource.c b/util/qemu-co-shared-resource.c index a66cc07e75..752eb5a1c5 100644 --- a/util/qemu-co-shared-resource.c +++ b/util/qemu-co-shared-resource.c @@ -66,12 +66,6 @@ static bool co_try_get_from_shres_locked(SharedResource *s, uint64_t n) return false; } -bool co_try_get_from_shres(SharedResource *s, uint64_t n) -{ - QEMU_LOCK_GUARD(&s->lock); - return co_try_get_from_shres_locked(s, n); -} - void coroutine_fn co_get_from_shres(SharedResource *s, uint64_t n) { assert(n <= s->total); diff --git a/util/qemu-config.c b/util/qemu-config.c index 21fb41ec71..05ba9b7a32 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -1,16 +1,14 @@ #include "qemu/osdep.h" #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" -static QemuOptsList *vm_config_groups[48]; -static QemuOptsList *drive_config_groups[5]; +QemuOptsList *vm_config_groups[48]; +QemuOptsList *drive_config_groups[5]; static QemuOptsList *find_list(QemuOptsList **lists, const char *group, Error **errp) @@ -55,228 +53,6 @@ QemuOpts *qemu_find_opts_singleton(const char *group) return opts; } -static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc) -{ - CommandLineParameterInfoList *param_list = NULL; - CommandLineParameterInfo *info; - int i; - - for (i = 0; desc[i].name != NULL; i++) { - info = g_malloc0(sizeof(*info)); - info->name = g_strdup(desc[i].name); - - switch (desc[i].type) { - case QEMU_OPT_STRING: - info->type = COMMAND_LINE_PARAMETER_TYPE_STRING; - break; - case QEMU_OPT_BOOL: - info->type = COMMAND_LINE_PARAMETER_TYPE_BOOLEAN; - break; - case QEMU_OPT_NUMBER: - info->type = COMMAND_LINE_PARAMETER_TYPE_NUMBER; - break; - case QEMU_OPT_SIZE: - info->type = COMMAND_LINE_PARAMETER_TYPE_SIZE; - 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); - } - - QAPI_LIST_PREPEND(param_list, info); - } - - return param_list; -} - -/* remove repeated entry from the info list */ -static void cleanup_infolist(CommandLineParameterInfoList *head) -{ - CommandLineParameterInfoList *pre_entry, *cur, *del_entry; - - cur = head; - while (cur->next) { - pre_entry = head; - while (pre_entry != cur->next) { - if (!strcmp(pre_entry->value->name, cur->next->value->name)) { - del_entry = cur->next; - cur->next = cur->next->next; - del_entry->next = NULL; - qapi_free_CommandLineParameterInfoList(del_entry); - break; - } - pre_entry = pre_entry->next; - } - cur = cur->next; - } -} - -/* merge the description items of two parameter infolists */ -static void connect_infolist(CommandLineParameterInfoList *head, - CommandLineParameterInfoList *new) -{ - CommandLineParameterInfoList *cur; - - cur = head; - while (cur->next) { - cur = cur->next; - } - cur->next = new; -} - -/* access all the local QemuOptsLists for drive option */ -static CommandLineParameterInfoList *get_drive_infolist(void) -{ - CommandLineParameterInfoList *head = NULL, *cur; - int i; - - for (i = 0; drive_config_groups[i] != NULL; i++) { - if (!head) { - head = query_option_descs(drive_config_groups[i]->desc); - } else { - cur = query_option_descs(drive_config_groups[i]->desc); - connect_infolist(head, cur); - } - } - cleanup_infolist(head); - - 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 */ } - } -}; - -CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option, - const char *option, - Error **errp) -{ - CommandLineOptionInfoList *conf_list = NULL; - CommandLineOptionInfo *info; - int i; - - for (i = 0; vm_config_groups[i] != NULL; i++) { - if (!has_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)) { - info->parameters = get_drive_infolist(); - } else { - info->parameters = - query_option_descs(vm_config_groups[i]->desc); - } - QAPI_LIST_PREPEND(conf_list, info); - } - } - - if (!has_option || !strcmp(option, "machine")) { - info = g_malloc0(sizeof(*info)); - info->option = g_strdup("machine"); - info->parameters = query_option_descs(machine_opts.desc); - QAPI_LIST_PREPEND(conf_list, info); - } - - if (conf_list == NULL) { - error_setg(errp, "invalid option name: %s", option); - } - - return conf_list; -} - QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) { return find_list(vm_config_groups, group, errp); @@ -318,9 +94,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 +124,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 +198,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 +212,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 +243,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 +253,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 45c6b57374..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" @@ -202,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) { @@ -304,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. 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 356b746f0b..64d6264fc7 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 = NULL; + + 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); @@ -220,10 +351,51 @@ AioContext *qemu_coroutine_get_aio_context(Coroutine *co) 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-option.c b/util/qemu-option.c index eedd08929b..201f7a87f3 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -498,7 +498,7 @@ static bool opt_validate(QemuOpt *opt, Error **errp) desc = find_desc_by_name(list->desc, opt->name); if (!desc && !opts_accepts_any(list)) { - error_setg(errp, QERR_INVALID_PARAMETER, opt->name); + error_setg(errp, "Invalid parameter '%s'", opt->name); return false; } @@ -531,7 +531,7 @@ bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, desc = find_desc_by_name(list->desc, name); if (!desc && !opts_accepts_any(list)) { - error_setg(errp, QERR_INVALID_PARAMETER, name); + error_setg(errp, "Invalid parameter '%s'", name); return false; } @@ -554,7 +554,7 @@ bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, desc = find_desc_by_name(list->desc, name); if (!desc && !opts_accepts_any(list)) { - error_setg(errp, QERR_INVALID_PARAMETER, name); + error_setg(errp, "Invalid parameter '%s'", name); return false; } @@ -612,7 +612,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, if (list->merge_lists) { if (id) { - error_setg(errp, QERR_INVALID_PARAMETER, "id"); + error_setg(errp, "Invalid parameter 'id'"); return NULL; } opts = qemu_opts_find(list, NULL); @@ -1103,7 +1103,7 @@ bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) QTAILQ_FOREACH(opt, &opts->head, next) { opt->desc = find_desc_by_name(desc, opt->name); if (!opt->desc) { - error_setg(errp, QERR_INVALID_PARAMETER, opt->name); + error_setg(errp, "Invalid parameter '%s'", opt->name); return false; } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index d185245023..77477c1cd5 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; @@ -369,7 +367,6 @@ static int inet_connect_addr(const InetSocketAddress *saddr, addr->ai_family); return -1; } - socket_set_fast_reuse(sock); /* connect to peer */ do { @@ -382,7 +379,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 +389,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 +400,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; } @@ -487,7 +482,7 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) if (ret < 0) { error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); - closesocket(sock); + close(sock); return -1; } } @@ -499,20 +494,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 +579,7 @@ static int inet_dgram_saddr(InetSocketAddress *sraddr, err: if (sock != -1) { - closesocket(sock); + close(sock); } if (local) { freeaddrinfo(local); @@ -713,44 +706,24 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) } -/** - * Create a blocking socket and connect it to an address. - * - * @str: address string - * @errp: set in case of an error - * - * Returns -1 in case of error, file descriptor on success - **/ -int inet_connect(const char *str, Error **errp) -{ - int sock = -1; - InetSocketAddress *addr = g_new(InetSocketAddress, 1); - - if (!inet_parse(addr, str, errp)) { - sock = inet_connect_saddr(addr, errp); - } - qapi_free_InetSocketAddress(addr); - return sock; -} - #ifdef CONFIG_AF_VSOCK 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 +756,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 +793,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; @@ -935,7 +908,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 @@ -984,7 +957,7 @@ static int unix_listen_saddr(UnixSocketAddress *saddr, err: g_free(pathbuf); - closesocket(sock); + close(sock); return -1; } @@ -1047,7 +1020,7 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) return sock; err: - closesocket(sock); + close(sock); return -1; } @@ -1244,7 +1217,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; @@ -1427,21 +1400,6 @@ SocketAddress *socket_local_address(int fd, Error **errp) } -SocketAddress *socket_remote_address(int fd, Error **errp) -{ - struct sockaddr_storage ss; - socklen_t sslen = sizeof(ss); - - if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) { - error_setg_errno(errp, errno, "%s", - "Unable to query remote socket address"); - return NULL; - } - - return socket_sockaddr_to_address(&ss, sslen, errp); -} - - SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) { SocketAddress *addr; @@ -1470,7 +1428,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 bae938c670..b2e26e2120 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -18,6 +18,10 @@ #include "qemu/tsan.h" #include "qemu/bitmap.h" +#ifdef CONFIG_PTHREAD_SET_NAME_NP +#include +#endif + static bool name_threads; void qemu_thread_naming(bool enable) @@ -25,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"); @@ -223,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) { @@ -379,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); } @@ -394,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) @@ -413,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); } } @@ -480,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); diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 69db254ac7..a7fe3cc345 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -272,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); } @@ -286,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) @@ -304,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); } } diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 5eef4cbff6..f9733d950a 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -182,7 +182,7 @@ bool qemu_clock_has_timers(QEMUClockType type) bool timerlist_expired(QEMUTimerList *timer_list) { - int64_t expire_time; + int64_t expire_time = 0; if (!qatomic_read(&timer_list->active_timers)) { return false; @@ -212,7 +212,7 @@ bool qemu_clock_expired(QEMUClockType type) int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) { int64_t delta; - int64_t expire_time; + int64_t expire_time = 0; if (!qatomic_read(&timer_list->active_timers)) { return -1; @@ -286,16 +286,6 @@ int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) return deadline; } -QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list) -{ - return timer_list->clock->type; -} - -QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type) -{ - return main_loop_tlg.tl[type]; -} - void timerlist_notify(QEMUTimerList *timer_list) { if (timer_list->notify_cb) { @@ -477,7 +467,7 @@ void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time) { QEMUTimerList *timer_list = ts->timer_list; - bool rearm; + bool rearm = false; WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { if (ts->expire_time == -1 || ts->expire_time > expire_time) { @@ -661,6 +651,11 @@ int64_t qemu_clock_get_ns(QEMUClockType type) } } +static void qemu_virtual_clock_set_ns(int64_t time) +{ + return cpus_set_virtual_clock(time); +} + void init_clocks(QEMUTimerListNotifyCB *notify_cb) { QEMUClockType type; @@ -691,3 +686,34 @@ bool qemu_clock_run_all_timers(void) return progress; } + +int64_t qemu_clock_advance_virtual_time(int64_t dest) +{ + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + AioContext *aio_context; + int64_t deadline; + + aio_context = qemu_get_aio_context(); + + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + /* + * A deadline of < 0 indicates this timer is not enabled, so we + * won't get far trying to run it forward. + */ + while (deadline >= 0 && clock < dest) { + int64_t warp = qemu_soonest_timeout(dest - clock, deadline); + + qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); + + qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); + timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); + clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, + QEMU_TIMER_ATTR_ALL); + } + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + + return clock; +} 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..0f19674f52 100644 --- a/util/readline.c +++ b/util/readline.c @@ -271,6 +271,14 @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline) rs->hist_entry = -1; } +static void readline_kill_line(ReadLineState *rs) +{ + while (rs->cmd_buf_index > 0) { + readline_backward_char(rs); + readline_delete_char(rs); + } +} + /* completion support */ void readline_add_completion(ReadLineState *rs, const char *str) @@ -286,6 +294,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; @@ -397,7 +413,7 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 12: readline_clear_screen(rs); break; - case 10: + case 10: /* fallthrough */ case 13: rs->cmd_buf[rs->cmd_buf_size] = '\0'; if (!rs->read_password) { @@ -410,6 +426,18 @@ void readline_handle_byte(ReadLineState *rs, int ch) rs->last_cmd_buf_size = 0; rs->readline_func(rs->opaque, rs->cmd_buf, rs->readline_opaque); break; + case 14: + /* ^N Next line in history */ + readline_down_char(rs); + break; + case 16: + /* ^P Prev line in history */ + readline_up_char(rs); + break; + case 21: + /* ^U Kill backward from point to the beginning of the line. */ + readline_kill_line(rs); + break; case 23: /* ^W */ readline_backword(rs); @@ -417,7 +445,7 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 27: rs->esc_state = IS_ESC; break; - case 127: + case 127: /* fallthrough */ case 8: readline_backspace(rs); break; @@ -444,11 +472,11 @@ void readline_handle_byte(ReadLineState *rs, int ch) break; case IS_CSI: switch (ch) { - case 'A': + case 'A': /* fallthrough */ case 'F': readline_up_char(rs); break; - case 'B': + case 'B': /* fallthrough */ case 'E': readline_down_char(rs); break; @@ -472,12 +500,15 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 4: readline_eol(rs); break; + default: + break; } break; default: break; } rs->esc_state = IS_NORM; + /* fallthrough */ the_end: break; case IS_SS3: @@ -488,9 +519,13 @@ void readline_handle_byte(ReadLineState *rs, int ch) case 'H': readline_bol(rs); break; + default: + break; } rs->esc_state = IS_NORM; break; + default: + break; } readline_update(rs); } 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 index 4138245332..2bc7883b9e 100644 --- a/util/thread-context.c +++ b/util/thread-context.c @@ -90,16 +90,13 @@ static void thread_context_set_cpu_affinity(Object *obj, Visitor *v, uint16List *l, *host_cpus = NULL; unsigned long *bitmap = NULL; int nbits = 0, ret; - Error *err = NULL; if (tc->init_cpu_bitmap) { error_setg(errp, "Mixing CPU and node affinity not supported"); return; } - visit_type_uint16List(v, name, &host_cpus, &err); - if (err) { - error_propagate(errp, err); + if (!visit_type_uint16List(v, name, &host_cpus, errp)) { return; } @@ -178,7 +175,6 @@ static void thread_context_set_node_affinity(Object *obj, Visitor *v, uint16List *l, *host_nodes = NULL; unsigned long *bitmap = NULL; struct bitmask *tmp_cpus; - Error *err = NULL; int ret, i; if (tc->init_cpu_bitmap) { @@ -186,9 +182,7 @@ static void thread_context_set_node_affinity(Object *obj, Visitor *v, return; } - visit_type_uint16List(v, name, &host_nodes, &err); - if (err) { - error_propagate(errp, err); + if (!visit_type_uint16List(v, name, &host_nodes, errp)) { return; } 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/timed-average.c b/util/timed-average.c index 2b49d532ce..5b5c22afd8 100644 --- a/util/timed-average.c +++ b/util/timed-average.c @@ -8,10 +8,12 @@ * Benoît Canet * Alberto Garcia * + * SPDX-License-Identifier: GPL-2.0-or-later + * * 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) version 3 or any later version. + * (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 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 deleted file mode 100644 index ff72c6005f..0000000000 --- a/util/uri.c +++ /dev/null @@ -1,2314 +0,0 @@ -/** - * uri.c: set of generic URI related routines - * - * Reference: RFCs 3986, 2732 and 2373 - * - * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. - * - * 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 - * DANIEL VEILLARD 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. - * - * Except as contained in this notice, the name of Daniel Veillard shall not - * be used in advertising or otherwise to promote the sale, use or other - * dealings in this Software without prior written authorization from him. - * - * daniel@veillard.com - * - ** - * - * Copyright (C) 2007, 2009-2010 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Authors: - * Richard W.M. Jones - * - */ - -#include "qemu/osdep.h" -#include "qemu/cutils.h" - -#include "qemu/uri.h" - -static void uri_clean(URI *uri); - -/* - * Old rule from 2396 used in legacy handling code - * alpha = lowalpha | upalpha - */ -#define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x)) - -/* - * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | - * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | - * "u" | "v" | "w" | "x" | "y" | "z" - */ - -#define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z')) - -/* - * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | - * "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | - * "U" | "V" | "W" | "X" | "Y" | "Z" - */ -#define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z')) - -#ifdef IS_DIGIT -#undef IS_DIGIT -#endif -/* - * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" - */ -#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9')) - -/* - * alphanum = alpha | digit - */ - -#define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) - -/* - * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" - */ - -#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ - ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ - ((x) == '(') || ((x) == ')')) - -/* - * unwise = "{" | "}" | "|" | "\" | "^" | "`" - */ - -#define IS_UNWISE(p) \ - (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ - ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ - ((*(p) == ']')) || ((*(p) == '`'))) -/* - * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | - * "[" | "]" - */ - -#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ - ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ - ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ - ((x) == ']')) - -/* - * unreserved = alphanum | mark - */ - -#define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) - -/* - * Skip to next pointer char, handle escaped sequences - */ - -#define NEXT(p) ((*p == '%') ? p += 3 : p++) - -/* - * Productions from the spec. - * - * authority = server | reg_name - * reg_name = 1*( unreserved | escaped | "$" | "," | - * ";" | ":" | "@" | "&" | "=" | "+" ) - * - * path = [ abs_path | opaque_part ] - */ - -/************************************************************************ - * * - * RFC 3986 parser * - * * - ************************************************************************/ - -#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9')) -#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \ - ((*(p) >= 'A') && (*(p) <= 'Z'))) -#define ISA_HEXDIG(p) \ - (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \ - ((*(p) >= 'A') && (*(p) <= 'F'))) - -/* - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -#define ISA_SUB_DELIM(p) \ - (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \ - ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \ - ((*(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 / "-" / "." / "_" / "~" - */ -#define ISA_UNRESERVED(p) \ - ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ - ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) - -/* - * pct-encoded = "%" HEXDIG HEXDIG - */ -#define ISA_PCT_ENCODED(p) \ - ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2))) - -/* - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - */ -#define ISA_PCHAR(p) \ - (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ - ((*(p) == ':')) || ((*(p) == '@'))) - -/** - * rfc3986_parse_scheme: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI scheme - * - * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_scheme(URI *uri, const char **str) -{ - const char *cur; - - if (str == NULL) { - return -1; - } - - cur = *str; - if (!ISA_ALPHA(cur)) { - return 2; - } - cur++; - while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') || - (*cur == '.')) { - cur++; - } - if (uri != NULL) { - g_free(uri->scheme); - uri->scheme = g_strndup(*str, cur - *str); - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_fragment: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse the query part of an URI - * - * fragment = *( pchar / "/" / "?" ) - * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']' - * in the fragment identifier but this is used very broadly for - * xpointer scheme selection, so we are allowing it here to not break - * for example all the DocBook processing chains. - * - * Returns 0 or the error code - */ -static int rfc3986_parse_fragment(URI *uri, const char **str) -{ - const char *cur; - - if (str == NULL) { - return -1; - } - - cur = *str; - - while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || - (*cur == '[') || (*cur == ']') || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { - NEXT(cur); - } - if (uri != NULL) { - g_free(uri->fragment); - if (uri->cleanup & 2) { - uri->fragment = g_strndup(*str, cur - *str); - } else { - uri->fragment = uri_string_unescape(*str, cur - *str, NULL); - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_query: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse the query part of an URI - * - * query = *uric - * - * Returns 0 or the error code - */ -static int rfc3986_parse_query(URI *uri, const char **str) -{ - const char *cur; - - if (str == NULL) { - return -1; - } - - cur = *str; - - while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { - NEXT(cur); - } - if (uri != NULL) { - g_free(uri->query); - uri->query = g_strndup(*str, cur - *str); - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_port: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse a port part and fills in the appropriate fields - * of the @uri structure - * - * port = *DIGIT - * - * Returns 0 or the error code - */ -static int rfc3986_parse_port(URI *uri, const char **str) -{ - const char *cur = *str; - int port = 0; - - if (ISA_DIGIT(cur)) { - while (ISA_DIGIT(cur)) { - port = port * 10 + (*cur - '0'); - if (port > 65535) { - return 1; - } - cur++; - } - if (uri) { - uri->port = port; - } - *str = cur; - return 0; - } - return 1; -} - -/** - * rfc3986_parse_user_info: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse a user information part and fill in the appropriate fields - * of the @uri structure - * - * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_user_info(URI *uri, const char **str) -{ - const char *cur; - - cur = *str; - while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur) || - (*cur == ':')) { - NEXT(cur); - } - if (*cur == '@') { - if (uri != NULL) { - g_free(uri->user); - if (uri->cleanup & 2) { - uri->user = g_strndup(*str, cur - *str); - } else { - uri->user = uri_string_unescape(*str, cur - *str, NULL); - } - } - *str = cur; - return 0; - } - return 1; -} - -/** - * rfc3986_parse_dec_octet: - * @str: the string to analyze - * - * dec-octet = DIGIT ; 0-9 - * / %x31-39 DIGIT ; 10-99 - * / "1" 2DIGIT ; 100-199 - * / "2" %x30-34 DIGIT ; 200-249 - * / "25" %x30-35 ; 250-255 - * - * Skip a dec-octet. - * - * Returns 0 if found and skipped, 1 otherwise - */ -static int rfc3986_parse_dec_octet(const char **str) -{ - const char *cur = *str; - - if (!(ISA_DIGIT(cur))) { - return 1; - } - if (!ISA_DIGIT(cur + 1)) { - cur++; - } else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur + 2))) { - cur += 2; - } else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) { - cur += 3; - } else if ((*cur == '2') && (*(cur + 1) >= '0') && (*(cur + 1) <= '4') && - (ISA_DIGIT(cur + 2))) { - cur += 3; - } else if ((*cur == '2') && (*(cur + 1) == '5') && (*(cur + 2) >= '0') && - (*(cur + 1) <= '5')) { - cur += 3; - } else { - return 1; - } - *str = cur; - return 0; -} -/** - * rfc3986_parse_host: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an host part and fills in the appropriate fields - * of the @uri structure - * - * host = IP-literal / IPv4address / reg-name - * IP-literal = "[" ( IPv6address / IPvFuture ) "]" - * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet - * reg-name = *( unreserved / pct-encoded / sub-delims ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_host(URI *uri, const char **str) -{ - const char *cur = *str; - const char *host; - - host = cur; - /* - * IPv6 and future addressing scheme are enclosed between brackets - */ - if (*cur == '[') { - cur++; - while ((*cur != ']') && (*cur != 0)) { - cur++; - } - if (*cur != ']') { - return 1; - } - cur++; - goto found; - } - /* - * try to parse an IPv4 - */ - if (ISA_DIGIT(cur)) { - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - if (*cur != '.') { - goto not_ipv4; - } - cur++; - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - if (*cur != '.') { - goto not_ipv4; - } - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - if (*cur != '.') { - goto not_ipv4; - } - if (rfc3986_parse_dec_octet(&cur) != 0) { - goto not_ipv4; - } - goto found; - not_ipv4: - cur = *str; - } - /* - * then this should be a hostname which can be empty - */ - while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) { - NEXT(cur); - } -found: - if (uri != NULL) { - g_free(uri->authority); - uri->authority = NULL; - g_free(uri->server); - if (cur != host) { - if (uri->cleanup & 2) { - uri->server = g_strndup(host, cur - host); - } else { - uri->server = uri_string_unescape(host, cur - host, NULL); - } - } else { - uri->server = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_authority: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an authority part and fills in the appropriate fields - * of the @uri structure - * - * authority = [ userinfo "@" ] host [ ":" port ] - * - * Returns 0 or the error code - */ -static int rfc3986_parse_authority(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - /* - * try to parse a userinfo and check for the trailing @ - */ - ret = rfc3986_parse_user_info(uri, &cur); - if ((ret != 0) || (*cur != '@')) { - cur = *str; - } else { - cur++; - } - ret = rfc3986_parse_host(uri, &cur); - if (ret != 0) { - return ret; - } - if (*cur == ':') { - cur++; - ret = rfc3986_parse_port(uri, &cur); - if (ret != 0) { - return ret; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_segment: - * @str: the string to analyze - * @forbid: an optional forbidden character - * @empty: allow an empty segment - * - * Parse a segment and fills in the appropriate fields - * of the @uri structure - * - * segment = *pchar - * segment-nz = 1*pchar - * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) - * ; non-zero-length segment without any colon ":" - * - * Returns 0 or the error code - */ -static int rfc3986_parse_segment(const char **str, char forbid, int empty) -{ - const char *cur; - - cur = *str; - if (!ISA_PCHAR(cur)) { - if (empty) { - return 0; - } - return 1; - } - while (ISA_PCHAR(cur) && (*cur != forbid)) { - NEXT(cur); - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_ab_empty: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path absolute or empty and fills in the appropriate fields - * of the @uri structure - * - * path-abempty = *( "/" segment ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_ab_empty(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - if (uri != NULL) { - g_free(uri->path); - if (*str != cur) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_absolute: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path absolute and fills in the appropriate fields - * of the @uri structure - * - * path-absolute = "/" [ segment-nz *( "/" segment ) ] - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_absolute(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - if (*cur != '/') { - return 1; - } - cur++; - ret = rfc3986_parse_segment(&cur, 0, 0); - if (ret == 0) { - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - } - if (uri != NULL) { - g_free(uri->path); - if (cur != *str) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_rootless: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path without root and fills in the appropriate fields - * of the @uri structure - * - * path-rootless = segment-nz *( "/" segment ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_rootless(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - ret = rfc3986_parse_segment(&cur, 0, 0); - if (ret != 0) { - return ret; - } - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - if (uri != NULL) { - g_free(uri->path); - if (cur != *str) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_path_no_scheme: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an path which is not a scheme and fills in the appropriate fields - * of the @uri structure - * - * path-noscheme = segment-nz-nc *( "/" segment ) - * - * Returns 0 or the error code - */ -static int rfc3986_parse_path_no_scheme(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - ret = rfc3986_parse_segment(&cur, ':', 0); - if (ret != 0) { - return ret; - } - while (*cur == '/') { - cur++; - ret = rfc3986_parse_segment(&cur, 0, 1); - if (ret != 0) { - return ret; - } - } - if (uri != NULL) { - g_free(uri->path); - if (cur != *str) { - if (uri->cleanup & 2) { - uri->path = g_strndup(*str, cur - *str); - } else { - uri->path = uri_string_unescape(*str, cur - *str, NULL); - } - } else { - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_hier_part: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an hierarchical part and fills in the appropriate fields - * of the @uri structure - * - * hier-part = "//" authority path-abempty - * / path-absolute - * / path-rootless - * / path-empty - * - * Returns 0 or the error code - */ -static int rfc3986_parse_hier_part(URI *uri, const char **str) -{ - const char *cur; - int ret; - - cur = *str; - - if ((*cur == '/') && (*(cur + 1) == '/')) { - cur += 2; - ret = rfc3986_parse_authority(uri, &cur); - if (ret != 0) { - return ret; - } - ret = rfc3986_parse_path_ab_empty(uri, &cur); - if (ret != 0) { - return ret; - } - *str = cur; - return 0; - } else if (*cur == '/') { - ret = rfc3986_parse_path_absolute(uri, &cur); - if (ret != 0) { - return ret; - } - } else if (ISA_PCHAR(cur)) { - ret = rfc3986_parse_path_rootless(uri, &cur); - if (ret != 0) { - return ret; - } - } else { - /* path-empty is effectively empty */ - if (uri != NULL) { - g_free(uri->path); - uri->path = NULL; - } - } - *str = cur; - return 0; -} - -/** - * rfc3986_parse_relative_ref: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI string and fills in the appropriate fields - * of the @uri structure - * - * relative-ref = relative-part [ "?" query ] [ "#" fragment ] - * relative-part = "//" authority path-abempty - * / path-absolute - * / path-noscheme - * / path-empty - * - * Returns 0 or the error code - */ -static int rfc3986_parse_relative_ref(URI *uri, const char *str) -{ - int ret; - - if ((*str == '/') && (*(str + 1) == '/')) { - str += 2; - ret = rfc3986_parse_authority(uri, &str); - if (ret != 0) { - return ret; - } - ret = rfc3986_parse_path_ab_empty(uri, &str); - if (ret != 0) { - return ret; - } - } else if (*str == '/') { - ret = rfc3986_parse_path_absolute(uri, &str); - if (ret != 0) { - return ret; - } - } else if (ISA_PCHAR(str)) { - ret = rfc3986_parse_path_no_scheme(uri, &str); - if (ret != 0) { - return ret; - } - } else { - /* path-empty is effectively empty */ - if (uri != NULL) { - g_free(uri->path); - uri->path = NULL; - } - } - - if (*str == '?') { - str++; - ret = rfc3986_parse_query(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str == '#') { - str++; - ret = rfc3986_parse_fragment(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str != 0) { - uri_clean(uri); - return 1; - } - return 0; -} - -/** - * rfc3986_parse: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI string and fills in the appropriate fields - * of the @uri structure - * - * scheme ":" hier-part [ "?" query ] [ "#" fragment ] - * - * Returns 0 or the error code - */ -static int rfc3986_parse(URI *uri, const char *str) -{ - int ret; - - ret = rfc3986_parse_scheme(uri, &str); - if (ret != 0) { - return ret; - } - if (*str != ':') { - return 1; - } - str++; - ret = rfc3986_parse_hier_part(uri, &str); - if (ret != 0) { - return ret; - } - if (*str == '?') { - str++; - ret = rfc3986_parse_query(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str == '#') { - str++; - ret = rfc3986_parse_fragment(uri, &str); - if (ret != 0) { - return ret; - } - } - if (*str != 0) { - uri_clean(uri); - return 1; - } - return 0; -} - -/** - * rfc3986_parse_uri_reference: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI reference string and fills in the appropriate fields - * of the @uri structure - * - * URI-reference = URI / relative-ref - * - * Returns 0 or the error code - */ -static int rfc3986_parse_uri_reference(URI *uri, const char *str) -{ - int ret; - - if (str == NULL) { - return -1; - } - uri_clean(uri); - - /* - * Try first to parse absolute refs, then fallback to relative if - * it fails. - */ - ret = rfc3986_parse(uri, str); - if (ret != 0) { - uri_clean(uri); - ret = rfc3986_parse_relative_ref(uri, str); - if (ret != 0) { - uri_clean(uri); - return ret; - } - } - return 0; -} - -/** - * uri_parse: - * @str: the URI string to analyze - * - * Parse an URI based on RFC 3986 - * - * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] - * - * Returns a newly built URI or NULL in case of error - */ -URI *uri_parse(const char *str) -{ - URI *uri; - int ret; - - if (str == NULL) { - return NULL; - } - uri = uri_new(); - ret = rfc3986_parse_uri_reference(uri, str); - if (ret) { - uri_free(uri); - return NULL; - } - return uri; -} - -/** - * uri_parse_into: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI reference string based on RFC 3986 and fills in the - * appropriate fields of the @uri structure - * - * URI-reference = URI / relative-ref - * - * Returns 0 or the error code - */ -int uri_parse_into(URI *uri, const char *str) -{ - return rfc3986_parse_uri_reference(uri, str); -} - -/** - * uri_parse_raw: - * @str: the URI string to analyze - * @raw: if 1 unescaping of URI pieces are disabled - * - * Parse an URI but allows to keep intact the original fragments. - * - * URI-reference = URI / relative-ref - * - * Returns a newly built URI or NULL in case of error - */ -URI *uri_parse_raw(const char *str, int raw) -{ - URI *uri; - int ret; - - if (str == NULL) { - return NULL; - } - uri = uri_new(); - if (raw) { - uri->cleanup |= 2; - } - ret = uri_parse_into(uri, str); - if (ret) { - uri_free(uri); - return NULL; - } - return uri; -} - -/************************************************************************ - * * - * Generic URI structure functions * - * * - ************************************************************************/ - -/** - * uri_new: - * - * Simply creates an empty URI - * - * Returns the new structure or NULL in case of error - */ -URI *uri_new(void) -{ - return g_new0(URI, 1); -} - -/** - * realloc2n: - * - * Function to handle properly a reallocation when saving an URI - * Also imposes some limit on the length of an URI string output - */ -static char *realloc2n(char *ret, int *max) -{ - char *temp; - int tmp; - - tmp = *max * 2; - temp = g_realloc(ret, (tmp + 1)); - *max = tmp; - return temp; -} - -/** - * uri_to_string: - * @uri: pointer to an URI - * - * Save the URI as an escaped string - * - * Returns a new string (to be deallocated by caller) - */ -char *uri_to_string(URI *uri) -{ - char *ret = NULL; - char *temp; - const char *p; - int len; - int max; - - if (uri == NULL) { - return NULL; - } - - max = 80; - ret = g_malloc(max + 1); - len = 0; - - if (uri->scheme != NULL) { - p = uri->scheme; - while (*p != 0) { - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - } - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = ':'; - } - if (uri->opaque != NULL) { - p = uri->opaque; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } else { - if (uri->server != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '/'; - ret[len++] = '/'; - if (uri->user != NULL) { - p = uri->user; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || ((*(p) == ';')) || - ((*(p) == ':')) || ((*(p) == '&')) || ((*(p) == '=')) || - ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ','))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '@'; - } - p = uri->server; - while (*p != 0) { - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - } - if (uri->port > 0) { - if (len + 10 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - len += snprintf(&ret[len], max - len, ":%d", uri->port); - } - } else if (uri->authority != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '/'; - ret[len++] = '/'; - p = uri->authority; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || ((*(p) == '$')) || - ((*(p) == ',')) || ((*(p) == ';')) || ((*(p) == ':')) || - ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) || - ((*(p) == '+'))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } else if (uri->scheme != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '/'; - ret[len++] = '/'; - } - if (uri->path != NULL) { - p = uri->path; - /* - * the colon in file:///d: should not be escaped or - * Windows accesses fail later. - */ - if ((uri->scheme != NULL) && (p[0] == '/') && - (((p[1] >= 'a') && (p[1] <= 'z')) || - ((p[1] >= 'A') && (p[1] <= 'Z'))) && - (p[2] == ':') && (!strcmp(uri->scheme, "file"))) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - ret[len++] = *p++; - ret[len++] = *p++; - } - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || - ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || - ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || - ((*(p) == ','))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } - if (uri->query != NULL) { - if (len + 1 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '?'; - p = uri->query; - while (*p != 0) { - if (len + 1 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = *p++; - } - } - } - if (uri->fragment != NULL) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len++] = '#'; - p = uri->fragment; - while (*p != 0) { - if (len + 3 >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) { - ret[len++] = *p++; - } else { - int val = *(unsigned char *)p++; - int hi = val / 0x10, lo = val % 0x10; - ret[len++] = '%'; - ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); - ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); - } - } - } - if (len >= max) { - temp = realloc2n(ret, &max); - ret = temp; - } - ret[len] = 0; - return ret; -} - -/** - * uri_clean: - * @uri: pointer to an URI - * - * Make sure the URI struct is free of content - */ -static void uri_clean(URI *uri) -{ - if (uri == NULL) { - return; - } - - g_free(uri->scheme); - uri->scheme = NULL; - g_free(uri->server); - uri->server = NULL; - g_free(uri->user); - uri->user = NULL; - g_free(uri->path); - uri->path = NULL; - g_free(uri->fragment); - uri->fragment = NULL; - g_free(uri->opaque); - uri->opaque = NULL; - g_free(uri->authority); - uri->authority = NULL; - g_free(uri->query); - uri->query = NULL; -} - -/** - * uri_free: - * @uri: pointer to an URI, NULL is ignored - * - * Free up the URI struct - */ -void uri_free(URI *uri) -{ - uri_clean(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. - */ - -struct QueryParams *query_params_new(int init_alloc) -{ - struct QueryParams *ps; - - if (init_alloc <= 0) { - init_alloc = 1; - } - - ps = g_new(QueryParams, 1); - ps->n = 0; - ps->alloc = init_alloc; - ps->p = g_new(QueryParam, ps->alloc); - - return ps; -} - -/* Ensure there is space to store at least one more parameter - * at the end of the set. - */ -static int query_params_append(struct QueryParams *ps, const char *name, - const char *value) -{ - if (ps->n >= ps->alloc) { - ps->p = g_renew(QueryParam, ps->p, ps->alloc * 2); - ps->alloc *= 2; - } - - ps->p[ps->n].name = g_strdup(name); - ps->p[ps->n].value = g_strdup(value); - ps->p[ps->n].ignore = 0; - ps->n++; - - return 0; -} - -void query_params_free(struct QueryParams *ps) -{ - int i; - - for (i = 0; i < ps->n; ++i) { - g_free(ps->p[i].name); - g_free(ps->p[i].value); - } - g_free(ps->p); - g_free(ps); -} - -struct QueryParams *query_params_parse(const char *query) -{ - struct QueryParams *ps; - const char *end, *eq; - - ps = query_params_new(0); - if (!query || query[0] == '\0') { - return ps; - } - - while (*query) { - char *name = NULL, *value = NULL; - - /* Find the next separator, or end of the string. */ - end = strchr(query, '&'); - if (!end) { - end = qemu_strchrnul(query, ';'); - } - - /* Find the first '=' character between here and end. */ - eq = strchr(query, '='); - if (eq && eq >= end) { - eq = NULL; - } - - /* Empty section (eg. "&&"). */ - if (end == query) { - goto next; - } - - /* If there is no '=' character, then we have just "name" - * and consistent with CGI.pm we assume value is "". - */ - else if (!eq) { - name = uri_string_unescape(query, end - query, 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); - value = g_new0(char, 1); - } - /* If the '=' character is at the beginning then we have - * "=value" and consistent with CGI.pm we _ignore_ this. - */ - else if (query == eq) { - goto next; - } - - /* Otherwise it's "name=value". */ - else { - name = uri_string_unescape(query, eq - query, NULL); - value = uri_string_unescape(eq + 1, end - (eq + 1), NULL); - } - - /* Append to the parameter set. */ - query_params_append(ps, name, value); - g_free(name); - g_free(value); - - next: - query = end; - if (*query) { - query++; /* skip '&' separator */ - } - } - - return ps; -} diff --git a/util/userfaultfd.c b/util/userfaultfd.c index f1cd6af2b1..2396104f23 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; @@ -200,7 +240,7 @@ int uffd_change_protection(int uffd_fd, void *addr, uint64_t length, * Copy range of source pages to the destination to resolve * missing page fault somewhere in the destination range. * - * Returns 0 on success, negative value in case of an error + * Returns 0 on success, -errno in case of an error * * @uffd_fd: UFFD file descriptor * @dst_addr: destination base address @@ -219,10 +259,11 @@ int uffd_copy_page(int uffd_fd, void *dst_addr, void *src_addr, uffd_copy.mode = dont_wake ? UFFDIO_COPY_MODE_DONTWAKE : 0; if (ioctl(uffd_fd, UFFDIO_COPY, &uffd_copy)) { + int e = errno; error_report("uffd_copy_page() failed: dst_addr=%p src_addr=%p length=%" PRIu64 " mode=%" PRIx64 " errno=%i", dst_addr, src_addr, - length, (uint64_t) uffd_copy.mode, errno); - return -1; + length, (uint64_t) uffd_copy.mode, e); + return -e; } return 0; @@ -233,7 +274,7 @@ int uffd_copy_page(int uffd_fd, void *dst_addr, void *src_addr, * * Fill range pages with zeroes to resolve missing page fault within the range. * - * Returns 0 on success, negative value in case of an error + * Returns 0 on success, -errno in case of an error * * @uffd_fd: UFFD file descriptor * @addr: base address @@ -249,10 +290,11 @@ int uffd_zero_page(int uffd_fd, void *addr, uint64_t length, bool dont_wake) uffd_zeropage.mode = dont_wake ? UFFDIO_ZEROPAGE_MODE_DONTWAKE : 0; if (ioctl(uffd_fd, UFFDIO_ZEROPAGE, &uffd_zeropage)) { + int e = errno; error_report("uffd_zero_page() failed: addr=%p length=%" PRIu64 " mode=%" PRIx64 " errno=%i", addr, length, - (uint64_t) uffd_zeropage.mode, errno); - return -1; + (uint64_t) uffd_zeropage.mode, e); + return -e; } return 0; @@ -266,7 +308,7 @@ int uffd_zero_page(int uffd_fd, void *addr, uint64_t length, bool dont_wake) * via UFFD-IO IOCTLs with MODE_DONTWAKE flag set, then after that all waits * for the whole memory range are satisfied in a single call to uffd_wakeup(). * - * Returns 0 on success, negative value in case of an error + * Returns 0 on success, -errno in case of an error * * @uffd_fd: UFFD file descriptor * @addr: base address @@ -280,9 +322,10 @@ int uffd_wakeup(int uffd_fd, void *addr, uint64_t length) uffd_range.len = length; if (ioctl(uffd_fd, UFFDIO_WAKE, &uffd_range)) { + int e = errno; error_report("uffd_wakeup() failed: addr=%p length=%" PRIu64 " errno=%i", - addr, length, errno); - return -1; + addr, length, e); + return -e; } return 0; @@ -315,31 +358,3 @@ int uffd_read_events(int uffd_fd, struct uffd_msg *msgs, int count) return (int) (res / sizeof(struct uffd_msg)); } - -/** - * uffd_poll_events: poll UFFD file descriptor for read - * - * Returns true if events are available for read, false otherwise - * - * @uffd_fd: UFFD file descriptor - * @tmo: timeout value - */ -bool uffd_poll_events(int uffd_fd, int tmo) -{ - int res; - struct pollfd poll_fd = { .fd = uffd_fd, .events = POLLIN, .revents = 0 }; - - do { - res = poll(&poll_fd, 1, tmo); - } while (res < 0 && errno == EINTR); - - if (res == 0) { - return false; - } - if (res < 0) { - error_report("uffd_poll_events() failed: errno=%i", errno); - return false; - } - - return (poll_fd.revents & POLLIN) != 0; -} 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 4670867e1f..f8bab46c68 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -242,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; } @@ -256,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; } @@ -273,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; diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index 232984ace6..b19229074a 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" @@ -64,6 +65,18 @@ static void vmsg_close_fds(VhostUserMsg *vmsg) static void vmsg_unblock_fds(VhostUserMsg *vmsg) { int i; + + /* + * These messages carry fd used to map memory, not to send/receive messages, + * so this operation is useless. In addition, in some systems this + * operation can fail (e.g. in macOS setting an fd returned by shm_open() + * non-blocking fails with errno = ENOTTY) + */ + if (vmsg->request == VHOST_USER_ADD_MEM_REG || + vmsg->request == VHOST_USER_SET_MEM_TABLE) { + return; + } + for (i = 0; i < vmsg->fd_num; i++) { qemu_socket_set_nonblock(vmsg->fds[i]); } @@ -74,20 +87,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 +135,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 +212,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 +297,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 +325,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 +370,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 +385,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 +394,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 +423,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 +456,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; diff --git a/winpcap-loader/meson.build b/winpcap-loader/meson.build index 3c3265921f..29e338068e 100644 --- a/winpcap-loader/meson.build +++ b/winpcap-loader/meson.build @@ -1,3 +1,3 @@ -softmmu_ss.add(when: 'CONFIG_WIN32', if_true: files( - 'loader.c', -)) +if host_os == 'windows' + system_ss.add(files('loader.c')) +endif